Established sockets and listening sockets of TCP and TCPv6
never changes their addresses and ports in their life-cycle.
So they can be stored to the caching layer.

A program pasted at the end of this log message requires the
retrieving the addresses and ports more than 50000 * 2 times; the child
process calls send 50000 times for a socket and the parent process
calls recv 50000 times for the peer of the socket.

With this commit:
% time ./strace -o /dev/null -f -e recvfrom,sendto -yy ./a.out
./strace -o /dev/null -f -e recvfrom,sendto -yy ./a.out  0.22s user 1.22s 
system 173% cpu 0.827 total

Without this commit:
% time ./strace -o /dev/null -f -e recvfrom,sendto -yy ./a.out
./strace-no-cache -o /dev/null -f -e recvfrom,sendto -yy ./a.out  0.55s user 
26.29s system 103% cpu 25.870 total

Changes:
* socketutils.c (inet4_entry, inet6_entry): New structs.
(inet4_print, inet6_print): New functions.
(udp_protocol, tcp_protocol, udpv6_protocol, tcpv6_protocol): New protocols.
(inet_parse_response): Instead of printing the socket related addresses and 
ports
directly, it stores the them to the buffer where
entry variable points. Then it passes the buffer to scache_entry_print
for printing. Stable information is also cached.

Test target:

    #include <assert.h>
    #include <stddef.h>
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>

    #define COUNT 10000*5

    int main(void)
    {
            static const char data[] = "data";
            const size_t size = sizeof(data) - 1;
            struct sockaddr_in addr;
            socklen_t len = sizeof(addr);
            pid_t pid;

            memset(&addr, 0, sizeof(addr));
            addr.sin_family = AF_INET;
            addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

            close(0);
            close(1);

            if (socket(PF_INET, SOCK_STREAM, 0)) {
                    perror("socket");
                    return 77;
            }
            if (bind(0, (struct sockaddr *) &addr, len)) {
                    perror("bind");
                    return 77;
            }
            assert(listen(0, 5) == 0);

            memset(&addr, 0, sizeof(addr));
            assert(getsockname(0, (struct sockaddr *) &addr, &len) == 0);

            assert((pid = fork()) >= 0);

            if (pid) {
                    char buf[sizeof(data)];
                    int status;
                    int i;

                    assert(accept(0, (struct sockaddr *) &addr, &len) == 1);
                    assert(close(0) == 0);

                    while (recv(1, buf, sizeof(buf), MSG_WAITALL) > 0)
                            ;

                    assert(waitpid(pid, &status, 0) == pid);
                    assert(status == 0);
                    assert(close(1) == 0);
            } else {
                    int i;
                    assert(close(0) == 0);
                    assert(socket(PF_INET, SOCK_STREAM, 0) == 0);
                    assert(connect(0, (struct sockaddr *) &addr, len) == 0);
                    for (i = 0; i < COUNT; i++)
                            assert(send(0, data, size, MSG_DONTROUTE) == (int) 
size);
                    assert(close(0) == 0);
            }

            return 0;
    }

Signed-off-by: Masatake YAMATO <yam...@redhat.com>
---
 socketutils.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 114 insertions(+), 10 deletions(-)

diff --git a/socketutils.c b/socketutils.c
index 7d8df75..0544592 100644
--- a/socketutils.c
+++ b/socketutils.c
@@ -265,6 +265,74 @@ inet_send_query(const int fd, const int family, const int 
proto)
        }
 }
 
