aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2020-05-31 23:22:22 +0200
committerMattias Andrée <maandree@kth.se>2020-05-31 23:22:22 +0200
commitbe1c092cd99a023b3d4178066925b644feb0565f (patch)
treeea5fd38c39aacdc93b55fa8fc9271ad523032f7c
parentm (diff)
downloadsctrace-be1c092cd99a023b3d4178066925b644feb0565f.tar.gz
sctrace-be1c092cd99a023b3d4178066925b644feb0565f.tar.bz2
sctrace-be1c092cd99a023b3d4178066925b644feb0565f.tar.xz
Tracing fork children does not work
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat (limited to '')
-rw-r--r--common.h8
-rw-r--r--process.c31
-rw-r--r--sctrace.c248
-rw-r--r--util.c53
4 files changed, 187 insertions, 153 deletions
diff --git a/common.h b/common.h
index 43b3d24..a00bc67 100644
--- a/common.h
+++ b/common.h
@@ -79,10 +79,11 @@ enum state {
struct process {
pid_t pid;
- pid_t thread_group_leader;
+ pid_t thread_leader;
struct process *next;
struct process *prev;
enum state state;
+ int silent_until_execed;
/* Syscall data */
unsigned long long int scall;
@@ -119,4 +120,7 @@ void remove_process(struct process *proc);
/* util.c */
void setup_trace_output(FILE *fp, int multiprocess);
void tprintf(struct process *proc, const char *fmt, ...);
-_Noreturn void eprintf(const char *fmt, ...);
+void weprintf(const char *fmt, ...);
+#define eprintf(...) (weprintf(__VA_ARGS__), exit(1))
+#define eprintf_and_kill(PID, ...) (weprintf(__VA_ARGS__), kill((PID), SIGKILL), exit(1))
+FILE *xfopen(const char *file, const char *mode);
diff --git a/process.c b/process.c
index c5a09be..056a103 100644
--- a/process.c
+++ b/process.c
@@ -29,28 +29,31 @@ struct process *
add_process(pid_t pid, unsigned long int trace_options)
{
struct process *proc;
- int saved_errno;
+ int status;
+
proc = calloc(1, sizeof(*proc));
if (!proc)
eprintf("calloc: %s\n");
proc->pid = pid;
- if (waitpid(pid, NULL, 0) < 0) {
- eprintf("waitpid <child> NULL 0:");
- kill(pid, SIGKILL);
- exit(1);
- }
- if (ptrace(PTRACE_SETOPTIONS, pid, 0, trace_options)) {
- saved_errno = errno;
- kill(pid, SIGKILL);
- errno = saved_errno;
- eprintf("ptrace PTRACE_SETOPTIONS %ju 0 ...:", (uintmax_t)pid);
- }
- if (ptrace(PTRACE_SYSCALL, pid, NULL, 0))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)pid);
proc->next = &tail;
proc->prev = tail.prev;
proc->prev->next = proc;
tail.prev = proc;
+
+ while (waitpid(pid, &status, WSTOPPED) != pid) {
+ if (errno == EINTR)
+ continue;
+ eprintf_and_kill(pid, "waitpid %ju <buffer> WSTOPPED:", (uintmax_t)pid);
+ }
+ if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP)
+ eprintf_and_kill(pid, "unexpected return of waitpid %ju <buffer> WSTOPPED:", (uintmax_t)pid);
+ if (ptrace(PTRACE_SEIZE, pid, 0, trace_options))
+ eprintf_and_kill(pid, "ptrace PTRACE_SEIZE %ju 0 ...:", (uintmax_t)pid);
+ if (ptrace(PTRACE_INTERRUPT, pid, 0, 0))
+ eprintf_and_kill(pid, "ptrace PTRACE_INTERRUPT %ju 0 0:", (uintmax_t)pid);
+ if (kill(pid, SIGCONT) < 0)
+ eprintf_and_kill(pid, "kill &ju SIGCONT:", (uintmax_t)pid);
+
return proc;
}
diff --git a/sctrace.c b/sctrace.c
index 7490a86..30535fb 100644
--- a/sctrace.c
+++ b/sctrace.c
@@ -3,6 +3,7 @@
char *argv0;
+static unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;
static void
@@ -70,11 +71,6 @@ handle_syscall(struct process *proc)
break;
case Exec:
- if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
- proc->state = Normal;
- break;
-
case VforkParent:
if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
@@ -91,18 +87,95 @@ handle_syscall(struct process *proc)
}
+static void
+handle_event(struct process *proc, int status)
+{
+ int trace_event, sig;
+ unsigned long int event;
+ struct process *proc2;
+
+ sig = WSTOPSIG(status);
+ trace_event = ((status >> 8) ^ SIGTRAP) >> 8;
+ switch (trace_event) {
+
+ case PTRACE_EVENT_VFORK:
+ tprintf(proc, "\nProcess stopped by vfork until child exits or exec(2)s\n");
+ /* fall thought */
+ case PTRACE_EVENT_FORK:
+ case PTRACE_EVENT_CLONE:
+ if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event))
+ eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid);
+ proc2 = add_process((pid_t)event, trace_options);
+ if (trace_event == PTRACE_EVENT_CLONE)
+ proc2->thread_leader = proc->pid;
+ proc->ret = event;
+ if (trace_event == PTRACE_EVENT_VFORK) {
+ proc2->continue_on_exit = proc;
+ proc->vfork_waiting_on = proc2;
+ proc->state = VforkParent;
+ } else {
+ proc->state = trace_event == PTRACE_EVENT_CLONE ? CloneParent : ForkParent;
+ handle_syscall(proc);
+ }
+ tprintf(proc2, "\nTracing new process\n");
+ proc2->state = trace_event == PTRACE_EVENT_FORK ? ForkChild :
+ trace_event == PTRACE_EVENT_VFORK ? VforkChild : CloneChild;
+ handle_syscall(proc2);
+ break;
+
+ case PTRACE_EVENT_EXEC:
+ proc->state = Exec;
+ handle_syscall(proc);
+ proc2 = proc->continue_on_exit;
+ if (proc2) {
+ proc->continue_on_exit = NULL;
+ proc2->vfork_waiting_on = NULL;
+ tprintf(proc2, "\nProcess continues due to exec(2) of vfork child\n");
+ handle_syscall(proc2);
+ }
+ break;
+
+ case PTRACE_EVENT_STOP:
+ switch (sig) {
+ case SIGSTOP:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ stop_signal:
+ tprintf(proc, "\nProcess stopped by signal %i (%s: %s)\n", sig, get_signum_name(sig), strsignal(sig));
+ if (ptrace(PTRACE_LISTEN, proc->pid, NULL, 0))
+ eprintf("ptrace PTRACE_LISTEN %ju NULL 0:", (uintmax_t)proc->pid);
+ break;
+ default:
+ if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
+ eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
+ break;
+ }
+ break;
+
+ default:
+ abort();
+
+ case 0:
+ /* TODO ensure proper handling of signals (multithreaded?, siginfo?, in new processes?) */
+ if (ptrace(PTRACE_GETSIGINFO, proc->pid, 0, &(siginfo_t){0}))
+ goto stop_signal;
+ tprintf(proc, "\nProcess received signal %i (%s: %s)\n", sig, get_signum_name(sig), strsignal(sig));
+ if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, sig))
+ eprintf("ptrace PTRACE_SYSCALL %ju NULL %i:", (uintmax_t)proc->pid, sig);
+ break;
+ }
+}
+
+
int
main(int argc, char **argv)
{
pid_t pid, orig_pid;
- long int tmp;
char *outfile = NULL;
FILE *outfp = stderr;
- const char *num = NULL;
- int status, exit_value = 0, trace_event, with_argv0 = 0, multiprocess = 0;
- unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;
+ int status, exit_code = 0, with_argv0 = 0, multiprocess = 0;
struct process *proc, *proc2;
- unsigned long int event;
/* TODO add option to trace signals with siginfo (-s) */
ARGBEGIN {
@@ -128,154 +201,67 @@ main(int argc, char **argv)
if (!argc)
usage();
- init_process_list();
-
/* Start program to trace */
- pid = fork();
- switch (pid) {
+ orig_pid = fork();
+ switch (orig_pid) {
case -1:
eprintf("fork:");
- return 1;
case 0:
- if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
- eprintf("ptrace PTRACE_TRACEME 0 NULL 0:");
- return 1;
- }
- /* exec will block until parent attaches */
+ if (raise(SIGSTOP))
+ eprintf_and_kill(getppid(), "raise SIGSTOP:");
execvp(*argv, &argv[with_argv0]);
- kill(getppid(), SIGKILL);
- eprintf("execvp %s:", *argv);
+ eprintf_and_kill(getppid(), "execvp %s:", *argv);
default:
- orig_pid = pid;
- add_process(pid, trace_options);
break;
}
- /* Open trace output file */
- if (outfile) {
- if (!strncmp(outfile, "/dev/fd/", sizeof("/dev/fd/") - 1))
- num = &outfile[sizeof("/dev/fd/") - 1];
- else if (!strncmp(outfile, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1))
- num = &outfile[sizeof("/proc/self/fd/") - 1];
- else if (!strcmp(outfile, "/dev/stdin"))
- num = "0";
- else if (!strcmp(outfile, "/dev/stdout"))
- num = "1";
- else if (!strcmp(outfile, "/dev/stderr"))
- num = "2";
- if (num && isdigit(*num)) {
- errno = 0;
- tmp = strtol(num, (void *)&num, 10);
- if (!errno && tmp >= 0 &&
-#if INT_MAX < LONG_MAX
- tmp < INT_MAX &&
-#endif
- !*num) {
- outfp = fdopen((int)tmp, "wb");
- if (!outfp) {
- eprintf("fdopen %li wb:", tmp);
- return 1;
- }
- goto have_outfp;
- }
- }
- outfp = fopen(outfile, "wb");
- if (!outfp) {
- eprintf("fopen %s wb:", outfile);
- return 1;
- }
- }
-
-have_outfp:
+ /* TODO need to reset signal handlers and mask */
+ outfp = outfile ? xfopen(outfile, "wb") : stderr;
setup_trace_output(outfp, multiprocess);
+ init_process_list();
+ add_process(orig_pid, trace_options);
for (;;) {
- pid = wait(&status);
+ pid = waitpid(-1, &status, __WALL | WCONTINUED);
if (pid < 0) {
if (errno == ECHILD)
- return exit_value;
- eprintf("wait <buffer>:");
- return 1;
+ return WEXITSTATUS(exit_code); /* TODO mimic kill by signal */
+ if (errno == EINTR)
+ continue;
+ eprintf("waitpid -1 <buffer> __WALL:");
}
proc = find_process(pid);
if (!proc)
continue;
- if (WIFEXITED(status)) {
+ if (WIFSTOPPED(status)) {
+ if (WSTOPSIG(status) == (SIGTRAP | 0x80))
+ handle_syscall(proc);
+ else
+ handle_event(proc, status);
+ } else if (WIFCONTINUED(status)) {
+ tprintf(proc, "\nProcess continued, presumably by signal %i (SIGCONT: %s)\n", SIGCONT, strsignal(SIGCONT));
+ } else {
if (pid == orig_pid)
- exit_value = WEXITSTATUS(status);
- tprintf(proc, "\nProcess exited with value %i\n", WEXITSTATUS(status));
+ exit_code = status;
+ if (WIFEXITED(status)) {
+ tprintf(proc, "\nProcess exited with value %i\n", WEXITSTATUS(status));
+ } else {
+ tprintf(proc, "\nProcess terminated by signal %i (%s: %s)\n", WTERMSIG(status),
+ get_signum_name(WTERMSIG(status)), strsignal(WTERMSIG(status)));
+ }
proc2 = proc->continue_on_exit;
remove_process(proc);
if (proc2) {
- tprintf(proc2, "\nProcess continues due to exit of vfork child\n");
+ if (WIFEXITED(status))
+ tprintf(proc2, "\nProcess continues due to exit of vfork child\n");
+ else
+ tprintf(proc2, "\nProcess continues due to abnormal termination of vfork child\n");
handle_syscall(proc2);
}
-
- } else if (WIFSIGNALED(status)) {
- tprintf(proc, "\nProcess terminated by signal %i (%s: %s)\n", WTERMSIG(status),
- get_signum_name(WTERMSIG(status)), strsignal(WTERMSIG(status)));
-
- } else if (WIFSTOPPED(status)) {
- if (WSTOPSIG(status) == (SIGTRAP | 0x80)) {
- handle_syscall(proc);
- } else if (WSTOPSIG(status) == SIGTRAP) {
- trace_event = ((status >> 8) ^ SIGTRAP) >> 8;
- switch (trace_event) {
-
- case PTRACE_EVENT_VFORK:
- tprintf(proc, "\nProcess stopped by vfork until child exits or exec(2)s\n");
- /* fall thought */
- case PTRACE_EVENT_FORK:
- case PTRACE_EVENT_CLONE:
- if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event))
- eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid);
- proc2 = add_process((pid_t)event, trace_options);
- if (trace_event == PTRACE_EVENT_CLONE)
- proc2->thread_group_leader = proc->pid;
- proc->ret = event;
- if (trace_event == PTRACE_EVENT_VFORK) {
- proc2->continue_on_exit = proc;
- proc->vfork_waiting_on = proc2;
- proc->state = VforkParent;
- } else {
- proc->state = trace_event == PTRACE_EVENT_CLONE ? CloneParent : ForkParent;
- handle_syscall(proc);
- }
- tprintf(proc2, "\nTracing new process\n");
- proc2->state = trace_event == PTRACE_EVENT_FORK ? ForkChild :
- trace_event == PTRACE_EVENT_VFORK ? VforkChild : CloneChild;
- handle_syscall(proc2);
- break;
-
- case PTRACE_EVENT_EXEC:
- proc->state = Exec;
- handle_syscall(proc);
- proc2 = proc->continue_on_exit;
- if (proc2) {
- proc->continue_on_exit = NULL;
- proc2->vfork_waiting_on = NULL;
- tprintf(proc2, "\nProcess continues due to exec(2) of vfork child\n");
- handle_syscall(proc2);
- }
- break;
-
- default:
- goto print_signal;
- }
- } else {
- print_signal:
- tprintf(proc, "\nProcess received signal %i (%s: %s)\n", WSTOPSIG(status),
- get_signum_name(WSTOPSIG(status)), strsignal(WSTOPSIG(status)));
- /* TODO handle signals properly (siginfo?, SIGSTOP &c does not stop the process) */
- if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, WSTOPSIG(status)))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL %i:", (uintmax_t)proc->pid, WSTOPSIG(status));
- }
-
- } else if (WIFCONTINUED(status)) {
- tprintf(proc, "\nProcess continued\n", (uintmax_t)pid);
}
+
}
fclose(outfp);
diff --git a/util.c b/util.c
index 2933fcc..8e9a971 100644
--- a/util.c
+++ b/util.c
@@ -20,17 +20,19 @@ void
tprintf(struct process *proc, const char *fmt, ...)
{
va_list ap;
+ if (proc->silent_until_execed)
+ return;
if (fmt[0] == '\n' && fmt[1]) {
last_pid = 0;
if (multiproctrace || last_char == '\n')
fmt = &fmt[1];
}
if (multiproctrace) {
- if (proc->thread_group_leader) {
+ if (proc->thread_leader) {
if (last_char == '\n')
- fprintf(trace_fp, "[%ju, %ju] ", (uintmax_t)proc->thread_group_leader, (uintmax_t)proc->pid);
+ fprintf(trace_fp, "[%ju, %ju] ", (uintmax_t)proc->thread_leader, (uintmax_t)proc->pid);
else if (proc->pid != last_pid)
- fprintf(trace_fp, "\n[%ju, %ju] ", (uintmax_t)proc->thread_group_leader, (uintmax_t)proc->pid);
+ fprintf(trace_fp, "\n[%ju, %ju] ", (uintmax_t)proc->thread_leader, (uintmax_t)proc->pid);
} else {
if (last_char == '\n')
fprintf(trace_fp, "[%ju] ", (uintmax_t)proc->pid);
@@ -46,8 +48,8 @@ tprintf(struct process *proc, const char *fmt, ...)
}
-_Noreturn void
-eprintf(const char *fmt, ...)
+void
+weprintf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
@@ -64,5 +66,44 @@ eprintf(const char *fmt, ...)
break;
}
va_end(ap);
- exit(1);
+}
+
+
+FILE *
+xfopen(const char *file, const char *mode)
+{
+ FILE *ret;
+ const char *num = NULL;
+ long int tmp;
+
+ if (!strncmp(file, "/dev/fd/", sizeof("/dev/fd/") - 1))
+ num = &file[sizeof("/dev/fd/") - 1];
+ else if (!strncmp(file, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1))
+ num = &file[sizeof("/proc/self/fd/") - 1];
+ else if (!strcmp(file, "/dev/stdin"))
+ num = "0";
+ else if (!strcmp(file, "/dev/stdout"))
+ num = "1";
+ else if (!strcmp(file, "/dev/stderr"))
+ num = "2";
+
+ if (num && isdigit(*num)) {
+ errno = 0;
+ tmp = strtol(num, (void *)&num, 10);
+ if (!errno && tmp >= 0 &&
+#if INT_MAX < LONG_MAX
+ tmp < INT_MAX &&
+#endif
+ !*num) {
+ ret = fdopen((int)tmp, mode);
+ if (!ret)
+ eprintf("fdopen %li %s:", tmp, mode);
+ return ret;
+ }
+ }
+
+ ret = fopen(file, mode);
+ if (!ret)
+ eprintf("fopen %s %s:", file, mode);
+ return ret;
}