[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