diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 10:41:01 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 10:41:01 -0400 |
commit | e0972916e8fe943f342b0dd1c9d43dbf5bc261c2 (patch) | |
tree | 690c436f1f9b839c4ba34d17ab3efa63b97a2dce | |
parent | 1f889ec62c3f0d8913f3c32f9aff2a1e15099346 (diff) | |
parent | 5ac2b5c2721501a8f5c5e1cd4116cbc31ace6886 (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:
"Features:
- Add "uretprobes" - an optimization to uprobes, like kretprobes are
an optimization to kprobes. "perf probe -x file sym%return" now
works like kretprobes. By Oleg Nesterov.
- Introduce per core aggregation in 'perf stat', from Stephane
Eranian.
- Add memory profiling via PEBS, from Stephane Eranian.
- Event group view for 'annotate' in --stdio, --tui and --gtk, from
Namhyung Kim.
- Add support for AMD NB and L2I "uncore" counters, by Jacob Shin.
- Add Ivy Bridge-EP uncore support, by Zheng Yan
- IBM zEnterprise EC12 oprofile support patchlet from Robert Richter.
- Add perf test entries for checking breakpoint overflow signal
handler issues, from Jiri Olsa.
- Add perf test entry for for checking number of EXIT events, from
Namhyung Kim.
- Add perf test entries for checking --cpu in record and stat, from
Jiri Olsa.
- Introduce perf stat --repeat forever, from Frederik Deweerdt.
- Add --no-demangle to report/top, from Namhyung Kim.
- PowerPC fixes plus a couple of cleanups/optimizations in uprobes
and trace_uprobes, by Oleg Nesterov.
Various fixes and refactorings:
- Fix dependency of the python binding wrt libtraceevent, from
Naohiro Aota.
- Simplify some perf_evlist methods and to allow 'stat' to share code
with 'record' and 'trace', by Arnaldo Carvalho de Melo.
- Remove dead code in related to libtraceevent integration, from
Namhyung Kim.
- Revert "perf sched: Handle PERF_RECORD_EXIT events" to get 'perf
sched lat' back working, by Arnaldo Carvalho de Melo
- We don't use Newt anymore, just plain libslang, by Arnaldo Carvalho
de Melo.
- Kill a bunch of die() calls, from Namhyung Kim.
- Fix build on non-glibc systems due to libio.h absence, from Cody P
Schafer.
- Remove some perf_session and tracing dead code, from David Ahern.
- Honor parallel jobs, fix from Borislav Petkov
- Introduce tools/lib/lk library, initially just removing duplication
among tools/perf and tools/vm. from Borislav Petkov
... and many more I missed to list, see the shortlog and git log for
more details."
* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (136 commits)
perf/x86/intel/P4: Robistify P4 PMU types
perf/x86/amd: Fix AMD NB and L2I "uncore" support
perf/x86/amd: Remove old-style NB counter support from perf_event_amd.c
perf/x86: Check all MSRs before passing hw check
perf/x86/amd: Add support for AMD NB and L2I "uncore" counters
perf/x86/intel: Add Ivy Bridge-EP uncore support
perf/x86/intel: Fix SNB-EP CBO and PCU uncore PMU filter management
perf/x86: Avoid kfree() in CPU_{STARTING,DYING}
uprobes/perf: Avoid perf_trace_buf_prepare/submit if ->perf_events is empty
uprobes/tracing: Don't pass addr=ip to perf_trace_buf_submit()
uprobes/tracing: Change create_trace_uprobe() to support uretprobes
uprobes/tracing: Make seq_printf() code uretprobe-friendly
uprobes/tracing: Make register_uprobe_event() paths uretprobe-friendly
uprobes/tracing: Make uprobe_{trace,perf}_print() uretprobe-friendly
uprobes/tracing: Introduce is_ret_probe() and uretprobe_dispatcher()
uprobes/tracing: Introduce uprobe_{trace,perf}_print() helpers
uprobes/tracing: Generalize struct uprobe_trace_entry_head
uprobes/tracing: Kill the pointless local_save_flags/preempt_count calls
uprobes/tracing: Kill the pointless seq_print_ip_sym() call
uprobes/tracing: Kill the pointless task_pt_regs() calls
...
130 files changed, 5670 insertions, 1708 deletions
diff --git a/Documentation/trace/uprobetracer.txt b/Documentation/trace/uprobetracer.txt index 24ce6823a09e..d9c3e682312c 100644 --- a/Documentation/trace/uprobetracer.txt +++ b/Documentation/trace/uprobetracer.txt | |||
@@ -1,6 +1,8 @@ | |||
1 | Uprobe-tracer: Uprobe-based Event Tracing | 1 | Uprobe-tracer: Uprobe-based Event Tracing |
2 | ========================================= | 2 | ========================================= |
3 | Documentation written by Srikar Dronamraju | 3 | |
4 | Documentation written by Srikar Dronamraju | ||
5 | |||
4 | 6 | ||
5 | Overview | 7 | Overview |
6 | -------- | 8 | -------- |
@@ -13,78 +15,94 @@ current_tracer. Instead of that, add probe points via | |||
13 | /sys/kernel/debug/tracing/events/uprobes/<EVENT>/enabled. | 15 | /sys/kernel/debug/tracing/events/uprobes/<EVENT>/enabled. |
14 | 16 | ||
15 | However unlike kprobe-event tracer, the uprobe event interface expects the | 17 | However unlike kprobe-event tracer, the uprobe event interface expects the |
16 | user to calculate the offset of the probepoint in the object | 18 | user to calculate the offset of the probepoint in the object. |
17 | 19 | ||
18 | Synopsis of uprobe_tracer | 20 | Synopsis of uprobe_tracer |
19 | ------------------------- | 21 | ------------------------- |
20 | p[:[GRP/]EVENT] PATH:SYMBOL[+offs] [FETCHARGS] : Set a probe | 22 | p[:[GRP/]EVENT] PATH:SYMBOL[+offs] [FETCHARGS] : Set a uprobe |
23 | r[:[GRP/]EVENT] PATH:SYMBOL[+offs] [FETCHARGS] : Set a return uprobe (uretprobe) | ||
24 | -:[GRP/]EVENT : Clear uprobe or uretprobe event | ||
21 | 25 | ||
22 | GRP : Group name. If omitted, use "uprobes" for it. | 26 | GRP : Group name. If omitted, "uprobes" is the default value. |
23 | EVENT : Event name. If omitted, the event name is generated | 27 | EVENT : Event name. If omitted, the event name is generated based |
24 | based on SYMBOL+offs. | 28 | on SYMBOL+offs. |
25 | PATH : path to an executable or a library. | 29 | PATH : Path to an executable or a library. |
26 | SYMBOL[+offs] : Symbol+offset where the probe is inserted. | 30 | SYMBOL[+offs] : Symbol+offset where the probe is inserted. |
27 | 31 | ||
28 | FETCHARGS : Arguments. Each probe can have up to 128 args. | 32 | FETCHARGS : Arguments. Each probe can have up to 128 args. |
29 | %REG : Fetch register REG | 33 | %REG : Fetch register REG |
30 | 34 | ||
31 | Event Profiling | 35 | Event Profiling |
32 | --------------- | 36 | --------------- |
33 | You can check the total number of probe hits and probe miss-hits via | 37 | You can check the total number of probe hits and probe miss-hits via |
34 | /sys/kernel/debug/tracing/uprobe_profile. | 38 | /sys/kernel/debug/tracing/uprobe_profile. |
35 | The first column is event name, the second is the number of probe hits, | 39 | The first column is event name, the second is the number of probe hits, |
36 | the third is the number of probe miss-hits. | 40 | the third is the number of probe miss-hits. |
37 | 41 | ||
38 | Usage examples | 42 | Usage examples |
39 | -------------- | 43 | -------------- |
40 | To add a probe as a new event, write a new definition to uprobe_events | 44 | * Add a probe as a new uprobe event, write a new definition to uprobe_events |
41 | as below. | 45 | as below: (sets a uprobe at an offset of 0x4245c0 in the executable /bin/bash) |
46 | |||
47 | echo 'p: /bin/bash:0x4245c0' > /sys/kernel/debug/tracing/uprobe_events | ||
48 | |||
49 | * Add a probe as a new uretprobe event: | ||
50 | |||
51 | echo 'r: /bin/bash:0x4245c0' > /sys/kernel/debug/tracing/uprobe_events | ||
52 | |||
53 | * Unset registered event: | ||
42 | 54 | ||
43 | echo 'p: /bin/bash:0x4245c0' > /sys/kernel/debug/tracing/uprobe_events | 55 | echo '-:bash_0x4245c0' >> /sys/kernel/debug/tracing/uprobe_events |
44 | 56 | ||
45 | This sets a uprobe at an offset of 0x4245c0 in the executable /bin/bash | 57 | * Print out the events that are registered: |
46 | 58 | ||
47 | echo > /sys/kernel/debug/tracing/uprobe_events | 59 | cat /sys/kernel/debug/tracing/uprobe_events |
48 | 60 | ||
49 | This clears all probe points. | 61 | * Clear all events: |
50 | 62 | ||
51 | The following example shows how to dump the instruction pointer and %ax | 63 | echo > /sys/kernel/debug/tracing/uprobe_events |
52 | a register at the probed text address. Here we are trying to probe | 64 | |
53 | function zfree in /bin/zsh | 65 | Following example shows how to dump the instruction pointer and %ax register |
66 | at the probed text address. Probe zfree function in /bin/zsh: | ||
54 | 67 | ||
55 | # cd /sys/kernel/debug/tracing/ | 68 | # cd /sys/kernel/debug/tracing/ |
56 | # cat /proc/`pgrep zsh`/maps | grep /bin/zsh | grep r-xp | 69 | # cat /proc/`pgrep zsh`/maps | grep /bin/zsh | grep r-xp |
57 | 00400000-0048a000 r-xp 00000000 08:03 130904 /bin/zsh | 70 | 00400000-0048a000 r-xp 00000000 08:03 130904 /bin/zsh |
58 | # objdump -T /bin/zsh | grep -w zfree | 71 | # objdump -T /bin/zsh | grep -w zfree |
59 | 0000000000446420 g DF .text 0000000000000012 Base zfree | 72 | 0000000000446420 g DF .text 0000000000000012 Base zfree |
60 | 73 | ||
61 | 0x46420 is the offset of zfree in object /bin/zsh that is loaded at | 74 | 0x46420 is the offset of zfree in object /bin/zsh that is loaded at |
62 | 0x00400000. Hence the command to probe would be : | 75 | 0x00400000. Hence the command to uprobe would be: |
76 | |||
77 | # echo 'p:zfree_entry /bin/zsh:0x46420 %ip %ax' > uprobe_events | ||
78 | |||
79 | And the same for the uretprobe would be: | ||
63 | 80 | ||
64 | # echo 'p /bin/zsh:0x46420 %ip %ax' > uprobe_events | 81 | # echo 'r:zfree_exit /bin/zsh:0x46420 %ip %ax' >> uprobe_events |
65 | 82 | ||
66 | Please note: User has to explicitly calculate the offset of the probepoint | 83 | Please note: User has to explicitly calculate the offset of the probe-point |
67 | in the object. We can see the events that are registered by looking at the | 84 | in the object. We can see the events that are registered by looking at the |
68 | uprobe_events file. | 85 | uprobe_events file. |
69 | 86 | ||
70 | # cat uprobe_events | 87 | # cat uprobe_events |
71 | p:uprobes/p_zsh_0x46420 /bin/zsh:0x00046420 arg1=%ip arg2=%ax | 88 | p:uprobes/zfree_entry /bin/zsh:0x00046420 arg1=%ip arg2=%ax |
89 | r:uprobes/zfree_exit /bin/zsh:0x00046420 arg1=%ip arg2=%ax | ||
72 | 90 | ||
73 | The format of events can be seen by viewing the file events/uprobes/p_zsh_0x46420/format | 91 | Format of events can be seen by viewing the file events/uprobes/zfree_entry/format |
74 | 92 | ||
75 | # cat events/uprobes/p_zsh_0x46420/format | 93 | # cat events/uprobes/zfree_entry/format |
76 | name: p_zsh_0x46420 | 94 | name: zfree_entry |
77 | ID: 922 | 95 | ID: 922 |
78 | format: | 96 | format: |
79 | field:unsigned short common_type; offset:0; size:2; signed:0; | 97 | field:unsigned short common_type; offset:0; size:2; signed:0; |
80 | field:unsigned char common_flags; offset:2; size:1; signed:0; | 98 | field:unsigned char common_flags; offset:2; size:1; signed:0; |
81 | field:unsigned char common_preempt_count; offset:3; size:1; signed:0; | 99 | field:unsigned char common_preempt_count; offset:3; size:1; signed:0; |
82 | field:int common_pid; offset:4; size:4; signed:1; | 100 | field:int common_pid; offset:4; size:4; signed:1; |
83 | field:int common_padding; offset:8; size:4; signed:1; | 101 | field:int common_padding; offset:8; size:4; signed:1; |
84 | 102 | ||
85 | field:unsigned long __probe_ip; offset:12; size:4; signed:0; | 103 | field:unsigned long __probe_ip; offset:12; size:4; signed:0; |
86 | field:u32 arg1; offset:16; size:4; signed:0; | 104 | field:u32 arg1; offset:16; size:4; signed:0; |
87 | field:u32 arg2; offset:20; size:4; signed:0; | 105 | field:u32 arg2; offset:20; size:4; signed:0; |
88 | 106 | ||
89 | print fmt: "(%lx) arg1=%lx arg2=%lx", REC->__probe_ip, REC->arg1, REC->arg2 | 107 | print fmt: "(%lx) arg1=%lx arg2=%lx", REC->__probe_ip, REC->arg1, REC->arg2 |
90 | 108 | ||
@@ -94,6 +112,7 @@ events, you need to enable it by: | |||
94 | # echo 1 > events/uprobes/enable | 112 | # echo 1 > events/uprobes/enable |
95 | 113 | ||
96 | Lets disable the event after sleeping for some time. | 114 | Lets disable the event after sleeping for some time. |
115 | |||
97 | # sleep 20 | 116 | # sleep 20 |
98 | # echo 0 > events/uprobes/enable | 117 | # echo 0 > events/uprobes/enable |
99 | 118 | ||
@@ -104,10 +123,11 @@ And you can see the traced information via /sys/kernel/debug/tracing/trace. | |||
104 | # | 123 | # |
105 | # TASK-PID CPU# TIMESTAMP FUNCTION | 124 | # TASK-PID CPU# TIMESTAMP FUNCTION |
106 | # | | | | | | 125 | # | | | | | |
107 | zsh-24842 [006] 258544.995456: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79 | 126 | zsh-24842 [006] 258544.995456: zfree_entry: (0x446420) arg1=446420 arg2=79 |
108 | zsh-24842 [007] 258545.000270: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79 | 127 | zsh-24842 [007] 258545.000270: zfree_exit: (0x446540 <- 0x446420) arg1=446540 arg2=0 |
109 | zsh-24842 [002] 258545.043929: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79 | 128 | zsh-24842 [002] 258545.043929: zfree_entry: (0x446420) arg1=446420 arg2=79 |
110 | zsh-24842 [004] 258547.046129: p_zsh_0x46420: (0x446420) arg1=446421 arg2=79 | 129 | zsh-24842 [004] 258547.046129: zfree_exit: (0x446540 <- 0x446420) arg1=446540 arg2=0 |
111 | 130 | ||
112 | Each line shows us probes were triggered for a pid 24842 with ip being | 131 | Output shows us uprobe was triggered for a pid 24842 with ip being 0x446420 |
113 | 0x446421 and contents of ax register being 79. | 132 | and contents of ax register being 79. And uretprobe was triggered with ip at |
133 | 0x446540 with counterpart function entry at 0x446420. | ||
@@ -1332,11 +1332,11 @@ kernelversion: | |||
1332 | # Clear a bunch of variables before executing the submake | 1332 | # Clear a bunch of variables before executing the submake |
1333 | tools/: FORCE | 1333 | tools/: FORCE |
1334 | $(Q)mkdir -p $(objtree)/tools | 1334 | $(Q)mkdir -p $(objtree)/tools |
1335 | $(Q)$(MAKE) LDFLAGS= MAKEFLAGS= O=$(objtree) subdir=tools -C $(src)/tools/ | 1335 | $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(objtree) subdir=tools -C $(src)/tools/ |
1336 | 1336 | ||
1337 | tools/%: FORCE | 1337 | tools/%: FORCE |
1338 | $(Q)mkdir -p $(objtree)/tools | 1338 | $(Q)mkdir -p $(objtree)/tools |
1339 | $(Q)$(MAKE) LDFLAGS= MAKEFLAGS= O=$(objtree) subdir=tools -C $(src)/tools/ $* | 1339 | $(Q)$(MAKE) LDFLAGS= MAKEFLAGS="$(filter --j% -j,$(MAKEFLAGS))" O=$(objtree) subdir=tools -C $(src)/tools/ $* |
1340 | 1340 | ||
1341 | # Single targets | 1341 | # Single targets |
1342 | # --------------------------------------------------------------------------- | 1342 | # --------------------------------------------------------------------------- |
diff --git a/arch/powerpc/include/asm/uprobes.h b/arch/powerpc/include/asm/uprobes.h index b532060d0916..23016020915e 100644 --- a/arch/powerpc/include/asm/uprobes.h +++ b/arch/powerpc/include/asm/uprobes.h | |||
@@ -51,4 +51,5 @@ extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); | |||
51 | extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); | 51 | extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); |
52 | extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); | 52 | extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); |
53 | extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); | 53 | extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); |
54 | extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); | ||
54 | #endif /* _ASM_UPROBES_H */ | 55 | #endif /* _ASM_UPROBES_H */ |
diff --git a/arch/powerpc/kernel/uprobes.c b/arch/powerpc/kernel/uprobes.c index bc77834dbf43..59f419b935f2 100644 --- a/arch/powerpc/kernel/uprobes.c +++ b/arch/powerpc/kernel/uprobes.c | |||
@@ -31,6 +31,16 @@ | |||
31 | #define UPROBE_TRAP_NR UINT_MAX | 31 | #define UPROBE_TRAP_NR UINT_MAX |
32 | 32 | ||
33 | /** | 33 | /** |
34 | * is_trap_insn - check if the instruction is a trap variant | ||
35 | * @insn: instruction to be checked. | ||
36 | * Returns true if @insn is a trap variant. | ||
37 | */ | ||
38 | bool is_trap_insn(uprobe_opcode_t *insn) | ||
39 | { | ||
40 | return (is_trap(*insn)); | ||
41 | } | ||
42 | |||
43 | /** | ||
34 | * arch_uprobe_analyze_insn | 44 | * arch_uprobe_analyze_insn |
35 | * @mm: the probed address space. | 45 | * @mm: the probed address space. |
36 | * @arch_uprobe: the probepoint information. | 46 | * @arch_uprobe: the probepoint information. |
@@ -43,12 +53,6 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, | |||
43 | if (addr & 0x03) | 53 | if (addr & 0x03) |
44 | return -EINVAL; | 54 | return -EINVAL; |
45 | 55 | ||
46 | /* | ||
47 | * We currently don't support a uprobe on an already | ||
48 | * existing breakpoint instruction underneath | ||
49 | */ | ||
50 | if (is_trap(auprobe->ainsn)) | ||
51 | return -ENOTSUPP; | ||
52 | return 0; | 56 | return 0; |
53 | } | 57 | } |
54 | 58 | ||
@@ -188,3 +192,16 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
188 | 192 | ||
189 | return false; | 193 | return false; |
190 | } | 194 | } |
195 | |||
196 | unsigned long | ||
197 | arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs) | ||
198 | { | ||
199 | unsigned long orig_ret_vaddr; | ||
200 | |||
201 | orig_ret_vaddr = regs->link; | ||
202 | |||
203 | /* Replace the return addr with trampoline addr */ | ||
204 | regs->link = trampoline_vaddr; | ||
205 | |||
206 | return orig_ret_vaddr; | ||
207 | } | ||
diff --git a/arch/s390/oprofile/init.c b/arch/s390/oprofile/init.c index 584b93674ea4..ffeb17ce7f31 100644 --- a/arch/s390/oprofile/init.c +++ b/arch/s390/oprofile/init.c | |||
@@ -440,6 +440,7 @@ static int oprofile_hwsampler_init(struct oprofile_operations *ops) | |||
440 | switch (id.machine) { | 440 | switch (id.machine) { |
441 | case 0x2097: case 0x2098: ops->cpu_type = "s390/z10"; break; | 441 | case 0x2097: case 0x2098: ops->cpu_type = "s390/z10"; break; |
442 | case 0x2817: case 0x2818: ops->cpu_type = "s390/z196"; break; | 442 | case 0x2817: case 0x2818: ops->cpu_type = "s390/z196"; break; |
443 | case 0x2827: ops->cpu_type = "s390/zEC12"; break; | ||
443 | default: return -ENODEV; | 444 | default: return -ENODEV; |
444 | } | 445 | } |
445 | } | 446 | } |
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h index 93fe929d1cee..ac10df72925b 100644 --- a/arch/x86/include/asm/cpufeature.h +++ b/arch/x86/include/asm/cpufeature.h | |||
@@ -168,6 +168,7 @@ | |||
168 | #define X86_FEATURE_TOPOEXT (6*32+22) /* topology extensions CPUID leafs */ | 168 | #define X86_FEATURE_TOPOEXT (6*32+22) /* topology extensions CPUID leafs */ |
169 | #define X86_FEATURE_PERFCTR_CORE (6*32+23) /* core performance counter extensions */ | 169 | #define X86_FEATURE_PERFCTR_CORE (6*32+23) /* core performance counter extensions */ |
170 | #define X86_FEATURE_PERFCTR_NB (6*32+24) /* NB performance counter extensions */ | 170 | #define X86_FEATURE_PERFCTR_NB (6*32+24) /* NB performance counter extensions */ |
171 | #define X86_FEATURE_PERFCTR_L2 (6*32+28) /* L2 performance counter extensions */ | ||
171 | 172 | ||
172 | /* | 173 | /* |
173 | * Auxiliary flags: Linux defined - For features scattered in various | 174 | * Auxiliary flags: Linux defined - For features scattered in various |
@@ -311,6 +312,7 @@ extern const char * const x86_power_flags[32]; | |||
311 | #define cpu_has_pclmulqdq boot_cpu_has(X86_FEATURE_PCLMULQDQ) | 312 | #define cpu_has_pclmulqdq boot_cpu_has(X86_FEATURE_PCLMULQDQ) |
312 | #define cpu_has_perfctr_core boot_cpu_has(X86_FEATURE_PERFCTR_CORE) | 313 | #define cpu_has_perfctr_core boot_cpu_has(X86_FEATURE_PERFCTR_CORE) |
313 | #define cpu_has_perfctr_nb boot_cpu_has(X86_FEATURE_PERFCTR_NB) | 314 | #define cpu_has_perfctr_nb boot_cpu_has(X86_FEATURE_PERFCTR_NB) |
315 | #define cpu_has_perfctr_l2 boot_cpu_has(X86_FEATURE_PERFCTR_L2) | ||
314 | #define cpu_has_cx8 boot_cpu_has(X86_FEATURE_CX8) | 316 | #define cpu_has_cx8 boot_cpu_has(X86_FEATURE_CX8) |
315 | #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) | 317 | #define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16) |
316 | #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) | 318 | #define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU) |
diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index 4f7e67e2345e..85e13ccf15c4 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h | |||
@@ -24,45 +24,45 @@ | |||
24 | #define ARCH_P4_CNTRVAL_MASK ((1ULL << ARCH_P4_CNTRVAL_BITS) - 1) | 24 | #define ARCH_P4_CNTRVAL_MASK ((1ULL << ARCH_P4_CNTRVAL_BITS) - 1) |
25 | #define ARCH_P4_UNFLAGGED_BIT ((1ULL) << (ARCH_P4_CNTRVAL_BITS - 1)) | 25 | #define ARCH_P4_UNFLAGGED_BIT ((1ULL) << (ARCH_P4_CNTRVAL_BITS - 1)) |
26 | 26 | ||
27 | #define P4_ESCR_EVENT_MASK 0x7e000000U | 27 | #define P4_ESCR_EVENT_MASK 0x7e000000ULL |
28 | #define P4_ESCR_EVENT_SHIFT 25 | 28 | #define P4_ESCR_EVENT_SHIFT 25 |
29 | #define P4_ESCR_EVENTMASK_MASK 0x01fffe00U | 29 | #define P4_ESCR_EVENTMASK_MASK 0x01fffe00ULL |
30 | #define P4_ESCR_EVENTMASK_SHIFT 9 | 30 | #define P4_ESCR_EVENTMASK_SHIFT 9 |
31 | #define P4_ESCR_TAG_MASK 0x000001e0U | 31 | #define P4_ESCR_TAG_MASK 0x000001e0ULL |
32 | #define P4_ESCR_TAG_SHIFT 5 | 32 | #define P4_ESCR_TAG_SHIFT 5 |
33 | #define P4_ESCR_TAG_ENABLE 0x00000010U | 33 | #define P4_ESCR_TAG_ENABLE 0x00000010ULL |
34 | #define P4_ESCR_T0_OS 0x00000008U | 34 | #define P4_ESCR_T0_OS 0x00000008ULL |
35 | #define P4_ESCR_T0_USR 0x00000004U | 35 | #define P4_ESCR_T0_USR 0x00000004ULL |
36 | #define P4_ESCR_T1_OS 0x00000002U | 36 | #define P4_ESCR_T1_OS 0x00000002ULL |
37 | #define P4_ESCR_T1_USR 0x00000001U | 37 | #define P4_ESCR_T1_USR 0x00000001ULL |
38 | 38 | ||
39 | #define P4_ESCR_EVENT(v) ((v) << P4_ESCR_EVENT_SHIFT) | 39 | #define P4_ESCR_EVENT(v) ((v) << P4_ESCR_EVENT_SHIFT) |
40 | #define P4_ESCR_EMASK(v) ((v) << P4_ESCR_EVENTMASK_SHIFT) | 40 | #define P4_ESCR_EMASK(v) ((v) << P4_ESCR_EVENTMASK_SHIFT) |
41 | #define P4_ESCR_TAG(v) ((v) << P4_ESCR_TAG_SHIFT) | 41 | #define P4_ESCR_TAG(v) ((v) << P4_ESCR_TAG_SHIFT) |
42 | 42 | ||
43 | #define P4_CCCR_OVF 0x80000000U | 43 | #define P4_CCCR_OVF 0x80000000ULL |
44 | #define P4_CCCR_CASCADE 0x40000000U | 44 | #define P4_CCCR_CASCADE 0x40000000ULL |
45 | #define P4_CCCR_OVF_PMI_T0 0x04000000U | 45 | #define P4_CCCR_OVF_PMI_T0 0x04000000ULL |
46 | #define P4_CCCR_OVF_PMI_T1 0x08000000U | 46 | #define P4_CCCR_OVF_PMI_T1 0x08000000ULL |
47 | #define P4_CCCR_FORCE_OVF 0x02000000U | 47 | #define P4_CCCR_FORCE_OVF 0x02000000ULL |
48 | #define P4_CCCR_EDGE 0x01000000U | 48 | #define P4_CCCR_EDGE 0x01000000ULL |
49 | #define P4_CCCR_THRESHOLD_MASK 0x00f00000U | 49 | #define P4_CCCR_THRESHOLD_MASK 0x00f00000ULL |
50 | #define P4_CCCR_THRESHOLD_SHIFT 20 | 50 | #define P4_CCCR_THRESHOLD_SHIFT 20 |
51 | #define P4_CCCR_COMPLEMENT 0x00080000U | 51 | #define P4_CCCR_COMPLEMENT 0x00080000ULL |
52 | #define P4_CCCR_COMPARE 0x00040000U | 52 | #define P4_CCCR_COMPARE 0x00040000ULL |
53 | #define P4_CCCR_ESCR_SELECT_MASK 0x0000e000U | 53 | #define P4_CCCR_ESCR_SELECT_MASK 0x0000e000ULL |
54 | #define P4_CCCR_ESCR_SELECT_SHIFT 13 | 54 | #define P4_CCCR_ESCR_SELECT_SHIFT 13 |
55 | #define P4_CCCR_ENABLE 0x00001000U | 55 | #define P4_CCCR_ENABLE 0x00001000ULL |
56 | #define P4_CCCR_THREAD_SINGLE 0x00010000U | 56 | #define P4_CCCR_THREAD_SINGLE 0x00010000ULL |
57 | #define P4_CCCR_THREAD_BOTH 0x00020000U | 57 | #define P4_CCCR_THREAD_BOTH 0x00020000ULL |
58 | #define P4_CCCR_THREAD_ANY 0x00030000U | 58 | #define P4_CCCR_THREAD_ANY 0x00030000ULL |
59 | #define P4_CCCR_RESERVED 0x00000fffU | 59 | #define P4_CCCR_RESERVED 0x00000fffULL |
60 | 60 | ||
61 | #define P4_CCCR_THRESHOLD(v) ((v) << P4_CCCR_THRESHOLD_SHIFT) | 61 | #define P4_CCCR_THRESHOLD(v) ((v) << P4_CCCR_THRESHOLD_SHIFT) |
62 | #define P4_CCCR_ESEL(v) ((v) << P4_CCCR_ESCR_SELECT_SHIFT) | 62 | #define P4_CCCR_ESEL(v) ((v) << P4_CCCR_ESCR_SELECT_SHIFT) |
63 | 63 | ||
64 | #define P4_GEN_ESCR_EMASK(class, name, bit) \ | 64 | #define P4_GEN_ESCR_EMASK(class, name, bit) \ |
65 | class##__##name = ((1 << bit) << P4_ESCR_EVENTMASK_SHIFT) | 65 | class##__##name = ((1ULL << bit) << P4_ESCR_EVENTMASK_SHIFT) |
66 | #define P4_ESCR_EMASK_BIT(class, name) class##__##name | 66 | #define P4_ESCR_EMASK_BIT(class, name) class##__##name |
67 | 67 | ||
68 | /* | 68 | /* |
@@ -107,7 +107,7 @@ | |||
107 | * P4_PEBS_CONFIG_MASK and related bits on | 107 | * P4_PEBS_CONFIG_MASK and related bits on |
108 | * modification.) | 108 | * modification.) |
109 | */ | 109 | */ |
110 | #define P4_CONFIG_ALIASABLE (1 << 9) | 110 | #define P4_CONFIG_ALIASABLE (1ULL << 9) |
111 | 111 | ||
112 | /* | 112 | /* |
113 | * The bits we allow to pass for RAW events | 113 | * The bits we allow to pass for RAW events |
@@ -784,17 +784,17 @@ enum P4_ESCR_EMASKS { | |||
784 | * Note we have UOP and PEBS bits reserved for now | 784 | * Note we have UOP and PEBS bits reserved for now |
785 | * just in case if we will need them once | 785 | * just in case if we will need them once |
786 | */ | 786 | */ |
787 | #define P4_PEBS_CONFIG_ENABLE (1 << 7) | 787 | #define P4_PEBS_CONFIG_ENABLE (1ULL << 7) |
788 | #define P4_PEBS_CONFIG_UOP_TAG (1 << 8) | 788 | #define P4_PEBS_CONFIG_UOP_TAG (1ULL << 8) |
789 | #define P4_PEBS_CONFIG_METRIC_MASK 0x3f | 789 | #define P4_PEBS_CONFIG_METRIC_MASK 0x3FLL |
790 | #define P4_PEBS_CONFIG_MASK 0xff | 790 | #define P4_PEBS_CONFIG_MASK 0xFFLL |
791 | 791 | ||
792 | /* | 792 | /* |
793 | * mem: Only counters MSR_IQ_COUNTER4 (16) and | 793 | * mem: Only counters MSR_IQ_COUNTER4 (16) and |
794 | * MSR_IQ_COUNTER5 (17) are allowed for PEBS sampling | 794 | * MSR_IQ_COUNTER5 (17) are allowed for PEBS sampling |
795 | */ | 795 | */ |
796 | #define P4_PEBS_ENABLE 0x02000000U | 796 | #define P4_PEBS_ENABLE 0x02000000ULL |
797 | #define P4_PEBS_ENABLE_UOP_TAG 0x01000000U | 797 | #define P4_PEBS_ENABLE_UOP_TAG 0x01000000ULL |
798 | 798 | ||
799 | #define p4_config_unpack_metric(v) (((u64)(v)) & P4_PEBS_CONFIG_METRIC_MASK) | 799 | #define p4_config_unpack_metric(v) (((u64)(v)) & P4_PEBS_CONFIG_METRIC_MASK) |
800 | #define p4_config_unpack_pebs(v) (((u64)(v)) & P4_PEBS_CONFIG_MASK) | 800 | #define p4_config_unpack_pebs(v) (((u64)(v)) & P4_PEBS_CONFIG_MASK) |
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 8ff8be7835ab..6e5197910fd8 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h | |||
@@ -55,4 +55,5 @@ extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); | |||
55 | extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); | 55 | extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); |
56 | extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); | 56 | extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); |
57 | extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); | 57 | extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); |
58 | extern unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs); | ||
58 | #endif /* _ASM_UPROBES_H */ | 59 | #endif /* _ASM_UPROBES_H */ |
diff --git a/arch/x86/include/uapi/asm/msr-index.h b/arch/x86/include/uapi/asm/msr-index.h index 7a060f4b411f..b5757885d7a4 100644 --- a/arch/x86/include/uapi/asm/msr-index.h +++ b/arch/x86/include/uapi/asm/msr-index.h | |||
@@ -72,6 +72,7 @@ | |||
72 | #define MSR_IA32_PEBS_ENABLE 0x000003f1 | 72 | #define MSR_IA32_PEBS_ENABLE 0x000003f1 |
73 | #define MSR_IA32_DS_AREA 0x00000600 | 73 | #define MSR_IA32_DS_AREA 0x00000600 |
74 | #define MSR_IA32_PERF_CAPABILITIES 0x00000345 | 74 | #define MSR_IA32_PERF_CAPABILITIES 0x00000345 |
75 | #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 | ||
75 | 76 | ||
76 | #define MSR_MTRRfix64K_00000 0x00000250 | 77 | #define MSR_MTRRfix64K_00000 0x00000250 |
77 | #define MSR_MTRRfix16K_80000 0x00000258 | 78 | #define MSR_MTRRfix16K_80000 0x00000258 |
@@ -195,6 +196,10 @@ | |||
195 | #define MSR_AMD64_IBSBRTARGET 0xc001103b | 196 | #define MSR_AMD64_IBSBRTARGET 0xc001103b |
196 | #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ | 197 | #define MSR_AMD64_IBS_REG_COUNT_MAX 8 /* includes MSR_AMD64_IBSBRTARGET */ |
197 | 198 | ||
199 | /* Fam 16h MSRs */ | ||
200 | #define MSR_F16H_L2I_PERF_CTL 0xc0010230 | ||
201 | #define MSR_F16H_L2I_PERF_CTR 0xc0010231 | ||
202 | |||
198 | /* Fam 15h MSRs */ | 203 | /* Fam 15h MSRs */ |
199 | #define MSR_F15H_PERF_CTL 0xc0010200 | 204 | #define MSR_F15H_PERF_CTL 0xc0010200 |
200 | #define MSR_F15H_PERF_CTR 0xc0010201 | 205 | #define MSR_F15H_PERF_CTR 0xc0010201 |
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index c9496313843b..deef0399fc78 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile | |||
@@ -31,7 +31,7 @@ obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o | |||
31 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o | 31 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o |
32 | 32 | ||
33 | ifdef CONFIG_PERF_EVENTS | 33 | ifdef CONFIG_PERF_EVENTS |
34 | obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd.o | 34 | obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd.o perf_event_amd_uncore.o |
35 | obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_knc.o perf_event_p4.o | 35 | obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_knc.o perf_event_p4.o |
36 | obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o | 36 | obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o |
37 | obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore.o | 37 | obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore.o |
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index bf0f01aea994..1025f3c99d20 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c | |||
@@ -180,8 +180,9 @@ static void release_pmc_hardware(void) {} | |||
180 | 180 | ||
181 | static bool check_hw_exists(void) | 181 | static bool check_hw_exists(void) |
182 | { | 182 | { |
183 | u64 val, val_new = ~0; | 183 | u64 val, val_fail, val_new= ~0; |
184 | int i, reg, ret = 0; | 184 | int i, reg, reg_fail, ret = 0; |
185 | int bios_fail = 0; | ||
185 | 186 | ||
186 | /* | 187 | /* |
187 | * Check to see if the BIOS enabled any of the counters, if so | 188 | * Check to see if the BIOS enabled any of the counters, if so |
@@ -192,8 +193,11 @@ static bool check_hw_exists(void) | |||
192 | ret = rdmsrl_safe(reg, &val); | 193 | ret = rdmsrl_safe(reg, &val); |
193 | if (ret) | 194 | if (ret) |
194 | goto msr_fail; | 195 | goto msr_fail; |
195 | if (val & ARCH_PERFMON_EVENTSEL_ENABLE) | 196 | if (val & ARCH_PERFMON_EVENTSEL_ENABLE) { |
196 | goto bios_fail; | 197 | bios_fail = 1; |
198 | val_fail = val; | ||
199 | reg_fail = reg; | ||
200 | } | ||
197 | } | 201 | } |
198 | 202 | ||
199 | if (x86_pmu.num_counters_fixed) { | 203 | if (x86_pmu.num_counters_fixed) { |
@@ -202,8 +206,11 @@ static bool check_hw_exists(void) | |||
202 | if (ret) | 206 | if (ret) |
203 | goto msr_fail; | 207 | goto msr_fail; |
204 | for (i = 0; i < x86_pmu.num_counters_fixed; i++) { | 208 | for (i = 0; i < x86_pmu.num_counters_fixed; i++) { |
205 | if (val & (0x03 << i*4)) | 209 | if (val & (0x03 << i*4)) { |
206 | goto bios_fail; | 210 | bios_fail = 1; |
211 | val_fail = val; | ||
212 | reg_fail = reg; | ||
213 | } | ||
207 | } | 214 | } |
208 | } | 215 | } |
209 | 216 | ||
@@ -221,14 +228,13 @@ static bool check_hw_exists(void) | |||
221 | if (ret || val != val_new) | 228 | if (ret || val != val_new) |
222 | goto msr_fail; | 229 | goto msr_fail; |
223 | 230 | ||
224 | return true; | ||
225 | |||
226 | bios_fail: | ||
227 | /* | 231 | /* |
228 | * We still allow the PMU driver to operate: | 232 | * We still allow the PMU driver to operate: |
229 | */ | 233 | */ |
230 | printk(KERN_CONT "Broken BIOS detected, complain to your hardware vendor.\n"); | 234 | if (bios_fail) { |
231 | printk(KERN_ERR FW_BUG "the BIOS has corrupted hw-PMU resources (MSR %x is %Lx)\n", reg, val); | 235 | printk(KERN_CONT "Broken BIOS detected, complain to your hardware vendor.\n"); |
236 | printk(KERN_ERR FW_BUG "the BIOS has corrupted hw-PMU resources (MSR %x is %Lx)\n", reg_fail, val_fail); | ||
237 | } | ||
232 | 238 | ||
233 | return true; | 239 | return true; |
234 | 240 | ||
@@ -1316,9 +1322,16 @@ static struct attribute_group x86_pmu_format_group = { | |||
1316 | */ | 1322 | */ |
1317 | static void __init filter_events(struct attribute **attrs) | 1323 | static void __init filter_events(struct attribute **attrs) |
1318 | { | 1324 | { |
1325 | struct device_attribute *d; | ||
1326 | struct perf_pmu_events_attr *pmu_attr; | ||
1319 | int i, j; | 1327 | int i, j; |
1320 | 1328 | ||
1321 | for (i = 0; attrs[i]; i++) { | 1329 | for (i = 0; attrs[i]; i++) { |
1330 | d = (struct device_attribute *)attrs[i]; | ||
1331 | pmu_attr = container_of(d, struct perf_pmu_events_attr, attr); | ||
1332 | /* str trumps id */ | ||
1333 | if (pmu_attr->event_str) | ||
1334 | continue; | ||
1322 | if (x86_pmu.event_map(i)) | 1335 | if (x86_pmu.event_map(i)) |
1323 | continue; | 1336 | continue; |
1324 | 1337 | ||
@@ -1330,22 +1343,45 @@ static void __init filter_events(struct attribute **attrs) | |||
1330 | } | 1343 | } |
1331 | } | 1344 | } |
1332 | 1345 | ||
1333 | static ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr, | 1346 | /* Merge two pointer arrays */ |
1347 | static __init struct attribute **merge_attr(struct attribute **a, struct attribute **b) | ||
1348 | { | ||
1349 | struct attribute **new; | ||
1350 | int j, i; | ||
1351 | |||
1352 | for (j = 0; a[j]; j++) | ||
1353 | ; | ||
1354 | for (i = 0; b[i]; i++) | ||
1355 | j++; | ||
1356 | j++; | ||
1357 | |||
1358 | new = kmalloc(sizeof(struct attribute *) * j, GFP_KERNEL); | ||
1359 | if (!new) | ||
1360 | return NULL; | ||
1361 | |||
1362 | j = 0; | ||
1363 | for (i = 0; a[i]; i++) | ||
1364 | new[j++] = a[i]; | ||
1365 | for (i = 0; b[i]; i++) | ||
1366 | new[j++] = b[i]; | ||
1367 | new[j] = NULL; | ||
1368 | |||
1369 | return new; | ||
1370 | } | ||
1371 | |||
1372 | ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr, | ||
1334 | char *page) | 1373 | char *page) |
1335 | { | 1374 | { |
1336 | struct perf_pmu_events_attr *pmu_attr = \ | 1375 | struct perf_pmu_events_attr *pmu_attr = \ |
1337 | container_of(attr, struct perf_pmu_events_attr, attr); | 1376 | container_of(attr, struct perf_pmu_events_attr, attr); |
1338 | |||
1339 | u64 config = x86_pmu.event_map(pmu_attr->id); | 1377 | u64 config = x86_pmu.event_map(pmu_attr->id); |
1340 | return x86_pmu.events_sysfs_show(page, config); | ||
1341 | } | ||
1342 | 1378 | ||
1343 | #define EVENT_VAR(_id) event_attr_##_id | 1379 | /* string trumps id */ |
1344 | #define EVENT_PTR(_id) &event_attr_##_id.attr.attr | 1380 | if (pmu_attr->event_str) |
1381 | return sprintf(page, "%s", pmu_attr->event_str); | ||
1345 | 1382 | ||
1346 | #define EVENT_ATTR(_name, _id) \ | 1383 | return x86_pmu.events_sysfs_show(page, config); |
1347 | PMU_EVENT_ATTR(_name, EVENT_VAR(_id), PERF_COUNT_HW_##_id, \ | 1384 | } |
1348 | events_sysfs_show) | ||
1349 | 1385 | ||
1350 | EVENT_ATTR(cpu-cycles, CPU_CYCLES ); | 1386 | EVENT_ATTR(cpu-cycles, CPU_CYCLES ); |
1351 | EVENT_ATTR(instructions, INSTRUCTIONS ); | 1387 | EVENT_ATTR(instructions, INSTRUCTIONS ); |
@@ -1459,16 +1495,27 @@ static int __init init_hw_perf_events(void) | |||
1459 | 1495 | ||
1460 | unconstrained = (struct event_constraint) | 1496 | unconstrained = (struct event_constraint) |
1461 | __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1, | 1497 | __EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1, |
1462 | 0, x86_pmu.num_counters, 0); | 1498 | 0, x86_pmu.num_counters, 0, 0); |
1463 | 1499 | ||
1464 | x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */ | 1500 | x86_pmu.attr_rdpmc = 1; /* enable userspace RDPMC usage by default */ |
1465 | x86_pmu_format_group.attrs = x86_pmu.format_attrs; | 1501 | x86_pmu_format_group.attrs = x86_pmu.format_attrs; |
1466 | 1502 | ||
1503 | if (x86_pmu.event_attrs) | ||
1504 | x86_pmu_events_group.attrs = x86_pmu.event_attrs; | ||
1505 | |||
1467 | if (!x86_pmu.events_sysfs_show) | 1506 | if (!x86_pmu.events_sysfs_show) |
1468 | x86_pmu_events_group.attrs = &empty_attrs; | 1507 | x86_pmu_events_group.attrs = &empty_attrs; |
1469 | else | 1508 | else |
1470 | filter_events(x86_pmu_events_group.attrs); | 1509 | filter_events(x86_pmu_events_group.attrs); |
1471 | 1510 | ||
1511 | if (x86_pmu.cpu_events) { | ||
1512 | struct attribute **tmp; | ||
1513 | |||
1514 | tmp = merge_attr(x86_pmu_events_group.attrs, x86_pmu.cpu_events); | ||
1515 | if (!WARN_ON(!tmp)) | ||
1516 | x86_pmu_events_group.attrs = tmp; | ||
1517 | } | ||
1518 | |||
1472 | pr_info("... version: %d\n", x86_pmu.version); | 1519 | pr_info("... version: %d\n", x86_pmu.version); |
1473 | pr_info("... bit width: %d\n", x86_pmu.cntval_bits); | 1520 | pr_info("... bit width: %d\n", x86_pmu.cntval_bits); |
1474 | pr_info("... generic registers: %d\n", x86_pmu.num_counters); | 1521 | pr_info("... generic registers: %d\n", x86_pmu.num_counters); |
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h index 7f5c75c2afdd..ba9aadfa683b 100644 --- a/arch/x86/kernel/cpu/perf_event.h +++ b/arch/x86/kernel/cpu/perf_event.h | |||
@@ -46,6 +46,7 @@ enum extra_reg_type { | |||
46 | EXTRA_REG_RSP_0 = 0, /* offcore_response_0 */ | 46 | EXTRA_REG_RSP_0 = 0, /* offcore_response_0 */ |
47 | EXTRA_REG_RSP_1 = 1, /* offcore_response_1 */ | 47 | EXTRA_REG_RSP_1 = 1, /* offcore_response_1 */ |
48 | EXTRA_REG_LBR = 2, /* lbr_select */ | 48 | EXTRA_REG_LBR = 2, /* lbr_select */ |
49 | EXTRA_REG_LDLAT = 3, /* ld_lat_threshold */ | ||
49 | 50 | ||
50 | EXTRA_REG_MAX /* number of entries needed */ | 51 | EXTRA_REG_MAX /* number of entries needed */ |
51 | }; | 52 | }; |
@@ -59,7 +60,13 @@ struct event_constraint { | |||
59 | u64 cmask; | 60 | u64 cmask; |
60 | int weight; | 61 | int weight; |
61 | int overlap; | 62 | int overlap; |
63 | int flags; | ||
62 | }; | 64 | }; |
65 | /* | ||
66 | * struct event_constraint flags | ||
67 | */ | ||
68 | #define PERF_X86_EVENT_PEBS_LDLAT 0x1 /* ld+ldlat data address sampling */ | ||
69 | #define PERF_X86_EVENT_PEBS_ST 0x2 /* st data address sampling */ | ||
63 | 70 | ||
64 | struct amd_nb { | 71 | struct amd_nb { |
65 | int nb_id; /* NorthBridge id */ | 72 | int nb_id; /* NorthBridge id */ |
@@ -170,16 +177,17 @@ struct cpu_hw_events { | |||
170 | void *kfree_on_online; | 177 | void *kfree_on_online; |
171 | }; | 178 | }; |
172 | 179 | ||
173 | #define __EVENT_CONSTRAINT(c, n, m, w, o) {\ | 180 | #define __EVENT_CONSTRAINT(c, n, m, w, o, f) {\ |
174 | { .idxmsk64 = (n) }, \ | 181 | { .idxmsk64 = (n) }, \ |
175 | .code = (c), \ | 182 | .code = (c), \ |
176 | .cmask = (m), \ | 183 | .cmask = (m), \ |
177 | .weight = (w), \ | 184 | .weight = (w), \ |
178 | .overlap = (o), \ | 185 | .overlap = (o), \ |
186 | .flags = f, \ | ||
179 | } | 187 | } |
180 | 188 | ||
181 | #define EVENT_CONSTRAINT(c, n, m) \ | 189 | #define EVENT_CONSTRAINT(c, n, m) \ |
182 | __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n), 0) | 190 | __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n), 0, 0) |
183 | 191 | ||
184 | /* | 192 | /* |
185 | * The overlap flag marks event constraints with overlapping counter | 193 | * The overlap flag marks event constraints with overlapping counter |
@@ -203,7 +211,7 @@ struct cpu_hw_events { | |||
203 | * and its counter masks must be kept at a minimum. | 211 | * and its counter masks must be kept at a minimum. |
204 | */ | 212 | */ |
205 | #define EVENT_CONSTRAINT_OVERLAP(c, n, m) \ | 213 | #define EVENT_CONSTRAINT_OVERLAP(c, n, m) \ |
206 | __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n), 1) | 214 | __EVENT_CONSTRAINT(c, n, m, HWEIGHT(n), 1, 0) |
207 | 215 | ||
208 | /* | 216 | /* |
209 | * Constraint on the Event code. | 217 | * Constraint on the Event code. |
@@ -231,6 +239,14 @@ struct cpu_hw_events { | |||
231 | #define INTEL_UEVENT_CONSTRAINT(c, n) \ | 239 | #define INTEL_UEVENT_CONSTRAINT(c, n) \ |
232 | EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) | 240 | EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK) |
233 | 241 | ||
242 | #define INTEL_PLD_CONSTRAINT(c, n) \ | ||
243 | __EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK, \ | ||
244 | HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_LDLAT) | ||
245 | |||
246 | #define INTEL_PST_CONSTRAINT(c, n) \ | ||
247 | __EVENT_CONSTRAINT(c, n, INTEL_ARCH_EVENT_MASK, \ | ||
248 | HWEIGHT(n), 0, PERF_X86_EVENT_PEBS_ST) | ||
249 | |||
234 | #define EVENT_CONSTRAINT_END \ | 250 | #define EVENT_CONSTRAINT_END \ |
235 | EVENT_CONSTRAINT(0, 0, 0) | 251 | EVENT_CONSTRAINT(0, 0, 0) |
236 | 252 | ||
@@ -260,12 +276,22 @@ struct extra_reg { | |||
260 | .msr = (ms), \ | 276 | .msr = (ms), \ |
261 | .config_mask = (m), \ | 277 | .config_mask = (m), \ |
262 | .valid_mask = (vm), \ | 278 | .valid_mask = (vm), \ |
263 | .idx = EXTRA_REG_##i \ | 279 | .idx = EXTRA_REG_##i, \ |
264 | } | 280 | } |
265 | 281 | ||
266 | #define INTEL_EVENT_EXTRA_REG(event, msr, vm, idx) \ | 282 | #define INTEL_EVENT_EXTRA_REG(event, msr, vm, idx) \ |
267 | EVENT_EXTRA_REG(event, msr, ARCH_PERFMON_EVENTSEL_EVENT, vm, idx) | 283 | EVENT_EXTRA_REG(event, msr, ARCH_PERFMON_EVENTSEL_EVENT, vm, idx) |
268 | 284 | ||
285 | #define INTEL_UEVENT_EXTRA_REG(event, msr, vm, idx) \ | ||
286 | EVENT_EXTRA_REG(event, msr, ARCH_PERFMON_EVENTSEL_EVENT | \ | ||
287 | ARCH_PERFMON_EVENTSEL_UMASK, vm, idx) | ||
288 | |||
289 | #define INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(c) \ | ||
290 | INTEL_UEVENT_EXTRA_REG(c, \ | ||
291 | MSR_PEBS_LD_LAT_THRESHOLD, \ | ||
292 | 0xffff, \ | ||
293 | LDLAT) | ||
294 | |||
269 | #define EVENT_EXTRA_END EVENT_EXTRA_REG(0, 0, 0, 0, RSP_0) | 295 | #define EVENT_EXTRA_END EVENT_EXTRA_REG(0, 0, 0, 0, RSP_0) |
270 | 296 | ||
271 | union perf_capabilities { | 297 | union perf_capabilities { |
@@ -355,8 +381,10 @@ struct x86_pmu { | |||
355 | */ | 381 | */ |
356 | int attr_rdpmc; | 382 | int attr_rdpmc; |
357 | struct attribute **format_attrs; | 383 | struct attribute **format_attrs; |
384 | struct attribute **event_attrs; | ||
358 | 385 | ||
359 | ssize_t (*events_sysfs_show)(char *page, u64 config); | 386 | ssize_t (*events_sysfs_show)(char *page, u64 config); |
387 | struct attribute **cpu_events; | ||
360 | 388 | ||
361 | /* | 389 | /* |
362 | * CPU Hotplug hooks | 390 | * CPU Hotplug hooks |
@@ -421,6 +449,23 @@ do { \ | |||
421 | #define ERF_NO_HT_SHARING 1 | 449 | #define ERF_NO_HT_SHARING 1 |
422 | #define ERF_HAS_RSP_1 2 | 450 | #define ERF_HAS_RSP_1 2 |
423 | 451 | ||
452 | #define EVENT_VAR(_id) event_attr_##_id | ||
453 | #define EVENT_PTR(_id) &event_attr_##_id.attr.attr | ||
454 | |||
455 | #define EVENT_ATTR(_name, _id) \ | ||
456 | static struct perf_pmu_events_attr EVENT_VAR(_id) = { \ | ||
457 | .attr = __ATTR(_name, 0444, events_sysfs_show, NULL), \ | ||
458 | .id = PERF_COUNT_HW_##_id, \ | ||
459 | .event_str = NULL, \ | ||
460 | }; | ||
461 | |||
462 | #define EVENT_ATTR_STR(_name, v, str) \ | ||
463 | static struct perf_pmu_events_attr event_attr_##v = { \ | ||
464 | .attr = __ATTR(_name, 0444, events_sysfs_show, NULL), \ | ||
465 | .id = 0, \ | ||
466 | .event_str = str, \ | ||
467 | }; | ||
468 | |||
424 | extern struct x86_pmu x86_pmu __read_mostly; | 469 | extern struct x86_pmu x86_pmu __read_mostly; |
425 | 470 | ||
426 | DECLARE_PER_CPU(struct cpu_hw_events, cpu_hw_events); | 471 | DECLARE_PER_CPU(struct cpu_hw_events, cpu_hw_events); |
@@ -628,6 +673,9 @@ int p6_pmu_init(void); | |||
628 | 673 | ||
629 | int knc_pmu_init(void); | 674 | int knc_pmu_init(void); |
630 | 675 | ||
676 | ssize_t events_sysfs_show(struct device *dev, struct device_attribute *attr, | ||
677 | char *page); | ||
678 | |||
631 | #else /* CONFIG_CPU_SUP_INTEL */ | 679 | #else /* CONFIG_CPU_SUP_INTEL */ |
632 | 680 | ||
633 | static inline void reserve_ds_buffers(void) | 681 | static inline void reserve_ds_buffers(void) |
diff --git a/arch/x86/kernel/cpu/perf_event_amd.c b/arch/x86/kernel/cpu/perf_event_amd.c index dfdab42aed27..7e28d9467bb4 100644 --- a/arch/x86/kernel/cpu/perf_event_amd.c +++ b/arch/x86/kernel/cpu/perf_event_amd.c | |||
@@ -132,14 +132,11 @@ static u64 amd_pmu_event_map(int hw_event) | |||
132 | return amd_perfmon_event_map[hw_event]; | 132 | return amd_perfmon_event_map[hw_event]; |
133 | } | 133 | } |
134 | 134 | ||
135 | static struct event_constraint *amd_nb_event_constraint; | ||
136 | |||
137 | /* | 135 | /* |
138 | * Previously calculated offsets | 136 | * Previously calculated offsets |
139 | */ | 137 | */ |
140 | static unsigned int event_offsets[X86_PMC_IDX_MAX] __read_mostly; | 138 | static unsigned int event_offsets[X86_PMC_IDX_MAX] __read_mostly; |
141 | static unsigned int count_offsets[X86_PMC_IDX_MAX] __read_mostly; | 139 | static unsigned int count_offsets[X86_PMC_IDX_MAX] __read_mostly; |
142 | static unsigned int rdpmc_indexes[X86_PMC_IDX_MAX] __read_mostly; | ||
143 | 140 | ||
144 | /* | 141 | /* |
145 | * Legacy CPUs: | 142 | * Legacy CPUs: |
@@ -147,14 +144,10 @@ static unsigned int rdpmc_indexes[X86_PMC_IDX_MAX] __read_mostly; | |||
147 | * | 144 | * |
148 | * CPUs with core performance counter extensions: | 145 | * CPUs with core performance counter extensions: |
149 | * 6 counters starting at 0xc0010200 each offset by 2 | 146 | * 6 counters starting at 0xc0010200 each offset by 2 |
150 | * | ||
151 | * CPUs with north bridge performance counter extensions: | ||
152 | * 4 additional counters starting at 0xc0010240 each offset by 2 | ||
153 | * (indexed right above either one of the above core counters) | ||
154 | */ | 147 | */ |
155 | static inline int amd_pmu_addr_offset(int index, bool eventsel) | 148 | static inline int amd_pmu_addr_offset(int index, bool eventsel) |
156 | { | 149 | { |
157 | int offset, first, base; | 150 | int offset; |
158 | 151 | ||
159 | if (!index) | 152 | if (!index) |
160 | return index; | 153 | return index; |
@@ -167,23 +160,7 @@ static inline int amd_pmu_addr_offset(int index, bool eventsel) | |||
167 | if (offset) | 160 | if (offset) |
168 | return offset; | 161 | return offset; |
169 | 162 | ||
170 | if (amd_nb_event_constraint && | 163 | if (!cpu_has_perfctr_core) |
171 | test_bit(index, amd_nb_event_constraint->idxmsk)) { | ||
172 | /* | ||
173 | * calculate the offset of NB counters with respect to | ||
174 | * base eventsel or perfctr | ||
175 | */ | ||
176 | |||
177 | first = find_first_bit(amd_nb_event_constraint->idxmsk, | ||
178 | X86_PMC_IDX_MAX); | ||
179 | |||
180 | if (eventsel) | ||
181 | base = MSR_F15H_NB_PERF_CTL - x86_pmu.eventsel; | ||
182 | else | ||
183 | base = MSR_F15H_NB_PERF_CTR - x86_pmu.perfctr; | ||
184 | |||
185 | offset = base + ((index - first) << 1); | ||
186 | } else if (!cpu_has_perfctr_core) | ||
187 | offset = index; | 164 | offset = index; |
188 | else | 165 | else |
189 | offset = index << 1; | 166 | offset = index << 1; |
@@ -196,36 +173,6 @@ static inline int amd_pmu_addr_offset(int index, bool eventsel) | |||
196 | return offset; | 173 | return offset; |
197 | } | 174 | } |
198 | 175 | ||
199 | static inline int amd_pmu_rdpmc_index(int index) | ||
200 | { | ||
201 | int ret, first; | ||
202 | |||
203 | if (!index) | ||
204 | return index; | ||
205 | |||
206 | ret = rdpmc_indexes[index]; | ||
207 | |||
208 | if (ret) | ||
209 | return ret; | ||
210 | |||
211 | if (amd_nb_event_constraint && | ||
212 | test_bit(index, amd_nb_event_constraint->idxmsk)) { | ||
213 | /* | ||
214 | * according to the mnual, ECX value of the NB counters is | ||
215 | * the index of the NB counter (0, 1, 2 or 3) plus 6 | ||
216 | */ | ||
217 | |||
218 | first = find_first_bit(amd_nb_event_constraint->idxmsk, | ||
219 | X86_PMC_IDX_MAX); | ||
220 | ret = index - first + 6; | ||
221 | } else | ||
222 | ret = index; | ||
223 | |||
224 | rdpmc_indexes[index] = ret; | ||
225 | |||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | static int amd_core_hw_config(struct perf_event *event) | 176 | static int amd_core_hw_config(struct perf_event *event) |
230 | { | 177 | { |
231 | if (event->attr.exclude_host && event->attr.exclude_guest) | 178 | if (event->attr.exclude_host && event->attr.exclude_guest) |
@@ -245,34 +192,6 @@ static int amd_core_hw_config(struct perf_event *event) | |||
245 | } | 192 | } |
246 | 193 | ||
247 | /* | 194 | /* |
248 | * NB counters do not support the following event select bits: | ||
249 | * Host/Guest only | ||
250 | * Counter mask | ||
251 | * Invert counter mask | ||
252 | * Edge detect | ||
253 | * OS/User mode | ||
254 | */ | ||
255 | static int amd_nb_hw_config(struct perf_event *event) | ||
256 | { | ||
257 | /* for NB, we only allow system wide counting mode */ | ||
258 | if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) | ||
259 | return -EINVAL; | ||
260 | |||
261 | if (event->attr.exclude_user || event->attr.exclude_kernel || | ||
262 | event->attr.exclude_host || event->attr.exclude_guest) | ||
263 | return -EINVAL; | ||
264 | |||
265 | event->hw.config &= ~(ARCH_PERFMON_EVENTSEL_USR | | ||
266 | ARCH_PERFMON_EVENTSEL_OS); | ||
267 | |||
268 | if (event->hw.config & ~(AMD64_RAW_EVENT_MASK_NB | | ||
269 | ARCH_PERFMON_EVENTSEL_INT)) | ||
270 | return -EINVAL; | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | /* | ||
276 | * AMD64 events are detected based on their event codes. | 195 | * AMD64 events are detected based on their event codes. |
277 | */ | 196 | */ |
278 | static inline unsigned int amd_get_event_code(struct hw_perf_event *hwc) | 197 | static inline unsigned int amd_get_event_code(struct hw_perf_event *hwc) |
@@ -285,11 +204,6 @@ static inline int amd_is_nb_event(struct hw_perf_event *hwc) | |||
285 | return (hwc->config & 0xe0) == 0xe0; | 204 | return (hwc->config & 0xe0) == 0xe0; |
286 | } | 205 | } |
287 | 206 | ||
288 | static inline int amd_is_perfctr_nb_event(struct hw_perf_event *hwc) | ||
289 | { | ||
290 | return amd_nb_event_constraint && amd_is_nb_event(hwc); | ||
291 | } | ||
292 | |||
293 | static inline int amd_has_nb(struct cpu_hw_events *cpuc) | 207 | static inline int amd_has_nb(struct cpu_hw_events *cpuc) |
294 | { | 208 | { |
295 | struct amd_nb *nb = cpuc->amd_nb; | 209 | struct amd_nb *nb = cpuc->amd_nb; |
@@ -315,9 +229,6 @@ static int amd_pmu_hw_config(struct perf_event *event) | |||
315 | if (event->attr.type == PERF_TYPE_RAW) | 229 | if (event->attr.type == PERF_TYPE_RAW) |
316 | event->hw.config |= event->attr.config & AMD64_RAW_EVENT_MASK; | 230 | event->hw.config |= event->attr.config & AMD64_RAW_EVENT_MASK; |
317 | 231 | ||
318 | if (amd_is_perfctr_nb_event(&event->hw)) | ||
319 | return amd_nb_hw_config(event); | ||
320 | |||
321 | return amd_core_hw_config(event); | 232 | return amd_core_hw_config(event); |
322 | } | 233 | } |
323 | 234 | ||
@@ -341,19 +252,6 @@ static void __amd_put_nb_event_constraints(struct cpu_hw_events *cpuc, | |||
341 | } | 252 | } |
342 | } | 253 | } |
343 | 254 | ||
344 | static void amd_nb_interrupt_hw_config(struct hw_perf_event *hwc) | ||
345 | { | ||
346 | int core_id = cpu_data(smp_processor_id()).cpu_core_id; | ||
347 | |||
348 | /* deliver interrupts only to this core */ | ||
349 | if (hwc->config & ARCH_PERFMON_EVENTSEL_INT) { | ||
350 | hwc->config |= AMD64_EVENTSEL_INT_CORE_ENABLE; | ||
351 | hwc->config &= ~AMD64_EVENTSEL_INT_CORE_SEL_MASK; | ||
352 | hwc->config |= (u64)(core_id) << | ||
353 | AMD64_EVENTSEL_INT_CORE_SEL_SHIFT; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | /* | 255 | /* |
358 | * AMD64 NorthBridge events need special treatment because | 256 | * AMD64 NorthBridge events need special treatment because |
359 | * counter access needs to be synchronized across all cores | 257 | * counter access needs to be synchronized across all cores |
@@ -441,9 +339,6 @@ __amd_get_nb_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *ev | |||
441 | if (new == -1) | 339 | if (new == -1) |
442 | return &emptyconstraint; | 340 | return &emptyconstraint; |
443 | 341 | ||
444 | if (amd_is_perfctr_nb_event(hwc)) | ||
445 | amd_nb_interrupt_hw_config(hwc); | ||
446 | |||
447 | return &nb->event_constraints[new]; | 342 | return &nb->event_constraints[new]; |
448 | } | 343 | } |
449 | 344 | ||
@@ -543,8 +438,7 @@ amd_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) | |||
543 | if (!(amd_has_nb(cpuc) && amd_is_nb_event(&event->hw))) | 438 | if (!(amd_has_nb(cpuc) && amd_is_nb_event(&event->hw))) |
544 | return &unconstrained; | 439 | return &unconstrained; |
545 | 440 | ||
546 | return __amd_get_nb_event_constraints(cpuc, event, | 441 | return __amd_get_nb_event_constraints(cpuc, event, NULL); |
547 | amd_nb_event_constraint); | ||
548 | } | 442 | } |
549 | 443 | ||
550 | static void amd_put_event_constraints(struct cpu_hw_events *cpuc, | 444 | static void amd_put_event_constraints(struct cpu_hw_events *cpuc, |
@@ -643,9 +537,6 @@ static struct event_constraint amd_f15_PMC30 = EVENT_CONSTRAINT_OVERLAP(0, 0x09, | |||
643 | static struct event_constraint amd_f15_PMC50 = EVENT_CONSTRAINT(0, 0x3F, 0); | 537 | static struct event_constraint amd_f15_PMC50 = EVENT_CONSTRAINT(0, 0x3F, 0); |
644 | static struct event_constraint amd_f15_PMC53 = EVENT_CONSTRAINT(0, 0x38, 0); | 538 | static struct event_constraint amd_f15_PMC53 = EVENT_CONSTRAINT(0, 0x38, 0); |
645 | 539 | ||
646 | static struct event_constraint amd_NBPMC96 = EVENT_CONSTRAINT(0, 0x3C0, 0); | ||
647 | static struct event_constraint amd_NBPMC74 = EVENT_CONSTRAINT(0, 0xF0, 0); | ||
648 | |||
649 | static struct event_constraint * | 540 | static struct event_constraint * |
650 | amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, struct perf_event *event) | 541 | amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, struct perf_event *event) |
651 | { | 542 | { |
@@ -711,8 +602,8 @@ amd_get_event_constraints_f15h(struct cpu_hw_events *cpuc, struct perf_event *ev | |||
711 | return &amd_f15_PMC20; | 602 | return &amd_f15_PMC20; |
712 | } | 603 | } |
713 | case AMD_EVENT_NB: | 604 | case AMD_EVENT_NB: |
714 | return __amd_get_nb_event_constraints(cpuc, event, | 605 | /* moved to perf_event_amd_uncore.c */ |
715 | amd_nb_event_constraint); | 606 | return &emptyconstraint; |
716 | default: | 607 | default: |
717 | return &emptyconstraint; | 608 | return &emptyconstraint; |
718 | } | 609 | } |
@@ -738,7 +629,6 @@ static __initconst const struct x86_pmu amd_pmu = { | |||
738 | .eventsel = MSR_K7_EVNTSEL0, | 629 | .eventsel = MSR_K7_EVNTSEL0, |
739 | .perfctr = MSR_K7_PERFCTR0, | 630 | .perfctr = MSR_K7_PERFCTR0, |
740 | .addr_offset = amd_pmu_addr_offset, | 631 | .addr_offset = amd_pmu_addr_offset, |
741 | .rdpmc_index = amd_pmu_rdpmc_index, | ||
742 | .event_map = amd_pmu_event_map, | 632 | .event_map = amd_pmu_event_map, |
743 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), | 633 | .max_events = ARRAY_SIZE(amd_perfmon_event_map), |
744 | .num_counters = AMD64_NUM_COUNTERS, | 634 | .num_counters = AMD64_NUM_COUNTERS, |
@@ -790,23 +680,6 @@ static int setup_perfctr_core(void) | |||
790 | return 0; | 680 | return 0; |
791 | } | 681 | } |
792 | 682 | ||
793 | static int setup_perfctr_nb(void) | ||
794 | { | ||
795 | if (!cpu_has_perfctr_nb) | ||
796 | return -ENODEV; | ||
797 | |||
798 | x86_pmu.num_counters += AMD64_NUM_COUNTERS_NB; | ||
799 | |||
800 | if (cpu_has_perfctr_core) | ||
801 | amd_nb_event_constraint = &amd_NBPMC96; | ||
802 | else | ||
803 | amd_nb_event_constraint = &amd_NBPMC74; | ||
804 | |||
805 | printk(KERN_INFO "perf: AMD northbridge performance counters detected\n"); | ||
806 | |||
807 | return 0; | ||
808 | } | ||
809 | |||
810 | __init int amd_pmu_init(void) | 683 | __init int amd_pmu_init(void) |
811 | { | 684 | { |
812 | /* Performance-monitoring supported from K7 and later: */ | 685 | /* Performance-monitoring supported from K7 and later: */ |
@@ -817,7 +690,6 @@ __init int amd_pmu_init(void) | |||
817 | 690 | ||
818 | setup_event_constraints(); | 691 | setup_event_constraints(); |
819 | setup_perfctr_core(); | 692 | setup_perfctr_core(); |
820 | setup_perfctr_nb(); | ||
821 | 693 | ||
822 | /* Events are common for all AMDs */ | 694 | /* Events are common for all AMDs */ |
823 | memcpy(hw_cache_event_ids, amd_hw_cache_event_ids, | 695 | memcpy(hw_cache_event_ids, amd_hw_cache_event_ids, |
diff --git a/arch/x86/kernel/cpu/perf_event_amd_uncore.c b/arch/x86/kernel/cpu/perf_event_amd_uncore.c new file mode 100644 index 000000000000..c0c661adf03e --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event_amd_uncore.c | |||
@@ -0,0 +1,547 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2013 Advanced Micro Devices, Inc. | ||
3 | * | ||
4 | * Author: Jacob Shin <jacob.shin@amd.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/perf_event.h> | ||
12 | #include <linux/percpu.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/cpu.h> | ||
17 | #include <linux/cpumask.h> | ||
18 | |||
19 | #include <asm/cpufeature.h> | ||
20 | #include <asm/perf_event.h> | ||
21 | #include <asm/msr.h> | ||
22 | |||
23 | #define NUM_COUNTERS_NB 4 | ||
24 | #define NUM_COUNTERS_L2 4 | ||
25 | #define MAX_COUNTERS NUM_COUNTERS_NB | ||
26 | |||
27 | #define RDPMC_BASE_NB 6 | ||
28 | #define RDPMC_BASE_L2 10 | ||
29 | |||
30 | #define COUNTER_SHIFT 16 | ||
31 | |||
32 | struct amd_uncore { | ||
33 | int id; | ||
34 | int refcnt; | ||
35 | int cpu; | ||
36 | int num_counters; | ||
37 | int rdpmc_base; | ||
38 | u32 msr_base; | ||
39 | cpumask_t *active_mask; | ||
40 | struct pmu *pmu; | ||
41 | struct perf_event *events[MAX_COUNTERS]; | ||
42 | struct amd_uncore *free_when_cpu_online; | ||
43 | }; | ||
44 | |||
45 | static struct amd_uncore * __percpu *amd_uncore_nb; | ||
46 | static struct amd_uncore * __percpu *amd_uncore_l2; | ||
47 | |||
48 | static struct pmu amd_nb_pmu; | ||
49 | static struct pmu amd_l2_pmu; | ||
50 | |||
51 | static cpumask_t amd_nb_active_mask; | ||
52 | static cpumask_t amd_l2_active_mask; | ||
53 | |||
54 | static bool is_nb_event(struct perf_event *event) | ||
55 | { | ||
56 | return event->pmu->type == amd_nb_pmu.type; | ||
57 | } | ||
58 | |||
59 | static bool is_l2_event(struct perf_event *event) | ||
60 | { | ||
61 | return event->pmu->type == amd_l2_pmu.type; | ||
62 | } | ||
63 | |||
64 | static struct amd_uncore *event_to_amd_uncore(struct perf_event *event) | ||
65 | { | ||
66 | if (is_nb_event(event) && amd_uncore_nb) | ||
67 | return *per_cpu_ptr(amd_uncore_nb, event->cpu); | ||
68 | else if (is_l2_event(event) && amd_uncore_l2) | ||
69 | return *per_cpu_ptr(amd_uncore_l2, event->cpu); | ||
70 | |||
71 | return NULL; | ||
72 | } | ||
73 | |||
74 | static void amd_uncore_read(struct perf_event *event) | ||
75 | { | ||
76 | struct hw_perf_event *hwc = &event->hw; | ||
77 | u64 prev, new; | ||
78 | s64 delta; | ||
79 | |||
80 | /* | ||
81 | * since we do not enable counter overflow interrupts, | ||
82 | * we do not have to worry about prev_count changing on us | ||
83 | */ | ||
84 | |||
85 | prev = local64_read(&hwc->prev_count); | ||
86 | rdpmcl(hwc->event_base_rdpmc, new); | ||
87 | local64_set(&hwc->prev_count, new); | ||
88 | delta = (new << COUNTER_SHIFT) - (prev << COUNTER_SHIFT); | ||
89 | delta >>= COUNTER_SHIFT; | ||
90 | local64_add(delta, &event->count); | ||
91 | } | ||
92 | |||
93 | static void amd_uncore_start(struct perf_event *event, int flags) | ||
94 | { | ||
95 | struct hw_perf_event *hwc = &event->hw; | ||
96 | |||
97 | if (flags & PERF_EF_RELOAD) | ||
98 | wrmsrl(hwc->event_base, (u64)local64_read(&hwc->prev_count)); | ||
99 | |||
100 | hwc->state = 0; | ||
101 | wrmsrl(hwc->config_base, (hwc->config | ARCH_PERFMON_EVENTSEL_ENABLE)); | ||
102 | perf_event_update_userpage(event); | ||
103 | } | ||
104 | |||
105 | static void amd_uncore_stop(struct perf_event *event, int flags) | ||
106 | { | ||
107 | struct hw_perf_event *hwc = &event->hw; | ||
108 | |||
109 | wrmsrl(hwc->config_base, hwc->config); | ||
110 | hwc->state |= PERF_HES_STOPPED; | ||
111 | |||
112 | if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) { | ||
113 | amd_uncore_read(event); | ||
114 | hwc->state |= PERF_HES_UPTODATE; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static int amd_uncore_add(struct perf_event *event, int flags) | ||
119 | { | ||
120 | int i; | ||
121 | struct amd_uncore *uncore = event_to_amd_uncore(event); | ||
122 | struct hw_perf_event *hwc = &event->hw; | ||
123 | |||
124 | /* are we already assigned? */ | ||
125 | if (hwc->idx != -1 && uncore->events[hwc->idx] == event) | ||
126 | goto out; | ||
127 | |||
128 | for (i = 0; i < uncore->num_counters; i++) { | ||
129 | if (uncore->events[i] == event) { | ||
130 | hwc->idx = i; | ||
131 | goto out; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /* if not, take the first available counter */ | ||
136 | hwc->idx = -1; | ||
137 | for (i = 0; i < uncore->num_counters; i++) { | ||
138 | if (cmpxchg(&uncore->events[i], NULL, event) == NULL) { | ||
139 | hwc->idx = i; | ||
140 | break; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | out: | ||
145 | if (hwc->idx == -1) | ||
146 | return -EBUSY; | ||
147 | |||
148 | hwc->config_base = uncore->msr_base + (2 * hwc->idx); | ||
149 | hwc->event_base = uncore->msr_base + 1 + (2 * hwc->idx); | ||
150 | hwc->event_base_rdpmc = uncore->rdpmc_base + hwc->idx; | ||
151 | hwc->state = PERF_HES_UPTODATE | PERF_HES_STOPPED; | ||
152 | |||
153 | if (flags & PERF_EF_START) | ||
154 | amd_uncore_start(event, PERF_EF_RELOAD); | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static void amd_uncore_del(struct perf_event *event, int flags) | ||
160 | { | ||
161 | int i; | ||
162 | struct amd_uncore *uncore = event_to_amd_uncore(event); | ||
163 | struct hw_perf_event *hwc = &event->hw; | ||
164 | |||
165 | amd_uncore_stop(event, PERF_EF_UPDATE); | ||
166 | |||
167 | for (i = 0; i < uncore->num_counters; i++) { | ||
168 | if (cmpxchg(&uncore->events[i], event, NULL) == event) | ||
169 | break; | ||
170 | } | ||
171 | |||
172 | hwc->idx = -1; | ||
173 | } | ||
174 | |||
175 | static int amd_uncore_event_init(struct perf_event *event) | ||
176 | { | ||
177 | struct amd_uncore *uncore; | ||
178 | struct hw_perf_event *hwc = &event->hw; | ||
179 | |||
180 | if (event->attr.type != event->pmu->type) | ||
181 | return -ENOENT; | ||
182 | |||
183 | /* | ||
184 | * NB and L2 counters (MSRs) are shared across all cores that share the | ||
185 | * same NB / L2 cache. Interrupts can be directed to a single target | ||
186 | * core, however, event counts generated by processes running on other | ||
187 | * cores cannot be masked out. So we do not support sampling and | ||
188 | * per-thread events. | ||
189 | */ | ||
190 | if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK) | ||
191 | return -EINVAL; | ||
192 | |||
193 | /* NB and L2 counters do not have usr/os/guest/host bits */ | ||
194 | if (event->attr.exclude_user || event->attr.exclude_kernel || | ||
195 | event->attr.exclude_host || event->attr.exclude_guest) | ||
196 | return -EINVAL; | ||
197 | |||
198 | /* and we do not enable counter overflow interrupts */ | ||
199 | hwc->config = event->attr.config & AMD64_RAW_EVENT_MASK_NB; | ||
200 | hwc->idx = -1; | ||
201 | |||
202 | if (event->cpu < 0) | ||
203 | return -EINVAL; | ||
204 | |||
205 | uncore = event_to_amd_uncore(event); | ||
206 | if (!uncore) | ||
207 | return -ENODEV; | ||
208 | |||
209 | /* | ||
210 | * since request can come in to any of the shared cores, we will remap | ||
211 | * to a single common cpu. | ||
212 | */ | ||
213 | event->cpu = uncore->cpu; | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static ssize_t amd_uncore_attr_show_cpumask(struct device *dev, | ||
219 | struct device_attribute *attr, | ||
220 | char *buf) | ||
221 | { | ||
222 | int n; | ||
223 | cpumask_t *active_mask; | ||
224 | struct pmu *pmu = dev_get_drvdata(dev); | ||
225 | |||
226 | if (pmu->type == amd_nb_pmu.type) | ||
227 | active_mask = &amd_nb_active_mask; | ||
228 | else if (pmu->type == amd_l2_pmu.type) | ||
229 | active_mask = &amd_l2_active_mask; | ||
230 | else | ||
231 | return 0; | ||
232 | |||
233 | n = cpulist_scnprintf(buf, PAGE_SIZE - 2, active_mask); | ||
234 | buf[n++] = '\n'; | ||
235 | buf[n] = '\0'; | ||
236 | return n; | ||
237 | } | ||
238 | static DEVICE_ATTR(cpumask, S_IRUGO, amd_uncore_attr_show_cpumask, NULL); | ||
239 | |||
240 | static struct attribute *amd_uncore_attrs[] = { | ||
241 | &dev_attr_cpumask.attr, | ||
242 | NULL, | ||
243 | }; | ||
244 | |||
245 | static struct attribute_group amd_uncore_attr_group = { | ||
246 | .attrs = amd_uncore_attrs, | ||
247 | }; | ||
248 | |||
249 | PMU_FORMAT_ATTR(event, "config:0-7,32-35"); | ||
250 | PMU_FORMAT_ATTR(umask, "config:8-15"); | ||
251 | |||
252 | static struct attribute *amd_uncore_format_attr[] = { | ||
253 | &format_attr_event.attr, | ||
254 | &format_attr_umask.attr, | ||
255 | NULL, | ||
256 | }; | ||
257 | |||
258 | static struct attribute_group amd_uncore_format_group = { | ||
259 | .name = "format", | ||
260 | .attrs = amd_uncore_format_attr, | ||
261 | }; | ||
262 | |||
263 | static const struct attribute_group *amd_uncore_attr_groups[] = { | ||
264 | &amd_uncore_attr_group, | ||
265 | &amd_uncore_format_group, | ||
266 | NULL, | ||
267 | }; | ||
268 | |||
269 | static struct pmu amd_nb_pmu = { | ||
270 | .attr_groups = amd_uncore_attr_groups, | ||
271 | .name = "amd_nb", | ||
272 | .event_init = amd_uncore_event_init, | ||
273 | .add = amd_uncore_add, | ||
274 | .del = amd_uncore_del, | ||
275 | .start = amd_uncore_start, | ||
276 | .stop = amd_uncore_stop, | ||
277 | .read = amd_uncore_read, | ||
278 | }; | ||
279 | |||
280 | static struct pmu amd_l2_pmu = { | ||
281 | .attr_groups = amd_uncore_attr_groups, | ||
282 | .name = "amd_l2", | ||
283 | .event_init = amd_uncore_event_init, | ||
284 | .add = amd_uncore_add, | ||
285 | .del = amd_uncore_del, | ||
286 | .start = amd_uncore_start, | ||
287 | .stop = amd_uncore_stop, | ||
288 | .read = amd_uncore_read, | ||
289 | }; | ||
290 | |||
291 | static struct amd_uncore * __cpuinit amd_uncore_alloc(unsigned int cpu) | ||
292 | { | ||
293 | return kzalloc_node(sizeof(struct amd_uncore), GFP_KERNEL, | ||
294 | cpu_to_node(cpu)); | ||
295 | } | ||
296 | |||
297 | static void __cpuinit amd_uncore_cpu_up_prepare(unsigned int cpu) | ||
298 | { | ||
299 | struct amd_uncore *uncore; | ||
300 | |||
301 | if (amd_uncore_nb) { | ||
302 | uncore = amd_uncore_alloc(cpu); | ||
303 | uncore->cpu = cpu; | ||
304 | uncore->num_counters = NUM_COUNTERS_NB; | ||
305 | uncore->rdpmc_base = RDPMC_BASE_NB; | ||
306 | uncore->msr_base = MSR_F15H_NB_PERF_CTL; | ||
307 | uncore->active_mask = &amd_nb_active_mask; | ||
308 | uncore->pmu = &amd_nb_pmu; | ||
309 | *per_cpu_ptr(amd_uncore_nb, cpu) = uncore; | ||
310 | } | ||
311 | |||
312 | if (amd_uncore_l2) { | ||
313 | uncore = amd_uncore_alloc(cpu); | ||
314 | uncore->cpu = cpu; | ||
315 | uncore->num_counters = NUM_COUNTERS_L2; | ||
316 | uncore->rdpmc_base = RDPMC_BASE_L2; | ||
317 | uncore->msr_base = MSR_F16H_L2I_PERF_CTL; | ||
318 | uncore->active_mask = &amd_l2_active_mask; | ||
319 | uncore->pmu = &amd_l2_pmu; | ||
320 | *per_cpu_ptr(amd_uncore_l2, cpu) = uncore; | ||
321 | } | ||
322 | } | ||
323 | |||
324 | static struct amd_uncore * | ||
325 | __cpuinit amd_uncore_find_online_sibling(struct amd_uncore *this, | ||
326 | struct amd_uncore * __percpu *uncores) | ||
327 | { | ||
328 | unsigned int cpu; | ||
329 | struct amd_uncore *that; | ||
330 | |||
331 | for_each_online_cpu(cpu) { | ||
332 | that = *per_cpu_ptr(uncores, cpu); | ||
333 | |||
334 | if (!that) | ||
335 | continue; | ||
336 | |||
337 | if (this == that) | ||
338 | continue; | ||
339 | |||
340 | if (this->id == that->id) { | ||
341 | that->free_when_cpu_online = this; | ||
342 | this = that; | ||
343 | break; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | this->refcnt++; | ||
348 | return this; | ||
349 | } | ||
350 | |||
351 | static void __cpuinit amd_uncore_cpu_starting(unsigned int cpu) | ||
352 | { | ||
353 | unsigned int eax, ebx, ecx, edx; | ||
354 | struct amd_uncore *uncore; | ||
355 | |||
356 | if (amd_uncore_nb) { | ||
357 | uncore = *per_cpu_ptr(amd_uncore_nb, cpu); | ||
358 | cpuid(0x8000001e, &eax, &ebx, &ecx, &edx); | ||
359 | uncore->id = ecx & 0xff; | ||
360 | |||
361 | uncore = amd_uncore_find_online_sibling(uncore, amd_uncore_nb); | ||
362 | *per_cpu_ptr(amd_uncore_nb, cpu) = uncore; | ||
363 | } | ||
364 | |||
365 | if (amd_uncore_l2) { | ||
366 | unsigned int apicid = cpu_data(cpu).apicid; | ||
367 | unsigned int nshared; | ||
368 | |||
369 | uncore = *per_cpu_ptr(amd_uncore_l2, cpu); | ||
370 | cpuid_count(0x8000001d, 2, &eax, &ebx, &ecx, &edx); | ||
371 | nshared = ((eax >> 14) & 0xfff) + 1; | ||
372 | uncore->id = apicid - (apicid % nshared); | ||
373 | |||
374 | uncore = amd_uncore_find_online_sibling(uncore, amd_uncore_l2); | ||
375 | *per_cpu_ptr(amd_uncore_l2, cpu) = uncore; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | static void __cpuinit uncore_online(unsigned int cpu, | ||
380 | struct amd_uncore * __percpu *uncores) | ||
381 | { | ||
382 | struct amd_uncore *uncore = *per_cpu_ptr(uncores, cpu); | ||
383 | |||
384 | kfree(uncore->free_when_cpu_online); | ||
385 | uncore->free_when_cpu_online = NULL; | ||
386 | |||
387 | if (cpu == uncore->cpu) | ||
388 | cpumask_set_cpu(cpu, uncore->active_mask); | ||
389 | } | ||
390 | |||
391 | static void __cpuinit amd_uncore_cpu_online(unsigned int cpu) | ||
392 | { | ||
393 | if (amd_uncore_nb) | ||
394 | uncore_online(cpu, amd_uncore_nb); | ||
395 | |||
396 | if (amd_uncore_l2) | ||
397 | uncore_online(cpu, amd_uncore_l2); | ||
398 | } | ||
399 | |||
400 | static void __cpuinit uncore_down_prepare(unsigned int cpu, | ||
401 | struct amd_uncore * __percpu *uncores) | ||
402 | { | ||
403 | unsigned int i; | ||
404 | struct amd_uncore *this = *per_cpu_ptr(uncores, cpu); | ||
405 | |||
406 | if (this->cpu != cpu) | ||
407 | return; | ||
408 | |||
409 | /* this cpu is going down, migrate to a shared sibling if possible */ | ||
410 | for_each_online_cpu(i) { | ||
411 | struct amd_uncore *that = *per_cpu_ptr(uncores, i); | ||
412 | |||
413 | if (cpu == i) | ||
414 | continue; | ||
415 | |||
416 | if (this == that) { | ||
417 | perf_pmu_migrate_context(this->pmu, cpu, i); | ||
418 | cpumask_clear_cpu(cpu, that->active_mask); | ||
419 | cpumask_set_cpu(i, that->active_mask); | ||
420 | that->cpu = i; | ||
421 | break; | ||
422 | } | ||
423 | } | ||
424 | } | ||
425 | |||
426 | static void __cpuinit amd_uncore_cpu_down_prepare(unsigned int cpu) | ||
427 | { | ||
428 | if (amd_uncore_nb) | ||
429 | uncore_down_prepare(cpu, amd_uncore_nb); | ||
430 | |||
431 | if (amd_uncore_l2) | ||
432 | uncore_down_prepare(cpu, amd_uncore_l2); | ||
433 | } | ||
434 | |||
435 | static void __cpuinit uncore_dead(unsigned int cpu, | ||
436 | struct amd_uncore * __percpu *uncores) | ||
437 | { | ||
438 | struct amd_uncore *uncore = *per_cpu_ptr(uncores, cpu); | ||
439 | |||
440 | if (cpu == uncore->cpu) | ||
441 | cpumask_clear_cpu(cpu, uncore->active_mask); | ||
442 | |||
443 | if (!--uncore->refcnt) | ||
444 | kfree(uncore); | ||
445 | *per_cpu_ptr(amd_uncore_nb, cpu) = NULL; | ||
446 | } | ||
447 | |||
448 | static void __cpuinit amd_uncore_cpu_dead(unsigned int cpu) | ||
449 | { | ||
450 | if (amd_uncore_nb) | ||
451 | uncore_dead(cpu, amd_uncore_nb); | ||
452 | |||
453 | if (amd_uncore_l2) | ||
454 | uncore_dead(cpu, amd_uncore_l2); | ||
455 | } | ||
456 | |||
457 | static int __cpuinit | ||
458 | amd_uncore_cpu_notifier(struct notifier_block *self, unsigned long action, | ||
459 | void *hcpu) | ||
460 | { | ||
461 | unsigned int cpu = (long)hcpu; | ||
462 | |||
463 | switch (action & ~CPU_TASKS_FROZEN) { | ||
464 | case CPU_UP_PREPARE: | ||
465 | amd_uncore_cpu_up_prepare(cpu); | ||
466 | break; | ||
467 | |||
468 | case CPU_STARTING: | ||
469 | amd_uncore_cpu_starting(cpu); | ||
470 | break; | ||
471 | |||
472 | case CPU_ONLINE: | ||
473 | amd_uncore_cpu_online(cpu); | ||
474 | break; | ||
475 | |||
476 | case CPU_DOWN_PREPARE: | ||
477 | amd_uncore_cpu_down_prepare(cpu); | ||
478 | break; | ||
479 | |||
480 | case CPU_UP_CANCELED: | ||
481 | case CPU_DEAD: | ||
482 | amd_uncore_cpu_dead(cpu); | ||
483 | break; | ||
484 | |||
485 | default: | ||
486 | break; | ||
487 | } | ||
488 | |||
489 | return NOTIFY_OK; | ||
490 | } | ||
491 | |||
492 | static struct notifier_block amd_uncore_cpu_notifier_block __cpuinitdata = { | ||
493 | .notifier_call = amd_uncore_cpu_notifier, | ||
494 | .priority = CPU_PRI_PERF + 1, | ||
495 | }; | ||
496 | |||
497 | static void __init init_cpu_already_online(void *dummy) | ||
498 | { | ||
499 | unsigned int cpu = smp_processor_id(); | ||
500 | |||
501 | amd_uncore_cpu_starting(cpu); | ||
502 | amd_uncore_cpu_online(cpu); | ||
503 | } | ||
504 | |||
505 | static int __init amd_uncore_init(void) | ||
506 | { | ||
507 | unsigned int cpu; | ||
508 | int ret = -ENODEV; | ||
509 | |||
510 | if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) | ||
511 | return -ENODEV; | ||
512 | |||
513 | if (!cpu_has_topoext) | ||
514 | return -ENODEV; | ||
515 | |||
516 | if (cpu_has_perfctr_nb) { | ||
517 | amd_uncore_nb = alloc_percpu(struct amd_uncore *); | ||
518 | perf_pmu_register(&amd_nb_pmu, amd_nb_pmu.name, -1); | ||
519 | |||
520 | printk(KERN_INFO "perf: AMD NB counters detected\n"); | ||
521 | ret = 0; | ||
522 | } | ||
523 | |||
524 | if (cpu_has_perfctr_l2) { | ||
525 | amd_uncore_l2 = alloc_percpu(struct amd_uncore *); | ||
526 | perf_pmu_register(&amd_l2_pmu, amd_l2_pmu.name, -1); | ||
527 | |||
528 | printk(KERN_INFO "perf: AMD L2I counters detected\n"); | ||
529 | ret = 0; | ||
530 | } | ||
531 | |||
532 | if (ret) | ||
533 | return -ENODEV; | ||
534 | |||
535 | get_online_cpus(); | ||
536 | /* init cpus already online before registering for hotplug notifier */ | ||
537 | for_each_online_cpu(cpu) { | ||
538 | amd_uncore_cpu_up_prepare(cpu); | ||
539 | smp_call_function_single(cpu, init_cpu_already_online, NULL, 1); | ||
540 | } | ||
541 | |||
542 | register_cpu_notifier(&amd_uncore_cpu_notifier_block); | ||
543 | put_online_cpus(); | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | device_initcall(amd_uncore_init); | ||
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index cc45deb791b0..ffd6050a1de4 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c | |||
@@ -81,6 +81,7 @@ static struct event_constraint intel_nehalem_event_constraints[] __read_mostly = | |||
81 | static struct extra_reg intel_nehalem_extra_regs[] __read_mostly = | 81 | static struct extra_reg intel_nehalem_extra_regs[] __read_mostly = |
82 | { | 82 | { |
83 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff, RSP_0), | 83 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff, RSP_0), |
84 | INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(0x100b), | ||
84 | EVENT_EXTRA_END | 85 | EVENT_EXTRA_END |
85 | }; | 86 | }; |
86 | 87 | ||
@@ -108,6 +109,8 @@ static struct event_constraint intel_snb_event_constraints[] __read_mostly = | |||
108 | INTEL_EVENT_CONSTRAINT(0x48, 0x4), /* L1D_PEND_MISS.PENDING */ | 109 | INTEL_EVENT_CONSTRAINT(0x48, 0x4), /* L1D_PEND_MISS.PENDING */ |
109 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */ | 110 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */ |
110 | INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.LOAD_LATENCY */ | 111 | INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.LOAD_LATENCY */ |
112 | INTEL_UEVENT_CONSTRAINT(0x04a3, 0xf), /* CYCLE_ACTIVITY.CYCLES_NO_DISPATCH */ | ||
113 | INTEL_UEVENT_CONSTRAINT(0x02a3, 0x4), /* CYCLE_ACTIVITY.CYCLES_L1D_PENDING */ | ||
111 | EVENT_CONSTRAINT_END | 114 | EVENT_CONSTRAINT_END |
112 | }; | 115 | }; |
113 | 116 | ||
@@ -136,6 +139,7 @@ static struct extra_reg intel_westmere_extra_regs[] __read_mostly = | |||
136 | { | 139 | { |
137 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff, RSP_0), | 140 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff, RSP_0), |
138 | INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0xffff, RSP_1), | 141 | INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0xffff, RSP_1), |
142 | INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(0x100b), | ||
139 | EVENT_EXTRA_END | 143 | EVENT_EXTRA_END |
140 | }; | 144 | }; |
141 | 145 | ||
@@ -155,6 +159,8 @@ static struct event_constraint intel_gen_event_constraints[] __read_mostly = | |||
155 | static struct extra_reg intel_snb_extra_regs[] __read_mostly = { | 159 | static struct extra_reg intel_snb_extra_regs[] __read_mostly = { |
156 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0x3f807f8fffull, RSP_0), | 160 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0x3f807f8fffull, RSP_0), |
157 | INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0x3f807f8fffull, RSP_1), | 161 | INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0x3f807f8fffull, RSP_1), |
162 | INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(0x01cd), | ||
163 | INTEL_UEVENT_PEBS_LDLAT_EXTRA_REG(0x01cd), | ||
158 | EVENT_EXTRA_END | 164 | EVENT_EXTRA_END |
159 | }; | 165 | }; |
160 | 166 | ||
@@ -164,6 +170,21 @@ static struct extra_reg intel_snbep_extra_regs[] __read_mostly = { | |||
164 | EVENT_EXTRA_END | 170 | EVENT_EXTRA_END |
165 | }; | 171 | }; |
166 | 172 | ||
173 | EVENT_ATTR_STR(mem-loads, mem_ld_nhm, "event=0x0b,umask=0x10,ldlat=3"); | ||
174 | EVENT_ATTR_STR(mem-loads, mem_ld_snb, "event=0xcd,umask=0x1,ldlat=3"); | ||
175 | EVENT_ATTR_STR(mem-stores, mem_st_snb, "event=0xcd,umask=0x2"); | ||
176 | |||
177 | struct attribute *nhm_events_attrs[] = { | ||
178 | EVENT_PTR(mem_ld_nhm), | ||
179 | NULL, | ||
180 | }; | ||
181 | |||
182 | struct attribute *snb_events_attrs[] = { | ||
183 | EVENT_PTR(mem_ld_snb), | ||
184 | EVENT_PTR(mem_st_snb), | ||
185 | NULL, | ||
186 | }; | ||
187 | |||
167 | static u64 intel_pmu_event_map(int hw_event) | 188 | static u64 intel_pmu_event_map(int hw_event) |
168 | { | 189 | { |
169 | return intel_perfmon_event_map[hw_event]; | 190 | return intel_perfmon_event_map[hw_event]; |
@@ -1398,8 +1419,11 @@ x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) | |||
1398 | 1419 | ||
1399 | if (x86_pmu.event_constraints) { | 1420 | if (x86_pmu.event_constraints) { |
1400 | for_each_event_constraint(c, x86_pmu.event_constraints) { | 1421 | for_each_event_constraint(c, x86_pmu.event_constraints) { |
1401 | if ((event->hw.config & c->cmask) == c->code) | 1422 | if ((event->hw.config & c->cmask) == c->code) { |
1423 | /* hw.flags zeroed at initialization */ | ||
1424 | event->hw.flags |= c->flags; | ||
1402 | return c; | 1425 | return c; |
1426 | } | ||
1403 | } | 1427 | } |
1404 | } | 1428 | } |
1405 | 1429 | ||
@@ -1444,6 +1468,7 @@ intel_put_shared_regs_event_constraints(struct cpu_hw_events *cpuc, | |||
1444 | static void intel_put_event_constraints(struct cpu_hw_events *cpuc, | 1468 | static void intel_put_event_constraints(struct cpu_hw_events *cpuc, |
1445 | struct perf_event *event) | 1469 | struct perf_event *event) |
1446 | { | 1470 | { |
1471 | event->hw.flags = 0; | ||
1447 | intel_put_shared_regs_event_constraints(cpuc, event); | 1472 | intel_put_shared_regs_event_constraints(cpuc, event); |
1448 | } | 1473 | } |
1449 | 1474 | ||
@@ -1767,6 +1792,8 @@ static void intel_pmu_flush_branch_stack(void) | |||
1767 | 1792 | ||
1768 | PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63"); | 1793 | PMU_FORMAT_ATTR(offcore_rsp, "config1:0-63"); |
1769 | 1794 | ||
1795 | PMU_FORMAT_ATTR(ldlat, "config1:0-15"); | ||
1796 | |||
1770 | static struct attribute *intel_arch3_formats_attr[] = { | 1797 | static struct attribute *intel_arch3_formats_attr[] = { |
1771 | &format_attr_event.attr, | 1798 | &format_attr_event.attr, |
1772 | &format_attr_umask.attr, | 1799 | &format_attr_umask.attr, |
@@ -1777,6 +1804,7 @@ static struct attribute *intel_arch3_formats_attr[] = { | |||
1777 | &format_attr_cmask.attr, | 1804 | &format_attr_cmask.attr, |
1778 | 1805 | ||
1779 | &format_attr_offcore_rsp.attr, /* XXX do NHM/WSM + SNB breakout */ | 1806 | &format_attr_offcore_rsp.attr, /* XXX do NHM/WSM + SNB breakout */ |
1807 | &format_attr_ldlat.attr, /* PEBS load latency */ | ||
1780 | NULL, | 1808 | NULL, |
1781 | }; | 1809 | }; |
1782 | 1810 | ||
@@ -2037,6 +2065,8 @@ __init int intel_pmu_init(void) | |||
2037 | x86_pmu.enable_all = intel_pmu_nhm_enable_all; | 2065 | x86_pmu.enable_all = intel_pmu_nhm_enable_all; |
2038 | x86_pmu.extra_regs = intel_nehalem_extra_regs; | 2066 | x86_pmu.extra_regs = intel_nehalem_extra_regs; |
2039 | 2067 | ||
2068 | x86_pmu.cpu_events = nhm_events_attrs; | ||
2069 | |||
2040 | /* UOPS_ISSUED.STALLED_CYCLES */ | 2070 | /* UOPS_ISSUED.STALLED_CYCLES */ |
2041 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = | 2071 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = |
2042 | X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1); | 2072 | X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1); |
@@ -2080,6 +2110,8 @@ __init int intel_pmu_init(void) | |||
2080 | x86_pmu.extra_regs = intel_westmere_extra_regs; | 2110 | x86_pmu.extra_regs = intel_westmere_extra_regs; |
2081 | x86_pmu.er_flags |= ERF_HAS_RSP_1; | 2111 | x86_pmu.er_flags |= ERF_HAS_RSP_1; |
2082 | 2112 | ||
2113 | x86_pmu.cpu_events = nhm_events_attrs; | ||
2114 | |||
2083 | /* UOPS_ISSUED.STALLED_CYCLES */ | 2115 | /* UOPS_ISSUED.STALLED_CYCLES */ |
2084 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = | 2116 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = |
2085 | X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1); | 2117 | X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1); |
@@ -2111,6 +2143,8 @@ __init int intel_pmu_init(void) | |||
2111 | x86_pmu.er_flags |= ERF_HAS_RSP_1; | 2143 | x86_pmu.er_flags |= ERF_HAS_RSP_1; |
2112 | x86_pmu.er_flags |= ERF_NO_HT_SHARING; | 2144 | x86_pmu.er_flags |= ERF_NO_HT_SHARING; |
2113 | 2145 | ||
2146 | x86_pmu.cpu_events = snb_events_attrs; | ||
2147 | |||
2114 | /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */ | 2148 | /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */ |
2115 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = | 2149 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = |
2116 | X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1); | 2150 | X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1); |
@@ -2140,6 +2174,8 @@ __init int intel_pmu_init(void) | |||
2140 | x86_pmu.er_flags |= ERF_HAS_RSP_1; | 2174 | x86_pmu.er_flags |= ERF_HAS_RSP_1; |
2141 | x86_pmu.er_flags |= ERF_NO_HT_SHARING; | 2175 | x86_pmu.er_flags |= ERF_NO_HT_SHARING; |
2142 | 2176 | ||
2177 | x86_pmu.cpu_events = snb_events_attrs; | ||
2178 | |||
2143 | /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */ | 2179 | /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */ |
2144 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = | 2180 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = |
2145 | X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1); | 2181 | X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1); |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c index 26830f3af0df..60250f687052 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_ds.c +++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c | |||
@@ -24,6 +24,130 @@ struct pebs_record_32 { | |||
24 | 24 | ||
25 | */ | 25 | */ |
26 | 26 | ||
27 | union intel_x86_pebs_dse { | ||
28 | u64 val; | ||
29 | struct { | ||
30 | unsigned int ld_dse:4; | ||
31 | unsigned int ld_stlb_miss:1; | ||
32 | unsigned int ld_locked:1; | ||
33 | unsigned int ld_reserved:26; | ||
34 | }; | ||
35 | struct { | ||
36 | unsigned int st_l1d_hit:1; | ||
37 | unsigned int st_reserved1:3; | ||
38 | unsigned int st_stlb_miss:1; | ||
39 | unsigned int st_locked:1; | ||
40 | unsigned int st_reserved2:26; | ||
41 | }; | ||
42 | }; | ||
43 | |||
44 | |||
45 | /* | ||
46 | * Map PEBS Load Latency Data Source encodings to generic | ||
47 | * memory data source information | ||
48 | */ | ||
49 | #define P(a, b) PERF_MEM_S(a, b) | ||
50 | #define OP_LH (P(OP, LOAD) | P(LVL, HIT)) | ||
51 | #define SNOOP_NONE_MISS (P(SNOOP, NONE) | P(SNOOP, MISS)) | ||
52 | |||
53 | static const u64 pebs_data_source[] = { | ||
54 | P(OP, LOAD) | P(LVL, MISS) | P(LVL, L3) | P(SNOOP, NA),/* 0x00:ukn L3 */ | ||
55 | OP_LH | P(LVL, L1) | P(SNOOP, NONE), /* 0x01: L1 local */ | ||
56 | OP_LH | P(LVL, LFB) | P(SNOOP, NONE), /* 0x02: LFB hit */ | ||
57 | OP_LH | P(LVL, L2) | P(SNOOP, NONE), /* 0x03: L2 hit */ | ||
58 | OP_LH | P(LVL, L3) | P(SNOOP, NONE), /* 0x04: L3 hit */ | ||
59 | OP_LH | P(LVL, L3) | P(SNOOP, MISS), /* 0x05: L3 hit, snoop miss */ | ||
60 | OP_LH | P(LVL, L3) | P(SNOOP, HIT), /* 0x06: L3 hit, snoop hit */ | ||
61 | OP_LH | P(LVL, L3) | P(SNOOP, HITM), /* 0x07: L3 hit, snoop hitm */ | ||
62 | OP_LH | P(LVL, REM_CCE1) | P(SNOOP, HIT), /* 0x08: L3 miss snoop hit */ | ||
63 | OP_LH | P(LVL, REM_CCE1) | P(SNOOP, HITM), /* 0x09: L3 miss snoop hitm*/ | ||
64 | OP_LH | P(LVL, LOC_RAM) | P(SNOOP, HIT), /* 0x0a: L3 miss, shared */ | ||
65 | OP_LH | P(LVL, REM_RAM1) | P(SNOOP, HIT), /* 0x0b: L3 miss, shared */ | ||
66 | OP_LH | P(LVL, LOC_RAM) | SNOOP_NONE_MISS,/* 0x0c: L3 miss, excl */ | ||
67 | OP_LH | P(LVL, REM_RAM1) | SNOOP_NONE_MISS,/* 0x0d: L3 miss, excl */ | ||
68 | OP_LH | P(LVL, IO) | P(SNOOP, NONE), /* 0x0e: I/O */ | ||
69 | OP_LH | P(LVL, UNC) | P(SNOOP, NONE), /* 0x0f: uncached */ | ||
70 | }; | ||
71 | |||
72 | static u64 precise_store_data(u64 status) | ||
73 | { | ||
74 | union intel_x86_pebs_dse dse; | ||
75 | u64 val = P(OP, STORE) | P(SNOOP, NA) | P(LVL, L1) | P(TLB, L2); | ||
76 | |||
77 | dse.val = status; | ||
78 | |||
79 | /* | ||
80 | * bit 4: TLB access | ||
81 | * 1 = stored missed 2nd level TLB | ||
82 | * | ||
83 | * so it either hit the walker or the OS | ||
84 | * otherwise hit 2nd level TLB | ||
85 | */ | ||
86 | if (dse.st_stlb_miss) | ||
87 | val |= P(TLB, MISS); | ||
88 | else | ||
89 | val |= P(TLB, HIT); | ||
90 | |||
91 | /* | ||
92 | * bit 0: hit L1 data cache | ||
93 | * if not set, then all we know is that | ||
94 | * it missed L1D | ||
95 | */ | ||
96 | if (dse.st_l1d_hit) | ||
97 | val |= P(LVL, HIT); | ||
98 | else | ||
99 | val |= P(LVL, MISS); | ||
100 | |||
101 | /* | ||
102 | * bit 5: Locked prefix | ||
103 | */ | ||
104 | if (dse.st_locked) | ||
105 | val |= P(LOCK, LOCKED); | ||
106 | |||
107 | return val; | ||
108 | } | ||
109 | |||
110 | static u64 load_latency_data(u64 status) | ||
111 | { | ||
112 | union intel_x86_pebs_dse dse; | ||
113 | u64 val; | ||
114 | int model = boot_cpu_data.x86_model; | ||
115 | int fam = boot_cpu_data.x86; | ||
116 | |||
117 | dse.val = status; | ||
118 | |||
119 | /* | ||
120 | * use the mapping table for bit 0-3 | ||
121 | */ | ||
122 | val = pebs_data_source[dse.ld_dse]; | ||
123 | |||
124 | /* | ||
125 | * Nehalem models do not support TLB, Lock infos | ||
126 | */ | ||
127 | if (fam == 0x6 && (model == 26 || model == 30 | ||
128 | || model == 31 || model == 46)) { | ||
129 | val |= P(TLB, NA) | P(LOCK, NA); | ||
130 | return val; | ||
131 | } | ||
132 | /* | ||
133 | * bit 4: TLB access | ||
134 | * 0 = did not miss 2nd level TLB | ||
135 | * 1 = missed 2nd level TLB | ||
136 | */ | ||
137 | if (dse.ld_stlb_miss) | ||
138 | val |= P(TLB, MISS) | P(TLB, L2); | ||
139 | else | ||
140 | val |= P(TLB, HIT) | P(TLB, L1) | P(TLB, L2); | ||
141 | |||
142 | /* | ||
143 | * bit 5: locked prefix | ||
144 | */ | ||
145 | if (dse.ld_locked) | ||
146 | val |= P(LOCK, LOCKED); | ||
147 | |||
148 | return val; | ||
149 | } | ||
150 | |||
27 | struct pebs_record_core { | 151 | struct pebs_record_core { |
28 | u64 flags, ip; | 152 | u64 flags, ip; |
29 | u64 ax, bx, cx, dx; | 153 | u64 ax, bx, cx, dx; |
@@ -365,7 +489,7 @@ struct event_constraint intel_atom_pebs_event_constraints[] = { | |||
365 | }; | 489 | }; |
366 | 490 | ||
367 | struct event_constraint intel_nehalem_pebs_event_constraints[] = { | 491 | struct event_constraint intel_nehalem_pebs_event_constraints[] = { |
368 | INTEL_EVENT_CONSTRAINT(0x0b, 0xf), /* MEM_INST_RETIRED.* */ | 492 | INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */ |
369 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ | 493 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ |
370 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ | 494 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ |
371 | INTEL_EVENT_CONSTRAINT(0xc0, 0xf), /* INST_RETIRED.ANY */ | 495 | INTEL_EVENT_CONSTRAINT(0xc0, 0xf), /* INST_RETIRED.ANY */ |
@@ -380,7 +504,7 @@ struct event_constraint intel_nehalem_pebs_event_constraints[] = { | |||
380 | }; | 504 | }; |
381 | 505 | ||
382 | struct event_constraint intel_westmere_pebs_event_constraints[] = { | 506 | struct event_constraint intel_westmere_pebs_event_constraints[] = { |
383 | INTEL_EVENT_CONSTRAINT(0x0b, 0xf), /* MEM_INST_RETIRED.* */ | 507 | INTEL_PLD_CONSTRAINT(0x100b, 0xf), /* MEM_INST_RETIRED.* */ |
384 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ | 508 | INTEL_EVENT_CONSTRAINT(0x0f, 0xf), /* MEM_UNCORE_RETIRED.* */ |
385 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ | 509 | INTEL_UEVENT_CONSTRAINT(0x010c, 0xf), /* MEM_STORE_RETIRED.DTLB_MISS */ |
386 | INTEL_EVENT_CONSTRAINT(0xc0, 0xf), /* INSTR_RETIRED.* */ | 510 | INTEL_EVENT_CONSTRAINT(0xc0, 0xf), /* INSTR_RETIRED.* */ |
@@ -400,7 +524,8 @@ struct event_constraint intel_snb_pebs_event_constraints[] = { | |||
400 | INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */ | 524 | INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */ |
401 | INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ | 525 | INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ |
402 | INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */ | 526 | INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */ |
403 | INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.* */ | 527 | INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */ |
528 | INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ | ||
404 | INTEL_EVENT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */ | 529 | INTEL_EVENT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */ |
405 | INTEL_EVENT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ | 530 | INTEL_EVENT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ |
406 | INTEL_EVENT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ | 531 | INTEL_EVENT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ |
@@ -414,7 +539,8 @@ struct event_constraint intel_ivb_pebs_event_constraints[] = { | |||
414 | INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */ | 539 | INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */ |
415 | INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ | 540 | INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */ |
416 | INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */ | 541 | INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */ |
417 | INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.* */ | 542 | INTEL_PLD_CONSTRAINT(0x01cd, 0x8), /* MEM_TRANS_RETIRED.LAT_ABOVE_THR */ |
543 | INTEL_PST_CONSTRAINT(0x02cd, 0x8), /* MEM_TRANS_RETIRED.PRECISE_STORES */ | ||
418 | INTEL_EVENT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */ | 544 | INTEL_EVENT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */ |
419 | INTEL_EVENT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ | 545 | INTEL_EVENT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */ |
420 | INTEL_EVENT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ | 546 | INTEL_EVENT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */ |
@@ -431,8 +557,10 @@ struct event_constraint *intel_pebs_constraints(struct perf_event *event) | |||
431 | 557 | ||
432 | if (x86_pmu.pebs_constraints) { | 558 | if (x86_pmu.pebs_constraints) { |
433 | for_each_event_constraint(c, x86_pmu.pebs_constraints) { | 559 | for_each_event_constraint(c, x86_pmu.pebs_constraints) { |
434 | if ((event->hw.config & c->cmask) == c->code) | 560 | if ((event->hw.config & c->cmask) == c->code) { |
561 | event->hw.flags |= c->flags; | ||
435 | return c; | 562 | return c; |
563 | } | ||
436 | } | 564 | } |
437 | } | 565 | } |
438 | 566 | ||
@@ -447,6 +575,11 @@ void intel_pmu_pebs_enable(struct perf_event *event) | |||
447 | hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; | 575 | hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT; |
448 | 576 | ||
449 | cpuc->pebs_enabled |= 1ULL << hwc->idx; | 577 | cpuc->pebs_enabled |= 1ULL << hwc->idx; |
578 | |||
579 | if (event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT) | ||
580 | cpuc->pebs_enabled |= 1ULL << (hwc->idx + 32); | ||
581 | else if (event->hw.flags & PERF_X86_EVENT_PEBS_ST) | ||
582 | cpuc->pebs_enabled |= 1ULL << 63; | ||
450 | } | 583 | } |
451 | 584 | ||
452 | void intel_pmu_pebs_disable(struct perf_event *event) | 585 | void intel_pmu_pebs_disable(struct perf_event *event) |
@@ -559,20 +692,51 @@ static void __intel_pmu_pebs_event(struct perf_event *event, | |||
559 | struct pt_regs *iregs, void *__pebs) | 692 | struct pt_regs *iregs, void *__pebs) |
560 | { | 693 | { |
561 | /* | 694 | /* |
562 | * We cast to pebs_record_core since that is a subset of | 695 | * We cast to pebs_record_nhm to get the load latency data |
563 | * both formats and we don't use the other fields in this | 696 | * if extra_reg MSR_PEBS_LD_LAT_THRESHOLD used |
564 | * routine. | ||
565 | */ | 697 | */ |
566 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 698 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
567 | struct pebs_record_core *pebs = __pebs; | 699 | struct pebs_record_nhm *pebs = __pebs; |
568 | struct perf_sample_data data; | 700 | struct perf_sample_data data; |
569 | struct pt_regs regs; | 701 | struct pt_regs regs; |
702 | u64 sample_type; | ||
703 | int fll, fst; | ||
570 | 704 | ||
571 | if (!intel_pmu_save_and_restart(event)) | 705 | if (!intel_pmu_save_and_restart(event)) |
572 | return; | 706 | return; |
573 | 707 | ||
708 | fll = event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT; | ||
709 | fst = event->hw.flags & PERF_X86_EVENT_PEBS_ST; | ||
710 | |||
574 | perf_sample_data_init(&data, 0, event->hw.last_period); | 711 | perf_sample_data_init(&data, 0, event->hw.last_period); |
575 | 712 | ||
713 | data.period = event->hw.last_period; | ||
714 | sample_type = event->attr.sample_type; | ||
715 | |||
716 | /* | ||
717 | * if PEBS-LL or PreciseStore | ||
718 | */ | ||
719 | if (fll || fst) { | ||
720 | if (sample_type & PERF_SAMPLE_ADDR) | ||
721 | data.addr = pebs->dla; | ||
722 | |||
723 | /* | ||
724 | * Use latency for weight (only avail with PEBS-LL) | ||
725 | */ | ||
726 | if (fll && (sample_type & PERF_SAMPLE_WEIGHT)) | ||
727 | data.weight = pebs->lat; | ||
728 | |||
729 | /* | ||
730 | * data.data_src encodes the data source | ||
731 | */ | ||
732 | if (sample_type & PERF_SAMPLE_DATA_SRC) { | ||
733 | if (fll) | ||
734 | data.data_src.val = load_latency_data(pebs->dse); | ||
735 | else | ||
736 | data.data_src.val = precise_store_data(pebs->dse); | ||
737 | } | ||
738 | } | ||
739 | |||
576 | /* | 740 | /* |
577 | * We use the interrupt regs as a base because the PEBS record | 741 | * We use the interrupt regs as a base because the PEBS record |
578 | * does not contain a full regs set, specifically it seems to | 742 | * does not contain a full regs set, specifically it seems to |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c index b43200dbfe7e..d0f9e5aa2151 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c | |||
@@ -17,6 +17,9 @@ static struct event_constraint constraint_fixed = | |||
17 | static struct event_constraint constraint_empty = | 17 | static struct event_constraint constraint_empty = |
18 | EVENT_CONSTRAINT(0, 0, 0); | 18 | EVENT_CONSTRAINT(0, 0, 0); |
19 | 19 | ||
20 | #define __BITS_VALUE(x, i, n) ((typeof(x))(((x) >> ((i) * (n))) & \ | ||
21 | ((1ULL << (n)) - 1))) | ||
22 | |||
20 | DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7"); | 23 | DEFINE_UNCORE_FORMAT_ATTR(event, event, "config:0-7"); |
21 | DEFINE_UNCORE_FORMAT_ATTR(event_ext, event, "config:0-7,21"); | 24 | DEFINE_UNCORE_FORMAT_ATTR(event_ext, event, "config:0-7,21"); |
22 | DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15"); | 25 | DEFINE_UNCORE_FORMAT_ATTR(umask, umask, "config:8-15"); |
@@ -31,9 +34,13 @@ DEFINE_UNCORE_FORMAT_ATTR(occ_sel, occ_sel, "config:14-15"); | |||
31 | DEFINE_UNCORE_FORMAT_ATTR(occ_invert, occ_invert, "config:30"); | 34 | DEFINE_UNCORE_FORMAT_ATTR(occ_invert, occ_invert, "config:30"); |
32 | DEFINE_UNCORE_FORMAT_ATTR(occ_edge, occ_edge, "config:14-51"); | 35 | DEFINE_UNCORE_FORMAT_ATTR(occ_edge, occ_edge, "config:14-51"); |
33 | DEFINE_UNCORE_FORMAT_ATTR(filter_tid, filter_tid, "config1:0-4"); | 36 | DEFINE_UNCORE_FORMAT_ATTR(filter_tid, filter_tid, "config1:0-4"); |
37 | DEFINE_UNCORE_FORMAT_ATTR(filter_link, filter_link, "config1:5-8"); | ||
34 | DEFINE_UNCORE_FORMAT_ATTR(filter_nid, filter_nid, "config1:10-17"); | 38 | DEFINE_UNCORE_FORMAT_ATTR(filter_nid, filter_nid, "config1:10-17"); |
39 | DEFINE_UNCORE_FORMAT_ATTR(filter_nid2, filter_nid, "config1:32-47"); | ||
35 | DEFINE_UNCORE_FORMAT_ATTR(filter_state, filter_state, "config1:18-22"); | 40 | DEFINE_UNCORE_FORMAT_ATTR(filter_state, filter_state, "config1:18-22"); |
41 | DEFINE_UNCORE_FORMAT_ATTR(filter_state2, filter_state, "config1:17-22"); | ||
36 | DEFINE_UNCORE_FORMAT_ATTR(filter_opc, filter_opc, "config1:23-31"); | 42 | DEFINE_UNCORE_FORMAT_ATTR(filter_opc, filter_opc, "config1:23-31"); |
43 | DEFINE_UNCORE_FORMAT_ATTR(filter_opc2, filter_opc, "config1:52-60"); | ||
37 | DEFINE_UNCORE_FORMAT_ATTR(filter_band0, filter_band0, "config1:0-7"); | 44 | DEFINE_UNCORE_FORMAT_ATTR(filter_band0, filter_band0, "config1:0-7"); |
38 | DEFINE_UNCORE_FORMAT_ATTR(filter_band1, filter_band1, "config1:8-15"); | 45 | DEFINE_UNCORE_FORMAT_ATTR(filter_band1, filter_band1, "config1:8-15"); |
39 | DEFINE_UNCORE_FORMAT_ATTR(filter_band2, filter_band2, "config1:16-23"); | 46 | DEFINE_UNCORE_FORMAT_ATTR(filter_band2, filter_band2, "config1:16-23"); |
@@ -110,6 +117,21 @@ static void uncore_put_constraint(struct intel_uncore_box *box, struct perf_even | |||
110 | reg1->alloc = 0; | 117 | reg1->alloc = 0; |
111 | } | 118 | } |
112 | 119 | ||
120 | static u64 uncore_shared_reg_config(struct intel_uncore_box *box, int idx) | ||
121 | { | ||
122 | struct intel_uncore_extra_reg *er; | ||
123 | unsigned long flags; | ||
124 | u64 config; | ||
125 | |||
126 | er = &box->shared_regs[idx]; | ||
127 | |||
128 | raw_spin_lock_irqsave(&er->lock, flags); | ||
129 | config = er->config; | ||
130 | raw_spin_unlock_irqrestore(&er->lock, flags); | ||
131 | |||
132 | return config; | ||
133 | } | ||
134 | |||
113 | /* Sandy Bridge-EP uncore support */ | 135 | /* Sandy Bridge-EP uncore support */ |
114 | static struct intel_uncore_type snbep_uncore_cbox; | 136 | static struct intel_uncore_type snbep_uncore_cbox; |
115 | static struct intel_uncore_type snbep_uncore_pcu; | 137 | static struct intel_uncore_type snbep_uncore_pcu; |
@@ -205,7 +227,7 @@ static void snbep_uncore_msr_enable_event(struct intel_uncore_box *box, struct p | |||
205 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; | 227 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; |
206 | 228 | ||
207 | if (reg1->idx != EXTRA_REG_NONE) | 229 | if (reg1->idx != EXTRA_REG_NONE) |
208 | wrmsrl(reg1->reg, reg1->config); | 230 | wrmsrl(reg1->reg, uncore_shared_reg_config(box, 0)); |
209 | 231 | ||
210 | wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN); | 232 | wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN); |
211 | } | 233 | } |
@@ -226,29 +248,6 @@ static void snbep_uncore_msr_init_box(struct intel_uncore_box *box) | |||
226 | wrmsrl(msr, SNBEP_PMON_BOX_CTL_INT); | 248 | wrmsrl(msr, SNBEP_PMON_BOX_CTL_INT); |
227 | } | 249 | } |
228 | 250 | ||
229 | static int snbep_uncore_hw_config(struct intel_uncore_box *box, struct perf_event *event) | ||
230 | { | ||
231 | struct hw_perf_event *hwc = &event->hw; | ||
232 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; | ||
233 | |||
234 | if (box->pmu->type == &snbep_uncore_cbox) { | ||
235 | reg1->reg = SNBEP_C0_MSR_PMON_BOX_FILTER + | ||
236 | SNBEP_CBO_MSR_OFFSET * box->pmu->pmu_idx; | ||
237 | reg1->config = event->attr.config1 & | ||
238 | SNBEP_CB0_MSR_PMON_BOX_FILTER_MASK; | ||
239 | } else { | ||
240 | if (box->pmu->type == &snbep_uncore_pcu) { | ||
241 | reg1->reg = SNBEP_PCU_MSR_PMON_BOX_FILTER; | ||
242 | reg1->config = event->attr.config1 & SNBEP_PCU_MSR_PMON_BOX_FILTER_MASK; | ||
243 | } else { | ||
244 | return 0; | ||
245 | } | ||
246 | } | ||
247 | reg1->idx = 0; | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static struct attribute *snbep_uncore_formats_attr[] = { | 251 | static struct attribute *snbep_uncore_formats_attr[] = { |
253 | &format_attr_event.attr, | 252 | &format_attr_event.attr, |
254 | &format_attr_umask.attr, | 253 | &format_attr_umask.attr, |
@@ -345,16 +344,16 @@ static struct attribute_group snbep_uncore_qpi_format_group = { | |||
345 | .attrs = snbep_uncore_qpi_formats_attr, | 344 | .attrs = snbep_uncore_qpi_formats_attr, |
346 | }; | 345 | }; |
347 | 346 | ||
347 | #define SNBEP_UNCORE_MSR_OPS_COMMON_INIT() \ | ||
348 | .init_box = snbep_uncore_msr_init_box, \ | ||
349 | .disable_box = snbep_uncore_msr_disable_box, \ | ||
350 | .enable_box = snbep_uncore_msr_enable_box, \ | ||
351 | .disable_event = snbep_uncore_msr_disable_event, \ | ||
352 | .enable_event = snbep_uncore_msr_enable_event, \ | ||
353 | .read_counter = uncore_msr_read_counter | ||
354 | |||
348 | static struct intel_uncore_ops snbep_uncore_msr_ops = { | 355 | static struct intel_uncore_ops snbep_uncore_msr_ops = { |
349 | .init_box = snbep_uncore_msr_init_box, | 356 | SNBEP_UNCORE_MSR_OPS_COMMON_INIT(), |
350 | .disable_box = snbep_uncore_msr_disable_box, | ||
351 | .enable_box = snbep_uncore_msr_enable_box, | ||
352 | .disable_event = snbep_uncore_msr_disable_event, | ||
353 | .enable_event = snbep_uncore_msr_enable_event, | ||
354 | .read_counter = uncore_msr_read_counter, | ||
355 | .get_constraint = uncore_get_constraint, | ||
356 | .put_constraint = uncore_put_constraint, | ||
357 | .hw_config = snbep_uncore_hw_config, | ||
358 | }; | 357 | }; |
359 | 358 | ||
360 | static struct intel_uncore_ops snbep_uncore_pci_ops = { | 359 | static struct intel_uncore_ops snbep_uncore_pci_ops = { |
@@ -372,6 +371,7 @@ static struct event_constraint snbep_uncore_cbox_constraints[] = { | |||
372 | UNCORE_EVENT_CONSTRAINT(0x04, 0x3), | 371 | UNCORE_EVENT_CONSTRAINT(0x04, 0x3), |
373 | UNCORE_EVENT_CONSTRAINT(0x05, 0x3), | 372 | UNCORE_EVENT_CONSTRAINT(0x05, 0x3), |
374 | UNCORE_EVENT_CONSTRAINT(0x07, 0x3), | 373 | UNCORE_EVENT_CONSTRAINT(0x07, 0x3), |
374 | UNCORE_EVENT_CONSTRAINT(0x09, 0x3), | ||
375 | UNCORE_EVENT_CONSTRAINT(0x11, 0x1), | 375 | UNCORE_EVENT_CONSTRAINT(0x11, 0x1), |
376 | UNCORE_EVENT_CONSTRAINT(0x12, 0x3), | 376 | UNCORE_EVENT_CONSTRAINT(0x12, 0x3), |
377 | UNCORE_EVENT_CONSTRAINT(0x13, 0x3), | 377 | UNCORE_EVENT_CONSTRAINT(0x13, 0x3), |
@@ -421,6 +421,14 @@ static struct event_constraint snbep_uncore_r3qpi_constraints[] = { | |||
421 | UNCORE_EVENT_CONSTRAINT(0x24, 0x3), | 421 | UNCORE_EVENT_CONSTRAINT(0x24, 0x3), |
422 | UNCORE_EVENT_CONSTRAINT(0x25, 0x3), | 422 | UNCORE_EVENT_CONSTRAINT(0x25, 0x3), |
423 | UNCORE_EVENT_CONSTRAINT(0x26, 0x3), | 423 | UNCORE_EVENT_CONSTRAINT(0x26, 0x3), |
424 | UNCORE_EVENT_CONSTRAINT(0x28, 0x3), | ||
425 | UNCORE_EVENT_CONSTRAINT(0x29, 0x3), | ||
426 | UNCORE_EVENT_CONSTRAINT(0x2a, 0x3), | ||
427 | UNCORE_EVENT_CONSTRAINT(0x2b, 0x3), | ||
428 | UNCORE_EVENT_CONSTRAINT(0x2c, 0x3), | ||
429 | UNCORE_EVENT_CONSTRAINT(0x2d, 0x3), | ||
430 | UNCORE_EVENT_CONSTRAINT(0x2e, 0x3), | ||
431 | UNCORE_EVENT_CONSTRAINT(0x2f, 0x3), | ||
424 | UNCORE_EVENT_CONSTRAINT(0x30, 0x3), | 432 | UNCORE_EVENT_CONSTRAINT(0x30, 0x3), |
425 | UNCORE_EVENT_CONSTRAINT(0x31, 0x3), | 433 | UNCORE_EVENT_CONSTRAINT(0x31, 0x3), |
426 | UNCORE_EVENT_CONSTRAINT(0x32, 0x3), | 434 | UNCORE_EVENT_CONSTRAINT(0x32, 0x3), |
@@ -428,6 +436,8 @@ static struct event_constraint snbep_uncore_r3qpi_constraints[] = { | |||
428 | UNCORE_EVENT_CONSTRAINT(0x34, 0x3), | 436 | UNCORE_EVENT_CONSTRAINT(0x34, 0x3), |
429 | UNCORE_EVENT_CONSTRAINT(0x36, 0x3), | 437 | UNCORE_EVENT_CONSTRAINT(0x36, 0x3), |
430 | UNCORE_EVENT_CONSTRAINT(0x37, 0x3), | 438 | UNCORE_EVENT_CONSTRAINT(0x37, 0x3), |
439 | UNCORE_EVENT_CONSTRAINT(0x38, 0x3), | ||
440 | UNCORE_EVENT_CONSTRAINT(0x39, 0x3), | ||
431 | EVENT_CONSTRAINT_END | 441 | EVENT_CONSTRAINT_END |
432 | }; | 442 | }; |
433 | 443 | ||
@@ -446,6 +456,145 @@ static struct intel_uncore_type snbep_uncore_ubox = { | |||
446 | .format_group = &snbep_uncore_ubox_format_group, | 456 | .format_group = &snbep_uncore_ubox_format_group, |
447 | }; | 457 | }; |
448 | 458 | ||
459 | static struct extra_reg snbep_uncore_cbox_extra_regs[] = { | ||
460 | SNBEP_CBO_EVENT_EXTRA_REG(SNBEP_CBO_PMON_CTL_TID_EN, | ||
461 | SNBEP_CBO_PMON_CTL_TID_EN, 0x1), | ||
462 | SNBEP_CBO_EVENT_EXTRA_REG(0x0334, 0xffff, 0x4), | ||
463 | SNBEP_CBO_EVENT_EXTRA_REG(0x0534, 0xffff, 0x4), | ||
464 | SNBEP_CBO_EVENT_EXTRA_REG(0x0934, 0xffff, 0x4), | ||
465 | SNBEP_CBO_EVENT_EXTRA_REG(0x4134, 0xffff, 0x6), | ||
466 | SNBEP_CBO_EVENT_EXTRA_REG(0x0135, 0xffff, 0x8), | ||
467 | SNBEP_CBO_EVENT_EXTRA_REG(0x0335, 0xffff, 0x8), | ||
468 | SNBEP_CBO_EVENT_EXTRA_REG(0x4135, 0xffff, 0xc), | ||
469 | SNBEP_CBO_EVENT_EXTRA_REG(0x4335, 0xffff, 0xc), | ||
470 | SNBEP_CBO_EVENT_EXTRA_REG(0x4435, 0xffff, 0x2), | ||
471 | SNBEP_CBO_EVENT_EXTRA_REG(0x4835, 0xffff, 0x2), | ||
472 | SNBEP_CBO_EVENT_EXTRA_REG(0x4a35, 0xffff, 0x2), | ||
473 | SNBEP_CBO_EVENT_EXTRA_REG(0x5035, 0xffff, 0x2), | ||
474 | SNBEP_CBO_EVENT_EXTRA_REG(0x0136, 0xffff, 0x8), | ||
475 | SNBEP_CBO_EVENT_EXTRA_REG(0x0336, 0xffff, 0x8), | ||
476 | SNBEP_CBO_EVENT_EXTRA_REG(0x4136, 0xffff, 0xc), | ||
477 | SNBEP_CBO_EVENT_EXTRA_REG(0x4336, 0xffff, 0xc), | ||
478 | SNBEP_CBO_EVENT_EXTRA_REG(0x4436, 0xffff, 0x2), | ||
479 | SNBEP_CBO_EVENT_EXTRA_REG(0x4836, 0xffff, 0x2), | ||
480 | SNBEP_CBO_EVENT_EXTRA_REG(0x4a36, 0xffff, 0x2), | ||
481 | SNBEP_CBO_EVENT_EXTRA_REG(0x4037, 0x40ff, 0x2), | ||
482 | EVENT_EXTRA_END | ||
483 | }; | ||
484 | |||
485 | static void snbep_cbox_put_constraint(struct intel_uncore_box *box, struct perf_event *event) | ||
486 | { | ||
487 | struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; | ||
488 | struct intel_uncore_extra_reg *er = &box->shared_regs[0]; | ||
489 | int i; | ||
490 | |||
491 | if (uncore_box_is_fake(box)) | ||
492 | return; | ||
493 | |||
494 | for (i = 0; i < 5; i++) { | ||
495 | if (reg1->alloc & (0x1 << i)) | ||
496 | atomic_sub(1 << (i * 6), &er->ref); | ||
497 | } | ||
498 | reg1->alloc = 0; | ||
499 | } | ||
500 | |||
501 | static struct event_constraint * | ||
502 | __snbep_cbox_get_constraint(struct intel_uncore_box *box, struct perf_event *event, | ||
503 | u64 (*cbox_filter_mask)(int fields)) | ||
504 | { | ||
505 | struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; | ||
506 | struct intel_uncore_extra_reg *er = &box->shared_regs[0]; | ||
507 | int i, alloc = 0; | ||
508 | unsigned long flags; | ||
509 | u64 mask; | ||
510 | |||
511 | if (reg1->idx == EXTRA_REG_NONE) | ||
512 | return NULL; | ||
513 | |||
514 | raw_spin_lock_irqsave(&er->lock, flags); | ||
515 | for (i = 0; i < 5; i++) { | ||
516 | if (!(reg1->idx & (0x1 << i))) | ||
517 | continue; | ||
518 | if (!uncore_box_is_fake(box) && (reg1->alloc & (0x1 << i))) | ||
519 | continue; | ||
520 | |||
521 | mask = cbox_filter_mask(0x1 << i); | ||
522 | if (!__BITS_VALUE(atomic_read(&er->ref), i, 6) || | ||
523 | !((reg1->config ^ er->config) & mask)) { | ||
524 | atomic_add(1 << (i * 6), &er->ref); | ||
525 | er->config &= ~mask; | ||
526 | er->config |= reg1->config & mask; | ||
527 | alloc |= (0x1 << i); | ||
528 | } else { | ||
529 | break; | ||
530 | } | ||
531 | } | ||
532 | raw_spin_unlock_irqrestore(&er->lock, flags); | ||
533 | if (i < 5) | ||
534 | goto fail; | ||
535 | |||
536 | if (!uncore_box_is_fake(box)) | ||
537 | reg1->alloc |= alloc; | ||
538 | |||
539 | return 0; | ||
540 | fail: | ||
541 | for (; i >= 0; i--) { | ||
542 | if (alloc & (0x1 << i)) | ||
543 | atomic_sub(1 << (i * 6), &er->ref); | ||
544 | } | ||
545 | return &constraint_empty; | ||
546 | } | ||
547 | |||
548 | static u64 snbep_cbox_filter_mask(int fields) | ||
549 | { | ||
550 | u64 mask = 0; | ||
551 | |||
552 | if (fields & 0x1) | ||
553 | mask |= SNBEP_CB0_MSR_PMON_BOX_FILTER_TID; | ||
554 | if (fields & 0x2) | ||
555 | mask |= SNBEP_CB0_MSR_PMON_BOX_FILTER_NID; | ||
556 | if (fields & 0x4) | ||
557 | mask |= SNBEP_CB0_MSR_PMON_BOX_FILTER_STATE; | ||
558 | if (fields & 0x8) | ||
559 | mask |= SNBEP_CB0_MSR_PMON_BOX_FILTER_OPC; | ||
560 | |||
561 | return mask; | ||
562 | } | ||
563 | |||
564 | static struct event_constraint * | ||
565 | snbep_cbox_get_constraint(struct intel_uncore_box *box, struct perf_event *event) | ||
566 | { | ||
567 | return __snbep_cbox_get_constraint(box, event, snbep_cbox_filter_mask); | ||
568 | } | ||
569 | |||
570 | static int snbep_cbox_hw_config(struct intel_uncore_box *box, struct perf_event *event) | ||
571 | { | ||
572 | struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; | ||
573 | struct extra_reg *er; | ||
574 | int idx = 0; | ||
575 | |||
576 | for (er = snbep_uncore_cbox_extra_regs; er->msr; er++) { | ||
577 | if (er->event != (event->hw.config & er->config_mask)) | ||
578 | continue; | ||
579 | idx |= er->idx; | ||
580 | } | ||
581 | |||
582 | if (idx) { | ||
583 | reg1->reg = SNBEP_C0_MSR_PMON_BOX_FILTER + | ||
584 | SNBEP_CBO_MSR_OFFSET * box->pmu->pmu_idx; | ||
585 | reg1->config = event->attr.config1 & snbep_cbox_filter_mask(idx); | ||
586 | reg1->idx = idx; | ||
587 | } | ||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | static struct intel_uncore_ops snbep_uncore_cbox_ops = { | ||
592 | SNBEP_UNCORE_MSR_OPS_COMMON_INIT(), | ||
593 | .hw_config = snbep_cbox_hw_config, | ||
594 | .get_constraint = snbep_cbox_get_constraint, | ||
595 | .put_constraint = snbep_cbox_put_constraint, | ||
596 | }; | ||
597 | |||
449 | static struct intel_uncore_type snbep_uncore_cbox = { | 598 | static struct intel_uncore_type snbep_uncore_cbox = { |
450 | .name = "cbox", | 599 | .name = "cbox", |
451 | .num_counters = 4, | 600 | .num_counters = 4, |
@@ -458,10 +607,104 @@ static struct intel_uncore_type snbep_uncore_cbox = { | |||
458 | .msr_offset = SNBEP_CBO_MSR_OFFSET, | 607 | .msr_offset = SNBEP_CBO_MSR_OFFSET, |
459 | .num_shared_regs = 1, | 608 | .num_shared_regs = 1, |
460 | .constraints = snbep_uncore_cbox_constraints, | 609 | .constraints = snbep_uncore_cbox_constraints, |
461 | .ops = &snbep_uncore_msr_ops, | 610 | .ops = &snbep_uncore_cbox_ops, |
462 | .format_group = &snbep_uncore_cbox_format_group, | 611 | .format_group = &snbep_uncore_cbox_format_group, |
463 | }; | 612 | }; |
464 | 613 | ||
614 | static u64 snbep_pcu_alter_er(struct perf_event *event, int new_idx, bool modify) | ||
615 | { | ||
616 | struct hw_perf_event *hwc = &event->hw; | ||
617 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; | ||
618 | u64 config = reg1->config; | ||
619 | |||
620 | if (new_idx > reg1->idx) | ||
621 | config <<= 8 * (new_idx - reg1->idx); | ||
622 | else | ||
623 | config >>= 8 * (reg1->idx - new_idx); | ||
624 | |||
625 | if (modify) { | ||
626 | hwc->config += new_idx - reg1->idx; | ||
627 | reg1->config = config; | ||
628 | reg1->idx = new_idx; | ||
629 | } | ||
630 | return config; | ||
631 | } | ||
632 | |||
633 | static struct event_constraint * | ||
634 | snbep_pcu_get_constraint(struct intel_uncore_box *box, struct perf_event *event) | ||
635 | { | ||
636 | struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; | ||
637 | struct intel_uncore_extra_reg *er = &box->shared_regs[0]; | ||
638 | unsigned long flags; | ||
639 | int idx = reg1->idx; | ||
640 | u64 mask, config1 = reg1->config; | ||
641 | bool ok = false; | ||
642 | |||
643 | if (reg1->idx == EXTRA_REG_NONE || | ||
644 | (!uncore_box_is_fake(box) && reg1->alloc)) | ||
645 | return NULL; | ||
646 | again: | ||
647 | mask = 0xff << (idx * 8); | ||
648 | raw_spin_lock_irqsave(&er->lock, flags); | ||
649 | if (!__BITS_VALUE(atomic_read(&er->ref), idx, 8) || | ||
650 | !((config1 ^ er->config) & mask)) { | ||
651 | atomic_add(1 << (idx * 8), &er->ref); | ||
652 | er->config &= ~mask; | ||
653 | er->config |= config1 & mask; | ||
654 | ok = true; | ||
655 | } | ||
656 | raw_spin_unlock_irqrestore(&er->lock, flags); | ||
657 | |||
658 | if (!ok) { | ||
659 | idx = (idx + 1) % 4; | ||
660 | if (idx != reg1->idx) { | ||
661 | config1 = snbep_pcu_alter_er(event, idx, false); | ||
662 | goto again; | ||
663 | } | ||
664 | return &constraint_empty; | ||
665 | } | ||
666 | |||
667 | if (!uncore_box_is_fake(box)) { | ||
668 | if (idx != reg1->idx) | ||
669 | snbep_pcu_alter_er(event, idx, true); | ||
670 | reg1->alloc = 1; | ||
671 | } | ||
672 | return NULL; | ||
673 | } | ||
674 | |||
675 | static void snbep_pcu_put_constraint(struct intel_uncore_box *box, struct perf_event *event) | ||
676 | { | ||
677 | struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; | ||
678 | struct intel_uncore_extra_reg *er = &box->shared_regs[0]; | ||
679 | |||
680 | if (uncore_box_is_fake(box) || !reg1->alloc) | ||
681 | return; | ||
682 | |||
683 | atomic_sub(1 << (reg1->idx * 8), &er->ref); | ||
684 | reg1->alloc = 0; | ||
685 | } | ||
686 | |||
687 | static int snbep_pcu_hw_config(struct intel_uncore_box *box, struct perf_event *event) | ||
688 | { | ||
689 | struct hw_perf_event *hwc = &event->hw; | ||
690 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; | ||
691 | int ev_sel = hwc->config & SNBEP_PMON_CTL_EV_SEL_MASK; | ||
692 | |||
693 | if (ev_sel >= 0xb && ev_sel <= 0xe) { | ||
694 | reg1->reg = SNBEP_PCU_MSR_PMON_BOX_FILTER; | ||
695 | reg1->idx = ev_sel - 0xb; | ||
696 | reg1->config = event->attr.config1 & (0xff << reg1->idx); | ||
697 | } | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | static struct intel_uncore_ops snbep_uncore_pcu_ops = { | ||
702 | SNBEP_UNCORE_MSR_OPS_COMMON_INIT(), | ||
703 | .hw_config = snbep_pcu_hw_config, | ||
704 | .get_constraint = snbep_pcu_get_constraint, | ||
705 | .put_constraint = snbep_pcu_put_constraint, | ||
706 | }; | ||
707 | |||
465 | static struct intel_uncore_type snbep_uncore_pcu = { | 708 | static struct intel_uncore_type snbep_uncore_pcu = { |
466 | .name = "pcu", | 709 | .name = "pcu", |
467 | .num_counters = 4, | 710 | .num_counters = 4, |
@@ -472,7 +715,7 @@ static struct intel_uncore_type snbep_uncore_pcu = { | |||
472 | .event_mask = SNBEP_PCU_MSR_PMON_RAW_EVENT_MASK, | 715 | .event_mask = SNBEP_PCU_MSR_PMON_RAW_EVENT_MASK, |
473 | .box_ctl = SNBEP_PCU_MSR_PMON_BOX_CTL, | 716 | .box_ctl = SNBEP_PCU_MSR_PMON_BOX_CTL, |
474 | .num_shared_regs = 1, | 717 | .num_shared_regs = 1, |
475 | .ops = &snbep_uncore_msr_ops, | 718 | .ops = &snbep_uncore_pcu_ops, |
476 | .format_group = &snbep_uncore_pcu_format_group, | 719 | .format_group = &snbep_uncore_pcu_format_group, |
477 | }; | 720 | }; |
478 | 721 | ||
@@ -544,55 +787,63 @@ static struct intel_uncore_type snbep_uncore_r3qpi = { | |||
544 | SNBEP_UNCORE_PCI_COMMON_INIT(), | 787 | SNBEP_UNCORE_PCI_COMMON_INIT(), |
545 | }; | 788 | }; |
546 | 789 | ||
790 | enum { | ||
791 | SNBEP_PCI_UNCORE_HA, | ||
792 | SNBEP_PCI_UNCORE_IMC, | ||
793 | SNBEP_PCI_UNCORE_QPI, | ||
794 | SNBEP_PCI_UNCORE_R2PCIE, | ||
795 | SNBEP_PCI_UNCORE_R3QPI, | ||
796 | }; | ||
797 | |||
547 | static struct intel_uncore_type *snbep_pci_uncores[] = { | 798 | static struct intel_uncore_type *snbep_pci_uncores[] = { |
548 | &snbep_uncore_ha, | 799 | [SNBEP_PCI_UNCORE_HA] = &snbep_uncore_ha, |
549 | &snbep_uncore_imc, | 800 | [SNBEP_PCI_UNCORE_IMC] = &snbep_uncore_imc, |
550 | &snbep_uncore_qpi, | 801 | [SNBEP_PCI_UNCORE_QPI] = &snbep_uncore_qpi, |
551 | &snbep_uncore_r2pcie, | 802 | [SNBEP_PCI_UNCORE_R2PCIE] = &snbep_uncore_r2pcie, |
552 | &snbep_uncore_r3qpi, | 803 | [SNBEP_PCI_UNCORE_R3QPI] = &snbep_uncore_r3qpi, |
553 | NULL, | 804 | NULL, |
554 | }; | 805 | }; |
555 | 806 | ||
556 | static DEFINE_PCI_DEVICE_TABLE(snbep_uncore_pci_ids) = { | 807 | static DEFINE_PCI_DEVICE_TABLE(snbep_uncore_pci_ids) = { |
557 | { /* Home Agent */ | 808 | { /* Home Agent */ |
558 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_HA), | 809 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_HA), |
559 | .driver_data = (unsigned long)&snbep_uncore_ha, | 810 | .driver_data = SNBEP_PCI_UNCORE_HA, |
560 | }, | 811 | }, |
561 | { /* MC Channel 0 */ | 812 | { /* MC Channel 0 */ |
562 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC0), | 813 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC0), |
563 | .driver_data = (unsigned long)&snbep_uncore_imc, | 814 | .driver_data = SNBEP_PCI_UNCORE_IMC, |
564 | }, | 815 | }, |
565 | { /* MC Channel 1 */ | 816 | { /* MC Channel 1 */ |
566 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC1), | 817 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC1), |
567 | .driver_data = (unsigned long)&snbep_uncore_imc, | 818 | .driver_data = SNBEP_PCI_UNCORE_IMC, |
568 | }, | 819 | }, |
569 | { /* MC Channel 2 */ | 820 | { /* MC Channel 2 */ |
570 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC2), | 821 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC2), |
571 | .driver_data = (unsigned long)&snbep_uncore_imc, | 822 | .driver_data = SNBEP_PCI_UNCORE_IMC, |
572 | }, | 823 | }, |
573 | { /* MC Channel 3 */ | 824 | { /* MC Channel 3 */ |
574 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC3), | 825 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_IMC3), |
575 | .driver_data = (unsigned long)&snbep_uncore_imc, | 826 | .driver_data = SNBEP_PCI_UNCORE_IMC, |
576 | }, | 827 | }, |
577 | { /* QPI Port 0 */ | 828 | { /* QPI Port 0 */ |
578 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_QPI0), | 829 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_QPI0), |
579 | .driver_data = (unsigned long)&snbep_uncore_qpi, | 830 | .driver_data = SNBEP_PCI_UNCORE_QPI, |
580 | }, | 831 | }, |
581 | { /* QPI Port 1 */ | 832 | { /* QPI Port 1 */ |
582 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_QPI1), | 833 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_QPI1), |
583 | .driver_data = (unsigned long)&snbep_uncore_qpi, | 834 | .driver_data = SNBEP_PCI_UNCORE_QPI, |
584 | }, | 835 | }, |
585 | { /* P2PCIe */ | 836 | { /* R2PCIe */ |
586 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R2PCIE), | 837 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R2PCIE), |
587 | .driver_data = (unsigned long)&snbep_uncore_r2pcie, | 838 | .driver_data = SNBEP_PCI_UNCORE_R2PCIE, |
588 | }, | 839 | }, |
589 | { /* R3QPI Link 0 */ | 840 | { /* R3QPI Link 0 */ |
590 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R3QPI0), | 841 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R3QPI0), |
591 | .driver_data = (unsigned long)&snbep_uncore_r3qpi, | 842 | .driver_data = SNBEP_PCI_UNCORE_R3QPI, |
592 | }, | 843 | }, |
593 | { /* R3QPI Link 1 */ | 844 | { /* R3QPI Link 1 */ |
594 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R3QPI1), | 845 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_UNC_R3QPI1), |
595 | .driver_data = (unsigned long)&snbep_uncore_r3qpi, | 846 | .driver_data = SNBEP_PCI_UNCORE_R3QPI, |
596 | }, | 847 | }, |
597 | { /* end: all zeroes */ } | 848 | { /* end: all zeroes */ } |
598 | }; | 849 | }; |
@@ -605,7 +856,7 @@ static struct pci_driver snbep_uncore_pci_driver = { | |||
605 | /* | 856 | /* |
606 | * build pci bus to socket mapping | 857 | * build pci bus to socket mapping |
607 | */ | 858 | */ |
608 | static int snbep_pci2phy_map_init(void) | 859 | static int snbep_pci2phy_map_init(int devid) |
609 | { | 860 | { |
610 | struct pci_dev *ubox_dev = NULL; | 861 | struct pci_dev *ubox_dev = NULL; |
611 | int i, bus, nodeid; | 862 | int i, bus, nodeid; |
@@ -614,9 +865,7 @@ static int snbep_pci2phy_map_init(void) | |||
614 | 865 | ||
615 | while (1) { | 866 | while (1) { |
616 | /* find the UBOX device */ | 867 | /* find the UBOX device */ |
617 | ubox_dev = pci_get_device(PCI_VENDOR_ID_INTEL, | 868 | ubox_dev = pci_get_device(PCI_VENDOR_ID_INTEL, devid, ubox_dev); |
618 | PCI_DEVICE_ID_INTEL_JAKETOWN_UBOX, | ||
619 | ubox_dev); | ||
620 | if (!ubox_dev) | 869 | if (!ubox_dev) |
621 | break; | 870 | break; |
622 | bus = ubox_dev->bus->number; | 871 | bus = ubox_dev->bus->number; |
@@ -639,7 +888,7 @@ static int snbep_pci2phy_map_init(void) | |||
639 | break; | 888 | break; |
640 | } | 889 | } |
641 | } | 890 | } |
642 | }; | 891 | } |
643 | 892 | ||
644 | if (ubox_dev) | 893 | if (ubox_dev) |
645 | pci_dev_put(ubox_dev); | 894 | pci_dev_put(ubox_dev); |
@@ -648,6 +897,440 @@ static int snbep_pci2phy_map_init(void) | |||
648 | } | 897 | } |
649 | /* end of Sandy Bridge-EP uncore support */ | 898 | /* end of Sandy Bridge-EP uncore support */ |
650 | 899 | ||
900 | /* IvyTown uncore support */ | ||
901 | static void ivt_uncore_msr_init_box(struct intel_uncore_box *box) | ||
902 | { | ||
903 | unsigned msr = uncore_msr_box_ctl(box); | ||
904 | if (msr) | ||
905 | wrmsrl(msr, IVT_PMON_BOX_CTL_INT); | ||
906 | } | ||
907 | |||
908 | static void ivt_uncore_pci_init_box(struct intel_uncore_box *box) | ||
909 | { | ||
910 | struct pci_dev *pdev = box->pci_dev; | ||
911 | |||
912 | pci_write_config_dword(pdev, SNBEP_PCI_PMON_BOX_CTL, IVT_PMON_BOX_CTL_INT); | ||
913 | } | ||
914 | |||
915 | #define IVT_UNCORE_MSR_OPS_COMMON_INIT() \ | ||
916 | .init_box = ivt_uncore_msr_init_box, \ | ||
917 | .disable_box = snbep_uncore_msr_disable_box, \ | ||
918 | .enable_box = snbep_uncore_msr_enable_box, \ | ||
919 | .disable_event = snbep_uncore_msr_disable_event, \ | ||
920 | .enable_event = snbep_uncore_msr_enable_event, \ | ||
921 | .read_counter = uncore_msr_read_counter | ||
922 | |||
923 | static struct intel_uncore_ops ivt_uncore_msr_ops = { | ||
924 | IVT_UNCORE_MSR_OPS_COMMON_INIT(), | ||
925 | }; | ||
926 | |||
927 | static struct intel_uncore_ops ivt_uncore_pci_ops = { | ||
928 | .init_box = ivt_uncore_pci_init_box, | ||
929 | .disable_box = snbep_uncore_pci_disable_box, | ||
930 | .enable_box = snbep_uncore_pci_enable_box, | ||
931 | .disable_event = snbep_uncore_pci_disable_event, | ||
932 | .enable_event = snbep_uncore_pci_enable_event, | ||
933 | .read_counter = snbep_uncore_pci_read_counter, | ||
934 | }; | ||
935 | |||
936 | #define IVT_UNCORE_PCI_COMMON_INIT() \ | ||
937 | .perf_ctr = SNBEP_PCI_PMON_CTR0, \ | ||
938 | .event_ctl = SNBEP_PCI_PMON_CTL0, \ | ||
939 | .event_mask = IVT_PMON_RAW_EVENT_MASK, \ | ||
940 | .box_ctl = SNBEP_PCI_PMON_BOX_CTL, \ | ||
941 | .ops = &ivt_uncore_pci_ops, \ | ||
942 | .format_group = &ivt_uncore_format_group | ||
943 | |||
944 | static struct attribute *ivt_uncore_formats_attr[] = { | ||
945 | &format_attr_event.attr, | ||
946 | &format_attr_umask.attr, | ||
947 | &format_attr_edge.attr, | ||
948 | &format_attr_inv.attr, | ||
949 | &format_attr_thresh8.attr, | ||
950 | NULL, | ||
951 | }; | ||
952 | |||
953 | static struct attribute *ivt_uncore_ubox_formats_attr[] = { | ||
954 | &format_attr_event.attr, | ||
955 | &format_attr_umask.attr, | ||
956 | &format_attr_edge.attr, | ||
957 | &format_attr_inv.attr, | ||
958 | &format_attr_thresh5.attr, | ||
959 | NULL, | ||
960 | }; | ||
961 | |||
962 | static struct attribute *ivt_uncore_cbox_formats_attr[] = { | ||
963 | &format_attr_event.attr, | ||
964 | &format_attr_umask.attr, | ||
965 | &format_attr_edge.attr, | ||
966 | &format_attr_tid_en.attr, | ||
967 | &format_attr_thresh8.attr, | ||
968 | &format_attr_filter_tid.attr, | ||
969 | &format_attr_filter_link.attr, | ||
970 | &format_attr_filter_state2.attr, | ||
971 | &format_attr_filter_nid2.attr, | ||
972 | &format_attr_filter_opc2.attr, | ||
973 | NULL, | ||
974 | }; | ||
975 | |||
976 | static struct attribute *ivt_uncore_pcu_formats_attr[] = { | ||
977 | &format_attr_event_ext.attr, | ||
978 | &format_attr_occ_sel.attr, | ||
979 | &format_attr_edge.attr, | ||
980 | &format_attr_thresh5.attr, | ||
981 | &format_attr_occ_invert.attr, | ||
982 | &format_attr_occ_edge.attr, | ||
983 | &format_attr_filter_band0.attr, | ||
984 | &format_attr_filter_band1.attr, | ||
985 | &format_attr_filter_band2.attr, | ||
986 | &format_attr_filter_band3.attr, | ||
987 | NULL, | ||
988 | }; | ||
989 | |||
990 | static struct attribute *ivt_uncore_qpi_formats_attr[] = { | ||
991 | &format_attr_event_ext.attr, | ||
992 | &format_attr_umask.attr, | ||
993 | &format_attr_edge.attr, | ||
994 | &format_attr_thresh8.attr, | ||
995 | NULL, | ||
996 | }; | ||
997 | |||
998 | static struct attribute_group ivt_uncore_format_group = { | ||
999 | .name = "format", | ||
1000 | .attrs = ivt_uncore_formats_attr, | ||
1001 | }; | ||
1002 | |||
1003 | static struct attribute_group ivt_uncore_ubox_format_group = { | ||
1004 | .name = "format", | ||
1005 | .attrs = ivt_uncore_ubox_formats_attr, | ||
1006 | }; | ||
1007 | |||
1008 | static struct attribute_group ivt_uncore_cbox_format_group = { | ||
1009 | .name = "format", | ||
1010 | .attrs = ivt_uncore_cbox_formats_attr, | ||
1011 | }; | ||
1012 | |||
1013 | static struct attribute_group ivt_uncore_pcu_format_group = { | ||
1014 | .name = "format", | ||
1015 | .attrs = ivt_uncore_pcu_formats_attr, | ||
1016 | }; | ||
1017 | |||
1018 | static struct attribute_group ivt_uncore_qpi_format_group = { | ||
1019 | .name = "format", | ||
1020 | .attrs = ivt_uncore_qpi_formats_attr, | ||
1021 | }; | ||
1022 | |||
1023 | static struct intel_uncore_type ivt_uncore_ubox = { | ||
1024 | .name = "ubox", | ||
1025 | .num_counters = 2, | ||
1026 | .num_boxes = 1, | ||
1027 | .perf_ctr_bits = 44, | ||
1028 | .fixed_ctr_bits = 48, | ||
1029 | .perf_ctr = SNBEP_U_MSR_PMON_CTR0, | ||
1030 | .event_ctl = SNBEP_U_MSR_PMON_CTL0, | ||
1031 | .event_mask = IVT_U_MSR_PMON_RAW_EVENT_MASK, | ||
1032 | .fixed_ctr = SNBEP_U_MSR_PMON_UCLK_FIXED_CTR, | ||
1033 | .fixed_ctl = SNBEP_U_MSR_PMON_UCLK_FIXED_CTL, | ||
1034 | .ops = &ivt_uncore_msr_ops, | ||
1035 | .format_group = &ivt_uncore_ubox_format_group, | ||
1036 | }; | ||
1037 | |||
1038 | static struct extra_reg ivt_uncore_cbox_extra_regs[] = { | ||
1039 | SNBEP_CBO_EVENT_EXTRA_REG(SNBEP_CBO_PMON_CTL_TID_EN, | ||
1040 | SNBEP_CBO_PMON_CTL_TID_EN, 0x1), | ||
1041 | SNBEP_CBO_EVENT_EXTRA_REG(0x1031, 0x10ff, 0x2), | ||
1042 | SNBEP_CBO_EVENT_EXTRA_REG(0x0334, 0xffff, 0x4), | ||
1043 | SNBEP_CBO_EVENT_EXTRA_REG(0x0534, 0xffff, 0x4), | ||
1044 | SNBEP_CBO_EVENT_EXTRA_REG(0x0934, 0xffff, 0x4), | ||
1045 | SNBEP_CBO_EVENT_EXTRA_REG(0x4134, 0xffff, 0xc), | ||
1046 | SNBEP_CBO_EVENT_EXTRA_REG(0x0135, 0xffff, 0x10), | ||
1047 | SNBEP_CBO_EVENT_EXTRA_REG(0x0335, 0xffff, 0x10), | ||
1048 | SNBEP_CBO_EVENT_EXTRA_REG(0x2135, 0xffff, 0x10), | ||
1049 | SNBEP_CBO_EVENT_EXTRA_REG(0x2335, 0xffff, 0x10), | ||
1050 | SNBEP_CBO_EVENT_EXTRA_REG(0x4135, 0xffff, 0x18), | ||
1051 | SNBEP_CBO_EVENT_EXTRA_REG(0x4335, 0xffff, 0x18), | ||
1052 | SNBEP_CBO_EVENT_EXTRA_REG(0x4435, 0xffff, 0x8), | ||
1053 | SNBEP_CBO_EVENT_EXTRA_REG(0x4835, 0xffff, 0x8), | ||
1054 | SNBEP_CBO_EVENT_EXTRA_REG(0x4a35, 0xffff, 0x8), | ||
1055 | SNBEP_CBO_EVENT_EXTRA_REG(0x5035, 0xffff, 0x8), | ||
1056 | SNBEP_CBO_EVENT_EXTRA_REG(0x8135, 0xffff, 0x10), | ||
1057 | SNBEP_CBO_EVENT_EXTRA_REG(0x8335, 0xffff, 0x10), | ||
1058 | SNBEP_CBO_EVENT_EXTRA_REG(0x0136, 0xffff, 0x10), | ||
1059 | SNBEP_CBO_EVENT_EXTRA_REG(0x0336, 0xffff, 0x10), | ||
1060 | SNBEP_CBO_EVENT_EXTRA_REG(0x2336, 0xffff, 0x10), | ||
1061 | SNBEP_CBO_EVENT_EXTRA_REG(0x2336, 0xffff, 0x10), | ||
1062 | SNBEP_CBO_EVENT_EXTRA_REG(0x4136, 0xffff, 0x18), | ||
1063 | SNBEP_CBO_EVENT_EXTRA_REG(0x4336, 0xffff, 0x18), | ||
1064 | SNBEP_CBO_EVENT_EXTRA_REG(0x4436, 0xffff, 0x8), | ||
1065 | SNBEP_CBO_EVENT_EXTRA_REG(0x4836, 0xffff, 0x8), | ||
1066 | SNBEP_CBO_EVENT_EXTRA_REG(0x4a36, 0xffff, 0x8), | ||
1067 | SNBEP_CBO_EVENT_EXTRA_REG(0x5036, 0xffff, 0x8), | ||
1068 | SNBEP_CBO_EVENT_EXTRA_REG(0x8136, 0xffff, 0x10), | ||
1069 | SNBEP_CBO_EVENT_EXTRA_REG(0x8336, 0xffff, 0x10), | ||
1070 | SNBEP_CBO_EVENT_EXTRA_REG(0x4037, 0x40ff, 0x8), | ||
1071 | EVENT_EXTRA_END | ||
1072 | }; | ||
1073 | |||
1074 | static u64 ivt_cbox_filter_mask(int fields) | ||
1075 | { | ||
1076 | u64 mask = 0; | ||
1077 | |||
1078 | if (fields & 0x1) | ||
1079 | mask |= IVT_CB0_MSR_PMON_BOX_FILTER_TID; | ||
1080 | if (fields & 0x2) | ||
1081 | mask |= IVT_CB0_MSR_PMON_BOX_FILTER_LINK; | ||
1082 | if (fields & 0x4) | ||
1083 | mask |= IVT_CB0_MSR_PMON_BOX_FILTER_STATE; | ||
1084 | if (fields & 0x8) | ||
1085 | mask |= IVT_CB0_MSR_PMON_BOX_FILTER_NID; | ||
1086 | if (fields & 0x10) | ||
1087 | mask |= IVT_CB0_MSR_PMON_BOX_FILTER_OPC; | ||
1088 | |||
1089 | return mask; | ||
1090 | } | ||
1091 | |||
1092 | static struct event_constraint * | ||
1093 | ivt_cbox_get_constraint(struct intel_uncore_box *box, struct perf_event *event) | ||
1094 | { | ||
1095 | return __snbep_cbox_get_constraint(box, event, ivt_cbox_filter_mask); | ||
1096 | } | ||
1097 | |||
1098 | static int ivt_cbox_hw_config(struct intel_uncore_box *box, struct perf_event *event) | ||
1099 | { | ||
1100 | struct hw_perf_event_extra *reg1 = &event->hw.extra_reg; | ||
1101 | struct extra_reg *er; | ||
1102 | int idx = 0; | ||
1103 | |||
1104 | for (er = ivt_uncore_cbox_extra_regs; er->msr; er++) { | ||
1105 | if (er->event != (event->hw.config & er->config_mask)) | ||
1106 | continue; | ||
1107 | idx |= er->idx; | ||
1108 | } | ||
1109 | |||
1110 | if (idx) { | ||
1111 | reg1->reg = SNBEP_C0_MSR_PMON_BOX_FILTER + | ||
1112 | SNBEP_CBO_MSR_OFFSET * box->pmu->pmu_idx; | ||
1113 | reg1->config = event->attr.config1 & ivt_cbox_filter_mask(idx); | ||
1114 | reg1->idx = idx; | ||
1115 | } | ||
1116 | return 0; | ||
1117 | } | ||
1118 | |||
1119 | static void ivt_cbox_enable_event(struct intel_uncore_box *box, struct perf_event *event) | ||
1120 | { | ||
1121 | struct hw_perf_event *hwc = &event->hw; | ||
1122 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; | ||
1123 | |||
1124 | if (reg1->idx != EXTRA_REG_NONE) { | ||
1125 | u64 filter = uncore_shared_reg_config(box, 0); | ||
1126 | wrmsrl(reg1->reg, filter & 0xffffffff); | ||
1127 | wrmsrl(reg1->reg + 6, filter >> 32); | ||
1128 | } | ||
1129 | |||
1130 | wrmsrl(hwc->config_base, hwc->config | SNBEP_PMON_CTL_EN); | ||
1131 | } | ||
1132 | |||
1133 | static struct intel_uncore_ops ivt_uncore_cbox_ops = { | ||
1134 | .init_box = ivt_uncore_msr_init_box, | ||
1135 | .disable_box = snbep_uncore_msr_disable_box, | ||
1136 | .enable_box = snbep_uncore_msr_enable_box, | ||
1137 | .disable_event = snbep_uncore_msr_disable_event, | ||
1138 | .enable_event = ivt_cbox_enable_event, | ||
1139 | .read_counter = uncore_msr_read_counter, | ||
1140 | .hw_config = ivt_cbox_hw_config, | ||
1141 | .get_constraint = ivt_cbox_get_constraint, | ||
1142 | .put_constraint = snbep_cbox_put_constraint, | ||
1143 | }; | ||
1144 | |||
1145 | static struct intel_uncore_type ivt_uncore_cbox = { | ||
1146 | .name = "cbox", | ||
1147 | .num_counters = 4, | ||
1148 | .num_boxes = 15, | ||
1149 | .perf_ctr_bits = 44, | ||
1150 | .event_ctl = SNBEP_C0_MSR_PMON_CTL0, | ||
1151 | .perf_ctr = SNBEP_C0_MSR_PMON_CTR0, | ||
1152 | .event_mask = IVT_CBO_MSR_PMON_RAW_EVENT_MASK, | ||
1153 | .box_ctl = SNBEP_C0_MSR_PMON_BOX_CTL, | ||
1154 | .msr_offset = SNBEP_CBO_MSR_OFFSET, | ||
1155 | .num_shared_regs = 1, | ||
1156 | .constraints = snbep_uncore_cbox_constraints, | ||
1157 | .ops = &ivt_uncore_cbox_ops, | ||
1158 | .format_group = &ivt_uncore_cbox_format_group, | ||
1159 | }; | ||
1160 | |||
1161 | static struct intel_uncore_ops ivt_uncore_pcu_ops = { | ||
1162 | IVT_UNCORE_MSR_OPS_COMMON_INIT(), | ||
1163 | .hw_config = snbep_pcu_hw_config, | ||
1164 | .get_constraint = snbep_pcu_get_constraint, | ||
1165 | .put_constraint = snbep_pcu_put_constraint, | ||
1166 | }; | ||
1167 | |||
1168 | static struct intel_uncore_type ivt_uncore_pcu = { | ||
1169 | .name = "pcu", | ||
1170 | .num_counters = 4, | ||
1171 | .num_boxes = 1, | ||
1172 | .perf_ctr_bits = 48, | ||
1173 | .perf_ctr = SNBEP_PCU_MSR_PMON_CTR0, | ||
1174 | .event_ctl = SNBEP_PCU_MSR_PMON_CTL0, | ||
1175 | .event_mask = IVT_PCU_MSR_PMON_RAW_EVENT_MASK, | ||
1176 | .box_ctl = SNBEP_PCU_MSR_PMON_BOX_CTL, | ||
1177 | .num_shared_regs = 1, | ||
1178 | .ops = &ivt_uncore_pcu_ops, | ||
1179 | .format_group = &ivt_uncore_pcu_format_group, | ||
1180 | }; | ||
1181 | |||
1182 | static struct intel_uncore_type *ivt_msr_uncores[] = { | ||
1183 | &ivt_uncore_ubox, | ||
1184 | &ivt_uncore_cbox, | ||
1185 | &ivt_uncore_pcu, | ||
1186 | NULL, | ||
1187 | }; | ||
1188 | |||
1189 | static struct intel_uncore_type ivt_uncore_ha = { | ||
1190 | .name = "ha", | ||
1191 | .num_counters = 4, | ||
1192 | .num_boxes = 2, | ||
1193 | .perf_ctr_bits = 48, | ||
1194 | IVT_UNCORE_PCI_COMMON_INIT(), | ||
1195 | }; | ||
1196 | |||
1197 | static struct intel_uncore_type ivt_uncore_imc = { | ||
1198 | .name = "imc", | ||
1199 | .num_counters = 4, | ||
1200 | .num_boxes = 8, | ||
1201 | .perf_ctr_bits = 48, | ||
1202 | .fixed_ctr_bits = 48, | ||
1203 | .fixed_ctr = SNBEP_MC_CHy_PCI_PMON_FIXED_CTR, | ||
1204 | .fixed_ctl = SNBEP_MC_CHy_PCI_PMON_FIXED_CTL, | ||
1205 | IVT_UNCORE_PCI_COMMON_INIT(), | ||
1206 | }; | ||
1207 | |||
1208 | static struct intel_uncore_type ivt_uncore_qpi = { | ||
1209 | .name = "qpi", | ||
1210 | .num_counters = 4, | ||
1211 | .num_boxes = 3, | ||
1212 | .perf_ctr_bits = 48, | ||
1213 | .perf_ctr = SNBEP_PCI_PMON_CTR0, | ||
1214 | .event_ctl = SNBEP_PCI_PMON_CTL0, | ||
1215 | .event_mask = IVT_QPI_PCI_PMON_RAW_EVENT_MASK, | ||
1216 | .box_ctl = SNBEP_PCI_PMON_BOX_CTL, | ||
1217 | .ops = &ivt_uncore_pci_ops, | ||
1218 | .format_group = &ivt_uncore_qpi_format_group, | ||
1219 | }; | ||
1220 | |||
1221 | static struct intel_uncore_type ivt_uncore_r2pcie = { | ||
1222 | .name = "r2pcie", | ||
1223 | .num_counters = 4, | ||
1224 | .num_boxes = 1, | ||
1225 | .perf_ctr_bits = 44, | ||
1226 | .constraints = snbep_uncore_r2pcie_constraints, | ||
1227 | IVT_UNCORE_PCI_COMMON_INIT(), | ||
1228 | }; | ||
1229 | |||
1230 | static struct intel_uncore_type ivt_uncore_r3qpi = { | ||
1231 | .name = "r3qpi", | ||
1232 | .num_counters = 3, | ||
1233 | .num_boxes = 2, | ||
1234 | .perf_ctr_bits = 44, | ||
1235 | .constraints = snbep_uncore_r3qpi_constraints, | ||
1236 | IVT_UNCORE_PCI_COMMON_INIT(), | ||
1237 | }; | ||
1238 | |||
1239 | enum { | ||
1240 | IVT_PCI_UNCORE_HA, | ||
1241 | IVT_PCI_UNCORE_IMC, | ||
1242 | IVT_PCI_UNCORE_QPI, | ||
1243 | IVT_PCI_UNCORE_R2PCIE, | ||
1244 | IVT_PCI_UNCORE_R3QPI, | ||
1245 | }; | ||
1246 | |||
1247 | static struct intel_uncore_type *ivt_pci_uncores[] = { | ||
1248 | [IVT_PCI_UNCORE_HA] = &ivt_uncore_ha, | ||
1249 | [IVT_PCI_UNCORE_IMC] = &ivt_uncore_imc, | ||
1250 | [IVT_PCI_UNCORE_QPI] = &ivt_uncore_qpi, | ||
1251 | [IVT_PCI_UNCORE_R2PCIE] = &ivt_uncore_r2pcie, | ||
1252 | [IVT_PCI_UNCORE_R3QPI] = &ivt_uncore_r3qpi, | ||
1253 | NULL, | ||
1254 | }; | ||
1255 | |||
1256 | static DEFINE_PCI_DEVICE_TABLE(ivt_uncore_pci_ids) = { | ||
1257 | { /* Home Agent 0 */ | ||
1258 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe30), | ||
1259 | .driver_data = IVT_PCI_UNCORE_HA, | ||
1260 | }, | ||
1261 | { /* Home Agent 1 */ | ||
1262 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe38), | ||
1263 | .driver_data = IVT_PCI_UNCORE_HA, | ||
1264 | }, | ||
1265 | { /* MC0 Channel 0 */ | ||
1266 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xeb4), | ||
1267 | .driver_data = IVT_PCI_UNCORE_IMC, | ||
1268 | }, | ||
1269 | { /* MC0 Channel 1 */ | ||
1270 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xeb5), | ||
1271 | .driver_data = IVT_PCI_UNCORE_IMC, | ||
1272 | }, | ||
1273 | { /* MC0 Channel 3 */ | ||
1274 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xeb0), | ||
1275 | .driver_data = IVT_PCI_UNCORE_IMC, | ||
1276 | }, | ||
1277 | { /* MC0 Channel 4 */ | ||
1278 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xeb1), | ||
1279 | .driver_data = IVT_PCI_UNCORE_IMC, | ||
1280 | }, | ||
1281 | { /* MC1 Channel 0 */ | ||
1282 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xef4), | ||
1283 | .driver_data = IVT_PCI_UNCORE_IMC, | ||
1284 | }, | ||
1285 | { /* MC1 Channel 1 */ | ||
1286 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xef5), | ||
1287 | .driver_data = IVT_PCI_UNCORE_IMC, | ||
1288 | }, | ||
1289 | { /* MC1 Channel 3 */ | ||
1290 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xef0), | ||
1291 | .driver_data = IVT_PCI_UNCORE_IMC, | ||
1292 | }, | ||
1293 | { /* MC1 Channel 4 */ | ||
1294 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xef1), | ||
1295 | .driver_data = IVT_PCI_UNCORE_IMC, | ||
1296 | }, | ||
1297 | { /* QPI0 Port 0 */ | ||
1298 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe32), | ||
1299 | .driver_data = IVT_PCI_UNCORE_QPI, | ||
1300 | }, | ||
1301 | { /* QPI0 Port 1 */ | ||
1302 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe33), | ||
1303 | .driver_data = IVT_PCI_UNCORE_QPI, | ||
1304 | }, | ||
1305 | { /* QPI1 Port 2 */ | ||
1306 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe3a), | ||
1307 | .driver_data = IVT_PCI_UNCORE_QPI, | ||
1308 | }, | ||
1309 | { /* R2PCIe */ | ||
1310 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe34), | ||
1311 | .driver_data = IVT_PCI_UNCORE_R2PCIE, | ||
1312 | }, | ||
1313 | { /* R3QPI0 Link 0 */ | ||
1314 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe36), | ||
1315 | .driver_data = IVT_PCI_UNCORE_R3QPI, | ||
1316 | }, | ||
1317 | { /* R3QPI0 Link 1 */ | ||
1318 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe37), | ||
1319 | .driver_data = IVT_PCI_UNCORE_R3QPI, | ||
1320 | }, | ||
1321 | { /* R3QPI1 Link 2 */ | ||
1322 | PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe3e), | ||
1323 | .driver_data = IVT_PCI_UNCORE_R3QPI, | ||
1324 | }, | ||
1325 | { /* end: all zeroes */ } | ||
1326 | }; | ||
1327 | |||
1328 | static struct pci_driver ivt_uncore_pci_driver = { | ||
1329 | .name = "ivt_uncore", | ||
1330 | .id_table = ivt_uncore_pci_ids, | ||
1331 | }; | ||
1332 | /* end of IvyTown uncore support */ | ||
1333 | |||
651 | /* Sandy Bridge uncore support */ | 1334 | /* Sandy Bridge uncore support */ |
652 | static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) | 1335 | static void snb_uncore_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) |
653 | { | 1336 | { |
@@ -808,9 +1491,6 @@ static struct intel_uncore_type *nhm_msr_uncores[] = { | |||
808 | /* end of Nehalem uncore support */ | 1491 | /* end of Nehalem uncore support */ |
809 | 1492 | ||
810 | /* Nehalem-EX uncore support */ | 1493 | /* Nehalem-EX uncore support */ |
811 | #define __BITS_VALUE(x, i, n) ((typeof(x))(((x) >> ((i) * (n))) & \ | ||
812 | ((1ULL << (n)) - 1))) | ||
813 | |||
814 | DEFINE_UNCORE_FORMAT_ATTR(event5, event, "config:1-5"); | 1494 | DEFINE_UNCORE_FORMAT_ATTR(event5, event, "config:1-5"); |
815 | DEFINE_UNCORE_FORMAT_ATTR(counter, counter, "config:6-7"); | 1495 | DEFINE_UNCORE_FORMAT_ATTR(counter, counter, "config:6-7"); |
816 | DEFINE_UNCORE_FORMAT_ATTR(match, match, "config1:0-63"); | 1496 | DEFINE_UNCORE_FORMAT_ATTR(match, match, "config1:0-63"); |
@@ -1161,7 +1841,7 @@ static struct extra_reg nhmex_uncore_mbox_extra_regs[] = { | |||
1161 | }; | 1841 | }; |
1162 | 1842 | ||
1163 | /* Nehalem-EX or Westmere-EX ? */ | 1843 | /* Nehalem-EX or Westmere-EX ? */ |
1164 | bool uncore_nhmex; | 1844 | static bool uncore_nhmex; |
1165 | 1845 | ||
1166 | static bool nhmex_mbox_get_shared_reg(struct intel_uncore_box *box, int idx, u64 config) | 1846 | static bool nhmex_mbox_get_shared_reg(struct intel_uncore_box *box, int idx, u64 config) |
1167 | { | 1847 | { |
@@ -1239,7 +1919,7 @@ static void nhmex_mbox_put_shared_reg(struct intel_uncore_box *box, int idx) | |||
1239 | atomic_sub(1 << (idx * 8), &er->ref); | 1919 | atomic_sub(1 << (idx * 8), &er->ref); |
1240 | } | 1920 | } |
1241 | 1921 | ||
1242 | u64 nhmex_mbox_alter_er(struct perf_event *event, int new_idx, bool modify) | 1922 | static u64 nhmex_mbox_alter_er(struct perf_event *event, int new_idx, bool modify) |
1243 | { | 1923 | { |
1244 | struct hw_perf_event *hwc = &event->hw; | 1924 | struct hw_perf_event *hwc = &event->hw; |
1245 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; | 1925 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; |
@@ -1554,7 +2234,7 @@ static struct intel_uncore_type nhmex_uncore_mbox = { | |||
1554 | .format_group = &nhmex_uncore_mbox_format_group, | 2234 | .format_group = &nhmex_uncore_mbox_format_group, |
1555 | }; | 2235 | }; |
1556 | 2236 | ||
1557 | void nhmex_rbox_alter_er(struct intel_uncore_box *box, struct perf_event *event) | 2237 | static void nhmex_rbox_alter_er(struct intel_uncore_box *box, struct perf_event *event) |
1558 | { | 2238 | { |
1559 | struct hw_perf_event *hwc = &event->hw; | 2239 | struct hw_perf_event *hwc = &event->hw; |
1560 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; | 2240 | struct hw_perf_event_extra *reg1 = &hwc->extra_reg; |
@@ -1724,21 +2404,6 @@ static int nhmex_rbox_hw_config(struct intel_uncore_box *box, struct perf_event | |||
1724 | return 0; | 2404 | return 0; |
1725 | } | 2405 | } |
1726 | 2406 | ||
1727 | static u64 nhmex_rbox_shared_reg_config(struct intel_uncore_box *box, int idx) | ||
1728 | { | ||
1729 | struct intel_uncore_extra_reg *er; | ||
1730 | unsigned long flags; | ||
1731 | u64 config; | ||
1732 | |||
1733 | er = &box->shared_regs[idx]; | ||
1734 | |||
1735 | raw_spin_lock_irqsave(&er->lock, flags); | ||
1736 | config = er->config; | ||
1737 | raw_spin_unlock_irqrestore(&er->lock, flags); | ||
1738 | |||
1739 | return config; | ||
1740 | } | ||
1741 | |||
1742 | static void nhmex_rbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) | 2407 | static void nhmex_rbox_msr_enable_event(struct intel_uncore_box *box, struct perf_event *event) |
1743 | { | 2408 | { |
1744 | struct hw_perf_event *hwc = &event->hw; | 2409 | struct hw_perf_event *hwc = &event->hw; |
@@ -1759,7 +2424,7 @@ static void nhmex_rbox_msr_enable_event(struct intel_uncore_box *box, struct per | |||
1759 | case 2: | 2424 | case 2: |
1760 | case 3: | 2425 | case 3: |
1761 | wrmsrl(NHMEX_R_MSR_PORTN_QLX_CFG(port), | 2426 | wrmsrl(NHMEX_R_MSR_PORTN_QLX_CFG(port), |
1762 | nhmex_rbox_shared_reg_config(box, 2 + (idx / 6) * 5)); | 2427 | uncore_shared_reg_config(box, 2 + (idx / 6) * 5)); |
1763 | break; | 2428 | break; |
1764 | case 4: | 2429 | case 4: |
1765 | wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port), | 2430 | wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port), |
@@ -2285,7 +2950,7 @@ out: | |||
2285 | return ret; | 2950 | return ret; |
2286 | } | 2951 | } |
2287 | 2952 | ||
2288 | int uncore_pmu_event_init(struct perf_event *event) | 2953 | static int uncore_pmu_event_init(struct perf_event *event) |
2289 | { | 2954 | { |
2290 | struct intel_uncore_pmu *pmu; | 2955 | struct intel_uncore_pmu *pmu; |
2291 | struct intel_uncore_box *box; | 2956 | struct intel_uncore_box *box; |
@@ -2438,7 +3103,7 @@ static int __init uncore_type_init(struct intel_uncore_type *type) | |||
2438 | 3103 | ||
2439 | type->unconstrainted = (struct event_constraint) | 3104 | type->unconstrainted = (struct event_constraint) |
2440 | __EVENT_CONSTRAINT(0, (1ULL << type->num_counters) - 1, | 3105 | __EVENT_CONSTRAINT(0, (1ULL << type->num_counters) - 1, |
2441 | 0, type->num_counters, 0); | 3106 | 0, type->num_counters, 0, 0); |
2442 | 3107 | ||
2443 | for (i = 0; i < type->num_boxes; i++) { | 3108 | for (i = 0; i < type->num_boxes; i++) { |
2444 | pmus[i].func_id = -1; | 3109 | pmus[i].func_id = -1; |
@@ -2556,6 +3221,8 @@ static void uncore_pci_remove(struct pci_dev *pdev) | |||
2556 | if (WARN_ON_ONCE(phys_id != box->phys_id)) | 3221 | if (WARN_ON_ONCE(phys_id != box->phys_id)) |
2557 | return; | 3222 | return; |
2558 | 3223 | ||
3224 | pci_set_drvdata(pdev, NULL); | ||
3225 | |||
2559 | raw_spin_lock(&uncore_box_lock); | 3226 | raw_spin_lock(&uncore_box_lock); |
2560 | list_del(&box->list); | 3227 | list_del(&box->list); |
2561 | raw_spin_unlock(&uncore_box_lock); | 3228 | raw_spin_unlock(&uncore_box_lock); |
@@ -2574,11 +3241,7 @@ static void uncore_pci_remove(struct pci_dev *pdev) | |||
2574 | static int uncore_pci_probe(struct pci_dev *pdev, | 3241 | static int uncore_pci_probe(struct pci_dev *pdev, |
2575 | const struct pci_device_id *id) | 3242 | const struct pci_device_id *id) |
2576 | { | 3243 | { |
2577 | struct intel_uncore_type *type; | 3244 | return uncore_pci_add(pci_uncores[id->driver_data], pdev); |
2578 | |||
2579 | type = (struct intel_uncore_type *)id->driver_data; | ||
2580 | |||
2581 | return uncore_pci_add(type, pdev); | ||
2582 | } | 3245 | } |
2583 | 3246 | ||
2584 | static int __init uncore_pci_init(void) | 3247 | static int __init uncore_pci_init(void) |
@@ -2587,12 +3250,19 @@ static int __init uncore_pci_init(void) | |||
2587 | 3250 | ||
2588 | switch (boot_cpu_data.x86_model) { | 3251 | switch (boot_cpu_data.x86_model) { |
2589 | case 45: /* Sandy Bridge-EP */ | 3252 | case 45: /* Sandy Bridge-EP */ |
2590 | ret = snbep_pci2phy_map_init(); | 3253 | ret = snbep_pci2phy_map_init(0x3ce0); |
2591 | if (ret) | 3254 | if (ret) |
2592 | return ret; | 3255 | return ret; |
2593 | pci_uncores = snbep_pci_uncores; | 3256 | pci_uncores = snbep_pci_uncores; |
2594 | uncore_pci_driver = &snbep_uncore_pci_driver; | 3257 | uncore_pci_driver = &snbep_uncore_pci_driver; |
2595 | break; | 3258 | break; |
3259 | case 62: /* IvyTown */ | ||
3260 | ret = snbep_pci2phy_map_init(0x0e1e); | ||
3261 | if (ret) | ||
3262 | return ret; | ||
3263 | pci_uncores = ivt_pci_uncores; | ||
3264 | uncore_pci_driver = &ivt_uncore_pci_driver; | ||
3265 | break; | ||
2596 | default: | 3266 | default: |
2597 | return 0; | 3267 | return 0; |
2598 | } | 3268 | } |
@@ -2622,6 +3292,21 @@ static void __init uncore_pci_exit(void) | |||
2622 | } | 3292 | } |
2623 | } | 3293 | } |
2624 | 3294 | ||
3295 | /* CPU hot plug/unplug are serialized by cpu_add_remove_lock mutex */ | ||
3296 | static LIST_HEAD(boxes_to_free); | ||
3297 | |||
3298 | static void __cpuinit uncore_kfree_boxes(void) | ||
3299 | { | ||
3300 | struct intel_uncore_box *box; | ||
3301 | |||
3302 | while (!list_empty(&boxes_to_free)) { | ||
3303 | box = list_entry(boxes_to_free.next, | ||
3304 | struct intel_uncore_box, list); | ||
3305 | list_del(&box->list); | ||
3306 | kfree(box); | ||
3307 | } | ||
3308 | } | ||
3309 | |||
2625 | static void __cpuinit uncore_cpu_dying(int cpu) | 3310 | static void __cpuinit uncore_cpu_dying(int cpu) |
2626 | { | 3311 | { |
2627 | struct intel_uncore_type *type; | 3312 | struct intel_uncore_type *type; |
@@ -2636,7 +3321,7 @@ static void __cpuinit uncore_cpu_dying(int cpu) | |||
2636 | box = *per_cpu_ptr(pmu->box, cpu); | 3321 | box = *per_cpu_ptr(pmu->box, cpu); |
2637 | *per_cpu_ptr(pmu->box, cpu) = NULL; | 3322 | *per_cpu_ptr(pmu->box, cpu) = NULL; |
2638 | if (box && atomic_dec_and_test(&box->refcnt)) | 3323 | if (box && atomic_dec_and_test(&box->refcnt)) |
2639 | kfree(box); | 3324 | list_add(&box->list, &boxes_to_free); |
2640 | } | 3325 | } |
2641 | } | 3326 | } |
2642 | } | 3327 | } |
@@ -2666,8 +3351,11 @@ static int __cpuinit uncore_cpu_starting(int cpu) | |||
2666 | if (exist && exist->phys_id == phys_id) { | 3351 | if (exist && exist->phys_id == phys_id) { |
2667 | atomic_inc(&exist->refcnt); | 3352 | atomic_inc(&exist->refcnt); |
2668 | *per_cpu_ptr(pmu->box, cpu) = exist; | 3353 | *per_cpu_ptr(pmu->box, cpu) = exist; |
2669 | kfree(box); | 3354 | if (box) { |
2670 | box = NULL; | 3355 | list_add(&box->list, |
3356 | &boxes_to_free); | ||
3357 | box = NULL; | ||
3358 | } | ||
2671 | break; | 3359 | break; |
2672 | } | 3360 | } |
2673 | } | 3361 | } |
@@ -2806,6 +3494,10 @@ static int | |||
2806 | case CPU_DYING: | 3494 | case CPU_DYING: |
2807 | uncore_cpu_dying(cpu); | 3495 | uncore_cpu_dying(cpu); |
2808 | break; | 3496 | break; |
3497 | case CPU_ONLINE: | ||
3498 | case CPU_DEAD: | ||
3499 | uncore_kfree_boxes(); | ||
3500 | break; | ||
2809 | default: | 3501 | default: |
2810 | break; | 3502 | break; |
2811 | } | 3503 | } |
@@ -2871,6 +3563,12 @@ static int __init uncore_cpu_init(void) | |||
2871 | nhmex_uncore_cbox.num_boxes = max_cores; | 3563 | nhmex_uncore_cbox.num_boxes = max_cores; |
2872 | msr_uncores = nhmex_msr_uncores; | 3564 | msr_uncores = nhmex_msr_uncores; |
2873 | break; | 3565 | break; |
3566 | case 62: /* IvyTown */ | ||
3567 | if (ivt_uncore_cbox.num_boxes > max_cores) | ||
3568 | ivt_uncore_cbox.num_boxes = max_cores; | ||
3569 | msr_uncores = ivt_msr_uncores; | ||
3570 | break; | ||
3571 | |||
2874 | default: | 3572 | default: |
2875 | return 0; | 3573 | return 0; |
2876 | } | 3574 | } |
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h index e68a4550e952..f9528917f6e8 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h +++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h | |||
@@ -76,7 +76,7 @@ | |||
76 | #define SNBEP_PMON_CTL_UMASK_MASK 0x0000ff00 | 76 | #define SNBEP_PMON_CTL_UMASK_MASK 0x0000ff00 |
77 | #define SNBEP_PMON_CTL_RST (1 << 17) | 77 | #define SNBEP_PMON_CTL_RST (1 << 17) |
78 | #define SNBEP_PMON_CTL_EDGE_DET (1 << 18) | 78 | #define SNBEP_PMON_CTL_EDGE_DET (1 << 18) |
79 | #define SNBEP_PMON_CTL_EV_SEL_EXT (1 << 21) /* only for QPI */ | 79 | #define SNBEP_PMON_CTL_EV_SEL_EXT (1 << 21) |
80 | #define SNBEP_PMON_CTL_EN (1 << 22) | 80 | #define SNBEP_PMON_CTL_EN (1 << 22) |
81 | #define SNBEP_PMON_CTL_INVERT (1 << 23) | 81 | #define SNBEP_PMON_CTL_INVERT (1 << 23) |
82 | #define SNBEP_PMON_CTL_TRESH_MASK 0xff000000 | 82 | #define SNBEP_PMON_CTL_TRESH_MASK 0xff000000 |
@@ -148,9 +148,20 @@ | |||
148 | #define SNBEP_C0_MSR_PMON_CTL0 0xd10 | 148 | #define SNBEP_C0_MSR_PMON_CTL0 0xd10 |
149 | #define SNBEP_C0_MSR_PMON_BOX_CTL 0xd04 | 149 | #define SNBEP_C0_MSR_PMON_BOX_CTL 0xd04 |
150 | #define SNBEP_C0_MSR_PMON_BOX_FILTER 0xd14 | 150 | #define SNBEP_C0_MSR_PMON_BOX_FILTER 0xd14 |
151 | #define SNBEP_CB0_MSR_PMON_BOX_FILTER_MASK 0xfffffc1f | ||
152 | #define SNBEP_CBO_MSR_OFFSET 0x20 | 151 | #define SNBEP_CBO_MSR_OFFSET 0x20 |
153 | 152 | ||
153 | #define SNBEP_CB0_MSR_PMON_BOX_FILTER_TID 0x1f | ||
154 | #define SNBEP_CB0_MSR_PMON_BOX_FILTER_NID 0x3fc00 | ||
155 | #define SNBEP_CB0_MSR_PMON_BOX_FILTER_STATE 0x7c0000 | ||
156 | #define SNBEP_CB0_MSR_PMON_BOX_FILTER_OPC 0xff800000 | ||
157 | |||
158 | #define SNBEP_CBO_EVENT_EXTRA_REG(e, m, i) { \ | ||
159 | .event = (e), \ | ||
160 | .msr = SNBEP_C0_MSR_PMON_BOX_FILTER, \ | ||
161 | .config_mask = (m), \ | ||
162 | .idx = (i) \ | ||
163 | } | ||
164 | |||
154 | /* SNB-EP PCU register */ | 165 | /* SNB-EP PCU register */ |
155 | #define SNBEP_PCU_MSR_PMON_CTR0 0xc36 | 166 | #define SNBEP_PCU_MSR_PMON_CTR0 0xc36 |
156 | #define SNBEP_PCU_MSR_PMON_CTL0 0xc30 | 167 | #define SNBEP_PCU_MSR_PMON_CTL0 0xc30 |
@@ -160,6 +171,55 @@ | |||
160 | #define SNBEP_PCU_MSR_CORE_C3_CTR 0x3fc | 171 | #define SNBEP_PCU_MSR_CORE_C3_CTR 0x3fc |
161 | #define SNBEP_PCU_MSR_CORE_C6_CTR 0x3fd | 172 | #define SNBEP_PCU_MSR_CORE_C6_CTR 0x3fd |
162 | 173 | ||
174 | /* IVT event control */ | ||
175 | #define IVT_PMON_BOX_CTL_INT (SNBEP_PMON_BOX_CTL_RST_CTRL | \ | ||
176 | SNBEP_PMON_BOX_CTL_RST_CTRS) | ||
177 | #define IVT_PMON_RAW_EVENT_MASK (SNBEP_PMON_CTL_EV_SEL_MASK | \ | ||
178 | SNBEP_PMON_CTL_UMASK_MASK | \ | ||
179 | SNBEP_PMON_CTL_EDGE_DET | \ | ||
180 | SNBEP_PMON_CTL_TRESH_MASK) | ||
181 | /* IVT Ubox */ | ||
182 | #define IVT_U_MSR_PMON_GLOBAL_CTL 0xc00 | ||
183 | #define IVT_U_PMON_GLOBAL_FRZ_ALL (1 << 31) | ||
184 | #define IVT_U_PMON_GLOBAL_UNFRZ_ALL (1 << 29) | ||
185 | |||
186 | #define IVT_U_MSR_PMON_RAW_EVENT_MASK \ | ||
187 | (SNBEP_PMON_CTL_EV_SEL_MASK | \ | ||
188 | SNBEP_PMON_CTL_UMASK_MASK | \ | ||
189 | SNBEP_PMON_CTL_EDGE_DET | \ | ||
190 | SNBEP_U_MSR_PMON_CTL_TRESH_MASK) | ||
191 | /* IVT Cbo */ | ||
192 | #define IVT_CBO_MSR_PMON_RAW_EVENT_MASK (IVT_PMON_RAW_EVENT_MASK | \ | ||
193 | SNBEP_CBO_PMON_CTL_TID_EN) | ||
194 | |||
195 | #define IVT_CB0_MSR_PMON_BOX_FILTER_TID (0x1fULL << 0) | ||
196 | #define IVT_CB0_MSR_PMON_BOX_FILTER_LINK (0xfULL << 5) | ||
197 | #define IVT_CB0_MSR_PMON_BOX_FILTER_STATE (0x3fULL << 17) | ||
198 | #define IVT_CB0_MSR_PMON_BOX_FILTER_NID (0xffffULL << 32) | ||
199 | #define IVT_CB0_MSR_PMON_BOX_FILTER_OPC (0x1ffULL << 52) | ||
200 | #define IVT_CB0_MSR_PMON_BOX_FILTER_C6 (0x1ULL << 61) | ||
201 | #define IVT_CB0_MSR_PMON_BOX_FILTER_NC (0x1ULL << 62) | ||
202 | #define IVT_CB0_MSR_PMON_BOX_FILTER_IOSC (0x1ULL << 63) | ||
203 | |||
204 | /* IVT home agent */ | ||
205 | #define IVT_HA_PCI_PMON_CTL_Q_OCC_RST (1 << 16) | ||
206 | #define IVT_HA_PCI_PMON_RAW_EVENT_MASK \ | ||
207 | (IVT_PMON_RAW_EVENT_MASK | \ | ||
208 | IVT_HA_PCI_PMON_CTL_Q_OCC_RST) | ||
209 | /* IVT PCU */ | ||
210 | #define IVT_PCU_MSR_PMON_RAW_EVENT_MASK \ | ||
211 | (SNBEP_PMON_CTL_EV_SEL_MASK | \ | ||
212 | SNBEP_PMON_CTL_EV_SEL_EXT | \ | ||
213 | SNBEP_PCU_MSR_PMON_CTL_OCC_SEL_MASK | \ | ||
214 | SNBEP_PMON_CTL_EDGE_DET | \ | ||
215 | SNBEP_PCU_MSR_PMON_CTL_TRESH_MASK | \ | ||
216 | SNBEP_PCU_MSR_PMON_CTL_OCC_INVERT | \ | ||
217 | SNBEP_PCU_MSR_PMON_CTL_OCC_EDGE_DET) | ||
218 | /* IVT QPI */ | ||
219 | #define IVT_QPI_PCI_PMON_RAW_EVENT_MASK \ | ||
220 | (IVT_PMON_RAW_EVENT_MASK | \ | ||
221 | SNBEP_PMON_CTL_EV_SEL_EXT) | ||
222 | |||
163 | /* NHM-EX event control */ | 223 | /* NHM-EX event control */ |
164 | #define NHMEX_PMON_CTL_EV_SEL_MASK 0x000000ff | 224 | #define NHMEX_PMON_CTL_EV_SEL_MASK 0x000000ff |
165 | #define NHMEX_PMON_CTL_UMASK_MASK 0x0000ff00 | 225 | #define NHMEX_PMON_CTL_UMASK_MASK 0x0000ff00 |
diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index 92c7e39a079f..3486e6660357 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c | |||
@@ -895,8 +895,8 @@ static void p4_pmu_disable_pebs(void) | |||
895 | * So at moment let leave metrics turned on forever -- it's | 895 | * So at moment let leave metrics turned on forever -- it's |
896 | * ok for now but need to be revisited! | 896 | * ok for now but need to be revisited! |
897 | * | 897 | * |
898 | * (void)wrmsrl_safe(MSR_IA32_PEBS_ENABLE, (u64)0); | 898 | * (void)wrmsrl_safe(MSR_IA32_PEBS_ENABLE, 0); |
899 | * (void)wrmsrl_safe(MSR_P4_PEBS_MATRIX_VERT, (u64)0); | 899 | * (void)wrmsrl_safe(MSR_P4_PEBS_MATRIX_VERT, 0); |
900 | */ | 900 | */ |
901 | } | 901 | } |
902 | 902 | ||
@@ -910,8 +910,7 @@ static inline void p4_pmu_disable_event(struct perf_event *event) | |||
910 | * asserted again and again | 910 | * asserted again and again |
911 | */ | 911 | */ |
912 | (void)wrmsrl_safe(hwc->config_base, | 912 | (void)wrmsrl_safe(hwc->config_base, |
913 | (u64)(p4_config_unpack_cccr(hwc->config)) & | 913 | p4_config_unpack_cccr(hwc->config) & ~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED); |
914 | ~P4_CCCR_ENABLE & ~P4_CCCR_OVF & ~P4_CCCR_RESERVED); | ||
915 | } | 914 | } |
916 | 915 | ||
917 | static void p4_pmu_disable_all(void) | 916 | static void p4_pmu_disable_all(void) |
@@ -957,7 +956,7 @@ static void p4_pmu_enable_event(struct perf_event *event) | |||
957 | u64 escr_addr, cccr; | 956 | u64 escr_addr, cccr; |
958 | 957 | ||
959 | bind = &p4_event_bind_map[idx]; | 958 | bind = &p4_event_bind_map[idx]; |
960 | escr_addr = (u64)bind->escr_msr[thread]; | 959 | escr_addr = bind->escr_msr[thread]; |
961 | 960 | ||
962 | /* | 961 | /* |
963 | * - we dont support cascaded counters yet | 962 | * - we dont support cascaded counters yet |
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c index 7bfe318d3d8a..9895a9a41380 100644 --- a/arch/x86/kernel/kprobes/core.c +++ b/arch/x86/kernel/kprobes/core.c | |||
@@ -353,7 +353,11 @@ int __kprobes __copy_instruction(u8 *dest, u8 *src) | |||
353 | * have given. | 353 | * have given. |
354 | */ | 354 | */ |
355 | newdisp = (u8 *) src + (s64) insn.displacement.value - (u8 *) dest; | 355 | newdisp = (u8 *) src + (s64) insn.displacement.value - (u8 *) dest; |
356 | BUG_ON((s64) (s32) newdisp != newdisp); /* Sanity check. */ | 356 | if ((s64) (s32) newdisp != newdisp) { |
357 | pr_err("Kprobes error: new displacement does not fit into s32 (%llx)\n", newdisp); | ||
358 | pr_err("\tSrc: %p, Dest: %p, old disp: %x\n", src, dest, insn.displacement.value); | ||
359 | return 0; | ||
360 | } | ||
357 | disp = (u8 *) dest + insn_offset_displacement(&insn); | 361 | disp = (u8 *) dest + insn_offset_displacement(&insn); |
358 | *(s32 *) disp = (s32) newdisp; | 362 | *(s32 *) disp = (s32) newdisp; |
359 | } | 363 | } |
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 0ba4cfb4f412..2ed845928b5f 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c | |||
@@ -697,3 +697,32 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | |||
697 | send_sig(SIGTRAP, current, 0); | 697 | send_sig(SIGTRAP, current, 0); |
698 | return ret; | 698 | return ret; |
699 | } | 699 | } |
700 | |||
701 | unsigned long | ||
702 | arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs) | ||
703 | { | ||
704 | int rasize, ncopied; | ||
705 | unsigned long orig_ret_vaddr = 0; /* clear high bits for 32-bit apps */ | ||
706 | |||
707 | rasize = is_ia32_task() ? 4 : 8; | ||
708 | ncopied = copy_from_user(&orig_ret_vaddr, (void __user *)regs->sp, rasize); | ||
709 | if (unlikely(ncopied)) | ||
710 | return -1; | ||
711 | |||
712 | /* check whether address has been already hijacked */ | ||
713 | if (orig_ret_vaddr == trampoline_vaddr) | ||
714 | return orig_ret_vaddr; | ||
715 | |||
716 | ncopied = copy_to_user((void __user *)regs->sp, &trampoline_vaddr, rasize); | ||
717 | if (likely(!ncopied)) | ||
718 | return orig_ret_vaddr; | ||
719 | |||
720 | if (ncopied != rasize) { | ||
721 | pr_err("uprobe: return address clobbered: pid=%d, %%sp=%#lx, " | ||
722 | "%%ip=%#lx\n", current->pid, regs->sp, regs->ip); | ||
723 | |||
724 | force_sig_info(SIGSEGV, SEND_SIG_FORCED, current); | ||
725 | } | ||
726 | |||
727 | return -1; | ||
728 | } | ||
diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 10b8f23fab0f..92669cd182a6 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h | |||
@@ -351,4 +351,10 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); | |||
351 | */ | 351 | */ |
352 | #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) | 352 | #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) |
353 | 353 | ||
354 | /* Ignore/forbid kprobes attach on very low level functions marked by this attribute: */ | ||
355 | #ifdef CONFIG_KPROBES | ||
356 | # define __kprobes __attribute__((__section__(".kprobes.text"))) | ||
357 | #else | ||
358 | # define __kprobes | ||
359 | #endif | ||
354 | #endif /* __LINUX_COMPILER_H */ | 360 | #endif /* __LINUX_COMPILER_H */ |
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h index 4b6ef4d33cc2..ca1d27a0d6a6 100644 --- a/include/linux/kprobes.h +++ b/include/linux/kprobes.h | |||
@@ -29,6 +29,7 @@ | |||
29 | * <jkenisto@us.ibm.com> and Prasanna S Panchamukhi | 29 | * <jkenisto@us.ibm.com> and Prasanna S Panchamukhi |
30 | * <prasanna@in.ibm.com> added function-return probes. | 30 | * <prasanna@in.ibm.com> added function-return probes. |
31 | */ | 31 | */ |
32 | #include <linux/compiler.h> /* for __kprobes */ | ||
32 | #include <linux/linkage.h> | 33 | #include <linux/linkage.h> |
33 | #include <linux/list.h> | 34 | #include <linux/list.h> |
34 | #include <linux/notifier.h> | 35 | #include <linux/notifier.h> |
@@ -49,16 +50,11 @@ | |||
49 | #define KPROBE_REENTER 0x00000004 | 50 | #define KPROBE_REENTER 0x00000004 |
50 | #define KPROBE_HIT_SSDONE 0x00000008 | 51 | #define KPROBE_HIT_SSDONE 0x00000008 |
51 | 52 | ||
52 | /* Attach to insert probes on any functions which should be ignored*/ | ||
53 | #define __kprobes __attribute__((__section__(".kprobes.text"))) | ||
54 | |||
55 | #else /* CONFIG_KPROBES */ | 53 | #else /* CONFIG_KPROBES */ |
56 | typedef int kprobe_opcode_t; | 54 | typedef int kprobe_opcode_t; |
57 | struct arch_specific_insn { | 55 | struct arch_specific_insn { |
58 | int dummy; | 56 | int dummy; |
59 | }; | 57 | }; |
60 | #define __kprobes | ||
61 | |||
62 | #endif /* CONFIG_KPROBES */ | 58 | #endif /* CONFIG_KPROBES */ |
63 | 59 | ||
64 | struct kprobe; | 60 | struct kprobe; |
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 1d795df6f4cf..e0373d26c244 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -21,7 +21,6 @@ | |||
21 | */ | 21 | */ |
22 | 22 | ||
23 | #ifdef CONFIG_PERF_EVENTS | 23 | #ifdef CONFIG_PERF_EVENTS |
24 | # include <linux/cgroup.h> | ||
25 | # include <asm/perf_event.h> | 24 | # include <asm/perf_event.h> |
26 | # include <asm/local64.h> | 25 | # include <asm/local64.h> |
27 | #endif | 26 | #endif |
@@ -128,6 +127,7 @@ struct hw_perf_event { | |||
128 | int event_base_rdpmc; | 127 | int event_base_rdpmc; |
129 | int idx; | 128 | int idx; |
130 | int last_cpu; | 129 | int last_cpu; |
130 | int flags; | ||
131 | 131 | ||
132 | struct hw_perf_event_extra extra_reg; | 132 | struct hw_perf_event_extra extra_reg; |
133 | struct hw_perf_event_extra branch_reg; | 133 | struct hw_perf_event_extra branch_reg; |
@@ -299,22 +299,7 @@ struct swevent_hlist { | |||
299 | #define PERF_ATTACH_GROUP 0x02 | 299 | #define PERF_ATTACH_GROUP 0x02 |
300 | #define PERF_ATTACH_TASK 0x04 | 300 | #define PERF_ATTACH_TASK 0x04 |
301 | 301 | ||
302 | #ifdef CONFIG_CGROUP_PERF | 302 | struct perf_cgroup; |
303 | /* | ||
304 | * perf_cgroup_info keeps track of time_enabled for a cgroup. | ||
305 | * This is a per-cpu dynamically allocated data structure. | ||
306 | */ | ||
307 | struct perf_cgroup_info { | ||
308 | u64 time; | ||
309 | u64 timestamp; | ||
310 | }; | ||
311 | |||
312 | struct perf_cgroup { | ||
313 | struct cgroup_subsys_state css; | ||
314 | struct perf_cgroup_info *info; /* timing info, one per cpu */ | ||
315 | }; | ||
316 | #endif | ||
317 | |||
318 | struct ring_buffer; | 303 | struct ring_buffer; |
319 | 304 | ||
320 | /** | 305 | /** |
@@ -583,11 +568,13 @@ struct perf_sample_data { | |||
583 | u32 reserved; | 568 | u32 reserved; |
584 | } cpu_entry; | 569 | } cpu_entry; |
585 | u64 period; | 570 | u64 period; |
571 | union perf_mem_data_src data_src; | ||
586 | struct perf_callchain_entry *callchain; | 572 | struct perf_callchain_entry *callchain; |
587 | struct perf_raw_record *raw; | 573 | struct perf_raw_record *raw; |
588 | struct perf_branch_stack *br_stack; | 574 | struct perf_branch_stack *br_stack; |
589 | struct perf_regs_user regs_user; | 575 | struct perf_regs_user regs_user; |
590 | u64 stack_user_size; | 576 | u64 stack_user_size; |
577 | u64 weight; | ||
591 | }; | 578 | }; |
592 | 579 | ||
593 | static inline void perf_sample_data_init(struct perf_sample_data *data, | 580 | static inline void perf_sample_data_init(struct perf_sample_data *data, |
@@ -601,6 +588,8 @@ static inline void perf_sample_data_init(struct perf_sample_data *data, | |||
601 | data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE; | 588 | data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE; |
602 | data->regs_user.regs = NULL; | 589 | data->regs_user.regs = NULL; |
603 | data->stack_user_size = 0; | 590 | data->stack_user_size = 0; |
591 | data->weight = 0; | ||
592 | data->data_src.val = 0; | ||
604 | } | 593 | } |
605 | 594 | ||
606 | extern void perf_output_sample(struct perf_output_handle *handle, | 595 | extern void perf_output_sample(struct perf_output_handle *handle, |
@@ -831,6 +820,7 @@ do { \ | |||
831 | struct perf_pmu_events_attr { | 820 | struct perf_pmu_events_attr { |
832 | struct device_attribute attr; | 821 | struct device_attribute attr; |
833 | u64 id; | 822 | u64 id; |
823 | const char *event_str; | ||
834 | }; | 824 | }; |
835 | 825 | ||
836 | #define PMU_EVENT_ATTR(_name, _var, _id, _show) \ | 826 | #define PMU_EVENT_ATTR(_name, _var, _id, _show) \ |
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 02b83db8e2c5..06f28beed7c2 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h | |||
@@ -38,6 +38,8 @@ struct inode; | |||
38 | #define UPROBE_HANDLER_REMOVE 1 | 38 | #define UPROBE_HANDLER_REMOVE 1 |
39 | #define UPROBE_HANDLER_MASK 1 | 39 | #define UPROBE_HANDLER_MASK 1 |
40 | 40 | ||
41 | #define MAX_URETPROBE_DEPTH 64 | ||
42 | |||
41 | enum uprobe_filter_ctx { | 43 | enum uprobe_filter_ctx { |
42 | UPROBE_FILTER_REGISTER, | 44 | UPROBE_FILTER_REGISTER, |
43 | UPROBE_FILTER_UNREGISTER, | 45 | UPROBE_FILTER_UNREGISTER, |
@@ -46,6 +48,9 @@ enum uprobe_filter_ctx { | |||
46 | 48 | ||
47 | struct uprobe_consumer { | 49 | struct uprobe_consumer { |
48 | int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); | 50 | int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); |
51 | int (*ret_handler)(struct uprobe_consumer *self, | ||
52 | unsigned long func, | ||
53 | struct pt_regs *regs); | ||
49 | bool (*filter)(struct uprobe_consumer *self, | 54 | bool (*filter)(struct uprobe_consumer *self, |
50 | enum uprobe_filter_ctx ctx, | 55 | enum uprobe_filter_ctx ctx, |
51 | struct mm_struct *mm); | 56 | struct mm_struct *mm); |
@@ -68,6 +73,8 @@ struct uprobe_task { | |||
68 | enum uprobe_task_state state; | 73 | enum uprobe_task_state state; |
69 | struct arch_uprobe_task autask; | 74 | struct arch_uprobe_task autask; |
70 | 75 | ||
76 | struct return_instance *return_instances; | ||
77 | unsigned int depth; | ||
71 | struct uprobe *active_uprobe; | 78 | struct uprobe *active_uprobe; |
72 | 79 | ||
73 | unsigned long xol_vaddr; | 80 | unsigned long xol_vaddr; |
@@ -100,6 +107,7 @@ struct uprobes_state { | |||
100 | extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); | 107 | extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); |
101 | extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); | 108 | extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); |
102 | extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); | 109 | extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); |
110 | extern bool __weak is_trap_insn(uprobe_opcode_t *insn); | ||
103 | extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); | 111 | extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); |
104 | extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); | 112 | extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); |
105 | extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); | 113 | extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); |
diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 9fa9c622a7f4..fb104e51496e 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h | |||
@@ -132,8 +132,10 @@ enum perf_event_sample_format { | |||
132 | PERF_SAMPLE_BRANCH_STACK = 1U << 11, | 132 | PERF_SAMPLE_BRANCH_STACK = 1U << 11, |
133 | PERF_SAMPLE_REGS_USER = 1U << 12, | 133 | PERF_SAMPLE_REGS_USER = 1U << 12, |
134 | PERF_SAMPLE_STACK_USER = 1U << 13, | 134 | PERF_SAMPLE_STACK_USER = 1U << 13, |
135 | PERF_SAMPLE_WEIGHT = 1U << 14, | ||
136 | PERF_SAMPLE_DATA_SRC = 1U << 15, | ||
135 | 137 | ||
136 | PERF_SAMPLE_MAX = 1U << 14, /* non-ABI */ | 138 | PERF_SAMPLE_MAX = 1U << 16, /* non-ABI */ |
137 | }; | 139 | }; |
138 | 140 | ||
139 | /* | 141 | /* |
@@ -443,6 +445,7 @@ struct perf_event_mmap_page { | |||
443 | #define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) | 445 | #define PERF_RECORD_MISC_GUEST_KERNEL (4 << 0) |
444 | #define PERF_RECORD_MISC_GUEST_USER (5 << 0) | 446 | #define PERF_RECORD_MISC_GUEST_USER (5 << 0) |
445 | 447 | ||
448 | #define PERF_RECORD_MISC_MMAP_DATA (1 << 13) | ||
446 | /* | 449 | /* |
447 | * Indicates that the content of PERF_SAMPLE_IP points to | 450 | * Indicates that the content of PERF_SAMPLE_IP points to |
448 | * the actual instruction that triggered the event. See also | 451 | * the actual instruction that triggered the event. See also |
@@ -588,6 +591,9 @@ enum perf_event_type { | |||
588 | * { u64 size; | 591 | * { u64 size; |
589 | * char data[size]; | 592 | * char data[size]; |
590 | * u64 dyn_size; } && PERF_SAMPLE_STACK_USER | 593 | * u64 dyn_size; } && PERF_SAMPLE_STACK_USER |
594 | * | ||
595 | * { u64 weight; } && PERF_SAMPLE_WEIGHT | ||
596 | * { u64 data_src; } && PERF_SAMPLE_DATA_SRC | ||
591 | * }; | 597 | * }; |
592 | */ | 598 | */ |
593 | PERF_RECORD_SAMPLE = 9, | 599 | PERF_RECORD_SAMPLE = 9, |
@@ -613,4 +619,67 @@ enum perf_callchain_context { | |||
613 | #define PERF_FLAG_FD_OUTPUT (1U << 1) | 619 | #define PERF_FLAG_FD_OUTPUT (1U << 1) |
614 | #define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ | 620 | #define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ |
615 | 621 | ||
622 | union perf_mem_data_src { | ||
623 | __u64 val; | ||
624 | struct { | ||
625 | __u64 mem_op:5, /* type of opcode */ | ||
626 | mem_lvl:14, /* memory hierarchy level */ | ||
627 | mem_snoop:5, /* snoop mode */ | ||
628 | mem_lock:2, /* lock instr */ | ||
629 | mem_dtlb:7, /* tlb access */ | ||
630 | mem_rsvd:31; | ||
631 | }; | ||
632 | }; | ||
633 | |||
634 | /* type of opcode (load/store/prefetch,code) */ | ||
635 | #define PERF_MEM_OP_NA 0x01 /* not available */ | ||
636 | #define PERF_MEM_OP_LOAD 0x02 /* load instruction */ | ||
637 | #define PERF_MEM_OP_STORE 0x04 /* store instruction */ | ||
638 | #define PERF_MEM_OP_PFETCH 0x08 /* prefetch */ | ||
639 | #define PERF_MEM_OP_EXEC 0x10 /* code (execution) */ | ||
640 | #define PERF_MEM_OP_SHIFT 0 | ||
641 | |||
642 | /* memory hierarchy (memory level, hit or miss) */ | ||
643 | #define PERF_MEM_LVL_NA 0x01 /* not available */ | ||
644 | #define PERF_MEM_LVL_HIT 0x02 /* hit level */ | ||
645 | #define PERF_MEM_LVL_MISS 0x04 /* miss level */ | ||
646 | #define PERF_MEM_LVL_L1 0x08 /* L1 */ | ||
647 | #define PERF_MEM_LVL_LFB 0x10 /* Line Fill Buffer */ | ||
648 | #define PERF_MEM_LVL_L2 0x20 /* L2 */ | ||
649 | #define PERF_MEM_LVL_L3 0x40 /* L3 */ | ||
650 | #define PERF_MEM_LVL_LOC_RAM 0x80 /* Local DRAM */ | ||
651 | #define PERF_MEM_LVL_REM_RAM1 0x100 /* Remote DRAM (1 hop) */ | ||
652 | #define PERF_MEM_LVL_REM_RAM2 0x200 /* Remote DRAM (2 hops) */ | ||
653 | #define PERF_MEM_LVL_REM_CCE1 0x400 /* Remote Cache (1 hop) */ | ||
654 | #define PERF_MEM_LVL_REM_CCE2 0x800 /* Remote Cache (2 hops) */ | ||
655 | #define PERF_MEM_LVL_IO 0x1000 /* I/O memory */ | ||
656 | #define PERF_MEM_LVL_UNC 0x2000 /* Uncached memory */ | ||
657 | #define PERF_MEM_LVL_SHIFT 5 | ||
658 | |||
659 | /* snoop mode */ | ||
660 | #define PERF_MEM_SNOOP_NA 0x01 /* not available */ | ||
661 | #define PERF_MEM_SNOOP_NONE 0x02 /* no snoop */ | ||
662 | #define PERF_MEM_SNOOP_HIT 0x04 /* snoop hit */ | ||
663 | #define PERF_MEM_SNOOP_MISS 0x08 /* snoop miss */ | ||
664 | #define PERF_MEM_SNOOP_HITM 0x10 /* snoop hit modified */ | ||
665 | #define PERF_MEM_SNOOP_SHIFT 19 | ||
666 | |||
667 | /* locked instruction */ | ||
668 | #define PERF_MEM_LOCK_NA 0x01 /* not available */ | ||
669 | #define PERF_MEM_LOCK_LOCKED 0x02 /* locked transaction */ | ||
670 | #define PERF_MEM_LOCK_SHIFT 24 | ||
671 | |||
672 | /* TLB access */ | ||
673 | #define PERF_MEM_TLB_NA 0x01 /* not available */ | ||
674 | #define PERF_MEM_TLB_HIT 0x02 /* hit level */ | ||
675 | #define PERF_MEM_TLB_MISS 0x04 /* miss level */ | ||
676 | #define PERF_MEM_TLB_L1 0x08 /* L1 */ | ||
677 | #define PERF_MEM_TLB_L2 0x10 /* L2 */ | ||
678 | #define PERF_MEM_TLB_WK 0x20 /* Hardware Walker*/ | ||
679 | #define PERF_MEM_TLB_OS 0x40 /* OS fault handler */ | ||
680 | #define PERF_MEM_TLB_SHIFT 26 | ||
681 | |||
682 | #define PERF_MEM_S(a, s) \ | ||
683 | (((u64)PERF_MEM_##a##_##s) << PERF_MEM_##a##_SHIFT) | ||
684 | |||
616 | #endif /* _UAPI_LINUX_PERF_EVENT_H */ | 685 | #endif /* _UAPI_LINUX_PERF_EVENT_H */ |
diff --git a/kernel/events/core.c b/kernel/events/core.c index dce6e13cf9d7..3820e3cefbae 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/ftrace_event.h> | 37 | #include <linux/ftrace_event.h> |
38 | #include <linux/hw_breakpoint.h> | 38 | #include <linux/hw_breakpoint.h> |
39 | #include <linux/mm_types.h> | 39 | #include <linux/mm_types.h> |
40 | #include <linux/cgroup.h> | ||
40 | 41 | ||
41 | #include "internal.h" | 42 | #include "internal.h" |
42 | 43 | ||
@@ -234,6 +235,20 @@ static void perf_ctx_unlock(struct perf_cpu_context *cpuctx, | |||
234 | #ifdef CONFIG_CGROUP_PERF | 235 | #ifdef CONFIG_CGROUP_PERF |
235 | 236 | ||
236 | /* | 237 | /* |
238 | * perf_cgroup_info keeps track of time_enabled for a cgroup. | ||
239 | * This is a per-cpu dynamically allocated data structure. | ||
240 | */ | ||
241 | struct perf_cgroup_info { | ||
242 | u64 time; | ||
243 | u64 timestamp; | ||
244 | }; | ||
245 | |||
246 | struct perf_cgroup { | ||
247 | struct cgroup_subsys_state css; | ||
248 | struct perf_cgroup_info __percpu *info; | ||
249 | }; | ||
250 | |||
251 | /* | ||
237 | * Must ensure cgroup is pinned (css_get) before calling | 252 | * Must ensure cgroup is pinned (css_get) before calling |
238 | * this function. In other words, we cannot call this function | 253 | * this function. In other words, we cannot call this function |
239 | * if there is no cgroup event for the current CPU context. | 254 | * if there is no cgroup event for the current CPU context. |
@@ -976,9 +991,15 @@ static void perf_event__header_size(struct perf_event *event) | |||
976 | if (sample_type & PERF_SAMPLE_PERIOD) | 991 | if (sample_type & PERF_SAMPLE_PERIOD) |
977 | size += sizeof(data->period); | 992 | size += sizeof(data->period); |
978 | 993 | ||
994 | if (sample_type & PERF_SAMPLE_WEIGHT) | ||
995 | size += sizeof(data->weight); | ||
996 | |||
979 | if (sample_type & PERF_SAMPLE_READ) | 997 | if (sample_type & PERF_SAMPLE_READ) |
980 | size += event->read_size; | 998 | size += event->read_size; |
981 | 999 | ||
1000 | if (sample_type & PERF_SAMPLE_DATA_SRC) | ||
1001 | size += sizeof(data->data_src.val); | ||
1002 | |||
982 | event->header_size = size; | 1003 | event->header_size = size; |
983 | } | 1004 | } |
984 | 1005 | ||
@@ -4193,6 +4214,12 @@ void perf_output_sample(struct perf_output_handle *handle, | |||
4193 | perf_output_sample_ustack(handle, | 4214 | perf_output_sample_ustack(handle, |
4194 | data->stack_user_size, | 4215 | data->stack_user_size, |
4195 | data->regs_user.regs); | 4216 | data->regs_user.regs); |
4217 | |||
4218 | if (sample_type & PERF_SAMPLE_WEIGHT) | ||
4219 | perf_output_put(handle, data->weight); | ||
4220 | |||
4221 | if (sample_type & PERF_SAMPLE_DATA_SRC) | ||
4222 | perf_output_put(handle, data->data_src.val); | ||
4196 | } | 4223 | } |
4197 | 4224 | ||
4198 | void perf_prepare_sample(struct perf_event_header *header, | 4225 | void perf_prepare_sample(struct perf_event_header *header, |
@@ -4782,6 +4809,9 @@ got_name: | |||
4782 | mmap_event->file_name = name; | 4809 | mmap_event->file_name = name; |
4783 | mmap_event->file_size = size; | 4810 | mmap_event->file_size = size; |
4784 | 4811 | ||
4812 | if (!(vma->vm_flags & VM_EXEC)) | ||
4813 | mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_DATA; | ||
4814 | |||
4785 | mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size; | 4815 | mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size; |
4786 | 4816 | ||
4787 | rcu_read_lock(); | 4817 | rcu_read_lock(); |
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index a567c8c7ef31..f3569747d629 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c | |||
@@ -75,6 +75,15 @@ struct uprobe { | |||
75 | struct arch_uprobe arch; | 75 | struct arch_uprobe arch; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | struct return_instance { | ||
79 | struct uprobe *uprobe; | ||
80 | unsigned long func; | ||
81 | unsigned long orig_ret_vaddr; /* original return address */ | ||
82 | bool chained; /* true, if instance is nested */ | ||
83 | |||
84 | struct return_instance *next; /* keep as stack */ | ||
85 | }; | ||
86 | |||
78 | /* | 87 | /* |
79 | * valid_vma: Verify if the specified vma is an executable vma | 88 | * valid_vma: Verify if the specified vma is an executable vma |
80 | * Relax restrictions while unregistering: vm_flags might have | 89 | * Relax restrictions while unregistering: vm_flags might have |
@@ -173,10 +182,31 @@ bool __weak is_swbp_insn(uprobe_opcode_t *insn) | |||
173 | return *insn == UPROBE_SWBP_INSN; | 182 | return *insn == UPROBE_SWBP_INSN; |
174 | } | 183 | } |
175 | 184 | ||
176 | static void copy_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t *opcode) | 185 | /** |
186 | * is_trap_insn - check if instruction is breakpoint instruction. | ||
187 | * @insn: instruction to be checked. | ||
188 | * Default implementation of is_trap_insn | ||
189 | * Returns true if @insn is a breakpoint instruction. | ||
190 | * | ||
191 | * This function is needed for the case where an architecture has multiple | ||
192 | * trap instructions (like powerpc). | ||
193 | */ | ||
194 | bool __weak is_trap_insn(uprobe_opcode_t *insn) | ||
195 | { | ||
196 | return is_swbp_insn(insn); | ||
197 | } | ||
198 | |||
199 | static void copy_from_page(struct page *page, unsigned long vaddr, void *dst, int len) | ||
177 | { | 200 | { |
178 | void *kaddr = kmap_atomic(page); | 201 | void *kaddr = kmap_atomic(page); |
179 | memcpy(opcode, kaddr + (vaddr & ~PAGE_MASK), UPROBE_SWBP_INSN_SIZE); | 202 | memcpy(dst, kaddr + (vaddr & ~PAGE_MASK), len); |
203 | kunmap_atomic(kaddr); | ||
204 | } | ||
205 | |||
206 | static void copy_to_page(struct page *page, unsigned long vaddr, const void *src, int len) | ||
207 | { | ||
208 | void *kaddr = kmap_atomic(page); | ||
209 | memcpy(kaddr + (vaddr & ~PAGE_MASK), src, len); | ||
180 | kunmap_atomic(kaddr); | 210 | kunmap_atomic(kaddr); |
181 | } | 211 | } |
182 | 212 | ||
@@ -185,7 +215,16 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t | |||
185 | uprobe_opcode_t old_opcode; | 215 | uprobe_opcode_t old_opcode; |
186 | bool is_swbp; | 216 | bool is_swbp; |
187 | 217 | ||
188 | copy_opcode(page, vaddr, &old_opcode); | 218 | /* |
219 | * Note: We only check if the old_opcode is UPROBE_SWBP_INSN here. | ||
220 | * We do not check if it is any other 'trap variant' which could | ||
221 | * be conditional trap instruction such as the one powerpc supports. | ||
222 | * | ||
223 | * The logic is that we do not care if the underlying instruction | ||
224 | * is a trap variant; uprobes always wins over any other (gdb) | ||
225 | * breakpoint. | ||
226 | */ | ||
227 | copy_from_page(page, vaddr, &old_opcode, UPROBE_SWBP_INSN_SIZE); | ||
189 | is_swbp = is_swbp_insn(&old_opcode); | 228 | is_swbp = is_swbp_insn(&old_opcode); |
190 | 229 | ||
191 | if (is_swbp_insn(new_opcode)) { | 230 | if (is_swbp_insn(new_opcode)) { |
@@ -204,7 +243,7 @@ static int verify_opcode(struct page *page, unsigned long vaddr, uprobe_opcode_t | |||
204 | * Expect the breakpoint instruction to be the smallest size instruction for | 243 | * Expect the breakpoint instruction to be the smallest size instruction for |
205 | * the architecture. If an arch has variable length instruction and the | 244 | * the architecture. If an arch has variable length instruction and the |
206 | * breakpoint instruction is not of the smallest length instruction | 245 | * breakpoint instruction is not of the smallest length instruction |
207 | * supported by that architecture then we need to modify is_swbp_at_addr and | 246 | * supported by that architecture then we need to modify is_trap_at_addr and |
208 | * write_opcode accordingly. This would never be a problem for archs that | 247 | * write_opcode accordingly. This would never be a problem for archs that |
209 | * have fixed length instructions. | 248 | * have fixed length instructions. |
210 | */ | 249 | */ |
@@ -225,7 +264,6 @@ static int write_opcode(struct mm_struct *mm, unsigned long vaddr, | |||
225 | uprobe_opcode_t opcode) | 264 | uprobe_opcode_t opcode) |
226 | { | 265 | { |
227 | struct page *old_page, *new_page; | 266 | struct page *old_page, *new_page; |
228 | void *vaddr_old, *vaddr_new; | ||
229 | struct vm_area_struct *vma; | 267 | struct vm_area_struct *vma; |
230 | int ret; | 268 | int ret; |
231 | 269 | ||
@@ -246,15 +284,8 @@ retry: | |||
246 | 284 | ||
247 | __SetPageUptodate(new_page); | 285 | __SetPageUptodate(new_page); |
248 | 286 | ||
249 | /* copy the page now that we've got it stable */ | 287 | copy_highpage(new_page, old_page); |
250 | vaddr_old = kmap_atomic(old_page); | 288 | copy_to_page(new_page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); |
251 | vaddr_new = kmap_atomic(new_page); | ||
252 | |||
253 | memcpy(vaddr_new, vaddr_old, PAGE_SIZE); | ||
254 | memcpy(vaddr_new + (vaddr & ~PAGE_MASK), &opcode, UPROBE_SWBP_INSN_SIZE); | ||
255 | |||
256 | kunmap_atomic(vaddr_new); | ||
257 | kunmap_atomic(vaddr_old); | ||
258 | 289 | ||
259 | ret = anon_vma_prepare(vma); | 290 | ret = anon_vma_prepare(vma); |
260 | if (ret) | 291 | if (ret) |
@@ -477,30 +508,18 @@ __copy_insn(struct address_space *mapping, struct file *filp, char *insn, | |||
477 | unsigned long nbytes, loff_t offset) | 508 | unsigned long nbytes, loff_t offset) |
478 | { | 509 | { |
479 | struct page *page; | 510 | struct page *page; |
480 | void *vaddr; | ||
481 | unsigned long off; | ||
482 | pgoff_t idx; | ||
483 | |||
484 | if (!filp) | ||
485 | return -EINVAL; | ||
486 | 511 | ||
487 | if (!mapping->a_ops->readpage) | 512 | if (!mapping->a_ops->readpage) |
488 | return -EIO; | 513 | return -EIO; |
489 | |||
490 | idx = offset >> PAGE_CACHE_SHIFT; | ||
491 | off = offset & ~PAGE_MASK; | ||
492 | |||
493 | /* | 514 | /* |
494 | * Ensure that the page that has the original instruction is | 515 | * Ensure that the page that has the original instruction is |
495 | * populated and in page-cache. | 516 | * populated and in page-cache. |
496 | */ | 517 | */ |
497 | page = read_mapping_page(mapping, idx, filp); | 518 | page = read_mapping_page(mapping, offset >> PAGE_CACHE_SHIFT, filp); |
498 | if (IS_ERR(page)) | 519 | if (IS_ERR(page)) |
499 | return PTR_ERR(page); | 520 | return PTR_ERR(page); |
500 | 521 | ||
501 | vaddr = kmap_atomic(page); | 522 | copy_from_page(page, offset, insn, nbytes); |
502 | memcpy(insn, vaddr + off, nbytes); | ||
503 | kunmap_atomic(vaddr); | ||
504 | page_cache_release(page); | 523 | page_cache_release(page); |
505 | 524 | ||
506 | return 0; | 525 | return 0; |
@@ -550,7 +569,7 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file, | |||
550 | goto out; | 569 | goto out; |
551 | 570 | ||
552 | ret = -ENOTSUPP; | 571 | ret = -ENOTSUPP; |
553 | if (is_swbp_insn((uprobe_opcode_t *)uprobe->arch.insn)) | 572 | if (is_trap_insn((uprobe_opcode_t *)uprobe->arch.insn)) |
554 | goto out; | 573 | goto out; |
555 | 574 | ||
556 | ret = arch_uprobe_analyze_insn(&uprobe->arch, mm, vaddr); | 575 | ret = arch_uprobe_analyze_insn(&uprobe->arch, mm, vaddr); |
@@ -758,7 +777,7 @@ register_for_each_vma(struct uprobe *uprobe, struct uprobe_consumer *new) | |||
758 | down_write(&mm->mmap_sem); | 777 | down_write(&mm->mmap_sem); |
759 | vma = find_vma(mm, info->vaddr); | 778 | vma = find_vma(mm, info->vaddr); |
760 | if (!vma || !valid_vma(vma, is_register) || | 779 | if (!vma || !valid_vma(vma, is_register) || |
761 | vma->vm_file->f_mapping->host != uprobe->inode) | 780 | file_inode(vma->vm_file) != uprobe->inode) |
762 | goto unlock; | 781 | goto unlock; |
763 | 782 | ||
764 | if (vma->vm_start > info->vaddr || | 783 | if (vma->vm_start > info->vaddr || |
@@ -828,6 +847,10 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer * | |||
828 | struct uprobe *uprobe; | 847 | struct uprobe *uprobe; |
829 | int ret; | 848 | int ret; |
830 | 849 | ||
850 | /* Uprobe must have at least one set consumer */ | ||
851 | if (!uc->handler && !uc->ret_handler) | ||
852 | return -EINVAL; | ||
853 | |||
831 | /* Racy, just to catch the obvious mistakes */ | 854 | /* Racy, just to catch the obvious mistakes */ |
832 | if (offset > i_size_read(inode)) | 855 | if (offset > i_size_read(inode)) |
833 | return -EINVAL; | 856 | return -EINVAL; |
@@ -917,7 +940,7 @@ static int unapply_uprobe(struct uprobe *uprobe, struct mm_struct *mm) | |||
917 | loff_t offset; | 940 | loff_t offset; |
918 | 941 | ||
919 | if (!valid_vma(vma, false) || | 942 | if (!valid_vma(vma, false) || |
920 | vma->vm_file->f_mapping->host != uprobe->inode) | 943 | file_inode(vma->vm_file) != uprobe->inode) |
921 | continue; | 944 | continue; |
922 | 945 | ||
923 | offset = (loff_t)vma->vm_pgoff << PAGE_SHIFT; | 946 | offset = (loff_t)vma->vm_pgoff << PAGE_SHIFT; |
@@ -1010,7 +1033,7 @@ int uprobe_mmap(struct vm_area_struct *vma) | |||
1010 | if (no_uprobe_events() || !valid_vma(vma, true)) | 1033 | if (no_uprobe_events() || !valid_vma(vma, true)) |
1011 | return 0; | 1034 | return 0; |
1012 | 1035 | ||
1013 | inode = vma->vm_file->f_mapping->host; | 1036 | inode = file_inode(vma->vm_file); |
1014 | if (!inode) | 1037 | if (!inode) |
1015 | return 0; | 1038 | return 0; |
1016 | 1039 | ||
@@ -1041,7 +1064,7 @@ vma_has_uprobes(struct vm_area_struct *vma, unsigned long start, unsigned long e | |||
1041 | struct inode *inode; | 1064 | struct inode *inode; |
1042 | struct rb_node *n; | 1065 | struct rb_node *n; |
1043 | 1066 | ||
1044 | inode = vma->vm_file->f_mapping->host; | 1067 | inode = file_inode(vma->vm_file); |
1045 | 1068 | ||
1046 | min = vaddr_to_offset(vma, start); | 1069 | min = vaddr_to_offset(vma, start); |
1047 | max = min + (end - start) - 1; | 1070 | max = min + (end - start) - 1; |
@@ -1114,6 +1137,7 @@ static struct xol_area *get_xol_area(void) | |||
1114 | { | 1137 | { |
1115 | struct mm_struct *mm = current->mm; | 1138 | struct mm_struct *mm = current->mm; |
1116 | struct xol_area *area; | 1139 | struct xol_area *area; |
1140 | uprobe_opcode_t insn = UPROBE_SWBP_INSN; | ||
1117 | 1141 | ||
1118 | area = mm->uprobes_state.xol_area; | 1142 | area = mm->uprobes_state.xol_area; |
1119 | if (area) | 1143 | if (area) |
@@ -1131,7 +1155,12 @@ static struct xol_area *get_xol_area(void) | |||
1131 | if (!area->page) | 1155 | if (!area->page) |
1132 | goto free_bitmap; | 1156 | goto free_bitmap; |
1133 | 1157 | ||
1158 | /* allocate first slot of task's xol_area for the return probes */ | ||
1159 | set_bit(0, area->bitmap); | ||
1160 | copy_to_page(area->page, 0, &insn, UPROBE_SWBP_INSN_SIZE); | ||
1161 | atomic_set(&area->slot_count, 1); | ||
1134 | init_waitqueue_head(&area->wq); | 1162 | init_waitqueue_head(&area->wq); |
1163 | |||
1135 | if (!xol_add_vma(area)) | 1164 | if (!xol_add_vma(area)) |
1136 | return area; | 1165 | return area; |
1137 | 1166 | ||
@@ -1216,9 +1245,7 @@ static unsigned long xol_take_insn_slot(struct xol_area *area) | |||
1216 | static unsigned long xol_get_insn_slot(struct uprobe *uprobe) | 1245 | static unsigned long xol_get_insn_slot(struct uprobe *uprobe) |
1217 | { | 1246 | { |
1218 | struct xol_area *area; | 1247 | struct xol_area *area; |
1219 | unsigned long offset; | ||
1220 | unsigned long xol_vaddr; | 1248 | unsigned long xol_vaddr; |
1221 | void *vaddr; | ||
1222 | 1249 | ||
1223 | area = get_xol_area(); | 1250 | area = get_xol_area(); |
1224 | if (!area) | 1251 | if (!area) |
@@ -1229,10 +1256,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe) | |||
1229 | return 0; | 1256 | return 0; |
1230 | 1257 | ||
1231 | /* Initialize the slot */ | 1258 | /* Initialize the slot */ |
1232 | offset = xol_vaddr & ~PAGE_MASK; | 1259 | copy_to_page(area->page, xol_vaddr, uprobe->arch.insn, MAX_UINSN_BYTES); |
1233 | vaddr = kmap_atomic(area->page); | ||
1234 | memcpy(vaddr + offset, uprobe->arch.insn, MAX_UINSN_BYTES); | ||
1235 | kunmap_atomic(vaddr); | ||
1236 | /* | 1260 | /* |
1237 | * We probably need flush_icache_user_range() but it needs vma. | 1261 | * We probably need flush_icache_user_range() but it needs vma. |
1238 | * This should work on supported architectures too. | 1262 | * This should work on supported architectures too. |
@@ -1298,6 +1322,7 @@ unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs) | |||
1298 | void uprobe_free_utask(struct task_struct *t) | 1322 | void uprobe_free_utask(struct task_struct *t) |
1299 | { | 1323 | { |
1300 | struct uprobe_task *utask = t->utask; | 1324 | struct uprobe_task *utask = t->utask; |
1325 | struct return_instance *ri, *tmp; | ||
1301 | 1326 | ||
1302 | if (!utask) | 1327 | if (!utask) |
1303 | return; | 1328 | return; |
@@ -1305,6 +1330,15 @@ void uprobe_free_utask(struct task_struct *t) | |||
1305 | if (utask->active_uprobe) | 1330 | if (utask->active_uprobe) |
1306 | put_uprobe(utask->active_uprobe); | 1331 | put_uprobe(utask->active_uprobe); |
1307 | 1332 | ||
1333 | ri = utask->return_instances; | ||
1334 | while (ri) { | ||
1335 | tmp = ri; | ||
1336 | ri = ri->next; | ||
1337 | |||
1338 | put_uprobe(tmp->uprobe); | ||
1339 | kfree(tmp); | ||
1340 | } | ||
1341 | |||
1308 | xol_free_insn_slot(t); | 1342 | xol_free_insn_slot(t); |
1309 | kfree(utask); | 1343 | kfree(utask); |
1310 | t->utask = NULL; | 1344 | t->utask = NULL; |
@@ -1333,6 +1367,93 @@ static struct uprobe_task *get_utask(void) | |||
1333 | return current->utask; | 1367 | return current->utask; |
1334 | } | 1368 | } |
1335 | 1369 | ||
1370 | /* | ||
1371 | * Current area->vaddr notion assume the trampoline address is always | ||
1372 | * equal area->vaddr. | ||
1373 | * | ||
1374 | * Returns -1 in case the xol_area is not allocated. | ||
1375 | */ | ||
1376 | static unsigned long get_trampoline_vaddr(void) | ||
1377 | { | ||
1378 | struct xol_area *area; | ||
1379 | unsigned long trampoline_vaddr = -1; | ||
1380 | |||
1381 | area = current->mm->uprobes_state.xol_area; | ||
1382 | smp_read_barrier_depends(); | ||
1383 | if (area) | ||
1384 | trampoline_vaddr = area->vaddr; | ||
1385 | |||
1386 | return trampoline_vaddr; | ||
1387 | } | ||
1388 | |||
1389 | static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs) | ||
1390 | { | ||
1391 | struct return_instance *ri; | ||
1392 | struct uprobe_task *utask; | ||
1393 | unsigned long orig_ret_vaddr, trampoline_vaddr; | ||
1394 | bool chained = false; | ||
1395 | |||
1396 | if (!get_xol_area()) | ||
1397 | return; | ||
1398 | |||
1399 | utask = get_utask(); | ||
1400 | if (!utask) | ||
1401 | return; | ||
1402 | |||
1403 | if (utask->depth >= MAX_URETPROBE_DEPTH) { | ||
1404 | printk_ratelimited(KERN_INFO "uprobe: omit uretprobe due to" | ||
1405 | " nestedness limit pid/tgid=%d/%d\n", | ||
1406 | current->pid, current->tgid); | ||
1407 | return; | ||
1408 | } | ||
1409 | |||
1410 | ri = kzalloc(sizeof(struct return_instance), GFP_KERNEL); | ||
1411 | if (!ri) | ||
1412 | goto fail; | ||
1413 | |||
1414 | trampoline_vaddr = get_trampoline_vaddr(); | ||
1415 | orig_ret_vaddr = arch_uretprobe_hijack_return_addr(trampoline_vaddr, regs); | ||
1416 | if (orig_ret_vaddr == -1) | ||
1417 | goto fail; | ||
1418 | |||
1419 | /* | ||
1420 | * We don't want to keep trampoline address in stack, rather keep the | ||
1421 | * original return address of first caller thru all the consequent | ||
1422 | * instances. This also makes breakpoint unwrapping easier. | ||
1423 | */ | ||
1424 | if (orig_ret_vaddr == trampoline_vaddr) { | ||
1425 | if (!utask->return_instances) { | ||
1426 | /* | ||
1427 | * This situation is not possible. Likely we have an | ||
1428 | * attack from user-space. | ||
1429 | */ | ||
1430 | pr_warn("uprobe: unable to set uretprobe pid/tgid=%d/%d\n", | ||
1431 | current->pid, current->tgid); | ||
1432 | goto fail; | ||
1433 | } | ||
1434 | |||
1435 | chained = true; | ||
1436 | orig_ret_vaddr = utask->return_instances->orig_ret_vaddr; | ||
1437 | } | ||
1438 | |||
1439 | atomic_inc(&uprobe->ref); | ||
1440 | ri->uprobe = uprobe; | ||
1441 | ri->func = instruction_pointer(regs); | ||
1442 | ri->orig_ret_vaddr = orig_ret_vaddr; | ||
1443 | ri->chained = chained; | ||
1444 | |||
1445 | utask->depth++; | ||
1446 | |||
1447 | /* add instance to the stack */ | ||
1448 | ri->next = utask->return_instances; | ||
1449 | utask->return_instances = ri; | ||
1450 | |||
1451 | return; | ||
1452 | |||
1453 | fail: | ||
1454 | kfree(ri); | ||
1455 | } | ||
1456 | |||
1336 | /* Prepare to single-step probed instruction out of line. */ | 1457 | /* Prepare to single-step probed instruction out of line. */ |
1337 | static int | 1458 | static int |
1338 | pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr) | 1459 | pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long bp_vaddr) |
@@ -1431,7 +1552,7 @@ static void mmf_recalc_uprobes(struct mm_struct *mm) | |||
1431 | clear_bit(MMF_HAS_UPROBES, &mm->flags); | 1552 | clear_bit(MMF_HAS_UPROBES, &mm->flags); |
1432 | } | 1553 | } |
1433 | 1554 | ||
1434 | static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr) | 1555 | static int is_trap_at_addr(struct mm_struct *mm, unsigned long vaddr) |
1435 | { | 1556 | { |
1436 | struct page *page; | 1557 | struct page *page; |
1437 | uprobe_opcode_t opcode; | 1558 | uprobe_opcode_t opcode; |
@@ -1449,10 +1570,11 @@ static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr) | |||
1449 | if (result < 0) | 1570 | if (result < 0) |
1450 | return result; | 1571 | return result; |
1451 | 1572 | ||
1452 | copy_opcode(page, vaddr, &opcode); | 1573 | copy_from_page(page, vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); |
1453 | put_page(page); | 1574 | put_page(page); |
1454 | out: | 1575 | out: |
1455 | return is_swbp_insn(&opcode); | 1576 | /* This needs to return true for any variant of the trap insn */ |
1577 | return is_trap_insn(&opcode); | ||
1456 | } | 1578 | } |
1457 | 1579 | ||
1458 | static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp) | 1580 | static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp) |
@@ -1465,14 +1587,14 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp) | |||
1465 | vma = find_vma(mm, bp_vaddr); | 1587 | vma = find_vma(mm, bp_vaddr); |
1466 | if (vma && vma->vm_start <= bp_vaddr) { | 1588 | if (vma && vma->vm_start <= bp_vaddr) { |
1467 | if (valid_vma(vma, false)) { | 1589 | if (valid_vma(vma, false)) { |
1468 | struct inode *inode = vma->vm_file->f_mapping->host; | 1590 | struct inode *inode = file_inode(vma->vm_file); |
1469 | loff_t offset = vaddr_to_offset(vma, bp_vaddr); | 1591 | loff_t offset = vaddr_to_offset(vma, bp_vaddr); |
1470 | 1592 | ||
1471 | uprobe = find_uprobe(inode, offset); | 1593 | uprobe = find_uprobe(inode, offset); |
1472 | } | 1594 | } |
1473 | 1595 | ||
1474 | if (!uprobe) | 1596 | if (!uprobe) |
1475 | *is_swbp = is_swbp_at_addr(mm, bp_vaddr); | 1597 | *is_swbp = is_trap_at_addr(mm, bp_vaddr); |
1476 | } else { | 1598 | } else { |
1477 | *is_swbp = -EFAULT; | 1599 | *is_swbp = -EFAULT; |
1478 | } | 1600 | } |
@@ -1488,16 +1610,27 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) | |||
1488 | { | 1610 | { |
1489 | struct uprobe_consumer *uc; | 1611 | struct uprobe_consumer *uc; |
1490 | int remove = UPROBE_HANDLER_REMOVE; | 1612 | int remove = UPROBE_HANDLER_REMOVE; |
1613 | bool need_prep = false; /* prepare return uprobe, when needed */ | ||
1491 | 1614 | ||
1492 | down_read(&uprobe->register_rwsem); | 1615 | down_read(&uprobe->register_rwsem); |
1493 | for (uc = uprobe->consumers; uc; uc = uc->next) { | 1616 | for (uc = uprobe->consumers; uc; uc = uc->next) { |
1494 | int rc = uc->handler(uc, regs); | 1617 | int rc = 0; |
1618 | |||
1619 | if (uc->handler) { | ||
1620 | rc = uc->handler(uc, regs); | ||
1621 | WARN(rc & ~UPROBE_HANDLER_MASK, | ||
1622 | "bad rc=0x%x from %pf()\n", rc, uc->handler); | ||
1623 | } | ||
1624 | |||
1625 | if (uc->ret_handler) | ||
1626 | need_prep = true; | ||
1495 | 1627 | ||
1496 | WARN(rc & ~UPROBE_HANDLER_MASK, | ||
1497 | "bad rc=0x%x from %pf()\n", rc, uc->handler); | ||
1498 | remove &= rc; | 1628 | remove &= rc; |
1499 | } | 1629 | } |
1500 | 1630 | ||
1631 | if (need_prep && !remove) | ||
1632 | prepare_uretprobe(uprobe, regs); /* put bp at return */ | ||
1633 | |||
1501 | if (remove && uprobe->consumers) { | 1634 | if (remove && uprobe->consumers) { |
1502 | WARN_ON(!uprobe_is_active(uprobe)); | 1635 | WARN_ON(!uprobe_is_active(uprobe)); |
1503 | unapply_uprobe(uprobe, current->mm); | 1636 | unapply_uprobe(uprobe, current->mm); |
@@ -1505,6 +1638,64 @@ static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) | |||
1505 | up_read(&uprobe->register_rwsem); | 1638 | up_read(&uprobe->register_rwsem); |
1506 | } | 1639 | } |
1507 | 1640 | ||
1641 | static void | ||
1642 | handle_uretprobe_chain(struct return_instance *ri, struct pt_regs *regs) | ||
1643 | { | ||
1644 | struct uprobe *uprobe = ri->uprobe; | ||
1645 | struct uprobe_consumer *uc; | ||
1646 | |||
1647 | down_read(&uprobe->register_rwsem); | ||
1648 | for (uc = uprobe->consumers; uc; uc = uc->next) { | ||
1649 | if (uc->ret_handler) | ||
1650 | uc->ret_handler(uc, ri->func, regs); | ||
1651 | } | ||
1652 | up_read(&uprobe->register_rwsem); | ||
1653 | } | ||
1654 | |||
1655 | static bool handle_trampoline(struct pt_regs *regs) | ||
1656 | { | ||
1657 | struct uprobe_task *utask; | ||
1658 | struct return_instance *ri, *tmp; | ||
1659 | bool chained; | ||
1660 | |||
1661 | utask = current->utask; | ||
1662 | if (!utask) | ||
1663 | return false; | ||
1664 | |||
1665 | ri = utask->return_instances; | ||
1666 | if (!ri) | ||
1667 | return false; | ||
1668 | |||
1669 | /* | ||
1670 | * TODO: we should throw out return_instance's invalidated by | ||
1671 | * longjmp(), currently we assume that the probed function always | ||
1672 | * returns. | ||
1673 | */ | ||
1674 | instruction_pointer_set(regs, ri->orig_ret_vaddr); | ||
1675 | |||
1676 | for (;;) { | ||
1677 | handle_uretprobe_chain(ri, regs); | ||
1678 | |||
1679 | chained = ri->chained; | ||
1680 | put_uprobe(ri->uprobe); | ||
1681 | |||
1682 | tmp = ri; | ||
1683 | ri = ri->next; | ||
1684 | kfree(tmp); | ||
1685 | |||
1686 | if (!chained) | ||
1687 | break; | ||
1688 | |||
1689 | utask->depth--; | ||
1690 | |||
1691 | BUG_ON(!ri); | ||
1692 | } | ||
1693 | |||
1694 | utask->return_instances = ri; | ||
1695 | |||
1696 | return true; | ||
1697 | } | ||
1698 | |||
1508 | /* | 1699 | /* |
1509 | * Run handler and ask thread to singlestep. | 1700 | * Run handler and ask thread to singlestep. |
1510 | * Ensure all non-fatal signals cannot interrupt thread while it singlesteps. | 1701 | * Ensure all non-fatal signals cannot interrupt thread while it singlesteps. |
@@ -1516,8 +1707,15 @@ static void handle_swbp(struct pt_regs *regs) | |||
1516 | int uninitialized_var(is_swbp); | 1707 | int uninitialized_var(is_swbp); |
1517 | 1708 | ||
1518 | bp_vaddr = uprobe_get_swbp_addr(regs); | 1709 | bp_vaddr = uprobe_get_swbp_addr(regs); |
1519 | uprobe = find_active_uprobe(bp_vaddr, &is_swbp); | 1710 | if (bp_vaddr == get_trampoline_vaddr()) { |
1711 | if (handle_trampoline(regs)) | ||
1712 | return; | ||
1713 | |||
1714 | pr_warn("uprobe: unable to handle uretprobe pid/tgid=%d/%d\n", | ||
1715 | current->pid, current->tgid); | ||
1716 | } | ||
1520 | 1717 | ||
1718 | uprobe = find_active_uprobe(bp_vaddr, &is_swbp); | ||
1521 | if (!uprobe) { | 1719 | if (!uprobe) { |
1522 | if (is_swbp > 0) { | 1720 | if (is_swbp > 0) { |
1523 | /* No matching uprobe; signal SIGTRAP. */ | 1721 | /* No matching uprobe; signal SIGTRAP. */ |
@@ -1616,7 +1814,11 @@ void uprobe_notify_resume(struct pt_regs *regs) | |||
1616 | */ | 1814 | */ |
1617 | int uprobe_pre_sstep_notifier(struct pt_regs *regs) | 1815 | int uprobe_pre_sstep_notifier(struct pt_regs *regs) |
1618 | { | 1816 | { |
1619 | if (!current->mm || !test_bit(MMF_HAS_UPROBES, ¤t->mm->flags)) | 1817 | if (!current->mm) |
1818 | return 0; | ||
1819 | |||
1820 | if (!test_bit(MMF_HAS_UPROBES, ¤t->mm->flags) && | ||
1821 | (!current->utask || !current->utask->return_instances)) | ||
1620 | return 0; | 1822 | return 0; |
1621 | 1823 | ||
1622 | set_thread_flag(TIF_UPROBE); | 1824 | set_thread_flag(TIF_UPROBE); |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 9e014582e763..711ca7d3e7f1 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -109,11 +109,6 @@ struct kretprobe_trace_entry_head { | |||
109 | unsigned long ret_ip; | 109 | unsigned long ret_ip; |
110 | }; | 110 | }; |
111 | 111 | ||
112 | struct uprobe_trace_entry_head { | ||
113 | struct trace_entry ent; | ||
114 | unsigned long ip; | ||
115 | }; | ||
116 | |||
117 | /* | 112 | /* |
118 | * trace_flag_type is an enumeration that holds different | 113 | * trace_flag_type is an enumeration that holds different |
119 | * states when a trace occurs. These are: | 114 | * states when a trace occurs. These are: |
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 8dad2a92dee9..32494fb0ee64 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c | |||
@@ -28,6 +28,18 @@ | |||
28 | 28 | ||
29 | #define UPROBE_EVENT_SYSTEM "uprobes" | 29 | #define UPROBE_EVENT_SYSTEM "uprobes" |
30 | 30 | ||
31 | struct uprobe_trace_entry_head { | ||
32 | struct trace_entry ent; | ||
33 | unsigned long vaddr[]; | ||
34 | }; | ||
35 | |||
36 | #define SIZEOF_TRACE_ENTRY(is_return) \ | ||
37 | (sizeof(struct uprobe_trace_entry_head) + \ | ||
38 | sizeof(unsigned long) * (is_return ? 2 : 1)) | ||
39 | |||
40 | #define DATAOF_TRACE_ENTRY(entry, is_return) \ | ||
41 | ((void*)(entry) + SIZEOF_TRACE_ENTRY(is_return)) | ||
42 | |||
31 | struct trace_uprobe_filter { | 43 | struct trace_uprobe_filter { |
32 | rwlock_t rwlock; | 44 | rwlock_t rwlock; |
33 | int nr_systemwide; | 45 | int nr_systemwide; |
@@ -64,6 +76,8 @@ static DEFINE_MUTEX(uprobe_lock); | |||
64 | static LIST_HEAD(uprobe_list); | 76 | static LIST_HEAD(uprobe_list); |
65 | 77 | ||
66 | static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs); | 78 | static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs); |
79 | static int uretprobe_dispatcher(struct uprobe_consumer *con, | ||
80 | unsigned long func, struct pt_regs *regs); | ||
67 | 81 | ||
68 | static inline void init_trace_uprobe_filter(struct trace_uprobe_filter *filter) | 82 | static inline void init_trace_uprobe_filter(struct trace_uprobe_filter *filter) |
69 | { | 83 | { |
@@ -77,11 +91,16 @@ static inline bool uprobe_filter_is_empty(struct trace_uprobe_filter *filter) | |||
77 | return !filter->nr_systemwide && list_empty(&filter->perf_events); | 91 | return !filter->nr_systemwide && list_empty(&filter->perf_events); |
78 | } | 92 | } |
79 | 93 | ||
94 | static inline bool is_ret_probe(struct trace_uprobe *tu) | ||
95 | { | ||
96 | return tu->consumer.ret_handler != NULL; | ||
97 | } | ||
98 | |||
80 | /* | 99 | /* |
81 | * Allocate new trace_uprobe and initialize it (including uprobes). | 100 | * Allocate new trace_uprobe and initialize it (including uprobes). |
82 | */ | 101 | */ |
83 | static struct trace_uprobe * | 102 | static struct trace_uprobe * |
84 | alloc_trace_uprobe(const char *group, const char *event, int nargs) | 103 | alloc_trace_uprobe(const char *group, const char *event, int nargs, bool is_ret) |
85 | { | 104 | { |
86 | struct trace_uprobe *tu; | 105 | struct trace_uprobe *tu; |
87 | 106 | ||
@@ -106,6 +125,8 @@ alloc_trace_uprobe(const char *group, const char *event, int nargs) | |||
106 | 125 | ||
107 | INIT_LIST_HEAD(&tu->list); | 126 | INIT_LIST_HEAD(&tu->list); |
108 | tu->consumer.handler = uprobe_dispatcher; | 127 | tu->consumer.handler = uprobe_dispatcher; |
128 | if (is_ret) | ||
129 | tu->consumer.ret_handler = uretprobe_dispatcher; | ||
109 | init_trace_uprobe_filter(&tu->filter); | 130 | init_trace_uprobe_filter(&tu->filter); |
110 | return tu; | 131 | return tu; |
111 | 132 | ||
@@ -180,7 +201,7 @@ end: | |||
180 | 201 | ||
181 | /* | 202 | /* |
182 | * Argument syntax: | 203 | * Argument syntax: |
183 | * - Add uprobe: p[:[GRP/]EVENT] PATH:SYMBOL[+offs] [FETCHARGS] | 204 | * - Add uprobe: p|r[:[GRP/]EVENT] PATH:SYMBOL [FETCHARGS] |
184 | * | 205 | * |
185 | * - Remove uprobe: -:[GRP/]EVENT | 206 | * - Remove uprobe: -:[GRP/]EVENT |
186 | */ | 207 | */ |
@@ -192,20 +213,23 @@ static int create_trace_uprobe(int argc, char **argv) | |||
192 | char buf[MAX_EVENT_NAME_LEN]; | 213 | char buf[MAX_EVENT_NAME_LEN]; |
193 | struct path path; | 214 | struct path path; |
194 | unsigned long offset; | 215 | unsigned long offset; |
195 | bool is_delete; | 216 | bool is_delete, is_return; |
196 | int i, ret; | 217 | int i, ret; |
197 | 218 | ||
198 | inode = NULL; | 219 | inode = NULL; |
199 | ret = 0; | 220 | ret = 0; |
200 | is_delete = false; | 221 | is_delete = false; |
222 | is_return = false; | ||
201 | event = NULL; | 223 | event = NULL; |
202 | group = NULL; | 224 | group = NULL; |
203 | 225 | ||
204 | /* argc must be >= 1 */ | 226 | /* argc must be >= 1 */ |
205 | if (argv[0][0] == '-') | 227 | if (argv[0][0] == '-') |
206 | is_delete = true; | 228 | is_delete = true; |
229 | else if (argv[0][0] == 'r') | ||
230 | is_return = true; | ||
207 | else if (argv[0][0] != 'p') { | 231 | else if (argv[0][0] != 'p') { |
208 | pr_info("Probe definition must be started with 'p' or '-'.\n"); | 232 | pr_info("Probe definition must be started with 'p', 'r' or '-'.\n"); |
209 | return -EINVAL; | 233 | return -EINVAL; |
210 | } | 234 | } |
211 | 235 | ||
@@ -303,7 +327,7 @@ static int create_trace_uprobe(int argc, char **argv) | |||
303 | kfree(tail); | 327 | kfree(tail); |
304 | } | 328 | } |
305 | 329 | ||
306 | tu = alloc_trace_uprobe(group, event, argc); | 330 | tu = alloc_trace_uprobe(group, event, argc, is_return); |
307 | if (IS_ERR(tu)) { | 331 | if (IS_ERR(tu)) { |
308 | pr_info("Failed to allocate trace_uprobe.(%d)\n", (int)PTR_ERR(tu)); | 332 | pr_info("Failed to allocate trace_uprobe.(%d)\n", (int)PTR_ERR(tu)); |
309 | ret = PTR_ERR(tu); | 333 | ret = PTR_ERR(tu); |
@@ -414,9 +438,10 @@ static void probes_seq_stop(struct seq_file *m, void *v) | |||
414 | static int probes_seq_show(struct seq_file *m, void *v) | 438 | static int probes_seq_show(struct seq_file *m, void *v) |
415 | { | 439 | { |
416 | struct trace_uprobe *tu = v; | 440 | struct trace_uprobe *tu = v; |
441 | char c = is_ret_probe(tu) ? 'r' : 'p'; | ||
417 | int i; | 442 | int i; |
418 | 443 | ||
419 | seq_printf(m, "p:%s/%s", tu->call.class->system, tu->call.name); | 444 | seq_printf(m, "%c:%s/%s", c, tu->call.class->system, tu->call.name); |
420 | seq_printf(m, " %s:0x%p", tu->filename, (void *)tu->offset); | 445 | seq_printf(m, " %s:0x%p", tu->filename, (void *)tu->offset); |
421 | 446 | ||
422 | for (i = 0; i < tu->nr_args; i++) | 447 | for (i = 0; i < tu->nr_args; i++) |
@@ -485,65 +510,81 @@ static const struct file_operations uprobe_profile_ops = { | |||
485 | .release = seq_release, | 510 | .release = seq_release, |
486 | }; | 511 | }; |
487 | 512 | ||
488 | /* uprobe handler */ | 513 | static void uprobe_trace_print(struct trace_uprobe *tu, |
489 | static int uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs) | 514 | unsigned long func, struct pt_regs *regs) |
490 | { | 515 | { |
491 | struct uprobe_trace_entry_head *entry; | 516 | struct uprobe_trace_entry_head *entry; |
492 | struct ring_buffer_event *event; | 517 | struct ring_buffer_event *event; |
493 | struct ring_buffer *buffer; | 518 | struct ring_buffer *buffer; |
494 | u8 *data; | 519 | void *data; |
495 | int size, i, pc; | 520 | int size, i; |
496 | unsigned long irq_flags; | ||
497 | struct ftrace_event_call *call = &tu->call; | 521 | struct ftrace_event_call *call = &tu->call; |
498 | 522 | ||
499 | local_save_flags(irq_flags); | 523 | size = SIZEOF_TRACE_ENTRY(is_ret_probe(tu)); |
500 | pc = preempt_count(); | ||
501 | |||
502 | size = sizeof(*entry) + tu->size; | ||
503 | |||
504 | event = trace_current_buffer_lock_reserve(&buffer, call->event.type, | 524 | event = trace_current_buffer_lock_reserve(&buffer, call->event.type, |
505 | size, irq_flags, pc); | 525 | size + tu->size, 0, 0); |
506 | if (!event) | 526 | if (!event) |
507 | return 0; | 527 | return; |
508 | 528 | ||
509 | entry = ring_buffer_event_data(event); | 529 | entry = ring_buffer_event_data(event); |
510 | entry->ip = instruction_pointer(task_pt_regs(current)); | 530 | if (is_ret_probe(tu)) { |
511 | data = (u8 *)&entry[1]; | 531 | entry->vaddr[0] = func; |
532 | entry->vaddr[1] = instruction_pointer(regs); | ||
533 | data = DATAOF_TRACE_ENTRY(entry, true); | ||
534 | } else { | ||
535 | entry->vaddr[0] = instruction_pointer(regs); | ||
536 | data = DATAOF_TRACE_ENTRY(entry, false); | ||
537 | } | ||
538 | |||
512 | for (i = 0; i < tu->nr_args; i++) | 539 | for (i = 0; i < tu->nr_args; i++) |
513 | call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); | 540 | call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); |
514 | 541 | ||
515 | if (!filter_current_check_discard(buffer, call, entry, event)) | 542 | if (!filter_current_check_discard(buffer, call, entry, event)) |
516 | trace_buffer_unlock_commit(buffer, event, irq_flags, pc); | 543 | trace_buffer_unlock_commit(buffer, event, 0, 0); |
544 | } | ||
517 | 545 | ||
546 | /* uprobe handler */ | ||
547 | static int uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs) | ||
548 | { | ||
549 | if (!is_ret_probe(tu)) | ||
550 | uprobe_trace_print(tu, 0, regs); | ||
518 | return 0; | 551 | return 0; |
519 | } | 552 | } |
520 | 553 | ||
554 | static void uretprobe_trace_func(struct trace_uprobe *tu, unsigned long func, | ||
555 | struct pt_regs *regs) | ||
556 | { | ||
557 | uprobe_trace_print(tu, func, regs); | ||
558 | } | ||
559 | |||
521 | /* Event entry printers */ | 560 | /* Event entry printers */ |
522 | static enum print_line_t | 561 | static enum print_line_t |
523 | print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *event) | 562 | print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *event) |
524 | { | 563 | { |
525 | struct uprobe_trace_entry_head *field; | 564 | struct uprobe_trace_entry_head *entry; |
526 | struct trace_seq *s = &iter->seq; | 565 | struct trace_seq *s = &iter->seq; |
527 | struct trace_uprobe *tu; | 566 | struct trace_uprobe *tu; |
528 | u8 *data; | 567 | u8 *data; |
529 | int i; | 568 | int i; |
530 | 569 | ||
531 | field = (struct uprobe_trace_entry_head *)iter->ent; | 570 | entry = (struct uprobe_trace_entry_head *)iter->ent; |
532 | tu = container_of(event, struct trace_uprobe, call.event); | 571 | tu = container_of(event, struct trace_uprobe, call.event); |
533 | 572 | ||
534 | if (!trace_seq_printf(s, "%s: (", tu->call.name)) | 573 | if (is_ret_probe(tu)) { |
535 | goto partial; | 574 | if (!trace_seq_printf(s, "%s: (0x%lx <- 0x%lx)", tu->call.name, |
536 | 575 | entry->vaddr[1], entry->vaddr[0])) | |
537 | if (!seq_print_ip_sym(s, field->ip, flags | TRACE_ITER_SYM_OFFSET)) | 576 | goto partial; |
538 | goto partial; | 577 | data = DATAOF_TRACE_ENTRY(entry, true); |
539 | 578 | } else { | |
540 | if (!trace_seq_puts(s, ")")) | 579 | if (!trace_seq_printf(s, "%s: (0x%lx)", tu->call.name, |
541 | goto partial; | 580 | entry->vaddr[0])) |
581 | goto partial; | ||
582 | data = DATAOF_TRACE_ENTRY(entry, false); | ||
583 | } | ||
542 | 584 | ||
543 | data = (u8 *)&field[1]; | ||
544 | for (i = 0; i < tu->nr_args; i++) { | 585 | for (i = 0; i < tu->nr_args; i++) { |
545 | if (!tu->args[i].type->print(s, tu->args[i].name, | 586 | if (!tu->args[i].type->print(s, tu->args[i].name, |
546 | data + tu->args[i].offset, field)) | 587 | data + tu->args[i].offset, entry)) |
547 | goto partial; | 588 | goto partial; |
548 | } | 589 | } |
549 | 590 | ||
@@ -595,16 +636,23 @@ static void probe_event_disable(struct trace_uprobe *tu, int flag) | |||
595 | 636 | ||
596 | static int uprobe_event_define_fields(struct ftrace_event_call *event_call) | 637 | static int uprobe_event_define_fields(struct ftrace_event_call *event_call) |
597 | { | 638 | { |
598 | int ret, i; | 639 | int ret, i, size; |
599 | struct uprobe_trace_entry_head field; | 640 | struct uprobe_trace_entry_head field; |
600 | struct trace_uprobe *tu = (struct trace_uprobe *)event_call->data; | 641 | struct trace_uprobe *tu = event_call->data; |
601 | 642 | ||
602 | DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0); | 643 | if (is_ret_probe(tu)) { |
644 | DEFINE_FIELD(unsigned long, vaddr[0], FIELD_STRING_FUNC, 0); | ||
645 | DEFINE_FIELD(unsigned long, vaddr[1], FIELD_STRING_RETIP, 0); | ||
646 | size = SIZEOF_TRACE_ENTRY(true); | ||
647 | } else { | ||
648 | DEFINE_FIELD(unsigned long, vaddr[0], FIELD_STRING_IP, 0); | ||
649 | size = SIZEOF_TRACE_ENTRY(false); | ||
650 | } | ||
603 | /* Set argument names as fields */ | 651 | /* Set argument names as fields */ |
604 | for (i = 0; i < tu->nr_args; i++) { | 652 | for (i = 0; i < tu->nr_args; i++) { |
605 | ret = trace_define_field(event_call, tu->args[i].type->fmttype, | 653 | ret = trace_define_field(event_call, tu->args[i].type->fmttype, |
606 | tu->args[i].name, | 654 | tu->args[i].name, |
607 | sizeof(field) + tu->args[i].offset, | 655 | size + tu->args[i].offset, |
608 | tu->args[i].type->size, | 656 | tu->args[i].type->size, |
609 | tu->args[i].type->is_signed, | 657 | tu->args[i].type->is_signed, |
610 | FILTER_OTHER); | 658 | FILTER_OTHER); |
@@ -622,8 +670,13 @@ static int __set_print_fmt(struct trace_uprobe *tu, char *buf, int len) | |||
622 | int i; | 670 | int i; |
623 | int pos = 0; | 671 | int pos = 0; |
624 | 672 | ||
625 | fmt = "(%lx)"; | 673 | if (is_ret_probe(tu)) { |
626 | arg = "REC->" FIELD_STRING_IP; | 674 | fmt = "(%lx <- %lx)"; |
675 | arg = "REC->" FIELD_STRING_FUNC ", REC->" FIELD_STRING_RETIP; | ||
676 | } else { | ||
677 | fmt = "(%lx)"; | ||
678 | arg = "REC->" FIELD_STRING_IP; | ||
679 | } | ||
627 | 680 | ||
628 | /* When len=0, we just calculate the needed length */ | 681 | /* When len=0, we just calculate the needed length */ |
629 | 682 | ||
@@ -752,49 +805,68 @@ static bool uprobe_perf_filter(struct uprobe_consumer *uc, | |||
752 | return ret; | 805 | return ret; |
753 | } | 806 | } |
754 | 807 | ||
755 | /* uprobe profile handler */ | 808 | static void uprobe_perf_print(struct trace_uprobe *tu, |
756 | static int uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) | 809 | unsigned long func, struct pt_regs *regs) |
757 | { | 810 | { |
758 | struct ftrace_event_call *call = &tu->call; | 811 | struct ftrace_event_call *call = &tu->call; |
759 | struct uprobe_trace_entry_head *entry; | 812 | struct uprobe_trace_entry_head *entry; |
760 | struct hlist_head *head; | 813 | struct hlist_head *head; |
761 | u8 *data; | 814 | void *data; |
762 | int size, __size, i; | 815 | int size, rctx, i; |
763 | int rctx; | ||
764 | 816 | ||
765 | if (!uprobe_perf_filter(&tu->consumer, 0, current->mm)) | 817 | size = SIZEOF_TRACE_ENTRY(is_ret_probe(tu)); |
766 | return UPROBE_HANDLER_REMOVE; | 818 | size = ALIGN(size + tu->size + sizeof(u32), sizeof(u64)) - sizeof(u32); |
767 | |||
768 | __size = sizeof(*entry) + tu->size; | ||
769 | size = ALIGN(__size + sizeof(u32), sizeof(u64)); | ||
770 | size -= sizeof(u32); | ||
771 | if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough")) | 819 | if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough")) |
772 | return 0; | 820 | return; |
773 | 821 | ||
774 | preempt_disable(); | 822 | preempt_disable(); |
823 | head = this_cpu_ptr(call->perf_events); | ||
824 | if (hlist_empty(head)) | ||
825 | goto out; | ||
775 | 826 | ||
776 | entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx); | 827 | entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx); |
777 | if (!entry) | 828 | if (!entry) |
778 | goto out; | 829 | goto out; |
779 | 830 | ||
780 | entry->ip = instruction_pointer(task_pt_regs(current)); | 831 | if (is_ret_probe(tu)) { |
781 | data = (u8 *)&entry[1]; | 832 | entry->vaddr[0] = func; |
833 | entry->vaddr[1] = instruction_pointer(regs); | ||
834 | data = DATAOF_TRACE_ENTRY(entry, true); | ||
835 | } else { | ||
836 | entry->vaddr[0] = instruction_pointer(regs); | ||
837 | data = DATAOF_TRACE_ENTRY(entry, false); | ||
838 | } | ||
839 | |||
782 | for (i = 0; i < tu->nr_args; i++) | 840 | for (i = 0; i < tu->nr_args; i++) |
783 | call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); | 841 | call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset); |
784 | 842 | ||
785 | head = this_cpu_ptr(call->perf_events); | 843 | perf_trace_buf_submit(entry, size, rctx, 0, 1, regs, head, NULL); |
786 | perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head, NULL); | ||
787 | |||
788 | out: | 844 | out: |
789 | preempt_enable(); | 845 | preempt_enable(); |
846 | } | ||
847 | |||
848 | /* uprobe profile handler */ | ||
849 | static int uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs) | ||
850 | { | ||
851 | if (!uprobe_perf_filter(&tu->consumer, 0, current->mm)) | ||
852 | return UPROBE_HANDLER_REMOVE; | ||
853 | |||
854 | if (!is_ret_probe(tu)) | ||
855 | uprobe_perf_print(tu, 0, regs); | ||
790 | return 0; | 856 | return 0; |
791 | } | 857 | } |
858 | |||
859 | static void uretprobe_perf_func(struct trace_uprobe *tu, unsigned long func, | ||
860 | struct pt_regs *regs) | ||
861 | { | ||
862 | uprobe_perf_print(tu, func, regs); | ||
863 | } | ||
792 | #endif /* CONFIG_PERF_EVENTS */ | 864 | #endif /* CONFIG_PERF_EVENTS */ |
793 | 865 | ||
794 | static | 866 | static |
795 | int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, void *data) | 867 | int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, void *data) |
796 | { | 868 | { |
797 | struct trace_uprobe *tu = (struct trace_uprobe *)event->data; | 869 | struct trace_uprobe *tu = event->data; |
798 | 870 | ||
799 | switch (type) { | 871 | switch (type) { |
800 | case TRACE_REG_REGISTER: | 872 | case TRACE_REG_REGISTER: |
@@ -843,6 +915,23 @@ static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs) | |||
843 | return ret; | 915 | return ret; |
844 | } | 916 | } |
845 | 917 | ||
918 | static int uretprobe_dispatcher(struct uprobe_consumer *con, | ||
919 | unsigned long func, struct pt_regs *regs) | ||
920 | { | ||
921 | struct trace_uprobe *tu; | ||
922 | |||
923 | tu = container_of(con, struct trace_uprobe, consumer); | ||
924 | |||
925 | if (tu->flags & TP_FLAG_TRACE) | ||
926 | uretprobe_trace_func(tu, func, regs); | ||
927 | |||
928 | #ifdef CONFIG_PERF_EVENTS | ||
929 | if (tu->flags & TP_FLAG_PROFILE) | ||
930 | uretprobe_perf_func(tu, func, regs); | ||
931 | #endif | ||
932 | return 0; | ||
933 | } | ||
934 | |||
846 | static struct trace_event_functions uprobe_funcs = { | 935 | static struct trace_event_functions uprobe_funcs = { |
847 | .trace = print_uprobe_event | 936 | .trace = print_uprobe_event |
848 | }; | 937 | }; |
diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 4a944676358e..05039e348f07 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c | |||
@@ -517,6 +517,11 @@ int proc_dowatchdog(struct ctl_table *table, int write, | |||
517 | return ret; | 517 | return ret; |
518 | 518 | ||
519 | set_sample_period(); | 519 | set_sample_period(); |
520 | /* | ||
521 | * Watchdog threads shouldn't be enabled if they are | ||
522 | * disabled. The 'watchdog_disabled' variable check in | ||
523 | * watchdog_*_all_cpus() function takes care of this. | ||
524 | */ | ||
520 | if (watchdog_enabled && watchdog_thresh) | 525 | if (watchdog_enabled && watchdog_thresh) |
521 | watchdog_enable_all_cpus(); | 526 | watchdog_enable_all_cpus(); |
522 | else | 527 | else |
diff --git a/tools/Makefile b/tools/Makefile index fa36565b209d..6aaeb6cd867d 100644 --- a/tools/Makefile +++ b/tools/Makefile | |||
@@ -34,7 +34,13 @@ help: | |||
34 | cpupower: FORCE | 34 | cpupower: FORCE |
35 | $(call descend,power/$@) | 35 | $(call descend,power/$@) |
36 | 36 | ||
37 | cgroup firewire lguest perf usb virtio vm: FORCE | 37 | cgroup firewire guest usb virtio vm: FORCE |
38 | $(call descend,$@) | ||
39 | |||
40 | liblk: FORCE | ||
41 | $(call descend,lib/lk) | ||
42 | |||
43 | perf: liblk FORCE | ||
38 | $(call descend,$@) | 44 | $(call descend,$@) |
39 | 45 | ||
40 | selftests: FORCE | 46 | selftests: FORCE |
@@ -62,7 +68,13 @@ install: cgroup_install cpupower_install firewire_install lguest_install \ | |||
62 | cpupower_clean: | 68 | cpupower_clean: |
63 | $(call descend,power/cpupower,clean) | 69 | $(call descend,power/cpupower,clean) |
64 | 70 | ||
65 | cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean: | 71 | cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean: |
72 | $(call descend,$(@:_clean=),clean) | ||
73 | |||
74 | liblk_clean: | ||
75 | $(call descend,lib/lk,clean) | ||
76 | |||
77 | perf_clean: liblk_clean | ||
66 | $(call descend,$(@:_clean=),clean) | 78 | $(call descend,$(@:_clean=),clean) |
67 | 79 | ||
68 | selftests_clean: | 80 | selftests_clean: |
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile new file mode 100644 index 000000000000..926cbf3efc7f --- /dev/null +++ b/tools/lib/lk/Makefile | |||
@@ -0,0 +1,35 @@ | |||
1 | include ../../scripts/Makefile.include | ||
2 | |||
3 | # guard against environment variables | ||
4 | LIB_H= | ||
5 | LIB_OBJS= | ||
6 | |||
7 | LIB_H += debugfs.h | ||
8 | |||
9 | LIB_OBJS += $(OUTPUT)debugfs.o | ||
10 | |||
11 | LIBFILE = liblk.a | ||
12 | |||
13 | CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC | ||
14 | EXTLIBS = -lpthread -lrt -lelf -lm | ||
15 | ALL_CFLAGS = $(CFLAGS) $(BASIC_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 | ||
16 | ALL_LDFLAGS = $(LDFLAGS) | ||
17 | |||
18 | RM = rm -f | ||
19 | |||
20 | $(LIBFILE): $(LIB_OBJS) | ||
21 | $(QUIET_AR)$(RM) $@ && $(AR) rcs $(OUTPUT)$@ $(LIB_OBJS) | ||
22 | |||
23 | $(LIB_OBJS): $(LIB_H) | ||
24 | |||
25 | $(OUTPUT)%.o: %.c | ||
26 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | ||
27 | $(OUTPUT)%.s: %.c | ||
28 | $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< | ||
29 | $(OUTPUT)%.o: %.S | ||
30 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | ||
31 | |||
32 | clean: | ||
33 | $(RM) $(LIB_OBJS) $(LIBFILE) | ||
34 | |||
35 | .PHONY: clean | ||
diff --git a/tools/perf/util/debugfs.c b/tools/lib/lk/debugfs.c index dd8b19319c03..099e7cd022e4 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/lib/lk/debugfs.c | |||
@@ -1,36 +1,39 @@ | |||
1 | #include "util.h" | 1 | #include <errno.h> |
2 | #include "debugfs.h" | 2 | #include <stdio.h> |
3 | #include "cache.h" | 3 | #include <stdlib.h> |
4 | 4 | #include <string.h> | |
5 | #include <linux/kernel.h> | 5 | #include <stdbool.h> |
6 | #include <sys/vfs.h> | ||
6 | #include <sys/mount.h> | 7 | #include <sys/mount.h> |
8 | #include <linux/magic.h> | ||
9 | #include <linux/kernel.h> | ||
10 | |||
11 | #include "debugfs.h" | ||
7 | 12 | ||
8 | static int debugfs_premounted; | ||
9 | char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug"; | 13 | char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug"; |
10 | char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; | ||
11 | 14 | ||
12 | static const char *debugfs_known_mountpoints[] = { | 15 | static const char * const debugfs_known_mountpoints[] = { |
13 | "/sys/kernel/debug/", | 16 | "/sys/kernel/debug/", |
14 | "/debug/", | 17 | "/debug/", |
15 | 0, | 18 | 0, |
16 | }; | 19 | }; |
17 | 20 | ||
18 | static int debugfs_found; | 21 | static bool debugfs_found; |
19 | 22 | ||
20 | /* find the path to the mounted debugfs */ | 23 | /* find the path to the mounted debugfs */ |
21 | const char *debugfs_find_mountpoint(void) | 24 | const char *debugfs_find_mountpoint(void) |
22 | { | 25 | { |
23 | const char **ptr; | 26 | const char * const *ptr; |
24 | char type[100]; | 27 | char type[100]; |
25 | FILE *fp; | 28 | FILE *fp; |
26 | 29 | ||
27 | if (debugfs_found) | 30 | if (debugfs_found) |
28 | return (const char *) debugfs_mountpoint; | 31 | return (const char *)debugfs_mountpoint; |
29 | 32 | ||
30 | ptr = debugfs_known_mountpoints; | 33 | ptr = debugfs_known_mountpoints; |
31 | while (*ptr) { | 34 | while (*ptr) { |
32 | if (debugfs_valid_mountpoint(*ptr) == 0) { | 35 | if (debugfs_valid_mountpoint(*ptr) == 0) { |
33 | debugfs_found = 1; | 36 | debugfs_found = true; |
34 | strcpy(debugfs_mountpoint, *ptr); | 37 | strcpy(debugfs_mountpoint, *ptr); |
35 | return debugfs_mountpoint; | 38 | return debugfs_mountpoint; |
36 | } | 39 | } |
@@ -52,7 +55,7 @@ const char *debugfs_find_mountpoint(void) | |||
52 | if (strcmp(type, "debugfs") != 0) | 55 | if (strcmp(type, "debugfs") != 0) |
53 | return NULL; | 56 | return NULL; |
54 | 57 | ||
55 | debugfs_found = 1; | 58 | debugfs_found = true; |
56 | 59 | ||
57 | return debugfs_mountpoint; | 60 | return debugfs_mountpoint; |
58 | } | 61 | } |
@@ -71,21 +74,12 @@ int debugfs_valid_mountpoint(const char *debugfs) | |||
71 | return 0; | 74 | return 0; |
72 | } | 75 | } |
73 | 76 | ||
74 | static void debugfs_set_tracing_events_path(const char *mountpoint) | ||
75 | { | ||
76 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | ||
77 | mountpoint, "tracing/events"); | ||
78 | } | ||
79 | |||
80 | /* mount the debugfs somewhere if it's not mounted */ | 77 | /* mount the debugfs somewhere if it's not mounted */ |
81 | |||
82 | char *debugfs_mount(const char *mountpoint) | 78 | char *debugfs_mount(const char *mountpoint) |
83 | { | 79 | { |
84 | /* see if it's already mounted */ | 80 | /* see if it's already mounted */ |
85 | if (debugfs_find_mountpoint()) { | 81 | if (debugfs_find_mountpoint()) |
86 | debugfs_premounted = 1; | ||
87 | goto out; | 82 | goto out; |
88 | } | ||
89 | 83 | ||
90 | /* if not mounted and no argument */ | 84 | /* if not mounted and no argument */ |
91 | if (mountpoint == NULL) { | 85 | if (mountpoint == NULL) { |
@@ -100,15 +94,8 @@ char *debugfs_mount(const char *mountpoint) | |||
100 | return NULL; | 94 | return NULL; |
101 | 95 | ||
102 | /* save the mountpoint */ | 96 | /* save the mountpoint */ |
103 | debugfs_found = 1; | 97 | debugfs_found = true; |
104 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); | 98 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); |
105 | out: | 99 | out: |
106 | debugfs_set_tracing_events_path(debugfs_mountpoint); | ||
107 | return debugfs_mountpoint; | 100 | return debugfs_mountpoint; |
108 | } | 101 | } |
109 | |||
110 | void debugfs_set_path(const char *mountpoint) | ||
111 | { | ||
112 | snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); | ||
113 | debugfs_set_tracing_events_path(mountpoint); | ||
114 | } | ||
diff --git a/tools/lib/lk/debugfs.h b/tools/lib/lk/debugfs.h new file mode 100644 index 000000000000..935c59bdb442 --- /dev/null +++ b/tools/lib/lk/debugfs.h | |||
@@ -0,0 +1,29 @@ | |||
1 | #ifndef __LK_DEBUGFS_H__ | ||
2 | #define __LK_DEBUGFS_H__ | ||
3 | |||
4 | #define _STR(x) #x | ||
5 | #define STR(x) _STR(x) | ||
6 | |||
7 | /* | ||
8 | * On most systems <limits.h> would have given us this, but not on some systems | ||
9 | * (e.g. GNU/Hurd). | ||
10 | */ | ||
11 | #ifndef PATH_MAX | ||
12 | #define PATH_MAX 4096 | ||
13 | #endif | ||
14 | |||
15 | #ifndef DEBUGFS_MAGIC | ||
16 | #define DEBUGFS_MAGIC 0x64626720 | ||
17 | #endif | ||
18 | |||
19 | #ifndef PERF_DEBUGFS_ENVIRONMENT | ||
20 | #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" | ||
21 | #endif | ||
22 | |||
23 | const char *debugfs_find_mountpoint(void); | ||
24 | int debugfs_valid_mountpoint(const char *debugfs); | ||
25 | char *debugfs_mount(const char *mountpoint); | ||
26 | |||
27 | extern char debugfs_mountpoint[]; | ||
28 | |||
29 | #endif /* __LK_DEBUGFS_H__ */ | ||
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index 5ad07ef417f0..e9cd39a92dc2 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
@@ -93,6 +93,9 @@ OPTIONS | |||
93 | --skip-missing:: | 93 | --skip-missing:: |
94 | Skip symbols that cannot be annotated. | 94 | Skip symbols that cannot be annotated. |
95 | 95 | ||
96 | --group:: | ||
97 | Show event group information together | ||
98 | |||
96 | SEE ALSO | 99 | SEE ALSO |
97 | -------- | 100 | -------- |
98 | linkperf:perf-record[1], linkperf:perf-report[1] | 101 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt new file mode 100644 index 000000000000..888d51137fbe --- /dev/null +++ b/tools/perf/Documentation/perf-mem.txt | |||
@@ -0,0 +1,48 @@ | |||
1 | perf-mem(1) | ||
2 | =========== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-mem - Profile memory accesses | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf mem' [<options>] (record [<command>] | report) | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | "perf mem -t <TYPE> record" runs a command and gathers memory operation data | ||
16 | from it, into perf.data. Perf record options are accepted and are passed through. | ||
17 | |||
18 | "perf mem -t <TYPE> report" displays the result. It invokes perf report with the | ||
19 | right set of options to display a memory access profile. | ||
20 | |||
21 | OPTIONS | ||
22 | ------- | ||
23 | <command>...:: | ||
24 | Any command you can specify in a shell. | ||
25 | |||
26 | -t:: | ||
27 | --type=:: | ||
28 | Select the memory operation type: load or store (default: load) | ||
29 | |||
30 | -D:: | ||
31 | --dump-raw-samples=:: | ||
32 | Dump the raw decoded samples on the screen in a format that is easy to parse with | ||
33 | one sample per line. | ||
34 | |||
35 | -x:: | ||
36 | --field-separator:: | ||
37 | Specify the field separator used when dump raw samples (-D option). By default, | ||
38 | The separator is the space character. | ||
39 | |||
40 | -C:: | ||
41 | --cpu-list:: | ||
42 | Restrict dump of raw samples to those provided via this option. Note that the same | ||
43 | option can be passed in record mode. It will be interpreted the same way as perf | ||
44 | record. | ||
45 | |||
46 | SEE ALSO | ||
47 | -------- | ||
48 | linkperf:perf-record[1], linkperf:perf-report[1] | ||
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 938e8904f64d..d4da111ef53d 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -182,6 +182,12 @@ is enabled for all the sampling events. The sampled branch type is the same for | |||
182 | The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k | 182 | The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k |
183 | Note that this feature may not be available on all processors. | 183 | Note that this feature may not be available on all processors. |
184 | 184 | ||
185 | -W:: | ||
186 | --weight:: | ||
187 | Enable weightened sampling. An additional weight is recorded per sample and can be | ||
188 | displayed with the weight and local_weight sort keys. This currently works for TSX | ||
189 | abort events and some memory events in precise mode on modern Intel CPUs. | ||
190 | |||
185 | SEE ALSO | 191 | SEE ALSO |
186 | -------- | 192 | -------- |
187 | linkperf:perf-stat[1], linkperf:perf-list[1] | 193 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 02284a0067f0..7d5f4f38aa52 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -59,7 +59,7 @@ OPTIONS | |||
59 | --sort=:: | 59 | --sort=:: |
60 | Sort histogram entries by given key(s) - multiple keys can be specified | 60 | Sort histogram entries by given key(s) - multiple keys can be specified |
61 | in CSV format. Following sort keys are available: | 61 | in CSV format. Following sort keys are available: |
62 | pid, comm, dso, symbol, parent, cpu, srcline. | 62 | pid, comm, dso, symbol, parent, cpu, srcline, weight, local_weight. |
63 | 63 | ||
64 | Each key has following meaning: | 64 | Each key has following meaning: |
65 | 65 | ||
@@ -206,6 +206,10 @@ OPTIONS | |||
206 | --group:: | 206 | --group:: |
207 | Show event group information together. | 207 | Show event group information together. |
208 | 208 | ||
209 | --demangle:: | ||
210 | Demangle symbol names to human readable form. It's enabled by default, | ||
211 | disable with --no-demangle. | ||
212 | |||
209 | SEE ALSO | 213 | SEE ALSO |
210 | -------- | 214 | -------- |
211 | linkperf:perf-stat[1], linkperf:perf-annotate[1] | 215 | linkperf:perf-stat[1], linkperf:perf-annotate[1] |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index faf4f4feebcc..2fe87fb558f0 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -52,7 +52,7 @@ OPTIONS | |||
52 | 52 | ||
53 | -r:: | 53 | -r:: |
54 | --repeat=<n>:: | 54 | --repeat=<n>:: |
55 | repeat command and print average + stddev (max: 100) | 55 | repeat command and print average + stddev (max: 100). 0 means forever. |
56 | 56 | ||
57 | -B:: | 57 | -B:: |
58 | --big-num:: | 58 | --big-num:: |
@@ -119,13 +119,19 @@ perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- m | |||
119 | Print count deltas every N milliseconds (minimum: 100ms) | 119 | Print count deltas every N milliseconds (minimum: 100ms) |
120 | example: perf stat -I 1000 -e cycles -a sleep 5 | 120 | example: perf stat -I 1000 -e cycles -a sleep 5 |
121 | 121 | ||
122 | --aggr-socket:: | 122 | --per-socket:: |
123 | Aggregate counts per processor socket for system-wide mode measurements. This | 123 | Aggregate counts per processor socket for system-wide mode measurements. This |
124 | is a useful mode to detect imbalance between sockets. To enable this mode, | 124 | is a useful mode to detect imbalance between sockets. To enable this mode, |
125 | use --aggr-socket in addition to -a. (system-wide). The output includes the | 125 | use --per-socket in addition to -a. (system-wide). The output includes the |
126 | socket number and the number of online processors on that socket. This is | 126 | socket number and the number of online processors on that socket. This is |
127 | useful to gauge the amount of aggregation. | 127 | useful to gauge the amount of aggregation. |
128 | 128 | ||
129 | --per-core:: | ||
130 | Aggregate counts per physical processor for system-wide mode measurements. This | ||
131 | is a useful mode to detect imbalance between physical cores. To enable this mode, | ||
132 | use --per-core in addition to -a. (system-wide). The output includes the | ||
133 | core number and the number of online logical processors on that physical processor. | ||
134 | |||
129 | EXAMPLES | 135 | EXAMPLES |
130 | -------- | 136 | -------- |
131 | 137 | ||
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index a414bc95fd52..9f1a2fe54757 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -112,7 +112,7 @@ Default is to monitor all CPUS. | |||
112 | 112 | ||
113 | -s:: | 113 | -s:: |
114 | --sort:: | 114 | --sort:: |
115 | Sort by key(s): pid, comm, dso, symbol, parent, srcline. | 115 | Sort by key(s): pid, comm, dso, symbol, parent, srcline, weight, local_weight. |
116 | 116 | ||
117 | -n:: | 117 | -n:: |
118 | --show-nr-samples:: | 118 | --show-nr-samples:: |
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 39d41068484f..025de796067c 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
@@ -1,6 +1,7 @@ | |||
1 | tools/perf | 1 | tools/perf |
2 | tools/scripts | 2 | tools/scripts |
3 | tools/lib/traceevent | 3 | tools/lib/traceevent |
4 | tools/lib/lk | ||
4 | include/linux/const.h | 5 | include/linux/const.h |
5 | include/linux/perf_event.h | 6 | include/linux/perf_event.h |
6 | include/linux/rbtree.h | 7 | include/linux/rbtree.h |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index bb74c79cd16e..b0f164b133d9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -35,7 +35,9 @@ include config/utilities.mak | |||
35 | # | 35 | # |
36 | # Define WERROR=0 to disable treating any warnings as errors. | 36 | # Define WERROR=0 to disable treating any warnings as errors. |
37 | # | 37 | # |
38 | # Define NO_NEWT if you do not want TUI support. | 38 | # Define NO_NEWT if you do not want TUI support. (deprecated) |
39 | # | ||
40 | # Define NO_SLANG if you do not want TUI support. | ||
39 | # | 41 | # |
40 | # Define NO_GTK2 if you do not want GTK+ GUI support. | 42 | # Define NO_GTK2 if you do not want GTK+ GUI support. |
41 | # | 43 | # |
@@ -104,6 +106,10 @@ ifdef PARSER_DEBUG | |||
104 | PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG | 106 | PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG |
105 | endif | 107 | endif |
106 | 108 | ||
109 | ifdef NO_NEWT | ||
110 | NO_SLANG=1 | ||
111 | endif | ||
112 | |||
107 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -funwind-tables -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS) | 113 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -funwind-tables -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS) |
108 | EXTLIBS = -lpthread -lrt -lelf -lm | 114 | EXTLIBS = -lpthread -lrt -lelf -lm |
109 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | 115 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE |
@@ -215,6 +221,7 @@ BASIC_CFLAGS = \ | |||
215 | -Iutil \ | 221 | -Iutil \ |
216 | -I. \ | 222 | -I. \ |
217 | -I$(TRACE_EVENT_DIR) \ | 223 | -I$(TRACE_EVENT_DIR) \ |
224 | -I../lib/ \ | ||
218 | -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | 225 | -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE |
219 | 226 | ||
220 | BASIC_LDFLAGS = | 227 | BASIC_LDFLAGS = |
@@ -240,19 +247,28 @@ SCRIPT_SH += perf-archive.sh | |||
240 | grep-libs = $(filter -l%,$(1)) | 247 | grep-libs = $(filter -l%,$(1)) |
241 | strip-libs = $(filter-out -l%,$(1)) | 248 | strip-libs = $(filter-out -l%,$(1)) |
242 | 249 | ||
250 | LK_DIR = ../lib/lk/ | ||
243 | TRACE_EVENT_DIR = ../lib/traceevent/ | 251 | TRACE_EVENT_DIR = ../lib/traceevent/ |
244 | 252 | ||
253 | LK_PATH=$(LK_DIR) | ||
254 | |||
245 | ifneq ($(OUTPUT),) | 255 | ifneq ($(OUTPUT),) |
246 | TE_PATH=$(OUTPUT) | 256 | TE_PATH=$(OUTPUT) |
257 | ifneq ($(subdir),) | ||
258 | LK_PATH=$(OUTPUT)$(LK_DIR) | ||
259 | else | ||
260 | LK_PATH=$(OUTPUT) | ||
261 | endif | ||
247 | else | 262 | else |
248 | TE_PATH=$(TRACE_EVENT_DIR) | 263 | TE_PATH=$(TRACE_EVENT_DIR) |
249 | endif | 264 | endif |
250 | 265 | ||
251 | LIBTRACEEVENT = $(TE_PATH)libtraceevent.a | 266 | LIBTRACEEVENT = $(TE_PATH)libtraceevent.a |
252 | TE_LIB := -L$(TE_PATH) -ltraceevent | ||
253 | |||
254 | export LIBTRACEEVENT | 267 | export LIBTRACEEVENT |
255 | 268 | ||
269 | LIBLK = $(LK_PATH)liblk.a | ||
270 | export LIBLK | ||
271 | |||
256 | # python extension build directories | 272 | # python extension build directories |
257 | PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ | 273 | PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ |
258 | PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ | 274 | PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ |
@@ -262,7 +278,7 @@ export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP | |||
262 | python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so | 278 | python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so |
263 | 279 | ||
264 | PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) | 280 | PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) |
265 | PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py | 281 | PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) |
266 | 282 | ||
267 | $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) | 283 | $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) |
268 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ | 284 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ |
@@ -355,7 +371,6 @@ LIB_H += util/cache.h | |||
355 | LIB_H += util/callchain.h | 371 | LIB_H += util/callchain.h |
356 | LIB_H += util/build-id.h | 372 | LIB_H += util/build-id.h |
357 | LIB_H += util/debug.h | 373 | LIB_H += util/debug.h |
358 | LIB_H += util/debugfs.h | ||
359 | LIB_H += util/sysfs.h | 374 | LIB_H += util/sysfs.h |
360 | LIB_H += util/pmu.h | 375 | LIB_H += util/pmu.h |
361 | LIB_H += util/event.h | 376 | LIB_H += util/event.h |
@@ -416,7 +431,6 @@ LIB_OBJS += $(OUTPUT)util/annotate.o | |||
416 | LIB_OBJS += $(OUTPUT)util/build-id.o | 431 | LIB_OBJS += $(OUTPUT)util/build-id.o |
417 | LIB_OBJS += $(OUTPUT)util/config.o | 432 | LIB_OBJS += $(OUTPUT)util/config.o |
418 | LIB_OBJS += $(OUTPUT)util/ctype.o | 433 | LIB_OBJS += $(OUTPUT)util/ctype.o |
419 | LIB_OBJS += $(OUTPUT)util/debugfs.o | ||
420 | LIB_OBJS += $(OUTPUT)util/sysfs.o | 434 | LIB_OBJS += $(OUTPUT)util/sysfs.o |
421 | LIB_OBJS += $(OUTPUT)util/pmu.o | 435 | LIB_OBJS += $(OUTPUT)util/pmu.o |
422 | LIB_OBJS += $(OUTPUT)util/environment.o | 436 | LIB_OBJS += $(OUTPUT)util/environment.o |
@@ -503,6 +517,10 @@ LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o | |||
503 | LIB_OBJS += $(OUTPUT)tests/pmu.o | 517 | LIB_OBJS += $(OUTPUT)tests/pmu.o |
504 | LIB_OBJS += $(OUTPUT)tests/hists_link.o | 518 | LIB_OBJS += $(OUTPUT)tests/hists_link.o |
505 | LIB_OBJS += $(OUTPUT)tests/python-use.o | 519 | LIB_OBJS += $(OUTPUT)tests/python-use.o |
520 | LIB_OBJS += $(OUTPUT)tests/bp_signal.o | ||
521 | LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o | ||
522 | LIB_OBJS += $(OUTPUT)tests/task-exit.o | ||
523 | LIB_OBJS += $(OUTPUT)tests/sw-clock.o | ||
506 | 524 | ||
507 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 525 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
508 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 526 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
@@ -535,8 +553,9 @@ BUILTIN_OBJS += $(OUTPUT)builtin-lock.o | |||
535 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o | 553 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o |
536 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o | 554 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o |
537 | BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o | 555 | BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o |
556 | BUILTIN_OBJS += $(OUTPUT)builtin-mem.o | ||
538 | 557 | ||
539 | PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) | 558 | PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) |
540 | 559 | ||
541 | # | 560 | # |
542 | # Platform specific tweaks | 561 | # Platform specific tweaks |
@@ -667,15 +686,15 @@ ifndef NO_LIBAUDIT | |||
667 | endif | 686 | endif |
668 | endif | 687 | endif |
669 | 688 | ||
670 | ifndef NO_NEWT | 689 | ifndef NO_SLANG |
671 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt | 690 | FLAGS_SLANG=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -I/usr/include/slang -lslang |
672 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT),libnewt),y) | 691 | ifneq ($(call try-cc,$(SOURCE_SLANG),$(FLAGS_SLANG),libslang),y) |
673 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); | 692 | msg := $(warning slang not found, disables TUI support. Please install slang-devel or libslang-dev); |
674 | else | 693 | else |
675 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | 694 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h |
676 | BASIC_CFLAGS += -I/usr/include/slang | 695 | BASIC_CFLAGS += -I/usr/include/slang |
677 | BASIC_CFLAGS += -DNEWT_SUPPORT | 696 | BASIC_CFLAGS += -DSLANG_SUPPORT |
678 | EXTLIBS += -lnewt -lslang | 697 | EXTLIBS += -lslang |
679 | LIB_OBJS += $(OUTPUT)ui/browser.o | 698 | LIB_OBJS += $(OUTPUT)ui/browser.o |
680 | LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o | 699 | LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o |
681 | LIB_OBJS += $(OUTPUT)ui/browsers/hists.o | 700 | LIB_OBJS += $(OUTPUT)ui/browsers/hists.o |
@@ -1051,6 +1070,18 @@ $(LIBTRACEEVENT): | |||
1051 | $(LIBTRACEEVENT)-clean: | 1070 | $(LIBTRACEEVENT)-clean: |
1052 | $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean | 1071 | $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean |
1053 | 1072 | ||
1073 | # if subdir is set, we've been called from above so target has been built | ||
1074 | # already | ||
1075 | $(LIBLK): | ||
1076 | ifeq ($(subdir),) | ||
1077 | $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a | ||
1078 | endif | ||
1079 | |||
1080 | $(LIBLK)-clean: | ||
1081 | ifeq ($(subdir),) | ||
1082 | $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean | ||
1083 | endif | ||
1084 | |||
1054 | help: | 1085 | help: |
1055 | @echo 'Perf make targets:' | 1086 | @echo 'Perf make targets:' |
1056 | @echo ' doc - make *all* documentation (see below)' | 1087 | @echo ' doc - make *all* documentation (see below)' |
@@ -1171,7 +1202,7 @@ $(INSTALL_DOC_TARGETS): | |||
1171 | 1202 | ||
1172 | ### Cleaning rules | 1203 | ### Cleaning rules |
1173 | 1204 | ||
1174 | clean: $(LIBTRACEEVENT)-clean | 1205 | clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean |
1175 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) | 1206 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) |
1176 | $(RM) $(ALL_PROGRAMS) perf | 1207 | $(RM) $(ALL_PROGRAMS) perf |
1177 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 1208 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
@@ -1181,6 +1212,6 @@ clean: $(LIBTRACEEVENT)-clean | |||
1181 | $(RM) $(OUTPUT)util/*-flex* | 1212 | $(RM) $(OUTPUT)util/*-flex* |
1182 | $(python-clean) | 1213 | $(python-clean) |
1183 | 1214 | ||
1184 | .PHONY: all install clean strip $(LIBTRACEEVENT) | 1215 | .PHONY: all install clean strip $(LIBTRACEEVENT) $(LIBLK) |
1185 | .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell | 1216 | .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell |
1186 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS | 1217 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS |
diff --git a/tools/perf/arch/arm/util/dwarf-regs.c b/tools/perf/arch/arm/util/dwarf-regs.c index e8d5c551c69c..33ec5b339da8 100644 --- a/tools/perf/arch/arm/util/dwarf-regs.c +++ b/tools/perf/arch/arm/util/dwarf-regs.c | |||
@@ -8,10 +8,7 @@ | |||
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <stdlib.h> | 11 | #include <stddef.h> |
12 | #ifndef __UCLIBC__ | ||
13 | #include <libio.h> | ||
14 | #endif | ||
15 | #include <dwarf-regs.h> | 12 | #include <dwarf-regs.h> |
16 | 13 | ||
17 | struct pt_regs_dwarfnum { | 14 | struct pt_regs_dwarfnum { |
diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c index 7cdd61d0e27c..733151cdf46e 100644 --- a/tools/perf/arch/powerpc/util/dwarf-regs.c +++ b/tools/perf/arch/powerpc/util/dwarf-regs.c | |||
@@ -9,10 +9,7 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <stdlib.h> | 12 | #include <stddef.h> |
13 | #ifndef __UCLIBC__ | ||
14 | #include <libio.h> | ||
15 | #endif | ||
16 | #include <dwarf-regs.h> | 13 | #include <dwarf-regs.h> |
17 | 14 | ||
18 | 15 | ||
diff --git a/tools/perf/arch/s390/util/dwarf-regs.c b/tools/perf/arch/s390/util/dwarf-regs.c index e19653e025fa..0469df02ee62 100644 --- a/tools/perf/arch/s390/util/dwarf-regs.c +++ b/tools/perf/arch/s390/util/dwarf-regs.c | |||
@@ -6,7 +6,7 @@ | |||
6 | * | 6 | * |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <libio.h> | 9 | #include <stddef.h> |
10 | #include <dwarf-regs.h> | 10 | #include <dwarf-regs.h> |
11 | 11 | ||
12 | #define NUM_GPRS 16 | 12 | #define NUM_GPRS 16 |
diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c index a11edb007a6c..0d0897f57a10 100644 --- a/tools/perf/arch/sh/util/dwarf-regs.c +++ b/tools/perf/arch/sh/util/dwarf-regs.c | |||
@@ -19,7 +19,7 @@ | |||
19 | * | 19 | * |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <libio.h> | 22 | #include <stddef.h> |
23 | #include <dwarf-regs.h> | 23 | #include <dwarf-regs.h> |
24 | 24 | ||
25 | /* | 25 | /* |
diff --git a/tools/perf/arch/sparc/util/dwarf-regs.c b/tools/perf/arch/sparc/util/dwarf-regs.c index 0ab88483720c..92eda412fed3 100644 --- a/tools/perf/arch/sparc/util/dwarf-regs.c +++ b/tools/perf/arch/sparc/util/dwarf-regs.c | |||
@@ -9,7 +9,7 @@ | |||
9 | * 2 of the License, or (at your option) any later version. | 9 | * 2 of the License, or (at your option) any later version. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <libio.h> | 12 | #include <stddef.h> |
13 | #include <dwarf-regs.h> | 13 | #include <dwarf-regs.h> |
14 | 14 | ||
15 | #define SPARC_MAX_REGS 96 | 15 | #define SPARC_MAX_REGS 96 |
diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c index a794d3081928..be22dd463232 100644 --- a/tools/perf/arch/x86/util/dwarf-regs.c +++ b/tools/perf/arch/x86/util/dwarf-regs.c | |||
@@ -20,7 +20,7 @@ | |||
20 | * | 20 | * |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <libio.h> | 23 | #include <stddef.h> |
24 | #include <dwarf-regs.h> | 24 | #include <dwarf-regs.h> |
25 | 25 | ||
26 | /* | 26 | /* |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 2e6961ea3184..db491e9a812b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -63,7 +63,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, | |||
63 | return 0; | 63 | return 0; |
64 | } | 64 | } |
65 | 65 | ||
66 | he = __hists__add_entry(&evsel->hists, al, NULL, 1); | 66 | he = __hists__add_entry(&evsel->hists, al, NULL, 1, 1); |
67 | if (he == NULL) | 67 | if (he == NULL) |
68 | return -ENOMEM; | 68 | return -ENOMEM; |
69 | 69 | ||
@@ -109,14 +109,16 @@ static int process_sample_event(struct perf_tool *tool, | |||
109 | return 0; | 109 | return 0; |
110 | } | 110 | } |
111 | 111 | ||
112 | static int hist_entry__tty_annotate(struct hist_entry *he, int evidx, | 112 | static int hist_entry__tty_annotate(struct hist_entry *he, |
113 | struct perf_evsel *evsel, | ||
113 | struct perf_annotate *ann) | 114 | struct perf_annotate *ann) |
114 | { | 115 | { |
115 | return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx, | 116 | return symbol__tty_annotate(he->ms.sym, he->ms.map, evsel, |
116 | ann->print_line, ann->full_paths, 0, 0); | 117 | ann->print_line, ann->full_paths, 0, 0); |
117 | } | 118 | } |
118 | 119 | ||
119 | static void hists__find_annotations(struct hists *self, int evidx, | 120 | static void hists__find_annotations(struct hists *self, |
121 | struct perf_evsel *evsel, | ||
120 | struct perf_annotate *ann) | 122 | struct perf_annotate *ann) |
121 | { | 123 | { |
122 | struct rb_node *nd = rb_first(&self->entries), *next; | 124 | struct rb_node *nd = rb_first(&self->entries), *next; |
@@ -142,14 +144,14 @@ find_next: | |||
142 | if (use_browser == 2) { | 144 | if (use_browser == 2) { |
143 | int ret; | 145 | int ret; |
144 | 146 | ||
145 | ret = hist_entry__gtk_annotate(he, evidx, NULL); | 147 | ret = hist_entry__gtk_annotate(he, evsel, NULL); |
146 | if (!ret || !ann->skip_missing) | 148 | if (!ret || !ann->skip_missing) |
147 | return; | 149 | return; |
148 | 150 | ||
149 | /* skip missing symbols */ | 151 | /* skip missing symbols */ |
150 | nd = rb_next(nd); | 152 | nd = rb_next(nd); |
151 | } else if (use_browser == 1) { | 153 | } else if (use_browser == 1) { |
152 | key = hist_entry__tui_annotate(he, evidx, NULL); | 154 | key = hist_entry__tui_annotate(he, evsel, NULL); |
153 | switch (key) { | 155 | switch (key) { |
154 | case -1: | 156 | case -1: |
155 | if (!ann->skip_missing) | 157 | if (!ann->skip_missing) |
@@ -168,7 +170,7 @@ find_next: | |||
168 | if (next != NULL) | 170 | if (next != NULL) |
169 | nd = next; | 171 | nd = next; |
170 | } else { | 172 | } else { |
171 | hist_entry__tty_annotate(he, evidx, ann); | 173 | hist_entry__tty_annotate(he, evsel, ann); |
172 | nd = rb_next(nd); | 174 | nd = rb_next(nd); |
173 | /* | 175 | /* |
174 | * Since we have a hist_entry per IP for the same | 176 | * Since we have a hist_entry per IP for the same |
@@ -230,7 +232,12 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
230 | total_nr_samples += nr_samples; | 232 | total_nr_samples += nr_samples; |
231 | hists__collapse_resort(hists); | 233 | hists__collapse_resort(hists); |
232 | hists__output_resort(hists); | 234 | hists__output_resort(hists); |
233 | hists__find_annotations(hists, pos->idx, ann); | 235 | |
236 | if (symbol_conf.event_group && | ||
237 | !perf_evsel__is_group_leader(pos)) | ||
238 | continue; | ||
239 | |||
240 | hists__find_annotations(hists, pos, ann); | ||
234 | } | 241 | } |
235 | } | 242 | } |
236 | 243 | ||
@@ -312,6 +319,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
312 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 319 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
313 | OPT_STRING(0, "objdump", &objdump_path, "path", | 320 | OPT_STRING(0, "objdump", &objdump_path, "path", |
314 | "objdump binary to use for disassembly and annotations"), | 321 | "objdump binary to use for disassembly and annotations"), |
322 | OPT_BOOLEAN(0, "group", &symbol_conf.event_group, | ||
323 | "Show event group information together"), | ||
315 | OPT_END() | 324 | OPT_END() |
316 | }; | 325 | }; |
317 | 326 | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index d207a97a2db1..2d0462d89a97 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -231,9 +231,10 @@ int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, | |||
231 | } | 231 | } |
232 | 232 | ||
233 | static int hists__add_entry(struct hists *self, | 233 | static int hists__add_entry(struct hists *self, |
234 | struct addr_location *al, u64 period) | 234 | struct addr_location *al, u64 period, |
235 | u64 weight) | ||
235 | { | 236 | { |
236 | if (__hists__add_entry(self, al, NULL, period) != NULL) | 237 | if (__hists__add_entry(self, al, NULL, period, weight) != NULL) |
237 | return 0; | 238 | return 0; |
238 | return -ENOMEM; | 239 | return -ENOMEM; |
239 | } | 240 | } |
@@ -255,7 +256,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
255 | if (al.filtered) | 256 | if (al.filtered) |
256 | return 0; | 257 | return 0; |
257 | 258 | ||
258 | if (hists__add_entry(&evsel->hists, &al, sample->period)) { | 259 | if (hists__add_entry(&evsel->hists, &al, sample->period, sample->weight)) { |
259 | pr_warning("problem incrementing symbol period, skipping event\n"); | 260 | pr_warning("problem incrementing symbol period, skipping event\n"); |
260 | return -1; | 261 | return -1; |
261 | } | 262 | } |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 37a769d7f9fe..533501e2b07c 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -12,7 +12,7 @@ | |||
12 | #include "util/parse-options.h" | 12 | #include "util/parse-options.h" |
13 | #include "util/trace-event.h" | 13 | #include "util/trace-event.h" |
14 | #include "util/debug.h" | 14 | #include "util/debug.h" |
15 | #include "util/debugfs.h" | 15 | #include <lk/debugfs.h> |
16 | #include "util/tool.h" | 16 | #include "util/tool.h" |
17 | #include "util/stat.h" | 17 | #include "util/stat.h" |
18 | 18 | ||
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c new file mode 100644 index 000000000000..a8ff6d264e50 --- /dev/null +++ b/tools/perf/builtin-mem.c | |||
@@ -0,0 +1,242 @@ | |||
1 | #include "builtin.h" | ||
2 | #include "perf.h" | ||
3 | |||
4 | #include "util/parse-options.h" | ||
5 | #include "util/trace-event.h" | ||
6 | #include "util/tool.h" | ||
7 | #include "util/session.h" | ||
8 | |||
9 | #define MEM_OPERATION_LOAD "load" | ||
10 | #define MEM_OPERATION_STORE "store" | ||
11 | |||
12 | static const char *mem_operation = MEM_OPERATION_LOAD; | ||
13 | |||
14 | struct perf_mem { | ||
15 | struct perf_tool tool; | ||
16 | char const *input_name; | ||
17 | symbol_filter_t annotate_init; | ||
18 | bool hide_unresolved; | ||
19 | bool dump_raw; | ||
20 | const char *cpu_list; | ||
21 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
22 | }; | ||
23 | |||
24 | static const char * const mem_usage[] = { | ||
25 | "perf mem [<options>] {record <command> |report}", | ||
26 | NULL | ||
27 | }; | ||
28 | |||
29 | static int __cmd_record(int argc, const char **argv) | ||
30 | { | ||
31 | int rec_argc, i = 0, j; | ||
32 | const char **rec_argv; | ||
33 | char event[64]; | ||
34 | int ret; | ||
35 | |||
36 | rec_argc = argc + 4; | ||
37 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
38 | if (!rec_argv) | ||
39 | return -1; | ||
40 | |||
41 | rec_argv[i++] = strdup("record"); | ||
42 | if (!strcmp(mem_operation, MEM_OPERATION_LOAD)) | ||
43 | rec_argv[i++] = strdup("-W"); | ||
44 | rec_argv[i++] = strdup("-d"); | ||
45 | rec_argv[i++] = strdup("-e"); | ||
46 | |||
47 | if (strcmp(mem_operation, MEM_OPERATION_LOAD)) | ||
48 | sprintf(event, "cpu/mem-stores/pp"); | ||
49 | else | ||
50 | sprintf(event, "cpu/mem-loads/pp"); | ||
51 | |||
52 | rec_argv[i++] = strdup(event); | ||
53 | for (j = 1; j < argc; j++, i++) | ||
54 | rec_argv[i] = argv[j]; | ||
55 | |||
56 | ret = cmd_record(i, rec_argv, NULL); | ||
57 | free(rec_argv); | ||
58 | return ret; | ||
59 | } | ||
60 | |||
61 | static int | ||
62 | dump_raw_samples(struct perf_tool *tool, | ||
63 | union perf_event *event, | ||
64 | struct perf_sample *sample, | ||
65 | struct perf_evsel *evsel __maybe_unused, | ||
66 | struct machine *machine) | ||
67 | { | ||
68 | struct perf_mem *mem = container_of(tool, struct perf_mem, tool); | ||
69 | struct addr_location al; | ||
70 | const char *fmt; | ||
71 | |||
72 | if (perf_event__preprocess_sample(event, machine, &al, sample, | ||
73 | mem->annotate_init) < 0) { | ||
74 | fprintf(stderr, "problem processing %d event, skipping it.\n", | ||
75 | event->header.type); | ||
76 | return -1; | ||
77 | } | ||
78 | |||
79 | if (al.filtered || (mem->hide_unresolved && al.sym == NULL)) | ||
80 | return 0; | ||
81 | |||
82 | if (al.map != NULL) | ||
83 | al.map->dso->hit = 1; | ||
84 | |||
85 | if (symbol_conf.field_sep) { | ||
86 | fmt = "%d%s%d%s0x%"PRIx64"%s0x%"PRIx64"%s%"PRIu64 | ||
87 | "%s0x%"PRIx64"%s%s:%s\n"; | ||
88 | } else { | ||
89 | fmt = "%5d%s%5d%s0x%016"PRIx64"%s0x016%"PRIx64 | ||
90 | "%s%5"PRIu64"%s0x%06"PRIx64"%s%s:%s\n"; | ||
91 | symbol_conf.field_sep = " "; | ||
92 | } | ||
93 | |||
94 | printf(fmt, | ||
95 | sample->pid, | ||
96 | symbol_conf.field_sep, | ||
97 | sample->tid, | ||
98 | symbol_conf.field_sep, | ||
99 | event->ip.ip, | ||
100 | symbol_conf.field_sep, | ||
101 | sample->addr, | ||
102 | symbol_conf.field_sep, | ||
103 | sample->weight, | ||
104 | symbol_conf.field_sep, | ||
105 | sample->data_src, | ||
106 | symbol_conf.field_sep, | ||
107 | al.map ? (al.map->dso ? al.map->dso->long_name : "???") : "???", | ||
108 | al.sym ? al.sym->name : "???"); | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static int process_sample_event(struct perf_tool *tool, | ||
114 | union perf_event *event, | ||
115 | struct perf_sample *sample, | ||
116 | struct perf_evsel *evsel, | ||
117 | struct machine *machine) | ||
118 | { | ||
119 | return dump_raw_samples(tool, event, sample, evsel, machine); | ||
120 | } | ||
121 | |||
122 | static int report_raw_events(struct perf_mem *mem) | ||
123 | { | ||
124 | int err = -EINVAL; | ||
125 | int ret; | ||
126 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, | ||
127 | 0, false, &mem->tool); | ||
128 | |||
129 | if (session == NULL) | ||
130 | return -ENOMEM; | ||
131 | |||
132 | if (mem->cpu_list) { | ||
133 | ret = perf_session__cpu_bitmap(session, mem->cpu_list, | ||
134 | mem->cpu_bitmap); | ||
135 | if (ret) | ||
136 | goto out_delete; | ||
137 | } | ||
138 | |||
139 | if (symbol__init() < 0) | ||
140 | return -1; | ||
141 | |||
142 | printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n"); | ||
143 | |||
144 | err = perf_session__process_events(session, &mem->tool); | ||
145 | if (err) | ||
146 | return err; | ||
147 | |||
148 | return 0; | ||
149 | |||
150 | out_delete: | ||
151 | perf_session__delete(session); | ||
152 | return err; | ||
153 | } | ||
154 | |||
155 | static int report_events(int argc, const char **argv, struct perf_mem *mem) | ||
156 | { | ||
157 | const char **rep_argv; | ||
158 | int ret, i = 0, j, rep_argc; | ||
159 | |||
160 | if (mem->dump_raw) | ||
161 | return report_raw_events(mem); | ||
162 | |||
163 | rep_argc = argc + 3; | ||
164 | rep_argv = calloc(rep_argc + 1, sizeof(char *)); | ||
165 | if (!rep_argv) | ||
166 | return -1; | ||
167 | |||
168 | rep_argv[i++] = strdup("report"); | ||
169 | rep_argv[i++] = strdup("--mem-mode"); | ||
170 | rep_argv[i++] = strdup("-n"); /* display number of samples */ | ||
171 | |||
172 | /* | ||
173 | * there is no weight (cost) associated with stores, so don't print | ||
174 | * the column | ||
175 | */ | ||
176 | if (strcmp(mem_operation, MEM_OPERATION_LOAD)) | ||
177 | rep_argv[i++] = strdup("--sort=mem,sym,dso,symbol_daddr," | ||
178 | "dso_daddr,tlb,locked"); | ||
179 | |||
180 | for (j = 1; j < argc; j++, i++) | ||
181 | rep_argv[i] = argv[j]; | ||
182 | |||
183 | ret = cmd_report(i, rep_argv, NULL); | ||
184 | free(rep_argv); | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | ||
189 | { | ||
190 | struct stat st; | ||
191 | struct perf_mem mem = { | ||
192 | .tool = { | ||
193 | .sample = process_sample_event, | ||
194 | .mmap = perf_event__process_mmap, | ||
195 | .comm = perf_event__process_comm, | ||
196 | .lost = perf_event__process_lost, | ||
197 | .fork = perf_event__process_fork, | ||
198 | .build_id = perf_event__process_build_id, | ||
199 | .ordered_samples = true, | ||
200 | }, | ||
201 | .input_name = "perf.data", | ||
202 | }; | ||
203 | const struct option mem_options[] = { | ||
204 | OPT_STRING('t', "type", &mem_operation, | ||
205 | "type", "memory operations(load/store)"), | ||
206 | OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw, | ||
207 | "dump raw samples in ASCII"), | ||
208 | OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved, | ||
209 | "Only display entries resolved to a symbol"), | ||
210 | OPT_STRING('i', "input", &input_name, "file", | ||
211 | "input file name"), | ||
212 | OPT_STRING('C', "cpu", &mem.cpu_list, "cpu", | ||
213 | "list of cpus to profile"), | ||
214 | OPT_STRING('x', "field-separator", &symbol_conf.field_sep, | ||
215 | "separator", | ||
216 | "separator for columns, no spaces will be added" | ||
217 | " between columns '.' is reserved."), | ||
218 | OPT_END() | ||
219 | }; | ||
220 | |||
221 | argc = parse_options(argc, argv, mem_options, mem_usage, | ||
222 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
223 | |||
224 | if (!argc || !(strncmp(argv[0], "rec", 3) || mem_operation)) | ||
225 | usage_with_options(mem_usage, mem_options); | ||
226 | |||
227 | if (!mem.input_name || !strlen(mem.input_name)) { | ||
228 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) | ||
229 | mem.input_name = "-"; | ||
230 | else | ||
231 | mem.input_name = "perf.data"; | ||
232 | } | ||
233 | |||
234 | if (!strncmp(argv[0], "rec", 3)) | ||
235 | return __cmd_record(argc, argv); | ||
236 | else if (!strncmp(argv[0], "rep", 3)) | ||
237 | return report_events(argc, argv, &mem); | ||
238 | else | ||
239 | usage_with_options(mem_usage, mem_options); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index de38a034b129..e8a66f9a6715 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -37,7 +37,7 @@ | |||
37 | #include "util/strfilter.h" | 37 | #include "util/strfilter.h" |
38 | #include "util/symbol.h" | 38 | #include "util/symbol.h" |
39 | #include "util/debug.h" | 39 | #include "util/debug.h" |
40 | #include "util/debugfs.h" | 40 | #include <lk/debugfs.h> |
41 | #include "util/parse-options.h" | 41 | #include "util/parse-options.h" |
42 | #include "util/probe-finder.h" | 42 | #include "util/probe-finder.h" |
43 | #include "util/probe-event.h" | 43 | #include "util/probe-event.h" |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f1a939ebc19c..cdf58ecc04b1 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -5,8 +5,6 @@ | |||
5 | * (or a CPU, or a PID) into the perf.data output file - for | 5 | * (or a CPU, or a PID) into the perf.data output file - for |
6 | * later analysis via perf report. | 6 | * later analysis via perf report. |
7 | */ | 7 | */ |
8 | #define _FILE_OFFSET_BITS 64 | ||
9 | |||
10 | #include "builtin.h" | 8 | #include "builtin.h" |
11 | 9 | ||
12 | #include "perf.h" | 10 | #include "perf.h" |
@@ -474,7 +472,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
474 | } | 472 | } |
475 | 473 | ||
476 | if (forks) { | 474 | if (forks) { |
477 | err = perf_evlist__prepare_workload(evsel_list, opts, argv); | 475 | err = perf_evlist__prepare_workload(evsel_list, &opts->target, |
476 | argv, opts->pipe_output, | ||
477 | true); | ||
478 | if (err < 0) { | 478 | if (err < 0) { |
479 | pr_err("Couldn't run the workload!\n"); | 479 | pr_err("Couldn't run the workload!\n"); |
480 | goto out_delete_session; | 480 | goto out_delete_session; |
@@ -953,6 +953,8 @@ const struct option record_options[] = { | |||
953 | OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack, | 953 | OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack, |
954 | "branch filter mask", "branch stack filter modes", | 954 | "branch filter mask", "branch stack filter modes", |
955 | parse_branch_stack), | 955 | parse_branch_stack), |
956 | OPT_BOOLEAN('W', "weight", &record.opts.sample_weight, | ||
957 | "sample by weight (on special events only)"), | ||
956 | OPT_END() | 958 | OPT_END() |
957 | }; | 959 | }; |
958 | 960 | ||
@@ -964,7 +966,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
964 | struct perf_record *rec = &record; | 966 | struct perf_record *rec = &record; |
965 | char errbuf[BUFSIZ]; | 967 | char errbuf[BUFSIZ]; |
966 | 968 | ||
967 | evsel_list = perf_evlist__new(NULL, NULL); | 969 | evsel_list = perf_evlist__new(); |
968 | if (evsel_list == NULL) | 970 | if (evsel_list == NULL) |
969 | return -ENOMEM; | 971 | return -ENOMEM; |
970 | 972 | ||
@@ -1026,7 +1028,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1026 | ui__error("%s", errbuf); | 1028 | ui__error("%s", errbuf); |
1027 | 1029 | ||
1028 | err = -saved_errno; | 1030 | err = -saved_errno; |
1029 | goto out_free_fd; | 1031 | goto out_symbol_exit; |
1030 | } | 1032 | } |
1031 | 1033 | ||
1032 | err = -ENOMEM; | 1034 | err = -ENOMEM; |
@@ -1057,6 +1059,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1057 | } | 1059 | } |
1058 | 1060 | ||
1059 | err = __cmd_record(&record, argc, argv); | 1061 | err = __cmd_record(&record, argc, argv); |
1062 | |||
1063 | perf_evlist__munmap(evsel_list); | ||
1064 | perf_evlist__close(evsel_list); | ||
1060 | out_free_fd: | 1065 | out_free_fd: |
1061 | perf_evlist__delete_maps(evsel_list); | 1066 | perf_evlist__delete_maps(evsel_list); |
1062 | out_symbol_exit: | 1067 | out_symbol_exit: |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 96b5a7fee4bb..bd0ca81eeaca 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -13,7 +13,6 @@ | |||
13 | #include "util/annotate.h" | 13 | #include "util/annotate.h" |
14 | #include "util/color.h" | 14 | #include "util/color.h" |
15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
16 | #include "util/cache.h" | ||
17 | #include <linux/rbtree.h> | 16 | #include <linux/rbtree.h> |
18 | #include "util/symbol.h" | 17 | #include "util/symbol.h" |
19 | #include "util/callchain.h" | 18 | #include "util/callchain.h" |
@@ -47,6 +46,7 @@ struct perf_report { | |||
47 | bool show_full_info; | 46 | bool show_full_info; |
48 | bool show_threads; | 47 | bool show_threads; |
49 | bool inverted_callchain; | 48 | bool inverted_callchain; |
49 | bool mem_mode; | ||
50 | struct perf_read_values show_threads_values; | 50 | struct perf_read_values show_threads_values; |
51 | const char *pretty_printing_style; | 51 | const char *pretty_printing_style; |
52 | symbol_filter_t annotate_init; | 52 | symbol_filter_t annotate_init; |
@@ -65,6 +65,99 @@ static int perf_report_config(const char *var, const char *value, void *cb) | |||
65 | return perf_default_config(var, value, cb); | 65 | return perf_default_config(var, value, cb); |
66 | } | 66 | } |
67 | 67 | ||
68 | static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | ||
69 | struct addr_location *al, | ||
70 | struct perf_sample *sample, | ||
71 | struct perf_evsel *evsel, | ||
72 | struct machine *machine, | ||
73 | union perf_event *event) | ||
74 | { | ||
75 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | ||
76 | struct symbol *parent = NULL; | ||
77 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
78 | int err = 0; | ||
79 | struct hist_entry *he; | ||
80 | struct mem_info *mi, *mx; | ||
81 | uint64_t cost; | ||
82 | |||
83 | if ((sort__has_parent || symbol_conf.use_callchain) && | ||
84 | sample->callchain) { | ||
85 | err = machine__resolve_callchain(machine, evsel, al->thread, | ||
86 | sample, &parent); | ||
87 | if (err) | ||
88 | return err; | ||
89 | } | ||
90 | |||
91 | mi = machine__resolve_mem(machine, al->thread, sample, cpumode); | ||
92 | if (!mi) | ||
93 | return -ENOMEM; | ||
94 | |||
95 | if (rep->hide_unresolved && !al->sym) | ||
96 | return 0; | ||
97 | |||
98 | cost = sample->weight; | ||
99 | if (!cost) | ||
100 | cost = 1; | ||
101 | |||
102 | /* | ||
103 | * must pass period=weight in order to get the correct | ||
104 | * sorting from hists__collapse_resort() which is solely | ||
105 | * based on periods. We want sorting be done on nr_events * weight | ||
106 | * and this is indirectly achieved by passing period=weight here | ||
107 | * and the he_stat__add_period() function. | ||
108 | */ | ||
109 | he = __hists__add_mem_entry(&evsel->hists, al, parent, mi, cost, cost); | ||
110 | if (!he) | ||
111 | return -ENOMEM; | ||
112 | |||
113 | /* | ||
114 | * In the TUI browser, we are doing integrated annotation, | ||
115 | * so we don't allocate the extra space needed because the stdio | ||
116 | * code will not use it. | ||
117 | */ | ||
118 | if (sort__has_sym && he->ms.sym && use_browser > 0) { | ||
119 | struct annotation *notes = symbol__annotation(he->ms.sym); | ||
120 | |||
121 | assert(evsel != NULL); | ||
122 | |||
123 | if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0) | ||
124 | goto out; | ||
125 | |||
126 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | ||
127 | if (err) | ||
128 | goto out; | ||
129 | } | ||
130 | |||
131 | if (sort__has_sym && he->mem_info->daddr.sym && use_browser > 0) { | ||
132 | struct annotation *notes; | ||
133 | |||
134 | mx = he->mem_info; | ||
135 | |||
136 | notes = symbol__annotation(mx->daddr.sym); | ||
137 | if (notes->src == NULL && symbol__alloc_hist(mx->daddr.sym) < 0) | ||
138 | goto out; | ||
139 | |||
140 | err = symbol__inc_addr_samples(mx->daddr.sym, | ||
141 | mx->daddr.map, | ||
142 | evsel->idx, | ||
143 | mx->daddr.al_addr); | ||
144 | if (err) | ||
145 | goto out; | ||
146 | } | ||
147 | |||
148 | evsel->hists.stats.total_period += cost; | ||
149 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
150 | err = 0; | ||
151 | |||
152 | if (symbol_conf.use_callchain) { | ||
153 | err = callchain_append(he->callchain, | ||
154 | &callchain_cursor, | ||
155 | sample->period); | ||
156 | } | ||
157 | out: | ||
158 | return err; | ||
159 | } | ||
160 | |||
68 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | 161 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, |
69 | struct addr_location *al, | 162 | struct addr_location *al, |
70 | struct perf_sample *sample, | 163 | struct perf_sample *sample, |
@@ -99,7 +192,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
99 | * and not events sampled. Thus we use a pseudo period of 1. | 192 | * and not events sampled. Thus we use a pseudo period of 1. |
100 | */ | 193 | */ |
101 | he = __hists__add_branch_entry(&evsel->hists, al, parent, | 194 | he = __hists__add_branch_entry(&evsel->hists, al, parent, |
102 | &bi[i], 1); | 195 | &bi[i], 1, 1); |
103 | if (he) { | 196 | if (he) { |
104 | struct annotation *notes; | 197 | struct annotation *notes; |
105 | err = -ENOMEM; | 198 | err = -ENOMEM; |
@@ -157,7 +250,8 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
157 | return err; | 250 | return err; |
158 | } | 251 | } |
159 | 252 | ||
160 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period); | 253 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period, |
254 | sample->weight); | ||
161 | if (he == NULL) | 255 | if (he == NULL) |
162 | return -ENOMEM; | 256 | return -ENOMEM; |
163 | 257 | ||
@@ -169,7 +263,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
169 | return err; | 263 | return err; |
170 | } | 264 | } |
171 | /* | 265 | /* |
172 | * Only in the newt browser we are doing integrated annotation, | 266 | * Only in the TUI browser we are doing integrated annotation, |
173 | * so we don't allocated the extra space needed because the stdio | 267 | * so we don't allocated the extra space needed because the stdio |
174 | * code will not use it. | 268 | * code will not use it. |
175 | */ | 269 | */ |
@@ -220,6 +314,12 @@ static int process_sample_event(struct perf_tool *tool, | |||
220 | pr_debug("problem adding lbr entry, skipping event\n"); | 314 | pr_debug("problem adding lbr entry, skipping event\n"); |
221 | return -1; | 315 | return -1; |
222 | } | 316 | } |
317 | } else if (rep->mem_mode == 1) { | ||
318 | if (perf_report__add_mem_hist_entry(tool, &al, sample, | ||
319 | evsel, machine, event)) { | ||
320 | pr_debug("problem adding mem entry, skipping event\n"); | ||
321 | return -1; | ||
322 | } | ||
223 | } else { | 323 | } else { |
224 | if (al.map != NULL) | 324 | if (al.map != NULL) |
225 | al.map->dso->hit = 1; | 325 | al.map->dso->hit = 1; |
@@ -303,7 +403,8 @@ static void sig_handler(int sig __maybe_unused) | |||
303 | session_done = 1; | 403 | session_done = 1; |
304 | } | 404 | } |
305 | 405 | ||
306 | static size_t hists__fprintf_nr_sample_events(struct hists *self, | 406 | static size_t hists__fprintf_nr_sample_events(struct perf_report *rep, |
407 | struct hists *self, | ||
307 | const char *evname, FILE *fp) | 408 | const char *evname, FILE *fp) |
308 | { | 409 | { |
309 | size_t ret; | 410 | size_t ret; |
@@ -314,7 +415,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, | |||
314 | char buf[512]; | 415 | char buf[512]; |
315 | size_t size = sizeof(buf); | 416 | size_t size = sizeof(buf); |
316 | 417 | ||
317 | if (symbol_conf.event_group && evsel->nr_members > 1) { | 418 | if (perf_evsel__is_group_event(evsel)) { |
318 | struct perf_evsel *pos; | 419 | struct perf_evsel *pos; |
319 | 420 | ||
320 | perf_evsel__group_desc(evsel, buf, size); | 421 | perf_evsel__group_desc(evsel, buf, size); |
@@ -331,7 +432,11 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, | |||
331 | if (evname != NULL) | 432 | if (evname != NULL) |
332 | ret += fprintf(fp, " of event '%s'", evname); | 433 | ret += fprintf(fp, " of event '%s'", evname); |
333 | 434 | ||
334 | ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); | 435 | if (rep->mem_mode) { |
436 | ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events); | ||
437 | ret += fprintf(fp, "\n# Sort order : %s", sort_order); | ||
438 | } else | ||
439 | ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); | ||
335 | return ret + fprintf(fp, "\n#\n"); | 440 | return ret + fprintf(fp, "\n#\n"); |
336 | } | 441 | } |
337 | 442 | ||
@@ -349,7 +454,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
349 | !perf_evsel__is_group_leader(pos)) | 454 | !perf_evsel__is_group_leader(pos)) |
350 | continue; | 455 | continue; |
351 | 456 | ||
352 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 457 | hists__fprintf_nr_sample_events(rep, hists, evname, stdout); |
353 | hists__fprintf(hists, true, 0, 0, stdout); | 458 | hists__fprintf(hists, true, 0, 0, stdout); |
354 | fprintf(stdout, "\n\n"); | 459 | fprintf(stdout, "\n\n"); |
355 | } | 460 | } |
@@ -645,7 +750,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
645 | "Use the stdio interface"), | 750 | "Use the stdio interface"), |
646 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 751 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
647 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," | 752 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," |
648 | " dso_to, dso_from, symbol_to, symbol_from, mispredict"), | 753 | " dso_to, dso_from, symbol_to, symbol_from, mispredict," |
754 | " weight, local_weight, mem, symbol_daddr, dso_daddr, tlb, " | ||
755 | "snoop, locked"), | ||
649 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 756 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
650 | "Show sample percentage for different cpu modes"), | 757 | "Show sample percentage for different cpu modes"), |
651 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 758 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
@@ -693,6 +800,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
693 | "use branch records for histogram filling", parse_branch_mode), | 800 | "use branch records for histogram filling", parse_branch_mode), |
694 | OPT_STRING(0, "objdump", &objdump_path, "path", | 801 | OPT_STRING(0, "objdump", &objdump_path, "path", |
695 | "objdump binary to use for disassembly and annotations"), | 802 | "objdump binary to use for disassembly and annotations"), |
803 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, | ||
804 | "Disable symbol demangling"), | ||
805 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), | ||
696 | OPT_END() | 806 | OPT_END() |
697 | }; | 807 | }; |
698 | 808 | ||
@@ -750,12 +860,24 @@ repeat: | |||
750 | "dso_to,symbol_to"; | 860 | "dso_to,symbol_to"; |
751 | 861 | ||
752 | } | 862 | } |
863 | if (report.mem_mode) { | ||
864 | if (sort__branch_mode == 1) { | ||
865 | fprintf(stderr, "branch and mem mode incompatible\n"); | ||
866 | goto error; | ||
867 | } | ||
868 | /* | ||
869 | * if no sort_order is provided, then specify | ||
870 | * branch-mode specific order | ||
871 | */ | ||
872 | if (sort_order == default_sort_order) | ||
873 | sort_order = "local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked"; | ||
874 | } | ||
753 | 875 | ||
754 | if (setup_sorting() < 0) | 876 | if (setup_sorting() < 0) |
755 | usage_with_options(report_usage, options); | 877 | usage_with_options(report_usage, options); |
756 | 878 | ||
757 | /* | 879 | /* |
758 | * Only in the newt browser we are doing integrated annotation, | 880 | * Only in the TUI browser we are doing integrated annotation, |
759 | * so don't allocate extra space that won't be used in the stdio | 881 | * so don't allocate extra space that won't be used in the stdio |
760 | * implementation. | 882 | * implementation. |
761 | */ | 883 | */ |
@@ -815,6 +937,14 @@ repeat: | |||
815 | sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout); | 937 | sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout); |
816 | sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout); | 938 | sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout); |
817 | } else { | 939 | } else { |
940 | if (report.mem_mode) { | ||
941 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "symbol_daddr", stdout); | ||
942 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso_daddr", stdout); | ||
943 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "mem", stdout); | ||
944 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "local_weight", stdout); | ||
945 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "tlb", stdout); | ||
946 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "snoop", stdout); | ||
947 | } | ||
818 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | 948 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); |
819 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | 949 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); |
820 | } | 950 | } |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 138229439a93..2da2a6ca22bf 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -1671,7 +1671,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1671 | .sample = perf_sched__process_tracepoint_sample, | 1671 | .sample = perf_sched__process_tracepoint_sample, |
1672 | .comm = perf_event__process_comm, | 1672 | .comm = perf_event__process_comm, |
1673 | .lost = perf_event__process_lost, | 1673 | .lost = perf_event__process_lost, |
1674 | .exit = perf_event__process_exit, | ||
1675 | .fork = perf_event__process_fork, | 1674 | .fork = perf_event__process_fork, |
1676 | .ordered_samples = true, | 1675 | .ordered_samples = true, |
1677 | }, | 1676 | }, |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 99848761f573..7e910bab1097 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -68,7 +68,7 @@ | |||
68 | static void print_stat(int argc, const char **argv); | 68 | static void print_stat(int argc, const char **argv); |
69 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix); | 69 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix); |
70 | static void print_counter(struct perf_evsel *counter, char *prefix); | 70 | static void print_counter(struct perf_evsel *counter, char *prefix); |
71 | static void print_aggr_socket(char *prefix); | 71 | static void print_aggr(char *prefix); |
72 | 72 | ||
73 | static struct perf_evlist *evsel_list; | 73 | static struct perf_evlist *evsel_list; |
74 | 74 | ||
@@ -76,11 +76,17 @@ static struct perf_target target = { | |||
76 | .uid = UINT_MAX, | 76 | .uid = UINT_MAX, |
77 | }; | 77 | }; |
78 | 78 | ||
79 | enum aggr_mode { | ||
80 | AGGR_NONE, | ||
81 | AGGR_GLOBAL, | ||
82 | AGGR_SOCKET, | ||
83 | AGGR_CORE, | ||
84 | }; | ||
85 | |||
79 | static int run_count = 1; | 86 | static int run_count = 1; |
80 | static bool no_inherit = false; | 87 | static bool no_inherit = false; |
81 | static bool scale = true; | 88 | static bool scale = true; |
82 | static bool no_aggr = false; | 89 | static enum aggr_mode aggr_mode = AGGR_GLOBAL; |
83 | static bool aggr_socket = false; | ||
84 | static pid_t child_pid = -1; | 90 | static pid_t child_pid = -1; |
85 | static bool null_run = false; | 91 | static bool null_run = false; |
86 | static int detailed_run = 0; | 92 | static int detailed_run = 0; |
@@ -94,8 +100,10 @@ static const char *pre_cmd = NULL; | |||
94 | static const char *post_cmd = NULL; | 100 | static const char *post_cmd = NULL; |
95 | static bool sync_run = false; | 101 | static bool sync_run = false; |
96 | static unsigned int interval = 0; | 102 | static unsigned int interval = 0; |
103 | static bool forever = false; | ||
97 | static struct timespec ref_time; | 104 | static struct timespec ref_time; |
98 | static struct cpu_map *sock_map; | 105 | static struct cpu_map *aggr_map; |
106 | static int (*aggr_get_id)(struct cpu_map *m, int cpu); | ||
99 | 107 | ||
100 | static volatile int done = 0; | 108 | static volatile int done = 0; |
101 | 109 | ||
@@ -125,6 +133,11 @@ static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) | |||
125 | return perf_evsel__cpus(evsel)->nr; | 133 | return perf_evsel__cpus(evsel)->nr; |
126 | } | 134 | } |
127 | 135 | ||
136 | static void perf_evsel__reset_stat_priv(struct perf_evsel *evsel) | ||
137 | { | ||
138 | memset(evsel->priv, 0, sizeof(struct perf_stat)); | ||
139 | } | ||
140 | |||
128 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | 141 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) |
129 | { | 142 | { |
130 | evsel->priv = zalloc(sizeof(struct perf_stat)); | 143 | evsel->priv = zalloc(sizeof(struct perf_stat)); |
@@ -160,6 +173,35 @@ static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) | |||
160 | evsel->prev_raw_counts = NULL; | 173 | evsel->prev_raw_counts = NULL; |
161 | } | 174 | } |
162 | 175 | ||
176 | static void perf_evlist__free_stats(struct perf_evlist *evlist) | ||
177 | { | ||
178 | struct perf_evsel *evsel; | ||
179 | |||
180 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
181 | perf_evsel__free_stat_priv(evsel); | ||
182 | perf_evsel__free_counts(evsel); | ||
183 | perf_evsel__free_prev_raw_counts(evsel); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | static int perf_evlist__alloc_stats(struct perf_evlist *evlist, bool alloc_raw) | ||
188 | { | ||
189 | struct perf_evsel *evsel; | ||
190 | |||
191 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
192 | if (perf_evsel__alloc_stat_priv(evsel) < 0 || | ||
193 | perf_evsel__alloc_counts(evsel, perf_evsel__nr_cpus(evsel)) < 0 || | ||
194 | (alloc_raw && perf_evsel__alloc_prev_raw_counts(evsel) < 0)) | ||
195 | goto out_free; | ||
196 | } | ||
197 | |||
198 | return 0; | ||
199 | |||
200 | out_free: | ||
201 | perf_evlist__free_stats(evlist); | ||
202 | return -1; | ||
203 | } | ||
204 | |||
163 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; | 205 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; |
164 | static struct stats runtime_cycles_stats[MAX_NR_CPUS]; | 206 | static struct stats runtime_cycles_stats[MAX_NR_CPUS]; |
165 | static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS]; | 207 | static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS]; |
@@ -173,6 +215,29 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; | |||
173 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; | 215 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; |
174 | static struct stats walltime_nsecs_stats; | 216 | static struct stats walltime_nsecs_stats; |
175 | 217 | ||
218 | static void perf_stat__reset_stats(struct perf_evlist *evlist) | ||
219 | { | ||
220 | struct perf_evsel *evsel; | ||
221 | |||
222 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
223 | perf_evsel__reset_stat_priv(evsel); | ||
224 | perf_evsel__reset_counts(evsel, perf_evsel__nr_cpus(evsel)); | ||
225 | } | ||
226 | |||
227 | memset(runtime_nsecs_stats, 0, sizeof(runtime_nsecs_stats)); | ||
228 | memset(runtime_cycles_stats, 0, sizeof(runtime_cycles_stats)); | ||
229 | memset(runtime_stalled_cycles_front_stats, 0, sizeof(runtime_stalled_cycles_front_stats)); | ||
230 | memset(runtime_stalled_cycles_back_stats, 0, sizeof(runtime_stalled_cycles_back_stats)); | ||
231 | memset(runtime_branches_stats, 0, sizeof(runtime_branches_stats)); | ||
232 | memset(runtime_cacherefs_stats, 0, sizeof(runtime_cacherefs_stats)); | ||
233 | memset(runtime_l1_dcache_stats, 0, sizeof(runtime_l1_dcache_stats)); | ||
234 | memset(runtime_l1_icache_stats, 0, sizeof(runtime_l1_icache_stats)); | ||
235 | memset(runtime_ll_cache_stats, 0, sizeof(runtime_ll_cache_stats)); | ||
236 | memset(runtime_itlb_cache_stats, 0, sizeof(runtime_itlb_cache_stats)); | ||
237 | memset(runtime_dtlb_cache_stats, 0, sizeof(runtime_dtlb_cache_stats)); | ||
238 | memset(&walltime_nsecs_stats, 0, sizeof(walltime_nsecs_stats)); | ||
239 | } | ||
240 | |||
176 | static int create_perf_stat_counter(struct perf_evsel *evsel) | 241 | static int create_perf_stat_counter(struct perf_evsel *evsel) |
177 | { | 242 | { |
178 | struct perf_event_attr *attr = &evsel->attr; | 243 | struct perf_event_attr *attr = &evsel->attr; |
@@ -249,7 +314,7 @@ static int read_counter_aggr(struct perf_evsel *counter) | |||
249 | int i; | 314 | int i; |
250 | 315 | ||
251 | if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter), | 316 | if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter), |
252 | evsel_list->threads->nr, scale) < 0) | 317 | thread_map__nr(evsel_list->threads), scale) < 0) |
253 | return -1; | 318 | return -1; |
254 | 319 | ||
255 | for (i = 0; i < 3; i++) | 320 | for (i = 0; i < 3; i++) |
@@ -297,56 +362,68 @@ static void print_interval(void) | |||
297 | struct timespec ts, rs; | 362 | struct timespec ts, rs; |
298 | char prefix[64]; | 363 | char prefix[64]; |
299 | 364 | ||
300 | if (no_aggr) { | 365 | if (aggr_mode == AGGR_GLOBAL) { |
301 | list_for_each_entry(counter, &evsel_list->entries, node) { | 366 | list_for_each_entry(counter, &evsel_list->entries, node) { |
302 | ps = counter->priv; | 367 | ps = counter->priv; |
303 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | 368 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); |
304 | read_counter(counter); | 369 | read_counter_aggr(counter); |
305 | } | 370 | } |
306 | } else { | 371 | } else { |
307 | list_for_each_entry(counter, &evsel_list->entries, node) { | 372 | list_for_each_entry(counter, &evsel_list->entries, node) { |
308 | ps = counter->priv; | 373 | ps = counter->priv; |
309 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | 374 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); |
310 | read_counter_aggr(counter); | 375 | read_counter(counter); |
311 | } | 376 | } |
312 | } | 377 | } |
378 | |||
313 | clock_gettime(CLOCK_MONOTONIC, &ts); | 379 | clock_gettime(CLOCK_MONOTONIC, &ts); |
314 | diff_timespec(&rs, &ts, &ref_time); | 380 | diff_timespec(&rs, &ts, &ref_time); |
315 | sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep); | 381 | sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep); |
316 | 382 | ||
317 | if (num_print_interval == 0 && !csv_output) { | 383 | if (num_print_interval == 0 && !csv_output) { |
318 | if (aggr_socket) | 384 | switch (aggr_mode) { |
385 | case AGGR_SOCKET: | ||
319 | fprintf(output, "# time socket cpus counts events\n"); | 386 | fprintf(output, "# time socket cpus counts events\n"); |
320 | else if (no_aggr) | 387 | break; |
388 | case AGGR_CORE: | ||
389 | fprintf(output, "# time core cpus counts events\n"); | ||
390 | break; | ||
391 | case AGGR_NONE: | ||
321 | fprintf(output, "# time CPU counts events\n"); | 392 | fprintf(output, "# time CPU counts events\n"); |
322 | else | 393 | break; |
394 | case AGGR_GLOBAL: | ||
395 | default: | ||
323 | fprintf(output, "# time counts events\n"); | 396 | fprintf(output, "# time counts events\n"); |
397 | } | ||
324 | } | 398 | } |
325 | 399 | ||
326 | if (++num_print_interval == 25) | 400 | if (++num_print_interval == 25) |
327 | num_print_interval = 0; | 401 | num_print_interval = 0; |
328 | 402 | ||
329 | if (aggr_socket) | 403 | switch (aggr_mode) { |
330 | print_aggr_socket(prefix); | 404 | case AGGR_CORE: |
331 | else if (no_aggr) { | 405 | case AGGR_SOCKET: |
406 | print_aggr(prefix); | ||
407 | break; | ||
408 | case AGGR_NONE: | ||
332 | list_for_each_entry(counter, &evsel_list->entries, node) | 409 | list_for_each_entry(counter, &evsel_list->entries, node) |
333 | print_counter(counter, prefix); | 410 | print_counter(counter, prefix); |
334 | } else { | 411 | break; |
412 | case AGGR_GLOBAL: | ||
413 | default: | ||
335 | list_for_each_entry(counter, &evsel_list->entries, node) | 414 | list_for_each_entry(counter, &evsel_list->entries, node) |
336 | print_counter_aggr(counter, prefix); | 415 | print_counter_aggr(counter, prefix); |
337 | } | 416 | } |
338 | } | 417 | } |
339 | 418 | ||
340 | static int __run_perf_stat(int argc __maybe_unused, const char **argv) | 419 | static int __run_perf_stat(int argc, const char **argv) |
341 | { | 420 | { |
342 | char msg[512]; | 421 | char msg[512]; |
343 | unsigned long long t0, t1; | 422 | unsigned long long t0, t1; |
344 | struct perf_evsel *counter; | 423 | struct perf_evsel *counter; |
345 | struct timespec ts; | 424 | struct timespec ts; |
346 | int status = 0; | 425 | int status = 0; |
347 | int child_ready_pipe[2], go_pipe[2]; | ||
348 | const bool forks = (argc > 0); | 426 | const bool forks = (argc > 0); |
349 | char buf; | ||
350 | 427 | ||
351 | if (interval) { | 428 | if (interval) { |
352 | ts.tv_sec = interval / 1000; | 429 | ts.tv_sec = interval / 1000; |
@@ -356,61 +433,12 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) | |||
356 | ts.tv_nsec = 0; | 433 | ts.tv_nsec = 0; |
357 | } | 434 | } |
358 | 435 | ||
359 | if (aggr_socket | ||
360 | && cpu_map__build_socket_map(evsel_list->cpus, &sock_map)) { | ||
361 | perror("cannot build socket map"); | ||
362 | return -1; | ||
363 | } | ||
364 | |||
365 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { | ||
366 | perror("failed to create pipes"); | ||
367 | return -1; | ||
368 | } | ||
369 | |||
370 | if (forks) { | 436 | if (forks) { |
371 | if ((child_pid = fork()) < 0) | 437 | if (perf_evlist__prepare_workload(evsel_list, &target, argv, |
372 | perror("failed to fork"); | 438 | false, false) < 0) { |
373 | 439 | perror("failed to prepare workload"); | |
374 | if (!child_pid) { | 440 | return -1; |
375 | close(child_ready_pipe[0]); | ||
376 | close(go_pipe[1]); | ||
377 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
378 | |||
379 | /* | ||
380 | * Do a dummy execvp to get the PLT entry resolved, | ||
381 | * so we avoid the resolver overhead on the real | ||
382 | * execvp call. | ||
383 | */ | ||
384 | execvp("", (char **)argv); | ||
385 | |||
386 | /* | ||
387 | * Tell the parent we're ready to go | ||
388 | */ | ||
389 | close(child_ready_pipe[1]); | ||
390 | |||
391 | /* | ||
392 | * Wait until the parent tells us to go. | ||
393 | */ | ||
394 | if (read(go_pipe[0], &buf, 1) == -1) | ||
395 | perror("unable to read pipe"); | ||
396 | |||
397 | execvp(argv[0], (char **)argv); | ||
398 | |||
399 | perror(argv[0]); | ||
400 | exit(-1); | ||
401 | } | 441 | } |
402 | |||
403 | if (perf_target__none(&target)) | ||
404 | evsel_list->threads->map[0] = child_pid; | ||
405 | |||
406 | /* | ||
407 | * Wait for the child to be ready to exec. | ||
408 | */ | ||
409 | close(child_ready_pipe[1]); | ||
410 | close(go_pipe[0]); | ||
411 | if (read(child_ready_pipe[0], &buf, 1) == -1) | ||
412 | perror("unable to read pipe"); | ||
413 | close(child_ready_pipe[0]); | ||
414 | } | 442 | } |
415 | 443 | ||
416 | if (group) | 444 | if (group) |
@@ -457,7 +485,8 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) | |||
457 | clock_gettime(CLOCK_MONOTONIC, &ref_time); | 485 | clock_gettime(CLOCK_MONOTONIC, &ref_time); |
458 | 486 | ||
459 | if (forks) { | 487 | if (forks) { |
460 | close(go_pipe[1]); | 488 | perf_evlist__start_workload(evsel_list); |
489 | |||
461 | if (interval) { | 490 | if (interval) { |
462 | while (!waitpid(child_pid, &status, WNOHANG)) { | 491 | while (!waitpid(child_pid, &status, WNOHANG)) { |
463 | nanosleep(&ts, NULL); | 492 | nanosleep(&ts, NULL); |
@@ -479,16 +508,16 @@ static int __run_perf_stat(int argc __maybe_unused, const char **argv) | |||
479 | 508 | ||
480 | update_stats(&walltime_nsecs_stats, t1 - t0); | 509 | update_stats(&walltime_nsecs_stats, t1 - t0); |
481 | 510 | ||
482 | if (no_aggr) { | 511 | if (aggr_mode == AGGR_GLOBAL) { |
483 | list_for_each_entry(counter, &evsel_list->entries, node) { | 512 | list_for_each_entry(counter, &evsel_list->entries, node) { |
484 | read_counter(counter); | 513 | read_counter_aggr(counter); |
485 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); | 514 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), |
515 | thread_map__nr(evsel_list->threads)); | ||
486 | } | 516 | } |
487 | } else { | 517 | } else { |
488 | list_for_each_entry(counter, &evsel_list->entries, node) { | 518 | list_for_each_entry(counter, &evsel_list->entries, node) { |
489 | read_counter_aggr(counter); | 519 | read_counter(counter); |
490 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), | 520 | perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1); |
491 | evsel_list->threads->nr); | ||
492 | } | 521 | } |
493 | } | 522 | } |
494 | 523 | ||
@@ -542,26 +571,47 @@ static void print_noise(struct perf_evsel *evsel, double avg) | |||
542 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); | 571 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); |
543 | } | 572 | } |
544 | 573 | ||
545 | static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | 574 | static void aggr_printout(struct perf_evsel *evsel, int id, int nr) |
546 | { | 575 | { |
547 | double msecs = avg / 1e6; | 576 | switch (aggr_mode) { |
548 | char cpustr[16] = { '\0', }; | 577 | case AGGR_CORE: |
549 | const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s"; | 578 | fprintf(output, "S%d-C%*d%s%*d%s", |
550 | 579 | cpu_map__id_to_socket(id), | |
551 | if (aggr_socket) | 580 | csv_output ? 0 : -8, |
552 | sprintf(cpustr, "S%*d%s%*d%s", | 581 | cpu_map__id_to_cpu(id), |
582 | csv_sep, | ||
583 | csv_output ? 0 : 4, | ||
584 | nr, | ||
585 | csv_sep); | ||
586 | break; | ||
587 | case AGGR_SOCKET: | ||
588 | fprintf(output, "S%*d%s%*d%s", | ||
553 | csv_output ? 0 : -5, | 589 | csv_output ? 0 : -5, |
554 | cpu, | 590 | id, |
555 | csv_sep, | 591 | csv_sep, |
556 | csv_output ? 0 : 4, | 592 | csv_output ? 0 : 4, |
557 | nr, | 593 | nr, |
558 | csv_sep); | 594 | csv_sep); |
559 | else if (no_aggr) | 595 | break; |
560 | sprintf(cpustr, "CPU%*d%s", | 596 | case AGGR_NONE: |
597 | fprintf(output, "CPU%*d%s", | ||
561 | csv_output ? 0 : -4, | 598 | csv_output ? 0 : -4, |
562 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); | 599 | perf_evsel__cpus(evsel)->map[id], csv_sep); |
600 | break; | ||
601 | case AGGR_GLOBAL: | ||
602 | default: | ||
603 | break; | ||
604 | } | ||
605 | } | ||
606 | |||
607 | static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | ||
608 | { | ||
609 | double msecs = avg / 1e6; | ||
610 | const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s"; | ||
611 | |||
612 | aggr_printout(evsel, cpu, nr); | ||
563 | 613 | ||
564 | fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel)); | 614 | fprintf(output, fmt, msecs, csv_sep, perf_evsel__name(evsel)); |
565 | 615 | ||
566 | if (evsel->cgrp) | 616 | if (evsel->cgrp) |
567 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 617 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
@@ -758,32 +808,21 @@ static void print_ll_cache_misses(int cpu, | |||
758 | static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | 808 | static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) |
759 | { | 809 | { |
760 | double total, ratio = 0.0; | 810 | double total, ratio = 0.0; |
761 | char cpustr[16] = { '\0', }; | ||
762 | const char *fmt; | 811 | const char *fmt; |
763 | 812 | ||
764 | if (csv_output) | 813 | if (csv_output) |
765 | fmt = "%s%.0f%s%s"; | 814 | fmt = "%.0f%s%s"; |
766 | else if (big_num) | 815 | else if (big_num) |
767 | fmt = "%s%'18.0f%s%-25s"; | 816 | fmt = "%'18.0f%s%-25s"; |
768 | else | 817 | else |
769 | fmt = "%s%18.0f%s%-25s"; | 818 | fmt = "%18.0f%s%-25s"; |
770 | 819 | ||
771 | if (aggr_socket) | 820 | aggr_printout(evsel, cpu, nr); |
772 | sprintf(cpustr, "S%*d%s%*d%s", | 821 | |
773 | csv_output ? 0 : -5, | 822 | if (aggr_mode == AGGR_GLOBAL) |
774 | cpu, | ||
775 | csv_sep, | ||
776 | csv_output ? 0 : 4, | ||
777 | nr, | ||
778 | csv_sep); | ||
779 | else if (no_aggr) | ||
780 | sprintf(cpustr, "CPU%*d%s", | ||
781 | csv_output ? 0 : -4, | ||
782 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); | ||
783 | else | ||
784 | cpu = 0; | 823 | cpu = 0; |
785 | 824 | ||
786 | fprintf(output, fmt, cpustr, avg, csv_sep, perf_evsel__name(evsel)); | 825 | fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel)); |
787 | 826 | ||
788 | if (evsel->cgrp) | 827 | if (evsel->cgrp) |
789 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 828 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
@@ -882,23 +921,23 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | |||
882 | } | 921 | } |
883 | } | 922 | } |
884 | 923 | ||
885 | static void print_aggr_socket(char *prefix) | 924 | static void print_aggr(char *prefix) |
886 | { | 925 | { |
887 | struct perf_evsel *counter; | 926 | struct perf_evsel *counter; |
927 | int cpu, s, s2, id, nr; | ||
888 | u64 ena, run, val; | 928 | u64 ena, run, val; |
889 | int cpu, s, s2, sock, nr; | ||
890 | 929 | ||
891 | if (!sock_map) | 930 | if (!(aggr_map || aggr_get_id)) |
892 | return; | 931 | return; |
893 | 932 | ||
894 | for (s = 0; s < sock_map->nr; s++) { | 933 | for (s = 0; s < aggr_map->nr; s++) { |
895 | sock = cpu_map__socket(sock_map, s); | 934 | id = aggr_map->map[s]; |
896 | list_for_each_entry(counter, &evsel_list->entries, node) { | 935 | list_for_each_entry(counter, &evsel_list->entries, node) { |
897 | val = ena = run = 0; | 936 | val = ena = run = 0; |
898 | nr = 0; | 937 | nr = 0; |
899 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | 938 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { |
900 | s2 = cpu_map__get_socket(evsel_list->cpus, cpu); | 939 | s2 = aggr_get_id(evsel_list->cpus, cpu); |
901 | if (s2 != sock) | 940 | if (s2 != id) |
902 | continue; | 941 | continue; |
903 | val += counter->counts->cpu[cpu].val; | 942 | val += counter->counts->cpu[cpu].val; |
904 | ena += counter->counts->cpu[cpu].ena; | 943 | ena += counter->counts->cpu[cpu].ena; |
@@ -909,18 +948,15 @@ static void print_aggr_socket(char *prefix) | |||
909 | fprintf(output, "%s", prefix); | 948 | fprintf(output, "%s", prefix); |
910 | 949 | ||
911 | if (run == 0 || ena == 0) { | 950 | if (run == 0 || ena == 0) { |
912 | fprintf(output, "S%*d%s%*d%s%*s%s%*s", | 951 | aggr_printout(counter, cpu, nr); |
913 | csv_output ? 0 : -5, | 952 | |
914 | s, | 953 | fprintf(output, "%*s%s%*s", |
915 | csv_sep, | ||
916 | csv_output ? 0 : 4, | ||
917 | nr, | ||
918 | csv_sep, | ||
919 | csv_output ? 0 : 18, | 954 | csv_output ? 0 : 18, |
920 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | 955 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, |
921 | csv_sep, | 956 | csv_sep, |
922 | csv_output ? 0 : -24, | 957 | csv_output ? 0 : -24, |
923 | perf_evsel__name(counter)); | 958 | perf_evsel__name(counter)); |
959 | |||
924 | if (counter->cgrp) | 960 | if (counter->cgrp) |
925 | fprintf(output, "%s%s", | 961 | fprintf(output, "%s%s", |
926 | csv_sep, counter->cgrp->name); | 962 | csv_sep, counter->cgrp->name); |
@@ -930,9 +966,9 @@ static void print_aggr_socket(char *prefix) | |||
930 | } | 966 | } |
931 | 967 | ||
932 | if (nsec_counter(counter)) | 968 | if (nsec_counter(counter)) |
933 | nsec_printout(sock, nr, counter, val); | 969 | nsec_printout(id, nr, counter, val); |
934 | else | 970 | else |
935 | abs_printout(sock, nr, counter, val); | 971 | abs_printout(id, nr, counter, val); |
936 | 972 | ||
937 | if (!csv_output) { | 973 | if (!csv_output) { |
938 | print_noise(counter, 1.0); | 974 | print_noise(counter, 1.0); |
@@ -1073,14 +1109,21 @@ static void print_stat(int argc, const char **argv) | |||
1073 | fprintf(output, ":\n\n"); | 1109 | fprintf(output, ":\n\n"); |
1074 | } | 1110 | } |
1075 | 1111 | ||
1076 | if (aggr_socket) | 1112 | switch (aggr_mode) { |
1077 | print_aggr_socket(NULL); | 1113 | case AGGR_CORE: |
1078 | else if (no_aggr) { | 1114 | case AGGR_SOCKET: |
1079 | list_for_each_entry(counter, &evsel_list->entries, node) | 1115 | print_aggr(NULL); |
1080 | print_counter(counter, NULL); | 1116 | break; |
1081 | } else { | 1117 | case AGGR_GLOBAL: |
1082 | list_for_each_entry(counter, &evsel_list->entries, node) | 1118 | list_for_each_entry(counter, &evsel_list->entries, node) |
1083 | print_counter_aggr(counter, NULL); | 1119 | print_counter_aggr(counter, NULL); |
1120 | break; | ||
1121 | case AGGR_NONE: | ||
1122 | list_for_each_entry(counter, &evsel_list->entries, node) | ||
1123 | print_counter(counter, NULL); | ||
1124 | break; | ||
1125 | default: | ||
1126 | break; | ||
1084 | } | 1127 | } |
1085 | 1128 | ||
1086 | if (!csv_output) { | 1129 | if (!csv_output) { |
@@ -1126,6 +1169,32 @@ static int stat__set_big_num(const struct option *opt __maybe_unused, | |||
1126 | return 0; | 1169 | return 0; |
1127 | } | 1170 | } |
1128 | 1171 | ||
1172 | static int perf_stat_init_aggr_mode(void) | ||
1173 | { | ||
1174 | switch (aggr_mode) { | ||
1175 | case AGGR_SOCKET: | ||
1176 | if (cpu_map__build_socket_map(evsel_list->cpus, &aggr_map)) { | ||
1177 | perror("cannot build socket map"); | ||
1178 | return -1; | ||
1179 | } | ||
1180 | aggr_get_id = cpu_map__get_socket; | ||
1181 | break; | ||
1182 | case AGGR_CORE: | ||
1183 | if (cpu_map__build_core_map(evsel_list->cpus, &aggr_map)) { | ||
1184 | perror("cannot build core map"); | ||
1185 | return -1; | ||
1186 | } | ||
1187 | aggr_get_id = cpu_map__get_core; | ||
1188 | break; | ||
1189 | case AGGR_NONE: | ||
1190 | case AGGR_GLOBAL: | ||
1191 | default: | ||
1192 | break; | ||
1193 | } | ||
1194 | return 0; | ||
1195 | } | ||
1196 | |||
1197 | |||
1129 | /* | 1198 | /* |
1130 | * Add default attributes, if there were no attributes specified or | 1199 | * Add default attributes, if there were no attributes specified or |
1131 | * if -d/--detailed, -d -d or -d -d -d is used: | 1200 | * if -d/--detailed, -d -d or -d -d -d is used: |
@@ -1296,7 +1365,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1296 | OPT_INCR('v', "verbose", &verbose, | 1365 | OPT_INCR('v', "verbose", &verbose, |
1297 | "be more verbose (show counter open errors, etc)"), | 1366 | "be more verbose (show counter open errors, etc)"), |
1298 | OPT_INTEGER('r', "repeat", &run_count, | 1367 | OPT_INTEGER('r', "repeat", &run_count, |
1299 | "repeat command and print average + stddev (max: 100)"), | 1368 | "repeat command and print average + stddev (max: 100, forever: 0)"), |
1300 | OPT_BOOLEAN('n', "null", &null_run, | 1369 | OPT_BOOLEAN('n', "null", &null_run, |
1301 | "null run - dont start any counters"), | 1370 | "null run - dont start any counters"), |
1302 | OPT_INCR('d', "detailed", &detailed_run, | 1371 | OPT_INCR('d', "detailed", &detailed_run, |
@@ -1308,7 +1377,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1308 | stat__set_big_num), | 1377 | stat__set_big_num), |
1309 | OPT_STRING('C', "cpu", &target.cpu_list, "cpu", | 1378 | OPT_STRING('C', "cpu", &target.cpu_list, "cpu", |
1310 | "list of cpus to monitor in system-wide"), | 1379 | "list of cpus to monitor in system-wide"), |
1311 | OPT_BOOLEAN('A', "no-aggr", &no_aggr, "disable CPU count aggregation"), | 1380 | OPT_SET_UINT('A', "no-aggr", &aggr_mode, |
1381 | "disable CPU count aggregation", AGGR_NONE), | ||
1312 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | 1382 | OPT_STRING('x', "field-separator", &csv_sep, "separator", |
1313 | "print counts with custom separator"), | 1383 | "print counts with custom separator"), |
1314 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | 1384 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
@@ -1323,20 +1393,22 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1323 | "command to run after to the measured command"), | 1393 | "command to run after to the measured command"), |
1324 | OPT_UINTEGER('I', "interval-print", &interval, | 1394 | OPT_UINTEGER('I', "interval-print", &interval, |
1325 | "print counts at regular interval in ms (>= 100)"), | 1395 | "print counts at regular interval in ms (>= 100)"), |
1326 | OPT_BOOLEAN(0, "aggr-socket", &aggr_socket, "aggregate counts per processor socket"), | 1396 | OPT_SET_UINT(0, "per-socket", &aggr_mode, |
1397 | "aggregate counts per processor socket", AGGR_SOCKET), | ||
1398 | OPT_SET_UINT(0, "per-core", &aggr_mode, | ||
1399 | "aggregate counts per physical processor core", AGGR_CORE), | ||
1327 | OPT_END() | 1400 | OPT_END() |
1328 | }; | 1401 | }; |
1329 | const char * const stat_usage[] = { | 1402 | const char * const stat_usage[] = { |
1330 | "perf stat [<options>] [<command>]", | 1403 | "perf stat [<options>] [<command>]", |
1331 | NULL | 1404 | NULL |
1332 | }; | 1405 | }; |
1333 | struct perf_evsel *pos; | ||
1334 | int status = -ENOMEM, run_idx; | 1406 | int status = -ENOMEM, run_idx; |
1335 | const char *mode; | 1407 | const char *mode; |
1336 | 1408 | ||
1337 | setlocale(LC_ALL, ""); | 1409 | setlocale(LC_ALL, ""); |
1338 | 1410 | ||
1339 | evsel_list = perf_evlist__new(NULL, NULL); | 1411 | evsel_list = perf_evlist__new(); |
1340 | if (evsel_list == NULL) | 1412 | if (evsel_list == NULL) |
1341 | return -ENOMEM; | 1413 | return -ENOMEM; |
1342 | 1414 | ||
@@ -1399,23 +1471,21 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1399 | 1471 | ||
1400 | if (!argc && !perf_target__has_task(&target)) | 1472 | if (!argc && !perf_target__has_task(&target)) |
1401 | usage_with_options(stat_usage, options); | 1473 | usage_with_options(stat_usage, options); |
1402 | if (run_count <= 0) | 1474 | if (run_count < 0) { |
1403 | usage_with_options(stat_usage, options); | 1475 | usage_with_options(stat_usage, options); |
1476 | } else if (run_count == 0) { | ||
1477 | forever = true; | ||
1478 | run_count = 1; | ||
1479 | } | ||
1404 | 1480 | ||
1405 | /* no_aggr, cgroup are for system-wide only */ | 1481 | /* no_aggr, cgroup are for system-wide only */ |
1406 | if ((no_aggr || nr_cgroups) && !perf_target__has_cpu(&target)) { | 1482 | if ((aggr_mode != AGGR_GLOBAL || nr_cgroups) |
1483 | && !perf_target__has_cpu(&target)) { | ||
1407 | fprintf(stderr, "both cgroup and no-aggregation " | 1484 | fprintf(stderr, "both cgroup and no-aggregation " |
1408 | "modes only available in system-wide mode\n"); | 1485 | "modes only available in system-wide mode\n"); |
1409 | 1486 | ||
1410 | usage_with_options(stat_usage, options); | 1487 | usage_with_options(stat_usage, options); |
1411 | } | 1488 | return -1; |
1412 | |||
1413 | if (aggr_socket) { | ||
1414 | if (!perf_target__has_cpu(&target)) { | ||
1415 | fprintf(stderr, "--aggr-socket only available in system-wide mode (-a)\n"); | ||
1416 | usage_with_options(stat_usage, options); | ||
1417 | } | ||
1418 | no_aggr = true; | ||
1419 | } | 1489 | } |
1420 | 1490 | ||
1421 | if (add_default_attributes()) | 1491 | if (add_default_attributes()) |
@@ -1438,17 +1508,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1438 | return -1; | 1508 | return -1; |
1439 | } | 1509 | } |
1440 | 1510 | ||
1441 | list_for_each_entry(pos, &evsel_list->entries, node) { | 1511 | if (perf_evlist__alloc_stats(evsel_list, interval)) |
1442 | if (perf_evsel__alloc_stat_priv(pos) < 0 || | 1512 | goto out_free_maps; |
1443 | perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0) | 1513 | |
1444 | goto out_free_fd; | 1514 | if (perf_stat_init_aggr_mode()) |
1445 | } | 1515 | goto out; |
1446 | if (interval) { | ||
1447 | list_for_each_entry(pos, &evsel_list->entries, node) { | ||
1448 | if (perf_evsel__alloc_prev_raw_counts(pos) < 0) | ||
1449 | goto out_free_fd; | ||
1450 | } | ||
1451 | } | ||
1452 | 1516 | ||
1453 | /* | 1517 | /* |
1454 | * We dont want to block the signals - that would cause | 1518 | * We dont want to block the signals - that would cause |
@@ -1457,28 +1521,30 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1457 | * task, but being ignored by perf stat itself: | 1521 | * task, but being ignored by perf stat itself: |
1458 | */ | 1522 | */ |
1459 | atexit(sig_atexit); | 1523 | atexit(sig_atexit); |
1460 | signal(SIGINT, skip_signal); | 1524 | if (!forever) |
1525 | signal(SIGINT, skip_signal); | ||
1461 | signal(SIGCHLD, skip_signal); | 1526 | signal(SIGCHLD, skip_signal); |
1462 | signal(SIGALRM, skip_signal); | 1527 | signal(SIGALRM, skip_signal); |
1463 | signal(SIGABRT, skip_signal); | 1528 | signal(SIGABRT, skip_signal); |
1464 | 1529 | ||
1465 | status = 0; | 1530 | status = 0; |
1466 | for (run_idx = 0; run_idx < run_count; run_idx++) { | 1531 | for (run_idx = 0; forever || run_idx < run_count; run_idx++) { |
1467 | if (run_count != 1 && verbose) | 1532 | if (run_count != 1 && verbose) |
1468 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", | 1533 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
1469 | run_idx + 1); | 1534 | run_idx + 1); |
1470 | 1535 | ||
1471 | status = run_perf_stat(argc, argv); | 1536 | status = run_perf_stat(argc, argv); |
1537 | if (forever && status != -1) { | ||
1538 | print_stat(argc, argv); | ||
1539 | perf_stat__reset_stats(evsel_list); | ||
1540 | } | ||
1472 | } | 1541 | } |
1473 | 1542 | ||
1474 | if (status != -1 && !interval) | 1543 | if (!forever && status != -1 && !interval) |
1475 | print_stat(argc, argv); | 1544 | print_stat(argc, argv); |
1476 | out_free_fd: | 1545 | |
1477 | list_for_each_entry(pos, &evsel_list->entries, node) { | 1546 | perf_evlist__free_stats(evsel_list); |
1478 | perf_evsel__free_stat_priv(pos); | 1547 | out_free_maps: |
1479 | perf_evsel__free_counts(pos); | ||
1480 | perf_evsel__free_prev_raw_counts(pos); | ||
1481 | } | ||
1482 | perf_evlist__delete_maps(evsel_list); | 1548 | perf_evlist__delete_maps(evsel_list); |
1483 | out: | 1549 | out: |
1484 | perf_evlist__delete(evsel_list); | 1550 | perf_evlist__delete(evsel_list); |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 72f6eb7b4173..67bdb9f14ad6 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -231,7 +231,7 @@ static void perf_top__show_details(struct perf_top *top) | |||
231 | printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name); | 231 | printf("Showing %s for %s\n", perf_evsel__name(top->sym_evsel), symbol->name); |
232 | printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter); | 232 | printf(" Events Pcnt (>=%d%%)\n", top->sym_pcnt_filter); |
233 | 233 | ||
234 | more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx, | 234 | more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel, |
235 | 0, top->sym_pcnt_filter, top->print_entries, 4); | 235 | 0, top->sym_pcnt_filter, top->print_entries, 4); |
236 | if (top->zero) | 236 | if (top->zero) |
237 | symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx); | 237 | symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx); |
@@ -251,7 +251,8 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
251 | { | 251 | { |
252 | struct hist_entry *he; | 252 | struct hist_entry *he; |
253 | 253 | ||
254 | he = __hists__add_entry(&evsel->hists, al, NULL, sample->period); | 254 | he = __hists__add_entry(&evsel->hists, al, NULL, sample->period, |
255 | sample->weight); | ||
255 | if (he == NULL) | 256 | if (he == NULL) |
256 | return NULL; | 257 | return NULL; |
257 | 258 | ||
@@ -1088,7 +1089,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1088 | OPT_INCR('v', "verbose", &verbose, | 1089 | OPT_INCR('v', "verbose", &verbose, |
1089 | "be more verbose (show counter open errors, etc)"), | 1090 | "be more verbose (show counter open errors, etc)"), |
1090 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1091 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1091 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1092 | "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"), |
1092 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | 1093 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
1093 | "Show a column with the number of samples"), | 1094 | "Show a column with the number of samples"), |
1094 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, | 1095 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, |
@@ -1116,7 +1117,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1116 | NULL | 1117 | NULL |
1117 | }; | 1118 | }; |
1118 | 1119 | ||
1119 | top.evlist = perf_evlist__new(NULL, NULL); | 1120 | top.evlist = perf_evlist__new(); |
1120 | if (top.evlist == NULL) | 1121 | if (top.evlist == NULL) |
1121 | return -ENOMEM; | 1122 | return -ENOMEM; |
1122 | 1123 | ||
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d222d7fc7e96..ab3ed4af1466 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -419,7 +419,7 @@ out_dump: | |||
419 | 419 | ||
420 | static int trace__run(struct trace *trace, int argc, const char **argv) | 420 | static int trace__run(struct trace *trace, int argc, const char **argv) |
421 | { | 421 | { |
422 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 422 | struct perf_evlist *evlist = perf_evlist__new(); |
423 | struct perf_evsel *evsel; | 423 | struct perf_evsel *evsel; |
424 | int err = -1, i; | 424 | int err = -1, i; |
425 | unsigned long before; | 425 | unsigned long before; |
@@ -452,7 +452,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
452 | err = trace__symbols_init(trace, evlist); | 452 | err = trace__symbols_init(trace, evlist); |
453 | if (err < 0) { | 453 | if (err < 0) { |
454 | printf("Problems initializing symbol libraries!\n"); | 454 | printf("Problems initializing symbol libraries!\n"); |
455 | goto out_delete_evlist; | 455 | goto out_delete_maps; |
456 | } | 456 | } |
457 | 457 | ||
458 | perf_evlist__config(evlist, &trace->opts); | 458 | perf_evlist__config(evlist, &trace->opts); |
@@ -461,23 +461,24 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
461 | signal(SIGINT, sig_handler); | 461 | signal(SIGINT, sig_handler); |
462 | 462 | ||
463 | if (forks) { | 463 | if (forks) { |
464 | err = perf_evlist__prepare_workload(evlist, &trace->opts, argv); | 464 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, |
465 | argv, false, false); | ||
465 | if (err < 0) { | 466 | if (err < 0) { |
466 | printf("Couldn't run the workload!\n"); | 467 | printf("Couldn't run the workload!\n"); |
467 | goto out_delete_evlist; | 468 | goto out_delete_maps; |
468 | } | 469 | } |
469 | } | 470 | } |
470 | 471 | ||
471 | err = perf_evlist__open(evlist); | 472 | err = perf_evlist__open(evlist); |
472 | if (err < 0) { | 473 | if (err < 0) { |
473 | printf("Couldn't create the events: %s\n", strerror(errno)); | 474 | printf("Couldn't create the events: %s\n", strerror(errno)); |
474 | goto out_delete_evlist; | 475 | goto out_delete_maps; |
475 | } | 476 | } |
476 | 477 | ||
477 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 478 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
478 | if (err < 0) { | 479 | if (err < 0) { |
479 | printf("Couldn't mmap the events: %s\n", strerror(errno)); | 480 | printf("Couldn't mmap the events: %s\n", strerror(errno)); |
480 | goto out_delete_evlist; | 481 | goto out_close_evlist; |
481 | } | 482 | } |
482 | 483 | ||
483 | perf_evlist__enable(evlist); | 484 | perf_evlist__enable(evlist); |
@@ -526,13 +527,6 @@ again: | |||
526 | continue; | 527 | continue; |
527 | } | 528 | } |
528 | 529 | ||
529 | if (sample.raw_data == NULL) { | ||
530 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | ||
531 | perf_evsel__name(evsel), sample.tid, | ||
532 | sample.cpu, sample.raw_size); | ||
533 | continue; | ||
534 | } | ||
535 | |||
536 | handler = evsel->handler.func; | 530 | handler = evsel->handler.func; |
537 | handler(trace, evsel, &sample); | 531 | handler(trace, evsel, &sample); |
538 | } | 532 | } |
@@ -540,7 +534,7 @@ again: | |||
540 | 534 | ||
541 | if (trace->nr_events == before) { | 535 | if (trace->nr_events == before) { |
542 | if (done) | 536 | if (done) |
543 | goto out_delete_evlist; | 537 | goto out_unmap_evlist; |
544 | 538 | ||
545 | poll(evlist->pollfd, evlist->nr_fds, -1); | 539 | poll(evlist->pollfd, evlist->nr_fds, -1); |
546 | } | 540 | } |
@@ -550,6 +544,12 @@ again: | |||
550 | 544 | ||
551 | goto again; | 545 | goto again; |
552 | 546 | ||
547 | out_unmap_evlist: | ||
548 | perf_evlist__munmap(evlist); | ||
549 | out_close_evlist: | ||
550 | perf_evlist__close(evlist); | ||
551 | out_delete_maps: | ||
552 | perf_evlist__delete_maps(evlist); | ||
553 | out_delete_evlist: | 553 | out_delete_evlist: |
554 | perf_evlist__delete(evlist); | 554 | perf_evlist__delete(evlist); |
555 | out: | 555 | out: |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 08143bd854c7..b210d62907e4 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -36,6 +36,7 @@ extern int cmd_kvm(int argc, const char **argv, const char *prefix); | |||
36 | extern int cmd_test(int argc, const char **argv, const char *prefix); | 36 | extern int cmd_test(int argc, const char **argv, const char *prefix); |
37 | extern int cmd_trace(int argc, const char **argv, const char *prefix); | 37 | extern int cmd_trace(int argc, const char **argv, const char *prefix); |
38 | extern int cmd_inject(int argc, const char **argv, const char *prefix); | 38 | extern int cmd_inject(int argc, const char **argv, const char *prefix); |
39 | extern int cmd_mem(int argc, const char **argv, const char *prefix); | ||
39 | 40 | ||
40 | extern int find_scripts(char **scripts_array, char **scripts_path_array); | 41 | extern int find_scripts(char **scripts_array, char **scripts_path_array); |
41 | #endif | 42 | #endif |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 3e86bbd8c2d5..0906fc401c52 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -10,17 +10,18 @@ perf-buildid-list mainporcelain common | |||
10 | perf-diff mainporcelain common | 10 | perf-diff mainporcelain common |
11 | perf-evlist mainporcelain common | 11 | perf-evlist mainporcelain common |
12 | perf-inject mainporcelain common | 12 | perf-inject mainporcelain common |
13 | perf-kmem mainporcelain common | ||
14 | perf-kvm mainporcelain common | ||
13 | perf-list mainporcelain common | 15 | perf-list mainporcelain common |
14 | perf-sched mainporcelain common | 16 | perf-lock mainporcelain common |
17 | perf-mem mainporcelain common | ||
18 | perf-probe mainporcelain full | ||
15 | perf-record mainporcelain common | 19 | perf-record mainporcelain common |
16 | perf-report mainporcelain common | 20 | perf-report mainporcelain common |
21 | perf-sched mainporcelain common | ||
22 | perf-script mainporcelain common | ||
17 | perf-stat mainporcelain common | 23 | perf-stat mainporcelain common |
24 | perf-test mainporcelain common | ||
18 | perf-timechart mainporcelain common | 25 | perf-timechart mainporcelain common |
19 | perf-top mainporcelain common | 26 | perf-top mainporcelain common |
20 | perf-trace mainporcelain common | 27 | perf-trace mainporcelain common |
21 | perf-script mainporcelain common | ||
22 | perf-probe mainporcelain full | ||
23 | perf-kmem mainporcelain common | ||
24 | perf-lock mainporcelain common | ||
25 | perf-kvm mainporcelain common | ||
26 | perf-test mainporcelain common | ||
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index b4eabb44e381..708fb8e9822a 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak | |||
@@ -61,15 +61,13 @@ int main(void) | |||
61 | } | 61 | } |
62 | endef | 62 | endef |
63 | 63 | ||
64 | ifndef NO_NEWT | 64 | ifndef NO_SLANG |
65 | define SOURCE_NEWT | 65 | define SOURCE_SLANG |
66 | #include <newt.h> | 66 | #include <slang.h> |
67 | 67 | ||
68 | int main(void) | 68 | int main(void) |
69 | { | 69 | { |
70 | newtInit(); | 70 | return SLsmg_init_smg(); |
71 | newtCls(); | ||
72 | return newtFinished(); | ||
73 | } | 71 | } |
74 | endef | 72 | endef |
75 | endif | 73 | endif |
@@ -235,4 +233,4 @@ int main(void) | |||
235 | numa_available(); | 233 | numa_available(); |
236 | return 0; | 234 | return 0; |
237 | } | 235 | } |
238 | endef \ No newline at end of file | 236 | endef |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 095b88207cd3..85e1aed95204 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -13,7 +13,7 @@ | |||
13 | #include "util/quote.h" | 13 | #include "util/quote.h" |
14 | #include "util/run-command.h" | 14 | #include "util/run-command.h" |
15 | #include "util/parse-events.h" | 15 | #include "util/parse-events.h" |
16 | #include "util/debugfs.h" | 16 | #include <lk/debugfs.h> |
17 | #include <pthread.h> | 17 | #include <pthread.h> |
18 | 18 | ||
19 | const char perf_usage_string[] = | 19 | const char perf_usage_string[] = |
@@ -60,6 +60,7 @@ static struct cmd_struct commands[] = { | |||
60 | { "trace", cmd_trace, 0 }, | 60 | { "trace", cmd_trace, 0 }, |
61 | #endif | 61 | #endif |
62 | { "inject", cmd_inject, 0 }, | 62 | { "inject", cmd_inject, 0 }, |
63 | { "mem", cmd_mem, 0 }, | ||
63 | }; | 64 | }; |
64 | 65 | ||
65 | struct pager_config { | 66 | struct pager_config { |
@@ -193,13 +194,13 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) | |||
193 | fprintf(stderr, "No directory given for --debugfs-dir.\n"); | 194 | fprintf(stderr, "No directory given for --debugfs-dir.\n"); |
194 | usage(perf_usage_string); | 195 | usage(perf_usage_string); |
195 | } | 196 | } |
196 | debugfs_set_path((*argv)[1]); | 197 | perf_debugfs_set_path((*argv)[1]); |
197 | if (envchanged) | 198 | if (envchanged) |
198 | *envchanged = 1; | 199 | *envchanged = 1; |
199 | (*argv)++; | 200 | (*argv)++; |
200 | (*argc)--; | 201 | (*argc)--; |
201 | } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { | 202 | } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { |
202 | debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR)); | 203 | perf_debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR)); |
203 | fprintf(stderr, "dir: %s\n", debugfs_mountpoint); | 204 | fprintf(stderr, "dir: %s\n", debugfs_mountpoint); |
204 | if (envchanged) | 205 | if (envchanged) |
205 | *envchanged = 1; | 206 | *envchanged = 1; |
@@ -461,7 +462,7 @@ int main(int argc, const char **argv) | |||
461 | if (!cmd) | 462 | if (!cmd) |
462 | cmd = "perf-help"; | 463 | cmd = "perf-help"; |
463 | /* get debugfs mount point from /proc/mounts */ | 464 | /* get debugfs mount point from /proc/mounts */ |
464 | debugfs_mount(NULL); | 465 | perf_debugfs_mount(NULL); |
465 | /* | 466 | /* |
466 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: | 467 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: |
467 | * | 468 | * |
@@ -517,9 +518,8 @@ int main(int argc, const char **argv) | |||
517 | 518 | ||
518 | while (1) { | 519 | while (1) { |
519 | static int done_help; | 520 | static int done_help; |
520 | static int was_alias; | 521 | int was_alias = run_argv(&argc, &argv); |
521 | 522 | ||
522 | was_alias = run_argv(&argc, &argv); | ||
523 | if (errno != ENOENT) | 523 | if (errno != ENOENT) |
524 | break; | 524 | break; |
525 | 525 | ||
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 74659ecf93e0..32bd102c32b6 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -218,6 +218,7 @@ struct perf_record_opts { | |||
218 | bool pipe_output; | 218 | bool pipe_output; |
219 | bool raw_samples; | 219 | bool raw_samples; |
220 | bool sample_address; | 220 | bool sample_address; |
221 | bool sample_weight; | ||
221 | bool sample_time; | 222 | bool sample_time; |
222 | bool period; | 223 | bool period; |
223 | unsigned int freq; | 224 | unsigned int freq; |
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index bdcceb886f77..038de3ecb8cb 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c | |||
@@ -147,10 +147,15 @@ void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | |||
147 | 147 | ||
148 | static int run_dir(const char *d, const char *perf) | 148 | static int run_dir(const char *d, const char *perf) |
149 | { | 149 | { |
150 | char v[] = "-vvvvv"; | ||
151 | int vcnt = min(verbose, (int) sizeof(v) - 1); | ||
150 | char cmd[3*PATH_MAX]; | 152 | char cmd[3*PATH_MAX]; |
151 | 153 | ||
152 | snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %s", | 154 | if (verbose) |
153 | d, d, perf, verbose ? "-v" : ""); | 155 | vcnt++; |
156 | |||
157 | snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %.*s", | ||
158 | d, d, perf, vcnt, v); | ||
154 | 159 | ||
155 | return system(cmd); | 160 | return system(cmd); |
156 | } | 161 | } |
diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/attr.py index 2f629ca485bc..c9b4b6269b51 100644 --- a/tools/perf/tests/attr.py +++ b/tools/perf/tests/attr.py | |||
@@ -24,6 +24,7 @@ class Unsup(Exception): | |||
24 | 24 | ||
25 | class Event(dict): | 25 | class Event(dict): |
26 | terms = [ | 26 | terms = [ |
27 | 'cpu', | ||
27 | 'flags', | 28 | 'flags', |
28 | 'type', | 29 | 'type', |
29 | 'size', | 30 | 'size', |
@@ -121,7 +122,7 @@ class Test(object): | |||
121 | parser = ConfigParser.SafeConfigParser() | 122 | parser = ConfigParser.SafeConfigParser() |
122 | parser.read(path) | 123 | parser.read(path) |
123 | 124 | ||
124 | log.debug("running '%s'" % path) | 125 | log.warning("running '%s'" % path) |
125 | 126 | ||
126 | self.path = path | 127 | self.path = path |
127 | self.test_dir = options.test_dir | 128 | self.test_dir = options.test_dir |
@@ -172,7 +173,7 @@ class Test(object): | |||
172 | self.perf, self.command, tempdir, self.args) | 173 | self.perf, self.command, tempdir, self.args) |
173 | ret = os.WEXITSTATUS(os.system(cmd)) | 174 | ret = os.WEXITSTATUS(os.system(cmd)) |
174 | 175 | ||
175 | log.warning(" running '%s' ret %d " % (cmd, ret)) | 176 | log.info(" '%s' ret %d " % (cmd, ret)) |
176 | 177 | ||
177 | if ret != int(self.ret): | 178 | if ret != int(self.ret): |
178 | raise Unsup(self) | 179 | raise Unsup(self) |
diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/attr/base-record index 5bc3880f7be5..b4fc835de607 100644 --- a/tools/perf/tests/attr/base-record +++ b/tools/perf/tests/attr/base-record | |||
@@ -2,6 +2,7 @@ | |||
2 | fd=1 | 2 | fd=1 |
3 | group_fd=-1 | 3 | group_fd=-1 |
4 | flags=0 | 4 | flags=0 |
5 | cpu=* | ||
5 | type=0|1 | 6 | type=0|1 |
6 | size=96 | 7 | size=96 |
7 | config=0 | 8 | config=0 |
diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/attr/base-stat index 4bd79a82784f..748ee949a204 100644 --- a/tools/perf/tests/attr/base-stat +++ b/tools/perf/tests/attr/base-stat | |||
@@ -2,6 +2,7 @@ | |||
2 | fd=1 | 2 | fd=1 |
3 | group_fd=-1 | 3 | group_fd=-1 |
4 | flags=0 | 4 | flags=0 |
5 | cpu=* | ||
5 | type=0 | 6 | type=0 |
6 | size=96 | 7 | size=96 |
7 | config=0 | 8 | config=0 |
diff --git a/tools/perf/tests/attr/test-record-C0 b/tools/perf/tests/attr/test-record-C0 new file mode 100644 index 000000000000..d6a7e43f61b3 --- /dev/null +++ b/tools/perf/tests/attr/test-record-C0 | |||
@@ -0,0 +1,13 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -C 0 kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | cpu=0 | ||
7 | |||
8 | # no enable on exec for CPU attached | ||
9 | enable_on_exec=0 | ||
10 | |||
11 | # PERF_SAMPLE_IP | PERF_SAMPLE_TID PERF_SAMPLE_TIME | # PERF_SAMPLE_PERIOD | ||
12 | # + PERF_SAMPLE_CPU added by -C 0 | ||
13 | sample_type=391 | ||
diff --git a/tools/perf/tests/attr/test-stat-C0 b/tools/perf/tests/attr/test-stat-C0 new file mode 100644 index 000000000000..aa835950751f --- /dev/null +++ b/tools/perf/tests/attr/test-stat-C0 | |||
@@ -0,0 +1,9 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -e cycles -C 0 kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event:base-stat] | ||
7 | # events are enabled by default when attached to cpu | ||
8 | disabled=0 | ||
9 | enable_on_exec=0 | ||
diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c new file mode 100644 index 000000000000..68daa289e94c --- /dev/null +++ b/tools/perf/tests/bp_signal.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* | ||
2 | * Inspired by breakpoint overflow test done by | ||
3 | * Vince Weaver <vincent.weaver@maine.edu> for perf_event_tests | ||
4 | * (git://github.com/deater/perf_event_tests) | ||
5 | */ | ||
6 | |||
7 | #include <stdlib.h> | ||
8 | #include <stdio.h> | ||
9 | #include <unistd.h> | ||
10 | #include <string.h> | ||
11 | #include <sys/ioctl.h> | ||
12 | #include <time.h> | ||
13 | #include <fcntl.h> | ||
14 | #include <signal.h> | ||
15 | #include <sys/mman.h> | ||
16 | #include <linux/compiler.h> | ||
17 | #include <linux/hw_breakpoint.h> | ||
18 | |||
19 | #include "tests.h" | ||
20 | #include "debug.h" | ||
21 | #include "perf.h" | ||
22 | |||
23 | static int fd1; | ||
24 | static int fd2; | ||
25 | static int overflows; | ||
26 | |||
27 | __attribute__ ((noinline)) | ||
28 | static int test_function(void) | ||
29 | { | ||
30 | return time(NULL); | ||
31 | } | ||
32 | |||
33 | static void sig_handler(int signum __maybe_unused, | ||
34 | siginfo_t *oh __maybe_unused, | ||
35 | void *uc __maybe_unused) | ||
36 | { | ||
37 | overflows++; | ||
38 | |||
39 | if (overflows > 10) { | ||
40 | /* | ||
41 | * This should be executed only once during | ||
42 | * this test, if we are here for the 10th | ||
43 | * time, consider this the recursive issue. | ||
44 | * | ||
45 | * We can get out of here by disable events, | ||
46 | * so no new SIGIO is delivered. | ||
47 | */ | ||
48 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); | ||
49 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); | ||
50 | } | ||
51 | } | ||
52 | |||
53 | static int bp_event(void *fn, int setup_signal) | ||
54 | { | ||
55 | struct perf_event_attr pe; | ||
56 | int fd; | ||
57 | |||
58 | memset(&pe, 0, sizeof(struct perf_event_attr)); | ||
59 | pe.type = PERF_TYPE_BREAKPOINT; | ||
60 | pe.size = sizeof(struct perf_event_attr); | ||
61 | |||
62 | pe.config = 0; | ||
63 | pe.bp_type = HW_BREAKPOINT_X; | ||
64 | pe.bp_addr = (unsigned long) fn; | ||
65 | pe.bp_len = sizeof(long); | ||
66 | |||
67 | pe.sample_period = 1; | ||
68 | pe.sample_type = PERF_SAMPLE_IP; | ||
69 | pe.wakeup_events = 1; | ||
70 | |||
71 | pe.disabled = 1; | ||
72 | pe.exclude_kernel = 1; | ||
73 | pe.exclude_hv = 1; | ||
74 | |||
75 | fd = sys_perf_event_open(&pe, 0, -1, -1, 0); | ||
76 | if (fd < 0) { | ||
77 | pr_debug("failed opening event %llx\n", pe.config); | ||
78 | return TEST_FAIL; | ||
79 | } | ||
80 | |||
81 | if (setup_signal) { | ||
82 | fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); | ||
83 | fcntl(fd, F_SETSIG, SIGIO); | ||
84 | fcntl(fd, F_SETOWN, getpid()); | ||
85 | } | ||
86 | |||
87 | ioctl(fd, PERF_EVENT_IOC_RESET, 0); | ||
88 | |||
89 | return fd; | ||
90 | } | ||
91 | |||
92 | static long long bp_count(int fd) | ||
93 | { | ||
94 | long long count; | ||
95 | int ret; | ||
96 | |||
97 | ret = read(fd, &count, sizeof(long long)); | ||
98 | if (ret != sizeof(long long)) { | ||
99 | pr_debug("failed to read: %d\n", ret); | ||
100 | return TEST_FAIL; | ||
101 | } | ||
102 | |||
103 | return count; | ||
104 | } | ||
105 | |||
106 | int test__bp_signal(void) | ||
107 | { | ||
108 | struct sigaction sa; | ||
109 | long long count1, count2; | ||
110 | |||
111 | /* setup SIGIO signal handler */ | ||
112 | memset(&sa, 0, sizeof(struct sigaction)); | ||
113 | sa.sa_sigaction = (void *) sig_handler; | ||
114 | sa.sa_flags = SA_SIGINFO; | ||
115 | |||
116 | if (sigaction(SIGIO, &sa, NULL) < 0) { | ||
117 | pr_debug("failed setting up signal handler\n"); | ||
118 | return TEST_FAIL; | ||
119 | } | ||
120 | |||
121 | /* | ||
122 | * We create following events: | ||
123 | * | ||
124 | * fd1 - breakpoint event on test_function with SIGIO | ||
125 | * signal configured. We should get signal | ||
126 | * notification each time the breakpoint is hit | ||
127 | * | ||
128 | * fd2 - breakpoint event on sig_handler without SIGIO | ||
129 | * configured. | ||
130 | * | ||
131 | * Following processing should happen: | ||
132 | * - execute test_function | ||
133 | * - fd1 event breakpoint hit -> count1 == 1 | ||
134 | * - SIGIO is delivered -> overflows == 1 | ||
135 | * - fd2 event breakpoint hit -> count2 == 1 | ||
136 | * | ||
137 | * The test case check following error conditions: | ||
138 | * - we get stuck in signal handler because of debug | ||
139 | * exception being triggered receursively due to | ||
140 | * the wrong RF EFLAG management | ||
141 | * | ||
142 | * - we never trigger the sig_handler breakpoint due | ||
143 | * to the rong RF EFLAG management | ||
144 | * | ||
145 | */ | ||
146 | |||
147 | fd1 = bp_event(test_function, 1); | ||
148 | fd2 = bp_event(sig_handler, 0); | ||
149 | |||
150 | ioctl(fd1, PERF_EVENT_IOC_ENABLE, 0); | ||
151 | ioctl(fd2, PERF_EVENT_IOC_ENABLE, 0); | ||
152 | |||
153 | /* | ||
154 | * Kick off the test by trigering 'fd1' | ||
155 | * breakpoint. | ||
156 | */ | ||
157 | test_function(); | ||
158 | |||
159 | ioctl(fd1, PERF_EVENT_IOC_DISABLE, 0); | ||
160 | ioctl(fd2, PERF_EVENT_IOC_DISABLE, 0); | ||
161 | |||
162 | count1 = bp_count(fd1); | ||
163 | count2 = bp_count(fd2); | ||
164 | |||
165 | close(fd1); | ||
166 | close(fd2); | ||
167 | |||
168 | pr_debug("count1 %lld, count2 %lld, overflow %d\n", | ||
169 | count1, count2, overflows); | ||
170 | |||
171 | if (count1 != 1) { | ||
172 | if (count1 == 11) | ||
173 | pr_debug("failed: RF EFLAG recursion issue detected\n"); | ||
174 | else | ||
175 | pr_debug("failed: wrong count for bp1%lld\n", count1); | ||
176 | } | ||
177 | |||
178 | if (overflows != 1) | ||
179 | pr_debug("failed: wrong overflow hit\n"); | ||
180 | |||
181 | if (count2 != 1) | ||
182 | pr_debug("failed: wrong count for bp2\n"); | ||
183 | |||
184 | return count1 == 1 && overflows == 1 && count2 == 1 ? | ||
185 | TEST_OK : TEST_FAIL; | ||
186 | } | ||
diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c new file mode 100644 index 000000000000..fe7ed28815f8 --- /dev/null +++ b/tools/perf/tests/bp_signal_overflow.c | |||
@@ -0,0 +1,126 @@ | |||
1 | /* | ||
2 | * Originally done by Vince Weaver <vincent.weaver@maine.edu> for | ||
3 | * perf_event_tests (git://github.com/deater/perf_event_tests) | ||
4 | */ | ||
5 | |||
6 | #include <stdlib.h> | ||
7 | #include <stdio.h> | ||
8 | #include <unistd.h> | ||
9 | #include <string.h> | ||
10 | #include <sys/ioctl.h> | ||
11 | #include <time.h> | ||
12 | #include <fcntl.h> | ||
13 | #include <signal.h> | ||
14 | #include <sys/mman.h> | ||
15 | #include <linux/compiler.h> | ||
16 | #include <linux/hw_breakpoint.h> | ||
17 | |||
18 | #include "tests.h" | ||
19 | #include "debug.h" | ||
20 | #include "perf.h" | ||
21 | |||
22 | static int overflows; | ||
23 | |||
24 | __attribute__ ((noinline)) | ||
25 | static int test_function(void) | ||
26 | { | ||
27 | return time(NULL); | ||
28 | } | ||
29 | |||
30 | static void sig_handler(int signum __maybe_unused, | ||
31 | siginfo_t *oh __maybe_unused, | ||
32 | void *uc __maybe_unused) | ||
33 | { | ||
34 | overflows++; | ||
35 | } | ||
36 | |||
37 | static long long bp_count(int fd) | ||
38 | { | ||
39 | long long count; | ||
40 | int ret; | ||
41 | |||
42 | ret = read(fd, &count, sizeof(long long)); | ||
43 | if (ret != sizeof(long long)) { | ||
44 | pr_debug("failed to read: %d\n", ret); | ||
45 | return TEST_FAIL; | ||
46 | } | ||
47 | |||
48 | return count; | ||
49 | } | ||
50 | |||
51 | #define EXECUTIONS 10000 | ||
52 | #define THRESHOLD 100 | ||
53 | |||
54 | int test__bp_signal_overflow(void) | ||
55 | { | ||
56 | struct perf_event_attr pe; | ||
57 | struct sigaction sa; | ||
58 | long long count; | ||
59 | int fd, i, fails = 0; | ||
60 | |||
61 | /* setup SIGIO signal handler */ | ||
62 | memset(&sa, 0, sizeof(struct sigaction)); | ||
63 | sa.sa_sigaction = (void *) sig_handler; | ||
64 | sa.sa_flags = SA_SIGINFO; | ||
65 | |||
66 | if (sigaction(SIGIO, &sa, NULL) < 0) { | ||
67 | pr_debug("failed setting up signal handler\n"); | ||
68 | return TEST_FAIL; | ||
69 | } | ||
70 | |||
71 | memset(&pe, 0, sizeof(struct perf_event_attr)); | ||
72 | pe.type = PERF_TYPE_BREAKPOINT; | ||
73 | pe.size = sizeof(struct perf_event_attr); | ||
74 | |||
75 | pe.config = 0; | ||
76 | pe.bp_type = HW_BREAKPOINT_X; | ||
77 | pe.bp_addr = (unsigned long) test_function; | ||
78 | pe.bp_len = sizeof(long); | ||
79 | |||
80 | pe.sample_period = THRESHOLD; | ||
81 | pe.sample_type = PERF_SAMPLE_IP; | ||
82 | pe.wakeup_events = 1; | ||
83 | |||
84 | pe.disabled = 1; | ||
85 | pe.exclude_kernel = 1; | ||
86 | pe.exclude_hv = 1; | ||
87 | |||
88 | fd = sys_perf_event_open(&pe, 0, -1, -1, 0); | ||
89 | if (fd < 0) { | ||
90 | pr_debug("failed opening event %llx\n", pe.config); | ||
91 | return TEST_FAIL; | ||
92 | } | ||
93 | |||
94 | fcntl(fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC); | ||
95 | fcntl(fd, F_SETSIG, SIGIO); | ||
96 | fcntl(fd, F_SETOWN, getpid()); | ||
97 | |||
98 | ioctl(fd, PERF_EVENT_IOC_RESET, 0); | ||
99 | ioctl(fd, PERF_EVENT_IOC_ENABLE, 0); | ||
100 | |||
101 | for (i = 0; i < EXECUTIONS; i++) | ||
102 | test_function(); | ||
103 | |||
104 | ioctl(fd, PERF_EVENT_IOC_DISABLE, 0); | ||
105 | |||
106 | count = bp_count(fd); | ||
107 | |||
108 | close(fd); | ||
109 | |||
110 | pr_debug("count %lld, overflow %d\n", | ||
111 | count, overflows); | ||
112 | |||
113 | if (count != EXECUTIONS) { | ||
114 | pr_debug("\tWrong number of executions %lld != %d\n", | ||
115 | count, EXECUTIONS); | ||
116 | fails++; | ||
117 | } | ||
118 | |||
119 | if (overflows != EXECUTIONS / THRESHOLD) { | ||
120 | pr_debug("\tWrong number of overflows %d != %d\n", | ||
121 | overflows, EXECUTIONS / THRESHOLD); | ||
122 | fails++; | ||
123 | } | ||
124 | |||
125 | return fails ? TEST_FAIL : TEST_OK; | ||
126 | } | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index acb98e0e39f2..0918ada4cc41 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -78,6 +78,22 @@ static struct test { | |||
78 | .func = test__python_use, | 78 | .func = test__python_use, |
79 | }, | 79 | }, |
80 | { | 80 | { |
81 | .desc = "Test breakpoint overflow signal handler", | ||
82 | .func = test__bp_signal, | ||
83 | }, | ||
84 | { | ||
85 | .desc = "Test breakpoint overflow sampling", | ||
86 | .func = test__bp_signal_overflow, | ||
87 | }, | ||
88 | { | ||
89 | .desc = "Test number of exit event of a simple workload", | ||
90 | .func = test__task_exit, | ||
91 | }, | ||
92 | { | ||
93 | .desc = "Test software clock events have valid period values", | ||
94 | .func = test__sw_clock_freq, | ||
95 | }, | ||
96 | { | ||
81 | .func = NULL, | 97 | .func = NULL, |
82 | }, | 98 | }, |
83 | }; | 99 | }; |
diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 0fd99a9adb91..0197bda9c461 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c | |||
@@ -8,7 +8,7 @@ static int perf_evsel__roundtrip_cache_name_test(void) | |||
8 | char name[128]; | 8 | char name[128]; |
9 | int type, op, err = 0, ret = 0, i, idx; | 9 | int type, op, err = 0, ret = 0, i, idx; |
10 | struct perf_evsel *evsel; | 10 | struct perf_evsel *evsel; |
11 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 11 | struct perf_evlist *evlist = perf_evlist__new(); |
12 | 12 | ||
13 | if (evlist == NULL) | 13 | if (evlist == NULL) |
14 | return -ENOMEM; | 14 | return -ENOMEM; |
@@ -64,7 +64,7 @@ static int __perf_evsel__name_array_test(const char *names[], int nr_names) | |||
64 | { | 64 | { |
65 | int i, err; | 65 | int i, err; |
66 | struct perf_evsel *evsel; | 66 | struct perf_evsel *evsel; |
67 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 67 | struct perf_evlist *evlist = perf_evlist__new(); |
68 | 68 | ||
69 | if (evlist == NULL) | 69 | if (evlist == NULL) |
70 | return -ENOMEM; | 70 | return -ENOMEM; |
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 1be64a6c5daf..89085a9615e2 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
@@ -223,7 +223,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
223 | &sample, 0) < 0) | 223 | &sample, 0) < 0) |
224 | goto out; | 224 | goto out; |
225 | 225 | ||
226 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1); | 226 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); |
227 | if (he == NULL) | 227 | if (he == NULL) |
228 | goto out; | 228 | goto out; |
229 | 229 | ||
@@ -247,7 +247,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
247 | &sample, 0) < 0) | 247 | &sample, 0) < 0) |
248 | goto out; | 248 | goto out; |
249 | 249 | ||
250 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1); | 250 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); |
251 | if (he == NULL) | 251 | if (he == NULL) |
252 | goto out; | 252 | goto out; |
253 | 253 | ||
@@ -436,7 +436,7 @@ int test__hists_link(void) | |||
436 | struct machines machines; | 436 | struct machines machines; |
437 | struct machine *machine = NULL; | 437 | struct machine *machine = NULL; |
438 | struct perf_evsel *evsel, *first; | 438 | struct perf_evsel *evsel, *first; |
439 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 439 | struct perf_evlist *evlist = perf_evlist__new(); |
440 | 440 | ||
441 | if (evlist == NULL) | 441 | if (evlist == NULL) |
442 | return -ENOMEM; | 442 | return -ENOMEM; |
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index cdd50755af51..5b1b5aba722b 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c | |||
@@ -53,12 +53,14 @@ int test__basic_mmap(void) | |||
53 | goto out_free_cpus; | 53 | goto out_free_cpus; |
54 | } | 54 | } |
55 | 55 | ||
56 | evlist = perf_evlist__new(cpus, threads); | 56 | evlist = perf_evlist__new(); |
57 | if (evlist == NULL) { | 57 | if (evlist == NULL) { |
58 | pr_debug("perf_evlist__new\n"); | 58 | pr_debug("perf_evlist__new\n"); |
59 | goto out_free_cpus; | 59 | goto out_free_cpus; |
60 | } | 60 | } |
61 | 61 | ||
62 | perf_evlist__set_maps(evlist, cpus, threads); | ||
63 | |||
62 | for (i = 0; i < nsyscalls; ++i) { | 64 | for (i = 0; i < nsyscalls; ++i) { |
63 | char name[64]; | 65 | char name[64]; |
64 | 66 | ||
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 1c52fdc1164e..fc5b9fca8b47 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c | |||
@@ -18,7 +18,7 @@ int test__syscall_open_tp_fields(void) | |||
18 | }; | 18 | }; |
19 | const char *filename = "/etc/passwd"; | 19 | const char *filename = "/etc/passwd"; |
20 | int flags = O_RDONLY | O_DIRECTORY; | 20 | int flags = O_RDONLY | O_DIRECTORY; |
21 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 21 | struct perf_evlist *evlist = perf_evlist__new(); |
22 | struct perf_evsel *evsel; | 22 | struct perf_evsel *evsel; |
23 | int err = -1, i, nr_events = 0, nr_polls = 0; | 23 | int err = -1, i, nr_events = 0, nr_polls = 0; |
24 | 24 | ||
@@ -48,13 +48,13 @@ int test__syscall_open_tp_fields(void) | |||
48 | err = perf_evlist__open(evlist); | 48 | err = perf_evlist__open(evlist); |
49 | if (err < 0) { | 49 | if (err < 0) { |
50 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | 50 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); |
51 | goto out_delete_evlist; | 51 | goto out_delete_maps; |
52 | } | 52 | } |
53 | 53 | ||
54 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 54 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
55 | if (err < 0) { | 55 | if (err < 0) { |
56 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | 56 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); |
57 | goto out_delete_evlist; | 57 | goto out_close_evlist; |
58 | } | 58 | } |
59 | 59 | ||
60 | perf_evlist__enable(evlist); | 60 | perf_evlist__enable(evlist); |
@@ -110,6 +110,10 @@ out_ok: | |||
110 | err = 0; | 110 | err = 0; |
111 | out_munmap: | 111 | out_munmap: |
112 | perf_evlist__munmap(evlist); | 112 | perf_evlist__munmap(evlist); |
113 | out_close_evlist: | ||
114 | perf_evlist__close(evlist); | ||
115 | out_delete_maps: | ||
116 | perf_evlist__delete_maps(evlist); | ||
113 | out_delete_evlist: | 117 | out_delete_evlist: |
114 | perf_evlist__delete(evlist); | 118 | perf_evlist__delete(evlist); |
115 | out: | 119 | out: |
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index c5636f36fe31..88e2f44cb157 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -3,7 +3,7 @@ | |||
3 | #include "evsel.h" | 3 | #include "evsel.h" |
4 | #include "evlist.h" | 4 | #include "evlist.h" |
5 | #include "sysfs.h" | 5 | #include "sysfs.h" |
6 | #include "debugfs.h" | 6 | #include <lk/debugfs.h> |
7 | #include "tests.h" | 7 | #include "tests.h" |
8 | #include <linux/hw_breakpoint.h> | 8 | #include <linux/hw_breakpoint.h> |
9 | 9 | ||
@@ -1218,7 +1218,7 @@ static int test_event(struct evlist_test *e) | |||
1218 | struct perf_evlist *evlist; | 1218 | struct perf_evlist *evlist; |
1219 | int ret; | 1219 | int ret; |
1220 | 1220 | ||
1221 | evlist = perf_evlist__new(NULL, NULL); | 1221 | evlist = perf_evlist__new(); |
1222 | if (evlist == NULL) | 1222 | if (evlist == NULL) |
1223 | return -ENOMEM; | 1223 | return -ENOMEM; |
1224 | 1224 | ||
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 1e8e5128d0da..72d8881873b0 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c | |||
@@ -45,7 +45,7 @@ int test__PERF_RECORD(void) | |||
45 | }; | 45 | }; |
46 | cpu_set_t cpu_mask; | 46 | cpu_set_t cpu_mask; |
47 | size_t cpu_mask_size = sizeof(cpu_mask); | 47 | size_t cpu_mask_size = sizeof(cpu_mask); |
48 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 48 | struct perf_evlist *evlist = perf_evlist__new(); |
49 | struct perf_evsel *evsel; | 49 | struct perf_evsel *evsel; |
50 | struct perf_sample sample; | 50 | struct perf_sample sample; |
51 | const char *cmd = "sleep"; | 51 | const char *cmd = "sleep"; |
@@ -93,7 +93,8 @@ int test__PERF_RECORD(void) | |||
93 | * so that we have time to open the evlist (calling sys_perf_event_open | 93 | * so that we have time to open the evlist (calling sys_perf_event_open |
94 | * on all the fds) and then mmap them. | 94 | * on all the fds) and then mmap them. |
95 | */ | 95 | */ |
96 | err = perf_evlist__prepare_workload(evlist, &opts, argv); | 96 | err = perf_evlist__prepare_workload(evlist, &opts.target, argv, |
97 | false, false); | ||
97 | if (err < 0) { | 98 | if (err < 0) { |
98 | pr_debug("Couldn't run the workload!\n"); | 99 | pr_debug("Couldn't run the workload!\n"); |
99 | goto out_delete_maps; | 100 | goto out_delete_maps; |
@@ -142,7 +143,7 @@ int test__PERF_RECORD(void) | |||
142 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | 143 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); |
143 | if (err < 0) { | 144 | if (err < 0) { |
144 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | 145 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); |
145 | goto out_delete_maps; | 146 | goto out_close_evlist; |
146 | } | 147 | } |
147 | 148 | ||
148 | /* | 149 | /* |
@@ -305,6 +306,8 @@ found_exit: | |||
305 | } | 306 | } |
306 | out_err: | 307 | out_err: |
307 | perf_evlist__munmap(evlist); | 308 | perf_evlist__munmap(evlist); |
309 | out_close_evlist: | ||
310 | perf_evlist__close(evlist); | ||
308 | out_delete_maps: | 311 | out_delete_maps: |
309 | perf_evlist__delete_maps(evlist); | 312 | perf_evlist__delete_maps(evlist); |
310 | out_delete_evlist: | 313 | out_delete_evlist: |
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c new file mode 100644 index 000000000000..2e41e2d32ccc --- /dev/null +++ b/tools/perf/tests/sw-clock.c | |||
@@ -0,0 +1,119 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <signal.h> | ||
4 | #include <sys/mman.h> | ||
5 | |||
6 | #include "tests.h" | ||
7 | #include "util/evsel.h" | ||
8 | #include "util/evlist.h" | ||
9 | #include "util/cpumap.h" | ||
10 | #include "util/thread_map.h" | ||
11 | |||
12 | #define NR_LOOPS 1000000 | ||
13 | |||
14 | /* | ||
15 | * This test will open software clock events (cpu-clock, task-clock) | ||
16 | * then check their frequency -> period conversion has no artifact of | ||
17 | * setting period to 1 forcefully. | ||
18 | */ | ||
19 | static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | ||
20 | { | ||
21 | int i, err = -1; | ||
22 | volatile int tmp = 0; | ||
23 | u64 total_periods = 0; | ||
24 | int nr_samples = 0; | ||
25 | union perf_event *event; | ||
26 | struct perf_evsel *evsel; | ||
27 | struct perf_evlist *evlist; | ||
28 | struct perf_event_attr attr = { | ||
29 | .type = PERF_TYPE_SOFTWARE, | ||
30 | .config = clock_id, | ||
31 | .sample_type = PERF_SAMPLE_PERIOD, | ||
32 | .exclude_kernel = 1, | ||
33 | .disabled = 1, | ||
34 | .freq = 1, | ||
35 | }; | ||
36 | |||
37 | attr.sample_freq = 10000; | ||
38 | |||
39 | evlist = perf_evlist__new(); | ||
40 | if (evlist == NULL) { | ||
41 | pr_debug("perf_evlist__new\n"); | ||
42 | return -1; | ||
43 | } | ||
44 | |||
45 | evsel = perf_evsel__new(&attr, 0); | ||
46 | if (evsel == NULL) { | ||
47 | pr_debug("perf_evsel__new\n"); | ||
48 | goto out_free_evlist; | ||
49 | } | ||
50 | perf_evlist__add(evlist, evsel); | ||
51 | |||
52 | evlist->cpus = cpu_map__dummy_new(); | ||
53 | evlist->threads = thread_map__new_by_tid(getpid()); | ||
54 | if (!evlist->cpus || !evlist->threads) { | ||
55 | err = -ENOMEM; | ||
56 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
57 | goto out_delete_maps; | ||
58 | } | ||
59 | |||
60 | perf_evlist__open(evlist); | ||
61 | |||
62 | err = perf_evlist__mmap(evlist, 128, true); | ||
63 | if (err < 0) { | ||
64 | pr_debug("failed to mmap event: %d (%s)\n", errno, | ||
65 | strerror(errno)); | ||
66 | goto out_close_evlist; | ||
67 | } | ||
68 | |||
69 | perf_evlist__enable(evlist); | ||
70 | |||
71 | /* collect samples */ | ||
72 | for (i = 0; i < NR_LOOPS; i++) | ||
73 | tmp++; | ||
74 | |||
75 | perf_evlist__disable(evlist); | ||
76 | |||
77 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | ||
78 | struct perf_sample sample; | ||
79 | |||
80 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
81 | continue; | ||
82 | |||
83 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
84 | if (err < 0) { | ||
85 | pr_debug("Error during parse sample\n"); | ||
86 | goto out_unmap_evlist; | ||
87 | } | ||
88 | |||
89 | total_periods += sample.period; | ||
90 | nr_samples++; | ||
91 | } | ||
92 | |||
93 | if ((u64) nr_samples == total_periods) { | ||
94 | pr_debug("All (%d) samples have period value of 1!\n", | ||
95 | nr_samples); | ||
96 | err = -1; | ||
97 | } | ||
98 | |||
99 | out_unmap_evlist: | ||
100 | perf_evlist__munmap(evlist); | ||
101 | out_close_evlist: | ||
102 | perf_evlist__close(evlist); | ||
103 | out_delete_maps: | ||
104 | perf_evlist__delete_maps(evlist); | ||
105 | out_free_evlist: | ||
106 | perf_evlist__delete(evlist); | ||
107 | return err; | ||
108 | } | ||
109 | |||
110 | int test__sw_clock_freq(void) | ||
111 | { | ||
112 | int ret; | ||
113 | |||
114 | ret = __test__sw_clock_freq(PERF_COUNT_SW_CPU_CLOCK); | ||
115 | if (!ret) | ||
116 | ret = __test__sw_clock_freq(PERF_COUNT_SW_TASK_CLOCK); | ||
117 | |||
118 | return ret; | ||
119 | } | ||
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c new file mode 100644 index 000000000000..28fe5894b061 --- /dev/null +++ b/tools/perf/tests/task-exit.c | |||
@@ -0,0 +1,123 @@ | |||
1 | #include "evlist.h" | ||
2 | #include "evsel.h" | ||
3 | #include "thread_map.h" | ||
4 | #include "cpumap.h" | ||
5 | #include "tests.h" | ||
6 | |||
7 | #include <signal.h> | ||
8 | |||
9 | static int exited; | ||
10 | static int nr_exit; | ||
11 | |||
12 | static void sig_handler(int sig) | ||
13 | { | ||
14 | exited = 1; | ||
15 | |||
16 | if (sig == SIGUSR1) | ||
17 | nr_exit = -1; | ||
18 | } | ||
19 | |||
20 | /* | ||
21 | * This test will start a workload that does nothing then it checks | ||
22 | * if the number of exit event reported by the kernel is 1 or not | ||
23 | * in order to check the kernel returns correct number of event. | ||
24 | */ | ||
25 | int test__task_exit(void) | ||
26 | { | ||
27 | int err = -1; | ||
28 | union perf_event *event; | ||
29 | struct perf_evsel *evsel; | ||
30 | struct perf_evlist *evlist; | ||
31 | struct perf_target target = { | ||
32 | .uid = UINT_MAX, | ||
33 | .uses_mmap = true, | ||
34 | }; | ||
35 | const char *argv[] = { "true", NULL }; | ||
36 | |||
37 | signal(SIGCHLD, sig_handler); | ||
38 | signal(SIGUSR1, sig_handler); | ||
39 | |||
40 | evlist = perf_evlist__new(); | ||
41 | if (evlist == NULL) { | ||
42 | pr_debug("perf_evlist__new\n"); | ||
43 | return -1; | ||
44 | } | ||
45 | /* | ||
46 | * We need at least one evsel in the evlist, use the default | ||
47 | * one: "cycles". | ||
48 | */ | ||
49 | err = perf_evlist__add_default(evlist); | ||
50 | if (err < 0) { | ||
51 | pr_debug("Not enough memory to create evsel\n"); | ||
52 | goto out_free_evlist; | ||
53 | } | ||
54 | |||
55 | /* | ||
56 | * Create maps of threads and cpus to monitor. In this case | ||
57 | * we start with all threads and cpus (-1, -1) but then in | ||
58 | * perf_evlist__prepare_workload we'll fill in the only thread | ||
59 | * we're monitoring, the one forked there. | ||
60 | */ | ||
61 | evlist->cpus = cpu_map__dummy_new(); | ||
62 | evlist->threads = thread_map__new_by_tid(-1); | ||
63 | if (!evlist->cpus || !evlist->threads) { | ||
64 | err = -ENOMEM; | ||
65 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
66 | goto out_delete_maps; | ||
67 | } | ||
68 | |||
69 | err = perf_evlist__prepare_workload(evlist, &target, argv, false, true); | ||
70 | if (err < 0) { | ||
71 | pr_debug("Couldn't run the workload!\n"); | ||
72 | goto out_delete_maps; | ||
73 | } | ||
74 | |||
75 | evsel = perf_evlist__first(evlist); | ||
76 | evsel->attr.task = 1; | ||
77 | evsel->attr.sample_freq = 0; | ||
78 | evsel->attr.inherit = 0; | ||
79 | evsel->attr.watermark = 0; | ||
80 | evsel->attr.wakeup_events = 1; | ||
81 | evsel->attr.exclude_kernel = 1; | ||
82 | |||
83 | err = perf_evlist__open(evlist); | ||
84 | if (err < 0) { | ||
85 | pr_debug("Couldn't open the evlist: %s\n", strerror(-err)); | ||
86 | goto out_delete_maps; | ||
87 | } | ||
88 | |||
89 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | ||
90 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
91 | strerror(errno)); | ||
92 | goto out_close_evlist; | ||
93 | } | ||
94 | |||
95 | perf_evlist__start_workload(evlist); | ||
96 | |||
97 | retry: | ||
98 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | ||
99 | if (event->header.type != PERF_RECORD_EXIT) | ||
100 | continue; | ||
101 | |||
102 | nr_exit++; | ||
103 | } | ||
104 | |||
105 | if (!exited || !nr_exit) { | ||
106 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
107 | goto retry; | ||
108 | } | ||
109 | |||
110 | if (nr_exit != 1) { | ||
111 | pr_debug("received %d EXIT records\n", nr_exit); | ||
112 | err = -1; | ||
113 | } | ||
114 | |||
115 | perf_evlist__munmap(evlist); | ||
116 | out_close_evlist: | ||
117 | perf_evlist__close(evlist); | ||
118 | out_delete_maps: | ||
119 | perf_evlist__delete_maps(evlist); | ||
120 | out_free_evlist: | ||
121 | perf_evlist__delete(evlist); | ||
122 | return err; | ||
123 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 5de0be1ff4b6..dd7feae2d37b 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -23,5 +23,9 @@ int test__dso_data(void); | |||
23 | int test__parse_events(void); | 23 | int test__parse_events(void); |
24 | int test__hists_link(void); | 24 | int test__hists_link(void); |
25 | int test__python_use(void); | 25 | int test__python_use(void); |
26 | int test__bp_signal(void); | ||
27 | int test__bp_signal_overflow(void); | ||
28 | int test__task_exit(void); | ||
29 | int test__sw_clock_freq(void); | ||
26 | 30 | ||
27 | #endif /* TESTS_H */ | 31 | #endif /* TESTS_H */ |
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 809ea4632a34..bbc782e364b0 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c | |||
@@ -2,7 +2,6 @@ | |||
2 | #include "../cache.h" | 2 | #include "../cache.h" |
3 | #include "../../perf.h" | 3 | #include "../../perf.h" |
4 | #include "libslang.h" | 4 | #include "libslang.h" |
5 | #include <newt.h> | ||
6 | #include "ui.h" | 5 | #include "ui.h" |
7 | #include "util.h" | 6 | #include "util.h" |
8 | #include <linux/compiler.h> | 7 | #include <linux/compiler.h> |
@@ -234,7 +233,7 @@ void ui_browser__reset_index(struct ui_browser *browser) | |||
234 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) | 233 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) |
235 | { | 234 | { |
236 | SLsmg_gotorc(0, 0); | 235 | SLsmg_gotorc(0, 0); |
237 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | 236 | ui_browser__set_color(browser, HE_COLORSET_ROOT); |
238 | slsmg_write_nstring(title, browser->width + 1); | 237 | slsmg_write_nstring(title, browser->width + 1); |
239 | } | 238 | } |
240 | 239 | ||
@@ -514,6 +513,12 @@ static struct ui_browser_colorset { | |||
514 | .bg = "default", | 513 | .bg = "default", |
515 | }, | 514 | }, |
516 | { | 515 | { |
516 | .colorset = HE_COLORSET_ROOT, | ||
517 | .name = "root", | ||
518 | .fg = "white", | ||
519 | .bg = "blue", | ||
520 | }, | ||
521 | { | ||
517 | .name = NULL, | 522 | .name = NULL, |
518 | } | 523 | } |
519 | }; | 524 | }; |
diff --git a/tools/perf/ui/browser.h b/tools/perf/ui/browser.h index af70314605e5..404ff66a3e36 100644 --- a/tools/perf/ui/browser.h +++ b/tools/perf/ui/browser.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #define HE_COLORSET_SELECTED 53 | 11 | #define HE_COLORSET_SELECTED 53 |
12 | #define HE_COLORSET_CODE 54 | 12 | #define HE_COLORSET_CODE 54 |
13 | #define HE_COLORSET_ADDR 55 | 13 | #define HE_COLORSET_ADDR 55 |
14 | #define HE_COLORSET_ROOT 56 | ||
14 | 15 | ||
15 | struct ui_browser { | 16 | struct ui_browser { |
16 | u64 index, top_idx; | 17 | u64 index, top_idx; |
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 7dca1555c610..cc64d3f7fc36 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -8,15 +8,19 @@ | |||
8 | #include "../../util/hist.h" | 8 | #include "../../util/hist.h" |
9 | #include "../../util/sort.h" | 9 | #include "../../util/sort.h" |
10 | #include "../../util/symbol.h" | 10 | #include "../../util/symbol.h" |
11 | #include "../../util/evsel.h" | ||
11 | #include <pthread.h> | 12 | #include <pthread.h> |
12 | #include <newt.h> | ||
13 | 13 | ||
14 | struct browser_disasm_line { | 14 | struct browser_disasm_line { |
15 | struct rb_node rb_node; | 15 | struct rb_node rb_node; |
16 | double percent; | ||
17 | u32 idx; | 16 | u32 idx; |
18 | int idx_asm; | 17 | int idx_asm; |
19 | int jump_sources; | 18 | int jump_sources; |
19 | /* | ||
20 | * actual length of this array is saved on the nr_events field | ||
21 | * of the struct annotate_browser | ||
22 | */ | ||
23 | double percent[1]; | ||
20 | }; | 24 | }; |
21 | 25 | ||
22 | static struct annotate_browser_opt { | 26 | static struct annotate_browser_opt { |
@@ -33,8 +37,9 @@ struct annotate_browser { | |||
33 | struct ui_browser b; | 37 | struct ui_browser b; |
34 | struct rb_root entries; | 38 | struct rb_root entries; |
35 | struct rb_node *curr_hot; | 39 | struct rb_node *curr_hot; |
36 | struct disasm_line *selection; | 40 | struct disasm_line *selection; |
37 | struct disasm_line **offsets; | 41 | struct disasm_line **offsets; |
42 | int nr_events; | ||
38 | u64 start; | 43 | u64 start; |
39 | int nr_asm_entries; | 44 | int nr_asm_entries; |
40 | int nr_entries; | 45 | int nr_entries; |
@@ -94,14 +99,24 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
94 | (!current_entry || (browser->use_navkeypressed && | 99 | (!current_entry || (browser->use_navkeypressed && |
95 | !browser->navkeypressed))); | 100 | !browser->navkeypressed))); |
96 | int width = browser->width, printed; | 101 | int width = browser->width, printed; |
102 | int i, pcnt_width = 7 * ab->nr_events; | ||
103 | double percent_max = 0.0; | ||
97 | char bf[256]; | 104 | char bf[256]; |
98 | 105 | ||
99 | if (dl->offset != -1 && bdl->percent != 0.0) { | 106 | for (i = 0; i < ab->nr_events; i++) { |
100 | ui_browser__set_percent_color(browser, bdl->percent, current_entry); | 107 | if (bdl->percent[i] > percent_max) |
101 | slsmg_printf("%6.2f ", bdl->percent); | 108 | percent_max = bdl->percent[i]; |
109 | } | ||
110 | |||
111 | if (dl->offset != -1 && percent_max != 0.0) { | ||
112 | for (i = 0; i < ab->nr_events; i++) { | ||
113 | ui_browser__set_percent_color(browser, bdl->percent[i], | ||
114 | current_entry); | ||
115 | slsmg_printf("%6.2f ", bdl->percent[i]); | ||
116 | } | ||
102 | } else { | 117 | } else { |
103 | ui_browser__set_percent_color(browser, 0, current_entry); | 118 | ui_browser__set_percent_color(browser, 0, current_entry); |
104 | slsmg_write_nstring(" ", 7); | 119 | slsmg_write_nstring(" ", pcnt_width); |
105 | } | 120 | } |
106 | 121 | ||
107 | SLsmg_write_char(' '); | 122 | SLsmg_write_char(' '); |
@@ -111,12 +126,12 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
111 | width += 1; | 126 | width += 1; |
112 | 127 | ||
113 | if (!*dl->line) | 128 | if (!*dl->line) |
114 | slsmg_write_nstring(" ", width - 7); | 129 | slsmg_write_nstring(" ", width - pcnt_width); |
115 | else if (dl->offset == -1) { | 130 | else if (dl->offset == -1) { |
116 | printed = scnprintf(bf, sizeof(bf), "%*s ", | 131 | printed = scnprintf(bf, sizeof(bf), "%*s ", |
117 | ab->addr_width, " "); | 132 | ab->addr_width, " "); |
118 | slsmg_write_nstring(bf, printed); | 133 | slsmg_write_nstring(bf, printed); |
119 | slsmg_write_nstring(dl->line, width - printed - 6); | 134 | slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1); |
120 | } else { | 135 | } else { |
121 | u64 addr = dl->offset; | 136 | u64 addr = dl->offset; |
122 | int color = -1; | 137 | int color = -1; |
@@ -175,7 +190,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
175 | } | 190 | } |
176 | 191 | ||
177 | disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset); | 192 | disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset); |
178 | slsmg_write_nstring(bf, width - 10 - printed); | 193 | slsmg_write_nstring(bf, width - pcnt_width - 3 - printed); |
179 | } | 194 | } |
180 | 195 | ||
181 | if (current_entry) | 196 | if (current_entry) |
@@ -200,6 +215,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) | |||
200 | unsigned int from, to; | 215 | unsigned int from, to; |
201 | struct map_symbol *ms = ab->b.priv; | 216 | struct map_symbol *ms = ab->b.priv; |
202 | struct symbol *sym = ms->sym; | 217 | struct symbol *sym = ms->sym; |
218 | u8 pcnt_width = 7; | ||
203 | 219 | ||
204 | /* PLT symbols contain external offsets */ | 220 | /* PLT symbols contain external offsets */ |
205 | if (strstr(sym->name, "@plt")) | 221 | if (strstr(sym->name, "@plt")) |
@@ -223,57 +239,44 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) | |||
223 | to = (u64)btarget->idx; | 239 | to = (u64)btarget->idx; |
224 | } | 240 | } |
225 | 241 | ||
242 | pcnt_width *= ab->nr_events; | ||
243 | |||
226 | ui_browser__set_color(browser, HE_COLORSET_CODE); | 244 | ui_browser__set_color(browser, HE_COLORSET_CODE); |
227 | __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to); | 245 | __ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width, |
246 | from, to); | ||
228 | } | 247 | } |
229 | 248 | ||
230 | static unsigned int annotate_browser__refresh(struct ui_browser *browser) | 249 | static unsigned int annotate_browser__refresh(struct ui_browser *browser) |
231 | { | 250 | { |
251 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); | ||
232 | int ret = ui_browser__list_head_refresh(browser); | 252 | int ret = ui_browser__list_head_refresh(browser); |
253 | int pcnt_width; | ||
254 | |||
255 | pcnt_width = 7 * ab->nr_events; | ||
233 | 256 | ||
234 | if (annotate_browser__opts.jump_arrows) | 257 | if (annotate_browser__opts.jump_arrows) |
235 | annotate_browser__draw_current_jump(browser); | 258 | annotate_browser__draw_current_jump(browser); |
236 | 259 | ||
237 | ui_browser__set_color(browser, HE_COLORSET_NORMAL); | 260 | ui_browser__set_color(browser, HE_COLORSET_NORMAL); |
238 | __ui_browser__vline(browser, 7, 0, browser->height - 1); | 261 | __ui_browser__vline(browser, pcnt_width, 0, browser->height - 1); |
239 | return ret; | 262 | return ret; |
240 | } | 263 | } |
241 | 264 | ||
242 | static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx) | 265 | static int disasm__cmp(struct browser_disasm_line *a, |
266 | struct browser_disasm_line *b, int nr_pcnt) | ||
243 | { | 267 | { |
244 | double percent = 0.0; | 268 | int i; |
245 | |||
246 | if (dl->offset != -1) { | ||
247 | int len = sym->end - sym->start; | ||
248 | unsigned int hits = 0; | ||
249 | struct annotation *notes = symbol__annotation(sym); | ||
250 | struct source_line *src_line = notes->src->lines; | ||
251 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
252 | s64 offset = dl->offset; | ||
253 | struct disasm_line *next; | ||
254 | 269 | ||
255 | next = disasm__get_next_ip_line(¬es->src->source, dl); | 270 | for (i = 0; i < nr_pcnt; i++) { |
256 | while (offset < (s64)len && | 271 | if (a->percent[i] == b->percent[i]) |
257 | (next == NULL || offset < next->offset)) { | 272 | continue; |
258 | if (src_line) { | 273 | return a->percent[i] < b->percent[i]; |
259 | percent += src_line[offset].percent; | ||
260 | } else | ||
261 | hits += h->addr[offset]; | ||
262 | |||
263 | ++offset; | ||
264 | } | ||
265 | /* | ||
266 | * If the percentage wasn't already calculated in | ||
267 | * symbol__get_source_line, do it now: | ||
268 | */ | ||
269 | if (src_line == NULL && h->sum) | ||
270 | percent = 100.0 * hits / h->sum; | ||
271 | } | 274 | } |
272 | 275 | return 0; | |
273 | return percent; | ||
274 | } | 276 | } |
275 | 277 | ||
276 | static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) | 278 | static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl, |
279 | int nr_events) | ||
277 | { | 280 | { |
278 | struct rb_node **p = &root->rb_node; | 281 | struct rb_node **p = &root->rb_node; |
279 | struct rb_node *parent = NULL; | 282 | struct rb_node *parent = NULL; |
@@ -282,7 +285,8 @@ static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_l | |||
282 | while (*p != NULL) { | 285 | while (*p != NULL) { |
283 | parent = *p; | 286 | parent = *p; |
284 | l = rb_entry(parent, struct browser_disasm_line, rb_node); | 287 | l = rb_entry(parent, struct browser_disasm_line, rb_node); |
285 | if (bdl->percent < l->percent) | 288 | |
289 | if (disasm__cmp(bdl, l, nr_events)) | ||
286 | p = &(*p)->rb_left; | 290 | p = &(*p)->rb_left; |
287 | else | 291 | else |
288 | p = &(*p)->rb_right; | 292 | p = &(*p)->rb_right; |
@@ -331,12 +335,13 @@ static void annotate_browser__set_rb_top(struct annotate_browser *browser, | |||
331 | } | 335 | } |
332 | 336 | ||
333 | static void annotate_browser__calc_percent(struct annotate_browser *browser, | 337 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
334 | int evidx) | 338 | struct perf_evsel *evsel) |
335 | { | 339 | { |
336 | struct map_symbol *ms = browser->b.priv; | 340 | struct map_symbol *ms = browser->b.priv; |
337 | struct symbol *sym = ms->sym; | 341 | struct symbol *sym = ms->sym; |
338 | struct annotation *notes = symbol__annotation(sym); | 342 | struct annotation *notes = symbol__annotation(sym); |
339 | struct disasm_line *pos; | 343 | struct disasm_line *pos, *next; |
344 | s64 len = symbol__size(sym); | ||
340 | 345 | ||
341 | browser->entries = RB_ROOT; | 346 | browser->entries = RB_ROOT; |
342 | 347 | ||
@@ -344,12 +349,34 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, | |||
344 | 349 | ||
345 | list_for_each_entry(pos, ¬es->src->source, node) { | 350 | list_for_each_entry(pos, ¬es->src->source, node) { |
346 | struct browser_disasm_line *bpos = disasm_line__browser(pos); | 351 | struct browser_disasm_line *bpos = disasm_line__browser(pos); |
347 | bpos->percent = disasm_line__calc_percent(pos, sym, evidx); | 352 | const char *path = NULL; |
348 | if (bpos->percent < 0.01) { | 353 | double max_percent = 0.0; |
354 | int i; | ||
355 | |||
356 | if (pos->offset == -1) { | ||
357 | RB_CLEAR_NODE(&bpos->rb_node); | ||
358 | continue; | ||
359 | } | ||
360 | |||
361 | next = disasm__get_next_ip_line(¬es->src->source, pos); | ||
362 | |||
363 | for (i = 0; i < browser->nr_events; i++) { | ||
364 | bpos->percent[i] = disasm__calc_percent(notes, | ||
365 | evsel->idx + i, | ||
366 | pos->offset, | ||
367 | next ? next->offset : len, | ||
368 | &path); | ||
369 | |||
370 | if (max_percent < bpos->percent[i]) | ||
371 | max_percent = bpos->percent[i]; | ||
372 | } | ||
373 | |||
374 | if (max_percent < 0.01) { | ||
349 | RB_CLEAR_NODE(&bpos->rb_node); | 375 | RB_CLEAR_NODE(&bpos->rb_node); |
350 | continue; | 376 | continue; |
351 | } | 377 | } |
352 | disasm_rb_tree__insert(&browser->entries, bpos); | 378 | disasm_rb_tree__insert(&browser->entries, bpos, |
379 | browser->nr_events); | ||
353 | } | 380 | } |
354 | pthread_mutex_unlock(¬es->lock); | 381 | pthread_mutex_unlock(¬es->lock); |
355 | 382 | ||
@@ -401,7 +428,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser) | |||
401 | browser->b.nr_entries = browser->nr_asm_entries; | 428 | browser->b.nr_entries = browser->nr_asm_entries; |
402 | } | 429 | } |
403 | 430 | ||
404 | static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, | 431 | static bool annotate_browser__callq(struct annotate_browser *browser, |
432 | struct perf_evsel *evsel, | ||
405 | struct hist_browser_timer *hbt) | 433 | struct hist_browser_timer *hbt) |
406 | { | 434 | { |
407 | struct map_symbol *ms = browser->b.priv; | 435 | struct map_symbol *ms = browser->b.priv; |
@@ -432,7 +460,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, | |||
432 | } | 460 | } |
433 | 461 | ||
434 | pthread_mutex_unlock(¬es->lock); | 462 | pthread_mutex_unlock(¬es->lock); |
435 | symbol__tui_annotate(target, ms->map, evidx, hbt); | 463 | symbol__tui_annotate(target, ms->map, evsel, hbt); |
436 | ui_browser__show_title(&browser->b, sym->name); | 464 | ui_browser__show_title(&browser->b, sym->name); |
437 | return true; | 465 | return true; |
438 | } | 466 | } |
@@ -615,7 +643,8 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser | |||
615 | browser->addr_width += browser->jumps_width + 1; | 643 | browser->addr_width += browser->jumps_width + 1; |
616 | } | 644 | } |
617 | 645 | ||
618 | static int annotate_browser__run(struct annotate_browser *browser, int evidx, | 646 | static int annotate_browser__run(struct annotate_browser *browser, |
647 | struct perf_evsel *evsel, | ||
619 | struct hist_browser_timer *hbt) | 648 | struct hist_browser_timer *hbt) |
620 | { | 649 | { |
621 | struct rb_node *nd = NULL; | 650 | struct rb_node *nd = NULL; |
@@ -628,7 +657,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
628 | if (ui_browser__show(&browser->b, sym->name, help) < 0) | 657 | if (ui_browser__show(&browser->b, sym->name, help) < 0) |
629 | return -1; | 658 | return -1; |
630 | 659 | ||
631 | annotate_browser__calc_percent(browser, evidx); | 660 | annotate_browser__calc_percent(browser, evsel); |
632 | 661 | ||
633 | if (browser->curr_hot) { | 662 | if (browser->curr_hot) { |
634 | annotate_browser__set_rb_top(browser, browser->curr_hot); | 663 | annotate_browser__set_rb_top(browser, browser->curr_hot); |
@@ -641,7 +670,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
641 | key = ui_browser__run(&browser->b, delay_secs); | 670 | key = ui_browser__run(&browser->b, delay_secs); |
642 | 671 | ||
643 | if (delay_secs != 0) { | 672 | if (delay_secs != 0) { |
644 | annotate_browser__calc_percent(browser, evidx); | 673 | annotate_browser__calc_percent(browser, evsel); |
645 | /* | 674 | /* |
646 | * Current line focus got out of the list of most active | 675 | * Current line focus got out of the list of most active |
647 | * lines, NULL it so that if TAB|UNTAB is pressed, we | 676 | * lines, NULL it so that if TAB|UNTAB is pressed, we |
@@ -657,7 +686,7 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
657 | hbt->timer(hbt->arg); | 686 | hbt->timer(hbt->arg); |
658 | 687 | ||
659 | if (delay_secs != 0) | 688 | if (delay_secs != 0) |
660 | symbol__annotate_decay_histogram(sym, evidx); | 689 | symbol__annotate_decay_histogram(sym, evsel->idx); |
661 | continue; | 690 | continue; |
662 | case K_TAB: | 691 | case K_TAB: |
663 | if (nd != NULL) { | 692 | if (nd != NULL) { |
@@ -754,7 +783,7 @@ show_help: | |||
754 | goto show_sup_ins; | 783 | goto show_sup_ins; |
755 | goto out; | 784 | goto out; |
756 | } else if (!(annotate_browser__jump(browser) || | 785 | } else if (!(annotate_browser__jump(browser) || |
757 | annotate_browser__callq(browser, evidx, hbt))) { | 786 | annotate_browser__callq(browser, evsel, hbt))) { |
758 | show_sup_ins: | 787 | show_sup_ins: |
759 | ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); | 788 | ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); |
760 | } | 789 | } |
@@ -776,10 +805,10 @@ out: | |||
776 | return key; | 805 | return key; |
777 | } | 806 | } |
778 | 807 | ||
779 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | 808 | int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, |
780 | struct hist_browser_timer *hbt) | 809 | struct hist_browser_timer *hbt) |
781 | { | 810 | { |
782 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt); | 811 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt); |
783 | } | 812 | } |
784 | 813 | ||
785 | static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, | 814 | static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, |
@@ -826,7 +855,8 @@ static inline int width_jumps(int n) | |||
826 | return 1; | 855 | return 1; |
827 | } | 856 | } |
828 | 857 | ||
829 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 858 | int symbol__tui_annotate(struct symbol *sym, struct map *map, |
859 | struct perf_evsel *evsel, | ||
830 | struct hist_browser_timer *hbt) | 860 | struct hist_browser_timer *hbt) |
831 | { | 861 | { |
832 | struct disasm_line *pos, *n; | 862 | struct disasm_line *pos, *n; |
@@ -847,6 +877,8 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
847 | }, | 877 | }, |
848 | }; | 878 | }; |
849 | int ret = -1; | 879 | int ret = -1; |
880 | int nr_pcnt = 1; | ||
881 | size_t sizeof_bdl = sizeof(struct browser_disasm_line); | ||
850 | 882 | ||
851 | if (sym == NULL) | 883 | if (sym == NULL) |
852 | return -1; | 884 | return -1; |
@@ -862,7 +894,12 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
862 | return -1; | 894 | return -1; |
863 | } | 895 | } |
864 | 896 | ||
865 | if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) { | 897 | if (perf_evsel__is_group_event(evsel)) { |
898 | nr_pcnt = evsel->nr_members; | ||
899 | sizeof_bdl += sizeof(double) * (nr_pcnt - 1); | ||
900 | } | ||
901 | |||
902 | if (symbol__annotate(sym, map, sizeof_bdl) < 0) { | ||
866 | ui__error("%s", ui_helpline__last_msg); | 903 | ui__error("%s", ui_helpline__last_msg); |
867 | goto out_free_offsets; | 904 | goto out_free_offsets; |
868 | } | 905 | } |
@@ -900,6 +937,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
900 | browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); | 937 | browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); |
901 | browser.max_addr_width = hex_width(sym->end); | 938 | browser.max_addr_width = hex_width(sym->end); |
902 | browser.jumps_width = width_jumps(browser.max_jump_sources); | 939 | browser.jumps_width = width_jumps(browser.max_jump_sources); |
940 | browser.nr_events = nr_pcnt; | ||
903 | browser.b.nr_entries = browser.nr_entries; | 941 | browser.b.nr_entries = browser.nr_entries; |
904 | browser.b.entries = ¬es->src->source, | 942 | browser.b.entries = ¬es->src->source, |
905 | browser.b.width += 18; /* Percentage */ | 943 | browser.b.width += 18; /* Percentage */ |
@@ -909,7 +947,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
909 | 947 | ||
910 | annotate_browser__update_addr_width(&browser); | 948 | annotate_browser__update_addr_width(&browser); |
911 | 949 | ||
912 | ret = annotate_browser__run(&browser, evidx, hbt); | 950 | ret = annotate_browser__run(&browser, evsel, hbt); |
913 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 951 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
914 | list_del(&pos->node); | 952 | list_del(&pos->node); |
915 | disasm_line__free(pos); | 953 | disasm_line__free(pos); |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index aa22704047d6..d88a2d0acb6d 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -2,7 +2,6 @@ | |||
2 | #include "../libslang.h" | 2 | #include "../libslang.h" |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <newt.h> | ||
6 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
7 | 6 | ||
8 | #include "../../util/evsel.h" | 7 | #include "../../util/evsel.h" |
@@ -1193,7 +1192,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, | |||
1193 | char buf[512]; | 1192 | char buf[512]; |
1194 | size_t buflen = sizeof(buf); | 1193 | size_t buflen = sizeof(buf); |
1195 | 1194 | ||
1196 | if (symbol_conf.event_group && evsel->nr_members > 1) { | 1195 | if (perf_evsel__is_group_event(evsel)) { |
1197 | struct perf_evsel *pos; | 1196 | struct perf_evsel *pos; |
1198 | 1197 | ||
1199 | perf_evsel__group_desc(evsel, buf, buflen); | 1198 | perf_evsel__group_desc(evsel, buf, buflen); |
@@ -1599,7 +1598,7 @@ do_annotate: | |||
1599 | * Don't let this be freed, say, by hists__decay_entry. | 1598 | * Don't let this be freed, say, by hists__decay_entry. |
1600 | */ | 1599 | */ |
1601 | he->used = true; | 1600 | he->used = true; |
1602 | err = hist_entry__tui_annotate(he, evsel->idx, hbt); | 1601 | err = hist_entry__tui_annotate(he, evsel, hbt); |
1603 | he->used = false; | 1602 | he->used = false; |
1604 | /* | 1603 | /* |
1605 | * offer option to annotate the other branch source or target | 1604 | * offer option to annotate the other branch source or target |
@@ -1709,7 +1708,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1709 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | 1708 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
1710 | HE_COLORSET_NORMAL); | 1709 | HE_COLORSET_NORMAL); |
1711 | 1710 | ||
1712 | if (symbol_conf.event_group && evsel->nr_members > 1) { | 1711 | if (perf_evsel__is_group_event(evsel)) { |
1713 | struct perf_evsel *pos; | 1712 | struct perf_evsel *pos; |
1714 | 1713 | ||
1715 | ev_name = perf_evsel__group_name(evsel); | 1714 | ev_name = perf_evsel__group_name(evsel); |
diff --git a/tools/perf/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index 98851d55a53e..95c7cfb8f2c6 100644 --- a/tools/perf/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include "../libslang.h" | 1 | #include "../libslang.h" |
2 | #include <elf.h> | 2 | #include <elf.h> |
3 | #include <newt.h> | ||
4 | #include <inttypes.h> | 3 | #include <inttypes.h> |
5 | #include <sys/ttydefaults.h> | 4 | #include <sys/ttydefaults.h> |
6 | #include <string.h> | 5 | #include <string.h> |
@@ -10,41 +9,9 @@ | |||
10 | #include "../../util/symbol.h" | 9 | #include "../../util/symbol.h" |
11 | #include "../browser.h" | 10 | #include "../browser.h" |
12 | #include "../helpline.h" | 11 | #include "../helpline.h" |
12 | #include "../keysyms.h" | ||
13 | #include "map.h" | 13 | #include "map.h" |
14 | 14 | ||
15 | static int ui_entry__read(const char *title, char *bf, size_t size, int width) | ||
16 | { | ||
17 | struct newtExitStruct es; | ||
18 | newtComponent form, entry; | ||
19 | const char *result; | ||
20 | int err = -1; | ||
21 | |||
22 | newtCenteredWindow(width, 1, title); | ||
23 | form = newtForm(NULL, NULL, 0); | ||
24 | if (form == NULL) | ||
25 | return -1; | ||
26 | |||
27 | entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL); | ||
28 | if (entry == NULL) | ||
29 | goto out_free_form; | ||
30 | |||
31 | newtFormAddComponent(form, entry); | ||
32 | newtFormAddHotKey(form, NEWT_KEY_ENTER); | ||
33 | newtFormAddHotKey(form, NEWT_KEY_ESCAPE); | ||
34 | newtFormAddHotKey(form, NEWT_KEY_LEFT); | ||
35 | newtFormAddHotKey(form, CTRL('c')); | ||
36 | newtFormRun(form, &es); | ||
37 | |||
38 | if (result != NULL) { | ||
39 | strncpy(bf, result, size); | ||
40 | err = 0; | ||
41 | } | ||
42 | out_free_form: | ||
43 | newtPopWindow(); | ||
44 | newtFormDestroy(form); | ||
45 | return err; | ||
46 | } | ||
47 | |||
48 | struct map_browser { | 15 | struct map_browser { |
49 | struct ui_browser b; | 16 | struct ui_browser b; |
50 | struct map *map; | 17 | struct map *map; |
@@ -78,10 +45,11 @@ static int map_browser__search(struct map_browser *self) | |||
78 | { | 45 | { |
79 | char target[512]; | 46 | char target[512]; |
80 | struct symbol *sym; | 47 | struct symbol *sym; |
81 | int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40); | 48 | int err = ui_browser__input_window("Search by name/addr", |
82 | 49 | "Prefix with 0x to search by address", | |
83 | if (err) | 50 | target, "ENTER: OK, ESC: Cancel", 0); |
84 | return err; | 51 | if (err != K_ENTER) |
52 | return -1; | ||
85 | 53 | ||
86 | if (target[0] == '0' && tolower(target[1]) == 'x') { | 54 | if (target[0] == '0' && tolower(target[1]) == 'x') { |
87 | u64 addr = strtoull(target, NULL, 16); | 55 | u64 addr = strtoull(target, NULL, 16); |
@@ -112,12 +80,20 @@ static int map_browser__run(struct map_browser *self) | |||
112 | while (1) { | 80 | while (1) { |
113 | key = ui_browser__run(&self->b, 0); | 81 | key = ui_browser__run(&self->b, 0); |
114 | 82 | ||
115 | if (verbose && key == '/') | 83 | switch (key) { |
116 | map_browser__search(self); | 84 | case '/': |
117 | else | 85 | if (verbose) |
86 | map_browser__search(self); | ||
87 | default: | ||
118 | break; | 88 | break; |
89 | case K_LEFT: | ||
90 | case K_ESC: | ||
91 | case 'q': | ||
92 | case CTRL('c'): | ||
93 | goto out; | ||
94 | } | ||
119 | } | 95 | } |
120 | 96 | out: | |
121 | ui_browser__hide(&self->b); | 97 | ui_browser__hide(&self->b); |
122 | return key; | 98 | return key; |
123 | } | 99 | } |
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index cbbd44b0d93e..12f009e61e94 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c | |||
@@ -1,5 +1,4 @@ | |||
1 | #include <elf.h> | 1 | #include <elf.h> |
2 | #include <newt.h> | ||
3 | #include <inttypes.h> | 2 | #include <inttypes.h> |
4 | #include <sys/ttydefaults.h> | 3 | #include <sys/ttydefaults.h> |
5 | #include <string.h> | 4 | #include <string.h> |
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c index 7d8dc581a545..f538794615db 100644 --- a/tools/perf/ui/gtk/annotate.c +++ b/tools/perf/ui/gtk/annotate.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "gtk.h" | 1 | #include "gtk.h" |
2 | #include "util/debug.h" | 2 | #include "util/debug.h" |
3 | #include "util/annotate.h" | 3 | #include "util/annotate.h" |
4 | #include "util/evsel.h" | ||
4 | #include "ui/helpline.h" | 5 | #include "ui/helpline.h" |
5 | 6 | ||
6 | 7 | ||
@@ -32,7 +33,7 @@ static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym, | |||
32 | return 0; | 33 | return 0; |
33 | 34 | ||
34 | symhist = annotation__histogram(symbol__annotation(sym), evidx); | 35 | symhist = annotation__histogram(symbol__annotation(sym), evidx); |
35 | if (!symhist->addr[dl->offset]) | 36 | if (!symbol_conf.event_group && !symhist->addr[dl->offset]) |
36 | return 0; | 37 | return 0; |
37 | 38 | ||
38 | percent = 100.0 * symhist->addr[dl->offset] / symhist->sum; | 39 | percent = 100.0 * symhist->addr[dl->offset] / symhist->sum; |
@@ -85,7 +86,7 @@ static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl) | |||
85 | } | 86 | } |
86 | 87 | ||
87 | static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, | 88 | static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, |
88 | struct map *map, int evidx, | 89 | struct map *map, struct perf_evsel *evsel, |
89 | struct hist_browser_timer *hbt __maybe_unused) | 90 | struct hist_browser_timer *hbt __maybe_unused) |
90 | { | 91 | { |
91 | struct disasm_line *pos, *n; | 92 | struct disasm_line *pos, *n; |
@@ -118,10 +119,24 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, | |||
118 | 119 | ||
119 | list_for_each_entry(pos, ¬es->src->source, node) { | 120 | list_for_each_entry(pos, ¬es->src->source, node) { |
120 | GtkTreeIter iter; | 121 | GtkTreeIter iter; |
122 | int ret = 0; | ||
121 | 123 | ||
122 | gtk_list_store_append(store, &iter); | 124 | gtk_list_store_append(store, &iter); |
123 | 125 | ||
124 | if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evidx)) | 126 | if (perf_evsel__is_group_event(evsel)) { |
127 | for (i = 0; i < evsel->nr_members; i++) { | ||
128 | ret += perf_gtk__get_percent(s + ret, | ||
129 | sizeof(s) - ret, | ||
130 | sym, pos, | ||
131 | evsel->idx + i); | ||
132 | ret += scnprintf(s + ret, sizeof(s) - ret, " "); | ||
133 | } | ||
134 | } else { | ||
135 | ret = perf_gtk__get_percent(s, sizeof(s), sym, pos, | ||
136 | evsel->idx); | ||
137 | } | ||
138 | |||
139 | if (ret) | ||
125 | gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1); | 140 | gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1); |
126 | if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos)) | 141 | if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos)) |
127 | gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1); | 142 | gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1); |
@@ -139,7 +154,8 @@ static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, | |||
139 | return 0; | 154 | return 0; |
140 | } | 155 | } |
141 | 156 | ||
142 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, | 157 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, |
158 | struct perf_evsel *evsel, | ||
143 | struct hist_browser_timer *hbt) | 159 | struct hist_browser_timer *hbt) |
144 | { | 160 | { |
145 | GtkWidget *window; | 161 | GtkWidget *window; |
@@ -206,7 +222,7 @@ int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, | |||
206 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, | 222 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, |
207 | tab_label); | 223 | tab_label); |
208 | 224 | ||
209 | perf_gtk__annotate_symbol(scrolled_window, sym, map, evidx, hbt); | 225 | perf_gtk__annotate_symbol(scrolled_window, sym, map, evsel, hbt); |
210 | return 0; | 226 | return 0; |
211 | } | 227 | } |
212 | 228 | ||
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 1e764a8ad259..6f259b3d14e2 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c | |||
@@ -32,21 +32,18 @@ static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
32 | int ret; | 32 | int ret; |
33 | double percent = 0.0; | 33 | double percent = 0.0; |
34 | struct hists *hists = he->hists; | 34 | struct hists *hists = he->hists; |
35 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
35 | 36 | ||
36 | if (hists->stats.total_period) | 37 | if (hists->stats.total_period) |
37 | percent = 100.0 * get_field(he) / hists->stats.total_period; | 38 | percent = 100.0 * get_field(he) / hists->stats.total_period; |
38 | 39 | ||
39 | ret = __percent_color_snprintf(hpp->buf, hpp->size, percent); | 40 | ret = __percent_color_snprintf(hpp->buf, hpp->size, percent); |
40 | 41 | ||
41 | if (symbol_conf.event_group) { | 42 | if (perf_evsel__is_group_event(evsel)) { |
42 | int prev_idx, idx_delta; | 43 | int prev_idx, idx_delta; |
43 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
44 | struct hist_entry *pair; | 44 | struct hist_entry *pair; |
45 | int nr_members = evsel->nr_members; | 45 | int nr_members = evsel->nr_members; |
46 | 46 | ||
47 | if (nr_members <= 1) | ||
48 | return ret; | ||
49 | |||
50 | prev_idx = perf_evsel__group_idx(evsel); | 47 | prev_idx = perf_evsel__group_idx(evsel); |
51 | 48 | ||
52 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { | 49 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index d671e63aa351..4bf91b09d62d 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -16,6 +16,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
16 | { | 16 | { |
17 | int ret; | 17 | int ret; |
18 | struct hists *hists = he->hists; | 18 | struct hists *hists = he->hists; |
19 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
19 | 20 | ||
20 | if (fmt_percent) { | 21 | if (fmt_percent) { |
21 | double percent = 0.0; | 22 | double percent = 0.0; |
@@ -28,15 +29,11 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
28 | } else | 29 | } else |
29 | ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); | 30 | ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); |
30 | 31 | ||
31 | if (symbol_conf.event_group) { | 32 | if (perf_evsel__is_group_event(evsel)) { |
32 | int prev_idx, idx_delta; | 33 | int prev_idx, idx_delta; |
33 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
34 | struct hist_entry *pair; | 34 | struct hist_entry *pair; |
35 | int nr_members = evsel->nr_members; | 35 | int nr_members = evsel->nr_members; |
36 | 36 | ||
37 | if (nr_members <= 1) | ||
38 | return ret; | ||
39 | |||
40 | prev_idx = perf_evsel__group_idx(evsel); | 37 | prev_idx = perf_evsel__group_idx(evsel); |
41 | 38 | ||
42 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { | 39 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { |
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 81efa192e86c..b9401482d110 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #include <newt.h> | ||
2 | #include <signal.h> | 1 | #include <signal.h> |
3 | #include <stdbool.h> | 2 | #include <stdbool.h> |
4 | 3 | ||
@@ -88,13 +87,6 @@ int ui__getch(int delay_secs) | |||
88 | return SLkp_getkey(); | 87 | return SLkp_getkey(); |
89 | } | 88 | } |
90 | 89 | ||
91 | static void newt_suspend(void *d __maybe_unused) | ||
92 | { | ||
93 | newtSuspend(); | ||
94 | raise(SIGTSTP); | ||
95 | newtResume(); | ||
96 | } | ||
97 | |||
98 | static void ui__signal(int sig) | 90 | static void ui__signal(int sig) |
99 | { | 91 | { |
100 | ui__exit(false); | 92 | ui__exit(false); |
@@ -106,7 +98,17 @@ int ui__init(void) | |||
106 | { | 98 | { |
107 | int err; | 99 | int err; |
108 | 100 | ||
109 | newtInit(); | 101 | SLutf8_enable(-1); |
102 | SLtt_get_terminfo(); | ||
103 | SLtt_get_screen_size(); | ||
104 | |||
105 | err = SLsmg_init_smg(); | ||
106 | if (err < 0) | ||
107 | goto out; | ||
108 | err = SLang_init_tty(0, 0, 0); | ||
109 | if (err < 0) | ||
110 | goto out; | ||
111 | |||
110 | err = SLkp_init(); | 112 | err = SLkp_init(); |
111 | if (err < 0) { | 113 | if (err < 0) { |
112 | pr_err("TUI initialization failed.\n"); | 114 | pr_err("TUI initialization failed.\n"); |
@@ -115,7 +117,6 @@ int ui__init(void) | |||
115 | 117 | ||
116 | SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); | 118 | SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); |
117 | 119 | ||
118 | newtSetSuspendCallback(newt_suspend, NULL); | ||
119 | ui_helpline__init(); | 120 | ui_helpline__init(); |
120 | ui_browser__init(); | 121 | ui_browser__init(); |
121 | ui_progress__init(); | 122 | ui_progress__init(); |
diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index d86359c99907..70cb0d4eb8aa 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h | |||
@@ -12,7 +12,7 @@ extern int use_browser; | |||
12 | void setup_browser(bool fallback_to_pager); | 12 | void setup_browser(bool fallback_to_pager); |
13 | void exit_browser(bool wait_for_ok); | 13 | void exit_browser(bool wait_for_ok); |
14 | 14 | ||
15 | #ifdef NEWT_SUPPORT | 15 | #ifdef SLANG_SUPPORT |
16 | int ui__init(void); | 16 | int ui__init(void); |
17 | void ui__exit(bool wait_for_ok); | 17 | void ui__exit(bool wait_for_ok); |
18 | #else | 18 | #else |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d33fe937e6f1..d102716c43a1 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "symbol.h" | 14 | #include "symbol.h" |
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | #include "annotate.h" | 16 | #include "annotate.h" |
17 | #include "evsel.h" | ||
17 | #include <pthread.h> | 18 | #include <pthread.h> |
18 | #include <linux/bitops.h> | 19 | #include <linux/bitops.h> |
19 | 20 | ||
@@ -602,8 +603,42 @@ struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disa | |||
602 | return NULL; | 603 | return NULL; |
603 | } | 604 | } |
604 | 605 | ||
606 | double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, | ||
607 | s64 end, const char **path) | ||
608 | { | ||
609 | struct source_line *src_line = notes->src->lines; | ||
610 | double percent = 0.0; | ||
611 | |||
612 | if (src_line) { | ||
613 | size_t sizeof_src_line = sizeof(*src_line) + | ||
614 | sizeof(src_line->p) * (src_line->nr_pcnt - 1); | ||
615 | |||
616 | while (offset < end) { | ||
617 | src_line = (void *)notes->src->lines + | ||
618 | (sizeof_src_line * offset); | ||
619 | |||
620 | if (*path == NULL) | ||
621 | *path = src_line->path; | ||
622 | |||
623 | percent += src_line->p[evidx].percent; | ||
624 | offset++; | ||
625 | } | ||
626 | } else { | ||
627 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
628 | unsigned int hits = 0; | ||
629 | |||
630 | while (offset < end) | ||
631 | hits += h->addr[offset++]; | ||
632 | |||
633 | if (h->sum) | ||
634 | percent = 100.0 * hits / h->sum; | ||
635 | } | ||
636 | |||
637 | return percent; | ||
638 | } | ||
639 | |||
605 | static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, | 640 | static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, |
606 | int evidx, u64 len, int min_pcnt, int printed, | 641 | struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, |
607 | int max_lines, struct disasm_line *queue) | 642 | int max_lines, struct disasm_line *queue) |
608 | { | 643 | { |
609 | static const char *prev_line; | 644 | static const char *prev_line; |
@@ -611,34 +646,37 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
611 | 646 | ||
612 | if (dl->offset != -1) { | 647 | if (dl->offset != -1) { |
613 | const char *path = NULL; | 648 | const char *path = NULL; |
614 | unsigned int hits = 0; | 649 | double percent, max_percent = 0.0; |
615 | double percent = 0.0; | 650 | double *ppercents = &percent; |
651 | int i, nr_percent = 1; | ||
616 | const char *color; | 652 | const char *color; |
617 | struct annotation *notes = symbol__annotation(sym); | 653 | struct annotation *notes = symbol__annotation(sym); |
618 | struct source_line *src_line = notes->src->lines; | ||
619 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
620 | s64 offset = dl->offset; | 654 | s64 offset = dl->offset; |
621 | const u64 addr = start + offset; | 655 | const u64 addr = start + offset; |
622 | struct disasm_line *next; | 656 | struct disasm_line *next; |
623 | 657 | ||
624 | next = disasm__get_next_ip_line(¬es->src->source, dl); | 658 | next = disasm__get_next_ip_line(¬es->src->source, dl); |
625 | 659 | ||
626 | while (offset < (s64)len && | 660 | if (perf_evsel__is_group_event(evsel)) { |
627 | (next == NULL || offset < next->offset)) { | 661 | nr_percent = evsel->nr_members; |
628 | if (src_line) { | 662 | ppercents = calloc(nr_percent, sizeof(double)); |
629 | if (path == NULL) | 663 | if (ppercents == NULL) |
630 | path = src_line[offset].path; | 664 | return -1; |
631 | percent += src_line[offset].percent; | ||
632 | } else | ||
633 | hits += h->addr[offset]; | ||
634 | |||
635 | ++offset; | ||
636 | } | 665 | } |
637 | 666 | ||
638 | if (src_line == NULL && h->sum) | 667 | for (i = 0; i < nr_percent; i++) { |
639 | percent = 100.0 * hits / h->sum; | 668 | percent = disasm__calc_percent(notes, |
669 | notes->src->lines ? i : evsel->idx + i, | ||
670 | offset, | ||
671 | next ? next->offset : (s64) len, | ||
672 | &path); | ||
673 | |||
674 | ppercents[i] = percent; | ||
675 | if (percent > max_percent) | ||
676 | max_percent = percent; | ||
677 | } | ||
640 | 678 | ||
641 | if (percent < min_pcnt) | 679 | if (max_percent < min_pcnt) |
642 | return -1; | 680 | return -1; |
643 | 681 | ||
644 | if (max_lines && printed >= max_lines) | 682 | if (max_lines && printed >= max_lines) |
@@ -648,12 +686,12 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
648 | list_for_each_entry_from(queue, ¬es->src->source, node) { | 686 | list_for_each_entry_from(queue, ¬es->src->source, node) { |
649 | if (queue == dl) | 687 | if (queue == dl) |
650 | break; | 688 | break; |
651 | disasm_line__print(queue, sym, start, evidx, len, | 689 | disasm_line__print(queue, sym, start, evsel, len, |
652 | 0, 0, 1, NULL); | 690 | 0, 0, 1, NULL); |
653 | } | 691 | } |
654 | } | 692 | } |
655 | 693 | ||
656 | color = get_percent_color(percent); | 694 | color = get_percent_color(max_percent); |
657 | 695 | ||
658 | /* | 696 | /* |
659 | * Also color the filename and line if needed, with | 697 | * Also color the filename and line if needed, with |
@@ -669,25 +707,59 @@ static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 st | |||
669 | } | 707 | } |
670 | } | 708 | } |
671 | 709 | ||
672 | color_fprintf(stdout, color, " %7.2f", percent); | 710 | for (i = 0; i < nr_percent; i++) { |
711 | percent = ppercents[i]; | ||
712 | color = get_percent_color(percent); | ||
713 | color_fprintf(stdout, color, " %7.2f", percent); | ||
714 | } | ||
715 | |||
673 | printf(" : "); | 716 | printf(" : "); |
674 | color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); | 717 | color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); |
675 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); | 718 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); |
719 | |||
720 | if (ppercents != &percent) | ||
721 | free(ppercents); | ||
722 | |||
676 | } else if (max_lines && printed >= max_lines) | 723 | } else if (max_lines && printed >= max_lines) |
677 | return 1; | 724 | return 1; |
678 | else { | 725 | else { |
726 | int width = 8; | ||
727 | |||
679 | if (queue) | 728 | if (queue) |
680 | return -1; | 729 | return -1; |
681 | 730 | ||
731 | if (perf_evsel__is_group_event(evsel)) | ||
732 | width *= evsel->nr_members; | ||
733 | |||
682 | if (!*dl->line) | 734 | if (!*dl->line) |
683 | printf(" :\n"); | 735 | printf(" %*s:\n", width, " "); |
684 | else | 736 | else |
685 | printf(" : %s\n", dl->line); | 737 | printf(" %*s: %s\n", width, " ", dl->line); |
686 | } | 738 | } |
687 | 739 | ||
688 | return 0; | 740 | return 0; |
689 | } | 741 | } |
690 | 742 | ||
743 | /* | ||
744 | * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw) | ||
745 | * which looks like following | ||
746 | * | ||
747 | * 0000000000415500 <_init>: | ||
748 | * 415500: sub $0x8,%rsp | ||
749 | * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8> | ||
750 | * 41550b: test %rax,%rax | ||
751 | * 41550e: je 415515 <_init+0x15> | ||
752 | * 415510: callq 416e70 <__gmon_start__@plt> | ||
753 | * 415515: add $0x8,%rsp | ||
754 | * 415519: retq | ||
755 | * | ||
756 | * it will be parsed and saved into struct disasm_line as | ||
757 | * <offset> <name> <ops.raw> | ||
758 | * | ||
759 | * The offset will be a relative offset from the start of the symbol and -1 | ||
760 | * means that it's not a disassembly line so should be treated differently. | ||
761 | * The ops.raw part will be parsed further according to type of the instruction. | ||
762 | */ | ||
691 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | 763 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, |
692 | FILE *file, size_t privsize) | 764 | FILE *file, size_t privsize) |
693 | { | 765 | { |
@@ -858,7 +930,7 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin | |||
858 | struct source_line *iter; | 930 | struct source_line *iter; |
859 | struct rb_node **p = &root->rb_node; | 931 | struct rb_node **p = &root->rb_node; |
860 | struct rb_node *parent = NULL; | 932 | struct rb_node *parent = NULL; |
861 | int ret; | 933 | int i, ret; |
862 | 934 | ||
863 | while (*p != NULL) { | 935 | while (*p != NULL) { |
864 | parent = *p; | 936 | parent = *p; |
@@ -866,7 +938,8 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin | |||
866 | 938 | ||
867 | ret = strcmp(iter->path, src_line->path); | 939 | ret = strcmp(iter->path, src_line->path); |
868 | if (ret == 0) { | 940 | if (ret == 0) { |
869 | iter->percent_sum += src_line->percent; | 941 | for (i = 0; i < src_line->nr_pcnt; i++) |
942 | iter->p[i].percent_sum += src_line->p[i].percent; | ||
870 | return; | 943 | return; |
871 | } | 944 | } |
872 | 945 | ||
@@ -876,12 +949,26 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin | |||
876 | p = &(*p)->rb_right; | 949 | p = &(*p)->rb_right; |
877 | } | 950 | } |
878 | 951 | ||
879 | src_line->percent_sum = src_line->percent; | 952 | for (i = 0; i < src_line->nr_pcnt; i++) |
953 | src_line->p[i].percent_sum = src_line->p[i].percent; | ||
880 | 954 | ||
881 | rb_link_node(&src_line->node, parent, p); | 955 | rb_link_node(&src_line->node, parent, p); |
882 | rb_insert_color(&src_line->node, root); | 956 | rb_insert_color(&src_line->node, root); |
883 | } | 957 | } |
884 | 958 | ||
959 | static int cmp_source_line(struct source_line *a, struct source_line *b) | ||
960 | { | ||
961 | int i; | ||
962 | |||
963 | for (i = 0; i < a->nr_pcnt; i++) { | ||
964 | if (a->p[i].percent_sum == b->p[i].percent_sum) | ||
965 | continue; | ||
966 | return a->p[i].percent_sum > b->p[i].percent_sum; | ||
967 | } | ||
968 | |||
969 | return 0; | ||
970 | } | ||
971 | |||
885 | static void __resort_source_line(struct rb_root *root, struct source_line *src_line) | 972 | static void __resort_source_line(struct rb_root *root, struct source_line *src_line) |
886 | { | 973 | { |
887 | struct source_line *iter; | 974 | struct source_line *iter; |
@@ -892,7 +979,7 @@ static void __resort_source_line(struct rb_root *root, struct source_line *src_l | |||
892 | parent = *p; | 979 | parent = *p; |
893 | iter = rb_entry(parent, struct source_line, node); | 980 | iter = rb_entry(parent, struct source_line, node); |
894 | 981 | ||
895 | if (src_line->percent_sum > iter->percent_sum) | 982 | if (cmp_source_line(src_line, iter)) |
896 | p = &(*p)->rb_left; | 983 | p = &(*p)->rb_left; |
897 | else | 984 | else |
898 | p = &(*p)->rb_right; | 985 | p = &(*p)->rb_right; |
@@ -924,32 +1011,52 @@ static void symbol__free_source_line(struct symbol *sym, int len) | |||
924 | { | 1011 | { |
925 | struct annotation *notes = symbol__annotation(sym); | 1012 | struct annotation *notes = symbol__annotation(sym); |
926 | struct source_line *src_line = notes->src->lines; | 1013 | struct source_line *src_line = notes->src->lines; |
1014 | size_t sizeof_src_line; | ||
927 | int i; | 1015 | int i; |
928 | 1016 | ||
929 | for (i = 0; i < len; i++) | 1017 | sizeof_src_line = sizeof(*src_line) + |
930 | free(src_line[i].path); | 1018 | (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); |
1019 | |||
1020 | for (i = 0; i < len; i++) { | ||
1021 | free(src_line->path); | ||
1022 | src_line = (void *)src_line + sizeof_src_line; | ||
1023 | } | ||
931 | 1024 | ||
932 | free(src_line); | 1025 | free(notes->src->lines); |
933 | notes->src->lines = NULL; | 1026 | notes->src->lines = NULL; |
934 | } | 1027 | } |
935 | 1028 | ||
936 | /* Get the filename:line for the colored entries */ | 1029 | /* Get the filename:line for the colored entries */ |
937 | static int symbol__get_source_line(struct symbol *sym, struct map *map, | 1030 | static int symbol__get_source_line(struct symbol *sym, struct map *map, |
938 | int evidx, struct rb_root *root, int len, | 1031 | struct perf_evsel *evsel, |
1032 | struct rb_root *root, int len, | ||
939 | const char *filename) | 1033 | const char *filename) |
940 | { | 1034 | { |
941 | u64 start; | 1035 | u64 start; |
942 | int i; | 1036 | int i, k; |
1037 | int evidx = evsel->idx; | ||
943 | char cmd[PATH_MAX * 2]; | 1038 | char cmd[PATH_MAX * 2]; |
944 | struct source_line *src_line; | 1039 | struct source_line *src_line; |
945 | struct annotation *notes = symbol__annotation(sym); | 1040 | struct annotation *notes = symbol__annotation(sym); |
946 | struct sym_hist *h = annotation__histogram(notes, evidx); | 1041 | struct sym_hist *h = annotation__histogram(notes, evidx); |
947 | struct rb_root tmp_root = RB_ROOT; | 1042 | struct rb_root tmp_root = RB_ROOT; |
1043 | int nr_pcnt = 1; | ||
1044 | u64 h_sum = h->sum; | ||
1045 | size_t sizeof_src_line = sizeof(struct source_line); | ||
1046 | |||
1047 | if (perf_evsel__is_group_event(evsel)) { | ||
1048 | for (i = 1; i < evsel->nr_members; i++) { | ||
1049 | h = annotation__histogram(notes, evidx + i); | ||
1050 | h_sum += h->sum; | ||
1051 | } | ||
1052 | nr_pcnt = evsel->nr_members; | ||
1053 | sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p); | ||
1054 | } | ||
948 | 1055 | ||
949 | if (!h->sum) | 1056 | if (!h_sum) |
950 | return 0; | 1057 | return 0; |
951 | 1058 | ||
952 | src_line = notes->src->lines = calloc(len, sizeof(struct source_line)); | 1059 | src_line = notes->src->lines = calloc(len, sizeof_src_line); |
953 | if (!notes->src->lines) | 1060 | if (!notes->src->lines) |
954 | return -1; | 1061 | return -1; |
955 | 1062 | ||
@@ -960,29 +1067,41 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
960 | size_t line_len; | 1067 | size_t line_len; |
961 | u64 offset; | 1068 | u64 offset; |
962 | FILE *fp; | 1069 | FILE *fp; |
1070 | double percent_max = 0.0; | ||
963 | 1071 | ||
964 | src_line[i].percent = 100.0 * h->addr[i] / h->sum; | 1072 | src_line->nr_pcnt = nr_pcnt; |
965 | if (src_line[i].percent <= 0.5) | 1073 | |
966 | continue; | 1074 | for (k = 0; k < nr_pcnt; k++) { |
1075 | h = annotation__histogram(notes, evidx + k); | ||
1076 | src_line->p[k].percent = 100.0 * h->addr[i] / h->sum; | ||
1077 | |||
1078 | if (src_line->p[k].percent > percent_max) | ||
1079 | percent_max = src_line->p[k].percent; | ||
1080 | } | ||
1081 | |||
1082 | if (percent_max <= 0.5) | ||
1083 | goto next; | ||
967 | 1084 | ||
968 | offset = start + i; | 1085 | offset = start + i; |
969 | sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); | 1086 | sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); |
970 | fp = popen(cmd, "r"); | 1087 | fp = popen(cmd, "r"); |
971 | if (!fp) | 1088 | if (!fp) |
972 | continue; | 1089 | goto next; |
973 | 1090 | ||
974 | if (getline(&path, &line_len, fp) < 0 || !line_len) | 1091 | if (getline(&path, &line_len, fp) < 0 || !line_len) |
975 | goto next; | 1092 | goto next_close; |
976 | 1093 | ||
977 | src_line[i].path = malloc(sizeof(char) * line_len + 1); | 1094 | src_line->path = malloc(sizeof(char) * line_len + 1); |
978 | if (!src_line[i].path) | 1095 | if (!src_line->path) |
979 | goto next; | 1096 | goto next_close; |
980 | 1097 | ||
981 | strcpy(src_line[i].path, path); | 1098 | strcpy(src_line->path, path); |
982 | insert_source_line(&tmp_root, &src_line[i]); | 1099 | insert_source_line(&tmp_root, src_line); |
983 | 1100 | ||
984 | next: | 1101 | next_close: |
985 | pclose(fp); | 1102 | pclose(fp); |
1103 | next: | ||
1104 | src_line = (void *)src_line + sizeof_src_line; | ||
986 | } | 1105 | } |
987 | 1106 | ||
988 | resort_source_line(root, &tmp_root); | 1107 | resort_source_line(root, &tmp_root); |
@@ -1004,24 +1123,33 @@ static void print_summary(struct rb_root *root, const char *filename) | |||
1004 | 1123 | ||
1005 | node = rb_first(root); | 1124 | node = rb_first(root); |
1006 | while (node) { | 1125 | while (node) { |
1007 | double percent; | 1126 | double percent, percent_max = 0.0; |
1008 | const char *color; | 1127 | const char *color; |
1009 | char *path; | 1128 | char *path; |
1129 | int i; | ||
1010 | 1130 | ||
1011 | src_line = rb_entry(node, struct source_line, node); | 1131 | src_line = rb_entry(node, struct source_line, node); |
1012 | percent = src_line->percent_sum; | 1132 | for (i = 0; i < src_line->nr_pcnt; i++) { |
1013 | color = get_percent_color(percent); | 1133 | percent = src_line->p[i].percent_sum; |
1134 | color = get_percent_color(percent); | ||
1135 | color_fprintf(stdout, color, " %7.2f", percent); | ||
1136 | |||
1137 | if (percent > percent_max) | ||
1138 | percent_max = percent; | ||
1139 | } | ||
1140 | |||
1014 | path = src_line->path; | 1141 | path = src_line->path; |
1142 | color = get_percent_color(percent_max); | ||
1143 | color_fprintf(stdout, color, " %s", path); | ||
1015 | 1144 | ||
1016 | color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||
1017 | node = rb_next(node); | 1145 | node = rb_next(node); |
1018 | } | 1146 | } |
1019 | } | 1147 | } |
1020 | 1148 | ||
1021 | static void symbol__annotate_hits(struct symbol *sym, int evidx) | 1149 | static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel) |
1022 | { | 1150 | { |
1023 | struct annotation *notes = symbol__annotation(sym); | 1151 | struct annotation *notes = symbol__annotation(sym); |
1024 | struct sym_hist *h = annotation__histogram(notes, evidx); | 1152 | struct sym_hist *h = annotation__histogram(notes, evsel->idx); |
1025 | u64 len = symbol__size(sym), offset; | 1153 | u64 len = symbol__size(sym), offset; |
1026 | 1154 | ||
1027 | for (offset = 0; offset < len; ++offset) | 1155 | for (offset = 0; offset < len; ++offset) |
@@ -1031,9 +1159,9 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx) | |||
1031 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); | 1159 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); |
1032 | } | 1160 | } |
1033 | 1161 | ||
1034 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | 1162 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
1035 | bool full_paths, int min_pcnt, int max_lines, | 1163 | struct perf_evsel *evsel, bool full_paths, |
1036 | int context) | 1164 | int min_pcnt, int max_lines, int context) |
1037 | { | 1165 | { |
1038 | struct dso *dso = map->dso; | 1166 | struct dso *dso = map->dso; |
1039 | char *filename; | 1167 | char *filename; |
@@ -1044,6 +1172,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
1044 | int printed = 2, queue_len = 0; | 1172 | int printed = 2, queue_len = 0; |
1045 | int more = 0; | 1173 | int more = 0; |
1046 | u64 len; | 1174 | u64 len; |
1175 | int width = 8; | ||
1176 | int namelen; | ||
1047 | 1177 | ||
1048 | filename = strdup(dso->long_name); | 1178 | filename = strdup(dso->long_name); |
1049 | if (!filename) | 1179 | if (!filename) |
@@ -1055,12 +1185,18 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
1055 | d_filename = basename(filename); | 1185 | d_filename = basename(filename); |
1056 | 1186 | ||
1057 | len = symbol__size(sym); | 1187 | len = symbol__size(sym); |
1188 | namelen = strlen(d_filename); | ||
1189 | |||
1190 | if (perf_evsel__is_group_event(evsel)) | ||
1191 | width *= evsel->nr_members; | ||
1058 | 1192 | ||
1059 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); | 1193 | printf(" %-*.*s| Source code & Disassembly of %s\n", |
1060 | printf("------------------------------------------------\n"); | 1194 | width, width, "Percent", d_filename); |
1195 | printf("-%-*.*s-------------------------------------\n", | ||
1196 | width+namelen, width+namelen, graph_dotted_line); | ||
1061 | 1197 | ||
1062 | if (verbose) | 1198 | if (verbose) |
1063 | symbol__annotate_hits(sym, evidx); | 1199 | symbol__annotate_hits(sym, evsel); |
1064 | 1200 | ||
1065 | list_for_each_entry(pos, ¬es->src->source, node) { | 1201 | list_for_each_entry(pos, ¬es->src->source, node) { |
1066 | if (context && queue == NULL) { | 1202 | if (context && queue == NULL) { |
@@ -1068,7 +1204,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | |||
1068 | queue_len = 0; | 1204 | queue_len = 0; |
1069 | } | 1205 | } |
1070 | 1206 | ||
1071 | switch (disasm_line__print(pos, sym, start, evidx, len, | 1207 | switch (disasm_line__print(pos, sym, start, evsel, len, |
1072 | min_pcnt, printed, max_lines, | 1208 | min_pcnt, printed, max_lines, |
1073 | queue)) { | 1209 | queue)) { |
1074 | case 0: | 1210 | case 0: |
@@ -1163,9 +1299,9 @@ size_t disasm__fprintf(struct list_head *head, FILE *fp) | |||
1163 | return printed; | 1299 | return printed; |
1164 | } | 1300 | } |
1165 | 1301 | ||
1166 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | 1302 | int symbol__tty_annotate(struct symbol *sym, struct map *map, |
1167 | bool print_lines, bool full_paths, int min_pcnt, | 1303 | struct perf_evsel *evsel, bool print_lines, |
1168 | int max_lines) | 1304 | bool full_paths, int min_pcnt, int max_lines) |
1169 | { | 1305 | { |
1170 | struct dso *dso = map->dso; | 1306 | struct dso *dso = map->dso; |
1171 | const char *filename = dso->long_name; | 1307 | const char *filename = dso->long_name; |
@@ -1178,12 +1314,12 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
1178 | len = symbol__size(sym); | 1314 | len = symbol__size(sym); |
1179 | 1315 | ||
1180 | if (print_lines) { | 1316 | if (print_lines) { |
1181 | symbol__get_source_line(sym, map, evidx, &source_line, | 1317 | symbol__get_source_line(sym, map, evsel, &source_line, |
1182 | len, filename); | 1318 | len, filename); |
1183 | print_summary(&source_line, filename); | 1319 | print_summary(&source_line, filename); |
1184 | } | 1320 | } |
1185 | 1321 | ||
1186 | symbol__annotate_printf(sym, map, evidx, full_paths, | 1322 | symbol__annotate_printf(sym, map, evsel, full_paths, |
1187 | min_pcnt, max_lines, 0); | 1323 | min_pcnt, max_lines, 0); |
1188 | if (print_lines) | 1324 | if (print_lines) |
1189 | symbol__free_source_line(sym, len); | 1325 | symbol__free_source_line(sym, len); |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index c422440fe611..af755156d278 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -50,6 +50,8 @@ bool ins__is_jump(const struct ins *ins); | |||
50 | bool ins__is_call(const struct ins *ins); | 50 | bool ins__is_call(const struct ins *ins); |
51 | int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); | 51 | int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); |
52 | 52 | ||
53 | struct annotation; | ||
54 | |||
53 | struct disasm_line { | 55 | struct disasm_line { |
54 | struct list_head node; | 56 | struct list_head node; |
55 | s64 offset; | 57 | s64 offset; |
@@ -68,17 +70,24 @@ void disasm_line__free(struct disasm_line *dl); | |||
68 | struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos); | 70 | struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos); |
69 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw); | 71 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw); |
70 | size_t disasm__fprintf(struct list_head *head, FILE *fp); | 72 | size_t disasm__fprintf(struct list_head *head, FILE *fp); |
73 | double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, | ||
74 | s64 end, const char **path); | ||
71 | 75 | ||
72 | struct sym_hist { | 76 | struct sym_hist { |
73 | u64 sum; | 77 | u64 sum; |
74 | u64 addr[0]; | 78 | u64 addr[0]; |
75 | }; | 79 | }; |
76 | 80 | ||
77 | struct source_line { | 81 | struct source_line_percent { |
78 | struct rb_node node; | ||
79 | double percent; | 82 | double percent; |
80 | double percent_sum; | 83 | double percent_sum; |
84 | }; | ||
85 | |||
86 | struct source_line { | ||
87 | struct rb_node node; | ||
81 | char *path; | 88 | char *path; |
89 | int nr_pcnt; | ||
90 | struct source_line_percent p[1]; | ||
82 | }; | 91 | }; |
83 | 92 | ||
84 | /** struct annotated_source - symbols with hits have this attached as in sannotation | 93 | /** struct annotated_source - symbols with hits have this attached as in sannotation |
@@ -130,47 +139,49 @@ void symbol__annotate_zero_histograms(struct symbol *sym); | |||
130 | 139 | ||
131 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); | 140 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); |
132 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); | 141 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); |
133 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | 142 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
134 | bool full_paths, int min_pcnt, int max_lines, | 143 | struct perf_evsel *evsel, bool full_paths, |
135 | int context); | 144 | int min_pcnt, int max_lines, int context); |
136 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); | 145 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); |
137 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); | 146 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); |
138 | void disasm__purge(struct list_head *head); | 147 | void disasm__purge(struct list_head *head); |
139 | 148 | ||
140 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | 149 | int symbol__tty_annotate(struct symbol *sym, struct map *map, |
141 | bool print_lines, bool full_paths, int min_pcnt, | 150 | struct perf_evsel *evsel, bool print_lines, |
142 | int max_lines); | 151 | bool full_paths, int min_pcnt, int max_lines); |
143 | 152 | ||
144 | #ifdef NEWT_SUPPORT | 153 | #ifdef SLANG_SUPPORT |
145 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 154 | int symbol__tui_annotate(struct symbol *sym, struct map *map, |
155 | struct perf_evsel *evsel, | ||
146 | struct hist_browser_timer *hbt); | 156 | struct hist_browser_timer *hbt); |
147 | #else | 157 | #else |
148 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, | 158 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, |
149 | struct map *map __maybe_unused, | 159 | struct map *map __maybe_unused, |
150 | int evidx __maybe_unused, | 160 | struct perf_evsel *evsel __maybe_unused, |
151 | struct hist_browser_timer *hbt | 161 | struct hist_browser_timer *hbt |
152 | __maybe_unused) | 162 | __maybe_unused) |
153 | { | 163 | { |
154 | return 0; | 164 | return 0; |
155 | } | 165 | } |
156 | #endif | 166 | #endif |
157 | 167 | ||
158 | #ifdef GTK2_SUPPORT | 168 | #ifdef GTK2_SUPPORT |
159 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, | 169 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, |
170 | struct perf_evsel *evsel, | ||
160 | struct hist_browser_timer *hbt); | 171 | struct hist_browser_timer *hbt); |
161 | 172 | ||
162 | static inline int hist_entry__gtk_annotate(struct hist_entry *he, int evidx, | 173 | static inline int hist_entry__gtk_annotate(struct hist_entry *he, |
174 | struct perf_evsel *evsel, | ||
163 | struct hist_browser_timer *hbt) | 175 | struct hist_browser_timer *hbt) |
164 | { | 176 | { |
165 | return symbol__gtk_annotate(he->ms.sym, he->ms.map, evidx, hbt); | 177 | return symbol__gtk_annotate(he->ms.sym, he->ms.map, evsel, hbt); |
166 | } | 178 | } |
167 | 179 | ||
168 | void perf_gtk__show_annotations(void); | 180 | void perf_gtk__show_annotations(void); |
169 | #else | 181 | #else |
170 | static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, | 182 | static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, |
171 | int evidx __maybe_unused, | 183 | struct perf_evsel *evsel __maybe_unused, |
172 | struct hist_browser_timer *hbt | 184 | struct hist_browser_timer *hbt __maybe_unused) |
173 | __maybe_unused) | ||
174 | { | 185 | { |
175 | return 0; | 186 | return 0; |
176 | } | 187 | } |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index f817046e22b1..beb8cf9f9976 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "cpumap.h" | 4 | #include "cpumap.h" |
5 | #include <assert.h> | 5 | #include <assert.h> |
6 | #include <stdio.h> | 6 | #include <stdio.h> |
7 | #include <stdlib.h> | ||
7 | 8 | ||
8 | static struct cpu_map *cpu_map__default_new(void) | 9 | static struct cpu_map *cpu_map__default_new(void) |
9 | { | 10 | { |
@@ -219,7 +220,7 @@ int cpu_map__get_socket(struct cpu_map *map, int idx) | |||
219 | if (!mnt) | 220 | if (!mnt) |
220 | return -1; | 221 | return -1; |
221 | 222 | ||
222 | sprintf(path, | 223 | snprintf(path, PATH_MAX, |
223 | "%s/devices/system/cpu/cpu%d/topology/physical_package_id", | 224 | "%s/devices/system/cpu/cpu%d/topology/physical_package_id", |
224 | mnt, cpu); | 225 | mnt, cpu); |
225 | 226 | ||
@@ -231,27 +232,88 @@ int cpu_map__get_socket(struct cpu_map *map, int idx) | |||
231 | return ret == 1 ? cpu : -1; | 232 | return ret == 1 ? cpu : -1; |
232 | } | 233 | } |
233 | 234 | ||
234 | int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp) | 235 | static int cmp_ids(const void *a, const void *b) |
235 | { | 236 | { |
236 | struct cpu_map *sock; | 237 | return *(int *)a - *(int *)b; |
238 | } | ||
239 | |||
240 | static int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, | ||
241 | int (*f)(struct cpu_map *map, int cpu)) | ||
242 | { | ||
243 | struct cpu_map *c; | ||
237 | int nr = cpus->nr; | 244 | int nr = cpus->nr; |
238 | int cpu, s1, s2; | 245 | int cpu, s1, s2; |
239 | 246 | ||
240 | sock = calloc(1, sizeof(*sock) + nr * sizeof(int)); | 247 | /* allocate as much as possible */ |
241 | if (!sock) | 248 | c = calloc(1, sizeof(*c) + nr * sizeof(int)); |
249 | if (!c) | ||
242 | return -1; | 250 | return -1; |
243 | 251 | ||
244 | for (cpu = 0; cpu < nr; cpu++) { | 252 | for (cpu = 0; cpu < nr; cpu++) { |
245 | s1 = cpu_map__get_socket(cpus, cpu); | 253 | s1 = f(cpus, cpu); |
246 | for (s2 = 0; s2 < sock->nr; s2++) { | 254 | for (s2 = 0; s2 < c->nr; s2++) { |
247 | if (s1 == sock->map[s2]) | 255 | if (s1 == c->map[s2]) |
248 | break; | 256 | break; |
249 | } | 257 | } |
250 | if (s2 == sock->nr) { | 258 | if (s2 == c->nr) { |
251 | sock->map[sock->nr] = s1; | 259 | c->map[c->nr] = s1; |
252 | sock->nr++; | 260 | c->nr++; |
253 | } | 261 | } |
254 | } | 262 | } |
255 | *sockp = sock; | 263 | /* ensure we process id in increasing order */ |
264 | qsort(c->map, c->nr, sizeof(int), cmp_ids); | ||
265 | |||
266 | *res = c; | ||
256 | return 0; | 267 | return 0; |
257 | } | 268 | } |
269 | |||
270 | int cpu_map__get_core(struct cpu_map *map, int idx) | ||
271 | { | ||
272 | FILE *fp; | ||
273 | const char *mnt; | ||
274 | char path[PATH_MAX]; | ||
275 | int cpu, ret, s; | ||
276 | |||
277 | if (idx > map->nr) | ||
278 | return -1; | ||
279 | |||
280 | cpu = map->map[idx]; | ||
281 | |||
282 | mnt = sysfs_find_mountpoint(); | ||
283 | if (!mnt) | ||
284 | return -1; | ||
285 | |||
286 | snprintf(path, PATH_MAX, | ||
287 | "%s/devices/system/cpu/cpu%d/topology/core_id", | ||
288 | mnt, cpu); | ||
289 | |||
290 | fp = fopen(path, "r"); | ||
291 | if (!fp) | ||
292 | return -1; | ||
293 | ret = fscanf(fp, "%d", &cpu); | ||
294 | fclose(fp); | ||
295 | if (ret != 1) | ||
296 | return -1; | ||
297 | |||
298 | s = cpu_map__get_socket(map, idx); | ||
299 | if (s == -1) | ||
300 | return -1; | ||
301 | |||
302 | /* | ||
303 | * encode socket in upper 16 bits | ||
304 | * core_id is relative to socket, and | ||
305 | * we need a global id. So we combine | ||
306 | * socket+ core id | ||
307 | */ | ||
308 | return (s << 16) | (cpu & 0xffff); | ||
309 | } | ||
310 | |||
311 | int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp) | ||
312 | { | ||
313 | return cpu_map__build_map(cpus, sockp, cpu_map__get_socket); | ||
314 | } | ||
315 | |||
316 | int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep) | ||
317 | { | ||
318 | return cpu_map__build_map(cpus, corep, cpu_map__get_core); | ||
319 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 161b00756a12..9bed02e5fb3d 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -15,7 +15,9 @@ void cpu_map__delete(struct cpu_map *map); | |||
15 | struct cpu_map *cpu_map__read(FILE *file); | 15 | struct cpu_map *cpu_map__read(FILE *file); |
16 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); | 16 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); |
17 | int cpu_map__get_socket(struct cpu_map *map, int idx); | 17 | int cpu_map__get_socket(struct cpu_map *map, int idx); |
18 | int cpu_map__get_core(struct cpu_map *map, int idx); | ||
18 | int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp); | 19 | int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp); |
20 | int cpu_map__build_core_map(struct cpu_map *cpus, struct cpu_map **corep); | ||
19 | 21 | ||
20 | static inline int cpu_map__socket(struct cpu_map *sock, int s) | 22 | static inline int cpu_map__socket(struct cpu_map *sock, int s) |
21 | { | 23 | { |
@@ -24,6 +26,16 @@ static inline int cpu_map__socket(struct cpu_map *sock, int s) | |||
24 | return sock->map[s]; | 26 | return sock->map[s]; |
25 | } | 27 | } |
26 | 28 | ||
29 | static inline int cpu_map__id_to_socket(int id) | ||
30 | { | ||
31 | return id >> 16; | ||
32 | } | ||
33 | |||
34 | static inline int cpu_map__id_to_cpu(int id) | ||
35 | { | ||
36 | return id & 0xffff; | ||
37 | } | ||
38 | |||
27 | static inline int cpu_map__nr(const struct cpu_map *map) | 39 | static inline int cpu_map__nr(const struct cpu_map *map) |
28 | { | 40 | { |
29 | return map ? map->nr : 1; | 41 | return map ? map->nr : 1; |
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h deleted file mode 100644 index 68f3e87ec57f..000000000000 --- a/tools/perf/util/debugfs.h +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | #ifndef __DEBUGFS_H__ | ||
2 | #define __DEBUGFS_H__ | ||
3 | |||
4 | const char *debugfs_find_mountpoint(void); | ||
5 | int debugfs_valid_mountpoint(const char *debugfs); | ||
6 | char *debugfs_mount(const char *mountpoint); | ||
7 | void debugfs_set_path(const char *mountpoint); | ||
8 | |||
9 | extern char debugfs_mountpoint[]; | ||
10 | extern char tracing_events_path[]; | ||
11 | |||
12 | #endif /* __DEBUGFS_H__ */ | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 0d573ff4771a..181389535c0c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -88,8 +88,10 @@ struct perf_sample { | |||
88 | u64 id; | 88 | u64 id; |
89 | u64 stream_id; | 89 | u64 stream_id; |
90 | u64 period; | 90 | u64 period; |
91 | u64 weight; | ||
91 | u32 cpu; | 92 | u32 cpu; |
92 | u32 raw_size; | 93 | u32 raw_size; |
94 | u64 data_src; | ||
93 | void *raw_data; | 95 | void *raw_data; |
94 | struct ip_callchain *callchain; | 96 | struct ip_callchain *callchain; |
95 | struct branch_stack *branch_stack; | 97 | struct branch_stack *branch_stack; |
@@ -97,6 +99,13 @@ struct perf_sample { | |||
97 | struct stack_dump user_stack; | 99 | struct stack_dump user_stack; |
98 | }; | 100 | }; |
99 | 101 | ||
102 | #define PERF_MEM_DATA_SRC_NONE \ | ||
103 | (PERF_MEM_S(OP, NA) |\ | ||
104 | PERF_MEM_S(LVL, NA) |\ | ||
105 | PERF_MEM_S(SNOOP, NA) |\ | ||
106 | PERF_MEM_S(LOCK, NA) |\ | ||
107 | PERF_MEM_S(TLB, NA)) | ||
108 | |||
100 | struct build_id_event { | 109 | struct build_id_event { |
101 | struct perf_event_header header; | 110 | struct perf_event_header header; |
102 | pid_t pid; | 111 | pid_t pid; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c8be0fbc5145..f7c727801aab 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Released under the GPL v2. (and only v2, not any later version) | 7 | * Released under the GPL v2. (and only v2, not any later version) |
8 | */ | 8 | */ |
9 | #include "util.h" | 9 | #include "util.h" |
10 | #include "debugfs.h" | 10 | #include <lk/debugfs.h> |
11 | #include <poll.h> | 11 | #include <poll.h> |
12 | #include "cpumap.h" | 12 | #include "cpumap.h" |
13 | #include "thread_map.h" | 13 | #include "thread_map.h" |
@@ -38,13 +38,12 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | |||
38 | evlist->workload.pid = -1; | 38 | evlist->workload.pid = -1; |
39 | } | 39 | } |
40 | 40 | ||
41 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 41 | struct perf_evlist *perf_evlist__new(void) |
42 | struct thread_map *threads) | ||
43 | { | 42 | { |
44 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | 43 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); |
45 | 44 | ||
46 | if (evlist != NULL) | 45 | if (evlist != NULL) |
47 | perf_evlist__init(evlist, cpus, threads); | 46 | perf_evlist__init(evlist, NULL, NULL); |
48 | 47 | ||
49 | return evlist; | 48 | return evlist; |
50 | } | 49 | } |
@@ -228,12 +227,14 @@ void perf_evlist__disable(struct perf_evlist *evlist) | |||
228 | { | 227 | { |
229 | int cpu, thread; | 228 | int cpu, thread; |
230 | struct perf_evsel *pos; | 229 | struct perf_evsel *pos; |
230 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
231 | int nr_threads = thread_map__nr(evlist->threads); | ||
231 | 232 | ||
232 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 233 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
233 | list_for_each_entry(pos, &evlist->entries, node) { | 234 | list_for_each_entry(pos, &evlist->entries, node) { |
234 | if (!perf_evsel__is_group_leader(pos)) | 235 | if (!perf_evsel__is_group_leader(pos)) |
235 | continue; | 236 | continue; |
236 | for (thread = 0; thread < evlist->threads->nr; thread++) | 237 | for (thread = 0; thread < nr_threads; thread++) |
237 | ioctl(FD(pos, cpu, thread), | 238 | ioctl(FD(pos, cpu, thread), |
238 | PERF_EVENT_IOC_DISABLE, 0); | 239 | PERF_EVENT_IOC_DISABLE, 0); |
239 | } | 240 | } |
@@ -244,12 +245,14 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
244 | { | 245 | { |
245 | int cpu, thread; | 246 | int cpu, thread; |
246 | struct perf_evsel *pos; | 247 | struct perf_evsel *pos; |
248 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
249 | int nr_threads = thread_map__nr(evlist->threads); | ||
247 | 250 | ||
248 | for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { | 251 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
249 | list_for_each_entry(pos, &evlist->entries, node) { | 252 | list_for_each_entry(pos, &evlist->entries, node) { |
250 | if (!perf_evsel__is_group_leader(pos)) | 253 | if (!perf_evsel__is_group_leader(pos)) |
251 | continue; | 254 | continue; |
252 | for (thread = 0; thread < evlist->threads->nr; thread++) | 255 | for (thread = 0; thread < nr_threads; thread++) |
253 | ioctl(FD(pos, cpu, thread), | 256 | ioctl(FD(pos, cpu, thread), |
254 | PERF_EVENT_IOC_ENABLE, 0); | 257 | PERF_EVENT_IOC_ENABLE, 0); |
255 | } | 258 | } |
@@ -258,7 +261,9 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
258 | 261 | ||
259 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 262 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
260 | { | 263 | { |
261 | int nfds = cpu_map__nr(evlist->cpus) * evlist->threads->nr * evlist->nr_entries; | 264 | int nr_cpus = cpu_map__nr(evlist->cpus); |
265 | int nr_threads = thread_map__nr(evlist->threads); | ||
266 | int nfds = nr_cpus * nr_threads * evlist->nr_entries; | ||
262 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 267 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); |
263 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | 268 | return evlist->pollfd != NULL ? 0 : -ENOMEM; |
264 | } | 269 | } |
@@ -417,7 +422,7 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | |||
417 | { | 422 | { |
418 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); | 423 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); |
419 | if (cpu_map__all(evlist->cpus)) | 424 | if (cpu_map__all(evlist->cpus)) |
420 | evlist->nr_mmaps = evlist->threads->nr; | 425 | evlist->nr_mmaps = thread_map__nr(evlist->threads); |
421 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); | 426 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); |
422 | return evlist->mmap != NULL ? 0 : -ENOMEM; | 427 | return evlist->mmap != NULL ? 0 : -ENOMEM; |
423 | } | 428 | } |
@@ -442,11 +447,13 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m | |||
442 | { | 447 | { |
443 | struct perf_evsel *evsel; | 448 | struct perf_evsel *evsel; |
444 | int cpu, thread; | 449 | int cpu, thread; |
450 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
451 | int nr_threads = thread_map__nr(evlist->threads); | ||
445 | 452 | ||
446 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 453 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
447 | int output = -1; | 454 | int output = -1; |
448 | 455 | ||
449 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 456 | for (thread = 0; thread < nr_threads; thread++) { |
450 | list_for_each_entry(evsel, &evlist->entries, node) { | 457 | list_for_each_entry(evsel, &evlist->entries, node) { |
451 | int fd = FD(evsel, cpu, thread); | 458 | int fd = FD(evsel, cpu, thread); |
452 | 459 | ||
@@ -470,7 +477,7 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m | |||
470 | return 0; | 477 | return 0; |
471 | 478 | ||
472 | out_unmap: | 479 | out_unmap: |
473 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 480 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
474 | if (evlist->mmap[cpu].base != NULL) { | 481 | if (evlist->mmap[cpu].base != NULL) { |
475 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | 482 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); |
476 | evlist->mmap[cpu].base = NULL; | 483 | evlist->mmap[cpu].base = NULL; |
@@ -483,8 +490,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
483 | { | 490 | { |
484 | struct perf_evsel *evsel; | 491 | struct perf_evsel *evsel; |
485 | int thread; | 492 | int thread; |
493 | int nr_threads = thread_map__nr(evlist->threads); | ||
486 | 494 | ||
487 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 495 | for (thread = 0; thread < nr_threads; thread++) { |
488 | int output = -1; | 496 | int output = -1; |
489 | 497 | ||
490 | list_for_each_entry(evsel, &evlist->entries, node) { | 498 | list_for_each_entry(evsel, &evlist->entries, node) { |
@@ -509,7 +517,7 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
509 | return 0; | 517 | return 0; |
510 | 518 | ||
511 | out_unmap: | 519 | out_unmap: |
512 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 520 | for (thread = 0; thread < nr_threads; thread++) { |
513 | if (evlist->mmap[thread].base != NULL) { | 521 | if (evlist->mmap[thread].base != NULL) { |
514 | munmap(evlist->mmap[thread].base, evlist->mmap_len); | 522 | munmap(evlist->mmap[thread].base, evlist->mmap_len); |
515 | evlist->mmap[thread].base = NULL; | 523 | evlist->mmap[thread].base = NULL; |
@@ -610,7 +618,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist) | |||
610 | struct perf_evsel *evsel; | 618 | struct perf_evsel *evsel; |
611 | int err = 0; | 619 | int err = 0; |
612 | const int ncpus = cpu_map__nr(evlist->cpus), | 620 | const int ncpus = cpu_map__nr(evlist->cpus), |
613 | nthreads = evlist->threads->nr; | 621 | nthreads = thread_map__nr(evlist->threads); |
614 | 622 | ||
615 | list_for_each_entry(evsel, &evlist->entries, node) { | 623 | list_for_each_entry(evsel, &evlist->entries, node) { |
616 | if (evsel->filter == NULL) | 624 | if (evsel->filter == NULL) |
@@ -629,7 +637,7 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) | |||
629 | struct perf_evsel *evsel; | 637 | struct perf_evsel *evsel; |
630 | int err = 0; | 638 | int err = 0; |
631 | const int ncpus = cpu_map__nr(evlist->cpus), | 639 | const int ncpus = cpu_map__nr(evlist->cpus), |
632 | nthreads = evlist->threads->nr; | 640 | nthreads = thread_map__nr(evlist->threads); |
633 | 641 | ||
634 | list_for_each_entry(evsel, &evlist->entries, node) { | 642 | list_for_each_entry(evsel, &evlist->entries, node) { |
635 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); | 643 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); |
@@ -712,10 +720,20 @@ void perf_evlist__set_selected(struct perf_evlist *evlist, | |||
712 | evlist->selected = evsel; | 720 | evlist->selected = evsel; |
713 | } | 721 | } |
714 | 722 | ||
723 | void perf_evlist__close(struct perf_evlist *evlist) | ||
724 | { | ||
725 | struct perf_evsel *evsel; | ||
726 | int ncpus = cpu_map__nr(evlist->cpus); | ||
727 | int nthreads = thread_map__nr(evlist->threads); | ||
728 | |||
729 | list_for_each_entry_reverse(evsel, &evlist->entries, node) | ||
730 | perf_evsel__close(evsel, ncpus, nthreads); | ||
731 | } | ||
732 | |||
715 | int perf_evlist__open(struct perf_evlist *evlist) | 733 | int perf_evlist__open(struct perf_evlist *evlist) |
716 | { | 734 | { |
717 | struct perf_evsel *evsel; | 735 | struct perf_evsel *evsel; |
718 | int err, ncpus, nthreads; | 736 | int err; |
719 | 737 | ||
720 | list_for_each_entry(evsel, &evlist->entries, node) { | 738 | list_for_each_entry(evsel, &evlist->entries, node) { |
721 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); | 739 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); |
@@ -725,19 +743,15 @@ int perf_evlist__open(struct perf_evlist *evlist) | |||
725 | 743 | ||
726 | return 0; | 744 | return 0; |
727 | out_err: | 745 | out_err: |
728 | ncpus = evlist->cpus ? evlist->cpus->nr : 1; | 746 | perf_evlist__close(evlist); |
729 | nthreads = evlist->threads ? evlist->threads->nr : 1; | ||
730 | |||
731 | list_for_each_entry_reverse(evsel, &evlist->entries, node) | ||
732 | perf_evsel__close(evsel, ncpus, nthreads); | ||
733 | |||
734 | errno = -err; | 747 | errno = -err; |
735 | return err; | 748 | return err; |
736 | } | 749 | } |
737 | 750 | ||
738 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 751 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, |
739 | struct perf_record_opts *opts, | 752 | struct perf_target *target, |
740 | const char *argv[]) | 753 | const char *argv[], bool pipe_output, |
754 | bool want_signal) | ||
741 | { | 755 | { |
742 | int child_ready_pipe[2], go_pipe[2]; | 756 | int child_ready_pipe[2], go_pipe[2]; |
743 | char bf; | 757 | char bf; |
@@ -759,7 +773,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
759 | } | 773 | } |
760 | 774 | ||
761 | if (!evlist->workload.pid) { | 775 | if (!evlist->workload.pid) { |
762 | if (opts->pipe_output) | 776 | if (pipe_output) |
763 | dup2(2, 1); | 777 | dup2(2, 1); |
764 | 778 | ||
765 | close(child_ready_pipe[0]); | 779 | close(child_ready_pipe[0]); |
@@ -787,11 +801,12 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
787 | execvp(argv[0], (char **)argv); | 801 | execvp(argv[0], (char **)argv); |
788 | 802 | ||
789 | perror(argv[0]); | 803 | perror(argv[0]); |
790 | kill(getppid(), SIGUSR1); | 804 | if (want_signal) |
805 | kill(getppid(), SIGUSR1); | ||
791 | exit(-1); | 806 | exit(-1); |
792 | } | 807 | } |
793 | 808 | ||
794 | if (perf_target__none(&opts->target)) | 809 | if (perf_target__none(target)) |
795 | evlist->threads->map[0] = evlist->workload.pid; | 810 | evlist->threads->map[0] = evlist->workload.pid; |
796 | 811 | ||
797 | close(child_ready_pipe[1]); | 812 | close(child_ready_pipe[1]); |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 2dd07bd60b4f..0583d36252be 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -49,8 +49,7 @@ struct perf_evsel_str_handler { | |||
49 | void *handler; | 49 | void *handler; |
50 | }; | 50 | }; |
51 | 51 | ||
52 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 52 | struct perf_evlist *perf_evlist__new(void); |
53 | struct thread_map *threads); | ||
54 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | 53 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, |
55 | struct thread_map *threads); | 54 | struct thread_map *threads); |
56 | void perf_evlist__exit(struct perf_evlist *evlist); | 55 | void perf_evlist__exit(struct perf_evlist *evlist); |
@@ -82,13 +81,15 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | |||
82 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 81 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); |
83 | 82 | ||
84 | int perf_evlist__open(struct perf_evlist *evlist); | 83 | int perf_evlist__open(struct perf_evlist *evlist); |
84 | void perf_evlist__close(struct perf_evlist *evlist); | ||
85 | 85 | ||
86 | void perf_evlist__config(struct perf_evlist *evlist, | 86 | void perf_evlist__config(struct perf_evlist *evlist, |
87 | struct perf_record_opts *opts); | 87 | struct perf_record_opts *opts); |
88 | 88 | ||
89 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 89 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, |
90 | struct perf_record_opts *opts, | 90 | struct perf_target *target, |
91 | const char *argv[]); | 91 | const char *argv[], bool pipe_output, |
92 | bool want_signal); | ||
92 | int perf_evlist__start_workload(struct perf_evlist *evlist); | 93 | int perf_evlist__start_workload(struct perf_evlist *evlist); |
93 | 94 | ||
94 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 95 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 9c82f98f26de..07b1a3ad3e24 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -10,7 +10,7 @@ | |||
10 | #include <byteswap.h> | 10 | #include <byteswap.h> |
11 | #include <linux/bitops.h> | 11 | #include <linux/bitops.h> |
12 | #include "asm/bug.h" | 12 | #include "asm/bug.h" |
13 | #include "debugfs.h" | 13 | #include <lk/debugfs.h> |
14 | #include "event-parse.h" | 14 | #include "event-parse.h" |
15 | #include "evsel.h" | 15 | #include "evsel.h" |
16 | #include "evlist.h" | 16 | #include "evlist.h" |
@@ -554,6 +554,9 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
554 | perf_evsel__set_sample_bit(evsel, CPU); | 554 | perf_evsel__set_sample_bit(evsel, CPU); |
555 | } | 555 | } |
556 | 556 | ||
557 | if (opts->sample_address) | ||
558 | attr->sample_type |= PERF_SAMPLE_DATA_SRC; | ||
559 | |||
557 | if (opts->no_delay) { | 560 | if (opts->no_delay) { |
558 | attr->watermark = 0; | 561 | attr->watermark = 0; |
559 | attr->wakeup_events = 1; | 562 | attr->wakeup_events = 1; |
@@ -563,6 +566,9 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
563 | attr->branch_sample_type = opts->branch_stack; | 566 | attr->branch_sample_type = opts->branch_stack; |
564 | } | 567 | } |
565 | 568 | ||
569 | if (opts->sample_weight) | ||
570 | attr->sample_type |= PERF_SAMPLE_WEIGHT; | ||
571 | |||
566 | attr->mmap = track; | 572 | attr->mmap = track; |
567 | attr->comm = track; | 573 | attr->comm = track; |
568 | 574 | ||
@@ -633,6 +639,12 @@ int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
633 | return 0; | 639 | return 0; |
634 | } | 640 | } |
635 | 641 | ||
642 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus) | ||
643 | { | ||
644 | memset(evsel->counts, 0, (sizeof(*evsel->counts) + | ||
645 | (ncpus * sizeof(struct perf_counts_values)))); | ||
646 | } | ||
647 | |||
636 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 648 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
637 | { | 649 | { |
638 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 650 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
@@ -673,9 +685,8 @@ void perf_evsel__free_counts(struct perf_evsel *evsel) | |||
673 | void perf_evsel__exit(struct perf_evsel *evsel) | 685 | void perf_evsel__exit(struct perf_evsel *evsel) |
674 | { | 686 | { |
675 | assert(list_empty(&evsel->node)); | 687 | assert(list_empty(&evsel->node)); |
676 | xyarray__delete(evsel->fd); | 688 | perf_evsel__free_fd(evsel); |
677 | xyarray__delete(evsel->sample_id); | 689 | perf_evsel__free_id(evsel); |
678 | free(evsel->id); | ||
679 | } | 690 | } |
680 | 691 | ||
681 | void perf_evsel__delete(struct perf_evsel *evsel) | 692 | void perf_evsel__delete(struct perf_evsel *evsel) |
@@ -1012,6 +1023,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1012 | data->cpu = data->pid = data->tid = -1; | 1023 | data->cpu = data->pid = data->tid = -1; |
1013 | data->stream_id = data->id = data->time = -1ULL; | 1024 | data->stream_id = data->id = data->time = -1ULL; |
1014 | data->period = 1; | 1025 | data->period = 1; |
1026 | data->weight = 0; | ||
1015 | 1027 | ||
1016 | if (event->header.type != PERF_RECORD_SAMPLE) { | 1028 | if (event->header.type != PERF_RECORD_SAMPLE) { |
1017 | if (!evsel->attr.sample_id_all) | 1029 | if (!evsel->attr.sample_id_all) |
@@ -1162,6 +1174,18 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1162 | } | 1174 | } |
1163 | } | 1175 | } |
1164 | 1176 | ||
1177 | data->weight = 0; | ||
1178 | if (type & PERF_SAMPLE_WEIGHT) { | ||
1179 | data->weight = *array; | ||
1180 | array++; | ||
1181 | } | ||
1182 | |||
1183 | data->data_src = PERF_MEM_DATA_SRC_NONE; | ||
1184 | if (type & PERF_SAMPLE_DATA_SRC) { | ||
1185 | data->data_src = *array; | ||
1186 | array++; | ||
1187 | } | ||
1188 | |||
1165 | return 0; | 1189 | return 0; |
1166 | } | 1190 | } |
1167 | 1191 | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 52021c3087df..3f156ccc1acb 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "xyarray.h" | 9 | #include "xyarray.h" |
10 | #include "cgroup.h" | 10 | #include "cgroup.h" |
11 | #include "hist.h" | 11 | #include "hist.h" |
12 | #include "symbol.h" | ||
12 | 13 | ||
13 | struct perf_counts_values { | 14 | struct perf_counts_values { |
14 | union { | 15 | union { |
@@ -120,6 +121,7 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); | |||
120 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 121 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
121 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 122 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); |
122 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 123 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
124 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); | ||
123 | void perf_evsel__free_fd(struct perf_evsel *evsel); | 125 | void perf_evsel__free_fd(struct perf_evsel *evsel); |
124 | void perf_evsel__free_id(struct perf_evsel *evsel); | 126 | void perf_evsel__free_id(struct perf_evsel *evsel); |
125 | void perf_evsel__free_counts(struct perf_evsel *evsel); | 127 | void perf_evsel__free_counts(struct perf_evsel *evsel); |
@@ -246,11 +248,34 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) | |||
246 | return list_entry(evsel->node.next, struct perf_evsel, node); | 248 | return list_entry(evsel->node.next, struct perf_evsel, node); |
247 | } | 249 | } |
248 | 250 | ||
251 | /** | ||
252 | * perf_evsel__is_group_leader - Return whether given evsel is a leader event | ||
253 | * | ||
254 | * @evsel - evsel selector to be tested | ||
255 | * | ||
256 | * Return %true if @evsel is a group leader or a stand-alone event | ||
257 | */ | ||
249 | static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel) | 258 | static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel) |
250 | { | 259 | { |
251 | return evsel->leader == evsel; | 260 | return evsel->leader == evsel; |
252 | } | 261 | } |
253 | 262 | ||
263 | /** | ||
264 | * perf_evsel__is_group_event - Return whether given evsel is a group event | ||
265 | * | ||
266 | * @evsel - evsel selector to be tested | ||
267 | * | ||
268 | * Return %true iff event group view is enabled and @evsel is a actual group | ||
269 | * leader which has other members in the group | ||
270 | */ | ||
271 | static inline bool perf_evsel__is_group_event(struct perf_evsel *evsel) | ||
272 | { | ||
273 | if (!symbol_conf.event_group) | ||
274 | return false; | ||
275 | |||
276 | return perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1; | ||
277 | } | ||
278 | |||
254 | struct perf_attr_details { | 279 | struct perf_attr_details { |
255 | bool freq; | 280 | bool freq; |
256 | bool verbose; | 281 | bool verbose; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f4bfd79ef6a7..326068a593a5 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1,5 +1,3 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
3 | #include "util.h" | 1 | #include "util.h" |
4 | #include <sys/types.h> | 2 | #include <sys/types.h> |
5 | #include <byteswap.h> | 3 | #include <byteswap.h> |
@@ -1672,8 +1670,8 @@ static int process_tracing_data(struct perf_file_section *section __maybe_unused | |||
1672 | struct perf_header *ph __maybe_unused, | 1670 | struct perf_header *ph __maybe_unused, |
1673 | int fd, void *data) | 1671 | int fd, void *data) |
1674 | { | 1672 | { |
1675 | trace_report(fd, data, false); | 1673 | ssize_t ret = trace_report(fd, data, false); |
1676 | return 0; | 1674 | return ret < 0 ? -1 : 0; |
1677 | } | 1675 | } |
1678 | 1676 | ||
1679 | static int process_build_id(struct perf_file_section *section, | 1677 | static int process_build_id(struct perf_file_section *section, |
@@ -2752,6 +2750,11 @@ static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel, | |||
2752 | if (evsel->tp_format) | 2750 | if (evsel->tp_format) |
2753 | return 0; | 2751 | return 0; |
2754 | 2752 | ||
2753 | if (pevent == NULL) { | ||
2754 | pr_debug("broken or missing trace data\n"); | ||
2755 | return -1; | ||
2756 | } | ||
2757 | |||
2755 | event = pevent_find_event(pevent, evsel->attr.config); | 2758 | event = pevent_find_event(pevent, evsel->attr.config); |
2756 | if (event == NULL) | 2759 | if (event == NULL) |
2757 | return -1; | 2760 | return -1; |
@@ -2789,7 +2792,7 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
2789 | u64 f_id; | 2792 | u64 f_id; |
2790 | int nr_attrs, nr_ids, i, j; | 2793 | int nr_attrs, nr_ids, i, j; |
2791 | 2794 | ||
2792 | session->evlist = perf_evlist__new(NULL, NULL); | 2795 | session->evlist = perf_evlist__new(); |
2793 | if (session->evlist == NULL) | 2796 | if (session->evlist == NULL) |
2794 | return -ENOMEM; | 2797 | return -ENOMEM; |
2795 | 2798 | ||
@@ -2940,7 +2943,7 @@ int perf_event__process_attr(union perf_event *event, | |||
2940 | struct perf_evlist *evlist = *pevlist; | 2943 | struct perf_evlist *evlist = *pevlist; |
2941 | 2944 | ||
2942 | if (evlist == NULL) { | 2945 | if (evlist == NULL) { |
2943 | *pevlist = evlist = perf_evlist__new(NULL, NULL); | 2946 | *pevlist = evlist = perf_evlist__new(); |
2944 | if (evlist == NULL) | 2947 | if (evlist == NULL) |
2945 | return -ENOMEM; | 2948 | return -ENOMEM; |
2946 | } | 2949 | } |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f855941bebea..6b32721f829a 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -67,12 +67,16 @@ static void hists__set_unres_dso_col_len(struct hists *hists, int dso) | |||
67 | void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | 67 | void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
68 | { | 68 | { |
69 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 69 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
70 | int symlen; | ||
70 | u16 len; | 71 | u16 len; |
71 | 72 | ||
72 | if (h->ms.sym) | 73 | if (h->ms.sym) |
73 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4); | 74 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4); |
74 | else | 75 | else { |
76 | symlen = unresolved_col_width + 4 + 2; | ||
77 | hists__new_col_len(hists, HISTC_SYMBOL, symlen); | ||
75 | hists__set_unres_dso_col_len(hists, HISTC_DSO); | 78 | hists__set_unres_dso_col_len(hists, HISTC_DSO); |
79 | } | ||
76 | 80 | ||
77 | len = thread__comm_len(h->thread); | 81 | len = thread__comm_len(h->thread); |
78 | if (hists__new_col_len(hists, HISTC_COMM, len)) | 82 | if (hists__new_col_len(hists, HISTC_COMM, len)) |
@@ -87,7 +91,6 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
87 | hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen); | 91 | hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen); |
88 | 92 | ||
89 | if (h->branch_info) { | 93 | if (h->branch_info) { |
90 | int symlen; | ||
91 | /* | 94 | /* |
92 | * +4 accounts for '[x] ' priv level info | 95 | * +4 accounts for '[x] ' priv level info |
93 | * +2 account of 0x prefix on raw addresses | 96 | * +2 account of 0x prefix on raw addresses |
@@ -116,6 +119,42 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
116 | hists__set_unres_dso_col_len(hists, HISTC_DSO_TO); | 119 | hists__set_unres_dso_col_len(hists, HISTC_DSO_TO); |
117 | } | 120 | } |
118 | } | 121 | } |
122 | |||
123 | if (h->mem_info) { | ||
124 | /* | ||
125 | * +4 accounts for '[x] ' priv level info | ||
126 | * +2 account of 0x prefix on raw addresses | ||
127 | */ | ||
128 | if (h->mem_info->daddr.sym) { | ||
129 | symlen = (int)h->mem_info->daddr.sym->namelen + 4 | ||
130 | + unresolved_col_width + 2; | ||
131 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, | ||
132 | symlen); | ||
133 | } else { | ||
134 | symlen = unresolved_col_width + 4 + 2; | ||
135 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, | ||
136 | symlen); | ||
137 | } | ||
138 | if (h->mem_info->daddr.map) { | ||
139 | symlen = dso__name_len(h->mem_info->daddr.map->dso); | ||
140 | hists__new_col_len(hists, HISTC_MEM_DADDR_DSO, | ||
141 | symlen); | ||
142 | } else { | ||
143 | symlen = unresolved_col_width + 4 + 2; | ||
144 | hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); | ||
145 | } | ||
146 | } else { | ||
147 | symlen = unresolved_col_width + 4 + 2; | ||
148 | hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, symlen); | ||
149 | hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO); | ||
150 | } | ||
151 | |||
152 | hists__new_col_len(hists, HISTC_MEM_LOCKED, 6); | ||
153 | hists__new_col_len(hists, HISTC_MEM_TLB, 22); | ||
154 | hists__new_col_len(hists, HISTC_MEM_SNOOP, 12); | ||
155 | hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); | ||
156 | hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); | ||
157 | hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); | ||
119 | } | 158 | } |
120 | 159 | ||
121 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 160 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
@@ -155,9 +194,12 @@ static void hist_entry__add_cpumode_period(struct hist_entry *he, | |||
155 | } | 194 | } |
156 | } | 195 | } |
157 | 196 | ||
158 | static void he_stat__add_period(struct he_stat *he_stat, u64 period) | 197 | static void he_stat__add_period(struct he_stat *he_stat, u64 period, |
198 | u64 weight) | ||
159 | { | 199 | { |
200 | |||
160 | he_stat->period += period; | 201 | he_stat->period += period; |
202 | he_stat->weight += weight; | ||
161 | he_stat->nr_events += 1; | 203 | he_stat->nr_events += 1; |
162 | } | 204 | } |
163 | 205 | ||
@@ -169,12 +211,14 @@ static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src) | |||
169 | dest->period_guest_sys += src->period_guest_sys; | 211 | dest->period_guest_sys += src->period_guest_sys; |
170 | dest->period_guest_us += src->period_guest_us; | 212 | dest->period_guest_us += src->period_guest_us; |
171 | dest->nr_events += src->nr_events; | 213 | dest->nr_events += src->nr_events; |
214 | dest->weight += src->weight; | ||
172 | } | 215 | } |
173 | 216 | ||
174 | static void hist_entry__decay(struct hist_entry *he) | 217 | static void hist_entry__decay(struct hist_entry *he) |
175 | { | 218 | { |
176 | he->stat.period = (he->stat.period * 7) / 8; | 219 | he->stat.period = (he->stat.period * 7) / 8; |
177 | he->stat.nr_events = (he->stat.nr_events * 7) / 8; | 220 | he->stat.nr_events = (he->stat.nr_events * 7) / 8; |
221 | /* XXX need decay for weight too? */ | ||
178 | } | 222 | } |
179 | 223 | ||
180 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | 224 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) |
@@ -239,7 +283,7 @@ void hists__decay_entries_threaded(struct hists *hists, | |||
239 | static struct hist_entry *hist_entry__new(struct hist_entry *template) | 283 | static struct hist_entry *hist_entry__new(struct hist_entry *template) |
240 | { | 284 | { |
241 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; | 285 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; |
242 | struct hist_entry *he = malloc(sizeof(*he) + callchain_size); | 286 | struct hist_entry *he = zalloc(sizeof(*he) + callchain_size); |
243 | 287 | ||
244 | if (he != NULL) { | 288 | if (he != NULL) { |
245 | *he = *template; | 289 | *he = *template; |
@@ -254,6 +298,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
254 | he->branch_info->to.map->referenced = true; | 298 | he->branch_info->to.map->referenced = true; |
255 | } | 299 | } |
256 | 300 | ||
301 | if (he->mem_info) { | ||
302 | if (he->mem_info->iaddr.map) | ||
303 | he->mem_info->iaddr.map->referenced = true; | ||
304 | if (he->mem_info->daddr.map) | ||
305 | he->mem_info->daddr.map->referenced = true; | ||
306 | } | ||
307 | |||
257 | if (symbol_conf.use_callchain) | 308 | if (symbol_conf.use_callchain) |
258 | callchain_init(he->callchain); | 309 | callchain_init(he->callchain); |
259 | 310 | ||
@@ -282,7 +333,8 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
282 | static struct hist_entry *add_hist_entry(struct hists *hists, | 333 | static struct hist_entry *add_hist_entry(struct hists *hists, |
283 | struct hist_entry *entry, | 334 | struct hist_entry *entry, |
284 | struct addr_location *al, | 335 | struct addr_location *al, |
285 | u64 period) | 336 | u64 period, |
337 | u64 weight) | ||
286 | { | 338 | { |
287 | struct rb_node **p; | 339 | struct rb_node **p; |
288 | struct rb_node *parent = NULL; | 340 | struct rb_node *parent = NULL; |
@@ -306,7 +358,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
306 | cmp = hist_entry__cmp(he, entry); | 358 | cmp = hist_entry__cmp(he, entry); |
307 | 359 | ||
308 | if (!cmp) { | 360 | if (!cmp) { |
309 | he_stat__add_period(&he->stat, period); | 361 | he_stat__add_period(&he->stat, period, weight); |
310 | 362 | ||
311 | /* If the map of an existing hist_entry has | 363 | /* If the map of an existing hist_entry has |
312 | * become out-of-date due to an exec() or | 364 | * become out-of-date due to an exec() or |
@@ -341,11 +393,42 @@ out_unlock: | |||
341 | return he; | 393 | return he; |
342 | } | 394 | } |
343 | 395 | ||
396 | struct hist_entry *__hists__add_mem_entry(struct hists *self, | ||
397 | struct addr_location *al, | ||
398 | struct symbol *sym_parent, | ||
399 | struct mem_info *mi, | ||
400 | u64 period, | ||
401 | u64 weight) | ||
402 | { | ||
403 | struct hist_entry entry = { | ||
404 | .thread = al->thread, | ||
405 | .ms = { | ||
406 | .map = al->map, | ||
407 | .sym = al->sym, | ||
408 | }, | ||
409 | .stat = { | ||
410 | .period = period, | ||
411 | .weight = weight, | ||
412 | .nr_events = 1, | ||
413 | }, | ||
414 | .cpu = al->cpu, | ||
415 | .ip = al->addr, | ||
416 | .level = al->level, | ||
417 | .parent = sym_parent, | ||
418 | .filtered = symbol__parent_filter(sym_parent), | ||
419 | .hists = self, | ||
420 | .mem_info = mi, | ||
421 | .branch_info = NULL, | ||
422 | }; | ||
423 | return add_hist_entry(self, &entry, al, period, weight); | ||
424 | } | ||
425 | |||
344 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | 426 | struct hist_entry *__hists__add_branch_entry(struct hists *self, |
345 | struct addr_location *al, | 427 | struct addr_location *al, |
346 | struct symbol *sym_parent, | 428 | struct symbol *sym_parent, |
347 | struct branch_info *bi, | 429 | struct branch_info *bi, |
348 | u64 period) | 430 | u64 period, |
431 | u64 weight) | ||
349 | { | 432 | { |
350 | struct hist_entry entry = { | 433 | struct hist_entry entry = { |
351 | .thread = al->thread, | 434 | .thread = al->thread, |
@@ -359,19 +442,22 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, | |||
359 | .stat = { | 442 | .stat = { |
360 | .period = period, | 443 | .period = period, |
361 | .nr_events = 1, | 444 | .nr_events = 1, |
445 | .weight = weight, | ||
362 | }, | 446 | }, |
363 | .parent = sym_parent, | 447 | .parent = sym_parent, |
364 | .filtered = symbol__parent_filter(sym_parent), | 448 | .filtered = symbol__parent_filter(sym_parent), |
365 | .branch_info = bi, | 449 | .branch_info = bi, |
366 | .hists = self, | 450 | .hists = self, |
451 | .mem_info = NULL, | ||
367 | }; | 452 | }; |
368 | 453 | ||
369 | return add_hist_entry(self, &entry, al, period); | 454 | return add_hist_entry(self, &entry, al, period, weight); |
370 | } | 455 | } |
371 | 456 | ||
372 | struct hist_entry *__hists__add_entry(struct hists *self, | 457 | struct hist_entry *__hists__add_entry(struct hists *self, |
373 | struct addr_location *al, | 458 | struct addr_location *al, |
374 | struct symbol *sym_parent, u64 period) | 459 | struct symbol *sym_parent, u64 period, |
460 | u64 weight) | ||
375 | { | 461 | { |
376 | struct hist_entry entry = { | 462 | struct hist_entry entry = { |
377 | .thread = al->thread, | 463 | .thread = al->thread, |
@@ -385,13 +471,16 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
385 | .stat = { | 471 | .stat = { |
386 | .period = period, | 472 | .period = period, |
387 | .nr_events = 1, | 473 | .nr_events = 1, |
474 | .weight = weight, | ||
388 | }, | 475 | }, |
389 | .parent = sym_parent, | 476 | .parent = sym_parent, |
390 | .filtered = symbol__parent_filter(sym_parent), | 477 | .filtered = symbol__parent_filter(sym_parent), |
391 | .hists = self, | 478 | .hists = self, |
479 | .branch_info = NULL, | ||
480 | .mem_info = NULL, | ||
392 | }; | 481 | }; |
393 | 482 | ||
394 | return add_hist_entry(self, &entry, al, period); | 483 | return add_hist_entry(self, &entry, al, period, weight); |
395 | } | 484 | } |
396 | 485 | ||
397 | int64_t | 486 | int64_t |
@@ -431,6 +520,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
431 | void hist_entry__free(struct hist_entry *he) | 520 | void hist_entry__free(struct hist_entry *he) |
432 | { | 521 | { |
433 | free(he->branch_info); | 522 | free(he->branch_info); |
523 | free(he->mem_info); | ||
434 | free(he); | 524 | free(he); |
435 | } | 525 | } |
436 | 526 | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 226a4ae2f936..14c2fe20aa62 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -49,6 +49,14 @@ enum hist_column { | |||
49 | HISTC_DSO_FROM, | 49 | HISTC_DSO_FROM, |
50 | HISTC_DSO_TO, | 50 | HISTC_DSO_TO, |
51 | HISTC_SRCLINE, | 51 | HISTC_SRCLINE, |
52 | HISTC_LOCAL_WEIGHT, | ||
53 | HISTC_GLOBAL_WEIGHT, | ||
54 | HISTC_MEM_DADDR_SYMBOL, | ||
55 | HISTC_MEM_DADDR_DSO, | ||
56 | HISTC_MEM_LOCKED, | ||
57 | HISTC_MEM_TLB, | ||
58 | HISTC_MEM_LVL, | ||
59 | HISTC_MEM_SNOOP, | ||
52 | HISTC_NR_COLS, /* Last entry */ | 60 | HISTC_NR_COLS, /* Last entry */ |
53 | }; | 61 | }; |
54 | 62 | ||
@@ -73,7 +81,8 @@ struct hists { | |||
73 | 81 | ||
74 | struct hist_entry *__hists__add_entry(struct hists *self, | 82 | struct hist_entry *__hists__add_entry(struct hists *self, |
75 | struct addr_location *al, | 83 | struct addr_location *al, |
76 | struct symbol *parent, u64 period); | 84 | struct symbol *parent, u64 period, |
85 | u64 weight); | ||
77 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); | 86 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); |
78 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); | 87 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); |
79 | int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, | 88 | int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size, |
@@ -84,7 +93,15 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, | |||
84 | struct addr_location *al, | 93 | struct addr_location *al, |
85 | struct symbol *sym_parent, | 94 | struct symbol *sym_parent, |
86 | struct branch_info *bi, | 95 | struct branch_info *bi, |
87 | u64 period); | 96 | u64 period, |
97 | u64 weight); | ||
98 | |||
99 | struct hist_entry *__hists__add_mem_entry(struct hists *self, | ||
100 | struct addr_location *al, | ||
101 | struct symbol *sym_parent, | ||
102 | struct mem_info *mi, | ||
103 | u64 period, | ||
104 | u64 weight); | ||
88 | 105 | ||
89 | void hists__output_resort(struct hists *self); | 106 | void hists__output_resort(struct hists *self); |
90 | void hists__output_resort_threaded(struct hists *hists); | 107 | void hists__output_resort_threaded(struct hists *hists); |
@@ -175,9 +192,9 @@ struct hist_browser_timer { | |||
175 | int refresh; | 192 | int refresh; |
176 | }; | 193 | }; |
177 | 194 | ||
178 | #ifdef NEWT_SUPPORT | 195 | #ifdef SLANG_SUPPORT |
179 | #include "../ui/keysyms.h" | 196 | #include "../ui/keysyms.h" |
180 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | 197 | int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel, |
181 | struct hist_browser_timer *hbt); | 198 | struct hist_browser_timer *hbt); |
182 | 199 | ||
183 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 200 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
@@ -196,7 +213,8 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
196 | 213 | ||
197 | static inline int hist_entry__tui_annotate(struct hist_entry *self | 214 | static inline int hist_entry__tui_annotate(struct hist_entry *self |
198 | __maybe_unused, | 215 | __maybe_unused, |
199 | int evidx __maybe_unused, | 216 | struct perf_evsel *evsel |
217 | __maybe_unused, | ||
200 | struct hist_browser_timer *hbt | 218 | struct hist_browser_timer *hbt |
201 | __maybe_unused) | 219 | __maybe_unused) |
202 | { | 220 | { |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index efdb38e65a92..b2ecad6ec46b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -955,6 +955,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
955 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 955 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
956 | struct thread *thread; | 956 | struct thread *thread; |
957 | struct map *map; | 957 | struct map *map; |
958 | enum map_type type; | ||
958 | int ret = 0; | 959 | int ret = 0; |
959 | 960 | ||
960 | if (dump_trace) | 961 | if (dump_trace) |
@@ -971,10 +972,17 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
971 | thread = machine__findnew_thread(machine, event->mmap.pid); | 972 | thread = machine__findnew_thread(machine, event->mmap.pid); |
972 | if (thread == NULL) | 973 | if (thread == NULL) |
973 | goto out_problem; | 974 | goto out_problem; |
975 | |||
976 | if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) | ||
977 | type = MAP__VARIABLE; | ||
978 | else | ||
979 | type = MAP__FUNCTION; | ||
980 | |||
974 | map = map__new(&machine->user_dsos, event->mmap.start, | 981 | map = map__new(&machine->user_dsos, event->mmap.start, |
975 | event->mmap.len, event->mmap.pgoff, | 982 | event->mmap.len, event->mmap.pgoff, |
976 | event->mmap.pid, event->mmap.filename, | 983 | event->mmap.pid, event->mmap.filename, |
977 | MAP__FUNCTION); | 984 | type); |
985 | |||
978 | if (map == NULL) | 986 | if (map == NULL) |
979 | goto out_problem; | 987 | goto out_problem; |
980 | 988 | ||
@@ -1003,6 +1011,17 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
1003 | return 0; | 1011 | return 0; |
1004 | } | 1012 | } |
1005 | 1013 | ||
1014 | static void machine__remove_thread(struct machine *machine, struct thread *th) | ||
1015 | { | ||
1016 | machine->last_match = NULL; | ||
1017 | rb_erase(&th->rb_node, &machine->threads); | ||
1018 | /* | ||
1019 | * We may have references to this thread, for instance in some hist_entry | ||
1020 | * instances, so just move them to a separate list. | ||
1021 | */ | ||
1022 | list_add_tail(&th->node, &machine->dead_threads); | ||
1023 | } | ||
1024 | |||
1006 | int machine__process_exit_event(struct machine *machine, union perf_event *event) | 1025 | int machine__process_exit_event(struct machine *machine, union perf_event *event) |
1007 | { | 1026 | { |
1008 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1027 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
@@ -1039,17 +1058,6 @@ int machine__process_event(struct machine *machine, union perf_event *event) | |||
1039 | return ret; | 1058 | return ret; |
1040 | } | 1059 | } |
1041 | 1060 | ||
1042 | void machine__remove_thread(struct machine *machine, struct thread *th) | ||
1043 | { | ||
1044 | machine->last_match = NULL; | ||
1045 | rb_erase(&th->rb_node, &machine->threads); | ||
1046 | /* | ||
1047 | * We may have references to this thread, for instance in some hist_entry | ||
1048 | * instances, so just move them to a separate list. | ||
1049 | */ | ||
1050 | list_add_tail(&th->node, &machine->dead_threads); | ||
1051 | } | ||
1052 | |||
1053 | static bool symbol__match_parent_regex(struct symbol *sym) | 1061 | static bool symbol__match_parent_regex(struct symbol *sym) |
1054 | { | 1062 | { |
1055 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | 1063 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) |
@@ -1097,6 +1105,38 @@ found: | |||
1097 | ams->map = al.map; | 1105 | ams->map = al.map; |
1098 | } | 1106 | } |
1099 | 1107 | ||
1108 | static void ip__resolve_data(struct machine *machine, struct thread *thread, | ||
1109 | u8 m, struct addr_map_symbol *ams, u64 addr) | ||
1110 | { | ||
1111 | struct addr_location al; | ||
1112 | |||
1113 | memset(&al, 0, sizeof(al)); | ||
1114 | |||
1115 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, &al, | ||
1116 | NULL); | ||
1117 | ams->addr = addr; | ||
1118 | ams->al_addr = al.addr; | ||
1119 | ams->sym = al.sym; | ||
1120 | ams->map = al.map; | ||
1121 | } | ||
1122 | |||
1123 | struct mem_info *machine__resolve_mem(struct machine *machine, | ||
1124 | struct thread *thr, | ||
1125 | struct perf_sample *sample, | ||
1126 | u8 cpumode) | ||
1127 | { | ||
1128 | struct mem_info *mi = zalloc(sizeof(*mi)); | ||
1129 | |||
1130 | if (!mi) | ||
1131 | return NULL; | ||
1132 | |||
1133 | ip__resolve_ams(machine, thr, &mi->iaddr, sample->ip); | ||
1134 | ip__resolve_data(machine, thr, cpumode, &mi->daddr, sample->addr); | ||
1135 | mi->data_src.val = sample->data_src; | ||
1136 | |||
1137 | return mi; | ||
1138 | } | ||
1139 | |||
1100 | struct branch_info *machine__resolve_bstack(struct machine *machine, | 1140 | struct branch_info *machine__resolve_bstack(struct machine *machine, |
1101 | struct thread *thr, | 1141 | struct thread *thr, |
1102 | struct branch_stack *bs) | 1142 | struct branch_stack *bs) |
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 5ac5892f2326..77940680f1fc 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -76,6 +76,9 @@ void machine__delete(struct machine *machine); | |||
76 | struct branch_info *machine__resolve_bstack(struct machine *machine, | 76 | struct branch_info *machine__resolve_bstack(struct machine *machine, |
77 | struct thread *thread, | 77 | struct thread *thread, |
78 | struct branch_stack *bs); | 78 | struct branch_stack *bs); |
79 | struct mem_info *machine__resolve_mem(struct machine *machine, | ||
80 | struct thread *thread, | ||
81 | struct perf_sample *sample, u8 cpumode); | ||
79 | int machine__resolve_callchain(struct machine *machine, | 82 | int machine__resolve_callchain(struct machine *machine, |
80 | struct perf_evsel *evsel, | 83 | struct perf_evsel *evsel, |
81 | struct thread *thread, | 84 | struct thread *thread, |
@@ -97,7 +100,6 @@ static inline bool machine__is_host(struct machine *machine) | |||
97 | } | 100 | } |
98 | 101 | ||
99 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); | 102 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); |
100 | void machine__remove_thread(struct machine *machine, struct thread *th); | ||
101 | 103 | ||
102 | size_t machine__fprintf(struct machine *machine, FILE *fp); | 104 | size_t machine__fprintf(struct machine *machine, FILE *fp); |
103 | 105 | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index c84f48cf9678..6c8bb0fb189b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -10,7 +10,7 @@ | |||
10 | #include "symbol.h" | 10 | #include "symbol.h" |
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
13 | #include "debugfs.h" | 13 | #include <lk/debugfs.h> |
14 | #include "parse-events-bison.h" | 14 | #include "parse-events-bison.h" |
15 | #define YY_EXTRA_TYPE int | 15 | #define YY_EXTRA_TYPE int |
16 | #include "parse-events-flex.h" | 16 | #include "parse-events-flex.h" |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 49a256e6e0a2..aa04bf9c9ad7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -40,7 +40,7 @@ | |||
40 | #include "color.h" | 40 | #include "color.h" |
41 | #include "symbol.h" | 41 | #include "symbol.h" |
42 | #include "thread.h" | 42 | #include "thread.h" |
43 | #include "debugfs.h" | 43 | #include <lk/debugfs.h> |
44 | #include "trace-event.h" /* For __maybe_unused */ | 44 | #include "trace-event.h" /* For __maybe_unused */ |
45 | #include "probe-event.h" | 45 | #include "probe-event.h" |
46 | #include "probe-finder.h" | 46 | #include "probe-finder.h" |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 64536a993f4a..f75ae1b9900c 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
@@ -15,7 +15,6 @@ util/thread_map.c | |||
15 | util/util.c | 15 | util/util.c |
16 | util/xyarray.c | 16 | util/xyarray.c |
17 | util/cgroup.c | 17 | util/cgroup.c |
18 | util/debugfs.c | ||
19 | util/rblist.c | 18 | util/rblist.c |
20 | util/strlist.c | 19 | util/strlist.c |
21 | util/sysfs.c | 20 | util/sysfs.c |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index bd85280bb6e8..cf1fe01b7e89 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1,5 +1,3 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
3 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
4 | 2 | ||
5 | #include <byteswap.h> | 3 | #include <byteswap.h> |
@@ -800,6 +798,12 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, | |||
800 | 798 | ||
801 | if (sample_type & PERF_SAMPLE_STACK_USER) | 799 | if (sample_type & PERF_SAMPLE_STACK_USER) |
802 | stack_user__printf(&sample->user_stack); | 800 | stack_user__printf(&sample->user_stack); |
801 | |||
802 | if (sample_type & PERF_SAMPLE_WEIGHT) | ||
803 | printf("... weight: %" PRIu64 "\n", sample->weight); | ||
804 | |||
805 | if (sample_type & PERF_SAMPLE_DATA_SRC) | ||
806 | printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); | ||
803 | } | 807 | } |
804 | 808 | ||
805 | static struct machine * | 809 | static struct machine * |
@@ -1365,18 +1369,6 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp) | |||
1365 | return machine__fprintf(&session->machines.host, fp); | 1369 | return machine__fprintf(&session->machines.host, fp); |
1366 | } | 1370 | } |
1367 | 1371 | ||
1368 | void perf_session__remove_thread(struct perf_session *session, | ||
1369 | struct thread *th) | ||
1370 | { | ||
1371 | /* | ||
1372 | * FIXME: This one makes no sense, we need to remove the thread from | ||
1373 | * the machine it belongs to, perf_session can have many machines, so | ||
1374 | * doing it always on ->machines.host is wrong. Fix when auditing all | ||
1375 | * the 'perf kvm' code. | ||
1376 | */ | ||
1377 | machine__remove_thread(&session->machines.host, th); | ||
1378 | } | ||
1379 | |||
1380 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | 1372 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, |
1381 | unsigned int type) | 1373 | unsigned int type) |
1382 | { | 1374 | { |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index b5c0847edfa9..6b51d47acdba 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -72,7 +72,6 @@ void perf_event__attr_swap(struct perf_event_attr *attr); | |||
72 | int perf_session__create_kernel_maps(struct perf_session *self); | 72 | int perf_session__create_kernel_maps(struct perf_session *self); |
73 | 73 | ||
74 | void perf_session__set_id_hdr_size(struct perf_session *session); | 74 | void perf_session__set_id_hdr_size(struct perf_session *session); |
75 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); | ||
76 | 75 | ||
77 | static inline | 76 | static inline |
78 | struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) | 77 | struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 73d510269784..6b0ed322907e 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -24,6 +24,7 @@ cflags += getenv('CFLAGS', '').split() | |||
24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | 24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') |
25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | 25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') |
26 | libtraceevent = getenv('LIBTRACEEVENT') | 26 | libtraceevent = getenv('LIBTRACEEVENT') |
27 | liblk = getenv('LIBLK') | ||
27 | 28 | ||
28 | ext_sources = [f.strip() for f in file('util/python-ext-sources') | 29 | ext_sources = [f.strip() for f in file('util/python-ext-sources') |
29 | if len(f.strip()) > 0 and f[0] != '#'] | 30 | if len(f.strip()) > 0 and f[0] != '#'] |
@@ -32,7 +33,7 @@ perf = Extension('perf', | |||
32 | sources = ext_sources, | 33 | sources = ext_sources, |
33 | include_dirs = ['util/include'], | 34 | include_dirs = ['util/include'], |
34 | extra_compile_args = cflags, | 35 | extra_compile_args = cflags, |
35 | extra_objects = [libtraceevent], | 36 | extra_objects = [libtraceevent, liblk], |
36 | ) | 37 | ) |
37 | 38 | ||
38 | setup(name='perf', | 39 | setup(name='perf', |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d41926cb9e3f..5f52d492590c 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -198,11 +198,19 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
198 | } | 198 | } |
199 | 199 | ||
200 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); | 200 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); |
201 | if (sym) | 201 | if (sym && map) { |
202 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | 202 | if (map->type == MAP__VARIABLE) { |
203 | width - ret, | 203 | ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name); |
204 | sym->name); | 204 | ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", |
205 | else { | 205 | ip - map->unmap_ip(map, sym->start)); |
206 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
207 | width - ret, ""); | ||
208 | } else { | ||
209 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
210 | width - ret, | ||
211 | sym->name); | ||
212 | } | ||
213 | } else { | ||
206 | size_t len = BITS_PER_LONG / 4; | 214 | size_t len = BITS_PER_LONG / 4; |
207 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", | 215 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", |
208 | len, ip); | 216 | len, ip); |
@@ -457,6 +465,304 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, | |||
457 | return repsep_snprintf(bf, size, "%-*s", width, out); | 465 | return repsep_snprintf(bf, size, "%-*s", width, out); |
458 | } | 466 | } |
459 | 467 | ||
468 | /* --sort daddr_sym */ | ||
469 | static int64_t | ||
470 | sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right) | ||
471 | { | ||
472 | uint64_t l = 0, r = 0; | ||
473 | |||
474 | if (left->mem_info) | ||
475 | l = left->mem_info->daddr.addr; | ||
476 | if (right->mem_info) | ||
477 | r = right->mem_info->daddr.addr; | ||
478 | |||
479 | return (int64_t)(r - l); | ||
480 | } | ||
481 | |||
482 | static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf, | ||
483 | size_t size, unsigned int width) | ||
484 | { | ||
485 | uint64_t addr = 0; | ||
486 | struct map *map = NULL; | ||
487 | struct symbol *sym = NULL; | ||
488 | |||
489 | if (self->mem_info) { | ||
490 | addr = self->mem_info->daddr.addr; | ||
491 | map = self->mem_info->daddr.map; | ||
492 | sym = self->mem_info->daddr.sym; | ||
493 | } | ||
494 | return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size, | ||
495 | width); | ||
496 | } | ||
497 | |||
498 | static int64_t | ||
499 | sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right) | ||
500 | { | ||
501 | struct map *map_l = NULL; | ||
502 | struct map *map_r = NULL; | ||
503 | |||
504 | if (left->mem_info) | ||
505 | map_l = left->mem_info->daddr.map; | ||
506 | if (right->mem_info) | ||
507 | map_r = right->mem_info->daddr.map; | ||
508 | |||
509 | return _sort__dso_cmp(map_l, map_r); | ||
510 | } | ||
511 | |||
512 | static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf, | ||
513 | size_t size, unsigned int width) | ||
514 | { | ||
515 | struct map *map = NULL; | ||
516 | |||
517 | if (self->mem_info) | ||
518 | map = self->mem_info->daddr.map; | ||
519 | |||
520 | return _hist_entry__dso_snprintf(map, bf, size, width); | ||
521 | } | ||
522 | |||
523 | static int64_t | ||
524 | sort__locked_cmp(struct hist_entry *left, struct hist_entry *right) | ||
525 | { | ||
526 | union perf_mem_data_src data_src_l; | ||
527 | union perf_mem_data_src data_src_r; | ||
528 | |||
529 | if (left->mem_info) | ||
530 | data_src_l = left->mem_info->data_src; | ||
531 | else | ||
532 | data_src_l.mem_lock = PERF_MEM_LOCK_NA; | ||
533 | |||
534 | if (right->mem_info) | ||
535 | data_src_r = right->mem_info->data_src; | ||
536 | else | ||
537 | data_src_r.mem_lock = PERF_MEM_LOCK_NA; | ||
538 | |||
539 | return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock); | ||
540 | } | ||
541 | |||
542 | static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf, | ||
543 | size_t size, unsigned int width) | ||
544 | { | ||
545 | const char *out; | ||
546 | u64 mask = PERF_MEM_LOCK_NA; | ||
547 | |||
548 | if (self->mem_info) | ||
549 | mask = self->mem_info->data_src.mem_lock; | ||
550 | |||
551 | if (mask & PERF_MEM_LOCK_NA) | ||
552 | out = "N/A"; | ||
553 | else if (mask & PERF_MEM_LOCK_LOCKED) | ||
554 | out = "Yes"; | ||
555 | else | ||
556 | out = "No"; | ||
557 | |||
558 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
559 | } | ||
560 | |||
561 | static int64_t | ||
562 | sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right) | ||
563 | { | ||
564 | union perf_mem_data_src data_src_l; | ||
565 | union perf_mem_data_src data_src_r; | ||
566 | |||
567 | if (left->mem_info) | ||
568 | data_src_l = left->mem_info->data_src; | ||
569 | else | ||
570 | data_src_l.mem_dtlb = PERF_MEM_TLB_NA; | ||
571 | |||
572 | if (right->mem_info) | ||
573 | data_src_r = right->mem_info->data_src; | ||
574 | else | ||
575 | data_src_r.mem_dtlb = PERF_MEM_TLB_NA; | ||
576 | |||
577 | return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb); | ||
578 | } | ||
579 | |||
580 | static const char * const tlb_access[] = { | ||
581 | "N/A", | ||
582 | "HIT", | ||
583 | "MISS", | ||
584 | "L1", | ||
585 | "L2", | ||
586 | "Walker", | ||
587 | "Fault", | ||
588 | }; | ||
589 | #define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *)) | ||
590 | |||
591 | static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf, | ||
592 | size_t size, unsigned int width) | ||
593 | { | ||
594 | char out[64]; | ||
595 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
596 | size_t l = 0, i; | ||
597 | u64 m = PERF_MEM_TLB_NA; | ||
598 | u64 hit, miss; | ||
599 | |||
600 | out[0] = '\0'; | ||
601 | |||
602 | if (self->mem_info) | ||
603 | m = self->mem_info->data_src.mem_dtlb; | ||
604 | |||
605 | hit = m & PERF_MEM_TLB_HIT; | ||
606 | miss = m & PERF_MEM_TLB_MISS; | ||
607 | |||
608 | /* already taken care of */ | ||
609 | m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS); | ||
610 | |||
611 | for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) { | ||
612 | if (!(m & 0x1)) | ||
613 | continue; | ||
614 | if (l) { | ||
615 | strcat(out, " or "); | ||
616 | l += 4; | ||
617 | } | ||
618 | strncat(out, tlb_access[i], sz - l); | ||
619 | l += strlen(tlb_access[i]); | ||
620 | } | ||
621 | if (*out == '\0') | ||
622 | strcpy(out, "N/A"); | ||
623 | if (hit) | ||
624 | strncat(out, " hit", sz - l); | ||
625 | if (miss) | ||
626 | strncat(out, " miss", sz - l); | ||
627 | |||
628 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
629 | } | ||
630 | |||
631 | static int64_t | ||
632 | sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right) | ||
633 | { | ||
634 | union perf_mem_data_src data_src_l; | ||
635 | union perf_mem_data_src data_src_r; | ||
636 | |||
637 | if (left->mem_info) | ||
638 | data_src_l = left->mem_info->data_src; | ||
639 | else | ||
640 | data_src_l.mem_lvl = PERF_MEM_LVL_NA; | ||
641 | |||
642 | if (right->mem_info) | ||
643 | data_src_r = right->mem_info->data_src; | ||
644 | else | ||
645 | data_src_r.mem_lvl = PERF_MEM_LVL_NA; | ||
646 | |||
647 | return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl); | ||
648 | } | ||
649 | |||
650 | static const char * const mem_lvl[] = { | ||
651 | "N/A", | ||
652 | "HIT", | ||
653 | "MISS", | ||
654 | "L1", | ||
655 | "LFB", | ||
656 | "L2", | ||
657 | "L3", | ||
658 | "Local RAM", | ||
659 | "Remote RAM (1 hop)", | ||
660 | "Remote RAM (2 hops)", | ||
661 | "Remote Cache (1 hop)", | ||
662 | "Remote Cache (2 hops)", | ||
663 | "I/O", | ||
664 | "Uncached", | ||
665 | }; | ||
666 | #define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *)) | ||
667 | |||
668 | static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf, | ||
669 | size_t size, unsigned int width) | ||
670 | { | ||
671 | char out[64]; | ||
672 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
673 | size_t i, l = 0; | ||
674 | u64 m = PERF_MEM_LVL_NA; | ||
675 | u64 hit, miss; | ||
676 | |||
677 | if (self->mem_info) | ||
678 | m = self->mem_info->data_src.mem_lvl; | ||
679 | |||
680 | out[0] = '\0'; | ||
681 | |||
682 | hit = m & PERF_MEM_LVL_HIT; | ||
683 | miss = m & PERF_MEM_LVL_MISS; | ||
684 | |||
685 | /* already taken care of */ | ||
686 | m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS); | ||
687 | |||
688 | for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) { | ||
689 | if (!(m & 0x1)) | ||
690 | continue; | ||
691 | if (l) { | ||
692 | strcat(out, " or "); | ||
693 | l += 4; | ||
694 | } | ||
695 | strncat(out, mem_lvl[i], sz - l); | ||
696 | l += strlen(mem_lvl[i]); | ||
697 | } | ||
698 | if (*out == '\0') | ||
699 | strcpy(out, "N/A"); | ||
700 | if (hit) | ||
701 | strncat(out, " hit", sz - l); | ||
702 | if (miss) | ||
703 | strncat(out, " miss", sz - l); | ||
704 | |||
705 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
706 | } | ||
707 | |||
708 | static int64_t | ||
709 | sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right) | ||
710 | { | ||
711 | union perf_mem_data_src data_src_l; | ||
712 | union perf_mem_data_src data_src_r; | ||
713 | |||
714 | if (left->mem_info) | ||
715 | data_src_l = left->mem_info->data_src; | ||
716 | else | ||
717 | data_src_l.mem_snoop = PERF_MEM_SNOOP_NA; | ||
718 | |||
719 | if (right->mem_info) | ||
720 | data_src_r = right->mem_info->data_src; | ||
721 | else | ||
722 | data_src_r.mem_snoop = PERF_MEM_SNOOP_NA; | ||
723 | |||
724 | return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop); | ||
725 | } | ||
726 | |||
727 | static const char * const snoop_access[] = { | ||
728 | "N/A", | ||
729 | "None", | ||
730 | "Miss", | ||
731 | "Hit", | ||
732 | "HitM", | ||
733 | }; | ||
734 | #define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *)) | ||
735 | |||
736 | static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf, | ||
737 | size_t size, unsigned int width) | ||
738 | { | ||
739 | char out[64]; | ||
740 | size_t sz = sizeof(out) - 1; /* -1 for null termination */ | ||
741 | size_t i, l = 0; | ||
742 | u64 m = PERF_MEM_SNOOP_NA; | ||
743 | |||
744 | out[0] = '\0'; | ||
745 | |||
746 | if (self->mem_info) | ||
747 | m = self->mem_info->data_src.mem_snoop; | ||
748 | |||
749 | for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) { | ||
750 | if (!(m & 0x1)) | ||
751 | continue; | ||
752 | if (l) { | ||
753 | strcat(out, " or "); | ||
754 | l += 4; | ||
755 | } | ||
756 | strncat(out, snoop_access[i], sz - l); | ||
757 | l += strlen(snoop_access[i]); | ||
758 | } | ||
759 | |||
760 | if (*out == '\0') | ||
761 | strcpy(out, "N/A"); | ||
762 | |||
763 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
764 | } | ||
765 | |||
460 | struct sort_entry sort_mispredict = { | 766 | struct sort_entry sort_mispredict = { |
461 | .se_header = "Branch Mispredicted", | 767 | .se_header = "Branch Mispredicted", |
462 | .se_cmp = sort__mispredict_cmp, | 768 | .se_cmp = sort__mispredict_cmp, |
@@ -464,6 +770,91 @@ struct sort_entry sort_mispredict = { | |||
464 | .se_width_idx = HISTC_MISPREDICT, | 770 | .se_width_idx = HISTC_MISPREDICT, |
465 | }; | 771 | }; |
466 | 772 | ||
773 | static u64 he_weight(struct hist_entry *he) | ||
774 | { | ||
775 | return he->stat.nr_events ? he->stat.weight / he->stat.nr_events : 0; | ||
776 | } | ||
777 | |||
778 | static int64_t | ||
779 | sort__local_weight_cmp(struct hist_entry *left, struct hist_entry *right) | ||
780 | { | ||
781 | return he_weight(left) - he_weight(right); | ||
782 | } | ||
783 | |||
784 | static int hist_entry__local_weight_snprintf(struct hist_entry *self, char *bf, | ||
785 | size_t size, unsigned int width) | ||
786 | { | ||
787 | return repsep_snprintf(bf, size, "%-*llu", width, he_weight(self)); | ||
788 | } | ||
789 | |||
790 | struct sort_entry sort_local_weight = { | ||
791 | .se_header = "Local Weight", | ||
792 | .se_cmp = sort__local_weight_cmp, | ||
793 | .se_snprintf = hist_entry__local_weight_snprintf, | ||
794 | .se_width_idx = HISTC_LOCAL_WEIGHT, | ||
795 | }; | ||
796 | |||
797 | static int64_t | ||
798 | sort__global_weight_cmp(struct hist_entry *left, struct hist_entry *right) | ||
799 | { | ||
800 | return left->stat.weight - right->stat.weight; | ||
801 | } | ||
802 | |||
803 | static int hist_entry__global_weight_snprintf(struct hist_entry *self, char *bf, | ||
804 | size_t size, unsigned int width) | ||
805 | { | ||
806 | return repsep_snprintf(bf, size, "%-*llu", width, self->stat.weight); | ||
807 | } | ||
808 | |||
809 | struct sort_entry sort_global_weight = { | ||
810 | .se_header = "Weight", | ||
811 | .se_cmp = sort__global_weight_cmp, | ||
812 | .se_snprintf = hist_entry__global_weight_snprintf, | ||
813 | .se_width_idx = HISTC_GLOBAL_WEIGHT, | ||
814 | }; | ||
815 | |||
816 | struct sort_entry sort_mem_daddr_sym = { | ||
817 | .se_header = "Data Symbol", | ||
818 | .se_cmp = sort__daddr_cmp, | ||
819 | .se_snprintf = hist_entry__daddr_snprintf, | ||
820 | .se_width_idx = HISTC_MEM_DADDR_SYMBOL, | ||
821 | }; | ||
822 | |||
823 | struct sort_entry sort_mem_daddr_dso = { | ||
824 | .se_header = "Data Object", | ||
825 | .se_cmp = sort__dso_daddr_cmp, | ||
826 | .se_snprintf = hist_entry__dso_daddr_snprintf, | ||
827 | .se_width_idx = HISTC_MEM_DADDR_SYMBOL, | ||
828 | }; | ||
829 | |||
830 | struct sort_entry sort_mem_locked = { | ||
831 | .se_header = "Locked", | ||
832 | .se_cmp = sort__locked_cmp, | ||
833 | .se_snprintf = hist_entry__locked_snprintf, | ||
834 | .se_width_idx = HISTC_MEM_LOCKED, | ||
835 | }; | ||
836 | |||
837 | struct sort_entry sort_mem_tlb = { | ||
838 | .se_header = "TLB access", | ||
839 | .se_cmp = sort__tlb_cmp, | ||
840 | .se_snprintf = hist_entry__tlb_snprintf, | ||
841 | .se_width_idx = HISTC_MEM_TLB, | ||
842 | }; | ||
843 | |||
844 | struct sort_entry sort_mem_lvl = { | ||
845 | .se_header = "Memory access", | ||
846 | .se_cmp = sort__lvl_cmp, | ||
847 | .se_snprintf = hist_entry__lvl_snprintf, | ||
848 | .se_width_idx = HISTC_MEM_LVL, | ||
849 | }; | ||
850 | |||
851 | struct sort_entry sort_mem_snoop = { | ||
852 | .se_header = "Snoop", | ||
853 | .se_cmp = sort__snoop_cmp, | ||
854 | .se_snprintf = hist_entry__snoop_snprintf, | ||
855 | .se_width_idx = HISTC_MEM_SNOOP, | ||
856 | }; | ||
857 | |||
467 | struct sort_dimension { | 858 | struct sort_dimension { |
468 | const char *name; | 859 | const char *name; |
469 | struct sort_entry *entry; | 860 | struct sort_entry *entry; |
@@ -480,6 +871,14 @@ static struct sort_dimension common_sort_dimensions[] = { | |||
480 | DIM(SORT_PARENT, "parent", sort_parent), | 871 | DIM(SORT_PARENT, "parent", sort_parent), |
481 | DIM(SORT_CPU, "cpu", sort_cpu), | 872 | DIM(SORT_CPU, "cpu", sort_cpu), |
482 | DIM(SORT_SRCLINE, "srcline", sort_srcline), | 873 | DIM(SORT_SRCLINE, "srcline", sort_srcline), |
874 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), | ||
875 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), | ||
876 | DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym), | ||
877 | DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso), | ||
878 | DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked), | ||
879 | DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb), | ||
880 | DIM(SORT_MEM_LVL, "mem", sort_mem_lvl), | ||
881 | DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop), | ||
483 | }; | 882 | }; |
484 | 883 | ||
485 | #undef DIM | 884 | #undef DIM |
@@ -516,7 +915,10 @@ int sort_dimension__add(const char *tok) | |||
516 | return -EINVAL; | 915 | return -EINVAL; |
517 | } | 916 | } |
518 | sort__has_parent = 1; | 917 | sort__has_parent = 1; |
519 | } else if (sd->entry == &sort_sym) { | 918 | } else if (sd->entry == &sort_sym || |
919 | sd->entry == &sort_sym_from || | ||
920 | sd->entry == &sort_sym_to || | ||
921 | sd->entry == &sort_mem_daddr_sym) { | ||
520 | sort__has_sym = 1; | 922 | sort__has_sym = 1; |
521 | } | 923 | } |
522 | 924 | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index b13e56f6ccbe..f24bdf64238c 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -49,6 +49,7 @@ struct he_stat { | |||
49 | u64 period_us; | 49 | u64 period_us; |
50 | u64 period_guest_sys; | 50 | u64 period_guest_sys; |
51 | u64 period_guest_us; | 51 | u64 period_guest_us; |
52 | u64 weight; | ||
52 | u32 nr_events; | 53 | u32 nr_events; |
53 | }; | 54 | }; |
54 | 55 | ||
@@ -100,7 +101,8 @@ struct hist_entry { | |||
100 | struct rb_root sorted_chain; | 101 | struct rb_root sorted_chain; |
101 | struct branch_info *branch_info; | 102 | struct branch_info *branch_info; |
102 | struct hists *hists; | 103 | struct hists *hists; |
103 | struct callchain_root callchain[0]; | 104 | struct mem_info *mem_info; |
105 | struct callchain_root callchain[0]; /* must be last member */ | ||
104 | }; | 106 | }; |
105 | 107 | ||
106 | static inline bool hist_entry__has_pairs(struct hist_entry *he) | 108 | static inline bool hist_entry__has_pairs(struct hist_entry *he) |
@@ -130,6 +132,14 @@ enum sort_type { | |||
130 | SORT_PARENT, | 132 | SORT_PARENT, |
131 | SORT_CPU, | 133 | SORT_CPU, |
132 | SORT_SRCLINE, | 134 | SORT_SRCLINE, |
135 | SORT_LOCAL_WEIGHT, | ||
136 | SORT_GLOBAL_WEIGHT, | ||
137 | SORT_MEM_DADDR_SYMBOL, | ||
138 | SORT_MEM_DADDR_DSO, | ||
139 | SORT_MEM_LOCKED, | ||
140 | SORT_MEM_TLB, | ||
141 | SORT_MEM_LVL, | ||
142 | SORT_MEM_SNOOP, | ||
133 | 143 | ||
134 | /* branch stack specific sort keys */ | 144 | /* branch stack specific sort keys */ |
135 | __SORT_BRANCH_STACK, | 145 | __SORT_BRANCH_STACK, |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 54efcb5659ac..4b12bf850325 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -806,9 +806,12 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
806 | * DWARF DW_compile_unit has this, but we don't always have access | 806 | * DWARF DW_compile_unit has this, but we don't always have access |
807 | * to it... | 807 | * to it... |
808 | */ | 808 | */ |
809 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | 809 | if (symbol_conf.demangle) { |
810 | if (demangled != NULL) | 810 | demangled = bfd_demangle(NULL, elf_name, |
811 | elf_name = demangled; | 811 | DMGL_PARAMS | DMGL_ANSI); |
812 | if (demangled != NULL) | ||
813 | elf_name = demangled; | ||
814 | } | ||
812 | new_symbol: | 815 | new_symbol: |
813 | f = symbol__new(sym.st_value, sym.st_size, | 816 | f = symbol__new(sym.st_value, sym.st_size, |
814 | GELF_ST_BIND(sym.st_info), elf_name); | 817 | GELF_ST_BIND(sym.st_info), elf_name); |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e6432d85b43d..8cf3b5426a9a 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -36,6 +36,7 @@ struct symbol_conf symbol_conf = { | |||
36 | .use_modules = true, | 36 | .use_modules = true, |
37 | .try_vmlinux_path = true, | 37 | .try_vmlinux_path = true, |
38 | .annotate_src = true, | 38 | .annotate_src = true, |
39 | .demangle = true, | ||
39 | .symfs = "", | 40 | .symfs = "", |
40 | }; | 41 | }; |
41 | 42 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index b62ca37c4b77..5f720dc076da 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -97,7 +97,8 @@ struct symbol_conf { | |||
97 | kptr_restrict, | 97 | kptr_restrict, |
98 | annotate_asm_raw, | 98 | annotate_asm_raw, |
99 | annotate_src, | 99 | annotate_src, |
100 | event_group; | 100 | event_group, |
101 | demangle; | ||
101 | const char *vmlinux_name, | 102 | const char *vmlinux_name, |
102 | *kallsyms_name, | 103 | *kallsyms_name, |
103 | *source_prefix, | 104 | *source_prefix, |
@@ -155,6 +156,12 @@ struct branch_info { | |||
155 | struct branch_flags flags; | 156 | struct branch_flags flags; |
156 | }; | 157 | }; |
157 | 158 | ||
159 | struct mem_info { | ||
160 | struct addr_map_symbol iaddr; | ||
161 | struct addr_map_symbol daddr; | ||
162 | union perf_mem_data_src data_src; | ||
163 | }; | ||
164 | |||
158 | struct addr_location { | 165 | struct addr_location { |
159 | struct thread *thread; | 166 | struct thread *thread; |
160 | struct map *map; | 167 | struct map *map; |
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index f718df8a3c59..0cd8b3108084 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
@@ -21,4 +21,9 @@ void thread_map__delete(struct thread_map *threads); | |||
21 | 21 | ||
22 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); | 22 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); |
23 | 23 | ||
24 | static inline int thread_map__nr(struct thread_map *threads) | ||
25 | { | ||
26 | return threads ? threads->nr : 1; | ||
27 | } | ||
28 | |||
24 | #endif /* __PERF_THREAD_MAP_H */ | 29 | #endif /* __PERF_THREAD_MAP_H */ |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index a8d81c35ef66..3917eb9a8479 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -38,52 +38,20 @@ | |||
38 | 38 | ||
39 | #include "../perf.h" | 39 | #include "../perf.h" |
40 | #include "trace-event.h" | 40 | #include "trace-event.h" |
41 | #include "debugfs.h" | 41 | #include <lk/debugfs.h> |
42 | #include "evsel.h" | 42 | #include "evsel.h" |
43 | 43 | ||
44 | #define VERSION "0.5" | 44 | #define VERSION "0.5" |
45 | 45 | ||
46 | #define TRACE_CTRL "tracing_on" | ||
47 | #define TRACE "trace" | ||
48 | #define AVAILABLE "available_tracers" | ||
49 | #define CURRENT "current_tracer" | ||
50 | #define ITER_CTRL "trace_options" | ||
51 | #define MAX_LATENCY "tracing_max_latency" | ||
52 | |||
53 | unsigned int page_size; | ||
54 | |||
55 | static const char *output_file = "trace.info"; | ||
56 | static int output_fd; | 46 | static int output_fd; |
57 | 47 | ||
58 | struct event_list { | ||
59 | struct event_list *next; | ||
60 | const char *event; | ||
61 | }; | ||
62 | |||
63 | struct events { | ||
64 | struct events *sibling; | ||
65 | struct events *children; | ||
66 | struct events *next; | ||
67 | char *name; | ||
68 | }; | ||
69 | |||
70 | |||
71 | static void *malloc_or_die(unsigned int size) | ||
72 | { | ||
73 | void *data; | ||
74 | |||
75 | data = malloc(size); | ||
76 | if (!data) | ||
77 | die("malloc"); | ||
78 | return data; | ||
79 | } | ||
80 | 48 | ||
81 | static const char *find_debugfs(void) | 49 | static const char *find_debugfs(void) |
82 | { | 50 | { |
83 | const char *path = debugfs_mount(NULL); | 51 | const char *path = perf_debugfs_mount(NULL); |
84 | 52 | ||
85 | if (!path) | 53 | if (!path) |
86 | die("Your kernel not support debugfs filesystem"); | 54 | pr_debug("Your kernel does not support the debugfs filesystem"); |
87 | 55 | ||
88 | return path; | 56 | return path; |
89 | } | 57 | } |
@@ -102,8 +70,12 @@ static const char *find_tracing_dir(void) | |||
102 | return tracing; | 70 | return tracing; |
103 | 71 | ||
104 | debugfs = find_debugfs(); | 72 | debugfs = find_debugfs(); |
73 | if (!debugfs) | ||
74 | return NULL; | ||
105 | 75 | ||
106 | tracing = malloc_or_die(strlen(debugfs) + 9); | 76 | tracing = malloc(strlen(debugfs) + 9); |
77 | if (!tracing) | ||
78 | return NULL; | ||
107 | 79 | ||
108 | sprintf(tracing, "%s/tracing", debugfs); | 80 | sprintf(tracing, "%s/tracing", debugfs); |
109 | 81 | ||
@@ -120,7 +92,9 @@ static char *get_tracing_file(const char *name) | |||
120 | if (!tracing) | 92 | if (!tracing) |
121 | return NULL; | 93 | return NULL; |
122 | 94 | ||
123 | file = malloc_or_die(strlen(tracing) + strlen(name) + 2); | 95 | file = malloc(strlen(tracing) + strlen(name) + 2); |
96 | if (!file) | ||
97 | return NULL; | ||
124 | 98 | ||
125 | sprintf(file, "%s/%s", tracing, name); | 99 | sprintf(file, "%s/%s", tracing, name); |
126 | return file; | 100 | return file; |
@@ -131,24 +105,6 @@ static void put_tracing_file(char *file) | |||
131 | free(file); | 105 | free(file); |
132 | } | 106 | } |
133 | 107 | ||
134 | static ssize_t calc_data_size; | ||
135 | |||
136 | static ssize_t write_or_die(const void *buf, size_t len) | ||
137 | { | ||
138 | int ret; | ||
139 | |||
140 | if (calc_data_size) { | ||
141 | calc_data_size += len; | ||
142 | return len; | ||
143 | } | ||
144 | |||
145 | ret = write(output_fd, buf, len); | ||
146 | if (ret < 0) | ||
147 | die("writing to '%s'", output_file); | ||
148 | |||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | int bigendian(void) | 108 | int bigendian(void) |
153 | { | 109 | { |
154 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; | 110 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; |
@@ -159,59 +115,106 @@ int bigendian(void) | |||
159 | } | 115 | } |
160 | 116 | ||
161 | /* unfortunately, you can not stat debugfs or proc files for size */ | 117 | /* unfortunately, you can not stat debugfs or proc files for size */ |
162 | static void record_file(const char *file, size_t hdr_sz) | 118 | static int record_file(const char *file, ssize_t hdr_sz) |
163 | { | 119 | { |
164 | unsigned long long size = 0; | 120 | unsigned long long size = 0; |
165 | char buf[BUFSIZ], *sizep; | 121 | char buf[BUFSIZ], *sizep; |
166 | off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); | 122 | off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); |
167 | int r, fd; | 123 | int r, fd; |
124 | int err = -EIO; | ||
168 | 125 | ||
169 | fd = open(file, O_RDONLY); | 126 | fd = open(file, O_RDONLY); |
170 | if (fd < 0) | 127 | if (fd < 0) { |
171 | die("Can't read '%s'", file); | 128 | pr_debug("Can't read '%s'", file); |
129 | return -errno; | ||
130 | } | ||
172 | 131 | ||
173 | /* put in zeros for file size, then fill true size later */ | 132 | /* put in zeros for file size, then fill true size later */ |
174 | if (hdr_sz) | 133 | if (hdr_sz) { |
175 | write_or_die(&size, hdr_sz); | 134 | if (write(output_fd, &size, hdr_sz) != hdr_sz) |
135 | goto out; | ||
136 | } | ||
176 | 137 | ||
177 | do { | 138 | do { |
178 | r = read(fd, buf, BUFSIZ); | 139 | r = read(fd, buf, BUFSIZ); |
179 | if (r > 0) { | 140 | if (r > 0) { |
180 | size += r; | 141 | size += r; |
181 | write_or_die(buf, r); | 142 | if (write(output_fd, buf, r) != r) |
143 | goto out; | ||
182 | } | 144 | } |
183 | } while (r > 0); | 145 | } while (r > 0); |
184 | close(fd); | ||
185 | 146 | ||
186 | /* ugh, handle big-endian hdr_size == 4 */ | 147 | /* ugh, handle big-endian hdr_size == 4 */ |
187 | sizep = (char*)&size; | 148 | sizep = (char*)&size; |
188 | if (bigendian()) | 149 | if (bigendian()) |
189 | sizep += sizeof(u64) - hdr_sz; | 150 | sizep += sizeof(u64) - hdr_sz; |
190 | 151 | ||
191 | if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) | 152 | if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) { |
192 | die("writing to %s", output_file); | 153 | pr_debug("writing file size failed\n"); |
154 | goto out; | ||
155 | } | ||
156 | |||
157 | err = 0; | ||
158 | out: | ||
159 | close(fd); | ||
160 | return err; | ||
193 | } | 161 | } |
194 | 162 | ||
195 | static void read_header_files(void) | 163 | static int read_header_files(void) |
196 | { | 164 | { |
197 | char *path; | 165 | char *path; |
198 | struct stat st; | 166 | struct stat st; |
167 | int err = -EIO; | ||
199 | 168 | ||
200 | path = get_tracing_file("events/header_page"); | 169 | path = get_tracing_file("events/header_page"); |
201 | if (stat(path, &st) < 0) | 170 | if (!path) { |
202 | die("can't read '%s'", path); | 171 | pr_debug("can't get tracing/events/header_page"); |
172 | return -ENOMEM; | ||
173 | } | ||
174 | |||
175 | if (stat(path, &st) < 0) { | ||
176 | pr_debug("can't read '%s'", path); | ||
177 | goto out; | ||
178 | } | ||
179 | |||
180 | if (write(output_fd, "header_page", 12) != 12) { | ||
181 | pr_debug("can't write header_page\n"); | ||
182 | goto out; | ||
183 | } | ||
184 | |||
185 | if (record_file(path, 8) < 0) { | ||
186 | pr_debug("can't record header_page file\n"); | ||
187 | goto out; | ||
188 | } | ||
203 | 189 | ||
204 | write_or_die("header_page", 12); | ||
205 | record_file(path, 8); | ||
206 | put_tracing_file(path); | 190 | put_tracing_file(path); |
207 | 191 | ||
208 | path = get_tracing_file("events/header_event"); | 192 | path = get_tracing_file("events/header_event"); |
209 | if (stat(path, &st) < 0) | 193 | if (!path) { |
210 | die("can't read '%s'", path); | 194 | pr_debug("can't get tracing/events/header_event"); |
195 | err = -ENOMEM; | ||
196 | goto out; | ||
197 | } | ||
198 | |||
199 | if (stat(path, &st) < 0) { | ||
200 | pr_debug("can't read '%s'", path); | ||
201 | goto out; | ||
202 | } | ||
211 | 203 | ||
212 | write_or_die("header_event", 13); | 204 | if (write(output_fd, "header_event", 13) != 13) { |
213 | record_file(path, 8); | 205 | pr_debug("can't write header_event\n"); |
206 | goto out; | ||
207 | } | ||
208 | |||
209 | if (record_file(path, 8) < 0) { | ||
210 | pr_debug("can't record header_event file\n"); | ||
211 | goto out; | ||
212 | } | ||
213 | |||
214 | err = 0; | ||
215 | out: | ||
214 | put_tracing_file(path); | 216 | put_tracing_file(path); |
217 | return err; | ||
215 | } | 218 | } |
216 | 219 | ||
217 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 220 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -225,7 +228,7 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | |||
225 | return false; | 228 | return false; |
226 | } | 229 | } |
227 | 230 | ||
228 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) | 231 | static int copy_event_system(const char *sys, struct tracepoint_path *tps) |
229 | { | 232 | { |
230 | struct dirent *dent; | 233 | struct dirent *dent; |
231 | struct stat st; | 234 | struct stat st; |
@@ -233,10 +236,13 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
233 | DIR *dir; | 236 | DIR *dir; |
234 | int count = 0; | 237 | int count = 0; |
235 | int ret; | 238 | int ret; |
239 | int err; | ||
236 | 240 | ||
237 | dir = opendir(sys); | 241 | dir = opendir(sys); |
238 | if (!dir) | 242 | if (!dir) { |
239 | die("can't read directory '%s'", sys); | 243 | pr_debug("can't read directory '%s'", sys); |
244 | return -errno; | ||
245 | } | ||
240 | 246 | ||
241 | while ((dent = readdir(dir))) { | 247 | while ((dent = readdir(dir))) { |
242 | if (dent->d_type != DT_DIR || | 248 | if (dent->d_type != DT_DIR || |
@@ -244,7 +250,11 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
244 | strcmp(dent->d_name, "..") == 0 || | 250 | strcmp(dent->d_name, "..") == 0 || |
245 | !name_in_tp_list(dent->d_name, tps)) | 251 | !name_in_tp_list(dent->d_name, tps)) |
246 | continue; | 252 | continue; |
247 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); | 253 | format = malloc(strlen(sys) + strlen(dent->d_name) + 10); |
254 | if (!format) { | ||
255 | err = -ENOMEM; | ||
256 | goto out; | ||
257 | } | ||
248 | sprintf(format, "%s/%s/format", sys, dent->d_name); | 258 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
249 | ret = stat(format, &st); | 259 | ret = stat(format, &st); |
250 | free(format); | 260 | free(format); |
@@ -253,7 +263,11 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
253 | count++; | 263 | count++; |
254 | } | 264 | } |
255 | 265 | ||
256 | write_or_die(&count, 4); | 266 | if (write(output_fd, &count, 4) != 4) { |
267 | err = -EIO; | ||
268 | pr_debug("can't write count\n"); | ||
269 | goto out; | ||
270 | } | ||
257 | 271 | ||
258 | rewinddir(dir); | 272 | rewinddir(dir); |
259 | while ((dent = readdir(dir))) { | 273 | while ((dent = readdir(dir))) { |
@@ -262,27 +276,45 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
262 | strcmp(dent->d_name, "..") == 0 || | 276 | strcmp(dent->d_name, "..") == 0 || |
263 | !name_in_tp_list(dent->d_name, tps)) | 277 | !name_in_tp_list(dent->d_name, tps)) |
264 | continue; | 278 | continue; |
265 | format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10); | 279 | format = malloc(strlen(sys) + strlen(dent->d_name) + 10); |
280 | if (!format) { | ||
281 | err = -ENOMEM; | ||
282 | goto out; | ||
283 | } | ||
266 | sprintf(format, "%s/%s/format", sys, dent->d_name); | 284 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
267 | ret = stat(format, &st); | 285 | ret = stat(format, &st); |
268 | 286 | ||
269 | if (ret >= 0) | 287 | if (ret >= 0) { |
270 | record_file(format, 8); | 288 | err = record_file(format, 8); |
271 | 289 | if (err) { | |
290 | free(format); | ||
291 | goto out; | ||
292 | } | ||
293 | } | ||
272 | free(format); | 294 | free(format); |
273 | } | 295 | } |
296 | err = 0; | ||
297 | out: | ||
274 | closedir(dir); | 298 | closedir(dir); |
299 | return err; | ||
275 | } | 300 | } |
276 | 301 | ||
277 | static void read_ftrace_files(struct tracepoint_path *tps) | 302 | static int read_ftrace_files(struct tracepoint_path *tps) |
278 | { | 303 | { |
279 | char *path; | 304 | char *path; |
305 | int ret; | ||
280 | 306 | ||
281 | path = get_tracing_file("events/ftrace"); | 307 | path = get_tracing_file("events/ftrace"); |
308 | if (!path) { | ||
309 | pr_debug("can't get tracing/events/ftrace"); | ||
310 | return -ENOMEM; | ||
311 | } | ||
282 | 312 | ||
283 | copy_event_system(path, tps); | 313 | ret = copy_event_system(path, tps); |
284 | 314 | ||
285 | put_tracing_file(path); | 315 | put_tracing_file(path); |
316 | |||
317 | return ret; | ||
286 | } | 318 | } |
287 | 319 | ||
288 | static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) | 320 | static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -296,7 +328,7 @@ static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) | |||
296 | return false; | 328 | return false; |
297 | } | 329 | } |
298 | 330 | ||
299 | static void read_event_files(struct tracepoint_path *tps) | 331 | static int read_event_files(struct tracepoint_path *tps) |
300 | { | 332 | { |
301 | struct dirent *dent; | 333 | struct dirent *dent; |
302 | struct stat st; | 334 | struct stat st; |
@@ -305,12 +337,20 @@ static void read_event_files(struct tracepoint_path *tps) | |||
305 | DIR *dir; | 337 | DIR *dir; |
306 | int count = 0; | 338 | int count = 0; |
307 | int ret; | 339 | int ret; |
340 | int err; | ||
308 | 341 | ||
309 | path = get_tracing_file("events"); | 342 | path = get_tracing_file("events"); |
343 | if (!path) { | ||
344 | pr_debug("can't get tracing/events"); | ||
345 | return -ENOMEM; | ||
346 | } | ||
310 | 347 | ||
311 | dir = opendir(path); | 348 | dir = opendir(path); |
312 | if (!dir) | 349 | if (!dir) { |
313 | die("can't read directory '%s'", path); | 350 | err = -errno; |
351 | pr_debug("can't read directory '%s'", path); | ||
352 | goto out; | ||
353 | } | ||
314 | 354 | ||
315 | while ((dent = readdir(dir))) { | 355 | while ((dent = readdir(dir))) { |
316 | if (dent->d_type != DT_DIR || | 356 | if (dent->d_type != DT_DIR || |
@@ -322,7 +362,11 @@ static void read_event_files(struct tracepoint_path *tps) | |||
322 | count++; | 362 | count++; |
323 | } | 363 | } |
324 | 364 | ||
325 | write_or_die(&count, 4); | 365 | if (write(output_fd, &count, 4) != 4) { |
366 | err = -EIO; | ||
367 | pr_debug("can't write count\n"); | ||
368 | goto out; | ||
369 | } | ||
326 | 370 | ||
327 | rewinddir(dir); | 371 | rewinddir(dir); |
328 | while ((dent = readdir(dir))) { | 372 | while ((dent = readdir(dir))) { |
@@ -332,56 +376,90 @@ static void read_event_files(struct tracepoint_path *tps) | |||
332 | strcmp(dent->d_name, "ftrace") == 0 || | 376 | strcmp(dent->d_name, "ftrace") == 0 || |
333 | !system_in_tp_list(dent->d_name, tps)) | 377 | !system_in_tp_list(dent->d_name, tps)) |
334 | continue; | 378 | continue; |
335 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | 379 | sys = malloc(strlen(path) + strlen(dent->d_name) + 2); |
380 | if (!sys) { | ||
381 | err = -ENOMEM; | ||
382 | goto out; | ||
383 | } | ||
336 | sprintf(sys, "%s/%s", path, dent->d_name); | 384 | sprintf(sys, "%s/%s", path, dent->d_name); |
337 | ret = stat(sys, &st); | 385 | ret = stat(sys, &st); |
338 | if (ret >= 0) { | 386 | if (ret >= 0) { |
339 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); | 387 | ssize_t size = strlen(dent->d_name) + 1; |
340 | copy_event_system(sys, tps); | 388 | |
389 | if (write(output_fd, dent->d_name, size) != size || | ||
390 | copy_event_system(sys, tps) < 0) { | ||
391 | err = -EIO; | ||
392 | free(sys); | ||
393 | goto out; | ||
394 | } | ||
341 | } | 395 | } |
342 | free(sys); | 396 | free(sys); |
343 | } | 397 | } |
344 | 398 | err = 0; | |
399 | out: | ||
345 | closedir(dir); | 400 | closedir(dir); |
346 | put_tracing_file(path); | 401 | put_tracing_file(path); |
402 | |||
403 | return err; | ||
347 | } | 404 | } |
348 | 405 | ||
349 | static void read_proc_kallsyms(void) | 406 | static int read_proc_kallsyms(void) |
350 | { | 407 | { |
351 | unsigned int size; | 408 | unsigned int size; |
352 | const char *path = "/proc/kallsyms"; | 409 | const char *path = "/proc/kallsyms"; |
353 | struct stat st; | 410 | struct stat st; |
354 | int ret; | 411 | int ret, err = 0; |
355 | 412 | ||
356 | ret = stat(path, &st); | 413 | ret = stat(path, &st); |
357 | if (ret < 0) { | 414 | if (ret < 0) { |
358 | /* not found */ | 415 | /* not found */ |
359 | size = 0; | 416 | size = 0; |
360 | write_or_die(&size, 4); | 417 | if (write(output_fd, &size, 4) != 4) |
361 | return; | 418 | err = -EIO; |
419 | return err; | ||
362 | } | 420 | } |
363 | record_file(path, 4); | 421 | return record_file(path, 4); |
364 | } | 422 | } |
365 | 423 | ||
366 | static void read_ftrace_printk(void) | 424 | static int read_ftrace_printk(void) |
367 | { | 425 | { |
368 | unsigned int size; | 426 | unsigned int size; |
369 | char *path; | 427 | char *path; |
370 | struct stat st; | 428 | struct stat st; |
371 | int ret; | 429 | int ret, err = 0; |
372 | 430 | ||
373 | path = get_tracing_file("printk_formats"); | 431 | path = get_tracing_file("printk_formats"); |
432 | if (!path) { | ||
433 | pr_debug("can't get tracing/printk_formats"); | ||
434 | return -ENOMEM; | ||
435 | } | ||
436 | |||
374 | ret = stat(path, &st); | 437 | ret = stat(path, &st); |
375 | if (ret < 0) { | 438 | if (ret < 0) { |
376 | /* not found */ | 439 | /* not found */ |
377 | size = 0; | 440 | size = 0; |
378 | write_or_die(&size, 4); | 441 | if (write(output_fd, &size, 4) != 4) |
442 | err = -EIO; | ||
379 | goto out; | 443 | goto out; |
380 | } | 444 | } |
381 | record_file(path, 4); | 445 | err = record_file(path, 4); |
382 | 446 | ||
383 | out: | 447 | out: |
384 | put_tracing_file(path); | 448 | put_tracing_file(path); |
449 | return err; | ||
450 | } | ||
451 | |||
452 | static void | ||
453 | put_tracepoints_path(struct tracepoint_path *tps) | ||
454 | { | ||
455 | while (tps) { | ||
456 | struct tracepoint_path *t = tps; | ||
457 | |||
458 | tps = tps->next; | ||
459 | free(t->name); | ||
460 | free(t->system); | ||
461 | free(t); | ||
462 | } | ||
385 | } | 463 | } |
386 | 464 | ||
387 | static struct tracepoint_path * | 465 | static struct tracepoint_path * |
@@ -396,27 +474,17 @@ get_tracepoints_path(struct list_head *pattrs) | |||
396 | continue; | 474 | continue; |
397 | ++nr_tracepoints; | 475 | ++nr_tracepoints; |
398 | ppath->next = tracepoint_id_to_path(pos->attr.config); | 476 | ppath->next = tracepoint_id_to_path(pos->attr.config); |
399 | if (!ppath->next) | 477 | if (!ppath->next) { |
400 | die("%s\n", "No memory to alloc tracepoints list"); | 478 | pr_debug("No memory to alloc tracepoints list\n"); |
479 | put_tracepoints_path(&path); | ||
480 | return NULL; | ||
481 | } | ||
401 | ppath = ppath->next; | 482 | ppath = ppath->next; |
402 | } | 483 | } |
403 | 484 | ||
404 | return nr_tracepoints > 0 ? path.next : NULL; | 485 | return nr_tracepoints > 0 ? path.next : NULL; |
405 | } | 486 | } |
406 | 487 | ||
407 | static void | ||
408 | put_tracepoints_path(struct tracepoint_path *tps) | ||
409 | { | ||
410 | while (tps) { | ||
411 | struct tracepoint_path *t = tps; | ||
412 | |||
413 | tps = tps->next; | ||
414 | free(t->name); | ||
415 | free(t->system); | ||
416 | free(t); | ||
417 | } | ||
418 | } | ||
419 | |||
420 | bool have_tracepoints(struct list_head *pattrs) | 488 | bool have_tracepoints(struct list_head *pattrs) |
421 | { | 489 | { |
422 | struct perf_evsel *pos; | 490 | struct perf_evsel *pos; |
@@ -428,9 +496,10 @@ bool have_tracepoints(struct list_head *pattrs) | |||
428 | return false; | 496 | return false; |
429 | } | 497 | } |
430 | 498 | ||
431 | static void tracing_data_header(void) | 499 | static int tracing_data_header(void) |
432 | { | 500 | { |
433 | char buf[20]; | 501 | char buf[20]; |
502 | ssize_t size; | ||
434 | 503 | ||
435 | /* just guessing this is someone's birthday.. ;) */ | 504 | /* just guessing this is someone's birthday.. ;) */ |
436 | buf[0] = 23; | 505 | buf[0] = 23; |
@@ -438,9 +507,12 @@ static void tracing_data_header(void) | |||
438 | buf[2] = 68; | 507 | buf[2] = 68; |
439 | memcpy(buf + 3, "tracing", 7); | 508 | memcpy(buf + 3, "tracing", 7); |
440 | 509 | ||
441 | write_or_die(buf, 10); | 510 | if (write(output_fd, buf, 10) != 10) |
511 | return -1; | ||
442 | 512 | ||
443 | write_or_die(VERSION, strlen(VERSION) + 1); | 513 | size = strlen(VERSION) + 1; |
514 | if (write(output_fd, VERSION, size) != size) | ||
515 | return -1; | ||
444 | 516 | ||
445 | /* save endian */ | 517 | /* save endian */ |
446 | if (bigendian()) | 518 | if (bigendian()) |
@@ -450,15 +522,19 @@ static void tracing_data_header(void) | |||
450 | 522 | ||
451 | read_trace_init(buf[0], buf[0]); | 523 | read_trace_init(buf[0], buf[0]); |
452 | 524 | ||
453 | write_or_die(buf, 1); | 525 | if (write(output_fd, buf, 1) != 1) |
526 | return -1; | ||
454 | 527 | ||
455 | /* save size of long */ | 528 | /* save size of long */ |
456 | buf[0] = sizeof(long); | 529 | buf[0] = sizeof(long); |
457 | write_or_die(buf, 1); | 530 | if (write(output_fd, buf, 1) != 1) |
531 | return -1; | ||
458 | 532 | ||
459 | /* save page_size */ | 533 | /* save page_size */ |
460 | page_size = sysconf(_SC_PAGESIZE); | 534 | if (write(output_fd, &page_size, 4) != 4) |
461 | write_or_die(&page_size, 4); | 535 | return -1; |
536 | |||
537 | return 0; | ||
462 | } | 538 | } |
463 | 539 | ||
464 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | 540 | struct tracing_data *tracing_data_get(struct list_head *pattrs, |
@@ -466,6 +542,7 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
466 | { | 542 | { |
467 | struct tracepoint_path *tps; | 543 | struct tracepoint_path *tps; |
468 | struct tracing_data *tdata; | 544 | struct tracing_data *tdata; |
545 | int err; | ||
469 | 546 | ||
470 | output_fd = fd; | 547 | output_fd = fd; |
471 | 548 | ||
@@ -473,7 +550,10 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
473 | if (!tps) | 550 | if (!tps) |
474 | return NULL; | 551 | return NULL; |
475 | 552 | ||
476 | tdata = malloc_or_die(sizeof(*tdata)); | 553 | tdata = malloc(sizeof(*tdata)); |
554 | if (!tdata) | ||
555 | return NULL; | ||
556 | |||
477 | tdata->temp = temp; | 557 | tdata->temp = temp; |
478 | tdata->size = 0; | 558 | tdata->size = 0; |
479 | 559 | ||
@@ -482,12 +562,16 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
482 | 562 | ||
483 | snprintf(tdata->temp_file, sizeof(tdata->temp_file), | 563 | snprintf(tdata->temp_file, sizeof(tdata->temp_file), |
484 | "/tmp/perf-XXXXXX"); | 564 | "/tmp/perf-XXXXXX"); |
485 | if (!mkstemp(tdata->temp_file)) | 565 | if (!mkstemp(tdata->temp_file)) { |
486 | die("Can't make temp file"); | 566 | pr_debug("Can't make temp file"); |
567 | return NULL; | ||
568 | } | ||
487 | 569 | ||
488 | temp_fd = open(tdata->temp_file, O_RDWR); | 570 | temp_fd = open(tdata->temp_file, O_RDWR); |
489 | if (temp_fd < 0) | 571 | if (temp_fd < 0) { |
490 | die("Can't read '%s'", tdata->temp_file); | 572 | pr_debug("Can't read '%s'", tdata->temp_file); |
573 | return NULL; | ||
574 | } | ||
491 | 575 | ||
492 | /* | 576 | /* |
493 | * Set the temp file the default output, so all the | 577 | * Set the temp file the default output, so all the |
@@ -496,13 +580,24 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
496 | output_fd = temp_fd; | 580 | output_fd = temp_fd; |
497 | } | 581 | } |
498 | 582 | ||
499 | tracing_data_header(); | 583 | err = tracing_data_header(); |
500 | read_header_files(); | 584 | if (err) |
501 | read_ftrace_files(tps); | 585 | goto out; |
502 | read_event_files(tps); | 586 | err = read_header_files(); |
503 | read_proc_kallsyms(); | 587 | if (err) |
504 | read_ftrace_printk(); | 588 | goto out; |
589 | err = read_ftrace_files(tps); | ||
590 | if (err) | ||
591 | goto out; | ||
592 | err = read_event_files(tps); | ||
593 | if (err) | ||
594 | goto out; | ||
595 | err = read_proc_kallsyms(); | ||
596 | if (err) | ||
597 | goto out; | ||
598 | err = read_ftrace_printk(); | ||
505 | 599 | ||
600 | out: | ||
506 | /* | 601 | /* |
507 | * All tracing data are stored by now, we can restore | 602 | * All tracing data are stored by now, we can restore |
508 | * the default output file in case we used temp file. | 603 | * the default output file in case we used temp file. |
@@ -513,22 +608,31 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
513 | output_fd = fd; | 608 | output_fd = fd; |
514 | } | 609 | } |
515 | 610 | ||
611 | if (err) { | ||
612 | free(tdata); | ||
613 | tdata = NULL; | ||
614 | } | ||
615 | |||
516 | put_tracepoints_path(tps); | 616 | put_tracepoints_path(tps); |
517 | return tdata; | 617 | return tdata; |
518 | } | 618 | } |
519 | 619 | ||
520 | void tracing_data_put(struct tracing_data *tdata) | 620 | int tracing_data_put(struct tracing_data *tdata) |
521 | { | 621 | { |
622 | int err = 0; | ||
623 | |||
522 | if (tdata->temp) { | 624 | if (tdata->temp) { |
523 | record_file(tdata->temp_file, 0); | 625 | err = record_file(tdata->temp_file, 0); |
524 | unlink(tdata->temp_file); | 626 | unlink(tdata->temp_file); |
525 | } | 627 | } |
526 | 628 | ||
527 | free(tdata); | 629 | free(tdata); |
630 | return err; | ||
528 | } | 631 | } |
529 | 632 | ||
530 | int read_tracing_data(int fd, struct list_head *pattrs) | 633 | int read_tracing_data(int fd, struct list_head *pattrs) |
531 | { | 634 | { |
635 | int err; | ||
532 | struct tracing_data *tdata; | 636 | struct tracing_data *tdata; |
533 | 637 | ||
534 | /* | 638 | /* |
@@ -539,6 +643,6 @@ int read_tracing_data(int fd, struct list_head *pattrs) | |||
539 | if (!tdata) | 643 | if (!tdata) |
540 | return -ENOMEM; | 644 | return -ENOMEM; |
541 | 645 | ||
542 | tracing_data_put(tdata); | 646 | err = tracing_data_put(tdata); |
543 | return 0; | 647 | return err; |
544 | } | 648 | } |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 3aabcd687cd5..4454835a9ebc 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -183,43 +183,6 @@ void event_format__print(struct event_format *event, | |||
183 | trace_seq_do_printf(&s); | 183 | trace_seq_do_printf(&s); |
184 | } | 184 | } |
185 | 185 | ||
186 | void print_trace_event(struct pevent *pevent, int cpu, void *data, int size) | ||
187 | { | ||
188 | int type = trace_parse_common_type(pevent, data); | ||
189 | struct event_format *event = pevent_find_event(pevent, type); | ||
190 | |||
191 | if (!event) { | ||
192 | warning("ug! no event found for type %d", type); | ||
193 | return; | ||
194 | } | ||
195 | |||
196 | event_format__print(event, cpu, data, size); | ||
197 | } | ||
198 | |||
199 | void print_event(struct pevent *pevent, int cpu, void *data, int size, | ||
200 | unsigned long long nsecs, char *comm) | ||
201 | { | ||
202 | struct pevent_record record; | ||
203 | struct trace_seq s; | ||
204 | int pid; | ||
205 | |||
206 | pevent->latency_format = latency_format; | ||
207 | |||
208 | record.ts = nsecs; | ||
209 | record.cpu = cpu; | ||
210 | record.size = size; | ||
211 | record.data = data; | ||
212 | pid = pevent_data_pid(pevent, &record); | ||
213 | |||
214 | if (!pevent_pid_is_registered(pevent, pid)) | ||
215 | pevent_register_comm(pevent, comm, pid); | ||
216 | |||
217 | trace_seq_init(&s); | ||
218 | pevent_print_event(pevent, &s, &record); | ||
219 | trace_seq_do_printf(&s); | ||
220 | printf("\n"); | ||
221 | } | ||
222 | |||
223 | void parse_proc_kallsyms(struct pevent *pevent, | 186 | void parse_proc_kallsyms(struct pevent *pevent, |
224 | char *file, unsigned int size __maybe_unused) | 187 | char *file, unsigned int size __maybe_unused) |
225 | { | 188 | { |
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 3741572696af..af215c0d2379 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -18,8 +18,6 @@ | |||
18 | * | 18 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 20 | */ |
21 | #define _FILE_OFFSET_BITS 64 | ||
22 | |||
23 | #include <dirent.h> | 21 | #include <dirent.h> |
24 | #include <stdio.h> | 22 | #include <stdio.h> |
25 | #include <stdlib.h> | 23 | #include <stdlib.h> |
@@ -41,26 +39,14 @@ | |||
41 | 39 | ||
42 | static int input_fd; | 40 | static int input_fd; |
43 | 41 | ||
44 | static int read_page; | ||
45 | |||
46 | int file_bigendian; | 42 | int file_bigendian; |
47 | int host_bigendian; | 43 | int host_bigendian; |
48 | static int long_size; | 44 | static int long_size; |
49 | 45 | ||
50 | static ssize_t calc_data_size; | 46 | static ssize_t trace_data_size; |
51 | static bool repipe; | 47 | static bool repipe; |
52 | 48 | ||
53 | static void *malloc_or_die(int size) | 49 | static int __do_read(int fd, void *buf, int size) |
54 | { | ||
55 | void *ret; | ||
56 | |||
57 | ret = malloc(size); | ||
58 | if (!ret) | ||
59 | die("malloc"); | ||
60 | return ret; | ||
61 | } | ||
62 | |||
63 | static int do_read(int fd, void *buf, int size) | ||
64 | { | 50 | { |
65 | int rsize = size; | 51 | int rsize = size; |
66 | 52 | ||
@@ -73,8 +59,10 @@ static int do_read(int fd, void *buf, int size) | |||
73 | if (repipe) { | 59 | if (repipe) { |
74 | int retw = write(STDOUT_FILENO, buf, ret); | 60 | int retw = write(STDOUT_FILENO, buf, ret); |
75 | 61 | ||
76 | if (retw <= 0 || retw != ret) | 62 | if (retw <= 0 || retw != ret) { |
77 | die("repiping input file"); | 63 | pr_debug("repiping input file"); |
64 | return -1; | ||
65 | } | ||
78 | } | 66 | } |
79 | 67 | ||
80 | size -= ret; | 68 | size -= ret; |
@@ -84,17 +72,18 @@ static int do_read(int fd, void *buf, int size) | |||
84 | return rsize; | 72 | return rsize; |
85 | } | 73 | } |
86 | 74 | ||
87 | static int read_or_die(void *data, int size) | 75 | static int do_read(void *data, int size) |
88 | { | 76 | { |
89 | int r; | 77 | int r; |
90 | 78 | ||
91 | r = do_read(input_fd, data, size); | 79 | r = __do_read(input_fd, data, size); |
92 | if (r <= 0) | 80 | if (r <= 0) { |
93 | die("reading input file (size expected=%d received=%d)", | 81 | pr_debug("reading input file (size expected=%d received=%d)", |
94 | size, r); | 82 | size, r); |
83 | return -1; | ||
84 | } | ||
95 | 85 | ||
96 | if (calc_data_size) | 86 | trace_data_size += r; |
97 | calc_data_size += r; | ||
98 | 87 | ||
99 | return r; | 88 | return r; |
100 | } | 89 | } |
@@ -107,7 +96,7 @@ static void skip(int size) | |||
107 | 96 | ||
108 | while (size) { | 97 | while (size) { |
109 | r = size > BUFSIZ ? BUFSIZ : size; | 98 | r = size > BUFSIZ ? BUFSIZ : size; |
110 | read_or_die(buf, r); | 99 | do_read(buf, r); |
111 | size -= r; | 100 | size -= r; |
112 | }; | 101 | }; |
113 | } | 102 | } |
@@ -116,7 +105,8 @@ static unsigned int read4(struct pevent *pevent) | |||
116 | { | 105 | { |
117 | unsigned int data; | 106 | unsigned int data; |
118 | 107 | ||
119 | read_or_die(&data, 4); | 108 | if (do_read(&data, 4) < 0) |
109 | return 0; | ||
120 | return __data2host4(pevent, data); | 110 | return __data2host4(pevent, data); |
121 | } | 111 | } |
122 | 112 | ||
@@ -124,7 +114,8 @@ static unsigned long long read8(struct pevent *pevent) | |||
124 | { | 114 | { |
125 | unsigned long long data; | 115 | unsigned long long data; |
126 | 116 | ||
127 | read_or_die(&data, 8); | 117 | if (do_read(&data, 8) < 0) |
118 | return 0; | ||
128 | return __data2host8(pevent, data); | 119 | return __data2host8(pevent, data); |
129 | } | 120 | } |
130 | 121 | ||
@@ -138,17 +129,23 @@ static char *read_string(void) | |||
138 | 129 | ||
139 | for (;;) { | 130 | for (;;) { |
140 | r = read(input_fd, &c, 1); | 131 | r = read(input_fd, &c, 1); |
141 | if (r < 0) | 132 | if (r < 0) { |
142 | die("reading input file"); | 133 | pr_debug("reading input file"); |
134 | goto out; | ||
135 | } | ||
143 | 136 | ||
144 | if (!r) | 137 | if (!r) { |
145 | die("no data"); | 138 | pr_debug("no data"); |
139 | goto out; | ||
140 | } | ||
146 | 141 | ||
147 | if (repipe) { | 142 | if (repipe) { |
148 | int retw = write(STDOUT_FILENO, &c, 1); | 143 | int retw = write(STDOUT_FILENO, &c, 1); |
149 | 144 | ||
150 | if (retw <= 0 || retw != r) | 145 | if (retw <= 0 || retw != r) { |
151 | die("repiping input file string"); | 146 | pr_debug("repiping input file string"); |
147 | goto out; | ||
148 | } | ||
152 | } | 149 | } |
153 | 150 | ||
154 | buf[size++] = c; | 151 | buf[size++] = c; |
@@ -157,60 +154,79 @@ static char *read_string(void) | |||
157 | break; | 154 | break; |
158 | } | 155 | } |
159 | 156 | ||
160 | if (calc_data_size) | 157 | trace_data_size += size; |
161 | calc_data_size += size; | ||
162 | |||
163 | str = malloc_or_die(size); | ||
164 | memcpy(str, buf, size); | ||
165 | 158 | ||
159 | str = malloc(size); | ||
160 | if (str) | ||
161 | memcpy(str, buf, size); | ||
162 | out: | ||
166 | return str; | 163 | return str; |
167 | } | 164 | } |
168 | 165 | ||
169 | static void read_proc_kallsyms(struct pevent *pevent) | 166 | static int read_proc_kallsyms(struct pevent *pevent) |
170 | { | 167 | { |
171 | unsigned int size; | 168 | unsigned int size; |
172 | char *buf; | 169 | char *buf; |
173 | 170 | ||
174 | size = read4(pevent); | 171 | size = read4(pevent); |
175 | if (!size) | 172 | if (!size) |
176 | return; | 173 | return 0; |
174 | |||
175 | buf = malloc(size + 1); | ||
176 | if (buf == NULL) | ||
177 | return -1; | ||
177 | 178 | ||
178 | buf = malloc_or_die(size + 1); | 179 | if (do_read(buf, size) < 0) { |
179 | read_or_die(buf, size); | 180 | free(buf); |
181 | return -1; | ||
182 | } | ||
180 | buf[size] = '\0'; | 183 | buf[size] = '\0'; |
181 | 184 | ||
182 | parse_proc_kallsyms(pevent, buf, size); | 185 | parse_proc_kallsyms(pevent, buf, size); |
183 | 186 | ||
184 | free(buf); | 187 | free(buf); |
188 | return 0; | ||
185 | } | 189 | } |
186 | 190 | ||
187 | static void read_ftrace_printk(struct pevent *pevent) | 191 | static int read_ftrace_printk(struct pevent *pevent) |
188 | { | 192 | { |
189 | unsigned int size; | 193 | unsigned int size; |
190 | char *buf; | 194 | char *buf; |
191 | 195 | ||
196 | /* it can have 0 size */ | ||
192 | size = read4(pevent); | 197 | size = read4(pevent); |
193 | if (!size) | 198 | if (!size) |
194 | return; | 199 | return 0; |
200 | |||
201 | buf = malloc(size); | ||
202 | if (buf == NULL) | ||
203 | return -1; | ||
195 | 204 | ||
196 | buf = malloc_or_die(size); | 205 | if (do_read(buf, size) < 0) { |
197 | read_or_die(buf, size); | 206 | free(buf); |
207 | return -1; | ||
208 | } | ||
198 | 209 | ||
199 | parse_ftrace_printk(pevent, buf, size); | 210 | parse_ftrace_printk(pevent, buf, size); |
200 | 211 | ||
201 | free(buf); | 212 | free(buf); |
213 | return 0; | ||
202 | } | 214 | } |
203 | 215 | ||
204 | static void read_header_files(struct pevent *pevent) | 216 | static int read_header_files(struct pevent *pevent) |
205 | { | 217 | { |
206 | unsigned long long size; | 218 | unsigned long long size; |
207 | char *header_event; | 219 | char *header_event; |
208 | char buf[BUFSIZ]; | 220 | char buf[BUFSIZ]; |
221 | int ret = 0; | ||
209 | 222 | ||
210 | read_or_die(buf, 12); | 223 | if (do_read(buf, 12) < 0) |
224 | return -1; | ||
211 | 225 | ||
212 | if (memcmp(buf, "header_page", 12) != 0) | 226 | if (memcmp(buf, "header_page", 12) != 0) { |
213 | die("did not read header page"); | 227 | pr_debug("did not read header page"); |
228 | return -1; | ||
229 | } | ||
214 | 230 | ||
215 | size = read8(pevent); | 231 | size = read8(pevent); |
216 | skip(size); | 232 | skip(size); |
@@ -221,269 +237,107 @@ static void read_header_files(struct pevent *pevent) | |||
221 | */ | 237 | */ |
222 | long_size = header_page_size_size; | 238 | long_size = header_page_size_size; |
223 | 239 | ||
224 | read_or_die(buf, 13); | 240 | if (do_read(buf, 13) < 0) |
225 | if (memcmp(buf, "header_event", 13) != 0) | 241 | return -1; |
226 | die("did not read header event"); | 242 | |
243 | if (memcmp(buf, "header_event", 13) != 0) { | ||
244 | pr_debug("did not read header event"); | ||
245 | return -1; | ||
246 | } | ||
227 | 247 | ||
228 | size = read8(pevent); | 248 | size = read8(pevent); |
229 | header_event = malloc_or_die(size); | 249 | header_event = malloc(size); |
230 | read_or_die(header_event, size); | 250 | if (header_event == NULL) |
251 | return -1; | ||
252 | |||
253 | if (do_read(header_event, size) < 0) | ||
254 | ret = -1; | ||
255 | |||
231 | free(header_event); | 256 | free(header_event); |
257 | return ret; | ||
232 | } | 258 | } |
233 | 259 | ||
234 | static void read_ftrace_file(struct pevent *pevent, unsigned long long size) | 260 | static int read_ftrace_file(struct pevent *pevent, unsigned long long size) |
235 | { | 261 | { |
236 | char *buf; | 262 | char *buf; |
237 | 263 | ||
238 | buf = malloc_or_die(size); | 264 | buf = malloc(size); |
239 | read_or_die(buf, size); | 265 | if (buf == NULL) |
266 | return -1; | ||
267 | |||
268 | if (do_read(buf, size) < 0) { | ||
269 | free(buf); | ||
270 | return -1; | ||
271 | } | ||
272 | |||
240 | parse_ftrace_file(pevent, buf, size); | 273 | parse_ftrace_file(pevent, buf, size); |
241 | free(buf); | 274 | free(buf); |
275 | return 0; | ||
242 | } | 276 | } |
243 | 277 | ||
244 | static void read_event_file(struct pevent *pevent, char *sys, | 278 | static int read_event_file(struct pevent *pevent, char *sys, |
245 | unsigned long long size) | 279 | unsigned long long size) |
246 | { | 280 | { |
247 | char *buf; | 281 | char *buf; |
248 | 282 | ||
249 | buf = malloc_or_die(size); | 283 | buf = malloc(size); |
250 | read_or_die(buf, size); | 284 | if (buf == NULL) |
285 | return -1; | ||
286 | |||
287 | if (do_read(buf, size) < 0) { | ||
288 | free(buf); | ||
289 | return -1; | ||
290 | } | ||
291 | |||
251 | parse_event_file(pevent, buf, size, sys); | 292 | parse_event_file(pevent, buf, size, sys); |
252 | free(buf); | 293 | free(buf); |
294 | return 0; | ||
253 | } | 295 | } |
254 | 296 | ||
255 | static void read_ftrace_files(struct pevent *pevent) | 297 | static int read_ftrace_files(struct pevent *pevent) |
256 | { | 298 | { |
257 | unsigned long long size; | 299 | unsigned long long size; |
258 | int count; | 300 | int count; |
259 | int i; | 301 | int i; |
302 | int ret; | ||
260 | 303 | ||
261 | count = read4(pevent); | 304 | count = read4(pevent); |
262 | 305 | ||
263 | for (i = 0; i < count; i++) { | 306 | for (i = 0; i < count; i++) { |
264 | size = read8(pevent); | 307 | size = read8(pevent); |
265 | read_ftrace_file(pevent, size); | 308 | ret = read_ftrace_file(pevent, size); |
309 | if (ret) | ||
310 | return ret; | ||
266 | } | 311 | } |
312 | return 0; | ||
267 | } | 313 | } |
268 | 314 | ||
269 | static void read_event_files(struct pevent *pevent) | 315 | static int read_event_files(struct pevent *pevent) |
270 | { | 316 | { |
271 | unsigned long long size; | 317 | unsigned long long size; |
272 | char *sys; | 318 | char *sys; |
273 | int systems; | 319 | int systems; |
274 | int count; | 320 | int count; |
275 | int i,x; | 321 | int i,x; |
322 | int ret; | ||
276 | 323 | ||
277 | systems = read4(pevent); | 324 | systems = read4(pevent); |
278 | 325 | ||
279 | for (i = 0; i < systems; i++) { | 326 | for (i = 0; i < systems; i++) { |
280 | sys = read_string(); | 327 | sys = read_string(); |
328 | if (sys == NULL) | ||
329 | return -1; | ||
281 | 330 | ||
282 | count = read4(pevent); | 331 | count = read4(pevent); |
332 | |||
283 | for (x=0; x < count; x++) { | 333 | for (x=0; x < count; x++) { |
284 | size = read8(pevent); | 334 | size = read8(pevent); |
285 | read_event_file(pevent, sys, size); | 335 | ret = read_event_file(pevent, sys, size); |
336 | if (ret) | ||
337 | return ret; | ||
286 | } | 338 | } |
287 | } | 339 | } |
288 | } | 340 | return 0; |
289 | |||
290 | struct cpu_data { | ||
291 | unsigned long long offset; | ||
292 | unsigned long long size; | ||
293 | unsigned long long timestamp; | ||
294 | struct pevent_record *next; | ||
295 | char *page; | ||
296 | int cpu; | ||
297 | int index; | ||
298 | int page_size; | ||
299 | }; | ||
300 | |||
301 | static struct cpu_data *cpu_data; | ||
302 | |||
303 | static void update_cpu_data_index(int cpu) | ||
304 | { | ||
305 | cpu_data[cpu].offset += page_size; | ||
306 | cpu_data[cpu].size -= page_size; | ||
307 | cpu_data[cpu].index = 0; | ||
308 | } | ||
309 | |||
310 | static void get_next_page(int cpu) | ||
311 | { | ||
312 | off_t save_seek; | ||
313 | off_t ret; | ||
314 | |||
315 | if (!cpu_data[cpu].page) | ||
316 | return; | ||
317 | |||
318 | if (read_page) { | ||
319 | if (cpu_data[cpu].size <= page_size) { | ||
320 | free(cpu_data[cpu].page); | ||
321 | cpu_data[cpu].page = NULL; | ||
322 | return; | ||
323 | } | ||
324 | |||
325 | update_cpu_data_index(cpu); | ||
326 | |||
327 | /* other parts of the code may expect the pointer to not move */ | ||
328 | save_seek = lseek(input_fd, 0, SEEK_CUR); | ||
329 | |||
330 | ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET); | ||
331 | if (ret == (off_t)-1) | ||
332 | die("failed to lseek"); | ||
333 | ret = read(input_fd, cpu_data[cpu].page, page_size); | ||
334 | if (ret < 0) | ||
335 | die("failed to read page"); | ||
336 | |||
337 | /* reset the file pointer back */ | ||
338 | lseek(input_fd, save_seek, SEEK_SET); | ||
339 | |||
340 | return; | ||
341 | } | ||
342 | |||
343 | munmap(cpu_data[cpu].page, page_size); | ||
344 | cpu_data[cpu].page = NULL; | ||
345 | |||
346 | if (cpu_data[cpu].size <= page_size) | ||
347 | return; | ||
348 | |||
349 | update_cpu_data_index(cpu); | ||
350 | |||
351 | cpu_data[cpu].page = mmap(NULL, page_size, PROT_READ, MAP_PRIVATE, | ||
352 | input_fd, cpu_data[cpu].offset); | ||
353 | if (cpu_data[cpu].page == MAP_FAILED) | ||
354 | die("failed to mmap cpu %d at offset 0x%llx", | ||
355 | cpu, cpu_data[cpu].offset); | ||
356 | } | ||
357 | |||
358 | static unsigned int type_len4host(unsigned int type_len_ts) | ||
359 | { | ||
360 | if (file_bigendian) | ||
361 | return (type_len_ts >> 27) & ((1 << 5) - 1); | ||
362 | else | ||
363 | return type_len_ts & ((1 << 5) - 1); | ||
364 | } | ||
365 | |||
366 | static unsigned int ts4host(unsigned int type_len_ts) | ||
367 | { | ||
368 | if (file_bigendian) | ||
369 | return type_len_ts & ((1 << 27) - 1); | ||
370 | else | ||
371 | return type_len_ts >> 5; | ||
372 | } | ||
373 | |||
374 | static int calc_index(void *ptr, int cpu) | ||
375 | { | ||
376 | return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page; | ||
377 | } | ||
378 | |||
379 | struct pevent_record *trace_peek_data(struct pevent *pevent, int cpu) | ||
380 | { | ||
381 | struct pevent_record *data; | ||
382 | void *page = cpu_data[cpu].page; | ||
383 | int idx = cpu_data[cpu].index; | ||
384 | void *ptr = page + idx; | ||
385 | unsigned long long extend; | ||
386 | unsigned int type_len_ts; | ||
387 | unsigned int type_len; | ||
388 | unsigned int delta; | ||
389 | unsigned int length = 0; | ||
390 | |||
391 | if (cpu_data[cpu].next) | ||
392 | return cpu_data[cpu].next; | ||
393 | |||
394 | if (!page) | ||
395 | return NULL; | ||
396 | |||
397 | if (!idx) { | ||
398 | /* FIXME: handle header page */ | ||
399 | if (header_page_ts_size != 8) | ||
400 | die("expected a long long type for timestamp"); | ||
401 | cpu_data[cpu].timestamp = data2host8(pevent, ptr); | ||
402 | ptr += 8; | ||
403 | switch (header_page_size_size) { | ||
404 | case 4: | ||
405 | cpu_data[cpu].page_size = data2host4(pevent, ptr); | ||
406 | ptr += 4; | ||
407 | break; | ||
408 | case 8: | ||
409 | cpu_data[cpu].page_size = data2host8(pevent, ptr); | ||
410 | ptr += 8; | ||
411 | break; | ||
412 | default: | ||
413 | die("bad long size"); | ||
414 | } | ||
415 | ptr = cpu_data[cpu].page + header_page_data_offset; | ||
416 | } | ||
417 | |||
418 | read_again: | ||
419 | idx = calc_index(ptr, cpu); | ||
420 | |||
421 | if (idx >= cpu_data[cpu].page_size) { | ||
422 | get_next_page(cpu); | ||
423 | return trace_peek_data(pevent, cpu); | ||
424 | } | ||
425 | |||
426 | type_len_ts = data2host4(pevent, ptr); | ||
427 | ptr += 4; | ||
428 | |||
429 | type_len = type_len4host(type_len_ts); | ||
430 | delta = ts4host(type_len_ts); | ||
431 | |||
432 | switch (type_len) { | ||
433 | case RINGBUF_TYPE_PADDING: | ||
434 | if (!delta) | ||
435 | die("error, hit unexpected end of page"); | ||
436 | length = data2host4(pevent, ptr); | ||
437 | ptr += 4; | ||
438 | length *= 4; | ||
439 | ptr += length; | ||
440 | goto read_again; | ||
441 | |||
442 | case RINGBUF_TYPE_TIME_EXTEND: | ||
443 | extend = data2host4(pevent, ptr); | ||
444 | ptr += 4; | ||
445 | extend <<= TS_SHIFT; | ||
446 | extend += delta; | ||
447 | cpu_data[cpu].timestamp += extend; | ||
448 | goto read_again; | ||
449 | |||
450 | case RINGBUF_TYPE_TIME_STAMP: | ||
451 | ptr += 12; | ||
452 | break; | ||
453 | case 0: | ||
454 | length = data2host4(pevent, ptr); | ||
455 | ptr += 4; | ||
456 | die("here! length=%d", length); | ||
457 | break; | ||
458 | default: | ||
459 | length = type_len * 4; | ||
460 | break; | ||
461 | } | ||
462 | |||
463 | cpu_data[cpu].timestamp += delta; | ||
464 | |||
465 | data = malloc_or_die(sizeof(*data)); | ||
466 | memset(data, 0, sizeof(*data)); | ||
467 | |||
468 | data->ts = cpu_data[cpu].timestamp; | ||
469 | data->size = length; | ||
470 | data->data = ptr; | ||
471 | ptr += length; | ||
472 | |||
473 | cpu_data[cpu].index = calc_index(ptr, cpu); | ||
474 | cpu_data[cpu].next = data; | ||
475 | |||
476 | return data; | ||
477 | } | ||
478 | |||
479 | struct pevent_record *trace_read_data(struct pevent *pevent, int cpu) | ||
480 | { | ||
481 | struct pevent_record *data; | ||
482 | |||
483 | data = trace_peek_data(pevent, cpu); | ||
484 | cpu_data[cpu].next = NULL; | ||
485 | |||
486 | return data; | ||
487 | } | 341 | } |
488 | 342 | ||
489 | ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) | 343 | ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) |
@@ -494,58 +348,85 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) | |||
494 | int show_version = 0; | 348 | int show_version = 0; |
495 | int show_funcs = 0; | 349 | int show_funcs = 0; |
496 | int show_printk = 0; | 350 | int show_printk = 0; |
497 | ssize_t size; | 351 | ssize_t size = -1; |
352 | struct pevent *pevent; | ||
353 | int err; | ||
498 | 354 | ||
499 | calc_data_size = 1; | 355 | *ppevent = NULL; |
500 | repipe = __repipe; | ||
501 | 356 | ||
357 | repipe = __repipe; | ||
502 | input_fd = fd; | 358 | input_fd = fd; |
503 | 359 | ||
504 | read_or_die(buf, 3); | 360 | if (do_read(buf, 3) < 0) |
505 | if (memcmp(buf, test, 3) != 0) | 361 | return -1; |
506 | die("no trace data in the file"); | 362 | if (memcmp(buf, test, 3) != 0) { |
363 | pr_debug("no trace data in the file"); | ||
364 | return -1; | ||
365 | } | ||
507 | 366 | ||
508 | read_or_die(buf, 7); | 367 | if (do_read(buf, 7) < 0) |
509 | if (memcmp(buf, "tracing", 7) != 0) | 368 | return -1; |
510 | die("not a trace file (missing 'tracing' tag)"); | 369 | if (memcmp(buf, "tracing", 7) != 0) { |
370 | pr_debug("not a trace file (missing 'tracing' tag)"); | ||
371 | return -1; | ||
372 | } | ||
511 | 373 | ||
512 | version = read_string(); | 374 | version = read_string(); |
375 | if (version == NULL) | ||
376 | return -1; | ||
513 | if (show_version) | 377 | if (show_version) |
514 | printf("version = %s\n", version); | 378 | printf("version = %s\n", version); |
515 | free(version); | 379 | free(version); |
516 | 380 | ||
517 | read_or_die(buf, 1); | 381 | if (do_read(buf, 1) < 0) |
382 | return -1; | ||
518 | file_bigendian = buf[0]; | 383 | file_bigendian = buf[0]; |
519 | host_bigendian = bigendian(); | 384 | host_bigendian = bigendian(); |
520 | 385 | ||
521 | *ppevent = read_trace_init(file_bigendian, host_bigendian); | 386 | pevent = read_trace_init(file_bigendian, host_bigendian); |
522 | if (*ppevent == NULL) | 387 | if (pevent == NULL) { |
523 | die("read_trace_init failed"); | 388 | pr_debug("read_trace_init failed"); |
389 | goto out; | ||
390 | } | ||
524 | 391 | ||
525 | read_or_die(buf, 1); | 392 | if (do_read(buf, 1) < 0) |
393 | goto out; | ||
526 | long_size = buf[0]; | 394 | long_size = buf[0]; |
527 | 395 | ||
528 | page_size = read4(*ppevent); | 396 | page_size = read4(pevent); |
529 | 397 | if (!page_size) | |
530 | read_header_files(*ppevent); | 398 | goto out; |
531 | 399 | ||
532 | read_ftrace_files(*ppevent); | 400 | err = read_header_files(pevent); |
533 | read_event_files(*ppevent); | 401 | if (err) |
534 | read_proc_kallsyms(*ppevent); | 402 | goto out; |
535 | read_ftrace_printk(*ppevent); | 403 | err = read_ftrace_files(pevent); |
536 | 404 | if (err) | |
537 | size = calc_data_size - 1; | 405 | goto out; |
538 | calc_data_size = 0; | 406 | err = read_event_files(pevent); |
407 | if (err) | ||
408 | goto out; | ||
409 | err = read_proc_kallsyms(pevent); | ||
410 | if (err) | ||
411 | goto out; | ||
412 | err = read_ftrace_printk(pevent); | ||
413 | if (err) | ||
414 | goto out; | ||
415 | |||
416 | size = trace_data_size; | ||
539 | repipe = false; | 417 | repipe = false; |
540 | 418 | ||
541 | if (show_funcs) { | 419 | if (show_funcs) { |
542 | pevent_print_funcs(*ppevent); | 420 | pevent_print_funcs(pevent); |
543 | return size; | 421 | } else if (show_printk) { |
544 | } | 422 | pevent_print_printk(pevent); |
545 | if (show_printk) { | ||
546 | pevent_print_printk(*ppevent); | ||
547 | return size; | ||
548 | } | 423 | } |
549 | 424 | ||
425 | *ppevent = pevent; | ||
426 | pevent = NULL; | ||
427 | |||
428 | out: | ||
429 | if (pevent) | ||
430 | pevent_free(pevent); | ||
550 | return size; | 431 | return size; |
551 | } | 432 | } |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index a55fd37ffea1..1978c398ad87 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -30,13 +30,9 @@ enum { | |||
30 | int bigendian(void); | 30 | int bigendian(void); |
31 | 31 | ||
32 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); | 32 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); |
33 | void print_trace_event(struct pevent *pevent, int cpu, void *data, int size); | ||
34 | void event_format__print(struct event_format *event, | 33 | void event_format__print(struct event_format *event, |
35 | int cpu, void *data, int size); | 34 | int cpu, void *data, int size); |
36 | 35 | ||
37 | void print_event(struct pevent *pevent, int cpu, void *data, int size, | ||
38 | unsigned long long nsecs, char *comm); | ||
39 | |||
40 | int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); | 36 | int parse_ftrace_file(struct pevent *pevent, char *buf, unsigned long size); |
41 | int parse_event_file(struct pevent *pevent, | 37 | int parse_event_file(struct pevent *pevent, |
42 | char *buf, unsigned long size, char *sys); | 38 | char *buf, unsigned long size, char *sys); |
@@ -72,7 +68,7 @@ struct tracing_data { | |||
72 | 68 | ||
73 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | 69 | struct tracing_data *tracing_data_get(struct list_head *pattrs, |
74 | int fd, bool temp); | 70 | int fd, bool temp); |
75 | void tracing_data_put(struct tracing_data *tdata); | 71 | int tracing_data_put(struct tracing_data *tdata); |
76 | 72 | ||
77 | 73 | ||
78 | struct addr_location; | 74 | struct addr_location; |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 805d1f52c5b4..59d868add275 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -17,6 +17,8 @@ bool test_attr__enabled; | |||
17 | bool perf_host = true; | 17 | bool perf_host = true; |
18 | bool perf_guest = false; | 18 | bool perf_guest = false; |
19 | 19 | ||
20 | char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events"; | ||
21 | |||
20 | void event_attr_init(struct perf_event_attr *attr) | 22 | void event_attr_init(struct perf_event_attr *attr) |
21 | { | 23 | { |
22 | if (!perf_host) | 24 | if (!perf_host) |
@@ -242,3 +244,28 @@ void get_term_dimensions(struct winsize *ws) | |||
242 | ws->ws_row = 25; | 244 | ws->ws_row = 25; |
243 | ws->ws_col = 80; | 245 | ws->ws_col = 80; |
244 | } | 246 | } |
247 | |||
248 | static void set_tracing_events_path(const char *mountpoint) | ||
249 | { | ||
250 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | ||
251 | mountpoint, "tracing/events"); | ||
252 | } | ||
253 | |||
254 | const char *perf_debugfs_mount(const char *mountpoint) | ||
255 | { | ||
256 | const char *mnt; | ||
257 | |||
258 | mnt = debugfs_mount(mountpoint); | ||
259 | if (!mnt) | ||
260 | return NULL; | ||
261 | |||
262 | set_tracing_events_path(mnt); | ||
263 | |||
264 | return mnt; | ||
265 | } | ||
266 | |||
267 | void perf_debugfs_set_path(const char *mntpt) | ||
268 | { | ||
269 | snprintf(debugfs_mountpoint, strlen(debugfs_mountpoint), "%s", mntpt); | ||
270 | set_tracing_events_path(mntpt); | ||
271 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 09b4c26b71aa..a45710b70a55 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -1,8 +1,6 @@ | |||
1 | #ifndef GIT_COMPAT_UTIL_H | 1 | #ifndef GIT_COMPAT_UTIL_H |
2 | #define GIT_COMPAT_UTIL_H | 2 | #define GIT_COMPAT_UTIL_H |
3 | 3 | ||
4 | #define _FILE_OFFSET_BITS 64 | ||
5 | |||
6 | #ifndef FLEX_ARRAY | 4 | #ifndef FLEX_ARRAY |
7 | /* | 5 | /* |
8 | * See if our compiler is known to support flexible array members. | 6 | * See if our compiler is known to support flexible array members. |
@@ -73,10 +71,14 @@ | |||
73 | #include <linux/magic.h> | 71 | #include <linux/magic.h> |
74 | #include "types.h" | 72 | #include "types.h" |
75 | #include <sys/ttydefaults.h> | 73 | #include <sys/ttydefaults.h> |
74 | #include <lk/debugfs.h> | ||
76 | 75 | ||
77 | extern const char *graph_line; | 76 | extern const char *graph_line; |
78 | extern const char *graph_dotted_line; | 77 | extern const char *graph_dotted_line; |
79 | extern char buildid_dir[]; | 78 | extern char buildid_dir[]; |
79 | extern char tracing_events_path[]; | ||
80 | extern void perf_debugfs_set_path(const char *mountpoint); | ||
81 | const char *perf_debugfs_mount(const char *mountpoint); | ||
80 | 82 | ||
81 | /* On most systems <limits.h> would have given us this, but | 83 | /* On most systems <limits.h> would have given us this, but |
82 | * not on some systems (e.g. GNU/Hurd). | 84 | * not on some systems (e.g. GNU/Hurd). |
@@ -274,5 +276,4 @@ extern unsigned int page_size; | |||
274 | 276 | ||
275 | struct winsize; | 277 | struct winsize; |
276 | void get_term_dimensions(struct winsize *ws); | 278 | void get_term_dimensions(struct winsize *ws); |
277 | 279 | #endif /* GIT_COMPAT_UTIL_H */ | |
278 | #endif | ||
diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 2964b96aa55f..f03e681f8891 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include | |||
@@ -1,3 +1,4 @@ | |||
1 | ifneq ($(O),) | ||
1 | ifeq ($(origin O), command line) | 2 | ifeq ($(origin O), command line) |
2 | dummy := $(if $(shell test -d $(O) || echo $(O)),$(error O=$(O) does not exist),) | 3 | dummy := $(if $(shell test -d $(O) || echo $(O)),$(error O=$(O) does not exist),) |
3 | ABSOLUTE_O := $(shell cd $(O) ; pwd) | 4 | ABSOLUTE_O := $(shell cd $(O) ; pwd) |
@@ -7,9 +8,10 @@ ifeq ($(objtree),) | |||
7 | objtree := $(O) | 8 | objtree := $(O) |
8 | endif | 9 | endif |
9 | endif | 10 | endif |
11 | endif | ||
10 | 12 | ||
11 | ifneq ($(OUTPUT),) | ||
12 | # check that the output directory actually exists | 13 | # check that the output directory actually exists |
14 | ifneq ($(OUTPUT),) | ||
13 | OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) | 15 | OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) |
14 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) | 16 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) |
15 | endif | 17 | endif |
@@ -70,7 +72,7 @@ ifndef V | |||
70 | QUIET_BISON = @echo ' ' BISON $@; | 72 | QUIET_BISON = @echo ' ' BISON $@; |
71 | 73 | ||
72 | descend = \ | 74 | descend = \ |
73 | @echo ' ' DESCEND $(1); \ | 75 | +@echo ' ' DESCEND $(1); \ |
74 | mkdir -p $(OUTPUT)$(1) && \ | 76 | mkdir -p $(OUTPUT)$(1) && \ |
75 | $(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2) | 77 | $(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2) |
76 | endif | 78 | endif |
diff --git a/tools/vm/Makefile b/tools/vm/Makefile index 8e30e5c40f8a..24e9ddd93fa4 100644 --- a/tools/vm/Makefile +++ b/tools/vm/Makefile | |||
@@ -1,11 +1,22 @@ | |||
1 | # Makefile for vm tools | 1 | # Makefile for vm tools |
2 | # | ||
3 | TARGETS=page-types slabinfo | ||
4 | |||
5 | LK_DIR = ../lib/lk | ||
6 | LIBLK = $(LK_DIR)/liblk.a | ||
2 | 7 | ||
3 | CC = $(CROSS_COMPILE)gcc | 8 | CC = $(CROSS_COMPILE)gcc |
4 | CFLAGS = -Wall -Wextra | 9 | CFLAGS = -Wall -Wextra -I../lib/ |
10 | LDFLAGS = $(LIBLK) | ||
11 | |||
12 | $(TARGETS): liblk | ||
13 | |||
14 | liblk: | ||
15 | make -C $(LK_DIR) | ||
5 | 16 | ||
6 | all: page-types slabinfo | ||
7 | %: %.c | 17 | %: %.c |
8 | $(CC) $(CFLAGS) -o $@ $^ | 18 | $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) |
9 | 19 | ||
10 | clean: | 20 | clean: |
11 | $(RM) page-types slabinfo | 21 | $(RM) page-types slabinfo |
22 | make -C ../lib/lk clean | ||
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index b76edf2f8333..71c9c2511ee7 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c | |||
@@ -36,7 +36,7 @@ | |||
36 | #include <sys/statfs.h> | 36 | #include <sys/statfs.h> |
37 | #include "../../include/uapi/linux/magic.h" | 37 | #include "../../include/uapi/linux/magic.h" |
38 | #include "../../include/uapi/linux/kernel-page-flags.h" | 38 | #include "../../include/uapi/linux/kernel-page-flags.h" |
39 | 39 | #include <lk/debugfs.h> | |
40 | 40 | ||
41 | #ifndef MAX_PATH | 41 | #ifndef MAX_PATH |
42 | # define MAX_PATH 256 | 42 | # define MAX_PATH 256 |
@@ -178,7 +178,7 @@ static int kpageflags_fd; | |||
178 | static int opt_hwpoison; | 178 | static int opt_hwpoison; |
179 | static int opt_unpoison; | 179 | static int opt_unpoison; |
180 | 180 | ||
181 | static char hwpoison_debug_fs[MAX_PATH+1]; | 181 | static char *hwpoison_debug_fs; |
182 | static int hwpoison_inject_fd; | 182 | static int hwpoison_inject_fd; |
183 | static int hwpoison_forget_fd; | 183 | static int hwpoison_forget_fd; |
184 | 184 | ||
@@ -458,81 +458,6 @@ static uint64_t kpageflags_flags(uint64_t flags) | |||
458 | return flags; | 458 | return flags; |
459 | } | 459 | } |
460 | 460 | ||
461 | /* verify that a mountpoint is actually a debugfs instance */ | ||
462 | static int debugfs_valid_mountpoint(const char *debugfs) | ||
463 | { | ||
464 | struct statfs st_fs; | ||
465 | |||
466 | if (statfs(debugfs, &st_fs) < 0) | ||
467 | return -ENOENT; | ||
468 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
469 | return -ENOENT; | ||
470 | |||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | /* find the path to the mounted debugfs */ | ||
475 | static const char *debugfs_find_mountpoint(void) | ||
476 | { | ||
477 | const char *const *ptr; | ||
478 | char type[100]; | ||
479 | FILE *fp; | ||
480 | |||
481 | ptr = debugfs_known_mountpoints; | ||
482 | while (*ptr) { | ||
483 | if (debugfs_valid_mountpoint(*ptr) == 0) { | ||
484 | strcpy(hwpoison_debug_fs, *ptr); | ||
485 | return hwpoison_debug_fs; | ||
486 | } | ||
487 | ptr++; | ||
488 | } | ||
489 | |||
490 | /* give up and parse /proc/mounts */ | ||
491 | fp = fopen("/proc/mounts", "r"); | ||
492 | if (fp == NULL) | ||
493 | perror("Can't open /proc/mounts for read"); | ||
494 | |||
495 | while (fscanf(fp, "%*s %" | ||
496 | STR(MAX_PATH) | ||
497 | "s %99s %*s %*d %*d\n", | ||
498 | hwpoison_debug_fs, type) == 2) { | ||
499 | if (strcmp(type, "debugfs") == 0) | ||
500 | break; | ||
501 | } | ||
502 | fclose(fp); | ||
503 | |||
504 | if (strcmp(type, "debugfs") != 0) | ||
505 | return NULL; | ||
506 | |||
507 | return hwpoison_debug_fs; | ||
508 | } | ||
509 | |||
510 | /* mount the debugfs somewhere if it's not mounted */ | ||
511 | |||
512 | static void debugfs_mount(void) | ||
513 | { | ||
514 | const char *const *ptr; | ||
515 | |||
516 | /* see if it's already mounted */ | ||
517 | if (debugfs_find_mountpoint()) | ||
518 | return; | ||
519 | |||
520 | ptr = debugfs_known_mountpoints; | ||
521 | while (*ptr) { | ||
522 | if (mount(NULL, *ptr, "debugfs", 0, NULL) == 0) { | ||
523 | /* save the mountpoint */ | ||
524 | strcpy(hwpoison_debug_fs, *ptr); | ||
525 | break; | ||
526 | } | ||
527 | ptr++; | ||
528 | } | ||
529 | |||
530 | if (*ptr == NULL) { | ||
531 | perror("mount debugfs"); | ||
532 | exit(EXIT_FAILURE); | ||
533 | } | ||
534 | } | ||
535 | |||
536 | /* | 461 | /* |
537 | * page actions | 462 | * page actions |
538 | */ | 463 | */ |
@@ -541,7 +466,11 @@ static void prepare_hwpoison_fd(void) | |||
541 | { | 466 | { |
542 | char buf[MAX_PATH + 1]; | 467 | char buf[MAX_PATH + 1]; |
543 | 468 | ||
544 | debugfs_mount(); | 469 | hwpoison_debug_fs = debugfs_mount(NULL); |
470 | if (!hwpoison_debug_fs) { | ||
471 | perror("mount debugfs"); | ||
472 | exit(EXIT_FAILURE); | ||
473 | } | ||
545 | 474 | ||
546 | if (opt_hwpoison && !hwpoison_inject_fd) { | 475 | if (opt_hwpoison && !hwpoison_inject_fd) { |
547 | snprintf(buf, MAX_PATH, "%s/hwpoison/corrupt-pfn", | 476 | snprintf(buf, MAX_PATH, "%s/hwpoison/corrupt-pfn", |