Strace ku-Linux: umlando, ukuklama nokusetshenziswa

Strace ku-Linux: umlando, ukuklama nokusetshenziswa

Kuzinhlelo zokusebenza ezifana ne-Unix, ukuxhumana kohlelo nomhlaba wangaphandle kanye nesistimu yokusebenza kwenzeka ngesethi encane yemisebenzi - izingcingo zesistimu. Lokhu kusho ukuthi ngezinjongo zokulungisa iphutha kungaba usizo ukuhlola izingcingo zesistimu ezenziwa yizinqubo.

Isisetshenziswa sikusiza ukuthi ugade "impilo eseduze" yezinhlelo ku-Linux strace, okuyisihloko salesi sihloko. Izibonelo zokusetshenziswa kwemishini yezinhloli zihambisana nomlando omfushane strace kanye nencazelo yokwakheka kwezinhlelo ezinjalo.

Okuqukethwe

Umsuka wezinhlobo

Ukuxhumana okuyinhloko phakathi kwezinhlelo kanye ne-OS kernel ku-Unix izingcingo zesistimu. izingcingo zesistimu, ama-syscalls), ukusebenzelana kwezinhlelo nomhlaba wangaphandle kwenzeka ngazo kuphela.

Kepha kunguqulo yokuqala yomphakathi ye-Unix (Inguqulo yesi-6 ye-Unix, 1975) zazingekho izindlela ezilula zokulandelela ukuziphatha kwezinqubo zabasebenzisi. Ukuze kuxazululwe le nkinga, ama-Bell Labs azothuthukela enguqulweni elandelayo (Inguqulo yesi-7 ye-Unix, 1979) uphakamise ucingo lwesistimu entsha - ptrace.

i-ptrace yenzelwe ikakhulukazi abalungisa iphutha abasebenzisanayo, kodwa ekupheleni kweminyaka yama-80s (ngenkathi yokuhweba Ukukhishwa kwesistimu V 4) ngenxa yalesi sisekelo, kwavela ama-debugger agxile kancaneβ€”ama-call call tracersβ€”futhi asetshenziswa kabanzi.

Okokuqala inguqulo efanayo ye-strace yanyatheliswa ngu-Paul Cronenburg ohlwini lwamakheli we-com.sources.sun ngo-1992 njengenye indlela yensiza evaliwe trace kusuka eLangeni. Kokubili i-clone kanye neyokuqala bekuhloselwe i-SunOS, kodwa ngo-1994 strace yathuthelwa ku-System V, uSolaris kanye neLinux eyanda ukuduma.

Namuhla i-strace isekela kuphela i-Linux futhi incike okufanayo ptrace, egcwele izandiso eziningi.

Umnakekeli wesimanje (futhi osebenza kakhulu). strace - UDmitry Levin. Ngenxa yakhe, insiza ithole izici ezithuthukile njengokujova amaphutha kumakholi wesistimu, ukusekelwa kwezinhlobonhlobo zezakhiwo futhi, okubaluleke kakhulu, umaskandi. Imithombo engekho emthethweni ithi ukukhetha kwawela intshe ngenxa yokuvumelana phakathi kwegama lesiRashiya elithi β€œostrich” nelesiNgisi elithi β€œstrace”.

Kubalulekile futhi ukuthi ucingo lwesistimu ye-ptrace nama-tracer akukaze kufakwe ku-POSIX, naphezu komlando omude nokusebenza ku-Linux, FreeBSD, OpenBSD kanye ne-Unix yendabuko.

Strace idivayisi ngamafuphi: Piglet Trace

"Akulindelekile ukuthi ukuqonde lokhu" (Dennis Ritchie, phawula ku-Version 6 ikhodi yomthombo ye-Unix)

Kusukela ngisemncane, angikwazi ukuma amabhokisi amnyama: Angizange ngidlale ngamathoyizi, kodwa ngazama ukuqonda isakhiwo sabo (abadala basebenzisa igama elithi "ukuphuka," kodwa ungakholelwa izilimi ezimbi). Mhlawumbe yingakho isiko elingakahleleki le-Unix yokuqala kanye nenhlangano yesimanje yomthombo ovulekile iseduze kakhulu nami.

