Єдина Країна! Единая Страна!

Зневадження коду за допомогою strace

Лютий 8th, 2009

Ви коли небуть ломали собі голову над пошуком помилки? Помилки, яку ніяк не вдається знайти у джерельних текстах, але яка часто проявляється після компіляції і запуску програми. Знайомтесь: strace. strace — це утиліта, яка дозволяє вам трасувати системні виклики і сигнали конкретної команди. Якої команди? А які у вас є?

strace — вільне програмне забезпечення, що розповсюджується на умовах ліцензії, подібної до ліцензії BSD. Спершу утиліта була написана Полом Краненбургом (Paul Kranenburg) для SunOS за мотивами іншої утиліти для SunOS, trace. На Linux її портував Бранко Ланкестер (Branko Lankester), котрий, окрім цього, реалізував її підтримку в ядрі. У 1993, Рік Следкі (Rick Sladkey) об’єднав strace 2.5 для SunOS з другою версією strace для Linux, додавши при цьому багато можливостей truss(1) з SVR4. В результаті, з’явилася strace, котра працювала на обох платформах. Сьогодні, strace підтримується Уічертом Аккерманом (Wichert Akkerman) і Роландом МакҐрасом (Roland McGrath).

Трасування helloworld

Робота strace полягає у перехопленні і записі системних викликів, виконаних процесом, а також отриманих ним сигналів. Для кожного системного виклику, до стандартного файлу помилок, або ж до будь якого іншого заданого файлу, виводиться його ім’я, аргументи і код виклику. Як щодо кількох прикладів?

Щоб не ускладнювати роботу strace, я використав найпростішу з можливих програм.

#include 
 
main()
{
    printf("Hello World n");
}

Компілюємо її:

# gcc -o helloworld helloworld.c

Готово! Запускаємо: ./helloworld, і отримаємо на стандартний вихід:

Hello World

Виглядає надзвичайно просто. Тепер запустимо strace. strace протрасує, і виведе на stderr усі системні виклики і сигнали:

# strace ./helloworld

