Strace sa Linux: kasaysayan, disenyo ug paggamit

Strace sa Linux: kasaysayan, disenyo ug paggamit

Sa mga operating system nga sama sa Unix, ang komunikasyon sa usa ka programa sa gawas sa kalibutan ug ang operating system mahitabo pinaagi sa gamay nga hugpong sa mga gimbuhaton - mga tawag sa sistema. Kini nagpasabot nga alang sa mga katuyoan sa pag-debug kini mahimong mapuslanon sa pagpaniid sa mga tawag sa sistema nga gipatuman sa mga proseso.

Ang usa ka utility makatabang kanimo sa pagmonitor sa "suod nga kinabuhi" sa mga programa sa Linux strace, nga mao ang ulohan niini nga artikulo. Ang mga pananglitan sa paggamit sa mga kagamitan sa espiya giubanan sa usa ka mubo nga kasaysayan strace ug usa ka paghulagway sa disenyo sa maong mga programa.

Mga sulod

Sinugdanan sa species

Ang nag-unang interface sa taliwala sa mga programa ug ang OS kernel sa Unix mao ang mga tawag sa sistema. sistema nga tawag, syscalls), ang interaksyon sa mga programa sa gawas nga kalibutan mahitabo lamang pinaagi kanila.

Apan sa unang publikong bersyon sa Unix (Bersyon 6 Unix, 1975) walay sayon ​​nga mga paagi sa pagsubay sa kinaiya sa mga proseso sa user. Aron masulbad kini nga isyu, ang Bell Labs mag-update sa sunod nga bersyon (Bersyon 7 Unix, 1979) misugyot og bag-ong sistema sa tawag - ptrace.

ptrace kay naugmad ilabina alang sa interactive debuggers, apan sa katapusan sa 80s (sa panahon sa komersyal nga Sistema V Release 4) pinasukad niini, ang mga debugger nga makitid nga naka-focus β€” mga tigsubay sa tawag sa sistema β€” nagpakita ug kaylap nga gigamit.

Ang una ang sama nga bersyon sa strace gimantala ni Paul Cronenburg sa comp.sources.sun mailing list niadtong 1992 isip alternatibo sa closed utility trace gikan sa Sun. Ang clone ug ang orihinal gituyo alang sa SunOS, apan sa 1994 strace gi-port ngadto sa System V, Solaris ug ang mas popular nga Linux.

Karon ang strace nagsuporta ra sa Linux ug nagsalig sa parehas ptrace, napuno sa daghang mga extension.

Moderno (ug aktibo kaayo) nga tigmentinar strace - Dmitry Levin. Salamat kaniya, nakuha sa utility ang mga advanced nga bahin sama sa pag-inject sa sayup sa mga tawag sa sistema, suporta alang sa usa ka halapad nga arkitektura ug, labi ka hinungdanon, maskot. Ang dili opisyal nga mga tinubdan nag-ingon nga ang pagpili nahulog sa ostrich tungod sa consonance tali sa Russian nga pulong nga "ostrich" ug sa English nga pulong nga "strace".

Importante usab nga ang ptrace system call ug mga tracer wala gayud maapil sa POSIX, bisan pa sa taas nga kasaysayan ug pagpatuman sa Linux, FreeBSD, OpenBSD ug tradisyonal nga Unix.

Strace device sa usa ka nutshell: Piglet Trace

"Dili ka gidahom nga masabtan kini" (Dennis Ritchie, komento sa Bersyon 6 Unix source code)

Sukad sa pagkabata, dili ako makabarog sa itom nga mga kahon: Wala ako magdula sa mga dulaan, apan naningkamot nga masabtan ang ilang gambalay (gigamit sa mga hamtong ang pulong nga "nabuak," apan dili motuo sa daotang mga dila). Tingali kini ang hinungdan ngano nga ang dili pormal nga kultura sa una nga Unix ug ang modernong open-source nga kalihukan duol kaayo kanako.

Alang sa mga katuyoan niini nga artikulo, dili makatarunganon ang pag-disassemble sa source code sa strace, nga mitubo sa mga dekada. Apan kinahanglan nga wala’y mga sekreto nga nahabilin alang sa mga magbabasa. Busa, aron ipakita ang prinsipyo sa operasyon sa maong mga programa sa strace, akong ihatag ang code alang sa usa ka miniature tracer - Piglet Trace (ptr). Wala kini mahibal-an kung unsaon pagbuhat ang bisan unsang espesyal, apan ang panguna nga butang mao ang mga tawag sa sistema sa programa - kini nagpagawas:

$ 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

Giila sa Piglet Trace ang bahin sa gatusan nga mga tawag sa sistema sa Linux (tan-awa. ang lamesa) ug nagtrabaho lamang sa x86-64 nga arkitektura. Kini igo na alang sa mga katuyoan sa edukasyon.

Atong tan-awon ang trabaho sa atong clone. Sa kaso sa Linux, ang mga debugger ug mga tracer naggamit, sama sa gihisgutan sa ibabaw, ang ptrace system call. Naglihok kini pinaagi sa pagpasa sa unang argumento sa command identifiers, nga gikinahanglan lamang nato PTRACE_TRACEME, PTRACE_SYSCALL ΠΈ PTRACE_GETREGS.

Nagsugod ang tracer sa naandan nga istilo sa Unix: fork(2) naglunsad og proseso sa bata, nga sa baylo naggamit exec(3) naglunsad sa programa nga gitun-an. Ang bugtong maliputon dinhi mao ang hagit ptrace(PTRACE_TRACEME) kaniadto exec: Ang proseso sa bata nagpaabot nga ang proseso sa ginikanan mag-monitor niini:

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

Ang proseso sa ginikanan kinahanglan nga motawag wait(2) sa proseso sa bata, nga mao, siguruha nga ang pagbalhin sa mode sa pagsubay nahitabo:

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

Niini nga punto, ang mga pagpangandam kompleto ug mahimo ka nga magpadayon direkta sa pagsubay sa mga tawag sa sistema sa usa ka walay katapusan nga loop.

Hagit ptrace(PTRACE_SYSCALL) garantiya nga mosunod wait makompleto ang ginikanan sa dili pa ipatuman ang tawag sa sistema o pagkahuman pagkahuman niini. Taliwala sa duha ka tawag mahimo nimong buhaton ang bisan unsang mga aksyon: pulihan ang tawag sa usa ka alternatibo, usba ang mga argumento o ang kantidad sa pagbalik.

Kinahanglan lang namon nga tawagan ang mando sa makaduha ptrace(PTRACE_GETREGS)aron makuha ang estado sa rehistro rax sa wala pa ang tawag (numero sa tawag sa sistema) ug pagkahuman dayon (pagbalik nga kantidad).

Sa tinuud, ang siklo:

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

Mao kana ang tibuok nga tracer. Karon nahibal-an na nimo kung asa magsugod ang sunod nga porting DTrace sa Linux.

Mga sukaranan: pagpadagan sa usa ka programa nga nagdagan nga strace

Ingon usa ka una nga kaso sa paggamit strace, tingali angayan nga isulti ang pinakasimple nga paagi - paglansad sa usa ka aplikasyon nga nagdagan strace.

Aron dili masusi ang walay katapusan nga lista sa mga tawag sa usa ka tipikal nga programa, nagsulat kami minimum nga programa Π²ΠΎΠΊΡ€ΡƒΠ³ 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;
}

Atong tukuron ang programa ug siguruha nga kini molihok:

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

Ug sa katapusan, atong padaganon kini ubos sa strace control:

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

Very "wordy" ug dili kaayo educational. Adunay duha ka mga problema dinhi: ang output sa programa gisagol sa output strace ug daghang mga tawag sa sistema nga dili kami interesado.

Mahimo nimong ibulag ang standard output stream sa programa ug strace error output gamit ang -o switch, nga nag-redirect sa lista sa mga tawag sa sistema sa usa ka file sa argumento.

Nagpabilin kini sa pag-atubang sa problema sa "dugang" nga mga tawag. Atong isipon nga kita interesado lamang sa mga tawag write. yawe -e nagtugot kanimo sa pagtino sa mga ekspresyon diin ang mga tawag sa sistema masala. Ang labing popular nga kapilian sa kondisyon mao, natural, trace=*, diin mahimo nimong ibilin ang mga tawag nga makapainteres kanamo.

Kung gigamit nga dungan -o ΠΈ -e atong makuha:

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

