Skip to content

Commit 3e66fe8

Browse files
committed
tcppktlat: Add histogram mode with per-PID grouping
Add -H option to display latency as histograms grouped by PID. Supports periodic printing with interval argument and works with existing filters like -p, -t, -l, -r. Signed-off-by: Wenbo Zhang <[email protected]>
1 parent 380ee01 commit 3e66fe8

File tree

4 files changed

+453
-70
lines changed

4 files changed

+453
-70
lines changed

libbpf-tools/tcppktlat.bpf.c

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,22 @@
55
#include <bpf/bpf_core_read.h>
66
#include <bpf/bpf_tracing.h>
77

8+
#include "bits.bpf.h"
89
#include "compat.bpf.h"
910
#include "core_fixes.bpf.h"
11+
#include "maps.bpf.h"
1012
#include "tcppktlat.h"
1113

12-
#define MAX_ENTRIES 10240
13-
#define AF_INET 2
14+
#define MAX_ENTRIES 10240
15+
#define AF_INET 2
1416

1517
const volatile pid_t targ_pid = 0;
1618
const volatile pid_t targ_tid = 0;
1719
const volatile __u16 targ_sport = 0;
1820
const volatile __u16 targ_dport = 0;
1921
const volatile __u64 targ_min_us = 0;
22+
const volatile bool targ_hist = false;
23+
const volatile bool targ_per_thread = false;
2024

2125
struct {
2226
__uint(type, BPF_MAP_TYPE_HASH);
@@ -25,6 +29,15 @@ struct {
2529
__type(value, u64);
2630
} start SEC(".maps");
2731

32+
static struct hist zero;
33+
34+
struct {
35+
__uint(type, BPF_MAP_TYPE_HASH);
36+
__uint(max_entries, MAX_ENTRIES);
37+
__type(key, u32);
38+
__type(value, struct hist);
39+
} hists SEC(".maps");
40+
2841
static int handle_tcp_probe(struct sock *sk, struct sk_buff *skb)
2942
{
3043
const struct inet_sock *inet = (struct inet_sock *)(sk);
@@ -33,9 +46,10 @@ static int handle_tcp_probe(struct sock *sk, struct sk_buff *skb)
3346

3447
if (targ_sport && targ_sport != BPF_CORE_READ(inet, inet_sport))
3548
return 0;
36-
if (targ_dport && targ_dport != BPF_CORE_READ(sk, __sk_common.skc_dport))
49+
if (targ_dport &&
50+
targ_dport != BPF_CORE_READ(sk, __sk_common.skc_dport))
3751
return 0;
38-
th = (const struct tcphdr*)BPF_CORE_READ(skb, data);
52+
th = (const struct tcphdr *)BPF_CORE_READ(skb, data);
3953
doff = BPF_CORE_READ_BITFIELD_PROBED(th, doff);
4054
len = BPF_CORE_READ(skb, len);
4155
/* `doff * 4` means `__tcp_hdrlen` */
@@ -70,26 +84,70 @@ static int handle_tcp_rcv_space_adjust(void *ctx, struct sock *sk)
7084
if (delta_us < 0 || delta_us <= targ_min_us)
7185
goto cleanup;
7286

73-
eventp = reserve_buf(sizeof(*eventp));
74-
if (!eventp)
75-
goto cleanup;
76-
77-
eventp->pid = pid;
78-
eventp->tid = tid;
79-
eventp->delta_us = delta_us;
80-
eventp->sport = BPF_CORE_READ(inet, inet_sport);
81-
eventp->dport = BPF_CORE_READ(sk, __sk_common.skc_dport);
82-
bpf_get_current_comm(&eventp->comm, TASK_COMM_LEN);
83-
family = BPF_CORE_READ(sk, __sk_common.skc_family);
84-
if (family == AF_INET) {
85-
eventp->saddr[0] = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr);
86-
eventp->daddr[0] = BPF_CORE_READ(sk, __sk_common.skc_daddr);
87-
} else { /* family == AF_INET6 */
88-
BPF_CORE_READ_INTO(eventp->saddr, sk, __sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
89-
BPF_CORE_READ_INTO(eventp->daddr, sk, __sk_common.skc_v6_daddr.in6_u.u6_addr32);
87+
if (targ_hist) {
88+
struct hist *histp;
89+
struct task_struct *current;
90+
u32 hkey;
91+
u64 slot;
92+
93+
/* Use TID for per-thread, PID (tgid) for per-process */
94+
if (targ_per_thread)
95+
hkey = tid;
96+
else
97+
hkey = pid;
98+
99+
histp = bpf_map_lookup_or_try_init(&hists, &hkey, &zero);
100+
if (!histp)
101+
goto cleanup;
102+
103+
/* Store comm if not already set */
104+
if (!histp->comm[0]) {
105+
if (targ_per_thread) {
106+
/* For per-thread, use current thread comm */
107+
bpf_get_current_comm(&histp->comm,
108+
TASK_COMM_LEN);
109+
} else {
110+
/* For per-process, use process group leader comm */
111+
current = (struct task_struct *)
112+
bpf_get_current_task();
113+
BPF_CORE_READ_STR_INTO(&histp->comm,
114+
current,
115+
group_leader,
116+
comm);
117+
}
118+
}
119+
slot = log2l(delta_us);
120+
if (slot >= MAX_SLOTS)
121+
slot = MAX_SLOTS - 1;
122+
__sync_fetch_and_add(&histp->slots[slot], 1);
123+
} else {
124+
eventp = reserve_buf(sizeof(*eventp));
125+
if (!eventp)
126+
goto cleanup;
127+
128+
eventp->pid = pid;
129+
eventp->tid = tid;
130+
eventp->delta_us = delta_us;
131+
eventp->sport = BPF_CORE_READ(inet, inet_sport);
132+
eventp->dport = BPF_CORE_READ(sk, __sk_common.skc_dport);
133+
bpf_get_current_comm(&eventp->comm, TASK_COMM_LEN);
134+
family = BPF_CORE_READ(sk, __sk_common.skc_family);
135+
if (family == AF_INET) {
136+
eventp->saddr[0] =
137+
BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr);
138+
eventp->daddr[0] =
139+
BPF_CORE_READ(sk, __sk_common.skc_daddr);
140+
} else { /* family == AF_INET6 */
141+
BPF_CORE_READ_INTO(
142+
eventp->saddr, sk,
143+
__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
144+
BPF_CORE_READ_INTO(
145+
eventp->daddr, sk,
146+
__sk_common.skc_v6_daddr.in6_u.u6_addr32);
147+
}
148+
eventp->family = family;
149+
submit_buf(ctx, eventp, sizeof(*eventp));
90150
}
91-
eventp->family = family;
92-
submit_buf(ctx, eventp, sizeof(*eventp));
93151

94152
cleanup:
95153
bpf_map_delete_elem(&start, &sock_ident);
@@ -123,7 +181,8 @@ int BPF_PROG(tcp_destroy_sock_btf, struct sock *sk)
123181
}
124182

125183
SEC("raw_tp/tcp_probe")
126-
int BPF_PROG(tcp_probe, struct sock *sk, struct sk_buff *skb) {
184+
int BPF_PROG(tcp_probe, struct sock *sk, struct sk_buff *skb)
185+
{
127186
return handle_tcp_probe(sk, skb);
128187
}
129188

0 commit comments

Comments
 (0)