Strace Π² Linux: історія, пристрій Ρ‚Π° використання

Strace Π² Linux: історія, пристрій Ρ‚Π° використання

Π£ Unix-ΠΏΠΎΠ΄Ρ–Π±Π½ΠΈΡ… ΠΎΠΏΠ΅Ρ€Π°Ρ†Ρ–ΠΉΠ½ΠΈΡ… систСмах спілкування ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ Ρ–Π· Π·ΠΎΠ²Π½Ρ–ΡˆΠ½Ρ–ΠΌ світом Ρ‚Π° ΠΎΠΏΠ΅Ρ€Π°Ρ†Ρ–ΠΉΠ½ΠΎΡŽ ΡΠΈΡΡ‚Π΅ΠΌΠΎΡŽ Π²Ρ–Π΄Π±ΡƒΠ²Π°Ρ”Ρ‚ΡŒΡΡ Ρ‡Π΅Ρ€Π΅Π· Π½Π΅Π²Π΅Π»ΠΈΠΊΠΈΠΉ Π½Π°Π±Ρ–Ρ€ Ρ„ΡƒΠ½ΠΊΡ†Ρ–ΠΉ – систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π². А Π·Π½Π°Ρ‡ΠΈΡ‚ΡŒ, Ρƒ Π½Π°Π»Π°Π³ΠΎΠ΄ΠΆΡƒΠ²Π°Π»ΡŒΠ½ΠΈΡ… цілях корисно Π±ΡƒΠ²Π°Ρ” піддивитися Π·Π° систСмними Π²ΠΈΠΊΠ»ΠΈΠΊΠ°ΠΌΠΈ, Ρ‰ΠΎ Π²ΠΈΠΊΠΎΠ½ΡƒΡŽΡ‚ΡŒΡΡ процСсами.

Π‘Π»Ρ–Π΄ΠΊΡƒΠ²Π°Ρ‚ΠΈ Π·Π° Β«Ρ–Π½Ρ‚ΠΈΠΌΠ½ΠΈΠΌ Титтям» ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌ Π½Π° Linux Π΄ΠΎΠΏΠΎΠΌΠ°Π³Π°Ρ” ΡƒΡ‚ΠΈΠ»Ρ–Ρ‚Π° strace, якій Ρ– присвячСно Ρ†ΡŽ ΡΡ‚Π°Ρ‚Ρ‚ΡŽ. Π”ΠΎ ΠΏΡ€ΠΈΠΊΠ»Π°Π΄Ρ–Π² використання Β«ΡˆΠΏΠΈΠ³ΡƒΠ½ΡΡŒΠΊΠΎΠ³ΠΎΒ» обладнання Π΄ΠΎΠ΄Π°ΡŽΡ‚ΡŒΡΡ ΠΊΠΎΡ€ΠΎΡ‚ΠΊΠ° історія strace Ρ‚Π° опис ΠΏΡ€ΠΈΡΡ‚Ρ€ΠΎΡŽ ΠΏΠΎΠ΄Ρ–Π±Π½ΠΈΡ… ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌ.

Зміст

походТСння Π²ΠΈΠ΄Ρ–Π²

Π“ΠΎΠ»ΠΎΠ²Π½ΠΈΠΉ інтСрфСйс ΠΌΡ–ΠΆ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ°ΠΌΠΈ Ρ‚Π° ядром OC Π² Unix – систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ (Π°Π½Π³Π». систСмні Π΄Π·Π²Ρ–Π½ΠΊΠΈ, систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ), взаємодія ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌ Ρ–Π· Π·ΠΎΠ²Π½Ρ–ΡˆΠ½Ρ–ΠΌ світом Π²Ρ–Π΄Π±ΡƒΠ²Π°Ρ”Ρ‚ΡŒΡΡ Π²ΠΈΠΊΠ»ΡŽΡ‡Π½ΠΎ Ρ‡Π΅Ρ€Π΅Π· Π½ΠΈΡ….

АлС Π² ΠΏΠ΅Ρ€ΡˆΡ–ΠΉ ΠΏΡƒΠ±Π»Ρ–Ρ‡Π½Ρ–ΠΉ вСрсії Unix (ВСрсія 6 Unix, 1975 Ρ€Ρ–ΠΊ) Π·Ρ€ΡƒΡ‡Π½ΠΈΡ… способів відстСТСння ΠΏΠΎΠ²Π΅Π΄Ρ–Π½ΠΊΠΈ ΠΊΠΎΡ€ΠΈΡΡ‚ΡƒΠ²Π°Π»ΡŒΠ½ΠΈΡ†ΡŒΠΊΠΈΡ… процСсів Π±ΡƒΠ². Для Π²ΠΈΡ€Ρ–ΡˆΠ΅Π½Π½Ρ Ρ†Ρ–Ρ”Ρ— ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ Bell Labs Π΄ΠΎ наступної вСрсії (ВСрсія 7 Unix, 1979 Ρ€Ρ–ΠΊ) Π·Π°ΠΏΡ€ΠΎΠΏΠΎΠ½ΡƒΠ²Π°Π»ΠΈ Π½ΠΎΠ²ΠΈΠΉ систСмний Π²ΠΈΠΊΠ»ΠΈΠΊ - ptrace.

Розроблявся ptrace насампСрСд для Ρ–Π½Ρ‚Π΅Ρ€Π°ΠΊΡ‚ΠΈΠ²Π½ΠΈΡ… Π½Π°Π»Π°Π³ΠΎΠ΄ΠΆΡƒΠ²Π°Ρ‡Ρ–Π², Π°Π»Π΅ Π΄ΠΎ кінця 80-Ρ… (Π² Π΅ΠΏΠΎΡ…Ρƒ ΠΊΠΎΠΌΠ΅Ρ€Ρ†Ρ–ΠΉΠ½ΠΎΠ³ΠΎ Π²ΠΆΠ΅ System V Release 4) Π½Π° Ρ†Ρ–ΠΉ основі Π·'явилися Ρ– Π½Π°Π±ΡƒΠ»ΠΈ Π½Π°ΠΉΡˆΠΈΡ€ΡˆΠΎΠ³ΠΎ ΠΏΠΎΡˆΠΈΡ€Π΅Π½Π½Ρ Π²ΡƒΠ·ΡŒΠΊΠΎΠ½Π°ΠΏΡ€Π°Π²Π»Π΅Π½Ρ– Π²Ρ–Π΄Π»Π°Π΄Π½ΠΈΠΊΠΈ - Ρ‚Ρ€Π°ΡΡƒΠ²Π°Π»ΡŒΠ½ΠΈΠΊΠΈ систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π².

ΠΏΠ΅Ρ€ΡˆΠ° ΠΆ вСрсія strace Π±ΡƒΠ»Π° ΠΎΠΏΡƒΠ±Π»Ρ–ΠΊΠΎΠ²Π°Π½Π° Полом ΠšΡ€ΠΎΠ½Π΅Π½Π±ΡƒΡ€Π³ΠΎΠΌ Ρƒ розсилці comp.sources.sun Π² 1992 Ρ€ΠΎΡ†Ρ– як Π°Π»ΡŒΡ‚Π΅Ρ€Π½Π°Ρ‚ΠΈΠ²Π° Π·Π°ΠΊΡ€ΠΈΡ‚Ρ–ΠΉ ΡƒΡ‚ΠΈΠ»Ρ–Ρ‚Ρ– trace Π²Ρ–Π΄ Sun. Π―ΠΊ ΠΊΠ»ΠΎΠ½, Ρ– ΠΎΡ€ΠΈΠ³Ρ–Π½Π°Π» призначалися для SunOS, Π°Π»Π΅ Π΄ΠΎ 1994 Ρ€ΠΎΠΊΡƒ strace Π±ΡƒΠ»Π° ΠΏΠΎΡ€Ρ‚ΠΎΠ²Π°Π½Π° Π½Π° System V, Solaris Ρ– Linux, Ρ‰ΠΎ Π½Π°Π±ΠΈΡ€Π°Ρ” популярності.

Π‘ΡŒΠΎΠ³ΠΎΠ΄Π½Ρ– strace ΠΏΡ–Π΄Ρ‚Ρ€ΠΈΠΌΡƒΡ” Ρ‚Ρ–Π»ΡŒΠΊΠΈ Linux Ρ– ΡΠΏΠΈΡ€Π°Ρ”Ρ‚ΡŒΡΡ Π½Π° всС Ρ‚ΠΎΠΉ ΠΆΠ΅ ptrace, Ρ‰ΠΎ оброс Π±Π΅Π·Π»Ρ–Ρ‡Ρ‡ΡŽ Ρ€ΠΎΠ·ΡˆΠΈΡ€Π΅Π½ΡŒ.

Бучасний (Ρ– Π΄ΡƒΠΆΠ΅ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΈΠΉ) ΠΌΠ΅ΠΉΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ strace - Π”ΠΌΠΈΡ‚Ρ€ΠΎ Π›Π΅Π²Ρ–Π½. Завдяки ΠΉΠΎΠΌΡƒ ΡƒΡ‚ΠΈΠ»Ρ–Ρ‚Π° ΠΎΡ‚Ρ€ΠΈΠΌΠ°Π»Π° просунуті моТливості Π½Π° ΠΊΡˆΡ‚Π°Π»Ρ‚ Ρ–Π½'Ρ”ΠΊΡ†Ρ–Ρ— ΠΏΠΎΠΌΠΈΠ»ΠΎΠΊ Ρƒ систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ, ΠΏΡ–Π΄Ρ‚Ρ€ΠΈΠΌΠΊΠΎΡŽ ΡˆΠΈΡ€ΠΎΠΊΠΎΠ³ΠΎ спСктра Π°Ρ€Ρ…Ρ–Ρ‚Π΅ΠΊΡ‚ΡƒΡ€ Ρ–, Π³ΠΎΠ»ΠΎΠ²Π½Π΅, маскотом. НСофіційні Π΄ΠΆΠ΅Ρ€Π΅Π»Π° ΡΡ‚Π²Π΅Ρ€Π΄ΠΆΡƒΡŽΡ‚ΡŒ, Ρ‰ΠΎ Π²ΠΈΠ±Ρ–Ρ€ Π»Ρ–Π³ Π½Π° страуса Ρ‡Π΅Ρ€Π΅Π· ΡΠΏΡ–Π²Π·Π²ΡƒΡ‡Π½Ρ–ΡΡ‚ΡŒ Ρ€ΠΎΡΡ–ΠΉΡΡŒΠΊΠΎΠ³ΠΎ слова «страус» Ρ‚Π° Π°Π½Π³Π»Ρ–ΠΉΡΡŒΠΊΠΎΠ³ΠΎ "strace".

Π’Π°ΠΆΠ»ΠΈΠ²ΠΎ ΠΉ Ρ‚Π΅, Ρ‰ΠΎ систСмний Π²ΠΈΠΊΠ»ΠΈΠΊ ptrace Ρ– Ρ‚Ρ€Π°ΡΡƒΠ²Π°Π»ΡŒΠ½ΠΈΠΊΠΈ Ρ‚Π°ΠΊ Ρ– Π½Π΅ Π±ΡƒΠ»ΠΈ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Ρ– Π΄ΠΎ POSIX, Π½Π΅Π·Π²Π°ΠΆΠ°ΡŽΡ‡ΠΈ Π½Π° Π΄ΠΎΠ²Π³Ρƒ Ρ–ΡΡ‚ΠΎΡ€Ρ–ΡŽ Ρ‚Π° Π½Π°ΡΠ²Π½Ρ–ΡΡ‚ΡŒ Ρ€Π΅Π°Π»Ρ–Π·Π°Ρ†Ρ–Ρ— Π² Linux, FreeBSD, OpenBSD Ρ‚Π° Ρ‚Ρ€Π°Π΄ΠΈΡ†Ρ–ΠΉΠ½ΠΈΡ… Unix.

ΠŸΡ€ΠΈΡΡ‚Ρ€Ρ–ΠΉ strace Ρƒ Π΄Π²ΠΎΡ… словах: Piglet Trace

"You are not expected to understand this" (ДСніс Π Ρ–Ρ‡Ρ–, ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€ Ρƒ Π²ΠΈΡ…Ρ–Π΄Π½ΠΎΠΌΡƒ ΠΊΠΎΠ΄Ρ– Version 6 Unix)

Π— Ρ€Π°Π½Π½ΡŒΠΎΠ³ΠΎ дитинства я Ρ‚Π΅Ρ€ΠΏΡ–Ρ‚ΠΈ Π½Π΅ ΠΌΠΎΠΆΡƒ Ρ‡ΠΎΡ€Π½Ρ– ящики: Π· Ρ–Π³Ρ€Π°ΡˆΠΊΠ°ΠΌΠΈ я Π½Π΅ Π³Ρ€Π°Π², Π° намагався розібратися Π² Ρ—Ρ…Π½ΡŒΠΎΠΌΡƒ пристрої (дорослі Π²ΠΆΠΈΠ²Π°Π»ΠΈ слово Β«Π»Π°ΠΌΠ°Π²Β», Π°Π»Π΅ Π½Π΅ Π²Ρ–Ρ€Ρ‚Π΅ Π·Π»ΠΈΠΌ ΠΌΠΎΠ²Π°ΠΌ). МоТливо, Ρ‚ΠΎΠΌΡƒ ΠΌΠ΅Π½Ρ– Ρ‚Π°ΠΊΡ– Π±Π»ΠΈΠ·ΡŒΠΊΡ– Π½Π΅Ρ„ΠΎΡ€ΠΌΠ°Π»ΡŒΠ½Π° ΠΊΡƒΠ»ΡŒΡ‚ΡƒΡ€Π° ΠΏΠ΅Ρ€ΡˆΠΈΡ… Unix Ρ– сучасного open-source-Ρ€ΡƒΡ…Ρƒ.

