我最近一直在使用 ftrace 来监控我系统的一些行为特征。我一直在通过一个小脚本来打开/关闭跟踪。运行脚本后,我的系统会崩溃并自行重新启动。最初,我认为脚本本身可能有错误,但后来我确定崩溃和重新启动是由于echo
将一些跟踪器添加到 /sys/kernel/debug/tracing/current_tracer 时current_tracer
设置为 function_graph。
也就是说,以下命令序列将导致崩溃/重新启动:
echo "function_graph" > /sys/kernel/debug/tracing/current_tracer
echo "function" > /sys/kernel/debug/tracing/current_tracer
由上述原因引起的崩溃后重新启动时echo
语句,我看到很多输出内容如下:
清除孤立的 inode<inode>
我尝试通过替换来重现这个问题current_tracer
从 function_graph 到 C 程序中其他内容的值:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int openCurrentTracer()
{
int fd = open("/sys/kernel/debug/tracing/current_tracer", O_WRONLY);
if(fd < 0)
exit(1);
return fd;
}
int writeTracer(int fd, char* tracer)
{
if(write(fd, tracer, strlen(tracer)) != strlen(tracer)) {
printf("Failure writing %s\n", tracer);
return 0;
}
return 1;
}
int main(int argc, char* argv[])
{
int fd = openCurrentTracer();
char* blockTracer = "blk";
if(!writeTracer(fd, blockTracer))
return 1;
close(fd);
fd = openCurrentTracer();
char* graphTracer = "function_graph";
if(!writeTracer(fd, graphTracer))
return 1;
close(fd);
printf("Preparing to fail!\n");
fd = openCurrentTracer();
if(!writeTracer(fd, blockTracer))
return 1;
close(fd);
return 0;
}
奇怪的是,C 程序并没有使我的系统崩溃。
我最初在使用 Ubuntu(Unity 环境)16.04 LTS 时遇到了这个问题,并确认这是 4.4.0 和 4.5.5 内核上的问题。我还在运行 Ubuntu(Mate 环境)15.10、4.2.0 和 4.5.5 内核的机器上测试了这个问题,但无法重现该问题。这只会让我更加困惑。
谁能让我了解正在发生的事情?具体来说,为什么我能够write()
但不是echo
到/sys/kernel/debug/tracing/current_tracer?
Update
正如维尔梅蒂指出的那样,其他人也遇到了类似的问题(如所见here https://lkml.org/lkml/2016/5/13/327).
The ftrace_disable_ftrace_graph_caller()
修改 jmp 指令ftrace_graph_call
假设它是 jmp (e9) 附近的 5 个字节。
然而,它是一个短 jmp,仅包含 2 个字节 (eb )。和ftrace_stub()
位于正下方ftrace_graph_caller
所以
上面的修改破坏了指令,导致内核错误
这ftrace_stub()
无效操作码如下:
该补丁(如下所示)解决了echo
问题,但我还是不明白为什么echo
之前被打破时write()
不是。
diff --git a/arch/x86/kernel/mcount_64.S b/arch/x86/kernel/mcount_64.S
index ed48a9f465f8..e13a695c3084 100644
--- a/arch/x86/kernel/mcount_64.S
+++ b/arch/x86/kernel/mcount_64.S
@@ -182,7 +182,8 @@ GLOBAL(ftrace_graph_call)
jmp ftrace_stub
#endif
-GLOBAL(ftrace_stub)
+/* This is weak to keep gas from relaxing the jumps */
+WEAK(ftrace_stub)
retq
END(ftrace_caller)
via https://lkml.org/lkml/2016/5/16/493 https://lkml.org/lkml/2016/5/16/493