ΠΠΎ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ²Π½ΠΈΡΠ΅ ΡΠΈΡΡΠ΅ΠΌΠΈ ΡΠ»ΠΈΡΠ½ΠΈ Π½Π° Unix, ΠΊΠΎΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΡΠ°ΡΠ° Π½Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°ΡΠ° ΡΠΎ Π½Π°Π΄Π²ΠΎΡΠ΅ΡΠ½ΠΈΠΎΡ ΡΠ²Π΅Ρ ΠΈ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ²Π½ΠΈΠΎΡ ΡΠΈΡΡΠ΅ΠΌ ΡΠ΅ ΡΠ»ΡΡΡΠ²Π° ΠΏΡΠ΅ΠΊΡ ΠΌΠ°Π» ΡΠ΅Ρ Π½Π° ΡΡΠ½ΠΊΡΠΈΠΈ - ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ²ΠΈΡΠΈ. ΠΠ²Π° Π·Π½Π°ΡΠΈ Π΄Π΅ΠΊΠ° Π·Π° ΡΠ΅Π»ΠΈΡΠ΅ Π½Π° Π΄Π΅Π±Π°Π³ΠΈΡΠ°ΡΠ΅ ΠΌΠΎΠΆΠ΅ Π΄Π° Π±ΠΈΠ΄Π΅ ΠΊΠΎΡΠΈΡΠ½ΠΎ Π΄Π° ΡΠ΅ ΡΠΏΠΈΠΎΠ½ΠΈΡΠ°Π°Ρ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈΡΠ΅ ΠΏΠΎΠ²ΠΈΡΠΈ ΡΡΠΎ ΡΠ΅ ΠΈΠ·Π²ΡΡΡΠ²Π°Π°Ρ ΠΎΠ΄ ΠΏΡΠΎΡΠ΅ΡΠΈΡΠ΅.
ΠΠ»Π°ΡΠΊΠ°ΡΠ° Π²ΠΈ ΠΏΠΎΠΌΠ°Π³Π° Π΄Π° Π³ΠΎ ΡΠ»Π΅Π΄ΠΈΡΠ΅ βΠΈΠ½ΡΠΈΠΌΠ½ΠΈΠΎΡ ΠΆΠΈΠ²ΠΎΡβ Π½Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈΡΠ΅ Π½Π° Linux strace
, ΡΡΠΎ Π΅ ΡΠ΅ΠΌΠ° Π½Π° ΠΎΠ²ΠΎΡ Π½Π°ΠΏΠΈΡ. ΠΡΠΈΠΌΠ΅ΡΠΈΡΠ΅ Π·Π° ΡΠΏΠΎΡΡΠ΅Π±Π° Π½Π° ΡΠΏΠΈΠΎΠ½ΡΠΊΠ° ΠΎΠΏΡΠ΅ΠΌΠ° ΡΠ΅ ΠΏΡΠΈΠ΄ΡΡΠΆΠ΅Π½ΠΈ ΡΠΎ ΠΊΡΠ°ΡΠΊΠ° ΠΈΡΡΠΎΡΠΈΡΠ° strace
ΠΈ ΠΎΠΏΠΈΡ Π½Π° Π΄ΠΈΠ·Π°ΡΠ½ΠΎΡ Π½Π° ΡΠ°ΠΊΠ²ΠΈΡΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈ.
ΡΠΎΠ΄ΡΠΆΠΈΠ½Π°
ΠΠΎΡΠ΅ΠΊΠ»ΠΎ Π½Π° Π²ΠΈΠ΄ΠΎΠ²ΠΈΡΠ΅ Π£ΡΠ΅Π΄ Π·Π° ΡΡΡΠ°ΡΡ Π½Π°ΠΊΡΠ°ΡΠΊΠΎ: Piglet Trace ΠΡΠ½ΠΎΠ²ΠΈ: Π²ΠΎΠ΄Π΅ΡΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ° ΠΊΠΎΡΠ° ΡΠ°Π±ΠΎΡΠΈ Π½Π° ΡΡΠ°ΡΠ° ΠΡΠ½ΠΎΠ²ΠΈ: ΠΏΡΠΈΠΊΠ»ΡΡΡΠ²Π°ΡΠ΅ Π½Π° ΠΏΡΠΎΡΠ΅ΡΠΎΡ Π²ΠΎ Π»Π΅Ρ ΠΡΠΈΠΌΠ΅Ρ: Π‘Π»Π΅Π΄Π΅ΡΠ΅ Π½Π° Π΄Π΅ΡΡΠΊΠΈ ΠΏΡΠΎΡΠ΅ΡΠΈ ΠΡΠΈΠΌΠ΅Ρ: ΠΏΠ°ΡΠ΅ΠΊΠΈ Π·Π° Π΄Π°ΡΠΎΡΠ΅ΠΊΠΈ Π½Π°ΠΌΠ΅ΡΡΠΎ ΡΠ°ΡΠΊΠΈ ΠΡΠΈΠΌΠ΅Ρ: Π‘Π»Π΅Π΄Π΅ΡΠ΅ Π½Π° ΠΏΡΠΈΡΡΠ°ΠΏ Π΄ΠΎ Π΄Π°ΡΠΎΡΠ΅ΠΊΠΈ ΠΡΠΈΠΌΠ΅Ρ: ΠΠΎΠ²Π΅ΡΠ΅Π½ΠΈΡΠΊΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈ ΠΠ°ΡΡΠ΅Ρ ΠΊΠ»Π°ΡΠ°: ΠΎΠ±ΡΠ°Π±ΠΎΡΠ΅ΡΠ΅ Π³ΠΎ ΡΡΠ΅ΠΊ Π²ΠΎ ΠΌΠΎΠΌΠ΅Π½ΡΠΎΡ Π½Π° ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ²ΠΈΠΊ ΠΠ°ΡΡΠ΅Ρ ΠΊΠ»Π°ΡΠ°: Π²Π±ΡΠΈΠ·Π³ΡΠ²Π°ΡΠ΅ Π½Π° Π³ΡΠ΅ΡΠΊΠ° ΠΠΎΠ³ΠΎΠ²ΠΎΡ
ΠΠΎΡΠ΅ΠΊΠ»ΠΎ Π½Π° Π²ΠΈΠ΄ΠΎΠ²ΠΈΡΠ΅
ΠΠ»Π°Π²Π½ΠΈΠΎΡ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΡΡ ΠΏΠΎΠΌΠ΅ΡΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈΡΠ΅ ΠΈ ΠΊΠ΅ΡΠ½Π΅Π»ΠΎΡ Π½Π° ΠΠ‘ Π²ΠΎ Unix ΡΠ΅ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈΡΠ΅ ΠΏΠΎΠ²ΠΈΡΠΈ. ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ²ΠΈΡΠΈ, syscals), ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΡΠ°ΡΠ° Π½Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈΡΠ΅ ΡΠΎ Π½Π°Π΄Π²ΠΎΡΠ΅ΡΠ½ΠΈΠΎΡ ΡΠ²Π΅Ρ ΡΠ΅ ΡΠ»ΡΡΡΠ²Π° ΠΈΡΠΊΠ»ΡΡΠΈΠ²ΠΎ ΠΏΡΠ΅ΠΊΡ Π½ΠΈΠ².
ΠΠΎ, Π²ΠΎ ΠΏΡΠ²Π°ΡΠ° ΡΠ°Π²Π½Π° Π²Π΅ΡΠ·ΠΈΡΠ° Π½Π° Unix (ptrace
.
ptrace Π±Π΅ΡΠ΅ ΡΠ°Π·Π²ΠΈΠ΅Π½ ΠΏΡΠ²Π΅Π½ΡΡΠ²Π΅Π½ΠΎ Π·Π° ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΠΈ Π΄Π΅Π±Π°Π³Π΅ΡΠΈ, Π½ΠΎ Π΄ΠΎ ΠΊΡΠ°ΡΠΎΡ Π½Π° 80-ΡΠΈΡΠ΅ (Π²ΠΎ Π΅ΡΠ°ΡΠ° Π½Π° ΠΊΠΎΠΌΠ΅ΡΡΠΈΡΠ°Π»Π½ΠΈ
trace
ΠΎΠ΄ Π‘ΠΎΠ½ΡΠ΅ΡΠΎ. Π ΠΊΠ»ΠΎΠ½ΠΎΡ ΠΈ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»ΠΎΡ Π±Π΅Π° Π½Π°ΠΌΠ΅Π½Π΅ΡΠΈ Π·Π° SunOS, Π½ΠΎ Π΄ΠΎ 1994 Π³ΠΎΠ΄ΠΈΠ½Π° strace
Π±Π΅ΡΠ΅ ΠΏΡΠ΅Π½Π΅ΡΠ΅Π½ Π½Π° System V, Solaris ΠΈ ΡΓ¨ ΠΏΠΎΠΏΠΎΠΏΡΠ»Π°ΡΠ½ΠΈΠΎΡ Linux.
ΠΠ΅Π½Π΅Ρ strace ΠΏΠΎΠ΄Π΄ΡΠΆΡΠ²Π° ΡΠ°ΠΌΠΎ Linux ΠΈ ΡΠ΅ ΠΏΠΎΡΠΏΠΈΡΠ° Π½Π° ΠΈΡΡΠΎΡΠΎ ptrace
, ΠΎΠ±ΡΠ°ΡΠ½Π°Ρ ΡΠΎ ΠΌΠ½ΠΎΠ³Ρ ΠΏΡΠΎΡΠΈΡΡΠ²Π°ΡΠ°.
ΠΠΎΠ΄Π΅ΡΠ΅Π½ (ΠΈ ΠΌΠ½ΠΎΠ³Ρ Π°ΠΊΡΠΈΠ²Π΅Π½) ΠΎΠ΄ΡΠΆΡΠ²Π°Ρ strace
-
ΠΡΡΠΎ ΡΠ°ΠΊΠ°, Π²Π°ΠΆΠ½ΠΎ Π΅ ΡΡΠΎ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈΠΎΡ ΠΏΠΎΠ²ΠΈΠΊ ptrace ΠΈ ΡΡΠ°ΡΠ΅ΡΠΈΡΠ΅ Π½ΠΈΠΊΠΎΠ³Π°Ρ Π½Π΅ Π±ΠΈΠ»Π΅ Π²ΠΊΠ»ΡΡΠ΅Π½ΠΈ Π²ΠΎ POSIX, ΠΈ ΠΏΠΎΠΊΡΠ°Ρ Π΄ΠΎΠ»Π³Π°ΡΠ° ΠΈΡΡΠΎΡΠΈΡΠ° ΠΈ ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½ΡΠ°ΡΠΈΡΠ° Π²ΠΎ Linux, FreeBSD, OpenBSD ΠΈ ΡΡΠ°Π΄ΠΈΡΠΈΠΎΠ½Π°Π»Π½ΠΈΠΎΡ Unix.
Π£ΡΠ΅Π΄ Π·Π° ΡΡΡΠ°ΡΡ Π½Π°ΠΊΡΠ°ΡΠΊΠΎ: Piglet Trace
βΠΠ΅ ΡΠ΅ ΠΎΡΠ΅ΠΊΡΠ²Π° Π΄Π° Π³ΠΎ ΡΠ°Π·Π±Π΅ΡΠ΅ΡΠ΅ ΠΎΠ²Π°β (ΠΠ΅Π½ΠΈΡ Π ΠΈΡΠΈ, ΠΊΠΎΠΌΠ΅Π½ΡΠ°Ρ Π²ΠΎ ΠΈΠ·Π²ΠΎΡΠ½ΠΈΠΎΡ ΠΊΠΎΠ΄ Π½Π° Unix Π²Π΅ΡΠ·ΠΈΡΠ° 6)
ΠΠ΄ ΡΠ°Π½ΠΎΡΠΎ Π΄Π΅ΡΡΡΠ²ΠΎ, Π½Π΅ ΠΌΠΎΠΆΠ°ΠΌ Π΄Π° ΠΏΠΎΠ΄Π½Π΅ΡΠ°ΠΌ ΡΡΠ½ΠΈ ΠΊΡΡΠΈΠΈ: Π½Π΅ ΡΠΈ ΠΈΠ³ΡΠ°Π² ΡΠΎ ΠΈΠ³ΡΠ°ΡΠΊΠΈ, ΡΡΠΊΡ ΡΠ΅ ΠΎΠ±ΠΈΠ΄ΠΎΠ² Π΄Π° ΡΠ° ΡΠ°Π·Π±Π΅ΡΠ°ΠΌ Π½ΠΈΠ²Π½Π°ΡΠ° ΡΡΡΡΠΊΡΡΡΠ° (Π²ΠΎΠ·ΡΠ°ΡΠ½ΠΈΡΠ΅ Π³ΠΎ ΠΊΠΎΡΠΈΡΡΠ΅Π° Π·Π±ΠΎΡΠΎΡ βΡΠΊΡΡΠΈβ, Π½ΠΎ Π½Π΅ ΠΈΠΌ Π²Π΅ΡΡΠ²Π°Π°Ρ Π½Π° Π·Π»ΠΈΡΠ΅ ΡΠ°Π·ΠΈΡΠΈ). ΠΠΎΠΆΠ΅Π±ΠΈ Π·Π°ΡΠΎΠ° Π½Π΅ΡΠΎΡΠΌΠ°Π»Π½Π°ΡΠ° ΠΊΡΠ»ΡΡΡΠ° Π½Π° ΠΏΡΠ²ΠΈΠΎΡ Unix ΠΈ ΠΌΠΎΠ΄Π΅ΡΠ½ΠΎΡΠΎ Π΄Π²ΠΈΠΆΠ΅ΡΠ΅ Π·Π° ΠΎΡΠ²ΠΎΡΠ΅Π½ ΠΊΠΎΠ΄ ΠΌΠΈ Π΅ ΡΠΎΠ»ΠΊΡ Π±Π»ΠΈΡΠΊΡ.
ΠΠ° ΡΠ΅Π»ΠΈΡΠ΅ Π½Π° ΠΎΠ²ΠΎΡ Π½Π°ΠΏΠΈΡ, Π½Π΅ΡΠ°Π·ΡΠΌΠ½ΠΎ Π΅ Π΄Π° ΡΠ΅ ΡΠ°ΡΠΊΠ»ΠΎΠΏΠΈ ΠΈΠ·Π²ΠΎΡΠ½ΠΈΠΎΡ ΠΊΠΎΠ΄ Π½Π° 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
.
ΠΠ° Π΄Π° Π½Π΅ Π½Π°Π²Π»Π΅Π³ΡΠ²Π°ΠΌΠ΅ Π²ΠΎ Π±Π΅ΡΠΊΡΠ°ΡΠ½Π°ΡΠ° Π»ΠΈΡΡΠ° Π½Π° ΠΏΠΎΠ²ΠΈΡΠΈ Π½Π° ΡΠΈΠΏΠΈΡΠ½Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°, ΠΏΠΈΡΡΠ²Π°ΠΌΠ΅ 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 ./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
ΠΈ ΠΈΠ·ΠΎΠ±ΠΈΠ»ΡΡΠ²ΠΎ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ²ΠΈΡΠΈ ΠΊΠΎΠΈ Π½Π΅ Π½Γ¨ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠΈΡΠ°Π°Ρ.
ΠΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π³ΠΎ ΡΠ°Π·Π΄Π²ΠΎΠΈΡΠ΅ ΡΡΠ°Π½Π΄Π°ΡΠ΄Π½ΠΈΠΎΡ ΠΈΠ·Π»Π΅Π·Π΅Π½ ΡΠ΅ΠΊ Π½Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°ΡΠ° ΠΈ ΠΈΠ·Π»Π΅Π·ΠΎΡ Π½Π° Π³ΡΠ΅ΡΠΊΠ° Π²ΠΎ ΡΡΠ°ΡΠ°ΡΠ° ΠΊΠΎΡΠΈΡΡΠ΅ΡΡΠΈ Π³ΠΎ ΠΏΡΠ΅ΠΊΠΈΠ½ΡΠ²Π°ΡΠΎΡ -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
, ΠΌΠΎΠΆΠ΅ Π΄Π° ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠΈ ΡΠ°ΠΌΠΎ ΠΏΡΠΈ ΠΈΠ·Π²ΡΡΡΠ²Π°ΡΠ΅ Π½Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°ΡΠ° Π²ΠΎ ΠΏΠΎΡΠ΅Π±Π΅Π½ ΡΠ΅ΠΆΠΈΠΌ. ΠΠ²Π° ΠΎΠ³ΡΠ°Π½ΠΈΡΡΠ²Π°ΡΠ΅ ΠΌΠΎΠΆΠ΅Π±ΠΈ Π·Π²ΡΡΠ΅ΡΠ΅ ΡΠ°Π·ΡΠΌΠ½ΠΎ Π²ΠΎ Π΄Π΅Π½ΠΎΠ²ΠΈΡΠ΅ Π½Π° Π²Π΅ΡΠ·ΠΈΡΠ°ΡΠ° 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
. ΠΠ²Π΅ ΡΠ°Π±ΠΎΡΠΈ ΡΡΠ°Π½Π°Π° ΡΠ°ΡΠ½ΠΈ: ΠΏΠ°ΡΠ·ΠΈΡΠ°ΡΠΊΠΈΠΎΡ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ²ΠΈΠΊ Π³ΠΈ ΠΈΠ³Π½ΠΎΡΠΈΡΠ° ΡΠΈΠ³Π½Π°Π»ΠΈΡΠ΅ Π±Π΅Π· ΡΠ°ΠΊΡΠ²Π°ΡΠΈ ΠΈ, ΡΡΡΠ΅ ΠΏΠΎΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎ, strace ΠΌΠΎΠ½ΠΈΡΠΎΡΠΈ Π½Π΅ ΡΠ°ΠΌΠΎ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ²ΠΈΡΠΈ, ΡΡΠΊΡ ΠΈ Π΄ΠΎΡΠ΄ΠΎΠ²Π½ΠΈ ΡΠΈΠ³Π½Π°Π»ΠΈ.
ΠΡΠΈΠΌΠ΅Ρ: Π‘Π»Π΅Π΄Π΅ΡΠ΅ Π½Π° Π΄Π΅ΡΡΠΊΠΈ ΠΏΡΠΎΡΠ΅ΡΠΈ
Π Π°Π±ΠΎΡΠ° ΡΠΎ ΠΏΡΠΎΡΠ΅ΡΠΈ ΠΏΡΠ΅ΠΊΡ ΠΏΠΎΠ²ΠΈΠΊ fork
- ΠΎΡΠ½ΠΎΠ²Π°ΡΠ° Π½Π° ΡΠΈΡΠ΅ Unixes. ΠΡΠ΄Π΅ Π΄Π° Π²ΠΈΠ΄ΠΈΠΌΠ΅ ΠΊΠ°ΠΊΠΎ ΡΡΡΠ΅ΡΡΠΎΡ ΡΠ°Π±ΠΎΡΠΈ ΡΠΎ ΠΏΡΠΎΡΠ΅ΡΠ½ΠΎ Π΄ΡΠ²ΠΎ ΠΊΠΎΡΠΈΡΡΠ΅ΡΡΠΈ Π³ΠΎ ΠΏΡΠΈΠΌΠ΅ΡΠΎΡ Π½Π° Π΅Π΄Π½ΠΎΡΡΠ°Π²Π½ΠΎ βΡΠ°Π·ΠΌΠ½ΠΎΠΆΡΠ²Π°ΡΠ΅β
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;
}
ΠΡΠ΄Π΅ Π΄Π° Π³ΠΈ ΡΠ»Π΅Π΄ΠΈΠΌΠ΅ Π΄Π²Π°ΡΠ° ΠΏΠΎΠ²ΠΈΡΠΈ Π·Π° ΠΏΠΈΡΡΠ²Π°ΡΠ΅:
$ 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
Π²ΠΎ ΡΠΈΡΠ΅ ΠΏΠΎΠ²ΠΈΡΠΈ Π·Π° ΠΏΠΈΡΡΠ²Π°ΡΠ΅:
$ 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
, Π²ΠΊΠ»ΡΡΡΠ²Π°ΡΡΠΈ Π³ΠΎ ΠΈ ΠΏΠΎΠ²ΠΈΠΊΠΎΡ ΡΠΊΡΠΈΠ΅Π½ Π·Π°Π΄ ΡΠΆΠ°Ρ. ΠΠΌΠ° ΡΠΌΠΈΡΠ»Π° Π΄Π° ΡΠ΅ Π²ΡΠ°ΡΠΈ Π³ΡΠ΅ΡΠΊΠ° ΡΠ°ΠΌΠΎ Π·Π° ΠΏΡΠ²ΠΈΠΎΡ ΠΎΠ΄ ΠΏΠΎΠ²ΠΈΡΠΈΡΠ΅:
$ 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
ΠΈ ΡΠ»ΠΎΠ±ΠΎΠ΄Π½ΠΎ ΡΠΈΡΠ½Π΅ΡΠ΅ Π²ΠΎ Π²Π°ΡΠΈΡΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈ!
ΠΠ·Π²ΠΎΡ: www.habr.com