上面架构图展示了BCC
的执行流程:
BPF
通过python
接口.attach*
来调用底下libbpf
库的bpf_attach_*
接口来让内核挂钩事件,这些接口可以见https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md
和https://github.com/iovisor/bcc/blob/master/src/cc/libbpf.h
python
脚本的嵌入C
代码通过python
接口BPF(text=...)
到Rewriter
调用Clang/LLVM
产生BPF
字节码,再通过bpf_prog_load
接口把字节码加入到内核里,在内核里,BPF
虚拟机会调用Verifier
对代码进行校验,再由BPF
虚拟机执行。在x86
体系里,由于CPU
幽灵漏洞原因,大多会调用JIT
把它编译成机器码再执行。python
脚本会使用.print_log2_hist
之类的接口调用libbpf
库的bpf_create_map/bpf_*_elem
接口来和内核的BPF
代码进行交互,进行数据传输。见https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md
的Map APIs
章节和https://man7.org/linux/man-pages/man2/bpf.2.html
python
脚本通过.perf_buffer_poll
来调用libbpf
库的perf_reader_poll
来读取BPF
在内核里的性能缓存USDT
事件可以通过调用libbpf
的bcc_usdt_enable_probe
来打开跟踪以下是在编写自己的自定义 BCC
程序时应该注意的关于 BCC
工具开发的六个重要方面:
BPF C
(嵌入式 C
)是受限的:没有循环或内核函数调用。您只能使用 bpf_*
内核辅助函数和一些编译器内置函数。但是,如果循环有确定的次数,则可以展开循环。例如,strcmp
将不起作用,但如果知道要与其他字符串进行比较的字符串的长度,则可以解决该问题。以下是执行字符串比较的展开循环解决方法的示例:
#define MY_STR_LEN 10static inline bool equal_to_mystr(char *str) {
char comparand[MY_STR_LEN];
bpf_probe_read(&comparand, sizeof(comparand), str);
char mystr[] = "my string!";
for (int i = 0; i < MY_STR_LEN; ++i)
if (comparand[i] != mystr[i])
return false;
return true;
}
同样,不能使用 memcpy
从内存区域写入和写入内存区域。相反,必须使用 BCC
的内置函数 __builtin_memcpy(&dest, str, sizeof(dest))
。
bpf_probe_read()
读取,它会进行必要的安全检查。如果想取消引用 a->b->c->d
,可以尝试这样做,因为 BCC
有一个重写器可以将它翻译成必要的 bpf_probe_read()
。然而,显式调用 bpf_probe_read()
始终是一个安全的选择并被推荐。内存只能读到 BPF
映射的 BPF
堆栈。堆栈的大小有限,因此使用 BPF
映射来存储大型对象和/或保存大量事件的数据。BPF_PERF_OUTPUT(...)
和output_name.perf_submit(...)
:BPF_HISTOGRAM(...)
或其它BPF
映射:BPF_PERF_OUTPUT
)
bpf_trace_printk(...):bpf_trace_printk()
调用和一些跟踪器(例如 ftrace
)写入同一个公共 trace_pipe
USDT
跟踪点)而不是动态跟踪(kprobes
、uprobes
)。回想一下,动态跟踪有一个不稳定的 API
(因为我们挂钩到函数的名称,它可以随软件版本而改变),所以如果它正在跟踪的代码发生变化,你的工具就会失效。BPF C
程序中而不是在用户空间中做尽可能多的工作。与处理用户空间中的大部分工作相比,在内核中为每个事件完成工作要快得多。关键要记住,获得这种可观察性并不是没有代价。每个启用的探测/跟踪点在每次被命中时都会产生一些需要完成的工作,无论是在内核空间还是用户空间,都会产生 CPU
开销。
确定跟踪程序的CPU
开销的三个主要因素是:
一个程序在每个CPU
的开销公式如下:
overhead = (frequency * workload) / numCPUs
换句话说,在单个 CPU 上每秒跟踪 100 万个事件可能会使系统龟速,而 128 个 CPU 的系统可能几乎不受影响。
暗号:cec28