Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F103134644
D38825.id119311.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
25 KB
Referenced Files
None
Subscribers
None
D38825.id119311.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,56 @@
#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;
+ } *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_mod; /* 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 */
+#define DF_EXISTS 0x01 /* function exists in module */
+#define DF_REGULAR 0x02 /* function has regular definition */
+#define DF_INLINE 0x04 /* function has inline definition */
+#define DF_ENTRY 0x08 /* probe is entry */
+#define DF_RETURN 0x10 /* probe is return */
+#define DF_NEWPDESC 0x20 /* we're searching a new pdesc */
+ int dtsp_flags; /* kinst-related flags */
+ char dtsp_func[DTRACE_FUNCNAMELEN]; /* current function */
+ char dtsp_name[DTRACE_NAMELEN]; /* current probe name */
+ 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 +239,625 @@
}
/*
- * 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 int
+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) {
+ warnx("dt_sugar: elf_version(): %s", elf_errmsg(-1));
+ return (-1);
+ }
+ if ((ei->fd = open(file, O_RDONLY)) < 0) {
+ dt_dprintf("dt_sugar: open(%s)", file);
+ return (-1);
+ }
+ if ((ei->elf = elf_begin(ei->fd, ELF_C_READ, NULL)) == NULL) {
+ warnx("dt_sugar: elf_begin(): %s", elf_errmsg(-1));
+ return (-1);
+ }
+ if (elf_kind(ei->elf) == ELF_K_NONE) {
+ warnx("dt_sugar: not an ELF file: %s", file);
+ return (-1);
+ }
+
+ /* Load ELF sections */
+ if (!elf_getshnum(ei->elf, &ei->shnum)) {
+ warnx("dt_sugar: elf_getshnum(): %s", elf_errmsg(-1));
+ return (-1);
+ }
+ ei->sl = dt_alloc(dtp, ei->shnum * sizeof(struct section));
+ if (ei->sl == NULL)
+ longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
+ if (!elf_getshstrndx(ei->elf, &shstrndx)) {
+ warnx("dt_sugar: elf_getshstrndx(): %s", elf_errmsg(-1));
+ return (-1);
+ }
+ if ((scn = elf_getscn(ei->elf, 0)) == NULL) {
+ warnx("dt_sugar: elf_getscn(): %s", elf_errmsg(-1));
+ return (-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));
+
+ return (0);
+}
+
+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, int last)
+{
+ 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_mod.shnum; i++) {
+ s = &dp->dtsp_elf_mod.sl[i];
+ if (s->sh.sh_type != SHT_SYMTAB && s->sh.sh_type != SHT_DYNSYM)
+ continue;
+ if (s->sh.sh_link >= dp->dtsp_elf_mod.shnum)
+ continue;
+ stab = s->sh.sh_link;
+ len = (int)(s->sh.sh_size / s->sh.sh_entsize);
+ (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 (len > INT_MAX)
+ continue;
+ for (j = 0; j < len; j++) {
+ if (gelf_getsym(d, j, &sym) != &sym) {
+ warnx("dt_sugar: gelf_getsym(): %s",
+ elf_errmsg(-1));
+ continue;
+ }
+ if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
+ 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_mod.elf, stab,
+ sym.st_name)) != NULL)
+ break;
+ }
+ }
+
+ /* Find inline copy's return offset. */
+ for (i = 1; i < dp->dtsp_elf_mod.shnum; i++) {
+ s = &dp->dtsp_elf_mod.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;
+
+ while (addr != lo) {
+ addr++;
+ buf++;
+ }
+
+ if (dp->dtsp_flags & DF_ENTRY) {
+ off->val = addr_lo - lo;
+ } else if (dp->dtsp_flags & DF_RETURN) {
+ if (addr_hi == hi || last) {
+ /*
+ * In this case the offset is one instruction
+ * *outside* the inline or the caller function,
+ * so we have to go back one instruction to
+ * stay within bounds.
+ */
+
+ /*
+ * 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;
+ }
+ addr -= d86.d86_len;
+ off->val = addr - lo;
+ } else
+ off->val = addr_hi - 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) {
+ /* Find if the function exists in the current module. */
+ if (dwarf_hasattr(die, DW_AT_name, &v_flag, &error) !=
+ DW_DLV_OK) {
+ warnx("dt_sugar: %s", dwarf_errmsg(error));
+ goto cont;
+ }
+ if (!v_flag)
+ goto cont;
+ if (dwarf_diename(die, &v_str, &error) != DW_DLV_OK) {
+ warnx("dt_sugar: %s", dwarf_errmsg(error));
+ goto cont;
+ }
+ if (strcmp(v_str, dp->dtsp_func) == 0)
+ dp->dtsp_flags |= DF_EXISTS;
+ else
+ goto cont;
+
+ 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) {
+ dp->dtsp_flags |= DF_INLINE;
+ found = 1;
+ } else
+ dp->dtsp_flags |= DF_REGULAR;
+ 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 (dp->dtsp_flags & DF_ENTRY) {
+ /*
+ * Trace the first instruction of the first
+ * range since this is the beginning of the
+ * inline copy.
+ */
+ noff = 1;
+ } else if (dp->dtsp_flags & DF_RETURN) {
+ /*
+ * 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)
+ longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
+ 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,
+ (dp->dtsp_flags & DF_RETURN) &&
+ i == noff - 1);
+ }
+ 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)
+ longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
+ dt_sugar_kinst_find_caller_func(dp, off, lowpc,
+ lowpc + highpc,
+ dp->dtsp_flags & DF_RETURN);
+ }
+ } else
+ goto cont;
+
+ e = dt_alloc(dp->dtsp_dtp, sizeof(struct entry));
+ if (e == NULL)
+ longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM);
+ 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;
+ 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 *dnp, *pdesc;
+ struct entry *e;
+ char buf[DTRACE_FULLNAMELEN];
+ int i, n;
+
+ dnp = dp->dtsp_clause_list->dn_pdescs;
+ n = dp->dtsp_flags & DF_NEWPDESC;
+
+ /* We have found inline copies. 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 (dp->dtsp_flags & DF_NEWPDESC) {
+ /* We want to get here only once per-pdesc. */
+ dp->dtsp_flags &= ~DF_NEWPDESC;
+ /*
+ * 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 {
+ /*
+ * Create a new probe description for each
+ * inline copy and append it to the main pdesc
+ * list.
+ */
+ 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);
+ }
+ if (!(dp->dtsp_flags & DF_INLINE)) {
+ /*
+ * Delegate non-inline function probes to FBT so that we don't
+ * duplicate FBT code in kinst. Regular kinst probes are not
+ * affected by this.
+ */
+ strlcpy(dp->dtsp_desc->dtpd_provider, "fbt",
+ sizeof(dp->dtsp_desc->dtpd_provider));
+ } else if ((~dp->dtsp_flags & (DF_REGULAR | DF_INLINE)) == 0 && n) {
+ /*
+ * If we have found a regular definition along with an inline
+ * one, create an FBT probe for the non-inline definition as
+ * well.
+ */
+ snprintf(buf, sizeof(buf), "fbt:%s:%s:%s",
+ dp->dtsp_desc->dtpd_mod, dp->dtsp_func, dp->dtsp_name);
+ pdesc = dt_node_pdesc_by_name(strdup(buf));
+ dnp = dt_node_link(dnp, pdesc);
+ }
+}
+
+/*
+ * Initialize libelf and libdwarf and parse kernel.debug's DWARF info.
+ */
+static void
+dt_sugar_do_kinst_inline(dt_sugar_parse_t *dp)
+{
+ dt_module_t *dmp;
+ Dwarf_Debug dbg;
+ Dwarf_Die die;
+ Dwarf_Error error;
+ char dbgfile[MAXPATHLEN];
+ int res = DW_DLV_OK;
+
+ strlcpy(dp->dtsp_func, dp->dtsp_desc->dtpd_func, sizeof(dp->dtsp_func));
+ strlcpy(dp->dtsp_name, dp->dtsp_desc->dtpd_name, sizeof(dp->dtsp_func));
+
+ dp->dtsp_flags = 0;
+
+ /* We only make entry and return probes for inline functions. */
+ if (strcmp(dp->dtsp_name, "entry") == 0)
+ dp->dtsp_flags |= DF_ENTRY;
+ else if (strcmp(dp->dtsp_name, "return") == 0)
+ dp->dtsp_flags |= DF_RETURN;
+ else
+ return;
+ dp->dtsp_flags |= DF_NEWPDESC;
+
+ for (dmp = dt_list_next(&dp->dtsp_dtp->dt_modlist); dmp != NULL;
+ dmp = dt_list_next(dmp)) {
+ if (strcmp(dmp->dm_name, "C") == 0 ||
+ strcmp(dmp->dm_name, "D") == 0)
+ continue;
+
+ (void) snprintf(dbgfile, sizeof(dbgfile),
+ "/usr/lib/debug/%s.debug", dmp->dm_file);
+ if (dt_sugar_elf_init(dp->dtsp_dtp, &dp->dtsp_elf_mod,
+ dmp->dm_file) < 0)
+ continue;
+ if (dt_sugar_elf_init(dp->dtsp_dtp, &dp->dtsp_elf_dbg,
+ dbgfile) < 0)
+ continue;
+ if (dwarf_elf_init(dp->dtsp_elf_dbg.elf, DW_DLC_READ, NULL,
+ NULL, &dbg, &error) != DW_DLV_OK) {
+ warnx("dt_sugar: dwarf_elf_init(): %s",
+ dwarf_errmsg(error));
+ continue;
+ }
+
+ TAILQ_INIT(&dp->dtsp_head);
+ dp->dtsp_flags &= ~(DF_INLINE | DF_REGULAR | DF_EXISTS);
+
+ 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));
+
+ if (!(dp->dtsp_flags & DF_EXISTS))
+ goto cont;
+
+ dt_sugar_kinst_create_probes(dp);
+cont:
+ dwarf_finish(dbg, &error);
+ dt_sugar_elf_deinit(dp->dtsp_dtp, &dp->dtsp_elf_mod);
+ dt_sugar_elf_deinit(dp->dtsp_dtp, &dp->dtsp_elf_dbg);
+ }
+}
+
+/*
+ * 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 +1131,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 +1178,47 @@
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_desc = dnp->dn_desc;
+ dt_sugar_do_kinst_inline(&dp);
+ }
+
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
@@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd February 27, 2023
+.Dd March 18, 2023
.Dt DTRACE_KINST 4
.Os
.Sh NAME
@@ -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 new
+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,50 @@
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::critical_enter:return'
+dtrace: description 'kinst::critical_enter:return' matched 130 probes
+CPU ID FUNCTION:NAME
+ 1 71024 spinlock_enter:53
+ 0 71024 spinlock_enter:53
+ 1 70992 uma_zalloc_arg:49
+ 1 70925 malloc_type_zone_allocated:21
+ 1 70994 uma_zfree_arg:365
+ 1 70924 malloc_type_freed:21
+ 1 71024 spinlock_enter:53
+ 0 71024 spinlock_enter:53
+ 0 71024 spinlock_enter:53
+ 0 71024 spinlock_enter:53
+ 1 71024 spinlock_enter:53
+ 0 71024 spinlock_enter:53
+ 0 70947 _epoch_enter_preempt:122
+ 0 70949 _epoch_exit_preempt:28
+ 0 71024 spinlock_enter:53
+ 0 71024 spinlock_enter:53
+ 0 70947 _epoch_enter_preempt:122
+ 0 70949 _epoch_exit_preempt:28
+ ^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, 11:19 AM (19 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14775173
Default Alt Text
D38825.id119311.diff (25 KB)
Attached To
Mode
D38825: libdtrace: implement inline function tracing for kinst
Attached
Detach File
Event Timeline
Log In to Comment