linux - ftrace: fallo del sistema al cambiar current_tracer de function_graph a través de echo
shell linux-kernel (1)
He estado jugando con ftrace recientemente para monitorear algunas características de comportamiento de mi sistema. He estado manejando la activación / desactivación del rastreo a través de un pequeño script. Después de ejecutar el script, mi sistema se bloqueaba y se reiniciaba. Inicialmente, creí que podría haber un error con el script en sí mismo, pero desde entonces he determinado que el bloqueo y el reinicio son el resultado de echo
algún rastreador a / sys / kernel / debug / tracing / current_tracer cuando current_tracer
está configurado como function_graph .
Es decir, la siguiente secuencia de comandos producirá el bloqueo / reinicio:
echo "function_graph" > /sys/kernel/debug/tracing/current_tracer
echo "function" > /sys/kernel/debug/tracing/current_tracer
Durning el reinicio después de la caída causada por las declaraciones de echo
anteriores, veo un montón de salida que lee:
borrar inodo huérfano
<inode>
Intenté reproducir este problema reemplazando el valor current_tracer
de function_graph a otra cosa en un programa 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;
}
Curiosamente, el programa C no falla mi sistema.
Originalmente encontré este problema al usar Ubuntu (entorno Unity) 16.04 LTS y confirmé que era un problema en los kernels 4.4.0 y 4.5.5. También he probado este problema en una máquina que ejecuta Ubuntu (entorno Mate) 15.10, en los kernels 4.2.0 y 4.5.5, pero no pude reproducir el problema. Esto solo me ha confundido aún más.
¿Alguien puede darme una idea de lo que está pasando? Específicamente, ¿por qué podría write()
pero no hacer echo
en / sys / kernel / debug / tracing / current_tracer?
Actualizar
Como señaló vielmetti, otros han tenido un problema similar (como se ve here ).
ftrace_disable_ftrace_graph_caller()
modifica la instrucción jmp enftrace_graph_call
asumiendo que está a 5 bytes cerca de jmp (e9). Sin embargo, es un jmp corto que consta de 2 bytes solamente (eb). Yftrace_stub()
se encuentra justo debajo deftrace_graph_caller
por lo que la modificación anterior rompe la instrucción que da como resultado que el kernel oops enftrace_stub()
con el código de operación no válido, como se muestra a continuación:
El parche (que se muestra a continuación) solucionó el problema del echo
, pero aún no entiendo por qué el echo
estaba rompiendo anteriormente cuando no lo era 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)
a través de https://lkml.org/lkml/2016/5/16/493
Parece que no eres la única persona que nota este comportamiento. Ya veo
como un informe del problema, y
como un parche para el núcleo que lo dirige. Leyendo todo ese hilo, parece que el problema son algunas optimizaciones del compilador.