Strace a cikin Linux: tarihi, ƙira da amfani

Strace a cikin Linux: tarihi, ƙira da amfani

A cikin tsarin aiki kamar Unix, sadarwar shirin tare da duniyar waje da tsarin aiki yana faruwa ta hanyar ƙaramin tsarin ayyuka - kiran tsarin. Wannan yana nufin cewa don dalilai na lalata yana iya zama da amfani don rahõto kan kiran tsarin da ake aiwatar da su ta hanyar matakai.

Mai amfani yana taimaka muku saka idanu "rayuwar kud da kud" na shirye-shirye akan Linux strace, wanda shine batun wannan labarin. Misalai na amfani da kayan leƙen asiri suna tare da taƙaitaccen tarihi strace da kuma bayanin yadda ake tsara irin waɗannan shirye-shiryen.

Abubuwa

Asalin jinsin

Babban haɗin kai tsakanin shirye-shirye da kernel OS a cikin Unix shine kiran tsarin. kira tsarin, kiran waya), hulɗar shirye-shirye tare da duniyar waje yana faruwa ne kawai ta hanyar su.

Amma a cikin sigar jama'a ta farko ta Unix (Shafin 6 Unix, 1975) babu hanyoyin da suka dace don bin diddigin halayen masu amfani. Don warware wannan batu, Bell Labs zai sabunta zuwa na gaba version (Shafin 7 Unix, 1979) ya ba da shawarar sabon tsarin kira - ptrace.

Ptrace an haɓaka shi ne da farko don masu ɓarna ma'amala, amma a ƙarshen 80s (a zamanin kasuwanci). Sakin Tsarin V 4) a kan wannan, ƙunƙuntaccen mayar da hankali ga masu gyara kurakurai - na'urorin kira na tsarin - sun bayyana kuma sun zama masu amfani da yawa.

Na farko Paul Cronenburg ya buga wannan nau'in nau'in nau'in a kan comp.sources.sun aikawasiku a cikin 1992 a matsayin madadin rufaffiyar mai amfani. trace daga Sun. Dukansu clone da asali an yi niyya ne don SunOS, amma ta 1994 strace An tura shi zuwa System V, Solaris da kuma ƙaramar Linux.

A yau strace kawai yana goyan bayan Linux kuma yana dogara iri ɗaya ptrace, cike da kari da yawa.

Mai kula da zamani (kuma mai aiki sosai). strace - Dmitry Levin. Godiya a gare shi, mai amfani ya sami abubuwan ci gaba kamar kuskuren allurar cikin kiran tsarin, tallafi don kewayon gine-gine da, mafi mahimmanci, mascot. Majiyoyin da ba na hukuma ba sun yi iƙirarin cewa zaɓin ya faɗi a kan jimina saboda haɗin kai tsakanin kalmar Rasha "jimina" da kalmar Ingilishi "strace".

Hakanan yana da mahimmanci cewa kiran tsarin ptrace da masu ganowa ba a taɓa haɗa su cikin POSIX ba, duk da dogon tarihi da aiwatarwa a cikin Linux, FreeBSD, OpenBSD da Unix na gargajiya.

Na'urar madaidaici a takaice: Piglet Trace

"Ba a sa ran za ku fahimci wannan ba" (Dennis Ritchie, sharhi a cikin Sigar 6 Unix lambar tushe)

Tun daga farkon yara, ba zan iya tsayawa baƙar fata: Ban yi wasa da kayan wasa ba, amma na yi ƙoƙarin fahimtar tsarin su (manyan sunyi amfani da kalmar "karye," amma kada ku yarda da mugayen harsuna). Wataƙila wannan shine dalilin da ya sa al'adun gargajiya na Unix na farko da ƙungiyoyin buɗe ido na zamani suna kusa da ni.

