Strace amin'ny Linux: tantara, famolavolana ary fampiasana

Strace amin'ny Linux: tantara, famolavolana ary fampiasana

Ao amin'ny rafitra miasa toa an'i Unix, ny fifandraisana amin'ny programa amin'ny tontolo ivelany sy ny rafitra miasa dia miseho amin'ny alàlan'ny andiana fiasa kely - antso an-tariby. Midika izany fa ho an'ny tanjona debugging dia mety ilaina ny mitsikilo ny antson'ny rafitra ataon'ny dingana.

Ny fitaovana iray dia manampy anao hanara-maso ny "fiainana akaiky" ny programa amin'ny Linux strace, izay no lohahevitry ny lahatsoratra. Ny ohatra amin'ny fampiasana fitaovana fitsikilovana dia miaraka amin'ny tantara fohy strace ary ny filazalazana momba ny famolavolana programa toy izany.

afa-po

Ny niandohan'ny karazana

Ny fifandraisana lehibe eo amin'ny programa sy ny kernel OS ao amin'ny Unix dia antso an-tariby. antson'ny rafitra, syscalls), ny fifandraisan'ny programa amin'ny tontolo ivelany dia miseho amin'ny alalan'izy ireo irery ihany.

Saingy amin'ny dikan-teny voalohany amin'ny Unix (Version 6 Unix, 1975) tsy nisy fomba mety hanaraha-maso ny fihetsiky ny mpampiasa. Mba hamahana ity olana ity, Bell Labs dia havaozina amin'ny dikan-teny manaraka (Version 7 Unix, 1979) dia nanolotra antso an-tariby vaovao - ptrace.

ptrace dia novolavolaina indrindra ho an'ny debugger interactive, saingy tamin'ny faran'ny taona 80 (tamin'ny vanim-potoanan'ny varotra). Famoahana System V 4) amin'ity fototra ity, dia nipoitra ireo debugger mifantoka tery — mpandinika antso an-tariby — ary nanjary nampiasaina be.

Ny voalohany Ny dikan-teny mitovy amin'ny strace dia navoakan'i Paul Cronenburg tao amin'ny lisitry ny mailaka comp.sources.sun tamin'ny 1992 ho solon'ny fitaovana mihidy. trace avy amin'ny Masoandro. Na ny clone na ny tany am-boalohany dia natao ho an'ny SunOS, saingy tamin'ny 1994 strace dia nafindra tany amin'ny System V, Solaris ary ny Linux malaza.

Ankehitriny ny strace dia manohana ny Linux ary miantehitra amin'izany ihany ptrace, be loatra amin'ny fanitarana maro.

Mpiambina maoderina (ary tena mavitrika). strace - Dmitry Levin. Misaotra azy, nahazo endri-javatra mandroso toy ny fanindronana diso amin'ny antso an-tariby ny fitaovana, fanohanana ny maritrano isan-karazany ary, indrindra indrindra, mascot. Ny loharanom-baovao tsy ofisialy dia milaza fa ny safidy dia latsaka tamin'ny aotirisy noho ny fampifanarahana ny teny Rosiana "ostrich" sy ny teny anglisy "strace".

Zava-dehibe ihany koa ny tsy nampidirina tao amin'ny POSIX mihitsy ny antson'ny rafitra ptrace sy ny tracers, na dia teo aza ny tantara lava sy ny fampiharana amin'ny Linux, FreeBSD, OpenBSD ary Unix nentim-paharazana.

Fitaovana strace amin'ny teny fohy: Piglet Trace

