Strace hauv Linux: keeb kwm, tsim thiab siv

Strace hauv Linux: keeb kwm, tsim thiab siv

Nyob rau hauv Unix-zoo li kev khiav hauj lwm systems, ib qho kev pab cuam kev sib txuas lus nrog lub ntiaj teb sab nraud thiab cov kev khiav hauj lwm qhov system tshwm sim los ntawm ib tug me me lub luag hauj lwm - system hu. Qhov no txhais tau hais tias rau kev debugging lub hom phiaj nws tuaj yeem pab tau rau cov neeg soj xyuas ntawm kev hu xov tooj raug tua los ntawm cov txheej txheem.

Ib qho txiaj ntsig pab koj saib xyuas "lub neej zoo" ntawm cov kev pab cuam ntawm Linux strace, uas yog lub ntsiab lus ntawm kab lus no. Piv txwv ntawm kev siv cov cuab yeej neeg soj xyuas yog nrog los ntawm keeb kwm luv luv strace thiab piav qhia txog kev tsim cov kev pab cuam zoo li no.

Txheem

Keeb kwm ntawm hom

Lub ntsiab interface ntawm cov kev pab cuam thiab OS kernel hauv Unix yog kev hu xov tooj. hu xovtooj, syscalls), kev sib cuam tshuam ntawm cov kev pab cuam nrog lub ntiaj teb sab nraud tshwm sim tshwj xeeb los ntawm lawv.

Tab sis nyob rau hauv thawj pej xeem version ntawm Unix (Version 6 Unix, 1975) tsis muaj txoj hauv kev yooj yim los taug qab tus cwj pwm ntawm cov neeg siv cov txheej txheem. Txhawm rau daws qhov teeb meem no, Tswb Labs yuav hloov kho mus rau lwm qhov version (Version 7 Unix, 1979) tau thov ib qho kev hu xov tooj tshiab - ptrace.

ptrace tau tsim feem ntau rau kev sib tham debuggers, tab sis thaum kawg ntawm 80s (nyob rau hauv lub era ntawm kev lag luam. System V tso 4) ntawm lub hauv paus no, nqaim tsom debuggers-system hu tracers-pom thiab tau siv dav.

Ua Ntej tib cov qauv ntawm txoj hlua tau luam tawm los ntawm Paul Cronenburg ntawm daim ntawv teev npe hauv comp.sources.sun hauv 1992 raws li lwm txoj hauv kev siv hluav taws xob kaw. trace los ntawm Sun. Ob lub clone thiab tus thawj yog npaj rau SunOS, tab sis los ntawm 1994 strace tau xa mus rau System V, Solaris thiab Linux nrov zuj zus.

Niaj hnub no strace tsuas yog txhawb nqa Linux thiab tso siab rau tib yam ptrace, overgrown nrog ntau extensions.

Niaj hnub nimno (thiab nquag heev) tus saib xyuas strace - Dmitry Levin. Ua tsaug rau nws, cov nqi hluav taws xob tau txais cov yam ntxwv zoo xws li kev txhaj tshuaj yuam kev rau hauv kev hu xov tooj, kev txhawb nqa rau ntau yam kev tsim vaj tsev thiab, qhov tseem ceeb tshaj plaws, mascot. Cov ntaub ntawv tsis raug cai hais tias qhov kev xaiv poob rau ntawm tus noog vim yog kev sib raug zoo ntawm lo lus Lavxias "ostrich" thiab lo lus Askiv "strace".

Nws tseem yog ib qho tseem ceeb uas lub ptrace system hu thiab tracers yeej tsis suav nrog POSIX, txawm tias muaj keeb kwm ntev thiab kev siv hauv Linux, FreeBSD, OpenBSD thiab ib txwm siv Unix.

Strace ntaus ntawv nyob rau hauv ib tug nutshell: Piglet Trace

