Truss program. Requires procfs.

This commit is contained in:
Sean Eric Fagan 1997-12-06 05:23:12 +00:00
parent a367d57578
commit bbeaf6c0c9
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=31567
12 changed files with 1667 additions and 0 deletions

23
usr.bin/truss/Makefile Normal file
View File

@ -0,0 +1,23 @@
PROG= truss
SRCS= main.c setup.c i386-fbsd.c i386-linux.c \
syscalls.c linux_syscalls.h syscalls.h
CFLAGS+= -I${.CURDIR} -I.
CLEANFILES+=i386l-syscalls.master syscalls.master linux_syscalls.h syscalls.h
.SUFFIXES: .master
i386l-syscalls.master: ${.CURDIR}/../../sys/i386/linux/syscalls.master
cp ${.ALLSRC} i386l-syscalls.master
linux_syscalls.h: i386l-syscalls.master
/bin/sh ${.CURDIR}/../../sys/kern/makesyscalls.sh i386l-syscalls.master \
${.CURDIR}/i386linux.conf
syscalls.master: ${.CURDIR}/../../sys/kern/syscalls.master
cp ${.ALLSRC} syscalls.master
syscalls.h: syscalls.master
/bin/sh ${.CURDIR}/../../sys/kern/makesyscalls.sh syscalls.master \
${.CURDIR}/i386.conf
.include <bsd.prog.mk>

View File

@ -0,0 +1,281 @@
/*
* FreeBSD/386-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.
*/
/*
* $Id$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/pioctl.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <sys/syscall.h>
#include "syscall.h"
static int fd = -1;
static int cpid = -1;
extern int Procfd;
extern FILE *outfile;
#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.
*/
static struct freebsd_syscall {
struct syscall *sc;
char *name;
int number;
unsigned long *args;
int nargs; /* number of arguments -- *not* number of words! */
char **s_args; /* the printable arguments */
} fsc;
/* Clear up and free parts of the fsc structure. */
static inline void
clear_fsc() {
if (fsc.args) {
free(fsc.args);
}
if (fsc.s_args) {
int i;
for (i = 0; i < fsc.nargs; i++)
if (fsc.s_args[i])
free(fsc.s_args[i]);
free(fsc.s_args);
}
memset(&fsc, 0, sizeof(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(int pid, int nargs) {
char buf[32];
struct reg regs = { 0 };
int syscall;
int i;
int memfd;
unsigned int parm_offset;
struct syscall *sc;
if (fd == -1 || pid != cpid) {
sprintf(buf, "/proc/%d/regs", pid);
fd = open(buf, O_RDWR);
if (fd == -1) {
fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
return;
}
cpid = pid;
}
clear_fsc();
lseek(fd, 0L, 0);
i = read(fd, &regs, sizeof(regs));
parm_offset = regs.r_esp + sizeof(int);
/*
* FreeBSD has two special kinds of system call redirctions --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basicly; the latter is for quad-aligned arguments.
*/
syscall = regs.r_eax;
switch (syscall) {
case SYS_syscall:
lseek(Procfd, parm_offset, SEEK_SET);
read(Procfd, &syscall, sizeof(int));
parm_offset += sizeof(int);
break;
case SYS___syscall:
lseek(Procfd, parm_offset, SEEK_SET);
read(Procfd, &syscall, sizeof(int));
parm_offset += sizeof(quad_t);
break;
}
fsc.number = syscall;
fsc.name =
(syscall < 0 || syscall > nsyscalls) ? NULL : syscallnames[syscall];
if (!fsc.name) {
fprintf(outfile, "-- UNKNOWN SYSCALL %d --\n", syscall);
}
if (nargs == 0)
return;
fsc.args = malloc((1+nargs) * sizeof(unsigned long));
lseek(Procfd, parm_offset, SEEK_SET);
if (read(Procfd, fsc.args, nargs * sizeof(unsigned long)) == -1)
return;
sc = get_syscall(fsc.name);
if (sc) {
fsc.nargs = sc->nargs;
} else {
#if DEBUG
fprintf(outfile, "unknown syscall %s -- setting args to %d\n",
fsc.name, nargs);
#endif
fsc.nargs = nargs;
}
fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*));
memset(fsc.s_args, 0, 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) {
char *tmp;
#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(Procfd, &sc->args[i], fsc.args);
}
}
#if DEBUG
fprintf(stderr, ")\n");
#endif
}
#if DEBUG
fprintf(outfile, "\n");
#endif
/*
* Some system calls should be printed out before they are done --
* execve() and exit(), for example, never return. Possibly change
* this to work for any system call that doesn't have an OUT
* parameter?
*/
if (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit")) {
print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args);
}
return;
}
/*
* 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 sytem call number instead of, say, an error status).
*/
void
i386_syscall_exit(int pid, int syscall) {
char buf[32];
struct reg regs;
int retval;
int i;
int errorp;
struct syscall *sc;
if (fd == -1 || pid != cpid) {
sprintf(buf, "/proc/%d/regs", pid);
fd = open(buf, O_RDONLY);
if (fd == -1) {
fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
return;
}
cpid = pid;
}
lseek(fd, 0L, 0);
if (read(fd, &regs, sizeof(regs)) != sizeof(regs))
return;
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.
*/
sc = fsc.sc;
if (!sc) {
for (i = 0; i < fsc.nargs; i++) {
fsc.s_args[i] = malloc(12);
sprintf(fsc.s_args[i], "0x%x", 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, than don't bothe getting the data;
* it may not be valid.
*/
if (errorp) {
temp = malloc(12);
sprintf(temp, "0x%x", fsc.args[sc->args[i].offset]);
} else {
temp = print_arg(Procfd, &sc->args[i], fsc.args);
}
fsc.s_args[i] = temp;
}
}
}
/*
* It would probably be a good idea to merge the error handling,
* but that complicates things considerably.
*/
print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args);
if (errorp) {
fprintf(outfile, "errno %d '%s'\n", retval, strerror(retval));
} else {
fprintf(outfile, "returns %d (0x%x)\n", retval, retval);
}
clear_fsc();
return;
}

