-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 <yam...@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 ------------------------------------------------------------------------------ Dive into the World of Parallel Programming The Go Parallel Website, sponsored by Intel and developed in partnership with Slashdot Media, is your hub for all things parallel software development, from weekly thought leadership blogs to news, videos, case studies, tutorials and more. Take a look and join the conversation now. http://goparallel.sourceforge.net/ _______________________________________________ Strace-devel mailing list Strace-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/strace-devel