28

最近、システムの動作特性を監視するために ftrace をいじっています。小さなスクリプトを使用して、トレースのオン/オフを切り替えています。スクリプトを実行した後、システムがクラッシュして再起動しました。最初は、スクリプト自体にエラーがあるのではないかと考えていましたが、クラッシュと再起動は、関数グラフに設定されているechoときに /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ステートメントによってクラッシュが発生した後の再起動中に、次のような多くの出力が表示されます。

孤立した i ノードの消去<inode>

current_tracerfunction_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 を実行しているマシンでもこの問題をテストしましたが、問題を再現できませんでした。これは私をさらに混乱させるだけです。

何が起こっているのかについて誰かが私に洞察を与えることができますか? 具体的には、 /sys/kernel/debug/tracing/current_tracer にアクセスできるのにできないのはなぜwrite()ですか?echo

アップデート

vielmetti が指摘したように、他の人にも同様の問題がありました (ここで見られるように)。

jmp (e9) に近い 5 バイトであると想定してftrace_disable_ftrace_graph_caller()jmp 命令を変更 します。ftrace_graph_callただし、これは 2 バイトのみで構成される短い jmp です (eb )。And ftrace_stub()は のすぐ下にあるため、上記の変更により命令が中断され、以下のような無効なオペコードftrace_graph_callerでカーネル oops が発生します。ftrace_stub()

パッチ (以下を参照) で問題は解決しましたが、以前は壊れechoていなかったのに、なぜ壊れていたのかはまだわかりません。echowrite()

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