[PATCH 1/3] Introduce socket address information caching(scache) layer

Masatake YAMATO yamato at redhat.com
Thu Mar 19 16:17:28 UTC 2015


-yy option especially net socket makes strace slow.
The option requires frequently conversation with linux kernel
for solving addresses associated with one socket.

scache is for caching the information to reduce the
needs of conversation.

The information for established and listening sockets(TCP, TCPv6, UNIX)
are never changed in the socket life-cycle. strace can store the
address to somewhere for reusing in the future.

A hashtable keyed by inode number and a lru queue is used for storing.
The lru is used to throw inodes and associated addresses unused awhile away.

* socketutils.c (print_sockaddr_by_inode): The default printing code
for socket is moved from printfd to here.
(print_sockaddr_in_cache): New function. This is the entry point
for the socket address caching layer.
(scache_entries_add): New function.
(scache_entries_find, scache_lru_update, scache_lru_add): Ditto.
(scache_entries_remove): New function.
(scache_lru_tail, scache_lru_head): New variables.
(scache_lru_budget): New variable.
(SCACHE_LRU_BUDGET): New constant.
(scache_entries): New variable.
(SCACHE_ENTRIES_SIZE): New constant.
(scache_entry_print): New function.
(scach_entry_free): New function.
(scache_entry_init): New function.
(scache_protocol): New struct..
(scache_entry): New struct.
(assert.h): Newly included file.

* util.c (printsockinode): New helper function derived from
printfd. This function calls newly introduced print_sockaddr_in_cache.
(printfd): Simplified.

Signed-off-by: Masatake YAMATO <yamato at redhat.com>
---
 defs.h        |   1 +
 socketutils.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 util.c        |  28 +++++---
 3 files changed, 242 insertions(+), 10 deletions(-)

diff --git a/defs.h b/defs.h
index dad4fe8..5915da1 100644
--- a/defs.h
+++ b/defs.h
@@ -491,6 +491,7 @@ extern void sprint_timespec(char *, struct tcb *, long);
 extern void printsiginfo(const siginfo_t *, int);
 extern void printsiginfo_at(struct tcb *tcp, long addr);
 extern void printfd(struct tcb *, int);
+extern bool print_sockaddr_in_cache(const unsigned long);
 extern bool print_sockaddr_by_inode(const unsigned long, const char *);
 extern void print_dirfd(struct tcb *, int);
 extern void printsock(struct tcb *, long, int);
diff --git a/socketutils.c b/socketutils.c
index 93f5f16..1c7c69d 100644
--- a/socketutils.c
+++ b/socketutils.c
@@ -7,6 +7,7 @@
 #include <linux/inet_diag.h>
 #include <linux/unix_diag.h>
 #include <linux/rtnetlink.h>
+#include <assert.h>
 
 #if !defined NETLINK_SOCK_DIAG && defined NETLINK_INET_DIAG
 # define NETLINK_SOCK_DIAG NETLINK_INET_DIAG
@@ -17,6 +18,211 @@
 # define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) 0)->sun_path)
 #endif
 