"Tsy andrasana ho azonao izany" (Dennis Ritchie, fanehoan-kevitra ao amin'ny kaody loharano Unix Version 6)

Hatramin'ny fahazazako dia tsy zakako ny boaty mainty: tsy nilalao kilalao aho, fa niezaka ny hahatakatra ny firafiny (nampiasa ny teny hoe "tapaka" ny olon-dehibe, fa tsy mino ny lela ratsy). Angamba izany no mahatonga ny kolontsaina tsy ara-potoanan'ny Unix voalohany sy ny hetsika open-source maoderina akaiky ahy.

Ho an'ny tanjon'ity lahatsoratra ity dia tsy mitombina ny famongorana ny kaody loharanon'ny strace, izay nitombo nandritra ny am-polony taona maro. Saingy tsy tokony hisy tsiambaratelo tavela ho an'ny mpamaky. Noho izany, mba hampisehoana ny fitsipiky ny fiasan'ny programa strace toy izany dia hanome ny code ho an'ny tracer kely aho - Piglet Trace (ptr). Tsy hainy ny manao zavatra manokana, fa ny tena zava-dehibe dia ny antso an-tariby ny programa - mamoaka izany:

$ 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 dia mahafantatra momba ny antso an-jatony amin'ny rafitra Linux (jereo. table) ary miasa amin'ny maritrano x86-64 ihany. Ampy ho an'ny tanjona fanabeazana izany.

Andeha hojerentsika ny asan'ny klonentsika. Raha ny Linux, ny debuggers sy ny tracers dia mampiasa, araka ny voalaza etsy ambony, ny ptrace system call. Izy io dia miasa amin'ny alàlan'ny fandefasana ao amin'ny tohan-kevitra voalohany ny famantarana ny baiko, izay ilaintsika fotsiny PTRACE_TRACEME, PTRACE_SYSCALL и PTRACE_GETREGS.

Manomboka amin'ny fomba Unix mahazatra ny tracer: fork(2) manomboka dingana ho an'ny ankizy, izay indray mampiasa exec(3) manomboka ny fandaharam-pianarana ianarana. Ny hany hafetsena eto dia ny fanamby ptrace(PTRACE_TRACEME) alohan'ny exec: Ny fizotry ny zaza dia manantena ny fizotry ny ray aman-dreny hanara-maso izany:

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

Tokony hiantso izao ny fizotran'ny ray aman-dreny wait(2) ao amin'ny dingan'ny ankizy, izany hoe, ho azo antoka fa efa nitranga ny fifindran'ny fomba trace:

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

Amin'izao fotoana izao, efa vita ny fanomanana ary afaka miroso mivantana amin'ny fanaraha-maso ny antson'ny rafitra amin'ny loop tsy misy farany ianao.

antso ptrace(PTRACE_SYSCALL) miantoka fa manaraka izany wait ray aman-dreny dia hamita na alohan'ny hanatanterahana ny antso an-tariby na avy hatrany aorian'ny fahavitan'izany. Eo anelanelan'ny antso roa dia azonao atao ny manao hetsika rehetra: soloy amin'ny iray hafa ny antso, ovay ny hevitra na ny sanda miverina.

Mila miantso ny baiko indroa fotsiny isika ptrace(PTRACE_GETREGS)mba hahazoana ny rejisitra fanjakana rax alohan'ny fiantsoana (nomerin'ny antso an-tariby) ary avy hatrany aorian'ny (vidim-bola).

Raha ny marina, ny cycle:

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

Izany no tracer manontolo. Fantatrao izao hoe aiza no hanombohana ny porting manaraka DTrace amin'ny Linux.

Fototra: fampandehanana programa mihazakazaka

Toy ny fampiasana voalohany strace, angamba ilaina ny mitanisa ny fomba tsotra indrindra - ny fandefasana fampiharana mandeha strace.

Mba tsy hidirana amin'ny lisitry ny antso tsy misy farany amin'ny programa mahazatra, dia manoratra izahay programa kely indrindra manodidina 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;
}

Andao hanangana ny programa ary ho azo antoka fa miasa izany:

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

Ary farany, andeha hojerentsika eo ambany fifehezana 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)                           = ?

Tena "teny" ary tsy dia manabe. Misy olana roa eto: mifangaro amin'ny vokatra ny output program strace ary be dia be ny antso an-tariby izay tsy mahaliana antsika.

Azonao atao ny manasaraka ny onjam-pamokarana manara-penitra an'ny programa sy ny famoahana ny hadisoana amin'ny alàlan'ny -o switch, izay mamindra ny lisitry ny antson'ny rafitra mankany amin'ny rakitra argument.

Mijanona amin'ny fiatrehana ny olan'ny antso "fanampiny". Aoka hatao hoe antso ihany no mahaliana antsika write. ANDININ- -e mamela anao hamaritra fomba fiteny izay hanasivana ny antson'ny rafitra. Ny safidy fepetra malaza indrindra dia, voajanahary, trace=*, izay tsy ahafahanao mametraka afa-tsy ireo antso mahaliana anay.

Rehefa ampiasaina miaraka -o и -e hahazo isika:

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

