Nyob rau hauv Unix-zoo li kev khiav hauj lwm systems, ib qho kev pab cuam kev sib txuas lus nrog lub ntiaj teb sab nraud thiab cov kev khiav hauj lwm qhov system tshwm sim los ntawm ib tug me me lub luag hauj lwm - system hu. Qhov no txhais tau hais tias rau kev debugging lub hom phiaj nws tuaj yeem pab tau rau cov neeg soj xyuas ntawm kev hu xov tooj raug tua los ntawm cov txheej txheem.
Lub ntsiab interface ntawm cov kev pab cuam thiab OS kernel hauv Unix yog kev hu xov tooj. hu xovtooj, syscalls), kev sib cuam tshuam ntawm cov kev pab cuam nrog lub ntiaj teb sab nraud tshwm sim tshwj xeeb los ntawm lawv.
Tab sis nyob rau hauv thawj pej xeem version ntawm Unix (Version 6 Unix, 1975) tsis muaj txoj hauv kev yooj yim los taug qab tus cwj pwm ntawm cov neeg siv cov txheej txheem. Txhawm rau daws qhov teeb meem no, Tswb Labs yuav hloov kho mus rau lwm qhov version (Version 7 Unix, 1979) tau thov ib qho kev hu xov tooj tshiab - ptrace.
ptrace tau tsim feem ntau rau kev sib tham debuggers, tab sis thaum kawg ntawm 80s (nyob rau hauv lub era ntawm kev lag luam. System V tso 4) ntawm lub hauv paus no, nqaim tsom debuggers-system hu tracers-pom thiab tau siv dav.
Ua Ntej tib cov qauv ntawm txoj hlua tau luam tawm los ntawm Paul Cronenburg ntawm daim ntawv teev npe hauv comp.sources.sun hauv 1992 raws li lwm txoj hauv kev siv hluav taws xob kaw. trace los ntawm Sun. Ob lub clone thiab tus thawj yog npaj rau SunOS, tab sis los ntawm 1994 strace tau xa mus rau System V, Solaris thiab Linux nrov zuj zus.
Niaj hnub no strace tsuas yog txhawb nqa Linux thiab tso siab rau tib yam ptrace, overgrown nrog ntau extensions.
Niaj hnub nimno (thiab nquag heev) tus saib xyuas strace - Dmitry Levin. Ua tsaug rau nws, cov nqi hluav taws xob tau txais cov yam ntxwv zoo xws li kev txhaj tshuaj yuam kev rau hauv kev hu xov tooj, kev txhawb nqa rau ntau yam kev tsim vaj tsev thiab, qhov tseem ceeb tshaj plaws, mascot. Cov ntaub ntawv tsis raug cai hais tias qhov kev xaiv poob rau ntawm tus noog vim yog kev sib raug zoo ntawm lo lus Lavxias "ostrich" thiab lo lus Askiv "strace".
"Koj tsis xav kom nkag siab qhov no" (Dennis Ritchie, tawm tswv yim hauv Version 6 Unix qhov chaws)
Txij li thaum yau, kuv tsis tuaj yeem sawv ntsug lub thawv dub: Kuv tsis tau ua si nrog cov khoom ua si, tab sis sim nkag siab txog lawv cov qauv (cov laus siv lo lus "tawg," tab sis tsis txhob ntseeg tus nplaig phem). Tej zaum qhov no yog vim li cas cov kab lis kev cai tsis raug cai ntawm thawj Unix thiab cov kev hloov pauv niaj hnub no tau nyob ze rau kuv.
Rau lub hom phiaj ntawm tsab xov xwm no, nws yog tsis tsim nyog rau disassemble lub hauv paus code ntawm strace, uas tau loj hlob ntau xyoo lawm. Tab sis yuav tsum tsis muaj secrets tshuav rau cov nyeem. Yog li ntawd, los qhia txog lub hauv paus ntsiab lus ntawm kev ua haujlwm ntawm cov kev pab cuam zoo li no, kuv yuav muab cov cai rau tus neeg taug qab me me - Piglet Trace (ptr). Nws tsis paub yuav ua li cas tshwj xeeb, tab sis qhov tseem ceeb tshaj plaws yog lub kaw lus hu ntawm qhov kev pab cuam - nws outputs:
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");
}
Cov txheej txheem niam txiv yuav tsum hu tam sim no wait(2) nyob rau hauv tus txheej txheem menyuam yaus, uas yog, xyuas kom meej tias kev hloov mus rau hom kab mob tau tshwm sim:
/* 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");
Lub sijhawm no, cov kev npaj ua tiav thiab koj tuaj yeem mus ncaj qha mus rau qhov kev hu xov tooj hauv lub voj voog tsis kawg.
Hu ptrace(PTRACE_SYSCALL) lav tias tom qab ntawd wait niam txiv yuav ua kom tiav ua ntej qhov kev hu xov tooj raug tua lossis tam sim ntawd tom qab ua tiav. Nruab nrab ntawm ob hu koj tuaj yeem ua ib qho kev nqis tes ua: hloov kev hu nrog lwm qhov, hloov cov lus sib cav lossis tus nqi rov qab.
Peb tsuas yog xav tau hu rau cov lus txib ob zaug ptrace(PTRACE_GETREGS)kom tau lub xeev register rax ua ntej hu (system hu xov tooj) thiab tam sim ntawd tom qab (rov qab tus nqi).
Qhov tseeb, lub voj voog:
/* 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, ®isters) == -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, ®isters) == -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);
}
Qhov ntawd yog tag nrho cov tracer. Tam sim no koj paub qhov twg yuav pib qhov chaw nres nkoj tom ntej DTrace ntawm Linux.
Basics: khiav ib qhov kev pab cuam khiav strace
Raws li kev siv thawj zaug strace, tej zaum nws tsim nyog hais txog txoj kev yooj yim tshaj plaws - tso tawm daim ntawv thov khiav strace.
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;
}
Cia peb tsim qhov program thiab xyuas kom nws ua haujlwm:
$ gcc examples/write-simple.c -o write-simple
$ ./write-simple
write me to stdout
$ ./write-sleep &
[1] 15329
write me
$ strace -p 15329
strace: Process 15329 attached
pause(
^Cstrace: Process 15329 detached
<detached ...>
Program thaiv los ntawm kev hu pause. Cia peb saib seb nws yuav ua li cas rau cov teeb liab:
$ 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 +++
Peb pib qhov kev pab cuam khov thiab koom nrog nws siv strace. Ob yam dhau los ua qhov tseeb: lub kaw lus ncua hu tsis quav ntsej cov teeb liab yam tsis muaj tus tuav thiab, qhov zoo dua, kev saib xyuas tsis yog tsuas yog hu xov tooj xwb, tab sis kuj tseem muaj cov teeb liab tawm.
Piv txwv: Taug qab Cov Txheej Txheem Me Nyuam
Ua haujlwm nrog cov txheej txheem los ntawm kev hu fork - lub hauv paus ntawm tag nrho Unixes. Cia peb saib yuav ua li cas strace ua haujlwm nrog cov txheej txheem ntoo siv qhov piv txwv ntawm "kev yug me nyuam" yooj yim cov kev pab cuam:
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);
}
Ntawm no tus txheej txheem qub tsim cov txheej txheem menyuam yaus, ob qho tib si sau rau cov qauv tsim tawm:
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;
}
Thaum ib txwm hu strace yuav qhia tus nqi ntawm tus lej piav qhia dhau mus rau kev hu xov tooj:
$ 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 +++
Nrog tus chij -y Tus nqi hluav taws xob qhia txoj hauv kev rau cov ntaub ntawv uas tus piav qhia sib raug:
$ 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 +++
Piv txwv: Cov ntaub ntawv nkag mus
Lwm qhov muaj txiaj ntsig zoo: tso saib tsuas yog hu xov tooj cuam tshuam nrog cov ntaub ntawv tshwj xeeb. Tom ntej no qhov kev zov me nyuam appends ib kab rau cov ntaub ntawv arbitrary dhau los ua ib qho kev sib cav:
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;
}
$ 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 +++
Piv txwv: Multithreaded Programs
Π’ΠΈΠ»ΠΈΡΠ° strace tuaj yeem pab tau thaum ua haujlwm nrog ntau txoj xov qhov kev pab cuam. Cov kev pab cuam hauv qab no sau rau cov qauv tsim tawm los ntawm ob lub kwj:
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);
}
Lawm, nws yuav tsum tau muab tso ua ke nrog tshwj xeeb txais tos rau tus linker - tus chij -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
$
Los ntawm txoj kev, cov lus nug. Qhov kev hu xov tooj twg yog siv los tsim cov xov tshiab? Qhov kev hu xov tooj no txawv li cas ntawm kev hu rau cov txheej txheem?
Master chav kawm: txheej txheej txheej thaum lub sijhawm hu xov tooj
Ib qho ntawm cov tsis ntev los no tau tshwm sim strace peev xwm - tso saib cov pawg ntawm kev hu xov tooj thaum lub sijhawm hu xov tooj. Yooj yim Piv txwv:
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;
}
Lawm, qhov kev pab cuam tso zis ntau voluminous, thiab, ntxiv rau tus chij -k (hu rau pawg zaub), nws ua rau kev nkag siab los lim cov npe hu los ntawm lub npe:
$ 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 +++
Master chav kawm: yuam kev txhaj tshuaj
Thiab ib qho ntxiv tshiab thiab muaj txiaj ntsig zoo heev: kev txhaj tshuaj yuam kev. Ntawm no qhov kev zov me nyuam, sau ob kab rau cov zis tso zis:
Nws yog nthuav dab tsi yuam kev rov qab tag nrho kev sib tw write, suav nrog kev hu xov tooj zais tom qab yuam kev. Nws tsuas yog ua rau kev txiav txim siab rov qab ua yuam kev rau thawj zaug ntawm kev hu:
$ 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 +++
Los yog qhov thib ob:
$ 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 +++