عصري (او ډیر فعال) ساتونکی strace - دمیتري لیوین. د هغه څخه مننه، افادیت پرمختللي ب featuresې ترلاسه کړې لکه د سیسټم تلیفونونو کې د خطا انجیکشن ، د پراخه جوړښتونو ملاتړ او خورا مهم ، ماسکوټ. غیر رسمي سرچینې ادعا کوي چې انتخاب د شتر مرغ په برخه کې د روسي کلمې "ostrich" او د انګلیسي کلمې "strace" ترمنځ د همغږۍ له امله راوتلی.
دا هم مهمه ده چې په لینکس، FreeBSD، OpenBSD او دودیز یونیکس کې د اوږد تاریخ او پلي کولو سره سره، د ptrace سیسټم کال او ټریسرز هیڅکله په POSIX کې شامل نه وو.
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");
}
/* 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");
ننګونې ptrace(PTRACE_SYSCALL) د دې وروسته تضمین کوي wait والدین به یا د سیسټم کال اجرا کیدو دمخه یا سمدستي وروسته بشپړ شي. د دوو زنګونو په مینځ کې تاسو کولی شئ هر ډول کړنې ترسره کړئ: زنګ د بدیل سره بدل کړئ، دلیلونه یا د بیرته ستنیدو ارزښت بدل کړئ.
موږ باید دوه ځله کمانډ ته زنګ ووهو ptrace(PTRACE_GETREGS)د راجستر حالت ترلاسه کولو لپاره rax د زنګ څخه دمخه (د سیسټم کال شمیره) او سمدلاسه وروسته (د بیرته ستنیدو ارزښت).
په حقیقت کې، دوره:
/* 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);
}
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;
}
راځئ چې برنامه جوړه کړو او ډاډ ترلاسه کړو چې دا کار کوي:
$ 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 ...>
برنامه د تلیفون له لارې بنده شوې pause. راځئ وګورو چې هغه سیګنالونو ته څنګه عکس العمل ښیې:
$ 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 +++
موږ منجمد برنامه پیل کړه او په کارولو سره یې یوځای شو strace. دوه شیان روښانه شول: د وقفې سیسټم کال د هینډلرونو پرته سیګنالونه له پامه غورځوي او په زړه پورې خبره دا ده چې سټریس مانیټرز نه یوازې د سیسټم تلیفونونه ، بلکه راتلونکي سیګنالونه هم.
بېلګه: د ماشومانو د پروسې تعقیب
د تلیفون له لارې د پروسو سره کار کول fork - د ټولو یونیکس اساس. راځئ وګورو چې سټریس څنګه د ساده "نسل" مثال په کارولو سره د پروسې ونې سره کار کوي پروګرامونه:
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);
}
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 ډیری غیر ضروري معلومات ښکاره کوي. بیرغ -P د استدلال سره د سټریس لامل کیږي چې یوازې ټاکل شوي فایل ته زنګونه چاپ کړي:
$ 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 +++
بېلګه: څو اړخیز پروګرامونه
افادیت strace هم مرسته کولی شي کله چې د څو تارونو سره کار کوي برنامه. لاندې برنامه د دوه جریانونو څخه معیاري محصول ته لیکي:
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);
}
په طبیعي توګه، دا باید لینکر ته د ځانګړي سلام سره ترتیب شي - د -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
$