View File

@ -0,0 +1,221 @@
/*
* 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.
*/
/*
* $Id$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/pioctl.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include "syscall.h"
static int fd = -1;
static int cpid = -1;
extern int Procfd;
extern FILE *outfile;
#include "linux_syscalls.h"
static int nsyscalls =
sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]);
/* See the comment in i386-fbsd.c about this structure. */
static struct linux_syscall {
struct syscall *sc;
char *name;
int number;
unsigned long args[5];
int nargs; /* number of arguments -- *not* number of words! */
char **s_args; /* the printable arguments */
} lsc;
static inline void
clear_lsc() {
if (lsc.s_args) {
int i;
for (i = 0; i < lsc.nargs; i++)
if (lsc.s_args[i])
free(lsc.s_args[i]);
free(lsc.s_args);
}
memset(&lsc, 0, sizeof(lsc));
}
void
i386_linux_syscall_entry(int pid, int nargs) {
char buf[32];
struct reg regs = { 0 };
int syscall;
int i;
int memfd;
struct syscall *sc;
if (fd == -1 || pid != cpid) {
sprintf(buf, "/proc/%d/regs", pid);
fd = open(buf, O_RDWR);
if (fd == -1) {
fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
return;
}
cpid = pid;
}
clear_lsc();
lseek(fd, 0L, 0);
i = read(fd, &regs, sizeof(regs));
syscall = regs.r_eax;
lsc.number = syscall;
lsc.name =
(syscall < 0 || syscall > nsyscalls) ? NULL : linux_syscallnames[syscall];
if (!lsc.name) {
fprintf (outfile, "-- UNKNOWN SYSCALL %d\n", syscall);
}
if (nargs == 0)
return;
/*
* Linux passes syscall arguments in registers, not
* on the stack. Fortunately, we've got access to the
* register set. Note that we don't bother checking the
* number of arguments. And what does linux do for syscalls
* that have more than five arguments?
*/
lsc.args[0] = regs.r_ebx;
lsc.args[1] = regs.r_ecx;
lsc.args[2] = regs.r_edx;
lsc.args[3] = regs.r_esi;
lsc.args[4] = regs.r_edi;
sc = get_syscall(lsc.name);
if (sc) {
lsc.nargs = sc->nargs;
} else {
#ifdef DEBUG
fprintf(outfile, "unknown syscall %s -- setting args to %d\n",
lsc.name, nargs);
#endif
lsc.nargs = nargs;
}
lsc.s_args = malloc((1+lsc.nargs) * sizeof(char*));
memset(lsc.s_args, 0, lsc.nargs * sizeof(char*));
lsc.sc = sc;
if (lsc.name) {
char *tmp;
#ifdef DEBUG
fprintf(stderr, "syscall %s(", lsc.name);
#endif
for (i = 0; i < lsc.nargs ; i++) {
#ifdef DEBUG
fprintf(stderr, "0x%x%s",
sc ?
lsc.args[sc->args[i].offset]
: lsc.args[i],
i < (lsc.nargs - 1) ? "," : "");
#endif
if (sc && !(sc->args[i].type & OUT)) {
lsc.s_args[i] = print_arg(Procfd, &sc->args[i], lsc.args);
}
}
#ifdef DEBUG
fprintf(stderr, ")\n");
#endif
}
if (!strcmp(lsc.name, "linux_execve") || !strcmp(lsc.name, "exit")) {
print_syscall(outfile, lsc.name, lsc.nargs, lsc.s_args);
}
return;
}
/*
* Linux syscalls return negative errno's, we do positive and map them
*/
int bsd_to_linux_errno[] = {
-0, -1, -2, -3, -4, -5, -6, -7, -8, -9,
-10, -35, -12, -13, -14, -15, -16, -17, -18, -19,
-20, -21, -22, -23, -24, -25, -26, -27, -28, -29,
-30, -31, -32, -33, -34, -11,-115,-114, -88, -89,
-90, -91, -92, -93, -94, -95, -96, -97, -98, -99,
-100,-101,-102,-103,-104,-105,-106,-107,-108,-109,
-110,-111, -40, -36,-112,-113, -39, -11, -87,-122,
-116, -66, -6, -6, -6, -6, -6, -37, -38, -9,
-6,
};
void
i386_linux_syscall_exit(int pid, int syscall) {
char buf[32];
struct reg regs;
int retval;
int i;
int errorp;
struct syscall *sc;
if (fd == -1 || pid != cpid) {
sprintf(buf, "/proc/%d/regs", pid);
fd = open(buf, O_RDONLY);
if (fd == -1) {
fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
return;
}
cpid = pid;
}
lseek(fd, 0L, 0);
if (read(fd, &regs, sizeof(regs)) != sizeof(regs))
return;
retval = regs.r_eax;
errorp = !!(regs.r_eflags & PSL_C);
sc = lsc.sc;
if (!sc) {
for (i = 0; i < lsc.nargs; i++) {
lsc.s_args[i] = malloc(12);
sprintf(lsc.s_args[i], "0x%x", lsc.args[i]);
}
} else {
for (i = 0; i < sc->nargs; i++) {
char *temp;
if (sc->args[i].type & OUT) {
if (errorp) {
temp = malloc(12);
sprintf(temp, "0x%x", lsc.args[sc->args[i].offset]);
} else {
temp = print_arg(Procfd, &sc->args[i], lsc.args);
}
lsc.s_args[i] = temp;
}
}
}
print_syscall(outfile, lsc.name, lsc.nargs, lsc.s_args);
if (errorp) {
for (i = 0; i < sizeof(bsd_to_linux_errno) / sizeof(int); i++)
if (retval == bsd_to_linux_errno[i])
break;
fprintf(outfile, "errno %d '%s'\n", retval, strerror(i));
} else {
fprintf(outfile, "returns %d (0x%x)\n", retval, retval);
}
clear_lsc();
return;
}