+/* Socket info cache(scache) entry
+ * For connectoin oriented established sockets and lisning sockets associated
+ * addresses and ports are not changed in their life cycle. So their
+ * information can be recorded as cached information.
+ */
+#define ENTRY(e) ((struct scache_entry*)(e))
+struct scache_entry {
+	unsigned long inode;
+	struct scache_protocol *protocol;
+	struct scache_entry *hash_next;
+	struct scache_entry *lru_next;
+	struct scache_entry *lru_prev;
+};
+
+struct scache_protocol {
+	const char *name;			     /* Must */
+	size_t size;				     /* Must */
+	void (* print) (struct scache_entry *entry); /* Must */
+};
+
+static struct scache_entry *
+scache_entry_init(struct scache_entry *entry,
+		  unsigned long inode,
+		  struct scache_protocol *protocol)
+{
+	memset(entry, 0, protocol->size);
+	entry->inode = inode;
+	entry->protocol = protocol;
+	return entry;
+}
+
+static void
+scach_entry_free(struct scache_entry *entry)
+{
+	free (entry);
+}
+
+static void
+scache_entry_print(struct scache_entry *entry)
+{
+	tprintf("%s:[", entry->protocol->name);
+	entry->protocol->print (entry);
+	tprints("]");
+}
+
+/* Hashtable keyed by inode number */
+#define SCACHE_ENTRIES_SIZE 43
+static struct scache_entry *scache_entries [SCACHE_ENTRIES_SIZE];
+
+/* Least Recenly Used queue
+ * If strace records all established sockets, the cache will overflow.
+ * With this LRU queue, unused sockets can be thrown away.
+ */
+#define SCACHE_LRU_BUDGET 256
+static int scache_lru_budget = SCACHE_LRU_BUDGET;
+static struct scache_entry *scache_lru_head; /* the latest recently used */
+static struct scache_entry *scache_lru_tail; /* the unused */
+
+static void
+scache_entries_remove(struct scache_entry *entry)
+{
+	unsigned long slot;
+	struct scache_entry **chain;
+	struct scache_entry *tmp;
+
+	assert(entry);
+	/* Before removing an entry from the hashtable,
+	 * the entry must be removed from the LRU queue. */
+	assert(entry->lru_prev == NULL);
+	assert(entry->lru_next == NULL);
+
+	slot = entry->inode % SCACHE_ENTRIES_SIZE;
+	chain = &scache_entries[slot];
+
+	if ((*chain) == entry) {
+		*chain = entry->hash_next;
+		entry->hash_next = NULL;
+		free(entry);
+	} else {
+		tmp = *chain;
+
+		while (tmp) {
+			if (tmp->hash_next == entry) {
+				tmp->hash_next = entry->hash_next;
+				entry->hash_next = NULL;
+				scach_entry_free(entry);
+				break;
+			} else
+				tmp = tmp->hash_next;
+		}
+	}
+}
+
+static void
+scache_lru_add(struct scache_entry *entry)
+{
+	assert(entry->lru_next == NULL);
+	assert(entry->lru_prev == NULL);
+
+	entry->lru_prev = NULL;
+	entry->lru_next = scache_lru_head;
+	if (scache_lru_head)
+		scache_lru_head->lru_prev = entry;
+	scache_lru_head = entry;
+	if (scache_lru_tail == NULL)
+		scache_lru_tail = entry;
+
+	assert(entry->lru_prev == NULL);
+	assert(scache_lru_tail);
+
+	scache_lru_budget--;
+	if (scache_lru_budget == 0) {
+		struct scache_entry *evicted_entry;
+
+		assert(scache_lru_tail);
+		assert(scache_lru_head);
+		assert(scache_lru_tail->lru_prev);
+		assert(scache_lru_tail->lru_next == NULL);
+		assert(scache_lru_head->lru_next);
+		assert(scache_lru_head->lru_prev == NULL);
+
+		evicted_entry = scache_lru_tail;
+		scache_lru_tail = evicted_entry->lru_prev;
+		scache_lru_tail->lru_next = NULL;
+		evicted_entry->lru_prev = NULL;
+		scache_entries_remove(evicted_entry);
+		scache_lru_budget++;
+	}
+}
+
+/* Move the entry to the head of LRU queue */
+static void
+scache_lru_update(struct scache_entry *entry)
+{
+	if (entry == scache_lru_head) {
+		assert(entry->lru_next || scache_lru_tail == entry);
+		return;
+	} else if (entry == scache_lru_tail) {
+		assert(entry->lru_prev);
+
+		scache_lru_tail = entry->lru_prev;
+		scache_lru_tail->lru_next = NULL;
+		scache_lru_head->lru_prev = entry;
+		entry->lru_next = scache_lru_head;
+		scache_lru_head = entry;
+		entry->lru_prev = NULL;
+	} else {
+		assert(entry->lru_next);
+		assert(entry->lru_prev);
+
+		entry->lru_prev->lru_next = entry->lru_next;
+		entry->lru_next->lru_prev = entry->lru_prev;
+		entry->lru_prev = NULL;
+		entry->lru_next = scache_lru_head;
+		scache_lru_head->lru_prev = entry;
+		scache_lru_head = entry;
+	}
+}
+
+static struct scache_entry *
+scache_entries_find(unsigned long inode)
+{
+	unsigned long slot;
+	struct scache_entry *chain, *result = NULL;
+
+	slot = inode % SCACHE_ENTRIES_SIZE;
+	chain = scache_entries[slot];
+
+	while (chain) {
+		if (chain->inode == inode) {
+			result = chain;
+			break;
+		}
+		else
+			chain = chain->hash_next;
+	}
+
+	if (result)
+		scache_lru_update(result);
+
+	return result;
+}
+
+static void
+scache_entries_add(struct scache_entry *entry)
+{
+	unsigned long slot;
+	struct scache_entry **chain;
+	struct scache_entry *e;
+	size_t s;
+
+	s = entry->protocol->size;
+	e = malloc(s);
+	if (!e)
+		die_out_of_memory();
+	memcpy(e, entry, s);
+
+	slot = entry->inode % SCACHE_ENTRIES_SIZE;
+	chain = &scache_entries[slot];
+	e->hash_next = (*chain);
+	*chain = e;
+	scache_lru_add(e);
+}
+
+
 static bool
 inet_send_query(const int fd, const int family, const int proto)
 {
@@ -275,6 +481,18 @@ unix_print(int fd, const unsigned long inode)
 		&& receive_responses(fd, inode, "UNIX", unix_parse_response);
 }
 