Don dalilan wannan labarin, ba daidai ba ne a tarwatsa lambar tushe na strace, wanda ya girma cikin shekaru da yawa. Amma bai kamata a bar sirrin masu karatu ba. Sabili da haka, don nuna ka'idar aiki na irin waɗannan shirye-shiryen strace, zan samar da lambar don ƙaramin ma'auni - Trace Piglet (ptr). Ba ta san yadda ake yin wani abu na musamman ba, amma babban abu shine kiran tsarin tsarin - yana fitar da:

$ 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

Trace Piglet ya gane kusan ɗaruruwan kiran tsarin Linux (duba. tebur) kuma kawai yana aiki akan gine-ginen x86-64. Wannan ya isa ga dalilai na ilimi.

Bari mu dubi aikin mu clone. A cikin yanayin Linux, masu gyara kurakurai da masu gano suna amfani da su, kamar yadda aka ambata a sama, tsarin ptrace yana kira. Yana aiki ta hanyar wucewa a cikin hujja ta farko masu gano umarni, wanda kawai muke buƙata PTRACE_TRACEME, PTRACE_SYSCALL и PTRACE_GETREGS.

Mai binciken yana farawa a cikin salon Unix da aka saba: fork(2) kaddamar da tsarin yara, wanda kuma yana amfani da shi exec(3) kaddamar da shirin da ake nazari. Iyakar dabara anan shine kalubale ptrace(PTRACE_TRACEME) kafin exec: Tsarin yaro yana tsammanin tsarin iyaye ya sa ido akan shi:

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

Dole ne tsarin iyaye ya kira yanzu wait(2) a cikin tsarin yara, wato, tabbatar da cewa canzawa zuwa yanayin gano ya faru:

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

A wannan gaba, shirye-shiryen sun cika kuma zaku iya ci gaba kai tsaye zuwa kiran tsarin sa ido a cikin madauki mara iyaka.

Kira ptrace(PTRACE_SYSCALL) ya tabbatar da haka wait iyaye za su kammala ko dai kafin a aiwatar da kiran tsarin ko kuma nan da nan bayan ya kammala. Tsakanin kira guda biyu zaka iya yin kowane ayyuka: maye gurbin kira tare da madadin ɗaya, canza gardama ko ƙimar dawowa.

Muna buƙatar kiran umarnin sau biyu kawai ptrace(PTRACE_GETREGS)don samun jihar rajista rax kafin kiran (lambar kiran tsarin) da kuma nan da nan bayan (ƙimar dawowa).

A zahiri, zagayowar:

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

Shi ke nan gaba xaya. Yanzu kun san inda za ku fara jigilar kaya na gaba DTrace na Linux.

Mahimmanci: gudanar da tsarin aiki mai gudana

A matsayin yanayin amfani na farko strace, watakila yana da daraja ambaton hanya mafi sauƙi - ƙaddamar da aikace-aikacen yana gudana strace.

Don kada mu shiga cikin jerin kira na yau da kullun na shirin, muna rubutawa mafi ƙarancin shirin kewaye 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;
}

Bari mu gina shirin kuma mu tabbatar yana aiki:

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

Kuma a ƙarshe, bari mu gudanar da shi ƙarƙashin sarrafa 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)                           = ?

“Maganar magana” sosai kuma ba ilimi sosai ba. Akwai matsaloli guda biyu a nan: an gauraya fitowar shirin tare da fitarwa strace da kuma yawan kira na tsarin da ba sa sha'awar mu.

Kuna iya raba daidaitaccen madaidaicin fitarwa na shirin da fitowar kurakurai ta hanyar amfani da -o switch, wanda ke tura jerin kiran tsarin zuwa fayil ɗin gardama.

Ya rage don magance matsalar "ƙarin" kira. Bari mu ɗauka cewa muna sha'awar kira kawai write. Maɓalli -e yana ba ku damar tantance kalmomin ta wacce tsarin kiran kira za a tace. Mafi mashahuri zaɓin yanayin shine, ta halitta, trace=*, wanda zaku iya barin kawai kiran da ke sha'awar mu.

