Index: cddl/contrib/opensolaris/common/ctf/ctf_create.c =================================================================== --- cddl/contrib/opensolaris/common/ctf/ctf_create.c +++ cddl/contrib/opensolaris/common/ctf/ctf_create.c @@ -300,6 +300,9 @@ case CTF_K_ENUM: size += sizeof (ctf_enum_t) * vlen; break; + case CTF_K_CONSTVAL: + size += vlen; + break; } } @@ -407,6 +410,25 @@ t = ctf_copy_emembers(dtd, (uint_t)(s - s0), t); s = ctf_copy_membnames(dtd, s); break; + + case CTF_K_CONSTVAL: + switch (vlen) { + case 0: + break; + case 4: { + int32_t i32 = dtd->dtd_u.dtu_extra; + + memcpy(t, &i32, sizeof(i32)); + t += sizeof(i32); + } + break; + case 8: + memcpy(t, &dtd->dtd_u.dtu_extra, + sizeof(dtd->dtd_u.dtu_extra)); + t += sizeof(dtd->dtd_u.dtu_extra); + break; + } + break; } } @@ -532,6 +554,7 @@ case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: + case CTF_K_CONSTVAL: ctf_ref_dec(fp, dtd->dtd_data.ctt_type); break; } @@ -982,6 +1005,38 @@ return (ctf_add_reftype(fp, flag, ref, CTF_K_RESTRICT)); } +/* cribbed from ctf_add_reftype() */ +static ctf_id_t +ctf_add_constval(ctf_file_t *fp, ctf_id_t ref, int vlen, int64_t val) +{ + ctf_dtdef_t *dtd; + ctf_id_t type; + + if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE) + return (ctf_set_errno(fp, EINVAL)); + + if ((type = ctf_add_generic(fp, CTF_ADD_NONROOT, NULL, &dtd)) == + CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + ctf_ref_inc(fp, ref); + + if (vlen < 0) { + if (val == 0) + vlen = 0; + else if (val >= INT32_MIN && val <= INT32_MAX) + vlen = 4; + else + vlen = 8; + } + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_CONSTVAL, 0, vlen); + dtd->dtd_data.ctt_type = (ushort_t)ref; + dtd->dtd_u.dtu_extra = val; + + return (type); +} + int ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value) { @@ -1399,6 +1454,41 @@ dst_type = ctf_add_reftype(dst_fp, flag, src_type, kind); break; + case CTF_K_CONSTVAL: { + ctf_id_t ref_type; + ssize_t increment; + int64_t i64; + int32_t i32; + void *dptr; + + ref_type = ctf_type_reference(src_fp, src_type); + ref_type = ctf_add_type(dst_fp, src_fp, ref_type); + + if (ref_type == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + (void) ctf_get_ctt_size(src_fp, tp, NULL, &increment); + dptr = (void *)((uintptr_t)tp + increment); + + switch (vlen) { + case 0: + i64 = 0; + break; + case 4: + memcpy(&i32, dptr, sizeof(i32)); + i64 = i32; + break; + case 8: + memcpy(&i64, dptr, sizeof(i64)); + break; + default: + return (ctf_set_errno(src_fp, EINVAL)); + } + + dst_type = ctf_add_constval(dst_fp, ref_type, vlen, i64); + break; + } + case CTF_K_ARRAY: if (ctf_array_info(src_fp, src_type, &src_ar) == CTF_ERR) return (ctf_set_errno(dst_fp, ctf_errno(src_fp))); Index: cddl/contrib/opensolaris/common/ctf/ctf_impl.h =================================================================== --- cddl/contrib/opensolaris/common/ctf/ctf_impl.h +++ cddl/contrib/opensolaris/common/ctf/ctf_impl.h @@ -156,6 +156,7 @@ ctf_arinfo_t dtu_arr; /* array */ ctf_encoding_t dtu_enc; /* integer or float */ ctf_id_t *dtu_argv; /* function */ + int64_t dtu_extra; /* constval */ } dtd_u; } ctf_dtdef_t; Index: cddl/contrib/opensolaris/common/ctf/ctf_open.c =================================================================== --- cddl/contrib/opensolaris/common/ctf/ctf_open.c +++ cddl/contrib/opensolaris/common/ctf/ctf_open.c @@ -291,6 +291,10 @@ child |= CTF_TYPE_ISCHILD(tp->ctt_type); vbytes = 0; break; + case CTF_K_CONSTVAL: + child |= CTF_TYPE_ISCHILD(tp->ctt_type); + vbytes = vlen; + break; default: ctf_dprintf("detected invalid CTF kind -- %u\n", kind); return (ECTF_CORRUPT); @@ -325,7 +329,7 @@ if ((err = ctf_hash_create(&fp->ctf_names, pop[CTF_K_INTEGER] + pop[CTF_K_FLOAT] + pop[CTF_K_FUNCTION] + pop[CTF_K_TYPEDEF] + pop[CTF_K_POINTER] + pop[CTF_K_VOLATILE] + - pop[CTF_K_CONST] + pop[CTF_K_RESTRICT])) != 0) + pop[CTF_K_CONST] + pop[CTF_K_RESTRICT] + pop[CTF_K_CONSTVAL])) != 0) return (err); fp->ctf_txlate = ctf_alloc(sizeof (uint_t) * (fp->ctf_typemax + 1)); @@ -497,6 +501,10 @@ default: vbytes = 0; break; + + case CTF_K_CONSTVAL: + vbytes = vlen; + break; } *xp = (uint_t)((uintptr_t)tp - (uintptr_t)fp->ctf_buf); Index: cddl/contrib/opensolaris/common/ctf/ctf_types.c =================================================================== --- cddl/contrib/opensolaris/common/ctf/ctf_types.c +++ cddl/contrib/opensolaris/common/ctf/ctf_types.c @@ -295,6 +295,32 @@ case CTF_K_RESTRICT: ctf_decl_sprintf(&cd, "restrict"); break; + case CTF_K_CONSTVAL: { + ssize_t increment; + int32_t i32; + int64_t i64; + void *dptr; + int vlen; + + (void) ctf_get_ctt_size(rfp, tp, NULL, &increment); + dptr = (void *)((uintptr_t)tp + increment); + vlen = LCTF_INFO_VLEN(rfp, tp->ctt_info); + + i64 = 0; + switch (vlen) { + case 0: + break; + case 4: + memcpy(&i32, dptr, sizeof(i32)); + i64 = i32; + break; + case 8: + memcpy(&i64, dptr, sizeof(i64)); + break; + } + ctf_decl_sprintf(&cd, "constval<%jd>", (intmax_t)i64); + break; + } } k = cdp->cd_kind; @@ -484,6 +510,7 @@ case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: + case CTF_K_CONSTVAL: return (tp->ctt_type); default: return (ctf_set_errno(ofp, ECTF_NOTREF)); Index: cddl/contrib/opensolaris/lib/libctf/common/ctf.5 =================================================================== --- cddl/contrib/opensolaris/lib/libctf/common/ctf.5 +++ cddl/contrib/opensolaris/lib/libctf/common/ctf.5 @@ -11,7 +11,7 @@ .\" .\" Copyright (c) 2014 Joyent, Inc. .\" -.Dd Sep 26, 2014 +.Dd Feb 17, 2019 .Dt CTF 5 .Os .Sh NAME @@ -488,6 +488,7 @@ #define CTF_K_VOLATILE 11 #define CTF_K_CONST 12 #define CTF_K_RESTRICT 13 +#define CTF_K_CONSTVAL 14 .Ed .Lp Programs directly reference many types; however, other types are referenced @@ -1139,6 +1140,28 @@ These types have no variable list entries and use the member .Em ctt_type to refer to the base type that they modify. +.Ss Encoding of Typed Constant Values +Sometimes, compilers will optimize out constant arguments to subroutines. +It is still useful to track that a logical argument is present for dtrace. +This kind of constant value as a function parameter type is represented much like +the typedef, volatile, const, or restrict types above. +The +.Nm +type is +.Sy CTF_K_CONSTVAL +and like the other referential types, +.Em ctt_type +refers to the type of the value. +.Lp +This type has an associated signed, 64-bit numerical value. +It is stored in a variable-length encoding. +The length of the encoded integer in bytes is specified in +.Sy vlen . +Valid encodings are zero, to represent the numerical value zero; +four, to represent a signed 32-bit value in native endian; +and eight, to represent a signed 64-bit value. +.Lp +Independent values each get their own type. .Ss Encoding of Unknown Types Types with the kind .Sy CTF_K_UNKNOWN Index: cddl/contrib/opensolaris/lib/libdtrace/common/dt_dis.c =================================================================== --- cddl/contrib/opensolaris/lib/libdtrace/common/dt_dis.c +++ cddl/contrib/opensolaris/lib/libdtrace/common/dt_dis.c @@ -309,6 +309,9 @@ case CTF_K_RESTRICT: (void) strcpy(ckind, "restrict"); break; + case CTF_K_CONSTVAL: + (void) strcpy(ckind, "constval"); + break; default: (void) snprintf(ckind, sizeof (ckind), "0x%x", t->dtdt_ckind); } Index: sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c +++ sys/cddl/contrib/opensolaris/uts/common/dtrace/dtrace.c @@ -7371,6 +7371,41 @@ mstate.dtms_arg[3] = arg3; mstate.dtms_arg[4] = arg4; + /* + * Munge captured registers into logical argument order by reinjecting + * optimized-out constant values. + * + * At probe registration time, fbt computes a bitmask of constval + * arguments. Most of the time, this will be zero, and we can hint + * that to the compiler. + */ + if (__predict_false(probe->dtpr_constvalmask != 0)) { + dtrace_provider_t *pv; + ssize_t i, skip; + + pv = probe->dtpr_provider; + + skip = __builtin_popcount(probe->dtpr_constvalmask); + for (i = nitems(mstate.dtms_arg) - 1; i >= 0; i--) { + + if (skip == 0) + break; + if ((probe->dtpr_constvalmask & (1 << i)) == 0) { + mstate.dtms_arg[i] = mstate.dtms_arg[i - skip]; + continue; + } + + skip--; + if (pv->dtpv_pops.dtps_getargval == NULL) { + mstate.dtms_arg[i] = UINT64_MAX; + continue; + } + mstate.dtms_arg[i] = pv->dtpv_pops.dtps_getargval( + pv->dtpv_arg, id, probe->dtpr_arg, i, + probe->dtpr_aframes); + } + } + flags = (volatile uint16_t *)&cpu_core[cpuid].cpuc_dtrace_flags; for (ecb = probe->dtpr_ecb; ecb != NULL; ecb = ecb->dte_next) { @@ -9192,6 +9227,22 @@ return (id); } +void +dtrace_probe_set_constvalmask(dtrace_provider_id_t id, uint8_t mask) +{ + dtrace_probe_t *probe; + + mutex_enter(&dtrace_lock); + if (id == 0 || id > dtrace_nprobes) + goto out; + + probe = (dtrace_probes[id - 1]); + probe->dtpr_constvalmask = mask; + +out: + mutex_exit(&dtrace_lock); +} + static dtrace_probe_t * dtrace_probe_lookup_id(dtrace_id_t id) { Index: sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h +++ sys/cddl/contrib/opensolaris/uts/common/sys/dtrace.h @@ -2154,6 +2154,7 @@ char *, char *); extern dtrace_id_t dtrace_probe_create(dtrace_provider_id_t, const char *, const char *, const char *, int, void *); +extern void dtrace_probe_set_constvalmask(dtrace_provider_id_t, uint8_t); extern void *dtrace_probe_arg(dtrace_provider_id_t, dtrace_id_t); extern void dtrace_probe(dtrace_id_t, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4); Index: sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h +++ sys/cddl/contrib/opensolaris/uts/common/sys/dtrace_impl.h @@ -115,7 +115,8 @@ dtrace_ecb_t *dtpr_ecb_last; /* last ECB in list */ void *dtpr_arg; /* provider argument */ dtrace_cacheid_t dtpr_predcache; /* predicate cache ID */ - int dtpr_aframes; /* artificial frames */ + unsigned dtpr_constvalmask : 8; /* mask of constant args */ + int dtpr_aframes : 24; /* artificial frames */ dtrace_provider_t *dtpr_provider; /* pointer to provider */ char *dtpr_mod; /* probe's module name */ char *dtpr_func; /* probe's function name */ Index: sys/cddl/dev/fbt/aarch64/fbt_isa.c =================================================================== --- sys/cddl/dev/fbt/aarch64/fbt_isa.c +++ sys/cddl/dev/fbt/aarch64/fbt_isa.c @@ -120,6 +120,8 @@ fbt->fbtp_rval = DTRACE_INVOP_PUSHM; fbt->fbtp_symindx = symindx; + fbt_update_constvalmask(fbt); + fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; Index: sys/cddl/dev/fbt/arm/fbt_isa.c =================================================================== --- sys/cddl/dev/fbt/arm/fbt_isa.c +++ sys/cddl/dev/fbt/arm/fbt_isa.c @@ -127,6 +127,8 @@ fbt->fbtp_rval = DTRACE_INVOP_PUSHM; fbt->fbtp_symindx = symindx; + fbt_update_constvalmask(fbt); + fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; Index: sys/cddl/dev/fbt/fbt.h =================================================================== --- sys/cddl/dev/fbt/fbt.h +++ sys/cddl/dev/fbt/fbt.h @@ -65,6 +65,7 @@ void fbt_patch_tracepoint(fbt_probe_t *, fbt_patchval_t); int fbt_provide_module_function(struct linker_file *, int, struct linker_symval *, void *); +void fbt_update_constvalmask(fbt_probe_t *); int fbt_excluded(const char *name); extern dtrace_provider_id_t fbt_id; Index: sys/cddl/dev/fbt/fbt.c =================================================================== --- sys/cddl/dev/fbt/fbt.c +++ sys/cddl/dev/fbt/fbt.c @@ -60,6 +60,7 @@ #include #include +#include #include "fbt.h" @@ -72,6 +73,7 @@ static d_open_t fbt_open; static int fbt_unload(void); static void fbt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *); +static uint64_t fbt_getargval(void *, dtrace_id_t, void *, int, int); static void fbt_provide_module(void *, modctl_t *); static void fbt_destroy(void *, dtrace_id_t, void *); static void fbt_enable(void *, dtrace_id_t, void *); @@ -102,7 +104,7 @@ .dtps_suspend = fbt_suspend, .dtps_resume = fbt_resume, .dtps_getargdesc = fbt_getargdesc, - .dtps_getargval = NULL, + .dtps_getargval = fbt_getargval, .dtps_usermode = NULL, .dtps_destroy = fbt_destroy }; @@ -534,6 +536,10 @@ child |= CTF_TYPE_ISCHILD(tp->ctt_type); vbytes = 0; break; + case CTF_K_CONSTVAL: + child |= CTF_TYPE_ISCHILD(tp->ctt_type); + vbytes = vlen; + break; default: printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind); return (EIO); @@ -611,6 +617,9 @@ case CTF_K_RESTRICT: vbytes = 0; break; + case CTF_K_CONSTVAL: + vbytes = vlen; + break; default: printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind); return (EIO); @@ -777,6 +786,107 @@ return (tp); } +static const ctf_type_t * +fbt_get_type_of_arg(fbt_probe_t *fbt, int argno, int *nconstvals) +{ + const ctf_type_t *tp; + const ushort_t *dp; + linker_ctf_t lc; + modctl_t *ctl; + uint32_t *ctfoff, offset; + ushort_t info, kind, n; + int symindx, i; + + if (nconstvals != NULL) + *nconstvals = 0; + + tp = NULL; + ctl = fbt->fbtp_ctl; + symindx = fbt->fbtp_symindx; + + /* Return probe? or garbage input? */ + if (fbt->fbtp_roffset != 0 || argno < 0) + goto out; + + /* Get a pointer to the CTF data and it's length. */ + if (linker_ctf_get(ctl, &lc) != 0) + /* No CTF data? Something wrong? *shrug* */ + goto out; + + /* Check if this module hasn't been initialised yet. */ + if (*lc.ctfoffp == NULL) { + if (fbt_ctfoff_init(ctl, &lc) != 0) + goto out; + if (fbt_typoff_init(&lc) != 0) + goto out; + } + + ctfoff = *lc.ctfoffp; + if (ctfoff == NULL || *lc.typoffp == NULL) + goto out; + if (symindx >= lc.nsym) + goto out; + if ((offset = ctfoff[symindx]) == 0xffffffff) + goto out; + + dp = (const ushort_t *)(lc.ctftab + offset + sizeof(ctf_header_t)); + info = *dp++; + kind = CTF_INFO_KIND(info); + n = CTF_INFO_VLEN(info); + + if (kind != CTF_K_FUNCTION) { +#if 0 /* spammy */ + printf("%s(%d): %s function!\n", __func__, __LINE__, + (kind == CTF_K_UNKNOWN) ? "Unknown" : "Expected a"); +#endif + goto out; + } + + /* skip over return type */ + dp++; + + /* + * only walk through defined arguments. a given index beyond defined + * arguments may be valid for a varargs function, but we don't have + * type information there. + */ + tp = NULL; + i = 0; + if (nconstvals == NULL) { + i = argno; + dp += argno; + } + for (; i <= argno && i < n; i++, dp++) { + if (*dp == 0) + break; + tp = ctf_lookup_by_id(&lc, *dp); + if (CTF_INFO_KIND(tp->ctt_info) == CTF_K_CONSTVAL && + nconstvals != NULL) + (*nconstvals)++; + } + +out: + return (tp); +} + +void +fbt_update_constvalmask(fbt_probe_t *fbt) +{ + const ctf_type_t *tp; + uint8_t mask; + size_t i; + + for (i = 0, mask = 0; i < 8; i++) { + tp = fbt_get_type_of_arg(fbt, i, NULL); + if (tp == NULL) + break; + if (CTF_INFO_KIND(tp->ctt_info) != CTF_K_CONSTVAL) + continue; + mask |= (1 << i); + } + dtrace_probe_set_constvalmask(fbt->fbtp_id, mask); +} + static void fbt_array_info(linker_ctf_t *lc, ctf_id_t type, ctf_arinfo_t *arp) { @@ -860,6 +970,7 @@ case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: + case CTF_K_CONSTVAL: ctf_decl_push(cd, lc, tp->ctt_type); prec = cd->cd_qualp; is_qual++; @@ -989,6 +1100,13 @@ case CTF_K_VOLATILE: ctf_decl_sprintf(&cd, "volatile"); break; + case CTF_K_CONSTVAL: + /* + * This isn't amazing, but avoids having to + * hack up the D language grammar for this + * feature. + */ + /* FALLTHROUGH */ case CTF_K_CONST: ctf_decl_sprintf(&cd, "const"); break; @@ -1096,6 +1214,66 @@ return; } +static uint64_t +fbt_getargval(void *arg __unused, dtrace_id_t id __unused, void *parg, + int argno, int aframes) +{ + const ctf_type_t *tp; + fbt_probe_t *fbt; + linker_ctf_t lc; + modctl_t *ctl; + int nconstvals; + + fbt = parg; + ctl = fbt->fbtp_ctl; + tp = fbt_get_type_of_arg(fbt, argno, &nconstvals); + + /* No CTF data? Something wrong? *shrug* */ + if (linker_ctf_get(ctl, &lc) != 0 || *lc.ctfoffp == NULL) + goto ordinary_regarg; + + /* handle constval requested args specially */ + if (tp != NULL && CTF_INFO_KIND(tp->ctt_info) == CTF_K_CONSTVAL) { + const ctf_header_t *hp; + ssize_t increment; + const void *dp; + int64_t i64; + int32_t i32; + + hp = (const ctf_header_t *)lc.ctftab; + (void) fbt_get_ctt_size(hp->cth_version, tp, NULL, &increment); + dp = (void *)((uintptr_t)tp + increment); + + switch (CTF_INFO_VLEN(tp->ctt_info)) { + case 0: + i64 = 0; + break; + case 4: + memcpy(&i32, dp, sizeof(i32)); + i64 = i32; + break; + case 8: + memcpy(&i64, dp, sizeof(i64)); + break; + default: + printf("%s(%d): bad VLEN in CTF data!\n", __func__, + __LINE__); + i64 = -1; + } + return ((uint64_t)i64); + } + + /* + * the compiler optimized out the constant values, so look up the + * appropriate register. + */ + argno -= nconstvals; + /* FALLTHROUGH */ + +ordinary_regarg: + return (dtrace_getarg(argno, aframes)); +} + static int fbt_linker_file_cb(linker_file_t lf, void *arg) { Index: sys/cddl/dev/fbt/mips/fbt_isa.c =================================================================== --- sys/cddl/dev/fbt/mips/fbt_isa.c +++ sys/cddl/dev/fbt/mips/fbt_isa.c @@ -117,6 +117,8 @@ fbt->fbtp_rval = DTRACE_INVOP_SD; fbt->fbtp_symindx = symindx; + fbt_update_constvalmask(fbt); + fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; Index: sys/cddl/dev/fbt/powerpc/fbt_isa.c =================================================================== --- sys/cddl/dev/fbt/powerpc/fbt_isa.c +++ sys/cddl/dev/fbt/powerpc/fbt_isa.c @@ -152,6 +152,8 @@ fbt->fbtp_rval = DTRACE_INVOP_MFLR_R0; fbt->fbtp_symindx = symindx; + fbt_update_constvalmask(fbt); + fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; Index: sys/cddl/dev/fbt/riscv/fbt_isa.c =================================================================== --- sys/cddl/dev/fbt/riscv/fbt_isa.c +++ sys/cddl/dev/fbt/riscv/fbt_isa.c @@ -187,6 +187,8 @@ fbt->fbtp_rval = rval; fbt->fbtp_symindx = symindx; + fbt_update_constvalmask(fbt); + fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; Index: sys/cddl/dev/fbt/x86/fbt_isa.c =================================================================== --- sys/cddl/dev/fbt/x86/fbt_isa.c +++ sys/cddl/dev/fbt/x86/fbt_isa.c @@ -228,6 +228,8 @@ fbt->fbtp_patchval = FBT_PATCHVAL; fbt->fbtp_symindx = symindx; + fbt_update_constvalmask(fbt); + for (hash = fbt_probetab[FBT_ADDR2NDX(instr)]; hash != NULL; hash = hash->fbtp_hashnext) { if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {