From 1175b23f779571351f8c98aa0f7608decab0c08f Mon Sep 17 00:00:00 2001 From: John Baldwin Date: Tue, 6 Dec 2016 00:39:00 +0000 Subject: [PATCH] Rework syscall structure lookups. Avoid always using an O(n^2) loop over known syscall structures with strcmp() on each system call. Instead, use a per-ABI cache indexed by the system call number. The first 1024 system calls (which should cover all of the normal system calls in currently-supported ABIs) use a flat array indexed by the system call number to find system call structure. For other system calls, a linked list of structures storing an integer to structure mapping is stored in the ABI. The linked list isn't very smart, but it should only be used by buggy applications invoking unknown system calls. This also fixes handling of unknown system calls which currently trigger a NULL pointer dereference. Reviewed by: kib MFC after: 2 weeks --- usr.bin/truss/aarch64-cloudabi64.c | 4 +- usr.bin/truss/aarch64-freebsd.c | 4 +- usr.bin/truss/amd64-cloudabi64.c | 4 +- usr.bin/truss/amd64-freebsd.c | 4 +- usr.bin/truss/amd64-freebsd32.c | 8 +++- usr.bin/truss/amd64-linux.c | 4 +- usr.bin/truss/amd64-linux32.c | 4 +- usr.bin/truss/arm-freebsd.c | 4 +- usr.bin/truss/i386-freebsd.c | 8 +++- usr.bin/truss/i386-linux.c | 4 +- usr.bin/truss/mips-freebsd.c | 4 +- usr.bin/truss/powerpc-freebsd.c | 4 +- usr.bin/truss/powerpc64-freebsd.c | 4 +- usr.bin/truss/powerpc64-freebsd32.c | 4 +- usr.bin/truss/setup.c | 30 ++++++------- usr.bin/truss/sparc64-freebsd.c | 4 +- usr.bin/truss/syscall.h | 3 +- usr.bin/truss/syscalls.c | 65 +++++++++++++++++++++++++---- usr.bin/truss/truss.h | 21 ++++++++-- 19 files changed, 141 insertions(+), 46 deletions(-) diff --git a/usr.bin/truss/aarch64-cloudabi64.c b/usr.bin/truss/aarch64-cloudabi64.c index 6a1694a965df..ee81a5321a81 100644 --- a/usr.bin/truss/aarch64-cloudabi64.c +++ b/usr.bin/truss/aarch64-cloudabi64.c @@ -80,7 +80,9 @@ static struct procabi aarch64_cloudabi64 = { "CloudABI ELF64", SYSDECODE_ABI_CLOUDABI64, aarch64_cloudabi64_fetch_args, - aarch64_cloudabi64_fetch_retval + aarch64_cloudabi64_fetch_retval, + STAILQ_HEAD_INITIALIZER(aarch64_cloudabi64.extra_syscalls), + { NULL } }; PROCABI(aarch64_cloudabi64); diff --git a/usr.bin/truss/aarch64-freebsd.c b/usr.bin/truss/aarch64-freebsd.c index a9944f52f4fd..b47d0f8e1011 100644 --- a/usr.bin/truss/aarch64-freebsd.c +++ b/usr.bin/truss/aarch64-freebsd.c @@ -102,7 +102,9 @@ static struct procabi aarch64_freebsd = { "FreeBSD ELF64", SYSDECODE_ABI_FREEBSD, aarch64_fetch_args, - aarch64_fetch_retval + aarch64_fetch_retval, + STAILQ_HEAD_INITIALIZER(aarch64_freebsd.extra_syscalls), + { NULL } }; PROCABI(aarch64_freebsd); diff --git a/usr.bin/truss/amd64-cloudabi64.c b/usr.bin/truss/amd64-cloudabi64.c index 06de94474c1e..d0caaec67679 100644 --- a/usr.bin/truss/amd64-cloudabi64.c +++ b/usr.bin/truss/amd64-cloudabi64.c @@ -89,7 +89,9 @@ static struct procabi amd64_cloudabi64 = { "CloudABI ELF64", SYSDECODE_ABI_CLOUDABI64, amd64_cloudabi64_fetch_args, - amd64_cloudabi64_fetch_retval + amd64_cloudabi64_fetch_retval, + STAILQ_HEAD_INITIALIZER(amd64_cloudabi64.extra_syscalls), + { NULL } }; PROCABI(amd64_cloudabi64); diff --git a/usr.bin/truss/amd64-freebsd.c b/usr.bin/truss/amd64-freebsd.c index fa844fa09340..7e2ed160a90d 100644 --- a/usr.bin/truss/amd64-freebsd.c +++ b/usr.bin/truss/amd64-freebsd.c @@ -124,7 +124,9 @@ static struct procabi amd64_freebsd = { "FreeBSD ELF64", SYSDECODE_ABI_FREEBSD, amd64_fetch_args, - amd64_fetch_retval + amd64_fetch_retval, + STAILQ_HEAD_INITIALIZER(amd64_freebsd.extra_syscalls), + { NULL } }; PROCABI(amd64_freebsd); diff --git a/usr.bin/truss/amd64-freebsd32.c b/usr.bin/truss/amd64-freebsd32.c index adbc3e74c0c5..66ca417e9495 100644 --- a/usr.bin/truss/amd64-freebsd32.c +++ b/usr.bin/truss/amd64-freebsd32.c @@ -120,7 +120,9 @@ static struct procabi amd64_freebsd32 = { "FreeBSD ELF32", SYSDECODE_ABI_FREEBSD32, amd64_freebsd32_fetch_args, - amd64_freebsd32_fetch_retval + amd64_freebsd32_fetch_retval, + STAILQ_HEAD_INITIALIZER(amd64_freebsd32.extra_syscalls), + { NULL } }; PROCABI(amd64_freebsd32); @@ -129,7 +131,9 @@ static struct procabi amd64_freebsd32_aout = { "FreeBSD a.out", SYSDECODE_ABI_FREEBSD32, amd64_freebsd32_fetch_args, - amd64_freebsd32_fetch_retval + amd64_freebsd32_fetch_retval, + STAILQ_HEAD_INITIALIZER(amd64_freebsd32.extra_syscalls), + { NULL } }; PROCABI(amd64_freebsd32_aout); diff --git a/usr.bin/truss/amd64-linux.c b/usr.bin/truss/amd64-linux.c index 94a3d6abce94..00eb06ff75f8 100644 --- a/usr.bin/truss/amd64-linux.c +++ b/usr.bin/truss/amd64-linux.c @@ -99,7 +99,9 @@ static struct procabi amd64_linux = { "Linux ELF64", SYSDECODE_ABI_LINUX, amd64_linux_fetch_args, - amd64_linux_fetch_retval + amd64_linux_fetch_retval, + STAILQ_HEAD_INITIALIZER(amd64_linux.extra_syscalls), + { NULL } }; PROCABI(amd64_linux); diff --git a/usr.bin/truss/amd64-linux32.c b/usr.bin/truss/amd64-linux32.c index 3f32c8473f37..70e201adfe91 100644 --- a/usr.bin/truss/amd64-linux32.c +++ b/usr.bin/truss/amd64-linux32.c @@ -109,7 +109,9 @@ static struct procabi amd64_linux32 = { "Linux ELF32", SYSDECODE_ABI_LINUX32, amd64_linux32_fetch_args, - amd64_linux32_fetch_retval + amd64_linux32_fetch_retval, + STAILQ_HEAD_INITIALIZER(amd64_linux32.extra_syscalls), + { NULL } }; PROCABI(amd64_linux32); diff --git a/usr.bin/truss/arm-freebsd.c b/usr.bin/truss/arm-freebsd.c index 826a8d838294..753829005946 100644 --- a/usr.bin/truss/arm-freebsd.c +++ b/usr.bin/truss/arm-freebsd.c @@ -131,7 +131,9 @@ static struct procabi arm_freebsd = { "FreeBSD ELF32", SYSDECODE_ABI_FREEBSD, arm_fetch_args, - arm_fetch_retval + arm_fetch_retval, + STAILQ_HEAD_INITIALIZER(arm_freebsd.extra_syscalls), + { NULL } }; PROCABI(arm_freebsd); diff --git a/usr.bin/truss/i386-freebsd.c b/usr.bin/truss/i386-freebsd.c index 469ab7de9b7a..8103ca7beead 100644 --- a/usr.bin/truss/i386-freebsd.c +++ b/usr.bin/truss/i386-freebsd.c @@ -113,7 +113,9 @@ static struct procabi i386_freebsd = { "FreeBSD ELF32", SYSDECODE_ABI_FREEBSD, i386_fetch_args, - i386_fetch_retval + i386_fetch_retval, + STAILQ_HEAD_INITIALIZER(i386_freebsd.extra_syscalls), + { NULL } }; PROCABI(i386_freebsd); @@ -122,7 +124,9 @@ static struct procabi i386_freebsd_aout = { "FreeBSD a.out", SYSDECODE_ABI_FREEBSD, i386_fetch_args, - i386_fetch_retval + i386_fetch_retval, + STAILQ_HEAD_INITIALIZER(i386_freebsd_aout.extra_syscalls), + { NULL } }; PROCABI(i386_freebsd_aout); diff --git a/usr.bin/truss/i386-linux.c b/usr.bin/truss/i386-linux.c index 56fab65cefb7..e96db16c7a91 100644 --- a/usr.bin/truss/i386-linux.c +++ b/usr.bin/truss/i386-linux.c @@ -106,7 +106,9 @@ static struct procabi i386_linux = { "Linux ELF", SYSDECODE_ABI_LINUX, i386_linux_fetch_args, - i386_linux_fetch_retval + i386_linux_fetch_retval, + STAILQ_HEAD_INITIALIZER(i386_linux.extra_syscalls), + { NULL } }; PROCABI(i386_linux); diff --git a/usr.bin/truss/mips-freebsd.c b/usr.bin/truss/mips-freebsd.c index ac803b942f27..e3f2a04e97fb 100644 --- a/usr.bin/truss/mips-freebsd.c +++ b/usr.bin/truss/mips-freebsd.c @@ -134,7 +134,9 @@ static struct procabi mips_freebsd = { #endif SYSDECODE_ABI_FREEBSD, mips_fetch_args, - mips_fetch_retval + mips_fetch_retval, + STAILQ_HEAD_INITIALIZER(mips_freebsd.extra_syscalls), + { NULL } }; PROCABI(mips_freebsd); diff --git a/usr.bin/truss/powerpc-freebsd.c b/usr.bin/truss/powerpc-freebsd.c index dbeba2b36baf..f60c3339fdd1 100644 --- a/usr.bin/truss/powerpc-freebsd.c +++ b/usr.bin/truss/powerpc-freebsd.c @@ -115,7 +115,9 @@ static struct procabi powerpc_freebsd = { "FreeBSD ELF32", SYSDECODE_ABI_FREEBSD, powerpc_fetch_args, - powerpc_fetch_retval + powerpc_fetch_retval, + STAILQ_HEAD_INITIALIZER(powerpc_freebsd.extra_syscalls), + { NULL } }; PROCABI(powerpc_freebsd); diff --git a/usr.bin/truss/powerpc64-freebsd.c b/usr.bin/truss/powerpc64-freebsd.c index a8927bb5cc7c..ca2e108a2a82 100644 --- a/usr.bin/truss/powerpc64-freebsd.c +++ b/usr.bin/truss/powerpc64-freebsd.c @@ -111,7 +111,9 @@ static struct procabi powerpc64_freebsd = { "FreeBSD ELF64", SYSDECODE_ABI_FREEBSD, powerpc64_fetch_args, - powerpc64_fetch_retval + powerpc64_fetch_retval, + STAILQ_HEAD_INITIALIZER(powerpc64_freebsd.extra_syscalls), + { NULL } }; PROCABI(powerpc64_freebsd); diff --git a/usr.bin/truss/powerpc64-freebsd32.c b/usr.bin/truss/powerpc64-freebsd32.c index bc8d2fc97a7f..cddf492acd6a 100644 --- a/usr.bin/truss/powerpc64-freebsd32.c +++ b/usr.bin/truss/powerpc64-freebsd32.c @@ -120,7 +120,9 @@ static struct procabi powerpc64_freebsd32 = { "FreeBSD ELF32", SYSDECODE_ABI_FREEBSD32, powerpc64_freebsd32_fetch_args, - powerpc64_freebsd32_fetch_retval + powerpc64_freebsd32_fetch_retval, + STAILQ_HEAD_INITIALIZER(powerpc64_freebsd32.extra_syscalls), + { NULL } }; PROCABI(powerpc64_freebsd32); diff --git a/usr.bin/truss/setup.c b/usr.bin/truss/setup.c index 036cb8f40ccc..24e028a98186 100644 --- a/usr.bin/truss/setup.c +++ b/usr.bin/truss/setup.c @@ -344,7 +344,7 @@ alloc_syscall(struct threadinfo *t, struct ptrace_lwpinfo *pl) assert(t->in_syscall == 0); assert(t->cs.number == 0); - assert(t->cs.name == NULL); + assert(t->cs.sc == NULL); assert(t->cs.nargs == 0); for (i = 0; i < nitems(t->cs.s_args); i++) assert(t->cs.s_args[i] == NULL); @@ -378,12 +378,11 @@ enter_syscall(struct trussinfo *info, struct threadinfo *t, return; } - t->cs.name = sysdecode_syscallname(t->proc->abi->abi, t->cs.number); - if (t->cs.name == NULL) + sc = get_syscall(t, t->cs.number, narg); + if (sc->unknown) 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)); @@ -396,25 +395,22 @@ enter_syscall(struct trussinfo *info, struct threadinfo *t, * 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); + fprintf(stderr, "syscall %s(", sc->name); #endif - for (i = 0; i < t->cs.nargs; i++) { + 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) ? "," : ""); + fprintf(stderr, "0x%lx%s", t->cs.args[sc->args[i].offset], + 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 (!(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 DEBUG + fprintf(stderr, ")\n"); +#endif clock_gettime(CLOCK_REALTIME, &t->before); } diff --git a/usr.bin/truss/sparc64-freebsd.c b/usr.bin/truss/sparc64-freebsd.c index 3cbad50df70b..d1ae89685de7 100644 --- a/usr.bin/truss/sparc64-freebsd.c +++ b/usr.bin/truss/sparc64-freebsd.c @@ -118,7 +118,9 @@ static struct procabi sparc64_freebsd = { "FreeBSD ELF64", SYSDECODE_ABI_FREEBSD, sparc64_fetch_args, - sparc64_fetch_retval + sparc64_fetch_retval, + STAILQ_HEAD_INITIALIZER(sparc64_freebsd.extra_syscalls), + { NULL } }; PROCABI(sparc64_freebsd); diff --git a/usr.bin/truss/syscall.h b/usr.bin/truss/syscall.h index 0cd752d77a03..90b0e8aa94cd 100644 --- a/usr.bin/truss/syscall.h +++ b/usr.bin/truss/syscall.h @@ -72,9 +72,10 @@ struct syscall { struct timespec time; /* Time spent for this call */ int ncalls; /* Number of calls */ int nerror; /* Number of calls that returned with error */ + bool unknown; /* Unknown system call */ }; -struct syscall *get_syscall(const char *, int nargs); +struct syscall *get_syscall(struct threadinfo *, u_int, u_int); char *print_arg(struct syscall_args *, unsigned long*, long *, struct trussinfo *); /* diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c index 46bc87f9575b..2aeafcb224a6 100644 --- a/usr.bin/truss/syscalls.c +++ b/usr.bin/truss/syscalls.c @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include @@ -819,21 +820,66 @@ init_syscalls(void) for (sc = decoded_syscalls; sc->name != NULL; sc++) STAILQ_INSERT_HEAD(&syscalls, sc, entries); } + +static struct syscall * +find_syscall(struct procabi *abi, u_int number) +{ + struct extra_syscall *es; + + if (number < nitems(abi->syscalls)) + return (abi->syscalls[number]); + STAILQ_FOREACH(es, &abi->extra_syscalls, entries) { + if (es->number == number) + return (es->sc); + } + return (NULL); +} + +static void +add_syscall(struct procabi *abi, u_int number, struct syscall *sc) +{ + struct extra_syscall *es; + + if (number < nitems(abi->syscalls)) { + assert(abi->syscalls[number] == NULL); + abi->syscalls[number] = sc; + } else { + es = malloc(sizeof(*es)); + es->sc = sc; + es->number = number; + STAILQ_INSERT_TAIL(&abi->extra_syscalls, es, 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, int nargs) +get_syscall(struct threadinfo *t, u_int number, u_int nargs) { struct syscall *sc; - int i; + const char *name; + char *new_name; + u_int i; - if (name == NULL) - return (NULL); - STAILQ_FOREACH(sc, &syscalls, entries) - if (strcmp(name, sc->name) == 0) + sc = find_syscall(t->proc->abi, number); + if (sc != NULL) + return (sc); + + name = sysdecode_syscallname(t->proc->abi->abi, number); + if (name == NULL) { + asprintf(&new_name, "#%d", number); + name = new_name; + } else + new_name = NULL; + STAILQ_FOREACH(sc, &syscalls, entries) { + if (strcmp(name, sc->name) == 0) { + add_syscall(t->proc->abi, number, sc); + free(new_name); return (sc); + } + } /* It is unknown. Add it into the list. */ #if DEBUG @@ -842,7 +888,9 @@ get_syscall(const char *name, int nargs) #endif sc = calloc(1, sizeof(struct syscall)); - sc->name = strdup(name); + sc->name = name; + if (new_name != NULL) + sc->unknown = true; sc->ret_type = 1; sc->nargs = nargs; for (i = 0; i < nargs; i++) { @@ -851,6 +899,7 @@ get_syscall(const char *name, int nargs) sc->args[i].type = LongHex; } STAILQ_INSERT_HEAD(&syscalls, sc, entries); + add_syscall(t->proc->abi, number, sc); return (sc); } @@ -1866,7 +1915,7 @@ print_syscall(struct trussinfo *trussinfo) t = trussinfo->curthread; - name = t->cs.name; + name = t->cs.sc->name; nargs = t->cs.nargs; s_args = t->cs.s_args; diff --git a/usr.bin/truss/truss.h b/usr.bin/truss/truss.h index dee2155dfb22..53282965cc93 100644 --- a/usr.bin/truss/truss.h +++ b/usr.bin/truss/truss.h @@ -38,13 +38,29 @@ #define DISPLAYTIDS 0x00000080 struct procinfo; +struct syscall; struct trussinfo; +/* + * The lookup of normal system calls are optimized by using a fixed + * array for the first 1024 system calls that can be indexed directly. + * Unknown system calls with other IDs are stored in a linked list. + */ +#define SYSCALL_NORMAL_COUNT 1024 + +struct extra_syscall { + STAILQ_ENTRY(extra_syscall) entries; + struct syscall *sc; + u_int number; +}; + struct procabi { const char *type; enum sysdecode_abi abi; int (*fetch_args)(struct trussinfo *, u_int); int (*fetch_retval)(struct trussinfo *, long *, int *); + STAILQ_HEAD(, extra_syscall) extra_syscalls; + struct syscall *syscalls[SYSCALL_NORMAL_COUNT]; }; #define PROCABI(abi) DATA_SET(procabi, abi) @@ -64,10 +80,9 @@ struct procabi { */ struct current_syscall { struct syscall *sc; - const char *name; - int number; - unsigned long args[10]; + unsigned int number; unsigned int nargs; + unsigned long args[10]; char *s_args[10]; /* the printable arguments */ };