truss: split counting of syscalls and syscall calling convention
This change is a refactoring cleanup to improve support for compat32 syscalls (and compat64 on CHERI systems). Each process ABI now has it's own struct sycall instead of using one global list. The list of all syscalls is replaced with a list of seen syscalls. Looking up the syscall argument passing convention now interates over the fixed-size array instead of using a link-list that's populated on startup so we no longer need the init_syscall() function. The actual functional changes are in D27625. Reviewed By: jhb Differential Revision: https://reviews.freebsd.org/D27636
This commit is contained in:
parent
172a624f0c
commit
6019514b0b
@ -87,7 +87,6 @@ main(int ac, char **av)
|
|||||||
trussinfo->strsize = 32;
|
trussinfo->strsize = 32;
|
||||||
trussinfo->curthread = NULL;
|
trussinfo->curthread = NULL;
|
||||||
LIST_INIT(&trussinfo->proclist);
|
LIST_INIT(&trussinfo->proclist);
|
||||||
init_syscalls();
|
|
||||||
while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) {
|
while ((c = getopt(ac, av, "p:o:facedDs:SH")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'p': /* specified pid */
|
case 'p': /* specified pid */
|
||||||
|
@ -463,8 +463,8 @@ enter_syscall(struct trussinfo *info, struct threadinfo *t,
|
|||||||
fprintf(info->outfile, "-- UNKNOWN %s SYSCALL %d --\n",
|
fprintf(info->outfile, "-- UNKNOWN %s SYSCALL %d --\n",
|
||||||
t->proc->abi->type, t->cs.number);
|
t->proc->abi->type, t->cs.number);
|
||||||
|
|
||||||
t->cs.nargs = sc->nargs;
|
t->cs.nargs = sc->decode.nargs;
|
||||||
assert(sc->nargs <= nitems(t->cs.s_args));
|
assert(sc->decode.nargs <= nitems(t->cs.s_args));
|
||||||
|
|
||||||
t->cs.sc = sc;
|
t->cs.sc = sc;
|
||||||
|
|
||||||
@ -480,11 +480,12 @@ enter_syscall(struct trussinfo *info, struct threadinfo *t,
|
|||||||
#endif
|
#endif
|
||||||
for (i = 0; i < t->cs.nargs; i++) {
|
for (i = 0; i < t->cs.nargs; i++) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
fprintf(stderr, "0x%lx%s", t->cs.args[sc->args[i].offset],
|
fprintf(stderr, "0x%lx%s",
|
||||||
|
t->cs.args[sc->decode.args[i].offset],
|
||||||
i < (t->cs.nargs - 1) ? "," : "");
|
i < (t->cs.nargs - 1) ? "," : "");
|
||||||
#endif
|
#endif
|
||||||
if (!(sc->args[i].type & OUT)) {
|
if (!(sc->decode.args[i].type & OUT)) {
|
||||||
t->cs.s_args[i] = print_arg(&sc->args[i],
|
t->cs.s_args[i] = print_arg(&sc->decode.args[i],
|
||||||
t->cs.args, NULL, info);
|
t->cs.args, NULL, info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,19 +543,19 @@ exit_syscall(struct trussinfo *info, struct ptrace_lwpinfo *pl)
|
|||||||
* Here, we only look for arguments that have OUT masked in --
|
* Here, we only look for arguments that have OUT masked in --
|
||||||
* otherwise, they were handled in enter_syscall().
|
* otherwise, they were handled in enter_syscall().
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < sc->nargs; i++) {
|
for (i = 0; i < sc->decode.nargs; i++) {
|
||||||
char *temp;
|
char *temp;
|
||||||
|
|
||||||
if (sc->args[i].type & OUT) {
|
if (sc->decode.args[i].type & OUT) {
|
||||||
/*
|
/*
|
||||||
* If an error occurred, then don't bother
|
* If an error occurred, then don't bother
|
||||||
* getting the data; it may not be valid.
|
* getting the data; it may not be valid.
|
||||||
*/
|
*/
|
||||||
if (psr.sr_error != 0) {
|
if (psr.sr_error != 0) {
|
||||||
asprintf(&temp, "0x%lx",
|
asprintf(&temp, "0x%lx",
|
||||||
t->cs.args[sc->args[i].offset]);
|
t->cs.args[sc->decode.args[i].offset]);
|
||||||
} else {
|
} else {
|
||||||
temp = print_arg(&sc->args[i],
|
temp = print_arg(&sc->decode.args[i],
|
||||||
t->cs.args, psr.sr_retval, info);
|
t->cs.args, psr.sr_retval, info);
|
||||||
}
|
}
|
||||||
t->cs.s_args[i] = temp;
|
t->cs.s_args[i] = temp;
|
||||||
|
@ -218,18 +218,28 @@ enum Argtype {
|
|||||||
_Static_assert(ARG_MASK > MAX_ARG_TYPE,
|
_Static_assert(ARG_MASK > MAX_ARG_TYPE,
|
||||||
"ARG_MASK overlaps with Argtype values");
|
"ARG_MASK overlaps with Argtype values");
|
||||||
|
|
||||||
struct syscall_args {
|
struct syscall_arg {
|
||||||
enum Argtype type;
|
enum Argtype type;
|
||||||
int offset;
|
int offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct syscall_decode {
|
||||||
|
const char *name; /* Name for calling convention lookup. */
|
||||||
|
/*
|
||||||
|
* Syscall return type:
|
||||||
|
* 0: no return value (e.g. exit)
|
||||||
|
* 1: normal return value (a single int/long/pointer)
|
||||||
|
* 2: off_t return value (two values for 32-bit ABIs)
|
||||||
|
*/
|
||||||
|
u_int ret_type;
|
||||||
|
u_int nargs; /* number of meaningful arguments */
|
||||||
|
struct syscall_arg args[10]; /* Hopefully no syscalls with > 10 args */
|
||||||
|
};
|
||||||
|
|
||||||
struct syscall {
|
struct syscall {
|
||||||
STAILQ_ENTRY(syscall) entries;
|
STAILQ_ENTRY(syscall) entries;
|
||||||
const char *name;
|
const char *name; /* Name to be displayed, might be malloc()'d */
|
||||||
u_int ret_type; /* 0, 1, or 2 return values */
|
struct syscall_decode decode;
|
||||||
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 */
|
struct timespec time; /* Time spent for this call */
|
||||||
int ncalls; /* Number of calls */
|
int ncalls; /* Number of calls */
|
||||||
int nerror; /* Number of calls that returned with error */
|
int nerror; /* Number of calls that returned with error */
|
||||||
@ -237,7 +247,7 @@ struct syscall {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct syscall *get_syscall(struct threadinfo *, u_int, u_int);
|
struct syscall *get_syscall(struct threadinfo *, u_int, u_int);
|
||||||
char *print_arg(struct syscall_args *, unsigned long*, register_t *,
|
char *print_arg(struct syscall_arg *, unsigned long *, register_t *,
|
||||||
struct trussinfo *);
|
struct trussinfo *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -280,7 +290,6 @@ struct linux_socketcall_args {
|
|||||||
char args_l_[PADL_(l_ulong)]; l_ulong args; char args_r_[PADR_(l_ulong)];
|
char args_l_[PADL_(l_ulong)]; l_ulong args; char args_r_[PADR_(l_ulong)];
|
||||||
};
|
};
|
||||||
|
|
||||||
void init_syscalls(void);
|
|
||||||
void print_syscall(struct trussinfo *);
|
void print_syscall(struct trussinfo *);
|
||||||
void print_syscall_ret(struct trussinfo *, int, register_t *);
|
void print_syscall_ret(struct trussinfo *, int, register_t *);
|
||||||
void print_summary(struct trussinfo *trussinfo);
|
void print_summary(struct trussinfo *trussinfo);
|
||||||
|
@ -47,8 +47,10 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/ioccom.h>
|
#include <sys/ioccom.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
#include <sys/sched.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#define _WANT_FREEBSD11_STAT
|
#define _WANT_FREEBSD11_STAT
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -66,8 +68,6 @@ __FBSDID("$FreeBSD$");
|
|||||||
#define _WANT_KERNEL_ERRNO
|
#define _WANT_KERNEL_ERRNO
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <poll.h>
|
|
||||||
#include <sched.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -85,8 +85,13 @@ __FBSDID("$FreeBSD$");
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* This should probably be in its own file, sorted alphabetically.
|
* This should probably be in its own file, sorted alphabetically.
|
||||||
|
*
|
||||||
|
* Note: We only scan this table on the initial syscall number to calling
|
||||||
|
* convention lookup, i.e. once each time a new syscall is encountered. This
|
||||||
|
* is unlikely to be a performance issue, but if it is we could sort this array
|
||||||
|
* and use a binary search instead.
|
||||||
*/
|
*/
|
||||||
static struct syscall decoded_syscalls[] = {
|
static const struct syscall_decode decoded_syscalls[] = {
|
||||||
/* Native ABI */
|
/* Native ABI */
|
||||||
{ .name = "__acl_aclcheck_fd", .ret_type = 1, .nargs = 3,
|
{ .name = "__acl_aclcheck_fd", .ret_type = 1, .nargs = 3,
|
||||||
.args = { { Int, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
|
.args = { { Int, 0 }, { Acltype, 1 }, { Ptr, 2 } } },
|
||||||
@ -706,10 +711,8 @@ static struct syscall decoded_syscalls[] = {
|
|||||||
{ .name = "cloudabi_sys_thread_exit", .ret_type = 1, .nargs = 2,
|
{ .name = "cloudabi_sys_thread_exit", .ret_type = 1, .nargs = 2,
|
||||||
.args = { { Ptr, 0 }, { CloudABIMFlags, 1 } } },
|
.args = { { Ptr, 0 }, { CloudABIMFlags, 1 } } },
|
||||||
{ .name = "cloudabi_sys_thread_yield", .ret_type = 1, .nargs = 0 },
|
{ .name = "cloudabi_sys_thread_yield", .ret_type = 1, .nargs = 0 },
|
||||||
|
|
||||||
{ .name = 0 },
|
|
||||||
};
|
};
|
||||||
static STAILQ_HEAD(, syscall) syscalls;
|
static STAILQ_HEAD(, syscall) seen_syscalls;
|
||||||
|
|
||||||
/* Xlat idea taken from strace */
|
/* Xlat idea taken from strace */
|
||||||
struct xlat {
|
struct xlat {
|
||||||
@ -969,7 +972,7 @@ print_mask_arg32(bool (*decoder)(FILE *, uint32_t, uint32_t *), FILE *fp,
|
|||||||
* decoding arguments.
|
* decoding arguments.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
quad_fixup(struct syscall *sc)
|
quad_fixup(struct syscall_decode *sc)
|
||||||
{
|
{
|
||||||
int offset, prev;
|
int offset, prev;
|
||||||
u_int i;
|
u_int i;
|
||||||
@ -1007,20 +1010,6 @@ quad_fixup(struct syscall *sc)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
|
||||||
init_syscalls(void)
|
|
||||||
{
|
|
||||||
struct syscall *sc;
|
|
||||||
|
|
||||||
STAILQ_INIT(&syscalls);
|
|
||||||
for (sc = decoded_syscalls; sc->name != NULL; sc++) {
|
|
||||||
#ifndef __LP64__
|
|
||||||
quad_fixup(sc);
|
|
||||||
#endif
|
|
||||||
STAILQ_INSERT_HEAD(&syscalls, sc, entries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct syscall *
|
static struct syscall *
|
||||||
find_syscall(struct procabi *abi, u_int number)
|
find_syscall(struct procabi *abi, u_int number)
|
||||||
{
|
{
|
||||||
@ -1040,6 +1029,11 @@ add_syscall(struct procabi *abi, u_int number, struct syscall *sc)
|
|||||||
{
|
{
|
||||||
struct extra_syscall *es;
|
struct extra_syscall *es;
|
||||||
|
|
||||||
|
#ifndef __LP64__
|
||||||
|
/* FIXME: should be based on syscall ABI not truss ABI */
|
||||||
|
quad_fixup(&sc->decode);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (number < nitems(abi->syscalls)) {
|
if (number < nitems(abi->syscalls)) {
|
||||||
assert(abi->syscalls[number] == NULL);
|
assert(abi->syscalls[number] == NULL);
|
||||||
abi->syscalls[number] = sc;
|
abi->syscalls[number] = sc;
|
||||||
@ -1049,6 +1043,8 @@ add_syscall(struct procabi *abi, u_int number, struct syscall *sc)
|
|||||||
es->number = number;
|
es->number = number;
|
||||||
STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, entries);
|
STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
STAILQ_INSERT_HEAD(&seen_syscalls, sc, entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1059,24 +1055,28 @@ struct syscall *
|
|||||||
get_syscall(struct threadinfo *t, u_int number, u_int nargs)
|
get_syscall(struct threadinfo *t, u_int number, u_int nargs)
|
||||||
{
|
{
|
||||||
struct syscall *sc;
|
struct syscall *sc;
|
||||||
|
const char *sysdecode_name;
|
||||||
const char *name;
|
const char *name;
|
||||||
char *new_name;
|
|
||||||
u_int i;
|
u_int i;
|
||||||
|
|
||||||
sc = find_syscall(t->proc->abi, number);
|
sc = find_syscall(t->proc->abi, number);
|
||||||
if (sc != NULL)
|
if (sc != NULL)
|
||||||
return (sc);
|
return (sc);
|
||||||
|
|
||||||
name = sysdecode_syscallname(t->proc->abi->abi, number);
|
/* Memory is not explicitly deallocated, it's released on exit(). */
|
||||||
if (name == NULL) {
|
sysdecode_name = sysdecode_syscallname(t->proc->abi->abi, number);
|
||||||
asprintf(&new_name, "#%d", number);
|
if (sysdecode_name == NULL)
|
||||||
name = new_name;
|
asprintf(__DECONST(char **, &name), "#%d", number);
|
||||||
} else
|
else
|
||||||
new_name = NULL;
|
name = sysdecode_name;
|
||||||
STAILQ_FOREACH(sc, &syscalls, entries) {
|
|
||||||
if (strcmp(name, sc->name) == 0) {
|
sc = calloc(1, sizeof(*sc));
|
||||||
|
sc->name = name;
|
||||||
|
|
||||||
|
for (i = 0; i < nitems(decoded_syscalls); i++) {
|
||||||
|
if (strcmp(name, decoded_syscalls[i].name) == 0) {
|
||||||
|
sc->decode = decoded_syscalls[i];
|
||||||
add_syscall(t->proc->abi, number, sc);
|
add_syscall(t->proc->abi, number, sc);
|
||||||
free(new_name);
|
|
||||||
return (sc);
|
return (sc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1086,21 +1086,15 @@ get_syscall(struct threadinfo *t, u_int number, u_int nargs)
|
|||||||
fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name,
|
fprintf(stderr, "unknown syscall %s -- setting args to %d\n", name,
|
||||||
nargs);
|
nargs);
|
||||||
#endif
|
#endif
|
||||||
|
sc->unknown = sysdecode_name == NULL;
|
||||||
sc = calloc(1, sizeof(struct syscall));
|
sc->decode.ret_type = 1; /* Assume 1 return value. */
|
||||||
sc->name = name;
|
sc->decode.nargs = nargs;
|
||||||
if (new_name != NULL)
|
|
||||||
sc->unknown = true;
|
|
||||||
sc->ret_type = 1;
|
|
||||||
sc->nargs = nargs;
|
|
||||||
for (i = 0; i < nargs; i++) {
|
for (i = 0; i < nargs; i++) {
|
||||||
sc->args[i].offset = i;
|
sc->decode.args[i].offset = i;
|
||||||
/* Treat all unknown arguments as LongHex. */
|
/* Treat all unknown arguments as LongHex. */
|
||||||
sc->args[i].type = LongHex;
|
sc->decode.args[i].type = LongHex;
|
||||||
}
|
}
|
||||||
STAILQ_INSERT_HEAD(&syscalls, sc, entries);
|
|
||||||
add_syscall(t->proc->abi, number, sc);
|
add_syscall(t->proc->abi, number, sc);
|
||||||
|
|
||||||
return (sc);
|
return (sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1717,7 +1711,7 @@ print_sysctl(FILE *fp, int *oid, size_t len)
|
|||||||
* an array of all of the system call arguments.
|
* an array of all of the system call arguments.
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
print_arg(struct syscall_args *sc, unsigned long *args, register_t *retval,
|
print_arg(struct syscall_arg *sc, unsigned long *args, register_t *retval,
|
||||||
struct trussinfo *trussinfo)
|
struct trussinfo *trussinfo)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@ -2992,7 +2986,7 @@ print_syscall_ret(struct trussinfo *trussinfo, int error, register_t *retval)
|
|||||||
strerror(error));
|
strerror(error));
|
||||||
}
|
}
|
||||||
#ifndef __LP64__
|
#ifndef __LP64__
|
||||||
else if (sc->ret_type == 2) {
|
else if (sc->decode.ret_type == 2) {
|
||||||
off_t off;
|
off_t off;
|
||||||
|
|
||||||
#if _BYTE_ORDER == _LITTLE_ENDIAN
|
#if _BYTE_ORDER == _LITTLE_ENDIAN
|
||||||
@ -3019,7 +3013,7 @@ print_summary(struct trussinfo *trussinfo)
|
|||||||
fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
|
fprintf(trussinfo->outfile, "%-20s%15s%8s%8s\n",
|
||||||
"syscall", "seconds", "calls", "errors");
|
"syscall", "seconds", "calls", "errors");
|
||||||
ncall = nerror = 0;
|
ncall = nerror = 0;
|
||||||
STAILQ_FOREACH(sc, &syscalls, entries)
|
STAILQ_FOREACH(sc, &seen_syscalls, entries) {
|
||||||
if (sc->ncalls) {
|
if (sc->ncalls) {
|
||||||
fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
|
fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
|
||||||
sc->name, (intmax_t)sc->time.tv_sec,
|
sc->name, (intmax_t)sc->time.tv_sec,
|
||||||
@ -3028,6 +3022,7 @@ print_summary(struct trussinfo *trussinfo)
|
|||||||
ncall += sc->ncalls;
|
ncall += sc->ncalls;
|
||||||
nerror += sc->nerror;
|
nerror += sc->nerror;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n",
|
fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n",
|
||||||
"", "-------------", "-------", "-------");
|
"", "-------------", "-------", "-------");
|
||||||
fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
|
fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n",
|
||||||
|
Loading…
Reference in New Issue
Block a user