Unix ã®ãããªãªãã¬ãŒãã£ã³ã° ã·ã¹ãã ã§ã¯ãããã°ã©ã ãšãªãã¬ãŒãã£ã³ã° ã·ã¹ãã ãšã®å€éšã®éä¿¡ã¯ãã·ã¹ãã ã³ãŒã«ãšããå°ããªé¢æ°ã»ãããéããŠè¡ãããŸãã ããã¯ããããã°ç®çã§ãããã»ã¹ã«ãã£ãŠå®è¡ãããŠããã·ã¹ãã ã³ãŒã«ãã¹ãã€ããããšã圹ç«ã€å¯èœæ§ãããããšãæå³ããŸãã
Linux äžã®ããã°ã©ã ã®ãå¯æ¥ãªç掻ããç£èŠããã®ã«åœ¹ç«ã€ãŠãŒãã£ãªã㣠strace
ãããããã®èšäºã®äž»é¡ã§ãã ã¹ãã€æ©åšã®äœ¿çšäŸã«ã¯ç°¡åãªæŽå²ãæ·»ããããŠããŸã strace
ããã³ãã®ãããªããã°ã©ã ã®èšèšã®èª¬æã
ããŒãžå 容
çš®ã®èµ·æº Strace ããã€ã¹ã®æŠèŠ: Piglet Trace åºæ¬: ããã°ã©ã ã®å®è¡ strace ã®å®è¡ åºæ¬: ãªã³ã¶ãã©ã€ã§ããã»ã¹ã«åå ãã äŸ: åããã»ã¹ã®è¿œè·¡ äŸ: ãã³ãã«ã®ä»£ããã«ãã¡ã€ã«ãã¹ äŸ: ãã¡ã€ã«ã¢ã¯ã»ã¹è¿œè·¡ äŸ: ãã«ãã¹ã¬ããããã°ã©ã ãã¹ã¿ãŒã¯ã©ã¹ïŒã·ã¹ãã ã³ãŒã«æã®ããã»ã¹ã¹ã¿ã㯠ãã¹ã¿ãŒã¯ã©ã¹: ãšã©ãŒæ¿å ¥ åŸæžã
çš®ã®èµ·æº
Unix ã«ãããããã°ã©ã ãš OS ã«ãŒãã«éã®äž»ãªã€ã³ã¿ãŒãã§ã€ã¹ã¯ã·ã¹ãã ã³ãŒã«ã§ãã ã·ã¹ãã ã³ãŒã«, ã·ã¹ãã ã³ãŒã«)ãããã°ã©ã ãšå€éšäžçãšã®çžäºäœçšã¯ãããã°ã©ã ãéããŠã®ã¿è¡ãããŸãã
ããããUnix ã®æåã®å
¬éããŒãžã§ã³ã§ã¯ (ptrace
.
ptrace ã¯äž»ã«å¯Ÿè©±åãããã¬çšã«éçºãããŸãããã80 幎代ã®çµãããŸã§ã« (åçšã®æ代ã«ã¯)
trace
ãµã³ããã ã¯ããŒã³ãšãªãªãžãã«ã¯ã©ã¡ãã SunOS åãã§ãããã1994 幎ãŸã§ã« strace
System VãSolarisããããŠäººæ°ãé«ãŸã£ãŠãã Linux ã«ç§»æ€ãããŸããã
çŸåšãstrace 㯠Linux ã®ã¿ããµããŒãããŠãããåããã®ã«äŸåããŠããŸãã ptrace
ãå€ãã®æ¡åŒµåãçãèã£ãŠããŸãã
ææ°ã® (ãããŠéåžžã«ã¢ã¯ãã£ããª) ã¡ã³ãã strace
-
ãŸããptrace ã·ã¹ãã ã³ãŒã«ãšãã¬ãŒãµã¯ãLinuxãFreeBSDãOpenBSDãããã³åŸæ¥ã® Unix ã§ã®é·ãæŽå²ãšå®è£ ã«ãããããããPOSIX ã«ã¯æ±ºããŠå«ãŸããŠããªãã£ãããšãéèŠã§ãã
Strace ããã€ã¹ã®æŠèŠ: Piglet Trace
ããããç解ããããšã¯æåŸ ãããŠããŸããã (ããã¹ã»ãªãããŒãããŒãžã§ã³ 6 Unix ãœãŒã¹ ã³ãŒãã®ã³ã¡ã³ã)
幌ãé ãããç§ã¯ãã©ãã¯ããã¯ã¹ãå«ãã§ãããããã¡ãã§éã¶ããšã¯ããŸããã§ãããããã®æ§é ãç解ããããšããŸããïŒå€§äººã¯ãå£ããããšããèšèã䜿ããŸããããéªæªãªèšèã¯ä¿¡ããŠããŸããïŒã ããããããããæåã® Unix ã®éå ¬åŒæåãšçŸä»£ã®ãªãŒãã³ãœãŒã¹éåãç§ã«ãšã£ãŠéåžžã«èº«è¿ãªçç±ã§ãã
ãã®èšäºã®ç®çã®ããã«ãæ°å幎ã«ããã£ãŠæé·ãã strace ã®ãœãŒã¹ ã³ãŒããéã¢ã»ã³ãã«ããã®ã¯ç¡çããããŸãã ããããèªè
ã«ç§å¯ãæ®ãã¹ãã§ã¯ãããŸããã ãããã£ãŠããã®ãã㪠strace ããã°ã©ã ã®åäœåçã瀺ãããã«ãå°åãã¬ãŒãµãŒã®ã³ãŒããæäŸããŸãã
$ gcc examples/piglet-trace.c -o ptr
$ ptr echo test > /dev/null
BRK(12) -> 94744690540544
ACCESS(21) -> 18446744073709551614
ACCESS(21) -> 18446744073709551614
unknown(257) -> 3
FSTAT(5) -> 0
MMAP(9) -> 140694657216512
CLOSE(3) -> 0
ACCESS(21) -> 18446744073709551614
unknown(257) -> 3
READ(0) -> 832
FSTAT(5) -> 0
MMAP(9) -> 140694657208320
MMAP(9) -> 140694650953728
MPROTECT(10) -> 0
MMAP(9) -> 140694655045632
MMAP(9) -> 140694655070208
CLOSE(3) -> 0
unknown(158) -> 0
MPROTECT(10) -> 0
MPROTECT(10) -> 0
MPROTECT(10) -> 0
MUNMAP(11) -> 0
BRK(12) -> 94744690540544
BRK(12) -> 94744690675712
unknown(257) -> 3
FSTAT(5) -> 0
MMAP(9) -> 140694646390784
CLOSE(3) -> 0
FSTAT(5) -> 0
IOCTL(16) -> 18446744073709551591
WRITE(1) -> 5
CLOSE(3) -> 0
CLOSE(3) -> 0
unknown(231)
Tracee terminated
Piglet Trace ã¯ãçŽæ°çŸã® Linux ã·ã¹ãã ã³ãŒã«ãèªèããŸã (ã.
ã¯ããŒã³ã®åäœãèŠãŠã¿ãŸãããã Linux ã®å Žåããããã¬ãšãã¬ãŒãµã¯ãåè¿°ã®ããã« ptrace ã·ã¹ãã ã³ãŒã«ã䜿çšããŸãã ããã¯ãæåã®åŒæ°ã«ã³ãã³ãèå¥åãæž¡ãããšã«ãã£ãŠæ©èœããŸããã³ãã³ãèå¥åã®ã¿ãå¿
èŠã§ãã PTRACE_TRACEME
, PTRACE_SYSCALL
О PTRACE_GETREGS
.
ãã¬ãŒãµã¯éåžžã® Unix ã¹ã¿ã€ã«ã§éå§ããŸãã fork(2)
åããã»ã¹ãèµ·åãã次ã«äœ¿çšãã exec(3)
ç 究äžã®ããã°ã©ã ãèµ·åããŸãã ããã§ã®å¯äžã®åŸ®åŠãªç¹ã¯ææŠã§ã ptrace(PTRACE_TRACEME)
пеÑеЎ exec
: åããã»ã¹ã¯ã芪ããã»ã¹ããããç£èŠããããšãæåŸ
ããŸãã
pid_t child_pid = fork();
switch (child_pid) {
case -1:
err(EXIT_FAILURE, "fork");
case 0:
/* Child here */
/* A traced mode has to be enabled. A parent will have to wait(2) for it
* to happen. */
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
/* Replace itself with a program to be run. */
execvp(argv[1], argv + 1);
err(EXIT_FAILURE, "exec");
}
芪ããã»ã¹ã¯ããã§åŒã³åºãå¿
èŠããããŸã wait(2)
åããã»ã¹å
ã§ãã€ãŸãããã¬ãŒã¹ ã¢ãŒããžã®åãæ¿ããè¡ãããããšã確èªããŸãã
/* Parent */
/* First we wait for the child to set the traced mode (see
* ptrace(PTRACE_TRACEME) above) */
if (waitpid(child_pid, NULL, 0) == -1)
err(EXIT_FAILURE, "traceme -> waitpid");
ãã®æç¹ã§æºåã¯å®äºããç¡éã«ãŒãã§ã·ã¹ãã ã³ãŒã«ã®è¿œè·¡ã«çŽæ¥é²ãããšãã§ããŸãã
ææŠ ptrace(PTRACE_SYSCALL)
ãã®åŸã®ããšãä¿èšŒããŸã wait
芪ã¯ãã·ã¹ãã ã³ãŒã«ãå®è¡ãããåããŸãã¯ã·ã¹ãã ã³ãŒã«ãå®äºããçŽåŸã«å®äºããŸãã XNUMX ã€ã®åŒã³åºãã®éã§ã¯ãåŒã³åºããå¥ã®åŒã³åºãã«çœ®ãæããããåŒæ°ãæ»ãå€ãå€æŽããããããªã©ãããããã¢ã¯ã·ã§ã³ãå®è¡ã§ããŸãã
ã³ãã³ãã XNUMX ååŒã³åºãã ãã§ã 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) = ?
éåžžã«ããã©ããããããŸãæè²çã§ã¯ãããŸããã ããã«ã¯ XNUMX ã€ã®åé¡ããããŸããããã°ã©ã ã®åºåãåºåãšæ··åãããããšã§ãã strace
ãããŠç§ãã¡ã«ã¯èå³ã®ãªãã·ã¹ãã ã³ãŒã«ããããããããŸãã
-o ã¹ã€ããã䜿çšãããšãããã°ã©ã ã®æšæºåºåã¹ããªãŒã ãš 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 +++
é€å€ãããåŒã³åºãã®ãªã¹ãã«ãããšã¹ã±ãŒããããæå笊ã«æ³šæããŠãã ãããããã¯ã³ãã³ã ã·ã§ã«ã§å¿ èŠã§ãã shell).
ç§ã®ããŒãžã§ã³ã® 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
ã XNUMX ã€ã®ããšãæããã«ãªããŸããã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 +++
äŸ: ãã¡ã€ã«ã¢ã¯ã»ã¹è¿œè·¡
ãã XNUMX ã€ã®äŸ¿å©ãªæ©èœ: ç¹å®ã®ãã¡ã€ã«ã«é¢é£ä»ããããã·ã¹ãã ã³ãŒã«ã®ã¿ã衚瀺ããŸãã 次
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 ã¹ã¬ããæšæºã®å®è£ ãšããæå³ã§ã®ã¹ã¬ããèå¥åã®ããšã§ã¯ãªãã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 +++
ãã¹ã¿ãŒã¯ã©ã¹: ãšã©ãŒæ¿å ¥
ãããŠããã XNUMX ã€ã®éåžžã«äŸ¿å©ãªæ°æ©èœããšã©ãŒ ã€ã³ãžã§ã¯ã·ã§ã³ã§ãã ãã
#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 +++
ãŸã㯠XNUMX çªç®:
$ 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