Strace di Linux: sajarah, desain sareng panggunaan

Strace di Linux: sajarah, desain sareng panggunaan

Dina sistem operasi kawas Unix, komunikasi program urang jeung dunya luar jeung sistem operasi lumangsung ngaliwatan sakumpulan leutik fungsi - system calls. Ieu ngandung harti yén pikeun tujuan debugging tiasa mangpaat pikeun nénjo telepon sistem anu dieksekusi ku prosés.

Utiliti ngabantosan anjeun ngawas "kahirupan intim" program dina Linux strace, nu jadi subyek artikel ieu. Conto pamakean alat-alat nénjo dibarengan ku riwayat singget strace sareng pedaran ngeunaan desain program sapertos kitu.

eusi

Asalna spésiés

Antarbeungeut utama antara program sareng kernel OS di Unix nyaéta panggero sistem. nelepon sistem, baling-baling), interaksi program jeung dunya luar lumangsung sacara éksklusif ngaliwatan aranjeunna.

Tapi dina versi umum mimiti Unix (Vérsi 6 Unix, 1975) euweuh cara merenah pikeun ngalacak paripolah prosés pamaké. Pikeun ngabéréskeun masalah ieu, Bell Labs bakal ngamutahirkeun kana versi salajengna (Vérsi 7 Unix, 1979) ngusulkeun panggero sistem anyar - ptrace.

ptrace dikembangkeun utamina pikeun debugger interaktif, tapi dina ahir taun 80an (dina jaman komersil. Sistem V Kaluaran 4) dina dasar ieu, heureut fokus debuggers-system call tracers-muncul tur jadi loba dipaké.

kahiji versi sarua strace diterbitkeun ku Paul Cronenburg dina milis comp.sources.sun di 1992 salaku alternatif pikeun utiliti katutup. trace ti Sun. Duanana klon sareng anu asli ditujukeun pikeun SunOS, tapi ku 1994 strace dialihkeun ka System V, Solaris sareng Linux anu beuki populer.

Kiwari strace ngan ukur ngadukung Linux sareng ngandelkeun anu sami ptrace, ditumbuh ku seueur ekstensi.

Modern (jeung aktip pisan) maintainer strace - Dmitry Levin. Hatur nuhun ka anjeunna, utilitas ngagaduhan fitur-fitur canggih sapertos suntikan kasalahan kana telepon sistem, dukungan pikeun rupa-rupa arsitéktur sareng, anu paling penting, maskot. Sumber teu resmi nyatakeun yén pilihan éta murag dina manuk onta kusabab konsonan antara kecap Rusia "burung onta" sareng kecap Inggris "strace".

Éta ogé penting yén ptrace system call sareng tracers henteu pernah kalebet dina POSIX, sanaos sajarah panjang sareng palaksanaan dina Linux, FreeBSD, OpenBSD sareng Unix tradisional.

Alat Strace dina nutshell: Piglet Trace

"Anjeun henteu diperkirakeun ngartos ieu" (Dennis Ritchie, koméntar dina kode sumber Unix Vérsi 6)

Kusabab PAUD, abdi teu tiasa nangtung buleud hideung: Kuring teu maén kalawan Toys, tapi diusahakeun ngartos struktur maranéhanana (dewasa dipaké kecap "pegat", tapi teu percanten ka basa jahat). Panginten ieu sababna budaya informal Unix munggaran sareng gerakan open-source modéren caket pisan sareng kuring.

Pikeun kaperluan artikel ieu, teu munasabah mun ngabongkar kode sumber strace, nu geus tumuwuh leuwih dekade. Tapi kudu aya euweuh Rahasia ditinggalkeun pikeun pamiarsa. Kukituna, pikeun nunjukkeun prinsip operasi program strace sapertos kitu, kuring bakal nyayogikeun kodeu pikeun tracer miniatur - Lacak Babi (ptr). Éta henteu terang kumaha ngalakukeun anu khusus, tapi anu utama nyaéta panggero sistem program - éta kaluaran:

$ 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 ngakuan kira-kira ratusan telepon sistem Linux (tingali. tabél) sareng ngan ukur dianggo dina arsitektur x86-64. Ieu cukup keur kaperluan atikan.

Hayu urang tingali karya clone urang. Dina kasus Linux Ubuntu, debuggers na tracers ngagunakeun, sakumaha disebutkeun di luhur, ptrace system call. Gawéna ku jalan ngalirkeun dina argumen kahiji identifiers paréntah, nu urang ngan butuh PTRACE_TRACEME, PTRACE_SYSCALL и PTRACE_GETREGS.

Tracer dimimitian dina gaya Unix biasa: fork(2) ngajalankeun prosés anak, anu dina gilirannana ngagunakeun exec(3) ngaluncurkeun program anu ditalungtik. Hijina subtlety didieu nyaeta tantangan ptrace(PTRACE_TRACEME) sateuacan exec: Prosés anak ngaharepkeun prosés indungna pikeun ngawas éta:

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

