• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * TCPSendStack Summarize tcp_sendmsg() calling stack traces.
3  *              For Linux, uses BCC, eBPF. Embedded C.
4  *
5  * Basic example of BCC in-kernel stack trace dedup.
6  *
7  * USAGE: TCPSendStack [duration]
8  *
9  * Copyright (c) Facebook, Inc.
10  * Licensed under the Apache License, Version 2.0 (the "License")
11  */
12 
13 #include <unistd.h>
14 #include <algorithm>
15 #include <iostream>
16 
17 #include "BPF.h"
18 
19 const std::string BPF_PROGRAM = R"(
20 #include <linux/sched.h>
21 #include <uapi/linux/ptrace.h>
22 
23 struct stack_key_t {
24   int pid;
25   char name[16];
26   int user_stack;
27   int kernel_stack;
28 };
29 
30 BPF_STACK_TRACE(stack_traces, 16384);
31 BPF_HASH(counts, struct stack_key_t, uint64_t);
32 
33 int on_tcp_send(struct pt_regs *ctx) {
34   struct stack_key_t key = {};
35   key.pid = bpf_get_current_pid_tgid() >> 32;
36   bpf_get_current_comm(&key.name, sizeof(key.name));
37   key.kernel_stack = stack_traces.get_stackid(ctx, 0);
38   key.user_stack = stack_traces.get_stackid(ctx, BPF_F_USER_STACK);
39 
40   u64 zero = 0, *val;
41   val = counts.lookup_or_init(&key, &zero);
42   (*val)++;
43 
44   return 0;
45 }
46 )";
47 
48 // Define the same struct to use in user space.
49 struct stack_key_t {
50   int pid;
51   char name[16];
52   int user_stack;
53   int kernel_stack;
54 };
55 
main(int argc,char ** argv)56 int main(int argc, char** argv) {
57   ebpf::BPF bpf;
58   auto init_res = bpf.init(BPF_PROGRAM);
59   if (init_res.code() != 0) {
60     std::cerr << init_res.msg() << std::endl;
61     return 1;
62   }
63 
64   auto attach_res = bpf.attach_kprobe("tcp_sendmsg", "on_tcp_send");
65   if (attach_res.code() != 0) {
66     std::cerr << attach_res.msg() << std::endl;
67     return 1;
68   }
69 
70   int probe_time = 10;
71   if (argc == 2) {
72     probe_time = atoi(argv[1]);
73   }
74   std::cout << "Probing for " << probe_time << " seconds" << std::endl;
75   sleep(probe_time);
76 
77   auto detach_res = bpf.detach_kprobe("tcp_sendmsg");
78   if (detach_res.code() != 0) {
79     std::cerr << detach_res.msg() << std::endl;
80     return 1;
81   }
82 
83   auto table =
84       bpf.get_hash_table<stack_key_t, uint64_t>("counts").get_table_offline();
85   std::sort(
86       table.begin(), table.end(),
87       [](std::pair<stack_key_t, uint64_t> a,
88          std::pair<stack_key_t, uint64_t> b) { return a.second < b.second; });
89   auto stacks = bpf.get_stack_table("stack_traces");
90 
91   int lost_stacks = 0;
92   for (auto it : table) {
93     std::cout << "PID: " << it.first.pid << " (" << it.first.name << ") "
94               << "made " << it.second
95               << " TCP sends on following stack: " << std::endl;
96     if (it.first.kernel_stack >= 0) {
97       std::cout << "  Kernel Stack:" << std::endl;
98       auto syms = stacks.get_stack_symbol(it.first.kernel_stack, -1);
99       for (auto sym : syms)
100         std::cout << "    " << sym << std::endl;
101     } else {
102       // -EFAULT normally means the stack is not availiable and not an error
103       if (it.first.kernel_stack != -EFAULT) {
104         lost_stacks++;
105         std::cout << "    [Lost Kernel Stack" << it.first.kernel_stack << "]"
106                   << std::endl;
107       }
108     }
109     if (it.first.user_stack >= 0) {
110       std::cout << "  User Stack:" << std::endl;
111       auto syms = stacks.get_stack_symbol(it.first.user_stack, it.first.pid);
112       for (auto sym : syms)
113         std::cout << "    " << sym << std::endl;
114     } else {
115       // -EFAULT normally means the stack is not availiable and not an error
116       if (it.first.user_stack != -EFAULT) {
117         lost_stacks++;
118         std::cout << "    [Lost User Stack " << it.first.user_stack << "]"
119                   << std::endl;
120       }
121     }
122   }
123 
124   if (lost_stacks > 0)
125     std::cout << "Total " << lost_stacks << " stack-traces lost due to "
126               << "hash collision or stack table full" << std::endl;
127 
128   return 0;
129 }
130