Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf srcline: Fallback between addr2line implementations

Factor the addr2line function implementation into separate source files
(addr2line.[ch]) and rename the addr2line function cmd__addr2line. In
srcline replace the ifdef-ed addr2line implementations with one that
first tries the llvm__addr2line implementation, then the deprecated
libbfd__addr2line function and on failure uses cmd__addr2line.

If HAVE_LIBLLVM_SUPPORT is enabled the llvm__addr2line will execute
against the libLLVM.so it is linked against.

If HAVE_LIBLLVM_DYNAMIC is enabled then libperf-llvm.so (that links
against libLLVM.so) will be dlopened. If the dlopen succeeds then the
behavior should match HAVE_LIBLLVM_SUPPORT. On failure cmd__addr2line is
used. The dlopen is only tried once.

If HAVE_LIBLLVM_DYNAMIC isn't enabled then llvm__addr2line immediately
fails and cmd__addr2line is used.

Clean up the dso__free_a2l logic, which is only needed in the non-LLVM
version and moved to addr2line.c.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Ghiti <alexghiti@rivosinc.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Bill Wendling <morbo@google.com>
Cc: Charlie Jenkins <charlie@rivosinc.com>
Cc: Collin Funk <collin.funk1@gmail.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Haibo Xu <haibo1.xu@intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Justin Stitt <justinstitt@google.com>
Cc: Li Huafei <lihuafei1@huawei.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Nick Desaulniers <nick.desaulniers+lkml@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Song Liu <song@kernel.org>
Cc: Stephen Brennan <stephen.s.brennan@oracle.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
257046a3 fa770f1a

