09月21, 2022

bpftrace 入门(5) 内置函数

bpftrace 无法定义函数,但提供了约 30 个内置函数,可以在 bpftrace 脚本的任意位置调用它们。这里介绍几个最常用的内置函数,完整的列表可以参考官方文档(https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md)

printf

熟悉 C 语言的同学会很熟悉,bpftrace 的 printf 函数行为与 C 语言基本一致,区别在于,它只支持有限的格式化字符,不如 C 语言支持的那么多。

printf(fmt, ...)
  • fmt,格式化字符串,它决定了以什么格式输出内容,也决定了可变参数的数量和类型;
  • 数量和类型可变的参数。

下面是一个官方示例:

# bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s called %s\n", comm, str(args->filename)); }'
Attaching 1 probe...
bash called /bin/ls
bash called /usr/bin/man

代码中的两个 %s 表示后续还有 2 个参数,并且它们都是字符串。输出时,%s 会被替换为这两个字符串。

一般可以借助 printf 将需要的内容打印到标准输出。

time

time 函数用于打印当前时间,可以通过参数中的格式化字符串指定,如果没有指定格式化字符串,那么默认个时是%H:%M:%S\n.

time(fmt)

time 函数完全兼容 strftime 的格式化字符,下面列出一些常用项,这里有完整的格式化字符清单(https://linux.die.net/man/3/strftime)。

  • %S 秒,00-60;
  • %M 分钟,00-59;
  • %I 小时,01-12;%H 小时,00-23;
  • %d 每月的第几天,01-31;
  • %w 星期,0-6, 0 指 星期日;
  • %m 月份,01-12;
  • %y 年份,00-99;%Y 完整的年份;

注意: 格式化字符结尾不要忘记换行,否则不会自动清空缓冲区到标准输出,就看不到输出了。

下面通过定时器,每秒输出当前时间,注意格式化字符尾部的 \n

# bpftrace -e 'interval:s:1 {time("%Y %H:%M:%S\n");}'
Attaching 1 probe...
2022 01:39:13
2022 01:39:14
2022 01:39:15

system

该函数可以调用 shell,用于 probe 触发其他用户态可执行程序非常有用。

下面是一个简单的例子,定时调用 `ps. 查看当前进程:

# bpftrace --unsafe -e 'kprobe:do_nanosleep { system("ps -p %d\n", pid); }'
Attaching 1 probe...
  PID TTY          TIME CMD
 1339 ?        00:00:15 iscsid
  PID TTY          TIME CMD
 1339 ?        00:00:15 iscsid

ustack

当使用 uprobe 时,很可能需要关注用户进程的 stack 情况,ustack 函数接受 2 个参数:

  • mode,stack 模式,可选 bpftrace、perf;
  • limit,一个整数,获取 stack 的最大深度;

这两个参数可以同时使用,或者只用 1 个。下面是一个官方示例:

# bpftrace -e 'uprobe:bash:readline { printf("%s\n", ustack(perf, 3)); }'
Attaching 1 probe...

    5649feec4090 readline+0 (/home/mmarchini/bash/bash/bash)
    5649fee2bfa6 yy_readline_get+451 (/home/mmarchini/bash/bash/bash)
    5649fee2bdc6 yy_getc+13 (/home/mmarchini/bash/bash/bash)

本文链接:http://www.thinkinpython.com/post/bpftrace_tutorial_5.html

-- EOF --