Lokacin amfani da lokaci guda -o и -e za mu samu:

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

Don haka, kun ga, ya fi sauƙin karantawa.

Hakanan zaka iya cire kiran tsarin, misali waɗanda ke da alaƙa da rarraba ƙwaƙwalwar ajiya da 'yantar da su:

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

Kula da alamar tsawa ta kuɓuta a cikin jerin keɓe kira: wannan harsashi na umarni yana buƙata. harsashi).

A cikin sigar na glibc, kiran tsarin yana ƙare aikin exit_group, ba na gargajiya ba _exit. Wannan shine wahalar aiki tare da kiran tsarin: haɗin da mai tsara shirye-shirye ke aiki dashi ba shi da alaƙa kai tsaye da kiran tsarin. Bugu da ƙari, yana canzawa akai-akai dangane da aiwatarwa da dandamali.

Basics: shiga cikin tsari a kan tashi

Da farko, tsarin ptrace yana kira wanda aka gina shi strace, za a iya amfani da shi kawai lokacin gudanar da shirin a cikin yanayi na musamman. Wataƙila wannan ƙayyadaddun ƙayyadaddun ya yi sauti mai ma'ana a zamanin Sigar 6 Unix. A zamanin yau, wannan bai isa ba: wani lokacin kuna buƙatar bincika matsalolin shirin aiki. Misali na yau da kullun shine tsari da aka toshe akan hannu ko barci. Don haka na zamani strace zai iya shiga matakai akan tashi.

Misali mai daskarewa shirye-shirye:

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

Mu gina shirin kuma mu tabbatar ya daskare:

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

Yanzu bari mu gwada shiga ta:

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

An katange shirin ta hanyar kira pause. Bari mu ga yadda ta ke mayar da martani ga alamun:

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

Mun kaddamar da daskararre shirin kuma muka shiga ta amfani da shi strace. Abubuwa biyu sun bayyana a sarari: tsarin dakatar da kiran yana watsi da sigina ba tare da masu sarrafawa ba kuma, mafi ban sha'awa, layin sa ido ba kiran tsarin kawai ba, har ma da sigina masu shigowa.

Misali: Bibiyar Tsarin Yara

Yin aiki tare da matakai ta hanyar kira fork - tushen duk Unixes. Bari mu ga yadda strace ke aiki tare da bishiyar tsari ta amfani da misalin “kiwo” mai sauƙi. shirye-shirye:

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

Anan tsarin asali yana haifar da tsarin yaro, duka biyu suna rubutu zuwa daidaitaccen fitarwa:

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

Ta hanyar tsoho, za mu ga kiran tsarin ne kawai daga tsarin iyaye:

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

Tuta tana taimaka muku waƙa da duk bishiyar tsari -f, wanda strace saka idanu tsarin kira a cikin yaro matakai. Wannan yana ƙara zuwa kowane layi na fitarwa pid tsarin da ke yin fitar da tsarin:

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

A cikin wannan mahallin, tacewa ta ƙungiyar tsarin kira na iya zama da amfani:

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

Af, wane kira tsarin ake amfani dashi don ƙirƙirar sabon tsari?

Misali: hanyoyin fayil maimakon hannaye

Sanin masu bayanin fayil tabbas yana da amfani, amma sunayen takamaiman fayilolin da shirin ke shiga zasu iya zuwa da amfani.

Na gaba shirin ya rubuta layin zuwa fayil ɗin wucin gadi:

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

Yayin kiran al'ada strace zai nuna darajar lambar bayanin da aka wuce zuwa tsarin kiran tsarin:

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

Tare da tuta -y Mai amfani yana nuna hanyar zuwa fayil ɗin wanda mai siffantawa ya dace da shi:

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

Misali: Bibiyar Samun Fayil

