summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2015-10-27 17:00:04 +0000
committerjhb <jhb@FreeBSD.org>2015-10-27 17:00:04 +0000
commitbff6260e9693ab2384e92fcb007f10ab59f7f92e (patch)
treee91d3c160ea1427e2ab3a5ae4ff111bda64d984e
parent6a0c65530b71a49cd9cb7e44929e03629823a38d (diff)
downloadFreeBSD-src-bff6260e9693ab2384e92fcb007f10ab59f7f92e.zip
FreeBSD-src-bff6260e9693ab2384e92fcb007f10ab59f7f92e.tar.gz
MFC 271389,286330,286331,286358,286378,286380,286381,286383,286388,286848,
286849,286857,286860,286913,286914,286937-286940,286962,286963,288405, 288406,288424,288454-288456,288625,288626,288832,288834,288950,288997, 289080: Merge most of the recent changes to truss in HEAD. The largest effects are that fork following now uses a single truss process (and thus truss -c reports counts for the entire tree of processes instead of separate dumps for each process). truss -c also reports counts for all system calls instead of only a subset. More system call arguments are also decoded. System calls should now report the correct number of arguments (instead of 6), and some platforms that did not properly decode arguments might now do so (e.g. mips64). Changes relative to the equivalent commits to HEAD include: - The ia64 backend was refactored similar to the other backends. - _umtx_lock/_umtx_unlock entries were updated similar to other system call entries. - 10 does not have futimens(), utimensat(), EVFILT_PROCDESC, EVFILT_SENDFILE, RLIMIT_KQUEUES, O_VERIFY, NOTE_FILE_POLL, or EV_FORCEONESHOT. 271389: Stop accessing the saved stack pointer by looking past the end of the array of registers. 286330: Whitespace fix: remove some spurious spaces before commas. 286331: Rework get_string() to make it more robust when fetching strings of unknown length. In particular, instead of blinding fetching 1k blocks, do an initial fetch up to the end of the current page followed by page-sized fetches up to the maximum size. Previously if the 1k buffer crossed a page boundary and the second page was not valid, the entire operation would fail. 286358: Add recently added values of various flags and enumerations including kevent filters, kevent flags, flags to mmap, seek locations, fcntl operations, file flags, socket domains, open flags, resource limits, and pathconf values. 286378: Don't mark the fcntl flag argument as an output parameter so that it is always decoded. Previously the argument was not decoded if fcntl() failed. 286380: Decode the arguments to mkfifo() and fix an off-by-one error in the arguments to mknod(). 286381: Decode the arguments passed to the *at() family of system calls. This is especially useful now that libc's open() always calls openat(). While here, fix a few other things: - Decode the mode argument passed to access(), eaccess(), and faccessat(). - Decode the atfd paramete to pretty-print AT_FDCWD. - Decode the special AT_* flags used with some of the *at() system calls. - Decode arguments for fchmod(), lchmod(), fchown(), lchown(), eaccess(), and futimens(). - Decode both of the timeval structures passed to futimes() instead of just the first one. 286383: Whitespace fixes to consistently use spaces before }'s and wrap long lines. 286388: Consistently use both leading and trailing spaces inside of the {}'s when pretty-printing structures. Most structures used both spaces, but some only used a trailing space and some used neither. 286848: - Decode the arguments for several signal-related system calls: sigpending, sigqueue, sigreturn, sigsuspend, sigtimedwait, sigwait, sigwaitinfo, and thr_kill. - Print signal sets as a structure (with {}'s) and in particular use this to differentiate empty sets from a NULL pointer. - Decode arguments for some other system calls: issetugid, pipe2, sysarch (operations are only decoded for amd64 and i386), and thr_self. 286849: Decode the optional SOCK_NONBLOCK and SOCK_CLOEXEC flags passed in a socket type. 286857: Tidy the linux_socketcall decoding: - Don't exit if get_struct() fails, instead print the raw pointer value to match all other argument decoding cases. - Use an xlat table instead of a home-rolled switch for the operation name. - Display the nested socketcall args structure as a structure instead of as two inline arguments. 286860: Use an xlat table and xlookup() instead of a home-rolled version for the sigprocmask operation type. 286913: Change the argument formatting function to use a stdio FILE object opened with open_memstream() to build the string for each argument. This allows for more complicated argument building without resorting to intermediate malloc's, etc. Related, the strsig*() functions no longer return allocated strings but use a static global buffer instead. 286914: Expand the decoding of kevent structures. - Print the ident value as decimal instead of hexadecimal for filter types that use "small" values such as file descriptors and PIDs. - Decode NOTE_* flags in the fflags field of kevents for several system filter types. 286937: Use nitems(). 286938: Various style and whitespace fixes. 287939: Always use %j with an intmax_t cast to print time_t values. time_t is longer than long on 32-bit platforms with a 64-bit time_t. 286940: ino_t is unsigned, so use uintmax_t instead of intmax_t when printing it. 286962: Rework the argv and env string fetching for execve to be more robust. Before truss would fetch 100 string pointers and happily walk off the end of the array if it never found a NULL. This also means for a short argv list it could fail entirely if the 100 string pointers spanned into an unmapped page. Instead, fetch page-aligned blocks of string pointers in a loop fetching each string until a NULL is found. While here, make use of the open memstream file descriptor instead of allocating a temporary array. This allows us to fetch each string once instead of twice. 286963: Handle the conditional decoding of execve() argument and environment arrays generically rather than duplicating a hack in all of the backends. - Add two new system call argument types and use them instead of StringArray for the argument and environment arguments execve and linux_execve. - Honor the -a/-e flags in the handling of these new types. - Instead of printing "<missing argument>" when the decoding is disabled, print the raw pointer value. 288405: Decode recently added procctl(2) operations. 288406: Trim trailing whitespace. 288424: Several changes to truss. - Refactor the interface between the ABI-independent code and the ABI-specific backends. The backends now provide smaller hooks to fetch system call arguments and return values. The rest of the system call entry and exit handling that was previously duplicated among all the backends has been moved to one place. - Merge the loop when waiting for an event with the loop for handling stops. This also means not emulating a procfs-like interface on top of ptrace(). Instead, use a single event loop that fetches process events via waitid(). Among other things this allows us to report the full 32-bit exit value. - Use PT_FOLLOW_FORK to follow new child processes instead of forking a new truss process for each new child. This allows one truss process to monitor a tree of processes and truss -c should now display one total for the entire tree instead of separate summaries per process. - Use the recently added fields to ptrace_lwpinfo to determine the current system call number and argument count. The latter is especially useful and fixes a regression since the conversion from procfs. truss now generally prints the correct number of arguments for most system calls rather than printing extra arguments for any call not listed in the table in syscalls.c. - Actually check the new ABI when processes call exec. The comments claimed that this happened but it was not being done (perhaps this was another regression in the conversion to ptrace()). If the new ABI after exec is not supported, truss detaches from the process. If truss does not support the ABI for a newly executed process the process is killed before it returns from exec. - Along with the refactor, teach the various ABI-specific backends to fetch both return values, not just the first. Use this to properly report the full 64-bit return value from lseek(). In addition, the handler for "pipe" now pulls the pair of descriptors out of the return values (which is the true kernel system call interface) but displays them as an argument (which matches the interface exported by libc). - Each ABI handler adds entries to a linker set rather than requiring a statically defined table of handlers in main.c. - The arm and mips system call fetching code was changed to follow the same pattern as amd64 (and the in-kernel handler) of fetching register arguments first and then reading any remaining arguments from the stack. This should fix indirect system call arguments on at least arm. - The mipsn32 and n64 ABIs will now look for arguments in A4 through A7. - Use register %ebp for the 6th system call argument for Linux/i386 ABIs to match the in-kernel argument fetch code. - For powerpc binaries on a powerpc64 system, fetch the extra arguments on the stack as 32-bit values that are then copied into the 64-bit argument array instead of reading the 32-bit values directly into the 64-bit array. 288454: - Remove extra integer argument from truncate() and ftruncate(). This is probably fallout from the removal of the extra padding argument before off_t in 7. However, that padding still exists for 32-bit powerpc, so use QUAD_ALIGN. - Fix QUAD_ALIGN to be zero for powerpc64. It should only be set to 1 for 32-bit platforms that add padding to align 64-bit arguments. 288455: The id_t type used to pass IDs to wait6(2) and procctl(6) is a 64-bit integer. Fix the argument decoding to treat this as a quad instead of an int. This includes using QUAD_ALIGN and QUAD_SLOTS as necessary. To continue printing IDs in decimal, add a new QuadHex argument type that prints a 64-bit integer in hex, use QuadHex for the existing off_t arguments, repurpose Quad to print a 64-bit integer in decimal, and use Quad for id_t arguments. This fixes the decoding of wait6(2) and procctl(2) on 32-bit platforms. 288456: Rather than groveling around in a socket address structure for a socket address's length (and then overriding it if it "looks wrong"), use the next argument to the system call to determine the length. This is more reliable since this is what the kernel depends on anyway and is also simpler. 288625: Add decoding for struct statfs. 288626: Style fix. 288832: Fix tracking of unknown syscalls for 'truss -c'. This is done by changing get_syscall() to either lookup the known syscall or add it into the list with the default handlers for printing. This also simplifies some code to not have to check if the syscall variable is set or NULL. 288834: Add decoding for modfind(2) 288950: Group the decoded system calls by ABI and sort the calls within each ABI. 288997: Correct a comment. 289080: Let -c imply -S (hide signal output). Without this, the signals are shown seemingly randomly in the output before the final summary is shown. This is especially noticeable when there is not much output from the application being traced.
-rw-r--r--usr.bin/truss/amd64-fbsd.c292
-rw-r--r--usr.bin/truss/amd64-fbsd32.c297
-rw-r--r--usr.bin/truss/amd64-linux32.c278
-rw-r--r--usr.bin/truss/arm-fbsd.c318
-rw-r--r--usr.bin/truss/extern.h43
-rw-r--r--usr.bin/truss/i386-fbsd.c282
-rw-r--r--usr.bin/truss/i386-linux.c276
-rw-r--r--usr.bin/truss/ia64-fbsd.c272
-rw-r--r--usr.bin/truss/main.c249
-rw-r--r--usr.bin/truss/mips-fbsd.c350
-rw-r--r--usr.bin/truss/powerpc-fbsd.c334
-rw-r--r--usr.bin/truss/powerpc64-fbsd.c298
-rw-r--r--usr.bin/truss/setup.c527
-rw-r--r--usr.bin/truss/sparc64-fbsd.c320
-rw-r--r--usr.bin/truss/syscall.h22
-rw-r--r--usr.bin/truss/syscalls.c1482
-rw-r--r--usr.bin/truss/truss.14
-rw-r--r--usr.bin/truss/truss.h61
18 files changed, 1926 insertions, 3779 deletions
diff --git a/usr.bin/truss/amd64-fbsd.c b/usr.bin/truss/amd64-fbsd.c
index d0be3e6..ade9322 100644
--- a/usr.bin/truss/amd64-fbsd.c
+++ b/usr.bin/truss/amd64-fbsd.c
@@ -29,311 +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 = sizeof(syscallnames) / sizeof(syscallnames[0]);
-
-/*
- * 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;
+ 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%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
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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 74d45a2..84a4b6b 100644
--- a/usr.bin/truss/amd64-fbsd32.c
+++ b/usr.bin/truss/amd64-fbsd32.c
@@ -29,312 +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 = sizeof(freebsd32_syscallnames) /
- sizeof(freebsd32_syscallnames[0]);
-
-/*
- * 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
-
- if (fsc->name != NULL && (strcmp(fsc->name, "freebsd32_execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "freebsd32_execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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 2c025a3..4f64af3 100644
--- a/usr.bin/truss/amd64-linux32.c
+++ b/usr.bin/truss/amd64-linux32.c
@@ -29,124 +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 =
- sizeof(linux32_syscallnames) / sizeof(linux32_syscallnames[0]);
-
-/*
- * 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
@@ -154,82 +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;
- }
-
- 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
+ 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;
}
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "linux_execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
/*
@@ -247,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);
+ retval[0] = regs.r_rax & 0xffffffff;
+ retval[1] = regs.r_rdx & 0xffffffff;
+ *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 (*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 < sizeof(bsd_to_linux_errno) / sizeof(int); 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 2aea4e4..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,324 +41,98 @@ __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 = sizeof(syscallnames) / sizeof(syscallnames[0]);
-
-/*
- * 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];
#else
- if ((syscall_num = ptrace(PT_READ_I, tid,
+ 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[_REG_SP] +
+ 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
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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 c60c6c7..708d055 100644
--- a/usr.bin/truss/extern.h
+++ b/usr.bin/truss/extern.h
@@ -31,46 +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 __ia64__
-extern void ia64_syscall_entry(struct trussinfo *, int);
-extern long ia64_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 89879d2..029815e 100644
--- a/usr.bin/truss/i386-fbsd.c
+++ b/usr.bin/truss/i386-fbsd.c
@@ -29,305 +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 = sizeof(syscallnames) / sizeof(syscallnames[0]);
-
-/*
- * 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);
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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.
- */
+ retval[0] = regs.r_eax;
+ retval[1] = regs.r_edx;
+ *errorp = !!(regs.r_eflags & PSL_C);
+ return (0);
+}
- 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;
- }
- }
- }
+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 84f1451..fa57af2 100644
--- a/usr.bin/truss/i386-linux.c
+++ b/usr.bin/truss/i386-linux.c
@@ -29,124 +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 =
- sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]);
-
-/*
- * 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
@@ -154,82 +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;
- }
-
- 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
+ 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;
}
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
-
- if (fsc->name != NULL && (strcmp(fsc->name, "linux_execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "linux_execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- trussinfo->curthread->fsc = fsc;
+ return (0);
}
/*
@@ -247,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);
+ retval[0] = regs.r_eax;
+ retval[1] = regs.r_edx;
+ *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;
- 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 < sizeof(bsd_to_linux_errno) / sizeof(int); 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/ia64-fbsd.c b/usr.bin/truss/ia64-fbsd.c
index 2218fe5..16ce749 100644
--- a/usr.bin/truss/ia64-fbsd.c
+++ b/usr.bin/truss/ia64-fbsd.c
@@ -29,101 +29,33 @@
* SUCH DAMAGE.
*/
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
-/*
- * FreeBSD/ia64-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/ia64-specific system call handling. */
-#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <machine/reg.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 = sizeof(syscallnames) / sizeof(syscallnames[0]);
-
-/*
- * 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 ia64/ia64/trap.c
- * is ever changed these functions need to keep up.
- */
-
-void
-ia64_syscall_entry(struct trussinfo *trussinfo, int nargs)
+static int
+ia64_fetch_args(struct trussinfo *trussinfo, u_int narg)
{
struct reg regs;
- struct freebsd_syscall *fsc;
- struct syscall *sc;
+ struct current_syscall *cs;
unsigned long *parm_offset;
lwpid_t tid;
- int i, syscall_num;
+ 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;
@@ -131,184 +63,50 @@ ia64_syscall_entry(struct trussinfo *trussinfo, int nargs)
parm_offset = &regs.r_scratch.gr16;
/*
- * 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.
*/
- syscall_num = regs.r_scratch.gr15; /* XXX double-check. */
- if (syscall_num == SYS_syscall || syscall_num == SYS___syscall)
- syscall_num = (int)*parm_offset++;
-
- 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, "rfork") == 0 ||
- strcmp(fsc->name, "vfork") == 0))
- trussinfo->curthread->in_fork = 1;
-
- if (nargs == 0)
- return;
-
- fsc->args = malloc((1 + nargs) * sizeof(unsigned long));
- memcpy(fsc->args, parm_offset, nargs * sizeof(long));
-
- 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
+ switch (regs.r_scratch.gr15) {
+ case SYS_syscall:
+ case SYS___syscall:
+ parm_offset++;
+ break;
}
-#if DEBUG
- fprintf(trussinfo->outfile, "\n");
-#endif
+ memcpy(cs->args, parm_offset, narg * sizeof(long));
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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
-ia64_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused)
+static int
+ia64_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_scratch.gr8;
- errorp = (regs.r_scratch.gr10 != 0) ? 1 : 0;
-
- /*
- * 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_scratch.gr8;
+ retval[1] = regs.r_scratch.gr9;
+ *errorp = (regs.r_scratch.gr10 != 0) ? 1 : 0;
+ return (0);
+}
- print_syscall_ret(trussinfo, fsc->name, fsc->nargs, fsc->s_args, errorp,
- retval, fsc->sc);
- free_fsc(fsc);
+static struct procabi ia64_fbsd = {
+ "FreeBSD ELF64",
+ syscallnames,
+ nitems(syscallnames),
+ ia64_fetch_args,
+ ia64_fetch_retval
+};
- return (retval);
-}
+PROCABI(ia64_fbsd);
diff --git a/usr.bin/truss/main.c b/usr.bin/truss/main.c
index ecaa0db..5eec953 100644
--- a/usr.bin/truss/main.c
+++ b/usr.bin/truss/main.c
@@ -38,20 +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 <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <time.h>
#include <unistd.h>
@@ -59,8 +51,6 @@ __FBSDID("$FreeBSD$");
#include "extern.h"
#include "syscall.h"
-#define MAXARGS 6
-
static void
usage(void)
{
@@ -70,133 +60,48 @@ 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 __ia64__
- { "FreeBSD ELF64", ia64_syscall_entry, ia64_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)
{
- char *ret;
+ static char tmp[64];
- ret = NULL;
if (sig > 0 && sig < NSIG) {
- asprintf(&ret, "SIG%s", sys_signame[sig]);
- if (ret == NULL)
- return (NULL);
+ snprintf(tmp, sizeof(tmp), "SIG%s", sys_signame[sig]);
+ return (tmp);
}
- return (ret);
+ return (NULL);
}
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);
+ init_syscalls();
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 */
@@ -206,7 +111,7 @@ main(int ac, char **av)
trussinfo->flags |= EXECVEARGS;
break;
case 'c': /* Count number of system calls and time. */
- trussinfo->flags |= COUNTONLY;
+ trussinfo->flags |= (COUNTONLY | NOSIGS);
break;
case 'e': /* Print execve() environment strings. */
trussinfo->flags |= EXECVEENVS;
@@ -232,8 +137,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 */
@@ -251,10 +156,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);
@@ -265,119 +170,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, "%ld.%09ld ",
- (long)timediff.tv_sec,
- timediff.tv_nsec);
- }
- if (trussinfo->flags & RELATIVETIMESTAMPS) {
- timespecsubt(&trussinfo->curthread->after,
- &trussinfo->curthread->before, &timediff);
- fprintf(trussinfo->outfile, "%ld.%09ld ",
- (long)timediff.tv_sec,
- timediff.tv_nsec);
- }
- signame = strsig(trussinfo->pr_data);
- fprintf(trussinfo->outfile,
- "SIGNAL %u (%s)\n", trussinfo->pr_data,
- signame == NULL ? "?" : signame);
- free(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, "%ld.%09ld ",
- (long)timediff.tv_sec,
- timediff.tv_nsec);
- }
- if (trussinfo->flags & RELATIVETIMESTAMPS) {
- timespecsubt(&trussinfo->curthread->after,
- &trussinfo->curthread->before, &timediff);
- fprintf(trussinfo->outfile, "%ld.%09ld ",
- (long)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 71e9efa..d488b7e 100644
--- a/usr.bin/truss/mips-fbsd.c
+++ b/usr.bin/truss/mips-fbsd.c
@@ -29,339 +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 = sizeof(syscallnames) / sizeof(syscallnames[0]);
-
-/*
- * 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
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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 990da29..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,285 +47,104 @@ static const char rcsid[] =
#include "syscalls.h"
#endif
-static int nsyscalls = sizeof(syscallnames) / sizeof(syscallnames[0]);
-
-/*
- * 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
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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 9d5cbd9..5ca5c22 100644
--- a/usr.bin/truss/powerpc64-fbsd.c
+++ b/usr.bin/truss/powerpc64-fbsd.c
@@ -25,308 +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 = sizeof(syscallnames) / sizeof(syscallnames[0]);
-
-/*
- * 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
-
- if (fsc->name && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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 a52bd16..74678e6 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,114 +124,469 @@ 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);
+}
- return (waitval);
+/*
+ * Determine the ABI. This is called after every exec, and when
+ * a process is first monitored.
+ */
+static struct procabi *
+find_abi(pid_t pid)
+{
+ struct procabi **pabi;
+ size_t len;
+ int error;
+ int mib[4];
+ char progt[32];
+
+ len = sizeof(progt);
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_SV_NAME;
+ mib[3] = pid;
+ error = sysctl(mib, 4, progt, &len, NULL, 0);
+ if (error != 0)
+ err(2, "can not get sysvec name");
+
+ SET_FOREACH(pabi, procabi) {
+ if (strcmp((*pabi)->type, progt) == 0)
+ 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);
+ }
+
+ 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;
+
+ np = find_proc(info, pid);
+ assert(np != NULL);
- info->curthread = NULL;
- SLIST_FOREACH(np, &info->threadlist, entries) {
- if (np->tid == lwpid) {
- info->curthread = np;
+ 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, narg);
+ t->cs.nargs = sc->nargs;
+ assert(sc->nargs <= nitems(t->cs.s_args));
+
+ 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->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 (WIFCONTINUED(waitval)) {
- info->pr_why = S_NONE;
+ 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;
- }
- if (WIFEXITED(waitval)) {
- info->pr_why = S_EXIT;
- info->pr_data = WEXITSTATUS(waitval);
+
+ 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;
+
+ sc = t->cs.sc;
+ /*
+ * 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);
}
- /* We didn't send the SIGTRAP, just forward it. */
- /* FALLTHROUGH */
- default:
- info->pr_why = S_SIG;
- info->pr_data = WSTOPSIG(waitval);
- pending_signal = info->pr_data;
- break;
+ t->cs.s_args[i] = temp;
}
}
- if (WIFSIGNALED(waitval)) {
- info->pr_why = S_EXIT;
- info->pr_data = 0;
- return;
+
+ 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 &&
+ (pl.pl_flags & (PL_FLAG_SCE|PL_FLAG_SCX)) != 0) {
+ if (pl.pl_flags & PL_FLAG_SCE)
+ enter_syscall(info, &pl);
+ else if (pl.pl_flags & PL_FLAG_SCX)
+ exit_syscall(info, &pl);
+ 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;
+ }
+ 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;
+ }
}
}
diff --git a/usr.bin/truss/sparc64-fbsd.c b/usr.bin/truss/sparc64-fbsd.c
index 3c6de5f..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,310 +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 = sizeof(syscallnames) / sizeof(syscallnames[0]);
-
-/*
- * 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
-
- if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 ||
- strcmp(fsc->name, "exit") == 0)) {
- /*
- * XXX
- * This could be done in a more general
- * manner but it still wouldn't be very pretty.
- */
- if (strcmp(fsc->name, "execve") == 0) {
- if ((trussinfo->flags & EXECVEARGS) == 0) {
- if (fsc->s_args[1]) {
- free(fsc->s_args[1]);
- fsc->s_args[1] = NULL;
- }
- }
- if ((trussinfo->flags & EXECVEENVS) == 0) {
- if (fsc->s_args[2]) {
- free(fsc->s_args[2]);
- fsc->s_args[2] = NULL;
- }
- }
- }
- }
- 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 b74eded..a240749 100644
--- a/usr.bin/truss/syscall.h
+++ b/usr.bin/truss/syscall.h
@@ -10,6 +10,7 @@
* BinString -- pointer to an array of chars, printed via strvisx().
* Ptr -- pointer to some unspecified structure. Just print as hex for now.
* Stat -- a pointer to a stat buffer. Prints a couple fields.
+ * StatFs -- a pointer to a statfs buffer. Prints a few fields.
* Ioctl -- an ioctl command. Woefully limited.
* Quad -- a double-word value. e.g., lseek(int, offset_t, int)
* Signal -- a signal number. Prints the signal name (SIGxxx)
@@ -39,10 +40,11 @@
enum Argtype { None = 1, Hex, Octal, Int, LongHex, Name, Ptr, Stat, Ioctl, Quad,
Signal, Sockaddr, StringArray, Timespec, Timeval, Itimerval, Pollfd,
Fd_set, Sigaction, Fcntl, Mprot, Mmapflags, Whence, Readlinkres,
- Umtx, Sigset, Sigprocmask, Kevent, Sockdomain, Socktype, Open,
+ Umtx, Sigset, Sigprocmask, StatFs, Kevent, Sockdomain, Socktype, Open,
Fcntlflag, Rusage, BinString, Shutdown, Resource, Rlimit, Timeval2,
Pathconf, Rforkflags, ExitStatus, Waitoptions, Idtype, Procctl,
- LinuxSockArgs, Umtxop };
+ LinuxSockArgs, Umtxop, Atfd, Atflags, Accessmode, Long,
+ Sysarch, ExecArgs, ExecEnv, PipeFds, QuadHex };
#define ARG_MASK 0xff
#define OUT 0x100
@@ -54,9 +56,10 @@ struct syscall_args {
};
struct syscall {
+ STAILQ_ENTRY(syscall) entries;
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 */
@@ -64,8 +67,8 @@ struct syscall {
int nerror; /* Number of calls that returned with error */
};
-struct syscall *get_syscall(const char*);
-char *print_arg(struct syscall_args *, unsigned long*, long, struct trussinfo *);
+struct syscall *get_syscall(const char *, int nargs);
+char *print_arg(struct syscall_args *, unsigned long*, long *, struct trussinfo *);
/*
* Linux Socket defines
@@ -86,11 +89,11 @@ char *print_arg(struct syscall_args *, unsigned long*, long, struct trussinfo *)
#define LINUX_SETSOCKOPT 14
#define LINUX_GETSOCKOPT 15
#define LINUX_SENDMSG 16
-#define LINUX_RECVMSG 17
+#define LINUX_RECVMSG 17
#define PAD_(t) (sizeof(register_t) <= sizeof(t) ? \
0 : sizeof(register_t) - sizeof(t))
-
+
#if BYTE_ORDER == LITTLE_ENDIAN
#define PADL_(t) 0
#define PADR_(t) PAD_(t)
@@ -107,7 +110,8 @@ struct linux_socketcall_args {
char args_l_[PADL_(l_ulong)]; l_ulong args; char args_r_[PADR_(l_ulong)];
};
+void init_syscalls(void);
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 f473be4..7815fd1 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,28 +38,30 @@ static const char rcsid[] =
*/
#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/ioccom.h>
#include <sys/mman.h>
+#include <sys/mount.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 <machine/atomic.h>
+#include <errno.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 <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
+#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -75,7 +75,7 @@ static const char rcsid[] =
#include "syscall.h"
/* 64-bit alignment on 32-bit platforms. */
-#ifdef __powerpc__
+#if !defined(__LP64__) && defined(__powerpc__)
#define QUAD_ALIGN 1
#else
#define QUAD_ALIGN 0
@@ -91,203 +91,296 @@ static const char rcsid[] =
/*
* This should probably be in its own file, sorted alphabetically.
*/
-static struct syscall syscalls[] = {
- { .name = "fcntl", .ret_type = 1, .nargs = 3,
- .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 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 = "readlink", .ret_type = 1, .nargs = 3,
- .args = { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } },
- { .name = "lseek", .ret_type = 2, .nargs = 3,
- .args = { { Int, 0 }, { Quad, 1 + QUAD_ALIGN }, { 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,
- .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 }, { Int, 4 }, { Quad, 5 + QUAD_ALIGN } } },
- { .name = "linux_mkdir", .ret_type = 1, .nargs = 2,
- .args = { { Name | IN, 0} , {Int, 1}}},
- { .name = "mprotect", .ret_type = 1, .nargs = 3,
- .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } },
- { .name = "open", .ret_type = 1, .nargs = 3,
- .args = { { Name | IN, 0 } , { Open, 1 }, { Octal, 2 } } },
- { .name = "mkdir", .ret_type = 1, .nargs = 2,
- .args = { { Name, 0 } , { Octal, 1 } } },
- { .name = "linux_open", .ret_type = 1, .nargs = 3,
- .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
- { .name = "close", .ret_type = 1, .nargs = 1,
- .args = { { Int, 0 } } },
- { .name = "link", .ret_type = 0, .nargs = 2,
- .args = { { Name, 0 }, { Name, 1 } } },
- { .name = "unlink", .ret_type = 0, .nargs = 1,
- .args = { { Name, 0 } } },
- { .name = "chdir", .ret_type = 0, .nargs = 1,
- .args = { { Name, 0 } } },
- { .name = "chroot", .ret_type = 0, .nargs = 1,
- .args = { { Name, 0 } } },
- { .name = "mknod", .ret_type = 0, .nargs = 3,
- .args = { { Name, 0 }, { Octal, 1 }, { Int, 3 } } },
- { .name = "chmod", .ret_type = 0, .nargs = 2,
- .args = { { Name, 0 }, { Octal, 1 } } },
- { .name = "chown", .ret_type = 0, .nargs = 3,
- .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
- { .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,
- .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } },
- { .name = "umount", .ret_type = 0, .nargs = 2,
- .args = { { Name, 0 }, { Int, 2 } } },
- { .name = "fstat", .ret_type = 1, .nargs = 2,
- .args = { { Int, 0 }, { Stat | OUT , 1 } } },
- { .name = "stat", .ret_type = 1, .nargs = 2,
- .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
- { .name = "lstat", .ret_type = 1, .nargs = 2,
- .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
- { .name = "linux_newstat", .ret_type = 1, .nargs = 2,
- .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
- { .name = "linux_access", .ret_type = 1, .nargs = 2,
- .args = { { Name, 0 }, { Int, 1 }}},
- { .name = "linux_newfstat", .ret_type = 1, .nargs = 2,
- .args = { { Int, 0 }, { Ptr | OUT, 1 } } },
- { .name = "write", .ret_type = 1, .nargs = 3,
- .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } },
- { .name = "ioctl", .ret_type = 1, .nargs = 3,
- .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } },
- { .name = "break", .ret_type = 1, .nargs = 1,
- .args = { { Ptr, 0 } } },
- { .name = "exit", .ret_type = 0, .nargs = 1,
- .args = { { Hex, 0 } } },
- { .name = "access", .ret_type = 1, .nargs = 2,
- .args = { { Name | IN, 0 }, { Int, 1 } } },
- { .name = "sigaction", .ret_type = 1, .nargs = 3,
- .args = { { Signal, 0 }, { Sigaction | IN, 1 }, { Sigaction | OUT, 2 } } },
+static struct syscall decoded_syscalls[] = {
+ /* Native ABI */
+ { .name = "__getcwd", .ret_type = 1, .nargs = 2,
+ .args = { { Name | OUT, 0 }, { Int, 1 } } },
+ { .name = "_umtx_lock", .ret_type = 1, .nargs = 1,
+ .args = { { Umtx, 0 } } },
+ { .name = "_umtx_op", .ret_type = 1, .nargs = 5,
+ .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 },
+ { Ptr, 4 } } },
+ { .name = "_umtx_unlock", .ret_type = 1, .nargs = 1,
+ .args = { { Umtx, 0 } } },
{ .name = "accept", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
+ { .name = "access", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
{ .name = "bind", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
+ { .name = "bindat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
+ { Int, 3 } } },
+ { .name = "break", .ret_type = 1, .nargs = 1,
+ .args = { { Ptr, 0 } } },
+ { .name = "chdir", .ret_type = 1, .nargs = 1,
+ .args = { { Name, 0 } } },
+ { .name = "chflags", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Hex, 1 } } },
+ { .name = "chmod", .ret_type = 1, .nargs = 2,
+ .args = { { Name, 0 }, { Octal, 1 } } },
+ { .name = "chown", .ret_type = 1, .nargs = 3,
+ .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
+ { .name = "chroot", .ret_type = 1, .nargs = 1,
+ .args = { { Name, 0 } } },
+ { .name = "clock_gettime", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
+ { .name = "close", .ret_type = 1, .nargs = 1,
+ .args = { { Int, 0 } } },
{ .name = "connect", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Sockaddr | IN, 1 }, { Int, 2 } } },
+ { .name = "connectat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Int, 1 }, { Sockaddr | IN, 2 },
+ { Int, 3 } } },
+ { .name = "eaccess", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Accessmode, 1 } } },
+ { .name = "execve", .ret_type = 1, .nargs = 3,
+ .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
+ { ExecEnv | IN, 2 } } },
+ { .name = "exit", .ret_type = 0, .nargs = 1,
+ .args = { { Hex, 0 } } },
+ { .name = "faccessat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Name | IN, 1 }, { Accessmode, 2 },
+ { Atflags, 3 } } },
+ { .name = "fchmod", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { Octal, 1 } } },
+ { .name = "fchmodat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Atflags, 3 } } },
+ { .name = "fchown", .ret_type = 1, .nargs = 3,
+ .args = { { Int, 0 }, { Int, 1 }, { Int, 2 } } },
+ { .name = "fchownat", .ret_type = 1, .nargs = 5,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Int, 2 }, { Int, 3 },
+ { Atflags, 4 } } },
+ { .name = "fcntl", .ret_type = 1, .nargs = 3,
+ .args = { { Int, 0 }, { Fcntl, 1 }, { Fcntlflag, 2 } } },
+ { .name = "fstat", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { Stat | OUT, 1 } } },
+ { .name = "fstatat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Name | IN, 1 }, { Stat | OUT, 2 },
+ { Atflags, 3 } } },
+ { .name = "fstatfs", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { StatFs | OUT, 1 } } },
+ { .name = "ftruncate", .ret_type = 1, .nargs = 2,
+ .args = { { Int | IN, 0 }, { QuadHex | IN, 1 + QUAD_ALIGN } } },
+ { .name = "futimes", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { Timeval2 | IN, 1 } } },
+ { .name = "futimesat", .ret_type = 1, .nargs = 3,
+ .args = { { Atfd, 0 }, { Name | IN, 1 }, { Timeval2 | IN, 2 } } },
+ { .name = "getitimer", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { Itimerval | OUT, 2 } } },
{ .name = "getpeername", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
+ { .name = "getpgid", .ret_type = 1, .nargs = 1,
+ .args = { { Int, 0 } } },
+ { .name = "getrlimit", .ret_type = 1, .nargs = 2,
+ .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } },
+ { .name = "getrusage", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { Rusage | OUT, 1 } } },
+ { .name = "getsid", .ret_type = 1, .nargs = 1,
+ .args = { { Int, 0 } } },
{ .name = "getsockname", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { Sockaddr | OUT, 1 }, { Ptr | OUT, 2 } } },
- { .name = "recvfrom", .ret_type = 1, .nargs = 6,
- .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
- { .name = "sendto", .ret_type = 1, .nargs = 6,
- .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 }, { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
- { .name = "execve", .ret_type = 1, .nargs = 3,
- .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
- { .name = "linux_execve", .ret_type = 1, .nargs = 3,
- .args = { { Name | IN, 0 }, { StringArray | IN, 1 }, { StringArray | IN, 2 } } },
- { .name = "kldload", .ret_type = 0, .nargs = 1,
+ { .name = "gettimeofday", .ret_type = 1, .nargs = 2,
+ .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } },
+ { .name = "ioctl", .ret_type = 1, .nargs = 3,
+ .args = { { Int, 0 }, { Ioctl, 1 }, { Hex, 2 } } },
+ { .name = "kevent", .ret_type = 1, .nargs = 6,
+ .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 },
+ { Int, 4 }, { Timespec, 5 } } },
+ { .name = "kill", .ret_type = 1, .nargs = 2,
+ .args = { { Int | IN, 0 }, { Signal | IN, 1 } } },
+ { .name = "kldfind", .ret_type = 1, .nargs = 1,
.args = { { Name | IN, 0 } } },
- { .name = "kldunload", .ret_type = 0, .nargs = 1,
+ { .name = "kldfirstmod", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
- { .name = "kldfind", .ret_type = 0, .nargs = 1,
+ { .name = "kldload", .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 = "kldunload", .ret_type = 1, .nargs = 1,
.args = { { Int, 0 } } },
- { .name = "nanosleep", .ret_type = 0, .nargs = 1,
- .args = { { Timespec, 0 } } },
- { .name = "select", .ret_type = 1, .nargs = 5,
- .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 }, { Timeval, 4 } } },
- { .name = "poll", .ret_type = 1, .nargs = 3,
- .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } },
- { .name = "gettimeofday", .ret_type = 1, .nargs = 2,
- .args = { { Timeval | OUT, 0 }, { Ptr, 1 } } },
- { .name = "clock_gettime", .ret_type = 1, .nargs = 2,
- .args = { { Int, 0 }, { Timespec | OUT, 1 } } },
- { .name = "getitimer", .ret_type = 1, .nargs = 2,
- .args = { { Int, 0 }, { Itimerval | OUT, 2 } } },
- { .name = "setitimer", .ret_type = 1, .nargs = 3,
- .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,
- .args = { { Int, 0 }, { Kevent, 1 }, { Int, 2 }, { Kevent | OUT, 3 }, { Int, 4 }, { Timespec, 5 } } },
- { .name = "_umtx_lock", .ret_type = 0, .nargs = 1,
- .args = { { Umtx, 0 } } },
- { .name = "_umtx_unlock", .ret_type = 0, .nargs = 1,
- .args = { { Umtx, 0 } } },
- { .name = "sigprocmask", .ret_type = 0, .nargs = 3,
- .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
- { .name = "unmount", .ret_type = 1, .nargs = 2,
- .args = { { Name, 0 }, { Int, 1 } } },
- { .name = "socket", .ret_type = 1, .nargs = 3,
- .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } },
- { .name = "getrusage", .ret_type = 1, .nargs = 2,
- .args = { { Int, 0 }, { Rusage | OUT, 1 } } },
- { .name = "__getcwd", .ret_type = 1, .nargs = 2,
- .args = { { Name | OUT, 0 }, { Int, 1 } } },
- { .name = "shutdown", .ret_type = 1, .nargs = 2,
- .args = { { Int, 0 }, { Shutdown, 1 } } },
- { .name = "getrlimit", .ret_type = 1, .nargs = 2,
- .args = { { Resource, 0 }, { Rlimit | OUT, 1 } } },
- { .name = "setrlimit", .ret_type = 1, .nargs = 2,
- .args = { { Resource, 0 }, { Rlimit | IN, 1 } } },
- { .name = "utimes", .ret_type = 1, .nargs = 2,
- .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
- { .name = "lutimes", .ret_type = 1, .nargs = 2,
- .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
- { .name = "futimes", .ret_type = 1, .nargs = 2,
- .args = { { Int, 0 }, { Timeval | IN, 1 } } },
- { .name = "chflags", .ret_type = 1, .nargs = 2,
- .args = { { Name | IN, 0 }, { Hex, 1 } } },
{ .name = "lchflags", .ret_type = 1, .nargs = 2,
.args = { { Name | IN, 0 }, { Hex, 1 } } },
+ { .name = "lchmod", .ret_type = 1, .nargs = 2,
+ .args = { { Name, 0 }, { Octal, 1 } } },
+ { .name = "lchown", .ret_type = 1, .nargs = 3,
+ .args = { { Name, 0 }, { Int, 1 }, { Int, 2 } } },
+ { .name = "link", .ret_type = 1, .nargs = 2,
+ .args = { { Name, 0 }, { Name, 1 } } },
+ { .name = "linkat", .ret_type = 1, .nargs = 5,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 },
+ { Atflags, 4 } } },
+ { .name = "lseek", .ret_type = 2, .nargs = 3,
+ .args = { { Int, 0 }, { QuadHex, 1 + QUAD_ALIGN },
+ { Whence, 1 + QUAD_SLOTS + QUAD_ALIGN } } },
+ { .name = "lstat", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
+ { .name = "lutimes", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
+ { .name = "mkdir", .ret_type = 1, .nargs = 2,
+ .args = { { Name, 0 }, { Octal, 1 } } },
+ { .name = "mkdirat", .ret_type = 1, .nargs = 3,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
+ { .name = "mkfifo", .ret_type = 1, .nargs = 2,
+ .args = { { Name, 0 }, { Octal, 1 } } },
+ { .name = "mkfifoat", .ret_type = 1, .nargs = 3,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 } } },
+ { .name = "mknod", .ret_type = 1, .nargs = 3,
+ .args = { { Name, 0 }, { Octal, 1 }, { Int, 2 } } },
+ { .name = "mknodat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Octal, 2 }, { Int, 3 } } },
+ { .name = "mmap", .ret_type = 1, .nargs = 6,
+ .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 }, { Mmapflags, 3 },
+ { Int, 4 }, { QuadHex, 5 + QUAD_ALIGN } } },
+ { .name = "modfind", .ret_type = 1, .nargs = 1,
+ .args = { { Name | IN, 0 } } },
+ { .name = "mount", .ret_type = 1, .nargs = 4,
+ .args = { { Name, 0 }, { Name, 1 }, { Int, 2 }, { Ptr, 3 } } },
+ { .name = "mprotect", .ret_type = 1, .nargs = 3,
+ .args = { { Ptr, 0 }, { Int, 1 }, { Mprot, 2 } } },
+ { .name = "munmap", .ret_type = 1, .nargs = 2,
+ .args = { { Ptr, 0 }, { Int, 1 } } },
+ { .name = "nanosleep", .ret_type = 1, .nargs = 1,
+ .args = { { Timespec, 0 } } },
+ { .name = "open", .ret_type = 1, .nargs = 3,
+ .args = { { Name | IN, 0 }, { Open, 1 }, { Octal, 2 } } },
+ { .name = "openat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Name | IN, 1 }, { Open, 2 },
+ { Octal, 3 } } },
{ .name = "pathconf", .ret_type = 1, .nargs = 2,
.args = { { Name | IN, 0 }, { Pathconf, 1 } } },
{ .name = "pipe", .ret_type = 1, .nargs = 1,
- .args = { { Ptr, 0 } } },
- { .name = "truncate", .ret_type = 1, .nargs = 3,
- .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
- { .name = "ftruncate", .ret_type = 1, .nargs = 3,
- .args = { { Int | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } },
- { .name = "kill", .ret_type = 1, .nargs = 2,
- .args = { { Int | IN, 0 }, { Signal | IN, 1 } } },
- { .name = "munmap", .ret_type = 1, .nargs = 2,
- .args = { { Ptr, 0 }, { Int, 1 } } },
+ .args = { { PipeFds | OUT, 0 } } },
+ { .name = "pipe2", .ret_type = 1, .nargs = 2,
+ .args = { { Ptr, 0 }, { Open, 1 } } },
+ { .name = "poll", .ret_type = 1, .nargs = 3,
+ .args = { { Pollfd, 0 }, { Int, 1 }, { Int, 2 } } },
+ { .name = "posix_openpt", .ret_type = 1, .nargs = 1,
+ .args = { { Open, 0 } } },
+ { .name = "procctl", .ret_type = 1, .nargs = 4,
+ .args = { { Idtype, 0 }, { Quad, 1 + QUAD_ALIGN },
+ { Procctl, 1 + QUAD_ALIGN + QUAD_SLOTS },
+ { Ptr, 2 + QUAD_ALIGN + QUAD_SLOTS } } },
{ .name = "read", .ret_type = 1, .nargs = 3,
.args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 } } },
+ { .name = "readlink", .ret_type = 1, .nargs = 3,
+ .args = { { Name, 0 }, { Readlinkres | OUT, 1 }, { Int, 2 } } },
+ { .name = "readlinkat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Readlinkres | OUT, 2 },
+ { Int, 3 } } },
+ { .name = "recvfrom", .ret_type = 1, .nargs = 6,
+ .args = { { Int, 0 }, { BinString | OUT, 1 }, { Int, 2 }, { Hex, 3 },
+ { Sockaddr | OUT, 4 }, { Ptr | OUT, 5 } } },
{ .name = "rename", .ret_type = 1, .nargs = 2,
- .args = { { Name , 0 } , { Name, 1 } } },
+ .args = { { Name, 0 }, { Name, 1 } } },
+ { .name = "renameat", .ret_type = 1, .nargs = 4,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Atfd, 2 }, { Name, 3 } } },
+ { .name = "rfork", .ret_type = 1, .nargs = 1,
+ .args = { { Rforkflags, 0 } } },
+ { .name = "select", .ret_type = 1, .nargs = 5,
+ .args = { { Int, 0 }, { Fd_set, 1 }, { Fd_set, 2 }, { Fd_set, 3 },
+ { Timeval, 4 } } },
+ { .name = "sendto", .ret_type = 1, .nargs = 6,
+ .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 }, { Hex, 3 },
+ { Sockaddr | IN, 4 }, { Ptr | IN, 5 } } },
+ { .name = "setitimer", .ret_type = 1, .nargs = 3,
+ .args = { { Int, 0 }, { Itimerval, 1 }, { Itimerval | OUT, 2 } } },
+ { .name = "setrlimit", .ret_type = 1, .nargs = 2,
+ .args = { { Resource, 0 }, { Rlimit | IN, 1 } } },
+ { .name = "shutdown", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { Shutdown, 1 } } },
+ { .name = "sigaction", .ret_type = 1, .nargs = 3,
+ .args = { { Signal, 0 }, { Sigaction | IN, 1 },
+ { Sigaction | OUT, 2 } } },
+ { .name = "sigpending", .ret_type = 1, .nargs = 1,
+ .args = { { Sigset | OUT, 0 } } },
+ { .name = "sigprocmask", .ret_type = 1, .nargs = 3,
+ .args = { { Sigprocmask, 0 }, { Sigset, 1 }, { Sigset | OUT, 2 } } },
+ { .name = "sigqueue", .ret_type = 1, .nargs = 3,
+ .args = { { Int, 0 }, { Signal, 1 }, { LongHex, 2 } } },
+ { .name = "sigreturn", .ret_type = 1, .nargs = 1,
+ .args = { { Ptr, 0 } } },
+ { .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 } } },
+ { .name = "sigwait", .ret_type = 1, .nargs = 2,
+ .args = { { Sigset | IN, 0 }, { Ptr, 1 } } },
+ { .name = "sigwaitinfo", .ret_type = 1, .nargs = 2,
+ .args = { { Sigset | IN, 0 }, { Ptr, 1 } } },
+ { .name = "socket", .ret_type = 1, .nargs = 3,
+ .args = { { Sockdomain, 0 }, { Socktype, 1 }, { Int, 2 } } },
+ { .name = "stat", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Stat | OUT, 1 } } },
+ { .name = "statfs", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { StatFs | OUT, 1 } } },
{ .name = "symlink", .ret_type = 1, .nargs = 2,
- .args = { { Name , 0 } , { Name, 1 } } },
- { .name = "posix_openpt", .ret_type = 1, .nargs = 1,
- .args = { { Open, 0 } } },
+ .args = { { Name, 0 }, { Name, 1 } } },
+ { .name = "symlinkat", .ret_type = 1, .nargs = 3,
+ .args = { { Name, 0 }, { Atfd, 1 }, { Name, 2 } } },
+ { .name = "sysarch", .ret_type = 1, .nargs = 2,
+ .args = { { Sysarch, 0 }, { Ptr, 1 } } },
+ { .name = "thr_kill", .ret_type = 1, .nargs = 2,
+ .args = { { Long, 0 }, { Signal, 1 } } },
+ { .name = "thr_self", .ret_type = 1, .nargs = 1,
+ .args = { { Ptr, 0 } } },
+ { .name = "truncate", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { QuadHex | IN, 1 + QUAD_ALIGN } } },
+#if 0
+ /* Does not exist */
+ { .name = "umount", .ret_type = 1, .nargs = 2,
+ .args = { { Name, 0 }, { Int, 2 } } },
+#endif
+ { .name = "unlink", .ret_type = 1, .nargs = 1,
+ .args = { { Name, 0 } } },
+ { .name = "unlinkat", .ret_type = 1, .nargs = 3,
+ .args = { { Atfd, 0 }, { Name, 1 }, { Atflags, 2 } } },
+ { .name = "unmount", .ret_type = 1, .nargs = 2,
+ .args = { { Name, 0 }, { Int, 1 } } },
+ { .name = "utimes", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Timeval2 | IN, 1 } } },
{ .name = "wait4", .ret_type = 1, .nargs = 4,
.args = { { Int, 0 }, { ExitStatus | OUT, 1 }, { Waitoptions, 2 },
{ Rusage | OUT, 3 } } },
{ .name = "wait6", .ret_type = 1, .nargs = 6,
- .args = { { Idtype, 0 }, { Int, 1 }, { ExitStatus | OUT, 2 },
- { Waitoptions, 3 }, { Rusage | OUT, 4 }, { Ptr, 5 } } },
- { .name = "procctl", .ret_type = 1, .nargs = 4,
- .args = { { Idtype, 0 }, { Int, 1 }, { Procctl, 2 }, { Ptr, 3 } } },
- { .name = "_umtx_op", .ret_type = 1, .nargs = 5,
- .args = { { Ptr, 0 }, { Umtxop, 1 }, { LongHex, 2 }, { Ptr, 3 },
- { Ptr, 4 } } },
+ .args = { { Idtype, 0 }, { Quad, 1 + QUAD_ALIGN },
+ { ExitStatus | OUT, 1 + QUAD_ALIGN + QUAD_SLOTS },
+ { Waitoptions, 2 + QUAD_ALIGN + QUAD_SLOTS },
+ { Rusage | OUT, 3 + QUAD_ALIGN + QUAD_SLOTS },
+ { Ptr, 4 + QUAD_ALIGN + QUAD_SLOTS } } },
+ { .name = "write", .ret_type = 1, .nargs = 3,
+ .args = { { Int, 0 }, { BinString | IN, 1 }, { Int, 2 } } },
+
+ /* Linux ABI */
+ { .name = "linux_access", .ret_type = 1, .nargs = 2,
+ .args = { { Name, 0 }, { Accessmode, 1 } } },
+ { .name = "linux_execve", .ret_type = 1, .nargs = 3,
+ .args = { { Name | IN, 0 }, { ExecArgs | IN, 1 },
+ { ExecEnv | IN, 2 } } },
+ { .name = "linux_lseek", .ret_type = 2, .nargs = 3,
+ .args = { { Int, 0 }, { Int, 1 }, { Whence, 2 } } },
+ { .name = "linux_mkdir", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Int, 1 } } },
+ { .name = "linux_newfstat", .ret_type = 1, .nargs = 2,
+ .args = { { Int, 0 }, { Ptr | OUT, 1 } } },
+ { .name = "linux_newstat", .ret_type = 1, .nargs = 2,
+ .args = { { Name | IN, 0 }, { Ptr | OUT, 1 } } },
+ { .name = "linux_open", .ret_type = 1, .nargs = 3,
+ .args = { { Name, 0 }, { Hex, 1 }, { Octal, 2 } } },
+ { .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 = "linux_stat64", .ret_type = 1, .nargs = 3,
+ .args = { { Name | IN, 0 }, { Ptr | OUT, 1 }, { Ptr | IN, 1 } } },
+
{ .name = 0 },
};
+static STAILQ_HEAD(, syscall) syscalls;
/* Xlat idea taken from strace */
struct xlat {
@@ -301,12 +394,37 @@ struct xlat {
static struct xlat kevent_filters[] = {
X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE)
X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER)
- X(EVFILT_FS) X(EVFILT_READ) XEND
+ X(EVFILT_FS) X(EVFILT_LIO) X(EVFILT_USER) XEND
};
static struct xlat kevent_flags[] = {
X(EV_ADD) X(EV_DELETE) X(EV_ENABLE) X(EV_DISABLE) X(EV_ONESHOT)
- X(EV_CLEAR) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND
+ X(EV_CLEAR) X(EV_RECEIPT) X(EV_DISPATCH)
+ X(EV_DROP) X(EV_FLAG1) X(EV_ERROR) X(EV_EOF) XEND
+};
+
+static struct xlat kevent_user_ffctrl[] = {
+ X(NOTE_FFNOP) X(NOTE_FFAND) X(NOTE_FFOR) X(NOTE_FFCOPY)
+ XEND
+};
+
+static struct xlat kevent_rdwr_fflags[] = {
+ X(NOTE_LOWAT) XEND
+};
+
+static struct xlat kevent_vnode_fflags[] = {
+ X(NOTE_DELETE) X(NOTE_WRITE) X(NOTE_EXTEND) X(NOTE_ATTRIB)
+ X(NOTE_LINK) X(NOTE_RENAME) X(NOTE_REVOKE) XEND
+};
+
+static struct xlat kevent_proc_fflags[] = {
+ X(NOTE_EXIT) X(NOTE_FORK) X(NOTE_EXEC) X(NOTE_TRACK) X(NOTE_TRACKERR)
+ X(NOTE_CHILD) XEND
+};
+
+static struct xlat kevent_timer_fflags[] = {
+ X(NOTE_SECONDS) X(NOTE_MSECONDS) X(NOTE_USECONDS) X(NOTE_NSECONDS)
+ XEND
};
static struct xlat poll_flags[] = {
@@ -319,7 +437,7 @@ static struct xlat mmap_flags[] = {
X(MAP_SHARED) X(MAP_PRIVATE) X(MAP_FIXED) X(MAP_RENAME)
X(MAP_NORESERVE) X(MAP_RESERVED0080) X(MAP_RESERVED0100)
X(MAP_HASSEMAPHORE) X(MAP_STACK) X(MAP_NOSYNC) X(MAP_ANON)
- X(MAP_NOCORE) X(MAP_PREFAULT_READ)
+ X(MAP_EXCL) X(MAP_NOCORE) X(MAP_PREFAULT_READ)
#ifdef MAP_32BIT
X(MAP_32BIT)
#endif
@@ -331,7 +449,7 @@ static struct xlat mprot_flags[] = {
};
static struct xlat whence_arg[] = {
- X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) XEND
+ X(SEEK_SET) X(SEEK_CUR) X(SEEK_END) X(SEEK_DATA) X(SEEK_HOLE) XEND
};
static struct xlat sigaction_flags[] = {
@@ -341,7 +459,10 @@ static struct xlat sigaction_flags[] = {
static struct xlat fcntl_arg[] = {
X(F_DUPFD) X(F_GETFD) X(F_SETFD) X(F_GETFL) X(F_SETFL)
- X(F_GETOWN) X(F_SETOWN) X(F_GETLK) X(F_SETLK) X(F_SETLKW) XEND
+ X(F_GETOWN) X(F_SETOWN) X(F_OGETLK) X(F_OSETLK) X(F_OSETLKW)
+ X(F_DUP2FD) X(F_GETLK) X(F_SETLK) X(F_SETLKW) X(F_SETLK_REMOTE)
+ X(F_READAHEAD) X(F_RDAHEAD) X(F_DUPFD_CLOEXEC) X(F_DUP2FD_CLOEXEC)
+ XEND
};
static struct xlat fcntlfd_arg[] = {
@@ -350,7 +471,7 @@ static struct xlat fcntlfd_arg[] = {
static struct xlat fcntlfl_arg[] = {
X(O_APPEND) X(O_ASYNC) X(O_FSYNC) X(O_NONBLOCK) X(O_NOFOLLOW)
- X(O_DIRECT) XEND
+ X(FRDAHEAD) X(O_DIRECT) XEND
};
static struct xlat sockdomain_arg[] = {
@@ -361,7 +482,8 @@ static struct xlat sockdomain_arg[] = {
X(PF_LINK) X(PF_XTP) X(PF_COIP) X(PF_CNT) X(PF_SIP) X(PF_IPX)
X(PF_RTIP) X(PF_PIP) X(PF_ISDN) X(PF_KEY) X(PF_INET6)
X(PF_NATM) X(PF_ATM) X(PF_NETGRAPH) X(PF_SLOW) X(PF_SCLUSTER)
- X(PF_ARP) X(PF_BLUETOOTH) XEND
+ X(PF_ARP) X(PF_BLUETOOTH) X(PF_IEEE80211) X(PF_INET_SDP)
+ X(PF_INET6_SDP) XEND
};
static struct xlat socktype_arg[] = {
@@ -373,7 +495,8 @@ static struct xlat open_flags[] = {
X(O_RDONLY) X(O_WRONLY) X(O_RDWR) X(O_ACCMODE) X(O_NONBLOCK)
X(O_APPEND) X(O_SHLOCK) X(O_EXLOCK) X(O_ASYNC) X(O_FSYNC)
X(O_NOFOLLOW) X(O_CREAT) X(O_TRUNC) X(O_EXCL) X(O_NOCTTY)
- X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC) XEND
+ X(O_DIRECT) X(O_DIRECTORY) X(O_EXEC) X(O_TTY_INIT) X(O_CLOEXEC)
+ XEND
};
static struct xlat shutdown_arg[] = {
@@ -383,7 +506,8 @@ static struct xlat shutdown_arg[] = {
static struct xlat resource_arg[] = {
X(RLIMIT_CPU) X(RLIMIT_FSIZE) X(RLIMIT_DATA) X(RLIMIT_STACK)
X(RLIMIT_CORE) X(RLIMIT_RSS) X(RLIMIT_MEMLOCK) X(RLIMIT_NPROC)
- X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) XEND
+ X(RLIMIT_NOFILE) X(RLIMIT_SBSIZE) X(RLIMIT_VMEM) X(RLIMIT_NPTS)
+ X(RLIMIT_SWAP) XEND
};
static struct xlat pathconf_arg[] = {
@@ -396,12 +520,12 @@ static struct xlat pathconf_arg[] = {
X(_PC_REC_MIN_XFER_SIZE) X(_PC_REC_XFER_ALIGN)
X(_PC_SYMLINK_MAX) X(_PC_ACL_EXTENDED) X(_PC_ACL_PATH_MAX)
X(_PC_CAP_PRESENT) X(_PC_INF_PRESENT) X(_PC_MAC_PRESENT)
- XEND
+ X(_PC_ACL_NFS4) X(_PC_MIN_HOLE_SIZE) XEND
};
static struct xlat rfork_flags[] = {
- X(RFPROC) X(RFNOWAIT) X(RFFDG) X(RFCFDG) X(RFTHREAD) X(RFMEM)
- X(RFSIGSHARE) X(RFTSIGZMB) X(RFLINUXTHPN) XEND
+ X(RFFDG) X(RFPROC) X(RFMEM) X(RFNOWAIT) X(RFCFDG) X(RFTHREAD)
+ X(RFSIGSHARE) X(RFLINUXTHPN) X(RFTSIGZMB) X(RFPPWAIT) XEND
};
static struct xlat wait_options[] = {
@@ -416,7 +540,9 @@ static struct xlat idtype_arg[] = {
};
static struct xlat procctl_arg[] = {
- X(PROC_SPROTECT) XEND
+ X(PROC_SPROTECT) X(PROC_REAP_ACQUIRE) X(PROC_REAP_RELEASE)
+ X(PROC_REAP_STATUS) X(PROC_REAP_GETPIDS) X(PROC_REAP_KILL)
+ X(PROC_TRACE_CTL) X(PROC_TRACE_STATUS) XEND
};
static struct xlat umtx_ops[] = {
@@ -431,6 +557,40 @@ static struct xlat umtx_ops[] = {
XEND
};
+static struct xlat at_flags[] = {
+ X(AT_EACCESS) X(AT_SYMLINK_NOFOLLOW) X(AT_SYMLINK_FOLLOW)
+ X(AT_REMOVEDIR) XEND
+};
+
+static struct xlat access_modes[] = {
+ X(R_OK) X(W_OK) X(X_OK) XEND
+};
+
+static struct xlat sysarch_ops[] = {
+#if defined(__i386__) || defined(__amd64__)
+ X(I386_GET_LDT) X(I386_SET_LDT) X(I386_GET_IOPERM) X(I386_SET_IOPERM)
+ X(I386_VM86) X(I386_GET_FSBASE) X(I386_SET_FSBASE) X(I386_GET_GSBASE)
+ X(I386_SET_GSBASE) X(I386_GET_XFPUSTATE) X(AMD64_GET_FSBASE)
+ X(AMD64_SET_FSBASE) X(AMD64_GET_GSBASE) X(AMD64_SET_GSBASE)
+ X(AMD64_GET_XFPUSTATE)
+#endif
+ XEND
+};
+
+static struct xlat linux_socketcall_ops[] = {
+ X(LINUX_SOCKET) X(LINUX_BIND) X(LINUX_CONNECT) X(LINUX_LISTEN)
+ X(LINUX_ACCEPT) X(LINUX_GETSOCKNAME) X(LINUX_GETPEERNAME)
+ X(LINUX_SOCKETPAIR) X(LINUX_SEND) X(LINUX_RECV) X(LINUX_SENDTO)
+ X(LINUX_RECVFROM) X(LINUX_SHUTDOWN) X(LINUX_SETSOCKOPT)
+ X(LINUX_GETSOCKOPT) X(LINUX_SENDMSG) X(LINUX_RECVMSG)
+ XEND
+};
+
+static struct xlat sigprocmask_ops[] = {
+ X(SIG_BLOCK) X(SIG_UNBLOCK) X(SIG_SETMASK)
+ XEND
+};
+
#undef X
#undef XEND
@@ -470,9 +630,11 @@ xlookup(struct xlat *xlat, int val)
return (lookup(xlat, val, 16));
}
-/* Searches an xlat array containing bitfield values. Remaining bits
- set after removing the known ones are printed at the end:
- IN|0x400 */
+/*
+ * Searches an xlat array containing bitfield values. Remaining bits
+ * set after removing the known ones are printed at the end:
+ * IN|0x400.
+ */
static char *
xlookup_bits(struct xlat *xlat, int val)
{
@@ -483,15 +645,21 @@ xlookup_bits(struct xlat *xlat, int val)
rem = val;
for (; xlat->str != NULL; xlat++) {
if ((xlat->val & rem) == xlat->val) {
- /* don't print the "all-bits-zero" string unless all
- bits are really zero */
+ /*
+ * Don't print the "all-bits-zero" string unless all
+ * bits are really zero.
+ */
if (xlat->val == 0 && val != 0)
continue;
len += sprintf(str + len, "%s|", xlat->str);
rem &= ~(xlat->val);
}
}
- /* if we have leftover bits or didn't match anything */
+
+ /*
+ * If we have leftover bits or didn't match anything, print
+ * the remainder.
+ */
if (rem || len == 0)
len += sprintf(str + len, "0x%x", rem);
if (len && str[len - 1] == '|')
@@ -500,33 +668,54 @@ xlookup_bits(struct xlat *xlat, int val)
return (str);
}
+void
+init_syscalls(void)
+{
+ struct syscall *sc;
+
+ STAILQ_INIT(&syscalls);
+ for (sc = decoded_syscalls; sc->name != NULL; sc++)
+ STAILQ_INSERT_HEAD(&syscalls, sc, entries);
+}
/*
* If/when the list gets big, it might be desirable to do it
* as a hash table or binary search.
*/
-
struct syscall *
-get_syscall(const char *name)
+get_syscall(const char *name, int nargs)
{
struct syscall *sc;
+ int i;
- sc = syscalls;
if (name == NULL)
return (NULL);
- while (sc->name) {
+ STAILQ_FOREACH(sc, &syscalls, entries)
if (strcmp(name, sc->name) == 0)
return (sc);
- sc++;
+
+ /* It is unknown. Add it into the list. */
+#if DEBUG
+ fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name,
+ nargs);
+#endif
+
+ sc = calloc(1, sizeof(struct syscall));
+ sc->name = strdup(name);
+ sc->ret_type = 1;
+ sc->nargs = nargs;
+ for (i = 0; i < nargs; i++) {
+ sc->args[i].offset = i;
+ /* Treat all unknown arguments as LongHex. */
+ sc->args[i].type = LongHex;
}
- return (NULL);
+ STAILQ_INSERT_HEAD(&syscalls, sc, entries);
+
+ return (sc);
}
/*
- * get_struct
- *
* Copy a fixed amount of bytes from the process.
*/
-
static int
get_struct(pid_t pid, void *offset, void *buf, int len)
{
@@ -542,44 +731,55 @@ get_struct(pid_t pid, void *offset, void *buf, int len)
}
#define MAXSIZE 4096
-#define BLOCKSIZE 1024
+
/*
- * get_string
* Copy a string from the process. Note that it is
* expected to be a C string, but if max is set, it will
* only get that much.
*/
-
static char *
-get_string(pid_t pid, void *offset, int max)
+get_string(pid_t pid, void *addr, int max)
{
struct ptrace_io_desc iorequest;
- char *buf;
- int diff, i, size, totalsize;
+ char *buf, *nbuf;
+ size_t offset, size, totalsize;
- diff = 0;
- totalsize = size = max ? (max + 1) : BLOCKSIZE;
+ offset = 0;
+ if (max)
+ size = max + 1;
+ else {
+ /* Read up to the end of the current page. */
+ size = PAGE_SIZE - ((uintptr_t)addr % PAGE_SIZE);
+ if (size > MAXSIZE)
+ size = MAXSIZE;
+ }
+ totalsize = size;
buf = malloc(totalsize);
if (buf == NULL)
return (NULL);
for (;;) {
- diff = totalsize - size;
iorequest.piod_op = PIOD_READ_D;
- iorequest.piod_offs = (char *)offset + diff;
- iorequest.piod_addr = buf + diff;
+ iorequest.piod_offs = (char *)addr + offset;
+ iorequest.piod_addr = buf + offset;
iorequest.piod_len = size;
if (ptrace(PT_IO, pid, (caddr_t)&iorequest, 0) < 0) {
free(buf);
return (NULL);
}
- for (i = 0 ; i < size; i++) {
- if (buf[diff + i] == '\0')
+ if (memchr(buf + offset, '\0', size) != NULL)
+ return (buf);
+ offset += size;
+ if (totalsize < MAXSIZE && max == 0) {
+ size = MAXSIZE - totalsize;
+ if (size > PAGE_SIZE)
+ size = PAGE_SIZE;
+ nbuf = realloc(buf, totalsize + size);
+ if (nbuf == NULL) {
+ buf[totalsize - 1] = '\0';
return (buf);
- }
- if (totalsize < MAXSIZE - BLOCKSIZE && max == 0) {
- totalsize += BLOCKSIZE;
- buf = realloc(buf, totalsize);
- size = BLOCKSIZE;
+ }
+ buf = nbuf;
+ totalsize += size;
} else {
buf[totalsize - 1] = '\0';
return (buf);
@@ -590,72 +790,136 @@ get_string(pid_t pid, void *offset, int max)
static char *
strsig2(int sig)
{
- char *tmp;
+ static char tmp[sizeof(int) * 3 + 1];
+ char *ret;
- tmp = strsig(sig);
- if (tmp == NULL)
- asprintf(&tmp, "%d", sig);
- return (tmp);
+ ret = strsig(sig);
+ if (ret == NULL) {
+ snprintf(tmp, sizeof(tmp), "%d", sig);
+ ret = tmp;
+ }
+ return (ret);
+}
+
+static void
+print_kevent(FILE *fp, struct kevent *ke, int input)
+{
+
+ switch (ke->filter) {
+ case EVFILT_READ:
+ case EVFILT_WRITE:
+ case EVFILT_VNODE:
+ case EVFILT_PROC:
+ case EVFILT_TIMER:
+ fprintf(fp, "%ju", (uintmax_t)ke->ident);
+ break;
+ case EVFILT_SIGNAL:
+ fputs(strsig2(ke->ident), fp);
+ break;
+ default:
+ fprintf(fp, "%p", (void *)ke->ident);
+ }
+ fprintf(fp, ",%s,%s,", xlookup(kevent_filters, ke->filter),
+ xlookup_bits(kevent_flags, ke->flags));
+ switch (ke->filter) {
+ case EVFILT_READ:
+ case EVFILT_WRITE:
+ fputs(xlookup_bits(kevent_rdwr_fflags, ke->fflags), fp);
+ break;
+ case EVFILT_VNODE:
+ fputs(xlookup_bits(kevent_vnode_fflags, ke->fflags), fp);
+ break;
+ case EVFILT_PROC:
+ fputs(xlookup_bits(kevent_proc_fflags, ke->fflags), fp);
+ break;
+ case EVFILT_TIMER:
+ fputs(xlookup_bits(kevent_timer_fflags, ke->fflags), fp);
+ break;
+ case EVFILT_USER: {
+ int ctrl, data;
+
+ ctrl = ke->fflags & NOTE_FFCTRLMASK;
+ data = ke->fflags & NOTE_FFLAGSMASK;
+ if (input) {
+ fputs(xlookup(kevent_user_ffctrl, ctrl), fp);
+ if (ke->fflags & NOTE_TRIGGER)
+ fputs("|NOTE_TRIGGER", fp);
+ if (data != 0)
+ fprintf(fp, "|%#x", data);
+ } else {
+ fprintf(fp, "%#x", data);
+ }
+ break;
+ }
+ default:
+ fprintf(fp, "%#x", ke->fflags);
+ }
+ fprintf(fp, ",%p,%p", (void *)ke->data, (void *)ke->udata);
}
/*
- * print_arg
* Converts a syscall argument into a string. Said string is
- * allocated via malloc(), so needs to be free()'d. The file
- * descriptor is for the process' memory (via /proc), and is used
- * to get any data (where the argument is a pointer). sc is
+ * allocated via malloc(), so needs to be free()'d. sc is
* a pointer to the syscall description (see above); args is
* 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;
char *tmp;
+ size_t tmplen;
pid_t pid;
- tmp = NULL;
- pid = trussinfo->pid;
+ fp = open_memstream(&tmp, &tmplen);
+ pid = trussinfo->curthread->proc->pid;
switch (sc->type & ARG_MASK) {
case Hex:
- asprintf(&tmp, "0x%x", (int)args[sc->offset]);
+ fprintf(fp, "0x%x", (int)args[sc->offset]);
break;
case Octal:
- asprintf(&tmp, "0%o", (int)args[sc->offset]);
+ fprintf(fp, "0%o", (int)args[sc->offset]);
break;
case Int:
- asprintf(&tmp, "%d", (int)args[sc->offset]);
+ fprintf(fp, "%d", (int)args[sc->offset]);
break;
case LongHex:
- asprintf(&tmp, "0x%lx", args[sc->offset]);
- break;
+ fprintf(fp, "0x%lx", args[sc->offset]);
+ break;
+ case Long:
+ fprintf(fp, "%ld", args[sc->offset]);
+ break;
case Name: {
/* NULL-terminated string. */
char *tmp2;
+
tmp2 = get_string(pid, (void*)args[sc->offset], 0);
- asprintf(&tmp, "\"%s\"", tmp2);
+ fprintf(fp, "\"%s\"", tmp2);
free(tmp2);
break;
}
case BinString: {
- /* Binary block of data that might have printable characters.
- XXX If type|OUT, assume that the length is the syscall's
- return value. Otherwise, assume that the length of the block
- is in the next syscall argument. */
+ /*
+ * Binary block of data that might have printable characters.
+ * XXX If type|OUT, assume that the length is the syscall's
+ * return value. Otherwise, assume that the length of the block
+ * is in the next syscall argument.
+ */
int max_string = trussinfo->strsize;
- char tmp2[max_string+1], *tmp3;
+ char tmp2[max_string + 1], *tmp3;
int len;
int truncated = 0;
if (sc->type & OUT)
- len = retval;
+ len = retval[0];
else
len = args[sc->offset + 1];
- /* Don't print more than max_string characters, to avoid word
- wrap. If we have to truncate put some ... after the string.
- */
+ /*
+ * Don't print more than max_string characters, to avoid word
+ * wrap. If we have to truncate put some ... after the string.
+ */
if (len > max_string) {
len = max_string;
truncated = 1;
@@ -670,84 +934,132 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
len--;
truncated = 1;
};
- asprintf(&tmp, "\"%s\"%s", tmp3, truncated ?
+ fprintf(fp, "\"%s\"%s", tmp3, truncated ?
"..." : "");
free(tmp3);
} else {
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
}
break;
}
+ case ExecArgs:
+ case ExecEnv:
case StringArray: {
- int num, size, i;
- char *tmp2;
+ uintptr_t addr;
+ union {
+ char *strarray[0];
+ char buf[PAGE_SIZE];
+ } u;
char *string;
- char *strarray[100]; /* XXX This is ugly. */
-
- if (get_struct(pid, (void *)args[sc->offset],
- (void *)&strarray, sizeof(strarray)) == -1)
- err(1, "get_struct %p", (void *)args[sc->offset]);
- num = 0;
- size = 0;
-
- /* Find out how large of a buffer we'll need. */
- while (strarray[num] != NULL) {
- string = get_string(pid, (void*)strarray[num], 0);
- size += strlen(string);
- free(string);
- num++;
+ size_t len;
+ u_int first, i;
+
+ /*
+ * Only parse argv[] and environment arrays from exec calls
+ * if requested.
+ */
+ if (((sc->type & ARG_MASK) == ExecArgs &&
+ (trussinfo->flags & EXECVEARGS) == 0) ||
+ ((sc->type & ARG_MASK) == ExecEnv &&
+ (trussinfo->flags & EXECVEENVS) == 0)) {
+ fprintf(fp, "0x%lx", args[sc->offset]);
+ break;
+ }
+
+ /*
+ * Read a page of pointers at a time. Punt if the top-level
+ * pointer is not aligned. Note that the first read is of
+ * a partial page.
+ */
+ addr = args[sc->offset];
+ if (addr % sizeof(char *) != 0) {
+ fprintf(fp, "0x%lx", args[sc->offset]);
+ break;
+ }
+
+ len = PAGE_SIZE - (addr & PAGE_MASK);
+ if (get_struct(pid, (void *)addr, u.buf, len) == -1) {
+ fprintf(fp, "0x%lx", args[sc->offset]);
+ break;
}
- size += 4 + (num * 4);
- tmp = (char *)malloc(size);
- tmp2 = tmp;
-
- tmp2 += sprintf(tmp2, " [");
- for (i = 0; i < num; i++) {
- string = get_string(pid, (void*)strarray[i], 0);
- tmp2 += sprintf(tmp2, " \"%s\"%c", string,
- (i + 1 == num) ? ' ' : ',');
+
+ fputc('[', fp);
+ first = 1;
+ i = 0;
+ while (u.strarray[i] != NULL) {
+ string = get_string(pid, u.strarray[i], 0);
+ fprintf(fp, "%s \"%s\"", first ? "" : ",", string);
free(string);
+ first = 0;
+
+ i++;
+ if (i == len / sizeof(char *)) {
+ addr += len;
+ len = PAGE_SIZE;
+ if (get_struct(pid, (void *)addr, u.buf, len) ==
+ -1) {
+ fprintf(fp, ", <inval>");
+ break;
+ }
+ i = 0;
+ }
}
- tmp2 += sprintf(tmp2, "]");
+ fputs(" ]", fp);
break;
}
#ifdef __LP64__
case Quad:
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "%ld", args[sc->offset]);
+ break;
+ case QuadHex:
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
#else
- case Quad: {
+ case Quad:
+ case QuadHex: {
unsigned long long ll;
- ll = *(unsigned long long *)(args + sc->offset);
- asprintf(&tmp, "0x%llx", ll);
+
+#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
+ if ((sc->type & ARG_MASK) == Quad)
+ fprintf(fp, "%lld", ll);
+ else
+ fprintf(fp, "0x%llx", ll);
break;
}
#endif
case Ptr:
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
case Readlinkres: {
char *tmp2;
- if (retval == -1) {
- tmp = strdup("");
+
+ if (retval[0] == -1)
break;
- }
- tmp2 = get_string(pid, (void*)args[sc->offset], retval);
- asprintf(&tmp, "\"%s\"", tmp2);
+ tmp2 = get_string(pid, (void*)args[sc->offset], retval[0]);
+ fprintf(fp, "\"%s\"", tmp2);
free(tmp2);
break;
}
case Ioctl: {
- const char *temp = ioctlname(args[sc->offset]);
+ const char *temp;
+ unsigned long cmd;
+
+ cmd = args[sc->offset];
+ temp = ioctlname(cmd);
if (temp)
- tmp = strdup(temp);
+ fputs(temp, fp);
else {
- unsigned long arg = args[sc->offset];
- asprintf(&tmp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }",
- arg, arg & IOC_OUT ? "R" : "",
- arg & IOC_IN ? "W" : "", IOCGROUP(arg),
- isprint(IOCGROUP(arg)) ? (char)IOCGROUP(arg) : '?',
- arg & 0xFF, IOCPARM_LEN(arg));
+ fprintf(fp, "0x%lx { IO%s%s 0x%lx('%c'), %lu, %lu }",
+ cmd, cmd & IOC_OUT ? "R" : "",
+ cmd & IOC_IN ? "W" : "", IOCGROUP(cmd),
+ isprint(IOCGROUP(cmd)) ? (char)IOCGROUP(cmd) : '?',
+ cmd & 0xFF, IOCPARM_LEN(cmd));
}
break;
}
@@ -755,123 +1067,70 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
struct umtx umtx;
if (get_struct(pid, (void *)args[sc->offset], &umtx,
sizeof(umtx)) != -1)
- asprintf(&tmp, "{ 0x%lx }", (long)umtx.u_owner);
+ fprintf(fp, "{ 0x%lx }", (long)umtx.u_owner);
else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case Timespec: {
struct timespec ts;
+
if (get_struct(pid, (void *)args[sc->offset], &ts,
sizeof(ts)) != -1)
- asprintf(&tmp, "{%ld.%09ld }", (long)ts.tv_sec,
+ fprintf(fp, "{ %jd.%09ld }", (intmax_t)ts.tv_sec,
ts.tv_nsec);
else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case Timeval: {
struct timeval tv;
+
if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
!= -1)
- asprintf(&tmp, "{%ld.%06ld }", (long)tv.tv_sec,
+ fprintf(fp, "{ %jd.%06ld }", (intmax_t)tv.tv_sec,
tv.tv_usec);
else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case Timeval2: {
struct timeval tv[2];
+
if (get_struct(pid, (void *)args[sc->offset], &tv, sizeof(tv))
!= -1)
- asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }",
- (long)tv[0].tv_sec, tv[0].tv_usec,
- (long)tv[1].tv_sec, tv[1].tv_usec);
+ fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
+ (intmax_t)tv[0].tv_sec, tv[0].tv_usec,
+ (intmax_t)tv[1].tv_sec, tv[1].tv_usec);
else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case Itimerval: {
struct itimerval itv;
+
if (get_struct(pid, (void *)args[sc->offset], &itv,
sizeof(itv)) != -1)
- asprintf(&tmp, "{%ld.%06ld, %ld.%06ld }",
- (long)itv.it_interval.tv_sec,
+ fprintf(fp, "{ %jd.%06ld, %jd.%06ld }",
+ (intmax_t)itv.it_interval.tv_sec,
itv.it_interval.tv_usec,
- (long)itv.it_value.tv_sec,
+ (intmax_t)itv.it_value.tv_sec,
itv.it_value.tv_usec);
else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case LinuxSockArgs:
{
struct linux_socketcall_args largs;
- if (get_struct(pid, (void *)args[sc->offset], (void *)&largs,
- sizeof(largs)) == -1) {
- err(1, "get_struct %p", (void *)args[sc->offset]);
- }
- const char *what;
- char buf[30];
- switch (largs.what) {
- case LINUX_SOCKET:
- what = "LINUX_SOCKET";
- break;
- case LINUX_BIND:
- what = "LINUX_BIND";
- break;
- case LINUX_CONNECT:
- what = "LINUX_CONNECT";
- break;
- case LINUX_LISTEN:
- what = "LINUX_LISTEN";
- break;
- case LINUX_ACCEPT:
- what = "LINUX_ACCEPT";
- break;
- case LINUX_GETSOCKNAME:
- what = "LINUX_GETSOCKNAME";
- break;
- case LINUX_GETPEERNAME:
- what = "LINUX_GETPEERNAME";
- break;
- case LINUX_SOCKETPAIR:
- what = "LINUX_SOCKETPAIR";
- break;
- case LINUX_SEND:
- what = "LINUX_SEND";
- break;
- case LINUX_RECV:
- what = "LINUX_RECV";
- break;
- case LINUX_SENDTO:
- what = "LINUX_SENDTO";
- break;
- case LINUX_RECVFROM:
- what = "LINUX_RECVFROM";
- break;
- case LINUX_SHUTDOWN:
- what = "LINUX_SHUTDOWN";
- break;
- case LINUX_SETSOCKOPT:
- what = "LINUX_SETSOCKOPT";
- break;
- case LINUX_GETSOCKOPT:
- what = "LINUX_GETSOCKOPT";
- break;
- case LINUX_SENDMSG:
- what = "LINUX_SENDMSG";
- break;
- case LINUX_RECVMSG:
- what = "LINUX_RECVMSG";
- break;
- default:
- sprintf(buf, "%d", largs.what);
- what = buf;
- break;
- }
- asprintf(&tmp, "(0x%lx)%s, 0x%lx", args[sc->offset], what, (long unsigned int)largs.args);
+ if (get_struct(pid, (void *)args[sc->offset], (void *)&largs,
+ sizeof(largs)) != -1)
+ fprintf(fp, "{ %s, 0x%lx }",
+ lookup(linux_socketcall_ops, largs.what, 10),
+ (long unsigned int)largs.args);
+ else
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case Pollfd: {
@@ -881,35 +1140,23 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
* syscall.
*/
struct pollfd *pfd;
- int numfds = args[sc->offset+1];
- int bytes = sizeof(struct pollfd) * numfds;
- int i, tmpsize, u, used;
- const int per_fd = 100;
+ int numfds = args[sc->offset + 1];
+ size_t bytes = sizeof(struct pollfd) * numfds;
+ int i;
if ((pfd = malloc(bytes)) == NULL)
- err(1, "Cannot malloc %d bytes for pollfd array",
+ err(1, "Cannot malloc %zu bytes for pollfd array",
bytes);
if (get_struct(pid, (void *)args[sc->offset], pfd, bytes)
!= -1) {
- used = 0;
- tmpsize = 1 + per_fd * numfds + 2;
- if ((tmp = malloc(tmpsize)) == NULL)
- err(1, "Cannot alloc %d bytes for poll output",
- tmpsize);
-
- tmp[used++] = '{';
+ fputs("{", fp);
for (i = 0; i < numfds; i++) {
-
- u = snprintf(tmp + used, per_fd, "%s%d/%s",
- i > 0 ? " " : "", pfd[i].fd,
+ fprintf(fp, " %d/%s", pfd[i].fd,
xlookup_bits(poll_flags, pfd[i].events));
- if (u > 0)
- used += u < per_fd ? u : per_fd;
}
- tmp[used++] = '}';
- tmp[used++] = '\0';
+ fputs(" }", fp);
} else {
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
}
free(pfd);
break;
@@ -922,114 +1169,86 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
*/
fd_set *fds;
int numfds = args[0];
- int bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
- int i, tmpsize, u, used;
- const int per_fd = 20;
+ size_t bytes = _howmany(numfds, _NFDBITS) * _NFDBITS;
+ int i;
if ((fds = malloc(bytes)) == NULL)
- err(1, "Cannot malloc %d bytes for fd_set array",
+ err(1, "Cannot malloc %zu bytes for fd_set array",
bytes);
if (get_struct(pid, (void *)args[sc->offset], fds, bytes)
!= -1) {
- used = 0;
- tmpsize = 1 + numfds * per_fd + 2;
- if ((tmp = malloc(tmpsize)) == NULL)
- err(1, "Cannot alloc %d bytes for fd_set "
- "output", tmpsize);
-
- tmp[used++] = '{';
+ fputs("{", fp);
for (i = 0; i < numfds; i++) {
- if (FD_ISSET(i, fds)) {
- u = snprintf(tmp + used, per_fd, "%d ",
- i);
- if (u > 0)
- used += u < per_fd ? u : per_fd;
- }
+ if (FD_ISSET(i, fds))
+ fprintf(fp, " %d", i);
}
- if (tmp[used-1] == ' ')
- used--;
- tmp[used++] = '}';
- tmp[used++] = '\0';
+ fputs(" }", fp);
} else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
free(fds);
break;
}
case Signal:
- tmp = strsig2(args[sc->offset]);
+ fputs(strsig2(args[sc->offset]), fp);
break;
case Sigset: {
long sig;
sigset_t ss;
- int i, used;
- char *signame;
+ int i, first;
sig = args[sc->offset];
if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
sizeof(ss)) == -1) {
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
- tmp = malloc(sys_nsig * 8); /* 7 bytes avg per signal name */
- used = 0;
+ fputs("{ ", fp);
+ first = 1;
for (i = 1; i < sys_nsig; i++) {
if (sigismember(&ss, i)) {
- signame = strsig(i);
- used += sprintf(tmp + used, "%s|", signame);
- free(signame);
+ fprintf(fp, "%s%s", !first ? "|" : "",
+ strsig(i));
+ first = 0;
}
}
- if (used)
- tmp[used-1] = 0;
- else
- strcpy(tmp, "0x0");
+ if (!first)
+ fputc(' ', fp);
+ fputc('}', fp);
break;
}
case Sigprocmask: {
- switch (args[sc->offset]) {
-#define S(a) case a: tmp = strdup(#a); break;
- S(SIG_BLOCK);
- S(SIG_UNBLOCK);
- S(SIG_SETMASK);
-#undef S
- }
- if (tmp == NULL)
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fputs(xlookup(sigprocmask_ops, args[sc->offset]), fp);
break;
}
case Fcntlflag: {
- /* XXX output depends on the value of the previous argument */
- switch (args[sc->offset-1]) {
+ /* XXX: Output depends on the value of the previous argument. */
+ switch (args[sc->offset - 1]) {
case F_SETFD:
- tmp = strdup(xlookup_bits(fcntlfd_arg,
- args[sc->offset]));
+ fputs(xlookup_bits(fcntlfd_arg, args[sc->offset]), fp);
break;
case F_SETFL:
- tmp = strdup(xlookup_bits(fcntlfl_arg,
- args[sc->offset]));
+ fputs(xlookup_bits(fcntlfl_arg, args[sc->offset]), fp);
break;
case F_GETFD:
case F_GETFL:
case F_GETOWN:
- tmp = strdup("");
break;
default:
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
break;
}
case Open:
- tmp = strdup(xlookup_bits(open_flags, args[sc->offset]));
+ fputs(xlookup_bits(open_flags, args[sc->offset]), fp);
break;
case Fcntl:
- tmp = strdup(xlookup(fcntl_arg, args[sc->offset]));
+ fputs(xlookup(fcntl_arg, args[sc->offset]), fp);
break;
case Mprot:
- tmp = strdup(xlookup_bits(mprot_flags, args[sc->offset]));
+ fputs(xlookup_bits(mprot_flags, args[sc->offset]), fp);
break;
case Mmapflags: {
- char *base, *alignstr;
int align, flags;
/*
@@ -1043,285 +1262,332 @@ print_arg(struct syscall_args *sc, unsigned long *args, long retval,
align = args[sc->offset] & MAP_ALIGNMENT_MASK;
if (align != 0) {
if (align == MAP_ALIGNED_SUPER)
- alignstr = strdup("MAP_ALIGNED_SUPER");
+ fputs("MAP_ALIGNED_SUPER", fp);
else
- asprintf(&alignstr, "MAP_ALIGNED(%d)",
+ fprintf(fp, "MAP_ALIGNED(%d)",
align >> MAP_ALIGNMENT_SHIFT);
- if (flags == 0) {
- tmp = alignstr;
+ if (flags == 0)
break;
- }
- } else
- alignstr = NULL;
- base = strdup(xlookup_bits(mmap_flags, flags));
- if (alignstr == NULL) {
- tmp = base;
- break;
+ fputc('|', fp);
}
- asprintf(&tmp, "%s|%s", alignstr, base);
- free(alignstr);
- free(base);
+ fputs(xlookup_bits(mmap_flags, flags), fp);
break;
}
case Whence:
- tmp = strdup(xlookup(whence_arg, args[sc->offset]));
+ fputs(xlookup(whence_arg, args[sc->offset]), fp);
break;
case Sockdomain:
- tmp = strdup(xlookup(sockdomain_arg, args[sc->offset]));
+ fputs(xlookup(sockdomain_arg, args[sc->offset]), fp);
break;
- case Socktype:
- tmp = strdup(xlookup(socktype_arg, args[sc->offset]));
+ case Socktype: {
+ int type, flags;
+
+ flags = args[sc->offset] & (SOCK_CLOEXEC | SOCK_NONBLOCK);
+ type = args[sc->offset] & ~flags;
+ fputs(xlookup(socktype_arg, type), fp);
+ if (flags & SOCK_CLOEXEC)
+ fprintf(fp, "|SOCK_CLOEXEC");
+ if (flags & SOCK_NONBLOCK)
+ fprintf(fp, "|SOCK_NONBLOCK");
break;
+ }
case Shutdown:
- tmp = strdup(xlookup(shutdown_arg, args[sc->offset]));
+ fputs(xlookup(shutdown_arg, args[sc->offset]), fp);
break;
case Resource:
- tmp = strdup(xlookup(resource_arg, args[sc->offset]));
+ fputs(xlookup(resource_arg, args[sc->offset]), fp);
break;
case Pathconf:
- tmp = strdup(xlookup(pathconf_arg, args[sc->offset]));
+ fputs(xlookup(pathconf_arg, args[sc->offset]), fp);
break;
case Rforkflags:
- tmp = strdup(xlookup_bits(rfork_flags, args[sc->offset]));
+ fputs(xlookup_bits(rfork_flags, args[sc->offset]), fp);
break;
case Sockaddr: {
- struct sockaddr_storage ss;
char addr[64];
struct sockaddr_in *lsin;
struct sockaddr_in6 *lsin6;
struct sockaddr_un *sun;
struct sockaddr *sa;
- char *p;
+ socklen_t len;
u_char *q;
- int i;
if (args[sc->offset] == 0) {
- asprintf(&tmp, "NULL");
+ fputs("NULL", fp);
break;
}
- /* yuck: get ss_len */
- if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
- sizeof(ss.ss_len) + sizeof(ss.ss_family)) == -1)
- err(1, "get_struct %p", (void *)args[sc->offset]);
/*
- * If ss_len is 0, then try to guess from the sockaddr type.
- * AF_UNIX may be initialized incorrectly, so always frob
- * it by using the "right" size.
+ * Extract the address length from the next argument. If
+ * this is an output sockaddr (OUT is set), then the
+ * next argument is a pointer to a socklen_t. Otherwise
+ * the next argument contains a socklen_t by value.
*/
- if (ss.ss_len == 0 || ss.ss_family == AF_UNIX) {
- switch (ss.ss_family) {
- case AF_INET:
- ss.ss_len = sizeof(*lsin);
- break;
- case AF_UNIX:
- ss.ss_len = sizeof(*sun);
- break;
- default:
- /* hurrrr */
+ if (sc->type & OUT) {
+ if (get_struct(pid, (void *)args[sc->offset + 1],
+ &len, sizeof(len)) == -1) {
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
+ } else
+ len = args[sc->offset + 1];
+
+ /* If the length is too small, just bail. */
+ if (len < sizeof(*sa)) {
+ fprintf(fp, "0x%lx", args[sc->offset]);
+ break;
}
- if (get_struct(pid, (void *)args[sc->offset], (void *)&ss,
- ss.ss_len) == -1) {
- err(2, "get_struct %p", (void *)args[sc->offset]);
+
+ sa = calloc(1, len);
+ if (get_struct(pid, (void *)args[sc->offset], sa, len) == -1) {
+ free(sa);
+ fprintf(fp, "0x%lx", args[sc->offset]);
+ break;
}
- switch (ss.ss_family) {
+ switch (sa->sa_family) {
case AF_INET:
- lsin = (struct sockaddr_in *)&ss;
- inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof addr);
- asprintf(&tmp, "{ AF_INET %s:%d }", addr,
+ if (len < sizeof(*lsin))
+ goto sockaddr_short;
+ lsin = (struct sockaddr_in *)(void *)sa;
+ inet_ntop(AF_INET, &lsin->sin_addr, addr, sizeof(addr));
+ fprintf(fp, "{ AF_INET %s:%d }", addr,
htons(lsin->sin_port));
break;
case AF_INET6:
- lsin6 = (struct sockaddr_in6 *)&ss;
+ if (len < sizeof(*lsin6))
+ goto sockaddr_short;
+ lsin6 = (struct sockaddr_in6 *)(void *)sa;
inet_ntop(AF_INET6, &lsin6->sin6_addr, addr,
- sizeof addr);
- asprintf(&tmp, "{ AF_INET6 [%s]:%d }", addr,
+ sizeof(addr));
+ fprintf(fp, "{ AF_INET6 [%s]:%d }", addr,
htons(lsin6->sin6_port));
break;
case AF_UNIX:
- sun = (struct sockaddr_un *)&ss;
- asprintf(&tmp, "{ AF_UNIX \"%s\" }", sun->sun_path);
+ sun = (struct sockaddr_un *)sa;
+ fprintf(fp, "{ AF_UNIX \"%.*s\" }",
+ (int)(len - offsetof(struct sockaddr_un, sun_path)),
+ sun->sun_path);
break;
default:
- sa = (struct sockaddr *)&ss;
- asprintf(&tmp, "{ sa_len = %d, sa_family = %d, sa_data "
- "= {%n%*s } }", (int)sa->sa_len, (int)sa->sa_family,
- &i, 6 * (int)(sa->sa_len - ((char *)&sa->sa_data -
- (char *)sa)), "");
- if (tmp != NULL) {
- p = tmp + i;
- for (q = (u_char *)&sa->sa_data;
- q < (u_char *)sa + sa->sa_len; q++)
- p += sprintf(p, " %#02x,", *q);
- }
+ sockaddr_short:
+ fprintf(fp,
+ "{ sa_len = %d, sa_family = %d, sa_data = {",
+ (int)sa->sa_len, (int)sa->sa_family);
+ for (q = (u_char *)sa->sa_data;
+ q < (u_char *)sa + len; q++)
+ fprintf(fp, "%s 0x%02x",
+ q == (u_char *)sa->sa_data ? "" : ",",
+ *q);
+ fputs(" } }", fp);
}
+ free(sa);
break;
}
case Sigaction: {
struct sigaction sa;
- char *hand;
- const char *h;
if (get_struct(pid, (void *)args[sc->offset], &sa, sizeof(sa))
!= -1) {
- asprintf(&hand, "%p", sa.sa_handler);
+ fputs("{ ", fp);
if (sa.sa_handler == SIG_DFL)
- h = "SIG_DFL";
+ fputs("SIG_DFL", fp);
else if (sa.sa_handler == SIG_IGN)
- h = "SIG_IGN";
+ fputs("SIG_IGN", fp);
else
- h = hand;
-
- asprintf(&tmp, "{ %s %s ss_t }", h,
+ fprintf(fp, "%p", sa.sa_handler);
+ fprintf(fp, " %s ss_t }",
xlookup_bits(sigaction_flags, sa.sa_flags));
- free(hand);
} else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case Kevent: {
/*
- * XXX XXX: the size of the array is determined by either the
- * next syscall argument, or by the syscall returnvalue,
+ * XXX XXX: The size of the array is determined by either the
+ * next syscall argument, or by the syscall return value,
* depending on which argument number we are. This matches the
* kevent syscall, but luckily that's the only syscall that uses
* them.
*/
struct kevent *ke;
int numevents = -1;
- int bytes = 0;
- int i, tmpsize, u, used;
- const int per_ke = 100;
+ size_t bytes;
+ int i;
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)
+ if (numevents >= 0) {
bytes = sizeof(struct kevent) * numevents;
- if ((ke = malloc(bytes)) == NULL)
- err(1, "Cannot malloc %d bytes for kevent array",
- bytes);
+ if ((ke = malloc(bytes)) == NULL)
+ err(1,
+ "Cannot malloc %zu bytes for kevent array",
+ bytes);
+ } else
+ ke = NULL;
if (numevents >= 0 && get_struct(pid, (void *)args[sc->offset],
ke, bytes) != -1) {
- used = 0;
- tmpsize = 1 + per_ke * numevents + 2;
- if ((tmp = malloc(tmpsize)) == NULL)
- err(1, "Cannot alloc %d bytes for kevent "
- "output", tmpsize);
-
- tmp[used++] = '{';
+ fputc('{', fp);
for (i = 0; i < numevents; i++) {
- u = snprintf(tmp + used, per_ke,
- "%s%p,%s,%s,%d,%p,%p",
- i > 0 ? " " : "",
- (void *)ke[i].ident,
- xlookup(kevent_filters, ke[i].filter),
- xlookup_bits(kevent_flags, ke[i].flags),
- ke[i].fflags,
- (void *)ke[i].data,
- (void *)ke[i].udata);
- if (u > 0)
- used += u < per_ke ? u : per_ke;
+ fputc(' ', fp);
+ print_kevent(fp, &ke[i], sc->offset == 1);
}
- tmp[used++] = '}';
- tmp[used++] = '\0';
+ fputs(" }", fp);
} else {
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
}
free(ke);
break;
}
case Stat: {
struct stat st;
+
if (get_struct(pid, (void *)args[sc->offset], &st, sizeof(st))
!= -1) {
char mode[12];
+
strmode(st.st_mode, mode);
- asprintf(&tmp,
- "{ mode=%s,inode=%jd,size=%jd,blksize=%ld }", mode,
- (intmax_t)st.st_ino, (intmax_t)st.st_size,
+ fprintf(fp,
+ "{ mode=%s,inode=%ju,size=%jd,blksize=%ld }", mode,
+ (uintmax_t)st.st_ino, (intmax_t)st.st_size,
(long)st.st_blksize);
} else {
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
}
break;
}
+ case StatFs: {
+ unsigned int i;
+ struct statfs buf;
+
+ if (get_struct(pid, (void *)args[sc->offset], &buf,
+ sizeof(buf)) != -1) {
+ char fsid[17];
+
+ bzero(fsid, sizeof(fsid));
+ if (buf.f_fsid.val[0] != 0 || buf.f_fsid.val[1] != 0) {
+ for (i = 0; i < sizeof(buf.f_fsid); i++)
+ snprintf(&fsid[i*2],
+ sizeof(fsid) - (i*2), "%02x",
+ ((u_char *)&buf.f_fsid)[i]);
+ }
+ fprintf(fp,
+ "{ fstypename=%s,mntonname=%s,mntfromname=%s,"
+ "fsid=%s }", buf.f_fstypename, buf.f_mntonname,
+ buf.f_mntfromname, fsid);
+ } else
+ fprintf(fp, "0x%lx", args[sc->offset]);
+ break;
+ }
+
case Rusage: {
struct rusage ru;
+
if (get_struct(pid, (void *)args[sc->offset], &ru, sizeof(ru))
!= -1) {
- asprintf(&tmp,
- "{ u=%ld.%06ld,s=%ld.%06ld,in=%ld,out=%ld }",
- (long)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
- (long)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
+ fprintf(fp,
+ "{ u=%jd.%06ld,s=%jd.%06ld,in=%ld,out=%ld }",
+ (intmax_t)ru.ru_utime.tv_sec, ru.ru_utime.tv_usec,
+ (intmax_t)ru.ru_stime.tv_sec, ru.ru_stime.tv_usec,
ru.ru_inblock, ru.ru_oublock);
} else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case Rlimit: {
struct rlimit rl;
+
if (get_struct(pid, (void *)args[sc->offset], &rl, sizeof(rl))
!= -1) {
- asprintf(&tmp, "{ cur=%ju,max=%ju }",
+ fprintf(fp, "{ cur=%ju,max=%ju }",
rl.rlim_cur, rl.rlim_max);
} else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case ExitStatus: {
- char *signame;
int status;
- signame = NULL;
+
if (get_struct(pid, (void *)args[sc->offset], &status,
sizeof(status)) != -1) {
+ fputs("{ ", fp);
if (WIFCONTINUED(status))
- tmp = strdup("{ CONTINUED }");
+ fputs("CONTINUED", fp);
else if (WIFEXITED(status))
- asprintf(&tmp, "{ EXITED,val=%d }",
+ fprintf(fp, "EXITED,val=%d",
WEXITSTATUS(status));
else if (WIFSIGNALED(status))
- asprintf(&tmp, "{ SIGNALED,sig=%s%s }",
- signame = strsig2(WTERMSIG(status)),
+ fprintf(fp, "SIGNALED,sig=%s%s",
+ strsig2(WTERMSIG(status)),
WCOREDUMP(status) ? ",cored" : "");
else
- asprintf(&tmp, "{ STOPPED,sig=%s }",
- signame = strsig2(WTERMSIG(status)));
+ fprintf(fp, "STOPPED,sig=%s",
+ strsig2(WTERMSIG(status)));
+ fputs(" }", fp);
} else
- asprintf(&tmp, "0x%lx", args[sc->offset]);
- free(signame);
+ fprintf(fp, "0x%lx", args[sc->offset]);
break;
}
case Waitoptions:
- tmp = strdup(xlookup_bits(wait_options, args[sc->offset]));
+ fputs(xlookup_bits(wait_options, args[sc->offset]), fp);
break;
case Idtype:
- tmp = strdup(xlookup(idtype_arg, args[sc->offset]));
+ fputs(xlookup(idtype_arg, args[sc->offset]), fp);
break;
case Procctl:
- tmp = strdup(xlookup(procctl_arg, args[sc->offset]));
+ fputs(xlookup(procctl_arg, args[sc->offset]), fp);
break;
case Umtxop:
- tmp = strdup(xlookup(umtx_ops, args[sc->offset]));
+ fputs(xlookup(umtx_ops, args[sc->offset]), fp);
+ break;
+ case Atfd:
+ if ((int)args[sc->offset] == AT_FDCWD)
+ fputs("AT_FDCWD", fp);
+ else
+ fprintf(fp, "%d", (int)args[sc->offset]);
+ break;
+ case Atflags:
+ fputs(xlookup_bits(at_flags, args[sc->offset]), fp);
+ break;
+ case Accessmode:
+ if (args[sc->offset] == F_OK)
+ fputs("F_OK", fp);
+ else
+ fputs(xlookup_bits(access_modes, args[sc->offset]), fp);
+ break;
+ 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);
}
+ fclose(fp);
return (tmp);
}
/*
- * print_syscall
* Print (to outfile) the system call and its arguments. Note that
* nargs is the number of arguments (not the number of words; this is
* potentially confusing, I know).
*/
-
void
print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
char **s_args)
@@ -1331,7 +1597,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)) {
@@ -1341,15 +1608,15 @@ print_syscall(struct trussinfo *trussinfo, const char *name, int nargs,
if (trussinfo->flags & ABSOLUTETIMESTAMPS) {
timespecsubt(&trussinfo->curthread->after,
&trussinfo->start_time, &timediff);
- len += fprintf(trussinfo->outfile, "%ld.%09ld ",
- (long)timediff.tv_sec, timediff.tv_nsec);
+ len += 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);
- len += fprintf(trussinfo->outfile, "%ld.%09ld ",
- (long)timediff.tv_sec, timediff.tv_nsec);
+ len += fprintf(trussinfo->outfile, "%jd.%09ld ",
+ (intmax_t)timediff.tv_sec, timediff.tv_nsec);
}
len += fprintf(trussinfo->outfile, "%s(", name);
@@ -1370,13 +1637,11 @@ 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;
if (trussinfo->flags & COUNTONLY) {
- if (!sc)
- return;
clock_gettime(CLOCK_REALTIME, &trussinfo->curthread->after);
timespecsubt(&trussinfo->curthread->after,
&trussinfo->curthread->before, &timediff);
@@ -1390,17 +1655,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->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
@@ -1413,7 +1685,7 @@ print_summary(struct trussinfo *trussinfo)
fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
"syscall", "seconds", "calls", "errors");
ncall = nerror = 0;
- for (sc = syscalls; sc->name != NULL; sc++)
+ STAILQ_FOREACH(sc, &syscalls, entries)
if (sc->ncalls) {
fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
sc->name, (intmax_t)sc->time.tv_sec,
diff --git a/usr.bin/truss/truss.1 b/usr.bin/truss/truss.1
index 6a01451..b5a1cfe 100644
--- a/usr.bin/truss/truss.1
+++ b/usr.bin/truss/truss.1
@@ -1,6 +1,6 @@
.\" $FreeBSD$
.\"
-.Dd May 12, 2009
+.Dd October 9, 2015
.Dt TRUSS 1
.Os
.Sh NAME
@@ -37,7 +37,7 @@ Show the argument strings that are passed in each
.Xr execve 2
system call.
.It Fl c
-Do not display individual system calls.
+Do not display individual system calls or signals.
Instead, before exiting, print a summary containing for each system call:
the total system time used,
the number of times the call was invoked,
diff --git a/usr.bin/truss/truss.h b/usr.bin/truss/truss.h
index dcc86a5..5a57de7 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. syscall.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