diff options
author | Steven Rostedt <srostedt@redhat.com> | 2011-02-09 18:52:19 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2011-02-09 19:30:58 -0500 |
commit | 4896461d2e344dcac0aadbfef381c0760c860eae (patch) | |
tree | 31a40a1b1034c3ece83edea08dbfdbfae2d9bee7 | |
parent | 685cc721962012caad6ddf890bd601b7f5f1e5da (diff) |
trace-cmd: Use ptrace to trace children of the process
Since there's no good way to know when a child forks, and we
want to update the filters when it does. We must resort to ptrace.
Yes it sucks, but it works.
When performing a trace-cmd record -F -c program
The "-c" switch will cause trace-cmd to ptrace the program. If
the program forks, trace-cmd will update the filters to follow
the children of the program.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | trace-cmd.c | 109 | ||||
-rw-r--r-- | trace-usage.c | 1 |
2 files changed, 105 insertions, 5 deletions
diff --git a/trace-cmd.c b/trace-cmd.c index 5322e61..a287a3a 100644 --- a/trace-cmd.c +++ b/trace-cmd.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <sys/stat.h> | 28 | #include <sys/stat.h> |
29 | #include <sys/wait.h> | 29 | #include <sys/wait.h> |
30 | #include <sys/socket.h> | 30 | #include <sys/socket.h> |
31 | #include <sys/ptrace.h> | ||
31 | #include <netdb.h> | 32 | #include <netdb.h> |
32 | #include <pthread.h> | 33 | #include <pthread.h> |
33 | #include <fcntl.h> | 34 | #include <fcntl.h> |
@@ -76,9 +77,13 @@ static char *host; | |||
76 | static int *client_ports; | 77 | static int *client_ports; |
77 | static int sfd; | 78 | static int sfd; |
78 | 79 | ||
80 | static int do_ptrace; | ||
81 | |||
79 | static int filter_task; | 82 | static int filter_task; |
80 | static int filter_pid = -1; | 83 | static int filter_pid = -1; |
81 | 84 | ||
85 | static int finished; | ||
86 | |||
82 | struct func_list { | 87 | struct func_list { |
83 | struct func_list *next; | 88 | struct func_list *next; |
84 | const char *func; | 89 | const char *func; |
@@ -397,6 +402,89 @@ static void update_task_filter(void) | |||
397 | enable_tracing(); | 402 | enable_tracing(); |
398 | } | 403 | } |
399 | 404 | ||
405 | static void ptrace_attach(int pid) | ||
406 | { | ||
407 | int ret; | ||
408 | |||
409 | ret = ptrace(PTRACE_ATTACH, pid, NULL, 0); | ||
410 | if (ret < 0) { | ||
411 | warning("Unable to trace process %d children", pid); | ||
412 | do_ptrace = 0; | ||
413 | return; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | static void enable_ptrace(void) | ||
418 | { | ||
419 | if (!do_ptrace || !filter_task) | ||
420 | return; | ||
421 | |||
422 | ptrace(PTRACE_TRACEME, 0, NULL, 0); | ||
423 | } | ||
424 | |||
425 | static void ptrace_wait(int main_pid) | ||
426 | { | ||
427 | unsigned long send_sig; | ||
428 | unsigned long child; | ||
429 | siginfo_t sig; | ||
430 | int cstatus; | ||
431 | int status; | ||
432 | int event; | ||
433 | int pid; | ||
434 | int ret; | ||
435 | |||
436 | do { | ||
437 | ret = waitpid(-1, &status, WSTOPPED | __WALL); | ||
438 | if (ret < 0) | ||
439 | continue; | ||
440 | |||
441 | pid = ret; | ||
442 | |||
443 | if (WIFSTOPPED(status)) { | ||
444 | event = (status >> 16) & 0xff; | ||
445 | ptrace(PTRACE_GETSIGINFO, pid, NULL, &sig); | ||
446 | send_sig = sig.si_signo; | ||
447 | /* Don't send ptrace sigs to child */ | ||
448 | if (send_sig == SIGTRAP || send_sig == SIGSTOP) | ||
449 | send_sig = 0; | ||
450 | switch (event) { | ||
451 | case PTRACE_EVENT_FORK: | ||
452 | case PTRACE_EVENT_VFORK: | ||
453 | case PTRACE_EVENT_CLONE: | ||
454 | /* forked a child */ | ||
455 | ptrace(PTRACE_GETEVENTMSG, pid, NULL, &child); | ||
456 | ptrace(PTRACE_SETOPTIONS, child, NULL, | ||
457 | PTRACE_O_TRACEFORK | | ||
458 | PTRACE_O_TRACEVFORK | | ||
459 | PTRACE_O_TRACECLONE | | ||
460 | PTRACE_O_TRACEEXIT); | ||
461 | ptrace(PTRACE_CONT, child, NULL, 0); | ||
462 | break; | ||
463 | |||
464 | case PTRACE_EVENT_EXIT: | ||
465 | ptrace(PTRACE_GETEVENTMSG, pid, NULL, &cstatus); | ||
466 | ptrace(PTRACE_DETACH, pid, NULL, NULL); | ||
467 | break; | ||
468 | } | ||
469 | ptrace(PTRACE_SETOPTIONS, pid, NULL, | ||
470 | PTRACE_O_TRACEFORK | | ||
471 | PTRACE_O_TRACEVFORK | | ||
472 | PTRACE_O_TRACECLONE | | ||
473 | PTRACE_O_TRACEEXIT); | ||
474 | ptrace(PTRACE_CONT, pid, NULL, send_sig); | ||
475 | } | ||
476 | } while (!finished && ret > 0 && | ||
477 | (!WIFEXITED(status) || pid != main_pid)); | ||
478 | } | ||
479 | |||
480 | void trace_or_sleep(void) | ||
481 | { | ||
482 | if (do_ptrace && filter_pid >= 0) | ||
483 | ptrace_wait(filter_pid); | ||
484 | else | ||
485 | sleep(10); | ||
486 | } | ||
487 | |||
400 | void run_cmd(int argc, char **argv) | 488 | void run_cmd(int argc, char **argv) |
401 | { | 489 | { |
402 | int status; | 490 | int status; |
@@ -407,10 +495,14 @@ void run_cmd(int argc, char **argv) | |||
407 | if (!pid) { | 495 | if (!pid) { |
408 | /* child */ | 496 | /* child */ |
409 | update_task_filter(); | 497 | update_task_filter(); |
498 | enable_ptrace(); | ||
410 | if (execvp(argv[0], argv)) | 499 | if (execvp(argv[0], argv)) |
411 | exit(-1); | 500 | exit(-1); |
412 | } | 501 | } |
413 | waitpid(pid, &status, 0); | 502 | if (do_ptrace) |
503 | ptrace_wait(pid); | ||
504 | else | ||
505 | waitpid(pid, &status, 0); | ||
414 | } | 506 | } |
415 | 507 | ||
416 | static void show_events(void) | 508 | static void show_events(void) |
@@ -1074,8 +1166,6 @@ static int count_cpus(void) | |||
1074 | return cpus; | 1166 | return cpus; |
1075 | } | 1167 | } |
1076 | 1168 | ||
1077 | static int finished; | ||
1078 | |||
1079 | static void finish(int sig) | 1169 | static void finish(int sig) |
1080 | { | 1170 | { |
1081 | /* all done */ | 1171 | /* all done */ |
@@ -1511,7 +1601,7 @@ int main (int argc, char **argv) | |||
1511 | (strcmp(argv[1], "start") == 0) || | 1601 | (strcmp(argv[1], "start") == 0) || |
1512 | ((extract = strcmp(argv[1], "extract") == 0))) { | 1602 | ((extract = strcmp(argv[1], "extract") == 0))) { |
1513 | 1603 | ||
1514 | while ((c = getopt(argc-1, argv+1, "+hae:f:Fp:do:O:s:r:vg:l:n:P:N:tb:ki")) >= 0) { | 1604 | while ((c = getopt(argc-1, argv+1, "+hae:f:Fp:cdo:O:s:r:vg:l:n:P:N:tb:ki")) >= 0) { |
1515 | switch (c) { | 1605 | switch (c) { |
1516 | case 'h': | 1606 | case 'h': |
1517 | usage(argv); | 1607 | usage(argv); |
@@ -1582,6 +1672,9 @@ int main (int argc, char **argv) | |||
1582 | die("only one -P pid can be filtered at a time"); | 1672 | die("only one -P pid can be filtered at a time"); |
1583 | filter_pid = atoi(optarg); | 1673 | filter_pid = atoi(optarg); |
1584 | break; | 1674 | break; |
1675 | case 'c': | ||
1676 | do_ptrace = 1; | ||
1677 | break; | ||
1585 | case 'v': | 1678 | case 'v': |
1586 | if (extract) | 1679 | if (extract) |
1587 | usage(argv); | 1680 | usage(argv); |
@@ -1719,6 +1812,9 @@ int main (int argc, char **argv) | |||
1719 | usage(argv); | 1812 | usage(argv); |
1720 | } | 1813 | } |
1721 | 1814 | ||
1815 | if (do_ptrace && !filter_task && (filter_pid < 0)) | ||
1816 | die(" -c can only be used with -F or -P"); | ||
1817 | |||
1722 | if ((argc - optind) >= 2) { | 1818 | if ((argc - optind) >= 2) { |
1723 | if (!record) | 1819 | if (!record) |
1724 | die("Command start does not take any commands\n" | 1820 | die("Command start does not take any commands\n" |
@@ -1794,10 +1890,13 @@ int main (int argc, char **argv) | |||
1794 | run_cmd((argc - optind) - 1, &argv[optind + 1]); | 1890 | run_cmd((argc - optind) - 1, &argv[optind + 1]); |
1795 | else { | 1891 | else { |
1796 | update_task_filter(); | 1892 | update_task_filter(); |
1893 | /* We don't ptrace ourself */ | ||
1894 | if (do_ptrace && filter_pid >= 0) | ||
1895 | ptrace_attach(filter_pid); | ||
1797 | /* sleep till we are woken with Ctrl^C */ | 1896 | /* sleep till we are woken with Ctrl^C */ |
1798 | printf("Hit Ctrl^C to stop recording\n"); | 1897 | printf("Hit Ctrl^C to stop recording\n"); |
1799 | while (!finished) | 1898 | while (!finished) |
1800 | sleep(10); | 1899 | trace_or_sleep(); |
1801 | } | 1900 | } |
1802 | 1901 | ||
1803 | disable_tracing(); | 1902 | disable_tracing(); |
diff --git a/trace-usage.c b/trace-usage.c index 88f744c..d1f3bfd 100644 --- a/trace-usage.c +++ b/trace-usage.c | |||
@@ -24,6 +24,7 @@ static struct usage_help usage_help[] = { | |||
24 | " -p run command with plugin enabled\n" | 24 | " -p run command with plugin enabled\n" |
25 | " -F filter only on the given process\n" | 25 | " -F filter only on the given process\n" |
26 | " -P trace the given pid like -F for the command\n" | 26 | " -P trace the given pid like -F for the command\n" |
27 | " -c also trace the childen of -F or -P\n" | ||
27 | " -l filter function name\n" | 28 | " -l filter function name\n" |
28 | " -g set graph function\n" | 29 | " -g set graph function\n" |
29 | " -n do not trace function\n" | 30 | " -n do not trace function\n" |