diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-09 18:43:55 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-02-09 18:43:55 -0500 |
| commit | a4cbbf549a9be10b7583c44249efccd64839533d (patch) | |
| tree | 4b4862e4513b629723b8853e379cd38fee08b095 | |
| parent | 8308756f45a12e2ff4f7749c2694fc83cdef0be9 (diff) | |
| parent | 2fde4f94e0a9531251e706fa57131b51b0df042e (diff) | |
Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar:
"Kernel side changes:
- AMD range breakpoints support:
Extend breakpoint tools and core to support address range through
perf event with initial backend support for AMD extended
breakpoints.
The syntax is:
perf record -e mem:addr/len:type
For example set write breakpoint from 0x1000 to 0x1200 (0x1000 + 512)
perf record -e mem:0x1000/512:w
- event throttling/rotating fixes
- various event group handling fixes, cleanups and general paranoia
code to be more robust against bugs in the future.
- kernel stack overhead fixes
User-visible tooling side changes:
- Show precise number of samples in at the end of a 'record' session,
if processing build ids, since we will then traverse the whole
perf.data file and see all the PERF_RECORD_SAMPLE records,
otherwise stop showing the previous off-base heuristicly counted
number of "samples" (Namhyung Kim).
- Support to read compressed module from build-id cache (Namhyung
Kim)
- Enable sampling loads and stores simultaneously in 'perf mem'
(Stephane Eranian)
- 'perf diff' output improvements (Namhyung Kim)
- Fix error reporting for evsel pgfault constructor (Arnaldo Carvalho
de Melo)
Tooling side infrastructure changes:
- Cache eh/debug frame offset for dwarf unwind (Namhyung Kim)
- Support parsing parameterized events (Cody P Schafer)
- Add support for IP address formats in libtraceevent (David Ahern)
Plus other misc fixes"
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (48 commits)
perf: Decouple unthrottling and rotating
perf: Drop module reference on event init failure
perf: Use POLLIN instead of POLL_IN for perf poll data in flag
perf: Fix put_event() ctx lock
perf: Fix move_group() order
perf: Fix event->ctx locking
perf: Add a bit of paranoia
perf symbols: Convert lseek + read to pread
perf tools: Use perf_data_file__fd() consistently
perf symbols: Support to read compressed module from build-id cache
perf evsel: Set attr.task bit for a tracking event
perf header: Set header version correctly
perf record: Show precise number of samples
perf tools: Do not use __perf_session__process_events() directly
perf callchain: Cache eh/debug frame offset for dwarf unwind
perf tools: Provide stub for missing pthread_attr_setaffinity_np
perf evsel: Don't rely on malloc working for sz 0
tools lib traceevent: Add support for IP address formats
perf ui/tui: Show fatal error message only if exists
perf tests: Fix typo in sample-parsing.c
...
76 files changed, 1611 insertions, 666 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events index 20979f8b3edb..505f080d20a1 100644 --- a/Documentation/ABI/testing/sysfs-bus-event_source-devices-events +++ b/Documentation/ABI/testing/sysfs-bus-event_source-devices-events | |||
| @@ -52,12 +52,18 @@ Description: Per-pmu performance monitoring events specific to the running syste | |||
| 52 | event=0x2abc | 52 | event=0x2abc |
| 53 | event=0x423,inv,cmask=0x3 | 53 | event=0x423,inv,cmask=0x3 |
| 54 | domain=0x1,offset=0x8,starting_index=0xffff | 54 | domain=0x1,offset=0x8,starting_index=0xffff |
| 55 | domain=0x1,offset=0x8,core=? | ||
| 55 | 56 | ||
| 56 | Each of the assignments indicates a value to be assigned to a | 57 | Each of the assignments indicates a value to be assigned to a |
| 57 | particular set of bits (as defined by the format file | 58 | particular set of bits (as defined by the format file |
| 58 | corresponding to the <term>) in the perf_event structure passed | 59 | corresponding to the <term>) in the perf_event structure passed |
| 59 | to the perf_open syscall. | 60 | to the perf_open syscall. |
| 60 | 61 | ||
| 62 | In the case of the last example, a value replacing "?" would | ||
| 63 | need to be provided by the user selecting the particular event. | ||
| 64 | This is referred to as "event parameterization". Event | ||
| 65 | parameters have the format 'param=?'. | ||
| 66 | |||
| 61 | What: /sys/bus/event_source/devices/<pmu>/events/<event>.unit | 67 | What: /sys/bus/event_source/devices/<pmu>/events/<event>.unit |
| 62 | Date: 2014/02/24 | 68 | Date: 2014/02/24 |
| 63 | Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org> | 69 | Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org> |
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index aede2c347bde..90a54851aedc 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h | |||
| @@ -174,6 +174,7 @@ | |||
| 174 | #define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */ | 174 | #define X86_FEATURE_TOPOEXT ( 6*32+22) /* topology extensions CPUID leafs */ |
| 175 | #define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */ | 175 | #define X86_FEATURE_PERFCTR_CORE ( 6*32+23) /* core performance counter extensions */ |
| 176 | #define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */ | 176 | #define X86_FEATURE_PERFCTR_NB ( 6*32+24) /* NB performance counter extensions */ |
| 177 | #define X86_FEATURE_BPEXT (6*32+26) /* data breakpoint extension */ | ||
| 177 | #define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */ | 178 | #define X86_FEATURE_PERFCTR_L2 ( 6*32+28) /* L2 performance counter extensions */ |
| 178 | 179 | ||
| 179 | /* | 180 | /* |
| @@ -388,6 +389,7 @@ extern const char * const x86_bug_flags[NBUGINTS*32]; | |||
| 388 | #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) | 389 | #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) |
| 389 | #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) | 390 | #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) |
| 390 | #define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) | 391 | #define cpu_has_topoext boot_cpu_has(X86_FEATURE_TOPOEXT) |
| 392 | #define cpu_has_bpext boot_cpu_has(X86_FEATURE_BPEXT) | ||
| 391 | 393 | ||
| 392 | #if __GNUC__ >= 4 | 394 | #if __GNUC__ >= 4 |
| 393 | extern void warn_pre_alternatives(void); | 395 | extern void warn_pre_alternatives(void); |
diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h index 61fd18b83b6c..12cb66f6d3a5 100644 --- a/arch/x86/include/asm/debugreg.h +++ b/arch/x86/include/asm/debugreg.h | |||
| @@ -114,5 +114,10 @@ static inline void debug_stack_usage_inc(void) { } | |||
| 114 | static inline void debug_stack_usage_dec(void) { } | 114 | static inline void debug_stack_usage_dec(void) { } |
| 115 | #endif /* X86_64 */ | 115 | #endif /* X86_64 */ |
| 116 | 116 | ||
| 117 | #ifdef CONFIG_CPU_SUP_AMD | ||
| 118 | extern void set_dr_addr_mask(unsigned long mask, int dr); | ||
| 119 | #else | ||
| 120 | static inline void set_dr_addr_mask(unsigned long mask, int dr) { } | ||
| 121 | #endif | ||
| 117 | 122 | ||
| 118 | #endif /* _ASM_X86_DEBUGREG_H */ | 123 | #endif /* _ASM_X86_DEBUGREG_H */ |
diff --git a/arch/x86/include/asm/hw_breakpoint.h b/arch/x86/include/asm/hw_breakpoint.h index ef1c4d2d41ec..6c98be864a75 100644 --- a/arch/x86/include/asm/hw_breakpoint.h +++ b/arch/x86/include/asm/hw_breakpoint.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | */ | 12 | */ |
| 13 | struct arch_hw_breakpoint { | 13 | struct arch_hw_breakpoint { |
| 14 | unsigned long address; | 14 | unsigned long address; |
| 15 | unsigned long mask; | ||
| 15 | u8 len; | 16 | u8 len; |
| 16 | u8 type; | 17 | u8 type; |
| 17 | }; | 18 | }; |
diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index c8aa65d56027..d979e5abae55 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h | |||
| @@ -251,6 +251,10 @@ | |||
| 251 | /* Fam 16h MSRs */ | 251 | /* Fam 16h MSRs */ |
| 252 | #define MSR_F16H_L2I_PERF_CTL 0xc0010230 | 252 | #define MSR_F16H_L2I_PERF_CTL 0xc0010230 |
| 253 | #define MSR_F16H_L2I_PERF_CTR 0xc0010231 | 253 | #define MSR_F16H_L2I_PERF_CTR 0xc0010231 |
| 254 | #define MSR_F16H_DR1_ADDR_MASK 0xc0011019 | ||
| 255 | #define MSR_F16H_DR2_ADDR_MASK 0xc001101a | ||
| 256 | #define MSR_F16H_DR3_ADDR_MASK 0xc001101b | ||
| 257 | #define MSR_F16H_DR0_ADDR_MASK 0xc0011027 | ||
| 254 | 258 | ||
| 255 | /* Fam 15h MSRs */ | 259 | /* Fam 15h MSRs */ |
| 256 | #define MSR_F15H_PERF_CTL 0xc0010200 | 260 | #define MSR_F15H_PERF_CTL 0xc0010200 |
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 15c5df92f74e..a220239cea65 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c | |||
| @@ -869,3 +869,22 @@ static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum) | |||
| 869 | 869 | ||
| 870 | return false; | 870 | return false; |
| 871 | } | 871 | } |
| 872 | |||
| 873 | void set_dr_addr_mask(unsigned long mask, int dr) | ||
| 874 | { | ||
| 875 | if (!cpu_has_bpext) | ||
| 876 | return; | ||
| 877 | |||
| 878 | switch (dr) { | ||
| 879 | case 0: | ||
| 880 | wrmsr(MSR_F16H_DR0_ADDR_MASK, mask, 0); | ||
| 881 | break; | ||
| 882 | case 1: | ||
| 883 | case 2: | ||
| 884 | case 3: | ||
| 885 | wrmsr(MSR_F16H_DR1_ADDR_MASK - 1 + dr, mask, 0); | ||
| 886 | break; | ||
| 887 | default: | ||
| 888 | break; | ||
| 889 | } | ||
| 890 | } | ||
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c index 3d5fb509bdeb..7114ba220fd4 100644 --- a/arch/x86/kernel/hw_breakpoint.c +++ b/arch/x86/kernel/hw_breakpoint.c | |||
| @@ -126,6 +126,8 @@ int arch_install_hw_breakpoint(struct perf_event *bp) | |||
| 126 | *dr7 |= encode_dr7(i, info->len, info->type); | 126 | *dr7 |= encode_dr7(i, info->len, info->type); |
| 127 | 127 | ||
| 128 | set_debugreg(*dr7, 7); | 128 | set_debugreg(*dr7, 7); |
| 129 | if (info->mask) | ||
| 130 | set_dr_addr_mask(info->mask, i); | ||
| 129 | 131 | ||
| 130 | return 0; | 132 | return 0; |
| 131 | } | 133 | } |
| @@ -161,29 +163,8 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) | |||
| 161 | *dr7 &= ~__encode_dr7(i, info->len, info->type); | 163 | *dr7 &= ~__encode_dr7(i, info->len, info->type); |
| 162 | 164 | ||
| 163 | set_debugreg(*dr7, 7); | 165 | set_debugreg(*dr7, 7); |
| 164 | } | 166 | if (info->mask) |
| 165 | 167 | set_dr_addr_mask(0, i); | |
| 166 | static int get_hbp_len(u8 hbp_len) | ||
| 167 | { | ||
| 168 | unsigned int len_in_bytes = 0; | ||
| 169 | |||
| 170 | switch (hbp_len) { | ||
| 171 | case X86_BREAKPOINT_LEN_1: | ||
| 172 | len_in_bytes = 1; | ||
| 173 | break; | ||
| 174 | case X86_BREAKPOINT_LEN_2: | ||
| 175 | len_in_bytes = 2; | ||
| 176 | break; | ||
| 177 | case X86_BREAKPOINT_LEN_4: | ||
| 178 | len_in_bytes = 4; | ||
| 179 | break; | ||
| 180 | #ifdef CONFIG_X86_64 | ||
| 181 | case X86_BREAKPOINT_LEN_8: | ||
| 182 | len_in_bytes = 8; | ||
| 183 | break; | ||
| 184 | #endif | ||
| 185 | } | ||
| 186 | return len_in_bytes; | ||
| 187 | } | 168 | } |
| 188 | 169 | ||
| 189 | /* | 170 | /* |
| @@ -196,7 +177,7 @@ int arch_check_bp_in_kernelspace(struct perf_event *bp) | |||
| 196 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); | 177 | struct arch_hw_breakpoint *info = counter_arch_bp(bp); |
| 197 | 178 | ||
| 198 | va = info->address; | 179 | va = info->address; |
| 199 | len = get_hbp_len(info->len); | 180 | len = bp->attr.bp_len; |
| 200 | 181 | ||
| 201 | return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); | 182 | return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); |
| 202 | } | 183 | } |
| @@ -277,6 +258,8 @@ static int arch_build_bp_info(struct perf_event *bp) | |||
| 277 | } | 258 | } |
| 278 | 259 | ||
| 279 | /* Len */ | 260 | /* Len */ |
| 261 | info->mask = 0; | ||
| 262 | |||
| 280 | switch (bp->attr.bp_len) { | 263 | switch (bp->attr.bp_len) { |
| 281 | case HW_BREAKPOINT_LEN_1: | 264 | case HW_BREAKPOINT_LEN_1: |
| 282 | info->len = X86_BREAKPOINT_LEN_1; | 265 | info->len = X86_BREAKPOINT_LEN_1; |
| @@ -293,11 +276,17 @@ static int arch_build_bp_info(struct perf_event *bp) | |||
| 293 | break; | 276 | break; |
| 294 | #endif | 277 | #endif |
| 295 | default: | 278 | default: |
| 296 | return -EINVAL; | 279 | if (!is_power_of_2(bp->attr.bp_len)) |
| 280 | return -EINVAL; | ||
| 281 | if (!cpu_has_bpext) | ||
| 282 | return -EOPNOTSUPP; | ||
| 283 | info->mask = bp->attr.bp_len - 1; | ||
| 284 | info->len = X86_BREAKPOINT_LEN_1; | ||
| 297 | } | 285 | } |
| 298 | 286 | ||
| 299 | return 0; | 287 | return 0; |
| 300 | } | 288 | } |
| 289 | |||
| 301 | /* | 290 | /* |
| 302 | * Validate the arch-specific HW Breakpoint register settings | 291 | * Validate the arch-specific HW Breakpoint register settings |
| 303 | */ | 292 | */ |
| @@ -312,11 +301,11 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) | |||
| 312 | if (ret) | 301 | if (ret) |
| 313 | return ret; | 302 | return ret; |
| 314 | 303 | ||
| 315 | ret = -EINVAL; | ||
| 316 | |||
| 317 | switch (info->len) { | 304 | switch (info->len) { |
| 318 | case X86_BREAKPOINT_LEN_1: | 305 | case X86_BREAKPOINT_LEN_1: |
| 319 | align = 0; | 306 | align = 0; |
| 307 | if (info->mask) | ||
| 308 | align = info->mask; | ||
| 320 | break; | 309 | break; |
| 321 | case X86_BREAKPOINT_LEN_2: | 310 | case X86_BREAKPOINT_LEN_2: |
| 322 | align = 1; | 311 | align = 1; |
| @@ -330,7 +319,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp) | |||
| 330 | break; | 319 | break; |
| 331 | #endif | 320 | #endif |
| 332 | default: | 321 | default: |
| 333 | return ret; | 322 | WARN_ON_ONCE(1); |
| 334 | } | 323 | } |
| 335 | 324 | ||
| 336 | /* | 325 | /* |
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 0bebb5c348b8..d36f68b08acc 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h | |||
| @@ -595,7 +595,7 @@ extern int ftrace_profile_set_filter(struct perf_event *event, int event_id, | |||
| 595 | char *filter_str); | 595 | char *filter_str); |
| 596 | extern void ftrace_profile_free_filter(struct perf_event *event); | 596 | extern void ftrace_profile_free_filter(struct perf_event *event); |
| 597 | extern void *perf_trace_buf_prepare(int size, unsigned short type, | 597 | extern void *perf_trace_buf_prepare(int size, unsigned short type, |
| 598 | struct pt_regs *regs, int *rctxp); | 598 | struct pt_regs **regs, int *rctxp); |
| 599 | 599 | ||
| 600 | static inline void | 600 | static inline void |
| 601 | perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr, | 601 | perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr, |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 664de5a4ec46..5cad0e6f3552 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -469,6 +469,7 @@ struct perf_event_context { | |||
| 469 | */ | 469 | */ |
| 470 | struct mutex mutex; | 470 | struct mutex mutex; |
| 471 | 471 | ||
| 472 | struct list_head active_ctx_list; | ||
| 472 | struct list_head pinned_groups; | 473 | struct list_head pinned_groups; |
| 473 | struct list_head flexible_groups; | 474 | struct list_head flexible_groups; |
| 474 | struct list_head event_list; | 475 | struct list_head event_list; |
| @@ -519,7 +520,6 @@ struct perf_cpu_context { | |||
| 519 | int exclusive; | 520 | int exclusive; |
| 520 | struct hrtimer hrtimer; | 521 | struct hrtimer hrtimer; |
| 521 | ktime_t hrtimer_interval; | 522 | ktime_t hrtimer_interval; |
| 522 | struct list_head rotation_list; | ||
| 523 | struct pmu *unique_pmu; | 523 | struct pmu *unique_pmu; |
| 524 | struct perf_cgroup *cgrp; | 524 | struct perf_cgroup *cgrp; |
| 525 | }; | 525 | }; |
| @@ -659,6 +659,7 @@ static inline int is_software_event(struct perf_event *event) | |||
| 659 | 659 | ||
| 660 | extern struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX]; | 660 | extern struct static_key perf_swevent_enabled[PERF_COUNT_SW_MAX]; |
| 661 | 661 | ||
| 662 | extern void ___perf_sw_event(u32, u64, struct pt_regs *, u64); | ||
| 662 | extern void __perf_sw_event(u32, u64, struct pt_regs *, u64); | 663 | extern void __perf_sw_event(u32, u64, struct pt_regs *, u64); |
| 663 | 664 | ||
| 664 | #ifndef perf_arch_fetch_caller_regs | 665 | #ifndef perf_arch_fetch_caller_regs |
| @@ -683,14 +684,25 @@ static inline void perf_fetch_caller_regs(struct pt_regs *regs) | |||
| 683 | static __always_inline void | 684 | static __always_inline void |
| 684 | perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) | 685 | perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) |
| 685 | { | 686 | { |
| 686 | struct pt_regs hot_regs; | 687 | if (static_key_false(&perf_swevent_enabled[event_id])) |
| 688 | __perf_sw_event(event_id, nr, regs, addr); | ||
| 689 | } | ||
| 690 | |||
| 691 | DECLARE_PER_CPU(struct pt_regs, __perf_regs[4]); | ||
| 687 | 692 | ||
| 693 | /* | ||
| 694 | * 'Special' version for the scheduler, it hard assumes no recursion, | ||
| 695 | * which is guaranteed by us not actually scheduling inside other swevents | ||
| 696 | * because those disable preemption. | ||
| 697 | */ | ||
| 698 | static __always_inline void | ||
| 699 | perf_sw_event_sched(u32 event_id, u64 nr, u64 addr) | ||
| 700 | { | ||
| 688 | if (static_key_false(&perf_swevent_enabled[event_id])) { | 701 | if (static_key_false(&perf_swevent_enabled[event_id])) { |
| 689 | if (!regs) { | 702 | struct pt_regs *regs = this_cpu_ptr(&__perf_regs[0]); |
| 690 | perf_fetch_caller_regs(&hot_regs); | 703 | |
| 691 | regs = &hot_regs; | 704 | perf_fetch_caller_regs(regs); |
| 692 | } | 705 | ___perf_sw_event(event_id, nr, regs, addr); |
| 693 | __perf_sw_event(event_id, nr, regs, addr); | ||
| 694 | } | 706 | } |
| 695 | } | 707 | } |
| 696 | 708 | ||
| @@ -706,7 +718,7 @@ static inline void perf_event_task_sched_in(struct task_struct *prev, | |||
| 706 | static inline void perf_event_task_sched_out(struct task_struct *prev, | 718 | static inline void perf_event_task_sched_out(struct task_struct *prev, |
| 707 | struct task_struct *next) | 719 | struct task_struct *next) |
| 708 | { | 720 | { |
| 709 | perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0); | 721 | perf_sw_event_sched(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 0); |
| 710 | 722 | ||
| 711 | if (static_key_false(&perf_sched_events.key)) | 723 | if (static_key_false(&perf_sched_events.key)) |
| 712 | __perf_event_task_sched_out(prev, next); | 724 | __perf_event_task_sched_out(prev, next); |
| @@ -817,6 +829,8 @@ static inline int perf_event_refresh(struct perf_event *event, int refresh) | |||
| 817 | static inline void | 829 | static inline void |
| 818 | perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) { } | 830 | perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) { } |
| 819 | static inline void | 831 | static inline void |
| 832 | perf_sw_event_sched(u32 event_id, u64 nr, u64 addr) { } | ||
| 833 | static inline void | ||
| 820 | perf_bp_event(struct perf_event *event, void *data) { } | 834 | perf_bp_event(struct perf_event *event, void *data) { } |
| 821 | 835 | ||
| 822 | static inline int perf_register_guest_info_callbacks | 836 | static inline int perf_register_guest_info_callbacks |
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 139b5067345b..27609dfcce25 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h | |||
| @@ -763,7 +763,7 @@ perf_trace_##call(void *__data, proto) \ | |||
| 763 | struct ftrace_event_call *event_call = __data; \ | 763 | struct ftrace_event_call *event_call = __data; \ |
| 764 | struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\ | 764 | struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\ |
| 765 | struct ftrace_raw_##call *entry; \ | 765 | struct ftrace_raw_##call *entry; \ |
| 766 | struct pt_regs __regs; \ | 766 | struct pt_regs *__regs; \ |
| 767 | u64 __addr = 0, __count = 1; \ | 767 | u64 __addr = 0, __count = 1; \ |
| 768 | struct task_struct *__task = NULL; \ | 768 | struct task_struct *__task = NULL; \ |
| 769 | struct hlist_head *head; \ | 769 | struct hlist_head *head; \ |
| @@ -782,18 +782,19 @@ perf_trace_##call(void *__data, proto) \ | |||
| 782 | sizeof(u64)); \ | 782 | sizeof(u64)); \ |
| 783 | __entry_size -= sizeof(u32); \ | 783 | __entry_size -= sizeof(u32); \ |
| 784 | \ | 784 | \ |
| 785 | perf_fetch_caller_regs(&__regs); \ | ||
| 786 | entry = perf_trace_buf_prepare(__entry_size, \ | 785 | entry = perf_trace_buf_prepare(__entry_size, \ |
| 787 | event_call->event.type, &__regs, &rctx); \ | 786 | event_call->event.type, &__regs, &rctx); \ |
| 788 | if (!entry) \ | 787 | if (!entry) \ |
| 789 | return; \ | 788 | return; \ |
| 790 | \ | 789 | \ |
| 790 | perf_fetch_caller_regs(__regs); \ | ||
| 791 | \ | ||
| 791 | tstruct \ | 792 | tstruct \ |
| 792 | \ | 793 | \ |
| 793 | { assign; } \ | 794 | { assign; } \ |
| 794 | \ | 795 | \ |
| 795 | perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \ | 796 | perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \ |
| 796 | __count, &__regs, head, __task); \ | 797 | __count, __regs, head, __task); \ |
| 797 | } | 798 | } |
| 798 | 799 | ||
| 799 | /* | 800 | /* |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 19efcf13375a..7f2fbb8b5069 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
| @@ -872,22 +872,32 @@ void perf_pmu_enable(struct pmu *pmu) | |||
| 872 | pmu->pmu_enable(pmu); | 872 | pmu->pmu_enable(pmu); |
| 873 | } | 873 | } |
| 874 | 874 | ||
| 875 | static DEFINE_PER_CPU(struct list_head, rotation_list); | 875 | static DEFINE_PER_CPU(struct list_head, active_ctx_list); |
| 876 | 876 | ||
| 877 | /* | 877 | /* |
| 878 | * perf_pmu_rotate_start() and perf_rotate_context() are fully serialized | 878 | * perf_event_ctx_activate(), perf_event_ctx_deactivate(), and |
| 879 | * because they're strictly cpu affine and rotate_start is called with IRQs | 879 | * perf_event_task_tick() are fully serialized because they're strictly cpu |
| 880 | * disabled, while rotate_context is called from IRQ context. | 880 | * affine and perf_event_ctx{activate,deactivate} are called with IRQs |
| 881 | * disabled, while perf_event_task_tick is called from IRQ context. | ||
| 881 | */ | 882 | */ |
| 882 | static void perf_pmu_rotate_start(struct pmu *pmu) | 883 | static void perf_event_ctx_activate(struct perf_event_context *ctx) |
| 883 | { | 884 | { |
| 884 | struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); | 885 | struct list_head *head = this_cpu_ptr(&active_ctx_list); |
| 885 | struct list_head *head = this_cpu_ptr(&rotation_list); | ||
| 886 | 886 | ||
| 887 | WARN_ON(!irqs_disabled()); | 887 | WARN_ON(!irqs_disabled()); |
| 888 | 888 | ||
| 889 | if (list_empty(&cpuctx->rotation_list)) | 889 | WARN_ON(!list_empty(&ctx->active_ctx_list)); |
| 890 | list_add(&cpuctx->rotation_list, head); | 890 | |
| 891 | list_add(&ctx->active_ctx_list, head); | ||
| 892 | } | ||
| 893 | |||
| 894 | static void perf_event_ctx_deactivate(struct perf_event_context *ctx) | ||
| 895 | { | ||
| 896 | WARN_ON(!irqs_disabled()); | ||
| 897 | |||
| 898 | WARN_ON(list_empty(&ctx->active_ctx_list)); | ||
| 899 | |||
| 900 | list_del_init(&ctx->active_ctx_list); | ||
| 891 | } | 901 | } |
| 892 | 902 | ||
| 893 | static void get_ctx(struct perf_event_context *ctx) | 903 | static void get_ctx(struct perf_event_context *ctx) |
| @@ -907,6 +917,84 @@ static void put_ctx(struct perf_event_context *ctx) | |||
| 907 | } | 917 | } |
| 908 | 918 | ||
| 909 | /* | 919 | /* |
| 920 | * Because of perf_event::ctx migration in sys_perf_event_open::move_group and | ||
| 921 | * perf_pmu_migrate_context() we need some magic. | ||
| 922 | * | ||
| 923 | * Those places that change perf_event::ctx will hold both | ||
| 924 | * perf_event_ctx::mutex of the 'old' and 'new' ctx value. | ||
| 925 | * | ||
| 926 | * Lock ordering is by mutex address. There is one other site where | ||
| 927 | * perf_event_context::mutex nests and that is put_event(). But remember that | ||
| 928 | * that is a parent<->child context relation, and migration does not affect | ||
| 929 | * children, therefore these two orderings should not interact. | ||
| 930 | * | ||
| 931 | * The change in perf_event::ctx does not affect children (as claimed above) | ||
| 932 | * because the sys_perf_event_open() case will install a new event and break | ||
| 933 | * the ctx parent<->child relation, and perf_pmu_migrate_context() is only | ||
| 934 | * concerned with cpuctx and that doesn't have children. | ||
| 935 | * | ||
| 936 | * The places that change perf_event::ctx will issue: | ||
| 937 | * | ||
| 938 | * perf_remove_from_context(); | ||
| 939 | * synchronize_rcu(); | ||
| 940 | * perf_install_in_context(); | ||
| 941 | * | ||
| 942 | * to affect the change. The remove_from_context() + synchronize_rcu() should | ||
| 943 | * quiesce the event, after which we can install it in the new location. This | ||
| 944 | * means that only external vectors (perf_fops, prctl) can perturb the event | ||
| 945 | * while in transit. Therefore all such accessors should also acquire | ||
| 946 | * perf_event_context::mutex to serialize against this. | ||
| 947 | * | ||
| 948 | * However; because event->ctx can change while we're waiting to acquire | ||
| 949 | * ctx->mutex we must be careful and use the below perf_event_ctx_lock() | ||
| 950 | * function. | ||
| 951 | * | ||
| 952 | * Lock order: | ||
| 953 | * task_struct::perf_event_mutex | ||
| 954 | * perf_event_context::mutex | ||
| 955 | * perf_event_context::lock | ||
| 956 | * perf_event::child_mutex; | ||
| 957 | * perf_event::mmap_mutex | ||
| 958 | * mmap_sem | ||
| 959 | */ | ||
| 960 | static struct perf_event_context * | ||
| 961 | perf_event_ctx_lock_nested(struct perf_event *event, int nesting) | ||
| 962 | { | ||
| 963 | struct perf_event_context *ctx; | ||
| 964 | |||
| 965 | again: | ||
| 966 | rcu_read_lock(); | ||
| 967 | ctx = ACCESS_ONCE(event->ctx); | ||
| 968 | if (!atomic_inc_not_zero(&ctx->refcount)) { | ||
| 969 | rcu_read_unlock(); | ||
| 970 | goto again; | ||
| 971 | } | ||
| 972 | rcu_read_unlock(); | ||
| 973 | |||
| 974 | mutex_lock_nested(&ctx->mutex, nesting); | ||
| 975 | if (event->ctx != ctx) { | ||
| 976 | mutex_unlock(&ctx->mutex); | ||
| 977 | put_ctx(ctx); | ||
| 978 | goto again; | ||
| 979 | } | ||
| 980 | |||
| 981 | return ctx; | ||
| 982 | } | ||
| 983 | |||
| 984 | static inline struct perf_event_context * | ||
| 985 | perf_event_ctx_lock(struct perf_event *event) | ||
| 986 | { | ||
| 987 | return perf_event_ctx_lock_nested(event, 0); | ||
| 988 | } | ||
| 989 | |||
| 990 | static void perf_event_ctx_unlock(struct perf_event *event, | ||
| 991 | struct perf_event_context *ctx) | ||
| 992 | { | ||
| 993 | mutex_unlock(&ctx->mutex); | ||
| 994 | put_ctx(ctx); | ||
| 995 | } | ||
| 996 | |||
| 997 | /* | ||
| 910 | * This must be done under the ctx->lock, such as to serialize against | 998 | * This must be done under the ctx->lock, such as to serialize against |
| 911 | * context_equiv(), therefore we cannot call put_ctx() since that might end up | 999 | * context_equiv(), therefore we cannot call put_ctx() since that might end up |
| 912 | * calling scheduler related locks and ctx->lock nests inside those. | 1000 | * calling scheduler related locks and ctx->lock nests inside those. |
| @@ -1155,8 +1243,6 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx) | |||
| 1155 | ctx->nr_branch_stack++; | 1243 | ctx->nr_branch_stack++; |
| 1156 | 1244 | ||
| 1157 | list_add_rcu(&event->event_entry, &ctx->event_list); | 1245 | list_add_rcu(&event->event_entry, &ctx->event_list); |
| 1158 | if (!ctx->nr_events) | ||
| 1159 | perf_pmu_rotate_start(ctx->pmu); | ||
| 1160 | ctx->nr_events++; | 1246 | ctx->nr_events++; |
| 1161 | if (event->attr.inherit_stat) | 1247 | if (event->attr.inherit_stat) |
| 1162 | ctx->nr_stat++; | 1248 | ctx->nr_stat++; |
| @@ -1275,6 +1361,8 @@ static void perf_group_attach(struct perf_event *event) | |||
| 1275 | if (group_leader == event) | 1361 | if (group_leader == event) |
| 1276 | return; | 1362 | return; |
| 1277 | 1363 | ||
| 1364 | WARN_ON_ONCE(group_leader->ctx != event->ctx); | ||
| 1365 | |||
| 1278 | if (group_leader->group_flags & PERF_GROUP_SOFTWARE && | 1366 | if (group_leader->group_flags & PERF_GROUP_SOFTWARE && |
| 1279 | !is_software_event(event)) | 1367 | !is_software_event(event)) |
| 1280 | group_leader->group_flags &= ~PERF_GROUP_SOFTWARE; | 1368 | group_leader->group_flags &= ~PERF_GROUP_SOFTWARE; |
| @@ -1296,6 +1384,10 @@ static void | |||
| 1296 | list_del_event(struct perf_event *event, struct perf_event_context *ctx) | 1384 | list_del_event(struct perf_event *event, struct perf_event_context *ctx) |
| 1297 | { | 1385 | { |
| 1298 | struct perf_cpu_context *cpuctx; | 1386 | struct perf_cpu_context *cpuctx; |
| 1387 | |||
| 1388 | WARN_ON_ONCE(event->ctx != ctx); | ||
| 1389 | lockdep_assert_held(&ctx->lock); | ||
| 1390 | |||
| 1299 | /* | 1391 | /* |
| 1300 | * We can have double detach due to exit/hot-unplug + close. | 1392 | * We can have double detach due to exit/hot-unplug + close. |
| 1301 | */ | 1393 | */ |
| @@ -1380,6 +1472,8 @@ static void perf_group_detach(struct perf_event *event) | |||
| 1380 | 1472 | ||
| 1381 | /* Inherit group flags from the previous leader */ | 1473 | /* Inherit group flags from the previous leader */ |
| 1382 | sibling->group_flags = event->group_flags; | 1474 | sibling->group_flags = event->group_flags; |
| 1475 | |||
| 1476 | WARN_ON_ONCE(sibling->ctx != event->ctx); | ||
| 1383 | } | 1477 | } |
| 1384 | 1478 | ||
| 1385 | out: | 1479 | out: |
| @@ -1442,6 +1536,10 @@ event_sched_out(struct perf_event *event, | |||
| 1442 | { | 1536 | { |
| 1443 | u64 tstamp = perf_event_time(event); | 1537 | u64 tstamp = perf_event_time(event); |
| 1444 | u64 delta; | 1538 | u64 delta; |
| 1539 | |||
| 1540 | WARN_ON_ONCE(event->ctx != ctx); | ||
| 1541 | lockdep_assert_held(&ctx->lock); | ||
| 1542 | |||
| 1445 | /* | 1543 | /* |
| 1446 | * An event which could not be activated because of | 1544 | * An event which could not be activated because of |
| 1447 | * filter mismatch still needs to have its timings | 1545 | * filter mismatch still needs to have its timings |
| @@ -1471,7 +1569,8 @@ event_sched_out(struct perf_event *event, | |||
| 1471 | 1569 | ||
| 1472 | if (!is_software_event(event)) | 1570 | if (!is_software_event(event)) |
| 1473 | cpuctx->active_oncpu--; | 1571 | cpuctx->active_oncpu--; |
| 1474 | ctx->nr_active--; | 1572 | if (!--ctx->nr_active) |
| 1573 | perf_event_ctx_deactivate(ctx); | ||
| 1475 | if (event->attr.freq && event->attr.sample_freq) | 1574 | if (event->attr.freq && event->attr.sample_freq) |
| 1476 | ctx->nr_freq--; | 1575 | ctx->nr_freq--; |
| 1477 | if (event->attr.exclusive || !cpuctx->active_oncpu) | 1576 | if (event->attr.exclusive || !cpuctx->active_oncpu) |
| @@ -1654,7 +1753,7 @@ int __perf_event_disable(void *info) | |||
| 1654 | * is the current context on this CPU and preemption is disabled, | 1753 | * is the current context on this CPU and preemption is disabled, |
| 1655 | * hence we can't get into perf_event_task_sched_out for this context. | 1754 | * hence we can't get into perf_event_task_sched_out for this context. |
| 1656 | */ | 1755 | */ |
| 1657 | void perf_event_disable(struct perf_event *event) | 1756 | static void _perf_event_disable(struct perf_event *event) |
| 1658 | { | 1757 | { |
| 1659 | struct perf_event_context *ctx = event->ctx; | 1758 | struct perf_event_context *ctx = event->ctx; |
| 1660 | struct task_struct *task = ctx->task; | 1759 | struct task_struct *task = ctx->task; |
| @@ -1695,6 +1794,19 @@ retry: | |||
| 1695 | } | 1794 | } |
| 1696 | raw_spin_unlock_irq(&ctx->lock); | 1795 | raw_spin_unlock_irq(&ctx->lock); |
| 1697 | } | 1796 | } |
| 1797 | |||
| 1798 | /* | ||
| 1799 | * Strictly speaking kernel users cannot create groups and therefore this | ||
| 1800 | * interface does not need the perf_event_ctx_lock() magic. | ||
| 1801 | */ | ||
| 1802 | void perf_event_disable(struct perf_event *event) | ||
| 1803 | { | ||
| 1804 | struct perf_event_context *ctx; | ||
| 1805 | |||
| 1806 | ctx = perf_event_ctx_lock(event); | ||
| 1807 | _perf_event_disable(event); | ||
| 1808 | perf_event_ctx_unlock(event, ctx); | ||
| 1809 | } | ||
| 1698 | EXPORT_SYMBOL_GPL(perf_event_disable); | 1810 | EXPORT_SYMBOL_GPL(perf_event_disable); |
| 1699 | 1811 | ||
| 1700 | static void perf_set_shadow_time(struct perf_event *event, | 1812 | static void perf_set_shadow_time(struct perf_event *event, |
| @@ -1782,7 +1894,8 @@ event_sched_in(struct perf_event *event, | |||
| 1782 | 1894 | ||
| 1783 | if (!is_software_event(event)) | 1895 | if (!is_software_event(event)) |
| 1784 | cpuctx->active_oncpu++; | 1896 | cpuctx->active_oncpu++; |
| 1785 | ctx->nr_active++; | 1897 | if (!ctx->nr_active++) |
| 1898 | perf_event_ctx_activate(ctx); | ||
| 1786 | if (event->attr.freq && event->attr.sample_freq) | 1899 | if (event->attr.freq && event->attr.sample_freq) |
| 1787 | ctx->nr_freq++; | 1900 | ctx->nr_freq++; |
| 1788 | 1901 | ||
| @@ -2158,7 +2271,7 @@ unlock: | |||
| 2158 | * perf_event_for_each_child or perf_event_for_each as described | 2271 | * perf_event_for_each_child or perf_event_for_each as described |
| 2159 | * for perf_event_disable. | 2272 | * for perf_event_disable. |
| 2160 | */ | 2273 | */ |
| 2161 | void perf_event_enable(struct perf_event *event) | 2274 | static void _perf_event_enable(struct perf_event *event) |
| 2162 | { | 2275 | { |
| 2163 | struct perf_event_context *ctx = event->ctx; | 2276 | struct perf_event_context *ctx = event->ctx; |
| 2164 | struct task_struct *task = ctx->task; | 2277 | struct task_struct *task = ctx->task; |
| @@ -2214,9 +2327,21 @@ retry: | |||
| 2214 | out: | 2327 | out: |
| 2215 | raw_spin_unlock_irq(&ctx->lock); | 2328 | raw_spin_unlock_irq(&ctx->lock); |
| 2216 | } | 2329 | } |
| 2330 | |||
| 2331 | /* | ||
| 2332 | * See perf_event_disable(); | ||
| 2333 | */ | ||
| 2334 | void perf_event_enable(struct perf_event *event) | ||
| 2335 | { | ||
| 2336 | struct perf_event_context *ctx; | ||
| 2337 | |||
| 2338 | ctx = perf_event_ctx_lock(event); | ||
| 2339 | _perf_event_enable(event); | ||
| 2340 | perf_event_ctx_unlock(event, ctx); | ||
| 2341 | } | ||
| 2217 | EXPORT_SYMBOL_GPL(perf_event_enable); | 2342 | EXPORT_SYMBOL_GPL(perf_event_enable); |
| 2218 | 2343 | ||
| 2219 | int perf_event_refresh(struct perf_event *event, int refresh) | 2344 | static int _perf_event_refresh(struct perf_event *event, int refresh) |
| 2220 | { | 2345 | { |
| 2221 | /* | 2346 | /* |
| 2222 | * not supported on inherited events | 2347 | * not supported on inherited events |
| @@ -2225,10 +2350,25 @@ int perf_event_refresh(struct perf_event *event, int refresh) | |||
| 2225 | return -EINVAL; | 2350 | return -EINVAL; |
| 2226 | 2351 | ||
| 2227 | atomic_add(refresh, &event->event_limit); | 2352 | atomic_add(refresh, &event->event_limit); |
| 2228 | perf_event_enable(event); | 2353 | _perf_event_enable(event); |
| 2229 | 2354 | ||
| 2230 | return 0; | 2355 | return 0; |
| 2231 | } | 2356 | } |
| 2357 | |||
| 2358 | /* | ||
| 2359 | * See perf_event_disable() | ||
| 2360 | */ | ||
| 2361 | int perf_event_refresh(struct perf_event *event, int refresh) | ||
| 2362 | { | ||
| 2363 | struct perf_event_context *ctx; | ||
| 2364 | int ret; | ||
| 2365 | |||
| 2366 | ctx = perf_event_ctx_lock(event); | ||
| 2367 | ret = _perf_event_refresh(event, refresh); | ||
| 2368 | perf_event_ctx_unlock(event, ctx); | ||
| 2369 | |||
| 2370 | return ret; | ||
| 2371 | } | ||
| 2232 | EXPORT_SYMBOL_GPL(perf_event_refresh); | 2372 | EXPORT_SYMBOL_GPL(perf_event_refresh); |
| 2233 | 2373 | ||
| 2234 | static void ctx_sched_out(struct perf_event_context *ctx, | 2374 | static void ctx_sched_out(struct perf_event_context *ctx, |
| @@ -2612,12 +2752,6 @@ static void perf_event_context_sched_in(struct perf_event_context *ctx, | |||
| 2612 | 2752 | ||
| 2613 | perf_pmu_enable(ctx->pmu); | 2753 | perf_pmu_enable(ctx->pmu); |
| 2614 | perf_ctx_unlock(cpuctx, ctx); | 2754 | perf_ctx_unlock(cpuctx, ctx); |
| 2615 | |||
| 2616 | /* | ||
| 2617 | * Since these rotations are per-cpu, we need to ensure the | ||
| 2618 | * cpu-context we got scheduled on is actually rotating. | ||
| 2619 | */ | ||
| 2620 | perf_pmu_rotate_start(ctx->pmu); | ||
| 2621 | } | 2755 | } |
| 2622 | 2756 | ||
| 2623 | /* | 2757 | /* |
| @@ -2905,25 +3039,18 @@ static void rotate_ctx(struct perf_event_context *ctx) | |||
| 2905 | list_rotate_left(&ctx->flexible_groups); | 3039 | list_rotate_left(&ctx->flexible_groups); |
| 2906 | } | 3040 | } |
| 2907 | 3041 | ||
| 2908 | /* | ||
| 2909 | * perf_pmu_rotate_start() and perf_rotate_context() are fully serialized | ||
| 2910 | * because they're strictly cpu affine and rotate_start is called with IRQs | ||
| 2911 | * disabled, while rotate_context is called from IRQ context. | ||
| 2912 | */ | ||
| 2913 | static int perf_rotate_context(struct perf_cpu_context *cpuctx) | 3042 | static int perf_rotate_context(struct perf_cpu_context *cpuctx) |
| 2914 | { | 3043 | { |
| 2915 | struct perf_event_context *ctx = NULL; | 3044 | struct perf_event_context *ctx = NULL; |
| 2916 | int rotate = 0, remove = 1; | 3045 | int rotate = 0; |
| 2917 | 3046 | ||
| 2918 | if (cpuctx->ctx.nr_events) { | 3047 | if (cpuctx->ctx.nr_events) { |
| 2919 | remove = 0; | ||
| 2920 | if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active) | 3048 | if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active) |
| 2921 | rotate = 1; | 3049 | rotate = 1; |
| 2922 | } | 3050 | } |
| 2923 | 3051 | ||
| 2924 | ctx = cpuctx->task_ctx; | 3052 | ctx = cpuctx->task_ctx; |
| 2925 | if (ctx && ctx->nr_events) { | 3053 | if (ctx && ctx->nr_events) { |
| 2926 | remove = 0; | ||
| 2927 | if (ctx->nr_events != ctx->nr_active) | 3054 | if (ctx->nr_events != ctx->nr_active) |
| 2928 | rotate = 1; | 3055 | rotate = 1; |
| 2929 | } | 3056 | } |
| @@ -2947,8 +3074,6 @@ static int perf_rotate_context(struct perf_cpu_context *cpuctx) | |||
| 2947 | perf_pmu_enable(cpuctx->ctx.pmu); | 3074 | perf_pmu_enable(cpuctx->ctx.pmu); |
| 2948 | perf_ctx_unlock(cpuctx, cpuctx->task_ctx); | 3075 | perf_ctx_unlock(cpuctx, cpuctx->task_ctx); |
| 2949 | done: | 3076 | done: |
| 2950 | if (remove) | ||
| 2951 | list_del_init(&cpuctx->rotation_list); | ||
| 2952 | 3077 | ||
| 2953 | return rotate; | 3078 | return rotate; |
| 2954 | } | 3079 | } |
| @@ -2966,9 +3091,8 @@ bool perf_event_can_stop_tick(void) | |||
| 2966 | 3091 | ||
| 2967 | void perf_event_task_tick(void) | 3092 | void perf_event_task_tick(void) |
| 2968 | { | 3093 | { |
| 2969 | struct list_head *head = this_cpu_ptr(&rotation_list); | 3094 | struct list_head *head = this_cpu_ptr(&active_ctx_list); |
| 2970 | struct perf_cpu_context *cpuctx, *tmp; | 3095 | struct perf_event_context *ctx, *tmp; |
| 2971 | struct perf_event_context *ctx; | ||
| 2972 | int throttled; | 3096 | int throttled; |
| 2973 | 3097 | ||
| 2974 | WARN_ON(!irqs_disabled()); | 3098 | WARN_ON(!irqs_disabled()); |
| @@ -2976,14 +3100,8 @@ void perf_event_task_tick(void) | |||
| 2976 | __this_cpu_inc(perf_throttled_seq); | 3100 | __this_cpu_inc(perf_throttled_seq); |
| 2977 | throttled = __this_cpu_xchg(perf_throttled_count, 0); | 3101 | throttled = __this_cpu_xchg(perf_throttled_count, 0); |
| 2978 | 3102 | ||
| 2979 | list_for_each_entry_safe(cpuctx, tmp, head, rotation_list) { | 3103 | list_for_each_entry_safe(ctx, tmp, head, active_ctx_list) |
| 2980 | ctx = &cpuctx->ctx; | ||
| 2981 | perf_adjust_freq_unthr_context(ctx, throttled); | 3104 | perf_adjust_freq_unthr_context(ctx, throttled); |
| 2982 | |||
| 2983 | ctx = cpuctx->task_ctx; | ||
| 2984 | if (ctx) | ||
| 2985 | perf_adjust_freq_unthr_context(ctx, throttled); | ||
| 2986 | } | ||
| 2987 | } | 3105 | } |
| 2988 | 3106 | ||
| 2989 | static int event_enable_on_exec(struct perf_event *event, | 3107 | static int event_enable_on_exec(struct perf_event *event, |
| @@ -3142,6 +3260,7 @@ static void __perf_event_init_context(struct perf_event_context *ctx) | |||
| 3142 | { | 3260 | { |
| 3143 | raw_spin_lock_init(&ctx->lock); | 3261 | raw_spin_lock_init(&ctx->lock); |
| 3144 | mutex_init(&ctx->mutex); | 3262 | mutex_init(&ctx->mutex); |
| 3263 | INIT_LIST_HEAD(&ctx->active_ctx_list); | ||
| 3145 | INIT_LIST_HEAD(&ctx->pinned_groups); | 3264 | INIT_LIST_HEAD(&ctx->pinned_groups); |
| 3146 | INIT_LIST_HEAD(&ctx->flexible_groups); | 3265 | INIT_LIST_HEAD(&ctx->flexible_groups); |
| 3147 | INIT_LIST_HEAD(&ctx->event_list); | 3266 | INIT_LIST_HEAD(&ctx->event_list); |
| @@ -3421,7 +3540,16 @@ static void perf_remove_from_owner(struct perf_event *event) | |||
| 3421 | rcu_read_unlock(); | 3540 | rcu_read_unlock(); |
| 3422 | 3541 | ||
| 3423 | if (owner) { | 3542 | if (owner) { |
| 3424 | mutex_lock(&owner->perf_event_mutex); | 3543 | /* |
| 3544 | * If we're here through perf_event_exit_task() we're already | ||
| 3545 | * holding ctx->mutex which would be an inversion wrt. the | ||
| 3546 | * normal lock order. | ||
| 3547 | * | ||
| 3548 | * However we can safely take this lock because its the child | ||
| 3549 | * ctx->mutex. | ||
| 3550 | */ | ||
| 3551 | mutex_lock_nested(&owner->perf_event_mutex, SINGLE_DEPTH_NESTING); | ||
| 3552 | |||
| 3425 | /* | 3553 | /* |
| 3426 | * We have to re-check the event->owner field, if it is cleared | 3554 | * We have to re-check the event->owner field, if it is cleared |
| 3427 | * we raced with perf_event_exit_task(), acquiring the mutex | 3555 | * we raced with perf_event_exit_task(), acquiring the mutex |
| @@ -3440,7 +3568,7 @@ static void perf_remove_from_owner(struct perf_event *event) | |||
| 3440 | */ | 3568 | */ |
| 3441 | static void put_event(struct perf_event *event) | 3569 | static void put_event(struct perf_event *event) |
| 3442 | { | 3570 | { |
| 3443 | struct perf_event_context *ctx = event->ctx; | 3571 | struct perf_event_context *ctx; |
| 3444 | 3572 | ||
| 3445 | if (!atomic_long_dec_and_test(&event->refcount)) | 3573 | if (!atomic_long_dec_and_test(&event->refcount)) |
| 3446 | return; | 3574 | return; |
| @@ -3448,7 +3576,6 @@ static void put_event(struct perf_event *event) | |||
| 3448 | if (!is_kernel_event(event)) | 3576 | if (!is_kernel_event(event)) |
| 3449 | perf_remove_from_owner(event); | 3577 | perf_remove_from_owner(event); |
| 3450 | 3578 | ||
| 3451 | WARN_ON_ONCE(ctx->parent_ctx); | ||
| 3452 | /* | 3579 | /* |
| 3453 | * There are two ways this annotation is useful: | 3580 | * There are two ways this annotation is useful: |
| 3454 | * | 3581 | * |
| @@ -3461,7 +3588,8 @@ static void put_event(struct perf_event *event) | |||
| 3461 | * the last filedesc died, so there is no possibility | 3588 | * the last filedesc died, so there is no possibility |
| 3462 | * to trigger the AB-BA case. | 3589 | * to trigger the AB-BA case. |
| 3463 | */ | 3590 | */ |
| 3464 | mutex_lock_nested(&ctx->mutex, SINGLE_DEPTH_NESTING); | 3591 | ctx = perf_event_ctx_lock_nested(event, SINGLE_DEPTH_NESTING); |
| 3592 | WARN_ON_ONCE(ctx->parent_ctx); | ||
| 3465 | perf_remove_from_context(event, true); | 3593 | perf_remove_from_context(event, true); |
| 3466 | mutex_unlock(&ctx->mutex); | 3594 | mutex_unlock(&ctx->mutex); |
| 3467 | 3595 | ||
| @@ -3547,12 +3675,13 @@ static int perf_event_read_group(struct perf_event *event, | |||
| 3547 | u64 read_format, char __user *buf) | 3675 | u64 read_format, char __user *buf) |
| 3548 | { | 3676 | { |
| 3549 | struct perf_event *leader = event->group_leader, *sub; | 3677 | struct perf_event *leader = event->group_leader, *sub; |
| 3550 | int n = 0, size = 0, ret = -EFAULT; | ||
| 3551 | struct perf_event_context *ctx = leader->ctx; | 3678 | struct perf_event_context *ctx = leader->ctx; |
| 3552 | u64 values[5]; | 3679 | int n = 0, size = 0, ret; |
| 3553 | u64 count, enabled, running; | 3680 | u64 count, enabled, running; |
| 3681 | u64 values[5]; | ||
| 3682 | |||
| 3683 | lockdep_assert_held(&ctx->mutex); | ||
| 3554 | 3684 | ||
| 3555 | mutex_lock(&ctx->mutex); | ||
| 3556 | count = perf_event_read_value(leader, &enabled, &running); | 3685 | count = perf_event_read_value(leader, &enabled, &running); |
| 3557 | 3686 | ||
| 3558 | values[n++] = 1 + leader->nr_siblings; | 3687 | values[n++] = 1 + leader->nr_siblings; |
| @@ -3567,7 +3696,7 @@ static int perf_event_read_group(struct perf_event *event, | |||
| 3567 | size = n * sizeof(u64); | 3696 | size = n * sizeof(u64); |
| 3568 | 3697 | ||
| 3569 | if (copy_to_user(buf, values, size)) | 3698 | if (copy_to_user(buf, values, size)) |
| 3570 | goto unlock; | 3699 | return -EFAULT; |
| 3571 | 3700 | ||
| 3572 | ret = size; | 3701 | ret = size; |
| 3573 | 3702 | ||
| @@ -3581,14 +3710,11 @@ static int perf_event_read_group(struct perf_event *event, | |||
| 3581 | size = n * sizeof(u64); | 3710 | size = n * sizeof(u64); |
| 3582 | 3711 | ||
| 3583 | if (copy_to_user(buf + ret, values, size)) { | 3712 | if (copy_to_user(buf + ret, values, size)) { |
| 3584 | ret = -EFAULT; | 3713 | return -EFAULT; |
| 3585 | goto unlock; | ||
| 3586 | } | 3714 | } |
| 3587 | 3715 | ||
| 3588 | ret += size; | 3716 | ret += size; |
| 3589 | } | 3717 | } |
| 3590 | unlock: | ||
| 3591 | mutex_unlock(&ctx->mutex); | ||
| 3592 | 3718 | ||
| 3593 | return ret; | 3719 | return ret; |
| 3594 | } | 3720 | } |
| @@ -3660,8 +3786,14 @@ static ssize_t | |||
| 3660 | perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 3786 | perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) |
| 3661 | { | 3787 | { |
| 3662 | struct perf_event *event = file->private_data; | 3788 | struct perf_event *event = file->private_data; |
| 3789 | struct perf_event_context *ctx; | ||
| 3790 | int ret; | ||
| 3663 | 3791 | ||
| 3664 | return perf_read_hw(event, buf, count); | 3792 | ctx = perf_event_ctx_lock(event); |
| 3793 | ret = perf_read_hw(event, buf, count); | ||
| 3794 | perf_event_ctx_unlock(event, ctx); | ||
| 3795 | |||
| 3796 | return ret; | ||
| 3665 | } | 3797 | } |
| 3666 | 3798 | ||
| 3667 | static unsigned int perf_poll(struct file *file, poll_table *wait) | 3799 | static unsigned int perf_poll(struct file *file, poll_table *wait) |
| @@ -3687,7 +3819,7 @@ static unsigned int perf_poll(struct file *file, poll_table *wait) | |||
| 3687 | return events; | 3819 | return events; |
| 3688 | } | 3820 | } |
| 3689 | 3821 | ||
| 3690 | static void perf_event_reset(struct perf_event *event) | 3822 | static void _perf_event_reset(struct perf_event *event) |
| 3691 | { | 3823 | { |
| 3692 | (void)perf_event_read(event); | 3824 | (void)perf_event_read(event); |
| 3693 | local64_set(&event->count, 0); | 3825 | local64_set(&event->count, 0); |
| @@ -3706,6 +3838,7 @@ static void perf_event_for_each_child(struct perf_event *event, | |||
| 3706 | struct perf_event *child; | 3838 | struct perf_event *child; |
| 3707 | 3839 | ||
| 3708 | WARN_ON_ONCE(event->ctx->parent_ctx); | 3840 | WARN_ON_ONCE(event->ctx->parent_ctx); |
| 3841 | |||
| 3709 | mutex_lock(&event->child_mutex); | 3842 | mutex_lock(&event->child_mutex); |
| 3710 | func(event); | 3843 | func(event); |
| 3711 | list_for_each_entry(child, &event->child_list, child_list) | 3844 | list_for_each_entry(child, &event->child_list, child_list) |
| @@ -3719,14 +3852,13 @@ static void perf_event_for_each(struct perf_event *event, | |||
| 3719 | struct perf_event_context *ctx = event->ctx; | 3852 | struct perf_event_context *ctx = event->ctx; |
| 3720 | struct perf_event *sibling; | 3853 | struct perf_event *sibling; |
| 3721 | 3854 | ||
| 3722 | WARN_ON_ONCE(ctx->parent_ctx); | 3855 | lockdep_assert_held(&ctx->mutex); |
| 3723 | mutex_lock(&ctx->mutex); | 3856 | |
| 3724 | event = event->group_leader; | 3857 | event = event->group_leader; |
| 3725 | 3858 | ||
| 3726 | perf_event_for_each_child(event, func); | 3859 | perf_event_for_each_child(event, func); |
| 3727 | list_for_each_entry(sibling, &event->sibling_list, group_entry) | 3860 | list_for_each_entry(sibling, &event->sibling_list, group_entry) |
| 3728 | perf_event_for_each_child(sibling, func); | 3861 | perf_event_for_each_child(sibling, func); |
| 3729 | mutex_unlock(&ctx->mutex); | ||
| 3730 | } | 3862 | } |
| 3731 | 3863 | ||
| 3732 | static int perf_event_period(struct perf_event *event, u64 __user *arg) | 3864 | static int perf_event_period(struct perf_event *event, u64 __user *arg) |
| @@ -3796,25 +3928,24 @@ static int perf_event_set_output(struct perf_event *event, | |||
| 3796 | struct perf_event *output_event); | 3928 | struct perf_event *output_event); |
| 3797 | static int perf_event_set_filter(struct perf_event *event, void __user *arg); | 3929 | static int perf_event_set_filter(struct perf_event *event, void __user *arg); |
| 3798 | 3930 | ||
| 3799 | static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 3931 | static long _perf_ioctl(struct perf_event *event, unsigned int cmd, unsigned long arg) |
| 3800 | { | 3932 | { |
| 3801 | struct perf_event *event = file->private_data; | ||
| 3802 | void (*func)(struct perf_event *); | 3933 | void (*func)(struct perf_event *); |
| 3803 | u32 flags = arg; | 3934 | u32 flags = arg; |
| 3804 | 3935 | ||
| 3805 | switch (cmd) { | 3936 | switch (cmd) { |
| 3806 | case PERF_EVENT_IOC_ENABLE: | 3937 | case PERF_EVENT_IOC_ENABLE: |
| 3807 | func = perf_event_enable; | 3938 | func = _perf_event_enable; |
| 3808 | break; | 3939 | break; |
| 3809 | case PERF_EVENT_IOC_DISABLE: | 3940 | case PERF_EVENT_IOC_DISABLE: |
| 3810 | func = perf_event_disable; | 3941 | func = _perf_event_disable; |
| 3811 | break; | 3942 | break; |
| 3812 | case PERF_EVENT_IOC_RESET: | 3943 | case PERF_EVENT_IOC_RESET: |
| 3813 | func = perf_event_reset; | 3944 | func = _perf_event_reset; |
| 3814 | break; | 3945 | break; |
| 3815 | 3946 | ||
| 3816 | case PERF_EVENT_IOC_REFRESH: | 3947 | case PERF_EVENT_IOC_REFRESH: |
| 3817 | return perf_event_refresh(event, arg); | 3948 | return _perf_event_refresh(event, arg); |
| 3818 | 3949 | ||
| 3819 | case PERF_EVENT_IOC_PERIOD: | 3950 | case PERF_EVENT_IOC_PERIOD: |
| 3820 | return perf_event_period(event, (u64 __user *)arg); | 3951 | return perf_event_period(event, (u64 __user *)arg); |
| @@ -3861,6 +3992,19 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
| 3861 | return 0; | 3992 | return 0; |
| 3862 | } | 3993 | } |
| 3863 | 3994 | ||
| 3995 | static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
| 3996 | { | ||
| 3997 | struct perf_event *event = file->private_data; | ||
| 3998 | struct perf_event_context *ctx; | ||
| 3999 | long ret; | ||
| 4000 | |||
| 4001 | ctx = perf_event_ctx_lock(event); | ||
| 4002 | ret = _perf_ioctl(event, cmd, arg); | ||
| 4003 | perf_event_ctx_unlock(event, ctx); | ||
| 4004 | |||
| 4005 | return ret; | ||
| 4006 | } | ||
| 4007 | |||
| 3864 | #ifdef CONFIG_COMPAT | 4008 | #ifdef CONFIG_COMPAT |
| 3865 | static long perf_compat_ioctl(struct file *file, unsigned int cmd, | 4009 | static long perf_compat_ioctl(struct file *file, unsigned int cmd, |
| 3866 | unsigned long arg) | 4010 | unsigned long arg) |
| @@ -3883,11 +4027,15 @@ static long perf_compat_ioctl(struct file *file, unsigned int cmd, | |||
| 3883 | 4027 | ||
| 3884 | int perf_event_task_enable(void) | 4028 | int perf_event_task_enable(void) |
| 3885 | { | 4029 | { |
| 4030 | struct perf_event_context *ctx; | ||
| 3886 | struct perf_event *event; | 4031 | struct perf_event *event; |
| 3887 | 4032 | ||
| 3888 | mutex_lock(¤t->perf_event_mutex); | 4033 | mutex_lock(¤t->perf_event_mutex); |
| 3889 | list_for_each_entry(event, ¤t->perf_event_list, owner_entry) | 4034 | list_for_each_entry(event, ¤t->perf_event_list, owner_entry) { |
| 3890 | perf_event_for_each_child(event, perf_event_enable); | 4035 | ctx = perf_event_ctx_lock(event); |
| 4036 | perf_event_for_each_child(event, _perf_event_enable); | ||
| 4037 | perf_event_ctx_unlock(event, ctx); | ||
| 4038 | } | ||
| 3891 | mutex_unlock(¤t->perf_event_mutex); | 4039 | mutex_unlock(¤t->perf_event_mutex); |
| 3892 | 4040 | ||
| 3893 | return 0; | 4041 | return 0; |
| @@ -3895,11 +4043,15 @@ int perf_event_task_enable(void) | |||
| 3895 | 4043 | ||
| 3896 | int perf_event_task_disable(void) | 4044 | int perf_event_task_disable(void) |
| 3897 | { | 4045 | { |
| 4046 | struct perf_event_context *ctx; | ||
| 3898 | struct perf_event *event; | 4047 | struct perf_event *event; |
| 3899 | 4048 | ||
| 3900 | mutex_lock(¤t->perf_event_mutex); | 4049 | mutex_lock(¤t->perf_event_mutex); |
| 3901 | list_for_each_entry(event, ¤t->perf_event_list, owner_entry) | 4050 | list_for_each_entry(event, ¤t->perf_event_list, owner_entry) { |
| 3902 | perf_event_for_each_child(event, perf_event_disable); | 4051 | ctx = perf_event_ctx_lock(event); |
| 4052 | perf_event_for_each_child(event, _perf_event_disable); | ||
| 4053 | perf_event_ctx_unlock(event, ctx); | ||
| 4054 | } | ||
| 3903 | mutex_unlock(¤t->perf_event_mutex); | 4055 | mutex_unlock(¤t->perf_event_mutex); |
| 3904 | 4056 | ||
| 3905 | return 0; | 4057 | return 0; |
| @@ -5889,6 +6041,8 @@ end: | |||
| 5889 | rcu_read_unlock(); | 6041 | rcu_read_unlock(); |
| 5890 | } | 6042 | } |
| 5891 | 6043 | ||
| 6044 | DEFINE_PER_CPU(struct pt_regs, __perf_regs[4]); | ||
| 6045 | |||
| 5892 | int perf_swevent_get_recursion_context(void) | 6046 | int perf_swevent_get_recursion_context(void) |
| 5893 | { | 6047 | { |
| 5894 | struct swevent_htable *swhash = this_cpu_ptr(&swevent_htable); | 6048 | struct swevent_htable *swhash = this_cpu_ptr(&swevent_htable); |
| @@ -5904,21 +6058,30 @@ inline void perf_swevent_put_recursion_context(int rctx) | |||
| 5904 | put_recursion_context(swhash->recursion, rctx); | 6058 | put_recursion_context(swhash->recursion, rctx); |
| 5905 | } | 6059 | } |
| 5906 | 6060 | ||
| 5907 | void __perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) | 6061 | void ___perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) |
| 5908 | { | 6062 | { |
| 5909 | struct perf_sample_data data; | 6063 | struct perf_sample_data data; |
| 5910 | int rctx; | ||
| 5911 | 6064 | ||
| 5912 | preempt_disable_notrace(); | 6065 | if (WARN_ON_ONCE(!regs)) |
| 5913 | rctx = perf_swevent_get_recursion_context(); | ||
| 5914 | if (rctx < 0) | ||
| 5915 | return; | 6066 | return; |
| 5916 | 6067 | ||
| 5917 | perf_sample_data_init(&data, addr, 0); | 6068 | perf_sample_data_init(&data, addr, 0); |
| 5918 | |||
| 5919 | do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, &data, regs); | 6069 | do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, &data, regs); |
| 6070 | } | ||
| 6071 | |||
| 6072 | void __perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) | ||
| 6073 | { | ||
| 6074 | int rctx; | ||
| 6075 | |||
| 6076 | preempt_disable_notrace(); | ||
| 6077 | rctx = perf_swevent_get_recursion_context(); | ||
| 6078 | if (unlikely(rctx < 0)) | ||
| 6079 | goto fail; | ||
| 6080 | |||
| 6081 | ___perf_sw_event(event_id, nr, regs, addr); | ||
| 5920 | 6082 | ||
| 5921 | perf_swevent_put_recursion_context(rctx); | 6083 | perf_swevent_put_recursion_context(rctx); |
| 6084 | fail: | ||
| 5922 | preempt_enable_notrace(); | 6085 | preempt_enable_notrace(); |
| 5923 | } | 6086 | } |
| 5924 | 6087 | ||
| @@ -6780,7 +6943,6 @@ skip_type: | |||
| 6780 | 6943 | ||
| 6781 | __perf_cpu_hrtimer_init(cpuctx, cpu); | 6944 | __perf_cpu_hrtimer_init(cpuctx, cpu); |
| 6782 | 6945 | ||
| 6783 | INIT_LIST_HEAD(&cpuctx->rotation_list); | ||
| 6784 | cpuctx->unique_pmu = pmu; | 6946 | cpuctx->unique_pmu = pmu; |
| 6785 | } | 6947 | } |
| 6786 | 6948 | ||
| @@ -6853,6 +7015,20 @@ void perf_pmu_unregister(struct pmu *pmu) | |||
| 6853 | } | 7015 | } |
| 6854 | EXPORT_SYMBOL_GPL(perf_pmu_unregister); | 7016 | EXPORT_SYMBOL_GPL(perf_pmu_unregister); |
| 6855 | 7017 | ||
| 7018 | static int perf_try_init_event(struct pmu *pmu, struct perf_event *event) | ||
| 7019 | { | ||
| 7020 | int ret; | ||
| 7021 | |||
| 7022 | if (!try_module_get(pmu->module)) | ||
| 7023 | return -ENODEV; | ||
| 7024 | event->pmu = pmu; | ||
| 7025 | ret = pmu->event_init(event); | ||
| 7026 | if (ret) | ||
| 7027 | module_put(pmu->module); | ||
| 7028 | |||
| 7029 | return ret; | ||
| 7030 | } | ||
| 7031 | |||
| 6856 | struct pmu *perf_init_event(struct perf_event *event) | 7032 | struct pmu *perf_init_event(struct perf_event *event) |
| 6857 | { | 7033 | { |
| 6858 | struct pmu *pmu = NULL; | 7034 | struct pmu *pmu = NULL; |
| @@ -6865,24 +7041,14 @@ struct pmu *perf_init_event(struct perf_event *event) | |||
| 6865 | pmu = idr_find(&pmu_idr, event->attr.type); | 7041 | pmu = idr_find(&pmu_idr, event->attr.type); |
| 6866 | rcu_read_unlock(); | 7042 | rcu_read_unlock(); |
| 6867 | if (pmu) { | 7043 | if (pmu) { |
| 6868 | if (!try_module_get(pmu->module)) { | 7044 | ret = perf_try_init_event(pmu, event); |
| 6869 | pmu = ERR_PTR(-ENODEV); | ||
| 6870 | goto unlock; | ||
| 6871 | } | ||
| 6872 | event->pmu = pmu; | ||
| 6873 | ret = pmu->event_init(event); | ||
| 6874 | if (ret) | 7045 | if (ret) |
| 6875 | pmu = ERR_PTR(ret); | 7046 | pmu = ERR_PTR(ret); |
| 6876 | goto unlock; | 7047 | goto unlock; |
| 6877 | } | 7048 | } |
| 6878 | 7049 | ||
| 6879 | list_for_each_entry_rcu(pmu, &pmus, entry) { | 7050 | list_for_each_entry_rcu(pmu, &pmus, entry) { |
| 6880 | if (!try_module_get(pmu->module)) { | 7051 | ret = perf_try_init_event(pmu, event); |
| 6881 | pmu = ERR_PTR(-ENODEV); | ||
| 6882 | goto unlock; | ||
| 6883 | } | ||
| 6884 | event->pmu = pmu; | ||
| 6885 | ret = pmu->event_init(event); | ||
| 6886 | if (!ret) | 7052 | if (!ret) |
| 6887 | goto unlock; | 7053 | goto unlock; |
| 6888 | 7054 | ||
| @@ -7246,6 +7412,15 @@ out: | |||
| 7246 | return ret; | 7412 | return ret; |
| 7247 | } | 7413 | } |
| 7248 | 7414 | ||
| 7415 | static void mutex_lock_double(struct mutex *a, struct mutex *b) | ||
| 7416 | { | ||
| 7417 | if (b < a) | ||
| 7418 | swap(a, b); | ||
| 7419 | |||
| 7420 | mutex_lock(a); | ||
| 7421 | mutex_lock_nested(b, SINGLE_DEPTH_NESTING); | ||
| 7422 | } | ||
| 7423 | |||
| 7249 | /** | 7424 | /** |
| 7250 | * sys_perf_event_open - open a performance event, associate it to a task/cpu | 7425 | * sys_perf_event_open - open a performance event, associate it to a task/cpu |
| 7251 | * | 7426 | * |
| @@ -7261,7 +7436,7 @@ SYSCALL_DEFINE5(perf_event_open, | |||
| 7261 | struct perf_event *group_leader = NULL, *output_event = NULL; | 7436 | struct perf_event *group_leader = NULL, *output_event = NULL; |
| 7262 | struct perf_event *event, *sibling; | 7437 | struct perf_event *event, *sibling; |
| 7263 | struct perf_event_attr attr; | 7438 | struct perf_event_attr attr; |
| 7264 | struct perf_event_context *ctx; | 7439 | struct perf_event_context *ctx, *uninitialized_var(gctx); |
| 7265 | struct file *event_file = NULL; | 7440 | struct file *event_file = NULL; |
| 7266 | struct fd group = {NULL, 0}; | 7441 | struct fd group = {NULL, 0}; |
| 7267 | struct task_struct *task = NULL; | 7442 | struct task_struct *task = NULL; |
| @@ -7459,43 +7634,68 @@ SYSCALL_DEFINE5(perf_event_open, | |||
| 7459 | } | 7634 | } |
| 7460 | 7635 | ||
| 7461 | if (move_group) { | 7636 | if (move_group) { |
| 7462 | struct perf_event_context *gctx = group_leader->ctx; | 7637 | gctx = group_leader->ctx; |
| 7463 | |||
| 7464 | mutex_lock(&gctx->mutex); | ||
| 7465 | perf_remove_from_context(group_leader, false); | ||
| 7466 | 7638 | ||
| 7467 | /* | 7639 | /* |
| 7468 | * Removing from the context ends up with disabled | 7640 | * See perf_event_ctx_lock() for comments on the details |
| 7469 | * event. What we want here is event in the initial | 7641 | * of swizzling perf_event::ctx. |
| 7470 | * startup state, ready to be add into new context. | ||
| 7471 | */ | 7642 | */ |
| 7472 | perf_event__state_init(group_leader); | 7643 | mutex_lock_double(&gctx->mutex, &ctx->mutex); |
| 7644 | |||
| 7645 | perf_remove_from_context(group_leader, false); | ||
| 7646 | |||
| 7473 | list_for_each_entry(sibling, &group_leader->sibling_list, | 7647 | list_for_each_entry(sibling, &group_leader->sibling_list, |
| 7474 | group_entry) { | 7648 | group_entry) { |
| 7475 | perf_remove_from_context(sibling, false); | 7649 | perf_remove_from_context(sibling, false); |
| 7476 | perf_event__state_init(sibling); | ||
| 7477 | put_ctx(gctx); | 7650 | put_ctx(gctx); |
| 7478 | } | 7651 | } |
| 7479 | mutex_unlock(&gctx->mutex); | 7652 | } else { |
| 7480 | put_ctx(gctx); | 7653 | mutex_lock(&ctx->mutex); |
| 7481 | } | 7654 | } |
| 7482 | 7655 | ||
| 7483 | WARN_ON_ONCE(ctx->parent_ctx); | 7656 | WARN_ON_ONCE(ctx->parent_ctx); |
| 7484 | mutex_lock(&ctx->mutex); | ||
| 7485 | 7657 | ||
| 7486 | if (move_group) { | 7658 | if (move_group) { |
| 7659 | /* | ||
| 7660 | * Wait for everybody to stop referencing the events through | ||
| 7661 | * the old lists, before installing it on new lists. | ||
| 7662 | */ | ||
| 7487 | synchronize_rcu(); | 7663 | synchronize_rcu(); |
| 7488 | perf_install_in_context(ctx, group_leader, group_leader->cpu); | 7664 | |
| 7489 | get_ctx(ctx); | 7665 | /* |
| 7666 | * Install the group siblings before the group leader. | ||
| 7667 | * | ||
| 7668 | * Because a group leader will try and install the entire group | ||
| 7669 | * (through the sibling list, which is still in-tact), we can | ||
| 7670 | * end up with siblings installed in the wrong context. | ||
| 7671 | * | ||
| 7672 | * By installing siblings first we NO-OP because they're not | ||
| 7673 | * reachable through the group lists. | ||
| 7674 | */ | ||
| 7490 | list_for_each_entry(sibling, &group_leader->sibling_list, | 7675 | list_for_each_entry(sibling, &group_leader->sibling_list, |
| 7491 | group_entry) { | 7676 | group_entry) { |
| 7677 | perf_event__state_init(sibling); | ||
| 7492 | perf_install_in_context(ctx, sibling, sibling->cpu); | 7678 | perf_install_in_context(ctx, sibling, sibling->cpu); |
| 7493 | get_ctx(ctx); | 7679 | get_ctx(ctx); |
| 7494 | } | 7680 | } |
| 7681 | |||
| 7682 | /* | ||
| 7683 | * Removing from the context ends up with disabled | ||
| 7684 | * event. What we want here is event in the initial | ||
| 7685 | * startup state, ready to be add into new context. | ||
| 7686 | */ | ||
| 7687 | perf_event__state_init(group_leader); | ||
| 7688 | perf_install_in_context(ctx, group_leader, group_leader->cpu); | ||
| 7689 | get_ctx(ctx); | ||
| 7495 | } | 7690 | } |
| 7496 | 7691 | ||
| 7497 | perf_install_in_context(ctx, event, event->cpu); | 7692 | perf_install_in_context(ctx, event, event->cpu); |
| 7498 | perf_unpin_context(ctx); | 7693 | perf_unpin_context(ctx); |
| 7694 | |||
| 7695 | if (move_group) { | ||
| 7696 | mutex_unlock(&gctx->mutex); | ||
| 7697 | put_ctx(gctx); | ||
| 7698 | } | ||
| 7499 | mutex_unlock(&ctx->mutex); | 7699 | mutex_unlock(&ctx->mutex); |
| 7500 | 7700 | ||
| 7501 | put_online_cpus(); | 7701 | put_online_cpus(); |
| @@ -7603,7 +7803,11 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) | |||
| 7603 | src_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, src_cpu)->ctx; | 7803 | src_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, src_cpu)->ctx; |
| 7604 | dst_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, dst_cpu)->ctx; | 7804 | dst_ctx = &per_cpu_ptr(pmu->pmu_cpu_context, dst_cpu)->ctx; |
| 7605 | 7805 | ||
| 7606 | mutex_lock(&src_ctx->mutex); | 7806 | /* |
| 7807 | * See perf_event_ctx_lock() for comments on the details | ||
| 7808 | * of swizzling perf_event::ctx. | ||
| 7809 | */ | ||
| 7810 | mutex_lock_double(&src_ctx->mutex, &dst_ctx->mutex); | ||
| 7607 | list_for_each_entry_safe(event, tmp, &src_ctx->event_list, | 7811 | list_for_each_entry_safe(event, tmp, &src_ctx->event_list, |
| 7608 | event_entry) { | 7812 | event_entry) { |
| 7609 | perf_remove_from_context(event, false); | 7813 | perf_remove_from_context(event, false); |
| @@ -7611,11 +7815,36 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) | |||
| 7611 | put_ctx(src_ctx); | 7815 | put_ctx(src_ctx); |
| 7612 | list_add(&event->migrate_entry, &events); | 7816 | list_add(&event->migrate_entry, &events); |
| 7613 | } | 7817 | } |
| 7614 | mutex_unlock(&src_ctx->mutex); | ||
| 7615 | 7818 | ||
| 7819 | /* | ||
| 7820 | * Wait for the events to quiesce before re-instating them. | ||
| 7821 | */ | ||
| 7616 | synchronize_rcu(); | 7822 | synchronize_rcu(); |
| 7617 | 7823 | ||
| 7618 | mutex_lock(&dst_ctx->mutex); | 7824 | /* |
| 7825 | * Re-instate events in 2 passes. | ||
| 7826 | * | ||
| 7827 | * Skip over group leaders and only install siblings on this first | ||
| 7828 | * pass, siblings will not get enabled without a leader, however a | ||
| 7829 | * leader will enable its siblings, even if those are still on the old | ||
| 7830 | * context. | ||
| 7831 | */ | ||
| 7832 | list_for_each_entry_safe(event, tmp, &events, migrate_entry) { | ||
| 7833 | if (event->group_leader == event) | ||
| 7834 | continue; | ||
| 7835 | |||
| 7836 | list_del(&event->migrate_entry); | ||
| 7837 | if (event->state >= PERF_EVENT_STATE_OFF) | ||
| 7838 | event->state = PERF_EVENT_STATE_INACTIVE; | ||
| 7839 | account_event_cpu(event, dst_cpu); | ||
| 7840 | perf_install_in_context(dst_ctx, event, dst_cpu); | ||
| 7841 | get_ctx(dst_ctx); | ||
| 7842 | } | ||
| 7843 | |||
| 7844 | /* | ||
| 7845 | * Once all the siblings are setup properly, install the group leaders | ||
| 7846 | * to make it go. | ||
| 7847 | */ | ||
| 7619 | list_for_each_entry_safe(event, tmp, &events, migrate_entry) { | 7848 | list_for_each_entry_safe(event, tmp, &events, migrate_entry) { |
| 7620 | list_del(&event->migrate_entry); | 7849 | list_del(&event->migrate_entry); |
| 7621 | if (event->state >= PERF_EVENT_STATE_OFF) | 7850 | if (event->state >= PERF_EVENT_STATE_OFF) |
| @@ -7625,6 +7854,7 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu) | |||
| 7625 | get_ctx(dst_ctx); | 7854 | get_ctx(dst_ctx); |
| 7626 | } | 7855 | } |
| 7627 | mutex_unlock(&dst_ctx->mutex); | 7856 | mutex_unlock(&dst_ctx->mutex); |
| 7857 | mutex_unlock(&src_ctx->mutex); | ||
| 7628 | } | 7858 | } |
| 7629 | EXPORT_SYMBOL_GPL(perf_pmu_migrate_context); | 7859 | EXPORT_SYMBOL_GPL(perf_pmu_migrate_context); |
| 7630 | 7860 | ||
| @@ -7811,14 +8041,19 @@ static void perf_free_event(struct perf_event *event, | |||
| 7811 | 8041 | ||
| 7812 | put_event(parent); | 8042 | put_event(parent); |
| 7813 | 8043 | ||
| 8044 | raw_spin_lock_irq(&ctx->lock); | ||
| 7814 | perf_group_detach(event); | 8045 | perf_group_detach(event); |
| 7815 | list_del_event(event, ctx); | 8046 | list_del_event(event, ctx); |
| 8047 | raw_spin_unlock_irq(&ctx->lock); | ||
| 7816 | free_event(event); | 8048 | free_event(event); |
| 7817 | } | 8049 | } |
| 7818 | 8050 | ||
| 7819 | /* | 8051 | /* |
| 7820 | * free an unexposed, unused context as created by inheritance by | 8052 | * Free an unexposed, unused context as created by inheritance by |
| 7821 | * perf_event_init_task below, used by fork() in case of fail. | 8053 | * perf_event_init_task below, used by fork() in case of fail. |
| 8054 | * | ||
| 8055 | * Not all locks are strictly required, but take them anyway to be nice and | ||
| 8056 | * help out with the lockdep assertions. | ||
| 7822 | */ | 8057 | */ |
| 7823 | void perf_event_free_task(struct task_struct *task) | 8058 | void perf_event_free_task(struct task_struct *task) |
| 7824 | { | 8059 | { |
| @@ -8137,7 +8372,7 @@ static void __init perf_event_init_all_cpus(void) | |||
| 8137 | for_each_possible_cpu(cpu) { | 8372 | for_each_possible_cpu(cpu) { |
| 8138 | swhash = &per_cpu(swevent_htable, cpu); | 8373 | swhash = &per_cpu(swevent_htable, cpu); |
| 8139 | mutex_init(&swhash->hlist_mutex); | 8374 | mutex_init(&swhash->hlist_mutex); |
| 8140 | INIT_LIST_HEAD(&per_cpu(rotation_list, cpu)); | 8375 | INIT_LIST_HEAD(&per_cpu(active_ctx_list, cpu)); |
| 8141 | } | 8376 | } |
| 8142 | } | 8377 | } |
| 8143 | 8378 | ||
| @@ -8158,22 +8393,11 @@ static void perf_event_init_cpu(int cpu) | |||
| 8158 | } | 8393 | } |
| 8159 | 8394 | ||
| 8160 | #if defined CONFIG_HOTPLUG_CPU || defined CONFIG_KEXEC | 8395 | #if defined CONFIG_HOTPLUG_CPU || defined CONFIG_KEXEC |
| 8161 | static void perf_pmu_rotate_stop(struct pmu *pmu) | ||
| 8162 | { | ||
| 8163 | struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context); | ||
| 8164 | |||
| 8165 | WARN_ON(!irqs_disabled()); | ||
| 8166 | |||
| 8167 | list_del_init(&cpuctx->rotation_list); | ||
| 8168 | } | ||
| 8169 | |||
| 8170 | static void __perf_event_exit_context(void *__info) | 8396 | static void __perf_event_exit_context(void *__info) |
| 8171 | { | 8397 | { |
| 8172 | struct remove_event re = { .detach_group = true }; | 8398 | struct remove_event re = { .detach_group = true }; |
| 8173 | struct perf_event_context *ctx = __info; | 8399 | struct perf_event_context *ctx = __info; |
| 8174 | 8400 | ||
| 8175 | perf_pmu_rotate_stop(ctx->pmu); | ||
| 8176 | |||
| 8177 | rcu_read_lock(); | 8401 | rcu_read_lock(); |
| 8178 | list_for_each_entry_rcu(re.event, &ctx->event_list, event_entry) | 8402 | list_for_each_entry_rcu(re.event, &ctx->event_list, event_entry) |
| 8179 | __perf_remove_from_context(&re); | 8403 | __perf_remove_from_context(&re); |
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 146a5792b1d2..eadb95ce7aac 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c | |||
| @@ -13,12 +13,13 @@ | |||
| 13 | #include <linux/vmalloc.h> | 13 | #include <linux/vmalloc.h> |
| 14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
| 15 | #include <linux/circ_buf.h> | 15 | #include <linux/circ_buf.h> |
| 16 | #include <linux/poll.h> | ||
| 16 | 17 | ||
| 17 | #include "internal.h" | 18 | #include "internal.h" |
| 18 | 19 | ||
| 19 | static void perf_output_wakeup(struct perf_output_handle *handle) | 20 | static void perf_output_wakeup(struct perf_output_handle *handle) |
| 20 | { | 21 | { |
| 21 | atomic_set(&handle->rb->poll, POLL_IN); | 22 | atomic_set(&handle->rb->poll, POLLIN); |
| 22 | 23 | ||
| 23 | handle->event->pending_wakeup = 1; | 24 | handle->event->pending_wakeup = 1; |
| 24 | irq_work_queue(&handle->event->pending); | 25 | irq_work_queue(&handle->event->pending); |
diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 5eab11d4b747..1612578a5b7a 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c | |||
| @@ -1082,7 +1082,7 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu) | |||
| 1082 | if (p->sched_class->migrate_task_rq) | 1082 | if (p->sched_class->migrate_task_rq) |
| 1083 | p->sched_class->migrate_task_rq(p, new_cpu); | 1083 | p->sched_class->migrate_task_rq(p, new_cpu); |
| 1084 | p->se.nr_migrations++; | 1084 | p->se.nr_migrations++; |
| 1085 | perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, NULL, 0); | 1085 | perf_sw_event_sched(PERF_COUNT_SW_CPU_MIGRATIONS, 1, 0); |
| 1086 | } | 1086 | } |
| 1087 | 1087 | ||
| 1088 | __set_task_cpu(p, new_cpu); | 1088 | __set_task_cpu(p, new_cpu); |
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 4b9c114ee9de..6fa484de2ba1 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c | |||
| @@ -261,7 +261,7 @@ void perf_trace_del(struct perf_event *p_event, int flags) | |||
| 261 | } | 261 | } |
| 262 | 262 | ||
| 263 | void *perf_trace_buf_prepare(int size, unsigned short type, | 263 | void *perf_trace_buf_prepare(int size, unsigned short type, |
| 264 | struct pt_regs *regs, int *rctxp) | 264 | struct pt_regs **regs, int *rctxp) |
| 265 | { | 265 | { |
| 266 | struct trace_entry *entry; | 266 | struct trace_entry *entry; |
| 267 | unsigned long flags; | 267 | unsigned long flags; |
| @@ -280,6 +280,8 @@ void *perf_trace_buf_prepare(int size, unsigned short type, | |||
| 280 | if (*rctxp < 0) | 280 | if (*rctxp < 0) |
| 281 | return NULL; | 281 | return NULL; |
| 282 | 282 | ||
| 283 | if (regs) | ||
| 284 | *regs = this_cpu_ptr(&__perf_regs[*rctxp]); | ||
| 283 | raw_data = this_cpu_ptr(perf_trace_buf[*rctxp]); | 285 | raw_data = this_cpu_ptr(perf_trace_buf[*rctxp]); |
| 284 | 286 | ||
| 285 | /* zero the dead bytes from align to not leak stack to user */ | 287 | /* zero the dead bytes from align to not leak stack to user */ |
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 5edb518be345..296079ae6583 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c | |||
| @@ -1148,7 +1148,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs) | |||
| 1148 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); | 1148 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); |
| 1149 | size -= sizeof(u32); | 1149 | size -= sizeof(u32); |
| 1150 | 1150 | ||
| 1151 | entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx); | 1151 | entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx); |
| 1152 | if (!entry) | 1152 | if (!entry) |
| 1153 | return; | 1153 | return; |
| 1154 | 1154 | ||
| @@ -1179,7 +1179,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, | |||
| 1179 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); | 1179 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); |
| 1180 | size -= sizeof(u32); | 1180 | size -= sizeof(u32); |
| 1181 | 1181 | ||
| 1182 | entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx); | 1182 | entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx); |
| 1183 | if (!entry) | 1183 | if (!entry) |
| 1184 | return; | 1184 | return; |
| 1185 | 1185 | ||
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index c6ee36fcbf90..f97f6e3a676c 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c | |||
| @@ -574,7 +574,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) | |||
| 574 | size -= sizeof(u32); | 574 | size -= sizeof(u32); |
| 575 | 575 | ||
| 576 | rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size, | 576 | rec = (struct syscall_trace_enter *)perf_trace_buf_prepare(size, |
| 577 | sys_data->enter_event->event.type, regs, &rctx); | 577 | sys_data->enter_event->event.type, NULL, &rctx); |
| 578 | if (!rec) | 578 | if (!rec) |
| 579 | return; | 579 | return; |
| 580 | 580 | ||
| @@ -647,7 +647,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) | |||
| 647 | size -= sizeof(u32); | 647 | size -= sizeof(u32); |
| 648 | 648 | ||
| 649 | rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size, | 649 | rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size, |
| 650 | sys_data->exit_event->event.type, regs, &rctx); | 650 | sys_data->exit_event->event.type, NULL, &rctx); |
| 651 | if (!rec) | 651 | if (!rec) |
| 652 | return; | 652 | return; |
| 653 | 653 | ||
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 8520acc34b18..b11441321e7a 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c | |||
| @@ -1111,7 +1111,7 @@ static void __uprobe_perf_func(struct trace_uprobe *tu, | |||
| 1111 | if (hlist_empty(head)) | 1111 | if (hlist_empty(head)) |
| 1112 | goto out; | 1112 | goto out; |
| 1113 | 1113 | ||
| 1114 | entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx); | 1114 | entry = perf_trace_buf_prepare(size, call->event.type, NULL, &rctx); |
| 1115 | if (!entry) | 1115 | if (!entry) |
| 1116 | goto out; | 1116 | goto out; |
| 1117 | 1117 | ||
diff --git a/tools/lib/api/fs/debugfs.c b/tools/lib/api/fs/debugfs.c index 86ea2d7b8845..d2b18e887071 100644 --- a/tools/lib/api/fs/debugfs.c +++ b/tools/lib/api/fs/debugfs.c | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #define _GNU_SOURCE | ||
| 1 | #include <errno.h> | 2 | #include <errno.h> |
| 2 | #include <stdio.h> | 3 | #include <stdio.h> |
| 3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
| @@ -98,3 +99,45 @@ char *debugfs_mount(const char *mountpoint) | |||
| 98 | out: | 99 | out: |
| 99 | return debugfs_mountpoint; | 100 | return debugfs_mountpoint; |
| 100 | } | 101 | } |
| 102 | |||
| 103 | int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename) | ||
| 104 | { | ||
| 105 | char sbuf[128]; | ||
| 106 | |||
| 107 | switch (err) { | ||
| 108 | case ENOENT: | ||
| 109 | if (debugfs_found) { | ||
| 110 | snprintf(buf, size, | ||
| 111 | "Error:\tFile %s/%s not found.\n" | ||
| 112 | "Hint:\tPerhaps this kernel misses some CONFIG_ setting to enable this feature?.\n", | ||
| 113 | debugfs_mountpoint, filename); | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | snprintf(buf, size, "%s", | ||
| 117 | "Error:\tUnable to find debugfs\n" | ||
| 118 | "Hint:\tWas your kernel compiled with debugfs support?\n" | ||
| 119 | "Hint:\tIs the debugfs filesystem mounted?\n" | ||
| 120 | "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); | ||
| 121 | break; | ||
| 122 | case EACCES: | ||
| 123 | snprintf(buf, size, | ||
| 124 | "Error:\tNo permissions to read %s/%s\n" | ||
| 125 | "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", | ||
| 126 | debugfs_mountpoint, filename, debugfs_mountpoint); | ||
| 127 | break; | ||
| 128 | default: | ||
| 129 | snprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); | ||
| 130 | break; | ||
| 131 | } | ||
| 132 | |||
| 133 | return 0; | ||
| 134 | } | ||
| 135 | |||
| 136 | int debugfs__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name) | ||
| 137 | { | ||
| 138 | char path[PATH_MAX]; | ||
| 139 | |||
| 140 | snprintf(path, PATH_MAX, "tracing/events/%s/%s", sys, name ?: "*"); | ||
| 141 | |||
| 142 | return debugfs__strerror_open(err, buf, size, path); | ||
| 143 | } | ||
diff --git a/tools/lib/api/fs/debugfs.h b/tools/lib/api/fs/debugfs.h index f19d3df9609d..0739881a9897 100644 --- a/tools/lib/api/fs/debugfs.h +++ b/tools/lib/api/fs/debugfs.h | |||
| @@ -26,4 +26,7 @@ char *debugfs_mount(const char *mountpoint); | |||
| 26 | 26 | ||
| 27 | extern char debugfs_mountpoint[]; | 27 | extern char debugfs_mountpoint[]; |
| 28 | 28 | ||
| 29 | int debugfs__strerror_open(int err, char *buf, size_t size, const char *filename); | ||
| 30 | int debugfs__strerror_open_tp(int err, char *buf, size_t size, const char *sys, const char *name); | ||
| 31 | |||
| 29 | #endif /* __API_DEBUGFS_H__ */ | 32 | #endif /* __API_DEBUGFS_H__ */ |
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index cf3a44bf1ec3..afe20ed9fac8 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
| @@ -32,6 +32,7 @@ | |||
| 32 | #include <stdint.h> | 32 | #include <stdint.h> |
| 33 | #include <limits.h> | 33 | #include <limits.h> |
| 34 | 34 | ||
| 35 | #include <netinet/ip6.h> | ||
| 35 | #include "event-parse.h" | 36 | #include "event-parse.h" |
| 36 | #include "event-utils.h" | 37 | #include "event-utils.h" |
| 37 | 38 | ||
| @@ -4149,6 +4150,324 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, | |||
| 4149 | trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); | 4150 | trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); |
| 4150 | } | 4151 | } |
| 4151 | 4152 | ||
| 4153 | static void print_ip4_addr(struct trace_seq *s, char i, unsigned char *buf) | ||
| 4154 | { | ||
| 4155 | const char *fmt; | ||
| 4156 | |||
| 4157 | if (i == 'i') | ||
| 4158 | fmt = "%03d.%03d.%03d.%03d"; | ||
| 4159 | else | ||
| 4160 | fmt = "%d.%d.%d.%d"; | ||
| 4161 | |||
| 4162 | trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3]); | ||
| 4163 | } | ||
| 4164 | |||
| 4165 | static inline bool ipv6_addr_v4mapped(const struct in6_addr *a) | ||
| 4166 | { | ||
| 4167 | return ((unsigned long)(a->s6_addr32[0] | a->s6_addr32[1]) | | ||
| 4168 | (unsigned long)(a->s6_addr32[2] ^ htonl(0x0000ffff))) == 0UL; | ||
| 4169 | } | ||
| 4170 | |||
| 4171 | static inline bool ipv6_addr_is_isatap(const struct in6_addr *addr) | ||
| 4172 | { | ||
| 4173 | return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); | ||
| 4174 | } | ||
| 4175 | |||
| 4176 | static void print_ip6c_addr(struct trace_seq *s, unsigned char *addr) | ||
| 4177 | { | ||
| 4178 | int i, j, range; | ||
| 4179 | unsigned char zerolength[8]; | ||
| 4180 | int longest = 1; | ||
| 4181 | int colonpos = -1; | ||
| 4182 | uint16_t word; | ||
| 4183 | uint8_t hi, lo; | ||
| 4184 | bool needcolon = false; | ||
| 4185 | bool useIPv4; | ||
| 4186 | struct in6_addr in6; | ||
| 4187 | |||
| 4188 | memcpy(&in6, addr, sizeof(struct in6_addr)); | ||
| 4189 | |||
| 4190 | useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); | ||
| 4191 | |||
| 4192 | memset(zerolength, 0, sizeof(zerolength)); | ||
| 4193 | |||
| 4194 | if (useIPv4) | ||
| 4195 | range = 6; | ||
| 4196 | else | ||
| 4197 | range = 8; | ||
| 4198 | |||
| 4199 | /* find position of longest 0 run */ | ||
| 4200 | for (i = 0; i < range; i++) { | ||
| 4201 | for (j = i; j < range; j++) { | ||
| 4202 | if (in6.s6_addr16[j] != 0) | ||
| 4203 | break; | ||
| 4204 | zerolength[i]++; | ||
| 4205 | } | ||
| 4206 | } | ||
| 4207 | for (i = 0; i < range; i++) { | ||
| 4208 | if (zerolength[i] > longest) { | ||
| 4209 | longest = zerolength[i]; | ||
| 4210 | colonpos = i; | ||
| 4211 | } | ||
| 4212 | } | ||
| 4213 | if (longest == 1) /* don't compress a single 0 */ | ||
| 4214 | colonpos = -1; | ||
| 4215 | |||
| 4216 | /* emit address */ | ||
| 4217 | for (i = 0; i < range; i++) { | ||
| 4218 | if (i == colonpos) { | ||
| 4219 | if (needcolon || i == 0) | ||
| 4220 | trace_seq_printf(s, ":"); | ||
| 4221 | trace_seq_printf(s, ":"); | ||
| 4222 | needcolon = false; | ||
| 4223 | i += longest - 1; | ||
| 4224 | continue; | ||
| 4225 | } | ||
| 4226 | if (needcolon) { | ||
| 4227 | trace_seq_printf(s, ":"); | ||
| 4228 | needcolon = false; | ||
| 4229 | } | ||
| 4230 | /* hex u16 without leading 0s */ | ||
| 4231 | word = ntohs(in6.s6_addr16[i]); | ||
| 4232 | hi = word >> 8; | ||
| 4233 | lo = word & 0xff; | ||
| 4234 | if (hi) | ||
| 4235 | trace_seq_printf(s, "%x%02x", hi, lo); | ||
| 4236 | else | ||
| 4237 | trace_seq_printf(s, "%x", lo); | ||
| 4238 | |||
| 4239 | needcolon = true; | ||
| 4240 | } | ||
| 4241 | |||
| 4242 | if (useIPv4) { | ||
| 4243 | if (needcolon) | ||
| 4244 | trace_seq_printf(s, ":"); | ||
| 4245 | print_ip4_addr(s, 'I', &in6.s6_addr[12]); | ||
| 4246 | } | ||
| 4247 | |||
| 4248 | return; | ||
| 4249 | } | ||
| 4250 | |||
| 4251 | static void print_ip6_addr(struct trace_seq *s, char i, unsigned char *buf) | ||
| 4252 | { | ||
| 4253 | int j; | ||
| 4254 | |||
| 4255 | for (j = 0; j < 16; j += 2) { | ||
| 4256 | trace_seq_printf(s, "%02x%02x", buf[j], buf[j+1]); | ||
| 4257 | if (i == 'I' && j < 14) | ||
| 4258 | trace_seq_printf(s, ":"); | ||
| 4259 | } | ||
| 4260 | } | ||
| 4261 | |||
| 4262 | /* | ||
| 4263 | * %pi4 print an IPv4 address with leading zeros | ||
| 4264 | * %pI4 print an IPv4 address without leading zeros | ||
| 4265 | * %pi6 print an IPv6 address without colons | ||
| 4266 | * %pI6 print an IPv6 address with colons | ||
| 4267 | * %pI6c print an IPv6 address in compressed form with colons | ||
| 4268 | * %pISpc print an IP address based on sockaddr; p adds port. | ||
| 4269 | */ | ||
| 4270 | static int print_ipv4_arg(struct trace_seq *s, const char *ptr, char i, | ||
| 4271 | void *data, int size, struct event_format *event, | ||
| 4272 | struct print_arg *arg) | ||
| 4273 | { | ||
| 4274 | unsigned char *buf; | ||
| 4275 | |||
| 4276 | if (arg->type == PRINT_FUNC) { | ||
| 4277 | process_defined_func(s, data, size, event, arg); | ||
| 4278 | return 0; | ||
| 4279 | } | ||
| 4280 | |||
| 4281 | if (arg->type != PRINT_FIELD) { | ||
| 4282 | trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); | ||
| 4283 | return 0; | ||
| 4284 | } | ||
| 4285 | |||
| 4286 | if (!arg->field.field) { | ||
| 4287 | arg->field.field = | ||
| 4288 | pevent_find_any_field(event, arg->field.name); | ||
| 4289 | if (!arg->field.field) { | ||
| 4290 | do_warning("%s: field %s not found", | ||
| 4291 | __func__, arg->field.name); | ||
| 4292 | return 0; | ||
| 4293 | } | ||
| 4294 | } | ||
| 4295 | |||
| 4296 | buf = data + arg->field.field->offset; | ||
| 4297 | |||
| 4298 | if (arg->field.field->size != 4) { | ||
| 4299 | trace_seq_printf(s, "INVALIDIPv4"); | ||
| 4300 | return 0; | ||
| 4301 | } | ||
| 4302 | print_ip4_addr(s, i, buf); | ||
| 4303 | |||
| 4304 | return 0; | ||
| 4305 | } | ||
| 4306 | |||
| 4307 | static int print_ipv6_arg(struct trace_seq *s, const char *ptr, char i, | ||
| 4308 | void *data, int size, struct event_format *event, | ||
| 4309 | struct print_arg *arg) | ||
| 4310 | { | ||
| 4311 | char have_c = 0; | ||
| 4312 | unsigned char *buf; | ||
| 4313 | int rc = 0; | ||
| 4314 | |||
| 4315 | /* pI6c */ | ||
| 4316 | if (i == 'I' && *ptr == 'c') { | ||
| 4317 | have_c = 1; | ||
| 4318 | ptr++; | ||
| 4319 | rc++; | ||
| 4320 | } | ||
| 4321 | |||
| 4322 | if (arg->type == PRINT_FUNC) { | ||
| 4323 | process_defined_func(s, data, size, event, arg); | ||
| 4324 | return rc; | ||
| 4325 | } | ||
| 4326 | |||
| 4327 | if (arg->type != PRINT_FIELD) { | ||
| 4328 | trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); | ||
| 4329 | return rc; | ||
| 4330 | } | ||
| 4331 | |||
| 4332 | if (!arg->field.field) { | ||
| 4333 | arg->field.field = | ||
| 4334 | pevent_find_any_field(event, arg->field.name); | ||
| 4335 | if (!arg->field.field) { | ||
| 4336 | do_warning("%s: field %s not found", | ||
| 4337 | __func__, arg->field.name); | ||
| 4338 | return rc; | ||
| 4339 | } | ||
| 4340 | } | ||
| 4341 | |||
| 4342 | buf = data + arg->field.field->offset; | ||
| 4343 | |||
| 4344 | if (arg->field.field->size != 16) { | ||
| 4345 | trace_seq_printf(s, "INVALIDIPv6"); | ||
| 4346 | return rc; | ||
| 4347 | } | ||
| 4348 | |||
| 4349 | if (have_c) | ||
| 4350 | print_ip6c_addr(s, buf); | ||
| 4351 | else | ||
| 4352 | print_ip6_addr(s, i, buf); | ||
| 4353 | |||
| 4354 | return rc; | ||
| 4355 | } | ||
| 4356 | |||
| 4357 | static int print_ipsa_arg(struct trace_seq *s, const char *ptr, char i, | ||
| 4358 | void *data, int size, struct event_format *event, | ||
| 4359 | struct print_arg *arg) | ||
| 4360 | { | ||
| 4361 | char have_c = 0, have_p = 0; | ||
| 4362 | unsigned char *buf; | ||
| 4363 | struct sockaddr_storage *sa; | ||
| 4364 | int rc = 0; | ||
| 4365 | |||
| 4366 | /* pISpc */ | ||
| 4367 | if (i == 'I') { | ||
| 4368 | if (*ptr == 'p') { | ||
| 4369 | have_p = 1; | ||
| 4370 | ptr++; | ||
| 4371 | rc++; | ||
| 4372 | } | ||
| 4373 | if (*ptr == 'c') { | ||
| 4374 | have_c = 1; | ||
| 4375 | ptr++; | ||
| 4376 | rc++; | ||
| 4377 | } | ||
| 4378 | } | ||
| 4379 | |||
| 4380 | if (arg->type == PRINT_FUNC) { | ||
| 4381 | process_defined_func(s, data, size, event, arg); | ||
| 4382 | return rc; | ||
| 4383 | } | ||
| 4384 | |||
| 4385 | if (arg->type != PRINT_FIELD) { | ||
| 4386 | trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", arg->type); | ||
| 4387 | return rc; | ||
| 4388 | } | ||
| 4389 | |||
| 4390 | if (!arg->field.field) { | ||
| 4391 | arg->field.field = | ||
| 4392 | pevent_find_any_field(event, arg->field.name); | ||
| 4393 | if (!arg->field.field) { | ||
| 4394 | do_warning("%s: field %s not found", | ||
| 4395 | __func__, arg->field.name); | ||
| 4396 | return rc; | ||
| 4397 | } | ||
| 4398 | } | ||
| 4399 | |||
| 4400 | sa = (struct sockaddr_storage *) (data + arg->field.field->offset); | ||
| 4401 | |||
| 4402 | if (sa->ss_family == AF_INET) { | ||
| 4403 | struct sockaddr_in *sa4 = (struct sockaddr_in *) sa; | ||
| 4404 | |||
| 4405 | if (arg->field.field->size < sizeof(struct sockaddr_in)) { | ||
| 4406 | trace_seq_printf(s, "INVALIDIPv4"); | ||
| 4407 | return rc; | ||
| 4408 | } | ||
| 4409 | |||
| 4410 | print_ip4_addr(s, i, (unsigned char *) &sa4->sin_addr); | ||
| 4411 | if (have_p) | ||
| 4412 | trace_seq_printf(s, ":%d", ntohs(sa4->sin_port)); | ||
| 4413 | |||
| 4414 | |||
| 4415 | } else if (sa->ss_family == AF_INET6) { | ||
| 4416 | struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa; | ||
| 4417 | |||
| 4418 | if (arg->field.field->size < sizeof(struct sockaddr_in6)) { | ||
| 4419 | trace_seq_printf(s, "INVALIDIPv6"); | ||
| 4420 | return rc; | ||
| 4421 | } | ||
| 4422 | |||
| 4423 | if (have_p) | ||
| 4424 | trace_seq_printf(s, "["); | ||
| 4425 | |||
| 4426 | buf = (unsigned char *) &sa6->sin6_addr; | ||
| 4427 | if (have_c) | ||
| 4428 | print_ip6c_addr(s, buf); | ||
| 4429 | else | ||
| 4430 | print_ip6_addr(s, i, buf); | ||
| 4431 | |||
| 4432 | if (have_p) | ||
| 4433 | trace_seq_printf(s, "]:%d", ntohs(sa6->sin6_port)); | ||
| 4434 | } | ||
| 4435 | |||
| 4436 | return rc; | ||
| 4437 | } | ||
| 4438 | |||
| 4439 | static int print_ip_arg(struct trace_seq *s, const char *ptr, | ||
| 4440 | void *data, int size, struct event_format *event, | ||
| 4441 | struct print_arg *arg) | ||
| 4442 | { | ||
| 4443 | char i = *ptr; /* 'i' or 'I' */ | ||
| 4444 | char ver; | ||
| 4445 | int rc = 0; | ||
| 4446 | |||
| 4447 | ptr++; | ||
| 4448 | rc++; | ||
| 4449 | |||
| 4450 | ver = *ptr; | ||
| 4451 | ptr++; | ||
| 4452 | rc++; | ||
| 4453 | |||
| 4454 | switch (ver) { | ||
| 4455 | case '4': | ||
| 4456 | rc += print_ipv4_arg(s, ptr, i, data, size, event, arg); | ||
| 4457 | break; | ||
| 4458 | case '6': | ||
| 4459 | rc += print_ipv6_arg(s, ptr, i, data, size, event, arg); | ||
| 4460 | break; | ||
| 4461 | case 'S': | ||
| 4462 | rc += print_ipsa_arg(s, ptr, i, data, size, event, arg); | ||
| 4463 | break; | ||
| 4464 | default: | ||
| 4465 | return 0; | ||
| 4466 | } | ||
| 4467 | |||
| 4468 | return rc; | ||
| 4469 | } | ||
| 4470 | |||
| 4152 | static int is_printable_array(char *p, unsigned int len) | 4471 | static int is_printable_array(char *p, unsigned int len) |
| 4153 | { | 4472 | { |
| 4154 | unsigned int i; | 4473 | unsigned int i; |
| @@ -4337,6 +4656,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event | |||
| 4337 | ptr++; | 4656 | ptr++; |
| 4338 | arg = arg->next; | 4657 | arg = arg->next; |
| 4339 | break; | 4658 | break; |
| 4659 | } else if (*(ptr+1) == 'I' || *(ptr+1) == 'i') { | ||
| 4660 | int n; | ||
| 4661 | |||
| 4662 | n = print_ip_arg(s, ptr+1, data, size, event, arg); | ||
| 4663 | if (n > 0) { | ||
| 4664 | ptr += n; | ||
| 4665 | arg = arg->next; | ||
| 4666 | break; | ||
| 4667 | } | ||
| 4340 | } | 4668 | } |
| 4341 | 4669 | ||
| 4342 | /* fall through */ | 4670 | /* fall through */ |
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index fd77d81ea748..0294c57b1f5e 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt | |||
| @@ -38,7 +38,7 @@ OPTIONS | |||
| 38 | --remove=:: | 38 | --remove=:: |
| 39 | Remove specified file from the cache. | 39 | Remove specified file from the cache. |
| 40 | -M:: | 40 | -M:: |
| 41 | --missing=:: | 41 | --missing=:: |
| 42 | List missing build ids in the cache for the specified file. | 42 | List missing build ids in the cache for the specified file. |
| 43 | -u:: | 43 | -u:: |
| 44 | --update:: | 44 | --update:: |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index cbb4f743d921..3e2aec94f806 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
| @@ -89,6 +89,19 @@ raw encoding of 0x1A8 can be used: | |||
| 89 | You should refer to the processor specific documentation for getting these | 89 | You should refer to the processor specific documentation for getting these |
| 90 | details. Some of them are referenced in the SEE ALSO section below. | 90 | details. Some of them are referenced in the SEE ALSO section below. |
| 91 | 91 | ||
| 92 | PARAMETERIZED EVENTS | ||
| 93 | -------------------- | ||
| 94 | |||
| 95 | Some pmu events listed by 'perf-list' will be displayed with '?' in them. For | ||
| 96 | example: | ||
| 97 | |||
| 98 | hv_gpci/dtbp_ptitc,phys_processor_idx=?/ | ||
| 99 | |||
| 100 | This means that when provided as an event, a value for '?' must | ||
| 101 | also be supplied. For example: | ||
| 102 | |||
| 103 | perf stat -C 0 -e 'hv_gpci/dtbp_ptitc,phys_processor_idx=0x2/' ... | ||
| 104 | |||
| 92 | OPTIONS | 105 | OPTIONS |
| 93 | ------- | 106 | ------- |
| 94 | 107 | ||
diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt index 1d78a4064da4..43310d8661fe 100644 --- a/tools/perf/Documentation/perf-mem.txt +++ b/tools/perf/Documentation/perf-mem.txt | |||
| @@ -12,11 +12,12 @@ SYNOPSIS | |||
| 12 | 12 | ||
| 13 | DESCRIPTION | 13 | DESCRIPTION |
| 14 | ----------- | 14 | ----------- |
| 15 | "perf mem -t <TYPE> record" runs a command and gathers memory operation data | 15 | "perf mem record" runs a command and gathers memory operation data |
| 16 | from it, into perf.data. Perf record options are accepted and are passed through. | 16 | from it, into perf.data. Perf record options are accepted and are passed through. |
| 17 | 17 | ||
| 18 | "perf mem -t <TYPE> report" displays the result. It invokes perf report with the | 18 | "perf mem report" displays the result. It invokes perf report with the |
| 19 | right set of options to display a memory access profile. | 19 | right set of options to display a memory access profile. By default, loads |
| 20 | and stores are sampled. Use the -t option to limit to loads or stores. | ||
| 20 | 21 | ||
| 21 | Note that on Intel systems the memory latency reported is the use-latency, | 22 | Note that on Intel systems the memory latency reported is the use-latency, |
| 22 | not the pure load (or store latency). Use latency includes any pipeline | 23 | not the pure load (or store latency). Use latency includes any pipeline |
| @@ -29,7 +30,7 @@ OPTIONS | |||
| 29 | 30 | ||
| 30 | -t:: | 31 | -t:: |
| 31 | --type=:: | 32 | --type=:: |
| 32 | Select the memory operation type: load or store (default: load) | 33 | Select the memory operation type: load or store (default: load,store) |
| 33 | 34 | ||
| 34 | -D:: | 35 | -D:: |
| 35 | --dump-raw-samples=:: | 36 | --dump-raw-samples=:: |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index af9a54ece024..31e977459c51 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
| @@ -33,12 +33,27 @@ OPTIONS | |||
| 33 | - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a | 33 | - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a |
| 34 | hexadecimal event descriptor. | 34 | hexadecimal event descriptor. |
| 35 | 35 | ||
| 36 | - a hardware breakpoint event in the form of '\mem:addr[:access]' | 36 | - a symbolically formed PMU event like 'pmu/param1=0x3,param2/' where |
| 37 | 'param1', 'param2', etc are defined as formats for the PMU in | ||
| 38 | /sys/bus/event_sources/devices/<pmu>/format/*. | ||
| 39 | |||
| 40 | - a symbolically formed event like 'pmu/config=M,config1=N,config3=K/' | ||
| 41 | |||
| 42 | where M, N, K are numbers (in decimal, hex, octal format). Acceptable | ||
| 43 | values for each of 'config', 'config1' and 'config2' are defined by | ||
| 44 | corresponding entries in /sys/bus/event_sources/devices/<pmu>/format/* | ||
| 45 | param1 and param2 are defined as formats for the PMU in: | ||
| 46 | /sys/bus/event_sources/devices/<pmu>/format/* | ||
| 47 | |||
| 48 | - a hardware breakpoint event in the form of '\mem:addr[/len][:access]' | ||
| 37 | where addr is the address in memory you want to break in. | 49 | where addr is the address in memory you want to break in. |
| 38 | Access is the memory access type (read, write, execute) it can | 50 | Access is the memory access type (read, write, execute) it can |
| 39 | be passed as follows: '\mem:addr[:[r][w][x]]'. | 51 | be passed as follows: '\mem:addr[:[r][w][x]]'. len is the range, |
| 52 | number of bytes from specified addr, which the breakpoint will cover. | ||
| 40 | If you want to profile read-write accesses in 0x1000, just set | 53 | If you want to profile read-write accesses in 0x1000, just set |
| 41 | 'mem:0x1000:rw'. | 54 | 'mem:0x1000:rw'. |
| 55 | If you want to profile write accesses in [0x1000~1008), just set | ||
| 56 | 'mem:0x1000/8:w'. | ||
| 42 | 57 | ||
| 43 | --filter=<filter>:: | 58 | --filter=<filter>:: |
| 44 | Event filter. | 59 | Event filter. |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 21494806c0ab..a21eec05bc42 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
| @@ -125,46 +125,46 @@ OPTIONS | |||
| 125 | is equivalent to: | 125 | is equivalent to: |
| 126 | 126 | ||
| 127 | perf script -f trace:<fields> -f sw:<fields> -f hw:<fields> | 127 | perf script -f trace:<fields> -f sw:<fields> -f hw:<fields> |
| 128 | 128 | ||
| 129 | i.e., the specified fields apply to all event types if the type string | 129 | i.e., the specified fields apply to all event types if the type string |
| 130 | is not given. | 130 | is not given. |
| 131 | 131 | ||
| 132 | The arguments are processed in the order received. A later usage can | 132 | The arguments are processed in the order received. A later usage can |
| 133 | reset a prior request. e.g.: | 133 | reset a prior request. e.g.: |
| 134 | 134 | ||
| 135 | -f trace: -f comm,tid,time,ip,sym | 135 | -f trace: -f comm,tid,time,ip,sym |
| 136 | 136 | ||
| 137 | The first -f suppresses trace events (field list is ""), but then the | 137 | The first -f suppresses trace events (field list is ""), but then the |
| 138 | second invocation sets the fields to comm,tid,time,ip,sym. In this case a | 138 | second invocation sets the fields to comm,tid,time,ip,sym. In this case a |
| 139 | warning is given to the user: | 139 | warning is given to the user: |
| 140 | 140 | ||
| 141 | "Overriding previous field request for all events." | 141 | "Overriding previous field request for all events." |
| 142 | 142 | ||
| 143 | Alternatively, consider the order: | 143 | Alternatively, consider the order: |
| 144 | 144 | ||
| 145 | -f comm,tid,time,ip,sym -f trace: | 145 | -f comm,tid,time,ip,sym -f trace: |
| 146 | 146 | ||
| 147 | The first -f sets the fields for all events and the second -f | 147 | The first -f sets the fields for all events and the second -f |
| 148 | suppresses trace events. The user is given a warning message about | 148 | suppresses trace events. The user is given a warning message about |
| 149 | the override, and the result of the above is that only S/W and H/W | 149 | the override, and the result of the above is that only S/W and H/W |
| 150 | events are displayed with the given fields. | 150 | events are displayed with the given fields. |
| 151 | 151 | ||
| 152 | For the 'wildcard' option if a user selected field is invalid for an | 152 | For the 'wildcard' option if a user selected field is invalid for an |
| 153 | event type, a message is displayed to the user that the option is | 153 | event type, a message is displayed to the user that the option is |
| 154 | ignored for that type. For example: | 154 | ignored for that type. For example: |
| 155 | 155 | ||
| 156 | $ perf script -f comm,tid,trace | 156 | $ perf script -f comm,tid,trace |
| 157 | 'trace' not valid for hardware events. Ignoring. | 157 | 'trace' not valid for hardware events. Ignoring. |
| 158 | 'trace' not valid for software events. Ignoring. | 158 | 'trace' not valid for software events. Ignoring. |
| 159 | 159 | ||
| 160 | Alternatively, if the type is given an invalid field is specified it | 160 | Alternatively, if the type is given an invalid field is specified it |
| 161 | is an error. For example: | 161 | is an error. For example: |
| 162 | 162 | ||
| 163 | perf script -v -f sw:comm,tid,trace | 163 | perf script -v -f sw:comm,tid,trace |
| 164 | 'trace' not valid for software events. | 164 | 'trace' not valid for software events. |
| 165 | 165 | ||
| 166 | At this point usage is displayed, and perf-script exits. | 166 | At this point usage is displayed, and perf-script exits. |
| 167 | 167 | ||
| 168 | Finally, a user may not set fields to none for all event types. | 168 | Finally, a user may not set fields to none for all event types. |
| 169 | i.e., -f "" is not allowed. | 169 | i.e., -f "" is not allowed. |
| 170 | 170 | ||
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 29ee857c09c6..04e150d83e7d 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
| @@ -25,10 +25,22 @@ OPTIONS | |||
| 25 | 25 | ||
| 26 | -e:: | 26 | -e:: |
| 27 | --event=:: | 27 | --event=:: |
| 28 | Select the PMU event. Selection can be a symbolic event name | 28 | Select the PMU event. Selection can be: |
| 29 | (use 'perf list' to list all events) or a raw PMU | 29 | |
| 30 | event (eventsel+umask) in the form of rNNN where NNN is a | 30 | - a symbolic event name (use 'perf list' to list all events) |
| 31 | hexadecimal event descriptor. | 31 | |
| 32 | - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a | ||
| 33 | hexadecimal event descriptor. | ||
| 34 | |||
| 35 | - a symbolically formed event like 'pmu/param1=0x3,param2/' where | ||
| 36 | param1 and param2 are defined as formats for the PMU in | ||
| 37 | /sys/bus/event_sources/devices/<pmu>/format/* | ||
| 38 | |||
| 39 | - a symbolically formed event like 'pmu/config=M,config1=N,config2=K/' | ||
| 40 | where M, N, K are numbers (in decimal, hex, octal format). | ||
| 41 | Acceptable values for each of 'config', 'config1' and 'config2' | ||
| 42 | parameters are defined by corresponding entries in | ||
| 43 | /sys/bus/event_sources/devices/<pmu>/format/* | ||
| 32 | 44 | ||
| 33 | -i:: | 45 | -i:: |
| 34 | --no-inherit:: | 46 | --no-inherit:: |
diff --git a/tools/perf/bench/futex.h b/tools/perf/bench/futex.h index 71f2844cf97f..7ed22ff1e1ac 100644 --- a/tools/perf/bench/futex.h +++ b/tools/perf/bench/futex.h | |||
| @@ -68,4 +68,17 @@ futex_cmp_requeue(u_int32_t *uaddr, u_int32_t val, u_int32_t *uaddr2, int nr_wak | |||
| 68 | val, opflags); | 68 | val, opflags); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | #ifndef HAVE_PTHREAD_ATTR_SETAFFINITY_NP | ||
| 72 | #include <pthread.h> | ||
| 73 | static inline int pthread_attr_setaffinity_np(pthread_attr_t *attr, | ||
| 74 | size_t cpusetsize, | ||
| 75 | cpu_set_t *cpuset) | ||
| 76 | { | ||
| 77 | attr = attr; | ||
| 78 | cpusetsize = cpusetsize; | ||
| 79 | cpuset = cpuset; | ||
| 80 | return 0; | ||
| 81 | } | ||
| 82 | #endif | ||
| 83 | |||
| 71 | #endif /* _FUTEX_H */ | 84 | #endif /* _FUTEX_H */ |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 77d5cae54c6a..50e6b66aea1f 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
| @@ -236,10 +236,10 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) | |||
| 236 | if (errno == ENOENT) | 236 | if (errno == ENOENT) |
| 237 | return false; | 237 | return false; |
| 238 | 238 | ||
| 239 | pr_warning("Problems with %s file, consider removing it from the cache\n", | 239 | pr_warning("Problems with %s file, consider removing it from the cache\n", |
| 240 | filename); | 240 | filename); |
| 241 | } else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) { | 241 | } else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) { |
| 242 | pr_warning("Problems with %s file, consider removing it from the cache\n", | 242 | pr_warning("Problems with %s file, consider removing it from the cache\n", |
| 243 | filename); | 243 | filename); |
| 244 | } | 244 | } |
| 245 | 245 | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 1fd96c13f199..74aada554b12 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
| @@ -390,6 +390,15 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist) | |||
| 390 | } | 390 | } |
| 391 | } | 391 | } |
| 392 | 392 | ||
| 393 | static struct data__file *fmt_to_data_file(struct perf_hpp_fmt *fmt) | ||
| 394 | { | ||
| 395 | struct diff_hpp_fmt *dfmt = container_of(fmt, struct diff_hpp_fmt, fmt); | ||
| 396 | void *ptr = dfmt - dfmt->idx; | ||
| 397 | struct data__file *d = container_of(ptr, struct data__file, fmt); | ||
| 398 | |||
| 399 | return d; | ||
| 400 | } | ||
| 401 | |||
| 393 | static struct hist_entry* | 402 | static struct hist_entry* |
| 394 | get_pair_data(struct hist_entry *he, struct data__file *d) | 403 | get_pair_data(struct hist_entry *he, struct data__file *d) |
| 395 | { | 404 | { |
| @@ -407,8 +416,7 @@ get_pair_data(struct hist_entry *he, struct data__file *d) | |||
| 407 | static struct hist_entry* | 416 | static struct hist_entry* |
| 408 | get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt) | 417 | get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt) |
| 409 | { | 418 | { |
| 410 | void *ptr = dfmt - dfmt->idx; | 419 | struct data__file *d = fmt_to_data_file(&dfmt->fmt); |
| 411 | struct data__file *d = container_of(ptr, struct data__file, fmt); | ||
| 412 | 420 | ||
| 413 | return get_pair_data(he, d); | 421 | return get_pair_data(he, d); |
| 414 | } | 422 | } |
| @@ -430,7 +438,7 @@ static void hists__baseline_only(struct hists *hists) | |||
| 430 | next = rb_next(&he->rb_node_in); | 438 | next = rb_next(&he->rb_node_in); |
| 431 | if (!hist_entry__next_pair(he)) { | 439 | if (!hist_entry__next_pair(he)) { |
| 432 | rb_erase(&he->rb_node_in, root); | 440 | rb_erase(&he->rb_node_in, root); |
| 433 | hist_entry__free(he); | 441 | hist_entry__delete(he); |
| 434 | } | 442 | } |
| 435 | } | 443 | } |
| 436 | } | 444 | } |
| @@ -448,26 +456,30 @@ static void hists__precompute(struct hists *hists) | |||
| 448 | next = rb_first(root); | 456 | next = rb_first(root); |
| 449 | while (next != NULL) { | 457 | while (next != NULL) { |
| 450 | struct hist_entry *he, *pair; | 458 | struct hist_entry *he, *pair; |
| 459 | struct data__file *d; | ||
| 460 | int i; | ||
| 451 | 461 | ||
| 452 | he = rb_entry(next, struct hist_entry, rb_node_in); | 462 | he = rb_entry(next, struct hist_entry, rb_node_in); |
| 453 | next = rb_next(&he->rb_node_in); | 463 | next = rb_next(&he->rb_node_in); |
| 454 | 464 | ||
| 455 | pair = get_pair_data(he, &data__files[sort_compute]); | 465 | data__for_each_file_new(i, d) { |
| 456 | if (!pair) | 466 | pair = get_pair_data(he, d); |
| 457 | continue; | 467 | if (!pair) |
| 468 | continue; | ||
| 458 | 469 | ||
| 459 | switch (compute) { | 470 | switch (compute) { |
| 460 | case COMPUTE_DELTA: | 471 | case COMPUTE_DELTA: |
| 461 | compute_delta(he, pair); | 472 | compute_delta(he, pair); |
| 462 | break; | 473 | break; |
| 463 | case COMPUTE_RATIO: | 474 | case COMPUTE_RATIO: |
| 464 | compute_ratio(he, pair); | 475 | compute_ratio(he, pair); |
| 465 | break; | 476 | break; |
| 466 | case COMPUTE_WEIGHTED_DIFF: | 477 | case COMPUTE_WEIGHTED_DIFF: |
| 467 | compute_wdiff(he, pair); | 478 | compute_wdiff(he, pair); |
| 468 | break; | 479 | break; |
| 469 | default: | 480 | default: |
| 470 | BUG_ON(1); | 481 | BUG_ON(1); |
| 482 | } | ||
| 471 | } | 483 | } |
| 472 | } | 484 | } |
| 473 | } | 485 | } |
| @@ -517,7 +529,7 @@ __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | |||
| 517 | 529 | ||
| 518 | static int64_t | 530 | static int64_t |
| 519 | hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | 531 | hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, |
| 520 | int c) | 532 | int c, int sort_idx) |
| 521 | { | 533 | { |
| 522 | bool pairs_left = hist_entry__has_pairs(left); | 534 | bool pairs_left = hist_entry__has_pairs(left); |
| 523 | bool pairs_right = hist_entry__has_pairs(right); | 535 | bool pairs_right = hist_entry__has_pairs(right); |
| @@ -529,8 +541,8 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | |||
| 529 | if (!pairs_left || !pairs_right) | 541 | if (!pairs_left || !pairs_right) |
| 530 | return pairs_left ? -1 : 1; | 542 | return pairs_left ? -1 : 1; |
| 531 | 543 | ||
| 532 | p_left = get_pair_data(left, &data__files[sort_compute]); | 544 | p_left = get_pair_data(left, &data__files[sort_idx]); |
| 533 | p_right = get_pair_data(right, &data__files[sort_compute]); | 545 | p_right = get_pair_data(right, &data__files[sort_idx]); |
| 534 | 546 | ||
| 535 | if (!p_left && !p_right) | 547 | if (!p_left && !p_right) |
| 536 | return 0; | 548 | return 0; |
| @@ -546,90 +558,102 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | |||
| 546 | } | 558 | } |
| 547 | 559 | ||
| 548 | static int64_t | 560 | static int64_t |
| 549 | hist_entry__cmp_nop(struct hist_entry *left __maybe_unused, | 561 | hist_entry__cmp_compute_idx(struct hist_entry *left, struct hist_entry *right, |
| 562 | int c, int sort_idx) | ||
| 563 | { | ||
| 564 | struct hist_entry *p_right, *p_left; | ||
| 565 | |||
| 566 | p_left = get_pair_data(left, &data__files[sort_idx]); | ||
| 567 | p_right = get_pair_data(right, &data__files[sort_idx]); | ||
| 568 | |||
| 569 | if (!p_left && !p_right) | ||
| 570 | return 0; | ||
| 571 | |||
| 572 | if (!p_left || !p_right) | ||
| 573 | return p_left ? -1 : 1; | ||
| 574 | |||
| 575 | if (c != COMPUTE_DELTA) { | ||
| 576 | /* | ||
| 577 | * The delta can be computed without the baseline, but | ||
| 578 | * others are not. Put those entries which have no | ||
| 579 | * values below. | ||
| 580 | */ | ||
| 581 | if (left->dummy && right->dummy) | ||
| 582 | return 0; | ||
| 583 | |||
| 584 | if (left->dummy || right->dummy) | ||
| 585 | return left->dummy ? 1 : -1; | ||
| 586 | } | ||
| 587 | |||
| 588 | return __hist_entry__cmp_compute(p_left, p_right, c); | ||
| 589 | } | ||
| 590 | |||
| 591 | static int64_t | ||
| 592 | hist_entry__cmp_nop(struct perf_hpp_fmt *fmt __maybe_unused, | ||
| 593 | struct hist_entry *left __maybe_unused, | ||
| 550 | struct hist_entry *right __maybe_unused) | 594 | struct hist_entry *right __maybe_unused) |
| 551 | { | 595 | { |
| 552 | return 0; | 596 | return 0; |
| 553 | } | 597 | } |
| 554 | 598 | ||
| 555 | static int64_t | 599 | static int64_t |
| 556 | hist_entry__cmp_baseline(struct hist_entry *left, struct hist_entry *right) | 600 | hist_entry__cmp_baseline(struct perf_hpp_fmt *fmt __maybe_unused, |
| 601 | struct hist_entry *left, struct hist_entry *right) | ||
| 557 | { | 602 | { |
| 558 | if (sort_compute) | ||
| 559 | return 0; | ||
| 560 | |||
| 561 | if (left->stat.period == right->stat.period) | 603 | if (left->stat.period == right->stat.period) |
| 562 | return 0; | 604 | return 0; |
| 563 | return left->stat.period > right->stat.period ? 1 : -1; | 605 | return left->stat.period > right->stat.period ? 1 : -1; |
| 564 | } | 606 | } |
| 565 | 607 | ||
| 566 | static int64_t | 608 | static int64_t |
| 567 | hist_entry__cmp_delta(struct hist_entry *left, struct hist_entry *right) | 609 | hist_entry__cmp_delta(struct perf_hpp_fmt *fmt, |
| 610 | struct hist_entry *left, struct hist_entry *right) | ||
| 568 | { | 611 | { |
| 569 | return hist_entry__cmp_compute(right, left, COMPUTE_DELTA); | 612 | struct data__file *d = fmt_to_data_file(fmt); |
| 613 | |||
| 614 | return hist_entry__cmp_compute(right, left, COMPUTE_DELTA, d->idx); | ||
| 570 | } | 615 | } |
| 571 | 616 | ||
| 572 | static int64_t | 617 | static int64_t |
| 573 | hist_entry__cmp_ratio(struct hist_entry *left, struct hist_entry *right) | 618 | hist_entry__cmp_ratio(struct perf_hpp_fmt *fmt, |
| 619 | struct hist_entry *left, struct hist_entry *right) | ||
| 574 | { | 620 | { |
| 575 | return hist_entry__cmp_compute(right, left, COMPUTE_RATIO); | 621 | struct data__file *d = fmt_to_data_file(fmt); |
| 622 | |||
| 623 | return hist_entry__cmp_compute(right, left, COMPUTE_RATIO, d->idx); | ||
| 576 | } | 624 | } |
| 577 | 625 | ||
| 578 | static int64_t | 626 | static int64_t |
| 579 | hist_entry__cmp_wdiff(struct hist_entry *left, struct hist_entry *right) | 627 | hist_entry__cmp_wdiff(struct perf_hpp_fmt *fmt, |
| 628 | struct hist_entry *left, struct hist_entry *right) | ||
| 580 | { | 629 | { |
| 581 | return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF); | 630 | struct data__file *d = fmt_to_data_file(fmt); |
| 631 | |||
| 632 | return hist_entry__cmp_compute(right, left, COMPUTE_WEIGHTED_DIFF, d->idx); | ||
| 582 | } | 633 | } |
| 583 | 634 | ||
| 584 | static void insert_hist_entry_by_compute(struct rb_root *root, | 635 | static int64_t |
| 585 | struct hist_entry *he, | 636 | hist_entry__cmp_delta_idx(struct perf_hpp_fmt *fmt __maybe_unused, |
| 586 | int c) | 637 | struct hist_entry *left, struct hist_entry *right) |
| 587 | { | 638 | { |
| 588 | struct rb_node **p = &root->rb_node; | 639 | return hist_entry__cmp_compute_idx(right, left, COMPUTE_DELTA, |
| 589 | struct rb_node *parent = NULL; | 640 | sort_compute); |
| 590 | struct hist_entry *iter; | ||
| 591 | |||
| 592 | while (*p != NULL) { | ||
| 593 | parent = *p; | ||
| 594 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
| 595 | if (hist_entry__cmp_compute(he, iter, c) < 0) | ||
| 596 | p = &(*p)->rb_left; | ||
| 597 | else | ||
| 598 | p = &(*p)->rb_right; | ||
| 599 | } | ||
| 600 | |||
| 601 | rb_link_node(&he->rb_node, parent, p); | ||
| 602 | rb_insert_color(&he->rb_node, root); | ||
| 603 | } | 641 | } |
| 604 | 642 | ||
| 605 | static void hists__compute_resort(struct hists *hists) | 643 | static int64_t |
| 644 | hist_entry__cmp_ratio_idx(struct perf_hpp_fmt *fmt __maybe_unused, | ||
| 645 | struct hist_entry *left, struct hist_entry *right) | ||
| 606 | { | 646 | { |
| 607 | struct rb_root *root; | 647 | return hist_entry__cmp_compute_idx(right, left, COMPUTE_RATIO, |
| 608 | struct rb_node *next; | 648 | sort_compute); |
| 609 | 649 | } | |
| 610 | if (sort__need_collapse) | ||
| 611 | root = &hists->entries_collapsed; | ||
| 612 | else | ||
| 613 | root = hists->entries_in; | ||
| 614 | |||
| 615 | hists->entries = RB_ROOT; | ||
| 616 | next = rb_first(root); | ||
| 617 | |||
| 618 | hists__reset_stats(hists); | ||
| 619 | hists__reset_col_len(hists); | ||
| 620 | |||
| 621 | while (next != NULL) { | ||
| 622 | struct hist_entry *he; | ||
| 623 | |||
| 624 | he = rb_entry(next, struct hist_entry, rb_node_in); | ||
| 625 | next = rb_next(&he->rb_node_in); | ||
| 626 | |||
| 627 | insert_hist_entry_by_compute(&hists->entries, he, compute); | ||
| 628 | hists__inc_stats(hists, he); | ||
| 629 | 650 | ||
| 630 | if (!he->filtered) | 651 | static int64_t |
| 631 | hists__calc_col_len(hists, he); | 652 | hist_entry__cmp_wdiff_idx(struct perf_hpp_fmt *fmt __maybe_unused, |
| 632 | } | 653 | struct hist_entry *left, struct hist_entry *right) |
| 654 | { | ||
| 655 | return hist_entry__cmp_compute_idx(right, left, COMPUTE_WEIGHTED_DIFF, | ||
| 656 | sort_compute); | ||
| 633 | } | 657 | } |
| 634 | 658 | ||
| 635 | static void hists__process(struct hists *hists) | 659 | static void hists__process(struct hists *hists) |
| @@ -637,12 +661,8 @@ static void hists__process(struct hists *hists) | |||
| 637 | if (show_baseline_only) | 661 | if (show_baseline_only) |
| 638 | hists__baseline_only(hists); | 662 | hists__baseline_only(hists); |
| 639 | 663 | ||
| 640 | if (sort_compute) { | 664 | hists__precompute(hists); |
| 641 | hists__precompute(hists); | 665 | hists__output_resort(hists, NULL); |
| 642 | hists__compute_resort(hists); | ||
| 643 | } else { | ||
| 644 | hists__output_resort(hists, NULL); | ||
| 645 | } | ||
| 646 | 666 | ||
| 647 | hists__fprintf(hists, true, 0, 0, 0, stdout); | 667 | hists__fprintf(hists, true, 0, 0, 0, stdout); |
| 648 | } | 668 | } |
| @@ -841,7 +861,7 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt, | |||
| 841 | char pfmt[20] = " "; | 861 | char pfmt[20] = " "; |
| 842 | 862 | ||
| 843 | if (!pair) | 863 | if (!pair) |
| 844 | goto dummy_print; | 864 | goto no_print; |
| 845 | 865 | ||
| 846 | switch (comparison_method) { | 866 | switch (comparison_method) { |
| 847 | case COMPUTE_DELTA: | 867 | case COMPUTE_DELTA: |
| @@ -850,8 +870,6 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt, | |||
| 850 | else | 870 | else |
| 851 | diff = compute_delta(he, pair); | 871 | diff = compute_delta(he, pair); |
| 852 | 872 | ||
| 853 | if (fabs(diff) < 0.01) | ||
| 854 | goto dummy_print; | ||
| 855 | scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1); | 873 | scnprintf(pfmt, 20, "%%%+d.2f%%%%", dfmt->header_width - 1); |
| 856 | return percent_color_snprintf(hpp->buf, hpp->size, | 874 | return percent_color_snprintf(hpp->buf, hpp->size, |
| 857 | pfmt, diff); | 875 | pfmt, diff); |
| @@ -883,6 +901,9 @@ static int __hpp__color_compare(struct perf_hpp_fmt *fmt, | |||
| 883 | } | 901 | } |
| 884 | dummy_print: | 902 | dummy_print: |
| 885 | return scnprintf(hpp->buf, hpp->size, "%*s", | 903 | return scnprintf(hpp->buf, hpp->size, "%*s", |
| 904 | dfmt->header_width, "N/A"); | ||
| 905 | no_print: | ||
| 906 | return scnprintf(hpp->buf, hpp->size, "%*s", | ||
| 886 | dfmt->header_width, pfmt); | 907 | dfmt->header_width, pfmt); |
| 887 | } | 908 | } |
| 888 | 909 | ||
| @@ -932,14 +953,15 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, | |||
| 932 | else | 953 | else |
| 933 | diff = compute_delta(he, pair); | 954 | diff = compute_delta(he, pair); |
| 934 | 955 | ||
| 935 | if (fabs(diff) >= 0.01) | 956 | scnprintf(buf, size, "%+4.2F%%", diff); |
| 936 | scnprintf(buf, size, "%+4.2F%%", diff); | ||
| 937 | break; | 957 | break; |
| 938 | 958 | ||
| 939 | case PERF_HPP_DIFF__RATIO: | 959 | case PERF_HPP_DIFF__RATIO: |
| 940 | /* No point for ratio number if we are dummy.. */ | 960 | /* No point for ratio number if we are dummy.. */ |
| 941 | if (he->dummy) | 961 | if (he->dummy) { |
| 962 | scnprintf(buf, size, "N/A"); | ||
| 942 | break; | 963 | break; |
| 964 | } | ||
| 943 | 965 | ||
| 944 | if (pair->diff.computed) | 966 | if (pair->diff.computed) |
| 945 | ratio = pair->diff.period_ratio; | 967 | ratio = pair->diff.period_ratio; |
| @@ -952,8 +974,10 @@ hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, | |||
| 952 | 974 | ||
| 953 | case PERF_HPP_DIFF__WEIGHTED_DIFF: | 975 | case PERF_HPP_DIFF__WEIGHTED_DIFF: |
| 954 | /* No point for wdiff number if we are dummy.. */ | 976 | /* No point for wdiff number if we are dummy.. */ |
| 955 | if (he->dummy) | 977 | if (he->dummy) { |
| 978 | scnprintf(buf, size, "N/A"); | ||
| 956 | break; | 979 | break; |
| 980 | } | ||
| 957 | 981 | ||
| 958 | if (pair->diff.computed) | 982 | if (pair->diff.computed) |
| 959 | wdiff = pair->diff.wdiff; | 983 | wdiff = pair->diff.wdiff; |
| @@ -1105,9 +1129,10 @@ static void data__hpp_register(struct data__file *d, int idx) | |||
| 1105 | perf_hpp__register_sort_field(fmt); | 1129 | perf_hpp__register_sort_field(fmt); |
| 1106 | } | 1130 | } |
| 1107 | 1131 | ||
| 1108 | static void ui_init(void) | 1132 | static int ui_init(void) |
| 1109 | { | 1133 | { |
| 1110 | struct data__file *d; | 1134 | struct data__file *d; |
| 1135 | struct perf_hpp_fmt *fmt; | ||
| 1111 | int i; | 1136 | int i; |
| 1112 | 1137 | ||
| 1113 | data__for_each_file(i, d) { | 1138 | data__for_each_file(i, d) { |
| @@ -1137,6 +1162,46 @@ static void ui_init(void) | |||
| 1137 | data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD : | 1162 | data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD : |
| 1138 | PERF_HPP_DIFF__PERIOD_BASELINE); | 1163 | PERF_HPP_DIFF__PERIOD_BASELINE); |
| 1139 | } | 1164 | } |
| 1165 | |||
| 1166 | if (!sort_compute) | ||
| 1167 | return 0; | ||
| 1168 | |||
| 1169 | /* | ||
| 1170 | * Prepend an fmt to sort on columns at 'sort_compute' first. | ||
| 1171 | * This fmt is added only to the sort list but not to the | ||
| 1172 | * output fields list. | ||
| 1173 | * | ||
| 1174 | * Note that this column (data) can be compared twice - one | ||
| 1175 | * for this 'sort_compute' fmt and another for the normal | ||
| 1176 | * diff_hpp_fmt. But it shouldn't a problem as most entries | ||
| 1177 | * will be sorted out by first try or baseline and comparing | ||
| 1178 | * is not a costly operation. | ||
| 1179 | */ | ||
| 1180 | fmt = zalloc(sizeof(*fmt)); | ||
| 1181 | if (fmt == NULL) { | ||
| 1182 | pr_err("Memory allocation failed\n"); | ||
| 1183 | return -1; | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | fmt->cmp = hist_entry__cmp_nop; | ||
| 1187 | fmt->collapse = hist_entry__cmp_nop; | ||
| 1188 | |||
| 1189 | switch (compute) { | ||
| 1190 | case COMPUTE_DELTA: | ||
| 1191 | fmt->sort = hist_entry__cmp_delta_idx; | ||
| 1192 | break; | ||
| 1193 | case COMPUTE_RATIO: | ||
| 1194 | fmt->sort = hist_entry__cmp_ratio_idx; | ||
| 1195 | break; | ||
| 1196 | case COMPUTE_WEIGHTED_DIFF: | ||
| 1197 | fmt->sort = hist_entry__cmp_wdiff_idx; | ||
| 1198 | break; | ||
| 1199 | default: | ||
| 1200 | BUG_ON(1); | ||
| 1201 | } | ||
| 1202 | |||
| 1203 | list_add(&fmt->sort_list, &perf_hpp__sort_list); | ||
| 1204 | return 0; | ||
| 1140 | } | 1205 | } |
| 1141 | 1206 | ||
| 1142 | static int data_init(int argc, const char **argv) | 1207 | static int data_init(int argc, const char **argv) |
| @@ -1202,7 +1267,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1202 | if (data_init(argc, argv) < 0) | 1267 | if (data_init(argc, argv) < 0) |
| 1203 | return -1; | 1268 | return -1; |
| 1204 | 1269 | ||
| 1205 | ui_init(); | 1270 | if (ui_init() < 0) |
| 1271 | return -1; | ||
| 1206 | 1272 | ||
| 1207 | sort__mode = SORT_MODE__DIFF; | 1273 | sort__mode = SORT_MODE__DIFF; |
| 1208 | 1274 | ||
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 84df2deed988..a13641e066f5 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
| @@ -343,6 +343,7 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 343 | int ret = -EINVAL; | 343 | int ret = -EINVAL; |
| 344 | struct perf_session *session = inject->session; | 344 | struct perf_session *session = inject->session; |
| 345 | struct perf_data_file *file_out = &inject->output; | 345 | struct perf_data_file *file_out = &inject->output; |
| 346 | int fd = perf_data_file__fd(file_out); | ||
| 346 | 347 | ||
| 347 | signal(SIGINT, sig_handler); | 348 | signal(SIGINT, sig_handler); |
| 348 | 349 | ||
| @@ -376,7 +377,7 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 376 | } | 377 | } |
| 377 | 378 | ||
| 378 | if (!file_out->is_pipe) | 379 | if (!file_out->is_pipe) |
| 379 | lseek(file_out->fd, session->header.data_offset, SEEK_SET); | 380 | lseek(fd, session->header.data_offset, SEEK_SET); |
| 380 | 381 | ||
| 381 | ret = perf_session__process_events(session, &inject->tool); | 382 | ret = perf_session__process_events(session, &inject->tool); |
| 382 | 383 | ||
| @@ -385,7 +386,7 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 385 | perf_header__set_feat(&session->header, | 386 | perf_header__set_feat(&session->header, |
| 386 | HEADER_BUILD_ID); | 387 | HEADER_BUILD_ID); |
| 387 | session->header.data_size = inject->bytes_written; | 388 | session->header.data_size = inject->bytes_written; |
| 388 | perf_session__write_header(session, session->evlist, file_out->fd, true); | 389 | perf_session__write_header(session, session->evlist, fd, true); |
| 389 | } | 390 | } |
| 390 | 391 | ||
| 391 | return ret; | 392 | return ret; |
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 24db6ffe2957..9b5663950a4d 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
| @@ -7,44 +7,47 @@ | |||
| 7 | #include "util/session.h" | 7 | #include "util/session.h" |
| 8 | #include "util/data.h" | 8 | #include "util/data.h" |
| 9 | 9 | ||
| 10 | #define MEM_OPERATION_LOAD "load" | 10 | #define MEM_OPERATION_LOAD 0x1 |
| 11 | #define MEM_OPERATION_STORE "store" | 11 | #define MEM_OPERATION_STORE 0x2 |
| 12 | |||
| 13 | static const char *mem_operation = MEM_OPERATION_LOAD; | ||
| 14 | 12 | ||
| 15 | struct perf_mem { | 13 | struct perf_mem { |
| 16 | struct perf_tool tool; | 14 | struct perf_tool tool; |
| 17 | char const *input_name; | 15 | char const *input_name; |
| 18 | bool hide_unresolved; | 16 | bool hide_unresolved; |
| 19 | bool dump_raw; | 17 | bool dump_raw; |
| 18 | int operation; | ||
| 20 | const char *cpu_list; | 19 | const char *cpu_list; |
| 21 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 20 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
| 22 | }; | 21 | }; |
| 23 | 22 | ||
| 24 | static int __cmd_record(int argc, const char **argv) | 23 | static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) |
| 25 | { | 24 | { |
| 26 | int rec_argc, i = 0, j; | 25 | int rec_argc, i = 0, j; |
| 27 | const char **rec_argv; | 26 | const char **rec_argv; |
| 28 | char event[64]; | ||
| 29 | int ret; | 27 | int ret; |
| 30 | 28 | ||
| 31 | rec_argc = argc + 4; | 29 | rec_argc = argc + 7; /* max number of arguments */ |
| 32 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 30 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
| 33 | if (!rec_argv) | 31 | if (!rec_argv) |
| 34 | return -1; | 32 | return -1; |
| 35 | 33 | ||
| 36 | rec_argv[i++] = strdup("record"); | 34 | rec_argv[i++] = "record"; |
| 37 | if (!strcmp(mem_operation, MEM_OPERATION_LOAD)) | ||
| 38 | rec_argv[i++] = strdup("-W"); | ||
| 39 | rec_argv[i++] = strdup("-d"); | ||
| 40 | rec_argv[i++] = strdup("-e"); | ||
| 41 | 35 | ||
| 42 | if (strcmp(mem_operation, MEM_OPERATION_LOAD)) | 36 | if (mem->operation & MEM_OPERATION_LOAD) |
| 43 | sprintf(event, "cpu/mem-stores/pp"); | 37 | rec_argv[i++] = "-W"; |
| 44 | else | 38 | |
| 45 | sprintf(event, "cpu/mem-loads/pp"); | 39 | rec_argv[i++] = "-d"; |
| 40 | |||
| 41 | if (mem->operation & MEM_OPERATION_LOAD) { | ||
| 42 | rec_argv[i++] = "-e"; | ||
| 43 | rec_argv[i++] = "cpu/mem-loads/pp"; | ||
| 44 | } | ||
| 45 | |||
| 46 | if (mem->operation & MEM_OPERATION_STORE) { | ||
| 47 | rec_argv[i++] = "-e"; | ||
| 48 | rec_argv[i++] = "cpu/mem-stores/pp"; | ||
| 49 | } | ||
| 46 | 50 | ||
| 47 | rec_argv[i++] = strdup(event); | ||
| 48 | for (j = 1; j < argc; j++, i++) | 51 | for (j = 1; j < argc; j++, i++) |
| 49 | rec_argv[i] = argv[j]; | 52 | rec_argv[i] = argv[j]; |
| 50 | 53 | ||
| @@ -162,17 +165,17 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem) | |||
| 162 | if (!rep_argv) | 165 | if (!rep_argv) |
| 163 | return -1; | 166 | return -1; |
| 164 | 167 | ||
| 165 | rep_argv[i++] = strdup("report"); | 168 | rep_argv[i++] = "report"; |
| 166 | rep_argv[i++] = strdup("--mem-mode"); | 169 | rep_argv[i++] = "--mem-mode"; |
| 167 | rep_argv[i++] = strdup("-n"); /* display number of samples */ | 170 | rep_argv[i++] = "-n"; /* display number of samples */ |
| 168 | 171 | ||
| 169 | /* | 172 | /* |
| 170 | * there is no weight (cost) associated with stores, so don't print | 173 | * there is no weight (cost) associated with stores, so don't print |
| 171 | * the column | 174 | * the column |
| 172 | */ | 175 | */ |
| 173 | if (strcmp(mem_operation, MEM_OPERATION_LOAD)) | 176 | if (!(mem->operation & MEM_OPERATION_LOAD)) |
| 174 | rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr," | 177 | rep_argv[i++] = "--sort=mem,sym,dso,symbol_daddr," |
| 175 | "dso_daddr,tlb,locked"); | 178 | "dso_daddr,tlb,locked"; |
| 176 | 179 | ||
| 177 | for (j = 1; j < argc; j++, i++) | 180 | for (j = 1; j < argc; j++, i++) |
| 178 | rep_argv[i] = argv[j]; | 181 | rep_argv[i] = argv[j]; |
| @@ -182,6 +185,75 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem) | |||
| 182 | return ret; | 185 | return ret; |
| 183 | } | 186 | } |
| 184 | 187 | ||
| 188 | struct mem_mode { | ||
| 189 | const char *name; | ||
| 190 | int mode; | ||
| 191 | }; | ||
| 192 | |||
| 193 | #define MEM_OPT(n, m) \ | ||
| 194 | { .name = n, .mode = (m) } | ||
| 195 | |||
| 196 | #define MEM_END { .name = NULL } | ||
| 197 | |||
| 198 | static const struct mem_mode mem_modes[]={ | ||
| 199 | MEM_OPT("load", MEM_OPERATION_LOAD), | ||
| 200 | MEM_OPT("store", MEM_OPERATION_STORE), | ||
| 201 | MEM_END | ||
| 202 | }; | ||
| 203 | |||
| 204 | static int | ||
| 205 | parse_mem_ops(const struct option *opt, const char *str, int unset) | ||
| 206 | { | ||
| 207 | int *mode = (int *)opt->value; | ||
| 208 | const struct mem_mode *m; | ||
| 209 | char *s, *os = NULL, *p; | ||
| 210 | int ret = -1; | ||
| 211 | |||
| 212 | if (unset) | ||
| 213 | return 0; | ||
| 214 | |||
| 215 | /* str may be NULL in case no arg is passed to -t */ | ||
| 216 | if (str) { | ||
| 217 | /* because str is read-only */ | ||
| 218 | s = os = strdup(str); | ||
| 219 | if (!s) | ||
| 220 | return -1; | ||
| 221 | |||
| 222 | /* reset mode */ | ||
| 223 | *mode = 0; | ||
| 224 | |||
| 225 | for (;;) { | ||
| 226 | p = strchr(s, ','); | ||
| 227 | if (p) | ||
| 228 | *p = '\0'; | ||
| 229 | |||
| 230 | for (m = mem_modes; m->name; m++) { | ||
| 231 | if (!strcasecmp(s, m->name)) | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | if (!m->name) { | ||
| 235 | fprintf(stderr, "unknown sampling op %s," | ||
| 236 | " check man page\n", s); | ||
| 237 | goto error; | ||
| 238 | } | ||
| 239 | |||
| 240 | *mode |= m->mode; | ||
| 241 | |||
| 242 | if (!p) | ||
| 243 | break; | ||
| 244 | |||
| 245 | s = p + 1; | ||
| 246 | } | ||
| 247 | } | ||
| 248 | ret = 0; | ||
| 249 | |||
| 250 | if (*mode == 0) | ||
| 251 | *mode = MEM_OPERATION_LOAD; | ||
| 252 | error: | ||
| 253 | free(os); | ||
| 254 | return ret; | ||
| 255 | } | ||
| 256 | |||
| 185 | int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | 257 | int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) |
| 186 | { | 258 | { |
| 187 | struct stat st; | 259 | struct stat st; |
| @@ -197,10 +269,15 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 197 | .ordered_events = true, | 269 | .ordered_events = true, |
| 198 | }, | 270 | }, |
| 199 | .input_name = "perf.data", | 271 | .input_name = "perf.data", |
| 272 | /* | ||
| 273 | * default to both load an store sampling | ||
| 274 | */ | ||
| 275 | .operation = MEM_OPERATION_LOAD | MEM_OPERATION_STORE, | ||
| 200 | }; | 276 | }; |
| 201 | const struct option mem_options[] = { | 277 | const struct option mem_options[] = { |
| 202 | OPT_STRING('t', "type", &mem_operation, | 278 | OPT_CALLBACK('t', "type", &mem.operation, |
| 203 | "type", "memory operations(load/store)"), | 279 | "type", "memory operations(load,store) Default load,store", |
| 280 | parse_mem_ops), | ||
| 204 | OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw, | 281 | OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw, |
| 205 | "dump raw samples in ASCII"), | 282 | "dump raw samples in ASCII"), |
| 206 | OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved, | 283 | OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved, |
| @@ -225,7 +302,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 225 | argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, | 302 | argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands, |
| 226 | mem_usage, PARSE_OPT_STOP_AT_NON_OPTION); | 303 | mem_usage, PARSE_OPT_STOP_AT_NON_OPTION); |
| 227 | 304 | ||
| 228 | if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) | 305 | if (!argc || !(strncmp(argv[0], "rec", 3) || mem.operation)) |
| 229 | usage_with_options(mem_usage, mem_options); | 306 | usage_with_options(mem_usage, mem_options); |
| 230 | 307 | ||
| 231 | if (!mem.input_name || !strlen(mem.input_name)) { | 308 | if (!mem.input_name || !strlen(mem.input_name)) { |
| @@ -236,7 +313,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 236 | } | 313 | } |
| 237 | 314 | ||
| 238 | if (!strncmp(argv[0], "rec", 3)) | 315 | if (!strncmp(argv[0], "rec", 3)) |
| 239 | return __cmd_record(argc, argv); | 316 | return __cmd_record(argc, argv, &mem); |
| 240 | else if (!strncmp(argv[0], "rep", 3)) | 317 | else if (!strncmp(argv[0], "rep", 3)) |
| 241 | return report_events(argc, argv, &mem); | 318 | return report_events(argc, argv, &mem); |
| 242 | else | 319 | else |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 8648c6d3003d..404ab3434052 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -190,16 +190,30 @@ out: | |||
| 190 | return rc; | 190 | return rc; |
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | static int process_sample_event(struct perf_tool *tool, | ||
| 194 | union perf_event *event, | ||
| 195 | struct perf_sample *sample, | ||
| 196 | struct perf_evsel *evsel, | ||
| 197 | struct machine *machine) | ||
| 198 | { | ||
| 199 | struct record *rec = container_of(tool, struct record, tool); | ||
| 200 | |||
| 201 | rec->samples++; | ||
| 202 | |||
| 203 | return build_id__mark_dso_hit(tool, event, sample, evsel, machine); | ||
| 204 | } | ||
| 205 | |||
| 193 | static int process_buildids(struct record *rec) | 206 | static int process_buildids(struct record *rec) |
| 194 | { | 207 | { |
| 195 | struct perf_data_file *file = &rec->file; | 208 | struct perf_data_file *file = &rec->file; |
| 196 | struct perf_session *session = rec->session; | 209 | struct perf_session *session = rec->session; |
| 197 | u64 start = session->header.data_offset; | ||
| 198 | 210 | ||
| 199 | u64 size = lseek(file->fd, 0, SEEK_CUR); | 211 | u64 size = lseek(perf_data_file__fd(file), 0, SEEK_CUR); |
| 200 | if (size == 0) | 212 | if (size == 0) |
| 201 | return 0; | 213 | return 0; |
| 202 | 214 | ||
| 215 | file->size = size; | ||
| 216 | |||
| 203 | /* | 217 | /* |
| 204 | * During this process, it'll load kernel map and replace the | 218 | * During this process, it'll load kernel map and replace the |
| 205 | * dso->long_name to a real pathname it found. In this case | 219 | * dso->long_name to a real pathname it found. In this case |
| @@ -211,9 +225,7 @@ static int process_buildids(struct record *rec) | |||
| 211 | */ | 225 | */ |
| 212 | symbol_conf.ignore_vmlinux_buildid = true; | 226 | symbol_conf.ignore_vmlinux_buildid = true; |
| 213 | 227 | ||
| 214 | return __perf_session__process_events(session, start, | 228 | return perf_session__process_events(session, &rec->tool); |
| 215 | size - start, | ||
| 216 | size, &build_id__mark_dso_hit_ops); | ||
| 217 | } | 229 | } |
| 218 | 230 | ||
| 219 | static void perf_event__synthesize_guest_os(struct machine *machine, void *data) | 231 | static void perf_event__synthesize_guest_os(struct machine *machine, void *data) |
| @@ -322,6 +334,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 322 | struct perf_data_file *file = &rec->file; | 334 | struct perf_data_file *file = &rec->file; |
| 323 | struct perf_session *session; | 335 | struct perf_session *session; |
| 324 | bool disabled = false, draining = false; | 336 | bool disabled = false, draining = false; |
| 337 | int fd; | ||
| 325 | 338 | ||
| 326 | rec->progname = argv[0]; | 339 | rec->progname = argv[0]; |
| 327 | 340 | ||
| @@ -336,6 +349,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 336 | return -1; | 349 | return -1; |
| 337 | } | 350 | } |
| 338 | 351 | ||
| 352 | fd = perf_data_file__fd(file); | ||
| 339 | rec->session = session; | 353 | rec->session = session; |
| 340 | 354 | ||
| 341 | record__init_features(rec); | 355 | record__init_features(rec); |
| @@ -360,12 +374,11 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 360 | perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); | 374 | perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); |
| 361 | 375 | ||
| 362 | if (file->is_pipe) { | 376 | if (file->is_pipe) { |
| 363 | err = perf_header__write_pipe(file->fd); | 377 | err = perf_header__write_pipe(fd); |
| 364 | if (err < 0) | 378 | if (err < 0) |
| 365 | goto out_child; | 379 | goto out_child; |
| 366 | } else { | 380 | } else { |
| 367 | err = perf_session__write_header(session, rec->evlist, | 381 | err = perf_session__write_header(session, rec->evlist, fd, false); |
| 368 | file->fd, false); | ||
| 369 | if (err < 0) | 382 | if (err < 0) |
| 370 | goto out_child; | 383 | goto out_child; |
| 371 | } | 384 | } |
| @@ -397,7 +410,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 397 | * return this more properly and also | 410 | * return this more properly and also |
| 398 | * propagate errors that now are calling die() | 411 | * propagate errors that now are calling die() |
| 399 | */ | 412 | */ |
| 400 | err = perf_event__synthesize_tracing_data(tool, file->fd, rec->evlist, | 413 | err = perf_event__synthesize_tracing_data(tool, fd, rec->evlist, |
| 401 | process_synthesized_event); | 414 | process_synthesized_event); |
| 402 | if (err <= 0) { | 415 | if (err <= 0) { |
| 403 | pr_err("Couldn't record tracing data.\n"); | 416 | pr_err("Couldn't record tracing data.\n"); |
| @@ -504,19 +517,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 504 | goto out_child; | 517 | goto out_child; |
| 505 | } | 518 | } |
| 506 | 519 | ||
| 507 | if (!quiet) { | 520 | if (!quiet) |
| 508 | fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); | 521 | fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); |
| 509 | 522 | ||
| 510 | /* | ||
| 511 | * Approximate RIP event size: 24 bytes. | ||
| 512 | */ | ||
| 513 | fprintf(stderr, | ||
| 514 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", | ||
| 515 | (double)rec->bytes_written / 1024.0 / 1024.0, | ||
| 516 | file->path, | ||
| 517 | rec->bytes_written / 24); | ||
| 518 | } | ||
| 519 | |||
| 520 | out_child: | 523 | out_child: |
| 521 | if (forks) { | 524 | if (forks) { |
| 522 | int exit_status; | 525 | int exit_status; |
| @@ -535,13 +538,29 @@ out_child: | |||
| 535 | } else | 538 | } else |
| 536 | status = err; | 539 | status = err; |
| 537 | 540 | ||
| 541 | /* this will be recalculated during process_buildids() */ | ||
| 542 | rec->samples = 0; | ||
| 543 | |||
| 538 | if (!err && !file->is_pipe) { | 544 | if (!err && !file->is_pipe) { |
| 539 | rec->session->header.data_size += rec->bytes_written; | 545 | rec->session->header.data_size += rec->bytes_written; |
| 540 | 546 | ||
| 541 | if (!rec->no_buildid) | 547 | if (!rec->no_buildid) |
| 542 | process_buildids(rec); | 548 | process_buildids(rec); |
| 543 | perf_session__write_header(rec->session, rec->evlist, | 549 | perf_session__write_header(rec->session, rec->evlist, fd, true); |
| 544 | file->fd, true); | 550 | } |
| 551 | |||
| 552 | if (!err && !quiet) { | ||
| 553 | char samples[128]; | ||
| 554 | |||
| 555 | if (rec->samples) | ||
| 556 | scnprintf(samples, sizeof(samples), | ||
| 557 | " (%" PRIu64 " samples)", rec->samples); | ||
| 558 | else | ||
| 559 | samples[0] = '\0'; | ||
| 560 | |||
| 561 | fprintf(stderr, "[ perf record: Captured and wrote %.3f MB %s%s ]\n", | ||
| 562 | perf_data_file__size(file) / 1024.0 / 1024.0, | ||
| 563 | file->path, samples); | ||
| 545 | } | 564 | } |
| 546 | 565 | ||
| 547 | out_delete_session: | 566 | out_delete_session: |
| @@ -720,6 +739,13 @@ static struct record record = { | |||
| 720 | .default_per_cpu = true, | 739 | .default_per_cpu = true, |
| 721 | }, | 740 | }, |
| 722 | }, | 741 | }, |
| 742 | .tool = { | ||
| 743 | .sample = process_sample_event, | ||
| 744 | .fork = perf_event__process_fork, | ||
| 745 | .comm = perf_event__process_comm, | ||
| 746 | .mmap = perf_event__process_mmap, | ||
| 747 | .mmap2 = perf_event__process_mmap2, | ||
| 748 | }, | ||
| 723 | }; | 749 | }; |
| 724 | 750 | ||
| 725 | #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: " | 751 | #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: " |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 072ae8ad67fc..2f91094e228b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -86,17 +86,6 @@ static int report__config(const char *var, const char *value, void *cb) | |||
| 86 | return perf_default_config(var, value, cb); | 86 | return perf_default_config(var, value, cb); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | static void report__inc_stats(struct report *rep, struct hist_entry *he) | ||
| 90 | { | ||
| 91 | /* | ||
| 92 | * The @he is either of a newly created one or an existing one | ||
| 93 | * merging current sample. We only want to count a new one so | ||
| 94 | * checking ->nr_events being 1. | ||
| 95 | */ | ||
| 96 | if (he->stat.nr_events == 1) | ||
| 97 | rep->nr_entries++; | ||
| 98 | } | ||
| 99 | |||
| 100 | static int hist_iter__report_callback(struct hist_entry_iter *iter, | 89 | static int hist_iter__report_callback(struct hist_entry_iter *iter, |
| 101 | struct addr_location *al, bool single, | 90 | struct addr_location *al, bool single, |
| 102 | void *arg) | 91 | void *arg) |
| @@ -108,8 +97,6 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter, | |||
| 108 | struct mem_info *mi; | 97 | struct mem_info *mi; |
| 109 | struct branch_info *bi; | 98 | struct branch_info *bi; |
| 110 | 99 | ||
| 111 | report__inc_stats(rep, he); | ||
| 112 | |||
| 113 | if (!ui__has_annotation()) | 100 | if (!ui__has_annotation()) |
| 114 | return 0; | 101 | return 0; |
| 115 | 102 | ||
| @@ -499,6 +486,9 @@ static int __cmd_report(struct report *rep) | |||
| 499 | 486 | ||
| 500 | report__warn_kptr_restrict(rep); | 487 | report__warn_kptr_restrict(rep); |
| 501 | 488 | ||
| 489 | evlist__for_each(session->evlist, pos) | ||
| 490 | rep->nr_entries += evsel__hists(pos)->nr_entries; | ||
| 491 | |||
| 502 | if (use_browser == 0) { | 492 | if (use_browser == 0) { |
| 503 | if (verbose > 3) | 493 | if (verbose > 3) |
| 504 | perf_session__fprintf(session, stdout); | 494 | perf_session__fprintf(session, stdout); |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 891086376381..e598e4e98170 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -1730,7 +1730,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1730 | "detailed run - start a lot of events"), | 1730 | "detailed run - start a lot of events"), |
| 1731 | OPT_BOOLEAN('S', "sync", &sync_run, | 1731 | OPT_BOOLEAN('S', "sync", &sync_run, |
| 1732 | "call sync() before starting a run"), | 1732 | "call sync() before starting a run"), |
| 1733 | OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, | 1733 | OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, |
| 1734 | "print large numbers with thousands\' separators", | 1734 | "print large numbers with thousands\' separators", |
| 1735 | stat__set_big_num), | 1735 | stat__set_big_num), |
| 1736 | OPT_STRING('C', "cpu", &target.cpu_list, "cpu", | 1736 | OPT_STRING('C', "cpu", &target.cpu_list, "cpu", |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 616f0fcb4701..c4c7eac69de4 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -165,7 +165,7 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip) | |||
| 165 | err ? "[unknown]" : uts.release, perf_version_string); | 165 | err ? "[unknown]" : uts.release, perf_version_string); |
| 166 | if (use_browser <= 0) | 166 | if (use_browser <= 0) |
| 167 | sleep(5); | 167 | sleep(5); |
| 168 | 168 | ||
| 169 | map->erange_warned = true; | 169 | map->erange_warned = true; |
| 170 | } | 170 | } |
| 171 | 171 | ||
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index badfabc6a01f..7e935f1083ec 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -929,66 +929,66 @@ static struct syscall_fmt { | |||
| 929 | .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, | 929 | .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, |
| 930 | { .name = "clock_gettime", .errmsg = true, STRARRAY(0, clk_id, clockid), }, | 930 | { .name = "clock_gettime", .errmsg = true, STRARRAY(0, clk_id, clockid), }, |
| 931 | { .name = "close", .errmsg = true, | 931 | { .name = "close", .errmsg = true, |
| 932 | .arg_scnprintf = { [0] = SCA_CLOSE_FD, /* fd */ }, }, | 932 | .arg_scnprintf = { [0] = SCA_CLOSE_FD, /* fd */ }, }, |
| 933 | { .name = "connect", .errmsg = true, }, | 933 | { .name = "connect", .errmsg = true, }, |
| 934 | { .name = "dup", .errmsg = true, | 934 | { .name = "dup", .errmsg = true, |
| 935 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 935 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 936 | { .name = "dup2", .errmsg = true, | 936 | { .name = "dup2", .errmsg = true, |
| 937 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 937 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 938 | { .name = "dup3", .errmsg = true, | 938 | { .name = "dup3", .errmsg = true, |
| 939 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 939 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 940 | { .name = "epoll_ctl", .errmsg = true, STRARRAY(1, op, epoll_ctl_ops), }, | 940 | { .name = "epoll_ctl", .errmsg = true, STRARRAY(1, op, epoll_ctl_ops), }, |
| 941 | { .name = "eventfd2", .errmsg = true, | 941 | { .name = "eventfd2", .errmsg = true, |
| 942 | .arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, }, | 942 | .arg_scnprintf = { [1] = SCA_EFD_FLAGS, /* flags */ }, }, |
| 943 | { .name = "faccessat", .errmsg = true, | 943 | { .name = "faccessat", .errmsg = true, |
| 944 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, | 944 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, |
| 945 | { .name = "fadvise64", .errmsg = true, | 945 | { .name = "fadvise64", .errmsg = true, |
| 946 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 946 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 947 | { .name = "fallocate", .errmsg = true, | 947 | { .name = "fallocate", .errmsg = true, |
| 948 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 948 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 949 | { .name = "fchdir", .errmsg = true, | 949 | { .name = "fchdir", .errmsg = true, |
| 950 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 950 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 951 | { .name = "fchmod", .errmsg = true, | 951 | { .name = "fchmod", .errmsg = true, |
| 952 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 952 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 953 | { .name = "fchmodat", .errmsg = true, | 953 | { .name = "fchmodat", .errmsg = true, |
| 954 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, | 954 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, |
| 955 | { .name = "fchown", .errmsg = true, | 955 | { .name = "fchown", .errmsg = true, |
| 956 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 956 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 957 | { .name = "fchownat", .errmsg = true, | 957 | { .name = "fchownat", .errmsg = true, |
| 958 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, | 958 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, |
| 959 | { .name = "fcntl", .errmsg = true, | 959 | { .name = "fcntl", .errmsg = true, |
| 960 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ | 960 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ |
| 961 | [1] = SCA_STRARRAY, /* cmd */ }, | 961 | [1] = SCA_STRARRAY, /* cmd */ }, |
| 962 | .arg_parm = { [1] = &strarray__fcntl_cmds, /* cmd */ }, }, | 962 | .arg_parm = { [1] = &strarray__fcntl_cmds, /* cmd */ }, }, |
| 963 | { .name = "fdatasync", .errmsg = true, | 963 | { .name = "fdatasync", .errmsg = true, |
| 964 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 964 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 965 | { .name = "flock", .errmsg = true, | 965 | { .name = "flock", .errmsg = true, |
| 966 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ | 966 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ |
| 967 | [1] = SCA_FLOCK, /* cmd */ }, }, | 967 | [1] = SCA_FLOCK, /* cmd */ }, }, |
| 968 | { .name = "fsetxattr", .errmsg = true, | 968 | { .name = "fsetxattr", .errmsg = true, |
| 969 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 969 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 970 | { .name = "fstat", .errmsg = true, .alias = "newfstat", | 970 | { .name = "fstat", .errmsg = true, .alias = "newfstat", |
| 971 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 971 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 972 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", | 972 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", |
| 973 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, | 973 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, |
| 974 | { .name = "fstatfs", .errmsg = true, | 974 | { .name = "fstatfs", .errmsg = true, |
| 975 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 975 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 976 | { .name = "fsync", .errmsg = true, | 976 | { .name = "fsync", .errmsg = true, |
| 977 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 977 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 978 | { .name = "ftruncate", .errmsg = true, | 978 | { .name = "ftruncate", .errmsg = true, |
| 979 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 979 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 980 | { .name = "futex", .errmsg = true, | 980 | { .name = "futex", .errmsg = true, |
| 981 | .arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, }, | 981 | .arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, }, |
| 982 | { .name = "futimesat", .errmsg = true, | 982 | { .name = "futimesat", .errmsg = true, |
| 983 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, | 983 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, |
| 984 | { .name = "getdents", .errmsg = true, | 984 | { .name = "getdents", .errmsg = true, |
| 985 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 985 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 986 | { .name = "getdents64", .errmsg = true, | 986 | { .name = "getdents64", .errmsg = true, |
| 987 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 987 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 988 | { .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), }, | 988 | { .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), }, |
| 989 | { .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, | 989 | { .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, |
| 990 | { .name = "ioctl", .errmsg = true, | 990 | { .name = "ioctl", .errmsg = true, |
| 991 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ | 991 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ |
| 992 | #if defined(__i386__) || defined(__x86_64__) | 992 | #if defined(__i386__) || defined(__x86_64__) |
| 993 | /* | 993 | /* |
| 994 | * FIXME: Make this available to all arches. | 994 | * FIXME: Make this available to all arches. |
| @@ -1002,7 +1002,7 @@ static struct syscall_fmt { | |||
| 1002 | { .name = "kill", .errmsg = true, | 1002 | { .name = "kill", .errmsg = true, |
| 1003 | .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, | 1003 | .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, |
| 1004 | { .name = "linkat", .errmsg = true, | 1004 | { .name = "linkat", .errmsg = true, |
| 1005 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, | 1005 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, |
| 1006 | { .name = "lseek", .errmsg = true, | 1006 | { .name = "lseek", .errmsg = true, |
| 1007 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ | 1007 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ |
| 1008 | [2] = SCA_STRARRAY, /* whence */ }, | 1008 | [2] = SCA_STRARRAY, /* whence */ }, |
| @@ -1012,9 +1012,9 @@ static struct syscall_fmt { | |||
| 1012 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | 1012 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ |
| 1013 | [2] = SCA_MADV_BHV, /* behavior */ }, }, | 1013 | [2] = SCA_MADV_BHV, /* behavior */ }, }, |
| 1014 | { .name = "mkdirat", .errmsg = true, | 1014 | { .name = "mkdirat", .errmsg = true, |
| 1015 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, | 1015 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, |
| 1016 | { .name = "mknodat", .errmsg = true, | 1016 | { .name = "mknodat", .errmsg = true, |
| 1017 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, | 1017 | .arg_scnprintf = { [0] = SCA_FDAT, /* fd */ }, }, |
| 1018 | { .name = "mlock", .errmsg = true, | 1018 | { .name = "mlock", .errmsg = true, |
| 1019 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, | 1019 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, |
| 1020 | { .name = "mlockall", .errmsg = true, | 1020 | { .name = "mlockall", .errmsg = true, |
| @@ -1036,9 +1036,9 @@ static struct syscall_fmt { | |||
| 1036 | { .name = "munmap", .errmsg = true, | 1036 | { .name = "munmap", .errmsg = true, |
| 1037 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, | 1037 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, |
| 1038 | { .name = "name_to_handle_at", .errmsg = true, | 1038 | { .name = "name_to_handle_at", .errmsg = true, |
| 1039 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, | 1039 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, |
| 1040 | { .name = "newfstatat", .errmsg = true, | 1040 | { .name = "newfstatat", .errmsg = true, |
| 1041 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, | 1041 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, |
| 1042 | { .name = "open", .errmsg = true, | 1042 | { .name = "open", .errmsg = true, |
| 1043 | .arg_scnprintf = { [1] = SCA_OPEN_FLAGS, /* flags */ }, }, | 1043 | .arg_scnprintf = { [1] = SCA_OPEN_FLAGS, /* flags */ }, }, |
| 1044 | { .name = "open_by_handle_at", .errmsg = true, | 1044 | { .name = "open_by_handle_at", .errmsg = true, |
| @@ -1052,20 +1052,20 @@ static struct syscall_fmt { | |||
| 1052 | { .name = "poll", .errmsg = true, .timeout = true, }, | 1052 | { .name = "poll", .errmsg = true, .timeout = true, }, |
| 1053 | { .name = "ppoll", .errmsg = true, .timeout = true, }, | 1053 | { .name = "ppoll", .errmsg = true, .timeout = true, }, |
| 1054 | { .name = "pread", .errmsg = true, .alias = "pread64", | 1054 | { .name = "pread", .errmsg = true, .alias = "pread64", |
| 1055 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1055 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1056 | { .name = "preadv", .errmsg = true, .alias = "pread", | 1056 | { .name = "preadv", .errmsg = true, .alias = "pread", |
| 1057 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1057 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1058 | { .name = "prlimit64", .errmsg = true, STRARRAY(1, resource, rlimit_resources), }, | 1058 | { .name = "prlimit64", .errmsg = true, STRARRAY(1, resource, rlimit_resources), }, |
| 1059 | { .name = "pwrite", .errmsg = true, .alias = "pwrite64", | 1059 | { .name = "pwrite", .errmsg = true, .alias = "pwrite64", |
| 1060 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1060 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1061 | { .name = "pwritev", .errmsg = true, | 1061 | { .name = "pwritev", .errmsg = true, |
| 1062 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1062 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1063 | { .name = "read", .errmsg = true, | 1063 | { .name = "read", .errmsg = true, |
| 1064 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1064 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1065 | { .name = "readlinkat", .errmsg = true, | 1065 | { .name = "readlinkat", .errmsg = true, |
| 1066 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, | 1066 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, |
| 1067 | { .name = "readv", .errmsg = true, | 1067 | { .name = "readv", .errmsg = true, |
| 1068 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1068 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1069 | { .name = "recvfrom", .errmsg = true, | 1069 | { .name = "recvfrom", .errmsg = true, |
| 1070 | .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, | 1070 | .arg_scnprintf = { [3] = SCA_MSG_FLAGS, /* flags */ }, }, |
| 1071 | { .name = "recvmmsg", .errmsg = true, | 1071 | { .name = "recvmmsg", .errmsg = true, |
| @@ -1073,7 +1073,7 @@ static struct syscall_fmt { | |||
| 1073 | { .name = "recvmsg", .errmsg = true, | 1073 | { .name = "recvmsg", .errmsg = true, |
| 1074 | .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, | 1074 | .arg_scnprintf = { [2] = SCA_MSG_FLAGS, /* flags */ }, }, |
| 1075 | { .name = "renameat", .errmsg = true, | 1075 | { .name = "renameat", .errmsg = true, |
| 1076 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, | 1076 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, |
| 1077 | { .name = "rt_sigaction", .errmsg = true, | 1077 | { .name = "rt_sigaction", .errmsg = true, |
| 1078 | .arg_scnprintf = { [0] = SCA_SIGNUM, /* sig */ }, }, | 1078 | .arg_scnprintf = { [0] = SCA_SIGNUM, /* sig */ }, }, |
| 1079 | { .name = "rt_sigprocmask", .errmsg = true, STRARRAY(0, how, sighow), }, | 1079 | { .name = "rt_sigprocmask", .errmsg = true, STRARRAY(0, how, sighow), }, |
| @@ -1091,7 +1091,7 @@ static struct syscall_fmt { | |||
| 1091 | { .name = "setitimer", .errmsg = true, STRARRAY(0, which, itimers), }, | 1091 | { .name = "setitimer", .errmsg = true, STRARRAY(0, which, itimers), }, |
| 1092 | { .name = "setrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, | 1092 | { .name = "setrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, |
| 1093 | { .name = "shutdown", .errmsg = true, | 1093 | { .name = "shutdown", .errmsg = true, |
| 1094 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1094 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1095 | { .name = "socket", .errmsg = true, | 1095 | { .name = "socket", .errmsg = true, |
| 1096 | .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ | 1096 | .arg_scnprintf = { [0] = SCA_STRARRAY, /* family */ |
| 1097 | [1] = SCA_SK_TYPE, /* type */ }, | 1097 | [1] = SCA_SK_TYPE, /* type */ }, |
| @@ -1102,7 +1102,7 @@ static struct syscall_fmt { | |||
| 1102 | .arg_parm = { [0] = &strarray__socket_families, /* family */ }, }, | 1102 | .arg_parm = { [0] = &strarray__socket_families, /* family */ }, }, |
| 1103 | { .name = "stat", .errmsg = true, .alias = "newstat", }, | 1103 | { .name = "stat", .errmsg = true, .alias = "newstat", }, |
| 1104 | { .name = "symlinkat", .errmsg = true, | 1104 | { .name = "symlinkat", .errmsg = true, |
| 1105 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, | 1105 | .arg_scnprintf = { [0] = SCA_FDAT, /* dfd */ }, }, |
| 1106 | { .name = "tgkill", .errmsg = true, | 1106 | { .name = "tgkill", .errmsg = true, |
| 1107 | .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, | 1107 | .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, |
| 1108 | { .name = "tkill", .errmsg = true, | 1108 | { .name = "tkill", .errmsg = true, |
| @@ -1113,9 +1113,9 @@ static struct syscall_fmt { | |||
| 1113 | { .name = "utimensat", .errmsg = true, | 1113 | { .name = "utimensat", .errmsg = true, |
| 1114 | .arg_scnprintf = { [0] = SCA_FDAT, /* dirfd */ }, }, | 1114 | .arg_scnprintf = { [0] = SCA_FDAT, /* dirfd */ }, }, |
| 1115 | { .name = "write", .errmsg = true, | 1115 | { .name = "write", .errmsg = true, |
| 1116 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1116 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1117 | { .name = "writev", .errmsg = true, | 1117 | { .name = "writev", .errmsg = true, |
| 1118 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1118 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
| 1119 | }; | 1119 | }; |
| 1120 | 1120 | ||
| 1121 | static int syscall_fmt__cmp(const void *name, const void *fmtp) | 1121 | static int syscall_fmt__cmp(const void *name, const void *fmtp) |
| @@ -1191,7 +1191,7 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) | |||
| 1191 | 1191 | ||
| 1192 | if (thread__priv(thread) == NULL) | 1192 | if (thread__priv(thread) == NULL) |
| 1193 | thread__set_priv(thread, thread_trace__new()); | 1193 | thread__set_priv(thread, thread_trace__new()); |
| 1194 | 1194 | ||
| 1195 | if (thread__priv(thread) == NULL) | 1195 | if (thread__priv(thread) == NULL) |
| 1196 | goto fail; | 1196 | goto fail; |
| 1197 | 1197 | ||
| @@ -2056,23 +2056,24 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
| 2056 | if (trace->trace_syscalls && | 2056 | if (trace->trace_syscalls && |
| 2057 | perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, | 2057 | perf_evlist__add_syscall_newtp(evlist, trace__sys_enter, |
| 2058 | trace__sys_exit)) | 2058 | trace__sys_exit)) |
| 2059 | goto out_error_tp; | 2059 | goto out_error_raw_syscalls; |
| 2060 | 2060 | ||
| 2061 | if (trace->trace_syscalls) | 2061 | if (trace->trace_syscalls) |
| 2062 | perf_evlist__add_vfs_getname(evlist); | 2062 | perf_evlist__add_vfs_getname(evlist); |
| 2063 | 2063 | ||
| 2064 | if ((trace->trace_pgfaults & TRACE_PFMAJ) && | 2064 | if ((trace->trace_pgfaults & TRACE_PFMAJ) && |
| 2065 | perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MAJ)) | 2065 | perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MAJ)) { |
| 2066 | goto out_error_tp; | 2066 | goto out_error_mem; |
| 2067 | } | ||
| 2067 | 2068 | ||
| 2068 | if ((trace->trace_pgfaults & TRACE_PFMIN) && | 2069 | if ((trace->trace_pgfaults & TRACE_PFMIN) && |
| 2069 | perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MIN)) | 2070 | perf_evlist__add_pgfault(evlist, PERF_COUNT_SW_PAGE_FAULTS_MIN)) |
| 2070 | goto out_error_tp; | 2071 | goto out_error_mem; |
| 2071 | 2072 | ||
| 2072 | if (trace->sched && | 2073 | if (trace->sched && |
| 2073 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | 2074 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", |
| 2074 | trace__sched_stat_runtime)) | 2075 | trace__sched_stat_runtime)) |
| 2075 | goto out_error_tp; | 2076 | goto out_error_sched_stat_runtime; |
| 2076 | 2077 | ||
| 2077 | err = perf_evlist__create_maps(evlist, &trace->opts.target); | 2078 | err = perf_evlist__create_maps(evlist, &trace->opts.target); |
| 2078 | if (err < 0) { | 2079 | if (err < 0) { |
| @@ -2202,8 +2203,12 @@ out: | |||
| 2202 | { | 2203 | { |
| 2203 | char errbuf[BUFSIZ]; | 2204 | char errbuf[BUFSIZ]; |
| 2204 | 2205 | ||
| 2205 | out_error_tp: | 2206 | out_error_sched_stat_runtime: |
| 2206 | perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf)); | 2207 | debugfs__strerror_open_tp(errno, errbuf, sizeof(errbuf), "sched", "sched_stat_runtime"); |
| 2208 | goto out_error; | ||
| 2209 | |||
| 2210 | out_error_raw_syscalls: | ||
| 2211 | debugfs__strerror_open_tp(errno, errbuf, sizeof(errbuf), "raw_syscalls", "sys_(enter|exit)"); | ||
| 2207 | goto out_error; | 2212 | goto out_error; |
| 2208 | 2213 | ||
| 2209 | out_error_mmap: | 2214 | out_error_mmap: |
| @@ -2217,6 +2222,9 @@ out_error: | |||
| 2217 | fprintf(trace->output, "%s\n", errbuf); | 2222 | fprintf(trace->output, "%s\n", errbuf); |
| 2218 | goto out_delete_evlist; | 2223 | goto out_delete_evlist; |
| 2219 | } | 2224 | } |
| 2225 | out_error_mem: | ||
| 2226 | fprintf(trace->output, "Not enough memory to run!\n"); | ||
| 2227 | goto out_delete_evlist; | ||
| 2220 | } | 2228 | } |
| 2221 | 2229 | ||
| 2222 | static int trace__replay(struct trace *trace) | 2230 | static int trace__replay(struct trace *trace) |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 648e31ff4021..cc224080b525 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
| @@ -198,6 +198,7 @@ CORE_FEATURE_TESTS = \ | |||
| 198 | libpython-version \ | 198 | libpython-version \ |
| 199 | libslang \ | 199 | libslang \ |
| 200 | libunwind \ | 200 | libunwind \ |
| 201 | pthread-attr-setaffinity-np \ | ||
| 201 | stackprotector-all \ | 202 | stackprotector-all \ |
| 202 | timerfd \ | 203 | timerfd \ |
| 203 | libdw-dwarf-unwind \ | 204 | libdw-dwarf-unwind \ |
| @@ -226,6 +227,7 @@ VF_FEATURE_TESTS = \ | |||
| 226 | libelf-getphdrnum \ | 227 | libelf-getphdrnum \ |
| 227 | libelf-mmap \ | 228 | libelf-mmap \ |
| 228 | libpython-version \ | 229 | libpython-version \ |
| 230 | pthread-attr-setaffinity-np \ | ||
| 229 | stackprotector-all \ | 231 | stackprotector-all \ |
| 230 | timerfd \ | 232 | timerfd \ |
| 231 | libunwind-debug-frame \ | 233 | libunwind-debug-frame \ |
| @@ -301,6 +303,10 @@ ifeq ($(feature-sync-compare-and-swap), 1) | |||
| 301 | CFLAGS += -DHAVE_SYNC_COMPARE_AND_SWAP_SUPPORT | 303 | CFLAGS += -DHAVE_SYNC_COMPARE_AND_SWAP_SUPPORT |
| 302 | endif | 304 | endif |
| 303 | 305 | ||
| 306 | ifeq ($(feature-pthread-attr-setaffinity-np), 1) | ||
| 307 | CFLAGS += -DHAVE_PTHREAD_ATTR_SETAFFINITY_NP | ||
| 308 | endif | ||
| 309 | |||
| 304 | ifndef NO_BIONIC | 310 | ifndef NO_BIONIC |
| 305 | $(call feature_check,bionic) | 311 | $(call feature_check,bionic) |
| 306 | ifeq ($(feature-bionic), 1) | 312 | ifeq ($(feature-bionic), 1) |
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 53f19b5dbc37..42ac05aaf8ac 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile | |||
| @@ -25,6 +25,7 @@ FILES= \ | |||
| 25 | test-libslang.bin \ | 25 | test-libslang.bin \ |
| 26 | test-libunwind.bin \ | 26 | test-libunwind.bin \ |
| 27 | test-libunwind-debug-frame.bin \ | 27 | test-libunwind-debug-frame.bin \ |
| 28 | test-pthread-attr-setaffinity-np.bin \ | ||
| 28 | test-stackprotector-all.bin \ | 29 | test-stackprotector-all.bin \ |
| 29 | test-timerfd.bin \ | 30 | test-timerfd.bin \ |
| 30 | test-libdw-dwarf-unwind.bin \ | 31 | test-libdw-dwarf-unwind.bin \ |
| @@ -47,6 +48,9 @@ test-all.bin: | |||
| 47 | test-hello.bin: | 48 | test-hello.bin: |
| 48 | $(BUILD) | 49 | $(BUILD) |
| 49 | 50 | ||
| 51 | test-pthread-attr-setaffinity-np.bin: | ||
| 52 | $(BUILD) -Werror -lpthread | ||
| 53 | |||
| 50 | test-stackprotector-all.bin: | 54 | test-stackprotector-all.bin: |
| 51 | $(BUILD) -Werror -fstack-protector-all | 55 | $(BUILD) -Werror -fstack-protector-all |
| 52 | 56 | ||
diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index 652e0098eba6..6d4d09323922 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c | |||
| @@ -97,6 +97,10 @@ | |||
| 97 | # include "test-zlib.c" | 97 | # include "test-zlib.c" |
| 98 | #undef main | 98 | #undef main |
| 99 | 99 | ||
| 100 | #define main main_test_pthread_attr_setaffinity_np | ||
| 101 | # include "test-pthread_attr_setaffinity_np.c" | ||
| 102 | #undef main | ||
| 103 | |||
| 100 | int main(int argc, char *argv[]) | 104 | int main(int argc, char *argv[]) |
| 101 | { | 105 | { |
| 102 | main_test_libpython(); | 106 | main_test_libpython(); |
| @@ -121,6 +125,7 @@ int main(int argc, char *argv[]) | |||
| 121 | main_test_libdw_dwarf_unwind(); | 125 | main_test_libdw_dwarf_unwind(); |
| 122 | main_test_sync_compare_and_swap(argc, argv); | 126 | main_test_sync_compare_and_swap(argc, argv); |
| 123 | main_test_zlib(); | 127 | main_test_zlib(); |
| 128 | main_test_pthread_attr_setaffinity_np(); | ||
| 124 | 129 | ||
| 125 | return 0; | 130 | return 0; |
| 126 | } | 131 | } |
diff --git a/tools/perf/config/feature-checks/test-pthread-attr-setaffinity-np.c b/tools/perf/config/feature-checks/test-pthread-attr-setaffinity-np.c new file mode 100644 index 000000000000..0a0d3ecb4e8a --- /dev/null +++ b/tools/perf/config/feature-checks/test-pthread-attr-setaffinity-np.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | #include <stdint.h> | ||
| 2 | #include <pthread.h> | ||
| 3 | |||
| 4 | int main(void) | ||
| 5 | { | ||
| 6 | int ret = 0; | ||
| 7 | pthread_attr_t thread_attr; | ||
| 8 | |||
| 9 | pthread_attr_init(&thread_attr); | ||
| 10 | /* don't care abt exact args, just the API itself in libpthread */ | ||
| 11 | ret = pthread_attr_setaffinity_np(&thread_attr, 0, NULL); | ||
| 12 | |||
| 13 | return ret; | ||
| 14 | } | ||
diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/attr.py index c9b4b6269b51..1091bd47adfd 100644 --- a/tools/perf/tests/attr.py +++ b/tools/perf/tests/attr.py | |||
| @@ -104,7 +104,6 @@ class Event(dict): | |||
| 104 | continue | 104 | continue |
| 105 | if not self.compare_data(self[t], other[t]): | 105 | if not self.compare_data(self[t], other[t]): |
| 106 | log.warning("expected %s=%s, got %s" % (t, self[t], other[t])) | 106 | log.warning("expected %s=%s, got %s" % (t, self[t], other[t])) |
| 107 | |||
| 108 | 107 | ||
| 109 | # Test file description needs to have following sections: | 108 | # Test file description needs to have following sections: |
| 110 | # [config] | 109 | # [config] |
diff --git a/tools/perf/tests/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 8d110dec393e..18619966454c 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c | |||
| @@ -140,7 +140,7 @@ static void del_hist_entries(struct hists *hists) | |||
| 140 | he = rb_entry(node, struct hist_entry, rb_node); | 140 | he = rb_entry(node, struct hist_entry, rb_node); |
| 141 | rb_erase(node, root_out); | 141 | rb_erase(node, root_out); |
| 142 | rb_erase(&he->rb_node_in, root_in); | 142 | rb_erase(&he->rb_node_in, root_in); |
| 143 | hist_entry__free(he); | 143 | hist_entry__delete(he); |
| 144 | } | 144 | } |
| 145 | } | 145 | } |
| 146 | 146 | ||
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index f5547610da02..b52c9faea224 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c | |||
| @@ -106,7 +106,7 @@ static void del_hist_entries(struct hists *hists) | |||
| 106 | he = rb_entry(node, struct hist_entry, rb_node); | 106 | he = rb_entry(node, struct hist_entry, rb_node); |
| 107 | rb_erase(node, root_out); | 107 | rb_erase(node, root_out); |
| 108 | rb_erase(&he->rb_node_in, root_in); | 108 | rb_erase(&he->rb_node_in, root_in); |
| 109 | hist_entry__free(he); | 109 | hist_entry__delete(he); |
| 110 | } | 110 | } |
| 111 | } | 111 | } |
| 112 | 112 | ||
diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 69a71ff84e01..75709d2b17b4 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make | |||
| @@ -222,7 +222,6 @@ tarpkg: | |||
| 222 | @cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \ | 222 | @cmd="$(PERF)/tests/perf-targz-src-pkg $(PERF)"; \ |
| 223 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ | 223 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ |
| 224 | ( eval $$cmd ) >> $@ 2>&1 | 224 | ( eval $$cmd ) >> $@ 2>&1 |
| 225 | |||
| 226 | 225 | ||
| 227 | all: $(run) $(run_O) tarpkg | 226 | all: $(run) $(run_O) tarpkg |
| 228 | @echo OK | 227 | @echo OK |
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 7f2f51f93619..1cdab0ce00e2 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
| @@ -1145,6 +1145,49 @@ static int test__pinned_group(struct perf_evlist *evlist) | |||
| 1145 | return 0; | 1145 | return 0; |
| 1146 | } | 1146 | } |
| 1147 | 1147 | ||
| 1148 | static int test__checkevent_breakpoint_len(struct perf_evlist *evlist) | ||
| 1149 | { | ||
| 1150 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
| 1151 | |||
| 1152 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 1153 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
| 1154 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
| 1155 | TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == | ||
| 1156 | evsel->attr.bp_type); | ||
| 1157 | TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_1 == | ||
| 1158 | evsel->attr.bp_len); | ||
| 1159 | |||
| 1160 | return 0; | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | static int test__checkevent_breakpoint_len_w(struct perf_evlist *evlist) | ||
| 1164 | { | ||
| 1165 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
| 1166 | |||
| 1167 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
| 1168 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); | ||
| 1169 | TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); | ||
| 1170 | TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_W == | ||
| 1171 | evsel->attr.bp_type); | ||
| 1172 | TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_2 == | ||
| 1173 | evsel->attr.bp_len); | ||
| 1174 | |||
| 1175 | return 0; | ||
| 1176 | } | ||
| 1177 | |||
| 1178 | static int | ||
| 1179 | test__checkevent_breakpoint_len_rw_modifier(struct perf_evlist *evlist) | ||
| 1180 | { | ||
| 1181 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
| 1182 | |||
| 1183 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
| 1184 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
| 1185 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 1186 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 1187 | |||
| 1188 | return test__checkevent_breakpoint_rw(evlist); | ||
| 1189 | } | ||
| 1190 | |||
| 1148 | static int count_tracepoints(void) | 1191 | static int count_tracepoints(void) |
| 1149 | { | 1192 | { |
| 1150 | char events_path[PATH_MAX]; | 1193 | char events_path[PATH_MAX]; |
| @@ -1420,6 +1463,21 @@ static struct evlist_test test__events[] = { | |||
| 1420 | .check = test__pinned_group, | 1463 | .check = test__pinned_group, |
| 1421 | .id = 41, | 1464 | .id = 41, |
| 1422 | }, | 1465 | }, |
| 1466 | { | ||
| 1467 | .name = "mem:0/1", | ||
| 1468 | .check = test__checkevent_breakpoint_len, | ||
| 1469 | .id = 42, | ||
| 1470 | }, | ||
| 1471 | { | ||
| 1472 | .name = "mem:0/2:w", | ||
| 1473 | .check = test__checkevent_breakpoint_len_w, | ||
| 1474 | .id = 43, | ||
| 1475 | }, | ||
| 1476 | { | ||
| 1477 | .name = "mem:0/4:rw:u", | ||
| 1478 | .check = test__checkevent_breakpoint_len_rw_modifier, | ||
| 1479 | .id = 44 | ||
| 1480 | }, | ||
| 1423 | #if defined(__s390x__) | 1481 | #if defined(__s390x__) |
| 1424 | { | 1482 | { |
| 1425 | .name = "kvm-s390:kvm_s390_create_vm", | 1483 | .name = "kvm-s390:kvm_s390_create_vm", |
| @@ -1471,7 +1529,7 @@ static int test_event(struct evlist_test *e) | |||
| 1471 | } else { | 1529 | } else { |
| 1472 | ret = e->check(evlist); | 1530 | ret = e->check(evlist); |
| 1473 | } | 1531 | } |
| 1474 | 1532 | ||
| 1475 | perf_evlist__delete(evlist); | 1533 | perf_evlist__delete(evlist); |
| 1476 | 1534 | ||
| 1477 | return ret; | 1535 | return ret; |
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 4908c648a597..30c02181e78b 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c | |||
| @@ -110,7 +110,7 @@ static bool samples_same(const struct perf_sample *s1, | |||
| 110 | 110 | ||
| 111 | if (type & PERF_SAMPLE_STACK_USER) { | 111 | if (type & PERF_SAMPLE_STACK_USER) { |
| 112 | COMP(user_stack.size); | 112 | COMP(user_stack.size); |
| 113 | if (memcmp(s1->user_stack.data, s1->user_stack.data, | 113 | if (memcmp(s1->user_stack.data, s2->user_stack.data, |
| 114 | s1->user_stack.size)) { | 114 | s1->user_stack.size)) { |
| 115 | pr_debug("Samples differ at 'user_stack'\n"); | 115 | pr_debug("Samples differ at 'user_stack'\n"); |
| 116 | return false; | 116 | return false; |
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 1e0a2fd80115..9d32e3c0cfee 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
| @@ -517,7 +517,7 @@ static bool annotate_browser__jump(struct annotate_browser *browser) | |||
| 517 | } | 517 | } |
| 518 | 518 | ||
| 519 | annotate_browser__set_top(browser, dl, idx); | 519 | annotate_browser__set_top(browser, dl, idx); |
| 520 | 520 | ||
| 521 | return true; | 521 | return true; |
| 522 | } | 522 | } |
| 523 | 523 | ||
| @@ -867,7 +867,6 @@ static void annotate_browser__mark_jump_targets(struct annotate_browser *browser | |||
| 867 | 867 | ||
| 868 | ++browser->nr_jumps; | 868 | ++browser->nr_jumps; |
| 869 | } | 869 | } |
| 870 | |||
| 871 | } | 870 | } |
| 872 | 871 | ||
| 873 | static inline int width_jumps(int n) | 872 | static inline int width_jumps(int n) |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 482adae3cc44..25d608394d74 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
| @@ -285,7 +285,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ | |||
| 285 | } | 285 | } |
| 286 | 286 | ||
| 287 | #define __HPP_SORT_FN(_type, _field) \ | 287 | #define __HPP_SORT_FN(_type, _field) \ |
| 288 | static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ | 288 | static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
| 289 | struct hist_entry *a, struct hist_entry *b) \ | ||
| 289 | { \ | 290 | { \ |
| 290 | return __hpp__sort(a, b, he_get_##_field); \ | 291 | return __hpp__sort(a, b, he_get_##_field); \ |
| 291 | } | 292 | } |
| @@ -312,7 +313,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ | |||
| 312 | } | 313 | } |
| 313 | 314 | ||
| 314 | #define __HPP_SORT_ACC_FN(_type, _field) \ | 315 | #define __HPP_SORT_ACC_FN(_type, _field) \ |
| 315 | static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ | 316 | static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
| 317 | struct hist_entry *a, struct hist_entry *b) \ | ||
| 316 | { \ | 318 | { \ |
| 317 | return __hpp__sort_acc(a, b, he_get_acc_##_field); \ | 319 | return __hpp__sort_acc(a, b, he_get_acc_##_field); \ |
| 318 | } | 320 | } |
| @@ -331,7 +333,8 @@ static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ | |||
| 331 | } | 333 | } |
| 332 | 334 | ||
| 333 | #define __HPP_SORT_RAW_FN(_type, _field) \ | 335 | #define __HPP_SORT_RAW_FN(_type, _field) \ |
| 334 | static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ | 336 | static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
| 337 | struct hist_entry *a, struct hist_entry *b) \ | ||
| 335 | { \ | 338 | { \ |
| 336 | return __hpp__sort(a, b, he_get_raw_##_field); \ | 339 | return __hpp__sort(a, b, he_get_raw_##_field); \ |
| 337 | } | 340 | } |
| @@ -361,7 +364,8 @@ HPP_PERCENT_ACC_FNS(overhead_acc, period) | |||
| 361 | HPP_RAW_FNS(samples, nr_events) | 364 | HPP_RAW_FNS(samples, nr_events) |
| 362 | HPP_RAW_FNS(period, period) | 365 | HPP_RAW_FNS(period, period) |
| 363 | 366 | ||
| 364 | static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, | 367 | static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, |
| 368 | struct hist_entry *a __maybe_unused, | ||
| 365 | struct hist_entry *b __maybe_unused) | 369 | struct hist_entry *b __maybe_unused) |
| 366 | { | 370 | { |
| 367 | return 0; | 371 | return 0; |
diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index f34f89eb607c..717d39d3052b 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h | |||
| @@ -4,12 +4,12 @@ | |||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | 5 | ||
| 6 | void ui_progress__finish(void); | 6 | void ui_progress__finish(void); |
| 7 | 7 | ||
| 8 | struct ui_progress { | 8 | struct ui_progress { |
| 9 | const char *title; | 9 | const char *title; |
| 10 | u64 curr, next, step, total; | 10 | u64 curr, next, step, total; |
| 11 | }; | 11 | }; |
| 12 | 12 | ||
| 13 | void ui_progress__init(struct ui_progress *p, u64 total, const char *title); | 13 | void ui_progress__init(struct ui_progress *p, u64 total, const char *title); |
| 14 | void ui_progress__update(struct ui_progress *p, u64 adv); | 14 | void ui_progress__update(struct ui_progress *p, u64 adv); |
| 15 | 15 | ||
diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c index 1c8b9afd5d6e..88f5143a5981 100644 --- a/tools/perf/ui/tui/helpline.c +++ b/tools/perf/ui/tui/helpline.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "../libslang.h" | 9 | #include "../libslang.h" |
| 10 | 10 | ||
| 11 | char ui_helpline__last_msg[1024]; | 11 | char ui_helpline__last_msg[1024]; |
| 12 | bool tui_helpline__set; | ||
| 12 | 13 | ||
| 13 | static void tui_helpline__pop(void) | 14 | static void tui_helpline__pop(void) |
| 14 | { | 15 | { |
| @@ -35,6 +36,8 @@ static int tui_helpline__show(const char *format, va_list ap) | |||
| 35 | sizeof(ui_helpline__last_msg) - backlog, format, ap); | 36 | sizeof(ui_helpline__last_msg) - backlog, format, ap); |
| 36 | backlog += ret; | 37 | backlog += ret; |
| 37 | 38 | ||
| 39 | tui_helpline__set = true; | ||
| 40 | |||
| 38 | if (ui_helpline__last_msg[backlog - 1] == '\n') { | 41 | if (ui_helpline__last_msg[backlog - 1] == '\n') { |
| 39 | ui_helpline__puts(ui_helpline__last_msg); | 42 | ui_helpline__puts(ui_helpline__last_msg); |
| 40 | SLsmg_refresh(); | 43 | SLsmg_refresh(); |
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 3c38f25b1695..b77e1d771363 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | static volatile int ui__need_resize; | 17 | static volatile int ui__need_resize; |
| 18 | 18 | ||
| 19 | extern struct perf_error_ops perf_tui_eops; | 19 | extern struct perf_error_ops perf_tui_eops; |
| 20 | extern bool tui_helpline__set; | ||
| 20 | 21 | ||
| 21 | extern void hist_browser__init_hpp(void); | 22 | extern void hist_browser__init_hpp(void); |
| 22 | 23 | ||
| @@ -159,7 +160,7 @@ out: | |||
| 159 | 160 | ||
| 160 | void ui__exit(bool wait_for_ok) | 161 | void ui__exit(bool wait_for_ok) |
| 161 | { | 162 | { |
| 162 | if (wait_for_ok) | 163 | if (wait_for_ok && tui_helpline__set) |
| 163 | ui__question_window("Fatal Error", | 164 | ui__question_window("Fatal Error", |
| 164 | ui_helpline__last_msg, | 165 | ui_helpline__last_msg, |
| 165 | "Press any key...", 0); | 166 | "Press any key...", 0); |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 01bc4e23a2cf..61bf9128e1f2 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -239,7 +239,7 @@ static int mov__parse(struct ins_operands *ops) | |||
| 239 | *s = '\0'; | 239 | *s = '\0'; |
| 240 | ops->source.raw = strdup(ops->raw); | 240 | ops->source.raw = strdup(ops->raw); |
| 241 | *s = ','; | 241 | *s = ','; |
| 242 | 242 | ||
| 243 | if (ops->source.raw == NULL) | 243 | if (ops->source.raw == NULL) |
| 244 | return -1; | 244 | return -1; |
| 245 | 245 | ||
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index f4654183d391..55355b3d4f85 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
| @@ -5,132 +5,6 @@ | |||
| 5 | 5 | ||
| 6 | int perf_use_color_default = -1; | 6 | int perf_use_color_default = -1; |
| 7 | 7 | ||
| 8 | static int parse_color(const char *name, int len) | ||
| 9 | { | ||
| 10 | static const char * const color_names[] = { | ||
| 11 | "normal", "black", "red", "green", "yellow", | ||
| 12 | "blue", "magenta", "cyan", "white" | ||
| 13 | }; | ||
| 14 | char *end; | ||
| 15 | int i; | ||
| 16 | |||
| 17 | for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) { | ||
| 18 | const char *str = color_names[i]; | ||
| 19 | if (!strncasecmp(name, str, len) && !str[len]) | ||
| 20 | return i - 1; | ||
| 21 | } | ||
| 22 | i = strtol(name, &end, 10); | ||
| 23 | if (end - name == len && i >= -1 && i <= 255) | ||
| 24 | return i; | ||
| 25 | return -2; | ||
| 26 | } | ||
| 27 | |||
| 28 | static int parse_attr(const char *name, int len) | ||
| 29 | { | ||
| 30 | static const int attr_values[] = { 1, 2, 4, 5, 7 }; | ||
| 31 | static const char * const attr_names[] = { | ||
| 32 | "bold", "dim", "ul", "blink", "reverse" | ||
| 33 | }; | ||
| 34 | unsigned int i; | ||
| 35 | |||
| 36 | for (i = 0; i < ARRAY_SIZE(attr_names); i++) { | ||
| 37 | const char *str = attr_names[i]; | ||
| 38 | if (!strncasecmp(name, str, len) && !str[len]) | ||
| 39 | return attr_values[i]; | ||
| 40 | } | ||
| 41 | return -1; | ||
| 42 | } | ||
| 43 | |||
| 44 | void color_parse(const char *value, const char *var, char *dst) | ||
| 45 | { | ||
| 46 | color_parse_mem(value, strlen(value), var, dst); | ||
| 47 | } | ||
| 48 | |||
| 49 | void color_parse_mem(const char *value, int value_len, const char *var, | ||
| 50 | char *dst) | ||
| 51 | { | ||
| 52 | const char *ptr = value; | ||
| 53 | int len = value_len; | ||
| 54 | int attr = -1; | ||
| 55 | int fg = -2; | ||
| 56 | int bg = -2; | ||
| 57 | |||
| 58 | if (!strncasecmp(value, "reset", len)) { | ||
| 59 | strcpy(dst, PERF_COLOR_RESET); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | |||
| 63 | /* [fg [bg]] [attr] */ | ||
| 64 | while (len > 0) { | ||
| 65 | const char *word = ptr; | ||
| 66 | int val, wordlen = 0; | ||
| 67 | |||
| 68 | while (len > 0 && !isspace(word[wordlen])) { | ||
| 69 | wordlen++; | ||
| 70 | len--; | ||
| 71 | } | ||
| 72 | |||
| 73 | ptr = word + wordlen; | ||
| 74 | while (len > 0 && isspace(*ptr)) { | ||
| 75 | ptr++; | ||
| 76 | len--; | ||
| 77 | } | ||
| 78 | |||
| 79 | val = parse_color(word, wordlen); | ||
| 80 | if (val >= -1) { | ||
| 81 | if (fg == -2) { | ||
| 82 | fg = val; | ||
| 83 | continue; | ||
| 84 | } | ||
| 85 | if (bg == -2) { | ||
| 86 | bg = val; | ||
| 87 | continue; | ||
| 88 | } | ||
| 89 | goto bad; | ||
| 90 | } | ||
| 91 | val = parse_attr(word, wordlen); | ||
| 92 | if (val < 0 || attr != -1) | ||
| 93 | goto bad; | ||
| 94 | attr = val; | ||
| 95 | } | ||
| 96 | |||
| 97 | if (attr >= 0 || fg >= 0 || bg >= 0) { | ||
| 98 | int sep = 0; | ||
| 99 | |||
| 100 | *dst++ = '\033'; | ||
| 101 | *dst++ = '['; | ||
| 102 | if (attr >= 0) { | ||
| 103 | *dst++ = '0' + attr; | ||
| 104 | sep++; | ||
| 105 | } | ||
| 106 | if (fg >= 0) { | ||
| 107 | if (sep++) | ||
| 108 | *dst++ = ';'; | ||
| 109 | if (fg < 8) { | ||
| 110 | *dst++ = '3'; | ||
| 111 | *dst++ = '0' + fg; | ||
| 112 | } else { | ||
| 113 | dst += sprintf(dst, "38;5;%d", fg); | ||
| 114 | } | ||
| 115 | } | ||
| 116 | if (bg >= 0) { | ||
| 117 | if (sep++) | ||
| 118 | *dst++ = ';'; | ||
| 119 | if (bg < 8) { | ||
| 120 | *dst++ = '4'; | ||
| 121 | *dst++ = '0' + bg; | ||
| 122 | } else { | ||
| 123 | dst += sprintf(dst, "48;5;%d", bg); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | *dst++ = 'm'; | ||
| 127 | } | ||
| 128 | *dst = 0; | ||
| 129 | return; | ||
| 130 | bad: | ||
| 131 | die("bad color value '%.*s' for variable '%s'", value_len, value, var); | ||
| 132 | } | ||
| 133 | |||
| 134 | int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) | 8 | int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) |
| 135 | { | 9 | { |
| 136 | if (value) { | 10 | if (value) { |
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 0a594b8a0c26..38146f922c54 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h | |||
| @@ -30,8 +30,6 @@ extern int perf_use_color_default; | |||
| 30 | int perf_color_default_config(const char *var, const char *value, void *cb); | 30 | int perf_color_default_config(const char *var, const char *value, void *cb); |
| 31 | 31 | ||
| 32 | int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); | 32 | int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty); |
| 33 | void color_parse(const char *value, const char *var, char *dst); | ||
| 34 | void color_parse_mem(const char *value, int len, const char *var, char *dst); | ||
| 35 | int color_vsnprintf(char *bf, size_t size, const char *color, | 33 | int color_vsnprintf(char *bf, size_t size, const char *color, |
| 36 | const char *fmt, va_list args); | 34 | const char *fmt, va_list args); |
| 37 | int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args); | 35 | int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args); |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 45be944d450a..c2f7d3b90966 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
| @@ -532,12 +532,8 @@ dso_cache__read(struct dso *dso, u64 offset, u8 *data, ssize_t size) | |||
| 532 | break; | 532 | break; |
| 533 | 533 | ||
| 534 | cache_offset = offset & DSO__DATA_CACHE_MASK; | 534 | cache_offset = offset & DSO__DATA_CACHE_MASK; |
| 535 | ret = -EINVAL; | ||
| 536 | 535 | ||
| 537 | if (-1 == lseek(dso->data.fd, cache_offset, SEEK_SET)) | 536 | ret = pread(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE, cache_offset); |
| 538 | break; | ||
| 539 | |||
| 540 | ret = read(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE); | ||
| 541 | if (ret <= 0) | 537 | if (ret <= 0) |
| 542 | break; | 538 | break; |
| 543 | 539 | ||
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 3782c82c6e44..ced92841ff97 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
| @@ -139,6 +139,7 @@ struct dso { | |||
| 139 | u32 status_seen; | 139 | u32 status_seen; |
| 140 | size_t file_size; | 140 | size_t file_size; |
| 141 | struct list_head open_entry; | 141 | struct list_head open_entry; |
| 142 | u64 frame_offset; | ||
| 142 | } data; | 143 | } data; |
| 143 | 144 | ||
| 144 | union { /* Tool specific area */ | 145 | union { /* Tool specific area */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 2e507b5025a3..28b8ce86bf12 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
| @@ -1436,33 +1436,6 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) | |||
| 1436 | return printed + fprintf(fp, "\n"); | 1436 | return printed + fprintf(fp, "\n"); |
| 1437 | } | 1437 | } |
| 1438 | 1438 | ||
| 1439 | int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, | ||
| 1440 | int err, char *buf, size_t size) | ||
| 1441 | { | ||
| 1442 | char sbuf[128]; | ||
| 1443 | |||
| 1444 | switch (err) { | ||
| 1445 | case ENOENT: | ||
| 1446 | scnprintf(buf, size, "%s", | ||
| 1447 | "Error:\tUnable to find debugfs\n" | ||
| 1448 | "Hint:\tWas your kernel compiled with debugfs support?\n" | ||
| 1449 | "Hint:\tIs the debugfs filesystem mounted?\n" | ||
| 1450 | "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); | ||
| 1451 | break; | ||
| 1452 | case EACCES: | ||
| 1453 | scnprintf(buf, size, | ||
| 1454 | "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" | ||
| 1455 | "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", | ||
| 1456 | debugfs_mountpoint, debugfs_mountpoint); | ||
| 1457 | break; | ||
| 1458 | default: | ||
| 1459 | scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); | ||
| 1460 | break; | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | return 0; | ||
| 1464 | } | ||
| 1465 | |||
| 1466 | int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | 1439 | int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, |
| 1467 | int err, char *buf, size_t size) | 1440 | int err, char *buf, size_t size) |
| 1468 | { | 1441 | { |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 0ba93f67ab94..c94a9e03ecf1 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
| @@ -183,7 +183,6 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) | |||
| 183 | 183 | ||
| 184 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); | 184 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); |
| 185 | 185 | ||
| 186 | int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); | ||
| 187 | int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); | 186 | int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); |
| 188 | int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size); | 187 | int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size); |
| 189 | 188 | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 1e90c8557ede..ea51a90e20a0 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -709,6 +709,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 709 | if (opts->sample_weight) | 709 | if (opts->sample_weight) |
| 710 | perf_evsel__set_sample_bit(evsel, WEIGHT); | 710 | perf_evsel__set_sample_bit(evsel, WEIGHT); |
| 711 | 711 | ||
| 712 | attr->task = track; | ||
| 712 | attr->mmap = track; | 713 | attr->mmap = track; |
| 713 | attr->mmap2 = track && !perf_missing_features.mmap2; | 714 | attr->mmap2 = track && !perf_missing_features.mmap2; |
| 714 | attr->comm = track; | 715 | attr->comm = track; |
| @@ -797,6 +798,9 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
| 797 | 798 | ||
| 798 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 799 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
| 799 | { | 800 | { |
| 801 | if (ncpus == 0 || nthreads == 0) | ||
| 802 | return 0; | ||
| 803 | |||
| 800 | if (evsel->system_wide) | 804 | if (evsel->system_wide) |
| 801 | nthreads = 1; | 805 | nthreads = 1; |
| 802 | 806 | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index b20e40c74468..1f407f7352a7 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -2237,6 +2237,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz, | |||
| 2237 | * - unique number to identify actual perf.data files | 2237 | * - unique number to identify actual perf.data files |
| 2238 | * - encode endianness of file | 2238 | * - encode endianness of file |
| 2239 | */ | 2239 | */ |
| 2240 | ph->version = PERF_HEADER_VERSION_2; | ||
| 2240 | 2241 | ||
| 2241 | /* check magic number with one endianness */ | 2242 | /* check magic number with one endianness */ |
| 2242 | if (magic == __perf_magic2) | 2243 | if (magic == __perf_magic2) |
| @@ -2247,7 +2248,6 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz, | |||
| 2247 | return -1; | 2248 | return -1; |
| 2248 | 2249 | ||
| 2249 | ph->needs_swap = true; | 2250 | ph->needs_swap = true; |
| 2250 | ph->version = PERF_HEADER_VERSION_2; | ||
| 2251 | 2251 | ||
| 2252 | return 0; | 2252 | return 0; |
| 2253 | } | 2253 | } |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 182395546ddc..70b48a65064c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -241,6 +241,20 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | |||
| 241 | return he->stat.period == 0; | 241 | return he->stat.period == 0; |
| 242 | } | 242 | } |
| 243 | 243 | ||
| 244 | static void hists__delete_entry(struct hists *hists, struct hist_entry *he) | ||
| 245 | { | ||
| 246 | rb_erase(&he->rb_node, &hists->entries); | ||
| 247 | |||
| 248 | if (sort__need_collapse) | ||
| 249 | rb_erase(&he->rb_node_in, &hists->entries_collapsed); | ||
| 250 | |||
| 251 | --hists->nr_entries; | ||
| 252 | if (!he->filtered) | ||
| 253 | --hists->nr_non_filtered_entries; | ||
| 254 | |||
| 255 | hist_entry__delete(he); | ||
| 256 | } | ||
| 257 | |||
| 244 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | 258 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) |
| 245 | { | 259 | { |
| 246 | struct rb_node *next = rb_first(&hists->entries); | 260 | struct rb_node *next = rb_first(&hists->entries); |
| @@ -258,16 +272,7 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | |||
| 258 | (zap_kernel && n->level != '.') || | 272 | (zap_kernel && n->level != '.') || |
| 259 | hists__decay_entry(hists, n)) && | 273 | hists__decay_entry(hists, n)) && |
| 260 | !n->used) { | 274 | !n->used) { |
| 261 | rb_erase(&n->rb_node, &hists->entries); | 275 | hists__delete_entry(hists, n); |
| 262 | |||
| 263 | if (sort__need_collapse) | ||
| 264 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
| 265 | |||
| 266 | --hists->nr_entries; | ||
| 267 | if (!n->filtered) | ||
| 268 | --hists->nr_non_filtered_entries; | ||
| 269 | |||
| 270 | hist_entry__free(n); | ||
| 271 | } | 276 | } |
| 272 | } | 277 | } |
| 273 | } | 278 | } |
| @@ -281,16 +286,7 @@ void hists__delete_entries(struct hists *hists) | |||
| 281 | n = rb_entry(next, struct hist_entry, rb_node); | 286 | n = rb_entry(next, struct hist_entry, rb_node); |
| 282 | next = rb_next(&n->rb_node); | 287 | next = rb_next(&n->rb_node); |
| 283 | 288 | ||
| 284 | rb_erase(&n->rb_node, &hists->entries); | 289 | hists__delete_entry(hists, n); |
| 285 | |||
| 286 | if (sort__need_collapse) | ||
| 287 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
| 288 | |||
| 289 | --hists->nr_entries; | ||
| 290 | if (!n->filtered) | ||
| 291 | --hists->nr_non_filtered_entries; | ||
| 292 | |||
| 293 | hist_entry__free(n); | ||
| 294 | } | 290 | } |
| 295 | } | 291 | } |
| 296 | 292 | ||
| @@ -433,6 +429,8 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
| 433 | if (!he) | 429 | if (!he) |
| 434 | return NULL; | 430 | return NULL; |
| 435 | 431 | ||
| 432 | hists->nr_entries++; | ||
| 433 | |||
| 436 | rb_link_node(&he->rb_node_in, parent, p); | 434 | rb_link_node(&he->rb_node_in, parent, p); |
| 437 | rb_insert_color(&he->rb_node_in, hists->entries_in); | 435 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
| 438 | out: | 436 | out: |
| @@ -915,7 +913,7 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 915 | if (perf_hpp__should_skip(fmt)) | 913 | if (perf_hpp__should_skip(fmt)) |
| 916 | continue; | 914 | continue; |
| 917 | 915 | ||
| 918 | cmp = fmt->cmp(left, right); | 916 | cmp = fmt->cmp(fmt, left, right); |
| 919 | if (cmp) | 917 | if (cmp) |
| 920 | break; | 918 | break; |
| 921 | } | 919 | } |
| @@ -933,7 +931,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 933 | if (perf_hpp__should_skip(fmt)) | 931 | if (perf_hpp__should_skip(fmt)) |
| 934 | continue; | 932 | continue; |
| 935 | 933 | ||
| 936 | cmp = fmt->collapse(left, right); | 934 | cmp = fmt->collapse(fmt, left, right); |
| 937 | if (cmp) | 935 | if (cmp) |
| 938 | break; | 936 | break; |
| 939 | } | 937 | } |
| @@ -941,7 +939,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 941 | return cmp; | 939 | return cmp; |
| 942 | } | 940 | } |
| 943 | 941 | ||
| 944 | void hist_entry__free(struct hist_entry *he) | 942 | void hist_entry__delete(struct hist_entry *he) |
| 945 | { | 943 | { |
| 946 | zfree(&he->branch_info); | 944 | zfree(&he->branch_info); |
| 947 | zfree(&he->mem_info); | 945 | zfree(&he->mem_info); |
| @@ -981,7 +979,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, | |||
| 981 | iter->callchain, | 979 | iter->callchain, |
| 982 | he->callchain); | 980 | he->callchain); |
| 983 | } | 981 | } |
| 984 | hist_entry__free(he); | 982 | hist_entry__delete(he); |
| 985 | return false; | 983 | return false; |
| 986 | } | 984 | } |
| 987 | 985 | ||
| @@ -1063,7 +1061,7 @@ static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b) | |||
| 1063 | if (perf_hpp__should_skip(fmt)) | 1061 | if (perf_hpp__should_skip(fmt)) |
| 1064 | continue; | 1062 | continue; |
| 1065 | 1063 | ||
| 1066 | cmp = fmt->sort(a, b); | 1064 | cmp = fmt->sort(fmt, a, b); |
| 1067 | if (cmp) | 1065 | if (cmp) |
| 1068 | break; | 1066 | break; |
| 1069 | } | 1067 | } |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 46bd50344f85..2b690d028907 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -119,7 +119,7 @@ int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); | |||
| 119 | int hist_entry__transaction_len(void); | 119 | int hist_entry__transaction_len(void); |
| 120 | int hist_entry__sort_snprintf(struct hist_entry *he, char *bf, size_t size, | 120 | 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__delete(struct hist_entry *he); |
| 123 | 123 | ||
| 124 | void hists__output_resort(struct hists *hists, struct ui_progress *prog); | 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); |
| @@ -195,9 +195,12 @@ struct perf_hpp_fmt { | |||
| 195 | struct hist_entry *he); | 195 | struct hist_entry *he); |
| 196 | int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 196 | int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 197 | struct hist_entry *he); | 197 | struct hist_entry *he); |
| 198 | int64_t (*cmp)(struct hist_entry *a, struct hist_entry *b); | 198 | int64_t (*cmp)(struct perf_hpp_fmt *fmt, |
| 199 | int64_t (*collapse)(struct hist_entry *a, struct hist_entry *b); | 199 | struct hist_entry *a, struct hist_entry *b); |
| 200 | int64_t (*sort)(struct hist_entry *a, struct hist_entry *b); | 200 | int64_t (*collapse)(struct perf_hpp_fmt *fmt, |
| 201 | struct hist_entry *a, struct hist_entry *b); | ||
| 202 | int64_t (*sort)(struct perf_hpp_fmt *fmt, | ||
| 203 | struct hist_entry *a, struct hist_entry *b); | ||
| 201 | 204 | ||
| 202 | struct list_head list; | 205 | struct list_head list; |
| 203 | struct list_head sort_list; | 206 | struct list_head sort_list; |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 77b43fe43d55..7f8ec6ce2823 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -526,7 +526,7 @@ do { \ | |||
| 526 | } | 526 | } |
| 527 | 527 | ||
| 528 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | 528 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
| 529 | void *ptr, char *type) | 529 | void *ptr, char *type, u64 len) |
| 530 | { | 530 | { |
| 531 | struct perf_event_attr attr; | 531 | struct perf_event_attr attr; |
| 532 | 532 | ||
| @@ -536,14 +536,15 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, | |||
| 536 | if (parse_breakpoint_type(type, &attr)) | 536 | if (parse_breakpoint_type(type, &attr)) |
| 537 | return -EINVAL; | 537 | return -EINVAL; |
| 538 | 538 | ||
| 539 | /* | 539 | /* Provide some defaults if len is not specified */ |
| 540 | * We should find a nice way to override the access length | 540 | if (!len) { |
| 541 | * Provide some defaults for now | 541 | if (attr.bp_type == HW_BREAKPOINT_X) |
| 542 | */ | 542 | len = sizeof(long); |
| 543 | if (attr.bp_type == HW_BREAKPOINT_X) | 543 | else |
| 544 | attr.bp_len = sizeof(long); | 544 | len = HW_BREAKPOINT_LEN_4; |
| 545 | else | 545 | } |
| 546 | attr.bp_len = HW_BREAKPOINT_LEN_4; | 546 | |
| 547 | attr.bp_len = len; | ||
| 547 | 548 | ||
| 548 | attr.type = PERF_TYPE_BREAKPOINT; | 549 | attr.type = PERF_TYPE_BREAKPOINT; |
| 549 | attr.sample_period = 1; | 550 | attr.sample_period = 1; |
| @@ -1121,7 +1122,7 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | |||
| 1121 | return; | 1122 | return; |
| 1122 | 1123 | ||
| 1123 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { | 1124 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { |
| 1124 | if (subsys_glob != NULL && | 1125 | if (subsys_glob != NULL && |
| 1125 | !strglobmatch(sys_dirent.d_name, subsys_glob)) | 1126 | !strglobmatch(sys_dirent.d_name, subsys_glob)) |
| 1126 | continue; | 1127 | continue; |
| 1127 | 1128 | ||
| @@ -1132,7 +1133,7 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | |||
| 1132 | continue; | 1133 | continue; |
| 1133 | 1134 | ||
| 1134 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | 1135 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { |
| 1135 | if (event_glob != NULL && | 1136 | if (event_glob != NULL && |
| 1136 | !strglobmatch(evt_dirent.d_name, event_glob)) | 1137 | !strglobmatch(evt_dirent.d_name, event_glob)) |
| 1137 | continue; | 1138 | continue; |
| 1138 | 1139 | ||
| @@ -1305,7 +1306,7 @@ static void print_symbol_events(const char *event_glob, unsigned type, | |||
| 1305 | 1306 | ||
| 1306 | for (i = 0; i < max; i++, syms++) { | 1307 | for (i = 0; i < max; i++, syms++) { |
| 1307 | 1308 | ||
| 1308 | if (event_glob != NULL && | 1309 | if (event_glob != NULL && |
| 1309 | !(strglobmatch(syms->symbol, event_glob) || | 1310 | !(strglobmatch(syms->symbol, event_glob) || |
| 1310 | (syms->alias && strglobmatch(syms->alias, event_glob)))) | 1311 | (syms->alias && strglobmatch(syms->alias, event_glob)))) |
| 1311 | continue; | 1312 | continue; |
| @@ -1366,7 +1367,7 @@ void print_events(const char *event_glob, bool name_only) | |||
| 1366 | printf("\n"); | 1367 | printf("\n"); |
| 1367 | 1368 | ||
| 1368 | printf(" %-50s [%s]\n", | 1369 | printf(" %-50s [%s]\n", |
| 1369 | "mem:<addr>[:access]", | 1370 | "mem:<addr>[/len][:access]", |
| 1370 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | 1371 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); |
| 1371 | printf("\n"); | 1372 | printf("\n"); |
| 1372 | } | 1373 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index db2cf78ff0f3..ff6e1fa4111e 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -71,6 +71,7 @@ struct parse_events_term { | |||
| 71 | int type_val; | 71 | int type_val; |
| 72 | int type_term; | 72 | int type_term; |
| 73 | struct list_head list; | 73 | struct list_head list; |
| 74 | bool used; | ||
| 74 | }; | 75 | }; |
| 75 | 76 | ||
| 76 | struct parse_events_evlist { | 77 | struct parse_events_evlist { |
| @@ -104,7 +105,7 @@ int parse_events_add_numeric(struct list_head *list, int *idx, | |||
| 104 | int parse_events_add_cache(struct list_head *list, int *idx, | 105 | int parse_events_add_cache(struct list_head *list, int *idx, |
| 105 | char *type, char *op_result1, char *op_result2); | 106 | char *type, char *op_result1, char *op_result2); |
| 106 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | 107 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
| 107 | void *ptr, char *type); | 108 | void *ptr, char *type, u64 len); |
| 108 | int parse_events_add_pmu(struct list_head *list, int *idx, | 109 | int parse_events_add_pmu(struct list_head *list, int *idx, |
| 109 | char *pmu , struct list_head *head_config); | 110 | char *pmu , struct list_head *head_config); |
| 110 | enum perf_pmu_event_symbol_type | 111 | enum perf_pmu_event_symbol_type |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 906630bbf8eb..94eacb6c1ef7 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
| @@ -159,6 +159,7 @@ branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE | |||
| 159 | <mem>{ | 159 | <mem>{ |
| 160 | {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } | 160 | {modifier_bp} { return str(yyscanner, PE_MODIFIER_BP); } |
| 161 | : { return ':'; } | 161 | : { return ':'; } |
| 162 | "/" { return '/'; } | ||
| 162 | {num_dec} { return value(yyscanner, 10); } | 163 | {num_dec} { return value(yyscanner, 10); } |
| 163 | {num_hex} { return value(yyscanner, 16); } | 164 | {num_hex} { return value(yyscanner, 16); } |
| 164 | /* | 165 | /* |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 93c4c9fbc922..72def077dbbf 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
| @@ -326,6 +326,28 @@ PE_NAME_CACHE_TYPE | |||
| 326 | } | 326 | } |
| 327 | 327 | ||
| 328 | event_legacy_mem: | 328 | event_legacy_mem: |
| 329 | PE_PREFIX_MEM PE_VALUE '/' PE_VALUE ':' PE_MODIFIER_BP sep_dc | ||
| 330 | { | ||
| 331 | struct parse_events_evlist *data = _data; | ||
| 332 | struct list_head *list; | ||
| 333 | |||
| 334 | ALLOC_LIST(list); | ||
| 335 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | ||
| 336 | (void *) $2, $6, $4)); | ||
| 337 | $$ = list; | ||
| 338 | } | ||
| 339 | | | ||
| 340 | PE_PREFIX_MEM PE_VALUE '/' PE_VALUE sep_dc | ||
| 341 | { | ||
| 342 | struct parse_events_evlist *data = _data; | ||
| 343 | struct list_head *list; | ||
| 344 | |||
| 345 | ALLOC_LIST(list); | ||
| 346 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | ||
| 347 | (void *) $2, NULL, $4)); | ||
| 348 | $$ = list; | ||
| 349 | } | ||
| 350 | | | ||
| 329 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | 351 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc |
| 330 | { | 352 | { |
| 331 | struct parse_events_evlist *data = _data; | 353 | struct parse_events_evlist *data = _data; |
| @@ -333,7 +355,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | |||
| 333 | 355 | ||
| 334 | ALLOC_LIST(list); | 356 | ALLOC_LIST(list); |
| 335 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | 357 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, |
| 336 | (void *) $2, $4)); | 358 | (void *) $2, $4, 0)); |
| 337 | $$ = list; | 359 | $$ = list; |
| 338 | } | 360 | } |
| 339 | | | 361 | | |
| @@ -344,7 +366,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc | |||
| 344 | 366 | ||
| 345 | ALLOC_LIST(list); | 367 | ALLOC_LIST(list); |
| 346 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | 368 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, |
| 347 | (void *) $2, NULL)); | 369 | (void *) $2, NULL, 0)); |
| 348 | $$ = list; | 370 | $$ = list; |
| 349 | } | 371 | } |
| 350 | 372 | ||
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index f62dee7bd924..4a015f77e2b5 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
| @@ -46,7 +46,7 @@ static int get_value(struct parse_opt_ctx_t *p, | |||
| 46 | return opterror(opt, "is not usable", flags); | 46 | return opterror(opt, "is not usable", flags); |
| 47 | 47 | ||
| 48 | if (opt->flags & PARSE_OPT_EXCLUSIVE) { | 48 | if (opt->flags & PARSE_OPT_EXCLUSIVE) { |
| 49 | if (p->excl_opt) { | 49 | if (p->excl_opt && p->excl_opt != opt) { |
| 50 | char msg[128]; | 50 | char msg[128]; |
| 51 | 51 | ||
| 52 | if (((flags & OPT_SHORT) && p->excl_opt->short_name) || | 52 | if (((flags & OPT_SHORT) && p->excl_opt->short_name) || |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 5c9c4947cfb4..48411674da0f 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
| @@ -551,31 +551,68 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, | |||
| 551 | } | 551 | } |
| 552 | 552 | ||
| 553 | /* | 553 | /* |
| 554 | * Term is a string term, and might be a param-term. Try to look up it's value | ||
| 555 | * in the remaining terms. | ||
| 556 | * - We have a term like "base-or-format-term=param-term", | ||
| 557 | * - We need to find the value supplied for "param-term" (with param-term named | ||
| 558 | * in a config string) later on in the term list. | ||
| 559 | */ | ||
| 560 | static int pmu_resolve_param_term(struct parse_events_term *term, | ||
| 561 | struct list_head *head_terms, | ||
| 562 | __u64 *value) | ||
| 563 | { | ||
| 564 | struct parse_events_term *t; | ||
| 565 | |||
| 566 | list_for_each_entry(t, head_terms, list) { | ||
| 567 | if (t->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { | ||
| 568 | if (!strcmp(t->config, term->config)) { | ||
| 569 | t->used = true; | ||
| 570 | *value = t->val.num; | ||
| 571 | return 0; | ||
| 572 | } | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | if (verbose) | ||
| 577 | printf("Required parameter '%s' not specified\n", term->config); | ||
| 578 | |||
| 579 | return -1; | ||
| 580 | } | ||
| 581 | |||
| 582 | /* | ||
| 554 | * Setup one of config[12] attr members based on the | 583 | * Setup one of config[12] attr members based on the |
| 555 | * user input data - term parameter. | 584 | * user input data - term parameter. |
| 556 | */ | 585 | */ |
| 557 | static int pmu_config_term(struct list_head *formats, | 586 | static int pmu_config_term(struct list_head *formats, |
| 558 | struct perf_event_attr *attr, | 587 | struct perf_event_attr *attr, |
| 559 | struct parse_events_term *term, | 588 | struct parse_events_term *term, |
| 589 | struct list_head *head_terms, | ||
| 560 | bool zero) | 590 | bool zero) |
| 561 | { | 591 | { |
| 562 | struct perf_pmu_format *format; | 592 | struct perf_pmu_format *format; |
| 563 | __u64 *vp; | 593 | __u64 *vp; |
| 594 | __u64 val; | ||
| 595 | |||
| 596 | /* | ||
| 597 | * If this is a parameter we've already used for parameterized-eval, | ||
| 598 | * skip it in normal eval. | ||
| 599 | */ | ||
| 600 | if (term->used) | ||
| 601 | return 0; | ||
| 564 | 602 | ||
| 565 | /* | 603 | /* |
| 566 | * Support only for hardcoded and numnerial terms. | ||
| 567 | * Hardcoded terms should be already in, so nothing | 604 | * Hardcoded terms should be already in, so nothing |
| 568 | * to be done for them. | 605 | * to be done for them. |
| 569 | */ | 606 | */ |
| 570 | if (parse_events__is_hardcoded_term(term)) | 607 | if (parse_events__is_hardcoded_term(term)) |
| 571 | return 0; | 608 | return 0; |
| 572 | 609 | ||
| 573 | if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) | ||
| 574 | return -EINVAL; | ||
| 575 | |||
| 576 | format = pmu_find_format(formats, term->config); | 610 | format = pmu_find_format(formats, term->config); |
| 577 | if (!format) | 611 | if (!format) { |
| 612 | if (verbose) | ||
| 613 | printf("Invalid event/parameter '%s'\n", term->config); | ||
| 578 | return -EINVAL; | 614 | return -EINVAL; |
| 615 | } | ||
| 579 | 616 | ||
| 580 | switch (format->value) { | 617 | switch (format->value) { |
| 581 | case PERF_PMU_FORMAT_VALUE_CONFIG: | 618 | case PERF_PMU_FORMAT_VALUE_CONFIG: |
| @@ -592,11 +629,25 @@ static int pmu_config_term(struct list_head *formats, | |||
| 592 | } | 629 | } |
| 593 | 630 | ||
| 594 | /* | 631 | /* |
| 595 | * XXX If we ever decide to go with string values for | 632 | * Either directly use a numeric term, or try to translate string terms |
| 596 | * non-hardcoded terms, here's the place to translate | 633 | * using event parameters. |
| 597 | * them into value. | ||
| 598 | */ | 634 | */ |
| 599 | pmu_format_value(format->bits, term->val.num, vp, zero); | 635 | if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) |
| 636 | val = term->val.num; | ||
| 637 | else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { | ||
| 638 | if (strcmp(term->val.str, "?")) { | ||
| 639 | if (verbose) | ||
| 640 | pr_info("Invalid sysfs entry %s=%s\n", | ||
| 641 | term->config, term->val.str); | ||
| 642 | return -EINVAL; | ||
| 643 | } | ||
| 644 | |||
| 645 | if (pmu_resolve_param_term(term, head_terms, &val)) | ||
| 646 | return -EINVAL; | ||
| 647 | } else | ||
| 648 | return -EINVAL; | ||
| 649 | |||
| 650 | pmu_format_value(format->bits, val, vp, zero); | ||
| 600 | return 0; | 651 | return 0; |
| 601 | } | 652 | } |
| 602 | 653 | ||
| @@ -607,9 +658,10 @@ int perf_pmu__config_terms(struct list_head *formats, | |||
| 607 | { | 658 | { |
| 608 | struct parse_events_term *term; | 659 | struct parse_events_term *term; |
| 609 | 660 | ||
| 610 | list_for_each_entry(term, head_terms, list) | 661 | list_for_each_entry(term, head_terms, list) { |
| 611 | if (pmu_config_term(formats, attr, term, zero)) | 662 | if (pmu_config_term(formats, attr, term, head_terms, zero)) |
| 612 | return -EINVAL; | 663 | return -EINVAL; |
| 664 | } | ||
| 613 | 665 | ||
| 614 | return 0; | 666 | return 0; |
| 615 | } | 667 | } |
| @@ -767,10 +819,36 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to) | |||
| 767 | set_bit(b, bits); | 819 | set_bit(b, bits); |
| 768 | } | 820 | } |
| 769 | 821 | ||
| 822 | static int sub_non_neg(int a, int b) | ||
| 823 | { | ||
| 824 | if (b > a) | ||
| 825 | return 0; | ||
| 826 | return a - b; | ||
| 827 | } | ||
| 828 | |||
| 770 | static char *format_alias(char *buf, int len, struct perf_pmu *pmu, | 829 | static char *format_alias(char *buf, int len, struct perf_pmu *pmu, |
| 771 | struct perf_pmu_alias *alias) | 830 | struct perf_pmu_alias *alias) |
| 772 | { | 831 | { |
| 773 | snprintf(buf, len, "%s/%s/", pmu->name, alias->name); | 832 | struct parse_events_term *term; |
| 833 | int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); | ||
| 834 | |||
| 835 | list_for_each_entry(term, &alias->terms, list) { | ||
| 836 | if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) | ||
| 837 | used += snprintf(buf + used, sub_non_neg(len, used), | ||
| 838 | ",%s=%s", term->config, | ||
| 839 | term->val.str); | ||
| 840 | } | ||
| 841 | |||
| 842 | if (sub_non_neg(len, used) > 0) { | ||
| 843 | buf[used] = '/'; | ||
| 844 | used++; | ||
| 845 | } | ||
| 846 | if (sub_non_neg(len, used) > 0) { | ||
| 847 | buf[used] = '\0'; | ||
| 848 | used++; | ||
| 849 | } else | ||
| 850 | buf[len - 1] = '\0'; | ||
| 851 | |||
| 774 | return buf; | 852 | return buf; |
| 775 | } | 853 | } |
| 776 | 854 | ||
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 3dda85ca50c1..d906d0ad5d40 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
| @@ -768,7 +768,7 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, | |||
| 768 | Py_DECREF(file); | 768 | Py_DECREF(file); |
| 769 | goto free_list; | 769 | goto free_list; |
| 770 | } | 770 | } |
| 771 | 771 | ||
| 772 | Py_DECREF(file); | 772 | Py_DECREF(file); |
| 773 | } | 773 | } |
| 774 | 774 | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index d808a328f4dc..0c815a40a6e8 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
| @@ -89,7 +89,7 @@ static void handler_call_die(const char *handler_name) | |||
| 89 | 89 | ||
| 90 | /* | 90 | /* |
| 91 | * Insert val into into the dictionary and decrement the reference counter. | 91 | * Insert val into into the dictionary and decrement the reference counter. |
| 92 | * This is necessary for dictionaries since PyDict_SetItemString() does not | 92 | * This is necessary for dictionaries since PyDict_SetItemString() does not |
| 93 | * steal a reference, as opposed to PyTuple_SetItem(). | 93 | * steal a reference, as opposed to PyTuple_SetItem(). |
| 94 | */ | 94 | */ |
| 95 | static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObject *val) | 95 | static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObject *val) |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5f0e05a76c05..0baf75f12b7c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -274,7 +274,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
| 274 | if (tool->id_index == NULL) | 274 | if (tool->id_index == NULL) |
| 275 | tool->id_index = process_id_index_stub; | 275 | tool->id_index = process_id_index_stub; |
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | static void swap_sample_id_all(union perf_event *event, void *data) | 278 | static void swap_sample_id_all(union perf_event *event, void *data) |
| 279 | { | 279 | { |
| 280 | void *end = (void *) event + event->header.size; | 280 | void *end = (void *) event + event->header.size; |
| @@ -1251,9 +1251,9 @@ fetch_mmaped_event(struct perf_session *session, | |||
| 1251 | #define NUM_MMAPS 128 | 1251 | #define NUM_MMAPS 128 |
| 1252 | #endif | 1252 | #endif |
| 1253 | 1253 | ||
| 1254 | int __perf_session__process_events(struct perf_session *session, | 1254 | static int __perf_session__process_events(struct perf_session *session, |
| 1255 | u64 data_offset, u64 data_size, | 1255 | u64 data_offset, u64 data_size, |
| 1256 | u64 file_size, struct perf_tool *tool) | 1256 | u64 file_size, struct perf_tool *tool) |
| 1257 | { | 1257 | { |
| 1258 | int fd = perf_data_file__fd(session->file); | 1258 | int fd = perf_data_file__fd(session->file); |
| 1259 | u64 head, page_offset, file_offset, file_pos, size; | 1259 | u64 head, page_offset, file_offset, file_pos, size; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index dc26ebf60fe4..6d663dc76404 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -49,9 +49,6 @@ int perf_session__peek_event(struct perf_session *session, off_t file_offset, | |||
| 49 | union perf_event **event_ptr, | 49 | union perf_event **event_ptr, |
| 50 | struct perf_sample *sample); | 50 | struct perf_sample *sample); |
| 51 | 51 | ||
| 52 | int __perf_session__process_events(struct perf_session *session, | ||
| 53 | u64 data_offset, u64 data_size, u64 size, | ||
| 54 | struct perf_tool *tool); | ||
| 55 | int perf_session__process_events(struct perf_session *session, | 52 | int perf_session__process_events(struct perf_session *session, |
| 56 | struct perf_tool *tool); | 53 | struct perf_tool *tool); |
| 57 | 54 | ||
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9139dda9f9a3..7a39c1ed8d37 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -1304,6 +1304,37 @@ static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | |||
| 1304 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); | 1304 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); |
| 1305 | } | 1305 | } |
| 1306 | 1306 | ||
| 1307 | static int64_t __sort__hpp_cmp(struct perf_hpp_fmt *fmt, | ||
| 1308 | struct hist_entry *a, struct hist_entry *b) | ||
| 1309 | { | ||
| 1310 | struct hpp_sort_entry *hse; | ||
| 1311 | |||
| 1312 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
| 1313 | return hse->se->se_cmp(a, b); | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | static int64_t __sort__hpp_collapse(struct perf_hpp_fmt *fmt, | ||
| 1317 | struct hist_entry *a, struct hist_entry *b) | ||
| 1318 | { | ||
| 1319 | struct hpp_sort_entry *hse; | ||
| 1320 | int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *); | ||
| 1321 | |||
| 1322 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
| 1323 | collapse_fn = hse->se->se_collapse ?: hse->se->se_cmp; | ||
| 1324 | return collapse_fn(a, b); | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | static int64_t __sort__hpp_sort(struct perf_hpp_fmt *fmt, | ||
| 1328 | struct hist_entry *a, struct hist_entry *b) | ||
| 1329 | { | ||
| 1330 | struct hpp_sort_entry *hse; | ||
| 1331 | int64_t (*sort_fn)(struct hist_entry *, struct hist_entry *); | ||
| 1332 | |||
| 1333 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | ||
| 1334 | sort_fn = hse->se->se_sort ?: hse->se->se_cmp; | ||
| 1335 | return sort_fn(a, b); | ||
| 1336 | } | ||
| 1337 | |||
| 1307 | static struct hpp_sort_entry * | 1338 | static struct hpp_sort_entry * |
| 1308 | __sort_dimension__alloc_hpp(struct sort_dimension *sd) | 1339 | __sort_dimension__alloc_hpp(struct sort_dimension *sd) |
| 1309 | { | 1340 | { |
| @@ -1322,9 +1353,9 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
| 1322 | hse->hpp.entry = __sort__hpp_entry; | 1353 | hse->hpp.entry = __sort__hpp_entry; |
| 1323 | hse->hpp.color = NULL; | 1354 | hse->hpp.color = NULL; |
| 1324 | 1355 | ||
| 1325 | hse->hpp.cmp = sd->entry->se_cmp; | 1356 | hse->hpp.cmp = __sort__hpp_cmp; |
| 1326 | hse->hpp.collapse = sd->entry->se_collapse ? : sd->entry->se_cmp; | 1357 | hse->hpp.collapse = __sort__hpp_collapse; |
| 1327 | hse->hpp.sort = sd->entry->se_sort ? : hse->hpp.collapse; | 1358 | hse->hpp.sort = __sort__hpp_sort; |
| 1328 | 1359 | ||
| 1329 | INIT_LIST_HEAD(&hse->hpp.list); | 1360 | INIT_LIST_HEAD(&hse->hpp.list); |
| 1330 | INIT_LIST_HEAD(&hse->hpp.sort_list); | 1361 | INIT_LIST_HEAD(&hse->hpp.sort_list); |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 06fcd1bf98b6..b24f9d8727a8 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
| @@ -574,13 +574,16 @@ static int decompress_kmodule(struct dso *dso, const char *name, | |||
| 574 | const char *ext = strrchr(name, '.'); | 574 | const char *ext = strrchr(name, '.'); |
| 575 | char tmpbuf[] = "/tmp/perf-kmod-XXXXXX"; | 575 | char tmpbuf[] = "/tmp/perf-kmod-XXXXXX"; |
| 576 | 576 | ||
| 577 | if ((type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && | 577 | if (type != DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && |
| 578 | type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP) || | 578 | type != DSO_BINARY_TYPE__GUEST_KMODULE_COMP && |
| 579 | type != dso->symtab_type) | 579 | type != DSO_BINARY_TYPE__BUILD_ID_CACHE) |
| 580 | return -1; | 580 | return -1; |
| 581 | 581 | ||
| 582 | if (!ext || !is_supported_compression(ext + 1)) | 582 | if (!ext || !is_supported_compression(ext + 1)) { |
| 583 | return -1; | 583 | ext = strrchr(dso->name, '.'); |
| 584 | if (!ext || !is_supported_compression(ext + 1)) | ||
| 585 | return -1; | ||
| 586 | } | ||
| 584 | 587 | ||
| 585 | fd = mkstemp(tmpbuf); | 588 | fd = mkstemp(tmpbuf); |
| 586 | if (fd < 0) | 589 | if (fd < 0) |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index a194702a0a2f..a69066865a55 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -685,7 +685,7 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta, | |||
| 685 | struct machine *machine = kmaps->machine; | 685 | struct machine *machine = kmaps->machine; |
| 686 | struct map *curr_map = map; | 686 | struct map *curr_map = map; |
| 687 | struct symbol *pos; | 687 | struct symbol *pos; |
| 688 | int count = 0, moved = 0; | 688 | int count = 0, moved = 0; |
| 689 | struct rb_root *root = &dso->symbols[map->type]; | 689 | struct rb_root *root = &dso->symbols[map->type]; |
| 690 | struct rb_node *next = rb_first(root); | 690 | struct rb_node *next = rb_first(root); |
| 691 | int kernel_range = 0; | 691 | int kernel_range = 0; |
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 6edf535f65c2..e3c40a520a25 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c | |||
| @@ -266,14 +266,17 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, | |||
| 266 | u64 *fde_count) | 266 | u64 *fde_count) |
| 267 | { | 267 | { |
| 268 | int ret = -EINVAL, fd; | 268 | int ret = -EINVAL, fd; |
| 269 | u64 offset; | 269 | u64 offset = dso->data.frame_offset; |
| 270 | 270 | ||
| 271 | fd = dso__data_fd(dso, machine); | 271 | if (offset == 0) { |
| 272 | if (fd < 0) | 272 | fd = dso__data_fd(dso, machine); |
| 273 | return -EINVAL; | 273 | if (fd < 0) |
| 274 | return -EINVAL; | ||
| 274 | 275 | ||
| 275 | /* Check the .eh_frame section for unwinding info */ | 276 | /* Check the .eh_frame section for unwinding info */ |
| 276 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | 277 | offset = elf_section_offset(fd, ".eh_frame_hdr"); |
| 278 | dso->data.frame_offset = offset; | ||
| 279 | } | ||
| 277 | 280 | ||
| 278 | if (offset) | 281 | if (offset) |
| 279 | ret = unwind_spec_ehframe(dso, machine, offset, | 282 | ret = unwind_spec_ehframe(dso, machine, offset, |
| @@ -287,14 +290,20 @@ static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, | |||
| 287 | static int read_unwind_spec_debug_frame(struct dso *dso, | 290 | static int read_unwind_spec_debug_frame(struct dso *dso, |
| 288 | struct machine *machine, u64 *offset) | 291 | struct machine *machine, u64 *offset) |
| 289 | { | 292 | { |
| 290 | int fd = dso__data_fd(dso, machine); | 293 | int fd; |
| 294 | u64 ofs = dso->data.frame_offset; | ||
| 291 | 295 | ||
| 292 | if (fd < 0) | 296 | if (ofs == 0) { |
| 293 | return -EINVAL; | 297 | fd = dso__data_fd(dso, machine); |
| 298 | if (fd < 0) | ||
| 299 | return -EINVAL; | ||
| 294 | 300 | ||
| 295 | /* Check the .debug_frame section for unwinding info */ | 301 | /* Check the .debug_frame section for unwinding info */ |
| 296 | *offset = elf_section_offset(fd, ".debug_frame"); | 302 | ofs = elf_section_offset(fd, ".debug_frame"); |
| 303 | dso->data.frame_offset = ofs; | ||
| 304 | } | ||
| 297 | 305 | ||
| 306 | *offset = ofs; | ||
| 298 | if (*offset) | 307 | if (*offset) |
| 299 | return 0; | 308 | return 0; |
| 300 | 309 | ||
