我正在跟踪各种内核函数和系统调用,并在它们之间建立可用于某些性能分析的模式。
我注意到的一件事是,有时,即使在我的简单测试应用程序中,它旋转了一些使用互斥锁的线程,我也不会收到对kretprobe__sys_futex
的任何调用,但是我会得到很多kprobe__sys_futex
。
我认为这是因为线程正在调用sys_futex
,然后进入睡眠状态或终止运行,但是实际上,我看到相同的进程连续多次调用sys_futex
,而return探针从未注意到任何事情。
然后我认为问题出在我如何过滤对kprobe__sys_futex
的呼叫,因此我举了一个使用BCC / eBPF进行测试的最小示例:
#! /usr/bin/env python
from bcc import BPF
b = BPF(text="""
BPF_HASH(call_count, int, int);
int kprobe__sys_futex() {
int zero = 0;
call_count.lookup_or_init(&zero, &zero);
bpf_trace_printk("futex start\\n");
call_count.increment(zero);
return 0;
}
int kretprobe__sys_futex() {
int zero = 0;
int *val_p = call_count.lookup(&zero);
if (val_p != NULL) {
int val = *val_p;
val--;
call_count.update(&zero, &val);
bpf_trace_printk("futex calls with no return: %d\\n", val);
} else { bpf_trace_printk("unexpected futex return\\n"); }
return 0;
}
""")
b.trace_print()
我注意到,在各种各样的应用程序中(一个很好的例子是mysql-server,即使在空闲时它也会执行常规的futex操作-至少在我的机器上),打印了许多(通常10+)futex start
在返回探针发出消息之前。
这是上面程序的示例跟踪,在我撰写本文时,我花了几分钟时间运行该程序:
... hundreds of lines of much the same as below
gdbus-612 [001] .... 211229.997665: 0x00000001: futex start
NetworkManager-541 [001] .... 211229.997667: 0x00000001: futex start
gdbus-612 [001] .... 211229.997670: 0x00000001: futex start
mysqld-697 [001] .... 211230.789205: 0x00000001: futex start
mysqld-697 [001] .... 211230.789227: 0x00000001: futex start
mysqld-703 [001] .... 211230.789251: 0x00000001: futex start
mysqld-703 [001] .... 211230.789253: 0x00000001: futex start
mysqld-704 [001] d... 211230.789258: 0x00000001: futex calls with no return: 3994
mysqld-704 [001] .... 211230.789259: 0x00000001: futex start
mysqld-704 [001] d... 211230.789260: 0x00000001: futex calls with no return: 3994
mysqld-704 [001] .... 211230.789272: 0x00000001: futex start
mysqld-713 [000] .... 211231.037016: 0x00000001: futex start
mysqld-713 [000] .... 211231.037036: 0x00000001: futex start
vmstats-895 [000] .... 211231.464867: 0x00000001: futex start
mysqld-697 [001] .... 211231.790738: 0x00000001: futex start
mysqld-697 [001] .... 211231.790784: 0x00000001: futex start
mysqld-703 [001] .... 211231.790796: 0x00000001: futex start
mysqld-703 [001] .... 211231.790799: 0x00000001: futex start
mysqld-704 [001] d... 211231.790809: 0x00000001: futex calls with no return: 4001
mysqld-704 [001] .... 211231.790812: 0x00000001: futex start
mysqld-704 [001] d... 211231.790814: 0x00000001: futex calls with no return: 4001
如您所见,例如pid 697似乎已经四次调用sys_futex
,而没有返回此小轨迹。
我不认为这是eBPF代码中的竞争条件,因为如果您使打印语句静音并且仅定期打印,则sys_write
的计数通常在零附近,这会更频繁地发生比sys_futex
(至少在我的系统的工作量上)要多,所以我希望任何竞争条件都会恶化而无法解决。
我正在VirtualBox上的Ubuntu 18.04 LTS上运行Kernel 4.15.0-43-generic。
很高兴提供更多可能有用的上下文!
IOVisor邮件列表中有一个与之相关的主题:https://lists.iovisor.org/g/iovisor-dev/topic/29702757
答案 0 :(得分:1)
这是密件抄送的已知限制(参见iovisor/bcc#1072)。基本上,对于跟踪上下文,活动探针的最大数量设置得太低,因此您缺少一些返回探针。
在密件抄送中,maxactive
值(活动探针的最大数量,请参见下面的文档摘录)保留其默认值。由于Alban Crequy是Linux内核的补丁程序(参见iovisor/bcc#1072),因此在通过debugfs附加探针时,可以更改maxactive
的值。但是,新的API尚未通过bcc公开。我将在本周尝试为此发送补丁。
在执行探测的函数时,其返回地址为 存储在kretprobe_instance类型的对象中。致电之前 register_kretprobe(),用户设置 kretprobe结构指定指定实例的数量 功能可以同时进行探查。 register_kretprobe() 预分配指示数量的kretprobe_instance对象。
例如,如果函数是非递归的,并且使用 保持自旋锁,maxactive = 1应该足够。如果功能是 非递归且永远不会放弃CPU(例如通过信号灯 或抢占),NR_CPUS应该足够了。如果maxactive <= 0,则为 设置为默认值。如果启用了CONFIG_PREEMPT,则默认 最大值(10,2 * NR_CPUS)。否则,默认值为NR_CPUS。