Ngezinjongo zalesi sihloko, akunangqondo ukuqaqa ikhodi yomthombo ye-strace, ekhule emashumini eminyaka. Kodwa akumele kube nezimfihlo ezishiyelwa abafundi. Ngakho-ke, ukukhombisa isimiso sokusebenza kwezinhlelo ezinjalo ze-strace, ngizohlinzeka ngekhodi ye-tracer miniature - Ingulube Trace (ptr). Akwazi ukwenza noma yini ekhethekile, kodwa into eyinhloko izingcingo zesistimu zohlelo - oluphumayo:

$ 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

I-Piglet Trace ibona amakhulukhulu ezingcingo zesistimu ye-Linux (bona. itafula) futhi isebenza kuphela ku-architecture ye-x86-64. Lokhu kwanele ngezinjongo zemfundo.

Ake sibheke umsebenzi we-clone yethu. Endabeni ye-Linux, abalungisa iphutha nabalandeleli basebenzisa, njengoba kushiwo ngenhla, ucingo lwesistimu ye-ptrace. Isebenza ngokudlulisela empikiswaneni yokuqala izihlonzi zomyalo, esizidingayo kuphela PTRACE_TRACEME, PTRACE_SYSCALL ΠΈ PTRACE_GETREGS.

I-tracer iqala ngesitayela esijwayelekile se-Unix: fork(2) iqala inqubo yengane, yona esebenzisa exec(3) wethula uhlelo olusafundwayo. Okuwubuqili kuphela lapha inselele ptrace(PTRACE_TRACEME) ΠΏΠ΅Ρ€Π΅Π΄ exec: Inqubo yengane ilindele inqubo yomzali ukuyiqapha:

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

Inqubo yomzali kufanele manje ishaye ucingo wait(2) enqubweni yengane, okungukuthi, qiniseka ukuthi ukushintshela kumodi yokulandelela kwenzeke:

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

Kuleli qophelo, amalungiselelo aseqedile futhi ungaqhubekela ngqo ekulandeleleni izingcingo zesistimu ngeluphu engapheli.

Shayela ptrace(PTRACE_SYSCALL) iqinisekisa lokho okulandelayo wait umzali uzoqedela ngaphambi kokwenziwa kwekholi yesistimu noma ngokushesha ngemva kokuqeda. Phakathi kwezingcingo ezimbili ungenza noma yiziphi izenzo: shintsha ucingo ufake olunye, shintsha izimpikiswano noma inani lokubuyisela.

Sidinga nje ukubiza umyalo kabili ptrace(PTRACE_GETREGS)ukuze uthole isimo sokubhalisa rax ngaphambi kocingo (inombolo yocingo lwesistimu) futhi ngokushesha ngemva (inani lokubuyisela).

Eqinisweni, umjikelezo:

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

Yilokho umkhondo wonke. Manje uyazi ukuthi ungaqala kuphi ukuthuthwa okulandelayo I-DTrace ku-Linux.

Okuyisisekelo: ukusebenzisa uhlelo olusebenzisa i-strace

Njengecala lokuqala lokusetshenziswa strace, mhlawumbe kufanelekile ukusho indlela elula - ukwethula uhlelo lokusebenza olusebenzayo strace.

Ukuze singangeni ohlwini lwezingcingo ezingapheli zohlelo olujwayelekile, siyabhala Uhlelo oluncane nxazonke 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;
}

Asakhe uhlelo futhi siqinisekise ukuthi luyasebenza:

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

Futhi ekugcineni, masiyiqhube ngaphansi kokulawulwa kwe-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)                           = ?

"Amagama" kakhulu futhi awafundisi kakhulu. Kunezinkinga ezimbili lapha: okuphumayo kohlelo kuxutshwa nokuphumayo strace kanye nenqwaba yezingcingo zesistimu ezingasithandi.

Ungahlukanisa ukusakaza okujwayelekile okukhiphayo kohlelo kanye nokukhiphayo kwephutha usebenzisa i- -o switch, eqondisa kabusha uhlu lwamakholi wesistimu kufayela lokungqubuzana.

Kusele ukubhekana nenkinga yezingcingo "ezengeziwe". Ake sicabange ukuthi sinentshisekelo ezicingweni kuphela write. Ukhiye -e ikuvumela ukuthi ucacise izinkulumo izingcingo zesistimu zizohlungwa ngazo. Inketho yesimo edume kakhulu, ngokwemvelo, trace=*, ongashiya ngazo kuphela izingcingo esizithandayo.

