mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
perf libdw: Optimize fallback logic for missing DWARF
When libdw encounters a DWARF definition it cannot accurately un-inline
or an address block natively missing structural line maps,
dwfl_module_getsrc() returns NULL.
Previously, libdw__addr2line() returned -1 in both dwfl_module_getdwarf()
and dwfl_module_getsrc() failure paths while addr2line() in srcline.c
only stopped its fallback loop on ret > 0.
This caused perf's addr2line_style loop to fall back to llvm, libbfd,
and the cmd__addr2line() subprocess wrapper on every unmapped address or
stripped DSO.
Update the addr2line_style pipeline return semantics consistently:
1. Backends return 0 to denote conclusively missing information
(e.g. valid ELF but no DWARF inside, or parsed DWARF with no mapped
lines), stopping the fallback loop.
2. Backends (and uncompiled stubs) return < 0 to denote initialization
or backend errors, instructing the loop to iterate to the next
configured fallback.
3. Backends return > 0 on successful source line resolution.
Fixes: bf96e296d5 ("perf libdw: Fix Dwfl discovery with split files")
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alessio Podda <aleph.pi.gh@gmail.com>
Cc: Alireza Haghdoost <haghdoost@uber.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Michael Liang <mliang@purestorage.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Tanushree Shah <tshah@linux.ibm.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
cb28bc71af
commit
62d7cdc8d5
|
|
@ -298,6 +298,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
|
|||
struct io io = { .eof = false };
|
||||
enum cmd_a2l_style cmd_a2l_style;
|
||||
const char *current_dso_name;
|
||||
bool a2l_synced = false;
|
||||
|
||||
mutex_lock(dso__lock(dso));
|
||||
current_dso_name = dso__symsrc_filename(dso) ?: dso_name;
|
||||
|
|
@ -316,11 +317,14 @@ int cmd__addr2line(const char *dso_name, u64 addr,
|
|||
if (a2l == NULL) {
|
||||
if (!symbol_conf.addr2line_disable_warn)
|
||||
pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name);
|
||||
ret = -1;
|
||||
goto out_unlock;
|
||||
}
|
||||
cmd_a2l_style = cmd_addr2line_configure(a2l, dso_name);
|
||||
if (cmd_a2l_style == BROKEN)
|
||||
if (cmd_a2l_style == BROKEN) {
|
||||
ret = -1;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take ownership of the a2l subprocess so we can safely perform
|
||||
|
|
@ -344,6 +348,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
|
|||
if (written != len) {
|
||||
if (!symbol_conf.addr2line_disable_warn)
|
||||
pr_warning("%s %s: could not send request\n", __func__, dso_name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
io__init(&io, a2l->out, buf, sizeof(buf));
|
||||
|
|
@ -353,6 +358,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
|
|||
case -1:
|
||||
if (!symbol_conf.addr2line_disable_warn)
|
||||
pr_warning("%s %s: could not read first record\n", __func__, dso_name);
|
||||
ret = -1;
|
||||
goto out;
|
||||
case 0:
|
||||
/*
|
||||
|
|
@ -373,6 +379,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
|
|||
break;
|
||||
case 0:
|
||||
/* The sentinel as expected. */
|
||||
a2l_synced = true;
|
||||
break;
|
||||
default:
|
||||
if (!symbol_conf.addr2line_disable_warn)
|
||||
|
|
@ -388,17 +395,23 @@ int cmd__addr2line(const char *dso_name, u64 addr,
|
|||
|
||||
if (file) {
|
||||
*file = strdup(record_filename);
|
||||
ret = 1;
|
||||
if (!*file) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (line_nr)
|
||||
*line_nr = record_line_nr;
|
||||
ret = 1;
|
||||
|
||||
if (unwind_inlines) {
|
||||
if (node && inline_list__append_record(dso, node, sym,
|
||||
record_function,
|
||||
record_filename,
|
||||
record_line_nr)) {
|
||||
ret = 0;
|
||||
if (file && *file)
|
||||
zfree(file);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
@ -421,12 +434,17 @@ int cmd__addr2line(const char *dso_name, u64 addr,
|
|||
record_function,
|
||||
record_filename,
|
||||
record_line_nr)) {
|
||||
ret = 0;
|
||||
if (file && *file)
|
||||
zfree(file);
|
||||
inline_node__clear_frames(node);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ret = 1; /* found at least one inline frame */
|
||||
}
|
||||
}
|
||||
if (record_status == 0)
|
||||
a2l_synced = true;
|
||||
|
||||
out:
|
||||
mutex_lock(dso__lock(dso));
|
||||
|
|
@ -434,7 +452,7 @@ int cmd__addr2line(const char *dso_name, u64 addr,
|
|||
free(record_filename);
|
||||
|
||||
current_dso_name = dso__symsrc_filename(dso) ?: dso__long_name(dso);
|
||||
if (!io.eof && dso__a2l(dso) == NULL && current_dso_name &&
|
||||
if (a2l_synced && !io.eof && dso__a2l(dso) == NULL && current_dso_name &&
|
||||
!strcmp(current_dso_name, dso_name))
|
||||
dso__set_a2l(dso, a2l);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -760,7 +760,7 @@ static inline const char *dso__symsrc_filename(const struct dso *dso)
|
|||
return RC_CHK_ACCESS(dso)->symsrc_filename;
|
||||
}
|
||||
|
||||
void dso__set_symsrc_filename(struct dso *dso, char *val);
|
||||
void dso__set_symsrc_filename(struct dso *dso, char *val) EXCLUSIVE_LOCKS_REQUIRED(dso__lock(dso));
|
||||
|
||||
static inline void dso__free_symsrc_filename(struct dso *dso)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ int libbfd__addr2line(const char *dso_name, u64 addr,
|
|||
|
||||
if (node && inline_list__append_dso_a2l(dso, node, sym)) {
|
||||
inline_node__clear_frames(node);
|
||||
ret = 0;
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +302,7 @@ int libbfd__addr2line(const char *dso_name, u64 addr,
|
|||
if (node != NULL) {
|
||||
if (inline_list__append_dso_a2l(dso, node, sym)) {
|
||||
inline_node__clear_frames(node);
|
||||
ret = 0;
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
@ -312,7 +312,7 @@ int libbfd__addr2line(const char *dso_name, u64 addr,
|
|||
if (file) {
|
||||
*file = a2l->filename ? strdup(a2l->filename) : NULL;
|
||||
if (!*file) {
|
||||
/* Leave ret as 0 so that another addr2line is tried. */
|
||||
ret = a2l->filename ? -ENOMEM : 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "callchain.h"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
static const Dwfl_Callbacks offline_callbacks = {
|
||||
|
|
@ -175,10 +176,15 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
|
|||
int lineno = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!dso__has_srcline(dso))
|
||||
return 0;
|
||||
|
||||
mutex_lock(dso__lock(dso));
|
||||
dwfl = dso__libdw_dwfl(dso);
|
||||
if (!dwfl)
|
||||
if (!dwfl) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mod = dwfl_addrmodule(dwfl, addr);
|
||||
if (!mod) {
|
||||
|
|
@ -192,14 +198,23 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
|
|||
* libdw.
|
||||
*/
|
||||
if (!dwfl_module_getdwarf(mod, &bias)) {
|
||||
ret = -1;
|
||||
/*
|
||||
* Free the Dwfl session so the mmap'd ELF binary is unmapped
|
||||
* immediately, and mark the DSO as lacking srcline/DWARF info
|
||||
* so neither libdw nor slower fallback backends (addr2line
|
||||
* subprocess, libbfd) are repeatedly invoked on stripped DSOs.
|
||||
*/
|
||||
dso__free_libdw(dso);
|
||||
dso__set_has_srcline(dso, false);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Find source line information for the address. */
|
||||
dwline = dwfl_module_getsrc(mod, addr + bias);
|
||||
if (!dwline) {
|
||||
ret = -1;
|
||||
/* conclusively missing information -> abort fallbacks */
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +243,7 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
|
|||
free(*file);
|
||||
*file = NULL;
|
||||
}
|
||||
ret = 0;
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +260,7 @@ int libdw__addr2line(u64 addr, char **file, unsigned int *line_nr,
|
|||
*file = NULL;
|
||||
}
|
||||
inline_node__clear_frames(node);
|
||||
ret = 0;
|
||||
ret = args.err;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ struct symbol;
|
|||
* finds the source line information for the given address, and optionally
|
||||
* resolves inline function call chains.
|
||||
*
|
||||
* Returns 1 on success (found), 0 on failure (not found).
|
||||
* Returns 1 on success (found), 0 when DWARF/line info is conclusively
|
||||
* missing, and < 0 on initialization or uncompiled error.
|
||||
*/
|
||||
int libdw__addr2line(u64 addr, char **file,
|
||||
unsigned int *line_nr, struct dso *dso,
|
||||
|
|
@ -47,7 +48,7 @@ static inline int libdw__addr2line(u64 addr __maybe_unused, char **file __maybe_
|
|||
struct inline_node *node __maybe_unused,
|
||||
struct symbol *sym __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline void dso__free_libdw(struct dso *dso __maybe_unused)
|
||||
|
|
|
|||
|
|
@ -50,20 +50,20 @@ static LLVMSymbolizer *get_symbolizer()
|
|||
return instance;
|
||||
}
|
||||
|
||||
/* Returns 0 on error, 1 on success. */
|
||||
/* Returns < 0 on error, 0 when line info is missing, 1 on success. */
|
||||
static int extract_file_and_line(const DILineInfo &line_info, char **file,
|
||||
unsigned int *line)
|
||||
{
|
||||
if (file) {
|
||||
if (line_info.FileName == "<invalid>") {
|
||||
/* Match the convention of libbfd. */
|
||||
if (line_info.FileName == "<invalid>") {
|
||||
if (file)
|
||||
*file = nullptr;
|
||||
} else {
|
||||
/* The caller expects to get something it can free(). */
|
||||
*file = strdup(line_info.FileName.c_str());
|
||||
if (*file == nullptr)
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (file) {
|
||||
/* The caller expects to get something it can free(). */
|
||||
*file = strdup(line_info.FileName.c_str());
|
||||
if (*file == nullptr)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (line)
|
||||
*line = line_info.Line;
|
||||
|
|
@ -86,30 +86,42 @@ int llvm_addr2line(const char *dso_name, u64 addr,
|
|||
Expected<DIInliningInfo> res_or_err =
|
||||
symbolizer->symbolizeInlinedCode(dso_name,
|
||||
sectioned_addr);
|
||||
if (!res_or_err)
|
||||
return 0;
|
||||
if (!res_or_err) {
|
||||
consumeError(res_or_err.takeError());
|
||||
return -1;
|
||||
}
|
||||
unsigned num_frames = res_or_err->getNumberOfFrames();
|
||||
if (num_frames == 0)
|
||||
return 0;
|
||||
|
||||
if (extract_file_and_line(res_or_err->getFrame(0),
|
||||
file, line) == 0)
|
||||
return 0;
|
||||
int ret = extract_file_and_line(res_or_err->getFrame(0),
|
||||
file, line);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
*inline_frames = (llvm_a2l_frame *)calloc(
|
||||
num_frames, sizeof(**inline_frames));
|
||||
if (*inline_frames == nullptr)
|
||||
return 0;
|
||||
if (*inline_frames == nullptr) {
|
||||
if (file)
|
||||
zfree(file);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < num_frames; ++i) {
|
||||
const DILineInfo &src = res_or_err->getFrame(i);
|
||||
|
||||
llvm_a2l_frame &dst = (*inline_frames)[i];
|
||||
if (src.FileName == "<invalid>")
|
||||
/* Match the convention of libbfd. */
|
||||
dst.filename = nullptr;
|
||||
else
|
||||
dst.filename = strdup(src.FileName.c_str());
|
||||
if (src.FileName == "<invalid>") {
|
||||
for (unsigned j = 0; j < i; ++j) {
|
||||
zfree(&(*inline_frames)[j].filename);
|
||||
zfree(&(*inline_frames)[j].funcname);
|
||||
}
|
||||
zfree(inline_frames);
|
||||
if (file)
|
||||
zfree(file);
|
||||
return 0;
|
||||
}
|
||||
dst.filename = strdup(src.FileName.c_str());
|
||||
dst.funcname = strdup(src.FunctionName.c_str());
|
||||
dst.line = src.Line;
|
||||
|
||||
|
|
@ -120,7 +132,9 @@ int llvm_addr2line(const char *dso_name, u64 addr,
|
|||
zfree(&(*inline_frames)[j].funcname);
|
||||
}
|
||||
zfree(inline_frames);
|
||||
return 0;
|
||||
if (file)
|
||||
zfree(file);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,8 +145,10 @@ int llvm_addr2line(const char *dso_name, u64 addr,
|
|||
|
||||
Expected<DILineInfo> res_or_err =
|
||||
symbolizer->symbolizeCode(dso_name, sectioned_addr);
|
||||
if (!res_or_err)
|
||||
return 0;
|
||||
if (!res_or_err) {
|
||||
consumeError(res_or_err.takeError());
|
||||
return -1;
|
||||
}
|
||||
return extract_file_and_line(*res_or_err, file, line);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,11 @@ int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused
|
|||
int num_frames = llvm_addr2line(dso_name, addr, file, line,
|
||||
node && unwind_inlines, &inline_frames);
|
||||
|
||||
if (num_frames == 0 || !inline_frames) {
|
||||
/* Error, or we didn't want inlines. */
|
||||
if (num_frames <= 0 || !inline_frames) {
|
||||
/*
|
||||
* Error (< 0 -> iterate fallbacks), missing line info (0), or
|
||||
* we resolved lines without wanting inlines (1).
|
||||
*/
|
||||
return num_frames;
|
||||
}
|
||||
|
||||
|
|
@ -60,8 +63,11 @@ int llvm__addr2line(const char *dso_name __maybe_unused, u64 addr __maybe_unused
|
|||
inline_frames[i].line);
|
||||
}
|
||||
if (inline_list__append(inline_sym, srcline, node) != 0) {
|
||||
if (file)
|
||||
zfree(file);
|
||||
inline_node__clear_frames(node);
|
||||
free_llvm_inline_frames(inline_frames, num_frames);
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
free_llvm_inline_frames(inline_frames, num_frames);
|
||||
|
|
|
|||
|
|
@ -181,9 +181,9 @@ static int addr2line(const char *dso_name, u64 addr, char **file, unsigned int *
|
|||
break;
|
||||
case A2L_STYLE_UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
if (ret > 0)
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -250,6 +250,10 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
|
|||
|
||||
addr2line(dso_name, addr, /*file=*/NULL, /*line_nr=*/NULL, dso,
|
||||
/*unwind_inlines=*/true, node, sym);
|
||||
if (!dso__has_srcline(dso) && list_empty(&node->val)) {
|
||||
inline_node__delete(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
|
@ -276,8 +280,8 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
|||
if (dso_name == NULL)
|
||||
goto out_err;
|
||||
|
||||
if (!addr2line(dso_name, addr, &file, &line, dso,
|
||||
unwind_inlines, /*node=*/NULL, sym)) {
|
||||
if (addr2line(dso_name, addr, &file, &line, dso,
|
||||
unwind_inlines, /*node=*/NULL, sym) <= 0) {
|
||||
free(dso_name);
|
||||
goto out_err;
|
||||
}
|
||||
|
|
@ -302,6 +306,7 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
|||
dso__set_has_srcline(dso, false);
|
||||
dso__free_a2l(dso);
|
||||
dso__free_a2l_libbfd(dso);
|
||||
dso__free_libdw(dso);
|
||||
}
|
||||
mutex_unlock(dso__lock(dso));
|
||||
out:
|
||||
|
|
@ -331,8 +336,8 @@ char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line)
|
|||
if (dso_name == NULL)
|
||||
goto out_err;
|
||||
|
||||
if (!addr2line(dso_name, addr, &file, line, dso, /*unwind_inlines=*/true,
|
||||
/*node=*/NULL, /*sym=*/NULL)) {
|
||||
if (addr2line(dso_name, addr, &file, line, dso, /*unwind_inlines=*/true,
|
||||
/*node=*/NULL, /*sym=*/NULL) <= 0) {
|
||||
free(dso_name);
|
||||
goto out_err;
|
||||
}
|
||||
|
|
@ -350,6 +355,7 @@ char *get_srcline_split(struct dso *dso, u64 addr, unsigned *line)
|
|||
dso__set_has_srcline(dso, false);
|
||||
dso__free_a2l(dso);
|
||||
dso__free_a2l_libbfd(dso);
|
||||
dso__free_libdw(dso);
|
||||
}
|
||||
mutex_unlock(dso__lock(dso));
|
||||
|
||||
|
|
@ -448,6 +454,9 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
|
|||
char *dso_name;
|
||||
struct inline_node *node;
|
||||
|
||||
if (!dso__has_srcline(dso))
|
||||
return NULL;
|
||||
|
||||
dso_name = srcline_dso_name(dso);
|
||||
if (dso_name == NULL)
|
||||
return NULL;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user