Strace nan Linux: istwa, konsepsyon ak itilizasyon

Strace nan Linux: istwa, konsepsyon ak itilizasyon

Nan sistèm operasyon tankou Unix, kominikasyon yon pwogram ak mond lan deyò ak sistèm operasyon an rive atravè yon ti seri fonksyon - apèl sistèm. Sa vle di ke pou rezon debogaj li ka itil al rekonèt sou apèl sistèm ke yo te egzekite pa pwosesis.

Yon sèvis piblik ede ou kontwole "lavi entim" nan pwogram sou Linux strace, ki se sijè a nan atik sa a. Men kèk egzanp sou itilizasyon ekipman espyon yo akonpaye pa yon istwa tou kout strace ak yon deskripsyon konsepsyon pwogram sa yo.

Content

Orijin nan espès yo

Koòdone prensipal ant pwogram yo ak nwayo OS nan Unix se apèl sistèm. apèl sistèm yo, syscalls), entèraksyon an nan pwogram ak mond lan deyò fèt sèlman atravè yo.

Men, nan premye vèsyon piblik UNIX (Version 6 Unix, 1975) pa te gen okenn fason pratik yo swiv konpòtman an nan pwosesis itilizatè yo. Pou rezoud pwoblèm sa a, Bell Labs pral mete ajou nan pwochen vèsyon an (Version 7 Unix, 1979) te pwopoze yon nouvo apèl sistèm - ptrace.

ptrace te devlope sitou pou debogaj entèaktif, men nan fen ane 80 yo (nan epòk komèsyal yo. Sistèm V Version 4) sou baz sa a, debogaj etwat konsantre-trazeur apèl sistèm-parèt epi yo te vin lajman itilize.

Premye a menm vèsyon an strace te pibliye pa Paul Cronenburg sou lis adrès comp.sources.sun an 1992 kòm yon altènativ a yon sèvis piblik fèmen. trace soti nan Solèy. Tou de script la ak orijinal la te gen entansyon pou SunOS, men nan 1994 strace te pote sou System V, Solaris ak Linux de pli zan pli popilè.

Jodi a strace sèlman sipòte Linux ak depann sou menm bagay la tou ptrace, anvayi ak anpil ekstansyon.

Modèn (ak trè aktif) antretyen strace - Dmitri Levin. Mèsi a li, sèvis piblik la akeri karakteristik avanse tankou piki erè nan apèl sistèm, sipò pou yon pakèt achitekti ak, sa ki pi enpòtan, maskot. Sous ofisyèl yo di ke chwa a tonbe sou otrich la akòz konsonans ki genyen ant mo Ris "otrich" ak mo angle "strace".

Li enpòtan tou pou apèl sistèm ptrace ak trasè yo pa t janm enkli nan POSIX, malgre yon istwa long ak aplikasyon nan Linux, FreeBSD, OpenBSD ak tradisyonèl Unix.

Aparèy Strace nan yon Nutshell: Piglet Trace

"Ou pa atann pou w konprann sa" (Dennis Ritchie, kòmantè nan kòd sous Version 6 Unix)

Depi timoun piti, mwen pa ka kanpe bwat nwa: mwen pa t jwe ak jwèt, men yo te eseye konprann estrikti yo (granmoun yo te itilize mo "kraze," men yo pa kwè lang sa ki mal). Petèt se poutèt sa kilti enfòmèl premye Unix la ak mouvman sous louvri modèn lan tèlman pre m.

Pou rezon atik sa a, li pa rezonab pou demonte kòd sous strace, ki te grandi pandan plizyè dizèn ane. Men, pa ta dwe rete sekrè pou lektè yo. Se poutèt sa, yo montre prensip la nan operasyon nan pwogram strace sa yo, mwen pral bay kòd la pou yon traseur Miniature - Tras kochon (ptr). Li pa konnen ki jan fè anyen espesyal, men bagay prensipal la se apèl sistèm nan pwogram nan - li soti:

$ 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 rekonèt anviwon plizyè santèn apèl sistèm Linux (gade. tab) epi sèlman travay sou achitekti x86-64. Sa a se ase pou rezon edikasyon.

Ann gade travay klon nou an. Nan ka Linux, debogaj ak traseur itilize, jan mansyone pi wo a, apèl sistèm ptrace la. Li travay pa pase nan premye agiman idantifyan yo kòmand, nan ki nou sèlman bezwen PTRACE_TRACEME, PTRACE_SYSCALL и PTRACE_GETREGS.

Tracer la kòmanse nan style nòmal Unix la: fork(2) lanse yon pwosesis timoun, ki an vire itilize exec(3) lanse pwogram nan etid la. Sèl sibtilite isit la se defi a ptrace(PTRACE_TRACEME) anvan exec: Pwosesis timoun nan espere pwosesis paran an kontwole li:

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

