Strace Π² Linux: история, устройство ΠΈ использованиС

Strace Π² Linux: история, устройство ΠΈ использованиС

Π’ Unix-ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Ρ… ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½Ρ‹Ρ… систСмах ΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ с внСшним ΠΌΠΈΡ€ΠΎΠΌ ΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΉ систСмой происходит Ρ‡Π΅Ρ€Π΅Π· нСбольшой Π½Π°Π±ΠΎΡ€ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ β€” систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ². А Π·Π½Π°Ρ‡ΠΈΡ‚, Π² ΠΎΡ‚Π»Π°Π΄ΠΎΡ‡Π½Ρ‹Ρ… цСлях ΠΏΠΎΠ»Π΅Π·Π½ΠΎ Π±Ρ‹Π²Π°Π΅Ρ‚ ΠΏΠΎΠ΄ΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π·Π° выполняСмыми процСссами систСмными Π²Ρ‹Π·ΠΎΠ²Π°ΠΌΠΈ.

Π‘Π»Π΅Π΄ΠΈΡ‚ΡŒ Π·Π° Β«ΠΈΠ½Ρ‚ΠΈΠΌΠ½ΠΎΠΉ Тизнью» ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ Π½Π° Linux ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ‚ ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Π° strace, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΠΈ посвящСна эта ΡΡ‚Π°Ρ‚ΡŒΡ. К ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°ΠΌ использования «шпионского» оборудования ΠΏΡ€ΠΈΠ»Π°Π³Π°ΡŽΡ‚ΡΡ краткая история strace ΠΈ описаниС устройства ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Ρ… ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ.

Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠ°Π½ΠΈΠ΅

ΠŸΡ€ΠΎΠΈΡΡ…ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ Π²ΠΈΠ΄ΠΎΠ²

Π“Π»Π°Π²Π½Ρ‹ΠΉ интСрфСйс ΠΌΠ΅ΠΆΠ΄Ρƒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°ΠΌΠΈ ΠΈ ядром OC Π² Unix β€” систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹ (Π°Π½Π³Π». system calls, syscalls), взаимодСйствиС ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ с внСшним ΠΌΠΈΡ€ΠΎΠΌ происходит ΠΈΡΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Ρ‡Π΅Ρ€Π΅Π· Π½ΠΈΡ….

Но Π² ΠΏΠ΅Ρ€Π²ΠΎΠΉ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠΉ вСрсии Unix (Version 6 Unix, 1975 Π³ΠΎΠ΄) ΡƒΠ΄ΠΎΠ±Π½Ρ‹Ρ… способов отслСТивания повСдСния ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… процСссов Π½Π΅ Π±Ρ‹Π»ΠΎ. Для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ этой ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ Bell Labs ΠΊ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ вСрсии (Version 7 Unix, 1979 Π³ΠΎΠ΄) ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠΈΠ»ΠΈ Π½ΠΎΠ²Ρ‹ΠΉ систСмный Π²Ρ‹Π·ΠΎΠ² β€” ptrace.

Разрабатывался ptrace ΠΏΡ€Π΅ΠΆΠ΄Π΅ всСго для ΠΈΠ½Ρ‚Π΅Ρ€Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… ΠΎΡ‚Π»Π°Π΄Ρ‡ΠΈΠΊΠΎΠ², Π½ΠΎ ΠΊ ΠΊΠΎΠ½Ρ†Ρƒ 80-Ρ… (Π² эпоху коммСрчСского ΡƒΠΆΠ΅ System V Release 4) Π½Π° этой основС появились ΠΈ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΈ ΡˆΠΈΡ€ΠΎΡ‡Π°ΠΉΡˆΠ΅Π΅ распространСниС ΡƒΠ·ΠΊΠΎΠ½Π°ΠΏΡ€Π°Π²Π»Π΅Π½Π½Ρ‹Π΅ ΠΎΡ‚Π»Π°Π΄Ρ‡ΠΈΠΊΠΈ β€” трассировщики систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ².

ΠŸΠ΅Ρ€Π²Π°Ρ ΠΆΠ΅ вСрсия strace Π±Ρ‹Π»Π° ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½Π° Полом ΠšΡ€ΠΎΠ½Π΅Π½Π±ΡƒΡ€Π³ΠΎΠΌ Π² рассылкС comp.sources.sun Π² 1992 Π³ΠΎΠ΄Ρƒ Π² качСствС Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Ρ‹ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΎΠΉ ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Π΅ trace ΠΎΡ‚ Sun. Как ΠΊΠ»ΠΎΠ½, Ρ‚Π°ΠΊ ΠΈ ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π» ΠΏΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π°Π»ΠΈΡΡŒ для SunOS, Π½ΠΎ ΠΊ 1994 Π³ΠΎΠ΄Ρƒ strace Π±Ρ‹Π»Π° ΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π° Π½Π° System V, Solaris ΠΈ Π½Π°Π±ΠΈΡ€Π°ΡŽΡ‰ΠΈΠΉ ΠΏΠΎΠΏΡƒΠ»ΡΡ€Π½ΠΎΡΡ‚ΡŒ Linux.

БСгодня strace ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Linux ΠΈ опираСтся Π½Π° всё Ρ‚ΠΎΡ‚ ΠΆΠ΅ ptrace, ΠΎΠ±Ρ€ΠΎΡΡˆΠΈΠΉ мноТСством Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠΉ.

Π‘ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ (ΠΈ вСсьма Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΉ) ΠΌΠ΅ΠΉΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ strace β€” Π”ΠΌΠΈΡ‚Ρ€ΠΈΠΉ Π›Π΅Π²ΠΈΠ½. Благодаря Π΅ΠΌΡƒ ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Π° обзавСлась ΠΏΡ€ΠΎΠ΄Π²ΠΈΠ½ΡƒΡ‚Ρ‹ΠΌΠΈ возмоТностями Π²Ρ€ΠΎΠ΄Π΅ ΠΈΠ½ΡŠΠ΅ΠΊΡ†ΠΈΠΈ ошибок Π² систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹, ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ ΡˆΠΈΡ€ΠΎΠΊΠΎΠ³ΠΎ спСктра Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€ ΠΈ, Π³Π»Π°Π²Π½ΠΎΠ΅, маскотом. ΠΠ΅ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½Ρ‹Π΅ источники ΡƒΡ‚Π²Π΅Ρ€ΠΆΠ΄Π°ΡŽΡ‚, Ρ‡Ρ‚ΠΎ Π²Ρ‹Π±ΠΎΡ€ ΠΏΠ°Π» Π½Π° страуса ΠΈΠ·-Π·Π° созвучности русского слова «страус» ΠΈ английского "strace".

НСмаловаТно ΠΈ Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ систСмный Π²Ρ‹Π·ΠΎΠ² ptrace ΠΈ трассировщики Ρ‚Π°ΠΊ ΠΈ Π½Π΅ Π±Ρ‹Π»ΠΈ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ‹ Π² POSIX, нСсмотря Π½Π° Π΄ΠΎΠ»Π³ΡƒΡŽ ΠΈΡΡ‚ΠΎΡ€ΠΈΡŽ ΠΈ Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Π² Linux, FreeBSD, OpenBSD ΠΈ Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½Ρ‹Ρ… Unix.

