Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F103127261
D38825.id119037.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
23 KB
Referenced Files
None
Subscribers
None
D38825.id119037.diff
View Options
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 <sys/types.h>
#include <sys/wait.h>
#include <sys/sysmacros.h>
+#include <sys/param.h>
+#include <sys/queue.h>
#include <assert.h>
-#include <strings.h>
-#include <stdlib.h>
-#include <stdio.h>
#include <ctype.h>
+#include <dwarf.h>
+#include <err.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libdwarf.h>
+#include <libelf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
#include <dt_module.h>
#include <dt_program.h>
#include <dt_provider.h>
@@ -54,14 +65,50 @@
#include <dt_string.h>
#include <dt_impl.h>
+#include <dis_tables.h>
+
+/* 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 {
+ struct off {
+ const char *func;
+ uint64_t val;
+ int valid;
+ } *off;
+ int noff;
+ 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 */
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 +233,562 @@
}
/*
- * 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;
+ }
+ }
+
+ /* 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;
+
+ /*
+ * Compiling without `-mno-omit-leaf-frame-pointer` might
+ * result in, as the name suggests, leaf functions omitting
+ * `push %rbp`. kinst ignores any function that doesn't start
+ * with this instruction, so in order to avoid having dtrace(1)
+ * exit because one of the probes we're creating is a leaf
+ * function with its 'push %rbp' ommitted, we're catching this
+ * error before we get to kinst.
+ */
+ while (addr != lo) {
+ addr++;
+ buf++;
+ }
+ if (*buf != 0x55) {
+ warnx("dt_sugar: ignoring '%s': function does not "
+ "begin with 'push %%rbp'", off->func);
+ off->valid = 0;
+ return;
+ }
+ off->valid = 1;
+
+ /*
+ * Get to the inline copy's start manually to avoid potential
+ * dtrace_disx86() failures.
+ */
+ while (addr != addr_lo) {
+ addr++;
+ buf++;
+ }
+
+ if (strcmp(dp->dtsp_desc->dtpd_name, "entry") == 0) {
+ off->val = addr - lo;
+ break;
+ } else if (strcmp(dp->dtsp_desc->dtpd_name, "return") == 0)
+ ; /* nothing */
+
+ 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.
+ *
+ * If foo() is an inline function, and is called from functions bar() and baz()
+ * at offsets 10 and 20 respectively, we'll transform the parse tree from:
+ *
+ * kinst::foo:<entry/return>
+ * /<pred>/
+ * {
+ * <acts>
+ * }
+ *
+ * To:
+ *
+ * kinst::bar:10,
+ * kinst::baz:20
+ * /<pred>/
+ * {
+ * <acts>
+ * }
+ */
+static void
+dt_sugar_kinst_create_probes(dt_sugar_parse_t *dp)
+{
+ dt_node_t *pdesc, *dnp;
+ struct entry *e;
+ char buf[DTRACE_FULLNAMELEN];
+ int i, j = 0;
+
+ dnp = dp->dtsp_clause_list->dn_pdescs;
+
+ /* 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 (!e->off[i].valid)
+ continue;
+ if (j++ == 0) {
+ /*
+ * Since we're trying to trace inline copies of
+ * a given function by requesting a probe of
+ * the form
+ * `kinst::<inline_func_name>:<entry/return>`,
+ * 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 overriding the requested
+ * probe's <function> and <offset> fields with
+ * the very first inline copy's information.
+ */
+ snprintf(buf, sizeof(buf), "%lu", e->off[i].val);
+ strlcpy(dp->dtsp_desc->dtpd_func, e->off[i].func,
+ sizeof(dp->dtsp_desc->dtpd_func));
+ strlcpy(dp->dtsp_desc->dtpd_name, buf,
+ sizeof(dp->dtsp_desc->dtpd_name));
+ } else {
+ /*
+ * Append the probe description of each inline
+ * copy to main clause.
+ */
+ 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));
+ dnp = dt_node_link(dnp, pdesc);
+ }
+ }
+ 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)
+{
+ 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.
+ */
+ 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));
+
+ 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)
@@ -461,6 +1062,7 @@
dt_compile_sugar(dtrace_hdl_t *dtp, dt_node_t *clause)
{
dt_sugar_parse_t dp = { 0 };
+ dt_node_t *dnp;
int condid = 0;
dp.dtsp_dtp = dtp;
@@ -507,10 +1109,60 @@
dt_sugar_new_clearerror_clause(&dp));
}
+ /*
+ * This loop is a bit of a hack. What it does is iterate through all
+ * probe descriptions and handle kinst entry/return probes, but you
+ * will notice that in case we handle inline function probes,
+ * dt_sugar_kinst_create_probes() appends new elements to the list
+ * we're looping through, yet it works just fine!
+ *
+ * Consider the following initial `dn_pdescs` list:
+ *
+ * dn_pdescs = kinst::inlinefunc1:entry
+ * dn_pdescs->dn_list = kinst::inlinefunc2:return
+ * dn_pdescs->dn_list->dn_list = kinst::normalfunc1:0
+ * dn_pdescs->dn_list->dn_list->dn_list = kinst::normalfunc2:entry
+ *
+ * The final list will look like this (read the comments in
+ * dt_sugar_kinst_create_probes()):
+ *
+ * dn_pdescs = kinst::callerfunc1:<x>
+ * dn_pdescs->dn_list = kinst::callerfunc2:<y>
+ * dn_pdescs->dn_list->dn_list = kinst::normalfunc1:0
+ * dn_pdescs->dn_list->dn_list->dn_list = fbt::normalfunc2:entry
+ * ... = new probes are appended here
+ *
+ * Because it is guaranteed that any new probes appended to the list by
+ * dt_sugar_kinst_create_probes() will be regular kinst probes, the
+ * loop below *does* loop through them as well, but does nothing since
+ * regular kinst probes are skipped.
+ */
+ for (dnp = dp.dtsp_clause_list->dn_pdescs; dnp != NULL;
+ dnp = dnp->dn_list) {
+ if (strcmp(dnp->dn_desc->dtpd_provider, "kinst") != 0)
+ continue;
+ dp.dtsp_inline = 0;
+ dp.dtsp_entry_or_return = 0;
+ dp.dtsp_desc = dnp->dn_desc;
+ dt_sugar_do_kinst_inline(&dp);
+ if (dp.dtsp_inline)
+ dt_sugar_kinst_create_probes(&dp);
+ 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.
-<function> corresponds to the function to be traced, and <instruction> is the
-offset to the specific instruction, and can be obtained from the function's
-disassembly using kgdb from the gdb package.
+.Cm <function>
+corresponds to the function to be traced, and
+.Cm <instruction>
+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::<inline_func>:<entry/return> .
+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
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Nov 22, 9:00 AM (18 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14773623
Default Alt Text
D38825.id119037.diff (23 KB)
Attached To
Mode
D38825: libdtrace: implement inline function tracing for kinst
Attached
Detach File
Event Timeline
Log In to Comment