+/*
+ * Inet and Inet6 entry
+ */
+
+struct inet4_entry {
+       struct scache_entry base;
+       char saddr[INET_ADDRSTRLEN];
+       char daddr[INET_ADDRSTRLEN];
+       unsigned short sport;
+       unsigned short dport;
+};
+
+static void
+inet4_print(struct scache_entry *entry)
+{
+       struct inet4_entry *entry4 = (struct inet4_entry *)entry;
+       if (entry4->dport)
+               tprintf("%s:%u->%s:%u",
+                       entry4->saddr, entry4->sport,
+                       entry4->daddr, entry4->dport);
+       else
+               tprintf("%s:%u", entry4->saddr, entry4->sport);
+}
+
+static struct scache_protocol udp_protocol = {
+       .name  = "UDP",
+       .size = sizeof(struct inet4_entry),
+       .print = inet4_print,
+};
+
+static struct scache_protocol tcp_protocol = {
+       .name  = "TCP",
+       .size = sizeof(struct inet4_entry),
+       .print = inet4_print,
+};
+
+struct inet6_entry {
+       struct scache_entry base;
+       char saddr[INET6_ADDRSTRLEN];
+       char daddr[INET6_ADDRSTRLEN];
+       unsigned short sport;
+       unsigned short dport;
+};
+
+static void
+inet6_print(struct scache_entry *entry)
+{
+       struct inet6_entry *entry6 = (struct inet6_entry *)entry;
+       if (entry6->dport)
+               tprintf("%s:%u->%s:%u",
+                       entry6->saddr, entry6->sport,
+                       entry6->daddr, entry6->dport);
+       else
+               tprintf("%s:%u", entry6->saddr, entry6->sport);
+}
+
+static struct scache_protocol udpv6_protocol = {
+       .name  = "UDPv6",
+       .size = sizeof(struct inet6_entry),
+       .print = inet6_print,
+};
+
+static struct scache_protocol tcpv6_protocol = {
+       .name  = "TCPv6",
+       .size = sizeof(struct inet6_entry),
+       .print = inet6_print,
+};
+
 static bool
 inet_parse_response(const char *proto_name, const void *data, int data_len,
                    const unsigned long inode)
@@ -272,6 +340,13 @@ inet_parse_response(const char *proto_name, const void 
*data, int data_len,
        const struct inet_diag_msg *diag_msg = data;
        static const char zero_addr[sizeof(struct in6_addr)];
        socklen_t addr_size, text_size;
+       struct scache_entry *entry;
+       struct inet4_entry entry4;
+       struct inet6_entry entry6;
+       char *src_buf;
+       char *dst_buf;
+       struct scache_protocol *fpro;
+       bool stable = false;
 
        if (diag_msg->idiag_inode != inode)
                return false;
@@ -280,38 +355,67 @@ inet_parse_response(const char *proto_name, const void 
*data, int data_len,
                case AF_INET:
                        addr_size = sizeof(struct in_addr);
                        text_size = INET_ADDRSTRLEN;
+                       if (!strcmp(proto_name, "TCP")) {
+                               fpro = &tcp_protocol;
+                               if (diag_msg->idiag_state)
+                                       stable = true;
+                       }
+                       else
+                               fpro = &udp_protocol;
+                       entry = scache_entry_init(ENTRY(&entry4), inode, fpro);
+                       src_buf = entry4.saddr;
+                       dst_buf = entry4.daddr;
                        break;
                case AF_INET6:
                        addr_size = sizeof(struct in6_addr);
                        text_size = INET6_ADDRSTRLEN;
+                       if (!strcmp(proto_name, "TCPv6")) {
+                               fpro = &tcpv6_protocol;
+                               if (diag_msg->idiag_state)
+                                       stable = true;
+                       }
+                       else
+                               fpro = &udpv6_protocol;
+                       entry = scache_entry_init(ENTRY(&entry6), inode, fpro);
+                       src_buf = entry6.saddr;
+                       dst_buf = entry6.daddr;
                        break;
                default:
                        return false;
        }
 
-       char src_buf[text_size];
-
        if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_src,
                       src_buf, text_size))
                return false;
 
        if (diag_msg->id.idiag_dport ||
            memcmp(zero_addr, diag_msg->id.idiag_dst, addr_size)) {
-               char dst_buf[text_size];
-
                if (!inet_ntop(diag_msg->idiag_family, diag_msg->id.idiag_dst,
                               dst_buf, text_size))
                        return false;
 
-               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 (diag_msg->idiag_family == AF_INET) {
+                       entry4.sport = ntohs(diag_msg->id.idiag_sport);
+                       entry4.dport = ntohs(diag_msg->id.idiag_dport);
+               } else {
+                       entry6.sport = ntohs(diag_msg->id.idiag_sport);
+                       entry6.dport = ntohs(diag_msg->id.idiag_dport);
+               }
        } else {
-               tprintf("%s:[%s:%u]", proto_name, src_buf,
-                       ntohs(diag_msg->id.idiag_sport));
+               if (diag_msg->idiag_family == AF_INET) {
+                       entry4.sport = ntohs(diag_msg->id.idiag_sport);
+                       entry4.dport = 0;
+               } else {
+                       entry6.sport = ntohs(diag_msg->id.idiag_sport);
+                       entry6.dport = 0;
+               }
        }
 
+       scache_entry_print(entry);
+
+       if (stable)
+               scache_entries_add (entry);
+
        return true;
 }
 
-- 
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

Reply via email to