Устройство strace Π² Π΄Π²ΡƒΡ… словах: Piglet Trace

"You are not expected to understand this" (ДСннис Π ΠΈΡ‡ΠΈ, ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ Π² исходном ΠΊΠΎΠ΄Π΅ Version 6 Unix)

Π‘ Ρ€Π°Π½Π½Π΅Π³ΠΎ дСтства я Ρ‚Π΅Ρ€ΠΏΠ΅Ρ‚ΡŒ Π½Π΅ ΠΌΠΎΠ³Ρƒ Ρ‡Ρ‘Ρ€Π½Ρ‹Π΅ ящики: с ΠΈΠ³Ρ€ΡƒΡˆΠΊΠ°ΠΌΠΈ я Π½Π΅ ΠΈΠ³Ρ€Π°Π», Π° пытался Ρ€Π°Π·ΠΎΠ±Ρ€Π°Ρ‚ΡŒΡΡ Π² ΠΈΡ… устройствС (взрослыС употрСбляли слово Β«Π»ΠΎΠΌΠ°Π»Β», Π½ΠΎ Π½Π΅ Π²Π΅Ρ€ΡŒΡ‚Π΅ Π·Π»Ρ‹ΠΌ языкам). Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, поэтому ΠΌΠ½Π΅ Ρ‚Π°ΠΊ Π±Π»ΠΈΠ·ΠΊΠΈ Π½Π΅Ρ„ΠΎΡ€ΠΌΠ°Π»ΡŒΠ½Π°Ρ ΠΊΡƒΠ»ΡŒΡ‚ΡƒΡ€Π° ΠΏΠ΅Ρ€Π²Ρ‹Ρ… Unix ΠΈ соврСмСнного open-source-двиТСния.

Π’ Ρ€Π°ΠΌΠΊΠ°Ρ… этой ΡΡ‚Π°Ρ‚ΡŒΠΈ Ρ€Π°Π·Π±ΠΈΡ€Π°Ρ‚ΡŒ исходный ΠΊΠΎΠ΄ Ρ€Π°Π·Π΄ΠΎΠ±Ρ€Π΅Π²ΡˆΠ΅ΠΉ Π·Π° дСсятилСтия strace Π½Π΅Ρ€Π°Π·ΡƒΠΌΠ½ΠΎ. Но ΠΈ Ρ‚Π°ΠΉΠ½ для Ρ‡ΠΈΡ‚Π°Ρ‚Π΅Π»Π΅ΠΉ ΠΎΡΡ‚Π°Π²Π°Ρ‚ΡŒΡΡ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏ Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Ρ… strace ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ, я ΠΏΡ€ΠΈΠ²Π΅Π΄Ρƒ ΠΊΠΎΠ΄ ΠΌΠΈΠ½ΠΈΠ°Ρ‚ΡŽΡ€Π½ΠΎΠ³ΠΎ трассировщика β€” Piglet Trace (ptr). НичСго особСнного ΠΎΠ½ Π΄Π΅Π»Π°Ρ‚ΡŒ Π½Π΅ ΡƒΠΌΠ΅Π΅Ρ‚, Π½ΠΎ Π³Π»Π°Π²Π½ΠΎΠ΅ β€” систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ β€” Π²Ρ‹Π²ΠΎΠ΄ΠΈΡ‚:

$ gcc examples/piglet-trace.c -o ptr
$ ptr echo test > /dev/null
BRK(12) -> 94744690540544
ACCESS(21) -> 18446744073709551614
ACCESS(21) -> 18446744073709551614
unknown(257) -> 3
FSTAT(5) -> 0
MMAP(9) -> 140694657216512
CLOSE(3) -> 0
ACCESS(21) -> 18446744073709551614
unknown(257) -> 3
READ(0) -> 832
FSTAT(5) -> 0
MMAP(9) -> 140694657208320
MMAP(9) -> 140694650953728
MPROTECT(10) -> 0
MMAP(9) -> 140694655045632
MMAP(9) -> 140694655070208
CLOSE(3) -> 0
unknown(158) -> 0
MPROTECT(10) -> 0
MPROTECT(10) -> 0
MPROTECT(10) -> 0
MUNMAP(11) -> 0
BRK(12) -> 94744690540544
BRK(12) -> 94744690675712
unknown(257) -> 3
FSTAT(5) -> 0
MMAP(9) -> 140694646390784
CLOSE(3) -> 0
FSTAT(5) -> 0
IOCTL(16) -> 18446744073709551591
WRITE(1) -> 5
CLOSE(3) -> 0
CLOSE(3) -> 0
unknown(231)
Tracee terminated

Piglet Trace распознаСт порядка сотни систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² Linux (см. Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ) ΠΈ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π½Π° Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π΅ x86-64. Для ΡƒΡ‡Π΅Π±Π½Ρ‹Ρ… Ρ†Π΅Π»Π΅ΠΉ этого достаточно.

Π”Π°Π²Π°ΠΉΡ‚Π΅ Ρ€Π°Π·Π±Π΅Ρ€Ρ‘ΠΌ Ρ€Π°Π±ΠΎΡ‚Ρƒ нашСго ΠΊΠ»ΠΎΠ½Π°. Π’ случаС с Linux для ΠΎΡ‚Π»Π°Π΄Ρ‡ΠΈΠΊΠΎΠ² ΠΈ трассировщиков ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ, ΠΊΠ°ΠΊ ΡƒΠΏΠΎΠΌΠΈΠ½Π°Π»ΠΎΡΡŒ Π²Ρ‹ΡˆΠ΅, систСмный Π²Ρ‹Π·ΠΎΠ² ptrace. Он Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ посрСдством ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ Π² ΠΏΠ΅Ρ€Π²ΠΎΠΌ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π΅ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² ΠΊΠΎΠΌΠ°Π½Π΄, ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π½Π°ΠΌ Π½ΡƒΠΆΠ½Ρ‹ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ PTRACE_TRACEME, PTRACE_SYSCALL ΠΈ PTRACE_GETREGS.

Π Π°Π±ΠΎΡ‚Π° трассировщика начинаСтся Π² ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠΌ Unix-стилС: fork(2) запускаСт Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΠΉ процСсс, Π° Ρ‚ΠΎΡ‚ Π² свою ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ exec(3) запускаСт ΠΈΡΡΠ»Π΅Π΄ΡƒΠ΅ΠΌΡƒΡŽ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ. ЕдинствСнная Ρ‚ΠΎΠ½ΠΊΠΎΡΡ‚ΡŒ здСсь β€” Π²Ρ‹Π·ΠΎΠ² ptrace(PTRACE_TRACEME) ΠΏΠ΅Ρ€Π΅Π΄ exec: процСсс-ΠΏΠΎΡ‚ΠΎΠΌΠΎΠΊ ΠΎΠΆΠΈΠ΄Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ процСсс-Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒ Π±ΡƒΠ΄Π΅Ρ‚ Π΅Π³ΠΎ ΠΎΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Ρ‚ΡŒ:

pid_t child_pid = fork();
switch (child_pid) {
case -1:
    err(EXIT_FAILURE, "fork");
case 0:
    /* Child here */
    /* A traced mode has to be enabled. A parent will have to wait(2) for it
     * to happen. */
    ptrace(PTRACE_TRACEME, 0, NULL, NULL);
    /* Replace itself with a program to be run. */
    execvp(argv[1], argv + 1);
    err(EXIT_FAILURE, "exec");
}

ΠŸΡ€ΠΎΡ†Π΅ΡΡ-Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ wait(2) Π² Π΄ΠΎΡ‡Π΅Ρ€Π½Π΅ΠΌ процСссС, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ ΡƒΠ±Π΅Π΄ΠΈΡ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Π² Ρ€Π΅ΠΆΠΈΠΌ трассировки ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»ΠΎ:

/* Parent */

/* First we wait for the child to set the traced mode (see
 * ptrace(PTRACE_TRACEME) above) */
if (waitpid(child_pid, NULL, 0) == -1)
    err(EXIT_FAILURE, "traceme -> waitpid");

На этом приготовлСния Π·Π°ΠΊΠΎΠ½Ρ‡Π΅Π½Ρ‹ ΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΈΡΡ‚ΡƒΠΏΠ°Ρ‚ΡŒ нСпосрСдствСнно ΠΊ ΠΎΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΡŽ систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² Π² бСсконСчном Ρ†ΠΈΠΊΠ»Π΅.

Π’Ρ‹Π·ΠΎΠ² ptrace(PTRACE_SYSCALL) Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ wait родитСля Π·Π°Π²Π΅Ρ€ΡˆΠΈΡ‚ΡΡ Π»ΠΈΠ±ΠΎ ΠΏΠ΅Ρ€Π΅Π΄ исполнСниСм систСмного Π²Ρ‹Π·ΠΎΠ²Π°, Π»ΠΈΠ±ΠΎ сразу послС Π΅Π³ΠΎ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ. ΠœΠ΅ΠΆΠ΄Ρƒ двумя Π²Ρ‹Π·ΠΎΠ²Π°ΠΌΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²ΠΈΡ‚ΡŒ ΠΊΠ°ΠΊΠΈΠ΅-Π»ΠΈΠ±ΠΎ дСйствия: Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π²Ρ‹Π·ΠΎΠ² Π½Π° Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π½Ρ‹ΠΉ, ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹ ΠΈΠ»ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅.

Нам ΠΆΠ΅ достаточно Π΄Π²Π°ΠΆΠ΄Ρ‹ Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ ptrace(PTRACE_GETREGS), Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ состояниС рСгистра rax Π΄ΠΎ Π²Ρ‹Π·ΠΎΠ²Π° (Π½ΠΎΠΌΠ΅Ρ€ систСмного Π²Ρ‹Π·ΠΎΠ²Π°) ΠΈ сразу послС (Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΠΎΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅).

БобствСнно, Ρ†ΠΈΠΊΠ»:

/* A system call tracing loop, one interation per call. */
for (;;) {
    /* A non-portable structure defined for ptrace/GDB/strace usage mostly.
     * It allows to conveniently dump and access register state using
     * ptrace. */
    struct user_regs_struct registers;

    /* Enter syscall: continue execution until the next system call
     * beginning. Stop right before syscall.
     *
     * It's possible to change the system call number, system call
     * arguments, return value or even avoid executing the system call
     * completely. */
  if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL) == -1)
      err(EXIT_FAILURE, "enter_syscall");
  if (waitpid(child_pid, NULL, 0) == -1)
      err(EXIT_FAILURE, "enter_syscall -> waitpid");

  /* According to the x86-64 system call convention on Linux (see man 2
   * syscall) the number identifying a syscall should be put into the rax
   * general purpose register, with the rest of the arguments residing in
   * other general purpose registers (rdi,rsi, rdx, r10, r8, r9). */
  if (ptrace(PTRACE_GETREGS, child_pid, NULL, &registers) == -1)
      err(EXIT_FAILURE, "enter_syscall -> getregs");

  /* Note how orig_rax is used here. That's because on x86-64 rax is used
   * both for executing a syscall, and returning a value from it. To
   * differentiate between the cases both rax and orig_rax are updated on
   * syscall entry/exit, and only rax is updated on exit. */
  print_syscall_enter(registers.orig_rax);

  /* Exit syscall: execute of the syscall, and stop on system
   * call exit.
   *
   * More system call tinkering possible: change the return value, record
   * time it took to finish the system call, etc. */
  if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL) == -1)
      err(EXIT_FAILURE, "exit_syscall");
  if (waitpid(child_pid, NULL, 0) == -1)
      err(EXIT_FAILURE, "exit_syscall -> waitpid");

  /* Retrieve register state again as we want to inspect system call
   * return value. */
  if (ptrace(PTRACE_GETREGS, child_pid, NULL, &registers) == -1) {
      /* ESRCH is returned when a child terminates using a syscall and no
       * return value is possible, e.g. as a result of exit(2). */
      if (errno == ESRCH) {
          fprintf(stderr, "nTracee terminatedn");
          break;
      }
      err(EXIT_FAILURE, "exit_syscall -> getregs");
  }

  /* Done with this system call, let the next iteration handle the next
   * one */
  print_syscall_exit(registers.rax);
}

Π’ΠΎΡ‚ ΠΈ вСсь трассировщик. Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π²Ρ‹ Π·Π½Π°Π΅Ρ‚Π΅, с Ρ‡Π΅Π³ΠΎ Π½Π°Ρ‡ΠΈΠ½Π°Ρ‚ΡŒ ΠΎΡ‡Π΅Ρ€Π΅Π΄Π½ΠΎΠ΅ ΠΏΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ DTrace Π½Π° Linux.

Азы: запуск ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ ΠΏΠΎΠ΄ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ strace

Π’ качСствС ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° использования strace, ΠΏΠΎΠΆΠ°Π»ΡƒΠΉ, стоит привСсти самый простой способ β€” запуск прилоТСния ΠΏΠΎΠ΄ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ strace.

Π§Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ ΠΊΠΎΠΏΠ°Ρ‚ΡŒΡΡ Π² бСсконСчном спискС Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² Ρ‚ΠΈΠΏΠΈΡ‡Π½ΠΎΠΉ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹, напишСм ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½ΡƒΡŽ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ Π²ΠΎΠΊΡ€ΡƒΠ³ write:

int main(int argc, char *argv[])
{
    char str[] = "write me to stdoutn";
    /* write(2) is a simple wrapper around a syscall so it should be easy to
     * find in the syscall trace. */
    if (sizeof(str) != write(STDOUT_FILENO, str, sizeof(str))){
        perror("write");
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

Π‘ΠΎΠ±Π΅Ρ€Π΅ΠΌ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ ΠΈ убСдимся, Ρ‡Ρ‚ΠΎ ΠΎΠ½Π° Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚:

$ gcc examples/write-simple.c -o write-simple
$ ./write-simple
write me to stdout

И Π½Π°ΠΊΠΎΠ½Π΅Ρ† запустим Π΅Π΅ ΠΏΠΎΠ΄ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ strace:

$ strace ./write-simple
pexecve("./write", ["./write"], 0x7ffebd6145b0 /* 71 vars */) = 0
brk(NULL)                               = 0x55ff5489e000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=197410, ...}) = 0
mmap(NULL, 197410, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7a2a633000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF21133>1260342"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7a2a631000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7a2a04c000
mprotect(0x7f7a2a233000, 2097152, PROT_NONE) = 0
mmap(0x7f7a2a433000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f7a2a433000
mmap(0x7f7a2a439000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7a2a439000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f7a2a6324c0) = 0
mprotect(0x7f7a2a433000, 16384, PROT_READ) = 0
mprotect(0x55ff52b52000, 4096, PROT_READ) = 0
mprotect(0x7f7a2a664000, 4096, PROT_READ) = 0
munmap(0x7f7a2a633000, 197410)          = 0
write(1, "write me to stdoutn", 20write me to stdout
)  = 20
exit_group(0)                           = ?

ΠžΡ‡Π΅Π½ΡŒ «многословно» ΠΈ Π½Π΅ ΠΎΡ‡Π΅Π½ΡŒ ΠΏΠΎΠ·Π½Π°Π²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ. ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ здСсь Π΄Π²Π΅: Π²Ρ‹Π²ΠΎΠ΄ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ смСшан с Π²Ρ‹Π²ΠΎΠ΄ΠΎΠΌ strace ΠΈ ΠΈΠ·ΠΎΠ±ΠΈΠ»ΠΈΠ΅ систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ нас Π½Π΅ ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΡƒΡŽΡ‚.

Π Π°Π·Π΄Π΅Π»ΠΈΡ‚ΡŒ стандартный ΠΏΠΎΡ‚ΠΎΠΊ Π²Ρ‹Π²ΠΎΠ΄Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ ΠΈ Π²Ρ‹Π²ΠΎΠ΄ ошибок strace ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ ΠΊΠ»ΡŽΡ‡Π° -o, ΠΏΠ΅Ρ€Π΅Π½Π°ΠΏΡ€Π°Π²Π»ΡΡŽΡ‰Π΅Π³ΠΎ список систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² Π² Ρ„Π°ΠΉΠ»-Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚.

ΠžΡΡ‚Π°Π»ΠΎΡΡŒ Ρ€Π°Π·ΠΎΠ±Ρ€Π°Ρ‚ΡŒΡΡ с ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΎΠΉ Β«Π»ΠΈΡˆΠ½ΠΈΡ…Β» Π²Ρ‹Π·ΠΎΠ²ΠΎΠ². ΠŸΡ€Π΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠΈΠΌ, Ρ‡Ρ‚ΠΎ нас ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΡƒΡŽΡ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π²Ρ‹Π·ΠΎΠ²Ρ‹ write. ΠšΠ»ΡŽΡ‡ -e позволяСт ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ выраТСния, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ Π±ΡƒΠ΄ΡƒΡ‚ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹. Π‘Π°ΠΌΡ‹ΠΉ популярный Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ условия β€” СстСствСнно, trace=*, ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΡƒΡŽΡ‰ΠΈΠ΅ нас Π²Ρ‹Π·ΠΎΠ²Ρ‹.

ΠŸΡ€ΠΈ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΌ использовании -o ΠΈ -e ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ:

$ strace -e trace=write -owrite-simple.log ./write-simple
write me to stdout
$ cat write-simple.log
write(1, "write me to stdoutn", 20
)  = 20
+++ exited with 0 +++

Π’Π°ΠΊ, ΡΠΎΠ³Π»Π°ΡΠΈΡ‚Π΅ΡΡŒ, Π½Π°ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΡ‰Π΅ читаСтся.

А Π΅Ρ‰Ρ‘ ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ±ΠΈΡ€Π°Ρ‚ΡŒ систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹ β€” Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, связанныС с Π²Ρ‹Π΄Π΅Π»Π΅Π½ΠΈΠ΅ΠΌ ΠΈ освобоТдСниСм памяти:

$ strace -e trace=!brk,mmap,mprotect,munmap -owrite-simple.log ./write-simple
write me to stdout
$ cat write-simple.log
execve("./write-simple", ["./write-simple"], 0x7ffe9972a498 /* 69 vars */) = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=124066, ...}) = 0
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF21133>1260342"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f00f0be74c0) = 0
write(1, "write me to stdoutn", 20)  = 20
exit_group(0)                           = ?
+++ exited with 0 +++

ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π½Π° экранированный Π²ΠΎΡΠΊΠ»ΠΈΡ†Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ Π·Π½Π°ΠΊ Π² спискС ΠΈΡΠΊΠ»ΡŽΡ‡Ρ‘Π½Π½Ρ‹Ρ… Π²Ρ‹Π·ΠΎΠ²ΠΎΠ²: этого Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ командная ΠΎΠ±ΠΎΠ»ΠΎΡ‡ΠΊΠ° (Π°Π½Π³Π». shell).

Π’ ΠΌΠΎΠ΅ΠΉ вСрсии glibc Π·Π°Π²Π΅Ρ€ΡˆΠ°Π΅Ρ‚ исполнСниС процСсса систСмный Π²Ρ‹Π·ΠΎΠ² exit_group, Π° Π½Π΅ Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½Ρ‹ΠΉ _exit. Π’ этом состоит ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с систСмными Π²Ρ‹Π·ΠΎΠ²Π°ΠΌΠΈ: интСрфСйс, с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ программист, Π½Π΅ ΠΈΠΌΠ΅Π΅Ρ‚ прямого ΠΎΡ‚Π½ΠΎΡˆΠ΅Π½ΠΈΡ ΠΊ систСмным Π²Ρ‹Π·ΠΎΠ²Π°ΠΌ. Π‘ΠΎΠ»Π΅Π΅ Ρ‚ΠΎΠ³ΠΎ, ΠΎΠ½ рСгулярно мСняСтся Π² зависимости ΠΎΡ‚ Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΈ ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΡ‹.

Азы: присоСдинСниС ΠΊ процСссу Π½Π° Π»Π΅Ρ‚Ρƒ

