diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_sugar.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_sugar.c --- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_sugar.c +++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_sugar.c @@ -38,12 +38,23 @@ #include #include #include +#include +#include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -54,14 +65,51 @@ #include #include +#include + +/* kinst-related */ +struct elf_info { + Elf *elf; + struct section { + Elf_Scn *scn; + GElf_Shdr sh; + const char *name; + } *sl; + size_t shnum; + int fd; +}; + +struct entry { + const char *callerfunc; + int noff; + struct off { + const char *func; + uint64_t val; + } *off; + TAILQ_ENTRY(entry) next; +}; + typedef struct dt_sugar_parse { dtrace_hdl_t *dtsp_dtp; /* dtrace handle */ dt_node_t *dtsp_pdescs; /* probe descriptions */ int dtsp_num_conditions; /* number of condition variables */ int dtsp_num_ifs; /* number of "if" statements */ + int dtsp_kinst; /* specify if the provider is kinst */ dt_node_t *dtsp_clause_list; /* list of clauses */ + struct elf_info dtsp_elf_kern; /* ELF info of the kernel executable */ + struct elf_info dtsp_elf_dbg; /* ELF info of the kernel debug file */ + dtrace_probedesc_t *dtsp_desc; /* kinst pdesc to duplicate contents */ + Dwarf_Off dtsp_dieoff; /* DIE offset of kinst inline definition */ + int dtsp_inline; /* kinst probe function is inline */ + int dtsp_entry_or_return; /* kinst probe is entry or return */ + TAILQ_HEAD(, entry) dtsp_head; /* kinst inline copy entry TAILQ */ } dt_sugar_parse_t; +enum { + F_SUBPROGRAM, + F_INLINE_COPY, +}; + static void dt_sugar_visit_stmts(dt_sugar_parse_t *, dt_node_t *, int); /* @@ -186,8 +234,574 @@ } /* - * Visit the specified node and all of its descendants. Currently this is only - * used to count the number of "if" statements (dtsp_num_ifs). + * kinst-related + */ +static void +dt_sugar_elf_init(dtrace_hdl_t *dtp, struct elf_info *ei, const char *file) +{ + Elf_Scn *scn; + GElf_Shdr sh; + struct section *s; + const char *name; + size_t shstrndx, ndx; + + if (elf_version(EV_CURRENT) == EV_NONE) + errx(1, "dt_sugar: elf_version(): %s", elf_errmsg(-1)); + if ((ei->fd = open(file, O_RDONLY)) < 0) + err(1, "dt_sugar: open(%s)", file); + if ((ei->elf = elf_begin(ei->fd, ELF_C_READ, NULL)) == NULL) + errx(1, "dt_sugar: elf_begin(): %s", elf_errmsg(-1)); + if (elf_kind(ei->elf) == ELF_K_NONE) + errx(1, "dt_sugar: not an ELF file: %s", file); + + /* Load ELF sections */ + if (!elf_getshnum(ei->elf, &ei->shnum)) + errx(1, "dt_sugar: elf_getshnum(): %s", elf_errmsg(-1)); + ei->sl = dt_alloc(dtp, ei->shnum * sizeof(struct section)); + if (ei->sl == NULL) + err(1, "dt_sugar: dt_alloc()"); + if (!elf_getshstrndx(ei->elf, &shstrndx)) + errx(1, "dt_sugar: elf_getshstrndx(): %s", elf_errmsg(-1)); + if ((scn = elf_getscn(ei->elf, 0)) == NULL) + errx(1, "dt_sugar: elf_getscn(): %s", elf_errmsg(-1)); + (void) elf_errno(); + + do { + if (gelf_getshdr(scn, &sh) == NULL) { + warnx("dt_sugar: gelf_getshdr(): %s", elf_errmsg(-1)); + (void) elf_errno(); + continue; + } + if ((name = elf_strptr(ei->elf, shstrndx, sh.sh_name)) == NULL) + (void) elf_errno(); + if ((ndx = elf_ndxscn(scn)) == SHN_UNDEF && elf_errno() != 0) { + warnx("dt_sugar: elf_ndxscn(): %s", elf_errmsg(-1)); + continue; + } + if (ndx >= ei->shnum) + continue; + s = &ei->sl[ndx]; + s->scn = scn; + s->sh = sh; + s->name = name; + } while ((scn = elf_nextscn(ei->elf, scn)) != NULL); + if (elf_errno() != 0) + warnx("dt_sugar: elf_nextscn(): %s", elf_errmsg(-1)); +} + +static void +dt_sugar_elf_deinit(dtrace_hdl_t *dtp, struct elf_info *ei) +{ + dt_free(dtp, ei->sl); + close(ei->fd); + elf_end(ei->elf); +} + +static int +dt_sugar_dis_get_byte(void *p) +{ + int ret; + uint8_t **instr = p; + + ret = **instr; + (*instr)++; + + return (ret); +} + +/* + * Find the caller function and offset of an inline copy. Since we know the + * inline copy's boundaries (`addr_lo` and `addr_hi` arguments), the caller + * function is going to be the ELF symbol that the inline copy's boundaries are + * inside of. + */ +static void +dt_sugar_kinst_find_caller_func(dt_sugar_parse_t *dp, struct off *off, + uint64_t addr_lo, uint64_t addr_hi) +{ + Elf_Data *d; + GElf_Sym sym; + struct section *s; + dis86_t d86; + uint8_t *buf; + uint64_t addr, lo, hi; + uint32_t stab; + int len, i, j; + + /* Find the caller function's boundaries and name. */ + off->func = NULL; + for (i = 1; i < dp->dtsp_elf_kern.shnum; i++) { + s = &dp->dtsp_elf_kern.sl[i]; + if (s->sh.sh_type != SHT_SYMTAB && s->sh.sh_type != SHT_DYNSYM) + continue; + if (s->sh.sh_link >= dp->dtsp_elf_kern.shnum) + continue; + stab = s->sh.sh_link; + (void) elf_errno(); + if ((d = elf_getdata(s->scn, NULL)) == NULL) { + if (elf_errno() != 0) + warnx("dt_sugar: elf_getdata(): %s", + elf_errmsg(-1)); + continue; + } + if (d->d_size <= 0) + continue; + if (s->sh.sh_entsize == 0) + continue; + else if (s->sh.sh_size / s->sh.sh_entsize > INT_MAX) + continue; + len = (int)(s->sh.sh_size / s->sh.sh_entsize); + for (j = 0; j < len; j++) { + if (gelf_getsym(d, j, &sym) != &sym) { + warnx("dt_sugar: gelf_getsym(): %s", + elf_errmsg(-1)); + continue; + } + lo = sym.st_value; + hi = sym.st_value + sym.st_size; + if (addr_lo < lo || addr_hi > hi) + continue; + if ((off->func = elf_strptr(dp->dtsp_elf_kern.elf, stab, + sym.st_name)) != NULL) + break; + } + } + + if (strcmp(dp->dtsp_desc->dtpd_name, "entry") == 0) { + off->val = addr_lo - lo; + return; + } else if (strcmp(dp->dtsp_desc->dtpd_name, "return") == 0) + ; /* nothing */ + + /* Find inline copy's return offset. */ + for (i = 1; i < dp->dtsp_elf_kern.shnum; i++) { + s = &dp->dtsp_elf_kern.sl[i]; + if (strcmp(s->name, ".text") != 0 || + s->sh.sh_type != SHT_PROGBITS) + continue; + (void) elf_errno(); + if ((d = elf_getdata(s->scn, NULL)) == NULL) { + if (elf_errno() != 0) + warnx("dt_sugar: elf_getdata(): %s", + elf_errmsg(-1)); + continue; + } + if (d->d_size <= 0 || d->d_buf == NULL) + continue; + + buf = d->d_buf; + addr = s->sh.sh_addr + d->d_off; + + /* + * Get to the inline copy's start manually to avoid potential + * dtrace_disx86() failures. + */ + while (addr != addr_lo) { + addr++; + buf++; + } + + d86.d86_data = &buf; + d86.d86_get_byte = dt_sugar_dis_get_byte; + d86.d86_check_func = NULL; + + /* Get to the inline copy's end. */ + while (addr != addr_hi) { + /* + * XXX We might have to add #ifdefs when we port kinst + * to other architectures. + */ + if (dtrace_disx86(&d86, SIZE64) != 0) { + warnx("dt_sugar: dtrace_disx86() failed"); + return; + } + addr += d86.d86_len; + } + /* + * DWARF considers addr_hi to be the instruction + * *after* the inline copy's end, so we want to go back + * one instruction. + */ + addr -= d86.d86_len; + off->val = addr - lo; + break; + } +} + +/* + * Parse DWARF info recursively and create a TAILQ of entries that correspond + * to inline copies of the probe function. + */ +static void +dt_sugar_kinst_parse_die(dt_sugar_parse_t *dp, Dwarf_Debug dbg, Dwarf_Die die, + int level, int flag) +{ + static Dwarf_Die die_root; + Dwarf_Die die_next; + Dwarf_Ranges *ranges, *rp; + Dwarf_Attribute attp; + Dwarf_Addr base0, lowpc, highpc; + Dwarf_Off dieoff, cuoff, culen, v_off; + Dwarf_Unsigned nbytes, v_udata; + Dwarf_Signed nranges; + Dwarf_Half attr, tag; + Dwarf_Bool v_flag; + Dwarf_Error error; + struct entry *e; + struct off *off; + char *v_str; + int res, noff, i, found = 0; + + if (level == 0) + die_root = die; + + if (dwarf_dieoffset(die, &dieoff, &error) != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + if (dwarf_die_CU_offset_range(die, &cuoff, &culen, &error) != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + cuoff = 0; + } + if (dwarf_tag(die, &tag, &error) != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) + goto cont; + if (flag == F_SUBPROGRAM && tag == DW_TAG_subprogram) { + if (dwarf_hasattr(die, DW_AT_inline, &v_flag, &error) != + DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + if (!v_flag) + goto cont; + res = dwarf_diename(die, &v_str, &error); + if (res != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + if (strcmp(v_str, dp->dtsp_desc->dtpd_func) != 0) + goto cont; + /* + * The function name we're searching for has an inline + * definition. + */ + found = 1; + goto cont; + } else if (flag == F_INLINE_COPY && tag == DW_TAG_inlined_subroutine) { + res = dwarf_attr(die, DW_AT_abstract_origin, &attp, &error); + if (res != DW_DLV_OK) { + if (res == DW_DLV_ERROR) + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + if (dwarf_formref(attp, &v_off, &error) != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + v_off += cuoff; + /* Doesn't point to the definition's DIE offset. */ + if (v_off != dp->dtsp_dieoff) + goto cont; + + if (dwarf_hasattr(die, DW_AT_ranges, &v_flag, &error) != + DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + if (v_flag) { + /* DIE has ranges */ + res = dwarf_attr(die, DW_AT_ranges, &attp, &error); + if (res != DW_DLV_OK) { + if (res == DW_DLV_ERROR) + warnx("dt_sugar: %s", + dwarf_errmsg(error)); + goto cont; + } + if (dwarf_global_formref(attp, &v_off, &error) != + DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + if (dwarf_get_ranges(dbg, v_off, &ranges, &nranges, + &nbytes, &error) != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + + res = dwarf_lowpc(die_root, &lowpc, &error); + if (res != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + base0 = lowpc; + + if (strcmp(dp->dtsp_desc->dtpd_name, "entry") == 0) { + /* + * Trace the first instruction of the first + * range since this is the beginning of the + * inline copy. + */ + noff = 1; + } else if (strcmp(dp->dtsp_desc->dtpd_name, "return") == 0) { + /* + * Trace the last instruction of every range in + * case the inline copy is split into multiple + * ranges (e.g if it has early `return`s). + */ + noff = nranges - 1; + } + off = dt_alloc(dp->dtsp_dtp, noff * sizeof(struct off)); + if (off == NULL) + err(1, "dt_sugar: dt_alloc()"); + for (i = 0; i < noff; i++) { + rp = &ranges[i]; + if (rp->dwr_type == DW_RANGES_ADDRESS_SELECTION) + base0 = rp->dwr_addr2; + dt_sugar_kinst_find_caller_func(dp, &off[i], + rp->dwr_addr1 + base0, + rp->dwr_addr2 + base0); + } + dwarf_ranges_dealloc(dbg, ranges, nranges); + } else { + /* DIE has high/low PC boundaries */ + res = dwarf_lowpc(die, &lowpc, &error); + if (res != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + res = dwarf_highpc(die, &highpc, &error); + if (res != DW_DLV_OK) { + warnx("dt_sugar: %s", dwarf_errmsg(error)); + goto cont; + } + noff = 1; + off = dt_alloc(dp->dtsp_dtp, noff * sizeof(struct off)); + if (off == NULL) + err(1, "dt_sugar: dt_alloc()"); + dt_sugar_kinst_find_caller_func(dp, off, lowpc, + lowpc + highpc); + } + } else + goto cont; + + e = dt_alloc(dp->dtsp_dtp, sizeof(struct entry)); + if (e == NULL) + err(1, "dt_sugar: dt_alloc()"); + e->noff = noff; + e->off = off; + TAILQ_INSERT_TAIL(&dp->dtsp_head, e, next); +cont: + /* + * Inline copies might appear before the declaration, so we need to + * re-parse the CU. + * + * The rationale for choosing to re-parse the CU instead of using a + * hash table of DIEs is that, because we re-parse only when an inline + * definition of the function we want is found, statistically, we won't + * have to re-parse many times at all considering that only a handful + * of CUs will define the same function, whereas if we have used a hash + * table, we would first need to parse the whole CU at once and store + * all DW_TAG_inlined_subroutine DIEs (so that we can match them + * afterwards). In this case, we always have to "parse" twice -- first + * the CU, then the DIE table -- and also, the program would use much + * more memory since we would have allocated DIEs, which most of them + * would never be used. + */ + if (found) { + die = die_root; + level = 0; + /* + * We'll be checking against the DIE offset of the definition + * to determine if the inline copy's DW_AT_abstract_origin + * points to it. + */ + dp->dtsp_dieoff = dieoff; + dp->dtsp_inline = 1; + flag = F_INLINE_COPY; + } + + res = dwarf_child(die, &die_next, &error); + if (res == DW_DLV_ERROR) + warnx("dt_sugar: %s", dwarf_errmsg(error)); + else if (res == DW_DLV_OK) + dt_sugar_kinst_parse_die(dp, dbg, die_next, level + 1, flag); + + res = dwarf_siblingof(dbg, die, &die_next, &error); + if (res == DW_DLV_ERROR) + warnx("dt_sugar: %s", dwarf_errmsg(error)); + else if (res == DW_DLV_OK) + dt_sugar_kinst_parse_die(dp, dbg, die_next, level, flag); + + /* + * Deallocating on level 0 will attempt to double-free, since die_root + * points to the first DIE. We'll deallocate the root DIE in main(). + */ + if (level > 0) + dwarf_dealloc(dbg, die, DW_DLA_DIE); +} + +/* + * Append new clauses for each inline copy to the parse tree. + * + * For example, if foo() is an inline function, and bar() is an inline copy of + * it called from function baz() at offsets 10 and 20 respectively, we'll + * transform the parse tree from: + * + * kinst::foo: /pred/{ acts } + * + * To: + * + * kinst::baz:10 /pred/{ acts } + * kinst::baz:20 /pred/{ acts } + */ +static void +dt_sugar_kinst_create_probes(dt_sugar_parse_t *dp, dt_node_t *dnp) +{ + dt_node_t *pdesc, *dcopy, *dcopyhead, *p, *q; + struct entry *e; + char buf[DTRACE_FULLNAMELEN]; + int i, j = 0; + + /* + * Perform a deep copy of the predicates and actions so that we can + * clone them when we create new clauses for inline copies. If we don't + * have a deep copy we'll end up in an infinite loop when we start + * appending clauses to the clause list. + */ + p = dp->dtsp_clause_list; + dcopy = NULL; + if (p != NULL) { + dcopy = dt_node_xalloc(dp->dtsp_dtp, p->dn_kind); + if (dcopy == NULL) + err(1, "dt_sugar: dt_node_xalloc()"); + dcopyhead = dcopy; + for (q = p; q != NULL; q = q->dn_list) { + dcopy->dn_pred = NULL; + dcopy->dn_acts = NULL; + + if (q->dn_pred != NULL) + dcopy->dn_pred = q->dn_pred; + if (q->dn_acts != NULL) + dcopy->dn_acts = q->dn_acts; + if (q->dn_list != NULL) { + /* XXX are we leaking memory? */ + dcopy->dn_list = dt_node_xalloc(dp->dtsp_dtp, + q->dn_kind); + if (dcopy->dn_list == NULL) + err(1, "dt_sugar: dt_node_xalloc()"); + dcopy = dcopy->dn_list; + } + } + dcopy = NULL; + dcopy = dcopyhead; + } + + /* Clean up as well */ + while (!TAILQ_EMPTY(&dp->dtsp_head)) { + e = TAILQ_FIRST(&dp->dtsp_head); + TAILQ_REMOVE(&dp->dtsp_head, e, next); + for (i = 0; i < e->noff; i++) { + if (j++ == 0) { + /* + * Since we're trying to trace inline copies of + * a given function by requesting a probe of + * the form + * `kinst:::`, + * the requested probe, by definition cannot be + * traced, and as a result DTrace will exit + * with an error because it cannot create a + * probe for this function. In order to get + * around this, we're replacing the requested + * probe's and fields with + * the very first inline copy's information. + */ + snprintf(buf, sizeof(buf), "%lu", e->off[i].val); + strcpy(dp->dtsp_desc->dtpd_func, e->off[i].func); + strcpy(dp->dtsp_desc->dtpd_name, buf); + } else { + /* + * Create new clauses for each inline copy with + * the requested probe's predicates and + * actions. + */ + snprintf(buf, sizeof(buf), "%s:%s:%s:%lu", + dp->dtsp_desc->dtpd_provider, + dp->dtsp_desc->dtpd_mod, + e->off[i].func, e->off[i].val); + pdesc = dt_node_pdesc_by_name(strdup(buf)); + + /* Clone all predicates and actions. */ + for (p = dcopy; p != NULL; p = p->dn_list) { + dt_sugar_append_clause(dp, + dt_node_clause(pdesc, + p->dn_pred, p->dn_acts)); + } + } + } + dt_free(dp->dtsp_dtp, e->off); + dt_free(dp->dtsp_dtp, e); + } +} + +/* + * Initialize libelf and libdwarf and parse kernel.debug's DWARF info. + */ +static void +dt_sugar_do_kinst_inline(dt_sugar_parse_t *dp, dt_node_t *dnp) +{ + Dwarf_Debug dbg; + Dwarf_Die die; + Dwarf_Error error; + char dbgfile[MAXPATHLEN]; + int res = DW_DLV_OK; + + /* We only make entry and return probes for inline functions. */ + if (strcmp(dp->dtsp_desc->dtpd_name, "entry") != 0 && + strcmp(dp->dtsp_desc->dtpd_name, "return") != 0) + return; + + (void) snprintf(dbgfile, sizeof(dbgfile), "/usr/lib/debug/%s.debug", + dp->dtsp_dtp->bootfile); + dt_sugar_elf_init(dp->dtsp_dtp, &dp->dtsp_elf_kern, + dp->dtsp_dtp->bootfile); + dt_sugar_elf_init(dp->dtsp_dtp, &dp->dtsp_elf_dbg, dbgfile); + + if (dwarf_elf_init(dp->dtsp_elf_dbg.elf, DW_DLC_READ, NULL, NULL, &dbg, + &error) != DW_DLV_OK) + errx(1, "dt_sugar: dwarf_elf_init(): %s", dwarf_errmsg(error)); + + dp->dtsp_entry_or_return = 1; + + TAILQ_INIT(&dp->dtsp_head); + /* + * Parse DWARF info for kernel.debug and create entries for the inline + * copies we'll create probes for. + */ + do { + while ((res = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, + NULL, &error)) == DW_DLV_OK) { + die = NULL; + while (dwarf_siblingof(dbg, die, &die, &error) == + DW_DLV_OK) { + dt_sugar_kinst_parse_die(dp, dbg, die, 0, + F_SUBPROGRAM); + } + dwarf_dealloc(dbg, die, DW_DLA_DIE); + } + if (res == DW_DLV_ERROR) + warnx("dt_sugar: %s", dwarf_errmsg(error)); + } while (dwarf_next_types_section(dbg, &error) == DW_DLV_OK); + + dt_sugar_elf_deinit(dp->dtsp_dtp, &dp->dtsp_elf_kern); + dt_sugar_elf_deinit(dp->dtsp_dtp, &dp->dtsp_elf_dbg); + dwarf_finish(dbg, &error); +} + +/* + * Visit the specified node and all of its descendants. */ static void dt_sugar_visit_all(dt_sugar_parse_t *dp, dt_node_t *dnp) @@ -201,7 +815,18 @@ case DT_NODE_SYM: case DT_NODE_TYPE: case DT_NODE_PROBE: + break; + case DT_NODE_PDESC: + if (strcmp(dnp->dn_desc->dtpd_provider, "kinst") != 0) + break; + dp->dtsp_kinst = 1; + dp->dtsp_inline = 0; + dp->dtsp_entry_or_return = 0; + dp->dtsp_desc = dnp->dn_desc; + dt_sugar_do_kinst_inline(dp, dnp); + break; + case DT_NODE_IDENT: break; @@ -507,10 +1132,25 @@ dt_sugar_new_clearerror_clause(&dp)); } + if (dp.dtsp_kinst) { + if (dp.dtsp_inline) + dt_sugar_kinst_create_probes(&dp, clause); + else if (!dp.dtsp_inline && dp.dtsp_entry_or_return) { + /* + * Delegate non-inline function probes to FBT so that + * we don't duplicate FBT code in kinst. + */ + strlcpy(dp.dtsp_desc->dtpd_provider, "fbt", + sizeof(dp.dtsp_desc->dtpd_provider)); + } + /* Regular kinst probes are not affected. */ + } + if (dp.dtsp_clause_list != NULL && dp.dtsp_clause_list->dn_list != NULL && !dtp->dt_has_sugar) { dtp->dt_has_sugar = B_TRUE; dt_sugar_prepend_clause(&dp, dt_sugar_makeerrorclause()); } + return (dp.dtsp_clause_list); } diff --git a/cddl/lib/libdtrace/Makefile b/cddl/lib/libdtrace/Makefile --- a/cddl/lib/libdtrace/Makefile +++ b/cddl/lib/libdtrace/Makefile @@ -128,7 +128,7 @@ YFLAGS+=-d -LIBADD= ctf elf proc pthread rtld_db +LIBADD= ctf dwarf elf proc pthread rtld_db CLEANFILES= dt_errtags.c dt_names.c diff --git a/share/man/man4/dtrace_kinst.4 b/share/man/man4/dtrace_kinst.4 --- a/share/man/man4/dtrace_kinst.4 +++ b/share/man/man4/dtrace_kinst.4 @@ -36,9 +36,22 @@ The DTrace .Nm kinst provider allows the user to trace any instruction in a given kernel function. - corresponds to the function to be traced, and is the -offset to the specific instruction, and can be obtained from the function's -disassembly using kgdb from the gdb package. +.Cm +corresponds to the function to be traced, and +.Cm +is the offset to the specific instruction, and can be obtained from the +function's disassembly using kgdb from the gdb package. +.Pp +.Nm kinst +can also trace inline functions by requesting a probe of the form +.Cm kinst::: . +If the probe is +.Cm entry +or +.Cm return +but the function is not an inline one, the probe will be delegated to FBT, +otherwise DTrace will find all call sites of the inline function and create +individual probes for each one of them. .Pp .Nm kinst creates probes on-demand, meaning it searches for and parses the function's @@ -72,13 +85,39 @@ 2 81500 vm_fault:4 0x1fab9bef0000 2 81500 vm_fault:4 0xe16cf749000 0 81500 vm_fault:4 0x13587c366000 - ... + ^C .Ed .Pp Trace all instructions in .Fn amd64_syscall : .Bd -literal -offset indent # dtrace -n 'kinst::amd64_syscall:' +dtrace: description 'kinst::amd64_syscall:' matched 458 probes +CPU ID FUNCTION:NAME + 2 80676 amd64_syscall:323 + 2 80677 amd64_syscall:326 + 2 80678 amd64_syscall:334 + 2 80679 amd64_syscall:339 + 2 80680 amd64_syscall:345 + 2 80681 amd64_syscall:353 + ^C +.Ed +.Pp +Trace the return point of +.Fn vm_page_mvqueue , +which is an inline function: +.Bd -literal -offset indent +# dtrace -n 'kinst::vm_page_mvqueue:return' +dtrace: description 'kinst::vm_page_mvqueue:return' matched 15 probes +CPU ID FUNCTION:NAME + 2 80627 vm_page_advise:421 + 2 80627 vm_page_advise:421 + 2 80627 vm_page_advise:421 + 2 80618 vm_page_deactivate:85 + 1 80618 vm_page_deactivate:85 + 2 80618 vm_page_deactivate:85 + 1 80618 vm_page_deactivate:85 + ^C .Ed .Sh SEE ALSO .Xr dtrace 1 diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -384,7 +384,7 @@ _DP_vmmapi= util _DP_opencsd= cxxrt _DP_ctf= spl z -_DP_dtrace= ctf elf proc pthread rtld_db +_DP_dtrace= ctf dwarf elf proc pthread rtld_db _DP_xo= util _DP_ztest= geom m nvpair umem zpool pthread avl zfs_core spl zutil zfs uutil icp # The libc dependencies are not strictly needed but are defined to make the