Π£ Ρ€Π°ΠΌΠΊΠ°Ρ… Ρ†Ρ–Ρ”Ρ— статті Ρ€ΠΎΠ·Π±ΠΈΡ€Π°Ρ‚ΠΈ Π²ΠΈΡ…Ρ–Π΄Π½ΠΈΠΉ ΠΊΠΎΠ΄ strace, Ρ‰ΠΎ Ρ€ΠΎΠ·Π΄ΠΎΠ±Ρ€Ρ–Π»Π° Π·Π° дСсятиліття, Π½Π΅Ρ€ΠΎΠ·ΡƒΠΌΠ½ΠΎ. АлС ΠΉ Ρ‚Π°Ρ”ΠΌΠ½ΠΈΡ†ΡŒ для Ρ‡ΠΈΡ‚Π°Ρ‡Ρ–Π² Π·Π°Π»ΠΈΡˆΠ°Ρ‚ΠΈΡΡŒ Π½Π΅ ΠΏΠΎΠ²ΠΈΠ½Π½ΠΎ. Π’ΠΎΠΌΡƒ, Ρ‰ΠΎΠ± ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΠΈ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏ Ρ€ΠΎΠ±ΠΎΡ‚ΠΈ ΠΏΠΎΠ΄Ρ–Π±Π½ΠΈΡ… strace ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌ, я Π½Π°Π²Π΅Π΄Ρƒ ΠΊΠΎΠ΄ ΠΌΡ–Π½Ρ–Π°Ρ‚ΡŽΡ€Π½ΠΎΠ³ΠΎ Ρ‚Ρ€Π°ΡΡƒΠ²Π°Π»ΡŒΠ½ΠΈΠΊΠ°. Piglet Trace (Ptr). Нічого особливого Π²Ρ–Π½ Ρ€ΠΎΠ±ΠΈΡ‚ΠΈ Π½Π΅ Π²ΠΌΡ–Ρ”, Π°Π»Π΅ Π³ΠΎΠ»ΠΎΠ²Π½Π΅ – систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ – Π²ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ΡŒ:

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

Piglet Trace Ρ€ΠΎΠ·ΠΏΡ–Π·Π½Π°Ρ” близько сотні систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π² Linux (Π΄ΠΈΠ². Ρ‚Π°Π±Π»ΠΈΡ†ΡŽ) Ρ‚Π° ΠΏΡ€Π°Ρ†ΡŽΡ” Ρ‚Ρ–Π»ΡŒΠΊΠΈ Π½Π° Π°Ρ€Ρ…Ρ–Ρ‚Π΅ΠΊΡ‚ΡƒΡ€Ρ– x86-64. Для Π½Π°Π²Ρ‡Π°Π»ΡŒΠ½ΠΈΡ… Ρ†Ρ–Π»Π΅ΠΉ Ρ†ΡŒΠΎΠ³ΠΎ Π΄ΠΎΡΡ‚Π°Ρ‚Π½ΡŒΠΎ.

Π”Π°Π²Π°ΠΉΡ‚Π΅ Ρ€ΠΎΠ·Π±Π΅Ρ€Π΅ΠΌΠΎ Ρ€ΠΎΠ±ΠΎΡ‚Ρƒ нашого ΠΊΠ»ΠΎΠ½Ρƒ. Π£ Π²ΠΈΠΏΠ°Π΄ΠΊΡƒ Π· Linux для Π²Ρ–Π΄Π»Π°Π΄Ρ‡ΠΈΠΊΡ–Π² Ρ‚Π° Ρ‚Ρ€Π°ΡΡƒΠ²Π°Π»ΡŒΠ½ΠΈΠΊΡ–Π² Π²ΠΈΠΊΠΎΡ€ΠΈΡΡ‚ΠΎΠ²ΡƒΡ”Ρ‚ΡŒΡΡ, як згадувалося Π²ΠΈΡ‰Π΅, систСмний Π²ΠΈΠΊΠ»ΠΈΠΊ ptrace. Π’Ρ–Π½ ΠΏΡ€Π°Ρ†ΡŽΡ” Π·Π° допомогою ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡Ρ– Ρƒ ΠΏΠ΅Ρ€ΡˆΠΎΠΌΡƒ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ– Ρ–Π΄Π΅Π½Ρ‚ΠΈΡ„Ρ–ΠΊΠ°Ρ‚ΠΎΡ€Ρ–Π² ΠΊΠΎΠΌΠ°Π½Π΄, Π· яких Π½Π°ΠΌ ΠΏΠΎΡ‚Ρ€Ρ–Π±Π½Ρ– лишС PTRACE_TRACEME, PTRACE_SYSCALL ΠΈ PTRACE_GETREGS.

Π ΠΎΠ±ΠΎΡ‚Π° Ρ‚Ρ€Π°ΡΡƒΠ²Π°Π»ΡŒΠ½ΠΈΠΊΠ° ΠΏΠΎΡ‡ΠΈΠ½Π°Ρ”Ρ‚ΡŒΡΡ Ρƒ Π·Π²ΠΈΡ‡Π°ΠΉΠ½ΠΎΠΌΡƒ Unix-стилі: fork(2) запускає Π΄ΠΎΡ‡Ρ–Ρ€Π½Ρ–ΠΉ процСс, Π° Ρ‚ΠΎΠΉ Ρƒ свою Ρ‡Π΅Ρ€Π³Ρƒ Π·Π° допомогою exec(3) запускає дослідТувану ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΡƒ. Π„Π΄ΠΈΠ½Π° Ρ‚ΠΎΠ½ΠΊΡ–ΡΡ‚ΡŒ Ρ‚ΡƒΡ‚ – Π²ΠΈΠΊΠ»ΠΈΠΊ ptrace(PTRACE_TRACEME) ΠΏΠ΅Ρ€Π΅Π΄ exec: процСс-Π½Π°Ρ‰Π°Π΄ΠΎΠΊ ΠΎΡ‡Ρ–ΠΊΡƒΡ”, Ρ‰ΠΎ процСс-Π±Π°Ρ‚ΡŒΠΊΠΎ ΠΉΠΎΠ³ΠΎ відстСТуватимС:

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

ΠŸΡ€ΠΎΡ†Π΅Ρ-Π±Π°Ρ‚ΡŒΠΊΠΎ Ρ‚Π΅ΠΏΠ΅Ρ€ ΠΌΠ°Ρ” Π²ΠΈΠΊΠ»ΠΈΠΊΠ°Ρ‚ΠΈ wait(2) Ρƒ Π΄ΠΎΡ‡Ρ–Ρ€Π½ΡŒΠΎΠΌΡƒ процСсі, Ρ‚ΠΎΠ±Ρ‚ΠΎ пСрСконатися, Ρ‰ΠΎ ΠΏΠ΅Ρ€Π΅ΠΊΠ»ΡŽΡ‡Π΅Π½Π½Ρ Π² Ρ€Π΅ΠΆΠΈΠΌ трасування відбулося:

/* Parent */

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

