/* * TCPSendStack Summarize tcp_sendmsg() calling stack traces. * For Linux, uses BCC, eBPF. Embedded C. * * Basic example of BCC in-kernel stack trace dedup. * * USAGE: TCPSendStack [duration] * * Copyright (c) Facebook, Inc. * Licensed under the Apache License, Version 2.0 (the "License") */ #include #include #include #include "BPF.h" const std::string BPF_PROGRAM = R"( #include #include struct stack_key_t { int pid; char name[16]; int user_stack; int kernel_stack; }; BPF_STACK_TRACE(stack_traces, 16384); BPF_HASH(counts, struct stack_key_t, uint64_t); int on_tcp_send(struct pt_regs *ctx) { struct stack_key_t key = {}; key.pid = bpf_get_current_pid_tgid() >> 32; bpf_get_current_comm(&key.name, sizeof(key.name)); key.kernel_stack = stack_traces.get_stackid(ctx, 0); key.user_stack = stack_traces.get_stackid(ctx, BPF_F_USER_STACK); u64 zero = 0, *val; val = counts.lookup_or_try_init(&key, &zero); if (val) { (*val)++; } return 0; } )"; // Define the same struct to use in user space. struct stack_key_t { int pid; char name[16]; int user_stack; int kernel_stack; }; int main(int argc, char** argv) { ebpf::BPF bpf; auto init_res = bpf.init(BPF_PROGRAM); if (!init_res.ok()) { std::cerr << init_res.msg() << std::endl; return 1; } auto attach_res = bpf.attach_kprobe("tcp_sendmsg", "on_tcp_send"); if (!attach_res.ok()) { std::cerr << attach_res.msg() << std::endl; return 1; } int probe_time = 10; if (argc == 2) { probe_time = atoi(argv[1]); } std::cout << "Probing for " << probe_time << " seconds" << std::endl; sleep(probe_time); auto detach_res = bpf.detach_kprobe("tcp_sendmsg"); if (!detach_res.ok()) { std::cerr << detach_res.msg() << std::endl; return 1; } auto table = bpf.get_hash_table("counts").get_table_offline(); std::sort( table.begin(), table.end(), [](std::pair a, std::pair b) { return a.second < b.second; }); auto stacks = bpf.get_stack_table("stack_traces"); int lost_stacks = 0; for (auto it : table) { std::cout << "PID: " << it.first.pid << " (" << it.first.name << ") " << "made " << it.second << " TCP sends on following stack: " << std::endl; if (it.first.kernel_stack >= 0) { std::cout << " Kernel Stack:" << std::endl; auto syms = stacks.get_stack_symbol(it.first.kernel_stack, -1); for (auto sym : syms) std::cout << " " << sym << std::endl; } else { // -EFAULT normally means the stack is not available and not an error if (it.first.kernel_stack != -EFAULT) { lost_stacks++; std::cout << " [Lost Kernel Stack" << it.first.kernel_stack << "]" << std::endl; } } if (it.first.user_stack >= 0) { std::cout << " User Stack:" << std::endl; auto syms = stacks.get_stack_symbol(it.first.user_stack, it.first.pid); for (auto sym : syms) std::cout << " " << sym << std::endl; } else { // -EFAULT normally means the stack is not available and not an error if (it.first.user_stack != -EFAULT) { lost_stacks++; std::cout << " [Lost User Stack " << it.first.user_stack << "]" << std::endl; } } } if (lost_stacks > 0) std::cout << "Total " << lost_stacks << " stack-traces lost due to " << "hash collision or stack table full" << std::endl; return 0; }