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; |
