#if !defined(__APPLE__)
#include <sys/dtrace.h>
#include <sys/systrace.h>
#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/atomic.h>
#define SYSTRACE_ARTIFICIAL_FRAMES 1
#else
#ifdef KERNEL
#ifndef _KERNEL
#define _KERNEL
#endif
#endif
#define MACH__POSIX_C_SOURCE_PRIVATE 1
#include <kern/thread.h>
#include <mach/thread_status.h>
#if defined(__i386__) || defined (__x86_64__)
#define SYSCALL_CLASS_SHIFT 24
#define SYSCALL_CLASS_MASK (0xFF << SYSCALL_CLASS_SHIFT)
#define SYSCALL_NUMBER_MASK (~SYSCALL_CLASS_MASK)
#define I386_SYSCALL_NUMBER_MASK (0xFFFF)
typedef x86_saved_state_t savearea_t;
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <miscfs/devfs/devfs.h>
#include <sys/dtrace.h>
#include <sys/dtrace_impl.h>
#include "systrace.h"
#include <sys/stat.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/user.h>
#include <machine/pal_routines.h>
#if defined(__i386__) || defined (__x86_64__)
#define SYSTRACE_ARTIFICIAL_FRAMES 2
#define MACHTRACE_ARTIFICIAL_FRAMES 3
#else
#error Unknown Architecture
#endif
#include <sys/sysent.h>
#define sy_callc sy_call
#define NSYSCALL nsysent
extern const char *syscallnames[];
#include <sys/dtrace_glue.h>
#define casptr dtrace_casptr
#define membar_enter dtrace_membar_producer
#define LOADABLE_SYSCALL(a) 0
#define LOADED_SYSCALL(a) 1
extern lck_attr_t* dtrace_lck_attr;
extern lck_grp_t* dtrace_lck_grp;
static lck_mtx_t dtrace_systrace_lock;
systrace_sysent_t *systrace_sysent = NULL;
void (*systrace_probe)(dtrace_id_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
static uint64_t systrace_getarg(void *, dtrace_id_t, void *, int, int);
void
systrace_stub(dtrace_id_t id, uint64_t arg0, uint64_t arg1,
uint64_t arg2, uint64_t arg3, uint64_t arg4)
{
#pragma unused(id,arg0,arg1,arg2,arg3,arg4)
}
int32_t
dtrace_systrace_syscall(struct proc *pp, void *uap, int *rv)
{
boolean_t flavor;
unsigned short code;
systrace_sysent_t *sy;
dtrace_id_t id;
int32_t rval;
#if 0
proc_t *p;
#endif
syscall_arg_t *ip = (syscall_arg_t *)uap;
#if defined(__i386__) || defined (__x86_64__)
#pragma unused(flavor)
{
pal_register_cache_state(current_thread(), VALID);
x86_saved_state_t *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
if (is_saved_state64(tagged_regs)) {
x86_saved_state64_t *regs = saved_state64(tagged_regs);
code = regs->rax & SYSCALL_NUMBER_MASK;
if (code == 0) {
code = regs->rdi;
}
} else {
code = saved_state32(tagged_regs)->eax & I386_SYSCALL_NUMBER_MASK;
if (code == 0) {
vm_offset_t params = (vm_offset_t) (saved_state32(tagged_regs)->uesp + sizeof (int));
code = fuword(params);
}
}
}
#else
#error Unknown Architecture
#endif
sy = (code >= NUM_SYSENT) ? &systrace_sysent[63] : &systrace_sysent[code];
if ((id = sy->stsy_entry) != DTRACE_IDNONE) {
uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
if (uthread)
uthread->t_dtrace_syscall_args = (void *)ip;
if (ip)
(*systrace_probe)(id, *ip, *(ip+1), *(ip+2), *(ip+3), *(ip+4));
else
(*systrace_probe)(id, 0, 0, 0, 0, 0);
if (uthread)
uthread->t_dtrace_syscall_args = (void *)0;
}
#if 0
p = ttoproc(curthread);
mutex_enter(&p->p_lock);
if (curthread->t_dtrace_stop && !curthread->t_lwp->lwp_nostop) {
curthread->t_dtrace_stop = 0;
stop(PR_REQUESTED, 0);
}
mutex_exit(&p->p_lock);
#endif
rval = (*sy->stsy_underlying)(pp, uap, rv);
if ((id = sy->stsy_return) != DTRACE_IDNONE) {
uint64_t munged_rv0, munged_rv1;
uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
if (uthread)
uthread->t_dtrace_errno = rval;
if (rval == ERESTART) {
munged_rv0 = -1LL;
munged_rv1 = -1LL;
} else if (rval != EJUSTRETURN) {
if (rval) {
munged_rv0 = -1LL;
munged_rv1 = -1LL;
} else {
switch (sy->stsy_return_type) {
case _SYSCALL_RET_INT_T:
munged_rv0 = rv[0];
munged_rv1 = rv[1];
break;
case _SYSCALL_RET_UINT_T:
munged_rv0 = ((u_int)rv[0]);
munged_rv1 = ((u_int)rv[1]);
break;
case _SYSCALL_RET_OFF_T:
case _SYSCALL_RET_UINT64_T:
munged_rv0 = *(u_int64_t *)rv;
munged_rv1 = 0LL;
break;
case _SYSCALL_RET_ADDR_T:
case _SYSCALL_RET_SIZE_T:
case _SYSCALL_RET_SSIZE_T:
munged_rv0 = *(user_addr_t *)rv;
munged_rv1 = 0LL;
break;
case _SYSCALL_RET_NONE:
munged_rv0 = 0LL;
munged_rv1 = 0LL;
break;
default:
munged_rv0 = 0LL;
munged_rv1 = 0LL;
break;
}
}
} else {
munged_rv0 = 0LL;
munged_rv1 = 0LL;
}
(*systrace_probe)(id, munged_rv0, munged_rv0, munged_rv1, (uint64_t)rval, 0);
}
return (rval);
}
void
dtrace_systrace_syscall_return(unsigned short code, int rval, int *rv)
{
systrace_sysent_t *sy;
dtrace_id_t id;
sy = (code >= NUM_SYSENT) ? &systrace_sysent[63] : &systrace_sysent[code];
if ((id = sy->stsy_return) != DTRACE_IDNONE) {
uint64_t munged_rv0, munged_rv1;
uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
if (uthread)
uthread->t_dtrace_errno = rval;
if (rval == ERESTART) {
munged_rv0 = -1LL;
munged_rv1 = -1LL;
} else if (rval != EJUSTRETURN) {
if (rval) {
munged_rv0 = -1LL;
munged_rv1 = -1LL;
} else {
switch (sy->stsy_return_type) {
case _SYSCALL_RET_INT_T:
munged_rv0 = rv[0];
munged_rv1 = rv[1];
break;
case _SYSCALL_RET_UINT_T:
munged_rv0 = ((u_int)rv[0]);
munged_rv1 = ((u_int)rv[1]);
break;
case _SYSCALL_RET_OFF_T:
case _SYSCALL_RET_UINT64_T:
munged_rv0 = *(u_int64_t *)rv;
munged_rv1 = 0LL;
break;
case _SYSCALL_RET_ADDR_T:
case _SYSCALL_RET_SIZE_T:
case _SYSCALL_RET_SSIZE_T:
munged_rv0 = *(user_addr_t *)rv;
munged_rv1 = 0LL;
break;
case _SYSCALL_RET_NONE:
munged_rv0 = 0LL;
munged_rv1 = 0LL;
break;
default:
munged_rv0 = 0LL;
munged_rv1 = 0LL;
break;
}
}
} else {
munged_rv0 = 0LL;
munged_rv1 = 0LL;
}
(*systrace_probe)(id, munged_rv0, munged_rv0, munged_rv1, (uint64_t)rval, 0);
}
}
#endif
#define SYSTRACE_SHIFT 16
#define SYSTRACE_ISENTRY(x) ((int)(x) >> SYSTRACE_SHIFT)
#define SYSTRACE_SYSNUM(x) ((int)(x) & ((1 << SYSTRACE_SHIFT) - 1))
#define SYSTRACE_ENTRY(id) ((1 << SYSTRACE_SHIFT) | (id))
#define SYSTRACE_RETURN(id) (id)
#if ((1 << SYSTRACE_SHIFT) <= NSYSCALL)
#error 1 << SYSTRACE_SHIFT must exceed number of system calls
#endif
static dev_info_t *systrace_devi;
static dtrace_provider_id_t systrace_id;
#if !defined (__APPLE__)
static void
systrace_init(struct sysent *actual, systrace_sysent_t **interposed)
{
systrace_sysent_t *sysent = *interposed;
int i;
if (sysent == NULL) {
*interposed = sysent = kmem_zalloc(sizeof (systrace_sysent_t) *
NSYSCALL, KM_SLEEP);
}
for (i = 0; i < NSYSCALL; i++) {
struct sysent *a = &actual[i];
systrace_sysent_t *s = &sysent[i];
if (LOADABLE_SYSCALL(a) && !LOADED_SYSCALL(a))
continue;
if (a->sy_callc == dtrace_systrace_syscall)
continue;
#ifdef _SYSCALL32_IMPL
if (a->sy_callc == dtrace_systrace_syscall32)
continue;
#endif
s->stsy_underlying = a->sy_callc;
}
}
#else
#define systrace_init _systrace_init
static void
systrace_init(struct sysent *actual, systrace_sysent_t **interposed)
{
systrace_sysent_t *ssysent = *interposed;
int i;
if (ssysent == NULL) {
*interposed = ssysent = kmem_zalloc(sizeof (systrace_sysent_t) *
NSYSCALL, KM_SLEEP);
}
for (i = 0; i < NSYSCALL; i++) {
struct sysent *a = &actual[i];
systrace_sysent_t *s = &ssysent[i];
if (LOADABLE_SYSCALL(a) && !LOADED_SYSCALL(a))
continue;
if (a->sy_callc == dtrace_systrace_syscall)
continue;
#ifdef _SYSCALL32_IMPL
if (a->sy_callc == dtrace_systrace_syscall32)
continue;
#endif
s->stsy_underlying = a->sy_callc;
s->stsy_return_type = a->sy_return_type;
}
lck_mtx_init(&dtrace_systrace_lock, dtrace_lck_grp, dtrace_lck_attr);
}
#endif
static void
systrace_provide(void *arg, const dtrace_probedesc_t *desc)
{
#pragma unused(arg)
int i;
if (desc != NULL)
return;
systrace_init(sysent, &systrace_sysent);
#ifdef _SYSCALL32_IMPL
systrace_init(sysent32, &systrace_sysent32);
#endif
for (i = 0; i < NSYSCALL; i++) {
if (systrace_sysent[i].stsy_underlying == NULL)
continue;
if (dtrace_probe_lookup(systrace_id, NULL,
syscallnames[i], "entry") != 0)
continue;
(void) dtrace_probe_create(systrace_id, NULL, syscallnames[i],
"entry", SYSTRACE_ARTIFICIAL_FRAMES,
(void *)((uintptr_t)SYSTRACE_ENTRY(i)));
(void) dtrace_probe_create(systrace_id, NULL, syscallnames[i],
"return", SYSTRACE_ARTIFICIAL_FRAMES,
(void *)((uintptr_t)SYSTRACE_RETURN(i)));
systrace_sysent[i].stsy_entry = DTRACE_IDNONE;
systrace_sysent[i].stsy_return = DTRACE_IDNONE;
#ifdef _SYSCALL32_IMPL
systrace_sysent32[i].stsy_entry = DTRACE_IDNONE;
systrace_sysent32[i].stsy_return = DTRACE_IDNONE;
#endif
}
}
#if defined(__APPLE__)
#undef systrace_init
#endif
static void
systrace_destroy(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
#pragma unused(sysnum)
if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
ASSERT(systrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE);
#ifdef _SYSCALL32_IMPL
ASSERT(systrace_sysent32[sysnum].stsy_entry == DTRACE_IDNONE);
#endif
} else {
ASSERT(systrace_sysent[sysnum].stsy_return == DTRACE_IDNONE);
#ifdef _SYSCALL32_IMPL
ASSERT(systrace_sysent32[sysnum].stsy_return == DTRACE_IDNONE);
#endif
}
}
static int
systrace_enable(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg)
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
int enabled = (systrace_sysent[sysnum].stsy_entry != DTRACE_IDNONE ||
systrace_sysent[sysnum].stsy_return != DTRACE_IDNONE);
if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
systrace_sysent[sysnum].stsy_entry = id;
#ifdef _SYSCALL32_IMPL
systrace_sysent32[sysnum].stsy_entry = id;
#endif
} else {
systrace_sysent[sysnum].stsy_return = id;
#ifdef _SYSCALL32_IMPL
systrace_sysent32[sysnum].stsy_return = id;
#endif
}
if (enabled) {
ASSERT(sysent[sysnum].sy_callc == dtrace_systrace_syscall);
return(0);
}
#ifdef _SYSCALL32_IMPL
(void) casptr(&sysent32[sysnum].sy_callc,
(void *)systrace_sysent32[sysnum].stsy_underlying,
(void *)dtrace_systrace_syscall32);
#endif
lck_mtx_lock(&dtrace_systrace_lock);
if (sysent[sysnum].sy_callc == systrace_sysent[sysnum].stsy_underlying) {
vm_offset_t dss = (vm_offset_t)&dtrace_systrace_syscall;
ml_nofault_copy((vm_offset_t)&dss, (vm_offset_t)&sysent[sysnum].sy_callc, sizeof(vm_offset_t));
}
lck_mtx_unlock(&dtrace_systrace_lock);
return (0);
}
static void
systrace_disable(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
int disable = (systrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE ||
systrace_sysent[sysnum].stsy_return == DTRACE_IDNONE);
if (disable) {
lck_mtx_lock(&dtrace_systrace_lock);
if (sysent[sysnum].sy_callc == dtrace_systrace_syscall)
ml_nofault_copy((vm_offset_t)&systrace_sysent[sysnum].stsy_underlying, (vm_offset_t)&sysent[sysnum].sy_callc, sizeof(systrace_sysent[sysnum].stsy_underlying));
lck_mtx_unlock(&dtrace_systrace_lock);
#ifdef _SYSCALL32_IMPL
(void) casptr(&sysent32[sysnum].sy_callc,
(void *)dtrace_systrace_syscall32,
(void *)systrace_sysent32[sysnum].stsy_underlying);
#endif
}
if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
systrace_sysent[sysnum].stsy_entry = DTRACE_IDNONE;
#ifdef _SYSCALL32_IMPL
systrace_sysent32[sysnum].stsy_entry = DTRACE_IDNONE;
#endif
} else {
systrace_sysent[sysnum].stsy_return = DTRACE_IDNONE;
#ifdef _SYSCALL32_IMPL
systrace_sysent32[sysnum].stsy_return = DTRACE_IDNONE;
#endif
}
}
static dtrace_pattr_t systrace_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};
static dtrace_pops_t systrace_pops = {
systrace_provide,
NULL,
systrace_enable,
systrace_disable,
NULL,
NULL,
NULL,
systrace_getarg,
NULL,
systrace_destroy
};
static int
systrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
#if !defined(__APPLE__)
systrace_probe = (void (*)())dtrace_probe;
membar_enter();
if (ddi_create_minor_node(devi, "systrace", S_IFCHR, 0,
DDI_PSEUDO, NULL) == DDI_FAILURE ||
dtrace_register("syscall", &systrace_attr, DTRACE_PRIV_USER, NULL,
&systrace_pops, NULL, &systrace_id) != 0) {
systrace_probe = systrace_stub;
ddi_remove_minor_node(devi, NULL);
return (DDI_FAILURE);
}
#else
systrace_probe = (void(*))&dtrace_probe;
membar_enter();
if (ddi_create_minor_node(devi, "systrace", S_IFCHR, 0,
DDI_PSEUDO, 0) == DDI_FAILURE ||
dtrace_register("syscall", &systrace_attr, DTRACE_PRIV_USER, NULL,
&systrace_pops, NULL, &systrace_id) != 0) {
systrace_probe = systrace_stub;
ddi_remove_minor_node(devi, NULL);
return (DDI_FAILURE);
}
#endif
ddi_report_dev(devi);
systrace_devi = devi;
return (DDI_SUCCESS);
}
#if !defined(__APPLE__)
static int
systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (dtrace_unregister(systrace_id) != 0)
return (DDI_FAILURE);
ddi_remove_minor_node(devi, NULL);
systrace_probe = systrace_stub;
return (DDI_SUCCESS);
}
static int
systrace_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = (void *)systrace_devi;
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
static int
systrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
{
return (0);
}
static struct cb_ops systrace_cb_ops = {
systrace_open,
nodev,
nulldev,
nulldev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
0,
D_NEW | D_MP
};
static struct dev_ops systrace_ops = {
DEVO_REV,
0,
systrace_info,
nulldev,
nulldev,
systrace_attach,
systrace_detach,
nodev,
&systrace_cb_ops,
NULL,
nodev
};
static struct modldrv modldrv = {
&mod_driverops,
"System Call Tracing",
&systrace_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
#else
typedef kern_return_t (*mach_call_t)(void *);
typedef void mach_munge_t(const void *, void *);
typedef struct {
int mach_trap_arg_count;
kern_return_t (*mach_trap_function)(void *);
#if 0
mach_munge_t *mach_trap_arg_munge32;
mach_munge_t *mach_trap_arg_munge64;
#endif
#if MACH_ASSERT
const char* mach_trap_name;
#endif
} mach_trap_t;
extern mach_trap_t mach_trap_table[];
extern int mach_trap_count;
extern const char *mach_syscall_name_table[];
struct mach_call_args {
syscall_arg_t arg1;
syscall_arg_t arg2;
syscall_arg_t arg3;
syscall_arg_t arg4;
syscall_arg_t arg5;
syscall_arg_t arg6;
syscall_arg_t arg7;
syscall_arg_t arg8;
syscall_arg_t arg9;
};
#undef NSYSCALL
#define NSYSCALL mach_trap_count
#if ((1 << SYSTRACE_SHIFT) <= NSYSCALL)
#error 1 << SYSTRACE_SHIFT must exceed number of Mach traps
#endif
typedef struct machtrace_sysent {
dtrace_id_t stsy_entry;
dtrace_id_t stsy_return;
kern_return_t (*stsy_underlying)(void *);
int32_t stsy_return_type;
} machtrace_sysent_t;
static machtrace_sysent_t *machtrace_sysent = NULL;
void (*machtrace_probe)(dtrace_id_t, uint64_t, uint64_t,
uint64_t, uint64_t, uint64_t);
static uint64_t machtrace_getarg(void *, dtrace_id_t, void *, int, int);
static dev_info_t *machtrace_devi;
static dtrace_provider_id_t machtrace_id;
static kern_return_t
dtrace_machtrace_syscall(struct mach_call_args *args)
{
boolean_t flavor;
unsigned short code;
machtrace_sysent_t *sy;
dtrace_id_t id;
kern_return_t rval;
#if 0
proc_t *p;
#endif
syscall_arg_t *ip = (syscall_arg_t *)args;
mach_call_t mach_call;
#if defined(__i386__) || defined (__x86_64__)
#pragma unused(flavor)
{
pal_register_cache_state(current_thread(), VALID);
x86_saved_state_t *tagged_regs = (x86_saved_state_t *)find_user_regs(current_thread());
if (is_saved_state64(tagged_regs)) {
code = saved_state64(tagged_regs)->rax & SYSCALL_NUMBER_MASK;
} else {
code = -saved_state32(tagged_regs)->eax;
}
}
#else
#error Unknown Architecture
#endif
sy = &machtrace_sysent[code];
if ((id = sy->stsy_entry) != DTRACE_IDNONE) {
uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
if (uthread)
uthread->t_dtrace_syscall_args = (void *)ip;
(*machtrace_probe)(id, *ip, *(ip+1), *(ip+2), *(ip+3), *(ip+4));
if (uthread)
uthread->t_dtrace_syscall_args = (void *)0;
}
#if 0
p = ttoproc(curthread);
mutex_enter(&p->p_lock);
if (curthread->t_dtrace_stop && !curthread->t_lwp->lwp_nostop) {
curthread->t_dtrace_stop = 0;
stop(PR_REQUESTED, 0);
}
mutex_exit(&p->p_lock);
#endif
mach_call = (mach_call_t)(*sy->stsy_underlying);
rval = mach_call(args);
if ((id = sy->stsy_return) != DTRACE_IDNONE)
(*machtrace_probe)(id, (uint64_t)rval, 0, 0, 0, 0);
return (rval);
}
static void
machtrace_init(mach_trap_t *actual, machtrace_sysent_t **interposed)
{
machtrace_sysent_t *msysent = *interposed;
int i;
if (msysent == NULL) {
*interposed = msysent = kmem_zalloc(sizeof (machtrace_sysent_t) *
NSYSCALL, KM_SLEEP);
}
for (i = 0; i < NSYSCALL; i++) {
mach_trap_t *a = &actual[i];
machtrace_sysent_t *s = &msysent[i];
if (LOADABLE_SYSCALL(a) && !LOADED_SYSCALL(a))
continue;
if (a->mach_trap_function == (mach_call_t)(dtrace_machtrace_syscall))
continue;
s->stsy_underlying = a->mach_trap_function;
}
}
static void
machtrace_provide(void *arg, const dtrace_probedesc_t *desc)
{
#pragma unused(arg)
int i;
if (desc != NULL)
return;
machtrace_init(mach_trap_table, &machtrace_sysent);
for (i = 0; i < NSYSCALL; i++) {
if (machtrace_sysent[i].stsy_underlying == NULL)
continue;
if (dtrace_probe_lookup(machtrace_id, NULL,
mach_syscall_name_table[i], "entry") != 0)
continue;
(void) dtrace_probe_create(machtrace_id, NULL, mach_syscall_name_table[i],
"entry", MACHTRACE_ARTIFICIAL_FRAMES,
(void *)((uintptr_t)SYSTRACE_ENTRY(i)));
(void) dtrace_probe_create(machtrace_id, NULL, mach_syscall_name_table[i],
"return", MACHTRACE_ARTIFICIAL_FRAMES,
(void *)((uintptr_t)SYSTRACE_RETURN(i)));
machtrace_sysent[i].stsy_entry = DTRACE_IDNONE;
machtrace_sysent[i].stsy_return = DTRACE_IDNONE;
}
}
static void
machtrace_destroy(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
#pragma unused(sysnum)
if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
ASSERT(machtrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE);
} else {
ASSERT(machtrace_sysent[sysnum].stsy_return == DTRACE_IDNONE);
}
}
static int
machtrace_enable(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg)
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
int enabled = (machtrace_sysent[sysnum].stsy_entry != DTRACE_IDNONE ||
machtrace_sysent[sysnum].stsy_return != DTRACE_IDNONE);
if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
machtrace_sysent[sysnum].stsy_entry = id;
} else {
machtrace_sysent[sysnum].stsy_return = id;
}
if (enabled) {
ASSERT(mach_trap_table[sysnum].mach_trap_function == (void *)dtrace_machtrace_syscall);
return(0);
}
lck_mtx_lock(&dtrace_systrace_lock);
if (mach_trap_table[sysnum].mach_trap_function == machtrace_sysent[sysnum].stsy_underlying) {
vm_offset_t dss = (vm_offset_t)&dtrace_machtrace_syscall;
ml_nofault_copy((vm_offset_t)&dss, (vm_offset_t)&mach_trap_table[sysnum].mach_trap_function, sizeof(vm_offset_t));
}
lck_mtx_unlock(&dtrace_systrace_lock);
return(0);
}
static void
machtrace_disable(void *arg, dtrace_id_t id, void *parg)
{
#pragma unused(arg,id)
int sysnum = SYSTRACE_SYSNUM((uintptr_t)parg);
int disable = (machtrace_sysent[sysnum].stsy_entry == DTRACE_IDNONE ||
machtrace_sysent[sysnum].stsy_return == DTRACE_IDNONE);
if (disable) {
lck_mtx_lock(&dtrace_systrace_lock);
if (mach_trap_table[sysnum].mach_trap_function == (mach_call_t)dtrace_machtrace_syscall) {
ml_nofault_copy((vm_offset_t)&machtrace_sysent[sysnum].stsy_underlying, (vm_offset_t)&mach_trap_table[sysnum].mach_trap_function, sizeof(vm_offset_t));
}
lck_mtx_unlock(&dtrace_systrace_lock);
}
if (SYSTRACE_ISENTRY((uintptr_t)parg)) {
machtrace_sysent[sysnum].stsy_entry = DTRACE_IDNONE;
} else {
machtrace_sysent[sysnum].stsy_return = DTRACE_IDNONE;
}
}
static dtrace_pattr_t machtrace_attr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};
static dtrace_pops_t machtrace_pops = {
machtrace_provide,
NULL,
machtrace_enable,
machtrace_disable,
NULL,
NULL,
NULL,
machtrace_getarg,
NULL,
machtrace_destroy
};
static int
machtrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
#if !defined(__APPLE__)
machtrace_probe = (void (*)())dtrace_probe;
membar_enter();
if (ddi_create_minor_node(devi, "machtrace", S_IFCHR, 0,
DDI_PSEUDO, NULL) == DDI_FAILURE ||
dtrace_register("mach_trap", &machtrace_attr, DTRACE_PRIV_USER, NULL,
&machtrace_pops, NULL, &machtrace_id) != 0) {
machtrace_probe = systrace_stub;
#else
machtrace_probe = dtrace_probe;
membar_enter();
if (ddi_create_minor_node(devi, "machtrace", S_IFCHR, 0,
DDI_PSEUDO, 0) == DDI_FAILURE ||
dtrace_register("mach_trap", &machtrace_attr, DTRACE_PRIV_USER, NULL,
&machtrace_pops, NULL, &machtrace_id) != 0) {
machtrace_probe = (void (*))&systrace_stub;
#endif
ddi_remove_minor_node(devi, NULL);
return (DDI_FAILURE);
}
ddi_report_dev(devi);
machtrace_devi = devi;
return (DDI_SUCCESS);
}
d_open_t _systrace_open;
int _systrace_open(dev_t dev, int flags, int devtype, struct proc *p)
{
#pragma unused(dev,flags,devtype,p)
return 0;
}
#define SYSTRACE_MAJOR -24
static struct cdevsw systrace_cdevsw =
{
_systrace_open,
eno_opcl,
eno_rdwrt,
eno_rdwrt,
eno_ioctl,
(stop_fcn_t *)nulldev,
(reset_fcn_t *)nulldev,
NULL,
eno_select,
eno_mmap,
eno_strat,
eno_getc,
eno_putc,
0
};
static int gSysTraceInited = 0;
void systrace_init( void );
void systrace_init( void )
{
if (0 == gSysTraceInited) {
int majdevno = cdevsw_add(SYSTRACE_MAJOR, &systrace_cdevsw);
if (majdevno < 0) {
printf("systrace_init: failed to allocate a major number!\n");
gSysTraceInited = 0;
return;
}
systrace_attach( (dev_info_t *)(uintptr_t)majdevno, DDI_ATTACH );
machtrace_attach( (dev_info_t *)(uintptr_t)majdevno, DDI_ATTACH );
gSysTraceInited = 1;
} else
panic("systrace_init: called twice!\n");
}
#undef SYSTRACE_MAJOR
#endif
static uint64_t
systrace_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes)
{
#pragma unused(arg,id,parg,aframes)
uint64_t val = 0;
syscall_arg_t *stack = (syscall_arg_t *)NULL;
uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
if (uthread)
stack = (syscall_arg_t *)uthread->t_dtrace_syscall_args;
if (!stack)
return(0);
DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
val = (uint64_t)*(stack+argno);
DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
return (val);
}
static uint64_t
machtrace_getarg(void *arg, dtrace_id_t id, void *parg, int argno, int aframes)
{
#pragma unused(arg,id,parg,aframes)
uint64_t val = 0;
syscall_arg_t *stack = (syscall_arg_t *)NULL;
uthread_t uthread = (uthread_t)get_bsdthread_info(current_thread());
if (uthread)
stack = (syscall_arg_t *)uthread->t_dtrace_syscall_args;
if (!stack)
return(0);
DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
val = (uint64_t)*(stack+argno);
DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
return (val);
}