diff options
Diffstat (limited to 'tools/perf/util/srcline.c')
-rw-r--r-- | tools/perf/util/srcline.c | 183 |
1 files changed, 112 insertions, 71 deletions
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 33321867416b..cfca03abd6f8 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -10,6 +10,8 @@ #include <linux/string.h> #include <linux/zalloc.h> +#include <api/io.h> + #include "util/dso.h" #include "util/debug.h" #include "util/callchain.h" @@ -366,12 +368,6 @@ void dso__free_a2l(struct dso *dso) #else /* HAVE_LIBBFD_SUPPORT */ -struct a2l_subprocess { - struct child_process addr2line; - FILE *to_child; - FILE *from_child; -}; - static int filename_split(char *filename, unsigned int *line_nr) { char *sep; @@ -393,77 +389,98 @@ static int filename_split(char *filename, unsigned int *line_nr) return 0; } -static void addr2line_subprocess_cleanup(struct a2l_subprocess *a2l) +static void addr2line_subprocess_cleanup(struct child_process *a2l) { - if (a2l->addr2line.pid != -1) { - kill(a2l->addr2line.pid, SIGKILL); - finish_command(&a2l->addr2line); /* ignore result, we don't care */ - a2l->addr2line.pid = -1; - } - - if (a2l->to_child != NULL) { - fclose(a2l->to_child); - a2l->to_child = NULL; - } - - if (a2l->from_child != NULL) { - fclose(a2l->from_child); - a2l->from_child = NULL; + if (a2l->pid != -1) { + kill(a2l->pid, SIGKILL); + finish_command(a2l); /* ignore result, we don't care */ + a2l->pid = -1; } free(a2l); } -static struct a2l_subprocess *addr2line_subprocess_init(const char *path) +static struct child_process *addr2line_subprocess_init(const char *addr2line_path, + const char *binary_path) { - const char *argv[] = { "addr2line", "-e", path, "-i", "-f", NULL }; - struct a2l_subprocess *a2l = zalloc(sizeof(*a2l)); + const char *argv[] = { + addr2line_path ?: "addr2line", + "-e", binary_path, + "-i", "-f", NULL + }; + struct child_process *a2l = zalloc(sizeof(*a2l)); int start_command_status = 0; - if (a2l == NULL) - goto out; - - a2l->to_child = NULL; - a2l->from_child = NULL; + if (a2l == NULL) { + pr_err("Failed to allocate memory for addr2line"); + return NULL; + } - a2l->addr2line.pid = -1; - a2l->addr2line.in = -1; - a2l->addr2line.out = -1; - a2l->addr2line.no_stderr = 1; + a2l->pid = -1; + a2l->in = -1; + a2l->out = -1; + a2l->no_stderr = 1; - a2l->addr2line.argv = argv; - start_command_status = start_command(&a2l->addr2line); - a2l->addr2line.argv = NULL; /* it's not used after start_command; avoid dangling pointers */ + a2l->argv = argv; + start_command_status = start_command(a2l); + a2l->argv = NULL; /* it's not used after start_command; avoid dangling pointers */ if (start_command_status != 0) { - pr_warning("could not start addr2line for %s: start_command return code %d\n", - path, - start_command_status); - goto out; - } - - a2l->to_child = fdopen(a2l->addr2line.in, "w"); - if (a2l->to_child == NULL) { - pr_warning("could not open write-stream to addr2line of %s\n", path); - goto out; - } - - a2l->from_child = fdopen(a2l->addr2line.out, "r"); - if (a2l->from_child == NULL) { - pr_warning("could not open read-stream from addr2line of %s\n", path); - goto out; + pr_warning("could not start addr2line (%s) for %s: start_command return code %d\n", + addr2line_path, binary_path, start_command_status); + addr2line_subprocess_cleanup(a2l); + return NULL; } return a2l; +} -out: - if (a2l) - addr2line_subprocess_cleanup(a2l); +enum a2l_style { + BROKEN, + GNU_BINUTILS, + LLVM, +}; - return NULL; +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'); + } + /* Ignore SIGPIPE in the event addr2line exits. */ + signal(SIGPIPE, SIG_IGN); + } + return style; } -static int read_addr2line_record(struct a2l_subprocess *a2l, +static int read_addr2line_record(struct io *io, + enum a2l_style style, char **function, char **filename, unsigned int *line_nr) @@ -488,19 +505,25 @@ static int read_addr2line_record(struct a2l_subprocess *a2l, if (line_nr != NULL) *line_nr = 0; - if (getline(&line, &line_len, a2l->from_child) < 0 || !line_len) + 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)); zfree(&line); line_len = 0; - if (getline(&line, &line_len, a2l->from_child) < 0 || !line_len) + 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; } @@ -541,19 +564,25 @@ static int addr2line(const char *dso_name, u64 addr, struct inline_node *node, struct symbol *sym __maybe_unused) { - struct a2l_subprocess *a2l = dso->a2l; + struct child_process *a2l = dso->a2l; char *record_function = NULL; char *record_filename = NULL; unsigned int record_line_nr = 0; int record_status = -1; int ret = 0; size_t inline_count = 0; + int len; + char buf[128]; + ssize_t written; + struct io io; + enum a2l_style a2l_style; if (!a2l) { if (!filename__has_section(dso_name, ".debug_line")) goto out; - dso->a2l = addr2line_subprocess_init(dso_name); + dso->a2l = addr2line_subprocess_init(symbol_conf.addr2line_path, + dso_name); a2l = dso->a2l; } @@ -562,23 +591,34 @@ 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. */ - if (fprintf(a2l->to_child, "%016"PRIx64"\n,\n", addr) < 0 || fflush(a2l->to_child) != 0) { + len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr); + written = len > 0 ? write(a2l->in, buf, len) : -1; + if (written != len) { if (!symbol_conf.disable_add2line_warn) pr_warning("%s %s: could not send request\n", __func__, dso_name); goto out; } + io__init(&io, a2l->out, buf, sizeof(buf)); - switch (read_addr2line_record(a2l, &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); @@ -588,7 +628,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(a2l, 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", @@ -626,7 +666,8 @@ 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(a2l, + while ((record_status = read_addr2line_record(&io, + a2l_style, &record_function, &record_filename, &record_line_nr)) == 1) { @@ -650,7 +691,7 @@ out: void dso__free_a2l(struct dso *dso) { - struct a2l_subprocess *a2l = dso->a2l; + struct child_process *a2l = dso->a2l; if (!a2l) return; |