Вивід команди `strace ./helloworld`:

 1  execve("./helloworld", ["./helloworld"], [/* 22 vars */]) = 0
 2  uname({sys="Linux", node="knuth", ...}) = 0
 3  brk(0)                                  = 0x81f7000
 4  access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
 5  open("/etc/ld.so.cache", O_RDONLY)      = 3
 6  fstat64(3, {st_mode=S_IFREG|0644, st_size=73856, ...}) = 0
 7  old_mmap(NULL, 73856, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fed000
 8  close(3)                                = 0
 9  open("/lib/tls/libc.so.6", O_RDONLY)    = 3
10  read(3, "177ELF111���������3�3�1��� ?B�00"..., 512) = 512
11  fstat64(3, {st_mode=S_IFREG|0755, st_size=1454835, ...}) = 0
12  old_mmap(0x40f000, 1215644, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x40f000
13  old_mmap(0x532000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x123000) = 0x532000
14  old_mmap(0x536000, 7324, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x536000
15  close(3)                                = 0
16  old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fec000
17  mprotect(0x532000, 4096, PROT_READ)     = 0
18  mprotect(0x40b000, 4096, PROT_READ)     = 0
19  set_thread_area({entry_number:-1 -> 6, base_addr:0xb7fec940, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
20  munmap(0xb7fed000, 73856)               = 0
21  fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
22  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fff000
23  write(1, "Hello World n", 13)          = 13
24  munmap(0xb7fff000, 4096)                = 0
25  exit_group(13)                          = ?

Перше, що ми бачимо — це execve. execve() виконує програму, задану ім’ям файлу. Далі, uname() отримує ім’я і тип системи, яку я використовував для тесту. Після цього, наша програма запитує певну кількість пам’яті і мапи розміщення розділюваних бібліотек, необхідних для роботи, наприклад, динамічний завантажувач і libc. До всієї цієї інформації ми скоро повернемося, а поки подивимося, що відбувається далі. Рядок 23 виводу трасування, нарешті відкриває нам призначення нашої програми helloworld (як би ми не бачили джерельного коду): вивести у файловий дескриптор 1 (STDOUT), повідомити кількість байт у виведеній інформації та їх значення. Не складно здогадатися, що застосування трасування може допомогти вам відстежити у програмі конкретний виклик або сигнал. Чим же це корисно? Якщо ви пишете програму трохи більш складну, ніж helloworld.c, і вона дає збій, видаючи мінімум, або взагалі не видаючи інформації про те, що з нею сталося, трасування може вам допомогти виявити причину, яка викликає збій у процесі роботи.

Трасування команди date

Давайте розглянемо трохи більш складний, але й більш реальний приклад. Програма date, окрім виведення дати і часу, виконує масу різних системних викликів. Запустіть трасування для команди дата:

# strace date

Вивід команди `strace date`:

 1  execve("/bin/date", ["date"], [/* 22 vars */]) = 0
 2  uname({sys="Linux", node="knuth", ...}) = 0
 3  brk(0)                                  = 0x9388000
 4  access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
 5  open("/etc/ld.so.cache", O_RDONLY)      = 3
 6  fstat64(3, {st_mode=S_IFREG|0644, st_size=73856, ...}) = 0
 7  old_mmap(NULL, 73856, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7fed000
 8  close(3)                                = 0
 9  open("/lib/tls/librt.so.1", O_RDONLY)   = 3
10  read(3, "177ELF111���������3�3�1���320260"..., 512) = 512
11  fstat64(3, {st_mode=S_IFREG|0755, st_size=47851, ...}) = 0
12  old_mmap(0x349000, 81656, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x349000
13  old_mmap(0x351000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x7000) = 0x351000
14  old_mmap(0x353000, 40696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x353000
15  close(3)                                = 0
16  open("/lib/tls/libc.so.6", O_RDONLY)    = 3
17  read(3, "177ELF111���������3�3�1��� ?B�00"..., 512) = 512
18  fstat64(3, {st_mode=S_IFREG|0755, st_size=1454835, ...}) = 0
19  old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fec000
20  old_mmap(0x40f000, 1215644, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x40f000
21  old_mmap(0x532000, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x123000) = 0x532000
22  old_mmap(0x536000, 7324, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x536000
23  close(3)                                = 0
24  open("/lib/tls/libpthread.so.0", O_RDONLY) = 3
25  read(3, "177ELF111���������3�3�1���@He�00"..., 512) = 512
26  fstat64(3, {st_mode=S_IFREG|0755, st_size=94480, ...}) = 0
27  old_mmap(0x650000, 70108, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x650000
28  old_mmap(0x65e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xd000) = 0x65e000
29  old_mmap(0x660000, 4572, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x660000
30  close(3)                                = 0
31  old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7feb000
32  mprotect(0x532000, 4096, PROT_READ)     = 0
33  mprotect(0x40b000, 4096, PROT_READ)     = 0
34  set_thread_area({entry_number:-1 -> 6, base_addr:0xb7feb6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
35  munmap(0xb7fed000, 73856)               = 0
36  set_tid_address(0xb7feb708)             = 12157
37  rt_sigaction(SIGRTMIN, {0x6543c0, [], SA_RESTORER|SA_SIGINFO, 0x65b8b0}, NULL, 8) = 0
38  rt_sigaction(SIGRT_1, {0x654430, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x65b8b0}, NULL, 8) = 0
39  rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
40  getrlimit(RLIMIT_STACK, {rlim_cur=10240*1024, rlim_max=RLIM_INFINITY}) = 0
41  _sysctl({{CTL_KERN, KERN_VERSION, 0, 20a31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 2, 0xbff7f448, 31, (nil), 0}) = 0
42  brk(0)                                  = 0x9388000
43  brk(0x93a9000)                          = 0x93a9000
44  open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE) = 3
45  fstat64(3, {st_mode=S_IFREG|0644, st_size=39554352, ...}) = 0
46  mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7deb000
47  close(3)                                = 0
48  open("/usr/share/locale/locale.alias", O_RDONLY) = 3
49  fstat64(3, {st_mode=S_IFREG|0644, st_size=2528, ...}) = 0
50  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7dea000
51  read(3, "# Locale name alias data base.n#"..., 4096) = 2528
52  read(3, "", 4096)                       = 0
53  close(3)                                = 0
54  munmap(0xb7dea000, 4096)                = 0
55  open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
56  open("/usr/share/locale/en_US.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
57  open("/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
58  open("/usr/share/locale/en.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
59  open("/usr/share/locale/en.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
60  open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
61  clock_gettime(CLOCK_REALTIME, {1122826951, 37464000}) = 0
62  open("/etc/localtime", O_RDONLY)        = 3
63  fstat64(3, {st_mode=S_IFREG|0644, st_size=1267, ...}) = 0
64  fstat64(3, {st_mode=S_IFREG|0644, st_size=1267, ...}) = 0
65  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7dea000
66  read(3, "TZif�������������������4���4�"..., 4096) = 1267
67  close(3)                                = 0
68  munmap(0xb7dea000, 4096)                = 0
69  stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=1267, ...}) = 0
70  stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=1267, ...}) = 0
71  fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
72  mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7dea000
73  write(1, "Sun Jul 31 12:22:31 EDT 2005n", 29) = 29
74  munmap(0xb7dea000, 4096)                = 0
75  exit_group(0)                           = ?

Ми бачимо виклики execve(), uname() і все ту ж інформацію компонувальника та інформацію про потоки (ми маємо на увазі виконання на одній і тій же системі, що видно за викликом uname()), після чого починається безпосередньо робота. Рядок 44 показує нам, що було викликано local.archive для визначення нашої локалі зі списку доступних (рядок 48). У рядку 61, date викликає clock_gettime() для визначення “реального часу”, після чого з’ясовується наш часовий пояс (рядок 62) за допомогою виклику read у рядку 66. Уся ця діяльність вінчає викликом написати в рядку 73 в тому ж форматі, що й у нашому прикладі Hello World, за винятком того, що у даному випадку, дата генерує висновок, ґрунтуючись на конфігурації з файлу, а не задається користувачем.

Тепер поговоримо про вивід компонувальника, потоки і пам’ять. Ми бачимо кілька посилань на різні компоненти LD. LD це компоновник GNU, а його робота полягає у складанні об’єктних і ресурсних файлів, переміщення їх даних та вирішенні символічних вказівників. Виклик LD, як правило, є останнім кроком у процесі компіляції програми. Окрім того, ми бачимо багато викликів, що виділяють і звільняють пам’ять. Кожен раз, коли ми щось читаємо або записуємо, ми бачимо виклики mmap (у нашому випадку mmap2) і munmap. Таким чином, наша програма розміщує в пам’яті файли, і пристрої. Це особливо зручно для налагодження програм, які інтенсивно використовують пам’ять, адже ви можете по запису та читання з пам’яті точно бачити, де конкретно в програмі написані виклики, які виконують операції розміщення в пам’яті. Детальна інформація по mmap2 доступна на сайті linuxinfor.com.

Трасування за ідентифікатором процесу

Якщо ви системний адміністратор, то, можливо, ви сидите і думаєте: “Яке відношення все це має до мене?”. Оскільки ви відповідаєте за надійне функціонування систем, то повинні тестувати системне ПЗ до і після оновлення. Часто системним адміністраторам доводиться бути позаштатними відладчик: знайти помилку, відправити звіт та все таке. І в цій справі трасування може сильно нагоді.

Однією з найбільш потужних можливостей трасування є приєднання до існуючого процесу для трасування його системних викликів. Робиться це за допомогою ключа-р із зазначенням ідентифікатора процесу, до якого потрібно підключитися. Наприклад, одного разу ви виявили нову версію Баш. Ви оновили свою систему, а на наступний день продовжили виконувати свої рутинні завдання. Але раптом ви виявляєте, що нова версія веде себе несподіваним чином.

Ось, як можна дізнатись ідентефікатор процесу командного інтерпретатора:

echo $$

Припустимо, що він рівний 12307. В іншому інтерпретаторі запустіть strace з ключем -p:

strace -p 12307

у відповідь побачите:

Process 12307 attached - interrupt to quit
read(0,

Незавершена функція read() очікує вводу. Припустмо, що користувач виконує команду date. Перше, що ми бачимо, оскільки вже під’єднані до процесу командного інтерпретатора, це введені символи:

Process 12307 attached - interrupt to quit
read(0, "d", 1) = 1
write(2, "d", 1) = 1
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
read(0, "a", 1) = 1
write(2, "a", 1) = 1
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
read(0, "t", 1) = 1
write(2, "t", 1) = 1
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
read(0, "e", 1) = 1
write(2, "e", 1) = 1
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
read(0,

І знову read() очікує. Користувач натискає клавішу Enter.

Вивід:

 1  "r", 1)                        = 1
 2  write(2, "n", 1)                       = 1
 3  rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
 4  ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig icanon echo ...}) = 0
 5  rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
 6  rt_sigaction(SIGINT, {0x8087415, [], SA_RESTORER, 0x436a48}, {0x80b6e56, [], SA_RESTORER, 0x436a48}, 8) = 0
 7  rt_sigaction(SIGTERM, {SIG_IGN}, {SIG_IGN}, 8) = 0
 8  rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_IGN}, 8) = 0
 9  rt_sigaction(SIGALRM, {0x8087357, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x436a48}, {0x80b6e56, [], SA_RESTORER, 0x436a48}, 8) = 0
10  rt_sigaction(SIGTSTP, {SIG_IGN}, {SIG_IGN}, 8) = 0
11  rt_sigaction(SIGTTOU, {SIG_IGN}, {SIG_IGN}, 8) = 0
12  rt_sigaction(SIGTTIN, {SIG_IGN}, {SIG_IGN}, 8) = 0
13  rt_sigaction(SIGWINCH, {0x8078544, [], SA_RESTORER, 0x436a48}, {0x80b6a20, [], SA_RESTORER|SA_RESTART, 0x436a48}, 8) = 0
14  rt_sigaction(SIGINT, {0x8087415, [], SA_RESTORER, 0x436a48}, {0x8087415, [], SA_RESTORER, 0x436a48}, 8) = 0
15  rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
16  rt_sigaction(SIGINT, {0x8087415, [], SA_RESTORER, 0x436a48}, {0x8087415, [], SA_RESTORER, 0x436a48}, 8) = 0
17  rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
18  write(1, "33]0;mfrye@knuth:~/articles/rh"..., 41) = 41
19  time(NULL)                              = 1123036120
20  rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0
21  ioctl(255, TIOCSPGRP, [12307])           = 0
22  rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
23  rt_sigaction(SIGINT, {0x8087415, [], SA_RESTORER, 0x436a48}, {0x8087415, [], SA_RESTORER, 0x436a48}, 8) = 0
24  rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
25  ioctl(0, TIOCGWINSZ, {ws_row=37, ws_col=125, ws_xpixel=0, ws_ypixel=0}) = 0
26  ioctl(0, TIOCSWINSZ, {ws_row=37, ws_col=125, ws_xpixel=0, ws_ypixel=0}) = 0
27  ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
28  ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig -icanon -echo ...}) = 0
29  rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
30  rt_sigaction(SIGINT, {0x80b6e56, [], SA_RESTORER, 0x436a48}, {0x8087415, [], SA_RESTORER, 0x436a48}, 8) = 0
31  rt_sigaction(SIGTERM, {0x80b6e56, [], SA_RESTORER, 0x436a48}, {SIG_IGN}, 8) = 0
32  rt_sigaction(SIGTERM, {SIG_IGN}, {0x80b6e56, [], SA_RESTORER, 0x436a48}, 8) = 0
33  rt_sigaction(SIGQUIT, {0x80b6e56, [], SA_RESTORER, 0x436a48}, {SIG_IGN}, 8) = 0
34  rt_sigaction(SIGQUIT, {SIG_IGN}, {0x80b6e56, [], SA_RESTORER, 0x436a48}, 8) = 0
35  rt_sigaction(SIGALRM, {0x80b6e56, [], SA_RESTORER, 0x436a48}, {0x8087357, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x436a48}, 8) = 0
36  rt_sigaction(SIGTSTP, {0x80b6e56, [], SA_RESTORER, 0x436a48}, {SIG_IGN}, 8) = 0
37  rt_sigaction(SIGTSTP, {SIG_IGN}, {0x80b6e56, [], SA_RESTORER, 0x436a48}, 8) = 0
38  rt_sigaction(SIGTTOU, {0x80b6e56, [], SA_RESTORER, 0x436a48}, {SIG_IGN}, 8) = 0
39  rt_sigaction(SIGTTOU, {SIG_IGN}, {0x80b6e56, [], SA_RESTORER, 0x436a48}, 8) = 0
40  rt_sigaction(SIGTTIN, {0x80b6e56, [], SA_RESTORER, 0x436a48}, {SIG_IGN}, 8) = 0
41  rt_sigaction(SIGTTIN, {SIG_IGN}, {0x80b6e56, [], SA_RESTORER, 0x436a48}, 8) = 0
42  rt_sigaction(SIGWINCH, {0x80b6a20, [], SA_RESTORER|SA_RESTART, 0x436a48}, {0x8078544, [], SA_RESTORER, 0x436a48}, 8) = 0
43  write(2, "[mfrye@knuth strace]$ ", 25) = 25
44  rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
45  read(0,

Пропустивши виклики rt_ і ioctl, у рядку 43 ми бачимо висновок нашого запрошення, записаного до самого командного інтерпретатора у рядку 18. Цей виклик write() служить гарним розділювач для усіх інших системних викликів, включаючи недокументовані виклики rt_. Ви можете спробувати це вдома. Ви розберетьсь. За допомогою strace ви можете трасувати усі типи помилок.
Висновокstrace — дуже корисна утиліти діагностики і відлагодження. Програмістам вона корисна при локалізації помилок, а системним адміністраторам — бесцінна при визначенні проблем у загальних програмах.


Міць strace полягає у можливості виявлення помилок, навіть коли джерельний код недоступний, або неможлива перекомпіляції. Це може допомогти при дослідженні роботи програмного забезпечення з закритим джерельним кодом. І хоча дехто вважає це “негативною” властивістю, у дійсності, вона може служити лише для поглиблення розуміння програмістом / адміністратором роботи таких програм. Зрештою, методи та стилі програмування, використані при написанні програмного, забезпечення залишаються у таємниці, не вимагаючи подальшого судового розгляду. strace’ійніть що-небудь.

This article is protected by the Open Publication License, V1.0 or later.
Copyright © 2005 by Red Hat, Inc.
Original article: http://www.redhat.com/magazine/010aug05/features/strace/

Коментарі

коментарі

Powered by Facebook Comments

1 коментар to “Зневадження коду за допомогою strace”

  1. Никита Says:

    Рекомендую взклянуть так же на ltrace—утилиту для трассировки библиотечных вызовов. К сожалению, она не может перехватить вызовы ф-ий из статичестких библиотек (т.к опирается на функции динамического линкера), но все ф-ии из динамических библиотек перехватывает.

Leave a Reply