diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-01-11 14:47:45 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-01-11 14:47:45 -0500 |
commit | ddb321a8dd158520d97ed1cbade1d4ac36b6af31 (patch) | |
tree | 842f5550c5947d4aebff56dcd1091950b0cc0f82 | |
parent | 1e6c3e8f8fb94a8914a380e02a7e8ad81d47273e (diff) | |
parent | 5306c31c5733cb4a79cc002e0c3ad256fd439614 (diff) |
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf fixes from Ingo Molnar:
"Mostly tooling fixes, but also some kernel side fixes: uncore PMU
driver fix, user regs sampling fix and an instruction decoder fix that
unbreaks PEBS precise sampling"
* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
perf/x86/uncore/hsw-ep: Handle systems with only two SBOXes
perf/x86_64: Improve user regs sampling
perf: Move task_pt_regs sampling into arch code
x86: Fix off-by-one in instruction decoder
perf hists browser: Fix segfault when showing callchain
perf callchain: Free callchains when hist entries are deleted
perf hists: Fix children sort key behavior
perf diff: Fix to sort by baseline field by default
perf list: Fix --raw-dump option
perf probe: Fix crash in dwarf_getcfi_elf
perf probe: Fix to fall back to find probe point in symbols
perf callchain: Append callchains only when requested
perf ui/tui: Print backtrace symbols when segfault occurs
perf report: Show progress bar for output resorting
26 files changed, 371 insertions, 77 deletions
diff --git a/arch/arm/kernel/perf_regs.c b/arch/arm/kernel/perf_regs.c index 6e4379c67cbc..592dda3f21ff 100644 --- a/arch/arm/kernel/perf_regs.c +++ b/arch/arm/kernel/perf_regs.c | |||
@@ -28,3 +28,11 @@ u64 perf_reg_abi(struct task_struct *task) | |||
28 | { | 28 | { |
29 | return PERF_SAMPLE_REGS_ABI_32; | 29 | return PERF_SAMPLE_REGS_ABI_32; |
30 | } | 30 | } |
31 | |||
32 | void perf_get_regs_user(struct perf_regs *regs_user, | ||
33 | struct pt_regs *regs, | ||
34 | struct pt_regs *regs_user_copy) | ||
35 | { | ||
36 | regs_user->regs = task_pt_regs(current); | ||
37 | regs_user->abi = perf_reg_abi(current); | ||
38 | } | ||
diff --git a/arch/arm64/kernel/perf_regs.c b/arch/arm64/kernel/perf_regs.c index 6762ad705587..3f62b35fb6f1 100644 --- a/arch/arm64/kernel/perf_regs.c +++ b/arch/arm64/kernel/perf_regs.c | |||
@@ -50,3 +50,11 @@ u64 perf_reg_abi(struct task_struct *task) | |||
50 | else | 50 | else |
51 | return PERF_SAMPLE_REGS_ABI_64; | 51 | return PERF_SAMPLE_REGS_ABI_64; |
52 | } | 52 | } |
53 | |||
54 | void perf_get_regs_user(struct perf_regs *regs_user, | ||
55 | struct pt_regs *regs, | ||
56 | struct pt_regs *regs_user_copy) | ||
57 | { | ||
58 | regs_user->regs = task_pt_regs(current); | ||
59 | regs_user->abi = perf_reg_abi(current); | ||
60 | } | ||
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h index 18eb78bbdd10..863d9b02563e 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h | |||
@@ -17,7 +17,7 @@ | |||
17 | #define UNCORE_PCI_DEV_TYPE(data) ((data >> 8) & 0xff) | 17 | #define UNCORE_PCI_DEV_TYPE(data) ((data >> 8) & 0xff) |
18 | #define UNCORE_PCI_DEV_IDX(data) (data & 0xff) | 18 | #define UNCORE_PCI_DEV_IDX(data) (data & 0xff) |
19 | #define UNCORE_EXTRA_PCI_DEV 0xff | 19 | #define UNCORE_EXTRA_PCI_DEV 0xff |
20 | #define UNCORE_EXTRA_PCI_DEV_MAX 2 | 20 | #define UNCORE_EXTRA_PCI_DEV_MAX 3 |
21 | 21 | ||
22 | /* support up to 8 sockets */ | 22 | /* support up to 8 sockets */ |
23 | #define UNCORE_SOCKET_MAX 8 | 23 | #define UNCORE_SOCKET_MAX 8 |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c index 745b158e9a65..21af6149edf2 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore_snbep.c | |||
@@ -891,6 +891,7 @@ void snbep_uncore_cpu_init(void) | |||
891 | enum { | 891 | enum { |
892 | SNBEP_PCI_QPI_PORT0_FILTER, | 892 | SNBEP_PCI_QPI_PORT0_FILTER, |
893 | SNBEP_PCI_QPI_PORT1_FILTER, | 893 | SNBEP_PCI_QPI_PORT1_FILTER, |
894 | HSWEP_PCI_PCU_3, | ||
894 | }; | 895 | }; |
895 | 896 | ||
896 | static int snbep_qpi_hw_config(struct intel_uncore_box *box, struct perf_event *event) | 897 | static int snbep_qpi_hw_config(struct intel_uncore_box *box, struct perf_event *event) |
@@ -2026,6 +2027,17 @@ void hswep_uncore_cpu_init(void) | |||
2026 | { | 2027 | { |
2027 | if (hswep_uncore_cbox.num_boxes > boot_cpu_data.x86_max_cores) | 2028 | if (hswep_uncore_cbox.num_boxes > boot_cpu_data.x86_max_cores) |
2028 | hswep_uncore_cbox.num_boxes = boot_cpu_data.x86_max_cores; | 2029 | hswep_uncore_cbox.num_boxes = boot_cpu_data.x86_max_cores; |
2030 | |||
2031 | /* Detect 6-8 core systems with only two SBOXes */ | ||
2032 | if (uncore_extra_pci_dev[0][HSWEP_PCI_PCU_3]) { | ||
2033 | u32 capid4; | ||
2034 | |||
2035 | pci_read_config_dword(uncore_extra_pci_dev[0][HSWEP_PCI_PCU_3], | ||
2036 | 0x94, &capid4); | ||
2037 | if (((capid4 >> 6) & 0x3) == 0) | ||
2038 | hswep_uncore_sbox.num_boxes = 2; | ||
2039 | } | ||
2040 | |||
2029 | uncore_msr_uncores = hswep_msr_uncores; | 2041 | uncore_msr_uncores = hswep_msr_uncores; |
2030 | } | 2042 | } |
2031 | 2043 | ||
@@ -2287,6 +2299,11 @@ static DEFINE_PCI_DEVICE_TABLE(hswep_uncore_pci_ids) = { | |||
2287 | .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, | 2299 | .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, |
2288 | SNBEP_PCI_QPI_PORT1_FILTER), | 2300 | SNBEP_PCI_QPI_PORT1_FILTER), |
2289 | }, | 2301 | }, |
2302 | { /* PCU.3 (for Capability registers) */ | ||
2303 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x2fc0), | ||
2304 | .driver_data = UNCORE_PCI_DEV_DATA(UNCORE_EXTRA_PCI_DEV, | ||
2305 | HSWEP_PCI_PCU_3), | ||
2306 | }, | ||
2290 | { /* end: all zeroes */ } | 2307 | { /* end: all zeroes */ } |
2291 | }; | 2308 | }; |
2292 | 2309 | ||
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c index e309cc5c276e..781861cc5ee8 100644 --- a/arch/x86/kernel/perf_regs.c +++ b/arch/x86/kernel/perf_regs.c | |||
@@ -78,6 +78,14 @@ u64 perf_reg_abi(struct task_struct *task) | |||
78 | { | 78 | { |
79 | return PERF_SAMPLE_REGS_ABI_32; | 79 | return PERF_SAMPLE_REGS_ABI_32; |
80 | } | 80 | } |
81 | |||
82 | void perf_get_regs_user(struct perf_regs *regs_user, | ||
83 | struct pt_regs *regs, | ||
84 | struct pt_regs *regs_user_copy) | ||
85 | { | ||
86 | regs_user->regs = task_pt_regs(current); | ||
87 | regs_user->abi = perf_reg_abi(current); | ||
88 | } | ||
81 | #else /* CONFIG_X86_64 */ | 89 | #else /* CONFIG_X86_64 */ |
82 | #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ | 90 | #define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \ |
83 | (1ULL << PERF_REG_X86_ES) | \ | 91 | (1ULL << PERF_REG_X86_ES) | \ |
@@ -102,4 +110,86 @@ u64 perf_reg_abi(struct task_struct *task) | |||
102 | else | 110 | else |
103 | return PERF_SAMPLE_REGS_ABI_64; | 111 | return PERF_SAMPLE_REGS_ABI_64; |
104 | } | 112 | } |
113 | |||
114 | void perf_get_regs_user(struct perf_regs *regs_user, | ||
115 | struct pt_regs *regs, | ||
116 | struct pt_regs *regs_user_copy) | ||
117 | { | ||
118 | struct pt_regs *user_regs = task_pt_regs(current); | ||
119 | |||
120 | /* | ||
121 | * If we're in an NMI that interrupted task_pt_regs setup, then | ||
122 | * we can't sample user regs at all. This check isn't really | ||
123 | * sufficient, though, as we could be in an NMI inside an interrupt | ||
124 | * that happened during task_pt_regs setup. | ||
125 | */ | ||
126 | if (regs->sp > (unsigned long)&user_regs->r11 && | ||
127 | regs->sp <= (unsigned long)(user_regs + 1)) { | ||
128 | regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE; | ||
129 | regs_user->regs = NULL; | ||
130 | return; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * RIP, flags, and the argument registers are usually saved. | ||
135 | * orig_ax is probably okay, too. | ||
136 | */ | ||
137 | regs_user_copy->ip = user_regs->ip; | ||
138 | regs_user_copy->cx = user_regs->cx; | ||
139 | regs_user_copy->dx = user_regs->dx; | ||
140 | regs_user_copy->si = user_regs->si; | ||
141 | regs_user_copy->di = user_regs->di; | ||
142 | regs_user_copy->r8 = user_regs->r8; | ||
143 | regs_user_copy->r9 = user_regs->r9; | ||
144 | regs_user_copy->r10 = user_regs->r10; | ||
145 | regs_user_copy->r11 = user_regs->r11; | ||
146 | regs_user_copy->orig_ax = user_regs->orig_ax; | ||
147 | regs_user_copy->flags = user_regs->flags; | ||
148 | |||
149 | /* | ||
150 | * Don't even try to report the "rest" regs. | ||
151 | */ | ||
152 | regs_user_copy->bx = -1; | ||
153 | regs_user_copy->bp = -1; | ||
154 | regs_user_copy->r12 = -1; | ||
155 | regs_user_copy->r13 = -1; | ||
156 | regs_user_copy->r14 = -1; | ||
157 | regs_user_copy->r15 = -1; | ||
158 | |||
159 | /* | ||
160 | * For this to be at all useful, we need a reasonable guess for | ||
161 | * sp and the ABI. Be careful: we're in NMI context, and we're | ||
162 | * considering current to be the current task, so we should | ||
163 | * be careful not to look at any other percpu variables that might | ||
164 | * change during context switches. | ||
165 | */ | ||
166 | if (IS_ENABLED(CONFIG_IA32_EMULATION) && | ||
167 | task_thread_info(current)->status & TS_COMPAT) { | ||
168 | /* Easy case: we're in a compat syscall. */ | ||
169 | regs_user->abi = PERF_SAMPLE_REGS_ABI_32; | ||
170 | regs_user_copy->sp = user_regs->sp; | ||
171 | regs_user_copy->cs = user_regs->cs; | ||
172 | regs_user_copy->ss = user_regs->ss; | ||
173 | } else if (user_regs->orig_ax != -1) { | ||
174 | /* | ||
175 | * We're probably in a 64-bit syscall. | ||
176 | * Warning: this code is severely racy. At least it's better | ||
177 | * than just blindly copying user_regs. | ||
178 | */ | ||
179 | regs_user->abi = PERF_SAMPLE_REGS_ABI_64; | ||
180 | regs_user_copy->sp = this_cpu_read(old_rsp); | ||
181 | regs_user_copy->cs = __USER_CS; | ||
182 | regs_user_copy->ss = __USER_DS; | ||
183 | regs_user_copy->cx = -1; /* usually contains garbage */ | ||
184 | } else { | ||
185 | /* We're probably in an interrupt or exception. */ | ||
186 | regs_user->abi = user_64bit_mode(user_regs) ? | ||
187 | PERF_SAMPLE_REGS_ABI_64 : PERF_SAMPLE_REGS_ABI_32; | ||
188 | regs_user_copy->sp = user_regs->sp; | ||
189 | regs_user_copy->cs = user_regs->cs; | ||
190 | regs_user_copy->ss = user_regs->ss; | ||
191 | } | ||
192 | |||
193 | regs_user->regs = regs_user_copy; | ||
194 | } | ||
105 | #endif /* CONFIG_X86_32 */ | 195 | #endif /* CONFIG_X86_32 */ |
diff --git a/arch/x86/lib/insn.c b/arch/x86/lib/insn.c index 2480978b31cc..1313ae6b478b 100644 --- a/arch/x86/lib/insn.c +++ b/arch/x86/lib/insn.c | |||
@@ -28,7 +28,7 @@ | |||
28 | 28 | ||
29 | /* Verify next sizeof(t) bytes can be on the same instruction */ | 29 | /* Verify next sizeof(t) bytes can be on the same instruction */ |
30 | #define validate_next(t, insn, n) \ | 30 | #define validate_next(t, insn, n) \ |
31 | ((insn)->next_byte + sizeof(t) + n < (insn)->end_kaddr) | 31 | ((insn)->next_byte + sizeof(t) + n <= (insn)->end_kaddr) |
32 | 32 | ||
33 | #define __get_next(t, insn) \ | 33 | #define __get_next(t, insn) \ |
34 | ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) | 34 | ({ t r = *(t*)insn->next_byte; insn->next_byte += sizeof(t); r; }) |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 486e84ccb1f9..4f7a61ca4b39 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -79,11 +79,6 @@ struct perf_branch_stack { | |||
79 | struct perf_branch_entry entries[0]; | 79 | struct perf_branch_entry entries[0]; |
80 | }; | 80 | }; |
81 | 81 | ||
82 | struct perf_regs { | ||
83 | __u64 abi; | ||
84 | struct pt_regs *regs; | ||
85 | }; | ||
86 | |||
87 | struct task_struct; | 82 | struct task_struct; |
88 | 83 | ||
89 | /* | 84 | /* |
@@ -610,7 +605,14 @@ struct perf_sample_data { | |||
610 | u32 reserved; | 605 | u32 reserved; |
611 | } cpu_entry; | 606 | } cpu_entry; |
612 | struct perf_callchain_entry *callchain; | 607 | struct perf_callchain_entry *callchain; |
608 | |||
609 | /* | ||
610 | * regs_user may point to task_pt_regs or to regs_user_copy, depending | ||
611 | * on arch details. | ||
612 | */ | ||
613 | struct perf_regs regs_user; | 613 | struct perf_regs regs_user; |
614 | struct pt_regs regs_user_copy; | ||
615 | |||
614 | struct perf_regs regs_intr; | 616 | struct perf_regs regs_intr; |
615 | u64 stack_user_size; | 617 | u64 stack_user_size; |
616 | } ____cacheline_aligned; | 618 | } ____cacheline_aligned; |
diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h index 3c73d5fe18be..a5f98d53d732 100644 --- a/include/linux/perf_regs.h +++ b/include/linux/perf_regs.h | |||
@@ -1,11 +1,19 @@ | |||
1 | #ifndef _LINUX_PERF_REGS_H | 1 | #ifndef _LINUX_PERF_REGS_H |
2 | #define _LINUX_PERF_REGS_H | 2 | #define _LINUX_PERF_REGS_H |
3 | 3 | ||
4 | struct perf_regs { | ||
5 | __u64 abi; | ||
6 | struct pt_regs *regs; | ||
7 | }; | ||
8 | |||
4 | #ifdef CONFIG_HAVE_PERF_REGS | 9 | #ifdef CONFIG_HAVE_PERF_REGS |
5 | #include <asm/perf_regs.h> | 10 | #include <asm/perf_regs.h> |
6 | u64 perf_reg_value(struct pt_regs *regs, int idx); | 11 | u64 perf_reg_value(struct pt_regs *regs, int idx); |
7 | int perf_reg_validate(u64 mask); | 12 | int perf_reg_validate(u64 mask); |
8 | u64 perf_reg_abi(struct task_struct *task); | 13 | u64 perf_reg_abi(struct task_struct *task); |
14 | void perf_get_regs_user(struct perf_regs *regs_user, | ||
15 | struct pt_regs *regs, | ||
16 | struct pt_regs *regs_user_copy); | ||
9 | #else | 17 | #else |
10 | static inline u64 perf_reg_value(struct pt_regs *regs, int idx) | 18 | static inline u64 perf_reg_value(struct pt_regs *regs, int idx) |
11 | { | 19 | { |
@@ -21,5 +29,13 @@ static inline u64 perf_reg_abi(struct task_struct *task) | |||
21 | { | 29 | { |
22 | return PERF_SAMPLE_REGS_ABI_NONE; | 30 | return PERF_SAMPLE_REGS_ABI_NONE; |
23 | } | 31 | } |
32 | |||
33 | static inline void perf_get_regs_user(struct perf_regs *regs_user, | ||
34 | struct pt_regs *regs, | ||
35 | struct pt_regs *regs_user_copy) | ||
36 | { | ||
37 | regs_user->regs = task_pt_regs(current); | ||
38 | regs_user->abi = perf_reg_abi(current); | ||
39 | } | ||
24 | #endif /* CONFIG_HAVE_PERF_REGS */ | 40 | #endif /* CONFIG_HAVE_PERF_REGS */ |
25 | #endif /* _LINUX_PERF_REGS_H */ | 41 | #endif /* _LINUX_PERF_REGS_H */ |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 4c1ee7f2bebc..882f835a0d85 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -4461,18 +4461,14 @@ perf_output_sample_regs(struct perf_output_handle *handle, | |||
4461 | } | 4461 | } |
4462 | 4462 | ||
4463 | static void perf_sample_regs_user(struct perf_regs *regs_user, | 4463 | static void perf_sample_regs_user(struct perf_regs *regs_user, |
4464 | struct pt_regs *regs) | 4464 | struct pt_regs *regs, |
4465 | struct pt_regs *regs_user_copy) | ||
4465 | { | 4466 | { |
4466 | if (!user_mode(regs)) { | 4467 | if (user_mode(regs)) { |
4467 | if (current->mm) | 4468 | regs_user->abi = perf_reg_abi(current); |
4468 | regs = task_pt_regs(current); | ||
4469 | else | ||
4470 | regs = NULL; | ||
4471 | } | ||
4472 | |||
4473 | if (regs) { | ||
4474 | regs_user->abi = perf_reg_abi(current); | ||
4475 | regs_user->regs = regs; | 4469 | regs_user->regs = regs; |
4470 | } else if (current->mm) { | ||
4471 | perf_get_regs_user(regs_user, regs, regs_user_copy); | ||
4476 | } else { | 4472 | } else { |
4477 | regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE; | 4473 | regs_user->abi = PERF_SAMPLE_REGS_ABI_NONE; |
4478 | regs_user->regs = NULL; | 4474 | regs_user->regs = NULL; |
@@ -4951,7 +4947,8 @@ void perf_prepare_sample(struct perf_event_header *header, | |||
4951 | } | 4947 | } |
4952 | 4948 | ||
4953 | if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER)) | 4949 | if (sample_type & (PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER)) |
4954 | perf_sample_regs_user(&data->regs_user, regs); | 4950 | perf_sample_regs_user(&data->regs_user, regs, |
4951 | &data->regs_user_copy); | ||
4955 | 4952 | ||
4956 | if (sample_type & PERF_SAMPLE_REGS_USER) { | 4953 | if (sample_type & PERF_SAMPLE_REGS_USER) { |
4957 | /* regs dump ABI info */ | 4954 | /* regs dump ABI info */ |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index e7417fe97a97..747f86103599 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -232,7 +232,7 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
232 | if (nr_samples > 0) { | 232 | if (nr_samples > 0) { |
233 | total_nr_samples += nr_samples; | 233 | total_nr_samples += nr_samples; |
234 | hists__collapse_resort(hists, NULL); | 234 | hists__collapse_resort(hists, NULL); |
235 | hists__output_resort(hists); | 235 | hists__output_resort(hists, NULL); |
236 | 236 | ||
237 | if (symbol_conf.event_group && | 237 | if (symbol_conf.event_group && |
238 | !perf_evsel__is_group_leader(pos)) | 238 | !perf_evsel__is_group_leader(pos)) |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 1ce425d101a9..1fd96c13f199 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -545,6 +545,42 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | |||
545 | return __hist_entry__cmp_compute(p_left, p_right, c); | 545 | return __hist_entry__cmp_compute(p_left, p_right, c); |
546 | } | 546 | } |
547 | 547 | ||
548 | static int64_t | ||
549 | hist_entry__cmp_nop(struct hist_entry *left __maybe_unused, | ||
550 | struct hist_entry *right __maybe_unused) | ||
551 | { | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static int64_t | ||
556 | hist_entry__cmp_baseline(struct hist_entry *left, struct hist_entry *right) | ||
557 | { | ||
558 | if (sort_compute) | ||
559 | return 0; | ||
560 | |||
561 | if (left->stat.period == right->stat.period) | ||
562 | return 0; | ||
563 | return left->stat.period > right->stat.period ? 1 : -1; | ||
564 | } | ||
565 | |||
566 | static int64_t | ||
567 | hist_entry__cmp_delta(struct hist_entry *left, struct hist_entry *right) | ||
568 | { | ||
569 | return hist_entry__cmp_compute(right, left, COMPUTE_DELTA); | ||
570 | } | ||
571 | |||
572 | static int64_t | ||
573 | hist_entry__cmp_ratio(struct hist_entry *left, struct hist_entry *right) | ||
574 | { | ||
575 | return hist_entry__cmp_compute(right, left, COMPUTE_RATIO); | ||
576 | } | ||
577 | |||
578 | static int64_t | ||
579 | hist_entry__cmp_wdiff(struct hist_entry *left, struct hist_entry *right) | ||
580 | { | ||
581 | return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF); | ||
582 | } | ||
583 | |||
548 | static void insert_hist_entry_by_compute(struct rb_root *root, | 584 | static void insert_hist_entry_by_compute(struct rb_root *root, |
549 | struct hist_entry *he, | 585 | struct hist_entry *he, |
550 | int c) | 586 | int c) |
@@ -605,7 +641,7 @@ static void hists__process(struct hists *hists) | |||
605 | hists__precompute(hists); | 641 | hists__precompute(hists); |
606 | hists__compute_resort(hists); | 642 | hists__compute_resort(hists); |
607 | } else { | 643 | } else { |
608 | hists__output_resort(hists); | 644 | hists__output_resort(hists, NULL); |
609 | } | 645 | } |
610 | 646 | ||
611 | hists__fprintf(hists, true, 0, 0, 0, stdout); | 647 | hists__fprintf(hists, true, 0, 0, 0, stdout); |
@@ -1038,27 +1074,35 @@ static void data__hpp_register(struct data__file *d, int idx) | |||
1038 | fmt->header = hpp__header; | 1074 | fmt->header = hpp__header; |
1039 | fmt->width = hpp__width; | 1075 | fmt->width = hpp__width; |
1040 | fmt->entry = hpp__entry_global; | 1076 | fmt->entry = hpp__entry_global; |
1077 | fmt->cmp = hist_entry__cmp_nop; | ||
1078 | fmt->collapse = hist_entry__cmp_nop; | ||
1041 | 1079 | ||
1042 | /* TODO more colors */ | 1080 | /* TODO more colors */ |
1043 | switch (idx) { | 1081 | switch (idx) { |
1044 | case PERF_HPP_DIFF__BASELINE: | 1082 | case PERF_HPP_DIFF__BASELINE: |
1045 | fmt->color = hpp__color_baseline; | 1083 | fmt->color = hpp__color_baseline; |
1084 | fmt->sort = hist_entry__cmp_baseline; | ||
1046 | break; | 1085 | break; |
1047 | case PERF_HPP_DIFF__DELTA: | 1086 | case PERF_HPP_DIFF__DELTA: |
1048 | fmt->color = hpp__color_delta; | 1087 | fmt->color = hpp__color_delta; |
1088 | fmt->sort = hist_entry__cmp_delta; | ||
1049 | break; | 1089 | break; |
1050 | case PERF_HPP_DIFF__RATIO: | 1090 | case PERF_HPP_DIFF__RATIO: |
1051 | fmt->color = hpp__color_ratio; | 1091 | fmt->color = hpp__color_ratio; |
1092 | fmt->sort = hist_entry__cmp_ratio; | ||
1052 | break; | 1093 | break; |
1053 | case PERF_HPP_DIFF__WEIGHTED_DIFF: | 1094 | case PERF_HPP_DIFF__WEIGHTED_DIFF: |
1054 | fmt->color = hpp__color_wdiff; | 1095 | fmt->color = hpp__color_wdiff; |
1096 | fmt->sort = hist_entry__cmp_wdiff; | ||
1055 | break; | 1097 | break; |
1056 | default: | 1098 | default: |
1099 | fmt->sort = hist_entry__cmp_nop; | ||
1057 | break; | 1100 | break; |
1058 | } | 1101 | } |
1059 | 1102 | ||
1060 | init_header(d, dfmt); | 1103 | init_header(d, dfmt); |
1061 | perf_hpp__column_register(fmt); | 1104 | perf_hpp__column_register(fmt); |
1105 | perf_hpp__register_sort_field(fmt); | ||
1062 | } | 1106 | } |
1063 | 1107 | ||
1064 | static void ui_init(void) | 1108 | static void ui_init(void) |
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 011195e38f21..198f3c3aff95 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c | |||
@@ -19,7 +19,9 @@ | |||
19 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | 19 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) |
20 | { | 20 | { |
21 | int i; | 21 | int i; |
22 | const struct option list_options[] = { | 22 | bool raw_dump = false; |
23 | struct option list_options[] = { | ||
24 | OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), | ||
23 | OPT_END() | 25 | OPT_END() |
24 | }; | 26 | }; |
25 | const char * const list_usage[] = { | 27 | const char * const list_usage[] = { |
@@ -27,11 +29,18 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
27 | NULL | 29 | NULL |
28 | }; | 30 | }; |
29 | 31 | ||
32 | set_option_flag(list_options, 0, "raw-dump", PARSE_OPT_HIDDEN); | ||
33 | |||
30 | argc = parse_options(argc, argv, list_options, list_usage, | 34 | argc = parse_options(argc, argv, list_options, list_usage, |
31 | PARSE_OPT_STOP_AT_NON_OPTION); | 35 | PARSE_OPT_STOP_AT_NON_OPTION); |
32 | 36 | ||
33 | setup_pager(); | 37 | setup_pager(); |
34 | 38 | ||
39 | if (raw_dump) { | ||
40 | print_events(NULL, true); | ||
41 | return 0; | ||
42 | } | ||
43 | |||
35 | if (argc == 0) { | 44 | if (argc == 0) { |
36 | print_events(NULL, false); | 45 | print_events(NULL, false); |
37 | return 0; | 46 | return 0; |
@@ -53,8 +62,6 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
53 | print_hwcache_events(NULL, false); | 62 | print_hwcache_events(NULL, false); |
54 | else if (strcmp(argv[i], "pmu") == 0) | 63 | else if (strcmp(argv[i], "pmu") == 0) |
55 | print_pmu_events(NULL, false); | 64 | print_pmu_events(NULL, false); |
56 | else if (strcmp(argv[i], "--raw-dump") == 0) | ||
57 | print_events(NULL, true); | ||
58 | else { | 65 | else { |
59 | char *sep = strchr(argv[i], ':'), *s; | 66 | char *sep = strchr(argv[i], ':'), *s; |
60 | int sep_idx; | 67 | int sep_idx; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 39367609c707..072ae8ad67fc 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -457,6 +457,19 @@ static void report__collapse_hists(struct report *rep) | |||
457 | ui_progress__finish(); | 457 | ui_progress__finish(); |
458 | } | 458 | } |
459 | 459 | ||
460 | static void report__output_resort(struct report *rep) | ||
461 | { | ||
462 | struct ui_progress prog; | ||
463 | struct perf_evsel *pos; | ||
464 | |||
465 | ui_progress__init(&prog, rep->nr_entries, "Sorting events for output..."); | ||
466 | |||
467 | evlist__for_each(rep->session->evlist, pos) | ||
468 | hists__output_resort(evsel__hists(pos), &prog); | ||
469 | |||
470 | ui_progress__finish(); | ||
471 | } | ||
472 | |||
460 | static int __cmd_report(struct report *rep) | 473 | static int __cmd_report(struct report *rep) |
461 | { | 474 | { |
462 | int ret; | 475 | int ret; |
@@ -505,13 +518,20 @@ static int __cmd_report(struct report *rep) | |||
505 | if (session_done()) | 518 | if (session_done()) |
506 | return 0; | 519 | return 0; |
507 | 520 | ||
521 | /* | ||
522 | * recalculate number of entries after collapsing since it | ||
523 | * might be changed during the collapse phase. | ||
524 | */ | ||
525 | rep->nr_entries = 0; | ||
526 | evlist__for_each(session->evlist, pos) | ||
527 | rep->nr_entries += evsel__hists(pos)->nr_entries; | ||
528 | |||
508 | if (rep->nr_entries == 0) { | 529 | if (rep->nr_entries == 0) { |
509 | ui__error("The %s file has no samples!\n", file->path); | 530 | ui__error("The %s file has no samples!\n", file->path); |
510 | return 0; | 531 | return 0; |
511 | } | 532 | } |
512 | 533 | ||
513 | evlist__for_each(session->evlist, pos) | 534 | report__output_resort(rep); |
514 | hists__output_resort(evsel__hists(pos)); | ||
515 | 535 | ||
516 | return report__browse_hists(rep); | 536 | return report__browse_hists(rep); |
517 | } | 537 | } |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 0aa7747ff139..961cea183a83 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -285,7 +285,7 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
285 | } | 285 | } |
286 | 286 | ||
287 | hists__collapse_resort(hists, NULL); | 287 | hists__collapse_resort(hists, NULL); |
288 | hists__output_resort(hists); | 288 | hists__output_resort(hists, NULL); |
289 | 289 | ||
290 | hists__output_recalc_col_len(hists, top->print_entries - printed); | 290 | hists__output_recalc_col_len(hists, top->print_entries - printed); |
291 | putchar('\n'); | 291 | putchar('\n'); |
@@ -554,7 +554,7 @@ static void perf_top__sort_new_samples(void *arg) | |||
554 | } | 554 | } |
555 | 555 | ||
556 | hists__collapse_resort(hists, NULL); | 556 | hists__collapse_resort(hists, NULL); |
557 | hists__output_resort(hists); | 557 | hists__output_resort(hists, NULL); |
558 | } | 558 | } |
559 | 559 | ||
560 | static void *display_thread_tui(void *arg) | 560 | static void *display_thread_tui(void *arg) |
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 614d5c4978ab..8d110dec393e 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c | |||
@@ -187,7 +187,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec | |||
187 | * function since TEST_ASSERT_VAL() returns in case of failure. | 187 | * function since TEST_ASSERT_VAL() returns in case of failure. |
188 | */ | 188 | */ |
189 | hists__collapse_resort(hists, NULL); | 189 | hists__collapse_resort(hists, NULL); |
190 | hists__output_resort(hists); | 190 | hists__output_resort(hists, NULL); |
191 | 191 | ||
192 | if (verbose > 2) { | 192 | if (verbose > 2) { |
193 | pr_info("use callchain: %d, cumulate callchain: %d\n", | 193 | pr_info("use callchain: %d, cumulate callchain: %d\n", |
@@ -454,12 +454,12 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) | |||
454 | * 30.00% 10.00% perf perf [.] cmd_record | 454 | * 30.00% 10.00% perf perf [.] cmd_record |
455 | * 20.00% 0.00% bash libc [.] malloc | 455 | * 20.00% 0.00% bash libc [.] malloc |
456 | * 10.00% 10.00% bash [kernel] [k] page_fault | 456 | * 10.00% 10.00% bash [kernel] [k] page_fault |
457 | * 10.00% 10.00% perf [kernel] [k] schedule | 457 | * 10.00% 10.00% bash bash [.] xmalloc |
458 | * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open | ||
459 | * 10.00% 10.00% perf [kernel] [k] page_fault | 458 | * 10.00% 10.00% perf [kernel] [k] page_fault |
460 | * 10.00% 10.00% perf libc [.] free | ||
461 | * 10.00% 10.00% perf libc [.] malloc | 459 | * 10.00% 10.00% perf libc [.] malloc |
462 | * 10.00% 10.00% bash bash [.] xmalloc | 460 | * 10.00% 10.00% perf [kernel] [k] schedule |
461 | * 10.00% 10.00% perf libc [.] free | ||
462 | * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open | ||
463 | */ | 463 | */ |
464 | struct result expected[] = { | 464 | struct result expected[] = { |
465 | { 7000, 2000, "perf", "perf", "main" }, | 465 | { 7000, 2000, "perf", "perf", "main" }, |
@@ -468,12 +468,12 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) | |||
468 | { 3000, 1000, "perf", "perf", "cmd_record" }, | 468 | { 3000, 1000, "perf", "perf", "cmd_record" }, |
469 | { 2000, 0, "bash", "libc", "malloc" }, | 469 | { 2000, 0, "bash", "libc", "malloc" }, |
470 | { 1000, 1000, "bash", "[kernel]", "page_fault" }, | 470 | { 1000, 1000, "bash", "[kernel]", "page_fault" }, |
471 | { 1000, 1000, "perf", "[kernel]", "schedule" }, | 471 | { 1000, 1000, "bash", "bash", "xmalloc" }, |
472 | { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" }, | ||
473 | { 1000, 1000, "perf", "[kernel]", "page_fault" }, | 472 | { 1000, 1000, "perf", "[kernel]", "page_fault" }, |
473 | { 1000, 1000, "perf", "[kernel]", "schedule" }, | ||
474 | { 1000, 1000, "perf", "libc", "free" }, | 474 | { 1000, 1000, "perf", "libc", "free" }, |
475 | { 1000, 1000, "perf", "libc", "malloc" }, | 475 | { 1000, 1000, "perf", "libc", "malloc" }, |
476 | { 1000, 1000, "bash", "bash", "xmalloc" }, | 476 | { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" }, |
477 | }; | 477 | }; |
478 | 478 | ||
479 | symbol_conf.use_callchain = false; | 479 | symbol_conf.use_callchain = false; |
@@ -537,10 +537,13 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
537 | * malloc | 537 | * malloc |
538 | * main | 538 | * main |
539 | * | 539 | * |
540 | * 10.00% 10.00% perf [kernel] [k] schedule | 540 | * 10.00% 10.00% bash bash [.] xmalloc |
541 | * | | 541 | * | |
542 | * --- schedule | 542 | * --- xmalloc |
543 | * run_command | 543 | * malloc |
544 | * xmalloc <--- NOTE: there's a cycle | ||
545 | * malloc | ||
546 | * xmalloc | ||
544 | * main | 547 | * main |
545 | * | 548 | * |
546 | * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open | 549 | * 10.00% 0.00% perf [kernel] [k] sys_perf_event_open |
@@ -556,6 +559,12 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
556 | * run_command | 559 | * run_command |
557 | * main | 560 | * main |
558 | * | 561 | * |
562 | * 10.00% 10.00% perf [kernel] [k] schedule | ||
563 | * | | ||
564 | * --- schedule | ||
565 | * run_command | ||
566 | * main | ||
567 | * | ||
559 | * 10.00% 10.00% perf libc [.] free | 568 | * 10.00% 10.00% perf libc [.] free |
560 | * | | 569 | * | |
561 | * --- free | 570 | * --- free |
@@ -570,15 +579,6 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
570 | * run_command | 579 | * run_command |
571 | * main | 580 | * main |
572 | * | 581 | * |
573 | * 10.00% 10.00% bash bash [.] xmalloc | ||
574 | * | | ||
575 | * --- xmalloc | ||
576 | * malloc | ||
577 | * xmalloc <--- NOTE: there's a cycle | ||
578 | * malloc | ||
579 | * xmalloc | ||
580 | * main | ||
581 | * | ||
582 | */ | 582 | */ |
583 | struct result expected[] = { | 583 | struct result expected[] = { |
584 | { 7000, 2000, "perf", "perf", "main" }, | 584 | { 7000, 2000, "perf", "perf", "main" }, |
@@ -587,12 +587,12 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
587 | { 3000, 1000, "perf", "perf", "cmd_record" }, | 587 | { 3000, 1000, "perf", "perf", "cmd_record" }, |
588 | { 2000, 0, "bash", "libc", "malloc" }, | 588 | { 2000, 0, "bash", "libc", "malloc" }, |
589 | { 1000, 1000, "bash", "[kernel]", "page_fault" }, | 589 | { 1000, 1000, "bash", "[kernel]", "page_fault" }, |
590 | { 1000, 1000, "perf", "[kernel]", "schedule" }, | 590 | { 1000, 1000, "bash", "bash", "xmalloc" }, |
591 | { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" }, | 591 | { 1000, 0, "perf", "[kernel]", "sys_perf_event_open" }, |
592 | { 1000, 1000, "perf", "[kernel]", "page_fault" }, | 592 | { 1000, 1000, "perf", "[kernel]", "page_fault" }, |
593 | { 1000, 1000, "perf", "[kernel]", "schedule" }, | ||
593 | { 1000, 1000, "perf", "libc", "free" }, | 594 | { 1000, 1000, "perf", "libc", "free" }, |
594 | { 1000, 1000, "perf", "libc", "malloc" }, | 595 | { 1000, 1000, "perf", "libc", "malloc" }, |
595 | { 1000, 1000, "bash", "bash", "xmalloc" }, | ||
596 | }; | 596 | }; |
597 | struct callchain_result expected_callchain[] = { | 597 | struct callchain_result expected_callchain[] = { |
598 | { | 598 | { |
@@ -622,9 +622,12 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
622 | { "bash", "main" }, }, | 622 | { "bash", "main" }, }, |
623 | }, | 623 | }, |
624 | { | 624 | { |
625 | 3, { { "[kernel]", "schedule" }, | 625 | 6, { { "bash", "xmalloc" }, |
626 | { "perf", "run_command" }, | 626 | { "libc", "malloc" }, |
627 | { "perf", "main" }, }, | 627 | { "bash", "xmalloc" }, |
628 | { "libc", "malloc" }, | ||
629 | { "bash", "xmalloc" }, | ||
630 | { "bash", "main" }, }, | ||
628 | }, | 631 | }, |
629 | { | 632 | { |
630 | 3, { { "[kernel]", "sys_perf_event_open" }, | 633 | 3, { { "[kernel]", "sys_perf_event_open" }, |
@@ -638,6 +641,11 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
638 | { "perf", "main" }, }, | 641 | { "perf", "main" }, }, |
639 | }, | 642 | }, |
640 | { | 643 | { |
644 | 3, { { "[kernel]", "schedule" }, | ||
645 | { "perf", "run_command" }, | ||
646 | { "perf", "main" }, }, | ||
647 | }, | ||
648 | { | ||
641 | 4, { { "libc", "free" }, | 649 | 4, { { "libc", "free" }, |
642 | { "perf", "cmd_record" }, | 650 | { "perf", "cmd_record" }, |
643 | { "perf", "run_command" }, | 651 | { "perf", "run_command" }, |
@@ -649,14 +657,6 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
649 | { "perf", "run_command" }, | 657 | { "perf", "run_command" }, |
650 | { "perf", "main" }, }, | 658 | { "perf", "main" }, }, |
651 | }, | 659 | }, |
652 | { | ||
653 | 6, { { "bash", "xmalloc" }, | ||
654 | { "libc", "malloc" }, | ||
655 | { "bash", "xmalloc" }, | ||
656 | { "libc", "malloc" }, | ||
657 | { "bash", "xmalloc" }, | ||
658 | { "bash", "main" }, }, | ||
659 | }, | ||
660 | }; | 660 | }; |
661 | 661 | ||
662 | symbol_conf.use_callchain = true; | 662 | symbol_conf.use_callchain = true; |
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 74f257a81265..59e53db7914c 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c | |||
@@ -138,7 +138,7 @@ int test__hists_filter(void) | |||
138 | struct hists *hists = evsel__hists(evsel); | 138 | struct hists *hists = evsel__hists(evsel); |
139 | 139 | ||
140 | hists__collapse_resort(hists, NULL); | 140 | hists__collapse_resort(hists, NULL); |
141 | hists__output_resort(hists); | 141 | hists__output_resort(hists, NULL); |
142 | 142 | ||
143 | if (verbose > 2) { | 143 | if (verbose > 2) { |
144 | pr_info("Normal histogram\n"); | 144 | pr_info("Normal histogram\n"); |
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index a748f2be1222..f5547610da02 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c | |||
@@ -152,7 +152,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine) | |||
152 | goto out; | 152 | goto out; |
153 | 153 | ||
154 | hists__collapse_resort(hists, NULL); | 154 | hists__collapse_resort(hists, NULL); |
155 | hists__output_resort(hists); | 155 | hists__output_resort(hists, NULL); |
156 | 156 | ||
157 | if (verbose > 2) { | 157 | if (verbose > 2) { |
158 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 158 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
@@ -252,7 +252,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine) | |||
252 | goto out; | 252 | goto out; |
253 | 253 | ||
254 | hists__collapse_resort(hists, NULL); | 254 | hists__collapse_resort(hists, NULL); |
255 | hists__output_resort(hists); | 255 | hists__output_resort(hists, NULL); |
256 | 256 | ||
257 | if (verbose > 2) { | 257 | if (verbose > 2) { |
258 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 258 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
@@ -306,7 +306,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) | |||
306 | goto out; | 306 | goto out; |
307 | 307 | ||
308 | hists__collapse_resort(hists, NULL); | 308 | hists__collapse_resort(hists, NULL); |
309 | hists__output_resort(hists); | 309 | hists__output_resort(hists, NULL); |
310 | 310 | ||
311 | if (verbose > 2) { | 311 | if (verbose > 2) { |
312 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 312 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
@@ -384,7 +384,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
384 | goto out; | 384 | goto out; |
385 | 385 | ||
386 | hists__collapse_resort(hists, NULL); | 386 | hists__collapse_resort(hists, NULL); |
387 | hists__output_resort(hists); | 387 | hists__output_resort(hists, NULL); |
388 | 388 | ||
389 | if (verbose > 2) { | 389 | if (verbose > 2) { |
390 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 390 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
@@ -487,7 +487,7 @@ static int test5(struct perf_evsel *evsel, struct machine *machine) | |||
487 | goto out; | 487 | goto out; |
488 | 488 | ||
489 | hists__collapse_resort(hists, NULL); | 489 | hists__collapse_resort(hists, NULL); |
490 | hists__output_resort(hists); | 490 | hists__output_resort(hists, NULL); |
491 | 491 | ||
492 | if (verbose > 2) { | 492 | if (verbose > 2) { |
493 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); | 493 | pr_info("[fields = %s, sort = %s]\n", field_order, sort_order); |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index e6bb04b5b09b..788506eef567 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -550,7 +550,7 @@ static int hist_browser__show_callchain(struct hist_browser *browser, | |||
550 | bool need_percent; | 550 | bool need_percent; |
551 | 551 | ||
552 | node = rb_first(root); | 552 | node = rb_first(root); |
553 | need_percent = !!rb_next(node); | 553 | need_percent = node && rb_next(node); |
554 | 554 | ||
555 | while (node) { | 555 | while (node) { |
556 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 556 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index dc0d095f318c..482adae3cc44 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -204,6 +204,9 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, | |||
204 | if (ret) | 204 | if (ret) |
205 | return ret; | 205 | return ret; |
206 | 206 | ||
207 | if (a->thread != b->thread || !symbol_conf.use_callchain) | ||
208 | return 0; | ||
209 | |||
207 | ret = b->callchain->max_depth - a->callchain->max_depth; | 210 | ret = b->callchain->max_depth - a->callchain->max_depth; |
208 | } | 211 | } |
209 | return ret; | 212 | return ret; |
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 2f612562978c..3c38f25b1695 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c | |||
@@ -1,5 +1,8 @@ | |||
1 | #include <signal.h> | 1 | #include <signal.h> |
2 | #include <stdbool.h> | 2 | #include <stdbool.h> |
3 | #ifdef HAVE_BACKTRACE_SUPPORT | ||
4 | #include <execinfo.h> | ||
5 | #endif | ||
3 | 6 | ||
4 | #include "../../util/cache.h" | 7 | #include "../../util/cache.h" |
5 | #include "../../util/debug.h" | 8 | #include "../../util/debug.h" |
@@ -88,6 +91,25 @@ int ui__getch(int delay_secs) | |||
88 | return SLkp_getkey(); | 91 | return SLkp_getkey(); |
89 | } | 92 | } |
90 | 93 | ||
94 | #ifdef HAVE_BACKTRACE_SUPPORT | ||
95 | static void ui__signal_backtrace(int sig) | ||
96 | { | ||
97 | void *stackdump[32]; | ||
98 | size_t size; | ||
99 | |||
100 | ui__exit(false); | ||
101 | psignal(sig, "perf"); | ||
102 | |||
103 | printf("-------- backtrace --------\n"); | ||
104 | size = backtrace(stackdump, ARRAY_SIZE(stackdump)); | ||
105 | backtrace_symbols_fd(stackdump, size, STDOUT_FILENO); | ||
106 | |||
107 | exit(0); | ||
108 | } | ||
109 | #else | ||
110 | # define ui__signal_backtrace ui__signal | ||
111 | #endif | ||
112 | |||
91 | static void ui__signal(int sig) | 113 | static void ui__signal(int sig) |
92 | { | 114 | { |
93 | ui__exit(false); | 115 | ui__exit(false); |
@@ -122,8 +144,8 @@ int ui__init(void) | |||
122 | ui_browser__init(); | 144 | ui_browser__init(); |
123 | tui_progress__init(); | 145 | tui_progress__init(); |
124 | 146 | ||
125 | signal(SIGSEGV, ui__signal); | 147 | signal(SIGSEGV, ui__signal_backtrace); |
126 | signal(SIGFPE, ui__signal); | 148 | signal(SIGFPE, ui__signal_backtrace); |
127 | signal(SIGINT, ui__signal); | 149 | signal(SIGINT, ui__signal); |
128 | signal(SIGQUIT, ui__signal); | 150 | signal(SIGQUIT, ui__signal); |
129 | signal(SIGTERM, ui__signal); | 151 | signal(SIGTERM, ui__signal); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 64b377e591e4..14e7a123d43b 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -841,3 +841,33 @@ char *callchain_list__sym_name(struct callchain_list *cl, | |||
841 | 841 | ||
842 | return bf; | 842 | return bf; |
843 | } | 843 | } |
844 | |||
845 | static void free_callchain_node(struct callchain_node *node) | ||
846 | { | ||
847 | struct callchain_list *list, *tmp; | ||
848 | struct callchain_node *child; | ||
849 | struct rb_node *n; | ||
850 | |||
851 | list_for_each_entry_safe(list, tmp, &node->val, list) { | ||
852 | list_del(&list->list); | ||
853 | free(list); | ||
854 | } | ||
855 | |||
856 | n = rb_first(&node->rb_root_in); | ||
857 | while (n) { | ||
858 | child = container_of(n, struct callchain_node, rb_node_in); | ||
859 | n = rb_next(n); | ||
860 | rb_erase(&child->rb_node_in, &node->rb_root_in); | ||
861 | |||
862 | free_callchain_node(child); | ||
863 | free(child); | ||
864 | } | ||
865 | } | ||
866 | |||
867 | void free_callchain(struct callchain_root *root) | ||
868 | { | ||
869 | if (!symbol_conf.use_callchain) | ||
870 | return; | ||
871 | |||
872 | free_callchain_node(&root->node); | ||
873 | } | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index dbc08cf5f970..c0ec1acc38e4 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -198,4 +198,6 @@ static inline int arch_skip_callchain_idx(struct thread *thread __maybe_unused, | |||
198 | char *callchain_list__sym_name(struct callchain_list *cl, | 198 | char *callchain_list__sym_name(struct callchain_list *cl, |
199 | char *bf, size_t bfsize, bool show_dso); | 199 | char *bf, size_t bfsize, bool show_dso); |
200 | 200 | ||
201 | void free_callchain(struct callchain_root *root); | ||
202 | |||
201 | #endif /* __PERF_CALLCHAIN_H */ | 203 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 6e88b9e395df..182395546ddc 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include "evlist.h" | 6 | #include "evlist.h" |
7 | #include "evsel.h" | 7 | #include "evsel.h" |
8 | #include "annotate.h" | 8 | #include "annotate.h" |
9 | #include "ui/progress.h" | ||
9 | #include <math.h> | 10 | #include <math.h> |
10 | 11 | ||
11 | static bool hists__filter_entry_by_dso(struct hists *hists, | 12 | static bool hists__filter_entry_by_dso(struct hists *hists, |
@@ -303,7 +304,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, | |||
303 | size_t callchain_size = 0; | 304 | size_t callchain_size = 0; |
304 | struct hist_entry *he; | 305 | struct hist_entry *he; |
305 | 306 | ||
306 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) | 307 | if (symbol_conf.use_callchain) |
307 | callchain_size = sizeof(struct callchain_root); | 308 | callchain_size = sizeof(struct callchain_root); |
308 | 309 | ||
309 | he = zalloc(sizeof(*he) + callchain_size); | 310 | he = zalloc(sizeof(*he) + callchain_size); |
@@ -736,7 +737,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, | |||
736 | iter->he = he; | 737 | iter->he = he; |
737 | he_cache[iter->curr++] = he; | 738 | he_cache[iter->curr++] = he; |
738 | 739 | ||
739 | callchain_append(he->callchain, &callchain_cursor, sample->period); | 740 | hist_entry__append_callchain(he, sample); |
740 | 741 | ||
741 | /* | 742 | /* |
742 | * We need to re-initialize the cursor since callchain_append() | 743 | * We need to re-initialize the cursor since callchain_append() |
@@ -809,7 +810,8 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, | |||
809 | iter->he = he; | 810 | iter->he = he; |
810 | he_cache[iter->curr++] = he; | 811 | he_cache[iter->curr++] = he; |
811 | 812 | ||
812 | callchain_append(he->callchain, &cursor, sample->period); | 813 | if (symbol_conf.use_callchain) |
814 | callchain_append(he->callchain, &cursor, sample->period); | ||
813 | return 0; | 815 | return 0; |
814 | } | 816 | } |
815 | 817 | ||
@@ -945,6 +947,7 @@ void hist_entry__free(struct hist_entry *he) | |||
945 | zfree(&he->mem_info); | 947 | zfree(&he->mem_info); |
946 | zfree(&he->stat_acc); | 948 | zfree(&he->stat_acc); |
947 | free_srcline(he->srcline); | 949 | free_srcline(he->srcline); |
950 | free_callchain(he->callchain); | ||
948 | free(he); | 951 | free(he); |
949 | } | 952 | } |
950 | 953 | ||
@@ -987,6 +990,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | |||
987 | else | 990 | else |
988 | p = &(*p)->rb_right; | 991 | p = &(*p)->rb_right; |
989 | } | 992 | } |
993 | hists->nr_entries++; | ||
990 | 994 | ||
991 | rb_link_node(&he->rb_node_in, parent, p); | 995 | rb_link_node(&he->rb_node_in, parent, p); |
992 | rb_insert_color(&he->rb_node_in, root); | 996 | rb_insert_color(&he->rb_node_in, root); |
@@ -1024,7 +1028,10 @@ void hists__collapse_resort(struct hists *hists, struct ui_progress *prog) | |||
1024 | if (!sort__need_collapse) | 1028 | if (!sort__need_collapse) |
1025 | return; | 1029 | return; |
1026 | 1030 | ||
1031 | hists->nr_entries = 0; | ||
1032 | |||
1027 | root = hists__get_rotate_entries_in(hists); | 1033 | root = hists__get_rotate_entries_in(hists); |
1034 | |||
1028 | next = rb_first(root); | 1035 | next = rb_first(root); |
1029 | 1036 | ||
1030 | while (next) { | 1037 | while (next) { |
@@ -1119,7 +1126,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
1119 | rb_insert_color(&he->rb_node, entries); | 1126 | rb_insert_color(&he->rb_node, entries); |
1120 | } | 1127 | } |
1121 | 1128 | ||
1122 | void hists__output_resort(struct hists *hists) | 1129 | void hists__output_resort(struct hists *hists, struct ui_progress *prog) |
1123 | { | 1130 | { |
1124 | struct rb_root *root; | 1131 | struct rb_root *root; |
1125 | struct rb_node *next; | 1132 | struct rb_node *next; |
@@ -1148,6 +1155,9 @@ void hists__output_resort(struct hists *hists) | |||
1148 | 1155 | ||
1149 | if (!n->filtered) | 1156 | if (!n->filtered) |
1150 | hists__calc_col_len(hists, n); | 1157 | hists__calc_col_len(hists, n); |
1158 | |||
1159 | if (prog) | ||
1160 | ui_progress__update(prog, 1); | ||
1151 | } | 1161 | } |
1152 | } | 1162 | } |
1153 | 1163 | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index d0ef9a19a744..46bd50344f85 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -121,7 +121,7 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, | |||
121 | struct hists *hists); | 121 | struct hists *hists); |
122 | void hist_entry__free(struct hist_entry *); | 122 | void hist_entry__free(struct hist_entry *); |
123 | 123 | ||
124 | void hists__output_resort(struct hists *hists); | 124 | void hists__output_resort(struct hists *hists, struct ui_progress *prog); |
125 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); | 125 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); |
126 | 126 | ||
127 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 127 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 28eb1417cb2a..7f9b8632e433 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -495,9 +495,11 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
495 | } | 495 | } |
496 | 496 | ||
497 | if (ntevs == 0) { /* No error but failed to find probe point. */ | 497 | if (ntevs == 0) { /* No error but failed to find probe point. */ |
498 | pr_warning("Probe point '%s' not found.\n", | 498 | pr_warning("Probe point '%s' not found in debuginfo.\n", |
499 | synthesize_perf_probe_point(&pev->point)); | 499 | synthesize_perf_probe_point(&pev->point)); |
500 | return -ENOENT; | 500 | if (need_dwarf) |
501 | return -ENOENT; | ||
502 | return 0; | ||
501 | } | 503 | } |
502 | /* Error path : ntevs < 0 */ | 504 | /* Error path : ntevs < 0 */ |
503 | pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); | 505 | pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c7918f83b300..b5247d777f0e 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -989,8 +989,24 @@ static int debuginfo__find_probes(struct debuginfo *dbg, | |||
989 | int ret = 0; | 989 | int ret = 0; |
990 | 990 | ||
991 | #if _ELFUTILS_PREREQ(0, 142) | 991 | #if _ELFUTILS_PREREQ(0, 142) |
992 | Elf *elf; | ||
993 | GElf_Ehdr ehdr; | ||
994 | GElf_Shdr shdr; | ||
995 | |||
992 | /* Get the call frame information from this dwarf */ | 996 | /* Get the call frame information from this dwarf */ |
993 | pf->cfi = dwarf_getcfi_elf(dwarf_getelf(dbg->dbg)); | 997 | elf = dwarf_getelf(dbg->dbg); |
998 | if (elf == NULL) | ||
999 | return -EINVAL; | ||
1000 | |||
1001 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
1002 | return -EINVAL; | ||
1003 | |||
1004 | if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) && | ||
1005 | shdr.sh_type == SHT_PROGBITS) { | ||
1006 | pf->cfi = dwarf_getcfi_elf(elf); | ||
1007 | } else { | ||
1008 | pf->cfi = dwarf_getcfi(dbg->dbg); | ||
1009 | } | ||
994 | #endif | 1010 | #endif |
995 | 1011 | ||
996 | off = 0; | 1012 | off = 0; |