Strace ann an Linux: eachdraidh, dealbhadh agus cleachdadh

Strace ann an Linux: eachdraidh, dealbhadh agus cleachdadh

Ann an siostaman obrachaidh coltach ri Unix, bidh conaltradh prògram leis an t-saoghal a-muigh agus an siostam obrachaidh a ’tachairt tro sheata bheag de dhleastanasan - gairmean siostam. Tha seo a’ ciallachadh gum faod e a bhith feumail airson adhbharan dì-bhugachaidh a bhith a’ spionadh air fiosan siostam a thèid a chuir gu bàs le pròiseasan.

Cuidichidh goireas thu le sùil a chumail air “beatha dlùth” phrògraman air Linux strace, a tha na chuspair san artaigil seo. An cois eisimpleirean de chleachdadh uidheamachd brathaidh tha eachdraidh ghoirid strace agus tuairisgeul air dealbhadh phrògraman mar sin.

Clàr-innse

Tùs nan gnèithean

Is e am prìomh eadar-aghaidh eadar prògraman agus kernel OS ann an Unix gairmean siostaim. gairmean siostam, sycalls), bidh eadar-obrachadh phrògraman leis an t-saoghal a-muigh a’ tachairt troimhe a-mhàin.

Ach anns a’ chiad dreach poblach de Unix (Tionndadh 6 Unix, 1975) cha robh dòighean freagarrach ann airson sùil a chumail air giùlan phròiseasan luchd-cleachdaidh. Gus a’ chùis seo fhuasgladh, bheir Bell Labs ùrachadh chun ath dhreach (Tionndadh 7 Unix, 1979) gairm siostam ùr a mholadh - ptrace.

Chaidh ptrace a leasachadh gu sònraichte airson debuggers eadar-ghnìomhach, ach ro dheireadh nan 80n (ann an àm malairteach Siostam V Sgaoileadh 4) air a’ bhunait seo, nochd debuggers le fòcas cumhang - lorgairean gairm siostam - agus chaidh an cleachdadh gu farsaing.

A 'chiad fhear chaidh an aon dreach de strace fhoillseachadh le Paul Cronenburg air liosta puist comp.sources.sun ann an 1992 mar roghainn eile seach goireas dùinte trace bhon Ghrian. Bha an dà chuid an clon agus an tè tùsail airson SunOS, ach ro 1994 strace a ghluasad gu System V, Solaris agus an Linux a tha a’ sìor fhàs mòr-chòrdte.

An-diugh chan eil strace a’ toirt taic ach do Linux agus tha e an urra ris an aon rud ptrace, air fàs gu mòr le mòran leudachadh.

Neach-gleidhidh ùr-nodha (agus glè ghnìomhach). strace - Dmitry Levin. Taing dha, fhuair an goireas feartan adhartach leithid in-stealladh mearachd ann an gairmean siostaim, taic airson raon farsaing de ailtireachd agus, nas cudromaiche, suaichnean. Tha stòran neo-oifigeil ag agairt gun do thuit an roghainn air an ostrich air sgàth a’ chonnspaid eadar am facal Ruiseanach “ostrich” agus am facal Beurla “strace”.

Tha e cuideachd cudromach nach deach gairm siostam ptrace agus lorgairean a thoirt a-steach do POSIX a-riamh, a dh’ aindeoin eachdraidh fhada agus buileachadh ann an Linux, FreeBSD, OpenBSD agus Unix traidiseanta.

Inneal Strace ann an ùine ghoirid: Piglet Trace

“Chan eilear an dùil gun tuig thu seo” (Dennis Ritchie, beachd ann an dreach 6 còd stòr Unix)

Bho òige, chan urrainn dhomh bogsaichean dubha a sheasamh: cha do chluich mi le dèideagan, ach dh'fheuch mi ris an structar aca a thuigsinn (chleachd inbhich am facal "bhris," ach na creid na droch theangan). Is dòcha gur e seo as coireach gu bheil cultar neo-fhoirmeil a’ chiad Unix agus an gluasad fosgailte ùr-nodha cho faisg orm.

Airson adhbharan an artaigil seo, tha e mì-reusanta an còd stòr strace a thoirt às a chèile, a tha air fàs thar deicheadan. Ach cha bu chòir dìomhaireachd sam bith a bhith air fhàgail airson luchd-leughaidh. Mar sin, gus prionnsapal obrachaidh phrògraman mar sin a nochdadh, bheir mi seachad an còd airson lorgaire beag - Lorg Piglet (ptr). Chan eil fios aige ciamar a nì e dad sònraichte, ach is e am prìomh rud fiosan siostam a ’phrògraim - bidh e a’ toirt a-mach:

$ 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

Tha Piglet Trace ag aithneachadh mu cheudan de ghairmean siostam Linux (faic. bòrd) agus dìreach ag obair air ailtireachd x86-64. Tha seo gu leòr airson adhbharan foghlaim.

Bheir sinn sùil air obair ar clone. A thaobh Linux, bidh luchd-debuggers agus lorgairean a’ cleachdadh, mar a chaidh ainmeachadh gu h-àrd, gairm siostam ptrace. Bidh e ag obair le bhith a’ toirt seachad a’ chiad argamaid na aithnichearan àithne, air nach eil feum againn ach air PTRACE_TRACEME, PTRACE_SYSCALL и PTRACE_GETREGS.

Bidh an tracer a’ tòiseachadh anns an stoidhle àbhaisteach Unix: fork(2) a 'cur air bhog pròiseas leanabh, a tha e an uair sin a' cleachdadh exec(3) a’ cur air bhog am prògram fo sgrùdadh. Is e an aon mhisneachd an seo an dùbhlan ptrace(PTRACE_TRACEME) перед exec: Tha pròiseas an leanaibh an dùil gun dèan am pròiseas pàrant sùil air:

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

Bu chòir don phròiseas pàrant gairm a-nis wait(2) ann am pròiseas an leanaibh, is e sin, dèan cinnteach gu bheil atharrachadh gu modh lorg air tachairt:

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

Aig an ìre seo, tha an ullachadh deiseil agus faodaidh tu a dhol air adhart gu dìreach gu bhith a ’cumail sùil air fiosan siostam ann an lùb gun chrìoch.

Dùbhlan ptrace(PTRACE_SYSCALL) a 'gealltainn sin às deidh sin wait crìochnaichidh pàrant an dàrna cuid mus tèid gairm an t-siostaim a chuir gu bàs no dìreach às deidh a chrìochnachadh. Eadar dà ghairm faodaidh tu gnìomhan sam bith a dhèanamh: fear eile a chuir na àite, atharraich na h-argamaidean no an luach tilleadh.

Feumaidh sinn dìreach an àithne a ghairm dà uair ptrace(PTRACE_GETREGS)gus an stàit clàraidh fhaighinn rax ron ghairm (àireamh gairm siostam) agus dìreach às deidh sin (luach tilleadh).

Gu dearbh, an cearcall:

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

Sin an tracer gu lèir. A-nis tha fios agad càite an tòisich thu air an ath phortadh DTrace air Linux.

Basics: a 'ruith prògram a' ruith strace

Mar chiad chùis cleachdaidh strace, 's dòcha gum b' fhiach iomradh a thoirt air an dòigh as sìmplidh - a 'cur air bhog iarrtas a' ruith strace.

Gus nach tèid sinn a-steach don liosta gun chrìoch de ghairmean prògram àbhaisteach, bidh sinn a’ sgrìobhadh prògram as ìsle timcheall 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;
}

Togamaid am prògram agus dèan cinnteach gu bheil e ag obair:

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

Agus mu dheireadh, leig dhuinn a ruith fo smachd teann:

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

Gu math “facalach” agus chan eil e gu math foghlaim. Tha dà dhuilgheadas an seo: tha toradh a’ phrògraim measgaichte leis an toradh strace agus pailteas de shiostaman fiosan nach eil inntinneach dhuinn.

Faodaidh tu sruth toraidh àbhaisteach a’ phrògraim agus toradh mearachd strace a sgaradh le bhith a’ cleachdadh an -o switch, a bhios ag ath-stiùireadh liosta nan gairmean siostaim gu faidhle argamaid.

Tha e fhathast gus dèiligeadh ris an duilgheadas a thaobh gairmean “a bharrachd”. Gabhamaid ris nach eil ùidh againn ach ann an gairmean write. iuchair -e a’ leigeil leat abairtean a shònrachadh leis an tèid gairmean siostam a chriathradh. Is e an roghainn suidheachadh as mòr-chòrdte, gu nàdarra, trace=*, leis nach urrainn dhut fhàgail ach na gairmean anns a bheil ùidh againn.

Nuair a thèid a chleachdadh aig an aon àm -o и -e gheibh sinn:

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

Mar sin, chì thu, tha e tòrr nas fhasa a leughadh.

Faodaidh tu cuideachd fiosan siostam a thoirt air falbh, mar eisimpleir an fheadhainn co-cheangailte ri riarachadh cuimhne agus saoradh:

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