281
usr.bin/truss/i386-fbsd.c Normal file
View File

@ -0,0 +1,281 @@
/*
* FreeBSD/386-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.
*/
/*
* $Id$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/pioctl.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <sys/syscall.h>
#include "syscall.h"
static int fd = -1;
static int cpid = -1;
extern int Procfd;
extern FILE *outfile;
#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.
*/
static struct freebsd_syscall {
struct syscall *sc;
char *name;
int number;
unsigned long *args;
int nargs; /* number of arguments -- *not* number of words! */
char **s_args; /* the printable arguments */
} fsc;
/* Clear up and free parts of the fsc structure. */
static inline void
clear_fsc() {
if (fsc.args) {
free(fsc.args);
}
if (fsc.s_args) {
int i;
for (i = 0; i < fsc.nargs; i++)
if (fsc.s_args[i])
free(fsc.s_args[i]);
free(fsc.s_args);
}
memset(&fsc, 0, sizeof(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(int pid, int nargs) {
char buf[32];
struct reg regs = { 0 };
int syscall;
int i;
int memfd;
unsigned int parm_offset;
struct syscall *sc;
if (fd == -1 || pid != cpid) {
sprintf(buf, "/proc/%d/regs", pid);
fd = open(buf, O_RDWR);
if (fd == -1) {
fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
return;
}
cpid = pid;
}
clear_fsc();
lseek(fd, 0L, 0);
i = read(fd, &regs, sizeof(regs));
parm_offset = regs.r_esp + sizeof(int);
/*
* FreeBSD has two special kinds of system call redirctions --
* SYS_syscall, and SYS___syscall. The former is the old syscall()
* routine, basicly; the latter is for quad-aligned arguments.
*/
syscall = regs.r_eax;
switch (syscall) {
case SYS_syscall:
lseek(Procfd, parm_offset, SEEK_SET);
read(Procfd, &syscall, sizeof(int));
parm_offset += sizeof(int);
break;
case SYS___syscall:
lseek(Procfd, parm_offset, SEEK_SET);
read(Procfd, &syscall, sizeof(int));
parm_offset += sizeof(quad_t);
break;
}
fsc.number = syscall;
fsc.name =
(syscall < 0 || syscall > nsyscalls) ? NULL : syscallnames[syscall];
if (!fsc.name) {
fprintf(outfile, "-- UNKNOWN SYSCALL %d --\n", syscall);
}
if (nargs == 0)
return;
fsc.args = malloc((1+nargs) * sizeof(unsigned long));
lseek(Procfd, parm_offset, SEEK_SET);
if (read(Procfd, fsc.args, nargs * sizeof(unsigned long)) == -1)
return;
sc = get_syscall(fsc.name);
if (sc) {
fsc.nargs = sc->nargs;
} else {
#if DEBUG
fprintf(outfile, "unknown syscall %s -- setting args to %d\n",
fsc.name, nargs);
#endif
fsc.nargs = nargs;
}
fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*));
memset(fsc.s_args, 0, 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) {
char *tmp;
#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(Procfd, &sc->args[i], fsc.args);
}
}
#if DEBUG
fprintf(stderr, ")\n");
#endif
}
#if DEBUG
fprintf(outfile, "\n");
#endif
/*
* Some system calls should be printed out before they are done --
* execve() and exit(), for example, never return. Possibly change
* this to work for any system call that doesn't have an OUT
* parameter?
*/
if (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit")) {
print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args);
}
return;
}
/*
* 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 sytem call number instead of, say, an error status).
*/
void
i386_syscall_exit(int pid, int syscall) {
char buf[32];
struct reg regs;
int retval;
int i;
int errorp;
struct syscall *sc;
if (fd == -1 || pid != cpid) {
sprintf(buf, "/proc/%d/regs", pid);
fd = open(buf, O_RDONLY);
if (fd == -1) {
fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
return;
}
cpid = pid;
}
lseek(fd, 0L, 0);
if (read(fd, &regs, sizeof(regs)) != sizeof(regs))
return;
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.
*/
sc = fsc.sc;
if (!sc) {
for (i = 0; i < fsc.nargs; i++) {
fsc.s_args[i] = malloc(12);
sprintf(fsc.s_args[i], "0x%x", 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, than don't bothe getting the data;
* it may not be valid.
*/
if (errorp) {
temp = malloc(12);
sprintf(temp, "0x%x", fsc.args[sc->args[i].offset]);
} else {
temp = print_arg(Procfd, &sc->args[i], fsc.args);
}
fsc.s_args[i] = temp;
}
}
}
/*
* It would probably be a good idea to merge the error handling,
* but that complicates things considerably.
*/
print_syscall(outfile, fsc.name, fsc.nargs, fsc.s_args);
if (errorp) {
fprintf(outfile, "errno %d '%s'\n", retval, strerror(retval));
} else {
fprintf(outfile, "returns %d (0x%x)\n", retval, retval);
}
clear_fsc();
return;
}

221
usr.bin/truss/i386-linux.c Normal file
View File

@ -0,0 +1,221 @@
/*
* 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.
*/
/*
* $Id$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/pioctl.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include "syscall.h"
static int fd = -1;
static int cpid = -1;
extern int Procfd;
extern FILE *outfile;
#include "linux_syscalls.h"
static int nsyscalls =
sizeof(linux_syscallnames) / sizeof(linux_syscallnames[0]);
/* See the comment in i386-fbsd.c about this structure. */
static struct linux_syscall {
struct syscall *sc;
char *name;
int number;
unsigned long args[5];
int nargs; /* number of arguments -- *not* number of words! */
char **s_args; /* the printable arguments */
} lsc;
static inline void
clear_lsc() {
if (lsc.s_args) {
int i;
for (i = 0; i < lsc.nargs; i++)
if (lsc.s_args[i])
free(lsc.s_args[i]);
free(lsc.s_args);
}
memset(&lsc, 0, sizeof(lsc));
}
void
i386_linux_syscall_entry(int pid, int nargs) {
char buf[32];
struct reg regs = { 0 };
int syscall;
int i;
int memfd;
struct syscall *sc;
if (fd == -1 || pid != cpid) {
sprintf(buf, "/proc/%d/regs", pid);
fd = open(buf, O_RDWR);
if (fd == -1) {
fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
return;
}
cpid = pid;
}
clear_lsc();
lseek(fd, 0L, 0);
i = read(fd, &regs, sizeof(regs));
syscall = regs.r_eax;
lsc.number = syscall;
lsc.name =
(syscall < 0 || syscall > nsyscalls) ? NULL : linux_syscallnames[syscall];
if (!lsc.name) {
fprintf (outfile, "-- UNKNOWN SYSCALL %d\n", syscall);
}
if (nargs == 0)
return;
/*
* Linux passes syscall arguments in registers, not
* on the stack. Fortunately, we've got access to the
* register set. Note that we don't bother checking the
* number of arguments. And what does linux do for syscalls
* that have more than five arguments?
*/
lsc.args[0] = regs.r_ebx;
lsc.args[1] = regs.r_ecx;
lsc.args[2] = regs.r_edx;
lsc.args[3] = regs.r_esi;
lsc.args[4] = regs.r_edi;
sc = get_syscall(lsc.name);
if (sc) {
lsc.nargs = sc->nargs;
} else {
#ifdef DEBUG
fprintf(outfile, "unknown syscall %s -- setting args to %d\n",
lsc.name, nargs);
#endif
lsc.nargs = nargs;
}
lsc.s_args = malloc((1+lsc.nargs) * sizeof(char*));
memset(lsc.s_args, 0, lsc.nargs * sizeof(char*));
lsc.sc = sc;
if (lsc.name) {
char *tmp;
#ifdef DEBUG
fprintf(stderr, "syscall %s(", lsc.name);
#endif
for (i = 0; i < lsc.nargs ; i++) {
#ifdef DEBUG
fprintf(stderr, "0x%x%s",
sc ?
lsc.args[sc->args[i].offset]
: lsc.args[i],
i < (lsc.nargs - 1) ? "," : "");
#endif
if (sc && !(sc->args[i].type & OUT)) {
lsc.s_args[i] = print_arg(Procfd, &sc->args[i], lsc.args);
}
}
#ifdef DEBUG
fprintf(stderr, ")\n");
#endif
}
if (!strcmp(lsc.name, "linux_execve") || !strcmp(lsc.name, "exit")) {
print_syscall(outfile, lsc.name, lsc.nargs, lsc.s_args);
}
return;
}
/*
* Linux syscalls return negative errno's, we do positive and map them
*/
int bsd_to_linux_errno[] = {
-0, -1, -2, -3, -4, -5, -6, -7, -8, -9,
-10, -35, -12, -13, -14, -15, -16, -17, -18, -19,
-20, -21, -22, -23, -24, -25, -26, -27, -28, -29,
-30, -31, -32, -33, -34, -11,-115,-114, -88, -89,
-90, -91, -92, -93, -94, -95, -96, -97, -98, -99,
-100,-101,-102,-103,-104,-105,-106,-107,-108,-109,
-110,-111, -40, -36,-112,-113, -39, -11, -87,-122,
-116, -66, -6, -6, -6, -6, -6, -37, -38, -9,
-6,
};
void
i386_linux_syscall_exit(int pid, int syscall) {
char buf[32];
struct reg regs;
int retval;
int i;
int errorp;
struct syscall *sc;
if (fd == -1 || pid != cpid) {
sprintf(buf, "/proc/%d/regs", pid);
fd = open(buf, O_RDONLY);
if (fd == -1) {
fprintf(outfile, "-- CANNOT READ REGISTERS --\n");
return;
}
cpid = pid;
}
lseek(fd, 0L, 0);
if (read(fd, &regs, sizeof(regs)) != sizeof(regs))
return;
retval = regs.r_eax;
errorp = !!(regs.r_eflags & PSL_C);
sc = lsc.sc;
if (!sc) {
for (i = 0; i < lsc.nargs; i++) {
lsc.s_args[i] = malloc(12);
sprintf(lsc.s_args[i], "0x%x", lsc.args[i]);
}
} else {
for (i = 0; i < sc->nargs; i++) {
char *temp;
if (sc->args[i].type & OUT) {
if (errorp) {
temp = malloc(12);
sprintf(temp, "0x%x", lsc.args[sc->args[i].offset]);
} else {
temp = print_arg(Procfd, &sc->args[i], lsc.args);
}
lsc.s_args[i] = temp;
}
}
}
print_syscall(outfile, lsc.name, lsc.nargs, lsc.s_args);
if (errorp) {
for (i = 0; i < sizeof(bsd_to_linux_errno) / sizeof(int); i++)
if (retval == bsd_to_linux_errno[i])
break;
fprintf(outfile, "errno %d '%s'\n", retval, strerror(i));
} else {
fprintf(outfile, "returns %d (0x%x)\n", retval, retval);
}
clear_lsc();
return;
}

10
usr.bin/truss/i386.conf Normal file
View File

@ -0,0 +1,10 @@
sysnames="syscalls.h"
sysproto="/dev/null"
sysproto_h="/dev/null"
syshdr="/dev/null"
syssw="/dev/null"
syshide="/dev/null"
syscallprefix="SYS_"
switchname="sysent"
namesname="syscallnames"

View File

@ -0,0 +1,10 @@
sysnames="linux_syscalls.h"
sysproto="/dev/null"
sysproto_h="/dev/null"
syshdr="/dev/null"
syssw="/dev/null"
syshide="/dev/null"
syscallprefix="SYS_"
switchname="sysent"
namesname="linux_syscallnames"

201
usr.bin/truss/main.c Normal file
View File

@ -0,0 +1,201 @@
/*
* The main module for truss. Suprisingly simple, but, then, the other
* files handle the bulk of the work. And, of course, the kernel has to
* do a lot of the work :).
*/
/*
* $Id$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <err.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/pioctl.h>
extern int setup_and_wait(char **);
extern int start_tracing(int, int);
extern void i386_syscall_entry(int, int);
extern void i386_syscall_exit(int, int);
extern void i386_linux_syscall_entry(int, int);
extern void i386_linux_syscall_exit(int, int);
/*
* These should really be parameterized -- I don't like having globals,
* but this is the easiest way, right now, to deal with them.
*/
int pid = 0;
int nosigs = 0;
FILE *outfile = stderr;
char *prog;
int Procfd;
char progtype[50]; /* OS and type of executable */
static inline void
usage(void) {
fprintf(stderr, "usage: %s [-o <file>] [-S] { [-p <pid> ] | "
"[ <command> <args>] }\n", prog);
exit(1);
}
struct ex_types {
char *type;
void (*enter_syscall)(int, int);
void (*exit_syscall)(int, int);
} ex_types[] = {
{ "FreeBSD a.out", i386_syscall_entry, i386_syscall_exit },
{ "Linux ELF", i386_linux_syscall_entry, i386_linux_syscall_exit },
{ 0, 0, 0 },
};
/*
* Set the execution type. This is called after every exec, and when
* a process is first monitored. The procfs pseudo-file "etype" has
* the execution module type -- see /proc/curproc/etype for an example.
*/
static struct ex_types *
set_etype() {
struct ex_types *funcs;
char etype[24];
char progtype[32];
int fd;
sprintf(etype, "/proc/%d/etype", pid);
if ((fd = open(etype, O_RDONLY)) == -1) {
strcpy(progtype, "FreeBSD a.out");
} else {
int len = read(fd, progtype, sizeof(progtype));
progtype[len-1] = '\0';
close(etype);
}
for (funcs = ex_types; funcs->type; funcs++)
if (!strcmp(funcs->type, progtype))
break;
return funcs;
}
main(int ac, char **av) {
int mask;
int c;
int i;
char **command;
struct procfs_status pfs;
char etype[25];
struct ex_types *funcs;
int fd;
int in_exec = 0;
prog = av[0];
while ((c = getopt(ac, av, "p:o:S")) != EOF) {
switch (c) {
case 'p': /* specified pid */
pid = atoi(optarg);
break;
case 'o': /* Specified output file */
if ((outfile = fopen(optarg, "w")) == NULL) {
fprintf (stderr, "%s: cannot open %s\n", av[0], optarg);
exit(1);
}
break;
case 'S': /* Don't trace signals */
nosigs = 1;
break;
default:
usage();
}
}
ac -= optind; av += optind;
if (ac && pid != 0)
usage();
/*
* If truss starts the process itself, it will ignore some signals --
* they should be passed off to the process, which may or may not
* exit. If, however, we are examining an already-running process,
* then we restore the event mask on these same signals.
*/
if (pid == 0) { /* Start a command ourselves */
command = av;
pid = setup_and_wait(command);
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
} else {
extern void restore_proc(int);
signal(SIGINT, restore_proc);
signal(SIGTERM, restore_proc);
signal(SIGQUIT, restore_proc);
}
/*
* At this point, if we started the process, it is stopped waiting to
* be woken up, either in exit() or in execve().
*/
Procfd = start_tracing(pid, S_EXEC | S_SCE | S_SCX | S_CORE | S_EXIT |
(nosigs ? 0 : S_SIG));
pfs.why = 0;
funcs = set_etype();
/*
* 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.
*/
do {
int val = 0;
if (ioctl(Procfd, PIOCWAIT, &pfs) == -1)
perror("PIOCWAIT top of loop");
else {
switch(i = pfs.why) {
case S_SCE:
funcs->enter_syscall(pid, pfs.val);
break;
case S_SCX:
/*
* This is so we don't get two messages for an exec -- one
* for the S_EXEC, and one for the syscall exit. It also,
* conveniently, ensures that the first message printed out
* isn't the return-from-syscall used to create the process.
*/
if (in_exec) {
in_exec = 0;
break;
}
funcs->exit_syscall(pid, pfs.val);
break;
case S_SIG:
fprintf(outfile, "SIGNAL %d\n", pfs.val);
break;
case S_EXIT:
fprintf (outfile, "process exit, rval = %d\n", pfs.val);
break;
case S_EXEC:
funcs = set_etype();
in_exec = 1;
break;
default:
fprintf (outfile, "Process stopped because of: %d\n", i);
break;
}
}
if (ioctl(Procfd, PIOCCONT, &val) == -1)
perror("PIOCCONT");
} while (pfs.why != S_EXIT);
return 0;
}

