BCC (BPF Compiler Collection)

BPF Compiler Collection (BCC)是基于eBPF的Linux内核分析、跟踪、网络监控工具。其源码存放于https://github.com/iovisor/bcc

BCC包括一些列的工具

bcc - 图1

安装BCC

Ubuntu:

  1. echo "deb [trusted=yes] https://repo.iovisor.org/apt/xenial xenial-nightly main" | sudo tee /etc/apt/sources.list.d/iovisor.list
  2. sudo apt-get update
  3. sudo apt-get install -y bcc-tools libbcc-examples python-bcc

CentOS:

  1. echo -e '[iovisor]\nbaseurl=https://repo.iovisor.org/yum/nightly/f23/$basearch\nenabled=1\ngpgcheck=0' | sudo tee /etc/yum.repos.d/iovisor.repo
  2. yum install -y bcc-tools

安装完成后,bcc工具会放到/usr/share/bcc/tools目录中

  1. $ ls /usr/share/bcc/tools
  2. argdist cachestat ext4dist hardirqs offwaketime softirqs tcpconnect vfscount
  3. bashreadline cachetop ext4slower killsnoop old solisten tcpconnlat vfsstat
  4. biolatency capable filelife llcstat oomkill sslsniff tcplife wakeuptime
  5. biosnoop cpudist fileslower mdflush opensnoop stackcount tcpretrans xfsdist
  6. biotop dcsnoop filetop memleak pidpersec stacksnoop tcptop xfsslower
  7. bitesize dcstat funccount mountsnoop profile statsnoop tplist zfsdist
  8. btrfsdist doc funclatency mysqld_qslower runqlat syncsnoop trace zfsslower
  9. btrfsslower execsnoop gethostlatency offcputime slabratetop tcpaccept ttysnoop

常用工具示例

capable

capable检查Linux进程的security capabilities:

  1. $ capable
  2. TIME UID PID COMM CAP NAME AUDIT
  3. 22:11:23 114 2676 snmpd 12 CAP_NET_ADMIN 1
  4. 22:11:23 0 6990 run 24 CAP_SYS_RESOURCE 1
  5. 22:11:23 0 7003 chmod 3 CAP_FOWNER 1
  6. 22:11:23 0 7003 chmod 4 CAP_FSETID 1
  7. 22:11:23 0 7005 chmod 4 CAP_FSETID 1
  8. 22:11:23 0 7005 chmod 4 CAP_FSETID 1
  9. 22:11:23 0 7006 chown 4 CAP_FSETID 1
  10. 22:11:23 0 7006 chown 4 CAP_FSETID 1
  11. 22:11:23 0 6990 setuidgid 6 CAP_SETGID 1
  12. 22:11:23 0 6990 setuidgid 6 CAP_SETGID 1
  13. 22:11:23 0 6990 setuidgid 7 CAP_SETUID 1
  14. 22:11:24 0 7013 run 24 CAP_SYS_RESOURCE 1
  15. 22:11:24 0 7026 chmod 3 CAP_FOWNER 1
  16. [...]

tcpconnect

tcpconnect检查活跃的TCP连接,并输出源和目的地址:

  1. $ ./tcpconnect
  2. PID COMM IP SADDR DADDR DPORT
  3. 2462 curl 4 192.168.1.99 74.125.23.138 80

tcptop

tcptop统计TCP发送和接受流量:

  1. $ ./tcptop -C 1 3
  2. Tracing... Output every 1 secs. Hit Ctrl-C to end
  3. 08:06:45 loadavg: 0.04 0.01 0.00 2/174 3099
  4. PID COMM LADDR RADDR RX_KB TX_KB
  5. 1740 sshd 192.168.1.99:22 192.168.0.29:60315 0 0
  6. 08:06:46 loadavg: 0.04 0.01 0.00 2/174 3099
  7. PID COMM LADDR RADDR RX_KB TX_KB
  8. 1740 sshd 192.168.1.99:22 192.168.0.29:60315 0 0
  9. 08:06:47 loadavg: 0.04 0.01 0.00 2/174 3099
  10. PID COMM LADDR RADDR RX_KB TX_KB
  11. 1740 sshd 192.168.1.99:22 192.168.0.29:60315 0 0

扩展工具

