2 * Copyright (c) 2015-2021 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2 only,
6 * as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <sys/types.h>
27 #include <sys/ptrace.h>
29 #include <sys/signal.h>
34 #include <lttng/lttng.h>
35 #include <lttng/handle.h>
36 #include <lttng/session.h>
37 #include <lttng/tracker.h>
39 #define MESSAGE_PREFIX "[lttng-ptrace] "
42 #ifndef PTRACE_EVENT_STOP
43 #define PTRACE_EVENT_STOP 128
46 #define PERROR(msg) perror(msg "\n")
47 #define ERR(fmt, args...) fprintf(stderr, fmt "\n", ## args)
50 #define DBG(fmt, args...) printf(fmt "\n", ## args)
52 #define DBG(fmt, args...)
55 #define __unused __attribute__((unused))
57 static pid_t sigfwd_pid
;
59 static bool opt_help
= false,
60 opt_no_context
= false,
62 opt_no_syscall
= false,
67 static const char *output_path
;
68 static const char *session_name
;
70 struct lttng_trace_ctx
{
71 char session_name
[LTTNG_NAME_MAX
];
77 long ptrace_setup(pid_t pid
)
82 flags
= PTRACE_O_TRACECLONE
| PTRACE_O_TRACEEXIT
83 | PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK
85 //ptrace_ret = ptrace(PTRACE_SETOPTIONS, pid,
86 ptrace_ret
= ptrace(PTRACE_SEIZE
, pid
,
87 NULL
, (void *) flags
);
89 //PERROR("ptrace setoptions");
90 PERROR("ptrace seize");
97 int wait_on_children(pid_t top_pid
, struct lttng_handle
**handle
,
106 DBG("Setup ptrace options on top child pid %d", pid
);
107 ret
= ptrace_setup(pid
);
111 for (i
= 0; i
< nr_handles
; i
++) {
112 ret
= lttng_track_pid(handle
[i
], pid
);
113 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
114 ERR("Error %d tracking pid %d", ret
, pid
);
118 /* Restart initial raise(SIGSTOP) */
119 //ptrace_ret = ptrace(PTRACE_CONT, pid, 0, restartsig);
120 //TODO wait for child to have stopped....
121 ret
= kill(pid
, SIGCONT
);
131 pid
= waitpid(-1, &status
, __WALL
);
132 DBG("Activity on child pid %d", pid
);
134 if (errno
== ECHILD
) {
135 /* No more children to possibly wait for. */
141 } else if (pid
== 0) {
142 ERR("Unexpected PID 0");
145 if (WIFSTOPPED(status
)) {
146 int shiftstatus
, restartsig
;
148 DBG("Child pid %d is stopped", pid
);
149 shiftstatus
= status
>> 8;
150 if (shiftstatus
== (SIGTRAP
| (PTRACE_EVENT_EXIT
<< 8))) {
151 DBG("Child pid %d is exiting", pid
);
153 for (i
= 0; i
< nr_handles
; i
++) {
154 ret
= lttng_untrack_pid(handle
[i
], pid
);
155 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
156 ERR("Error %d untracking pid %d", ret
, pid
);
160 } else if (shiftstatus
== (SIGTRAP
| (PTRACE_EVENT_FORK
<< 8))) {
163 ptrace_ret
= ptrace(PTRACE_GETEVENTMSG
, pid
, 0, &newpid
);
168 DBG("Child pid %d is forking, child pid %ld", pid
, newpid
);
169 for (i
= 0; i
< nr_handles
; i
++) {
170 ret
= lttng_track_pid(handle
[i
], newpid
);
171 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
172 ERR("Error %d tracking pid %ld", ret
, newpid
);
175 } else if (shiftstatus
== (SIGTRAP
| (PTRACE_EVENT_VFORK
<< 8))) {
178 ptrace_ret
= ptrace(PTRACE_GETEVENTMSG
, pid
, 0, &newpid
);
183 DBG("Child pid %d issuing vfork, child pid %ld", pid
, newpid
);
184 for (i
= 0; i
< nr_handles
; i
++) {
185 ret
= lttng_track_pid(handle
[i
], newpid
);
186 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
187 ERR("Error %d tracking pid %ld", ret
, newpid
);
190 } else if (shiftstatus
== (SIGTRAP
| PTRACE_EVENT_CLONE
<< 8)) {
193 ptrace_ret
= ptrace(PTRACE_GETEVENTMSG
, pid
, 0, &newpid
);
198 DBG("Child pid %d issuing clone, child pid %ld", pid
, newpid
);
199 for (i
= 0; i
< nr_handles
; i
++) {
200 ret
= lttng_track_pid(handle
[i
], newpid
);
201 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
202 ERR("Error %d tracking pid %ld", ret
, newpid
);
205 } else if (shiftstatus
== (SIGTRAP
| PTRACE_EVENT_EXEC
<< 8)) {
208 ptrace_ret
= ptrace(PTRACE_GETEVENTMSG
, pid
, 0, &oldpid
);
213 DBG("Child pid (old: %ld, new: %d) is issuing exec",
216 * Needed for exec issued from
217 * multithreaded process.
219 for (i
= 0; i
< nr_handles
; i
++) {
220 ret
= lttng_untrack_pid(handle
[i
], oldpid
);
221 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
222 ERR("Error %d untracking pid %ld", ret
, oldpid
);
224 ret
= lttng_track_pid(handle
[i
], pid
);
225 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
226 ERR("Error %d tracking pid %d", ret
, pid
);
229 } else if (shiftstatus
== SIGTRAP
) {
230 DBG("Received SIGTRAP from pid %d without event of interest", pid
);
231 } else if (shiftstatus
== SIGSTOP
) {
232 DBG("Received SIGSTOP from pid %d without event of interest", pid
);
233 } else if (shiftstatus
== SIGSEGV
) {
234 DBG("Received SIGSEGV from pid %d without event of interest", pid
);
235 } else if (shiftstatus
== SIGTTIN
) {
236 DBG("Received SIGTTIN from pid %d without event of interest", pid
);
237 } else if (shiftstatus
== SIGTTOU
) {
238 DBG("Received SIGTTOU from pid %d without event of interest", pid
);
239 } else if (shiftstatus
== SIGTSTP
) {
240 DBG("Received SIGTSTP from pid %d without event of interest", pid
);
242 DBG("Ignoring signal %d (status %d) from pid %d (eventcode = %u)",
243 WSTOPSIG(status
), status
, pid
,
244 (shiftstatus
& ~WSTOPSIG(status
)) >> 8);
247 restartsig
= WSTOPSIG(status
);
248 switch (restartsig
) {
257 //ptrace_ret = ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo);
258 //if (ptrace_ret < 0 && errno == EINVAL) {
259 if (restartsig
== SIGTTIN
) {
260 ret
= kill(pid
, SIGTTIN
);
265 } else if (status
>> 16 == PTRACE_EVENT_STOP
) {
267 //ptrace_ret = ptrace(PTRACE_LISTEN, pid, 0, 0);
268 ptrace_ret
= ptrace(PTRACE_CONT
, pid
, 0, 0);
270 PERROR("ptrace cont");
274 DBG("job control stop ret %ld errno %d", ptrace_ret
, errno
);
276 * It's not a group-stop, so restart process,
277 * skipping the signal.
279 ptrace_ret
= ptrace(PTRACE_CONT
, pid
, 0, 0);
281 PERROR("ptrace cont");
289 //unsigned long data;
291 //if (ptrace(PTRACE_GETEVENTMSG, pid, NULL, &data) == 0) {
293 * Restart process skipping the signal when
294 * receiving a message.
296 ptrace_ret
= ptrace(PTRACE_CONT
, pid
, 0, 0);
306 /* Restart with original signal. */
307 ptrace_ret
= ptrace(PTRACE_CONT
, pid
, 0, restartsig
);
313 } else if (WIFEXITED(status
)) {
314 DBG("Child pid %d exited normally with status %d",
315 pid
, WEXITSTATUS(status
));
316 for (i
= 0; i
< nr_handles
; i
++) {
317 ret
= lttng_untrack_pid(handle
[i
], pid
);
318 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
319 ERR("Error %d tracking pid %d", ret
, pid
);
322 } else if (WIFSIGNALED(status
)) {
323 DBG("Child pid %d terminated by signal %d", pid
,
325 for (i
= 0; i
< nr_handles
; i
++) {
326 ret
= lttng_untrack_pid(handle
[i
], pid
);
327 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
328 ERR("Error %d tracking pid %d", ret
, pid
);
332 DBG("Unhandled status %d from child %d", status
, pid
);
339 int run_child(int argc
, char **argv
)
345 ERR("Please provide executable name as first argument.");
352 DBG("Child process created (pid: %d)", pid
);
353 } else if (pid
== 0) {
358 ptraceret
= ptrace(PTRACE_TRACEME
, 0, NULL
, NULL
);
364 ret
= raise(SIGSTOP
);
369 ret
= execvp(argv
[0], &argv
[0]);
382 int create_session(struct lttng_trace_ctx
*ctx
)
384 return lttng_create_session(ctx
->session_name
, ctx
->path
);
388 int destroy_session(struct lttng_trace_ctx
*ctx
)
390 return lttng_destroy_session(ctx
->session_name
);
394 int start_session(struct lttng_trace_ctx
*ctx
)
396 return lttng_start_tracing(ctx
->session_name
);
400 int enable_syscalls(struct lttng_trace_ctx
*ctx
)
402 struct lttng_domain domain
;
403 struct lttng_event
*ev
;
404 struct lttng_handle
*handle
;
409 memset(&domain
, 0, sizeof(domain
));
410 ev
= lttng_event_create();
413 domain
.type
= LTTNG_DOMAIN_KERNEL
;
414 domain
.buf_type
= LTTNG_BUFFER_GLOBAL
;
416 handle
= lttng_create_handle(ctx
->session_name
, &domain
);
419 ev
->type
= LTTNG_EVENT_SYSCALL
;
420 strcpy(ev
->name
, "*");
421 ev
->loglevel_type
= LTTNG_EVENT_LOGLEVEL_ALL
;
422 ret
= lttng_enable_event_with_exclusions(handle
,
423 ev
, NULL
, NULL
, 0, NULL
);
426 lttng_destroy_handle(handle
);
431 int add_contexts(struct lttng_trace_ctx
*ctx
, enum lttng_domain_type domain_type
)
433 struct lttng_domain domain
;
434 struct lttng_event_context event_ctx
;
435 struct lttng_handle
*handle
;
439 memset(&domain
, 0, sizeof(domain
));
440 switch (domain_type
) {
441 case LTTNG_DOMAIN_KERNEL
:
442 domain
.buf_type
= LTTNG_BUFFER_GLOBAL
;
444 case LTTNG_DOMAIN_UST
:
445 domain
.buf_type
= LTTNG_BUFFER_PER_UID
;
450 domain
.type
= domain_type
;
452 handle
= lttng_create_handle(ctx
->session_name
, &domain
);
456 memset(&event_ctx
, 0, sizeof(event_ctx
));
457 event_ctx
.ctx
= LTTNG_EVENT_CONTEXT_PROCNAME
;
458 if (lttng_add_context(handle
, &event_ctx
, NULL
, NULL
) < 0)
461 memset(&event_ctx
, 0, sizeof(event_ctx
));
462 event_ctx
.ctx
= LTTNG_EVENT_CONTEXT_VPID
;
463 if (lttng_add_context(handle
, &event_ctx
, NULL
, NULL
) < 0)
466 memset(&event_ctx
, 0, sizeof(event_ctx
));
467 event_ctx
.ctx
= LTTNG_EVENT_CONTEXT_VTID
;
468 if (lttng_add_context(handle
, &event_ctx
, NULL
, NULL
) < 0)
471 lttng_destroy_handle(handle
);
476 int create_channels(struct lttng_trace_ctx
*ctx
, enum lttng_domain_type domain_type
)
478 struct lttng_domain domain
;
479 struct lttng_channel
*channel
;
480 struct lttng_handle
*handle
;
482 memset(&domain
, 0, sizeof(domain
));
483 switch (domain_type
) {
484 case LTTNG_DOMAIN_KERNEL
:
485 domain
.buf_type
= LTTNG_BUFFER_GLOBAL
;
487 case LTTNG_DOMAIN_UST
:
488 domain
.buf_type
= LTTNG_BUFFER_PER_UID
;
493 domain
.type
= domain_type
;
494 channel
= lttng_channel_create(&domain
);
495 channel
->enabled
= 1;
497 handle
= lttng_create_handle(ctx
->session_name
, &domain
);
500 if (lttng_enable_channel(handle
, channel
) < 0)
502 lttng_destroy_handle(handle
);
504 lttng_channel_destroy(channel
);
509 struct lttng_handle
*create_kernel_handle(struct lttng_trace_ctx
*ctx
)
511 struct lttng_domain domain
;
513 memset(&domain
, 0, sizeof(domain
));
514 domain
.type
= LTTNG_DOMAIN_KERNEL
;
515 domain
.buf_type
= LTTNG_BUFFER_GLOBAL
;
516 return lttng_create_handle(ctx
->session_name
, &domain
);
520 struct lttng_handle
*create_ust_handle(struct lttng_trace_ctx
*ctx
)
522 struct lttng_domain domain
;
524 memset(&domain
, 0, sizeof(domain
));
525 domain
.type
= LTTNG_DOMAIN_UST
;
526 domain
.buf_type
= LTTNG_BUFFER_PER_UID
;
527 return lttng_create_handle(ctx
->session_name
, &domain
);
531 void sighandler(int signo
, siginfo_t
*siginfo __unused
, void *context __unused
)
535 DBG("sighandler receives signal %d, forwarding to child %d",
537 ret
= kill(sigfwd_pid
, signo
);
545 int lttng_trace_ctx_init(struct lttng_trace_ctx
*ctx
)
553 ctx
->creation_time
= time(NULL
);
554 if (ctx
->creation_time
== (time_t) -1)
556 timeinfo
= localtime(&ctx
->creation_time
);
559 strftime(datetime
, sizeof(datetime
), "%Y%m%d-%H%M%S", timeinfo
);
562 if (strlen(session_name
) > LTTNG_NAME_MAX
- 1) {
565 strcpy(ctx
->session_name
, session_name
);
567 memset(ctx
, 0, sizeof(*ctx
));
568 strcpy(ctx
->session_name
, "lttng-ptrace-");
570 ret
= sprintf(pid_str
, "%d", (int) pid
);
573 strcat(ctx
->session_name
, pid_str
);
574 strcat(ctx
->session_name
, "-");
575 strcat(ctx
->session_name
, datetime
);
579 if (strlen(output_path
) > PATH_MAX
- 1) {
582 strcpy(ctx
->path
, output_path
);
584 strcpy(ctx
->path
, "/tmp/lttng-ptrace/");
585 strcat(ctx
->path
, ctx
->session_name
);
591 int lttng_trace_untrack_all(struct lttng_handle
**handle
,
597 for (i
= 0; i
< nr_handles
; i
++) {
598 ret
= lttng_untrack_pid(handle
[i
], -1);
599 if (ret
&& ret
!= -LTTNG_ERR_INVALID
) {
600 ERR("Error %d untracking pid %d", ret
, -1);
608 * >= 0: number of arguments to skip before command.
612 int parse_args(int argc
, char **argv
)
616 for (i
= 1; i
< argc
; i
++) {
617 const char *str
= argv
[i
];
619 if (!strcmp(str
, "--")) {
620 i
++; /* Next is command position. */
624 goto end
; /* Cursor at command position. */
626 if (!strcmp(str
, "--help")) {
629 if (!strcmp(str
, "--no-context")) {
630 opt_no_context
= true;
632 if (!strcmp(str
, "--no-pause")) {
635 if (!strcmp(str
, "--no-syscall")) {
636 opt_no_syscall
= true;
638 if (!strcmp(str
, "--output")) {
641 ERR("Expected path argument after --output");
644 output_path
= argv
[++i
];
646 if (!strcmp(str
, "--session")) {
649 ERR("Expected path argument after --session");
652 session_name
= argv
[++i
];
654 if (!strcmp(str
, "--view")) {
659 if (i
== argc
&& !opt_help
) {
660 ERR("Expected COMMAND argument after options. See `%s --help` for details.", argv
[0]);
667 int show_help(int argc __unused
, char **argv
)
669 printf("Usage of %s:\n", argv
[0]);
671 printf(" %s [OPTION] [--] COMMAND [COMMAND OPTIONS]\n", argv
[0]);
673 printf("Runs COMMAND while tracing the system calls of the children\n");
674 printf("process hierarchy. See standard error output while executing\n");
675 printf("this command for more information.\n");
677 printf("Supported options:\n");
678 printf(" --help: This help screen.\n");
679 printf(" --no-context: Do not trace default contexts (vpid, vtid, procname).\n");
680 printf(" --no-pause: Do not wait for user input before running COMMAND.\n");
681 printf(" --no-syscall: Do not trace system calls.\n");
682 printf(" --output PATH: Write trace into output PATH. (default: /tmp/lttng-ptrace/$SESSION_NAME)\n");
683 printf(" --session NAME: Tracing session name. (default: lttng-ptrace-$PID-$DATETIME)\n");
684 printf(" --view: View trace after end of COMMAND execution.\n");
689 int main(int argc
, char **argv
)
693 struct lttng_handle
*handle
[NR_HANDLES
];
694 struct sigaction act
;
695 struct lttng_trace_ctx ptrace_ctx
;
698 skip_args
= parse_args(argc
, argv
);
703 show_help(argc
, argv
);
707 if (lttng_trace_ctx_init(&ptrace_ctx
))
710 act
.sa_sigaction
= sighandler
;
711 act
.sa_flags
= SA_SIGINFO
| SA_RESTART
;
712 sigemptyset(&act
.sa_mask
);
713 ret
= sigaction(SIGTERM
, &act
, NULL
);
716 ret
= sigaction(SIGINT
, &act
, NULL
);
720 if (create_session(&ptrace_ctx
) < 0) {
721 fprintf(stderr
, "%sError: Unable to create tracing session. Please ensure that lttng-sessiond is running as root and that your user belongs to the `tracing` group.\n", MESSAGE_PREFIX
);
725 handle
[0] = create_kernel_handle(&ptrace_ctx
);
728 goto end_kernel_handle
;
730 handle
[1] = create_ust_handle(&ptrace_ctx
);
735 if (create_channels(&ptrace_ctx
, LTTNG_DOMAIN_KERNEL
) < 0) {
737 goto end_wait_on_children
;
739 if (create_channels(&ptrace_ctx
, LTTNG_DOMAIN_UST
) < 0) {
741 goto end_wait_on_children
;
743 if (enable_syscalls(&ptrace_ctx
) < 0) {
745 goto end_wait_on_children
;
747 if (add_contexts(&ptrace_ctx
, LTTNG_DOMAIN_KERNEL
) < 0) {
749 goto end_wait_on_children
;
751 if (add_contexts(&ptrace_ctx
, LTTNG_DOMAIN_UST
) < 0) {
753 goto end_wait_on_children
;
755 if (lttng_trace_untrack_all(handle
, NR_HANDLES
) < 0) {
757 goto end_wait_on_children
;
759 fprintf(stderr
, "%sTracing session `%s` created. It can be customized using the `lttng` command.\n", MESSAGE_PREFIX
, ptrace_ctx
.session_name
);
761 fprintf(stderr
, "Press <ENTER> key when ready to run the child process.\n");
765 if (start_session(&ptrace_ctx
) < 0) {
767 goto end_wait_on_children
;
770 //TODO: signal off before we can forward it.
771 pid
= run_child(argc
- skip_args
, argv
+ skip_args
);
780 ret
= wait_on_children(pid
, handle
, NR_HANDLES
);
783 goto end_wait_on_children
;
787 end_wait_on_children
:
788 lttng_destroy_handle(handle
[1]);
790 lttng_destroy_handle(handle
[0]);
792 if (destroy_session(&ptrace_ctx
))
798 fprintf(stderr
, "%sSub-process hierarchy traced successfully. View trace with `babeltrace2 %s`.\n", MESSAGE_PREFIX
,
801 return execlp("babeltrace2", "babeltrace2", ptrace_ctx
.path
, NULL
);