+488 -484
+1
tools/perf/util/Build
··· 2 2 include $(srctree)/tools/scripts/utilities.mak 3 3 4 4 perf-util-y += arm64-frame-pointer-unwind-support.o 5 + perf-util-y += addr2line.o 5 6 perf-util-y += addr_location.o 6 7 perf-util-y += annotate.o 7 8 perf-util-y += block-info.o
+439
tools/perf/util/addr2line.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include "addr2line.h" 3 + #include "debug.h" 4 + #include "dso.h" 5 + #include "string2.h" 6 + #include "srcline.h" 7 + #include "symbol.h" 8 + #include "symbol_conf.h" 9 + 10 + #include <api/io.h> 11 + #include <linux/zalloc.h> 12 + #include <subcmd/run-command.h> 13 + 14 + #include <inttypes.h> 15 + #include <signal.h> 16 + #include <stdlib.h> 17 + #include <string.h> 18 + 19 + #define MAX_INLINE_NEST 1024 20 + 21 + /* If addr2line doesn't return data for 1 second then timeout. */ 22 + int addr2line_timeout_ms = 1 * 1000; 23 + 24 + static int filename_split(char *filename, unsigned int *line_nr) 25 + { 26 + char *sep; 27 + 28 + sep = strchr(filename, '\n'); 29 + if (sep) 30 + *sep = '\0'; 31 + 32 + if (!strcmp(filename, "??:0")) 33 + return 0; 34 + 35 + sep = strchr(filename, ':'); 36 + if (sep) { 37 + *sep++ = '\0'; 38 + *line_nr = strtoul(sep, NULL, 0); 39 + return 1; 40 + } 41 + pr_debug("addr2line missing ':' in filename split\n"); 42 + return 0; 43 + } 44 + 45 + static void addr2line_subprocess_cleanup(struct child_process *a2l) 46 + { 47 + if (a2l->pid != -1) { 48 + kill(a2l->pid, SIGKILL); 49 + finish_command(a2l); /* ignore result, we don't care */ 50 + a2l->pid = -1; 51 + close(a2l->in); 52 + close(a2l->out); 53 + } 54 + 55 + free(a2l); 56 + } 57 + 58 + static struct child_process *addr2line_subprocess_init(const char *addr2line_path, 59 + const char *binary_path) 60 + { 61 + const char *argv[] = { 62 + addr2line_path ?: "addr2line", 63 + "-e", binary_path, 64 + "-a", "-i", "-f", NULL 65 + }; 66 + struct child_process *a2l = zalloc(sizeof(*a2l)); 67 + int start_command_status = 0; 68 + 69 + if (a2l == NULL) { 70 + pr_err("Failed to allocate memory for addr2line"); 71 + return NULL; 72 + } 73 + 74 + a2l->pid = -1; 75 + a2l->in = -1; 76 + a2l->out = -1; 77 + a2l->no_stderr = 1; 78 + 79 + a2l->argv = argv; 80 + start_command_status = start_command(a2l); 81 + a2l->argv = NULL; /* it's not used after start_command; avoid dangling pointers */ 82 + 83 + if (start_command_status != 0) { 84 + pr_warning("could not start addr2line (%s) for %s: start_command return code %d\n", 85 + addr2line_path, binary_path, start_command_status); 86 + addr2line_subprocess_cleanup(a2l); 87 + return NULL; 88 + } 89 + 90 + return a2l; 91 + } 92 + 93 + enum a2l_style { 94 + BROKEN, 95 + GNU_BINUTILS, 96 + LLVM, 97 + }; 98 + 99 + static enum a2l_style addr2line_configure(struct child_process *a2l, const char *dso_name) 100 + { 101 + static bool cached; 102 + static enum a2l_style style; 103 + 104 + if (!cached) { 105 + char buf[128]; 106 + struct io io; 107 + int ch; 108 + int lines; 109 + 110 + if (write(a2l->in, ",\n", 2) != 2) 111 + return BROKEN; 112 + 113 + io__init(&io, a2l->out, buf, sizeof(buf)); 114 + ch = io__get_char(&io); 115 + if (ch == ',') { 116 + style = LLVM; 117 + cached = true; 118 + lines = 1; 119 + pr_debug3("Detected LLVM addr2line style\n"); 120 + } else if (ch == '0') { 121 + style = GNU_BINUTILS; 122 + cached = true; 123 + lines = 3; 124 + pr_debug3("Detected binutils addr2line style\n"); 125 + } else { 126 + if (!symbol_conf.disable_add2line_warn) { 127 + char *output = NULL; 128 + size_t output_len; 129 + 130 + io__getline(&io, &output, &output_len); 131 + pr_warning("%s %s: addr2line configuration failed\n", 132 + __func__, dso_name); 133 + pr_warning("\t%c%s", ch, output); 134 + } 135 + pr_debug("Unknown/broken addr2line style\n"); 136 + return BROKEN; 137 + } 138 + while (lines) { 139 + ch = io__get_char(&io); 140 + if (ch <= 0) 141 + break; 142 + if (ch == '\n') 143 + lines--; 144 + } 145 + /* Ignore SIGPIPE in the event addr2line exits. */ 146 + signal(SIGPIPE, SIG_IGN); 147 + } 148 + return style; 149 + } 150 + 151 + static int read_addr2line_record(struct io *io, 152 + enum a2l_style style, 153 + const char *dso_name, 154 + u64 addr, 155 + bool first, 156 + char **function, 157 + char **filename, 158 + unsigned int *line_nr) 159 + { 160 + /* 161 + * Returns: 162 + * -1 ==> error 163 + * 0 ==> sentinel (or other ill-formed) record read 164 + * 1 ==> a genuine record read 165 + */ 166 + char *line = NULL; 167 + size_t line_len = 0; 168 + unsigned int dummy_line_nr = 0; 169 + int ret = -1; 170 + 171 + if (function != NULL) 172 + zfree(function); 173 + 174 + if (filename != NULL) 175 + zfree(filename); 176 + 177 + if (line_nr != NULL) 178 + *line_nr = 0; 179 + 180 + /* 181 + * Read the first line. Without an error this will be: 182 + * - for the first line an address like 0x1234, 183 + * - the binutils sentinel 0x0000000000000000, 184 + * - the llvm-addr2line the sentinel ',' character, 185 + * - the function name line for an inlined function. 186 + */ 187 + if (io__getline(io, &line, &line_len) < 0 || !line_len) 188 + goto error; 189 + 190 + pr_debug3("%s %s: addr2line read address for sentinel: %s", __func__, dso_name, line); 191 + if (style == LLVM && line_len == 2 && line[0] == ',') { 192 + /* Found the llvm-addr2line sentinel character. */ 193 + zfree(&line); 194 + return 0; 195 + } else if (style == GNU_BINUTILS && (!first || addr != 0)) { 196 + int zero_count = 0, non_zero_count = 0; 197 + /* 198 + * Check for binutils sentinel ignoring it for the case the 199 + * requested address is 0. 200 + */ 201 + 202 + /* A given address should always start 0x. */ 203 + if (line_len >= 2 || line[0] != '0' || line[1] != 'x') { 204 + for (size_t i = 2; i < line_len; i++) { 205 + if (line[i] == '0') 206 + zero_count++; 207 + else if (line[i] != '\n') 208 + non_zero_count++; 209 + } 210 + if (!non_zero_count) { 211 + int ch; 212 + 213 + if (first && !zero_count) { 214 + /* Line was erroneous just '0x'. */ 215 + goto error; 216 + } 217 + /* 218 + * Line was 0x0..0, the sentinel for binutils. Remove 219 + * the function and filename lines. 220 + */ 221 + zfree(&line); 222 + do { 223 + ch = io__get_char(io); 224 + } while (ch > 0 && ch != '\n'); 225 + do { 226 + ch = io__get_char(io); 227 + } while (ch > 0 && ch != '\n'); 228 + return 0; 229 + } 230 + } 231 + } 232 + /* Read the second function name line (if inline data then this is the first line). */ 233 + if (first && (io__getline(io, &line, &line_len) < 0 || !line_len)) 234 + goto error; 235 + 236 + pr_debug3("%s %s: addr2line read line: %s", __func__, dso_name, line); 237 + if (function != NULL) 238 + *function = strdup(strim(line)); 239 + 240 + zfree(&line); 241 + line_len = 0; 242 + 243 + /* Read the third filename and line number line. */ 244 + if (io__getline(io, &line, &line_len) < 0 || !line_len) 245 + goto error; 246 + 247 + pr_debug3("%s %s: addr2line filename:number : %s", __func__, dso_name, line); 248 + if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0 && 249 + style == GNU_BINUTILS) { 250 + ret = 0; 251 + goto error; 252 + } 253 + 254 + if (filename != NULL) 255 + *filename = strdup(line); 256 + 257 + zfree(&line); 258 + line_len = 0; 259 + 260 + return 1; 261 + 262 + error: 263 + free(line); 264 + if (function != NULL) 265 + zfree(function); 266 + if (filename != NULL) 267 + zfree(filename); 268 + return ret; 269 + } 270 + 271 + static int inline_list__append_record(struct dso *dso, 272 + struct inline_node *node, 273 + struct symbol *sym, 274 + const char *function, 275 + const char *filename, 276 + unsigned int line_nr) 277 + { 278 + struct symbol *inline_sym = new_inline_sym(dso, sym, function); 279 + 280 + return inline_list__append(inline_sym, srcline_from_fileline(filename, line_nr), node); 281 + } 282 + 283 + int cmd__addr2line(const char *dso_name, u64 addr, 284 + char **file, unsigned int *line_nr, 285 + struct dso *dso, 286 + bool unwind_inlines, 287 + struct inline_node *node, 288 + struct symbol *sym __maybe_unused) 289 + { 290 + struct child_process *a2l = dso__a2l(dso); 291 + char *record_function = NULL; 292 + char *record_filename = NULL; 293 + unsigned int record_line_nr = 0; 294 + int record_status = -1; 295 + int ret = 0; 296 + size_t inline_count = 0; 297 + int len; 298 + char buf[128]; 299 + ssize_t written; 300 + struct io io = { .eof = false }; 301 + enum a2l_style a2l_style; 302 + 303 + if (!a2l) { 304 + if (!filename__has_section(dso_name, ".debug_line")) 305 + goto out; 306 + 307 + dso__set_a2l(dso, 308 + addr2line_subprocess_init(symbol_conf.addr2line_path, dso_name)); 309 + a2l = dso__a2l(dso); 310 + } 311 + 312 + if (a2l == NULL) { 313 + if (!symbol_conf.disable_add2line_warn) 314 + pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name); 315 + goto out; 316 + } 317 + a2l_style = addr2line_configure(a2l, dso_name); 318 + if (a2l_style == BROKEN) 319 + goto out; 320 + 321 + /* 322 + * Send our request and then *deliberately* send something that can't be 323 + * interpreted as a valid address to ask addr2line about (namely, 324 + * ","). This causes addr2line to first write out the answer to our 325 + * request, in an unbounded/unknown number of records, and then to write 326 + * out the lines "0x0...0", "??" and "??:0", for GNU binutils, or "," 327 + * for llvm-addr2line, so that we can detect when it has finished giving 328 + * us anything useful. 329 + */ 330 + len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr); 331 + written = len > 0 ? write(a2l->in, buf, len) : -1; 332 + if (written != len) { 333 + if (!symbol_conf.disable_add2line_warn) 334 + pr_warning("%s %s: could not send request\n", __func__, dso_name); 335 + goto out; 336 + } 337 + io__init(&io, a2l->out, buf, sizeof(buf)); 338 + io.timeout_ms = addr2line_timeout_ms; 339 + switch (read_addr2line_record(&io, a2l_style, dso_name, addr, /*first=*/true, 340 + &record_function, &record_filename, &record_line_nr)) { 341 + case -1: 342 + if (!symbol_conf.disable_add2line_warn) 343 + pr_warning("%s %s: could not read first record\n", __func__, dso_name); 344 + goto out; 345 + case 0: 346 + /* 347 + * The first record was invalid, so return failure, but first 348 + * read another record, since we sent a sentinel ',' for the 349 + * sake of detected the last inlined function. Treat this as the 350 + * first of a record as the ',' generates a new start with GNU 351 + * binutils, also force a non-zero address as we're no longer 352 + * reading that record. 353 + */ 354 + switch (read_addr2line_record(&io, a2l_style, dso_name, 355 + /*addr=*/1, /*first=*/true, 356 + NULL, NULL, NULL)) { 357 + case -1: 358 + if (!symbol_conf.disable_add2line_warn) 359 + pr_warning("%s %s: could not read sentinel record\n", 360 + __func__, dso_name); 361 + break; 362 + case 0: 363 + /* The sentinel as expected. */ 364 + break; 365 + default: 366 + if (!symbol_conf.disable_add2line_warn) 367 + pr_warning("%s %s: unexpected record instead of sentinel", 368 + __func__, dso_name); 369 + break; 370 + } 371 + goto out; 372 + default: 373 + /* First record as expected. */ 374 + break; 375 + } 376 + 377 + if (file) { 378 + *file = strdup(record_filename); 379 + ret = 1; 380 + } 381 + if (line_nr) 382 + *line_nr = record_line_nr; 383 + 384 + if (unwind_inlines) { 385 + if (node && inline_list__append_record(dso, node, sym, 386 + record_function, 387 + record_filename, 388 + record_line_nr)) { 389 + ret = 0; 390 + goto out; 391 + } 392 + } 393 + 394 + /* 395 + * We have to read the records even if we don't care about the inline 396 + * info. This isn't the first record and force the address to non-zero 397 + * as we're reading records beyond the first. 398 + */ 399 + while ((record_status = read_addr2line_record(&io, 400 + a2l_style, 401 + dso_name, 402 + /*addr=*/1, 403 + /*first=*/false, 404 + &record_function, 405 + &record_filename, 406 + &record_line_nr)) == 1) { 407 + if (unwind_inlines && node && inline_count++ < MAX_INLINE_NEST) { 408 + if (inline_list__append_record(dso, node, sym, 409 + record_function, 410 + record_filename, 411 + record_line_nr)) { 412 + ret = 0; 413 + goto out; 414 + } 415 + ret = 1; /* found at least one inline frame */ 416 + } 417 + } 418 + 419 + out: 420 + free(record_function); 421 + free(record_filename); 422 + if (io.eof) { 423 + dso__set_a2l(dso, NULL); 424 + addr2line_subprocess_cleanup(a2l); 425 + } 426 + return ret; 427 + } 428 + 429 + void dso__free_a2l(struct dso *dso) 430 + { 431 + struct child_process *a2l = dso__a2l(dso); 432 + 433 + if (!a2l) 434 + return; 435 + 436 + addr2line_subprocess_cleanup(a2l); 437 + 438 + dso__set_a2l(dso, NULL); 439 + }
+20
tools/perf/util/addr2line.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef __PERF_ADDR2LINE_H 3 + #define __PERF_ADDR2LINE_H 4 + 5 + #include <linux/types.h> 6 + 7 + struct dso; 8 + struct inline_node; 9 + struct symbol; 10 + 11 + extern int addr2line_timeout_ms; 12 + 13 + int cmd__addr2line(const char *dso_name, u64 addr, 14 + char **file, unsigned int *line_nr, 15 + struct dso *dso, 16 + bool unwind_inlines, 17 + struct inline_node *node, 18 + struct symbol *sym); 19 + 20 + #endif /* __PERF_ADDR2LINE_H */
+1 -1
tools/perf/util/config.c
··· 19 19 #include "util/hist.h" /* perf_hist_config */ 20 20 #include "util/stat.h" /* perf_stat__set_big_num */ 21 21 #include "util/evsel.h" /* evsel__hw_names, evsel__use_bpf_counters */ 22 - #include "util/srcline.h" /* addr2line_timeout_ms */ 22 + #include "util/addr2line.h" /* addr2line_timeout_ms */ 23 23 #include "build-id.h" 24 24 #include "debug.h" 25 25 #include "config.h"
-5
tools/perf/util/llvm.c
··· 70 70 #endif 71 71 } 72 72 73 - void dso__free_a2l_llvm(struct dso *dso __maybe_unused) 74 - { 75 - /* Nothing to free. */ 76 - } 77 - 78 73 #ifdef HAVE_LIBLLVM_SUPPORT 79 74 static void init_llvm(void) 80 75 {
-3
tools/perf/util/llvm.h
··· 15 15 bool unwind_inlines, struct inline_node *node, 16 16 struct symbol *sym); 17 17 18 - 19 - void dso__free_a2l_llvm(struct dso *dso); 20 - 21 18 int symbol__disassemble_llvm(const char *filename, struct symbol *sym, 22 19 struct annotate_args *args); 23 20
+27 -474
tools/perf/util/srcline.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 - #include <inttypes.h> 3 - #include <signal.h> 4 - #include <stdio.h> 5 - #include <stdlib.h> 6 - #include <string.h> 7 - #include <sys/types.h> 8 - 9 - #include <linux/compiler.h> 10 - #include <linux/kernel.h> 11 - #include <linux/string.h> 12 - #include <linux/zalloc.h> 13 - 14 - #include <api/io.h> 15 - 16 - #include "util/dso.h" 17 - #include "util/debug.h" 18 - #include "util/callchain.h" 19 - #include "util/symbol_conf.h" 20 - #include "llvm.h" 21 2 #include "srcline.h" 22 - #include "string2.h" 3 + #include "addr2line.h" 4 + #include "dso.h" 5 + #include "callchain.h" 6 + #include "libbfd.h" 7 + #include "llvm.h" 23 8 #include "symbol.h" 24 - #include "subcmd/run-command.h" 25 9 26 - /* If addr2line doesn't return data for 1 second then timeout. */ 27 - int addr2line_timeout_ms = 1 * 1000; 10 + #include <inttypes.h> 11 + #include <string.h> 12 + 28 13 bool srcline_full_filename; 29 14 30 15 char *srcline__unknown = (char *)"??:0"; ··· 114 129 return inline_sym; 115 130 } 116 131 117 - #ifdef HAVE_LIBLLVM_SUPPORT 118 - #include "llvm.h" 119 - 120 - static int addr2line(const char *dso_name, u64 addr, 121 - char **file, unsigned int *line, struct dso *dso, 122 - bool unwind_inlines, struct inline_node *node, 123 - struct symbol *sym) 124 - { 125 - return llvm__addr2line(dso_name, addr, file, line, dso, unwind_inlines, node, sym); 126 - } 127 - 128 - void dso__free_a2l(struct dso *dso) 129 - { 130 - dso__free_a2l_llvm(dso); 131 - } 132 - #elif defined(HAVE_LIBBFD_SUPPORT) 133 - #include "libbfd.h" 134 - 135 - static int addr2line(const char *dso_name, u64 addr, 136 - char **file, unsigned int *line, struct dso *dso, 137 - bool unwind_inlines, struct inline_node *node, 132 + static int addr2line(const char *dso_name, u64 addr, char **file, unsigned int *line_nr, 133 + struct dso *dso, bool unwind_inlines, struct inline_node *node, 138 134 struct symbol *sym) 139 135 { 140 - return libbfd__addr2line(dso_name, addr, file, line, dso, unwind_inlines, node, sym); 136 + int ret; 137 + 138 + ret = llvm__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym); 139 + if (ret > 0) 140 + return ret; 141 + 142 + ret = libbfd__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym); 143 + if (ret > 0) 144 + return ret; 145 + 146 + return cmd__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym); 141 147 } 142 - 143 - void dso__free_a2l(struct dso *dso) 144 - { 145 - dso__free_a2l_libbfd(dso); 146 - } 147 - 148 - #else /* HAVE_LIBBFD_SUPPORT */ 149 - 150 - static int filename_split(char *filename, unsigned int *line_nr) 151 - { 152 - char *sep; 153 - 154 - sep = strchr(filename, '\n'); 155 - if (sep) 156 - *sep = '\0'; 157 - 158 - if (!strcmp(filename, "??:0")) 159 - return 0; 160 - 161 - sep = strchr(filename, ':'); 162 - if (sep) { 163 - *sep++ = '\0'; 164 - *line_nr = strtoul(sep, NULL, 0); 165 - return 1; 166 - } 167 - pr_debug("addr2line missing ':' in filename split\n"); 168 - return 0; 169 - } 170 - 171 - static void addr2line_subprocess_cleanup(struct child_process *a2l) 172 - { 173 - if (a2l->pid != -1) { 174 - kill(a2l->pid, SIGKILL); 175 - finish_command(a2l); /* ignore result, we don't care */ 176 - a2l->pid = -1; 177 - close(a2l->in); 178 - close(a2l->out); 179 - } 180 - 181 - free(a2l); 182 - } 183 - 184 - static struct child_process *addr2line_subprocess_init(const char *addr2line_path, 185 - const char *binary_path) 186 - { 187 - const char *argv[] = { 188 - addr2line_path ?: "addr2line", 189 - "-e", binary_path, 190 - "-a", "-i", "-f", NULL 191 - }; 192 - struct child_process *a2l = zalloc(sizeof(*a2l)); 193 - int start_command_status = 0; 194 - 195 - if (a2l == NULL) { 196 - pr_err("Failed to allocate memory for addr2line"); 197 - return NULL; 198 - } 199 - 200 - a2l->pid = -1; 201 - a2l->in = -1; 202 - a2l->out = -1; 203 - a2l->no_stderr = 1; 204 - 205 - a2l->argv = argv; 206 - start_command_status = start_command(a2l); 207 - a2l->argv = NULL; /* it's not used after start_command; avoid dangling pointers */ 208 - 209 - if (start_command_status != 0) { 210 - pr_warning("could not start addr2line (%s) for %s: start_command return code %d\n", 211 - addr2line_path, binary_path, start_command_status); 212 - addr2line_subprocess_cleanup(a2l); 213 - return NULL; 214 - } 215 - 216 - return a2l; 217 - } 218 - 219 - enum a2l_style { 220 - BROKEN, 221 - GNU_BINUTILS, 222 - LLVM, 223 - }; 224 - 225 - static enum a2l_style addr2line_configure(struct child_process *a2l, const char *dso_name) 226 - { 227 - static bool cached; 228 - static enum a2l_style style; 229 - 230 - if (!cached) { 231 - char buf[128]; 232 - struct io io; 233 - int ch; 234 - int lines; 235 - 236 - if (write(a2l->in, ",\n", 2) != 2) 237 - return BROKEN; 238 - 239 - io__init(&io, a2l->out, buf, sizeof(buf)); 240 - ch = io__get_char(&io); 241 - if (ch == ',') { 242 - style = LLVM; 243 - cached = true; 244 - lines = 1; 245 - pr_debug3("Detected LLVM addr2line style\n"); 246 - } else if (ch == '0') { 247 - style = GNU_BINUTILS; 248 - cached = true; 249 - lines = 3; 250 - pr_debug3("Detected binutils addr2line style\n"); 251 - } else { 252 - if (!symbol_conf.disable_add2line_warn) { 253 - char *output = NULL; 254 - size_t output_len; 255 - 256 - io__getline(&io, &output, &output_len); 257 - pr_warning("%s %s: addr2line configuration failed\n", 258 - __func__, dso_name); 259 - pr_warning("\t%c%s", ch, output); 260 - } 261 - pr_debug("Unknown/broken addr2line style\n"); 262 - return BROKEN; 263 - } 264 - while (lines) { 265 - ch = io__get_char(&io); 266 - if (ch <= 0) 267 - break; 268 - if (ch == '\n') 269 - lines--; 270 - } 271 - /* Ignore SIGPIPE in the event addr2line exits. */ 272 - signal(SIGPIPE, SIG_IGN); 273 - } 274 - return style; 275 - } 276 - 277 - static int read_addr2line_record(struct io *io, 278 - enum a2l_style style, 279 - const char *dso_name, 280 - u64 addr, 281 - bool first, 282 - char **function, 283 - char **filename, 284 - unsigned int *line_nr) 285 - { 286 - /* 287 - * Returns: 288 - * -1 ==> error 289 - * 0 ==> sentinel (or other ill-formed) record read 290 - * 1 ==> a genuine record read 291 - */ 292 - char *line = NULL; 293 - size_t line_len = 0; 294 - unsigned int dummy_line_nr = 0; 295 - int ret = -1; 296 - 297 - if (function != NULL) 298 - zfree(function); 299 - 300 - if (filename != NULL) 301 - zfree(filename); 302 - 303 - if (line_nr != NULL) 304 - *line_nr = 0; 305 - 306 - /* 307 - * Read the first line. Without an error this will be: 308 - * - for the first line an address like 0x1234, 309 - * - the binutils sentinel 0x0000000000000000, 310 - * - the llvm-addr2line the sentinel ',' character, 311 - * - the function name line for an inlined function. 312 - */ 313 - if (io__getline(io, &line, &line_len) < 0 || !line_len) 314 - goto error; 315 - 316 - pr_debug3("%s %s: addr2line read address for sentinel: %s", __func__, dso_name, line); 317 - if (style == LLVM && line_len == 2 && line[0] == ',') { 318 - /* Found the llvm-addr2line sentinel character. */ 319 - zfree(&line); 320 - return 0; 321 - } else if (style == GNU_BINUTILS && (!first || addr != 0)) { 322 - int zero_count = 0, non_zero_count = 0; 323 - /* 324 - * Check for binutils sentinel ignoring it for the case the 325 - * requested address is 0. 326 - */ 327 - 328 - /* A given address should always start 0x. */ 329 - if (line_len >= 2 || line[0] != '0' || line[1] != 'x') { 330 - for (size_t i = 2; i < line_len; i++) { 331 - if (line[i] == '0') 332 - zero_count++; 333 - else if (line[i] != '\n') 334 - non_zero_count++; 335 - } 336 - if (!non_zero_count) { 337 - int ch; 338 - 339 - if (first && !zero_count) { 340 - /* Line was erroneous just '0x'. */ 341 - goto error; 342 - } 343 - /* 344 - * Line was 0x0..0, the sentinel for binutils. Remove 345 - * the function and filename lines. 346 - */ 347 - zfree(&line); 348 - do { 349 - ch = io__get_char(io); 350 - } while (ch > 0 && ch != '\n'); 351 - do { 352 - ch = io__get_char(io); 353 - } while (ch > 0 && ch != '\n'); 354 - return 0; 355 - } 356 - } 357 - } 358 - /* Read the second function name line (if inline data then this is the first line). */ 359 - if (first && (io__getline(io, &line, &line_len) < 0 || !line_len)) 360 - goto error; 361 - 362 - pr_debug3("%s %s: addr2line read line: %s", __func__, dso_name, line); 363 - if (function != NULL) 364 - *function = strdup(strim(line)); 365 - 366 - zfree(&line); 367 - line_len = 0; 368 - 369 - /* Read the third filename and line number line. */ 370 - if (io__getline(io, &line, &line_len) < 0 || !line_len) 371 - goto error; 372 - 373 - pr_debug3("%s %s: addr2line filename:number : %s", __func__, dso_name, line); 374 - if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0 && 375 - style == GNU_BINUTILS) { 376 - ret = 0; 377 - goto error; 378 - } 379 - 380 - if (filename != NULL) 381 - *filename = strdup(line); 382 - 383 - zfree(&line); 384 - line_len = 0; 385 - 386 - return 1; 387 - 388 - error: 389 - free(line); 390 - if (function != NULL) 391 - zfree(function); 392 - if (filename != NULL) 393 - zfree(filename); 394 - return ret; 395 - } 396 - 397 - static int inline_list__append_record(struct dso *dso, 398 - struct inline_node *node, 399 - struct symbol *sym, 400 - const char *function, 401 - const char *filename, 402 - unsigned int line_nr) 403 - { 404 - struct symbol *inline_sym = new_inline_sym(dso, sym, function); 405 - 406 - return inline_list__append(inline_sym, srcline_from_fileline(filename, line_nr), node); 407 - } 408 - 409 - static int addr2line(const char *dso_name, u64 addr, 410 - char **file, unsigned int *line_nr, 411 - struct dso *dso, 412 - bool unwind_inlines, 413 - struct inline_node *node, 414 - struct symbol *sym __maybe_unused) 415 - { 416 - struct child_process *a2l = dso__a2l(dso); 417 - char *record_function = NULL; 418 - char *record_filename = NULL; 419 - unsigned int record_line_nr = 0; 420 - int record_status = -1; 421 - int ret = 0; 422 - size_t inline_count = 0; 423 - int len; 424 - char buf[128]; 425 - ssize_t written; 426 - struct io io = { .eof = false }; 427 - enum a2l_style a2l_style; 428 - 429 - if (!a2l) { 430 - if (!filename__has_section(dso_name, ".debug_line")) 431 - goto out; 432 - 433 - dso__set_a2l(dso, 434 - addr2line_subprocess_init(symbol_conf.addr2line_path, dso_name)); 435 - a2l = dso__a2l(dso); 436 - } 437 - 438 - if (a2l == NULL) { 439 - if (!symbol_conf.disable_add2line_warn) 440 - pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name); 441 - goto out; 442 - } 443 - a2l_style = addr2line_configure(a2l, dso_name); 444 - if (a2l_style == BROKEN) 445 - goto out; 446 - 447 - /* 448 - * Send our request and then *deliberately* send something that can't be 449 - * interpreted as a valid address to ask addr2line about (namely, 450 - * ","). This causes addr2line to first write out the answer to our 451 - * request, in an unbounded/unknown number of records, and then to write 452 - * out the lines "0x0...0", "??" and "??:0", for GNU binutils, or "," 453 - * for llvm-addr2line, so that we can detect when it has finished giving 454 - * us anything useful. 455 - */ 456 - len = snprintf(buf, sizeof(buf), "%016"PRIx64"\n,\n", addr); 457 - written = len > 0 ? write(a2l->in, buf, len) : -1; 458 - if (written != len) { 459 - if (!symbol_conf.disable_add2line_warn) 460 - pr_warning("%s %s: could not send request\n", __func__, dso_name); 461 - goto out; 462 - } 463 - io__init(&io, a2l->out, buf, sizeof(buf)); 464 - io.timeout_ms = addr2line_timeout_ms; 465 - switch (read_addr2line_record(&io, a2l_style, dso_name, addr, /*first=*/true, 466 - &record_function, &record_filename, &record_line_nr)) { 467 - case -1: 468 - if (!symbol_conf.disable_add2line_warn) 469 - pr_warning("%s %s: could not read first record\n", __func__, dso_name); 470 - goto out; 471 - case 0: 472 - /* 473 - * The first record was invalid, so return failure, but first 474 - * read another record, since we sent a sentinel ',' for the 475 - * sake of detected the last inlined function. Treat this as the 476 - * first of a record as the ',' generates a new start with GNU 477 - * binutils, also force a non-zero address as we're no longer 478 - * reading that record. 479 - */ 480 - switch (read_addr2line_record(&io, a2l_style, dso_name, 481 - /*addr=*/1, /*first=*/true, 482 - NULL, NULL, NULL)) { 483 - case -1: 484 - if (!symbol_conf.disable_add2line_warn) 485 - pr_warning("%s %s: could not read sentinel record\n", 486 - __func__, dso_name); 487 - break; 488 - case 0: 489 - /* The sentinel as expected. */ 490 - break; 491 - default: 492 - if (!symbol_conf.disable_add2line_warn) 493 - pr_warning("%s %s: unexpected record instead of sentinel", 494 - __func__, dso_name); 495 - break; 496 - } 497 - goto out; 498 - default: 499 - /* First record as expected. */ 500 - break; 501 - } 502 - 503 - if (file) { 504 - *file = strdup(record_filename); 505 - ret = 1; 506 - } 507 - if (line_nr) 508 - *line_nr = record_line_nr; 509 - 510 - if (unwind_inlines) { 511 - if (node && inline_list__append_record(dso, node, sym, 512 - record_function, 513 - record_filename, 514 - record_line_nr)) { 515 - ret = 0; 516 - goto out; 517 - } 518 - } 519 - 520 - /* 521 - * We have to read the records even if we don't care about the inline 522 - * info. This isn't the first record and force the address to non-zero 523 - * as we're reading records beyond the first. 524 - */ 525 - while ((record_status = read_addr2line_record(&io, 526 - a2l_style, 527 - dso_name, 528 - /*addr=*/1, 529 - /*first=*/false, 530 - &record_function, 531 - &record_filename, 532 - &record_line_nr)) == 1) { 533 - if (unwind_inlines && node && inline_count++ < MAX_INLINE_NEST) { 534 - if (inline_list__append_record(dso, node, sym, 535 - record_function, 536 - record_filename, 537 - record_line_nr)) { 538 - ret = 0; 539 - goto out; 540 - } 541 - ret = 1; /* found at least one inline frame */ 542 - } 543 - } 544 - 545 - out: 546 - free(record_function); 547 - free(record_filename); 548 - if (io.eof) { 549 - dso__set_a2l(dso, NULL); 550 - addr2line_subprocess_cleanup(a2l); 551 - } 552 - return ret; 553 - } 554 - 555 - void dso__free_a2l(struct dso *dso) 556 - { 557 - struct child_process *a2l = dso__a2l(dso); 558 - 559 - if (!a2l) 560 - return; 561 - 562 - addr2line_subprocess_cleanup(a2l); 563 - 564 - dso__set_a2l(dso, NULL); 565 - } 566 - 567 - #endif /* HAVE_LIBBFD_SUPPORT */ 568 148 569 149 static struct inline_node *addr2inlines(const char *dso_name, u64 addr, 570 150 struct dso *dso, struct symbol *sym) ··· 145 595 INIT_LIST_HEAD(&node->val); 146 596 node->addr = addr; 147 597 148 - addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym); 598 + addr2line(dso_name, addr, /*file=*/NULL, /*line_nr=*/NULL, dso, 599 + /*unwind_inlines=*/true, node, sym); 600 + 149 601 return node; 150 602 } 151 603 ··· 174 622 goto out_err; 175 623 176 624 if (!addr2line(dso_name, addr, &file, &line, dso, 177 - unwind_inlines, NULL, sym)) 625 + unwind_inlines, /*node=*/NULL, sym)) 178 626 goto out_err; 179 627 180 628 srcline = srcline_from_fileline(file, line); ··· 220 668 if (dso_name == NULL) 221 669 goto out_err; 222 670 223 - if (!addr2line(dso_name, addr, &file, line, dso, true, NULL, NULL)) 671 + if (!addr2line(dso_name, addr, &file, line, dso, /*unwind_inlines=*/true, 672 + /*node=*/NULL, /*sym=*/NULL)) 224 673 goto out_err; 225 674 226 675 dso__set_a2l_fails(dso, 0);
-1
tools/perf/util/srcline.h
··· 9 9 struct dso; 10 10 struct symbol; 11 11 12 - extern int addr2line_timeout_ms; 13 12 extern bool srcline_full_filename; 14 13 char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, 15 14 bool show_sym, bool show_addr, u64 ip);