Noho izany, hitanareo, dia mora kokoa ny mamaky.

Azonao atao koa ny manaisotra ny antson'ny rafitra, ohatra izay mifandraika amin'ny fizarana fahatsiarovana sy fanafahana:

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

Mariho ny mari-pamantarana nandositra ao amin'ny lisitry ny antso voahilika: takian'ny baiko baiko izany. akorany).

Ao amin'ny dikan-ko glibc, antson'ny rafitra no mamarana ny dingana exit_group, tsy nentim-paharazana _exit. Ity no fahasarotana amin'ny fiasana amin'ny antso an-tariby: tsy mifandray mivantana amin'ny antson'ny rafitra ny interface izay iasan'ny mpandrindra. Ankoatra izany, miova tsy tapaka izy io arakaraka ny fampiharana sy ny sehatra.

Fototra: manatevin-daharana ny dingana amin'ny lalitra

Tany am-boalohany dia miantso ny rafitra ptrace izay nanorenana azy strace, dia tsy azo ampiasaina afa-tsy rehefa mampandeha ny programa amin'ny fomba manokana. Mety ho toa mitombina izany fetra izany tamin'ny andron'ny Version 6 Unix. Amin'izao fotoana izao dia tsy ampy intsony izany: indraindray mila manadihady ny olan'ny programa miasa ianao. Ny ohatra mahazatra dia dingana iray voasakana amin'ny tahony na torimaso. Noho izany maoderina strace afaka miditra amin'ny dingana amin'ny lalitra.

Ohatra mangatsiaka fandaharana:

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

Andao hanangana ny programa ary ho azo antoka fa mivaingana izany:

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

Andeha isika hiezaka hanatevin-daharana azy:

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

Nosakanan'ny antso ny fandaharana pause. Andeha hojerentsika ny fihetsiky ny famantarana:

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

Nanokatra ny programa mangatsiaka izahay ary nanatevin-daharana azy io strace. Zavatra roa no nanjary nazava: ny antso an-tariby fiatoana dia tsy miraharaha famantarana tsy misy mpitantana ary, ny mahaliana kokoa, ny fanaraha-maso ny strace dia tsy ny antson'ny rafitra ihany, fa ny famantarana miditra koa.

Ohatra: Fanaraha-maso ny fizotry ny zaza

Miasa miaraka amin'ireo dingana amin'ny alàlan'ny antso fork - fototry ny Unix rehetra. Andeha hojerentsika ny fomba fiasan'ny strace amin'ny hazo fizotry ny fampiasana ny ohatry ny "fiompiana" tsotra fandaharana:

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

Eto ny dingana voalohany dia mamorona dingan'ny ankizy, samy manoratra amin'ny vokatra mahazatra:

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

Amin'ny alàlan'ny default, ny antso an-tariby avy amin'ny fizotry ny ray aman-dreny ihany no ho hitantsika:

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

Ny saina dia manampy anao hanara-maso ny hazo fizotry ny dingana manontolo -f, izay strace manara-maso ny antson'ny rafitra amin'ny fizotry ny zaza. Izany dia manampy amin'ny andalana tsirairay avy pid dingana izay mahatonga ny rafitra 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 +++

Amin'ity toe-javatra ity, ny sivana amin'ny vondrona antso an-tariby dia mety ilaina:

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

Raha ny marina, inona ny antso an-tariby ampiasaina hamoronana dingana vaovao?

Ohatra: lalan-drakitra fa tsy tantana

Ny fahafantarana ireo mpamoritra rakitra dia azo antoka fa ilaina, fa ny anaran'ireo rakitra manokana idiran'ny programa dia mety ho azo ampiasaina ihany koa.

manaraka ny fandaharana manoratra ny tsipika mankany amin'ny rakitra vonjimaika:

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

Nandritra ny antso mahazatra strace dia hampiseho ny sandan'ny laharan'ny mpamaritra nampitaina tamin'ny antson'ny rafitra:

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

Miaraka amin'ny saina -y Ny utility dia mampiseho ny lalana mankany amin'ny rakitra izay mifanitsy amin'ny mpamoritra:

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

Ohatra: Fanaraha-maso ny fidirana amin'ny rakitra

Endri-javatra mahasoa hafa: asehoy ny antso an-tariby mifandray amin'ny rakitra manokana. Manaraka ny fandaharana manisy tsipika amin'ny rakitra arbitrary nandalo ho adihevitra:

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

