Недавно я играл с ftrace, чтобы отслеживать некоторые характеристики поведения моей системы. Я обрабатывал включение/выключение трассировки с помощью небольшого script. После запуска script моя система выйдет из строя и перезагрузится. Первоначально я считал, что может произойти ошибка с самим script, но с тех пор я решил, что авария и перезагрузка являются результатом echo
использования некоторого трассера для /sys/kernel/debug/tracing/current _tracer, когда current_tracer
установлено значение function_graph.
То есть следующая последовательность команд приведет к сбою/перезагрузке:
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 environment) 16.04 LTS и подтвердил, что это проблема в ядрах 4.4.0 и 4.5.5. Я также протестировал эту проблему на машине с Ubuntu (среда Mate) 15.10, в ядрах 4.2.0 и 4.5.5, но не смог воспроизвести проблему. Это только смутило меня дальше.
Может ли кто-нибудь дать мне понять, что происходит? В частности, почему я мог бы write()
, но не echo
в /sys/kernel/debug/tracing/current _tracer?
Обновление
Как отметил vielmetti, у других была аналогичная проблема (см. здесь).
ftrace_disable_ftrace_graph_caller()
изменяет инструкцию jmp вftrace_graph_call
, предположив, что он равен 5 байтам около jmp (e9). Однако это короткий jmp, состоящий только из 2 байтов (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)