125
usr.bin/truss/setup.c Normal file
View File

@ -0,0 +1,125 @@
/*
* Various setup functions for truss. Not the cleanest-written code,
* I'm afraid.
*/
/*
* $Id$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/pioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
static int evflags = 0;
/*
* setup_and_wait() is called to start a process. All it really does
* is vfork(), 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.
*/
int
setup_and_wait(char *command[]) {
struct procfs_status pfs;
char buf[32];
int fd;
int pid;
extern char *prog;
int flags;
pid = vfork();
if (pid == -1) {
err(1, "vfork failed\n");
}
if (pid == 0) { /* Child */
int mask = S_EXEC | S_EXIT;
fd = open("/proc/curproc/mem", O_WRONLY);
if (fd == -1)
err(2, "cannot open /proc/curproc/mem: %s\n", strerror(errno));
if (ioctl(fd, PIOCBIS, &mask) == -1)
err(3, "PIOCBIS: %s\n", strerror(errno));
execvp(command[0], command);
mask = ~0;
ioctl(fd, PIOCBIC, &mask);
err(4, "execvp %s", command[0]);
}
/* Only in the parent here */
if (waitpid(pid, NULL, WNOHANG) != 0) {
/*
* Process exited before it got to us -- meaning the exec failed
* miserably -- so we just quietly exit.
*/
exit(1);
}
sprintf(buf, "/proc/%d/mem", pid);
if ((fd = open(buf, O_RDWR)) == -1)
err(5, "cannot open %s: %s\n", buf, strerror(errno));
if (ioctl(fd, PIOCWAIT, &pfs) == -1)
err(6, "PIOCWAIT: %s\n", strerror(errno));
if (pfs.why == S_EXIT) {
int zero = 0;
fprintf(stderr, "process exited before exec'ing\n");
ioctl(fd, PIOCCONT, &zero);
wait(0);
exit(7);
}
close(fd);
return 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.
*/
int
start_tracing(int pid, int flags) {
int fd;
char buf[32];
struct procfs_status tmp;
sprintf(buf, "/proc/%d/mem", pid);
fd = open(buf, O_RDWR);
if (fd == -1)
err(8, "cannot open %s", buf);
if (ioctl(fd, PIOCSTATUS, &tmp) == -1) {
err(10, "cannot get procfs status struct");
}
evflags = tmp.events;
if (ioctl(fd, PIOCBIS, &flags) == -1)
err(9, "cannot set procfs event bit mask");
return fd;
}
/*
* Restore a process back to it's pre-truss state.
* Called for SIGINT, SIGTERM, SIGQUIT. This only
* applies if truss was told to monitor an already-existing
* process.
*/
void
restore_proc(int signo) {
extern int Procfd;
int i;
i = ~0;
ioctl(Procfd, PIOCBIC, &i);
if (evflags)
ioctl(Procfd, PIOCBIS, &evflags);
exit(0);
}

43
usr.bin/truss/syscall.h Normal file
View File

@ -0,0 +1,43 @@
/*
* System call arguments come in several flavours:
* Hex -- values that should be printed in hex (addresses)
* Octal -- Same as above, but octal
* Int -- normal integer values (file descriptors, for example)
* String -- pointers to sensible data. Note that we treat read() and
* write() arguments as such, even though they may *not* be
* printable data.
* Ptr -- pointer to some specific structure. Just print as hex for now.
* Quad -- a double-word value. e.g., lseek(int, offset_t, int)
* Stat -- a pointer to a stat buffer. Currently unused.
*
* In addition, the pointer types (String, Ptr) may have OUT masked in --
* this means that the data is set on *return* from the system call -- or
* IN (meaning that the data is passed *into* the system call).
*/
/*
* $Id$
*/
enum Argtype { None = 1, Hex, Octal, Int, String, Ptr, Stat, Quad };
#define ARG_MASK 0xff
#define OUT 0x100
#define IN /*0x20*/0
struct syscall_args {
enum Argtype type;
int offset;
};
struct syscall {
char *name;
int ret_type; /* 0, 1, or 2 return values */
int nargs; /* actual number of meaningful arguments */
/* Hopefully, no syscalls with > 10 args */
struct syscall_args args[10];
};
struct syscall *get_syscall(const char*);
char *get_string(int, void*, int);
char *print_arg(int, struct syscall_args *, unsigned long*);
void print_syscall(FILE *, const char *, int, char **);

194
usr.bin/truss/syscalls.c Normal file
View File

@ -0,0 +1,194 @@
/*
* This file has routines used to print out system calls and their
* arguments.
*/
/*
* $Id$
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include "syscall.h"
/*
* This should probably be in its own file.
*/
struct syscall syscalls[] = {
{ "readlink", 1, 3,
{ { String, 0 } , { String | OUT, 1 }, { Int, 2 }}},
{ "lseek", 2, 3,
{ { Int, 0 }, {Quad, 2 }, { Int, 4 }}},
{ "mmap", 2, 6,
{ { Hex, 0 }, {Int, 1}, {Hex, 2}, {Hex, 3}, {Int, 4}, {Quad, 6}}},
{ "open", 1, 3,
{ { String | IN, 0} , { Int, 1}, {Octal, 2}}},
{ "linux_open", 1, 3,
{ { String, 0 }, { Int, 1}, { Octal, 2 }}},
{ "close", 1, 1, { { Int, 0 } } },
{ "fstat", 1, 2,
{ { Int, 0}, {Ptr | OUT , 1 }}},
{ "stat", 1, 2,
{ { String | IN, 0 }, { Ptr | OUT, 1 }}},
{ "linux_newstat", 1, 2,
{ { String | IN, 0 }, { Ptr | OUT, 1 }}},
{ "linux_newfstat", 1, 2,
{ { Int, 0 }, { Ptr | OUT, 1 }}},
{ "write", 1, 3,
{ { Int, 0}, { Ptr | IN, 1 }, { Int, 2 }}},
{ "break", 1, 1, { { Hex, 0 }}},
{ "exit", 0, 1, { { Hex, 0 }}},
{ 0, 0, 0, { 0, 0 } },
};
/*
* 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) {
struct syscall *sc = syscalls;
while (sc->name) {
if (!strcmp(name, sc->name))
return sc;
sc++;
}
return NULL;
}
/*
* 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.
*/
char *
get_string(int procfd, void *offset, int max) {
char *buf, *tmp;
int size, len, c;
FILE *p;
if ((p = fdopen(procfd, "r")) == NULL) {
perror("fdopen");
exit(1);
}
buf = malloc( size = (max ? max : 64 ) );
len = 0;
fseek(p, (long)offset, SEEK_SET);
while ((c = fgetc(p)) != EOF) {
buf[len++] = c;
if (c == 0 || len == max) {
buf[len] = 0;
break;
}
if (len == size) {
char *tmp = buf;
tmp = realloc(buf, size+64);
if (tmp == NULL) {
buf[len] = 0;
return buf;
}
size += 64;
}
}
return buf;
}
/*
* Gag. This is really unportable. Multiplication is more portable.
* But slower, from the code I saw.
*/
static long long
make_quad(unsigned long p1, unsigned long p2) {
union {
long long ll;
unsigned long l[2];
} t;
t.l[0] = p1;
t.l[1] = p2;
return t.ll;
}
/*
* 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
* a pointer to the syscall description (see above); args is
* an array of all of the system call arguments.
*/
char *
print_arg(int fd, struct syscall_args *sc, unsigned long *args) {
char *tmp;
switch (sc->type & ARG_MASK) {
case Hex:
tmp = malloc(12);
sprintf(tmp, "0x%x", args[sc->offset]);
break;
case Octal:
tmp = malloc(13);
sprintf(tmp, "0%o", args[sc->offset]);
break;
case Int:
tmp = malloc(12);
sprintf(tmp, "%d", args[sc->offset]);
break;
case String:
{
char *tmp2;
tmp2 = get_string(fd, (void*)args[sc->offset], 0);
tmp = malloc(strlen(tmp2) + 3);
sprintf(tmp, "\"%s\"", tmp2);
free(tmp2);
}
break;
case Quad:
{
unsigned long long t;
unsigned long l1, l2;
l1 = args[sc->offset];
l2 = args[sc->offset+1];
t = make_quad(l1, l2);
tmp = malloc(24);
sprintf(tmp, "0x%qx", t);
break;
}
case Ptr:
tmp = malloc(12);
sprintf(tmp, "0x%x", args[sc->offset]);
break;
}
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(FILE *outfile, const char *name, int nargs, char **s_args) {
int i;
fprintf(outfile, "syscall %s(", name);
for (i = 0; i < nargs; i++) {
if (s_args[i])
fprintf(outfile, "%s", s_args[i]);
else
fprintf(outfile, "<missing argument>");
fprintf(outfile, "%s", i < (nargs - 1) ? "," : "");
}
fprintf(outfile, ")\n\t");
}

57
usr.bin/truss/truss.1 Normal file
View File

@ -0,0 +1,57 @@
.Dd Nov 23, 1997
.Dt TRUSS 1
.Os FreeBSD
.Sh NAME
.Nm \&truss
.Nd trace system calls
.Sh Synopsis
.Nm \&truss
.Op Fl S
.Op Fl p Ar pid
.Op Fl o Ar file
command
.Sh DESCRIPTION
.Nm \&truss
traces the system calls called by the specified process or program.
Output is to the specified output file, or standard error by default.
It does this by stopping and restarting the process being monitored via
.Xr procfs 5 .
.Pp
The options are as follows:
.Bl -tag -width command
.It Fl S
Do not display information about signals received by the process.
(Normally,
.Nm \&ps
displays signal as well as system call events.)
.It Fl p
Follow the process specified by
.Ar pid
instead of a new command.
.It Fl o
Print the output to the specified file instead of standard error.
.It Ar command
Execute
.Ar command
and trace the system calls of it.
(The
.Fl p
and
.Ar command
options are mutually exclusive.)
.Sh EXAMPLES
# Follow the system calls used in echoing "hello"
.Dl $ truss /bin/echo hello
# Do the same, but put the output into a file
.Dl $ truss -o /tmp/truss.out /bin/echo hello
# Follow an already-running process
.Dl $ truss -p 1
.Sh SEE ALSO
.Xr procfs 5 ,
.Xr ktrace 1 ,
.Xr kdump 1
.Sh HISTORY
The
.Nm truss
command was written by Sean Eric Fagan for FreeBSD; it was modeled after
similar commands available for System V Release 4 and SunOS.