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

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

New features:

- Add option in 'perf sched' to merge like comms to lat output (Josef Bacik)

- Improve 'perf probe' error messages when not finding a
suitable vmlinux (Masami Hiramatsu)

Infrastructure changes:

- Use atomic.h for various pre-existing reference counts (Arnaldo Carvalho de Melo)

- Leg work for refcounting 'struct map' (Arnaldo Carvalho de Melo)

- Assign default value for some pointers (Martin Liška)

- Improve setting of gcc debug option (Martin Liška)

- Separate the tests and tools in installation (Nam T. Nguyen)

- Reduce number of arguments of hist_entry_iter__add() (Namhyung Kim)

- DSO data cache fixes (Namhyung Kim)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>

+332 -204
+5 -1
tools/perf/Makefile.perf
··· 464 464 465 465 install-gtk: 466 466 467 - install-bin: all install-gtk 467 + install-tools: all install-gtk 468 468 $(call QUIET_INSTALL, binaries) \ 469 469 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \ 470 470 $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \ ··· 502 502 $(call QUIET_INSTALL, perf_completion-script) \ 503 503 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'; \ 504 504 $(INSTALL) perf-completion.sh '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' 505 + 506 + install-tests: all install-gtk 505 507 $(call QUIET_INSTALL, tests) \ 506 508 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ 507 509 $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests'; \ 508 510 $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr'; \ 509 511 $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' 512 + 513 + install-bin: install-tools install-tests 510 514 511 515 install: install-bin try-install-man install-traceevent-plugins 512 516
+1 -1
tools/perf/arch/common.c
··· 61 61 static bool lookup_path(char *name) 62 62 { 63 63 bool found = false; 64 - char *path, *tmp; 64 + char *path, *tmp = NULL; 65 65 char buf[PATH_MAX]; 66 66 char *env = getenv("PATH"); 67 67
+5 -4
tools/perf/builtin-report.c
··· 139 139 struct report *rep = container_of(tool, struct report, tool); 140 140 struct addr_location al; 141 141 struct hist_entry_iter iter = { 142 - .hide_unresolved = rep->hide_unresolved, 143 - .add_entry_cb = hist_iter__report_callback, 142 + .evsel = evsel, 143 + .sample = sample, 144 + .hide_unresolved = rep->hide_unresolved, 145 + .add_entry_cb = hist_iter__report_callback, 144 146 }; 145 147 int ret = 0; 146 148 ··· 170 168 if (al.map != NULL) 171 169 al.map->dso->hit = 1; 172 170 173 - ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack, 174 - rep); 171 + ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep); 175 172 if (ret < 0) 176 173 pr_debug("problem adding hist entry, skipping event\n"); 177 174 out_put:
+72 -5
tools/perf/builtin-sched.c
··· 95 95 u64 total_lat; 96 96 u64 nb_atoms; 97 97 u64 total_runtime; 98 + int num_merged; 98 99 }; 99 100 100 101 typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *); ··· 169 168 u64 all_runtime; 170 169 u64 all_count; 171 170 u64 cpu_last_switched[MAX_CPUS]; 172 - struct rb_root atom_root, sorted_atom_root; 171 + struct rb_root atom_root, sorted_atom_root, merged_atom_root; 173 172 struct list_head sort_list, cmp_pid; 174 173 bool force; 174 + bool skip_merge; 175 175 }; 176 176 177 177 static u64 get_nsecs(void) ··· 1184 1182 sched->all_runtime += work_list->total_runtime; 1185 1183 sched->all_count += work_list->nb_atoms; 1186 1184 1187 - ret = printf(" %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid); 1185 + if (work_list->num_merged > 1) 1186 + ret = printf(" %s:(%d) ", thread__comm_str(work_list->thread), work_list->num_merged); 1187 + else 1188 + ret = printf(" %s:%d ", thread__comm_str(work_list->thread), work_list->thread->tid); 1188 1189 1189 1190 for (i = 0; i < 24 - ret; i++) 1190 1191 printf(" "); ··· 1307 1302 static void perf_sched__sort_lat(struct perf_sched *sched) 1308 1303 { 1309 1304 struct rb_node *node; 1310 - 1305 + struct rb_root *root = &sched->atom_root; 1306 + again: 1311 1307 for (;;) { 1312 1308 struct work_atoms *data; 1313 - node = rb_first(&sched->atom_root); 1309 + node = rb_first(root); 1314 1310 if (!node) 1315 1311 break; 1316 1312 1317 - rb_erase(node, &sched->atom_root); 1313 + rb_erase(node, root); 1318 1314 data = rb_entry(node, struct work_atoms, node); 1319 1315 __thread_latency_insert(&sched->sorted_atom_root, data, &sched->sort_list); 1316 + } 1317 + if (root == &sched->atom_root) { 1318 + root = &sched->merged_atom_root; 1319 + goto again; 1320 1320 } 1321 1321 } 1322 1322 ··· 1582 1572 } 1583 1573 } 1584 1574 1575 + static void __merge_work_atoms(struct rb_root *root, struct work_atoms *data) 1576 + { 1577 + struct rb_node **new = &(root->rb_node), *parent = NULL; 1578 + struct work_atoms *this; 1579 + const char *comm = thread__comm_str(data->thread), *this_comm; 1580 + 1581 + while (*new) { 1582 + int cmp; 1583 + 1584 + this = container_of(*new, struct work_atoms, node); 1585 + parent = *new; 1586 + 1587 + this_comm = thread__comm_str(this->thread); 1588 + cmp = strcmp(comm, this_comm); 1589 + if (cmp > 0) { 1590 + new = &((*new)->rb_left); 1591 + } else if (cmp < 0) { 1592 + new = &((*new)->rb_right); 1593 + } else { 1594 + this->num_merged++; 1595 + this->total_runtime += data->total_runtime; 1596 + this->nb_atoms += data->nb_atoms; 1597 + this->total_lat += data->total_lat; 1598 + list_splice(&data->work_list, &this->work_list); 1599 + if (this->max_lat < data->max_lat) { 1600 + this->max_lat = data->max_lat; 1601 + this->max_lat_at = data->max_lat_at; 1602 + } 1603 + zfree(&data); 1604 + return; 1605 + } 1606 + } 1607 + 1608 + data->num_merged++; 1609 + rb_link_node(&data->node, parent, new); 1610 + rb_insert_color(&data->node, root); 1611 + } 1612 + 1613 + static void perf_sched__merge_lat(struct perf_sched *sched) 1614 + { 1615 + struct work_atoms *data; 1616 + struct rb_node *node; 1617 + 1618 + if (sched->skip_merge) 1619 + return; 1620 + 1621 + while ((node = rb_first(&sched->atom_root))) { 1622 + rb_erase(node, &sched->atom_root); 1623 + data = rb_entry(node, struct work_atoms, node); 1624 + __merge_work_atoms(&sched->merged_atom_root, data); 1625 + } 1626 + } 1627 + 1585 1628 static int perf_sched__lat(struct perf_sched *sched) 1586 1629 { 1587 1630 struct rb_node *next; ··· 1644 1581 if (perf_sched__read_events(sched)) 1645 1582 return -1; 1646 1583 1584 + perf_sched__merge_lat(sched); 1647 1585 perf_sched__sort_lat(sched); 1648 1586 1649 1587 printf("\n -----------------------------------------------------------------------------------------------------------------\n"); ··· 1796 1732 .profile_cpu = -1, 1797 1733 .next_shortname1 = 'A', 1798 1734 .next_shortname2 = '0', 1735 + .skip_merge = 0, 1799 1736 }; 1800 1737 const struct option latency_options[] = { 1801 1738 OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]", ··· 1807 1742 "CPU to profile on"), 1808 1743 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 1809 1744 "dump raw trace in ASCII"), 1745 + OPT_BOOLEAN('p', "pids", &sched.skip_merge, 1746 + "latency stats per pid instead of per comm"), 1810 1747 OPT_END() 1811 1748 }; 1812 1749 const struct option replay_options[] = {
+4 -3
tools/perf/builtin-top.c
··· 775 775 if (al.sym == NULL || !al.sym->ignore) { 776 776 struct hists *hists = evsel__hists(evsel); 777 777 struct hist_entry_iter iter = { 778 - .add_entry_cb = hist_iter__top_callback, 778 + .evsel = evsel, 779 + .sample = sample, 780 + .add_entry_cb = hist_iter__top_callback, 779 781 }; 780 782 781 783 if (symbol_conf.cumulate_callchain) ··· 787 785 788 786 pthread_mutex_lock(&hists->lock); 789 787 790 - err = hist_entry_iter__add(&iter, &al, evsel, sample, 791 - top->max_stack, top); 788 + err = hist_entry_iter__add(&iter, &al, top->max_stack, top); 792 789 if (err < 0) 793 790 pr_err("Problem incrementing symbol period, skipping event\n"); 794 791
+3 -1
tools/perf/config/Makefile
··· 32 32 LIBUNWIND_LIBS = -lunwind -lunwind-x86_64 33 33 $(call detected,CONFIG_X86_64) 34 34 else 35 - LIBUNWIND_LIBS = -lunwind -lunwind-x86 35 + LIBUNWIND_LIBS = -lunwind-x86 -llzma -lunwind 36 36 endif 37 37 NO_PERF_REGS := 0 38 38 endif ··· 130 130 131 131 ifeq ($(DEBUG),0) 132 132 CFLAGS += -O6 133 + else 134 + CFLAGS += $(call cc-option,-Og,-O0) 133 135 endif 134 136 135 137 ifdef PARSER_DEBUG
+19
tools/perf/config/utilities.mak
··· 177 177 endef 178 178 _ge_attempt = $(if $(get-executable),$(get-executable),$(call _gea_err,$(2))) 179 179 _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) 180 + 181 + # try-run 182 + # Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise) 183 + # Exit code chooses option. "$$TMP" is can be used as temporary file and 184 + # is automatically cleaned up. 185 + try-run = $(shell set -e; \ 186 + TMP="$(TMPOUT).$$$$.tmp"; \ 187 + TMPO="$(TMPOUT).$$$$.o"; \ 188 + if ($(1)) >/dev/null 2>&1; \ 189 + then echo "$(2)"; \ 190 + else echo "$(3)"; \ 191 + fi; \ 192 + rm -f "$$TMP" "$$TMPO") 193 + 194 + # cc-option 195 + # Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586) 196 + 197 + cc-option = $(call try-run,\ 198 + $(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",$(1),$(2))
+11
tools/perf/tests/dso-data.c
··· 99 99 }, 100 100 }; 101 101 102 + /* move it from util/dso.c for compatibility */ 103 + static int dso__data_fd(struct dso *dso, struct machine *machine) 104 + { 105 + int fd = dso__data_get_fd(dso, machine); 106 + 107 + if (fd >= 0) 108 + dso__data_put_fd(dso); 109 + 110 + return fd; 111 + } 112 + 102 113 int test__dso_data(void) 103 114 { 104 115 struct machine machine;
+4 -2
tools/perf/tests/hists_cumulate.c
··· 87 87 }, 88 88 }; 89 89 struct hist_entry_iter iter = { 90 + .evsel = evsel, 91 + .sample = &sample, 90 92 .hide_unresolved = false, 91 93 }; 92 94 ··· 106 104 &sample) < 0) 107 105 goto out; 108 106 109 - if (hist_entry_iter__add(&iter, &al, evsel, &sample, 110 - PERF_MAX_STACK_DEPTH, NULL) < 0) { 107 + if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, 108 + NULL) < 0) { 111 109 addr_location__put(&al); 112 110 goto out; 113 111 }
+3 -1
tools/perf/tests/hists_filter.c
··· 63 63 }, 64 64 }; 65 65 struct hist_entry_iter iter = { 66 + .evsel = evsel, 67 + .sample = &sample, 66 68 .ops = &hist_iter_normal, 67 69 .hide_unresolved = false, 68 70 }; ··· 83 81 &sample) < 0) 84 82 goto out; 85 83 86 - if (hist_entry_iter__add(&iter, &al, evsel, &sample, 84 + if (hist_entry_iter__add(&iter, &al, 87 85 PERF_MAX_STACK_DEPTH, NULL) < 0) { 88 86 addr_location__put(&al); 89 87 goto out;
+4 -2
tools/perf/tests/hists_output.c
··· 57 57 }, 58 58 }; 59 59 struct hist_entry_iter iter = { 60 + .evsel = evsel, 61 + .sample = &sample, 60 62 .ops = &hist_iter_normal, 61 63 .hide_unresolved = false, 62 64 }; ··· 72 70 &sample) < 0) 73 71 goto out; 74 72 75 - if (hist_entry_iter__add(&iter, &al, evsel, &sample, 76 - PERF_MAX_STACK_DEPTH, NULL) < 0) { 73 + if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH, 74 + NULL) < 0) { 77 75 addr_location__put(&al); 78 76 goto out; 79 77 }
+17 -17
tools/perf/tests/vmlinux-kallsyms.c
··· 23 23 int err = -1; 24 24 struct rb_node *nd; 25 25 struct symbol *sym; 26 - struct map *kallsyms_map, *vmlinux_map; 26 + struct map *kallsyms_map, *vmlinux_map, *map; 27 27 struct machine kallsyms, vmlinux; 28 28 enum map_type type = MAP__FUNCTION; 29 + struct rb_root *maps = &vmlinux.kmaps.maps[type]; 29 30 u64 mem_start, mem_end; 30 31 31 32 /* ··· 185 184 186 185 pr_info("Maps only in vmlinux:\n"); 187 186 188 - for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { 189 - struct map *pos = rb_entry(nd, struct map, rb_node), *pair; 187 + for (map = maps__first(maps); map; map = map__next(map)) { 188 + struct map * 190 189 /* 191 190 * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while 192 191 * the kernel will have the path for the vmlinux file being used, ··· 194 193 * both cases. 195 194 */ 196 195 pair = map_groups__find_by_name(&kallsyms.kmaps, type, 197 - (pos->dso->kernel ? 198 - pos->dso->short_name : 199 - pos->dso->name)); 196 + (map->dso->kernel ? 197 + map->dso->short_name : 198 + map->dso->name)); 200 199 if (pair) 201 200 pair->priv = 1; 202 201 else 203 - map__fprintf(pos, stderr); 202 + map__fprintf(map, stderr); 204 203 } 205 204 206 205 pr_info("Maps in vmlinux with a different name in kallsyms:\n"); 207 206 208 - for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { 209 - struct map *pos = rb_entry(nd, struct map, rb_node), *pair; 207 + for (map = maps__first(maps); map; map = map__next(map)) { 208 + struct map *pair; 210 209 211 - mem_start = vmlinux_map->unmap_ip(vmlinux_map, pos->start); 212 - mem_end = vmlinux_map->unmap_ip(vmlinux_map, pos->end); 210 + mem_start = vmlinux_map->unmap_ip(vmlinux_map, map->start); 211 + mem_end = vmlinux_map->unmap_ip(vmlinux_map, map->end); 213 212 214 213 pair = map_groups__find(&kallsyms.kmaps, type, mem_start); 215 214 if (pair == NULL || pair->priv) ··· 218 217 if (pair->start == mem_start) { 219 218 pair->priv = 1; 220 219 pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", 221 - pos->start, pos->end, pos->pgoff, pos->dso->name); 220 + map->start, map->end, map->pgoff, map->dso->name); 222 221 if (mem_end != pair->end) 223 222 pr_info(":\n*%" PRIx64 "-%" PRIx64 " %" PRIx64, 224 223 pair->start, pair->end, pair->pgoff); ··· 229 228 230 229 pr_info("Maps only in kallsyms:\n"); 231 230 232 - for (nd = rb_first(&kallsyms.kmaps.maps[type]); 233 - nd; nd = rb_next(nd)) { 234 - struct map *pos = rb_entry(nd, struct map, rb_node); 231 + maps = &kallsyms.kmaps.maps[type]; 235 232 236 - if (!pos->priv) 237 - map__fprintf(pos, stderr); 233 + for (map = maps__first(maps); map; map = map__next(map)) { 234 + if (!map->priv) 235 + map__fprintf(map, stderr); 238 236 } 239 237 out: 240 238 machine__exit(&kallsyms);
+9 -4
tools/perf/util/comm.c
··· 2 2 #include "util.h" 3 3 #include <stdlib.h> 4 4 #include <stdio.h> 5 + #include <linux/atomic.h> 5 6 6 7 struct comm_str { 7 8 char *str; 8 9 struct rb_node rb_node; 9 - int ref; 10 + atomic_t refcnt; 10 11 }; 11 12 12 13 /* Should perhaps be moved to struct machine */ 13 14 static struct rb_root comm_str_root; 14 15 15 - static void comm_str__get(struct comm_str *cs) 16 + static struct comm_str *comm_str__get(struct comm_str *cs) 16 17 { 17 - cs->ref++; 18 + if (cs) 19 + atomic_inc(&cs->refcnt); 20 + return cs; 18 21 } 19 22 20 23 static void comm_str__put(struct comm_str *cs) 21 24 { 22 - if (!--cs->ref) { 25 + if (cs && atomic_dec_and_test(&cs->refcnt)) { 23 26 rb_erase(&cs->rb_node, &comm_str_root); 24 27 zfree(&cs->str); 25 28 free(cs); ··· 42 39 free(cs); 43 40 return NULL; 44 41 } 42 + 43 + atomic_set(&cs->refcnt, 0); 45 44 46 45 return cs; 47 46 }
+50 -38
tools/perf/util/dso.c
··· 440 440 pthread_mutex_unlock(&dso__data_open_lock); 441 441 } 442 442 443 - /** 444 - * dso__data_fd - Get dso's data file descriptor 445 - * @dso: dso object 446 - * @machine: machine object 447 - * 448 - * External interface to find dso's file, open it and 449 - * returns file descriptor. 450 - */ 451 - int dso__data_fd(struct dso *dso, struct machine *machine) 443 + static void try_to_open_dso(struct dso *dso, struct machine *machine) 452 444 { 453 445 enum dso_binary_type binary_type_data[] = { 454 446 DSO_BINARY_TYPE__BUILD_ID_CACHE, ··· 449 457 }; 450 458 int i = 0; 451 459 452 - if (dso->data.status == DSO_DATA_STATUS_ERROR) 453 - return -1; 454 - 455 - pthread_mutex_lock(&dso__data_open_lock); 456 - 457 460 if (dso->data.fd >= 0) 458 - goto out; 461 + return; 459 462 460 463 if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) { 461 464 dso->data.fd = open_dso(dso, machine); ··· 470 483 dso->data.status = DSO_DATA_STATUS_OK; 471 484 else 472 485 dso->data.status = DSO_DATA_STATUS_ERROR; 486 + } 473 487 474 - pthread_mutex_unlock(&dso__data_open_lock); 488 + /** 489 + * dso__data_get_fd - Get dso's data file descriptor 490 + * @dso: dso object 491 + * @machine: machine object 492 + * 493 + * External interface to find dso's file, open it and 494 + * returns file descriptor. It should be paired with 495 + * dso__data_put_fd() if it returns non-negative value. 496 + */ 497 + int dso__data_get_fd(struct dso *dso, struct machine *machine) 498 + { 499 + if (dso->data.status == DSO_DATA_STATUS_ERROR) 500 + return -1; 501 + 502 + if (pthread_mutex_lock(&dso__data_open_lock) < 0) 503 + return -1; 504 + 505 + try_to_open_dso(dso, machine); 506 + 507 + if (dso->data.fd < 0) 508 + pthread_mutex_unlock(&dso__data_open_lock); 509 + 475 510 return dso->data.fd; 511 + } 512 + 513 + void dso__data_put_fd(struct dso *dso __maybe_unused) 514 + { 515 + pthread_mutex_unlock(&dso__data_open_lock); 476 516 } 477 517 478 518 bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by) ··· 623 609 * dso->data.fd might be closed if other thread opened another 624 610 * file (dso) due to open file limit (RLIMIT_NOFILE). 625 611 */ 612 + try_to_open_dso(dso, machine); 613 + 626 614 if (dso->data.fd < 0) { 627 - dso->data.fd = open_dso(dso, machine); 628 - if (dso->data.fd < 0) { 629 - ret = -errno; 630 - dso->data.status = DSO_DATA_STATUS_ERROR; 631 - break; 632 - } 615 + ret = -errno; 616 + dso->data.status = DSO_DATA_STATUS_ERROR; 617 + break; 633 618 } 634 619 635 620 cache_offset = offset & DSO__DATA_CACHE_MASK; ··· 715 702 if (dso->data.file_size) 716 703 return 0; 717 704 705 + if (dso->data.status == DSO_DATA_STATUS_ERROR) 706 + return -1; 707 + 718 708 pthread_mutex_lock(&dso__data_open_lock); 719 709 720 710 /* 721 711 * dso->data.fd might be closed if other thread opened another 722 712 * file (dso) due to open file limit (RLIMIT_NOFILE). 723 713 */ 714 + try_to_open_dso(dso, machine); 715 + 724 716 if (dso->data.fd < 0) { 725 - dso->data.fd = open_dso(dso, machine); 726 - if (dso->data.fd < 0) { 727 - ret = -errno; 728 - dso->data.status = DSO_DATA_STATUS_ERROR; 729 - goto out; 730 - } 717 + ret = -errno; 718 + dso->data.status = DSO_DATA_STATUS_ERROR; 719 + goto out; 731 720 } 732 721 733 722 if (fstat(dso->data.fd, &st) < 0) { ··· 755 740 */ 756 741 off_t dso__data_size(struct dso *dso, struct machine *machine) 757 742 { 758 - int fd; 759 - 760 - fd = dso__data_fd(dso, machine); 761 - if (fd < 0) 762 - return fd; 763 - 764 743 if (data_file_size(dso, machine)) 765 744 return -1; 766 745 ··· 1209 1200 enum dso_type dso__type(struct dso *dso, struct machine *machine) 1210 1201 { 1211 1202 int fd; 1203 + enum dso_type type = DSO__TYPE_UNKNOWN; 1212 1204 1213 - fd = dso__data_fd(dso, machine); 1214 - if (fd < 0) 1215 - return DSO__TYPE_UNKNOWN; 1205 + fd = dso__data_get_fd(dso, machine); 1206 + if (fd >= 0) { 1207 + type = dso__type_fd(fd); 1208 + dso__data_put_fd(dso); 1209 + } 1216 1210 1217 - return dso__type_fd(fd); 1211 + return type; 1218 1212 } 1219 1213 1220 1214 int dso__strerror_load(struct dso *dso, char *buf, size_t buflen)
+9 -4
tools/perf/util/dso.h
··· 240 240 241 241 /* 242 242 * The dso__data_* external interface provides following functions: 243 - * dso__data_fd 243 + * dso__data_get_fd 244 + * dso__data_put_fd 244 245 * dso__data_close 245 246 * dso__data_size 246 247 * dso__data_read_offset ··· 258 257 * The current usage of the dso__data_* interface is as follows: 259 258 * 260 259 * Get DSO's fd: 261 - * int fd = dso__data_fd(dso, machine); 262 - * USE 'fd' SOMEHOW 260 + * int fd = dso__data_get_fd(dso, machine); 261 + * if (fd >= 0) { 262 + * USE 'fd' SOMEHOW 263 + * dso__data_put_fd(dso); 264 + * } 263 265 * 264 266 * Read DSO's data: 265 267 * n = dso__data_read_offset(dso_0, &machine, 0, buf, BUFSIZE); ··· 281 277 * 282 278 * TODO 283 279 */ 284 - int dso__data_fd(struct dso *dso, struct machine *machine); 280 + int dso__data_get_fd(struct dso *dso, struct machine *machine); 281 + void dso__data_put_fd(struct dso *dso __maybe_unused); 285 282 void dso__data_close(struct dso *dso); 286 283 287 284 off_t dso__data_size(struct dso *dso, struct machine *machine);
+3 -4
tools/perf/util/event.c
··· 329 329 struct machine *machine) 330 330 { 331 331 int rc = 0; 332 - struct rb_node *nd; 332 + struct map *pos; 333 333 struct map_groups *kmaps = &machine->kmaps; 334 + struct rb_root *maps = &kmaps->maps[MAP__FUNCTION]; 334 335 union perf_event *event = zalloc((sizeof(event->mmap) + 335 336 machine->id_hdr_size)); 336 337 if (event == NULL) { ··· 351 350 else 352 351 event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; 353 352 354 - for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); 355 - nd; nd = rb_next(nd)) { 353 + for (pos = maps__first(maps); pos; pos = map__next(pos)) { 356 354 size_t size; 357 - struct map *pos = rb_entry(nd, struct map, rb_node); 358 355 359 356 if (pos->dso->kernel) 360 357 continue;
+10 -14
tools/perf/util/hist.c
··· 362 362 return 0; 363 363 } 364 364 365 - static struct hist_entry *add_hist_entry(struct hists *hists, 366 - struct hist_entry *entry, 367 - struct addr_location *al, 368 - bool sample_self) 365 + static struct hist_entry *hists__findnew_entry(struct hists *hists, 366 + struct hist_entry *entry, 367 + struct addr_location *al, 368 + bool sample_self) 369 369 { 370 370 struct rb_node **p; 371 371 struct rb_node *parent = NULL; ··· 468 468 .transaction = transaction, 469 469 }; 470 470 471 - return add_hist_entry(hists, &entry, al, sample_self); 471 + return hists__findnew_entry(hists, &entry, al, sample_self); 472 472 } 473 473 474 474 static int ··· 548 548 549 549 out: 550 550 /* 551 - * We don't need to free iter->priv (mem_info) here since 552 - * the mem info was either already freed in add_hist_entry() or 553 - * passed to a new hist entry by hist_entry__new(). 551 + * We don't need to free iter->priv (mem_info) here since the mem info 552 + * was either already freed in hists__findnew_entry() or passed to a 553 + * new hist entry by hist_entry__new(). 554 554 */ 555 555 iter->priv = NULL; 556 556 ··· 851 851 }; 852 852 853 853 int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, 854 - struct perf_evsel *evsel, struct perf_sample *sample, 855 854 int max_stack_depth, void *arg) 856 855 { 857 856 int err, err2; 858 857 859 - err = sample__resolve_callchain(sample, &iter->parent, evsel, al, 860 - max_stack_depth); 858 + err = sample__resolve_callchain(iter->sample, &iter->parent, 859 + iter->evsel, al, max_stack_depth); 861 860 if (err) 862 861 return err; 863 - 864 - iter->evsel = evsel; 865 - iter->sample = sample; 866 862 867 863 err = iter->ops->prepare_entry(iter, al); 868 864 if (err)
-1
tools/perf/util/hist.h
··· 111 111 u64 weight, u64 transaction, 112 112 bool sample_self); 113 113 int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al, 114 - struct perf_evsel *evsel, struct perf_sample *sample, 115 114 int max_stack_depth, void *arg); 116 115 117 116 int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
+14
tools/perf/util/include/linux/rbtree.h
··· 1 + #ifndef __TOOLS_LINUX_PERF_RBTREE_H 2 + #define __TOOLS_LINUX_PERF_RBTREE_H 1 3 #include <stdbool.h> 2 4 #include "../../../../include/linux/rbtree.h" 5 + 6 + /* 7 + * Handy for checking that we are not deleting an entry that is 8 + * already in a list, found in block/{blk-throttle,cfq-iosched}.c, 9 + * probably should be moved to lib/rbtree.c... 10 + */ 11 + static inline void rb_erase_init(struct rb_node *n, struct rb_root *root) 12 + { 13 + rb_erase(n, root); 14 + RB_CLEAR_NODE(n); 15 + } 16 + #endif /* __TOOLS_LINUX_PERF_RBTREE_H */
+3 -3
tools/perf/util/machine.c
··· 333 333 if (!map_groups__empty(th->mg)) 334 334 pr_err("Discarding thread maps for %d:%d\n", 335 335 th->pid_, th->tid); 336 - map_groups__delete(th->mg); 336 + map_groups__put(th->mg); 337 337 } 338 338 339 339 th->mg = map_groups__get(leader->mg); ··· 400 400 * leader and that would screwed the rb tree. 401 401 */ 402 402 if (thread__init_map_groups(th, machine)) { 403 - rb_erase(&th->rb_node, &machine->threads); 403 + rb_erase_init(&th->rb_node, &machine->threads); 404 404 RB_CLEAR_NODE(&th->rb_node); 405 405 thread__delete(th); 406 406 return NULL; ··· 1314 1314 BUG_ON(atomic_read(&th->refcnt) == 0); 1315 1315 if (lock) 1316 1316 pthread_rwlock_wrlock(&machine->threads_lock); 1317 - rb_erase(&th->rb_node, &machine->threads); 1317 + rb_erase_init(&th->rb_node, &machine->threads); 1318 1318 RB_CLEAR_NODE(&th->rb_node); 1319 1319 /* 1320 1320 * Move it first to the dead_threads list, then drop the reference,
+5 -26
tools/perf/util/map.c
··· 498 498 map_groups__delete(mg); 499 499 } 500 500 501 - void map_groups__flush(struct map_groups *mg) 502 - { 503 - int type; 504 - 505 - for (type = 0; type < MAP__NR_TYPES; type++) { 506 - struct rb_root *root = &mg->maps[type]; 507 - struct rb_node *next = rb_first(root); 508 - 509 - while (next) { 510 - struct map *pos = rb_entry(next, struct map, rb_node); 511 - next = rb_next(&pos->rb_node); 512 - rb_erase(&pos->rb_node, root); 513 - /* 514 - * We may have references to this map, for 515 - * instance in some hist_entry instances, so 516 - * just move them to a separate list. 517 - */ 518 - list_add_tail(&pos->node, &mg->removed_maps[pos->type]); 519 - } 520 - } 521 - } 522 - 523 501 struct symbol *map_groups__find_symbol(struct map_groups *mg, 524 502 enum map_type type, u64 addr, 525 503 struct map **mapp, ··· 688 710 int map_groups__clone(struct map_groups *mg, 689 711 struct map_groups *parent, enum map_type type) 690 712 { 691 - struct rb_node *nd; 692 - for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { 693 - struct map *map = rb_entry(nd, struct map, rb_node); 713 + struct map *map; 714 + struct rb_root *maps = &parent->maps[type]; 715 + 716 + for (map = maps__first(maps); map; map = map__next(map)) { 694 717 struct map *new = map__clone(map); 695 718 if (new == NULL) 696 719 return -ENOMEM; ··· 754 775 return NULL; 755 776 } 756 777 757 - struct map *maps__next(struct map *map) 778 + struct map *map__next(struct map *map) 758 779 { 759 780 struct rb_node *next = rb_next(&map->rb_node); 760 781
+2 -4
tools/perf/util/map.h
··· 166 166 void maps__remove(struct rb_root *maps, struct map *map); 167 167 struct map *maps__find(struct rb_root *maps, u64 addr); 168 168 struct map *maps__first(struct rb_root *maps); 169 - struct map *maps__next(struct map *map); 169 + struct map *map__next(struct map *map); 170 170 void map_groups__init(struct map_groups *mg, struct machine *machine); 171 171 void map_groups__exit(struct map_groups *mg); 172 172 int map_groups__clone(struct map_groups *mg, ··· 201 201 202 202 static inline struct map *map_groups__next(struct map *map) 203 203 { 204 - return maps__next(map); 204 + return map__next(map); 205 205 } 206 206 207 207 struct symbol *map_groups__find_symbol(struct map_groups *mg, ··· 232 232 233 233 struct map *map_groups__find_by_name(struct map_groups *mg, 234 234 enum map_type type, const char *name); 235 - 236 - void map_groups__flush(struct map_groups *mg); 237 235 238 236 #endif /* __PERF_MAP_H */
+10 -8
tools/perf/util/parse-events.c
··· 25 25 extern int parse_events_debug; 26 26 #endif 27 27 int parse_events_parse(void *data, void *scanner); 28 - int parse_events_term__num(struct parse_events_term **term, 29 - int type_term, char *config, u64 num, 30 - YYLTYPE *loc_term, YYLTYPE *loc_val); 31 - int parse_events_term__str(struct parse_events_term **term, 32 - int type_term, char *config, char *str, 33 - YYLTYPE *loc_term, YYLTYPE *loc_val); 34 28 35 29 static struct perf_pmu_event_symbol *perf_pmu_events_list; 36 30 /* ··· 1595 1601 1596 1602 int parse_events_term__num(struct parse_events_term **term, 1597 1603 int type_term, char *config, u64 num, 1598 - YYLTYPE *loc_term, YYLTYPE *loc_val) 1604 + void *loc_term_, void *loc_val_) 1599 1605 { 1606 + YYLTYPE *loc_term = loc_term_; 1607 + YYLTYPE *loc_val = loc_val_; 1608 + 1600 1609 return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, 1601 1610 config, NULL, num, 1602 1611 loc_term ? loc_term->first_column : 0, ··· 1608 1611 1609 1612 int parse_events_term__str(struct parse_events_term **term, 1610 1613 int type_term, char *config, char *str, 1611 - YYLTYPE *loc_term, YYLTYPE *loc_val) 1614 + void *loc_term_, void *loc_val_) 1612 1615 { 1616 + YYLTYPE *loc_term = loc_term_; 1617 + YYLTYPE *loc_val = loc_val_; 1618 + 1613 1619 return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, 1614 1620 config, str, 0, 1615 1621 loc_term ? loc_term->first_column : 0, ··· 1659 1659 { 1660 1660 struct parse_events_error *err = data->error; 1661 1661 1662 + if (!err) 1663 + return; 1662 1664 err->idx = idx; 1663 1665 err->str = strdup(str); 1664 1666 WARN_ONCE(!err->str, "WARNING: failed to allocate error string");
+6
tools/perf/util/parse-events.h
··· 98 98 }; 99 99 100 100 int parse_events__is_hardcoded_term(struct parse_events_term *term); 101 + int parse_events_term__num(struct parse_events_term **term, 102 + int type_term, char *config, u64 num, 103 + void *loc_term, void *loc_val); 104 + int parse_events_term__str(struct parse_events_term **term, 105 + int type_term, char *config, char *str, 106 + void *loc_term, void *loc_val); 101 107 int parse_events_term__sym_hw(struct parse_events_term **term, 102 108 char *config, unsigned idx); 103 109 int parse_events_term__clone(struct parse_events_term **new,
+4 -2
tools/perf/util/parse-events.y
··· 389 389 if (parse_events_add_tracepoint(list, &data->idx, $1, $3)) { 390 390 struct parse_events_error *error = data->error; 391 391 392 - error->idx = @1.first_column; 393 - error->str = strdup("unknown tracepoint"); 392 + if (error) { 393 + error->idx = @1.first_column; 394 + error->str = strdup("unknown tracepoint"); 395 + } 394 396 return -1; 395 397 } 396 398 $$ = list;
+4
tools/perf/util/pmu.c
··· 442 442 LIST_HEAD(aliases); 443 443 __u32 type; 444 444 445 + /* No support for intel_bts or intel_pt so disallow them */ 446 + if (!strcmp(name, "intel_bts") || !strcmp(name, "intel_pt")) 447 + return NULL; 448 + 445 449 /* 446 450 * The pmu data we store & need consists of the pmu 447 451 * type value and format definitions. Load both right
+32 -33
tools/perf/util/probe-event.c
··· 162 162 163 163 static struct map *kernel_get_module_map(const char *module) 164 164 { 165 - struct rb_node *nd; 166 165 struct map_groups *grp = &host_machine->kmaps; 166 + struct rb_root *maps = &grp->maps[MAP__FUNCTION]; 167 + struct map *pos; 167 168 168 169 /* A file path -- this is an offline module */ 169 170 if (module && strchr(module, '/')) ··· 173 172 if (!module) 174 173 module = "kernel"; 175 174 176 - for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { 177 - struct map *pos = rb_entry(nd, struct map, rb_node); 175 + for (pos = maps__first(maps); pos; pos = map__next(pos)) { 178 176 if (strncmp(pos->dso->short_name + 1, module, 179 177 pos->dso->short_name_len - 2) == 0) { 180 178 return pos; ··· 195 195 { 196 196 if (map && user) { 197 197 /* Only the user map needs to be released */ 198 - dso__delete(map->dso); 199 198 map__delete(map); 200 199 } 201 200 } 202 201 203 202 204 - static struct dso *kernel_get_module_dso(const char *module) 203 + static int kernel_get_module_dso(const char *module, struct dso **pdso) 205 204 { 206 205 struct dso *dso; 207 206 struct map *map; 208 207 const char *vmlinux_name; 208 + int ret = 0; 209 209 210 210 if (module) { 211 211 list_for_each_entry(dso, &host_machine->kernel_dsos.head, ··· 215 215 goto found; 216 216 } 217 217 pr_debug("Failed to find module %s.\n", module); 218 - return NULL; 218 + return -ENOENT; 219 219 } 220 220 221 221 map = host_machine->vmlinux_maps[MAP__FUNCTION]; 222 222 dso = map->dso; 223 223 224 224 vmlinux_name = symbol_conf.vmlinux_name; 225 - if (vmlinux_name) { 226 - if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0) 227 - return NULL; 228 - } else { 229 - if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { 230 - pr_debug("Failed to load kernel map.\n"); 231 - return NULL; 232 - } 233 - } 225 + dso->load_errno = 0; 226 + if (vmlinux_name) 227 + ret = dso__load_vmlinux(dso, map, vmlinux_name, false, NULL); 228 + else 229 + ret = dso__load_vmlinux_path(dso, map, NULL); 234 230 found: 235 - return dso; 236 - } 237 - 238 - const char *kernel_get_module_path(const char *module) 239 - { 240 - struct dso *dso = kernel_get_module_dso(module); 241 - return (dso) ? dso->long_name : NULL; 231 + *pdso = dso; 232 + return ret; 242 233 } 243 234 244 235 static int convert_exec_to_group(const char *exec, char **result) ··· 381 390 static struct debuginfo *open_debuginfo(const char *module, bool silent) 382 391 { 383 392 const char *path = module; 384 - struct debuginfo *ret; 393 + char reason[STRERR_BUFSIZE]; 394 + struct debuginfo *ret = NULL; 395 + struct dso *dso = NULL; 396 + int err; 385 397 386 398 if (!module || !strchr(module, '/')) { 387 - path = kernel_get_module_path(module); 388 - if (!path) { 399 + err = kernel_get_module_dso(module, &dso); 400 + if (err < 0) { 401 + if (!dso || dso->load_errno == 0) { 402 + if (!strerror_r(-err, reason, STRERR_BUFSIZE)) 403 + strcpy(reason, "(unknown)"); 404 + } else 405 + dso__strerror_load(dso, reason, STRERR_BUFSIZE); 389 406 if (!silent) 390 - pr_err("Failed to find path of %s module.\n", 391 - module ?: "kernel"); 407 + pr_err("Failed to find the path for %s: %s\n", 408 + module ?: "kernel", reason); 392 409 return NULL; 393 410 } 411 + path = dso->long_name; 394 412 } 395 413 ret = debuginfo__new(path); 396 414 if (!ret && !silent) { ··· 1791 1791 1792 1792 out: 1793 1793 if (map && !is_kprobe) { 1794 - dso__delete(map->dso); 1795 1794 map__delete(map); 1796 1795 } 1797 1796 ··· 2811 2812 goto error; 2812 2813 2813 2814 ret2 = del_trace_probe_events(ufd, filter, unamelist); 2814 - if (ret2 < 0 && ret2 != -ENOENT) 2815 + if (ret2 < 0 && ret2 != -ENOENT) { 2815 2816 ret = ret2; 2816 - else if (ret == -ENOENT && ret2 == -ENOENT) { 2817 + goto error; 2818 + } 2819 + if (ret == -ENOENT && ret2 == -ENOENT) 2817 2820 pr_debug("\"%s\" does not hit any event.\n", str); 2818 2821 /* Note that this is silently ignored */ 2819 - ret = 0; 2820 - } 2822 + ret = 0; 2821 2823 2822 2824 error: 2823 2825 if (kfd >= 0) { ··· 2884 2884 dso__fprintf_symbols_by_name(map->dso, map->type, stdout); 2885 2885 end: 2886 2886 if (user) { 2887 - dso__delete(map->dso); 2888 2887 map__delete(map); 2889 2888 } 2890 2889 exit_symbol_maps();
-3
tools/perf/util/probe-event.h
··· 131 131 /* Initialize line range */ 132 132 extern int line_range__init(struct line_range *lr); 133 133 134 - /* Internal use: Return kernel/module path */ 135 - extern const char *kernel_get_module_path(const char *module); 136 - 137 134 extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs); 138 135 extern int del_perf_probe_events(struct strfilter *filter); 139 136 extern int show_perf_probe_events(struct strfilter *filter);
+3 -3
tools/perf/util/session.c
··· 1182 1182 return -1; 1183 1183 1184 1184 if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 || 1185 - readn(fd, &buf, hdr_sz) != (ssize_t)hdr_sz) 1185 + readn(fd, buf, hdr_sz) != (ssize_t)hdr_sz) 1186 1186 return -1; 1187 1187 1188 1188 event = (union perf_event *)buf; ··· 1190 1190 if (session->header.needs_swap) 1191 1191 perf_event_header__bswap(&event->header); 1192 1192 1193 - if (event->header.size < hdr_sz) 1193 + if (event->header.size < hdr_sz || event->header.size > buf_sz) 1194 1194 return -1; 1195 1195 1196 1196 rest = event->header.size - hdr_sz; 1197 1197 1198 - if (readn(fd, &buf, rest) != (ssize_t)rest) 1198 + if (readn(fd, buf, rest) != (ssize_t)rest) 1199 1199 return -1; 1200 1200 1201 1201 if (session->header.needs_swap)
+11 -14
tools/perf/util/symbol.c
··· 202 202 203 203 void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) 204 204 { 205 - struct map *prev, *curr; 206 - struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); 205 + struct rb_root *maps = &mg->maps[type]; 206 + struct map *next, *curr; 207 207 208 - if (prevnd == NULL) 208 + curr = maps__first(maps); 209 + if (curr == NULL) 209 210 return; 210 211 211 - curr = rb_entry(prevnd, struct map, rb_node); 212 - 213 - for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { 214 - prev = curr; 215 - curr = rb_entry(nd, struct map, rb_node); 216 - prev->end = curr->start; 212 + for (next = map__next(curr); next; next = map__next(curr)) { 213 + curr->end = next->start; 214 + curr = next; 217 215 } 218 216 219 217 /* ··· 398 400 const char *name) 399 401 { 400 402 struct rb_node *n; 401 - struct symbol_name_rb_node *s; 403 + struct symbol_name_rb_node *s = NULL; 402 404 403 405 if (symbols == NULL) 404 406 return NULL; ··· 1520 1522 struct map *map_groups__find_by_name(struct map_groups *mg, 1521 1523 enum map_type type, const char *name) 1522 1524 { 1523 - struct rb_node *nd; 1525 + struct rb_root *maps = &mg->maps[type]; 1526 + struct map *map; 1524 1527 1525 - for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { 1526 - struct map *map = rb_entry(nd, struct map, rb_node); 1527 - 1528 + for (map = maps__first(maps); map; map = map__next(map)) { 1528 1529 if (map->dso && strcmp(map->dso->short_name, name) == 0) 1529 1530 return map; 1530 1531 }
-2
tools/perf/util/thread.c
··· 54 54 55 55 list_add(&comm->list, &thread->comm_list); 56 56 atomic_set(&thread->refcnt, 0); 57 - INIT_LIST_HEAD(&thread->node); 58 57 RB_CLEAR_NODE(&thread->rb_node); 59 58 } 60 59 ··· 69 70 struct comm *comm, *tmp; 70 71 71 72 BUG_ON(!RB_EMPTY_NODE(&thread->rb_node)); 72 - BUG_ON(!list_empty(&thread->node)); 73 73 74 74 thread_stack__free(thread); 75 75
+1 -1
tools/perf/util/trace-event-parse.c
··· 173 173 char *line; 174 174 char *next = NULL; 175 175 char *addr_str; 176 - char *fmt; 176 + char *fmt = NULL; 177 177 178 178 line = strtok_r(file, "\n", &next); 179 179 while (line) {
+8 -3
tools/perf/util/unwind-libunwind.c
··· 269 269 u64 offset = dso->data.eh_frame_hdr_offset; 270 270 271 271 if (offset == 0) { 272 - fd = dso__data_fd(dso, machine); 272 + fd = dso__data_get_fd(dso, machine); 273 273 if (fd < 0) 274 274 return -EINVAL; 275 275 276 276 /* Check the .eh_frame section for unwinding info */ 277 277 offset = elf_section_offset(fd, ".eh_frame_hdr"); 278 278 dso->data.eh_frame_hdr_offset = offset; 279 + dso__data_put_fd(dso); 279 280 } 280 281 281 282 if (offset) ··· 295 294 u64 ofs = dso->data.debug_frame_offset; 296 295 297 296 if (ofs == 0) { 298 - fd = dso__data_fd(dso, machine); 297 + fd = dso__data_get_fd(dso, machine); 299 298 if (fd < 0) 300 299 return -EINVAL; 301 300 302 301 /* Check the .debug_frame section for unwinding info */ 303 302 ofs = elf_section_offset(fd, ".debug_frame"); 304 303 dso->data.debug_frame_offset = ofs; 304 + dso__data_put_fd(dso); 305 305 } 306 306 307 307 *offset = ofs; ··· 355 353 #ifndef NO_LIBUNWIND_DEBUG_FRAME 356 354 /* Check the .debug_frame section for unwinding info */ 357 355 if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { 358 - int fd = dso__data_fd(map->dso, ui->machine); 356 + int fd = dso__data_get_fd(map->dso, ui->machine); 359 357 int is_exec = elf_is_exec(fd, map->dso->name); 360 358 unw_word_t base = is_exec ? 0 : map->start; 359 + 360 + if (fd >= 0) 361 + dso__data_put_fd(dso); 361 362 362 363 memset(&di, 0, sizeof(di)); 363 364 if (dwarf_find_debug_frame(0, &di, ip, base, map->dso->name,