2016-05-31 48 views
27

我一直在玩ftrace來監視我的系統的一些行爲特徵。我一直在通過一個小腳本處理打開/關閉軌跡。運行腳本後,我的系統會崩潰並重新啓動。最初,我認爲腳本本身可能存在錯誤,但是我確定當current_tracer設置爲function_graph時,崩潰和重新啓動是由於/ sys/kernel/debug/tracing/current_tracer中的某個跟蹤器導致的。 。ftrace:從function_graph通過echo更改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內核中的一個問題。我也在運行Ubuntu(Mate環境)15.10的機器上,在4.2.0和4.5.5內核上測試過這個問題,但無法重現該問題。這隻會讓我更加困惑。

任何人都可以給我洞察發生了什麼?具體來說,爲什麼我能夠在/ sys/kernel/debug/tracing/current_tracer中將write()而不是echo

更新

由於vielmetti指出,其他人也有類似的問題(如圖here)。

ftrace_disable_ftrace_graph_caller()ftrace_graph_call修改jmp指令假設它的鄰近的jmp(E9)一個5個字節。 但它是一個由2個字節組成的簡短jmp(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) 

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

+1

您是否試過使用單個C程序進行復制(除了可能調用'dd'外沒有'exec'調用)?有時候貝殼會做些時髦的東西。 – o11c

+2

你有沒有考慮過在其中一個Linux Stack Exchange站點上提問? – ashes999

+0

@ o11c,錯誤似乎只在寫入'/ sys/kernel/debug/tracing/current_tracer'時纔會發生。我這樣說是因爲我可以在不調用整個腳本的情況下產生這個問題,而只是對該文件進行回聲。我謹慎地聽取了你的建議,並且更新了我的帖子。 – buratino

回答

2

看起來你是沒有注意到這種行爲的唯一的人。我看到

的問題的報告,並

的補丁,解決它的內核。通過整個線程看來,問題是一些編譯器優化。

+0

補丁解決了這個問題,但我仍然不清楚究竟發生了什麼。如果你可以在你的回答中更具體地瞭解正在發生的事情(即使它涉及引用你鏈接的文章)**和**提供見解,爲什麼'迴應function_graph示蹤劑會使系統崩潰但是'write()'不會,那麼我會很樂意將這個答案標記爲完整並獎勵賞金。 – buratino

+1

本文的相關文字內容如下。我不知道這是否適用於您的問題,但應該有所幫助。 「的ftrace_disable_ftrace_graph_caller()在 修改jmp指令ftrace_graph_call假設它是鄰近的jmp一個5個字節(E9 )。 然而,它的)一個短的jmp只由2個字節(EB ),且 ftrace_stub(位於正下方上述ftrace_graph_caller 修改將導致帶有無效操作碼的ftrace_stub()在內核oops 上的指令。「 – vielmetti

+0

它適用於我的問題,但我不清楚爲什麼這只是'echo'而不是'write()'的情況。 – buratino