Changeset View
Standalone View
usr.bin/truss/setup.c
Show All 31 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
/* | /* | ||||
* Various setup functions for truss. Not the cleanest-written code, | * Various setup functions for truss. Not the cleanest-written code, | ||||
* I'm afraid. | * I'm afraid. | ||||
*/ | */ | ||||
#include <sys/param.h> | |||||
#include <sys/types.h> | |||||
#include <sys/ptrace.h> | #include <sys/ptrace.h> | ||||
#include <sys/sysctl.h> | |||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||
#include <assert.h> | |||||
#include <err.h> | #include <err.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | |||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdint.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <time.h> | #include <time.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <machine/reg.h> | |||||
#include "truss.h" | #include "truss.h" | ||||
#include "syscall.h" | |||||
#include "extern.h" | #include "extern.h" | ||||
SET_DECLARE(procabi, struct procabi); | |||||
static sig_atomic_t detaching; | static sig_atomic_t detaching; | ||||
static void new_proc(struct trussinfo *, pid_t); | |||||
/* | /* | ||||
* setup_and_wait() is called to start a process. All it really does | * setup_and_wait() is called to start a process. All it really does | ||||
* is fork(), set itself up to stop on exec or exit, and then exec | * is fork(), enable tracing in the child, and then exec the given | ||||
* the given command. At that point, the child process stops, and | * command. At that point, the child process stops, and the parent | ||||
* the parent can wake up and deal with it. | * can wake up and deal with it. | ||||
*/ | */ | ||||
void | |||||
int | setup_and_wait(struct trussinfo *info, char *command[]) | ||||
setup_and_wait(char *command[]) | |||||
{ | { | ||||
pid_t pid; | pid_t pid; | ||||
pid = vfork(); | pid = vfork(); | ||||
if (pid == -1) | if (pid == -1) | ||||
err(1, "fork failed"); | err(1, "fork failed"); | ||||
if (pid == 0) { /* Child */ | if (pid == 0) { /* Child */ | ||||
ptrace(PT_TRACE_ME, 0, 0, 0); | ptrace(PT_TRACE_ME, 0, 0, 0); | ||||
execvp(command[0], command); | execvp(command[0], command); | ||||
err(1, "execvp %s", command[0]); | err(1, "execvp %s", command[0]); | ||||
} | } | ||||
/* Only in the parent here */ | /* Only in the parent here */ | ||||
if (waitpid(pid, NULL, 0) < 0) | if (waitpid(pid, NULL, 0) < 0) | ||||
err(1, "unexpect stop in waitpid"); | err(1, "unexpect stop in waitpid"); | ||||
return (pid); | new_proc(info, pid); | ||||
} | } | ||||
/* | /* | ||||
* start_tracing picks up where setup_and_wait() dropped off -- namely, | * start_tracing is called to attach to an existing process. | ||||
* it sets the event mask for the given process id. Called for both | |||||
* monitoring an existing process and when we create our own. | |||||
*/ | */ | ||||
void | |||||
int | start_tracing(struct trussinfo *info, pid_t pid) | ||||
start_tracing(pid_t pid) | |||||
{ | { | ||||
int ret, retry; | int ret, retry; | ||||
retry = 10; | retry = 10; | ||||
do { | do { | ||||
ret = ptrace(PT_ATTACH, pid, NULL, 0); | ret = ptrace(PT_ATTACH, pid, NULL, 0); | ||||
usleep(200); | usleep(200); | ||||
} while (ret && retry-- > 0); | } while (ret && retry-- > 0); | ||||
if (ret) | if (ret) | ||||
err(1, "can not attach to target process"); | err(1, "can not attach to target process"); | ||||
if (waitpid(pid, NULL, 0) < 0) | if (waitpid(pid, NULL, 0) < 0) | ||||
err(1, "Unexpect stop in waitpid"); | err(1, "Unexpect stop in waitpid"); | ||||
return (0); | new_proc(info, pid); | ||||
} | } | ||||
/* | /* | ||||
* Restore a process back to it's pre-truss state. | * Restore a process back to it's pre-truss state. | ||||
* Called for SIGINT, SIGTERM, SIGQUIT. This only | * Called for SIGINT, SIGTERM, SIGQUIT. This only | ||||
* applies if truss was told to monitor an already-existing | * applies if truss was told to monitor an already-existing | ||||
* process. | * process. | ||||
*/ | */ | ||||
void | void | ||||
restore_proc(int signo __unused) | restore_proc(int signo __unused) | ||||
{ | { | ||||
detaching = 1; | detaching = 1; | ||||
} | } | ||||
static int | static void | ||||
detach_proc(pid_t pid) | detach_proc(pid_t pid) | ||||
{ | { | ||||
int waitval; | |||||
/* stop the child so that we can detach */ | /* stop the child so that we can detach */ | ||||
kill(pid, SIGSTOP); | kill(pid, SIGSTOP); | ||||
if (waitpid(pid, &waitval, 0) < 0) | if (waitpid(pid, NULL, 0) < 0) | ||||
err(1, "Unexpected stop in waitpid"); | err(1, "Unexpected stop in waitpid"); | ||||
if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0) | if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0) | ||||
err(1, "Can not detach the process"); | err(1, "Can not detach the process"); | ||||
kill(pid, SIGCONT); | kill(pid, SIGCONT); | ||||
} | |||||
return (waitval); | /* | ||||
* Determine the ABI. This is called after every exec, and when | |||||
* a process is first monitored. | |||||
*/ | |||||
static struct procabi * | |||||
find_abi(pid_t pid) | |||||
{ | |||||
struct procabi **pabi; | |||||
size_t len; | |||||
int error; | |||||
int mib[4]; | |||||
char progt[32]; | |||||
len = sizeof(progt); | |||||
mib[0] = CTL_KERN; | |||||
mib[1] = KERN_PROC; | |||||
mib[2] = KERN_PROC_SV_NAME; | |||||
mib[3] = pid; | |||||
error = sysctl(mib, 4, progt, &len, NULL, 0); | |||||
if (error != 0) | |||||
err(2, "can not get sysvec name"); | |||||
SET_FOREACH(pabi, procabi) { | |||||
if (strcmp((*pabi)->type, progt) == 0) | |||||
break; | |||||
} | } | ||||
if (*pabi == NULL) | |||||
warnx("ABI %s for pid %ld is not supported", progt, (long)pid); | |||||
return (*pabi); | |||||
} | |||||
static void | |||||
new_proc(struct trussinfo *info, pid_t pid) | |||||
{ | |||||
struct procinfo *np; | |||||
/* | /* | ||||
* Change curthread member based on lwpid. | * If this happens it means there is a bug in truss. Unfortunately | ||||
* If it is a new thread, create a threadinfo structure | * this will kill any processes are attached to. | ||||
*/ | */ | ||||
LIST_FOREACH(np, &info->proclist, entries) { | |||||
if (np->pid == pid) | |||||
errx(1, "Duplicate process for pid %ld", (long)pid); | |||||
} | |||||
if (info->flags & FOLLOWFORKS) | |||||
ptrace(PT_FOLLOW_FORK, pid, NULL, 1); | |||||
kib: Add error checking there ? | |||||
np = calloc(1, sizeof(struct procinfo)); | |||||
np->pid = pid; | |||||
np->abi = find_abi(pid); | |||||
SLIST_INIT(&np->threadlist); | |||||
LIST_INSERT_HEAD(&info->proclist, np, entries); | |||||
} | |||||
static void | static void | ||||
find_thread(struct trussinfo *info, lwpid_t lwpid) | free_proc(struct procinfo *p) | ||||
{ | { | ||||
struct threadinfo *np; | struct threadinfo *t, *t2; | ||||
info->curthread = NULL; | SLIST_FOREACH_SAFE(t, &p->threadlist, entries, t2) { | ||||
SLIST_FOREACH(np, &info->threadlist, entries) { | free(t); | ||||
if (np->tid == lwpid) { | } | ||||
info->curthread = np; | LIST_REMOVE(p, entries); | ||||
free(p); | |||||
} | |||||
static void | |||||
detach_all_procs(struct trussinfo *info) | |||||
{ | |||||
struct procinfo *p, *p2; | |||||
LIST_FOREACH_SAFE(p, &info->proclist, entries, p2) { | |||||
detach_proc(p->pid); | |||||
free_proc(p); | |||||
} | |||||
} | |||||
static struct procinfo * | |||||
find_proc(struct trussinfo *info, pid_t pid) | |||||
{ | |||||
struct procinfo *np; | |||||
LIST_FOREACH(np, &info->proclist, entries) { | |||||
if (np->pid == pid) | |||||
return (np); | |||||
} | |||||
return (NULL); | |||||
} | |||||
/* | |||||
* Change curthread member based on (pid, lwpid). | |||||
* If it is a new thread, create a threadinfo structure. | |||||
*/ | |||||
static void | |||||
find_thread(struct trussinfo *info, pid_t pid, lwpid_t lwpid) | |||||
Not Done Inline ActionsDo you need pid there ? I mean, lwpids are global. kib: Do you need pid there ? I mean, lwpids are global. | |||||
jhbAuthorUnsubmitted Not Done Inline ActionsSo truss does not try to be perfect here. In particular, it does not cull exited threads from its internal thread list, and it does not fetch the list of LWPs when attaching to an existing process. Instead, it just remembers an LWP the first time it appears. However, if an LWP exits and is reused for another process that is also being followed (due to fork following), then it might get confused. I instead opted to just leave the stale LWPs as-is and avoid having to be exact in LWP handling. I suppose, though, that if an LWP is reused in another process, it's really the same as reusing in the same process. I assume that on reuse a new SCE event will arrive and the previous SCE for thr_exit() will just be lost (since there is never an SCX event to clean it up). I have another set of changes to ptrace() to add optional event reporting when threads are created and exit that I plan to use in gdb instead of the libthread_db style of setting breakpoints in the threading libraries. With that in place I could be more exact in LWP handling. OTOH, I still need to ensure that the 'proc' pointer for the thread is correct, so even if there were a global list of LWPs I would still need the 'pid' to set that. jhb: So truss does not try to be perfect here. In particular, it does not cull exited threads from… | |||||
{ | |||||
struct procinfo *np; | |||||
struct threadinfo *nt; | |||||
np = find_proc(info, pid); | |||||
assert(np != NULL); | |||||
SLIST_FOREACH(nt, &np->threadlist, entries) { | |||||
if (nt->tid == lwpid) { | |||||
info->curthread = nt; | |||||
return; | return; | ||||
} | } | ||||
} | } | ||||
np = (struct threadinfo *)calloc(1, sizeof(struct threadinfo)); | nt = calloc(1, sizeof(struct threadinfo)); | ||||
if (np == NULL) | if (nt == NULL) | ||||
err(1, "calloc() failed"); | err(1, "calloc() failed"); | ||||
np->tid = lwpid; | nt->proc = np; | ||||
SLIST_INSERT_HEAD(&info->threadlist, np, entries); | nt->tid = lwpid; | ||||
info->curthread = np; | SLIST_INSERT_HEAD(&np->threadlist, nt, entries); | ||||
info->curthread = nt; | |||||
} | } | ||||
/* | /* | ||||
* Start the traced process and wait until it stoped. | * When a process exits, it no longer has any threads left. However, | ||||
* Fill trussinfo structure. | * the main loop expects a valid curthread. In cases when a thread | ||||
* When this even returns, the traced process is in stop state. | * triggers the termination (e.g. calling exit or triggering a fault) | ||||
* we would ideally use that thread. However, if a process is killed | |||||
* by a signal sent from another process then there is no "correct" | |||||
* thread. We just punt and use the first thread. | |||||
*/ | */ | ||||
void | static void | ||||
waitevent(struct trussinfo *info) | find_exit_thread(struct trussinfo *info, pid_t pid) | ||||
{ | { | ||||
struct ptrace_lwpinfo lwpinfo; | struct procinfo *np; | ||||
static int pending_signal = 0; | struct threadinfo *nt; | ||||
int waitval; | |||||
ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal); | np = find_proc(info, pid); | ||||
pending_signal = 0; | assert(np != NULL); | ||||
detach: | if (SLIST_EMPTY(&np->threadlist)) { | ||||
if (detaching) { | /* | ||||
waitval = detach_proc(info->pid); | * If an existing process exits right after we attach | ||||
info->pr_why = S_DETACHED; | * to it but before it posts any events, there won't | ||||
info->pr_data = WEXITSTATUS(waitval); | * be any threads. Create a dummy thread and set its | ||||
* "before" time to the global start time. | |||||
*/ | |||||
nt = calloc(1, sizeof(struct threadinfo)); | |||||
if (nt == NULL) | |||||
err(1, "calloc() failed"); | |||||
nt->proc = np; | |||||
nt->tid = 0; | |||||
SLIST_INSERT_HEAD(&np->threadlist, nt, entries); | |||||
nt->before = info->start_time; | |||||
} | |||||
info->curthread = SLIST_FIRST(&np->threadlist); | |||||
} | |||||
static void | |||||
alloc_syscall(struct threadinfo *t, struct ptrace_lwpinfo *pl) | |||||
{ | |||||
assert(t->in_syscall == 0); | |||||
assert(t->cs.number == 0); | |||||
assert(t->cs.name == NULL); | |||||
assert(t->cs.args == NULL); | |||||
assert(t->cs.nargs == 0); | |||||
assert(t->cs.s_args == NULL); | |||||
t->cs.number = pl->pl_syscall_code; | |||||
t->cs.nargs = pl->pl_syscall_narg; | |||||
if (t->cs.nargs != 0) | |||||
t->cs.args = calloc(1 + t->cs.nargs, sizeof(t->cs.args[0])); | |||||
t->in_syscall = 1; | |||||
} | |||||
static void | |||||
free_syscall(struct threadinfo *t) | |||||
{ | |||||
int i; | |||||
free(t->cs.args); | |||||
if (t->cs.s_args) { | |||||
for (i = 0; i < t->cs.nargs; i++) | |||||
free(t->cs.s_args[i]); | |||||
free(t->cs.s_args); | |||||
} | |||||
memset(&t->cs, 0, sizeof(t->cs)); | |||||
t->in_syscall = 0; | |||||
} | |||||
static void | |||||
enter_syscall(struct trussinfo *info, struct ptrace_lwpinfo *pl) | |||||
{ | |||||
struct threadinfo *t; | |||||
struct syscall *sc; | |||||
int i; | |||||
t = info->curthread; | |||||
alloc_syscall(t, pl); | |||||
if (t->cs.nargs != 0 && t->proc->abi->fetch_args(info) != 0) { | |||||
free_syscall(t); | |||||
return; | return; | ||||
} | } | ||||
if (waitpid(info->pid, &waitval, 0) == -1) { | if (t->cs.number >= 0 && t->cs.number < t->proc->abi->nsyscalls) | ||||
if (errno == EINTR) | t->cs.name = t->proc->abi->syscallnames[t->cs.number]; | ||||
goto detach; | if (t->cs.name == NULL) | ||||
err(1, "Unexpected stop in waitpid"); | fprintf(info->outfile, "-- UNKNOWN %s SYSCALL %d --\n", | ||||
t->proc->abi->type, t->cs.number); | |||||
sc = get_syscall(t->cs.name); | |||||
if (sc) | |||||
t->cs.nargs = sc->nargs; | |||||
else { | |||||
#if DEBUG | |||||
fprintf(stderr, "unknown syscall %s -- setting " | |||||
"args to %d\n", t->cs.name, t->cs.nargs); | |||||
#endif | |||||
} | } | ||||
Not Done Inline ActionsThis is strange, you trust the built-in lists more that the kernel, which now reports the number of args which are used, not some number which is supposed to be used. kib: This is strange, you trust the built-in lists more that the kernel, which now reports the… | |||||
jhbAuthorUnsubmitted Not Done Inline ActionsThis is how the existing code worked. However, what matters is that 'nargs' after this point is really the number of strings to print (items in s_args). When there is no special formatting magic, each raw argument is identity mapped to a hex string in s_args. The formatting rules for a given system call are free to coalesce arguments (e.g. print two raw arguments as one logical value if that makes sense), or print arguments using another source (e.g. the hack to print the pipe fd's as an argument to match libc even though the kernel returns them via retval[]). You might also want to omit some arguments (e.g. the padding words around off_t arguments on some platforms). jhb: This is how the existing code worked. However, what matters is that 'nargs' after this point… | |||||
if (WIFCONTINUED(waitval)) { | t->cs.s_args = calloc(1 + t->cs.nargs, sizeof(char *)); | ||||
info->pr_why = S_NONE; | t->cs.sc = sc; | ||||
/* | |||||
* At this point, we set up the system call arguments. | |||||
* We ignore any OUT ones, however -- those are arguments that | |||||
* are set by the system call, and so are probably meaningless | |||||
* now. This doesn't currently support arguments that are | |||||
* passed in *and* out, however. | |||||
*/ | |||||
if (t->cs.name != NULL) { | |||||
#if DEBUG | |||||
fprintf(stderr, "syscall %s(", t->cs.name); | |||||
#endif | |||||
for (i = 0; i < t->cs.nargs; i++) { | |||||
#if DEBUG | |||||
fprintf(stderr, "0x%lx%s", sc ? | |||||
t->cs.args[sc->args[i].offset] : t->cs.args[i], | |||||
i < (t->cs.nargs - 1) ? "," : ""); | |||||
#endif | |||||
if (sc && !(sc->args[i].type & OUT)) { | |||||
t->cs.s_args[i] = print_arg(&sc->args[i], | |||||
t->cs.args, 0, info); | |||||
} | |||||
} | |||||
#if DEBUG | |||||
fprintf(stderr, ")\n"); | |||||
#endif | |||||
} | |||||
clock_gettime(CLOCK_REALTIME, &t->before); | |||||
} | |||||
static void | |||||
exit_syscall(struct trussinfo *info, struct ptrace_lwpinfo *pl) | |||||
{ | |||||
struct threadinfo *t; | |||||
struct procinfo *p; | |||||
struct syscall *sc; | |||||
long retval[2]; | |||||
int errorp, i; | |||||
t = info->curthread; | |||||
if (!t->in_syscall) | |||||
return; | return; | ||||
clock_gettime(CLOCK_REALTIME, &t->after); | |||||
p = t->proc; | |||||
if (p->abi->fetch_retval(info, retval, &errorp) < 0) { | |||||
free_syscall(t); | |||||
return; | |||||
} | } | ||||
if (WIFEXITED(waitval)) { | |||||
info->pr_why = S_EXIT; | sc = t->cs.sc; | ||||
info->pr_data = WEXITSTATUS(waitval); | if (sc == NULL) { | ||||
for (i = 0; i < t->cs.nargs; i++) | |||||
asprintf(&t->cs.s_args[i], "0x%lx", t->cs.args[i]); | |||||
} else { | |||||
/* | |||||
* Here, we only look for arguments that have OUT masked in -- | |||||
* otherwise, they were handled in enter_syscall(). | |||||
*/ | |||||
for (i = 0; i < sc->nargs; i++) { | |||||
char *temp; | |||||
if (sc->args[i].type & OUT) { | |||||
/* | |||||
* If an error occurred, then don't bother | |||||
* getting the data; it may not be valid. | |||||
*/ | |||||
if (errorp) { | |||||
asprintf(&temp, "0x%lx", | |||||
t->cs.args[sc->args[i].offset]); | |||||
} else { | |||||
temp = print_arg(&sc->args[i], | |||||
t->cs.args, retval, info); | |||||
} | |||||
t->cs.s_args[i] = temp; | |||||
} | |||||
} | |||||
} | |||||
print_syscall_ret(info, t->cs.name, t->cs.nargs, t->cs.s_args, | |||||
errorp, retval, sc); | |||||
free_syscall(t); | |||||
/* | |||||
* If the process executed a new image, check the ABI. If the | |||||
* new ABI isn't supported, stop tracing this process. | |||||
*/ | |||||
if (pl->pl_flags & PL_FLAG_EXEC) { | |||||
p->abi = find_abi(p->pid); | |||||
if (p->abi == NULL) { | |||||
detach_proc(p->pid); | |||||
free_proc(p); | |||||
} | |||||
} | |||||
} | |||||
/* | |||||
* Wait for events until all the processes have exited or truss has been | |||||
* asked to stop. | |||||
*/ | |||||
void | |||||
eventloop(struct trussinfo *info) | |||||
{ | |||||
struct ptrace_lwpinfo pl; | |||||
struct timespec timediff; | |||||
siginfo_t si; | |||||
char *signame; | |||||
int pending_signal; | |||||
while (!LIST_EMPTY(&info->proclist)) { | |||||
if (detaching) { | |||||
detach_all_procs(info); | |||||
return; | return; | ||||
} | } | ||||
if (WIFSTOPPED(waitval)) { | |||||
ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo, | if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) { | ||||
sizeof(lwpinfo)); | if (errno == EINTR) | ||||
find_thread(info, lwpinfo.pl_lwpid); | continue; | ||||
switch (WSTOPSIG(waitval)) { | err(1, "Unexpected error from waitid"); | ||||
case SIGTRAP: | } | ||||
if (lwpinfo.pl_flags & PL_FLAG_SCE) { | |||||
info->pr_why = S_SCE; | assert(si.si_signo == SIGCHLD); | ||||
info->curthread->in_syscall = 1; | |||||
switch (si.si_code) { | |||||
case CLD_EXITED: | |||||
case CLD_KILLED: | |||||
case CLD_DUMPED: | |||||
find_exit_thread(info, si.si_pid); | |||||
if ((info->flags & COUNTONLY) == 0) { | |||||
if (info->flags & FOLLOWFORKS) | |||||
fprintf(info->outfile, "%5d: ", | |||||
si.si_pid); | |||||
clock_gettime(CLOCK_REALTIME, | |||||
&info->curthread->after); | |||||
if (info->flags & ABSOLUTETIMESTAMPS) { | |||||
timespecsubt(&info->curthread->after, | |||||
&info->start_time, &timediff); | |||||
fprintf(info->outfile, "%jd.%09ld ", | |||||
(intmax_t)timediff.tv_sec, | |||||
timediff.tv_nsec); | |||||
} | |||||
if (info->flags & RELATIVETIMESTAMPS) { | |||||
timespecsubt(&info->curthread->after, | |||||
&info->curthread->before, | |||||
&timediff); | |||||
fprintf(info->outfile, "%jd.%09ld ", | |||||
(intmax_t)timediff.tv_sec, | |||||
timediff.tv_nsec); | |||||
} | |||||
if (si.si_code == CLD_EXITED) | |||||
fprintf(info->outfile, | |||||
"process exit, rval = %u\n", | |||||
si.si_status); | |||||
else | |||||
fprintf(info->outfile, | |||||
"process killed, signal = %u%s\n", | |||||
si.si_status, | |||||
si.si_code == CLD_DUMPED ? | |||||
" (core dumped)" : ""); | |||||
} | |||||
free_proc(info->curthread->proc); | |||||
info->curthread = NULL; | |||||
break; | break; | ||||
} else if (lwpinfo.pl_flags & PL_FLAG_SCX) { | case CLD_TRAPPED: | ||||
info->pr_why = S_SCX; | if (ptrace(PT_LWPINFO, si.si_pid, (caddr_t)&pl, | ||||
info->curthread->in_syscall = 0; | sizeof(pl)) == -1) | ||||
break; | err(1, "ptrace(PT_LWPINFO)"); | ||||
} else { | |||||
if (pl.pl_flags & PL_FLAG_CHILD) { | |||||
new_proc(info, si.si_pid); | |||||
assert(LIST_FIRST(&info->proclist)->abi != | |||||
NULL); | |||||
} | |||||
find_thread(info, si.si_pid, pl.pl_lwpid); | |||||
if (si.si_status == SIGTRAP) { | |||||
if (pl.pl_flags & PL_FLAG_SCE) | |||||
enter_syscall(info, &pl); | |||||
else if (pl.pl_flags & PL_FLAG_SCX) | |||||
exit_syscall(info, &pl); | |||||
else | |||||
errx(1, | errx(1, | ||||
"pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX", | "pl_flags %x contains neither PL_FLAG_SCE nor PL_FLAG_SCX", | ||||
lwpinfo.pl_flags); | pl.pl_flags); | ||||
pending_signal = 0; | |||||
} else if (pl.pl_flags & PL_FLAG_CHILD) { | |||||
clock_gettime(CLOCK_REALTIME, | |||||
&info->curthread->after); | |||||
assert(info->flags & FOLLOWFORKS); | |||||
fprintf(info->outfile, "%5d: ", si.si_pid); | |||||
if (info->flags & ABSOLUTETIMESTAMPS) { | |||||
timespecsubt(&info->curthread->after, | |||||
&info->start_time, &timediff); | |||||
fprintf(info->outfile, "%jd.%09ld ", | |||||
(intmax_t)timediff.tv_sec, | |||||
timediff.tv_nsec); | |||||
} | } | ||||
default: | if (info->flags & RELATIVETIMESTAMPS) { | ||||
info->pr_why = S_SIG; | timediff.tv_sec = 0; | ||||
info->pr_data = WSTOPSIG(waitval); | timediff.tv_nsec = 0; | ||||
pending_signal = info->pr_data; | fprintf(info->outfile, "%jd.%09ld ", | ||||
break; | (intmax_t)timediff.tv_sec, | ||||
timediff.tv_nsec); | |||||
} | } | ||||
fprintf(info->outfile, "<new process>\n"); | |||||
pending_signal = 0; | |||||
} else if ((info->flags & NOSIGS) == 0) { | |||||
if (info->flags & FOLLOWFORKS) | |||||
fprintf(info->outfile, "%5d: ", | |||||
si.si_pid); | |||||
if (info->flags & ABSOLUTETIMESTAMPS) { | |||||
timespecsubt(&info->curthread->after, | |||||
&info->start_time, &timediff); | |||||
fprintf(info->outfile, "%jd.%09ld ", | |||||
(intmax_t)timediff.tv_sec, | |||||
timediff.tv_nsec); | |||||
} | } | ||||
if (WIFSIGNALED(waitval)) { | if (info->flags & RELATIVETIMESTAMPS) { | ||||
info->pr_why = S_EXIT; | timespecsubt(&info->curthread->after, | ||||
info->pr_data = 0; | &info->curthread->before, | ||||
return; | &timediff); | ||||
fprintf(info->outfile, "%jd.%09ld ", | |||||
(intmax_t)timediff.tv_sec, | |||||
timediff.tv_nsec); | |||||
} | |||||
signame = strsig(si.si_status); | |||||
fprintf(info->outfile, | |||||
"SIGNAL %u (%s)\n", si.si_status, | |||||
signame == NULL ? "?" : signame); | |||||
pending_signal = si.si_status; | |||||
} | |||||
ptrace(PT_SYSCALL, si.si_pid, (caddr_t)1, | |||||
pending_signal); | |||||
break; | |||||
case CLD_STOPPED: | |||||
errx(1, "waitid reported CLD_STOPPED"); | |||||
case CLD_CONTINUED: | |||||
break; | |||||
} | |||||
} | } | ||||
} | } |
Add error checking there ?