|
4 | 4 | # tcprtt Summarize TCP RTT as a histogram. For Linux, uses BCC, eBPF.
|
5 | 5 | #
|
6 | 6 | # USAGE: tcprtt [-h] [-T] [-D] [-m] [-i INTERVAL] [-d DURATION]
|
7 |
| -# [-p SPORT] [-P DPORT] [-a SADDR] [-A DADDR] |
| 7 | +# [-p LPORT] [-P RPORT] [-a LADDR] [-A RADDR] [-b] [-B] |
8 | 8 | #
|
9 | 9 | # Copyright (c) 2020 zhenwei pi
|
10 | 10 | # Licensed under the Apache License, Version 2.0 (the "License")
|
|
14 | 14 | from __future__ import print_function
|
15 | 15 | from bcc import BPF
|
16 | 16 | from time import sleep, strftime
|
| 17 | +from socket import inet_ntop, AF_INET |
17 | 18 | import socket, struct
|
18 | 19 | import argparse
|
19 | 20 |
|
|
22 | 23 | ./tcprtt # summarize TCP RTT
|
23 | 24 | ./tcprtt -i 1 -d 10 # print 1 second summaries, 10 times
|
24 | 25 | ./tcprtt -m -T # summarize in millisecond, and timestamps
|
25 |
| - ./tcprtt -p # filter for source port |
26 |
| - ./tcprtt -P # filter for destination port |
27 |
| - ./tcprtt -a # filter for source address |
28 |
| - ./tcprtt -A # filter for destination address |
| 26 | + ./tcprtt -p # filter for local port |
| 27 | + ./tcprtt -P # filter for remote port |
| 28 | + ./tcprtt -a # filter for local address |
| 29 | + ./tcprtt -A # filter for remote address |
| 30 | + ./tcprtt -b # show sockets histogram by local address |
| 31 | + ./tcprtt -B # show sockets histogram by remote address |
29 | 32 | ./tcprtt -D # show debug bpf text
|
30 | 33 | """
|
31 | 34 | parser = argparse.ArgumentParser(
|
|
40 | 43 | help="include timestamp on output")
|
41 | 44 | parser.add_argument("-m", "--milliseconds", action="store_true",
|
42 | 45 | help="millisecond histogram")
|
43 |
| -parser.add_argument("-p", "--sport", |
44 |
| - help="source port") |
45 |
| -parser.add_argument("-P", "--dport", |
46 |
| - help="destination port") |
47 |
| -parser.add_argument("-a", "--saddr", |
48 |
| - help="source address") |
49 |
| -parser.add_argument("-A", "--daddr", |
50 |
| - help="destination address") |
| 46 | +parser.add_argument("-p", "--lport", |
| 47 | + help="filter for local port") |
| 48 | +parser.add_argument("-P", "--rport", |
| 49 | + help="filter for remote port") |
| 50 | +parser.add_argument("-a", "--laddr", |
| 51 | + help="filter for local address") |
| 52 | +parser.add_argument("-A", "--raddr", |
| 53 | + help="filter for remote address") |
| 54 | +parser.add_argument("-b", "--byladdr", action="store_true", |
| 55 | + help="show sockets histogram by local address") |
| 56 | +parser.add_argument("-B", "--byraddr", action="store_true", |
| 57 | + help="show sockets histogram by remote address") |
51 | 58 | parser.add_argument("-D", "--debug", action="store_true",
|
52 | 59 | help="print BPF program before starting (for debugging purposes)")
|
53 | 60 | parser.add_argument("--ebpf", action="store_true",
|
|
67 | 74 | #include <net/inet_sock.h>
|
68 | 75 | #include <bcc/proto.h>
|
69 | 76 |
|
70 |
| -BPF_HISTOGRAM(hist_srtt); |
| 77 | +typedef struct sock_key { |
| 78 | + u64 addr; |
| 79 | + u64 slot; |
| 80 | +} sock_key_t; |
| 81 | +
|
| 82 | +STORAGE |
71 | 83 |
|
72 | 84 | int trace_tcp_rcv(struct pt_regs *ctx, struct sock *sk, struct sk_buff *skb)
|
73 | 85 | {
|
74 | 86 | struct tcp_sock *ts = tcp_sk(sk);
|
75 | 87 | u32 srtt = ts->srtt_us >> 3;
|
76 | 88 | const struct inet_sock *inet = inet_sk(sk);
|
| 89 | + u16 sport = 0; |
| 90 | + u16 dport = 0; |
| 91 | + u32 saddr = 0; |
| 92 | + u32 daddr = 0; |
| 93 | +
|
| 94 | + bpf_probe_read_kernel(&sport, sizeof(sport), (void *)&inet->inet_sport); |
| 95 | + bpf_probe_read_kernel(&dport, sizeof(dport), (void *)&inet->inet_dport); |
| 96 | + bpf_probe_read_kernel(&saddr, sizeof(saddr), (void *)&inet->inet_saddr); |
| 97 | + bpf_probe_read_kernel(&daddr, sizeof(daddr), (void *)&inet->inet_daddr); |
| 98 | +
|
| 99 | + LPORTFILTER |
| 100 | + RPORTFILTER |
| 101 | + LADDRFILTER |
| 102 | + RADDRFILTER |
77 | 103 |
|
78 |
| - SPORTFILTER |
79 |
| - DPORTFILTER |
80 |
| - SADDRFILTER |
81 |
| - DADDRFILTER |
82 | 104 | FACTOR
|
83 | 105 |
|
84 |
| - hist_srtt.increment(bpf_log2l(srtt)); |
| 106 | + STORE |
85 | 107 |
|
86 | 108 | return 0;
|
87 | 109 | }
|
88 | 110 | """
|
89 | 111 |
|
90 |
| -# filter for source port |
91 |
| -if args.sport: |
92 |
| - bpf_text = bpf_text.replace(b'SPORTFILTER', |
93 |
| - b"""u16 sport = 0; |
94 |
| - bpf_probe_read_kernel(&sport, sizeof(sport), (void *)&inet->inet_sport); |
95 |
| - if (ntohs(sport) != %d) |
96 |
| - return 0;""" % int(args.sport)) |
| 112 | +# filter for local port |
| 113 | +if args.lport: |
| 114 | + bpf_text = bpf_text.replace(b'LPORTFILTER', |
| 115 | + b"""if (ntohs(sport) != %d) |
| 116 | + return 0;""" % int(args.lport)) |
97 | 117 | else:
|
98 |
| - bpf_text = bpf_text.replace(b'SPORTFILTER', b'') |
| 118 | + bpf_text = bpf_text.replace(b'LPORTFILTER', b'') |
99 | 119 |
|
100 |
| -# filter for dest port |
101 |
| -if args.dport: |
102 |
| - bpf_text = bpf_text.replace(b'DPORTFILTER', |
103 |
| - b"""u16 dport = 0; |
104 |
| - bpf_probe_read_kernel(&dport, sizeof(dport), (void *)&inet->inet_dport); |
105 |
| - if (ntohs(dport) != %d) |
106 |
| - return 0;""" % int(args.dport)) |
| 120 | +# filter for remote port |
| 121 | +if args.rport: |
| 122 | + bpf_text = bpf_text.replace(b'RPORTFILTER', |
| 123 | + b"""if (ntohs(dport) != %d) |
| 124 | + return 0;""" % int(args.rport)) |
107 | 125 | else:
|
108 |
| - bpf_text = bpf_text.replace(b'DPORTFILTER', b'') |
| 126 | + bpf_text = bpf_text.replace(b'RPORTFILTER', b'') |
109 | 127 |
|
110 |
| -# filter for source address |
111 |
| -if args.saddr: |
112 |
| - bpf_text = bpf_text.replace(b'SADDRFILTER', |
113 |
| - b"""u32 saddr = 0; |
114 |
| - bpf_probe_read_kernel(&saddr, sizeof(saddr), (void *)&inet->inet_saddr); |
115 |
| - if (saddr != %d) |
116 |
| - return 0;""" % struct.unpack("=I", socket.inet_aton(args.saddr))[0]) |
| 128 | +# filter for local address |
| 129 | +if args.laddr: |
| 130 | + bpf_text = bpf_text.replace(b'LADDRFILTER', |
| 131 | + b"""if (saddr != %d) |
| 132 | + return 0;""" % struct.unpack("=I", socket.inet_aton(args.laddr))[0]) |
117 | 133 | else:
|
118 |
| - bpf_text = bpf_text.replace(b'SADDRFILTER', b'') |
| 134 | + bpf_text = bpf_text.replace(b'LADDRFILTER', b'') |
119 | 135 |
|
120 |
| -# filter for source address |
121 |
| -if args.daddr: |
122 |
| - bpf_text = bpf_text.replace(b'DADDRFILTER', |
123 |
| - b"""u32 daddr = 0; |
124 |
| - bpf_probe_read_kernel(&daddr, sizeof(daddr), (void *)&inet->inet_daddr); |
125 |
| - if (daddr != %d) |
126 |
| - return 0;""" % struct.unpack("=I", socket.inet_aton(args.daddr))[0]) |
| 136 | +# filter for remote address |
| 137 | +if args.raddr: |
| 138 | + bpf_text = bpf_text.replace(b'RADDRFILTER', |
| 139 | + b"""if (daddr != %d) |
| 140 | + return 0;""" % struct.unpack("=I", socket.inet_aton(args.raddr))[0]) |
127 | 141 | else:
|
128 |
| - bpf_text = bpf_text.replace(b'DADDRFILTER', b'') |
| 142 | + bpf_text = bpf_text.replace(b'RADDRFILTER', b'') |
129 | 143 |
|
130 | 144 | # show msecs or usecs[default]
|
131 | 145 | if args.milliseconds:
|
|
135 | 149 | bpf_text = bpf_text.replace('FACTOR', '')
|
136 | 150 | label = "usecs"
|
137 | 151 |
|
| 152 | +print_header = "srtt" |
| 153 | +# show byladdr/byraddr histogram |
| 154 | +if args.byladdr: |
| 155 | + bpf_text = bpf_text.replace('STORAGE', |
| 156 | + 'BPF_HISTOGRAM(hist_srtt, sock_key_t);') |
| 157 | + bpf_text = bpf_text.replace('STORE', |
| 158 | + b"""sock_key_t key; |
| 159 | + key.addr = saddr; |
| 160 | + key.slot = bpf_log2l(srtt); |
| 161 | + hist_srtt.increment(key);""") |
| 162 | + print_header = "Local Address: " |
| 163 | +elif args.byraddr: |
| 164 | + bpf_text = bpf_text.replace('STORAGE', |
| 165 | + 'BPF_HISTOGRAM(hist_srtt, sock_key_t);') |
| 166 | + bpf_text = bpf_text.replace('STORE', |
| 167 | + b"""sock_key_t key; |
| 168 | + key.addr = daddr; |
| 169 | + key.slot = bpf_log2l(srtt); |
| 170 | + hist_srtt.increment(key);""") |
| 171 | + print_header = "Remote Address: " |
| 172 | +else: |
| 173 | + bpf_text = bpf_text.replace('STORAGE', 'BPF_HISTOGRAM(hist_srtt);') |
| 174 | + bpf_text = bpf_text.replace('STORE', 'hist_srtt.increment(bpf_log2l(srtt));') |
| 175 | + |
138 | 176 | # debug/dump ebpf enable or not
|
139 | 177 | if args.debug or args.ebpf:
|
140 | 178 | print(bpf_text)
|
|
147 | 185 |
|
148 | 186 | print("Tracing TCP RTT... Hit Ctrl-C to end.")
|
149 | 187 |
|
| 188 | +def print_section(addr): |
| 189 | + if args.byladdr: |
| 190 | + return inet_ntop(AF_INET, struct.pack("I", addr)).encode() |
| 191 | + elif args.byraddr: |
| 192 | + return inet_ntop(AF_INET, struct.pack("I", addr)).encode() |
| 193 | + |
150 | 194 | # output
|
151 | 195 | exiting = 0 if args.interval else 1
|
152 | 196 | dist = b.get_table("hist_srtt")
|
|
162 | 206 | if args.timestamp:
|
163 | 207 | print("%-8s\n" % strftime("%H:%M:%S"), end="")
|
164 | 208 |
|
165 |
| - dist.print_log2_hist(label, "srtt") |
| 209 | + dist.print_log2_hist(label, section_header=print_header, section_print_fn=print_section) |
166 | 210 | dist.clear()
|
167 | 211 |
|
168 | 212 | if exiting or seconds >= args.duration:
|
|
0 commit comments