aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2011-02-09 18:52:19 -0500
committerSteven Rostedt <rostedt@goodmis.org>2011-02-09 19:30:58 -0500
commit4896461d2e344dcac0aadbfef381c0760c860eae (patch)
tree31a40a1b1034c3ece83edea08dbfdbfae2d9bee7
parent685cc721962012caad6ddf890bd601b7f5f1e5da (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.c109
-rw-r--r--trace-usage.c1
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;
76static int *client_ports; 77static int *client_ports;
77static int sfd; 78static int sfd;
78 79
80static int do_ptrace;
81
79static int filter_task; 82static int filter_task;
80static int filter_pid = -1; 83static int filter_pid = -1;
81 84
85static int finished;
86
82struct func_list { 87struct 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
405static 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
417static void enable_ptrace(void)
418{
419 if (!do_ptrace || !filter_task)
420 return;
421
422 ptrace(PTRACE_TRACEME, 0, NULL, 0);
423}
424
425static 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
480void trace_or_sleep(void)
481{
482 if (do_ptrace && filter_pid >= 0)
483 ptrace_wait(filter_pid);
484 else
485 sleep(10);
486}
487
400void run_cmd(int argc, char **argv) 488void 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
416static void show_events(void) 508static void show_events(void)
@@ -1074,8 +1166,6 @@ static int count_cpus(void)
1074 return cpus; 1166 return cpus;
1075} 1167}
1076 1168
1077static int finished;
1078
1079static void finish(int sig) 1169static 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"