На Ρ†ΡŒΠΎΠΌΡƒ приготування Π·Π°ΠΊΡ–Π½Ρ‡Π΅Π½ΠΎ Ρ– ΠΌΠΎΠΆΠ½Π° Ρ€ΠΎΠ·ΠΏΠΎΡ‡ΠΈΠ½Π°Ρ‚ΠΈ Π±Π΅Π·ΠΏΠΎΡΠ΅Ρ€Π΅Π΄Π½ΡŒΠΎ відстСТСння систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π² Ρƒ нСскінчСнному Ρ†ΠΈΠΊΠ»Ρ–.

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

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

ВласнС, Ρ†ΠΈΠΊΠ»:

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

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

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

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

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

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

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

Ось Ρ– вСсь Ρ‚Ρ€Π°ΡΡƒΠ²Π°Π»ΡŒΠ½ΠΈΠΊ. Π’Π΅ΠΏΠ΅Ρ€ Π²ΠΈ Π·Π½Π°Ρ”Ρ‚Π΅, Π· Ρ‡ΠΎΠ³ΠΎ Ρ€ΠΎΠ·ΠΏΠΎΡ‡ΠΈΠ½Π°Ρ‚ΠΈ Ρ‡Π΅Ρ€Π³ΠΎΠ²Π΅ портування DTrace Π½Π° Linux.

Ази: запуск ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ ΠΏΡ–Π΄ кСруванням strace

Π―ΠΊ ΠΏΠ΅Ρ€ΡˆΠΈΠΉ ΠΏΡ€ΠΈΠΊΠ»Π°Π΄ використання strace, ΠΌΠ°Π±ΡƒΡ‚ΡŒ, Π²Π°Ρ€Ρ‚ΠΎ навСсти Π½Π°ΠΉΠΏΡ€ΠΎΡΡ‚Ρ–ΡˆΠΈΠΉ спосіб - запуск ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ ΠΏΡ–Π΄ кСруванням strace.

Π©ΠΎΠ± Π½Π΅ копатися Π² нСскінчСнному списку Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π² Ρ‚ΠΈΠΏΠΎΠ²ΠΎΡ— ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ, напишСмо ΠΌΡ–Π½Ρ–ΠΌΠ°Π»ΡŒΠ½Ρƒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΡƒ Π½Π°Π²ΠΊΠΎΠ»ΠΎ write:

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

Π—Π±Π΅Ρ€Π΅ΠΌΠΎ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΡƒ Ρ– пСрСконаємося, Ρ‰ΠΎ Π²ΠΎΠ½Π° ΠΏΡ€Π°Ρ†ΡŽΡ”:

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

Π† Π½Π°Ρ€Π΅ΡˆΡ‚Ρ– запустимо Ρ—Ρ— ΠΏΡ–Π΄ кСруванням strace:

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

Π”ΡƒΠΆΠ΅ "багатослівно" Ρ– Π½Π΅ Π΄ΡƒΠΆΠ΅ ΠΏΡ–Π·Π½Π°Π²Π°Π»ΡŒΠ½ΠΎ. ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ Ρ‚ΡƒΡ‚ Π΄Π²Ρ–: вивСдСння ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ Π·ΠΌΡ–ΡˆΠ°Π½Π΅ Π· висновком strace Ρ‚Π° достаток систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π², які нас Π½Π΅ Ρ†Ρ–ΠΊΠ°Π²Π»ΡΡ‚ΡŒ.

Π ΠΎΠ·Π΄Ρ–Π»ΠΈΡ‚ΠΈ стандартний ΠΏΠΎΡ‚Ρ–ΠΊ вивСдСння ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ Ρ‚Π° вивСдСння ΠΏΠΎΠΌΠΈΠ»ΠΎΠΊ strace ΠΌΠΎΠΆΠ½Π° Π·Π° допомогою ΠΊΠ»ΡŽΡ‡Π° -o, Ρ‰ΠΎ пСрСнаправляє список систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π² Ρƒ Ρ„Π°ΠΉΠ»-Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚.

Π—Π°Π»ΠΈΡˆΠΈΠ»ΠΎΡΡ розібратися Ρ–Π· ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΎΡŽ Β«Π·Π°ΠΉΠ²ΠΈΡ…Β» Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π². ΠŸΡ€ΠΈΠΏΡƒΡΡ‚ΠΈΠΌΠΎ, Ρ‰ΠΎ нас Ρ†Ρ–ΠΊΠ°Π²Π»ΡΡ‚ΡŒ лишС Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ write. ΠšΠ»ΡŽΡ‡ -e дозволяє Π²ΠΊΠ°Π·ΡƒΠ²Π°Ρ‚ΠΈ Π²ΠΈΡ€Π°Π·ΠΈ, Π·Π° якими Ρ„Ρ–Π»ΡŒΡ‚Ρ€ΡƒΠ²Π°Ρ‚ΠΈΠΌΡƒΡ‚ΡŒΡΡ систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ. ΠΠ°ΠΉΠΏΠΎΠΏΡƒΠ»ΡΡ€Π½Ρ–ΡˆΠΈΠΉ Π²Π°Ρ€Ρ–Π°Π½Ρ‚ ΡƒΠΌΠΎΠ²ΠΈ - ΠΏΡ€ΠΈΡ€ΠΎΠ΄Π½ΠΎ, trace=*, Π·Π° допомогою якого ΠΌΠΎΠΆΠ½Π° Π·Π°Π»ΠΈΡˆΠΈΡ‚ΠΈ Ρ‚Ρ–Π»ΡŒΠΊΠΈ Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ, Ρ‰ΠΎ нас Ρ†Ρ–ΠΊΠ°Π²Π»ΡΡ‚ΡŒ.

ΠŸΡ€ΠΈ одночасному використанні -o ΠΈ -e ΠΌΠΈ ΠΎΡ‚Ρ€ΠΈΠΌΠ°Ρ”ΠΌΠΎ:

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

Π’Π°ΠΊ, ΠΏΠΎΠ³ΠΎΠ΄ΡŒΡ‚Π΅ΡΡ, Π½Π°Π±Π°Π³Π°Ρ‚ΠΎ лСгшС Ρ‡ΠΈΡ‚Π°Ρ”Ρ‚ΡŒΡΡ.

А Ρ‰Π΅ ΠΌΠΎΠΆΠ½Π° ΠΏΡ€ΠΈΠ±ΠΈΡ€Π°Ρ‚ΠΈ систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ - Π½Π°ΠΏΡ€ΠΈΠΊΠ»Π°Π΄, ΠΏΠΎΠ²'язані Π· виділСнням Ρ‚Π° Π·Π²Ρ–Π»ΡŒΠ½Π΅Π½Π½ΡΠΌ ΠΏΠ°ΠΌ'яті:

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

Π—Π²Π΅Ρ€Π½Ρ–Ρ‚ΡŒ ΡƒΠ²Π°Π³Ρƒ Π½Π° Π΅ΠΊΡ€Π°Π½ΠΎΠ²Π°Π½ΠΈΠΉ Π·Π½Π°ΠΊ ΠΎΠΊΠ»ΠΈΠΊΡƒ Π² списку Π²ΠΈΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ… Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π²: Ρ†ΡŒΠΎΠ³ΠΎ Π²ΠΈΠΌΠ°Π³Π°Ρ” ΠΊΠΎΠΌΠ°Π½Π΄Π½Π° ΠΎΠ±ΠΎΠ»ΠΎΠ½ΠΊΠ° (Π°Π½Π³Π». ΠΎΠ±ΠΎΠ»ΠΎΠ½ΠΊΠ°).

