为什么 systemTap 脚本在操作员错误附近报告读取错误?
Why systemTap script report a read fault near operator error?
我 运行在 CentOS Linux 版本 7.6.1810 上使用 SystemTap。 SystemTap 的版本是:
$ stap -V
Systemtap translator/driver (version 4.0/0.172/0.176, rpm 4.0-11.el7)
Copyright (C) 2005-2018 Red Hat, Inc. and others
This is free software; see the source for copying conditions.
tested kernel versions: 2.6.18 ... 4.19-rc7
enabled features: AVAHI BOOST_STRING_REF DYNINST BPF JAVA PYTHON2 LIBRPM LIBSQLITE3 LIBVIRT LIBXML2 NLS NSS READLINE
$ uname -rm
3.10.0-957.21.3.el7.x86_64 x86_64
$ rpm -qa | grep kernel-devel
kernel-devel-3.10.0-957.21.3.el7.x86_64
$ rpm -qa | grep kernel-debuginfo
kernel-debuginfo-3.10.0-957.21.3.el7.x86_64
kernel-debuginfo-common-x86_64-3.10.0-957.21.3.el7.x86_64
我有一个名为 sg.stp 的 systemTap 脚本,它用于监视为什么 rabbitmq 集群的 k8s pods 偶尔以退出代码 137 终止:
global target_pid = 32719
probe signal.send{
if (sig_pid == target_pid) {
printf("%s(%d) send %s to %s(%d)\n", execname(), pid(), sig_name, pid_name, sig_pid);
printf("parent of sender: %s(%d)\n", pexecname(), ppid())
printf("task_ancestry:%s\n", task_ancestry(pid2task(pid()), 1));
}
}
我在运行脚本的时候,过了一会儿就报错了:
$ stap sg.stp
ERROR: read fault [man error::fault] at 0x4a8 near operator '@cast' at /usr/share/systemtap/tapset/linux/task.stpm:2:5
epmd(29073) send SIGCHLD to rabbitmq-server(32719)
parent of sender: rabbitmq-server(32719)
WARNING: Number of errors: 1, skipped probes: 0
WARNING: /usr/bin/staprun exited with status: 1
Pass 5: run failed. [man error::pass5]
pid2task()
可以 return NULL
像这样检查 pid2task(pid())
或 current_task()
returning NULL:
task = pid2task(pid());
if (task) {
printf("task_ancestry:%s\n", task_ancestry(task, 1));
} else {
printf("task_ancestry more available\n");
}
请注意,我对以下解释并不完全确定:
可能会发生 task_struct 不再可用,即使您在 运行 pid()
的上下文中也是如此,因为进程已经终止并且 task_struct 已清理,因为不再需要它。
在那种情况下 pid2task()
returns NULL。 AFAICS 在以下两种情况下(可能更多),pid()
可能会发生这种情况:
您的探测与 运行 进程异步 - 在您使用信号探测的情况下,这似乎就是这种情况。
.return
探测器执行得太晚,可能是因为它在内核中停留的时间太长(例如阻塞调用)。
对于后者,似乎有一些简单的解决方法:
使用 @entry(task_ancestry(current_task()))
而不是 task_ancestry(current_task())
。通过这种方式,数据会在系统调用的入口点收集,进程很可能仍然完好无损。
但是在您的 Signal 案例中,我没有看到如此简单的解决方法,因此您必须检查 NULL。
请注意,我不完全确定这是您的问题,并且在不锁定页面的情况下检查 NULL 是完美的解决方案。因为即使您获得了指向某个结构的指针,包含该结构的页面也可能会在探测过程中消失,这要归功于 SMP。也许 stap
以某种方式防止这种情况。但我怀疑。
像这样的竞争条件很难调试和避免。
我 运行在 CentOS Linux 版本 7.6.1810 上使用 SystemTap。 SystemTap 的版本是:
$ stap -V
Systemtap translator/driver (version 4.0/0.172/0.176, rpm 4.0-11.el7)
Copyright (C) 2005-2018 Red Hat, Inc. and others
This is free software; see the source for copying conditions.
tested kernel versions: 2.6.18 ... 4.19-rc7
enabled features: AVAHI BOOST_STRING_REF DYNINST BPF JAVA PYTHON2 LIBRPM LIBSQLITE3 LIBVIRT LIBXML2 NLS NSS READLINE
$ uname -rm
3.10.0-957.21.3.el7.x86_64 x86_64
$ rpm -qa | grep kernel-devel
kernel-devel-3.10.0-957.21.3.el7.x86_64
$ rpm -qa | grep kernel-debuginfo
kernel-debuginfo-3.10.0-957.21.3.el7.x86_64
kernel-debuginfo-common-x86_64-3.10.0-957.21.3.el7.x86_64
我有一个名为 sg.stp 的 systemTap 脚本,它用于监视为什么 rabbitmq 集群的 k8s pods 偶尔以退出代码 137 终止:
global target_pid = 32719
probe signal.send{
if (sig_pid == target_pid) {
printf("%s(%d) send %s to %s(%d)\n", execname(), pid(), sig_name, pid_name, sig_pid);
printf("parent of sender: %s(%d)\n", pexecname(), ppid())
printf("task_ancestry:%s\n", task_ancestry(pid2task(pid()), 1));
}
}
我在运行脚本的时候,过了一会儿就报错了:
$ stap sg.stp
ERROR: read fault [man error::fault] at 0x4a8 near operator '@cast' at /usr/share/systemtap/tapset/linux/task.stpm:2:5
epmd(29073) send SIGCHLD to rabbitmq-server(32719)
parent of sender: rabbitmq-server(32719)
WARNING: Number of errors: 1, skipped probes: 0
WARNING: /usr/bin/staprun exited with status: 1
Pass 5: run failed. [man error::pass5]
pid2task()
可以 return NULL
像这样检查 pid2task(pid())
或 current_task()
returning NULL:
task = pid2task(pid());
if (task) {
printf("task_ancestry:%s\n", task_ancestry(task, 1));
} else {
printf("task_ancestry more available\n");
}
请注意,我对以下解释并不完全确定:
可能会发生 task_struct 不再可用,即使您在 运行 pid()
的上下文中也是如此,因为进程已经终止并且 task_struct 已清理,因为不再需要它。
在那种情况下 pid2task()
returns NULL。 AFAICS 在以下两种情况下(可能更多),pid()
可能会发生这种情况:
您的探测与 运行 进程异步 - 在您使用信号探测的情况下,这似乎就是这种情况。
.return
探测器执行得太晚,可能是因为它在内核中停留的时间太长(例如阻塞调用)。
对于后者,似乎有一些简单的解决方法:
使用 @entry(task_ancestry(current_task()))
而不是 task_ancestry(current_task())
。通过这种方式,数据会在系统调用的入口点收集,进程很可能仍然完好无损。
但是在您的 Signal 案例中,我没有看到如此简单的解决方法,因此您必须检查 NULL。
请注意,我不完全确定这是您的问题,并且在不锁定页面的情况下检查 NULL 是完美的解决方案。因为即使您获得了指向某个结构的指针,包含该结构的页面也可能会在探测过程中消失,这要归功于 SMP。也许 stap
以某种方式防止这种情况。但我怀疑。
像这样的竞争条件很难调试和避免。