bpftrace参考指南

您所在的位置:网站首页 btf文件转换 bpftrace参考指南

bpftrace参考指南

2024-01-23 23:02| 来源: 网络整理| 查看: 265

目录 前言一、名词解释二、使用示范1. help2. Hello World3. One-Liners程序4. 列出可跟踪点5. 调试输出-d6. 输出详情7. 预处理选项8. 环境变量9. 其它选项 三、语法1. 程序结构{...}2. 过滤/.../3. 注释//, /**/4. 常量5. c结构体访问:->6. 结构体定义: struct7. 三元操作符 ?::8. 条件语句 if () {..} else {..}9. 循环语句unroll10. 自增、自减++、--11. 数组访问[]12. 整形强转13. while循环14. 提前结束:return15. 元组( ,) 四、探针类型1. kprobe/kretprobe2. uprobe/uretprobe3. tracepoint4. usdt5. profile6. interval7. software8. hardware 五、变量1. 内置变量2. 基本变量: @、$3. 关联数组@[ ]5. 时间戳nsecs6. 内核栈kstack7. 用户栈追踪ustack8. 位置参数 六、函数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通常被称为BPFprobe软件或硬件中的一种插装点,用于生成可执行bpftrace程序的事件tracepoints内核用于提供静态插桩点的技术kprobes内核用于进行函数动态追踪的技术uprobes内核用于动态追踪用户态函数调用的技术USDT用户程序自己定义的静态插桩点(User Statically-Defined Tracing)BPF mapBPF的内存对象,bpftrace用它来创建一些高级对象BTFBPF类型格式:对与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... BPFTRACE_STRLEN 默认值64,使用str()获取BPF stack分配的字符串时返回的长度,当前可以设置的最大值为200,支持更大字符长度的问题仍在讨论中。BPFTRACE_NO_CPP_DEMANGLE 默认为0,默认启用了用户空间堆栈跟踪中的C++符号还原功能,将此环境变量设置为1,可以关闭此功能。BPFTRACE_MAP_KEYS_MAX 单个map中存储的最大key数量,默认4096。BPFTRACE_MAX_PROBES bpftrace程序支持attach的钩子数量,默认512。BPFTRACE_CACHE_USER_SYMBOLS 默认情况下bpftrace缓存符号的解析结果,如果ASLR没有开启(Address Space Layout Randomization),仅仅跟踪一个程序的时候,开启此选项可以获得性能上的提升。BPFTRACE_BTF BTF文件的路径,默认为NoneBPFTRACE_MAX_BPF_PROGS bpftrace可构造的最大BPF程序数量,默认值为512. 9. 其它选项 使用--version获取版本信息使用--no-warnings关闭警告使用-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"} 使用-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 = 1filename)); }' 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-clock或cpu:报告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 cyclesinstructionscache-referencescache-missesbranch-instructions or branchesbranch-missesbus-cyclesfrontend-stallsbackend-stallsref-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 - 用户IDgid - 组IDnsecs - 纳秒时间戳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 wideretval - 被跟踪函数的返回值func - 被跟踪函数的名称probe - 跟踪点全名curtask - 当前的task_struct(u64)rand - 随机数(u32)cgroup - 当前进程的cgroup IDcpid - 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() - 退出bpftracecgroupid(char *path) - 解析cgroupIDkstack([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 expressionprint(…) - 使用默认格式打印一个非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(2filp->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



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3