]> granicus.if.org Git - strace/commitdiff
Implement caching of print_sockaddr_by_inode
authorDmitry V. Levin <ldv@altlinux.org>
Mon, 1 Feb 2016 23:14:59 +0000 (23:14 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Wed, 3 Feb 2016 12:43:22 +0000 (12:43 +0000)
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
defs.h
socketutils.c
tests/net-yy-unix.c
util.c

diff --git a/NEWS b/NEWS
index b9ed032b982d6412da52b3c321cfa765066a433e..186c351fdd17f80b8bd0d31c935b49d3d1b152da 100644 (file)
--- 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 6b4f2ab672997301e612a65e95331805e6625d70..86b6434fb9e883ee04e59d6f7393673c1210d0ed 100644 (file)
--- 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);
index 7046d29897e1fd6fb264ca2c50d8740e5887d8c4..6df50fd783a7a1d596b619abe7a5b0d1c7f9db13 100644 (file)
 # 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
index 424f00c2ebe63a9507fb31c4548aa946017c1009..a15914babfc642b4103357295308a20b418a0cf0 100644 (file)
@@ -141,8 +141,8 @@ main(int ac, const char **av)
               connect_fd, connect_inode, accept_inode);
 
        assert(close(accept_fd) == 0);
-       printf("close(%d<UNIX:[%lu,\"%s\"]>) = 0\n",
-              accept_fd, accept_inode, av[1]);
+       printf("close(%d<UNIX:[%lu->%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<UNIX:[%lu,\"%s\"]>) = 0\n",
-              accept_fd, accept_inode, av[1]);
+       printf("close(%d<UNIX:[%lu->%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 4616920ab816b2a857a0ff8d0214cfc3139c9b6d..f6e9149525fb21dc6a412d9faa4e3560692dffe9 100644 (file)
--- 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)
 {