+bool
+print_sockaddr_in_cache(const unsigned long inode)
+{
+	struct scache_entry *r;
+
+	r = scache_entries_find(inode);
+	if (r)
+		scache_entry_print(r);
+
+	return r? true: false;
+}
+
 /* Given an inode number of a socket, print out the details
  * of the ip address and port. */
 bool
@@ -298,6 +516,11 @@ print_sockaddr_by_inode(const unsigned long inode, const char *proto_name)
 			r = inet_print(fd, AF_INET6, IPPROTO_UDP, inode, "UDPv6");
 		else if (strcmp(proto_name, "UNIX") == 0)
 			r = unix_print(fd, inode);
+
+		if (!r) {
+			tprintf("%s:[%lu]", proto_name, inode);
+			r = true;
+		}
 	} else {
 		const struct {
 			const int family;
diff --git a/util.c b/util.c
index 02c5b9a..4e834e2 100644
--- a/util.c
+++ b/util.c
@@ -459,6 +459,21 @@ getfdproto(struct tcb *tcp, int fd, char *buf, unsigned bufsize)
 #endif
 }
 
+static bool
+printsockinode(struct tcb *tcp, int fd, unsigned long inodenr)
+{
+	if (print_sockaddr_in_cache(inodenr))
+		return true;
+	else {
+#define PROTO_NAME_LEN 32
+		char proto_buf[PROTO_NAME_LEN];
+		const char *proto;
+
+		proto = getfdproto(tcp, fd, proto_buf, PROTO_NAME_LEN);
+		return print_sockaddr_by_inode(inodenr, proto);
+	}
+}
+
 void
 printfd(struct tcb *tcp, int fd)
 {
@@ -473,17 +488,10 @@ printfd(struct tcb *tcp, int fd)
 		    strncmp(path, socket_prefix, socket_prefix_len) == 0 &&
 		    path[path_len - 1] == ']') {
 			unsigned long inodenr;
-#define PROTO_NAME_LEN 32
-			char proto_buf[PROTO_NAME_LEN];
-			const char *proto =
-				getfdproto(tcp, fd, proto_buf, PROTO_NAME_LEN);
+
 			inodenr = strtoul(path + socket_prefix_len, NULL, 10);
-			if (!print_sockaddr_by_inode(inodenr, proto)) {
-				if (proto)
-					tprintf("%s:[%lu]", proto, inodenr);
-				else
-					tprints(path);
-			}
+			if (!printsockinode(tcp, fd, inodenr))
+				tprints(path);
 		} else {
 			print_quoted_string(path, path_len,
 					    QUOTE_OMIT_LEADING_TRAILING_QUOTES);
-- 
2.1.0





More information about the Strace-devel mailing list