Π£ ΠΌΠΎΡ—ΠΉ вСрсії glibc Π·Π°Π²Π΅Ρ€ΡˆΡƒΡ” виконання процСсу систСмний Π²ΠΈΠΊΠ»ΠΈΠΊ exit_group, Π° Π½Π΅ Ρ‚Ρ€Π°Π΄ΠΈΡ†Ρ–ΠΉΠ½ΠΈΠΉ _exit. Π’ Ρ†ΡŒΠΎΠΌΡƒ полягає ΡΠΊΠ»Π°Π΄Π½Ρ–ΡΡ‚ΡŒ Ρ€ΠΎΠ±ΠΎΡ‚ΠΈ Ρ–Π· систСмними Π²ΠΈΠΊΠ»ΠΈΠΊΠ°ΠΌΠΈ: інтСрфСйс, Π· яким ΠΏΡ€Π°Ρ†ΡŽΡ” програміст, Π½Π΅ ΠΌΠ°Ρ” прямого Π²Ρ–Π΄Π½ΠΎΡˆΠ΅Π½Π½Ρ Π΄ΠΎ систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π². Π‘Ρ–Π»ΡŒΡˆΠ΅ Ρ‚ΠΎΠ³ΠΎ, Π²Ρ–Π½ рСгулярно Π·ΠΌΡ–Π½ΡŽΡ”Ρ‚ΡŒΡΡ Π·Π°Π»Π΅ΠΆΠ½ΠΎ Π²Ρ–Π΄ Ρ€Π΅Π°Π»Ρ–Π·Π°Ρ†Ρ–Ρ— Ρ‚Π° ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΠΈ.

Ази: приєднання Π΄ΠΎ процСсу Π½Π° Π»ΡŒΠΎΡ‚Ρƒ

Π‘ΠΏΠΎΡ‡Π°Ρ‚ΠΊΡƒ систСмний Π²ΠΈΠΊΠ»ΠΈΠΊ ptrace, Π½Π° якому ΠΏΠΎΠ±ΡƒΠ΄ΠΎΠ²Π°Π½ΠΎ strace, ΠΌΠΎΠΆΠ½Π° Π±ΡƒΠ»ΠΎ використовувати лишС ΠΏΡ–Π΄ час запуску ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ Ρƒ ΡΠΏΠ΅Ρ†Ρ–Π°Π»ΡŒΠ½ΠΎΠΌΡƒ Ρ€Π΅ΠΆΠΈΠΌΡ–. Π’Π°ΠΊΠ΅ обмСТСння, ΠΌΠΎΠΆΠ»ΠΈΠ²ΠΎ, Π·Π²ΡƒΡ‡Π°Π»ΠΎ Ρ€ΠΎΠ·ΡƒΠΌΠ½ΠΎ Π·Π° часів Version 6 Unix. Π£ Π½Π°ΡˆΡ– ΠΆ Π΄Π½Ρ– Ρ†ΡŒΠΎΠ³ΠΎ Π½Π΅Π΄ΠΎΡΡ‚Π°Ρ‚Π½ΡŒΠΎ: Π±ΡƒΠ²Π°Ρ”, ΠΏΠΎΡ‚Ρ€Ρ–Π±Π½ΠΎ дослідТувати ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ ΠΏΡ€Π°Ρ†ΡŽΡŽΡ‡ΠΎΡ— ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ. Π’ΠΈΠΏΠΎΠ²ΠΈΠΉ ΠΏΡ€ΠΈΠΊΠ»Π°Π΄ - Π·Π°Π±Π»ΠΎΠΊΠΎΠ²Π°Π½ΠΈΠΉ Π½Π° дСскрипторі Π°Π±ΠΎ сплячий процСс. Π’ΠΎΠΌΡƒ сучасна strace Π²ΠΌΡ–Ρ” приєднуватися Π΄ΠΎ процСсів Π½Π° Π»ΡŒΠΎΡ‚Ρƒ.

ΠŸΡ€ΠΈΠΊΠ»Π°Π΄ зависає ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ:

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

    char str[] = "write men";

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

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

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

    return EXIT_SUCCESS;
}

Π—Π±Π΅Ρ€Π΅ΠΌΠΎ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΡƒ Ρ– пСрСконаємося Π² Ρ‚ΠΎΠΌΡƒ, Ρ‰ΠΎ Π²ΠΎΠ½Π° зависла:

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

А Ρ‚Π΅ΠΏΠ΅Ρ€ спробуємо приєднатися Π΄ΠΎ Π½Π΅Ρ—:

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

ΠŸΡ€ΠΎΠ³Ρ€Π°ΠΌΡƒ Π·Π°Π±Π»ΠΎΠΊΠΎΠ²Π°Π½ΠΎ Π²ΠΈΠΊΠ»ΠΈΠΊΠΎΠΌ pause. Подивимося, як Π²ΠΎΠ½Π° Π²Ρ–Π΄Ρ€Π΅Π°Π³ΡƒΡ” Π½Π° сигнали:

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

Ми запустили завислу ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΡƒ Ρ– приєдналися Π΄ΠΎ Π½Π΅Ρ— Π·Π° допомогою strace. Π—'ясувалися Π΄Π²Ρ– Ρ€Π΅Ρ‡Ρ–: систСмний Π²ΠΈΠΊΠ»ΠΈΠΊ pause Ρ–Π³Π½ΠΎΡ€ΡƒΡ” сигнали Π±Π΅Π· ΠΎΠ±Ρ€ΠΎΠ±Π½ΠΈΠΊΡ–Π² Ρ–, Ρ‰ΠΎ Ρ†Ρ–ΠΊΠ°Π²Ρ–ΡˆΠ΅, strace відстСТує Π½Π΅ лишС систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ, Π° ΠΉ сигнали, Ρ‰ΠΎ Π²Ρ…ΠΎΠ΄ΡΡ‚ΡŒ.

ΠŸΡ€ΠΈΠΊΠ»Π°Π΄: відстСТСння Π΄ΠΎΡ‡Ρ–Ρ€Π½Ρ–Ρ… процСсів

Π ΠΎΠ±ΠΎΡ‚Π° Π· процСсами Ρ‡Π΅Ρ€Π΅Π· Π²ΠΈΠΊΠ»ΠΈΠΊ fork - Основа всіх Unix. Π”Π°Π²Π°ΠΉΡ‚Π΅ подивимося, як strace ΠΏΡ€Π°Ρ†ΡŽΡ” Π· Π΄Π΅Ρ€Π΅Π²ΠΎΠΌ процСсів Π½Π° ΠΏΡ€ΠΈΠΊΠ»Π°Π΄Ρ– нСскладної Β«ΠΏΠ»Ρ–Π΄Π½ΠΎΡ—Β» ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ:

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

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

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

    wait(NULL);

    exit(EXIT_SUCCESS);
}

