Changeset View
Standalone View
cddl/contrib/opensolaris/lib/libdtrace/common/dt_sugar.c
| Show All 32 Lines | ||||||||||||||||
| * | * | |||||||||||||||
| * This infrastructure is designed to accommodate other syntactic sugar features | * This infrastructure is designed to accommodate other syntactic sugar features | |||||||||||||||
| * in the future. | * in the future. | |||||||||||||||
| */ | */ | |||||||||||||||
| #include <sys/types.h> | #include <sys/types.h> | |||||||||||||||
| #include <sys/wait.h> | #include <sys/wait.h> | |||||||||||||||
| #include <sys/sysmacros.h> | #include <sys/sysmacros.h> | |||||||||||||||
| #include <sys/param.h> | ||||||||||||||||
| #include <sys/queue.h> | ||||||||||||||||
| #include <assert.h> | #include <assert.h> | |||||||||||||||
| #include <strings.h> | ||||||||||||||||
| #include <stdlib.h> | ||||||||||||||||
| #include <stdio.h> | ||||||||||||||||
| #include <ctype.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_module.h> | |||||||||||||||
| #include <dt_program.h> | #include <dt_program.h> | |||||||||||||||
| #include <dt_provider.h> | #include <dt_provider.h> | |||||||||||||||
| #include <dt_printf.h> | #include <dt_printf.h> | |||||||||||||||
| #include <dt_pid.h> | #include <dt_pid.h> | |||||||||||||||
| #include <dt_grammar.h> | #include <dt_grammar.h> | |||||||||||||||
| #include <dt_ident.h> | #include <dt_ident.h> | |||||||||||||||
| #include <dt_string.h> | #include <dt_string.h> | |||||||||||||||
| #include <dt_impl.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 { | typedef struct dt_sugar_parse { | |||||||||||||||
| dtrace_hdl_t *dtsp_dtp; /* dtrace handle */ | dtrace_hdl_t *dtsp_dtp; /* dtrace handle */ | |||||||||||||||
| dt_node_t *dtsp_pdescs; /* probe descriptions */ | dt_node_t *dtsp_pdescs; /* probe descriptions */ | |||||||||||||||
| int dtsp_num_conditions; /* number of condition variables */ | int dtsp_num_conditions; /* number of condition variables */ | |||||||||||||||
| int dtsp_num_ifs; /* number of "if" statements */ | int dtsp_num_ifs; /* number of "if" statements */ | |||||||||||||||
| dt_node_t *dtsp_clause_list; /* list of clauses */ | 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; | } dt_sugar_parse_t; | |||||||||||||||
| enum { | ||||||||||||||||
| F_SUBPROGRAM, | ||||||||||||||||
| F_INLINE_COPY, | ||||||||||||||||
| }; | ||||||||||||||||
| static void dt_sugar_visit_stmts(dt_sugar_parse_t *, dt_node_t *, int); | static void dt_sugar_visit_stmts(dt_sugar_parse_t *, dt_node_t *, int); | |||||||||||||||
| /* | /* | |||||||||||||||
| * Return a node for "self->%error". | * Return a node for "self->%error". | |||||||||||||||
| * | * | |||||||||||||||
| * Note that the "%" is part of the variable name, and is included so that | * Note that the "%" is part of the variable name, and is included so that | |||||||||||||||
| * this variable name can not collide with any user-specified variable. | * this variable name can not collide with any user-specified variable. | |||||||||||||||
| * | * | |||||||||||||||
| Show All 12 Lines | ||||||||||||||||
| * Append this clause to the clause list. | * Append this clause to the clause list. | |||||||||||||||
| */ | */ | |||||||||||||||
| static void | static void | |||||||||||||||
| dt_sugar_append_clause(dt_sugar_parse_t *dp, dt_node_t *clause) | dt_sugar_append_clause(dt_sugar_parse_t *dp, dt_node_t *clause) | |||||||||||||||
| { | { | |||||||||||||||
| dp->dtsp_clause_list = dt_node_link(dp->dtsp_clause_list, clause); | dp->dtsp_clause_list = dt_node_link(dp->dtsp_clause_list, clause); | |||||||||||||||
| } | } | |||||||||||||||
| /* | /* | |||||||||||||||
markj: You should avoid adding global variables to libdtrace, all state should be accessed via the… | ||||||||||||||||
Done Inline ActionsSince this is dt_sugar-only code, do you think I could embed those globals christos: Since this is dt_sugar-only code, do you think I could embed those globals
in… | ||||||||||||||||
| * Prepend this clause to the clause list. | * Prepend this clause to the clause list. | |||||||||||||||
| */ | */ | |||||||||||||||
| static void | static void | |||||||||||||||
| dt_sugar_prepend_clause(dt_sugar_parse_t *dp, dt_node_t *clause) | dt_sugar_prepend_clause(dt_sugar_parse_t *dp, dt_node_t *clause) | |||||||||||||||
| { | { | |||||||||||||||
| dp->dtsp_clause_list = dt_node_link(clause, dp->dtsp_clause_list); | dp->dtsp_clause_list = dt_node_link(clause, dp->dtsp_clause_list); | |||||||||||||||
| } | } | |||||||||||||||
| ▲ Show 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | ||||||||||||||||
| { | { | |||||||||||||||
| dp->dtsp_num_conditions++; | dp->dtsp_num_conditions++; | |||||||||||||||
| dt_sugar_append_clause(dp, dt_sugar_new_condition_impl(dp, | dt_sugar_append_clause(dp, dt_sugar_new_condition_impl(dp, | |||||||||||||||
| pred, condid, dp->dtsp_num_conditions)); | pred, condid, dp->dtsp_num_conditions)); | |||||||||||||||
| return (dp->dtsp_num_conditions); | return (dp->dtsp_num_conditions); | |||||||||||||||
| } | } | |||||||||||||||
| /* | /* | |||||||||||||||
| * Visit the specified node and all of its descendants. Currently this is only | * kinst-related | |||||||||||||||
| * used to count the number of "if" statements (dtsp_num_ifs). | ||||||||||||||||
| */ | */ | |||||||||||||||
| static int | ||||||||||||||||
| dt_sugar_elf_init(dtrace_hdl_t *dtp, struct elf_info *ei, const char *file) | ||||||||||||||||
Done Inline ActionsAny reason not to use dt_alloc()? markj: Any reason not to use `dt_alloc()`? | ||||||||||||||||
| { | ||||||||||||||||
| 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; | ||||||||||||||||
Done Inline ActionsInstead of duplicating all of these fields, why not embed the GElf_Shdr directly into the section structure? markj: Instead of duplicating all of these fields, why not embed the GElf_Shdr directly into the… | ||||||||||||||||
| } 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 | 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)); | ||||||||||||||||
Done Inline ActionsWhat's the purpose of the else-if? markj: What's the purpose of the else-if? | ||||||||||||||||
Done Inline ActionsNo real use other than to make it obvious that I'm handling both entry christos: No real use other than to make it obvious that I'm handling both `entry`
and `return` probes. | ||||||||||||||||
| 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++; | ||||||||||||||||
| } | ||||||||||||||||
| #ifdef amd64 | ||||||||||||||||
| 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; | ||||||||||||||||
| #endif /* amd64 */ | ||||||||||||||||
| 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; | ||||||||||||||||
| /* 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) { | ||||||||||||||||
| 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 (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) | ||||||||||||||||
| dp->dtsp_flags |= DF_INLINE; | ||||||||||||||||
| else { | ||||||||||||||||
| dp->dtsp_flags |= DF_REGULAR; | ||||||||||||||||
| 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 (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, | ||||||||||||||||
Done Inline ActionsThis should use the kern.bootfile sysctl instead of hard-coding the path. Then we have three cases: debug info is embedded in the kernel executable itself (should be rare), kernel.debug is in the same directory as the kernel, or kernel.debug is in /usr/lib/debug/$(dirname $(sysctl -n kern.bootfile)). It might make sense to keep the bootfile path in the dtrace_hdl_t since there's some other code in libdtrace that uses it. (Since the current code should work fine, you could postpone fixing this until later, just be sure to add a TODO comment or so.) markj: This should use the `kern.bootfile` sysctl instead of hard-coding the path. Then we have three… | ||||||||||||||||
| 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)) && 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)); | ||||||||||||||||
| /* 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; | ||||||||||||||||
| 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) | dt_sugar_visit_all(dt_sugar_parse_t *dp, dt_node_t *dnp) | |||||||||||||||
| { | { | |||||||||||||||
| dt_node_t *arg; | dt_node_t *arg; | |||||||||||||||
| switch (dnp->dn_kind) { | switch (dnp->dn_kind) { | |||||||||||||||
| case DT_NODE_FREE: | case DT_NODE_FREE: | |||||||||||||||
| case DT_NODE_INT: | case DT_NODE_INT: | |||||||||||||||
| case DT_NODE_STRING: | case DT_NODE_STRING: | |||||||||||||||
| case DT_NODE_SYM: | case DT_NODE_SYM: | |||||||||||||||
| case DT_NODE_TYPE: | case DT_NODE_TYPE: | |||||||||||||||
| case DT_NODE_PROBE: | case DT_NODE_PROBE: | |||||||||||||||
| case DT_NODE_PDESC: | case DT_NODE_PDESC: | |||||||||||||||
| case DT_NODE_IDENT: | case DT_NODE_IDENT: | |||||||||||||||
Done Inline ActionsDon't we want to check the probe name as well? This code shouldn't modify regular kinst::<func>:<offset> probes. markj: Don't we want to check the probe name as well? This code shouldn't modify regular kinst::<func>… | ||||||||||||||||
Done Inline ActionsThis doesn't affect kinst::<func>:<offset> probes at all. The christos: This doesn't affect `kinst::<func>:<offset>` probes at all. The
flag is tested in… | ||||||||||||||||
Done Inline ActionsI don't see any other uses of cont, perhaps this can be done without using a goto? domagoj.stolfa_gmail.com: I don't see any other uses of `cont`, perhaps this can be done without using a goto? | ||||||||||||||||
| break; | break; | |||||||||||||||
| case DT_NODE_FUNC: | case DT_NODE_FUNC: | |||||||||||||||
Done Inline Actions
I guess it could be rewritten like this. christos: > I don't see any other uses of cont, perhaps this can be done without using a goto?
I guess it… | ||||||||||||||||
| for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list) | for (arg = dnp->dn_args; arg != NULL; arg = arg->dn_list) | |||||||||||||||
| dt_sugar_visit_all(dp, arg); | dt_sugar_visit_all(dp, arg); | |||||||||||||||
| break; | break; | |||||||||||||||
| case DT_NODE_OP1: | case DT_NODE_OP1: | |||||||||||||||
| dt_sugar_visit_all(dp, dnp->dn_child); | dt_sugar_visit_all(dp, dnp->dn_child); | |||||||||||||||
| break; | break; | |||||||||||||||
| ▲ Show 20 Lines • Show All 239 Lines • ▼ Show 20 Lines | ||||||||||||||||
| /* | /* | |||||||||||||||
| * Transform the super-clause into straight-D, returning the new list of | * Transform the super-clause into straight-D, returning the new list of | |||||||||||||||
| * sub-clauses. | * sub-clauses. | |||||||||||||||
| */ | */ | |||||||||||||||
| dt_node_t * | dt_node_t * | |||||||||||||||
| dt_compile_sugar(dtrace_hdl_t *dtp, dt_node_t *clause) | dt_compile_sugar(dtrace_hdl_t *dtp, dt_node_t *clause) | |||||||||||||||
| { | { | |||||||||||||||
| dt_sugar_parse_t dp = { 0 }; | dt_sugar_parse_t dp = { 0 }; | |||||||||||||||
| dt_node_t *dnp; | ||||||||||||||||
| int condid = 0; | int condid = 0; | |||||||||||||||
| dp.dtsp_dtp = dtp; | dp.dtsp_dtp = dtp; | |||||||||||||||
| dp.dtsp_pdescs = clause->dn_pdescs; | dp.dtsp_pdescs = clause->dn_pdescs; | |||||||||||||||
| /* make dt_node_int() generate an "int"-typed integer */ | /* make dt_node_int() generate an "int"-typed integer */ | |||||||||||||||
| yyintdecimal = B_TRUE; | yyintdecimal = B_TRUE; | |||||||||||||||
| yyintsuffix[0] = '\0'; | yyintsuffix[0] = '\0'; | |||||||||||||||
| Show All 30 Lines | if (dp.dtsp_num_ifs == 0 && dp.dtsp_num_conditions == 0) { | |||||||||||||||
| } | } | |||||||||||||||
| } | } | |||||||||||||||
| if (dp.dtsp_num_conditions != 0) { | if (dp.dtsp_num_conditions != 0) { | |||||||||||||||
| dt_sugar_prepend_clause(&dp, | dt_sugar_prepend_clause(&dp, | |||||||||||||||
| dt_sugar_new_clearerror_clause(&dp)); | 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; | ||||||||||||||||
| dp.dtsp_flags = 0; | ||||||||||||||||
| dp.dtsp_flags |= DF_NEWPDESC; | ||||||||||||||||
| dt_sugar_do_kinst_inline(&dp); | ||||||||||||||||
| } | ||||||||||||||||
| if (dp.dtsp_clause_list != NULL && | if (dp.dtsp_clause_list != NULL && | |||||||||||||||
| dp.dtsp_clause_list->dn_list != NULL && !dtp->dt_has_sugar) { | dp.dtsp_clause_list->dn_list != NULL && !dtp->dt_has_sugar) { | |||||||||||||||
| dtp->dt_has_sugar = B_TRUE; | dtp->dt_has_sugar = B_TRUE; | |||||||||||||||
| dt_sugar_prepend_clause(&dp, dt_sugar_makeerrorclause()); | dt_sugar_prepend_clause(&dp, dt_sugar_makeerrorclause()); | |||||||||||||||
| } | } | |||||||||||||||
| return (dp.dtsp_clause_list); | return (dp.dtsp_clause_list); | |||||||||||||||
| } | } | |||||||||||||||
You should avoid adding global variables to libdtrace, all state should be accessed via the dtrace_hdl_t structure.