Prosés indungna ayeuna kudu nelepon wait(2) dina prosés anak, nyaéta, pastikeun yén gentos ka modeu ngalacak parantos kajantenan:

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

Dina titik ieu, persiapan parantos réngsé sareng anjeun tiasa langsung neraskeun telepon sistem nyukcruk dina loop anu teu aya tungtungna.

nelpon ptrace(PTRACE_SYSCALL) jaminan yén saterusna wait indungna bakal ngarengsekeun sateuacan sauran sistem dieksekusi atanapi langsung saatos réngsé. Di antara dua telepon anjeun tiasa ngalakukeun tindakan naon waé: ngagentos sauran ku alternatif, ngarobih argumen atanapi nilai uih deui.

Urang ngan perlu nelepon paréntah dua kali ptrace(PTRACE_GETREGS)pikeun meunangkeun kaayaan register rax sateuacan nelepon (nomer panggero sistem) jeung langsung saatos (nilai balik).

Kanyataanna, siklus:

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

Éta sakabéh tracer. Ayeuna anjeun terang dimana ngamimitian porting salajengna DTrace dina Linux.

Dasar: ngajalankeun program ngajalankeun strace

Salaku kasus pamakéan munggaran strace, meureun éta patut disebatkeun metodeu pangbasajanna - ngaluncurkeun aplikasi anu dijalankeun strace.

Dina raraga teu delve kana daptar sajajalan tina panggero program has, urang nulis program minimum kira-kira 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;
}

Hayu urang ngawangun program sareng pastikeun éta jalanna:

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

Sarta pamustunganana, hayu urang ngajalankeun eta dina kontrol 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)                           = ?

Pisan "wordy" jeung teu pisan atikan. Aya dua masalah di dieu: kaluaran program dicampurkeun sareng kaluaran strace sareng seueur telepon sistem anu henteu dipikaresep ku urang.

Anjeun tiasa misahkeun aliran kaluaran standar program sareng kaluaran kasalahan nganggo saklar -o, anu alihan daptar telepon sistem ka file argumen.

Tetep nungkulan masalah telepon "tambahan". Anggap urang ngan museurkeun telepon write. konci -e ngidinan Anjeun pikeun nangtukeun ekspresi nu nelepon sistem bakal disaring. Pilihan kaayaan anu paling populér nyaéta, sacara alami, trace=*, anu anjeun tiasa ngantunkeun ngan ukur telepon anu dipikaresep ku urang.

Nalika dianggo sakaligus -o и -e urang bakal meunang:

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

Janten, anjeun tingali, éta langkung gampang dibaca.

Anjeun ogé tiasa ngahapus telepon sistem, contona anu aya hubunganana sareng alokasi mémori sareng ngabébaskeun:

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

Catet tanda seru anu lolos dina daptar telepon anu teu kalebet: ieu diperyogikeun ku cangkang paréntah. kerang).

Dina versi abdi glibc, panggero sistem terminates prosés exit_group, teu tradisional _exit. Ieu mangrupikeun kasusah pikeun damel sareng telepon sistem: antarmuka anu dianggo ku programer henteu langsung aya hubunganana sareng telepon sistem. Sumawona, éta robih sacara teratur gumantung kana palaksanaan sareng platform.

Dasar: gabung dina prosés on laleur

Mimitina, sistem ptrace nelepon dimana éta diwangun strace, ngan bisa dipaké nalika ngajalankeun program dina mode husus. Watesan ieu sigana wajar dina jaman Vérsi 6 Unix. Ayeuna, ieu henteu cekap deui: sakapeung anjeun kedah nalungtik masalah program kerja. Hiji conto has nyaéta prosés diblokir dina cecekelan atawa sare. Ku kituna modern strace bisa gabung prosés on laleur.

Contona katirisan program:

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

Hayu urang ngawangun program sareng pastikeun éta beku:

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

Ayeuna hayu urang coba gabung:

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

Program diblokir ku panggero pause. Hayu urang tingali kumaha anjeunna ngaréspon kana sinyal:

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

Kami ngaluncurkeun program beku sareng ngagabung nganggo strace. Dua hal janten jelas: panggero sistem pause malire sinyal tanpa pawang jeung, leuwih narik, strace monitor teu ukur nelepon sistem, tapi ogé sinyal asup.

Conto: Nyukcruk Prosés Anak

Gawe sareng prosés ngaliwatan telepon fork - dasar sadaya Unixes. Hayu urang tingali kumaha strace jalan sareng tangkal prosés nganggo conto "peternakan" saderhana program:

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

Di dieu prosés aslina nyiptakeun prosés anak, duanana nyerat kana kaluaran standar:

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