Lapho isetshenziswa kanyekanye -o ΠΈ -e sizothola:

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

Ngakho, uyabona, kulula kakhulu ukufunda.

Ungaphinda ususe amakholi wesistimu, isibonelo lawo ahlobene nokwabiwa kwememori nokukhulula:

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

Qaphela uphawu lokubabaza oluphunyukile ohlwini lwamakholi angabaliwe: lokhu kudingwa igobolondo lomyalo. igobolondo).

Enguqulweni yami ye-glibc, ucingo lwesistimu lunqamula inqubo exit_group, hhayi ngokwesiko _exit. Lobu ubunzima bokusebenza ngezingcingo zesistimu: isixhumi esibonakalayo lapho umenzi wohlelo asebenza khona asihlobene ngokuqondile nezingcingo zesistimu. Ngaphezu kwalokho, ishintsha njalo ngokuya ngokuqaliswa kanye nesikhulumi.

Okuyisisekelo: ukujoyina inqubo ngokuhamba

Ekuqaleni, isistimu ye-ptrace ibiza lapho yakhiwe khona strace, ingasetshenziswa kuphela uma usebenzisa uhlelo ngemodi ekhethekile. Lo mkhawulo kungenzeka ukuthi wawuzwakala unengqondo ezinsukwini ze-Version 6 Unix. Namuhla, lokhu akusanele: ngezinye izikhathi udinga ukuphenya izinkinga zohlelo lokusebenza. Isibonelo esijwayelekile inqubo evinjwe esibambeni noma ekuleleni. Ngakho-ke yesimanje strace angajoyina izinqubo ngokundiza.

Isibonelo sokuqandisa uhlelo:

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

Asakhe uhlelo futhi siqinisekise ukuthi luqandisiwe:

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

Manje ake sizame ukuyijoyina:

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

Uhlelo luvinjwe ngekholi pause. Ake sibone ukuthi usabela kanjani kumasignali:

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

Sethule uhlelo oluqandisiwe futhi salujoyina sisebenzisa strace. Izinto ezimbili zacaca: ucingo lwesistimu yokumisa isikhashana alinaki amasignali ngaphandle kwezibambi futhi, okuthakazelisayo nakakhulu, abaqaphi be-strace abagcini nje ngokubiza uhlelo, kodwa futhi namasignali angenayo.

Isibonelo: Ukulandelela Izinqubo Zengane

Ukusebenza ngezinqubo ngocingo fork - isisekelo sawo wonke ama-Unixes. Ake sibone ukuthi i-strace isebenza kanjani nesihlahla senqubo sisebenzisa isibonelo "sokuzalela" okulula. uhlelo:

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

Lapha inqubo yasekuqaleni idala inqubo yengane, zombili zibhalela kokuphumayo okujwayelekile:

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

Ngokuzenzakalela, sizobona kuphela amakholi esistimu asuka enqubweni yomzali:

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

Ifulegi likusiza ukuthi ulandelele sonke isihlahla senqubo -f, okuthi strace uqapha izingcingo zesistimu kuzinqubo zengane. Lokhu kwengeza emugqeni ngamunye wokukhiphayo pid inqubo eyenza ukuphuma kwesistimu:

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

Kulo mongo, ukuhlunga ngeqembu lezingcingo zesistimu kungaba usizo:

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

Ngendlela, yiluphi ucingo lwesistimu olusetshenziselwa ukudala inqubo entsha?

Isibonelo: izindlela zefayela esikhundleni sezibambo

Ukwazi izichazi zefayela kuyasiza impela, kodwa amagama amafayela athile afinyelelwa wuhlelo nawo angaba usizo.

Okulandelayo uhlelo ubhala umugqa wefayela lesikhashana:

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

Ngesikhathi socingo olujwayelekile strace izobonisa inani lenombolo yesichazi edluliselwe ocingweni lwesistimu:

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

Ngefulegi -y Uhlelo lokusebenza lubonisa indlela eya efayeleni lapho isichazi sihambisana khona:

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

Isibonelo: Ukulandelela Ukufinyelela Kwefayela

Esinye isici esiwusizo: bonisa kuphela amakholi esistimu ahlotshaniswa nefayela elithile. Olandelayo uhlelo yengeza umugqa efayeleni elingenamthetho eliphasiswe njengengxabano:

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

