28

我最近一直在玩 ftrace 来监控我系统的一些行为特征。我一直在处理通过一个小脚本打开/关闭跟踪。运行脚本后,我的系统会崩溃并自行重启。最初,我认为脚本本身可能存在错误,但后来我确定崩溃和重新启动是由于在设置为 function_graphecho时将一些跟踪器添加到 /sys/kernel/debug/tracing/current_tracer造成的。current_tracer

也就是说,以下命令序列将产生崩溃/重启:

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 内核上的问题。我还在 4.2.0 和 4.5.5 内核上运行 Ubuntu(Mate 环境)15.10 的机器上测试了这个问题,但无法重现该问题。这只会让我更加困惑。

谁能告诉我正在发生的事情?具体来说,为什么我能够write()但不能echo/sys/kernel/debug/tracing/current_tracer?

更新

正如 vielmetti 指出的那样,其他人也遇到了类似的问题(如此处所示

ftrace_disable_ftrace_graph_caller()修改 jmp 指令 假设它在ftrace_graph_calljmp (e9) 附近有 5 个字节。然而,它是一个简短的 jmp,仅包含 2 个字节(eb)。并且 ftrace_stub()位于ftrace_graph_caller上面的所以修改的正下方,会破坏导致内核 oops 的指令,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)

通过https://lkml.org/lkml/2016/5/16/493

4

1 回答 1

3

看起来你不是唯一注意到这种行为的人。我懂了

作为问题的报告,以及

作为解决它的内核补丁。通读整个线程,问题似乎是一些编译器优化。

于 2016-06-12T20:35:24.510 回答