【记录 bpftrace】

简介: 【记录 bpftrace】

一、bpftrace简介

bpftrace 是基于ebpf内核vm扩展出来的trace工具。

bpftrace 是 Linux 高级追踪工具和语言。该工具基于 eBPF 和 BBC 实现了通过探针机制采集内核和程序运行的信息,然后用图表等方式将信息展示出来,帮助开发者找到隐藏较深的 Bug、安全问题和性能瓶颈。

## bpftrace进行内核跟踪
#### bpftrace 命令行操作
单行命令工具:
bpftrace -e 'program'
bpftrace直接跟-e选项后面加单行命令,一些示例,比如:
bpftrace -e 'BEGIN { printf("Hello world!\n"); }'
bpftrace -e 'kprobe:vfs_read { @[tid] = count();}'
bpftrace -e 'kprobe:vfs_read  /pid == 123/ { @[tid, comm] = count();}'
bpftrace -e 't:block:block_rq_insert { @[kstack] = count(); }'
单行命令工具可以按ctrl-c也结束,只有结束后才会打印输出结果。
#### bpftrace 编写脚本工具
bpftrace支持脚本编写,只需要在其实出添加#!/usr/local/bin/bpftrace,会被认为是一个bpftrace脚本。
#!/usr/local/bin/bpftrace
// this program times vfs_read()
kprobe:vfs_read
{
    @start[tid] = nsecs;
} 
retprobe:vfs_read
/@start[tid]/
{
    $duration_us = (nsecs - @start[tid]) / 1000;
    @us = hist($duration_us);
    delete(@start[tid]);
}
#### debug调试
bpftrace -d
bpftrace -v
bpftrace语法结构
bpftrace编程语言参考了awk的语法,基础结构:
probes /filter/ { actions }
它是一种事件驱动的运行方式。
probes表示的就是事件,包括tracepoint、kprobe、kretprobe、uprobe等等。除了这些跟踪点,还有两个特殊的事件BEGIN、END,用于在脚本开始处和结束处执行。
filter表示的是过滤条件,当一个事件触发时,会先判断该条件,满足条件才会执行后面的action行为。
action表示的具体执行的操作。
示例:
bpftrace -e 'kprobe:vfs_read  /pid == 123/ { @[tid, comm] = count();}'
#### bpftrace 脚本变量
内部变量(built-in)
uid:    用户id。
tid:   线程id
pid:   进程id。
cpu:   cpu id。
cgroup:cgroup id.
probe: 当前的trace点。
comm:  进程名字。
nsecs: 纳秒级别的时间戳。
kstack:内核栈描述
curtask:当前进程的task_struct地址。
args:   获取该kprobe或者tracepoint的参数列表
arg0:   获取该kprobe的第一个变量,tracepoint不可用
arg1:   获取该kprobe的第二个变量,tracepoint不可用
arg2:   获取该kprobe的第三个变量,tracepoint不可用
retval: kretprobe中获取函数返回值
args->ret: kretprobe中获取函数返回值
备注:
bpftrace -lv tracepoint:syscalls:sys_enter_read
这个命令-lv可以用来查看一个tracepoint对应的参数都有哪些。
自定义临时变量
以"$"标志起始来定义和引用一个变量
Map变量
map变量是用于内核向用户空间传递数据的一种存储结构,定义方式是以"@"符号作为其实标记。
这个map可以有单个key或者多个key:
@path[tid] = nsecs
@path[pid, $fd] =nsecs
bpftrace默认在结束时会打印从内核接收到的map变量。
#### 函数
exit():退出bpftrace程序
str(char *):转换一个指针到string类型
system(format[, arguments ...]):运行一个shell命令
join(char *str[]):打印一个字符串列表并在每个前面加上空格,比如可以用来输出args->argv
ksym(addr):用于转换一个地址到内核symbol
kaddr(char *name):通过symbol转换为内核地址
print(@m [, top [, div]]):可选择参数打印map中的top n个数据,数据可选择除以一个div值
#### map函数
bpftrace内构的一些map函数,用于传递数据给map变量,注意接收他们的变量是map类型。常用的包括:
count():用于计算次数
sum(int n):用于累加计算
avg(int n):用于计算平均值
min(int n):用于计算最小值
max(int n):用于计算最大值
hist(int n):数据分布直方图(范围为2的幂次增长)
lhist(int n):数据线性直方图
delete(@m[key]):删除map中的对应的key数据
clear(@m):删除map中的所有数据
zero(@m):map中的所有值设置为0
#### 附件
常用的一些单行命令示例:
bpftrace -e 'tracepoint:block:block_rq_i* { @[probe] = count(); } interval:s:1 { print(@); clear(@); }'
bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret > 0/ { @bytes = sum(args->ret); }'
bpftrace -e 'tracepoint:syscalls:sys_exit_read { @ret = hist(args->ret); }'
bpftrace -e 'tracepoint:syscalls:sys_exit_read { @ret = lhist(args->ret, 0, 1000, 100); }'
bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret < 0/ { @[- args->ret] = count(); }'
bpftrace -e 'kprobe:vfs_* { @[probe] = count(); } END { print(@, 5); clear(@); }'
bpftrace -e 'kprobe:vfs_read { @start[tid] =nsecs; } kretprobe:vfs_read /@start[tid]/ { @ms[comm] = sum(nsecs - @start[tid]); delete(@start[tid]); } END { print(@ms, 0, 1000000); clear(@ms); clear(@start); }'
bpftrace -e 'k:vfs_read { @[pid] = count(); }'
bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%s -> %s\n", comm, str(args->filename)); }'
bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv); }'
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { printf("%s %s\n", comm, str(args->filename)); }'
bpftrace -e 'tracepoint:raw_syscalls:sys_enter {@[comm] = count(); }'
bpftrace -e 'tracepoint:syscalls:sys_enter_* {@[probe] = count(); }'
bpftrace -e 'tracepoint:raw_syscalls:sys_enter {@[pid, comm] = count(); }'
bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret/ { @[comm] = sum(args->ret); }'
bpftrace -e 'tracepoint:syscalls:sys_exit_read { @[comm] = hist(args->ret); }'
bpftrace -e 'tracepoint:block:block_rq_issue { printf("%d %s %d\n", pid, comm, args->bytes); }'
bpftrace -e 'software:major-faults:1 { @[comm] = count(); }'
bpftrace -e 'software:faults:1 { @[comm] = count(); }'
bpftrace -e 'tracepoint:syscalls:sys_enter_clone { printf("-> clone() by %s PID %d\n", comm, pid); } tracepoint:syscalls:sys_exit_clone { printf("<- clone() return %d, %s PID %d\n", args->ret, comm, pid); }'
bpftrace -e 'tracepoint:syscalls:sys_enter_setuid { printf("setuid by PID %d (%s), UID %d\n", pid, comm, uid); }'
bpftrace -e 'tracepoint:syscalls:sys_exit_setuid { printf("setuid by %s returned %d\n", comm, args->ret); }'
bpftrace -e 'tracepoint:block:block_rq_insert { printf("Block I/O by %s\n", kstack); }'
bpftrace -e 'tracepoint:syscalls:sys_enter_connect /pid == 123/ { printf("PID %d called connect()\n", $1); }'
bpftrace -e 'tracepoint:timer:hrtimer_start { @[ksym(args->function)] = count(); }'
bpftrace -e 't:syscalls:sys_enter_read { @reads = count(); } interval:s:5 { exit(); }'
bpftrace -e 'kprobe:vfs_read {@ID = pid;} interval:s:2 {printf("ID:%d\n", @ID);}'
相关文章
|
10月前
|
JSON 前端开发 JavaScript
java基本知识点记录
java基本知识点记录
|
11月前
|
Java 开发工具 Android开发
ASOP记录
ASOP记录
55 0
|
Web App开发 关系型数据库 MySQL
日常技巧记录-2018.08
日常技巧记录-2018.08
78 0
谁来帮我做记录
这一节,我们将要学习游戏开发中的记录员---变量。 开始之前,先提几个问题: - 变量的作用是什么? - 变量分为哪些类型? - 我们什么时候用到变量? - 如何判断使用哪种类型的变量?
160 0
谁来帮我做记录
记录什么 反抗什么
蒋方舟 蒋方舟/文 这个月,发了新书,紧张得每天去豆瓣上查关于新书的评价,偶尔,看到这样一条评价:“这本书的简历里写着9岁出书,23岁成为《新周刊》副主编。
1070 0
|
iOS开发
Xcode快捷键 记录
command+shift+k, clean command+b 编译
930 0