From f2a3f85e8cf599f4ae23b9d3bfd4fb272d9b98d2 Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Wed, 23 Nov 2016 18:24:10 +0000 Subject: [PATCH] dtrace: is-enabled probes for SDT This is the module side of the is-enabled probe implementation. SDT distinguishes is-enabled probes from normal probes by the leading ? in their sdpd_name; at probe-firing time, the arch-dependent code arranges to return 1 appropriately. On x86, also arrange to jump past the probe's NOP region. There was no need to do this before now, because a trap followed by a bunch of NOPs is a perfectly valid instruction stream: but is-enabled probes have a three-byte sequence implementing "xor %rax, %rax", and overwriting only the first byte of that leaves us with a couple of bytes that must be skipped. On SPARC, we drop the necessary return-value-changing instruction into the delay slot of the call that used to be there before we overwrote it with NOPs;: the instruction already there is setting up the function argument-and-return-value, which is 0 when the probe is disabled, so we can overwrite it safely. (We make minor adjustments to allow sdt_provide_probe_arch() to safely modify the sdp_patchpoint.) Finally, add a test use of an is-enabled probe to dt_test, used by the DTrace testsuite. [nca: sparc implementation, ip address adjustment, commit msg] Signed-off-by: Nick Alcock Acked-by: Kris Van Hees Orabug: 25143173 --- dtrace/dt_test_dev.c | 6 ++- dtrace/include/dtrace/dtrace_impl_defines.h | 8 --- dtrace/sdt_dev.c | 22 +++++++-- dtrace/sdt_impl.h | 7 +++ dtrace/sdt_sparc64.c | 54 +++++++++++++-------- dtrace/sdt_x86_64.c | 16 +++--- 6 files changed, 74 insertions(+), 39 deletions(-) diff --git a/dtrace/dt_test_dev.c b/dtrace/dt_test_dev.c index f833fe9b26df..1a6405c892a2 100644 --- a/dtrace/dt_test_dev.c +++ b/dtrace/dt_test_dev.c @@ -21,7 +21,7 @@ * * CDDL HEADER END * - * Copyright 2011-2014 Oracle, Inc. All rights reserved. + * Copyright 2011-2016 Oracle, Inc. All rights reserved. * Use is subject to license terms. */ @@ -99,6 +99,10 @@ static long dt_test_ioctl(struct file *file, return 0; } + if (DTRACE_PROBE_ENABLED(sdt__test)) { + DTRACE_PROBE(sdt__test__is__enabled) + } + DTRACE_PROBE(sdt__test); /* diff --git a/dtrace/include/dtrace/dtrace_impl_defines.h b/dtrace/include/dtrace/dtrace_impl_defines.h index 737dd50fb8c3..54d5b2518a9c 100644 --- a/dtrace/include/dtrace/dtrace_impl_defines.h +++ b/dtrace/include/dtrace/dtrace_impl_defines.h @@ -157,14 +157,6 @@ typedef enum dtrace_speculation_state { #define KERNELBASE (uintptr_t)_text -#if defined(__i386__) || defined(__x86_64__) -# define DTRACE_INVOP_PUSHL_EBP 1 -# define DTRACE_INVOP_POPL_EBP 2 -# define DTRACE_INVOP_LEAVE 3 -# define DTRACE_INVOP_NOP 4 -# define DTRACE_INVOP_RET 5 -#endif - #ifdef CONFIG_DT_DEBUG_MUTEX # define real_mutex_lock(x) mutex_lock(x) # define real_mutex_unlock(x) mutex_unlock(x) diff --git a/dtrace/sdt_dev.c b/dtrace/sdt_dev.c index ad6557ce456d..4b29b66d2ed1 100644 --- a/dtrace/sdt_dev.c +++ b/dtrace/sdt_dev.c @@ -21,7 +21,7 @@ * * CDDL HEADER END * - * Copyright 2010-2014 Oracle, Inc. All rights reserved. + * Copyright 2010-2016 Oracle, Inc. All rights reserved. * Use is subject to license terms. */ @@ -297,6 +297,13 @@ void sdt_provide_module(void *arg, struct module *mp) int i, j; dtrace_mprovider_t *prov; dtrace_id_t id; + sdt_probe_type_t ptype; + + if (name[0] == '?') { + ptype = SDTPT_IS_ENABLED; + name++; + } else + ptype = SDTPT_OFFSETS; for (prov = sdt_providers; prov->dtmp_pref != NULL; prov++) { char *prefix = prov->dtmp_pref; @@ -337,6 +344,7 @@ void sdt_provide_module(void *arg, struct module *mp) sdp->sdp_name = nname; sdp->sdp_namelen = len; sdp->sdp_provider = prov; + sdp->sdp_ptype = ptype; sdp->sdp_argdesc = sdt_setup_args(sdpd, &sdp->sdp_nargdesc); @@ -358,13 +366,13 @@ void sdt_provide_module(void *arg, struct module *mp) PDATA(mp)->sdt_probe_cnt++; } - sdp->sdp_hashnext = sdt_probetab[ - SDT_ADDR2NDX(sdpd->sdpd_offset)]; - sdt_probetab[SDT_ADDR2NDX(sdpd->sdpd_offset)] = sdp; - sdp->sdp_patchpoint = (asm_instr_t *)sdpd->sdpd_offset; sdt_provide_probe_arch(sdp, mp, idx); + + sdp->sdp_hashnext = sdt_probetab[ + SDT_ADDR2NDX(sdp->sdp_patchpoint)]; + sdt_probetab[SDT_ADDR2NDX(sdp->sdp_patchpoint)] = sdp; } } @@ -423,6 +431,10 @@ void sdt_getargdesc(void *arg, dtrace_id_t id, void *parg, desc->dtargd_native[0] = '\0'; desc->dtargd_xlate[0] = '\0'; + while ((sdp->sdp_ptype == SDTPT_IS_ENABLED) && + (sdp->sdp_next != NULL)) + sdp = sdp->sdp_next; + if (sdp->sdp_nargdesc <= desc->dtargd_ndx) { desc->dtargd_ndx = DTRACE_ARGNONE; return; diff --git a/dtrace/sdt_impl.h b/dtrace/sdt_impl.h index 2b03eee30e5f..785dc4fd02af 100644 --- a/dtrace/sdt_impl.h +++ b/dtrace/sdt_impl.h @@ -9,6 +9,12 @@ extern struct module *dtrace_kmod; struct sdt_argdesc; +typedef enum fasttrap_probe_type { + SDTPT_NONE = 0, + SDTPT_OFFSETS, + SDTPT_IS_ENABLED +} sdt_probe_type_t; + typedef struct sdt_probe { dtrace_mprovider_t *sdp_provider; /* provider */ char *sdp_name; /* name of probe */ @@ -17,6 +23,7 @@ typedef struct sdt_probe { struct module *sdp_module; /* modctl for module */ int sdp_loadcnt; /* load count for module */ int sdp_primary; /* non-zero if primary mod */ + sdt_probe_type_t sdp_ptype; /* probe type */ asm_instr_t *sdp_patchpoint;/* patch point */ asm_instr_t sdp_patchval; /* instruction to patch */ asm_instr_t sdp_savedval; /* saved instruction value */ diff --git a/dtrace/sdt_sparc64.c b/dtrace/sdt_sparc64.c index a1f77431d4b6..65bc8d07ca41 100644 --- a/dtrace/sdt_sparc64.c +++ b/dtrace/sdt_sparc64.c @@ -60,6 +60,9 @@ * mov %i4, %o5 * ret * restore + * + * For is-enabled probes, we just drop an "or %g0, 1, %o0" + * directly into the delay slot. */ #if SDT_TRAMP_SIZE < 11 # error SDT_TRAMP_SIZE is less than the required 11 instructions. @@ -124,27 +127,40 @@ void sdt_provide_probe_arch(sdt_probe_t *sdp, struct module *mp, int idx) SDT_TRAMP_SIZE]); asm_instr_t *instr = trampoline; - *instr++ = SDT_SAVE; - - if (sdp->sdp_id > (uint32_t)SDT_SIMM13_MAX) { - *instr++ = SDT_SETHI(sdp->sdp_id, SDT_REG_O0); - *instr++ = SDT_ORLO(SDT_REG_O0, sdp->sdp_id, SDT_REG_O0); - } else { - *instr++ = SDT_ORSIMM13(SDT_REG_G0, sdp->sdp_id, SDT_REG_O0); + if (sdp->sdp_ptype == SDTPT_OFFSETS) { + *instr++ = SDT_SAVE; + + if (sdp->sdp_id > (uint32_t)SDT_SIMM13_MAX) { + *instr++ = SDT_SETHI(sdp->sdp_id, SDT_REG_O0); + *instr++ = SDT_ORLO(SDT_REG_O0, sdp->sdp_id, + SDT_REG_O0); + } else { + *instr++ = SDT_ORSIMM13(SDT_REG_G0, sdp->sdp_id, + SDT_REG_O0); + } + + *instr++ = SDT_MOV(SDT_REG_I0, SDT_REG_O1); + *instr++ = SDT_MOV(SDT_REG_I1, SDT_REG_O2); + *instr++ = SDT_MOV(SDT_REG_I2, SDT_REG_O3); + *instr++ = SDT_MOV(SDT_REG_I3, SDT_REG_O4); + *instr = SDT_CALL(instr, dtrace_probe); + instr++; + *instr++ = SDT_MOV(SDT_REG_I4, SDT_REG_O5); + + *instr++ = SDT_RET; + *instr++ = SDT_RESTORE; + + sdp->sdp_patchval = SDT_CALL(sdp->sdp_patchpoint, trampoline); + } else { /* SDTPT_IS_ENABLED */ + /* + * We want to change the insn in the delay slot, + * which will be the arg setup. There is no + * trampoline. + */ + sdp->sdp_patchpoint++; /* next insn */ + sdp->sdp_patchval = SDT_ORSIMM13(SDT_REG_G0, 1, SDT_REG_O0); } - *instr++ = SDT_MOV(SDT_REG_I0, SDT_REG_O1); - *instr++ = SDT_MOV(SDT_REG_I1, SDT_REG_O2); - *instr++ = SDT_MOV(SDT_REG_I2, SDT_REG_O3); - *instr++ = SDT_MOV(SDT_REG_I3, SDT_REG_O4); - *instr = SDT_CALL(instr, dtrace_probe); - instr++; - *instr++ = SDT_MOV(SDT_REG_I4, SDT_REG_O5); - - *instr++ = SDT_RET; - *instr++ = SDT_RESTORE; - - sdp->sdp_patchval = SDT_CALL(sdp->sdp_patchpoint, trampoline); sdp->sdp_savedval = *sdp->sdp_patchpoint; } diff --git a/dtrace/sdt_x86_64.c b/dtrace/sdt_x86_64.c index ac0226c7079d..69440d2ce2e8 100644 --- a/dtrace/sdt_x86_64.c +++ b/dtrace/sdt_x86_64.c @@ -21,7 +21,7 @@ * * CDDL HEADER END * - * Copyright 2010-2014 Oracle, Inc. All rights reserved. + * Copyright 2010-2016 Oracle, Inc. All rights reserved. * Use is subject to license terms. */ @@ -42,14 +42,18 @@ static uint8_t sdt_invop(struct pt_regs *regs) for (; sdt != NULL; sdt = sdt->sdp_hashnext) { if ((uintptr_t)sdt->sdp_patchpoint == regs->ip) { - this_cpu_core->cpu_dtrace_regs = regs; + if (sdt->sdp_ptype == SDTPT_IS_ENABLED) + regs->ax = 1; + else { + this_cpu_core->cpu_dtrace_regs = regs; - dtrace_probe(sdt->sdp_id, regs->di, regs->si, - regs->dx, regs->cx, regs->r8); + dtrace_probe(sdt->sdp_id, regs->di, regs->si, + regs->dx, regs->cx, regs->r8); - this_cpu_core->cpu_dtrace_regs = NULL; + this_cpu_core->cpu_dtrace_regs = NULL; + } - return DTRACE_INVOP_NOP; + return ASM_CALL_SIZE; } } -- 2.50.1