Busa, nakita nimo, kini mas sayon ​​​​basahon.

Mahimo usab nimong tangtangon ang mga tawag sa sistema, pananglitan ang mga may kalabotan sa alokasyon sa memorya ug pagpahigawas:

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

Timan-i ang escaped exclamation mark sa listahan sa wala iapil nga mga tawag: kini gikinahanglan sa command shell. kabhang).

Sa akong bersyon sa glibc, usa ka tawag sa sistema ang nagtapos sa proseso exit_group, dili tradisyonal _exit. Kini ang kalisud sa pagtrabaho sa mga tawag sa sistema: ang interface nga gigamit sa programmer dili direktang may kalabutan sa mga tawag sa sistema. Dugang pa, kini kanunay nga nagbag-o depende sa pagpatuman ug plataporma.

Mga sukaranan: pag-apil sa proseso sa langaw

Sa sinugdan, ang ptrace system nga tawag diin kini gitukod strace, magamit ra kung nagdagan ang programa sa usa ka espesyal nga mode. Kini nga limitasyon mahimong makatarunganon sa mga adlaw sa Bersyon 6 Unix. Karong panahona, kini dili na igo: usahay kinahanglan nimo nga imbestigahan ang mga problema sa usa ka nagtrabaho nga programa. Usa ka tipikal nga pananglitan mao ang usa ka proseso nga gibabagan sa usa ka kuptanan o pagkatulog. Busa moderno strace makaapil sa mga proseso sa langaw.

Nagyelo nga pananglitan mga programa:

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

Atong tukuron ang programa ug siguroha nga kini nagyelo:

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

Karon atong sulayan ang pag-apil niini:

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

Ang programa gibabagan sa usa ka tawag pause. Atong tan-awon kon unsay iyang reaksiyon sa mga signal:

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

Gilunsad namo ang frozen nga programa ug giapil kini gamit strace. Duha ka butang ang nahimong tin-aw: ang pause system call wala magtagad sa mga signal nga walay handler ug, mas makaiikag, ang strace monitor dili lamang sa mga tawag sa sistema, kondili usab sa umaabot nga mga signal.

Pananglitan: Pagsubay sa mga Proseso sa Bata

Pagtrabaho sa mga proseso pinaagi sa usa ka tawag fork - ang sukaranan sa tanan nga Unix. Atong tan-awon kung giunsa paglihok ang strace sa usa ka punoan nga proseso gamit ang pananglitan sa usa ka yano nga "pagpasanay" mga programa:

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

Dinhi ang orihinal nga proseso nagmugna og proseso sa bata, pareho nga nagsulat sa standard nga output:

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

Sa kasagaran, makita ra nato ang mga tawag sa sistema gikan sa proseso sa ginikanan:

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

Ang bandila makatabang kanimo sa pagsubay sa tibuok nga proseso sa kahoy -f, nga strace nag-monitor sa mga tawag sa sistema sa mga proseso sa bata. Kini nagdugang sa matag linya sa output pid proseso nga naghimo sa usa ka sistema nga output:

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

Niini nga konteksto, ang pagsala sa grupo sa mga tawag sa sistema mahimong mapuslanon:

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

Pinaagi sa dalan, unsa nga sistema sa tawag ang gigamit sa paghimo og bag-ong proseso?

Pananglitan: mga agianan sa file imbes nga mga gunitanan

Ang pagkahibalo sa mga deskriptor sa file siguradong mapuslanon, apan ang mga ngalan sa piho nga mga file nga gi-access sa usa ka programa mahimo usab nga magamit.

Ang sunod ang programa nagsulat sa linya sa temporaryo nga file:

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

Atol sa usa ka normal nga tawag strace ipakita ang kantidad sa numero sa deskriptor nga gipasa sa tawag sa sistema:

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

Uban sa usa ka bandila -y Ang utility nagpakita sa dalan sa file diin ang descriptor katumbas:

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

Pananglitan: Pagsubay sa Pag-access sa File

Laing mapuslanon nga bahin: ipakita lamang ang mga tawag sa sistema nga adunay kalabotan sa usa ka piho nga file. Sunod ang programa gidugang ang usa ka linya sa usa ka arbitraryong file nga gipasa ingon usa ka argumento:

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

pinaagi sa default strace nagpakita sa daghang wala kinahanglana nga kasayuran. Bandila -P uban sa usa ka argumento hinungdan sa strace sa pag-imprinta lamang sa mga tawag sa gipiho nga file:

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

Pananglitan: Multithreaded Programs

Gamit strace makatabang usab kung nagtrabaho uban ang multi-threaded ang programa. Ang mosunod nga programa nagsulat sa standard nga output gikan sa duha ka sapa:

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

Siyempre, kini kinahanglan nga gihugpong sa usa ka espesyal nga pagtimbaya sa linker - ang -pthread nga bandila:

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

Flag -f, sama sa kaso sa regular nga mga proseso, idugang ang pid sa proseso sa sinugdanan sa matag linya.

Siyempre, wala kami maghisgot bahin sa usa ka thread identifier sa diwa sa pagpatuman sa POSIX Threads nga sumbanan, apan mahitungod sa gidaghanon nga gigamit sa task scheduler sa Linux. Gikan sa punto sa panglantaw sa ulahi, wala'y mga proseso o mga hilo - adunay mga buluhaton nga kinahanglan ipanghatag taliwala sa mga magamit nga mga cores sa makina.

Kung nagtrabaho sa daghang mga hilo, ang mga tawag sa sistema mahimong daghan kaayo:

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

Makatarunganon nga limitahan ang imong kaugalingon sa pagproseso sa pagdumala ug mga tawag sa sistema lamang 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 +++

By the way, mga pangutana. Unsa nga sistema sa tawag ang gigamit sa paghimo og bag-ong thread? Sa unsang paagi kini nga panawagan alang sa mga hilo lahi sa panawagan alang sa mga proseso?

Master nga klase: proseso stack sa panahon sa usa ka sistema sa tawag

Usa sa bag-o lang nagpakita strace mga kapabilidad - pagpakita sa stack sa mga tawag sa function sa panahon sa tawag sa sistema. Yano usa ka panig-ingnan:

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

Natural, ang output sa programa mahimong labi ka daghan, ug, dugang sa bandila -k (call stack display), makatarunganon ang pagsala sa mga tawag sa sistema pinaagi sa ngalan:

$ 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 nga klase: sayop nga pag-injection

Ug usa pa ka bag-o ug mapuslanon kaayo nga bahin: error injection. Dinhi ang programa, pagsulat og duha ka linya sa output stream:

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

Atong i-trace ang duha pagsulat tawag:

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

Karon atong gamiton ang ekspresyon injectsa pagsal-ot sa usa ka sayop EBADF sa tanan nga pagsulat tawag:

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

Makapainteres kung unsang mga sayup ang gibalik sa tanan nga mga mga hagit write, lakip ang tawag nga gitago sa luyo sa perror. Makataronganon ra nga ibalik ang usa ka sayup alang sa una sa mga tawag:

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

O ang ikaduha:

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

Dili kinahanglan nga ipiho ang tipo sa sayup:

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

Sa kombinasyon sa ubang mga bandera, mahimo nimong "mabungkag" ang pag-access sa usa ka piho nga file. Pananglitan:

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

Gawas sa error injection, mahimo pagpaila sa mga paglangay kon motawag o modawat ug signal.

Pagkahuman

Gamit strace - usa ka yano ug kasaligan nga himan. Apan dugang sa mga tawag sa sistema, ang ubang mga aspeto sa operasyon sa mga programa ug ang operating system mahimong ma-debug. Pananglitan, kini makasubay sa mga tawag sa dinamikong nalambigit nga mga librarya. pagsubay, mahimo nilang tan-awon ang operasyon sa operating system SystemTap ΠΈ dagway, ug nagtugot kanimo sa pag-imbestigar pag-ayo sa performance sa programa perf. Bisan pa niana, kini mao strace - ang una nga linya sa depensa kung adunay mga problema sa akong kaugalingon ug sa ubang mga programa sa mga tawo, ug gigamit nako kini labing menos kaduha sa usa ka semana.

Sa laktud, kung ganahan ka sa Unix, basaha man 1 strace ug mobati nga gawasnon sa pagtan-aw sa imong mga programa!

Source: www.habr.com

Idugang sa usa ka comment