Changeset View
Standalone View
usr.bin/truss/syscalls.c
Show First 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||||||||||
#include <sys/aio.h> | #include <sys/aio.h> | ||||||||||||
#include <sys/capsicum.h> | #include <sys/capsicum.h> | ||||||||||||
#include <sys/types.h> | #include <sys/types.h> | ||||||||||||
#define _WANT_FREEBSD11_KEVENT | #define _WANT_FREEBSD11_KEVENT | ||||||||||||
#include <sys/event.h> | #include <sys/event.h> | ||||||||||||
#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> | ||||||||||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||||||||||
#include <sys/time.h> | #include <sys/time.h> | ||||||||||||
#include <sys/un.h> | #include <sys/un.h> | ||||||||||||
#include <sys/wait.h> | #include <sys/wait.h> | ||||||||||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||||||||||
#include <netinet/sctp.h> | #include <netinet/sctp.h> | ||||||||||||
#include <arpa/inet.h> | #include <arpa/inet.h> | ||||||||||||
#include <assert.h> | #include <assert.h> | ||||||||||||
#include <ctype.h> | #include <ctype.h> | ||||||||||||
#include <err.h> | #include <err.h> | ||||||||||||
#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> | ||||||||||||
#include <stdlib.h> | #include <stdlib.h> | ||||||||||||
#include <string.h> | #include <string.h> | ||||||||||||
#include <sysdecode.h> | #include <sysdecode.h> | ||||||||||||
#include <unistd.h> | #include <unistd.h> | ||||||||||||
#include <vis.h> | #include <vis.h> | ||||||||||||
#include <contrib/cloudabi/cloudabi_types_common.h> | #include <contrib/cloudabi/cloudabi_types_common.h> | ||||||||||||
#include "truss.h" | #include "truss.h" | ||||||||||||
#include "extern.h" | #include "extern.h" | ||||||||||||
#include "syscall.h" | #include "syscall.h" | ||||||||||||
/* | /* | ||||||||||||
* 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. | |||||||||||||
jhb: Note that it is mostly sorted, it's just sorted by ABI first, then sorted alphabetically within… | |||||||||||||
*/ | */ | ||||||||||||
static struct syscall decoded_syscalls[] = { | static const struct syscall_cconv 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 } } }, | ||||||||||||
{ .name = "__acl_aclcheck_file", .ret_type = 1, .nargs = 3, | { .name = "__acl_aclcheck_file", .ret_type = 1, .nargs = 3, | ||||||||||||
.args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } }, | .args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } }, | ||||||||||||
{ .name = "__acl_aclcheck_link", .ret_type = 1, .nargs = 3, | { .name = "__acl_aclcheck_link", .ret_type = 1, .nargs = 3, | ||||||||||||
.args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } }, | .args = { { Name, 0 }, { Acltype, 1 }, { Ptr, 2 } } }, | ||||||||||||
{ .name = "__acl_delete_fd", .ret_type = 1, .nargs = 2, | { .name = "__acl_delete_fd", .ret_type = 1, .nargs = 2, | ||||||||||||
▲ Show 20 Lines • Show All 599 Lines • ▼ Show 20 Lines | /* CloudABI system calls. */ | ||||||||||||
.args = { { CloudABISignal, 0 } } }, | .args = { { CloudABISignal, 0 } } }, | ||||||||||||
{ .name = "cloudabi_sys_random_get", .ret_type = 1, .nargs = 2, | { .name = "cloudabi_sys_random_get", .ret_type = 1, .nargs = 2, | ||||||||||||
.args = { { BinString | OUT, 0 }, { Int, 1 } } }, | .args = { { BinString | OUT, 0 }, { Int, 1 } } }, | ||||||||||||
{ .name = "cloudabi_sys_sock_shutdown", .ret_type = 1, .nargs = 2, | { .name = "cloudabi_sys_sock_shutdown", .ret_type = 1, .nargs = 2, | ||||||||||||
.args = { { Int, 0 }, { CloudABISDFlags, 1 } } }, | .args = { { Int, 0 }, { CloudABISDFlags, 1 } } }, | ||||||||||||
{ .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 { | ||||||||||||
int val; | int val; | ||||||||||||
const char *str; | const char *str; | ||||||||||||
}; | }; | ||||||||||||
#define X(a) { a, #a }, | #define X(a) { a, #a }, | ||||||||||||
▲ Show 20 Lines • Show All 243 Lines • ▼ Show 20 Lines | |||||||||||||
/* | /* | ||||||||||||
* Add argument padding to subsequent system calls after Quad | * Add argument padding to subsequent system calls after Quad | ||||||||||||
* syscall arguments as needed. This used to be done by hand in the | * syscall arguments as needed. This used to be done by hand in the | ||||||||||||
* decoded_syscalls table which was ugly and error prone. It is | * decoded_syscalls table which was ugly and error prone. It is | ||||||||||||
* simpler to do the fixup of offsets at initialization time than when | * simpler to do the fixup of offsets at initialization time than when | ||||||||||||
* decoding arguments. | * decoding arguments. | ||||||||||||
*/ | */ | ||||||||||||
static void | static void | ||||||||||||
quad_fixup(struct syscall *sc) | quad_fixup(struct syscall_cconv *sc) | ||||||||||||
{ | { | ||||||||||||
int offset, prev; | int offset, prev; | ||||||||||||
u_int i; | u_int i; | ||||||||||||
offset = 0; | offset = 0; | ||||||||||||
prev = -1; | prev = -1; | ||||||||||||
for (i = 0; i < sc->nargs; i++) { | for (i = 0; i < sc->nargs; i++) { | ||||||||||||
/* This arg type is a dummy that doesn't use offset. */ | /* This arg type is a dummy that doesn't use offset. */ | ||||||||||||
Show All 21 Lines | #endif | ||||||||||||
offset++; | offset++; | ||||||||||||
default: | default: | ||||||||||||
break; | break; | ||||||||||||
} | } | ||||||||||||
} | } | ||||||||||||
} | } | ||||||||||||
#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) | ||||||||||||
{ | { | ||||||||||||
struct extra_syscall *es; | struct extra_syscall *es; | ||||||||||||
if (number < nitems(abi->syscalls)) | if (number < nitems(abi->syscalls)) | ||||||||||||
return (abi->syscalls[number]); | return (abi->syscalls[number]); | ||||||||||||
STAILQ_FOREACH(es, &abi->extra_syscalls, entries) { | STAILQ_FOREACH(es, &abi->extra_syscalls, entries) { | ||||||||||||
if (es->number == number) | if (es->number == number) | ||||||||||||
return (es->sc); | return (es->sc); | ||||||||||||
} | } | ||||||||||||
return (NULL); | return (NULL); | ||||||||||||
} | } | ||||||||||||
static void | static struct syscall * | ||||||||||||
jrtc27Unsubmitted Done Inline ActionsOr just leave it as void as it just returns sc? jrtc27: Or just leave it as void as it just returns sc? | |||||||||||||
arichardsonAuthorUnsubmitted Done Inline ActionsI originally had this function doing the allocation, now the return value is pointless. arichardson: I originally had this function doing the allocation, now the return value is pointless. | |||||||||||||
add_syscall(struct procabi *abi, u_int number, struct syscall *sc) | 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 sycall ABI not truss ABI */ | |||||||||||||
Done Inline Actions
jrtc27: | |||||||||||||
quad_fixup(&sc->info); | |||||||||||||
jrtc27Unsubmitted Not Done Inline ActionsHm, might be a little cleaner if there were a separate fixup_syscall, then this could just take a const struct syscall *. It's not obvious that adding a syscall modifies it. jrtc27: Hm, might be a little cleaner if there were a separate fixup_syscall, then this could just take… | |||||||||||||
arichardsonAuthorUnsubmitted Done Inline ActionsI could move the fixup to the caller, but then I'd need to do it twice. Also sc can't be const anyway due to the assignment. arichardson: I could move the fixup to the caller, but then I'd need to do it twice. Also `sc` can't be… | |||||||||||||
jrtc27Unsubmitted Not Done Inline ActionsRight you need to deal with the slist in it... I'd still prefer to not have a hidden fixup though. jrtc27: Right you need to deal with the slist in it... I'd still prefer to not have a hidden fixup… | |||||||||||||
arichardsonAuthorUnsubmitted Done Inline ActionsThe fixup can only be done once we know the ABI, so I think this is probably the right place to do it. arichardson: The fixup can only be done once we know the ABI, so I think this is probably the right place to… | |||||||||||||
jrtc27Unsubmitted Not Done Inline ActionsWell just like you have void add_syscall(struct procabi *abi, u_int number, struct syscall *sc) you'd have void fixup_syscall(struct procabi *abi, struct syscall *sc). jrtc27: Well just like you have `void add_syscall(struct procabi *abi, u_int number, struct syscall… | |||||||||||||
arichardsonAuthorUnsubmitted Done Inline ActionsYes that would also work, but since both will done at the same time we might as well merge them? arichardson: Yes that would also work, but since both will done at the same time we might as well merge them? | |||||||||||||
#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; | ||||||||||||
} else { | } else { | ||||||||||||
es = malloc(sizeof(*es)); | es = malloc(sizeof(*es)); | ||||||||||||
es->sc = sc; | es->sc = 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); | |||||||||||||
return (sc); | |||||||||||||
} | } | ||||||||||||
/* | /* | ||||||||||||
* If/when the list gets big, it might be desirable to do it | * If/when the list gets big, it might be desirable to do it | ||||||||||||
* as a hash table or binary search. | * as a hash table or binary search. | ||||||||||||
*/ | */ | ||||||||||||
struct syscall * | 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 *name; | const char *name; | ||||||||||||
char *new_name; | char *new_name; | ||||||||||||
u_int i; | u_int i; | ||||||||||||
bool unknown_name = false; | |||||||||||||
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); | name = sysdecode_syscallname(t->proc->abi->abi, number); | ||||||||||||
if (name == NULL) { | if (name == NULL) { | ||||||||||||
asprintf(&new_name, "#%d", number); | asprintf(&new_name, "#%d", number); | ||||||||||||
name = new_name; | unknown_name = true; | ||||||||||||
} else | } else | ||||||||||||
new_name = NULL; | new_name = strdup(name); | ||||||||||||
Done Inline Actions
jhb: | |||||||||||||
Done Inline ActionsI think you probably want this still as style(9) otherwise wants a blank line before the comment, and moving the comment up below the existing blank line seems simpler. jhb: I think you probably want this still as style(9) otherwise wants a blank line before the… | |||||||||||||
Done Inline ActionsSorry, missed that suggestion when updating the review. Will fix before committing. arichardson: Sorry, missed that suggestion when updating the review. Will fix before committing. | |||||||||||||
STAILQ_FOREACH(sc, &syscalls, entries) { | |||||||||||||
if (strcmp(name, sc->name) == 0) { | sc = calloc(1, sizeof(*sc)); | ||||||||||||
add_syscall(t->proc->abi, number, sc); | sc->kernel_name = new_name; | ||||||||||||
Done Inline ActionsWhy does this have to be allocated? There's no reason to ever free() these things vs just letting exit() reclaim them that I can think of? jhb: Why does this have to be allocated? There's no reason to ever free() these things vs just… | |||||||||||||
Done Inline ActionsGood catch, it was being free()'d unconditionally in a previous version of the patch, so it had to match asprintf(). This is no longer the case so I'll drop the strdup(). arichardson: Good catch, it was being free()'d unconditionally in a previous version of the patch, so it had… | |||||||||||||
free(new_name); | |||||||||||||
return (sc); | for (i = 0; i < nitems(decoded_syscalls); i++) { | ||||||||||||
Done Inline ActionsThis comment probably wants to cover the asprintf vs assigning as non-owned string literal case above too? jrtc27: This comment probably wants to cover the asprintf vs assigning as non-owned string literal case… | |||||||||||||
if (strcmp(new_name, decoded_syscalls[i].name) == 0) { | |||||||||||||
sc->info = decoded_syscalls[i]; | |||||||||||||
return (add_syscall(t->proc->abi, number, sc)); | |||||||||||||
} | } | ||||||||||||
} | } | ||||||||||||
/* It is unknown. Add it into the list. */ | /* It is unknown. Add it into the list. */ | ||||||||||||
#if DEBUG | #if DEBUG | ||||||||||||
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 = calloc(1, sizeof(struct syscall)); | * Note: info.name is either a constant or points to sc->kernel_name | ||||||||||||
Done Inline ActionsMaybe we can just leave this NULL in this case rather than filling it with a potentially-wrong value given using this field is an error? jrtc27: Maybe we can just leave this NULL in this case rather than filling it with a potentially-wrong… | |||||||||||||
sc->name = name; | * which be free'd toger with sc on exit. | ||||||||||||
if (new_name != NULL) | */ | ||||||||||||
sc->unknown = true; | sc->info.name = new_name; | ||||||||||||
sc->ret_type = 1; | sc->info.unknown = unknown_name; | ||||||||||||
sc->nargs = nargs; | sc->info.ret_type = 1; /* Assume 1 return value. */ | ||||||||||||
sc->info.nargs = nargs; | |||||||||||||
Done Inline ActionsDon't need the variable just check name == NULL. jrtc27: Don't need the variable just check `name == NULL`. | |||||||||||||
for (i = 0; i < nargs; i++) { | for (i = 0; i < nargs; i++) { | ||||||||||||
sc->args[i].offset = i; | sc->info.args[i].offset = i; | ||||||||||||
/* Treat all unknown arguments as LongHex. */ | /* Treat all unknown arguments as LongHex. */ | ||||||||||||
sc->args[i].type = LongHex; | sc->info.args[i].type = LongHex; | ||||||||||||
} | } | ||||||||||||
STAILQ_INSERT_HEAD(&syscalls, sc, entries); | return (add_syscall(t->proc->abi, number, sc)); | ||||||||||||
add_syscall(t->proc->abi, number, sc); | |||||||||||||
return (sc); | |||||||||||||
} | } | ||||||||||||
/* | /* | ||||||||||||
* Copy a fixed amount of bytes from the process. | * Copy a fixed amount of bytes from the process. | ||||||||||||
*/ | */ | ||||||||||||
static int | static int | ||||||||||||
get_struct(pid_t pid, uintptr_t offset, void *buf, int len) | get_struct(pid_t pid, uintptr_t offset, void *buf, int len) | ||||||||||||
{ | { | ||||||||||||
▲ Show 20 Lines • Show All 599 Lines • ▼ Show 20 Lines | |||||||||||||
/* | /* | ||||||||||||
* Converts a syscall argument into a string. Said string is | * Converts a syscall argument into a string. Said string is | ||||||||||||
* allocated via malloc(), so needs to be free()'d. sc is | * allocated via malloc(), so needs to be free()'d. sc is | ||||||||||||
* a pointer to the syscall description (see above); args is | * a pointer to the syscall description (see above); args is | ||||||||||||
* 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; | ||||||||||||
char *tmp; | char *tmp; | ||||||||||||
size_t tmplen; | size_t tmplen; | ||||||||||||
pid_t pid; | pid_t pid; | ||||||||||||
fp = open_memstream(&tmp, &tmplen); | fp = open_memstream(&tmp, &tmplen); | ||||||||||||
▲ Show 20 Lines • Show All 1,179 Lines • ▼ Show 20 Lines | |||||||||||||
{ | { | ||||||||||||
struct threadinfo *t; | struct threadinfo *t; | ||||||||||||
const char *name; | const char *name; | ||||||||||||
char **s_args; | char **s_args; | ||||||||||||
int i, len, nargs; | int i, len, nargs; | ||||||||||||
t = trussinfo->curthread; | t = trussinfo->curthread; | ||||||||||||
name = t->cs.sc->name; | name = t->cs.sc->kernel_name; | ||||||||||||
nargs = t->cs.nargs; | nargs = t->cs.nargs; | ||||||||||||
s_args = t->cs.s_args; | s_args = t->cs.s_args; | ||||||||||||
len = print_line_prefix(trussinfo); | len = print_line_prefix(trussinfo); | ||||||||||||
len += fprintf(trussinfo->outfile, "%s(", name); | len += fprintf(trussinfo->outfile, "%s(", name); | ||||||||||||
for (i = 0; i < nargs; i++) { | for (i = 0; i < nargs; i++) { | ||||||||||||
if (s_args[i] != NULL) | if (s_args[i] != NULL) | ||||||||||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | print_syscall_ret(struct trussinfo *trussinfo, int error, register_t *retval) | ||||||||||||
else if (error == EJUSTRETURN) | else if (error == EJUSTRETURN) | ||||||||||||
fprintf(trussinfo->outfile, " EJUSTRETURN\n"); | fprintf(trussinfo->outfile, " EJUSTRETURN\n"); | ||||||||||||
else if (error != 0) { | else if (error != 0) { | ||||||||||||
fprintf(trussinfo->outfile, " ERR#%d '%s'\n", | fprintf(trussinfo->outfile, " ERR#%d '%s'\n", | ||||||||||||
sysdecode_freebsd_to_abi_errno(t->proc->abi->abi, error), | sysdecode_freebsd_to_abi_errno(t->proc->abi->abi, error), | ||||||||||||
strerror(error)); | strerror(error)); | ||||||||||||
} | } | ||||||||||||
#ifndef __LP64__ | #ifndef __LP64__ | ||||||||||||
else if (sc->ret_type == 2) { | else if (sc->info.ret_type == 2) { | ||||||||||||
off_t off; | off_t off; | ||||||||||||
#if _BYTE_ORDER == _LITTLE_ENDIAN | #if _BYTE_ORDER == _LITTLE_ENDIAN | ||||||||||||
off = (off_t)retval[1] << 32 | retval[0]; | off = (off_t)retval[1] << 32 | retval[0]; | ||||||||||||
#else | #else | ||||||||||||
off = (off_t)retval[0] << 32 | retval[1]; | off = (off_t)retval[0] << 32 | retval[1]; | ||||||||||||
#endif | #endif | ||||||||||||
fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off, | fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", (intmax_t)off, | ||||||||||||
(intmax_t)off); | (intmax_t)off); | ||||||||||||
} | } | ||||||||||||
#endif | #endif | ||||||||||||
else | else | ||||||||||||
fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", | fprintf(trussinfo->outfile, " = %jd (0x%jx)\n", | ||||||||||||
(intmax_t)retval[0], (intmax_t)retval[0]); | (intmax_t)retval[0], (intmax_t)retval[0]); | ||||||||||||
} | } | ||||||||||||
void | void | ||||||||||||
print_summary(struct trussinfo *trussinfo) | print_summary(struct trussinfo *trussinfo, bool free_sc) | ||||||||||||
jrtc27Unsubmitted Done Inline ActionsWhy does this new argument exist? The function only gets called once AFAICT with the value true. It'd probably just be nicer to split out a separate function, or just forego it entirely and note that it's fine because we're about to return from main anyway. jrtc27: Why does this new argument exist? The function only gets called once AFAICT with the value true. | |||||||||||||
{ | { | ||||||||||||
struct timespec total = {0, 0}; | struct timespec total = {0, 0}; | ||||||||||||
struct syscall *sc; | struct syscall *sc; | ||||||||||||
struct syscall *sc_temp; | |||||||||||||
int ncall, nerror; | int ncall, nerror; | ||||||||||||
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_SAFE(sc, &seen_syscalls, entries, sc_temp) { | ||||||||||||
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->kernel_name, (intmax_t)sc->time.tv_sec, | ||||||||||||
sc->time.tv_nsec, sc->ncalls, sc->nerror); | sc->time.tv_nsec, sc->ncalls, sc->nerror); | ||||||||||||
timespecadd(&total, &sc->time, &total); | timespecadd(&total, &sc->time, &total); | ||||||||||||
ncall += sc->ncalls; | ncall += sc->ncalls; | ||||||||||||
nerror += sc->nerror; | nerror += sc->nerror; | ||||||||||||
} | |||||||||||||
if (free_sc) { | |||||||||||||
STAILQ_REMOVE(&seen_syscalls, sc, syscall, entries); | |||||||||||||
free(sc->kernel_name); | |||||||||||||
free(sc); | |||||||||||||
} | |||||||||||||
} | } | ||||||||||||
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", | ||||||||||||
"", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); | "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); | ||||||||||||
} | } |
Note that it is mostly sorted, it's just sorted by ABI first, then sorted alphabetically within the ABI.