2016-05-31 1 views
27

J'ai récemment joué avec ftrace pour surveiller certaines caractéristiques de comportement de mon système. J'ai géré la commutation de la trace on/off via un petit script. Après l'exécution du script, mon système planterait et redémarrerait lui-même. Initialement, je pensais qu'il y avait peut-être une erreur avec le script lui-même, mais j'ai depuis déterminé que le crash et le redémarrage sont le résultat d'un traceur vers/sys/kernel/debug/tracing/current_tracer lorsque current_tracer est défini sur function_graph .ftrace: panne du système lors du changement de current_tracer depuis function_graph via echo

C'est, la séquence de commandes suivante produira le crash/reboot:

echo "function_graph" > /sys/kernel/debug/tracing/current_tracer 
echo "function" > /sys/kernel/debug/tracing/current_tracer 

Durning le redémarrage après l'accident causé par les déclarations echo ci-dessus, je vois beaucoup de sortie qui se lit comme suit:

inode <inode>

devenus orphelins compensation

j'ai essayé de reproduire ce problème en remplaçant la valeur current_tracer de function_graph à quelque chose d'autre dans un programme 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; 
} 

Étrangement, le programme C ne se bloque pas mon système.

J'ai d'abord rencontré ce problème lors de l'utilisation d'Ubuntu (environnement Unity) 16.04 LTS et j'ai confirmé qu'il s'agissait d'un problème sur les noyaux 4.4.0 et 4.5.5. J'ai également testé ce problème sur une machine exécutant Ubuntu (environnement Mate) 15.10, sur les noyaux 4.2.0 et 4.5.5, mais je n'ai pas réussi à reproduire le problème. Cela m'a seulement confus davantage.

Quelqu'un peut-il me donner un aperçu de ce qui se passe? Plus précisément, pourquoi devrais-je pouvoir write() mais pas echo vers/sys/kernel/debug/tracing/current_tracer?

Mise à jour

Comme Vielmetti a fait remarquer, d'autres ont eu un problème similaire (comme on le voit here).

Le ftrace_disable_ftrace_graph_caller() modifie l'instruction jmp à ftrace_graph_call en supposant qu'il est un 5 octets près de JMP (E9). Cependant, il s'agit d'un jmp court composé de 2 octets seulement (eb). Et ftrace_stub() est situé juste en dessous du ftrace_graph_caller si modification casse ci-dessus l'instruction résultant dans le noyau oops sur le ftrace_stub() avec l'opcode invalide comme ci-dessous:

Le patch (ci-dessous) a résolu le problème echo, mais je ne comprends toujours pas pourquoi echo cassait précédemment lorsque write() ne l'était pas.

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) 

via https://lkml.org/lkml/2016/5/16/493

+1

Avez-vous essayé de reproduire en utilisant un seul programme C (sans appels 'exec' sauf éventuellement pour l'appel' dd')? Parfois, les coquilles font des trucs funky. – o11c

+2

Avez-vous envisagé de poser votre question sur l'un des sites Linux Stack Exchange? – ashes999

+0

@ o11c, l'erreur semble se produire uniquement lors de l'écriture dans '/ sys/kernel/debug/tracing/current_tracer'. Je dis cela parce que je peux produire ce problème sans invoquer le script entier, mais plutôt en faisant juste un 'echo'ing à ce fichier. J'ai écouté votre suggestion dans cet esprit, et j'ai mis à jour mon post. – buratino

Répondre

2

On dirait que vous n'êtes pas la seule personne à remarquer ce problème.Je vois

comme un rapport du problème, et

comme patch pour le noyau qui l'aborde. En parcourant tout ce fil, il semble que le problème concerne certaines optimisations du compilateur.

+0

Le correctif résout le problème, mais je ne suis toujours pas clair sur ce qui se passe exactement. Si vous pouviez être plus précis dans votre réponse quant à ce qui se passe (même si cela implique de citer l'article que vous avez lié) ** et ** offrant un aperçu de la raison pour laquelle 'echo'ing le traceur function_graph ferait planter un système mais 'ecrire()' ne le ferait pas, alors je marquerai volontiers cette réponse comme complète et accorderai la prime. – buratino

+1

Le texte pertinent de l'article se lit comme suit. Je ne sais pas si cela s'applique à 100% à votre problème, mais cela devrait être utile. « Le ftrace_disable_ftrace_graph_caller() modifie l'instruction jmp à ftrace_graph_call en supposant qu'il est un 5 octets près de JMP (E9 ). Cependant, il est un court JMP composé de 2 octets seulement (eb ). Et ftrace_stub() est situé juste au-dessous le ftrace_graph_caller donc modification ci-dessus interrompt l'instruction résultant du noyau oops sur le ftrace_stub() avec l'opcode invalide. " – vielmetti

+0

Cela s'applique à mon problème, mais je ne comprends pas pourquoi c'est uniquement le cas pour 'echo' et non' write() '. – buratino