Strace in Linux: stair, dearadh agus úsáid

Strace in Linux: stair, dearadh agus úsáid

I gcórais oibriúcháin cosúil le Unix, tarlaíonn cumarsáid clár leis an domhan lasmuigh agus leis an gcóras oibriúcháin trí shraith bheag feidhmeanna - glaonna córais. Ciallaíonn sé seo gur féidir, chun críocha dífhabhtaithe, a bheith úsáideach spy a dhéanamh ar ghlaonna córais atá á gcur i gcrích ag próisis.

Cabhraíonn fóntais leat monatóireacht a dhéanamh ar “shaol pearsanta” na gclár ar Linux strace, atá mar ábhar an ailt seo. Tá stair ghairid ag gabháil le samplaí d’úsáid trealaimh spiaireachta strace agus cur síos ar dhearadh na gclár sin.

Ábhar

Bunús na speiceas

Is é an príomh-chomhéadan idir cláir agus an eithne OS in Unix ná glaonna córais. glaonna córais, córais), tarlaíonn idirghníomhú clár leis an domhan lasmuigh go heisiach tríothu.

Ach sa chéad leagan poiblí de Unix (Leagan 6 Unix, 1975) nach raibh aon bhealaí áisiúla ann chun iompar próisis úsáideoirí a rianú. Chun an fhadhb seo a réiteach, nuashonróidh Bell Labs go dtí an chéad leagan eile (Leagan 7 Unix, 1979) glao córais nua molta - ptrace.

forbraíodh ptrace go príomha le haghaidh dífhabhtóirí idirghníomhacha, ach faoi dheireadh na 80í (i ré na tráchtála Córas V Eisiúint 4) ar an mbonn sin, tháinig dífhabhtóirí cúng-dírithe - rianairí glaonna córais - le feiceáil agus baineadh úsáid forleathan astu.

An chéad d'fhoilsigh Paul Cronenburg an leagan céanna de strace ar liosta seoltaí comp.sources.sun i 1992 mar mhalairt ar áirgiúlacht dúnta trace ón nGrian. Bhí an clón agus an bunleagan ceaptha do SunOS, ach faoi 1994 strace a aistriú chuig Córas V, Solaris agus an Linux atá ag éirí níos coitianta.

Ní thacaíonn strace inniu ach le Linux agus braitheann sé ar an gcéanna ptrace, overgrown le go leor síntí.

Cothaitheoir nua-aimseartha (agus an-ghníomhach). strace - Dmitry Levin. A bhuíochas leis, fuair an áirgiúlacht ardghnéithe amhail instealladh earráide i nglaonna córais, tacaíocht do raon leathan ailtireachtaí agus, níos tábhachtaí fós, mascot. Maíonn foinsí neamhoifigiúla gur thit an rogha ar an ostrais mar gheall ar an gconsan idir an focal Rúisise “ostrich” agus an focal Béarla “strace”.

Tá sé tábhachtach freisin nach raibh glao agus rianairí an chórais ptrace san áireamh i POSIX, in ainneoin stair fhada agus cur i bhfeidhm i Linux, FreeBSD, OpenBSD agus Unix traidisiúnta.

Gléas Strace in achomair: Trace Piglet

"Ní mheastar go dtuigeann tú é seo" (Dennis Ritchie, déan trácht i gcód foinse Leagan 6 Unix)

