Unix ๊ณ์ด ์ด์ ์ฒด์ ์์ ํ๋ก๊ทธ๋จ๊ณผ ์ธ๋ถ ์ธ๊ณ ๋ฐ ์ด์ ์ฒด์ ์์ ํต์ ์ ์์คํ ํธ์ถ์ด๋ผ๋ ์์ ๊ธฐ๋ฅ ์งํฉ์ ํตํด ์ด๋ฃจ์ด์ง๋๋ค. ์ด๋ ๋๋ฒ๊น ๋ชฉ์ ์ผ๋ก ํ๋ก์ธ์ค์์ ์คํ๋๋ ์์คํ ํธ์ถ์ ๊ฐ์ํ๋ ๊ฒ์ด ์ ์ฉํ ์ ์์์ ์๋ฏธํฉ๋๋ค.
Linux์์ ํ๋ก๊ทธ๋จ์ "์น๋ฐํ ์๋ช
"์ ๋ชจ๋ํฐ๋งํ๋ ๋ฐ ๋์์ด ๋๋ ์ ํธ๋ฆฌํฐ strace
, ์ด๊ฒ์ด ์ด ๊ธฐ์ฌ์ ์ฃผ์ ์
๋๋ค. ์คํ์ด ์ฅ๋น์ ์ฌ์ฉ ์ฌ๋ก๋ ๊ฐ๋ตํ ์ญ์ฌ๋ฅผ ๋๋ฐํฉ๋๋ค. strace
๊ทธ๋ฆฌ๊ณ ๊ทธ๋ฌํ ํ๋ก๊ทธ๋จ์ ์ค๊ณ์ ๋ํ ์ค๋ช
.
๋ด์ฉ
์ข ์ ๊ธฐ์ ๊ฐ๋จํ ๋งํด์ Strace ์ฅ์น: Piglet Trace ๊ธฐ๋ณธ ์ฌํญ: strace๋ฅผ ์คํํ๋ ํ๋ก๊ทธ๋จ ์คํ ๊ธฐ๋ณธ ์ฌํญ: ์ฆ์์์ ํ๋ก์ธ์ค์ ์ฐธ์ฌ ์: ํ์ ํ๋ก์ธ์ค ์ถ์ ์: ํธ๋ค ๋์ ํ์ผ ๊ฒฝ๋ก ์: ํ์ผ ์ก์ธ์ค ์ถ์ ์: ๋ค์ค ์ค๋ ๋ ํ๋ก๊ทธ๋จ ๋ง์คํฐ ํด๋์ค: ์์คํ ํธ์ถ ์ ํ๋ก์ธ์ค ์คํ ๋ง์คํฐ ํด๋์ค: ์ค๋ฅ ์ฃผ์ ์ฌํ
์ข ์ ๊ธฐ์
Unix์์ ํ๋ก๊ทธ๋จ๊ณผ OS ์ปค๋ ๊ฐ์ ์ฃผ์ ์ธํฐํ์ด์ค๋ ์์คํ ํธ์ถ์ ๋๋ค. ์์คํ ํธ์ถ, ์์คํ ํธ์ถ), ํ๋ก๊ทธ๋จ๊ณผ ์ธ๋ถ ์ธ๊ณ์ ์ํธ ์์ฉ์ ์ด๋ฅผ ํตํด์๋ง ๋ฐ์ํฉ๋๋ค.
๊ทธ๋ฌ๋ ์ต์ด์ ๊ณต๊ฐ ์ ๋์ค ๋ฒ์ ์์๋ (ptrace
.
ptrace๋ ์ฃผ๋ก ๋ํํ ๋๋ฒ๊ฑฐ๋ฅผ ์ํด ๊ฐ๋ฐ๋์์ผ๋ 80๋
๋ ๋ง(์์
ํ ์๋)์
trace
ํ์์์. ๋ณต์ ํ๊ณผ ์๋ณธ ๋ชจ๋ SunOS์ฉ์ผ๋ก ์ ์๋์์ง๋ง 1994๋
์๋ strace
System V, Solaris ๋ฐ ์ ์ ๋ ์ธ๊ธฐ๋ฅผ ์ป๊ณ ์๋ Linux๋ก ํฌํ
๋์์ต๋๋ค.
ํ์ฌ strace๋ Linux๋ง ์ง์ํ๋ฉฐ ๋์ผํ Linux์ ์์กดํฉ๋๋ค. ptrace
, ๋ง์ ํ์ฅ์ผ๋ก ์๋๋ค.
ํ๋์ ์ด๊ณ ๋งค์ฐ ํ๋์ ์ธ ๊ด๋ฆฌ์ strace
-
Linux, FreeBSD, OpenBSD ๋ฐ ๊ธฐ์กด Unix์์์ ์ค๋ ์ญ์ฌ์ ๊ตฌํ์๋ ๋ถ๊ตฌํ๊ณ ptrace ์์คํ ํธ์ถ ๋ฐ ์ถ์ ํ๋ก๊ทธ๋จ์ด POSIX์ ํฌํจ๋์ง ์์๋ค๋ ์ ๋ ์ค์ํฉ๋๋ค.
๊ฐ๋จํ ๋งํด์ Strace ์ฅ์น: Piglet Trace
"๋น์ ์ ์ด๊ฒ์ ์ดํดํ์ง ๋ชปํ ๊ฒ์ผ๋ก ์์๋ฉ๋๋ค"(Dennis Ritchie, ๋ฒ์ 6 Unix ์์ค ์ฝ๋์ ์ฃผ์)
์ด๋ฆฐ ์์ ๋ถํฐ ๋๋ ๋ธ๋ ๋ฐ์ค๋ฅผ ์ฐธ์ ์ ์์์ต๋๋ค. ์ฅ๋๊ฐ์ ๊ฐ์ง๊ณ ๋์ง๋ ์์์ง๋ง ๊ทธ ๊ตฌ์กฐ๋ฅผ ์ดํดํ๋ ค๊ณ ๋ ธ๋ ฅํ์ต๋๋ค (์ฑ์ธ๋ค์ "๋ถ์์ก๋ค"๋ผ๋ ๋จ์ด๋ฅผ ์ฌ์ฉํ์ง๋ง ์ฌ์ ํ ๋ฐฉ์ธ์ ๋ฏฟ์ง ์์์ต๋๋ค). ์๋ง๋ ์ด๊ฒ์ด ์ต์ด์ 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
parent๋ ์์คํ
ํธ์ถ์ด ์คํ๋๊ธฐ ์ ์ด๋ ์๋ฃ๋ ์งํ์ ์๋ฃ๋ฉ๋๋ค. ๋ ํธ์ถ ์ฌ์ด์์ ํธ์ถ์ ๋์ฒด ํธ์ถ๋ก ๋์ฒดํ๊ฑฐ๋ ์ธ์ ๋๋ ๋ฐํ ๊ฐ์ ๋ณ๊ฒฝํ๋ ๋ฑ ๋ชจ๋ ์์
์ ์ํํ ์ ์์ต๋๋ค.
๋ช
๋ น์ ๋ ๋ฒ๋ง ํธ์ถํ๋ฉด ๋ฉ๋๋ค. ptrace(PTRACE_GETREGS)
๋ ์ง์คํฐ ์ํ๋ฅผ ์ป์ผ๋ ค๋ฉด rax
ํธ์ถ ์ (์์คํ
ํธ์ถ ๋ฒํธ) ๋ฐ ์งํ(๋ฐํ ๊ฐ).
์ค์ ๋ก ์ฃผ๊ธฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
/* A system call tracing loop, one interation per call. */
for (;;) {
/* A non-portable structure defined for ptrace/GDB/strace usage mostly.
* It allows to conveniently dump and access register state using
* ptrace. */
struct user_regs_struct registers;
/* Enter syscall: continue execution until the next system call
* beginning. Stop right before syscall.
*
* It's possible to change the system call number, system call
* arguments, return value or even avoid executing the system call
* completely. */
if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL) == -1)
err(EXIT_FAILURE, "enter_syscall");
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "enter_syscall -> waitpid");
/* According to the x86-64 system call convention on Linux (see man 2
* syscall) the number identifying a syscall should be put into the rax
* general purpose register, with the rest of the arguments residing in
* other general purpose registers (rdi,rsi, rdx, r10, r8, r9). */
if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®isters) == -1)
err(EXIT_FAILURE, "enter_syscall -> getregs");
/* Note how orig_rax is used here. That's because on x86-64 rax is used
* both for executing a syscall, and returning a value from it. To
* differentiate between the cases both rax and orig_rax are updated on
* syscall entry/exit, and only rax is updated on exit. */
print_syscall_enter(registers.orig_rax);
/* Exit syscall: execute of the syscall, and stop on system
* call exit.
*
* More system call tinkering possible: change the return value, record
* time it took to finish the system call, etc. */
if (ptrace(PTRACE_SYSCALL, child_pid, NULL, NULL) == -1)
err(EXIT_FAILURE, "exit_syscall");
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "exit_syscall -> waitpid");
/* Retrieve register state again as we want to inspect system call
* return value. */
if (ptrace(PTRACE_GETREGS, child_pid, NULL, ®isters) == -1) {
/* ESRCH is returned when a child terminates using a syscall and no
* return value is possible, e.g. as a result of exit(2). */
if (errno == ESRCH) {
fprintf(stderr, "nTracee terminatedn");
break;
}
err(EXIT_FAILURE, "exit_syscall -> getregs");
}
/* Done with this system call, let the next iteration handle the next
* one */
print_syscall_exit(registers.rax);
}
๊ทธ๊ฒ์ด ์ ์ฒด ์ถ์ ์์
๋๋ค. ์ด์ ๋ค์ ํฌํ
์ ์์ํ ์์น๋ฅผ ์์์ต๋๋ค.
๊ธฐ๋ณธ ์ฌํญ: strace๋ฅผ ์คํํ๋ ํ๋ก๊ทธ๋จ ์คํ
์ฒซ ๋ฒ์งธ ์ฌ์ฉ ์ฌ๋ก๋ก strace
, ์๋ง๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์ธ์ฉํ ๊ฐ์น๊ฐ ์์ ๊ฒ์
๋๋ค. ์คํ ์ค์ธ ์ ํ๋ฆฌ์ผ์ด์
์ ์์ํ๋ ๊ฒ์
๋๋ค. strace
.
์ผ๋ฐ์ ์ธ ํ๋ก๊ทธ๋จ์ ๋์๋ ํธ์ถ ๋ชฉ๋ก์ ์กฐ์ฌํ์ง ์๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด ์์ฑํฉ๋๋ค. write
:
int main(int argc, char *argv[])
{
char str[] = "write me to stdoutn";
/* write(2) is a simple wrapper around a syscall so it should be easy to
* find in the syscall trace. */
if (sizeof(str) != write(STDOUT_FILENO, str, sizeof(str))){
perror("write");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
ํ๋ก๊ทธ๋จ์ ๋น๋ํ๊ณ ์๋ํ๋์ง ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
$ gcc examples/write-simple.c -o write-simple
$ ./write-simple
write me to stdout
๋ง์ง๋ง์ผ๋ก strace ์ ์ด ํ์์ ์คํํด ๋ณด๊ฒ ์ต๋๋ค.
$ strace ./write-simple
pexecve("./write", ["./write"], 0x7ffebd6145b0 /* 71 vars */) = 0
brk(NULL) = 0x55ff5489e000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=197410, ...}) = 0
mmap(NULL, 197410, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7a2a633000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF21133>1260342"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7a2a631000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7a2a04c000
mprotect(0x7f7a2a233000, 2097152, PROT_NONE) = 0
mmap(0x7f7a2a433000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f7a2a433000
mmap(0x7f7a2a439000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7a2a439000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f7a2a6324c0) = 0
mprotect(0x7f7a2a433000, 16384, PROT_READ) = 0
mprotect(0x55ff52b52000, 4096, PROT_READ) = 0
mprotect(0x7f7a2a664000, 4096, PROT_READ) = 0
munmap(0x7f7a2a633000, 197410) = 0
write(1, "write me to stdoutn", 20write me to stdout
) = 20
exit_group(0) = ?
๋งค์ฐ "๋ง์ด ๋ง๊ณ " ๋ณ๋ก ๊ต์ก์ ์ด์ง ์์ต๋๋ค. ์ฌ๊ธฐ์๋ ๋ ๊ฐ์ง ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ํ๋ก๊ทธ๋จ ์ถ๋ ฅ์ด ์ถ๋ ฅ๊ณผ ํผํฉ๋ฉ๋๋ค. strace
๊ทธ๋ฆฌ๊ณ ์ฐ๋ฆฌ์๊ฒ ๊ด์ฌ์ด ์๋ ํ๋ถํ ์์คํ
ํธ์ถ์ด ์์ต๋๋ค.
์์คํ ํธ์ถ ๋ชฉ๋ก์ ์ธ์ ํ์ผ๋ก ๋ฆฌ๋๋ ์ ํ๋ -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
- ๋ชจ๋ ์ ๋์ค์ ๊ธฐ์ด. ๊ฐ๋จํ "๋ฒ์"์ ์๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก์ธ์ค ํธ๋ฆฌ์์ 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;
}
๋ ๊ฐ์ง ์ฐ๊ธฐ ํธ์ถ์ ๋ชจ๋ ์ถ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
$ 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
, perror ๋ค์ ์จ๊ฒจ์ง ํธ์ถ์ ํฌํจํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ํธ์ถ์ ๋ํด์๋ง ์ค๋ฅ๋ฅผ ๋ฐํํ๋ ๊ฒ์ด ํฉ๋ฆฌ์ ์
๋๋ค.
$ strace -e trace=write -e inject=write:error=EBADF:when=1 -owrite-twice.log ./write-twice
write: Bad file descriptor
$ cat write-twice.log
write(1, "write me 1n", 12) = -1 EBADF (Bad file descriptor) (INJECTED)
write(3, "write: Bad file descriptorn", 27) = 27
+++ exited with 1 +++
์๋๋ฉด ๋ ๋ฒ์งธ ๊ฒ:
$ strace -e trace=write -e inject=write:error=EBADF:when=2 -owrite-twice.log ./write-twice
write me 1
write: Bad file descriptor
$ cat write-twice.log
write(1, "write me 1n", 12) = 12
write(1, "write me 2n", 12) = -1 EBADF (Bad file descriptor) (INJECTED)
write(3, "write: Bad file descriptorn", 27) = 27
+++ exited with 1 +++
์ค๋ฅ ์ ํ์ ์ง์ ํ ํ์๋ ์์ต๋๋ค.
$ strace -e trace=write -e fault=write:when=1 -owrite-twice.log ./write-twice
$ cat write-twice.log
write(1, "write me 1n", 12) = -1 ENOSYS (Function not implemented) (INJECTED)
write(3, "write: Function not implementedn", 32) = 32
+++ exited with 1 +++
๋ค๋ฅธ ํ๋๊ทธ์ ํจ๊ป ์ฌ์ฉํ๋ฉด ํน์ ํ์ผ์ ๋ํ ์ก์ธ์ค๋ฅผ "์ค๋จ"ํ ์ ์์ต๋๋ค. ์:
$ strace -y -P/tmp/test_file.log -e inject=file:error=ENOENT -o write-file.log ./write-file /tmp/test_file.log
open: No such file or directory
$ cat write-file.log
openat(AT_FDCWD, "/tmp/test_file.log", O_WRONLY|O_APPEND) = -1 ENOENT (No such file or directory) (INJECTED)
+++ exited with 1 +++
์ค๋ฅ ์ฃผ์
์ธ์๋
์ฌํ
๊ณต์ต ์ฌ์
strace
- ๊ฐ๋จํ๊ณ ์์ ์ ์ธ ๋๊ตฌ์
๋๋ค. ๊ทธ๋ฌ๋ ์์คํ
ํธ์ถ ์ธ์๋ ํ๋ก๊ทธ๋จ ์๋ ๋ฐ ์ด์ ์ฒด์ ์ ๋ค๋ฅธ ์ธก๋ฉด๋ ๋๋ฒ๊น
ํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด ๋์ ์ผ๋ก ์ฐ๊ฒฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํ ํธ์ถ์ ์ถ์ ํ ์ ์์ต๋๋ค. strace
- ๋ด ํ๋ก๊ทธ๋จ๊ณผ ๋ค๋ฅธ ์ฌ๋์ ํ๋ก๊ทธ๋จ์ ๋ฌธ์ ๊ฐ ์์ ๊ฒฝ์ฐ์ ์ฒซ ๋ฒ์งธ ๋ฐฉ์ด์ ์ด๋ฉฐ ์ผ์ฃผ์ผ์ ์ ์ด๋ ๋ ๋ฒ ์ด์ ์ฌ์ฉํฉ๋๋ค.
๊ฐ๋จํ ๋งํด์, Unix๋ฅผ ์ข์ํ๋ค๋ฉด ์ด ์ฑ
์ ์ฝ์ด๋ณด์ธ์. man 1 strace
๊ทธ๋ฆฌ๊ณ ์์ ๋กญ๊ฒ ํ๋ก๊ทธ๋จ์ ์ฟ๋ณด์ธ์!
์ถ์ฒ : habr.com