Ngokuzenzakalelayo strace ibonisa ulwazi oluningi olungadingekile. Hlaba umkhosi -P nge-agumenti ibangela uchungechunge lokuphrinta kuphela izingcingo eziya efayeleni elishiwo:

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

Isibonelo: Izinhlelo ezinemicu eminingi

Okusetshenziswayo strace ingasiza futhi uma isebenza nge-multi-threaded uhlelo. Uhlelo olulandelayo lubhalela okukhiphayo okujwayelekile okuvela ekusakazweni okubili:

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

Ngokwemvelo, kufanele ihlanganiswe ngokubingelela okukhethekile kusixhumanisi - ifulege le-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
$

Maka umkhosi -f, njengasesimweni sezinqubo ezijwayelekile, izongeza i-pid yenqubo ekuqaleni komugqa ngamunye.

Ngokwemvelo, asikhulumi ngesihlonzi sentambo ngomqondo wokusetshenziswa kwezinga le-POSIX Threads, kodwa mayelana nenombolo esetshenziswa umhleli wemisebenzi ku-Linux. Ngokombono wakamuva, azikho izinqubo noma imicu - kunemisebenzi edinga ukusabalalisa phakathi kwama-cores atholakalayo omshini.

Uma usebenza ngochungechunge oluningi, amakholi esistimu aba maningi kakhulu:

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

Kunengqondo ukuzikhawulela ekucubunguleni ukuphathwa nezingcingo zesistimu kuphela 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 +++

Ngokwesibonelo, imibuzo. Iyiphi ikholi yesistimu esetshenziselwa ukudala uchungechunge olusha? Le kholi yochungechunge ihluke kanjani ekubizeni izinqubo?

I-Master class: cubungula isitaki ngesikhathi socingo lwesistimu

Eyodwa esanda kuvela strace amakhono - ukubonisa inqwaba yezingcingo zokusebenza ngesikhathi socingo lwesistimu. Kulula isibonelo:

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

Ngokwemvelo, okuphumayo kohlelo kuba kakhulu, futhi, ngaphezu kwefulegi -k (i-call stack display), kunengqondo ukuhlunga izingcingo zesistimu ngegama:

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

Isigaba esikhulu: umjovo wephutha

Futhi esinye isici esisha futhi esiwusizo kakhulu: umjovo wephutha. Lapha uhlelo, ukubhala imigqa emibili emfudlaneni ophumayo:

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

Masilandele zombili izingcingo ezibhalayo:

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

Manje sisebenzisa isisho injectukufaka iphutha EBADF kuzo zonke bhala izingcingo:

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

Kuyathakazelisa ukuthi yimaphi amaphutha abuyiswayo konke izinselele write, okufaka phakathi ucingo olufihlwe ngenxa yokwesaba. Kunengqondo ukubuyisela iphutha okokuqala ngqa:

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

Noma owesibili:

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

Akudingekile ukucacisa uhlobo lwephutha:

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

Ngokuhlanganiswa namanye amafulegi, ungakwazi "ukuphula" ukufinyelela kufayela elithile. Isibonelo:

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

Ngaphandle komjovo wephutha, can yethula ukubambezeleka lapho ushaya izingcingo noma wamukela amasignali.

I-Afterword

Okusetshenziswayo strace - ithuluzi elilula nelithembekile. Kodwa ngaphezu kwezingcingo zesistimu, ezinye izici zokusebenza kwezinhlelo kanye nesistimu yokusebenza zingalungiswa. Isibonelo, ingakwazi ukulandelela amakholi kumalabhulali axhumeke ngamandla. i-ltrace, bangabheka ukusebenza kwesistimu yokusebenza I-SystemTap ΠΈ i-frace, futhi ikuvumela ukuthi uphenye ngokujulile ukusebenza kohlelo i-perf. Noma kunjalo, kunjalo strace - umugqa wokuqala wokuzivikela uma kunezinkinga ngezinhlelo zami kanye nezabanye abantu, futhi ngiyisebenzisa okungenani izikhathi ezimbalwa ngesonto.

Ngamafuphi, uma uthanda i-Unix, funda man 1 strace futhi uzizwe ukhululekile ukulunguza ezinhlelweni zakho!

Source: www.habr.com

Engeza amazwana