Ón luath-óige, ní féidir liom boscaí dubha a sheasamh: níor imir mé le bréagáin, ach rinne mé iarracht a struchtúr a thuiscint (d'úsáid daoine fásta an focal "bhris," ach ní chreideann siad na teangacha olc). B’fhéidir gurb é sin an fáth go bhfuil cultúr neamhfhoirmiúil an chéad Unix agus an ghluaiseacht foinse oscailte nua-aimseartha chomh gar domsa.

Chun críocha an ailt seo, tá sé míréasúnta an cód foinse strace a dhíchóimeáil, atá tar éis fás le blianta fada anuas. Ach níor cheart go mbeadh aon rúin fágtha do léitheoirí. Dá bhrí sin, chun prionsabal oibríochta cláir strace den sórt sin a thaispeáint, soláthróidh mé an cód le haghaidh rianaitheoir mionsamhla - Rian Muc (ptr). Níl a fhios aige conas aon rud speisialta a dhéanamh, ach is é an rud is mó ná glaonna córais an chláir - aschuir sé:

$ 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

Aithníonn Piglet Trace na céadta glaonna córais Linux (féach. tábla) agus ní oibríonn sé ach ar ailtireacht x86-64. Is leor é seo chun críocha oideachais.

Féachaimis ar obair ár gclón. I gcás Linux, úsáideann dífhabhtóirí agus rianairí, mar a luadh thuas, glao an chórais ptrace. Oibríonn sé trí na haitheantóirí ordaithe a chur isteach sa chéad argóint, nach bhfuil de dhíth orainn ach astu PTRACE_TRACEME, PTRACE_SYSCALL и PTRACE_GETREGS.

Tosaíonn an rianaitheoir sa ghnáth-stíl Unix: fork(2) Seolann próiseas linbh, a úsáideann ar a seal exec(3) seolann an clár atá á staidéar. Is é an t-aon subtlety anseo an dúshlán ptrace(PTRACE_TRACEME) roimh exec: Tá an próiseas linbh ag súil go ndéanfaidh an próiseas tuismitheora monatóireacht 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");
}

Ba cheart don phróiseas tuismitheora glaoch anois wait(2) i bpróiseas an linbh, is é sin, cinntigh gur tharla athrú go modh rianaithe:

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

Ag an bpointe seo, tá na hullmhúcháin críochnaithe agus is féidir leat dul ar aghaidh go díreach chuig glaonna córais rianaithe i lúb gan teorainn.

Glaoigh ptrace(PTRACE_SYSCALL) ráthaíonn sé sin ina dhiaidh sin wait críochnóidh an tuismitheoir sula gcuirfear an glao córais i gcrích nó díreach tar éis é a chríochnú. Idir dhá ghlao is féidir leat aon ghníomh a dhéanamh: glao eile a chur in ionad an ghlao, athraigh na hargóintí nó an luach fillte.

Ní mór dúinn ach an t-ordú a ghlaoch faoi dhó ptrace(PTRACE_GETREGS)chun an stát clárúcháin a fháil rax roimh an nglao (uimhir ghlao córais) agus díreach tar éis (luach fillte).

I ndáiríre, an timthriall:

/* 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 rianaire ar fad. Anois tá a fhios agat cá háit le tosú ar an gcéad aistriú eile DTrace ar Linux.

Bunúsacha: clár a rith ag rith strae

Mar chás den chéad úsáid strace, b'fhéidir gur fiú an modh is simplí a lua - feidhmchlár a sheoladh strace.

D'fhonn gan dul isteach ar an liosta gan teorainn glaonna ar ghnáthchlár, scríobhaimid clár íosta timpeall 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;
}

Déanaimis an clár a thógáil agus a chinntiú go n-oibríonn sé:

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

Agus ar deireadh, déanaimis é a rith faoi smacht géar:

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

An-"focal" agus ní an-oideachasúil. Tá dhá fhadhb anseo: measctar aschur an chláir leis an aschur strace agus raidhse glaonna córais nach bhfuil spéis againn iontu.

Is féidir leat sruth aschuir caighdeánach an chláir agus aschur earráide strace a scaradh trí úsáid a bhaint as an lasc -o, a atreoraíonn liosta na nglaonna córais chuig comhad argóinte.

Tá sé fós le déileáil le fadhb na nglaonna “breise”. Glacaimid leis nach bhfuil suim againn ach i nglaonna write. eochair -e is féidir leat na habairtí a shonrú trína ndéanfar glaonna córais a scagadh. Is é an rogha riocht is coitianta, go nádúrtha, trace=*, nach féidir leat a fhágáil ach na glaonna a bhfuil suim againn iontu.

Nuair a úsáidtear go comhuaineach -o и -e gheobhaimid:

$ 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, feiceann tú, tá sé i bhfad níos éasca é a léamh.

Is féidir leat glaonna córais a bhaint freisin, mar shampla iad siúd a bhaineann le leithdháileadh cuimhne agus saoráil cuimhne:

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

Tabhair faoi deara an comhartha exclamation éalaigh i liosta na nglaonna eisiata: tá sé seo ag teastáil ag an bhlaosc ordaithe. bhlaosc).

I mo leagan de glibc, cuireann glao córais deireadh leis an bpróiseas exit_group, ní traidisiúnta _exit. Is é seo an deacracht a bhaineann le bheith ag obair le glaonna córais: níl baint dhíreach ag an gcomhéadan lena n-oibríonn an ríomhchláraitheoir le glaonna córais. Thairis sin, athraíonn sé go rialta ag brath ar chur i bhfeidhm agus ardán.

Basics: a cheanglaíonn an próiseas ar an eitilt

Ar dtús, glaoigh an córas ptrace ar a tógadh é strace, ní fhéadfaí é a úsáid ach amháin nuair a bhíonn an clár á rith i mód speisialta. D’fhéadfadh go mbeadh cuma réasúnta ar an teorannú seo i laethanta Leagan 6 Unix. Sa lá atá inniu ann, ní leor é seo a thuilleadh: uaireanta ní mór duit na fadhbanna a bhaineann le clár oibre a fhiosrú. Is sampla tipiciúil é próiseas a gcuirtear bac ar láimhseáil nó a chodladh. Dá bhrí sin nua-aimseartha strace is féidir próisis a bheith páirteach ar an eitilt.

Sampla reo cláir:

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

Déanaimis an clár a thógáil agus a chinntiú go bhfuil sé reoite:

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

Anois déanaimis iarracht a bheith páirteach ann:

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

Tá an clár bac ar ghlao pause. Feicfimid conas a imoibríonn sí leis na comharthaí:

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

Sheolamar an clár reoite agus chuamar isteach ag baint úsáide as strace. Tháinig dhá rud chun solais: déanann glao an chórais sos neamhaird ar chomharthaí gan láimhseálaithe agus, níos suimiúla, déanann monatóireacht ar strace ní hamháin glaonna córais, ach freisin comharthaí ag teacht isteach.

Sampla: Próisis Leanaí a Rianú

Ag obair le próisis trí ghlao fork - bunús gach Unixes. Feicfimid conas a oibríonn strace le crann próisis ag baint úsáide as an sampla de “pórú” simplí cláir:

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

Anseo cruthaíonn an bunphróiseas próiseas linbh, agus an dá cheann ag scríobh chuig aschur caighdeánach:

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

De réir réamhshocraithe, ní fheicfimid ach glaonna córais ón máthairphróiseas:

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

Cuidíonn an bhratach leat an crann próiseas iomlán a rianú -f, a strace déanann sé monatóireacht ar ghlaonna córais i bpróisis leanaí. Cuireann sé seo le gach líne aschuir pid próiseas a dhéanann aschur córais:

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

Sa chomhthéacs seo, d’fhéadfadh scagadh de réir grúpa glaonna córais a bheith úsáideach:

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

Dála an scéil, cén glao córais a úsáidtear chun próiseas nua a chruthú?

Sampla: cosáin comhaid in ionad Láimhseálann

Is cinnte go bhfuil sé úsáideach tuairisceoirí comhaid a bheith ar an eolas, ach is féidir le hainmneacha na gcomhad ar leith a bhfuil rochtain ag clár orthu a bheith áisiúil freisin.

An chéad Clár scríobhann an líne chuig an gcomhad sealadach:

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

Le linn gnáthghlao strace taispeánfar luach uimhir an tuairisceora a cuireadh ar aghaidh chuig an nglao córais:

$ 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 Taispeánann an áirgiúlacht an cosán go dtí an comhad a gcomhfhreagraíonn an tuairisceoir dó:

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

Sampla: Rianú Rochtana Comhad

Gné úsáideach eile: ná taispeáin ach glaonna córais a bhaineann le comhad ar leith. Ar aghaidh Clár cuireann sé líne le comhad treallach a ritheadh ​​mar argóint:

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

De réir réamhshocraithe strace taispeánann sé go leor faisnéise neamhriachtanach. Bratach -P le hargóint is cúis le géar a phriontáil ach glaonna chuig an gcomhad sonraithe:

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

Sampla: Cláir Ilsnáithe

Fóntais strace cabhrú freisin agus iad ag obair le il-snáithithe an clár. Scríobhann an clár seo a leanas chuig aschur caighdeánach ó 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);
}

Ar ndóigh, ní mór é a thiomsú le beannacht speisialta don nascóir - an bhratach -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
$

Bratach -f, mar atá i gcás próisis rialta, cuirfidh sé pid an phróisis le tús gach líne.

Ar ndóigh, nílimid ag caint faoi aitheantóir snáithe sa chiall ar chur i bhfeidhm an chaighdeáin POSIX Threads, ach faoin uimhir a úsáideann an tasc sceidealóir i Linux. Ó thaobh an dara ceann de, níl aon phróisis nó snáitheanna ann - tá tascanna ann a chaithfear a dháileadh i measc na gcroí atá ar fáil den mheaisín.

Nuair a bhíonn roinnt snáitheanna ag obair, éiríonn an iomarca glaonna córais:

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

Déanann sé ciall tú féin a theorannú chuig glaonna bainistíochta agus córais amhá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 +++

Dála an scéil, ceisteanna. Cén glao córais a úsáidtear chun snáithe nua a chruthú? Cén difríocht atá idir an glao ar snáitheanna seo agus an glao ar phróisis?

Máistir-rang: stack próisis tráth glao córais

Ceann de na le feiceáil le déanaí strace cumais - ag taispeáint an chairn de ghlaonna feidhme tráth an ghlao ar an gcóras. Simplí mar shampla:

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

Ar ndóigh, éiríonn an t-aschur clár an-toirtiúil, agus, sa bhreis ar an bhratach -k (taispeáint cruachta glaonna), tá ciall le glaonna córais a scagadh de 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 +++

Máistir-rang: instealladh earráide

Agus gné nua agus an-úsáideach amháin eile: instealladh earráide. Anseo Clár, ag scríobh dhá líne leis an sruth aschuir:

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

Déanaimis an dá ghlao a scríobh:

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

Anois úsáidimid an slonn injectchun earráid a chur isteach EBADF i ngach glao scríofa:

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

Tá sé suimiúil cad iad na hearráidí a chuirtear ar ais go léir dúshláin write, lena n-áirítear an glaoch i bhfolach taobh thiar de terror. Ní dhéanann sé ciall ach earráid a sheoladh ar ais don chéad cheann de na glaonna:

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

Nó an dara ceann:

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

Ní gá an cineál earráide a shonrú:

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

I gcomhcheangal le bratacha eile, is féidir leat rochtain ar chomhad ar leith a “bhriseadh”. Sampla:

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

Seachas instealladh earráide, Is féidir ceann moilleanna a thabhairt isteach le linn glaonna a dhéanamh nó comharthaí a fháil.

Afterword

Fóntais strace - uirlis shimplí iontaofa. Ach i dteannta le glaonna córais, is féidir gnéithe eile d'oibriú na gclár agus an chórais oibriúcháin a dhífhabhtú. Mar shampla, is féidir leis glaonna chuig leabharlanna atá nasctha go dinimiciúil a rianú. ltrace, is féidir leo breathnú ar oibriú an chórais oibriúcháin SystemTap и ftraice, agus ligeann duit feidhmíocht cláir a imscrúdú go domhain foirfe. Mar sin féin, tá sé strace - an chéad líne chosanta i gcás fadhbanna le mo chuid féin agus cláir daoine eile, agus úsáidim é ar a laghad cúpla uair sa tseachtain.

I mbeagán focal, má tá grá agat ar Unix, léigh man 1 strace agus bíodh leisce ort breathnú ar do chuid clár!

Foinse: will.com

Add a comment