summaryrefslogtreecommitdiffstats
path: root/usr.bin/truss
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/truss')
-rw-r--r--usr.bin/truss/amd64-fbsd.c271
-rw-r--r--usr.bin/truss/amd64-fbsd32.c275
-rw-r--r--usr.bin/truss/amd64-linux32.c255
-rw-r--r--usr.bin/truss/arm-fbsd.c293
-rw-r--r--usr.bin/truss/extern.h39
-rw-r--r--usr.bin/truss/i386-fbsd.c261
-rw-r--r--usr.bin/truss/i386-linux.c253
-rw-r--r--usr.bin/truss/main.c234
-rw-r--r--usr.bin/truss/mips-fbsd.c329
-rw-r--r--usr.bin/truss/powerpc-fbsd.c313
-rw-r--r--usr.bin/truss/powerpc64-fbsd.c277
-rw-r--r--usr.bin/truss/setup.c543
-rw-r--r--usr.bin/truss/sparc64-fbsd.c299
-rw-r--r--usr.bin/truss/syscall.h10
-rw-r--r--usr.bin/truss/syscalls.c176
-rw-r--r--usr.bin/truss/truss.h61
16 files changed, 1106 insertions, 2783 deletions
diff --git a/usr.bin/truss/amd64-fbsd.c b/usr.bin/truss/amd64-fbsd.c
index eeea8db..ade9322 100644
--- a/usr.bin/truss/amd64-fbsd.c
+++ b/usr.bin/truss/amd64-fbsd.c
@@ -29,290 +29,103 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * FreeBSD/amd64-specific system call handling. This is probably the most
- * complex part of the entire truss program, although I've got lots of
- * it handled relatively cleanly now. The system call names are generated
- * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
- * names used for the various structures are confusing, I sadly admit.
- */
+/* FreeBSD/amd64-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/psl.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "syscalls.h"
-static int nsyscalls = nitems(syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct freebsd_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long *args;
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct freebsd_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct freebsd_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct freebsd_syscall *fsc)
-{
- int i;
-
- free(fsc->args);
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in amd64/amd64/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-amd64_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+amd64_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct ptrace_io_desc iorequest;
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
+ struct current_syscall *cs;
lwpid_t tid;
- int i, reg, syscall_num;
+ u_int i, reg;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
+ return (-1);
}
/*
- * FreeBSD has two special kinds of system call redirctions --
+ * FreeBSD has two special kinds of system call redirections --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basically; the latter is for quad-aligned arguments.
+ *
+ * The system call argument count and code from ptrace() already
+ * account for these, but we need to skip over %rax if it contains
+ * either of these values.
*/
reg = 0;
- syscall_num = regs.r_rax;
- switch (syscall_num) {
+ switch (regs.r_rax) {
case SYS_syscall:
case SYS___syscall:
- syscall_num = regs.r_rdi;
reg++;
break;
}
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
- }
-
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "fork") == 0 ||
- strcmp(fsc->name, "pdfork") == 0 ||
- strcmp(fsc->name, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
- for (i = 0; i < nargs && reg < 6; i++, reg++) {
+ for (i = 0; i < narg && reg < 6; i++, reg++) {
switch (reg) {
- case 0: fsc->args[i] = regs.r_rdi; break;
- case 1: fsc->args[i] = regs.r_rsi; break;
- case 2: fsc->args[i] = regs.r_rdx; break;
- case 3: fsc->args[i] = regs.r_rcx; break;
- case 4: fsc->args[i] = regs.r_r8; break;
- case 5: fsc->args[i] = regs.r_r9; break;
+ case 0: cs->args[i] = regs.r_rdi; break;
+ case 1: cs->args[i] = regs.r_rsi; break;
+ case 2: cs->args[i] = regs.r_rdx; break;
+ case 3: cs->args[i] = regs.r_rcx; break;
+ case 4: cs->args[i] = regs.r_r8; break;
+ case 5: cs->args[i] = regs.r_r9; break;
}
}
- if (nargs > i) {
+ if (narg > i) {
iorequest.piod_op = PIOD_READ_D;
iorequest.piod_offs = (void *)(regs.r_rsp + sizeof(register_t));
- iorequest.piod_addr = &fsc->args[i];
- iorequest.piod_len = (nargs - i) * sizeof(register_t);
+ iorequest.piod_addr = &cs->args[i];
+ iorequest.piod_len = (narg - i) * sizeof(register_t);
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
if (iorequest.piod_len == 0)
- return;
- }
-
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
+ return (-1);
}
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%lx%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
- }
-
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
-/*
- * And when the system call is done, we handle it here.
- * Currently, no attempt is made to ensure that the system calls
- * match -- this needs to be fixed (and is, in fact, why S_SCX includes
- * the system call number instead of, say, an error status).
- */
-
-long
-amd64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+amd64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.r_rax;
- errorp = !!(regs.r_rflags & PSL_C);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
-
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
- }
- }
- }
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
-
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
+ retval[0] = regs.r_rax;
+ retval[1] = regs.r_rdx;
+ *errorp = !!(regs.r_rflags & PSL_C);
+ return (0);
+}
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi amd64_fbsd = {
+ "FreeBSD ELF64",
+ syscallnames,
+ nitems(syscallnames),
+ amd64_fetch_args,
+ amd64_fetch_retval
+};
- return (retval);
-}
+PROCABI(amd64_fbsd);
diff --git a/usr.bin/truss/amd64-fbsd32.c b/usr.bin/truss/amd64-fbsd32.c
index 40aa152..84a4b6b 100644
--- a/usr.bin/truss/amd64-fbsd32.c
+++ b/usr.bin/truss/amd64-fbsd32.c
@@ -29,290 +29,109 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * FreeBSD/i386-specific system call handling. This is probably the most
- * complex part of the entire truss program, although I've got lots of
- * it handled relatively cleanly now. The system call names are generated
- * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
- * names used for the various structures are confusing, I sadly admit.
- */
+/* FreeBSD/i386-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/psl.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "freebsd32_syscalls.h"
-static int nsyscalls = nitems(freebsd32_syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct freebsd32_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long *args;
- unsigned int *args32;
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct freebsd32_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct freebsd32_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct freebsd32_syscall *fsc)
-{
- int i;
-
- free(fsc->args);
- free(fsc->args32);
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-amd64_fbsd32_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+amd64_fbsd32_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct ptrace_io_desc iorequest;
struct reg regs;
- struct freebsd32_syscall *fsc;
- struct syscall *sc;
- lwpid_t tid;
+ struct current_syscall *cs;
+ unsigned int args32[narg];
unsigned long parm_offset;
- int i, syscall_num;
+ lwpid_t tid;
+ u_int i;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
+ return (-1);
}
parm_offset = regs.r_rsp + sizeof(int);
/*
- * FreeBSD has two special kinds of system call redirctions --
+ * FreeBSD has two special kinds of system call redirections --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basically; the latter is for quad-aligned arguments.
+ *
+ * The system call argument count and code from ptrace() already
+ * account for these, but we need to skip over the first argument.
*/
- syscall_num = regs.r_rax;
- switch (syscall_num) {
+ switch (regs.r_rax) {
case SYS_syscall:
- syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0);
parm_offset += sizeof(int);
break;
case SYS___syscall:
- syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0);
parm_offset += sizeof(quad_t);
break;
}
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : freebsd32_syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
- }
-
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "fork") == 0 ||
- strcmp(fsc->name, "pdfork") == 0 ||
- strcmp(fsc->name, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args32 = malloc((1 + nargs) * sizeof(unsigned int));
iorequest.piod_op = PIOD_READ_D;
iorequest.piod_offs = (void *)parm_offset;
- iorequest.piod_addr = fsc->args32;
- iorequest.piod_len = (1 + nargs) * sizeof(unsigned int);
+ iorequest.piod_addr = args32;
+ iorequest.piod_len = sizeof(args32);
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
- if (iorequest.piod_len == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
- for (i = 0; i < nargs + 1; i++)
- fsc->args[i] = fsc->args32[i];
-
- sc = NULL;
- if (fsc->name)
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
- }
-
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
+ if (iorequest.piod_len == 0) {
+ return (-1);
}
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ for (i = 0; i < narg; i++)
+ cs->args[i] = args32[i];
+ return (0);
}
-/*
- * And when the system call is done, we handle it here.
- * Currently, no attempt is made to ensure that the system calls
- * match -- this needs to be fixed (and is, in fact, why S_SCX includes
- * the system call number instead of, say, an error status).
- */
-
-long
-amd64_fbsd32_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+amd64_fbsd32_fetch_retval(struct trussinfo *trussinfo, long *retval,
+ int *errorp)
{
struct reg regs;
- struct freebsd32_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.r_rax;
- errorp = !!(regs.r_rflags & PSL_C);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
-
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
- }
- }
- }
+ retval[0] = regs.r_rax & 0xffffffff;
+ retval[1] = regs.r_rdx & 0xffffffff;
+ *errorp = !!(regs.r_rflags & PSL_C);
+ return (0);
+}
- if (fsc->name != NULL && (strcmp(fsc->name, "freebsd32_execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
+static struct procabi amd64_fbsd32 = {
+ "FreeBSD ELF32",
+ freebsd32_syscallnames,
+ nitems(freebsd32_syscallnames),
+ amd64_fbsd32_fetch_args,
+ amd64_fbsd32_fetch_retval
+};
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
+PROCABI(amd64_fbsd32);
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi amd64_fbsd32_aout = {
+ "FreeBSD a.out",
+ freebsd32_syscallnames,
+ nitems(freebsd32_syscallnames),
+ amd64_fbsd32_fetch_args,
+ amd64_fbsd32_fetch_retval
+};
- return (retval);
-}
+PROCABI(amd64_fbsd32_aout);
diff --git a/usr.bin/truss/amd64-linux32.c b/usr.bin/truss/amd64-linux32.c
index b2d0d04..4f64af3 100644
--- a/usr.bin/truss/amd64-linux32.c
+++ b/usr.bin/truss/amd64-linux32.c
@@ -29,123 +29,36 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * Linux/i386-specific system call handling. Given how much of this code
- * is taken from the freebsd equivalent, I can probably put even more of
- * it in support routines that can be used by any personality support.
- */
+/* Linux/i386-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <machine/reg.h>
#include <machine/psl.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "linux32_syscalls.h"
-static int nsyscalls = nitems(linux32_syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct linux_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long args[5];
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct linux_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct linux_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct linux_syscall *fsc)
-{
- int i;
-
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-amd64_linux32_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+amd64_linux32_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct reg regs;
- struct linux_syscall *fsc;
- struct syscall *sc;
+ struct current_syscall *cs;
lwpid_t tid;
- int i, syscall_num;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
- }
-
- syscall_num = regs.r_rax;
-
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : linux32_syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
+ return (-1);
}
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "linux_fork") == 0 ||
- strcmp(fsc->name, "linux_vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
/*
* Linux passes syscall arguments in registers, not
* on the stack. Fortunately, we've got access to the
@@ -153,60 +66,22 @@ amd64_linux32_syscall_entry(struct trussinfo *trussinfo, int nargs)
* number of arguments. And what does linux do for syscalls
* that have more than five arguments?
*/
-
- fsc->args[0] = regs.r_rbx;
- fsc->args[1] = regs.r_rcx;
- fsc->args[2] = regs.r_rdx;
- fsc->args[3] = regs.r_rsi;
- fsc->args[4] = regs.r_rdi;
-
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
+ switch (narg) {
+ default:
+ cs->args[5] = regs.r_rbp; /* Unconfirmed */
+ case 5:
+ cs->args[4] = regs.r_rdi;
+ case 4:
+ cs->args[3] = regs.r_rsi;
+ case 3:
+ cs->args[2] = regs.r_rdx;
+ case 2:
+ cs->args[1] = regs.r_rcx;
+ case 1:
+ cs->args[0] = regs.r_rbx;
}
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
- }
-
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
/*
@@ -224,83 +99,43 @@ static const int bsd_to_linux_errno[] = {
-6,
};
-long
-amd64_linux32_syscall_exit(struct trussinfo *trussinfo,
- int syscall_num __unused)
+static int
+amd64_linux32_fetch_retval(struct trussinfo *trussinfo, long *retval,
+ int *errorp)
{
struct reg regs;
- struct linux_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
+ size_t i;
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.r_rax;
- errorp = !!(regs.r_rflags & PSL_C);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
+ retval[0] = regs.r_rax & 0xffffffff;
+ retval[1] = regs.r_rdx & 0xffffffff;
+ *errorp = !!(regs.r_rflags & PSL_C);
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
+ if (*errorp) {
+ for (i = 0; i < nitems(bsd_to_linux_errno); i++) {
+ if (retval[0] == bsd_to_linux_errno[i]) {
+ retval[0] = i;
+ return (0);
}
}
- }
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
- if (errorp) {
- for (i = 0; (size_t)i < nitems(bsd_to_linux_errno); i++) {
- if (retval == bsd_to_linux_errno[i])
- break;
- }
+ /* XXX: How to handle unknown errors? */
}
+ return (0);
+}
- if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
-
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- errorp ? i : retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi amd64_linux32 = {
+ "Linux ELF32",
+ linux32_syscallnames,
+ nitems(linux32_syscallnames),
+ amd64_linux32_fetch_args,
+ amd64_linux32_fetch_retval
+};
- return (retval);
-}
+PROCABI(amd64_linux32);
diff --git a/usr.bin/truss/arm-fbsd.c b/usr.bin/truss/arm-fbsd.c
index 8fa9fb90..4b8222a 100644
--- a/usr.bin/truss/arm-fbsd.c
+++ b/usr.bin/truss/arm-fbsd.c
@@ -29,17 +29,11 @@
* SUCH DAMAGE.
*/
-/*
- * FreeBSD/arm-specific system call handling. This is probably the most
- * complex part of the entire truss program, although I've got lots of
- * it handled relatively cleanly now. The system call names are generated
- * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
- * names used for the various structures are confusing, I sadly admit.
- */
-
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
-#include <sys/types.h>
+
+/* FreeBSD/arm-specific system call handling. */
+
#include <sys/ptrace.h>
#include <sys/syscall.h>
@@ -47,95 +41,35 @@ __FBSDID("$FreeBSD$");
#include <machine/armreg.h>
#include <machine/ucontext.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <err.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "syscalls.h"
-
-static int nsyscalls = nitems(syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct freebsd_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long *args;
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct freebsd_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct freebsd_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct freebsd_syscall *fsc)
-{
- int i;
-
- free(fsc->args);
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-arm_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+arm_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct ptrace_io_desc iorequest;
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
+ struct current_syscall *cs;
lwpid_t tid;
- int i, syscall_num;
- register_t *ap;
+ u_int i, reg, syscall_num;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
+ return (-1);
}
- ap = &regs.r[0];
/*
- * FreeBSD has two special kinds of system call redirctions --
+ * FreeBSD has two special kinds of system call redirections --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basically; the latter is for quad-aligned arguments.
+ *
+ * The system call argument count and code from ptrace() already
+ * account for these, but we need to skip over the first argument.
*/
#ifdef __ARM_EABI__
syscall_num = regs.r[7];
@@ -143,207 +77,62 @@ arm_syscall_entry(struct trussinfo *trussinfo, int nargs)
if ((syscall_num = ptrace(PT_READ_I, tid,
(caddr_t)(regs.r[_REG_PC] - INSN_SIZE), 0)) == -1) {
fprintf(trussinfo->outfile, "-- CANNOT READ PC --\n");
- return;
+ return (-1);
}
syscall_num = syscall_num & 0x000fffff;
#endif
+
+ reg = 0;
switch (syscall_num) {
case SYS_syscall:
- syscall_num = *ap++;
- nargs--;
+ reg = 1;
break;
case SYS___syscall:
- syscall_num = ap[_QUAD_LOWWORD];
- ap += 2;
- nargs -= 2;
+ reg = 2;
break;
}
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
- }
-
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "fork") == 0 ||
- strcmp(fsc->name, "pdfork") == 0 ||
- strcmp(fsc->name, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
- switch (nargs) {
- default:
- /*
- * The OS doesn't seem to allow more than 10 words of
- * parameters (yay!). So we shouldn't be here.
- */
- warn("More than 10 words (%d) of arguments!\n", nargs);
- break;
- case 10:
- case 9:
- case 8:
- case 7:
- case 6:
- case 5:
- /*
- * If there are 7-10 words of arguments, they are placed
- * on the stack, as is normal for other processors.
- * The fall-through for all of these is deliberate!!!
- */
- // XXX BAD constant used here
+ for (i = 0; i < narg && reg < 4; i++, reg++)
+ cs->args[i] = regs.r[reg];
+ if (narg > i) {
iorequest.piod_op = PIOD_READ_D;
iorequest.piod_offs = (void *)(regs.r_sp +
4 * sizeof(uint32_t));
- iorequest.piod_addr = &fsc->args[4];
- iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]);
+ iorequest.piod_addr = &cs->args[i];
+ iorequest.piod_len = (narg - i) * sizeof(cs->args[0]);
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
if (iorequest.piod_len == 0)
- return;
- case 4: fsc->args[3] = ap[3];
- case 3: fsc->args[2] = ap[2];
- case 2: fsc->args[1] = ap[1];
- case 1: fsc->args[0] = ap[0];
- case 0: break;
+ return (-1);
}
- sc = NULL;
- if (fsc->name)
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
- }
-
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
- }
-
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
-/*
- * And when the system call is done, we handle it here.
- * Currently, no attempt is made to ensure that the system calls
- * match -- this needs to be fixed (and is, in fact, why S_SCX includes
- * the system call number instead of, say, an error status).
- */
-
-long
-arm_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+arm_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.r[0];
- errorp = !!(regs.r_cpsr & PSR_C);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
-
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
- }
- }
- }
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
-
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
+ /* XXX: Does not have the __ARMEB__ handling for __syscall(). */
+ retval[0] = regs.r[0];
+ retval[1] = regs.r[1];
+ *errorp = !!(regs.r_cpsr & PSR_C);
+ return (0);
+}
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi arm_fbsd = {
+ "FreeBSD ELF32",
+ syscallnames,
+ nitems(syscallnames),
+ arm_fetch_args,
+ arm_fetch_retval
+};
- return (retval);
-}
+PROCABI(arm_fbsd);
diff --git a/usr.bin/truss/extern.h b/usr.bin/truss/extern.h
index 820f4f0..708d055 100644
--- a/usr.bin/truss/extern.h
+++ b/usr.bin/truss/extern.h
@@ -31,42 +31,9 @@
* $FreeBSD$
*/
-extern int setup_and_wait(char **);
-extern int start_tracing(pid_t);
+extern void setup_and_wait(struct trussinfo *, char **);
+extern void start_tracing(struct trussinfo *, pid_t);
extern void restore_proc(int);
-extern void waitevent(struct trussinfo *);
+extern void eventloop(struct trussinfo *);
extern const char *ioctlname(unsigned long val);
extern char *strsig(int sig);
-#ifdef __arm__
-extern void arm_syscall_entry(struct trussinfo *, int);
-extern long arm_syscall_exit(struct trussinfo *, int);
-#endif
-#ifdef __amd64__
-extern void amd64_syscall_entry(struct trussinfo *, int);
-extern long amd64_syscall_exit(struct trussinfo *, int);
-extern void amd64_linux32_syscall_entry(struct trussinfo *, int);
-extern long amd64_linux32_syscall_exit(struct trussinfo *, int);
-extern void amd64_fbsd32_syscall_entry(struct trussinfo *, int);
-extern long amd64_fbsd32_syscall_exit(struct trussinfo *, int);
-#endif
-#ifdef __i386__
-extern void i386_syscall_entry(struct trussinfo *, int);
-extern long i386_syscall_exit(struct trussinfo *, int);
-extern void i386_linux_syscall_entry(struct trussinfo *, int);
-extern long i386_linux_syscall_exit(struct trussinfo *, int);
-#endif
-#ifdef __powerpc__
-extern void powerpc_syscall_entry(struct trussinfo *, int);
-extern long powerpc_syscall_exit(struct trussinfo *, int);
-extern void powerpc64_syscall_entry(struct trussinfo *, int);
-extern long powerpc64_syscall_exit(struct trussinfo *, int);
-#endif
-#ifdef __sparc64__
-extern void sparc64_syscall_entry(struct trussinfo *, int);
-extern long sparc64_syscall_exit(struct trussinfo *, int);
-#endif
-#ifdef __mips__
-extern void mips_syscall_entry(struct trussinfo *, int);
-extern long mips_syscall_exit(struct trussinfo *, int);
-#endif
-
diff --git a/usr.bin/truss/i386-fbsd.c b/usr.bin/truss/i386-fbsd.c
index ef326f0..029815e 100644
--- a/usr.bin/truss/i386-fbsd.c
+++ b/usr.bin/truss/i386-fbsd.c
@@ -29,284 +29,103 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * FreeBSD/i386-specific system call handling. This is probably the most
- * complex part of the entire truss program, although I've got lots of
- * it handled relatively cleanly now. The system call names are generated
- * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
- * names used for the various structures are confusing, I sadly admit.
- */
+/* FreeBSD/i386-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/psl.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "syscalls.h"
-static int nsyscalls = nitems(syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct freebsd_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long *args;
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct freebsd_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct freebsd_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct freebsd_syscall *fsc)
-{
- int i;
-
- free(fsc->args);
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-i386_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+i386_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct ptrace_io_desc iorequest;
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
+ struct current_syscall *cs;
lwpid_t tid;
unsigned int parm_offset;
- int i, syscall_num;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
+ return (-1);
}
parm_offset = regs.r_esp + sizeof(int);
/*
- * FreeBSD has two special kinds of system call redirctions --
+ * FreeBSD has two special kinds of system call redirections --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basically; the latter is for quad-aligned arguments.
+ *
+ * The system call argument count and code from ptrace() already
+ * account for these, but we need to skip over the first argument.
*/
- syscall_num = regs.r_eax;
- switch (syscall_num) {
+ switch (regs.r_eax) {
case SYS_syscall:
- syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0);
parm_offset += sizeof(int);
break;
case SYS___syscall:
- syscall_num = ptrace(PT_READ_D, tid, (caddr_t)parm_offset, 0);
parm_offset += sizeof(quad_t);
break;
}
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
- }
-
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "fork") == 0 ||
- strcmp(fsc->name, "pdfork") == 0 ||
- strcmp(fsc->name, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
iorequest.piod_op = PIOD_READ_D;
iorequest.piod_offs = (void *)parm_offset;
- iorequest.piod_addr = fsc->args;
- iorequest.piod_len = (1 + nargs) * sizeof(unsigned long);
+ iorequest.piod_addr = cs->args;
+ iorequest.piod_len = narg * sizeof(unsigned long);
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
if (iorequest.piod_len == 0)
- return;
-
- sc = NULL;
- if (fsc->name)
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
- }
-
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
- }
-
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
+ return (-1);
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
-/*
- * And when the system call is done, we handle it here.
- * Currently, no attempt is made to ensure that the system calls
- * match -- this needs to be fixed (and is, in fact, why S_SCX includes
- * the system call number instead of, say, an error status).
- */
-
-long
-i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+i386_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.r_eax;
- errorp = !!(regs.r_eflags & PSL_C);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
-
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- for (i = 0; i < sc->nargs; i++) {
- char *temp;
+ retval[0] = regs.r_eax;
+ retval[1] = regs.r_edx;
+ *errorp = !!(regs.r_eflags & PSL_C);
+ return (0);
+}
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
- }
- }
- }
+static struct procabi i386_fbsd = {
+ "FreeBSD ELF32",
+ syscallnames,
+ nitems(syscallnames),
+ i386_fetch_args,
+ i386_fetch_retval
+};
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
+PROCABI(i386_fbsd);
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
+static struct procabi i386_fbsd_aout = {
+ "FreeBSD a.out",
+ syscallnames,
+ nitems(syscallnames),
+ i386_fetch_args,
+ i386_fetch_retval
+};
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+PROCABI(i386_fbsd_aout);
- return (retval);
-}
diff --git a/usr.bin/truss/i386-linux.c b/usr.bin/truss/i386-linux.c
index 6bd9309..fa57af2 100644
--- a/usr.bin/truss/i386-linux.c
+++ b/usr.bin/truss/i386-linux.c
@@ -29,123 +29,36 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * Linux/i386-specific system call handling. Given how much of this code
- * is taken from the freebsd equivalent, I can probably put even more of
- * it in support routines that can be used by any personality support.
- */
+/* Linux/i386-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <machine/reg.h>
#include <machine/psl.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "linux_syscalls.h"
-static int nsyscalls = nitems(linux_syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct linux_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long args[5];
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct linux_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct linux_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct linux_syscall *fsc)
-{
- int i;
-
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in i386/i386/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-i386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+i386_linux_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct reg regs;
- struct linux_syscall *fsc;
- struct syscall *sc;
+ struct current_syscall *cs;
lwpid_t tid;
- int i, syscall_num;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
- }
-
- syscall_num = regs.r_eax;
-
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : linux_syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
+ return (-1);
}
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "linux_fork") == 0 ||
- strcmp(fsc->name, "linux_vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
/*
* Linux passes syscall arguments in registers, not
* on the stack. Fortunately, we've got access to the
@@ -153,60 +66,22 @@ i386_linux_syscall_entry(struct trussinfo *trussinfo, int nargs)
* number of arguments. And what does linux do for syscalls
* that have more than five arguments?
*/
-
- fsc->args[0] = regs.r_ebx;
- fsc->args[1] = regs.r_ecx;
- fsc->args[2] = regs.r_edx;
- fsc->args[3] = regs.r_esi;
- fsc->args[4] = regs.r_edi;
-
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
+ switch (narg) {
+ default:
+ cs->args[5] = regs.r_ebp; /* Unconfirmed */
+ case 5:
+ cs->args[4] = regs.r_edi;
+ case 4:
+ cs->args[3] = regs.r_esi;
+ case 3:
+ cs->args[2] = regs.r_edx;
+ case 2:
+ cs->args[1] = regs.r_ecx;
+ case 1:
+ cs->args[0] = regs.r_ebx;
}
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
- }
-
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
/*
@@ -224,82 +99,42 @@ static const int bsd_to_linux_errno[] = {
-6,
};
-long
-i386_linux_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+i386_linux_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
- struct linux_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
+ size_t i;
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.r_eax;
- errorp = !!(regs.r_eflags & PSL_C);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
+ retval[0] = regs.r_eax;
+ retval[1] = regs.r_edx;
+ *errorp = !!(regs.r_eflags & PSL_C);
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
+ if (*errorp) {
+ for (i = 0; i < nitems(bsd_to_linux_errno); i++) {
+ if (retval[0] == bsd_to_linux_errno[i]) {
+ retval[0] = i;
+ return (0);
}
}
- }
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
- if (errorp) {
- for (i = 0; (size_t)i < nitems(bsd_to_linux_errno); i++) {
- if (retval == bsd_to_linux_errno[i])
- break;
- }
+ /* XXX: How to handle unknown errors? */
}
+ return (0);
+}
- if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
-
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- errorp ? i : retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi i386_linux = {
+ "Linux ELF32",
+ linux_syscallnames,
+ nitems(linux_syscallnames),
+ i386_linux_fetch_args,
+ i386_linux_fetch_retval
+};
- return (retval);
-}
+PROCABI(i386_linux);
diff --git a/usr.bin/truss/main.c b/usr.bin/truss/main.c
index 84ae313..55fdeda 100644
--- a/usr.bin/truss/main.c
+++ b/usr.bin/truss/main.c
@@ -38,21 +38,12 @@ __FBSDID("$FreeBSD$");
* do a lot of the work :).
*/
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/sysctl.h>
-#include <sys/wait.h>
+#include <sys/ptrace.h>
#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
#include <signal.h>
-#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <time.h>
#include <unistd.h>
@@ -60,8 +51,6 @@ __FBSDID("$FreeBSD$");
#include "extern.h"
#include "syscall.h"
-#define MAXARGS 6
-
static void
usage(void)
{
@@ -71,82 +60,6 @@ usage(void)
exit(1);
}
-/*
- * WARNING! "FreeBSD a.out" must be first, or set_etype will not
- * work correctly.
- */
-static struct ex_types {
- const char *type;
- void (*enter_syscall)(struct trussinfo *, int);
- long (*exit_syscall)(struct trussinfo *, int);
-} ex_types[] = {
-#ifdef __arm__
- { "FreeBSD ELF32", arm_syscall_entry, arm_syscall_exit },
-#endif
-#ifdef __amd64__
- { "FreeBSD ELF64", amd64_syscall_entry, amd64_syscall_exit },
- { "FreeBSD ELF32", amd64_fbsd32_syscall_entry, amd64_fbsd32_syscall_exit },
- { "Linux ELF32", amd64_linux32_syscall_entry, amd64_linux32_syscall_exit },
-#endif
-#ifdef __i386__
- { "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit },
- { "FreeBSD ELF", i386_syscall_entry, i386_syscall_exit },
- { "FreeBSD ELF32", i386_syscall_entry, i386_syscall_exit },
- { "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit },
-#endif
-#ifdef __powerpc__
- { "FreeBSD ELF", powerpc_syscall_entry, powerpc_syscall_exit },
- { "FreeBSD ELF32", powerpc_syscall_entry, powerpc_syscall_exit },
-#ifdef __powerpc64__
- { "FreeBSD ELF64", powerpc64_syscall_entry, powerpc64_syscall_exit },
-#endif
-#endif
-#ifdef __sparc64__
- { "FreeBSD ELF64", sparc64_syscall_entry, sparc64_syscall_exit },
-#endif
-#ifdef __mips__
- { "FreeBSD ELF", mips_syscall_entry, mips_syscall_exit },
- { "FreeBSD ELF32", mips_syscall_entry, mips_syscall_exit },
- { "FreeBSD ELF64", mips_syscall_entry, mips_syscall_exit }, // XXX
-#endif
- { 0, 0, 0 },
-};
-
-/*
- * Set the execution type. This is called after every exec, and when
- * a process is first monitored.
- */
-
-static struct ex_types *
-set_etype(struct trussinfo *trussinfo)
-{
- struct ex_types *funcs;
- 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] = trussinfo->pid;
- error = sysctl(mib, 4, progt, &len, NULL, 0);
- if (error != 0)
- err(2, "can not get etype");
-
- for (funcs = ex_types; funcs->type; funcs++)
- if (strcmp(funcs->type, progt) == 0)
- break;
-
- if (funcs->type == NULL) {
- funcs = &ex_types[0];
- warn("execution type %s is not supported -- using %s",
- progt, funcs->type);
- }
- return (funcs);
-}
-
char *
strsig(int sig)
{
@@ -162,37 +75,32 @@ strsig(int sig)
int
main(int ac, char **av)
{
- struct timespec timediff;
struct sigaction sa;
- struct ex_types *funcs;
struct trussinfo *trussinfo;
char *fname;
- char *signame;
char **command;
- pid_t childpid;
- int c, initial_open, status;
+ pid_t pid;
+ int c;
fname = NULL;
- initial_open = 1;
/* Initialize the trussinfo struct */
trussinfo = (struct trussinfo *)calloc(1, sizeof(struct trussinfo));
if (trussinfo == NULL)
errx(1, "calloc() failed");
+ pid = 0;
trussinfo->outfile = stderr;
trussinfo->strsize = 32;
- trussinfo->pr_why = S_NONE;
trussinfo->curthread = NULL;
- SLIST_INIT(&trussinfo->threadlist);
+ LIST_INIT(&trussinfo->proclist);
while ((c = getopt(ac, av, "p:o:facedDs:S")) != -1) {
switch (c) {
case 'p': /* specified pid */
- trussinfo->pid = atoi(optarg);
+ pid = atoi(optarg);
/* make sure i don't trace me */
- if (trussinfo->pid == getpid()) {
- fprintf(stderr, "attempt to grab self.\n");
- exit(2);
+ if (pid == getpid()) {
+ errx(2, "attempt to grab self.");
}
break;
case 'f': /* Follow fork()'s */
@@ -228,8 +136,8 @@ main(int ac, char **av)
}
ac -= optind; av += optind;
- if ((trussinfo->pid == 0 && ac == 0) ||
- (trussinfo->pid != 0 && ac != 0))
+ if ((pid == 0 && ac == 0) ||
+ (pid != 0 && ac != 0))
usage();
if (fname != NULL) { /* Use output file */
@@ -247,10 +155,10 @@ main(int ac, char **av)
* exit. If, however, we are examining an already-running process,
* then we restore the event mask on these same signals.
*/
-
- if (trussinfo->pid == 0) { /* Start a command ourselves */
+ if (pid == 0) {
+ /* Start a command ourselves */
command = av;
- trussinfo->pid = setup_and_wait(command);
+ setup_and_wait(trussinfo, command);
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
@@ -261,119 +169,37 @@ main(int ac, char **av)
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
- start_tracing(trussinfo->pid);
+ start_tracing(trussinfo, pid);
}
-
/*
* At this point, if we started the process, it is stopped waiting to
* be woken up, either in exit() or in execve().
*/
+ if (LIST_FIRST(&trussinfo->proclist)->abi == NULL) {
+ /*
+ * If we are not able to handle this ABI, detach from the
+ * process and exit. If we just created a new process to
+ * run a command, kill the new process rather than letting
+ * it run untraced.
+ */
+ if (pid == 0)
+ kill(LIST_FIRST(&trussinfo->proclist)->pid, SIGKILL);
+ ptrace(PT_DETACH, LIST_FIRST(&trussinfo->proclist)->pid, NULL,
+ 0);
+ return (1);
+ }
+ ptrace(PT_SYSCALL, LIST_FIRST(&trussinfo->proclist)->pid, (caddr_t)1,
+ 0);
-START_TRACE:
- funcs = set_etype(trussinfo);
-
- initial_open = 0;
/*
* At this point, it's a simple loop, waiting for the process to
* stop, finding out why, printing out why, and then continuing it.
* All of the grunt work is done in the support routines.
*/
-
clock_gettime(CLOCK_REALTIME, &trussinfo->start_time);
- do {
- waitevent(trussinfo);
-
- switch (trussinfo->pr_why) {
- case S_SCE:
- funcs->enter_syscall(trussinfo, MAXARGS);
- clock_gettime(CLOCK_REALTIME,
- &trussinfo->curthread->before);
- break;
- case S_SCX:
- clock_gettime(CLOCK_REALTIME,
- &trussinfo->curthread->after);
-
- if (trussinfo->curthread->in_fork &&
- (trussinfo->flags & FOLLOWFORKS)) {
- trussinfo->curthread->in_fork = 0;
- childpid = funcs->exit_syscall(trussinfo,
- trussinfo->pr_data);
-
- /*
- * Fork a new copy of ourself to trace
- * the child of the original traced
- * process.
- */
- if (fork() == 0) {
- trussinfo->pid = childpid;
- start_tracing(trussinfo->pid);
- goto START_TRACE;
- }
- break;
- }
- funcs->exit_syscall(trussinfo, MAXARGS);
- break;
- case S_SIG:
- if (trussinfo->flags & NOSIGS)
- break;
- if (trussinfo->flags & FOLLOWFORKS)
- fprintf(trussinfo->outfile, "%5d: ",
- trussinfo->pid);
- if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
- timespecsubt(&trussinfo->curthread->after,
- &trussinfo->start_time, &timediff);
- fprintf(trussinfo->outfile, "%jd.%09ld ",
- (intmax_t)timediff.tv_sec,
- timediff.tv_nsec);
- }
- if (trussinfo->flags & RELATIVETIMESTAMPS) {
- timespecsubt(&trussinfo->curthread->after,
- &trussinfo->curthread->before, &timediff);
- fprintf(trussinfo->outfile, "%jd.%09ld ",
- (intmax_t)timediff.tv_sec,
- timediff.tv_nsec);
- }
- signame = strsig(trussinfo->pr_data);
- fprintf(trussinfo->outfile,
- "SIGNAL %u (%s)\n", trussinfo->pr_data,
- signame == NULL ? "?" : signame);
- break;
- case S_EXIT:
- if (trussinfo->flags & COUNTONLY)
- break;
- if (trussinfo->flags & FOLLOWFORKS)
- fprintf(trussinfo->outfile, "%5d: ",
- trussinfo->pid);
- if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
- timespecsubt(&trussinfo->curthread->after,
- &trussinfo->start_time, &timediff);
- fprintf(trussinfo->outfile, "%jd.%09ld ",
- (intmax_t)timediff.tv_sec,
- timediff.tv_nsec);
- }
- if (trussinfo->flags & RELATIVETIMESTAMPS) {
- timespecsubt(&trussinfo->curthread->after,
- &trussinfo->curthread->before, &timediff);
- fprintf(trussinfo->outfile, "%jd.%09ld ",
- (intmax_t)timediff.tv_sec,
- timediff.tv_nsec);
- }
- fprintf(trussinfo->outfile,
- "process exit, rval = %u\n", trussinfo->pr_data);
- break;
- default:
- break;
- }
- } while (trussinfo->pr_why != S_EXIT &&
- trussinfo->pr_why != S_DETACHED);
-
- if (trussinfo->flags & FOLLOWFORKS) {
- do {
- childpid = wait(&status);
- } while (childpid != -1);
- }
+ eventloop(trussinfo);
if (trussinfo->flags & COUNTONLY)
print_summary(trussinfo);
diff --git a/usr.bin/truss/mips-fbsd.c b/usr.bin/truss/mips-fbsd.c
index d45241a..d488b7e 100644
--- a/usr.bin/truss/mips-fbsd.c
+++ b/usr.bin/truss/mips-fbsd.c
@@ -29,318 +29,113 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * FreeBSD/sparc64-specific system call handling. This is probably the most
- * complex part of the entire truss program, although I've got lots of
- * it handled relatively cleanly now. The system call names are generated
- * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
- * names used for the various structures are confusing, I sadly admit.
- *
- * This file is almost nothing more than a slightly-edited i386-fbsd.c.
- */
+/* FreeBSD/mips-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/frame.h>
#include <machine/reg.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stddef.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "syscalls.h"
-static int nsyscalls = nitems(syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct freebsd_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long *args;
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct freebsd_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct freebsd_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct freebsd_syscall *fsc)
-{
- int i;
-
- free(fsc->args);
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in sparc64/sparc64/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-mips_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+mips_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct ptrace_io_desc iorequest;
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
+ struct current_syscall *cs;
lwpid_t tid;
- int i, syscall_num;
- int indir; /* indirect system call */
+ u_int i, reg;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
- }
-
- indir = 0;
- syscall_num = regs.r_regs[V0];
- if (syscall_num == SYS_syscall) {
- indir = 1;
- syscall_num = regs.r_regs[A0];
+ return (-1);
}
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
+ /*
+ * FreeBSD has two special kinds of system call redirections --
+ * SYS_syscall, and SYS___syscall. The former is the old syscall()
+ * routine, basically; the latter is for quad-aligned arguments.
+ *
+ * The system call argument count and code from ptrace() already
+ * account for these, but we need to skip over the first argument.
+ */
+ reg = A0;
+ switch (regs.r_regs[V0]) {
+ case SYS_syscall:
+ reg = A1;
+ break;
+ case SYS___syscall:
+#if defined(__mips_n32) || defined(__mips_n64)
+ reg = A1;
+#else
+ reg = A2;
+#endif
+ break;
}
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "fork") == 0 ||
- strcmp(fsc->name, "pdfork") == 0 ||
- strcmp(fsc->name, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
-#if 0 // XXX
- iorequest.piod_op = PIOD_READ_D;
- iorequest.piod_offs = (void *)parm_offset;
- iorequest.piod_addr = fsc->args;
- iorequest.piod_len = (1 + nargs) * sizeof(unsigned long);
- ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
- if (iorequest.piod_len == 0)
- return;
+#if defined(__mips_n32) || defined(__mips_n64)
+#define MAXREG A7
#else
- iorequest.piod_op = PIOD_READ_D;
+#define MAXREG A3
#endif
- switch (nargs) {
- default:
- /*
- * The OS doesn't seem to allow more than 10 words of
- * parameters (yay!). So we shouldn't be here.
- */
- warn("More than 10 words (%d) of arguments!\n", nargs);
- break;
- case 10:
- case 9:
- case 8:
- case 7:
- case 6:
- case 5:
- /*
- * If there are 7-10 words of arguments, they are placed
- * on the stack, as is normal for other processors.
- * The fall-through for all of these is deliberate!!!
- */
- // XXX BAD constant used here
+ for (i = 0; i < narg && reg <= MAXREG; i++, reg++)
+ cs->args[i] = regs.r_regs[reg];
+ if (narg > i) {
iorequest.piod_op = PIOD_READ_D;
- iorequest.piod_offs = (void *)(regs.r_regs[SP] +
- 4 * sizeof(uint32_t));
- iorequest.piod_addr = &fsc->args[4];
- iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]);
+ iorequest.piod_offs = (void *)((uintptr_t)regs.r_regs[SP] +
+ 4 * sizeof(cs->args[0]));
+ iorequest.piod_addr = &cs->args[i];
+ iorequest.piod_len = (narg - i) * sizeof(cs->args[0]);
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
if (iorequest.piod_len == 0)
- return;
- case 4: fsc->args[3] = regs.r_regs[A3];
- case 3: fsc->args[2] = regs.r_regs[A2];
- case 2: fsc->args[1] = regs.r_regs[A1];
- case 1: fsc->args[0] = regs.r_regs[A0];
- case 0: break;
- }
- if (indir) {
- memmove(&fsc->args[0], &fsc->args[1],
- (nargs - 1) * sizeof(fsc->args[0]));
+ return (-1);
}
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
- }
-
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
- }
-
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
-/*
- * And when the system call is done, we handle it here.
- * Currently, no attempt is made to ensure that the system calls
- * match -- this needs to be fixed (and is, in fact, why S_SCX includes
- * the system call number instead of, say, an error status).
- */
-
-long
-mips_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+mips_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
- fprintf(trussinfo->outfile, "\n");
+ fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.r_regs[V0];
- errorp = !!regs.r_regs[A3];
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
-
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
- }
- }
- }
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
+ /* XXX: Does not have special handling for __syscall(). */
+ retval[0] = regs.r_regs[V0];
+ retval[1] = regs.r_regs[V1];
+ *errorp = !!regs.r_regs[A3];
+ return (0);
+}
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi mips_fbsd = {
+#ifdef __mips_n64
+ "FreeBSD ELF64",
+#else
+ "FreeBSD ELF32",
+#endif
+ syscallnames,
+ nitems(syscallnames),
+ mips_fetch_args,
+ mips_fetch_retval
+};
- return (retval);
-}
+PROCABI(mips_fbsd);
diff --git a/usr.bin/truss/powerpc-fbsd.c b/usr.bin/truss/powerpc-fbsd.c
index cdbd133..4a11459 100644
--- a/usr.bin/truss/powerpc-fbsd.c
+++ b/usr.bin/truss/powerpc-fbsd.c
@@ -25,41 +25,20 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * FreeBSD/powerpc-specific system call handling. This is probably the most
- * complex part of the entire truss program, although I've got lots of
- * it handled relatively cleanly now. The system call names are generated
- * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
- * names used for the various structures are confusing, I sadly admit.
- *
- * This file is almost nothing more than a slightly-edited i386-fbsd.c.
- */
+/* FreeBSD/powerpc-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/frame.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#ifdef __powerpc64__ /* 32-bit compatibility */
#include "freebsd32_syscalls.h"
@@ -68,264 +47,104 @@ static const char rcsid[] =
#include "syscalls.h"
#endif
-static int nsyscalls = nitems(syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct freebsd_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long *args;
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct freebsd_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct freebsd_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct freebsd_syscall *fsc)
-{
- int i;
-
- free(fsc->args);
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in powerpc/powerpc/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-powerpc_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+powerpc_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct ptrace_io_desc iorequest;
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
- void *args;
+ struct current_syscall *cs;
lwpid_t tid;
- int i, regargs, syscall_num;
-
- /* Account for a 64-bit argument with corresponding alignment. */
- nargs += 2;
+ u_int i, reg;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
+ return (-1);
}
/*
- * FreeBSD has two special kinds of system call redirctions --
+ * FreeBSD has two special kinds of system call redirections --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basically; the latter is for quad-aligned arguments.
+ *
+ * The system call argument count and code from ptrace() already
+ * account for these, but we need to skip over the first argument.
*/
- regargs = NARGREG;
- syscall_num = regs.fixreg[0];
- args = &regs.fixreg[3];
- if (syscall_num == SYS_syscall) {
- args = &regs.fixreg[4];
- regargs -= 1;
- syscall_num = regs.fixreg[3];
- } else if (syscall_num == SYS___syscall) {
- args = &regs.fixreg[5];
- regargs -= 2;
- syscall_num = regs.fixreg[4];
+ reg = 0;
+ switch (regs.fixreg[0]) {
+ case SYS_syscall:
+ reg += 1;
+ break;
+ case SYS___syscall:
+ reg += 2;
+ break;
}
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
+ for (i = 0; i < narg && reg < NARGREG; i++, reg++) {
+#ifdef __powerpc64__
+ cs->args[i] = regs.fixreg[FIRSTARG + reg] & 0xffffffff;
+#else
+ cs->args[i] = regs.fixreg[FIRSTARG + reg];
+#endif
}
+ if (narg > i) {
+#ifdef __powerpc64__
+ uint32_t args32[narg - i];
+ u_int j;
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "fork") == 0 ||
- strcmp(fsc->name, "pdfork") == 0 ||
- strcmp(fsc->name, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
-
- if (nargs > regargs) {
- memmove(&fsc->args[0], args, regargs * sizeof(fsc->args[0]));
-
+#endif
iorequest.piod_op = PIOD_READ_D;
iorequest.piod_offs = (void *)(regs.fixreg[1] + 8);
- iorequest.piod_addr = &fsc->args[regargs];
- iorequest.piod_len = (nargs - regargs) * sizeof(fsc->args[0]);
+#ifdef __powerpc64__
+ iorequest.piod_addr = args32;
+ iorequest.piod_len = sizeof(args32);
+#else
+ iorequest.piod_addr = &cs->args[i];
+ iorequest.piod_len = (narg - i) * sizeof(cs->args[0]);
+#endif
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
if (iorequest.piod_len == 0)
- return;
- } else
- memmove(&fsc->args[0], args, nargs * sizeof(fsc->args[0]));
-
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
+ return (-1);
+#ifdef __powerpc64__
+ for (j = 0; j < narg - i; j++)
+ cs->args[i + j] = args32[j];
#endif
- fsc->nargs = nargs;
}
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
- }
-
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
-/*
- * And when the system call is done, we handle it here.
- * Currently, no attempt is made to ensure that the system calls
- * match -- this needs to be fixed (and is, in fact, why S_SCX includes
- * the system call number instead of, say, an error status).
- */
-
-long
-powerpc_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+powerpc_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
- fprintf(trussinfo->outfile, "\n");
+ fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.fixreg[3];
- errorp = !!(regs.cr & 0x10000000);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
-
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * On 32-bit big-endian, the low word of a 64-bit return is
- * in the greater address. Switch to this. XXX note that
- * print_syscall_ret can't handle 64-bit return values (llseek)
- */
- if (sc->ret_type == 2)
- retval = regs.fixreg[4];
-
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
- }
- }
- }
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
-
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
+ /* XXX: Does not have fixup for __syscall(). */
+#ifdef __powerpc64__
+ retval[0] = regs.fixreg[3] & 0xffffffff;
+ retval[1] = regs.fixreg[4] & 0xffffffff;
+#else
+ retval[0] = regs.fixreg[3];
+ retval[1] = regs.fixreg[4];
+#endif
+ *errorp = !!(regs.cr & 0x10000000);
+ return (0);
+}
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi powerpc_fbsd = {
+ "FreeBSD ELF32",
+ syscallnames,
+ nitems(syscallnames),
+ powerpc_fetch_args,
+ powerpc_fetch_retval
+};
- return (retval);
-}
+PROCABI(powerpc_fbsd);
diff --git a/usr.bin/truss/powerpc64-fbsd.c b/usr.bin/truss/powerpc64-fbsd.c
index 1f50429..5ca5c22 100644
--- a/usr.bin/truss/powerpc64-fbsd.c
+++ b/usr.bin/truss/powerpc64-fbsd.c
@@ -25,287 +25,94 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * FreeBSD/powerpc-specific system call handling. This is probably the most
- * complex part of the entire truss program, although I've got lots of
- * it handled relatively cleanly now. The system call names are generated
- * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
- * names used for the various structures are confusing, I sadly admit.
- *
- * This file is almost nothing more than a slightly-edited i386-fbsd.c.
- */
+/* FreeBSD/powerpc64-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.h>
#include <machine/frame.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "syscalls.h"
-static int nsyscalls = nitems(syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct freebsd_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long *args;
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct freebsd_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct freebsd_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct freebsd_syscall *fsc)
-{
- int i;
-
- free(fsc->args);
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in powerpc/powerpc/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-powerpc64_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+powerpc64_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct ptrace_io_desc iorequest;
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
- void *args;
+ struct current_syscall *cs;
lwpid_t tid;
- int i, regargs, syscall_num;
+ u_int i, reg;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
+ return (-1);
}
/*
- * FreeBSD has two special kinds of system call redirctions --
+ * FreeBSD has two special kinds of system call redirections --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basically; the latter is for quad-aligned arguments.
+ *
+ * The system call argument count and code from ptrace() already
+ * account for these, but we need to skip over the first argument.
*/
- regargs = NARGREG;
- syscall_num = regs.fixreg[0];
- args = &regs.fixreg[3];
- if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) {
- args = &regs.fixreg[4];
- regargs -= 1;
- syscall_num = regs.fixreg[3];
- }
-
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
+ reg = 0;
+ switch (regs.fixreg[0]) {
+ case SYS_syscall:
+ case SYS___syscall:
+ reg += 1;
+ break;
}
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "fork") == 0 ||
- strcmp(fsc->name, "pdfork") == 0 ||
- strcmp(fsc->name, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
-
- if (nargs > regargs) {
- memmove(&fsc->args[0], args, regargs * sizeof(fsc->args[0]));
-
+ for (i = 0; i < narg && reg < NARGREG; i++, reg++)
+ cs->args[i] = regs.fixreg[FIRSTARG + reg];
+ if (narg > i) {
iorequest.piod_op = PIOD_READ_D;
iorequest.piod_offs = (void *)(regs.fixreg[1] + 48);
- iorequest.piod_addr = &fsc->args[regargs];
- iorequest.piod_len = (nargs - regargs) * sizeof(fsc->args[0]);
+ iorequest.piod_addr = &cs->args[i];
+ iorequest.piod_len = (narg - i) * sizeof(cs->args[0]);
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
if (iorequest.piod_len == 0)
- return;
- } else
- memmove(&fsc->args[0], args, nargs * sizeof(fsc->args[0]));
-
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
- }
-
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
+ return (-1);
}
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
-/*
- * And when the system call is done, we handle it here.
- * Currently, no attempt is made to ensure that the system calls
- * match -- this needs to be fixed (and is, in fact, why S_SCX includes
- * the system call number instead of, say, an error status).
- */
-
-long
-powerpc64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+powerpc64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
- fprintf(trussinfo->outfile, "\n");
+ fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.fixreg[3];
- errorp = !!(regs.cr & 0x10000000);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
-
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
- }
- }
- }
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
-
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
+ retval[0] = regs.fixreg[3];
+ retval[1] = regs.fixreg[4];
+ *errorp = !!(regs.cr & 0x10000000);
+ return (0);
+}
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi powerpc64_fbsd = {
+ "FreeBSD ELF64",
+ syscallnames,
+ nitems(syscallnames),
+ powerpc64_fetch_args,
+ powerpc64_fetch_retval
+};
- return (retval);
-}
+PROCABI(powerpc64_fbsd);
diff --git a/usr.bin/truss/setup.c b/usr.bin/truss/setup.c
index d6e4300..4452f9f 100644
--- a/usr.bin/truss/setup.c
+++ b/usr.bin/truss/setup.c
@@ -37,37 +37,39 @@ __FBSDID("$FreeBSD$");
* I'm afraid.
*/
-#include <sys/param.h>
-#include <sys/types.h>
#include <sys/ptrace.h>
+#include <sys/sysctl.h>
#include <sys/wait.h>
+#include <assert.h>
#include <err.h>
#include <errno.h>
-#include <fcntl.h>
#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
-#include <machine/reg.h>
-
#include "truss.h"
+#include "syscall.h"
#include "extern.h"
+SET_DECLARE(procabi, struct procabi);
+
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
- * is fork(), set itself up to stop on exec or exit, and then exec
- * the given command. At that point, the child process stops, and
- * the parent can wake up and deal with it.
+ * is fork(), enable tracing in the child, and then exec the given
+ * command. At that point, the child process stops, and the parent
+ * can wake up and deal with it.
*/
-
-int
-setup_and_wait(char *command[])
+void
+setup_and_wait(struct trussinfo *info, char *command[])
{
pid_t pid;
@@ -84,17 +86,14 @@ setup_and_wait(char *command[])
if (waitpid(pid, NULL, 0) < 0)
err(1, "unexpect stop in waitpid");
- return (pid);
+ new_proc(info, pid);
}
/*
- * start_tracing picks up where setup_and_wait() dropped off -- namely,
- * it sets the event mask for the given process id. Called for both
- * monitoring an existing process and when we create our own.
+ * start_tracing is called to attach to an existing process.
*/
-
-int
-start_tracing(pid_t pid)
+void
+start_tracing(struct trussinfo *info, pid_t pid)
{
int ret, retry;
@@ -109,7 +108,7 @@ start_tracing(pid_t pid)
if (waitpid(pid, NULL, 0) < 0)
err(1, "Unexpect stop in waitpid");
- return (0);
+ new_proc(info, pid);
}
/*
@@ -118,7 +117,6 @@ start_tracing(pid_t pid)
* applies if truss was told to monitor an already-existing
* process.
*/
-
void
restore_proc(int signo __unused)
{
@@ -126,116 +124,485 @@ restore_proc(int signo __unused)
detaching = 1;
}
-static int
+static void
detach_proc(pid_t pid)
{
- int waitval;
/* stop the child so that we can detach */
kill(pid, SIGSTOP);
- if (waitpid(pid, &waitval, 0) < 0)
+ if (waitpid(pid, NULL, 0) < 0)
err(1, "Unexpected stop in waitpid");
if (ptrace(PT_DETACH, pid, (caddr_t)1, 0) < 0)
err(1, "Can not detach the process");
kill(pid, SIGCONT);
+}
+
+/*
+ * 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)
+ return (*pabi);
+ }
+ warnx("ABI %s for pid %ld is not supported", progt, (long)pid);
+ return (NULL);
+}
+
+static void
+new_proc(struct trussinfo *info, pid_t pid)
+{
+ struct procinfo *np;
+
+ /*
+ * If this happens it means there is a bug in truss. Unfortunately
+ * 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);
+ }
- return (waitval);
+ if (info->flags & FOLLOWFORKS)
+ if (ptrace(PT_FOLLOW_FORK, pid, NULL, 1) == -1)
+ err(1, "Unable to follow forks for pid %ld", (long)pid);
+ 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
+free_proc(struct procinfo *p)
+{
+ struct threadinfo *t, *t2;
+
+ SLIST_FOREACH_SAFE(t, &p->threadlist, entries, t2) {
+ free(t);
+ }
+ 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 lwpid.
- * If it is a new thread, create a threadinfo structure
+ * Change curthread member based on (pid, lwpid).
+ * If it is a new thread, create a threadinfo structure.
*/
static void
-find_thread(struct trussinfo *info, lwpid_t lwpid)
+find_thread(struct trussinfo *info, pid_t pid, lwpid_t lwpid)
{
- struct threadinfo *np;
+ struct procinfo *np;
+ struct threadinfo *nt;
- info->curthread = NULL;
- SLIST_FOREACH(np, &info->threadlist, entries) {
- if (np->tid == lwpid) {
- info->curthread = np;
+ np = find_proc(info, pid);
+ assert(np != NULL);
+
+ SLIST_FOREACH(nt, &np->threadlist, entries) {
+ if (nt->tid == lwpid) {
+ info->curthread = nt;
return;
}
}
- np = (struct threadinfo *)calloc(1, sizeof(struct threadinfo));
- if (np == NULL)
+ nt = calloc(1, sizeof(struct threadinfo));
+ if (nt == NULL)
err(1, "calloc() failed");
- np->tid = lwpid;
- SLIST_INSERT_HEAD(&info->threadlist, np, entries);
- info->curthread = np;
+ nt->proc = np;
+ nt->tid = lwpid;
+ SLIST_INSERT_HEAD(&np->threadlist, nt, entries);
+ info->curthread = nt;
}
/*
- * Start the traced process and wait until it stoped.
- * Fill trussinfo structure.
- * When this even returns, the traced process is in stop state.
+ * When a process exits, it no longer has any threads left. However,
+ * the main loop expects a valid curthread. In cases when a thread
+ * 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
-waitevent(struct trussinfo *info)
+static void
+find_exit_thread(struct trussinfo *info, pid_t pid)
{
- struct ptrace_lwpinfo lwpinfo;
- static int pending_signal = 0;
- int waitval;
+ struct procinfo *np;
+ struct threadinfo *nt;
+
+ np = find_proc(info, pid);
+ assert(np != NULL);
+
+ if (SLIST_EMPTY(&np->threadlist)) {
+ /*
+ * If an existing process exits right after we attach
+ * to it but before it posts any events, there won't
+ * 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);
+}
- ptrace(PT_SYSCALL, info->pid, (caddr_t)1, pending_signal);
- pending_signal = 0;
+static void
+alloc_syscall(struct threadinfo *t, struct ptrace_lwpinfo *pl)
+{
+ u_int i;
+
+ assert(t->in_syscall == 0);
+ assert(t->cs.number == 0);
+ assert(t->cs.name == NULL);
+ assert(t->cs.nargs == 0);
+ for (i = 0; i < nitems(t->cs.s_args); i++)
+ assert(t->cs.s_args[i] == NULL);
+ memset(t->cs.args, 0, sizeof(t->cs.args));
+ t->cs.number = pl->pl_syscall_code;
+ t->in_syscall = 1;
+}
-detach:
- if (detaching) {
- waitval = detach_proc(info->pid);
- info->pr_why = S_DETACHED;
- info->pr_data = WEXITSTATUS(waitval);
+static void
+free_syscall(struct threadinfo *t)
+{
+ u_int i;
+
+ for (i = 0; i < t->cs.nargs; i++)
+ free(t->cs.s_args[i]);
+ 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;
+ u_int i, narg;
+
+ t = info->curthread;
+ alloc_syscall(t, pl);
+ narg = MIN(pl->pl_syscall_narg, nitems(t->cs.args));
+ if (narg != 0 && t->proc->abi->fetch_args(info, narg) != 0) {
+ free_syscall(t);
return;
}
- if (waitpid(info->pid, &waitval, 0) == -1) {
- if (errno == EINTR)
- goto detach;
- err(1, "Unexpected stop in waitpid");
+ if (t->cs.number >= 0 && t->cs.number < t->proc->abi->nsyscalls)
+ t->cs.name = t->proc->abi->syscallnames[t->cs.number];
+ if (t->cs.name == NULL)
+ 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;
+ assert(sc->nargs <= nitems(t->cs.s_args));
+ } else {
+#if DEBUG
+ fprintf(stderr, "unknown syscall %s -- setting "
+ "args to %d\n", t->cs.name, t->cs.nargs);
+#endif
+ t->cs.nargs = narg;
}
- if (WIFCONTINUED(waitval)) {
- info->pr_why = S_NONE;
- return;
+ 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
}
- if (WIFEXITED(waitval)) {
- info->pr_why = S_EXIT;
- info->pr_data = WEXITSTATUS(waitval);
+
+ 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];
+ u_int i;
+ int errorp;
+
+ t = info->curthread;
+ if (!t->in_syscall)
+ return;
+
+ clock_gettime(CLOCK_REALTIME, &t->after);
+ p = t->proc;
+ if (p->abi->fetch_retval(info, retval, &errorp) < 0) {
+ free_syscall(t);
return;
}
- if (WIFSTOPPED(waitval)) {
- ptrace(PT_LWPINFO, info->pid, (caddr_t)&lwpinfo,
- sizeof(lwpinfo));
- find_thread(info, lwpinfo.pl_lwpid);
- switch (WSTOPSIG(waitval)) {
- case SIGTRAP:
- if (lwpinfo.pl_flags & PL_FLAG_SCE) {
- info->pr_why = S_SCE;
- info->curthread->in_syscall = 1;
- break;
- } else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
- info->pr_why = S_SCX;
- info->curthread->in_syscall = 0;
- break;
- } else {
- errx(1,
+
+ sc = t->cs.sc;
+ 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) {
+ if (ptrace(PT_DETACH, p->pid, (caddr_t)1, 0) < 0)
+ err(1, "Can not detach the process");
+ free_proc(p);
+ }
+ }
+}
+
+static void
+report_exit(struct trussinfo *info, siginfo_t *si)
+{
+ struct timespec timediff;
+
+ 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)" : "");
+}
+
+static void
+report_new_child(struct trussinfo *info, pid_t pid)
+{
+ struct timespec timediff;
+
+ clock_gettime(CLOCK_REALTIME, &info->curthread->after);
+ assert(info->flags & FOLLOWFORKS);
+ fprintf(info->outfile, "%5d: ", 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 (info->flags & RELATIVETIMESTAMPS) {
+ timediff.tv_sec = 0;
+ timediff.tv_nsec = 0;
+ fprintf(info->outfile, "%jd.%09ld ", (intmax_t)timediff.tv_sec,
+ timediff.tv_nsec);
+ }
+ fprintf(info->outfile, "<new process>\n");
+}
+
+static void
+report_signal(struct trussinfo *info, siginfo_t *si)
+{
+ struct timespec timediff;
+ char *signame;
+
+ 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 (info->flags & RELATIVETIMESTAMPS) {
+ timespecsubt(&info->curthread->after, &info->curthread->before,
+ &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);
+}
+
+/*
+ * 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;
+ siginfo_t si;
+ int pending_signal;
+
+ while (!LIST_EMPTY(&info->proclist)) {
+ if (detaching) {
+ detach_all_procs(info);
+ return;
+ }
+
+ if (waitid(P_ALL, 0, &si, WTRAPPED | WEXITED) == -1) {
+ if (errno == EINTR)
+ continue;
+ err(1, "Unexpected error from waitid");
+ }
+
+ assert(si.si_signo == SIGCHLD);
+
+ 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)
+ report_exit(info, &si);
+ free_proc(info->curthread->proc);
+ info->curthread = NULL;
+ break;
+ case CLD_TRAPPED:
+ if (ptrace(PT_LWPINFO, si.si_pid, (caddr_t)&pl,
+ sizeof(pl)) == -1)
+ err(1, "ptrace(PT_LWPINFO)");
+
+ 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,
"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) {
+ if ((info->flags & COUNTONLY) == 0)
+ report_new_child(info, si.si_pid);
+ pending_signal = 0;
+ } else {
+ if ((info->flags & NOSIGS) == 0)
+ report_signal(info, &si);
+ pending_signal = si.si_status;
}
- default:
- info->pr_why = S_SIG;
- info->pr_data = WSTOPSIG(waitval);
- pending_signal = info->pr_data;
+ 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;
}
}
- if (WIFSIGNALED(waitval)) {
- info->pr_why = S_EXIT;
- info->pr_data = 0;
- return;
- }
}
diff --git a/usr.bin/truss/sparc64-fbsd.c b/usr.bin/truss/sparc64-fbsd.c
index 4e64c70..c8c1e8c 100644
--- a/usr.bin/truss/sparc64-fbsd.c
+++ b/usr.bin/truss/sparc64-fbsd.c
@@ -29,22 +29,11 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * FreeBSD/sparc64-specific system call handling. This is probably the most
- * complex part of the entire truss program, although I've got lots of
- * it handled relatively cleanly now. The system call names are generated
- * automatically, thanks to /usr/src/sys/kern/syscalls.master. The
- * names used for the various structures are confusing, I sadly admit.
- *
- * This file is almost nothing more than a slightly-edited i386-fbsd.c.
- */
+/* FreeBSD/sparc64-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
@@ -52,289 +41,85 @@ static const char rcsid[] =
#include <machine/reg.h>
#include <machine/tstate.h>
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
#include <stddef.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
#include "truss.h"
-#include "syscall.h"
-#include "extern.h"
#include "syscalls.h"
-static int nsyscalls = nitems(syscallnames);
-
-/*
- * This is what this particular file uses to keep track of a system call.
- * It is probably not quite sufficient -- I can probably use the same
- * structure for the various syscall personalities, and I also probably
- * need to nest system calls (for signal handlers).
- *
- * 'struct syscall' describes the system call; it may be NULL, however,
- * if we don't know about this particular system call yet.
- */
-struct freebsd_syscall {
- struct syscall *sc;
- const char *name;
- int number;
- unsigned long *args;
- int nargs; /* number of arguments -- *not* number of words! */
- char **s_args; /* the printable arguments */
-};
-
-static struct freebsd_syscall *
-alloc_fsc(void)
-{
-
- return (malloc(sizeof(struct freebsd_syscall)));
-}
-
-/* Clear up and free parts of the fsc structure. */
-static void
-free_fsc(struct freebsd_syscall *fsc)
-{
- int i;
-
- free(fsc->args);
- if (fsc->s_args) {
- for (i = 0; i < fsc->nargs; i++)
- free(fsc->s_args[i]);
- free(fsc->s_args);
- }
- free(fsc);
-}
-
-/*
- * Called when a process has entered a system call. nargs is the
- * number of words, not number of arguments (a necessary distinction
- * in some cases). Note that if the STOPEVENT() code in sparc64/sparc64/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-sparc64_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+sparc64_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct ptrace_io_desc iorequest;
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
+ struct current_syscall *cs;
lwpid_t tid;
- int i, syscall_num;
- int indir; /* indirect system call */
+ u_int i, reg;
tid = trussinfo->curthread->tid;
-
+ cs = &trussinfo->curthread->cs;
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
- return;
+ return (-1);
}
/*
- * FreeBSD has two special kinds of system call redirctions --
+ * FreeBSD has two special kinds of system call redirections --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basically; the latter is for quad-aligned arguments.
+ *
+ * The system call argument count and code from ptrace() already
+ * account for these, but we need to skip over the first argument.
*/
- indir = 0;
- syscall_num = regs.r_global[1];
- if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) {
- indir = 1;
- syscall_num = regs.r_out[0];
- }
-
- fsc = alloc_fsc();
- if (fsc == NULL)
- return;
- fsc->number = syscall_num;
- fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ?
- NULL : syscallnames[syscall_num];
- if (!fsc->name) {
- fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n",
- syscall_num);
+ reg = 0;
+ switch (regs.r_global[1]) {
+ case SYS_syscall:
+ case SYS___syscall:
+ reg = 1;
+ break;
}
- if (fsc->name && (trussinfo->flags & FOLLOWFORKS) &&
- (strcmp(fsc->name, "fork") == 0 ||
- strcmp(fsc->name, "pdfork") == 0 ||
- strcmp(fsc->name, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
- switch (nargs) {
- default:
- /*
- * The OS doesn't seem to allow more than 10 words of
- * parameters (yay!). So we shouldn't be here.
- */
- warn("More than 10 words (%d) of arguments!\n", nargs);
- break;
- case 10:
- case 9:
- case 8:
- case 7:
- /*
- * If there are 7-10 words of arguments, they are placed
- * on the stack, as is normal for other processors.
- * The fall-through for all of these is deliberate!!!
- */
+ for (i = 0; i < narg && reg < 6; i++, reg++)
+ cs->args[i] = regs.r_out[reg];
+ if (narg > i) {
iorequest.piod_op = PIOD_READ_D;
iorequest.piod_offs = (void *)(regs.r_out[6] + SPOFF +
offsetof(struct frame, fr_pad[6]));
- iorequest.piod_addr = &fsc->args[6];
- iorequest.piod_len = (nargs - 6) * sizeof(fsc->args[0]);
+ iorequest.piod_addr = &cs->args[i];
+ iorequest.piod_len = (narg - i) * sizeof(cs->args[0]);
ptrace(PT_IO, tid, (caddr_t)&iorequest, 0);
if (iorequest.piod_len == 0)
- return;
- case 6: fsc->args[5] = regs.r_out[5];
- case 5: fsc->args[4] = regs.r_out[4];
- case 4: fsc->args[3] = regs.r_out[3];
- case 3: fsc->args[2] = regs.r_out[2];
- case 2: fsc->args[1] = regs.r_out[1];
- case 1: fsc->args[0] = regs.r_out[0];
- case 0:
- break;
+ return (-1);
}
- if (indir)
- memmove(&fsc->args[0], &fsc->args[1], (nargs - 1) *
- sizeof(fsc->args[0]));
-
- sc = get_syscall(fsc->name);
- if (sc)
- fsc->nargs = sc->nargs;
- else {
-#if DEBUG
- fprintf(trussinfo->outfile, "unknown syscall %s -- setting "
- "args to %d\n", fsc->name, nargs);
-#endif
- fsc->nargs = nargs;
- }
-
- fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *));
- fsc->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 (fsc->name) {
-#if DEBUG
- fprintf(stderr, "syscall %s(", fsc->name);
-#endif
- for (i = 0; i < fsc->nargs; i++) {
-#if DEBUG
- fprintf(stderr, "0x%x%s", sc ?
- fsc->args[sc->args[i].offset] : fsc->args[i],
- i < (fsc->nargs - 1) ? "," : "");
-#endif
- if (sc && !(sc->args[i].type & OUT)) {
- fsc->s_args[i] = print_arg(&sc->args[i],
- fsc->args, 0, trussinfo);
- }
- }
-#if DEBUG
- fprintf(stderr, ")\n");
-#endif
- }
-
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
-/*
- * And when the system call is done, we handle it here.
- * Currently, no attempt is made to ensure that the system calls
- * match -- this needs to be fixed (and is, in fact, why S_SCX includes
- * the system call number instead of, say, an error status).
- */
-
-long
-sparc64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+sparc64_fetch_retval(struct trussinfo *trussinfo, long *retval, int *errorp)
{
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
lwpid_t tid;
- long retval;
- int errorp, i;
-
- if (trussinfo->curthread->fsc == NULL)
- return (-1);
tid = trussinfo->curthread->tid;
-
if (ptrace(PT_GETREGS, tid, (caddr_t)&regs, 0) < 0) {
- fprintf(trussinfo->outfile, "\n");
+ fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n");
return (-1);
}
- retval = regs.r_out[0];
- errorp = !!(regs.r_tstate & TSTATE_XCC_C);
-
- /*
- * This code, while simpler than the initial versions I used, could
- * stand some significant cleaning.
- */
-
- fsc = trussinfo->curthread->fsc;
- sc = fsc->sc;
- if (!sc) {
- for (i = 0; i < fsc->nargs; i++)
- asprintf(&fsc->s_args[i], "0x%lx", fsc->args[i]);
- } else {
- /*
- * Here, we only look for arguments that have OUT masked in --
- * otherwise, they were handled in the syscall_entry function.
- */
- 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",
- fsc->args[sc->args[i].offset]);
- } else {
- temp = print_arg(&sc->args[i],
- fsc->args, retval, trussinfo);
- }
- fsc->s_args[i] = temp;
- }
- }
- }
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0))
- trussinfo->curthread->in_syscall = 1;
-
- /*
- * It would probably be a good idea to merge the error handling,
- * but that complicates things considerably.
- */
+ retval[0] = regs.r_out[0];
+ retval[1] = regs.r_out[1];
+ *errorp = !!(regs.r_tstate & TSTATE_XCC_C);
+ return (0);
+}
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi sparc64_fbsd = {
+ "FreeBSD ELF64",
+ syscallnames,
+ nitems(syscallnames),
+ sparc64_fetch_args,
+ sparc64_fetch_retval
+};
- return (retval);
-}
+PROCABI(sparc64_fbsd);
diff --git a/usr.bin/truss/syscall.h b/usr.bin/truss/syscall.h
index f1f57d4..63b2299 100644
--- a/usr.bin/truss/syscall.h
+++ b/usr.bin/truss/syscall.h
@@ -42,7 +42,7 @@ enum Argtype { None = 1, Hex, Octal, Int, LongHex, Name, Ptr, Stat, Ioctl, Quad,
Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2,
Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype, Procctl,
LinuxSockArgs, Umtxop, Atfd, Atflags, Timespec2, Accessmode, Long,
- Sysarch, ExecArgs, ExecEnv };
+ Sysarch, ExecArgs, ExecEnv, PipeFds };
#define ARG_MASK 0xff
#define OUT 0x100
@@ -55,8 +55,8 @@ struct syscall_args {
struct syscall {
const char *name;
- int ret_type; /* 0, 1, or 2 return values */
- int nargs; /* actual number of meaningful arguments */
+ u_int ret_type; /* 0, 1, or 2 return values */
+ u_int nargs; /* actual number of meaningful arguments */
/* Hopefully, no syscalls with > 10 args */
struct syscall_args args[10];
struct timespec time; /* Time spent for this call */
@@ -65,7 +65,7 @@ struct syscall {
};
struct syscall *get_syscall(const char*);
-char *print_arg(struct syscall_args *, unsigned long*, long, struct trussinfo *);
+char *print_arg(struct syscall_args *, unsigned long*, long *, struct trussinfo *);
/*
* Linux Socket defines
@@ -109,5 +109,5 @@ struct linux_socketcall_args {
void print_syscall(struct trussinfo *, const char *, int, char **);
void print_syscall_ret(struct trussinfo *, const char *, int, char **, int,
- long, struct syscall *);
+ long *, struct syscall *);
void print_summary(struct trussinfo *trussinfo);
diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c
index 638ce5a..f46a2e1 100644
--- a/usr.bin/truss/syscalls.c
+++ b/usr.bin/truss/syscalls.c
@@ -29,10 +29,8 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
/*
* This file has routines used to print out system calls and their
@@ -40,23 +38,20 @@ static const char rcsid[] =
*/
#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/ioccom.h>
#include <sys/mman.h>
#include <sys/procctl.h>
#include <sys/ptrace.h>
+#include <sys/resource.h>
#include <sys/socket.h>
-#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/umtx.h>
#include <sys/un.h>
#include <sys/wait.h>
+#include <machine/sysarch.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <sys/ioccom.h>
-#include <machine/atomic.h>
-#include <errno.h>
-#include <sys/umtx.h>
-#include <sys/event.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-#include <machine/sysarch.h>
#include <ctype.h>
#include <err.h>
@@ -95,26 +90,16 @@ static const char rcsid[] =
static struct syscall syscalls[] = {
{ .name = "fcntl", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } },
- { .name = "fork", .ret_type = 1, .nargs = 0 },
- { .name = "vfork", .ret_type = 1, .nargs = 0 },
{ .name = "rfork", .ret_type = 1, .nargs = 1,
.args = { { Rforkflags, 0 } } },
- { .name = "getegid", .ret_type = 1, .nargs = 0 },
- { .name = "geteuid", .ret_type = 1, .nargs = 0 },
{ .name = "linux_readlink", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Name | OUT, 1 }, { Int, 2 } } },
{ .name = "linux_socketcall", .ret_type = 1, .nargs = 2,
.args = { { Int, 0 }, { LinuxSockArgs, 1 } } },
- { .name = "getgid", .ret_type = 1, .nargs = 0 },
- { .name = "getpid", .ret_type = 1, .nargs = 0 },
{ .name = "getpgid", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
- { .name = "getpgrp", .ret_type = 1, .nargs = 0 },
- { .name = "getppid", .ret_type = 1, .nargs = 0 },
{ .name = "getsid", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
- { .name = "getuid", .ret_type = 1, .nargs = 0 },
- { .name = "issetugid", .ret_type = 1, .nargs = 0 },
{ .name = "readlink", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } },
{ .name = "readlinkat", .ret_type = 1, .nargs = 4,
@@ -125,7 +110,7 @@ static struct syscall syscalls[] = {
{ Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } },
{ .name = "linux_lseek", .ret_type = 2, .nargs = 3,
.args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } },
- { .name = "mmap", .ret_type = 2, .nargs = 6,
+ { .name = "mmap", .ret_type = 1, .nargs = 6,
.args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 },
{ Int, 4 }, { Quad, 5 + QUAD_ALIGN } } },
{ .name = "linux_mkdir", .ret_type = 1, .nargs = 2,
@@ -145,49 +130,49 @@ static struct syscall syscalls[] = {
.args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
{ .name = "close", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
- { .name = "link", .ret_type = 0, .nargs = 2,
+ { .name = "link", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Name, 1 } } },
- { .name = "linkat", .ret_type = 0, .nargs = 5,
+ { .name = "linkat", .ret_type = 1, .nargs = 5,
.args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 },
{ Atflags, 4 } } },
- { .name = "unlink", .ret_type = 0, .nargs = 1,
+ { .name = "unlink", .ret_type = 1, .nargs = 1,
.args = { { Name, 0 } } },
- { .name = "unlinkat", .ret_type = 0, .nargs = 3,
+ { .name = "unlinkat", .ret_type = 1, .nargs = 3,
.args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } },
- { .name = "chdir", .ret_type = 0, .nargs = 1,
+ { .name = "chdir", .ret_type = 1, .nargs = 1,
.args = { { Name, 0 } } },
- { .name = "chroot", .ret_type = 0, .nargs = 1,
+ { .name = "chroot", .ret_type = 1, .nargs = 1,
.args = { { Name, 0 } } },
- { .name = "mkfifo", .ret_type = 0, .nargs = 2,
+ { .name = "mkfifo", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Octal, 1 } } },
- { .name = "mkfifoat", .ret_type = 0, .nargs = 3,
+ { .name = "mkfifoat", .ret_type = 1, .nargs = 3,
.args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
- { .name = "mknod", .ret_type = 0, .nargs = 3,
+ { .name = "mknod", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } },
- { .name = "mknodat", .ret_type = 0, .nargs = 4,
+ { .name = "mknodat", .ret_type = 1, .nargs = 4,
.args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } },
- { .name = "chmod", .ret_type = 0, .nargs = 2,
+ { .name = "chmod", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Octal, 1 } } },
- { .name = "fchmod", .ret_type = 0, .nargs = 2,
+ { .name = "fchmod", .ret_type = 1, .nargs = 2,
.args = { { Int, 0 }, { Octal, 1 } } },
- { .name = "lchmod", .ret_type = 0, .nargs = 2,
+ { .name = "lchmod", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Octal, 1 } } },
- { .name = "fchmodat", .ret_type = 0, .nargs = 4,
+ { .name = "fchmodat", .ret_type = 1, .nargs = 4,
.args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } },
- { .name = "chown", .ret_type = 0, .nargs = 3,
+ { .name = "chown", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
- { .name = "fchown", .ret_type = 0, .nargs = 3,
+ { .name = "fchown", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } },
- { .name = "lchown", .ret_type = 0, .nargs = 3,
+ { .name = "lchown", .ret_type = 1, .nargs = 3,
.args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
- { .name = "fchownat", .ret_type = 0, .nargs = 5,
+ { .name = "fchownat", .ret_type = 1, .nargs = 5,
.args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 },
{ Atflags, 4 } } },
{ .name = "linux_stat64", .ret_type = 1, .nargs = 3,
.args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 } } },
- { .name = "mount", .ret_type = 0, .nargs = 4,
+ { .name = "mount", .ret_type = 1, .nargs = 4,
.args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } },
- { .name = "umount", .ret_type = 0, .nargs = 2,
+ { .name = "umount", .ret_type = 1, .nargs = 2,
.args = { { Name, 0 }, { Int, 2 } } },
{ .name = "fstat", .ret_type = 1, .nargs = 2,
.args = { { Int, 0 }, { Stat | OUT, 1 } } },
@@ -250,19 +235,19 @@ static struct syscall syscalls[] = {
{ .name = "linux_execve", .ret_type = 1, .nargs = 3,
.args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
{ ExecEnv | IN, 2 } } },
- { .name = "kldload", .ret_type = 0, .nargs = 1,
+ { .name = "kldload", .ret_type = 1, .nargs = 1,
.args = { { Name | IN, 0 } } },
- { .name = "kldunload", .ret_type = 0, .nargs = 1,
+ { .name = "kldunload", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
- { .name = "kldfind", .ret_type = 0, .nargs = 1,
+ { .name = "kldfind", .ret_type = 1, .nargs = 1,
.args = { { Name | IN, 0 } } },
- { .name = "kldnext", .ret_type = 0, .nargs = 1,
+ { .name = "kldnext", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
- { .name = "kldstat", .ret_type = 0, .nargs = 2,
+ { .name = "kldstat", .ret_type = 1, .nargs = 2,
.args = { { Int, 0 }, { Ptr, 1 } } },
- { .name = "kldfirstmod", .ret_type = 0, .nargs = 1,
+ { .name = "kldfirstmod", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
- { .name = "nanosleep", .ret_type = 0, .nargs = 1,
+ { .name = "nanosleep", .ret_type = 1, .nargs = 1,
.args = { { Timespec, 0 } } },
{ .name = "select", .ret_type = 1, .nargs = 5,
.args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 },
@@ -279,18 +264,18 @@ static struct syscall syscalls[] = {
.args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } },
{ .name = "kse_release", .ret_type = 0, .nargs = 1,
.args = { { Timespec, 0 } } },
- { .name = "kevent", .ret_type = 0, .nargs = 6,
+ { .name = "kevent", .ret_type = 1, .nargs = 6,
.args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 },
{ Int, 4 }, { Timespec, 5 } } },
- { .name = "sigpending", .ret_type = 0, .nargs = 1,
+ { .name = "sigpending", .ret_type = 1, .nargs = 1,
.args = { { Sigset | OUT, 0 } } },
- { .name = "sigprocmask", .ret_type = 0, .nargs = 3,
+ { .name = "sigprocmask", .ret_type = 1, .nargs = 3,
.args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
- { .name = "sigqueue", .ret_type = 0, .nargs = 3,
+ { .name = "sigqueue", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } },
- { .name = "sigreturn", .ret_type = 0, .nargs = 1,
+ { .name = "sigreturn", .ret_type = 1, .nargs = 1,
.args = { { Ptr, 0 } } },
- { .name = "sigsuspend", .ret_type = 0, .nargs = 1,
+ { .name = "sigsuspend", .ret_type = 1, .nargs = 1,
.args = { { Sigset | IN, 0 } } },
{ .name = "sigtimedwait", .ret_type = 1, .nargs = 3,
.args = { { Sigset | IN, 0 }, { Ptr, 1 }, { Timespec | IN, 2 } } },
@@ -332,7 +317,7 @@ static struct syscall syscalls[] = {
{ .name = "pathconf", .ret_type = 1, .nargs = 2,
.args = { { Name | IN, 0 }, { Pathconf, 1 } } },
{ .name = "pipe", .ret_type = 1, .nargs = 1,
- .args = { { Ptr, 0 } } },
+ .args = { { PipeFds | OUT, 0 } } },
{ .name = "pipe2", .ret_type = 1, .nargs = 2,
.args = { { Ptr, 0 }, { Open, 1 } } },
{ .name = "truncate", .ret_type = 1, .nargs = 3,
@@ -368,9 +353,9 @@ static struct syscall syscalls[] = {
{ .name = "_umtx_op", .ret_type = 1, .nargs = 5,
.args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 },
{ Ptr, 4 } } },
- { .name = "thr_kill", .ret_type = 0, .nargs = 2,
+ { .name = "thr_kill", .ret_type = 1, .nargs = 2,
.args = { { Long, 0 }, { Signal, 1 } } },
- { .name = "thr_self", .ret_type = 0, .nargs = 1,
+ { .name = "thr_self", .ret_type = 1, .nargs = 1,
.args = { { Ptr, 0 } } },
{ .name = 0 },
};
@@ -836,7 +821,7 @@ print_kevent(FILE *fp, struct kevent *ke, int input)
* an array of all of the system call arguments.
*/
char *
-print_arg(struct syscall_args *sc, unsigned long *args, long retval,
+print_arg(struct syscall_args *sc, unsigned long *args, long *retval,
struct trussinfo *trussinfo)
{
FILE *fp;
@@ -845,7 +830,7 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
pid_t pid;
fp = open_memstream(&tmp, &tmplen);
- pid = trussinfo->pid;
+ pid = trussinfo->curthread->proc->pid;
switch (sc->type & ARG_MASK) {
case Hex:
fprintf(fp, "0x%x", (int)args[sc->offset]);
@@ -884,7 +869,7 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
int truncated = 0;
if (sc->type & OUT)
- len = retval;
+ len = retval[0];
else
len = args[sc->offset + 1];
@@ -924,7 +909,7 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
} u;
char *string;
size_t len;
- int first, i;
+ u_int first, i;
/*
* Only parse argv[] and environment arrays from exec calls
@@ -987,7 +972,13 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
case Quad: {
unsigned long long ll;
- ll = *(unsigned long long *)(args + sc->offset);
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+ ll = (unsigned long long)args[sc->offset + 1] << 32 |
+ args[sc->offset];
+#else
+ ll = (unsigned long long)args[sc->offset] << 32 |
+ args[sc->offset + 1];
+#endif
fprintf(fp, "0x%llx", ll);
break;
}
@@ -998,9 +989,9 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
case Readlinkres: {
char *tmp2;
- if (retval == -1)
+ if (retval[0] == -1)
break;
- tmp2 = get_string(pid, (void*)args[sc->offset], retval);
+ tmp2 = get_string(pid, (void*)args[sc->offset], retval[0]);
fprintf(fp, "\"%s\"", tmp2);
free(tmp2);
break;
@@ -1397,8 +1388,8 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
if (sc->offset == 1)
numevents = args[sc->offset+1];
- else if (sc->offset == 3 && retval != -1)
- numevents = retval;
+ else if (sc->offset == 3 && retval[0] != -1)
+ numevents = retval[0];
if (numevents >= 0) {
bytes = sizeof(struct kevent) * numevents;
@@ -1517,6 +1508,21 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
case Sysarch:
fputs(xlookup(sysarch_ops, args[sc->offset]), fp);
break;
+ case PipeFds:
+ /*
+ * The pipe() system call in the kernel returns its
+ * two file descriptors via return values. However,
+ * the interface exposed by libc is that pipe()
+ * accepts a pointer to an array of descriptors.
+ * Format the output to match the libc API by printing
+ * the returned file descriptors as a fake argument.
+ *
+ * Overwrite the first retval to signal a successful
+ * return as well.
+ */
+ fprintf(fp, "{ %ld, %ld }", retval[0], retval[1]);
+ retval[0] = 0;
+ break;
default:
errx(1, "Invalid argument type %d\n", sc->type & ARG_MASK);
}
@@ -1538,7 +1544,8 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
len = 0;
if (trussinfo->flags & FOLLOWFORKS)
- len += fprintf(trussinfo->outfile, "%5d: ", trussinfo->pid);
+ len += fprintf(trussinfo->outfile, "%5d: ",
+ trussinfo->curthread->proc->pid);
if (name != NULL && (strcmp(name, "execve") == 0 ||
strcmp(name, "exit") == 0)) {
@@ -1577,7 +1584,7 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
void
print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs,
- char **s_args, int errorp, long retval, struct syscall *sc)
+ char **s_args, int errorp, long *retval, struct syscall *sc)
{
struct timespec timediff;
@@ -1597,17 +1604,24 @@ print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs,
print_syscall(trussinfo, name, nargs, s_args);
fflush(trussinfo->outfile);
if (errorp)
- fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval,
- strerror(retval));
- else {
- /*
- * Because pipe(2) has a special assembly glue to provide the
- * libc API, we have to adjust retval.
- */
- if (name != NULL && strcmp(name, "pipe") == 0)
- retval = 0;
- fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval);
+ fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval[0],
+ strerror(retval[0]));
+#ifndef __LP64__
+ else if (sc != NULL && sc->ret_type == 2) {
+ off_t off;
+
+#if _BYTE_ORDER == _LITTLE_ENDIAN
+ off = (off_t)retval[1] << 32 | retval[0];
+#else
+ off = (off_t)retval[0] << 32 | retval[1];
+#endif
+ fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off,
+ (intmax_t)off);
}
+#endif
+ else
+ fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval[0],
+ retval[0]);
}
void
diff --git a/usr.bin/truss/truss.h b/usr.bin/truss/truss.h
index dcc86a5..0f58bd8 100644
--- a/usr.bin/truss/truss.h
+++ b/usr.bin/truss/truss.h
@@ -25,6 +25,7 @@
* $FreeBSD$
*/
+#include <sys/linker_set.h>
#include <sys/queue.h>
#define FOLLOWFORKS 0x00000001
@@ -35,23 +36,63 @@
#define EXECVEENVS 0x00000020
#define COUNTONLY 0x00000040
+struct procinfo;
+struct trussinfo;
+
+struct procabi {
+ const char *type;
+ const char **syscallnames;
+ int nsyscalls;
+ int (*fetch_args)(struct trussinfo *, u_int);
+ int (*fetch_retval)(struct trussinfo *, long *, int *);
+};
+
+#define PROCABI(abi) DATA_SET(procabi, abi)
+
+/*
+ * This is confusingly named. It holds per-thread state about the
+ * currently executing system call. syscalls.h defines a struct
+ * syscall that holds metadata used to format system call arguments.
+ *
+ * NB: args[] stores the raw argument values (e.g. from registers)
+ * passed to the system call. s_args[] stores a string representation
+ * of a system call's arguments. These do not necessarily map one to
+ * one. A system call description may omit individual arguments
+ * (padding) or combine adjacent arguments (e.g. when passing an off_t
+ * argument on a 32-bit system). The nargs member contains the count
+ * of valid pointers in s_args[], not args[].
+ */
+struct current_syscall {
+ struct syscall *sc;
+ const char *name;
+ int number;
+ unsigned long args[10];
+ unsigned int nargs;
+ char *s_args[10]; /* the printable arguments */
+};
+
struct threadinfo
{
SLIST_ENTRY(threadinfo) entries;
+ struct procinfo *proc;
lwpid_t tid;
int in_syscall;
- int in_fork;
- void *fsc;
+ struct current_syscall cs;
struct timespec before;
struct timespec after;
};
+struct procinfo {
+ LIST_ENTRY(procinfo) entries;
+ pid_t pid;
+ struct procabi *abi;
+
+ SLIST_HEAD(, threadinfo) threadlist;
+};
+
struct trussinfo
{
- pid_t pid;
int flags;
- int pr_why;
- int pr_data;
int strsize;
FILE *outfile;
@@ -59,7 +100,7 @@ struct trussinfo
struct threadinfo *curthread;
- SLIST_HEAD(, threadinfo) threadlist;
+ LIST_HEAD(, procinfo) proclist;
};
#define timespecsubt(tvp, uvp, vvp) \
@@ -81,11 +122,3 @@ struct trussinfo
(vvp)->tv_nsec -= 1000000000; \
} \
} while (0)
-
-#define S_NONE 0
-#define S_SCE 1
-#define S_SCX 2
-#define S_EXIT 3
-#define S_SIG 4
-#define S_EXEC 5
-#define S_DETACHED 6
OpenPOWER on IntegriCloud