Commit 2c4b9280 authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo

perf srcline: Support for llvm-addr2line

The sentinel value differs for llvm-addr2line. Configure this once and
then detect when reading records.
Signed-off-by: default avatarIan Rogers <irogers@google.com>
Acked-by: default avatarNamhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Tom Rix <trix@redhat.com>
Cc: llvm@lists.linux.dev
Link: https://lore.kernel.org/r/20230403184033.1836023-2-irogers@google.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent b3801e79
...@@ -435,7 +435,50 @@ static struct child_process *addr2line_subprocess_init(const char *addr2line_pat ...@@ -435,7 +435,50 @@ static struct child_process *addr2line_subprocess_init(const char *addr2line_pat
return a2l; return a2l;
} }
enum a2l_style {
BROKEN,
GNU_BINUTILS,
LLVM,
};
static enum a2l_style addr2line_configure(struct child_process *a2l)
{
static bool cached;
static enum a2l_style style;
if (!cached) {
char buf[128];
struct io io;
int ch;
if (write(a2l->in, ",\n", 2) != 2)
return BROKEN;
io__init(&io, a2l->out, buf, sizeof(buf));
ch = io__get_char(&io);
if (ch == ',') {
style = LLVM;
cached = true;
} else if (ch == '?') {
style = GNU_BINUTILS;
cached = true;
} else {
style = BROKEN;
}
do {
ch = io__get_char(&io);
} while (ch > 0 && ch != '\n');
if (style == GNU_BINUTILS) {
do {
ch = io__get_char(&io);
} while (ch > 0 && ch != '\n');
}
}
return style;
}
static int read_addr2line_record(struct io *io, static int read_addr2line_record(struct io *io,
enum a2l_style style,
char **function, char **function,
char **filename, char **filename,
unsigned int *line_nr) unsigned int *line_nr)
...@@ -462,6 +505,12 @@ static int read_addr2line_record(struct io *io, ...@@ -462,6 +505,12 @@ static int read_addr2line_record(struct io *io,
if (io__getline(io, &line, &line_len) < 0 || !line_len) if (io__getline(io, &line, &line_len) < 0 || !line_len)
goto error; goto error;
if (style == LLVM && line_len == 2 && line[0] == ',') {
zfree(&line);
return 0;
}
if (function != NULL) if (function != NULL)
*function = strdup(strim(line)); *function = strdup(strim(line));
...@@ -471,7 +520,8 @@ static int read_addr2line_record(struct io *io, ...@@ -471,7 +520,8 @@ static int read_addr2line_record(struct io *io,
if (io__getline(io, &line, &line_len) < 0 || !line_len) if (io__getline(io, &line, &line_len) < 0 || !line_len)
goto error; goto error;
if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0) { if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0 &&
style == GNU_BINUTILS) {
ret = 0; ret = 0;
goto error; goto error;
} }
...@@ -523,6 +573,7 @@ static int addr2line(const char *dso_name, u64 addr, ...@@ -523,6 +573,7 @@ static int addr2line(const char *dso_name, u64 addr,
char buf[128]; char buf[128];
ssize_t written; ssize_t written;
struct io io; struct io io;
enum a2l_style a2l_style;
if (!a2l) { if (!a2l) {
if (!filename__has_section(dso_name, ".debug_line")) if (!filename__has_section(dso_name, ".debug_line"))
...@@ -538,15 +589,22 @@ static int addr2line(const char *dso_name, u64 addr, ...@@ -538,15 +589,22 @@ static int addr2line(const char *dso_name, u64 addr,
pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name); pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name);
goto out; goto out;
} }
a2l_style = addr2line_configure(a2l);
if (a2l_style == BROKEN) {
if (!symbol_conf.disable_add2line_warn)
pr_warning("%s: addr2line configuration failed\n", __func__);
goto out;
}
/* /*
* Send our request and then *deliberately* send something that can't be interpreted as * Send our request and then *deliberately* send something that can't be interpreted as
* a valid address to ask addr2line about (namely, ","). This causes addr2line to first * a valid address to ask addr2line about (namely, ","). This causes addr2line to first
* write out the answer to our request, in an unbounded/unknown number of records, and * write out the answer to our request, in an unbounded/unknown number of records, and
* then to write out the lines "??" and "??:0", so that we can detect when it has * then to write out the lines "??" and "??:0", for GNU binutils, or "," for
* finished giving us anything useful. We have to be careful about the first record, * llvm-addr2line, so that we can detect when it has finished giving us anything
* though, because it may be genuinely unknown, in which case we'll get two sets of * useful. With GNU binutils, we have to be careful about the first record, though,
* "??"/"??:0" lines. * because it may be genuinely unknown, in which case we'll get two sets of "??"/"??:0"
* lines.
*/ */
len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr); len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr);
written = len > 0 ? write(a2l->in, buf, len) : -1; written = len > 0 ? write(a2l->in, buf, len) : -1;
...@@ -557,7 +615,8 @@ static int addr2line(const char *dso_name, u64 addr, ...@@ -557,7 +615,8 @@ static int addr2line(const char *dso_name, u64 addr,
} }
io__init(&io, a2l->out, buf, sizeof(buf)); io__init(&io, a2l->out, buf, sizeof(buf));
switch (read_addr2line_record(&io, &record_function, &record_filename, &record_line_nr)) { switch (read_addr2line_record(&io, a2l_style,
&record_function, &record_filename, &record_line_nr)) {
case -1: case -1:
if (!symbol_conf.disable_add2line_warn) if (!symbol_conf.disable_add2line_warn)
pr_warning("%s %s: could not read first record\n", __func__, dso_name); pr_warning("%s %s: could not read first record\n", __func__, dso_name);
...@@ -567,7 +626,7 @@ static int addr2line(const char *dso_name, u64 addr, ...@@ -567,7 +626,7 @@ static int addr2line(const char *dso_name, u64 addr,
* The first record was invalid, so return failure, but first read another * The first record was invalid, so return failure, but first read another
* record, since we asked a junk question and have to clear the answer out. * record, since we asked a junk question and have to clear the answer out.
*/ */
switch (read_addr2line_record(&io, NULL, NULL, NULL)) { switch (read_addr2line_record(&io, a2l_style, NULL, NULL, NULL)) {
case -1: case -1:
if (!symbol_conf.disable_add2line_warn) if (!symbol_conf.disable_add2line_warn)
pr_warning("%s %s: could not read delimiter record\n", pr_warning("%s %s: could not read delimiter record\n",
...@@ -606,6 +665,7 @@ static int addr2line(const char *dso_name, u64 addr, ...@@ -606,6 +665,7 @@ static int addr2line(const char *dso_name, u64 addr,
/* We have to read the records even if we don't care about the inline info. */ /* We have to read the records even if we don't care about the inline info. */
while ((record_status = read_addr2line_record(&io, while ((record_status = read_addr2line_record(&io,
a2l_style,
&record_function, &record_function,
&record_filename, &record_filename,
&record_line_nr)) == 1) { &record_line_nr)) == 1) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment