Linux: strace — отслеживаем выполнение процесса

Автор: | 02/17/2016
 

linux_logostrace — утилита для Linux, которая позволяет отследить выполнение системных вызовов (system call) и сигналов к ядру системы.

Для примера возьмем простую программу на С, которая выводит содержимое указанного файла:

#include <stdio.h>
#include <stdlib.h>

int main()
{
   char ch, file_name[25];
   FILE *fp;                          

   printf("Enter the name of file you wish to see\n");
   gets(file_name);
   fp = fopen(file_name,"r"); // read mode
   if( fp == NULL )
   {                       
      perror("Error while opening the file.\n");
      exit(EXIT_FAILURE);                                            
   }
                                                                   
   printf("The contents of %s file are :\n", file_name);
                                    
   while( ( ch = fgetc(fp) ) != EOF )
      printf("%c",ch);
   fclose(fp);
   return 0;
}

Ее выполнение:

$ ./openfile
Enter the name of file you wish to see
openfile.c                                                      
The contents of openfile.c file are :
#include <stdio.h>
#include <stdlib.h>
                              
int main()
{
...
   return 0;
}

Отображение всех вызовов

Запускаем openfile с starce:

$ strace ./openfile
execve("./openfile", ["./openfile"], [/* 30 vars */]) = 0
brk(0)                                  = 0x85b000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb7e074b000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY)      = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=47052, ...}) = 0
mmap(NULL, 47052, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fb7e073f000
close(3)                                = 0
open("/lib64/libc.so.6", O_RDONLY)      = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p\356\1\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1920936, ...}) = 0
mmap(NULL, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fb7e0199000
mprotect(0x7fb7e0323000, 2097152, PROT_NONE) = 0
mmap(0x7fb7e0523000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7fb7e0523000
mmap(0x7fb7e0528000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fb7e0528000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb7e073e000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb7e073d000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb7e073c000
arch_prctl(ARCH_SET_FS, 0x7fb7e073d700) = 0
mprotect(0x7fb7e0523000, 16384, PROT_READ) = 0
mprotect(0x7fb7e074c000, 4096, PROT_READ) = 0
munmap(0x7fb7e073f000, 47052)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 7), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb7e074a000
write(1, "Enter the name of file you wish "..., 39Enter the name of file you wish to see
) = 39
fstat(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 7), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb7e0749000
read(0, openfile.c
"openfile.c\n", 1024)           = 11
brk(0)                                  = 0x85b000
brk(0x87c000)                           = 0x87c000
open("openfile.c", O_RDONLY)            = 3
write(1, "The contents of openfile.c file "..., 38The contents of openfile.c file are :
) = 38
fstat(3, {st_mode=S_IFREG|0664, st_size=486, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb7e0748000
read(3, "#include <stdio.h>\n#include <std"..., 4096) = 486
write(1, "#include <stdio.h>\n", 19#include <stdio.h>
)    = 19
write(1, "#include <stdlib.h>\n", 20#include <stdlib.h>
...
write(1, "   fclose(fp);\n", 15   fclose(fp);
)        = 15
write(1, "   return 0;\n", 13   return 0;
)          = 13
write(1, "}\n", 2}
)                      = 2
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0x7fb7e0748000, 4096)            = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Например, тут виден вызов mmap(), который выполняет запрос на выделение памяти:

...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f115bfbb000
...

Проверяем:

# pmap 23342 | grep 7f115bfbb
00007f115bfbb000     12K rw---    [ anon ]

Или — файлы, к которым выполняется системный вызов open(), после передачи имени файла функции gets():

write(1, "Enter the name of file you wish "..., 39Enter the name of file you wish to see
) = 39
...
open("openfile.c", O_RDONLY)            = 3
write(1, "The contents of openfile.c file "..., 38The contents of openfile.c file are :
) = 38

Отображение определенных вызовов

Что бы вывести только список интересующих системных вызовов — используйте опцию -e.

Например — отобразить только вызовы open():

$ strace -e open ./openfile
open("/etc/ld.so.cache", O_RDONLY)      = 3
open("/lib64/libc.so.6", O_RDONLY)      = 3
Enter the name of file you wish to see
openfile.c
open("openfile.c", O_RDONLY)            = 3
The contents of openfile.c file are :
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
   char ch, stpchar, file_name[25];
   FILE *fp;
...

Что бы отобразить несколько определенных вызовов — добавьте -e trace=:

$ strace -e trace=open,mmap ./openfile
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd11b7a7000
open("/etc/ld.so.cache", O_RDONLY)      = 3
mmap(NULL, 47052, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fd11b79b000
open("/lib64/libc.so.6", O_RDONLY)      = 3
mmap(NULL, 3750152, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fd11b1f5000
mmap(0x7fd11b57f000, 20480, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x18a000) = 0x7fd11b57f000
mmap(0x7fd11b584000, 18696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fd11b584000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd11b79a000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd11b799000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd11b798000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd11b7a6000
Enter the name of file you wish to see
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd11b7a5000
...

Сохранение вывода в файл

Так как текста может быть много — иногда вывод удобнее сохранить в файл.

Для этого используйте опцию -o:

$ strace -o openfile_strace.log -e trace=open,mmap ./openfile
Enter the name of file you wish to see
sc
Error while opening the file.

Результат:

$ cat openfile_strace.log 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fee12b6f000
open("/etc/ld.so.cache", O_RDONLY)      = 3
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fee12b6d000
open("sc", O_RDONLY)                    = -1 ENOENT (No such file or directory)
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fee12b6c000
+++ exited with 1 +++

Трассировка вызовов запущенного процесса

Для подключения к уже работающему процессу — используйте опцию -p.

Например — берем любой PID:

$ ps -au setevoy
  PID TTY          TIME CMD
 1334 ?        00:04:17 uwsgi
 1335 ?        00:04:17 uwsgi
 1749 ?        00:13:26 uwsgi
...

И подключаемся к нему, записывая вывод в файл:

$ sudo strace -p 1334 -o uwsgi_strace.log -e mmap
[sudo] password for setevoy: 
Process 1334 attached
^CProcess 1334 detached

Проверяем:

$ cat uwsgi_strace.log 
lseek(2, 0, SEEK_CUR)                   = 525797
getsockopt(3, SOL_TCP, TCP_INFO, "\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0d\0\0\0"..., [104]) = 0
wait4(-1, 0x7ffc5a5853ac, WNOHANG, NULL) = 0
...
getsockopt(3, SOL_TCP, TCP_INFO, "\n\0\0\0\0\0\0\0@B\17\0\0\0\0\0\30\2\0\0\0\0\0\0\0\0\0\0d\0\0\0"..., [104]) = 0
wait4(-1, 0x7ffc5a5853ac, WNOHANG, NULL) = 0
epoll_wait(9,  <detached ...>

Отображение времени выполнения вызова

Добавим опцию -t:

$ strace -t -e trace=open,mmap ./openfile
14:04:51 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7a31229000
14:04:51 open("/etc/ld.so.cache", O_RDONLY) = 3
...
14:04:51 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7a3121b000
14:04:51 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7a3121a000
...

Статистика системных вызовов

С помощью опции -c — можно получить наглядную статистику выполнения программы:

$ strace -c ./openfile
Enter the name of file you wish to see
openfile.c
The contents of openfile.c file are :
#include <stdio.h>
#include <stdlib.h>
.. 

   fclose(fp);
   return 0;
}
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
  0.00    0.000000           0         4           read
  0.00    0.000000           0        31           write
  0.00    0.000000           0         3           open
  0.00    0.000000           0         3           close
  0.00    0.000000           0         5           fstat
  0.00    0.000000           0        11           mmap
  0.00    0.000000           0         3           mprotect
  0.00    0.000000           0         2           munmap
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         1           arch_prctl
------ ----------- ----------- --------- --------- ----------------
100.00    0.000000                    68         1 total

Получить подробную информацию о каждом вызове можно тут>>> .

Например, вызов:

...
open("openfile.c", O_RDONLY)            = 3
...

описан тут>>>.

А исходный код open() можно найти тут>>>, после чего — найти исходный код open.c, в котором в строке 1038 и описывается вызов open():

...
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
...

Ссылки по теме

http://www.digilife.be — LINUX System Call Quick Reference