Changeset View
Standalone View
cddl/contrib/opensolaris/lib/libdtrace/common/dt_sugar.c
Show All 9 Lines | ||||||||||||||||
* source. A copy of the CDDL is also available via the Internet at | * source. A copy of the CDDL is also available via the Internet at | |||||||||||||||
* http://www.illumos.org/license/CDDL. | * http://www.illumos.org/license/CDDL. | |||||||||||||||
* | * | |||||||||||||||
* CDDL HEADER END | * CDDL HEADER END | |||||||||||||||
*/ | */ | |||||||||||||||
/* | /* | |||||||||||||||
* Copyright (c) 2012, 2016 by Delphix. All rights reserved. | * Copyright (c) 2012, 2016 by Delphix. All rights reserved. | |||||||||||||||
* Copyright (c) 2023 The FreeBSD Foundation | ||||||||||||||||
* | ||||||||||||||||
* Portions of this software were developed by Christos Margiolis | ||||||||||||||||
* <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation. | ||||||||||||||||
*/ | */ | |||||||||||||||
/* | /* | |||||||||||||||
* Syntactic sugar features are implemented by transforming the D parse tree | * Syntactic sugar features are implemented by transforming the D parse tree | |||||||||||||||
* such that it only uses the subset of D that is supported by the rest of the | * such that it only uses the subset of D that is supported by the rest of the | |||||||||||||||
* compiler / the kernel. A clause containing these language features is | * compiler / the kernel. A clause containing these language features is | |||||||||||||||
* referred to as a "super-clause", and its transformation typically entails | * referred to as a "super-clause", and its transformation typically entails | |||||||||||||||
* creating several "sub-clauses" to implement it. For diagnosability, the | * creating several "sub-clauses" to implement it. For diagnosability, the | |||||||||||||||
* sub-clauses will be printed if the "-xtree=8" flag is specified. | * sub-clauses will be printed if the "-xtree=8" flag is specified. | |||||||||||||||
* | * | |||||||||||||||
* Currently, the only syntactic sugar feature is "if/else" statements. Each | * Currently, the only syntactic sugar feature is "if/else" statements. Each | |||||||||||||||
* basic block (e.g. the body of the "if" and "else" statements, and the | * basic block (e.g. the body of the "if" and "else" statements, and the | |||||||||||||||
* statements before and after) is turned into its own sub-clause, with a | * statements before and after) is turned into its own sub-clause, with a | |||||||||||||||
* predicate that causes it to be executed only if the code flows to this point. | * predicate that causes it to be executed only if the code flows to this point. | |||||||||||||||
* Nested if/else statements are supported. | * Nested if/else statements are supported. | |||||||||||||||
* | * | |||||||||||||||
* 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 <zlib.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; | ||||||||||||||||
unsigned long 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); | ||||||||||||||||
} | ||||||||||||||||
/* | ||||||||||||||||
* Verify the checksum in the .gnu_debuglink section to make sure the debug | ||||||||||||||||
* file is up to date. | ||||||||||||||||
*/ | ||||||||||||||||
static int | ||||||||||||||||
dt_sugar_elf_verify_debuglink(struct elf_info *ei, int dbgfd) | ||||||||||||||||
{ | ||||||||||||||||
Elf_Data *d; | ||||||||||||||||
struct section *s; | ||||||||||||||||
char buf[MAXPHYS]; | ||||||||||||||||
ssize_t nr; | ||||||||||||||||
uint32_t crc, compcrc; | ||||||||||||||||
int i; | ||||||||||||||||
for (i = 1; i < ei->shnum; i++) { | ||||||||||||||||
s = &ei->sl[i]; | ||||||||||||||||
if (strcmp(s->name, ".gnu_debuglink") == 0) | ||||||||||||||||
break; | ||||||||||||||||
} | ||||||||||||||||
if ((d = elf_getdata(s->scn, NULL)) == NULL) { | ||||||||||||||||
if (elf_errno() != 0) | ||||||||||||||||
warnx("dt_sugar: elf_getdata(): %s", elf_errmsg(-1)); | ||||||||||||||||
return (-1); | ||||||||||||||||
} | ||||||||||||||||
if (d->d_size < sizeof(crc) + 1 || d->d_buf == NULL) | ||||||||||||||||
return (-1); | ||||||||||||||||
if (strnlen(d->d_buf, d->d_size) >= d->d_size - sizeof(crc)) | ||||||||||||||||
return (-1); | ||||||||||||||||
memcpy(&crc, (uint8_t *)d->d_buf + d->d_size - sizeof(crc), | ||||||||||||||||
sizeof(crc)); | ||||||||||||||||
compcrc = crc32(0L, Z_NULL, 0); | ||||||||||||||||
while ((nr = read(dbgfd, buf, sizeof(buf))) > 0) | ||||||||||||||||
compcrc = crc32(compcrc, (char *)buf, nr); | ||||||||||||||||
if (nr != 0 || crc != compcrc) | ||||||||||||||||
return (-1); | ||||||||||||||||
return (0); | ||||||||||||||||
} | ||||||||||||||||
/* | ||||||||||||||||
* 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; | ||||||||||||||||
uint8_t *buf; | ||||||||||||||||
uint64_t addr, lo, hi; | ||||||||||||||||
uint32_t stab; | ||||||||||||||||
int len, i, j; | ||||||||||||||||
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. | ||||||||||||||||
/* 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++; | ||||||||||||||||
} | ||||||||||||||||
len = 0; | ||||||||||||||||
while (addr != addr_hi) { | ||||||||||||||||
len = dtrace_instr_size(buf); | ||||||||||||||||
addr += len; | ||||||||||||||||
buf += len; | ||||||||||||||||
} | ||||||||||||||||
addr -= 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) { | ||||||||||||||||
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 | ||||||||||||||||
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… | ||||||||||||||||
* 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) { | ||||||||||||||||
dt_sugar_elf_deinit(dp->dtsp_dtp, &dp->dtsp_elf_mod); | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
if (dt_sugar_elf_verify_debuglink(&dp->dtsp_elf_mod, | ||||||||||||||||
dp->dtsp_elf_dbg.fd) < 0) { | ||||||||||||||||
warnx("dt_sugar: debug link mismatch: " | ||||||||||||||||
"make sure '%s' is up to date", dbgfile); | ||||||||||||||||
dt_sugar_elf_deinit(dp->dtsp_dtp, &dp->dtsp_elf_mod); | ||||||||||||||||
dt_sugar_elf_deinit(dp->dtsp_dtp, &dp->dtsp_elf_dbg); | ||||||||||||||||
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; | ||||||||||||||||
domagoj.stolfa_gmail.comUnsubmitted 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? | ||||||||||||||||
dt_sugar_kinst_create_probes(dp); | ||||||||||||||||
cont: | ||||||||||||||||
christosAuthorUnsubmitted 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… | ||||||||||||||||
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… | ||||||||||||||||
break; | break; | |||||||||||||||
case DT_NODE_FUNC: | case DT_NODE_FUNC: | |||||||||||||||
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: | |||||||||||||||
▲ Show 20 Lines • Show All 242 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) { | ||||||||||||||||
int fd; | ||||||||||||||||
if (strcmp(dnp->dn_desc->dtpd_provider, "kinst") != 0) | ||||||||||||||||
continue; | ||||||||||||||||
/* | ||||||||||||||||
* Make sure kinst exists to avoid spending time parsing DWARF | ||||||||||||||||
* in case it doesn't. | ||||||||||||||||
*/ | ||||||||||||||||
fd = open("/dev/dtrace/kinst", O_RDONLY); | ||||||||||||||||
if (fd < 0) { | ||||||||||||||||
close(fd); | ||||||||||||||||
continue; | ||||||||||||||||
} | ||||||||||||||||
close(fd); | ||||||||||||||||
dp.dtsp_desc = dnp->dn_desc; | ||||||||||||||||
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.