diff --git a/sys/arm/arm/sdt_machdep.c b/sys/arm/arm/sdt_machdep.c new file mode 100644 index 0000000..262745c --- /dev/null +++ b/sys/arm/arm/sdt_machdep.c @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2016 Mark Johnston + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#define ARM_OPC_B 0xea +#define ARM_OPC_BL 0xeb + +#define ARM_NOP 0xe1a00000 /* mov r0, r0 */ +#define ARM_MOV_PC_LR 0xe1a0f00e /* mov pc, lr */ + +/* + * Defined by sdtstubs.sh at compile-time. + */ +void _sdt_probe_stub(void); + +uint64_t +sdt_md_patch_callsite(struct sdt_probe *probe, uint64_t offset, bool reloc) +{ + uint32_t *callinstr, newinstr; + uint8_t opcode; + + callinstr = (uint32_t *)(uintptr_t)offset; + opcode = (*callinstr & 0xff000000) >> 24; + if (opcode != ARM_OPC_B && opcode != ARM_OPC_BL) { + printf("sdt: opcode mismatch (0x%x) for %s:::%s@%p\n", + opcode, probe->prov->name, probe->name, + (void *)(uintptr_t)offset); + return (0); + } + + /* XXX check the branch target */ + switch (opcode) { + case ARM_OPC_B: + newinstr = ARM_MOV_PC_LR; + break; + case ARM_OPC_BL: + newinstr = ARM_NOP; + break; + } + + *callinstr = newinstr; + icache_sync((vm_offset_t)callinstr, sizeof(*callinstr)); + return (offset); +} diff --git a/sys/arm/include/trap.h b/sys/arm/include/trap.h index ae1c571..8e7da12 100644 --- a/sys/arm/include/trap.h +++ b/sys/arm/include/trap.h @@ -8,4 +8,5 @@ #define PTRACE_BREAKPOINT 0xe7fffff0 #define KERNEL_BREAKPOINT 0xe7ffffff #define FBT_BREAKPOINT 0xe7f000f0 +#define SDT_BREAKPOINT 0xe7f000f0 #endif /* _MACHINE_TRAP_H_ */ diff --git a/sys/cddl/compat/opensolaris/sys/sdt.h b/sys/cddl/compat/opensolaris/sys/sdt.h index 04e4abb..ad192b0 100644 --- a/sys/cddl/compat/opensolaris/sys/sdt.h +++ b/sys/cddl/compat/opensolaris/sys/sdt.h @@ -32,14 +32,21 @@ #include_next #ifdef KDTRACE_HOOKS + SDT_PROBE_DECLARE(sdt, , , set__error); -#define SET_ERROR(err) \ - ((sdt_sdt___set__error->id ? \ - (*sdt_probe_func)(sdt_sdt___set__error->id, \ - (uintptr_t)err, 0, 0, 0, 0) : 0), err) +static inline int +__sdt_set_error(int err) +{ + + SDT_PROBE1(sdt, , , set__error, err); + return (err); +} + +#define SET_ERROR(err) (__sdt_set_error(err)) + #else #define SET_ERROR(err) (err) #endif -#endif /* _OPENSOLARIS_SYS_SDT_H_ */ +#endif /* _OPENSOLARIS_SYS_SDT_H_ */ diff --git a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h index 92d3569..d647d26 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h +++ b/sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h @@ -2450,9 +2450,10 @@ extern void dtrace_helpers_destroy(proc_t *); #define DTRACE_INVOP_MASK ((1 << DTRACE_INVOP_SHIFT) - 1) #define DTRACE_INVOP_DATA(x) ((x) >> DTRACE_INVOP_SHIFT) -#define DTRACE_INVOP_PUSHM 1 -#define DTRACE_INVOP_POPM 2 -#define DTRACE_INVOP_B 3 +#define DTRACE_INVOP_PUSHM 1 +#define DTRACE_INVOP_POPM 2 +#define DTRACE_INVOP_B 3 +#define DTRACE_INVOP_NOP 4 #elif defined(__aarch64__) @@ -2482,6 +2483,7 @@ extern void dtrace_helpers_destroy(proc_t *); #define DTRACE_INVOP_PUSHM 1 #define DTRACE_INVOP_RET 2 #define DTRACE_INVOP_B 3 +#define DTRACE_INVOP_NOP 4 #elif defined(__mips__) @@ -2495,6 +2497,7 @@ extern void dtrace_helpers_destroy(proc_t *); #define DTRACE_INVOP_SD 1 #define DTRACE_INVOP_LD 2 +#define DTRACE_INVOP_NOP 3 #elif defined(__riscv__) diff --git a/sys/cddl/dev/dtrace/mips/dtrace_subr.c b/sys/cddl/dev/dtrace/mips/dtrace_subr.c index 1ed3cd0..890f80f 100644 --- a/sys/cddl/dev/dtrace/mips/dtrace_subr.c +++ b/sys/cddl/dev/dtrace/mips/dtrace_subr.c @@ -249,6 +249,11 @@ dtrace_invop_start(struct trapframe *frame) int invop; invop = dtrace_invop(frame->pc, frame, frame->pc); + if (invop == DTRACE_INVOP_NOP) { + frame->pc += INSN_SIZE; + return (0); + } + offs = (invop & LDSD_DATA_MASK); sp = (register_t *)((uint8_t *)frame->sp + offs); diff --git a/sys/cddl/dev/sdt/arm/sdt_isa.c b/sys/cddl/dev/sdt/arm/sdt_isa.c new file mode 100644 index 0000000..c2f1e33 --- /dev/null +++ b/sys/cddl/dev/sdt/arm/sdt_isa.c @@ -0,0 +1,52 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2016 Mark Johnston + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include + +#include "sdt.h" + +int +sdt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) +{ + + return (DTRACE_INVOP_NOP); +} + +void +sdt_probe_enable(struct sdt_probedesc *desc __unused) +{ +} + +void +sdt_probe_disable(struct sdt_probedesc *desc) +{ +} diff --git a/sys/cddl/dev/sdt/mips/sdt_isa.c b/sys/cddl/dev/sdt/mips/sdt_isa.c new file mode 100644 index 0000000..a4510c8 --- /dev/null +++ b/sys/cddl/dev/sdt/mips/sdt_isa.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 2016 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include + +#include "sdt.h" + +#define MIPS_NOP 0 + +int +sdt_invop(uintptr_t addr, struct trapframe *tf, uintptr_t rval) +{ + struct sdt_invoprec *rec; + + rec = sdt_lookup_site(addr); + if (rec == NULL) + return (0); + + dtrace_probe(rec->sr_id, tf->a0, tf->a1, tf->a2, + tf->a3, tf->a4); + + return (DTRACE_INVOP_NOP); +} + +static void +sdt_probe_patch(struct sdt_probedesc *desc, uint32_t instr) +{ + struct sdt_probe *probe; + uint32_t *callsite; + + if (desc->spd_offset == 0) { + probe = desc->li.spd_probe; + MPASS(strlen(probe->func) > 0); + SLIST_FOREACH(desc, &probe->site_list, li.spd_entry) { + callsite = (uint32_t *)desc->spd_offset; + callsite[0] = instr; + } + } else { + callsite = (uint32_t *)desc->spd_offset; + callsite[0] = instr; + } +} + +void +sdt_probe_enable(struct sdt_probedesc *desc __unused) +{ + + sdt_probe_patch(desc, MIPS_BREAK_INSTR); +} + +void +sdt_probe_disable(struct sdt_probedesc *desc) +{ + + sdt_probe_patch(desc, MIPS_NOP); +} diff --git a/sys/cddl/dev/sdt/sdt.c b/sys/cddl/dev/sdt/sdt.c index cef816f..7d53005 100644 --- a/sys/cddl/dev/sdt/sdt.c +++ b/sys/cddl/dev/sdt/sdt.c @@ -19,9 +19,6 @@ * CDDL HEADER END * * Portions Copyright 2006-2008 John Birrell jb@freebsd.org - * - * $FreeBSD$ - * */ /* @@ -40,6 +37,8 @@ */ #include +__FBSDID("$FreeBSD$"); + #include #include @@ -60,6 +59,18 @@ #include #include +/* XXX */ +#include "sdt.h" + +#define SDT_TABENTRIES 0x2000 +#define SDT_HADDR(addr) ((u_long)(((addr) >> 4) & sdt_hashmask)) +#define SDT_HENTRY(addr) (&sdt_probetab[SDT_HADDR(addr)]) + +LIST_HEAD(, sdt_invoprec) *sdt_probetab; +u_long sdt_hashmask; + +MALLOC_DECLARE(M_SDT); + /* DTrace methods. */ static void sdt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *); static void sdt_provide_probes(void *, dtrace_probedesc_t *); @@ -69,19 +80,18 @@ static void sdt_disable(void *, dtrace_id_t, void *); static void sdt_load(void); static int sdt_unload(void); +static void sdt_create_invoprec(struct sdt_probedesc *, dtrace_id_t); static void sdt_create_provider(struct sdt_provider *); -static void sdt_create_probe(struct sdt_probe *); +static void sdt_create_probe(struct sdt_probe *, struct linker_file *); static void sdt_kld_load(void *, struct linker_file *); static void sdt_kld_unload_try(void *, struct linker_file *, int *); -static MALLOC_DEFINE(M_SDT, "SDT", "DTrace SDT providers"); - static dtrace_pattr_t sdt_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 }, +{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA }, }; static dtrace_pops_t sdt_pops = { @@ -102,6 +112,29 @@ static TAILQ_HEAD(, sdt_provider) sdt_prov_list; static eventhandler_tag sdt_kld_load_tag; static eventhandler_tag sdt_kld_unload_try_tag; +struct sdt_invoprec * +sdt_lookup_site(uint64_t offset) +{ + struct sdt_invoprec *rec; + + LIST_FOREACH(rec, &sdt_probetab[SDT_HADDR(offset)], sr_next) { + if (rec->sr_desc->spd_offset == offset) + return (rec); + } + return (NULL); +} + +void +sdt_create_invoprec(struct sdt_probedesc *desc, dtrace_id_t id) +{ + struct sdt_invoprec *rec; + + rec = malloc(sizeof(*rec), M_SDT, M_WAITOK); + rec->sr_desc = desc; + rec->sr_id = id; + LIST_INSERT_HEAD(SDT_HENTRY(desc->spd_offset), rec, sr_next); +} + static void sdt_create_provider(struct sdt_provider *prov) { @@ -130,31 +163,63 @@ sdt_create_provider(struct sdt_provider *prov) prov->id = newprov->id; } +struct sdt_descmatch { + struct sdt_probedesc *desc; + char *func; +}; + +static int +sdt_desc_match(struct linker_file *lf, int symidx, linker_symval_t *sym, + void *arg) +{ + struct sdt_descmatch *match; + uint64_t offset; + + match = arg; + offset = match->desc->spd_offset; + if (offset >= (uint64_t)sym->value && offset < (uint64_t)sym->value + + sym->size) { + strlcpy(match->func, sym->name, DTRACE_FUNCNAMELEN); + /* Found a match, stop iterating. */ + return (EJUSTRETURN); + } + return (0); +} + static void -sdt_create_probe(struct sdt_probe *probe) +sdt_create_probe(struct sdt_probe *probe, struct linker_file *lf) { + struct sdt_probedesc *desc; + struct sdt_descmatch match; struct sdt_provider *prov; char mod[DTRACE_MODNAMELEN]; char func[DTRACE_FUNCNAMELEN]; char name[DTRACE_NAMELEN]; + dtrace_id_t id; const char *from; char *to; size_t len; + int aframes, error; if (probe->version != (int)sizeof(*probe)) { - printf("ignoring probe %p, version %u expected %u\n", + printf("sdt: ignoring probe %p, version %u expected %u\n", probe, probe->version, (int)sizeof(*probe)); return; } + aframes = 3; + probe->sdtp_lf = lf; + TAILQ_FOREACH(prov, &sdt_prov_list, prov_entry) if (strcmp(prov->name, probe->prov->name) == 0) break; KASSERT(prov != NULL, ("probe defined without a provider")); - /* If no module name was specified, use the module filename. */ - if (*probe->mod == 0) { + /* + * If no module name was specified, use the module filename. + */ + if (probe->mod[0] == '\0') { len = strlcpy(mod, probe->sdtp_lf->filename, sizeof(mod)); if (len > 3 && strcmp(mod + len - 3, ".ko") == 0) mod[len - 3] = '\0'; @@ -162,18 +227,11 @@ sdt_create_probe(struct sdt_probe *probe) strlcpy(mod, probe->mod, sizeof(mod)); /* - * Unfortunately this is necessary because the Solaris DTrace - * code mixes consts and non-consts with casts to override - * the incompatibilies. On FreeBSD, we use strict warnings - * in the C compiler, so we have to respect const vs non-const. + * Demangle the probe name: two consecutive underscores become a dash. */ - strlcpy(func, probe->func, sizeof(func)); - if (func[0] == '\0') - strcpy(func, "none"); - from = probe->name; to = name; - for (len = 0; len < (sizeof(name) - 1) && *from != '\0'; + for (len = 0; len < sizeof(name) - 1 && *from != '\0'; len++, from++, to++) { if (from[0] == '_' && from[1] == '_') { *to = '-'; @@ -183,10 +241,39 @@ sdt_create_probe(struct sdt_probe *probe) } *to = '\0'; - if (dtrace_probe_lookup(prov->id, mod, func, name) != DTRACE_IDNONE) - return; - - (void)dtrace_probe_create(prov->id, mod, func, name, 1, probe); + /* + * Finally, create the probe. If a function name is hard-coded, we + * register a single probe and use its ID for each site. Otherwise, we + * proceed normally and create a probe for each site. + */ + if (probe->func[0] != '\0') { + strlcpy(func, probe->func, sizeof(func)); + + desc = malloc(sizeof(*desc), M_SDT, M_WAITOK); + desc->li.spd_probe = probe; + desc->spd_offset = 0; + + id = dtrace_probe_create(prov->id, mod, func, name, aframes, + desc); + SLIST_FOREACH(desc, &probe->site_list, li.spd_entry) + sdt_create_invoprec(desc, id); + } else { + match.func = &func[0]; + while ((desc = SLIST_FIRST(&probe->site_list)) != NULL) { + match.desc = desc; + error = linker_file_function_listall(lf, sdt_desc_match, + &match); + if (error != EJUSTRETURN) + printf("sdt: no function at %#lx (error %d)\n", + desc->spd_offset, error); + SLIST_REMOVE_HEAD(&probe->site_list, li.spd_entry); + desc->li.spd_probe = probe; + + id = dtrace_probe_create(prov->id, mod, func, name, + aframes, desc); + sdt_create_invoprec(desc, id); + } + } } /* @@ -202,51 +289,57 @@ sdt_provide_probes(void *arg, dtrace_probedesc_t *desc) static void sdt_enable(void *arg __unused, dtrace_id_t id, void *parg) { - struct sdt_probe *probe = parg; + struct sdt_probedesc *desc = parg; + struct sdt_probe *probe = desc->li.spd_probe; - probe->id = id; probe->sdtp_lf->nenabled++; if (strcmp(probe->prov->name, "lockstat") == 0) lockstat_enabled++; + sdt_probe_enable(desc); } static void sdt_disable(void *arg __unused, dtrace_id_t id, void *parg) { - struct sdt_probe *probe = parg; + struct sdt_probedesc *desc = parg; + struct sdt_probe *probe = desc->li.spd_probe; KASSERT(probe->sdtp_lf->nenabled > 0, ("no probes enabled")); + sdt_probe_disable(desc); if (strcmp(probe->prov->name, "lockstat") == 0) lockstat_enabled--; - probe->id = 0; probe->sdtp_lf->nenabled--; } static void -sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc) +sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *argdesc) { struct sdt_argtype *argtype; - struct sdt_probe *probe = parg; + struct sdt_probedesc *desc; + struct sdt_probe *probe; - if (desc->dtargd_ndx >= probe->n_args) { - desc->dtargd_ndx = DTRACE_ARGNONE; + desc = parg; + probe = desc->li.spd_probe; + if (argdesc->dtargd_ndx >= probe->n_args) { + argdesc->dtargd_ndx = DTRACE_ARGNONE; return; } TAILQ_FOREACH(argtype, &probe->argtype_list, argtype_entry) { - if (desc->dtargd_ndx == argtype->ndx) { - desc->dtargd_mapping = desc->dtargd_ndx; + if (argdesc->dtargd_ndx == argtype->ndx) { + argdesc->dtargd_mapping = argdesc->dtargd_ndx; if (argtype->type == NULL) { - desc->dtargd_native[0] = '\0'; - desc->dtargd_xlate[0] = '\0'; + argdesc->dtargd_native[0] = '\0'; + argdesc->dtargd_xlate[0] = '\0'; continue; } - strlcpy(desc->dtargd_native, argtype->type, - sizeof(desc->dtargd_native)); + strlcpy(argdesc->dtargd_native, argtype->type, + sizeof(argdesc->dtargd_native)); if (argtype->xtype != NULL) - strlcpy(desc->dtargd_xlate, argtype->xtype, - sizeof(desc->dtargd_xlate)); + strlcpy(argdesc->dtargd_xlate, argtype->xtype, + sizeof(argdesc->dtargd_xlate)); + break; } } } @@ -254,8 +347,25 @@ sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc) static void sdt_destroy(void *arg, dtrace_id_t id, void *parg) { + struct sdt_probedesc *desc; + struct sdt_probe *probe; + + desc = parg; + if (desc->spd_offset == 0) { + probe = desc->li.spd_probe; + KASSERT(strlen(probe->func) > 0, + ("probefunc is empty for %s:::%s", probe->prov->name, + probe->name)); + free(desc, M_SDT); + } } +#define SDT_LINKER_SET_FOREACH(lf, set, it) \ + __typeof(it) __##set##_start, __##set##_end; \ + if (linker_file_lookup_set(lf, #set, &__##set##_start, \ + &__##set##_end, NULL) == 0) \ + for (it = __##set##_start; it < __##set##_end; it++) + /* * Called from the kernel linker when a module is loaded, before * dtrace_module_loaded() is called. This is done so that it's possible to @@ -266,53 +376,40 @@ sdt_destroy(void *arg, dtrace_id_t id, void *parg) static void sdt_kld_load(void *arg __unused, struct linker_file *lf) { - struct sdt_provider **prov, **begin, **end; - struct sdt_probe **probe, **p_begin, **p_end; - struct sdt_argtype **argtype, **a_begin, **a_end; - - if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end, - NULL) == 0) { - for (prov = begin; prov < end; prov++) - sdt_create_provider(*prov); + struct sdt_provider **prov; + struct sdt_probe **probe; + struct sdt_argtype **argtype; + + SDT_LINKER_SET_FOREACH(lf, sdt_providers_set, prov) { + sdt_create_provider(*prov); } - if (linker_file_lookup_set(lf, "sdt_probes_set", &p_begin, &p_end, - NULL) == 0) { - for (probe = p_begin; probe < p_end; probe++) { - (*probe)->sdtp_lf = lf; - sdt_create_probe(*probe); - TAILQ_INIT(&(*probe)->argtype_list); - } + SDT_LINKER_SET_FOREACH(lf, sdt_probes_set, probe) { + sdt_create_probe(*probe, lf); + TAILQ_INIT(&(*probe)->argtype_list); } - if (linker_file_lookup_set(lf, "sdt_argtypes_set", &a_begin, &a_end, - NULL) == 0) { - for (argtype = a_begin; argtype < a_end; argtype++) { - (*argtype)->probe->n_args++; - TAILQ_INSERT_TAIL(&(*argtype)->probe->argtype_list, - *argtype, argtype_entry); - } + SDT_LINKER_SET_FOREACH(lf, sdt_argtypes_set, argtype) { + (*argtype)->probe->n_args++; + TAILQ_INSERT_TAIL(&(*argtype)->probe->argtype_list, *argtype, + argtype_entry); } } static void sdt_kld_unload_try(void *arg __unused, struct linker_file *lf, int *error) { - struct sdt_provider *prov, **curr, **begin, **end, *tmp; + struct sdt_provider *prov, **curr, *tmp; if (*error != 0) /* We already have an error, so don't do anything. */ return; - else if (linker_file_lookup_set(lf, "sdt_providers_set", &begin, &end, - NULL)) - /* No DTrace providers are declared in this file. */ - return; /* * Go through all the providers declared in this linker file and * unregister any that aren't declared in another loaded file. */ - for (curr = begin; curr < end; curr++) { + SDT_LINKER_SET_FOREACH(lf, sdt_providers_set, curr) { TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) { if (strcmp(prov->name, (*curr)->name) != 0) continue; @@ -337,7 +434,6 @@ sdt_linker_file_cb(linker_file_t lf, void *arg __unused) { sdt_kld_load(NULL, lf); - return (0); } @@ -347,7 +443,7 @@ sdt_load() TAILQ_INIT(&sdt_prov_list); - sdt_probe_func = dtrace_probe; + sdt_probetab = hashinit(SDT_TABENTRIES, M_SDT, &sdt_hashmask); sdt_kld_load_tag = EVENTHANDLER_REGISTER(kld_load, sdt_kld_load, NULL, EVENTHANDLER_PRI_ANY); @@ -356,6 +452,8 @@ sdt_load() /* Pick up probes from the kernel and already-loaded linker files. */ linker_file_foreach(sdt_linker_file_cb, NULL); + + dtrace_invop_add(sdt_invop); } static int @@ -364,10 +462,13 @@ sdt_unload() struct sdt_provider *prov, *tmp; int ret; + dtrace_invop_remove(sdt_invop); + EVENTHANDLER_DEREGISTER(kld_load, sdt_kld_load_tag); EVENTHANDLER_DEREGISTER(kld_unload_try, sdt_kld_unload_try_tag); - sdt_probe_func = sdt_probe_stub; + /* XXX need to free recs. */ + hashdestroy(sdt_probetab, M_SDT, sdt_hashmask); TAILQ_FOREACH_SAFE(prov, &sdt_prov_list, prov_entry, tmp) { ret = dtrace_unregister(prov->id); @@ -377,7 +478,6 @@ sdt_unload() free(prov->name, M_SDT); free(prov, M_SDT); } - return (0); } diff --git a/sys/cddl/dev/sdt/sdt.h b/sys/cddl/dev/sdt/sdt.h new file mode 100644 index 0000000..7e80e11 --- /dev/null +++ b/sys/cddl/dev/sdt/sdt.h @@ -0,0 +1,48 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2016 Mark Johnston + */ + +#ifndef _SDT_H_ +#define _SDT_H_ + +struct trapframe; +struct sdt_probedesc; + +/* + * An entry in the SDT hash table. These records exist one-to-one with SDT probe + * descriptors, but are split into a separate struct to avoid bloat: descriptors + * are created at compile-time and always reside in memory. Note that multiple + * records may have the same id if the probe definition hard-codes a function + * name or a probe has multiple sites within a function. + */ +struct sdt_invoprec { + struct sdt_probedesc *sr_desc; + LIST_ENTRY(sdt_invoprec) sr_next; + dtrace_id_t sr_id; +}; + +struct sdt_invoprec *sdt_lookup_site(uint64_t); +int sdt_invop(uintptr_t, struct trapframe *, uintptr_t); +void sdt_probe_enable(struct sdt_probedesc *); +void sdt_probe_disable(struct sdt_probedesc *); + +#endif /* _SDT_H_ */ diff --git a/sys/cddl/dev/sdt/x86/sdt_isa.c b/sys/cddl/dev/sdt/x86/sdt_isa.c new file mode 100644 index 0000000..8b4e4c9 --- /dev/null +++ b/sys/cddl/dev/sdt/x86/sdt_isa.c @@ -0,0 +1,90 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + * + * Copyright 2016 Mark Johnston + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include + +#include "sdt.h" + +#define AMD64_BP 0xcc +#define AMD64_NOP 0x90 + +int +sdt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval) +{ + struct sdt_invoprec *rec; + + rec = sdt_lookup_site(addr); + if (rec == NULL) + return (0); + +#ifdef __amd64__ + dtrace_probe(rec->sr_id, frame->tf_rdi, frame->tf_rsi, frame->tf_rdx, + frame->tf_rcx, frame->tf_r8); +#else + dtrace_probe(rec->sr_id, stack[0], stack[1], stack[2], stack[3], + stack[4]); +#endif + return (DTRACE_INVOP_NOP); +} + +static void +sdt_probe_patch(struct sdt_probedesc *desc, uint8_t instr) +{ + struct sdt_probe *probe; + uint8_t *callsite; + + if (desc->spd_offset == 0) { + probe = desc->li.spd_probe; + MPASS(strlen(probe->func) > 0); + SLIST_FOREACH(desc, &probe->site_list, li.spd_entry) { + callsite = (uint8_t *)desc->spd_offset; + callsite[0] = instr; + } + } else { + callsite = (uint8_t *)desc->spd_offset; + callsite[0] = instr; + } +} + +void +sdt_probe_enable(struct sdt_probedesc *desc) +{ + + sdt_probe_patch(desc, AMD64_BP); +} + +void +sdt_probe_disable(struct sdt_probedesc *desc) +{ + + sdt_probe_patch(desc, AMD64_NOP); +} diff --git a/sys/conf/Makefile.arm b/sys/conf/Makefile.arm index 009bce8..3553b9b 100644 --- a/sys/conf/Makefile.arm +++ b/sys/conf/Makefile.arm @@ -58,7 +58,7 @@ genassym.o: bus_if.h device_if.h SYSTEM_LD_ = ${LD} -Bdynamic -T ldscript.$M.noheader ${_LDFLAGS} \ -warn-common -export-dynamic -dynamic-linker /red/herring -o \ - ${FULLKERNEL}.noheader -X ${SYSTEM_OBJS} vers.o + ${FULLKERNEL}.noheader -X ${FULLKERNEL}.reloc sdtstubs.o vers.o hack.pico SYSTEM_LD_TAIL +=;sed s/" + SIZEOF_HEADERS"// ldscript.$M\ >ldscript.$M.noheader; \ ${SYSTEM_LD_}; \ diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64 index be421d3..f5791bd 100644 --- a/sys/conf/files.amd64 +++ b/sys/conf/files.amd64 @@ -656,6 +656,7 @@ x86/x86/mp_watchdog.c optional mp_watchdog smp x86/x86/msi.c optional pci x86/x86/nexus.c standard x86/x86/pvclock.c standard +x86/x86/sdt_machdep.c optional kdtrace_hooks x86/x86/stack_machdep.c optional ddb | stack x86/x86/tsc.c standard x86/x86/delay.c standard diff --git a/sys/conf/files.arm b/sys/conf/files.arm index 9e50cf9..346a92c 100644 --- a/sys/conf/files.arm +++ b/sys/conf/files.arm @@ -81,6 +81,7 @@ arm/arm/pmap-v4.c optional !armv6 arm/arm/pmap-v6.c optional armv6 arm/arm/pmu.c optional pmu | fdt hwpmc arm/arm/sc_machdep.c optional sc +arm/arm/sdt_machdep.c optional kdtrace_hooks arm/arm/setcpsr.S standard arm/arm/setstack.s standard arm/arm/stack_machdep.c optional ddb | stack @@ -109,6 +110,7 @@ cddl/compat/opensolaris/kern/opensolaris_atomic.c optional zfs | dtrace compile- cddl/dev/dtrace/arm/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}" cddl/dev/dtrace/arm/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}" cddl/dev/fbt/arm/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}" +cddl/dev/sdt/arm/sdt_isa.c optional dtrace_sdt | dtraceall compile-with "${FBT_C} -I $S/cddl/dev/sdt" crypto/blowfish/bf_enc.c optional crypto | ipsec crypto/des/des_enc.c optional crypto | ipsec | netsmb dev/cpufreq/cpufreq_dt.c optional cpufreq fdt diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index cdf851a..1ad0ea4 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -626,6 +626,7 @@ x86/x86/mp_x86.c optional smp x86/x86/mp_watchdog.c optional mp_watchdog smp x86/x86/msi.c optional apic pci x86/x86/nexus.c standard +x86/x86/sdt_machdep.c optional kdtrace_hooks x86/x86/stack_machdep.c optional ddb | stack x86/x86/tsc.c standard x86/x86/pvclock.c standard diff --git a/sys/conf/files.mips b/sys/conf/files.mips index 8d1cf7d..e03dd30 100644 --- a/sys/conf/files.mips +++ b/sys/conf/files.mips @@ -34,6 +34,7 @@ mips/mips/pm_machdep.c standard mips/mips/pmap.c standard mips/mips/ptrace_machdep.c standard mips/mips/sc_machdep.c standard +mips/mips/sdt_machdep.c optional kdtrace_hooks mips/mips/stack_machdep.c optional ddb | stack mips/mips/stdatomic.c standard \ compile-with "${NORMAL_C:N-Wmissing-prototypes}" @@ -102,3 +103,4 @@ cddl/compat/opensolaris/kern/opensolaris_atomic.c optional zfs | dtrace compile- cddl/dev/dtrace/mips/dtrace_asm.S optional dtrace compile-with "${DTRACE_S}" cddl/dev/dtrace/mips/dtrace_subr.c optional dtrace compile-with "${DTRACE_C}" cddl/dev/fbt/mips/fbt_isa.c optional dtrace_fbt | dtraceall compile-with "${FBT_C}" +cddl/dev/sdt/mips/sdt_isa.c optional dtrace_sdt | dtraceall compile-with "${FBT_C} -I $S/cddl/dev/sdt" diff --git a/sys/conf/kern.post.mk b/sys/conf/kern.post.mk index 128e47d..d2e75e2 100644 --- a/sys/conf/kern.post.mk +++ b/sys/conf/kern.post.mk @@ -135,7 +135,14 @@ gdbinit: .endif .endif -${FULLKERNEL}: ${SYSTEM_DEP} vers.o +sdtstubs.c: ${FULLKERNEL}.reloc + AWK='${AWK}' OBJDUMP='${OBJDUMP}' NM='${NM}' \ + sh $S/tools/sdtstubs.sh ${.ALLSRC} > ${.TARGET} + +${FULLKERNEL}.reloc: ${SYSTEM_DEP} + @${LD} --relocatable -o ${.TARGET} ${SYSTEM_OBJS} + +${FULLKERNEL}: ${FULLKERNEL}.reloc sdtstubs.o vers.o @rm -f ${.TARGET} @echo linking ${.TARGET} ${SYSTEM_LD} @@ -149,6 +156,8 @@ ${FULLKERNEL}: ${SYSTEM_DEP} vers.o .if !defined(DEBUG) ${OBJCOPY} --strip-debug ${.TARGET} .endif + @AWK='${AWK}' OBJCOPY='${OBJCOPY}' OBJDUMP='${OBJDUMP}' \ + sh $S/tools/sdtstrip.sh ${.TARGET} ${SYSTEM_LD_TAIL} OBJS_DEPEND_GUESS+= assym.s vnode_if.h ${BEFORE_DEPEND:M*.h} \ diff --git a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk index 0a2d9a6..630e79d 100644 --- a/sys/conf/kern.pre.mk +++ b/sys/conf/kern.pre.mk @@ -38,6 +38,7 @@ CP?= cp LINT?= lint NM?= nm OBJCOPY?= objcopy +OBJDUMP?= objdump SIZE?= size .if defined(DEBUG) @@ -173,10 +174,9 @@ OFED_C= ${OFED_C_NOIMP} ${.IMPSRC} GEN_CFILES= $S/$M/$M/genassym.c ${MFILES:T:S/.m$/.c/} SYSTEM_CFILES= config.c env.c hints.c vnode_if.c -SYSTEM_DEP= Makefile ${SYSTEM_OBJS} +SYSTEM_DEP= Makefile ${SYSTEM_OBJS} hack.pico SYSTEM_OBJS= locore.o ${MDOBJS} ${OBJS} SYSTEM_OBJS+= ${SYSTEM_CFILES:.c=.o} -SYSTEM_OBJS+= hack.pico MD_ROOT_SIZE_CONFIGURED!= grep MD_ROOT_SIZE opt_md.h || true ; echo .if ${MFS_IMAGE:Uno} != "no" @@ -184,9 +184,10 @@ MD_ROOT_SIZE_CONFIGURED!= grep MD_ROOT_SIZE opt_md.h || true ; echo SYSTEM_OBJS+= embedfs_${MFS_IMAGE:T:R}.o .endif .endif +SYSTEM_LD_HEAD= @${LD} --relocatable -o ${.TARGET}.reloc ${SYSTEM_OBJS} SYSTEM_LD= @${LD} -Bdynamic -T ${LDSCRIPT} ${_LDFLAGS} --no-warn-mismatch \ --warn-common --export-dynamic --dynamic-linker /red/herring \ - -o ${.TARGET} -X ${SYSTEM_OBJS} vers.o + -o ${.TARGET} -X ${.TARGET}.reloc sdtstubs.o vers.o hack.pico SYSTEM_LD_TAIL= @${OBJCOPY} --strip-symbol gcc2_compiled. ${.TARGET} ; \ ${SIZE} ${.TARGET} ; chmod 755 ${.TARGET} SYSTEM_DEP+= ${LDSCRIPT} diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index 5a1e611..d56f860 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -68,6 +68,7 @@ KMODLOAD?= /sbin/kldload KMODUNLOAD?= /sbin/kldunload KMODISLOADED?= /sbin/kldstat -q -n OBJCOPY?= objcopy +OBJDUMP?= objdump .include # Grab all the options for a kernel build. For backwards compat, we need to diff --git a/sys/kern/kern_sdt.c b/sys/kern/kern_sdt.c index 5191a88..f1ee7dc 100644 --- a/sys/kern/kern_sdt.c +++ b/sys/kern/kern_sdt.c @@ -1,5 +1,6 @@ /*- * Copyright 2006-2008 John Birrell + * Copyright (c) 2016 Mark Johnston * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -21,33 +22,172 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include +__FBSDID("$FreeBSD$"); + #include #include -#include + +#include +#include +#include +#include +#include #include +#include + +#ifdef __arm__ +void _start(void); +#endif + SDT_PROVIDER_DEFINE(sdt); +MALLOC_DEFINE(M_SDT, "sdt", "statically-defined tracing"); + +static eventhandler_tag sdt_kld_unload_try_tag; + +static void +sdt_kld_unload_try(void *arg __unused, linker_file_t lf, int *error) +{ + struct sdt_probe **probe, **start, **end; + struct sdt_probedesc *desc; + + if (*error != 0) + return; + if (linker_file_lookup_set(lf, "sdt_probes_set", &start, &end, + NULL) != 0) + return; + + for (probe = start; probe < end; probe++) { + while ((desc = SLIST_FIRST(&(*probe)->site_list)) != NULL) { + SLIST_REMOVE_HEAD(&(*probe)->site_list, li.spd_entry); + free(*probe, M_SDT); + } + } +} + +static void +sdt_patch_callsite(struct sdt_probe *probe, struct sdt_probedesc *desc, + uint64_t offset) +{ + + offset = sdt_md_patch_callsite(probe, offset, desc == NULL); + if (offset == 0) + return; + + /* + * The probe site is patched; now we can associate the site with + * the probe itself. Descriptors allocated here are freed in the + * kld_unload event handler. + */ + if (desc == NULL) + desc = malloc(sizeof(*desc), M_SDT, M_WAITOK); + desc->spd_offset = offset; + SLIST_INSERT_HEAD(&probe->site_list, desc, li.spd_entry); +} + /* - * Hook for the DTrace probe function. The SDT provider will set this to - * dtrace_probe() when it loads. + * Use the SDT probe sites specified in the probe site linker set to overwrite + * each probe site with NOPs. At the moment, only the kernel will contain such a + * set - probe sites in KLDs are patched when the load-time linker sees a + * relocation against a symbol with a prefix of "__dtrace_sdt_". */ -sdt_probe_func_t sdt_probe_func = sdt_probe_stub; +static int +sdt_patch_linker_file(linker_file_t lf, void *arg __unused) +{ + struct sdt_probedesc *desc, *start, *end; + struct sdt_probe *probe, **probep, **startp, **endp; + const char *probename; + int error; + + error = linker_file_lookup_set(lf, "sdt_probe_site_set", &start, &end, + NULL); + if (error != 0) + return (0); + + /* + * Linker set iteration here deviates from the normal pattern because + * this linker set is special: it contains the probe descriptor structs + * themselves rather than pointers. + */ + for (desc = start; desc < end; desc++) { + probe = desc->li.spd_probe; +#ifdef __arm__ + sdt_patch_callsite(probe, desc, desc->spd_offset + + (uintptr_t)_start); +#else + sdt_patch_callsite(probe, desc, desc->spd_offset + + (uintptr_t)btext); +#endif + } + + /* + * Now look for "anonymous" SDT probes. + */ + error = linker_file_lookup_set(lf, "sdt_anon_probe_site_set", &start, + &end, NULL); + if (error != 0) + return (0); + + error = linker_file_lookup_set(lf, "sdt_probes_set", &startp, &endp, + NULL); + KASSERT(error == 0, ("probe definition set not found")); + + for (desc = start; desc < end; desc++) { + probename = desc->li.spd_probename; + probename += strlen("sdt_sdt___"); + for (probep = startp; probep < endp; probep++) { + probe = *probep; + if (strcmp(probe->prov->name, "sdt") == 0 && + strcmp(probe->name, probename) == 0) + break; + } + + KASSERT(probep != endp, ("didn't find anon probe %s", + probename)); + sdt_patch_callsite(*probep, desc, desc->spd_offset + + (uintptr_t)btext); + } + return (0); +} /* - * This is a stub for probe calls in case kernel DTrace support isn't - * enabled. It should never get called because there is no DTrace support - * to enable it. + * Patch the kernel's probe sites. + */ +static void +sdt_patch_kernel(void *arg __unused) +{ + + linker_file_foreach(sdt_patch_linker_file, NULL); + sdt_kld_unload_try_tag = EVENTHANDLER_REGISTER(kld_unload_try, + sdt_kld_unload_try, NULL, EVENTHANDLER_PRI_ANY); +} +SYSINIT(sdt_hotpatch, SI_SUB_KDTRACE, SI_ORDER_FIRST, sdt_patch_kernel, NULL); + +/* + * Called from the kernel linker for dynamically-loaded KLDs. */ void -sdt_probe_stub(uint32_t id, uintptr_t arg0, uintptr_t arg1, - uintptr_t arg2, uintptr_t arg3, uintptr_t arg4) +sdt_patch_reloc(linker_file_t lf, const char *symname, uint64_t base, + uint64_t offset) { + struct sdt_probe *probe; + caddr_t sym; + + KASSERT(strncmp(symname, SDT_PROBE_STUB_PREFIX, + sizeof(SDT_PROBE_STUB_PREFIX) - 1) == 0, + ("invalid reloc sym %s", symname)); + + symname += sizeof("__dtrace_") - 1; /* XXX */ + sym = linker_file_lookup_symbol(lf, symname, 0); + if (sym == 0) { + printf("sdt: couldn't find symbol %s\n", symname); + return; + } - printf("sdt_probe_stub: unexpectedly called\n"); - kdb_backtrace(); + probe = (struct sdt_probe *)sym; + sdt_patch_callsite(probe, NULL, base + offset); } diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c index 349a95e..b19161f 100644 --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -42,6 +42,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef KDTRACE_HOOKS +#include +#endif #include #include #include @@ -1007,9 +1010,6 @@ link_elf_load_file(linker_class_t cls, const char* filename, vn_lock(nd.ni_vp, LK_EXCLUSIVE | LK_RETRY); if (error != 0) goto out; - error = relocate_file(ef); - if (error != 0) - goto out; /* * Try and load the symbol table if it's present. (you can @@ -1078,6 +1078,10 @@ link_elf_load_file(linker_class_t cls, const char* filename, ef->ddbstrcnt = strcnt; ef->ddbstrtab = ef->strbase; + error = relocate_file(ef); + if (error != 0) + goto out; + nosyms: error = link_elf_link_common_finish(lf); if (error != 0) @@ -1182,6 +1186,25 @@ symbol_name(elf_file_t ef, Elf_Size r_info) return (NULL); } +#ifdef KDTRACE_HOOKS +/* + * Relocations against symbols whose names start with "__dtrace_sdt_" + * correspond to static DTrace probe sites and are handled specially. + */ +static int +sdt_taste_reloc(elf_file_t ef, const char *symname, Elf_Addr offset) +{ + + if (symname != NULL && strncmp(symname, SDT_PROBE_STUB_PREFIX, + sizeof(SDT_PROBE_STUB_PREFIX) - 1) == 0) { + sdt_patch_reloc(&ef->lf, symname, (uint64_t)ef->address, + offset); + return (1); + } + return (0); +} +#endif /* KDTRACE_HOOKS */ + static int relocate_file(elf_file_t ef) { @@ -1196,14 +1219,17 @@ relocate_file(elf_file_t ef) if (rel != NULL) { rellim = (const Elf_Rel *) ((const char *)ef->rel + ef->relsize); - while (rel < rellim) { + for (; rel < rellim; rel++) { if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL, elf_lookup)) { symname = symbol_name(ef, rel->r_info); +#ifdef KDTRACE_HOOKS + if (sdt_taste_reloc(ef, symname, rel->r_offset)) + continue; +#endif printf("link_elf: symbol %s undefined\n", symname); return (ENOENT); } - rel++; } } @@ -1212,15 +1238,19 @@ relocate_file(elf_file_t ef) if (rela != NULL) { relalim = (const Elf_Rela *) ((const char *)ef->rela + ef->relasize); - while (rela < relalim) { + for (; rela < relalim; rela++) { if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA, elf_lookup)) { symname = symbol_name(ef, rela->r_info); +#ifdef KDTRACE_HOOKS + if (sdt_taste_reloc(ef, symname, + rela->r_offset)) + continue; +#endif printf("link_elf: symbol %s undefined\n", symname); return (ENOENT); } - rela++; } } @@ -1229,15 +1259,18 @@ relocate_file(elf_file_t ef) if (rel != NULL) { rellim = (const Elf_Rel *) ((const char *)ef->pltrel + ef->pltrelsize); - while (rel < rellim) { + for (; rel < rellim; rel++) { if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rel, ELF_RELOC_REL, elf_lookup)) { symname = symbol_name(ef, rel->r_info); +#ifdef KDTRACE_HOOKS + if (sdt_taste_reloc(ef, symname, rel->r_offset)) + continue; +#endif printf("link_elf: symbol %s undefined\n", symname); return (ENOENT); } - rel++; } } @@ -1246,15 +1279,19 @@ relocate_file(elf_file_t ef) if (rela != NULL) { relalim = (const Elf_Rela *) ((const char *)ef->pltrela + ef->pltrelasize); - while (rela < relalim) { + for (; rela < relalim; rela++) { if (elf_reloc(&ef->lf, (Elf_Addr)ef->address, rela, ELF_RELOC_RELA, elf_lookup)) { symname = symbol_name(ef, rela->r_info); +#ifdef KDTRACE_HOOKS + if (sdt_taste_reloc(ef, symname, + rela->r_offset)) + continue; +#endif printf("link_elf: symbol %s undefined\n", symname); return (ENOENT); } - rela++; } } diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c index 08ae922..915e8ba 100644 --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -32,16 +32,22 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include +#include #include #include -#include #include -#include +#ifdef KDTRACE_HOOKS +#include +#endif +#include #include -#include +#ifdef DDB_CTF +#include +#endif #include @@ -59,10 +65,6 @@ __FBSDID("$FreeBSD$"); #include -#ifdef DDB_CTF -#include -#endif - #include "linker_if.h" typedef struct { @@ -1050,6 +1052,25 @@ findbase(elf_file_t ef, int sec) return base; } +#ifdef KDTRACE_HOOKS +/* + * Relocations against symbols whose names start with "__dtrace_sdt_" + * correspond to static DTrace probe sites and are handled specially. + */ +static int +sdt_taste_reloc(elf_file_t ef, const char *symname, Elf_Addr offset, + Elf_Addr base) +{ + + if (symname != NULL && strncmp(symname, SDT_PROBE_STUB_PREFIX, + sizeof(SDT_PROBE_STUB_PREFIX) - 1) == 0) { + sdt_patch_reloc(&ef->lf, symname, base, offset); + return (1); + } + return (0); +} +#endif /* KDTRACE_HOOKS */ + static int relocate_file(elf_file_t ef) { @@ -1063,7 +1084,6 @@ relocate_file(elf_file_t ef) Elf_Size symidx; Elf_Addr base; - /* Perform relocations without addend if there are any: */ for (i = 0; i < ef->nreltab; i++) { rel = ef->reltab[i].rel; @@ -1088,6 +1108,11 @@ relocate_file(elf_file_t ef) if (elf_reloc(&ef->lf, base, rel, ELF_RELOC_REL, elf_obj_lookup)) { symname = symbol_name(ef, rel->r_info); +#ifdef KDTRACE_HOOKS + if (sdt_taste_reloc(ef, symname, rel->r_offset, + base)) + continue; +#endif printf("link_elf_obj: symbol %s undefined\n", symname); return (ENOENT); @@ -1120,6 +1145,11 @@ relocate_file(elf_file_t ef) if (elf_reloc(&ef->lf, base, rela, ELF_RELOC_RELA, elf_obj_lookup)) { symname = symbol_name(ef, rela->r_info); +#ifdef KDTRACE_HOOKS + if (sdt_taste_reloc(ef, symname, rela->r_offset, + base)) + continue; +#endif printf("link_elf_obj: symbol %s undefined\n", symname); return (ENOENT); diff --git a/sys/mips/mips/sdt_machdep.c b/sys/mips/mips/sdt_machdep.c new file mode 100644 index 0000000..e70fc34 --- /dev/null +++ b/sys/mips/mips/sdt_machdep.c @@ -0,0 +1,58 @@ +/*- + * Copyright (c) 2016 Ruslan Bukin + * All rights reserved. + * + * Portions of this software were developed by SRI International and the + * University of Cambridge Computer Laboratory under DARPA/AFRL contract + * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Portions of this software were developed by the University of Cambridge + * Computer Laboratory as part of the CTSRD Project, with support from the + * UK Higher Education Innovation Fund (HEIF). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#define OPCODE_MASK 0xfc000000 +#define OPCODE_JAL 0x0c000000 + +uint64_t +sdt_md_patch_callsite(struct sdt_probe *probe __unused, + uint64_t offset, bool reloc __unused) +{ + uint32_t instr; + + instr = *(uint32_t *)offset; + + if ((instr & OPCODE_MASK) != OPCODE_JAL) + return (0); + + return (offset); +} diff --git a/sys/modules/dtrace/sdt/Makefile b/sys/modules/dtrace/sdt/Makefile index b77299d..0a57186 100644 --- a/sys/modules/dtrace/sdt/Makefile +++ b/sys/modules/dtrace/sdt/Makefile @@ -5,10 +5,17 @@ SYSDIR?= ${.CURDIR}/../../.. .PATH: ${SYSDIR}/cddl/dev/sdt KMOD= sdt -SRCS= sdt.c +SRCS= sdt.c sdt_isa.c SRCS+= vnode_if.h -CFLAGS+= -I${SYSDIR}/cddl/compat/opensolaris \ +.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" +.PATH: ${SYSDIR}/cddl/dev/sdt/x86 +.else +.PATH: ${SYSDIR}/cddl/dev/sdt/${MACHINE_CPUARCH} +.endif + +CFLAGS+= -I${SYSDIR}/cddl/dev/sdt \ + -I${SYSDIR}/cddl/compat/opensolaris \ -I${SYSDIR}/cddl/contrib/opensolaris/uts/common \ -I${SYSDIR} diff --git a/sys/sys/sdt.h b/sys/sys/sdt.h index 25423d7..5a19a7b 100644 --- a/sys/sys/sdt.h +++ b/sys/sys/sdt.h @@ -1,5 +1,6 @@ /*- * Copyright 2006-2008 John Birrell + * Copyright (c) 2015 Mark Johnston * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,232 +24,177 @@ * SUCH DAMAGE. * * $FreeBSD$ - * + */ + +/* * Statically Defined Tracing (SDT) definitions. * + * This file contains macros for defining DTrace SDT probes and probe sites. + * It also contains the macros used to create userland SDT probes (USDT). */ #ifndef _SYS_SDT_H #define _SYS_SDT_H -#ifndef _KERNEL - -#define _DTRACE_VERSION 1 - -#define DTRACE_PROBE(prov, name) { \ - extern void __dtrace_##prov##___##name(void); \ - __dtrace_##prov##___##name(); \ -} - -#define DTRACE_PROBE1(prov, name, arg1) { \ - extern void __dtrace_##prov##___##name(unsigned long); \ - __dtrace_##prov##___##name((unsigned long)arg1); \ -} - -#define DTRACE_PROBE2(prov, name, arg1, arg2) { \ - extern void __dtrace_##prov##___##name(unsigned long, \ - unsigned long); \ - __dtrace_##prov##___##name((unsigned long)arg1, \ - (unsigned long)arg2); \ -} +#ifdef _KERNEL -#define DTRACE_PROBE3(prov, name, arg1, arg2, arg3) { \ - extern void __dtrace_##prov##___##name(unsigned long, \ - unsigned long, unsigned long); \ - __dtrace_##prov##___##name((unsigned long)arg1, \ - (unsigned long)arg2, (unsigned long)arg3); \ -} - -#define DTRACE_PROBE4(prov, name, arg1, arg2, arg3, arg4) { \ - extern void __dtrace_##prov##___##name(unsigned long, \ - unsigned long, unsigned long, unsigned long); \ - __dtrace_##prov##___##name((unsigned long)arg1, \ - (unsigned long)arg2, (unsigned long)arg3, \ - (unsigned long)arg4); \ -} - -#define DTRACE_PROBE5(prov, name, arg1, arg2, arg3, arg4, arg5) { \ - extern void __dtrace_##prov##___##name(unsigned long, \ - unsigned long, unsigned long, unsigned long, unsigned long);\ - __dtrace_##prov##___##name((unsigned long)arg1, \ - (unsigned long)arg2, (unsigned long)arg3, \ - (unsigned long)arg4, (unsigned long)arg5); \ -} - -#else /* _KERNEL */ +#ifdef KDTRACE_HOOKS #include #include -#ifndef KDTRACE_HOOKS - -#define SDT_PROVIDER_DEFINE(prov) -#define SDT_PROVIDER_DECLARE(prov) -#define SDT_PROBE_DEFINE(prov, mod, func, name) -#define SDT_PROBE_DECLARE(prov, mod, func, name) -#define SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) -#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type, xtype) - -#define SDT_PROBE_DEFINE0(prov, mod, func, name) -#define SDT_PROBE_DEFINE1(prov, mod, func, name, arg0) -#define SDT_PROBE_DEFINE2(prov, mod, func, name, arg0, arg1) -#define SDT_PROBE_DEFINE3(prov, mod, func, name, arg0, arg1, arg2) -#define SDT_PROBE_DEFINE4(prov, mod, func, name, arg0, arg1, arg2, arg3) -#define SDT_PROBE_DEFINE5(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) -#define SDT_PROBE_DEFINE6(prov, mod, func, name, arg0, arg1, arg2, \ - arg3, arg4, arg5) -#define SDT_PROBE_DEFINE7(prov, mod, func, name, arg0, arg1, arg2, \ - arg3, arg4, arg5, arg6) +/* + * Utility macros used further down in this file. + */ -#define SDT_PROBE0(prov, mod, func, name) -#define SDT_PROBE1(prov, mod, func, name, arg0) -#define SDT_PROBE2(prov, mod, func, name, arg0, arg1) -#define SDT_PROBE3(prov, mod, func, name, arg0, arg1, arg2) -#define SDT_PROBE4(prov, mod, func, name, arg0, arg1, arg2, arg3) -#define SDT_PROBE5(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) -#define SDT_PROBE6(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, arg5) -#define SDT_PROBE7(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, arg5, \ - arg6) +#define _SDT_PROBE_STUB_PREFIX __dtrace_sdt_ -#define SDT_PROBE_DEFINE0_XLATE(prov, mod, func, name) -#define SDT_PROBE_DEFINE1_XLATE(prov, mod, func, name, arg0, xarg0) -#define SDT_PROBE_DEFINE2_XLATE(prov, mod, func, name, arg0, xarg0, \ - arg1, xarg1) -#define SDT_PROBE_DEFINE3_XLATE(prov, mod, func, name, arg0, xarg0, \ - arg1, xarg1, arg2, xarg2) -#define SDT_PROBE_DEFINE4_XLATE(prov, mod, func, name, arg0, xarg0, \ - arg1, xarg1, arg2, xarg2, arg3, xarg3) -#define SDT_PROBE_DEFINE5_XLATE(prov, mod, func, name, arg0, xarg0, \ - arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4) -#define SDT_PROBE_DEFINE6_XLATE(prov, mod, func, name, arg0, xarg0, \ - arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5) -#define SDT_PROBE_DEFINE7_XLATE(prov, mod, func, name, arg0, xarg0, \ - arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5, arg6, \ - xarg6) +#define SDT_PROBE_STUB_PREFIX __XSTRING(_SDT_PROBE_STUB_PREFIX) -#define DTRACE_PROBE(name) -#define DTRACE_PROBE1(name, type0, arg0) -#define DTRACE_PROBE2(name, type0, arg0, type1, arg1) -#define DTRACE_PROBE3(name, type0, arg0, type1, arg1, type2, arg2) -#define DTRACE_PROBE4(name, type0, arg0, type1, arg1, type2, arg2, type3, arg3) -#define DTRACE_PROBE5(name, type0, arg0, type1, arg1, type2, arg2, type3, arg3,\ - type4, arg4) +#define _SDT_PROBE_STUB(prov, mod, func, name) \ + __dtrace_sdt_##prov##_##mod##_##func##_##name -#else +#define _SDT_PROBE_NAME(prov, mod, func, name) \ + sdt_##prov##_##mod##_##func##_##name -SET_DECLARE(sdt_providers_set, struct sdt_provider); -SET_DECLARE(sdt_probes_set, struct sdt_probe); -SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); +/* + * Macros for defining SDT probes and argument info. + */ -#define SDT_PROVIDER_DEFINE(prov) \ - struct sdt_provider sdt_provider_##prov[1] = { \ - { #prov, { NULL, NULL }, 0, 0 } \ - }; \ +#define SDT_PROVIDER_DEFINE(prov) \ + struct sdt_provider sdt_provider_##prov[1] = { \ + { #prov, { NULL, NULL }, 0, 0 } \ + }; \ DATA_SET(sdt_providers_set, sdt_provider_##prov); -#define SDT_PROVIDER_DECLARE(prov) \ +#define SDT_PROVIDER_DECLARE(prov) \ extern struct sdt_provider sdt_provider_##prov[1] -#define SDT_PROBE_DEFINE(prov, mod, func, name) \ - struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1] = { \ - { sizeof(struct sdt_probe), sdt_provider_##prov, \ - { NULL, NULL }, { NULL, NULL }, #mod, #func, #name, 0, 0, \ - NULL } \ - }; \ - DATA_SET(sdt_probes_set, sdt_##prov##_##mod##_##func##_##name); +#define SDT_PROBE_DEFINE(prov, mod, func, name) \ + struct sdt_probe _SDT_PROBE_NAME(prov, mod, func, name)[1] = { \ + { sizeof(struct sdt_probe), sdt_provider_##prov, \ + { NULL, NULL }, { NULL }, { NULL, NULL }, \ + #mod, #func, #name, 0, NULL } \ + }; \ + DATA_SET(sdt_probes_set, _SDT_PROBE_NAME(prov, mod, func, name)) -#define SDT_PROBE_DECLARE(prov, mod, func, name) \ - extern struct sdt_probe sdt_##prov##_##mod##_##func##_##name[1] +#define SDT_PROBE_DECLARE(prov, mod, func, name) \ + extern struct sdt_probe _SDT_PROBE_NAME(prov, mod, func, name)[1] -#define SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) do { \ +#define SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) do { \ if (__predict_false(sdt_##prov##_##mod##_##func##_##name->id)) \ (*sdt_probe_func)(sdt_##prov##_##mod##_##func##_##name->id, \ (uintptr_t) arg0, (uintptr_t) arg1, (uintptr_t) arg2, \ (uintptr_t) arg3, (uintptr_t) arg4); \ } while (0) -#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type, xtype) \ - static struct sdt_argtype sdta_##prov##_##mod##_##func##_##name##num[1] \ - = { { num, type, xtype, { NULL, NULL }, \ - sdt_##prov##_##mod##_##func##_##name } \ - }; \ - DATA_SET(sdt_argtypes_set, sdta_##prov##_##mod##_##func##_##name##num); +#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type, xtype) \ + static struct sdt_argtype sdta_##prov##_##mod##_##func##_##name##num[1] \ + = { { num, type, xtype, { NULL, NULL }, \ + _SDT_PROBE_NAME(prov, mod, func, name) } \ + }; \ + DATA_SET(sdt_argtypes_set, sdta_##prov##_##mod##_##func##_##name##num) + +SET_DECLARE(sdt_providers_set, struct sdt_provider); +SET_DECLARE(sdt_probes_set, struct sdt_probe); +SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); #define SDT_PROBE_DEFINE0(prov, mod, func, name) \ SDT_PROBE_DEFINE(prov, mod, func, name) +#define _SDT_PROBE_DEFINE0(prov, mod, func, name) \ + SDT_PROBE_DEFINE0(prov, mod, func, name) -#define SDT_PROBE_DEFINE1(prov, mod, func, name, arg0) \ +#define SDT_PROBE_DEFINE1(prov, mod, func, name, t0) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL) + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, t0, NULL) +#define _SDT_PROBE_DEFINE1(prov, mod, func, name, t0) \ + SDT_PROBE_DEFINE1(prov, mod, func, name, #t0) -#define SDT_PROBE_DEFINE2(prov, mod, func, name, arg0, arg1) \ +#define SDT_PROBE_DEFINE2(prov, mod, func, name, t0, t1) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL) + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, t0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, t1, NULL) +#define _SDT_PROBE_DEFINE2(prov, mod, func, name, t0, t1) \ + SDT_PROBE_DEFINE2(prov, mod, func, name, #t0, #t1) -#define SDT_PROBE_DEFINE3(prov, mod, func, name, arg0, arg1, arg2)\ +#define SDT_PROBE_DEFINE3(prov, mod, func, name, t0, t1, t2) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL) + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, t0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, t1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, t2, NULL) +#define _SDT_PROBE_DEFINE3(prov, mod, func, name, t0, t1, t2) \ + SDT_PROBE_DEFINE3(prov, mod, func, name, #t0, #t1, #t2) -#define SDT_PROBE_DEFINE4(prov, mod, func, name, arg0, arg1, arg2, arg3) \ +#define SDT_PROBE_DEFINE4(prov, mod, func, name, t0, t1, t2, t3) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, NULL) - -#define SDT_PROBE_DEFINE5(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, t0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, t1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, t2, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, t3, NULL) +#define _SDT_PROBE_DEFINE4(prov, mod, func, name, t0, t1, t2, t3) \ + SDT_PROBE_DEFINE4(prov, mod, func, name, #t0, #t1, #t2, #t3) + +#define SDT_PROBE_DEFINE5(prov, mod, func, name, t0, t1, t2, t3, t4) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, NULL) - -#define SDT_PROBE_DEFINE6(prov, mod, func, name, arg0, arg1, arg2, arg3,\ - arg4, arg5) \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, t0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, t1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, t2, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, t3, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, t4, NULL) +#define _SDT_PROBE_DEFINE5(prov, mod, func, name, t0, t1, t2, t3, t4) \ + SDT_PROBE_DEFINE5(prov, mod, func, name, #t0, #t1, #t2, #t3, \ + #t4) + +#define SDT_PROBE_DEFINE6(prov, mod, func, name, t0, t1, t2, t3, t4, \ + t5) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5, NULL) - -#define SDT_PROBE_DEFINE7(prov, mod, func, name, arg0, arg1, arg2, arg3,\ - arg4, arg5, arg6) \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, t0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, t1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, t2, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, t3, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, t4, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, t5, NULL) +#define _SDT_PROBE_DEFINE6(prov, mod, func, name, t0, t1, t2, t3, t4, \ + t5) \ + SDT_PROBE_DEFINE6(prov, mod, func, name, #t0, #t1, #t2, #t3, \ + #t4, #t5) + +#define SDT_PROBE_DEFINE7(prov, mod, func, name, t0, t1, t2, t3, t4, \ + t5, t6) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5, NULL); \ - SDT_PROBE_ARGTYPE(prov, mod, func, name, 6, arg6, NULL) - -#define SDT_PROBE_DEFINE0_XLATE(prov, mod, func, name) \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, t0, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, t1, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, t2, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, t3, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, t4, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, t5, NULL); \ + SDT_PROBE_ARGTYPE(prov, mod, func, name, 6, t6, NULL) +#define _SDT_PROBE_DEFINE7(prov, mod, func, name, t0, t1, t2, t3, t4, \ + t5, t6) \ + SDT_PROBE_DEFINE7(prov, mod, func, name, #t0, #t1, #t2, #t3, \ + #t4, #t5, #t6) + +#define SDT_PROBE_DEFINE0_XLATE(prov, mod, func, name) \ SDT_PROBE_DEFINE(prov, mod, func, name) -#define SDT_PROBE_DEFINE1_XLATE(prov, mod, func, name, arg0, xarg0) \ +#define SDT_PROBE_DEFINE1_XLATE(prov, mod, func, name, arg0, xarg0) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0) -#define SDT_PROBE_DEFINE2_XLATE(prov, mod, func, name, arg0, xarg0, \ +#define SDT_PROBE_DEFINE2_XLATE(prov, mod, func, name, arg0, xarg0, \ arg1, xarg1) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1) -#define SDT_PROBE_DEFINE3_XLATE(prov, mod, func, name, arg0, xarg0, \ +#define SDT_PROBE_DEFINE3_XLATE(prov, mod, func, name, arg0, xarg0, \ arg1, xarg1, arg2, xarg2) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, xarg2) -#define SDT_PROBE_DEFINE4_XLATE(prov, mod, func, name, arg0, xarg0, \ +#define SDT_PROBE_DEFINE4_XLATE(prov, mod, func, name, arg0, xarg0, \ arg1, xarg1, arg2, xarg2, arg3, xarg3) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ @@ -256,7 +202,7 @@ SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); SDT_PROBE_ARGTYPE(prov, mod, func, name, 2, arg2, xarg2); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, xarg3) -#define SDT_PROBE_DEFINE5_XLATE(prov, mod, func, name, arg0, xarg0, \ +#define SDT_PROBE_DEFINE5_XLATE(prov, mod, func, name, arg0, xarg0, \ arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ @@ -265,7 +211,7 @@ SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); SDT_PROBE_ARGTYPE(prov, mod, func, name, 3, arg3, xarg3); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, xarg4) -#define SDT_PROBE_DEFINE6_XLATE(prov, mod, func, name, arg0, xarg0, \ +#define SDT_PROBE_DEFINE6_XLATE(prov, mod, func, name, arg0, xarg0, \ arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ @@ -275,9 +221,9 @@ SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); SDT_PROBE_ARGTYPE(prov, mod, func, name, 4, arg4, xarg4); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5, xarg5) -#define SDT_PROBE_DEFINE7_XLATE(prov, mod, func, name, arg0, xarg0, \ - arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5, arg6, \ - xarg6) \ +#define SDT_PROBE_DEFINE7_XLATE(prov, mod, func, name, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5, \ + arg6, xarg6) \ SDT_PROBE_DEFINE(prov, mod, func, name); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 0, arg0, xarg0); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 1, arg1, xarg1); \ @@ -287,139 +233,268 @@ SET_DECLARE(sdt_argtypes_set, struct sdt_argtype); SDT_PROBE_ARGTYPE(prov, mod, func, name, 5, arg5, xarg5); \ SDT_PROBE_ARGTYPE(prov, mod, func, name, 6, arg6, xarg6) -#define SDT_PROBE0(prov, mod, func, name) \ - SDT_PROBE(prov, mod, func, name, 0, 0, 0, 0, 0) -#define SDT_PROBE1(prov, mod, func, name, arg0) \ - SDT_PROBE(prov, mod, func, name, arg0, 0, 0, 0, 0) -#define SDT_PROBE2(prov, mod, func, name, arg0, arg1) \ - SDT_PROBE(prov, mod, func, name, arg0, arg1, 0, 0, 0) -#define SDT_PROBE3(prov, mod, func, name, arg0, arg1, arg2) \ - SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, 0, 0) -#define SDT_PROBE4(prov, mod, func, name, arg0, arg1, arg2, arg3) \ - SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, 0) -#define SDT_PROBE5(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) \ - SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) -#define SDT_PROBE6(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, arg5) \ - do { \ - if (sdt_##prov##_##mod##_##func##_##name->id) \ - (*(void (*)(uint32_t, uintptr_t, uintptr_t, uintptr_t, \ - uintptr_t, uintptr_t, uintptr_t))sdt_probe_func)( \ - sdt_##prov##_##mod##_##func##_##name->id, \ - (uintptr_t)arg0, (uintptr_t)arg1, (uintptr_t)arg2, \ - (uintptr_t)arg3, (uintptr_t)arg4, (uintptr_t)arg5);\ - } while (0) -#define SDT_PROBE7(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, arg5, \ - arg6) \ - do { \ - if (sdt_##prov##_##mod##_##func##_##name->id) \ - (*(void (*)(uint32_t, uintptr_t, uintptr_t, uintptr_t, \ - uintptr_t, uintptr_t, uintptr_t, uintptr_t)) \ - sdt_probe_func)( \ - sdt_##prov##_##mod##_##func##_##name->id, \ - (uintptr_t)arg0, (uintptr_t)arg1, (uintptr_t)arg2, \ - (uintptr_t)arg3, (uintptr_t)arg4, (uintptr_t)arg5, \ - (uintptr_t)arg6); \ - } while (0) - -#define DTRACE_PROBE_IMPL_START(name, arg0, arg1, arg2, arg3, arg4) do { \ - static SDT_PROBE_DEFINE(sdt, , , name); \ - SDT_PROBE(sdt, , , name, arg0, arg1, arg2, arg3, arg4); -#define DTRACE_PROBE_IMPL_END } while (0) - -#define DTRACE_PROBE(name) \ - DTRACE_PROBE_IMPL_START(name, 0, 0, 0, 0, 0) \ - DTRACE_PROBE_IMPL_END - -#define DTRACE_PROBE1(name, type0, arg0) \ - DTRACE_PROBE_IMPL_START(name, arg0, 0, 0, 0, 0) \ - SDT_PROBE_ARGTYPE(sdt, , , name, 0, #type0, NULL); \ - DTRACE_PROBE_IMPL_END - -#define DTRACE_PROBE2(name, type0, arg0, type1, arg1) \ - DTRACE_PROBE_IMPL_START(name, arg0, arg1, 0, 0, 0) \ - SDT_PROBE_ARGTYPE(sdt, , , name, 0, #type0, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 1, #type1, NULL); \ - DTRACE_PROBE_IMPL_END - -#define DTRACE_PROBE3(name, type0, arg0, type1, arg1, type2, arg2) \ - DTRACE_PROBE_IMPL_START(name, arg0, arg1, arg2, 0, 0) \ - SDT_PROBE_ARGTYPE(sdt, , , name, 0, #type0, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 1, #type1, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 2, #type2, NULL); \ - DTRACE_PROBE_IMPL_END - -#define DTRACE_PROBE4(name, type0, arg0, type1, arg1, type2, arg2, type3, arg3) \ - DTRACE_PROBE_IMPL_START(name, arg0, arg1, arg2, arg3, 0) \ - SDT_PROBE_ARGTYPE(sdt, , , name, 0, #type0, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 1, #type1, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 2, #type2, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 3, #type3, NULL); \ - DTRACE_PROBE_IMPL_END - -#define DTRACE_PROBE5(name, type0, arg0, type1, arg1, type2, arg2, type3, arg3, \ - type4, arg4) \ - DTRACE_PROBE_IMPL_START(name, arg0, arg1, arg2, arg3, arg4) \ - SDT_PROBE_ARGTYPE(sdt, , , name, 0, #type0, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 1, #type1, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 2, #type2, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 3, #type3, NULL); \ - SDT_PROBE_ARGTYPE(sdt, , , name, 4, #type4, NULL); \ - DTRACE_PROBE_IMPL_END - -#endif /* KDTRACE_HOOKS */ - /* - * This type definition must match that of dtrace_probe. It is defined this - * way to avoid having to rely on CDDL code. + * Macros for defining probe sites. */ -typedef void (*sdt_probe_func_t)(uint32_t, uintptr_t arg0, uintptr_t arg1, - uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); + +#define SDT_PROBE0(prov, mod, func, name) do { \ + extern void _SDT_PROBE_STUB(prov, mod, func, name)(void); \ + _SDT_PROBE_STUB(prov, mod, func, name)(); \ +} while (0) + +#define SDT_PROBE1(prov, mod, func, name, arg0) do { \ + extern void _SDT_PROBE_STUB(prov, mod, func, name)(uintptr_t); \ + _SDT_PROBE_STUB(prov, mod, func, name)((uintptr_t)arg0); \ +} while (0) + +#define SDT_PROBE2(prov, mod, func, name, arg0, arg1) do { \ + extern void _SDT_PROBE_STUB(prov, mod, func, name)(uintptr_t, \ + uintptr_t); \ + _SDT_PROBE_STUB(prov, mod, func, name)((uintptr_t)arg0, \ + (uintptr_t)arg1); \ +} while (0) + +#define SDT_PROBE3(prov, mod, func, name, arg0, arg1, arg2) do { \ + extern void _SDT_PROBE_STUB(prov, mod, func, name)(uintptr_t, \ + uintptr_t, uintptr_t); \ + _SDT_PROBE_STUB(prov, mod, func, name)((uintptr_t)arg0, \ + (uintptr_t)arg1, (uintptr_t)arg2); \ +} while (0) + +#define SDT_PROBE4(prov, mod, func, name, arg0, arg1, arg2, arg3) do { \ + extern void _SDT_PROBE_STUB(prov, mod, func, name)(uintptr_t, \ + uintptr_t, uintptr_t, uintptr_t); \ + _SDT_PROBE_STUB(prov, mod, func, name)((uintptr_t)arg0, \ + (uintptr_t)arg1, (uintptr_t)arg2, (uintptr_t)arg3); \ +} while (0) + +#define SDT_PROBE5(prov, mod, func, name, arg0, arg1, arg2, arg3, \ + arg4) do { \ + extern void _SDT_PROBE_STUB(prov, mod, func, name)(uintptr_t, \ + uintptr_t, uintptr_t, uintptr_t, uintptr_t); \ + _SDT_PROBE_STUB(prov, mod, func, name)((uintptr_t)arg0, \ + (uintptr_t)arg1, (uintptr_t)arg2, (uintptr_t)arg3, \ + (uintptr_t)arg4); \ +} while (0) + +#define SDT_PROBE6(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, \ + arg5) do { \ + extern void _SDT_PROBE_STUB(prov, mod, func, name)(uintptr_t, \ + uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); \ + _SDT_PROBE_STUB(prov, mod, func, name)((uintptr_t)arg0, \ + (uintptr_t)arg1, (uintptr_t)arg2, (uintptr_t)arg3, \ + (uintptr_t)arg4, (uintptr_t)arg5); \ +} while (0) + +#define SDT_PROBE7(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, \ + arg5, arg6) do { \ + extern void _SDT_PROBE_STUB(prov, mod, func, name)(uintptr_t, \ + uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, \ + uintptr_t); \ + _SDT_PROBE_STUB(prov, mod, func, name)((uintptr_t)arg0, \ + (uintptr_t)arg1, (uintptr_t)arg2, (uintptr_t)arg3, \ + (uintptr_t)arg4, (uintptr_t)arg5, (uintptr_t)arg6); \ +} while (0) /* - * The 'sdt' provider will set it to dtrace_probe when it loads. + * SDT macros for compatibility with illumos. These define the probe + * at the probe site and use the "sdt" provider, and are thus intended + * for use in ad-hoc debugging and analysis. */ -extern sdt_probe_func_t sdt_probe_func; +#define DTRACE_PROBE(name) DTRACE_PROBE0(name) + +#define DTRACE_PROBE0(name) do { \ + static _SDT_PROBE_DEFINE0(sdt, , , name); \ + SDT_PROBE0(sdt, , , name); \ +} while (0) + +#define DTRACE_PROBE1(name, t0, arg0) do { \ + static _SDT_PROBE_DEFINE1(sdt, , , name, t0); \ + SDT_PROBE1(sdt, , , name, arg0); \ +} while (0) + +#define DTRACE_PROBE2(name, t0, arg0, t1, arg1) do { \ + static _SDT_PROBE_DEFINE2(sdt, , , name, t0, t1); \ + SDT_PROBE2(sdt, , , name, arg0, arg1); \ +} while (0) + +#define DTRACE_PROBE3(name, t0, arg0, t1, arg1, t2, arg2) do { \ + static _SDT_PROBE_DEFINE3(sdt, , , name, t0, t1, t2); \ + SDT_PROBE3(sdt, , , name, arg0, arg1, arg2); \ +} while (0) + +#define DTRACE_PROBE4(name, t0, arg0, t1, arg1, t2, arg2, t3, arg3) do { \ + static _SDT_PROBE_DEFINE4(sdt, , , name, t0, t1, t2, t3); \ + SDT_PROBE4(sdt, , , name, arg0, arg1, arg2, arg3); \ +} while (0) + +#define DTRACE_PROBE5(name, t0, arg0, t1, arg1, t2, arg2, t3, arg3, t4, \ + arg4) do { \ + static _SDT_PROBE_DEFINE5(sdt, , , name, t0, t1, t2, t3, t4); \ + SDT_PROBE5(sdt, , , name, arg0, arg1, arg2, arg3, arg4); \ +} while (0) + +#define DTRACE_PROBE6(name, t0, arg0, t1, arg1, t2, arg2, t3, arg3, t4, \ + arg4, t5, arg5) do { \ + static _SDT_PROBE_DEFINE6(sdt, , , name, t0, t1, t2, t3, t4, \ + t5); \ + SDT_PROBE6(sdt, , , name, arg0, arg1, arg2, arg3, arg4, arg5); \ +} while (0) + +#define DTRACE_PROBE7(name, t0, arg0, t1, arg1, t2, arg2, t3, arg3, t4, \ + arg4, t5, arg5, t6, arg6) do { \ + static _SDT_PROBE_DEFINE7(sdt, , , name, t0, t1, t2, t3, t4, \ + t5, t6); \ + SDT_PROBE7(sdt, , , name, arg0, arg1, arg2, arg3, arg4, arg5, \ + arg6); \ +} while (0) + +#else /* !KDTRACE_HOOKS */ + +#define SDT_PROVIDER_DEFINE(prov) +#define SDT_PROVIDER_DECLARE(prov) +#define SDT_PROBE_DEFINE(prov, mod, func, name) +#define SDT_PROBE_DECLARE(prov, mod, func, name) +#define SDT_PROBE(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) +#define SDT_PROBE_ARGTYPE(prov, mod, func, name, num, type, xtype) + +#define SDT_PROBE_DEFINE0(prov, mod, func, name) +#define SDT_PROBE_DEFINE1(prov, mod, func, name, arg0) +#define SDT_PROBE_DEFINE2(prov, mod, func, name, arg0, arg1) +#define SDT_PROBE_DEFINE3(prov, mod, func, name, arg0, arg1, arg2) +#define SDT_PROBE_DEFINE4(prov, mod, func, name, arg0, arg1, arg2, arg3) +#define SDT_PROBE_DEFINE5(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) +#define SDT_PROBE_DEFINE6(prov, mod, func, name, arg0, arg1, arg2, arg3, \ + arg4, arg5) +#define SDT_PROBE_DEFINE7(prov, mod, func, name, arg0, arg1, arg2, arg3, \ + arg4, arg5, arg6) + +#define SDT_PROBE0(prov, mod, func, name) +#define SDT_PROBE1(prov, mod, func, name, arg0) +#define SDT_PROBE2(prov, mod, func, name, arg0, arg1) +#define SDT_PROBE3(prov, mod, func, name, arg0, arg1, arg2) +#define SDT_PROBE4(prov, mod, func, name, arg0, arg1, arg2, arg3) +#define SDT_PROBE5(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4) +#define SDT_PROBE6(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, arg5) +#define SDT_PROBE7(prov, mod, func, name, arg0, arg1, arg2, arg3, arg4, \ + arg5, arg6) + +#define SDT_PROBE_DEFINE0_XLATE(prov, mod, func, name) +#define SDT_PROBE_DEFINE1_XLATE(prov, mod, func, name, arg0, xarg0) +#define SDT_PROBE_DEFINE2_XLATE(prov, mod, func, name, arg0, xarg0, \ + arg1, xarg1) +#define SDT_PROBE_DEFINE3_XLATE(prov, mod, func, name, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2) +#define SDT_PROBE_DEFINE4_XLATE(prov, mod, func, name, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3) +#define SDT_PROBE_DEFINE5_XLATE(prov, mod, func, name, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4) +#define SDT_PROBE_DEFINE6_XLATE(prov, mod, func, name, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5) +#define SDT_PROBE_DEFINE7_XLATE(prov, mod, func, name, arg0, xarg0, \ + arg1, xarg1, arg2, xarg2, arg3, xarg3, arg4, xarg4, arg5, xarg5, \ + arg6, xarg6) + +#define DTRACE_PROBE(name) +#define DTRACE_PROBE1(name, t0, arg0) +#define DTRACE_PROBE2(name, t0, arg0, t1, arg1) +#define DTRACE_PROBE3(name, t0, arg0, t1, arg1, t2, arg2) +#define DTRACE_PROBE4(name, t0, arg0, t1, arg1, t2, arg2, t3, arg3) +#define DTRACE_PROBE5(name, t0, arg0, t1, arg1, t2, arg2, t3, arg3, t4, arg4) + +#endif /* KDTRACE_HOOKS */ + +struct linker_file; struct sdt_probe; struct sdt_provider; -struct linker_file; struct sdt_argtype { int ndx; /* Argument index. */ const char *type; /* Argument type string. */ const char *xtype; /* Translated argument type. */ - TAILQ_ENTRY(sdt_argtype) - argtype_entry; /* Argument type list entry. */ + TAILQ_ENTRY(sdt_argtype) argtype_entry; /* Argument type list entry. */ struct sdt_probe *probe; /* Ptr to the probe structure. */ }; struct sdt_probe { int version; /* Set to sizeof(struct sdt_probe). */ struct sdt_provider *prov; /* Ptr to the provider structure. */ - TAILQ_ENTRY(sdt_probe) - probe_entry; /* SDT probe list entry. */ + TAILQ_ENTRY(sdt_probe) probe_entry; /* SDT probe list entry. */ + SLIST_HEAD(, sdt_probedesc) site_list; /* probe sites */ TAILQ_HEAD(, sdt_argtype) argtype_list; const char *mod; const char *func; const char *name; - id_t id; /* DTrace probe ID. */ int n_args; /* Number of arguments. */ struct linker_file *sdtp_lf; /* Module in which we're defined. */ }; struct sdt_provider { char *name; /* Provider name. */ - TAILQ_ENTRY(sdt_provider) - prov_entry; /* SDT provider list entry. */ + TAILQ_ENTRY(sdt_provider) prov_entry; /* SDT provider list entry. */ uintptr_t id; /* DTrace provider ID. */ int sdt_refs; /* Number of module references. */ }; -void sdt_probe_stub(uint32_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, - uintptr_t); +struct sdt_probedesc { + union { + SLIST_ENTRY(sdt_probedesc) spd_entry; + struct sdt_probe *spd_probe; + const char *spd_probename; + } li; + u_long spd_offset; +}; SDT_PROVIDER_DECLARE(sdt); -#endif /* _KERNEL */ +uint64_t sdt_md_patch_callsite(struct sdt_probe *, uint64_t, bool); +void sdt_patch_reloc(struct linker_file *, const char *, uint64_t, uint64_t); + +#else /* !_KERNEL */ + +#define _DTRACE_VERSION 1 + +#define DTRACE_PROBE(prov, name) { \ + extern void __dtrace_##prov##___##name(void); \ + __dtrace_##prov##___##name(); \ +} + +#define DTRACE_PROBE1(prov, name, arg1) { \ + extern void __dtrace_##prov##___##name(unsigned long); \ + __dtrace_##prov##___##name((unsigned long)arg1); \ +} + +#define DTRACE_PROBE2(prov, name, arg1, arg2) { \ + extern void __dtrace_##prov##___##name(unsigned long, \ + unsigned long); \ + __dtrace_##prov##___##name((unsigned long)arg1, \ + (unsigned long)arg2); \ +} + +#define DTRACE_PROBE3(prov, name, arg1, arg2, arg3) { \ + extern void __dtrace_##prov##___##name(unsigned long, \ + unsigned long, unsigned long); \ + __dtrace_##prov##___##name((unsigned long)arg1, \ + (unsigned long)arg2, (unsigned long)arg3); \ +} + +#define DTRACE_PROBE4(prov, name, arg1, arg2, arg3, arg4) { \ + extern void __dtrace_##prov##___##name(unsigned long, \ + unsigned long, unsigned long, unsigned long); \ + __dtrace_##prov##___##name((unsigned long)arg1, \ + (unsigned long)arg2, (unsigned long)arg3, \ + (unsigned long)arg4); \ +} + +#define DTRACE_PROBE5(prov, name, arg1, arg2, arg3, arg4, arg5) { \ + extern void __dtrace_##prov##___##name(unsigned long, \ + unsigned long, unsigned long, unsigned long, unsigned long);\ + __dtrace_##prov##___##name((unsigned long)arg1, \ + (unsigned long)arg2, (unsigned long)arg3, \ + (unsigned long)arg4, (unsigned long)arg5); \ +} + +#endif /* !_KERNEL */ #endif /* _SYS_SDT_H */ diff --git a/sys/tools/sdtstrip.sh b/sys/tools/sdtstrip.sh new file mode 100644 index 0000000..88d1baa --- /dev/null +++ b/sys/tools/sdtstrip.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# +# Copyright (c) 2015 Mark Johnston +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +# +# Strip SDT probe stub symbols. After the kernel has been linked, they +# are not needed. +# +# Ideally we'd be able to do this with a simple pipeline in which we use +# objcopy --strip-symbols=/dev/stdin, but objcopy pouts if the argument +# to --strip-symbols isn't a regular file. +# + +obj=$1 +if [ ! -f "$obj" -o $# -ne 1 ]; then + echo "usage: $(basename $0) " >&2 + exit 1 +fi + +set -e + +symfile=$(mktemp) +${OBJDUMP} -j .text -t $obj | \ + ${AWK} '$NF ~ /^__dtrace_sdt_/{print $NF}' > $symfile +${OBJCOPY} --strip-symbols=${symfile} $obj +rm -f $symfile diff --git a/sys/tools/sdtstubs.sh b/sys/tools/sdtstubs.sh new file mode 100644 index 0000000..4c54212 --- /dev/null +++ b/sys/tools/sdtstubs.sh @@ -0,0 +1,123 @@ +#!/bin/sh +# +# Copyright (c) 2015 Mark Johnston +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# + +# +# Generate SDT probe stubs and record stub relocation info. +# + +obj=$1 +if [ ! -f "$obj" -o $# -ne 1 ]; then + echo "usage: $(basename $0) " >&2 + exit 1 +fi + +set -e + +class=$(readelf --file-header "$obj" | ${AWK} '/Class: /{print $2}') +case $class in +ELF32) + directive=long ;; +ELF64) + directive=quad ;; +*) + echo "$(basename $0): unknown ELF class $class" >&2 + exit 1 + ;; +esac + +# Emit the preamble. +cat <<__EOF__ +/* + * This is a machine-generated file - do not edit it! + */ +#include + +void _sdt_probe_stub(void); + +/* + * A no-op stub used as an alias for all SDT probe site stubs. + */ +void +_sdt_probe_stub(void) +{ +} + +__EOF__ + +# Make each probe stub an alias of _sdt_probe_stub(). +${NM} -u "$obj" | \ + ${AWK} ' +/^[[:space:]]*U __dtrace_sdt_[_[:alpha:]]+[_[:alnum:]]*$/ { + printf "__strong_reference(_sdt_probe_stub, %s);\n", $2; +}' + +# Emit a linker set containing a struct sdt_probedesc for each relocation +# against an SDT probe stub. +${OBJDUMP} -r -j .text "$obj" | \ + ${AWK} -v directive=$directive ' +function create_set(set) +{ + printf " \".pushsection set_%s_set, \\\"a\\\"\\n\"\n", set; + printf " \".align 8\\n\"\n"; + printf " \".global __start_set_%s_set\\n\"\n", set; + printf " \".global __stop_set_%s_set\\n\"\n", set; + printf " \".popsection\\n\"\n"; +} + +function emit_item(set, symname, addr) +{ + printf " \".pushsection set_%s_set, \\\"a\\\"\\n\"\n", set; + printf " \".%s %s\\n\"\n", directive, symname; + printf " \".%s 0x%s\\n\"\n", directive, addr; + printf " \".popsection\\n\"\n"; +} + +BEGIN { + print "__asm__("; + + create_set("sdt_probe_site"); + create_set("sdt_anon_probe_site"); +} + +$3 ~ /^__dtrace_sdt_[_[:alpha:]]+[_[:alnum:]]*\+?/ { + match($3, /sdt_[_[:alpha:]]+[_[:alnum:]]*/); + symname = substr($3, 10, RLENGTH); # 10 is strlen("__dtrace_") + 1. + if (substr(symname, 0, 8) == "sdt_sdt_") { + printf " \"1:\\n\"\n"; + printf " \".string \\\"%s\\\"\\n\"\n", symname; + emit_item("sdt_anon_probe_site", "1b", $1); + } else { + printf " \".global %s\\n\"\n", symname; + emit_item("sdt_probe_site", symname, $1); + } +} + +END { + print ");"; +}' diff --git a/sys/x86/x86/sdt_machdep.c b/sys/x86/x86/sdt_machdep.c new file mode 100644 index 0000000..5678e6c --- /dev/null +++ b/sys/x86/x86/sdt_machdep.c @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 2016 Mark Johnston + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#define X86_OPC_CALL32 0xe8 +#define X86_OPC_JMP32 0xe9 +#define X86_OPC_NOP 0x90 +#define X86_OPC_RET 0xc3 + +static const uint8_t nop3[3] = { 0x0f, 0x1f, 0x00 }; +static const uint8_t nop4[4] = { 0x0f, 0x1f, 0x40, 0x00 }; + +/* + * Defined by sdtstubs.sh at compile-time. + */ +void _sdt_probe_stub(void); + +uint64_t +sdt_md_patch_callsite(struct sdt_probe *probe, uint64_t offset, bool reloc) +{ + uintptr_t stubaddr; + uint32_t target; + uint8_t *callinstr, opcode; + + callinstr = (uint8_t *)(uintptr_t)(offset - 1); + opcode = callinstr[0]; + if (opcode != X86_OPC_CALL32 && opcode != X86_OPC_JMP32) { + printf("sdt: opcode mismatch (0x%x) for %s:::%s@%p\n", + opcode, probe->prov->name, probe->name, + (void *)(uintptr_t)offset); + return (0); + } + + /* + * If we've been passed a probe descriptor, verify that the call/jmp + * target is in fact the SDT stub. If it's not, something's wrong and + * we shouldn't touch anything. + */ + stubaddr = (uintptr_t)_sdt_probe_stub; + memcpy(&target, &callinstr[1], sizeof(target)); + if (!reloc && roundup2(target + (uintptr_t)callinstr, 16) != stubaddr) { + printf("sdt: offset mismatch: %p vs. %p\n", + (void *)roundup2(target + (uintptr_t)callinstr, 16), + (void *)stubaddr); + return (0); + } + + switch (opcode) { + case X86_OPC_CALL32: + callinstr[0] = X86_OPC_NOP; + memcpy(callinstr + 1, nop4, sizeof(nop4)); + break; + case X86_OPC_JMP32: + /* + * The probe site is a tail call, so we need a "ret" + * when the probe isn't enabled. We overwrite the second + * byte instead of the first: the first byte will be + * replaced with a breakpoint when the probe is enabled. + * + * XXX emulate ret for this case to avoid the extra nop + */ + callinstr[0] = X86_OPC_NOP; + callinstr[1] = X86_OPC_RET; + memcpy(callinstr + 2, nop3, sizeof(nop3)); + break; + } + return ((uint64_t)(uintptr_t)callinstr); +}