Π£ Unix-ΠΏΠΎΠ΄ΡΠ±Π½ΠΈΡ ΠΎΠΏΠ΅ΡΠ°ΡΡΠΉΠ½ΠΈΡ ΡΠΈΡΡΠ΅ΠΌΠ°Ρ ΡΠΏΡΠ»ΠΊΡΠ²Π°Π½Π½Ρ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈ ΡΠ· Π·ΠΎΠ²Π½ΡΡΠ½ΡΠΌ ΡΠ²ΡΡΠΎΠΌ ΡΠ° ΠΎΠΏΠ΅ΡΠ°ΡΡΠΉΠ½ΠΎΡ ΡΠΈΡΡΠ΅ΠΌΠΎΡ Π²ΡΠ΄Π±ΡΠ²Π°ΡΡΡΡΡ ΡΠ΅ΡΠ΅Π· Π½Π΅Π²Π΅Π»ΠΈΠΊΠΈΠΉ Π½Π°Π±ΡΡ ΡΡΠ½ΠΊΡΡΠΉ β ΡΠΈΡΡΠ΅ΠΌΠ½ΠΈΡ Π²ΠΈΠΊΠ»ΠΈΠΊΡΠ². Π Π·Π½Π°ΡΠΈΡΡ, Ρ Π½Π°Π»Π°Π³ΠΎΠ΄ΠΆΡΠ²Π°Π»ΡΠ½ΠΈΡ ΡΡΠ»ΡΡ ΠΊΠΎΡΠΈΡΠ½ΠΎ Π±ΡΠ²Π°Ρ ΠΏΡΠ΄Π΄ΠΈΠ²ΠΈΡΠΈΡΡ Π·Π° ΡΠΈΡΡΠ΅ΠΌΠ½ΠΈΠΌΠΈ Π²ΠΈΠΊΠ»ΠΈΠΊΠ°ΠΌΠΈ, ΡΠΎ Π²ΠΈΠΊΠΎΠ½ΡΡΡΡΡΡ ΠΏΡΠΎΡΠ΅ΡΠ°ΠΌΠΈ.
Π‘Π»ΡΠ΄ΠΊΡΠ²Π°ΡΠΈ Π·Π° Β«ΡΠ½ΡΠΈΠΌΠ½ΠΈΠΌ ΠΆΠΈΡΡΡΠΌΒ» ΠΏΡΠΎΠ³ΡΠ°ΠΌ Π½Π° Linux Π΄ΠΎΠΏΠΎΠΌΠ°Π³Π°Ρ ΡΡΠΈΠ»ΡΡΠ° strace
, ΡΠΊΡΠΉ Ρ ΠΏΡΠΈΡΠ²ΡΡΠ΅Π½ΠΎ ΡΡ ΡΡΠ°ΡΡΡ. ΠΠΎ ΠΏΡΠΈΠΊΠ»Π°Π΄ΡΠ² Π²ΠΈΠΊΠΎΡΠΈΡΡΠ°Π½Π½Ρ Β«ΡΠΏΠΈΠ³ΡΠ½ΡΡΠΊΠΎΠ³ΠΎΒ» ΠΎΠ±Π»Π°Π΄Π½Π°Π½Π½Ρ Π΄ΠΎΠ΄Π°ΡΡΡΡΡ ΠΊΠΎΡΠΎΡΠΊΠ° ΡΡΡΠΎΡΡΡ strace
ΡΠ° ΠΎΠΏΠΈΡ ΠΏΡΠΈΡΡΡΠΎΡ ΠΏΠΎΠ΄ΡΠ±Π½ΠΈΡ
ΠΏΡΠΎΠ³ΡΠ°ΠΌ.
ΠΠΌΡΡΡ
ΠΏΠΎΡ ΠΎΠ΄ΠΆΠ΅Π½Π½Ρ Π²ΠΈΠ΄ΡΠ² ΠΡΠΈΡΡΡΡΠΉ strace Ρ Π΄Π²ΠΎΡ ΡΠ»ΠΎΠ²Π°Ρ : Piglet Trace ΠΠ·ΠΈ: Π·Π°ΠΏΡΡΠΊ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈ ΠΏΡΠ΄ ΠΊΠ΅ΡΡΠ²Π°Π½Π½ΡΠΌ strace ΠΠ·ΠΈ: ΠΏΡΠΈΡΠ΄Π½Π°Π½Π½Ρ Π΄ΠΎ ΠΏΡΠΎΡΠ΅ΡΡ Π½Π° Π»ΡΠΎΡΡ ΠΡΠΈΠΊΠ»Π°Π΄: Π²ΡΠ΄ΡΡΠ΅ΠΆΠ΅Π½Π½Ρ Π΄ΠΎΡΡΡΠ½ΡΡ ΠΏΡΠΎΡΠ΅ΡΡΠ² ΠΡΠΈΠΊΠ»Π°Π΄: ΡΠ»ΡΡ ΠΈ Π΄ΠΎ ΡΠ°ΠΉΠ»ΡΠ² Π·Π°ΠΌΡΡΡΡ Π΄Π΅ΡΠΊΡΠΈΠΏΡΠΎΡΡΠ² ΠΡΠΈΠΊΠ»Π°Π΄: Π²ΡΠ΄ΡΡΠ΅ΠΆΠ΅Π½Π½Ρ Π·Π²Π΅ΡΠ½Π΅Π½Ρ Π΄ΠΎ ΡΠ°ΠΉΠ»ΡΠ² ΠΏΡΠΈΠΊΠ»Π°Π΄: Π±Π°Π³Π°ΡΠΎΠΏΠΎΡΠΎΠΊΠΎΠ²Ρ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈ ΠΠ°ΠΉΡΡΠ΅Ρ-ΠΊΠ»Π°Ρ: ΡΡΠ΅ΠΊ ΠΏΡΠΎΡΠ΅ΡΡ Π² ΠΌΠΎΠΌΠ΅Π½Ρ ΡΠΈΡΡΠ΅ΠΌΠ½ΠΎΠ³ΠΎ Π²ΠΈΠΊΠ»ΠΈΠΊΡ ΠΠ°ΠΉΡΡΠ΅Ρ-ΠΊΠ»Π°Ρ: ΡΠ½'ΡΠΊΡΡΡ ΠΏΠΎΠΌΠΈΠ»ΠΎΠΊ ΠΡΡΠ»ΡΠΌΠΎΠ²Π°
ΠΏΠΎΡ ΠΎΠ΄ΠΆΠ΅Π½Π½Ρ Π²ΠΈΠ΄ΡΠ²
ΠΠΎΠ»ΠΎΠ²Π½ΠΈΠΉ ΡΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ ΠΌΡΠΆ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°ΠΌΠΈ ΡΠ° ΡΠ΄ΡΠΎΠΌ OC Π² Unix β ΡΠΈΡΡΠ΅ΠΌΠ½Ρ Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ (Π°Π½Π³Π». ΡΠΈΡΡΠ΅ΠΌΠ½Ρ Π΄Π·Π²ΡΠ½ΠΊΠΈ, ΡΠΈΡΡΠ΅ΠΌΠ½Ρ Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ), Π²Π·Π°ΡΠΌΠΎΠ΄ΡΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌ ΡΠ· Π·ΠΎΠ²Π½ΡΡΠ½ΡΠΌ ΡΠ²ΡΡΠΎΠΌ Π²ΡΠ΄Π±ΡΠ²Π°ΡΡΡΡΡ Π²ΠΈΠΊΠ»ΡΡΠ½ΠΎ ΡΠ΅ΡΠ΅Π· Π½ΠΈΡ .
ΠΠ»Π΅ Π² ΠΏΠ΅ΡΡΡΠΉ ΠΏΡΠ±Π»ΡΡΠ½ΡΠΉ Π²Π΅ΡΡΡΡ Unix (ptrace
.
Π ΠΎΠ·ΡΠΎΠ±Π»ΡΠ²ΡΡ ptrace Π½Π°ΡΠ°ΠΌΠΏΠ΅ΡΠ΅Π΄ Π΄Π»Ρ ΡΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΠΈΡ
Π½Π°Π»Π°Π³ΠΎΠ΄ΠΆΡΠ²Π°ΡΡΠ², Π°Π»Π΅ Π΄ΠΎ ΠΊΡΠ½ΡΡ 80-Ρ
(Π² Π΅ΠΏΠΎΡ
Ρ ΠΊΠΎΠΌΠ΅ΡΡΡΠΉΠ½ΠΎΠ³ΠΎ Π²ΠΆΠ΅
trace
Π²ΡΠ΄ Sun. Π―ΠΊ ΠΊΠ»ΠΎΠ½, Ρ ΠΎΡΠΈΠ³ΡΠ½Π°Π» ΠΏΡΠΈΠ·Π½Π°ΡΠ°Π»ΠΈΡΡ Π΄Π»Ρ SunOS, Π°Π»Π΅ Π΄ΠΎ 1994 ΡΠΎΠΊΡ strace
Π±ΡΠ»Π° ΠΏΠΎΡΡΠΎΠ²Π°Π½Π° Π½Π° System V, Solaris Ρ Linux, ΡΠΎ Π½Π°Π±ΠΈΡΠ°Ρ ΠΏΠΎΠΏΡΠ»ΡΡΠ½ΠΎΡΡΡ.
Π‘ΡΠΎΠ³ΠΎΠ΄Π½Ρ strace ΠΏΡΠ΄ΡΡΠΈΠΌΡΡ ΡΡΠ»ΡΠΊΠΈ Linux Ρ ΡΠΏΠΈΡΠ°ΡΡΡΡΡ Π½Π° Π²ΡΠ΅ ΡΠΎΠΉ ΠΆΠ΅ ptrace
, ΡΠΎ ΠΎΠ±ΡΠΎΡ Π±Π΅Π·Π»ΡΡΡΡ ΡΠΎΠ·ΡΠΈΡΠ΅Π½Ρ.
Π‘ΡΡΠ°ΡΠ½ΠΈΠΉ (Ρ Π΄ΡΠΆΠ΅ Π°ΠΊΡΠΈΠ²Π½ΠΈΠΉ) ΠΌΠ΅ΠΉΠ½ΡΠ΅ΠΉΠ½Π΅Ρ 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 ΠΏΡΠΎΠ³ΡΠ°ΠΌ, Ρ Π½Π°Π²Π΅Π΄Ρ ΠΊΠΎΠ΄ ΠΌΡΠ½ΡΠ°ΡΡΡΠ½ΠΎΠ³ΠΎ ΡΡΠ°ΡΡΠ²Π°Π»ΡΠ½ΠΈΠΊΠ°.
$ 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 (Π΄ΠΈΠ².
ΠΠ°Π²Π°ΠΉΡΠ΅ ΡΠΎΠ·Π±Π΅ΡΠ΅ΠΌΠΎ ΡΠΎΠ±ΠΎΡΡ Π½Π°ΡΠΎΠ³ΠΎ ΠΊΠ»ΠΎΠ½Ρ. Π£ Π²ΠΈΠΏΠ°Π΄ΠΊΡ Π· 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, ®isters) == -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, ®isters) == -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);
}
ΠΡΡ Ρ Π²Π΅ΡΡ ΡΡΠ°ΡΡΠ²Π°Π»ΡΠ½ΠΈΠΊ. Π’Π΅ΠΏΠ΅Ρ Π²ΠΈ Π·Π½Π°ΡΡΠ΅, Π· ΡΠΎΠ³ΠΎ ΡΠΎΠ·ΠΏΠΎΡΠΈΠ½Π°ΡΠΈ ΡΠ΅ΡΠ³ΠΎΠ²Π΅ ΠΏΠΎΡΡΡΠ²Π°Π½Π½Ρ
ΠΠ·ΠΈ: Π·Π°ΠΏΡΡΠΊ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈ ΠΏΡΠ΄ ΠΊΠ΅ΡΡΠ²Π°Π½Π½ΡΠΌ 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 +++
ΠΠ²Π΅ΡΠ½ΡΡΡ ΡΠ²Π°Π³Ρ Π½Π° Π΅ΠΊΡΠ°Π½ΠΎΠ²Π°Π½ΠΈΠΉ Π·Π½Π°ΠΊ ΠΎΠΊΠ»ΠΈΠΊΡ Π² ΡΠΏΠΈΡΠΊΡ Π²ΠΈΠΊΠ»ΡΡΠ΅Π½ΠΈΡ Π²ΠΈΠΊΠ»ΠΈΠΊΡΠ²: ΡΡΠΎΠ³ΠΎ Π²ΠΈΠΌΠ°Π³Π°Ρ ΠΊΠΎΠΌΠ°Π½Π΄Π½Π° ΠΎΠ±ΠΎΠ»ΠΎΠ½ΠΊΠ° (Π°Π½Π³Π». ΠΎΠ±ΠΎΠ»ΠΎΠ½ΠΊΠ°).
Π£ ΠΌΠΎΡΠΉ Π²Π΅ΡΡΡΡ 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
- ΠΡΠΎΡΡΠΈΠΉ Ρ Π½Π°Π΄ΡΠΉΠ½ΠΈΠΉ ΡΠ½ΡΡΡΡΠΌΠ΅Π½Ρ. ΠΠ»Π΅ ΠΊΡΡΠΌ ΡΠΈΡΡΠ΅ΠΌΠ½ΠΈΡ
Π²ΠΈΠΊΠ»ΠΈΠΊΡΠ² Π½Π°Π»Π°Π³ΠΎΠ΄ΠΆΡΠ²Π°ΡΠΈ ΡΡΠ°ΠΏΠ»ΡΡΡΡΡΡ ΠΉ ΡΠ½ΡΡ Π°ΡΠΏΠ΅ΠΊΡΠΈ ΡΠΎΠ±ΠΎΡΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌ ΡΠ° ΠΎΠΏΠ΅ΡΠ°ΡΡΠΉΠ½ΠΎΡ ΡΠΈΡΡΠ΅ΠΌΠΈ. ΠΠ°ΠΏΡΠΈΠΊΠ»Π°Π΄, Π²ΡΠ΄ΡΡΠ΅ΠΆΡΠ²Π°ΡΠΈ Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ Π±ΡΠ±Π»ΡΠΎΡΠ΅ΠΊ, ΡΠΎ Π΄ΠΈΠ½Π°ΠΌΡΡΠ½ΠΎ Π»ΡΠ½ΡΡΡΡΡΡ, Π²ΠΌΡΡ strace
β ΠΏΠ΅ΡΡΠ° Π»ΡΠ½ΡΡ ΠΎΠ±ΠΎΡΠΎΠ½ΠΈ Ρ ΡΠ°Π·Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌ ΡΠ· Π²Π»Π°ΡΠ½ΠΈΠΌΠΈ ΡΠ° ΡΡΠΆΠΈΠΌΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°ΠΌΠΈ, Ρ Π²ΠΈΠΊΠΎΡΠΈΡΡΠΎΠ²ΡΡ Ρ ΡΡ ΠΌΡΠ½ΡΠΌΡΠΌ ΠΏΠ°ΡΡ ΡΠ°Π·ΡΠ² Π½Π° ΡΠΈΠΆΠ΄Π΅Π½Ρ.
Π‘Π»ΠΎΠ²ΠΎΠΌ, Π»ΡΠ±ΠΈΡΠ΅ Unix, ΡΠΈΡΠ°ΠΉΡΠ΅ man 1 strace
Ρ Π½Π΅ ΡΠΎΡΠΎΠΌΡΠ΅ΡΡ ΠΏΡΠ΄Π³Π»ΡΠ΄Π°ΡΠΈ Π·Π° Π²Π°ΡΠΈΠΌΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°ΠΌΠΈ!
ΠΠΆΠ΅ΡΠ΅Π»ΠΎ: habr.com