Thoir an aire don chomharra clisgeadh a theich anns an liosta de ghlaodhan air an dùnadh a-mach: tha seo riatanach leis an t-slige àithne. sligean).

Anns an dreach agam de glibc, tha gairm siostam a’ cur crìoch air a’ phròiseas exit_group, chan e traidiseanta _exit. Is e seo an duilgheadas a bhith ag obair le gairmean siostam: chan eil an eadar-aghaidh leis a bheil am prògramadair ag obair ceangailte gu dìreach ri gairmean siostam. A bharrachd air an sin, bidh e ag atharrachadh gu cunbhalach a rèir an gnìomhachaidh agus an àrd-ùrlar.

Basics: a 'tighinn còmhla ris a' phròiseas air an itealan

An toiseach, ghairm an siostam ptrace air an deach a thogail strace, cha ghabh a chleachdadh ach nuair a ruitheas tu am prògram ann am modh sònraichte. Is dòcha gu robh an cuingealachadh seo air a bhith reusanta ann an làithean Tionndadh 6 Unix. An-diugh, chan eil seo gu leòr tuilleadh: uaireannan feumaidh tu sgrùdadh a dhèanamh air duilgheadasan prògram obrach. Is e eisimpleir àbhaisteach pròiseas a tha air a bhacadh air làmh no cadal. Mar sin nuadh-aimsireil strace faodaidh iad a dhol còmhla ri pròiseasan air an itealan.

Eisimpleir reothadh prògraman:

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

Nach tog sinn am prògram agus dèan cinnteach gu bheil e reòta:

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

A-nis feuchaidh sinn ri dhol còmhla ris:

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

Am prògram air a bhacadh le fòn pause. Chì sinn mar a dhèiligeas i ris na comharran:

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

Chuir sinn air bhog am prògram reòta agus chaidh sinn còmhla ris le bhith a’ cleachdadh strace. Dh’fhàs dà rud soilleir: tha gairm an t-siostam stad a’ seachnadh comharran gun luchd-làimhseachaidh agus, nas inntinniche, bidh sgrùdairean strace chan ann a-mhàin air fiosan siostam, ach cuideachd air comharran a tha a’ tighinn a-steach.

Eisimpleir: Lorg Pròiseasan Cloinne

Ag obair le pròiseasan tro ghairm fork - bunait a h-uile Unixes. Chì sinn mar a tha strace ag obair le craobh pròiseas a 'cleachdadh an eisimpleir de "briodadh" sìmplidh prògraman:

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

An seo tha am pròiseas tùsail a’ cruthachadh pròiseas cloinne, an dà chuid a’ sgrìobhadh gu toradh àbhaisteach:

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

Gu gnàthach, chan fhaic sinn ach fiosan siostam bhon phròiseas phàrant:

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

Cuidichidh am bratach thu le bhith a’ cumail sùil air craobh a’ phròiseas gu lèir -f, a tha strace a’ cumail sùil air fiosan siostam ann am pròiseasan cloinne. Bidh seo a’ cur ri gach loidhne toraidh pid pròiseas a tha a 'dèanamh toradh siostam:

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

Anns a’ cho-theacsa seo, faodaidh sìoladh a-rèir buidheann de ghairmean siostam a bhith feumail:

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

Air an t-slighe, dè an gairm siostam a thathas a 'cleachdadh gus pròiseas ùr a chruthachadh?

Eisimpleir: slighean faidhle an àite làmhan

Tha eòlas air tuairisgeulan fhaidhlichean gu cinnteach feumail, ach faodaidh ainmean nam faidhlichean sònraichte a gheibh prògram a bhith feumail cuideachd.

An ath fhear am prògram a’ sgrìobhadh na loidhne dhan fhaidhle sealach:

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

Aig àm gairm àbhaisteach strace seallaidh e luach an àireamh tuairisgeul a chaidh a chuir gu gairm an t-siostaim:

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

Le bratach -y Tha an goireas a’ sealltainn an t-slighe chun an fhaidhle ris a bheil an tuairisgeul a’ freagairt:

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

Eisimpleir: Tracadh Cothrom Faidhle

Feart feumail eile: na seall ach fiosan siostam co-cheangailte ri faidhle sònraichte. Air adhart am prògram a’ ceangal loidhne ri faidhle neo-riaghailteach a chaidh aontachadh mar argamaid:

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

