bpftrace参考指南

目录

  • 前言
  • 一、名词解释
  • 二、使用示范
    • 1. help
    • 2. Hello World
    • 3. One-Liners程序
    • 4. 列出可跟踪点
    • 5. 调试输出-d
    • 6. 输出详情
    • 7. 预处理选项
    • 8. 环境变量
    • 9. 其它选项
  • 三、语法
    • 1. 程序结构{...}
    • 2. 过滤/.../
    • 3. 注释//, /**/
    • 4. 常量
    • 5. c结构体访问:->
    • 6. 结构体定义: struct
    • 7. 三元操作符 ?::
    • 8. 条件语句 if () {..} else {..}
    • 9. 循环语句unroll
    • 10. 自增、自减++、--
    • 11. 数组访问[]
    • 12. 整形强转
    • 13. while循环
    • 14. 提前结束:return
    • 15. 元组( ,)
  • 四、探针类型
    • 1. kprobe/kretprobe
    • 2. uprobe/uretprobe
    • 3. tracepoint
    • 4. usdt
    • 5. profile
    • 6. interval
    • 7. software
    • 8. hardware
  • 五、变量
    • 1. 内置变量
    • 2. 基本变量: @、$
    • 3. 关联数组@[ ]
    • 5. 时间戳nsecs
    • 6. 内核栈kstack
    • 7. 用户栈追踪ustack
    • 8. 位置参数
  • 六、函数
    • 1. 内置函数
    • 2. printf():格式化打印
    • 3. time():打印时间
    • 4. join():打印字符串数组
    • 5. str():打印字符串
    • 6. ksym()
    • 7. usym()
    • 8. kaddr()
    • 9. uaddr()
    • 10. reg()
    • 11. buf(void *d, int len)
    • 12. system()
    • 13. exit()
    • 14. cgroupid()
    • 15. ntop()
    • 16. kstack()
    • 17. ustack():
    • 18. cat():查看文件内容
    • 19. signal():向当前进程发送一个信号
    • 20. strncmp():字符串比较
    • 21. override():重写返回值
    • 22. sizeof()
    • 23. print()
    • 24. strftime():格式化时间戳
    • 25. path():返回完整路径
    • 26. uptr():注解用户地址
    • 27. kptr():注解内核地址
    • 28. macaddr():将mac地址转为文本
    • 29. cgroup_path():将cgroupid转为路径
  • 七、映射表操作函数
    • 1. 内置函数
    • 2. count()
    • 3. sum()
    • 4. avg()
    • 5. min()
    • 6. max()
    • 7. stats()
    • 8. hist()
    • 9. lhist()
    • 10. print()


前言

本文主要介绍bpftrace的使用及语法规则,主要内容来自于官网的文档,以及使用过程中遇到的一些问题;本文将不涉及ebpf概念、框架介绍等。


一、名词解释

术语 简介
BPF 伯克利数据包过滤器:最初开发用于优化数据包过滤器处理的核心技术(例如,tcpdump表达式)
eBPF 增强型BPF:一种扩展BPF的内核技术,它可以在任何事件上执行更通用的程序,如bpftrace程序,eBPF通常被称为BPF
probe 软件或硬件中的一种插装点,用于生成可执行bpftrace程序的事件
tracepoints 内核用于提供静态插桩点的技术
kprobes 内核用于进行函数动态追踪的技术
uprobes 内核用于动态追踪用户态函数调用的技术
USDT 用户程序自己定义的静态插桩点(User Statically-Defined Tracing)
BPF map BPF的内存对象,bpftrace用它来创建一些高级对象
BTF BPF类型格式:对与BPF程序/映射相关的调试信息进行编码的元数据格式。

二、使用示范

1. help

使用bpftrace命令行输出帮助信息(bpftrace或bpftrace --help)

# bpftrace
USAGE:
    bpftrace [options] filename
    bpftrace [options] -e 'program'

OPTIONS:
    -B MODE        output buffering mode ('line', 'full', or 'none')
    -d             debug info dry run
    -dd            verbose debug info dry run
    -e 'program'   execute this program
    -h             show this help message
    -I DIR         add the specified DIR to the search path for include files.
    --include FILE adds an implicit #include which is read before the source file is preprocessed.
    -l [search]    list probes
    ...

2. Hello World

注: bpftrace的执行(bpf程序的注入)需要root用户。

#bpftrace -e 'BEGIN { printf("hello world!\n"); }'
Attaching 1 probe...
hello world!

3. One-Liners程序

使用-e选项指定一个程序,用于构造单行程序,类似awk语法,下例打印了进入睡眠状态的进程:

# bpftrace -e 'tracepoint:syscalls:sys_enter_nanosleep { printf("%s enter sleeping\n", comm); }'
Attaching 1 probe...
GoImcore enter sleeping
GoImcore enter sleeping
GoImcore enter sleeping

4. 列出可跟踪点

使用-l选项列出当前可用追踪点

# bpftrace -l | more
software:alignment-faults:
software:bpf-output:
software:context-switches:

可使用通配符进行查询

# bpftrace -l '*sys_enter*' | more
tracepoint:syscalls:sys_enter_socket
tracepoint:syscalls:sys_enter_socketpair
tracepoint:syscalls:sys_enter_bind
tracepoint:syscalls:sys_enter_listen

使用-v选项可以列出tracepoint类型跟踪点的参数

# bpftrace -lv tracepoint:syscalls:sys_enter_shmctl
tracepoint:syscalls:sys_enter_shmctl
    int __syscall_nr;
    int shmid;
    int cmd;
    struct shmid_ds * buf;

如果BTF可用(内核选项CONFIG_DEBUG_INFO_BTF=y,查看有无/sys/kernel/btf/vmlinux验证),也可以查看结构体的定义,如:

# bpftrace -lv "struct path"
struct path {
        struct vfsmount *mnt;
        struct dentry *dentry;
};

5. 调试输出-d

