[PATCH 3/3] Adapt TCP and TCPv6 to the socket address caching layer

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


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 <yamato at 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





More information about the Strace-devel mailing list