Π£ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ²Π½ΠΈΠΌ ΡΠΈΡΡΠ΅ΠΌΠΈΠΌΠ° ΡΠ»ΠΈΡΠ½ΠΈΠΌ Π£Π½ΠΈΠΊ-Ρ, ΠΊΠΎΠΌΡΠ½ΠΈΠΊΠ°ΡΠΈΡΠ° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ° ΡΠ° ΡΠΏΠΎΡΠ½ΠΈΠΌ ΡΠ²Π΅ΡΠΎΠΌ ΠΈ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ²Π½ΠΈΠΌ ΡΠΈΡΡΠ΅ΠΌΠΎΠΌ ΠΎΠ΄Π²ΠΈΡΠ° ΡΠ΅ ΠΊΡΠΎΠ· ΠΌΠ°Π»ΠΈ ΡΠΊΡΠΏ ΡΡΠ½ΠΊΡΠΈΡΠ° β ΡΠΈΡΡΠ΅ΠΌΡΠΊΠ΅ ΠΏΠΎΠ·ΠΈΠ²Π΅. Π’ΠΎ Π·Π½Π°ΡΠΈ Π΄Π° Ρ ΡΠ²ΡΡ Ρ ΠΎΡΠΊΠ»Π°ΡΠ°ΡΠ° Π³ΡΠ΅ΡΠ°ΠΊΠ° ΠΌΠΎΠΆΠ΅ Π±ΠΈΡΠΈ ΠΊΠΎΡΠΈΡΠ½ΠΎ ΡΠΏΠΈΡΡΠ½ΠΈΡΠ°ΡΠΈ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠ΅ ΠΏΠΎΠ·ΠΈΠ²Π΅ ΠΊΠΎΡΠ΅ ΠΈΠ·Π²ΡΡΠ°Π²Π°ΡΡ ΠΏΡΠΎΡΠ΅ΡΠΈ.
Π£ΡΠ»ΡΠΆΠ½ΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌ Π²Π°ΠΌ ΠΏΠΎΠΌΠ°ΠΆΠ΅ Π΄Π° Π½Π°Π΄Π³Π»Π΅Π΄Π°ΡΠ΅ βΠΈΠ½ΡΠΈΠΌΠ½ΠΈ ΠΆΠΈΠ²ΠΎΡβ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ° Π½Π° ΠΠΈΠ½ΡΠΊ-Ρ strace
, ΡΡΠΎ ΡΠ΅ ΡΠ΅ΠΌΠ° ΠΎΠ²ΠΎΠ³ ΡΠ»Π°Π½ΠΊΠ°. ΠΡΠΈΠΌΠ΅ΡΠ΅ ΡΠΏΠΎΡΡΠ΅Π±Π΅ ΡΠΏΠΈΡΡΠ½ΡΠΊΠ΅ ΠΎΠΏΡΠ΅ΠΌΠ΅ ΠΏΡΠ°ΡΠΈ ΠΊΡΠ°ΡΠ°ΠΊ ΠΈΡΡΠΎΡΠΈΡΠ°Ρ strace
ΠΈ ΠΎΠΏΠΈΡ Π΄ΠΈΠ·Π°ΡΠ½Π° ΡΠ°ΠΊΠ²ΠΈΡ
ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°.
Π‘Π°Π΄ΡΠΆΠΈΠ½Π°
ΠΠΎΡΠ΅ΠΊΠ»ΠΎ Π²ΡΡΡΠ΅ Π‘ΡΡΠ°ΡΠ΅ ΡΡΠ΅ΡΠ°Ρ ΡΠΊΡΠ°ΡΠΊΠΎ: ΠΡΠ°ΡΠ°Π΄ ΠΏΡΠ°ΡΠ°Π΄ ΠΡΠ½ΠΎΠ²Π΅: ΠΏΠΎΠΊΡΠ΅ΡΠ°ΡΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ° ΠΊΠΎΡΠΈ ΡΠ΅ ΠΏΠΎΠΊΡΠ΅ΡΠ΅ ΡΡΡΠ°ΡΠ΅ ΠΡΠ½ΠΎΠ²Π΅: ΠΏΡΠΈΠ΄ΡΡΠΆΠΈΠ²Π°ΡΠ΅ ΠΏΡΠΎΡΠ΅ΡΡ Ρ Ρ ΠΎΠ΄Ρ ΠΡΠΈΠΌΠ΅Ρ: ΠΡΠ°ΡΠ΅ΡΠ΅ Π΄Π΅ΡΡΠΈΡ ΠΏΡΠΎΡΠ΅ΡΠ° ΠΡΠΈΠΌΠ΅Ρ: ΠΏΡΡΠ°ΡΠ΅ Π΄Π°ΡΠΎΡΠ΅ΠΊΠ° ΡΠΌΠ΅ΡΡΠΎ ΡΡΡΠΈΡΠ° ΠΡΠΈΠΌΠ΅Ρ: ΠΡΠ°ΡΠ΅ΡΠ΅ ΠΏΡΠΈΡΡΡΠΏΠ° Π΄Π°ΡΠΎΡΠ΅ΠΊΠ°ΠΌΠ° ΠΡΠΈΠΌΠ΅Ρ: ΠΡΠΎΠ³ΡΠ°ΠΌΠΈ ΡΠ° Π²ΠΈΡΠ΅ Π½ΠΈΡΠΈ ΠΠ°ΡΡΠ΅Ρ ΠΊΠ»Π°ΡΠ°: ΠΏΡΠΎΡΠ΅ΡΠ½ΠΈ ΡΡΠΎΠ³ Ρ Π²ΡΠ΅ΠΌΠ΅ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΎΠ³ ΠΏΠΎΠ·ΠΈΠ²Π° ΠΠ°ΡΡΠ΅Ρ ΠΊΠ»Π°ΡΠ°: ΡΠ±ΡΠΈΠ·Π³Π°Π²Π°ΡΠ΅ Π³ΡΠ΅ΡΠΊΠ΅ ΠΡΡΠ΅ΡΠ²ΠΎΡΠ΄
ΠΠΎΡΠ΅ΠΊΠ»ΠΎ Π²ΡΡΡΠ΅
ΠΠ»Π°Π²Π½ΠΈ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΡΡ ΠΈΠ·ΠΌΠ΅ΡΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ° ΠΈ ΡΠ΅Π·Π³ΡΠ° ΠΠ‘ Ρ Π£Π½ΠΈΠΊ-Ρ ΡΡ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ²ΠΈ. ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ²ΠΈ, ΡΠΈΡΡΠ°Π»Π»Ρ), ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΡΠ° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ° ΡΠ° ΡΠΏΠΎΡΠ½ΠΈΠΌ ΡΠ²Π΅ΡΠΎΠΌ ΡΠ΅ ΠΎΠ΄Π²ΠΈΡΠ° ΠΈΡΠΊΡΡΡΠΈΠ²ΠΎ ΠΏΡΠ΅ΠΊΠΎ ΡΠΈΡ .
ΠΠ»ΠΈ Ρ ΠΏΡΠ²ΠΎΡ ΡΠ°Π²Π½ΠΎΡ Π²Π΅ΡΠ·ΠΈΡΠΈ Π£Π½ΠΈΠΊ-Π° (ptrace
.
ΠΏΡΡΠ°ΡΠ΅ ΡΠ΅ ΡΠ°Π·Π²ΠΈΡΠ΅Π½ ΠΏΡΠ²Π΅Π½ΡΡΠ²Π΅Π½ΠΎ Π·Π° ΠΈΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½Π΅ Π΄Π΅Π±Π°Π³Π΅ΡΠ΅, Π°Π»ΠΈ Π΄ΠΎ ΠΊΡΠ°ΡΠ° 80-ΠΈΡ
(Ρ Π΅ΡΠΈ ΠΊΠΎΠΌΠ΅ΡΡΠΈΡΠ°Π»Π½ΠΈΡ
trace
ΠΎΠ΄ Π‘ΡΠ½ΡΠ°. Π ΠΊΠ»ΠΎΠ½ ΠΈ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π» ΡΡ Π±ΠΈΠ»ΠΈ Π½Π°ΠΌΠ΅ΡΠ΅Π½ΠΈ Π·Π° Π‘ΡΠ½ΠΠ‘, Π°Π»ΠΈ Π΄ΠΎ 1994 strace
ΡΠ΅ ΠΏΡΠ΅Π½Π΅Ρ Π½Π° Π‘ΠΈΡΡΠ΅ΠΌ Π, Π‘ΠΎΠ»Π°ΡΠΈΡ ΠΈ ΡΠ²Π΅ ΠΏΠΎΠΏΡΠ»Π°ΡΠ½ΠΈΡΠΈ ΠΠΈΠ½ΡΠΊ.
ΠΠ°Π½Π°Ρ ΡΡΡΠ°ΡΠ΅ ΠΏΠΎΠ΄ΡΠΆΠ°Π²Π° ΡΠ°ΠΌΠΎ ΠΠΈΠ½ΡΠΊ ΠΈ ΠΎΡΠ»Π°ΡΠ° ΡΠ΅ Π½Π° ΠΈΡΡΠΈ ptrace
, ΠΎΠ±ΡΠ°ΡΡΠ°ΠΎ ΠΌΠ½ΠΎΠ³ΠΈΠΌ ΠΏΡΠΎΡΠΈΡΠ΅ΡΠΈΠΌΠ°.
ΠΠΎΠ΄Π΅ΡΠ°Π½ (ΠΈ Π²Π΅ΠΎΠΌΠ° Π°ΠΊΡΠΈΠ²Π°Π½) ΠΎΠ΄ΡΠΆΠ°Π²Π°Ρ strace
-
Π’Π°ΠΊΠΎΡΠ΅ ΡΠ΅ Π²Π°ΠΆΠ½ΠΎ Π΄Π° ΠΏΡΡΠ°ΡΠ΅ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ² ΠΈ ΡΡΠ°Π³Π°ΡΠΈ Π½ΠΈΠΊΠ°Π΄Π° Π½ΠΈΡΡ Π±ΠΈΠ»ΠΈ ΡΠΊΡΡΡΠ΅Π½ΠΈ Ρ ΠΠΠ‘ΠΠΠ‘, ΡΠΏΡΠΊΠΎΡ Π΄ΡΠ³ΠΎΡ ΠΈΡΡΠΎΡΠΈΡΠΈ ΠΈ ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½ΡΠ°ΡΠΈΡΠΈ Ρ ΠΠΈΠ½ΡΠΊ, Π€ΡΠ΅Π΅ΠΠ‘Π, ΠΠΏΠ΅Π½ΠΠ‘Π ΠΈ ΡΡΠ°Π΄ΠΈΡΠΈΠΎΠ½Π°Π»Π½ΠΎΠΌ Π£Π½ΠΈΠΊΡ.
Π‘ΡΡΠ°ΡΠ΅ ΡΡΠ΅ΡΠ°Ρ ΡΠΊΡΠ°ΡΠΊΠΎ: ΠΡΠ°ΡΠ°Π΄ ΠΏΡΠ°ΡΠ°Π΄
βΠΠ΅ ΠΎΡΠ΅ΠΊΡΡΠ΅ ΡΠ΅ Π΄Π° ΠΎΠ²ΠΎ ΡΠ°Π·ΡΠΌΠ΅ΡΠ΅β (ΠΠ΅Π½ΠΈΡ Π ΠΈΡΠΈ, ΠΊΠΎΠΌΠ΅Π½ΡΠ°Ρ Ρ Π²Π΅ΡΠ·ΠΈΡΠΈ 6 Π£Π½ΠΈΠΊ ΠΈΠ·Π²ΠΎΡΠ½ΠΎΠ³ ΠΊΠΎΠ΄Π°)
ΠΠ΄ ΡΠ°Π½ΠΎΠ³ Π΄Π΅ΡΠΈΡΡΡΠ²Π° Π½Π΅ ΠΏΠΎΠ΄Π½ΠΎΡΠΈΠΌ ΡΡΠ½Π΅ ΠΊΡΡΠΈΡΠ΅: Π½ΠΈΡΠ°ΠΌ ΡΠ΅ ΠΈΠ³ΡΠ°ΠΎ ΡΠ° ΠΈΠ³ΡΠ°ΡΠΊΠ°ΠΌΠ°, Π²Π΅Ρ ΡΠ°ΠΌ ΠΏΠΎΠΊΡΡΠ°Π²Π°ΠΎ Π΄Π° ΡΠ°Π·ΡΠΌΠ΅ΠΌ ΡΠΈΡ ΠΎΠ²Ρ ΡΡΡΡΠΊΡΡΡΡ (ΠΎΠ΄ΡΠ°ΡΠ»ΠΈ ΡΡ ΠΊΠΎΡΠΈΡΡΠΈΠ»ΠΈ ΡΠ΅Ρ βΡΠ°Π·Π±ΠΈΡΠ΅Π½β, Π°Π»ΠΈ Π½Π΅ Π²Π΅ΡΡΡΡΠ΅ Π·Π»ΠΈΠΌ ΡΠ΅Π·ΠΈΡΠΈΠΌΠ°). ΠΠΎΠΆΠ΄Π° ΡΠ΅ ΡΠΎ ΡΠ°Π·Π»ΠΎΠ³ Π·Π°ΡΡΠΎ ΡΡ ΠΌΠΈ Π½Π΅ΡΠΎΡΠΌΠ°Π»Π½Π° ΠΊΡΠ»ΡΡΡΠ° ΠΏΡΠ²ΠΎΠ³ Π£Π½ΠΈΠΊΠ° ΠΈ ΠΌΠΎΠ΄Π΅ΡΠ½ΠΎΠ³ ΠΏΠΎΠΊΡΠ΅ΡΠ° ΠΎΡΠ²ΠΎΡΠ΅Π½ΠΎΠ³ ΠΊΠΎΠ΄Π° ΡΠ°ΠΊΠΎ Π±Π»ΠΈΡΠΊΠΈ.
ΠΠ° ΠΏΠΎΡΡΠ΅Π±Π΅ ΠΎΠ²ΠΎΠ³ ΡΠ»Π°Π½ΠΊΠ°, Π½Π΅ΡΠ°Π·ΡΠΌΠ½ΠΎ ΡΠ΅ ΡΠ°ΡΡΠ°Π²ΡΠ°ΡΠΈ ΠΈΠ·Π²ΠΎΡΠ½ΠΈ ΠΊΠΎΠ΄ ΡΡΡΠ°ΡΠ΅-Π°, ΠΊΠΎΡΠΈ ΡΠ΅ ΡΠ°ΡΡΠ°ΠΎ Π΄Π΅ΡΠ΅Π½ΠΈΡΠ°ΠΌΠ°. ΠΠ»ΠΈ Π·Π° ΡΠΈΡΠ°ΠΎΡΠ΅ Π½Π΅ Π±ΠΈ ΡΡΠ΅Π±Π°Π»ΠΎ Π΄Π° ΠΎΡΡΠ°Π½Π΅ Π½ΠΈΠΊΠ°ΠΊΠ²ΠΈΡ
ΡΠ°ΡΠ½ΠΈ. Π‘ΡΠΎΠ³Π°, Π΄Π° Π±ΠΈΡ
ΠΏΠΎΠΊΠ°Π·Π°ΠΎ ΠΏΡΠΈΠ½ΡΠΈΠΏ ΡΠ°Π΄Π° ΡΠ°ΠΊΠ²ΠΈΡ
ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ° Π·Π° ΠΏΡΠ°ΡΠ΅ΡΠ΅, ΠΏΡΡΠΆΠΈΡΡ ΠΊΠΎΠ΄ Π·Π° ΠΌΠΈΠ½ΠΈΡΠ°ΡΡΡΠ½ΠΈ ΡΡΠ°Π³Π°Ρ -
$ 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
ΠΠΈΠ³Π»Π΅Ρ Π’ΡΠ°ΡΠ΅ ΠΏΡΠ΅ΠΏΠΎΠ·Π½Π°ΡΠ΅ ΡΡΠΎΡΠΈΠ½Π΅ ΠΠΈΠ½ΡΠΊ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈΡ
ΠΏΠΎΠ·ΠΈΠ²Π° (Π²ΠΈΠ΄ΠΈ.
Π₯Π°ΡΠ΄Π΅ Π΄Π° ΠΏΠΎΠ³Π»Π΅Π΄Π°ΠΌΠΎ ΡΠ°Π΄ Π½Π°ΡΠ΅Π³ ΠΊΠ»ΠΎΠ½Π°. Π£ ΡΠ»ΡΡΠ°ΡΡ ΠΠΈΠ½ΡΠΊΠ°, ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈ Π·Π° ΠΎΡΠΊΠ»Π°ΡΠ°ΡΠ΅ Π³ΡΠ΅ΡΠ°ΠΊΠ° ΠΈ ΡΡΠ°Π³Π°ΡΠΈ ΠΊΠΎΡΠΈΡΡΠ΅, ΠΊΠ°ΠΎ ΡΡΠΎ ΡΠ΅ Π³ΠΎΡΠ΅ ΠΏΠΎΠΌΠ΅Π½ΡΡΠΎ, ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ² ΠΏΡΡΠ°ΡΠ΅. Π€ΡΠ½ΠΊΡΠΈΠΎΠ½ΠΈΡΠ΅ ΡΠ°ΠΊΠΎ ΡΡΠΎ ΡΠ΅ Ρ ΠΏΡΠ²ΠΎΠΌ Π°ΡΠ³ΡΠΌΠ΅Π½ΡΡ ΠΏΡΠΎΡΠ»Π΅ΡΡΡΡ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠΈ ΠΊΠΎΠΌΠ°Π½Π΄ΠΈ, ΠΎΠ΄ ΠΊΠΎΡΠΈΡ
ΡΡ Π½Π°ΠΌ ΡΠ°ΠΌΠΎ ΠΏΠΎΡΡΠ΅Π±Π½ΠΈ PTRACE_TRACEME
, PTRACE_SYSCALL
ΠΈ PTRACE_GETREGS
.
Π’ΡΠ°ΡΠ΅Ρ ΠΏΠΎΡΠΈΡΠ΅ Ρ ΡΠΎΠ±ΠΈΡΠ°ΡΠ΅Π½ΠΎΠΌ Π£Π½ΠΈΠΊ ΡΡΠΈΠ»Ρ: 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
ΠΈ ΠΎΠ±ΠΈΡΠ΅ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈΡ
ΠΏΠΎΠ·ΠΈΠ²Π° ΠΊΠΎΡΠΈ Π½Π°Ρ Π½Π΅ Π·Π°Π½ΠΈΠΌΠ°ΡΡ.
ΠΠΎΠΆΠ΅ΡΠ΅ Π΄Π° ΠΎΠ΄Π²ΠΎΡΠΈΡΠ΅ ΡΡΠ°Π½Π΄Π°ΡΠ΄Π½ΠΈ ΠΈΠ·Π»Π°Π·Π½ΠΈ ΡΠΎΠΊ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ° ΠΈ ΠΈΠ·Π»Π°Π· Π³ΡΠ΅ΡΠΊΠ΅ ΡΡΡΠ°ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠ΅ΡΠΈ -ΠΎ ΠΏΡΠ΅ΠΊΠΈΠ΄Π°Ρ, ΠΊΠΎΡΠΈ ΠΏΡΠ΅ΡΡΠΌΠ΅ΡΠ°Π²Π° Π»ΠΈΡΡΡ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈΡ ΠΏΠΎΠ·ΠΈΠ²Π° Ρ Π΄Π°ΡΠΎΡΠ΅ΠΊΡ ΡΠ° Π°ΡΠ³ΡΠΌΠ΅Π½ΡΠΈΠΌΠ°.
ΠΡΡΠ°ΡΠ΅ Π΄Π° ΡΠ΅ ΠΏΠΎΠ·Π°Π±Π°Π²ΠΈΠΌΠΎ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠΎΠΌ βΠ΄ΠΎΠ΄Π°ΡΠ½ΠΈΡ
β ΠΏΠΎΠ·ΠΈΠ²Π°. ΠΡΠ΅ΡΠΏΠΎΡΡΠ°Π²ΠΈΠΌΠΎ Π΄Π° Π½Π°Ρ Π·Π°Π½ΠΈΠΌΠ°ΡΡ ΡΠ°ΠΌΠΎ ΠΏΠΎΠ·ΠΈΠ²ΠΈ 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 +++
ΠΠ±ΡΠ°ΡΠΈΡΠ΅ ΠΏΠ°ΠΆΡΡ Π½Π° Π½Π΅ΠΈΡΠΊΡΡΡΠ΅Π½ΠΈ ΡΠ·Π²ΠΈΡΠ½ΠΈΠΊ Π½Π° Π»ΠΈΡΡΠΈ ΠΈΡΠΊΡΡΡΠ΅Π½ΠΈΡ ΠΏΠΎΠ·ΠΈΠ²Π°: ΠΎΠ²ΠΎ Π·Π°Ρ ΡΠ΅Π²Π° ΠΊΠΎΠΌΠ°Π½Π΄Π½Π° ΡΠΊΠΎΡΠΊΠ°. Π³ΡΠ°Π½Π°ΡΠ°).
Π£ ΠΌΠΎΡΠΎΡ Π²Π΅ΡΠ·ΠΈΡΠΈ Π³Π»ΠΈΠ±Ρ-Π°, ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ² ΠΏΡΠ΅ΠΊΠΈΠ΄Π° ΠΏΡΠΎΡΠ΅Ρ exit_group
, Π½Π΅ ΡΡΠ°Π΄ΠΈΡΠΈΠΎΠ½Π°Π»Π½ΠΎ _exit
. ΠΠ²ΠΎ ΡΠ΅ ΡΠ΅ΡΠΊΠΎΡΠ° ΡΠ°Π΄Π° ΡΠ° ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈΠΌ ΠΏΠΎΠ·ΠΈΠ²ΠΈΠΌΠ°: ΠΈΠ½ΡΠ΅ΡΡΠ΅ΡΡ ΡΠ° ΠΊΠΎΡΠΈΠΌ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ΅Ρ ΡΠ°Π΄ΠΈ Π½ΠΈΡΠ΅ Π΄ΠΈΡΠ΅ΠΊΡΠ½ΠΎ ΠΏΠΎΠ²Π΅Π·Π°Π½ ΡΠ° ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈΠΌ ΠΏΠΎΠ·ΠΈΠ²ΠΈΠΌΠ°. Π¨ΡΠ°Π²ΠΈΡΠ΅, ΡΠ΅Π΄ΠΎΠ²Π½ΠΎ ΡΠ΅ ΠΌΠ΅ΡΠ° Ρ Π·Π°Π²ΠΈΡΠ½ΠΎΡΡΠΈ ΠΎΠ΄ ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½ΡΠ°ΡΠΈΡΠ΅ ΠΈ ΠΏΠ»Π°ΡΡΠΎΡΠΌΠ΅.
ΠΡΠ½ΠΎΠ²Π΅: ΠΏΡΠΈΠ΄ΡΡΠΆΠΈΠ²Π°ΡΠ΅ ΠΏΡΠΎΡΠ΅ΡΡ Ρ Ρ ΠΎΠ΄Ρ
Π£ ΠΏΠΎΡΠ΅ΡΠΊΡ, ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ² ΠΏΡΡΠ°ΡΠ΅ Π½Π° ΠΊΠΎΡΠ΅ΠΌ ΡΠ΅ ΠΈΠ·Π³ΡΠ°ΡΠ΅Π½ strace
, ΠΌΠΎΠ³Π°ΠΎ ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠΈΡΠΈ ΡΠ°ΠΌΠΎ ΠΊΠ°Π΄Π° ΡΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌ ΠΏΠΎΠΊΡΠ΅ΡΠ΅ Ρ ΠΏΠΎΡΠ΅Π±Π½ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΡ. ΠΠ²ΠΎ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅ΡΠ΅ ΡΠ΅ ΠΌΠΎΠΆΠ΄Π° Π·Π²ΡΡΠ°Π»ΠΎ ΡΠ°Π·ΡΠΌΠ½ΠΎ Ρ Π΄Π°Π½ΠΈΠΌΠ° Π²Π΅ΡΠ·ΠΈΡΠ΅ 6 Π£Π½ΠΈΠΊ-Π°. ΠΠ°Π½Π°Ρ ΡΠΎ Π²ΠΈΡΠ΅ Π½ΠΈΡΠ΅ Π΄ΠΎΠ²ΠΎΡΠ½ΠΎ: ΠΏΠΎΠ½Π΅ΠΊΠ°Π΄ ΠΌΠΎΡΠ°ΡΠ΅ ΠΈΡΡΡΠ°ΠΆΠΈΡΠΈ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ΅ ΡΠ°Π΄Π½ΠΎΠ³ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ°. Π’ΠΈΠΏΠΈΡΠ°Π½ ΠΏΡΠΈΠΌΠ΅Ρ ΡΠ΅ ΠΏΡΠΎΡΠ΅Ρ Π±Π»ΠΎΠΊΠΈΡΠ°Π½ Π½Π° ΡΡΡΠΊΠΈ ΠΈΠ»ΠΈ ΡΠΏΠ°Π²Π°ΡΠ΅. Π‘ΡΠΎΠ³Π° ΠΌΠΎΠ΄Π΅ΡΠ½ΠΎ 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
. ΠΠ²Π΅ ΡΡΠ²Π°ΡΠΈ ΡΡ ΠΏΠΎΡΡΠ°Π»Π΅ ΡΠ°ΡΠ½Π΅: ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ² ΠΏΠ°ΡΠ·Π΅ ΠΈΠ³Π½ΠΎΡΠΈΡΠ΅ ΡΠΈΠ³Π½Π°Π»Π΅ Π±Π΅Π· ΡΡΠΊΠΎΠ²Π°ΠΎΡΠ° ΠΈ, ΡΡΠΎ ΡΠ΅ ΡΠΎΡ Π·Π°Π½ΠΈΠΌΡΠΈΠ²ΠΈΡΠ΅, ΡΡΡΠ°ΡΠ΅ ΠΏΡΠ°ΡΠΈ Π½Π΅ ΡΠ°ΠΌΠΎ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠ΅ ΠΏΠΎΠ·ΠΈΠ²Π΅, Π²Π΅Ρ ΠΈ Π΄ΠΎΠ»Π°Π·Π½Π΅ ΡΠΈΠ³Π½Π°Π»Π΅.
ΠΡΠΈΠΌΠ΅Ρ: ΠΡΠ°ΡΠ΅ΡΠ΅ Π΄Π΅ΡΡΠΈΡ ΠΏΡΠΎΡΠ΅ΡΠ°
Π Π°Π΄ ΡΠ° ΠΏΡΠΎΡΠ΅ΡΠΈΠΌΠ° ΠΏΡΠ΅ΠΊΠΎ ΠΏΠΎΠ·ΠΈΠ²Π° fork
- ΠΎΡΠ½ΠΎΠ²Π° ΡΠ²ΠΈΡ
Π£Π½ΠΈΠΊ-ΠΎΠ²Π°. Π₯Π°ΡΠ΄Π΅ Π΄Π° Π²ΠΈΠ΄ΠΈΠΌΠΎ ΠΊΠ°ΠΊΠΎ ΡΡΡΠ°ΡΠ΅ ΡΠ°Π΄ΠΈ ΡΠ° ΠΏΡΠΎΡΠ΅ΡΠ½ΠΈΠΌ ΡΡΠ°Π±Π»ΠΎΠΌ ΠΊΠΎΡΠΈΡΡΠ΅ΡΠΈ ΠΏΡΠΈΠΌΠ΅Ρ ΡΠ΅Π΄Π½ΠΎΡΡΠ°Π²Π½ΠΎΠ³ βΡΠ·Π³ΠΎΡΠ°β
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 +++
Π£Π·Π³ΡΠ΅Π΄, ΠΊΠΎΡΠΈ ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ² ΡΠ΅ ΠΊΠΎΡΠΈΡΡΠΈ Π·Π° ΠΊΡΠ΅ΠΈΡΠ°ΡΠ΅ Π½ΠΎΠ²ΠΎΠ³ ΠΏΡΠΎΡΠ΅ΡΠ°?
ΠΡΠΈΠΌΠ΅Ρ: ΠΏΡΡΠ°ΡΠ΅ Π΄Π°ΡΠΎΡΠ΅ΠΊΠ° ΡΠΌΠ΅ΡΡΠΎ ΡΡΡΠΈΡΠ°
ΠΠΎΠ·Π½Π°Π²Π°ΡΠ΅ Π΄Π΅ΡΠΊΡΠΈΠΏΡΠΎΡΠ° Π΄Π°ΡΠΎΡΠ΅ΠΊΠ° ΡΠ΅ ΡΠ²Π°ΠΊΠ°ΠΊΠΎ ΠΊΠΎΡΠΈΡΠ½ΠΎ, Π°Π»ΠΈ ΠΈΠΌΠ΅Π½Π° ΠΎΠ΄ΡΠ΅ΡΠ΅Π½ΠΈΡ Π΄Π°ΡΠΎΡΠ΅ΠΊΠ° ΠΊΠΎΡΠΈΠΌΠ° ΠΏΡΠΎΠ³ΡΠ°ΠΌ ΠΏΡΠΈΡΡΡΠΏΠ° ΡΠ°ΠΊΠΎΡΠ΅ ΠΌΠΎΠ³Ρ Π±ΠΈΡΠΈ ΠΎΠ΄ ΠΊΠΎΡΠΈΡΡΠΈ.
naredni
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 -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);
}
ΠΠ°ΡΠ°Π²Π½ΠΎ, ΠΌΠΎΡΠ° ΡΠ΅ ΡΠ°ΡΡΠ°Π²ΠΈΡΠΈ ΡΠ° ΠΏΠΎΡΠ΅Π±Π½ΠΈΠΌ ΠΏΠΎΠ·Π΄ΡΠ°Π²ΠΎΠΌ Π·Π° Π»ΠΈΠ½ΠΊΠ΅Ρ - Π·Π°ΡΡΠ°Π²ΠΈΡΠΎΠΌ -ΠΏΡΡ ΡΠ΅Π°Π΄:
$ 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
, ΠΊΠ°ΠΎ ΠΈΡ ΡΠ»ΡΡΠ°ΡΡ ΡΠ΅Π΄ΠΎΠ²Π½ΠΈΡ
ΠΏΡΠΎΡΠ΅ΡΠ°, Π΄ΠΎΠ΄Π°ΡΠ΅ ΠΏΠΈΠ΄ ΠΏΡΠΎΡΠ΅ΡΠ° Π½Π° ΠΏΠΎΡΠ΅ΡΠ°ΠΊ ΡΠ²Π°ΠΊΠΎΠ³ ΡΠ΅Π΄Π°.
ΠΠ°ΡΠ°Π²Π½ΠΎ, Π½Π΅ Π³ΠΎΠ²ΠΎΡΠΈΠΌΠΎ ΠΎ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΡ Π½ΠΈΡΠΈ Ρ ΡΠΌΠΈΡΠ»Ρ ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½ΡΠ°ΡΠΈΡΠ΅ ΡΡΠ°Π½Π΄Π°ΡΠ΄Π° ΠΠΠ‘ΠΠΠ‘ Π’Ρ ΡΠ΅Π°Π΄Ρ, Π²Π΅Ρ ΠΎ Π±ΡΠΎΡΡ ΠΊΠΎΡΠΈ ΠΊΠΎΡΠΈΡΡΠΈ ΠΏΠ»Π°Π½Π΅Ρ Π·Π°Π΄Π°ΡΠ°ΠΊΠ° Ρ ΠΠΈΠ½ΡΠΊΡ. Π‘Π° ΡΠ°ΡΠΊΠ΅ Π³Π»Π΅Π΄ΠΈΡΡΠ° ΠΏΠΎΡΠ»Π΅Π΄ΡΠ΅Π³, Π½Π΅ ΠΏΠΎΡΡΠΎΡΠ΅ ΠΏΡΠΎΡΠ΅ΡΠΈ ΠΈΠ»ΠΈ Π½ΠΈΡΠΈ - ΠΏΠΎΡΡΠΎΡΠ΅ Π·Π°Π΄Π°ΡΠΈ ΠΊΠΎΡΠΈ ΡΡΠ΅Π±Π° Π΄Π° ΡΠ΅ ΡΠ°ΡΠΏΠΎΡΠ΅Π΄Π΅ ΠΈΠ·ΠΌΠ΅ΡΡ Π΄ΠΎΡΡΡΠΏΠ½ΠΈΡ ΡΠ΅Π·Π³Π°ΡΠ° ΠΌΠ°ΡΠΈΠ½Π΅.
ΠΠ°Π΄Π° ΡΠ°Π΄ΠΈΡΠ΅ Ρ Π²ΠΈΡΠ΅ Π½ΠΈΡΠΈ, ΡΠΈΡΡΠ΅ΠΌΡΠΊΠΈ ΠΏΠΎΠ·ΠΈΠ²ΠΈ ΠΏΠΎΡΡΠ°ΡΡ ΠΏΡΠ΅Π²ΠΈΡΠ΅:
$ 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
- ΠΏΡΠ²Π° Π»ΠΈΠ½ΠΈΡΠ° ΠΎΠ΄Π±ΡΠ°Π½Π΅ Ρ ΡΠ»ΡΡΠ°ΡΡ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° ΡΠ° ΡΠΎΠΏΡΡΠ²Π΅Π½ΠΈΠΌ ΠΈ ΡΡΡΠΈΠΌ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΈΠΌΠ° ΠΈ ΠΊΠΎΡΠΈΡΡΠΈΠΌ ΡΠ΅ Π±Π°Ρ ΠΏΠ°Ρ ΠΏΡΡΠ° Π½Π΅Π΄Π΅ΡΠ½ΠΎ.
Π£ΠΊΡΠ°ΡΠΊΠΎ, Π°ΠΊΠΎ Π²ΠΎΠ»ΠΈΡΠ΅ Π£Π½ΠΈΠΊ, ΡΠΈΡΠ°ΡΡΠ΅ man 1 strace
ΠΈ ΡΠ»ΠΎΠ±ΠΎΠ΄Π½ΠΎ Π·Π°Π²ΠΈΡΠΈΡΠ΅ Ρ ΡΠ²ΠΎΡΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠ΅!
ΠΠ·Π²ΠΎΡ: Π²Π²Π².Ρ Π°Π±Ρ.ΡΠΎΠΌ