最近、システムの動作特性を監視するために 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_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 を実行しているマシンでもこの問題をテストしましたが、問題を再現できませんでした。これは私をさらに混乱させるだけです。
何が起こっているのかについて誰かが私に洞察を与えることができますか? 具体的には、 /sys/kernel/debug/tracing/current_tracer にアクセスできるのにできないのはなぜwrite()
ですか?echo
アップデート
vielmetti が指摘したように、他の人にも同様の問題がありました (ここで見られるように)。
jmp (e9) に近い 5 バイトであると想定して
ftrace_disable_ftrace_graph_caller()
jmp 命令を変更 します。ftrace_graph_call
ただし、これは 2 バイトのみで構成される短い jmp です (eb )。Andftrace_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)