perf srcline: Support for llvm-addr2line
authorIan Rogers <irogers@google.com>
Mon, 3 Apr 2023 18:40:32 +0000 (11:40 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 4 Apr 2023 16:23:59 +0000 (13:23 -0300)
The sentinel value differs for llvm-addr2line. Configure this once and
then detect when reading records.

Signed-off-by: Ian Rogers <irogers@google.com>
Acked-by: Namhyung 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.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/srcline.c

index 5339ab4c5e12367f3cc37eefe3569a69f5c407b8..f4fcdada821b5e934c79b5e9d89a035d00108b95 100644 (file)
@@ -435,7 +435,50 @@ static struct child_process *addr2line_subprocess_init(const char *addr2line_pat
        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,
+                                enum a2l_style style,
                                 char **function,
                                 char **filename,
                                 unsigned int *line_nr)
@@ -462,6 +505,12 @@ static int read_addr2line_record(struct io *io,
 
        if (io__getline(io, &line, &line_len) < 0 || !line_len)
                goto error;
+
+       if (style == LLVM && line_len == 2 && line[0] == ',') {
+               zfree(&line);
+               return 0;
+       }
+
        if (function != NULL)
                *function = strdup(strim(line));
 
@@ -471,7 +520,8 @@ static int read_addr2line_record(struct io *io,
        if (io__getline(io, &line, &line_len) < 0 || !line_len)
                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;
                goto error;
        }
@@ -523,6 +573,7 @@ static int addr2line(const char *dso_name, u64 addr,
        char buf[128];
        ssize_t written;
        struct io io;
+       enum a2l_style a2l_style;
 
        if (!a2l) {
                if (!filename__has_section(dso_name, ".debug_line"))
@@ -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);
                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
         * 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
-        * then to write out the lines "??" and "??:0", so that we can detect when it has
-        * finished giving us anything useful. We have to be careful about the first record,
-        * though, because it may be genuinely unknown, in which case we'll get two sets of
-        * "??"/"??:0" lines.
+        * then to write out the lines "??" and "??:0", for GNU binutils, or "," for
+        * llvm-addr2line, so that we can detect when it has finished giving us anything
+        * useful. With GNU binutils, we have to be careful about the first record, though,
+        * 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);
        written = len > 0 ? write(a2l->in, buf, len) : -1;
@@ -557,7 +615,8 @@ static int addr2line(const char *dso_name, u64 addr,
        }
        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:
                if (!symbol_conf.disable_add2line_warn)
                        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,
                 * 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.
                 */
-               switch (read_addr2line_record(&io, NULL, NULL, NULL)) {
+               switch (read_addr2line_record(&io, a2l_style, NULL, NULL, NULL)) {
                case -1:
                        if (!symbol_conf.disable_add2line_warn)
                                pr_warning("%s %s: could not read delimiter record\n",
@@ -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. */
        while ((record_status = read_addr2line_record(&io,
+                                                     a2l_style,
                                                      &record_function,
                                                      &record_filename,
                                                      &record_line_nr)) == 1) {