Π’ΡƒΡ‚ Π²ΠΈΡ…Ρ–Π΄Π½ΠΈΠΉ процСс ΡΡ‚Π²ΠΎΡ€ΡŽΡ” Π΄ΠΎΡ‡Ρ–Ρ€Π½Ρ–ΠΉ процСс, ΠΎΠ±ΠΈΠ΄Π²Π° ΠΏΠΈΡˆΡƒΡ‚ΡŒ стандартний ΠΏΠΎΡ‚Ρ–ΠΊ висновку:

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

Π—Π° умовчанням ΠΌΠΈ ΠΏΠΎΠ±Π°Ρ‡ΠΈΠΌΠΎ лишС систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ Π±Π°Ρ‚ΡŒΠΊΡ–Π²ΡΡŒΠΊΠΎΠ³ΠΎ процСсу:

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

ВідстСТувати Π΄Π΅Ρ€Π΅Π²ΠΎ процСсів ΠΏΠΎΠ²Π½Ρ–ΡΡ‚ΡŽ Π΄ΠΎΠΏΠΎΠΌΠ°Π³Π°Ρ” ΠΏΡ€Π°ΠΏΠΎΡ€ -f, Π· яким strace відстСТує систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ Ρƒ процСсах-Π½Π°Ρ‰Π°Π΄ΠΊΠ°Ρ…. Π”ΠΎ ΠΊΠΎΠΆΠ½ΠΎΠ³ΠΎ рядка Π²ΠΈΠ²ΠΎΠ΄Ρƒ ΠΏΡ€ΠΈ Ρ†ΡŒΠΎΠΌΡƒ Π΄ΠΎΠ΄Π°Ρ”Ρ‚ΡŒΡΡ pid процСсу, Ρ‰ΠΎ Ρ€ΠΎΠ±ΠΈΡ‚ΡŒ систСмний висновок:

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

Π£ Ρ†ΡŒΠΎΠΌΡƒ контСксті ΠΌΠΎΠΆΠ΅ стати Π² Π½Π°Π³ΠΎΠ΄Ρ– Ρ„Ρ–Π»ΡŒΡ‚Ρ€Π°Ρ†Ρ–Ρ Π·Π° Π³Ρ€ΡƒΠΏΠ°ΠΌΠΈ систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π²:

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

Π―ΠΊΠΈΠΉ систСмний Π²ΠΈΠΊΠ»ΠΈΠΊ Π²ΠΈΠΊΠΎΡ€ΠΈΡΡ‚ΠΎΠ²ΡƒΡ”Ρ‚ΡŒΡΡ для створСння Π½ΠΎΠ²ΠΎΠ³ΠΎ процСсу?

ΠŸΡ€ΠΈΠΊΠ»Π°Π΄: ΡˆΠ»ΡΡ…ΠΈ Π΄ΠΎ Ρ„Π°ΠΉΠ»Ρ–Π² Π·Π°ΠΌΡ–ΡΡ‚ΡŒ дСскрипторів

Π—Π½Π°Ρ‚ΠΈ Ρ„Π°ΠΉΠ»ΠΎΠ²Ρ– дСскриптори, Π±Π΅Π·ΡƒΠΌΠΎΠ²Π½ΠΎ, корисно, Π°Π»Π΅ Ρ–ΠΌΠ΅Π½Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΈΡ… Ρ„Π°ΠΉΠ»Ρ–Π², Π΄ΠΎ яких Π·Π²Π΅Ρ€Ρ‚Π°Ρ”Ρ‚ΡŒΡΡ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ°, ΠΌΠΎΠΆΡƒΡ‚ΡŒ стати Ρƒ Π½Π°Π³ΠΎΠ΄Ρ–.

Наступна ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ° пишС рядок Ρƒ тимчасовий Ρ„Π°ΠΉΠ»:

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

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

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

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

    do_write(out_fd);

    return EXIT_SUCCESS;
}

ΠŸΡ€ΠΈ Π·Π²ΠΈΡ‡Π°ΠΉΠ½ΠΎΠΌΡƒ Π²ΠΈΠΊΠ»ΠΈΠΊΡƒ strace ΠΏΠΎΠΊΠ°ΠΆΠ΅ значСння числа-дСскриптора, ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΎΠ³ΠΎ Π΄ΠΎ систСмного Π²ΠΈΠΊΠ»ΠΈΠΊΡƒ:

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

Π— ΠΏΡ€Π°ΠΏΠΎΡ€ΠΎΠΌ -y ΡƒΡ‚ΠΈΠ»Ρ–Ρ‚Π° ΠΏΠΎΠΊΠ°Π·ΡƒΡ” ΡˆΠ»ΡΡ… Π΄ΠΎ Ρ„Π°ΠΉΠ»Ρƒ, якому Π²Ρ–Π΄ΠΏΠΎΠ²Ρ–Π΄Π°Ρ” дСскриптор:

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

ΠŸΡ€ΠΈΠΊΠ»Π°Π΄: відстСТСння Π·Π²Π΅Ρ€Π½Π΅Π½ΡŒ Π΄ΠΎ Ρ„Π°ΠΉΠ»Ρ–Π²

Π©Π΅ ΠΎΠ΄Π½Π° корисна ΠΌΠΎΠΆΠ»ΠΈΠ²Ρ–ΡΡ‚ΡŒ: Π²Ρ–Π΄ΠΎΠ±Ρ€Π°ΠΆΠ°Ρ‚ΠΈ лишС систСмні Π΄Π·Π²Ρ–Π½ΠΊΠΈ, ΠΏΠΎΠ²'язані Π· ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΈΠΌ Ρ„Π°ΠΉΠ»ΠΎΠΌ. Наступна ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ° дописує рядок Π΄ΠΎΠ²Ρ–Π»ΡŒΠ½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Ρƒ, ΠΏΠ΅Ρ€Π΅Π΄Π°Π½ΠΈΠΉ Π² Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ–:

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

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

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

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

    do_write(out_fd);

    return EXIT_SUCCESS;
}

Π—Π° замовчуванням strace Π²ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ΡŒ Π±Π°Π³Π°Ρ‚ΠΎ Π·Π°ΠΉΠ²ΠΎΡ— Ρ–Π½Ρ„ΠΎΡ€ΠΌΠ°Ρ†Ρ–Ρ—. ΠŸΡ€Π°ΠΏΠΎΡ€ -P Π· Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ Π·ΠΌΡƒΡˆΡƒΡ” strace Π²ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ΠΈ лишС звСрнСння Π΄ΠΎ Π²ΠΊΠ°Π·Π°Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Ρƒ:

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

ΠΏΡ€ΠΈΠΊΠ»Π°Π΄: Π±Π°Π³Π°Ρ‚ΠΎΠΏΠΎΡ‚ΠΎΠΊΠΎΠ²Ρ– ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ

Π£Ρ‚ΠΈΠ»Ρ–Ρ‚Π° strace ΠΌΠΎΠΆΠ΅ Π΄ΠΎΠΏΠΎΠΌΠΎΠ³Ρ‚ΠΈ Ρ– ΠΏΡ€ΠΈ Ρ€ΠΎΠ±ΠΎΡ‚Ρ– Π· Π±Π°Π³Π°Ρ‚ΠΎΠΏΠΎΡ‚ΠΎΡ‡Π½ΠΎΡŽ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΎΡŽ. Наступна ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ° пишС стандартний ΠΏΠΎΡ‚Ρ–ΠΊ вивСдСння Π· Π΄Π²ΠΎΡ… ΠΏΠΎΡ‚ΠΎΠΊΡ–Π²:

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

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

    return NULL;
}

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

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

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

    printf("Initial thread: done");

    exit(EXIT_SUCCESS);
}

Π—Π±ΠΈΡ€Π°Ρ‚ΠΈ Ρ—Ρ— Ρ‚Ρ€Π΅Π±Π°, Π·Π²ΠΈΡ‡Π°ΠΉΠ½ΠΎ, Π·Ρ– ΡΠΏΠ΅Ρ†Ρ–Π°Π»ΡŒΠ½ΠΈΠΌ ΠΏΡ€ΠΈΠ²Ρ–Ρ‚ΠΎΠΌ Π»Ρ–Π½ΠΊΠΎΠ²Π½ΠΈΠΊΡƒ - ΠΏΡ€Π°ΠΏΠΎΡ€ΠΎΠΌ -pthread:

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

ΠΏΡ€Π°ΠΏΠΎΡ€ -f, як Ρ– Ρƒ Π²ΠΈΠΏΠ°Π΄ΠΊΡƒ Π·Ρ– Π·Π²ΠΈΡ‡Π°ΠΉΠ½ΠΈΠΌΠΈ процСсами, Π΄ΠΎΠ΄Π°ΡΡ‚ΡŒ Π½Π° ΠΏΠΎΡ‡Π°Ρ‚ΠΎΠΊ ΠΊΠΎΠΆΠ½ΠΎΠ³ΠΎ рядка pid процСсу.

Π—Π²ΠΈΡ‡Π°ΠΉΠ½ΠΎ, ΠΉΠ΄Π΅Ρ‚ΡŒΡΡ Π½Π΅ ΠΏΡ€ΠΎ Ρ–Π΄Π΅Π½Ρ‚ΠΈΡ„Ρ–ΠΊΠ°Ρ‚ΠΎΡ€ ΠΏΠΎΡ‚ΠΎΠΊΡƒ Π² сСнсі Ρ€Π΅Π°Π»Ρ–Π·Π°Ρ†Ρ–Ρ— стандарту POSIX Threads, Π° ΠΏΡ€ΠΎ Π½ΠΎΠΌΠ΅Ρ€, який Π²ΠΈΠΊΠΎΡ€ΠΈΡΡ‚ΠΎΠ²ΡƒΡ”Ρ‚ΡŒΡΡ ΠΏΠ»Π°Π½ΡƒΠ²Π°Π»ΡŒΠ½ΠΈΠΊΠΎΠΌ завдань Ρƒ Linux. Π— погляду ΠΎΡΡ‚Π°Π½Π½ΡŒΠΎΠ³ΠΎ Π½Π΅ΠΌΠ°Ρ” ΠΆΠΎΠ΄Π½ΠΈΡ… процСсів Ρ– ΠΏΠΎΡ‚ΠΎΠΊΡ–Π² β€” Ρ” завдання, які Ρ‚Ρ€Π΅Π±Π° Ρ€ΠΎΠ·ΠΏΠΎΠ΄Ρ–Π»ΠΈΡ‚ΠΈ доступними ядрами машини.

ΠŸΡ€ΠΈ Ρ€ΠΎΠ±ΠΎΡ‚Ρ– Π² ΠΊΡ–Π»ΡŒΠΊΠ° ΠΏΠΎΡ‚ΠΎΠΊΡ–Π² систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π² стає Π΄ΡƒΠΆΠ΅ Π±Π°Π³Π°Ρ‚ΠΎ:

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

ΠœΠ°Ρ” сСнс обмСТитися лишС управлінням процСсами Ρ‚Π° систСмним Π²ΠΈΠΊΠ»ΠΈΠΊΠΎΠΌ write:

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

Π”ΠΎ Ρ€Π΅Ρ‡Ρ–, питання. Π―ΠΊΠΈΠΉ Π΄Π·Π²Ρ–Π½ΠΎΠΊ Π²ΠΈΠΊΠΎΡ€ΠΈΡΡ‚ΠΎΠ²ΡƒΡ”Ρ‚ΡŒΡΡ для створСння Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΡ‚ΠΎΠΊΡƒ? Π§ΠΈΠΌ Ρ‚Π°ΠΊΠΈΠΉ Π΄Π·Π²Ρ–Π½ΠΎΠΊ для ΠΏΠΎΡ‚ΠΎΠΊΡ–Π² Π²Ρ–Π΄Ρ€Ρ–Π·Π½ΡΡ”Ρ‚ΡŒΡΡ Π²Ρ–Π΄ Π΄Π·Π²Ρ–Π½ΠΊΠ° для процСсів?

ΠœΠ°ΠΉΡΡ‚Π΅Ρ€-клас: стСк процСсу Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚ систСмного Π²ΠΈΠΊΠ»ΠΈΠΊΡƒ

Одна Π· Π½Π΅Ρ‰ΠΎΠ΄Π°Π²Π½ΠΎ Π·'явилася Π² strace моТливостСй β€” ВідобраТСння стСка Π΄Π·Π²Ρ–Π½ΠΊΡ–Π² Ρ„ΡƒΠ½ΠΊΡ†Ρ–ΠΉ ΠΏΡ–Π΄ час систСмного Π΄Π·Π²Ρ–Π½ΠΊΠ°. ΠŸΡ€ΠΎΡΡ‚ΠΈΠΉ ΠΏΡ€ΠΈΠΊΠ»Π°Π΄:

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

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

ΠŸΡ€ΠΈΡ€ΠΎΠ΄Π½ΠΎ, висновок ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ стає Π΄ΡƒΠΆΠ΅ ΠΎΠ±'Ρ”ΠΌΠ½ΠΈΠΌ, Ρ–, ΠΊΡ€Ρ–ΠΌ ΠΏΡ€Π°ΠΏΠΎΡ€Π° -k (відобраТСння стСка Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π²), ΠΌΠ°Ρ” сСнс Ρ„Ρ–Π»ΡŒΡ‚Ρ€ΡƒΠ²Π°Ρ‚ΠΈ систСмні Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ Π½Π° Ρ–ΠΌ'я:

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

ΠœΠ°ΠΉΡΡ‚Π΅Ρ€-клас: Ρ–Π½'єкція ΠΏΠΎΠΌΠΈΠ»ΠΎΠΊ

Π† Ρ‰Π΅ ΠΎΠ΄Π½Π° Π½ΠΎΠ²Π° Ρ‚Π° Π΄ΡƒΠΆΠ΅ корисна ΠΌΠΎΠΆΠ»ΠΈΠ²Ρ–ΡΡ‚ΡŒ: Ρ–Π½'єкція ΠΏΠΎΠΌΠΈΠ»ΠΎΠΊ. Ось ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ°, Ρ‰ΠΎ пишС Π΄Π²Π° рядки Π² ΠΏΠΎΡ‚Ρ–ΠΊ вивСдСння:

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

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

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

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

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

    return EXIT_SUCCESS;
}

