Changeset View
Changeset View
Standalone View
Standalone View
sys/cddl/dev/fbt/fbt.c
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
#include <sys/sysent.h> | #include <sys/sysent.h> | ||||
#include <sys/sysproto.h> | #include <sys/sysproto.h> | ||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <sys/unistd.h> | #include <sys/unistd.h> | ||||
#include <machine/stdarg.h> | #include <machine/stdarg.h> | ||||
#include <sys/dtrace.h> | #include <sys/dtrace.h> | ||||
#include <sys/dtrace_bsd.h> | #include <sys/dtrace_bsd.h> | ||||
#include <sys/dtrace_impl.h> | |||||
#include "fbt.h" | #include "fbt.h" | ||||
MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing"); | MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing"); | ||||
dtrace_provider_id_t fbt_id; | dtrace_provider_id_t fbt_id; | ||||
fbt_probe_t **fbt_probetab; | fbt_probe_t **fbt_probetab; | ||||
int fbt_probetab_mask; | int fbt_probetab_mask; | ||||
static d_open_t fbt_open; | static d_open_t fbt_open; | ||||
static int fbt_unload(void); | static int fbt_unload(void); | ||||
static void fbt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *); | static void fbt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *); | ||||
static uint64_t fbt_getargval(void *, dtrace_id_t, void *, int, int); | |||||
static void fbt_provide_module(void *, modctl_t *); | static void fbt_provide_module(void *, modctl_t *); | ||||
static void fbt_destroy(void *, dtrace_id_t, void *); | static void fbt_destroy(void *, dtrace_id_t, void *); | ||||
static void fbt_enable(void *, dtrace_id_t, void *); | static void fbt_enable(void *, dtrace_id_t, void *); | ||||
static void fbt_disable(void *, dtrace_id_t, void *); | static void fbt_disable(void *, dtrace_id_t, void *); | ||||
static void fbt_load(void *); | static void fbt_load(void *); | ||||
static void fbt_suspend(void *, dtrace_id_t, void *); | static void fbt_suspend(void *, dtrace_id_t, void *); | ||||
static void fbt_resume(void *, dtrace_id_t, void *); | static void fbt_resume(void *, dtrace_id_t, void *); | ||||
Show All 14 Lines | |||||
static dtrace_pops_t fbt_pops = { | static dtrace_pops_t fbt_pops = { | ||||
.dtps_provide = NULL, | .dtps_provide = NULL, | ||||
.dtps_provide_module = fbt_provide_module, | .dtps_provide_module = fbt_provide_module, | ||||
.dtps_enable = fbt_enable, | .dtps_enable = fbt_enable, | ||||
.dtps_disable = fbt_disable, | .dtps_disable = fbt_disable, | ||||
.dtps_suspend = fbt_suspend, | .dtps_suspend = fbt_suspend, | ||||
.dtps_resume = fbt_resume, | .dtps_resume = fbt_resume, | ||||
.dtps_getargdesc = fbt_getargdesc, | .dtps_getargdesc = fbt_getargdesc, | ||||
.dtps_getargval = NULL, | .dtps_getargval = fbt_getargval, | ||||
.dtps_usermode = NULL, | .dtps_usermode = NULL, | ||||
.dtps_destroy = fbt_destroy | .dtps_destroy = fbt_destroy | ||||
}; | }; | ||||
static struct cdev *fbt_cdev; | static struct cdev *fbt_cdev; | ||||
static int fbt_probetab_size; | static int fbt_probetab_size; | ||||
static int fbt_verbose = 0; | static int fbt_verbose = 0; | ||||
▲ Show 20 Lines • Show All 415 Lines • ▼ Show 20 Lines | for (tp = tbuf; tp < tend; ctf_typemax++) { | ||||
case CTF_K_POINTER: | case CTF_K_POINTER: | ||||
case CTF_K_TYPEDEF: | case CTF_K_TYPEDEF: | ||||
case CTF_K_VOLATILE: | case CTF_K_VOLATILE: | ||||
case CTF_K_CONST: | case CTF_K_CONST: | ||||
case CTF_K_RESTRICT: | case CTF_K_RESTRICT: | ||||
child |= CTF_TYPE_ISCHILD(tp->ctt_type); | child |= CTF_TYPE_ISCHILD(tp->ctt_type); | ||||
vbytes = 0; | vbytes = 0; | ||||
break; | break; | ||||
case CTF_K_CONSTVAL: | |||||
child |= CTF_TYPE_ISCHILD(tp->ctt_type); | |||||
vbytes = vlen; | |||||
break; | |||||
default: | default: | ||||
printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind); | printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind); | ||||
return (EIO); | return (EIO); | ||||
} | } | ||||
tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); | tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); | ||||
pop[kind]++; | pop[kind]++; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | case CTF_K_UNKNOWN: | ||||
break; | break; | ||||
case CTF_K_POINTER: | case CTF_K_POINTER: | ||||
case CTF_K_TYPEDEF: | case CTF_K_TYPEDEF: | ||||
case CTF_K_VOLATILE: | case CTF_K_VOLATILE: | ||||
case CTF_K_CONST: | case CTF_K_CONST: | ||||
case CTF_K_RESTRICT: | case CTF_K_RESTRICT: | ||||
vbytes = 0; | vbytes = 0; | ||||
break; | break; | ||||
case CTF_K_CONSTVAL: | |||||
vbytes = vlen; | |||||
break; | |||||
default: | default: | ||||
printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind); | printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind); | ||||
return (EIO); | return (EIO); | ||||
} | } | ||||
*xp = (uint32_t)((uintptr_t) tp - (uintptr_t) ctfdata); | *xp = (uint32_t)((uintptr_t) tp - (uintptr_t) ctfdata); | ||||
tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); | tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 150 Lines • ▼ Show 20 Lines | if ((offset = typoff[type]) == 0) { | ||||
return(NULL); | return(NULL); | ||||
} | } | ||||
tp = (const ctf_type_t *)(lc->ctftab + offset + sizeof(ctf_header_t)); | tp = (const ctf_type_t *)(lc->ctftab + offset + sizeof(ctf_header_t)); | ||||
return (tp); | return (tp); | ||||
} | } | ||||
static const ctf_type_t * | |||||
fbt_get_type_of_arg(fbt_probe_t *fbt, int argno, int *nconstvals) | |||||
{ | |||||
const ctf_type_t *tp; | |||||
const ushort_t *dp; | |||||
linker_ctf_t lc; | |||||
modctl_t *ctl; | |||||
uint32_t *ctfoff, offset; | |||||
ushort_t info, kind, n; | |||||
int symindx, i; | |||||
if (nconstvals != NULL) | |||||
*nconstvals = 0; | |||||
tp = NULL; | |||||
ctl = fbt->fbtp_ctl; | |||||
symindx = fbt->fbtp_symindx; | |||||
/* Return probe? or garbage input? */ | |||||
if (fbt->fbtp_roffset != 0 || argno < 0) | |||||
goto out; | |||||
/* Get a pointer to the CTF data and it's length. */ | |||||
if (linker_ctf_get(ctl, &lc) != 0) | |||||
/* No CTF data? Something wrong? *shrug* */ | |||||
goto out; | |||||
/* Check if this module hasn't been initialised yet. */ | |||||
if (*lc.ctfoffp == NULL) { | |||||
if (fbt_ctfoff_init(ctl, &lc) != 0) | |||||
goto out; | |||||
if (fbt_typoff_init(&lc) != 0) | |||||
goto out; | |||||
} | |||||
ctfoff = *lc.ctfoffp; | |||||
if (ctfoff == NULL || *lc.typoffp == NULL) | |||||
goto out; | |||||
if (symindx >= lc.nsym) | |||||
goto out; | |||||
if ((offset = ctfoff[symindx]) == 0xffffffff) | |||||
goto out; | |||||
dp = (const ushort_t *)(lc.ctftab + offset + sizeof(ctf_header_t)); | |||||
info = *dp++; | |||||
kind = CTF_INFO_KIND(info); | |||||
n = CTF_INFO_VLEN(info); | |||||
if (kind != CTF_K_FUNCTION) { | |||||
#if 0 /* spammy */ | |||||
printf("%s(%d): %s function!\n", __func__, __LINE__, | |||||
(kind == CTF_K_UNKNOWN) ? "Unknown" : "Expected a"); | |||||
#endif | |||||
goto out; | |||||
} | |||||
/* skip over return type */ | |||||
dp++; | |||||
/* | |||||
* only walk through defined arguments. a given index beyond defined | |||||
* arguments may be valid for a varargs function, but we don't have | |||||
* type information there. | |||||
*/ | |||||
tp = NULL; | |||||
i = 0; | |||||
if (nconstvals == NULL) { | |||||
i = argno; | |||||
dp += argno; | |||||
} | |||||
for (; i <= argno && i < n; i++, dp++) { | |||||
if (*dp == 0) | |||||
break; | |||||
tp = ctf_lookup_by_id(&lc, *dp); | |||||
if (CTF_INFO_KIND(tp->ctt_info) == CTF_K_CONSTVAL && | |||||
nconstvals != NULL) | |||||
(*nconstvals)++; | |||||
} | |||||
out: | |||||
return (tp); | |||||
} | |||||
void | |||||
fbt_update_constvalmask(fbt_probe_t *fbt) | |||||
{ | |||||
const ctf_type_t *tp; | |||||
uint8_t mask; | |||||
size_t i; | |||||
for (i = 0, mask = 0; i < 8; i++) { | |||||
tp = fbt_get_type_of_arg(fbt, i, NULL); | |||||
if (tp == NULL) | |||||
break; | |||||
if (CTF_INFO_KIND(tp->ctt_info) != CTF_K_CONSTVAL) | |||||
continue; | |||||
mask |= (1 << i); | |||||
} | |||||
dtrace_probe_set_constvalmask(fbt->fbtp_id, mask); | |||||
} | |||||
static void | static void | ||||
fbt_array_info(linker_ctf_t *lc, ctf_id_t type, ctf_arinfo_t *arp) | fbt_array_info(linker_ctf_t *lc, ctf_id_t type, ctf_arinfo_t *arp) | ||||
{ | { | ||||
const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab; | const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab; | ||||
const ctf_type_t *tp; | const ctf_type_t *tp; | ||||
const ctf_array_t *ap; | const ctf_array_t *ap; | ||||
ssize_t increment; | ssize_t increment; | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | ctf_decl_push(ctf_decl_t *cd, linker_ctf_t *lc, ctf_id_t type) | ||||
case CTF_K_POINTER: | case CTF_K_POINTER: | ||||
ctf_decl_push(cd, lc, tp->ctt_type); | ctf_decl_push(cd, lc, tp->ctt_type); | ||||
prec = CTF_PREC_POINTER; | prec = CTF_PREC_POINTER; | ||||
break; | break; | ||||
case CTF_K_VOLATILE: | case CTF_K_VOLATILE: | ||||
case CTF_K_CONST: | case CTF_K_CONST: | ||||
case CTF_K_RESTRICT: | case CTF_K_RESTRICT: | ||||
case CTF_K_CONSTVAL: | |||||
ctf_decl_push(cd, lc, tp->ctt_type); | ctf_decl_push(cd, lc, tp->ctt_type); | ||||
prec = cd->cd_qualp; | prec = cd->cd_qualp; | ||||
is_qual++; | is_qual++; | ||||
break; | break; | ||||
default: | default: | ||||
prec = CTF_PREC_BASE; | prec = CTF_PREC_BASE; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | for (cdp = ctf_list_next(&cd.cd_nodes[prec]); | ||||
ctf_decl_sprintf(&cd, "union %s", name); | ctf_decl_sprintf(&cd, "union %s", name); | ||||
break; | break; | ||||
case CTF_K_ENUM: | case CTF_K_ENUM: | ||||
ctf_decl_sprintf(&cd, "enum %s", name); | ctf_decl_sprintf(&cd, "enum %s", name); | ||||
break; | break; | ||||
case CTF_K_VOLATILE: | case CTF_K_VOLATILE: | ||||
ctf_decl_sprintf(&cd, "volatile"); | ctf_decl_sprintf(&cd, "volatile"); | ||||
break; | break; | ||||
case CTF_K_CONSTVAL: | |||||
/* | |||||
* This isn't amazing, but avoids having to | |||||
* hack up the D language grammar for this | |||||
* feature. | |||||
*/ | |||||
/* FALLTHROUGH */ | |||||
case CTF_K_CONST: | case CTF_K_CONST: | ||||
ctf_decl_sprintf(&cd, "const"); | ctf_decl_sprintf(&cd, "const"); | ||||
break; | break; | ||||
case CTF_K_RESTRICT: | case CTF_K_RESTRICT: | ||||
ctf_decl_sprintf(&cd, "restrict"); | ctf_decl_sprintf(&cd, "restrict"); | ||||
break; | break; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | if (fbt->fbtp_roffset != 0) { | ||||
/* Skip the return type and arguments up to the one requested. */ | /* Skip the return type and arguments up to the one requested. */ | ||||
dp += ndx + 1; | dp += ndx + 1; | ||||
} | } | ||||
if (fbt_type_name(&lc, *dp, desc->dtargd_native, sizeof(desc->dtargd_native)) > 0) | if (fbt_type_name(&lc, *dp, desc->dtargd_native, sizeof(desc->dtargd_native)) > 0) | ||||
desc->dtargd_ndx = ndx; | desc->dtargd_ndx = ndx; | ||||
return; | return; | ||||
} | |||||
static uint64_t | |||||
fbt_getargval(void *arg __unused, dtrace_id_t id __unused, void *parg, | |||||
int argno, int aframes) | |||||
{ | |||||
const ctf_type_t *tp; | |||||
fbt_probe_t *fbt; | |||||
linker_ctf_t lc; | |||||
modctl_t *ctl; | |||||
int nconstvals; | |||||
fbt = parg; | |||||
ctl = fbt->fbtp_ctl; | |||||
tp = fbt_get_type_of_arg(fbt, argno, &nconstvals); | |||||
/* No CTF data? Something wrong? *shrug* */ | |||||
if (linker_ctf_get(ctl, &lc) != 0 || *lc.ctfoffp == NULL) | |||||
goto ordinary_regarg; | |||||
/* handle constval requested args specially */ | |||||
if (tp != NULL && CTF_INFO_KIND(tp->ctt_info) == CTF_K_CONSTVAL) { | |||||
const ctf_header_t *hp; | |||||
ssize_t increment; | |||||
const void *dp; | |||||
int64_t i64; | |||||
int32_t i32; | |||||
hp = (const ctf_header_t *)lc.ctftab; | |||||
(void) fbt_get_ctt_size(hp->cth_version, tp, NULL, &increment); | |||||
dp = (void *)((uintptr_t)tp + increment); | |||||
switch (CTF_INFO_VLEN(tp->ctt_info)) { | |||||
case 0: | |||||
i64 = 0; | |||||
break; | |||||
case 4: | |||||
memcpy(&i32, dp, sizeof(i32)); | |||||
i64 = i32; | |||||
break; | |||||
case 8: | |||||
memcpy(&i64, dp, sizeof(i64)); | |||||
break; | |||||
default: | |||||
printf("%s(%d): bad VLEN in CTF data!\n", __func__, | |||||
__LINE__); | |||||
i64 = -1; | |||||
} | |||||
return ((uint64_t)i64); | |||||
} | |||||
/* | |||||
* the compiler optimized out the constant values, so look up the | |||||
* appropriate register. | |||||
*/ | |||||
argno -= nconstvals; | |||||
/* FALLTHROUGH */ | |||||
ordinary_regarg: | |||||
return (dtrace_getarg(argno, aframes)); | |||||
} | } | ||||
static int | static int | ||||
fbt_linker_file_cb(linker_file_t lf, void *arg) | fbt_linker_file_cb(linker_file_t lf, void *arg) | ||||
{ | { | ||||
fbt_provide_module(arg, lf); | fbt_provide_module(arg, lf); | ||||
▲ Show 20 Lines • Show All 93 Lines • Show Last 20 Lines |