Pwosesis paran an ta dwe kounye a rele wait(2) nan pwosesis timoun lan, se sa ki, asire w ke chanje nan mòd tras te fèt:

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

Nan pwen sa a, preparasyon yo konplè epi ou ka kontinye dirèkteman nan swiv apèl sistèm nan yon bouk kontinuèl.

Defi ptrace(PTRACE_SYSCALL) garanti sa ki vin apre wait paran ap konplete swa anvan apèl sistèm lan egzekite oswa imedyatman apre li fini. Ant de apèl ou ka fè nenpòt aksyon: ranplase apèl la ak yon lòt altènatif, chanje agiman yo oswa valè retounen an.

Nou jis bezwen rele kòmandman an de fwa ptrace(PTRACE_GETREGS)pou jwenn eta enskripsyon an rax anvan apèl la (nimewo apèl sistèm) ak imedyatman apre (valè retounen).

Aktyèlman, sik la:

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

Sa a tout traseur a. Koulye a, ou konnen ki kote yo kòmanse pòtaj kap vini an DTrace sou Linux.

Basics: kouri yon pwogram kouri strace

Kòm yon premye ka itilize strace, petèt li la vo site metòd ki pi senp - lanse yon aplikasyon kouri strace.

Nan lòd pa fouye nan lis la kontinuèl nan apèl nan yon pwogram tipik, nou ekri pwogram minimòm alantou 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;
}

Ann bati pwogram nan epi asire w ke li fonksyone:

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

Epi finalman, ann kouri li anba kontwòl 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)                           = ?

Trè "palè" epi yo pa trè edikatif. Genyen de pwoblèm isit la: pwodiksyon pwogram lan melanje ak pwodiksyon an strace ak yon abondans nan sistèm apèl ki pa enterese nou.

Ou ka separe kouran pwodiksyon estanda pwogram nan ak pwodiksyon erè strace lè l sèvi avèk -o switch la, ki redireksyon lis apèl sistèm nan yon dosye agiman.

Li rete fè fas ak pwoblèm nan nan apèl "siplemantè". Ann sipoze ke nou sèlman enterese nan apèl yo write. Kle -e pèmèt ou presize ekspresyon yo pral filtre apèl sistèm yo. Opsyon kondisyon ki pi popilè se, natirèlman, trace=*, ak ki ou ka kite sèlman apèl yo ki enterese nou.

Lè yo itilize ansanm -o и -e nou pral jwenn:

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

Se konsa, ou wè, li pi fasil pou li.

Ou kapab tou retire apèl sistèm, pou egzanp sa ki gen rapò ak alokasyon memwa ak libere:

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

Remake mak eksklamasyon ki chape nan lis apèl eskli yo: sa a mande pou koki kòmand la. koki).

Nan vèsyon mwen an nan glibc, yon apèl sistèm mete fen nan pwosesis la exit_group, pa tradisyonèl _exit. Sa a se difikilte pou travay ak apèl sistèm: koòdone ak ki pwogramè a ap travay pa dirèkteman gen rapò ak apèl sistèm. Anplis, li chanje regilyèman depann sou aplikasyon an ak platfòm.

Basics: rantre nan pwosesis la sou vole

Okòmansman, sistèm nan ptrace rele sou ki li te bati strace, ta ka itilize sèlman lè w ap kouri pwogram nan nan yon mòd espesyal. Limitasyon sa a te ka sonnen rezonab nan epòk Version 6 Unix. Sèjousi, sa a pa ase ankò: pafwa ou bezwen envestige pwoblèm ki genyen nan yon pwogram k ap travay. Yon egzanp tipik se yon pwosesis bloke sou yon manch oswa dòmi. Se poutèt sa modèn strace ka rantre nan pwosesis sou vole.

Egzanp konjelasyon pwogram:

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

Ann bati pwogram nan epi asire w ke li nan frizè:

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

Koulye a, ann eseye rantre nan li:

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

Pwogram bloke pa apèl pause. Ann wè ki jan li reyaji ak siyal yo:

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

Nou te lanse pwogram nan frizè ak rantre nan li lè l sèvi avèk strace. De bagay te vin klè: apèl sistèm poz la inyore siyal san moun kap okipe yo epi, pi enteresan, strace monitè pa sèlman apèl sistèm, men tou siyal k ap fèk ap rantre.

Egzanp: Swiv Pwosesis Timoun yo

Travay ak pwosesis atravè yon apèl fork - baz tout UNIX. Ann wè ki jan strace travay ak yon pye bwa pwosesis lè l sèvi avèk egzanp yon senp "elvaj" pwogram:

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

Isit la pwosesis orijinal la kreye yon pwosesis pitit, tou de ekri nan pwodiksyon estanda:

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

