aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/perf/arch/powerpc/Makefile1
-rw-r--r--tools/perf/arch/powerpc/util/dwarf-regs.c40
-rw-r--r--tools/perf/arch/powerpc/util/sym-handling.c43
-rw-r--r--tools/perf/builtin-diff.c4
-rw-r--r--tools/perf/builtin-report.c4
-rw-r--r--tools/perf/builtin-top.c8
-rw-r--r--tools/perf/builtin-trace.c87
-rw-r--r--tools/perf/tests/hists_common.c2
-rw-r--r--tools/perf/tests/hists_cumulate.c2
-rw-r--r--tools/perf/tests/hists_link.c4
-rw-r--r--tools/perf/tests/hists_output.c2
-rw-r--r--tools/perf/ui/browsers/hists.c32
-rw-r--r--tools/perf/ui/gtk/hists.c2
-rw-r--r--tools/perf/ui/hist.c2
-rw-r--r--tools/perf/util/annotate.c2
-rw-r--r--tools/perf/util/callchain.c2
-rw-r--r--tools/perf/util/evlist.c56
-rw-r--r--tools/perf/util/hist.c14
-rw-r--r--tools/perf/util/hist.h10
-rw-r--r--tools/perf/util/machine.c9
-rw-r--r--tools/perf/util/machine.h1
-rw-r--r--tools/perf/util/probe-event.c5
-rw-r--r--tools/perf/util/probe-event.h3
-rw-r--r--tools/perf/util/rb_resort.h149
-rw-r--r--tools/perf/util/sort.c35
-rw-r--r--tools/perf/util/sort.h7
-rw-r--r--tools/perf/util/symbol-elf.c7
-rw-r--r--tools/perf/util/symbol.h3
28 files changed, 382 insertions, 154 deletions
diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile
index 56e05f126ad8..cc3930904d68 100644
--- a/tools/perf/arch/powerpc/Makefile
+++ b/tools/perf/arch/powerpc/Makefile
@@ -3,4 +3,5 @@ PERF_HAVE_DWARF_REGS := 1
3endif 3endif
4 4
5HAVE_KVM_STAT_SUPPORT := 1 5HAVE_KVM_STAT_SUPPORT := 1
6PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
6PERF_HAVE_JITDUMP := 1 7PERF_HAVE_JITDUMP := 1
diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c
index 733151cdf46e..41bdf9530d82 100644
--- a/tools/perf/arch/powerpc/util/dwarf-regs.c
+++ b/tools/perf/arch/powerpc/util/dwarf-regs.c
@@ -10,19 +10,26 @@
10 */ 10 */
11 11
12#include <stddef.h> 12#include <stddef.h>
13#include <errno.h>
14#include <string.h>
13#include <dwarf-regs.h> 15#include <dwarf-regs.h>
14 16#include <linux/ptrace.h>
17#include <linux/kernel.h>
18#include "util.h"
15 19
16struct pt_regs_dwarfnum { 20struct pt_regs_dwarfnum {
17 const char *name; 21 const char *name;
18 unsigned int dwarfnum; 22 unsigned int dwarfnum;
23 unsigned int ptregs_offset;
19}; 24};
20 25
21#define STR(s) #s 26#define REG_DWARFNUM_NAME(r, num) \
22#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num} 27 {.name = STR(%)STR(r), .dwarfnum = num, \
23#define GPR_DWARFNUM_NAME(num) \ 28 .ptregs_offset = offsetof(struct pt_regs, r)}
24 {.name = STR(%gpr##num), .dwarfnum = num} 29#define GPR_DWARFNUM_NAME(num) \
25#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0} 30 {.name = STR(%gpr##num), .dwarfnum = num, \
31 .ptregs_offset = offsetof(struct pt_regs, gpr[num])}
32#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0, .ptregs_offset = 0}
26 33
27/* 34/*
28 * Reference: 35 * Reference:
@@ -61,12 +68,12 @@ static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
61 GPR_DWARFNUM_NAME(29), 68 GPR_DWARFNUM_NAME(29),
62 GPR_DWARFNUM_NAME(30), 69 GPR_DWARFNUM_NAME(30),
63 GPR_DWARFNUM_NAME(31), 70 GPR_DWARFNUM_NAME(31),
64 REG_DWARFNUM_NAME("%msr", 66), 71 REG_DWARFNUM_NAME(msr, 66),
65 REG_DWARFNUM_NAME("%ctr", 109), 72 REG_DWARFNUM_NAME(ctr, 109),
66 REG_DWARFNUM_NAME("%link", 108), 73 REG_DWARFNUM_NAME(link, 108),
67 REG_DWARFNUM_NAME("%xer", 101), 74 REG_DWARFNUM_NAME(xer, 101),
68 REG_DWARFNUM_NAME("%dar", 119), 75 REG_DWARFNUM_NAME(dar, 119),
69 REG_DWARFNUM_NAME("%dsisr", 118), 76 REG_DWARFNUM_NAME(dsisr, 118),
70 REG_DWARFNUM_END, 77 REG_DWARFNUM_END,
71}; 78};
72 79
@@ -86,3 +93,12 @@ const char *get_arch_regstr(unsigned int n)
86 return roff->name; 93 return roff->name;
87 return NULL; 94 return NULL;
88} 95}
96
97int regs_query_register_offset(const char *name)
98{
99 const struct pt_regs_dwarfnum *roff;
100 for (roff = regdwarfnum_table; roff->name != NULL; roff++)
101 if (!strcmp(roff->name, name))
102 return roff->ptregs_offset;
103 return -EINVAL;
104}
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index bbc1a50768dd..c6d0f91731a1 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -19,12 +19,6 @@ bool elf__needs_adjust_symbols(GElf_Ehdr ehdr)
19 ehdr.e_type == ET_DYN; 19 ehdr.e_type == ET_DYN;
20} 20}
21 21
22#if defined(_CALL_ELF) && _CALL_ELF == 2
23void arch__elf_sym_adjust(GElf_Sym *sym)
24{
25 sym->st_value += PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
26}
27#endif
28#endif 22#endif
29 23
30#if !defined(_CALL_ELF) || _CALL_ELF != 2 24#if !defined(_CALL_ELF) || _CALL_ELF != 2
@@ -65,18 +59,45 @@ bool arch__prefers_symtab(void)
65 return true; 59 return true;
66} 60}
67 61
62#ifdef HAVE_LIBELF_SUPPORT
63void arch__sym_update(struct symbol *s, GElf_Sym *sym)
64{
65 s->arch_sym = sym->st_other;
66}
67#endif
68
68#define PPC64LE_LEP_OFFSET 8 69#define PPC64LE_LEP_OFFSET 8
69 70
70void arch__fix_tev_from_maps(struct perf_probe_event *pev, 71void arch__fix_tev_from_maps(struct perf_probe_event *pev,
71 struct probe_trace_event *tev, struct map *map) 72 struct probe_trace_event *tev, struct map *map,
73 struct symbol *sym)
72{ 74{
75 int lep_offset;
76
73 /* 77 /*
74 * ppc64 ABIv2 local entry point is currently always 2 instructions 78 * When probing at a function entry point, we normally always want the
75 * (8 bytes) after the global entry point. 79 * LEP since that catches calls to the function through both the GEP and
80 * the LEP. Hence, we would like to probe at an offset of 8 bytes if
81 * the user only specified the function entry.
82 *
83 * However, if the user specifies an offset, we fall back to using the
84 * GEP since all userspace applications (objdump/readelf) show function
85 * disassembly with offsets from the GEP.
86 *
87 * In addition, we shouldn't specify an offset for kretprobes.
76 */ 88 */
77 if (!pev->uprobes && map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) { 89 if (pev->point.offset || pev->point.retprobe || !map || !sym)
78 tev->point.address += PPC64LE_LEP_OFFSET; 90 return;
91
92 lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
93
94 if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS)
79 tev->point.offset += PPC64LE_LEP_OFFSET; 95 tev->point.offset += PPC64LE_LEP_OFFSET;
96 else if (lep_offset) {
97 if (pev->uprobes)
98 tev->point.address += lep_offset;
99 else
100 tev->point.offset += lep_offset;
80 } 101 }
81} 102}
82#endif 103#endif
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index 8053a8ceefda..9ce354f469dc 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -428,7 +428,7 @@ static void hists__baseline_only(struct hists *hists)
428 struct rb_root *root; 428 struct rb_root *root;
429 struct rb_node *next; 429 struct rb_node *next;
430 430
431 if (sort__need_collapse) 431 if (hists__has(hists, need_collapse))
432 root = &hists->entries_collapsed; 432 root = &hists->entries_collapsed;
433 else 433 else
434 root = hists->entries_in; 434 root = hists->entries_in;
@@ -450,7 +450,7 @@ static void hists__precompute(struct hists *hists)
450 struct rb_root *root; 450 struct rb_root *root;
451 struct rb_node *next; 451 struct rb_node *next;
452 452
453 if (sort__need_collapse) 453 if (hists__has(hists, need_collapse))
454 root = &hists->entries_collapsed; 454 root = &hists->entries_collapsed;
455 else 455 else
456 root = hists->entries_in; 456 root = hists->entries_in;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 8d9b88af901d..87d40e3c4078 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -234,7 +234,7 @@ static int report__setup_sample_type(struct report *rep)
234 sample_type |= PERF_SAMPLE_BRANCH_STACK; 234 sample_type |= PERF_SAMPLE_BRANCH_STACK;
235 235
236 if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { 236 if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
237 if (sort__has_parent) { 237 if (perf_hpp_list.parent) {
238 ui__error("Selected --sort parent, but no " 238 ui__error("Selected --sort parent, but no "
239 "callchain data. Did you call " 239 "callchain data. Did you call "
240 "'perf record' without -g?\n"); 240 "'perf record' without -g?\n");
@@ -936,7 +936,7 @@ repeat:
936 goto error; 936 goto error;
937 } 937 }
938 938
939 sort__need_collapse = true; 939 perf_hpp_list.need_collapse = true;
940 } 940 }
941 941
942 /* Force tty output for header output and per-thread stat. */ 942 /* Force tty output for header output and per-thread stat. */
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index da18517b1d40..1793da585676 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -688,7 +688,7 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
688 struct hist_entry *he = iter->he; 688 struct hist_entry *he = iter->he;
689 struct perf_evsel *evsel = iter->evsel; 689 struct perf_evsel *evsel = iter->evsel;
690 690
691 if (sort__has_sym && single) 691 if (perf_hpp_list.sym && single)
692 perf_top__record_precise_ip(top, he, evsel->idx, al->addr); 692 perf_top__record_precise_ip(top, he, evsel->idx, al->addr);
693 693
694 hist__account_cycles(iter->sample->branch_stack, al, iter->sample, 694 hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
@@ -919,7 +919,7 @@ out_err:
919 919
920static int callchain_param__setup_sample_type(struct callchain_param *callchain) 920static int callchain_param__setup_sample_type(struct callchain_param *callchain)
921{ 921{
922 if (!sort__has_sym) { 922 if (!perf_hpp_list.sym) {
923 if (callchain->enabled) { 923 if (callchain->enabled) {
924 ui__error("Selected -g but \"sym\" not present in --sort/-s."); 924 ui__error("Selected -g but \"sym\" not present in --sort/-s.");
925 return -EINVAL; 925 return -EINVAL;
@@ -962,7 +962,7 @@ static int __cmd_top(struct perf_top *top)
962 machine__synthesize_threads(&top->session->machines.host, &opts->target, 962 machine__synthesize_threads(&top->session->machines.host, &opts->target,
963 top->evlist->threads, false, opts->proc_map_timeout); 963 top->evlist->threads, false, opts->proc_map_timeout);
964 964
965 if (sort__has_socket) { 965 if (perf_hpp_list.socket) {
966 ret = perf_env__read_cpu_topology_map(&perf_env); 966 ret = perf_env__read_cpu_topology_map(&perf_env);
967 if (ret < 0) 967 if (ret < 0)
968 goto out_err_cpu_topo; 968 goto out_err_cpu_topo;
@@ -1255,7 +1255,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1255 1255
1256 sort__mode = SORT_MODE__TOP; 1256 sort__mode = SORT_MODE__TOP;
1257 /* display thread wants entries to be collapsed in a different tree */ 1257 /* display thread wants entries to be collapsed in a different tree */
1258 sort__need_collapse = 1; 1258 perf_hpp_list.need_collapse = 1;
1259 1259
1260 if (top.use_stdio) 1260 if (top.use_stdio)
1261 use_browser = 0; 1261 use_browser = 0;
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 9e38fe973f0c..66aa2a00414b 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -36,6 +36,7 @@
36#include "util/bpf-loader.h" 36#include "util/bpf-loader.h"
37#include "callchain.h" 37#include "callchain.h"
38#include "syscalltbl.h" 38#include "syscalltbl.h"
39#include "rb_resort.h"
39 40
40#include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */ 41#include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */
41#include <stdlib.h> 42#include <stdlib.h>
@@ -2784,15 +2785,29 @@ static size_t trace__fprintf_threads_header(FILE *fp)
2784 return printed; 2785 return printed;
2785} 2786}
2786 2787
2788DEFINE_RESORT_RB(syscall_stats, a->msecs > b->msecs,
2789 struct stats *stats;
2790 double msecs;
2791 int syscall;
2792)
2793{
2794 struct int_node *source = rb_entry(nd, struct int_node, rb_node);
2795 struct stats *stats = source->priv;
2796
2797 entry->syscall = source->i;
2798 entry->stats = stats;
2799 entry->msecs = stats ? (u64)stats->n * (avg_stats(stats) / NSEC_PER_MSEC) : 0;
2800}
2801
2787static size_t thread__dump_stats(struct thread_trace *ttrace, 2802static size_t thread__dump_stats(struct thread_trace *ttrace,
2788 struct trace *trace, FILE *fp) 2803 struct trace *trace, FILE *fp)
2789{ 2804{
2790 struct stats *stats;
2791 size_t printed = 0; 2805 size_t printed = 0;
2792 struct syscall *sc; 2806 struct syscall *sc;
2793 struct int_node *inode = intlist__first(ttrace->syscall_stats); 2807 struct rb_node *nd;
2808 DECLARE_RESORT_RB_INTLIST(syscall_stats, ttrace->syscall_stats);
2794 2809
2795 if (inode == NULL) 2810 if (syscall_stats == NULL)
2796 return 0; 2811 return 0;
2797 2812
2798 printed += fprintf(fp, "\n"); 2813 printed += fprintf(fp, "\n");
@@ -2801,9 +2816,8 @@ static size_t thread__dump_stats(struct thread_trace *ttrace,
2801 printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n"); 2816 printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n");
2802 printed += fprintf(fp, " --------------- -------- --------- --------- --------- --------- ------\n"); 2817 printed += fprintf(fp, " --------------- -------- --------- --------- --------- --------- ------\n");
2803 2818
2804 /* each int_node is a syscall */ 2819 resort_rb__for_each(nd, syscall_stats) {
2805 while (inode) { 2820 struct stats *stats = syscall_stats_entry->stats;
2806 stats = inode->priv;
2807 if (stats) { 2821 if (stats) {
2808 double min = (double)(stats->min) / NSEC_PER_MSEC; 2822 double min = (double)(stats->min) / NSEC_PER_MSEC;
2809 double max = (double)(stats->max) / NSEC_PER_MSEC; 2823 double max = (double)(stats->max) / NSEC_PER_MSEC;
@@ -2814,34 +2828,23 @@ static size_t thread__dump_stats(struct thread_trace *ttrace,
2814 pct = avg ? 100.0 * stddev_stats(stats)/avg : 0.0; 2828 pct = avg ? 100.0 * stddev_stats(stats)/avg : 0.0;
2815 avg /= NSEC_PER_MSEC; 2829 avg /= NSEC_PER_MSEC;
2816 2830
2817 sc = &trace->syscalls.table[inode->i]; 2831 sc = &trace->syscalls.table[syscall_stats_entry->syscall];
2818 printed += fprintf(fp, " %-15s", sc->name); 2832 printed += fprintf(fp, " %-15s", sc->name);
2819 printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f %9.3f", 2833 printed += fprintf(fp, " %8" PRIu64 " %9.3f %9.3f %9.3f",
2820 n, avg * n, min, avg); 2834 n, syscall_stats_entry->msecs, min, avg);
2821 printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct); 2835 printed += fprintf(fp, " %9.3f %9.2f%%\n", max, pct);
2822 } 2836 }
2823
2824 inode = intlist__next(inode);
2825 } 2837 }
2826 2838
2839 resort_rb__delete(syscall_stats);
2827 printed += fprintf(fp, "\n\n"); 2840 printed += fprintf(fp, "\n\n");
2828 2841
2829 return printed; 2842 return printed;
2830} 2843}
2831 2844
2832/* struct used to pass data to per-thread function */ 2845static size_t trace__fprintf_thread(FILE *fp, struct thread *thread, struct trace *trace)
2833struct summary_data {
2834 FILE *fp;
2835 struct trace *trace;
2836 size_t printed;
2837};
2838
2839static int trace__fprintf_one_thread(struct thread *thread, void *priv)
2840{ 2846{
2841 struct summary_data *data = priv; 2847 size_t printed = 0;
2842 FILE *fp = data->fp;
2843 size_t printed = data->printed;
2844 struct trace *trace = data->trace;
2845 struct thread_trace *ttrace = thread__priv(thread); 2848 struct thread_trace *ttrace = thread__priv(thread);
2846 double ratio; 2849 double ratio;
2847 2850
@@ -2857,25 +2860,45 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
2857 printed += fprintf(fp, ", %lu majfaults", ttrace->pfmaj); 2860 printed += fprintf(fp, ", %lu majfaults", ttrace->pfmaj);
2858 if (ttrace->pfmin) 2861 if (ttrace->pfmin)
2859 printed += fprintf(fp, ", %lu minfaults", ttrace->pfmin); 2862 printed += fprintf(fp, ", %lu minfaults", ttrace->pfmin);
2860 printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms); 2863 if (trace->sched)
2864 printed += fprintf(fp, ", %.3f msec\n", ttrace->runtime_ms);
2865 else if (fputc('\n', fp) != EOF)
2866 ++printed;
2867
2861 printed += thread__dump_stats(ttrace, trace, fp); 2868 printed += thread__dump_stats(ttrace, trace, fp);
2862 2869
2863 data->printed += printed; 2870 return printed;
2871}
2872
2873static unsigned long thread__nr_events(struct thread_trace *ttrace)
2874{
2875 return ttrace ? ttrace->nr_events : 0;
2876}
2864 2877
2865 return 0; 2878DEFINE_RESORT_RB(threads, (thread__nr_events(a->thread->priv) < thread__nr_events(b->thread->priv)),
2879 struct thread *thread;
2880)
2881{
2882 entry->thread = rb_entry(nd, struct thread, rb_node);
2866} 2883}
2867 2884
2868static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) 2885static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp)
2869{ 2886{
2870 struct summary_data data = { 2887 DECLARE_RESORT_RB_MACHINE_THREADS(threads, trace->host);
2871 .fp = fp, 2888 size_t printed = trace__fprintf_threads_header(fp);
2872 .trace = trace 2889 struct rb_node *nd;
2873 }; 2890
2874 data.printed = trace__fprintf_threads_header(fp); 2891 if (threads == NULL) {
2892 fprintf(fp, "%s", "Error sorting output by nr_events!\n");
2893 return 0;
2894 }
2895
2896 resort_rb__for_each(nd, threads)
2897 printed += trace__fprintf_thread(fp, threads_entry->thread, trace);
2875 2898
2876 machine__for_each_thread(trace->host, trace__fprintf_one_thread, &data); 2899 resort_rb__delete(threads);
2877 2900
2878 return data.printed; 2901 return printed;
2879} 2902}
2880 2903
2881static int trace__set_duration(const struct option *opt, const char *str, 2904static int trace__set_duration(const struct option *opt, const char *str,
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c
index f55f4bd47932..6b21746d6eec 100644
--- a/tools/perf/tests/hists_common.c
+++ b/tools/perf/tests/hists_common.c
@@ -161,7 +161,7 @@ void print_hists_in(struct hists *hists)
161 struct rb_root *root; 161 struct rb_root *root;
162 struct rb_node *node; 162 struct rb_node *node;
163 163
164 if (sort__need_collapse) 164 if (hists__has(hists, need_collapse))
165 root = &hists->entries_collapsed; 165 root = &hists->entries_collapsed;
166 else 166 else
167 root = hists->entries_in; 167 root = hists->entries_in;
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c
index 4a2bbff9b1ee..a9e3db3afac4 100644
--- a/tools/perf/tests/hists_cumulate.c
+++ b/tools/perf/tests/hists_cumulate.c
@@ -126,7 +126,7 @@ static void del_hist_entries(struct hists *hists)
126 struct rb_root *root_out; 126 struct rb_root *root_out;
127 struct rb_node *node; 127 struct rb_node *node;
128 128
129 if (sort__need_collapse) 129 if (hists__has(hists, need_collapse))
130 root_in = &hists->entries_collapsed; 130 root_in = &hists->entries_collapsed;
131 else 131 else
132 root_in = hists->entries_in; 132 root_in = hists->entries_in;
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c
index 358324e47805..acf5a1301c07 100644
--- a/tools/perf/tests/hists_link.c
+++ b/tools/perf/tests/hists_link.c
@@ -145,7 +145,7 @@ static int __validate_match(struct hists *hists)
145 /* 145 /*
146 * Only entries from fake_common_samples should have a pair. 146 * Only entries from fake_common_samples should have a pair.
147 */ 147 */
148 if (sort__need_collapse) 148 if (hists__has(hists, need_collapse))
149 root = &hists->entries_collapsed; 149 root = &hists->entries_collapsed;
150 else 150 else
151 root = hists->entries_in; 151 root = hists->entries_in;
@@ -197,7 +197,7 @@ static int __validate_link(struct hists *hists, int idx)
197 * and some entries will have no pair. However every entry 197 * and some entries will have no pair. However every entry
198 * in other hists should have (dummy) pair. 198 * in other hists should have (dummy) pair.
199 */ 199 */
200 if (sort__need_collapse) 200 if (hists__has(hists, need_collapse))
201 root = &hists->entries_collapsed; 201 root = &hists->entries_collapsed;
202 else 202 else
203 root = hists->entries_in; 203 root = hists->entries_in;
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c
index 7cd8738e842f..63c5efaba1b5 100644
--- a/tools/perf/tests/hists_output.c
+++ b/tools/perf/tests/hists_output.c
@@ -92,7 +92,7 @@ static void del_hist_entries(struct hists *hists)
92 struct rb_root *root_out; 92 struct rb_root *root_out;
93 struct rb_node *node; 93 struct rb_node *node;
94 94
95 if (sort__need_collapse) 95 if (hists__has(hists, need_collapse))
96 root_in = &hists->entries_collapsed; 96 root_in = &hists->entries_collapsed;
97 else 97 else
98 root_in = hists->entries_in; 98 root_in = hists->entries_in;
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 6a4681932ba5..538bae880bfe 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -2135,7 +2135,7 @@ static int hists__browser_title(struct hists *hists,
2135 printed += snprintf(bf + printed, size - printed, 2135 printed += snprintf(bf + printed, size - printed,
2136 ", UID: %s", hists->uid_filter_str); 2136 ", UID: %s", hists->uid_filter_str);
2137 if (thread) { 2137 if (thread) {
2138 if (sort__has_thread) { 2138 if (hists__has(hists, thread)) {
2139 printed += scnprintf(bf + printed, size - printed, 2139 printed += scnprintf(bf + printed, size - printed,
2140 ", Thread: %s(%d)", 2140 ", Thread: %s(%d)",
2141 (thread->comm_set ? thread__comm_str(thread) : ""), 2141 (thread->comm_set ? thread__comm_str(thread) : ""),
@@ -2320,7 +2320,8 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2320{ 2320{
2321 struct thread *thread = act->thread; 2321 struct thread *thread = act->thread;
2322 2322
2323 if ((!sort__has_thread && !sort__has_comm) || thread == NULL) 2323 if ((!hists__has(browser->hists, thread) &&
2324 !hists__has(browser->hists, comm)) || thread == NULL)
2324 return 0; 2325 return 0;
2325 2326
2326 if (browser->hists->thread_filter) { 2327 if (browser->hists->thread_filter) {
@@ -2329,7 +2330,7 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
2329 thread__zput(browser->hists->thread_filter); 2330 thread__zput(browser->hists->thread_filter);
2330 ui_helpline__pop(); 2331 ui_helpline__pop();
2331 } else { 2332 } else {
2332 if (sort__has_thread) { 2333 if (hists__has(browser->hists, thread)) {
2333 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"", 2334 ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
2334 thread->comm_set ? thread__comm_str(thread) : "", 2335 thread->comm_set ? thread__comm_str(thread) : "",
2335 thread->tid); 2336 thread->tid);
@@ -2354,10 +2355,11 @@ add_thread_opt(struct hist_browser *browser, struct popup_action *act,
2354{ 2355{
2355 int ret; 2356 int ret;
2356 2357
2357 if ((!sort__has_thread && !sort__has_comm) || thread == NULL) 2358 if ((!hists__has(browser->hists, thread) &&
2359 !hists__has(browser->hists, comm)) || thread == NULL)
2358 return 0; 2360 return 0;
2359 2361
2360 if (sort__has_thread) { 2362 if (hists__has(browser->hists, thread)) {
2361 ret = asprintf(optstr, "Zoom %s %s(%d) thread", 2363 ret = asprintf(optstr, "Zoom %s %s(%d) thread",
2362 browser->hists->thread_filter ? "out of" : "into", 2364 browser->hists->thread_filter ? "out of" : "into",
2363 thread->comm_set ? thread__comm_str(thread) : "", 2365 thread->comm_set ? thread__comm_str(thread) : "",
@@ -2380,7 +2382,7 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
2380{ 2382{
2381 struct map *map = act->ms.map; 2383 struct map *map = act->ms.map;
2382 2384
2383 if (!sort__has_dso || map == NULL) 2385 if (!hists__has(browser->hists, dso) || map == NULL)
2384 return 0; 2386 return 0;
2385 2387
2386 if (browser->hists->dso_filter) { 2388 if (browser->hists->dso_filter) {
@@ -2407,7 +2409,7 @@ static int
2407add_dso_opt(struct hist_browser *browser, struct popup_action *act, 2409add_dso_opt(struct hist_browser *browser, struct popup_action *act,
2408 char **optstr, struct map *map) 2410 char **optstr, struct map *map)
2409{ 2411{
2410 if (!sort__has_dso || map == NULL) 2412 if (!hists__has(browser->hists, dso) || map == NULL)
2411 return 0; 2413 return 0;
2412 2414
2413 if (asprintf(optstr, "Zoom %s %s DSO", 2415 if (asprintf(optstr, "Zoom %s %s DSO",
@@ -2429,10 +2431,10 @@ do_browse_map(struct hist_browser *browser __maybe_unused,
2429} 2431}
2430 2432
2431static int 2433static int
2432add_map_opt(struct hist_browser *browser __maybe_unused, 2434add_map_opt(struct hist_browser *browser,
2433 struct popup_action *act, char **optstr, struct map *map) 2435 struct popup_action *act, char **optstr, struct map *map)
2434{ 2436{
2435 if (!sort__has_dso || map == NULL) 2437 if (!hists__has(browser->hists, dso) || map == NULL)
2436 return 0; 2438 return 0;
2437 2439
2438 if (asprintf(optstr, "Browse map details") < 0) 2440 if (asprintf(optstr, "Browse map details") < 0)
@@ -2534,7 +2536,7 @@ add_exit_opt(struct hist_browser *browser __maybe_unused,
2534static int 2536static int
2535do_zoom_socket(struct hist_browser *browser, struct popup_action *act) 2537do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
2536{ 2538{
2537 if (!sort__has_socket || act->socket < 0) 2539 if (!hists__has(browser->hists, socket) || act->socket < 0)
2538 return 0; 2540 return 0;
2539 2541
2540 if (browser->hists->socket_filter > -1) { 2542 if (browser->hists->socket_filter > -1) {
@@ -2556,7 +2558,7 @@ static int
2556add_socket_opt(struct hist_browser *browser, struct popup_action *act, 2558add_socket_opt(struct hist_browser *browser, struct popup_action *act,
2557 char **optstr, int socket_id) 2559 char **optstr, int socket_id)
2558{ 2560{
2559 if (!sort__has_socket || socket_id < 0) 2561 if (!hists__has(browser->hists, socket) || socket_id < 0)
2560 return 0; 2562 return 0;
2561 2563
2562 if (asprintf(optstr, "Zoom %s Processor Socket %d", 2564 if (asprintf(optstr, "Zoom %s Processor Socket %d",
@@ -2747,7 +2749,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2747 */ 2749 */
2748 goto out_free_stack; 2750 goto out_free_stack;
2749 case 'a': 2751 case 'a':
2750 if (!sort__has_sym) { 2752 if (!hists__has(hists, sym)) {
2751 ui_browser__warning(&browser->b, delay_secs * 2, 2753 ui_browser__warning(&browser->b, delay_secs * 2,
2752 "Annotation is only available for symbolic views, " 2754 "Annotation is only available for symbolic views, "
2753 "include \"sym*\" in --sort to use it."); 2755 "include \"sym*\" in --sort to use it.");
@@ -2910,7 +2912,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
2910 continue; 2912 continue;
2911 } 2913 }
2912 2914
2913 if (!sort__has_sym || browser->selection == NULL) 2915 if (!hists__has(hists, sym) || browser->selection == NULL)
2914 goto skip_annotation; 2916 goto skip_annotation;
2915 2917
2916 if (sort__mode == SORT_MODE__BRANCH) { 2918 if (sort__mode == SORT_MODE__BRANCH) {
@@ -2954,7 +2956,7 @@ skip_annotation:
2954 goto skip_scripting; 2956 goto skip_scripting;
2955 2957
2956 if (browser->he_selection) { 2958 if (browser->he_selection) {
2957 if (sort__has_thread && thread) { 2959 if (hists__has(hists, thread) && thread) {
2958 nr_options += add_script_opt(browser, 2960 nr_options += add_script_opt(browser,
2959 &actions[nr_options], 2961 &actions[nr_options],
2960 &options[nr_options], 2962 &options[nr_options],
@@ -2969,7 +2971,7 @@ skip_annotation:
2969 * 2971 *
2970 * See hist_browser__show_entry. 2972 * See hist_browser__show_entry.
2971 */ 2973 */
2972 if (sort__has_sym && browser->selection->sym) { 2974 if (hists__has(hists, sym) && browser->selection->sym) {
2973 nr_options += add_script_opt(browser, 2975 nr_options += add_script_opt(browser,
2974 &actions[nr_options], 2976 &actions[nr_options],
2975 &options[nr_options], 2977 &options[nr_options],
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index 2aa45b606fa4..932adfaa05af 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -379,7 +379,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
379 gtk_tree_store_set(store, &iter, col_idx++, s, -1); 379 gtk_tree_store_set(store, &iter, col_idx++, s, -1);
380 } 380 }
381 381
382 if (symbol_conf.use_callchain && sort__has_sym) { 382 if (symbol_conf.use_callchain && hists__has(hists, sym)) {
383 if (callchain_param.mode == CHAIN_GRAPH_REL) 383 if (callchain_param.mode == CHAIN_GRAPH_REL)
384 total = symbol_conf.cumulate_callchain ? 384 total = symbol_conf.cumulate_callchain ?
385 h->stat_acc->period : h->stat.period; 385 h->stat_acc->period : h->stat.period;
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 3baeaa6e71b5..af07ffb129ca 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -635,7 +635,7 @@ unsigned int hists__sort_list_width(struct hists *hists)
635 ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); 635 ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
636 } 636 }
637 637
638 if (verbose && sort__has_sym) /* Addr + origin */ 638 if (verbose && hists__has(hists, sym)) /* Addr + origin */
639 ret += 3 + BITS_PER_LONG / 4; 639 ret += 3 + BITS_PER_LONG / 4;
640 640
641 return ret; 641 return ret;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index b795b6994144..d4b3d034c503 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1665,5 +1665,5 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize)
1665 1665
1666bool ui__has_annotation(void) 1666bool ui__has_annotation(void)
1667{ 1667{
1668 return use_browser == 1 && sort__has_sym; 1668 return use_browser == 1 && perf_hpp_list.sym;
1669} 1669}
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index aa248dcb4440..07fd30bc2f81 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -799,7 +799,7 @@ int sample__resolve_callchain(struct perf_sample *sample,
799 return 0; 799 return 0;
800 800
801 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || 801 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
802 sort__has_parent) { 802 perf_hpp_list.parent) {
803 return thread__resolve_callchain(al->thread, cursor, evsel, sample, 803 return thread__resolve_callchain(al->thread, cursor, evsel, sample,
804 parent, al, max_stack); 804 parent, al, max_stack);
805 } 805 }
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 85271e54a63b..17cd01421e7f 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -679,39 +679,31 @@ static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist,
679 return NULL; 679 return NULL;
680} 680}
681 681
682union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) 682/* When check_messup is true, 'end' must points to a good entry */
683static union perf_event *
684perf_mmap__read(struct perf_mmap *md, bool check_messup, u64 start,
685 u64 end, u64 *prev)
683{ 686{
684 struct perf_mmap *md = &evlist->mmap[idx];
685 u64 head;
686 u64 old = md->prev;
687 int diff;
688 unsigned char *data = md->base + page_size; 687 unsigned char *data = md->base + page_size;
689 union perf_event *event = NULL; 688 union perf_event *event = NULL;
689 int diff = end - start;
690 690
691 /* 691 if (check_messup) {
692 * Check if event was unmapped due to a POLLHUP/POLLERR.
693 */
694 if (!atomic_read(&md->refcnt))
695 return NULL;
696
697 head = perf_mmap__read_head(md);
698 diff = head - old;
699 if (evlist->overwrite) {
700 /* 692 /*
701 * If we're further behind than half the buffer, there's a chance 693 * If we're further behind than half the buffer, there's a chance
702 * the writer will bite our tail and mess up the samples under us. 694 * the writer will bite our tail and mess up the samples under us.
703 * 695 *
704 * If we somehow ended up ahead of the head, we got messed up. 696 * If we somehow ended up ahead of the 'end', we got messed up.
705 * 697 *
706 * In either case, truncate and restart at head. 698 * In either case, truncate and restart at 'end'.
707 */ 699 */
708 if (diff > md->mask / 2 || diff < 0) { 700 if (diff > md->mask / 2 || diff < 0) {
709 fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); 701 fprintf(stderr, "WARNING: failed to keep up with mmap data.\n");
710 702
711 /* 703 /*
712 * head points to a known good entry, start there. 704 * 'end' points to a known good entry, start there.
713 */ 705 */
714 old = head; 706 start = end;
715 diff = 0; 707 diff = 0;
716 } 708 }
717 } 709 }
@@ -719,7 +711,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
719 if (diff >= (int)sizeof(event->header)) { 711 if (diff >= (int)sizeof(event->header)) {
720 size_t size; 712 size_t size;
721 713
722 event = (union perf_event *)&data[old & md->mask]; 714 event = (union perf_event *)&data[start & md->mask];
723 size = event->header.size; 715 size = event->header.size;
724 716
725 if (size < sizeof(event->header) || diff < (int)size) { 717 if (size < sizeof(event->header) || diff < (int)size) {
@@ -731,8 +723,8 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
731 * Event straddles the mmap boundary -- header should always 723 * Event straddles the mmap boundary -- header should always
732 * be inside due to u64 alignment of output. 724 * be inside due to u64 alignment of output.
733 */ 725 */
734 if ((old & md->mask) + size != ((old + size) & md->mask)) { 726 if ((start & md->mask) + size != ((start + size) & md->mask)) {
735 unsigned int offset = old; 727 unsigned int offset = start;
736 unsigned int len = min(sizeof(*event), size), cpy; 728 unsigned int len = min(sizeof(*event), size), cpy;
737 void *dst = md->event_copy; 729 void *dst = md->event_copy;
738 730
@@ -747,15 +739,33 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
747 event = (union perf_event *) md->event_copy; 739 event = (union perf_event *) md->event_copy;
748 } 740 }
749 741
750 old += size; 742 start += size;
751 } 743 }
752 744
753broken_event: 745broken_event:
754 md->prev = old; 746 if (prev)
747 *prev = start;
755 748
756 return event; 749 return event;
757} 750}
758 751
752union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
753{
754 struct perf_mmap *md = &evlist->mmap[idx];
755 u64 head;
756 u64 old = md->prev;
757
758 /*
759 * Check if event was unmapped due to a POLLHUP/POLLERR.
760 */
761 if (!atomic_read(&md->refcnt))
762 return NULL;
763
764 head = perf_mmap__read_head(md);
765
766 return perf_mmap__read(md, evlist->overwrite, old, head, &md->prev);
767}
768
759static bool perf_mmap__empty(struct perf_mmap *md) 769static bool perf_mmap__empty(struct perf_mmap *md)
760{ 770{
761 return perf_mmap__read_head(md) == md->prev && !md->auxtrace_mmap.base; 771 return perf_mmap__read_head(md) == md->prev && !md->auxtrace_mmap.base;
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 0f33d7e698c4..cfab531437c7 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -295,7 +295,7 @@ static void hists__delete_entry(struct hists *hists, struct hist_entry *he)
295 root_in = &he->parent_he->hroot_in; 295 root_in = &he->parent_he->hroot_in;
296 root_out = &he->parent_he->hroot_out; 296 root_out = &he->parent_he->hroot_out;
297 } else { 297 } else {
298 if (sort__need_collapse) 298 if (hists__has(hists, need_collapse))
299 root_in = &hists->entries_collapsed; 299 root_in = &hists->entries_collapsed;
300 else 300 else
301 root_in = hists->entries_in; 301 root_in = hists->entries_in;
@@ -1373,7 +1373,7 @@ int hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
1373 struct hist_entry *n; 1373 struct hist_entry *n;
1374 int ret; 1374 int ret;
1375 1375
1376 if (!sort__need_collapse) 1376 if (!hists__has(hists, need_collapse))
1377 return 0; 1377 return 0;
1378 1378
1379 hists->nr_entries = 0; 1379 hists->nr_entries = 0;
@@ -1632,7 +1632,7 @@ static void output_resort(struct hists *hists, struct ui_progress *prog,
1632 return; 1632 return;
1633 } 1633 }
1634 1634
1635 if (sort__need_collapse) 1635 if (hists__has(hists, need_collapse))
1636 root = &hists->entries_collapsed; 1636 root = &hists->entries_collapsed;
1637 else 1637 else
1638 root = hists->entries_in; 1638 root = hists->entries_in;
@@ -2036,7 +2036,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
2036 struct hist_entry *he; 2036 struct hist_entry *he;
2037 int64_t cmp; 2037 int64_t cmp;
2038 2038
2039 if (sort__need_collapse) 2039 if (hists__has(hists, need_collapse))
2040 root = &hists->entries_collapsed; 2040 root = &hists->entries_collapsed;
2041 else 2041 else
2042 root = hists->entries_in; 2042 root = hists->entries_in;
@@ -2078,7 +2078,7 @@ static struct hist_entry *hists__find_entry(struct hists *hists,
2078{ 2078{
2079 struct rb_node *n; 2079 struct rb_node *n;
2080 2080
2081 if (sort__need_collapse) 2081 if (hists__has(hists, need_collapse))
2082 n = hists->entries_collapsed.rb_node; 2082 n = hists->entries_collapsed.rb_node;
2083 else 2083 else
2084 n = hists->entries_in->rb_node; 2084 n = hists->entries_in->rb_node;
@@ -2107,7 +2107,7 @@ void hists__match(struct hists *leader, struct hists *other)
2107 struct rb_node *nd; 2107 struct rb_node *nd;
2108 struct hist_entry *pos, *pair; 2108 struct hist_entry *pos, *pair;
2109 2109
2110 if (sort__need_collapse) 2110 if (hists__has(leader, need_collapse))
2111 root = &leader->entries_collapsed; 2111 root = &leader->entries_collapsed;
2112 else 2112 else
2113 root = leader->entries_in; 2113 root = leader->entries_in;
@@ -2132,7 +2132,7 @@ int hists__link(struct hists *leader, struct hists *other)
2132 struct rb_node *nd; 2132 struct rb_node *nd;
2133 struct hist_entry *pos, *pair; 2133 struct hist_entry *pos, *pair;
2134 2134
2135 if (sort__need_collapse) 2135 if (hists__has(other, need_collapse))
2136 root = &other->entries_collapsed; 2136 root = &other->entries_collapsed;
2137 else 2137 else
2138 root = other->entries_in; 2138 root = other->entries_in;
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 588596561cb3..0f84bfb42bb1 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -82,6 +82,8 @@ struct hists {
82 int nr_hpp_node; 82 int nr_hpp_node;
83}; 83};
84 84
85#define hists__has(__h, __f) (__h)->hpp_list->__f
86
85struct hist_entry_iter; 87struct hist_entry_iter;
86 88
87struct hist_iter_ops { 89struct hist_iter_ops {
@@ -238,6 +240,14 @@ struct perf_hpp_fmt {
238struct perf_hpp_list { 240struct perf_hpp_list {
239 struct list_head fields; 241 struct list_head fields;
240 struct list_head sorts; 242 struct list_head sorts;
243
244 int need_collapse;
245 int parent;
246 int sym;
247 int dso;
248 int socket;
249 int thread;
250 int comm;
241}; 251};
242 252
243extern struct perf_hpp_list perf_hpp_list; 253extern struct perf_hpp_list perf_hpp_list;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 2cb95bbf9ea6..8c7bf4dbd479 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -32,6 +32,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
32 32
33 machine->threads = RB_ROOT; 33 machine->threads = RB_ROOT;
34 pthread_rwlock_init(&machine->threads_lock, NULL); 34 pthread_rwlock_init(&machine->threads_lock, NULL);
35 machine->nr_threads = 0;
35 INIT_LIST_HEAD(&machine->dead_threads); 36 INIT_LIST_HEAD(&machine->dead_threads);
36 machine->last_match = NULL; 37 machine->last_match = NULL;
37 38
@@ -430,6 +431,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine,
430 */ 431 */
431 thread__get(th); 432 thread__get(th);
432 machine->last_match = th; 433 machine->last_match = th;
434 ++machine->nr_threads;
433 } 435 }
434 436
435 return th; 437 return th;
@@ -681,11 +683,13 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp)
681 683
682size_t machine__fprintf(struct machine *machine, FILE *fp) 684size_t machine__fprintf(struct machine *machine, FILE *fp)
683{ 685{
684 size_t ret = 0; 686 size_t ret;
685 struct rb_node *nd; 687 struct rb_node *nd;
686 688
687 pthread_rwlock_rdlock(&machine->threads_lock); 689 pthread_rwlock_rdlock(&machine->threads_lock);
688 690
691 ret = fprintf(fp, "Threads: %u\n", machine->nr_threads);
692
689 for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { 693 for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) {
690 struct thread *pos = rb_entry(nd, struct thread, rb_node); 694 struct thread *pos = rb_entry(nd, struct thread, rb_node);
691 695
@@ -1419,6 +1423,7 @@ static void __machine__remove_thread(struct machine *machine, struct thread *th,
1419 pthread_rwlock_wrlock(&machine->threads_lock); 1423 pthread_rwlock_wrlock(&machine->threads_lock);
1420 rb_erase_init(&th->rb_node, &machine->threads); 1424 rb_erase_init(&th->rb_node, &machine->threads);
1421 RB_CLEAR_NODE(&th->rb_node); 1425 RB_CLEAR_NODE(&th->rb_node);
1426 --machine->nr_threads;
1422 /* 1427 /*
1423 * Move it first to the dead_threads list, then drop the reference, 1428 * Move it first to the dead_threads list, then drop the reference,
1424 * if this is the last reference, then the thread__delete destructor 1429 * if this is the last reference, then the thread__delete destructor
@@ -1647,7 +1652,7 @@ static int add_callchain_ip(struct thread *thread,
1647 } 1652 }
1648 1653
1649 if (al.sym != NULL) { 1654 if (al.sym != NULL) {
1650 if (sort__has_parent && !*parent && 1655 if (perf_hpp_list.parent && !*parent &&
1651 symbol__match_regex(al.sym, &parent_regex)) 1656 symbol__match_regex(al.sym, &parent_regex))
1652 *parent = al.sym; 1657 *parent = al.sym;
1653 else if (have_ignore_callees && root_al && 1658 else if (have_ignore_callees && root_al &&
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 4822de5e4544..83f46790c52f 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -31,6 +31,7 @@ struct machine {
31 char *root_dir; 31 char *root_dir;
32 struct rb_root threads; 32 struct rb_root threads;
33 pthread_rwlock_t threads_lock; 33 pthread_rwlock_t threads_lock;
34 unsigned int nr_threads;
34 struct list_head dead_threads; 35 struct list_head dead_threads;
35 struct thread *last_match; 36 struct thread *last_match;
36 struct vdso_info *vdso_info; 37 struct vdso_info *vdso_info;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 85d82f4dc5e9..c82c625395ab 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2477,7 +2477,8 @@ static int find_probe_functions(struct map *map, char *name,
2477 2477
2478void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused, 2478void __weak arch__fix_tev_from_maps(struct perf_probe_event *pev __maybe_unused,
2479 struct probe_trace_event *tev __maybe_unused, 2479 struct probe_trace_event *tev __maybe_unused,
2480 struct map *map __maybe_unused) { } 2480 struct map *map __maybe_unused,
2481 struct symbol *sym __maybe_unused) { }
2481 2482
2482/* 2483/*
2483 * Find probe function addresses from map. 2484 * Find probe function addresses from map.
@@ -2614,7 +2615,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
2614 strdup_or_goto(pev->args[i].type, 2615 strdup_or_goto(pev->args[i].type,
2615 nomem_out); 2616 nomem_out);
2616 } 2617 }
2617 arch__fix_tev_from_maps(pev, tev, map); 2618 arch__fix_tev_from_maps(pev, tev, map, sym);
2618 } 2619 }
2619 if (ret == skipped) { 2620 if (ret == skipped) {
2620 ret = -ENOENT; 2621 ret = -ENOENT;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index e2209623f981..5a27eb4fad05 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -154,7 +154,8 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
154int show_available_funcs(const char *module, struct strfilter *filter, bool user); 154int show_available_funcs(const char *module, struct strfilter *filter, bool user);
155bool arch__prefers_symtab(void); 155bool arch__prefers_symtab(void);
156void arch__fix_tev_from_maps(struct perf_probe_event *pev, 156void arch__fix_tev_from_maps(struct perf_probe_event *pev,
157 struct probe_trace_event *tev, struct map *map); 157 struct probe_trace_event *tev, struct map *map,
158 struct symbol *sym);
158 159
159/* If there is no space to write, returns -E2BIG. */ 160/* If there is no space to write, returns -E2BIG. */
160int e_snprintf(char *str, size_t size, const char *format, ...) 161int e_snprintf(char *str, size_t size, const char *format, ...)
diff --git a/tools/perf/util/rb_resort.h b/tools/perf/util/rb_resort.h
new file mode 100644
index 000000000000..abc76e3d3098
--- /dev/null
+++ b/tools/perf/util/rb_resort.h
@@ -0,0 +1,149 @@
1#ifndef _PERF_RESORT_RB_H_
2#define _PERF_RESORT_RB_H_
3/*
4 * Template for creating a class to resort an existing rb_tree according to
5 * a new sort criteria, that must be present in the entries of the source
6 * rb_tree.
7 *
8 * (c) 2016 Arnaldo Carvalho de Melo <acme@redhat.com>
9 *
10 * Quick example, resorting threads by its shortname:
11 *
12 * First define the prefix (threads) to be used for the functions and data
13 * structures created, and provide an expression for the sorting, then the
14 * fields to be present in each of the entries in the new, sorted, rb_tree.
15 *
16 * The body of the init function should collect the fields, maybe
17 * pre-calculating them from multiple entries in the original 'entry' from
18 * the rb_tree used as a source for the entries to be sorted:
19
20DEFINE_RB_RESORT_RB(threads, strcmp(a->thread->shortname,
21 b->thread->shortname) < 0,
22 struct thread *thread;
23)
24{
25 entry->thread = rb_entry(nd, struct thread, rb_node);
26}
27
28 * After this it is just a matter of instantiating it and iterating it,
29 * for a few data structures with existing rb_trees, such as 'struct machine',
30 * helpers are available to get the rb_root and the nr_entries:
31
32 DECLARE_RESORT_RB_MACHINE_THREADS(threads, machine_ptr);
33
34 * This will instantiate the new rb_tree and a cursor for it, that can be used as:
35
36 struct rb_node *nd;
37
38 resort_rb__for_each(nd, threads) {
39 struct thread *t = threads_entry;
40 printf("%s: %d\n", t->shortname, t->tid);
41 }
42
43 * Then delete it:
44
45 resort_rb__delete(threads);
46
47 * The name of the data structures and functions will have a _sorted suffix
48 * right before the method names, i.e. will look like:
49 *
50 * struct threads_sorted_entry {}
51 * threads_sorted__insert()
52 */
53
54#define DEFINE_RESORT_RB(__name, __comp, ...) \
55struct __name##_sorted_entry { \
56 struct rb_node rb_node; \
57 __VA_ARGS__ \
58}; \
59static void __name##_sorted__init_entry(struct rb_node *nd, \
60 struct __name##_sorted_entry *entry); \
61 \
62static int __name##_sorted__cmp(struct rb_node *nda, struct rb_node *ndb) \
63{ \
64 struct __name##_sorted_entry *a, *b; \
65 a = rb_entry(nda, struct __name##_sorted_entry, rb_node); \
66 b = rb_entry(ndb, struct __name##_sorted_entry, rb_node); \
67 return __comp; \
68} \
69 \
70struct __name##_sorted { \
71 struct rb_root entries; \
72 struct __name##_sorted_entry nd[0]; \
73}; \
74 \
75static void __name##_sorted__insert(struct __name##_sorted *sorted, \
76 struct rb_node *sorted_nd) \
77{ \
78 struct rb_node **p = &sorted->entries.rb_node, *parent = NULL; \
79 while (*p != NULL) { \
80 parent = *p; \
81 if (__name##_sorted__cmp(sorted_nd, parent)) \
82 p = &(*p)->rb_left; \
83 else \
84 p = &(*p)->rb_right; \
85 } \
86 rb_link_node(sorted_nd, parent, p); \
87 rb_insert_color(sorted_nd, &sorted->entries); \
88} \
89 \
90static void __name##_sorted__sort(struct __name##_sorted *sorted, \
91 struct rb_root *entries) \
92{ \
93 struct rb_node *nd; \
94 unsigned int i = 0; \
95 for (nd = rb_first(entries); nd; nd = rb_next(nd)) { \
96 struct __name##_sorted_entry *snd = &sorted->nd[i++]; \
97 __name##_sorted__init_entry(nd, snd); \
98 __name##_sorted__insert(sorted, &snd->rb_node); \
99 } \
100} \
101 \
102static struct __name##_sorted *__name##_sorted__new(struct rb_root *entries, \
103 int nr_entries) \
104{ \
105 struct __name##_sorted *sorted; \
106 sorted = malloc(sizeof(*sorted) + sizeof(sorted->nd[0]) * nr_entries); \
107 if (sorted) { \
108 sorted->entries = RB_ROOT; \
109 __name##_sorted__sort(sorted, entries); \
110 } \
111 return sorted; \
112} \
113 \
114static void __name##_sorted__delete(struct __name##_sorted *sorted) \
115{ \
116 free(sorted); \
117} \
118 \
119static void __name##_sorted__init_entry(struct rb_node *nd, \
120 struct __name##_sorted_entry *entry)
121
122#define DECLARE_RESORT_RB(__name) \
123struct __name##_sorted_entry *__name##_entry; \
124struct __name##_sorted *__name = __name##_sorted__new
125
126#define resort_rb__for_each(__nd, __name) \
127 for (__nd = rb_first(&__name->entries); \
128 __name##_entry = rb_entry(__nd, struct __name##_sorted_entry, \
129 rb_node), __nd; \
130 __nd = rb_next(__nd))
131
132#define resort_rb__delete(__name) \
133 __name##_sorted__delete(__name), __name = NULL
134
135/*
136 * Helpers for other classes that contains both an rbtree and the
137 * number of entries in it:
138 */
139
140/* For 'struct intlist' */
141#define DECLARE_RESORT_RB_INTLIST(__name, __ilist) \
142 DECLARE_RESORT_RB(__name)(&__ilist->rblist.entries, \
143 __ilist->rblist.nr_entries)
144
145/* For 'struct machine->threads' */
146#define DECLARE_RESORT_RB_MACHINE_THREADS(__name, __machine) \
147 DECLARE_RESORT_RB(__name)(&__machine->threads, __machine->nr_threads)
148
149#endif /* _PERF_RESORT_RB_H_ */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 47966a1618c7..772e2e461ec3 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -21,13 +21,6 @@ const char *sort_order;
21const char *field_order; 21const char *field_order;
22regex_t ignore_callees_regex; 22regex_t ignore_callees_regex;
23int have_ignore_callees = 0; 23int have_ignore_callees = 0;
24int sort__need_collapse = 0;
25int sort__has_parent = 0;
26int sort__has_sym = 0;
27int sort__has_dso = 0;
28int sort__has_socket = 0;
29int sort__has_thread = 0;
30int sort__has_comm = 0;
31enum sort_mode sort__mode = SORT_MODE__NORMAL; 24enum sort_mode sort__mode = SORT_MODE__NORMAL;
32 25
33/* 26/*
@@ -244,7 +237,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
244 * comparing symbol address alone is not enough since it's a 237 * comparing symbol address alone is not enough since it's a
245 * relative address within a dso. 238 * relative address within a dso.
246 */ 239 */
247 if (!sort__has_dso) { 240 if (!hists__has(left->hists, dso) || hists__has(right->hists, dso)) {
248 ret = sort__dso_cmp(left, right); 241 ret = sort__dso_cmp(left, right);
249 if (ret != 0) 242 if (ret != 0)
250 return ret; 243 return ret;
@@ -2163,7 +2156,7 @@ static int __sort_dimension__add(struct sort_dimension *sd,
2163 return -1; 2156 return -1;
2164 2157
2165 if (sd->entry->se_collapse) 2158 if (sd->entry->se_collapse)
2166 sort__need_collapse = 1; 2159 list->need_collapse = 1;
2167 2160
2168 sd->taken = 1; 2161 sd->taken = 1;
2169 2162
@@ -2245,9 +2238,9 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
2245 pr_err("Invalid regex: %s\n%s", parent_pattern, err); 2238 pr_err("Invalid regex: %s\n%s", parent_pattern, err);
2246 return -EINVAL; 2239 return -EINVAL;
2247 } 2240 }
2248 sort__has_parent = 1; 2241 list->parent = 1;
2249 } else if (sd->entry == &sort_sym) { 2242 } else if (sd->entry == &sort_sym) {
2250 sort__has_sym = 1; 2243 list->sym = 1;
2251 /* 2244 /*
2252 * perf diff displays the performance difference amongst 2245 * perf diff displays the performance difference amongst
2253 * two or more perf.data files. Those files could come 2246 * two or more perf.data files. Those files could come
@@ -2258,13 +2251,13 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
2258 sd->entry->se_collapse = sort__sym_sort; 2251 sd->entry->se_collapse = sort__sym_sort;
2259 2252
2260 } else if (sd->entry == &sort_dso) { 2253 } else if (sd->entry == &sort_dso) {
2261 sort__has_dso = 1; 2254 list->dso = 1;
2262 } else if (sd->entry == &sort_socket) { 2255 } else if (sd->entry == &sort_socket) {
2263 sort__has_socket = 1; 2256 list->socket = 1;
2264 } else if (sd->entry == &sort_thread) { 2257 } else if (sd->entry == &sort_thread) {
2265 sort__has_thread = 1; 2258 list->thread = 1;
2266 } else if (sd->entry == &sort_comm) { 2259 } else if (sd->entry == &sort_comm) {
2267 sort__has_comm = 1; 2260 list->comm = 1;
2268 } 2261 }
2269 2262
2270 return __sort_dimension__add(sd, list, level); 2263 return __sort_dimension__add(sd, list, level);
@@ -2289,7 +2282,7 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
2289 return -EINVAL; 2282 return -EINVAL;
2290 2283
2291 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) 2284 if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to)
2292 sort__has_sym = 1; 2285 list->sym = 1;
2293 2286
2294 __sort_dimension__add(sd, list, level); 2287 __sort_dimension__add(sd, list, level);
2295 return 0; 2288 return 0;
@@ -2305,7 +2298,7 @@ static int sort_dimension__add(struct perf_hpp_list *list, const char *tok,
2305 return -EINVAL; 2298 return -EINVAL;
2306 2299
2307 if (sd->entry == &sort_mem_daddr_sym) 2300 if (sd->entry == &sort_mem_daddr_sym)
2308 sort__has_sym = 1; 2301 list->sym = 1;
2309 2302
2310 __sort_dimension__add(sd, list, level); 2303 __sort_dimension__add(sd, list, level);
2311 return 0; 2304 return 0;
@@ -2746,10 +2739,10 @@ int setup_sorting(struct perf_evlist *evlist)
2746 2739
2747void reset_output_field(void) 2740void reset_output_field(void)
2748{ 2741{
2749 sort__need_collapse = 0; 2742 perf_hpp_list.need_collapse = 0;
2750 sort__has_parent = 0; 2743 perf_hpp_list.parent = 0;
2751 sort__has_sym = 0; 2744 perf_hpp_list.sym = 0;
2752 sort__has_dso = 0; 2745 perf_hpp_list.dso = 0;
2753 2746
2754 field_order = NULL; 2747 field_order = NULL;
2755 sort_order = NULL; 2748 sort_order = NULL;
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 3f4e35998119..42927f448bcb 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -31,13 +31,6 @@ extern const char *parent_pattern;
31extern const char default_sort_order[]; 31extern const char default_sort_order[];
32extern regex_t ignore_callees_regex; 32extern regex_t ignore_callees_regex;
33extern int have_ignore_callees; 33extern int have_ignore_callees;
34extern int sort__need_collapse;
35extern int sort__has_dso;
36extern int sort__has_parent;
37extern int sort__has_sym;
38extern int sort__has_socket;
39extern int sort__has_thread;
40extern int sort__has_comm;
41extern enum sort_mode sort__mode; 34extern enum sort_mode sort__mode;
42extern struct sort_entry sort_comm; 35extern struct sort_entry sort_comm;
43extern struct sort_entry sort_dso; 36extern struct sort_entry sort_dso;
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 3f9d6798bd18..87a297dd8901 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -770,7 +770,8 @@ static bool want_demangle(bool is_kernel_sym)
770 return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle; 770 return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle;
771} 771}
772 772
773void __weak arch__elf_sym_adjust(GElf_Sym *sym __maybe_unused) { } 773void __weak arch__sym_update(struct symbol *s __maybe_unused,
774 GElf_Sym *sym __maybe_unused) { }
774 775
775int dso__load_sym(struct dso *dso, struct map *map, 776int dso__load_sym(struct dso *dso, struct map *map,
776 struct symsrc *syms_ss, struct symsrc *runtime_ss, 777 struct symsrc *syms_ss, struct symsrc *runtime_ss,
@@ -947,8 +948,6 @@ int dso__load_sym(struct dso *dso, struct map *map,
947 (sym.st_value & 1)) 948 (sym.st_value & 1))
948 --sym.st_value; 949 --sym.st_value;
949 950
950 arch__elf_sym_adjust(&sym);
951
952 if (dso->kernel || kmodule) { 951 if (dso->kernel || kmodule) {
953 char dso_name[PATH_MAX]; 952 char dso_name[PATH_MAX];
954 953
@@ -1082,6 +1081,8 @@ new_symbol:
1082 if (!f) 1081 if (!f)
1083 goto out_elf_end; 1082 goto out_elf_end;
1084 1083
1084 arch__sym_update(f, &sym);
1085
1085 if (filter && filter(curr_map, f)) 1086 if (filter && filter(curr_map, f))
1086 symbol__delete(f); 1087 symbol__delete(f);
1087 else { 1088 else {
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index c8e43979ed5c..07211c2f8456 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -55,6 +55,7 @@ struct symbol {
55 u16 namelen; 55 u16 namelen;
56 u8 binding; 56 u8 binding;
57 bool ignore; 57 bool ignore;
58 u8 arch_sym;
58 char name[0]; 59 char name[0];
59}; 60};
60 61
@@ -323,7 +324,7 @@ int setup_intlist(struct intlist **list, const char *list_str,
323 324
324#ifdef HAVE_LIBELF_SUPPORT 325#ifdef HAVE_LIBELF_SUPPORT
325bool elf__needs_adjust_symbols(GElf_Ehdr ehdr); 326bool elf__needs_adjust_symbols(GElf_Ehdr ehdr);
326void arch__elf_sym_adjust(GElf_Sym *sym); 327void arch__sym_update(struct symbol *s, GElf_Sym *sym);
327#endif 328#endif
328 329
329#define SYMBOL_A 0 330#define SYMBOL_A 0