bho thùs strace a’ taisbeanadh tòrr fiosrachaidh neo-riatanach. Bratach -P le argamaid ag adhbhrachadh gun tèid fiosan a chlò-bhualadh a-mhàin chun an fhaidhle ainmichte:

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

Eisimpleir: Prògraman ioma-shnàithichte

Goireasach strace faodaidh e cuideachd cuideachadh nuair a bhios tu ag obair le ioma-snàthainn am prògram. Bidh am prògram a leanas a’ sgrìobhadh gu toradh àbhaisteach bho dhà shruth:

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

Gu nàdarra, feumar a chuir ri chèile le fàilte shònraichte don cheangal - bratach -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
$

Didòmhnaich -f, mar a tha ann an cùis phròiseasan cunbhalach, cuiridh e pid a 'phròiseis gu toiseach gach loidhne.

Gu nàdarra, chan eil sinn a’ bruidhinn air aithnichear snàithlean a thaobh buileachadh inbhe POSIX Threads, ach mun àireamh a bhios an clàr-ama gnìomh a’ cleachdadh ann an Linux. Bho shealladh an fheadhainn mu dheireadh, chan eil pròiseasan no snàithleanan ann - tha gnìomhan ann a dh’ fheumar a sgaoileadh am measg na coraichean a tha rim faighinn san inneal.

Nuair a bhios tu ag obair ann an grunn snàithleanan, bidh gairmean siostam a’ fàs cus:

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

Tha e ciallach thu fhèin a chuingealachadh ri riaghladh pròiseas agus fiosan siostam a-mhàin 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 +++

Air an t-slighe, ceistean. Dè an gairm siostam a thathas a’ cleachdadh gus snàithlean ùr a chruthachadh? Ciamar a tha an gairm seo airson snàithleanan eadar-dhealaichte bhon ghairm airson pròiseasan?

Prìomh chlas: stac pròiseas aig àm gairm siostam

Nochd fear dhiubh o chionn ghoirid strace comasan - a’ taisbeanadh an stac de ghlaodhan gnìomh aig àm gairm an t-siostaim. Sìmplidh eisimpleir:

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

Gu nàdarra, bidh toradh a 'phrògraim a' fàs gu math voluminous, agus, a bharrachd air a 'bhratach -k (taisbeanadh stac gairm), tha e ciallach fiosan siostam a shìoladh a rèir ainm:

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

Prìomh chlas: in-stealladh mearachd

Agus aon fheart ùr agus glè fheumail eile: in-stealladh mearachd. Seo am prògram, a’ sgrìobhadh dà loidhne ris an t-sruth toraidh:

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

Feuch an lorg sinn an dà chuid gairmean a sgrìobhadh:

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

A-nis bidh sinn a 'cleachdadh an abairt injectgus mearachd a chuir a-steach EBADF anns a h-uile gairm sgrìobhte:

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

Tha e inntinneach dè na mearachdan a thèid a thilleadh uile dùbhlain write, a’ gabhail a-steach a’ ghairm a tha falaichte air cùl na h-eucoir. Chan eil e ciallach ach mearachd a thilleadh airson a’ chiad de na gairmean:

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

No an dàrna fear:

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

Chan eil e riatanach an seòrsa mearachd a shònrachadh:

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

Còmhla ri brataichean eile, faodaidh tu “briseadh” ruigsinneachd gu faidhle sònraichte. Eisimpleir:

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

A bharrachd air in-stealladh mearachd, urrainn thoir a-steach dàil nuair a bhios tu a’ dèanamh gairmean no a’ faighinn chomharran.

Post-d gu caraid

Goireasach strace - inneal sìmplidh agus earbsach. Ach a bharrachd air fiosan siostam, faodar taobhan eile de ghnìomhachd phrògraman agus den t-siostam obrachaidh a dhì-bhugachadh. Mar eisimpleir, faodaidh e sùil a chumail air fiosan gu leabharlannan le ceangal fiùghantach. l lorg, faodaidh iad coimhead a-steach do obrachadh an t-siostam obrachaidh SystemTap и ftraice, agus leigidh e leat sgrùdadh domhainn a dhèanamh air coileanadh prògram perf. A dh'aindeoin sin, tha e strace - a’ chiad loidhne dìon gun fhios nach bi duilgheadasan ann leis na prògraman agam fhìn agus daoine eile, agus bidh mi ga chleachdadh co-dhiù dà uair san t-seachdain.

Ann an ùine ghoirid, ma tha gaol agad air Unix, leugh man 1 strace agus na bi leisg sùil a thoirt air na prògraman agad!

Source: www.habr.com

Cuir beachd ann