Sacara standar, urang ngan ukur ningali sauran sistem tina prosés indungna:

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

Bandéra ngabantosan anjeun ngalacak sadayana tangkal prosés -f, anu strace ngawas panggero sistem dina prosés anak. Ieu nambihan ka unggal garis kaluaran pid prosés nu nyieun kaluaran sistem:

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

Dina kontéks ieu, nyaring dumasar grup panggero sistem tiasa mangpaat:

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

Ku jalan kitu, naon sistem panggero anu dipaké pikeun nyieun prosés anyar?

Conto: jalur file tinimbang handles

Nyaho deskriptor file pasti mangpaat, tapi nami file khusus anu diaksés ku program ogé tiasa dianggo.

The salajengna program nyerat garis kana file samentara:

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

Dina telepon biasa strace bakal nunjukkeun nilai nomer deskriptor anu dikirimkeun ka telepon sistem:

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

Kalayan bandéra -y Utiliti nunjukkeun jalur ka file anu cocog sareng deskriptor:

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

Conto: Nyukcruk Aksés File

Fitur mangpaat séjén: ngan mintonkeun telepon sistem pakait sareng file husus. Teras program appends garis ka file sawenang diliwatan salaku argumen:

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

sacara standar strace mintonkeun loba informasi nu teu perlu. Bandéra -P kalawan argumen ngabalukarkeun strace mun nyitak ngan nelepon ka file dieusian:

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

Contona: Program Multithreaded

Utiliti strace ogé tiasa ngabantosan nalika damel sareng multi-threaded programna. Program ieu nyerat kana kaluaran standar tina dua aliran:

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

Alami, éta kudu disusun ku ucapan husus ka linker - bendera -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
$

bandera -f, Saperti dina kasus prosés biasa, bakal nambahan pid prosés ka awal unggal baris.

Alami, urang teu ngawangkong ngeunaan hiji identifier thread dina harti palaksanaan standar POSIX Threads, tapi ngeunaan jumlah dipaké ku scheduler tugas di Linux. Tina sudut pandang anu terakhir, henteu aya prosés atanapi benang - aya tugas anu kedah disebarkeun diantara inti mesin anu sayogi.

Nalika damel di sababaraha utas, sauran sistem janten seueur teuing:

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

Masuk akal pikeun ngawatesan diri pikeun ngolah manajemén sareng telepon sistem wungkul 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 +++

Ku jalan kitu, patarosan. Naon sistem panggero anu dipaké pikeun nyieun thread anyar? Kumaha panggero ieu threads béda ti panggero pikeun prosés?

Kelas master: tumpukan prosés dina waktos nelepon sistem

Salah sahiji nu anyar mucunghul strace kamampuhan - mintonkeun tumpukan télépon fungsi dina waktu nelepon sistem. Basajan conto:

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

Alami, kaluaran program janten pisan voluminous, sarta, sajaba bandéra -k (tampilan tumpukan panggero), asup akal pikeun nyaring telepon sistem ku nami:

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

Kelas master: suntik kasalahan

Sareng hiji deui fitur énggal sareng mangpaat pisan: suntikan kasalahan. Ieuh program, nulis dua baris kana aliran kaluaran:

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

Hayu urang ngalacak duanana panggero nulis:

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

Ayeuna urang ngagunakeun éksprési injectpikeun nyelapkeun kasalahan EBADF dina sadaya sauran nyerat:

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

Éta pikaresepeun naon kasalahan anu dipulangkeun sadaya tantangan write, kaasup panggero disumputkeun tukangeun perror. Ieu ukur masuk akal pikeun mulangkeun kasalahan pikeun sauran anu munggaran:

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

Atawa nu kadua:

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

Teu perlu nangtukeun jenis kasalahan:

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

Dina kombinasi kalayan umbul séjén, anjeun tiasa "megatkeun" aksés ka file husus. conto:

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

Salian suntikan kasalahan, bisa ngenalkeun telat nalika nelepon atanapi nampi sinyal.

afterword

Utiliti strace - alat basajan tur dipercaya. Tapi salian ti nelepon sistem, aspék séjén tina operasi program jeung sistem operasi bisa debugged. Contona, éta bisa ngalacak sauran ka perpustakaan numbu dinamis. ngalacak, aranjeunna tiasa ningali kana operasi sistem operasi SystemTap и jalan tol, sarta ngidinan Anjeun pikeun deeply nalungtik kinerja program parfum. Sanajan kitu, éta strace - garis pertahanan kahiji bisi aya masalah sareng program kuring sorangan sareng batur, sareng kuring nganggo sahenteuna sababaraha kali saminggu.

Pondokna, upami anjeun resep Unix, baca man 1 strace jeung ngarasa Luncat ka Toong dina program Anjeun!

sumber: www.habr.com

Tambahkeun komentar