"Koj tsis xav kom nkag siab qhov no" (Dennis Ritchie, tawm tswv yim hauv Version 6 Unix qhov chaws)

Txij li thaum yau, kuv tsis tuaj yeem sawv ntsug lub thawv dub: Kuv tsis tau ua si nrog cov khoom ua si, tab sis sim nkag siab txog lawv cov qauv (cov laus siv lo lus "tawg," tab sis tsis txhob ntseeg tus nplaig phem). Tej zaum qhov no yog vim li cas cov kab lis kev cai tsis raug cai ntawm thawj Unix thiab cov kev hloov pauv niaj hnub no tau nyob ze rau kuv.

Rau lub hom phiaj ntawm tsab xov xwm no, nws yog tsis tsim nyog rau disassemble lub hauv paus code ntawm strace, uas tau loj hlob ntau xyoo lawm. Tab sis yuav tsum tsis muaj secrets tshuav rau cov nyeem. Yog li ntawd, los qhia txog lub hauv paus ntsiab lus ntawm kev ua haujlwm ntawm cov kev pab cuam zoo li no, kuv yuav muab cov cai rau tus neeg taug qab me me - Piglet Trace (ptr). Nws tsis paub yuav ua li cas tshwj xeeb, tab sis qhov tseem ceeb tshaj plaws yog lub kaw lus hu ntawm qhov kev pab cuam - nws outputs:

$ 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 paub txog ntau pua ntawm Linux kev hu xov tooj (saib. rooj) thiab tsuas yog ua haujlwm ntawm x86-64 architecture. Qhov no txaus rau kev kawm.

Cia peb saib cov haujlwm ntawm peb cov clone. Nyob rau hauv rooj plaub ntawm Linux, debuggers thiab tracers siv, raws li tau hais los saum toj no, ptrace system hu. Nws ua haujlwm los ntawm kev dhau ntawm thawj qhov kev sib cav ntawm cov lus txib tus cim, uas peb tsuas yog xav tau PTRACE_TRACEME, PTRACE_SYSCALL ΠΈ PTRACE_GETREGS.

Tus taug qab pib nyob rau hauv ib txwm Unix style: fork(2) launches ib tug me nyuam txheej txheem, uas nyob rau hauv lem siv exec(3) launches qhov kev pab cuam nyob rau hauv kev kawm. Qhov tsuas yog subtlety ntawm no yog qhov kev sib tw ptrace(PTRACE_TRACEME) nkag exec: Cov txheej txheem me nyuam xav kom cov txheej txheem niam txiv saib xyuas nws:

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");
}

Cov txheej txheem niam txiv yuav tsum hu tam sim no wait(2) nyob rau hauv tus txheej txheem menyuam yaus, uas yog, xyuas kom meej tias kev hloov mus rau hom kab mob tau tshwm sim:

/* 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");

Lub sijhawm no, cov kev npaj ua tiav thiab koj tuaj yeem mus ncaj qha mus rau qhov kev hu xov tooj hauv lub voj voog tsis kawg.

Hu ptrace(PTRACE_SYSCALL) lav tias tom qab ntawd wait niam txiv yuav ua kom tiav ua ntej qhov kev hu xov tooj raug tua lossis tam sim ntawd tom qab ua tiav. Nruab nrab ntawm ob hu koj tuaj yeem ua ib qho kev nqis tes ua: hloov kev hu nrog lwm qhov, hloov cov lus sib cav lossis tus nqi rov qab.

Peb tsuas yog xav tau hu rau cov lus txib ob zaug ptrace(PTRACE_GETREGS)kom tau lub xeev register rax ua ntej hu (system hu xov tooj) thiab tam sim ntawd tom qab (rov qab tus nqi).

Qhov tseeb, lub voj voog:

/* 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);
}

Qhov ntawd yog tag nrho cov tracer. Tam sim no koj paub qhov twg yuav pib qhov chaw nres nkoj tom ntej DTrace ntawm Linux.

Basics: khiav ib qhov kev pab cuam khiav strace

Raws li kev siv thawj zaug strace, tej zaum nws tsim nyog hais txog txoj kev yooj yim tshaj plaws - tso tawm daim ntawv thov khiav strace.

Yuav kom tsis txhob delve rau hauv cov npe tsis kawg ntawm kev hu xov tooj ntawm ib qho kev pab cuam, peb sau yam tsawg kawg nkaus ib ncig 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;
}

Cia peb tsim qhov program thiab xyuas kom nws ua haujlwm:

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

Thiab thaum kawg, cia peb khiav nws nyob rau hauv txoj kev tswj:

$ 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)                           = ?

Heev "wordy" thiab tsis heev kev kawm. Muaj ob qhov teeb meem ntawm no: qhov kev pab cuam tso zis yog tov nrog cov zis strace thiab ntau qhov kev hu xov tooj uas tsis txaus siab rau peb.

Koj tuaj yeem cais qhov kev pab cuam tus qauv tso tawm cov kwj tawm thiab cov kev ua yuam kev yuam kev uas siv cov -o hloov, uas hloov cov npe ntawm cov kab ke hu mus rau cov ntaub ntawv sib cav.

Nws tseem yuav daws tau qhov teeb meem ntawm "ntxiv" hu. Cia peb xav tias peb tsuas yog txaus siab hu write. Ntsiab -e tso cai rau koj los qhia meej cov kab lus los ntawm qhov kev hu xov tooj yuav raug lim. Qhov kev xaiv zoo tshaj plaws yog, ib txwm, trace=*, uas koj tuaj yeem tawm tsuas yog hu xov tooj uas txaus siab rau peb.

Thaum siv ib txhij -o ΠΈ -e peb yuav tau:

$ 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 +++

Yog li, koj pom, nws yooj yim dua los nyeem.

Koj tuaj yeem tshem tawm kev hu xov tooj - piv txwv li, cov cuam tshuam nrog faib thiab tso lub cim xeeb:

$ 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 +++

Nco ntsoov lub cim exclamation dim nyob rau hauv daim ntawv teev npe hu tsis suav: qhov no yuav tsum tau los ntawm lub plhaub hais kom ua. plhaub).

Hauv kuv qhov version ntawm glibc, qhov kev hu xov tooj kaw qhov txheej txheem exit_group, tsis yog ib txwm _exit. Qhov no yog qhov nyuaj ntawm kev ua haujlwm nrog kev hu xov tooj: lub interface uas tus programmer ua haujlwm tsis ncaj qha rau kev hu xov tooj. Ntxiv mus, nws hloov tsis tu ncua nyob ntawm qhov kev siv thiab platform.

Basics: koom nrog cov txheej txheem ntawm ya

Thaum xub thawj, lub ptrace system hu rau qhov uas nws tau tsim strace, tsuas yog siv tau thaum khiav qhov program hauv hom tshwj xeeb. Qhov kev txwv no tuaj yeem ua rau lub suab tsim nyog nyob rau hnub ntawm Version 6 Unix. Niaj hnub no, qhov no tsis txaus: qee zaum koj yuav tsum tshawb xyuas cov teeb meem ntawm kev ua haujlwm. Ib qho piv txwv yog ib txoj kev thaiv ntawm tus kov lossis pw tsaug zog. Yog li niaj hnub strace tuaj yeem koom nrog cov txheej txheem ntawm ya.

Freezing piv txwv cov kev pab cuam:

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;
}

Cia peb tsim qhov program thiab xyuas kom nws khov:

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

Tam sim no cia peb sim koom nrog nws:

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

Program thaiv los ntawm kev hu pause. Cia peb saib seb nws yuav ua li cas rau cov teeb liab:

$ 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 +++

Peb pib qhov kev pab cuam khov thiab koom nrog nws siv strace. Ob yam dhau los ua qhov tseeb: lub kaw lus ncua hu tsis quav ntsej cov teeb liab yam tsis muaj tus tuav thiab, qhov zoo dua, kev saib xyuas tsis yog tsuas yog hu xov tooj xwb, tab sis kuj tseem muaj cov teeb liab tawm.

Piv txwv: Taug qab Cov Txheej Txheem Me Nyuam

Ua haujlwm nrog cov txheej txheem los ntawm kev hu fork - lub hauv paus ntawm tag nrho Unixes. Cia peb saib yuav ua li cas strace ua haujlwm nrog cov txheej txheem ntoo siv qhov piv txwv ntawm "kev yug me nyuam" yooj yim cov kev pab cuam:

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);
}

Ntawm no tus txheej txheem qub tsim cov txheej txheem menyuam yaus, ob qho tib si sau rau cov qauv tsim tawm:

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

Los ntawm lub neej ntawd, peb tsuas yog pom kev hu xov tooj los ntawm cov txheej txheem niam txiv:

$ 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 +++

Tus chij pab koj taug qab tag nrho cov txheej txheem ntoo -f, qho strace saib xyuas kev hu xov tooj hauv cov txheej txheem menyuam yaus. Qhov no ntxiv rau txhua kab ntawm cov zis pid txheej txheem uas ua rau ib tug system tso zis:

$ 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 +++

Hauv cov ntsiab lus no, kev lim dej los ntawm pab pawg hu xov tooj tuaj yeem pab tau:

$ 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 +++

Los ntawm txoj kev, qhov kev hu xov tooj twg yog siv los tsim cov txheej txheem tshiab?

Piv txwv li: file paths es tsis txhob ntawm tes

Paub cov ntaub ntawv piav qhia yeej muaj txiaj ntsig zoo, tab sis cov npe ntawm cov ntaub ntawv tshwj xeeb uas cov kev pab cuam nkag mus kuj tuaj yeem ua ke.

Qhov ntxiv mus qhov kev zov me nyuam sau kab rau cov ntaub ntawv ib ntus:

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;
}

Thaum ib txwm hu strace yuav qhia tus nqi ntawm tus lej piav qhia dhau mus rau kev hu xov tooj:

$ 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 +++

Nrog tus chij -y Tus nqi hluav taws xob qhia txoj hauv kev rau cov ntaub ntawv uas tus piav qhia sib raug:

$ 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 +++

Piv txwv: Cov ntaub ntawv nkag mus

Lwm qhov muaj txiaj ntsig zoo: tso saib tsuas yog hu xov tooj cuam tshuam nrog cov ntaub ntawv tshwj xeeb. Tom ntej no qhov kev zov me nyuam appends ib kab rau cov ntaub ntawv arbitrary dhau los ua ib qho kev sib cav:

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;
}

los ntawm neej ntawd hais strace qhia ntau cov ntaub ntawv tsis tsim nyog. Chij -P nrog ib qho kev sib cav ua rau strace luam tawm tsuas yog hu mus rau cov ntaub ntawv teev tseg:

$ 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 +++

Piv txwv: Multithreaded Programs

Π’ΠΈΠ»ΠΈΡ‚Π° strace tuaj yeem pab tau thaum ua haujlwm nrog ntau txoj xov qhov kev pab cuam. Cov kev pab cuam hauv qab no sau rau cov qauv tsim tawm los ntawm ob lub kwj:

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);
}

Lawm, nws yuav tsum tau muab tso ua ke nrog tshwj xeeb txais tos rau tus linker - tus chij -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
$

Chij -f, zoo li nyob rau hauv cov txheej txheem tsis tu ncua, yuav ntxiv pid ntawm cov txheej txheem rau qhov pib ntawm txhua kab.

Lawm, peb tsis tau tham txog cov xov tooj hauv qhov kev nkag siab ntawm kev siv POSIX Threads tus qauv, tab sis hais txog tus lej siv los ntawm lub sijhawm ua haujlwm hauv Linux. Los ntawm qhov kawg ntawm qhov kev xav, tsis muaj cov txheej txheem lossis xov - muaj cov haujlwm uas yuav tsum tau muab faib rau ntawm cov cores muaj ntawm lub tshuab.

Thaum ua hauj lwm nyob rau hauv ntau threads, system hu ua ntau dhau lawm:

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

Nws ua rau kev txiav txim siab txwv koj tus kheej rau kev tswj hwm thiab kev hu xov tooj nkaus xwb 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 +++

Los ntawm txoj kev, cov lus nug. Qhov kev hu xov tooj twg yog siv los tsim cov xov tshiab? Qhov kev hu xov tooj no txawv li cas ntawm kev hu rau cov txheej txheem?

Master chav kawm: txheej txheej txheej thaum lub sijhawm hu xov tooj

Ib qho ntawm cov tsis ntev los no tau tshwm sim strace peev xwm - tso saib cov pawg ntawm kev hu xov tooj thaum lub sijhawm hu xov tooj. Yooj yim Piv txwv:

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;
}

Lawm, qhov kev pab cuam tso zis ntau voluminous, thiab, ntxiv rau tus chij -k (hu rau pawg zaub), nws ua rau kev nkag siab los lim cov npe hu los ntawm lub npe:

$ 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 +++

Master chav kawm: yuam kev txhaj tshuaj

Thiab ib qho ntxiv tshiab thiab muaj txiaj ntsig zoo heev: kev txhaj tshuaj yuam kev. Ntawm no qhov kev zov me nyuam, sau ob kab rau cov zis tso zis:

#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;
}

Cia peb taug qab ob qho tib si sau hu:

$ 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 +++

Tam sim no peb siv cov lus qhia injectlos ntxig qhov yuam kev EBADF nyob rau hauv tag nrho cov sau hu:

$ 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 +++

Nws yog nthuav dab tsi yuam kev rov qab tag nrho kev sib tw write, suav nrog kev hu xov tooj zais tom qab yuam kev. Nws tsuas yog ua rau kev txiav txim siab rov qab ua yuam kev rau thawj zaug ntawm kev hu:

$ 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 +++

Los yog qhov thib ob:

$ 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 +++

Nws tsis yog tsim nyog los qhia qhov yuam kev hom:

$ 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 +++

Ua ke nrog lwm tus chij, koj tuaj yeem "tawg" nkag mus rau cov ntaub ntawv tshwj xeeb. Piv txwv:

$ 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 +++

Dhau li ntawm kev txhaj tshuaj yuam kev, tau qhia qeeb thaum hu lossis tau txais cov cim.

Tom qab ntawd

Π’ΠΈΠ»ΠΈΡ‚Π° strace - ib qho cuab yeej yooj yim thiab txhim khu kev qha. Tab sis ntxiv nrog rau kev hu xov tooj, lwm yam ntawm kev ua haujlwm ntawm cov kev pab cuam thiab kev ua haujlwm tuaj yeem raug debugged. Piv txwv li, nws tuaj yeem taug qab hu mus rau dynamically txuas cov tsev qiv ntawv. ltrace, lawv tuaj yeem saib rau hauv kev ua haujlwm ntawm kev ua haujlwm SystemTap ΠΈ fwm, thiab tso cai rau koj los tshawb xyuas qhov kev ua haujlwm zoo zoo tag nrho. Txawm li cas los xij, nws yog strace - thawj kab ntawm kev tiv thaiv thaum muaj teeb meem nrog kuv tus kheej thiab lwm tus neeg cov kev pab cuam, thiab kuv siv nws tsawg kawg ob peb zaug hauv ib lub lis piam.

Hauv luv, yog tias koj nyiam Unix, nyeem man 1 strace thiab xav tias dawb saib ntawm koj cov kev pab cuam!

Tau qhov twg los: www.hab.com

Ntxiv ib saib