ВідстСТуємо ΠΎΠ±ΠΈΠ΄Π²Π° Π΄Π·Π²Ρ–Π½ΠΊΠΈ write:

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

А Ρ‚Π΅ΠΏΠ΅Ρ€ використовуємо Π²ΠΈΡ€Π°Π· injectΡ‰ΠΎΠ± вставити ΠΏΠΎΠΌΠΈΠ»ΠΊΡƒ EBADF Ρƒ всі Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ write:

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

Π¦Ρ–ΠΊΠ°Π²ΠΎ, Ρ‰ΠΎ ΠΏΠΎΠΌΠΈΠ»ΠΊΠΈ ΠΏΠΎΠ²Π΅Ρ€Ρ‚Π°ΡŽΡ‚ΡŒ всі Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ write, Π²ΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‡ΠΈ Π΄Π·Π²Ρ–Π½ΠΎΠΊ, ΠΏΡ€ΠΈΡ…ΠΎΠ²Π°Π½ΠΈΠΉ Π·Π° perror. ΠœΠ°Ρ” сСнс ΠΏΠΎΠ²Π΅Ρ€Ρ‚Π°Ρ‚ΠΈ ΠΏΠΎΠΌΠΈΠ»ΠΊΡƒ лишС для ΠΏΠ΅Ρ€ΡˆΠΎΠ³ΠΎ Ρ–Π· Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π²:

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

Або Π΄Ρ€ΡƒΠ³ΠΎΠ³ΠΎ:

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

Π’ΠΈΠΏ ΠΏΠΎΠΌΠΈΠ»ΠΊΠΈ Π²ΠΊΠ°Π·ΡƒΠ²Π°Ρ‚ΠΈ Π½Π΅ ΠΎΠ±ΠΎΠ²'язково:

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

Π£ ΠΏΠΎΡ”Π΄Π½Π°Π½Π½Ρ– Π· Ρ–Π½ΡˆΠΈΠΌΠΈ ΠΏΡ€Π°ΠΏΠΎΡ€Π°ΠΌΠΈ ΠΌΠΎΠΆΠ½Π° Π»Π°ΠΌΠ°Ρ‚ΠΈ звСрнСння Π΄ΠΎ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ³ΠΎ Ρ„Π°ΠΉΠ»Ρƒ. ΠŸΡ€ΠΈΠΊΠ»Π°Π΄:

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

ΠšΡ€Ρ–ΠΌ Ρ–Π½'Ρ”ΠΊΡ†Ρ–ΠΉ ΠΏΠΎΠΌΠΈΠ»ΠΎΠΊ, ΠΌΠΎΠΆΠ½Π° Π²Π²ΠΎΠ΄ΠΈΡ‚ΠΈ Π·Π°Ρ‚Ρ€ΠΈΠΌΠΊΠΈ ΠΏΡ–Π΄ час здійснСння Π΄Π·Π²Ρ–Π½ΠΊΡ–Π² Π°Π±ΠΎ отримання сигналів.

ΠŸΡ–ΡΠ»ΡΠΌΠΎΠ²Π°

Π£Ρ‚ΠΈΠ»Ρ–Ρ‚Π° strace - ΠŸΡ€ΠΎΡΡ‚ΠΈΠΉ Ρ– Π½Π°Π΄Ρ–ΠΉΠ½ΠΈΠΉ інструмСнт. АлС ΠΊΡ€Ρ–ΠΌ систСмних Π²ΠΈΠΊΠ»ΠΈΠΊΡ–Π² Π½Π°Π»Π°Π³ΠΎΠ΄ΠΆΡƒΠ²Π°Ρ‚ΠΈ Ρ‚Ρ€Π°ΠΏΠ»ΡΡ”Ρ‚ΡŒΡΡ ΠΉ Ρ–Π½ΡˆΡ– аспСкти Ρ€ΠΎΠ±ΠΎΡ‚ΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌ Ρ‚Π° ΠΎΠΏΠ΅Ρ€Π°Ρ†Ρ–ΠΉΠ½ΠΎΡ— систСми. Наприклад, відстСТувати Π²ΠΈΠΊΠ»ΠΈΠΊΠΈ Π±Ρ–Π±Π»Ρ–ΠΎΡ‚Π΅ΠΊ, Ρ‰ΠΎ Π΄ΠΈΠ½Π°ΠΌΡ–Ρ‡Π½ΠΎ Π»Ρ–Π½ΡƒΡŽΡ‚ΡŒΡΡ, Π²ΠΌΡ–Ρ” ltrace, Π·Π°Π·ΠΈΡ€Π½ΡƒΡ‚ΠΈ Π² Ρ€ΠΎΠ±ΠΎΡ‚Ρƒ ΠΎΠΏΠ΅Ρ€Π°Ρ†Ρ–ΠΉΠ½ΠΎΡ— систСми ΠΌΠΎΠΆΡƒΡ‚ΡŒ ΠšΠ»Π°Ρ†Π½Ρ–Ρ‚ΡŒ System ΠΈ ftrace, Π° Π³Π»ΠΈΠ±ΠΎΠΊΠΎ дослідТувати ΠΏΡ€ΠΎΠ΄ΡƒΠΊΡ‚ΠΈΠ²Π½Ρ–ΡΡ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌ дозволяє ΠΏΠ΅Ρ€. ΠŸΡ€ΠΎΡ‚Π΅ самС strace β€” ΠΏΠ΅Ρ€ΡˆΠ° лінія ΠΎΠ±ΠΎΡ€ΠΎΠ½ΠΈ Ρƒ Ρ€Π°Π·Ρ– ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌ Ρ–Π· власними Ρ‚Π° Ρ‡ΡƒΠΆΠΈΠΌΠΈ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ°ΠΌΠΈ, Ρ– Π²ΠΈΠΊΠΎΡ€ΠΈΡΡ‚ΠΎΠ²ΡƒΡŽ я Ρ—Ρ— ΠΌΡ–Π½Ρ–ΠΌΡƒΠΌ ΠΏΠ°Ρ€Ρƒ Ρ€Π°Π·Ρ–Π² Π½Π° Ρ‚ΠΈΠΆΠ΄Π΅Π½ΡŒ.

Π‘Π»ΠΎΠ²ΠΎΠΌ, Π»ΡŽΠ±ΠΈΡ‚Π΅ Unix, Ρ‡ΠΈΡ‚Π°ΠΉΡ‚Π΅ man 1 strace Ρ– Π½Π΅ соромтСся підглядати Π·Π° вашими ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ°ΠΌΠΈ!

Π”ΠΆΠ΅Ρ€Π΅Π»ΠΎ: habr.com

Π”ΠΎΠ΄Π°Ρ‚ΠΈ ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€ Π°Π±ΠΎ Π²Ρ–Π΄Π³ΡƒΠΊ