From 3c17d1b5e17ac7e172b09bdb3bf5698c96dd3fd9 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Mon, 1 Feb 2016 23:14:59 +0000 Subject: [PATCH] Implement caching of print_sockaddr_by_inode As -yy parser, compared to -y, needs to do at least 5 extra syscalls (getxattr, socket, sendmsg, recvmsg, close) to print socket details, caching results of netlink conversations between strace and kernel noticeably reduces amount of system time spent by strace. The caching is safe since sockets do not change their addresses after successful bind or connect syscall. * defs.h (string_quote, print_sockaddr_by_inode_cached): New prototypes. * socketutils.c (cache_entry): New type. (CACHE_SIZE, CACHE_MASK): New macros. (cache): New static array. (cache_and_print_inode_details): New static function. (print_sockaddr_by_inode_cached): New function. (inet_parse_response, unix_parse_response): Use cache_and_print_inode_details. * util.c (printfd): Use string_quote and print_sockaddr_by_inode_cached. (string_quote): Remove static qualifier. * NEWS: Mention this improvement. * tests/unix-yy.c (main): Update. --- NEWS | 2 + defs.h | 2 + socketutils.c | 95 +++++++++++++++++++++++++++++++++------------ tests/net-yy-unix.c | 8 ++-- util.c | 18 +++++---- 5 files changed, 89 insertions(+), 36 deletions(-) diff --git a/NEWS b/NEWS index b9ed032b..186c351f 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,8 @@ Noteworthy changes in release ?.?? (????-??-??) and sched_setaffinity syscalls. * Enhanced decoding of getxpid, getxuid, and getxgid syscalls on alpha. * Added decoding of bind, listen, and setsockopt direct syscalls on sparc. + * Implemented caching of netlink conversations to reduce amount of time + spent in decoding socket details in -yy mode. * Bug fixes * Fixed build on arc, metag, nios2, or1k, and tile architectures. diff --git a/defs.h b/defs.h index 6b4f2ab6..86b6434f 100644 --- a/defs.h +++ b/defs.h @@ -536,6 +536,7 @@ extern int next_set_bit(const void *bit_array, unsigned cur_bit, unsigned size_b #define QUOTE_0_TERMINATED 0x01 #define QUOTE_OMIT_LEADING_TRAILING_QUOTES 0x02 +extern int string_quote(const char *, char *, unsigned int, unsigned int); extern int print_quoted_string(const char *, unsigned int, unsigned int); /* a refers to the lower numbered u_arg, @@ -613,6 +614,7 @@ extern void printpathn(struct tcb *, long, unsigned int); (sizeof(intmax_t)*3 * 2 + sizeof("{tv_sec=%jd, tv_nsec=%jd}")) extern void printfd(struct tcb *, int); extern bool print_sockaddr_by_inode(const unsigned long, const char *); +extern bool print_sockaddr_by_inode_cached(const unsigned long); extern void print_dirfd(struct tcb *, int); extern void printsock(struct tcb *, long, int); extern void print_sock_optmgmt(struct tcb *, long, int); diff --git a/socketutils.c b/socketutils.c index 7046d298..6df50fd7 100644 --- a/socketutils.c +++ b/socketutils.c @@ -45,6 +45,38 @@ # define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) 0)->sun_path) #endif +typedef struct { + unsigned long inode; + char *details; +} cache_entry; + +#define CACHE_SIZE 1024U +static cache_entry cache[CACHE_SIZE]; +#define CACHE_MASK (CACHE_SIZE - 1) + +static int +cache_and_print_inode_details(const unsigned long inode, char *details) +{ + cache_entry *e = &cache[inode & CACHE_MASK]; + free(e->details); + e->inode = inode; + e->details = details; + + tprints(details); + return 1; +} + +bool +print_sockaddr_by_inode_cached(const unsigned long inode) +{ + const cache_entry *e = &cache[inode & CACHE_MASK]; + if (e && inode == e->inode) { + tprints(e->details); + return true; + } + return false; +} + static bool inet_send_query(const int fd, const int family, const int proto) { @@ -114,6 +146,7 @@ inet_parse_response(const char *proto_name, const void *data, } char src_buf[text_size]; + char *details; if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src, src_buf, text_size)) @@ -127,16 +160,17 @@ inet_parse_response(const char *proto_name, const void *data, dst_buf, text_size)) return -1; - tprintf("%s:[%s:%u->%s:%u]", - proto_name, - src_buf, ntohs(diag_msg->id.idiag_sport), - dst_buf, ntohs(diag_msg->id.idiag_dport)); + if (asprintf(&details, "%s:[%s:%u->%s:%u]", proto_name, + src_buf, ntohs(diag_msg->id.idiag_sport), + dst_buf, ntohs(diag_msg->id.idiag_dport)) < 0) + return false; } else { - tprintf("%s:[%s:%u]", proto_name, src_buf, - ntohs(diag_msg->id.idiag_sport)); + if (asprintf(&details, "%s:[%s:%u]", proto_name, src_buf, + ntohs(diag_msg->id.idiag_sport)) < 0) + return false; } - return 1; + return cache_and_print_inode_details(inode, details); } static bool @@ -282,26 +316,39 @@ unix_parse_response(const char *proto_name, const void *data, * print obtained information in the following format: * "UNIX:[" SELF_INODE [ "->" PEER_INODE ][ "," SOCKET_FILE ] "]" */ - if (peer || path_len) { - tprintf("%s:[%lu", proto_name, inode); - if (peer) - tprintf("->%u", peer); - if (path_len) { - if (path[0] == '\0') { - tprints(",@"); - print_quoted_string(path + 1, path_len, - QUOTE_0_TERMINATED); - } else { - tprints(","); - print_quoted_string(path, path_len + 1, - QUOTE_0_TERMINATED); - } + if (!peer && !path_len) + return -1; + + char peer_str[3 + sizeof(peer) * 3]; + if (peer) + snprintf(peer_str, sizeof(peer_str), "->%u", peer); + else + peer_str[0] = '\0'; + + const char *path_str; + if (path_len) { + char *outstr = alloca(4 * path_len + 4); + + outstr[0] = ','; + if (path[0] == '\0') { + outstr[1] = '@'; + string_quote(path + 1, outstr + 2, + path_len - 1, QUOTE_0_TERMINATED); + } else { + string_quote(path, outstr + 1, + path_len, QUOTE_0_TERMINATED); } - tprints("]"); - return 1; + path_str = outstr; + } else { + path_str = ""; } - else + + char *details; + if (asprintf(&details, "%s:[%lu%s%s]", proto_name, inode, + peer_str, path_str) < 0) return -1; + + return cache_and_print_inode_details(inode, details); } static bool diff --git a/tests/net-yy-unix.c b/tests/net-yy-unix.c index 424f00c2..a15914ba 100644 --- a/tests/net-yy-unix.c +++ b/tests/net-yy-unix.c @@ -141,8 +141,8 @@ main(int ac, const char **av) connect_fd, connect_inode, accept_inode); assert(close(accept_fd) == 0); - printf("close(%d) = 0\n", - accept_fd, accept_inode, av[1]); + printf("close(%d%lu,\"%s\"]>) = 0\n", + accept_fd, accept_inode, connect_inode, av[1]); connect_fd = socket(PF_LOCAL, SOCK_STREAM, 0); if (connect_fd < 0) @@ -213,8 +213,8 @@ main(int ac, const char **av) connect_fd, connect_inode, accept_inode, sun_path1); assert(close(accept_fd) == 0); - printf("close(%d) = 0\n", - accept_fd, accept_inode, av[1]); + printf("close(%d%lu,\"%s\"]>) = 0\n", + accept_fd, accept_inode, connect_inode, av[1]); assert(unlink(av[1]) == 0); diff --git a/util.c b/util.c index 4616920a..f6e91495 100644 --- a/util.c +++ b/util.c @@ -512,14 +512,16 @@ printfd(struct tcb *tcp, int fd) if (show_fd_path > 1 && strncmp(path, socket_prefix, socket_prefix_len) == 0 && path[path_len - 1] == ']') { - unsigned long inodenr = + unsigned long inode = strtoul(path + socket_prefix_len, NULL, 10); -#define PROTO_NAME_LEN 32 - char proto_buf[PROTO_NAME_LEN]; - const char *proto = - getfdproto(tcp, fd, proto_buf, PROTO_NAME_LEN); - if (!print_sockaddr_by_inode(inodenr, proto)) - tprints(path); + + if (!print_sockaddr_by_inode_cached(inode)) { + char buf[256]; + const char *proto = + getfdproto(tcp, fd, buf, sizeof(buf)); + if (!print_sockaddr_by_inode(inode, proto)) + tprints(path); + } } else { print_quoted_string(path, path_len, QUOTE_OMIT_LEADING_TRAILING_QUOTES); @@ -543,7 +545,7 @@ printfd(struct tcb *tcp, int fd) * Returns 0 if QUOTE_0_TERMINATED is set and NUL was seen, 1 otherwise. * Note that if QUOTE_0_TERMINATED is not set, always returns 1. */ -static int +int string_quote(const char *instr, char *outstr, const unsigned int size, const unsigned int style) { -- 2.40.0