BCC Python开发教程&常用BCC工具(一)

一、BCC Python开发教程

翻译参考:https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md

这个教程主要目的是展示如何使用python来进行bcc工具开发和编程。教程主要分为两个部分:可观察性和网络。

文中的代码片段均都来自于bcc:代码片段的licenses见bcc中具体文件。

也可参考bcc开发者手册reference_guide.md以及end-users工具教程: tutorial.md。此外bcc还开放有lua接口。

Lesson 1. Hello World

我们通过运行examples/hello_world.py这个例子来开启我们的学习之旅。在一个终端运行这个脚本,同时在另外一个终端运行一些命令(例如"ls")。正常的预期是在新任务运行时打印"Hello, World!",如果结果不符合预期,说明还有一些东西没有准备好:参考 INSTALL.md。

下面是hello_world.py:

BCC Python开发教程&常用BCC工具(一)_第1张图片

BCC Python开发教程&常用BCC工具(一)_第2张图片

在这里我们有6样需要学习的东西:

  1. text='...':定义一个inline BPF 程序. 这个程序使用C语言编码风格。
  2. kprobe__sys_clone(): 这是通过kprobe进行内核函数动态跟踪的快捷方法。如果C语言函数名称以"kprobe__"作为前缀,则函数名其余部分则表示将要被跟踪的内核函数接口(名), 在我们这里的场景中就是跟踪内核函数sys_clone().
  3. void *ctx: ctx本来是具体类型的参数, 但是由于我们这里没有使用这个参数,因此就将其写成void *类型。
  4. bpf_trace_printk(): 一种将信息输出到trace_pipe(/sys/kernel/debug/tracing/trace_pipe)简单机制。 在一些简单用例中这样使用没有问题, but它也有一些限制:最多3 参数; 第一个参数必须是%s(即字符串);同时trace_pipe在内核中全局共享,so 其他并行使用trace_pipe的程序有可能会将trace_pipe的输出扰乱。 一个更好的方式是通过BPF_PERF_OUTPUT(), 稍后将会讲到。
  5. return 0;:必须这样,返回0 (如果要知道why, 参考 #139  https://github.com/iovisor/bcc/issues/139)。
  6. .trace_print(): bcc提供的一个功能用以读取trace_pipe的内容输出到终端。

Lesson 2. sys_sync()

这一课我们要写一个跟踪sys_sync()内核函数的程序。这个程序会在sys_sync()函数被调用时在终端打印"sys_sync() called" 。程序写好运行起来,并在另外一个终端运行sync命令来进行测试。Lesson 1中的hello_world.py 程序基本上不用怎么修改就够用。

不过,在Lesson 1的基础上再增加一条:在跟踪程序运行时会在第一条打印输出"Tracing sys_sync()... Ctrl-C to end." 。提示:it's just Python。

BCC Python开发教程&常用BCC工具(一)_第3张图片

 

Lesson 3. hello_fields.py

这个程序在examples/tracing/hello_fields.py. 示例输出如下 (命令运行在另外一个终端):

 BCC Python开发教程&常用BCC工具(一)_第4张图片

 代码如下:

#!/usr/bin/python
#
# This is a Hello World example that formats output as fields.

from bcc import BPF
from bcc.utils import printb

# define BPF program
prog = """
int hello(void *ctx) {
bpf_trace_printk("Hello, World!\\n");
return 0;
}
"""

# load BPF program
b = BPF(text=prog)
b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello")

# header
print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE"))

# format output
while 1:
try:
    (task, pid, cpu, flags, ts, msg) = b.trace_fields()
except ValueError:
    continue
except KeyboardInterrupt:
    exit()
printb(b"%-18.9f %-16s %-6d %s" % (ts, task, pid, msg))


这个程序与hello_world.py相似也是通过sys_clone()来跟踪新任务,但是添加了一些新的学习事项:

  1. prog =: 这一次我们将C程序定义为了变量,后续通过引用这个变量的方式来使用。如果你想根据命令行参数来进行一些字符串替换,这种方式就很有用。
  2. hello(): 我们定义了一个C语言函数而非kprobe__ 快捷方式,稍后我们将会引用这个函数。所有声明在BPF程序中的C函数在跟踪函数的kprobe会被执行,因而这里的C函数需要一个pt_reg* ctx类型的首参。如果你想定义一些helper函数,但是又不希望这些函数在probe时就执行,那么需要将这些helper函数定义为static inline 这样编译器可以将其编译为inlined属性; 有时候也许你需要使用_always_inline 函数属性来实现这一效果。
  3. b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello"):为内核的clone系统调用函数添加一个kprobe点, 这样实施后在clone()函数的kprobe会执行我们定义的hello() 函数。也可以多次调用attach_kprobe() 函数在需要跟踪的内核函数的kprobe点插入你定义的kprobe跟踪函数。
  4. b.trace_fields():从trace_pipe返回一组固定字段。类似于trace_print()这个函数一般只在调试时使用,如果在正式发布的工具中应该使用BPF_PERF_OUTPUT()来代替。

你可能感兴趣的:(内核源码分析,python,开发语言)