基于eBPF和bcc,可以很方便的扩展功能。bcc目前支持以下事件

  • kprobe__kernel_function_name (BPF.attach_kprobe())
  • kretprobe__kernel_function_name (BPF.attach_kretprobe())
  • TRACEPOINT_PROBE(category, event),支持的event列表参见/sys/kernel/debug/tracing/events/category/event/format
  • BPF.attach_uprobe()和BPF.attach_uretprobe()
  • 用户自定义探针(USDT) USDT.enable_probe()

简单示例

  1. #!/usr/bin/env python
  2. from __future__ import print_function
  3. from bcc import BPF
  4. text='int kprobe__sys_sync(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; }'
  5. prog="""
  6. int hello(void *ctx) {
  7. bpf_trace_printk("Hello, World!\\n");
  8. return 0;
  9. }
  10. """
  11. b = BPF(text=prog)
  12. b.attach_kprobe(event="sys_clone", fn_name="hello")
  13. print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE"))
  14. while True:
  15. try:
  16. (task, pid, cpu, flags, ts, msg) = b.trace_fields()
  17. except ValueError:
  18. continue
  19. print("%-18.9f %-16s %-6d %s" % (ts, task, pid, msg))

使用BPF_PERF_OUTPUT

  1. from __future__ import print_function
  2. from bcc import BPF
  3. import ctypes as ct
  4. # load BPF program
  5. b = BPF(text="""
  6. struct data_t {
  7. u64 ts;
  8. };
  9. BPF_PERF_OUTPUT(events);
  10. void kprobe__sys_sync(void *ctx) {
  11. struct data_t data = {};
  12. data.ts = bpf_ktime_get_ns() / 1000;
  13. events.perf_submit(ctx, &data, sizeof(data));
  14. };
  15. """)
  16. class Data(ct.Structure):
  17. _fields_ = [
  18. ("ts", ct.c_ulonglong)
  19. ]
  20. # header
  21. print("%-18s %s" % ("TIME(s)", "CALL"))
  22. # process event
  23. def print_event(cpu, data, size):
  24. event = ct.cast(data, ct.POINTER(Data)).contents
  25. print("%-18.9f sync()" % (float(event.ts) / 1000000))
  26. # loop with callback to print_event
  27. b["events"].open_perf_buffer(print_event)
  28. while True:
  29. b.kprobe_poll()

更多的示例参考https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md

用户自定义探针示例

  1. from __future__ import print_function
  2. from bcc import BPF
  3. from time import strftime
  4. import ctypes as ct
  5. # load BPF program
  6. bpf_text = """
  7. #include <uapi/linux/ptrace.h>
  8. struct str_t {
  9. u64 pid;
  10. char str[80];
  11. };
  12. BPF_PERF_OUTPUT(events);
  13. int printret(struct pt_regs *ctx) {
  14. struct str_t data = {};
  15. u32 pid;
  16. if (!PT_REGS_RC(ctx))
  17. return 0;
  18. pid = bpf_get_current_pid_tgid();
  19. data.pid = pid;
  20. bpf_probe_read(&data.str, sizeof(data.str), (void *)PT_REGS_RC(ctx));
  21. events.perf_submit(ctx,&data,sizeof(data));
  22. return 0;
  23. };
  24. """
  25. STR_DATA = 80
  26. class Data(ct.Structure):
  27. _fields_ = [
  28. ("pid", ct.c_ulonglong),
  29. ("str", ct.c_char * STR_DATA)
  30. ]
  31. b = BPF(text=bpf_text)
  32. b.attach_uretprobe(name="/bin/bash", sym="readline", fn_name="printret")
  33. # header
  34. print("%-9s %-6s %s" % ("TIME", "PID", "COMMAND"))
  35. def print_event(cpu, data, size):
  36. event = ct.cast(data, ct.POINTER(Data)).contents
  37. print("%-9s %-6d %s" % (strftime("%H:%M:%S"), event.pid, event.str))
  38. b["events"].open_perf_buffer(print_event)
  39. while 1:
  40. b.kprobe_poll()
  1. # ./bashreadline
  2. TIME PID COMMAND
  3. 08:22:44 2070 ls /
  4. 08:22:56 2070 ping -c3 google.com
  5. # ./gethostlatency
  6. TIME PID COMM LATms HOST
  7. 08:23:26 3370 ping 2.00 google.com
  8. 08:23:37 3372 ping 56.00 baidu.com

参考文档