diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index a347b19c961a..faec899435f2 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -1085,3 +1085,182 @@ int die_get_var_range(Dwarf_Die *sp_die __maybe_unused, return -ENOTSUP; } #endif + +/* + * die_has_loclist - Check if DW_AT_location of @vr_die is a location list + * @vr_die: a variable DIE + */ +static bool die_has_loclist(Dwarf_Die *vr_die) +{ + Dwarf_Attribute loc; + int tag = dwarf_tag(vr_die); + + if (tag != DW_TAG_formal_parameter && + tag != DW_TAG_variable) + return false; + + return (dwarf_attr_integrate(vr_die, DW_AT_location, &loc) && + dwarf_whatform(&loc) == DW_FORM_sec_offset); +} + +/* + * die_is_optimized_target - Check if target program is compiled with + * optimization + * @cu_die: a CU DIE + * + * For any object in given CU whose DW_AT_location is a location list, + * target program is compiled with optimization. This is applicable to + * clang as well. + */ +bool die_is_optimized_target(Dwarf_Die *cu_die) +{ + Dwarf_Die tmp_die; + + if (die_has_loclist(cu_die)) + return true; + + if (!dwarf_child(cu_die, &tmp_die) && + die_is_optimized_target(&tmp_die)) + return true; + + if (!dwarf_siblingof(cu_die, &tmp_die) && + die_is_optimized_target(&tmp_die)) + return true; + + return false; +} + +/* + * die_search_idx - Search index of given line address + * @lines: Line records of single CU + * @nr_lines: Number of @lines + * @addr: address we are looking for + * @idx: index to be set by this function (return value) + * + * Search for @addr by looping over every lines of CU. If address + * matches, set index of that line in @idx. Note that single source + * line can have multiple line records. i.e. single source line can + * have multiple index. + */ +static bool die_search_idx(Dwarf_Lines *lines, unsigned long nr_lines, + Dwarf_Addr addr, unsigned long *idx) +{ + unsigned long i; + Dwarf_Addr tmp; + + for (i = 0; i < nr_lines; i++) { + if (dwarf_lineaddr(dwarf_onesrcline(lines, i), &tmp)) + return false; + + if (tmp == addr) { + *idx = i; + return true; + } + } + return false; +} + +/* + * die_get_postprologue_addr - Search next address after function prologue + * @entrypc_idx: entrypc index + * @lines: Line records of single CU + * @nr_lines: Number of @lines + * @hignpc: high PC address of function + * @postprologue_addr: Next address after function prologue (return value) + * + * Look for prologue-end marker. If there is no explicit marker, return + * address of next line record or next source line. + */ +static bool die_get_postprologue_addr(unsigned long entrypc_idx, + Dwarf_Lines *lines, + unsigned long nr_lines, + Dwarf_Addr highpc, + Dwarf_Addr *postprologue_addr) +{ + unsigned long i; + int entrypc_lno, lno; + Dwarf_Line *line; + Dwarf_Addr addr; + bool p_end; + + /* entrypc_lno is actual source line number */ + line = dwarf_onesrcline(lines, entrypc_idx); + if (dwarf_lineno(line, &entrypc_lno)) + return false; + + for (i = entrypc_idx; i < nr_lines; i++) { + line = dwarf_onesrcline(lines, i); + + if (dwarf_lineaddr(line, &addr) || + dwarf_lineno(line, &lno) || + dwarf_lineprologueend(line, &p_end)) + return false; + + /* highpc is exclusive. [entrypc,highpc) */ + if (addr >= highpc) + break; + + /* clang supports prologue-end marker */ + if (p_end) + break; + + /* Actual next line in source */ + if (lno != entrypc_lno) + break; + + /* + * Single source line can have multiple line records. + * For Example, + * void foo() { printf("hello\n"); } + * contains two line records. One points to declaration and + * other points to printf() line. Variable 'lno' won't get + * incremented in this case but 'i' will. + */ + if (i != entrypc_idx) + break; + } + + dwarf_lineaddr(line, postprologue_addr); + if (*postprologue_addr >= highpc) + dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), + postprologue_addr); + + return true; +} + +/* + * die_skip_prologue - Use next address after prologue as probe location + * @sp_die: a subprogram DIE + * @cu_die: a CU DIE + * @entrypc: entrypc of the function + * + * Function prologue prepares stack and registers before executing function + * logic. When target program is compiled without optimization, function + * parameter information is only valid after prologue. When we probe entrypc + * of the function, and try to record function parameter, it contains + * garbage value. + */ +void die_skip_prologue(Dwarf_Die *sp_die, Dwarf_Die *cu_die, + Dwarf_Addr *entrypc) +{ + size_t nr_lines = 0; + unsigned long entrypc_idx = 0; + Dwarf_Lines *lines = NULL; + Dwarf_Addr postprologue_addr; + Dwarf_Addr highpc; + + if (dwarf_highpc(sp_die, &highpc)) + return; + + if (dwarf_getsrclines(cu_die, &lines, &nr_lines)) + return; + + if (!die_search_idx(lines, nr_lines, *entrypc, &entrypc_idx)) + return; + + if (!die_get_postprologue_addr(entrypc_idx, lines, nr_lines, + highpc, &postprologue_addr)) + return; + + *entrypc = postprologue_addr; +} diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index dc0ce1adb075..8b6d2f83af02 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -125,4 +125,12 @@ int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf); /* Get the name and type of given variable DIE, stored as "type\tname" */ int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf); int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf); + +/* Check if target program is compiled with optimization */ +bool die_is_optimized_target(Dwarf_Die *cu_die); + +/* Use next address after prologue as probe location */ +void die_skip_prologue(Dwarf_Die *sp_die, Dwarf_Die *cu_die, + Dwarf_Addr *entrypc); + #endif diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 003ecadae35d..8daca4fc1f8d 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -907,138 +907,6 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) return die_walk_lines(sp_die, probe_point_lazy_walker, pf); } -static bool var_has_loclist(Dwarf_Die *cu_die) -{ - Dwarf_Attribute loc; - int tag = dwarf_tag(cu_die); - - if (tag != DW_TAG_formal_parameter && - tag != DW_TAG_variable) - return false; - - return (dwarf_attr_integrate(cu_die, DW_AT_location, &loc) && - dwarf_whatform(&loc) == DW_FORM_sec_offset); -} - -/* - * For any object in given CU whose DW_AT_location is a location list, - * target program is compiled with optimization. - */ -static bool optimized_target(Dwarf_Die *cu_die) -{ - Dwarf_Die tmp_die; - - if (var_has_loclist(cu_die)) - return true; - - if (!dwarf_child(cu_die, &tmp_die) && optimized_target(&tmp_die)) - return true; - - if (!dwarf_siblingof(cu_die, &tmp_die) && optimized_target(&tmp_die)) - return true; - - return false; -} - -static bool get_entrypc_idx(Dwarf_Lines *lines, unsigned long nr_lines, - Dwarf_Addr pf_addr, unsigned long *entrypc_idx) -{ - unsigned long i; - Dwarf_Addr addr; - - for (i = 0; i < nr_lines; i++) { - if (dwarf_lineaddr(dwarf_onesrcline(lines, i), &addr)) - return false; - - if (addr == pf_addr) { - *entrypc_idx = i; - return true; - } - } - return false; -} - -static bool get_postprologue_addr(unsigned long entrypc_idx, - Dwarf_Lines *lines, - unsigned long nr_lines, - Dwarf_Addr highpc, - Dwarf_Addr *postprologue_addr) -{ - unsigned long i; - int entrypc_lno, lno; - Dwarf_Line *line; - Dwarf_Addr addr; - bool p_end; - - /* entrypc_lno is actual source line number */ - line = dwarf_onesrcline(lines, entrypc_idx); - if (dwarf_lineno(line, &entrypc_lno)) - return false; - - for (i = entrypc_idx; i < nr_lines; i++) { - line = dwarf_onesrcline(lines, i); - - if (dwarf_lineaddr(line, &addr) || - dwarf_lineno(line, &lno) || - dwarf_lineprologueend(line, &p_end)) - return false; - - /* highpc is exclusive. [entrypc,highpc) */ - if (addr >= highpc) - break; - - /* clang supports prologue-end marker */ - if (p_end) - break; - - /* Actual next line in source */ - if (lno != entrypc_lno) - break; - - /* - * Single source line can have multiple line records. - * For Example, - * void foo() { printf("hello\n"); } - * contains two line records. One points to declaration and - * other points to printf() line. Variable 'lno' won't get - * incremented in this case but 'i' will. - */ - if (i != entrypc_idx) - break; - } - - dwarf_lineaddr(line, postprologue_addr); - if (*postprologue_addr >= highpc) - dwarf_lineaddr(dwarf_onesrcline(lines, i - 1), - postprologue_addr); - - return true; -} - -static void __skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf) -{ - size_t nr_lines = 0; - unsigned long entrypc_idx = 0; - Dwarf_Lines *lines = NULL; - Dwarf_Addr postprologue_addr; - Dwarf_Addr highpc; - - if (dwarf_highpc(sp_die, &highpc)) - return; - - if (dwarf_getsrclines(&pf->cu_die, &lines, &nr_lines)) - return; - - if (!get_entrypc_idx(lines, nr_lines, pf->addr, &entrypc_idx)) - return; - - if (!get_postprologue_addr(entrypc_idx, lines, nr_lines, - highpc, &postprologue_addr)) - return; - - pf->addr = postprologue_addr; -} - static void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf) { struct perf_probe_point *pp = &pf->pev->point; @@ -1048,7 +916,7 @@ static void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf) return; /* Compiled with optimization? */ - if (optimized_target(&pf->cu_die)) + if (die_is_optimized_target(&pf->cu_die)) return; /* Don't know entrypc? */ @@ -1068,7 +936,7 @@ static void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf) "Probe on address 0x%" PRIx64 " to force probing at the function entry.\n\n", pf->addr); - __skip_prologue(sp_die, pf); + die_skip_prologue(sp_die, &pf->cu_die, &pf->addr); } static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)