Pa default, nou pral wè sèlman apèl sistèm nan pwosesis paran an:

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

Drapo a ede w swiv tout pye bwa pwosesis la -f, ki strace kontwole apèl sistèm nan pwosesis timoun yo. Sa a ajoute nan chak liy pwodiksyon an pid pwosesis ki fè yon pwodiksyon sistèm:

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

Nan kontèks sa a, filtraj pa gwoup apèl sistèm ka itil:

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

By wout la, ki apèl sistèm yo itilize pou kreye yon nouvo pwosesis?

Egzanp: chemen dosye olye pou yo manch

Konnen deskriptè dosye se sètènman itil, men non yo nan dosye yo espesifik ke yon pwogram aksè kapab tou vin an sou la men.

Next pwogram lan ekri liy lan nan yon dosye tanporè:

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

Pandan yon apèl nòmal strace pral montre valè nimewo deskriptè yo pase nan apèl sistèm lan:

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

Ak yon drapo -y Sèvis piblik la montre chemen an nan dosye a ki deskriptè a koresponn:

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

Egzanp: File Aksè Tracking

Yon lòt karakteristik itil: montre sèlman apèl sistèm ki asosye ak yon dosye espesifik. Next pwogram lan ajoute yon liy nan yon dosye abitrè pase kòm yon agiman:

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

Pa default strace montre anpil enfòmasyon ki pa nesesè. Drapo -P ak yon agiman lakòz strace enprime sèlman apèl nan dosye a espesifye:

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

Egzanp: Pwogram Multithreaded

Sèvis piblik strace ka ede tou lè w ap travay ak milti-threaded pwogram lan. Pwogram sa a ekri sou pwodiksyon estanda soti nan de kouran:

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

Natirèlman, li dwe konpile ak yon bonjou espesyal pou linker a - drapo a -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
$

Drapo -f, tankou nan ka a nan pwosesis regilye, pral ajoute pid nan pwosesis la nan kòmansman an nan chak liy.

Natirèlman, nou pa pale de yon idantifyan fil nan sans aplikasyon estanda POSIX Threads, men sou nimewo ki itilize pa pwogramè travay la nan Linux. Soti nan pwen de vi lèt la, pa gen okenn pwosesis oswa fil - gen travay ki bezwen distribye nan mitan am ki disponib nan machin nan.

Lè w ap travay nan plizyè fil, apèl sistèm vin twòp:

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

Li fè sans limite tèt ou nan jesyon pwosesis ak apèl sistèm sèlman 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 wout la, kesyon. Ki apèl sistèm ki itilize pou kreye yon nouvo fil? Ki jan apèl sa a pou fil diferan de apèl pou pwosesis la?

Klas mèt: pile pwosesis nan moman yon apèl sistèm

Youn nan fèk parèt strace kapasite - montre pil apèl fonksyon nan moman apèl sistèm lan. Senp egzanp:

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

Natirèlman, pwodiksyon an pwogram vin trè volumineuz, epi, nan adisyon a drapo a -k (rele pile ekspozisyon), li fè sans pou filtre apèl sistèm pa non:

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

Klas mèt: piki erè

Ak yon lòt karakteristik nouvo ak trè itil: piki erè. Isit la pwogram lan, ekri de liy nan kouran pwodiksyon an:

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

Ann trase tou de apèl ekri:

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

Koulye a, nou itilize ekspresyon an injectpou mete yon erè EBADF nan tout apèl ekri:

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

Li enteresan ki erè yo retounen tout defi write, ki gen ladan apèl la kache dèyè pèr. Li sèlman fè sans pou retounen yon erè pou premye nan apèl yo:

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

Oswa dezyèm lan:

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

Li pa nesesè pou presize kalite erè a:

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

Nan konbinezon ak lòt drapo, ou ka "kraze" aksè nan yon dosye espesifik. Egzanp:

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

Anplis piki erè, yon sèl kapab prezante reta lè w ap fè apèl oswa w ap resevwa siyal.

Apreword

Sèvis piblik strace - yon zouti ki senp epi serye. Men, anplis apèl sistèm, lòt aspè nan operasyon an nan pwogram ak sistèm nan fonksyone ka debogaj. Pou egzanp, li ka swiv apèl nan bibliyotèk ki lye dinamik. tras, yo ka gade nan operasyon an nan sistèm nan fonksyone SystemTap и ftrace, epi pèmèt ou envestige pwofondman pèfòmans pwogram lan pèf. Men, li se strace - premye liy defans nan ka ta gen pwoblèm ak pwogram pwòp mwen ak lòt moun, epi mwen sèvi ak li omwen yon koup de fwa nan yon semèn.

Nan ti bout tan, si ou renmen Unix, li man 1 strace epi santi yo lib pou gade nan pwogram ou yo!

Sous: www.habr.com

Add nouvo kòmantè