Support an arbitrary number of arguments to DTrace syscall probes.

Rather than pushing all eight possible arguments into dtrace_probe()'s
stack frame, make the syscall_args struct for the current syscall available
via the current thread. Using a custom getargval method for the systrace
provider, this allows any syscall argument to be fetched, even in kernels
that have modified the maximum number of system call arguments.

Sponsored by:	EMC / Isilon Storage Division
This commit is contained in:
Mark Johnston 2015-12-17 00:00:27 +00:00
parent f17f88d3e0
commit 8ff6d9dd22
4 changed files with 68 additions and 62 deletions

View File

@ -83,8 +83,8 @@ typedef struct kdtrace_thread {
uintptr_t td_dtrace_regv;
#endif
u_int64_t td_hrtime; /* Last time on cpu. */
int td_errno; /* Syscall return value. */
void *td_dtrace_sscr; /* Saved scratch space location. */
void *td_systrace_args; /* syscall probe arguments. */
} kdtrace_thread_t;
/*
@ -110,6 +110,7 @@ typedef struct kdtrace_thread {
#define t_dtrace_astpc td_dtrace->td_dtrace_astpc
#define t_dtrace_regv td_dtrace->td_dtrace_regv
#define t_dtrace_sscr td_dtrace->td_dtrace_sscr
#define t_dtrace_systrace_args td_dtrace->td_systrace_args
#define p_dtrace_helpers p_dtrace->p_dtrace_helpers
#define p_dtrace_count p_dtrace->p_dtrace_count
#define p_dtrace_probes p_dtrace->p_dtrace_probes

View File

@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/cpuvar.h>
#include <sys/dtrace.h>
#include <sys/fcntl.h>
#include <sys/filio.h>
#include <sys/kdb.h>
@ -53,9 +54,10 @@ __FBSDID("$FreeBSD$");
#include <sys/sysproto.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <machine/stdarg.h>
#include <sys/dtrace.h>
#include <cddl/dev/dtrace/dtrace_cddl.h>
#include <machine/stdarg.h>
#ifdef LINUX_SYSTRACE
#if defined(__amd64__)
@ -138,6 +140,7 @@ static void systrace_unload(void *);
static void systrace_getargdesc(void *, dtrace_id_t, void *,
dtrace_argdesc_t *);
static uint64_t systrace_getargval(void *, dtrace_id_t, void *, int, int);
static void systrace_provide(void *, dtrace_probedesc_t *);
static void systrace_destroy(void *, dtrace_id_t, void *);
static void systrace_enable(void *, dtrace_id_t, void *);
@ -164,16 +167,13 @@ static dtrace_pops_t systrace_pops = {
NULL,
NULL,
systrace_getargdesc,
NULL,
systrace_getargval,
NULL,
systrace_destroy
};
static dtrace_provider_id_t systrace_id;
typedef void (*systrace_dtrace_probe_t)(dtrace_id_t, uintptr_t, uintptr_t,
uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
#ifdef NATIVE_ABI
/*
* Probe callback function.
@ -183,48 +183,48 @@ typedef void (*systrace_dtrace_probe_t)(dtrace_id_t, uintptr_t, uintptr_t,
* compat syscall from something like Linux.
*/
static void
systrace_probe(uint32_t id, int sysnum, struct sysent *sysent, void *params,
int ret)
systrace_probe(struct syscall_args *sa, enum systrace_probe_t type, int retval)
{
uint64_t uargs[8];
systrace_dtrace_probe_t probe;
int n_args = 0;
uint64_t uargs[nitems(sa->args)];
dtrace_id_t id;
int n_args, sysnum;
sysnum = sa->code;
memset(uargs, 0, sizeof(uargs));
/*
* Check if this syscall has an argument conversion function
* registered.
*/
if (params != NULL && sysent->sy_systrace_args_func != NULL) {
if (type == SYSTRACE_ENTRY) {
id = sa->callp->sy_entry;
if (sa->callp->sy_systrace_args_func != NULL)
/*
* Convert the syscall parameters using the registered
* function.
*/
(*sa->callp->sy_systrace_args_func)(sysnum, sa->args,
uargs, &n_args);
else
/*
* Use the built-in system call argument conversion
* function to translate the syscall structure fields
* into the array of 64-bit values that DTrace expects.
*/
systrace_args(sysnum, sa->args, uargs, &n_args);
/*
* Convert the syscall parameters using the registered
* function.
* Save probe arguments now so that we can retrieve them if
* the getargval method is called from further down the stack.
*/
(*sysent->sy_systrace_args_func)(sysnum, params, uargs,
&n_args);
} else if (params != NULL) {
/*
* Use the built-in system call argument conversion
* function to translate the syscall structure fields
* into the array of 64-bit values that DTrace
* expects.
*/
systrace_args(sysnum, params, uargs, &n_args);
curthread->t_dtrace_systrace_args = uargs;
} else {
/*
* Since params is NULL, this is a 'return' probe.
* Set arg0 and arg1 as the return value of this syscall.
*/
uargs[0] = uargs[1] = ret;
id = sa->callp->sy_return;
curthread->t_dtrace_systrace_args = NULL;
/* Set arg0 and arg1 as the return value of this syscall. */
uargs[0] = uargs[1] = retval;
}
/* Process the probe using the converted argments. */
probe = (systrace_dtrace_probe_t)dtrace_probe;
probe(id, uargs[0], uargs[1], uargs[2], uargs[3], uargs[4], uargs[5],
uargs[6], uargs[7]);
dtrace_probe(id, uargs[0], uargs[1], uargs[2], uargs[3], uargs[4]);
}
#endif
static void
@ -244,6 +244,21 @@ systrace_getargdesc(void *arg, dtrace_id_t id, void *parg,
desc->dtargd_ndx = DTRACE_ARGNONE;
}
static uint64_t
systrace_getargval(void *arg __unused, dtrace_id_t id __unused,
void *parg __unused, int argno, int aframes __unused)
{
uint64_t *uargs;
uargs = curthread->t_dtrace_systrace_args;
if (uargs == NULL)
/* This is a return probe. */
return (0);
if (argno >= nitems(((struct syscall_args *)NULL)->args))
return (0);
return (uargs[argno]);
}
static void
systrace_provide(void *arg, dtrace_probedesc_t *desc)
{

View File

@ -126,14 +126,9 @@ syscallenter(struct thread *td, struct syscall_args *sa)
goto retval;
#ifdef KDTRACE_HOOKS
/*
* If the systrace module has registered it's probe
* callback and if there is a probe active for the
* syscall 'entry', process the probe.
*/
/* Give the syscall:::entry DTrace probe a chance to fire. */
if (systrace_probe_func != NULL && sa->callp->sy_entry != 0)
(*systrace_probe_func)(sa->callp->sy_entry, sa->code,
sa->callp, sa->args, 0);
(*systrace_probe_func)(sa, SYSTRACE_ENTRY, 0);
#endif
AUDIT_SYSCALL_ENTER(sa->code, td);
@ -145,14 +140,10 @@ syscallenter(struct thread *td, struct syscall_args *sa)
td->td_errno = error;
#ifdef KDTRACE_HOOKS
/*
* If the systrace module has registered it's probe
* callback and if there is a probe active for the
* syscall 'return', process the probe.
*/
/* Give the syscall:::return DTrace probe a chance to fire. */
if (systrace_probe_func != NULL && sa->callp->sy_return != 0)
(*systrace_probe_func)(sa->callp->sy_return, sa->code,
sa->callp, NULL, (error) ? -1 : td->td_retval[0]);
(*systrace_probe_func)(sa, SYSTRACE_RETURN,
error ? -1 : td->td_retval[0]);
#endif
syscall_thread_exit(td, sa->callp);
}

View File

@ -38,18 +38,18 @@ struct rlimit;
struct sysent;
struct thread;
struct ksiginfo;
struct syscall_args;
enum systrace_probe_t {
SYSTRACE_ENTRY,
SYSTRACE_RETURN,
};
typedef int sy_call_t(struct thread *, void *);
/* Used by the machine dependent syscall() code. */
typedef void (*systrace_probe_func_t)(u_int32_t, int, struct sysent *, void *,
int);
/*
* Used by loaded syscalls to convert arguments to a DTrace array
* of 64-bit arguments.
*/
typedef void (*systrace_args_func_t)(int, void *, u_int64_t *, int *);
typedef void (*systrace_probe_func_t)(struct syscall_args *,
enum systrace_probe_t, int);
typedef void (*systrace_args_func_t)(int, void *, uint64_t *, int *);
extern systrace_probe_func_t systrace_probe_func;
@ -84,7 +84,6 @@ struct sysent { /* system call table */
struct image_params;
struct __sigset;
struct syscall_args;
struct trapframe;
struct vnode;