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

perf srcline: Use long-running addr2line per DSO

Invoking addr2line in a separate subprocess, one for each required
lookup, takes a terribly long time.

This patch introduces a long-running addr2line process for each DSO,
*DRAMATICALLY* speeding up runs of perf.

What used to take tens of minutes now takes tens of seconds.

Debian bug report about this issue:

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=911815

Signed-off-by: Tony Garnock-Jones <tonyg@leastfixedpoint.com>
Tested-by: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20210916120939.453536-1-tonyg@leastfixedpoint.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Tony Garnock-Jones and committed by
Arnaldo Carvalho de Melo
be8ecc57 2b775152

+258 -96
+258 -96
tools/perf/util/srcline.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 #include <inttypes.h> 3 + #include <signal.h> 3 4 #include <stdio.h> 4 5 #include <stdlib.h> 5 6 #include <string.h> 7 + #include <sys/types.h> 6 8 7 9 #include <linux/kernel.h> 8 10 #include <linux/string.h> ··· 17 15 #include "srcline.h" 18 16 #include "string2.h" 19 17 #include "symbol.h" 18 + #include "subcmd/run-command.h" 20 19 21 20 bool srcline_full_filename; 22 21 ··· 121 118 122 119 return inline_sym; 123 120 } 121 + 122 + #define MAX_INLINE_NEST 1024 124 123 125 124 #ifdef HAVE_LIBBFD_SUPPORT 126 125 ··· 278 273 free(a2l); 279 274 } 280 275 281 - #define MAX_INLINE_NEST 1024 282 - 283 276 static int inline_list__append_dso_a2l(struct dso *dso, 284 277 struct inline_node *node, 285 278 struct symbol *sym) ··· 364 361 dso->a2l = NULL; 365 362 } 366 363 367 - static struct inline_node *addr2inlines(const char *dso_name, u64 addr, 368 - struct dso *dso, struct symbol *sym) 369 - { 370 - struct inline_node *node; 371 - 372 - node = zalloc(sizeof(*node)); 373 - if (node == NULL) { 374 - perror("not enough memory for the inline node"); 375 - return NULL; 376 - } 377 - 378 - INIT_LIST_HEAD(&node->val); 379 - node->addr = addr; 380 - 381 - addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym); 382 - return node; 383 - } 384 - 385 364 #else /* HAVE_LIBBFD_SUPPORT */ 365 + 366 + struct a2l_subprocess { 367 + struct child_process addr2line; 368 + FILE *to_child; 369 + FILE *from_child; 370 + }; 386 371 387 372 static int filename_split(char *filename, unsigned int *line_nr) 388 373 { ··· 393 402 return 0; 394 403 } 395 404 396 - static int addr2line(const char *dso_name, u64 addr, 397 - char **file, unsigned int *line_nr, 398 - struct dso *dso __maybe_unused, 399 - bool unwind_inlines __maybe_unused, 400 - struct inline_node *node __maybe_unused, 401 - struct symbol *sym __maybe_unused) 405 + static void addr2line_subprocess_cleanup(struct a2l_subprocess *a2l) 402 406 { 403 - FILE *fp; 404 - char cmd[PATH_MAX]; 405 - char *filename = NULL; 406 - size_t len; 407 - int ret = 0; 408 - 409 - scnprintf(cmd, sizeof(cmd), "addr2line -e %s %016"PRIx64, 410 - dso_name, addr); 411 - 412 - fp = popen(cmd, "r"); 413 - if (fp == NULL) { 414 - pr_warning("popen failed for %s\n", dso_name); 415 - return 0; 407 + if (a2l->addr2line.pid != -1) { 408 + kill(a2l->addr2line.pid, SIGKILL); 409 + finish_command(&a2l->addr2line); /* ignore result, we don't care */ 410 + a2l->addr2line.pid = -1; 416 411 } 417 412 418 - if (getline(&filename, &len, fp) < 0 || !len) { 419 - pr_warning("addr2line has no output for %s\n", dso_name); 413 + if (a2l->to_child != NULL) { 414 + fclose(a2l->to_child); 415 + a2l->to_child = NULL; 416 + } 417 + 418 + if (a2l->from_child != NULL) { 419 + fclose(a2l->from_child); 420 + a2l->from_child = NULL; 421 + } 422 + 423 + free(a2l); 424 + } 425 + 426 + static struct a2l_subprocess *addr2line_subprocess_init(const char *path) 427 + { 428 + const char *argv[] = { "addr2line", "-e", path, "-i", "-f", NULL }; 429 + struct a2l_subprocess *a2l = zalloc(sizeof(*a2l)); 430 + int start_command_status = 0; 431 + 432 + if (a2l == NULL) 433 + goto out; 434 + 435 + a2l->to_child = NULL; 436 + a2l->from_child = NULL; 437 + 438 + a2l->addr2line.pid = -1; 439 + a2l->addr2line.in = -1; 440 + a2l->addr2line.out = -1; 441 + a2l->addr2line.no_stderr = 1; 442 + 443 + a2l->addr2line.argv = argv; 444 + start_command_status = start_command(&a2l->addr2line); 445 + a2l->addr2line.argv = NULL; /* it's not used after start_command; avoid dangling pointers */ 446 + 447 + if (start_command_status != 0) { 448 + pr_warning("could not start addr2line for %s: start_command return code %d\n", 449 + path, 450 + start_command_status); 420 451 goto out; 421 452 } 422 453 423 - ret = filename_split(filename, line_nr); 424 - if (ret != 1) { 425 - free(filename); 454 + a2l->to_child = fdopen(a2l->addr2line.in, "w"); 455 + if (a2l->to_child == NULL) { 456 + pr_warning("could not open write-stream to addr2line of %s\n", path); 426 457 goto out; 427 458 } 428 459 429 - *file = filename; 460 + a2l->from_child = fdopen(a2l->addr2line.out, "r"); 461 + if (a2l->from_child == NULL) { 462 + pr_warning("could not open read-stream from addr2line of %s\n", path); 463 + goto out; 464 + } 465 + 466 + return a2l; 430 467 431 468 out: 432 - pclose(fp); 469 + if (a2l) 470 + addr2line_subprocess_cleanup(a2l); 471 + 472 + return NULL; 473 + } 474 + 475 + static int read_addr2line_record(struct a2l_subprocess *a2l, 476 + char **function, 477 + char **filename, 478 + unsigned int *line_nr) 479 + { 480 + /* 481 + * Returns: 482 + * -1 ==> error 483 + * 0 ==> sentinel (or other ill-formed) record read 484 + * 1 ==> a genuine record read 485 + */ 486 + char *line = NULL; 487 + size_t line_len = 0; 488 + unsigned int dummy_line_nr = 0; 489 + int ret = -1; 490 + 491 + if (function != NULL) 492 + zfree(function); 493 + 494 + if (filename != NULL) 495 + zfree(filename); 496 + 497 + if (line_nr != NULL) 498 + *line_nr = 0; 499 + 500 + if (getline(&line, &line_len, a2l->from_child) < 0 || !line_len) 501 + goto error; 502 + 503 + if (function != NULL) 504 + *function = strdup(strim(line)); 505 + 506 + zfree(&line); 507 + line_len = 0; 508 + 509 + if (getline(&line, &line_len, a2l->from_child) < 0 || !line_len) 510 + goto error; 511 + 512 + if (filename_split(line, line_nr == NULL ? &dummy_line_nr : line_nr) == 0) { 513 + ret = 0; 514 + goto error; 515 + } 516 + 517 + if (filename != NULL) 518 + *filename = strdup(line); 519 + 520 + zfree(&line); 521 + line_len = 0; 522 + 523 + return 1; 524 + 525 + error: 526 + free(line); 527 + if (function != NULL) 528 + zfree(function); 529 + if (filename != NULL) 530 + zfree(filename); 433 531 return ret; 434 532 } 435 533 436 - void dso__free_a2l(struct dso *dso __maybe_unused) 534 + static int inline_list__append_record(struct dso *dso, 535 + struct inline_node *node, 536 + struct symbol *sym, 537 + const char *function, 538 + const char *filename, 539 + unsigned int line_nr) 437 540 { 541 + struct symbol *inline_sym = new_inline_sym(dso, sym, function); 542 + 543 + return inline_list__append(inline_sym, srcline_from_fileline(filename, line_nr), node); 438 544 } 439 545 440 - static struct inline_node *addr2inlines(const char *dso_name, u64 addr, 441 - struct dso *dso __maybe_unused, 442 - struct symbol *sym) 546 + static int addr2line(const char *dso_name, u64 addr, 547 + char **file, unsigned int *line_nr, 548 + struct dso *dso, 549 + bool unwind_inlines, 550 + struct inline_node *node, 551 + struct symbol *sym __maybe_unused) 443 552 { 444 - FILE *fp; 445 - char cmd[PATH_MAX]; 446 - struct inline_node *node; 447 - char *filename = NULL; 448 - char *funcname = NULL; 449 - size_t filelen, funclen; 450 - unsigned int line_nr = 0; 553 + struct a2l_subprocess *a2l = dso->a2l; 554 + char *record_function = NULL; 555 + char *record_filename = NULL; 556 + unsigned int record_line_nr = 0; 557 + int record_status = -1; 558 + int ret = 0; 559 + size_t inline_count = 0; 451 560 452 - scnprintf(cmd, sizeof(cmd), "addr2line -e %s -i -f %016"PRIx64, 453 - dso_name, addr); 454 - 455 - fp = popen(cmd, "r"); 456 - if (fp == NULL) { 457 - pr_err("popen failed for %s\n", dso_name); 458 - return NULL; 561 + if (!a2l) { 562 + dso->a2l = addr2line_subprocess_init(dso_name); 563 + a2l = dso->a2l; 459 564 } 565 + 566 + if (a2l == NULL) { 567 + if (!symbol_conf.disable_add2line_warn) 568 + pr_warning("%s %s: addr2line_subprocess_init failed\n", __func__, dso_name); 569 + goto out; 570 + } 571 + 572 + /* 573 + * Send our request and then *deliberately* send something that can't be interpreted as 574 + * a valid address to ask addr2line about (namely, ","). This causes addr2line to first 575 + * write out the answer to our request, in an unbounded/unknown number of records, and 576 + * then to write out the lines "??" and "??:0", so that we can detect when it has 577 + * finished giving us anything useful. We have to be careful about the first record, 578 + * though, because it may be genuinely unknown, in which case we'll get two sets of 579 + * "??"/"??:0" lines. 580 + */ 581 + if (fprintf(a2l->to_child, "%016"PRIx64"\n,\n", addr) < 0 || fflush(a2l->to_child) != 0) { 582 + pr_warning("%s %s: could not send request\n", __func__, dso_name); 583 + goto out; 584 + } 585 + 586 + switch (read_addr2line_record(a2l, &record_function, &record_filename, &record_line_nr)) { 587 + case -1: 588 + pr_warning("%s %s: could not read first record\n", __func__, dso_name); 589 + goto out; 590 + case 0: 591 + /* 592 + * The first record was invalid, so return failure, but first read another 593 + * record, since we asked a junk question and have to clear the answer out. 594 + */ 595 + switch (read_addr2line_record(a2l, NULL, NULL, NULL)) { 596 + case -1: 597 + pr_warning("%s %s: could not read delimiter record\n", __func__, dso_name); 598 + break; 599 + case 0: 600 + /* As expected. */ 601 + break; 602 + default: 603 + pr_warning("%s %s: unexpected record instead of sentinel", 604 + __func__, dso_name); 605 + break; 606 + } 607 + goto out; 608 + default: 609 + break; 610 + } 611 + 612 + if (file) { 613 + *file = strdup(record_filename); 614 + ret = 1; 615 + } 616 + if (line_nr) 617 + *line_nr = record_line_nr; 618 + 619 + if (unwind_inlines) { 620 + if (node && inline_list__append_record(dso, node, sym, 621 + record_function, 622 + record_filename, 623 + record_line_nr)) { 624 + ret = 0; 625 + goto out; 626 + } 627 + } 628 + 629 + /* We have to read the records even if we don't care about the inline info. */ 630 + while ((record_status = read_addr2line_record(a2l, 631 + &record_function, 632 + &record_filename, 633 + &record_line_nr)) == 1) { 634 + if (unwind_inlines && node && inline_count++ < MAX_INLINE_NEST) { 635 + if (inline_list__append_record(dso, node, sym, 636 + record_function, 637 + record_filename, 638 + record_line_nr)) { 639 + ret = 0; 640 + goto out; 641 + } 642 + ret = 1; /* found at least one inline frame */ 643 + } 644 + } 645 + 646 + out: 647 + free(record_function); 648 + free(record_filename); 649 + return ret; 650 + } 651 + 652 + void dso__free_a2l(struct dso *dso) 653 + { 654 + struct a2l_subprocess *a2l = dso->a2l; 655 + 656 + if (!a2l) 657 + return; 658 + 659 + addr2line_subprocess_cleanup(a2l); 660 + 661 + dso->a2l = NULL; 662 + } 663 + 664 + #endif /* HAVE_LIBBFD_SUPPORT */ 665 + 666 + static struct inline_node *addr2inlines(const char *dso_name, u64 addr, 667 + struct dso *dso, struct symbol *sym) 668 + { 669 + struct inline_node *node; 460 670 461 671 node = zalloc(sizeof(*node)); 462 672 if (node == NULL) { 463 673 perror("not enough memory for the inline node"); 464 - goto out; 674 + return NULL; 465 675 } 466 676 467 677 INIT_LIST_HEAD(&node->val); 468 678 node->addr = addr; 469 679 470 - /* addr2line -f generates two lines for each inlined functions */ 471 - while (getline(&funcname, &funclen, fp) != -1) { 472 - char *srcline; 473 - struct symbol *inline_sym; 474 - 475 - strim(funcname); 476 - 477 - if (getline(&filename, &filelen, fp) == -1) 478 - goto out; 479 - 480 - if (filename_split(filename, &line_nr) != 1) 481 - goto out; 482 - 483 - srcline = srcline_from_fileline(filename, line_nr); 484 - inline_sym = new_inline_sym(dso, sym, funcname); 485 - 486 - if (inline_list__append(inline_sym, srcline, node) != 0) { 487 - free(srcline); 488 - if (inline_sym && inline_sym->inlined) 489 - symbol__delete(inline_sym); 490 - goto out; 491 - } 492 - } 493 - 494 - out: 495 - pclose(fp); 496 - free(filename); 497 - free(funcname); 498 - 680 + addr2line(dso_name, addr, NULL, NULL, dso, true, node, sym); 499 681 return node; 500 682 } 501 - 502 - #endif /* HAVE_LIBBFD_SUPPORT */ 503 683 504 684 /* 505 685 * Number of addr2line failures (without success) before disabling it for that