amin'ny alalan'ny toerana misy anao strace mampiseho fampahalalana tsy ilaina be dia be. sainam-pirenena -P miaraka amin'ny argument dia mahatonga ny strace hanonta ny antso amin'ny rakitra voafaritra ihany:

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

Ohatra: Fandaharana maromaro maromaro

Utility strace afaka manampy koa rehefa miasa amin'ny multi-threaded ny fandaharana. Ity programa manaraka ity dia manoratra amin'ny vokatra mahazatra avy amin'ny renirano roa:

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

Mazava ho azy fa tsy maintsy atambatra miaraka amin'ny fiarahabana manokana ho an'ny mpampitohy - ny saina -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
$

sainam-pirenena -f, toy ny amin'ny dingana tsy tapaka, dia hanampy ny pid amin'ny dingana amin'ny fiandohan'ny andalana tsirairay.

Mazava ho azy fa tsy miresaka momba ny famantarana kofehy isika amin'ny dikan'ny fampiharana ny fenitra POSIX Threads, fa momba ny isa ampiasain'ny mpandrindra asa amin'ny Linux. Avy amin'ny fomba fijerin'ity farany dia tsy misy dingana na kofehy - misy ny asa tokony hozaraina amin'ireo cores misy ny milina.

Rehefa miasa amina kofehy maromaro dia lasa maro loatra ny antson'ny rafitra:

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

Misy dikany ny mametra ny tenanao amin'ny fitantanana ny fitantanana sy ny antson'ny rafitra ihany 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 +++

Raha ny marina, fanontaniana. Inona no antso an-tariby ampiasaina hamoronana lohahevitra vaovao? Inona no maha-samihafa an'ity antso ho an'ny kofehy ity amin'ny antso ho an'ny dingana?

Kilasy maîtrise: filaharana amin'ny fotoanan'ny antson'ny rafitra

Nipoitra vao haingana ny iray tamin'ireo strace fahaiza-manao - mampiseho ny stack ny asa antso amin'ny fotoana ny rafitra antso. TSOTRA ohatra:

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

Mazava ho azy, ny fandaharana Output lasa be voluminous, ary, ankoatra ny saina -k (antsoina hoe stack display), misy dikany ny manivana ny antson'ny rafitra amin'ny anarana:

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

Kilasy master: tsindrona diso

Ary endri-javatra iray vaovao sy tena ilaina: tsindrona fahadisoana. Eto ny fandaharana, manoratra andalana roa mankany amin'ny stream output:

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

Andeha hojerentsika ny antso an-tariby roa:

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

Amin'izao fotoana izao dia mampiasa ny fomba fiteny isika injecthampiditra fahadisoana EBADF amin'ny antso an-tsoratra rehetra:

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

Mahaliana hoe inona ny fahadisoana averina ny rehetra Zava-tsarotra write, ao anatin'izany ny antso miafina ao ambadiky ny faharatsiana. Tsy misy dikany fotsiny ny mamerina hadisoana amin'ny antso voalohany:

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

Na ny faharoa:

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

Tsy ilaina ny mamaritra ny karazana fahadisoana:

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

Miaraka amin'ny sainam-pirenena hafa, azonao atao ny "manapaka" ny fidirana amin'ny rakitra manokana. Ohatra:

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

Ankoatra ny tsindrona diso, afaka mampiditra fahatarana rehefa miantso na mandray famantarana.

afterword

Utility strace - fitaovana tsotra sy azo antoka. Saingy ankoatry ny antson'ny rafitra, ny lafiny hafa amin'ny fampandehanan'ny programa sy ny rafitra fiasana dia azo alaina. Ohatra, afaka manara-maso ny antso amin'ny tranomboky mifandray mavitrika izy. ltrace, afaka mijery ny fiasan'ny rafitra fiasana izy ireo SystemTap и ftrace, ary mamela anao hanadihady lalina ny fahombiazan'ny programa tonga lafatra. Na izany aza, dia izany strace - ny andalana voalohany fiarovana raha misy olana amin'ny programa manokana sy ny olon-kafa, ary mampiasa izany aho in-droa isan-kerinandro farafahakeliny.

Raha fintinina, raha tia Unix ianao dia vakio man 1 strace ary aza misalasala mijery ny programanao!

Source: www.habr.com

Add a comment