Wani fasali mai amfani: nuni kawai kiran tsarin da ke da alaƙa da takamaiman fayil. Na gaba shirin yana haɗa layi zuwa fayil ɗin sabani da aka wuce azaman hujja:

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

da default strace yana nuna bayanan da ba dole ba. Tuta -P tare da hujja yana haifar da layi don buga kira kawai zuwa takamaiman fayil:

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

Misali: Shirye-shiryen Maɗaukaki Mai Rubutu

Mai amfani strace Hakanan zai iya taimakawa lokacin aiki tare da zaren multi-threaded shirin. Shirin mai zuwa yana rubutawa zuwa daidaitaccen fitarwa daga magudanan ruwa guda biyu:

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

A dabi'a, dole ne a haɗa shi tare da gaisuwa ta musamman ga mahaɗa - tuta -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
$

Flag -f, kamar yadda yake a cikin al'amuran yau da kullum, zai ƙara pid na tsari zuwa farkon kowane layi.

A zahiri, ba muna magana ne game da mai gano zaren a ma'anar aiwatar da ma'aunin POSIX Threads ba, amma game da lambar da mai tsara ɗawainiya ke amfani da shi a cikin Linux. Daga ra'ayi na ƙarshe, babu matakai ko zaren - akwai ayyukan da ake buƙatar rarrabawa a tsakanin abubuwan da ke cikin na'ura.

Lokacin aiki a cikin zaren da yawa, kiran tsarin ya yi yawa:

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

Yana da ma'ana don iyakance kanka ga sarrafa sarrafawa da kiran tsarin kawai 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 +++

Af, tambayoyi. Wane tsarin kira ake amfani dashi don ƙirƙirar sabon zaren? Ta yaya wannan kiran na zaren ya bambanta da kiran matakai?

Babban aji: tsari tari a lokacin kiran tsarin

Daya daga cikin kwanan nan ya bayyana strace iyawa - nuna tarin kira na aiki a lokacin kiran tsarin. Sauƙi misali:

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

A zahiri, fitowar shirin ya zama mai girma sosai, kuma, ban da tuta -k (nunin tari na kira), yana da ma'ana don tace kiran tsarin da suna:

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

Babban aji: kuskuren allura

Kuma ƙarin sabon abu kuma mai amfani sosai: allurar kuskure. nan shirin, rubuta layi biyu zuwa rafin fitarwa:

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

Bari mu gano duka rubuta kira:

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

Yanzu muna amfani da magana injectdon saka kuskure EBADF a duk rubuta kira:

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

Yana da ban sha'awa abin da aka dawo da kurakurai duk kalubale write, gami da kiran da ke boye a bayan ta'addanci. Yana da ma'ana kawai don dawo da kuskure don farkon kiran:

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

Ko kuma na biyu:

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

Ba lallai ba ne a saka nau'in kuskuren:

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

A haɗe tare da wasu tutoci, zaku iya “karye” samun dama ga takamaiman fayil. Misali:

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

Bayan kuskuren allura, iya gabatar da jinkiri lokacin yin kira ko karɓar sigina.

Bayanword

Mai amfani strace - kayan aiki mai sauƙi kuma abin dogara. Amma ban da kiran tsarin, ana iya cire sauran bangarorin aikin shirye-shirye da tsarin aiki. Misali, yana iya bin diddigin kira zuwa ɗakunan karatu masu alaƙa da ƙarfi. ltrace, za su iya duba cikin aiki na tsarin aiki SystemTap и zaba, kuma yana ba ku damar bincika aikin shirin sosai cikakke. Duk da haka, shi ne strace - layin farko na tsaro idan akwai matsala tare da shirye-shiryen kaina da sauran mutane, kuma ina amfani da shi akalla sau biyu a mako.

A takaice, idan kuna son Unix, karanta man 1 strace kuma ku ji daɗin kallon shirye-shiryenku!

source: www.habr.com

Add a comment