可以使用-d选项调试bpftrace程序,此时程序不会运行,可以使用```-dd``获得更多调试信息:

# bpftrace -d -e 'tracepoint:syscalls:sys_enter_nanosleep { printf("%s enter sleeping\n", comm); }'
#include 

Program
 tracepoint:syscalls:sys_enter_nanosleep
  call: printf
   string: %s enter sleeping\n
   builtin: comm

; ModuleID = 'bpftrace'
source_filename = "bpftrace"

6. 输出详情

使用-v选项获得更多程序运行时的信息:

# bpftrace -v -e 'tracepoint:syscalls:sys_enter_nanosleep { printf("%s enter sleeping\n", comm); }'
Attaching 1 probe...

Program ID: 85
Bytecode: 
0: (bf) r6 = r1
1: (b7) r1 = 0
2: (7b) *(u64 *)(r10 -40) = r1
last_idx 2 first_idx 0
regs=2 stack=0 before 1: (b7) r1 = 0
3: (7b) *(u64 *)(r10 -32) = r1
4: (7b) *(u64 *)(r10 -24) = r1
5: (7b) *(u64 *)(r10 -16) = r1
6: (7b) *(u64 *)(r10 -8) = r1
7: (bf) r1 = r10
8: (07) r1 += -16
9: (b7) r2 = 16
...

7. 预处理选项

使用 -I选项帮助bpftrace程序寻找头文件位置(与gcc相似),使用--include选项包含头文件,可多次使用:

#bpftrace -I /tmp/include test.bt

# bpftrace -e 'kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); }'
stdin:1:45-66: ERROR: Unknown struct/union: 'struct path'
kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); }
# bpftrace --include linux/path.h --include linux/dcache.h \
  -e 'kprobe:vfs_open { printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name)); }'
Attaching 1 probe...
open path: /
open path: status
open path: status

8. 环境变量

使用示例:

# BPFTRACE_MAP_KEYS_MAX=1024 bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s", comm); join(args->argv); }'
Attaching 1 probe...
  1. BPFTRACE_STRLEN
    默认值64,使用str()获取BPF stack分配的字符串时返回的长度,当前可以设置的最大值为200,支持更大字符长度的问题仍在讨论中。
  2. BPFTRACE_NO_CPP_DEMANGLE
    默认为0,默认启用了用户空间堆栈跟踪中的C++符号还原功能,将此环境变量设置为1,可以关闭此功能。
  3. BPFTRACE_MAP_KEYS_MAX
    单个map中存储的最大key数量,默认4096。
  4. BPFTRACE_MAX_PROBES
    bpftrace程序支持attach的钩子数量,默认512。
  5. BPFTRACE_CACHE_USER_SYMBOLS
    默认情况下bpftrace缓存符号的解析结果,如果ASLR没有开启(Address Space Layout Randomization),仅仅跟踪一个程序的时候,开启此选项可以获得性能上的提升。
  6. BPFTRACE_BTF
    BTF文件的路径,默认为None
  7. BPFTRACE_MAX_BPF_PROGS
    bpftrace可构造的最大BPF程序数量,默认值为512.

9. 其它选项

  1. 使用--version获取版本信息
  2. 使用--no-warnings关闭警告
  3. 使用-f选项指定输出信息格式,比如json
# bpftrace -f json -e 'tracepoint:syscalls:sys_enter_nanosleep { printf("%s enter sleeping\n", comm); }'
{"type": "attached_probes", "data": {"probes": 1}}
{"type": "printf", "data": "GoImcore enter sleeping\n"}
{"type": "printf", "data": "GoImcore enter sleeping\n"}
  1. 使用-o输出到文本
# bpftrace -f json -o ./sleep.json -e 'tracepoint:syscalls:sys_enter_nanosleep { printf("%s enter sleeping\n", comm); }'
^C
# cat sleep.json 
{"type": "attached_probes", "data": {"probes": 1}}
{"type": "printf", "data": "GoImcore enter sleeping\n"}
{"type": "printf", "data": "GoImcore enter sleeping\n"}

三、语法

1. 程序结构{…}

格式:probe[, probe, ...] /filter/ { action }
一个bpftrace程序可以有多个动作块,可使用过滤器。

# bpftrace -e 'kprobe:do_sys_open { printf("opening: %s\n", str(arg1)); }'
Attaching 1 probe...
opening: /proc/1804/cmdline
...

2. 过滤/…/

格式: /filter/
在探针之后添加过滤器,探针仍然会触发,在满足过滤条件之后才会执行动作。

# bpftrace -e 'kprobe:vfs_read /comm == "bash"/ { printf("read %d bytes\n", arg2); }'
Attaching 1 probe...
read 256 bytes
read 728 bytes

3. 注释//, /**/

// single-line comment
/*
 * multi-line comment
 */

4. 常量

支持整数、字符和字符串常量:

# bpftrace -e 'BEGIN { printf("%lu %lu %lu", 1000000, 1e6, 1_000_000)}'
Attaching 1 probe...
1000000 1000000 1000000

5. c结构体访问:->

# bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
Attaching 1 probe...
Xorg /proc/1996/cmdline

tracepoint类型的跟踪点可使用args参数中访问filename成员,通过args->格式;如果是kprobe类型跟踪点,则访问示例如下:

# cat path.bt
#!/usr/bin/bpftrace
#include 
#include 

kprobe:vfs_open
{
	printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name));
}

# bpftrace path.bt
Attaching 1 probe...
open path: dev
open path: if_inet6
open path: retrans_time_ms

使用了动态跟踪点对内核函数vfs_open进行了追踪,为了访问path和dentry结构,需要包含一些内核头文件。

6. 结构体定义: struct

// from fs/namei.c:
struct nameidata {
        struct path     path;
        struct qstr     last;
        // [...]
};

一些情况下,内核的头文件包中没有包含需要的结构体,你可以在bpftrace工具中手动定义结构体。

7. 三元操作符 ?::

语法同C语言,如下:

# bpftrace -e 'tracepoint:syscalls:sys_exit_read { @error[args->ret < 0 ? - args->ret : 0] = count(); }'
Attaching 1 probe...
^C

@error[11]: 51
@error[0]: 1744

8. 条件语句 if () {…} else {…}

bpftrace条件语句中目前仅支持if/else,暂不支持else if:

# bpftrace -e 'tracepoint:syscalls:sys_enter_read { @read = count(); if (args->count > 1024) { @large = count(); } }'
Attaching 1 probe...
^C

@large: 240

@read: 1206

9. 循环语句unroll

使用unroll()对语句进行循环执行

# bpftrace -e 'kprobe:do_nanosleep { $i = 1; unroll(5) { printf("i:%d\n", $i); $i = $i + 1; } }'
Attaching 1 probe...
i:1
i:2
i:3
i:4
i:5

10. 自增、自减++、–

++--可以用于maps或者变量的自增/自减,需要注意的是maps没有定义的话值会被隐式的初始化为0。变量需要初始化之后才能使用这些操作符。

# bpftrace -e 'BEGIN { $x = 0; $x++; printf("x:%d\n", $x); }'
Attaching 1 probe...
x:1
# bpftrace -e 'k:vfs_read { @++ }'
Attaching 1 probe...
^C
@: 633

带关键词的map:

# bpftrace -e 'k:vfs_read { @[probe]++ }'
Attaching 1 probe...
^C

@[kprobe:vfs_read]: 131

11. 数组访问[]

可以使用数组操作符[]访问一维常量数组;

12. 整形强转

整形内部为uint64,可以强制修改为以下内置类型:
(u)int8,(u)int16,(u)int32,(u)int64:

# bpftrace -e 'BEGIN { $x = 1<<16; printf("%d %d\n", (uint16)$x, $x); }'
Attaching 1 probe...
0 65536

13. while循环

内核版本>=5.3,bpftrace支持while循环,循环可以使用continuebreak来操作:

# bpftrace -e 'i:ms:100 { $i = 0; while ($i <= 100) { printf("%d ", $i); $i++} exit(); }'

14. 提前结束:return

return关键字用于提前结束probe,而exit()则用于退出bpftrace(包含一个或多个probe)。

15. 元组( ,)

使用.+index来访问元组,元组一经定义就不可以改变,同样也需要高版本内核支持:

# bpftrace -e 'BEGIN { $t = (1, 2, "string"); printf("%d %s\n", $t.1, $t.2); }'

四、探针类型

1. kprobe/kretprobe

语法:

kprobe:function_name[ + offset]
kretprobe:function_name

使用了内核的kprobe能力(https://www.kernel.org/doc/Documentation/kprobes.txt),在进入函数时触发kprobe,函数退出时触发kretprobe。
示例:

# bpftrace -e 'kprobe:do_nanosleep { printf("%s enter sleep\n", comm); }'
Attaching 1 probe...
dockerd enter sleep

也可以在probe函数内部使用偏移量:

# gdb -q /usr/lib/debug/boot/vmlinux-`uname -r` --ex 'disassemble do_sys_open'
Reading symbols from /usr/lib/debug/boot/vmlinux-5.0.0-32-generic...done.
Dump of assembler code for function do_sys_open:
   0xffffffff812b2ed0 <+0>:     callq  0xffffffff81c01820 <__fentry__>
   0xffffffff812b2ed5 <+5>:     push   %rbp
   0xffffffff812b2ed6 <+6>:     mov    %rsp,%rbp
   0xffffffff812b2ed9 <+9>:     push   %r15
...
# bpftrace -e 'kprobe:do_sys_open+9 { printf("in here\n"); }'
Attaching 1 probe...
in here
...

如果地址与指令边界和函数内的地址一致,则使用vmlinux(带调试符号)检查地址;如果bpftrace编译的时候使用了ALLOW_UNSAFE_PROBE选项,可以使用–unsafe选项来跳过此检查。
对于probe类型探针,可以使用argN(从0开始)的方式来访问探测点参数,对于retprobe则使用retval来获取返回值。

# bpftrace -e 'kprobe:do_sys_open { printf("open flags: %d\n", arg2); }'
Attaching 1 probe...
open flags: 557056
open flags: 32768
[...]
# bpftrace -e 'kretprobe:do_sys_open { printf("returned: %d\n", retval); }'
Attaching 1 probe...
returned: 8
[...]

对于结构体的访问如下:

# cat path.bt 
#!/usr/bin/bpftrace

#include 
#include 

kprobe:vfs_open
{
	printf("open path: %s\n", str(((struct path *)arg0)->dentry->d_name.name));
}

当内核支持BTF时,甚至不需要包含结构体的头文件。

2. uprobe/uretprobe

语法

uprobe:library_name:function_name[+offset]
uprobe:library_name:address
uretprobe:library_name:function_name

使用了内核的uprobe特性,可以用objdump或者bpftrace -l来获取探测点。

# objdump -tT /bin/bash | grep readline
0000000000139220 g    DO .bss	0000000000000008  Base        rl_readline_state
00000000000c0b20 g    DF .text	0000000000000352  Base        readline_internal_char
00000000000bfe90 g    DF .text	000000000000019c  Base        readline_internal_setup
000000000008bf40 g    DF .text	000000000000009a  Base        posix_readline_initialize
# # bpftrace -l 'u:/bin/bash' | grep readline
uprobe:/bin/bash:initialize_readline
uprobe:/bin/bash:pcomp_set_readline_variables
uprobe:/bin/bash:posix_readline_initialize
uprobe:/bin/bash:readline

uprobe也可以使用虚拟地址作为探测点:

# objdump -tT /bin/bash | grep main
000000000002fe90 g    DF .text	000000000000199e  Base        main
# bpftrace -e 'uprobe:/bin/bash:0x2fe90 { printf("main called!\n"); }'
Attaching 1 probe...

也可以使用探测点加上偏移的方式:

# objdump -d /root/test
[...]
0000000000401132 :
  401132:	55                   	push   %rbp
  401133:	48 89 e5             	mov    %rsp,%rbp
  401136:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  40113a:	48 89 75 f0          	mov    %rsi,-0x10(%rbp)
  40113e:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
[...]
# bpftrace -e 'u:/root/test:foo+4 {printf("in here\n");}'
Attaching 1 probe...
in here
in here

地址的对齐会通过指令边界进行检查,如果不对齐,将会probe将会添加失败,如果bpftrace编译时使用了ALLOW_UNSAFE_PROBE选项,也可以使用–unsafe选项来跳过此检查。

# bpftrace -e 'uprobe:/bin/bash:main+1 { printf("in here\n"); }'
Attaching 1 probe...
Could not add uprobe into middle of instruction: /bin/bash:main+1
# bpftrace -e 'uprobe:/bin/bash:main+1 { printf("in here\n"); } --unsafe'
Attaching 1 probe...
Unsafe uprobe in the middle of the instruction: /bin/bash:main+1

使用–unsafe选项,还可以在任意地址上放置uprobes。当二进制文件被strip时,这可能会派上用场。

3. tracepoint

使用了内核的静态探测点,对于参数的访问方式为args->

# bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
Attaching 1 probe...
vmware-vmx /proc/meminfo

每个跟踪点可用的成员可以在/sys目录下进行查看:

# cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/format
name: sys_enter_openat
ID: 622
format:
        field:unsigned short common_type;       offset:0;       size:2; signed:0;
        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
        field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;
        field:int common_pid;   offset:4;       size:4; signed:1;

        field:int __syscall_nr; offset:8;       size:4; signed:1;
        field:int dfd;  offset:16;      size:8; signed:0;
        field:const char * filename;    offset:24;      size:8; signed:0;
        field:int flags;        offset:32;      size:8; signed:0;
        field:umode_t mode;     offset:40;      size:8; signed:0;

print fmt: "dfd: 0x%08lx, filename: 0x%08lx, flags: 0x%08lx, mode: 0x%08lx", ((unsigned long)(REC->dfd)), ((unsigned long)(REC->filename)), ((unsigned long)(REC->flags)), ((unsigned long)(REC->mode))

4. usdt

USDT(user-level statically defined tracing),提供了用户空间版的跟踪点机制,linux对USDT的支持,最早来自于SytemTap项目的跟踪器;给用户程序添加USDT探针,有两种可选方式:
1)使用systemtap-sdt-dev包提供的头文件和工具
2)使用Facebook的Folly C++库

为应用程序添加USDT后,可使用bpftrace对跟踪点进行探测,语法:

usdt:binary_path:probe_name
usdt:binary_path:[probe_namespace]:probe_name
usdt:library_path:probe_name
usdt:library_path:[probe_namespace]:probe_name

如果探测名称是唯一的,也可以省略探测命名空间:

# bpftrace -e 'usdt:/root/tick:loop { printf("hi\n"); }'
Attaching 1 probe...
hi
hi
hi

参数使用argN进行访问:

# bpftrace -e 'usdt:/root/tick:loop /arg1 > 2/ { printf("%s: %d\n", str(arg0), arg1); }'
my string: 3
my string: 4
my string: 5
my string: 6
^C

5. profile

使用profile进行事件采样:

profile:hz:rate
profile:s:rate
profile:ms:rate
profile:us:rate

profile使用了perf_events能力,如:

# bpftrace -e 'profile:hz:99 { @[tid] = count(); }'
Attaching 1 probe...
^C

@[1280]: 1
@[866]: 1
@[58278]: 1

6. interval

语法:

interval:ms:rate
interval:s:rate
interval:us:rate
interval:hz:rate

这只在一个CPU上启动,并可用于生成每间隔的输出,如每秒输出系统调用的数量:

# bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @syscalls = count(); } interval:s:1 { print(@syscalls); clear(@syscalls); }'
Attaching 2 probes...
@syscalls: 18141

@syscalls: 34272

@syscalls: 48646

7. software

语法:

software:event_name:count
software:event_name:

这些是Linux内核提供的预定义软件事件,通常通过perf实用程序进行跟踪。它们类似于跟踪点,但只有十几个,记录在perf_event_open(2)手册页中。事件名称如下:

  • cpu-clockcpu:报告CPU时钟,per-cpu的高分辨率定时器
  • task-clock:指定正在运行的任务的时钟计数
  • page-faults or faults:报告触发缺页异常的次数
  • context-switches or cs:上下文切换,被报告为在内核发生的用户空间事件
  • cpu-migrations:报告进程迁移CPU的次数
  • minor-faults:报告小缺页中断(触发pagefault时,vma对应的地址空间存在disk中)的次数,不会上报磁盘I/O的情况
  • major-faults:报告大缺页中断(触发pagefault时,vma对应的地址空间已经被内核加载到了Page Cache中)的次数
  • alignment-faults:报告对齐异常的数量,某些架构支持,当发生未对齐的内存访问时触发
  • emulation-faults:统计方针异常的数量,内核有时会捕获未实现的指令,并在用户空间模拟它们
  • dummy:一个不重要的占位事件,允许在不需要计数事件的情况下收集此类记录
  • bpf-output
    下例对每一百个缺页异常的进程名称进行采样:
# bpftrace -e 'software:faults:100 { @[comm] = count(); }'
Attaching 1 probe...
^C

@[QThread]: 1
@[ping]: 1

8. hardware

语法:

hardware:event_name:count
hardware:event_name:

Linux内核提供的预定义硬件事件,通常由perf实用程序跟踪。它们是使用性能监视计数器(PMC)实现的:处理器上的硬件资源。记录在perf_event_open(2)手册页(https://man7.org/linux/man-pages/man2/perf_event_open.2.html)中,事件名称如下:

  • cpu-cycles or cycles
  • instructions
  • cache-references
  • cache-misses
  • branch-instructions or branches
  • branch-misses
  • bus-cycles
  • frontend-stalls
  • backend-stalls
  • ref-cycles
    计数是探测器的触发器,它将为每个计数事件触发一次。如果未提供计数,则使用默认值。
# bpftrace -e 'hardware:cache-misses:1000000 { @[pid] = count(); }'
Attaching 1 probe...
^C

@[7679]: 1
@[2662]: 1
@[400842]: 1

五、变量

1. 内置变量

  • pid - 进程号(kernel tgid)
  • tid - 线程号 (kernel pid)
  • uid - 用户ID
  • gid - 组ID
  • nsecs - 纳秒时间戳
  • elapsed - 自bpftrace初始化流逝的纳秒数
  • cpu - 处理器编号
  • comm - 进程名称
  • kstack - 内核栈回溯
  • ustack - 用户栈回溯
  • arg0, arg1, …, argN. - 跟踪函数的参数
  • sarg0, sarg1, …, sargN. - 跟踪函数的参数 (for programs that store arguments on the stack); assumed to be 64 bits wide
  • retval - 被跟踪函数的返回值
  • func - 被跟踪函数的名称
  • probe - 跟踪点全名
  • curtask - 当前的task_struct(u64)
  • rand - 随机数(u32)
  • cgroup - 当前进程的cgroup ID
  • cpid - Child pid(u32),仅-c command使用时有效
  • $1, $2, …, $N, $#. bpftrace程序的入参变量

2. 基本变量: @、$

@全局变量
@线程局部变量[tid]
$临时变量

2.1 全局变量

# bpftrace -e 'BEGIN { @start = nsecs; }
    kprobe:do_nanosleep /@start != 0/ { printf("at %d ms: sleep\n", (nsecs - @start) / 1000000); }'
Attaching 2 probes...
at 42 ms: sleep
at 43 ms: sleep
at 314 ms: sleep
^C

@start: 601563424957305

2.2 线程局部变量

# bpftrace -e 'kprobe:do_nanosleep { @start[tid] = nsecs; }
    kretprobe:do_nanosleep /@start[tid] != 0/ {
        printf("slept for %d ms\n", (nsecs - @start[tid]) / 1000000); delete(@start[tid]); }'
Attaching 2 probes...
slept for 1000 ms
slept for 1000 ms
slept for 1000 ms
slept for 1009 ms
slept for 2002 ms
[...]

2.3 临时变量
如$delta

# bpftrace -e 'kprobe:do_nanosleep { @start[tid] = nsecs; }
    kretprobe:do_nanosleep /@start[tid] != 0/ { $delta = nsecs - @start[tid];
        printf("slept for %d ms\n", $delta / 1000000); delete(@start[tid]); }'
Attaching 2 probes...
slept for 1000 ms
slept for 1000 ms
slept for 1000 ms

3. 关联数组@[ ]

语法:

@关联数组名[key_name] = value
@关联数组名[key_name, key_name2, ...] = value

都是使用bpf map实现的,如@start[tid]

# bpftrace -e 'kprobe:do_nanosleep { @start[tid] = nsecs; }
    kretprobe:do_nanosleep /@start[tid] != 0/ {
        printf("slept for %d ms\n", (nsecs - @start[tid]) / 1000000); delete(@start[tid]); }'
Attaching 2 probes...
slept for 1000 ms
slept for 1000 ms
slept for 1000 ms
[...]

5. 时间戳nsecs

通过 bpf_ktime_get_ns()实现,如上例

6. 内核栈kstack

也可通过kstack()使用:

# bpftrace -e 'kprobe:ip_output { @[kstack] = count(); }'
Attaching 1 probe...
^C

@[
    ip_output+1
    __ip_queue_xmit+378
    ip_queue_xmit+16
    __tcp_transmit_skb+1335
    __tcp_send_ack.part.0+203
    tcp_send_ack+28
    __tcp_ack_snd_check+60
    tcp_rcv_established+1426
    tcp_v4_do_rcv+320
    tcp_v4_rcv+3063
    ip_protocol_deliver_rcu+48
    ip_local_deliver_finish+72
    ip_local_deliver+115
    ip_rcv_finish+133
    ip_rcv+188
    __netif_receive_skb_one_core+135
    __netif_receive_skb+24
    netif_receive_skb_internal+69
    napi_gro_receive+255
    e1000_receive_skb+207
    e1000_clean_rx_irq+523
    e1000e_poll+122
    net_rx_action+314
    __softirqentry_text_start+225
    irq_exit+174
    do_IRQ+90
    ret_from_intr+0
    cpuidle_enter_state+197
    cpuidle_enter+46
    call_cpuidle+35
    do_idle+477
    cpu_startup_entry+32
    start_secondary+359
    secondary_startup_64+164
]: 1
@[
    ip_output+1
    __ip_queue_xmit+378
    ip_queue_xmit+16
    __tcp_transmit_skb+1335
    tcp_write_xmit+962
    __tcp_push_pending_frames+55
    tcp_push+253
    tcp_sendmsg_locked+3189
    tcp_sendmsg+45
    inet_sendmsg+67
    sock_sendmsg+94
    sock_write_iter+147
    new_sync_write+293
    __vfs_write+41
    vfs_write+185
    ksys_write+103
    __x64_sys_write+26
    do_syscall_64+87
    entry_SYSCALL_64_after_hwframe+68
]: 2

7. 用户栈追踪ustack

也可通过ustack()使用:

# bpftrace -e 'kprobe:do_sys_open { @[ustack] = count(); }'
Attaching 1 probe...
^C

@[
    __open64+212
    0x75746174732f3535
]: 1
@[
    __open64+212
    0x75746174732f3331
]: 1

8. 位置参数

格式: $1,$2,...,$N,$#

  • bpftrace程序的位置参数,也称为命令行参数。如果参数(完全)是数字,则可以将其用作数字。否则必须在str()调用中用作字符串。如果使用了未提供的参数,则数字上下文默认为零,字符串上下文默认为“”。位置参数也可以在探测参数中使用,并将被视为字符串参数。
  • 如果在str()中使用位置参数,它将被解释为指向实际给定字符串文字的指针,从而允许对其执行指针算术。只允许添加一个小于或等于所提供字符串长度的常数
  • 这允许编写使用基本参数来更改其行为的脚本。如果开发的脚本需要更复杂的参数处理,那么它可能更适合bcc,bcc支持Python的argparse和完全自定义的参数处理。
    在一行程序中使用位置参数:
# bpftrace -e 'BEGIN { printf("I got %d, %s (%d args)\n", $1, str($2), $#); }' 42 "hello"
Attaching 1 probe...
I got 42, hello (2 args)

在脚本中使用:

#!/usr/local/bin/bpftrace

BEGIN
{
	printf("Tracing block I/O sizes > %d bytes\n", $1);
}

tracepoint:block:block_rq_issue
/args->bytes > $1/
{
	@ = hist(args->bytes);
}

六、函数

1. 内置函数

  • printf(char *fmt, …) - 格式化打印
  • time(char *fmt) - 格式化打印时间
  • join(char *arr[] [, char *delim]) - 打印字符串数组
  • str(char *s [, int length]) - 返回指向s的字符串指针
  • ksym(void *p) - 解析内核地址
  • usym(void *p)- 解析用户空间地址
  • kaddr(char *name) - 解析内核符号
  • uaddr(char *name) - 解析用户空间符号
  • reg(char *name) - 返回存储在指定寄存器上的值
  • system(char *fmt) - 执行系统命令
  • exit() - 退出bpftrace
  • cgroupid(char *path) - 解析cgroupID
  • kstack([StackMode mode, ][int level]) - 内核栈回溯
  • ustack([StackMode mode, ][int level]) - 用户栈回溯
  • ntop([int af, ]int|char[4|16] addr) - 将ip地址转换为文本
  • cat(char *filename) - 打印文件内容
  • signal(char[] signal | u32 signal) - 给当前进程发送信号
  • strncmp(char *s1, char *s2, int length) - 比较两个字符串的前n个字节
  • override(u64 rc) - 重写返回值
  • buf(void *d [, int length]) - 返回d指向的16进制内容
  • sizeof(…) - 返回一个类型或语句的尺寸Return size of a type or expression
  • print(…) - 使用默认格式打印一个非map的值
  • strftime(char *format, int nsecs) - 返回格式化的时间戳
  • path(struct path *path) - 返回完整路径
  • uptr(void *p) - 注释为用户空间指针
  • kptr(void *p) - 注释为内核空间指针
  • macaddr(char[6] addr) - 转换mac地址

2. printf():格式化打印

类似于C风格的打印函数:

# 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
man called /apps/nflx-bash-utils/bin/preconv
man called /usr/local/sbin/preconv
man called /usr/local/bin/preconv
man called /usr/sbin/preconv
man called /usr/bin/preconv
man called /apps/nflx-bash-utils/bin/tbl
[...]

3. time():打印时间

使用指定格式打印时间,需要libc strftime(3)支持。
需要注意的是此时间打印的是用户空间程序处理时间队列的时间,而不是bpf程序调用时的时间

# bpftrace -e 'kprobe:do_nanosleep { time("%H:%M:%S\n"); }'
07:11:03
07:11:09

4. join():打印字符串数组

join()会将字符串数组与一个空格字符连接起来,并将其打印出来,以分隔符分隔。默认的分隔符(如果没有提供)是空格字符。当前版本不返回字符串,因此不能在printf()中用作参数。

# bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv); }'
Attaching 1 probe...
ls --color=auto

5. str():打印字符串

str(char *s, [int length])

返回字符串指针,length参数可选,用于限制s的长度;字符串默认长度为64,可使用BPFTRACE_STRLEN环境变量进行更改;

# 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

6. ksym()

bpftrace -e 'kprobe:do_nanosleep { printf("%s\n", ksym(reg("ip"))); }'
Attaching 1 probe...
do_nanosleep
do_nanosleep

7. usym()

# bpftrace -e 'uprobe:/bin/bash:readline { printf("%s\n", usym(reg("ip"))); }'
Attaching 1 probe...
readline
readline
readline

8. kaddr()

格式: kaddr(const char *name)

bpftrace -e 'BEGIN { printf("%s\n", str(*kaddr("usbcore_name"))); }'
Attaching 1 probe...
usbcore

9. uaddr()

uaddr函数返回指定符号的地址,在程序编译期间查找符号,不能动态使用。
格式:

  • u64 *uaddr(符号名) (默认)
  • u64 *uaddr(符号名)
  • u32 *uaddr(符号名)
  • u16 *uaddr(符号名)
  • u8 *uaddr(符号名)
    支持的探针类型:u(ret)probe、USDT
# bpftrace -e 'uprobe:/bin/bash:readline { printf("PS1: %s\n", str(*uaddr("ps1_prompt"))); }'
Attaching 1 probe...
PS1: \[\e[34;1m\]\u@\h:\w>\[\e[0m\]
PS1: \[\e[34;1m\]\u@\h:\w>\[\e[0m\]
^C

10. reg()

# bpftrace -e 'kprobe:tcp_sendmsg { @[ksym(reg("ip"))] = count(); }'
Attaching 1 probe...
^C
@[tcp_sendmsg]: 8

11. buf(void *d, int len)

返回一个十六进制字符串,由于缓冲区长度不可预测,因此总是需要长度参数来限制读取的字节数。默认最大读取字节数为64,也可使用BPFTRACE_STRLEN环境变量进行调整;
如果字节数在[32, 126]之间,使用他们的ASCII字符进行输出,其余的字节使用16进制(如\x00)。

# bpftrace -e 'tracepoint:syscalls:sys_enter_sendto
    { printf("Datagram bytes: %r\n", buf(args->buff, args->len)); }' -c 'ping 8.8.8.8 -c1'
Attaching 1 probe...
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
Datagram bytes: \x08\x00+\xb9\x06b\x00\x01Aen^\x00\x00\x00\x00KM\x0c\x00\x00\x00\x00\x00\x10\x11
\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&'()*+,-./01234567
64 bytes from 8.8.8.8: icmp_seq=1 ttl=52 time=19.4 ms

--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 19.426/19.426/19.426/0.000 ms

12. system()

让bpftrace执行一个系统命令,此行为不安全,因此使用时需要指定--unsafe选项:

# 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
  PID TTY          TIME CMD
 1518 ?        00:01:07 irqbalance
  PID TTY          TIME CMD
 1339 ?        00:00:15 iscsid
^C

13. exit()

退出bpftrace,可以与interval间隔探针相结合,以记录特定持续时间内的统计信息:

# bpftrace -e 'kprobe:do_sys_open { @opens=count(); } interval:s:1 { exit(); }'
Attaching 2 probes...
@opens: 46

14. cgroupid()

返回特定cgroup的cgroup ID,可与cgroup内置项组合,以过滤属于特定cgroup的任务,例如:

# bpftrace -e 'tracepoint:syscalls:sys_enter_openat /cgroup == cgroupid("/sys/fs/cgroup/unified/mycg")/
    { printf("%s\n", str(args->filename)); }':
Attaching 1 probe...
/etc/ld.so.cache
/lib64/libc.so.6
/usr/lib/locale/locale-archive
/etc/shadow
^C

另一个终端的行为如下:

# echo $$ > /sys/fs/cgroup/unified/mycg/cgroup.procs
# cat /etc/shadow

15. ntop()

bpftrace -e 'tracepoint:tcp:tcp_set_state { printf("%s\n", ntop(args->daddr_v6)) }'
Attaching 1 probe...
::ffff:216.58.194.164
::ffff:216.58.194.164
::ffff:216.58.194.164
::ffff:216.58.194.164
::ffff:216.58.194.164
bpftrace -e '#include 
BEGIN { printf("%s\n", ntop(AF_INET, 0x0100007f));}'
127.0.0.1
^C

16. kstack()

同kstack关键字,可以选择输出格式和栈最大深度,如:

# bpftrace -e 'kprobe:do_mmap { @[kstack(perf, 3)] = count(); }'
Attaching 1 probe...
[...]
@[
	ffffffffb4019501 do_mmap+1
	ffffffffb401700a sys_mmap_pgoff+266
	ffffffffb3e334eb sys_mmap+27

]: 22186

17. ustack():

同ustack变量,ustack变量

18. cat():查看文件内容

可使用cat(文件名)查看文件内容,如:

# bpftrace -e 'tracepoint:syscalls:sys_enter_sendmsg { printf("%s => ", comm);
    cat("/proc/%d/cmdline", pid); printf("\n") }'
Attaching 1 probe...
Gecko_IOThread => /usr/lib64/firefox/firefox
Gecko_IOThread => /usr/lib64/firefox/firefox

19. signal():向当前进程发送一个信号

  • 内核版本>=5.3
  • 支持的探针类型:k(ret)probes,u(ret)probes,USDT,profile
# bpftrace  -e 'kprobe:__x64_sys_execve /comm == "bash"/ { signal(5); }' --unsafe
$ ls
Trace/breakpoint trap (core dumped)

20. strncmp():字符串比较

同C语法格式,如果两个字符串的前n个字节相同,则返回0,否则返回非0:

# bpftrace -e 't:syscalls:sys_enter_* /strncmp("mpv", comm, 3) == 0/ { @[comm, probe] = count() }'
Attaching 320 probes...
[...]
@[mpv/vo, tracepoint:syscalls:sys_enter_rt_sigaction]: 238
@[mpv:gdrv0, tracepoint:syscalls:sys_enter_futex]: 680
@[mpv/ao, tracepoint:syscalls:sys_enter_write]: 1022
@[mpv, tracepoint:syscalls:sys_enter_ioctl]: 2677

21. override():重写返回值

  • 内核版本>=4.16
  • 探针类型: kprobes
    该特性需要内核配置了CONFIG_BPF_KPROBE_OVERRIDE选项,并且目标函数使用ALLOW_ERROR_INJECTION标签,bpftrace不测试被探测函数是否允许错误注入,而是测试是否无法将程序加载到内核。
bpftrace -e 'k:__x64_sys_getuid /comm == "id"/ { override(2<<21); }' --unsafe -c id
uid=4194304 gid=0(root) euid=0(root) groups=0(root)
ioctl(PERF_EVENT_IOC_SET_BPF): Invalid argument
Error attaching probe: 'kprobe:vfs_read'

22. sizeof()

# bpftrace -e 'struct Foo { int x; char c; } BEGIN { printf("%d\n", sizeof(struct Foo)); }'
Attaching 1 probe...
8

# bpftrace -e 'struct Foo { int x; char c; } BEGIN { printf("%d\n", sizeof(((struct Foo)0).c)); }'
Attaching 1 probe...
1

# bpftrace -e 'BEGIN { printf("%d\n", sizeof(1 == 1)); }'
Attaching 1 probe...
8

# bpftrace -e 'BEGIN { printf("%d\n", sizeof(struct task_struct)); }'
Attaching 1 probe...
13120

# bpftrace -e 'BEGIN { $x = 3; printf("%d\n", sizeof($x)); }'
Attaching 1 probe...
8

23. print()

使用print打印一个非map变量,如大多数的内置变量和局部变量:

# bpftrace -e 'BEGIN { $t = (1, "string"); print(123); print($t); print(comm) }'
Attaching 1 probe...
123
(1, string)
bpftrace
^C

24. strftime():格式化时间戳

语法:

strftime(const char *format, int nsecs)

返回一个可使用printf打印的格式化时间戳,此时间戳的格式必须被strftime所支持(并不是在内核bpf程序中返回,而是用户空间的时间)。nsecs参数为自启动以来的纳秒数。

# bpftrace -e 'i:s:1 { printf("%s\n", strftime("%H:%M:%S", nsecs)); }'
Attaching 1 probe...
13:11:22
13:11:23
13:11:24
13:11:25
13:11:26
^C

# bpftrace -e 'i:s:1 { printf("%s\n", strftime("%H:%M:%S:%f", nsecs)); }'
Attaching 1 probe...
15:22:24:104033
^C

25. path():返回完整路径

格式:

path(struct path *path)
# bpftrace  -e 'kfunc:filp_close { printf("%s\n", path(args->filp->f_path)); }'
Attaching 1 probe...
/proc/sys/net/ipv6/conf/eno2/disable_ipv6
/proc/sys/net/ipv6/conf/eno2/use_tempaddr
socket:[23276]
/proc/sys/net/ipv6/conf/eno2/disable_ipv6
socket:[17655]
/sys/devices/pci0000:00/0000:00:1c.5/0000:04:00.1/net/eno2/type
socket:[38745]
/proc/sys/net/ipv6/conf/eno2/disable_ipv6

# bpftrace  -e 'kretfunc:dentry_open { printf("%s\n", path(retval->f_path)); }'
Attaching 1 probe...
/dev/pts/1 -> /dev/pts/1

26. uptr():注解用户地址

格式:

uptr(void *p)

将p注解为用户空间地址,bpftrace通常可以推断指针的地址空间,然而在某些情况下会推测失败。例如,处理用户空间指针(如const char__user*p)的内核函数。在这些情况下,需要对指针进行注释。

# bpftrace -e 'kprobe:do_sys_open { printf("%s\n", str(uptr(arg1))) }'
Attaching 1 probe...
.
state
^C

27. kptr():注解内核地址

格式:

kptr(void *p)

类似于uptr,将p注解为内核态地址,通常只有在bpftrace错误地推断出指针地址空间的情况下需要。

28. macaddr():将mac地址转为文本

格式:

macaddr(char [6]addr)
# bpftrace -e 'kprobe:arp_create { printf("SRC %s, DST %s\n", macaddr(sarg0), macaddr(sarg1)); }'
SRC 18:C0:4D:08:2E:BB, DST 74:83:C2:7F:8C:FF
^C

29. cgroup_path():将cgroupid转为路径

格式:

cgroup_path(int cgroupid, string filter)

将给定的cgroup id转换为id出现在其中的每个cgroup层次结构的相应cgroup路径。因为转换是在用户空间中完成的,所以生成的对象只能用于打印。

# bpftrace -e 'BEGIN { print(cgroup_path(5386)); }'
Attaching 1 probe...
unified:/user.slice/user-1000.slice/session-3.scope

七、映射表操作函数

1. 内置函数

  • count() - 统计函数调用次数
  • sum(int n) - 求和
  • avg(int n) - 求平均值
  • min(int n) - 记录变量出现的最小值
  • max(int n) - 记录变量出现的最大值
  • stats(int n) - 返回变量出现的次数,平均值,总和
  • hist(int n) -将值保存为直方图
  • lhist(int n, int min, int max, int step) -将值保存为线性直方图
  • delete(@x[key]) - 从映射表中删除一个键值对
  • print(@x[, top [, div]]) - 打印映射表,可选top(只打印最高的top个)和div(将数值整除后再输出)参数
  • print(value) - 打印一个变量
  • clear(@x) - 删除映射表中全部键值对
  • zero(@x) - 将全部值置为0

2. count()

格式:

@counter_name[optional_keys] = count()

示例:

# bpftrace -e 'kprobe:vfs_* { @vfs_op = count(); }'
Attaching 65 probes...
^C
@vfs_op: 7185
# bpftrace -e 'kprobe:vfs_read { @reads[comm] = count(); }'
Attaching 1 probe...
^C

@reads[sleep]: 4
@reads[bash]: 5
@reads[ls]: 7
@reads[snmp-pass]: 8
@reads[snmpd]: 14
@reads[sshd]: 14

3. sum()

格式

@counter_name[optional_keys] = sum(value)

示例:

# bpftrace -e 'kretprobe:vfs_read /retval > 0/ { @bytes[comm] = sum(retval); }'
Attaching 1 probe...
^C

@bytes[bash]: 5
@bytes[sshd]: 1135
@bytes[systemd-journal]: 1699
@bytes[sleep]: 2496
@bytes[ls]: 4583
@bytes[snmpd]: 35549
@bytes[snmp-pass]: 55681

4. avg()

语法:

@counter_name[optional_keys] = avg(value)

示例:

# bpftrace -e 'kprobe:vfs_read { @bytes[comm] = avg(arg2); }'
Attaching 1 probe...
^C

@bytes[bash]: 1
@bytes[sleep]: 832
@bytes[ls]: 886
@bytes[snmpd]: 1706
@bytes[snmp-pass]: 8192
@bytes[sshd]: 16384

5. min()

格式:

@counter_name[optional_keys] = min(value)

示例:

# bpftrace -e 'kprobe:vfs_read { @bytes[comm] = min(arg2); }'
Attaching 1 probe...
^C

@bytes[bash]: 1
@bytes[systemd-journal]: 8
@bytes[snmpd]: 64
@bytes[ls]: 832
@bytes[sleep]: 832
@bytes[snmp-pass]: 8192
@bytes[sshd]: 16384

6. max()

格式:

@counter_name[optional_keys] = max(value)

示例:

# bpftrace -e 'kprobe:vfs_read { @bytes[comm] = max(arg2); }'
Attaching 1 probe...
^C

@bytes[bash]: 1
@bytes[systemd-journal]: 8
@bytes[sleep]: 832

7. stats()

格式:

@counter_name[optional_keys] = stats(value)

示例:

# bpftrace -e 'kprobe:vfs_read { @bytes[comm] = stats(arg2); }'
Attaching 1 probe...
^C

@bytes[bash]: count 7, average 1, total 7
@bytes[sleep]: count 5, average 832, total 4160
@bytes[ls]: count 7, average 886, total 6208
@bytes[snmpd]: count 18, average 1706, total 30718
@bytes[snmp-pass]: count 12, average 8192, total 98304
@bytes[sshd]: count 15, average 16384, total 245760

8. hist()

格式:

@histogram_name[optional_key] = hist(value)

示例:

# bpftrace -e 'kretprobe:vfs_read { @bytes = hist(retval); }'
Attaching 1 probe...
^C

@bytes:
(..., 0)             117 |@@@@@@@@@@@@                                        |
[0]                    5 |                                                    |
[1]                  325 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@                  |
[2, 4)                 6 |                                                    |
[4, 8)                 3 |                                                    |
[8, 16)              495 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[16, 32)              35 |@@@                                                 |
[32, 64)              25 |@@                                                  |
[64, 128)             21 |@@  

9. lhist()

格式:

@histogram_name[optional_key] = lhist(value, min, max, step)

示例:

# bpftrace -e 'kretprobe:vfs_read { @bytes = lhist(retval, 0, 10000, 1000); }'
Attaching 1 probe...
^C

@bytes:
[0, 1000)            480 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[1000, 2000)          49 |@@@@@                                               |
[2000, 3000)          12 |@                                                   |
[3000, 4000)          39 |@@@@                                                |
[4000, 5000)         267 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@                        |

10. print()

格式:

print(@map [, top [, divisor]])

print()函数将打印映射表,类似于bpftrace结束时的自动打印。可以提供两个可选参数:一个是top number,这样只打印top number个数目;另一个是divisor,用于分割值。
示例:

# bpftrace -e 'kprobe:vfs_* { @[func] = count(); } END { print(@, 5); clear(@); }'
Attaching 54 probes...
^C
@[vfs_getattr]: 91
@[vfs_getattr_nosec]: 92
@[vfs_statx_fd]: 135
@[vfs_open]: 188
@[vfs_read]: 405

最后使用clear来防止bpftrace结束是对映射表的自动打印。
使用divisor的示例:

bpftrace -e 'kprobe:vfs_read { @start[tid] = nsecs; }
    kretprobe:vfs_read /@start[tid]/ {@ms[pid] = sum(nsecs - @start[tid]); delete(@start[tid]); }
    END { print(@ms, 0, 1000000); clear(@ms); clear(@start); }'
@ms[2429]: 0
@ms[7679]: 0
@ms[2662]: 1
@ms[7633]: 1
@ms[343161]: 1
@ms[58241]: 1
@ms[7727]: 8503

原文: https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md

你可能感兴趣的:(linux)