Π˜Π·Π½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎ систСмный Π²Ρ‹Π·ΠΎΠ² ptrace, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ построСна strace, ΠΌΠΎΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΡ€ΠΈ запускС ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ Π² ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠΌ Ρ€Π΅ΠΆΠΈΠΌΠ΅. Π’Π°ΠΊΠΎΠ΅ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅, Π±Ρ‹Ρ‚ΡŒ ΠΌΠΎΠΆΠ΅Ρ‚, Π·Π²ΡƒΡ‡Π°Π»ΠΎ Ρ€Π°Π·ΡƒΠΌΠ½ΠΎ Π²ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½Π° Version 6 Unix. Π’ наши ΠΆΠ΅ Π΄Π½ΠΈ этого ΡƒΠΆΠ΅ нСдостаточно: Π±Ρ‹Π²Π°Π΅Ρ‚, Π½ΡƒΠΆΠ½ΠΎ ΠΈΡΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰Π΅ΠΉ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹. Π’ΠΈΠΏΠΈΡ‡Π½Ρ‹ΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ β€” Π·Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ Π½Π° дСскрипторС ΠΈΠ»ΠΈ спящий процСсс. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ соврСмСнная strace ΡƒΠΌΠ΅Π΅Ρ‚ ΠΏΡ€ΠΈΡΠΎΠ΅Π΄ΠΈΠ½ΡΡ‚ΡŒΡΡ ΠΊ процСссам Π½Π° Π»Π΅Ρ‚Ρƒ.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ Π·Π°Π²ΠΈΡΠ°ΡŽΡ‰Π΅ΠΉ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹:

int main(int argc, char *argv[])
{
    (void) argc; (void) argv;

    char str[] = "write men";

    write(STDOUT_FILENO, str, sizeof(str));

    /* Sleep indefinitely or until a signal arrives */
    pause();

    write(STDOUT_FILENO, str, sizeof(str));

    return EXIT_SUCCESS;
}

Π‘ΠΎΠ±Π΅Ρ€Ρ‘ΠΌ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ ΠΈ убСдимся Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ ΠΎΠ½Π° зависла:

$ gcc examples/write-sleep.c -o write-sleep
$ ./write-sleep
./write-sleep
write me
^C
$

А Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ ΠΏΡ€ΠΈΡΠΎΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒΡΡ ΠΊ Π½Π΅ΠΉ:

$ ./write-sleep &
[1] 15329
write me
$ strace -p 15329
strace: Process 15329 attached
pause(
^Cstrace: Process 15329 detached
 <detached ...>

ΠŸΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° Π·Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Π½Π° Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ pause. ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΠΌ, ΠΊΠ°ΠΊ ΠΎΠ½Π° ΠΎΡ‚Ρ€Π΅Π°Π³ΠΈΡ€ΡƒΠ΅Ρ‚ Π½Π° сигналы:

$ strace -o write-sleep.log -p 15329 &
strace: Process 15329 attached
$
$ kill -CONT 15329
$ cat write-sleep.log
pause()                                 = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=14989, si_uid=1001} ---
pause(
$
$ kill -TERM 15329
$ cat write-sleep.log
pause()                                 = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGCONT {si_signo=SIGCONT, si_code=SI_USER, si_pid=14989, si_uid=1001} ---
pause()                                 = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGTERM {si_signo=SIGTERM, si_code=SI_USER, si_pid=14989, si_uid=1001} ---
+++ killed by SIGTERM +++

ΠœΡ‹ запустили Π·Π°Π²ΠΈΡΠ½ΡƒΠ²ΡˆΡƒΡŽ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ ΠΈ ΠΏΡ€ΠΈΡΠΎΠ΅Π΄ΠΈΠ½ΠΈΠ»ΠΈΡΡŒ ΠΊ Π½Π΅ΠΉ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ strace. Π’Ρ‹ΡΡΠ½ΠΈΠ»ΠΈΡΡŒ Π΄Π²Π΅ Π²Π΅Ρ‰ΠΈ: систСмный Π²Ρ‹Π·ΠΎΠ² pause ΠΈΠ³Π½ΠΎΡ€ΠΈΡ€ΡƒΠ΅Ρ‚ сигналы Π±Π΅Π· ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² ΠΈ, Ρ‡Ρ‚ΠΎ интСрСснСС, strace отслСТиваСт Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹, Π½ΠΎ ΠΈ входящиС сигналы.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: отслСТиваниС Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΡ… процСссов

Π Π°Π±ΠΎΡ‚Π° с процСссами Ρ‡Π΅Ρ€Π΅Π· Π²Ρ‹Π·ΠΎΠ² fork β€” основа всСх Unix. Π”Π°Π²Π°ΠΉΡ‚Π΅ посмотрим, ΠΊΠ°ΠΊ strace Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ с Π΄Π΅Ρ€Π΅Π²ΠΎΠΌ процСссов Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ нСслоТной «плодящСйся» ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹:

int main(int argc, char *argv[])
{
    pid_t parent_pid = getpid();
    pid_t child_pid = fork();
    if (child_pid == 0) {
        /* A child is born! */
        child_pid = getpid();

        /* In the end of the day printf is just a call to write(2). */
        printf("child (self=%d)n", child_pid);
        exit(EXIT_SUCCESS);
    }

    printf("parent (self=%d, child=%d)n", parent_pid, child_pid);

    wait(NULL);

    exit(EXIT_SUCCESS);
}

Π—Π΄Π΅ΡΡŒ исходный процСсс создаёт Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΠΉ процСсс, ΠΎΠ±Π° ΠΏΠΈΡˆΡƒΡ‚ Π² стандартный ΠΏΠΎΡ‚ΠΎΠΊ Π²Ρ‹Π²ΠΎΠ΄Π°:

$ gcc examples/fork-write.c -o fork-write
$ ./fork-write
parent (self=11274, child=11275)
child (self=11275)

По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ ΠΌΡ‹ ΡƒΠ²ΠΈΠ΄ΠΈΠΌ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹ Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΎΠ³ΠΎ процСсса:

$ strace -e trace=write -ofork-write.log ./fork-write
child (self=22049)
parent (self=22048, child=22049)
$ cat fork-write.log
write(1, "parent (self=22048, child=22049)"..., 33) = 33
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=22049, si_uid=1001, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

ΠžΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Ρ‚ΡŒ Π΄Π΅Ρ€Π΅Π²ΠΎ процСссов Ρ†Π΅Π»ΠΈΠΊΠΎΠΌ ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ‚ Ρ„Π»Π°Π³ -f, с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ strace отслСТиваСт систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹ Π² процСссах-ΠΏΠΎΡ‚ΠΎΠΌΠΊΠ°Ρ…. К ΠΊΠ°ΠΆΠ΄ΠΎΠΉ строкС Π²Ρ‹Π²ΠΎΠ΄Π° ΠΏΡ€ΠΈ этом добавляСтся pid процСсса, Π΄Π΅Π»Π°ΡŽΡ‰Π΅Π³ΠΎ систСмный Π²Ρ‹Π²ΠΎΠ΄:

$ strace -f -e trace=write -ofork-write.log ./fork-write
parent (self=22710, child=22711)
child (self=22711)
$ cat fork-write.log
22710 write(1, "parent (self=22710, child=22711)"..., 33) = 33
22711 write(1, "child (self=22711)n", 19) = 19
22711 +++ exited with 0 +++
22710 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=22711, si_uid=1001, si_status=0, si_utime=0, si_stime=0} ---
22710 +++ exited with 0 +++

Π’ этом контСкстС ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€ΠΈΠ³ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΡ ΠΏΠΎ Π³Ρ€ΡƒΠΏΠΏΠ°ΠΌ систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ²:

$ strace -f -e trace=%process -ofork-write.log ./fork-write
parent (self=23610, child=23611)
child (self=23611)
$ cat fork-write.log
23610 execve("./fork-write", ["./fork-write"], 0x7fff696ff720 /* 63 vars */) = 0
23610 arch_prctl(ARCH_SET_FS, 0x7f3d03ba44c0) = 0
23610 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f3d03ba4790) = 23611
23610 wait4(-1,  <unfinished ...>
23611 exit_group(0)                     = ?
23611 +++ exited with 0 +++
23610 <... wait4 resumed> NULL, 0, NULL) = 23611
23610 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=23611, si_uid=1001, si_status=0, si_utime=0, si_stime=0} ---
23610 exit_group(0)                     = ?
23610 +++ exited with 0 +++

ΠšΡΡ‚Π°Ρ‚ΠΈ, ΠΊΠ°ΠΊΠΎΠΉ систСмный Π²Ρ‹Π·ΠΎΠ² ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для создания Π½ΠΎΠ²ΠΎΠ³ΠΎ процСсса?

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ΠΏΡƒΡ‚ΠΈ ΠΊ Ρ„Π°ΠΉΠ»Π°ΠΌ вмСсто дСскрипторов

Π—Π½Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ»ΠΎΠ²Ρ‹Π΅ дСскрипторы, бСзусловно, ΠΏΠΎΠ»Π΅Π·Π½ΠΎ, Π½ΠΎ ΠΈΠΌΠ΅Π½Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Ρ… Ρ„Π°ΠΉΠ»ΠΎΠ², ΠΊ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ обращаСтся ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°, Ρ‚ΠΎΠΆΠ΅ ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΡ€ΠΈΠ³ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ.

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰Π°Ρ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° ΠΏΠΈΡˆΠ΅Ρ‚ строку Π²ΠΎ Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ Ρ„Π°ΠΉΠ»:

void do_write(int out_fd)
{
    char str[] = "write me to a filen";

    if (sizeof(str) != write(out_fd, str, sizeof(str))){
        perror("write");
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char *argv[])
{
    char tmp_filename_template[] = "/tmp/output_fileXXXXXX";

    int out_fd = mkstemp(tmp_filename_template);
    if (out_fd == -1) {
        perror("mkstemp");
        exit(EXIT_FAILURE);
    }

    do_write(out_fd);

    return EXIT_SUCCESS;
}

ΠŸΡ€ΠΈ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠΌ Π²Ρ‹Π·ΠΎΠ²Π΅ strace ΠΏΠΎΠΊΠ°ΠΆΠ΅Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ числа-дСскриптора, ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½ΠΎΠ³ΠΎ Π² систСмный Π²Ρ‹Π·ΠΎΠ²:

$ strace -e trace=write -o write-tmp-file.log ./write-tmp-file
$ cat write-tmp-file.log
write(3, "write me to a filen", 20)  = 20
+++ exited with 0 +++

Π‘ Ρ„Π»Π°Π³ΠΎΠΌ -y ΡƒΡ‚ΠΈΠ»ΠΈΡ‚Π° ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ ΠΏΡƒΡ‚ΡŒ ΠΊ Ρ„Π°ΠΉΠ»Ρƒ, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ соотвСтствуСт дСскриптор:

$ strace -y -e trace=write -o write-tmp-file.log ./write-tmp-file
$ cat write-tmp-file.log
write(3</tmp/output_fileCf5MyW>, "write me to a filen", 20) = 20
+++ exited with 0 +++

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: отслСТиваниС ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠΉ ΠΊ Ρ„Π°ΠΉΠ»Π°ΠΌ

Π•Ρ‰Ρ‘ ΠΎΠ΄Π½Π° полСзная Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ: ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Ρ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹, связанныС с ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΌ Ρ„Π°ΠΉΠ»ΠΎΠΌ. Π‘Π»Π΅Π΄ΡƒΡŽΡ‰Π°Ρ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° дописываСт строку Π² ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹ΠΉ Ρ„Π°ΠΉΠ», ΠΏΠ΅Ρ€Π΅Π΄Π°Π½Π½Ρ‹ΠΉ Π² Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π΅:

void do_write(int out_fd)
{
    char str[] = "write me to a filen";

    if (sizeof(str) != write(out_fd, str, sizeof(str))){
        perror("write");
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char *argv[])
{
    /*
     * Path will be provided by the first program argument.
     *  */
    const char *path = argv[1];

    /*
     * Open an existing file for writing in append mode.
     *  */
    int out_fd = open(path, O_APPEND | O_WRONLY);
    if (out_fd == -1) {
        perror("open");
        exit(EXIT_FAILURE);
    }

    do_write(out_fd);

    return EXIT_SUCCESS;
}

По ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ strace Π²Ρ‹Π²ΠΎΠ΄ΠΈΡ‚ ΠΌΠ½ΠΎΠ³ΠΎ лишнСй ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ. Π€Π»Π°Π³ -P с Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ заставляСт strace Π²Ρ‹Π²ΠΎΠ΄ΠΈΡ‚ΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ обращСния ΠΊ ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠΌΡƒ Ρ„Π°ΠΉΠ»Ρƒ:

$ strace -y -P/tmp/test_file.log -o write-file.log ./write-file /tmp/test_file.log
$ cat write-file.log
openat(AT_FDCWD, "/tmp/test_file.log", O_WRONLY|O_APPEND) = 3</tmp/test_file.log>
write(3</tmp/test_file.log>, "write me to a filen", 20) = 20
+++ exited with 0 +++

ΠŸΡ€ΠΈΠΌΠ΅Ρ€: ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹

Π£Ρ‚ΠΈΠ»ΠΈΡ‚Π° strace ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠΌΠΎΡ‡ΡŒ ΠΈ ΠΏΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ с ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΠΉ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΎΠΉ. Π‘Π»Π΅Π΄ΡƒΡŽΡ‰Π°Ρ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° ΠΏΠΈΡˆΠ΅Ρ‚ Π² стандартный ΠΏΠΎΡ‚ΠΎΠΊ Π²Ρ‹Π²ΠΎΠ΄Π° ΠΈΠ· Π΄Π²ΡƒΡ… ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ²:

void *thread(void *arg)
{
    (void) arg;

    printf("Secondary thread: workingn");
    sleep(1);
    printf("Secondary thread: donen");

    return NULL;
}

int main(int argc, char *argv[])
{
    printf("Initial thread: launching a threadn");

    pthread_t thr;
    if (0 != pthread_create(&thr, NULL, thread, NULL)) {
        fprintf(stderr, "Initial thread: failed to create a thread");
        exit(EXIT_FAILURE);
    }

    printf("Initial thread: joining a threadn");
    if (0 != pthread_join(thr, NULL)) {
        fprintf(stderr, "Initial thread: failed to join a thread");
        exit(EXIT_FAILURE);
    };

    printf("Initial thread: done");

    exit(EXIT_SUCCESS);
}

Π‘ΠΎΠ±ΠΈΡ€Π°Ρ‚ΡŒ Π΅Ρ‘ Π½Π°Π΄ΠΎ, СстСствСнно, со ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΌ ΠΏΡ€ΠΈΠ²Π΅Ρ‚ΠΎΠΌ Π»ΠΈΠ½ΠΊΠΎΠ²Ρ‰ΠΈΠΊΡƒ β€” Ρ„Π»Π°Π³ΠΎΠΌ -pthread:

$ gcc examples/thread-write.c -pthread -o thread-write
$ ./thread-write
/thread-write
Initial thread: launching a thread
Initial thread: joining a thread
Secondary thread: working
Secondary thread: done
Initial thread: done
$

Π€Π»Π°Π³ -f, ΠΊΠ°ΠΊ ΠΈ Π² случаС с ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΌΠΈ процСссами, Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ Π² Π½Π°Ρ‡Π°Π»ΠΎ ΠΊΠ°ΠΆΠ΄ΠΎΠΉ строки pid процСсса.

ЕстСствСнно, Ρ€Π΅Ρ‡ΡŒ ΠΈΠ΄Ρ‘Ρ‚ Π½Π΅ ΠΎΠ± ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π΅ ΠΏΠΎΡ‚ΠΎΠΊΠ° Π² смыслС Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ стандарта POSIX Threads, Π° ΠΎ Π½ΠΎΠΌΠ΅Ρ€Π΅, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠΎΠΌ ΠΏΠ»Π°Π½ΠΈΡ€ΠΎΠ²Ρ‰ΠΈΠΊΠΎΠΌ Π·Π°Π΄Π°Ρ‡ Π² Linux. Π‘ Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния послСднСго Π½Π΅Ρ‚ Π½ΠΈΠΊΠ°ΠΊΠΈΡ… процСссов ΠΈ ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² β€” Π΅ΡΡ‚ΡŒ Π·Π°Π΄Π°Ρ‡ΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½Π°Π΄ΠΎ Ρ€Π°ΡΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΏΠΎ доступным ядрам ΠΌΠ°ΡˆΠΈΠ½Ρ‹.

ΠŸΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ Π² нСсколько ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² становится слишком ΠΌΠ½ΠΎΠ³ΠΎ:

$ strace -f -othread-write.log ./thread-write
$ wc -l thread-write.log
60 thread-write.log

Π˜ΠΌΠ΅Π΅Ρ‚ смысл ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ΡŒΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ΠΌ процСссами ΠΈ систСмным Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ write:

$ strace -f -e trace="%process,write" -othread-write.log ./thread-write
$ cat thread-write.log
18211 execve("./thread-write", ["./thread-write"], 0x7ffc6b8d58f0 /* 64 vars */) = 0
18211 arch_prctl(ARCH_SET_FS, 0x7f38ea3b7740) = 0
18211 write(1, "Initial thread: launching a thre"..., 35) = 35
18211 clone(child_stack=0x7f38e9ba2fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f38e9ba39d0, tls=0x7f38e9ba3700, child_tidptr=0x7f38e9ba39d0) = 18212
18211 write(1, "Initial thread: joining a thread"..., 33) = 33
18212 write(1, "Secondary thread: workingn", 26) = 26
18212 write(1, "Secondary thread: donen", 23) = 23
18212 exit(0)                           = ?
18212 +++ exited with 0 +++
18211 write(1, "Initial thread: done", 20) = 20
18211 exit_group(0)                     = ?
18211 +++ exited with 0 +++

ΠšΡΡ‚Π°Ρ‚ΠΈ, вопросы. Какой систСмный Π²Ρ‹Π·ΠΎΠ² ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для создания Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΠ°? Π§Π΅ΠΌ Ρ‚Π°ΠΊΠΎΠΉ Π²Ρ‹Π·ΠΎΠ² для ΠΏΠΎΡ‚ΠΎΠΊΠΎΠ² отличаСтся ΠΎΡ‚ Π²Ρ‹Π·ΠΎΠ²Π° для процСссов?

ΠœΠ°ΡΡ‚Π΅Ρ€-класс: стСк процСсса Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚ систСмного Π²Ρ‹Π·ΠΎΠ²Π°

Одна ΠΈΠ· Π½Π΅Π΄Π°Π²Π½ΠΎ ΠΏΠΎΡΠ²ΠΈΠ²ΡˆΠΈΡ…ΡΡ Π² strace возмоТностСй β€” ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ стСка Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚ систСмного Π²Ρ‹Π·ΠΎΠ²Π°. ΠŸΡ€ΠΎΡΡ‚ΠΎΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€:

void do_write(void)
{
    char str[] = "write me to stdoutn";
    if (sizeof(str) != write(STDOUT_FILENO, str, sizeof(str))){
        perror("write");
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char *argv[])
{
    do_write();
    return EXIT_SUCCESS;
}

ЕстСствСнно, Π²Ρ‹Π²ΠΎΠ΄ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ ΠΏΡ€ΠΈ этом становится ΠΎΡ‡Π΅Π½ΡŒ ΠΎΠ±ΡŠΡ‘ΠΌΠ½Ρ‹ΠΌ, ΠΈ, ΠΏΠΎΠΌΠΈΠΌΠΎ Ρ„Π»Π°Π³Π° -k (ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ стСка Π²Ρ‹Π·ΠΎΠ²ΠΎΠ²), ΠΈΠΌΠ΅Π΅Ρ‚ смысл Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠ²Π°Ρ‚ΡŒ систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹ ΠΏΠΎ ΠΈΠΌΠ΅Π½ΠΈ:

$ gcc examples/write-simple.c -o write-simple
$ strace -k -e trace=write -o write-simple.log ./write-simple
write me to stdout
$ cat write-simple.log
write(1, "write me to stdoutn", 20)  = 20
 > /lib/x86_64-linux-gnu/libc-2.27.so(__write+0x14) [0x110154]
 > /home/vkazanov/projects-my/strace-post/write-simple(do_write+0x50) [0x78a]
 > /home/vkazanov/projects-my/strace-post/write-simple(main+0x14) [0x7d1]
 > /lib/x86_64-linux-gnu/libc-2.27.so(__libc_start_main+0xe7) [0x21b97]
 > /home/vkazanov/projects-my/strace-post/write-simple(_start+0x2a) [0x65a]
+++ exited with 0 +++

ΠœΠ°ΡΡ‚Π΅Ρ€-класс: ΠΈΠ½ΡŠΠ΅ΠΊΡ†ΠΈΡ ошибок

И Π΅Ρ‰Ρ‘ ΠΎΠ΄Π½Π° новая ΠΈ ΠΎΡ‡Π΅Π½ΡŒ полСзная Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ: ΠΈΠ½ΡŠΠ΅ΠΊΡ†ΠΈΡ ошибок. Π’ΠΎΡ‚ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°, ΠΏΠΈΡˆΡƒΡ‰Π°Ρ Π΄Π²Π΅ строки Π² ΠΏΠΎΡ‚ΠΎΠΊ Π²Ρ‹Π²ΠΎΠ΄Π°:

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

void do_write(const char *str, ssize_t len)
{
    if (len != write(STDOUT_FILENO, str, (size_t)len)){
        perror("write");
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char *argv[])
{
    (void) argc; (void) argv;

    char str1[] = "write me 1n";
    do_write(str1, sizeof(str1));

    char str2[] = "write me 2n";
    do_write(str2, sizeof(str2));

    return EXIT_SUCCESS;
}

ΠžΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅ΠΌ ΠΎΠ±Π° Π²Ρ‹Π·ΠΎΠ²Π° write:

$ gcc examples/write-twice.c -o write-twice
$ ./write-twice
write me 1
write me 2
$ strace -e trace=write -owrite-twice.log ./write-twice
write me 1
write me 2
$ cat write-twice.log
write(1, "write me 1n", 12)          = 12
write(1, "write me 2n", 12)          = 12
+++ exited with 0 +++

А Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Π²Ρ‹Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅ inject, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΎΡˆΠΈΠ±ΠΊΡƒ EBADF Π²ΠΎ всС Π²Ρ‹Π·ΠΎΠ²Ρ‹ write:

$ strace -e trace=write -e inject=write:error=EBADF -owrite-twice.log ./write-twice
$ cat write-twice.log
write(1, "write me 1n", 12)          = -1 EBADF (Bad file descriptor) (INJECTED)
write(3, "write: Bad file descriptorn", 27) = -1 EBADF (Bad file descriptor) (INJECTED)
+++ exited with 1 +++

Π˜Π½Ρ‚Π΅Ρ€Π΅ΡΠ½ΠΎ, Ρ‡Ρ‚ΠΎ ошибки Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°ΡŽΡ‚ всС Π²Ρ‹Π·ΠΎΠ²Ρ‹ write, Π²ΠΊΠ»ΡŽΡ‡Π°Ρ Π²Ρ‹Π·ΠΎΠ², скрытый Π·Π° perror. Π˜ΠΌΠ΅Π΅Ρ‚ смысл Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Ρ‚ΡŒ ΠΎΡˆΠΈΠ±ΠΊΡƒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ ΠΈΠ· Π²Ρ‹Π·ΠΎΠ²ΠΎΠ²:

$ strace -e trace=write -e inject=write:error=EBADF:when=1 -owrite-twice.log ./write-twice
write: Bad file descriptor
$ cat write-twice.log
write(1, "write me 1n", 12)          = -1 EBADF (Bad file descriptor) (INJECTED)
write(3, "write: Bad file descriptorn", 27) = 27
+++ exited with 1 +++

Или Π²Ρ‚ΠΎΡ€ΠΎΠ³ΠΎ:

$ strace -e trace=write -e inject=write:error=EBADF:when=2 -owrite-twice.log ./write-twice
write me 1
write: Bad file descriptor
$ cat write-twice.log
write(1, "write me 1n", 12)          = 12
write(1, "write me 2n", 12)          = -1 EBADF (Bad file descriptor) (INJECTED)
write(3, "write: Bad file descriptorn", 27) = 27
+++ exited with 1 +++

Π’ΠΈΠΏ ошибки ΡƒΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ Π½Π΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ:

$ strace -e trace=write -e fault=write:when=1 -owrite-twice.log ./write-twice
$ cat write-twice.log
write(1, "write me 1n", 12)          = -1 ENOSYS (Function not implemented) (INJECTED)
write(3, "write: Function not implementedn", 32) = 32
+++ exited with 1 +++

Π’ сочСтании с Π΄Ρ€ΡƒΠ³ΠΈΠΌΠΈ Ρ„Π»Π°Π³Π°ΠΌΠΈ ΠΌΠΎΠΆΠ½ΠΎ Β«Π»ΠΎΠΌΠ°Ρ‚ΡŒΒ» обращСния ΠΊ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠΌΡƒ Ρ„Π°ΠΉΠ»Ρƒ. ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

$ strace -y -P/tmp/test_file.log -e inject=file:error=ENOENT -o write-file.log ./write-file /tmp/test_file.log
open: No such file or directory
$ cat write-file.log
openat(AT_FDCWD, "/tmp/test_file.log", O_WRONLY|O_APPEND) = -1 ENOENT (No such file or directory) (INJECTED)
+++ exited with 1 +++

Помимо ΠΈΠ½ΡŠΠ΅ΠΊΡ†ΠΈΠΉ ошибок, ΠΌΠΎΠΆΠ½ΠΎ Π²Π²ΠΎΠ΄ΠΈΡ‚ΡŒ Π·Π°Π΄Π΅Ρ€ΠΆΠΊΠΈ ΠΏΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² ΠΈΠ»ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ сигналов.

ПослСсловиС

Π£Ρ‚ΠΈΠ»ΠΈΡ‚Π° strace β€” простой ΠΈ Π½Π°Π΄Ρ‘ΠΆΠ½Ρ‹ΠΉ инструмСнт. Но ΠΏΠΎΠΌΠΈΠΌΠΎ систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² ΠΎΡ‚Π»Π°ΠΆΠΈΠ²Π°Ρ‚ΡŒ случаСтся ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ аспСкты Ρ€Π°Π±ΠΎΡ‚Ρ‹ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ ΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΉ систСмы. НапримСр, ΠΎΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Ρ‚ΡŒ Π²Ρ‹Π·ΠΎΠ²Ρ‹ динамичСски Π»ΠΈΠ½ΠΊΡƒΠ΅ΠΌΡ‹Ρ… Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ ΡƒΠΌΠ΅Π΅Ρ‚ ltrace, Π·Π°Π³Π»ΡΠ½ΡƒΡ‚ΡŒ Π² Ρ€Π°Π±ΠΎΡ‚Ρƒ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠΉ систСмы ΠΌΠΎΠ³ΡƒΡ‚ SystemTap ΠΈ ftrace, Π° Π³Π»ΡƒΠ±ΠΎΠΊΠΎ ΠΈΡΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ позволяСт perf. Π’Π΅ΠΌ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ ΠΈΠΌΠ΅Π½Π½ΠΎ strace β€” пСрвая линия ΠΎΠ±ΠΎΡ€ΠΎΠ½Ρ‹ Π² случаС ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ с собствСнными ΠΈ Ρ‡ΡƒΠΆΠΈΠΌΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°ΠΌΠΈ, ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽ я Π΅Ρ‘ ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ ΠΏΠ°Ρ€Ρƒ Ρ€Π°Π· Π² нСдСлю.

Π‘Π»ΠΎΠ²ΠΎΠΌ, Π»ΡŽΠ±ΠΈΡ‚Π΅ Unix, Ρ‡ΠΈΡ‚Π°ΠΉΡ‚Π΅ man 1 strace ΠΈ Π½Π΅ ΡΡ‚Π΅ΡΠ½ΡΠΉΡ‚Π΅ΡΡŒ ΠΏΠΎΠ΄Π³Π»ΡΠ΄Ρ‹Π²Π°Ρ‚ΡŒ Π·Π° вашими ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ°ΠΌΠΈ!

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com