最近我一直在使用ftrace监控系统的某些行为特征。我通过一个小脚本来处理开关跟踪功能。运行脚本后,我的系统会崩溃并重新启动。最初,我认为脚本本身可能存在错误,但后来确定造成崩溃和重启的原因是在current_tracer
设置为function_graph时向/sys/kernel/debug/tracing/current_tracer中echo
了一些跟踪器。
也就是说,下面这个命令序列将导致系统崩溃/重启:
echo "function_graph" > /sys/kernel/debug/tracing/current_tracer
echo "function" > /sys/kernel/debug/tracing/current_tracer
在上述echo
语句导致的崩溃重启期间,我看到大量输出,内容如下:
清理孤立的inode
<inode>
我尝试通过在C程序中将current_tracer
值从function_graph替换为其他值来复现此问题:
#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?
更新
正如vilemetti所指出的那样,其他人也遇到了类似的问题(参见此处)。
ftrace_disable_ftrace_graph_caller()
修改了在ftrace_graph_call
的jmp指令, 假设它是一个5字节的near jmp(e9)。 然而,它只是由2个字节组成的短跳(eb)。ftrace_stub()
位于ftrace_graph_caller
的下面,所以上面的修改破坏了该指令,导致内核 oops 在ftrace_stub()
处出现无效操作码,如下所示:
修补程序(如下所示)解决了echo
问题,但我仍然不明白为什么以前write()
没有问题而echo
有问题。
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)