Commit 1d229d54dbc26971142f61c3d271a68db236d178
Exists in
master
and in
4 other branches
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kerne…
…l/git/tip/linux-2.6-tip * 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: perf symbols: Check '/tmp/perf-' symbol file ownership perf sched: Usage leftover from trace -> script rename perf sched: Do not delete session object prematurely perf tools: Check $HOME/.perfconfig ownership perf, x86: Add model 45 SandyBridge support perf tools: Add support to install perf python extension perf tools: do not look at ./config for configuration perf tools: Make clean leaves some files perf lock: Dropping unsupported ':r' modifier perf probe: Fix coredump introduced by probe module option jump label: Reduce the cycle count by changing the link order perf report: Use ui__warning in some more places perf python: Add PERF_RECORD_{LOST,READ,SAMPLE} routine tables perf evlist: Introduce 'disable' method trace events: Update version number reference to new 3.x scheme for EVENT_POWER_TRACING_DEPRECATED perf buildid-cache: Zero out buffer of filenames when adding/removing buildid
Showing 16 changed files Inline Diff
- arch/x86/kernel/cpu/perf_event_intel.c
- kernel/Makefile
- kernel/trace/Kconfig
- tools/perf/Makefile
- tools/perf/builtin-lock.c
- tools/perf/builtin-record.c
- tools/perf/builtin-report.c
- tools/perf/builtin-sched.c
- tools/perf/util/config.c
- tools/perf/util/evlist.c
- tools/perf/util/evlist.h
- tools/perf/util/header.c
- tools/perf/util/probe-event.c
- tools/perf/util/python.c
- tools/perf/util/setup.py
- tools/perf/util/symbol.c
arch/x86/kernel/cpu/perf_event_intel.c
1 | #ifdef CONFIG_CPU_SUP_INTEL | 1 | #ifdef CONFIG_CPU_SUP_INTEL |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Per core/cpu state | 4 | * Per core/cpu state |
5 | * | 5 | * |
6 | * Used to coordinate shared registers between HT threads or | 6 | * Used to coordinate shared registers between HT threads or |
7 | * among events on a single PMU. | 7 | * among events on a single PMU. |
8 | */ | 8 | */ |
9 | struct intel_shared_regs { | 9 | struct intel_shared_regs { |
10 | struct er_account regs[EXTRA_REG_MAX]; | 10 | struct er_account regs[EXTRA_REG_MAX]; |
11 | int refcnt; /* per-core: #HT threads */ | 11 | int refcnt; /* per-core: #HT threads */ |
12 | unsigned core_id; /* per-core: core id */ | 12 | unsigned core_id; /* per-core: core id */ |
13 | }; | 13 | }; |
14 | 14 | ||
15 | /* | 15 | /* |
16 | * Intel PerfMon, used on Core and later. | 16 | * Intel PerfMon, used on Core and later. |
17 | */ | 17 | */ |
18 | static u64 intel_perfmon_event_map[PERF_COUNT_HW_MAX] __read_mostly = | 18 | static u64 intel_perfmon_event_map[PERF_COUNT_HW_MAX] __read_mostly = |
19 | { | 19 | { |
20 | [PERF_COUNT_HW_CPU_CYCLES] = 0x003c, | 20 | [PERF_COUNT_HW_CPU_CYCLES] = 0x003c, |
21 | [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, | 21 | [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, |
22 | [PERF_COUNT_HW_CACHE_REFERENCES] = 0x4f2e, | 22 | [PERF_COUNT_HW_CACHE_REFERENCES] = 0x4f2e, |
23 | [PERF_COUNT_HW_CACHE_MISSES] = 0x412e, | 23 | [PERF_COUNT_HW_CACHE_MISSES] = 0x412e, |
24 | [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, | 24 | [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, |
25 | [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, | 25 | [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, |
26 | [PERF_COUNT_HW_BUS_CYCLES] = 0x013c, | 26 | [PERF_COUNT_HW_BUS_CYCLES] = 0x013c, |
27 | }; | 27 | }; |
28 | 28 | ||
29 | static struct event_constraint intel_core_event_constraints[] __read_mostly = | 29 | static struct event_constraint intel_core_event_constraints[] __read_mostly = |
30 | { | 30 | { |
31 | INTEL_EVENT_CONSTRAINT(0x11, 0x2), /* FP_ASSIST */ | 31 | INTEL_EVENT_CONSTRAINT(0x11, 0x2), /* FP_ASSIST */ |
32 | INTEL_EVENT_CONSTRAINT(0x12, 0x2), /* MUL */ | 32 | INTEL_EVENT_CONSTRAINT(0x12, 0x2), /* MUL */ |
33 | INTEL_EVENT_CONSTRAINT(0x13, 0x2), /* DIV */ | 33 | INTEL_EVENT_CONSTRAINT(0x13, 0x2), /* DIV */ |
34 | INTEL_EVENT_CONSTRAINT(0x14, 0x1), /* CYCLES_DIV_BUSY */ | 34 | INTEL_EVENT_CONSTRAINT(0x14, 0x1), /* CYCLES_DIV_BUSY */ |
35 | INTEL_EVENT_CONSTRAINT(0x19, 0x2), /* DELAYED_BYPASS */ | 35 | INTEL_EVENT_CONSTRAINT(0x19, 0x2), /* DELAYED_BYPASS */ |
36 | INTEL_EVENT_CONSTRAINT(0xc1, 0x1), /* FP_COMP_INSTR_RET */ | 36 | INTEL_EVENT_CONSTRAINT(0xc1, 0x1), /* FP_COMP_INSTR_RET */ |
37 | EVENT_CONSTRAINT_END | 37 | EVENT_CONSTRAINT_END |
38 | }; | 38 | }; |
39 | 39 | ||
40 | static struct event_constraint intel_core2_event_constraints[] __read_mostly = | 40 | static struct event_constraint intel_core2_event_constraints[] __read_mostly = |
41 | { | 41 | { |
42 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ | 42 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ |
43 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ | 43 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ |
44 | /* | 44 | /* |
45 | * Core2 has Fixed Counter 2 listed as CPU_CLK_UNHALTED.REF and event | 45 | * Core2 has Fixed Counter 2 listed as CPU_CLK_UNHALTED.REF and event |
46 | * 0x013c as CPU_CLK_UNHALTED.BUS and specifies there is a fixed | 46 | * 0x013c as CPU_CLK_UNHALTED.BUS and specifies there is a fixed |
47 | * ratio between these counters. | 47 | * ratio between these counters. |
48 | */ | 48 | */ |
49 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ | 49 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ |
50 | INTEL_EVENT_CONSTRAINT(0x10, 0x1), /* FP_COMP_OPS_EXE */ | 50 | INTEL_EVENT_CONSTRAINT(0x10, 0x1), /* FP_COMP_OPS_EXE */ |
51 | INTEL_EVENT_CONSTRAINT(0x11, 0x2), /* FP_ASSIST */ | 51 | INTEL_EVENT_CONSTRAINT(0x11, 0x2), /* FP_ASSIST */ |
52 | INTEL_EVENT_CONSTRAINT(0x12, 0x2), /* MUL */ | 52 | INTEL_EVENT_CONSTRAINT(0x12, 0x2), /* MUL */ |
53 | INTEL_EVENT_CONSTRAINT(0x13, 0x2), /* DIV */ | 53 | INTEL_EVENT_CONSTRAINT(0x13, 0x2), /* DIV */ |
54 | INTEL_EVENT_CONSTRAINT(0x14, 0x1), /* CYCLES_DIV_BUSY */ | 54 | INTEL_EVENT_CONSTRAINT(0x14, 0x1), /* CYCLES_DIV_BUSY */ |
55 | INTEL_EVENT_CONSTRAINT(0x18, 0x1), /* IDLE_DURING_DIV */ | 55 | INTEL_EVENT_CONSTRAINT(0x18, 0x1), /* IDLE_DURING_DIV */ |
56 | INTEL_EVENT_CONSTRAINT(0x19, 0x2), /* DELAYED_BYPASS */ | 56 | INTEL_EVENT_CONSTRAINT(0x19, 0x2), /* DELAYED_BYPASS */ |
57 | INTEL_EVENT_CONSTRAINT(0xa1, 0x1), /* RS_UOPS_DISPATCH_CYCLES */ | 57 | INTEL_EVENT_CONSTRAINT(0xa1, 0x1), /* RS_UOPS_DISPATCH_CYCLES */ |
58 | INTEL_EVENT_CONSTRAINT(0xc9, 0x1), /* ITLB_MISS_RETIRED (T30-9) */ | 58 | INTEL_EVENT_CONSTRAINT(0xc9, 0x1), /* ITLB_MISS_RETIRED (T30-9) */ |
59 | INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED */ | 59 | INTEL_EVENT_CONSTRAINT(0xcb, 0x1), /* MEM_LOAD_RETIRED */ |
60 | EVENT_CONSTRAINT_END | 60 | EVENT_CONSTRAINT_END |
61 | }; | 61 | }; |
62 | 62 | ||
63 | static struct event_constraint intel_nehalem_event_constraints[] __read_mostly = | 63 | static struct event_constraint intel_nehalem_event_constraints[] __read_mostly = |
64 | { | 64 | { |
65 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ | 65 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ |
66 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ | 66 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ |
67 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ | 67 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ |
68 | INTEL_EVENT_CONSTRAINT(0x40, 0x3), /* L1D_CACHE_LD */ | 68 | INTEL_EVENT_CONSTRAINT(0x40, 0x3), /* L1D_CACHE_LD */ |
69 | INTEL_EVENT_CONSTRAINT(0x41, 0x3), /* L1D_CACHE_ST */ | 69 | INTEL_EVENT_CONSTRAINT(0x41, 0x3), /* L1D_CACHE_ST */ |
70 | INTEL_EVENT_CONSTRAINT(0x42, 0x3), /* L1D_CACHE_LOCK */ | 70 | INTEL_EVENT_CONSTRAINT(0x42, 0x3), /* L1D_CACHE_LOCK */ |
71 | INTEL_EVENT_CONSTRAINT(0x43, 0x3), /* L1D_ALL_REF */ | 71 | INTEL_EVENT_CONSTRAINT(0x43, 0x3), /* L1D_ALL_REF */ |
72 | INTEL_EVENT_CONSTRAINT(0x48, 0x3), /* L1D_PEND_MISS */ | 72 | INTEL_EVENT_CONSTRAINT(0x48, 0x3), /* L1D_PEND_MISS */ |
73 | INTEL_EVENT_CONSTRAINT(0x4e, 0x3), /* L1D_PREFETCH */ | 73 | INTEL_EVENT_CONSTRAINT(0x4e, 0x3), /* L1D_PREFETCH */ |
74 | INTEL_EVENT_CONSTRAINT(0x51, 0x3), /* L1D */ | 74 | INTEL_EVENT_CONSTRAINT(0x51, 0x3), /* L1D */ |
75 | INTEL_EVENT_CONSTRAINT(0x63, 0x3), /* CACHE_LOCK_CYCLES */ | 75 | INTEL_EVENT_CONSTRAINT(0x63, 0x3), /* CACHE_LOCK_CYCLES */ |
76 | EVENT_CONSTRAINT_END | 76 | EVENT_CONSTRAINT_END |
77 | }; | 77 | }; |
78 | 78 | ||
79 | static struct extra_reg intel_nehalem_extra_regs[] __read_mostly = | 79 | static struct extra_reg intel_nehalem_extra_regs[] __read_mostly = |
80 | { | 80 | { |
81 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff, RSP_0), | 81 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff, RSP_0), |
82 | EVENT_EXTRA_END | 82 | EVENT_EXTRA_END |
83 | }; | 83 | }; |
84 | 84 | ||
85 | static struct event_constraint intel_westmere_event_constraints[] __read_mostly = | 85 | static struct event_constraint intel_westmere_event_constraints[] __read_mostly = |
86 | { | 86 | { |
87 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ | 87 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ |
88 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ | 88 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ |
89 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ | 89 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ |
90 | INTEL_EVENT_CONSTRAINT(0x51, 0x3), /* L1D */ | 90 | INTEL_EVENT_CONSTRAINT(0x51, 0x3), /* L1D */ |
91 | INTEL_EVENT_CONSTRAINT(0x60, 0x1), /* OFFCORE_REQUESTS_OUTSTANDING */ | 91 | INTEL_EVENT_CONSTRAINT(0x60, 0x1), /* OFFCORE_REQUESTS_OUTSTANDING */ |
92 | INTEL_EVENT_CONSTRAINT(0x63, 0x3), /* CACHE_LOCK_CYCLES */ | 92 | INTEL_EVENT_CONSTRAINT(0x63, 0x3), /* CACHE_LOCK_CYCLES */ |
93 | INTEL_EVENT_CONSTRAINT(0xb3, 0x1), /* SNOOPQ_REQUEST_OUTSTANDING */ | 93 | INTEL_EVENT_CONSTRAINT(0xb3, 0x1), /* SNOOPQ_REQUEST_OUTSTANDING */ |
94 | EVENT_CONSTRAINT_END | 94 | EVENT_CONSTRAINT_END |
95 | }; | 95 | }; |
96 | 96 | ||
97 | static struct event_constraint intel_snb_event_constraints[] __read_mostly = | 97 | static struct event_constraint intel_snb_event_constraints[] __read_mostly = |
98 | { | 98 | { |
99 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ | 99 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ |
100 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ | 100 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ |
101 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ | 101 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ |
102 | INTEL_EVENT_CONSTRAINT(0x48, 0x4), /* L1D_PEND_MISS.PENDING */ | 102 | INTEL_EVENT_CONSTRAINT(0x48, 0x4), /* L1D_PEND_MISS.PENDING */ |
103 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */ | 103 | INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PREC_DIST */ |
104 | INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.LOAD_LATENCY */ | 104 | INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.LOAD_LATENCY */ |
105 | EVENT_CONSTRAINT_END | 105 | EVENT_CONSTRAINT_END |
106 | }; | 106 | }; |
107 | 107 | ||
108 | static struct extra_reg intel_westmere_extra_regs[] __read_mostly = | 108 | static struct extra_reg intel_westmere_extra_regs[] __read_mostly = |
109 | { | 109 | { |
110 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff, RSP_0), | 110 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0xffff, RSP_0), |
111 | INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0xffff, RSP_1), | 111 | INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0xffff, RSP_1), |
112 | EVENT_EXTRA_END | 112 | EVENT_EXTRA_END |
113 | }; | 113 | }; |
114 | 114 | ||
115 | static struct event_constraint intel_v1_event_constraints[] __read_mostly = | 115 | static struct event_constraint intel_v1_event_constraints[] __read_mostly = |
116 | { | 116 | { |
117 | EVENT_CONSTRAINT_END | 117 | EVENT_CONSTRAINT_END |
118 | }; | 118 | }; |
119 | 119 | ||
120 | static struct event_constraint intel_gen_event_constraints[] __read_mostly = | 120 | static struct event_constraint intel_gen_event_constraints[] __read_mostly = |
121 | { | 121 | { |
122 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ | 122 | FIXED_EVENT_CONSTRAINT(0x00c0, 0), /* INST_RETIRED.ANY */ |
123 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ | 123 | FIXED_EVENT_CONSTRAINT(0x003c, 1), /* CPU_CLK_UNHALTED.CORE */ |
124 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ | 124 | /* FIXED_EVENT_CONSTRAINT(0x013c, 2), CPU_CLK_UNHALTED.REF */ |
125 | EVENT_CONSTRAINT_END | 125 | EVENT_CONSTRAINT_END |
126 | }; | 126 | }; |
127 | 127 | ||
128 | static struct extra_reg intel_snb_extra_regs[] __read_mostly = { | 128 | static struct extra_reg intel_snb_extra_regs[] __read_mostly = { |
129 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0x3fffffffffull, RSP_0), | 129 | INTEL_EVENT_EXTRA_REG(0xb7, MSR_OFFCORE_RSP_0, 0x3fffffffffull, RSP_0), |
130 | INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0x3fffffffffull, RSP_1), | 130 | INTEL_EVENT_EXTRA_REG(0xbb, MSR_OFFCORE_RSP_1, 0x3fffffffffull, RSP_1), |
131 | EVENT_EXTRA_END | 131 | EVENT_EXTRA_END |
132 | }; | 132 | }; |
133 | 133 | ||
134 | static u64 intel_pmu_event_map(int hw_event) | 134 | static u64 intel_pmu_event_map(int hw_event) |
135 | { | 135 | { |
136 | return intel_perfmon_event_map[hw_event]; | 136 | return intel_perfmon_event_map[hw_event]; |
137 | } | 137 | } |
138 | 138 | ||
139 | static __initconst const u64 snb_hw_cache_event_ids | 139 | static __initconst const u64 snb_hw_cache_event_ids |
140 | [PERF_COUNT_HW_CACHE_MAX] | 140 | [PERF_COUNT_HW_CACHE_MAX] |
141 | [PERF_COUNT_HW_CACHE_OP_MAX] | 141 | [PERF_COUNT_HW_CACHE_OP_MAX] |
142 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | 142 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = |
143 | { | 143 | { |
144 | [ C(L1D) ] = { | 144 | [ C(L1D) ] = { |
145 | [ C(OP_READ) ] = { | 145 | [ C(OP_READ) ] = { |
146 | [ C(RESULT_ACCESS) ] = 0xf1d0, /* MEM_UOP_RETIRED.LOADS */ | 146 | [ C(RESULT_ACCESS) ] = 0xf1d0, /* MEM_UOP_RETIRED.LOADS */ |
147 | [ C(RESULT_MISS) ] = 0x0151, /* L1D.REPLACEMENT */ | 147 | [ C(RESULT_MISS) ] = 0x0151, /* L1D.REPLACEMENT */ |
148 | }, | 148 | }, |
149 | [ C(OP_WRITE) ] = { | 149 | [ C(OP_WRITE) ] = { |
150 | [ C(RESULT_ACCESS) ] = 0xf2d0, /* MEM_UOP_RETIRED.STORES */ | 150 | [ C(RESULT_ACCESS) ] = 0xf2d0, /* MEM_UOP_RETIRED.STORES */ |
151 | [ C(RESULT_MISS) ] = 0x0851, /* L1D.ALL_M_REPLACEMENT */ | 151 | [ C(RESULT_MISS) ] = 0x0851, /* L1D.ALL_M_REPLACEMENT */ |
152 | }, | 152 | }, |
153 | [ C(OP_PREFETCH) ] = { | 153 | [ C(OP_PREFETCH) ] = { |
154 | [ C(RESULT_ACCESS) ] = 0x0, | 154 | [ C(RESULT_ACCESS) ] = 0x0, |
155 | [ C(RESULT_MISS) ] = 0x024e, /* HW_PRE_REQ.DL1_MISS */ | 155 | [ C(RESULT_MISS) ] = 0x024e, /* HW_PRE_REQ.DL1_MISS */ |
156 | }, | 156 | }, |
157 | }, | 157 | }, |
158 | [ C(L1I ) ] = { | 158 | [ C(L1I ) ] = { |
159 | [ C(OP_READ) ] = { | 159 | [ C(OP_READ) ] = { |
160 | [ C(RESULT_ACCESS) ] = 0x0, | 160 | [ C(RESULT_ACCESS) ] = 0x0, |
161 | [ C(RESULT_MISS) ] = 0x0280, /* ICACHE.MISSES */ | 161 | [ C(RESULT_MISS) ] = 0x0280, /* ICACHE.MISSES */ |
162 | }, | 162 | }, |
163 | [ C(OP_WRITE) ] = { | 163 | [ C(OP_WRITE) ] = { |
164 | [ C(RESULT_ACCESS) ] = -1, | 164 | [ C(RESULT_ACCESS) ] = -1, |
165 | [ C(RESULT_MISS) ] = -1, | 165 | [ C(RESULT_MISS) ] = -1, |
166 | }, | 166 | }, |
167 | [ C(OP_PREFETCH) ] = { | 167 | [ C(OP_PREFETCH) ] = { |
168 | [ C(RESULT_ACCESS) ] = 0x0, | 168 | [ C(RESULT_ACCESS) ] = 0x0, |
169 | [ C(RESULT_MISS) ] = 0x0, | 169 | [ C(RESULT_MISS) ] = 0x0, |
170 | }, | 170 | }, |
171 | }, | 171 | }, |
172 | [ C(LL ) ] = { | 172 | [ C(LL ) ] = { |
173 | [ C(OP_READ) ] = { | 173 | [ C(OP_READ) ] = { |
174 | /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */ | 174 | /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */ |
175 | [ C(RESULT_ACCESS) ] = 0x01b7, | 175 | [ C(RESULT_ACCESS) ] = 0x01b7, |
176 | /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */ | 176 | /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */ |
177 | [ C(RESULT_MISS) ] = 0x01b7, | 177 | [ C(RESULT_MISS) ] = 0x01b7, |
178 | }, | 178 | }, |
179 | [ C(OP_WRITE) ] = { | 179 | [ C(OP_WRITE) ] = { |
180 | /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */ | 180 | /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */ |
181 | [ C(RESULT_ACCESS) ] = 0x01b7, | 181 | [ C(RESULT_ACCESS) ] = 0x01b7, |
182 | /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */ | 182 | /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */ |
183 | [ C(RESULT_MISS) ] = 0x01b7, | 183 | [ C(RESULT_MISS) ] = 0x01b7, |
184 | }, | 184 | }, |
185 | [ C(OP_PREFETCH) ] = { | 185 | [ C(OP_PREFETCH) ] = { |
186 | /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */ | 186 | /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */ |
187 | [ C(RESULT_ACCESS) ] = 0x01b7, | 187 | [ C(RESULT_ACCESS) ] = 0x01b7, |
188 | /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */ | 188 | /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */ |
189 | [ C(RESULT_MISS) ] = 0x01b7, | 189 | [ C(RESULT_MISS) ] = 0x01b7, |
190 | }, | 190 | }, |
191 | }, | 191 | }, |
192 | [ C(DTLB) ] = { | 192 | [ C(DTLB) ] = { |
193 | [ C(OP_READ) ] = { | 193 | [ C(OP_READ) ] = { |
194 | [ C(RESULT_ACCESS) ] = 0x81d0, /* MEM_UOP_RETIRED.ALL_LOADS */ | 194 | [ C(RESULT_ACCESS) ] = 0x81d0, /* MEM_UOP_RETIRED.ALL_LOADS */ |
195 | [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.CAUSES_A_WALK */ | 195 | [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.CAUSES_A_WALK */ |
196 | }, | 196 | }, |
197 | [ C(OP_WRITE) ] = { | 197 | [ C(OP_WRITE) ] = { |
198 | [ C(RESULT_ACCESS) ] = 0x82d0, /* MEM_UOP_RETIRED.ALL_STORES */ | 198 | [ C(RESULT_ACCESS) ] = 0x82d0, /* MEM_UOP_RETIRED.ALL_STORES */ |
199 | [ C(RESULT_MISS) ] = 0x0149, /* DTLB_STORE_MISSES.MISS_CAUSES_A_WALK */ | 199 | [ C(RESULT_MISS) ] = 0x0149, /* DTLB_STORE_MISSES.MISS_CAUSES_A_WALK */ |
200 | }, | 200 | }, |
201 | [ C(OP_PREFETCH) ] = { | 201 | [ C(OP_PREFETCH) ] = { |
202 | [ C(RESULT_ACCESS) ] = 0x0, | 202 | [ C(RESULT_ACCESS) ] = 0x0, |
203 | [ C(RESULT_MISS) ] = 0x0, | 203 | [ C(RESULT_MISS) ] = 0x0, |
204 | }, | 204 | }, |
205 | }, | 205 | }, |
206 | [ C(ITLB) ] = { | 206 | [ C(ITLB) ] = { |
207 | [ C(OP_READ) ] = { | 207 | [ C(OP_READ) ] = { |
208 | [ C(RESULT_ACCESS) ] = 0x1085, /* ITLB_MISSES.STLB_HIT */ | 208 | [ C(RESULT_ACCESS) ] = 0x1085, /* ITLB_MISSES.STLB_HIT */ |
209 | [ C(RESULT_MISS) ] = 0x0185, /* ITLB_MISSES.CAUSES_A_WALK */ | 209 | [ C(RESULT_MISS) ] = 0x0185, /* ITLB_MISSES.CAUSES_A_WALK */ |
210 | }, | 210 | }, |
211 | [ C(OP_WRITE) ] = { | 211 | [ C(OP_WRITE) ] = { |
212 | [ C(RESULT_ACCESS) ] = -1, | 212 | [ C(RESULT_ACCESS) ] = -1, |
213 | [ C(RESULT_MISS) ] = -1, | 213 | [ C(RESULT_MISS) ] = -1, |
214 | }, | 214 | }, |
215 | [ C(OP_PREFETCH) ] = { | 215 | [ C(OP_PREFETCH) ] = { |
216 | [ C(RESULT_ACCESS) ] = -1, | 216 | [ C(RESULT_ACCESS) ] = -1, |
217 | [ C(RESULT_MISS) ] = -1, | 217 | [ C(RESULT_MISS) ] = -1, |
218 | }, | 218 | }, |
219 | }, | 219 | }, |
220 | [ C(BPU ) ] = { | 220 | [ C(BPU ) ] = { |
221 | [ C(OP_READ) ] = { | 221 | [ C(OP_READ) ] = { |
222 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ | 222 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ |
223 | [ C(RESULT_MISS) ] = 0x00c5, /* BR_MISP_RETIRED.ALL_BRANCHES */ | 223 | [ C(RESULT_MISS) ] = 0x00c5, /* BR_MISP_RETIRED.ALL_BRANCHES */ |
224 | }, | 224 | }, |
225 | [ C(OP_WRITE) ] = { | 225 | [ C(OP_WRITE) ] = { |
226 | [ C(RESULT_ACCESS) ] = -1, | 226 | [ C(RESULT_ACCESS) ] = -1, |
227 | [ C(RESULT_MISS) ] = -1, | 227 | [ C(RESULT_MISS) ] = -1, |
228 | }, | 228 | }, |
229 | [ C(OP_PREFETCH) ] = { | 229 | [ C(OP_PREFETCH) ] = { |
230 | [ C(RESULT_ACCESS) ] = -1, | 230 | [ C(RESULT_ACCESS) ] = -1, |
231 | [ C(RESULT_MISS) ] = -1, | 231 | [ C(RESULT_MISS) ] = -1, |
232 | }, | 232 | }, |
233 | }, | 233 | }, |
234 | [ C(NODE) ] = { | 234 | [ C(NODE) ] = { |
235 | [ C(OP_READ) ] = { | 235 | [ C(OP_READ) ] = { |
236 | [ C(RESULT_ACCESS) ] = -1, | 236 | [ C(RESULT_ACCESS) ] = -1, |
237 | [ C(RESULT_MISS) ] = -1, | 237 | [ C(RESULT_MISS) ] = -1, |
238 | }, | 238 | }, |
239 | [ C(OP_WRITE) ] = { | 239 | [ C(OP_WRITE) ] = { |
240 | [ C(RESULT_ACCESS) ] = -1, | 240 | [ C(RESULT_ACCESS) ] = -1, |
241 | [ C(RESULT_MISS) ] = -1, | 241 | [ C(RESULT_MISS) ] = -1, |
242 | }, | 242 | }, |
243 | [ C(OP_PREFETCH) ] = { | 243 | [ C(OP_PREFETCH) ] = { |
244 | [ C(RESULT_ACCESS) ] = -1, | 244 | [ C(RESULT_ACCESS) ] = -1, |
245 | [ C(RESULT_MISS) ] = -1, | 245 | [ C(RESULT_MISS) ] = -1, |
246 | }, | 246 | }, |
247 | }, | 247 | }, |
248 | 248 | ||
249 | }; | 249 | }; |
250 | 250 | ||
251 | static __initconst const u64 westmere_hw_cache_event_ids | 251 | static __initconst const u64 westmere_hw_cache_event_ids |
252 | [PERF_COUNT_HW_CACHE_MAX] | 252 | [PERF_COUNT_HW_CACHE_MAX] |
253 | [PERF_COUNT_HW_CACHE_OP_MAX] | 253 | [PERF_COUNT_HW_CACHE_OP_MAX] |
254 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | 254 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = |
255 | { | 255 | { |
256 | [ C(L1D) ] = { | 256 | [ C(L1D) ] = { |
257 | [ C(OP_READ) ] = { | 257 | [ C(OP_READ) ] = { |
258 | [ C(RESULT_ACCESS) ] = 0x010b, /* MEM_INST_RETIRED.LOADS */ | 258 | [ C(RESULT_ACCESS) ] = 0x010b, /* MEM_INST_RETIRED.LOADS */ |
259 | [ C(RESULT_MISS) ] = 0x0151, /* L1D.REPL */ | 259 | [ C(RESULT_MISS) ] = 0x0151, /* L1D.REPL */ |
260 | }, | 260 | }, |
261 | [ C(OP_WRITE) ] = { | 261 | [ C(OP_WRITE) ] = { |
262 | [ C(RESULT_ACCESS) ] = 0x020b, /* MEM_INST_RETURED.STORES */ | 262 | [ C(RESULT_ACCESS) ] = 0x020b, /* MEM_INST_RETURED.STORES */ |
263 | [ C(RESULT_MISS) ] = 0x0251, /* L1D.M_REPL */ | 263 | [ C(RESULT_MISS) ] = 0x0251, /* L1D.M_REPL */ |
264 | }, | 264 | }, |
265 | [ C(OP_PREFETCH) ] = { | 265 | [ C(OP_PREFETCH) ] = { |
266 | [ C(RESULT_ACCESS) ] = 0x014e, /* L1D_PREFETCH.REQUESTS */ | 266 | [ C(RESULT_ACCESS) ] = 0x014e, /* L1D_PREFETCH.REQUESTS */ |
267 | [ C(RESULT_MISS) ] = 0x024e, /* L1D_PREFETCH.MISS */ | 267 | [ C(RESULT_MISS) ] = 0x024e, /* L1D_PREFETCH.MISS */ |
268 | }, | 268 | }, |
269 | }, | 269 | }, |
270 | [ C(L1I ) ] = { | 270 | [ C(L1I ) ] = { |
271 | [ C(OP_READ) ] = { | 271 | [ C(OP_READ) ] = { |
272 | [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ | 272 | [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ |
273 | [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ | 273 | [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ |
274 | }, | 274 | }, |
275 | [ C(OP_WRITE) ] = { | 275 | [ C(OP_WRITE) ] = { |
276 | [ C(RESULT_ACCESS) ] = -1, | 276 | [ C(RESULT_ACCESS) ] = -1, |
277 | [ C(RESULT_MISS) ] = -1, | 277 | [ C(RESULT_MISS) ] = -1, |
278 | }, | 278 | }, |
279 | [ C(OP_PREFETCH) ] = { | 279 | [ C(OP_PREFETCH) ] = { |
280 | [ C(RESULT_ACCESS) ] = 0x0, | 280 | [ C(RESULT_ACCESS) ] = 0x0, |
281 | [ C(RESULT_MISS) ] = 0x0, | 281 | [ C(RESULT_MISS) ] = 0x0, |
282 | }, | 282 | }, |
283 | }, | 283 | }, |
284 | [ C(LL ) ] = { | 284 | [ C(LL ) ] = { |
285 | [ C(OP_READ) ] = { | 285 | [ C(OP_READ) ] = { |
286 | /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */ | 286 | /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */ |
287 | [ C(RESULT_ACCESS) ] = 0x01b7, | 287 | [ C(RESULT_ACCESS) ] = 0x01b7, |
288 | /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */ | 288 | /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */ |
289 | [ C(RESULT_MISS) ] = 0x01b7, | 289 | [ C(RESULT_MISS) ] = 0x01b7, |
290 | }, | 290 | }, |
291 | /* | 291 | /* |
292 | * Use RFO, not WRITEBACK, because a write miss would typically occur | 292 | * Use RFO, not WRITEBACK, because a write miss would typically occur |
293 | * on RFO. | 293 | * on RFO. |
294 | */ | 294 | */ |
295 | [ C(OP_WRITE) ] = { | 295 | [ C(OP_WRITE) ] = { |
296 | /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */ | 296 | /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */ |
297 | [ C(RESULT_ACCESS) ] = 0x01b7, | 297 | [ C(RESULT_ACCESS) ] = 0x01b7, |
298 | /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */ | 298 | /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */ |
299 | [ C(RESULT_MISS) ] = 0x01b7, | 299 | [ C(RESULT_MISS) ] = 0x01b7, |
300 | }, | 300 | }, |
301 | [ C(OP_PREFETCH) ] = { | 301 | [ C(OP_PREFETCH) ] = { |
302 | /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */ | 302 | /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */ |
303 | [ C(RESULT_ACCESS) ] = 0x01b7, | 303 | [ C(RESULT_ACCESS) ] = 0x01b7, |
304 | /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */ | 304 | /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */ |
305 | [ C(RESULT_MISS) ] = 0x01b7, | 305 | [ C(RESULT_MISS) ] = 0x01b7, |
306 | }, | 306 | }, |
307 | }, | 307 | }, |
308 | [ C(DTLB) ] = { | 308 | [ C(DTLB) ] = { |
309 | [ C(OP_READ) ] = { | 309 | [ C(OP_READ) ] = { |
310 | [ C(RESULT_ACCESS) ] = 0x010b, /* MEM_INST_RETIRED.LOADS */ | 310 | [ C(RESULT_ACCESS) ] = 0x010b, /* MEM_INST_RETIRED.LOADS */ |
311 | [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.ANY */ | 311 | [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.ANY */ |
312 | }, | 312 | }, |
313 | [ C(OP_WRITE) ] = { | 313 | [ C(OP_WRITE) ] = { |
314 | [ C(RESULT_ACCESS) ] = 0x020b, /* MEM_INST_RETURED.STORES */ | 314 | [ C(RESULT_ACCESS) ] = 0x020b, /* MEM_INST_RETURED.STORES */ |
315 | [ C(RESULT_MISS) ] = 0x010c, /* MEM_STORE_RETIRED.DTLB_MISS */ | 315 | [ C(RESULT_MISS) ] = 0x010c, /* MEM_STORE_RETIRED.DTLB_MISS */ |
316 | }, | 316 | }, |
317 | [ C(OP_PREFETCH) ] = { | 317 | [ C(OP_PREFETCH) ] = { |
318 | [ C(RESULT_ACCESS) ] = 0x0, | 318 | [ C(RESULT_ACCESS) ] = 0x0, |
319 | [ C(RESULT_MISS) ] = 0x0, | 319 | [ C(RESULT_MISS) ] = 0x0, |
320 | }, | 320 | }, |
321 | }, | 321 | }, |
322 | [ C(ITLB) ] = { | 322 | [ C(ITLB) ] = { |
323 | [ C(OP_READ) ] = { | 323 | [ C(OP_READ) ] = { |
324 | [ C(RESULT_ACCESS) ] = 0x01c0, /* INST_RETIRED.ANY_P */ | 324 | [ C(RESULT_ACCESS) ] = 0x01c0, /* INST_RETIRED.ANY_P */ |
325 | [ C(RESULT_MISS) ] = 0x0185, /* ITLB_MISSES.ANY */ | 325 | [ C(RESULT_MISS) ] = 0x0185, /* ITLB_MISSES.ANY */ |
326 | }, | 326 | }, |
327 | [ C(OP_WRITE) ] = { | 327 | [ C(OP_WRITE) ] = { |
328 | [ C(RESULT_ACCESS) ] = -1, | 328 | [ C(RESULT_ACCESS) ] = -1, |
329 | [ C(RESULT_MISS) ] = -1, | 329 | [ C(RESULT_MISS) ] = -1, |
330 | }, | 330 | }, |
331 | [ C(OP_PREFETCH) ] = { | 331 | [ C(OP_PREFETCH) ] = { |
332 | [ C(RESULT_ACCESS) ] = -1, | 332 | [ C(RESULT_ACCESS) ] = -1, |
333 | [ C(RESULT_MISS) ] = -1, | 333 | [ C(RESULT_MISS) ] = -1, |
334 | }, | 334 | }, |
335 | }, | 335 | }, |
336 | [ C(BPU ) ] = { | 336 | [ C(BPU ) ] = { |
337 | [ C(OP_READ) ] = { | 337 | [ C(OP_READ) ] = { |
338 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ | 338 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ |
339 | [ C(RESULT_MISS) ] = 0x03e8, /* BPU_CLEARS.ANY */ | 339 | [ C(RESULT_MISS) ] = 0x03e8, /* BPU_CLEARS.ANY */ |
340 | }, | 340 | }, |
341 | [ C(OP_WRITE) ] = { | 341 | [ C(OP_WRITE) ] = { |
342 | [ C(RESULT_ACCESS) ] = -1, | 342 | [ C(RESULT_ACCESS) ] = -1, |
343 | [ C(RESULT_MISS) ] = -1, | 343 | [ C(RESULT_MISS) ] = -1, |
344 | }, | 344 | }, |
345 | [ C(OP_PREFETCH) ] = { | 345 | [ C(OP_PREFETCH) ] = { |
346 | [ C(RESULT_ACCESS) ] = -1, | 346 | [ C(RESULT_ACCESS) ] = -1, |
347 | [ C(RESULT_MISS) ] = -1, | 347 | [ C(RESULT_MISS) ] = -1, |
348 | }, | 348 | }, |
349 | }, | 349 | }, |
350 | [ C(NODE) ] = { | 350 | [ C(NODE) ] = { |
351 | [ C(OP_READ) ] = { | 351 | [ C(OP_READ) ] = { |
352 | [ C(RESULT_ACCESS) ] = 0x01b7, | 352 | [ C(RESULT_ACCESS) ] = 0x01b7, |
353 | [ C(RESULT_MISS) ] = 0x01b7, | 353 | [ C(RESULT_MISS) ] = 0x01b7, |
354 | }, | 354 | }, |
355 | [ C(OP_WRITE) ] = { | 355 | [ C(OP_WRITE) ] = { |
356 | [ C(RESULT_ACCESS) ] = 0x01b7, | 356 | [ C(RESULT_ACCESS) ] = 0x01b7, |
357 | [ C(RESULT_MISS) ] = 0x01b7, | 357 | [ C(RESULT_MISS) ] = 0x01b7, |
358 | }, | 358 | }, |
359 | [ C(OP_PREFETCH) ] = { | 359 | [ C(OP_PREFETCH) ] = { |
360 | [ C(RESULT_ACCESS) ] = 0x01b7, | 360 | [ C(RESULT_ACCESS) ] = 0x01b7, |
361 | [ C(RESULT_MISS) ] = 0x01b7, | 361 | [ C(RESULT_MISS) ] = 0x01b7, |
362 | }, | 362 | }, |
363 | }, | 363 | }, |
364 | }; | 364 | }; |
365 | 365 | ||
366 | /* | 366 | /* |
367 | * Nehalem/Westmere MSR_OFFCORE_RESPONSE bits; | 367 | * Nehalem/Westmere MSR_OFFCORE_RESPONSE bits; |
368 | * See IA32 SDM Vol 3B 30.6.1.3 | 368 | * See IA32 SDM Vol 3B 30.6.1.3 |
369 | */ | 369 | */ |
370 | 370 | ||
371 | #define NHM_DMND_DATA_RD (1 << 0) | 371 | #define NHM_DMND_DATA_RD (1 << 0) |
372 | #define NHM_DMND_RFO (1 << 1) | 372 | #define NHM_DMND_RFO (1 << 1) |
373 | #define NHM_DMND_IFETCH (1 << 2) | 373 | #define NHM_DMND_IFETCH (1 << 2) |
374 | #define NHM_DMND_WB (1 << 3) | 374 | #define NHM_DMND_WB (1 << 3) |
375 | #define NHM_PF_DATA_RD (1 << 4) | 375 | #define NHM_PF_DATA_RD (1 << 4) |
376 | #define NHM_PF_DATA_RFO (1 << 5) | 376 | #define NHM_PF_DATA_RFO (1 << 5) |
377 | #define NHM_PF_IFETCH (1 << 6) | 377 | #define NHM_PF_IFETCH (1 << 6) |
378 | #define NHM_OFFCORE_OTHER (1 << 7) | 378 | #define NHM_OFFCORE_OTHER (1 << 7) |
379 | #define NHM_UNCORE_HIT (1 << 8) | 379 | #define NHM_UNCORE_HIT (1 << 8) |
380 | #define NHM_OTHER_CORE_HIT_SNP (1 << 9) | 380 | #define NHM_OTHER_CORE_HIT_SNP (1 << 9) |
381 | #define NHM_OTHER_CORE_HITM (1 << 10) | 381 | #define NHM_OTHER_CORE_HITM (1 << 10) |
382 | /* reserved */ | 382 | /* reserved */ |
383 | #define NHM_REMOTE_CACHE_FWD (1 << 12) | 383 | #define NHM_REMOTE_CACHE_FWD (1 << 12) |
384 | #define NHM_REMOTE_DRAM (1 << 13) | 384 | #define NHM_REMOTE_DRAM (1 << 13) |
385 | #define NHM_LOCAL_DRAM (1 << 14) | 385 | #define NHM_LOCAL_DRAM (1 << 14) |
386 | #define NHM_NON_DRAM (1 << 15) | 386 | #define NHM_NON_DRAM (1 << 15) |
387 | 387 | ||
388 | #define NHM_ALL_DRAM (NHM_REMOTE_DRAM|NHM_LOCAL_DRAM) | 388 | #define NHM_ALL_DRAM (NHM_REMOTE_DRAM|NHM_LOCAL_DRAM) |
389 | 389 | ||
390 | #define NHM_DMND_READ (NHM_DMND_DATA_RD) | 390 | #define NHM_DMND_READ (NHM_DMND_DATA_RD) |
391 | #define NHM_DMND_WRITE (NHM_DMND_RFO|NHM_DMND_WB) | 391 | #define NHM_DMND_WRITE (NHM_DMND_RFO|NHM_DMND_WB) |
392 | #define NHM_DMND_PREFETCH (NHM_PF_DATA_RD|NHM_PF_DATA_RFO) | 392 | #define NHM_DMND_PREFETCH (NHM_PF_DATA_RD|NHM_PF_DATA_RFO) |
393 | 393 | ||
394 | #define NHM_L3_HIT (NHM_UNCORE_HIT|NHM_OTHER_CORE_HIT_SNP|NHM_OTHER_CORE_HITM) | 394 | #define NHM_L3_HIT (NHM_UNCORE_HIT|NHM_OTHER_CORE_HIT_SNP|NHM_OTHER_CORE_HITM) |
395 | #define NHM_L3_MISS (NHM_NON_DRAM|NHM_ALL_DRAM|NHM_REMOTE_CACHE_FWD) | 395 | #define NHM_L3_MISS (NHM_NON_DRAM|NHM_ALL_DRAM|NHM_REMOTE_CACHE_FWD) |
396 | #define NHM_L3_ACCESS (NHM_L3_HIT|NHM_L3_MISS) | 396 | #define NHM_L3_ACCESS (NHM_L3_HIT|NHM_L3_MISS) |
397 | 397 | ||
398 | static __initconst const u64 nehalem_hw_cache_extra_regs | 398 | static __initconst const u64 nehalem_hw_cache_extra_regs |
399 | [PERF_COUNT_HW_CACHE_MAX] | 399 | [PERF_COUNT_HW_CACHE_MAX] |
400 | [PERF_COUNT_HW_CACHE_OP_MAX] | 400 | [PERF_COUNT_HW_CACHE_OP_MAX] |
401 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | 401 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = |
402 | { | 402 | { |
403 | [ C(LL ) ] = { | 403 | [ C(LL ) ] = { |
404 | [ C(OP_READ) ] = { | 404 | [ C(OP_READ) ] = { |
405 | [ C(RESULT_ACCESS) ] = NHM_DMND_READ|NHM_L3_ACCESS, | 405 | [ C(RESULT_ACCESS) ] = NHM_DMND_READ|NHM_L3_ACCESS, |
406 | [ C(RESULT_MISS) ] = NHM_DMND_READ|NHM_L3_MISS, | 406 | [ C(RESULT_MISS) ] = NHM_DMND_READ|NHM_L3_MISS, |
407 | }, | 407 | }, |
408 | [ C(OP_WRITE) ] = { | 408 | [ C(OP_WRITE) ] = { |
409 | [ C(RESULT_ACCESS) ] = NHM_DMND_WRITE|NHM_L3_ACCESS, | 409 | [ C(RESULT_ACCESS) ] = NHM_DMND_WRITE|NHM_L3_ACCESS, |
410 | [ C(RESULT_MISS) ] = NHM_DMND_WRITE|NHM_L3_MISS, | 410 | [ C(RESULT_MISS) ] = NHM_DMND_WRITE|NHM_L3_MISS, |
411 | }, | 411 | }, |
412 | [ C(OP_PREFETCH) ] = { | 412 | [ C(OP_PREFETCH) ] = { |
413 | [ C(RESULT_ACCESS) ] = NHM_DMND_PREFETCH|NHM_L3_ACCESS, | 413 | [ C(RESULT_ACCESS) ] = NHM_DMND_PREFETCH|NHM_L3_ACCESS, |
414 | [ C(RESULT_MISS) ] = NHM_DMND_PREFETCH|NHM_L3_MISS, | 414 | [ C(RESULT_MISS) ] = NHM_DMND_PREFETCH|NHM_L3_MISS, |
415 | }, | 415 | }, |
416 | }, | 416 | }, |
417 | [ C(NODE) ] = { | 417 | [ C(NODE) ] = { |
418 | [ C(OP_READ) ] = { | 418 | [ C(OP_READ) ] = { |
419 | [ C(RESULT_ACCESS) ] = NHM_DMND_READ|NHM_ALL_DRAM, | 419 | [ C(RESULT_ACCESS) ] = NHM_DMND_READ|NHM_ALL_DRAM, |
420 | [ C(RESULT_MISS) ] = NHM_DMND_READ|NHM_REMOTE_DRAM, | 420 | [ C(RESULT_MISS) ] = NHM_DMND_READ|NHM_REMOTE_DRAM, |
421 | }, | 421 | }, |
422 | [ C(OP_WRITE) ] = { | 422 | [ C(OP_WRITE) ] = { |
423 | [ C(RESULT_ACCESS) ] = NHM_DMND_WRITE|NHM_ALL_DRAM, | 423 | [ C(RESULT_ACCESS) ] = NHM_DMND_WRITE|NHM_ALL_DRAM, |
424 | [ C(RESULT_MISS) ] = NHM_DMND_WRITE|NHM_REMOTE_DRAM, | 424 | [ C(RESULT_MISS) ] = NHM_DMND_WRITE|NHM_REMOTE_DRAM, |
425 | }, | 425 | }, |
426 | [ C(OP_PREFETCH) ] = { | 426 | [ C(OP_PREFETCH) ] = { |
427 | [ C(RESULT_ACCESS) ] = NHM_DMND_PREFETCH|NHM_ALL_DRAM, | 427 | [ C(RESULT_ACCESS) ] = NHM_DMND_PREFETCH|NHM_ALL_DRAM, |
428 | [ C(RESULT_MISS) ] = NHM_DMND_PREFETCH|NHM_REMOTE_DRAM, | 428 | [ C(RESULT_MISS) ] = NHM_DMND_PREFETCH|NHM_REMOTE_DRAM, |
429 | }, | 429 | }, |
430 | }, | 430 | }, |
431 | }; | 431 | }; |
432 | 432 | ||
433 | static __initconst const u64 nehalem_hw_cache_event_ids | 433 | static __initconst const u64 nehalem_hw_cache_event_ids |
434 | [PERF_COUNT_HW_CACHE_MAX] | 434 | [PERF_COUNT_HW_CACHE_MAX] |
435 | [PERF_COUNT_HW_CACHE_OP_MAX] | 435 | [PERF_COUNT_HW_CACHE_OP_MAX] |
436 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | 436 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = |
437 | { | 437 | { |
438 | [ C(L1D) ] = { | 438 | [ C(L1D) ] = { |
439 | [ C(OP_READ) ] = { | 439 | [ C(OP_READ) ] = { |
440 | [ C(RESULT_ACCESS) ] = 0x010b, /* MEM_INST_RETIRED.LOADS */ | 440 | [ C(RESULT_ACCESS) ] = 0x010b, /* MEM_INST_RETIRED.LOADS */ |
441 | [ C(RESULT_MISS) ] = 0x0151, /* L1D.REPL */ | 441 | [ C(RESULT_MISS) ] = 0x0151, /* L1D.REPL */ |
442 | }, | 442 | }, |
443 | [ C(OP_WRITE) ] = { | 443 | [ C(OP_WRITE) ] = { |
444 | [ C(RESULT_ACCESS) ] = 0x020b, /* MEM_INST_RETURED.STORES */ | 444 | [ C(RESULT_ACCESS) ] = 0x020b, /* MEM_INST_RETURED.STORES */ |
445 | [ C(RESULT_MISS) ] = 0x0251, /* L1D.M_REPL */ | 445 | [ C(RESULT_MISS) ] = 0x0251, /* L1D.M_REPL */ |
446 | }, | 446 | }, |
447 | [ C(OP_PREFETCH) ] = { | 447 | [ C(OP_PREFETCH) ] = { |
448 | [ C(RESULT_ACCESS) ] = 0x014e, /* L1D_PREFETCH.REQUESTS */ | 448 | [ C(RESULT_ACCESS) ] = 0x014e, /* L1D_PREFETCH.REQUESTS */ |
449 | [ C(RESULT_MISS) ] = 0x024e, /* L1D_PREFETCH.MISS */ | 449 | [ C(RESULT_MISS) ] = 0x024e, /* L1D_PREFETCH.MISS */ |
450 | }, | 450 | }, |
451 | }, | 451 | }, |
452 | [ C(L1I ) ] = { | 452 | [ C(L1I ) ] = { |
453 | [ C(OP_READ) ] = { | 453 | [ C(OP_READ) ] = { |
454 | [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ | 454 | [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ |
455 | [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ | 455 | [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ |
456 | }, | 456 | }, |
457 | [ C(OP_WRITE) ] = { | 457 | [ C(OP_WRITE) ] = { |
458 | [ C(RESULT_ACCESS) ] = -1, | 458 | [ C(RESULT_ACCESS) ] = -1, |
459 | [ C(RESULT_MISS) ] = -1, | 459 | [ C(RESULT_MISS) ] = -1, |
460 | }, | 460 | }, |
461 | [ C(OP_PREFETCH) ] = { | 461 | [ C(OP_PREFETCH) ] = { |
462 | [ C(RESULT_ACCESS) ] = 0x0, | 462 | [ C(RESULT_ACCESS) ] = 0x0, |
463 | [ C(RESULT_MISS) ] = 0x0, | 463 | [ C(RESULT_MISS) ] = 0x0, |
464 | }, | 464 | }, |
465 | }, | 465 | }, |
466 | [ C(LL ) ] = { | 466 | [ C(LL ) ] = { |
467 | [ C(OP_READ) ] = { | 467 | [ C(OP_READ) ] = { |
468 | /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */ | 468 | /* OFFCORE_RESPONSE.ANY_DATA.LOCAL_CACHE */ |
469 | [ C(RESULT_ACCESS) ] = 0x01b7, | 469 | [ C(RESULT_ACCESS) ] = 0x01b7, |
470 | /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */ | 470 | /* OFFCORE_RESPONSE.ANY_DATA.ANY_LLC_MISS */ |
471 | [ C(RESULT_MISS) ] = 0x01b7, | 471 | [ C(RESULT_MISS) ] = 0x01b7, |
472 | }, | 472 | }, |
473 | /* | 473 | /* |
474 | * Use RFO, not WRITEBACK, because a write miss would typically occur | 474 | * Use RFO, not WRITEBACK, because a write miss would typically occur |
475 | * on RFO. | 475 | * on RFO. |
476 | */ | 476 | */ |
477 | [ C(OP_WRITE) ] = { | 477 | [ C(OP_WRITE) ] = { |
478 | /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */ | 478 | /* OFFCORE_RESPONSE.ANY_RFO.LOCAL_CACHE */ |
479 | [ C(RESULT_ACCESS) ] = 0x01b7, | 479 | [ C(RESULT_ACCESS) ] = 0x01b7, |
480 | /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */ | 480 | /* OFFCORE_RESPONSE.ANY_RFO.ANY_LLC_MISS */ |
481 | [ C(RESULT_MISS) ] = 0x01b7, | 481 | [ C(RESULT_MISS) ] = 0x01b7, |
482 | }, | 482 | }, |
483 | [ C(OP_PREFETCH) ] = { | 483 | [ C(OP_PREFETCH) ] = { |
484 | /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */ | 484 | /* OFFCORE_RESPONSE.PREFETCH.LOCAL_CACHE */ |
485 | [ C(RESULT_ACCESS) ] = 0x01b7, | 485 | [ C(RESULT_ACCESS) ] = 0x01b7, |
486 | /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */ | 486 | /* OFFCORE_RESPONSE.PREFETCH.ANY_LLC_MISS */ |
487 | [ C(RESULT_MISS) ] = 0x01b7, | 487 | [ C(RESULT_MISS) ] = 0x01b7, |
488 | }, | 488 | }, |
489 | }, | 489 | }, |
490 | [ C(DTLB) ] = { | 490 | [ C(DTLB) ] = { |
491 | [ C(OP_READ) ] = { | 491 | [ C(OP_READ) ] = { |
492 | [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ | 492 | [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ |
493 | [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.ANY */ | 493 | [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.ANY */ |
494 | }, | 494 | }, |
495 | [ C(OP_WRITE) ] = { | 495 | [ C(OP_WRITE) ] = { |
496 | [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ | 496 | [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ |
497 | [ C(RESULT_MISS) ] = 0x010c, /* MEM_STORE_RETIRED.DTLB_MISS */ | 497 | [ C(RESULT_MISS) ] = 0x010c, /* MEM_STORE_RETIRED.DTLB_MISS */ |
498 | }, | 498 | }, |
499 | [ C(OP_PREFETCH) ] = { | 499 | [ C(OP_PREFETCH) ] = { |
500 | [ C(RESULT_ACCESS) ] = 0x0, | 500 | [ C(RESULT_ACCESS) ] = 0x0, |
501 | [ C(RESULT_MISS) ] = 0x0, | 501 | [ C(RESULT_MISS) ] = 0x0, |
502 | }, | 502 | }, |
503 | }, | 503 | }, |
504 | [ C(ITLB) ] = { | 504 | [ C(ITLB) ] = { |
505 | [ C(OP_READ) ] = { | 505 | [ C(OP_READ) ] = { |
506 | [ C(RESULT_ACCESS) ] = 0x01c0, /* INST_RETIRED.ANY_P */ | 506 | [ C(RESULT_ACCESS) ] = 0x01c0, /* INST_RETIRED.ANY_P */ |
507 | [ C(RESULT_MISS) ] = 0x20c8, /* ITLB_MISS_RETIRED */ | 507 | [ C(RESULT_MISS) ] = 0x20c8, /* ITLB_MISS_RETIRED */ |
508 | }, | 508 | }, |
509 | [ C(OP_WRITE) ] = { | 509 | [ C(OP_WRITE) ] = { |
510 | [ C(RESULT_ACCESS) ] = -1, | 510 | [ C(RESULT_ACCESS) ] = -1, |
511 | [ C(RESULT_MISS) ] = -1, | 511 | [ C(RESULT_MISS) ] = -1, |
512 | }, | 512 | }, |
513 | [ C(OP_PREFETCH) ] = { | 513 | [ C(OP_PREFETCH) ] = { |
514 | [ C(RESULT_ACCESS) ] = -1, | 514 | [ C(RESULT_ACCESS) ] = -1, |
515 | [ C(RESULT_MISS) ] = -1, | 515 | [ C(RESULT_MISS) ] = -1, |
516 | }, | 516 | }, |
517 | }, | 517 | }, |
518 | [ C(BPU ) ] = { | 518 | [ C(BPU ) ] = { |
519 | [ C(OP_READ) ] = { | 519 | [ C(OP_READ) ] = { |
520 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ | 520 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ |
521 | [ C(RESULT_MISS) ] = 0x03e8, /* BPU_CLEARS.ANY */ | 521 | [ C(RESULT_MISS) ] = 0x03e8, /* BPU_CLEARS.ANY */ |
522 | }, | 522 | }, |
523 | [ C(OP_WRITE) ] = { | 523 | [ C(OP_WRITE) ] = { |
524 | [ C(RESULT_ACCESS) ] = -1, | 524 | [ C(RESULT_ACCESS) ] = -1, |
525 | [ C(RESULT_MISS) ] = -1, | 525 | [ C(RESULT_MISS) ] = -1, |
526 | }, | 526 | }, |
527 | [ C(OP_PREFETCH) ] = { | 527 | [ C(OP_PREFETCH) ] = { |
528 | [ C(RESULT_ACCESS) ] = -1, | 528 | [ C(RESULT_ACCESS) ] = -1, |
529 | [ C(RESULT_MISS) ] = -1, | 529 | [ C(RESULT_MISS) ] = -1, |
530 | }, | 530 | }, |
531 | }, | 531 | }, |
532 | [ C(NODE) ] = { | 532 | [ C(NODE) ] = { |
533 | [ C(OP_READ) ] = { | 533 | [ C(OP_READ) ] = { |
534 | [ C(RESULT_ACCESS) ] = 0x01b7, | 534 | [ C(RESULT_ACCESS) ] = 0x01b7, |
535 | [ C(RESULT_MISS) ] = 0x01b7, | 535 | [ C(RESULT_MISS) ] = 0x01b7, |
536 | }, | 536 | }, |
537 | [ C(OP_WRITE) ] = { | 537 | [ C(OP_WRITE) ] = { |
538 | [ C(RESULT_ACCESS) ] = 0x01b7, | 538 | [ C(RESULT_ACCESS) ] = 0x01b7, |
539 | [ C(RESULT_MISS) ] = 0x01b7, | 539 | [ C(RESULT_MISS) ] = 0x01b7, |
540 | }, | 540 | }, |
541 | [ C(OP_PREFETCH) ] = { | 541 | [ C(OP_PREFETCH) ] = { |
542 | [ C(RESULT_ACCESS) ] = 0x01b7, | 542 | [ C(RESULT_ACCESS) ] = 0x01b7, |
543 | [ C(RESULT_MISS) ] = 0x01b7, | 543 | [ C(RESULT_MISS) ] = 0x01b7, |
544 | }, | 544 | }, |
545 | }, | 545 | }, |
546 | }; | 546 | }; |
547 | 547 | ||
548 | static __initconst const u64 core2_hw_cache_event_ids | 548 | static __initconst const u64 core2_hw_cache_event_ids |
549 | [PERF_COUNT_HW_CACHE_MAX] | 549 | [PERF_COUNT_HW_CACHE_MAX] |
550 | [PERF_COUNT_HW_CACHE_OP_MAX] | 550 | [PERF_COUNT_HW_CACHE_OP_MAX] |
551 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | 551 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = |
552 | { | 552 | { |
553 | [ C(L1D) ] = { | 553 | [ C(L1D) ] = { |
554 | [ C(OP_READ) ] = { | 554 | [ C(OP_READ) ] = { |
555 | [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI */ | 555 | [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI */ |
556 | [ C(RESULT_MISS) ] = 0x0140, /* L1D_CACHE_LD.I_STATE */ | 556 | [ C(RESULT_MISS) ] = 0x0140, /* L1D_CACHE_LD.I_STATE */ |
557 | }, | 557 | }, |
558 | [ C(OP_WRITE) ] = { | 558 | [ C(OP_WRITE) ] = { |
559 | [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI */ | 559 | [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI */ |
560 | [ C(RESULT_MISS) ] = 0x0141, /* L1D_CACHE_ST.I_STATE */ | 560 | [ C(RESULT_MISS) ] = 0x0141, /* L1D_CACHE_ST.I_STATE */ |
561 | }, | 561 | }, |
562 | [ C(OP_PREFETCH) ] = { | 562 | [ C(OP_PREFETCH) ] = { |
563 | [ C(RESULT_ACCESS) ] = 0x104e, /* L1D_PREFETCH.REQUESTS */ | 563 | [ C(RESULT_ACCESS) ] = 0x104e, /* L1D_PREFETCH.REQUESTS */ |
564 | [ C(RESULT_MISS) ] = 0, | 564 | [ C(RESULT_MISS) ] = 0, |
565 | }, | 565 | }, |
566 | }, | 566 | }, |
567 | [ C(L1I ) ] = { | 567 | [ C(L1I ) ] = { |
568 | [ C(OP_READ) ] = { | 568 | [ C(OP_READ) ] = { |
569 | [ C(RESULT_ACCESS) ] = 0x0080, /* L1I.READS */ | 569 | [ C(RESULT_ACCESS) ] = 0x0080, /* L1I.READS */ |
570 | [ C(RESULT_MISS) ] = 0x0081, /* L1I.MISSES */ | 570 | [ C(RESULT_MISS) ] = 0x0081, /* L1I.MISSES */ |
571 | }, | 571 | }, |
572 | [ C(OP_WRITE) ] = { | 572 | [ C(OP_WRITE) ] = { |
573 | [ C(RESULT_ACCESS) ] = -1, | 573 | [ C(RESULT_ACCESS) ] = -1, |
574 | [ C(RESULT_MISS) ] = -1, | 574 | [ C(RESULT_MISS) ] = -1, |
575 | }, | 575 | }, |
576 | [ C(OP_PREFETCH) ] = { | 576 | [ C(OP_PREFETCH) ] = { |
577 | [ C(RESULT_ACCESS) ] = 0, | 577 | [ C(RESULT_ACCESS) ] = 0, |
578 | [ C(RESULT_MISS) ] = 0, | 578 | [ C(RESULT_MISS) ] = 0, |
579 | }, | 579 | }, |
580 | }, | 580 | }, |
581 | [ C(LL ) ] = { | 581 | [ C(LL ) ] = { |
582 | [ C(OP_READ) ] = { | 582 | [ C(OP_READ) ] = { |
583 | [ C(RESULT_ACCESS) ] = 0x4f29, /* L2_LD.MESI */ | 583 | [ C(RESULT_ACCESS) ] = 0x4f29, /* L2_LD.MESI */ |
584 | [ C(RESULT_MISS) ] = 0x4129, /* L2_LD.ISTATE */ | 584 | [ C(RESULT_MISS) ] = 0x4129, /* L2_LD.ISTATE */ |
585 | }, | 585 | }, |
586 | [ C(OP_WRITE) ] = { | 586 | [ C(OP_WRITE) ] = { |
587 | [ C(RESULT_ACCESS) ] = 0x4f2A, /* L2_ST.MESI */ | 587 | [ C(RESULT_ACCESS) ] = 0x4f2A, /* L2_ST.MESI */ |
588 | [ C(RESULT_MISS) ] = 0x412A, /* L2_ST.ISTATE */ | 588 | [ C(RESULT_MISS) ] = 0x412A, /* L2_ST.ISTATE */ |
589 | }, | 589 | }, |
590 | [ C(OP_PREFETCH) ] = { | 590 | [ C(OP_PREFETCH) ] = { |
591 | [ C(RESULT_ACCESS) ] = 0, | 591 | [ C(RESULT_ACCESS) ] = 0, |
592 | [ C(RESULT_MISS) ] = 0, | 592 | [ C(RESULT_MISS) ] = 0, |
593 | }, | 593 | }, |
594 | }, | 594 | }, |
595 | [ C(DTLB) ] = { | 595 | [ C(DTLB) ] = { |
596 | [ C(OP_READ) ] = { | 596 | [ C(OP_READ) ] = { |
597 | [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ | 597 | [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ |
598 | [ C(RESULT_MISS) ] = 0x0208, /* DTLB_MISSES.MISS_LD */ | 598 | [ C(RESULT_MISS) ] = 0x0208, /* DTLB_MISSES.MISS_LD */ |
599 | }, | 599 | }, |
600 | [ C(OP_WRITE) ] = { | 600 | [ C(OP_WRITE) ] = { |
601 | [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ | 601 | [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ |
602 | [ C(RESULT_MISS) ] = 0x0808, /* DTLB_MISSES.MISS_ST */ | 602 | [ C(RESULT_MISS) ] = 0x0808, /* DTLB_MISSES.MISS_ST */ |
603 | }, | 603 | }, |
604 | [ C(OP_PREFETCH) ] = { | 604 | [ C(OP_PREFETCH) ] = { |
605 | [ C(RESULT_ACCESS) ] = 0, | 605 | [ C(RESULT_ACCESS) ] = 0, |
606 | [ C(RESULT_MISS) ] = 0, | 606 | [ C(RESULT_MISS) ] = 0, |
607 | }, | 607 | }, |
608 | }, | 608 | }, |
609 | [ C(ITLB) ] = { | 609 | [ C(ITLB) ] = { |
610 | [ C(OP_READ) ] = { | 610 | [ C(OP_READ) ] = { |
611 | [ C(RESULT_ACCESS) ] = 0x00c0, /* INST_RETIRED.ANY_P */ | 611 | [ C(RESULT_ACCESS) ] = 0x00c0, /* INST_RETIRED.ANY_P */ |
612 | [ C(RESULT_MISS) ] = 0x1282, /* ITLBMISSES */ | 612 | [ C(RESULT_MISS) ] = 0x1282, /* ITLBMISSES */ |
613 | }, | 613 | }, |
614 | [ C(OP_WRITE) ] = { | 614 | [ C(OP_WRITE) ] = { |
615 | [ C(RESULT_ACCESS) ] = -1, | 615 | [ C(RESULT_ACCESS) ] = -1, |
616 | [ C(RESULT_MISS) ] = -1, | 616 | [ C(RESULT_MISS) ] = -1, |
617 | }, | 617 | }, |
618 | [ C(OP_PREFETCH) ] = { | 618 | [ C(OP_PREFETCH) ] = { |
619 | [ C(RESULT_ACCESS) ] = -1, | 619 | [ C(RESULT_ACCESS) ] = -1, |
620 | [ C(RESULT_MISS) ] = -1, | 620 | [ C(RESULT_MISS) ] = -1, |
621 | }, | 621 | }, |
622 | }, | 622 | }, |
623 | [ C(BPU ) ] = { | 623 | [ C(BPU ) ] = { |
624 | [ C(OP_READ) ] = { | 624 | [ C(OP_READ) ] = { |
625 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ANY */ | 625 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ANY */ |
626 | [ C(RESULT_MISS) ] = 0x00c5, /* BP_INST_RETIRED.MISPRED */ | 626 | [ C(RESULT_MISS) ] = 0x00c5, /* BP_INST_RETIRED.MISPRED */ |
627 | }, | 627 | }, |
628 | [ C(OP_WRITE) ] = { | 628 | [ C(OP_WRITE) ] = { |
629 | [ C(RESULT_ACCESS) ] = -1, | 629 | [ C(RESULT_ACCESS) ] = -1, |
630 | [ C(RESULT_MISS) ] = -1, | 630 | [ C(RESULT_MISS) ] = -1, |
631 | }, | 631 | }, |
632 | [ C(OP_PREFETCH) ] = { | 632 | [ C(OP_PREFETCH) ] = { |
633 | [ C(RESULT_ACCESS) ] = -1, | 633 | [ C(RESULT_ACCESS) ] = -1, |
634 | [ C(RESULT_MISS) ] = -1, | 634 | [ C(RESULT_MISS) ] = -1, |
635 | }, | 635 | }, |
636 | }, | 636 | }, |
637 | }; | 637 | }; |
638 | 638 | ||
639 | static __initconst const u64 atom_hw_cache_event_ids | 639 | static __initconst const u64 atom_hw_cache_event_ids |
640 | [PERF_COUNT_HW_CACHE_MAX] | 640 | [PERF_COUNT_HW_CACHE_MAX] |
641 | [PERF_COUNT_HW_CACHE_OP_MAX] | 641 | [PERF_COUNT_HW_CACHE_OP_MAX] |
642 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = | 642 | [PERF_COUNT_HW_CACHE_RESULT_MAX] = |
643 | { | 643 | { |
644 | [ C(L1D) ] = { | 644 | [ C(L1D) ] = { |
645 | [ C(OP_READ) ] = { | 645 | [ C(OP_READ) ] = { |
646 | [ C(RESULT_ACCESS) ] = 0x2140, /* L1D_CACHE.LD */ | 646 | [ C(RESULT_ACCESS) ] = 0x2140, /* L1D_CACHE.LD */ |
647 | [ C(RESULT_MISS) ] = 0, | 647 | [ C(RESULT_MISS) ] = 0, |
648 | }, | 648 | }, |
649 | [ C(OP_WRITE) ] = { | 649 | [ C(OP_WRITE) ] = { |
650 | [ C(RESULT_ACCESS) ] = 0x2240, /* L1D_CACHE.ST */ | 650 | [ C(RESULT_ACCESS) ] = 0x2240, /* L1D_CACHE.ST */ |
651 | [ C(RESULT_MISS) ] = 0, | 651 | [ C(RESULT_MISS) ] = 0, |
652 | }, | 652 | }, |
653 | [ C(OP_PREFETCH) ] = { | 653 | [ C(OP_PREFETCH) ] = { |
654 | [ C(RESULT_ACCESS) ] = 0x0, | 654 | [ C(RESULT_ACCESS) ] = 0x0, |
655 | [ C(RESULT_MISS) ] = 0, | 655 | [ C(RESULT_MISS) ] = 0, |
656 | }, | 656 | }, |
657 | }, | 657 | }, |
658 | [ C(L1I ) ] = { | 658 | [ C(L1I ) ] = { |
659 | [ C(OP_READ) ] = { | 659 | [ C(OP_READ) ] = { |
660 | [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ | 660 | [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ |
661 | [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ | 661 | [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ |
662 | }, | 662 | }, |
663 | [ C(OP_WRITE) ] = { | 663 | [ C(OP_WRITE) ] = { |
664 | [ C(RESULT_ACCESS) ] = -1, | 664 | [ C(RESULT_ACCESS) ] = -1, |
665 | [ C(RESULT_MISS) ] = -1, | 665 | [ C(RESULT_MISS) ] = -1, |
666 | }, | 666 | }, |
667 | [ C(OP_PREFETCH) ] = { | 667 | [ C(OP_PREFETCH) ] = { |
668 | [ C(RESULT_ACCESS) ] = 0, | 668 | [ C(RESULT_ACCESS) ] = 0, |
669 | [ C(RESULT_MISS) ] = 0, | 669 | [ C(RESULT_MISS) ] = 0, |
670 | }, | 670 | }, |
671 | }, | 671 | }, |
672 | [ C(LL ) ] = { | 672 | [ C(LL ) ] = { |
673 | [ C(OP_READ) ] = { | 673 | [ C(OP_READ) ] = { |
674 | [ C(RESULT_ACCESS) ] = 0x4f29, /* L2_LD.MESI */ | 674 | [ C(RESULT_ACCESS) ] = 0x4f29, /* L2_LD.MESI */ |
675 | [ C(RESULT_MISS) ] = 0x4129, /* L2_LD.ISTATE */ | 675 | [ C(RESULT_MISS) ] = 0x4129, /* L2_LD.ISTATE */ |
676 | }, | 676 | }, |
677 | [ C(OP_WRITE) ] = { | 677 | [ C(OP_WRITE) ] = { |
678 | [ C(RESULT_ACCESS) ] = 0x4f2A, /* L2_ST.MESI */ | 678 | [ C(RESULT_ACCESS) ] = 0x4f2A, /* L2_ST.MESI */ |
679 | [ C(RESULT_MISS) ] = 0x412A, /* L2_ST.ISTATE */ | 679 | [ C(RESULT_MISS) ] = 0x412A, /* L2_ST.ISTATE */ |
680 | }, | 680 | }, |
681 | [ C(OP_PREFETCH) ] = { | 681 | [ C(OP_PREFETCH) ] = { |
682 | [ C(RESULT_ACCESS) ] = 0, | 682 | [ C(RESULT_ACCESS) ] = 0, |
683 | [ C(RESULT_MISS) ] = 0, | 683 | [ C(RESULT_MISS) ] = 0, |
684 | }, | 684 | }, |
685 | }, | 685 | }, |
686 | [ C(DTLB) ] = { | 686 | [ C(DTLB) ] = { |
687 | [ C(OP_READ) ] = { | 687 | [ C(OP_READ) ] = { |
688 | [ C(RESULT_ACCESS) ] = 0x2140, /* L1D_CACHE_LD.MESI (alias) */ | 688 | [ C(RESULT_ACCESS) ] = 0x2140, /* L1D_CACHE_LD.MESI (alias) */ |
689 | [ C(RESULT_MISS) ] = 0x0508, /* DTLB_MISSES.MISS_LD */ | 689 | [ C(RESULT_MISS) ] = 0x0508, /* DTLB_MISSES.MISS_LD */ |
690 | }, | 690 | }, |
691 | [ C(OP_WRITE) ] = { | 691 | [ C(OP_WRITE) ] = { |
692 | [ C(RESULT_ACCESS) ] = 0x2240, /* L1D_CACHE_ST.MESI (alias) */ | 692 | [ C(RESULT_ACCESS) ] = 0x2240, /* L1D_CACHE_ST.MESI (alias) */ |
693 | [ C(RESULT_MISS) ] = 0x0608, /* DTLB_MISSES.MISS_ST */ | 693 | [ C(RESULT_MISS) ] = 0x0608, /* DTLB_MISSES.MISS_ST */ |
694 | }, | 694 | }, |
695 | [ C(OP_PREFETCH) ] = { | 695 | [ C(OP_PREFETCH) ] = { |
696 | [ C(RESULT_ACCESS) ] = 0, | 696 | [ C(RESULT_ACCESS) ] = 0, |
697 | [ C(RESULT_MISS) ] = 0, | 697 | [ C(RESULT_MISS) ] = 0, |
698 | }, | 698 | }, |
699 | }, | 699 | }, |
700 | [ C(ITLB) ] = { | 700 | [ C(ITLB) ] = { |
701 | [ C(OP_READ) ] = { | 701 | [ C(OP_READ) ] = { |
702 | [ C(RESULT_ACCESS) ] = 0x00c0, /* INST_RETIRED.ANY_P */ | 702 | [ C(RESULT_ACCESS) ] = 0x00c0, /* INST_RETIRED.ANY_P */ |
703 | [ C(RESULT_MISS) ] = 0x0282, /* ITLB.MISSES */ | 703 | [ C(RESULT_MISS) ] = 0x0282, /* ITLB.MISSES */ |
704 | }, | 704 | }, |
705 | [ C(OP_WRITE) ] = { | 705 | [ C(OP_WRITE) ] = { |
706 | [ C(RESULT_ACCESS) ] = -1, | 706 | [ C(RESULT_ACCESS) ] = -1, |
707 | [ C(RESULT_MISS) ] = -1, | 707 | [ C(RESULT_MISS) ] = -1, |
708 | }, | 708 | }, |
709 | [ C(OP_PREFETCH) ] = { | 709 | [ C(OP_PREFETCH) ] = { |
710 | [ C(RESULT_ACCESS) ] = -1, | 710 | [ C(RESULT_ACCESS) ] = -1, |
711 | [ C(RESULT_MISS) ] = -1, | 711 | [ C(RESULT_MISS) ] = -1, |
712 | }, | 712 | }, |
713 | }, | 713 | }, |
714 | [ C(BPU ) ] = { | 714 | [ C(BPU ) ] = { |
715 | [ C(OP_READ) ] = { | 715 | [ C(OP_READ) ] = { |
716 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ANY */ | 716 | [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ANY */ |
717 | [ C(RESULT_MISS) ] = 0x00c5, /* BP_INST_RETIRED.MISPRED */ | 717 | [ C(RESULT_MISS) ] = 0x00c5, /* BP_INST_RETIRED.MISPRED */ |
718 | }, | 718 | }, |
719 | [ C(OP_WRITE) ] = { | 719 | [ C(OP_WRITE) ] = { |
720 | [ C(RESULT_ACCESS) ] = -1, | 720 | [ C(RESULT_ACCESS) ] = -1, |
721 | [ C(RESULT_MISS) ] = -1, | 721 | [ C(RESULT_MISS) ] = -1, |
722 | }, | 722 | }, |
723 | [ C(OP_PREFETCH) ] = { | 723 | [ C(OP_PREFETCH) ] = { |
724 | [ C(RESULT_ACCESS) ] = -1, | 724 | [ C(RESULT_ACCESS) ] = -1, |
725 | [ C(RESULT_MISS) ] = -1, | 725 | [ C(RESULT_MISS) ] = -1, |
726 | }, | 726 | }, |
727 | }, | 727 | }, |
728 | }; | 728 | }; |
729 | 729 | ||
730 | static void intel_pmu_disable_all(void) | 730 | static void intel_pmu_disable_all(void) |
731 | { | 731 | { |
732 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 732 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
733 | 733 | ||
734 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); | 734 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); |
735 | 735 | ||
736 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) | 736 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) |
737 | intel_pmu_disable_bts(); | 737 | intel_pmu_disable_bts(); |
738 | 738 | ||
739 | intel_pmu_pebs_disable_all(); | 739 | intel_pmu_pebs_disable_all(); |
740 | intel_pmu_lbr_disable_all(); | 740 | intel_pmu_lbr_disable_all(); |
741 | } | 741 | } |
742 | 742 | ||
743 | static void intel_pmu_enable_all(int added) | 743 | static void intel_pmu_enable_all(int added) |
744 | { | 744 | { |
745 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 745 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
746 | 746 | ||
747 | intel_pmu_pebs_enable_all(); | 747 | intel_pmu_pebs_enable_all(); |
748 | intel_pmu_lbr_enable_all(); | 748 | intel_pmu_lbr_enable_all(); |
749 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); | 749 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); |
750 | 750 | ||
751 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { | 751 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { |
752 | struct perf_event *event = | 752 | struct perf_event *event = |
753 | cpuc->events[X86_PMC_IDX_FIXED_BTS]; | 753 | cpuc->events[X86_PMC_IDX_FIXED_BTS]; |
754 | 754 | ||
755 | if (WARN_ON_ONCE(!event)) | 755 | if (WARN_ON_ONCE(!event)) |
756 | return; | 756 | return; |
757 | 757 | ||
758 | intel_pmu_enable_bts(event->hw.config); | 758 | intel_pmu_enable_bts(event->hw.config); |
759 | } | 759 | } |
760 | } | 760 | } |
761 | 761 | ||
762 | /* | 762 | /* |
763 | * Workaround for: | 763 | * Workaround for: |
764 | * Intel Errata AAK100 (model 26) | 764 | * Intel Errata AAK100 (model 26) |
765 | * Intel Errata AAP53 (model 30) | 765 | * Intel Errata AAP53 (model 30) |
766 | * Intel Errata BD53 (model 44) | 766 | * Intel Errata BD53 (model 44) |
767 | * | 767 | * |
768 | * The official story: | 768 | * The official story: |
769 | * These chips need to be 'reset' when adding counters by programming the | 769 | * These chips need to be 'reset' when adding counters by programming the |
770 | * magic three (non-counting) events 0x4300B5, 0x4300D2, and 0x4300B1 either | 770 | * magic three (non-counting) events 0x4300B5, 0x4300D2, and 0x4300B1 either |
771 | * in sequence on the same PMC or on different PMCs. | 771 | * in sequence on the same PMC or on different PMCs. |
772 | * | 772 | * |
773 | * In practise it appears some of these events do in fact count, and | 773 | * In practise it appears some of these events do in fact count, and |
774 | * we need to programm all 4 events. | 774 | * we need to programm all 4 events. |
775 | */ | 775 | */ |
776 | static void intel_pmu_nhm_workaround(void) | 776 | static void intel_pmu_nhm_workaround(void) |
777 | { | 777 | { |
778 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); | 778 | struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); |
779 | static const unsigned long nhm_magic[4] = { | 779 | static const unsigned long nhm_magic[4] = { |
780 | 0x4300B5, | 780 | 0x4300B5, |
781 | 0x4300D2, | 781 | 0x4300D2, |
782 | 0x4300B1, | 782 | 0x4300B1, |
783 | 0x4300B1 | 783 | 0x4300B1 |
784 | }; | 784 | }; |
785 | struct perf_event *event; | 785 | struct perf_event *event; |
786 | int i; | 786 | int i; |
787 | 787 | ||
788 | /* | 788 | /* |
789 | * The Errata requires below steps: | 789 | * The Errata requires below steps: |
790 | * 1) Clear MSR_IA32_PEBS_ENABLE and MSR_CORE_PERF_GLOBAL_CTRL; | 790 | * 1) Clear MSR_IA32_PEBS_ENABLE and MSR_CORE_PERF_GLOBAL_CTRL; |
791 | * 2) Configure 4 PERFEVTSELx with the magic events and clear | 791 | * 2) Configure 4 PERFEVTSELx with the magic events and clear |
792 | * the corresponding PMCx; | 792 | * the corresponding PMCx; |
793 | * 3) set bit0~bit3 of MSR_CORE_PERF_GLOBAL_CTRL; | 793 | * 3) set bit0~bit3 of MSR_CORE_PERF_GLOBAL_CTRL; |
794 | * 4) Clear MSR_CORE_PERF_GLOBAL_CTRL; | 794 | * 4) Clear MSR_CORE_PERF_GLOBAL_CTRL; |
795 | * 5) Clear 4 pairs of ERFEVTSELx and PMCx; | 795 | * 5) Clear 4 pairs of ERFEVTSELx and PMCx; |
796 | */ | 796 | */ |
797 | 797 | ||
798 | /* | 798 | /* |
799 | * The real steps we choose are a little different from above. | 799 | * The real steps we choose are a little different from above. |
800 | * A) To reduce MSR operations, we don't run step 1) as they | 800 | * A) To reduce MSR operations, we don't run step 1) as they |
801 | * are already cleared before this function is called; | 801 | * are already cleared before this function is called; |
802 | * B) Call x86_perf_event_update to save PMCx before configuring | 802 | * B) Call x86_perf_event_update to save PMCx before configuring |
803 | * PERFEVTSELx with magic number; | 803 | * PERFEVTSELx with magic number; |
804 | * C) With step 5), we do clear only when the PERFEVTSELx is | 804 | * C) With step 5), we do clear only when the PERFEVTSELx is |
805 | * not used currently. | 805 | * not used currently. |
806 | * D) Call x86_perf_event_set_period to restore PMCx; | 806 | * D) Call x86_perf_event_set_period to restore PMCx; |
807 | */ | 807 | */ |
808 | 808 | ||
809 | /* We always operate 4 pairs of PERF Counters */ | 809 | /* We always operate 4 pairs of PERF Counters */ |
810 | for (i = 0; i < 4; i++) { | 810 | for (i = 0; i < 4; i++) { |
811 | event = cpuc->events[i]; | 811 | event = cpuc->events[i]; |
812 | if (event) | 812 | if (event) |
813 | x86_perf_event_update(event); | 813 | x86_perf_event_update(event); |
814 | } | 814 | } |
815 | 815 | ||
816 | for (i = 0; i < 4; i++) { | 816 | for (i = 0; i < 4; i++) { |
817 | wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + i, nhm_magic[i]); | 817 | wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + i, nhm_magic[i]); |
818 | wrmsrl(MSR_ARCH_PERFMON_PERFCTR0 + i, 0x0); | 818 | wrmsrl(MSR_ARCH_PERFMON_PERFCTR0 + i, 0x0); |
819 | } | 819 | } |
820 | 820 | ||
821 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0xf); | 821 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0xf); |
822 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x0); | 822 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0x0); |
823 | 823 | ||
824 | for (i = 0; i < 4; i++) { | 824 | for (i = 0; i < 4; i++) { |
825 | event = cpuc->events[i]; | 825 | event = cpuc->events[i]; |
826 | 826 | ||
827 | if (event) { | 827 | if (event) { |
828 | x86_perf_event_set_period(event); | 828 | x86_perf_event_set_period(event); |
829 | __x86_pmu_enable_event(&event->hw, | 829 | __x86_pmu_enable_event(&event->hw, |
830 | ARCH_PERFMON_EVENTSEL_ENABLE); | 830 | ARCH_PERFMON_EVENTSEL_ENABLE); |
831 | } else | 831 | } else |
832 | wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + i, 0x0); | 832 | wrmsrl(MSR_ARCH_PERFMON_EVENTSEL0 + i, 0x0); |
833 | } | 833 | } |
834 | } | 834 | } |
835 | 835 | ||
836 | static void intel_pmu_nhm_enable_all(int added) | 836 | static void intel_pmu_nhm_enable_all(int added) |
837 | { | 837 | { |
838 | if (added) | 838 | if (added) |
839 | intel_pmu_nhm_workaround(); | 839 | intel_pmu_nhm_workaround(); |
840 | intel_pmu_enable_all(added); | 840 | intel_pmu_enable_all(added); |
841 | } | 841 | } |
842 | 842 | ||
843 | static inline u64 intel_pmu_get_status(void) | 843 | static inline u64 intel_pmu_get_status(void) |
844 | { | 844 | { |
845 | u64 status; | 845 | u64 status; |
846 | 846 | ||
847 | rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); | 847 | rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); |
848 | 848 | ||
849 | return status; | 849 | return status; |
850 | } | 850 | } |
851 | 851 | ||
852 | static inline void intel_pmu_ack_status(u64 ack) | 852 | static inline void intel_pmu_ack_status(u64 ack) |
853 | { | 853 | { |
854 | wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); | 854 | wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); |
855 | } | 855 | } |
856 | 856 | ||
857 | static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) | 857 | static void intel_pmu_disable_fixed(struct hw_perf_event *hwc) |
858 | { | 858 | { |
859 | int idx = hwc->idx - X86_PMC_IDX_FIXED; | 859 | int idx = hwc->idx - X86_PMC_IDX_FIXED; |
860 | u64 ctrl_val, mask; | 860 | u64 ctrl_val, mask; |
861 | 861 | ||
862 | mask = 0xfULL << (idx * 4); | 862 | mask = 0xfULL << (idx * 4); |
863 | 863 | ||
864 | rdmsrl(hwc->config_base, ctrl_val); | 864 | rdmsrl(hwc->config_base, ctrl_val); |
865 | ctrl_val &= ~mask; | 865 | ctrl_val &= ~mask; |
866 | wrmsrl(hwc->config_base, ctrl_val); | 866 | wrmsrl(hwc->config_base, ctrl_val); |
867 | } | 867 | } |
868 | 868 | ||
869 | static void intel_pmu_disable_event(struct perf_event *event) | 869 | static void intel_pmu_disable_event(struct perf_event *event) |
870 | { | 870 | { |
871 | struct hw_perf_event *hwc = &event->hw; | 871 | struct hw_perf_event *hwc = &event->hw; |
872 | 872 | ||
873 | if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { | 873 | if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { |
874 | intel_pmu_disable_bts(); | 874 | intel_pmu_disable_bts(); |
875 | intel_pmu_drain_bts_buffer(); | 875 | intel_pmu_drain_bts_buffer(); |
876 | return; | 876 | return; |
877 | } | 877 | } |
878 | 878 | ||
879 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { | 879 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { |
880 | intel_pmu_disable_fixed(hwc); | 880 | intel_pmu_disable_fixed(hwc); |
881 | return; | 881 | return; |
882 | } | 882 | } |
883 | 883 | ||
884 | x86_pmu_disable_event(event); | 884 | x86_pmu_disable_event(event); |
885 | 885 | ||
886 | if (unlikely(event->attr.precise_ip)) | 886 | if (unlikely(event->attr.precise_ip)) |
887 | intel_pmu_pebs_disable(event); | 887 | intel_pmu_pebs_disable(event); |
888 | } | 888 | } |
889 | 889 | ||
890 | static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) | 890 | static void intel_pmu_enable_fixed(struct hw_perf_event *hwc) |
891 | { | 891 | { |
892 | int idx = hwc->idx - X86_PMC_IDX_FIXED; | 892 | int idx = hwc->idx - X86_PMC_IDX_FIXED; |
893 | u64 ctrl_val, bits, mask; | 893 | u64 ctrl_val, bits, mask; |
894 | 894 | ||
895 | /* | 895 | /* |
896 | * Enable IRQ generation (0x8), | 896 | * Enable IRQ generation (0x8), |
897 | * and enable ring-3 counting (0x2) and ring-0 counting (0x1) | 897 | * and enable ring-3 counting (0x2) and ring-0 counting (0x1) |
898 | * if requested: | 898 | * if requested: |
899 | */ | 899 | */ |
900 | bits = 0x8ULL; | 900 | bits = 0x8ULL; |
901 | if (hwc->config & ARCH_PERFMON_EVENTSEL_USR) | 901 | if (hwc->config & ARCH_PERFMON_EVENTSEL_USR) |
902 | bits |= 0x2; | 902 | bits |= 0x2; |
903 | if (hwc->config & ARCH_PERFMON_EVENTSEL_OS) | 903 | if (hwc->config & ARCH_PERFMON_EVENTSEL_OS) |
904 | bits |= 0x1; | 904 | bits |= 0x1; |
905 | 905 | ||
906 | /* | 906 | /* |
907 | * ANY bit is supported in v3 and up | 907 | * ANY bit is supported in v3 and up |
908 | */ | 908 | */ |
909 | if (x86_pmu.version > 2 && hwc->config & ARCH_PERFMON_EVENTSEL_ANY) | 909 | if (x86_pmu.version > 2 && hwc->config & ARCH_PERFMON_EVENTSEL_ANY) |
910 | bits |= 0x4; | 910 | bits |= 0x4; |
911 | 911 | ||
912 | bits <<= (idx * 4); | 912 | bits <<= (idx * 4); |
913 | mask = 0xfULL << (idx * 4); | 913 | mask = 0xfULL << (idx * 4); |
914 | 914 | ||
915 | rdmsrl(hwc->config_base, ctrl_val); | 915 | rdmsrl(hwc->config_base, ctrl_val); |
916 | ctrl_val &= ~mask; | 916 | ctrl_val &= ~mask; |
917 | ctrl_val |= bits; | 917 | ctrl_val |= bits; |
918 | wrmsrl(hwc->config_base, ctrl_val); | 918 | wrmsrl(hwc->config_base, ctrl_val); |
919 | } | 919 | } |
920 | 920 | ||
921 | static void intel_pmu_enable_event(struct perf_event *event) | 921 | static void intel_pmu_enable_event(struct perf_event *event) |
922 | { | 922 | { |
923 | struct hw_perf_event *hwc = &event->hw; | 923 | struct hw_perf_event *hwc = &event->hw; |
924 | 924 | ||
925 | if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { | 925 | if (unlikely(hwc->idx == X86_PMC_IDX_FIXED_BTS)) { |
926 | if (!__this_cpu_read(cpu_hw_events.enabled)) | 926 | if (!__this_cpu_read(cpu_hw_events.enabled)) |
927 | return; | 927 | return; |
928 | 928 | ||
929 | intel_pmu_enable_bts(hwc->config); | 929 | intel_pmu_enable_bts(hwc->config); |
930 | return; | 930 | return; |
931 | } | 931 | } |
932 | 932 | ||
933 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { | 933 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { |
934 | intel_pmu_enable_fixed(hwc); | 934 | intel_pmu_enable_fixed(hwc); |
935 | return; | 935 | return; |
936 | } | 936 | } |
937 | 937 | ||
938 | if (unlikely(event->attr.precise_ip)) | 938 | if (unlikely(event->attr.precise_ip)) |
939 | intel_pmu_pebs_enable(event); | 939 | intel_pmu_pebs_enable(event); |
940 | 940 | ||
941 | __x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE); | 941 | __x86_pmu_enable_event(hwc, ARCH_PERFMON_EVENTSEL_ENABLE); |
942 | } | 942 | } |
943 | 943 | ||
944 | /* | 944 | /* |
945 | * Save and restart an expired event. Called by NMI contexts, | 945 | * Save and restart an expired event. Called by NMI contexts, |
946 | * so it has to be careful about preempting normal event ops: | 946 | * so it has to be careful about preempting normal event ops: |
947 | */ | 947 | */ |
948 | static int intel_pmu_save_and_restart(struct perf_event *event) | 948 | static int intel_pmu_save_and_restart(struct perf_event *event) |
949 | { | 949 | { |
950 | x86_perf_event_update(event); | 950 | x86_perf_event_update(event); |
951 | return x86_perf_event_set_period(event); | 951 | return x86_perf_event_set_period(event); |
952 | } | 952 | } |
953 | 953 | ||
954 | static void intel_pmu_reset(void) | 954 | static void intel_pmu_reset(void) |
955 | { | 955 | { |
956 | struct debug_store *ds = __this_cpu_read(cpu_hw_events.ds); | 956 | struct debug_store *ds = __this_cpu_read(cpu_hw_events.ds); |
957 | unsigned long flags; | 957 | unsigned long flags; |
958 | int idx; | 958 | int idx; |
959 | 959 | ||
960 | if (!x86_pmu.num_counters) | 960 | if (!x86_pmu.num_counters) |
961 | return; | 961 | return; |
962 | 962 | ||
963 | local_irq_save(flags); | 963 | local_irq_save(flags); |
964 | 964 | ||
965 | printk("clearing PMU state on CPU#%d\n", smp_processor_id()); | 965 | printk("clearing PMU state on CPU#%d\n", smp_processor_id()); |
966 | 966 | ||
967 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { | 967 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { |
968 | checking_wrmsrl(x86_pmu_config_addr(idx), 0ull); | 968 | checking_wrmsrl(x86_pmu_config_addr(idx), 0ull); |
969 | checking_wrmsrl(x86_pmu_event_addr(idx), 0ull); | 969 | checking_wrmsrl(x86_pmu_event_addr(idx), 0ull); |
970 | } | 970 | } |
971 | for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) | 971 | for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) |
972 | checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); | 972 | checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); |
973 | 973 | ||
974 | if (ds) | 974 | if (ds) |
975 | ds->bts_index = ds->bts_buffer_base; | 975 | ds->bts_index = ds->bts_buffer_base; |
976 | 976 | ||
977 | local_irq_restore(flags); | 977 | local_irq_restore(flags); |
978 | } | 978 | } |
979 | 979 | ||
980 | /* | 980 | /* |
981 | * This handler is triggered by the local APIC, so the APIC IRQ handling | 981 | * This handler is triggered by the local APIC, so the APIC IRQ handling |
982 | * rules apply: | 982 | * rules apply: |
983 | */ | 983 | */ |
984 | static int intel_pmu_handle_irq(struct pt_regs *regs) | 984 | static int intel_pmu_handle_irq(struct pt_regs *regs) |
985 | { | 985 | { |
986 | struct perf_sample_data data; | 986 | struct perf_sample_data data; |
987 | struct cpu_hw_events *cpuc; | 987 | struct cpu_hw_events *cpuc; |
988 | int bit, loops; | 988 | int bit, loops; |
989 | u64 status; | 989 | u64 status; |
990 | int handled; | 990 | int handled; |
991 | 991 | ||
992 | perf_sample_data_init(&data, 0); | 992 | perf_sample_data_init(&data, 0); |
993 | 993 | ||
994 | cpuc = &__get_cpu_var(cpu_hw_events); | 994 | cpuc = &__get_cpu_var(cpu_hw_events); |
995 | 995 | ||
996 | /* | 996 | /* |
997 | * Some chipsets need to unmask the LVTPC in a particular spot | 997 | * Some chipsets need to unmask the LVTPC in a particular spot |
998 | * inside the nmi handler. As a result, the unmasking was pushed | 998 | * inside the nmi handler. As a result, the unmasking was pushed |
999 | * into all the nmi handlers. | 999 | * into all the nmi handlers. |
1000 | * | 1000 | * |
1001 | * This handler doesn't seem to have any issues with the unmasking | 1001 | * This handler doesn't seem to have any issues with the unmasking |
1002 | * so it was left at the top. | 1002 | * so it was left at the top. |
1003 | */ | 1003 | */ |
1004 | apic_write(APIC_LVTPC, APIC_DM_NMI); | 1004 | apic_write(APIC_LVTPC, APIC_DM_NMI); |
1005 | 1005 | ||
1006 | intel_pmu_disable_all(); | 1006 | intel_pmu_disable_all(); |
1007 | handled = intel_pmu_drain_bts_buffer(); | 1007 | handled = intel_pmu_drain_bts_buffer(); |
1008 | status = intel_pmu_get_status(); | 1008 | status = intel_pmu_get_status(); |
1009 | if (!status) { | 1009 | if (!status) { |
1010 | intel_pmu_enable_all(0); | 1010 | intel_pmu_enable_all(0); |
1011 | return handled; | 1011 | return handled; |
1012 | } | 1012 | } |
1013 | 1013 | ||
1014 | loops = 0; | 1014 | loops = 0; |
1015 | again: | 1015 | again: |
1016 | intel_pmu_ack_status(status); | 1016 | intel_pmu_ack_status(status); |
1017 | if (++loops > 100) { | 1017 | if (++loops > 100) { |
1018 | WARN_ONCE(1, "perfevents: irq loop stuck!\n"); | 1018 | WARN_ONCE(1, "perfevents: irq loop stuck!\n"); |
1019 | perf_event_print_debug(); | 1019 | perf_event_print_debug(); |
1020 | intel_pmu_reset(); | 1020 | intel_pmu_reset(); |
1021 | goto done; | 1021 | goto done; |
1022 | } | 1022 | } |
1023 | 1023 | ||
1024 | inc_irq_stat(apic_perf_irqs); | 1024 | inc_irq_stat(apic_perf_irqs); |
1025 | 1025 | ||
1026 | intel_pmu_lbr_read(); | 1026 | intel_pmu_lbr_read(); |
1027 | 1027 | ||
1028 | /* | 1028 | /* |
1029 | * PEBS overflow sets bit 62 in the global status register | 1029 | * PEBS overflow sets bit 62 in the global status register |
1030 | */ | 1030 | */ |
1031 | if (__test_and_clear_bit(62, (unsigned long *)&status)) { | 1031 | if (__test_and_clear_bit(62, (unsigned long *)&status)) { |
1032 | handled++; | 1032 | handled++; |
1033 | x86_pmu.drain_pebs(regs); | 1033 | x86_pmu.drain_pebs(regs); |
1034 | } | 1034 | } |
1035 | 1035 | ||
1036 | for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { | 1036 | for_each_set_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { |
1037 | struct perf_event *event = cpuc->events[bit]; | 1037 | struct perf_event *event = cpuc->events[bit]; |
1038 | 1038 | ||
1039 | handled++; | 1039 | handled++; |
1040 | 1040 | ||
1041 | if (!test_bit(bit, cpuc->active_mask)) | 1041 | if (!test_bit(bit, cpuc->active_mask)) |
1042 | continue; | 1042 | continue; |
1043 | 1043 | ||
1044 | if (!intel_pmu_save_and_restart(event)) | 1044 | if (!intel_pmu_save_and_restart(event)) |
1045 | continue; | 1045 | continue; |
1046 | 1046 | ||
1047 | data.period = event->hw.last_period; | 1047 | data.period = event->hw.last_period; |
1048 | 1048 | ||
1049 | if (perf_event_overflow(event, &data, regs)) | 1049 | if (perf_event_overflow(event, &data, regs)) |
1050 | x86_pmu_stop(event, 0); | 1050 | x86_pmu_stop(event, 0); |
1051 | } | 1051 | } |
1052 | 1052 | ||
1053 | /* | 1053 | /* |
1054 | * Repeat if there is more work to be done: | 1054 | * Repeat if there is more work to be done: |
1055 | */ | 1055 | */ |
1056 | status = intel_pmu_get_status(); | 1056 | status = intel_pmu_get_status(); |
1057 | if (status) | 1057 | if (status) |
1058 | goto again; | 1058 | goto again; |
1059 | 1059 | ||
1060 | done: | 1060 | done: |
1061 | intel_pmu_enable_all(0); | 1061 | intel_pmu_enable_all(0); |
1062 | return handled; | 1062 | return handled; |
1063 | } | 1063 | } |
1064 | 1064 | ||
1065 | static struct event_constraint * | 1065 | static struct event_constraint * |
1066 | intel_bts_constraints(struct perf_event *event) | 1066 | intel_bts_constraints(struct perf_event *event) |
1067 | { | 1067 | { |
1068 | struct hw_perf_event *hwc = &event->hw; | 1068 | struct hw_perf_event *hwc = &event->hw; |
1069 | unsigned int hw_event, bts_event; | 1069 | unsigned int hw_event, bts_event; |
1070 | 1070 | ||
1071 | if (event->attr.freq) | 1071 | if (event->attr.freq) |
1072 | return NULL; | 1072 | return NULL; |
1073 | 1073 | ||
1074 | hw_event = hwc->config & INTEL_ARCH_EVENT_MASK; | 1074 | hw_event = hwc->config & INTEL_ARCH_EVENT_MASK; |
1075 | bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); | 1075 | bts_event = x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS); |
1076 | 1076 | ||
1077 | if (unlikely(hw_event == bts_event && hwc->sample_period == 1)) | 1077 | if (unlikely(hw_event == bts_event && hwc->sample_period == 1)) |
1078 | return &bts_constraint; | 1078 | return &bts_constraint; |
1079 | 1079 | ||
1080 | return NULL; | 1080 | return NULL; |
1081 | } | 1081 | } |
1082 | 1082 | ||
1083 | static bool intel_try_alt_er(struct perf_event *event, int orig_idx) | 1083 | static bool intel_try_alt_er(struct perf_event *event, int orig_idx) |
1084 | { | 1084 | { |
1085 | if (!(x86_pmu.er_flags & ERF_HAS_RSP_1)) | 1085 | if (!(x86_pmu.er_flags & ERF_HAS_RSP_1)) |
1086 | return false; | 1086 | return false; |
1087 | 1087 | ||
1088 | if (event->hw.extra_reg.idx == EXTRA_REG_RSP_0) { | 1088 | if (event->hw.extra_reg.idx == EXTRA_REG_RSP_0) { |
1089 | event->hw.config &= ~INTEL_ARCH_EVENT_MASK; | 1089 | event->hw.config &= ~INTEL_ARCH_EVENT_MASK; |
1090 | event->hw.config |= 0x01bb; | 1090 | event->hw.config |= 0x01bb; |
1091 | event->hw.extra_reg.idx = EXTRA_REG_RSP_1; | 1091 | event->hw.extra_reg.idx = EXTRA_REG_RSP_1; |
1092 | event->hw.extra_reg.reg = MSR_OFFCORE_RSP_1; | 1092 | event->hw.extra_reg.reg = MSR_OFFCORE_RSP_1; |
1093 | } else if (event->hw.extra_reg.idx == EXTRA_REG_RSP_1) { | 1093 | } else if (event->hw.extra_reg.idx == EXTRA_REG_RSP_1) { |
1094 | event->hw.config &= ~INTEL_ARCH_EVENT_MASK; | 1094 | event->hw.config &= ~INTEL_ARCH_EVENT_MASK; |
1095 | event->hw.config |= 0x01b7; | 1095 | event->hw.config |= 0x01b7; |
1096 | event->hw.extra_reg.idx = EXTRA_REG_RSP_0; | 1096 | event->hw.extra_reg.idx = EXTRA_REG_RSP_0; |
1097 | event->hw.extra_reg.reg = MSR_OFFCORE_RSP_0; | 1097 | event->hw.extra_reg.reg = MSR_OFFCORE_RSP_0; |
1098 | } | 1098 | } |
1099 | 1099 | ||
1100 | if (event->hw.extra_reg.idx == orig_idx) | 1100 | if (event->hw.extra_reg.idx == orig_idx) |
1101 | return false; | 1101 | return false; |
1102 | 1102 | ||
1103 | return true; | 1103 | return true; |
1104 | } | 1104 | } |
1105 | 1105 | ||
1106 | /* | 1106 | /* |
1107 | * manage allocation of shared extra msr for certain events | 1107 | * manage allocation of shared extra msr for certain events |
1108 | * | 1108 | * |
1109 | * sharing can be: | 1109 | * sharing can be: |
1110 | * per-cpu: to be shared between the various events on a single PMU | 1110 | * per-cpu: to be shared between the various events on a single PMU |
1111 | * per-core: per-cpu + shared by HT threads | 1111 | * per-core: per-cpu + shared by HT threads |
1112 | */ | 1112 | */ |
1113 | static struct event_constraint * | 1113 | static struct event_constraint * |
1114 | __intel_shared_reg_get_constraints(struct cpu_hw_events *cpuc, | 1114 | __intel_shared_reg_get_constraints(struct cpu_hw_events *cpuc, |
1115 | struct perf_event *event) | 1115 | struct perf_event *event) |
1116 | { | 1116 | { |
1117 | struct event_constraint *c = &emptyconstraint; | 1117 | struct event_constraint *c = &emptyconstraint; |
1118 | struct hw_perf_event_extra *reg = &event->hw.extra_reg; | 1118 | struct hw_perf_event_extra *reg = &event->hw.extra_reg; |
1119 | struct er_account *era; | 1119 | struct er_account *era; |
1120 | unsigned long flags; | 1120 | unsigned long flags; |
1121 | int orig_idx = reg->idx; | 1121 | int orig_idx = reg->idx; |
1122 | 1122 | ||
1123 | /* already allocated shared msr */ | 1123 | /* already allocated shared msr */ |
1124 | if (reg->alloc) | 1124 | if (reg->alloc) |
1125 | return &unconstrained; | 1125 | return &unconstrained; |
1126 | 1126 | ||
1127 | again: | 1127 | again: |
1128 | era = &cpuc->shared_regs->regs[reg->idx]; | 1128 | era = &cpuc->shared_regs->regs[reg->idx]; |
1129 | /* | 1129 | /* |
1130 | * we use spin_lock_irqsave() to avoid lockdep issues when | 1130 | * we use spin_lock_irqsave() to avoid lockdep issues when |
1131 | * passing a fake cpuc | 1131 | * passing a fake cpuc |
1132 | */ | 1132 | */ |
1133 | raw_spin_lock_irqsave(&era->lock, flags); | 1133 | raw_spin_lock_irqsave(&era->lock, flags); |
1134 | 1134 | ||
1135 | if (!atomic_read(&era->ref) || era->config == reg->config) { | 1135 | if (!atomic_read(&era->ref) || era->config == reg->config) { |
1136 | 1136 | ||
1137 | /* lock in msr value */ | 1137 | /* lock in msr value */ |
1138 | era->config = reg->config; | 1138 | era->config = reg->config; |
1139 | era->reg = reg->reg; | 1139 | era->reg = reg->reg; |
1140 | 1140 | ||
1141 | /* one more user */ | 1141 | /* one more user */ |
1142 | atomic_inc(&era->ref); | 1142 | atomic_inc(&era->ref); |
1143 | 1143 | ||
1144 | /* no need to reallocate during incremental event scheduling */ | 1144 | /* no need to reallocate during incremental event scheduling */ |
1145 | reg->alloc = 1; | 1145 | reg->alloc = 1; |
1146 | 1146 | ||
1147 | /* | 1147 | /* |
1148 | * All events using extra_reg are unconstrained. | 1148 | * All events using extra_reg are unconstrained. |
1149 | * Avoids calling x86_get_event_constraints() | 1149 | * Avoids calling x86_get_event_constraints() |
1150 | * | 1150 | * |
1151 | * Must revisit if extra_reg controlling events | 1151 | * Must revisit if extra_reg controlling events |
1152 | * ever have constraints. Worst case we go through | 1152 | * ever have constraints. Worst case we go through |
1153 | * the regular event constraint table. | 1153 | * the regular event constraint table. |
1154 | */ | 1154 | */ |
1155 | c = &unconstrained; | 1155 | c = &unconstrained; |
1156 | } else if (intel_try_alt_er(event, orig_idx)) { | 1156 | } else if (intel_try_alt_er(event, orig_idx)) { |
1157 | raw_spin_unlock(&era->lock); | 1157 | raw_spin_unlock(&era->lock); |
1158 | goto again; | 1158 | goto again; |
1159 | } | 1159 | } |
1160 | raw_spin_unlock_irqrestore(&era->lock, flags); | 1160 | raw_spin_unlock_irqrestore(&era->lock, flags); |
1161 | 1161 | ||
1162 | return c; | 1162 | return c; |
1163 | } | 1163 | } |
1164 | 1164 | ||
1165 | static void | 1165 | static void |
1166 | __intel_shared_reg_put_constraints(struct cpu_hw_events *cpuc, | 1166 | __intel_shared_reg_put_constraints(struct cpu_hw_events *cpuc, |
1167 | struct hw_perf_event_extra *reg) | 1167 | struct hw_perf_event_extra *reg) |
1168 | { | 1168 | { |
1169 | struct er_account *era; | 1169 | struct er_account *era; |
1170 | 1170 | ||
1171 | /* | 1171 | /* |
1172 | * only put constraint if extra reg was actually | 1172 | * only put constraint if extra reg was actually |
1173 | * allocated. Also takes care of event which do | 1173 | * allocated. Also takes care of event which do |
1174 | * not use an extra shared reg | 1174 | * not use an extra shared reg |
1175 | */ | 1175 | */ |
1176 | if (!reg->alloc) | 1176 | if (!reg->alloc) |
1177 | return; | 1177 | return; |
1178 | 1178 | ||
1179 | era = &cpuc->shared_regs->regs[reg->idx]; | 1179 | era = &cpuc->shared_regs->regs[reg->idx]; |
1180 | 1180 | ||
1181 | /* one fewer user */ | 1181 | /* one fewer user */ |
1182 | atomic_dec(&era->ref); | 1182 | atomic_dec(&era->ref); |
1183 | 1183 | ||
1184 | /* allocate again next time */ | 1184 | /* allocate again next time */ |
1185 | reg->alloc = 0; | 1185 | reg->alloc = 0; |
1186 | } | 1186 | } |
1187 | 1187 | ||
1188 | static struct event_constraint * | 1188 | static struct event_constraint * |
1189 | intel_shared_regs_constraints(struct cpu_hw_events *cpuc, | 1189 | intel_shared_regs_constraints(struct cpu_hw_events *cpuc, |
1190 | struct perf_event *event) | 1190 | struct perf_event *event) |
1191 | { | 1191 | { |
1192 | struct event_constraint *c = NULL; | 1192 | struct event_constraint *c = NULL; |
1193 | 1193 | ||
1194 | if (event->hw.extra_reg.idx != EXTRA_REG_NONE) | 1194 | if (event->hw.extra_reg.idx != EXTRA_REG_NONE) |
1195 | c = __intel_shared_reg_get_constraints(cpuc, event); | 1195 | c = __intel_shared_reg_get_constraints(cpuc, event); |
1196 | 1196 | ||
1197 | return c; | 1197 | return c; |
1198 | } | 1198 | } |
1199 | 1199 | ||
1200 | static struct event_constraint * | 1200 | static struct event_constraint * |
1201 | intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) | 1201 | intel_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event) |
1202 | { | 1202 | { |
1203 | struct event_constraint *c; | 1203 | struct event_constraint *c; |
1204 | 1204 | ||
1205 | c = intel_bts_constraints(event); | 1205 | c = intel_bts_constraints(event); |
1206 | if (c) | 1206 | if (c) |
1207 | return c; | 1207 | return c; |
1208 | 1208 | ||
1209 | c = intel_pebs_constraints(event); | 1209 | c = intel_pebs_constraints(event); |
1210 | if (c) | 1210 | if (c) |
1211 | return c; | 1211 | return c; |
1212 | 1212 | ||
1213 | c = intel_shared_regs_constraints(cpuc, event); | 1213 | c = intel_shared_regs_constraints(cpuc, event); |
1214 | if (c) | 1214 | if (c) |
1215 | return c; | 1215 | return c; |
1216 | 1216 | ||
1217 | return x86_get_event_constraints(cpuc, event); | 1217 | return x86_get_event_constraints(cpuc, event); |
1218 | } | 1218 | } |
1219 | 1219 | ||
1220 | static void | 1220 | static void |
1221 | intel_put_shared_regs_event_constraints(struct cpu_hw_events *cpuc, | 1221 | intel_put_shared_regs_event_constraints(struct cpu_hw_events *cpuc, |
1222 | struct perf_event *event) | 1222 | struct perf_event *event) |
1223 | { | 1223 | { |
1224 | struct hw_perf_event_extra *reg; | 1224 | struct hw_perf_event_extra *reg; |
1225 | 1225 | ||
1226 | reg = &event->hw.extra_reg; | 1226 | reg = &event->hw.extra_reg; |
1227 | if (reg->idx != EXTRA_REG_NONE) | 1227 | if (reg->idx != EXTRA_REG_NONE) |
1228 | __intel_shared_reg_put_constraints(cpuc, reg); | 1228 | __intel_shared_reg_put_constraints(cpuc, reg); |
1229 | } | 1229 | } |
1230 | 1230 | ||
1231 | static void intel_put_event_constraints(struct cpu_hw_events *cpuc, | 1231 | static void intel_put_event_constraints(struct cpu_hw_events *cpuc, |
1232 | struct perf_event *event) | 1232 | struct perf_event *event) |
1233 | { | 1233 | { |
1234 | intel_put_shared_regs_event_constraints(cpuc, event); | 1234 | intel_put_shared_regs_event_constraints(cpuc, event); |
1235 | } | 1235 | } |
1236 | 1236 | ||
1237 | static int intel_pmu_hw_config(struct perf_event *event) | 1237 | static int intel_pmu_hw_config(struct perf_event *event) |
1238 | { | 1238 | { |
1239 | int ret = x86_pmu_hw_config(event); | 1239 | int ret = x86_pmu_hw_config(event); |
1240 | 1240 | ||
1241 | if (ret) | 1241 | if (ret) |
1242 | return ret; | 1242 | return ret; |
1243 | 1243 | ||
1244 | if (event->attr.precise_ip && | 1244 | if (event->attr.precise_ip && |
1245 | (event->hw.config & X86_RAW_EVENT_MASK) == 0x003c) { | 1245 | (event->hw.config & X86_RAW_EVENT_MASK) == 0x003c) { |
1246 | /* | 1246 | /* |
1247 | * Use an alternative encoding for CPU_CLK_UNHALTED.THREAD_P | 1247 | * Use an alternative encoding for CPU_CLK_UNHALTED.THREAD_P |
1248 | * (0x003c) so that we can use it with PEBS. | 1248 | * (0x003c) so that we can use it with PEBS. |
1249 | * | 1249 | * |
1250 | * The regular CPU_CLK_UNHALTED.THREAD_P event (0x003c) isn't | 1250 | * The regular CPU_CLK_UNHALTED.THREAD_P event (0x003c) isn't |
1251 | * PEBS capable. However we can use INST_RETIRED.ANY_P | 1251 | * PEBS capable. However we can use INST_RETIRED.ANY_P |
1252 | * (0x00c0), which is a PEBS capable event, to get the same | 1252 | * (0x00c0), which is a PEBS capable event, to get the same |
1253 | * count. | 1253 | * count. |
1254 | * | 1254 | * |
1255 | * INST_RETIRED.ANY_P counts the number of cycles that retires | 1255 | * INST_RETIRED.ANY_P counts the number of cycles that retires |
1256 | * CNTMASK instructions. By setting CNTMASK to a value (16) | 1256 | * CNTMASK instructions. By setting CNTMASK to a value (16) |
1257 | * larger than the maximum number of instructions that can be | 1257 | * larger than the maximum number of instructions that can be |
1258 | * retired per cycle (4) and then inverting the condition, we | 1258 | * retired per cycle (4) and then inverting the condition, we |
1259 | * count all cycles that retire 16 or less instructions, which | 1259 | * count all cycles that retire 16 or less instructions, which |
1260 | * is every cycle. | 1260 | * is every cycle. |
1261 | * | 1261 | * |
1262 | * Thereby we gain a PEBS capable cycle counter. | 1262 | * Thereby we gain a PEBS capable cycle counter. |
1263 | */ | 1263 | */ |
1264 | u64 alt_config = 0x108000c0; /* INST_RETIRED.TOTAL_CYCLES */ | 1264 | u64 alt_config = 0x108000c0; /* INST_RETIRED.TOTAL_CYCLES */ |
1265 | 1265 | ||
1266 | alt_config |= (event->hw.config & ~X86_RAW_EVENT_MASK); | 1266 | alt_config |= (event->hw.config & ~X86_RAW_EVENT_MASK); |
1267 | event->hw.config = alt_config; | 1267 | event->hw.config = alt_config; |
1268 | } | 1268 | } |
1269 | 1269 | ||
1270 | if (event->attr.type != PERF_TYPE_RAW) | 1270 | if (event->attr.type != PERF_TYPE_RAW) |
1271 | return 0; | 1271 | return 0; |
1272 | 1272 | ||
1273 | if (!(event->attr.config & ARCH_PERFMON_EVENTSEL_ANY)) | 1273 | if (!(event->attr.config & ARCH_PERFMON_EVENTSEL_ANY)) |
1274 | return 0; | 1274 | return 0; |
1275 | 1275 | ||
1276 | if (x86_pmu.version < 3) | 1276 | if (x86_pmu.version < 3) |
1277 | return -EINVAL; | 1277 | return -EINVAL; |
1278 | 1278 | ||
1279 | if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) | 1279 | if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) |
1280 | return -EACCES; | 1280 | return -EACCES; |
1281 | 1281 | ||
1282 | event->hw.config |= ARCH_PERFMON_EVENTSEL_ANY; | 1282 | event->hw.config |= ARCH_PERFMON_EVENTSEL_ANY; |
1283 | 1283 | ||
1284 | return 0; | 1284 | return 0; |
1285 | } | 1285 | } |
1286 | 1286 | ||
1287 | static __initconst const struct x86_pmu core_pmu = { | 1287 | static __initconst const struct x86_pmu core_pmu = { |
1288 | .name = "core", | 1288 | .name = "core", |
1289 | .handle_irq = x86_pmu_handle_irq, | 1289 | .handle_irq = x86_pmu_handle_irq, |
1290 | .disable_all = x86_pmu_disable_all, | 1290 | .disable_all = x86_pmu_disable_all, |
1291 | .enable_all = x86_pmu_enable_all, | 1291 | .enable_all = x86_pmu_enable_all, |
1292 | .enable = x86_pmu_enable_event, | 1292 | .enable = x86_pmu_enable_event, |
1293 | .disable = x86_pmu_disable_event, | 1293 | .disable = x86_pmu_disable_event, |
1294 | .hw_config = x86_pmu_hw_config, | 1294 | .hw_config = x86_pmu_hw_config, |
1295 | .schedule_events = x86_schedule_events, | 1295 | .schedule_events = x86_schedule_events, |
1296 | .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, | 1296 | .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, |
1297 | .perfctr = MSR_ARCH_PERFMON_PERFCTR0, | 1297 | .perfctr = MSR_ARCH_PERFMON_PERFCTR0, |
1298 | .event_map = intel_pmu_event_map, | 1298 | .event_map = intel_pmu_event_map, |
1299 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), | 1299 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), |
1300 | .apic = 1, | 1300 | .apic = 1, |
1301 | /* | 1301 | /* |
1302 | * Intel PMCs cannot be accessed sanely above 32 bit width, | 1302 | * Intel PMCs cannot be accessed sanely above 32 bit width, |
1303 | * so we install an artificial 1<<31 period regardless of | 1303 | * so we install an artificial 1<<31 period regardless of |
1304 | * the generic event period: | 1304 | * the generic event period: |
1305 | */ | 1305 | */ |
1306 | .max_period = (1ULL << 31) - 1, | 1306 | .max_period = (1ULL << 31) - 1, |
1307 | .get_event_constraints = intel_get_event_constraints, | 1307 | .get_event_constraints = intel_get_event_constraints, |
1308 | .put_event_constraints = intel_put_event_constraints, | 1308 | .put_event_constraints = intel_put_event_constraints, |
1309 | .event_constraints = intel_core_event_constraints, | 1309 | .event_constraints = intel_core_event_constraints, |
1310 | }; | 1310 | }; |
1311 | 1311 | ||
1312 | static struct intel_shared_regs *allocate_shared_regs(int cpu) | 1312 | static struct intel_shared_regs *allocate_shared_regs(int cpu) |
1313 | { | 1313 | { |
1314 | struct intel_shared_regs *regs; | 1314 | struct intel_shared_regs *regs; |
1315 | int i; | 1315 | int i; |
1316 | 1316 | ||
1317 | regs = kzalloc_node(sizeof(struct intel_shared_regs), | 1317 | regs = kzalloc_node(sizeof(struct intel_shared_regs), |
1318 | GFP_KERNEL, cpu_to_node(cpu)); | 1318 | GFP_KERNEL, cpu_to_node(cpu)); |
1319 | if (regs) { | 1319 | if (regs) { |
1320 | /* | 1320 | /* |
1321 | * initialize the locks to keep lockdep happy | 1321 | * initialize the locks to keep lockdep happy |
1322 | */ | 1322 | */ |
1323 | for (i = 0; i < EXTRA_REG_MAX; i++) | 1323 | for (i = 0; i < EXTRA_REG_MAX; i++) |
1324 | raw_spin_lock_init(®s->regs[i].lock); | 1324 | raw_spin_lock_init(®s->regs[i].lock); |
1325 | 1325 | ||
1326 | regs->core_id = -1; | 1326 | regs->core_id = -1; |
1327 | } | 1327 | } |
1328 | return regs; | 1328 | return regs; |
1329 | } | 1329 | } |
1330 | 1330 | ||
1331 | static int intel_pmu_cpu_prepare(int cpu) | 1331 | static int intel_pmu_cpu_prepare(int cpu) |
1332 | { | 1332 | { |
1333 | struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); | 1333 | struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); |
1334 | 1334 | ||
1335 | if (!x86_pmu.extra_regs) | 1335 | if (!x86_pmu.extra_regs) |
1336 | return NOTIFY_OK; | 1336 | return NOTIFY_OK; |
1337 | 1337 | ||
1338 | cpuc->shared_regs = allocate_shared_regs(cpu); | 1338 | cpuc->shared_regs = allocate_shared_regs(cpu); |
1339 | if (!cpuc->shared_regs) | 1339 | if (!cpuc->shared_regs) |
1340 | return NOTIFY_BAD; | 1340 | return NOTIFY_BAD; |
1341 | 1341 | ||
1342 | return NOTIFY_OK; | 1342 | return NOTIFY_OK; |
1343 | } | 1343 | } |
1344 | 1344 | ||
1345 | static void intel_pmu_cpu_starting(int cpu) | 1345 | static void intel_pmu_cpu_starting(int cpu) |
1346 | { | 1346 | { |
1347 | struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); | 1347 | struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); |
1348 | int core_id = topology_core_id(cpu); | 1348 | int core_id = topology_core_id(cpu); |
1349 | int i; | 1349 | int i; |
1350 | 1350 | ||
1351 | init_debug_store_on_cpu(cpu); | 1351 | init_debug_store_on_cpu(cpu); |
1352 | /* | 1352 | /* |
1353 | * Deal with CPUs that don't clear their LBRs on power-up. | 1353 | * Deal with CPUs that don't clear their LBRs on power-up. |
1354 | */ | 1354 | */ |
1355 | intel_pmu_lbr_reset(); | 1355 | intel_pmu_lbr_reset(); |
1356 | 1356 | ||
1357 | if (!cpuc->shared_regs || (x86_pmu.er_flags & ERF_NO_HT_SHARING)) | 1357 | if (!cpuc->shared_regs || (x86_pmu.er_flags & ERF_NO_HT_SHARING)) |
1358 | return; | 1358 | return; |
1359 | 1359 | ||
1360 | for_each_cpu(i, topology_thread_cpumask(cpu)) { | 1360 | for_each_cpu(i, topology_thread_cpumask(cpu)) { |
1361 | struct intel_shared_regs *pc; | 1361 | struct intel_shared_regs *pc; |
1362 | 1362 | ||
1363 | pc = per_cpu(cpu_hw_events, i).shared_regs; | 1363 | pc = per_cpu(cpu_hw_events, i).shared_regs; |
1364 | if (pc && pc->core_id == core_id) { | 1364 | if (pc && pc->core_id == core_id) { |
1365 | kfree(cpuc->shared_regs); | 1365 | kfree(cpuc->shared_regs); |
1366 | cpuc->shared_regs = pc; | 1366 | cpuc->shared_regs = pc; |
1367 | break; | 1367 | break; |
1368 | } | 1368 | } |
1369 | } | 1369 | } |
1370 | 1370 | ||
1371 | cpuc->shared_regs->core_id = core_id; | 1371 | cpuc->shared_regs->core_id = core_id; |
1372 | cpuc->shared_regs->refcnt++; | 1372 | cpuc->shared_regs->refcnt++; |
1373 | } | 1373 | } |
1374 | 1374 | ||
1375 | static void intel_pmu_cpu_dying(int cpu) | 1375 | static void intel_pmu_cpu_dying(int cpu) |
1376 | { | 1376 | { |
1377 | struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); | 1377 | struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu); |
1378 | struct intel_shared_regs *pc; | 1378 | struct intel_shared_regs *pc; |
1379 | 1379 | ||
1380 | pc = cpuc->shared_regs; | 1380 | pc = cpuc->shared_regs; |
1381 | if (pc) { | 1381 | if (pc) { |
1382 | if (pc->core_id == -1 || --pc->refcnt == 0) | 1382 | if (pc->core_id == -1 || --pc->refcnt == 0) |
1383 | kfree(pc); | 1383 | kfree(pc); |
1384 | cpuc->shared_regs = NULL; | 1384 | cpuc->shared_regs = NULL; |
1385 | } | 1385 | } |
1386 | 1386 | ||
1387 | fini_debug_store_on_cpu(cpu); | 1387 | fini_debug_store_on_cpu(cpu); |
1388 | } | 1388 | } |
1389 | 1389 | ||
1390 | static __initconst const struct x86_pmu intel_pmu = { | 1390 | static __initconst const struct x86_pmu intel_pmu = { |
1391 | .name = "Intel", | 1391 | .name = "Intel", |
1392 | .handle_irq = intel_pmu_handle_irq, | 1392 | .handle_irq = intel_pmu_handle_irq, |
1393 | .disable_all = intel_pmu_disable_all, | 1393 | .disable_all = intel_pmu_disable_all, |
1394 | .enable_all = intel_pmu_enable_all, | 1394 | .enable_all = intel_pmu_enable_all, |
1395 | .enable = intel_pmu_enable_event, | 1395 | .enable = intel_pmu_enable_event, |
1396 | .disable = intel_pmu_disable_event, | 1396 | .disable = intel_pmu_disable_event, |
1397 | .hw_config = intel_pmu_hw_config, | 1397 | .hw_config = intel_pmu_hw_config, |
1398 | .schedule_events = x86_schedule_events, | 1398 | .schedule_events = x86_schedule_events, |
1399 | .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, | 1399 | .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, |
1400 | .perfctr = MSR_ARCH_PERFMON_PERFCTR0, | 1400 | .perfctr = MSR_ARCH_PERFMON_PERFCTR0, |
1401 | .event_map = intel_pmu_event_map, | 1401 | .event_map = intel_pmu_event_map, |
1402 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), | 1402 | .max_events = ARRAY_SIZE(intel_perfmon_event_map), |
1403 | .apic = 1, | 1403 | .apic = 1, |
1404 | /* | 1404 | /* |
1405 | * Intel PMCs cannot be accessed sanely above 32 bit width, | 1405 | * Intel PMCs cannot be accessed sanely above 32 bit width, |
1406 | * so we install an artificial 1<<31 period regardless of | 1406 | * so we install an artificial 1<<31 period regardless of |
1407 | * the generic event period: | 1407 | * the generic event period: |
1408 | */ | 1408 | */ |
1409 | .max_period = (1ULL << 31) - 1, | 1409 | .max_period = (1ULL << 31) - 1, |
1410 | .get_event_constraints = intel_get_event_constraints, | 1410 | .get_event_constraints = intel_get_event_constraints, |
1411 | .put_event_constraints = intel_put_event_constraints, | 1411 | .put_event_constraints = intel_put_event_constraints, |
1412 | 1412 | ||
1413 | .cpu_prepare = intel_pmu_cpu_prepare, | 1413 | .cpu_prepare = intel_pmu_cpu_prepare, |
1414 | .cpu_starting = intel_pmu_cpu_starting, | 1414 | .cpu_starting = intel_pmu_cpu_starting, |
1415 | .cpu_dying = intel_pmu_cpu_dying, | 1415 | .cpu_dying = intel_pmu_cpu_dying, |
1416 | }; | 1416 | }; |
1417 | 1417 | ||
1418 | static void intel_clovertown_quirks(void) | 1418 | static void intel_clovertown_quirks(void) |
1419 | { | 1419 | { |
1420 | /* | 1420 | /* |
1421 | * PEBS is unreliable due to: | 1421 | * PEBS is unreliable due to: |
1422 | * | 1422 | * |
1423 | * AJ67 - PEBS may experience CPL leaks | 1423 | * AJ67 - PEBS may experience CPL leaks |
1424 | * AJ68 - PEBS PMI may be delayed by one event | 1424 | * AJ68 - PEBS PMI may be delayed by one event |
1425 | * AJ69 - GLOBAL_STATUS[62] will only be set when DEBUGCTL[12] | 1425 | * AJ69 - GLOBAL_STATUS[62] will only be set when DEBUGCTL[12] |
1426 | * AJ106 - FREEZE_LBRS_ON_PMI doesn't work in combination with PEBS | 1426 | * AJ106 - FREEZE_LBRS_ON_PMI doesn't work in combination with PEBS |
1427 | * | 1427 | * |
1428 | * AJ67 could be worked around by restricting the OS/USR flags. | 1428 | * AJ67 could be worked around by restricting the OS/USR flags. |
1429 | * AJ69 could be worked around by setting PMU_FREEZE_ON_PMI. | 1429 | * AJ69 could be worked around by setting PMU_FREEZE_ON_PMI. |
1430 | * | 1430 | * |
1431 | * AJ106 could possibly be worked around by not allowing LBR | 1431 | * AJ106 could possibly be worked around by not allowing LBR |
1432 | * usage from PEBS, including the fixup. | 1432 | * usage from PEBS, including the fixup. |
1433 | * AJ68 could possibly be worked around by always programming | 1433 | * AJ68 could possibly be worked around by always programming |
1434 | * a pebs_event_reset[0] value and coping with the lost events. | 1434 | * a pebs_event_reset[0] value and coping with the lost events. |
1435 | * | 1435 | * |
1436 | * But taken together it might just make sense to not enable PEBS on | 1436 | * But taken together it might just make sense to not enable PEBS on |
1437 | * these chips. | 1437 | * these chips. |
1438 | */ | 1438 | */ |
1439 | printk(KERN_WARNING "PEBS disabled due to CPU errata.\n"); | 1439 | printk(KERN_WARNING "PEBS disabled due to CPU errata.\n"); |
1440 | x86_pmu.pebs = 0; | 1440 | x86_pmu.pebs = 0; |
1441 | x86_pmu.pebs_constraints = NULL; | 1441 | x86_pmu.pebs_constraints = NULL; |
1442 | } | 1442 | } |
1443 | 1443 | ||
1444 | static __init int intel_pmu_init(void) | 1444 | static __init int intel_pmu_init(void) |
1445 | { | 1445 | { |
1446 | union cpuid10_edx edx; | 1446 | union cpuid10_edx edx; |
1447 | union cpuid10_eax eax; | 1447 | union cpuid10_eax eax; |
1448 | unsigned int unused; | 1448 | unsigned int unused; |
1449 | unsigned int ebx; | 1449 | unsigned int ebx; |
1450 | int version; | 1450 | int version; |
1451 | 1451 | ||
1452 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) { | 1452 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) { |
1453 | switch (boot_cpu_data.x86) { | 1453 | switch (boot_cpu_data.x86) { |
1454 | case 0x6: | 1454 | case 0x6: |
1455 | return p6_pmu_init(); | 1455 | return p6_pmu_init(); |
1456 | case 0xf: | 1456 | case 0xf: |
1457 | return p4_pmu_init(); | 1457 | return p4_pmu_init(); |
1458 | } | 1458 | } |
1459 | return -ENODEV; | 1459 | return -ENODEV; |
1460 | } | 1460 | } |
1461 | 1461 | ||
1462 | /* | 1462 | /* |
1463 | * Check whether the Architectural PerfMon supports | 1463 | * Check whether the Architectural PerfMon supports |
1464 | * Branch Misses Retired hw_event or not. | 1464 | * Branch Misses Retired hw_event or not. |
1465 | */ | 1465 | */ |
1466 | cpuid(10, &eax.full, &ebx, &unused, &edx.full); | 1466 | cpuid(10, &eax.full, &ebx, &unused, &edx.full); |
1467 | if (eax.split.mask_length <= ARCH_PERFMON_BRANCH_MISSES_RETIRED) | 1467 | if (eax.split.mask_length <= ARCH_PERFMON_BRANCH_MISSES_RETIRED) |
1468 | return -ENODEV; | 1468 | return -ENODEV; |
1469 | 1469 | ||
1470 | version = eax.split.version_id; | 1470 | version = eax.split.version_id; |
1471 | if (version < 2) | 1471 | if (version < 2) |
1472 | x86_pmu = core_pmu; | 1472 | x86_pmu = core_pmu; |
1473 | else | 1473 | else |
1474 | x86_pmu = intel_pmu; | 1474 | x86_pmu = intel_pmu; |
1475 | 1475 | ||
1476 | x86_pmu.version = version; | 1476 | x86_pmu.version = version; |
1477 | x86_pmu.num_counters = eax.split.num_counters; | 1477 | x86_pmu.num_counters = eax.split.num_counters; |
1478 | x86_pmu.cntval_bits = eax.split.bit_width; | 1478 | x86_pmu.cntval_bits = eax.split.bit_width; |
1479 | x86_pmu.cntval_mask = (1ULL << eax.split.bit_width) - 1; | 1479 | x86_pmu.cntval_mask = (1ULL << eax.split.bit_width) - 1; |
1480 | 1480 | ||
1481 | /* | 1481 | /* |
1482 | * Quirk: v2 perfmon does not report fixed-purpose events, so | 1482 | * Quirk: v2 perfmon does not report fixed-purpose events, so |
1483 | * assume at least 3 events: | 1483 | * assume at least 3 events: |
1484 | */ | 1484 | */ |
1485 | if (version > 1) | 1485 | if (version > 1) |
1486 | x86_pmu.num_counters_fixed = max((int)edx.split.num_counters_fixed, 3); | 1486 | x86_pmu.num_counters_fixed = max((int)edx.split.num_counters_fixed, 3); |
1487 | 1487 | ||
1488 | /* | 1488 | /* |
1489 | * v2 and above have a perf capabilities MSR | 1489 | * v2 and above have a perf capabilities MSR |
1490 | */ | 1490 | */ |
1491 | if (version > 1) { | 1491 | if (version > 1) { |
1492 | u64 capabilities; | 1492 | u64 capabilities; |
1493 | 1493 | ||
1494 | rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities); | 1494 | rdmsrl(MSR_IA32_PERF_CAPABILITIES, capabilities); |
1495 | x86_pmu.intel_cap.capabilities = capabilities; | 1495 | x86_pmu.intel_cap.capabilities = capabilities; |
1496 | } | 1496 | } |
1497 | 1497 | ||
1498 | intel_ds_init(); | 1498 | intel_ds_init(); |
1499 | 1499 | ||
1500 | /* | 1500 | /* |
1501 | * Install the hw-cache-events table: | 1501 | * Install the hw-cache-events table: |
1502 | */ | 1502 | */ |
1503 | switch (boot_cpu_data.x86_model) { | 1503 | switch (boot_cpu_data.x86_model) { |
1504 | case 14: /* 65 nm core solo/duo, "Yonah" */ | 1504 | case 14: /* 65 nm core solo/duo, "Yonah" */ |
1505 | pr_cont("Core events, "); | 1505 | pr_cont("Core events, "); |
1506 | break; | 1506 | break; |
1507 | 1507 | ||
1508 | case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */ | 1508 | case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */ |
1509 | x86_pmu.quirks = intel_clovertown_quirks; | 1509 | x86_pmu.quirks = intel_clovertown_quirks; |
1510 | case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */ | 1510 | case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */ |
1511 | case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */ | 1511 | case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */ |
1512 | case 29: /* six-core 45 nm xeon "Dunnington" */ | 1512 | case 29: /* six-core 45 nm xeon "Dunnington" */ |
1513 | memcpy(hw_cache_event_ids, core2_hw_cache_event_ids, | 1513 | memcpy(hw_cache_event_ids, core2_hw_cache_event_ids, |
1514 | sizeof(hw_cache_event_ids)); | 1514 | sizeof(hw_cache_event_ids)); |
1515 | 1515 | ||
1516 | intel_pmu_lbr_init_core(); | 1516 | intel_pmu_lbr_init_core(); |
1517 | 1517 | ||
1518 | x86_pmu.event_constraints = intel_core2_event_constraints; | 1518 | x86_pmu.event_constraints = intel_core2_event_constraints; |
1519 | x86_pmu.pebs_constraints = intel_core2_pebs_event_constraints; | 1519 | x86_pmu.pebs_constraints = intel_core2_pebs_event_constraints; |
1520 | pr_cont("Core2 events, "); | 1520 | pr_cont("Core2 events, "); |
1521 | break; | 1521 | break; |
1522 | 1522 | ||
1523 | case 26: /* 45 nm nehalem, "Bloomfield" */ | 1523 | case 26: /* 45 nm nehalem, "Bloomfield" */ |
1524 | case 30: /* 45 nm nehalem, "Lynnfield" */ | 1524 | case 30: /* 45 nm nehalem, "Lynnfield" */ |
1525 | case 46: /* 45 nm nehalem-ex, "Beckton" */ | 1525 | case 46: /* 45 nm nehalem-ex, "Beckton" */ |
1526 | memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids, | 1526 | memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids, |
1527 | sizeof(hw_cache_event_ids)); | 1527 | sizeof(hw_cache_event_ids)); |
1528 | memcpy(hw_cache_extra_regs, nehalem_hw_cache_extra_regs, | 1528 | memcpy(hw_cache_extra_regs, nehalem_hw_cache_extra_regs, |
1529 | sizeof(hw_cache_extra_regs)); | 1529 | sizeof(hw_cache_extra_regs)); |
1530 | 1530 | ||
1531 | intel_pmu_lbr_init_nhm(); | 1531 | intel_pmu_lbr_init_nhm(); |
1532 | 1532 | ||
1533 | x86_pmu.event_constraints = intel_nehalem_event_constraints; | 1533 | x86_pmu.event_constraints = intel_nehalem_event_constraints; |
1534 | x86_pmu.pebs_constraints = intel_nehalem_pebs_event_constraints; | 1534 | x86_pmu.pebs_constraints = intel_nehalem_pebs_event_constraints; |
1535 | x86_pmu.enable_all = intel_pmu_nhm_enable_all; | 1535 | x86_pmu.enable_all = intel_pmu_nhm_enable_all; |
1536 | x86_pmu.extra_regs = intel_nehalem_extra_regs; | 1536 | x86_pmu.extra_regs = intel_nehalem_extra_regs; |
1537 | 1537 | ||
1538 | /* UOPS_ISSUED.STALLED_CYCLES */ | 1538 | /* UOPS_ISSUED.STALLED_CYCLES */ |
1539 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x180010e; | 1539 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x180010e; |
1540 | /* UOPS_EXECUTED.CORE_ACTIVE_CYCLES,c=1,i=1 */ | 1540 | /* UOPS_EXECUTED.CORE_ACTIVE_CYCLES,c=1,i=1 */ |
1541 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x1803fb1; | 1541 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x1803fb1; |
1542 | 1542 | ||
1543 | if (ebx & 0x40) { | 1543 | if (ebx & 0x40) { |
1544 | /* | 1544 | /* |
1545 | * Erratum AAJ80 detected, we work it around by using | 1545 | * Erratum AAJ80 detected, we work it around by using |
1546 | * the BR_MISP_EXEC.ANY event. This will over-count | 1546 | * the BR_MISP_EXEC.ANY event. This will over-count |
1547 | * branch-misses, but it's still much better than the | 1547 | * branch-misses, but it's still much better than the |
1548 | * architectural event which is often completely bogus: | 1548 | * architectural event which is often completely bogus: |
1549 | */ | 1549 | */ |
1550 | intel_perfmon_event_map[PERF_COUNT_HW_BRANCH_MISSES] = 0x7f89; | 1550 | intel_perfmon_event_map[PERF_COUNT_HW_BRANCH_MISSES] = 0x7f89; |
1551 | 1551 | ||
1552 | pr_cont("erratum AAJ80 worked around, "); | 1552 | pr_cont("erratum AAJ80 worked around, "); |
1553 | } | 1553 | } |
1554 | pr_cont("Nehalem events, "); | 1554 | pr_cont("Nehalem events, "); |
1555 | break; | 1555 | break; |
1556 | 1556 | ||
1557 | case 28: /* Atom */ | 1557 | case 28: /* Atom */ |
1558 | memcpy(hw_cache_event_ids, atom_hw_cache_event_ids, | 1558 | memcpy(hw_cache_event_ids, atom_hw_cache_event_ids, |
1559 | sizeof(hw_cache_event_ids)); | 1559 | sizeof(hw_cache_event_ids)); |
1560 | 1560 | ||
1561 | intel_pmu_lbr_init_atom(); | 1561 | intel_pmu_lbr_init_atom(); |
1562 | 1562 | ||
1563 | x86_pmu.event_constraints = intel_gen_event_constraints; | 1563 | x86_pmu.event_constraints = intel_gen_event_constraints; |
1564 | x86_pmu.pebs_constraints = intel_atom_pebs_event_constraints; | 1564 | x86_pmu.pebs_constraints = intel_atom_pebs_event_constraints; |
1565 | pr_cont("Atom events, "); | 1565 | pr_cont("Atom events, "); |
1566 | break; | 1566 | break; |
1567 | 1567 | ||
1568 | case 37: /* 32 nm nehalem, "Clarkdale" */ | 1568 | case 37: /* 32 nm nehalem, "Clarkdale" */ |
1569 | case 44: /* 32 nm nehalem, "Gulftown" */ | 1569 | case 44: /* 32 nm nehalem, "Gulftown" */ |
1570 | case 47: /* 32 nm Xeon E7 */ | 1570 | case 47: /* 32 nm Xeon E7 */ |
1571 | memcpy(hw_cache_event_ids, westmere_hw_cache_event_ids, | 1571 | memcpy(hw_cache_event_ids, westmere_hw_cache_event_ids, |
1572 | sizeof(hw_cache_event_ids)); | 1572 | sizeof(hw_cache_event_ids)); |
1573 | memcpy(hw_cache_extra_regs, nehalem_hw_cache_extra_regs, | 1573 | memcpy(hw_cache_extra_regs, nehalem_hw_cache_extra_regs, |
1574 | sizeof(hw_cache_extra_regs)); | 1574 | sizeof(hw_cache_extra_regs)); |
1575 | 1575 | ||
1576 | intel_pmu_lbr_init_nhm(); | 1576 | intel_pmu_lbr_init_nhm(); |
1577 | 1577 | ||
1578 | x86_pmu.event_constraints = intel_westmere_event_constraints; | 1578 | x86_pmu.event_constraints = intel_westmere_event_constraints; |
1579 | x86_pmu.enable_all = intel_pmu_nhm_enable_all; | 1579 | x86_pmu.enable_all = intel_pmu_nhm_enable_all; |
1580 | x86_pmu.pebs_constraints = intel_westmere_pebs_event_constraints; | 1580 | x86_pmu.pebs_constraints = intel_westmere_pebs_event_constraints; |
1581 | x86_pmu.extra_regs = intel_westmere_extra_regs; | 1581 | x86_pmu.extra_regs = intel_westmere_extra_regs; |
1582 | x86_pmu.er_flags |= ERF_HAS_RSP_1; | 1582 | x86_pmu.er_flags |= ERF_HAS_RSP_1; |
1583 | 1583 | ||
1584 | /* UOPS_ISSUED.STALLED_CYCLES */ | 1584 | /* UOPS_ISSUED.STALLED_CYCLES */ |
1585 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x180010e; | 1585 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x180010e; |
1586 | /* UOPS_EXECUTED.CORE_ACTIVE_CYCLES,c=1,i=1 */ | 1586 | /* UOPS_EXECUTED.CORE_ACTIVE_CYCLES,c=1,i=1 */ |
1587 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x1803fb1; | 1587 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x1803fb1; |
1588 | 1588 | ||
1589 | pr_cont("Westmere events, "); | 1589 | pr_cont("Westmere events, "); |
1590 | break; | 1590 | break; |
1591 | 1591 | ||
1592 | case 42: /* SandyBridge */ | 1592 | case 42: /* SandyBridge */ |
1593 | case 45: /* SandyBridge, "Romely-EP" */ | ||
1593 | memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, | 1594 | memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, |
1594 | sizeof(hw_cache_event_ids)); | 1595 | sizeof(hw_cache_event_ids)); |
1595 | 1596 | ||
1596 | intel_pmu_lbr_init_nhm(); | 1597 | intel_pmu_lbr_init_nhm(); |
1597 | 1598 | ||
1598 | x86_pmu.event_constraints = intel_snb_event_constraints; | 1599 | x86_pmu.event_constraints = intel_snb_event_constraints; |
1599 | x86_pmu.pebs_constraints = intel_snb_pebs_events; | 1600 | x86_pmu.pebs_constraints = intel_snb_pebs_events; |
1600 | x86_pmu.extra_regs = intel_snb_extra_regs; | 1601 | x86_pmu.extra_regs = intel_snb_extra_regs; |
1601 | /* all extra regs are per-cpu when HT is on */ | 1602 | /* all extra regs are per-cpu when HT is on */ |
1602 | x86_pmu.er_flags |= ERF_HAS_RSP_1; | 1603 | x86_pmu.er_flags |= ERF_HAS_RSP_1; |
1603 | x86_pmu.er_flags |= ERF_NO_HT_SHARING; | 1604 | x86_pmu.er_flags |= ERF_NO_HT_SHARING; |
1604 | 1605 | ||
1605 | /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */ | 1606 | /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */ |
1606 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x180010e; | 1607 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x180010e; |
1607 | /* UOPS_DISPATCHED.THREAD,c=1,i=1 to count stall cycles*/ | 1608 | /* UOPS_DISPATCHED.THREAD,c=1,i=1 to count stall cycles*/ |
1608 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x18001b1; | 1609 | intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x18001b1; |
1609 | 1610 | ||
1610 | pr_cont("SandyBridge events, "); | 1611 | pr_cont("SandyBridge events, "); |
1611 | break; | 1612 | break; |
1612 | 1613 | ||
1613 | default: | 1614 | default: |
1614 | switch (x86_pmu.version) { | 1615 | switch (x86_pmu.version) { |
1615 | case 1: | 1616 | case 1: |
1616 | x86_pmu.event_constraints = intel_v1_event_constraints; | 1617 | x86_pmu.event_constraints = intel_v1_event_constraints; |
1617 | pr_cont("generic architected perfmon v1, "); | 1618 | pr_cont("generic architected perfmon v1, "); |
1618 | break; | 1619 | break; |
1619 | default: | 1620 | default: |
1620 | /* | 1621 | /* |
1621 | * default constraints for v2 and up | 1622 | * default constraints for v2 and up |
1622 | */ | 1623 | */ |
1623 | x86_pmu.event_constraints = intel_gen_event_constraints; | 1624 | x86_pmu.event_constraints = intel_gen_event_constraints; |
1624 | pr_cont("generic architected perfmon, "); | 1625 | pr_cont("generic architected perfmon, "); |
1625 | break; | 1626 | break; |
1626 | } | 1627 | } |
1627 | } | 1628 | } |
1628 | return 0; | 1629 | return 0; |
1629 | } | 1630 | } |
1630 | 1631 | ||
1631 | #else /* CONFIG_CPU_SUP_INTEL */ | 1632 | #else /* CONFIG_CPU_SUP_INTEL */ |
1632 | 1633 | ||
1633 | static int intel_pmu_init(void) | 1634 | static int intel_pmu_init(void) |
1634 | { | 1635 | { |
1635 | return 0; | 1636 | return 0; |
1636 | } | 1637 | } |
1637 | 1638 | ||
1638 | static struct intel_shared_regs *allocate_shared_regs(int cpu) | 1639 | static struct intel_shared_regs *allocate_shared_regs(int cpu) |
1639 | { | 1640 | { |
1640 | return NULL; | 1641 | return NULL; |
1641 | } | 1642 | } |
1642 | #endif /* CONFIG_CPU_SUP_INTEL */ | 1643 | #endif /* CONFIG_CPU_SUP_INTEL */ |
1643 | 1644 |
kernel/Makefile
1 | # | 1 | # |
2 | # Makefile for the linux kernel. | 2 | # Makefile for the linux kernel. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ | 5 | obj-y = sched.o fork.o exec_domain.o panic.o printk.o \ |
6 | cpu.o exit.o itimer.o time.o softirq.o resource.o \ | 6 | cpu.o exit.o itimer.o time.o softirq.o resource.o \ |
7 | sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \ | 7 | sysctl.o sysctl_binary.o capability.o ptrace.o timer.o user.o \ |
8 | signal.o sys.o kmod.o workqueue.o pid.o \ | 8 | signal.o sys.o kmod.o workqueue.o pid.o \ |
9 | rcupdate.o extable.o params.o posix-timers.o \ | 9 | rcupdate.o extable.o params.o posix-timers.o \ |
10 | kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ | 10 | kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \ |
11 | hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ | 11 | hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \ |
12 | notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \ | 12 | notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \ |
13 | async.o range.o jump_label.o | 13 | async.o range.o |
14 | obj-y += groups.o | 14 | obj-y += groups.o |
15 | 15 | ||
16 | ifdef CONFIG_FUNCTION_TRACER | 16 | ifdef CONFIG_FUNCTION_TRACER |
17 | # Do not trace debug files and internal ftrace files | 17 | # Do not trace debug files and internal ftrace files |
18 | CFLAGS_REMOVE_lockdep.o = -pg | 18 | CFLAGS_REMOVE_lockdep.o = -pg |
19 | CFLAGS_REMOVE_lockdep_proc.o = -pg | 19 | CFLAGS_REMOVE_lockdep_proc.o = -pg |
20 | CFLAGS_REMOVE_mutex-debug.o = -pg | 20 | CFLAGS_REMOVE_mutex-debug.o = -pg |
21 | CFLAGS_REMOVE_rtmutex-debug.o = -pg | 21 | CFLAGS_REMOVE_rtmutex-debug.o = -pg |
22 | CFLAGS_REMOVE_cgroup-debug.o = -pg | 22 | CFLAGS_REMOVE_cgroup-debug.o = -pg |
23 | CFLAGS_REMOVE_sched_clock.o = -pg | 23 | CFLAGS_REMOVE_sched_clock.o = -pg |
24 | CFLAGS_REMOVE_irq_work.o = -pg | 24 | CFLAGS_REMOVE_irq_work.o = -pg |
25 | endif | 25 | endif |
26 | 26 | ||
27 | obj-$(CONFIG_FREEZER) += freezer.o | 27 | obj-$(CONFIG_FREEZER) += freezer.o |
28 | obj-$(CONFIG_PROFILING) += profile.o | 28 | obj-$(CONFIG_PROFILING) += profile.o |
29 | obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o | 29 | obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o |
30 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 30 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
31 | obj-y += time/ | 31 | obj-y += time/ |
32 | obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o | 32 | obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o |
33 | obj-$(CONFIG_LOCKDEP) += lockdep.o | 33 | obj-$(CONFIG_LOCKDEP) += lockdep.o |
34 | ifeq ($(CONFIG_PROC_FS),y) | 34 | ifeq ($(CONFIG_PROC_FS),y) |
35 | obj-$(CONFIG_LOCKDEP) += lockdep_proc.o | 35 | obj-$(CONFIG_LOCKDEP) += lockdep_proc.o |
36 | endif | 36 | endif |
37 | obj-$(CONFIG_FUTEX) += futex.o | 37 | obj-$(CONFIG_FUTEX) += futex.o |
38 | ifeq ($(CONFIG_COMPAT),y) | 38 | ifeq ($(CONFIG_COMPAT),y) |
39 | obj-$(CONFIG_FUTEX) += futex_compat.o | 39 | obj-$(CONFIG_FUTEX) += futex_compat.o |
40 | endif | 40 | endif |
41 | obj-$(CONFIG_RT_MUTEXES) += rtmutex.o | 41 | obj-$(CONFIG_RT_MUTEXES) += rtmutex.o |
42 | obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o | 42 | obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o |
43 | obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o | 43 | obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o |
44 | obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o | 44 | obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o |
45 | obj-$(CONFIG_SMP) += smp.o | 45 | obj-$(CONFIG_SMP) += smp.o |
46 | ifneq ($(CONFIG_SMP),y) | 46 | ifneq ($(CONFIG_SMP),y) |
47 | obj-y += up.o | 47 | obj-y += up.o |
48 | endif | 48 | endif |
49 | obj-$(CONFIG_SMP) += spinlock.o | 49 | obj-$(CONFIG_SMP) += spinlock.o |
50 | obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o | 50 | obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o |
51 | obj-$(CONFIG_PROVE_LOCKING) += spinlock.o | 51 | obj-$(CONFIG_PROVE_LOCKING) += spinlock.o |
52 | obj-$(CONFIG_UID16) += uid16.o | 52 | obj-$(CONFIG_UID16) += uid16.o |
53 | obj-$(CONFIG_MODULES) += module.o | 53 | obj-$(CONFIG_MODULES) += module.o |
54 | obj-$(CONFIG_KALLSYMS) += kallsyms.o | 54 | obj-$(CONFIG_KALLSYMS) += kallsyms.o |
55 | obj-$(CONFIG_PM) += power/ | 55 | obj-$(CONFIG_PM) += power/ |
56 | obj-$(CONFIG_FREEZER) += power/ | 56 | obj-$(CONFIG_FREEZER) += power/ |
57 | obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o | 57 | obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o |
58 | obj-$(CONFIG_KEXEC) += kexec.o | 58 | obj-$(CONFIG_KEXEC) += kexec.o |
59 | obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o | 59 | obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o |
60 | obj-$(CONFIG_COMPAT) += compat.o | 60 | obj-$(CONFIG_COMPAT) += compat.o |
61 | obj-$(CONFIG_CGROUPS) += cgroup.o | 61 | obj-$(CONFIG_CGROUPS) += cgroup.o |
62 | obj-$(CONFIG_CGROUP_FREEZER) += cgroup_freezer.o | 62 | obj-$(CONFIG_CGROUP_FREEZER) += cgroup_freezer.o |
63 | obj-$(CONFIG_CPUSETS) += cpuset.o | 63 | obj-$(CONFIG_CPUSETS) += cpuset.o |
64 | obj-$(CONFIG_UTS_NS) += utsname.o | 64 | obj-$(CONFIG_UTS_NS) += utsname.o |
65 | obj-$(CONFIG_USER_NS) += user_namespace.o | 65 | obj-$(CONFIG_USER_NS) += user_namespace.o |
66 | obj-$(CONFIG_PID_NS) += pid_namespace.o | 66 | obj-$(CONFIG_PID_NS) += pid_namespace.o |
67 | obj-$(CONFIG_IKCONFIG) += configs.o | 67 | obj-$(CONFIG_IKCONFIG) += configs.o |
68 | obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o | 68 | obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o |
69 | obj-$(CONFIG_SMP) += stop_machine.o | 69 | obj-$(CONFIG_SMP) += stop_machine.o |
70 | obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o | 70 | obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o |
71 | obj-$(CONFIG_AUDIT) += audit.o auditfilter.o | 71 | obj-$(CONFIG_AUDIT) += audit.o auditfilter.o |
72 | obj-$(CONFIG_AUDITSYSCALL) += auditsc.o | 72 | obj-$(CONFIG_AUDITSYSCALL) += auditsc.o |
73 | obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o | 73 | obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o |
74 | obj-$(CONFIG_AUDIT_TREE) += audit_tree.o | 74 | obj-$(CONFIG_AUDIT_TREE) += audit_tree.o |
75 | obj-$(CONFIG_GCOV_KERNEL) += gcov/ | 75 | obj-$(CONFIG_GCOV_KERNEL) += gcov/ |
76 | obj-$(CONFIG_KPROBES) += kprobes.o | 76 | obj-$(CONFIG_KPROBES) += kprobes.o |
77 | obj-$(CONFIG_KGDB) += debug/ | 77 | obj-$(CONFIG_KGDB) += debug/ |
78 | obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o | 78 | obj-$(CONFIG_DETECT_HUNG_TASK) += hung_task.o |
79 | obj-$(CONFIG_LOCKUP_DETECTOR) += watchdog.o | 79 | obj-$(CONFIG_LOCKUP_DETECTOR) += watchdog.o |
80 | obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ | 80 | obj-$(CONFIG_GENERIC_HARDIRQS) += irq/ |
81 | obj-$(CONFIG_SECCOMP) += seccomp.o | 81 | obj-$(CONFIG_SECCOMP) += seccomp.o |
82 | obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o | 82 | obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o |
83 | obj-$(CONFIG_TREE_RCU) += rcutree.o | 83 | obj-$(CONFIG_TREE_RCU) += rcutree.o |
84 | obj-$(CONFIG_TREE_PREEMPT_RCU) += rcutree.o | 84 | obj-$(CONFIG_TREE_PREEMPT_RCU) += rcutree.o |
85 | obj-$(CONFIG_TREE_RCU_TRACE) += rcutree_trace.o | 85 | obj-$(CONFIG_TREE_RCU_TRACE) += rcutree_trace.o |
86 | obj-$(CONFIG_TINY_RCU) += rcutiny.o | 86 | obj-$(CONFIG_TINY_RCU) += rcutiny.o |
87 | obj-$(CONFIG_TINY_PREEMPT_RCU) += rcutiny.o | 87 | obj-$(CONFIG_TINY_PREEMPT_RCU) += rcutiny.o |
88 | obj-$(CONFIG_RELAY) += relay.o | 88 | obj-$(CONFIG_RELAY) += relay.o |
89 | obj-$(CONFIG_SYSCTL) += utsname_sysctl.o | 89 | obj-$(CONFIG_SYSCTL) += utsname_sysctl.o |
90 | obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o | 90 | obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o |
91 | obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o | 91 | obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o |
92 | obj-$(CONFIG_TRACEPOINTS) += tracepoint.o | 92 | obj-$(CONFIG_TRACEPOINTS) += tracepoint.o |
93 | obj-$(CONFIG_LATENCYTOP) += latencytop.o | 93 | obj-$(CONFIG_LATENCYTOP) += latencytop.o |
94 | obj-$(CONFIG_BINFMT_ELF) += elfcore.o | 94 | obj-$(CONFIG_BINFMT_ELF) += elfcore.o |
95 | obj-$(CONFIG_COMPAT_BINFMT_ELF) += elfcore.o | 95 | obj-$(CONFIG_COMPAT_BINFMT_ELF) += elfcore.o |
96 | obj-$(CONFIG_BINFMT_ELF_FDPIC) += elfcore.o | 96 | obj-$(CONFIG_BINFMT_ELF_FDPIC) += elfcore.o |
97 | obj-$(CONFIG_FUNCTION_TRACER) += trace/ | 97 | obj-$(CONFIG_FUNCTION_TRACER) += trace/ |
98 | obj-$(CONFIG_TRACING) += trace/ | 98 | obj-$(CONFIG_TRACING) += trace/ |
99 | obj-$(CONFIG_X86_DS) += trace/ | 99 | obj-$(CONFIG_X86_DS) += trace/ |
100 | obj-$(CONFIG_RING_BUFFER) += trace/ | 100 | obj-$(CONFIG_RING_BUFFER) += trace/ |
101 | obj-$(CONFIG_TRACEPOINTS) += trace/ | 101 | obj-$(CONFIG_TRACEPOINTS) += trace/ |
102 | obj-$(CONFIG_SMP) += sched_cpupri.o | 102 | obj-$(CONFIG_SMP) += sched_cpupri.o |
103 | obj-$(CONFIG_IRQ_WORK) += irq_work.o | 103 | obj-$(CONFIG_IRQ_WORK) += irq_work.o |
104 | 104 | ||
105 | obj-$(CONFIG_PERF_EVENTS) += events/ | 105 | obj-$(CONFIG_PERF_EVENTS) += events/ |
106 | 106 | ||
107 | obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o | 107 | obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o |
108 | obj-$(CONFIG_PADATA) += padata.o | 108 | obj-$(CONFIG_PADATA) += padata.o |
109 | obj-$(CONFIG_CRASH_DUMP) += crash_dump.o | 109 | obj-$(CONFIG_CRASH_DUMP) += crash_dump.o |
110 | obj-$(CONFIG_JUMP_LABEL) += jump_label.o | ||
110 | 111 | ||
111 | ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) | 112 | ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) |
112 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is | 113 | # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is |
113 | # needed for x86 only. Why this used to be enabled for all architectures is beyond | 114 | # needed for x86 only. Why this used to be enabled for all architectures is beyond |
114 | # me. I suspect most platforms don't need this, but until we know that for sure | 115 | # me. I suspect most platforms don't need this, but until we know that for sure |
115 | # I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k | 116 | # I turn this off for IA-64 only. Andreas Schwab says it's also needed on m68k |
116 | # to get a correct value for the wait-channel (WCHAN in ps). --davidm | 117 | # to get a correct value for the wait-channel (WCHAN in ps). --davidm |
117 | CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer | 118 | CFLAGS_sched.o := $(PROFILING) -fno-omit-frame-pointer |
118 | endif | 119 | endif |
119 | 120 | ||
120 | $(obj)/configs.o: $(obj)/config_data.h | 121 | $(obj)/configs.o: $(obj)/config_data.h |
121 | 122 | ||
122 | # config_data.h contains the same information as ikconfig.h but gzipped. | 123 | # config_data.h contains the same information as ikconfig.h but gzipped. |
123 | # Info from config_data can be extracted from /proc/config* | 124 | # Info from config_data can be extracted from /proc/config* |
124 | targets += config_data.gz | 125 | targets += config_data.gz |
125 | $(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE | 126 | $(obj)/config_data.gz: $(KCONFIG_CONFIG) FORCE |
126 | $(call if_changed,gzip) | 127 | $(call if_changed,gzip) |
127 | 128 | ||
128 | filechk_ikconfiggz = (echo "static const char kernel_config_data[] __used = MAGIC_START"; cat $< | scripts/bin2c; echo "MAGIC_END;") | 129 | filechk_ikconfiggz = (echo "static const char kernel_config_data[] __used = MAGIC_START"; cat $< | scripts/bin2c; echo "MAGIC_END;") |
129 | targets += config_data.h | 130 | targets += config_data.h |
130 | $(obj)/config_data.h: $(obj)/config_data.gz FORCE | 131 | $(obj)/config_data.h: $(obj)/config_data.gz FORCE |
131 | $(call filechk,ikconfiggz) | 132 | $(call filechk,ikconfiggz) |
132 | 133 | ||
133 | $(obj)/time.o: $(obj)/timeconst.h | 134 | $(obj)/time.o: $(obj)/timeconst.h |
134 | 135 | ||
135 | quiet_cmd_timeconst = TIMEC $@ | 136 | quiet_cmd_timeconst = TIMEC $@ |
136 | cmd_timeconst = $(PERL) $< $(CONFIG_HZ) > $@ | 137 | cmd_timeconst = $(PERL) $< $(CONFIG_HZ) > $@ |
137 | targets += timeconst.h | 138 | targets += timeconst.h |
138 | $(obj)/timeconst.h: $(src)/timeconst.pl FORCE | 139 | $(obj)/timeconst.h: $(src)/timeconst.pl FORCE |
139 | $(call if_changed,timeconst) | 140 | $(call if_changed,timeconst) |
140 | 141 |
kernel/trace/Kconfig
1 | # | 1 | # |
2 | # Architectures that offer an FUNCTION_TRACER implementation should | 2 | # Architectures that offer an FUNCTION_TRACER implementation should |
3 | # select HAVE_FUNCTION_TRACER: | 3 | # select HAVE_FUNCTION_TRACER: |
4 | # | 4 | # |
5 | 5 | ||
6 | config USER_STACKTRACE_SUPPORT | 6 | config USER_STACKTRACE_SUPPORT |
7 | bool | 7 | bool |
8 | 8 | ||
9 | config NOP_TRACER | 9 | config NOP_TRACER |
10 | bool | 10 | bool |
11 | 11 | ||
12 | config HAVE_FTRACE_NMI_ENTER | 12 | config HAVE_FTRACE_NMI_ENTER |
13 | bool | 13 | bool |
14 | help | 14 | help |
15 | See Documentation/trace/ftrace-design.txt | 15 | See Documentation/trace/ftrace-design.txt |
16 | 16 | ||
17 | config HAVE_FUNCTION_TRACER | 17 | config HAVE_FUNCTION_TRACER |
18 | bool | 18 | bool |
19 | help | 19 | help |
20 | See Documentation/trace/ftrace-design.txt | 20 | See Documentation/trace/ftrace-design.txt |
21 | 21 | ||
22 | config HAVE_FUNCTION_GRAPH_TRACER | 22 | config HAVE_FUNCTION_GRAPH_TRACER |
23 | bool | 23 | bool |
24 | help | 24 | help |
25 | See Documentation/trace/ftrace-design.txt | 25 | See Documentation/trace/ftrace-design.txt |
26 | 26 | ||
27 | config HAVE_FUNCTION_GRAPH_FP_TEST | 27 | config HAVE_FUNCTION_GRAPH_FP_TEST |
28 | bool | 28 | bool |
29 | help | 29 | help |
30 | See Documentation/trace/ftrace-design.txt | 30 | See Documentation/trace/ftrace-design.txt |
31 | 31 | ||
32 | config HAVE_FUNCTION_TRACE_MCOUNT_TEST | 32 | config HAVE_FUNCTION_TRACE_MCOUNT_TEST |
33 | bool | 33 | bool |
34 | help | 34 | help |
35 | See Documentation/trace/ftrace-design.txt | 35 | See Documentation/trace/ftrace-design.txt |
36 | 36 | ||
37 | config HAVE_DYNAMIC_FTRACE | 37 | config HAVE_DYNAMIC_FTRACE |
38 | bool | 38 | bool |
39 | help | 39 | help |
40 | See Documentation/trace/ftrace-design.txt | 40 | See Documentation/trace/ftrace-design.txt |
41 | 41 | ||
42 | config HAVE_FTRACE_MCOUNT_RECORD | 42 | config HAVE_FTRACE_MCOUNT_RECORD |
43 | bool | 43 | bool |
44 | help | 44 | help |
45 | See Documentation/trace/ftrace-design.txt | 45 | See Documentation/trace/ftrace-design.txt |
46 | 46 | ||
47 | config HAVE_SYSCALL_TRACEPOINTS | 47 | config HAVE_SYSCALL_TRACEPOINTS |
48 | bool | 48 | bool |
49 | help | 49 | help |
50 | See Documentation/trace/ftrace-design.txt | 50 | See Documentation/trace/ftrace-design.txt |
51 | 51 | ||
52 | config HAVE_C_RECORDMCOUNT | 52 | config HAVE_C_RECORDMCOUNT |
53 | bool | 53 | bool |
54 | help | 54 | help |
55 | C version of recordmcount available? | 55 | C version of recordmcount available? |
56 | 56 | ||
57 | config TRACER_MAX_TRACE | 57 | config TRACER_MAX_TRACE |
58 | bool | 58 | bool |
59 | 59 | ||
60 | config RING_BUFFER | 60 | config RING_BUFFER |
61 | bool | 61 | bool |
62 | 62 | ||
63 | config FTRACE_NMI_ENTER | 63 | config FTRACE_NMI_ENTER |
64 | bool | 64 | bool |
65 | depends on HAVE_FTRACE_NMI_ENTER | 65 | depends on HAVE_FTRACE_NMI_ENTER |
66 | default y | 66 | default y |
67 | 67 | ||
68 | config EVENT_TRACING | 68 | config EVENT_TRACING |
69 | select CONTEXT_SWITCH_TRACER | 69 | select CONTEXT_SWITCH_TRACER |
70 | bool | 70 | bool |
71 | 71 | ||
72 | config EVENT_POWER_TRACING_DEPRECATED | 72 | config EVENT_POWER_TRACING_DEPRECATED |
73 | depends on EVENT_TRACING | 73 | depends on EVENT_TRACING |
74 | bool "Deprecated power event trace API, to be removed" | 74 | bool "Deprecated power event trace API, to be removed" |
75 | default y | 75 | default y |
76 | help | 76 | help |
77 | Provides old power event types: | 77 | Provides old power event types: |
78 | C-state/idle accounting events: | 78 | C-state/idle accounting events: |
79 | power:power_start | 79 | power:power_start |
80 | power:power_end | 80 | power:power_end |
81 | and old cpufreq accounting event: | 81 | and old cpufreq accounting event: |
82 | power:power_frequency | 82 | power:power_frequency |
83 | This is for userspace compatibility | 83 | This is for userspace compatibility |
84 | and will vanish after 5 kernel iterations, | 84 | and will vanish after 5 kernel iterations, |
85 | namely 2.6.41. | 85 | namely 3.1. |
86 | 86 | ||
87 | config CONTEXT_SWITCH_TRACER | 87 | config CONTEXT_SWITCH_TRACER |
88 | bool | 88 | bool |
89 | 89 | ||
90 | config RING_BUFFER_ALLOW_SWAP | 90 | config RING_BUFFER_ALLOW_SWAP |
91 | bool | 91 | bool |
92 | help | 92 | help |
93 | Allow the use of ring_buffer_swap_cpu. | 93 | Allow the use of ring_buffer_swap_cpu. |
94 | Adds a very slight overhead to tracing when enabled. | 94 | Adds a very slight overhead to tracing when enabled. |
95 | 95 | ||
96 | # All tracer options should select GENERIC_TRACER. For those options that are | 96 | # All tracer options should select GENERIC_TRACER. For those options that are |
97 | # enabled by all tracers (context switch and event tracer) they select TRACING. | 97 | # enabled by all tracers (context switch and event tracer) they select TRACING. |
98 | # This allows those options to appear when no other tracer is selected. But the | 98 | # This allows those options to appear when no other tracer is selected. But the |
99 | # options do not appear when something else selects it. We need the two options | 99 | # options do not appear when something else selects it. We need the two options |
100 | # GENERIC_TRACER and TRACING to avoid circular dependencies to accomplish the | 100 | # GENERIC_TRACER and TRACING to avoid circular dependencies to accomplish the |
101 | # hiding of the automatic options. | 101 | # hiding of the automatic options. |
102 | 102 | ||
103 | config TRACING | 103 | config TRACING |
104 | bool | 104 | bool |
105 | select DEBUG_FS | 105 | select DEBUG_FS |
106 | select RING_BUFFER | 106 | select RING_BUFFER |
107 | select STACKTRACE if STACKTRACE_SUPPORT | 107 | select STACKTRACE if STACKTRACE_SUPPORT |
108 | select TRACEPOINTS | 108 | select TRACEPOINTS |
109 | select NOP_TRACER | 109 | select NOP_TRACER |
110 | select BINARY_PRINTF | 110 | select BINARY_PRINTF |
111 | select EVENT_TRACING | 111 | select EVENT_TRACING |
112 | 112 | ||
113 | config GENERIC_TRACER | 113 | config GENERIC_TRACER |
114 | bool | 114 | bool |
115 | select TRACING | 115 | select TRACING |
116 | 116 | ||
117 | # | 117 | # |
118 | # Minimum requirements an architecture has to meet for us to | 118 | # Minimum requirements an architecture has to meet for us to |
119 | # be able to offer generic tracing facilities: | 119 | # be able to offer generic tracing facilities: |
120 | # | 120 | # |
121 | config TRACING_SUPPORT | 121 | config TRACING_SUPPORT |
122 | bool | 122 | bool |
123 | # PPC32 has no irqflags tracing support, but it can use most of the | 123 | # PPC32 has no irqflags tracing support, but it can use most of the |
124 | # tracers anyway, they were tested to build and work. Note that new | 124 | # tracers anyway, they were tested to build and work. Note that new |
125 | # exceptions to this list aren't welcomed, better implement the | 125 | # exceptions to this list aren't welcomed, better implement the |
126 | # irqflags tracing for your architecture. | 126 | # irqflags tracing for your architecture. |
127 | depends on TRACE_IRQFLAGS_SUPPORT || PPC32 | 127 | depends on TRACE_IRQFLAGS_SUPPORT || PPC32 |
128 | depends on STACKTRACE_SUPPORT | 128 | depends on STACKTRACE_SUPPORT |
129 | default y | 129 | default y |
130 | 130 | ||
131 | if TRACING_SUPPORT | 131 | if TRACING_SUPPORT |
132 | 132 | ||
133 | menuconfig FTRACE | 133 | menuconfig FTRACE |
134 | bool "Tracers" | 134 | bool "Tracers" |
135 | default y if DEBUG_KERNEL | 135 | default y if DEBUG_KERNEL |
136 | help | 136 | help |
137 | Enable the kernel tracing infrastructure. | 137 | Enable the kernel tracing infrastructure. |
138 | 138 | ||
139 | if FTRACE | 139 | if FTRACE |
140 | 140 | ||
141 | config FUNCTION_TRACER | 141 | config FUNCTION_TRACER |
142 | bool "Kernel Function Tracer" | 142 | bool "Kernel Function Tracer" |
143 | depends on HAVE_FUNCTION_TRACER | 143 | depends on HAVE_FUNCTION_TRACER |
144 | select FRAME_POINTER if !ARM_UNWIND && !S390 && !MICROBLAZE | 144 | select FRAME_POINTER if !ARM_UNWIND && !S390 && !MICROBLAZE |
145 | select KALLSYMS | 145 | select KALLSYMS |
146 | select GENERIC_TRACER | 146 | select GENERIC_TRACER |
147 | select CONTEXT_SWITCH_TRACER | 147 | select CONTEXT_SWITCH_TRACER |
148 | help | 148 | help |
149 | Enable the kernel to trace every kernel function. This is done | 149 | Enable the kernel to trace every kernel function. This is done |
150 | by using a compiler feature to insert a small, 5-byte No-Operation | 150 | by using a compiler feature to insert a small, 5-byte No-Operation |
151 | instruction at the beginning of every kernel function, which NOP | 151 | instruction at the beginning of every kernel function, which NOP |
152 | sequence is then dynamically patched into a tracer call when | 152 | sequence is then dynamically patched into a tracer call when |
153 | tracing is enabled by the administrator. If it's runtime disabled | 153 | tracing is enabled by the administrator. If it's runtime disabled |
154 | (the bootup default), then the overhead of the instructions is very | 154 | (the bootup default), then the overhead of the instructions is very |
155 | small and not measurable even in micro-benchmarks. | 155 | small and not measurable even in micro-benchmarks. |
156 | 156 | ||
157 | config FUNCTION_GRAPH_TRACER | 157 | config FUNCTION_GRAPH_TRACER |
158 | bool "Kernel Function Graph Tracer" | 158 | bool "Kernel Function Graph Tracer" |
159 | depends on HAVE_FUNCTION_GRAPH_TRACER | 159 | depends on HAVE_FUNCTION_GRAPH_TRACER |
160 | depends on FUNCTION_TRACER | 160 | depends on FUNCTION_TRACER |
161 | depends on !X86_32 || !CC_OPTIMIZE_FOR_SIZE | 161 | depends on !X86_32 || !CC_OPTIMIZE_FOR_SIZE |
162 | default y | 162 | default y |
163 | help | 163 | help |
164 | Enable the kernel to trace a function at both its return | 164 | Enable the kernel to trace a function at both its return |
165 | and its entry. | 165 | and its entry. |
166 | Its first purpose is to trace the duration of functions and | 166 | Its first purpose is to trace the duration of functions and |
167 | draw a call graph for each thread with some information like | 167 | draw a call graph for each thread with some information like |
168 | the return value. This is done by setting the current return | 168 | the return value. This is done by setting the current return |
169 | address on the current task structure into a stack of calls. | 169 | address on the current task structure into a stack of calls. |
170 | 170 | ||
171 | 171 | ||
172 | config IRQSOFF_TRACER | 172 | config IRQSOFF_TRACER |
173 | bool "Interrupts-off Latency Tracer" | 173 | bool "Interrupts-off Latency Tracer" |
174 | default n | 174 | default n |
175 | depends on TRACE_IRQFLAGS_SUPPORT | 175 | depends on TRACE_IRQFLAGS_SUPPORT |
176 | depends on !ARCH_USES_GETTIMEOFFSET | 176 | depends on !ARCH_USES_GETTIMEOFFSET |
177 | select TRACE_IRQFLAGS | 177 | select TRACE_IRQFLAGS |
178 | select GENERIC_TRACER | 178 | select GENERIC_TRACER |
179 | select TRACER_MAX_TRACE | 179 | select TRACER_MAX_TRACE |
180 | select RING_BUFFER_ALLOW_SWAP | 180 | select RING_BUFFER_ALLOW_SWAP |
181 | help | 181 | help |
182 | This option measures the time spent in irqs-off critical | 182 | This option measures the time spent in irqs-off critical |
183 | sections, with microsecond accuracy. | 183 | sections, with microsecond accuracy. |
184 | 184 | ||
185 | The default measurement method is a maximum search, which is | 185 | The default measurement method is a maximum search, which is |
186 | disabled by default and can be runtime (re-)started | 186 | disabled by default and can be runtime (re-)started |
187 | via: | 187 | via: |
188 | 188 | ||
189 | echo 0 > /sys/kernel/debug/tracing/tracing_max_latency | 189 | echo 0 > /sys/kernel/debug/tracing/tracing_max_latency |
190 | 190 | ||
191 | (Note that kernel size and overhead increase with this option | 191 | (Note that kernel size and overhead increase with this option |
192 | enabled. This option and the preempt-off timing option can be | 192 | enabled. This option and the preempt-off timing option can be |
193 | used together or separately.) | 193 | used together or separately.) |
194 | 194 | ||
195 | config PREEMPT_TRACER | 195 | config PREEMPT_TRACER |
196 | bool "Preemption-off Latency Tracer" | 196 | bool "Preemption-off Latency Tracer" |
197 | default n | 197 | default n |
198 | depends on !ARCH_USES_GETTIMEOFFSET | 198 | depends on !ARCH_USES_GETTIMEOFFSET |
199 | depends on PREEMPT | 199 | depends on PREEMPT |
200 | select GENERIC_TRACER | 200 | select GENERIC_TRACER |
201 | select TRACER_MAX_TRACE | 201 | select TRACER_MAX_TRACE |
202 | select RING_BUFFER_ALLOW_SWAP | 202 | select RING_BUFFER_ALLOW_SWAP |
203 | help | 203 | help |
204 | This option measures the time spent in preemption-off critical | 204 | This option measures the time spent in preemption-off critical |
205 | sections, with microsecond accuracy. | 205 | sections, with microsecond accuracy. |
206 | 206 | ||
207 | The default measurement method is a maximum search, which is | 207 | The default measurement method is a maximum search, which is |
208 | disabled by default and can be runtime (re-)started | 208 | disabled by default and can be runtime (re-)started |
209 | via: | 209 | via: |
210 | 210 | ||
211 | echo 0 > /sys/kernel/debug/tracing/tracing_max_latency | 211 | echo 0 > /sys/kernel/debug/tracing/tracing_max_latency |
212 | 212 | ||
213 | (Note that kernel size and overhead increase with this option | 213 | (Note that kernel size and overhead increase with this option |
214 | enabled. This option and the irqs-off timing option can be | 214 | enabled. This option and the irqs-off timing option can be |
215 | used together or separately.) | 215 | used together or separately.) |
216 | 216 | ||
217 | config SCHED_TRACER | 217 | config SCHED_TRACER |
218 | bool "Scheduling Latency Tracer" | 218 | bool "Scheduling Latency Tracer" |
219 | select GENERIC_TRACER | 219 | select GENERIC_TRACER |
220 | select CONTEXT_SWITCH_TRACER | 220 | select CONTEXT_SWITCH_TRACER |
221 | select TRACER_MAX_TRACE | 221 | select TRACER_MAX_TRACE |
222 | help | 222 | help |
223 | This tracer tracks the latency of the highest priority task | 223 | This tracer tracks the latency of the highest priority task |
224 | to be scheduled in, starting from the point it has woken up. | 224 | to be scheduled in, starting from the point it has woken up. |
225 | 225 | ||
226 | config ENABLE_DEFAULT_TRACERS | 226 | config ENABLE_DEFAULT_TRACERS |
227 | bool "Trace process context switches and events" | 227 | bool "Trace process context switches and events" |
228 | depends on !GENERIC_TRACER | 228 | depends on !GENERIC_TRACER |
229 | select TRACING | 229 | select TRACING |
230 | help | 230 | help |
231 | This tracer hooks to various trace points in the kernel, | 231 | This tracer hooks to various trace points in the kernel, |
232 | allowing the user to pick and choose which trace point they | 232 | allowing the user to pick and choose which trace point they |
233 | want to trace. It also includes the sched_switch tracer plugin. | 233 | want to trace. It also includes the sched_switch tracer plugin. |
234 | 234 | ||
235 | config FTRACE_SYSCALLS | 235 | config FTRACE_SYSCALLS |
236 | bool "Trace syscalls" | 236 | bool "Trace syscalls" |
237 | depends on HAVE_SYSCALL_TRACEPOINTS | 237 | depends on HAVE_SYSCALL_TRACEPOINTS |
238 | select GENERIC_TRACER | 238 | select GENERIC_TRACER |
239 | select KALLSYMS | 239 | select KALLSYMS |
240 | help | 240 | help |
241 | Basic tracer to catch the syscall entry and exit events. | 241 | Basic tracer to catch the syscall entry and exit events. |
242 | 242 | ||
243 | config TRACE_BRANCH_PROFILING | 243 | config TRACE_BRANCH_PROFILING |
244 | bool | 244 | bool |
245 | select GENERIC_TRACER | 245 | select GENERIC_TRACER |
246 | 246 | ||
247 | choice | 247 | choice |
248 | prompt "Branch Profiling" | 248 | prompt "Branch Profiling" |
249 | default BRANCH_PROFILE_NONE | 249 | default BRANCH_PROFILE_NONE |
250 | help | 250 | help |
251 | The branch profiling is a software profiler. It will add hooks | 251 | The branch profiling is a software profiler. It will add hooks |
252 | into the C conditionals to test which path a branch takes. | 252 | into the C conditionals to test which path a branch takes. |
253 | 253 | ||
254 | The likely/unlikely profiler only looks at the conditions that | 254 | The likely/unlikely profiler only looks at the conditions that |
255 | are annotated with a likely or unlikely macro. | 255 | are annotated with a likely or unlikely macro. |
256 | 256 | ||
257 | The "all branch" profiler will profile every if-statement in the | 257 | The "all branch" profiler will profile every if-statement in the |
258 | kernel. This profiler will also enable the likely/unlikely | 258 | kernel. This profiler will also enable the likely/unlikely |
259 | profiler. | 259 | profiler. |
260 | 260 | ||
261 | Either of the above profilers adds a bit of overhead to the system. | 261 | Either of the above profilers adds a bit of overhead to the system. |
262 | If unsure, choose "No branch profiling". | 262 | If unsure, choose "No branch profiling". |
263 | 263 | ||
264 | config BRANCH_PROFILE_NONE | 264 | config BRANCH_PROFILE_NONE |
265 | bool "No branch profiling" | 265 | bool "No branch profiling" |
266 | help | 266 | help |
267 | No branch profiling. Branch profiling adds a bit of overhead. | 267 | No branch profiling. Branch profiling adds a bit of overhead. |
268 | Only enable it if you want to analyse the branching behavior. | 268 | Only enable it if you want to analyse the branching behavior. |
269 | Otherwise keep it disabled. | 269 | Otherwise keep it disabled. |
270 | 270 | ||
271 | config PROFILE_ANNOTATED_BRANCHES | 271 | config PROFILE_ANNOTATED_BRANCHES |
272 | bool "Trace likely/unlikely profiler" | 272 | bool "Trace likely/unlikely profiler" |
273 | select TRACE_BRANCH_PROFILING | 273 | select TRACE_BRANCH_PROFILING |
274 | help | 274 | help |
275 | This tracer profiles all the the likely and unlikely macros | 275 | This tracer profiles all the the likely and unlikely macros |
276 | in the kernel. It will display the results in: | 276 | in the kernel. It will display the results in: |
277 | 277 | ||
278 | /sys/kernel/debug/tracing/trace_stat/branch_annotated | 278 | /sys/kernel/debug/tracing/trace_stat/branch_annotated |
279 | 279 | ||
280 | Note: this will add a significant overhead; only turn this | 280 | Note: this will add a significant overhead; only turn this |
281 | on if you need to profile the system's use of these macros. | 281 | on if you need to profile the system's use of these macros. |
282 | 282 | ||
283 | config PROFILE_ALL_BRANCHES | 283 | config PROFILE_ALL_BRANCHES |
284 | bool "Profile all if conditionals" | 284 | bool "Profile all if conditionals" |
285 | select TRACE_BRANCH_PROFILING | 285 | select TRACE_BRANCH_PROFILING |
286 | help | 286 | help |
287 | This tracer profiles all branch conditions. Every if () | 287 | This tracer profiles all branch conditions. Every if () |
288 | taken in the kernel is recorded whether it hit or miss. | 288 | taken in the kernel is recorded whether it hit or miss. |
289 | The results will be displayed in: | 289 | The results will be displayed in: |
290 | 290 | ||
291 | /sys/kernel/debug/tracing/trace_stat/branch_all | 291 | /sys/kernel/debug/tracing/trace_stat/branch_all |
292 | 292 | ||
293 | This option also enables the likely/unlikely profiler. | 293 | This option also enables the likely/unlikely profiler. |
294 | 294 | ||
295 | This configuration, when enabled, will impose a great overhead | 295 | This configuration, when enabled, will impose a great overhead |
296 | on the system. This should only be enabled when the system | 296 | on the system. This should only be enabled when the system |
297 | is to be analyzed in much detail. | 297 | is to be analyzed in much detail. |
298 | endchoice | 298 | endchoice |
299 | 299 | ||
300 | config TRACING_BRANCHES | 300 | config TRACING_BRANCHES |
301 | bool | 301 | bool |
302 | help | 302 | help |
303 | Selected by tracers that will trace the likely and unlikely | 303 | Selected by tracers that will trace the likely and unlikely |
304 | conditions. This prevents the tracers themselves from being | 304 | conditions. This prevents the tracers themselves from being |
305 | profiled. Profiling the tracing infrastructure can only happen | 305 | profiled. Profiling the tracing infrastructure can only happen |
306 | when the likelys and unlikelys are not being traced. | 306 | when the likelys and unlikelys are not being traced. |
307 | 307 | ||
308 | config BRANCH_TRACER | 308 | config BRANCH_TRACER |
309 | bool "Trace likely/unlikely instances" | 309 | bool "Trace likely/unlikely instances" |
310 | depends on TRACE_BRANCH_PROFILING | 310 | depends on TRACE_BRANCH_PROFILING |
311 | select TRACING_BRANCHES | 311 | select TRACING_BRANCHES |
312 | help | 312 | help |
313 | This traces the events of likely and unlikely condition | 313 | This traces the events of likely and unlikely condition |
314 | calls in the kernel. The difference between this and the | 314 | calls in the kernel. The difference between this and the |
315 | "Trace likely/unlikely profiler" is that this is not a | 315 | "Trace likely/unlikely profiler" is that this is not a |
316 | histogram of the callers, but actually places the calling | 316 | histogram of the callers, but actually places the calling |
317 | events into a running trace buffer to see when and where the | 317 | events into a running trace buffer to see when and where the |
318 | events happened, as well as their results. | 318 | events happened, as well as their results. |
319 | 319 | ||
320 | Say N if unsure. | 320 | Say N if unsure. |
321 | 321 | ||
322 | config STACK_TRACER | 322 | config STACK_TRACER |
323 | bool "Trace max stack" | 323 | bool "Trace max stack" |
324 | depends on HAVE_FUNCTION_TRACER | 324 | depends on HAVE_FUNCTION_TRACER |
325 | select FUNCTION_TRACER | 325 | select FUNCTION_TRACER |
326 | select STACKTRACE | 326 | select STACKTRACE |
327 | select KALLSYMS | 327 | select KALLSYMS |
328 | help | 328 | help |
329 | This special tracer records the maximum stack footprint of the | 329 | This special tracer records the maximum stack footprint of the |
330 | kernel and displays it in /sys/kernel/debug/tracing/stack_trace. | 330 | kernel and displays it in /sys/kernel/debug/tracing/stack_trace. |
331 | 331 | ||
332 | This tracer works by hooking into every function call that the | 332 | This tracer works by hooking into every function call that the |
333 | kernel executes, and keeping a maximum stack depth value and | 333 | kernel executes, and keeping a maximum stack depth value and |
334 | stack-trace saved. If this is configured with DYNAMIC_FTRACE | 334 | stack-trace saved. If this is configured with DYNAMIC_FTRACE |
335 | then it will not have any overhead while the stack tracer | 335 | then it will not have any overhead while the stack tracer |
336 | is disabled. | 336 | is disabled. |
337 | 337 | ||
338 | To enable the stack tracer on bootup, pass in 'stacktrace' | 338 | To enable the stack tracer on bootup, pass in 'stacktrace' |
339 | on the kernel command line. | 339 | on the kernel command line. |
340 | 340 | ||
341 | The stack tracer can also be enabled or disabled via the | 341 | The stack tracer can also be enabled or disabled via the |
342 | sysctl kernel.stack_tracer_enabled | 342 | sysctl kernel.stack_tracer_enabled |
343 | 343 | ||
344 | Say N if unsure. | 344 | Say N if unsure. |
345 | 345 | ||
346 | config BLK_DEV_IO_TRACE | 346 | config BLK_DEV_IO_TRACE |
347 | bool "Support for tracing block IO actions" | 347 | bool "Support for tracing block IO actions" |
348 | depends on SYSFS | 348 | depends on SYSFS |
349 | depends on BLOCK | 349 | depends on BLOCK |
350 | select RELAY | 350 | select RELAY |
351 | select DEBUG_FS | 351 | select DEBUG_FS |
352 | select TRACEPOINTS | 352 | select TRACEPOINTS |
353 | select GENERIC_TRACER | 353 | select GENERIC_TRACER |
354 | select STACKTRACE | 354 | select STACKTRACE |
355 | help | 355 | help |
356 | Say Y here if you want to be able to trace the block layer actions | 356 | Say Y here if you want to be able to trace the block layer actions |
357 | on a given queue. Tracing allows you to see any traffic happening | 357 | on a given queue. Tracing allows you to see any traffic happening |
358 | on a block device queue. For more information (and the userspace | 358 | on a block device queue. For more information (and the userspace |
359 | support tools needed), fetch the blktrace tools from: | 359 | support tools needed), fetch the blktrace tools from: |
360 | 360 | ||
361 | git://git.kernel.dk/blktrace.git | 361 | git://git.kernel.dk/blktrace.git |
362 | 362 | ||
363 | Tracing also is possible using the ftrace interface, e.g.: | 363 | Tracing also is possible using the ftrace interface, e.g.: |
364 | 364 | ||
365 | echo 1 > /sys/block/sda/sda1/trace/enable | 365 | echo 1 > /sys/block/sda/sda1/trace/enable |
366 | echo blk > /sys/kernel/debug/tracing/current_tracer | 366 | echo blk > /sys/kernel/debug/tracing/current_tracer |
367 | cat /sys/kernel/debug/tracing/trace_pipe | 367 | cat /sys/kernel/debug/tracing/trace_pipe |
368 | 368 | ||
369 | If unsure, say N. | 369 | If unsure, say N. |
370 | 370 | ||
371 | config KPROBE_EVENT | 371 | config KPROBE_EVENT |
372 | depends on KPROBES | 372 | depends on KPROBES |
373 | depends on HAVE_REGS_AND_STACK_ACCESS_API | 373 | depends on HAVE_REGS_AND_STACK_ACCESS_API |
374 | bool "Enable kprobes-based dynamic events" | 374 | bool "Enable kprobes-based dynamic events" |
375 | select TRACING | 375 | select TRACING |
376 | default y | 376 | default y |
377 | help | 377 | help |
378 | This allows the user to add tracing events (similar to tracepoints) | 378 | This allows the user to add tracing events (similar to tracepoints) |
379 | on the fly via the ftrace interface. See | 379 | on the fly via the ftrace interface. See |
380 | Documentation/trace/kprobetrace.txt for more details. | 380 | Documentation/trace/kprobetrace.txt for more details. |
381 | 381 | ||
382 | Those events can be inserted wherever kprobes can probe, and record | 382 | Those events can be inserted wherever kprobes can probe, and record |
383 | various register and memory values. | 383 | various register and memory values. |
384 | 384 | ||
385 | This option is also required by perf-probe subcommand of perf tools. | 385 | This option is also required by perf-probe subcommand of perf tools. |
386 | If you want to use perf tools, this option is strongly recommended. | 386 | If you want to use perf tools, this option is strongly recommended. |
387 | 387 | ||
388 | config DYNAMIC_FTRACE | 388 | config DYNAMIC_FTRACE |
389 | bool "enable/disable ftrace tracepoints dynamically" | 389 | bool "enable/disable ftrace tracepoints dynamically" |
390 | depends on FUNCTION_TRACER | 390 | depends on FUNCTION_TRACER |
391 | depends on HAVE_DYNAMIC_FTRACE | 391 | depends on HAVE_DYNAMIC_FTRACE |
392 | default y | 392 | default y |
393 | help | 393 | help |
394 | This option will modify all the calls to ftrace dynamically | 394 | This option will modify all the calls to ftrace dynamically |
395 | (will patch them out of the binary image and replace them | 395 | (will patch them out of the binary image and replace them |
396 | with a No-Op instruction) as they are called. A table is | 396 | with a No-Op instruction) as they are called. A table is |
397 | created to dynamically enable them again. | 397 | created to dynamically enable them again. |
398 | 398 | ||
399 | This way a CONFIG_FUNCTION_TRACER kernel is slightly larger, but | 399 | This way a CONFIG_FUNCTION_TRACER kernel is slightly larger, but |
400 | otherwise has native performance as long as no tracing is active. | 400 | otherwise has native performance as long as no tracing is active. |
401 | 401 | ||
402 | The changes to the code are done by a kernel thread that | 402 | The changes to the code are done by a kernel thread that |
403 | wakes up once a second and checks to see if any ftrace calls | 403 | wakes up once a second and checks to see if any ftrace calls |
404 | were made. If so, it runs stop_machine (stops all CPUS) | 404 | were made. If so, it runs stop_machine (stops all CPUS) |
405 | and modifies the code to jump over the call to ftrace. | 405 | and modifies the code to jump over the call to ftrace. |
406 | 406 | ||
407 | config FUNCTION_PROFILER | 407 | config FUNCTION_PROFILER |
408 | bool "Kernel function profiler" | 408 | bool "Kernel function profiler" |
409 | depends on FUNCTION_TRACER | 409 | depends on FUNCTION_TRACER |
410 | default n | 410 | default n |
411 | help | 411 | help |
412 | This option enables the kernel function profiler. A file is created | 412 | This option enables the kernel function profiler. A file is created |
413 | in debugfs called function_profile_enabled which defaults to zero. | 413 | in debugfs called function_profile_enabled which defaults to zero. |
414 | When a 1 is echoed into this file profiling begins, and when a | 414 | When a 1 is echoed into this file profiling begins, and when a |
415 | zero is entered, profiling stops. A "functions" file is created in | 415 | zero is entered, profiling stops. A "functions" file is created in |
416 | the trace_stats directory; this file shows the list of functions that | 416 | the trace_stats directory; this file shows the list of functions that |
417 | have been hit and their counters. | 417 | have been hit and their counters. |
418 | 418 | ||
419 | If in doubt, say N. | 419 | If in doubt, say N. |
420 | 420 | ||
421 | config FTRACE_MCOUNT_RECORD | 421 | config FTRACE_MCOUNT_RECORD |
422 | def_bool y | 422 | def_bool y |
423 | depends on DYNAMIC_FTRACE | 423 | depends on DYNAMIC_FTRACE |
424 | depends on HAVE_FTRACE_MCOUNT_RECORD | 424 | depends on HAVE_FTRACE_MCOUNT_RECORD |
425 | 425 | ||
426 | config FTRACE_SELFTEST | 426 | config FTRACE_SELFTEST |
427 | bool | 427 | bool |
428 | 428 | ||
429 | config FTRACE_STARTUP_TEST | 429 | config FTRACE_STARTUP_TEST |
430 | bool "Perform a startup test on ftrace" | 430 | bool "Perform a startup test on ftrace" |
431 | depends on GENERIC_TRACER | 431 | depends on GENERIC_TRACER |
432 | select FTRACE_SELFTEST | 432 | select FTRACE_SELFTEST |
433 | help | 433 | help |
434 | This option performs a series of startup tests on ftrace. On bootup | 434 | This option performs a series of startup tests on ftrace. On bootup |
435 | a series of tests are made to verify that the tracer is | 435 | a series of tests are made to verify that the tracer is |
436 | functioning properly. It will do tests on all the configured | 436 | functioning properly. It will do tests on all the configured |
437 | tracers of ftrace. | 437 | tracers of ftrace. |
438 | 438 | ||
439 | config EVENT_TRACE_TEST_SYSCALLS | 439 | config EVENT_TRACE_TEST_SYSCALLS |
440 | bool "Run selftest on syscall events" | 440 | bool "Run selftest on syscall events" |
441 | depends on FTRACE_STARTUP_TEST | 441 | depends on FTRACE_STARTUP_TEST |
442 | help | 442 | help |
443 | This option will also enable testing every syscall event. | 443 | This option will also enable testing every syscall event. |
444 | It only enables the event and disables it and runs various loads | 444 | It only enables the event and disables it and runs various loads |
445 | with the event enabled. This adds a bit more time for kernel boot | 445 | with the event enabled. This adds a bit more time for kernel boot |
446 | up since it runs this on every system call defined. | 446 | up since it runs this on every system call defined. |
447 | 447 | ||
448 | TBD - enable a way to actually call the syscalls as we test their | 448 | TBD - enable a way to actually call the syscalls as we test their |
449 | events | 449 | events |
450 | 450 | ||
451 | config MMIOTRACE | 451 | config MMIOTRACE |
452 | bool "Memory mapped IO tracing" | 452 | bool "Memory mapped IO tracing" |
453 | depends on HAVE_MMIOTRACE_SUPPORT && PCI | 453 | depends on HAVE_MMIOTRACE_SUPPORT && PCI |
454 | select GENERIC_TRACER | 454 | select GENERIC_TRACER |
455 | help | 455 | help |
456 | Mmiotrace traces Memory Mapped I/O access and is meant for | 456 | Mmiotrace traces Memory Mapped I/O access and is meant for |
457 | debugging and reverse engineering. It is called from the ioremap | 457 | debugging and reverse engineering. It is called from the ioremap |
458 | implementation and works via page faults. Tracing is disabled by | 458 | implementation and works via page faults. Tracing is disabled by |
459 | default and can be enabled at run-time. | 459 | default and can be enabled at run-time. |
460 | 460 | ||
461 | See Documentation/trace/mmiotrace.txt. | 461 | See Documentation/trace/mmiotrace.txt. |
462 | If you are not helping to develop drivers, say N. | 462 | If you are not helping to develop drivers, say N. |
463 | 463 | ||
464 | config MMIOTRACE_TEST | 464 | config MMIOTRACE_TEST |
465 | tristate "Test module for mmiotrace" | 465 | tristate "Test module for mmiotrace" |
466 | depends on MMIOTRACE && m | 466 | depends on MMIOTRACE && m |
467 | help | 467 | help |
468 | This is a dumb module for testing mmiotrace. It is very dangerous | 468 | This is a dumb module for testing mmiotrace. It is very dangerous |
469 | as it will write garbage to IO memory starting at a given address. | 469 | as it will write garbage to IO memory starting at a given address. |
470 | However, it should be safe to use on e.g. unused portion of VRAM. | 470 | However, it should be safe to use on e.g. unused portion of VRAM. |
471 | 471 | ||
472 | Say N, unless you absolutely know what you are doing. | 472 | Say N, unless you absolutely know what you are doing. |
473 | 473 | ||
474 | config RING_BUFFER_BENCHMARK | 474 | config RING_BUFFER_BENCHMARK |
475 | tristate "Ring buffer benchmark stress tester" | 475 | tristate "Ring buffer benchmark stress tester" |
476 | depends on RING_BUFFER | 476 | depends on RING_BUFFER |
477 | help | 477 | help |
478 | This option creates a test to stress the ring buffer and benchmark it. | 478 | This option creates a test to stress the ring buffer and benchmark it. |
479 | It creates its own ring buffer such that it will not interfere with | 479 | It creates its own ring buffer such that it will not interfere with |
480 | any other users of the ring buffer (such as ftrace). It then creates | 480 | any other users of the ring buffer (such as ftrace). It then creates |
481 | a producer and consumer that will run for 10 seconds and sleep for | 481 | a producer and consumer that will run for 10 seconds and sleep for |
482 | 10 seconds. Each interval it will print out the number of events | 482 | 10 seconds. Each interval it will print out the number of events |
483 | it recorded and give a rough estimate of how long each iteration took. | 483 | it recorded and give a rough estimate of how long each iteration took. |
484 | 484 | ||
485 | It does not disable interrupts or raise its priority, so it may be | 485 | It does not disable interrupts or raise its priority, so it may be |
486 | affected by processes that are running. | 486 | affected by processes that are running. |
487 | 487 | ||
488 | If unsure, say N. | 488 | If unsure, say N. |
489 | 489 | ||
490 | endif # FTRACE | 490 | endif # FTRACE |
491 | 491 | ||
492 | endif # TRACING_SUPPORT | 492 | endif # TRACING_SUPPORT |
493 | 493 | ||
494 | 494 |
tools/perf/Makefile
1 | ifeq ("$(origin O)", "command line") | 1 | ifeq ("$(origin O)", "command line") |
2 | OUTPUT := $(O)/ | 2 | OUTPUT := $(O)/ |
3 | endif | 3 | endif |
4 | 4 | ||
5 | # The default target of this Makefile is... | 5 | # The default target of this Makefile is... |
6 | all: | 6 | all: |
7 | 7 | ||
8 | include config/utilities.mak | 8 | include config/utilities.mak |
9 | 9 | ||
10 | ifneq ($(OUTPUT),) | 10 | ifneq ($(OUTPUT),) |
11 | # check that the output directory actually exists | 11 | # check that the output directory actually exists |
12 | OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) | 12 | OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) |
13 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) | 13 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) |
14 | endif | 14 | endif |
15 | 15 | ||
16 | # Define V to have a more verbose compile. | 16 | # Define V to have a more verbose compile. |
17 | # | 17 | # |
18 | # Define PYTHON to point to the python binary if the default | 18 | # Define PYTHON to point to the python binary if the default |
19 | # `python' is not correct; for example: PYTHON=python2 | 19 | # `python' is not correct; for example: PYTHON=python2 |
20 | # | 20 | # |
21 | # Define PYTHON_CONFIG to point to the python-config binary if | 21 | # Define PYTHON_CONFIG to point to the python-config binary if |
22 | # the default `$(PYTHON)-config' is not correct. | 22 | # the default `$(PYTHON)-config' is not correct. |
23 | # | 23 | # |
24 | # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 | 24 | # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 |
25 | # | 25 | # |
26 | # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. | 26 | # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72. |
27 | # | 27 | # |
28 | # Define LDFLAGS=-static to build a static binary. | 28 | # Define LDFLAGS=-static to build a static binary. |
29 | # | 29 | # |
30 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. | 30 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. |
31 | # | 31 | # |
32 | # Define NO_DWARF if you do not want debug-info analysis feature at all. | 32 | # Define NO_DWARF if you do not want debug-info analysis feature at all. |
33 | 33 | ||
34 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 34 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
35 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) | 35 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) |
36 | -include $(OUTPUT)PERF-VERSION-FILE | 36 | -include $(OUTPUT)PERF-VERSION-FILE |
37 | 37 | ||
38 | uname_M := $(shell uname -m 2>/dev/null || echo not) | 38 | uname_M := $(shell uname -m 2>/dev/null || echo not) |
39 | 39 | ||
40 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ | 40 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ |
41 | -e s/arm.*/arm/ -e s/sa110/arm/ \ | 41 | -e s/arm.*/arm/ -e s/sa110/arm/ \ |
42 | -e s/s390x/s390/ -e s/parisc64/parisc/ \ | 42 | -e s/s390x/s390/ -e s/parisc64/parisc/ \ |
43 | -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ | 43 | -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ |
44 | -e s/sh[234].*/sh/ ) | 44 | -e s/sh[234].*/sh/ ) |
45 | 45 | ||
46 | CC = $(CROSS_COMPILE)gcc | 46 | CC = $(CROSS_COMPILE)gcc |
47 | AR = $(CROSS_COMPILE)ar | 47 | AR = $(CROSS_COMPILE)ar |
48 | 48 | ||
49 | # Additional ARCH settings for x86 | 49 | # Additional ARCH settings for x86 |
50 | ifeq ($(ARCH),i386) | 50 | ifeq ($(ARCH),i386) |
51 | ARCH := x86 | 51 | ARCH := x86 |
52 | endif | 52 | endif |
53 | ifeq ($(ARCH),x86_64) | 53 | ifeq ($(ARCH),x86_64) |
54 | ARCH := x86 | 54 | ARCH := x86 |
55 | IS_X86_64 := 0 | 55 | IS_X86_64 := 0 |
56 | ifeq (, $(findstring m32,$(EXTRA_CFLAGS))) | 56 | ifeq (, $(findstring m32,$(EXTRA_CFLAGS))) |
57 | IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) | 57 | IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) |
58 | endif | 58 | endif |
59 | ifeq (${IS_X86_64}, 1) | 59 | ifeq (${IS_X86_64}, 1) |
60 | RAW_ARCH := x86_64 | 60 | RAW_ARCH := x86_64 |
61 | ARCH_CFLAGS := -DARCH_X86_64 | 61 | ARCH_CFLAGS := -DARCH_X86_64 |
62 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S | 62 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S |
63 | endif | 63 | endif |
64 | endif | 64 | endif |
65 | 65 | ||
66 | # | 66 | # |
67 | # Include saner warnings here, which can catch bugs: | 67 | # Include saner warnings here, which can catch bugs: |
68 | # | 68 | # |
69 | 69 | ||
70 | EXTRA_WARNINGS := -Wformat | 70 | EXTRA_WARNINGS := -Wformat |
71 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security | 71 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security |
72 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k | 72 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k |
73 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow | 73 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow |
74 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self | 74 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self |
75 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked | 75 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked |
76 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls | 76 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls |
77 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3 | 77 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3 |
78 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default | 78 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default |
79 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum | 79 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum |
80 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers | 80 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers |
81 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef | 81 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef |
82 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings | 82 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings |
83 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast | 83 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast |
84 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations | 84 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations |
85 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes | 85 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes |
86 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs | 86 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs |
87 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition | 87 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition |
88 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes | 88 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes |
89 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement | 89 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement |
90 | 90 | ||
91 | ifeq ("$(origin DEBUG)", "command line") | 91 | ifeq ("$(origin DEBUG)", "command line") |
92 | PERF_DEBUG = $(DEBUG) | 92 | PERF_DEBUG = $(DEBUG) |
93 | endif | 93 | endif |
94 | ifndef PERF_DEBUG | 94 | ifndef PERF_DEBUG |
95 | CFLAGS_OPTIMIZE = -O6 | 95 | CFLAGS_OPTIMIZE = -O6 |
96 | endif | 96 | endif |
97 | 97 | ||
98 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) | 98 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) |
99 | EXTLIBS = -lpthread -lrt -lelf -lm | 99 | EXTLIBS = -lpthread -lrt -lelf -lm |
100 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 | 100 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 |
101 | ALL_LDFLAGS = $(LDFLAGS) | 101 | ALL_LDFLAGS = $(LDFLAGS) |
102 | STRIP ?= strip | 102 | STRIP ?= strip |
103 | 103 | ||
104 | # Among the variables below, these: | 104 | # Among the variables below, these: |
105 | # perfexecdir | 105 | # perfexecdir |
106 | # template_dir | 106 | # template_dir |
107 | # mandir | 107 | # mandir |
108 | # infodir | 108 | # infodir |
109 | # htmldir | 109 | # htmldir |
110 | # ETC_PERFCONFIG (but not sysconfdir) | 110 | # ETC_PERFCONFIG (but not sysconfdir) |
111 | # can be specified as a relative path some/where/else; | 111 | # can be specified as a relative path some/where/else; |
112 | # this is interpreted as relative to $(prefix) and "perf" at | 112 | # this is interpreted as relative to $(prefix) and "perf" at |
113 | # runtime figures out where they are based on the path to the executable. | 113 | # runtime figures out where they are based on the path to the executable. |
114 | # This can help installing the suite in a relocatable way. | 114 | # This can help installing the suite in a relocatable way. |
115 | 115 | ||
116 | # Make the path relative to DESTDIR, not to prefix | 116 | # Make the path relative to DESTDIR, not to prefix |
117 | ifndef DESTDIR | 117 | ifndef DESTDIR |
118 | prefix = $(HOME) | 118 | prefix = $(HOME) |
119 | endif | 119 | endif |
120 | bindir_relative = bin | 120 | bindir_relative = bin |
121 | bindir = $(prefix)/$(bindir_relative) | 121 | bindir = $(prefix)/$(bindir_relative) |
122 | mandir = share/man | 122 | mandir = share/man |
123 | infodir = share/info | 123 | infodir = share/info |
124 | perfexecdir = libexec/perf-core | 124 | perfexecdir = libexec/perf-core |
125 | sharedir = $(prefix)/share | 125 | sharedir = $(prefix)/share |
126 | template_dir = share/perf-core/templates | 126 | template_dir = share/perf-core/templates |
127 | htmldir = share/doc/perf-doc | 127 | htmldir = share/doc/perf-doc |
128 | ifeq ($(prefix),/usr) | 128 | ifeq ($(prefix),/usr) |
129 | sysconfdir = /etc | 129 | sysconfdir = /etc |
130 | ETC_PERFCONFIG = $(sysconfdir)/perfconfig | 130 | ETC_PERFCONFIG = $(sysconfdir)/perfconfig |
131 | else | 131 | else |
132 | sysconfdir = $(prefix)/etc | 132 | sysconfdir = $(prefix)/etc |
133 | ETC_PERFCONFIG = etc/perfconfig | 133 | ETC_PERFCONFIG = etc/perfconfig |
134 | endif | 134 | endif |
135 | lib = lib | 135 | lib = lib |
136 | 136 | ||
137 | export prefix bindir sharedir sysconfdir | 137 | export prefix bindir sharedir sysconfdir |
138 | 138 | ||
139 | RM = rm -f | 139 | RM = rm -f |
140 | MKDIR = mkdir | 140 | MKDIR = mkdir |
141 | FIND = find | 141 | FIND = find |
142 | INSTALL = install | 142 | INSTALL = install |
143 | 143 | ||
144 | # sparse is architecture-neutral, which means that we need to tell it | 144 | # sparse is architecture-neutral, which means that we need to tell it |
145 | # explicitly what architecture to check for. Fix this up for yours.. | 145 | # explicitly what architecture to check for. Fix this up for yours.. |
146 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ | 146 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ |
147 | 147 | ||
148 | -include config/feature-tests.mak | 148 | -include config/feature-tests.mak |
149 | 149 | ||
150 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y) | 150 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y) |
151 | CFLAGS := $(CFLAGS) -fstack-protector-all | 151 | CFLAGS := $(CFLAGS) -fstack-protector-all |
152 | endif | 152 | endif |
153 | 153 | ||
154 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wstack-protector),y) | 154 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wstack-protector),y) |
155 | CFLAGS := $(CFLAGS) -Wstack-protector | 155 | CFLAGS := $(CFLAGS) -Wstack-protector |
156 | endif | 156 | endif |
157 | 157 | ||
158 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wvolatile-register-var),y) | 158 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wvolatile-register-var),y) |
159 | CFLAGS := $(CFLAGS) -Wvolatile-register-var | 159 | CFLAGS := $(CFLAGS) -Wvolatile-register-var |
160 | endif | 160 | endif |
161 | 161 | ||
162 | ### --- END CONFIGURATION SECTION --- | 162 | ### --- END CONFIGURATION SECTION --- |
163 | 163 | ||
164 | # Those must not be GNU-specific; they are shared with perl/ which may | 164 | # Those must not be GNU-specific; they are shared with perl/ which may |
165 | # be built by a different compiler. (Note that this is an artifact now | 165 | # be built by a different compiler. (Note that this is an artifact now |
166 | # but it still might be nice to keep that distinction.) | 166 | # but it still might be nice to keep that distinction.) |
167 | BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include | 167 | BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include |
168 | BASIC_LDFLAGS = | 168 | BASIC_LDFLAGS = |
169 | 169 | ||
170 | # Guard against environment variables | 170 | # Guard against environment variables |
171 | BUILTIN_OBJS = | 171 | BUILTIN_OBJS = |
172 | LIB_H = | 172 | LIB_H = |
173 | LIB_OBJS = | 173 | LIB_OBJS = |
174 | PYRF_OBJS = | 174 | PYRF_OBJS = |
175 | SCRIPT_SH = | 175 | SCRIPT_SH = |
176 | 176 | ||
177 | SCRIPT_SH += perf-archive.sh | 177 | SCRIPT_SH += perf-archive.sh |
178 | 178 | ||
179 | grep-libs = $(filter -l%,$(1)) | 179 | grep-libs = $(filter -l%,$(1)) |
180 | strip-libs = $(filter-out -l%,$(1)) | 180 | strip-libs = $(filter-out -l%,$(1)) |
181 | 181 | ||
182 | $(OUTPUT)python/perf.so: $(PYRF_OBJS) | 182 | $(OUTPUT)python/perf.so: $(PYRF_OBJS) |
183 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ | 183 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ |
184 | --quiet build_ext \ | 184 | --quiet build_ext; \ |
185 | --build-lib='$(OUTPUT)python' \ | 185 | mkdir -p $(OUTPUT)python && \ |
186 | --build-temp='$(OUTPUT)python/temp' | 186 | cp $(PYTHON_EXTBUILD_LIB)perf.so $(OUTPUT)python/ |
187 | # | 187 | # |
188 | # No Perl scripts right now: | 188 | # No Perl scripts right now: |
189 | # | 189 | # |
190 | 190 | ||
191 | SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) | 191 | SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) |
192 | 192 | ||
193 | # | 193 | # |
194 | # Single 'perf' binary right now: | 194 | # Single 'perf' binary right now: |
195 | # | 195 | # |
196 | PROGRAMS += $(OUTPUT)perf | 196 | PROGRAMS += $(OUTPUT)perf |
197 | 197 | ||
198 | LANG_BINDINGS = | 198 | LANG_BINDINGS = |
199 | 199 | ||
200 | # what 'all' will build and 'install' will install, in perfexecdir | 200 | # what 'all' will build and 'install' will install, in perfexecdir |
201 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) | 201 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) |
202 | 202 | ||
203 | # what 'all' will build but not install in perfexecdir | 203 | # what 'all' will build but not install in perfexecdir |
204 | OTHER_PROGRAMS = $(OUTPUT)perf | 204 | OTHER_PROGRAMS = $(OUTPUT)perf |
205 | 205 | ||
206 | # Set paths to tools early so that they can be used for version tests. | 206 | # Set paths to tools early so that they can be used for version tests. |
207 | ifndef SHELL_PATH | 207 | ifndef SHELL_PATH |
208 | SHELL_PATH = /bin/sh | 208 | SHELL_PATH = /bin/sh |
209 | endif | 209 | endif |
210 | ifndef PERL_PATH | 210 | ifndef PERL_PATH |
211 | PERL_PATH = /usr/bin/perl | 211 | PERL_PATH = /usr/bin/perl |
212 | endif | 212 | endif |
213 | 213 | ||
214 | export PERL_PATH | 214 | export PERL_PATH |
215 | 215 | ||
216 | LIB_FILE=$(OUTPUT)libperf.a | 216 | LIB_FILE=$(OUTPUT)libperf.a |
217 | 217 | ||
218 | LIB_H += ../../include/linux/perf_event.h | 218 | LIB_H += ../../include/linux/perf_event.h |
219 | LIB_H += ../../include/linux/rbtree.h | 219 | LIB_H += ../../include/linux/rbtree.h |
220 | LIB_H += ../../include/linux/list.h | 220 | LIB_H += ../../include/linux/list.h |
221 | LIB_H += ../../include/linux/const.h | 221 | LIB_H += ../../include/linux/const.h |
222 | LIB_H += ../../include/linux/hash.h | 222 | LIB_H += ../../include/linux/hash.h |
223 | LIB_H += ../../include/linux/stringify.h | 223 | LIB_H += ../../include/linux/stringify.h |
224 | LIB_H += util/include/linux/bitmap.h | 224 | LIB_H += util/include/linux/bitmap.h |
225 | LIB_H += util/include/linux/bitops.h | 225 | LIB_H += util/include/linux/bitops.h |
226 | LIB_H += util/include/linux/compiler.h | 226 | LIB_H += util/include/linux/compiler.h |
227 | LIB_H += util/include/linux/const.h | 227 | LIB_H += util/include/linux/const.h |
228 | LIB_H += util/include/linux/ctype.h | 228 | LIB_H += util/include/linux/ctype.h |
229 | LIB_H += util/include/linux/kernel.h | 229 | LIB_H += util/include/linux/kernel.h |
230 | LIB_H += util/include/linux/list.h | 230 | LIB_H += util/include/linux/list.h |
231 | LIB_H += util/include/linux/module.h | 231 | LIB_H += util/include/linux/module.h |
232 | LIB_H += util/include/linux/poison.h | 232 | LIB_H += util/include/linux/poison.h |
233 | LIB_H += util/include/linux/prefetch.h | 233 | LIB_H += util/include/linux/prefetch.h |
234 | LIB_H += util/include/linux/rbtree.h | 234 | LIB_H += util/include/linux/rbtree.h |
235 | LIB_H += util/include/linux/string.h | 235 | LIB_H += util/include/linux/string.h |
236 | LIB_H += util/include/linux/types.h | 236 | LIB_H += util/include/linux/types.h |
237 | LIB_H += util/include/linux/linkage.h | 237 | LIB_H += util/include/linux/linkage.h |
238 | LIB_H += util/include/asm/asm-offsets.h | 238 | LIB_H += util/include/asm/asm-offsets.h |
239 | LIB_H += util/include/asm/bug.h | 239 | LIB_H += util/include/asm/bug.h |
240 | LIB_H += util/include/asm/byteorder.h | 240 | LIB_H += util/include/asm/byteorder.h |
241 | LIB_H += util/include/asm/hweight.h | 241 | LIB_H += util/include/asm/hweight.h |
242 | LIB_H += util/include/asm/swab.h | 242 | LIB_H += util/include/asm/swab.h |
243 | LIB_H += util/include/asm/system.h | 243 | LIB_H += util/include/asm/system.h |
244 | LIB_H += util/include/asm/uaccess.h | 244 | LIB_H += util/include/asm/uaccess.h |
245 | LIB_H += util/include/dwarf-regs.h | 245 | LIB_H += util/include/dwarf-regs.h |
246 | LIB_H += util/include/asm/dwarf2.h | 246 | LIB_H += util/include/asm/dwarf2.h |
247 | LIB_H += util/include/asm/cpufeature.h | 247 | LIB_H += util/include/asm/cpufeature.h |
248 | LIB_H += perf.h | 248 | LIB_H += perf.h |
249 | LIB_H += util/annotate.h | 249 | LIB_H += util/annotate.h |
250 | LIB_H += util/cache.h | 250 | LIB_H += util/cache.h |
251 | LIB_H += util/callchain.h | 251 | LIB_H += util/callchain.h |
252 | LIB_H += util/build-id.h | 252 | LIB_H += util/build-id.h |
253 | LIB_H += util/debug.h | 253 | LIB_H += util/debug.h |
254 | LIB_H += util/debugfs.h | 254 | LIB_H += util/debugfs.h |
255 | LIB_H += util/event.h | 255 | LIB_H += util/event.h |
256 | LIB_H += util/evsel.h | 256 | LIB_H += util/evsel.h |
257 | LIB_H += util/evlist.h | 257 | LIB_H += util/evlist.h |
258 | LIB_H += util/exec_cmd.h | 258 | LIB_H += util/exec_cmd.h |
259 | LIB_H += util/types.h | 259 | LIB_H += util/types.h |
260 | LIB_H += util/levenshtein.h | 260 | LIB_H += util/levenshtein.h |
261 | LIB_H += util/map.h | 261 | LIB_H += util/map.h |
262 | LIB_H += util/parse-options.h | 262 | LIB_H += util/parse-options.h |
263 | LIB_H += util/parse-events.h | 263 | LIB_H += util/parse-events.h |
264 | LIB_H += util/quote.h | 264 | LIB_H += util/quote.h |
265 | LIB_H += util/util.h | 265 | LIB_H += util/util.h |
266 | LIB_H += util/xyarray.h | 266 | LIB_H += util/xyarray.h |
267 | LIB_H += util/header.h | 267 | LIB_H += util/header.h |
268 | LIB_H += util/help.h | 268 | LIB_H += util/help.h |
269 | LIB_H += util/session.h | 269 | LIB_H += util/session.h |
270 | LIB_H += util/strbuf.h | 270 | LIB_H += util/strbuf.h |
271 | LIB_H += util/strlist.h | 271 | LIB_H += util/strlist.h |
272 | LIB_H += util/strfilter.h | 272 | LIB_H += util/strfilter.h |
273 | LIB_H += util/svghelper.h | 273 | LIB_H += util/svghelper.h |
274 | LIB_H += util/run-command.h | 274 | LIB_H += util/run-command.h |
275 | LIB_H += util/sigchain.h | 275 | LIB_H += util/sigchain.h |
276 | LIB_H += util/symbol.h | 276 | LIB_H += util/symbol.h |
277 | LIB_H += util/color.h | 277 | LIB_H += util/color.h |
278 | LIB_H += util/values.h | 278 | LIB_H += util/values.h |
279 | LIB_H += util/sort.h | 279 | LIB_H += util/sort.h |
280 | LIB_H += util/hist.h | 280 | LIB_H += util/hist.h |
281 | LIB_H += util/thread.h | 281 | LIB_H += util/thread.h |
282 | LIB_H += util/thread_map.h | 282 | LIB_H += util/thread_map.h |
283 | LIB_H += util/trace-event.h | 283 | LIB_H += util/trace-event.h |
284 | LIB_H += util/probe-finder.h | 284 | LIB_H += util/probe-finder.h |
285 | LIB_H += util/dwarf-aux.h | 285 | LIB_H += util/dwarf-aux.h |
286 | LIB_H += util/probe-event.h | 286 | LIB_H += util/probe-event.h |
287 | LIB_H += util/pstack.h | 287 | LIB_H += util/pstack.h |
288 | LIB_H += util/cpumap.h | 288 | LIB_H += util/cpumap.h |
289 | LIB_H += util/top.h | 289 | LIB_H += util/top.h |
290 | LIB_H += $(ARCH_INCLUDE) | 290 | LIB_H += $(ARCH_INCLUDE) |
291 | LIB_H += util/cgroup.h | 291 | LIB_H += util/cgroup.h |
292 | 292 | ||
293 | LIB_OBJS += $(OUTPUT)util/abspath.o | 293 | LIB_OBJS += $(OUTPUT)util/abspath.o |
294 | LIB_OBJS += $(OUTPUT)util/alias.o | 294 | LIB_OBJS += $(OUTPUT)util/alias.o |
295 | LIB_OBJS += $(OUTPUT)util/annotate.o | 295 | LIB_OBJS += $(OUTPUT)util/annotate.o |
296 | LIB_OBJS += $(OUTPUT)util/build-id.o | 296 | LIB_OBJS += $(OUTPUT)util/build-id.o |
297 | LIB_OBJS += $(OUTPUT)util/config.o | 297 | LIB_OBJS += $(OUTPUT)util/config.o |
298 | LIB_OBJS += $(OUTPUT)util/ctype.o | 298 | LIB_OBJS += $(OUTPUT)util/ctype.o |
299 | LIB_OBJS += $(OUTPUT)util/debugfs.o | 299 | LIB_OBJS += $(OUTPUT)util/debugfs.o |
300 | LIB_OBJS += $(OUTPUT)util/environment.o | 300 | LIB_OBJS += $(OUTPUT)util/environment.o |
301 | LIB_OBJS += $(OUTPUT)util/event.o | 301 | LIB_OBJS += $(OUTPUT)util/event.o |
302 | LIB_OBJS += $(OUTPUT)util/evlist.o | 302 | LIB_OBJS += $(OUTPUT)util/evlist.o |
303 | LIB_OBJS += $(OUTPUT)util/evsel.o | 303 | LIB_OBJS += $(OUTPUT)util/evsel.o |
304 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o | 304 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o |
305 | LIB_OBJS += $(OUTPUT)util/help.o | 305 | LIB_OBJS += $(OUTPUT)util/help.o |
306 | LIB_OBJS += $(OUTPUT)util/levenshtein.o | 306 | LIB_OBJS += $(OUTPUT)util/levenshtein.o |
307 | LIB_OBJS += $(OUTPUT)util/parse-options.o | 307 | LIB_OBJS += $(OUTPUT)util/parse-options.o |
308 | LIB_OBJS += $(OUTPUT)util/parse-events.o | 308 | LIB_OBJS += $(OUTPUT)util/parse-events.o |
309 | LIB_OBJS += $(OUTPUT)util/path.o | 309 | LIB_OBJS += $(OUTPUT)util/path.o |
310 | LIB_OBJS += $(OUTPUT)util/rbtree.o | 310 | LIB_OBJS += $(OUTPUT)util/rbtree.o |
311 | LIB_OBJS += $(OUTPUT)util/bitmap.o | 311 | LIB_OBJS += $(OUTPUT)util/bitmap.o |
312 | LIB_OBJS += $(OUTPUT)util/hweight.o | 312 | LIB_OBJS += $(OUTPUT)util/hweight.o |
313 | LIB_OBJS += $(OUTPUT)util/run-command.o | 313 | LIB_OBJS += $(OUTPUT)util/run-command.o |
314 | LIB_OBJS += $(OUTPUT)util/quote.o | 314 | LIB_OBJS += $(OUTPUT)util/quote.o |
315 | LIB_OBJS += $(OUTPUT)util/strbuf.o | 315 | LIB_OBJS += $(OUTPUT)util/strbuf.o |
316 | LIB_OBJS += $(OUTPUT)util/string.o | 316 | LIB_OBJS += $(OUTPUT)util/string.o |
317 | LIB_OBJS += $(OUTPUT)util/strlist.o | 317 | LIB_OBJS += $(OUTPUT)util/strlist.o |
318 | LIB_OBJS += $(OUTPUT)util/strfilter.o | 318 | LIB_OBJS += $(OUTPUT)util/strfilter.o |
319 | LIB_OBJS += $(OUTPUT)util/top.o | 319 | LIB_OBJS += $(OUTPUT)util/top.o |
320 | LIB_OBJS += $(OUTPUT)util/usage.o | 320 | LIB_OBJS += $(OUTPUT)util/usage.o |
321 | LIB_OBJS += $(OUTPUT)util/wrapper.o | 321 | LIB_OBJS += $(OUTPUT)util/wrapper.o |
322 | LIB_OBJS += $(OUTPUT)util/sigchain.o | 322 | LIB_OBJS += $(OUTPUT)util/sigchain.o |
323 | LIB_OBJS += $(OUTPUT)util/symbol.o | 323 | LIB_OBJS += $(OUTPUT)util/symbol.o |
324 | LIB_OBJS += $(OUTPUT)util/color.o | 324 | LIB_OBJS += $(OUTPUT)util/color.o |
325 | LIB_OBJS += $(OUTPUT)util/pager.o | 325 | LIB_OBJS += $(OUTPUT)util/pager.o |
326 | LIB_OBJS += $(OUTPUT)util/header.o | 326 | LIB_OBJS += $(OUTPUT)util/header.o |
327 | LIB_OBJS += $(OUTPUT)util/callchain.o | 327 | LIB_OBJS += $(OUTPUT)util/callchain.o |
328 | LIB_OBJS += $(OUTPUT)util/values.o | 328 | LIB_OBJS += $(OUTPUT)util/values.o |
329 | LIB_OBJS += $(OUTPUT)util/debug.o | 329 | LIB_OBJS += $(OUTPUT)util/debug.o |
330 | LIB_OBJS += $(OUTPUT)util/map.o | 330 | LIB_OBJS += $(OUTPUT)util/map.o |
331 | LIB_OBJS += $(OUTPUT)util/pstack.o | 331 | LIB_OBJS += $(OUTPUT)util/pstack.o |
332 | LIB_OBJS += $(OUTPUT)util/session.o | 332 | LIB_OBJS += $(OUTPUT)util/session.o |
333 | LIB_OBJS += $(OUTPUT)util/thread.o | 333 | LIB_OBJS += $(OUTPUT)util/thread.o |
334 | LIB_OBJS += $(OUTPUT)util/thread_map.o | 334 | LIB_OBJS += $(OUTPUT)util/thread_map.o |
335 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o | 335 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o |
336 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o | 336 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o |
337 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o | 337 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o |
338 | LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o | 338 | LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o |
339 | LIB_OBJS += $(OUTPUT)util/svghelper.o | 339 | LIB_OBJS += $(OUTPUT)util/svghelper.o |
340 | LIB_OBJS += $(OUTPUT)util/sort.o | 340 | LIB_OBJS += $(OUTPUT)util/sort.o |
341 | LIB_OBJS += $(OUTPUT)util/hist.o | 341 | LIB_OBJS += $(OUTPUT)util/hist.o |
342 | LIB_OBJS += $(OUTPUT)util/probe-event.o | 342 | LIB_OBJS += $(OUTPUT)util/probe-event.o |
343 | LIB_OBJS += $(OUTPUT)util/util.o | 343 | LIB_OBJS += $(OUTPUT)util/util.o |
344 | LIB_OBJS += $(OUTPUT)util/xyarray.o | 344 | LIB_OBJS += $(OUTPUT)util/xyarray.o |
345 | LIB_OBJS += $(OUTPUT)util/cpumap.o | 345 | LIB_OBJS += $(OUTPUT)util/cpumap.o |
346 | LIB_OBJS += $(OUTPUT)util/cgroup.o | 346 | LIB_OBJS += $(OUTPUT)util/cgroup.o |
347 | 347 | ||
348 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 348 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
349 | 349 | ||
350 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 350 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
351 | 351 | ||
352 | # Benchmark modules | 352 | # Benchmark modules |
353 | BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o | 353 | BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o |
354 | BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o | 354 | BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o |
355 | ifeq ($(RAW_ARCH),x86_64) | 355 | ifeq ($(RAW_ARCH),x86_64) |
356 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o | 356 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o |
357 | endif | 357 | endif |
358 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o | 358 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o |
359 | 359 | ||
360 | BUILTIN_OBJS += $(OUTPUT)builtin-diff.o | 360 | BUILTIN_OBJS += $(OUTPUT)builtin-diff.o |
361 | BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o | 361 | BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o |
362 | BUILTIN_OBJS += $(OUTPUT)builtin-help.o | 362 | BUILTIN_OBJS += $(OUTPUT)builtin-help.o |
363 | BUILTIN_OBJS += $(OUTPUT)builtin-sched.o | 363 | BUILTIN_OBJS += $(OUTPUT)builtin-sched.o |
364 | BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o | 364 | BUILTIN_OBJS += $(OUTPUT)builtin-buildid-list.o |
365 | BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o | 365 | BUILTIN_OBJS += $(OUTPUT)builtin-buildid-cache.o |
366 | BUILTIN_OBJS += $(OUTPUT)builtin-list.o | 366 | BUILTIN_OBJS += $(OUTPUT)builtin-list.o |
367 | BUILTIN_OBJS += $(OUTPUT)builtin-record.o | 367 | BUILTIN_OBJS += $(OUTPUT)builtin-record.o |
368 | BUILTIN_OBJS += $(OUTPUT)builtin-report.o | 368 | BUILTIN_OBJS += $(OUTPUT)builtin-report.o |
369 | BUILTIN_OBJS += $(OUTPUT)builtin-stat.o | 369 | BUILTIN_OBJS += $(OUTPUT)builtin-stat.o |
370 | BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o | 370 | BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o |
371 | BUILTIN_OBJS += $(OUTPUT)builtin-top.o | 371 | BUILTIN_OBJS += $(OUTPUT)builtin-top.o |
372 | BUILTIN_OBJS += $(OUTPUT)builtin-script.o | 372 | BUILTIN_OBJS += $(OUTPUT)builtin-script.o |
373 | BUILTIN_OBJS += $(OUTPUT)builtin-probe.o | 373 | BUILTIN_OBJS += $(OUTPUT)builtin-probe.o |
374 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o | 374 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o |
375 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o | 375 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o |
376 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o | 376 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o |
377 | BUILTIN_OBJS += $(OUTPUT)builtin-test.o | 377 | BUILTIN_OBJS += $(OUTPUT)builtin-test.o |
378 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o | 378 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o |
379 | 379 | ||
380 | PERFLIBS = $(LIB_FILE) | 380 | PERFLIBS = $(LIB_FILE) |
381 | 381 | ||
382 | # Files needed for the python binding, perf.so | 382 | # Files needed for the python binding, perf.so |
383 | # pyrf is just an internal name needed for all those wrappers. | 383 | # pyrf is just an internal name needed for all those wrappers. |
384 | # This has to be in sync with what is in the 'sources' variable in | 384 | # This has to be in sync with what is in the 'sources' variable in |
385 | # tools/perf/util/setup.py | 385 | # tools/perf/util/setup.py |
386 | 386 | ||
387 | PYRF_OBJS += $(OUTPUT)util/cpumap.o | 387 | PYRF_OBJS += $(OUTPUT)util/cpumap.o |
388 | PYRF_OBJS += $(OUTPUT)util/ctype.o | 388 | PYRF_OBJS += $(OUTPUT)util/ctype.o |
389 | PYRF_OBJS += $(OUTPUT)util/evlist.o | 389 | PYRF_OBJS += $(OUTPUT)util/evlist.o |
390 | PYRF_OBJS += $(OUTPUT)util/evsel.o | 390 | PYRF_OBJS += $(OUTPUT)util/evsel.o |
391 | PYRF_OBJS += $(OUTPUT)util/python.o | 391 | PYRF_OBJS += $(OUTPUT)util/python.o |
392 | PYRF_OBJS += $(OUTPUT)util/thread_map.o | 392 | PYRF_OBJS += $(OUTPUT)util/thread_map.o |
393 | PYRF_OBJS += $(OUTPUT)util/util.o | 393 | PYRF_OBJS += $(OUTPUT)util/util.o |
394 | PYRF_OBJS += $(OUTPUT)util/xyarray.o | 394 | PYRF_OBJS += $(OUTPUT)util/xyarray.o |
395 | 395 | ||
396 | # | 396 | # |
397 | # Platform specific tweaks | 397 | # Platform specific tweaks |
398 | # | 398 | # |
399 | 399 | ||
400 | # We choose to avoid "if .. else if .. else .. endif endif" | 400 | # We choose to avoid "if .. else if .. else .. endif endif" |
401 | # because maintaining the nesting to match is a pain. If | 401 | # because maintaining the nesting to match is a pain. If |
402 | # we had "elif" things would have been much nicer... | 402 | # we had "elif" things would have been much nicer... |
403 | 403 | ||
404 | -include config.mak.autogen | 404 | -include config.mak.autogen |
405 | -include config.mak | 405 | -include config.mak |
406 | 406 | ||
407 | ifndef NO_DWARF | 407 | ifndef NO_DWARF |
408 | FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) | 408 | FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) |
409 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) | 409 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) |
410 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); | 410 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); |
411 | NO_DWARF := 1 | 411 | NO_DWARF := 1 |
412 | endif # Dwarf support | 412 | endif # Dwarf support |
413 | endif # NO_DWARF | 413 | endif # NO_DWARF |
414 | 414 | ||
415 | -include arch/$(ARCH)/Makefile | 415 | -include arch/$(ARCH)/Makefile |
416 | 416 | ||
417 | ifneq ($(OUTPUT),) | 417 | ifneq ($(OUTPUT),) |
418 | BASIC_CFLAGS += -I$(OUTPUT) | 418 | BASIC_CFLAGS += -I$(OUTPUT) |
419 | endif | 419 | endif |
420 | 420 | ||
421 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) | 421 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) |
422 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y) | 422 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y) |
423 | FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) | 423 | FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) |
424 | ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y) | 424 | ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y) |
425 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | 425 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); |
426 | else | 426 | else |
427 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel); | 427 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel); |
428 | endif | 428 | endif |
429 | endif | 429 | endif |
430 | 430 | ||
431 | ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y) | 431 | ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y) |
432 | BASIC_CFLAGS += -DLIBELF_NO_MMAP | 432 | BASIC_CFLAGS += -DLIBELF_NO_MMAP |
433 | endif | 433 | endif |
434 | 434 | ||
435 | ifndef NO_DWARF | 435 | ifndef NO_DWARF |
436 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) | 436 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) |
437 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); | 437 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); |
438 | else | 438 | else |
439 | BASIC_CFLAGS += -DDWARF_SUPPORT | 439 | BASIC_CFLAGS += -DDWARF_SUPPORT |
440 | EXTLIBS += -lelf -ldw | 440 | EXTLIBS += -lelf -ldw |
441 | LIB_OBJS += $(OUTPUT)util/probe-finder.o | 441 | LIB_OBJS += $(OUTPUT)util/probe-finder.o |
442 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o | 442 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o |
443 | endif # PERF_HAVE_DWARF_REGS | 443 | endif # PERF_HAVE_DWARF_REGS |
444 | endif # NO_DWARF | 444 | endif # NO_DWARF |
445 | 445 | ||
446 | ifdef NO_NEWT | 446 | ifdef NO_NEWT |
447 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT | 447 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT |
448 | else | 448 | else |
449 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt | 449 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt |
450 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y) | 450 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y) |
451 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); | 451 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); |
452 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT | 452 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT |
453 | else | 453 | else |
454 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | 454 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h |
455 | BASIC_CFLAGS += -I/usr/include/slang | 455 | BASIC_CFLAGS += -I/usr/include/slang |
456 | EXTLIBS += -lnewt -lslang | 456 | EXTLIBS += -lnewt -lslang |
457 | LIB_OBJS += $(OUTPUT)util/ui/setup.o | 457 | LIB_OBJS += $(OUTPUT)util/ui/setup.o |
458 | LIB_OBJS += $(OUTPUT)util/ui/browser.o | 458 | LIB_OBJS += $(OUTPUT)util/ui/browser.o |
459 | LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o | 459 | LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o |
460 | LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o | 460 | LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o |
461 | LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o | 461 | LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o |
462 | LIB_OBJS += $(OUTPUT)util/ui/browsers/top.o | 462 | LIB_OBJS += $(OUTPUT)util/ui/browsers/top.o |
463 | LIB_OBJS += $(OUTPUT)util/ui/helpline.o | 463 | LIB_OBJS += $(OUTPUT)util/ui/helpline.o |
464 | LIB_OBJS += $(OUTPUT)util/ui/progress.o | 464 | LIB_OBJS += $(OUTPUT)util/ui/progress.o |
465 | LIB_OBJS += $(OUTPUT)util/ui/util.o | 465 | LIB_OBJS += $(OUTPUT)util/ui/util.o |
466 | LIB_H += util/ui/browser.h | 466 | LIB_H += util/ui/browser.h |
467 | LIB_H += util/ui/browsers/map.h | 467 | LIB_H += util/ui/browsers/map.h |
468 | LIB_H += util/ui/helpline.h | 468 | LIB_H += util/ui/helpline.h |
469 | LIB_H += util/ui/libslang.h | 469 | LIB_H += util/ui/libslang.h |
470 | LIB_H += util/ui/progress.h | 470 | LIB_H += util/ui/progress.h |
471 | LIB_H += util/ui/util.h | 471 | LIB_H += util/ui/util.h |
472 | LIB_H += util/ui/ui.h | 472 | LIB_H += util/ui/ui.h |
473 | endif | 473 | endif |
474 | endif | 474 | endif |
475 | 475 | ||
476 | ifdef NO_LIBPERL | 476 | ifdef NO_LIBPERL |
477 | BASIC_CFLAGS += -DNO_LIBPERL | 477 | BASIC_CFLAGS += -DNO_LIBPERL |
478 | else | 478 | else |
479 | PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null) | 479 | PERL_EMBED_LDOPTS = $(shell perl -MExtUtils::Embed -e ldopts 2>/dev/null) |
480 | PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS)) | 480 | PERL_EMBED_LDFLAGS = $(call strip-libs,$(PERL_EMBED_LDOPTS)) |
481 | PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) | 481 | PERL_EMBED_LIBADD = $(call grep-libs,$(PERL_EMBED_LDOPTS)) |
482 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` | 482 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` |
483 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) | 483 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) |
484 | 484 | ||
485 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y) | 485 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y) |
486 | BASIC_CFLAGS += -DNO_LIBPERL | 486 | BASIC_CFLAGS += -DNO_LIBPERL |
487 | else | 487 | else |
488 | ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS) | 488 | ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS) |
489 | EXTLIBS += $(PERL_EMBED_LIBADD) | 489 | EXTLIBS += $(PERL_EMBED_LIBADD) |
490 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o | 490 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o |
491 | LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o | 491 | LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o |
492 | endif | 492 | endif |
493 | endif | 493 | endif |
494 | 494 | ||
495 | disable-python = $(eval $(disable-python_code)) | 495 | disable-python = $(eval $(disable-python_code)) |
496 | define disable-python_code | 496 | define disable-python_code |
497 | BASIC_CFLAGS += -DNO_LIBPYTHON | 497 | BASIC_CFLAGS += -DNO_LIBPYTHON |
498 | $(if $(1),$(warning No $(1) was found)) | 498 | $(if $(1),$(warning No $(1) was found)) |
499 | $(warning Python support won't be built) | 499 | $(warning Python support won't be built) |
500 | endef | 500 | endef |
501 | 501 | ||
502 | override PYTHON := \ | 502 | override PYTHON := \ |
503 | $(call get-executable-or-default,PYTHON,python) | 503 | $(call get-executable-or-default,PYTHON,python) |
504 | 504 | ||
505 | ifndef PYTHON | 505 | ifndef PYTHON |
506 | $(call disable-python,python interpreter) | 506 | $(call disable-python,python interpreter) |
507 | python-clean := | 507 | python-clean := |
508 | else | 508 | else |
509 | 509 | ||
510 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | 510 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) |
511 | 511 | ||
512 | python-clean := $(PYTHON_WORD) util/setup.py clean \ | 512 | # python extension build directories |
513 | --build-lib='$(OUTPUT)python' \ | 513 | PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ |
514 | --build-temp='$(OUTPUT)python/temp' | 514 | PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ |
515 | PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ | ||
516 | export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP | ||
515 | 517 | ||
518 | python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so | ||
519 | |||
516 | ifdef NO_LIBPYTHON | 520 | ifdef NO_LIBPYTHON |
517 | $(call disable-python) | 521 | $(call disable-python) |
518 | else | 522 | else |
519 | 523 | ||
520 | override PYTHON_CONFIG := \ | 524 | override PYTHON_CONFIG := \ |
521 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON)-config) | 525 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON)-config) |
522 | 526 | ||
523 | ifndef PYTHON_CONFIG | 527 | ifndef PYTHON_CONFIG |
524 | $(call disable-python,python-config tool) | 528 | $(call disable-python,python-config tool) |
525 | else | 529 | else |
526 | 530 | ||
527 | PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) | 531 | PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) |
528 | 532 | ||
529 | PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) | 533 | PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) |
530 | PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) | 534 | PYTHON_EMBED_LDFLAGS := $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) |
531 | PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) | 535 | PYTHON_EMBED_LIBADD := $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) |
532 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | 536 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) |
533 | FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) | 537 | FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) |
534 | 538 | ||
535 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) | 539 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) |
536 | $(call disable-python,Python.h (for Python 2.x)) | 540 | $(call disable-python,Python.h (for Python 2.x)) |
537 | else | 541 | else |
538 | 542 | ||
539 | ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED)),y) | 543 | ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED)),y) |
540 | $(warning Python 3 is not yet supported; please set) | 544 | $(warning Python 3 is not yet supported; please set) |
541 | $(warning PYTHON and/or PYTHON_CONFIG appropriately.) | 545 | $(warning PYTHON and/or PYTHON_CONFIG appropriately.) |
542 | $(warning If you also have Python 2 installed, then) | 546 | $(warning If you also have Python 2 installed, then) |
543 | $(warning try something like:) | 547 | $(warning try something like:) |
544 | $(warning $(and ,)) | 548 | $(warning $(and ,)) |
545 | $(warning $(and ,) make PYTHON=python2) | 549 | $(warning $(and ,) make PYTHON=python2) |
546 | $(warning $(and ,)) | 550 | $(warning $(and ,)) |
547 | $(warning Otherwise, disable Python support entirely:) | 551 | $(warning Otherwise, disable Python support entirely:) |
548 | $(warning $(and ,)) | 552 | $(warning $(and ,)) |
549 | $(warning $(and ,) make NO_LIBPYTHON=1) | 553 | $(warning $(and ,) make NO_LIBPYTHON=1) |
550 | $(warning $(and ,)) | 554 | $(warning $(and ,)) |
551 | $(error $(and ,)) | 555 | $(error $(and ,)) |
552 | else | 556 | else |
553 | ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS) | 557 | ALL_LDFLAGS += $(PYTHON_EMBED_LDFLAGS) |
554 | EXTLIBS += $(PYTHON_EMBED_LIBADD) | 558 | EXTLIBS += $(PYTHON_EMBED_LIBADD) |
555 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o | 559 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o |
556 | LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o | 560 | LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o |
557 | LANG_BINDINGS += $(OUTPUT)python/perf.so | 561 | LANG_BINDINGS += $(OUTPUT)python/perf.so |
558 | endif | 562 | endif |
559 | 563 | ||
560 | endif | 564 | endif |
561 | endif | 565 | endif |
562 | endif | 566 | endif |
563 | endif | 567 | endif |
564 | 568 | ||
565 | ifdef NO_DEMANGLE | 569 | ifdef NO_DEMANGLE |
566 | BASIC_CFLAGS += -DNO_DEMANGLE | 570 | BASIC_CFLAGS += -DNO_DEMANGLE |
567 | else | 571 | else |
568 | ifdef HAVE_CPLUS_DEMANGLE | 572 | ifdef HAVE_CPLUS_DEMANGLE |
569 | EXTLIBS += -liberty | 573 | EXTLIBS += -liberty |
570 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 574 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
571 | else | 575 | else |
572 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd | 576 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd |
573 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) | 577 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) |
574 | ifeq ($(has_bfd),y) | 578 | ifeq ($(has_bfd),y) |
575 | EXTLIBS += -lbfd | 579 | EXTLIBS += -lbfd |
576 | else | 580 | else |
577 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty | 581 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty |
578 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) | 582 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) |
579 | ifeq ($(has_bfd_iberty),y) | 583 | ifeq ($(has_bfd_iberty),y) |
580 | EXTLIBS += -lbfd -liberty | 584 | EXTLIBS += -lbfd -liberty |
581 | else | 585 | else |
582 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz | 586 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz |
583 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) | 587 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) |
584 | ifeq ($(has_bfd_iberty_z),y) | 588 | ifeq ($(has_bfd_iberty_z),y) |
585 | EXTLIBS += -lbfd -liberty -lz | 589 | EXTLIBS += -lbfd -liberty -lz |
586 | else | 590 | else |
587 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty | 591 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty |
588 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) | 592 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) |
589 | ifeq ($(has_cplus_demangle),y) | 593 | ifeq ($(has_cplus_demangle),y) |
590 | EXTLIBS += -liberty | 594 | EXTLIBS += -liberty |
591 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 595 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
592 | else | 596 | else |
593 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) | 597 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) |
594 | BASIC_CFLAGS += -DNO_DEMANGLE | 598 | BASIC_CFLAGS += -DNO_DEMANGLE |
595 | endif | 599 | endif |
596 | endif | 600 | endif |
597 | endif | 601 | endif |
598 | endif | 602 | endif |
599 | endif | 603 | endif |
600 | endif | 604 | endif |
601 | 605 | ||
602 | 606 | ||
603 | ifdef NO_STRLCPY | 607 | ifdef NO_STRLCPY |
604 | BASIC_CFLAGS += -DNO_STRLCPY | 608 | BASIC_CFLAGS += -DNO_STRLCPY |
605 | else | 609 | else |
606 | ifneq ($(call try-cc,$(SOURCE_STRLCPY),),y) | 610 | ifneq ($(call try-cc,$(SOURCE_STRLCPY),),y) |
607 | BASIC_CFLAGS += -DNO_STRLCPY | 611 | BASIC_CFLAGS += -DNO_STRLCPY |
608 | endif | 612 | endif |
609 | endif | 613 | endif |
610 | 614 | ||
611 | ifneq ($(findstring $(MAKEFLAGS),s),s) | 615 | ifneq ($(findstring $(MAKEFLAGS),s),s) |
612 | ifndef V | 616 | ifndef V |
613 | QUIET_CC = @echo ' ' CC $@; | 617 | QUIET_CC = @echo ' ' CC $@; |
614 | QUIET_AR = @echo ' ' AR $@; | 618 | QUIET_AR = @echo ' ' AR $@; |
615 | QUIET_LINK = @echo ' ' LINK $@; | 619 | QUIET_LINK = @echo ' ' LINK $@; |
616 | QUIET_MKDIR = @echo ' ' MKDIR $@; | 620 | QUIET_MKDIR = @echo ' ' MKDIR $@; |
617 | QUIET_GEN = @echo ' ' GEN $@; | 621 | QUIET_GEN = @echo ' ' GEN $@; |
618 | endif | 622 | endif |
619 | endif | 623 | endif |
620 | 624 | ||
621 | ifdef ASCIIDOC8 | 625 | ifdef ASCIIDOC8 |
622 | export ASCIIDOC8 | 626 | export ASCIIDOC8 |
623 | endif | 627 | endif |
624 | 628 | ||
625 | # Shell quote (do not use $(call) to accommodate ancient setups); | 629 | # Shell quote (do not use $(call) to accommodate ancient setups); |
626 | 630 | ||
627 | ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) | 631 | ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) |
628 | 632 | ||
629 | DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) | 633 | DESTDIR_SQ = $(subst ','\'',$(DESTDIR)) |
630 | bindir_SQ = $(subst ','\'',$(bindir)) | 634 | bindir_SQ = $(subst ','\'',$(bindir)) |
631 | bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) | 635 | bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) |
632 | mandir_SQ = $(subst ','\'',$(mandir)) | 636 | mandir_SQ = $(subst ','\'',$(mandir)) |
633 | infodir_SQ = $(subst ','\'',$(infodir)) | 637 | infodir_SQ = $(subst ','\'',$(infodir)) |
634 | perfexecdir_SQ = $(subst ','\'',$(perfexecdir)) | 638 | perfexecdir_SQ = $(subst ','\'',$(perfexecdir)) |
635 | template_dir_SQ = $(subst ','\'',$(template_dir)) | 639 | template_dir_SQ = $(subst ','\'',$(template_dir)) |
636 | htmldir_SQ = $(subst ','\'',$(htmldir)) | 640 | htmldir_SQ = $(subst ','\'',$(htmldir)) |
637 | prefix_SQ = $(subst ','\'',$(prefix)) | 641 | prefix_SQ = $(subst ','\'',$(prefix)) |
638 | 642 | ||
639 | SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) | 643 | SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) |
640 | 644 | ||
641 | LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group | 645 | LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive -Wl,--start-group $(EXTLIBS) -Wl,--end-group |
642 | 646 | ||
643 | ALL_CFLAGS += $(BASIC_CFLAGS) | 647 | ALL_CFLAGS += $(BASIC_CFLAGS) |
644 | ALL_CFLAGS += $(ARCH_CFLAGS) | 648 | ALL_CFLAGS += $(ARCH_CFLAGS) |
645 | ALL_LDFLAGS += $(BASIC_LDFLAGS) | 649 | ALL_LDFLAGS += $(BASIC_LDFLAGS) |
646 | 650 | ||
647 | export INSTALL SHELL_PATH | 651 | export INSTALL SHELL_PATH |
648 | 652 | ||
649 | 653 | ||
650 | ### Build rules | 654 | ### Build rules |
651 | 655 | ||
652 | SHELL = $(SHELL_PATH) | 656 | SHELL = $(SHELL_PATH) |
653 | 657 | ||
654 | all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS) | 658 | all: shell_compatibility_test $(ALL_PROGRAMS) $(LANG_BINDINGS) $(OTHER_PROGRAMS) |
655 | 659 | ||
656 | please_set_SHELL_PATH_to_a_more_modern_shell: | 660 | please_set_SHELL_PATH_to_a_more_modern_shell: |
657 | @$$(:) | 661 | @$$(:) |
658 | 662 | ||
659 | shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell | 663 | shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell |
660 | 664 | ||
661 | strip: $(PROGRAMS) $(OUTPUT)perf | 665 | strip: $(PROGRAMS) $(OUTPUT)perf |
662 | $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf | 666 | $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf |
663 | 667 | ||
664 | $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS | 668 | $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS |
665 | $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ | 669 | $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ |
666 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ | 670 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ |
667 | $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ | 671 | $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ |
668 | 672 | ||
669 | $(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) | 673 | $(OUTPUT)perf: $(OUTPUT)perf.o $(BUILTIN_OBJS) $(PERFLIBS) |
670 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \ | 674 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) $(OUTPUT)perf.o \ |
671 | $(BUILTIN_OBJS) $(LIBS) -o $@ | 675 | $(BUILTIN_OBJS) $(LIBS) -o $@ |
672 | 676 | ||
673 | $(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS | 677 | $(OUTPUT)builtin-help.o: builtin-help.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS |
674 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 678 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ |
675 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ | 679 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ |
676 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ | 680 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ |
677 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< | 681 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< |
678 | 682 | ||
679 | $(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS | 683 | $(OUTPUT)builtin-timechart.o: builtin-timechart.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS |
680 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 684 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ |
681 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ | 685 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ |
682 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ | 686 | '-DPERF_MAN_PATH="$(mandir_SQ)"' \ |
683 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< | 687 | '-DPERF_INFO_PATH="$(infodir_SQ)"' $< |
684 | 688 | ||
685 | $(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt | 689 | $(OUTPUT)common-cmds.h: util/generate-cmdlist.sh command-list.txt |
686 | 690 | ||
687 | $(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) | 691 | $(OUTPUT)common-cmds.h: $(wildcard Documentation/perf-*.txt) |
688 | $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ | 692 | $(QUIET_GEN). util/generate-cmdlist.sh > $@+ && mv $@+ $@ |
689 | 693 | ||
690 | $(SCRIPTS) : % : %.sh | 694 | $(SCRIPTS) : % : %.sh |
691 | $(QUIET_GEN)$(INSTALL) '$@.sh' '$(OUTPUT)$@' | 695 | $(QUIET_GEN)$(INSTALL) '$@.sh' '$(OUTPUT)$@' |
692 | 696 | ||
693 | # These can record PERF_VERSION | 697 | # These can record PERF_VERSION |
694 | $(OUTPUT)perf.o perf.spec \ | 698 | $(OUTPUT)perf.o perf.spec \ |
695 | $(SCRIPTS) \ | 699 | $(SCRIPTS) \ |
696 | : $(OUTPUT)PERF-VERSION-FILE | 700 | : $(OUTPUT)PERF-VERSION-FILE |
697 | 701 | ||
698 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS | 702 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS |
699 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 703 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< |
700 | $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS | 704 | $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS |
701 | $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< | 705 | $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< |
702 | $(OUTPUT)%.o: %.S | 706 | $(OUTPUT)%.o: %.S |
703 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 707 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< |
704 | 708 | ||
705 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS | 709 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS |
706 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 710 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ |
707 | '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ | 711 | '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ |
708 | '-DBINDIR="$(bindir_relative_SQ)"' \ | 712 | '-DBINDIR="$(bindir_relative_SQ)"' \ |
709 | '-DPREFIX="$(prefix_SQ)"' \ | 713 | '-DPREFIX="$(prefix_SQ)"' \ |
710 | $< | 714 | $< |
711 | 715 | ||
712 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS | 716 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS |
713 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 717 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
714 | 718 | ||
715 | $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS | 719 | $(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS |
716 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 720 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
717 | 721 | ||
718 | $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS | 722 | $(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS |
719 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 723 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
720 | 724 | ||
721 | $(OUTPUT)util/ui/browsers/top.o: util/ui/browsers/top.c $(OUTPUT)PERF-CFLAGS | 725 | $(OUTPUT)util/ui/browsers/top.o: util/ui/browsers/top.c $(OUTPUT)PERF-CFLAGS |
722 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 726 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
723 | 727 | ||
724 | $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS | 728 | $(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS |
725 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 729 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
726 | 730 | ||
727 | $(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS | 731 | $(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS |
728 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 732 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
729 | 733 | ||
730 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS | 734 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS |
731 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 735 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
732 | 736 | ||
733 | $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS | 737 | $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS |
734 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< | 738 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< |
735 | 739 | ||
736 | $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS | 740 | $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS |
737 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< | 741 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< |
738 | 742 | ||
739 | $(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS | 743 | $(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS |
740 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< | 744 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< |
741 | 745 | ||
742 | $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS | 746 | $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS |
743 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< | 747 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< |
744 | 748 | ||
745 | $(OUTPUT)perf-%: %.o $(PERFLIBS) | 749 | $(OUTPUT)perf-%: %.o $(PERFLIBS) |
746 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) | 750 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) |
747 | 751 | ||
748 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) | 752 | $(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H) |
749 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) | 753 | $(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h) |
750 | 754 | ||
751 | # we compile into subdirectories. if the target directory is not the source directory, they might not exists. So | 755 | # we compile into subdirectories. if the target directory is not the source directory, they might not exists. So |
752 | # we depend the various files onto their directories. | 756 | # we depend the various files onto their directories. |
753 | DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h | 757 | DIRECTORY_DEPS = $(LIB_OBJS) $(BUILTIN_OBJS) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h |
754 | $(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS))) | 758 | $(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS))) |
755 | # In the second step, we make a rule to actually create these directories | 759 | # In the second step, we make a rule to actually create these directories |
756 | $(sort $(dir $(DIRECTORY_DEPS))): | 760 | $(sort $(dir $(DIRECTORY_DEPS))): |
757 | $(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null | 761 | $(QUIET_MKDIR)$(MKDIR) -p $@ 2>/dev/null |
758 | 762 | ||
759 | $(LIB_FILE): $(LIB_OBJS) | 763 | $(LIB_FILE): $(LIB_OBJS) |
760 | $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) | 764 | $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) |
761 | 765 | ||
762 | help: | 766 | help: |
763 | @echo 'Perf make targets:' | 767 | @echo 'Perf make targets:' |
764 | @echo ' doc - make *all* documentation (see below)' | 768 | @echo ' doc - make *all* documentation (see below)' |
765 | @echo ' man - make manpage documentation (access with man <foo>)' | 769 | @echo ' man - make manpage documentation (access with man <foo>)' |
766 | @echo ' html - make html documentation' | 770 | @echo ' html - make html documentation' |
767 | @echo ' info - make GNU info documentation (access with info <foo>)' | 771 | @echo ' info - make GNU info documentation (access with info <foo>)' |
768 | @echo ' pdf - make pdf documentation' | 772 | @echo ' pdf - make pdf documentation' |
769 | @echo ' TAGS - use etags to make tag information for source browsing' | 773 | @echo ' TAGS - use etags to make tag information for source browsing' |
770 | @echo ' tags - use ctags to make tag information for source browsing' | 774 | @echo ' tags - use ctags to make tag information for source browsing' |
771 | @echo ' cscope - use cscope to make interactive browsing database' | 775 | @echo ' cscope - use cscope to make interactive browsing database' |
772 | @echo '' | 776 | @echo '' |
773 | @echo 'Perf install targets:' | 777 | @echo 'Perf install targets:' |
774 | @echo ' NOTE: documentation build requires asciidoc, xmlto packages to be installed' | 778 | @echo ' NOTE: documentation build requires asciidoc, xmlto packages to be installed' |
775 | @echo ' HINT: use "make prefix=<path> <install target>" to install to a particular' | 779 | @echo ' HINT: use "make prefix=<path> <install target>" to install to a particular' |
776 | @echo ' path like make prefix=/usr/local install install-doc' | 780 | @echo ' path like make prefix=/usr/local install install-doc' |
777 | @echo ' install - install compiled binaries' | 781 | @echo ' install - install compiled binaries' |
778 | @echo ' install-doc - install *all* documentation' | 782 | @echo ' install-doc - install *all* documentation' |
779 | @echo ' install-man - install manpage documentation' | 783 | @echo ' install-man - install manpage documentation' |
780 | @echo ' install-html - install html documentation' | 784 | @echo ' install-html - install html documentation' |
781 | @echo ' install-info - install GNU info documentation' | 785 | @echo ' install-info - install GNU info documentation' |
782 | @echo ' install-pdf - install pdf documentation' | 786 | @echo ' install-pdf - install pdf documentation' |
783 | @echo '' | 787 | @echo '' |
784 | @echo ' quick-install-doc - alias for quick-install-man' | 788 | @echo ' quick-install-doc - alias for quick-install-man' |
785 | @echo ' quick-install-man - install the documentation quickly' | 789 | @echo ' quick-install-man - install the documentation quickly' |
786 | @echo ' quick-install-html - install the html documentation quickly' | 790 | @echo ' quick-install-html - install the html documentation quickly' |
787 | @echo '' | 791 | @echo '' |
788 | @echo 'Perf maintainer targets:' | 792 | @echo 'Perf maintainer targets:' |
789 | @echo ' distclean - alias to clean' | 793 | @echo ' distclean - alias to clean' |
790 | @echo ' clean - clean all binary objects and build output' | 794 | @echo ' clean - clean all binary objects and build output' |
791 | 795 | ||
792 | doc: | 796 | doc: |
793 | $(MAKE) -C Documentation all | 797 | $(MAKE) -C Documentation all |
794 | 798 | ||
795 | man: | 799 | man: |
796 | $(MAKE) -C Documentation man | 800 | $(MAKE) -C Documentation man |
797 | 801 | ||
798 | html: | 802 | html: |
799 | $(MAKE) -C Documentation html | 803 | $(MAKE) -C Documentation html |
800 | 804 | ||
801 | info: | 805 | info: |
802 | $(MAKE) -C Documentation info | 806 | $(MAKE) -C Documentation info |
803 | 807 | ||
804 | pdf: | 808 | pdf: |
805 | $(MAKE) -C Documentation pdf | 809 | $(MAKE) -C Documentation pdf |
806 | 810 | ||
807 | TAGS: | 811 | TAGS: |
808 | $(RM) TAGS | 812 | $(RM) TAGS |
809 | $(FIND) . -name '*.[hcS]' -print | xargs etags -a | 813 | $(FIND) . -name '*.[hcS]' -print | xargs etags -a |
810 | 814 | ||
811 | tags: | 815 | tags: |
812 | $(RM) tags | 816 | $(RM) tags |
813 | $(FIND) . -name '*.[hcS]' -print | xargs ctags -a | 817 | $(FIND) . -name '*.[hcS]' -print | xargs ctags -a |
814 | 818 | ||
815 | cscope: | 819 | cscope: |
816 | $(RM) cscope* | 820 | $(RM) cscope* |
817 | $(FIND) . -name '*.[hcS]' -print | xargs cscope -b | 821 | $(FIND) . -name '*.[hcS]' -print | xargs cscope -b |
818 | 822 | ||
819 | ### Detect prefix changes | 823 | ### Detect prefix changes |
820 | TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\ | 824 | TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\ |
821 | $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) | 825 | $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ) |
822 | 826 | ||
823 | $(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS | 827 | $(OUTPUT)PERF-CFLAGS: .FORCE-PERF-CFLAGS |
824 | @FLAGS='$(TRACK_CFLAGS)'; \ | 828 | @FLAGS='$(TRACK_CFLAGS)'; \ |
825 | if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \ | 829 | if test x"$$FLAGS" != x"`cat $(OUTPUT)PERF-CFLAGS 2>/dev/null`" ; then \ |
826 | echo 1>&2 " * new build flags or prefix"; \ | 830 | echo 1>&2 " * new build flags or prefix"; \ |
827 | echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ | 831 | echo "$$FLAGS" >$(OUTPUT)PERF-CFLAGS; \ |
828 | fi | 832 | fi |
829 | 833 | ||
830 | ### Testing rules | 834 | ### Testing rules |
831 | 835 | ||
832 | # GNU make supports exporting all variables by "export" without parameters. | 836 | # GNU make supports exporting all variables by "export" without parameters. |
833 | # However, the environment gets quite big, and some programs have problems | 837 | # However, the environment gets quite big, and some programs have problems |
834 | # with that. | 838 | # with that. |
835 | 839 | ||
836 | check: $(OUTPUT)common-cmds.h | 840 | check: $(OUTPUT)common-cmds.h |
837 | if sparse; \ | 841 | if sparse; \ |
838 | then \ | 842 | then \ |
839 | for i in *.c */*.c; \ | 843 | for i in *.c */*.c; \ |
840 | do \ | 844 | do \ |
841 | sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \ | 845 | sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \ |
842 | done; \ | 846 | done; \ |
843 | else \ | 847 | else \ |
844 | exit 1; \ | 848 | exit 1; \ |
845 | fi | 849 | fi |
846 | 850 | ||
847 | ### Installation rules | 851 | ### Installation rules |
848 | 852 | ||
849 | ifneq ($(filter /%,$(firstword $(perfexecdir))),) | 853 | ifneq ($(filter /%,$(firstword $(perfexecdir))),) |
850 | perfexec_instdir = $(perfexecdir) | 854 | perfexec_instdir = $(perfexecdir) |
851 | else | 855 | else |
852 | perfexec_instdir = $(prefix)/$(perfexecdir) | 856 | perfexec_instdir = $(prefix)/$(perfexecdir) |
853 | endif | 857 | endif |
854 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) | 858 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) |
855 | 859 | ||
856 | install: all | 860 | install: all |
857 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' | 861 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' |
858 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' | 862 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' |
859 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 863 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
860 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 864 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
861 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 865 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
862 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 866 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
863 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' | 867 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' |
864 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 868 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
865 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' | 869 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' |
866 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 870 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
867 | $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' | 871 | $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' |
868 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' | 872 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' |
869 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 873 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
870 | 874 | ||
875 | install-python_ext: | ||
876 | $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' | ||
877 | |||
871 | install-doc: | 878 | install-doc: |
872 | $(MAKE) -C Documentation install | 879 | $(MAKE) -C Documentation install |
873 | 880 | ||
874 | install-man: | 881 | install-man: |
875 | $(MAKE) -C Documentation install-man | 882 | $(MAKE) -C Documentation install-man |
876 | 883 | ||
877 | install-html: | 884 | install-html: |
878 | $(MAKE) -C Documentation install-html | 885 | $(MAKE) -C Documentation install-html |
879 | 886 | ||
880 | install-info: | 887 | install-info: |
881 | $(MAKE) -C Documentation install-info | 888 | $(MAKE) -C Documentation install-info |
882 | 889 | ||
883 | install-pdf: | 890 | install-pdf: |
884 | $(MAKE) -C Documentation install-pdf | 891 | $(MAKE) -C Documentation install-pdf |
885 | 892 | ||
886 | quick-install-doc: | 893 | quick-install-doc: |
887 | $(MAKE) -C Documentation quick-install | 894 | $(MAKE) -C Documentation quick-install |
888 | 895 | ||
889 | quick-install-man: | 896 | quick-install-man: |
890 | $(MAKE) -C Documentation quick-install-man | 897 | $(MAKE) -C Documentation quick-install-man |
891 | 898 | ||
892 | quick-install-html: | 899 | quick-install-html: |
893 | $(MAKE) -C Documentation quick-install-html | 900 | $(MAKE) -C Documentation quick-install-html |
894 | 901 | ||
895 | ### Cleaning rules | 902 | ### Cleaning rules |
896 | 903 | ||
897 | clean: | 904 | clean: |
898 | $(RM) $(OUTPUT){*.o,*/*.o,*/*/*.o,*/*/*/*.o,$(LIB_FILE),perf-archive} | 905 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) |
899 | $(RM) $(ALL_PROGRAMS) perf | 906 | $(RM) $(ALL_PROGRAMS) perf |
900 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 907 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
901 | $(MAKE) -C Documentation/ clean | 908 | $(MAKE) -C Documentation/ clean |
902 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS | 909 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS |
903 | $(python-clean) | 910 | $(python-clean) |
904 | 911 | ||
905 | .PHONY: all install clean strip | 912 | .PHONY: all install clean strip |
906 | .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell | 913 | .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell |
907 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS | 914 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS |
908 | 915 |
tools/perf/builtin-lock.c
1 | #include "builtin.h" | 1 | #include "builtin.h" |
2 | #include "perf.h" | 2 | #include "perf.h" |
3 | 3 | ||
4 | #include "util/util.h" | 4 | #include "util/util.h" |
5 | #include "util/cache.h" | 5 | #include "util/cache.h" |
6 | #include "util/symbol.h" | 6 | #include "util/symbol.h" |
7 | #include "util/thread.h" | 7 | #include "util/thread.h" |
8 | #include "util/header.h" | 8 | #include "util/header.h" |
9 | 9 | ||
10 | #include "util/parse-options.h" | 10 | #include "util/parse-options.h" |
11 | #include "util/trace-event.h" | 11 | #include "util/trace-event.h" |
12 | 12 | ||
13 | #include "util/debug.h" | 13 | #include "util/debug.h" |
14 | #include "util/session.h" | 14 | #include "util/session.h" |
15 | 15 | ||
16 | #include <sys/types.h> | 16 | #include <sys/types.h> |
17 | #include <sys/prctl.h> | 17 | #include <sys/prctl.h> |
18 | #include <semaphore.h> | 18 | #include <semaphore.h> |
19 | #include <pthread.h> | 19 | #include <pthread.h> |
20 | #include <math.h> | 20 | #include <math.h> |
21 | #include <limits.h> | 21 | #include <limits.h> |
22 | 22 | ||
23 | #include <linux/list.h> | 23 | #include <linux/list.h> |
24 | #include <linux/hash.h> | 24 | #include <linux/hash.h> |
25 | 25 | ||
26 | static struct perf_session *session; | 26 | static struct perf_session *session; |
27 | 27 | ||
28 | /* based on kernel/lockdep.c */ | 28 | /* based on kernel/lockdep.c */ |
29 | #define LOCKHASH_BITS 12 | 29 | #define LOCKHASH_BITS 12 |
30 | #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) | 30 | #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) |
31 | 31 | ||
32 | static struct list_head lockhash_table[LOCKHASH_SIZE]; | 32 | static struct list_head lockhash_table[LOCKHASH_SIZE]; |
33 | 33 | ||
34 | #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) | 34 | #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) |
35 | #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) | 35 | #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) |
36 | 36 | ||
37 | struct lock_stat { | 37 | struct lock_stat { |
38 | struct list_head hash_entry; | 38 | struct list_head hash_entry; |
39 | struct rb_node rb; /* used for sorting */ | 39 | struct rb_node rb; /* used for sorting */ |
40 | 40 | ||
41 | /* | 41 | /* |
42 | * FIXME: raw_field_value() returns unsigned long long, | 42 | * FIXME: raw_field_value() returns unsigned long long, |
43 | * so address of lockdep_map should be dealed as 64bit. | 43 | * so address of lockdep_map should be dealed as 64bit. |
44 | * Is there more better solution? | 44 | * Is there more better solution? |
45 | */ | 45 | */ |
46 | void *addr; /* address of lockdep_map, used as ID */ | 46 | void *addr; /* address of lockdep_map, used as ID */ |
47 | char *name; /* for strcpy(), we cannot use const */ | 47 | char *name; /* for strcpy(), we cannot use const */ |
48 | 48 | ||
49 | unsigned int nr_acquire; | 49 | unsigned int nr_acquire; |
50 | unsigned int nr_acquired; | 50 | unsigned int nr_acquired; |
51 | unsigned int nr_contended; | 51 | unsigned int nr_contended; |
52 | unsigned int nr_release; | 52 | unsigned int nr_release; |
53 | 53 | ||
54 | unsigned int nr_readlock; | 54 | unsigned int nr_readlock; |
55 | unsigned int nr_trylock; | 55 | unsigned int nr_trylock; |
56 | /* these times are in nano sec. */ | 56 | /* these times are in nano sec. */ |
57 | u64 wait_time_total; | 57 | u64 wait_time_total; |
58 | u64 wait_time_min; | 58 | u64 wait_time_min; |
59 | u64 wait_time_max; | 59 | u64 wait_time_max; |
60 | 60 | ||
61 | int discard; /* flag of blacklist */ | 61 | int discard; /* flag of blacklist */ |
62 | }; | 62 | }; |
63 | 63 | ||
64 | /* | 64 | /* |
65 | * States of lock_seq_stat | 65 | * States of lock_seq_stat |
66 | * | 66 | * |
67 | * UNINITIALIZED is required for detecting first event of acquire. | 67 | * UNINITIALIZED is required for detecting first event of acquire. |
68 | * As the nature of lock events, there is no guarantee | 68 | * As the nature of lock events, there is no guarantee |
69 | * that the first event for the locks are acquire, | 69 | * that the first event for the locks are acquire, |
70 | * it can be acquired, contended or release. | 70 | * it can be acquired, contended or release. |
71 | */ | 71 | */ |
72 | #define SEQ_STATE_UNINITIALIZED 0 /* initial state */ | 72 | #define SEQ_STATE_UNINITIALIZED 0 /* initial state */ |
73 | #define SEQ_STATE_RELEASED 1 | 73 | #define SEQ_STATE_RELEASED 1 |
74 | #define SEQ_STATE_ACQUIRING 2 | 74 | #define SEQ_STATE_ACQUIRING 2 |
75 | #define SEQ_STATE_ACQUIRED 3 | 75 | #define SEQ_STATE_ACQUIRED 3 |
76 | #define SEQ_STATE_READ_ACQUIRED 4 | 76 | #define SEQ_STATE_READ_ACQUIRED 4 |
77 | #define SEQ_STATE_CONTENDED 5 | 77 | #define SEQ_STATE_CONTENDED 5 |
78 | 78 | ||
79 | /* | 79 | /* |
80 | * MAX_LOCK_DEPTH | 80 | * MAX_LOCK_DEPTH |
81 | * Imported from include/linux/sched.h. | 81 | * Imported from include/linux/sched.h. |
82 | * Should this be synchronized? | 82 | * Should this be synchronized? |
83 | */ | 83 | */ |
84 | #define MAX_LOCK_DEPTH 48 | 84 | #define MAX_LOCK_DEPTH 48 |
85 | 85 | ||
86 | /* | 86 | /* |
87 | * struct lock_seq_stat: | 87 | * struct lock_seq_stat: |
88 | * Place to put on state of one lock sequence | 88 | * Place to put on state of one lock sequence |
89 | * 1) acquire -> acquired -> release | 89 | * 1) acquire -> acquired -> release |
90 | * 2) acquire -> contended -> acquired -> release | 90 | * 2) acquire -> contended -> acquired -> release |
91 | * 3) acquire (with read or try) -> release | 91 | * 3) acquire (with read or try) -> release |
92 | * 4) Are there other patterns? | 92 | * 4) Are there other patterns? |
93 | */ | 93 | */ |
94 | struct lock_seq_stat { | 94 | struct lock_seq_stat { |
95 | struct list_head list; | 95 | struct list_head list; |
96 | int state; | 96 | int state; |
97 | u64 prev_event_time; | 97 | u64 prev_event_time; |
98 | void *addr; | 98 | void *addr; |
99 | 99 | ||
100 | int read_count; | 100 | int read_count; |
101 | }; | 101 | }; |
102 | 102 | ||
103 | struct thread_stat { | 103 | struct thread_stat { |
104 | struct rb_node rb; | 104 | struct rb_node rb; |
105 | 105 | ||
106 | u32 tid; | 106 | u32 tid; |
107 | struct list_head seq_list; | 107 | struct list_head seq_list; |
108 | }; | 108 | }; |
109 | 109 | ||
110 | static struct rb_root thread_stats; | 110 | static struct rb_root thread_stats; |
111 | 111 | ||
112 | static struct thread_stat *thread_stat_find(u32 tid) | 112 | static struct thread_stat *thread_stat_find(u32 tid) |
113 | { | 113 | { |
114 | struct rb_node *node; | 114 | struct rb_node *node; |
115 | struct thread_stat *st; | 115 | struct thread_stat *st; |
116 | 116 | ||
117 | node = thread_stats.rb_node; | 117 | node = thread_stats.rb_node; |
118 | while (node) { | 118 | while (node) { |
119 | st = container_of(node, struct thread_stat, rb); | 119 | st = container_of(node, struct thread_stat, rb); |
120 | if (st->tid == tid) | 120 | if (st->tid == tid) |
121 | return st; | 121 | return st; |
122 | else if (tid < st->tid) | 122 | else if (tid < st->tid) |
123 | node = node->rb_left; | 123 | node = node->rb_left; |
124 | else | 124 | else |
125 | node = node->rb_right; | 125 | node = node->rb_right; |
126 | } | 126 | } |
127 | 127 | ||
128 | return NULL; | 128 | return NULL; |
129 | } | 129 | } |
130 | 130 | ||
131 | static void thread_stat_insert(struct thread_stat *new) | 131 | static void thread_stat_insert(struct thread_stat *new) |
132 | { | 132 | { |
133 | struct rb_node **rb = &thread_stats.rb_node; | 133 | struct rb_node **rb = &thread_stats.rb_node; |
134 | struct rb_node *parent = NULL; | 134 | struct rb_node *parent = NULL; |
135 | struct thread_stat *p; | 135 | struct thread_stat *p; |
136 | 136 | ||
137 | while (*rb) { | 137 | while (*rb) { |
138 | p = container_of(*rb, struct thread_stat, rb); | 138 | p = container_of(*rb, struct thread_stat, rb); |
139 | parent = *rb; | 139 | parent = *rb; |
140 | 140 | ||
141 | if (new->tid < p->tid) | 141 | if (new->tid < p->tid) |
142 | rb = &(*rb)->rb_left; | 142 | rb = &(*rb)->rb_left; |
143 | else if (new->tid > p->tid) | 143 | else if (new->tid > p->tid) |
144 | rb = &(*rb)->rb_right; | 144 | rb = &(*rb)->rb_right; |
145 | else | 145 | else |
146 | BUG_ON("inserting invalid thread_stat\n"); | 146 | BUG_ON("inserting invalid thread_stat\n"); |
147 | } | 147 | } |
148 | 148 | ||
149 | rb_link_node(&new->rb, parent, rb); | 149 | rb_link_node(&new->rb, parent, rb); |
150 | rb_insert_color(&new->rb, &thread_stats); | 150 | rb_insert_color(&new->rb, &thread_stats); |
151 | } | 151 | } |
152 | 152 | ||
153 | static struct thread_stat *thread_stat_findnew_after_first(u32 tid) | 153 | static struct thread_stat *thread_stat_findnew_after_first(u32 tid) |
154 | { | 154 | { |
155 | struct thread_stat *st; | 155 | struct thread_stat *st; |
156 | 156 | ||
157 | st = thread_stat_find(tid); | 157 | st = thread_stat_find(tid); |
158 | if (st) | 158 | if (st) |
159 | return st; | 159 | return st; |
160 | 160 | ||
161 | st = zalloc(sizeof(struct thread_stat)); | 161 | st = zalloc(sizeof(struct thread_stat)); |
162 | if (!st) | 162 | if (!st) |
163 | die("memory allocation failed\n"); | 163 | die("memory allocation failed\n"); |
164 | 164 | ||
165 | st->tid = tid; | 165 | st->tid = tid; |
166 | INIT_LIST_HEAD(&st->seq_list); | 166 | INIT_LIST_HEAD(&st->seq_list); |
167 | 167 | ||
168 | thread_stat_insert(st); | 168 | thread_stat_insert(st); |
169 | 169 | ||
170 | return st; | 170 | return st; |
171 | } | 171 | } |
172 | 172 | ||
173 | static struct thread_stat *thread_stat_findnew_first(u32 tid); | 173 | static struct thread_stat *thread_stat_findnew_first(u32 tid); |
174 | static struct thread_stat *(*thread_stat_findnew)(u32 tid) = | 174 | static struct thread_stat *(*thread_stat_findnew)(u32 tid) = |
175 | thread_stat_findnew_first; | 175 | thread_stat_findnew_first; |
176 | 176 | ||
177 | static struct thread_stat *thread_stat_findnew_first(u32 tid) | 177 | static struct thread_stat *thread_stat_findnew_first(u32 tid) |
178 | { | 178 | { |
179 | struct thread_stat *st; | 179 | struct thread_stat *st; |
180 | 180 | ||
181 | st = zalloc(sizeof(struct thread_stat)); | 181 | st = zalloc(sizeof(struct thread_stat)); |
182 | if (!st) | 182 | if (!st) |
183 | die("memory allocation failed\n"); | 183 | die("memory allocation failed\n"); |
184 | st->tid = tid; | 184 | st->tid = tid; |
185 | INIT_LIST_HEAD(&st->seq_list); | 185 | INIT_LIST_HEAD(&st->seq_list); |
186 | 186 | ||
187 | rb_link_node(&st->rb, NULL, &thread_stats.rb_node); | 187 | rb_link_node(&st->rb, NULL, &thread_stats.rb_node); |
188 | rb_insert_color(&st->rb, &thread_stats); | 188 | rb_insert_color(&st->rb, &thread_stats); |
189 | 189 | ||
190 | thread_stat_findnew = thread_stat_findnew_after_first; | 190 | thread_stat_findnew = thread_stat_findnew_after_first; |
191 | return st; | 191 | return st; |
192 | } | 192 | } |
193 | 193 | ||
194 | /* build simple key function one is bigger than two */ | 194 | /* build simple key function one is bigger than two */ |
195 | #define SINGLE_KEY(member) \ | 195 | #define SINGLE_KEY(member) \ |
196 | static int lock_stat_key_ ## member(struct lock_stat *one, \ | 196 | static int lock_stat_key_ ## member(struct lock_stat *one, \ |
197 | struct lock_stat *two) \ | 197 | struct lock_stat *two) \ |
198 | { \ | 198 | { \ |
199 | return one->member > two->member; \ | 199 | return one->member > two->member; \ |
200 | } | 200 | } |
201 | 201 | ||
202 | SINGLE_KEY(nr_acquired) | 202 | SINGLE_KEY(nr_acquired) |
203 | SINGLE_KEY(nr_contended) | 203 | SINGLE_KEY(nr_contended) |
204 | SINGLE_KEY(wait_time_total) | 204 | SINGLE_KEY(wait_time_total) |
205 | SINGLE_KEY(wait_time_max) | 205 | SINGLE_KEY(wait_time_max) |
206 | 206 | ||
207 | static int lock_stat_key_wait_time_min(struct lock_stat *one, | 207 | static int lock_stat_key_wait_time_min(struct lock_stat *one, |
208 | struct lock_stat *two) | 208 | struct lock_stat *two) |
209 | { | 209 | { |
210 | u64 s1 = one->wait_time_min; | 210 | u64 s1 = one->wait_time_min; |
211 | u64 s2 = two->wait_time_min; | 211 | u64 s2 = two->wait_time_min; |
212 | if (s1 == ULLONG_MAX) | 212 | if (s1 == ULLONG_MAX) |
213 | s1 = 0; | 213 | s1 = 0; |
214 | if (s2 == ULLONG_MAX) | 214 | if (s2 == ULLONG_MAX) |
215 | s2 = 0; | 215 | s2 = 0; |
216 | return s1 > s2; | 216 | return s1 > s2; |
217 | } | 217 | } |
218 | 218 | ||
219 | struct lock_key { | 219 | struct lock_key { |
220 | /* | 220 | /* |
221 | * name: the value for specify by user | 221 | * name: the value for specify by user |
222 | * this should be simpler than raw name of member | 222 | * this should be simpler than raw name of member |
223 | * e.g. nr_acquired -> acquired, wait_time_total -> wait_total | 223 | * e.g. nr_acquired -> acquired, wait_time_total -> wait_total |
224 | */ | 224 | */ |
225 | const char *name; | 225 | const char *name; |
226 | int (*key)(struct lock_stat*, struct lock_stat*); | 226 | int (*key)(struct lock_stat*, struct lock_stat*); |
227 | }; | 227 | }; |
228 | 228 | ||
229 | static const char *sort_key = "acquired"; | 229 | static const char *sort_key = "acquired"; |
230 | 230 | ||
231 | static int (*compare)(struct lock_stat *, struct lock_stat *); | 231 | static int (*compare)(struct lock_stat *, struct lock_stat *); |
232 | 232 | ||
233 | static struct rb_root result; /* place to store sorted data */ | 233 | static struct rb_root result; /* place to store sorted data */ |
234 | 234 | ||
235 | #define DEF_KEY_LOCK(name, fn_suffix) \ | 235 | #define DEF_KEY_LOCK(name, fn_suffix) \ |
236 | { #name, lock_stat_key_ ## fn_suffix } | 236 | { #name, lock_stat_key_ ## fn_suffix } |
237 | struct lock_key keys[] = { | 237 | struct lock_key keys[] = { |
238 | DEF_KEY_LOCK(acquired, nr_acquired), | 238 | DEF_KEY_LOCK(acquired, nr_acquired), |
239 | DEF_KEY_LOCK(contended, nr_contended), | 239 | DEF_KEY_LOCK(contended, nr_contended), |
240 | DEF_KEY_LOCK(wait_total, wait_time_total), | 240 | DEF_KEY_LOCK(wait_total, wait_time_total), |
241 | DEF_KEY_LOCK(wait_min, wait_time_min), | 241 | DEF_KEY_LOCK(wait_min, wait_time_min), |
242 | DEF_KEY_LOCK(wait_max, wait_time_max), | 242 | DEF_KEY_LOCK(wait_max, wait_time_max), |
243 | 243 | ||
244 | /* extra comparisons much complicated should be here */ | 244 | /* extra comparisons much complicated should be here */ |
245 | 245 | ||
246 | { NULL, NULL } | 246 | { NULL, NULL } |
247 | }; | 247 | }; |
248 | 248 | ||
249 | static void select_key(void) | 249 | static void select_key(void) |
250 | { | 250 | { |
251 | int i; | 251 | int i; |
252 | 252 | ||
253 | for (i = 0; keys[i].name; i++) { | 253 | for (i = 0; keys[i].name; i++) { |
254 | if (!strcmp(keys[i].name, sort_key)) { | 254 | if (!strcmp(keys[i].name, sort_key)) { |
255 | compare = keys[i].key; | 255 | compare = keys[i].key; |
256 | return; | 256 | return; |
257 | } | 257 | } |
258 | } | 258 | } |
259 | 259 | ||
260 | die("Unknown compare key:%s\n", sort_key); | 260 | die("Unknown compare key:%s\n", sort_key); |
261 | } | 261 | } |
262 | 262 | ||
263 | static void insert_to_result(struct lock_stat *st, | 263 | static void insert_to_result(struct lock_stat *st, |
264 | int (*bigger)(struct lock_stat *, struct lock_stat *)) | 264 | int (*bigger)(struct lock_stat *, struct lock_stat *)) |
265 | { | 265 | { |
266 | struct rb_node **rb = &result.rb_node; | 266 | struct rb_node **rb = &result.rb_node; |
267 | struct rb_node *parent = NULL; | 267 | struct rb_node *parent = NULL; |
268 | struct lock_stat *p; | 268 | struct lock_stat *p; |
269 | 269 | ||
270 | while (*rb) { | 270 | while (*rb) { |
271 | p = container_of(*rb, struct lock_stat, rb); | 271 | p = container_of(*rb, struct lock_stat, rb); |
272 | parent = *rb; | 272 | parent = *rb; |
273 | 273 | ||
274 | if (bigger(st, p)) | 274 | if (bigger(st, p)) |
275 | rb = &(*rb)->rb_left; | 275 | rb = &(*rb)->rb_left; |
276 | else | 276 | else |
277 | rb = &(*rb)->rb_right; | 277 | rb = &(*rb)->rb_right; |
278 | } | 278 | } |
279 | 279 | ||
280 | rb_link_node(&st->rb, parent, rb); | 280 | rb_link_node(&st->rb, parent, rb); |
281 | rb_insert_color(&st->rb, &result); | 281 | rb_insert_color(&st->rb, &result); |
282 | } | 282 | } |
283 | 283 | ||
284 | /* returns left most element of result, and erase it */ | 284 | /* returns left most element of result, and erase it */ |
285 | static struct lock_stat *pop_from_result(void) | 285 | static struct lock_stat *pop_from_result(void) |
286 | { | 286 | { |
287 | struct rb_node *node = result.rb_node; | 287 | struct rb_node *node = result.rb_node; |
288 | 288 | ||
289 | if (!node) | 289 | if (!node) |
290 | return NULL; | 290 | return NULL; |
291 | 291 | ||
292 | while (node->rb_left) | 292 | while (node->rb_left) |
293 | node = node->rb_left; | 293 | node = node->rb_left; |
294 | 294 | ||
295 | rb_erase(node, &result); | 295 | rb_erase(node, &result); |
296 | return container_of(node, struct lock_stat, rb); | 296 | return container_of(node, struct lock_stat, rb); |
297 | } | 297 | } |
298 | 298 | ||
299 | static struct lock_stat *lock_stat_findnew(void *addr, const char *name) | 299 | static struct lock_stat *lock_stat_findnew(void *addr, const char *name) |
300 | { | 300 | { |
301 | struct list_head *entry = lockhashentry(addr); | 301 | struct list_head *entry = lockhashentry(addr); |
302 | struct lock_stat *ret, *new; | 302 | struct lock_stat *ret, *new; |
303 | 303 | ||
304 | list_for_each_entry(ret, entry, hash_entry) { | 304 | list_for_each_entry(ret, entry, hash_entry) { |
305 | if (ret->addr == addr) | 305 | if (ret->addr == addr) |
306 | return ret; | 306 | return ret; |
307 | } | 307 | } |
308 | 308 | ||
309 | new = zalloc(sizeof(struct lock_stat)); | 309 | new = zalloc(sizeof(struct lock_stat)); |
310 | if (!new) | 310 | if (!new) |
311 | goto alloc_failed; | 311 | goto alloc_failed; |
312 | 312 | ||
313 | new->addr = addr; | 313 | new->addr = addr; |
314 | new->name = zalloc(sizeof(char) * strlen(name) + 1); | 314 | new->name = zalloc(sizeof(char) * strlen(name) + 1); |
315 | if (!new->name) | 315 | if (!new->name) |
316 | goto alloc_failed; | 316 | goto alloc_failed; |
317 | strcpy(new->name, name); | 317 | strcpy(new->name, name); |
318 | 318 | ||
319 | new->wait_time_min = ULLONG_MAX; | 319 | new->wait_time_min = ULLONG_MAX; |
320 | 320 | ||
321 | list_add(&new->hash_entry, entry); | 321 | list_add(&new->hash_entry, entry); |
322 | return new; | 322 | return new; |
323 | 323 | ||
324 | alloc_failed: | 324 | alloc_failed: |
325 | die("memory allocation failed\n"); | 325 | die("memory allocation failed\n"); |
326 | } | 326 | } |
327 | 327 | ||
328 | static char const *input_name = "perf.data"; | 328 | static char const *input_name = "perf.data"; |
329 | 329 | ||
330 | struct raw_event_sample { | 330 | struct raw_event_sample { |
331 | u32 size; | 331 | u32 size; |
332 | char data[0]; | 332 | char data[0]; |
333 | }; | 333 | }; |
334 | 334 | ||
335 | struct trace_acquire_event { | 335 | struct trace_acquire_event { |
336 | void *addr; | 336 | void *addr; |
337 | const char *name; | 337 | const char *name; |
338 | int flag; | 338 | int flag; |
339 | }; | 339 | }; |
340 | 340 | ||
341 | struct trace_acquired_event { | 341 | struct trace_acquired_event { |
342 | void *addr; | 342 | void *addr; |
343 | const char *name; | 343 | const char *name; |
344 | }; | 344 | }; |
345 | 345 | ||
346 | struct trace_contended_event { | 346 | struct trace_contended_event { |
347 | void *addr; | 347 | void *addr; |
348 | const char *name; | 348 | const char *name; |
349 | }; | 349 | }; |
350 | 350 | ||
351 | struct trace_release_event { | 351 | struct trace_release_event { |
352 | void *addr; | 352 | void *addr; |
353 | const char *name; | 353 | const char *name; |
354 | }; | 354 | }; |
355 | 355 | ||
356 | struct trace_lock_handler { | 356 | struct trace_lock_handler { |
357 | void (*acquire_event)(struct trace_acquire_event *, | 357 | void (*acquire_event)(struct trace_acquire_event *, |
358 | struct event *, | 358 | struct event *, |
359 | int cpu, | 359 | int cpu, |
360 | u64 timestamp, | 360 | u64 timestamp, |
361 | struct thread *thread); | 361 | struct thread *thread); |
362 | 362 | ||
363 | void (*acquired_event)(struct trace_acquired_event *, | 363 | void (*acquired_event)(struct trace_acquired_event *, |
364 | struct event *, | 364 | struct event *, |
365 | int cpu, | 365 | int cpu, |
366 | u64 timestamp, | 366 | u64 timestamp, |
367 | struct thread *thread); | 367 | struct thread *thread); |
368 | 368 | ||
369 | void (*contended_event)(struct trace_contended_event *, | 369 | void (*contended_event)(struct trace_contended_event *, |
370 | struct event *, | 370 | struct event *, |
371 | int cpu, | 371 | int cpu, |
372 | u64 timestamp, | 372 | u64 timestamp, |
373 | struct thread *thread); | 373 | struct thread *thread); |
374 | 374 | ||
375 | void (*release_event)(struct trace_release_event *, | 375 | void (*release_event)(struct trace_release_event *, |
376 | struct event *, | 376 | struct event *, |
377 | int cpu, | 377 | int cpu, |
378 | u64 timestamp, | 378 | u64 timestamp, |
379 | struct thread *thread); | 379 | struct thread *thread); |
380 | }; | 380 | }; |
381 | 381 | ||
382 | static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr) | 382 | static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr) |
383 | { | 383 | { |
384 | struct lock_seq_stat *seq; | 384 | struct lock_seq_stat *seq; |
385 | 385 | ||
386 | list_for_each_entry(seq, &ts->seq_list, list) { | 386 | list_for_each_entry(seq, &ts->seq_list, list) { |
387 | if (seq->addr == addr) | 387 | if (seq->addr == addr) |
388 | return seq; | 388 | return seq; |
389 | } | 389 | } |
390 | 390 | ||
391 | seq = zalloc(sizeof(struct lock_seq_stat)); | 391 | seq = zalloc(sizeof(struct lock_seq_stat)); |
392 | if (!seq) | 392 | if (!seq) |
393 | die("Not enough memory\n"); | 393 | die("Not enough memory\n"); |
394 | seq->state = SEQ_STATE_UNINITIALIZED; | 394 | seq->state = SEQ_STATE_UNINITIALIZED; |
395 | seq->addr = addr; | 395 | seq->addr = addr; |
396 | 396 | ||
397 | list_add(&seq->list, &ts->seq_list); | 397 | list_add(&seq->list, &ts->seq_list); |
398 | return seq; | 398 | return seq; |
399 | } | 399 | } |
400 | 400 | ||
401 | enum broken_state { | 401 | enum broken_state { |
402 | BROKEN_ACQUIRE, | 402 | BROKEN_ACQUIRE, |
403 | BROKEN_ACQUIRED, | 403 | BROKEN_ACQUIRED, |
404 | BROKEN_CONTENDED, | 404 | BROKEN_CONTENDED, |
405 | BROKEN_RELEASE, | 405 | BROKEN_RELEASE, |
406 | BROKEN_MAX, | 406 | BROKEN_MAX, |
407 | }; | 407 | }; |
408 | 408 | ||
409 | static int bad_hist[BROKEN_MAX]; | 409 | static int bad_hist[BROKEN_MAX]; |
410 | 410 | ||
411 | enum acquire_flags { | 411 | enum acquire_flags { |
412 | TRY_LOCK = 1, | 412 | TRY_LOCK = 1, |
413 | READ_LOCK = 2, | 413 | READ_LOCK = 2, |
414 | }; | 414 | }; |
415 | 415 | ||
416 | static void | 416 | static void |
417 | report_lock_acquire_event(struct trace_acquire_event *acquire_event, | 417 | report_lock_acquire_event(struct trace_acquire_event *acquire_event, |
418 | struct event *__event __used, | 418 | struct event *__event __used, |
419 | int cpu __used, | 419 | int cpu __used, |
420 | u64 timestamp __used, | 420 | u64 timestamp __used, |
421 | struct thread *thread __used) | 421 | struct thread *thread __used) |
422 | { | 422 | { |
423 | struct lock_stat *ls; | 423 | struct lock_stat *ls; |
424 | struct thread_stat *ts; | 424 | struct thread_stat *ts; |
425 | struct lock_seq_stat *seq; | 425 | struct lock_seq_stat *seq; |
426 | 426 | ||
427 | ls = lock_stat_findnew(acquire_event->addr, acquire_event->name); | 427 | ls = lock_stat_findnew(acquire_event->addr, acquire_event->name); |
428 | if (ls->discard) | 428 | if (ls->discard) |
429 | return; | 429 | return; |
430 | 430 | ||
431 | ts = thread_stat_findnew(thread->pid); | 431 | ts = thread_stat_findnew(thread->pid); |
432 | seq = get_seq(ts, acquire_event->addr); | 432 | seq = get_seq(ts, acquire_event->addr); |
433 | 433 | ||
434 | switch (seq->state) { | 434 | switch (seq->state) { |
435 | case SEQ_STATE_UNINITIALIZED: | 435 | case SEQ_STATE_UNINITIALIZED: |
436 | case SEQ_STATE_RELEASED: | 436 | case SEQ_STATE_RELEASED: |
437 | if (!acquire_event->flag) { | 437 | if (!acquire_event->flag) { |
438 | seq->state = SEQ_STATE_ACQUIRING; | 438 | seq->state = SEQ_STATE_ACQUIRING; |
439 | } else { | 439 | } else { |
440 | if (acquire_event->flag & TRY_LOCK) | 440 | if (acquire_event->flag & TRY_LOCK) |
441 | ls->nr_trylock++; | 441 | ls->nr_trylock++; |
442 | if (acquire_event->flag & READ_LOCK) | 442 | if (acquire_event->flag & READ_LOCK) |
443 | ls->nr_readlock++; | 443 | ls->nr_readlock++; |
444 | seq->state = SEQ_STATE_READ_ACQUIRED; | 444 | seq->state = SEQ_STATE_READ_ACQUIRED; |
445 | seq->read_count = 1; | 445 | seq->read_count = 1; |
446 | ls->nr_acquired++; | 446 | ls->nr_acquired++; |
447 | } | 447 | } |
448 | break; | 448 | break; |
449 | case SEQ_STATE_READ_ACQUIRED: | 449 | case SEQ_STATE_READ_ACQUIRED: |
450 | if (acquire_event->flag & READ_LOCK) { | 450 | if (acquire_event->flag & READ_LOCK) { |
451 | seq->read_count++; | 451 | seq->read_count++; |
452 | ls->nr_acquired++; | 452 | ls->nr_acquired++; |
453 | goto end; | 453 | goto end; |
454 | } else { | 454 | } else { |
455 | goto broken; | 455 | goto broken; |
456 | } | 456 | } |
457 | break; | 457 | break; |
458 | case SEQ_STATE_ACQUIRED: | 458 | case SEQ_STATE_ACQUIRED: |
459 | case SEQ_STATE_ACQUIRING: | 459 | case SEQ_STATE_ACQUIRING: |
460 | case SEQ_STATE_CONTENDED: | 460 | case SEQ_STATE_CONTENDED: |
461 | broken: | 461 | broken: |
462 | /* broken lock sequence, discard it */ | 462 | /* broken lock sequence, discard it */ |
463 | ls->discard = 1; | 463 | ls->discard = 1; |
464 | bad_hist[BROKEN_ACQUIRE]++; | 464 | bad_hist[BROKEN_ACQUIRE]++; |
465 | list_del(&seq->list); | 465 | list_del(&seq->list); |
466 | free(seq); | 466 | free(seq); |
467 | goto end; | 467 | goto end; |
468 | break; | 468 | break; |
469 | default: | 469 | default: |
470 | BUG_ON("Unknown state of lock sequence found!\n"); | 470 | BUG_ON("Unknown state of lock sequence found!\n"); |
471 | break; | 471 | break; |
472 | } | 472 | } |
473 | 473 | ||
474 | ls->nr_acquire++; | 474 | ls->nr_acquire++; |
475 | seq->prev_event_time = timestamp; | 475 | seq->prev_event_time = timestamp; |
476 | end: | 476 | end: |
477 | return; | 477 | return; |
478 | } | 478 | } |
479 | 479 | ||
480 | static void | 480 | static void |
481 | report_lock_acquired_event(struct trace_acquired_event *acquired_event, | 481 | report_lock_acquired_event(struct trace_acquired_event *acquired_event, |
482 | struct event *__event __used, | 482 | struct event *__event __used, |
483 | int cpu __used, | 483 | int cpu __used, |
484 | u64 timestamp __used, | 484 | u64 timestamp __used, |
485 | struct thread *thread __used) | 485 | struct thread *thread __used) |
486 | { | 486 | { |
487 | struct lock_stat *ls; | 487 | struct lock_stat *ls; |
488 | struct thread_stat *ts; | 488 | struct thread_stat *ts; |
489 | struct lock_seq_stat *seq; | 489 | struct lock_seq_stat *seq; |
490 | u64 contended_term; | 490 | u64 contended_term; |
491 | 491 | ||
492 | ls = lock_stat_findnew(acquired_event->addr, acquired_event->name); | 492 | ls = lock_stat_findnew(acquired_event->addr, acquired_event->name); |
493 | if (ls->discard) | 493 | if (ls->discard) |
494 | return; | 494 | return; |
495 | 495 | ||
496 | ts = thread_stat_findnew(thread->pid); | 496 | ts = thread_stat_findnew(thread->pid); |
497 | seq = get_seq(ts, acquired_event->addr); | 497 | seq = get_seq(ts, acquired_event->addr); |
498 | 498 | ||
499 | switch (seq->state) { | 499 | switch (seq->state) { |
500 | case SEQ_STATE_UNINITIALIZED: | 500 | case SEQ_STATE_UNINITIALIZED: |
501 | /* orphan event, do nothing */ | 501 | /* orphan event, do nothing */ |
502 | return; | 502 | return; |
503 | case SEQ_STATE_ACQUIRING: | 503 | case SEQ_STATE_ACQUIRING: |
504 | break; | 504 | break; |
505 | case SEQ_STATE_CONTENDED: | 505 | case SEQ_STATE_CONTENDED: |
506 | contended_term = timestamp - seq->prev_event_time; | 506 | contended_term = timestamp - seq->prev_event_time; |
507 | ls->wait_time_total += contended_term; | 507 | ls->wait_time_total += contended_term; |
508 | if (contended_term < ls->wait_time_min) | 508 | if (contended_term < ls->wait_time_min) |
509 | ls->wait_time_min = contended_term; | 509 | ls->wait_time_min = contended_term; |
510 | if (ls->wait_time_max < contended_term) | 510 | if (ls->wait_time_max < contended_term) |
511 | ls->wait_time_max = contended_term; | 511 | ls->wait_time_max = contended_term; |
512 | break; | 512 | break; |
513 | case SEQ_STATE_RELEASED: | 513 | case SEQ_STATE_RELEASED: |
514 | case SEQ_STATE_ACQUIRED: | 514 | case SEQ_STATE_ACQUIRED: |
515 | case SEQ_STATE_READ_ACQUIRED: | 515 | case SEQ_STATE_READ_ACQUIRED: |
516 | /* broken lock sequence, discard it */ | 516 | /* broken lock sequence, discard it */ |
517 | ls->discard = 1; | 517 | ls->discard = 1; |
518 | bad_hist[BROKEN_ACQUIRED]++; | 518 | bad_hist[BROKEN_ACQUIRED]++; |
519 | list_del(&seq->list); | 519 | list_del(&seq->list); |
520 | free(seq); | 520 | free(seq); |
521 | goto end; | 521 | goto end; |
522 | break; | 522 | break; |
523 | 523 | ||
524 | default: | 524 | default: |
525 | BUG_ON("Unknown state of lock sequence found!\n"); | 525 | BUG_ON("Unknown state of lock sequence found!\n"); |
526 | break; | 526 | break; |
527 | } | 527 | } |
528 | 528 | ||
529 | seq->state = SEQ_STATE_ACQUIRED; | 529 | seq->state = SEQ_STATE_ACQUIRED; |
530 | ls->nr_acquired++; | 530 | ls->nr_acquired++; |
531 | seq->prev_event_time = timestamp; | 531 | seq->prev_event_time = timestamp; |
532 | end: | 532 | end: |
533 | return; | 533 | return; |
534 | } | 534 | } |
535 | 535 | ||
536 | static void | 536 | static void |
537 | report_lock_contended_event(struct trace_contended_event *contended_event, | 537 | report_lock_contended_event(struct trace_contended_event *contended_event, |
538 | struct event *__event __used, | 538 | struct event *__event __used, |
539 | int cpu __used, | 539 | int cpu __used, |
540 | u64 timestamp __used, | 540 | u64 timestamp __used, |
541 | struct thread *thread __used) | 541 | struct thread *thread __used) |
542 | { | 542 | { |
543 | struct lock_stat *ls; | 543 | struct lock_stat *ls; |
544 | struct thread_stat *ts; | 544 | struct thread_stat *ts; |
545 | struct lock_seq_stat *seq; | 545 | struct lock_seq_stat *seq; |
546 | 546 | ||
547 | ls = lock_stat_findnew(contended_event->addr, contended_event->name); | 547 | ls = lock_stat_findnew(contended_event->addr, contended_event->name); |
548 | if (ls->discard) | 548 | if (ls->discard) |
549 | return; | 549 | return; |
550 | 550 | ||
551 | ts = thread_stat_findnew(thread->pid); | 551 | ts = thread_stat_findnew(thread->pid); |
552 | seq = get_seq(ts, contended_event->addr); | 552 | seq = get_seq(ts, contended_event->addr); |
553 | 553 | ||
554 | switch (seq->state) { | 554 | switch (seq->state) { |
555 | case SEQ_STATE_UNINITIALIZED: | 555 | case SEQ_STATE_UNINITIALIZED: |
556 | /* orphan event, do nothing */ | 556 | /* orphan event, do nothing */ |
557 | return; | 557 | return; |
558 | case SEQ_STATE_ACQUIRING: | 558 | case SEQ_STATE_ACQUIRING: |
559 | break; | 559 | break; |
560 | case SEQ_STATE_RELEASED: | 560 | case SEQ_STATE_RELEASED: |
561 | case SEQ_STATE_ACQUIRED: | 561 | case SEQ_STATE_ACQUIRED: |
562 | case SEQ_STATE_READ_ACQUIRED: | 562 | case SEQ_STATE_READ_ACQUIRED: |
563 | case SEQ_STATE_CONTENDED: | 563 | case SEQ_STATE_CONTENDED: |
564 | /* broken lock sequence, discard it */ | 564 | /* broken lock sequence, discard it */ |
565 | ls->discard = 1; | 565 | ls->discard = 1; |
566 | bad_hist[BROKEN_CONTENDED]++; | 566 | bad_hist[BROKEN_CONTENDED]++; |
567 | list_del(&seq->list); | 567 | list_del(&seq->list); |
568 | free(seq); | 568 | free(seq); |
569 | goto end; | 569 | goto end; |
570 | break; | 570 | break; |
571 | default: | 571 | default: |
572 | BUG_ON("Unknown state of lock sequence found!\n"); | 572 | BUG_ON("Unknown state of lock sequence found!\n"); |
573 | break; | 573 | break; |
574 | } | 574 | } |
575 | 575 | ||
576 | seq->state = SEQ_STATE_CONTENDED; | 576 | seq->state = SEQ_STATE_CONTENDED; |
577 | ls->nr_contended++; | 577 | ls->nr_contended++; |
578 | seq->prev_event_time = timestamp; | 578 | seq->prev_event_time = timestamp; |
579 | end: | 579 | end: |
580 | return; | 580 | return; |
581 | } | 581 | } |
582 | 582 | ||
583 | static void | 583 | static void |
584 | report_lock_release_event(struct trace_release_event *release_event, | 584 | report_lock_release_event(struct trace_release_event *release_event, |
585 | struct event *__event __used, | 585 | struct event *__event __used, |
586 | int cpu __used, | 586 | int cpu __used, |
587 | u64 timestamp __used, | 587 | u64 timestamp __used, |
588 | struct thread *thread __used) | 588 | struct thread *thread __used) |
589 | { | 589 | { |
590 | struct lock_stat *ls; | 590 | struct lock_stat *ls; |
591 | struct thread_stat *ts; | 591 | struct thread_stat *ts; |
592 | struct lock_seq_stat *seq; | 592 | struct lock_seq_stat *seq; |
593 | 593 | ||
594 | ls = lock_stat_findnew(release_event->addr, release_event->name); | 594 | ls = lock_stat_findnew(release_event->addr, release_event->name); |
595 | if (ls->discard) | 595 | if (ls->discard) |
596 | return; | 596 | return; |
597 | 597 | ||
598 | ts = thread_stat_findnew(thread->pid); | 598 | ts = thread_stat_findnew(thread->pid); |
599 | seq = get_seq(ts, release_event->addr); | 599 | seq = get_seq(ts, release_event->addr); |
600 | 600 | ||
601 | switch (seq->state) { | 601 | switch (seq->state) { |
602 | case SEQ_STATE_UNINITIALIZED: | 602 | case SEQ_STATE_UNINITIALIZED: |
603 | goto end; | 603 | goto end; |
604 | break; | 604 | break; |
605 | case SEQ_STATE_ACQUIRED: | 605 | case SEQ_STATE_ACQUIRED: |
606 | break; | 606 | break; |
607 | case SEQ_STATE_READ_ACQUIRED: | 607 | case SEQ_STATE_READ_ACQUIRED: |
608 | seq->read_count--; | 608 | seq->read_count--; |
609 | BUG_ON(seq->read_count < 0); | 609 | BUG_ON(seq->read_count < 0); |
610 | if (!seq->read_count) { | 610 | if (!seq->read_count) { |
611 | ls->nr_release++; | 611 | ls->nr_release++; |
612 | goto end; | 612 | goto end; |
613 | } | 613 | } |
614 | break; | 614 | break; |
615 | case SEQ_STATE_ACQUIRING: | 615 | case SEQ_STATE_ACQUIRING: |
616 | case SEQ_STATE_CONTENDED: | 616 | case SEQ_STATE_CONTENDED: |
617 | case SEQ_STATE_RELEASED: | 617 | case SEQ_STATE_RELEASED: |
618 | /* broken lock sequence, discard it */ | 618 | /* broken lock sequence, discard it */ |
619 | ls->discard = 1; | 619 | ls->discard = 1; |
620 | bad_hist[BROKEN_RELEASE]++; | 620 | bad_hist[BROKEN_RELEASE]++; |
621 | goto free_seq; | 621 | goto free_seq; |
622 | break; | 622 | break; |
623 | default: | 623 | default: |
624 | BUG_ON("Unknown state of lock sequence found!\n"); | 624 | BUG_ON("Unknown state of lock sequence found!\n"); |
625 | break; | 625 | break; |
626 | } | 626 | } |
627 | 627 | ||
628 | ls->nr_release++; | 628 | ls->nr_release++; |
629 | free_seq: | 629 | free_seq: |
630 | list_del(&seq->list); | 630 | list_del(&seq->list); |
631 | free(seq); | 631 | free(seq); |
632 | end: | 632 | end: |
633 | return; | 633 | return; |
634 | } | 634 | } |
635 | 635 | ||
636 | /* lock oriented handlers */ | 636 | /* lock oriented handlers */ |
637 | /* TODO: handlers for CPU oriented, thread oriented */ | 637 | /* TODO: handlers for CPU oriented, thread oriented */ |
638 | static struct trace_lock_handler report_lock_ops = { | 638 | static struct trace_lock_handler report_lock_ops = { |
639 | .acquire_event = report_lock_acquire_event, | 639 | .acquire_event = report_lock_acquire_event, |
640 | .acquired_event = report_lock_acquired_event, | 640 | .acquired_event = report_lock_acquired_event, |
641 | .contended_event = report_lock_contended_event, | 641 | .contended_event = report_lock_contended_event, |
642 | .release_event = report_lock_release_event, | 642 | .release_event = report_lock_release_event, |
643 | }; | 643 | }; |
644 | 644 | ||
645 | static struct trace_lock_handler *trace_handler; | 645 | static struct trace_lock_handler *trace_handler; |
646 | 646 | ||
647 | static void | 647 | static void |
648 | process_lock_acquire_event(void *data, | 648 | process_lock_acquire_event(void *data, |
649 | struct event *event __used, | 649 | struct event *event __used, |
650 | int cpu __used, | 650 | int cpu __used, |
651 | u64 timestamp __used, | 651 | u64 timestamp __used, |
652 | struct thread *thread __used) | 652 | struct thread *thread __used) |
653 | { | 653 | { |
654 | struct trace_acquire_event acquire_event; | 654 | struct trace_acquire_event acquire_event; |
655 | u64 tmp; /* this is required for casting... */ | 655 | u64 tmp; /* this is required for casting... */ |
656 | 656 | ||
657 | tmp = raw_field_value(event, "lockdep_addr", data); | 657 | tmp = raw_field_value(event, "lockdep_addr", data); |
658 | memcpy(&acquire_event.addr, &tmp, sizeof(void *)); | 658 | memcpy(&acquire_event.addr, &tmp, sizeof(void *)); |
659 | acquire_event.name = (char *)raw_field_ptr(event, "name", data); | 659 | acquire_event.name = (char *)raw_field_ptr(event, "name", data); |
660 | acquire_event.flag = (int)raw_field_value(event, "flag", data); | 660 | acquire_event.flag = (int)raw_field_value(event, "flag", data); |
661 | 661 | ||
662 | if (trace_handler->acquire_event) | 662 | if (trace_handler->acquire_event) |
663 | trace_handler->acquire_event(&acquire_event, event, cpu, timestamp, thread); | 663 | trace_handler->acquire_event(&acquire_event, event, cpu, timestamp, thread); |
664 | } | 664 | } |
665 | 665 | ||
666 | static void | 666 | static void |
667 | process_lock_acquired_event(void *data, | 667 | process_lock_acquired_event(void *data, |
668 | struct event *event __used, | 668 | struct event *event __used, |
669 | int cpu __used, | 669 | int cpu __used, |
670 | u64 timestamp __used, | 670 | u64 timestamp __used, |
671 | struct thread *thread __used) | 671 | struct thread *thread __used) |
672 | { | 672 | { |
673 | struct trace_acquired_event acquired_event; | 673 | struct trace_acquired_event acquired_event; |
674 | u64 tmp; /* this is required for casting... */ | 674 | u64 tmp; /* this is required for casting... */ |
675 | 675 | ||
676 | tmp = raw_field_value(event, "lockdep_addr", data); | 676 | tmp = raw_field_value(event, "lockdep_addr", data); |
677 | memcpy(&acquired_event.addr, &tmp, sizeof(void *)); | 677 | memcpy(&acquired_event.addr, &tmp, sizeof(void *)); |
678 | acquired_event.name = (char *)raw_field_ptr(event, "name", data); | 678 | acquired_event.name = (char *)raw_field_ptr(event, "name", data); |
679 | 679 | ||
680 | if (trace_handler->acquire_event) | 680 | if (trace_handler->acquire_event) |
681 | trace_handler->acquired_event(&acquired_event, event, cpu, timestamp, thread); | 681 | trace_handler->acquired_event(&acquired_event, event, cpu, timestamp, thread); |
682 | } | 682 | } |
683 | 683 | ||
684 | static void | 684 | static void |
685 | process_lock_contended_event(void *data, | 685 | process_lock_contended_event(void *data, |
686 | struct event *event __used, | 686 | struct event *event __used, |
687 | int cpu __used, | 687 | int cpu __used, |
688 | u64 timestamp __used, | 688 | u64 timestamp __used, |
689 | struct thread *thread __used) | 689 | struct thread *thread __used) |
690 | { | 690 | { |
691 | struct trace_contended_event contended_event; | 691 | struct trace_contended_event contended_event; |
692 | u64 tmp; /* this is required for casting... */ | 692 | u64 tmp; /* this is required for casting... */ |
693 | 693 | ||
694 | tmp = raw_field_value(event, "lockdep_addr", data); | 694 | tmp = raw_field_value(event, "lockdep_addr", data); |
695 | memcpy(&contended_event.addr, &tmp, sizeof(void *)); | 695 | memcpy(&contended_event.addr, &tmp, sizeof(void *)); |
696 | contended_event.name = (char *)raw_field_ptr(event, "name", data); | 696 | contended_event.name = (char *)raw_field_ptr(event, "name", data); |
697 | 697 | ||
698 | if (trace_handler->acquire_event) | 698 | if (trace_handler->acquire_event) |
699 | trace_handler->contended_event(&contended_event, event, cpu, timestamp, thread); | 699 | trace_handler->contended_event(&contended_event, event, cpu, timestamp, thread); |
700 | } | 700 | } |
701 | 701 | ||
702 | static void | 702 | static void |
703 | process_lock_release_event(void *data, | 703 | process_lock_release_event(void *data, |
704 | struct event *event __used, | 704 | struct event *event __used, |
705 | int cpu __used, | 705 | int cpu __used, |
706 | u64 timestamp __used, | 706 | u64 timestamp __used, |
707 | struct thread *thread __used) | 707 | struct thread *thread __used) |
708 | { | 708 | { |
709 | struct trace_release_event release_event; | 709 | struct trace_release_event release_event; |
710 | u64 tmp; /* this is required for casting... */ | 710 | u64 tmp; /* this is required for casting... */ |
711 | 711 | ||
712 | tmp = raw_field_value(event, "lockdep_addr", data); | 712 | tmp = raw_field_value(event, "lockdep_addr", data); |
713 | memcpy(&release_event.addr, &tmp, sizeof(void *)); | 713 | memcpy(&release_event.addr, &tmp, sizeof(void *)); |
714 | release_event.name = (char *)raw_field_ptr(event, "name", data); | 714 | release_event.name = (char *)raw_field_ptr(event, "name", data); |
715 | 715 | ||
716 | if (trace_handler->acquire_event) | 716 | if (trace_handler->acquire_event) |
717 | trace_handler->release_event(&release_event, event, cpu, timestamp, thread); | 717 | trace_handler->release_event(&release_event, event, cpu, timestamp, thread); |
718 | } | 718 | } |
719 | 719 | ||
720 | static void | 720 | static void |
721 | process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread) | 721 | process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread) |
722 | { | 722 | { |
723 | struct event *event; | 723 | struct event *event; |
724 | int type; | 724 | int type; |
725 | 725 | ||
726 | type = trace_parse_common_type(data); | 726 | type = trace_parse_common_type(data); |
727 | event = trace_find_event(type); | 727 | event = trace_find_event(type); |
728 | 728 | ||
729 | if (!strcmp(event->name, "lock_acquire")) | 729 | if (!strcmp(event->name, "lock_acquire")) |
730 | process_lock_acquire_event(data, event, cpu, timestamp, thread); | 730 | process_lock_acquire_event(data, event, cpu, timestamp, thread); |
731 | if (!strcmp(event->name, "lock_acquired")) | 731 | if (!strcmp(event->name, "lock_acquired")) |
732 | process_lock_acquired_event(data, event, cpu, timestamp, thread); | 732 | process_lock_acquired_event(data, event, cpu, timestamp, thread); |
733 | if (!strcmp(event->name, "lock_contended")) | 733 | if (!strcmp(event->name, "lock_contended")) |
734 | process_lock_contended_event(data, event, cpu, timestamp, thread); | 734 | process_lock_contended_event(data, event, cpu, timestamp, thread); |
735 | if (!strcmp(event->name, "lock_release")) | 735 | if (!strcmp(event->name, "lock_release")) |
736 | process_lock_release_event(data, event, cpu, timestamp, thread); | 736 | process_lock_release_event(data, event, cpu, timestamp, thread); |
737 | } | 737 | } |
738 | 738 | ||
739 | static void print_bad_events(int bad, int total) | 739 | static void print_bad_events(int bad, int total) |
740 | { | 740 | { |
741 | /* Output for debug, this have to be removed */ | 741 | /* Output for debug, this have to be removed */ |
742 | int i; | 742 | int i; |
743 | const char *name[4] = | 743 | const char *name[4] = |
744 | { "acquire", "acquired", "contended", "release" }; | 744 | { "acquire", "acquired", "contended", "release" }; |
745 | 745 | ||
746 | pr_info("\n=== output for debug===\n\n"); | 746 | pr_info("\n=== output for debug===\n\n"); |
747 | pr_info("bad: %d, total: %d\n", bad, total); | 747 | pr_info("bad: %d, total: %d\n", bad, total); |
748 | pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100); | 748 | pr_info("bad rate: %f %%\n", (double)bad / (double)total * 100); |
749 | pr_info("histogram of events caused bad sequence\n"); | 749 | pr_info("histogram of events caused bad sequence\n"); |
750 | for (i = 0; i < BROKEN_MAX; i++) | 750 | for (i = 0; i < BROKEN_MAX; i++) |
751 | pr_info(" %10s: %d\n", name[i], bad_hist[i]); | 751 | pr_info(" %10s: %d\n", name[i], bad_hist[i]); |
752 | } | 752 | } |
753 | 753 | ||
754 | /* TODO: various way to print, coloring, nano or milli sec */ | 754 | /* TODO: various way to print, coloring, nano or milli sec */ |
755 | static void print_result(void) | 755 | static void print_result(void) |
756 | { | 756 | { |
757 | struct lock_stat *st; | 757 | struct lock_stat *st; |
758 | char cut_name[20]; | 758 | char cut_name[20]; |
759 | int bad, total; | 759 | int bad, total; |
760 | 760 | ||
761 | pr_info("%20s ", "Name"); | 761 | pr_info("%20s ", "Name"); |
762 | pr_info("%10s ", "acquired"); | 762 | pr_info("%10s ", "acquired"); |
763 | pr_info("%10s ", "contended"); | 763 | pr_info("%10s ", "contended"); |
764 | 764 | ||
765 | pr_info("%15s ", "total wait (ns)"); | 765 | pr_info("%15s ", "total wait (ns)"); |
766 | pr_info("%15s ", "max wait (ns)"); | 766 | pr_info("%15s ", "max wait (ns)"); |
767 | pr_info("%15s ", "min wait (ns)"); | 767 | pr_info("%15s ", "min wait (ns)"); |
768 | 768 | ||
769 | pr_info("\n\n"); | 769 | pr_info("\n\n"); |
770 | 770 | ||
771 | bad = total = 0; | 771 | bad = total = 0; |
772 | while ((st = pop_from_result())) { | 772 | while ((st = pop_from_result())) { |
773 | total++; | 773 | total++; |
774 | if (st->discard) { | 774 | if (st->discard) { |
775 | bad++; | 775 | bad++; |
776 | continue; | 776 | continue; |
777 | } | 777 | } |
778 | bzero(cut_name, 20); | 778 | bzero(cut_name, 20); |
779 | 779 | ||
780 | if (strlen(st->name) < 16) { | 780 | if (strlen(st->name) < 16) { |
781 | /* output raw name */ | 781 | /* output raw name */ |
782 | pr_info("%20s ", st->name); | 782 | pr_info("%20s ", st->name); |
783 | } else { | 783 | } else { |
784 | strncpy(cut_name, st->name, 16); | 784 | strncpy(cut_name, st->name, 16); |
785 | cut_name[16] = '.'; | 785 | cut_name[16] = '.'; |
786 | cut_name[17] = '.'; | 786 | cut_name[17] = '.'; |
787 | cut_name[18] = '.'; | 787 | cut_name[18] = '.'; |
788 | cut_name[19] = '\0'; | 788 | cut_name[19] = '\0'; |
789 | /* cut off name for saving output style */ | 789 | /* cut off name for saving output style */ |
790 | pr_info("%20s ", cut_name); | 790 | pr_info("%20s ", cut_name); |
791 | } | 791 | } |
792 | 792 | ||
793 | pr_info("%10u ", st->nr_acquired); | 793 | pr_info("%10u ", st->nr_acquired); |
794 | pr_info("%10u ", st->nr_contended); | 794 | pr_info("%10u ", st->nr_contended); |
795 | 795 | ||
796 | pr_info("%15" PRIu64 " ", st->wait_time_total); | 796 | pr_info("%15" PRIu64 " ", st->wait_time_total); |
797 | pr_info("%15" PRIu64 " ", st->wait_time_max); | 797 | pr_info("%15" PRIu64 " ", st->wait_time_max); |
798 | pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ? | 798 | pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ? |
799 | 0 : st->wait_time_min); | 799 | 0 : st->wait_time_min); |
800 | pr_info("\n"); | 800 | pr_info("\n"); |
801 | } | 801 | } |
802 | 802 | ||
803 | print_bad_events(bad, total); | 803 | print_bad_events(bad, total); |
804 | } | 804 | } |
805 | 805 | ||
806 | static bool info_threads, info_map; | 806 | static bool info_threads, info_map; |
807 | 807 | ||
808 | static void dump_threads(void) | 808 | static void dump_threads(void) |
809 | { | 809 | { |
810 | struct thread_stat *st; | 810 | struct thread_stat *st; |
811 | struct rb_node *node; | 811 | struct rb_node *node; |
812 | struct thread *t; | 812 | struct thread *t; |
813 | 813 | ||
814 | pr_info("%10s: comm\n", "Thread ID"); | 814 | pr_info("%10s: comm\n", "Thread ID"); |
815 | 815 | ||
816 | node = rb_first(&thread_stats); | 816 | node = rb_first(&thread_stats); |
817 | while (node) { | 817 | while (node) { |
818 | st = container_of(node, struct thread_stat, rb); | 818 | st = container_of(node, struct thread_stat, rb); |
819 | t = perf_session__findnew(session, st->tid); | 819 | t = perf_session__findnew(session, st->tid); |
820 | pr_info("%10d: %s\n", st->tid, t->comm); | 820 | pr_info("%10d: %s\n", st->tid, t->comm); |
821 | node = rb_next(node); | 821 | node = rb_next(node); |
822 | }; | 822 | }; |
823 | } | 823 | } |
824 | 824 | ||
825 | static void dump_map(void) | 825 | static void dump_map(void) |
826 | { | 826 | { |
827 | unsigned int i; | 827 | unsigned int i; |
828 | struct lock_stat *st; | 828 | struct lock_stat *st; |
829 | 829 | ||
830 | pr_info("Address of instance: name of class\n"); | 830 | pr_info("Address of instance: name of class\n"); |
831 | for (i = 0; i < LOCKHASH_SIZE; i++) { | 831 | for (i = 0; i < LOCKHASH_SIZE; i++) { |
832 | list_for_each_entry(st, &lockhash_table[i], hash_entry) { | 832 | list_for_each_entry(st, &lockhash_table[i], hash_entry) { |
833 | pr_info(" %p: %s\n", st->addr, st->name); | 833 | pr_info(" %p: %s\n", st->addr, st->name); |
834 | } | 834 | } |
835 | } | 835 | } |
836 | } | 836 | } |
837 | 837 | ||
838 | static void dump_info(void) | 838 | static void dump_info(void) |
839 | { | 839 | { |
840 | if (info_threads) | 840 | if (info_threads) |
841 | dump_threads(); | 841 | dump_threads(); |
842 | else if (info_map) | 842 | else if (info_map) |
843 | dump_map(); | 843 | dump_map(); |
844 | else | 844 | else |
845 | die("Unknown type of information\n"); | 845 | die("Unknown type of information\n"); |
846 | } | 846 | } |
847 | 847 | ||
848 | static int process_sample_event(union perf_event *event, | 848 | static int process_sample_event(union perf_event *event, |
849 | struct perf_sample *sample, | 849 | struct perf_sample *sample, |
850 | struct perf_evsel *evsel __used, | 850 | struct perf_evsel *evsel __used, |
851 | struct perf_session *s) | 851 | struct perf_session *s) |
852 | { | 852 | { |
853 | struct thread *thread = perf_session__findnew(s, sample->tid); | 853 | struct thread *thread = perf_session__findnew(s, sample->tid); |
854 | 854 | ||
855 | if (thread == NULL) { | 855 | if (thread == NULL) { |
856 | pr_debug("problem processing %d event, skipping it.\n", | 856 | pr_debug("problem processing %d event, skipping it.\n", |
857 | event->header.type); | 857 | event->header.type); |
858 | return -1; | 858 | return -1; |
859 | } | 859 | } |
860 | 860 | ||
861 | process_raw_event(sample->raw_data, sample->cpu, sample->time, thread); | 861 | process_raw_event(sample->raw_data, sample->cpu, sample->time, thread); |
862 | 862 | ||
863 | return 0; | 863 | return 0; |
864 | } | 864 | } |
865 | 865 | ||
866 | static struct perf_event_ops eops = { | 866 | static struct perf_event_ops eops = { |
867 | .sample = process_sample_event, | 867 | .sample = process_sample_event, |
868 | .comm = perf_event__process_comm, | 868 | .comm = perf_event__process_comm, |
869 | .ordered_samples = true, | 869 | .ordered_samples = true, |
870 | }; | 870 | }; |
871 | 871 | ||
872 | static int read_events(void) | 872 | static int read_events(void) |
873 | { | 873 | { |
874 | session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); | 874 | session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); |
875 | if (!session) | 875 | if (!session) |
876 | die("Initializing perf session failed\n"); | 876 | die("Initializing perf session failed\n"); |
877 | 877 | ||
878 | return perf_session__process_events(session, &eops); | 878 | return perf_session__process_events(session, &eops); |
879 | } | 879 | } |
880 | 880 | ||
881 | static void sort_result(void) | 881 | static void sort_result(void) |
882 | { | 882 | { |
883 | unsigned int i; | 883 | unsigned int i; |
884 | struct lock_stat *st; | 884 | struct lock_stat *st; |
885 | 885 | ||
886 | for (i = 0; i < LOCKHASH_SIZE; i++) { | 886 | for (i = 0; i < LOCKHASH_SIZE; i++) { |
887 | list_for_each_entry(st, &lockhash_table[i], hash_entry) { | 887 | list_for_each_entry(st, &lockhash_table[i], hash_entry) { |
888 | insert_to_result(st, compare); | 888 | insert_to_result(st, compare); |
889 | } | 889 | } |
890 | } | 890 | } |
891 | } | 891 | } |
892 | 892 | ||
893 | static void __cmd_report(void) | 893 | static void __cmd_report(void) |
894 | { | 894 | { |
895 | setup_pager(); | 895 | setup_pager(); |
896 | select_key(); | 896 | select_key(); |
897 | read_events(); | 897 | read_events(); |
898 | sort_result(); | 898 | sort_result(); |
899 | print_result(); | 899 | print_result(); |
900 | } | 900 | } |
901 | 901 | ||
902 | static const char * const report_usage[] = { | 902 | static const char * const report_usage[] = { |
903 | "perf lock report [<options>]", | 903 | "perf lock report [<options>]", |
904 | NULL | 904 | NULL |
905 | }; | 905 | }; |
906 | 906 | ||
907 | static const struct option report_options[] = { | 907 | static const struct option report_options[] = { |
908 | OPT_STRING('k', "key", &sort_key, "acquired", | 908 | OPT_STRING('k', "key", &sort_key, "acquired", |
909 | "key for sorting (acquired / contended / wait_total / wait_max / wait_min)"), | 909 | "key for sorting (acquired / contended / wait_total / wait_max / wait_min)"), |
910 | /* TODO: type */ | 910 | /* TODO: type */ |
911 | OPT_END() | 911 | OPT_END() |
912 | }; | 912 | }; |
913 | 913 | ||
914 | static const char * const info_usage[] = { | 914 | static const char * const info_usage[] = { |
915 | "perf lock info [<options>]", | 915 | "perf lock info [<options>]", |
916 | NULL | 916 | NULL |
917 | }; | 917 | }; |
918 | 918 | ||
919 | static const struct option info_options[] = { | 919 | static const struct option info_options[] = { |
920 | OPT_BOOLEAN('t', "threads", &info_threads, | 920 | OPT_BOOLEAN('t', "threads", &info_threads, |
921 | "dump thread list in perf.data"), | 921 | "dump thread list in perf.data"), |
922 | OPT_BOOLEAN('m', "map", &info_map, | 922 | OPT_BOOLEAN('m', "map", &info_map, |
923 | "map of lock instances (name:address table)"), | 923 | "map of lock instances (name:address table)"), |
924 | OPT_END() | 924 | OPT_END() |
925 | }; | 925 | }; |
926 | 926 | ||
927 | static const char * const lock_usage[] = { | 927 | static const char * const lock_usage[] = { |
928 | "perf lock [<options>] {record|trace|report}", | 928 | "perf lock [<options>] {record|trace|report}", |
929 | NULL | 929 | NULL |
930 | }; | 930 | }; |
931 | 931 | ||
932 | static const struct option lock_options[] = { | 932 | static const struct option lock_options[] = { |
933 | OPT_STRING('i', "input", &input_name, "file", "input file name"), | 933 | OPT_STRING('i', "input", &input_name, "file", "input file name"), |
934 | OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), | 934 | OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), |
935 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), | 935 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), |
936 | OPT_END() | 936 | OPT_END() |
937 | }; | 937 | }; |
938 | 938 | ||
939 | static const char *record_args[] = { | 939 | static const char *record_args[] = { |
940 | "record", | 940 | "record", |
941 | "-R", | 941 | "-R", |
942 | "-f", | 942 | "-f", |
943 | "-m", "1024", | 943 | "-m", "1024", |
944 | "-c", "1", | 944 | "-c", "1", |
945 | "-e", "lock:lock_acquire:r", | 945 | "-e", "lock:lock_acquire", |
946 | "-e", "lock:lock_acquired:r", | 946 | "-e", "lock:lock_acquired", |
947 | "-e", "lock:lock_contended:r", | 947 | "-e", "lock:lock_contended", |
948 | "-e", "lock:lock_release:r", | 948 | "-e", "lock:lock_release", |
949 | }; | 949 | }; |
950 | 950 | ||
951 | static int __cmd_record(int argc, const char **argv) | 951 | static int __cmd_record(int argc, const char **argv) |
952 | { | 952 | { |
953 | unsigned int rec_argc, i, j; | 953 | unsigned int rec_argc, i, j; |
954 | const char **rec_argv; | 954 | const char **rec_argv; |
955 | 955 | ||
956 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | 956 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; |
957 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 957 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
958 | 958 | ||
959 | if (rec_argv == NULL) | 959 | if (rec_argv == NULL) |
960 | return -ENOMEM; | 960 | return -ENOMEM; |
961 | 961 | ||
962 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 962 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
963 | rec_argv[i] = strdup(record_args[i]); | 963 | rec_argv[i] = strdup(record_args[i]); |
964 | 964 | ||
965 | for (j = 1; j < (unsigned int)argc; j++, i++) | 965 | for (j = 1; j < (unsigned int)argc; j++, i++) |
966 | rec_argv[i] = argv[j]; | 966 | rec_argv[i] = argv[j]; |
967 | 967 | ||
968 | BUG_ON(i != rec_argc); | 968 | BUG_ON(i != rec_argc); |
969 | 969 | ||
970 | return cmd_record(i, rec_argv, NULL); | 970 | return cmd_record(i, rec_argv, NULL); |
971 | } | 971 | } |
972 | 972 | ||
973 | int cmd_lock(int argc, const char **argv, const char *prefix __used) | 973 | int cmd_lock(int argc, const char **argv, const char *prefix __used) |
974 | { | 974 | { |
975 | unsigned int i; | 975 | unsigned int i; |
976 | 976 | ||
977 | symbol__init(); | 977 | symbol__init(); |
978 | for (i = 0; i < LOCKHASH_SIZE; i++) | 978 | for (i = 0; i < LOCKHASH_SIZE; i++) |
979 | INIT_LIST_HEAD(lockhash_table + i); | 979 | INIT_LIST_HEAD(lockhash_table + i); |
980 | 980 | ||
981 | argc = parse_options(argc, argv, lock_options, lock_usage, | 981 | argc = parse_options(argc, argv, lock_options, lock_usage, |
982 | PARSE_OPT_STOP_AT_NON_OPTION); | 982 | PARSE_OPT_STOP_AT_NON_OPTION); |
983 | if (!argc) | 983 | if (!argc) |
984 | usage_with_options(lock_usage, lock_options); | 984 | usage_with_options(lock_usage, lock_options); |
985 | 985 | ||
986 | if (!strncmp(argv[0], "rec", 3)) { | 986 | if (!strncmp(argv[0], "rec", 3)) { |
987 | return __cmd_record(argc, argv); | 987 | return __cmd_record(argc, argv); |
988 | } else if (!strncmp(argv[0], "report", 6)) { | 988 | } else if (!strncmp(argv[0], "report", 6)) { |
989 | trace_handler = &report_lock_ops; | 989 | trace_handler = &report_lock_ops; |
990 | if (argc) { | 990 | if (argc) { |
991 | argc = parse_options(argc, argv, | 991 | argc = parse_options(argc, argv, |
992 | report_options, report_usage, 0); | 992 | report_options, report_usage, 0); |
993 | if (argc) | 993 | if (argc) |
994 | usage_with_options(report_usage, report_options); | 994 | usage_with_options(report_usage, report_options); |
995 | } | 995 | } |
996 | __cmd_report(); | 996 | __cmd_report(); |
997 | } else if (!strcmp(argv[0], "script")) { | 997 | } else if (!strcmp(argv[0], "script")) { |
998 | /* Aliased to 'perf script' */ | 998 | /* Aliased to 'perf script' */ |
999 | return cmd_script(argc, argv, prefix); | 999 | return cmd_script(argc, argv, prefix); |
1000 | } else if (!strcmp(argv[0], "info")) { | 1000 | } else if (!strcmp(argv[0], "info")) { |
1001 | if (argc) { | 1001 | if (argc) { |
1002 | argc = parse_options(argc, argv, | 1002 | argc = parse_options(argc, argv, |
1003 | info_options, info_usage, 0); | 1003 | info_options, info_usage, 0); |
1004 | if (argc) | 1004 | if (argc) |
1005 | usage_with_options(info_usage, info_options); | 1005 | usage_with_options(info_usage, info_options); |
1006 | } | 1006 | } |
1007 | /* recycling report_lock_ops */ | 1007 | /* recycling report_lock_ops */ |
1008 | trace_handler = &report_lock_ops; | 1008 | trace_handler = &report_lock_ops; |
1009 | setup_pager(); | 1009 | setup_pager(); |
1010 | read_events(); | 1010 | read_events(); |
1011 | dump_info(); | 1011 | dump_info(); |
1012 | } else { | 1012 | } else { |
1013 | usage_with_options(lock_usage, lock_options); | 1013 | usage_with_options(lock_usage, lock_options); |
1014 | } | 1014 | } |
1015 | 1015 | ||
1016 | return 0; | 1016 | return 0; |
1017 | } | 1017 | } |
1018 | 1018 |
tools/perf/builtin-record.c
1 | /* | 1 | /* |
2 | * builtin-record.c | 2 | * builtin-record.c |
3 | * | 3 | * |
4 | * Builtin record command: Record the profile of a workload | 4 | * Builtin record command: Record the profile of a workload |
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 | 8 | #define _FILE_OFFSET_BITS 64 |
9 | 9 | ||
10 | #include "builtin.h" | 10 | #include "builtin.h" |
11 | 11 | ||
12 | #include "perf.h" | 12 | #include "perf.h" |
13 | 13 | ||
14 | #include "util/build-id.h" | 14 | #include "util/build-id.h" |
15 | #include "util/util.h" | 15 | #include "util/util.h" |
16 | #include "util/parse-options.h" | 16 | #include "util/parse-options.h" |
17 | #include "util/parse-events.h" | 17 | #include "util/parse-events.h" |
18 | 18 | ||
19 | #include "util/header.h" | 19 | #include "util/header.h" |
20 | #include "util/event.h" | 20 | #include "util/event.h" |
21 | #include "util/evlist.h" | 21 | #include "util/evlist.h" |
22 | #include "util/evsel.h" | 22 | #include "util/evsel.h" |
23 | #include "util/debug.h" | 23 | #include "util/debug.h" |
24 | #include "util/session.h" | 24 | #include "util/session.h" |
25 | #include "util/symbol.h" | 25 | #include "util/symbol.h" |
26 | #include "util/cpumap.h" | 26 | #include "util/cpumap.h" |
27 | #include "util/thread_map.h" | 27 | #include "util/thread_map.h" |
28 | 28 | ||
29 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #include <sched.h> | 30 | #include <sched.h> |
31 | #include <sys/mman.h> | 31 | #include <sys/mman.h> |
32 | 32 | ||
33 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
34 | |||
35 | enum write_mode_t { | 33 | enum write_mode_t { |
36 | WRITE_FORCE, | 34 | WRITE_FORCE, |
37 | WRITE_APPEND | 35 | WRITE_APPEND |
38 | }; | 36 | }; |
39 | 37 | ||
40 | static u64 user_interval = ULLONG_MAX; | 38 | static u64 user_interval = ULLONG_MAX; |
41 | static u64 default_interval = 0; | 39 | static u64 default_interval = 0; |
42 | 40 | ||
43 | static unsigned int page_size; | 41 | static unsigned int page_size; |
44 | static unsigned int mmap_pages = UINT_MAX; | 42 | static unsigned int mmap_pages = UINT_MAX; |
45 | static unsigned int user_freq = UINT_MAX; | 43 | static unsigned int user_freq = UINT_MAX; |
46 | static int freq = 1000; | 44 | static int freq = 1000; |
47 | static int output; | 45 | static int output; |
48 | static int pipe_output = 0; | 46 | static int pipe_output = 0; |
49 | static const char *output_name = NULL; | 47 | static const char *output_name = NULL; |
50 | static int group = 0; | 48 | static int group = 0; |
51 | static int realtime_prio = 0; | 49 | static int realtime_prio = 0; |
52 | static bool nodelay = false; | 50 | static bool nodelay = false; |
53 | static bool raw_samples = false; | 51 | static bool raw_samples = false; |
54 | static bool sample_id_all_avail = true; | 52 | static bool sample_id_all_avail = true; |
55 | static bool system_wide = false; | 53 | static bool system_wide = false; |
56 | static pid_t target_pid = -1; | 54 | static pid_t target_pid = -1; |
57 | static pid_t target_tid = -1; | 55 | static pid_t target_tid = -1; |
58 | static pid_t child_pid = -1; | 56 | static pid_t child_pid = -1; |
59 | static bool no_inherit = false; | 57 | static bool no_inherit = false; |
60 | static enum write_mode_t write_mode = WRITE_FORCE; | 58 | static enum write_mode_t write_mode = WRITE_FORCE; |
61 | static bool call_graph = false; | 59 | static bool call_graph = false; |
62 | static bool inherit_stat = false; | 60 | static bool inherit_stat = false; |
63 | static bool no_samples = false; | 61 | static bool no_samples = false; |
64 | static bool sample_address = false; | 62 | static bool sample_address = false; |
65 | static bool sample_time = false; | 63 | static bool sample_time = false; |
66 | static bool no_buildid = false; | 64 | static bool no_buildid = false; |
67 | static bool no_buildid_cache = false; | 65 | static bool no_buildid_cache = false; |
68 | static struct perf_evlist *evsel_list; | 66 | static struct perf_evlist *evsel_list; |
69 | 67 | ||
70 | static long samples = 0; | 68 | static long samples = 0; |
71 | static u64 bytes_written = 0; | 69 | static u64 bytes_written = 0; |
72 | 70 | ||
73 | static int file_new = 1; | 71 | static int file_new = 1; |
74 | static off_t post_processing_offset; | 72 | static off_t post_processing_offset; |
75 | 73 | ||
76 | static struct perf_session *session; | 74 | static struct perf_session *session; |
77 | static const char *cpu_list; | 75 | static const char *cpu_list; |
78 | 76 | ||
79 | static void advance_output(size_t size) | 77 | static void advance_output(size_t size) |
80 | { | 78 | { |
81 | bytes_written += size; | 79 | bytes_written += size; |
82 | } | 80 | } |
83 | 81 | ||
84 | static void write_output(void *buf, size_t size) | 82 | static void write_output(void *buf, size_t size) |
85 | { | 83 | { |
86 | while (size) { | 84 | while (size) { |
87 | int ret = write(output, buf, size); | 85 | int ret = write(output, buf, size); |
88 | 86 | ||
89 | if (ret < 0) | 87 | if (ret < 0) |
90 | die("failed to write"); | 88 | die("failed to write"); |
91 | 89 | ||
92 | size -= ret; | 90 | size -= ret; |
93 | buf += ret; | 91 | buf += ret; |
94 | 92 | ||
95 | bytes_written += ret; | 93 | bytes_written += ret; |
96 | } | 94 | } |
97 | } | 95 | } |
98 | 96 | ||
99 | static int process_synthesized_event(union perf_event *event, | 97 | static int process_synthesized_event(union perf_event *event, |
100 | struct perf_sample *sample __used, | 98 | struct perf_sample *sample __used, |
101 | struct perf_session *self __used) | 99 | struct perf_session *self __used) |
102 | { | 100 | { |
103 | write_output(event, event->header.size); | 101 | write_output(event, event->header.size); |
104 | return 0; | 102 | return 0; |
105 | } | 103 | } |
106 | 104 | ||
107 | static void mmap_read(struct perf_mmap *md) | 105 | static void mmap_read(struct perf_mmap *md) |
108 | { | 106 | { |
109 | unsigned int head = perf_mmap__read_head(md); | 107 | unsigned int head = perf_mmap__read_head(md); |
110 | unsigned int old = md->prev; | 108 | unsigned int old = md->prev; |
111 | unsigned char *data = md->base + page_size; | 109 | unsigned char *data = md->base + page_size; |
112 | unsigned long size; | 110 | unsigned long size; |
113 | void *buf; | 111 | void *buf; |
114 | 112 | ||
115 | if (old == head) | 113 | if (old == head) |
116 | return; | 114 | return; |
117 | 115 | ||
118 | samples++; | 116 | samples++; |
119 | 117 | ||
120 | size = head - old; | 118 | size = head - old; |
121 | 119 | ||
122 | if ((old & md->mask) + size != (head & md->mask)) { | 120 | if ((old & md->mask) + size != (head & md->mask)) { |
123 | buf = &data[old & md->mask]; | 121 | buf = &data[old & md->mask]; |
124 | size = md->mask + 1 - (old & md->mask); | 122 | size = md->mask + 1 - (old & md->mask); |
125 | old += size; | 123 | old += size; |
126 | 124 | ||
127 | write_output(buf, size); | 125 | write_output(buf, size); |
128 | } | 126 | } |
129 | 127 | ||
130 | buf = &data[old & md->mask]; | 128 | buf = &data[old & md->mask]; |
131 | size = head - old; | 129 | size = head - old; |
132 | old += size; | 130 | old += size; |
133 | 131 | ||
134 | write_output(buf, size); | 132 | write_output(buf, size); |
135 | 133 | ||
136 | md->prev = old; | 134 | md->prev = old; |
137 | perf_mmap__write_tail(md, old); | 135 | perf_mmap__write_tail(md, old); |
138 | } | 136 | } |
139 | 137 | ||
140 | static volatile int done = 0; | 138 | static volatile int done = 0; |
141 | static volatile int signr = -1; | 139 | static volatile int signr = -1; |
142 | 140 | ||
143 | static void sig_handler(int sig) | 141 | static void sig_handler(int sig) |
144 | { | 142 | { |
145 | done = 1; | 143 | done = 1; |
146 | signr = sig; | 144 | signr = sig; |
147 | } | 145 | } |
148 | 146 | ||
149 | static void sig_atexit(void) | 147 | static void sig_atexit(void) |
150 | { | 148 | { |
151 | if (child_pid > 0) | 149 | if (child_pid > 0) |
152 | kill(child_pid, SIGTERM); | 150 | kill(child_pid, SIGTERM); |
153 | 151 | ||
154 | if (signr == -1 || signr == SIGUSR1) | 152 | if (signr == -1 || signr == SIGUSR1) |
155 | return; | 153 | return; |
156 | 154 | ||
157 | signal(signr, SIG_DFL); | 155 | signal(signr, SIG_DFL); |
158 | kill(getpid(), signr); | 156 | kill(getpid(), signr); |
159 | } | 157 | } |
160 | 158 | ||
161 | static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) | 159 | static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) |
162 | { | 160 | { |
163 | struct perf_event_attr *attr = &evsel->attr; | 161 | struct perf_event_attr *attr = &evsel->attr; |
164 | int track = !evsel->idx; /* only the first counter needs these */ | 162 | int track = !evsel->idx; /* only the first counter needs these */ |
165 | 163 | ||
166 | attr->inherit = !no_inherit; | 164 | attr->inherit = !no_inherit; |
167 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 165 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
168 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 166 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
169 | PERF_FORMAT_ID; | 167 | PERF_FORMAT_ID; |
170 | 168 | ||
171 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 169 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
172 | 170 | ||
173 | if (evlist->nr_entries > 1) | 171 | if (evlist->nr_entries > 1) |
174 | attr->sample_type |= PERF_SAMPLE_ID; | 172 | attr->sample_type |= PERF_SAMPLE_ID; |
175 | 173 | ||
176 | /* | 174 | /* |
177 | * We default some events to a 1 default interval. But keep | 175 | * We default some events to a 1 default interval. But keep |
178 | * it a weak assumption overridable by the user. | 176 | * it a weak assumption overridable by the user. |
179 | */ | 177 | */ |
180 | if (!attr->sample_period || (user_freq != UINT_MAX && | 178 | if (!attr->sample_period || (user_freq != UINT_MAX && |
181 | user_interval != ULLONG_MAX)) { | 179 | user_interval != ULLONG_MAX)) { |
182 | if (freq) { | 180 | if (freq) { |
183 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 181 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
184 | attr->freq = 1; | 182 | attr->freq = 1; |
185 | attr->sample_freq = freq; | 183 | attr->sample_freq = freq; |
186 | } else { | 184 | } else { |
187 | attr->sample_period = default_interval; | 185 | attr->sample_period = default_interval; |
188 | } | 186 | } |
189 | } | 187 | } |
190 | 188 | ||
191 | if (no_samples) | 189 | if (no_samples) |
192 | attr->sample_freq = 0; | 190 | attr->sample_freq = 0; |
193 | 191 | ||
194 | if (inherit_stat) | 192 | if (inherit_stat) |
195 | attr->inherit_stat = 1; | 193 | attr->inherit_stat = 1; |
196 | 194 | ||
197 | if (sample_address) { | 195 | if (sample_address) { |
198 | attr->sample_type |= PERF_SAMPLE_ADDR; | 196 | attr->sample_type |= PERF_SAMPLE_ADDR; |
199 | attr->mmap_data = track; | 197 | attr->mmap_data = track; |
200 | } | 198 | } |
201 | 199 | ||
202 | if (call_graph) | 200 | if (call_graph) |
203 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 201 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
204 | 202 | ||
205 | if (system_wide) | 203 | if (system_wide) |
206 | attr->sample_type |= PERF_SAMPLE_CPU; | 204 | attr->sample_type |= PERF_SAMPLE_CPU; |
207 | 205 | ||
208 | if (sample_id_all_avail && | 206 | if (sample_id_all_avail && |
209 | (sample_time || system_wide || !no_inherit || cpu_list)) | 207 | (sample_time || system_wide || !no_inherit || cpu_list)) |
210 | attr->sample_type |= PERF_SAMPLE_TIME; | 208 | attr->sample_type |= PERF_SAMPLE_TIME; |
211 | 209 | ||
212 | if (raw_samples) { | 210 | if (raw_samples) { |
213 | attr->sample_type |= PERF_SAMPLE_TIME; | 211 | attr->sample_type |= PERF_SAMPLE_TIME; |
214 | attr->sample_type |= PERF_SAMPLE_RAW; | 212 | attr->sample_type |= PERF_SAMPLE_RAW; |
215 | attr->sample_type |= PERF_SAMPLE_CPU; | 213 | attr->sample_type |= PERF_SAMPLE_CPU; |
216 | } | 214 | } |
217 | 215 | ||
218 | if (nodelay) { | 216 | if (nodelay) { |
219 | attr->watermark = 0; | 217 | attr->watermark = 0; |
220 | attr->wakeup_events = 1; | 218 | attr->wakeup_events = 1; |
221 | } | 219 | } |
222 | 220 | ||
223 | attr->mmap = track; | 221 | attr->mmap = track; |
224 | attr->comm = track; | 222 | attr->comm = track; |
225 | 223 | ||
226 | if (target_pid == -1 && target_tid == -1 && !system_wide) { | 224 | if (target_pid == -1 && target_tid == -1 && !system_wide) { |
227 | attr->disabled = 1; | 225 | attr->disabled = 1; |
228 | attr->enable_on_exec = 1; | 226 | attr->enable_on_exec = 1; |
229 | } | 227 | } |
230 | } | 228 | } |
231 | 229 | ||
232 | static bool perf_evlist__equal(struct perf_evlist *evlist, | 230 | static bool perf_evlist__equal(struct perf_evlist *evlist, |
233 | struct perf_evlist *other) | 231 | struct perf_evlist *other) |
234 | { | 232 | { |
235 | struct perf_evsel *pos, *pair; | 233 | struct perf_evsel *pos, *pair; |
236 | 234 | ||
237 | if (evlist->nr_entries != other->nr_entries) | 235 | if (evlist->nr_entries != other->nr_entries) |
238 | return false; | 236 | return false; |
239 | 237 | ||
240 | pair = list_entry(other->entries.next, struct perf_evsel, node); | 238 | pair = list_entry(other->entries.next, struct perf_evsel, node); |
241 | 239 | ||
242 | list_for_each_entry(pos, &evlist->entries, node) { | 240 | list_for_each_entry(pos, &evlist->entries, node) { |
243 | if (memcmp(&pos->attr, &pair->attr, sizeof(pos->attr) != 0)) | 241 | if (memcmp(&pos->attr, &pair->attr, sizeof(pos->attr) != 0)) |
244 | return false; | 242 | return false; |
245 | pair = list_entry(pair->node.next, struct perf_evsel, node); | 243 | pair = list_entry(pair->node.next, struct perf_evsel, node); |
246 | } | 244 | } |
247 | 245 | ||
248 | return true; | 246 | return true; |
249 | } | 247 | } |
250 | 248 | ||
251 | static void open_counters(struct perf_evlist *evlist) | 249 | static void open_counters(struct perf_evlist *evlist) |
252 | { | 250 | { |
253 | struct perf_evsel *pos; | 251 | struct perf_evsel *pos; |
254 | 252 | ||
255 | if (evlist->cpus->map[0] < 0) | 253 | if (evlist->cpus->map[0] < 0) |
256 | no_inherit = true; | 254 | no_inherit = true; |
257 | 255 | ||
258 | list_for_each_entry(pos, &evlist->entries, node) { | 256 | list_for_each_entry(pos, &evlist->entries, node) { |
259 | struct perf_event_attr *attr = &pos->attr; | 257 | struct perf_event_attr *attr = &pos->attr; |
260 | /* | 258 | /* |
261 | * Check if parse_single_tracepoint_event has already asked for | 259 | * Check if parse_single_tracepoint_event has already asked for |
262 | * PERF_SAMPLE_TIME. | 260 | * PERF_SAMPLE_TIME. |
263 | * | 261 | * |
264 | * XXX this is kludgy but short term fix for problems introduced by | 262 | * XXX this is kludgy but short term fix for problems introduced by |
265 | * eac23d1c that broke 'perf script' by having different sample_types | 263 | * eac23d1c that broke 'perf script' by having different sample_types |
266 | * when using multiple tracepoint events when we use a perf binary | 264 | * when using multiple tracepoint events when we use a perf binary |
267 | * that tries to use sample_id_all on an older kernel. | 265 | * that tries to use sample_id_all on an older kernel. |
268 | * | 266 | * |
269 | * We need to move counter creation to perf_session, support | 267 | * We need to move counter creation to perf_session, support |
270 | * different sample_types, etc. | 268 | * different sample_types, etc. |
271 | */ | 269 | */ |
272 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | 270 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; |
273 | 271 | ||
274 | config_attr(pos, evlist); | 272 | config_attr(pos, evlist); |
275 | retry_sample_id: | 273 | retry_sample_id: |
276 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | 274 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; |
277 | try_again: | 275 | try_again: |
278 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group) < 0) { | 276 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group) < 0) { |
279 | int err = errno; | 277 | int err = errno; |
280 | 278 | ||
281 | if (err == EPERM || err == EACCES) { | 279 | if (err == EPERM || err == EACCES) { |
282 | ui__warning_paranoid(); | 280 | ui__warning_paranoid(); |
283 | exit(EXIT_FAILURE); | 281 | exit(EXIT_FAILURE); |
284 | } else if (err == ENODEV && cpu_list) { | 282 | } else if (err == ENODEV && cpu_list) { |
285 | die("No such device - did you specify" | 283 | die("No such device - did you specify" |
286 | " an out-of-range profile CPU?\n"); | 284 | " an out-of-range profile CPU?\n"); |
287 | } else if (err == EINVAL && sample_id_all_avail) { | 285 | } else if (err == EINVAL && sample_id_all_avail) { |
288 | /* | 286 | /* |
289 | * Old kernel, no attr->sample_id_type_all field | 287 | * Old kernel, no attr->sample_id_type_all field |
290 | */ | 288 | */ |
291 | sample_id_all_avail = false; | 289 | sample_id_all_avail = false; |
292 | if (!sample_time && !raw_samples && !time_needed) | 290 | if (!sample_time && !raw_samples && !time_needed) |
293 | attr->sample_type &= ~PERF_SAMPLE_TIME; | 291 | attr->sample_type &= ~PERF_SAMPLE_TIME; |
294 | 292 | ||
295 | goto retry_sample_id; | 293 | goto retry_sample_id; |
296 | } | 294 | } |
297 | 295 | ||
298 | /* | 296 | /* |
299 | * If it's cycles then fall back to hrtimer | 297 | * If it's cycles then fall back to hrtimer |
300 | * based cpu-clock-tick sw counter, which | 298 | * based cpu-clock-tick sw counter, which |
301 | * is always available even if no PMU support: | 299 | * is always available even if no PMU support: |
302 | */ | 300 | */ |
303 | if (attr->type == PERF_TYPE_HARDWARE | 301 | if (attr->type == PERF_TYPE_HARDWARE |
304 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | 302 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { |
305 | 303 | ||
306 | if (verbose) | 304 | if (verbose) |
307 | ui__warning("The cycles event is not supported, " | 305 | ui__warning("The cycles event is not supported, " |
308 | "trying to fall back to cpu-clock-ticks\n"); | 306 | "trying to fall back to cpu-clock-ticks\n"); |
309 | attr->type = PERF_TYPE_SOFTWARE; | 307 | attr->type = PERF_TYPE_SOFTWARE; |
310 | attr->config = PERF_COUNT_SW_CPU_CLOCK; | 308 | attr->config = PERF_COUNT_SW_CPU_CLOCK; |
311 | goto try_again; | 309 | goto try_again; |
312 | } | 310 | } |
313 | 311 | ||
314 | if (err == ENOENT) { | 312 | if (err == ENOENT) { |
315 | ui__warning("The %s event is not supported.\n", | 313 | ui__warning("The %s event is not supported.\n", |
316 | event_name(pos)); | 314 | event_name(pos)); |
317 | exit(EXIT_FAILURE); | 315 | exit(EXIT_FAILURE); |
318 | } | 316 | } |
319 | 317 | ||
320 | printf("\n"); | 318 | printf("\n"); |
321 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 319 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", |
322 | err, strerror(err)); | 320 | err, strerror(err)); |
323 | 321 | ||
324 | #if defined(__i386__) || defined(__x86_64__) | 322 | #if defined(__i386__) || defined(__x86_64__) |
325 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 323 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
326 | die("No hardware sampling interrupt available." | 324 | die("No hardware sampling interrupt available." |
327 | " No APIC? If so then you can boot the kernel" | 325 | " No APIC? If so then you can boot the kernel" |
328 | " with the \"lapic\" boot parameter to" | 326 | " with the \"lapic\" boot parameter to" |
329 | " force-enable it.\n"); | 327 | " force-enable it.\n"); |
330 | #endif | 328 | #endif |
331 | 329 | ||
332 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 330 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
333 | } | 331 | } |
334 | } | 332 | } |
335 | 333 | ||
336 | if (perf_evlist__set_filters(evlist)) { | 334 | if (perf_evlist__set_filters(evlist)) { |
337 | error("failed to set filter with %d (%s)\n", errno, | 335 | error("failed to set filter with %d (%s)\n", errno, |
338 | strerror(errno)); | 336 | strerror(errno)); |
339 | exit(-1); | 337 | exit(-1); |
340 | } | 338 | } |
341 | 339 | ||
342 | if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) | 340 | if (perf_evlist__mmap(evlist, mmap_pages, false) < 0) |
343 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 341 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
344 | 342 | ||
345 | if (file_new) | 343 | if (file_new) |
346 | session->evlist = evlist; | 344 | session->evlist = evlist; |
347 | else { | 345 | else { |
348 | if (!perf_evlist__equal(session->evlist, evlist)) { | 346 | if (!perf_evlist__equal(session->evlist, evlist)) { |
349 | fprintf(stderr, "incompatible append\n"); | 347 | fprintf(stderr, "incompatible append\n"); |
350 | exit(-1); | 348 | exit(-1); |
351 | } | 349 | } |
352 | } | 350 | } |
353 | 351 | ||
354 | perf_session__update_sample_type(session); | 352 | perf_session__update_sample_type(session); |
355 | } | 353 | } |
356 | 354 | ||
357 | static int process_buildids(void) | 355 | static int process_buildids(void) |
358 | { | 356 | { |
359 | u64 size = lseek(output, 0, SEEK_CUR); | 357 | u64 size = lseek(output, 0, SEEK_CUR); |
360 | 358 | ||
361 | if (size == 0) | 359 | if (size == 0) |
362 | return 0; | 360 | return 0; |
363 | 361 | ||
364 | session->fd = output; | 362 | session->fd = output; |
365 | return __perf_session__process_events(session, post_processing_offset, | 363 | return __perf_session__process_events(session, post_processing_offset, |
366 | size - post_processing_offset, | 364 | size - post_processing_offset, |
367 | size, &build_id__mark_dso_hit_ops); | 365 | size, &build_id__mark_dso_hit_ops); |
368 | } | 366 | } |
369 | 367 | ||
370 | static void atexit_header(void) | 368 | static void atexit_header(void) |
371 | { | 369 | { |
372 | if (!pipe_output) { | 370 | if (!pipe_output) { |
373 | session->header.data_size += bytes_written; | 371 | session->header.data_size += bytes_written; |
374 | 372 | ||
375 | if (!no_buildid) | 373 | if (!no_buildid) |
376 | process_buildids(); | 374 | process_buildids(); |
377 | perf_session__write_header(session, evsel_list, output, true); | 375 | perf_session__write_header(session, evsel_list, output, true); |
378 | perf_session__delete(session); | 376 | perf_session__delete(session); |
379 | perf_evlist__delete(evsel_list); | 377 | perf_evlist__delete(evsel_list); |
380 | symbol__exit(); | 378 | symbol__exit(); |
381 | } | 379 | } |
382 | } | 380 | } |
383 | 381 | ||
384 | static void perf_event__synthesize_guest_os(struct machine *machine, void *data) | 382 | static void perf_event__synthesize_guest_os(struct machine *machine, void *data) |
385 | { | 383 | { |
386 | int err; | 384 | int err; |
387 | struct perf_session *psession = data; | 385 | struct perf_session *psession = data; |
388 | 386 | ||
389 | if (machine__is_host(machine)) | 387 | if (machine__is_host(machine)) |
390 | return; | 388 | return; |
391 | 389 | ||
392 | /* | 390 | /* |
393 | *As for guest kernel when processing subcommand record&report, | 391 | *As for guest kernel when processing subcommand record&report, |
394 | *we arrange module mmap prior to guest kernel mmap and trigger | 392 | *we arrange module mmap prior to guest kernel mmap and trigger |
395 | *a preload dso because default guest module symbols are loaded | 393 | *a preload dso because default guest module symbols are loaded |
396 | *from guest kallsyms instead of /lib/modules/XXX/XXX. This | 394 | *from guest kallsyms instead of /lib/modules/XXX/XXX. This |
397 | *method is used to avoid symbol missing when the first addr is | 395 | *method is used to avoid symbol missing when the first addr is |
398 | *in module instead of in guest kernel. | 396 | *in module instead of in guest kernel. |
399 | */ | 397 | */ |
400 | err = perf_event__synthesize_modules(process_synthesized_event, | 398 | err = perf_event__synthesize_modules(process_synthesized_event, |
401 | psession, machine); | 399 | psession, machine); |
402 | if (err < 0) | 400 | if (err < 0) |
403 | pr_err("Couldn't record guest kernel [%d]'s reference" | 401 | pr_err("Couldn't record guest kernel [%d]'s reference" |
404 | " relocation symbol.\n", machine->pid); | 402 | " relocation symbol.\n", machine->pid); |
405 | 403 | ||
406 | /* | 404 | /* |
407 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms | 405 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms |
408 | * have no _text sometimes. | 406 | * have no _text sometimes. |
409 | */ | 407 | */ |
410 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, | 408 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
411 | psession, machine, "_text"); | 409 | psession, machine, "_text"); |
412 | if (err < 0) | 410 | if (err < 0) |
413 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, | 411 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
414 | psession, machine, | 412 | psession, machine, |
415 | "_stext"); | 413 | "_stext"); |
416 | if (err < 0) | 414 | if (err < 0) |
417 | pr_err("Couldn't record guest kernel [%d]'s reference" | 415 | pr_err("Couldn't record guest kernel [%d]'s reference" |
418 | " relocation symbol.\n", machine->pid); | 416 | " relocation symbol.\n", machine->pid); |
419 | } | 417 | } |
420 | 418 | ||
421 | static struct perf_event_header finished_round_event = { | 419 | static struct perf_event_header finished_round_event = { |
422 | .size = sizeof(struct perf_event_header), | 420 | .size = sizeof(struct perf_event_header), |
423 | .type = PERF_RECORD_FINISHED_ROUND, | 421 | .type = PERF_RECORD_FINISHED_ROUND, |
424 | }; | 422 | }; |
425 | 423 | ||
426 | static void mmap_read_all(void) | 424 | static void mmap_read_all(void) |
427 | { | 425 | { |
428 | int i; | 426 | int i; |
429 | 427 | ||
430 | for (i = 0; i < evsel_list->nr_mmaps; i++) { | 428 | for (i = 0; i < evsel_list->nr_mmaps; i++) { |
431 | if (evsel_list->mmap[i].base) | 429 | if (evsel_list->mmap[i].base) |
432 | mmap_read(&evsel_list->mmap[i]); | 430 | mmap_read(&evsel_list->mmap[i]); |
433 | } | 431 | } |
434 | 432 | ||
435 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) | 433 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) |
436 | write_output(&finished_round_event, sizeof(finished_round_event)); | 434 | write_output(&finished_round_event, sizeof(finished_round_event)); |
437 | } | 435 | } |
438 | 436 | ||
439 | static int __cmd_record(int argc, const char **argv) | 437 | static int __cmd_record(int argc, const char **argv) |
440 | { | 438 | { |
441 | int i; | ||
442 | struct stat st; | 439 | struct stat st; |
443 | int flags; | 440 | int flags; |
444 | int err; | 441 | int err; |
445 | unsigned long waking = 0; | 442 | unsigned long waking = 0; |
446 | int child_ready_pipe[2], go_pipe[2]; | 443 | int child_ready_pipe[2], go_pipe[2]; |
447 | const bool forks = argc > 0; | 444 | const bool forks = argc > 0; |
448 | char buf; | 445 | char buf; |
449 | struct machine *machine; | 446 | struct machine *machine; |
450 | 447 | ||
451 | page_size = sysconf(_SC_PAGE_SIZE); | 448 | page_size = sysconf(_SC_PAGE_SIZE); |
452 | 449 | ||
453 | atexit(sig_atexit); | 450 | atexit(sig_atexit); |
454 | signal(SIGCHLD, sig_handler); | 451 | signal(SIGCHLD, sig_handler); |
455 | signal(SIGINT, sig_handler); | 452 | signal(SIGINT, sig_handler); |
456 | signal(SIGUSR1, sig_handler); | 453 | signal(SIGUSR1, sig_handler); |
457 | 454 | ||
458 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { | 455 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { |
459 | perror("failed to create pipes"); | 456 | perror("failed to create pipes"); |
460 | exit(-1); | 457 | exit(-1); |
461 | } | 458 | } |
462 | 459 | ||
463 | if (!output_name) { | 460 | if (!output_name) { |
464 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) | 461 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) |
465 | pipe_output = 1; | 462 | pipe_output = 1; |
466 | else | 463 | else |
467 | output_name = "perf.data"; | 464 | output_name = "perf.data"; |
468 | } | 465 | } |
469 | if (output_name) { | 466 | if (output_name) { |
470 | if (!strcmp(output_name, "-")) | 467 | if (!strcmp(output_name, "-")) |
471 | pipe_output = 1; | 468 | pipe_output = 1; |
472 | else if (!stat(output_name, &st) && st.st_size) { | 469 | else if (!stat(output_name, &st) && st.st_size) { |
473 | if (write_mode == WRITE_FORCE) { | 470 | if (write_mode == WRITE_FORCE) { |
474 | char oldname[PATH_MAX]; | 471 | char oldname[PATH_MAX]; |
475 | snprintf(oldname, sizeof(oldname), "%s.old", | 472 | snprintf(oldname, sizeof(oldname), "%s.old", |
476 | output_name); | 473 | output_name); |
477 | unlink(oldname); | 474 | unlink(oldname); |
478 | rename(output_name, oldname); | 475 | rename(output_name, oldname); |
479 | } | 476 | } |
480 | } else if (write_mode == WRITE_APPEND) { | 477 | } else if (write_mode == WRITE_APPEND) { |
481 | write_mode = WRITE_FORCE; | 478 | write_mode = WRITE_FORCE; |
482 | } | 479 | } |
483 | } | 480 | } |
484 | 481 | ||
485 | flags = O_CREAT|O_RDWR; | 482 | flags = O_CREAT|O_RDWR; |
486 | if (write_mode == WRITE_APPEND) | 483 | if (write_mode == WRITE_APPEND) |
487 | file_new = 0; | 484 | file_new = 0; |
488 | else | 485 | else |
489 | flags |= O_TRUNC; | 486 | flags |= O_TRUNC; |
490 | 487 | ||
491 | if (pipe_output) | 488 | if (pipe_output) |
492 | output = STDOUT_FILENO; | 489 | output = STDOUT_FILENO; |
493 | else | 490 | else |
494 | output = open(output_name, flags, S_IRUSR | S_IWUSR); | 491 | output = open(output_name, flags, S_IRUSR | S_IWUSR); |
495 | if (output < 0) { | 492 | if (output < 0) { |
496 | perror("failed to create output file"); | 493 | perror("failed to create output file"); |
497 | exit(-1); | 494 | exit(-1); |
498 | } | 495 | } |
499 | 496 | ||
500 | session = perf_session__new(output_name, O_WRONLY, | 497 | session = perf_session__new(output_name, O_WRONLY, |
501 | write_mode == WRITE_FORCE, false, NULL); | 498 | write_mode == WRITE_FORCE, false, NULL); |
502 | if (session == NULL) { | 499 | if (session == NULL) { |
503 | pr_err("Not enough memory for reading perf file header\n"); | 500 | pr_err("Not enough memory for reading perf file header\n"); |
504 | return -1; | 501 | return -1; |
505 | } | 502 | } |
506 | 503 | ||
507 | if (!no_buildid) | 504 | if (!no_buildid) |
508 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); | 505 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); |
509 | 506 | ||
510 | if (!file_new) { | 507 | if (!file_new) { |
511 | err = perf_session__read_header(session, output); | 508 | err = perf_session__read_header(session, output); |
512 | if (err < 0) | 509 | if (err < 0) |
513 | goto out_delete_session; | 510 | goto out_delete_session; |
514 | } | 511 | } |
515 | 512 | ||
516 | if (have_tracepoints(&evsel_list->entries)) | 513 | if (have_tracepoints(&evsel_list->entries)) |
517 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 514 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
518 | 515 | ||
519 | /* 512 kiB: default amount of unprivileged mlocked memory */ | 516 | /* 512 kiB: default amount of unprivileged mlocked memory */ |
520 | if (mmap_pages == UINT_MAX) | 517 | if (mmap_pages == UINT_MAX) |
521 | mmap_pages = (512 * 1024) / page_size; | 518 | mmap_pages = (512 * 1024) / page_size; |
522 | 519 | ||
523 | if (forks) { | 520 | if (forks) { |
524 | child_pid = fork(); | 521 | child_pid = fork(); |
525 | if (child_pid < 0) { | 522 | if (child_pid < 0) { |
526 | perror("failed to fork"); | 523 | perror("failed to fork"); |
527 | exit(-1); | 524 | exit(-1); |
528 | } | 525 | } |
529 | 526 | ||
530 | if (!child_pid) { | 527 | if (!child_pid) { |
531 | if (pipe_output) | 528 | if (pipe_output) |
532 | dup2(2, 1); | 529 | dup2(2, 1); |
533 | close(child_ready_pipe[0]); | 530 | close(child_ready_pipe[0]); |
534 | close(go_pipe[1]); | 531 | close(go_pipe[1]); |
535 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | 532 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); |
536 | 533 | ||
537 | /* | 534 | /* |
538 | * Do a dummy execvp to get the PLT entry resolved, | 535 | * Do a dummy execvp to get the PLT entry resolved, |
539 | * so we avoid the resolver overhead on the real | 536 | * so we avoid the resolver overhead on the real |
540 | * execvp call. | 537 | * execvp call. |
541 | */ | 538 | */ |
542 | execvp("", (char **)argv); | 539 | execvp("", (char **)argv); |
543 | 540 | ||
544 | /* | 541 | /* |
545 | * Tell the parent we're ready to go | 542 | * Tell the parent we're ready to go |
546 | */ | 543 | */ |
547 | close(child_ready_pipe[1]); | 544 | close(child_ready_pipe[1]); |
548 | 545 | ||
549 | /* | 546 | /* |
550 | * Wait until the parent tells us to go. | 547 | * Wait until the parent tells us to go. |
551 | */ | 548 | */ |
552 | if (read(go_pipe[0], &buf, 1) == -1) | 549 | if (read(go_pipe[0], &buf, 1) == -1) |
553 | perror("unable to read pipe"); | 550 | perror("unable to read pipe"); |
554 | 551 | ||
555 | execvp(argv[0], (char **)argv); | 552 | execvp(argv[0], (char **)argv); |
556 | 553 | ||
557 | perror(argv[0]); | 554 | perror(argv[0]); |
558 | kill(getppid(), SIGUSR1); | 555 | kill(getppid(), SIGUSR1); |
559 | exit(-1); | 556 | exit(-1); |
560 | } | 557 | } |
561 | 558 | ||
562 | if (!system_wide && target_tid == -1 && target_pid == -1) | 559 | if (!system_wide && target_tid == -1 && target_pid == -1) |
563 | evsel_list->threads->map[0] = child_pid; | 560 | evsel_list->threads->map[0] = child_pid; |
564 | 561 | ||
565 | close(child_ready_pipe[1]); | 562 | close(child_ready_pipe[1]); |
566 | close(go_pipe[0]); | 563 | close(go_pipe[0]); |
567 | /* | 564 | /* |
568 | * wait for child to settle | 565 | * wait for child to settle |
569 | */ | 566 | */ |
570 | if (read(child_ready_pipe[0], &buf, 1) == -1) { | 567 | if (read(child_ready_pipe[0], &buf, 1) == -1) { |
571 | perror("unable to read pipe"); | 568 | perror("unable to read pipe"); |
572 | exit(-1); | 569 | exit(-1); |
573 | } | 570 | } |
574 | close(child_ready_pipe[0]); | 571 | close(child_ready_pipe[0]); |
575 | } | 572 | } |
576 | 573 | ||
577 | open_counters(evsel_list); | 574 | open_counters(evsel_list); |
578 | 575 | ||
579 | /* | 576 | /* |
580 | * perf_session__delete(session) will be called at atexit_header() | 577 | * perf_session__delete(session) will be called at atexit_header() |
581 | */ | 578 | */ |
582 | atexit(atexit_header); | 579 | atexit(atexit_header); |
583 | 580 | ||
584 | if (pipe_output) { | 581 | if (pipe_output) { |
585 | err = perf_header__write_pipe(output); | 582 | err = perf_header__write_pipe(output); |
586 | if (err < 0) | 583 | if (err < 0) |
587 | return err; | 584 | return err; |
588 | } else if (file_new) { | 585 | } else if (file_new) { |
589 | err = perf_session__write_header(session, evsel_list, | 586 | err = perf_session__write_header(session, evsel_list, |
590 | output, false); | 587 | output, false); |
591 | if (err < 0) | 588 | if (err < 0) |
592 | return err; | 589 | return err; |
593 | } | 590 | } |
594 | 591 | ||
595 | post_processing_offset = lseek(output, 0, SEEK_CUR); | 592 | post_processing_offset = lseek(output, 0, SEEK_CUR); |
596 | 593 | ||
597 | if (pipe_output) { | 594 | if (pipe_output) { |
598 | err = perf_session__synthesize_attrs(session, | 595 | err = perf_session__synthesize_attrs(session, |
599 | process_synthesized_event); | 596 | process_synthesized_event); |
600 | if (err < 0) { | 597 | if (err < 0) { |
601 | pr_err("Couldn't synthesize attrs.\n"); | 598 | pr_err("Couldn't synthesize attrs.\n"); |
602 | return err; | 599 | return err; |
603 | } | 600 | } |
604 | 601 | ||
605 | err = perf_event__synthesize_event_types(process_synthesized_event, | 602 | err = perf_event__synthesize_event_types(process_synthesized_event, |
606 | session); | 603 | session); |
607 | if (err < 0) { | 604 | if (err < 0) { |
608 | pr_err("Couldn't synthesize event_types.\n"); | 605 | pr_err("Couldn't synthesize event_types.\n"); |
609 | return err; | 606 | return err; |
610 | } | 607 | } |
611 | 608 | ||
612 | if (have_tracepoints(&evsel_list->entries)) { | 609 | if (have_tracepoints(&evsel_list->entries)) { |
613 | /* | 610 | /* |
614 | * FIXME err <= 0 here actually means that | 611 | * FIXME err <= 0 here actually means that |
615 | * there were no tracepoints so its not really | 612 | * there were no tracepoints so its not really |
616 | * an error, just that we don't need to | 613 | * an error, just that we don't need to |
617 | * synthesize anything. We really have to | 614 | * synthesize anything. We really have to |
618 | * return this more properly and also | 615 | * return this more properly and also |
619 | * propagate errors that now are calling die() | 616 | * propagate errors that now are calling die() |
620 | */ | 617 | */ |
621 | err = perf_event__synthesize_tracing_data(output, evsel_list, | 618 | err = perf_event__synthesize_tracing_data(output, evsel_list, |
622 | process_synthesized_event, | 619 | process_synthesized_event, |
623 | session); | 620 | session); |
624 | if (err <= 0) { | 621 | if (err <= 0) { |
625 | pr_err("Couldn't record tracing data.\n"); | 622 | pr_err("Couldn't record tracing data.\n"); |
626 | return err; | 623 | return err; |
627 | } | 624 | } |
628 | advance_output(err); | 625 | advance_output(err); |
629 | } | 626 | } |
630 | } | 627 | } |
631 | 628 | ||
632 | machine = perf_session__find_host_machine(session); | 629 | machine = perf_session__find_host_machine(session); |
633 | if (!machine) { | 630 | if (!machine) { |
634 | pr_err("Couldn't find native kernel information.\n"); | 631 | pr_err("Couldn't find native kernel information.\n"); |
635 | return -1; | 632 | return -1; |
636 | } | 633 | } |
637 | 634 | ||
638 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, | 635 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
639 | session, machine, "_text"); | 636 | session, machine, "_text"); |
640 | if (err < 0) | 637 | if (err < 0) |
641 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, | 638 | err = perf_event__synthesize_kernel_mmap(process_synthesized_event, |
642 | session, machine, "_stext"); | 639 | session, machine, "_stext"); |
643 | if (err < 0) | 640 | if (err < 0) |
644 | pr_err("Couldn't record kernel reference relocation symbol\n" | 641 | pr_err("Couldn't record kernel reference relocation symbol\n" |
645 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | 642 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" |
646 | "Check /proc/kallsyms permission or run as root.\n"); | 643 | "Check /proc/kallsyms permission or run as root.\n"); |
647 | 644 | ||
648 | err = perf_event__synthesize_modules(process_synthesized_event, | 645 | err = perf_event__synthesize_modules(process_synthesized_event, |
649 | session, machine); | 646 | session, machine); |
650 | if (err < 0) | 647 | if (err < 0) |
651 | pr_err("Couldn't record kernel module information.\n" | 648 | pr_err("Couldn't record kernel module information.\n" |
652 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | 649 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" |
653 | "Check /proc/modules permission or run as root.\n"); | 650 | "Check /proc/modules permission or run as root.\n"); |
654 | 651 | ||
655 | if (perf_guest) | 652 | if (perf_guest) |
656 | perf_session__process_machines(session, | 653 | perf_session__process_machines(session, |
657 | perf_event__synthesize_guest_os); | 654 | perf_event__synthesize_guest_os); |
658 | 655 | ||
659 | if (!system_wide) | 656 | if (!system_wide) |
660 | perf_event__synthesize_thread_map(evsel_list->threads, | 657 | perf_event__synthesize_thread_map(evsel_list->threads, |
661 | process_synthesized_event, | 658 | process_synthesized_event, |
662 | session); | 659 | session); |
663 | else | 660 | else |
664 | perf_event__synthesize_threads(process_synthesized_event, | 661 | perf_event__synthesize_threads(process_synthesized_event, |
665 | session); | 662 | session); |
666 | 663 | ||
667 | if (realtime_prio) { | 664 | if (realtime_prio) { |
668 | struct sched_param param; | 665 | struct sched_param param; |
669 | 666 | ||
670 | param.sched_priority = realtime_prio; | 667 | param.sched_priority = realtime_prio; |
671 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 668 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
672 | pr_err("Could not set realtime priority.\n"); | 669 | pr_err("Could not set realtime priority.\n"); |
673 | exit(-1); | 670 | exit(-1); |
674 | } | 671 | } |
675 | } | 672 | } |
676 | 673 | ||
677 | /* | 674 | /* |
678 | * Let the child rip | 675 | * Let the child rip |
679 | */ | 676 | */ |
680 | if (forks) | 677 | if (forks) |
681 | close(go_pipe[1]); | 678 | close(go_pipe[1]); |
682 | 679 | ||
683 | for (;;) { | 680 | for (;;) { |
684 | int hits = samples; | 681 | int hits = samples; |
685 | int thread; | ||
686 | 682 | ||
687 | mmap_read_all(); | 683 | mmap_read_all(); |
688 | 684 | ||
689 | if (hits == samples) { | 685 | if (hits == samples) { |
690 | if (done) | 686 | if (done) |
691 | break; | 687 | break; |
692 | err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); | 688 | err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); |
693 | waking++; | 689 | waking++; |
694 | } | 690 | } |
695 | 691 | ||
696 | if (done) { | 692 | if (done) |
697 | for (i = 0; i < evsel_list->cpus->nr; i++) { | 693 | perf_evlist__disable(evsel_list); |
698 | struct perf_evsel *pos; | ||
699 | |||
700 | list_for_each_entry(pos, &evsel_list->entries, node) { | ||
701 | for (thread = 0; | ||
702 | thread < evsel_list->threads->nr; | ||
703 | thread++) | ||
704 | ioctl(FD(pos, i, thread), | ||
705 | PERF_EVENT_IOC_DISABLE); | ||
706 | } | ||
707 | } | ||
708 | } | ||
709 | } | 694 | } |
710 | 695 | ||
711 | if (quiet || signr == SIGUSR1) | 696 | if (quiet || signr == SIGUSR1) |
712 | return 0; | 697 | return 0; |
713 | 698 | ||
714 | fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); | 699 | fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); |
715 | 700 | ||
716 | /* | 701 | /* |
717 | * Approximate RIP event size: 24 bytes. | 702 | * Approximate RIP event size: 24 bytes. |
718 | */ | 703 | */ |
719 | fprintf(stderr, | 704 | fprintf(stderr, |
720 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", | 705 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", |
721 | (double)bytes_written / 1024.0 / 1024.0, | 706 | (double)bytes_written / 1024.0 / 1024.0, |
722 | output_name, | 707 | output_name, |
723 | bytes_written / 24); | 708 | bytes_written / 24); |
724 | 709 | ||
725 | return 0; | 710 | return 0; |
726 | 711 | ||
727 | out_delete_session: | 712 | out_delete_session: |
728 | perf_session__delete(session); | 713 | perf_session__delete(session); |
729 | return err; | 714 | return err; |
730 | } | 715 | } |
731 | 716 | ||
732 | static const char * const record_usage[] = { | 717 | static const char * const record_usage[] = { |
733 | "perf record [<options>] [<command>]", | 718 | "perf record [<options>] [<command>]", |
734 | "perf record [<options>] -- <command> [<options>]", | 719 | "perf record [<options>] -- <command> [<options>]", |
735 | NULL | 720 | NULL |
736 | }; | 721 | }; |
737 | 722 | ||
738 | static bool force, append_file; | 723 | static bool force, append_file; |
739 | 724 | ||
740 | const struct option record_options[] = { | 725 | const struct option record_options[] = { |
741 | OPT_CALLBACK('e', "event", &evsel_list, "event", | 726 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
742 | "event selector. use 'perf list' to list available events", | 727 | "event selector. use 'perf list' to list available events", |
743 | parse_events_option), | 728 | parse_events_option), |
744 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | 729 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
745 | "event filter", parse_filter), | 730 | "event filter", parse_filter), |
746 | OPT_INTEGER('p', "pid", &target_pid, | 731 | OPT_INTEGER('p', "pid", &target_pid, |
747 | "record events on existing process id"), | 732 | "record events on existing process id"), |
748 | OPT_INTEGER('t', "tid", &target_tid, | 733 | OPT_INTEGER('t', "tid", &target_tid, |
749 | "record events on existing thread id"), | 734 | "record events on existing thread id"), |
750 | OPT_INTEGER('r', "realtime", &realtime_prio, | 735 | OPT_INTEGER('r', "realtime", &realtime_prio, |
751 | "collect data with this RT SCHED_FIFO priority"), | 736 | "collect data with this RT SCHED_FIFO priority"), |
752 | OPT_BOOLEAN('D', "no-delay", &nodelay, | 737 | OPT_BOOLEAN('D', "no-delay", &nodelay, |
753 | "collect data without buffering"), | 738 | "collect data without buffering"), |
754 | OPT_BOOLEAN('R', "raw-samples", &raw_samples, | 739 | OPT_BOOLEAN('R', "raw-samples", &raw_samples, |
755 | "collect raw sample records from all opened counters"), | 740 | "collect raw sample records from all opened counters"), |
756 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 741 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
757 | "system-wide collection from all CPUs"), | 742 | "system-wide collection from all CPUs"), |
758 | OPT_BOOLEAN('A', "append", &append_file, | 743 | OPT_BOOLEAN('A', "append", &append_file, |
759 | "append to the output file to do incremental profiling"), | 744 | "append to the output file to do incremental profiling"), |
760 | OPT_STRING('C', "cpu", &cpu_list, "cpu", | 745 | OPT_STRING('C', "cpu", &cpu_list, "cpu", |
761 | "list of cpus to monitor"), | 746 | "list of cpus to monitor"), |
762 | OPT_BOOLEAN('f', "force", &force, | 747 | OPT_BOOLEAN('f', "force", &force, |
763 | "overwrite existing data file (deprecated)"), | 748 | "overwrite existing data file (deprecated)"), |
764 | OPT_U64('c', "count", &user_interval, "event period to sample"), | 749 | OPT_U64('c', "count", &user_interval, "event period to sample"), |
765 | OPT_STRING('o', "output", &output_name, "file", | 750 | OPT_STRING('o', "output", &output_name, "file", |
766 | "output file name"), | 751 | "output file name"), |
767 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 752 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, |
768 | "child tasks do not inherit counters"), | 753 | "child tasks do not inherit counters"), |
769 | OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"), | 754 | OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"), |
770 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), | 755 | OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"), |
771 | OPT_BOOLEAN('g', "call-graph", &call_graph, | 756 | OPT_BOOLEAN('g', "call-graph", &call_graph, |
772 | "do call-graph (stack chain/backtrace) recording"), | 757 | "do call-graph (stack chain/backtrace) recording"), |
773 | OPT_INCR('v', "verbose", &verbose, | 758 | OPT_INCR('v', "verbose", &verbose, |
774 | "be more verbose (show counter open errors, etc)"), | 759 | "be more verbose (show counter open errors, etc)"), |
775 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), | 760 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), |
776 | OPT_BOOLEAN('s', "stat", &inherit_stat, | 761 | OPT_BOOLEAN('s', "stat", &inherit_stat, |
777 | "per thread counts"), | 762 | "per thread counts"), |
778 | OPT_BOOLEAN('d', "data", &sample_address, | 763 | OPT_BOOLEAN('d', "data", &sample_address, |
779 | "Sample addresses"), | 764 | "Sample addresses"), |
780 | OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"), | 765 | OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"), |
781 | OPT_BOOLEAN('n', "no-samples", &no_samples, | 766 | OPT_BOOLEAN('n', "no-samples", &no_samples, |
782 | "don't sample"), | 767 | "don't sample"), |
783 | OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache, | 768 | OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache, |
784 | "do not update the buildid cache"), | 769 | "do not update the buildid cache"), |
785 | OPT_BOOLEAN('B', "no-buildid", &no_buildid, | 770 | OPT_BOOLEAN('B', "no-buildid", &no_buildid, |
786 | "do not collect buildids in perf.data"), | 771 | "do not collect buildids in perf.data"), |
787 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | 772 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", |
788 | "monitor event in cgroup name only", | 773 | "monitor event in cgroup name only", |
789 | parse_cgroups), | 774 | parse_cgroups), |
790 | OPT_END() | 775 | OPT_END() |
791 | }; | 776 | }; |
792 | 777 | ||
793 | int cmd_record(int argc, const char **argv, const char *prefix __used) | 778 | int cmd_record(int argc, const char **argv, const char *prefix __used) |
794 | { | 779 | { |
795 | int err = -ENOMEM; | 780 | int err = -ENOMEM; |
796 | struct perf_evsel *pos; | 781 | struct perf_evsel *pos; |
797 | 782 | ||
798 | evsel_list = perf_evlist__new(NULL, NULL); | 783 | evsel_list = perf_evlist__new(NULL, NULL); |
799 | if (evsel_list == NULL) | 784 | if (evsel_list == NULL) |
800 | return -ENOMEM; | 785 | return -ENOMEM; |
801 | 786 | ||
802 | argc = parse_options(argc, argv, record_options, record_usage, | 787 | argc = parse_options(argc, argv, record_options, record_usage, |
803 | PARSE_OPT_STOP_AT_NON_OPTION); | 788 | PARSE_OPT_STOP_AT_NON_OPTION); |
804 | if (!argc && target_pid == -1 && target_tid == -1 && | 789 | if (!argc && target_pid == -1 && target_tid == -1 && |
805 | !system_wide && !cpu_list) | 790 | !system_wide && !cpu_list) |
806 | usage_with_options(record_usage, record_options); | 791 | usage_with_options(record_usage, record_options); |
807 | 792 | ||
808 | if (force && append_file) { | 793 | if (force && append_file) { |
809 | fprintf(stderr, "Can't overwrite and append at the same time." | 794 | fprintf(stderr, "Can't overwrite and append at the same time." |
810 | " You need to choose between -f and -A"); | 795 | " You need to choose between -f and -A"); |
811 | usage_with_options(record_usage, record_options); | 796 | usage_with_options(record_usage, record_options); |
812 | } else if (append_file) { | 797 | } else if (append_file) { |
813 | write_mode = WRITE_APPEND; | 798 | write_mode = WRITE_APPEND; |
814 | } else { | 799 | } else { |
815 | write_mode = WRITE_FORCE; | 800 | write_mode = WRITE_FORCE; |
816 | } | 801 | } |
817 | 802 | ||
818 | if (nr_cgroups && !system_wide) { | 803 | if (nr_cgroups && !system_wide) { |
819 | fprintf(stderr, "cgroup monitoring only available in" | 804 | fprintf(stderr, "cgroup monitoring only available in" |
820 | " system-wide mode\n"); | 805 | " system-wide mode\n"); |
821 | usage_with_options(record_usage, record_options); | 806 | usage_with_options(record_usage, record_options); |
822 | } | 807 | } |
823 | 808 | ||
824 | symbol__init(); | 809 | symbol__init(); |
825 | 810 | ||
826 | if (symbol_conf.kptr_restrict) | 811 | if (symbol_conf.kptr_restrict) |
827 | pr_warning( | 812 | pr_warning( |
828 | "WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n" | 813 | "WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,\n" |
829 | "check /proc/sys/kernel/kptr_restrict.\n\n" | 814 | "check /proc/sys/kernel/kptr_restrict.\n\n" |
830 | "Samples in kernel functions may not be resolved if a suitable vmlinux\n" | 815 | "Samples in kernel functions may not be resolved if a suitable vmlinux\n" |
831 | "file is not found in the buildid cache or in the vmlinux path.\n\n" | 816 | "file is not found in the buildid cache or in the vmlinux path.\n\n" |
832 | "Samples in kernel modules won't be resolved at all.\n\n" | 817 | "Samples in kernel modules won't be resolved at all.\n\n" |
833 | "If some relocation was applied (e.g. kexec) symbols may be misresolved\n" | 818 | "If some relocation was applied (e.g. kexec) symbols may be misresolved\n" |
834 | "even with a suitable vmlinux or kallsyms file.\n\n"); | 819 | "even with a suitable vmlinux or kallsyms file.\n\n"); |
835 | 820 | ||
836 | if (no_buildid_cache || no_buildid) | 821 | if (no_buildid_cache || no_buildid) |
837 | disable_buildid_cache(); | 822 | disable_buildid_cache(); |
838 | 823 | ||
839 | if (evsel_list->nr_entries == 0 && | 824 | if (evsel_list->nr_entries == 0 && |
840 | perf_evlist__add_default(evsel_list) < 0) { | 825 | perf_evlist__add_default(evsel_list) < 0) { |
841 | pr_err("Not enough memory for event selector list\n"); | 826 | pr_err("Not enough memory for event selector list\n"); |
842 | goto out_symbol_exit; | 827 | goto out_symbol_exit; |
843 | } | 828 | } |
844 | 829 | ||
845 | if (target_pid != -1) | 830 | if (target_pid != -1) |
846 | target_tid = target_pid; | 831 | target_tid = target_pid; |
847 | 832 | ||
848 | if (perf_evlist__create_maps(evsel_list, target_pid, | 833 | if (perf_evlist__create_maps(evsel_list, target_pid, |
849 | target_tid, cpu_list) < 0) | 834 | target_tid, cpu_list) < 0) |
850 | usage_with_options(record_usage, record_options); | 835 | usage_with_options(record_usage, record_options); |
851 | 836 | ||
852 | list_for_each_entry(pos, &evsel_list->entries, node) { | 837 | list_for_each_entry(pos, &evsel_list->entries, node) { |
853 | if (perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, | 838 | if (perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, |
854 | evsel_list->threads->nr) < 0) | 839 | evsel_list->threads->nr) < 0) |
855 | goto out_free_fd; | 840 | goto out_free_fd; |
856 | if (perf_header__push_event(pos->attr.config, event_name(pos))) | 841 | if (perf_header__push_event(pos->attr.config, event_name(pos))) |
857 | goto out_free_fd; | 842 | goto out_free_fd; |
858 | } | 843 | } |
859 | 844 | ||
860 | if (perf_evlist__alloc_pollfd(evsel_list) < 0) | 845 | if (perf_evlist__alloc_pollfd(evsel_list) < 0) |
861 | goto out_free_fd; | 846 | goto out_free_fd; |
862 | 847 | ||
863 | if (user_interval != ULLONG_MAX) | 848 | if (user_interval != ULLONG_MAX) |
864 | default_interval = user_interval; | 849 | default_interval = user_interval; |
865 | if (user_freq != UINT_MAX) | 850 | if (user_freq != UINT_MAX) |
866 | freq = user_freq; | 851 | freq = user_freq; |
867 | 852 | ||
868 | /* | 853 | /* |
869 | * User specified count overrides default frequency. | 854 | * User specified count overrides default frequency. |
870 | */ | 855 | */ |
871 | if (default_interval) | 856 | if (default_interval) |
872 | freq = 0; | 857 | freq = 0; |
873 | else if (freq) { | 858 | else if (freq) { |
874 | default_interval = freq; | 859 | default_interval = freq; |
875 | } else { | 860 | } else { |
876 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 861 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
877 | err = -EINVAL; | 862 | err = -EINVAL; |
878 | goto out_free_fd; | 863 | goto out_free_fd; |
879 | } | 864 | } |
880 | 865 | ||
881 | err = __cmd_record(argc, argv); | 866 | err = __cmd_record(argc, argv); |
882 | out_free_fd: | 867 | out_free_fd: |
883 | perf_evlist__delete_maps(evsel_list); | 868 | perf_evlist__delete_maps(evsel_list); |
884 | out_symbol_exit: | 869 | out_symbol_exit: |
885 | symbol__exit(); | 870 | symbol__exit(); |
886 | return err; | 871 | return err; |
887 | } | 872 | } |
888 | 873 |
tools/perf/builtin-report.c
1 | /* | 1 | /* |
2 | * builtin-report.c | 2 | * builtin-report.c |
3 | * | 3 | * |
4 | * Builtin report command: Analyze the perf.data input file, | 4 | * Builtin report command: Analyze the perf.data input file, |
5 | * look up and read DSOs and symbol information and display | 5 | * look up and read DSOs and symbol information and display |
6 | * a histogram of results, along various sorting keys. | 6 | * a histogram of results, along various sorting keys. |
7 | */ | 7 | */ |
8 | #include "builtin.h" | 8 | #include "builtin.h" |
9 | 9 | ||
10 | #include "util/util.h" | 10 | #include "util/util.h" |
11 | 11 | ||
12 | #include "util/annotate.h" | 12 | #include "util/annotate.h" |
13 | #include "util/color.h" | 13 | #include "util/color.h" |
14 | #include <linux/list.h> | 14 | #include <linux/list.h> |
15 | #include "util/cache.h" | 15 | #include "util/cache.h" |
16 | #include <linux/rbtree.h> | 16 | #include <linux/rbtree.h> |
17 | #include "util/symbol.h" | 17 | #include "util/symbol.h" |
18 | #include "util/callchain.h" | 18 | #include "util/callchain.h" |
19 | #include "util/strlist.h" | 19 | #include "util/strlist.h" |
20 | #include "util/values.h" | 20 | #include "util/values.h" |
21 | 21 | ||
22 | #include "perf.h" | 22 | #include "perf.h" |
23 | #include "util/debug.h" | 23 | #include "util/debug.h" |
24 | #include "util/evlist.h" | 24 | #include "util/evlist.h" |
25 | #include "util/evsel.h" | 25 | #include "util/evsel.h" |
26 | #include "util/header.h" | 26 | #include "util/header.h" |
27 | #include "util/session.h" | 27 | #include "util/session.h" |
28 | 28 | ||
29 | #include "util/parse-options.h" | 29 | #include "util/parse-options.h" |
30 | #include "util/parse-events.h" | 30 | #include "util/parse-events.h" |
31 | 31 | ||
32 | #include "util/thread.h" | 32 | #include "util/thread.h" |
33 | #include "util/sort.h" | 33 | #include "util/sort.h" |
34 | #include "util/hist.h" | 34 | #include "util/hist.h" |
35 | 35 | ||
36 | #include <linux/bitmap.h> | 36 | #include <linux/bitmap.h> |
37 | 37 | ||
38 | static char const *input_name = "perf.data"; | 38 | static char const *input_name = "perf.data"; |
39 | 39 | ||
40 | static bool force, use_tui, use_stdio; | 40 | static bool force, use_tui, use_stdio; |
41 | static bool hide_unresolved; | 41 | static bool hide_unresolved; |
42 | static bool dont_use_callchains; | 42 | static bool dont_use_callchains; |
43 | 43 | ||
44 | static bool show_threads; | 44 | static bool show_threads; |
45 | static struct perf_read_values show_threads_values; | 45 | static struct perf_read_values show_threads_values; |
46 | 46 | ||
47 | static const char default_pretty_printing_style[] = "normal"; | 47 | static const char default_pretty_printing_style[] = "normal"; |
48 | static const char *pretty_printing_style = default_pretty_printing_style; | 48 | static const char *pretty_printing_style = default_pretty_printing_style; |
49 | 49 | ||
50 | static char callchain_default_opt[] = "fractal,0.5,callee"; | 50 | static char callchain_default_opt[] = "fractal,0.5,callee"; |
51 | static bool inverted_callchain; | 51 | static bool inverted_callchain; |
52 | static symbol_filter_t annotate_init; | 52 | static symbol_filter_t annotate_init; |
53 | 53 | ||
54 | static const char *cpu_list; | 54 | static const char *cpu_list; |
55 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 55 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
56 | 56 | ||
57 | static int perf_session__add_hist_entry(struct perf_session *session, | 57 | static int perf_session__add_hist_entry(struct perf_session *session, |
58 | struct addr_location *al, | 58 | struct addr_location *al, |
59 | struct perf_sample *sample, | 59 | struct perf_sample *sample, |
60 | struct perf_evsel *evsel) | 60 | struct perf_evsel *evsel) |
61 | { | 61 | { |
62 | struct symbol *parent = NULL; | 62 | struct symbol *parent = NULL; |
63 | int err = 0; | 63 | int err = 0; |
64 | struct hist_entry *he; | 64 | struct hist_entry *he; |
65 | 65 | ||
66 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | 66 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { |
67 | err = perf_session__resolve_callchain(session, al->thread, | 67 | err = perf_session__resolve_callchain(session, al->thread, |
68 | sample->callchain, &parent); | 68 | sample->callchain, &parent); |
69 | if (err) | 69 | if (err) |
70 | return err; | 70 | return err; |
71 | } | 71 | } |
72 | 72 | ||
73 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period); | 73 | he = __hists__add_entry(&evsel->hists, al, parent, sample->period); |
74 | if (he == NULL) | 74 | if (he == NULL) |
75 | return -ENOMEM; | 75 | return -ENOMEM; |
76 | 76 | ||
77 | if (symbol_conf.use_callchain) { | 77 | if (symbol_conf.use_callchain) { |
78 | err = callchain_append(he->callchain, &session->callchain_cursor, | 78 | err = callchain_append(he->callchain, &session->callchain_cursor, |
79 | sample->period); | 79 | sample->period); |
80 | if (err) | 80 | if (err) |
81 | return err; | 81 | return err; |
82 | } | 82 | } |
83 | /* | 83 | /* |
84 | * Only in the newt browser we are doing integrated annotation, | 84 | * Only in the newt browser we are doing integrated annotation, |
85 | * so we don't allocated the extra space needed because the stdio | 85 | * so we don't allocated the extra space needed because the stdio |
86 | * code will not use it. | 86 | * code will not use it. |
87 | */ | 87 | */ |
88 | if (al->sym != NULL && use_browser > 0) { | 88 | if (al->sym != NULL && use_browser > 0) { |
89 | struct annotation *notes = symbol__annotation(he->ms.sym); | 89 | struct annotation *notes = symbol__annotation(he->ms.sym); |
90 | 90 | ||
91 | assert(evsel != NULL); | 91 | assert(evsel != NULL); |
92 | 92 | ||
93 | err = -ENOMEM; | 93 | err = -ENOMEM; |
94 | if (notes->src == NULL && | 94 | if (notes->src == NULL && |
95 | symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0) | 95 | symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0) |
96 | goto out; | 96 | goto out; |
97 | 97 | ||
98 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | 98 | err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
99 | } | 99 | } |
100 | 100 | ||
101 | evsel->hists.stats.total_period += sample->period; | 101 | evsel->hists.stats.total_period += sample->period; |
102 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 102 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
103 | out: | 103 | out: |
104 | return err; | 104 | return err; |
105 | } | 105 | } |
106 | 106 | ||
107 | 107 | ||
108 | static int process_sample_event(union perf_event *event, | 108 | static int process_sample_event(union perf_event *event, |
109 | struct perf_sample *sample, | 109 | struct perf_sample *sample, |
110 | struct perf_evsel *evsel, | 110 | struct perf_evsel *evsel, |
111 | struct perf_session *session) | 111 | struct perf_session *session) |
112 | { | 112 | { |
113 | struct addr_location al; | 113 | struct addr_location al; |
114 | 114 | ||
115 | if (perf_event__preprocess_sample(event, session, &al, sample, | 115 | if (perf_event__preprocess_sample(event, session, &al, sample, |
116 | annotate_init) < 0) { | 116 | annotate_init) < 0) { |
117 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 117 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
118 | event->header.type); | 118 | event->header.type); |
119 | return -1; | 119 | return -1; |
120 | } | 120 | } |
121 | 121 | ||
122 | if (al.filtered || (hide_unresolved && al.sym == NULL)) | 122 | if (al.filtered || (hide_unresolved && al.sym == NULL)) |
123 | return 0; | 123 | return 0; |
124 | 124 | ||
125 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | 125 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) |
126 | return 0; | 126 | return 0; |
127 | 127 | ||
128 | if (al.map != NULL) | 128 | if (al.map != NULL) |
129 | al.map->dso->hit = 1; | 129 | al.map->dso->hit = 1; |
130 | 130 | ||
131 | if (perf_session__add_hist_entry(session, &al, sample, evsel)) { | 131 | if (perf_session__add_hist_entry(session, &al, sample, evsel)) { |
132 | pr_debug("problem incrementing symbol period, skipping event\n"); | 132 | pr_debug("problem incrementing symbol period, skipping event\n"); |
133 | return -1; | 133 | return -1; |
134 | } | 134 | } |
135 | 135 | ||
136 | return 0; | 136 | return 0; |
137 | } | 137 | } |
138 | 138 | ||
139 | static int process_read_event(union perf_event *event, | 139 | static int process_read_event(union perf_event *event, |
140 | struct perf_sample *sample __used, | 140 | struct perf_sample *sample __used, |
141 | struct perf_session *session) | 141 | struct perf_session *session) |
142 | { | 142 | { |
143 | struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, | 143 | struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist, |
144 | event->read.id); | 144 | event->read.id); |
145 | if (show_threads) { | 145 | if (show_threads) { |
146 | const char *name = evsel ? event_name(evsel) : "unknown"; | 146 | const char *name = evsel ? event_name(evsel) : "unknown"; |
147 | perf_read_values_add_value(&show_threads_values, | 147 | perf_read_values_add_value(&show_threads_values, |
148 | event->read.pid, event->read.tid, | 148 | event->read.pid, event->read.tid, |
149 | event->read.id, | 149 | event->read.id, |
150 | name, | 150 | name, |
151 | event->read.value); | 151 | event->read.value); |
152 | } | 152 | } |
153 | 153 | ||
154 | dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, | 154 | dump_printf(": %d %d %s %" PRIu64 "\n", event->read.pid, event->read.tid, |
155 | evsel ? event_name(evsel) : "FAIL", | 155 | evsel ? event_name(evsel) : "FAIL", |
156 | event->read.value); | 156 | event->read.value); |
157 | 157 | ||
158 | return 0; | 158 | return 0; |
159 | } | 159 | } |
160 | 160 | ||
161 | static int perf_session__setup_sample_type(struct perf_session *self) | 161 | static int perf_session__setup_sample_type(struct perf_session *self) |
162 | { | 162 | { |
163 | if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { | 163 | if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { |
164 | if (sort__has_parent) { | 164 | if (sort__has_parent) { |
165 | fprintf(stderr, "selected --sort parent, but no" | 165 | ui__warning("Selected --sort parent, but no " |
166 | " callchain data. Did you call" | 166 | "callchain data. Did you call " |
167 | " perf record without -g?\n"); | 167 | "'perf record' without -g?\n"); |
168 | return -EINVAL; | 168 | return -EINVAL; |
169 | } | 169 | } |
170 | if (symbol_conf.use_callchain) { | 170 | if (symbol_conf.use_callchain) { |
171 | fprintf(stderr, "selected -g but no callchain data." | 171 | ui__warning("Selected -g but no callchain data. Did " |
172 | " Did you call perf record without" | 172 | "you call 'perf record' without -g?\n"); |
173 | " -g?\n"); | ||
174 | return -1; | 173 | return -1; |
175 | } | 174 | } |
176 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && | 175 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
177 | !symbol_conf.use_callchain) { | 176 | !symbol_conf.use_callchain) { |
178 | symbol_conf.use_callchain = true; | 177 | symbol_conf.use_callchain = true; |
179 | if (callchain_register_param(&callchain_param) < 0) { | 178 | if (callchain_register_param(&callchain_param) < 0) { |
180 | fprintf(stderr, "Can't register callchain" | 179 | ui__warning("Can't register callchain " |
181 | " params\n"); | 180 | "params.\n"); |
182 | return -EINVAL; | 181 | return -EINVAL; |
183 | } | 182 | } |
184 | } | 183 | } |
185 | 184 | ||
186 | return 0; | 185 | return 0; |
187 | } | 186 | } |
188 | 187 | ||
189 | static struct perf_event_ops event_ops = { | 188 | static struct perf_event_ops event_ops = { |
190 | .sample = process_sample_event, | 189 | .sample = process_sample_event, |
191 | .mmap = perf_event__process_mmap, | 190 | .mmap = perf_event__process_mmap, |
192 | .comm = perf_event__process_comm, | 191 | .comm = perf_event__process_comm, |
193 | .exit = perf_event__process_task, | 192 | .exit = perf_event__process_task, |
194 | .fork = perf_event__process_task, | 193 | .fork = perf_event__process_task, |
195 | .lost = perf_event__process_lost, | 194 | .lost = perf_event__process_lost, |
196 | .read = process_read_event, | 195 | .read = process_read_event, |
197 | .attr = perf_event__process_attr, | 196 | .attr = perf_event__process_attr, |
198 | .event_type = perf_event__process_event_type, | 197 | .event_type = perf_event__process_event_type, |
199 | .tracing_data = perf_event__process_tracing_data, | 198 | .tracing_data = perf_event__process_tracing_data, |
200 | .build_id = perf_event__process_build_id, | 199 | .build_id = perf_event__process_build_id, |
201 | .ordered_samples = true, | 200 | .ordered_samples = true, |
202 | .ordering_requires_timestamps = true, | 201 | .ordering_requires_timestamps = true, |
203 | }; | 202 | }; |
204 | 203 | ||
205 | extern volatile int session_done; | 204 | extern volatile int session_done; |
206 | 205 | ||
207 | static void sig_handler(int sig __used) | 206 | static void sig_handler(int sig __used) |
208 | { | 207 | { |
209 | session_done = 1; | 208 | session_done = 1; |
210 | } | 209 | } |
211 | 210 | ||
212 | static size_t hists__fprintf_nr_sample_events(struct hists *self, | 211 | static size_t hists__fprintf_nr_sample_events(struct hists *self, |
213 | const char *evname, FILE *fp) | 212 | const char *evname, FILE *fp) |
214 | { | 213 | { |
215 | size_t ret; | 214 | size_t ret; |
216 | char unit; | 215 | char unit; |
217 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 216 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; |
218 | 217 | ||
219 | nr_events = convert_unit(nr_events, &unit); | 218 | nr_events = convert_unit(nr_events, &unit); |
220 | ret = fprintf(fp, "# Events: %lu%c", nr_events, unit); | 219 | ret = fprintf(fp, "# Events: %lu%c", nr_events, unit); |
221 | if (evname != NULL) | 220 | if (evname != NULL) |
222 | ret += fprintf(fp, " %s", evname); | 221 | ret += fprintf(fp, " %s", evname); |
223 | return ret + fprintf(fp, "\n#\n"); | 222 | return ret + fprintf(fp, "\n#\n"); |
224 | } | 223 | } |
225 | 224 | ||
226 | static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | 225 | static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, |
227 | const char *help) | 226 | const char *help) |
228 | { | 227 | { |
229 | struct perf_evsel *pos; | 228 | struct perf_evsel *pos; |
230 | 229 | ||
231 | list_for_each_entry(pos, &evlist->entries, node) { | 230 | list_for_each_entry(pos, &evlist->entries, node) { |
232 | struct hists *hists = &pos->hists; | 231 | struct hists *hists = &pos->hists; |
233 | const char *evname = NULL; | 232 | const char *evname = NULL; |
234 | 233 | ||
235 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) | 234 | if (rb_first(&hists->entries) != rb_last(&hists->entries)) |
236 | evname = event_name(pos); | 235 | evname = event_name(pos); |
237 | 236 | ||
238 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 237 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
239 | hists__fprintf(hists, NULL, false, stdout); | 238 | hists__fprintf(hists, NULL, false, stdout); |
240 | fprintf(stdout, "\n\n"); | 239 | fprintf(stdout, "\n\n"); |
241 | } | 240 | } |
242 | 241 | ||
243 | if (sort_order == default_sort_order && | 242 | if (sort_order == default_sort_order && |
244 | parent_pattern == default_parent_pattern) { | 243 | parent_pattern == default_parent_pattern) { |
245 | fprintf(stdout, "#\n# (%s)\n#\n", help); | 244 | fprintf(stdout, "#\n# (%s)\n#\n", help); |
246 | 245 | ||
247 | if (show_threads) { | 246 | if (show_threads) { |
248 | bool style = !strcmp(pretty_printing_style, "raw"); | 247 | bool style = !strcmp(pretty_printing_style, "raw"); |
249 | perf_read_values_display(stdout, &show_threads_values, | 248 | perf_read_values_display(stdout, &show_threads_values, |
250 | style); | 249 | style); |
251 | perf_read_values_destroy(&show_threads_values); | 250 | perf_read_values_destroy(&show_threads_values); |
252 | } | 251 | } |
253 | } | 252 | } |
254 | 253 | ||
255 | return 0; | 254 | return 0; |
256 | } | 255 | } |
257 | 256 | ||
258 | static int __cmd_report(void) | 257 | static int __cmd_report(void) |
259 | { | 258 | { |
260 | int ret = -EINVAL; | 259 | int ret = -EINVAL; |
261 | u64 nr_samples; | 260 | u64 nr_samples; |
262 | struct perf_session *session; | 261 | struct perf_session *session; |
263 | struct perf_evsel *pos; | 262 | struct perf_evsel *pos; |
264 | struct map *kernel_map; | 263 | struct map *kernel_map; |
265 | struct kmap *kernel_kmap; | 264 | struct kmap *kernel_kmap; |
266 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | 265 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; |
267 | 266 | ||
268 | signal(SIGINT, sig_handler); | 267 | signal(SIGINT, sig_handler); |
269 | 268 | ||
270 | session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); | 269 | session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); |
271 | if (session == NULL) | 270 | if (session == NULL) |
272 | return -ENOMEM; | 271 | return -ENOMEM; |
273 | 272 | ||
274 | if (cpu_list) { | 273 | if (cpu_list) { |
275 | ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); | 274 | ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); |
276 | if (ret) | 275 | if (ret) |
277 | goto out_delete; | 276 | goto out_delete; |
278 | } | 277 | } |
279 | 278 | ||
280 | if (show_threads) | 279 | if (show_threads) |
281 | perf_read_values_init(&show_threads_values); | 280 | perf_read_values_init(&show_threads_values); |
282 | 281 | ||
283 | ret = perf_session__setup_sample_type(session); | 282 | ret = perf_session__setup_sample_type(session); |
284 | if (ret) | 283 | if (ret) |
285 | goto out_delete; | 284 | goto out_delete; |
286 | 285 | ||
287 | ret = perf_session__process_events(session, &event_ops); | 286 | ret = perf_session__process_events(session, &event_ops); |
288 | if (ret) | 287 | if (ret) |
289 | goto out_delete; | 288 | goto out_delete; |
290 | 289 | ||
291 | kernel_map = session->host_machine.vmlinux_maps[MAP__FUNCTION]; | 290 | kernel_map = session->host_machine.vmlinux_maps[MAP__FUNCTION]; |
292 | kernel_kmap = map__kmap(kernel_map); | 291 | kernel_kmap = map__kmap(kernel_map); |
293 | if (kernel_map == NULL || | 292 | if (kernel_map == NULL || |
294 | (kernel_map->dso->hit && | 293 | (kernel_map->dso->hit && |
295 | (kernel_kmap->ref_reloc_sym == NULL || | 294 | (kernel_kmap->ref_reloc_sym == NULL || |
296 | kernel_kmap->ref_reloc_sym->addr == 0))) { | 295 | kernel_kmap->ref_reloc_sym->addr == 0))) { |
297 | const struct dso *kdso = kernel_map->dso; | 296 | const struct dso *kdso = kernel_map->dso; |
298 | 297 | ||
299 | ui__warning( | 298 | ui__warning( |
300 | "Kernel address maps (/proc/{kallsyms,modules}) were restricted.\n\n" | 299 | "Kernel address maps (/proc/{kallsyms,modules}) were restricted.\n\n" |
301 | "Check /proc/sys/kernel/kptr_restrict before running 'perf record'.\n\n%s\n\n" | 300 | "Check /proc/sys/kernel/kptr_restrict before running 'perf record'.\n\n%s\n\n" |
302 | "Samples in kernel modules can't be resolved as well.\n\n", | 301 | "Samples in kernel modules can't be resolved as well.\n\n", |
303 | RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION]) ? | 302 | RB_EMPTY_ROOT(&kdso->symbols[MAP__FUNCTION]) ? |
304 | "As no suitable kallsyms nor vmlinux was found, kernel samples\n" | 303 | "As no suitable kallsyms nor vmlinux was found, kernel samples\n" |
305 | "can't be resolved." : | 304 | "can't be resolved." : |
306 | "If some relocation was applied (e.g. kexec) symbols may be misresolved."); | 305 | "If some relocation was applied (e.g. kexec) symbols may be misresolved."); |
307 | } | 306 | } |
308 | 307 | ||
309 | if (dump_trace) { | 308 | if (dump_trace) { |
310 | perf_session__fprintf_nr_events(session, stdout); | 309 | perf_session__fprintf_nr_events(session, stdout); |
311 | goto out_delete; | 310 | goto out_delete; |
312 | } | 311 | } |
313 | 312 | ||
314 | if (verbose > 3) | 313 | if (verbose > 3) |
315 | perf_session__fprintf(session, stdout); | 314 | perf_session__fprintf(session, stdout); |
316 | 315 | ||
317 | if (verbose > 2) | 316 | if (verbose > 2) |
318 | perf_session__fprintf_dsos(session, stdout); | 317 | perf_session__fprintf_dsos(session, stdout); |
319 | 318 | ||
320 | nr_samples = 0; | 319 | nr_samples = 0; |
321 | list_for_each_entry(pos, &session->evlist->entries, node) { | 320 | list_for_each_entry(pos, &session->evlist->entries, node) { |
322 | struct hists *hists = &pos->hists; | 321 | struct hists *hists = &pos->hists; |
323 | 322 | ||
324 | hists__collapse_resort(hists); | 323 | hists__collapse_resort(hists); |
325 | hists__output_resort(hists); | 324 | hists__output_resort(hists); |
326 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 325 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
327 | } | 326 | } |
328 | 327 | ||
329 | if (nr_samples == 0) { | 328 | if (nr_samples == 0) { |
330 | ui__warning("The %s file has no samples!\n", input_name); | 329 | ui__warning("The %s file has no samples!\n", input_name); |
331 | goto out_delete; | 330 | goto out_delete; |
332 | } | 331 | } |
333 | 332 | ||
334 | if (use_browser > 0) | 333 | if (use_browser > 0) |
335 | perf_evlist__tui_browse_hists(session->evlist, help); | 334 | perf_evlist__tui_browse_hists(session->evlist, help); |
336 | else | 335 | else |
337 | perf_evlist__tty_browse_hists(session->evlist, help); | 336 | perf_evlist__tty_browse_hists(session->evlist, help); |
338 | 337 | ||
339 | out_delete: | 338 | out_delete: |
340 | /* | 339 | /* |
341 | * Speed up the exit process, for large files this can | 340 | * Speed up the exit process, for large files this can |
342 | * take quite a while. | 341 | * take quite a while. |
343 | * | 342 | * |
344 | * XXX Enable this when using valgrind or if we ever | 343 | * XXX Enable this when using valgrind or if we ever |
345 | * librarize this command. | 344 | * librarize this command. |
346 | * | 345 | * |
347 | * Also experiment with obstacks to see how much speed | 346 | * Also experiment with obstacks to see how much speed |
348 | * up we'll get here. | 347 | * up we'll get here. |
349 | * | 348 | * |
350 | * perf_session__delete(session); | 349 | * perf_session__delete(session); |
351 | */ | 350 | */ |
352 | return ret; | 351 | return ret; |
353 | } | 352 | } |
354 | 353 | ||
355 | static int | 354 | static int |
356 | parse_callchain_opt(const struct option *opt __used, const char *arg, | 355 | parse_callchain_opt(const struct option *opt __used, const char *arg, |
357 | int unset) | 356 | int unset) |
358 | { | 357 | { |
359 | char *tok, *tok2; | 358 | char *tok, *tok2; |
360 | char *endptr; | 359 | char *endptr; |
361 | 360 | ||
362 | /* | 361 | /* |
363 | * --no-call-graph | 362 | * --no-call-graph |
364 | */ | 363 | */ |
365 | if (unset) { | 364 | if (unset) { |
366 | dont_use_callchains = true; | 365 | dont_use_callchains = true; |
367 | return 0; | 366 | return 0; |
368 | } | 367 | } |
369 | 368 | ||
370 | symbol_conf.use_callchain = true; | 369 | symbol_conf.use_callchain = true; |
371 | 370 | ||
372 | if (!arg) | 371 | if (!arg) |
373 | return 0; | 372 | return 0; |
374 | 373 | ||
375 | tok = strtok((char *)arg, ","); | 374 | tok = strtok((char *)arg, ","); |
376 | if (!tok) | 375 | if (!tok) |
377 | return -1; | 376 | return -1; |
378 | 377 | ||
379 | /* get the output mode */ | 378 | /* get the output mode */ |
380 | if (!strncmp(tok, "graph", strlen(arg))) | 379 | if (!strncmp(tok, "graph", strlen(arg))) |
381 | callchain_param.mode = CHAIN_GRAPH_ABS; | 380 | callchain_param.mode = CHAIN_GRAPH_ABS; |
382 | 381 | ||
383 | else if (!strncmp(tok, "flat", strlen(arg))) | 382 | else if (!strncmp(tok, "flat", strlen(arg))) |
384 | callchain_param.mode = CHAIN_FLAT; | 383 | callchain_param.mode = CHAIN_FLAT; |
385 | 384 | ||
386 | else if (!strncmp(tok, "fractal", strlen(arg))) | 385 | else if (!strncmp(tok, "fractal", strlen(arg))) |
387 | callchain_param.mode = CHAIN_GRAPH_REL; | 386 | callchain_param.mode = CHAIN_GRAPH_REL; |
388 | 387 | ||
389 | else if (!strncmp(tok, "none", strlen(arg))) { | 388 | else if (!strncmp(tok, "none", strlen(arg))) { |
390 | callchain_param.mode = CHAIN_NONE; | 389 | callchain_param.mode = CHAIN_NONE; |
391 | symbol_conf.use_callchain = false; | 390 | symbol_conf.use_callchain = false; |
392 | 391 | ||
393 | return 0; | 392 | return 0; |
394 | } | 393 | } |
395 | 394 | ||
396 | else | 395 | else |
397 | return -1; | 396 | return -1; |
398 | 397 | ||
399 | /* get the min percentage */ | 398 | /* get the min percentage */ |
400 | tok = strtok(NULL, ","); | 399 | tok = strtok(NULL, ","); |
401 | if (!tok) | 400 | if (!tok) |
402 | goto setup; | 401 | goto setup; |
403 | 402 | ||
404 | callchain_param.min_percent = strtod(tok, &endptr); | 403 | callchain_param.min_percent = strtod(tok, &endptr); |
405 | if (tok == endptr) | 404 | if (tok == endptr) |
406 | return -1; | 405 | return -1; |
407 | 406 | ||
408 | /* get the print limit */ | 407 | /* get the print limit */ |
409 | tok2 = strtok(NULL, ","); | 408 | tok2 = strtok(NULL, ","); |
410 | if (!tok2) | 409 | if (!tok2) |
411 | goto setup; | 410 | goto setup; |
412 | 411 | ||
413 | if (tok2[0] != 'c') { | 412 | if (tok2[0] != 'c') { |
414 | callchain_param.print_limit = strtod(tok2, &endptr); | 413 | callchain_param.print_limit = strtod(tok2, &endptr); |
415 | tok2 = strtok(NULL, ","); | 414 | tok2 = strtok(NULL, ","); |
416 | if (!tok2) | 415 | if (!tok2) |
417 | goto setup; | 416 | goto setup; |
418 | } | 417 | } |
419 | 418 | ||
420 | /* get the call chain order */ | 419 | /* get the call chain order */ |
421 | if (!strcmp(tok2, "caller")) | 420 | if (!strcmp(tok2, "caller")) |
422 | callchain_param.order = ORDER_CALLER; | 421 | callchain_param.order = ORDER_CALLER; |
423 | else if (!strcmp(tok2, "callee")) | 422 | else if (!strcmp(tok2, "callee")) |
424 | callchain_param.order = ORDER_CALLEE; | 423 | callchain_param.order = ORDER_CALLEE; |
425 | else | 424 | else |
426 | return -1; | 425 | return -1; |
427 | setup: | 426 | setup: |
428 | if (callchain_register_param(&callchain_param) < 0) { | 427 | if (callchain_register_param(&callchain_param) < 0) { |
429 | fprintf(stderr, "Can't register callchain params\n"); | 428 | fprintf(stderr, "Can't register callchain params\n"); |
430 | return -1; | 429 | return -1; |
431 | } | 430 | } |
432 | return 0; | 431 | return 0; |
433 | } | 432 | } |
434 | 433 | ||
435 | static const char * const report_usage[] = { | 434 | static const char * const report_usage[] = { |
436 | "perf report [<options>] <command>", | 435 | "perf report [<options>] <command>", |
437 | NULL | 436 | NULL |
438 | }; | 437 | }; |
439 | 438 | ||
440 | static const struct option options[] = { | 439 | static const struct option options[] = { |
441 | OPT_STRING('i', "input", &input_name, "file", | 440 | OPT_STRING('i', "input", &input_name, "file", |
442 | "input file name"), | 441 | "input file name"), |
443 | OPT_INCR('v', "verbose", &verbose, | 442 | OPT_INCR('v', "verbose", &verbose, |
444 | "be more verbose (show symbol address, etc)"), | 443 | "be more verbose (show symbol address, etc)"), |
445 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 444 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
446 | "dump raw trace in ASCII"), | 445 | "dump raw trace in ASCII"), |
447 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 446 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
448 | "file", "vmlinux pathname"), | 447 | "file", "vmlinux pathname"), |
449 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, | 448 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, |
450 | "file", "kallsyms pathname"), | 449 | "file", "kallsyms pathname"), |
451 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 450 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
452 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | 451 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, |
453 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 452 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
454 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | 453 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
455 | "Show a column with the number of samples"), | 454 | "Show a column with the number of samples"), |
456 | OPT_BOOLEAN('T', "threads", &show_threads, | 455 | OPT_BOOLEAN('T', "threads", &show_threads, |
457 | "Show per-thread event counters"), | 456 | "Show per-thread event counters"), |
458 | OPT_STRING(0, "pretty", &pretty_printing_style, "key", | 457 | OPT_STRING(0, "pretty", &pretty_printing_style, "key", |
459 | "pretty printing style key: normal raw"), | 458 | "pretty printing style key: normal raw"), |
460 | OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"), | 459 | OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"), |
461 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), | 460 | OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"), |
462 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 461 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
463 | "sort by key(s): pid, comm, dso, symbol, parent"), | 462 | "sort by key(s): pid, comm, dso, symbol, parent"), |
464 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 463 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
465 | "Show sample percentage for different cpu modes"), | 464 | "Show sample percentage for different cpu modes"), |
466 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 465 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
467 | "regex filter to identify parent, see: '--sort parent'"), | 466 | "regex filter to identify parent, see: '--sort parent'"), |
468 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, | 467 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
469 | "Only display entries with parent-match"), | 468 | "Only display entries with parent-match"), |
470 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent, call_order", | 469 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent, call_order", |
471 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold and callchain order. " | 470 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold and callchain order. " |
472 | "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt), | 471 | "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt), |
473 | OPT_BOOLEAN('G', "inverted", &inverted_callchain, "alias for inverted call graph"), | 472 | OPT_BOOLEAN('G', "inverted", &inverted_callchain, "alias for inverted call graph"), |
474 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 473 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
475 | "only consider symbols in these dsos"), | 474 | "only consider symbols in these dsos"), |
476 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 475 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
477 | "only consider symbols in these comms"), | 476 | "only consider symbols in these comms"), |
478 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 477 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
479 | "only consider these symbols"), | 478 | "only consider these symbols"), |
480 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, | 479 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, |
481 | "width[,width...]", | 480 | "width[,width...]", |
482 | "don't try to adjust column width, use these fixed values"), | 481 | "don't try to adjust column width, use these fixed values"), |
483 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", | 482 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", |
484 | "separator for columns, no spaces will be added between " | 483 | "separator for columns, no spaces will be added between " |
485 | "columns '.' is reserved."), | 484 | "columns '.' is reserved."), |
486 | OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved, | 485 | OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved, |
487 | "Only display entries resolved to a symbol"), | 486 | "Only display entries resolved to a symbol"), |
488 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 487 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
489 | "Look for files with symbols relative to this directory"), | 488 | "Look for files with symbols relative to this directory"), |
490 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), | 489 | OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"), |
491 | OPT_END() | 490 | OPT_END() |
492 | }; | 491 | }; |
493 | 492 | ||
494 | int cmd_report(int argc, const char **argv, const char *prefix __used) | 493 | int cmd_report(int argc, const char **argv, const char *prefix __used) |
495 | { | 494 | { |
496 | argc = parse_options(argc, argv, options, report_usage, 0); | 495 | argc = parse_options(argc, argv, options, report_usage, 0); |
497 | 496 | ||
498 | if (use_stdio) | 497 | if (use_stdio) |
499 | use_browser = 0; | 498 | use_browser = 0; |
500 | else if (use_tui) | 499 | else if (use_tui) |
501 | use_browser = 1; | 500 | use_browser = 1; |
502 | 501 | ||
503 | if (inverted_callchain) | 502 | if (inverted_callchain) |
504 | callchain_param.order = ORDER_CALLER; | 503 | callchain_param.order = ORDER_CALLER; |
505 | 504 | ||
506 | if (strcmp(input_name, "-") != 0) | 505 | if (strcmp(input_name, "-") != 0) |
507 | setup_browser(true); | 506 | setup_browser(true); |
508 | else | 507 | else |
509 | use_browser = 0; | 508 | use_browser = 0; |
510 | /* | 509 | /* |
511 | * Only in the newt browser we are doing integrated annotation, | 510 | * Only in the newt browser we are doing integrated annotation, |
512 | * so don't allocate extra space that won't be used in the stdio | 511 | * so don't allocate extra space that won't be used in the stdio |
513 | * implementation. | 512 | * implementation. |
514 | */ | 513 | */ |
515 | if (use_browser > 0) { | 514 | if (use_browser > 0) { |
516 | symbol_conf.priv_size = sizeof(struct annotation); | 515 | symbol_conf.priv_size = sizeof(struct annotation); |
517 | annotate_init = symbol__annotate_init; | 516 | annotate_init = symbol__annotate_init; |
518 | /* | 517 | /* |
519 | * For searching by name on the "Browse map details". | 518 | * For searching by name on the "Browse map details". |
520 | * providing it only in verbose mode not to bloat too | 519 | * providing it only in verbose mode not to bloat too |
521 | * much struct symbol. | 520 | * much struct symbol. |
522 | */ | 521 | */ |
523 | if (verbose) { | 522 | if (verbose) { |
524 | /* | 523 | /* |
525 | * XXX: Need to provide a less kludgy way to ask for | 524 | * XXX: Need to provide a less kludgy way to ask for |
526 | * more space per symbol, the u32 is for the index on | 525 | * more space per symbol, the u32 is for the index on |
527 | * the ui browser. | 526 | * the ui browser. |
528 | * See symbol__browser_index. | 527 | * See symbol__browser_index. |
529 | */ | 528 | */ |
530 | symbol_conf.priv_size += sizeof(u32); | 529 | symbol_conf.priv_size += sizeof(u32); |
531 | symbol_conf.sort_by_name = true; | 530 | symbol_conf.sort_by_name = true; |
532 | } | 531 | } |
533 | } | 532 | } |
534 | 533 | ||
535 | if (symbol__init() < 0) | 534 | if (symbol__init() < 0) |
536 | return -1; | 535 | return -1; |
537 | 536 | ||
538 | setup_sorting(report_usage, options); | 537 | setup_sorting(report_usage, options); |
539 | 538 | ||
540 | if (parent_pattern != default_parent_pattern) { | 539 | if (parent_pattern != default_parent_pattern) { |
541 | if (sort_dimension__add("parent") < 0) | 540 | if (sort_dimension__add("parent") < 0) |
542 | return -1; | 541 | return -1; |
543 | 542 | ||
544 | /* | 543 | /* |
545 | * Only show the parent fields if we explicitly | 544 | * Only show the parent fields if we explicitly |
546 | * sort that way. If we only use parent machinery | 545 | * sort that way. If we only use parent machinery |
547 | * for filtering, we don't want it. | 546 | * for filtering, we don't want it. |
548 | */ | 547 | */ |
549 | if (!strstr(sort_order, "parent")) | 548 | if (!strstr(sort_order, "parent")) |
550 | sort_parent.elide = 1; | 549 | sort_parent.elide = 1; |
551 | } else | 550 | } else |
552 | symbol_conf.exclude_other = false; | 551 | symbol_conf.exclude_other = false; |
553 | 552 | ||
554 | /* | 553 | /* |
555 | * Any (unrecognized) arguments left? | 554 | * Any (unrecognized) arguments left? |
556 | */ | 555 | */ |
557 | if (argc) | 556 | if (argc) |
558 | usage_with_options(report_usage, options); | 557 | usage_with_options(report_usage, options); |
559 | 558 | ||
560 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | 559 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); |
561 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | 560 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); |
562 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | 561 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); |
563 | 562 | ||
564 | return __cmd_report(); | 563 | return __cmd_report(); |
565 | } | 564 | } |
566 | 565 |
tools/perf/builtin-sched.c
1 | #include "builtin.h" | 1 | #include "builtin.h" |
2 | #include "perf.h" | 2 | #include "perf.h" |
3 | 3 | ||
4 | #include "util/util.h" | 4 | #include "util/util.h" |
5 | #include "util/cache.h" | 5 | #include "util/cache.h" |
6 | #include "util/symbol.h" | 6 | #include "util/symbol.h" |
7 | #include "util/thread.h" | 7 | #include "util/thread.h" |
8 | #include "util/header.h" | 8 | #include "util/header.h" |
9 | #include "util/session.h" | 9 | #include "util/session.h" |
10 | 10 | ||
11 | #include "util/parse-options.h" | 11 | #include "util/parse-options.h" |
12 | #include "util/trace-event.h" | 12 | #include "util/trace-event.h" |
13 | 13 | ||
14 | #include "util/debug.h" | 14 | #include "util/debug.h" |
15 | 15 | ||
16 | #include <sys/prctl.h> | 16 | #include <sys/prctl.h> |
17 | 17 | ||
18 | #include <semaphore.h> | 18 | #include <semaphore.h> |
19 | #include <pthread.h> | 19 | #include <pthread.h> |
20 | #include <math.h> | 20 | #include <math.h> |
21 | 21 | ||
22 | static char const *input_name = "perf.data"; | 22 | static char const *input_name = "perf.data"; |
23 | 23 | ||
24 | static char default_sort_order[] = "avg, max, switch, runtime"; | 24 | static char default_sort_order[] = "avg, max, switch, runtime"; |
25 | static const char *sort_order = default_sort_order; | 25 | static const char *sort_order = default_sort_order; |
26 | 26 | ||
27 | static int profile_cpu = -1; | 27 | static int profile_cpu = -1; |
28 | 28 | ||
29 | #define PR_SET_NAME 15 /* Set process name */ | 29 | #define PR_SET_NAME 15 /* Set process name */ |
30 | #define MAX_CPUS 4096 | 30 | #define MAX_CPUS 4096 |
31 | 31 | ||
32 | static u64 run_measurement_overhead; | 32 | static u64 run_measurement_overhead; |
33 | static u64 sleep_measurement_overhead; | 33 | static u64 sleep_measurement_overhead; |
34 | 34 | ||
35 | #define COMM_LEN 20 | 35 | #define COMM_LEN 20 |
36 | #define SYM_LEN 129 | 36 | #define SYM_LEN 129 |
37 | 37 | ||
38 | #define MAX_PID 65536 | 38 | #define MAX_PID 65536 |
39 | 39 | ||
40 | static unsigned long nr_tasks; | 40 | static unsigned long nr_tasks; |
41 | 41 | ||
42 | struct sched_atom; | 42 | struct sched_atom; |
43 | 43 | ||
44 | struct task_desc { | 44 | struct task_desc { |
45 | unsigned long nr; | 45 | unsigned long nr; |
46 | unsigned long pid; | 46 | unsigned long pid; |
47 | char comm[COMM_LEN]; | 47 | char comm[COMM_LEN]; |
48 | 48 | ||
49 | unsigned long nr_events; | 49 | unsigned long nr_events; |
50 | unsigned long curr_event; | 50 | unsigned long curr_event; |
51 | struct sched_atom **atoms; | 51 | struct sched_atom **atoms; |
52 | 52 | ||
53 | pthread_t thread; | 53 | pthread_t thread; |
54 | sem_t sleep_sem; | 54 | sem_t sleep_sem; |
55 | 55 | ||
56 | sem_t ready_for_work; | 56 | sem_t ready_for_work; |
57 | sem_t work_done_sem; | 57 | sem_t work_done_sem; |
58 | 58 | ||
59 | u64 cpu_usage; | 59 | u64 cpu_usage; |
60 | }; | 60 | }; |
61 | 61 | ||
62 | enum sched_event_type { | 62 | enum sched_event_type { |
63 | SCHED_EVENT_RUN, | 63 | SCHED_EVENT_RUN, |
64 | SCHED_EVENT_SLEEP, | 64 | SCHED_EVENT_SLEEP, |
65 | SCHED_EVENT_WAKEUP, | 65 | SCHED_EVENT_WAKEUP, |
66 | SCHED_EVENT_MIGRATION, | 66 | SCHED_EVENT_MIGRATION, |
67 | }; | 67 | }; |
68 | 68 | ||
69 | struct sched_atom { | 69 | struct sched_atom { |
70 | enum sched_event_type type; | 70 | enum sched_event_type type; |
71 | int specific_wait; | 71 | int specific_wait; |
72 | u64 timestamp; | 72 | u64 timestamp; |
73 | u64 duration; | 73 | u64 duration; |
74 | unsigned long nr; | 74 | unsigned long nr; |
75 | sem_t *wait_sem; | 75 | sem_t *wait_sem; |
76 | struct task_desc *wakee; | 76 | struct task_desc *wakee; |
77 | }; | 77 | }; |
78 | 78 | ||
79 | static struct task_desc *pid_to_task[MAX_PID]; | 79 | static struct task_desc *pid_to_task[MAX_PID]; |
80 | 80 | ||
81 | static struct task_desc **tasks; | 81 | static struct task_desc **tasks; |
82 | 82 | ||
83 | static pthread_mutex_t start_work_mutex = PTHREAD_MUTEX_INITIALIZER; | 83 | static pthread_mutex_t start_work_mutex = PTHREAD_MUTEX_INITIALIZER; |
84 | static u64 start_time; | 84 | static u64 start_time; |
85 | 85 | ||
86 | static pthread_mutex_t work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER; | 86 | static pthread_mutex_t work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER; |
87 | 87 | ||
88 | static unsigned long nr_run_events; | 88 | static unsigned long nr_run_events; |
89 | static unsigned long nr_sleep_events; | 89 | static unsigned long nr_sleep_events; |
90 | static unsigned long nr_wakeup_events; | 90 | static unsigned long nr_wakeup_events; |
91 | 91 | ||
92 | static unsigned long nr_sleep_corrections; | 92 | static unsigned long nr_sleep_corrections; |
93 | static unsigned long nr_run_events_optimized; | 93 | static unsigned long nr_run_events_optimized; |
94 | 94 | ||
95 | static unsigned long targetless_wakeups; | 95 | static unsigned long targetless_wakeups; |
96 | static unsigned long multitarget_wakeups; | 96 | static unsigned long multitarget_wakeups; |
97 | 97 | ||
98 | static u64 cpu_usage; | 98 | static u64 cpu_usage; |
99 | static u64 runavg_cpu_usage; | 99 | static u64 runavg_cpu_usage; |
100 | static u64 parent_cpu_usage; | 100 | static u64 parent_cpu_usage; |
101 | static u64 runavg_parent_cpu_usage; | 101 | static u64 runavg_parent_cpu_usage; |
102 | 102 | ||
103 | static unsigned long nr_runs; | 103 | static unsigned long nr_runs; |
104 | static u64 sum_runtime; | 104 | static u64 sum_runtime; |
105 | static u64 sum_fluct; | 105 | static u64 sum_fluct; |
106 | static u64 run_avg; | 106 | static u64 run_avg; |
107 | 107 | ||
108 | static unsigned int replay_repeat = 10; | 108 | static unsigned int replay_repeat = 10; |
109 | static unsigned long nr_timestamps; | 109 | static unsigned long nr_timestamps; |
110 | static unsigned long nr_unordered_timestamps; | 110 | static unsigned long nr_unordered_timestamps; |
111 | static unsigned long nr_state_machine_bugs; | 111 | static unsigned long nr_state_machine_bugs; |
112 | static unsigned long nr_context_switch_bugs; | 112 | static unsigned long nr_context_switch_bugs; |
113 | static unsigned long nr_events; | 113 | static unsigned long nr_events; |
114 | static unsigned long nr_lost_chunks; | 114 | static unsigned long nr_lost_chunks; |
115 | static unsigned long nr_lost_events; | 115 | static unsigned long nr_lost_events; |
116 | 116 | ||
117 | #define TASK_STATE_TO_CHAR_STR "RSDTtZX" | 117 | #define TASK_STATE_TO_CHAR_STR "RSDTtZX" |
118 | 118 | ||
119 | enum thread_state { | 119 | enum thread_state { |
120 | THREAD_SLEEPING = 0, | 120 | THREAD_SLEEPING = 0, |
121 | THREAD_WAIT_CPU, | 121 | THREAD_WAIT_CPU, |
122 | THREAD_SCHED_IN, | 122 | THREAD_SCHED_IN, |
123 | THREAD_IGNORE | 123 | THREAD_IGNORE |
124 | }; | 124 | }; |
125 | 125 | ||
126 | struct work_atom { | 126 | struct work_atom { |
127 | struct list_head list; | 127 | struct list_head list; |
128 | enum thread_state state; | 128 | enum thread_state state; |
129 | u64 sched_out_time; | 129 | u64 sched_out_time; |
130 | u64 wake_up_time; | 130 | u64 wake_up_time; |
131 | u64 sched_in_time; | 131 | u64 sched_in_time; |
132 | u64 runtime; | 132 | u64 runtime; |
133 | }; | 133 | }; |
134 | 134 | ||
135 | struct work_atoms { | 135 | struct work_atoms { |
136 | struct list_head work_list; | 136 | struct list_head work_list; |
137 | struct thread *thread; | 137 | struct thread *thread; |
138 | struct rb_node node; | 138 | struct rb_node node; |
139 | u64 max_lat; | 139 | u64 max_lat; |
140 | u64 max_lat_at; | 140 | u64 max_lat_at; |
141 | u64 total_lat; | 141 | u64 total_lat; |
142 | u64 nb_atoms; | 142 | u64 nb_atoms; |
143 | u64 total_runtime; | 143 | u64 total_runtime; |
144 | }; | 144 | }; |
145 | 145 | ||
146 | typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *); | 146 | typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *); |
147 | 147 | ||
148 | static struct rb_root atom_root, sorted_atom_root; | 148 | static struct rb_root atom_root, sorted_atom_root; |
149 | 149 | ||
150 | static u64 all_runtime; | 150 | static u64 all_runtime; |
151 | static u64 all_count; | 151 | static u64 all_count; |
152 | 152 | ||
153 | 153 | ||
154 | static u64 get_nsecs(void) | 154 | static u64 get_nsecs(void) |
155 | { | 155 | { |
156 | struct timespec ts; | 156 | struct timespec ts; |
157 | 157 | ||
158 | clock_gettime(CLOCK_MONOTONIC, &ts); | 158 | clock_gettime(CLOCK_MONOTONIC, &ts); |
159 | 159 | ||
160 | return ts.tv_sec * 1000000000ULL + ts.tv_nsec; | 160 | return ts.tv_sec * 1000000000ULL + ts.tv_nsec; |
161 | } | 161 | } |
162 | 162 | ||
163 | static void burn_nsecs(u64 nsecs) | 163 | static void burn_nsecs(u64 nsecs) |
164 | { | 164 | { |
165 | u64 T0 = get_nsecs(), T1; | 165 | u64 T0 = get_nsecs(), T1; |
166 | 166 | ||
167 | do { | 167 | do { |
168 | T1 = get_nsecs(); | 168 | T1 = get_nsecs(); |
169 | } while (T1 + run_measurement_overhead < T0 + nsecs); | 169 | } while (T1 + run_measurement_overhead < T0 + nsecs); |
170 | } | 170 | } |
171 | 171 | ||
172 | static void sleep_nsecs(u64 nsecs) | 172 | static void sleep_nsecs(u64 nsecs) |
173 | { | 173 | { |
174 | struct timespec ts; | 174 | struct timespec ts; |
175 | 175 | ||
176 | ts.tv_nsec = nsecs % 999999999; | 176 | ts.tv_nsec = nsecs % 999999999; |
177 | ts.tv_sec = nsecs / 999999999; | 177 | ts.tv_sec = nsecs / 999999999; |
178 | 178 | ||
179 | nanosleep(&ts, NULL); | 179 | nanosleep(&ts, NULL); |
180 | } | 180 | } |
181 | 181 | ||
182 | static void calibrate_run_measurement_overhead(void) | 182 | static void calibrate_run_measurement_overhead(void) |
183 | { | 183 | { |
184 | u64 T0, T1, delta, min_delta = 1000000000ULL; | 184 | u64 T0, T1, delta, min_delta = 1000000000ULL; |
185 | int i; | 185 | int i; |
186 | 186 | ||
187 | for (i = 0; i < 10; i++) { | 187 | for (i = 0; i < 10; i++) { |
188 | T0 = get_nsecs(); | 188 | T0 = get_nsecs(); |
189 | burn_nsecs(0); | 189 | burn_nsecs(0); |
190 | T1 = get_nsecs(); | 190 | T1 = get_nsecs(); |
191 | delta = T1-T0; | 191 | delta = T1-T0; |
192 | min_delta = min(min_delta, delta); | 192 | min_delta = min(min_delta, delta); |
193 | } | 193 | } |
194 | run_measurement_overhead = min_delta; | 194 | run_measurement_overhead = min_delta; |
195 | 195 | ||
196 | printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta); | 196 | printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta); |
197 | } | 197 | } |
198 | 198 | ||
199 | static void calibrate_sleep_measurement_overhead(void) | 199 | static void calibrate_sleep_measurement_overhead(void) |
200 | { | 200 | { |
201 | u64 T0, T1, delta, min_delta = 1000000000ULL; | 201 | u64 T0, T1, delta, min_delta = 1000000000ULL; |
202 | int i; | 202 | int i; |
203 | 203 | ||
204 | for (i = 0; i < 10; i++) { | 204 | for (i = 0; i < 10; i++) { |
205 | T0 = get_nsecs(); | 205 | T0 = get_nsecs(); |
206 | sleep_nsecs(10000); | 206 | sleep_nsecs(10000); |
207 | T1 = get_nsecs(); | 207 | T1 = get_nsecs(); |
208 | delta = T1-T0; | 208 | delta = T1-T0; |
209 | min_delta = min(min_delta, delta); | 209 | min_delta = min(min_delta, delta); |
210 | } | 210 | } |
211 | min_delta -= 10000; | 211 | min_delta -= 10000; |
212 | sleep_measurement_overhead = min_delta; | 212 | sleep_measurement_overhead = min_delta; |
213 | 213 | ||
214 | printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta); | 214 | printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta); |
215 | } | 215 | } |
216 | 216 | ||
217 | static struct sched_atom * | 217 | static struct sched_atom * |
218 | get_new_event(struct task_desc *task, u64 timestamp) | 218 | get_new_event(struct task_desc *task, u64 timestamp) |
219 | { | 219 | { |
220 | struct sched_atom *event = zalloc(sizeof(*event)); | 220 | struct sched_atom *event = zalloc(sizeof(*event)); |
221 | unsigned long idx = task->nr_events; | 221 | unsigned long idx = task->nr_events; |
222 | size_t size; | 222 | size_t size; |
223 | 223 | ||
224 | event->timestamp = timestamp; | 224 | event->timestamp = timestamp; |
225 | event->nr = idx; | 225 | event->nr = idx; |
226 | 226 | ||
227 | task->nr_events++; | 227 | task->nr_events++; |
228 | size = sizeof(struct sched_atom *) * task->nr_events; | 228 | size = sizeof(struct sched_atom *) * task->nr_events; |
229 | task->atoms = realloc(task->atoms, size); | 229 | task->atoms = realloc(task->atoms, size); |
230 | BUG_ON(!task->atoms); | 230 | BUG_ON(!task->atoms); |
231 | 231 | ||
232 | task->atoms[idx] = event; | 232 | task->atoms[idx] = event; |
233 | 233 | ||
234 | return event; | 234 | return event; |
235 | } | 235 | } |
236 | 236 | ||
237 | static struct sched_atom *last_event(struct task_desc *task) | 237 | static struct sched_atom *last_event(struct task_desc *task) |
238 | { | 238 | { |
239 | if (!task->nr_events) | 239 | if (!task->nr_events) |
240 | return NULL; | 240 | return NULL; |
241 | 241 | ||
242 | return task->atoms[task->nr_events - 1]; | 242 | return task->atoms[task->nr_events - 1]; |
243 | } | 243 | } |
244 | 244 | ||
245 | static void | 245 | static void |
246 | add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration) | 246 | add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration) |
247 | { | 247 | { |
248 | struct sched_atom *event, *curr_event = last_event(task); | 248 | struct sched_atom *event, *curr_event = last_event(task); |
249 | 249 | ||
250 | /* | 250 | /* |
251 | * optimize an existing RUN event by merging this one | 251 | * optimize an existing RUN event by merging this one |
252 | * to it: | 252 | * to it: |
253 | */ | 253 | */ |
254 | if (curr_event && curr_event->type == SCHED_EVENT_RUN) { | 254 | if (curr_event && curr_event->type == SCHED_EVENT_RUN) { |
255 | nr_run_events_optimized++; | 255 | nr_run_events_optimized++; |
256 | curr_event->duration += duration; | 256 | curr_event->duration += duration; |
257 | return; | 257 | return; |
258 | } | 258 | } |
259 | 259 | ||
260 | event = get_new_event(task, timestamp); | 260 | event = get_new_event(task, timestamp); |
261 | 261 | ||
262 | event->type = SCHED_EVENT_RUN; | 262 | event->type = SCHED_EVENT_RUN; |
263 | event->duration = duration; | 263 | event->duration = duration; |
264 | 264 | ||
265 | nr_run_events++; | 265 | nr_run_events++; |
266 | } | 266 | } |
267 | 267 | ||
268 | static void | 268 | static void |
269 | add_sched_event_wakeup(struct task_desc *task, u64 timestamp, | 269 | add_sched_event_wakeup(struct task_desc *task, u64 timestamp, |
270 | struct task_desc *wakee) | 270 | struct task_desc *wakee) |
271 | { | 271 | { |
272 | struct sched_atom *event, *wakee_event; | 272 | struct sched_atom *event, *wakee_event; |
273 | 273 | ||
274 | event = get_new_event(task, timestamp); | 274 | event = get_new_event(task, timestamp); |
275 | event->type = SCHED_EVENT_WAKEUP; | 275 | event->type = SCHED_EVENT_WAKEUP; |
276 | event->wakee = wakee; | 276 | event->wakee = wakee; |
277 | 277 | ||
278 | wakee_event = last_event(wakee); | 278 | wakee_event = last_event(wakee); |
279 | if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) { | 279 | if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) { |
280 | targetless_wakeups++; | 280 | targetless_wakeups++; |
281 | return; | 281 | return; |
282 | } | 282 | } |
283 | if (wakee_event->wait_sem) { | 283 | if (wakee_event->wait_sem) { |
284 | multitarget_wakeups++; | 284 | multitarget_wakeups++; |
285 | return; | 285 | return; |
286 | } | 286 | } |
287 | 287 | ||
288 | wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); | 288 | wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); |
289 | sem_init(wakee_event->wait_sem, 0, 0); | 289 | sem_init(wakee_event->wait_sem, 0, 0); |
290 | wakee_event->specific_wait = 1; | 290 | wakee_event->specific_wait = 1; |
291 | event->wait_sem = wakee_event->wait_sem; | 291 | event->wait_sem = wakee_event->wait_sem; |
292 | 292 | ||
293 | nr_wakeup_events++; | 293 | nr_wakeup_events++; |
294 | } | 294 | } |
295 | 295 | ||
296 | static void | 296 | static void |
297 | add_sched_event_sleep(struct task_desc *task, u64 timestamp, | 297 | add_sched_event_sleep(struct task_desc *task, u64 timestamp, |
298 | u64 task_state __used) | 298 | u64 task_state __used) |
299 | { | 299 | { |
300 | struct sched_atom *event = get_new_event(task, timestamp); | 300 | struct sched_atom *event = get_new_event(task, timestamp); |
301 | 301 | ||
302 | event->type = SCHED_EVENT_SLEEP; | 302 | event->type = SCHED_EVENT_SLEEP; |
303 | 303 | ||
304 | nr_sleep_events++; | 304 | nr_sleep_events++; |
305 | } | 305 | } |
306 | 306 | ||
307 | static struct task_desc *register_pid(unsigned long pid, const char *comm) | 307 | static struct task_desc *register_pid(unsigned long pid, const char *comm) |
308 | { | 308 | { |
309 | struct task_desc *task; | 309 | struct task_desc *task; |
310 | 310 | ||
311 | BUG_ON(pid >= MAX_PID); | 311 | BUG_ON(pid >= MAX_PID); |
312 | 312 | ||
313 | task = pid_to_task[pid]; | 313 | task = pid_to_task[pid]; |
314 | 314 | ||
315 | if (task) | 315 | if (task) |
316 | return task; | 316 | return task; |
317 | 317 | ||
318 | task = zalloc(sizeof(*task)); | 318 | task = zalloc(sizeof(*task)); |
319 | task->pid = pid; | 319 | task->pid = pid; |
320 | task->nr = nr_tasks; | 320 | task->nr = nr_tasks; |
321 | strcpy(task->comm, comm); | 321 | strcpy(task->comm, comm); |
322 | /* | 322 | /* |
323 | * every task starts in sleeping state - this gets ignored | 323 | * every task starts in sleeping state - this gets ignored |
324 | * if there's no wakeup pointing to this sleep state: | 324 | * if there's no wakeup pointing to this sleep state: |
325 | */ | 325 | */ |
326 | add_sched_event_sleep(task, 0, 0); | 326 | add_sched_event_sleep(task, 0, 0); |
327 | 327 | ||
328 | pid_to_task[pid] = task; | 328 | pid_to_task[pid] = task; |
329 | nr_tasks++; | 329 | nr_tasks++; |
330 | tasks = realloc(tasks, nr_tasks*sizeof(struct task_task *)); | 330 | tasks = realloc(tasks, nr_tasks*sizeof(struct task_task *)); |
331 | BUG_ON(!tasks); | 331 | BUG_ON(!tasks); |
332 | tasks[task->nr] = task; | 332 | tasks[task->nr] = task; |
333 | 333 | ||
334 | if (verbose) | 334 | if (verbose) |
335 | printf("registered task #%ld, PID %ld (%s)\n", nr_tasks, pid, comm); | 335 | printf("registered task #%ld, PID %ld (%s)\n", nr_tasks, pid, comm); |
336 | 336 | ||
337 | return task; | 337 | return task; |
338 | } | 338 | } |
339 | 339 | ||
340 | 340 | ||
341 | static void print_task_traces(void) | 341 | static void print_task_traces(void) |
342 | { | 342 | { |
343 | struct task_desc *task; | 343 | struct task_desc *task; |
344 | unsigned long i; | 344 | unsigned long i; |
345 | 345 | ||
346 | for (i = 0; i < nr_tasks; i++) { | 346 | for (i = 0; i < nr_tasks; i++) { |
347 | task = tasks[i]; | 347 | task = tasks[i]; |
348 | printf("task %6ld (%20s:%10ld), nr_events: %ld\n", | 348 | printf("task %6ld (%20s:%10ld), nr_events: %ld\n", |
349 | task->nr, task->comm, task->pid, task->nr_events); | 349 | task->nr, task->comm, task->pid, task->nr_events); |
350 | } | 350 | } |
351 | } | 351 | } |
352 | 352 | ||
353 | static void add_cross_task_wakeups(void) | 353 | static void add_cross_task_wakeups(void) |
354 | { | 354 | { |
355 | struct task_desc *task1, *task2; | 355 | struct task_desc *task1, *task2; |
356 | unsigned long i, j; | 356 | unsigned long i, j; |
357 | 357 | ||
358 | for (i = 0; i < nr_tasks; i++) { | 358 | for (i = 0; i < nr_tasks; i++) { |
359 | task1 = tasks[i]; | 359 | task1 = tasks[i]; |
360 | j = i + 1; | 360 | j = i + 1; |
361 | if (j == nr_tasks) | 361 | if (j == nr_tasks) |
362 | j = 0; | 362 | j = 0; |
363 | task2 = tasks[j]; | 363 | task2 = tasks[j]; |
364 | add_sched_event_wakeup(task1, 0, task2); | 364 | add_sched_event_wakeup(task1, 0, task2); |
365 | } | 365 | } |
366 | } | 366 | } |
367 | 367 | ||
368 | static void | 368 | static void |
369 | process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) | 369 | process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) |
370 | { | 370 | { |
371 | int ret = 0; | 371 | int ret = 0; |
372 | 372 | ||
373 | switch (atom->type) { | 373 | switch (atom->type) { |
374 | case SCHED_EVENT_RUN: | 374 | case SCHED_EVENT_RUN: |
375 | burn_nsecs(atom->duration); | 375 | burn_nsecs(atom->duration); |
376 | break; | 376 | break; |
377 | case SCHED_EVENT_SLEEP: | 377 | case SCHED_EVENT_SLEEP: |
378 | if (atom->wait_sem) | 378 | if (atom->wait_sem) |
379 | ret = sem_wait(atom->wait_sem); | 379 | ret = sem_wait(atom->wait_sem); |
380 | BUG_ON(ret); | 380 | BUG_ON(ret); |
381 | break; | 381 | break; |
382 | case SCHED_EVENT_WAKEUP: | 382 | case SCHED_EVENT_WAKEUP: |
383 | if (atom->wait_sem) | 383 | if (atom->wait_sem) |
384 | ret = sem_post(atom->wait_sem); | 384 | ret = sem_post(atom->wait_sem); |
385 | BUG_ON(ret); | 385 | BUG_ON(ret); |
386 | break; | 386 | break; |
387 | case SCHED_EVENT_MIGRATION: | 387 | case SCHED_EVENT_MIGRATION: |
388 | break; | 388 | break; |
389 | default: | 389 | default: |
390 | BUG_ON(1); | 390 | BUG_ON(1); |
391 | } | 391 | } |
392 | } | 392 | } |
393 | 393 | ||
394 | static u64 get_cpu_usage_nsec_parent(void) | 394 | static u64 get_cpu_usage_nsec_parent(void) |
395 | { | 395 | { |
396 | struct rusage ru; | 396 | struct rusage ru; |
397 | u64 sum; | 397 | u64 sum; |
398 | int err; | 398 | int err; |
399 | 399 | ||
400 | err = getrusage(RUSAGE_SELF, &ru); | 400 | err = getrusage(RUSAGE_SELF, &ru); |
401 | BUG_ON(err); | 401 | BUG_ON(err); |
402 | 402 | ||
403 | sum = ru.ru_utime.tv_sec*1e9 + ru.ru_utime.tv_usec*1e3; | 403 | sum = ru.ru_utime.tv_sec*1e9 + ru.ru_utime.tv_usec*1e3; |
404 | sum += ru.ru_stime.tv_sec*1e9 + ru.ru_stime.tv_usec*1e3; | 404 | sum += ru.ru_stime.tv_sec*1e9 + ru.ru_stime.tv_usec*1e3; |
405 | 405 | ||
406 | return sum; | 406 | return sum; |
407 | } | 407 | } |
408 | 408 | ||
409 | static int self_open_counters(void) | 409 | static int self_open_counters(void) |
410 | { | 410 | { |
411 | struct perf_event_attr attr; | 411 | struct perf_event_attr attr; |
412 | int fd; | 412 | int fd; |
413 | 413 | ||
414 | memset(&attr, 0, sizeof(attr)); | 414 | memset(&attr, 0, sizeof(attr)); |
415 | 415 | ||
416 | attr.type = PERF_TYPE_SOFTWARE; | 416 | attr.type = PERF_TYPE_SOFTWARE; |
417 | attr.config = PERF_COUNT_SW_TASK_CLOCK; | 417 | attr.config = PERF_COUNT_SW_TASK_CLOCK; |
418 | 418 | ||
419 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | 419 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); |
420 | 420 | ||
421 | if (fd < 0) | 421 | if (fd < 0) |
422 | die("Error: sys_perf_event_open() syscall returned" | 422 | die("Error: sys_perf_event_open() syscall returned" |
423 | "with %d (%s)\n", fd, strerror(errno)); | 423 | "with %d (%s)\n", fd, strerror(errno)); |
424 | return fd; | 424 | return fd; |
425 | } | 425 | } |
426 | 426 | ||
427 | static u64 get_cpu_usage_nsec_self(int fd) | 427 | static u64 get_cpu_usage_nsec_self(int fd) |
428 | { | 428 | { |
429 | u64 runtime; | 429 | u64 runtime; |
430 | int ret; | 430 | int ret; |
431 | 431 | ||
432 | ret = read(fd, &runtime, sizeof(runtime)); | 432 | ret = read(fd, &runtime, sizeof(runtime)); |
433 | BUG_ON(ret != sizeof(runtime)); | 433 | BUG_ON(ret != sizeof(runtime)); |
434 | 434 | ||
435 | return runtime; | 435 | return runtime; |
436 | } | 436 | } |
437 | 437 | ||
438 | static void *thread_func(void *ctx) | 438 | static void *thread_func(void *ctx) |
439 | { | 439 | { |
440 | struct task_desc *this_task = ctx; | 440 | struct task_desc *this_task = ctx; |
441 | u64 cpu_usage_0, cpu_usage_1; | 441 | u64 cpu_usage_0, cpu_usage_1; |
442 | unsigned long i, ret; | 442 | unsigned long i, ret; |
443 | char comm2[22]; | 443 | char comm2[22]; |
444 | int fd; | 444 | int fd; |
445 | 445 | ||
446 | sprintf(comm2, ":%s", this_task->comm); | 446 | sprintf(comm2, ":%s", this_task->comm); |
447 | prctl(PR_SET_NAME, comm2); | 447 | prctl(PR_SET_NAME, comm2); |
448 | fd = self_open_counters(); | 448 | fd = self_open_counters(); |
449 | 449 | ||
450 | again: | 450 | again: |
451 | ret = sem_post(&this_task->ready_for_work); | 451 | ret = sem_post(&this_task->ready_for_work); |
452 | BUG_ON(ret); | 452 | BUG_ON(ret); |
453 | ret = pthread_mutex_lock(&start_work_mutex); | 453 | ret = pthread_mutex_lock(&start_work_mutex); |
454 | BUG_ON(ret); | 454 | BUG_ON(ret); |
455 | ret = pthread_mutex_unlock(&start_work_mutex); | 455 | ret = pthread_mutex_unlock(&start_work_mutex); |
456 | BUG_ON(ret); | 456 | BUG_ON(ret); |
457 | 457 | ||
458 | cpu_usage_0 = get_cpu_usage_nsec_self(fd); | 458 | cpu_usage_0 = get_cpu_usage_nsec_self(fd); |
459 | 459 | ||
460 | for (i = 0; i < this_task->nr_events; i++) { | 460 | for (i = 0; i < this_task->nr_events; i++) { |
461 | this_task->curr_event = i; | 461 | this_task->curr_event = i; |
462 | process_sched_event(this_task, this_task->atoms[i]); | 462 | process_sched_event(this_task, this_task->atoms[i]); |
463 | } | 463 | } |
464 | 464 | ||
465 | cpu_usage_1 = get_cpu_usage_nsec_self(fd); | 465 | cpu_usage_1 = get_cpu_usage_nsec_self(fd); |
466 | this_task->cpu_usage = cpu_usage_1 - cpu_usage_0; | 466 | this_task->cpu_usage = cpu_usage_1 - cpu_usage_0; |
467 | ret = sem_post(&this_task->work_done_sem); | 467 | ret = sem_post(&this_task->work_done_sem); |
468 | BUG_ON(ret); | 468 | BUG_ON(ret); |
469 | 469 | ||
470 | ret = pthread_mutex_lock(&work_done_wait_mutex); | 470 | ret = pthread_mutex_lock(&work_done_wait_mutex); |
471 | BUG_ON(ret); | 471 | BUG_ON(ret); |
472 | ret = pthread_mutex_unlock(&work_done_wait_mutex); | 472 | ret = pthread_mutex_unlock(&work_done_wait_mutex); |
473 | BUG_ON(ret); | 473 | BUG_ON(ret); |
474 | 474 | ||
475 | goto again; | 475 | goto again; |
476 | } | 476 | } |
477 | 477 | ||
478 | static void create_tasks(void) | 478 | static void create_tasks(void) |
479 | { | 479 | { |
480 | struct task_desc *task; | 480 | struct task_desc *task; |
481 | pthread_attr_t attr; | 481 | pthread_attr_t attr; |
482 | unsigned long i; | 482 | unsigned long i; |
483 | int err; | 483 | int err; |
484 | 484 | ||
485 | err = pthread_attr_init(&attr); | 485 | err = pthread_attr_init(&attr); |
486 | BUG_ON(err); | 486 | BUG_ON(err); |
487 | err = pthread_attr_setstacksize(&attr, | 487 | err = pthread_attr_setstacksize(&attr, |
488 | (size_t) max(16 * 1024, PTHREAD_STACK_MIN)); | 488 | (size_t) max(16 * 1024, PTHREAD_STACK_MIN)); |
489 | BUG_ON(err); | 489 | BUG_ON(err); |
490 | err = pthread_mutex_lock(&start_work_mutex); | 490 | err = pthread_mutex_lock(&start_work_mutex); |
491 | BUG_ON(err); | 491 | BUG_ON(err); |
492 | err = pthread_mutex_lock(&work_done_wait_mutex); | 492 | err = pthread_mutex_lock(&work_done_wait_mutex); |
493 | BUG_ON(err); | 493 | BUG_ON(err); |
494 | for (i = 0; i < nr_tasks; i++) { | 494 | for (i = 0; i < nr_tasks; i++) { |
495 | task = tasks[i]; | 495 | task = tasks[i]; |
496 | sem_init(&task->sleep_sem, 0, 0); | 496 | sem_init(&task->sleep_sem, 0, 0); |
497 | sem_init(&task->ready_for_work, 0, 0); | 497 | sem_init(&task->ready_for_work, 0, 0); |
498 | sem_init(&task->work_done_sem, 0, 0); | 498 | sem_init(&task->work_done_sem, 0, 0); |
499 | task->curr_event = 0; | 499 | task->curr_event = 0; |
500 | err = pthread_create(&task->thread, &attr, thread_func, task); | 500 | err = pthread_create(&task->thread, &attr, thread_func, task); |
501 | BUG_ON(err); | 501 | BUG_ON(err); |
502 | } | 502 | } |
503 | } | 503 | } |
504 | 504 | ||
505 | static void wait_for_tasks(void) | 505 | static void wait_for_tasks(void) |
506 | { | 506 | { |
507 | u64 cpu_usage_0, cpu_usage_1; | 507 | u64 cpu_usage_0, cpu_usage_1; |
508 | struct task_desc *task; | 508 | struct task_desc *task; |
509 | unsigned long i, ret; | 509 | unsigned long i, ret; |
510 | 510 | ||
511 | start_time = get_nsecs(); | 511 | start_time = get_nsecs(); |
512 | cpu_usage = 0; | 512 | cpu_usage = 0; |
513 | pthread_mutex_unlock(&work_done_wait_mutex); | 513 | pthread_mutex_unlock(&work_done_wait_mutex); |
514 | 514 | ||
515 | for (i = 0; i < nr_tasks; i++) { | 515 | for (i = 0; i < nr_tasks; i++) { |
516 | task = tasks[i]; | 516 | task = tasks[i]; |
517 | ret = sem_wait(&task->ready_for_work); | 517 | ret = sem_wait(&task->ready_for_work); |
518 | BUG_ON(ret); | 518 | BUG_ON(ret); |
519 | sem_init(&task->ready_for_work, 0, 0); | 519 | sem_init(&task->ready_for_work, 0, 0); |
520 | } | 520 | } |
521 | ret = pthread_mutex_lock(&work_done_wait_mutex); | 521 | ret = pthread_mutex_lock(&work_done_wait_mutex); |
522 | BUG_ON(ret); | 522 | BUG_ON(ret); |
523 | 523 | ||
524 | cpu_usage_0 = get_cpu_usage_nsec_parent(); | 524 | cpu_usage_0 = get_cpu_usage_nsec_parent(); |
525 | 525 | ||
526 | pthread_mutex_unlock(&start_work_mutex); | 526 | pthread_mutex_unlock(&start_work_mutex); |
527 | 527 | ||
528 | for (i = 0; i < nr_tasks; i++) { | 528 | for (i = 0; i < nr_tasks; i++) { |
529 | task = tasks[i]; | 529 | task = tasks[i]; |
530 | ret = sem_wait(&task->work_done_sem); | 530 | ret = sem_wait(&task->work_done_sem); |
531 | BUG_ON(ret); | 531 | BUG_ON(ret); |
532 | sem_init(&task->work_done_sem, 0, 0); | 532 | sem_init(&task->work_done_sem, 0, 0); |
533 | cpu_usage += task->cpu_usage; | 533 | cpu_usage += task->cpu_usage; |
534 | task->cpu_usage = 0; | 534 | task->cpu_usage = 0; |
535 | } | 535 | } |
536 | 536 | ||
537 | cpu_usage_1 = get_cpu_usage_nsec_parent(); | 537 | cpu_usage_1 = get_cpu_usage_nsec_parent(); |
538 | if (!runavg_cpu_usage) | 538 | if (!runavg_cpu_usage) |
539 | runavg_cpu_usage = cpu_usage; | 539 | runavg_cpu_usage = cpu_usage; |
540 | runavg_cpu_usage = (runavg_cpu_usage*9 + cpu_usage)/10; | 540 | runavg_cpu_usage = (runavg_cpu_usage*9 + cpu_usage)/10; |
541 | 541 | ||
542 | parent_cpu_usage = cpu_usage_1 - cpu_usage_0; | 542 | parent_cpu_usage = cpu_usage_1 - cpu_usage_0; |
543 | if (!runavg_parent_cpu_usage) | 543 | if (!runavg_parent_cpu_usage) |
544 | runavg_parent_cpu_usage = parent_cpu_usage; | 544 | runavg_parent_cpu_usage = parent_cpu_usage; |
545 | runavg_parent_cpu_usage = (runavg_parent_cpu_usage*9 + | 545 | runavg_parent_cpu_usage = (runavg_parent_cpu_usage*9 + |
546 | parent_cpu_usage)/10; | 546 | parent_cpu_usage)/10; |
547 | 547 | ||
548 | ret = pthread_mutex_lock(&start_work_mutex); | 548 | ret = pthread_mutex_lock(&start_work_mutex); |
549 | BUG_ON(ret); | 549 | BUG_ON(ret); |
550 | 550 | ||
551 | for (i = 0; i < nr_tasks; i++) { | 551 | for (i = 0; i < nr_tasks; i++) { |
552 | task = tasks[i]; | 552 | task = tasks[i]; |
553 | sem_init(&task->sleep_sem, 0, 0); | 553 | sem_init(&task->sleep_sem, 0, 0); |
554 | task->curr_event = 0; | 554 | task->curr_event = 0; |
555 | } | 555 | } |
556 | } | 556 | } |
557 | 557 | ||
558 | static void run_one_test(void) | 558 | static void run_one_test(void) |
559 | { | 559 | { |
560 | u64 T0, T1, delta, avg_delta, fluct; | 560 | u64 T0, T1, delta, avg_delta, fluct; |
561 | 561 | ||
562 | T0 = get_nsecs(); | 562 | T0 = get_nsecs(); |
563 | wait_for_tasks(); | 563 | wait_for_tasks(); |
564 | T1 = get_nsecs(); | 564 | T1 = get_nsecs(); |
565 | 565 | ||
566 | delta = T1 - T0; | 566 | delta = T1 - T0; |
567 | sum_runtime += delta; | 567 | sum_runtime += delta; |
568 | nr_runs++; | 568 | nr_runs++; |
569 | 569 | ||
570 | avg_delta = sum_runtime / nr_runs; | 570 | avg_delta = sum_runtime / nr_runs; |
571 | if (delta < avg_delta) | 571 | if (delta < avg_delta) |
572 | fluct = avg_delta - delta; | 572 | fluct = avg_delta - delta; |
573 | else | 573 | else |
574 | fluct = delta - avg_delta; | 574 | fluct = delta - avg_delta; |
575 | sum_fluct += fluct; | 575 | sum_fluct += fluct; |
576 | if (!run_avg) | 576 | if (!run_avg) |
577 | run_avg = delta; | 577 | run_avg = delta; |
578 | run_avg = (run_avg*9 + delta)/10; | 578 | run_avg = (run_avg*9 + delta)/10; |
579 | 579 | ||
580 | printf("#%-3ld: %0.3f, ", | 580 | printf("#%-3ld: %0.3f, ", |
581 | nr_runs, (double)delta/1000000.0); | 581 | nr_runs, (double)delta/1000000.0); |
582 | 582 | ||
583 | printf("ravg: %0.2f, ", | 583 | printf("ravg: %0.2f, ", |
584 | (double)run_avg/1e6); | 584 | (double)run_avg/1e6); |
585 | 585 | ||
586 | printf("cpu: %0.2f / %0.2f", | 586 | printf("cpu: %0.2f / %0.2f", |
587 | (double)cpu_usage/1e6, (double)runavg_cpu_usage/1e6); | 587 | (double)cpu_usage/1e6, (double)runavg_cpu_usage/1e6); |
588 | 588 | ||
589 | #if 0 | 589 | #if 0 |
590 | /* | 590 | /* |
591 | * rusage statistics done by the parent, these are less | 591 | * rusage statistics done by the parent, these are less |
592 | * accurate than the sum_exec_runtime based statistics: | 592 | * accurate than the sum_exec_runtime based statistics: |
593 | */ | 593 | */ |
594 | printf(" [%0.2f / %0.2f]", | 594 | printf(" [%0.2f / %0.2f]", |
595 | (double)parent_cpu_usage/1e6, | 595 | (double)parent_cpu_usage/1e6, |
596 | (double)runavg_parent_cpu_usage/1e6); | 596 | (double)runavg_parent_cpu_usage/1e6); |
597 | #endif | 597 | #endif |
598 | 598 | ||
599 | printf("\n"); | 599 | printf("\n"); |
600 | 600 | ||
601 | if (nr_sleep_corrections) | 601 | if (nr_sleep_corrections) |
602 | printf(" (%ld sleep corrections)\n", nr_sleep_corrections); | 602 | printf(" (%ld sleep corrections)\n", nr_sleep_corrections); |
603 | nr_sleep_corrections = 0; | 603 | nr_sleep_corrections = 0; |
604 | } | 604 | } |
605 | 605 | ||
606 | static void test_calibrations(void) | 606 | static void test_calibrations(void) |
607 | { | 607 | { |
608 | u64 T0, T1; | 608 | u64 T0, T1; |
609 | 609 | ||
610 | T0 = get_nsecs(); | 610 | T0 = get_nsecs(); |
611 | burn_nsecs(1e6); | 611 | burn_nsecs(1e6); |
612 | T1 = get_nsecs(); | 612 | T1 = get_nsecs(); |
613 | 613 | ||
614 | printf("the run test took %" PRIu64 " nsecs\n", T1 - T0); | 614 | printf("the run test took %" PRIu64 " nsecs\n", T1 - T0); |
615 | 615 | ||
616 | T0 = get_nsecs(); | 616 | T0 = get_nsecs(); |
617 | sleep_nsecs(1e6); | 617 | sleep_nsecs(1e6); |
618 | T1 = get_nsecs(); | 618 | T1 = get_nsecs(); |
619 | 619 | ||
620 | printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0); | 620 | printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0); |
621 | } | 621 | } |
622 | 622 | ||
623 | #define FILL_FIELD(ptr, field, event, data) \ | 623 | #define FILL_FIELD(ptr, field, event, data) \ |
624 | ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data) | 624 | ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data) |
625 | 625 | ||
626 | #define FILL_ARRAY(ptr, array, event, data) \ | 626 | #define FILL_ARRAY(ptr, array, event, data) \ |
627 | do { \ | 627 | do { \ |
628 | void *__array = raw_field_ptr(event, #array, data); \ | 628 | void *__array = raw_field_ptr(event, #array, data); \ |
629 | memcpy(ptr.array, __array, sizeof(ptr.array)); \ | 629 | memcpy(ptr.array, __array, sizeof(ptr.array)); \ |
630 | } while(0) | 630 | } while(0) |
631 | 631 | ||
632 | #define FILL_COMMON_FIELDS(ptr, event, data) \ | 632 | #define FILL_COMMON_FIELDS(ptr, event, data) \ |
633 | do { \ | 633 | do { \ |
634 | FILL_FIELD(ptr, common_type, event, data); \ | 634 | FILL_FIELD(ptr, common_type, event, data); \ |
635 | FILL_FIELD(ptr, common_flags, event, data); \ | 635 | FILL_FIELD(ptr, common_flags, event, data); \ |
636 | FILL_FIELD(ptr, common_preempt_count, event, data); \ | 636 | FILL_FIELD(ptr, common_preempt_count, event, data); \ |
637 | FILL_FIELD(ptr, common_pid, event, data); \ | 637 | FILL_FIELD(ptr, common_pid, event, data); \ |
638 | FILL_FIELD(ptr, common_tgid, event, data); \ | 638 | FILL_FIELD(ptr, common_tgid, event, data); \ |
639 | } while (0) | 639 | } while (0) |
640 | 640 | ||
641 | 641 | ||
642 | 642 | ||
643 | struct trace_switch_event { | 643 | struct trace_switch_event { |
644 | u32 size; | 644 | u32 size; |
645 | 645 | ||
646 | u16 common_type; | 646 | u16 common_type; |
647 | u8 common_flags; | 647 | u8 common_flags; |
648 | u8 common_preempt_count; | 648 | u8 common_preempt_count; |
649 | u32 common_pid; | 649 | u32 common_pid; |
650 | u32 common_tgid; | 650 | u32 common_tgid; |
651 | 651 | ||
652 | char prev_comm[16]; | 652 | char prev_comm[16]; |
653 | u32 prev_pid; | 653 | u32 prev_pid; |
654 | u32 prev_prio; | 654 | u32 prev_prio; |
655 | u64 prev_state; | 655 | u64 prev_state; |
656 | char next_comm[16]; | 656 | char next_comm[16]; |
657 | u32 next_pid; | 657 | u32 next_pid; |
658 | u32 next_prio; | 658 | u32 next_prio; |
659 | }; | 659 | }; |
660 | 660 | ||
661 | struct trace_runtime_event { | 661 | struct trace_runtime_event { |
662 | u32 size; | 662 | u32 size; |
663 | 663 | ||
664 | u16 common_type; | 664 | u16 common_type; |
665 | u8 common_flags; | 665 | u8 common_flags; |
666 | u8 common_preempt_count; | 666 | u8 common_preempt_count; |
667 | u32 common_pid; | 667 | u32 common_pid; |
668 | u32 common_tgid; | 668 | u32 common_tgid; |
669 | 669 | ||
670 | char comm[16]; | 670 | char comm[16]; |
671 | u32 pid; | 671 | u32 pid; |
672 | u64 runtime; | 672 | u64 runtime; |
673 | u64 vruntime; | 673 | u64 vruntime; |
674 | }; | 674 | }; |
675 | 675 | ||
676 | struct trace_wakeup_event { | 676 | struct trace_wakeup_event { |
677 | u32 size; | 677 | u32 size; |
678 | 678 | ||
679 | u16 common_type; | 679 | u16 common_type; |
680 | u8 common_flags; | 680 | u8 common_flags; |
681 | u8 common_preempt_count; | 681 | u8 common_preempt_count; |
682 | u32 common_pid; | 682 | u32 common_pid; |
683 | u32 common_tgid; | 683 | u32 common_tgid; |
684 | 684 | ||
685 | char comm[16]; | 685 | char comm[16]; |
686 | u32 pid; | 686 | u32 pid; |
687 | 687 | ||
688 | u32 prio; | 688 | u32 prio; |
689 | u32 success; | 689 | u32 success; |
690 | u32 cpu; | 690 | u32 cpu; |
691 | }; | 691 | }; |
692 | 692 | ||
693 | struct trace_fork_event { | 693 | struct trace_fork_event { |
694 | u32 size; | 694 | u32 size; |
695 | 695 | ||
696 | u16 common_type; | 696 | u16 common_type; |
697 | u8 common_flags; | 697 | u8 common_flags; |
698 | u8 common_preempt_count; | 698 | u8 common_preempt_count; |
699 | u32 common_pid; | 699 | u32 common_pid; |
700 | u32 common_tgid; | 700 | u32 common_tgid; |
701 | 701 | ||
702 | char parent_comm[16]; | 702 | char parent_comm[16]; |
703 | u32 parent_pid; | 703 | u32 parent_pid; |
704 | char child_comm[16]; | 704 | char child_comm[16]; |
705 | u32 child_pid; | 705 | u32 child_pid; |
706 | }; | 706 | }; |
707 | 707 | ||
708 | struct trace_migrate_task_event { | 708 | struct trace_migrate_task_event { |
709 | u32 size; | 709 | u32 size; |
710 | 710 | ||
711 | u16 common_type; | 711 | u16 common_type; |
712 | u8 common_flags; | 712 | u8 common_flags; |
713 | u8 common_preempt_count; | 713 | u8 common_preempt_count; |
714 | u32 common_pid; | 714 | u32 common_pid; |
715 | u32 common_tgid; | 715 | u32 common_tgid; |
716 | 716 | ||
717 | char comm[16]; | 717 | char comm[16]; |
718 | u32 pid; | 718 | u32 pid; |
719 | 719 | ||
720 | u32 prio; | 720 | u32 prio; |
721 | u32 cpu; | 721 | u32 cpu; |
722 | }; | 722 | }; |
723 | 723 | ||
724 | struct trace_sched_handler { | 724 | struct trace_sched_handler { |
725 | void (*switch_event)(struct trace_switch_event *, | 725 | void (*switch_event)(struct trace_switch_event *, |
726 | struct perf_session *, | 726 | struct perf_session *, |
727 | struct event *, | 727 | struct event *, |
728 | int cpu, | 728 | int cpu, |
729 | u64 timestamp, | 729 | u64 timestamp, |
730 | struct thread *thread); | 730 | struct thread *thread); |
731 | 731 | ||
732 | void (*runtime_event)(struct trace_runtime_event *, | 732 | void (*runtime_event)(struct trace_runtime_event *, |
733 | struct perf_session *, | 733 | struct perf_session *, |
734 | struct event *, | 734 | struct event *, |
735 | int cpu, | 735 | int cpu, |
736 | u64 timestamp, | 736 | u64 timestamp, |
737 | struct thread *thread); | 737 | struct thread *thread); |
738 | 738 | ||
739 | void (*wakeup_event)(struct trace_wakeup_event *, | 739 | void (*wakeup_event)(struct trace_wakeup_event *, |
740 | struct perf_session *, | 740 | struct perf_session *, |
741 | struct event *, | 741 | struct event *, |
742 | int cpu, | 742 | int cpu, |
743 | u64 timestamp, | 743 | u64 timestamp, |
744 | struct thread *thread); | 744 | struct thread *thread); |
745 | 745 | ||
746 | void (*fork_event)(struct trace_fork_event *, | 746 | void (*fork_event)(struct trace_fork_event *, |
747 | struct event *, | 747 | struct event *, |
748 | int cpu, | 748 | int cpu, |
749 | u64 timestamp, | 749 | u64 timestamp, |
750 | struct thread *thread); | 750 | struct thread *thread); |
751 | 751 | ||
752 | void (*migrate_task_event)(struct trace_migrate_task_event *, | 752 | void (*migrate_task_event)(struct trace_migrate_task_event *, |
753 | struct perf_session *session, | 753 | struct perf_session *session, |
754 | struct event *, | 754 | struct event *, |
755 | int cpu, | 755 | int cpu, |
756 | u64 timestamp, | 756 | u64 timestamp, |
757 | struct thread *thread); | 757 | struct thread *thread); |
758 | }; | 758 | }; |
759 | 759 | ||
760 | 760 | ||
761 | static void | 761 | static void |
762 | replay_wakeup_event(struct trace_wakeup_event *wakeup_event, | 762 | replay_wakeup_event(struct trace_wakeup_event *wakeup_event, |
763 | struct perf_session *session __used, | 763 | struct perf_session *session __used, |
764 | struct event *event, | 764 | struct event *event, |
765 | int cpu __used, | 765 | int cpu __used, |
766 | u64 timestamp __used, | 766 | u64 timestamp __used, |
767 | struct thread *thread __used) | 767 | struct thread *thread __used) |
768 | { | 768 | { |
769 | struct task_desc *waker, *wakee; | 769 | struct task_desc *waker, *wakee; |
770 | 770 | ||
771 | if (verbose) { | 771 | if (verbose) { |
772 | printf("sched_wakeup event %p\n", event); | 772 | printf("sched_wakeup event %p\n", event); |
773 | 773 | ||
774 | printf(" ... pid %d woke up %s/%d\n", | 774 | printf(" ... pid %d woke up %s/%d\n", |
775 | wakeup_event->common_pid, | 775 | wakeup_event->common_pid, |
776 | wakeup_event->comm, | 776 | wakeup_event->comm, |
777 | wakeup_event->pid); | 777 | wakeup_event->pid); |
778 | } | 778 | } |
779 | 779 | ||
780 | waker = register_pid(wakeup_event->common_pid, "<unknown>"); | 780 | waker = register_pid(wakeup_event->common_pid, "<unknown>"); |
781 | wakee = register_pid(wakeup_event->pid, wakeup_event->comm); | 781 | wakee = register_pid(wakeup_event->pid, wakeup_event->comm); |
782 | 782 | ||
783 | add_sched_event_wakeup(waker, timestamp, wakee); | 783 | add_sched_event_wakeup(waker, timestamp, wakee); |
784 | } | 784 | } |
785 | 785 | ||
786 | static u64 cpu_last_switched[MAX_CPUS]; | 786 | static u64 cpu_last_switched[MAX_CPUS]; |
787 | 787 | ||
788 | static void | 788 | static void |
789 | replay_switch_event(struct trace_switch_event *switch_event, | 789 | replay_switch_event(struct trace_switch_event *switch_event, |
790 | struct perf_session *session __used, | 790 | struct perf_session *session __used, |
791 | struct event *event, | 791 | struct event *event, |
792 | int cpu, | 792 | int cpu, |
793 | u64 timestamp, | 793 | u64 timestamp, |
794 | struct thread *thread __used) | 794 | struct thread *thread __used) |
795 | { | 795 | { |
796 | struct task_desc *prev, __used *next; | 796 | struct task_desc *prev, __used *next; |
797 | u64 timestamp0; | 797 | u64 timestamp0; |
798 | s64 delta; | 798 | s64 delta; |
799 | 799 | ||
800 | if (verbose) | 800 | if (verbose) |
801 | printf("sched_switch event %p\n", event); | 801 | printf("sched_switch event %p\n", event); |
802 | 802 | ||
803 | if (cpu >= MAX_CPUS || cpu < 0) | 803 | if (cpu >= MAX_CPUS || cpu < 0) |
804 | return; | 804 | return; |
805 | 805 | ||
806 | timestamp0 = cpu_last_switched[cpu]; | 806 | timestamp0 = cpu_last_switched[cpu]; |
807 | if (timestamp0) | 807 | if (timestamp0) |
808 | delta = timestamp - timestamp0; | 808 | delta = timestamp - timestamp0; |
809 | else | 809 | else |
810 | delta = 0; | 810 | delta = 0; |
811 | 811 | ||
812 | if (delta < 0) | 812 | if (delta < 0) |
813 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); | 813 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); |
814 | 814 | ||
815 | if (verbose) { | 815 | if (verbose) { |
816 | printf(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n", | 816 | printf(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n", |
817 | switch_event->prev_comm, switch_event->prev_pid, | 817 | switch_event->prev_comm, switch_event->prev_pid, |
818 | switch_event->next_comm, switch_event->next_pid, | 818 | switch_event->next_comm, switch_event->next_pid, |
819 | delta); | 819 | delta); |
820 | } | 820 | } |
821 | 821 | ||
822 | prev = register_pid(switch_event->prev_pid, switch_event->prev_comm); | 822 | prev = register_pid(switch_event->prev_pid, switch_event->prev_comm); |
823 | next = register_pid(switch_event->next_pid, switch_event->next_comm); | 823 | next = register_pid(switch_event->next_pid, switch_event->next_comm); |
824 | 824 | ||
825 | cpu_last_switched[cpu] = timestamp; | 825 | cpu_last_switched[cpu] = timestamp; |
826 | 826 | ||
827 | add_sched_event_run(prev, timestamp, delta); | 827 | add_sched_event_run(prev, timestamp, delta); |
828 | add_sched_event_sleep(prev, timestamp, switch_event->prev_state); | 828 | add_sched_event_sleep(prev, timestamp, switch_event->prev_state); |
829 | } | 829 | } |
830 | 830 | ||
831 | 831 | ||
832 | static void | 832 | static void |
833 | replay_fork_event(struct trace_fork_event *fork_event, | 833 | replay_fork_event(struct trace_fork_event *fork_event, |
834 | struct event *event, | 834 | struct event *event, |
835 | int cpu __used, | 835 | int cpu __used, |
836 | u64 timestamp __used, | 836 | u64 timestamp __used, |
837 | struct thread *thread __used) | 837 | struct thread *thread __used) |
838 | { | 838 | { |
839 | if (verbose) { | 839 | if (verbose) { |
840 | printf("sched_fork event %p\n", event); | 840 | printf("sched_fork event %p\n", event); |
841 | printf("... parent: %s/%d\n", fork_event->parent_comm, fork_event->parent_pid); | 841 | printf("... parent: %s/%d\n", fork_event->parent_comm, fork_event->parent_pid); |
842 | printf("... child: %s/%d\n", fork_event->child_comm, fork_event->child_pid); | 842 | printf("... child: %s/%d\n", fork_event->child_comm, fork_event->child_pid); |
843 | } | 843 | } |
844 | register_pid(fork_event->parent_pid, fork_event->parent_comm); | 844 | register_pid(fork_event->parent_pid, fork_event->parent_comm); |
845 | register_pid(fork_event->child_pid, fork_event->child_comm); | 845 | register_pid(fork_event->child_pid, fork_event->child_comm); |
846 | } | 846 | } |
847 | 847 | ||
848 | static struct trace_sched_handler replay_ops = { | 848 | static struct trace_sched_handler replay_ops = { |
849 | .wakeup_event = replay_wakeup_event, | 849 | .wakeup_event = replay_wakeup_event, |
850 | .switch_event = replay_switch_event, | 850 | .switch_event = replay_switch_event, |
851 | .fork_event = replay_fork_event, | 851 | .fork_event = replay_fork_event, |
852 | }; | 852 | }; |
853 | 853 | ||
854 | struct sort_dimension { | 854 | struct sort_dimension { |
855 | const char *name; | 855 | const char *name; |
856 | sort_fn_t cmp; | 856 | sort_fn_t cmp; |
857 | struct list_head list; | 857 | struct list_head list; |
858 | }; | 858 | }; |
859 | 859 | ||
860 | static LIST_HEAD(cmp_pid); | 860 | static LIST_HEAD(cmp_pid); |
861 | 861 | ||
862 | static int | 862 | static int |
863 | thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r) | 863 | thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r) |
864 | { | 864 | { |
865 | struct sort_dimension *sort; | 865 | struct sort_dimension *sort; |
866 | int ret = 0; | 866 | int ret = 0; |
867 | 867 | ||
868 | BUG_ON(list_empty(list)); | 868 | BUG_ON(list_empty(list)); |
869 | 869 | ||
870 | list_for_each_entry(sort, list, list) { | 870 | list_for_each_entry(sort, list, list) { |
871 | ret = sort->cmp(l, r); | 871 | ret = sort->cmp(l, r); |
872 | if (ret) | 872 | if (ret) |
873 | return ret; | 873 | return ret; |
874 | } | 874 | } |
875 | 875 | ||
876 | return ret; | 876 | return ret; |
877 | } | 877 | } |
878 | 878 | ||
879 | static struct work_atoms * | 879 | static struct work_atoms * |
880 | thread_atoms_search(struct rb_root *root, struct thread *thread, | 880 | thread_atoms_search(struct rb_root *root, struct thread *thread, |
881 | struct list_head *sort_list) | 881 | struct list_head *sort_list) |
882 | { | 882 | { |
883 | struct rb_node *node = root->rb_node; | 883 | struct rb_node *node = root->rb_node; |
884 | struct work_atoms key = { .thread = thread }; | 884 | struct work_atoms key = { .thread = thread }; |
885 | 885 | ||
886 | while (node) { | 886 | while (node) { |
887 | struct work_atoms *atoms; | 887 | struct work_atoms *atoms; |
888 | int cmp; | 888 | int cmp; |
889 | 889 | ||
890 | atoms = container_of(node, struct work_atoms, node); | 890 | atoms = container_of(node, struct work_atoms, node); |
891 | 891 | ||
892 | cmp = thread_lat_cmp(sort_list, &key, atoms); | 892 | cmp = thread_lat_cmp(sort_list, &key, atoms); |
893 | if (cmp > 0) | 893 | if (cmp > 0) |
894 | node = node->rb_left; | 894 | node = node->rb_left; |
895 | else if (cmp < 0) | 895 | else if (cmp < 0) |
896 | node = node->rb_right; | 896 | node = node->rb_right; |
897 | else { | 897 | else { |
898 | BUG_ON(thread != atoms->thread); | 898 | BUG_ON(thread != atoms->thread); |
899 | return atoms; | 899 | return atoms; |
900 | } | 900 | } |
901 | } | 901 | } |
902 | return NULL; | 902 | return NULL; |
903 | } | 903 | } |
904 | 904 | ||
905 | static void | 905 | static void |
906 | __thread_latency_insert(struct rb_root *root, struct work_atoms *data, | 906 | __thread_latency_insert(struct rb_root *root, struct work_atoms *data, |
907 | struct list_head *sort_list) | 907 | struct list_head *sort_list) |
908 | { | 908 | { |
909 | struct rb_node **new = &(root->rb_node), *parent = NULL; | 909 | struct rb_node **new = &(root->rb_node), *parent = NULL; |
910 | 910 | ||
911 | while (*new) { | 911 | while (*new) { |
912 | struct work_atoms *this; | 912 | struct work_atoms *this; |
913 | int cmp; | 913 | int cmp; |
914 | 914 | ||
915 | this = container_of(*new, struct work_atoms, node); | 915 | this = container_of(*new, struct work_atoms, node); |
916 | parent = *new; | 916 | parent = *new; |
917 | 917 | ||
918 | cmp = thread_lat_cmp(sort_list, data, this); | 918 | cmp = thread_lat_cmp(sort_list, data, this); |
919 | 919 | ||
920 | if (cmp > 0) | 920 | if (cmp > 0) |
921 | new = &((*new)->rb_left); | 921 | new = &((*new)->rb_left); |
922 | else | 922 | else |
923 | new = &((*new)->rb_right); | 923 | new = &((*new)->rb_right); |
924 | } | 924 | } |
925 | 925 | ||
926 | rb_link_node(&data->node, parent, new); | 926 | rb_link_node(&data->node, parent, new); |
927 | rb_insert_color(&data->node, root); | 927 | rb_insert_color(&data->node, root); |
928 | } | 928 | } |
929 | 929 | ||
930 | static void thread_atoms_insert(struct thread *thread) | 930 | static void thread_atoms_insert(struct thread *thread) |
931 | { | 931 | { |
932 | struct work_atoms *atoms = zalloc(sizeof(*atoms)); | 932 | struct work_atoms *atoms = zalloc(sizeof(*atoms)); |
933 | if (!atoms) | 933 | if (!atoms) |
934 | die("No memory"); | 934 | die("No memory"); |
935 | 935 | ||
936 | atoms->thread = thread; | 936 | atoms->thread = thread; |
937 | INIT_LIST_HEAD(&atoms->work_list); | 937 | INIT_LIST_HEAD(&atoms->work_list); |
938 | __thread_latency_insert(&atom_root, atoms, &cmp_pid); | 938 | __thread_latency_insert(&atom_root, atoms, &cmp_pid); |
939 | } | 939 | } |
940 | 940 | ||
941 | static void | 941 | static void |
942 | latency_fork_event(struct trace_fork_event *fork_event __used, | 942 | latency_fork_event(struct trace_fork_event *fork_event __used, |
943 | struct event *event __used, | 943 | struct event *event __used, |
944 | int cpu __used, | 944 | int cpu __used, |
945 | u64 timestamp __used, | 945 | u64 timestamp __used, |
946 | struct thread *thread __used) | 946 | struct thread *thread __used) |
947 | { | 947 | { |
948 | /* should insert the newcomer */ | 948 | /* should insert the newcomer */ |
949 | } | 949 | } |
950 | 950 | ||
951 | __used | 951 | __used |
952 | static char sched_out_state(struct trace_switch_event *switch_event) | 952 | static char sched_out_state(struct trace_switch_event *switch_event) |
953 | { | 953 | { |
954 | const char *str = TASK_STATE_TO_CHAR_STR; | 954 | const char *str = TASK_STATE_TO_CHAR_STR; |
955 | 955 | ||
956 | return str[switch_event->prev_state]; | 956 | return str[switch_event->prev_state]; |
957 | } | 957 | } |
958 | 958 | ||
959 | static void | 959 | static void |
960 | add_sched_out_event(struct work_atoms *atoms, | 960 | add_sched_out_event(struct work_atoms *atoms, |
961 | char run_state, | 961 | char run_state, |
962 | u64 timestamp) | 962 | u64 timestamp) |
963 | { | 963 | { |
964 | struct work_atom *atom = zalloc(sizeof(*atom)); | 964 | struct work_atom *atom = zalloc(sizeof(*atom)); |
965 | if (!atom) | 965 | if (!atom) |
966 | die("Non memory"); | 966 | die("Non memory"); |
967 | 967 | ||
968 | atom->sched_out_time = timestamp; | 968 | atom->sched_out_time = timestamp; |
969 | 969 | ||
970 | if (run_state == 'R') { | 970 | if (run_state == 'R') { |
971 | atom->state = THREAD_WAIT_CPU; | 971 | atom->state = THREAD_WAIT_CPU; |
972 | atom->wake_up_time = atom->sched_out_time; | 972 | atom->wake_up_time = atom->sched_out_time; |
973 | } | 973 | } |
974 | 974 | ||
975 | list_add_tail(&atom->list, &atoms->work_list); | 975 | list_add_tail(&atom->list, &atoms->work_list); |
976 | } | 976 | } |
977 | 977 | ||
978 | static void | 978 | static void |
979 | add_runtime_event(struct work_atoms *atoms, u64 delta, u64 timestamp __used) | 979 | add_runtime_event(struct work_atoms *atoms, u64 delta, u64 timestamp __used) |
980 | { | 980 | { |
981 | struct work_atom *atom; | 981 | struct work_atom *atom; |
982 | 982 | ||
983 | BUG_ON(list_empty(&atoms->work_list)); | 983 | BUG_ON(list_empty(&atoms->work_list)); |
984 | 984 | ||
985 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); | 985 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); |
986 | 986 | ||
987 | atom->runtime += delta; | 987 | atom->runtime += delta; |
988 | atoms->total_runtime += delta; | 988 | atoms->total_runtime += delta; |
989 | } | 989 | } |
990 | 990 | ||
991 | static void | 991 | static void |
992 | add_sched_in_event(struct work_atoms *atoms, u64 timestamp) | 992 | add_sched_in_event(struct work_atoms *atoms, u64 timestamp) |
993 | { | 993 | { |
994 | struct work_atom *atom; | 994 | struct work_atom *atom; |
995 | u64 delta; | 995 | u64 delta; |
996 | 996 | ||
997 | if (list_empty(&atoms->work_list)) | 997 | if (list_empty(&atoms->work_list)) |
998 | return; | 998 | return; |
999 | 999 | ||
1000 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); | 1000 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); |
1001 | 1001 | ||
1002 | if (atom->state != THREAD_WAIT_CPU) | 1002 | if (atom->state != THREAD_WAIT_CPU) |
1003 | return; | 1003 | return; |
1004 | 1004 | ||
1005 | if (timestamp < atom->wake_up_time) { | 1005 | if (timestamp < atom->wake_up_time) { |
1006 | atom->state = THREAD_IGNORE; | 1006 | atom->state = THREAD_IGNORE; |
1007 | return; | 1007 | return; |
1008 | } | 1008 | } |
1009 | 1009 | ||
1010 | atom->state = THREAD_SCHED_IN; | 1010 | atom->state = THREAD_SCHED_IN; |
1011 | atom->sched_in_time = timestamp; | 1011 | atom->sched_in_time = timestamp; |
1012 | 1012 | ||
1013 | delta = atom->sched_in_time - atom->wake_up_time; | 1013 | delta = atom->sched_in_time - atom->wake_up_time; |
1014 | atoms->total_lat += delta; | 1014 | atoms->total_lat += delta; |
1015 | if (delta > atoms->max_lat) { | 1015 | if (delta > atoms->max_lat) { |
1016 | atoms->max_lat = delta; | 1016 | atoms->max_lat = delta; |
1017 | atoms->max_lat_at = timestamp; | 1017 | atoms->max_lat_at = timestamp; |
1018 | } | 1018 | } |
1019 | atoms->nb_atoms++; | 1019 | atoms->nb_atoms++; |
1020 | } | 1020 | } |
1021 | 1021 | ||
1022 | static void | 1022 | static void |
1023 | latency_switch_event(struct trace_switch_event *switch_event, | 1023 | latency_switch_event(struct trace_switch_event *switch_event, |
1024 | struct perf_session *session, | 1024 | struct perf_session *session, |
1025 | struct event *event __used, | 1025 | struct event *event __used, |
1026 | int cpu, | 1026 | int cpu, |
1027 | u64 timestamp, | 1027 | u64 timestamp, |
1028 | struct thread *thread __used) | 1028 | struct thread *thread __used) |
1029 | { | 1029 | { |
1030 | struct work_atoms *out_events, *in_events; | 1030 | struct work_atoms *out_events, *in_events; |
1031 | struct thread *sched_out, *sched_in; | 1031 | struct thread *sched_out, *sched_in; |
1032 | u64 timestamp0; | 1032 | u64 timestamp0; |
1033 | s64 delta; | 1033 | s64 delta; |
1034 | 1034 | ||
1035 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); | 1035 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); |
1036 | 1036 | ||
1037 | timestamp0 = cpu_last_switched[cpu]; | 1037 | timestamp0 = cpu_last_switched[cpu]; |
1038 | cpu_last_switched[cpu] = timestamp; | 1038 | cpu_last_switched[cpu] = timestamp; |
1039 | if (timestamp0) | 1039 | if (timestamp0) |
1040 | delta = timestamp - timestamp0; | 1040 | delta = timestamp - timestamp0; |
1041 | else | 1041 | else |
1042 | delta = 0; | 1042 | delta = 0; |
1043 | 1043 | ||
1044 | if (delta < 0) | 1044 | if (delta < 0) |
1045 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); | 1045 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); |
1046 | 1046 | ||
1047 | 1047 | ||
1048 | sched_out = perf_session__findnew(session, switch_event->prev_pid); | 1048 | sched_out = perf_session__findnew(session, switch_event->prev_pid); |
1049 | sched_in = perf_session__findnew(session, switch_event->next_pid); | 1049 | sched_in = perf_session__findnew(session, switch_event->next_pid); |
1050 | 1050 | ||
1051 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); | 1051 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); |
1052 | if (!out_events) { | 1052 | if (!out_events) { |
1053 | thread_atoms_insert(sched_out); | 1053 | thread_atoms_insert(sched_out); |
1054 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); | 1054 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); |
1055 | if (!out_events) | 1055 | if (!out_events) |
1056 | die("out-event: Internal tree error"); | 1056 | die("out-event: Internal tree error"); |
1057 | } | 1057 | } |
1058 | add_sched_out_event(out_events, sched_out_state(switch_event), timestamp); | 1058 | add_sched_out_event(out_events, sched_out_state(switch_event), timestamp); |
1059 | 1059 | ||
1060 | in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid); | 1060 | in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid); |
1061 | if (!in_events) { | 1061 | if (!in_events) { |
1062 | thread_atoms_insert(sched_in); | 1062 | thread_atoms_insert(sched_in); |
1063 | in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid); | 1063 | in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid); |
1064 | if (!in_events) | 1064 | if (!in_events) |
1065 | die("in-event: Internal tree error"); | 1065 | die("in-event: Internal tree error"); |
1066 | /* | 1066 | /* |
1067 | * Take came in we have not heard about yet, | 1067 | * Take came in we have not heard about yet, |
1068 | * add in an initial atom in runnable state: | 1068 | * add in an initial atom in runnable state: |
1069 | */ | 1069 | */ |
1070 | add_sched_out_event(in_events, 'R', timestamp); | 1070 | add_sched_out_event(in_events, 'R', timestamp); |
1071 | } | 1071 | } |
1072 | add_sched_in_event(in_events, timestamp); | 1072 | add_sched_in_event(in_events, timestamp); |
1073 | } | 1073 | } |
1074 | 1074 | ||
1075 | static void | 1075 | static void |
1076 | latency_runtime_event(struct trace_runtime_event *runtime_event, | 1076 | latency_runtime_event(struct trace_runtime_event *runtime_event, |
1077 | struct perf_session *session, | 1077 | struct perf_session *session, |
1078 | struct event *event __used, | 1078 | struct event *event __used, |
1079 | int cpu, | 1079 | int cpu, |
1080 | u64 timestamp, | 1080 | u64 timestamp, |
1081 | struct thread *this_thread __used) | 1081 | struct thread *this_thread __used) |
1082 | { | 1082 | { |
1083 | struct thread *thread = perf_session__findnew(session, runtime_event->pid); | 1083 | struct thread *thread = perf_session__findnew(session, runtime_event->pid); |
1084 | struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); | 1084 | struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); |
1085 | 1085 | ||
1086 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); | 1086 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); |
1087 | if (!atoms) { | 1087 | if (!atoms) { |
1088 | thread_atoms_insert(thread); | 1088 | thread_atoms_insert(thread); |
1089 | atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); | 1089 | atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); |
1090 | if (!atoms) | 1090 | if (!atoms) |
1091 | die("in-event: Internal tree error"); | 1091 | die("in-event: Internal tree error"); |
1092 | add_sched_out_event(atoms, 'R', timestamp); | 1092 | add_sched_out_event(atoms, 'R', timestamp); |
1093 | } | 1093 | } |
1094 | 1094 | ||
1095 | add_runtime_event(atoms, runtime_event->runtime, timestamp); | 1095 | add_runtime_event(atoms, runtime_event->runtime, timestamp); |
1096 | } | 1096 | } |
1097 | 1097 | ||
1098 | static void | 1098 | static void |
1099 | latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | 1099 | latency_wakeup_event(struct trace_wakeup_event *wakeup_event, |
1100 | struct perf_session *session, | 1100 | struct perf_session *session, |
1101 | struct event *__event __used, | 1101 | struct event *__event __used, |
1102 | int cpu __used, | 1102 | int cpu __used, |
1103 | u64 timestamp, | 1103 | u64 timestamp, |
1104 | struct thread *thread __used) | 1104 | struct thread *thread __used) |
1105 | { | 1105 | { |
1106 | struct work_atoms *atoms; | 1106 | struct work_atoms *atoms; |
1107 | struct work_atom *atom; | 1107 | struct work_atom *atom; |
1108 | struct thread *wakee; | 1108 | struct thread *wakee; |
1109 | 1109 | ||
1110 | /* Note for later, it may be interesting to observe the failing cases */ | 1110 | /* Note for later, it may be interesting to observe the failing cases */ |
1111 | if (!wakeup_event->success) | 1111 | if (!wakeup_event->success) |
1112 | return; | 1112 | return; |
1113 | 1113 | ||
1114 | wakee = perf_session__findnew(session, wakeup_event->pid); | 1114 | wakee = perf_session__findnew(session, wakeup_event->pid); |
1115 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); | 1115 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); |
1116 | if (!atoms) { | 1116 | if (!atoms) { |
1117 | thread_atoms_insert(wakee); | 1117 | thread_atoms_insert(wakee); |
1118 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); | 1118 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); |
1119 | if (!atoms) | 1119 | if (!atoms) |
1120 | die("wakeup-event: Internal tree error"); | 1120 | die("wakeup-event: Internal tree error"); |
1121 | add_sched_out_event(atoms, 'S', timestamp); | 1121 | add_sched_out_event(atoms, 'S', timestamp); |
1122 | } | 1122 | } |
1123 | 1123 | ||
1124 | BUG_ON(list_empty(&atoms->work_list)); | 1124 | BUG_ON(list_empty(&atoms->work_list)); |
1125 | 1125 | ||
1126 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); | 1126 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); |
1127 | 1127 | ||
1128 | /* | 1128 | /* |
1129 | * You WILL be missing events if you've recorded only | 1129 | * You WILL be missing events if you've recorded only |
1130 | * one CPU, or are only looking at only one, so don't | 1130 | * one CPU, or are only looking at only one, so don't |
1131 | * make useless noise. | 1131 | * make useless noise. |
1132 | */ | 1132 | */ |
1133 | if (profile_cpu == -1 && atom->state != THREAD_SLEEPING) | 1133 | if (profile_cpu == -1 && atom->state != THREAD_SLEEPING) |
1134 | nr_state_machine_bugs++; | 1134 | nr_state_machine_bugs++; |
1135 | 1135 | ||
1136 | nr_timestamps++; | 1136 | nr_timestamps++; |
1137 | if (atom->sched_out_time > timestamp) { | 1137 | if (atom->sched_out_time > timestamp) { |
1138 | nr_unordered_timestamps++; | 1138 | nr_unordered_timestamps++; |
1139 | return; | 1139 | return; |
1140 | } | 1140 | } |
1141 | 1141 | ||
1142 | atom->state = THREAD_WAIT_CPU; | 1142 | atom->state = THREAD_WAIT_CPU; |
1143 | atom->wake_up_time = timestamp; | 1143 | atom->wake_up_time = timestamp; |
1144 | } | 1144 | } |
1145 | 1145 | ||
1146 | static void | 1146 | static void |
1147 | latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, | 1147 | latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, |
1148 | struct perf_session *session, | 1148 | struct perf_session *session, |
1149 | struct event *__event __used, | 1149 | struct event *__event __used, |
1150 | int cpu __used, | 1150 | int cpu __used, |
1151 | u64 timestamp, | 1151 | u64 timestamp, |
1152 | struct thread *thread __used) | 1152 | struct thread *thread __used) |
1153 | { | 1153 | { |
1154 | struct work_atoms *atoms; | 1154 | struct work_atoms *atoms; |
1155 | struct work_atom *atom; | 1155 | struct work_atom *atom; |
1156 | struct thread *migrant; | 1156 | struct thread *migrant; |
1157 | 1157 | ||
1158 | /* | 1158 | /* |
1159 | * Only need to worry about migration when profiling one CPU. | 1159 | * Only need to worry about migration when profiling one CPU. |
1160 | */ | 1160 | */ |
1161 | if (profile_cpu == -1) | 1161 | if (profile_cpu == -1) |
1162 | return; | 1162 | return; |
1163 | 1163 | ||
1164 | migrant = perf_session__findnew(session, migrate_task_event->pid); | 1164 | migrant = perf_session__findnew(session, migrate_task_event->pid); |
1165 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); | 1165 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); |
1166 | if (!atoms) { | 1166 | if (!atoms) { |
1167 | thread_atoms_insert(migrant); | 1167 | thread_atoms_insert(migrant); |
1168 | register_pid(migrant->pid, migrant->comm); | 1168 | register_pid(migrant->pid, migrant->comm); |
1169 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); | 1169 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); |
1170 | if (!atoms) | 1170 | if (!atoms) |
1171 | die("migration-event: Internal tree error"); | 1171 | die("migration-event: Internal tree error"); |
1172 | add_sched_out_event(atoms, 'R', timestamp); | 1172 | add_sched_out_event(atoms, 'R', timestamp); |
1173 | } | 1173 | } |
1174 | 1174 | ||
1175 | BUG_ON(list_empty(&atoms->work_list)); | 1175 | BUG_ON(list_empty(&atoms->work_list)); |
1176 | 1176 | ||
1177 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); | 1177 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); |
1178 | atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp; | 1178 | atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp; |
1179 | 1179 | ||
1180 | nr_timestamps++; | 1180 | nr_timestamps++; |
1181 | 1181 | ||
1182 | if (atom->sched_out_time > timestamp) | 1182 | if (atom->sched_out_time > timestamp) |
1183 | nr_unordered_timestamps++; | 1183 | nr_unordered_timestamps++; |
1184 | } | 1184 | } |
1185 | 1185 | ||
1186 | static struct trace_sched_handler lat_ops = { | 1186 | static struct trace_sched_handler lat_ops = { |
1187 | .wakeup_event = latency_wakeup_event, | 1187 | .wakeup_event = latency_wakeup_event, |
1188 | .switch_event = latency_switch_event, | 1188 | .switch_event = latency_switch_event, |
1189 | .runtime_event = latency_runtime_event, | 1189 | .runtime_event = latency_runtime_event, |
1190 | .fork_event = latency_fork_event, | 1190 | .fork_event = latency_fork_event, |
1191 | .migrate_task_event = latency_migrate_task_event, | 1191 | .migrate_task_event = latency_migrate_task_event, |
1192 | }; | 1192 | }; |
1193 | 1193 | ||
1194 | static void output_lat_thread(struct work_atoms *work_list) | 1194 | static void output_lat_thread(struct work_atoms *work_list) |
1195 | { | 1195 | { |
1196 | int i; | 1196 | int i; |
1197 | int ret; | 1197 | int ret; |
1198 | u64 avg; | 1198 | u64 avg; |
1199 | 1199 | ||
1200 | if (!work_list->nb_atoms) | 1200 | if (!work_list->nb_atoms) |
1201 | return; | 1201 | return; |
1202 | /* | 1202 | /* |
1203 | * Ignore idle threads: | 1203 | * Ignore idle threads: |
1204 | */ | 1204 | */ |
1205 | if (!strcmp(work_list->thread->comm, "swapper")) | 1205 | if (!strcmp(work_list->thread->comm, "swapper")) |
1206 | return; | 1206 | return; |
1207 | 1207 | ||
1208 | all_runtime += work_list->total_runtime; | 1208 | all_runtime += work_list->total_runtime; |
1209 | all_count += work_list->nb_atoms; | 1209 | all_count += work_list->nb_atoms; |
1210 | 1210 | ||
1211 | ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid); | 1211 | ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid); |
1212 | 1212 | ||
1213 | for (i = 0; i < 24 - ret; i++) | 1213 | for (i = 0; i < 24 - ret; i++) |
1214 | printf(" "); | 1214 | printf(" "); |
1215 | 1215 | ||
1216 | avg = work_list->total_lat / work_list->nb_atoms; | 1216 | avg = work_list->total_lat / work_list->nb_atoms; |
1217 | 1217 | ||
1218 | printf("|%11.3f ms |%9" PRIu64 " | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n", | 1218 | printf("|%11.3f ms |%9" PRIu64 " | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n", |
1219 | (double)work_list->total_runtime / 1e6, | 1219 | (double)work_list->total_runtime / 1e6, |
1220 | work_list->nb_atoms, (double)avg / 1e6, | 1220 | work_list->nb_atoms, (double)avg / 1e6, |
1221 | (double)work_list->max_lat / 1e6, | 1221 | (double)work_list->max_lat / 1e6, |
1222 | (double)work_list->max_lat_at / 1e9); | 1222 | (double)work_list->max_lat_at / 1e9); |
1223 | } | 1223 | } |
1224 | 1224 | ||
1225 | static int pid_cmp(struct work_atoms *l, struct work_atoms *r) | 1225 | static int pid_cmp(struct work_atoms *l, struct work_atoms *r) |
1226 | { | 1226 | { |
1227 | if (l->thread->pid < r->thread->pid) | 1227 | if (l->thread->pid < r->thread->pid) |
1228 | return -1; | 1228 | return -1; |
1229 | if (l->thread->pid > r->thread->pid) | 1229 | if (l->thread->pid > r->thread->pid) |
1230 | return 1; | 1230 | return 1; |
1231 | 1231 | ||
1232 | return 0; | 1232 | return 0; |
1233 | } | 1233 | } |
1234 | 1234 | ||
1235 | static struct sort_dimension pid_sort_dimension = { | 1235 | static struct sort_dimension pid_sort_dimension = { |
1236 | .name = "pid", | 1236 | .name = "pid", |
1237 | .cmp = pid_cmp, | 1237 | .cmp = pid_cmp, |
1238 | }; | 1238 | }; |
1239 | 1239 | ||
1240 | static int avg_cmp(struct work_atoms *l, struct work_atoms *r) | 1240 | static int avg_cmp(struct work_atoms *l, struct work_atoms *r) |
1241 | { | 1241 | { |
1242 | u64 avgl, avgr; | 1242 | u64 avgl, avgr; |
1243 | 1243 | ||
1244 | if (!l->nb_atoms) | 1244 | if (!l->nb_atoms) |
1245 | return -1; | 1245 | return -1; |
1246 | 1246 | ||
1247 | if (!r->nb_atoms) | 1247 | if (!r->nb_atoms) |
1248 | return 1; | 1248 | return 1; |
1249 | 1249 | ||
1250 | avgl = l->total_lat / l->nb_atoms; | 1250 | avgl = l->total_lat / l->nb_atoms; |
1251 | avgr = r->total_lat / r->nb_atoms; | 1251 | avgr = r->total_lat / r->nb_atoms; |
1252 | 1252 | ||
1253 | if (avgl < avgr) | 1253 | if (avgl < avgr) |
1254 | return -1; | 1254 | return -1; |
1255 | if (avgl > avgr) | 1255 | if (avgl > avgr) |
1256 | return 1; | 1256 | return 1; |
1257 | 1257 | ||
1258 | return 0; | 1258 | return 0; |
1259 | } | 1259 | } |
1260 | 1260 | ||
1261 | static struct sort_dimension avg_sort_dimension = { | 1261 | static struct sort_dimension avg_sort_dimension = { |
1262 | .name = "avg", | 1262 | .name = "avg", |
1263 | .cmp = avg_cmp, | 1263 | .cmp = avg_cmp, |
1264 | }; | 1264 | }; |
1265 | 1265 | ||
1266 | static int max_cmp(struct work_atoms *l, struct work_atoms *r) | 1266 | static int max_cmp(struct work_atoms *l, struct work_atoms *r) |
1267 | { | 1267 | { |
1268 | if (l->max_lat < r->max_lat) | 1268 | if (l->max_lat < r->max_lat) |
1269 | return -1; | 1269 | return -1; |
1270 | if (l->max_lat > r->max_lat) | 1270 | if (l->max_lat > r->max_lat) |
1271 | return 1; | 1271 | return 1; |
1272 | 1272 | ||
1273 | return 0; | 1273 | return 0; |
1274 | } | 1274 | } |
1275 | 1275 | ||
1276 | static struct sort_dimension max_sort_dimension = { | 1276 | static struct sort_dimension max_sort_dimension = { |
1277 | .name = "max", | 1277 | .name = "max", |
1278 | .cmp = max_cmp, | 1278 | .cmp = max_cmp, |
1279 | }; | 1279 | }; |
1280 | 1280 | ||
1281 | static int switch_cmp(struct work_atoms *l, struct work_atoms *r) | 1281 | static int switch_cmp(struct work_atoms *l, struct work_atoms *r) |
1282 | { | 1282 | { |
1283 | if (l->nb_atoms < r->nb_atoms) | 1283 | if (l->nb_atoms < r->nb_atoms) |
1284 | return -1; | 1284 | return -1; |
1285 | if (l->nb_atoms > r->nb_atoms) | 1285 | if (l->nb_atoms > r->nb_atoms) |
1286 | return 1; | 1286 | return 1; |
1287 | 1287 | ||
1288 | return 0; | 1288 | return 0; |
1289 | } | 1289 | } |
1290 | 1290 | ||
1291 | static struct sort_dimension switch_sort_dimension = { | 1291 | static struct sort_dimension switch_sort_dimension = { |
1292 | .name = "switch", | 1292 | .name = "switch", |
1293 | .cmp = switch_cmp, | 1293 | .cmp = switch_cmp, |
1294 | }; | 1294 | }; |
1295 | 1295 | ||
1296 | static int runtime_cmp(struct work_atoms *l, struct work_atoms *r) | 1296 | static int runtime_cmp(struct work_atoms *l, struct work_atoms *r) |
1297 | { | 1297 | { |
1298 | if (l->total_runtime < r->total_runtime) | 1298 | if (l->total_runtime < r->total_runtime) |
1299 | return -1; | 1299 | return -1; |
1300 | if (l->total_runtime > r->total_runtime) | 1300 | if (l->total_runtime > r->total_runtime) |
1301 | return 1; | 1301 | return 1; |
1302 | 1302 | ||
1303 | return 0; | 1303 | return 0; |
1304 | } | 1304 | } |
1305 | 1305 | ||
1306 | static struct sort_dimension runtime_sort_dimension = { | 1306 | static struct sort_dimension runtime_sort_dimension = { |
1307 | .name = "runtime", | 1307 | .name = "runtime", |
1308 | .cmp = runtime_cmp, | 1308 | .cmp = runtime_cmp, |
1309 | }; | 1309 | }; |
1310 | 1310 | ||
1311 | static struct sort_dimension *available_sorts[] = { | 1311 | static struct sort_dimension *available_sorts[] = { |
1312 | &pid_sort_dimension, | 1312 | &pid_sort_dimension, |
1313 | &avg_sort_dimension, | 1313 | &avg_sort_dimension, |
1314 | &max_sort_dimension, | 1314 | &max_sort_dimension, |
1315 | &switch_sort_dimension, | 1315 | &switch_sort_dimension, |
1316 | &runtime_sort_dimension, | 1316 | &runtime_sort_dimension, |
1317 | }; | 1317 | }; |
1318 | 1318 | ||
1319 | #define NB_AVAILABLE_SORTS (int)(sizeof(available_sorts) / sizeof(struct sort_dimension *)) | 1319 | #define NB_AVAILABLE_SORTS (int)(sizeof(available_sorts) / sizeof(struct sort_dimension *)) |
1320 | 1320 | ||
1321 | static LIST_HEAD(sort_list); | 1321 | static LIST_HEAD(sort_list); |
1322 | 1322 | ||
1323 | static int sort_dimension__add(const char *tok, struct list_head *list) | 1323 | static int sort_dimension__add(const char *tok, struct list_head *list) |
1324 | { | 1324 | { |
1325 | int i; | 1325 | int i; |
1326 | 1326 | ||
1327 | for (i = 0; i < NB_AVAILABLE_SORTS; i++) { | 1327 | for (i = 0; i < NB_AVAILABLE_SORTS; i++) { |
1328 | if (!strcmp(available_sorts[i]->name, tok)) { | 1328 | if (!strcmp(available_sorts[i]->name, tok)) { |
1329 | list_add_tail(&available_sorts[i]->list, list); | 1329 | list_add_tail(&available_sorts[i]->list, list); |
1330 | 1330 | ||
1331 | return 0; | 1331 | return 0; |
1332 | } | 1332 | } |
1333 | } | 1333 | } |
1334 | 1334 | ||
1335 | return -1; | 1335 | return -1; |
1336 | } | 1336 | } |
1337 | 1337 | ||
1338 | static void setup_sorting(void); | 1338 | static void setup_sorting(void); |
1339 | 1339 | ||
1340 | static void sort_lat(void) | 1340 | static void sort_lat(void) |
1341 | { | 1341 | { |
1342 | struct rb_node *node; | 1342 | struct rb_node *node; |
1343 | 1343 | ||
1344 | for (;;) { | 1344 | for (;;) { |
1345 | struct work_atoms *data; | 1345 | struct work_atoms *data; |
1346 | node = rb_first(&atom_root); | 1346 | node = rb_first(&atom_root); |
1347 | if (!node) | 1347 | if (!node) |
1348 | break; | 1348 | break; |
1349 | 1349 | ||
1350 | rb_erase(node, &atom_root); | 1350 | rb_erase(node, &atom_root); |
1351 | data = rb_entry(node, struct work_atoms, node); | 1351 | data = rb_entry(node, struct work_atoms, node); |
1352 | __thread_latency_insert(&sorted_atom_root, data, &sort_list); | 1352 | __thread_latency_insert(&sorted_atom_root, data, &sort_list); |
1353 | } | 1353 | } |
1354 | } | 1354 | } |
1355 | 1355 | ||
1356 | static struct trace_sched_handler *trace_handler; | 1356 | static struct trace_sched_handler *trace_handler; |
1357 | 1357 | ||
1358 | static void | 1358 | static void |
1359 | process_sched_wakeup_event(void *data, struct perf_session *session, | 1359 | process_sched_wakeup_event(void *data, struct perf_session *session, |
1360 | struct event *event, | 1360 | struct event *event, |
1361 | int cpu __used, | 1361 | int cpu __used, |
1362 | u64 timestamp __used, | 1362 | u64 timestamp __used, |
1363 | struct thread *thread __used) | 1363 | struct thread *thread __used) |
1364 | { | 1364 | { |
1365 | struct trace_wakeup_event wakeup_event; | 1365 | struct trace_wakeup_event wakeup_event; |
1366 | 1366 | ||
1367 | FILL_COMMON_FIELDS(wakeup_event, event, data); | 1367 | FILL_COMMON_FIELDS(wakeup_event, event, data); |
1368 | 1368 | ||
1369 | FILL_ARRAY(wakeup_event, comm, event, data); | 1369 | FILL_ARRAY(wakeup_event, comm, event, data); |
1370 | FILL_FIELD(wakeup_event, pid, event, data); | 1370 | FILL_FIELD(wakeup_event, pid, event, data); |
1371 | FILL_FIELD(wakeup_event, prio, event, data); | 1371 | FILL_FIELD(wakeup_event, prio, event, data); |
1372 | FILL_FIELD(wakeup_event, success, event, data); | 1372 | FILL_FIELD(wakeup_event, success, event, data); |
1373 | FILL_FIELD(wakeup_event, cpu, event, data); | 1373 | FILL_FIELD(wakeup_event, cpu, event, data); |
1374 | 1374 | ||
1375 | if (trace_handler->wakeup_event) | 1375 | if (trace_handler->wakeup_event) |
1376 | trace_handler->wakeup_event(&wakeup_event, session, event, | 1376 | trace_handler->wakeup_event(&wakeup_event, session, event, |
1377 | cpu, timestamp, thread); | 1377 | cpu, timestamp, thread); |
1378 | } | 1378 | } |
1379 | 1379 | ||
1380 | /* | 1380 | /* |
1381 | * Track the current task - that way we can know whether there's any | 1381 | * Track the current task - that way we can know whether there's any |
1382 | * weird events, such as a task being switched away that is not current. | 1382 | * weird events, such as a task being switched away that is not current. |
1383 | */ | 1383 | */ |
1384 | static int max_cpu; | 1384 | static int max_cpu; |
1385 | 1385 | ||
1386 | static u32 curr_pid[MAX_CPUS] = { [0 ... MAX_CPUS-1] = -1 }; | 1386 | static u32 curr_pid[MAX_CPUS] = { [0 ... MAX_CPUS-1] = -1 }; |
1387 | 1387 | ||
1388 | static struct thread *curr_thread[MAX_CPUS]; | 1388 | static struct thread *curr_thread[MAX_CPUS]; |
1389 | 1389 | ||
1390 | static char next_shortname1 = 'A'; | 1390 | static char next_shortname1 = 'A'; |
1391 | static char next_shortname2 = '0'; | 1391 | static char next_shortname2 = '0'; |
1392 | 1392 | ||
1393 | static void | 1393 | static void |
1394 | map_switch_event(struct trace_switch_event *switch_event, | 1394 | map_switch_event(struct trace_switch_event *switch_event, |
1395 | struct perf_session *session, | 1395 | struct perf_session *session, |
1396 | struct event *event __used, | 1396 | struct event *event __used, |
1397 | int this_cpu, | 1397 | int this_cpu, |
1398 | u64 timestamp, | 1398 | u64 timestamp, |
1399 | struct thread *thread __used) | 1399 | struct thread *thread __used) |
1400 | { | 1400 | { |
1401 | struct thread *sched_out __used, *sched_in; | 1401 | struct thread *sched_out __used, *sched_in; |
1402 | int new_shortname; | 1402 | int new_shortname; |
1403 | u64 timestamp0; | 1403 | u64 timestamp0; |
1404 | s64 delta; | 1404 | s64 delta; |
1405 | int cpu; | 1405 | int cpu; |
1406 | 1406 | ||
1407 | BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0); | 1407 | BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0); |
1408 | 1408 | ||
1409 | if (this_cpu > max_cpu) | 1409 | if (this_cpu > max_cpu) |
1410 | max_cpu = this_cpu; | 1410 | max_cpu = this_cpu; |
1411 | 1411 | ||
1412 | timestamp0 = cpu_last_switched[this_cpu]; | 1412 | timestamp0 = cpu_last_switched[this_cpu]; |
1413 | cpu_last_switched[this_cpu] = timestamp; | 1413 | cpu_last_switched[this_cpu] = timestamp; |
1414 | if (timestamp0) | 1414 | if (timestamp0) |
1415 | delta = timestamp - timestamp0; | 1415 | delta = timestamp - timestamp0; |
1416 | else | 1416 | else |
1417 | delta = 0; | 1417 | delta = 0; |
1418 | 1418 | ||
1419 | if (delta < 0) | 1419 | if (delta < 0) |
1420 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); | 1420 | die("hm, delta: %" PRIu64 " < 0 ?\n", delta); |
1421 | 1421 | ||
1422 | 1422 | ||
1423 | sched_out = perf_session__findnew(session, switch_event->prev_pid); | 1423 | sched_out = perf_session__findnew(session, switch_event->prev_pid); |
1424 | sched_in = perf_session__findnew(session, switch_event->next_pid); | 1424 | sched_in = perf_session__findnew(session, switch_event->next_pid); |
1425 | 1425 | ||
1426 | curr_thread[this_cpu] = sched_in; | 1426 | curr_thread[this_cpu] = sched_in; |
1427 | 1427 | ||
1428 | printf(" "); | 1428 | printf(" "); |
1429 | 1429 | ||
1430 | new_shortname = 0; | 1430 | new_shortname = 0; |
1431 | if (!sched_in->shortname[0]) { | 1431 | if (!sched_in->shortname[0]) { |
1432 | sched_in->shortname[0] = next_shortname1; | 1432 | sched_in->shortname[0] = next_shortname1; |
1433 | sched_in->shortname[1] = next_shortname2; | 1433 | sched_in->shortname[1] = next_shortname2; |
1434 | 1434 | ||
1435 | if (next_shortname1 < 'Z') { | 1435 | if (next_shortname1 < 'Z') { |
1436 | next_shortname1++; | 1436 | next_shortname1++; |
1437 | } else { | 1437 | } else { |
1438 | next_shortname1='A'; | 1438 | next_shortname1='A'; |
1439 | if (next_shortname2 < '9') { | 1439 | if (next_shortname2 < '9') { |
1440 | next_shortname2++; | 1440 | next_shortname2++; |
1441 | } else { | 1441 | } else { |
1442 | next_shortname2='0'; | 1442 | next_shortname2='0'; |
1443 | } | 1443 | } |
1444 | } | 1444 | } |
1445 | new_shortname = 1; | 1445 | new_shortname = 1; |
1446 | } | 1446 | } |
1447 | 1447 | ||
1448 | for (cpu = 0; cpu <= max_cpu; cpu++) { | 1448 | for (cpu = 0; cpu <= max_cpu; cpu++) { |
1449 | if (cpu != this_cpu) | 1449 | if (cpu != this_cpu) |
1450 | printf(" "); | 1450 | printf(" "); |
1451 | else | 1451 | else |
1452 | printf("*"); | 1452 | printf("*"); |
1453 | 1453 | ||
1454 | if (curr_thread[cpu]) { | 1454 | if (curr_thread[cpu]) { |
1455 | if (curr_thread[cpu]->pid) | 1455 | if (curr_thread[cpu]->pid) |
1456 | printf("%2s ", curr_thread[cpu]->shortname); | 1456 | printf("%2s ", curr_thread[cpu]->shortname); |
1457 | else | 1457 | else |
1458 | printf(". "); | 1458 | printf(". "); |
1459 | } else | 1459 | } else |
1460 | printf(" "); | 1460 | printf(" "); |
1461 | } | 1461 | } |
1462 | 1462 | ||
1463 | printf(" %12.6f secs ", (double)timestamp/1e9); | 1463 | printf(" %12.6f secs ", (double)timestamp/1e9); |
1464 | if (new_shortname) { | 1464 | if (new_shortname) { |
1465 | printf("%s => %s:%d\n", | 1465 | printf("%s => %s:%d\n", |
1466 | sched_in->shortname, sched_in->comm, sched_in->pid); | 1466 | sched_in->shortname, sched_in->comm, sched_in->pid); |
1467 | } else { | 1467 | } else { |
1468 | printf("\n"); | 1468 | printf("\n"); |
1469 | } | 1469 | } |
1470 | } | 1470 | } |
1471 | 1471 | ||
1472 | 1472 | ||
1473 | static void | 1473 | static void |
1474 | process_sched_switch_event(void *data, struct perf_session *session, | 1474 | process_sched_switch_event(void *data, struct perf_session *session, |
1475 | struct event *event, | 1475 | struct event *event, |
1476 | int this_cpu, | 1476 | int this_cpu, |
1477 | u64 timestamp __used, | 1477 | u64 timestamp __used, |
1478 | struct thread *thread __used) | 1478 | struct thread *thread __used) |
1479 | { | 1479 | { |
1480 | struct trace_switch_event switch_event; | 1480 | struct trace_switch_event switch_event; |
1481 | 1481 | ||
1482 | FILL_COMMON_FIELDS(switch_event, event, data); | 1482 | FILL_COMMON_FIELDS(switch_event, event, data); |
1483 | 1483 | ||
1484 | FILL_ARRAY(switch_event, prev_comm, event, data); | 1484 | FILL_ARRAY(switch_event, prev_comm, event, data); |
1485 | FILL_FIELD(switch_event, prev_pid, event, data); | 1485 | FILL_FIELD(switch_event, prev_pid, event, data); |
1486 | FILL_FIELD(switch_event, prev_prio, event, data); | 1486 | FILL_FIELD(switch_event, prev_prio, event, data); |
1487 | FILL_FIELD(switch_event, prev_state, event, data); | 1487 | FILL_FIELD(switch_event, prev_state, event, data); |
1488 | FILL_ARRAY(switch_event, next_comm, event, data); | 1488 | FILL_ARRAY(switch_event, next_comm, event, data); |
1489 | FILL_FIELD(switch_event, next_pid, event, data); | 1489 | FILL_FIELD(switch_event, next_pid, event, data); |
1490 | FILL_FIELD(switch_event, next_prio, event, data); | 1490 | FILL_FIELD(switch_event, next_prio, event, data); |
1491 | 1491 | ||
1492 | if (curr_pid[this_cpu] != (u32)-1) { | 1492 | if (curr_pid[this_cpu] != (u32)-1) { |
1493 | /* | 1493 | /* |
1494 | * Are we trying to switch away a PID that is | 1494 | * Are we trying to switch away a PID that is |
1495 | * not current? | 1495 | * not current? |
1496 | */ | 1496 | */ |
1497 | if (curr_pid[this_cpu] != switch_event.prev_pid) | 1497 | if (curr_pid[this_cpu] != switch_event.prev_pid) |
1498 | nr_context_switch_bugs++; | 1498 | nr_context_switch_bugs++; |
1499 | } | 1499 | } |
1500 | if (trace_handler->switch_event) | 1500 | if (trace_handler->switch_event) |
1501 | trace_handler->switch_event(&switch_event, session, event, | 1501 | trace_handler->switch_event(&switch_event, session, event, |
1502 | this_cpu, timestamp, thread); | 1502 | this_cpu, timestamp, thread); |
1503 | 1503 | ||
1504 | curr_pid[this_cpu] = switch_event.next_pid; | 1504 | curr_pid[this_cpu] = switch_event.next_pid; |
1505 | } | 1505 | } |
1506 | 1506 | ||
1507 | static void | 1507 | static void |
1508 | process_sched_runtime_event(void *data, struct perf_session *session, | 1508 | process_sched_runtime_event(void *data, struct perf_session *session, |
1509 | struct event *event, | 1509 | struct event *event, |
1510 | int cpu __used, | 1510 | int cpu __used, |
1511 | u64 timestamp __used, | 1511 | u64 timestamp __used, |
1512 | struct thread *thread __used) | 1512 | struct thread *thread __used) |
1513 | { | 1513 | { |
1514 | struct trace_runtime_event runtime_event; | 1514 | struct trace_runtime_event runtime_event; |
1515 | 1515 | ||
1516 | FILL_ARRAY(runtime_event, comm, event, data); | 1516 | FILL_ARRAY(runtime_event, comm, event, data); |
1517 | FILL_FIELD(runtime_event, pid, event, data); | 1517 | FILL_FIELD(runtime_event, pid, event, data); |
1518 | FILL_FIELD(runtime_event, runtime, event, data); | 1518 | FILL_FIELD(runtime_event, runtime, event, data); |
1519 | FILL_FIELD(runtime_event, vruntime, event, data); | 1519 | FILL_FIELD(runtime_event, vruntime, event, data); |
1520 | 1520 | ||
1521 | if (trace_handler->runtime_event) | 1521 | if (trace_handler->runtime_event) |
1522 | trace_handler->runtime_event(&runtime_event, session, event, cpu, timestamp, thread); | 1522 | trace_handler->runtime_event(&runtime_event, session, event, cpu, timestamp, thread); |
1523 | } | 1523 | } |
1524 | 1524 | ||
1525 | static void | 1525 | static void |
1526 | process_sched_fork_event(void *data, | 1526 | process_sched_fork_event(void *data, |
1527 | struct event *event, | 1527 | struct event *event, |
1528 | int cpu __used, | 1528 | int cpu __used, |
1529 | u64 timestamp __used, | 1529 | u64 timestamp __used, |
1530 | struct thread *thread __used) | 1530 | struct thread *thread __used) |
1531 | { | 1531 | { |
1532 | struct trace_fork_event fork_event; | 1532 | struct trace_fork_event fork_event; |
1533 | 1533 | ||
1534 | FILL_COMMON_FIELDS(fork_event, event, data); | 1534 | FILL_COMMON_FIELDS(fork_event, event, data); |
1535 | 1535 | ||
1536 | FILL_ARRAY(fork_event, parent_comm, event, data); | 1536 | FILL_ARRAY(fork_event, parent_comm, event, data); |
1537 | FILL_FIELD(fork_event, parent_pid, event, data); | 1537 | FILL_FIELD(fork_event, parent_pid, event, data); |
1538 | FILL_ARRAY(fork_event, child_comm, event, data); | 1538 | FILL_ARRAY(fork_event, child_comm, event, data); |
1539 | FILL_FIELD(fork_event, child_pid, event, data); | 1539 | FILL_FIELD(fork_event, child_pid, event, data); |
1540 | 1540 | ||
1541 | if (trace_handler->fork_event) | 1541 | if (trace_handler->fork_event) |
1542 | trace_handler->fork_event(&fork_event, event, | 1542 | trace_handler->fork_event(&fork_event, event, |
1543 | cpu, timestamp, thread); | 1543 | cpu, timestamp, thread); |
1544 | } | 1544 | } |
1545 | 1545 | ||
1546 | static void | 1546 | static void |
1547 | process_sched_exit_event(struct event *event, | 1547 | process_sched_exit_event(struct event *event, |
1548 | int cpu __used, | 1548 | int cpu __used, |
1549 | u64 timestamp __used, | 1549 | u64 timestamp __used, |
1550 | struct thread *thread __used) | 1550 | struct thread *thread __used) |
1551 | { | 1551 | { |
1552 | if (verbose) | 1552 | if (verbose) |
1553 | printf("sched_exit event %p\n", event); | 1553 | printf("sched_exit event %p\n", event); |
1554 | } | 1554 | } |
1555 | 1555 | ||
1556 | static void | 1556 | static void |
1557 | process_sched_migrate_task_event(void *data, struct perf_session *session, | 1557 | process_sched_migrate_task_event(void *data, struct perf_session *session, |
1558 | struct event *event, | 1558 | struct event *event, |
1559 | int cpu __used, | 1559 | int cpu __used, |
1560 | u64 timestamp __used, | 1560 | u64 timestamp __used, |
1561 | struct thread *thread __used) | 1561 | struct thread *thread __used) |
1562 | { | 1562 | { |
1563 | struct trace_migrate_task_event migrate_task_event; | 1563 | struct trace_migrate_task_event migrate_task_event; |
1564 | 1564 | ||
1565 | FILL_COMMON_FIELDS(migrate_task_event, event, data); | 1565 | FILL_COMMON_FIELDS(migrate_task_event, event, data); |
1566 | 1566 | ||
1567 | FILL_ARRAY(migrate_task_event, comm, event, data); | 1567 | FILL_ARRAY(migrate_task_event, comm, event, data); |
1568 | FILL_FIELD(migrate_task_event, pid, event, data); | 1568 | FILL_FIELD(migrate_task_event, pid, event, data); |
1569 | FILL_FIELD(migrate_task_event, prio, event, data); | 1569 | FILL_FIELD(migrate_task_event, prio, event, data); |
1570 | FILL_FIELD(migrate_task_event, cpu, event, data); | 1570 | FILL_FIELD(migrate_task_event, cpu, event, data); |
1571 | 1571 | ||
1572 | if (trace_handler->migrate_task_event) | 1572 | if (trace_handler->migrate_task_event) |
1573 | trace_handler->migrate_task_event(&migrate_task_event, session, | 1573 | trace_handler->migrate_task_event(&migrate_task_event, session, |
1574 | event, cpu, timestamp, thread); | 1574 | event, cpu, timestamp, thread); |
1575 | } | 1575 | } |
1576 | 1576 | ||
1577 | static void process_raw_event(union perf_event *raw_event __used, | 1577 | static void process_raw_event(union perf_event *raw_event __used, |
1578 | struct perf_session *session, void *data, int cpu, | 1578 | struct perf_session *session, void *data, int cpu, |
1579 | u64 timestamp, struct thread *thread) | 1579 | u64 timestamp, struct thread *thread) |
1580 | { | 1580 | { |
1581 | struct event *event; | 1581 | struct event *event; |
1582 | int type; | 1582 | int type; |
1583 | 1583 | ||
1584 | 1584 | ||
1585 | type = trace_parse_common_type(data); | 1585 | type = trace_parse_common_type(data); |
1586 | event = trace_find_event(type); | 1586 | event = trace_find_event(type); |
1587 | 1587 | ||
1588 | if (!strcmp(event->name, "sched_switch")) | 1588 | if (!strcmp(event->name, "sched_switch")) |
1589 | process_sched_switch_event(data, session, event, cpu, timestamp, thread); | 1589 | process_sched_switch_event(data, session, event, cpu, timestamp, thread); |
1590 | if (!strcmp(event->name, "sched_stat_runtime")) | 1590 | if (!strcmp(event->name, "sched_stat_runtime")) |
1591 | process_sched_runtime_event(data, session, event, cpu, timestamp, thread); | 1591 | process_sched_runtime_event(data, session, event, cpu, timestamp, thread); |
1592 | if (!strcmp(event->name, "sched_wakeup")) | 1592 | if (!strcmp(event->name, "sched_wakeup")) |
1593 | process_sched_wakeup_event(data, session, event, cpu, timestamp, thread); | 1593 | process_sched_wakeup_event(data, session, event, cpu, timestamp, thread); |
1594 | if (!strcmp(event->name, "sched_wakeup_new")) | 1594 | if (!strcmp(event->name, "sched_wakeup_new")) |
1595 | process_sched_wakeup_event(data, session, event, cpu, timestamp, thread); | 1595 | process_sched_wakeup_event(data, session, event, cpu, timestamp, thread); |
1596 | if (!strcmp(event->name, "sched_process_fork")) | 1596 | if (!strcmp(event->name, "sched_process_fork")) |
1597 | process_sched_fork_event(data, event, cpu, timestamp, thread); | 1597 | process_sched_fork_event(data, event, cpu, timestamp, thread); |
1598 | if (!strcmp(event->name, "sched_process_exit")) | 1598 | if (!strcmp(event->name, "sched_process_exit")) |
1599 | process_sched_exit_event(event, cpu, timestamp, thread); | 1599 | process_sched_exit_event(event, cpu, timestamp, thread); |
1600 | if (!strcmp(event->name, "sched_migrate_task")) | 1600 | if (!strcmp(event->name, "sched_migrate_task")) |
1601 | process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); | 1601 | process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); |
1602 | } | 1602 | } |
1603 | 1603 | ||
1604 | static int process_sample_event(union perf_event *event, | 1604 | static int process_sample_event(union perf_event *event, |
1605 | struct perf_sample *sample, | 1605 | struct perf_sample *sample, |
1606 | struct perf_evsel *evsel __used, | 1606 | struct perf_evsel *evsel __used, |
1607 | struct perf_session *session) | 1607 | struct perf_session *session) |
1608 | { | 1608 | { |
1609 | struct thread *thread; | 1609 | struct thread *thread; |
1610 | 1610 | ||
1611 | if (!(session->sample_type & PERF_SAMPLE_RAW)) | 1611 | if (!(session->sample_type & PERF_SAMPLE_RAW)) |
1612 | return 0; | 1612 | return 0; |
1613 | 1613 | ||
1614 | thread = perf_session__findnew(session, sample->pid); | 1614 | thread = perf_session__findnew(session, sample->pid); |
1615 | if (thread == NULL) { | 1615 | if (thread == NULL) { |
1616 | pr_debug("problem processing %d event, skipping it.\n", | 1616 | pr_debug("problem processing %d event, skipping it.\n", |
1617 | event->header.type); | 1617 | event->header.type); |
1618 | return -1; | 1618 | return -1; |
1619 | } | 1619 | } |
1620 | 1620 | ||
1621 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 1621 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
1622 | 1622 | ||
1623 | if (profile_cpu != -1 && profile_cpu != (int)sample->cpu) | 1623 | if (profile_cpu != -1 && profile_cpu != (int)sample->cpu) |
1624 | return 0; | 1624 | return 0; |
1625 | 1625 | ||
1626 | process_raw_event(event, session, sample->raw_data, sample->cpu, | 1626 | process_raw_event(event, session, sample->raw_data, sample->cpu, |
1627 | sample->time, thread); | 1627 | sample->time, thread); |
1628 | 1628 | ||
1629 | return 0; | 1629 | return 0; |
1630 | } | 1630 | } |
1631 | 1631 | ||
1632 | static struct perf_event_ops event_ops = { | 1632 | static struct perf_event_ops event_ops = { |
1633 | .sample = process_sample_event, | 1633 | .sample = process_sample_event, |
1634 | .comm = perf_event__process_comm, | 1634 | .comm = perf_event__process_comm, |
1635 | .lost = perf_event__process_lost, | 1635 | .lost = perf_event__process_lost, |
1636 | .fork = perf_event__process_task, | 1636 | .fork = perf_event__process_task, |
1637 | .ordered_samples = true, | 1637 | .ordered_samples = true, |
1638 | }; | 1638 | }; |
1639 | 1639 | ||
1640 | static int read_events(void) | 1640 | static void read_events(bool destroy, struct perf_session **psession) |
1641 | { | 1641 | { |
1642 | int err = -EINVAL; | 1642 | int err = -EINVAL; |
1643 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, | 1643 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, |
1644 | 0, false, &event_ops); | 1644 | 0, false, &event_ops); |
1645 | if (session == NULL) | 1645 | if (session == NULL) |
1646 | return -ENOMEM; | 1646 | die("No Memory"); |
1647 | 1647 | ||
1648 | if (perf_session__has_traces(session, "record -R")) { | 1648 | if (perf_session__has_traces(session, "record -R")) { |
1649 | err = perf_session__process_events(session, &event_ops); | 1649 | err = perf_session__process_events(session, &event_ops); |
1650 | if (err) | ||
1651 | die("Failed to process events, error %d", err); | ||
1652 | |||
1650 | nr_events = session->hists.stats.nr_events[0]; | 1653 | nr_events = session->hists.stats.nr_events[0]; |
1651 | nr_lost_events = session->hists.stats.total_lost; | 1654 | nr_lost_events = session->hists.stats.total_lost; |
1652 | nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; | 1655 | nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; |
1653 | } | 1656 | } |
1654 | 1657 | ||
1655 | perf_session__delete(session); | 1658 | if (destroy) |
1656 | return err; | 1659 | perf_session__delete(session); |
1660 | |||
1661 | if (psession) | ||
1662 | *psession = session; | ||
1657 | } | 1663 | } |
1658 | 1664 | ||
1659 | static void print_bad_events(void) | 1665 | static void print_bad_events(void) |
1660 | { | 1666 | { |
1661 | if (nr_unordered_timestamps && nr_timestamps) { | 1667 | if (nr_unordered_timestamps && nr_timestamps) { |
1662 | printf(" INFO: %.3f%% unordered timestamps (%ld out of %ld)\n", | 1668 | printf(" INFO: %.3f%% unordered timestamps (%ld out of %ld)\n", |
1663 | (double)nr_unordered_timestamps/(double)nr_timestamps*100.0, | 1669 | (double)nr_unordered_timestamps/(double)nr_timestamps*100.0, |
1664 | nr_unordered_timestamps, nr_timestamps); | 1670 | nr_unordered_timestamps, nr_timestamps); |
1665 | } | 1671 | } |
1666 | if (nr_lost_events && nr_events) { | 1672 | if (nr_lost_events && nr_events) { |
1667 | printf(" INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n", | 1673 | printf(" INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n", |
1668 | (double)nr_lost_events/(double)nr_events*100.0, | 1674 | (double)nr_lost_events/(double)nr_events*100.0, |
1669 | nr_lost_events, nr_events, nr_lost_chunks); | 1675 | nr_lost_events, nr_events, nr_lost_chunks); |
1670 | } | 1676 | } |
1671 | if (nr_state_machine_bugs && nr_timestamps) { | 1677 | if (nr_state_machine_bugs && nr_timestamps) { |
1672 | printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)", | 1678 | printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)", |
1673 | (double)nr_state_machine_bugs/(double)nr_timestamps*100.0, | 1679 | (double)nr_state_machine_bugs/(double)nr_timestamps*100.0, |
1674 | nr_state_machine_bugs, nr_timestamps); | 1680 | nr_state_machine_bugs, nr_timestamps); |
1675 | if (nr_lost_events) | 1681 | if (nr_lost_events) |
1676 | printf(" (due to lost events?)"); | 1682 | printf(" (due to lost events?)"); |
1677 | printf("\n"); | 1683 | printf("\n"); |
1678 | } | 1684 | } |
1679 | if (nr_context_switch_bugs && nr_timestamps) { | 1685 | if (nr_context_switch_bugs && nr_timestamps) { |
1680 | printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)", | 1686 | printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)", |
1681 | (double)nr_context_switch_bugs/(double)nr_timestamps*100.0, | 1687 | (double)nr_context_switch_bugs/(double)nr_timestamps*100.0, |
1682 | nr_context_switch_bugs, nr_timestamps); | 1688 | nr_context_switch_bugs, nr_timestamps); |
1683 | if (nr_lost_events) | 1689 | if (nr_lost_events) |
1684 | printf(" (due to lost events?)"); | 1690 | printf(" (due to lost events?)"); |
1685 | printf("\n"); | 1691 | printf("\n"); |
1686 | } | 1692 | } |
1687 | } | 1693 | } |
1688 | 1694 | ||
1689 | static void __cmd_lat(void) | 1695 | static void __cmd_lat(void) |
1690 | { | 1696 | { |
1691 | struct rb_node *next; | 1697 | struct rb_node *next; |
1698 | struct perf_session *session; | ||
1692 | 1699 | ||
1693 | setup_pager(); | 1700 | setup_pager(); |
1694 | read_events(); | 1701 | read_events(false, &session); |
1695 | sort_lat(); | 1702 | sort_lat(); |
1696 | 1703 | ||
1697 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); | 1704 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); |
1698 | printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n"); | 1705 | printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n"); |
1699 | printf(" ---------------------------------------------------------------------------------------------------------------\n"); | 1706 | printf(" ---------------------------------------------------------------------------------------------------------------\n"); |
1700 | 1707 | ||
1701 | next = rb_first(&sorted_atom_root); | 1708 | next = rb_first(&sorted_atom_root); |
1702 | 1709 | ||
1703 | while (next) { | 1710 | while (next) { |
1704 | struct work_atoms *work_list; | 1711 | struct work_atoms *work_list; |
1705 | 1712 | ||
1706 | work_list = rb_entry(next, struct work_atoms, node); | 1713 | work_list = rb_entry(next, struct work_atoms, node); |
1707 | output_lat_thread(work_list); | 1714 | output_lat_thread(work_list); |
1708 | next = rb_next(next); | 1715 | next = rb_next(next); |
1709 | } | 1716 | } |
1710 | 1717 | ||
1711 | printf(" -----------------------------------------------------------------------------------------\n"); | 1718 | printf(" -----------------------------------------------------------------------------------------\n"); |
1712 | printf(" TOTAL: |%11.3f ms |%9" PRIu64 " |\n", | 1719 | printf(" TOTAL: |%11.3f ms |%9" PRIu64 " |\n", |
1713 | (double)all_runtime/1e6, all_count); | 1720 | (double)all_runtime/1e6, all_count); |
1714 | 1721 | ||
1715 | printf(" ---------------------------------------------------\n"); | 1722 | printf(" ---------------------------------------------------\n"); |
1716 | 1723 | ||
1717 | print_bad_events(); | 1724 | print_bad_events(); |
1718 | printf("\n"); | 1725 | printf("\n"); |
1719 | 1726 | ||
1727 | perf_session__delete(session); | ||
1720 | } | 1728 | } |
1721 | 1729 | ||
1722 | static struct trace_sched_handler map_ops = { | 1730 | static struct trace_sched_handler map_ops = { |
1723 | .wakeup_event = NULL, | 1731 | .wakeup_event = NULL, |
1724 | .switch_event = map_switch_event, | 1732 | .switch_event = map_switch_event, |
1725 | .runtime_event = NULL, | 1733 | .runtime_event = NULL, |
1726 | .fork_event = NULL, | 1734 | .fork_event = NULL, |
1727 | }; | 1735 | }; |
1728 | 1736 | ||
1729 | static void __cmd_map(void) | 1737 | static void __cmd_map(void) |
1730 | { | 1738 | { |
1731 | max_cpu = sysconf(_SC_NPROCESSORS_CONF); | 1739 | max_cpu = sysconf(_SC_NPROCESSORS_CONF); |
1732 | 1740 | ||
1733 | setup_pager(); | 1741 | setup_pager(); |
1734 | read_events(); | 1742 | read_events(true, NULL); |
1735 | print_bad_events(); | 1743 | print_bad_events(); |
1736 | } | 1744 | } |
1737 | 1745 | ||
1738 | static void __cmd_replay(void) | 1746 | static void __cmd_replay(void) |
1739 | { | 1747 | { |
1740 | unsigned long i; | 1748 | unsigned long i; |
1741 | 1749 | ||
1742 | calibrate_run_measurement_overhead(); | 1750 | calibrate_run_measurement_overhead(); |
1743 | calibrate_sleep_measurement_overhead(); | 1751 | calibrate_sleep_measurement_overhead(); |
1744 | 1752 | ||
1745 | test_calibrations(); | 1753 | test_calibrations(); |
1746 | 1754 | ||
1747 | read_events(); | 1755 | read_events(true, NULL); |
1748 | 1756 | ||
1749 | printf("nr_run_events: %ld\n", nr_run_events); | 1757 | printf("nr_run_events: %ld\n", nr_run_events); |
1750 | printf("nr_sleep_events: %ld\n", nr_sleep_events); | 1758 | printf("nr_sleep_events: %ld\n", nr_sleep_events); |
1751 | printf("nr_wakeup_events: %ld\n", nr_wakeup_events); | 1759 | printf("nr_wakeup_events: %ld\n", nr_wakeup_events); |
1752 | 1760 | ||
1753 | if (targetless_wakeups) | 1761 | if (targetless_wakeups) |
1754 | printf("target-less wakeups: %ld\n", targetless_wakeups); | 1762 | printf("target-less wakeups: %ld\n", targetless_wakeups); |
1755 | if (multitarget_wakeups) | 1763 | if (multitarget_wakeups) |
1756 | printf("multi-target wakeups: %ld\n", multitarget_wakeups); | 1764 | printf("multi-target wakeups: %ld\n", multitarget_wakeups); |
1757 | if (nr_run_events_optimized) | 1765 | if (nr_run_events_optimized) |
1758 | printf("run atoms optimized: %ld\n", | 1766 | printf("run atoms optimized: %ld\n", |
1759 | nr_run_events_optimized); | 1767 | nr_run_events_optimized); |
1760 | 1768 | ||
1761 | print_task_traces(); | 1769 | print_task_traces(); |
1762 | add_cross_task_wakeups(); | 1770 | add_cross_task_wakeups(); |
1763 | 1771 | ||
1764 | create_tasks(); | 1772 | create_tasks(); |
1765 | printf("------------------------------------------------------------\n"); | 1773 | printf("------------------------------------------------------------\n"); |
1766 | for (i = 0; i < replay_repeat; i++) | 1774 | for (i = 0; i < replay_repeat; i++) |
1767 | run_one_test(); | 1775 | run_one_test(); |
1768 | } | 1776 | } |
1769 | 1777 | ||
1770 | 1778 | ||
1771 | static const char * const sched_usage[] = { | 1779 | static const char * const sched_usage[] = { |
1772 | "perf sched [<options>] {record|latency|map|replay|trace}", | 1780 | "perf sched [<options>] {record|latency|map|replay|script}", |
1773 | NULL | 1781 | NULL |
1774 | }; | 1782 | }; |
1775 | 1783 | ||
1776 | static const struct option sched_options[] = { | 1784 | static const struct option sched_options[] = { |
1777 | OPT_STRING('i', "input", &input_name, "file", | 1785 | OPT_STRING('i', "input", &input_name, "file", |
1778 | "input file name"), | 1786 | "input file name"), |
1779 | OPT_INCR('v', "verbose", &verbose, | 1787 | OPT_INCR('v', "verbose", &verbose, |
1780 | "be more verbose (show symbol address, etc)"), | 1788 | "be more verbose (show symbol address, etc)"), |
1781 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1789 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1782 | "dump raw trace in ASCII"), | 1790 | "dump raw trace in ASCII"), |
1783 | OPT_END() | 1791 | OPT_END() |
1784 | }; | 1792 | }; |
1785 | 1793 | ||
1786 | static const char * const latency_usage[] = { | 1794 | static const char * const latency_usage[] = { |
1787 | "perf sched latency [<options>]", | 1795 | "perf sched latency [<options>]", |
1788 | NULL | 1796 | NULL |
1789 | }; | 1797 | }; |
1790 | 1798 | ||
1791 | static const struct option latency_options[] = { | 1799 | static const struct option latency_options[] = { |
1792 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1800 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1793 | "sort by key(s): runtime, switch, avg, max"), | 1801 | "sort by key(s): runtime, switch, avg, max"), |
1794 | OPT_INCR('v', "verbose", &verbose, | 1802 | OPT_INCR('v', "verbose", &verbose, |
1795 | "be more verbose (show symbol address, etc)"), | 1803 | "be more verbose (show symbol address, etc)"), |
1796 | OPT_INTEGER('C', "CPU", &profile_cpu, | 1804 | OPT_INTEGER('C', "CPU", &profile_cpu, |
1797 | "CPU to profile on"), | 1805 | "CPU to profile on"), |
1798 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1806 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1799 | "dump raw trace in ASCII"), | 1807 | "dump raw trace in ASCII"), |
1800 | OPT_END() | 1808 | OPT_END() |
1801 | }; | 1809 | }; |
1802 | 1810 | ||
1803 | static const char * const replay_usage[] = { | 1811 | static const char * const replay_usage[] = { |
1804 | "perf sched replay [<options>]", | 1812 | "perf sched replay [<options>]", |
1805 | NULL | 1813 | NULL |
1806 | }; | 1814 | }; |
1807 | 1815 | ||
1808 | static const struct option replay_options[] = { | 1816 | static const struct option replay_options[] = { |
1809 | OPT_UINTEGER('r', "repeat", &replay_repeat, | 1817 | OPT_UINTEGER('r', "repeat", &replay_repeat, |
1810 | "repeat the workload replay N times (-1: infinite)"), | 1818 | "repeat the workload replay N times (-1: infinite)"), |
1811 | OPT_INCR('v', "verbose", &verbose, | 1819 | OPT_INCR('v', "verbose", &verbose, |
1812 | "be more verbose (show symbol address, etc)"), | 1820 | "be more verbose (show symbol address, etc)"), |
1813 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1821 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1814 | "dump raw trace in ASCII"), | 1822 | "dump raw trace in ASCII"), |
1815 | OPT_END() | 1823 | OPT_END() |
1816 | }; | 1824 | }; |
1817 | 1825 | ||
1818 | static void setup_sorting(void) | 1826 | static void setup_sorting(void) |
1819 | { | 1827 | { |
1820 | char *tmp, *tok, *str = strdup(sort_order); | 1828 | char *tmp, *tok, *str = strdup(sort_order); |
1821 | 1829 | ||
1822 | for (tok = strtok_r(str, ", ", &tmp); | 1830 | for (tok = strtok_r(str, ", ", &tmp); |
1823 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 1831 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
1824 | if (sort_dimension__add(tok, &sort_list) < 0) { | 1832 | if (sort_dimension__add(tok, &sort_list) < 0) { |
1825 | error("Unknown --sort key: `%s'", tok); | 1833 | error("Unknown --sort key: `%s'", tok); |
1826 | usage_with_options(latency_usage, latency_options); | 1834 | usage_with_options(latency_usage, latency_options); |
1827 | } | 1835 | } |
1828 | } | 1836 | } |
1829 | 1837 | ||
1830 | free(str); | 1838 | free(str); |
1831 | 1839 | ||
1832 | sort_dimension__add("pid", &cmp_pid); | 1840 | sort_dimension__add("pid", &cmp_pid); |
1833 | } | 1841 | } |
1834 | 1842 | ||
1835 | static const char *record_args[] = { | 1843 | static const char *record_args[] = { |
1836 | "record", | 1844 | "record", |
1837 | "-a", | 1845 | "-a", |
1838 | "-R", | 1846 | "-R", |
1839 | "-f", | 1847 | "-f", |
1840 | "-m", "1024", | 1848 | "-m", "1024", |
1841 | "-c", "1", | 1849 | "-c", "1", |
1842 | "-e", "sched:sched_switch", | 1850 | "-e", "sched:sched_switch", |
1843 | "-e", "sched:sched_stat_wait", | 1851 | "-e", "sched:sched_stat_wait", |
1844 | "-e", "sched:sched_stat_sleep", | 1852 | "-e", "sched:sched_stat_sleep", |
1845 | "-e", "sched:sched_stat_iowait", | 1853 | "-e", "sched:sched_stat_iowait", |
1846 | "-e", "sched:sched_stat_runtime", | 1854 | "-e", "sched:sched_stat_runtime", |
1847 | "-e", "sched:sched_process_exit", | 1855 | "-e", "sched:sched_process_exit", |
1848 | "-e", "sched:sched_process_fork", | 1856 | "-e", "sched:sched_process_fork", |
1849 | "-e", "sched:sched_wakeup", | 1857 | "-e", "sched:sched_wakeup", |
1850 | "-e", "sched:sched_migrate_task", | 1858 | "-e", "sched:sched_migrate_task", |
1851 | }; | 1859 | }; |
1852 | 1860 | ||
1853 | static int __cmd_record(int argc, const char **argv) | 1861 | static int __cmd_record(int argc, const char **argv) |
1854 | { | 1862 | { |
1855 | unsigned int rec_argc, i, j; | 1863 | unsigned int rec_argc, i, j; |
1856 | const char **rec_argv; | 1864 | const char **rec_argv; |
1857 | 1865 | ||
1858 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | 1866 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; |
1859 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1867 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
1860 | 1868 | ||
1861 | if (rec_argv == NULL) | 1869 | if (rec_argv == NULL) |
1862 | return -ENOMEM; | 1870 | return -ENOMEM; |
1863 | 1871 | ||
1864 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 1872 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
1865 | rec_argv[i] = strdup(record_args[i]); | 1873 | rec_argv[i] = strdup(record_args[i]); |
1866 | 1874 | ||
1867 | for (j = 1; j < (unsigned int)argc; j++, i++) | 1875 | for (j = 1; j < (unsigned int)argc; j++, i++) |
1868 | rec_argv[i] = argv[j]; | 1876 | rec_argv[i] = argv[j]; |
1869 | 1877 | ||
1870 | BUG_ON(i != rec_argc); | 1878 | BUG_ON(i != rec_argc); |
1871 | 1879 | ||
1872 | return cmd_record(i, rec_argv, NULL); | 1880 | return cmd_record(i, rec_argv, NULL); |
1873 | } | 1881 | } |
1874 | 1882 | ||
1875 | int cmd_sched(int argc, const char **argv, const char *prefix __used) | 1883 | int cmd_sched(int argc, const char **argv, const char *prefix __used) |
1876 | { | 1884 | { |
1877 | argc = parse_options(argc, argv, sched_options, sched_usage, | 1885 | argc = parse_options(argc, argv, sched_options, sched_usage, |
1878 | PARSE_OPT_STOP_AT_NON_OPTION); | 1886 | PARSE_OPT_STOP_AT_NON_OPTION); |
1879 | if (!argc) | 1887 | if (!argc) |
1880 | usage_with_options(sched_usage, sched_options); | 1888 | usage_with_options(sched_usage, sched_options); |
1881 | 1889 | ||
1882 | /* | 1890 | /* |
1883 | * Aliased to 'perf script' for now: | 1891 | * Aliased to 'perf script' for now: |
1884 | */ | 1892 | */ |
1885 | if (!strcmp(argv[0], "script")) | 1893 | if (!strcmp(argv[0], "script")) |
1886 | return cmd_script(argc, argv, prefix); | 1894 | return cmd_script(argc, argv, prefix); |
1887 | 1895 | ||
1888 | symbol__init(); | 1896 | symbol__init(); |
1889 | if (!strncmp(argv[0], "rec", 3)) { | 1897 | if (!strncmp(argv[0], "rec", 3)) { |
1890 | return __cmd_record(argc, argv); | 1898 | return __cmd_record(argc, argv); |
1891 | } else if (!strncmp(argv[0], "lat", 3)) { | 1899 | } else if (!strncmp(argv[0], "lat", 3)) { |
1892 | trace_handler = &lat_ops; | 1900 | trace_handler = &lat_ops; |
1893 | if (argc > 1) { | 1901 | if (argc > 1) { |
1894 | argc = parse_options(argc, argv, latency_options, latency_usage, 0); | 1902 | argc = parse_options(argc, argv, latency_options, latency_usage, 0); |
1895 | if (argc) | 1903 | if (argc) |
1896 | usage_with_options(latency_usage, latency_options); | 1904 | usage_with_options(latency_usage, latency_options); |
1897 | } | 1905 | } |
1898 | setup_sorting(); | 1906 | setup_sorting(); |
1899 | __cmd_lat(); | 1907 | __cmd_lat(); |
1900 | } else if (!strcmp(argv[0], "map")) { | 1908 | } else if (!strcmp(argv[0], "map")) { |
1901 | trace_handler = &map_ops; | 1909 | trace_handler = &map_ops; |
1902 | setup_sorting(); | 1910 | setup_sorting(); |
1903 | __cmd_map(); | 1911 | __cmd_map(); |
1904 | } else if (!strncmp(argv[0], "rep", 3)) { | 1912 | } else if (!strncmp(argv[0], "rep", 3)) { |
1905 | trace_handler = &replay_ops; | 1913 | trace_handler = &replay_ops; |
1906 | if (argc) { | 1914 | if (argc) { |
1907 | argc = parse_options(argc, argv, replay_options, replay_usage, 0); | 1915 | argc = parse_options(argc, argv, replay_options, replay_usage, 0); |
1908 | if (argc) | 1916 | if (argc) |
1909 | usage_with_options(replay_usage, replay_options); | 1917 | usage_with_options(replay_usage, replay_options); |
1910 | } | 1918 | } |
1911 | __cmd_replay(); | 1919 | __cmd_replay(); |
1912 | } else { | 1920 | } else { |
1913 | usage_with_options(sched_usage, sched_options); | 1921 | usage_with_options(sched_usage, sched_options); |
1914 | } | 1922 | } |
1915 | 1923 | ||
1916 | return 0; | 1924 | return 0; |
1917 | } | 1925 | } |
1918 | 1926 |
tools/perf/util/config.c
1 | /* | 1 | /* |
2 | * GIT - The information manager from hell | 2 | * GIT - The information manager from hell |
3 | * | 3 | * |
4 | * Copyright (C) Linus Torvalds, 2005 | 4 | * Copyright (C) Linus Torvalds, 2005 |
5 | * Copyright (C) Johannes Schindelin, 2005 | 5 | * Copyright (C) Johannes Schindelin, 2005 |
6 | * | 6 | * |
7 | */ | 7 | */ |
8 | #include "util.h" | 8 | #include "util.h" |
9 | #include "cache.h" | 9 | #include "cache.h" |
10 | #include "exec_cmd.h" | 10 | #include "exec_cmd.h" |
11 | 11 | ||
12 | #define MAXNAME (256) | 12 | #define MAXNAME (256) |
13 | 13 | ||
14 | #define DEBUG_CACHE_DIR ".debug" | 14 | #define DEBUG_CACHE_DIR ".debug" |
15 | 15 | ||
16 | 16 | ||
17 | char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ | 17 | char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ |
18 | 18 | ||
19 | static FILE *config_file; | 19 | static FILE *config_file; |
20 | static const char *config_file_name; | 20 | static const char *config_file_name; |
21 | static int config_linenr; | 21 | static int config_linenr; |
22 | static int config_file_eof; | 22 | static int config_file_eof; |
23 | 23 | ||
24 | static const char *config_exclusive_filename; | 24 | static const char *config_exclusive_filename; |
25 | 25 | ||
26 | static int get_next_char(void) | 26 | static int get_next_char(void) |
27 | { | 27 | { |
28 | int c; | 28 | int c; |
29 | FILE *f; | 29 | FILE *f; |
30 | 30 | ||
31 | c = '\n'; | 31 | c = '\n'; |
32 | if ((f = config_file) != NULL) { | 32 | if ((f = config_file) != NULL) { |
33 | c = fgetc(f); | 33 | c = fgetc(f); |
34 | if (c == '\r') { | 34 | if (c == '\r') { |
35 | /* DOS like systems */ | 35 | /* DOS like systems */ |
36 | c = fgetc(f); | 36 | c = fgetc(f); |
37 | if (c != '\n') { | 37 | if (c != '\n') { |
38 | ungetc(c, f); | 38 | ungetc(c, f); |
39 | c = '\r'; | 39 | c = '\r'; |
40 | } | 40 | } |
41 | } | 41 | } |
42 | if (c == '\n') | 42 | if (c == '\n') |
43 | config_linenr++; | 43 | config_linenr++; |
44 | if (c == EOF) { | 44 | if (c == EOF) { |
45 | config_file_eof = 1; | 45 | config_file_eof = 1; |
46 | c = '\n'; | 46 | c = '\n'; |
47 | } | 47 | } |
48 | } | 48 | } |
49 | return c; | 49 | return c; |
50 | } | 50 | } |
51 | 51 | ||
52 | static char *parse_value(void) | 52 | static char *parse_value(void) |
53 | { | 53 | { |
54 | static char value[1024]; | 54 | static char value[1024]; |
55 | int quote = 0, comment = 0, space = 0; | 55 | int quote = 0, comment = 0, space = 0; |
56 | size_t len = 0; | 56 | size_t len = 0; |
57 | 57 | ||
58 | for (;;) { | 58 | for (;;) { |
59 | int c = get_next_char(); | 59 | int c = get_next_char(); |
60 | 60 | ||
61 | if (len >= sizeof(value) - 1) | 61 | if (len >= sizeof(value) - 1) |
62 | return NULL; | 62 | return NULL; |
63 | if (c == '\n') { | 63 | if (c == '\n') { |
64 | if (quote) | 64 | if (quote) |
65 | return NULL; | 65 | return NULL; |
66 | value[len] = 0; | 66 | value[len] = 0; |
67 | return value; | 67 | return value; |
68 | } | 68 | } |
69 | if (comment) | 69 | if (comment) |
70 | continue; | 70 | continue; |
71 | if (isspace(c) && !quote) { | 71 | if (isspace(c) && !quote) { |
72 | space = 1; | 72 | space = 1; |
73 | continue; | 73 | continue; |
74 | } | 74 | } |
75 | if (!quote) { | 75 | if (!quote) { |
76 | if (c == ';' || c == '#') { | 76 | if (c == ';' || c == '#') { |
77 | comment = 1; | 77 | comment = 1; |
78 | continue; | 78 | continue; |
79 | } | 79 | } |
80 | } | 80 | } |
81 | if (space) { | 81 | if (space) { |
82 | if (len) | 82 | if (len) |
83 | value[len++] = ' '; | 83 | value[len++] = ' '; |
84 | space = 0; | 84 | space = 0; |
85 | } | 85 | } |
86 | if (c == '\\') { | 86 | if (c == '\\') { |
87 | c = get_next_char(); | 87 | c = get_next_char(); |
88 | switch (c) { | 88 | switch (c) { |
89 | case '\n': | 89 | case '\n': |
90 | continue; | 90 | continue; |
91 | case 't': | 91 | case 't': |
92 | c = '\t'; | 92 | c = '\t'; |
93 | break; | 93 | break; |
94 | case 'b': | 94 | case 'b': |
95 | c = '\b'; | 95 | c = '\b'; |
96 | break; | 96 | break; |
97 | case 'n': | 97 | case 'n': |
98 | c = '\n'; | 98 | c = '\n'; |
99 | break; | 99 | break; |
100 | /* Some characters escape as themselves */ | 100 | /* Some characters escape as themselves */ |
101 | case '\\': case '"': | 101 | case '\\': case '"': |
102 | break; | 102 | break; |
103 | /* Reject unknown escape sequences */ | 103 | /* Reject unknown escape sequences */ |
104 | default: | 104 | default: |
105 | return NULL; | 105 | return NULL; |
106 | } | 106 | } |
107 | value[len++] = c; | 107 | value[len++] = c; |
108 | continue; | 108 | continue; |
109 | } | 109 | } |
110 | if (c == '"') { | 110 | if (c == '"') { |
111 | quote = 1-quote; | 111 | quote = 1-quote; |
112 | continue; | 112 | continue; |
113 | } | 113 | } |
114 | value[len++] = c; | 114 | value[len++] = c; |
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
118 | static inline int iskeychar(int c) | 118 | static inline int iskeychar(int c) |
119 | { | 119 | { |
120 | return isalnum(c) || c == '-'; | 120 | return isalnum(c) || c == '-'; |
121 | } | 121 | } |
122 | 122 | ||
123 | static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) | 123 | static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) |
124 | { | 124 | { |
125 | int c; | 125 | int c; |
126 | char *value; | 126 | char *value; |
127 | 127 | ||
128 | /* Get the full name */ | 128 | /* Get the full name */ |
129 | for (;;) { | 129 | for (;;) { |
130 | c = get_next_char(); | 130 | c = get_next_char(); |
131 | if (config_file_eof) | 131 | if (config_file_eof) |
132 | break; | 132 | break; |
133 | if (!iskeychar(c)) | 133 | if (!iskeychar(c)) |
134 | break; | 134 | break; |
135 | name[len++] = c; | 135 | name[len++] = c; |
136 | if (len >= MAXNAME) | 136 | if (len >= MAXNAME) |
137 | return -1; | 137 | return -1; |
138 | } | 138 | } |
139 | name[len] = 0; | 139 | name[len] = 0; |
140 | while (c == ' ' || c == '\t') | 140 | while (c == ' ' || c == '\t') |
141 | c = get_next_char(); | 141 | c = get_next_char(); |
142 | 142 | ||
143 | value = NULL; | 143 | value = NULL; |
144 | if (c != '\n') { | 144 | if (c != '\n') { |
145 | if (c != '=') | 145 | if (c != '=') |
146 | return -1; | 146 | return -1; |
147 | value = parse_value(); | 147 | value = parse_value(); |
148 | if (!value) | 148 | if (!value) |
149 | return -1; | 149 | return -1; |
150 | } | 150 | } |
151 | return fn(name, value, data); | 151 | return fn(name, value, data); |
152 | } | 152 | } |
153 | 153 | ||
154 | static int get_extended_base_var(char *name, int baselen, int c) | 154 | static int get_extended_base_var(char *name, int baselen, int c) |
155 | { | 155 | { |
156 | do { | 156 | do { |
157 | if (c == '\n') | 157 | if (c == '\n') |
158 | return -1; | 158 | return -1; |
159 | c = get_next_char(); | 159 | c = get_next_char(); |
160 | } while (isspace(c)); | 160 | } while (isspace(c)); |
161 | 161 | ||
162 | /* We require the format to be '[base "extension"]' */ | 162 | /* We require the format to be '[base "extension"]' */ |
163 | if (c != '"') | 163 | if (c != '"') |
164 | return -1; | 164 | return -1; |
165 | name[baselen++] = '.'; | 165 | name[baselen++] = '.'; |
166 | 166 | ||
167 | for (;;) { | 167 | for (;;) { |
168 | int ch = get_next_char(); | 168 | int ch = get_next_char(); |
169 | 169 | ||
170 | if (ch == '\n') | 170 | if (ch == '\n') |
171 | return -1; | 171 | return -1; |
172 | if (ch == '"') | 172 | if (ch == '"') |
173 | break; | 173 | break; |
174 | if (ch == '\\') { | 174 | if (ch == '\\') { |
175 | ch = get_next_char(); | 175 | ch = get_next_char(); |
176 | if (ch == '\n') | 176 | if (ch == '\n') |
177 | return -1; | 177 | return -1; |
178 | } | 178 | } |
179 | name[baselen++] = ch; | 179 | name[baselen++] = ch; |
180 | if (baselen > MAXNAME / 2) | 180 | if (baselen > MAXNAME / 2) |
181 | return -1; | 181 | return -1; |
182 | } | 182 | } |
183 | 183 | ||
184 | /* Final ']' */ | 184 | /* Final ']' */ |
185 | if (get_next_char() != ']') | 185 | if (get_next_char() != ']') |
186 | return -1; | 186 | return -1; |
187 | return baselen; | 187 | return baselen; |
188 | } | 188 | } |
189 | 189 | ||
190 | static int get_base_var(char *name) | 190 | static int get_base_var(char *name) |
191 | { | 191 | { |
192 | int baselen = 0; | 192 | int baselen = 0; |
193 | 193 | ||
194 | for (;;) { | 194 | for (;;) { |
195 | int c = get_next_char(); | 195 | int c = get_next_char(); |
196 | if (config_file_eof) | 196 | if (config_file_eof) |
197 | return -1; | 197 | return -1; |
198 | if (c == ']') | 198 | if (c == ']') |
199 | return baselen; | 199 | return baselen; |
200 | if (isspace(c)) | 200 | if (isspace(c)) |
201 | return get_extended_base_var(name, baselen, c); | 201 | return get_extended_base_var(name, baselen, c); |
202 | if (!iskeychar(c) && c != '.') | 202 | if (!iskeychar(c) && c != '.') |
203 | return -1; | 203 | return -1; |
204 | if (baselen > MAXNAME / 2) | 204 | if (baselen > MAXNAME / 2) |
205 | return -1; | 205 | return -1; |
206 | name[baselen++] = tolower(c); | 206 | name[baselen++] = tolower(c); |
207 | } | 207 | } |
208 | } | 208 | } |
209 | 209 | ||
210 | static int perf_parse_file(config_fn_t fn, void *data) | 210 | static int perf_parse_file(config_fn_t fn, void *data) |
211 | { | 211 | { |
212 | int comment = 0; | 212 | int comment = 0; |
213 | int baselen = 0; | 213 | int baselen = 0; |
214 | static char var[MAXNAME]; | 214 | static char var[MAXNAME]; |
215 | 215 | ||
216 | /* U+FEFF Byte Order Mark in UTF8 */ | 216 | /* U+FEFF Byte Order Mark in UTF8 */ |
217 | static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; | 217 | static const unsigned char *utf8_bom = (unsigned char *) "\xef\xbb\xbf"; |
218 | const unsigned char *bomptr = utf8_bom; | 218 | const unsigned char *bomptr = utf8_bom; |
219 | 219 | ||
220 | for (;;) { | 220 | for (;;) { |
221 | int c = get_next_char(); | 221 | int c = get_next_char(); |
222 | if (bomptr && *bomptr) { | 222 | if (bomptr && *bomptr) { |
223 | /* We are at the file beginning; skip UTF8-encoded BOM | 223 | /* We are at the file beginning; skip UTF8-encoded BOM |
224 | * if present. Sane editors won't put this in on their | 224 | * if present. Sane editors won't put this in on their |
225 | * own, but e.g. Windows Notepad will do it happily. */ | 225 | * own, but e.g. Windows Notepad will do it happily. */ |
226 | if ((unsigned char) c == *bomptr) { | 226 | if ((unsigned char) c == *bomptr) { |
227 | bomptr++; | 227 | bomptr++; |
228 | continue; | 228 | continue; |
229 | } else { | 229 | } else { |
230 | /* Do not tolerate partial BOM. */ | 230 | /* Do not tolerate partial BOM. */ |
231 | if (bomptr != utf8_bom) | 231 | if (bomptr != utf8_bom) |
232 | break; | 232 | break; |
233 | /* No BOM at file beginning. Cool. */ | 233 | /* No BOM at file beginning. Cool. */ |
234 | bomptr = NULL; | 234 | bomptr = NULL; |
235 | } | 235 | } |
236 | } | 236 | } |
237 | if (c == '\n') { | 237 | if (c == '\n') { |
238 | if (config_file_eof) | 238 | if (config_file_eof) |
239 | return 0; | 239 | return 0; |
240 | comment = 0; | 240 | comment = 0; |
241 | continue; | 241 | continue; |
242 | } | 242 | } |
243 | if (comment || isspace(c)) | 243 | if (comment || isspace(c)) |
244 | continue; | 244 | continue; |
245 | if (c == '#' || c == ';') { | 245 | if (c == '#' || c == ';') { |
246 | comment = 1; | 246 | comment = 1; |
247 | continue; | 247 | continue; |
248 | } | 248 | } |
249 | if (c == '[') { | 249 | if (c == '[') { |
250 | baselen = get_base_var(var); | 250 | baselen = get_base_var(var); |
251 | if (baselen <= 0) | 251 | if (baselen <= 0) |
252 | break; | 252 | break; |
253 | var[baselen++] = '.'; | 253 | var[baselen++] = '.'; |
254 | var[baselen] = 0; | 254 | var[baselen] = 0; |
255 | continue; | 255 | continue; |
256 | } | 256 | } |
257 | if (!isalpha(c)) | 257 | if (!isalpha(c)) |
258 | break; | 258 | break; |
259 | var[baselen] = tolower(c); | 259 | var[baselen] = tolower(c); |
260 | if (get_value(fn, data, var, baselen+1) < 0) | 260 | if (get_value(fn, data, var, baselen+1) < 0) |
261 | break; | 261 | break; |
262 | } | 262 | } |
263 | die("bad config file line %d in %s", config_linenr, config_file_name); | 263 | die("bad config file line %d in %s", config_linenr, config_file_name); |
264 | } | 264 | } |
265 | 265 | ||
266 | static int parse_unit_factor(const char *end, unsigned long *val) | 266 | static int parse_unit_factor(const char *end, unsigned long *val) |
267 | { | 267 | { |
268 | if (!*end) | 268 | if (!*end) |
269 | return 1; | 269 | return 1; |
270 | else if (!strcasecmp(end, "k")) { | 270 | else if (!strcasecmp(end, "k")) { |
271 | *val *= 1024; | 271 | *val *= 1024; |
272 | return 1; | 272 | return 1; |
273 | } | 273 | } |
274 | else if (!strcasecmp(end, "m")) { | 274 | else if (!strcasecmp(end, "m")) { |
275 | *val *= 1024 * 1024; | 275 | *val *= 1024 * 1024; |
276 | return 1; | 276 | return 1; |
277 | } | 277 | } |
278 | else if (!strcasecmp(end, "g")) { | 278 | else if (!strcasecmp(end, "g")) { |
279 | *val *= 1024 * 1024 * 1024; | 279 | *val *= 1024 * 1024 * 1024; |
280 | return 1; | 280 | return 1; |
281 | } | 281 | } |
282 | return 0; | 282 | return 0; |
283 | } | 283 | } |
284 | 284 | ||
285 | static int perf_parse_long(const char *value, long *ret) | 285 | static int perf_parse_long(const char *value, long *ret) |
286 | { | 286 | { |
287 | if (value && *value) { | 287 | if (value && *value) { |
288 | char *end; | 288 | char *end; |
289 | long val = strtol(value, &end, 0); | 289 | long val = strtol(value, &end, 0); |
290 | unsigned long factor = 1; | 290 | unsigned long factor = 1; |
291 | if (!parse_unit_factor(end, &factor)) | 291 | if (!parse_unit_factor(end, &factor)) |
292 | return 0; | 292 | return 0; |
293 | *ret = val * factor; | 293 | *ret = val * factor; |
294 | return 1; | 294 | return 1; |
295 | } | 295 | } |
296 | return 0; | 296 | return 0; |
297 | } | 297 | } |
298 | 298 | ||
299 | static void die_bad_config(const char *name) | 299 | static void die_bad_config(const char *name) |
300 | { | 300 | { |
301 | if (config_file_name) | 301 | if (config_file_name) |
302 | die("bad config value for '%s' in %s", name, config_file_name); | 302 | die("bad config value for '%s' in %s", name, config_file_name); |
303 | die("bad config value for '%s'", name); | 303 | die("bad config value for '%s'", name); |
304 | } | 304 | } |
305 | 305 | ||
306 | int perf_config_int(const char *name, const char *value) | 306 | int perf_config_int(const char *name, const char *value) |
307 | { | 307 | { |
308 | long ret = 0; | 308 | long ret = 0; |
309 | if (!perf_parse_long(value, &ret)) | 309 | if (!perf_parse_long(value, &ret)) |
310 | die_bad_config(name); | 310 | die_bad_config(name); |
311 | return ret; | 311 | return ret; |
312 | } | 312 | } |
313 | 313 | ||
314 | static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool) | 314 | static int perf_config_bool_or_int(const char *name, const char *value, int *is_bool) |
315 | { | 315 | { |
316 | *is_bool = 1; | 316 | *is_bool = 1; |
317 | if (!value) | 317 | if (!value) |
318 | return 1; | 318 | return 1; |
319 | if (!*value) | 319 | if (!*value) |
320 | return 0; | 320 | return 0; |
321 | if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) | 321 | if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on")) |
322 | return 1; | 322 | return 1; |
323 | if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) | 323 | if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off")) |
324 | return 0; | 324 | return 0; |
325 | *is_bool = 0; | 325 | *is_bool = 0; |
326 | return perf_config_int(name, value); | 326 | return perf_config_int(name, value); |
327 | } | 327 | } |
328 | 328 | ||
329 | int perf_config_bool(const char *name, const char *value) | 329 | int perf_config_bool(const char *name, const char *value) |
330 | { | 330 | { |
331 | int discard; | 331 | int discard; |
332 | return !!perf_config_bool_or_int(name, value, &discard); | 332 | return !!perf_config_bool_or_int(name, value, &discard); |
333 | } | 333 | } |
334 | 334 | ||
335 | const char *perf_config_dirname(const char *name, const char *value) | 335 | const char *perf_config_dirname(const char *name, const char *value) |
336 | { | 336 | { |
337 | if (!name) | 337 | if (!name) |
338 | return NULL; | 338 | return NULL; |
339 | return value; | 339 | return value; |
340 | } | 340 | } |
341 | 341 | ||
342 | static int perf_default_core_config(const char *var __used, const char *value __used) | 342 | static int perf_default_core_config(const char *var __used, const char *value __used) |
343 | { | 343 | { |
344 | /* Add other config variables here and to Documentation/config.txt. */ | 344 | /* Add other config variables here and to Documentation/config.txt. */ |
345 | return 0; | 345 | return 0; |
346 | } | 346 | } |
347 | 347 | ||
348 | int perf_default_config(const char *var, const char *value, void *dummy __used) | 348 | int perf_default_config(const char *var, const char *value, void *dummy __used) |
349 | { | 349 | { |
350 | if (!prefixcmp(var, "core.")) | 350 | if (!prefixcmp(var, "core.")) |
351 | return perf_default_core_config(var, value); | 351 | return perf_default_core_config(var, value); |
352 | 352 | ||
353 | /* Add other config variables here and to Documentation/config.txt. */ | 353 | /* Add other config variables here and to Documentation/config.txt. */ |
354 | return 0; | 354 | return 0; |
355 | } | 355 | } |
356 | 356 | ||
357 | static int perf_config_from_file(config_fn_t fn, const char *filename, void *data) | 357 | static int perf_config_from_file(config_fn_t fn, const char *filename, void *data) |
358 | { | 358 | { |
359 | int ret; | 359 | int ret; |
360 | FILE *f = fopen(filename, "r"); | 360 | FILE *f = fopen(filename, "r"); |
361 | 361 | ||
362 | ret = -1; | 362 | ret = -1; |
363 | if (f) { | 363 | if (f) { |
364 | config_file = f; | 364 | config_file = f; |
365 | config_file_name = filename; | 365 | config_file_name = filename; |
366 | config_linenr = 1; | 366 | config_linenr = 1; |
367 | config_file_eof = 0; | 367 | config_file_eof = 0; |
368 | ret = perf_parse_file(fn, data); | 368 | ret = perf_parse_file(fn, data); |
369 | fclose(f); | 369 | fclose(f); |
370 | config_file_name = NULL; | 370 | config_file_name = NULL; |
371 | } | 371 | } |
372 | return ret; | 372 | return ret; |
373 | } | 373 | } |
374 | 374 | ||
375 | static const char *perf_etc_perfconfig(void) | 375 | static const char *perf_etc_perfconfig(void) |
376 | { | 376 | { |
377 | static const char *system_wide; | 377 | static const char *system_wide; |
378 | if (!system_wide) | 378 | if (!system_wide) |
379 | system_wide = system_path(ETC_PERFCONFIG); | 379 | system_wide = system_path(ETC_PERFCONFIG); |
380 | return system_wide; | 380 | return system_wide; |
381 | } | 381 | } |
382 | 382 | ||
383 | static int perf_env_bool(const char *k, int def) | 383 | static int perf_env_bool(const char *k, int def) |
384 | { | 384 | { |
385 | const char *v = getenv(k); | 385 | const char *v = getenv(k); |
386 | return v ? perf_config_bool(k, v) : def; | 386 | return v ? perf_config_bool(k, v) : def; |
387 | } | 387 | } |
388 | 388 | ||
389 | static int perf_config_system(void) | 389 | static int perf_config_system(void) |
390 | { | 390 | { |
391 | return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); | 391 | return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0); |
392 | } | 392 | } |
393 | 393 | ||
394 | static int perf_config_global(void) | 394 | static int perf_config_global(void) |
395 | { | 395 | { |
396 | return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); | 396 | return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0); |
397 | } | 397 | } |
398 | 398 | ||
399 | int perf_config(config_fn_t fn, void *data) | 399 | int perf_config(config_fn_t fn, void *data) |
400 | { | 400 | { |
401 | int ret = 0, found = 0; | 401 | int ret = 0, found = 0; |
402 | char *repo_config = NULL; | ||
403 | const char *home = NULL; | 402 | const char *home = NULL; |
404 | 403 | ||
405 | /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ | 404 | /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ |
406 | if (config_exclusive_filename) | 405 | if (config_exclusive_filename) |
407 | return perf_config_from_file(fn, config_exclusive_filename, data); | 406 | return perf_config_from_file(fn, config_exclusive_filename, data); |
408 | if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { | 407 | if (perf_config_system() && !access(perf_etc_perfconfig(), R_OK)) { |
409 | ret += perf_config_from_file(fn, perf_etc_perfconfig(), | 408 | ret += perf_config_from_file(fn, perf_etc_perfconfig(), |
410 | data); | 409 | data); |
411 | found += 1; | 410 | found += 1; |
412 | } | 411 | } |
413 | 412 | ||
414 | home = getenv("HOME"); | 413 | home = getenv("HOME"); |
415 | if (perf_config_global() && home) { | 414 | if (perf_config_global() && home) { |
416 | char *user_config = strdup(mkpath("%s/.perfconfig", home)); | 415 | char *user_config = strdup(mkpath("%s/.perfconfig", home)); |
417 | if (!access(user_config, R_OK)) { | 416 | struct stat st; |
418 | ret += perf_config_from_file(fn, user_config, data); | 417 | |
419 | found += 1; | 418 | if (user_config == NULL) { |
419 | warning("Not enough memory to process %s/.perfconfig, " | ||
420 | "ignoring it.", home); | ||
421 | goto out; | ||
420 | } | 422 | } |
421 | free(user_config); | ||
422 | } | ||
423 | 423 | ||
424 | repo_config = perf_pathdup("config"); | 424 | if (stat(user_config, &st) < 0) |
425 | if (!access(repo_config, R_OK)) { | 425 | goto out_free; |
426 | ret += perf_config_from_file(fn, repo_config, data); | 426 | |
427 | if (st.st_uid && (st.st_uid != geteuid())) { | ||
428 | warning("File %s not owned by current user or root, " | ||
429 | "ignoring it.", user_config); | ||
430 | goto out_free; | ||
431 | } | ||
432 | |||
433 | if (!st.st_size) | ||
434 | goto out_free; | ||
435 | |||
436 | ret += perf_config_from_file(fn, user_config, data); | ||
427 | found += 1; | 437 | found += 1; |
438 | out_free: | ||
439 | free(user_config); | ||
428 | } | 440 | } |
429 | free(repo_config); | 441 | out: |
430 | if (found == 0) | 442 | if (found == 0) |
431 | return -1; | 443 | return -1; |
432 | return ret; | 444 | return ret; |
433 | } | 445 | } |
434 | 446 | ||
435 | /* | 447 | /* |
436 | * Call this to report error for your variable that should not | 448 | * Call this to report error for your variable that should not |
437 | * get a boolean value (i.e. "[my] var" means "true"). | 449 | * get a boolean value (i.e. "[my] var" means "true"). |
438 | */ | 450 | */ |
439 | int config_error_nonbool(const char *var) | 451 | int config_error_nonbool(const char *var) |
440 | { | 452 | { |
441 | return error("Missing value for '%s'", var); | 453 | return error("Missing value for '%s'", var); |
442 | } | 454 | } |
443 | 455 | ||
444 | struct buildid_dir_config { | 456 | struct buildid_dir_config { |
445 | char *dir; | 457 | char *dir; |
446 | }; | 458 | }; |
447 | 459 | ||
448 | static int buildid_dir_command_config(const char *var, const char *value, | 460 | static int buildid_dir_command_config(const char *var, const char *value, |
449 | void *data) | 461 | void *data) |
450 | { | 462 | { |
451 | struct buildid_dir_config *c = data; | 463 | struct buildid_dir_config *c = data; |
452 | const char *v; | 464 | const char *v; |
453 | 465 | ||
454 | /* same dir for all commands */ | 466 | /* same dir for all commands */ |
455 | if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) { | 467 | if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) { |
456 | v = perf_config_dirname(var, value); | 468 | v = perf_config_dirname(var, value); |
457 | if (!v) | 469 | if (!v) |
458 | return -1; | 470 | return -1; |
459 | strncpy(c->dir, v, MAXPATHLEN-1); | 471 | strncpy(c->dir, v, MAXPATHLEN-1); |
460 | c->dir[MAXPATHLEN-1] = '\0'; | 472 | c->dir[MAXPATHLEN-1] = '\0'; |
461 | } | 473 | } |
462 | return 0; | 474 | return 0; |
463 | } | 475 | } |
464 | 476 | ||
465 | static void check_buildid_dir_config(void) | 477 | static void check_buildid_dir_config(void) |
466 | { | 478 | { |
467 | struct buildid_dir_config c; | 479 | struct buildid_dir_config c; |
468 | c.dir = buildid_dir; | 480 | c.dir = buildid_dir; |
469 | perf_config(buildid_dir_command_config, &c); | 481 | perf_config(buildid_dir_command_config, &c); |
470 | } | 482 | } |
471 | 483 | ||
472 | void set_buildid_dir(void) | 484 | void set_buildid_dir(void) |
473 | { | 485 | { |
474 | buildid_dir[0] = '\0'; | 486 | buildid_dir[0] = '\0'; |
475 | 487 | ||
476 | /* try config file */ | 488 | /* try config file */ |
477 | check_buildid_dir_config(); | 489 | check_buildid_dir_config(); |
478 | 490 | ||
479 | /* default to $HOME/.debug */ | 491 | /* default to $HOME/.debug */ |
480 | if (buildid_dir[0] == '\0') { | 492 | if (buildid_dir[0] == '\0') { |
481 | char *v = getenv("HOME"); | 493 | char *v = getenv("HOME"); |
482 | if (v) { | 494 | if (v) { |
483 | snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", | 495 | snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", |
484 | v, DEBUG_CACHE_DIR); | 496 | v, DEBUG_CACHE_DIR); |
485 | } else { | 497 | } else { |
486 | strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); | 498 | strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); |
487 | } | 499 | } |
488 | buildid_dir[MAXPATHLEN-1] = '\0'; | 500 | buildid_dir[MAXPATHLEN-1] = '\0'; |
489 | } | 501 | } |
490 | /* for communicating with external commands */ | 502 | /* for communicating with external commands */ |
tools/perf/util/evlist.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
3 | * | 3 | * |
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | 4 | * Parts came from builtin-{top,stat,record}.c, see those files for further |
5 | * copyright notes. | 5 | * copyright notes. |
6 | * | 6 | * |
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 <poll.h> | 9 | #include <poll.h> |
10 | #include "cpumap.h" | 10 | #include "cpumap.h" |
11 | #include "thread_map.h" | 11 | #include "thread_map.h" |
12 | #include "evlist.h" | 12 | #include "evlist.h" |
13 | #include "evsel.h" | 13 | #include "evsel.h" |
14 | #include "util.h" | 14 | #include "util.h" |
15 | 15 | ||
16 | #include <sys/mman.h> | 16 | #include <sys/mman.h> |
17 | 17 | ||
18 | #include <linux/bitops.h> | 18 | #include <linux/bitops.h> |
19 | #include <linux/hash.h> | 19 | #include <linux/hash.h> |
20 | 20 | ||
21 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 21 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
22 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) | 22 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) |
23 | 23 | ||
24 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | 24 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, |
25 | struct thread_map *threads) | 25 | struct thread_map *threads) |
26 | { | 26 | { |
27 | int i; | 27 | int i; |
28 | 28 | ||
29 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) | 29 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) |
30 | INIT_HLIST_HEAD(&evlist->heads[i]); | 30 | INIT_HLIST_HEAD(&evlist->heads[i]); |
31 | INIT_LIST_HEAD(&evlist->entries); | 31 | INIT_LIST_HEAD(&evlist->entries); |
32 | perf_evlist__set_maps(evlist, cpus, threads); | 32 | perf_evlist__set_maps(evlist, cpus, threads); |
33 | } | 33 | } |
34 | 34 | ||
35 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 35 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, |
36 | struct thread_map *threads) | 36 | struct thread_map *threads) |
37 | { | 37 | { |
38 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | 38 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); |
39 | 39 | ||
40 | if (evlist != NULL) | 40 | if (evlist != NULL) |
41 | perf_evlist__init(evlist, cpus, threads); | 41 | perf_evlist__init(evlist, cpus, threads); |
42 | 42 | ||
43 | return evlist; | 43 | return evlist; |
44 | } | 44 | } |
45 | 45 | ||
46 | static void perf_evlist__purge(struct perf_evlist *evlist) | 46 | static void perf_evlist__purge(struct perf_evlist *evlist) |
47 | { | 47 | { |
48 | struct perf_evsel *pos, *n; | 48 | struct perf_evsel *pos, *n; |
49 | 49 | ||
50 | list_for_each_entry_safe(pos, n, &evlist->entries, node) { | 50 | list_for_each_entry_safe(pos, n, &evlist->entries, node) { |
51 | list_del_init(&pos->node); | 51 | list_del_init(&pos->node); |
52 | perf_evsel__delete(pos); | 52 | perf_evsel__delete(pos); |
53 | } | 53 | } |
54 | 54 | ||
55 | evlist->nr_entries = 0; | 55 | evlist->nr_entries = 0; |
56 | } | 56 | } |
57 | 57 | ||
58 | void perf_evlist__exit(struct perf_evlist *evlist) | 58 | void perf_evlist__exit(struct perf_evlist *evlist) |
59 | { | 59 | { |
60 | free(evlist->mmap); | 60 | free(evlist->mmap); |
61 | free(evlist->pollfd); | 61 | free(evlist->pollfd); |
62 | evlist->mmap = NULL; | 62 | evlist->mmap = NULL; |
63 | evlist->pollfd = NULL; | 63 | evlist->pollfd = NULL; |
64 | } | 64 | } |
65 | 65 | ||
66 | void perf_evlist__delete(struct perf_evlist *evlist) | 66 | void perf_evlist__delete(struct perf_evlist *evlist) |
67 | { | 67 | { |
68 | perf_evlist__purge(evlist); | 68 | perf_evlist__purge(evlist); |
69 | perf_evlist__exit(evlist); | 69 | perf_evlist__exit(evlist); |
70 | free(evlist); | 70 | free(evlist); |
71 | } | 71 | } |
72 | 72 | ||
73 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | 73 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) |
74 | { | 74 | { |
75 | list_add_tail(&entry->node, &evlist->entries); | 75 | list_add_tail(&entry->node, &evlist->entries); |
76 | ++evlist->nr_entries; | 76 | ++evlist->nr_entries; |
77 | } | 77 | } |
78 | 78 | ||
79 | int perf_evlist__add_default(struct perf_evlist *evlist) | 79 | int perf_evlist__add_default(struct perf_evlist *evlist) |
80 | { | 80 | { |
81 | struct perf_event_attr attr = { | 81 | struct perf_event_attr attr = { |
82 | .type = PERF_TYPE_HARDWARE, | 82 | .type = PERF_TYPE_HARDWARE, |
83 | .config = PERF_COUNT_HW_CPU_CYCLES, | 83 | .config = PERF_COUNT_HW_CPU_CYCLES, |
84 | }; | 84 | }; |
85 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | 85 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); |
86 | 86 | ||
87 | if (evsel == NULL) | 87 | if (evsel == NULL) |
88 | return -ENOMEM; | 88 | return -ENOMEM; |
89 | 89 | ||
90 | perf_evlist__add(evlist, evsel); | 90 | perf_evlist__add(evlist, evsel); |
91 | return 0; | 91 | return 0; |
92 | } | 92 | } |
93 | 93 | ||
94 | void perf_evlist__disable(struct perf_evlist *evlist) | ||
95 | { | ||
96 | int cpu, thread; | ||
97 | struct perf_evsel *pos; | ||
98 | |||
99 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
100 | list_for_each_entry(pos, &evlist->entries, node) { | ||
101 | for (thread = 0; thread < evlist->threads->nr; thread++) | ||
102 | ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_DISABLE); | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
94 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 107 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
95 | { | 108 | { |
96 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; | 109 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; |
97 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 110 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); |
98 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | 111 | return evlist->pollfd != NULL ? 0 : -ENOMEM; |
99 | } | 112 | } |
100 | 113 | ||
101 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | 114 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) |
102 | { | 115 | { |
103 | fcntl(fd, F_SETFL, O_NONBLOCK); | 116 | fcntl(fd, F_SETFL, O_NONBLOCK); |
104 | evlist->pollfd[evlist->nr_fds].fd = fd; | 117 | evlist->pollfd[evlist->nr_fds].fd = fd; |
105 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | 118 | evlist->pollfd[evlist->nr_fds].events = POLLIN; |
106 | evlist->nr_fds++; | 119 | evlist->nr_fds++; |
107 | } | 120 | } |
108 | 121 | ||
109 | static void perf_evlist__id_hash(struct perf_evlist *evlist, | 122 | static void perf_evlist__id_hash(struct perf_evlist *evlist, |
110 | struct perf_evsel *evsel, | 123 | struct perf_evsel *evsel, |
111 | int cpu, int thread, u64 id) | 124 | int cpu, int thread, u64 id) |
112 | { | 125 | { |
113 | int hash; | 126 | int hash; |
114 | struct perf_sample_id *sid = SID(evsel, cpu, thread); | 127 | struct perf_sample_id *sid = SID(evsel, cpu, thread); |
115 | 128 | ||
116 | sid->id = id; | 129 | sid->id = id; |
117 | sid->evsel = evsel; | 130 | sid->evsel = evsel; |
118 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); | 131 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); |
119 | hlist_add_head(&sid->node, &evlist->heads[hash]); | 132 | hlist_add_head(&sid->node, &evlist->heads[hash]); |
120 | } | 133 | } |
121 | 134 | ||
122 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | 135 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, |
123 | int cpu, int thread, u64 id) | 136 | int cpu, int thread, u64 id) |
124 | { | 137 | { |
125 | perf_evlist__id_hash(evlist, evsel, cpu, thread, id); | 138 | perf_evlist__id_hash(evlist, evsel, cpu, thread, id); |
126 | evsel->id[evsel->ids++] = id; | 139 | evsel->id[evsel->ids++] = id; |
127 | } | 140 | } |
128 | 141 | ||
129 | static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | 142 | static int perf_evlist__id_add_fd(struct perf_evlist *evlist, |
130 | struct perf_evsel *evsel, | 143 | struct perf_evsel *evsel, |
131 | int cpu, int thread, int fd) | 144 | int cpu, int thread, int fd) |
132 | { | 145 | { |
133 | u64 read_data[4] = { 0, }; | 146 | u64 read_data[4] = { 0, }; |
134 | int id_idx = 1; /* The first entry is the counter value */ | 147 | int id_idx = 1; /* The first entry is the counter value */ |
135 | 148 | ||
136 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | 149 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || |
137 | read(fd, &read_data, sizeof(read_data)) == -1) | 150 | read(fd, &read_data, sizeof(read_data)) == -1) |
138 | return -1; | 151 | return -1; |
139 | 152 | ||
140 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | 153 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) |
141 | ++id_idx; | 154 | ++id_idx; |
142 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | 155 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) |
143 | ++id_idx; | 156 | ++id_idx; |
144 | 157 | ||
145 | perf_evlist__id_add(evlist, evsel, cpu, thread, read_data[id_idx]); | 158 | perf_evlist__id_add(evlist, evsel, cpu, thread, read_data[id_idx]); |
146 | return 0; | 159 | return 0; |
147 | } | 160 | } |
148 | 161 | ||
149 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | 162 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) |
150 | { | 163 | { |
151 | struct hlist_head *head; | 164 | struct hlist_head *head; |
152 | struct hlist_node *pos; | 165 | struct hlist_node *pos; |
153 | struct perf_sample_id *sid; | 166 | struct perf_sample_id *sid; |
154 | int hash; | 167 | int hash; |
155 | 168 | ||
156 | if (evlist->nr_entries == 1) | 169 | if (evlist->nr_entries == 1) |
157 | return list_entry(evlist->entries.next, struct perf_evsel, node); | 170 | return list_entry(evlist->entries.next, struct perf_evsel, node); |
158 | 171 | ||
159 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | 172 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
160 | head = &evlist->heads[hash]; | 173 | head = &evlist->heads[hash]; |
161 | 174 | ||
162 | hlist_for_each_entry(sid, pos, head, node) | 175 | hlist_for_each_entry(sid, pos, head, node) |
163 | if (sid->id == id) | 176 | if (sid->id == id) |
164 | return sid->evsel; | 177 | return sid->evsel; |
165 | return NULL; | 178 | return NULL; |
166 | } | 179 | } |
167 | 180 | ||
168 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | 181 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) |
169 | { | 182 | { |
170 | /* XXX Move this to perf.c, making it generally available */ | 183 | /* XXX Move this to perf.c, making it generally available */ |
171 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | 184 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); |
172 | struct perf_mmap *md = &evlist->mmap[idx]; | 185 | struct perf_mmap *md = &evlist->mmap[idx]; |
173 | unsigned int head = perf_mmap__read_head(md); | 186 | unsigned int head = perf_mmap__read_head(md); |
174 | unsigned int old = md->prev; | 187 | unsigned int old = md->prev; |
175 | unsigned char *data = md->base + page_size; | 188 | unsigned char *data = md->base + page_size; |
176 | union perf_event *event = NULL; | 189 | union perf_event *event = NULL; |
177 | 190 | ||
178 | if (evlist->overwrite) { | 191 | if (evlist->overwrite) { |
179 | /* | 192 | /* |
180 | * If we're further behind than half the buffer, there's a chance | 193 | * If we're further behind than half the buffer, there's a chance |
181 | * the writer will bite our tail and mess up the samples under us. | 194 | * the writer will bite our tail and mess up the samples under us. |
182 | * | 195 | * |
183 | * If we somehow ended up ahead of the head, we got messed up. | 196 | * If we somehow ended up ahead of the head, we got messed up. |
184 | * | 197 | * |
185 | * In either case, truncate and restart at head. | 198 | * In either case, truncate and restart at head. |
186 | */ | 199 | */ |
187 | int diff = head - old; | 200 | int diff = head - old; |
188 | if (diff > md->mask / 2 || diff < 0) { | 201 | if (diff > md->mask / 2 || diff < 0) { |
189 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | 202 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); |
190 | 203 | ||
191 | /* | 204 | /* |
192 | * head points to a known good entry, start there. | 205 | * head points to a known good entry, start there. |
193 | */ | 206 | */ |
194 | old = head; | 207 | old = head; |
195 | } | 208 | } |
196 | } | 209 | } |
197 | 210 | ||
198 | if (old != head) { | 211 | if (old != head) { |
199 | size_t size; | 212 | size_t size; |
200 | 213 | ||
201 | event = (union perf_event *)&data[old & md->mask]; | 214 | event = (union perf_event *)&data[old & md->mask]; |
202 | size = event->header.size; | 215 | size = event->header.size; |
203 | 216 | ||
204 | /* | 217 | /* |
205 | * Event straddles the mmap boundary -- header should always | 218 | * Event straddles the mmap boundary -- header should always |
206 | * be inside due to u64 alignment of output. | 219 | * be inside due to u64 alignment of output. |
207 | */ | 220 | */ |
208 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | 221 | if ((old & md->mask) + size != ((old + size) & md->mask)) { |
209 | unsigned int offset = old; | 222 | unsigned int offset = old; |
210 | unsigned int len = min(sizeof(*event), size), cpy; | 223 | unsigned int len = min(sizeof(*event), size), cpy; |
211 | void *dst = &evlist->event_copy; | 224 | void *dst = &evlist->event_copy; |
212 | 225 | ||
213 | do { | 226 | do { |
214 | cpy = min(md->mask + 1 - (offset & md->mask), len); | 227 | cpy = min(md->mask + 1 - (offset & md->mask), len); |
215 | memcpy(dst, &data[offset & md->mask], cpy); | 228 | memcpy(dst, &data[offset & md->mask], cpy); |
216 | offset += cpy; | 229 | offset += cpy; |
217 | dst += cpy; | 230 | dst += cpy; |
218 | len -= cpy; | 231 | len -= cpy; |
219 | } while (len); | 232 | } while (len); |
220 | 233 | ||
221 | event = &evlist->event_copy; | 234 | event = &evlist->event_copy; |
222 | } | 235 | } |
223 | 236 | ||
224 | old += size; | 237 | old += size; |
225 | } | 238 | } |
226 | 239 | ||
227 | md->prev = old; | 240 | md->prev = old; |
228 | 241 | ||
229 | if (!evlist->overwrite) | 242 | if (!evlist->overwrite) |
230 | perf_mmap__write_tail(md, old); | 243 | perf_mmap__write_tail(md, old); |
231 | 244 | ||
232 | return event; | 245 | return event; |
233 | } | 246 | } |
234 | 247 | ||
235 | void perf_evlist__munmap(struct perf_evlist *evlist) | 248 | void perf_evlist__munmap(struct perf_evlist *evlist) |
236 | { | 249 | { |
237 | int i; | 250 | int i; |
238 | 251 | ||
239 | for (i = 0; i < evlist->nr_mmaps; i++) { | 252 | for (i = 0; i < evlist->nr_mmaps; i++) { |
240 | if (evlist->mmap[i].base != NULL) { | 253 | if (evlist->mmap[i].base != NULL) { |
241 | munmap(evlist->mmap[i].base, evlist->mmap_len); | 254 | munmap(evlist->mmap[i].base, evlist->mmap_len); |
242 | evlist->mmap[i].base = NULL; | 255 | evlist->mmap[i].base = NULL; |
243 | } | 256 | } |
244 | } | 257 | } |
245 | 258 | ||
246 | free(evlist->mmap); | 259 | free(evlist->mmap); |
247 | evlist->mmap = NULL; | 260 | evlist->mmap = NULL; |
248 | } | 261 | } |
249 | 262 | ||
250 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | 263 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist) |
251 | { | 264 | { |
252 | evlist->nr_mmaps = evlist->cpus->nr; | 265 | evlist->nr_mmaps = evlist->cpus->nr; |
253 | if (evlist->cpus->map[0] == -1) | 266 | if (evlist->cpus->map[0] == -1) |
254 | evlist->nr_mmaps = evlist->threads->nr; | 267 | evlist->nr_mmaps = evlist->threads->nr; |
255 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); | 268 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); |
256 | return evlist->mmap != NULL ? 0 : -ENOMEM; | 269 | return evlist->mmap != NULL ? 0 : -ENOMEM; |
257 | } | 270 | } |
258 | 271 | ||
259 | static int __perf_evlist__mmap(struct perf_evlist *evlist, | 272 | static int __perf_evlist__mmap(struct perf_evlist *evlist, |
260 | int idx, int prot, int mask, int fd) | 273 | int idx, int prot, int mask, int fd) |
261 | { | 274 | { |
262 | evlist->mmap[idx].prev = 0; | 275 | evlist->mmap[idx].prev = 0; |
263 | evlist->mmap[idx].mask = mask; | 276 | evlist->mmap[idx].mask = mask; |
264 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, | 277 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, |
265 | MAP_SHARED, fd, 0); | 278 | MAP_SHARED, fd, 0); |
266 | if (evlist->mmap[idx].base == MAP_FAILED) | 279 | if (evlist->mmap[idx].base == MAP_FAILED) |
267 | return -1; | 280 | return -1; |
268 | 281 | ||
269 | perf_evlist__add_pollfd(evlist, fd); | 282 | perf_evlist__add_pollfd(evlist, fd); |
270 | return 0; | 283 | return 0; |
271 | } | 284 | } |
272 | 285 | ||
273 | static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) | 286 | static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) |
274 | { | 287 | { |
275 | struct perf_evsel *evsel; | 288 | struct perf_evsel *evsel; |
276 | int cpu, thread; | 289 | int cpu, thread; |
277 | 290 | ||
278 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 291 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { |
279 | int output = -1; | 292 | int output = -1; |
280 | 293 | ||
281 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 294 | for (thread = 0; thread < evlist->threads->nr; thread++) { |
282 | list_for_each_entry(evsel, &evlist->entries, node) { | 295 | list_for_each_entry(evsel, &evlist->entries, node) { |
283 | int fd = FD(evsel, cpu, thread); | 296 | int fd = FD(evsel, cpu, thread); |
284 | 297 | ||
285 | if (output == -1) { | 298 | if (output == -1) { |
286 | output = fd; | 299 | output = fd; |
287 | if (__perf_evlist__mmap(evlist, cpu, | 300 | if (__perf_evlist__mmap(evlist, cpu, |
288 | prot, mask, output) < 0) | 301 | prot, mask, output) < 0) |
289 | goto out_unmap; | 302 | goto out_unmap; |
290 | } else { | 303 | } else { |
291 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) | 304 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) |
292 | goto out_unmap; | 305 | goto out_unmap; |
293 | } | 306 | } |
294 | 307 | ||
295 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 308 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
296 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | 309 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) |
297 | goto out_unmap; | 310 | goto out_unmap; |
298 | } | 311 | } |
299 | } | 312 | } |
300 | } | 313 | } |
301 | 314 | ||
302 | return 0; | 315 | return 0; |
303 | 316 | ||
304 | out_unmap: | 317 | out_unmap: |
305 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 318 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { |
306 | if (evlist->mmap[cpu].base != NULL) { | 319 | if (evlist->mmap[cpu].base != NULL) { |
307 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | 320 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); |
308 | evlist->mmap[cpu].base = NULL; | 321 | evlist->mmap[cpu].base = NULL; |
309 | } | 322 | } |
310 | } | 323 | } |
311 | return -1; | 324 | return -1; |
312 | } | 325 | } |
313 | 326 | ||
314 | static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) | 327 | static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) |
315 | { | 328 | { |
316 | struct perf_evsel *evsel; | 329 | struct perf_evsel *evsel; |
317 | int thread; | 330 | int thread; |
318 | 331 | ||
319 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 332 | for (thread = 0; thread < evlist->threads->nr; thread++) { |
320 | int output = -1; | 333 | int output = -1; |
321 | 334 | ||
322 | list_for_each_entry(evsel, &evlist->entries, node) { | 335 | list_for_each_entry(evsel, &evlist->entries, node) { |
323 | int fd = FD(evsel, 0, thread); | 336 | int fd = FD(evsel, 0, thread); |
324 | 337 | ||
325 | if (output == -1) { | 338 | if (output == -1) { |
326 | output = fd; | 339 | output = fd; |
327 | if (__perf_evlist__mmap(evlist, thread, | 340 | if (__perf_evlist__mmap(evlist, thread, |
328 | prot, mask, output) < 0) | 341 | prot, mask, output) < 0) |
329 | goto out_unmap; | 342 | goto out_unmap; |
330 | } else { | 343 | } else { |
331 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) | 344 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) |
332 | goto out_unmap; | 345 | goto out_unmap; |
333 | } | 346 | } |
334 | 347 | ||
335 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 348 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
336 | perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0) | 349 | perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0) |
337 | goto out_unmap; | 350 | goto out_unmap; |
338 | } | 351 | } |
339 | } | 352 | } |
340 | 353 | ||
341 | return 0; | 354 | return 0; |
342 | 355 | ||
343 | out_unmap: | 356 | out_unmap: |
344 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 357 | for (thread = 0; thread < evlist->threads->nr; thread++) { |
345 | if (evlist->mmap[thread].base != NULL) { | 358 | if (evlist->mmap[thread].base != NULL) { |
346 | munmap(evlist->mmap[thread].base, evlist->mmap_len); | 359 | munmap(evlist->mmap[thread].base, evlist->mmap_len); |
347 | evlist->mmap[thread].base = NULL; | 360 | evlist->mmap[thread].base = NULL; |
348 | } | 361 | } |
349 | } | 362 | } |
350 | return -1; | 363 | return -1; |
351 | } | 364 | } |
352 | 365 | ||
353 | /** perf_evlist__mmap - Create per cpu maps to receive events | 366 | /** perf_evlist__mmap - Create per cpu maps to receive events |
354 | * | 367 | * |
355 | * @evlist - list of events | 368 | * @evlist - list of events |
356 | * @pages - map length in pages | 369 | * @pages - map length in pages |
357 | * @overwrite - overwrite older events? | 370 | * @overwrite - overwrite older events? |
358 | * | 371 | * |
359 | * If overwrite is false the user needs to signal event consuption using: | 372 | * If overwrite is false the user needs to signal event consuption using: |
360 | * | 373 | * |
361 | * struct perf_mmap *m = &evlist->mmap[cpu]; | 374 | * struct perf_mmap *m = &evlist->mmap[cpu]; |
362 | * unsigned int head = perf_mmap__read_head(m); | 375 | * unsigned int head = perf_mmap__read_head(m); |
363 | * | 376 | * |
364 | * perf_mmap__write_tail(m, head) | 377 | * perf_mmap__write_tail(m, head) |
365 | * | 378 | * |
366 | * Using perf_evlist__read_on_cpu does this automatically. | 379 | * Using perf_evlist__read_on_cpu does this automatically. |
367 | */ | 380 | */ |
368 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite) | 381 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite) |
369 | { | 382 | { |
370 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | 383 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); |
371 | int mask = pages * page_size - 1; | 384 | int mask = pages * page_size - 1; |
372 | struct perf_evsel *evsel; | 385 | struct perf_evsel *evsel; |
373 | const struct cpu_map *cpus = evlist->cpus; | 386 | const struct cpu_map *cpus = evlist->cpus; |
374 | const struct thread_map *threads = evlist->threads; | 387 | const struct thread_map *threads = evlist->threads; |
375 | int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); | 388 | int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); |
376 | 389 | ||
377 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | 390 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) |
378 | return -ENOMEM; | 391 | return -ENOMEM; |
379 | 392 | ||
380 | if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0) | 393 | if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0) |
381 | return -ENOMEM; | 394 | return -ENOMEM; |
382 | 395 | ||
383 | evlist->overwrite = overwrite; | 396 | evlist->overwrite = overwrite; |
384 | evlist->mmap_len = (pages + 1) * page_size; | 397 | evlist->mmap_len = (pages + 1) * page_size; |
385 | 398 | ||
386 | list_for_each_entry(evsel, &evlist->entries, node) { | 399 | list_for_each_entry(evsel, &evlist->entries, node) { |
387 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 400 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
388 | evsel->sample_id == NULL && | 401 | evsel->sample_id == NULL && |
389 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | 402 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) |
390 | return -ENOMEM; | 403 | return -ENOMEM; |
391 | } | 404 | } |
392 | 405 | ||
393 | if (evlist->cpus->map[0] == -1) | 406 | if (evlist->cpus->map[0] == -1) |
394 | return perf_evlist__mmap_per_thread(evlist, prot, mask); | 407 | return perf_evlist__mmap_per_thread(evlist, prot, mask); |
395 | 408 | ||
396 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 409 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
397 | } | 410 | } |
398 | 411 | ||
399 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | 412 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, |
400 | pid_t target_tid, const char *cpu_list) | 413 | pid_t target_tid, const char *cpu_list) |
401 | { | 414 | { |
402 | evlist->threads = thread_map__new(target_pid, target_tid); | 415 | evlist->threads = thread_map__new(target_pid, target_tid); |
403 | 416 | ||
404 | if (evlist->threads == NULL) | 417 | if (evlist->threads == NULL) |
405 | return -1; | 418 | return -1; |
406 | 419 | ||
407 | if (cpu_list == NULL && target_tid != -1) | 420 | if (cpu_list == NULL && target_tid != -1) |
408 | evlist->cpus = cpu_map__dummy_new(); | 421 | evlist->cpus = cpu_map__dummy_new(); |
409 | else | 422 | else |
410 | evlist->cpus = cpu_map__new(cpu_list); | 423 | evlist->cpus = cpu_map__new(cpu_list); |
411 | 424 | ||
412 | if (evlist->cpus == NULL) | 425 | if (evlist->cpus == NULL) |
413 | goto out_delete_threads; | 426 | goto out_delete_threads; |
414 | 427 | ||
415 | return 0; | 428 | return 0; |
416 | 429 | ||
417 | out_delete_threads: | 430 | out_delete_threads: |
418 | thread_map__delete(evlist->threads); | 431 | thread_map__delete(evlist->threads); |
419 | return -1; | 432 | return -1; |
420 | } | 433 | } |
421 | 434 | ||
422 | void perf_evlist__delete_maps(struct perf_evlist *evlist) | 435 | void perf_evlist__delete_maps(struct perf_evlist *evlist) |
423 | { | 436 | { |
424 | cpu_map__delete(evlist->cpus); | 437 | cpu_map__delete(evlist->cpus); |
425 | thread_map__delete(evlist->threads); | 438 | thread_map__delete(evlist->threads); |
426 | evlist->cpus = NULL; | 439 | evlist->cpus = NULL; |
427 | evlist->threads = NULL; | 440 | evlist->threads = NULL; |
428 | } | 441 | } |
429 | 442 | ||
430 | int perf_evlist__set_filters(struct perf_evlist *evlist) | 443 | int perf_evlist__set_filters(struct perf_evlist *evlist) |
431 | { | 444 | { |
432 | const struct thread_map *threads = evlist->threads; | 445 | const struct thread_map *threads = evlist->threads; |
433 | const struct cpu_map *cpus = evlist->cpus; | 446 | const struct cpu_map *cpus = evlist->cpus; |
434 | struct perf_evsel *evsel; | 447 | struct perf_evsel *evsel; |
435 | char *filter; | 448 | char *filter; |
436 | int thread; | 449 | int thread; |
437 | int cpu; | 450 | int cpu; |
438 | int err; | 451 | int err; |
439 | int fd; | 452 | int fd; |
440 | 453 | ||
441 | list_for_each_entry(evsel, &evlist->entries, node) { | 454 | list_for_each_entry(evsel, &evlist->entries, node) { |
442 | filter = evsel->filter; | 455 | filter = evsel->filter; |
443 | if (!filter) | 456 | if (!filter) |
444 | continue; | 457 | continue; |
445 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 458 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
446 | for (thread = 0; thread < threads->nr; thread++) { | 459 | for (thread = 0; thread < threads->nr; thread++) { |
447 | fd = FD(evsel, cpu, thread); | 460 | fd = FD(evsel, cpu, thread); |
448 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); | 461 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); |
449 | if (err) | 462 | if (err) |
450 | return err; | 463 | return err; |
451 | } | 464 | } |
452 | } | 465 | } |
453 | } | 466 | } |
454 | 467 | ||
455 | return 0; | 468 | return 0; |
456 | } | 469 | } |
457 | 470 | ||
458 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist) | 471 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist) |
459 | { | 472 | { |
460 | struct perf_evsel *pos, *first; | 473 | struct perf_evsel *pos, *first; |
461 | 474 | ||
462 | pos = first = list_entry(evlist->entries.next, struct perf_evsel, node); | 475 | pos = first = list_entry(evlist->entries.next, struct perf_evsel, node); |
463 | 476 | ||
464 | list_for_each_entry_continue(pos, &evlist->entries, node) { | 477 | list_for_each_entry_continue(pos, &evlist->entries, node) { |
465 | if (first->attr.sample_type != pos->attr.sample_type) | 478 | if (first->attr.sample_type != pos->attr.sample_type) |
466 | return false; | 479 | return false; |
467 | } | 480 | } |
468 | 481 | ||
469 | return true; | 482 | return true; |
470 | } | 483 | } |
471 | 484 | ||
472 | u64 perf_evlist__sample_type(const struct perf_evlist *evlist) | 485 | u64 perf_evlist__sample_type(const struct perf_evlist *evlist) |
473 | { | 486 | { |
474 | struct perf_evsel *first; | 487 | struct perf_evsel *first; |
475 | 488 | ||
476 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 489 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
477 | return first->attr.sample_type; | 490 | return first->attr.sample_type; |
478 | } | 491 | } |
479 | 492 | ||
480 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist) | 493 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist) |
481 | { | 494 | { |
482 | struct perf_evsel *pos, *first; | 495 | struct perf_evsel *pos, *first; |
483 | 496 | ||
484 | pos = first = list_entry(evlist->entries.next, struct perf_evsel, node); | 497 | pos = first = list_entry(evlist->entries.next, struct perf_evsel, node); |
485 | 498 | ||
486 | list_for_each_entry_continue(pos, &evlist->entries, node) { | 499 | list_for_each_entry_continue(pos, &evlist->entries, node) { |
487 | if (first->attr.sample_id_all != pos->attr.sample_id_all) | 500 | if (first->attr.sample_id_all != pos->attr.sample_id_all) |
488 | return false; | 501 | return false; |
489 | } | 502 | } |
490 | 503 | ||
491 | return true; | 504 | return true; |
492 | } | 505 | } |
493 | 506 | ||
494 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) | 507 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) |
495 | { | 508 | { |
496 | struct perf_evsel *first; | 509 | struct perf_evsel *first; |
497 | 510 | ||
498 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 511 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
499 | return first->attr.sample_id_all; | 512 | return first->attr.sample_id_all; |
500 | } | 513 | } |
501 | 514 |
tools/perf/util/evlist.h
1 | #ifndef __PERF_EVLIST_H | 1 | #ifndef __PERF_EVLIST_H |
2 | #define __PERF_EVLIST_H 1 | 2 | #define __PERF_EVLIST_H 1 |
3 | 3 | ||
4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
5 | #include "../perf.h" | 5 | #include "../perf.h" |
6 | #include "event.h" | 6 | #include "event.h" |
7 | 7 | ||
8 | struct pollfd; | 8 | struct pollfd; |
9 | struct thread_map; | 9 | struct thread_map; |
10 | struct cpu_map; | 10 | struct cpu_map; |
11 | 11 | ||
12 | #define PERF_EVLIST__HLIST_BITS 8 | 12 | #define PERF_EVLIST__HLIST_BITS 8 |
13 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | 13 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) |
14 | 14 | ||
15 | struct perf_evlist { | 15 | struct perf_evlist { |
16 | struct list_head entries; | 16 | struct list_head entries; |
17 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | 17 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; |
18 | int nr_entries; | 18 | int nr_entries; |
19 | int nr_fds; | 19 | int nr_fds; |
20 | int nr_mmaps; | 20 | int nr_mmaps; |
21 | int mmap_len; | 21 | int mmap_len; |
22 | bool overwrite; | 22 | bool overwrite; |
23 | union perf_event event_copy; | 23 | union perf_event event_copy; |
24 | struct perf_mmap *mmap; | 24 | struct perf_mmap *mmap; |
25 | struct pollfd *pollfd; | 25 | struct pollfd *pollfd; |
26 | struct thread_map *threads; | 26 | struct thread_map *threads; |
27 | struct cpu_map *cpus; | 27 | struct cpu_map *cpus; |
28 | }; | 28 | }; |
29 | 29 | ||
30 | struct perf_evsel; | 30 | struct perf_evsel; |
31 | 31 | ||
32 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 32 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, |
33 | struct thread_map *threads); | 33 | struct thread_map *threads); |
34 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | 34 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, |
35 | struct thread_map *threads); | 35 | struct thread_map *threads); |
36 | void perf_evlist__exit(struct perf_evlist *evlist); | 36 | void perf_evlist__exit(struct perf_evlist *evlist); |
37 | void perf_evlist__delete(struct perf_evlist *evlist); | 37 | void perf_evlist__delete(struct perf_evlist *evlist); |
38 | 38 | ||
39 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); | 39 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); |
40 | int perf_evlist__add_default(struct perf_evlist *evlist); | 40 | int perf_evlist__add_default(struct perf_evlist *evlist); |
41 | 41 | ||
42 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | 42 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, |
43 | int cpu, int thread, u64 id); | 43 | int cpu, int thread, u64 id); |
44 | 44 | ||
45 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); | 45 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); |
46 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | 46 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); |
47 | 47 | ||
48 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | 48 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); |
49 | 49 | ||
50 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 50 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); |
51 | 51 | ||
52 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); | 52 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); |
53 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); | 53 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); |
54 | void perf_evlist__munmap(struct perf_evlist *evlist); | 54 | void perf_evlist__munmap(struct perf_evlist *evlist); |
55 | 55 | ||
56 | void perf_evlist__disable(struct perf_evlist *evlist); | ||
57 | |||
56 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | 58 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, |
57 | struct cpu_map *cpus, | 59 | struct cpu_map *cpus, |
58 | struct thread_map *threads) | 60 | struct thread_map *threads) |
59 | { | 61 | { |
60 | evlist->cpus = cpus; | 62 | evlist->cpus = cpus; |
61 | evlist->threads = threads; | 63 | evlist->threads = threads; |
62 | } | 64 | } |
63 | 65 | ||
64 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | 66 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, |
65 | pid_t target_tid, const char *cpu_list); | 67 | pid_t target_tid, const char *cpu_list); |
66 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 68 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
67 | int perf_evlist__set_filters(struct perf_evlist *evlist); | 69 | int perf_evlist__set_filters(struct perf_evlist *evlist); |
68 | 70 | ||
69 | u64 perf_evlist__sample_type(const struct perf_evlist *evlist); | 71 | u64 perf_evlist__sample_type(const struct perf_evlist *evlist); |
70 | bool perf_evlist__sample_id_all(const const struct perf_evlist *evlist); | 72 | bool perf_evlist__sample_id_all(const const struct perf_evlist *evlist); |
71 | 73 | ||
72 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); | 74 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); |
73 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); | 75 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); |
74 | #endif /* __PERF_EVLIST_H */ | 76 | #endif /* __PERF_EVLIST_H */ |
75 | 77 |
tools/perf/util/header.c
1 | #define _FILE_OFFSET_BITS 64 | 1 | #define _FILE_OFFSET_BITS 64 |
2 | 2 | ||
3 | #include <sys/types.h> | 3 | #include <sys/types.h> |
4 | #include <byteswap.h> | 4 | #include <byteswap.h> |
5 | #include <unistd.h> | 5 | #include <unistd.h> |
6 | #include <stdio.h> | 6 | #include <stdio.h> |
7 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | 10 | ||
11 | #include "evlist.h" | 11 | #include "evlist.h" |
12 | #include "evsel.h" | 12 | #include "evsel.h" |
13 | #include "util.h" | 13 | #include "util.h" |
14 | #include "header.h" | 14 | #include "header.h" |
15 | #include "../perf.h" | 15 | #include "../perf.h" |
16 | #include "trace-event.h" | 16 | #include "trace-event.h" |
17 | #include "session.h" | 17 | #include "session.h" |
18 | #include "symbol.h" | 18 | #include "symbol.h" |
19 | #include "debug.h" | 19 | #include "debug.h" |
20 | 20 | ||
21 | static bool no_buildid_cache = false; | 21 | static bool no_buildid_cache = false; |
22 | 22 | ||
23 | static int event_count; | 23 | static int event_count; |
24 | static struct perf_trace_event_type *events; | 24 | static struct perf_trace_event_type *events; |
25 | 25 | ||
26 | int perf_header__push_event(u64 id, const char *name) | 26 | int perf_header__push_event(u64 id, const char *name) |
27 | { | 27 | { |
28 | if (strlen(name) > MAX_EVENT_NAME) | 28 | if (strlen(name) > MAX_EVENT_NAME) |
29 | pr_warning("Event %s will be truncated\n", name); | 29 | pr_warning("Event %s will be truncated\n", name); |
30 | 30 | ||
31 | if (!events) { | 31 | if (!events) { |
32 | events = malloc(sizeof(struct perf_trace_event_type)); | 32 | events = malloc(sizeof(struct perf_trace_event_type)); |
33 | if (events == NULL) | 33 | if (events == NULL) |
34 | return -ENOMEM; | 34 | return -ENOMEM; |
35 | } else { | 35 | } else { |
36 | struct perf_trace_event_type *nevents; | 36 | struct perf_trace_event_type *nevents; |
37 | 37 | ||
38 | nevents = realloc(events, (event_count + 1) * sizeof(*events)); | 38 | nevents = realloc(events, (event_count + 1) * sizeof(*events)); |
39 | if (nevents == NULL) | 39 | if (nevents == NULL) |
40 | return -ENOMEM; | 40 | return -ENOMEM; |
41 | events = nevents; | 41 | events = nevents; |
42 | } | 42 | } |
43 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); | 43 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); |
44 | events[event_count].event_id = id; | 44 | events[event_count].event_id = id; |
45 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); | 45 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); |
46 | event_count++; | 46 | event_count++; |
47 | return 0; | 47 | return 0; |
48 | } | 48 | } |
49 | 49 | ||
50 | char *perf_header__find_event(u64 id) | 50 | char *perf_header__find_event(u64 id) |
51 | { | 51 | { |
52 | int i; | 52 | int i; |
53 | for (i = 0 ; i < event_count; i++) { | 53 | for (i = 0 ; i < event_count; i++) { |
54 | if (events[i].event_id == id) | 54 | if (events[i].event_id == id) |
55 | return events[i].name; | 55 | return events[i].name; |
56 | } | 56 | } |
57 | return NULL; | 57 | return NULL; |
58 | } | 58 | } |
59 | 59 | ||
60 | static const char *__perf_magic = "PERFFILE"; | 60 | static const char *__perf_magic = "PERFFILE"; |
61 | 61 | ||
62 | #define PERF_MAGIC (*(u64 *)__perf_magic) | 62 | #define PERF_MAGIC (*(u64 *)__perf_magic) |
63 | 63 | ||
64 | struct perf_file_attr { | 64 | struct perf_file_attr { |
65 | struct perf_event_attr attr; | 65 | struct perf_event_attr attr; |
66 | struct perf_file_section ids; | 66 | struct perf_file_section ids; |
67 | }; | 67 | }; |
68 | 68 | ||
69 | void perf_header__set_feat(struct perf_header *header, int feat) | 69 | void perf_header__set_feat(struct perf_header *header, int feat) |
70 | { | 70 | { |
71 | set_bit(feat, header->adds_features); | 71 | set_bit(feat, header->adds_features); |
72 | } | 72 | } |
73 | 73 | ||
74 | void perf_header__clear_feat(struct perf_header *header, int feat) | 74 | void perf_header__clear_feat(struct perf_header *header, int feat) |
75 | { | 75 | { |
76 | clear_bit(feat, header->adds_features); | 76 | clear_bit(feat, header->adds_features); |
77 | } | 77 | } |
78 | 78 | ||
79 | bool perf_header__has_feat(const struct perf_header *header, int feat) | 79 | bool perf_header__has_feat(const struct perf_header *header, int feat) |
80 | { | 80 | { |
81 | return test_bit(feat, header->adds_features); | 81 | return test_bit(feat, header->adds_features); |
82 | } | 82 | } |
83 | 83 | ||
84 | static int do_write(int fd, const void *buf, size_t size) | 84 | static int do_write(int fd, const void *buf, size_t size) |
85 | { | 85 | { |
86 | while (size) { | 86 | while (size) { |
87 | int ret = write(fd, buf, size); | 87 | int ret = write(fd, buf, size); |
88 | 88 | ||
89 | if (ret < 0) | 89 | if (ret < 0) |
90 | return -errno; | 90 | return -errno; |
91 | 91 | ||
92 | size -= ret; | 92 | size -= ret; |
93 | buf += ret; | 93 | buf += ret; |
94 | } | 94 | } |
95 | 95 | ||
96 | return 0; | 96 | return 0; |
97 | } | 97 | } |
98 | 98 | ||
99 | #define NAME_ALIGN 64 | 99 | #define NAME_ALIGN 64 |
100 | 100 | ||
101 | static int write_padded(int fd, const void *bf, size_t count, | 101 | static int write_padded(int fd, const void *bf, size_t count, |
102 | size_t count_aligned) | 102 | size_t count_aligned) |
103 | { | 103 | { |
104 | static const char zero_buf[NAME_ALIGN]; | 104 | static const char zero_buf[NAME_ALIGN]; |
105 | int err = do_write(fd, bf, count); | 105 | int err = do_write(fd, bf, count); |
106 | 106 | ||
107 | if (!err) | 107 | if (!err) |
108 | err = do_write(fd, zero_buf, count_aligned - count); | 108 | err = do_write(fd, zero_buf, count_aligned - count); |
109 | 109 | ||
110 | return err; | 110 | return err; |
111 | } | 111 | } |
112 | 112 | ||
113 | #define dsos__for_each_with_build_id(pos, head) \ | 113 | #define dsos__for_each_with_build_id(pos, head) \ |
114 | list_for_each_entry(pos, head, node) \ | 114 | list_for_each_entry(pos, head, node) \ |
115 | if (!pos->has_build_id) \ | 115 | if (!pos->has_build_id) \ |
116 | continue; \ | 116 | continue; \ |
117 | else | 117 | else |
118 | 118 | ||
119 | static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | 119 | static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, |
120 | u16 misc, int fd) | 120 | u16 misc, int fd) |
121 | { | 121 | { |
122 | struct dso *pos; | 122 | struct dso *pos; |
123 | 123 | ||
124 | dsos__for_each_with_build_id(pos, head) { | 124 | dsos__for_each_with_build_id(pos, head) { |
125 | int err; | 125 | int err; |
126 | struct build_id_event b; | 126 | struct build_id_event b; |
127 | size_t len; | 127 | size_t len; |
128 | 128 | ||
129 | if (!pos->hit) | 129 | if (!pos->hit) |
130 | continue; | 130 | continue; |
131 | len = pos->long_name_len + 1; | 131 | len = pos->long_name_len + 1; |
132 | len = ALIGN(len, NAME_ALIGN); | 132 | len = ALIGN(len, NAME_ALIGN); |
133 | memset(&b, 0, sizeof(b)); | 133 | memset(&b, 0, sizeof(b)); |
134 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | 134 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); |
135 | b.pid = pid; | 135 | b.pid = pid; |
136 | b.header.misc = misc; | 136 | b.header.misc = misc; |
137 | b.header.size = sizeof(b) + len; | 137 | b.header.size = sizeof(b) + len; |
138 | err = do_write(fd, &b, sizeof(b)); | 138 | err = do_write(fd, &b, sizeof(b)); |
139 | if (err < 0) | 139 | if (err < 0) |
140 | return err; | 140 | return err; |
141 | err = write_padded(fd, pos->long_name, | 141 | err = write_padded(fd, pos->long_name, |
142 | pos->long_name_len + 1, len); | 142 | pos->long_name_len + 1, len); |
143 | if (err < 0) | 143 | if (err < 0) |
144 | return err; | 144 | return err; |
145 | } | 145 | } |
146 | 146 | ||
147 | return 0; | 147 | return 0; |
148 | } | 148 | } |
149 | 149 | ||
150 | static int machine__write_buildid_table(struct machine *machine, int fd) | 150 | static int machine__write_buildid_table(struct machine *machine, int fd) |
151 | { | 151 | { |
152 | int err; | 152 | int err; |
153 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | 153 | u16 kmisc = PERF_RECORD_MISC_KERNEL, |
154 | umisc = PERF_RECORD_MISC_USER; | 154 | umisc = PERF_RECORD_MISC_USER; |
155 | 155 | ||
156 | if (!machine__is_host(machine)) { | 156 | if (!machine__is_host(machine)) { |
157 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | 157 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; |
158 | umisc = PERF_RECORD_MISC_GUEST_USER; | 158 | umisc = PERF_RECORD_MISC_GUEST_USER; |
159 | } | 159 | } |
160 | 160 | ||
161 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid, | 161 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid, |
162 | kmisc, fd); | 162 | kmisc, fd); |
163 | if (err == 0) | 163 | if (err == 0) |
164 | err = __dsos__write_buildid_table(&machine->user_dsos, | 164 | err = __dsos__write_buildid_table(&machine->user_dsos, |
165 | machine->pid, umisc, fd); | 165 | machine->pid, umisc, fd); |
166 | return err; | 166 | return err; |
167 | } | 167 | } |
168 | 168 | ||
169 | static int dsos__write_buildid_table(struct perf_header *header, int fd) | 169 | static int dsos__write_buildid_table(struct perf_header *header, int fd) |
170 | { | 170 | { |
171 | struct perf_session *session = container_of(header, | 171 | struct perf_session *session = container_of(header, |
172 | struct perf_session, header); | 172 | struct perf_session, header); |
173 | struct rb_node *nd; | 173 | struct rb_node *nd; |
174 | int err = machine__write_buildid_table(&session->host_machine, fd); | 174 | int err = machine__write_buildid_table(&session->host_machine, fd); |
175 | 175 | ||
176 | if (err) | 176 | if (err) |
177 | return err; | 177 | return err; |
178 | 178 | ||
179 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 179 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { |
180 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 180 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
181 | err = machine__write_buildid_table(pos, fd); | 181 | err = machine__write_buildid_table(pos, fd); |
182 | if (err) | 182 | if (err) |
183 | break; | 183 | break; |
184 | } | 184 | } |
185 | return err; | 185 | return err; |
186 | } | 186 | } |
187 | 187 | ||
188 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | 188 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
189 | const char *name, bool is_kallsyms) | 189 | const char *name, bool is_kallsyms) |
190 | { | 190 | { |
191 | const size_t size = PATH_MAX; | 191 | const size_t size = PATH_MAX; |
192 | char *realname, *filename = malloc(size), | 192 | char *realname, *filename = zalloc(size), |
193 | *linkname = malloc(size), *targetname; | 193 | *linkname = zalloc(size), *targetname; |
194 | int len, err = -1; | 194 | int len, err = -1; |
195 | 195 | ||
196 | if (is_kallsyms) { | 196 | if (is_kallsyms) { |
197 | if (symbol_conf.kptr_restrict) { | 197 | if (symbol_conf.kptr_restrict) { |
198 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | 198 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); |
199 | return 0; | 199 | return 0; |
200 | } | 200 | } |
201 | realname = (char *)name; | 201 | realname = (char *)name; |
202 | } else | 202 | } else |
203 | realname = realpath(name, NULL); | 203 | realname = realpath(name, NULL); |
204 | 204 | ||
205 | if (realname == NULL || filename == NULL || linkname == NULL) | 205 | if (realname == NULL || filename == NULL || linkname == NULL) |
206 | goto out_free; | 206 | goto out_free; |
207 | 207 | ||
208 | len = snprintf(filename, size, "%s%s%s", | 208 | len = snprintf(filename, size, "%s%s%s", |
209 | debugdir, is_kallsyms ? "/" : "", realname); | 209 | debugdir, is_kallsyms ? "/" : "", realname); |
210 | if (mkdir_p(filename, 0755)) | 210 | if (mkdir_p(filename, 0755)) |
211 | goto out_free; | 211 | goto out_free; |
212 | 212 | ||
213 | snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); | 213 | snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); |
214 | 214 | ||
215 | if (access(filename, F_OK)) { | 215 | if (access(filename, F_OK)) { |
216 | if (is_kallsyms) { | 216 | if (is_kallsyms) { |
217 | if (copyfile("/proc/kallsyms", filename)) | 217 | if (copyfile("/proc/kallsyms", filename)) |
218 | goto out_free; | 218 | goto out_free; |
219 | } else if (link(realname, filename) && copyfile(name, filename)) | 219 | } else if (link(realname, filename) && copyfile(name, filename)) |
220 | goto out_free; | 220 | goto out_free; |
221 | } | 221 | } |
222 | 222 | ||
223 | len = snprintf(linkname, size, "%s/.build-id/%.2s", | 223 | len = snprintf(linkname, size, "%s/.build-id/%.2s", |
224 | debugdir, sbuild_id); | 224 | debugdir, sbuild_id); |
225 | 225 | ||
226 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | 226 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) |
227 | goto out_free; | 227 | goto out_free; |
228 | 228 | ||
229 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | 229 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); |
230 | targetname = filename + strlen(debugdir) - 5; | 230 | targetname = filename + strlen(debugdir) - 5; |
231 | memcpy(targetname, "../..", 5); | 231 | memcpy(targetname, "../..", 5); |
232 | 232 | ||
233 | if (symlink(targetname, linkname) == 0) | 233 | if (symlink(targetname, linkname) == 0) |
234 | err = 0; | 234 | err = 0; |
235 | out_free: | 235 | out_free: |
236 | if (!is_kallsyms) | 236 | if (!is_kallsyms) |
237 | free(realname); | 237 | free(realname); |
238 | free(filename); | 238 | free(filename); |
239 | free(linkname); | 239 | free(linkname); |
240 | return err; | 240 | return err; |
241 | } | 241 | } |
242 | 242 | ||
243 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | 243 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, |
244 | const char *name, const char *debugdir, | 244 | const char *name, const char *debugdir, |
245 | bool is_kallsyms) | 245 | bool is_kallsyms) |
246 | { | 246 | { |
247 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 247 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
248 | 248 | ||
249 | build_id__sprintf(build_id, build_id_size, sbuild_id); | 249 | build_id__sprintf(build_id, build_id_size, sbuild_id); |
250 | 250 | ||
251 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); | 251 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); |
252 | } | 252 | } |
253 | 253 | ||
254 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | 254 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) |
255 | { | 255 | { |
256 | const size_t size = PATH_MAX; | 256 | const size_t size = PATH_MAX; |
257 | char *filename = malloc(size), | 257 | char *filename = zalloc(size), |
258 | *linkname = malloc(size); | 258 | *linkname = zalloc(size); |
259 | int err = -1; | 259 | int err = -1; |
260 | 260 | ||
261 | if (filename == NULL || linkname == NULL) | 261 | if (filename == NULL || linkname == NULL) |
262 | goto out_free; | 262 | goto out_free; |
263 | 263 | ||
264 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | 264 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", |
265 | debugdir, sbuild_id, sbuild_id + 2); | 265 | debugdir, sbuild_id, sbuild_id + 2); |
266 | 266 | ||
267 | if (access(linkname, F_OK)) | 267 | if (access(linkname, F_OK)) |
268 | goto out_free; | 268 | goto out_free; |
269 | 269 | ||
270 | if (readlink(linkname, filename, size) < 0) | 270 | if (readlink(linkname, filename, size) < 0) |
271 | goto out_free; | 271 | goto out_free; |
272 | 272 | ||
273 | if (unlink(linkname)) | 273 | if (unlink(linkname)) |
274 | goto out_free; | 274 | goto out_free; |
275 | 275 | ||
276 | /* | 276 | /* |
277 | * Since the link is relative, we must make it absolute: | 277 | * Since the link is relative, we must make it absolute: |
278 | */ | 278 | */ |
279 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | 279 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", |
280 | debugdir, sbuild_id, filename); | 280 | debugdir, sbuild_id, filename); |
281 | 281 | ||
282 | if (unlink(linkname)) | 282 | if (unlink(linkname)) |
283 | goto out_free; | 283 | goto out_free; |
284 | 284 | ||
285 | err = 0; | 285 | err = 0; |
286 | out_free: | 286 | out_free: |
287 | free(filename); | 287 | free(filename); |
288 | free(linkname); | 288 | free(linkname); |
289 | return err; | 289 | return err; |
290 | } | 290 | } |
291 | 291 | ||
292 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) | 292 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) |
293 | { | 293 | { |
294 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | 294 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; |
295 | 295 | ||
296 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), | 296 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), |
297 | dso->long_name, debugdir, is_kallsyms); | 297 | dso->long_name, debugdir, is_kallsyms); |
298 | } | 298 | } |
299 | 299 | ||
300 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | 300 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) |
301 | { | 301 | { |
302 | struct dso *pos; | 302 | struct dso *pos; |
303 | int err = 0; | 303 | int err = 0; |
304 | 304 | ||
305 | dsos__for_each_with_build_id(pos, head) | 305 | dsos__for_each_with_build_id(pos, head) |
306 | if (dso__cache_build_id(pos, debugdir)) | 306 | if (dso__cache_build_id(pos, debugdir)) |
307 | err = -1; | 307 | err = -1; |
308 | 308 | ||
309 | return err; | 309 | return err; |
310 | } | 310 | } |
311 | 311 | ||
312 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | 312 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) |
313 | { | 313 | { |
314 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir); | 314 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir); |
315 | ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir); | 315 | ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir); |
316 | return ret; | 316 | return ret; |
317 | } | 317 | } |
318 | 318 | ||
319 | static int perf_session__cache_build_ids(struct perf_session *session) | 319 | static int perf_session__cache_build_ids(struct perf_session *session) |
320 | { | 320 | { |
321 | struct rb_node *nd; | 321 | struct rb_node *nd; |
322 | int ret; | 322 | int ret; |
323 | char debugdir[PATH_MAX]; | 323 | char debugdir[PATH_MAX]; |
324 | 324 | ||
325 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); | 325 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); |
326 | 326 | ||
327 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | 327 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) |
328 | return -1; | 328 | return -1; |
329 | 329 | ||
330 | ret = machine__cache_build_ids(&session->host_machine, debugdir); | 330 | ret = machine__cache_build_ids(&session->host_machine, debugdir); |
331 | 331 | ||
332 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 332 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { |
333 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 333 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
334 | ret |= machine__cache_build_ids(pos, debugdir); | 334 | ret |= machine__cache_build_ids(pos, debugdir); |
335 | } | 335 | } |
336 | return ret ? -1 : 0; | 336 | return ret ? -1 : 0; |
337 | } | 337 | } |
338 | 338 | ||
339 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | 339 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) |
340 | { | 340 | { |
341 | bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits); | 341 | bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits); |
342 | ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits); | 342 | ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits); |
343 | return ret; | 343 | return ret; |
344 | } | 344 | } |
345 | 345 | ||
346 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | 346 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) |
347 | { | 347 | { |
348 | struct rb_node *nd; | 348 | struct rb_node *nd; |
349 | bool ret = machine__read_build_ids(&session->host_machine, with_hits); | 349 | bool ret = machine__read_build_ids(&session->host_machine, with_hits); |
350 | 350 | ||
351 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 351 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { |
352 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 352 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
353 | ret |= machine__read_build_ids(pos, with_hits); | 353 | ret |= machine__read_build_ids(pos, with_hits); |
354 | } | 354 | } |
355 | 355 | ||
356 | return ret; | 356 | return ret; |
357 | } | 357 | } |
358 | 358 | ||
359 | static int perf_header__adds_write(struct perf_header *header, | 359 | static int perf_header__adds_write(struct perf_header *header, |
360 | struct perf_evlist *evlist, int fd) | 360 | struct perf_evlist *evlist, int fd) |
361 | { | 361 | { |
362 | int nr_sections; | 362 | int nr_sections; |
363 | struct perf_session *session; | 363 | struct perf_session *session; |
364 | struct perf_file_section *feat_sec; | 364 | struct perf_file_section *feat_sec; |
365 | int sec_size; | 365 | int sec_size; |
366 | u64 sec_start; | 366 | u64 sec_start; |
367 | int idx = 0, err; | 367 | int idx = 0, err; |
368 | 368 | ||
369 | session = container_of(header, struct perf_session, header); | 369 | session = container_of(header, struct perf_session, header); |
370 | 370 | ||
371 | if (perf_header__has_feat(header, HEADER_BUILD_ID && | 371 | if (perf_header__has_feat(header, HEADER_BUILD_ID && |
372 | !perf_session__read_build_ids(session, true))) | 372 | !perf_session__read_build_ids(session, true))) |
373 | perf_header__clear_feat(header, HEADER_BUILD_ID); | 373 | perf_header__clear_feat(header, HEADER_BUILD_ID); |
374 | 374 | ||
375 | nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); | 375 | nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); |
376 | if (!nr_sections) | 376 | if (!nr_sections) |
377 | return 0; | 377 | return 0; |
378 | 378 | ||
379 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | 379 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); |
380 | if (feat_sec == NULL) | 380 | if (feat_sec == NULL) |
381 | return -ENOMEM; | 381 | return -ENOMEM; |
382 | 382 | ||
383 | sec_size = sizeof(*feat_sec) * nr_sections; | 383 | sec_size = sizeof(*feat_sec) * nr_sections; |
384 | 384 | ||
385 | sec_start = header->data_offset + header->data_size; | 385 | sec_start = header->data_offset + header->data_size; |
386 | lseek(fd, sec_start + sec_size, SEEK_SET); | 386 | lseek(fd, sec_start + sec_size, SEEK_SET); |
387 | 387 | ||
388 | if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { | 388 | if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { |
389 | struct perf_file_section *trace_sec; | 389 | struct perf_file_section *trace_sec; |
390 | 390 | ||
391 | trace_sec = &feat_sec[idx++]; | 391 | trace_sec = &feat_sec[idx++]; |
392 | 392 | ||
393 | /* Write trace info */ | 393 | /* Write trace info */ |
394 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 394 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); |
395 | read_tracing_data(fd, &evlist->entries); | 395 | read_tracing_data(fd, &evlist->entries); |
396 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 396 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; |
397 | } | 397 | } |
398 | 398 | ||
399 | if (perf_header__has_feat(header, HEADER_BUILD_ID)) { | 399 | if (perf_header__has_feat(header, HEADER_BUILD_ID)) { |
400 | struct perf_file_section *buildid_sec; | 400 | struct perf_file_section *buildid_sec; |
401 | 401 | ||
402 | buildid_sec = &feat_sec[idx++]; | 402 | buildid_sec = &feat_sec[idx++]; |
403 | 403 | ||
404 | /* Write build-ids */ | 404 | /* Write build-ids */ |
405 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | 405 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); |
406 | err = dsos__write_buildid_table(header, fd); | 406 | err = dsos__write_buildid_table(header, fd); |
407 | if (err < 0) { | 407 | if (err < 0) { |
408 | pr_debug("failed to write buildid table\n"); | 408 | pr_debug("failed to write buildid table\n"); |
409 | goto out_free; | 409 | goto out_free; |
410 | } | 410 | } |
411 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | 411 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - |
412 | buildid_sec->offset; | 412 | buildid_sec->offset; |
413 | if (!no_buildid_cache) | 413 | if (!no_buildid_cache) |
414 | perf_session__cache_build_ids(session); | 414 | perf_session__cache_build_ids(session); |
415 | } | 415 | } |
416 | 416 | ||
417 | lseek(fd, sec_start, SEEK_SET); | 417 | lseek(fd, sec_start, SEEK_SET); |
418 | err = do_write(fd, feat_sec, sec_size); | 418 | err = do_write(fd, feat_sec, sec_size); |
419 | if (err < 0) | 419 | if (err < 0) |
420 | pr_debug("failed to write feature section\n"); | 420 | pr_debug("failed to write feature section\n"); |
421 | out_free: | 421 | out_free: |
422 | free(feat_sec); | 422 | free(feat_sec); |
423 | return err; | 423 | return err; |
424 | } | 424 | } |
425 | 425 | ||
426 | int perf_header__write_pipe(int fd) | 426 | int perf_header__write_pipe(int fd) |
427 | { | 427 | { |
428 | struct perf_pipe_file_header f_header; | 428 | struct perf_pipe_file_header f_header; |
429 | int err; | 429 | int err; |
430 | 430 | ||
431 | f_header = (struct perf_pipe_file_header){ | 431 | f_header = (struct perf_pipe_file_header){ |
432 | .magic = PERF_MAGIC, | 432 | .magic = PERF_MAGIC, |
433 | .size = sizeof(f_header), | 433 | .size = sizeof(f_header), |
434 | }; | 434 | }; |
435 | 435 | ||
436 | err = do_write(fd, &f_header, sizeof(f_header)); | 436 | err = do_write(fd, &f_header, sizeof(f_header)); |
437 | if (err < 0) { | 437 | if (err < 0) { |
438 | pr_debug("failed to write perf pipe header\n"); | 438 | pr_debug("failed to write perf pipe header\n"); |
439 | return err; | 439 | return err; |
440 | } | 440 | } |
441 | 441 | ||
442 | return 0; | 442 | return 0; |
443 | } | 443 | } |
444 | 444 | ||
445 | int perf_session__write_header(struct perf_session *session, | 445 | int perf_session__write_header(struct perf_session *session, |
446 | struct perf_evlist *evlist, | 446 | struct perf_evlist *evlist, |
447 | int fd, bool at_exit) | 447 | int fd, bool at_exit) |
448 | { | 448 | { |
449 | struct perf_file_header f_header; | 449 | struct perf_file_header f_header; |
450 | struct perf_file_attr f_attr; | 450 | struct perf_file_attr f_attr; |
451 | struct perf_header *header = &session->header; | 451 | struct perf_header *header = &session->header; |
452 | struct perf_evsel *attr, *pair = NULL; | 452 | struct perf_evsel *attr, *pair = NULL; |
453 | int err; | 453 | int err; |
454 | 454 | ||
455 | lseek(fd, sizeof(f_header), SEEK_SET); | 455 | lseek(fd, sizeof(f_header), SEEK_SET); |
456 | 456 | ||
457 | if (session->evlist != evlist) | 457 | if (session->evlist != evlist) |
458 | pair = list_entry(session->evlist->entries.next, struct perf_evsel, node); | 458 | pair = list_entry(session->evlist->entries.next, struct perf_evsel, node); |
459 | 459 | ||
460 | list_for_each_entry(attr, &evlist->entries, node) { | 460 | list_for_each_entry(attr, &evlist->entries, node) { |
461 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 461 | attr->id_offset = lseek(fd, 0, SEEK_CUR); |
462 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); | 462 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); |
463 | if (err < 0) { | 463 | if (err < 0) { |
464 | out_err_write: | 464 | out_err_write: |
465 | pr_debug("failed to write perf header\n"); | 465 | pr_debug("failed to write perf header\n"); |
466 | return err; | 466 | return err; |
467 | } | 467 | } |
468 | if (session->evlist != evlist) { | 468 | if (session->evlist != evlist) { |
469 | err = do_write(fd, pair->id, pair->ids * sizeof(u64)); | 469 | err = do_write(fd, pair->id, pair->ids * sizeof(u64)); |
470 | if (err < 0) | 470 | if (err < 0) |
471 | goto out_err_write; | 471 | goto out_err_write; |
472 | attr->ids += pair->ids; | 472 | attr->ids += pair->ids; |
473 | pair = list_entry(pair->node.next, struct perf_evsel, node); | 473 | pair = list_entry(pair->node.next, struct perf_evsel, node); |
474 | } | 474 | } |
475 | } | 475 | } |
476 | 476 | ||
477 | header->attr_offset = lseek(fd, 0, SEEK_CUR); | 477 | header->attr_offset = lseek(fd, 0, SEEK_CUR); |
478 | 478 | ||
479 | list_for_each_entry(attr, &evlist->entries, node) { | 479 | list_for_each_entry(attr, &evlist->entries, node) { |
480 | f_attr = (struct perf_file_attr){ | 480 | f_attr = (struct perf_file_attr){ |
481 | .attr = attr->attr, | 481 | .attr = attr->attr, |
482 | .ids = { | 482 | .ids = { |
483 | .offset = attr->id_offset, | 483 | .offset = attr->id_offset, |
484 | .size = attr->ids * sizeof(u64), | 484 | .size = attr->ids * sizeof(u64), |
485 | } | 485 | } |
486 | }; | 486 | }; |
487 | err = do_write(fd, &f_attr, sizeof(f_attr)); | 487 | err = do_write(fd, &f_attr, sizeof(f_attr)); |
488 | if (err < 0) { | 488 | if (err < 0) { |
489 | pr_debug("failed to write perf header attribute\n"); | 489 | pr_debug("failed to write perf header attribute\n"); |
490 | return err; | 490 | return err; |
491 | } | 491 | } |
492 | } | 492 | } |
493 | 493 | ||
494 | header->event_offset = lseek(fd, 0, SEEK_CUR); | 494 | header->event_offset = lseek(fd, 0, SEEK_CUR); |
495 | header->event_size = event_count * sizeof(struct perf_trace_event_type); | 495 | header->event_size = event_count * sizeof(struct perf_trace_event_type); |
496 | if (events) { | 496 | if (events) { |
497 | err = do_write(fd, events, header->event_size); | 497 | err = do_write(fd, events, header->event_size); |
498 | if (err < 0) { | 498 | if (err < 0) { |
499 | pr_debug("failed to write perf header events\n"); | 499 | pr_debug("failed to write perf header events\n"); |
500 | return err; | 500 | return err; |
501 | } | 501 | } |
502 | } | 502 | } |
503 | 503 | ||
504 | header->data_offset = lseek(fd, 0, SEEK_CUR); | 504 | header->data_offset = lseek(fd, 0, SEEK_CUR); |
505 | 505 | ||
506 | if (at_exit) { | 506 | if (at_exit) { |
507 | err = perf_header__adds_write(header, evlist, fd); | 507 | err = perf_header__adds_write(header, evlist, fd); |
508 | if (err < 0) | 508 | if (err < 0) |
509 | return err; | 509 | return err; |
510 | } | 510 | } |
511 | 511 | ||
512 | f_header = (struct perf_file_header){ | 512 | f_header = (struct perf_file_header){ |
513 | .magic = PERF_MAGIC, | 513 | .magic = PERF_MAGIC, |
514 | .size = sizeof(f_header), | 514 | .size = sizeof(f_header), |
515 | .attr_size = sizeof(f_attr), | 515 | .attr_size = sizeof(f_attr), |
516 | .attrs = { | 516 | .attrs = { |
517 | .offset = header->attr_offset, | 517 | .offset = header->attr_offset, |
518 | .size = evlist->nr_entries * sizeof(f_attr), | 518 | .size = evlist->nr_entries * sizeof(f_attr), |
519 | }, | 519 | }, |
520 | .data = { | 520 | .data = { |
521 | .offset = header->data_offset, | 521 | .offset = header->data_offset, |
522 | .size = header->data_size, | 522 | .size = header->data_size, |
523 | }, | 523 | }, |
524 | .event_types = { | 524 | .event_types = { |
525 | .offset = header->event_offset, | 525 | .offset = header->event_offset, |
526 | .size = header->event_size, | 526 | .size = header->event_size, |
527 | }, | 527 | }, |
528 | }; | 528 | }; |
529 | 529 | ||
530 | memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features)); | 530 | memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features)); |
531 | 531 | ||
532 | lseek(fd, 0, SEEK_SET); | 532 | lseek(fd, 0, SEEK_SET); |
533 | err = do_write(fd, &f_header, sizeof(f_header)); | 533 | err = do_write(fd, &f_header, sizeof(f_header)); |
534 | if (err < 0) { | 534 | if (err < 0) { |
535 | pr_debug("failed to write perf header\n"); | 535 | pr_debug("failed to write perf header\n"); |
536 | return err; | 536 | return err; |
537 | } | 537 | } |
538 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); | 538 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); |
539 | 539 | ||
540 | header->frozen = 1; | 540 | header->frozen = 1; |
541 | return 0; | 541 | return 0; |
542 | } | 542 | } |
543 | 543 | ||
544 | static int perf_header__getbuffer64(struct perf_header *header, | 544 | static int perf_header__getbuffer64(struct perf_header *header, |
545 | int fd, void *buf, size_t size) | 545 | int fd, void *buf, size_t size) |
546 | { | 546 | { |
547 | if (readn(fd, buf, size) <= 0) | 547 | if (readn(fd, buf, size) <= 0) |
548 | return -1; | 548 | return -1; |
549 | 549 | ||
550 | if (header->needs_swap) | 550 | if (header->needs_swap) |
551 | mem_bswap_64(buf, size); | 551 | mem_bswap_64(buf, size); |
552 | 552 | ||
553 | return 0; | 553 | return 0; |
554 | } | 554 | } |
555 | 555 | ||
556 | int perf_header__process_sections(struct perf_header *header, int fd, | 556 | int perf_header__process_sections(struct perf_header *header, int fd, |
557 | int (*process)(struct perf_file_section *section, | 557 | int (*process)(struct perf_file_section *section, |
558 | struct perf_header *ph, | 558 | struct perf_header *ph, |
559 | int feat, int fd)) | 559 | int feat, int fd)) |
560 | { | 560 | { |
561 | struct perf_file_section *feat_sec; | 561 | struct perf_file_section *feat_sec; |
562 | int nr_sections; | 562 | int nr_sections; |
563 | int sec_size; | 563 | int sec_size; |
564 | int idx = 0; | 564 | int idx = 0; |
565 | int err = -1, feat = 1; | 565 | int err = -1, feat = 1; |
566 | 566 | ||
567 | nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); | 567 | nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); |
568 | if (!nr_sections) | 568 | if (!nr_sections) |
569 | return 0; | 569 | return 0; |
570 | 570 | ||
571 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | 571 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); |
572 | if (!feat_sec) | 572 | if (!feat_sec) |
573 | return -1; | 573 | return -1; |
574 | 574 | ||
575 | sec_size = sizeof(*feat_sec) * nr_sections; | 575 | sec_size = sizeof(*feat_sec) * nr_sections; |
576 | 576 | ||
577 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); | 577 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); |
578 | 578 | ||
579 | if (perf_header__getbuffer64(header, fd, feat_sec, sec_size)) | 579 | if (perf_header__getbuffer64(header, fd, feat_sec, sec_size)) |
580 | goto out_free; | 580 | goto out_free; |
581 | 581 | ||
582 | err = 0; | 582 | err = 0; |
583 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | 583 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { |
584 | if (perf_header__has_feat(header, feat)) { | 584 | if (perf_header__has_feat(header, feat)) { |
585 | struct perf_file_section *sec = &feat_sec[idx++]; | 585 | struct perf_file_section *sec = &feat_sec[idx++]; |
586 | 586 | ||
587 | err = process(sec, header, feat, fd); | 587 | err = process(sec, header, feat, fd); |
588 | if (err < 0) | 588 | if (err < 0) |
589 | break; | 589 | break; |
590 | } | 590 | } |
591 | ++feat; | 591 | ++feat; |
592 | } | 592 | } |
593 | out_free: | 593 | out_free: |
594 | free(feat_sec); | 594 | free(feat_sec); |
595 | return err; | 595 | return err; |
596 | } | 596 | } |
597 | 597 | ||
598 | int perf_file_header__read(struct perf_file_header *header, | 598 | int perf_file_header__read(struct perf_file_header *header, |
599 | struct perf_header *ph, int fd) | 599 | struct perf_header *ph, int fd) |
600 | { | 600 | { |
601 | lseek(fd, 0, SEEK_SET); | 601 | lseek(fd, 0, SEEK_SET); |
602 | 602 | ||
603 | if (readn(fd, header, sizeof(*header)) <= 0 || | 603 | if (readn(fd, header, sizeof(*header)) <= 0 || |
604 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) | 604 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) |
605 | return -1; | 605 | return -1; |
606 | 606 | ||
607 | if (header->attr_size != sizeof(struct perf_file_attr)) { | 607 | if (header->attr_size != sizeof(struct perf_file_attr)) { |
608 | u64 attr_size = bswap_64(header->attr_size); | 608 | u64 attr_size = bswap_64(header->attr_size); |
609 | 609 | ||
610 | if (attr_size != sizeof(struct perf_file_attr)) | 610 | if (attr_size != sizeof(struct perf_file_attr)) |
611 | return -1; | 611 | return -1; |
612 | 612 | ||
613 | mem_bswap_64(header, offsetof(struct perf_file_header, | 613 | mem_bswap_64(header, offsetof(struct perf_file_header, |
614 | adds_features)); | 614 | adds_features)); |
615 | ph->needs_swap = true; | 615 | ph->needs_swap = true; |
616 | } | 616 | } |
617 | 617 | ||
618 | if (header->size != sizeof(*header)) { | 618 | if (header->size != sizeof(*header)) { |
619 | /* Support the previous format */ | 619 | /* Support the previous format */ |
620 | if (header->size == offsetof(typeof(*header), adds_features)) | 620 | if (header->size == offsetof(typeof(*header), adds_features)) |
621 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | 621 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); |
622 | else | 622 | else |
623 | return -1; | 623 | return -1; |
624 | } | 624 | } |
625 | 625 | ||
626 | memcpy(&ph->adds_features, &header->adds_features, | 626 | memcpy(&ph->adds_features, &header->adds_features, |
627 | sizeof(ph->adds_features)); | 627 | sizeof(ph->adds_features)); |
628 | /* | 628 | /* |
629 | * FIXME: hack that assumes that if we need swap the perf.data file | 629 | * FIXME: hack that assumes that if we need swap the perf.data file |
630 | * may be coming from an arch with a different word-size, ergo different | 630 | * may be coming from an arch with a different word-size, ergo different |
631 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | 631 | * DEFINE_BITMAP format, investigate more later, but for now its mostly |
632 | * safe to assume that we have a build-id section. Trace files probably | 632 | * safe to assume that we have a build-id section. Trace files probably |
633 | * have several other issues in this realm anyway... | 633 | * have several other issues in this realm anyway... |
634 | */ | 634 | */ |
635 | if (ph->needs_swap) { | 635 | if (ph->needs_swap) { |
636 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | 636 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); |
637 | perf_header__set_feat(ph, HEADER_BUILD_ID); | 637 | perf_header__set_feat(ph, HEADER_BUILD_ID); |
638 | } | 638 | } |
639 | 639 | ||
640 | ph->event_offset = header->event_types.offset; | 640 | ph->event_offset = header->event_types.offset; |
641 | ph->event_size = header->event_types.size; | 641 | ph->event_size = header->event_types.size; |
642 | ph->data_offset = header->data.offset; | 642 | ph->data_offset = header->data.offset; |
643 | ph->data_size = header->data.size; | 643 | ph->data_size = header->data.size; |
644 | return 0; | 644 | return 0; |
645 | } | 645 | } |
646 | 646 | ||
647 | static int __event_process_build_id(struct build_id_event *bev, | 647 | static int __event_process_build_id(struct build_id_event *bev, |
648 | char *filename, | 648 | char *filename, |
649 | struct perf_session *session) | 649 | struct perf_session *session) |
650 | { | 650 | { |
651 | int err = -1; | 651 | int err = -1; |
652 | struct list_head *head; | 652 | struct list_head *head; |
653 | struct machine *machine; | 653 | struct machine *machine; |
654 | u16 misc; | 654 | u16 misc; |
655 | struct dso *dso; | 655 | struct dso *dso; |
656 | enum dso_kernel_type dso_type; | 656 | enum dso_kernel_type dso_type; |
657 | 657 | ||
658 | machine = perf_session__findnew_machine(session, bev->pid); | 658 | machine = perf_session__findnew_machine(session, bev->pid); |
659 | if (!machine) | 659 | if (!machine) |
660 | goto out; | 660 | goto out; |
661 | 661 | ||
662 | misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 662 | misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
663 | 663 | ||
664 | switch (misc) { | 664 | switch (misc) { |
665 | case PERF_RECORD_MISC_KERNEL: | 665 | case PERF_RECORD_MISC_KERNEL: |
666 | dso_type = DSO_TYPE_KERNEL; | 666 | dso_type = DSO_TYPE_KERNEL; |
667 | head = &machine->kernel_dsos; | 667 | head = &machine->kernel_dsos; |
668 | break; | 668 | break; |
669 | case PERF_RECORD_MISC_GUEST_KERNEL: | 669 | case PERF_RECORD_MISC_GUEST_KERNEL: |
670 | dso_type = DSO_TYPE_GUEST_KERNEL; | 670 | dso_type = DSO_TYPE_GUEST_KERNEL; |
671 | head = &machine->kernel_dsos; | 671 | head = &machine->kernel_dsos; |
672 | break; | 672 | break; |
673 | case PERF_RECORD_MISC_USER: | 673 | case PERF_RECORD_MISC_USER: |
674 | case PERF_RECORD_MISC_GUEST_USER: | 674 | case PERF_RECORD_MISC_GUEST_USER: |
675 | dso_type = DSO_TYPE_USER; | 675 | dso_type = DSO_TYPE_USER; |
676 | head = &machine->user_dsos; | 676 | head = &machine->user_dsos; |
677 | break; | 677 | break; |
678 | default: | 678 | default: |
679 | goto out; | 679 | goto out; |
680 | } | 680 | } |
681 | 681 | ||
682 | dso = __dsos__findnew(head, filename); | 682 | dso = __dsos__findnew(head, filename); |
683 | if (dso != NULL) { | 683 | if (dso != NULL) { |
684 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 684 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
685 | 685 | ||
686 | dso__set_build_id(dso, &bev->build_id); | 686 | dso__set_build_id(dso, &bev->build_id); |
687 | 687 | ||
688 | if (filename[0] == '[') | 688 | if (filename[0] == '[') |
689 | dso->kernel = dso_type; | 689 | dso->kernel = dso_type; |
690 | 690 | ||
691 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | 691 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), |
692 | sbuild_id); | 692 | sbuild_id); |
693 | pr_debug("build id event received for %s: %s\n", | 693 | pr_debug("build id event received for %s: %s\n", |
694 | dso->long_name, sbuild_id); | 694 | dso->long_name, sbuild_id); |
695 | } | 695 | } |
696 | 696 | ||
697 | err = 0; | 697 | err = 0; |
698 | out: | 698 | out: |
699 | return err; | 699 | return err; |
700 | } | 700 | } |
701 | 701 | ||
702 | static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | 702 | static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, |
703 | int input, u64 offset, u64 size) | 703 | int input, u64 offset, u64 size) |
704 | { | 704 | { |
705 | struct perf_session *session = container_of(header, struct perf_session, header); | 705 | struct perf_session *session = container_of(header, struct perf_session, header); |
706 | struct { | 706 | struct { |
707 | struct perf_event_header header; | 707 | struct perf_event_header header; |
708 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | 708 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; |
709 | char filename[0]; | 709 | char filename[0]; |
710 | } old_bev; | 710 | } old_bev; |
711 | struct build_id_event bev; | 711 | struct build_id_event bev; |
712 | char filename[PATH_MAX]; | 712 | char filename[PATH_MAX]; |
713 | u64 limit = offset + size; | 713 | u64 limit = offset + size; |
714 | 714 | ||
715 | while (offset < limit) { | 715 | while (offset < limit) { |
716 | ssize_t len; | 716 | ssize_t len; |
717 | 717 | ||
718 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) | 718 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) |
719 | return -1; | 719 | return -1; |
720 | 720 | ||
721 | if (header->needs_swap) | 721 | if (header->needs_swap) |
722 | perf_event_header__bswap(&old_bev.header); | 722 | perf_event_header__bswap(&old_bev.header); |
723 | 723 | ||
724 | len = old_bev.header.size - sizeof(old_bev); | 724 | len = old_bev.header.size - sizeof(old_bev); |
725 | if (read(input, filename, len) != len) | 725 | if (read(input, filename, len) != len) |
726 | return -1; | 726 | return -1; |
727 | 727 | ||
728 | bev.header = old_bev.header; | 728 | bev.header = old_bev.header; |
729 | bev.pid = 0; | 729 | bev.pid = 0; |
730 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); | 730 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); |
731 | __event_process_build_id(&bev, filename, session); | 731 | __event_process_build_id(&bev, filename, session); |
732 | 732 | ||
733 | offset += bev.header.size; | 733 | offset += bev.header.size; |
734 | } | 734 | } |
735 | 735 | ||
736 | return 0; | 736 | return 0; |
737 | } | 737 | } |
738 | 738 | ||
739 | static int perf_header__read_build_ids(struct perf_header *header, | 739 | static int perf_header__read_build_ids(struct perf_header *header, |
740 | int input, u64 offset, u64 size) | 740 | int input, u64 offset, u64 size) |
741 | { | 741 | { |
742 | struct perf_session *session = container_of(header, struct perf_session, header); | 742 | struct perf_session *session = container_of(header, struct perf_session, header); |
743 | struct build_id_event bev; | 743 | struct build_id_event bev; |
744 | char filename[PATH_MAX]; | 744 | char filename[PATH_MAX]; |
745 | u64 limit = offset + size, orig_offset = offset; | 745 | u64 limit = offset + size, orig_offset = offset; |
746 | int err = -1; | 746 | int err = -1; |
747 | 747 | ||
748 | while (offset < limit) { | 748 | while (offset < limit) { |
749 | ssize_t len; | 749 | ssize_t len; |
750 | 750 | ||
751 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | 751 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) |
752 | goto out; | 752 | goto out; |
753 | 753 | ||
754 | if (header->needs_swap) | 754 | if (header->needs_swap) |
755 | perf_event_header__bswap(&bev.header); | 755 | perf_event_header__bswap(&bev.header); |
756 | 756 | ||
757 | len = bev.header.size - sizeof(bev); | 757 | len = bev.header.size - sizeof(bev); |
758 | if (read(input, filename, len) != len) | 758 | if (read(input, filename, len) != len) |
759 | goto out; | 759 | goto out; |
760 | /* | 760 | /* |
761 | * The a1645ce1 changeset: | 761 | * The a1645ce1 changeset: |
762 | * | 762 | * |
763 | * "perf: 'perf kvm' tool for monitoring guest performance from host" | 763 | * "perf: 'perf kvm' tool for monitoring guest performance from host" |
764 | * | 764 | * |
765 | * Added a field to struct build_id_event that broke the file | 765 | * Added a field to struct build_id_event that broke the file |
766 | * format. | 766 | * format. |
767 | * | 767 | * |
768 | * Since the kernel build-id is the first entry, process the | 768 | * Since the kernel build-id is the first entry, process the |
769 | * table using the old format if the well known | 769 | * table using the old format if the well known |
770 | * '[kernel.kallsyms]' string for the kernel build-id has the | 770 | * '[kernel.kallsyms]' string for the kernel build-id has the |
771 | * first 4 characters chopped off (where the pid_t sits). | 771 | * first 4 characters chopped off (where the pid_t sits). |
772 | */ | 772 | */ |
773 | if (memcmp(filename, "nel.kallsyms]", 13) == 0) { | 773 | if (memcmp(filename, "nel.kallsyms]", 13) == 0) { |
774 | if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) | 774 | if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) |
775 | return -1; | 775 | return -1; |
776 | return perf_header__read_build_ids_abi_quirk(header, input, offset, size); | 776 | return perf_header__read_build_ids_abi_quirk(header, input, offset, size); |
777 | } | 777 | } |
778 | 778 | ||
779 | __event_process_build_id(&bev, filename, session); | 779 | __event_process_build_id(&bev, filename, session); |
780 | 780 | ||
781 | offset += bev.header.size; | 781 | offset += bev.header.size; |
782 | } | 782 | } |
783 | err = 0; | 783 | err = 0; |
784 | out: | 784 | out: |
785 | return err; | 785 | return err; |
786 | } | 786 | } |
787 | 787 | ||
788 | static int perf_file_section__process(struct perf_file_section *section, | 788 | static int perf_file_section__process(struct perf_file_section *section, |
789 | struct perf_header *ph, | 789 | struct perf_header *ph, |
790 | int feat, int fd) | 790 | int feat, int fd) |
791 | { | 791 | { |
792 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | 792 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { |
793 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | 793 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " |
794 | "%d, continuing...\n", section->offset, feat); | 794 | "%d, continuing...\n", section->offset, feat); |
795 | return 0; | 795 | return 0; |
796 | } | 796 | } |
797 | 797 | ||
798 | switch (feat) { | 798 | switch (feat) { |
799 | case HEADER_TRACE_INFO: | 799 | case HEADER_TRACE_INFO: |
800 | trace_report(fd, false); | 800 | trace_report(fd, false); |
801 | break; | 801 | break; |
802 | 802 | ||
803 | case HEADER_BUILD_ID: | 803 | case HEADER_BUILD_ID: |
804 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | 804 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) |
805 | pr_debug("Failed to read buildids, continuing...\n"); | 805 | pr_debug("Failed to read buildids, continuing...\n"); |
806 | break; | 806 | break; |
807 | default: | 807 | default: |
808 | pr_debug("unknown feature %d, continuing...\n", feat); | 808 | pr_debug("unknown feature %d, continuing...\n", feat); |
809 | } | 809 | } |
810 | 810 | ||
811 | return 0; | 811 | return 0; |
812 | } | 812 | } |
813 | 813 | ||
814 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, | 814 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, |
815 | struct perf_header *ph, int fd, | 815 | struct perf_header *ph, int fd, |
816 | bool repipe) | 816 | bool repipe) |
817 | { | 817 | { |
818 | if (readn(fd, header, sizeof(*header)) <= 0 || | 818 | if (readn(fd, header, sizeof(*header)) <= 0 || |
819 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) | 819 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) |
820 | return -1; | 820 | return -1; |
821 | 821 | ||
822 | if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) | 822 | if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) |
823 | return -1; | 823 | return -1; |
824 | 824 | ||
825 | if (header->size != sizeof(*header)) { | 825 | if (header->size != sizeof(*header)) { |
826 | u64 size = bswap_64(header->size); | 826 | u64 size = bswap_64(header->size); |
827 | 827 | ||
828 | if (size != sizeof(*header)) | 828 | if (size != sizeof(*header)) |
829 | return -1; | 829 | return -1; |
830 | 830 | ||
831 | ph->needs_swap = true; | 831 | ph->needs_swap = true; |
832 | } | 832 | } |
833 | 833 | ||
834 | return 0; | 834 | return 0; |
835 | } | 835 | } |
836 | 836 | ||
837 | static int perf_header__read_pipe(struct perf_session *session, int fd) | 837 | static int perf_header__read_pipe(struct perf_session *session, int fd) |
838 | { | 838 | { |
839 | struct perf_header *header = &session->header; | 839 | struct perf_header *header = &session->header; |
840 | struct perf_pipe_file_header f_header; | 840 | struct perf_pipe_file_header f_header; |
841 | 841 | ||
842 | if (perf_file_header__read_pipe(&f_header, header, fd, | 842 | if (perf_file_header__read_pipe(&f_header, header, fd, |
843 | session->repipe) < 0) { | 843 | session->repipe) < 0) { |
844 | pr_debug("incompatible file format\n"); | 844 | pr_debug("incompatible file format\n"); |
845 | return -EINVAL; | 845 | return -EINVAL; |
846 | } | 846 | } |
847 | 847 | ||
848 | session->fd = fd; | 848 | session->fd = fd; |
849 | 849 | ||
850 | return 0; | 850 | return 0; |
851 | } | 851 | } |
852 | 852 | ||
853 | int perf_session__read_header(struct perf_session *session, int fd) | 853 | int perf_session__read_header(struct perf_session *session, int fd) |
854 | { | 854 | { |
855 | struct perf_header *header = &session->header; | 855 | struct perf_header *header = &session->header; |
856 | struct perf_file_header f_header; | 856 | struct perf_file_header f_header; |
857 | struct perf_file_attr f_attr; | 857 | struct perf_file_attr f_attr; |
858 | u64 f_id; | 858 | u64 f_id; |
859 | int nr_attrs, nr_ids, i, j; | 859 | int nr_attrs, nr_ids, i, j; |
860 | 860 | ||
861 | session->evlist = perf_evlist__new(NULL, NULL); | 861 | session->evlist = perf_evlist__new(NULL, NULL); |
862 | if (session->evlist == NULL) | 862 | if (session->evlist == NULL) |
863 | return -ENOMEM; | 863 | return -ENOMEM; |
864 | 864 | ||
865 | if (session->fd_pipe) | 865 | if (session->fd_pipe) |
866 | return perf_header__read_pipe(session, fd); | 866 | return perf_header__read_pipe(session, fd); |
867 | 867 | ||
868 | if (perf_file_header__read(&f_header, header, fd) < 0) { | 868 | if (perf_file_header__read(&f_header, header, fd) < 0) { |
869 | pr_debug("incompatible file format\n"); | 869 | pr_debug("incompatible file format\n"); |
870 | return -EINVAL; | 870 | return -EINVAL; |
871 | } | 871 | } |
872 | 872 | ||
873 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | 873 | nr_attrs = f_header.attrs.size / sizeof(f_attr); |
874 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 874 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
875 | 875 | ||
876 | for (i = 0; i < nr_attrs; i++) { | 876 | for (i = 0; i < nr_attrs; i++) { |
877 | struct perf_evsel *evsel; | 877 | struct perf_evsel *evsel; |
878 | off_t tmp; | 878 | off_t tmp; |
879 | 879 | ||
880 | if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) | 880 | if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) |
881 | goto out_errno; | 881 | goto out_errno; |
882 | 882 | ||
883 | if (header->needs_swap) | 883 | if (header->needs_swap) |
884 | perf_event__attr_swap(&f_attr.attr); | 884 | perf_event__attr_swap(&f_attr.attr); |
885 | 885 | ||
886 | tmp = lseek(fd, 0, SEEK_CUR); | 886 | tmp = lseek(fd, 0, SEEK_CUR); |
887 | evsel = perf_evsel__new(&f_attr.attr, i); | 887 | evsel = perf_evsel__new(&f_attr.attr, i); |
888 | 888 | ||
889 | if (evsel == NULL) | 889 | if (evsel == NULL) |
890 | goto out_delete_evlist; | 890 | goto out_delete_evlist; |
891 | /* | 891 | /* |
892 | * Do it before so that if perf_evsel__alloc_id fails, this | 892 | * Do it before so that if perf_evsel__alloc_id fails, this |
893 | * entry gets purged too at perf_evlist__delete(). | 893 | * entry gets purged too at perf_evlist__delete(). |
894 | */ | 894 | */ |
895 | perf_evlist__add(session->evlist, evsel); | 895 | perf_evlist__add(session->evlist, evsel); |
896 | 896 | ||
897 | nr_ids = f_attr.ids.size / sizeof(u64); | 897 | nr_ids = f_attr.ids.size / sizeof(u64); |
898 | /* | 898 | /* |
899 | * We don't have the cpu and thread maps on the header, so | 899 | * We don't have the cpu and thread maps on the header, so |
900 | * for allocating the perf_sample_id table we fake 1 cpu and | 900 | * for allocating the perf_sample_id table we fake 1 cpu and |
901 | * hattr->ids threads. | 901 | * hattr->ids threads. |
902 | */ | 902 | */ |
903 | if (perf_evsel__alloc_id(evsel, 1, nr_ids)) | 903 | if (perf_evsel__alloc_id(evsel, 1, nr_ids)) |
904 | goto out_delete_evlist; | 904 | goto out_delete_evlist; |
905 | 905 | ||
906 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 906 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
907 | 907 | ||
908 | for (j = 0; j < nr_ids; j++) { | 908 | for (j = 0; j < nr_ids; j++) { |
909 | if (perf_header__getbuffer64(header, fd, &f_id, sizeof(f_id))) | 909 | if (perf_header__getbuffer64(header, fd, &f_id, sizeof(f_id))) |
910 | goto out_errno; | 910 | goto out_errno; |
911 | 911 | ||
912 | perf_evlist__id_add(session->evlist, evsel, 0, j, f_id); | 912 | perf_evlist__id_add(session->evlist, evsel, 0, j, f_id); |
913 | } | 913 | } |
914 | 914 | ||
915 | lseek(fd, tmp, SEEK_SET); | 915 | lseek(fd, tmp, SEEK_SET); |
916 | } | 916 | } |
917 | 917 | ||
918 | if (f_header.event_types.size) { | 918 | if (f_header.event_types.size) { |
919 | lseek(fd, f_header.event_types.offset, SEEK_SET); | 919 | lseek(fd, f_header.event_types.offset, SEEK_SET); |
920 | events = malloc(f_header.event_types.size); | 920 | events = malloc(f_header.event_types.size); |
921 | if (events == NULL) | 921 | if (events == NULL) |
922 | return -ENOMEM; | 922 | return -ENOMEM; |
923 | if (perf_header__getbuffer64(header, fd, events, | 923 | if (perf_header__getbuffer64(header, fd, events, |
924 | f_header.event_types.size)) | 924 | f_header.event_types.size)) |
925 | goto out_errno; | 925 | goto out_errno; |
926 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 926 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
927 | } | 927 | } |
928 | 928 | ||
929 | perf_header__process_sections(header, fd, perf_file_section__process); | 929 | perf_header__process_sections(header, fd, perf_file_section__process); |
930 | 930 | ||
931 | lseek(fd, header->data_offset, SEEK_SET); | 931 | lseek(fd, header->data_offset, SEEK_SET); |
932 | 932 | ||
933 | header->frozen = 1; | 933 | header->frozen = 1; |
934 | return 0; | 934 | return 0; |
935 | out_errno: | 935 | out_errno: |
936 | return -errno; | 936 | return -errno; |
937 | 937 | ||
938 | out_delete_evlist: | 938 | out_delete_evlist: |
939 | perf_evlist__delete(session->evlist); | 939 | perf_evlist__delete(session->evlist); |
940 | session->evlist = NULL; | 940 | session->evlist = NULL; |
941 | return -ENOMEM; | 941 | return -ENOMEM; |
942 | } | 942 | } |
943 | 943 | ||
944 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | 944 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, |
945 | perf_event__handler_t process, | 945 | perf_event__handler_t process, |
946 | struct perf_session *session) | 946 | struct perf_session *session) |
947 | { | 947 | { |
948 | union perf_event *ev; | 948 | union perf_event *ev; |
949 | size_t size; | 949 | size_t size; |
950 | int err; | 950 | int err; |
951 | 951 | ||
952 | size = sizeof(struct perf_event_attr); | 952 | size = sizeof(struct perf_event_attr); |
953 | size = ALIGN(size, sizeof(u64)); | 953 | size = ALIGN(size, sizeof(u64)); |
954 | size += sizeof(struct perf_event_header); | 954 | size += sizeof(struct perf_event_header); |
955 | size += ids * sizeof(u64); | 955 | size += ids * sizeof(u64); |
956 | 956 | ||
957 | ev = malloc(size); | 957 | ev = malloc(size); |
958 | 958 | ||
959 | if (ev == NULL) | 959 | if (ev == NULL) |
960 | return -ENOMEM; | 960 | return -ENOMEM; |
961 | 961 | ||
962 | ev->attr.attr = *attr; | 962 | ev->attr.attr = *attr; |
963 | memcpy(ev->attr.id, id, ids * sizeof(u64)); | 963 | memcpy(ev->attr.id, id, ids * sizeof(u64)); |
964 | 964 | ||
965 | ev->attr.header.type = PERF_RECORD_HEADER_ATTR; | 965 | ev->attr.header.type = PERF_RECORD_HEADER_ATTR; |
966 | ev->attr.header.size = size; | 966 | ev->attr.header.size = size; |
967 | 967 | ||
968 | err = process(ev, NULL, session); | 968 | err = process(ev, NULL, session); |
969 | 969 | ||
970 | free(ev); | 970 | free(ev); |
971 | 971 | ||
972 | return err; | 972 | return err; |
973 | } | 973 | } |
974 | 974 | ||
975 | int perf_session__synthesize_attrs(struct perf_session *session, | 975 | int perf_session__synthesize_attrs(struct perf_session *session, |
976 | perf_event__handler_t process) | 976 | perf_event__handler_t process) |
977 | { | 977 | { |
978 | struct perf_evsel *attr; | 978 | struct perf_evsel *attr; |
979 | int err = 0; | 979 | int err = 0; |
980 | 980 | ||
981 | list_for_each_entry(attr, &session->evlist->entries, node) { | 981 | list_for_each_entry(attr, &session->evlist->entries, node) { |
982 | err = perf_event__synthesize_attr(&attr->attr, attr->ids, | 982 | err = perf_event__synthesize_attr(&attr->attr, attr->ids, |
983 | attr->id, process, session); | 983 | attr->id, process, session); |
984 | if (err) { | 984 | if (err) { |
985 | pr_debug("failed to create perf header attribute\n"); | 985 | pr_debug("failed to create perf header attribute\n"); |
986 | return err; | 986 | return err; |
987 | } | 987 | } |
988 | } | 988 | } |
989 | 989 | ||
990 | return err; | 990 | return err; |
991 | } | 991 | } |
992 | 992 | ||
993 | int perf_event__process_attr(union perf_event *event, | 993 | int perf_event__process_attr(union perf_event *event, |
994 | struct perf_session *session) | 994 | struct perf_session *session) |
995 | { | 995 | { |
996 | unsigned int i, ids, n_ids; | 996 | unsigned int i, ids, n_ids; |
997 | struct perf_evsel *evsel; | 997 | struct perf_evsel *evsel; |
998 | 998 | ||
999 | if (session->evlist == NULL) { | 999 | if (session->evlist == NULL) { |
1000 | session->evlist = perf_evlist__new(NULL, NULL); | 1000 | session->evlist = perf_evlist__new(NULL, NULL); |
1001 | if (session->evlist == NULL) | 1001 | if (session->evlist == NULL) |
1002 | return -ENOMEM; | 1002 | return -ENOMEM; |
1003 | } | 1003 | } |
1004 | 1004 | ||
1005 | evsel = perf_evsel__new(&event->attr.attr, | 1005 | evsel = perf_evsel__new(&event->attr.attr, |
1006 | session->evlist->nr_entries); | 1006 | session->evlist->nr_entries); |
1007 | if (evsel == NULL) | 1007 | if (evsel == NULL) |
1008 | return -ENOMEM; | 1008 | return -ENOMEM; |
1009 | 1009 | ||
1010 | perf_evlist__add(session->evlist, evsel); | 1010 | perf_evlist__add(session->evlist, evsel); |
1011 | 1011 | ||
1012 | ids = event->header.size; | 1012 | ids = event->header.size; |
1013 | ids -= (void *)&event->attr.id - (void *)event; | 1013 | ids -= (void *)&event->attr.id - (void *)event; |
1014 | n_ids = ids / sizeof(u64); | 1014 | n_ids = ids / sizeof(u64); |
1015 | /* | 1015 | /* |
1016 | * We don't have the cpu and thread maps on the header, so | 1016 | * We don't have the cpu and thread maps on the header, so |
1017 | * for allocating the perf_sample_id table we fake 1 cpu and | 1017 | * for allocating the perf_sample_id table we fake 1 cpu and |
1018 | * hattr->ids threads. | 1018 | * hattr->ids threads. |
1019 | */ | 1019 | */ |
1020 | if (perf_evsel__alloc_id(evsel, 1, n_ids)) | 1020 | if (perf_evsel__alloc_id(evsel, 1, n_ids)) |
1021 | return -ENOMEM; | 1021 | return -ENOMEM; |
1022 | 1022 | ||
1023 | for (i = 0; i < n_ids; i++) { | 1023 | for (i = 0; i < n_ids; i++) { |
1024 | perf_evlist__id_add(session->evlist, evsel, 0, i, | 1024 | perf_evlist__id_add(session->evlist, evsel, 0, i, |
1025 | event->attr.id[i]); | 1025 | event->attr.id[i]); |
1026 | } | 1026 | } |
1027 | 1027 | ||
1028 | perf_session__update_sample_type(session); | 1028 | perf_session__update_sample_type(session); |
1029 | 1029 | ||
1030 | return 0; | 1030 | return 0; |
1031 | } | 1031 | } |
1032 | 1032 | ||
1033 | int perf_event__synthesize_event_type(u64 event_id, char *name, | 1033 | int perf_event__synthesize_event_type(u64 event_id, char *name, |
1034 | perf_event__handler_t process, | 1034 | perf_event__handler_t process, |
1035 | struct perf_session *session) | 1035 | struct perf_session *session) |
1036 | { | 1036 | { |
1037 | union perf_event ev; | 1037 | union perf_event ev; |
1038 | size_t size = 0; | 1038 | size_t size = 0; |
1039 | int err = 0; | 1039 | int err = 0; |
1040 | 1040 | ||
1041 | memset(&ev, 0, sizeof(ev)); | 1041 | memset(&ev, 0, sizeof(ev)); |
1042 | 1042 | ||
1043 | ev.event_type.event_type.event_id = event_id; | 1043 | ev.event_type.event_type.event_id = event_id; |
1044 | memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME); | 1044 | memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME); |
1045 | strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); | 1045 | strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); |
1046 | 1046 | ||
1047 | ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; | 1047 | ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; |
1048 | size = strlen(name); | 1048 | size = strlen(name); |
1049 | size = ALIGN(size, sizeof(u64)); | 1049 | size = ALIGN(size, sizeof(u64)); |
1050 | ev.event_type.header.size = sizeof(ev.event_type) - | 1050 | ev.event_type.header.size = sizeof(ev.event_type) - |
1051 | (sizeof(ev.event_type.event_type.name) - size); | 1051 | (sizeof(ev.event_type.event_type.name) - size); |
1052 | 1052 | ||
1053 | err = process(&ev, NULL, session); | 1053 | err = process(&ev, NULL, session); |
1054 | 1054 | ||
1055 | return err; | 1055 | return err; |
1056 | } | 1056 | } |
1057 | 1057 | ||
1058 | int perf_event__synthesize_event_types(perf_event__handler_t process, | 1058 | int perf_event__synthesize_event_types(perf_event__handler_t process, |
1059 | struct perf_session *session) | 1059 | struct perf_session *session) |
1060 | { | 1060 | { |
1061 | struct perf_trace_event_type *type; | 1061 | struct perf_trace_event_type *type; |
1062 | int i, err = 0; | 1062 | int i, err = 0; |
1063 | 1063 | ||
1064 | for (i = 0; i < event_count; i++) { | 1064 | for (i = 0; i < event_count; i++) { |
1065 | type = &events[i]; | 1065 | type = &events[i]; |
1066 | 1066 | ||
1067 | err = perf_event__synthesize_event_type(type->event_id, | 1067 | err = perf_event__synthesize_event_type(type->event_id, |
1068 | type->name, process, | 1068 | type->name, process, |
1069 | session); | 1069 | session); |
1070 | if (err) { | 1070 | if (err) { |
1071 | pr_debug("failed to create perf header event type\n"); | 1071 | pr_debug("failed to create perf header event type\n"); |
1072 | return err; | 1072 | return err; |
1073 | } | 1073 | } |
1074 | } | 1074 | } |
1075 | 1075 | ||
1076 | return err; | 1076 | return err; |
1077 | } | 1077 | } |
1078 | 1078 | ||
1079 | int perf_event__process_event_type(union perf_event *event, | 1079 | int perf_event__process_event_type(union perf_event *event, |
1080 | struct perf_session *session __unused) | 1080 | struct perf_session *session __unused) |
1081 | { | 1081 | { |
1082 | if (perf_header__push_event(event->event_type.event_type.event_id, | 1082 | if (perf_header__push_event(event->event_type.event_type.event_id, |
1083 | event->event_type.event_type.name) < 0) | 1083 | event->event_type.event_type.name) < 0) |
1084 | return -ENOMEM; | 1084 | return -ENOMEM; |
1085 | 1085 | ||
1086 | return 0; | 1086 | return 0; |
1087 | } | 1087 | } |
1088 | 1088 | ||
1089 | int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | 1089 | int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
1090 | perf_event__handler_t process, | 1090 | perf_event__handler_t process, |
1091 | struct perf_session *session __unused) | 1091 | struct perf_session *session __unused) |
1092 | { | 1092 | { |
1093 | union perf_event ev; | 1093 | union perf_event ev; |
1094 | ssize_t size = 0, aligned_size = 0, padding; | 1094 | ssize_t size = 0, aligned_size = 0, padding; |
1095 | int err __used = 0; | 1095 | int err __used = 0; |
1096 | 1096 | ||
1097 | memset(&ev, 0, sizeof(ev)); | 1097 | memset(&ev, 0, sizeof(ev)); |
1098 | 1098 | ||
1099 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 1099 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
1100 | size = read_tracing_data_size(fd, &evlist->entries); | 1100 | size = read_tracing_data_size(fd, &evlist->entries); |
1101 | if (size <= 0) | 1101 | if (size <= 0) |
1102 | return size; | 1102 | return size; |
1103 | aligned_size = ALIGN(size, sizeof(u64)); | 1103 | aligned_size = ALIGN(size, sizeof(u64)); |
1104 | padding = aligned_size - size; | 1104 | padding = aligned_size - size; |
1105 | ev.tracing_data.header.size = sizeof(ev.tracing_data); | 1105 | ev.tracing_data.header.size = sizeof(ev.tracing_data); |
1106 | ev.tracing_data.size = aligned_size; | 1106 | ev.tracing_data.size = aligned_size; |
1107 | 1107 | ||
1108 | process(&ev, NULL, session); | 1108 | process(&ev, NULL, session); |
1109 | 1109 | ||
1110 | err = read_tracing_data(fd, &evlist->entries); | 1110 | err = read_tracing_data(fd, &evlist->entries); |
1111 | write_padded(fd, NULL, 0, padding); | 1111 | write_padded(fd, NULL, 0, padding); |
1112 | 1112 | ||
1113 | return aligned_size; | 1113 | return aligned_size; |
1114 | } | 1114 | } |
1115 | 1115 | ||
1116 | int perf_event__process_tracing_data(union perf_event *event, | 1116 | int perf_event__process_tracing_data(union perf_event *event, |
1117 | struct perf_session *session) | 1117 | struct perf_session *session) |
1118 | { | 1118 | { |
1119 | ssize_t size_read, padding, size = event->tracing_data.size; | 1119 | ssize_t size_read, padding, size = event->tracing_data.size; |
1120 | off_t offset = lseek(session->fd, 0, SEEK_CUR); | 1120 | off_t offset = lseek(session->fd, 0, SEEK_CUR); |
1121 | char buf[BUFSIZ]; | 1121 | char buf[BUFSIZ]; |
1122 | 1122 | ||
1123 | /* setup for reading amidst mmap */ | 1123 | /* setup for reading amidst mmap */ |
1124 | lseek(session->fd, offset + sizeof(struct tracing_data_event), | 1124 | lseek(session->fd, offset + sizeof(struct tracing_data_event), |
1125 | SEEK_SET); | 1125 | SEEK_SET); |
1126 | 1126 | ||
1127 | size_read = trace_report(session->fd, session->repipe); | 1127 | size_read = trace_report(session->fd, session->repipe); |
1128 | 1128 | ||
1129 | padding = ALIGN(size_read, sizeof(u64)) - size_read; | 1129 | padding = ALIGN(size_read, sizeof(u64)) - size_read; |
1130 | 1130 | ||
1131 | if (read(session->fd, buf, padding) < 0) | 1131 | if (read(session->fd, buf, padding) < 0) |
1132 | die("reading input file"); | 1132 | die("reading input file"); |
1133 | if (session->repipe) { | 1133 | if (session->repipe) { |
1134 | int retw = write(STDOUT_FILENO, buf, padding); | 1134 | int retw = write(STDOUT_FILENO, buf, padding); |
1135 | if (retw <= 0 || retw != padding) | 1135 | if (retw <= 0 || retw != padding) |
1136 | die("repiping tracing data padding"); | 1136 | die("repiping tracing data padding"); |
1137 | } | 1137 | } |
1138 | 1138 | ||
1139 | if (size_read + padding != size) | 1139 | if (size_read + padding != size) |
1140 | die("tracing data size mismatch"); | 1140 | die("tracing data size mismatch"); |
1141 | 1141 | ||
1142 | return size_read + padding; | 1142 | return size_read + padding; |
1143 | } | 1143 | } |
1144 | 1144 | ||
1145 | int perf_event__synthesize_build_id(struct dso *pos, u16 misc, | 1145 | int perf_event__synthesize_build_id(struct dso *pos, u16 misc, |
1146 | perf_event__handler_t process, | 1146 | perf_event__handler_t process, |
1147 | struct machine *machine, | 1147 | struct machine *machine, |
1148 | struct perf_session *session) | 1148 | struct perf_session *session) |
1149 | { | 1149 | { |
1150 | union perf_event ev; | 1150 | union perf_event ev; |
1151 | size_t len; | 1151 | size_t len; |
1152 | int err = 0; | 1152 | int err = 0; |
1153 | 1153 | ||
1154 | if (!pos->hit) | 1154 | if (!pos->hit) |
1155 | return err; | 1155 | return err; |
1156 | 1156 | ||
1157 | memset(&ev, 0, sizeof(ev)); | 1157 | memset(&ev, 0, sizeof(ev)); |
1158 | 1158 | ||
1159 | len = pos->long_name_len + 1; | 1159 | len = pos->long_name_len + 1; |
1160 | len = ALIGN(len, NAME_ALIGN); | 1160 | len = ALIGN(len, NAME_ALIGN); |
1161 | memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); | 1161 | memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id)); |
1162 | ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; | 1162 | ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; |
1163 | ev.build_id.header.misc = misc; | 1163 | ev.build_id.header.misc = misc; |
1164 | ev.build_id.pid = machine->pid; | 1164 | ev.build_id.pid = machine->pid; |
1165 | ev.build_id.header.size = sizeof(ev.build_id) + len; | 1165 | ev.build_id.header.size = sizeof(ev.build_id) + len; |
1166 | memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); | 1166 | memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); |
1167 | 1167 | ||
1168 | err = process(&ev, NULL, session); | 1168 | err = process(&ev, NULL, session); |
1169 | 1169 | ||
1170 | return err; | 1170 | return err; |
1171 | } | 1171 | } |
1172 | 1172 | ||
1173 | int perf_event__process_build_id(union perf_event *event, | 1173 | int perf_event__process_build_id(union perf_event *event, |
1174 | struct perf_session *session) | 1174 | struct perf_session *session) |
1175 | { | 1175 | { |
1176 | __event_process_build_id(&event->build_id, | 1176 | __event_process_build_id(&event->build_id, |
1177 | event->build_id.filename, | 1177 | event->build_id.filename, |
1178 | session); | 1178 | session); |
1179 | return 0; | 1179 | return 0; |
1180 | } | 1180 | } |
1181 | 1181 | ||
1182 | void disable_buildid_cache(void) | 1182 | void disable_buildid_cache(void) |
1183 | { | 1183 | { |
1184 | no_buildid_cache = true; | 1184 | no_buildid_cache = true; |
1185 | } | 1185 | } |
1186 | 1186 |
tools/perf/util/probe-event.c
1 | /* | 1 | /* |
2 | * probe-event.c : perf-probe definition to probe_events format converter | 2 | * probe-event.c : perf-probe definition to probe_events format converter |
3 | * | 3 | * |
4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | 4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software | 17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
19 | * | 19 | * |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #define _GNU_SOURCE | 22 | #define _GNU_SOURCE |
23 | #include <sys/utsname.h> | 23 | #include <sys/utsname.h> |
24 | #include <sys/types.h> | 24 | #include <sys/types.h> |
25 | #include <sys/stat.h> | 25 | #include <sys/stat.h> |
26 | #include <fcntl.h> | 26 | #include <fcntl.h> |
27 | #include <errno.h> | 27 | #include <errno.h> |
28 | #include <stdio.h> | 28 | #include <stdio.h> |
29 | #include <unistd.h> | 29 | #include <unistd.h> |
30 | #include <stdlib.h> | 30 | #include <stdlib.h> |
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <limits.h> | 33 | #include <limits.h> |
34 | #include <elf.h> | 34 | #include <elf.h> |
35 | 35 | ||
36 | #undef _GNU_SOURCE | 36 | #undef _GNU_SOURCE |
37 | #include "util.h" | 37 | #include "util.h" |
38 | #include "event.h" | 38 | #include "event.h" |
39 | #include "string.h" | 39 | #include "string.h" |
40 | #include "strlist.h" | 40 | #include "strlist.h" |
41 | #include "debug.h" | 41 | #include "debug.h" |
42 | #include "cache.h" | 42 | #include "cache.h" |
43 | #include "color.h" | 43 | #include "color.h" |
44 | #include "symbol.h" | 44 | #include "symbol.h" |
45 | #include "thread.h" | 45 | #include "thread.h" |
46 | #include "debugfs.h" | 46 | #include "debugfs.h" |
47 | #include "trace-event.h" /* For __unused */ | 47 | #include "trace-event.h" /* For __unused */ |
48 | #include "probe-event.h" | 48 | #include "probe-event.h" |
49 | #include "probe-finder.h" | 49 | #include "probe-finder.h" |
50 | 50 | ||
51 | #define MAX_CMDLEN 256 | 51 | #define MAX_CMDLEN 256 |
52 | #define MAX_PROBE_ARGS 128 | 52 | #define MAX_PROBE_ARGS 128 |
53 | #define PERFPROBE_GROUP "probe" | 53 | #define PERFPROBE_GROUP "probe" |
54 | 54 | ||
55 | bool probe_event_dry_run; /* Dry run flag */ | 55 | bool probe_event_dry_run; /* Dry run flag */ |
56 | 56 | ||
57 | #define semantic_error(msg ...) pr_err("Semantic error :" msg) | 57 | #define semantic_error(msg ...) pr_err("Semantic error :" msg) |
58 | 58 | ||
59 | /* If there is no space to write, returns -E2BIG. */ | 59 | /* If there is no space to write, returns -E2BIG. */ |
60 | static int e_snprintf(char *str, size_t size, const char *format, ...) | 60 | static int e_snprintf(char *str, size_t size, const char *format, ...) |
61 | __attribute__((format(printf, 3, 4))); | 61 | __attribute__((format(printf, 3, 4))); |
62 | 62 | ||
63 | static int e_snprintf(char *str, size_t size, const char *format, ...) | 63 | static int e_snprintf(char *str, size_t size, const char *format, ...) |
64 | { | 64 | { |
65 | int ret; | 65 | int ret; |
66 | va_list ap; | 66 | va_list ap; |
67 | va_start(ap, format); | 67 | va_start(ap, format); |
68 | ret = vsnprintf(str, size, format, ap); | 68 | ret = vsnprintf(str, size, format, ap); |
69 | va_end(ap); | 69 | va_end(ap); |
70 | if (ret >= (int)size) | 70 | if (ret >= (int)size) |
71 | ret = -E2BIG; | 71 | ret = -E2BIG; |
72 | return ret; | 72 | return ret; |
73 | } | 73 | } |
74 | 74 | ||
75 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); | 75 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); |
76 | static struct machine machine; | 76 | static struct machine machine; |
77 | 77 | ||
78 | /* Initialize symbol maps and path of vmlinux/modules */ | 78 | /* Initialize symbol maps and path of vmlinux/modules */ |
79 | static int init_vmlinux(void) | 79 | static int init_vmlinux(void) |
80 | { | 80 | { |
81 | int ret; | 81 | int ret; |
82 | 82 | ||
83 | symbol_conf.sort_by_name = true; | 83 | symbol_conf.sort_by_name = true; |
84 | if (symbol_conf.vmlinux_name == NULL) | 84 | if (symbol_conf.vmlinux_name == NULL) |
85 | symbol_conf.try_vmlinux_path = true; | 85 | symbol_conf.try_vmlinux_path = true; |
86 | else | 86 | else |
87 | pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); | 87 | pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); |
88 | ret = symbol__init(); | 88 | ret = symbol__init(); |
89 | if (ret < 0) { | 89 | if (ret < 0) { |
90 | pr_debug("Failed to init symbol map.\n"); | 90 | pr_debug("Failed to init symbol map.\n"); |
91 | goto out; | 91 | goto out; |
92 | } | 92 | } |
93 | 93 | ||
94 | ret = machine__init(&machine, "", HOST_KERNEL_ID); | 94 | ret = machine__init(&machine, "", HOST_KERNEL_ID); |
95 | if (ret < 0) | 95 | if (ret < 0) |
96 | goto out; | 96 | goto out; |
97 | 97 | ||
98 | if (machine__create_kernel_maps(&machine) < 0) { | 98 | if (machine__create_kernel_maps(&machine) < 0) { |
99 | pr_debug("machine__create_kernel_maps() failed.\n"); | 99 | pr_debug("machine__create_kernel_maps() failed.\n"); |
100 | goto out; | 100 | goto out; |
101 | } | 101 | } |
102 | out: | 102 | out: |
103 | if (ret < 0) | 103 | if (ret < 0) |
104 | pr_warning("Failed to init vmlinux path.\n"); | 104 | pr_warning("Failed to init vmlinux path.\n"); |
105 | return ret; | 105 | return ret; |
106 | } | 106 | } |
107 | 107 | ||
108 | static struct symbol *__find_kernel_function_by_name(const char *name, | 108 | static struct symbol *__find_kernel_function_by_name(const char *name, |
109 | struct map **mapp) | 109 | struct map **mapp) |
110 | { | 110 | { |
111 | return machine__find_kernel_function_by_name(&machine, name, mapp, | 111 | return machine__find_kernel_function_by_name(&machine, name, mapp, |
112 | NULL); | 112 | NULL); |
113 | } | 113 | } |
114 | 114 | ||
115 | static struct map *kernel_get_module_map(const char *module) | 115 | static struct map *kernel_get_module_map(const char *module) |
116 | { | 116 | { |
117 | struct rb_node *nd; | 117 | struct rb_node *nd; |
118 | struct map_groups *grp = &machine.kmaps; | 118 | struct map_groups *grp = &machine.kmaps; |
119 | 119 | ||
120 | /* A file path -- this is an offline module */ | 120 | /* A file path -- this is an offline module */ |
121 | if (module && strchr(module, '/')) | 121 | if (module && strchr(module, '/')) |
122 | return machine__new_module(&machine, 0, module); | 122 | return machine__new_module(&machine, 0, module); |
123 | 123 | ||
124 | if (!module) | 124 | if (!module) |
125 | module = "kernel"; | 125 | module = "kernel"; |
126 | 126 | ||
127 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | 127 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { |
128 | struct map *pos = rb_entry(nd, struct map, rb_node); | 128 | struct map *pos = rb_entry(nd, struct map, rb_node); |
129 | if (strncmp(pos->dso->short_name + 1, module, | 129 | if (strncmp(pos->dso->short_name + 1, module, |
130 | pos->dso->short_name_len - 2) == 0) { | 130 | pos->dso->short_name_len - 2) == 0) { |
131 | return pos; | 131 | return pos; |
132 | } | 132 | } |
133 | } | 133 | } |
134 | return NULL; | 134 | return NULL; |
135 | } | 135 | } |
136 | 136 | ||
137 | static struct dso *kernel_get_module_dso(const char *module) | 137 | static struct dso *kernel_get_module_dso(const char *module) |
138 | { | 138 | { |
139 | struct dso *dso; | 139 | struct dso *dso; |
140 | struct map *map; | 140 | struct map *map; |
141 | const char *vmlinux_name; | 141 | const char *vmlinux_name; |
142 | 142 | ||
143 | if (module) { | 143 | if (module) { |
144 | list_for_each_entry(dso, &machine.kernel_dsos, node) { | 144 | list_for_each_entry(dso, &machine.kernel_dsos, node) { |
145 | if (strncmp(dso->short_name + 1, module, | 145 | if (strncmp(dso->short_name + 1, module, |
146 | dso->short_name_len - 2) == 0) | 146 | dso->short_name_len - 2) == 0) |
147 | goto found; | 147 | goto found; |
148 | } | 148 | } |
149 | pr_debug("Failed to find module %s.\n", module); | 149 | pr_debug("Failed to find module %s.\n", module); |
150 | return NULL; | 150 | return NULL; |
151 | } | 151 | } |
152 | 152 | ||
153 | map = machine.vmlinux_maps[MAP__FUNCTION]; | 153 | map = machine.vmlinux_maps[MAP__FUNCTION]; |
154 | dso = map->dso; | 154 | dso = map->dso; |
155 | 155 | ||
156 | vmlinux_name = symbol_conf.vmlinux_name; | 156 | vmlinux_name = symbol_conf.vmlinux_name; |
157 | if (vmlinux_name) { | 157 | if (vmlinux_name) { |
158 | if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0) | 158 | if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0) |
159 | return NULL; | 159 | return NULL; |
160 | } else { | 160 | } else { |
161 | if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { | 161 | if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { |
162 | pr_debug("Failed to load kernel map.\n"); | 162 | pr_debug("Failed to load kernel map.\n"); |
163 | return NULL; | 163 | return NULL; |
164 | } | 164 | } |
165 | } | 165 | } |
166 | found: | 166 | found: |
167 | return dso; | 167 | return dso; |
168 | } | 168 | } |
169 | 169 | ||
170 | const char *kernel_get_module_path(const char *module) | 170 | const char *kernel_get_module_path(const char *module) |
171 | { | 171 | { |
172 | struct dso *dso = kernel_get_module_dso(module); | 172 | struct dso *dso = kernel_get_module_dso(module); |
173 | return (dso) ? dso->long_name : NULL; | 173 | return (dso) ? dso->long_name : NULL; |
174 | } | 174 | } |
175 | 175 | ||
176 | #ifdef DWARF_SUPPORT | 176 | #ifdef DWARF_SUPPORT |
177 | /* Open new debuginfo of given module */ | 177 | /* Open new debuginfo of given module */ |
178 | static struct debuginfo *open_debuginfo(const char *module) | 178 | static struct debuginfo *open_debuginfo(const char *module) |
179 | { | 179 | { |
180 | const char *path; | 180 | const char *path; |
181 | 181 | ||
182 | /* A file path -- this is an offline module */ | 182 | /* A file path -- this is an offline module */ |
183 | if (module && strchr(module, '/')) | 183 | if (module && strchr(module, '/')) |
184 | path = module; | 184 | path = module; |
185 | else { | 185 | else { |
186 | path = kernel_get_module_path(module); | 186 | path = kernel_get_module_path(module); |
187 | 187 | ||
188 | if (!path) { | 188 | if (!path) { |
189 | pr_err("Failed to find path of %s module.\n", | 189 | pr_err("Failed to find path of %s module.\n", |
190 | module ?: "kernel"); | 190 | module ?: "kernel"); |
191 | return NULL; | 191 | return NULL; |
192 | } | 192 | } |
193 | } | 193 | } |
194 | return debuginfo__new(path); | 194 | return debuginfo__new(path); |
195 | } | 195 | } |
196 | 196 | ||
197 | /* | 197 | /* |
198 | * Convert trace point to probe point with debuginfo | 198 | * Convert trace point to probe point with debuginfo |
199 | * Currently only handles kprobes. | 199 | * Currently only handles kprobes. |
200 | */ | 200 | */ |
201 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | 201 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, |
202 | struct perf_probe_point *pp) | 202 | struct perf_probe_point *pp) |
203 | { | 203 | { |
204 | struct symbol *sym; | 204 | struct symbol *sym; |
205 | struct map *map; | 205 | struct map *map; |
206 | u64 addr; | 206 | u64 addr; |
207 | int ret = -ENOENT; | 207 | int ret = -ENOENT; |
208 | struct debuginfo *dinfo; | 208 | struct debuginfo *dinfo; |
209 | 209 | ||
210 | sym = __find_kernel_function_by_name(tp->symbol, &map); | 210 | sym = __find_kernel_function_by_name(tp->symbol, &map); |
211 | if (sym) { | 211 | if (sym) { |
212 | addr = map->unmap_ip(map, sym->start + tp->offset); | 212 | addr = map->unmap_ip(map, sym->start + tp->offset); |
213 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, | 213 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, |
214 | tp->offset, addr); | 214 | tp->offset, addr); |
215 | 215 | ||
216 | dinfo = debuginfo__new_online_kernel(addr); | 216 | dinfo = debuginfo__new_online_kernel(addr); |
217 | if (dinfo) { | 217 | if (dinfo) { |
218 | ret = debuginfo__find_probe_point(dinfo, | 218 | ret = debuginfo__find_probe_point(dinfo, |
219 | (unsigned long)addr, pp); | 219 | (unsigned long)addr, pp); |
220 | debuginfo__delete(dinfo); | 220 | debuginfo__delete(dinfo); |
221 | } else { | 221 | } else { |
222 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", | 222 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", |
223 | addr); | 223 | addr); |
224 | ret = -ENOENT; | 224 | ret = -ENOENT; |
225 | } | 225 | } |
226 | } | 226 | } |
227 | if (ret <= 0) { | 227 | if (ret <= 0) { |
228 | pr_debug("Failed to find corresponding probes from " | 228 | pr_debug("Failed to find corresponding probes from " |
229 | "debuginfo. Use kprobe event information.\n"); | 229 | "debuginfo. Use kprobe event information.\n"); |
230 | pp->function = strdup(tp->symbol); | 230 | pp->function = strdup(tp->symbol); |
231 | if (pp->function == NULL) | 231 | if (pp->function == NULL) |
232 | return -ENOMEM; | 232 | return -ENOMEM; |
233 | pp->offset = tp->offset; | 233 | pp->offset = tp->offset; |
234 | } | 234 | } |
235 | pp->retprobe = tp->retprobe; | 235 | pp->retprobe = tp->retprobe; |
236 | 236 | ||
237 | return 0; | 237 | return 0; |
238 | } | 238 | } |
239 | 239 | ||
240 | static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, | 240 | static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, |
241 | int ntevs, const char *module) | 241 | int ntevs, const char *module) |
242 | { | 242 | { |
243 | int i, ret = 0; | 243 | int i, ret = 0; |
244 | char *tmp; | 244 | char *tmp; |
245 | 245 | ||
246 | if (!module) | 246 | if (!module) |
247 | return 0; | 247 | return 0; |
248 | 248 | ||
249 | tmp = strrchr(module, '/'); | 249 | tmp = strrchr(module, '/'); |
250 | if (tmp) { | 250 | if (tmp) { |
251 | /* This is a module path -- get the module name */ | 251 | /* This is a module path -- get the module name */ |
252 | module = strdup(tmp + 1); | 252 | module = strdup(tmp + 1); |
253 | if (!module) | 253 | if (!module) |
254 | return -ENOMEM; | 254 | return -ENOMEM; |
255 | tmp = strchr(module, '.'); | 255 | tmp = strchr(module, '.'); |
256 | if (tmp) | 256 | if (tmp) |
257 | *tmp = '\0'; | 257 | *tmp = '\0'; |
258 | tmp = (char *)module; /* For free() */ | 258 | tmp = (char *)module; /* For free() */ |
259 | } | 259 | } |
260 | 260 | ||
261 | for (i = 0; i < ntevs; i++) { | 261 | for (i = 0; i < ntevs; i++) { |
262 | tevs[i].point.module = strdup(module); | 262 | tevs[i].point.module = strdup(module); |
263 | if (!tevs[i].point.module) { | 263 | if (!tevs[i].point.module) { |
264 | ret = -ENOMEM; | 264 | ret = -ENOMEM; |
265 | break; | 265 | break; |
266 | } | 266 | } |
267 | } | 267 | } |
268 | 268 | ||
269 | if (tmp) | 269 | if (tmp) |
270 | free(tmp); | 270 | free(tmp); |
271 | 271 | ||
272 | return ret; | 272 | return ret; |
273 | } | 273 | } |
274 | 274 | ||
275 | /* Try to find perf_probe_event with debuginfo */ | 275 | /* Try to find perf_probe_event with debuginfo */ |
276 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 276 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
277 | struct probe_trace_event **tevs, | 277 | struct probe_trace_event **tevs, |
278 | int max_tevs, const char *module) | 278 | int max_tevs, const char *module) |
279 | { | 279 | { |
280 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 280 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
281 | struct debuginfo *dinfo = open_debuginfo(module); | 281 | struct debuginfo *dinfo = open_debuginfo(module); |
282 | int ntevs, ret = 0; | 282 | int ntevs, ret = 0; |
283 | 283 | ||
284 | if (!dinfo) { | 284 | if (!dinfo) { |
285 | if (need_dwarf) { | 285 | if (need_dwarf) { |
286 | pr_warning("Failed to open debuginfo file.\n"); | 286 | pr_warning("Failed to open debuginfo file.\n"); |
287 | return -ENOENT; | 287 | return -ENOENT; |
288 | } | 288 | } |
289 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); | 289 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); |
290 | return 0; | 290 | return 0; |
291 | } | 291 | } |
292 | 292 | ||
293 | /* Searching trace events corresponding to a probe event */ | 293 | /* Searching trace events corresponding to a probe event */ |
294 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); | 294 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); |
295 | 295 | ||
296 | debuginfo__delete(dinfo); | 296 | debuginfo__delete(dinfo); |
297 | 297 | ||
298 | if (ntevs > 0) { /* Succeeded to find trace events */ | 298 | if (ntevs > 0) { /* Succeeded to find trace events */ |
299 | pr_debug("find %d probe_trace_events.\n", ntevs); | 299 | pr_debug("find %d probe_trace_events.\n", ntevs); |
300 | if (module) | 300 | if (module) |
301 | ret = add_module_to_probe_trace_events(*tevs, ntevs, | 301 | ret = add_module_to_probe_trace_events(*tevs, ntevs, |
302 | module); | 302 | module); |
303 | return ret < 0 ? ret : ntevs; | 303 | return ret < 0 ? ret : ntevs; |
304 | } | 304 | } |
305 | 305 | ||
306 | if (ntevs == 0) { /* No error but failed to find probe point. */ | 306 | if (ntevs == 0) { /* No error but failed to find probe point. */ |
307 | pr_warning("Probe point '%s' not found.\n", | 307 | pr_warning("Probe point '%s' not found.\n", |
308 | synthesize_perf_probe_point(&pev->point)); | 308 | synthesize_perf_probe_point(&pev->point)); |
309 | return -ENOENT; | 309 | return -ENOENT; |
310 | } | 310 | } |
311 | /* Error path : ntevs < 0 */ | 311 | /* Error path : ntevs < 0 */ |
312 | pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); | 312 | pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); |
313 | if (ntevs == -EBADF) { | 313 | if (ntevs == -EBADF) { |
314 | pr_warning("Warning: No dwarf info found in the vmlinux - " | 314 | pr_warning("Warning: No dwarf info found in the vmlinux - " |
315 | "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); | 315 | "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); |
316 | if (!need_dwarf) { | 316 | if (!need_dwarf) { |
317 | pr_debug("Trying to use symbols.\n"); | 317 | pr_debug("Trying to use symbols.\n"); |
318 | return 0; | 318 | return 0; |
319 | } | 319 | } |
320 | } | 320 | } |
321 | return ntevs; | 321 | return ntevs; |
322 | } | 322 | } |
323 | 323 | ||
324 | /* | 324 | /* |
325 | * Find a src file from a DWARF tag path. Prepend optional source path prefix | 325 | * Find a src file from a DWARF tag path. Prepend optional source path prefix |
326 | * and chop off leading directories that do not exist. Result is passed back as | 326 | * and chop off leading directories that do not exist. Result is passed back as |
327 | * a newly allocated path on success. | 327 | * a newly allocated path on success. |
328 | * Return 0 if file was found and readable, -errno otherwise. | 328 | * Return 0 if file was found and readable, -errno otherwise. |
329 | */ | 329 | */ |
330 | static int get_real_path(const char *raw_path, const char *comp_dir, | 330 | static int get_real_path(const char *raw_path, const char *comp_dir, |
331 | char **new_path) | 331 | char **new_path) |
332 | { | 332 | { |
333 | const char *prefix = symbol_conf.source_prefix; | 333 | const char *prefix = symbol_conf.source_prefix; |
334 | 334 | ||
335 | if (!prefix) { | 335 | if (!prefix) { |
336 | if (raw_path[0] != '/' && comp_dir) | 336 | if (raw_path[0] != '/' && comp_dir) |
337 | /* If not an absolute path, try to use comp_dir */ | 337 | /* If not an absolute path, try to use comp_dir */ |
338 | prefix = comp_dir; | 338 | prefix = comp_dir; |
339 | else { | 339 | else { |
340 | if (access(raw_path, R_OK) == 0) { | 340 | if (access(raw_path, R_OK) == 0) { |
341 | *new_path = strdup(raw_path); | 341 | *new_path = strdup(raw_path); |
342 | return 0; | 342 | return 0; |
343 | } else | 343 | } else |
344 | return -errno; | 344 | return -errno; |
345 | } | 345 | } |
346 | } | 346 | } |
347 | 347 | ||
348 | *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2)); | 348 | *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2)); |
349 | if (!*new_path) | 349 | if (!*new_path) |
350 | return -ENOMEM; | 350 | return -ENOMEM; |
351 | 351 | ||
352 | for (;;) { | 352 | for (;;) { |
353 | sprintf(*new_path, "%s/%s", prefix, raw_path); | 353 | sprintf(*new_path, "%s/%s", prefix, raw_path); |
354 | 354 | ||
355 | if (access(*new_path, R_OK) == 0) | 355 | if (access(*new_path, R_OK) == 0) |
356 | return 0; | 356 | return 0; |
357 | 357 | ||
358 | if (!symbol_conf.source_prefix) | 358 | if (!symbol_conf.source_prefix) |
359 | /* In case of searching comp_dir, don't retry */ | 359 | /* In case of searching comp_dir, don't retry */ |
360 | return -errno; | 360 | return -errno; |
361 | 361 | ||
362 | switch (errno) { | 362 | switch (errno) { |
363 | case ENAMETOOLONG: | 363 | case ENAMETOOLONG: |
364 | case ENOENT: | 364 | case ENOENT: |
365 | case EROFS: | 365 | case EROFS: |
366 | case EFAULT: | 366 | case EFAULT: |
367 | raw_path = strchr(++raw_path, '/'); | 367 | raw_path = strchr(++raw_path, '/'); |
368 | if (!raw_path) { | 368 | if (!raw_path) { |
369 | free(*new_path); | 369 | free(*new_path); |
370 | *new_path = NULL; | 370 | *new_path = NULL; |
371 | return -ENOENT; | 371 | return -ENOENT; |
372 | } | 372 | } |
373 | continue; | 373 | continue; |
374 | 374 | ||
375 | default: | 375 | default: |
376 | free(*new_path); | 376 | free(*new_path); |
377 | *new_path = NULL; | 377 | *new_path = NULL; |
378 | return -errno; | 378 | return -errno; |
379 | } | 379 | } |
380 | } | 380 | } |
381 | } | 381 | } |
382 | 382 | ||
383 | #define LINEBUF_SIZE 256 | 383 | #define LINEBUF_SIZE 256 |
384 | #define NR_ADDITIONAL_LINES 2 | 384 | #define NR_ADDITIONAL_LINES 2 |
385 | 385 | ||
386 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) | 386 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) |
387 | { | 387 | { |
388 | char buf[LINEBUF_SIZE]; | 388 | char buf[LINEBUF_SIZE]; |
389 | const char *color = show_num ? "" : PERF_COLOR_BLUE; | 389 | const char *color = show_num ? "" : PERF_COLOR_BLUE; |
390 | const char *prefix = NULL; | 390 | const char *prefix = NULL; |
391 | 391 | ||
392 | do { | 392 | do { |
393 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | 393 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) |
394 | goto error; | 394 | goto error; |
395 | if (skip) | 395 | if (skip) |
396 | continue; | 396 | continue; |
397 | if (!prefix) { | 397 | if (!prefix) { |
398 | prefix = show_num ? "%7d " : " "; | 398 | prefix = show_num ? "%7d " : " "; |
399 | color_fprintf(stdout, color, prefix, l); | 399 | color_fprintf(stdout, color, prefix, l); |
400 | } | 400 | } |
401 | color_fprintf(stdout, color, "%s", buf); | 401 | color_fprintf(stdout, color, "%s", buf); |
402 | 402 | ||
403 | } while (strchr(buf, '\n') == NULL); | 403 | } while (strchr(buf, '\n') == NULL); |
404 | 404 | ||
405 | return 1; | 405 | return 1; |
406 | error: | 406 | error: |
407 | if (ferror(fp)) { | 407 | if (ferror(fp)) { |
408 | pr_warning("File read error: %s\n", strerror(errno)); | 408 | pr_warning("File read error: %s\n", strerror(errno)); |
409 | return -1; | 409 | return -1; |
410 | } | 410 | } |
411 | return 0; | 411 | return 0; |
412 | } | 412 | } |
413 | 413 | ||
414 | static int _show_one_line(FILE *fp, int l, bool skip, bool show_num) | 414 | static int _show_one_line(FILE *fp, int l, bool skip, bool show_num) |
415 | { | 415 | { |
416 | int rv = __show_one_line(fp, l, skip, show_num); | 416 | int rv = __show_one_line(fp, l, skip, show_num); |
417 | if (rv == 0) { | 417 | if (rv == 0) { |
418 | pr_warning("Source file is shorter than expected.\n"); | 418 | pr_warning("Source file is shorter than expected.\n"); |
419 | rv = -1; | 419 | rv = -1; |
420 | } | 420 | } |
421 | return rv; | 421 | return rv; |
422 | } | 422 | } |
423 | 423 | ||
424 | #define show_one_line_with_num(f,l) _show_one_line(f,l,false,true) | 424 | #define show_one_line_with_num(f,l) _show_one_line(f,l,false,true) |
425 | #define show_one_line(f,l) _show_one_line(f,l,false,false) | 425 | #define show_one_line(f,l) _show_one_line(f,l,false,false) |
426 | #define skip_one_line(f,l) _show_one_line(f,l,true,false) | 426 | #define skip_one_line(f,l) _show_one_line(f,l,true,false) |
427 | #define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false) | 427 | #define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false) |
428 | 428 | ||
429 | /* | 429 | /* |
430 | * Show line-range always requires debuginfo to find source file and | 430 | * Show line-range always requires debuginfo to find source file and |
431 | * line number. | 431 | * line number. |
432 | */ | 432 | */ |
433 | int show_line_range(struct line_range *lr, const char *module) | 433 | int show_line_range(struct line_range *lr, const char *module) |
434 | { | 434 | { |
435 | int l = 1; | 435 | int l = 1; |
436 | struct line_node *ln; | 436 | struct line_node *ln; |
437 | struct debuginfo *dinfo; | 437 | struct debuginfo *dinfo; |
438 | FILE *fp; | 438 | FILE *fp; |
439 | int ret; | 439 | int ret; |
440 | char *tmp; | 440 | char *tmp; |
441 | 441 | ||
442 | /* Search a line range */ | 442 | /* Search a line range */ |
443 | ret = init_vmlinux(); | 443 | ret = init_vmlinux(); |
444 | if (ret < 0) | 444 | if (ret < 0) |
445 | return ret; | 445 | return ret; |
446 | 446 | ||
447 | dinfo = open_debuginfo(module); | 447 | dinfo = open_debuginfo(module); |
448 | if (!dinfo) { | 448 | if (!dinfo) { |
449 | pr_warning("Failed to open debuginfo file.\n"); | 449 | pr_warning("Failed to open debuginfo file.\n"); |
450 | return -ENOENT; | 450 | return -ENOENT; |
451 | } | 451 | } |
452 | 452 | ||
453 | ret = debuginfo__find_line_range(dinfo, lr); | 453 | ret = debuginfo__find_line_range(dinfo, lr); |
454 | debuginfo__delete(dinfo); | 454 | debuginfo__delete(dinfo); |
455 | if (ret == 0) { | 455 | if (ret == 0) { |
456 | pr_warning("Specified source line is not found.\n"); | 456 | pr_warning("Specified source line is not found.\n"); |
457 | return -ENOENT; | 457 | return -ENOENT; |
458 | } else if (ret < 0) { | 458 | } else if (ret < 0) { |
459 | pr_warning("Debuginfo analysis failed. (%d)\n", ret); | 459 | pr_warning("Debuginfo analysis failed. (%d)\n", ret); |
460 | return ret; | 460 | return ret; |
461 | } | 461 | } |
462 | 462 | ||
463 | /* Convert source file path */ | 463 | /* Convert source file path */ |
464 | tmp = lr->path; | 464 | tmp = lr->path; |
465 | ret = get_real_path(tmp, lr->comp_dir, &lr->path); | 465 | ret = get_real_path(tmp, lr->comp_dir, &lr->path); |
466 | free(tmp); /* Free old path */ | 466 | free(tmp); /* Free old path */ |
467 | if (ret < 0) { | 467 | if (ret < 0) { |
468 | pr_warning("Failed to find source file. (%d)\n", ret); | 468 | pr_warning("Failed to find source file. (%d)\n", ret); |
469 | return ret; | 469 | return ret; |
470 | } | 470 | } |
471 | 471 | ||
472 | setup_pager(); | 472 | setup_pager(); |
473 | 473 | ||
474 | if (lr->function) | 474 | if (lr->function) |
475 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, | 475 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, |
476 | lr->start - lr->offset); | 476 | lr->start - lr->offset); |
477 | else | 477 | else |
478 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); | 478 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); |
479 | 479 | ||
480 | fp = fopen(lr->path, "r"); | 480 | fp = fopen(lr->path, "r"); |
481 | if (fp == NULL) { | 481 | if (fp == NULL) { |
482 | pr_warning("Failed to open %s: %s\n", lr->path, | 482 | pr_warning("Failed to open %s: %s\n", lr->path, |
483 | strerror(errno)); | 483 | strerror(errno)); |
484 | return -errno; | 484 | return -errno; |
485 | } | 485 | } |
486 | /* Skip to starting line number */ | 486 | /* Skip to starting line number */ |
487 | while (l < lr->start) { | 487 | while (l < lr->start) { |
488 | ret = skip_one_line(fp, l++); | 488 | ret = skip_one_line(fp, l++); |
489 | if (ret < 0) | 489 | if (ret < 0) |
490 | goto end; | 490 | goto end; |
491 | } | 491 | } |
492 | 492 | ||
493 | list_for_each_entry(ln, &lr->line_list, list) { | 493 | list_for_each_entry(ln, &lr->line_list, list) { |
494 | for (; ln->line > l; l++) { | 494 | for (; ln->line > l; l++) { |
495 | ret = show_one_line(fp, l - lr->offset); | 495 | ret = show_one_line(fp, l - lr->offset); |
496 | if (ret < 0) | 496 | if (ret < 0) |
497 | goto end; | 497 | goto end; |
498 | } | 498 | } |
499 | ret = show_one_line_with_num(fp, l++ - lr->offset); | 499 | ret = show_one_line_with_num(fp, l++ - lr->offset); |
500 | if (ret < 0) | 500 | if (ret < 0) |
501 | goto end; | 501 | goto end; |
502 | } | 502 | } |
503 | 503 | ||
504 | if (lr->end == INT_MAX) | 504 | if (lr->end == INT_MAX) |
505 | lr->end = l + NR_ADDITIONAL_LINES; | 505 | lr->end = l + NR_ADDITIONAL_LINES; |
506 | while (l <= lr->end) { | 506 | while (l <= lr->end) { |
507 | ret = show_one_line_or_eof(fp, l++ - lr->offset); | 507 | ret = show_one_line_or_eof(fp, l++ - lr->offset); |
508 | if (ret <= 0) | 508 | if (ret <= 0) |
509 | break; | 509 | break; |
510 | } | 510 | } |
511 | end: | 511 | end: |
512 | fclose(fp); | 512 | fclose(fp); |
513 | return ret; | 513 | return ret; |
514 | } | 514 | } |
515 | 515 | ||
516 | static int show_available_vars_at(struct debuginfo *dinfo, | 516 | static int show_available_vars_at(struct debuginfo *dinfo, |
517 | struct perf_probe_event *pev, | 517 | struct perf_probe_event *pev, |
518 | int max_vls, struct strfilter *_filter, | 518 | int max_vls, struct strfilter *_filter, |
519 | bool externs) | 519 | bool externs) |
520 | { | 520 | { |
521 | char *buf; | 521 | char *buf; |
522 | int ret, i, nvars; | 522 | int ret, i, nvars; |
523 | struct str_node *node; | 523 | struct str_node *node; |
524 | struct variable_list *vls = NULL, *vl; | 524 | struct variable_list *vls = NULL, *vl; |
525 | const char *var; | 525 | const char *var; |
526 | 526 | ||
527 | buf = synthesize_perf_probe_point(&pev->point); | 527 | buf = synthesize_perf_probe_point(&pev->point); |
528 | if (!buf) | 528 | if (!buf) |
529 | return -EINVAL; | 529 | return -EINVAL; |
530 | pr_debug("Searching variables at %s\n", buf); | 530 | pr_debug("Searching variables at %s\n", buf); |
531 | 531 | ||
532 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, | 532 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, |
533 | max_vls, externs); | 533 | max_vls, externs); |
534 | if (ret <= 0) { | 534 | if (ret <= 0) { |
535 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 535 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); |
536 | goto end; | 536 | goto end; |
537 | } | 537 | } |
538 | /* Some variables are found */ | 538 | /* Some variables are found */ |
539 | fprintf(stdout, "Available variables at %s\n", buf); | 539 | fprintf(stdout, "Available variables at %s\n", buf); |
540 | for (i = 0; i < ret; i++) { | 540 | for (i = 0; i < ret; i++) { |
541 | vl = &vls[i]; | 541 | vl = &vls[i]; |
542 | /* | 542 | /* |
543 | * A probe point might be converted to | 543 | * A probe point might be converted to |
544 | * several trace points. | 544 | * several trace points. |
545 | */ | 545 | */ |
546 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, | 546 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, |
547 | vl->point.offset); | 547 | vl->point.offset); |
548 | free(vl->point.symbol); | 548 | free(vl->point.symbol); |
549 | nvars = 0; | 549 | nvars = 0; |
550 | if (vl->vars) { | 550 | if (vl->vars) { |
551 | strlist__for_each(node, vl->vars) { | 551 | strlist__for_each(node, vl->vars) { |
552 | var = strchr(node->s, '\t') + 1; | 552 | var = strchr(node->s, '\t') + 1; |
553 | if (strfilter__compare(_filter, var)) { | 553 | if (strfilter__compare(_filter, var)) { |
554 | fprintf(stdout, "\t\t%s\n", node->s); | 554 | fprintf(stdout, "\t\t%s\n", node->s); |
555 | nvars++; | 555 | nvars++; |
556 | } | 556 | } |
557 | } | 557 | } |
558 | strlist__delete(vl->vars); | 558 | strlist__delete(vl->vars); |
559 | } | 559 | } |
560 | if (nvars == 0) | 560 | if (nvars == 0) |
561 | fprintf(stdout, "\t\t(No matched variables)\n"); | 561 | fprintf(stdout, "\t\t(No matched variables)\n"); |
562 | } | 562 | } |
563 | free(vls); | 563 | free(vls); |
564 | end: | 564 | end: |
565 | free(buf); | 565 | free(buf); |
566 | return ret; | 566 | return ret; |
567 | } | 567 | } |
568 | 568 | ||
569 | /* Show available variables on given probe point */ | 569 | /* Show available variables on given probe point */ |
570 | int show_available_vars(struct perf_probe_event *pevs, int npevs, | 570 | int show_available_vars(struct perf_probe_event *pevs, int npevs, |
571 | int max_vls, const char *module, | 571 | int max_vls, const char *module, |
572 | struct strfilter *_filter, bool externs) | 572 | struct strfilter *_filter, bool externs) |
573 | { | 573 | { |
574 | int i, ret = 0; | 574 | int i, ret = 0; |
575 | struct debuginfo *dinfo; | 575 | struct debuginfo *dinfo; |
576 | 576 | ||
577 | ret = init_vmlinux(); | 577 | ret = init_vmlinux(); |
578 | if (ret < 0) | 578 | if (ret < 0) |
579 | return ret; | 579 | return ret; |
580 | 580 | ||
581 | dinfo = open_debuginfo(module); | 581 | dinfo = open_debuginfo(module); |
582 | if (!dinfo) { | 582 | if (!dinfo) { |
583 | pr_warning("Failed to open debuginfo file.\n"); | 583 | pr_warning("Failed to open debuginfo file.\n"); |
584 | return -ENOENT; | 584 | return -ENOENT; |
585 | } | 585 | } |
586 | 586 | ||
587 | setup_pager(); | 587 | setup_pager(); |
588 | 588 | ||
589 | for (i = 0; i < npevs && ret >= 0; i++) | 589 | for (i = 0; i < npevs && ret >= 0; i++) |
590 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, | 590 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, |
591 | externs); | 591 | externs); |
592 | 592 | ||
593 | debuginfo__delete(dinfo); | 593 | debuginfo__delete(dinfo); |
594 | return ret; | 594 | return ret; |
595 | } | 595 | } |
596 | 596 | ||
597 | #else /* !DWARF_SUPPORT */ | 597 | #else /* !DWARF_SUPPORT */ |
598 | 598 | ||
599 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | 599 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, |
600 | struct perf_probe_point *pp) | 600 | struct perf_probe_point *pp) |
601 | { | 601 | { |
602 | struct symbol *sym; | 602 | struct symbol *sym; |
603 | 603 | ||
604 | sym = __find_kernel_function_by_name(tp->symbol, NULL); | 604 | sym = __find_kernel_function_by_name(tp->symbol, NULL); |
605 | if (!sym) { | 605 | if (!sym) { |
606 | pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); | 606 | pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); |
607 | return -ENOENT; | 607 | return -ENOENT; |
608 | } | 608 | } |
609 | pp->function = strdup(tp->symbol); | 609 | pp->function = strdup(tp->symbol); |
610 | if (pp->function == NULL) | 610 | if (pp->function == NULL) |
611 | return -ENOMEM; | 611 | return -ENOMEM; |
612 | pp->offset = tp->offset; | 612 | pp->offset = tp->offset; |
613 | pp->retprobe = tp->retprobe; | 613 | pp->retprobe = tp->retprobe; |
614 | 614 | ||
615 | return 0; | 615 | return 0; |
616 | } | 616 | } |
617 | 617 | ||
618 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 618 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
619 | struct probe_trace_event **tevs __unused, | 619 | struct probe_trace_event **tevs __unused, |
620 | int max_tevs __unused, const char *mod __unused) | 620 | int max_tevs __unused, const char *mod __unused) |
621 | { | 621 | { |
622 | if (perf_probe_event_need_dwarf(pev)) { | 622 | if (perf_probe_event_need_dwarf(pev)) { |
623 | pr_warning("Debuginfo-analysis is not supported.\n"); | 623 | pr_warning("Debuginfo-analysis is not supported.\n"); |
624 | return -ENOSYS; | 624 | return -ENOSYS; |
625 | } | 625 | } |
626 | return 0; | 626 | return 0; |
627 | } | 627 | } |
628 | 628 | ||
629 | int show_line_range(struct line_range *lr __unused, const char *module __unused) | 629 | int show_line_range(struct line_range *lr __unused, const char *module __unused) |
630 | { | 630 | { |
631 | pr_warning("Debuginfo-analysis is not supported.\n"); | 631 | pr_warning("Debuginfo-analysis is not supported.\n"); |
632 | return -ENOSYS; | 632 | return -ENOSYS; |
633 | } | 633 | } |
634 | 634 | ||
635 | int show_available_vars(struct perf_probe_event *pevs __unused, | 635 | int show_available_vars(struct perf_probe_event *pevs __unused, |
636 | int npevs __unused, int max_vls __unused, | 636 | int npevs __unused, int max_vls __unused, |
637 | const char *module __unused, | 637 | const char *module __unused, |
638 | struct strfilter *filter __unused, | 638 | struct strfilter *filter __unused, |
639 | bool externs __unused) | 639 | bool externs __unused) |
640 | { | 640 | { |
641 | pr_warning("Debuginfo-analysis is not supported.\n"); | 641 | pr_warning("Debuginfo-analysis is not supported.\n"); |
642 | return -ENOSYS; | 642 | return -ENOSYS; |
643 | } | 643 | } |
644 | #endif | 644 | #endif |
645 | 645 | ||
646 | static int parse_line_num(char **ptr, int *val, const char *what) | 646 | static int parse_line_num(char **ptr, int *val, const char *what) |
647 | { | 647 | { |
648 | const char *start = *ptr; | 648 | const char *start = *ptr; |
649 | 649 | ||
650 | errno = 0; | 650 | errno = 0; |
651 | *val = strtol(*ptr, ptr, 0); | 651 | *val = strtol(*ptr, ptr, 0); |
652 | if (errno || *ptr == start) { | 652 | if (errno || *ptr == start) { |
653 | semantic_error("'%s' is not a valid number.\n", what); | 653 | semantic_error("'%s' is not a valid number.\n", what); |
654 | return -EINVAL; | 654 | return -EINVAL; |
655 | } | 655 | } |
656 | return 0; | 656 | return 0; |
657 | } | 657 | } |
658 | 658 | ||
659 | /* | 659 | /* |
660 | * Stuff 'lr' according to the line range described by 'arg'. | 660 | * Stuff 'lr' according to the line range described by 'arg'. |
661 | * The line range syntax is described by: | 661 | * The line range syntax is described by: |
662 | * | 662 | * |
663 | * SRC[:SLN[+NUM|-ELN]] | 663 | * SRC[:SLN[+NUM|-ELN]] |
664 | * FNC[@SRC][:SLN[+NUM|-ELN]] | 664 | * FNC[@SRC][:SLN[+NUM|-ELN]] |
665 | */ | 665 | */ |
666 | int parse_line_range_desc(const char *arg, struct line_range *lr) | 666 | int parse_line_range_desc(const char *arg, struct line_range *lr) |
667 | { | 667 | { |
668 | char *range, *file, *name = strdup(arg); | 668 | char *range, *file, *name = strdup(arg); |
669 | int err; | 669 | int err; |
670 | 670 | ||
671 | if (!name) | 671 | if (!name) |
672 | return -ENOMEM; | 672 | return -ENOMEM; |
673 | 673 | ||
674 | lr->start = 0; | 674 | lr->start = 0; |
675 | lr->end = INT_MAX; | 675 | lr->end = INT_MAX; |
676 | 676 | ||
677 | range = strchr(name, ':'); | 677 | range = strchr(name, ':'); |
678 | if (range) { | 678 | if (range) { |
679 | *range++ = '\0'; | 679 | *range++ = '\0'; |
680 | 680 | ||
681 | err = parse_line_num(&range, &lr->start, "start line"); | 681 | err = parse_line_num(&range, &lr->start, "start line"); |
682 | if (err) | 682 | if (err) |
683 | goto err; | 683 | goto err; |
684 | 684 | ||
685 | if (*range == '+' || *range == '-') { | 685 | if (*range == '+' || *range == '-') { |
686 | const char c = *range++; | 686 | const char c = *range++; |
687 | 687 | ||
688 | err = parse_line_num(&range, &lr->end, "end line"); | 688 | err = parse_line_num(&range, &lr->end, "end line"); |
689 | if (err) | 689 | if (err) |
690 | goto err; | 690 | goto err; |
691 | 691 | ||
692 | if (c == '+') { | 692 | if (c == '+') { |
693 | lr->end += lr->start; | 693 | lr->end += lr->start; |
694 | /* | 694 | /* |
695 | * Adjust the number of lines here. | 695 | * Adjust the number of lines here. |
696 | * If the number of lines == 1, the | 696 | * If the number of lines == 1, the |
697 | * the end of line should be equal to | 697 | * the end of line should be equal to |
698 | * the start of line. | 698 | * the start of line. |
699 | */ | 699 | */ |
700 | lr->end--; | 700 | lr->end--; |
701 | } | 701 | } |
702 | } | 702 | } |
703 | 703 | ||
704 | pr_debug("Line range is %d to %d\n", lr->start, lr->end); | 704 | pr_debug("Line range is %d to %d\n", lr->start, lr->end); |
705 | 705 | ||
706 | err = -EINVAL; | 706 | err = -EINVAL; |
707 | if (lr->start > lr->end) { | 707 | if (lr->start > lr->end) { |
708 | semantic_error("Start line must be smaller" | 708 | semantic_error("Start line must be smaller" |
709 | " than end line.\n"); | 709 | " than end line.\n"); |
710 | goto err; | 710 | goto err; |
711 | } | 711 | } |
712 | if (*range != '\0') { | 712 | if (*range != '\0') { |
713 | semantic_error("Tailing with invalid str '%s'.\n", range); | 713 | semantic_error("Tailing with invalid str '%s'.\n", range); |
714 | goto err; | 714 | goto err; |
715 | } | 715 | } |
716 | } | 716 | } |
717 | 717 | ||
718 | file = strchr(name, '@'); | 718 | file = strchr(name, '@'); |
719 | if (file) { | 719 | if (file) { |
720 | *file = '\0'; | 720 | *file = '\0'; |
721 | lr->file = strdup(++file); | 721 | lr->file = strdup(++file); |
722 | if (lr->file == NULL) { | 722 | if (lr->file == NULL) { |
723 | err = -ENOMEM; | 723 | err = -ENOMEM; |
724 | goto err; | 724 | goto err; |
725 | } | 725 | } |
726 | lr->function = name; | 726 | lr->function = name; |
727 | } else if (strchr(name, '.')) | 727 | } else if (strchr(name, '.')) |
728 | lr->file = name; | 728 | lr->file = name; |
729 | else | 729 | else |
730 | lr->function = name; | 730 | lr->function = name; |
731 | 731 | ||
732 | return 0; | 732 | return 0; |
733 | err: | 733 | err: |
734 | free(name); | 734 | free(name); |
735 | return err; | 735 | return err; |
736 | } | 736 | } |
737 | 737 | ||
738 | /* Check the name is good for event/group */ | 738 | /* Check the name is good for event/group */ |
739 | static bool check_event_name(const char *name) | 739 | static bool check_event_name(const char *name) |
740 | { | 740 | { |
741 | if (!isalpha(*name) && *name != '_') | 741 | if (!isalpha(*name) && *name != '_') |
742 | return false; | 742 | return false; |
743 | while (*++name != '\0') { | 743 | while (*++name != '\0') { |
744 | if (!isalpha(*name) && !isdigit(*name) && *name != '_') | 744 | if (!isalpha(*name) && !isdigit(*name) && *name != '_') |
745 | return false; | 745 | return false; |
746 | } | 746 | } |
747 | return true; | 747 | return true; |
748 | } | 748 | } |
749 | 749 | ||
750 | /* Parse probepoint definition. */ | 750 | /* Parse probepoint definition. */ |
751 | static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | 751 | static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) |
752 | { | 752 | { |
753 | struct perf_probe_point *pp = &pev->point; | 753 | struct perf_probe_point *pp = &pev->point; |
754 | char *ptr, *tmp; | 754 | char *ptr, *tmp; |
755 | char c, nc = 0; | 755 | char c, nc = 0; |
756 | /* | 756 | /* |
757 | * <Syntax> | 757 | * <Syntax> |
758 | * perf probe [EVENT=]SRC[:LN|;PTN] | 758 | * perf probe [EVENT=]SRC[:LN|;PTN] |
759 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] | 759 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] |
760 | * | 760 | * |
761 | * TODO:Group name support | 761 | * TODO:Group name support |
762 | */ | 762 | */ |
763 | 763 | ||
764 | ptr = strpbrk(arg, ";=@+%"); | 764 | ptr = strpbrk(arg, ";=@+%"); |
765 | if (ptr && *ptr == '=') { /* Event name */ | 765 | if (ptr && *ptr == '=') { /* Event name */ |
766 | *ptr = '\0'; | 766 | *ptr = '\0'; |
767 | tmp = ptr + 1; | 767 | tmp = ptr + 1; |
768 | if (strchr(arg, ':')) { | 768 | if (strchr(arg, ':')) { |
769 | semantic_error("Group name is not supported yet.\n"); | 769 | semantic_error("Group name is not supported yet.\n"); |
770 | return -ENOTSUP; | 770 | return -ENOTSUP; |
771 | } | 771 | } |
772 | if (!check_event_name(arg)) { | 772 | if (!check_event_name(arg)) { |
773 | semantic_error("%s is bad for event name -it must " | 773 | semantic_error("%s is bad for event name -it must " |
774 | "follow C symbol-naming rule.\n", arg); | 774 | "follow C symbol-naming rule.\n", arg); |
775 | return -EINVAL; | 775 | return -EINVAL; |
776 | } | 776 | } |
777 | pev->event = strdup(arg); | 777 | pev->event = strdup(arg); |
778 | if (pev->event == NULL) | 778 | if (pev->event == NULL) |
779 | return -ENOMEM; | 779 | return -ENOMEM; |
780 | pev->group = NULL; | 780 | pev->group = NULL; |
781 | arg = tmp; | 781 | arg = tmp; |
782 | } | 782 | } |
783 | 783 | ||
784 | ptr = strpbrk(arg, ";:+@%"); | 784 | ptr = strpbrk(arg, ";:+@%"); |
785 | if (ptr) { | 785 | if (ptr) { |
786 | nc = *ptr; | 786 | nc = *ptr; |
787 | *ptr++ = '\0'; | 787 | *ptr++ = '\0'; |
788 | } | 788 | } |
789 | 789 | ||
790 | tmp = strdup(arg); | 790 | tmp = strdup(arg); |
791 | if (tmp == NULL) | 791 | if (tmp == NULL) |
792 | return -ENOMEM; | 792 | return -ENOMEM; |
793 | 793 | ||
794 | /* Check arg is function or file and copy it */ | 794 | /* Check arg is function or file and copy it */ |
795 | if (strchr(tmp, '.')) /* File */ | 795 | if (strchr(tmp, '.')) /* File */ |
796 | pp->file = tmp; | 796 | pp->file = tmp; |
797 | else /* Function */ | 797 | else /* Function */ |
798 | pp->function = tmp; | 798 | pp->function = tmp; |
799 | 799 | ||
800 | /* Parse other options */ | 800 | /* Parse other options */ |
801 | while (ptr) { | 801 | while (ptr) { |
802 | arg = ptr; | 802 | arg = ptr; |
803 | c = nc; | 803 | c = nc; |
804 | if (c == ';') { /* Lazy pattern must be the last part */ | 804 | if (c == ';') { /* Lazy pattern must be the last part */ |
805 | pp->lazy_line = strdup(arg); | 805 | pp->lazy_line = strdup(arg); |
806 | if (pp->lazy_line == NULL) | 806 | if (pp->lazy_line == NULL) |
807 | return -ENOMEM; | 807 | return -ENOMEM; |
808 | break; | 808 | break; |
809 | } | 809 | } |
810 | ptr = strpbrk(arg, ";:+@%"); | 810 | ptr = strpbrk(arg, ";:+@%"); |
811 | if (ptr) { | 811 | if (ptr) { |
812 | nc = *ptr; | 812 | nc = *ptr; |
813 | *ptr++ = '\0'; | 813 | *ptr++ = '\0'; |
814 | } | 814 | } |
815 | switch (c) { | 815 | switch (c) { |
816 | case ':': /* Line number */ | 816 | case ':': /* Line number */ |
817 | pp->line = strtoul(arg, &tmp, 0); | 817 | pp->line = strtoul(arg, &tmp, 0); |
818 | if (*tmp != '\0') { | 818 | if (*tmp != '\0') { |
819 | semantic_error("There is non-digit char" | 819 | semantic_error("There is non-digit char" |
820 | " in line number.\n"); | 820 | " in line number.\n"); |
821 | return -EINVAL; | 821 | return -EINVAL; |
822 | } | 822 | } |
823 | break; | 823 | break; |
824 | case '+': /* Byte offset from a symbol */ | 824 | case '+': /* Byte offset from a symbol */ |
825 | pp->offset = strtoul(arg, &tmp, 0); | 825 | pp->offset = strtoul(arg, &tmp, 0); |
826 | if (*tmp != '\0') { | 826 | if (*tmp != '\0') { |
827 | semantic_error("There is non-digit character" | 827 | semantic_error("There is non-digit character" |
828 | " in offset.\n"); | 828 | " in offset.\n"); |
829 | return -EINVAL; | 829 | return -EINVAL; |
830 | } | 830 | } |
831 | break; | 831 | break; |
832 | case '@': /* File name */ | 832 | case '@': /* File name */ |
833 | if (pp->file) { | 833 | if (pp->file) { |
834 | semantic_error("SRC@SRC is not allowed.\n"); | 834 | semantic_error("SRC@SRC is not allowed.\n"); |
835 | return -EINVAL; | 835 | return -EINVAL; |
836 | } | 836 | } |
837 | pp->file = strdup(arg); | 837 | pp->file = strdup(arg); |
838 | if (pp->file == NULL) | 838 | if (pp->file == NULL) |
839 | return -ENOMEM; | 839 | return -ENOMEM; |
840 | break; | 840 | break; |
841 | case '%': /* Probe places */ | 841 | case '%': /* Probe places */ |
842 | if (strcmp(arg, "return") == 0) { | 842 | if (strcmp(arg, "return") == 0) { |
843 | pp->retprobe = 1; | 843 | pp->retprobe = 1; |
844 | } else { /* Others not supported yet */ | 844 | } else { /* Others not supported yet */ |
845 | semantic_error("%%%s is not supported.\n", arg); | 845 | semantic_error("%%%s is not supported.\n", arg); |
846 | return -ENOTSUP; | 846 | return -ENOTSUP; |
847 | } | 847 | } |
848 | break; | 848 | break; |
849 | default: /* Buggy case */ | 849 | default: /* Buggy case */ |
850 | pr_err("This program has a bug at %s:%d.\n", | 850 | pr_err("This program has a bug at %s:%d.\n", |
851 | __FILE__, __LINE__); | 851 | __FILE__, __LINE__); |
852 | return -ENOTSUP; | 852 | return -ENOTSUP; |
853 | break; | 853 | break; |
854 | } | 854 | } |
855 | } | 855 | } |
856 | 856 | ||
857 | /* Exclusion check */ | 857 | /* Exclusion check */ |
858 | if (pp->lazy_line && pp->line) { | 858 | if (pp->lazy_line && pp->line) { |
859 | semantic_error("Lazy pattern can't be used with" | 859 | semantic_error("Lazy pattern can't be used with" |
860 | " line number.\n"); | 860 | " line number.\n"); |
861 | return -EINVAL; | 861 | return -EINVAL; |
862 | } | 862 | } |
863 | 863 | ||
864 | if (pp->lazy_line && pp->offset) { | 864 | if (pp->lazy_line && pp->offset) { |
865 | semantic_error("Lazy pattern can't be used with offset.\n"); | 865 | semantic_error("Lazy pattern can't be used with offset.\n"); |
866 | return -EINVAL; | 866 | return -EINVAL; |
867 | } | 867 | } |
868 | 868 | ||
869 | if (pp->line && pp->offset) { | 869 | if (pp->line && pp->offset) { |
870 | semantic_error("Offset can't be used with line number.\n"); | 870 | semantic_error("Offset can't be used with line number.\n"); |
871 | return -EINVAL; | 871 | return -EINVAL; |
872 | } | 872 | } |
873 | 873 | ||
874 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { | 874 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { |
875 | semantic_error("File always requires line number or " | 875 | semantic_error("File always requires line number or " |
876 | "lazy pattern.\n"); | 876 | "lazy pattern.\n"); |
877 | return -EINVAL; | 877 | return -EINVAL; |
878 | } | 878 | } |
879 | 879 | ||
880 | if (pp->offset && !pp->function) { | 880 | if (pp->offset && !pp->function) { |
881 | semantic_error("Offset requires an entry function.\n"); | 881 | semantic_error("Offset requires an entry function.\n"); |
882 | return -EINVAL; | 882 | return -EINVAL; |
883 | } | 883 | } |
884 | 884 | ||
885 | if (pp->retprobe && !pp->function) { | 885 | if (pp->retprobe && !pp->function) { |
886 | semantic_error("Return probe requires an entry function.\n"); | 886 | semantic_error("Return probe requires an entry function.\n"); |
887 | return -EINVAL; | 887 | return -EINVAL; |
888 | } | 888 | } |
889 | 889 | ||
890 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { | 890 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { |
891 | semantic_error("Offset/Line/Lazy pattern can't be used with " | 891 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
892 | "return probe.\n"); | 892 | "return probe.\n"); |
893 | return -EINVAL; | 893 | return -EINVAL; |
894 | } | 894 | } |
895 | 895 | ||
896 | pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", | 896 | pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", |
897 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, | 897 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, |
898 | pp->lazy_line); | 898 | pp->lazy_line); |
899 | return 0; | 899 | return 0; |
900 | } | 900 | } |
901 | 901 | ||
902 | /* Parse perf-probe event argument */ | 902 | /* Parse perf-probe event argument */ |
903 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | 903 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) |
904 | { | 904 | { |
905 | char *tmp, *goodname; | 905 | char *tmp, *goodname; |
906 | struct perf_probe_arg_field **fieldp; | 906 | struct perf_probe_arg_field **fieldp; |
907 | 907 | ||
908 | pr_debug("parsing arg: %s into ", str); | 908 | pr_debug("parsing arg: %s into ", str); |
909 | 909 | ||
910 | tmp = strchr(str, '='); | 910 | tmp = strchr(str, '='); |
911 | if (tmp) { | 911 | if (tmp) { |
912 | arg->name = strndup(str, tmp - str); | 912 | arg->name = strndup(str, tmp - str); |
913 | if (arg->name == NULL) | 913 | if (arg->name == NULL) |
914 | return -ENOMEM; | 914 | return -ENOMEM; |
915 | pr_debug("name:%s ", arg->name); | 915 | pr_debug("name:%s ", arg->name); |
916 | str = tmp + 1; | 916 | str = tmp + 1; |
917 | } | 917 | } |
918 | 918 | ||
919 | tmp = strchr(str, ':'); | 919 | tmp = strchr(str, ':'); |
920 | if (tmp) { /* Type setting */ | 920 | if (tmp) { /* Type setting */ |
921 | *tmp = '\0'; | 921 | *tmp = '\0'; |
922 | arg->type = strdup(tmp + 1); | 922 | arg->type = strdup(tmp + 1); |
923 | if (arg->type == NULL) | 923 | if (arg->type == NULL) |
924 | return -ENOMEM; | 924 | return -ENOMEM; |
925 | pr_debug("type:%s ", arg->type); | 925 | pr_debug("type:%s ", arg->type); |
926 | } | 926 | } |
927 | 927 | ||
928 | tmp = strpbrk(str, "-.["); | 928 | tmp = strpbrk(str, "-.["); |
929 | if (!is_c_varname(str) || !tmp) { | 929 | if (!is_c_varname(str) || !tmp) { |
930 | /* A variable, register, symbol or special value */ | 930 | /* A variable, register, symbol or special value */ |
931 | arg->var = strdup(str); | 931 | arg->var = strdup(str); |
932 | if (arg->var == NULL) | 932 | if (arg->var == NULL) |
933 | return -ENOMEM; | 933 | return -ENOMEM; |
934 | pr_debug("%s\n", arg->var); | 934 | pr_debug("%s\n", arg->var); |
935 | return 0; | 935 | return 0; |
936 | } | 936 | } |
937 | 937 | ||
938 | /* Structure fields or array element */ | 938 | /* Structure fields or array element */ |
939 | arg->var = strndup(str, tmp - str); | 939 | arg->var = strndup(str, tmp - str); |
940 | if (arg->var == NULL) | 940 | if (arg->var == NULL) |
941 | return -ENOMEM; | 941 | return -ENOMEM; |
942 | goodname = arg->var; | 942 | goodname = arg->var; |
943 | pr_debug("%s, ", arg->var); | 943 | pr_debug("%s, ", arg->var); |
944 | fieldp = &arg->field; | 944 | fieldp = &arg->field; |
945 | 945 | ||
946 | do { | 946 | do { |
947 | *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); | 947 | *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); |
948 | if (*fieldp == NULL) | 948 | if (*fieldp == NULL) |
949 | return -ENOMEM; | 949 | return -ENOMEM; |
950 | if (*tmp == '[') { /* Array */ | 950 | if (*tmp == '[') { /* Array */ |
951 | str = tmp; | 951 | str = tmp; |
952 | (*fieldp)->index = strtol(str + 1, &tmp, 0); | 952 | (*fieldp)->index = strtol(str + 1, &tmp, 0); |
953 | (*fieldp)->ref = true; | 953 | (*fieldp)->ref = true; |
954 | if (*tmp != ']' || tmp == str + 1) { | 954 | if (*tmp != ']' || tmp == str + 1) { |
955 | semantic_error("Array index must be a" | 955 | semantic_error("Array index must be a" |
956 | " number.\n"); | 956 | " number.\n"); |
957 | return -EINVAL; | 957 | return -EINVAL; |
958 | } | 958 | } |
959 | tmp++; | 959 | tmp++; |
960 | if (*tmp == '\0') | 960 | if (*tmp == '\0') |
961 | tmp = NULL; | 961 | tmp = NULL; |
962 | } else { /* Structure */ | 962 | } else { /* Structure */ |
963 | if (*tmp == '.') { | 963 | if (*tmp == '.') { |
964 | str = tmp + 1; | 964 | str = tmp + 1; |
965 | (*fieldp)->ref = false; | 965 | (*fieldp)->ref = false; |
966 | } else if (tmp[1] == '>') { | 966 | } else if (tmp[1] == '>') { |
967 | str = tmp + 2; | 967 | str = tmp + 2; |
968 | (*fieldp)->ref = true; | 968 | (*fieldp)->ref = true; |
969 | } else { | 969 | } else { |
970 | semantic_error("Argument parse error: %s\n", | 970 | semantic_error("Argument parse error: %s\n", |
971 | str); | 971 | str); |
972 | return -EINVAL; | 972 | return -EINVAL; |
973 | } | 973 | } |
974 | tmp = strpbrk(str, "-.["); | 974 | tmp = strpbrk(str, "-.["); |
975 | } | 975 | } |
976 | if (tmp) { | 976 | if (tmp) { |
977 | (*fieldp)->name = strndup(str, tmp - str); | 977 | (*fieldp)->name = strndup(str, tmp - str); |
978 | if ((*fieldp)->name == NULL) | 978 | if ((*fieldp)->name == NULL) |
979 | return -ENOMEM; | 979 | return -ENOMEM; |
980 | if (*str != '[') | 980 | if (*str != '[') |
981 | goodname = (*fieldp)->name; | 981 | goodname = (*fieldp)->name; |
982 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); | 982 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); |
983 | fieldp = &(*fieldp)->next; | 983 | fieldp = &(*fieldp)->next; |
984 | } | 984 | } |
985 | } while (tmp); | 985 | } while (tmp); |
986 | (*fieldp)->name = strdup(str); | 986 | (*fieldp)->name = strdup(str); |
987 | if ((*fieldp)->name == NULL) | 987 | if ((*fieldp)->name == NULL) |
988 | return -ENOMEM; | 988 | return -ENOMEM; |
989 | if (*str != '[') | 989 | if (*str != '[') |
990 | goodname = (*fieldp)->name; | 990 | goodname = (*fieldp)->name; |
991 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); | 991 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); |
992 | 992 | ||
993 | /* If no name is specified, set the last field name (not array index)*/ | 993 | /* If no name is specified, set the last field name (not array index)*/ |
994 | if (!arg->name) { | 994 | if (!arg->name) { |
995 | arg->name = strdup(goodname); | 995 | arg->name = strdup(goodname); |
996 | if (arg->name == NULL) | 996 | if (arg->name == NULL) |
997 | return -ENOMEM; | 997 | return -ENOMEM; |
998 | } | 998 | } |
999 | return 0; | 999 | return 0; |
1000 | } | 1000 | } |
1001 | 1001 | ||
1002 | /* Parse perf-probe event command */ | 1002 | /* Parse perf-probe event command */ |
1003 | int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) | 1003 | int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) |
1004 | { | 1004 | { |
1005 | char **argv; | 1005 | char **argv; |
1006 | int argc, i, ret = 0; | 1006 | int argc, i, ret = 0; |
1007 | 1007 | ||
1008 | argv = argv_split(cmd, &argc); | 1008 | argv = argv_split(cmd, &argc); |
1009 | if (!argv) { | 1009 | if (!argv) { |
1010 | pr_debug("Failed to split arguments.\n"); | 1010 | pr_debug("Failed to split arguments.\n"); |
1011 | return -ENOMEM; | 1011 | return -ENOMEM; |
1012 | } | 1012 | } |
1013 | if (argc - 1 > MAX_PROBE_ARGS) { | 1013 | if (argc - 1 > MAX_PROBE_ARGS) { |
1014 | semantic_error("Too many probe arguments (%d).\n", argc - 1); | 1014 | semantic_error("Too many probe arguments (%d).\n", argc - 1); |
1015 | ret = -ERANGE; | 1015 | ret = -ERANGE; |
1016 | goto out; | 1016 | goto out; |
1017 | } | 1017 | } |
1018 | /* Parse probe point */ | 1018 | /* Parse probe point */ |
1019 | ret = parse_perf_probe_point(argv[0], pev); | 1019 | ret = parse_perf_probe_point(argv[0], pev); |
1020 | if (ret < 0) | 1020 | if (ret < 0) |
1021 | goto out; | 1021 | goto out; |
1022 | 1022 | ||
1023 | /* Copy arguments and ensure return probe has no C argument */ | 1023 | /* Copy arguments and ensure return probe has no C argument */ |
1024 | pev->nargs = argc - 1; | 1024 | pev->nargs = argc - 1; |
1025 | pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); | 1025 | pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); |
1026 | if (pev->args == NULL) { | 1026 | if (pev->args == NULL) { |
1027 | ret = -ENOMEM; | 1027 | ret = -ENOMEM; |
1028 | goto out; | 1028 | goto out; |
1029 | } | 1029 | } |
1030 | for (i = 0; i < pev->nargs && ret >= 0; i++) { | 1030 | for (i = 0; i < pev->nargs && ret >= 0; i++) { |
1031 | ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); | 1031 | ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); |
1032 | if (ret >= 0 && | 1032 | if (ret >= 0 && |
1033 | is_c_varname(pev->args[i].var) && pev->point.retprobe) { | 1033 | is_c_varname(pev->args[i].var) && pev->point.retprobe) { |
1034 | semantic_error("You can't specify local variable for" | 1034 | semantic_error("You can't specify local variable for" |
1035 | " kretprobe.\n"); | 1035 | " kretprobe.\n"); |
1036 | ret = -EINVAL; | 1036 | ret = -EINVAL; |
1037 | } | 1037 | } |
1038 | } | 1038 | } |
1039 | out: | 1039 | out: |
1040 | argv_free(argv); | 1040 | argv_free(argv); |
1041 | 1041 | ||
1042 | return ret; | 1042 | return ret; |
1043 | } | 1043 | } |
1044 | 1044 | ||
1045 | /* Return true if this perf_probe_event requires debuginfo */ | 1045 | /* Return true if this perf_probe_event requires debuginfo */ |
1046 | bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) | 1046 | bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) |
1047 | { | 1047 | { |
1048 | int i; | 1048 | int i; |
1049 | 1049 | ||
1050 | if (pev->point.file || pev->point.line || pev->point.lazy_line) | 1050 | if (pev->point.file || pev->point.line || pev->point.lazy_line) |
1051 | return true; | 1051 | return true; |
1052 | 1052 | ||
1053 | for (i = 0; i < pev->nargs; i++) | 1053 | for (i = 0; i < pev->nargs; i++) |
1054 | if (is_c_varname(pev->args[i].var)) | 1054 | if (is_c_varname(pev->args[i].var)) |
1055 | return true; | 1055 | return true; |
1056 | 1056 | ||
1057 | return false; | 1057 | return false; |
1058 | } | 1058 | } |
1059 | 1059 | ||
1060 | /* Parse probe_events event into struct probe_point */ | 1060 | /* Parse probe_events event into struct probe_point */ |
1061 | static int parse_probe_trace_command(const char *cmd, | 1061 | static int parse_probe_trace_command(const char *cmd, |
1062 | struct probe_trace_event *tev) | 1062 | struct probe_trace_event *tev) |
1063 | { | 1063 | { |
1064 | struct probe_trace_point *tp = &tev->point; | 1064 | struct probe_trace_point *tp = &tev->point; |
1065 | char pr; | 1065 | char pr; |
1066 | char *p; | 1066 | char *p; |
1067 | int ret, i, argc; | 1067 | int ret, i, argc; |
1068 | char **argv; | 1068 | char **argv; |
1069 | 1069 | ||
1070 | pr_debug("Parsing probe_events: %s\n", cmd); | 1070 | pr_debug("Parsing probe_events: %s\n", cmd); |
1071 | argv = argv_split(cmd, &argc); | 1071 | argv = argv_split(cmd, &argc); |
1072 | if (!argv) { | 1072 | if (!argv) { |
1073 | pr_debug("Failed to split arguments.\n"); | 1073 | pr_debug("Failed to split arguments.\n"); |
1074 | return -ENOMEM; | 1074 | return -ENOMEM; |
1075 | } | 1075 | } |
1076 | if (argc < 2) { | 1076 | if (argc < 2) { |
1077 | semantic_error("Too few probe arguments.\n"); | 1077 | semantic_error("Too few probe arguments.\n"); |
1078 | ret = -ERANGE; | 1078 | ret = -ERANGE; |
1079 | goto out; | 1079 | goto out; |
1080 | } | 1080 | } |
1081 | 1081 | ||
1082 | /* Scan event and group name. */ | 1082 | /* Scan event and group name. */ |
1083 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", | 1083 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", |
1084 | &pr, (float *)(void *)&tev->group, | 1084 | &pr, (float *)(void *)&tev->group, |
1085 | (float *)(void *)&tev->event); | 1085 | (float *)(void *)&tev->event); |
1086 | if (ret != 3) { | 1086 | if (ret != 3) { |
1087 | semantic_error("Failed to parse event name: %s\n", argv[0]); | 1087 | semantic_error("Failed to parse event name: %s\n", argv[0]); |
1088 | ret = -EINVAL; | 1088 | ret = -EINVAL; |
1089 | goto out; | 1089 | goto out; |
1090 | } | 1090 | } |
1091 | pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); | 1091 | pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); |
1092 | 1092 | ||
1093 | tp->retprobe = (pr == 'r'); | 1093 | tp->retprobe = (pr == 'r'); |
1094 | 1094 | ||
1095 | /* Scan module name(if there), function name and offset */ | 1095 | /* Scan module name(if there), function name and offset */ |
1096 | p = strchr(argv[1], ':'); | 1096 | p = strchr(argv[1], ':'); |
1097 | if (p) { | 1097 | if (p) { |
1098 | tp->module = strndup(argv[1], p - argv[1]); | 1098 | tp->module = strndup(argv[1], p - argv[1]); |
1099 | p++; | 1099 | p++; |
1100 | } else | 1100 | } else |
1101 | p = argv[1]; | 1101 | p = argv[1]; |
1102 | ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol, | 1102 | ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol, |
1103 | &tp->offset); | 1103 | &tp->offset); |
1104 | if (ret == 1) | 1104 | if (ret == 1) |
1105 | tp->offset = 0; | 1105 | tp->offset = 0; |
1106 | 1106 | ||
1107 | tev->nargs = argc - 2; | 1107 | tev->nargs = argc - 2; |
1108 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | 1108 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
1109 | if (tev->args == NULL) { | 1109 | if (tev->args == NULL) { |
1110 | ret = -ENOMEM; | 1110 | ret = -ENOMEM; |
1111 | goto out; | 1111 | goto out; |
1112 | } | 1112 | } |
1113 | for (i = 0; i < tev->nargs; i++) { | 1113 | for (i = 0; i < tev->nargs; i++) { |
1114 | p = strchr(argv[i + 2], '='); | 1114 | p = strchr(argv[i + 2], '='); |
1115 | if (p) /* We don't need which register is assigned. */ | 1115 | if (p) /* We don't need which register is assigned. */ |
1116 | *p++ = '\0'; | 1116 | *p++ = '\0'; |
1117 | else | 1117 | else |
1118 | p = argv[i + 2]; | 1118 | p = argv[i + 2]; |
1119 | tev->args[i].name = strdup(argv[i + 2]); | 1119 | tev->args[i].name = strdup(argv[i + 2]); |
1120 | /* TODO: parse regs and offset */ | 1120 | /* TODO: parse regs and offset */ |
1121 | tev->args[i].value = strdup(p); | 1121 | tev->args[i].value = strdup(p); |
1122 | if (tev->args[i].name == NULL || tev->args[i].value == NULL) { | 1122 | if (tev->args[i].name == NULL || tev->args[i].value == NULL) { |
1123 | ret = -ENOMEM; | 1123 | ret = -ENOMEM; |
1124 | goto out; | 1124 | goto out; |
1125 | } | 1125 | } |
1126 | } | 1126 | } |
1127 | ret = 0; | 1127 | ret = 0; |
1128 | out: | 1128 | out: |
1129 | argv_free(argv); | 1129 | argv_free(argv); |
1130 | return ret; | 1130 | return ret; |
1131 | } | 1131 | } |
1132 | 1132 | ||
1133 | /* Compose only probe arg */ | 1133 | /* Compose only probe arg */ |
1134 | int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | 1134 | int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) |
1135 | { | 1135 | { |
1136 | struct perf_probe_arg_field *field = pa->field; | 1136 | struct perf_probe_arg_field *field = pa->field; |
1137 | int ret; | 1137 | int ret; |
1138 | char *tmp = buf; | 1138 | char *tmp = buf; |
1139 | 1139 | ||
1140 | if (pa->name && pa->var) | 1140 | if (pa->name && pa->var) |
1141 | ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var); | 1141 | ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var); |
1142 | else | 1142 | else |
1143 | ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var); | 1143 | ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var); |
1144 | if (ret <= 0) | 1144 | if (ret <= 0) |
1145 | goto error; | 1145 | goto error; |
1146 | tmp += ret; | 1146 | tmp += ret; |
1147 | len -= ret; | 1147 | len -= ret; |
1148 | 1148 | ||
1149 | while (field) { | 1149 | while (field) { |
1150 | if (field->name[0] == '[') | 1150 | if (field->name[0] == '[') |
1151 | ret = e_snprintf(tmp, len, "%s", field->name); | 1151 | ret = e_snprintf(tmp, len, "%s", field->name); |
1152 | else | 1152 | else |
1153 | ret = e_snprintf(tmp, len, "%s%s", | 1153 | ret = e_snprintf(tmp, len, "%s%s", |
1154 | field->ref ? "->" : ".", field->name); | 1154 | field->ref ? "->" : ".", field->name); |
1155 | if (ret <= 0) | 1155 | if (ret <= 0) |
1156 | goto error; | 1156 | goto error; |
1157 | tmp += ret; | 1157 | tmp += ret; |
1158 | len -= ret; | 1158 | len -= ret; |
1159 | field = field->next; | 1159 | field = field->next; |
1160 | } | 1160 | } |
1161 | 1161 | ||
1162 | if (pa->type) { | 1162 | if (pa->type) { |
1163 | ret = e_snprintf(tmp, len, ":%s", pa->type); | 1163 | ret = e_snprintf(tmp, len, ":%s", pa->type); |
1164 | if (ret <= 0) | 1164 | if (ret <= 0) |
1165 | goto error; | 1165 | goto error; |
1166 | tmp += ret; | 1166 | tmp += ret; |
1167 | len -= ret; | 1167 | len -= ret; |
1168 | } | 1168 | } |
1169 | 1169 | ||
1170 | return tmp - buf; | 1170 | return tmp - buf; |
1171 | error: | 1171 | error: |
1172 | pr_debug("Failed to synthesize perf probe argument: %s\n", | 1172 | pr_debug("Failed to synthesize perf probe argument: %s\n", |
1173 | strerror(-ret)); | 1173 | strerror(-ret)); |
1174 | return ret; | 1174 | return ret; |
1175 | } | 1175 | } |
1176 | 1176 | ||
1177 | /* Compose only probe point (not argument) */ | 1177 | /* Compose only probe point (not argument) */ |
1178 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | 1178 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp) |
1179 | { | 1179 | { |
1180 | char *buf, *tmp; | 1180 | char *buf, *tmp; |
1181 | char offs[32] = "", line[32] = "", file[32] = ""; | 1181 | char offs[32] = "", line[32] = "", file[32] = ""; |
1182 | int ret, len; | 1182 | int ret, len; |
1183 | 1183 | ||
1184 | buf = zalloc(MAX_CMDLEN); | 1184 | buf = zalloc(MAX_CMDLEN); |
1185 | if (buf == NULL) { | 1185 | if (buf == NULL) { |
1186 | ret = -ENOMEM; | 1186 | ret = -ENOMEM; |
1187 | goto error; | 1187 | goto error; |
1188 | } | 1188 | } |
1189 | if (pp->offset) { | 1189 | if (pp->offset) { |
1190 | ret = e_snprintf(offs, 32, "+%lu", pp->offset); | 1190 | ret = e_snprintf(offs, 32, "+%lu", pp->offset); |
1191 | if (ret <= 0) | 1191 | if (ret <= 0) |
1192 | goto error; | 1192 | goto error; |
1193 | } | 1193 | } |
1194 | if (pp->line) { | 1194 | if (pp->line) { |
1195 | ret = e_snprintf(line, 32, ":%d", pp->line); | 1195 | ret = e_snprintf(line, 32, ":%d", pp->line); |
1196 | if (ret <= 0) | 1196 | if (ret <= 0) |
1197 | goto error; | 1197 | goto error; |
1198 | } | 1198 | } |
1199 | if (pp->file) { | 1199 | if (pp->file) { |
1200 | tmp = pp->file; | 1200 | tmp = pp->file; |
1201 | len = strlen(tmp); | 1201 | len = strlen(tmp); |
1202 | if (len > 30) { | 1202 | if (len > 30) { |
1203 | tmp = strchr(pp->file + len - 30, '/'); | 1203 | tmp = strchr(pp->file + len - 30, '/'); |
1204 | tmp = tmp ? tmp + 1 : pp->file + len - 30; | 1204 | tmp = tmp ? tmp + 1 : pp->file + len - 30; |
1205 | } | 1205 | } |
1206 | ret = e_snprintf(file, 32, "@%s", tmp); | 1206 | ret = e_snprintf(file, 32, "@%s", tmp); |
1207 | if (ret <= 0) | 1207 | if (ret <= 0) |
1208 | goto error; | 1208 | goto error; |
1209 | } | 1209 | } |
1210 | 1210 | ||
1211 | if (pp->function) | 1211 | if (pp->function) |
1212 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, | 1212 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, |
1213 | offs, pp->retprobe ? "%return" : "", line, | 1213 | offs, pp->retprobe ? "%return" : "", line, |
1214 | file); | 1214 | file); |
1215 | else | 1215 | else |
1216 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); | 1216 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); |
1217 | if (ret <= 0) | 1217 | if (ret <= 0) |
1218 | goto error; | 1218 | goto error; |
1219 | 1219 | ||
1220 | return buf; | 1220 | return buf; |
1221 | error: | 1221 | error: |
1222 | pr_debug("Failed to synthesize perf probe point: %s\n", | 1222 | pr_debug("Failed to synthesize perf probe point: %s\n", |
1223 | strerror(-ret)); | 1223 | strerror(-ret)); |
1224 | if (buf) | 1224 | if (buf) |
1225 | free(buf); | 1225 | free(buf); |
1226 | return NULL; | 1226 | return NULL; |
1227 | } | 1227 | } |
1228 | 1228 | ||
1229 | #if 0 | 1229 | #if 0 |
1230 | char *synthesize_perf_probe_command(struct perf_probe_event *pev) | 1230 | char *synthesize_perf_probe_command(struct perf_probe_event *pev) |
1231 | { | 1231 | { |
1232 | char *buf; | 1232 | char *buf; |
1233 | int i, len, ret; | 1233 | int i, len, ret; |
1234 | 1234 | ||
1235 | buf = synthesize_perf_probe_point(&pev->point); | 1235 | buf = synthesize_perf_probe_point(&pev->point); |
1236 | if (!buf) | 1236 | if (!buf) |
1237 | return NULL; | 1237 | return NULL; |
1238 | 1238 | ||
1239 | len = strlen(buf); | 1239 | len = strlen(buf); |
1240 | for (i = 0; i < pev->nargs; i++) { | 1240 | for (i = 0; i < pev->nargs; i++) { |
1241 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | 1241 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", |
1242 | pev->args[i].name); | 1242 | pev->args[i].name); |
1243 | if (ret <= 0) { | 1243 | if (ret <= 0) { |
1244 | free(buf); | 1244 | free(buf); |
1245 | return NULL; | 1245 | return NULL; |
1246 | } | 1246 | } |
1247 | len += ret; | 1247 | len += ret; |
1248 | } | 1248 | } |
1249 | 1249 | ||
1250 | return buf; | 1250 | return buf; |
1251 | } | 1251 | } |
1252 | #endif | 1252 | #endif |
1253 | 1253 | ||
1254 | static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref, | 1254 | static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref, |
1255 | char **buf, size_t *buflen, | 1255 | char **buf, size_t *buflen, |
1256 | int depth) | 1256 | int depth) |
1257 | { | 1257 | { |
1258 | int ret; | 1258 | int ret; |
1259 | if (ref->next) { | 1259 | if (ref->next) { |
1260 | depth = __synthesize_probe_trace_arg_ref(ref->next, buf, | 1260 | depth = __synthesize_probe_trace_arg_ref(ref->next, buf, |
1261 | buflen, depth + 1); | 1261 | buflen, depth + 1); |
1262 | if (depth < 0) | 1262 | if (depth < 0) |
1263 | goto out; | 1263 | goto out; |
1264 | } | 1264 | } |
1265 | 1265 | ||
1266 | ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); | 1266 | ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); |
1267 | if (ret < 0) | 1267 | if (ret < 0) |
1268 | depth = ret; | 1268 | depth = ret; |
1269 | else { | 1269 | else { |
1270 | *buf += ret; | 1270 | *buf += ret; |
1271 | *buflen -= ret; | 1271 | *buflen -= ret; |
1272 | } | 1272 | } |
1273 | out: | 1273 | out: |
1274 | return depth; | 1274 | return depth; |
1275 | 1275 | ||
1276 | } | 1276 | } |
1277 | 1277 | ||
1278 | static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, | 1278 | static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, |
1279 | char *buf, size_t buflen) | 1279 | char *buf, size_t buflen) |
1280 | { | 1280 | { |
1281 | struct probe_trace_arg_ref *ref = arg->ref; | 1281 | struct probe_trace_arg_ref *ref = arg->ref; |
1282 | int ret, depth = 0; | 1282 | int ret, depth = 0; |
1283 | char *tmp = buf; | 1283 | char *tmp = buf; |
1284 | 1284 | ||
1285 | /* Argument name or separator */ | 1285 | /* Argument name or separator */ |
1286 | if (arg->name) | 1286 | if (arg->name) |
1287 | ret = e_snprintf(buf, buflen, " %s=", arg->name); | 1287 | ret = e_snprintf(buf, buflen, " %s=", arg->name); |
1288 | else | 1288 | else |
1289 | ret = e_snprintf(buf, buflen, " "); | 1289 | ret = e_snprintf(buf, buflen, " "); |
1290 | if (ret < 0) | 1290 | if (ret < 0) |
1291 | return ret; | 1291 | return ret; |
1292 | buf += ret; | 1292 | buf += ret; |
1293 | buflen -= ret; | 1293 | buflen -= ret; |
1294 | 1294 | ||
1295 | /* Special case: @XXX */ | 1295 | /* Special case: @XXX */ |
1296 | if (arg->value[0] == '@' && arg->ref) | 1296 | if (arg->value[0] == '@' && arg->ref) |
1297 | ref = ref->next; | 1297 | ref = ref->next; |
1298 | 1298 | ||
1299 | /* Dereferencing arguments */ | 1299 | /* Dereferencing arguments */ |
1300 | if (ref) { | 1300 | if (ref) { |
1301 | depth = __synthesize_probe_trace_arg_ref(ref, &buf, | 1301 | depth = __synthesize_probe_trace_arg_ref(ref, &buf, |
1302 | &buflen, 1); | 1302 | &buflen, 1); |
1303 | if (depth < 0) | 1303 | if (depth < 0) |
1304 | return depth; | 1304 | return depth; |
1305 | } | 1305 | } |
1306 | 1306 | ||
1307 | /* Print argument value */ | 1307 | /* Print argument value */ |
1308 | if (arg->value[0] == '@' && arg->ref) | 1308 | if (arg->value[0] == '@' && arg->ref) |
1309 | ret = e_snprintf(buf, buflen, "%s%+ld", arg->value, | 1309 | ret = e_snprintf(buf, buflen, "%s%+ld", arg->value, |
1310 | arg->ref->offset); | 1310 | arg->ref->offset); |
1311 | else | 1311 | else |
1312 | ret = e_snprintf(buf, buflen, "%s", arg->value); | 1312 | ret = e_snprintf(buf, buflen, "%s", arg->value); |
1313 | if (ret < 0) | 1313 | if (ret < 0) |
1314 | return ret; | 1314 | return ret; |
1315 | buf += ret; | 1315 | buf += ret; |
1316 | buflen -= ret; | 1316 | buflen -= ret; |
1317 | 1317 | ||
1318 | /* Closing */ | 1318 | /* Closing */ |
1319 | while (depth--) { | 1319 | while (depth--) { |
1320 | ret = e_snprintf(buf, buflen, ")"); | 1320 | ret = e_snprintf(buf, buflen, ")"); |
1321 | if (ret < 0) | 1321 | if (ret < 0) |
1322 | return ret; | 1322 | return ret; |
1323 | buf += ret; | 1323 | buf += ret; |
1324 | buflen -= ret; | 1324 | buflen -= ret; |
1325 | } | 1325 | } |
1326 | /* Print argument type */ | 1326 | /* Print argument type */ |
1327 | if (arg->type) { | 1327 | if (arg->type) { |
1328 | ret = e_snprintf(buf, buflen, ":%s", arg->type); | 1328 | ret = e_snprintf(buf, buflen, ":%s", arg->type); |
1329 | if (ret <= 0) | 1329 | if (ret <= 0) |
1330 | return ret; | 1330 | return ret; |
1331 | buf += ret; | 1331 | buf += ret; |
1332 | } | 1332 | } |
1333 | 1333 | ||
1334 | return buf - tmp; | 1334 | return buf - tmp; |
1335 | } | 1335 | } |
1336 | 1336 | ||
1337 | char *synthesize_probe_trace_command(struct probe_trace_event *tev) | 1337 | char *synthesize_probe_trace_command(struct probe_trace_event *tev) |
1338 | { | 1338 | { |
1339 | struct probe_trace_point *tp = &tev->point; | 1339 | struct probe_trace_point *tp = &tev->point; |
1340 | char *buf; | 1340 | char *buf; |
1341 | int i, len, ret; | 1341 | int i, len, ret; |
1342 | 1342 | ||
1343 | buf = zalloc(MAX_CMDLEN); | 1343 | buf = zalloc(MAX_CMDLEN); |
1344 | if (buf == NULL) | 1344 | if (buf == NULL) |
1345 | return NULL; | 1345 | return NULL; |
1346 | 1346 | ||
1347 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", | 1347 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", |
1348 | tp->retprobe ? 'r' : 'p', | 1348 | tp->retprobe ? 'r' : 'p', |
1349 | tev->group, tev->event, | 1349 | tev->group, tev->event, |
1350 | tp->module ?: "", tp->module ? ":" : "", | 1350 | tp->module ?: "", tp->module ? ":" : "", |
1351 | tp->symbol, tp->offset); | 1351 | tp->symbol, tp->offset); |
1352 | if (len <= 0) | 1352 | if (len <= 0) |
1353 | goto error; | 1353 | goto error; |
1354 | 1354 | ||
1355 | for (i = 0; i < tev->nargs; i++) { | 1355 | for (i = 0; i < tev->nargs; i++) { |
1356 | ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, | 1356 | ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, |
1357 | MAX_CMDLEN - len); | 1357 | MAX_CMDLEN - len); |
1358 | if (ret <= 0) | 1358 | if (ret <= 0) |
1359 | goto error; | 1359 | goto error; |
1360 | len += ret; | 1360 | len += ret; |
1361 | } | 1361 | } |
1362 | 1362 | ||
1363 | return buf; | 1363 | return buf; |
1364 | error: | 1364 | error: |
1365 | free(buf); | 1365 | free(buf); |
1366 | return NULL; | 1366 | return NULL; |
1367 | } | 1367 | } |
1368 | 1368 | ||
1369 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, | 1369 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, |
1370 | struct perf_probe_event *pev) | 1370 | struct perf_probe_event *pev) |
1371 | { | 1371 | { |
1372 | char buf[64] = ""; | 1372 | char buf[64] = ""; |
1373 | int i, ret; | 1373 | int i, ret; |
1374 | 1374 | ||
1375 | /* Convert event/group name */ | 1375 | /* Convert event/group name */ |
1376 | pev->event = strdup(tev->event); | 1376 | pev->event = strdup(tev->event); |
1377 | pev->group = strdup(tev->group); | 1377 | pev->group = strdup(tev->group); |
1378 | if (pev->event == NULL || pev->group == NULL) | 1378 | if (pev->event == NULL || pev->group == NULL) |
1379 | return -ENOMEM; | 1379 | return -ENOMEM; |
1380 | 1380 | ||
1381 | /* Convert trace_point to probe_point */ | 1381 | /* Convert trace_point to probe_point */ |
1382 | ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); | 1382 | ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); |
1383 | if (ret < 0) | 1383 | if (ret < 0) |
1384 | return ret; | 1384 | return ret; |
1385 | 1385 | ||
1386 | /* Convert trace_arg to probe_arg */ | 1386 | /* Convert trace_arg to probe_arg */ |
1387 | pev->nargs = tev->nargs; | 1387 | pev->nargs = tev->nargs; |
1388 | pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); | 1388 | pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); |
1389 | if (pev->args == NULL) | 1389 | if (pev->args == NULL) |
1390 | return -ENOMEM; | 1390 | return -ENOMEM; |
1391 | for (i = 0; i < tev->nargs && ret >= 0; i++) { | 1391 | for (i = 0; i < tev->nargs && ret >= 0; i++) { |
1392 | if (tev->args[i].name) | 1392 | if (tev->args[i].name) |
1393 | pev->args[i].name = strdup(tev->args[i].name); | 1393 | pev->args[i].name = strdup(tev->args[i].name); |
1394 | else { | 1394 | else { |
1395 | ret = synthesize_probe_trace_arg(&tev->args[i], | 1395 | ret = synthesize_probe_trace_arg(&tev->args[i], |
1396 | buf, 64); | 1396 | buf, 64); |
1397 | pev->args[i].name = strdup(buf); | 1397 | pev->args[i].name = strdup(buf); |
1398 | } | 1398 | } |
1399 | if (pev->args[i].name == NULL && ret >= 0) | 1399 | if (pev->args[i].name == NULL && ret >= 0) |
1400 | ret = -ENOMEM; | 1400 | ret = -ENOMEM; |
1401 | } | 1401 | } |
1402 | 1402 | ||
1403 | if (ret < 0) | 1403 | if (ret < 0) |
1404 | clear_perf_probe_event(pev); | 1404 | clear_perf_probe_event(pev); |
1405 | 1405 | ||
1406 | return ret; | 1406 | return ret; |
1407 | } | 1407 | } |
1408 | 1408 | ||
1409 | void clear_perf_probe_event(struct perf_probe_event *pev) | 1409 | void clear_perf_probe_event(struct perf_probe_event *pev) |
1410 | { | 1410 | { |
1411 | struct perf_probe_point *pp = &pev->point; | 1411 | struct perf_probe_point *pp = &pev->point; |
1412 | struct perf_probe_arg_field *field, *next; | 1412 | struct perf_probe_arg_field *field, *next; |
1413 | int i; | 1413 | int i; |
1414 | 1414 | ||
1415 | if (pev->event) | 1415 | if (pev->event) |
1416 | free(pev->event); | 1416 | free(pev->event); |
1417 | if (pev->group) | 1417 | if (pev->group) |
1418 | free(pev->group); | 1418 | free(pev->group); |
1419 | if (pp->file) | 1419 | if (pp->file) |
1420 | free(pp->file); | 1420 | free(pp->file); |
1421 | if (pp->function) | 1421 | if (pp->function) |
1422 | free(pp->function); | 1422 | free(pp->function); |
1423 | if (pp->lazy_line) | 1423 | if (pp->lazy_line) |
1424 | free(pp->lazy_line); | 1424 | free(pp->lazy_line); |
1425 | for (i = 0; i < pev->nargs; i++) { | 1425 | for (i = 0; i < pev->nargs; i++) { |
1426 | if (pev->args[i].name) | 1426 | if (pev->args[i].name) |
1427 | free(pev->args[i].name); | 1427 | free(pev->args[i].name); |
1428 | if (pev->args[i].var) | 1428 | if (pev->args[i].var) |
1429 | free(pev->args[i].var); | 1429 | free(pev->args[i].var); |
1430 | if (pev->args[i].type) | 1430 | if (pev->args[i].type) |
1431 | free(pev->args[i].type); | 1431 | free(pev->args[i].type); |
1432 | field = pev->args[i].field; | 1432 | field = pev->args[i].field; |
1433 | while (field) { | 1433 | while (field) { |
1434 | next = field->next; | 1434 | next = field->next; |
1435 | if (field->name) | 1435 | if (field->name) |
1436 | free(field->name); | 1436 | free(field->name); |
1437 | free(field); | 1437 | free(field); |
1438 | field = next; | 1438 | field = next; |
1439 | } | 1439 | } |
1440 | } | 1440 | } |
1441 | if (pev->args) | 1441 | if (pev->args) |
1442 | free(pev->args); | 1442 | free(pev->args); |
1443 | memset(pev, 0, sizeof(*pev)); | 1443 | memset(pev, 0, sizeof(*pev)); |
1444 | } | 1444 | } |
1445 | 1445 | ||
1446 | static void clear_probe_trace_event(struct probe_trace_event *tev) | 1446 | static void clear_probe_trace_event(struct probe_trace_event *tev) |
1447 | { | 1447 | { |
1448 | struct probe_trace_arg_ref *ref, *next; | 1448 | struct probe_trace_arg_ref *ref, *next; |
1449 | int i; | 1449 | int i; |
1450 | 1450 | ||
1451 | if (tev->event) | 1451 | if (tev->event) |
1452 | free(tev->event); | 1452 | free(tev->event); |
1453 | if (tev->group) | 1453 | if (tev->group) |
1454 | free(tev->group); | 1454 | free(tev->group); |
1455 | if (tev->point.symbol) | 1455 | if (tev->point.symbol) |
1456 | free(tev->point.symbol); | 1456 | free(tev->point.symbol); |
1457 | if (tev->point.module) | 1457 | if (tev->point.module) |
1458 | free(tev->point.module); | 1458 | free(tev->point.module); |
1459 | for (i = 0; i < tev->nargs; i++) { | 1459 | for (i = 0; i < tev->nargs; i++) { |
1460 | if (tev->args[i].name) | 1460 | if (tev->args[i].name) |
1461 | free(tev->args[i].name); | 1461 | free(tev->args[i].name); |
1462 | if (tev->args[i].value) | 1462 | if (tev->args[i].value) |
1463 | free(tev->args[i].value); | 1463 | free(tev->args[i].value); |
1464 | if (tev->args[i].type) | 1464 | if (tev->args[i].type) |
1465 | free(tev->args[i].type); | 1465 | free(tev->args[i].type); |
1466 | ref = tev->args[i].ref; | 1466 | ref = tev->args[i].ref; |
1467 | while (ref) { | 1467 | while (ref) { |
1468 | next = ref->next; | 1468 | next = ref->next; |
1469 | free(ref); | 1469 | free(ref); |
1470 | ref = next; | 1470 | ref = next; |
1471 | } | 1471 | } |
1472 | } | 1472 | } |
1473 | if (tev->args) | 1473 | if (tev->args) |
1474 | free(tev->args); | 1474 | free(tev->args); |
1475 | memset(tev, 0, sizeof(*tev)); | 1475 | memset(tev, 0, sizeof(*tev)); |
1476 | } | 1476 | } |
1477 | 1477 | ||
1478 | static int open_kprobe_events(bool readwrite) | 1478 | static int open_kprobe_events(bool readwrite) |
1479 | { | 1479 | { |
1480 | char buf[PATH_MAX]; | 1480 | char buf[PATH_MAX]; |
1481 | const char *__debugfs; | 1481 | const char *__debugfs; |
1482 | int ret; | 1482 | int ret; |
1483 | 1483 | ||
1484 | __debugfs = debugfs_find_mountpoint(); | 1484 | __debugfs = debugfs_find_mountpoint(); |
1485 | if (__debugfs == NULL) { | 1485 | if (__debugfs == NULL) { |
1486 | pr_warning("Debugfs is not mounted.\n"); | 1486 | pr_warning("Debugfs is not mounted.\n"); |
1487 | return -ENOENT; | 1487 | return -ENOENT; |
1488 | } | 1488 | } |
1489 | 1489 | ||
1490 | ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs); | 1490 | ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs); |
1491 | if (ret >= 0) { | 1491 | if (ret >= 0) { |
1492 | pr_debug("Opening %s write=%d\n", buf, readwrite); | 1492 | pr_debug("Opening %s write=%d\n", buf, readwrite); |
1493 | if (readwrite && !probe_event_dry_run) | 1493 | if (readwrite && !probe_event_dry_run) |
1494 | ret = open(buf, O_RDWR, O_APPEND); | 1494 | ret = open(buf, O_RDWR, O_APPEND); |
1495 | else | 1495 | else |
1496 | ret = open(buf, O_RDONLY, 0); | 1496 | ret = open(buf, O_RDONLY, 0); |
1497 | } | 1497 | } |
1498 | 1498 | ||
1499 | if (ret < 0) { | 1499 | if (ret < 0) { |
1500 | if (errno == ENOENT) | 1500 | if (errno == ENOENT) |
1501 | pr_warning("kprobe_events file does not exist - please" | 1501 | pr_warning("kprobe_events file does not exist - please" |
1502 | " rebuild kernel with CONFIG_KPROBE_EVENT.\n"); | 1502 | " rebuild kernel with CONFIG_KPROBE_EVENT.\n"); |
1503 | else | 1503 | else |
1504 | pr_warning("Failed to open kprobe_events file: %s\n", | 1504 | pr_warning("Failed to open kprobe_events file: %s\n", |
1505 | strerror(errno)); | 1505 | strerror(errno)); |
1506 | } | 1506 | } |
1507 | return ret; | 1507 | return ret; |
1508 | } | 1508 | } |
1509 | 1509 | ||
1510 | /* Get raw string list of current kprobe_events */ | 1510 | /* Get raw string list of current kprobe_events */ |
1511 | static struct strlist *get_probe_trace_command_rawlist(int fd) | 1511 | static struct strlist *get_probe_trace_command_rawlist(int fd) |
1512 | { | 1512 | { |
1513 | int ret, idx; | 1513 | int ret, idx; |
1514 | FILE *fp; | 1514 | FILE *fp; |
1515 | char buf[MAX_CMDLEN]; | 1515 | char buf[MAX_CMDLEN]; |
1516 | char *p; | 1516 | char *p; |
1517 | struct strlist *sl; | 1517 | struct strlist *sl; |
1518 | 1518 | ||
1519 | sl = strlist__new(true, NULL); | 1519 | sl = strlist__new(true, NULL); |
1520 | 1520 | ||
1521 | fp = fdopen(dup(fd), "r"); | 1521 | fp = fdopen(dup(fd), "r"); |
1522 | while (!feof(fp)) { | 1522 | while (!feof(fp)) { |
1523 | p = fgets(buf, MAX_CMDLEN, fp); | 1523 | p = fgets(buf, MAX_CMDLEN, fp); |
1524 | if (!p) | 1524 | if (!p) |
1525 | break; | 1525 | break; |
1526 | 1526 | ||
1527 | idx = strlen(p) - 1; | 1527 | idx = strlen(p) - 1; |
1528 | if (p[idx] == '\n') | 1528 | if (p[idx] == '\n') |
1529 | p[idx] = '\0'; | 1529 | p[idx] = '\0'; |
1530 | ret = strlist__add(sl, buf); | 1530 | ret = strlist__add(sl, buf); |
1531 | if (ret < 0) { | 1531 | if (ret < 0) { |
1532 | pr_debug("strlist__add failed: %s\n", strerror(-ret)); | 1532 | pr_debug("strlist__add failed: %s\n", strerror(-ret)); |
1533 | strlist__delete(sl); | 1533 | strlist__delete(sl); |
1534 | return NULL; | 1534 | return NULL; |
1535 | } | 1535 | } |
1536 | } | 1536 | } |
1537 | fclose(fp); | 1537 | fclose(fp); |
1538 | 1538 | ||
1539 | return sl; | 1539 | return sl; |
1540 | } | 1540 | } |
1541 | 1541 | ||
1542 | /* Show an event */ | 1542 | /* Show an event */ |
1543 | static int show_perf_probe_event(struct perf_probe_event *pev) | 1543 | static int show_perf_probe_event(struct perf_probe_event *pev) |
1544 | { | 1544 | { |
1545 | int i, ret; | 1545 | int i, ret; |
1546 | char buf[128]; | 1546 | char buf[128]; |
1547 | char *place; | 1547 | char *place; |
1548 | 1548 | ||
1549 | /* Synthesize only event probe point */ | 1549 | /* Synthesize only event probe point */ |
1550 | place = synthesize_perf_probe_point(&pev->point); | 1550 | place = synthesize_perf_probe_point(&pev->point); |
1551 | if (!place) | 1551 | if (!place) |
1552 | return -EINVAL; | 1552 | return -EINVAL; |
1553 | 1553 | ||
1554 | ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); | 1554 | ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); |
1555 | if (ret < 0) | 1555 | if (ret < 0) |
1556 | return ret; | 1556 | return ret; |
1557 | 1557 | ||
1558 | printf(" %-20s (on %s", buf, place); | 1558 | printf(" %-20s (on %s", buf, place); |
1559 | 1559 | ||
1560 | if (pev->nargs > 0) { | 1560 | if (pev->nargs > 0) { |
1561 | printf(" with"); | 1561 | printf(" with"); |
1562 | for (i = 0; i < pev->nargs; i++) { | 1562 | for (i = 0; i < pev->nargs; i++) { |
1563 | ret = synthesize_perf_probe_arg(&pev->args[i], | 1563 | ret = synthesize_perf_probe_arg(&pev->args[i], |
1564 | buf, 128); | 1564 | buf, 128); |
1565 | if (ret < 0) | 1565 | if (ret < 0) |
1566 | break; | 1566 | break; |
1567 | printf(" %s", buf); | 1567 | printf(" %s", buf); |
1568 | } | 1568 | } |
1569 | } | 1569 | } |
1570 | printf(")\n"); | 1570 | printf(")\n"); |
1571 | free(place); | 1571 | free(place); |
1572 | return ret; | 1572 | return ret; |
1573 | } | 1573 | } |
1574 | 1574 | ||
1575 | /* List up current perf-probe events */ | 1575 | /* List up current perf-probe events */ |
1576 | int show_perf_probe_events(void) | 1576 | int show_perf_probe_events(void) |
1577 | { | 1577 | { |
1578 | int fd, ret; | 1578 | int fd, ret; |
1579 | struct probe_trace_event tev; | 1579 | struct probe_trace_event tev; |
1580 | struct perf_probe_event pev; | 1580 | struct perf_probe_event pev; |
1581 | struct strlist *rawlist; | 1581 | struct strlist *rawlist; |
1582 | struct str_node *ent; | 1582 | struct str_node *ent; |
1583 | 1583 | ||
1584 | setup_pager(); | 1584 | setup_pager(); |
1585 | ret = init_vmlinux(); | 1585 | ret = init_vmlinux(); |
1586 | if (ret < 0) | 1586 | if (ret < 0) |
1587 | return ret; | 1587 | return ret; |
1588 | 1588 | ||
1589 | memset(&tev, 0, sizeof(tev)); | 1589 | memset(&tev, 0, sizeof(tev)); |
1590 | memset(&pev, 0, sizeof(pev)); | 1590 | memset(&pev, 0, sizeof(pev)); |
1591 | 1591 | ||
1592 | fd = open_kprobe_events(false); | 1592 | fd = open_kprobe_events(false); |
1593 | if (fd < 0) | 1593 | if (fd < 0) |
1594 | return fd; | 1594 | return fd; |
1595 | 1595 | ||
1596 | rawlist = get_probe_trace_command_rawlist(fd); | 1596 | rawlist = get_probe_trace_command_rawlist(fd); |
1597 | close(fd); | 1597 | close(fd); |
1598 | if (!rawlist) | 1598 | if (!rawlist) |
1599 | return -ENOENT; | 1599 | return -ENOENT; |
1600 | 1600 | ||
1601 | strlist__for_each(ent, rawlist) { | 1601 | strlist__for_each(ent, rawlist) { |
1602 | ret = parse_probe_trace_command(ent->s, &tev); | 1602 | ret = parse_probe_trace_command(ent->s, &tev); |
1603 | if (ret >= 0) { | 1603 | if (ret >= 0) { |
1604 | ret = convert_to_perf_probe_event(&tev, &pev); | 1604 | ret = convert_to_perf_probe_event(&tev, &pev); |
1605 | if (ret >= 0) | 1605 | if (ret >= 0) |
1606 | ret = show_perf_probe_event(&pev); | 1606 | ret = show_perf_probe_event(&pev); |
1607 | } | 1607 | } |
1608 | clear_perf_probe_event(&pev); | 1608 | clear_perf_probe_event(&pev); |
1609 | clear_probe_trace_event(&tev); | 1609 | clear_probe_trace_event(&tev); |
1610 | if (ret < 0) | 1610 | if (ret < 0) |
1611 | break; | 1611 | break; |
1612 | } | 1612 | } |
1613 | strlist__delete(rawlist); | 1613 | strlist__delete(rawlist); |
1614 | 1614 | ||
1615 | return ret; | 1615 | return ret; |
1616 | } | 1616 | } |
1617 | 1617 | ||
1618 | /* Get current perf-probe event names */ | 1618 | /* Get current perf-probe event names */ |
1619 | static struct strlist *get_probe_trace_event_names(int fd, bool include_group) | 1619 | static struct strlist *get_probe_trace_event_names(int fd, bool include_group) |
1620 | { | 1620 | { |
1621 | char buf[128]; | 1621 | char buf[128]; |
1622 | struct strlist *sl, *rawlist; | 1622 | struct strlist *sl, *rawlist; |
1623 | struct str_node *ent; | 1623 | struct str_node *ent; |
1624 | struct probe_trace_event tev; | 1624 | struct probe_trace_event tev; |
1625 | int ret = 0; | 1625 | int ret = 0; |
1626 | 1626 | ||
1627 | memset(&tev, 0, sizeof(tev)); | 1627 | memset(&tev, 0, sizeof(tev)); |
1628 | rawlist = get_probe_trace_command_rawlist(fd); | 1628 | rawlist = get_probe_trace_command_rawlist(fd); |
1629 | sl = strlist__new(true, NULL); | 1629 | sl = strlist__new(true, NULL); |
1630 | strlist__for_each(ent, rawlist) { | 1630 | strlist__for_each(ent, rawlist) { |
1631 | ret = parse_probe_trace_command(ent->s, &tev); | 1631 | ret = parse_probe_trace_command(ent->s, &tev); |
1632 | if (ret < 0) | 1632 | if (ret < 0) |
1633 | break; | 1633 | break; |
1634 | if (include_group) { | 1634 | if (include_group) { |
1635 | ret = e_snprintf(buf, 128, "%s:%s", tev.group, | 1635 | ret = e_snprintf(buf, 128, "%s:%s", tev.group, |
1636 | tev.event); | 1636 | tev.event); |
1637 | if (ret >= 0) | 1637 | if (ret >= 0) |
1638 | ret = strlist__add(sl, buf); | 1638 | ret = strlist__add(sl, buf); |
1639 | } else | 1639 | } else |
1640 | ret = strlist__add(sl, tev.event); | 1640 | ret = strlist__add(sl, tev.event); |
1641 | clear_probe_trace_event(&tev); | 1641 | clear_probe_trace_event(&tev); |
1642 | if (ret < 0) | 1642 | if (ret < 0) |
1643 | break; | 1643 | break; |
1644 | } | 1644 | } |
1645 | strlist__delete(rawlist); | 1645 | strlist__delete(rawlist); |
1646 | 1646 | ||
1647 | if (ret < 0) { | 1647 | if (ret < 0) { |
1648 | strlist__delete(sl); | 1648 | strlist__delete(sl); |
1649 | return NULL; | 1649 | return NULL; |
1650 | } | 1650 | } |
1651 | return sl; | 1651 | return sl; |
1652 | } | 1652 | } |
1653 | 1653 | ||
1654 | static int write_probe_trace_event(int fd, struct probe_trace_event *tev) | 1654 | static int write_probe_trace_event(int fd, struct probe_trace_event *tev) |
1655 | { | 1655 | { |
1656 | int ret = 0; | 1656 | int ret = 0; |
1657 | char *buf = synthesize_probe_trace_command(tev); | 1657 | char *buf = synthesize_probe_trace_command(tev); |
1658 | 1658 | ||
1659 | if (!buf) { | 1659 | if (!buf) { |
1660 | pr_debug("Failed to synthesize probe trace event.\n"); | 1660 | pr_debug("Failed to synthesize probe trace event.\n"); |
1661 | return -EINVAL; | 1661 | return -EINVAL; |
1662 | } | 1662 | } |
1663 | 1663 | ||
1664 | pr_debug("Writing event: %s\n", buf); | 1664 | pr_debug("Writing event: %s\n", buf); |
1665 | if (!probe_event_dry_run) { | 1665 | if (!probe_event_dry_run) { |
1666 | ret = write(fd, buf, strlen(buf)); | 1666 | ret = write(fd, buf, strlen(buf)); |
1667 | if (ret <= 0) | 1667 | if (ret <= 0) |
1668 | pr_warning("Failed to write event: %s\n", | 1668 | pr_warning("Failed to write event: %s\n", |
1669 | strerror(errno)); | 1669 | strerror(errno)); |
1670 | } | 1670 | } |
1671 | free(buf); | 1671 | free(buf); |
1672 | return ret; | 1672 | return ret; |
1673 | } | 1673 | } |
1674 | 1674 | ||
1675 | static int get_new_event_name(char *buf, size_t len, const char *base, | 1675 | static int get_new_event_name(char *buf, size_t len, const char *base, |
1676 | struct strlist *namelist, bool allow_suffix) | 1676 | struct strlist *namelist, bool allow_suffix) |
1677 | { | 1677 | { |
1678 | int i, ret; | 1678 | int i, ret; |
1679 | 1679 | ||
1680 | /* Try no suffix */ | 1680 | /* Try no suffix */ |
1681 | ret = e_snprintf(buf, len, "%s", base); | 1681 | ret = e_snprintf(buf, len, "%s", base); |
1682 | if (ret < 0) { | 1682 | if (ret < 0) { |
1683 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); | 1683 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); |
1684 | return ret; | 1684 | return ret; |
1685 | } | 1685 | } |
1686 | if (!strlist__has_entry(namelist, buf)) | 1686 | if (!strlist__has_entry(namelist, buf)) |
1687 | return 0; | 1687 | return 0; |
1688 | 1688 | ||
1689 | if (!allow_suffix) { | 1689 | if (!allow_suffix) { |
1690 | pr_warning("Error: event \"%s\" already exists. " | 1690 | pr_warning("Error: event \"%s\" already exists. " |
1691 | "(Use -f to force duplicates.)\n", base); | 1691 | "(Use -f to force duplicates.)\n", base); |
1692 | return -EEXIST; | 1692 | return -EEXIST; |
1693 | } | 1693 | } |
1694 | 1694 | ||
1695 | /* Try to add suffix */ | 1695 | /* Try to add suffix */ |
1696 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | 1696 | for (i = 1; i < MAX_EVENT_INDEX; i++) { |
1697 | ret = e_snprintf(buf, len, "%s_%d", base, i); | 1697 | ret = e_snprintf(buf, len, "%s_%d", base, i); |
1698 | if (ret < 0) { | 1698 | if (ret < 0) { |
1699 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); | 1699 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); |
1700 | return ret; | 1700 | return ret; |
1701 | } | 1701 | } |
1702 | if (!strlist__has_entry(namelist, buf)) | 1702 | if (!strlist__has_entry(namelist, buf)) |
1703 | break; | 1703 | break; |
1704 | } | 1704 | } |
1705 | if (i == MAX_EVENT_INDEX) { | 1705 | if (i == MAX_EVENT_INDEX) { |
1706 | pr_warning("Too many events are on the same function.\n"); | 1706 | pr_warning("Too many events are on the same function.\n"); |
1707 | ret = -ERANGE; | 1707 | ret = -ERANGE; |
1708 | } | 1708 | } |
1709 | 1709 | ||
1710 | return ret; | 1710 | return ret; |
1711 | } | 1711 | } |
1712 | 1712 | ||
1713 | static int __add_probe_trace_events(struct perf_probe_event *pev, | 1713 | static int __add_probe_trace_events(struct perf_probe_event *pev, |
1714 | struct probe_trace_event *tevs, | 1714 | struct probe_trace_event *tevs, |
1715 | int ntevs, bool allow_suffix) | 1715 | int ntevs, bool allow_suffix) |
1716 | { | 1716 | { |
1717 | int i, fd, ret; | 1717 | int i, fd, ret; |
1718 | struct probe_trace_event *tev = NULL; | 1718 | struct probe_trace_event *tev = NULL; |
1719 | char buf[64]; | 1719 | char buf[64]; |
1720 | const char *event, *group; | 1720 | const char *event, *group; |
1721 | struct strlist *namelist; | 1721 | struct strlist *namelist; |
1722 | 1722 | ||
1723 | fd = open_kprobe_events(true); | 1723 | fd = open_kprobe_events(true); |
1724 | if (fd < 0) | 1724 | if (fd < 0) |
1725 | return fd; | 1725 | return fd; |
1726 | /* Get current event names */ | 1726 | /* Get current event names */ |
1727 | namelist = get_probe_trace_event_names(fd, false); | 1727 | namelist = get_probe_trace_event_names(fd, false); |
1728 | if (!namelist) { | 1728 | if (!namelist) { |
1729 | pr_debug("Failed to get current event list.\n"); | 1729 | pr_debug("Failed to get current event list.\n"); |
1730 | return -EIO; | 1730 | return -EIO; |
1731 | } | 1731 | } |
1732 | 1732 | ||
1733 | ret = 0; | 1733 | ret = 0; |
1734 | printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); | 1734 | printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); |
1735 | for (i = 0; i < ntevs; i++) { | 1735 | for (i = 0; i < ntevs; i++) { |
1736 | tev = &tevs[i]; | 1736 | tev = &tevs[i]; |
1737 | if (pev->event) | 1737 | if (pev->event) |
1738 | event = pev->event; | 1738 | event = pev->event; |
1739 | else | 1739 | else |
1740 | if (pev->point.function) | 1740 | if (pev->point.function) |
1741 | event = pev->point.function; | 1741 | event = pev->point.function; |
1742 | else | 1742 | else |
1743 | event = tev->point.symbol; | 1743 | event = tev->point.symbol; |
1744 | if (pev->group) | 1744 | if (pev->group) |
1745 | group = pev->group; | 1745 | group = pev->group; |
1746 | else | 1746 | else |
1747 | group = PERFPROBE_GROUP; | 1747 | group = PERFPROBE_GROUP; |
1748 | 1748 | ||
1749 | /* Get an unused new event name */ | 1749 | /* Get an unused new event name */ |
1750 | ret = get_new_event_name(buf, 64, event, | 1750 | ret = get_new_event_name(buf, 64, event, |
1751 | namelist, allow_suffix); | 1751 | namelist, allow_suffix); |
1752 | if (ret < 0) | 1752 | if (ret < 0) |
1753 | break; | 1753 | break; |
1754 | event = buf; | 1754 | event = buf; |
1755 | 1755 | ||
1756 | tev->event = strdup(event); | 1756 | tev->event = strdup(event); |
1757 | tev->group = strdup(group); | 1757 | tev->group = strdup(group); |
1758 | if (tev->event == NULL || tev->group == NULL) { | 1758 | if (tev->event == NULL || tev->group == NULL) { |
1759 | ret = -ENOMEM; | 1759 | ret = -ENOMEM; |
1760 | break; | 1760 | break; |
1761 | } | 1761 | } |
1762 | ret = write_probe_trace_event(fd, tev); | 1762 | ret = write_probe_trace_event(fd, tev); |
1763 | if (ret < 0) | 1763 | if (ret < 0) |
1764 | break; | 1764 | break; |
1765 | /* Add added event name to namelist */ | 1765 | /* Add added event name to namelist */ |
1766 | strlist__add(namelist, event); | 1766 | strlist__add(namelist, event); |
1767 | 1767 | ||
1768 | /* Trick here - save current event/group */ | 1768 | /* Trick here - save current event/group */ |
1769 | event = pev->event; | 1769 | event = pev->event; |
1770 | group = pev->group; | 1770 | group = pev->group; |
1771 | pev->event = tev->event; | 1771 | pev->event = tev->event; |
1772 | pev->group = tev->group; | 1772 | pev->group = tev->group; |
1773 | show_perf_probe_event(pev); | 1773 | show_perf_probe_event(pev); |
1774 | /* Trick here - restore current event/group */ | 1774 | /* Trick here - restore current event/group */ |
1775 | pev->event = (char *)event; | 1775 | pev->event = (char *)event; |
1776 | pev->group = (char *)group; | 1776 | pev->group = (char *)group; |
1777 | 1777 | ||
1778 | /* | 1778 | /* |
1779 | * Probes after the first probe which comes from same | 1779 | * Probes after the first probe which comes from same |
1780 | * user input are always allowed to add suffix, because | 1780 | * user input are always allowed to add suffix, because |
1781 | * there might be several addresses corresponding to | 1781 | * there might be several addresses corresponding to |
1782 | * one code line. | 1782 | * one code line. |
1783 | */ | 1783 | */ |
1784 | allow_suffix = true; | 1784 | allow_suffix = true; |
1785 | } | 1785 | } |
1786 | 1786 | ||
1787 | if (ret >= 0) { | 1787 | if (ret >= 0) { |
1788 | /* Show how to use the event. */ | 1788 | /* Show how to use the event. */ |
1789 | printf("\nYou can now use it on all perf tools, such as:\n\n"); | 1789 | printf("\nYou can now use it on all perf tools, such as:\n\n"); |
1790 | printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, | 1790 | printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, |
1791 | tev->event); | 1791 | tev->event); |
1792 | } | 1792 | } |
1793 | 1793 | ||
1794 | strlist__delete(namelist); | 1794 | strlist__delete(namelist); |
1795 | close(fd); | 1795 | close(fd); |
1796 | return ret; | 1796 | return ret; |
1797 | } | 1797 | } |
1798 | 1798 | ||
1799 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, | 1799 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
1800 | struct probe_trace_event **tevs, | 1800 | struct probe_trace_event **tevs, |
1801 | int max_tevs, const char *module) | 1801 | int max_tevs, const char *module) |
1802 | { | 1802 | { |
1803 | struct symbol *sym; | 1803 | struct symbol *sym; |
1804 | int ret = 0, i; | 1804 | int ret = 0, i; |
1805 | struct probe_trace_event *tev; | 1805 | struct probe_trace_event *tev; |
1806 | 1806 | ||
1807 | /* Convert perf_probe_event with debuginfo */ | 1807 | /* Convert perf_probe_event with debuginfo */ |
1808 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); | 1808 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); |
1809 | if (ret != 0) | 1809 | if (ret != 0) |
1810 | return ret; /* Found in debuginfo or got an error */ | 1810 | return ret; /* Found in debuginfo or got an error */ |
1811 | 1811 | ||
1812 | /* Allocate trace event buffer */ | 1812 | /* Allocate trace event buffer */ |
1813 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); | 1813 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); |
1814 | if (tev == NULL) | 1814 | if (tev == NULL) |
1815 | return -ENOMEM; | 1815 | return -ENOMEM; |
1816 | 1816 | ||
1817 | /* Copy parameters */ | 1817 | /* Copy parameters */ |
1818 | tev->point.symbol = strdup(pev->point.function); | 1818 | tev->point.symbol = strdup(pev->point.function); |
1819 | if (tev->point.symbol == NULL) { | 1819 | if (tev->point.symbol == NULL) { |
1820 | ret = -ENOMEM; | 1820 | ret = -ENOMEM; |
1821 | goto error; | 1821 | goto error; |
1822 | } | 1822 | } |
1823 | tev->point.module = strdup(module); | 1823 | |
1824 | if (tev->point.module == NULL) { | 1824 | if (module) { |
1825 | ret = -ENOMEM; | 1825 | tev->point.module = strdup(module); |
1826 | goto error; | 1826 | if (tev->point.module == NULL) { |
1827 | ret = -ENOMEM; | ||
1828 | goto error; | ||
1829 | } | ||
1827 | } | 1830 | } |
1831 | |||
1828 | tev->point.offset = pev->point.offset; | 1832 | tev->point.offset = pev->point.offset; |
1829 | tev->point.retprobe = pev->point.retprobe; | 1833 | tev->point.retprobe = pev->point.retprobe; |
1830 | tev->nargs = pev->nargs; | 1834 | tev->nargs = pev->nargs; |
1831 | if (tev->nargs) { | 1835 | if (tev->nargs) { |
1832 | tev->args = zalloc(sizeof(struct probe_trace_arg) | 1836 | tev->args = zalloc(sizeof(struct probe_trace_arg) |
1833 | * tev->nargs); | 1837 | * tev->nargs); |
1834 | if (tev->args == NULL) { | 1838 | if (tev->args == NULL) { |
1835 | ret = -ENOMEM; | 1839 | ret = -ENOMEM; |
1836 | goto error; | 1840 | goto error; |
1837 | } | 1841 | } |
1838 | for (i = 0; i < tev->nargs; i++) { | 1842 | for (i = 0; i < tev->nargs; i++) { |
1839 | if (pev->args[i].name) { | 1843 | if (pev->args[i].name) { |
1840 | tev->args[i].name = strdup(pev->args[i].name); | 1844 | tev->args[i].name = strdup(pev->args[i].name); |
1841 | if (tev->args[i].name == NULL) { | 1845 | if (tev->args[i].name == NULL) { |
1842 | ret = -ENOMEM; | 1846 | ret = -ENOMEM; |
1843 | goto error; | 1847 | goto error; |
1844 | } | 1848 | } |
1845 | } | 1849 | } |
1846 | tev->args[i].value = strdup(pev->args[i].var); | 1850 | tev->args[i].value = strdup(pev->args[i].var); |
1847 | if (tev->args[i].value == NULL) { | 1851 | if (tev->args[i].value == NULL) { |
1848 | ret = -ENOMEM; | 1852 | ret = -ENOMEM; |
1849 | goto error; | 1853 | goto error; |
1850 | } | 1854 | } |
1851 | if (pev->args[i].type) { | 1855 | if (pev->args[i].type) { |
1852 | tev->args[i].type = strdup(pev->args[i].type); | 1856 | tev->args[i].type = strdup(pev->args[i].type); |
1853 | if (tev->args[i].type == NULL) { | 1857 | if (tev->args[i].type == NULL) { |
1854 | ret = -ENOMEM; | 1858 | ret = -ENOMEM; |
1855 | goto error; | 1859 | goto error; |
1856 | } | 1860 | } |
1857 | } | 1861 | } |
1858 | } | 1862 | } |
1859 | } | 1863 | } |
1860 | 1864 | ||
1861 | /* Currently just checking function name from symbol map */ | 1865 | /* Currently just checking function name from symbol map */ |
1862 | sym = __find_kernel_function_by_name(tev->point.symbol, NULL); | 1866 | sym = __find_kernel_function_by_name(tev->point.symbol, NULL); |
1863 | if (!sym) { | 1867 | if (!sym) { |
1864 | pr_warning("Kernel symbol \'%s\' not found.\n", | 1868 | pr_warning("Kernel symbol \'%s\' not found.\n", |
1865 | tev->point.symbol); | 1869 | tev->point.symbol); |
1866 | ret = -ENOENT; | 1870 | ret = -ENOENT; |
1867 | goto error; | 1871 | goto error; |
1868 | } | 1872 | } |
1869 | 1873 | ||
1870 | return 1; | 1874 | return 1; |
1871 | error: | 1875 | error: |
1872 | clear_probe_trace_event(tev); | 1876 | clear_probe_trace_event(tev); |
1873 | free(tev); | 1877 | free(tev); |
1874 | *tevs = NULL; | 1878 | *tevs = NULL; |
1875 | return ret; | 1879 | return ret; |
1876 | } | 1880 | } |
1877 | 1881 | ||
1878 | struct __event_package { | 1882 | struct __event_package { |
1879 | struct perf_probe_event *pev; | 1883 | struct perf_probe_event *pev; |
1880 | struct probe_trace_event *tevs; | 1884 | struct probe_trace_event *tevs; |
1881 | int ntevs; | 1885 | int ntevs; |
1882 | }; | 1886 | }; |
1883 | 1887 | ||
1884 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | 1888 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, |
1885 | int max_tevs, const char *module, bool force_add) | 1889 | int max_tevs, const char *module, bool force_add) |
1886 | { | 1890 | { |
1887 | int i, j, ret; | 1891 | int i, j, ret; |
1888 | struct __event_package *pkgs; | 1892 | struct __event_package *pkgs; |
1889 | 1893 | ||
1890 | pkgs = zalloc(sizeof(struct __event_package) * npevs); | 1894 | pkgs = zalloc(sizeof(struct __event_package) * npevs); |
1891 | if (pkgs == NULL) | 1895 | if (pkgs == NULL) |
1892 | return -ENOMEM; | 1896 | return -ENOMEM; |
1893 | 1897 | ||
1894 | /* Init vmlinux path */ | 1898 | /* Init vmlinux path */ |
1895 | ret = init_vmlinux(); | 1899 | ret = init_vmlinux(); |
1896 | if (ret < 0) { | 1900 | if (ret < 0) { |
1897 | free(pkgs); | 1901 | free(pkgs); |
1898 | return ret; | 1902 | return ret; |
1899 | } | 1903 | } |
1900 | 1904 | ||
1901 | /* Loop 1: convert all events */ | 1905 | /* Loop 1: convert all events */ |
1902 | for (i = 0; i < npevs; i++) { | 1906 | for (i = 0; i < npevs; i++) { |
1903 | pkgs[i].pev = &pevs[i]; | 1907 | pkgs[i].pev = &pevs[i]; |
1904 | /* Convert with or without debuginfo */ | 1908 | /* Convert with or without debuginfo */ |
1905 | ret = convert_to_probe_trace_events(pkgs[i].pev, | 1909 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
1906 | &pkgs[i].tevs, | 1910 | &pkgs[i].tevs, |
1907 | max_tevs, | 1911 | max_tevs, |
1908 | module); | 1912 | module); |
1909 | if (ret < 0) | 1913 | if (ret < 0) |
1910 | goto end; | 1914 | goto end; |
1911 | pkgs[i].ntevs = ret; | 1915 | pkgs[i].ntevs = ret; |
1912 | } | 1916 | } |
1913 | 1917 | ||
1914 | /* Loop 2: add all events */ | 1918 | /* Loop 2: add all events */ |
1915 | for (i = 0; i < npevs; i++) { | 1919 | for (i = 0; i < npevs; i++) { |
1916 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1920 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
1917 | pkgs[i].ntevs, force_add); | 1921 | pkgs[i].ntevs, force_add); |
1918 | if (ret < 0) | 1922 | if (ret < 0) |
1919 | break; | 1923 | break; |
1920 | } | 1924 | } |
1921 | end: | 1925 | end: |
1922 | /* Loop 3: cleanup and free trace events */ | 1926 | /* Loop 3: cleanup and free trace events */ |
1923 | for (i = 0; i < npevs; i++) { | 1927 | for (i = 0; i < npevs; i++) { |
1924 | for (j = 0; j < pkgs[i].ntevs; j++) | 1928 | for (j = 0; j < pkgs[i].ntevs; j++) |
1925 | clear_probe_trace_event(&pkgs[i].tevs[j]); | 1929 | clear_probe_trace_event(&pkgs[i].tevs[j]); |
1926 | free(pkgs[i].tevs); | 1930 | free(pkgs[i].tevs); |
1927 | } | 1931 | } |
1928 | free(pkgs); | 1932 | free(pkgs); |
1929 | 1933 | ||
1930 | return ret; | 1934 | return ret; |
1931 | } | 1935 | } |
1932 | 1936 | ||
1933 | static int __del_trace_probe_event(int fd, struct str_node *ent) | 1937 | static int __del_trace_probe_event(int fd, struct str_node *ent) |
1934 | { | 1938 | { |
1935 | char *p; | 1939 | char *p; |
1936 | char buf[128]; | 1940 | char buf[128]; |
1937 | int ret; | 1941 | int ret; |
1938 | 1942 | ||
1939 | /* Convert from perf-probe event to trace-probe event */ | 1943 | /* Convert from perf-probe event to trace-probe event */ |
1940 | ret = e_snprintf(buf, 128, "-:%s", ent->s); | 1944 | ret = e_snprintf(buf, 128, "-:%s", ent->s); |
1941 | if (ret < 0) | 1945 | if (ret < 0) |
1942 | goto error; | 1946 | goto error; |
1943 | 1947 | ||
1944 | p = strchr(buf + 2, ':'); | 1948 | p = strchr(buf + 2, ':'); |
1945 | if (!p) { | 1949 | if (!p) { |
1946 | pr_debug("Internal error: %s should have ':' but not.\n", | 1950 | pr_debug("Internal error: %s should have ':' but not.\n", |
1947 | ent->s); | 1951 | ent->s); |
1948 | ret = -ENOTSUP; | 1952 | ret = -ENOTSUP; |
1949 | goto error; | 1953 | goto error; |
1950 | } | 1954 | } |
1951 | *p = '/'; | 1955 | *p = '/'; |
1952 | 1956 | ||
1953 | pr_debug("Writing event: %s\n", buf); | 1957 | pr_debug("Writing event: %s\n", buf); |
1954 | ret = write(fd, buf, strlen(buf)); | 1958 | ret = write(fd, buf, strlen(buf)); |
1955 | if (ret < 0) | 1959 | if (ret < 0) |
1956 | goto error; | 1960 | goto error; |
1957 | 1961 | ||
1958 | printf("Remove event: %s\n", ent->s); | 1962 | printf("Remove event: %s\n", ent->s); |
1959 | return 0; | 1963 | return 0; |
1960 | error: | 1964 | error: |
1961 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); | 1965 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); |
1962 | return ret; | 1966 | return ret; |
1963 | } | 1967 | } |
1964 | 1968 | ||
1965 | static int del_trace_probe_event(int fd, const char *group, | 1969 | static int del_trace_probe_event(int fd, const char *group, |
1966 | const char *event, struct strlist *namelist) | 1970 | const char *event, struct strlist *namelist) |
1967 | { | 1971 | { |
1968 | char buf[128]; | 1972 | char buf[128]; |
1969 | struct str_node *ent, *n; | 1973 | struct str_node *ent, *n; |
1970 | int found = 0, ret = 0; | 1974 | int found = 0, ret = 0; |
1971 | 1975 | ||
1972 | ret = e_snprintf(buf, 128, "%s:%s", group, event); | 1976 | ret = e_snprintf(buf, 128, "%s:%s", group, event); |
1973 | if (ret < 0) { | 1977 | if (ret < 0) { |
1974 | pr_err("Failed to copy event.\n"); | 1978 | pr_err("Failed to copy event.\n"); |
1975 | return ret; | 1979 | return ret; |
1976 | } | 1980 | } |
1977 | 1981 | ||
1978 | if (strpbrk(buf, "*?")) { /* Glob-exp */ | 1982 | if (strpbrk(buf, "*?")) { /* Glob-exp */ |
1979 | strlist__for_each_safe(ent, n, namelist) | 1983 | strlist__for_each_safe(ent, n, namelist) |
1980 | if (strglobmatch(ent->s, buf)) { | 1984 | if (strglobmatch(ent->s, buf)) { |
1981 | found++; | 1985 | found++; |
1982 | ret = __del_trace_probe_event(fd, ent); | 1986 | ret = __del_trace_probe_event(fd, ent); |
1983 | if (ret < 0) | 1987 | if (ret < 0) |
1984 | break; | 1988 | break; |
1985 | strlist__remove(namelist, ent); | 1989 | strlist__remove(namelist, ent); |
1986 | } | 1990 | } |
1987 | } else { | 1991 | } else { |
1988 | ent = strlist__find(namelist, buf); | 1992 | ent = strlist__find(namelist, buf); |
1989 | if (ent) { | 1993 | if (ent) { |
1990 | found++; | 1994 | found++; |
1991 | ret = __del_trace_probe_event(fd, ent); | 1995 | ret = __del_trace_probe_event(fd, ent); |
1992 | if (ret >= 0) | 1996 | if (ret >= 0) |
1993 | strlist__remove(namelist, ent); | 1997 | strlist__remove(namelist, ent); |
1994 | } | 1998 | } |
1995 | } | 1999 | } |
1996 | if (found == 0 && ret >= 0) | 2000 | if (found == 0 && ret >= 0) |
1997 | pr_info("Info: Event \"%s\" does not exist.\n", buf); | 2001 | pr_info("Info: Event \"%s\" does not exist.\n", buf); |
1998 | 2002 | ||
1999 | return ret; | 2003 | return ret; |
2000 | } | 2004 | } |
2001 | 2005 | ||
2002 | int del_perf_probe_events(struct strlist *dellist) | 2006 | int del_perf_probe_events(struct strlist *dellist) |
2003 | { | 2007 | { |
2004 | int fd, ret = 0; | 2008 | int fd, ret = 0; |
2005 | const char *group, *event; | 2009 | const char *group, *event; |
2006 | char *p, *str; | 2010 | char *p, *str; |
2007 | struct str_node *ent; | 2011 | struct str_node *ent; |
2008 | struct strlist *namelist; | 2012 | struct strlist *namelist; |
2009 | 2013 | ||
2010 | fd = open_kprobe_events(true); | 2014 | fd = open_kprobe_events(true); |
2011 | if (fd < 0) | 2015 | if (fd < 0) |
2012 | return fd; | 2016 | return fd; |
2013 | 2017 | ||
2014 | /* Get current event names */ | 2018 | /* Get current event names */ |
2015 | namelist = get_probe_trace_event_names(fd, true); | 2019 | namelist = get_probe_trace_event_names(fd, true); |
2016 | if (namelist == NULL) | 2020 | if (namelist == NULL) |
2017 | return -EINVAL; | 2021 | return -EINVAL; |
2018 | 2022 | ||
2019 | strlist__for_each(ent, dellist) { | 2023 | strlist__for_each(ent, dellist) { |
2020 | str = strdup(ent->s); | 2024 | str = strdup(ent->s); |
2021 | if (str == NULL) { | 2025 | if (str == NULL) { |
2022 | ret = -ENOMEM; | 2026 | ret = -ENOMEM; |
2023 | break; | 2027 | break; |
2024 | } | 2028 | } |
2025 | pr_debug("Parsing: %s\n", str); | 2029 | pr_debug("Parsing: %s\n", str); |
2026 | p = strchr(str, ':'); | 2030 | p = strchr(str, ':'); |
2027 | if (p) { | 2031 | if (p) { |
2028 | group = str; | 2032 | group = str; |
2029 | *p = '\0'; | 2033 | *p = '\0'; |
2030 | event = p + 1; | 2034 | event = p + 1; |
2031 | } else { | 2035 | } else { |
2032 | group = "*"; | 2036 | group = "*"; |
2033 | event = str; | 2037 | event = str; |
2034 | } | 2038 | } |
2035 | pr_debug("Group: %s, Event: %s\n", group, event); | 2039 | pr_debug("Group: %s, Event: %s\n", group, event); |
2036 | ret = del_trace_probe_event(fd, group, event, namelist); | 2040 | ret = del_trace_probe_event(fd, group, event, namelist); |
2037 | free(str); | 2041 | free(str); |
2038 | if (ret < 0) | 2042 | if (ret < 0) |
2039 | break; | 2043 | break; |
2040 | } | 2044 | } |
2041 | strlist__delete(namelist); | 2045 | strlist__delete(namelist); |
2042 | close(fd); | 2046 | close(fd); |
2043 | 2047 | ||
2044 | return ret; | 2048 | return ret; |
2045 | } | 2049 | } |
2046 | /* TODO: don't use a global variable for filter ... */ | 2050 | /* TODO: don't use a global variable for filter ... */ |
2047 | static struct strfilter *available_func_filter; | 2051 | static struct strfilter *available_func_filter; |
2048 | 2052 | ||
2049 | /* | 2053 | /* |
2050 | * If a symbol corresponds to a function with global binding and | 2054 | * If a symbol corresponds to a function with global binding and |
2051 | * matches filter return 0. For all others return 1. | 2055 | * matches filter return 0. For all others return 1. |
2052 | */ | 2056 | */ |
2053 | static int filter_available_functions(struct map *map __unused, | 2057 | static int filter_available_functions(struct map *map __unused, |
2054 | struct symbol *sym) | 2058 | struct symbol *sym) |
2055 | { | 2059 | { |
2056 | if (sym->binding == STB_GLOBAL && | 2060 | if (sym->binding == STB_GLOBAL && |
2057 | strfilter__compare(available_func_filter, sym->name)) | 2061 | strfilter__compare(available_func_filter, sym->name)) |
2058 | return 0; | 2062 | return 0; |
2059 | return 1; | 2063 | return 1; |
2060 | } | 2064 | } |
2061 | 2065 | ||
2062 | int show_available_funcs(const char *module, struct strfilter *_filter) | 2066 | int show_available_funcs(const char *module, struct strfilter *_filter) |
2063 | { | 2067 | { |
2064 | struct map *map; | 2068 | struct map *map; |
2065 | int ret; | 2069 | int ret; |
2066 | 2070 | ||
2067 | setup_pager(); | 2071 | setup_pager(); |
2068 | 2072 | ||
2069 | ret = init_vmlinux(); | 2073 | ret = init_vmlinux(); |
2070 | if (ret < 0) | 2074 | if (ret < 0) |
2071 | return ret; | 2075 | return ret; |
2072 | 2076 | ||
2073 | map = kernel_get_module_map(module); | 2077 | map = kernel_get_module_map(module); |
2074 | if (!map) { | 2078 | if (!map) { |
2075 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | 2079 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); |
2076 | return -EINVAL; | 2080 | return -EINVAL; |
2077 | } | 2081 | } |
2078 | available_func_filter = _filter; | 2082 | available_func_filter = _filter; |
2079 | if (map__load(map, filter_available_functions)) { | 2083 | if (map__load(map, filter_available_functions)) { |
2080 | pr_err("Failed to load map.\n"); | 2084 | pr_err("Failed to load map.\n"); |
2081 | return -EINVAL; | 2085 | return -EINVAL; |
2082 | } | 2086 | } |
2083 | if (!dso__sorted_by_name(map->dso, map->type)) | 2087 | if (!dso__sorted_by_name(map->dso, map->type)) |
2084 | dso__sort_by_name(map->dso, map->type); | 2088 | dso__sort_by_name(map->dso, map->type); |
2085 | 2089 | ||
2086 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | 2090 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); |
2087 | return 0; | 2091 | return 0; |
2088 | } | 2092 | } |
2089 | 2093 |
tools/perf/util/python.c
1 | #include <Python.h> | 1 | #include <Python.h> |
2 | #include <structmember.h> | 2 | #include <structmember.h> |
3 | #include <inttypes.h> | 3 | #include <inttypes.h> |
4 | #include <poll.h> | 4 | #include <poll.h> |
5 | #include "evlist.h" | 5 | #include "evlist.h" |
6 | #include "evsel.h" | 6 | #include "evsel.h" |
7 | #include "event.h" | 7 | #include "event.h" |
8 | #include "cpumap.h" | 8 | #include "cpumap.h" |
9 | #include "thread_map.h" | 9 | #include "thread_map.h" |
10 | 10 | ||
11 | /* Define PyVarObject_HEAD_INIT for python 2.5 */ | 11 | /* Define PyVarObject_HEAD_INIT for python 2.5 */ |
12 | #ifndef PyVarObject_HEAD_INIT | 12 | #ifndef PyVarObject_HEAD_INIT |
13 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, | 13 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, |
14 | #endif | 14 | #endif |
15 | 15 | ||
16 | struct throttle_event { | 16 | struct throttle_event { |
17 | struct perf_event_header header; | 17 | struct perf_event_header header; |
18 | u64 time; | 18 | u64 time; |
19 | u64 id; | 19 | u64 id; |
20 | u64 stream_id; | 20 | u64 stream_id; |
21 | }; | 21 | }; |
22 | 22 | ||
23 | PyMODINIT_FUNC initperf(void); | 23 | PyMODINIT_FUNC initperf(void); |
24 | 24 | ||
25 | #define member_def(type, member, ptype, help) \ | 25 | #define member_def(type, member, ptype, help) \ |
26 | { #member, ptype, \ | 26 | { #member, ptype, \ |
27 | offsetof(struct pyrf_event, event) + offsetof(struct type, member), \ | 27 | offsetof(struct pyrf_event, event) + offsetof(struct type, member), \ |
28 | 0, help } | 28 | 0, help } |
29 | 29 | ||
30 | #define sample_member_def(name, member, ptype, help) \ | 30 | #define sample_member_def(name, member, ptype, help) \ |
31 | { #name, ptype, \ | 31 | { #name, ptype, \ |
32 | offsetof(struct pyrf_event, sample) + offsetof(struct perf_sample, member), \ | 32 | offsetof(struct pyrf_event, sample) + offsetof(struct perf_sample, member), \ |
33 | 0, help } | 33 | 0, help } |
34 | 34 | ||
35 | struct pyrf_event { | 35 | struct pyrf_event { |
36 | PyObject_HEAD | 36 | PyObject_HEAD |
37 | struct perf_sample sample; | 37 | struct perf_sample sample; |
38 | union perf_event event; | 38 | union perf_event event; |
39 | }; | 39 | }; |
40 | 40 | ||
41 | #define sample_members \ | 41 | #define sample_members \ |
42 | sample_member_def(sample_ip, ip, T_ULONGLONG, "event type"), \ | 42 | sample_member_def(sample_ip, ip, T_ULONGLONG, "event type"), \ |
43 | sample_member_def(sample_pid, pid, T_INT, "event pid"), \ | 43 | sample_member_def(sample_pid, pid, T_INT, "event pid"), \ |
44 | sample_member_def(sample_tid, tid, T_INT, "event tid"), \ | 44 | sample_member_def(sample_tid, tid, T_INT, "event tid"), \ |
45 | sample_member_def(sample_time, time, T_ULONGLONG, "event timestamp"), \ | 45 | sample_member_def(sample_time, time, T_ULONGLONG, "event timestamp"), \ |
46 | sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"), \ | 46 | sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"), \ |
47 | sample_member_def(sample_id, id, T_ULONGLONG, "event id"), \ | 47 | sample_member_def(sample_id, id, T_ULONGLONG, "event id"), \ |
48 | sample_member_def(sample_stream_id, stream_id, T_ULONGLONG, "event stream id"), \ | 48 | sample_member_def(sample_stream_id, stream_id, T_ULONGLONG, "event stream id"), \ |
49 | sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \ | 49 | sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \ |
50 | sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"), | 50 | sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"), |
51 | 51 | ||
52 | static char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object."); | 52 | static char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object."); |
53 | 53 | ||
54 | static PyMemberDef pyrf_mmap_event__members[] = { | 54 | static PyMemberDef pyrf_mmap_event__members[] = { |
55 | sample_members | 55 | sample_members |
56 | member_def(perf_event_header, type, T_UINT, "event type"), | 56 | member_def(perf_event_header, type, T_UINT, "event type"), |
57 | member_def(mmap_event, pid, T_UINT, "event pid"), | 57 | member_def(mmap_event, pid, T_UINT, "event pid"), |
58 | member_def(mmap_event, tid, T_UINT, "event tid"), | 58 | member_def(mmap_event, tid, T_UINT, "event tid"), |
59 | member_def(mmap_event, start, T_ULONGLONG, "start of the map"), | 59 | member_def(mmap_event, start, T_ULONGLONG, "start of the map"), |
60 | member_def(mmap_event, len, T_ULONGLONG, "map length"), | 60 | member_def(mmap_event, len, T_ULONGLONG, "map length"), |
61 | member_def(mmap_event, pgoff, T_ULONGLONG, "page offset"), | 61 | member_def(mmap_event, pgoff, T_ULONGLONG, "page offset"), |
62 | member_def(mmap_event, filename, T_STRING_INPLACE, "backing store"), | 62 | member_def(mmap_event, filename, T_STRING_INPLACE, "backing store"), |
63 | { .name = NULL, }, | 63 | { .name = NULL, }, |
64 | }; | 64 | }; |
65 | 65 | ||
66 | static PyObject *pyrf_mmap_event__repr(struct pyrf_event *pevent) | 66 | static PyObject *pyrf_mmap_event__repr(struct pyrf_event *pevent) |
67 | { | 67 | { |
68 | PyObject *ret; | 68 | PyObject *ret; |
69 | char *s; | 69 | char *s; |
70 | 70 | ||
71 | if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRIx64 ", " | 71 | if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRIx64 ", " |
72 | "length: %#" PRIx64 ", offset: %#" PRIx64 ", " | 72 | "length: %#" PRIx64 ", offset: %#" PRIx64 ", " |
73 | "filename: %s }", | 73 | "filename: %s }", |
74 | pevent->event.mmap.pid, pevent->event.mmap.tid, | 74 | pevent->event.mmap.pid, pevent->event.mmap.tid, |
75 | pevent->event.mmap.start, pevent->event.mmap.len, | 75 | pevent->event.mmap.start, pevent->event.mmap.len, |
76 | pevent->event.mmap.pgoff, pevent->event.mmap.filename) < 0) { | 76 | pevent->event.mmap.pgoff, pevent->event.mmap.filename) < 0) { |
77 | ret = PyErr_NoMemory(); | 77 | ret = PyErr_NoMemory(); |
78 | } else { | 78 | } else { |
79 | ret = PyString_FromString(s); | 79 | ret = PyString_FromString(s); |
80 | free(s); | 80 | free(s); |
81 | } | 81 | } |
82 | return ret; | 82 | return ret; |
83 | } | 83 | } |
84 | 84 | ||
85 | static PyTypeObject pyrf_mmap_event__type = { | 85 | static PyTypeObject pyrf_mmap_event__type = { |
86 | PyVarObject_HEAD_INIT(NULL, 0) | 86 | PyVarObject_HEAD_INIT(NULL, 0) |
87 | .tp_name = "perf.mmap_event", | 87 | .tp_name = "perf.mmap_event", |
88 | .tp_basicsize = sizeof(struct pyrf_event), | 88 | .tp_basicsize = sizeof(struct pyrf_event), |
89 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | 89 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, |
90 | .tp_doc = pyrf_mmap_event__doc, | 90 | .tp_doc = pyrf_mmap_event__doc, |
91 | .tp_members = pyrf_mmap_event__members, | 91 | .tp_members = pyrf_mmap_event__members, |
92 | .tp_repr = (reprfunc)pyrf_mmap_event__repr, | 92 | .tp_repr = (reprfunc)pyrf_mmap_event__repr, |
93 | }; | 93 | }; |
94 | 94 | ||
95 | static char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object."); | 95 | static char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object."); |
96 | 96 | ||
97 | static PyMemberDef pyrf_task_event__members[] = { | 97 | static PyMemberDef pyrf_task_event__members[] = { |
98 | sample_members | 98 | sample_members |
99 | member_def(perf_event_header, type, T_UINT, "event type"), | 99 | member_def(perf_event_header, type, T_UINT, "event type"), |
100 | member_def(fork_event, pid, T_UINT, "event pid"), | 100 | member_def(fork_event, pid, T_UINT, "event pid"), |
101 | member_def(fork_event, ppid, T_UINT, "event ppid"), | 101 | member_def(fork_event, ppid, T_UINT, "event ppid"), |
102 | member_def(fork_event, tid, T_UINT, "event tid"), | 102 | member_def(fork_event, tid, T_UINT, "event tid"), |
103 | member_def(fork_event, ptid, T_UINT, "event ptid"), | 103 | member_def(fork_event, ptid, T_UINT, "event ptid"), |
104 | member_def(fork_event, time, T_ULONGLONG, "timestamp"), | 104 | member_def(fork_event, time, T_ULONGLONG, "timestamp"), |
105 | { .name = NULL, }, | 105 | { .name = NULL, }, |
106 | }; | 106 | }; |
107 | 107 | ||
108 | static PyObject *pyrf_task_event__repr(struct pyrf_event *pevent) | 108 | static PyObject *pyrf_task_event__repr(struct pyrf_event *pevent) |
109 | { | 109 | { |
110 | return PyString_FromFormat("{ type: %s, pid: %u, ppid: %u, tid: %u, " | 110 | return PyString_FromFormat("{ type: %s, pid: %u, ppid: %u, tid: %u, " |
111 | "ptid: %u, time: %" PRIu64 "}", | 111 | "ptid: %u, time: %" PRIu64 "}", |
112 | pevent->event.header.type == PERF_RECORD_FORK ? "fork" : "exit", | 112 | pevent->event.header.type == PERF_RECORD_FORK ? "fork" : "exit", |
113 | pevent->event.fork.pid, | 113 | pevent->event.fork.pid, |
114 | pevent->event.fork.ppid, | 114 | pevent->event.fork.ppid, |
115 | pevent->event.fork.tid, | 115 | pevent->event.fork.tid, |
116 | pevent->event.fork.ptid, | 116 | pevent->event.fork.ptid, |
117 | pevent->event.fork.time); | 117 | pevent->event.fork.time); |
118 | } | 118 | } |
119 | 119 | ||
120 | static PyTypeObject pyrf_task_event__type = { | 120 | static PyTypeObject pyrf_task_event__type = { |
121 | PyVarObject_HEAD_INIT(NULL, 0) | 121 | PyVarObject_HEAD_INIT(NULL, 0) |
122 | .tp_name = "perf.task_event", | 122 | .tp_name = "perf.task_event", |
123 | .tp_basicsize = sizeof(struct pyrf_event), | 123 | .tp_basicsize = sizeof(struct pyrf_event), |
124 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | 124 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, |
125 | .tp_doc = pyrf_task_event__doc, | 125 | .tp_doc = pyrf_task_event__doc, |
126 | .tp_members = pyrf_task_event__members, | 126 | .tp_members = pyrf_task_event__members, |
127 | .tp_repr = (reprfunc)pyrf_task_event__repr, | 127 | .tp_repr = (reprfunc)pyrf_task_event__repr, |
128 | }; | 128 | }; |
129 | 129 | ||
130 | static char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object."); | 130 | static char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object."); |
131 | 131 | ||
132 | static PyMemberDef pyrf_comm_event__members[] = { | 132 | static PyMemberDef pyrf_comm_event__members[] = { |
133 | sample_members | 133 | sample_members |
134 | member_def(perf_event_header, type, T_UINT, "event type"), | 134 | member_def(perf_event_header, type, T_UINT, "event type"), |
135 | member_def(comm_event, pid, T_UINT, "event pid"), | 135 | member_def(comm_event, pid, T_UINT, "event pid"), |
136 | member_def(comm_event, tid, T_UINT, "event tid"), | 136 | member_def(comm_event, tid, T_UINT, "event tid"), |
137 | member_def(comm_event, comm, T_STRING_INPLACE, "process name"), | 137 | member_def(comm_event, comm, T_STRING_INPLACE, "process name"), |
138 | { .name = NULL, }, | 138 | { .name = NULL, }, |
139 | }; | 139 | }; |
140 | 140 | ||
141 | static PyObject *pyrf_comm_event__repr(struct pyrf_event *pevent) | 141 | static PyObject *pyrf_comm_event__repr(struct pyrf_event *pevent) |
142 | { | 142 | { |
143 | return PyString_FromFormat("{ type: comm, pid: %u, tid: %u, comm: %s }", | 143 | return PyString_FromFormat("{ type: comm, pid: %u, tid: %u, comm: %s }", |
144 | pevent->event.comm.pid, | 144 | pevent->event.comm.pid, |
145 | pevent->event.comm.tid, | 145 | pevent->event.comm.tid, |
146 | pevent->event.comm.comm); | 146 | pevent->event.comm.comm); |
147 | } | 147 | } |
148 | 148 | ||
149 | static PyTypeObject pyrf_comm_event__type = { | 149 | static PyTypeObject pyrf_comm_event__type = { |
150 | PyVarObject_HEAD_INIT(NULL, 0) | 150 | PyVarObject_HEAD_INIT(NULL, 0) |
151 | .tp_name = "perf.comm_event", | 151 | .tp_name = "perf.comm_event", |
152 | .tp_basicsize = sizeof(struct pyrf_event), | 152 | .tp_basicsize = sizeof(struct pyrf_event), |
153 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | 153 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, |
154 | .tp_doc = pyrf_comm_event__doc, | 154 | .tp_doc = pyrf_comm_event__doc, |
155 | .tp_members = pyrf_comm_event__members, | 155 | .tp_members = pyrf_comm_event__members, |
156 | .tp_repr = (reprfunc)pyrf_comm_event__repr, | 156 | .tp_repr = (reprfunc)pyrf_comm_event__repr, |
157 | }; | 157 | }; |
158 | 158 | ||
159 | static char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object."); | 159 | static char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object."); |
160 | 160 | ||
161 | static PyMemberDef pyrf_throttle_event__members[] = { | 161 | static PyMemberDef pyrf_throttle_event__members[] = { |
162 | sample_members | 162 | sample_members |
163 | member_def(perf_event_header, type, T_UINT, "event type"), | 163 | member_def(perf_event_header, type, T_UINT, "event type"), |
164 | member_def(throttle_event, time, T_ULONGLONG, "timestamp"), | 164 | member_def(throttle_event, time, T_ULONGLONG, "timestamp"), |
165 | member_def(throttle_event, id, T_ULONGLONG, "event id"), | 165 | member_def(throttle_event, id, T_ULONGLONG, "event id"), |
166 | member_def(throttle_event, stream_id, T_ULONGLONG, "event stream id"), | 166 | member_def(throttle_event, stream_id, T_ULONGLONG, "event stream id"), |
167 | { .name = NULL, }, | 167 | { .name = NULL, }, |
168 | }; | 168 | }; |
169 | 169 | ||
170 | static PyObject *pyrf_throttle_event__repr(struct pyrf_event *pevent) | 170 | static PyObject *pyrf_throttle_event__repr(struct pyrf_event *pevent) |
171 | { | 171 | { |
172 | struct throttle_event *te = (struct throttle_event *)(&pevent->event.header + 1); | 172 | struct throttle_event *te = (struct throttle_event *)(&pevent->event.header + 1); |
173 | 173 | ||
174 | return PyString_FromFormat("{ type: %sthrottle, time: %" PRIu64 ", id: %" PRIu64 | 174 | return PyString_FromFormat("{ type: %sthrottle, time: %" PRIu64 ", id: %" PRIu64 |
175 | ", stream_id: %" PRIu64 " }", | 175 | ", stream_id: %" PRIu64 " }", |
176 | pevent->event.header.type == PERF_RECORD_THROTTLE ? "" : "un", | 176 | pevent->event.header.type == PERF_RECORD_THROTTLE ? "" : "un", |
177 | te->time, te->id, te->stream_id); | 177 | te->time, te->id, te->stream_id); |
178 | } | 178 | } |
179 | 179 | ||
180 | static PyTypeObject pyrf_throttle_event__type = { | 180 | static PyTypeObject pyrf_throttle_event__type = { |
181 | PyVarObject_HEAD_INIT(NULL, 0) | 181 | PyVarObject_HEAD_INIT(NULL, 0) |
182 | .tp_name = "perf.throttle_event", | 182 | .tp_name = "perf.throttle_event", |
183 | .tp_basicsize = sizeof(struct pyrf_event), | 183 | .tp_basicsize = sizeof(struct pyrf_event), |
184 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | 184 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, |
185 | .tp_doc = pyrf_throttle_event__doc, | 185 | .tp_doc = pyrf_throttle_event__doc, |
186 | .tp_members = pyrf_throttle_event__members, | 186 | .tp_members = pyrf_throttle_event__members, |
187 | .tp_repr = (reprfunc)pyrf_throttle_event__repr, | 187 | .tp_repr = (reprfunc)pyrf_throttle_event__repr, |
188 | }; | 188 | }; |
189 | 189 | ||
190 | static char pyrf_lost_event__doc[] = PyDoc_STR("perf lost event object."); | ||
191 | |||
192 | static PyMemberDef pyrf_lost_event__members[] = { | ||
193 | sample_members | ||
194 | member_def(lost_event, id, T_ULONGLONG, "event id"), | ||
195 | member_def(lost_event, lost, T_ULONGLONG, "number of lost events"), | ||
196 | { .name = NULL, }, | ||
197 | }; | ||
198 | |||
199 | static PyObject *pyrf_lost_event__repr(struct pyrf_event *pevent) | ||
200 | { | ||
201 | PyObject *ret; | ||
202 | char *s; | ||
203 | |||
204 | if (asprintf(&s, "{ type: lost, id: %#" PRIx64 ", " | ||
205 | "lost: %#" PRIx64 " }", | ||
206 | pevent->event.lost.id, pevent->event.lost.lost) < 0) { | ||
207 | ret = PyErr_NoMemory(); | ||
208 | } else { | ||
209 | ret = PyString_FromString(s); | ||
210 | free(s); | ||
211 | } | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static PyTypeObject pyrf_lost_event__type = { | ||
216 | PyVarObject_HEAD_INIT(NULL, 0) | ||
217 | .tp_name = "perf.lost_event", | ||
218 | .tp_basicsize = sizeof(struct pyrf_event), | ||
219 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
220 | .tp_doc = pyrf_lost_event__doc, | ||
221 | .tp_members = pyrf_lost_event__members, | ||
222 | .tp_repr = (reprfunc)pyrf_lost_event__repr, | ||
223 | }; | ||
224 | |||
225 | static char pyrf_read_event__doc[] = PyDoc_STR("perf read event object."); | ||
226 | |||
227 | static PyMemberDef pyrf_read_event__members[] = { | ||
228 | sample_members | ||
229 | member_def(read_event, pid, T_UINT, "event pid"), | ||
230 | member_def(read_event, tid, T_UINT, "event tid"), | ||
231 | { .name = NULL, }, | ||
232 | }; | ||
233 | |||
234 | static PyObject *pyrf_read_event__repr(struct pyrf_event *pevent) | ||
235 | { | ||
236 | return PyString_FromFormat("{ type: read, pid: %u, tid: %u }", | ||
237 | pevent->event.read.pid, | ||
238 | pevent->event.read.tid); | ||
239 | /* | ||
240 | * FIXME: return the array of read values, | ||
241 | * making this method useful ;-) | ||
242 | */ | ||
243 | } | ||
244 | |||
245 | static PyTypeObject pyrf_read_event__type = { | ||
246 | PyVarObject_HEAD_INIT(NULL, 0) | ||
247 | .tp_name = "perf.read_event", | ||
248 | .tp_basicsize = sizeof(struct pyrf_event), | ||
249 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
250 | .tp_doc = pyrf_read_event__doc, | ||
251 | .tp_members = pyrf_read_event__members, | ||
252 | .tp_repr = (reprfunc)pyrf_read_event__repr, | ||
253 | }; | ||
254 | |||
255 | static char pyrf_sample_event__doc[] = PyDoc_STR("perf sample event object."); | ||
256 | |||
257 | static PyMemberDef pyrf_sample_event__members[] = { | ||
258 | sample_members | ||
259 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
260 | { .name = NULL, }, | ||
261 | }; | ||
262 | |||
263 | static PyObject *pyrf_sample_event__repr(struct pyrf_event *pevent) | ||
264 | { | ||
265 | PyObject *ret; | ||
266 | char *s; | ||
267 | |||
268 | if (asprintf(&s, "{ type: sample }") < 0) { | ||
269 | ret = PyErr_NoMemory(); | ||
270 | } else { | ||
271 | ret = PyString_FromString(s); | ||
272 | free(s); | ||
273 | } | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | static PyTypeObject pyrf_sample_event__type = { | ||
278 | PyVarObject_HEAD_INIT(NULL, 0) | ||
279 | .tp_name = "perf.sample_event", | ||
280 | .tp_basicsize = sizeof(struct pyrf_event), | ||
281 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
282 | .tp_doc = pyrf_sample_event__doc, | ||
283 | .tp_members = pyrf_sample_event__members, | ||
284 | .tp_repr = (reprfunc)pyrf_sample_event__repr, | ||
285 | }; | ||
286 | |||
190 | static int pyrf_event__setup_types(void) | 287 | static int pyrf_event__setup_types(void) |
191 | { | 288 | { |
192 | int err; | 289 | int err; |
193 | pyrf_mmap_event__type.tp_new = | 290 | pyrf_mmap_event__type.tp_new = |
194 | pyrf_task_event__type.tp_new = | 291 | pyrf_task_event__type.tp_new = |
195 | pyrf_comm_event__type.tp_new = | 292 | pyrf_comm_event__type.tp_new = |
293 | pyrf_lost_event__type.tp_new = | ||
294 | pyrf_read_event__type.tp_new = | ||
295 | pyrf_sample_event__type.tp_new = | ||
196 | pyrf_throttle_event__type.tp_new = PyType_GenericNew; | 296 | pyrf_throttle_event__type.tp_new = PyType_GenericNew; |
197 | err = PyType_Ready(&pyrf_mmap_event__type); | 297 | err = PyType_Ready(&pyrf_mmap_event__type); |
198 | if (err < 0) | 298 | if (err < 0) |
199 | goto out; | 299 | goto out; |
300 | err = PyType_Ready(&pyrf_lost_event__type); | ||
301 | if (err < 0) | ||
302 | goto out; | ||
200 | err = PyType_Ready(&pyrf_task_event__type); | 303 | err = PyType_Ready(&pyrf_task_event__type); |
201 | if (err < 0) | 304 | if (err < 0) |
202 | goto out; | 305 | goto out; |
203 | err = PyType_Ready(&pyrf_comm_event__type); | 306 | err = PyType_Ready(&pyrf_comm_event__type); |
204 | if (err < 0) | 307 | if (err < 0) |
205 | goto out; | 308 | goto out; |
206 | err = PyType_Ready(&pyrf_throttle_event__type); | 309 | err = PyType_Ready(&pyrf_throttle_event__type); |
207 | if (err < 0) | 310 | if (err < 0) |
208 | goto out; | 311 | goto out; |
312 | err = PyType_Ready(&pyrf_read_event__type); | ||
313 | if (err < 0) | ||
314 | goto out; | ||
315 | err = PyType_Ready(&pyrf_sample_event__type); | ||
316 | if (err < 0) | ||
317 | goto out; | ||
209 | out: | 318 | out: |
210 | return err; | 319 | return err; |
211 | } | 320 | } |
212 | 321 | ||
213 | static PyTypeObject *pyrf_event__type[] = { | 322 | static PyTypeObject *pyrf_event__type[] = { |
214 | [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, | 323 | [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, |
215 | [PERF_RECORD_LOST] = &pyrf_mmap_event__type, | 324 | [PERF_RECORD_LOST] = &pyrf_lost_event__type, |
216 | [PERF_RECORD_COMM] = &pyrf_comm_event__type, | 325 | [PERF_RECORD_COMM] = &pyrf_comm_event__type, |
217 | [PERF_RECORD_EXIT] = &pyrf_task_event__type, | 326 | [PERF_RECORD_EXIT] = &pyrf_task_event__type, |
218 | [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, | 327 | [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, |
219 | [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, | 328 | [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, |
220 | [PERF_RECORD_FORK] = &pyrf_task_event__type, | 329 | [PERF_RECORD_FORK] = &pyrf_task_event__type, |
221 | [PERF_RECORD_READ] = &pyrf_mmap_event__type, | 330 | [PERF_RECORD_READ] = &pyrf_read_event__type, |
222 | [PERF_RECORD_SAMPLE] = &pyrf_mmap_event__type, | 331 | [PERF_RECORD_SAMPLE] = &pyrf_sample_event__type, |
223 | }; | 332 | }; |
224 | 333 | ||
225 | static PyObject *pyrf_event__new(union perf_event *event) | 334 | static PyObject *pyrf_event__new(union perf_event *event) |
226 | { | 335 | { |
227 | struct pyrf_event *pevent; | 336 | struct pyrf_event *pevent; |
228 | PyTypeObject *ptype; | 337 | PyTypeObject *ptype; |
229 | 338 | ||
230 | if (event->header.type < PERF_RECORD_MMAP || | 339 | if (event->header.type < PERF_RECORD_MMAP || |
231 | event->header.type > PERF_RECORD_SAMPLE) | 340 | event->header.type > PERF_RECORD_SAMPLE) |
232 | return NULL; | 341 | return NULL; |
233 | 342 | ||
234 | ptype = pyrf_event__type[event->header.type]; | 343 | ptype = pyrf_event__type[event->header.type]; |
235 | pevent = PyObject_New(struct pyrf_event, ptype); | 344 | pevent = PyObject_New(struct pyrf_event, ptype); |
236 | if (pevent != NULL) | 345 | if (pevent != NULL) |
237 | memcpy(&pevent->event, event, event->header.size); | 346 | memcpy(&pevent->event, event, event->header.size); |
238 | return (PyObject *)pevent; | 347 | return (PyObject *)pevent; |
239 | } | 348 | } |
240 | 349 | ||
241 | struct pyrf_cpu_map { | 350 | struct pyrf_cpu_map { |
242 | PyObject_HEAD | 351 | PyObject_HEAD |
243 | 352 | ||
244 | struct cpu_map *cpus; | 353 | struct cpu_map *cpus; |
245 | }; | 354 | }; |
246 | 355 | ||
247 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, | 356 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, |
248 | PyObject *args, PyObject *kwargs) | 357 | PyObject *args, PyObject *kwargs) |
249 | { | 358 | { |
250 | static char *kwlist[] = { "cpustr", NULL }; | 359 | static char *kwlist[] = { "cpustr", NULL }; |
251 | char *cpustr = NULL; | 360 | char *cpustr = NULL; |
252 | 361 | ||
253 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", | 362 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", |
254 | kwlist, &cpustr)) | 363 | kwlist, &cpustr)) |
255 | return -1; | 364 | return -1; |
256 | 365 | ||
257 | pcpus->cpus = cpu_map__new(cpustr); | 366 | pcpus->cpus = cpu_map__new(cpustr); |
258 | if (pcpus->cpus == NULL) | 367 | if (pcpus->cpus == NULL) |
259 | return -1; | 368 | return -1; |
260 | return 0; | 369 | return 0; |
261 | } | 370 | } |
262 | 371 | ||
263 | static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus) | 372 | static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus) |
264 | { | 373 | { |
265 | cpu_map__delete(pcpus->cpus); | 374 | cpu_map__delete(pcpus->cpus); |
266 | pcpus->ob_type->tp_free((PyObject*)pcpus); | 375 | pcpus->ob_type->tp_free((PyObject*)pcpus); |
267 | } | 376 | } |
268 | 377 | ||
269 | static Py_ssize_t pyrf_cpu_map__length(PyObject *obj) | 378 | static Py_ssize_t pyrf_cpu_map__length(PyObject *obj) |
270 | { | 379 | { |
271 | struct pyrf_cpu_map *pcpus = (void *)obj; | 380 | struct pyrf_cpu_map *pcpus = (void *)obj; |
272 | 381 | ||
273 | return pcpus->cpus->nr; | 382 | return pcpus->cpus->nr; |
274 | } | 383 | } |
275 | 384 | ||
276 | static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) | 385 | static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) |
277 | { | 386 | { |
278 | struct pyrf_cpu_map *pcpus = (void *)obj; | 387 | struct pyrf_cpu_map *pcpus = (void *)obj; |
279 | 388 | ||
280 | if (i >= pcpus->cpus->nr) | 389 | if (i >= pcpus->cpus->nr) |
281 | return NULL; | 390 | return NULL; |
282 | 391 | ||
283 | return Py_BuildValue("i", pcpus->cpus->map[i]); | 392 | return Py_BuildValue("i", pcpus->cpus->map[i]); |
284 | } | 393 | } |
285 | 394 | ||
286 | static PySequenceMethods pyrf_cpu_map__sequence_methods = { | 395 | static PySequenceMethods pyrf_cpu_map__sequence_methods = { |
287 | .sq_length = pyrf_cpu_map__length, | 396 | .sq_length = pyrf_cpu_map__length, |
288 | .sq_item = pyrf_cpu_map__item, | 397 | .sq_item = pyrf_cpu_map__item, |
289 | }; | 398 | }; |
290 | 399 | ||
291 | static char pyrf_cpu_map__doc[] = PyDoc_STR("cpu map object."); | 400 | static char pyrf_cpu_map__doc[] = PyDoc_STR("cpu map object."); |
292 | 401 | ||
293 | static PyTypeObject pyrf_cpu_map__type = { | 402 | static PyTypeObject pyrf_cpu_map__type = { |
294 | PyVarObject_HEAD_INIT(NULL, 0) | 403 | PyVarObject_HEAD_INIT(NULL, 0) |
295 | .tp_name = "perf.cpu_map", | 404 | .tp_name = "perf.cpu_map", |
296 | .tp_basicsize = sizeof(struct pyrf_cpu_map), | 405 | .tp_basicsize = sizeof(struct pyrf_cpu_map), |
297 | .tp_dealloc = (destructor)pyrf_cpu_map__delete, | 406 | .tp_dealloc = (destructor)pyrf_cpu_map__delete, |
298 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | 407 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, |
299 | .tp_doc = pyrf_cpu_map__doc, | 408 | .tp_doc = pyrf_cpu_map__doc, |
300 | .tp_as_sequence = &pyrf_cpu_map__sequence_methods, | 409 | .tp_as_sequence = &pyrf_cpu_map__sequence_methods, |
301 | .tp_init = (initproc)pyrf_cpu_map__init, | 410 | .tp_init = (initproc)pyrf_cpu_map__init, |
302 | }; | 411 | }; |
303 | 412 | ||
304 | static int pyrf_cpu_map__setup_types(void) | 413 | static int pyrf_cpu_map__setup_types(void) |
305 | { | 414 | { |
306 | pyrf_cpu_map__type.tp_new = PyType_GenericNew; | 415 | pyrf_cpu_map__type.tp_new = PyType_GenericNew; |
307 | return PyType_Ready(&pyrf_cpu_map__type); | 416 | return PyType_Ready(&pyrf_cpu_map__type); |
308 | } | 417 | } |
309 | 418 | ||
310 | struct pyrf_thread_map { | 419 | struct pyrf_thread_map { |
311 | PyObject_HEAD | 420 | PyObject_HEAD |
312 | 421 | ||
313 | struct thread_map *threads; | 422 | struct thread_map *threads; |
314 | }; | 423 | }; |
315 | 424 | ||
316 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, | 425 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, |
317 | PyObject *args, PyObject *kwargs) | 426 | PyObject *args, PyObject *kwargs) |
318 | { | 427 | { |
319 | static char *kwlist[] = { "pid", "tid", NULL }; | 428 | static char *kwlist[] = { "pid", "tid", NULL }; |
320 | int pid = -1, tid = -1; | 429 | int pid = -1, tid = -1; |
321 | 430 | ||
322 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", | 431 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", |
323 | kwlist, &pid, &tid)) | 432 | kwlist, &pid, &tid)) |
324 | return -1; | 433 | return -1; |
325 | 434 | ||
326 | pthreads->threads = thread_map__new(pid, tid); | 435 | pthreads->threads = thread_map__new(pid, tid); |
327 | if (pthreads->threads == NULL) | 436 | if (pthreads->threads == NULL) |
328 | return -1; | 437 | return -1; |
329 | return 0; | 438 | return 0; |
330 | } | 439 | } |
331 | 440 | ||
332 | static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads) | 441 | static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads) |
333 | { | 442 | { |
334 | thread_map__delete(pthreads->threads); | 443 | thread_map__delete(pthreads->threads); |
335 | pthreads->ob_type->tp_free((PyObject*)pthreads); | 444 | pthreads->ob_type->tp_free((PyObject*)pthreads); |
336 | } | 445 | } |
337 | 446 | ||
338 | static Py_ssize_t pyrf_thread_map__length(PyObject *obj) | 447 | static Py_ssize_t pyrf_thread_map__length(PyObject *obj) |
339 | { | 448 | { |
340 | struct pyrf_thread_map *pthreads = (void *)obj; | 449 | struct pyrf_thread_map *pthreads = (void *)obj; |
341 | 450 | ||
342 | return pthreads->threads->nr; | 451 | return pthreads->threads->nr; |
343 | } | 452 | } |
344 | 453 | ||
345 | static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i) | 454 | static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i) |
346 | { | 455 | { |
347 | struct pyrf_thread_map *pthreads = (void *)obj; | 456 | struct pyrf_thread_map *pthreads = (void *)obj; |
348 | 457 | ||
349 | if (i >= pthreads->threads->nr) | 458 | if (i >= pthreads->threads->nr) |
350 | return NULL; | 459 | return NULL; |
351 | 460 | ||
352 | return Py_BuildValue("i", pthreads->threads->map[i]); | 461 | return Py_BuildValue("i", pthreads->threads->map[i]); |
353 | } | 462 | } |
354 | 463 | ||
355 | static PySequenceMethods pyrf_thread_map__sequence_methods = { | 464 | static PySequenceMethods pyrf_thread_map__sequence_methods = { |
356 | .sq_length = pyrf_thread_map__length, | 465 | .sq_length = pyrf_thread_map__length, |
357 | .sq_item = pyrf_thread_map__item, | 466 | .sq_item = pyrf_thread_map__item, |
358 | }; | 467 | }; |
359 | 468 | ||
360 | static char pyrf_thread_map__doc[] = PyDoc_STR("thread map object."); | 469 | static char pyrf_thread_map__doc[] = PyDoc_STR("thread map object."); |
361 | 470 | ||
362 | static PyTypeObject pyrf_thread_map__type = { | 471 | static PyTypeObject pyrf_thread_map__type = { |
363 | PyVarObject_HEAD_INIT(NULL, 0) | 472 | PyVarObject_HEAD_INIT(NULL, 0) |
364 | .tp_name = "perf.thread_map", | 473 | .tp_name = "perf.thread_map", |
365 | .tp_basicsize = sizeof(struct pyrf_thread_map), | 474 | .tp_basicsize = sizeof(struct pyrf_thread_map), |
366 | .tp_dealloc = (destructor)pyrf_thread_map__delete, | 475 | .tp_dealloc = (destructor)pyrf_thread_map__delete, |
367 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | 476 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, |
368 | .tp_doc = pyrf_thread_map__doc, | 477 | .tp_doc = pyrf_thread_map__doc, |
369 | .tp_as_sequence = &pyrf_thread_map__sequence_methods, | 478 | .tp_as_sequence = &pyrf_thread_map__sequence_methods, |
370 | .tp_init = (initproc)pyrf_thread_map__init, | 479 | .tp_init = (initproc)pyrf_thread_map__init, |
371 | }; | 480 | }; |
372 | 481 | ||
373 | static int pyrf_thread_map__setup_types(void) | 482 | static int pyrf_thread_map__setup_types(void) |
374 | { | 483 | { |
375 | pyrf_thread_map__type.tp_new = PyType_GenericNew; | 484 | pyrf_thread_map__type.tp_new = PyType_GenericNew; |
376 | return PyType_Ready(&pyrf_thread_map__type); | 485 | return PyType_Ready(&pyrf_thread_map__type); |
377 | } | 486 | } |
378 | 487 | ||
379 | struct pyrf_evsel { | 488 | struct pyrf_evsel { |
380 | PyObject_HEAD | 489 | PyObject_HEAD |
381 | 490 | ||
382 | struct perf_evsel evsel; | 491 | struct perf_evsel evsel; |
383 | }; | 492 | }; |
384 | 493 | ||
385 | static int pyrf_evsel__init(struct pyrf_evsel *pevsel, | 494 | static int pyrf_evsel__init(struct pyrf_evsel *pevsel, |
386 | PyObject *args, PyObject *kwargs) | 495 | PyObject *args, PyObject *kwargs) |
387 | { | 496 | { |
388 | struct perf_event_attr attr = { | 497 | struct perf_event_attr attr = { |
389 | .type = PERF_TYPE_HARDWARE, | 498 | .type = PERF_TYPE_HARDWARE, |
390 | .config = PERF_COUNT_HW_CPU_CYCLES, | 499 | .config = PERF_COUNT_HW_CPU_CYCLES, |
391 | .sample_type = PERF_SAMPLE_PERIOD | PERF_SAMPLE_TID, | 500 | .sample_type = PERF_SAMPLE_PERIOD | PERF_SAMPLE_TID, |
392 | }; | 501 | }; |
393 | static char *kwlist[] = { | 502 | static char *kwlist[] = { |
394 | "type", | 503 | "type", |
395 | "config", | 504 | "config", |
396 | "sample_freq", | 505 | "sample_freq", |
397 | "sample_period", | 506 | "sample_period", |
398 | "sample_type", | 507 | "sample_type", |
399 | "read_format", | 508 | "read_format", |
400 | "disabled", | 509 | "disabled", |
401 | "inherit", | 510 | "inherit", |
402 | "pinned", | 511 | "pinned", |
403 | "exclusive", | 512 | "exclusive", |
404 | "exclude_user", | 513 | "exclude_user", |
405 | "exclude_kernel", | 514 | "exclude_kernel", |
406 | "exclude_hv", | 515 | "exclude_hv", |
407 | "exclude_idle", | 516 | "exclude_idle", |
408 | "mmap", | 517 | "mmap", |
409 | "comm", | 518 | "comm", |
410 | "freq", | 519 | "freq", |
411 | "inherit_stat", | 520 | "inherit_stat", |
412 | "enable_on_exec", | 521 | "enable_on_exec", |
413 | "task", | 522 | "task", |
414 | "watermark", | 523 | "watermark", |
415 | "precise_ip", | 524 | "precise_ip", |
416 | "mmap_data", | 525 | "mmap_data", |
417 | "sample_id_all", | 526 | "sample_id_all", |
418 | "wakeup_events", | 527 | "wakeup_events", |
419 | "bp_type", | 528 | "bp_type", |
420 | "bp_addr", | 529 | "bp_addr", |
421 | "bp_len", | 530 | "bp_len", |
422 | NULL | 531 | NULL |
423 | }; | 532 | }; |
424 | u64 sample_period = 0; | 533 | u64 sample_period = 0; |
425 | u32 disabled = 0, | 534 | u32 disabled = 0, |
426 | inherit = 0, | 535 | inherit = 0, |
427 | pinned = 0, | 536 | pinned = 0, |
428 | exclusive = 0, | 537 | exclusive = 0, |
429 | exclude_user = 0, | 538 | exclude_user = 0, |
430 | exclude_kernel = 0, | 539 | exclude_kernel = 0, |
431 | exclude_hv = 0, | 540 | exclude_hv = 0, |
432 | exclude_idle = 0, | 541 | exclude_idle = 0, |
433 | mmap = 0, | 542 | mmap = 0, |
434 | comm = 0, | 543 | comm = 0, |
435 | freq = 1, | 544 | freq = 1, |
436 | inherit_stat = 0, | 545 | inherit_stat = 0, |
437 | enable_on_exec = 0, | 546 | enable_on_exec = 0, |
438 | task = 0, | 547 | task = 0, |
439 | watermark = 0, | 548 | watermark = 0, |
440 | precise_ip = 0, | 549 | precise_ip = 0, |
441 | mmap_data = 0, | 550 | mmap_data = 0, |
442 | sample_id_all = 1; | 551 | sample_id_all = 1; |
443 | int idx = 0; | 552 | int idx = 0; |
444 | 553 | ||
445 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, | 554 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, |
446 | "|iKiKKiiiiiiiiiiiiiiiiiiiiiKK", kwlist, | 555 | "|iKiKKiiiiiiiiiiiiiiiiiiiiiKK", kwlist, |
447 | &attr.type, &attr.config, &attr.sample_freq, | 556 | &attr.type, &attr.config, &attr.sample_freq, |
448 | &sample_period, &attr.sample_type, | 557 | &sample_period, &attr.sample_type, |
449 | &attr.read_format, &disabled, &inherit, | 558 | &attr.read_format, &disabled, &inherit, |
450 | &pinned, &exclusive, &exclude_user, | 559 | &pinned, &exclusive, &exclude_user, |
451 | &exclude_kernel, &exclude_hv, &exclude_idle, | 560 | &exclude_kernel, &exclude_hv, &exclude_idle, |
452 | &mmap, &comm, &freq, &inherit_stat, | 561 | &mmap, &comm, &freq, &inherit_stat, |
453 | &enable_on_exec, &task, &watermark, | 562 | &enable_on_exec, &task, &watermark, |
454 | &precise_ip, &mmap_data, &sample_id_all, | 563 | &precise_ip, &mmap_data, &sample_id_all, |
455 | &attr.wakeup_events, &attr.bp_type, | 564 | &attr.wakeup_events, &attr.bp_type, |
456 | &attr.bp_addr, &attr.bp_len, &idx)) | 565 | &attr.bp_addr, &attr.bp_len, &idx)) |
457 | return -1; | 566 | return -1; |
458 | 567 | ||
459 | /* union... */ | 568 | /* union... */ |
460 | if (sample_period != 0) { | 569 | if (sample_period != 0) { |
461 | if (attr.sample_freq != 0) | 570 | if (attr.sample_freq != 0) |
462 | return -1; /* FIXME: throw right exception */ | 571 | return -1; /* FIXME: throw right exception */ |
463 | attr.sample_period = sample_period; | 572 | attr.sample_period = sample_period; |
464 | } | 573 | } |
465 | 574 | ||
466 | /* Bitfields */ | 575 | /* Bitfields */ |
467 | attr.disabled = disabled; | 576 | attr.disabled = disabled; |
468 | attr.inherit = inherit; | 577 | attr.inherit = inherit; |
469 | attr.pinned = pinned; | 578 | attr.pinned = pinned; |
470 | attr.exclusive = exclusive; | 579 | attr.exclusive = exclusive; |
471 | attr.exclude_user = exclude_user; | 580 | attr.exclude_user = exclude_user; |
472 | attr.exclude_kernel = exclude_kernel; | 581 | attr.exclude_kernel = exclude_kernel; |
473 | attr.exclude_hv = exclude_hv; | 582 | attr.exclude_hv = exclude_hv; |
474 | attr.exclude_idle = exclude_idle; | 583 | attr.exclude_idle = exclude_idle; |
475 | attr.mmap = mmap; | 584 | attr.mmap = mmap; |
476 | attr.comm = comm; | 585 | attr.comm = comm; |
477 | attr.freq = freq; | 586 | attr.freq = freq; |
478 | attr.inherit_stat = inherit_stat; | 587 | attr.inherit_stat = inherit_stat; |
479 | attr.enable_on_exec = enable_on_exec; | 588 | attr.enable_on_exec = enable_on_exec; |
480 | attr.task = task; | 589 | attr.task = task; |
481 | attr.watermark = watermark; | 590 | attr.watermark = watermark; |
482 | attr.precise_ip = precise_ip; | 591 | attr.precise_ip = precise_ip; |
483 | attr.mmap_data = mmap_data; | 592 | attr.mmap_data = mmap_data; |
484 | attr.sample_id_all = sample_id_all; | 593 | attr.sample_id_all = sample_id_all; |
485 | 594 | ||
486 | perf_evsel__init(&pevsel->evsel, &attr, idx); | 595 | perf_evsel__init(&pevsel->evsel, &attr, idx); |
487 | return 0; | 596 | return 0; |
488 | } | 597 | } |
489 | 598 | ||
490 | static void pyrf_evsel__delete(struct pyrf_evsel *pevsel) | 599 | static void pyrf_evsel__delete(struct pyrf_evsel *pevsel) |
491 | { | 600 | { |
492 | perf_evsel__exit(&pevsel->evsel); | 601 | perf_evsel__exit(&pevsel->evsel); |
493 | pevsel->ob_type->tp_free((PyObject*)pevsel); | 602 | pevsel->ob_type->tp_free((PyObject*)pevsel); |
494 | } | 603 | } |
495 | 604 | ||
496 | static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | 605 | static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, |
497 | PyObject *args, PyObject *kwargs) | 606 | PyObject *args, PyObject *kwargs) |
498 | { | 607 | { |
499 | struct perf_evsel *evsel = &pevsel->evsel; | 608 | struct perf_evsel *evsel = &pevsel->evsel; |
500 | struct cpu_map *cpus = NULL; | 609 | struct cpu_map *cpus = NULL; |
501 | struct thread_map *threads = NULL; | 610 | struct thread_map *threads = NULL; |
502 | PyObject *pcpus = NULL, *pthreads = NULL; | 611 | PyObject *pcpus = NULL, *pthreads = NULL; |
503 | int group = 0, inherit = 0; | 612 | int group = 0, inherit = 0; |
504 | static char *kwlist[] = { "cpus", "threads", "group", "inherit", NULL }; | 613 | static char *kwlist[] = { "cpus", "threads", "group", "inherit", NULL }; |
505 | 614 | ||
506 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, | 615 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, |
507 | &pcpus, &pthreads, &group, &inherit)) | 616 | &pcpus, &pthreads, &group, &inherit)) |
508 | return NULL; | 617 | return NULL; |
509 | 618 | ||
510 | if (pthreads != NULL) | 619 | if (pthreads != NULL) |
511 | threads = ((struct pyrf_thread_map *)pthreads)->threads; | 620 | threads = ((struct pyrf_thread_map *)pthreads)->threads; |
512 | 621 | ||
513 | if (pcpus != NULL) | 622 | if (pcpus != NULL) |
514 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; | 623 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; |
515 | 624 | ||
516 | evsel->attr.inherit = inherit; | 625 | evsel->attr.inherit = inherit; |
517 | if (perf_evsel__open(evsel, cpus, threads, group) < 0) { | 626 | if (perf_evsel__open(evsel, cpus, threads, group) < 0) { |
518 | PyErr_SetFromErrno(PyExc_OSError); | 627 | PyErr_SetFromErrno(PyExc_OSError); |
519 | return NULL; | 628 | return NULL; |
520 | } | 629 | } |
521 | 630 | ||
522 | Py_INCREF(Py_None); | 631 | Py_INCREF(Py_None); |
523 | return Py_None; | 632 | return Py_None; |
524 | } | 633 | } |
525 | 634 | ||
526 | static PyMethodDef pyrf_evsel__methods[] = { | 635 | static PyMethodDef pyrf_evsel__methods[] = { |
527 | { | 636 | { |
528 | .ml_name = "open", | 637 | .ml_name = "open", |
529 | .ml_meth = (PyCFunction)pyrf_evsel__open, | 638 | .ml_meth = (PyCFunction)pyrf_evsel__open, |
530 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | 639 | .ml_flags = METH_VARARGS | METH_KEYWORDS, |
531 | .ml_doc = PyDoc_STR("open the event selector file descriptor table.") | 640 | .ml_doc = PyDoc_STR("open the event selector file descriptor table.") |
532 | }, | 641 | }, |
533 | { .ml_name = NULL, } | 642 | { .ml_name = NULL, } |
534 | }; | 643 | }; |
535 | 644 | ||
536 | static char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object."); | 645 | static char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object."); |
537 | 646 | ||
538 | static PyTypeObject pyrf_evsel__type = { | 647 | static PyTypeObject pyrf_evsel__type = { |
539 | PyVarObject_HEAD_INIT(NULL, 0) | 648 | PyVarObject_HEAD_INIT(NULL, 0) |
540 | .tp_name = "perf.evsel", | 649 | .tp_name = "perf.evsel", |
541 | .tp_basicsize = sizeof(struct pyrf_evsel), | 650 | .tp_basicsize = sizeof(struct pyrf_evsel), |
542 | .tp_dealloc = (destructor)pyrf_evsel__delete, | 651 | .tp_dealloc = (destructor)pyrf_evsel__delete, |
543 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | 652 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, |
544 | .tp_doc = pyrf_evsel__doc, | 653 | .tp_doc = pyrf_evsel__doc, |
545 | .tp_methods = pyrf_evsel__methods, | 654 | .tp_methods = pyrf_evsel__methods, |
546 | .tp_init = (initproc)pyrf_evsel__init, | 655 | .tp_init = (initproc)pyrf_evsel__init, |
547 | }; | 656 | }; |
548 | 657 | ||
549 | static int pyrf_evsel__setup_types(void) | 658 | static int pyrf_evsel__setup_types(void) |
550 | { | 659 | { |
551 | pyrf_evsel__type.tp_new = PyType_GenericNew; | 660 | pyrf_evsel__type.tp_new = PyType_GenericNew; |
552 | return PyType_Ready(&pyrf_evsel__type); | 661 | return PyType_Ready(&pyrf_evsel__type); |
553 | } | 662 | } |
554 | 663 | ||
555 | struct pyrf_evlist { | 664 | struct pyrf_evlist { |
556 | PyObject_HEAD | 665 | PyObject_HEAD |
557 | 666 | ||
558 | struct perf_evlist evlist; | 667 | struct perf_evlist evlist; |
559 | }; | 668 | }; |
560 | 669 | ||
561 | static int pyrf_evlist__init(struct pyrf_evlist *pevlist, | 670 | static int pyrf_evlist__init(struct pyrf_evlist *pevlist, |
562 | PyObject *args, PyObject *kwargs __used) | 671 | PyObject *args, PyObject *kwargs __used) |
563 | { | 672 | { |
564 | PyObject *pcpus = NULL, *pthreads = NULL; | 673 | PyObject *pcpus = NULL, *pthreads = NULL; |
565 | struct cpu_map *cpus; | 674 | struct cpu_map *cpus; |
566 | struct thread_map *threads; | 675 | struct thread_map *threads; |
567 | 676 | ||
568 | if (!PyArg_ParseTuple(args, "OO", &pcpus, &pthreads)) | 677 | if (!PyArg_ParseTuple(args, "OO", &pcpus, &pthreads)) |
569 | return -1; | 678 | return -1; |
570 | 679 | ||
571 | threads = ((struct pyrf_thread_map *)pthreads)->threads; | 680 | threads = ((struct pyrf_thread_map *)pthreads)->threads; |
572 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; | 681 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; |
573 | perf_evlist__init(&pevlist->evlist, cpus, threads); | 682 | perf_evlist__init(&pevlist->evlist, cpus, threads); |
574 | return 0; | 683 | return 0; |
575 | } | 684 | } |
576 | 685 | ||
577 | static void pyrf_evlist__delete(struct pyrf_evlist *pevlist) | 686 | static void pyrf_evlist__delete(struct pyrf_evlist *pevlist) |
578 | { | 687 | { |
579 | perf_evlist__exit(&pevlist->evlist); | 688 | perf_evlist__exit(&pevlist->evlist); |
580 | pevlist->ob_type->tp_free((PyObject*)pevlist); | 689 | pevlist->ob_type->tp_free((PyObject*)pevlist); |
581 | } | 690 | } |
582 | 691 | ||
583 | static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, | 692 | static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, |
584 | PyObject *args, PyObject *kwargs) | 693 | PyObject *args, PyObject *kwargs) |
585 | { | 694 | { |
586 | struct perf_evlist *evlist = &pevlist->evlist; | 695 | struct perf_evlist *evlist = &pevlist->evlist; |
587 | static char *kwlist[] = { "pages", "overwrite", NULL }; | 696 | static char *kwlist[] = { "pages", "overwrite", NULL }; |
588 | int pages = 128, overwrite = false; | 697 | int pages = 128, overwrite = false; |
589 | 698 | ||
590 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, | 699 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, |
591 | &pages, &overwrite)) | 700 | &pages, &overwrite)) |
592 | return NULL; | 701 | return NULL; |
593 | 702 | ||
594 | if (perf_evlist__mmap(evlist, pages, overwrite) < 0) { | 703 | if (perf_evlist__mmap(evlist, pages, overwrite) < 0) { |
595 | PyErr_SetFromErrno(PyExc_OSError); | 704 | PyErr_SetFromErrno(PyExc_OSError); |
596 | return NULL; | 705 | return NULL; |
597 | } | 706 | } |
598 | 707 | ||
599 | Py_INCREF(Py_None); | 708 | Py_INCREF(Py_None); |
600 | return Py_None; | 709 | return Py_None; |
601 | } | 710 | } |
602 | 711 | ||
603 | static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | 712 | static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, |
604 | PyObject *args, PyObject *kwargs) | 713 | PyObject *args, PyObject *kwargs) |
605 | { | 714 | { |
606 | struct perf_evlist *evlist = &pevlist->evlist; | 715 | struct perf_evlist *evlist = &pevlist->evlist; |
607 | static char *kwlist[] = { "timeout", NULL }; | 716 | static char *kwlist[] = { "timeout", NULL }; |
608 | int timeout = -1, n; | 717 | int timeout = -1, n; |
609 | 718 | ||
610 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | 719 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) |
611 | return NULL; | 720 | return NULL; |
612 | 721 | ||
613 | n = poll(evlist->pollfd, evlist->nr_fds, timeout); | 722 | n = poll(evlist->pollfd, evlist->nr_fds, timeout); |
614 | if (n < 0) { | 723 | if (n < 0) { |
615 | PyErr_SetFromErrno(PyExc_OSError); | 724 | PyErr_SetFromErrno(PyExc_OSError); |
616 | return NULL; | 725 | return NULL; |
617 | } | 726 | } |
618 | 727 | ||
619 | return Py_BuildValue("i", n); | 728 | return Py_BuildValue("i", n); |
620 | } | 729 | } |
621 | 730 | ||
622 | static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, | 731 | static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, |
623 | PyObject *args __used, PyObject *kwargs __used) | 732 | PyObject *args __used, PyObject *kwargs __used) |
624 | { | 733 | { |
625 | struct perf_evlist *evlist = &pevlist->evlist; | 734 | struct perf_evlist *evlist = &pevlist->evlist; |
626 | PyObject *list = PyList_New(0); | 735 | PyObject *list = PyList_New(0); |
627 | int i; | 736 | int i; |
628 | 737 | ||
629 | for (i = 0; i < evlist->nr_fds; ++i) { | 738 | for (i = 0; i < evlist->nr_fds; ++i) { |
630 | PyObject *file; | 739 | PyObject *file; |
631 | FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); | 740 | FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); |
632 | 741 | ||
633 | if (fp == NULL) | 742 | if (fp == NULL) |
634 | goto free_list; | 743 | goto free_list; |
635 | 744 | ||
636 | file = PyFile_FromFile(fp, "perf", "r", NULL); | 745 | file = PyFile_FromFile(fp, "perf", "r", NULL); |
637 | if (file == NULL) | 746 | if (file == NULL) |
638 | goto free_list; | 747 | goto free_list; |
639 | 748 | ||
640 | if (PyList_Append(list, file) != 0) { | 749 | if (PyList_Append(list, file) != 0) { |
641 | Py_DECREF(file); | 750 | Py_DECREF(file); |
642 | goto free_list; | 751 | goto free_list; |
643 | } | 752 | } |
644 | 753 | ||
645 | Py_DECREF(file); | 754 | Py_DECREF(file); |
646 | } | 755 | } |
647 | 756 | ||
648 | return list; | 757 | return list; |
649 | free_list: | 758 | free_list: |
650 | return PyErr_NoMemory(); | 759 | return PyErr_NoMemory(); |
651 | } | 760 | } |
652 | 761 | ||
653 | 762 | ||
654 | static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, | 763 | static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, |
655 | PyObject *args, PyObject *kwargs __used) | 764 | PyObject *args, PyObject *kwargs __used) |
656 | { | 765 | { |
657 | struct perf_evlist *evlist = &pevlist->evlist; | 766 | struct perf_evlist *evlist = &pevlist->evlist; |
658 | PyObject *pevsel; | 767 | PyObject *pevsel; |
659 | struct perf_evsel *evsel; | 768 | struct perf_evsel *evsel; |
660 | 769 | ||
661 | if (!PyArg_ParseTuple(args, "O", &pevsel)) | 770 | if (!PyArg_ParseTuple(args, "O", &pevsel)) |
662 | return NULL; | 771 | return NULL; |
663 | 772 | ||
664 | Py_INCREF(pevsel); | 773 | Py_INCREF(pevsel); |
665 | evsel = &((struct pyrf_evsel *)pevsel)->evsel; | 774 | evsel = &((struct pyrf_evsel *)pevsel)->evsel; |
666 | evsel->idx = evlist->nr_entries; | 775 | evsel->idx = evlist->nr_entries; |
667 | perf_evlist__add(evlist, evsel); | 776 | perf_evlist__add(evlist, evsel); |
668 | 777 | ||
669 | return Py_BuildValue("i", evlist->nr_entries); | 778 | return Py_BuildValue("i", evlist->nr_entries); |
670 | } | 779 | } |
671 | 780 | ||
672 | static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | 781 | static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, |
673 | PyObject *args, PyObject *kwargs) | 782 | PyObject *args, PyObject *kwargs) |
674 | { | 783 | { |
675 | struct perf_evlist *evlist = &pevlist->evlist; | 784 | struct perf_evlist *evlist = &pevlist->evlist; |
676 | union perf_event *event; | 785 | union perf_event *event; |
677 | int sample_id_all = 1, cpu; | 786 | int sample_id_all = 1, cpu; |
678 | static char *kwlist[] = { "cpu", "sample_id_all", NULL }; | 787 | static char *kwlist[] = { "cpu", "sample_id_all", NULL }; |
679 | int err; | 788 | int err; |
680 | 789 | ||
681 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, | 790 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, |
682 | &cpu, &sample_id_all)) | 791 | &cpu, &sample_id_all)) |
683 | return NULL; | 792 | return NULL; |
684 | 793 | ||
685 | event = perf_evlist__mmap_read(evlist, cpu); | 794 | event = perf_evlist__mmap_read(evlist, cpu); |
686 | if (event != NULL) { | 795 | if (event != NULL) { |
687 | struct perf_evsel *first; | 796 | struct perf_evsel *first; |
688 | PyObject *pyevent = pyrf_event__new(event); | 797 | PyObject *pyevent = pyrf_event__new(event); |
689 | struct pyrf_event *pevent = (struct pyrf_event *)pyevent; | 798 | struct pyrf_event *pevent = (struct pyrf_event *)pyevent; |
690 | 799 | ||
691 | if (pyevent == NULL) | 800 | if (pyevent == NULL) |
692 | return PyErr_NoMemory(); | 801 | return PyErr_NoMemory(); |
693 | 802 | ||
694 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 803 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
695 | err = perf_event__parse_sample(event, first->attr.sample_type, | 804 | err = perf_event__parse_sample(event, first->attr.sample_type, |
696 | perf_evsel__sample_size(first), | 805 | perf_evsel__sample_size(first), |
697 | sample_id_all, &pevent->sample); | 806 | sample_id_all, &pevent->sample); |
698 | if (err) | 807 | if (err) |
699 | return PyErr_Format(PyExc_OSError, | 808 | return PyErr_Format(PyExc_OSError, |
700 | "perf: can't parse sample, err=%d", err); | 809 | "perf: can't parse sample, err=%d", err); |
701 | return pyevent; | 810 | return pyevent; |
702 | } | 811 | } |
703 | 812 | ||
704 | Py_INCREF(Py_None); | 813 | Py_INCREF(Py_None); |
705 | return Py_None; | 814 | return Py_None; |
706 | } | 815 | } |
707 | 816 | ||
708 | static PyMethodDef pyrf_evlist__methods[] = { | 817 | static PyMethodDef pyrf_evlist__methods[] = { |
709 | { | 818 | { |
710 | .ml_name = "mmap", | 819 | .ml_name = "mmap", |
711 | .ml_meth = (PyCFunction)pyrf_evlist__mmap, | 820 | .ml_meth = (PyCFunction)pyrf_evlist__mmap, |
712 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | 821 | .ml_flags = METH_VARARGS | METH_KEYWORDS, |
713 | .ml_doc = PyDoc_STR("mmap the file descriptor table.") | 822 | .ml_doc = PyDoc_STR("mmap the file descriptor table.") |
714 | }, | 823 | }, |
715 | { | 824 | { |
716 | .ml_name = "poll", | 825 | .ml_name = "poll", |
717 | .ml_meth = (PyCFunction)pyrf_evlist__poll, | 826 | .ml_meth = (PyCFunction)pyrf_evlist__poll, |
718 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | 827 | .ml_flags = METH_VARARGS | METH_KEYWORDS, |
719 | .ml_doc = PyDoc_STR("poll the file descriptor table.") | 828 | .ml_doc = PyDoc_STR("poll the file descriptor table.") |
720 | }, | 829 | }, |
721 | { | 830 | { |
722 | .ml_name = "get_pollfd", | 831 | .ml_name = "get_pollfd", |
723 | .ml_meth = (PyCFunction)pyrf_evlist__get_pollfd, | 832 | .ml_meth = (PyCFunction)pyrf_evlist__get_pollfd, |
724 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | 833 | .ml_flags = METH_VARARGS | METH_KEYWORDS, |
725 | .ml_doc = PyDoc_STR("get the poll file descriptor table.") | 834 | .ml_doc = PyDoc_STR("get the poll file descriptor table.") |
726 | }, | 835 | }, |
727 | { | 836 | { |
728 | .ml_name = "add", | 837 | .ml_name = "add", |
729 | .ml_meth = (PyCFunction)pyrf_evlist__add, | 838 | .ml_meth = (PyCFunction)pyrf_evlist__add, |
730 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | 839 | .ml_flags = METH_VARARGS | METH_KEYWORDS, |
731 | .ml_doc = PyDoc_STR("adds an event selector to the list.") | 840 | .ml_doc = PyDoc_STR("adds an event selector to the list.") |
732 | }, | 841 | }, |
733 | { | 842 | { |
734 | .ml_name = "read_on_cpu", | 843 | .ml_name = "read_on_cpu", |
735 | .ml_meth = (PyCFunction)pyrf_evlist__read_on_cpu, | 844 | .ml_meth = (PyCFunction)pyrf_evlist__read_on_cpu, |
736 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | 845 | .ml_flags = METH_VARARGS | METH_KEYWORDS, |
737 | .ml_doc = PyDoc_STR("reads an event.") | 846 | .ml_doc = PyDoc_STR("reads an event.") |
738 | }, | 847 | }, |
739 | { .ml_name = NULL, } | 848 | { .ml_name = NULL, } |
740 | }; | 849 | }; |
741 | 850 | ||
742 | static Py_ssize_t pyrf_evlist__length(PyObject *obj) | 851 | static Py_ssize_t pyrf_evlist__length(PyObject *obj) |
743 | { | 852 | { |
744 | struct pyrf_evlist *pevlist = (void *)obj; | 853 | struct pyrf_evlist *pevlist = (void *)obj; |
745 | 854 | ||
746 | return pevlist->evlist.nr_entries; | 855 | return pevlist->evlist.nr_entries; |
747 | } | 856 | } |
748 | 857 | ||
749 | static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) | 858 | static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) |
750 | { | 859 | { |
751 | struct pyrf_evlist *pevlist = (void *)obj; | 860 | struct pyrf_evlist *pevlist = (void *)obj; |
752 | struct perf_evsel *pos; | 861 | struct perf_evsel *pos; |
753 | 862 | ||
754 | if (i >= pevlist->evlist.nr_entries) | 863 | if (i >= pevlist->evlist.nr_entries) |
755 | return NULL; | 864 | return NULL; |
756 | 865 | ||
757 | list_for_each_entry(pos, &pevlist->evlist.entries, node) | 866 | list_for_each_entry(pos, &pevlist->evlist.entries, node) |
758 | if (i-- == 0) | 867 | if (i-- == 0) |
759 | break; | 868 | break; |
760 | 869 | ||
761 | return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); | 870 | return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); |
762 | } | 871 | } |
763 | 872 | ||
764 | static PySequenceMethods pyrf_evlist__sequence_methods = { | 873 | static PySequenceMethods pyrf_evlist__sequence_methods = { |
765 | .sq_length = pyrf_evlist__length, | 874 | .sq_length = pyrf_evlist__length, |
766 | .sq_item = pyrf_evlist__item, | 875 | .sq_item = pyrf_evlist__item, |
767 | }; | 876 | }; |
768 | 877 | ||
769 | static char pyrf_evlist__doc[] = PyDoc_STR("perf event selector list object."); | 878 | static char pyrf_evlist__doc[] = PyDoc_STR("perf event selector list object."); |
770 | 879 | ||
771 | static PyTypeObject pyrf_evlist__type = { | 880 | static PyTypeObject pyrf_evlist__type = { |
772 | PyVarObject_HEAD_INIT(NULL, 0) | 881 | PyVarObject_HEAD_INIT(NULL, 0) |
773 | .tp_name = "perf.evlist", | 882 | .tp_name = "perf.evlist", |
774 | .tp_basicsize = sizeof(struct pyrf_evlist), | 883 | .tp_basicsize = sizeof(struct pyrf_evlist), |
775 | .tp_dealloc = (destructor)pyrf_evlist__delete, | 884 | .tp_dealloc = (destructor)pyrf_evlist__delete, |
776 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | 885 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, |
777 | .tp_as_sequence = &pyrf_evlist__sequence_methods, | 886 | .tp_as_sequence = &pyrf_evlist__sequence_methods, |
778 | .tp_doc = pyrf_evlist__doc, | 887 | .tp_doc = pyrf_evlist__doc, |
779 | .tp_methods = pyrf_evlist__methods, | 888 | .tp_methods = pyrf_evlist__methods, |
780 | .tp_init = (initproc)pyrf_evlist__init, | 889 | .tp_init = (initproc)pyrf_evlist__init, |
781 | }; | 890 | }; |
782 | 891 | ||
783 | static int pyrf_evlist__setup_types(void) | 892 | static int pyrf_evlist__setup_types(void) |
784 | { | 893 | { |
785 | pyrf_evlist__type.tp_new = PyType_GenericNew; | 894 | pyrf_evlist__type.tp_new = PyType_GenericNew; |
786 | return PyType_Ready(&pyrf_evlist__type); | 895 | return PyType_Ready(&pyrf_evlist__type); |
787 | } | 896 | } |
788 | 897 | ||
789 | static struct { | 898 | static struct { |
790 | const char *name; | 899 | const char *name; |
791 | int value; | 900 | int value; |
792 | } perf__constants[] = { | 901 | } perf__constants[] = { |
793 | { "TYPE_HARDWARE", PERF_TYPE_HARDWARE }, | 902 | { "TYPE_HARDWARE", PERF_TYPE_HARDWARE }, |
794 | { "TYPE_SOFTWARE", PERF_TYPE_SOFTWARE }, | 903 | { "TYPE_SOFTWARE", PERF_TYPE_SOFTWARE }, |
795 | { "TYPE_TRACEPOINT", PERF_TYPE_TRACEPOINT }, | 904 | { "TYPE_TRACEPOINT", PERF_TYPE_TRACEPOINT }, |
796 | { "TYPE_HW_CACHE", PERF_TYPE_HW_CACHE }, | 905 | { "TYPE_HW_CACHE", PERF_TYPE_HW_CACHE }, |
797 | { "TYPE_RAW", PERF_TYPE_RAW }, | 906 | { "TYPE_RAW", PERF_TYPE_RAW }, |
798 | { "TYPE_BREAKPOINT", PERF_TYPE_BREAKPOINT }, | 907 | { "TYPE_BREAKPOINT", PERF_TYPE_BREAKPOINT }, |
799 | 908 | ||
800 | { "COUNT_HW_CPU_CYCLES", PERF_COUNT_HW_CPU_CYCLES }, | 909 | { "COUNT_HW_CPU_CYCLES", PERF_COUNT_HW_CPU_CYCLES }, |
801 | { "COUNT_HW_INSTRUCTIONS", PERF_COUNT_HW_INSTRUCTIONS }, | 910 | { "COUNT_HW_INSTRUCTIONS", PERF_COUNT_HW_INSTRUCTIONS }, |
802 | { "COUNT_HW_CACHE_REFERENCES", PERF_COUNT_HW_CACHE_REFERENCES }, | 911 | { "COUNT_HW_CACHE_REFERENCES", PERF_COUNT_HW_CACHE_REFERENCES }, |
803 | { "COUNT_HW_CACHE_MISSES", PERF_COUNT_HW_CACHE_MISSES }, | 912 | { "COUNT_HW_CACHE_MISSES", PERF_COUNT_HW_CACHE_MISSES }, |
804 | { "COUNT_HW_BRANCH_INSTRUCTIONS", PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, | 913 | { "COUNT_HW_BRANCH_INSTRUCTIONS", PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, |
805 | { "COUNT_HW_BRANCH_MISSES", PERF_COUNT_HW_BRANCH_MISSES }, | 914 | { "COUNT_HW_BRANCH_MISSES", PERF_COUNT_HW_BRANCH_MISSES }, |
806 | { "COUNT_HW_BUS_CYCLES", PERF_COUNT_HW_BUS_CYCLES }, | 915 | { "COUNT_HW_BUS_CYCLES", PERF_COUNT_HW_BUS_CYCLES }, |
807 | { "COUNT_HW_CACHE_L1D", PERF_COUNT_HW_CACHE_L1D }, | 916 | { "COUNT_HW_CACHE_L1D", PERF_COUNT_HW_CACHE_L1D }, |
808 | { "COUNT_HW_CACHE_L1I", PERF_COUNT_HW_CACHE_L1I }, | 917 | { "COUNT_HW_CACHE_L1I", PERF_COUNT_HW_CACHE_L1I }, |
809 | { "COUNT_HW_CACHE_LL", PERF_COUNT_HW_CACHE_LL }, | 918 | { "COUNT_HW_CACHE_LL", PERF_COUNT_HW_CACHE_LL }, |
810 | { "COUNT_HW_CACHE_DTLB", PERF_COUNT_HW_CACHE_DTLB }, | 919 | { "COUNT_HW_CACHE_DTLB", PERF_COUNT_HW_CACHE_DTLB }, |
811 | { "COUNT_HW_CACHE_ITLB", PERF_COUNT_HW_CACHE_ITLB }, | 920 | { "COUNT_HW_CACHE_ITLB", PERF_COUNT_HW_CACHE_ITLB }, |
812 | { "COUNT_HW_CACHE_BPU", PERF_COUNT_HW_CACHE_BPU }, | 921 | { "COUNT_HW_CACHE_BPU", PERF_COUNT_HW_CACHE_BPU }, |
813 | { "COUNT_HW_CACHE_OP_READ", PERF_COUNT_HW_CACHE_OP_READ }, | 922 | { "COUNT_HW_CACHE_OP_READ", PERF_COUNT_HW_CACHE_OP_READ }, |
814 | { "COUNT_HW_CACHE_OP_WRITE", PERF_COUNT_HW_CACHE_OP_WRITE }, | 923 | { "COUNT_HW_CACHE_OP_WRITE", PERF_COUNT_HW_CACHE_OP_WRITE }, |
815 | { "COUNT_HW_CACHE_OP_PREFETCH", PERF_COUNT_HW_CACHE_OP_PREFETCH }, | 924 | { "COUNT_HW_CACHE_OP_PREFETCH", PERF_COUNT_HW_CACHE_OP_PREFETCH }, |
816 | { "COUNT_HW_CACHE_RESULT_ACCESS", PERF_COUNT_HW_CACHE_RESULT_ACCESS }, | 925 | { "COUNT_HW_CACHE_RESULT_ACCESS", PERF_COUNT_HW_CACHE_RESULT_ACCESS }, |
817 | { "COUNT_HW_CACHE_RESULT_MISS", PERF_COUNT_HW_CACHE_RESULT_MISS }, | 926 | { "COUNT_HW_CACHE_RESULT_MISS", PERF_COUNT_HW_CACHE_RESULT_MISS }, |
818 | 927 | ||
819 | { "COUNT_HW_STALLED_CYCLES_FRONTEND", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, | 928 | { "COUNT_HW_STALLED_CYCLES_FRONTEND", PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, |
820 | { "COUNT_HW_STALLED_CYCLES_BACKEND", PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, | 929 | { "COUNT_HW_STALLED_CYCLES_BACKEND", PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, |
821 | 930 | ||
822 | { "COUNT_SW_CPU_CLOCK", PERF_COUNT_SW_CPU_CLOCK }, | 931 | { "COUNT_SW_CPU_CLOCK", PERF_COUNT_SW_CPU_CLOCK }, |
823 | { "COUNT_SW_TASK_CLOCK", PERF_COUNT_SW_TASK_CLOCK }, | 932 | { "COUNT_SW_TASK_CLOCK", PERF_COUNT_SW_TASK_CLOCK }, |
824 | { "COUNT_SW_PAGE_FAULTS", PERF_COUNT_SW_PAGE_FAULTS }, | 933 | { "COUNT_SW_PAGE_FAULTS", PERF_COUNT_SW_PAGE_FAULTS }, |
825 | { "COUNT_SW_CONTEXT_SWITCHES", PERF_COUNT_SW_CONTEXT_SWITCHES }, | 934 | { "COUNT_SW_CONTEXT_SWITCHES", PERF_COUNT_SW_CONTEXT_SWITCHES }, |
826 | { "COUNT_SW_CPU_MIGRATIONS", PERF_COUNT_SW_CPU_MIGRATIONS }, | 935 | { "COUNT_SW_CPU_MIGRATIONS", PERF_COUNT_SW_CPU_MIGRATIONS }, |
827 | { "COUNT_SW_PAGE_FAULTS_MIN", PERF_COUNT_SW_PAGE_FAULTS_MIN }, | 936 | { "COUNT_SW_PAGE_FAULTS_MIN", PERF_COUNT_SW_PAGE_FAULTS_MIN }, |
828 | { "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ }, | 937 | { "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ }, |
829 | { "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS }, | 938 | { "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS }, |
830 | { "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS }, | 939 | { "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS }, |
831 | 940 | ||
832 | { "SAMPLE_IP", PERF_SAMPLE_IP }, | 941 | { "SAMPLE_IP", PERF_SAMPLE_IP }, |
833 | { "SAMPLE_TID", PERF_SAMPLE_TID }, | 942 | { "SAMPLE_TID", PERF_SAMPLE_TID }, |
834 | { "SAMPLE_TIME", PERF_SAMPLE_TIME }, | 943 | { "SAMPLE_TIME", PERF_SAMPLE_TIME }, |
835 | { "SAMPLE_ADDR", PERF_SAMPLE_ADDR }, | 944 | { "SAMPLE_ADDR", PERF_SAMPLE_ADDR }, |
836 | { "SAMPLE_READ", PERF_SAMPLE_READ }, | 945 | { "SAMPLE_READ", PERF_SAMPLE_READ }, |
837 | { "SAMPLE_CALLCHAIN", PERF_SAMPLE_CALLCHAIN }, | 946 | { "SAMPLE_CALLCHAIN", PERF_SAMPLE_CALLCHAIN }, |
838 | { "SAMPLE_ID", PERF_SAMPLE_ID }, | 947 | { "SAMPLE_ID", PERF_SAMPLE_ID }, |
839 | { "SAMPLE_CPU", PERF_SAMPLE_CPU }, | 948 | { "SAMPLE_CPU", PERF_SAMPLE_CPU }, |
840 | { "SAMPLE_PERIOD", PERF_SAMPLE_PERIOD }, | 949 | { "SAMPLE_PERIOD", PERF_SAMPLE_PERIOD }, |
841 | { "SAMPLE_STREAM_ID", PERF_SAMPLE_STREAM_ID }, | 950 | { "SAMPLE_STREAM_ID", PERF_SAMPLE_STREAM_ID }, |
842 | { "SAMPLE_RAW", PERF_SAMPLE_RAW }, | 951 | { "SAMPLE_RAW", PERF_SAMPLE_RAW }, |
843 | 952 | ||
844 | { "FORMAT_TOTAL_TIME_ENABLED", PERF_FORMAT_TOTAL_TIME_ENABLED }, | 953 | { "FORMAT_TOTAL_TIME_ENABLED", PERF_FORMAT_TOTAL_TIME_ENABLED }, |
845 | { "FORMAT_TOTAL_TIME_RUNNING", PERF_FORMAT_TOTAL_TIME_RUNNING }, | 954 | { "FORMAT_TOTAL_TIME_RUNNING", PERF_FORMAT_TOTAL_TIME_RUNNING }, |
846 | { "FORMAT_ID", PERF_FORMAT_ID }, | 955 | { "FORMAT_ID", PERF_FORMAT_ID }, |
847 | { "FORMAT_GROUP", PERF_FORMAT_GROUP }, | 956 | { "FORMAT_GROUP", PERF_FORMAT_GROUP }, |
848 | 957 | ||
849 | { "RECORD_MMAP", PERF_RECORD_MMAP }, | 958 | { "RECORD_MMAP", PERF_RECORD_MMAP }, |
850 | { "RECORD_LOST", PERF_RECORD_LOST }, | 959 | { "RECORD_LOST", PERF_RECORD_LOST }, |
851 | { "RECORD_COMM", PERF_RECORD_COMM }, | 960 | { "RECORD_COMM", PERF_RECORD_COMM }, |
852 | { "RECORD_EXIT", PERF_RECORD_EXIT }, | 961 | { "RECORD_EXIT", PERF_RECORD_EXIT }, |
853 | { "RECORD_THROTTLE", PERF_RECORD_THROTTLE }, | 962 | { "RECORD_THROTTLE", PERF_RECORD_THROTTLE }, |
854 | { "RECORD_UNTHROTTLE", PERF_RECORD_UNTHROTTLE }, | 963 | { "RECORD_UNTHROTTLE", PERF_RECORD_UNTHROTTLE }, |
855 | { "RECORD_FORK", PERF_RECORD_FORK }, | 964 | { "RECORD_FORK", PERF_RECORD_FORK }, |
856 | { "RECORD_READ", PERF_RECORD_READ }, | 965 | { "RECORD_READ", PERF_RECORD_READ }, |
857 | { "RECORD_SAMPLE", PERF_RECORD_SAMPLE }, | 966 | { "RECORD_SAMPLE", PERF_RECORD_SAMPLE }, |
858 | { .name = NULL, }, | 967 | { .name = NULL, }, |
859 | }; | 968 | }; |
860 | 969 | ||
861 | static PyMethodDef perf__methods[] = { | 970 | static PyMethodDef perf__methods[] = { |
862 | { .ml_name = NULL, } | 971 | { .ml_name = NULL, } |
863 | }; | 972 | }; |
864 | 973 | ||
865 | PyMODINIT_FUNC initperf(void) | 974 | PyMODINIT_FUNC initperf(void) |
866 | { | 975 | { |
867 | PyObject *obj; | 976 | PyObject *obj; |
868 | int i; | 977 | int i; |
869 | PyObject *dict, *module = Py_InitModule("perf", perf__methods); | 978 | PyObject *dict, *module = Py_InitModule("perf", perf__methods); |
870 | 979 | ||
871 | if (module == NULL || | 980 | if (module == NULL || |
872 | pyrf_event__setup_types() < 0 || | 981 | pyrf_event__setup_types() < 0 || |
873 | pyrf_evlist__setup_types() < 0 || | 982 | pyrf_evlist__setup_types() < 0 || |
874 | pyrf_evsel__setup_types() < 0 || | 983 | pyrf_evsel__setup_types() < 0 || |
875 | pyrf_thread_map__setup_types() < 0 || | 984 | pyrf_thread_map__setup_types() < 0 || |
876 | pyrf_cpu_map__setup_types() < 0) | 985 | pyrf_cpu_map__setup_types() < 0) |
877 | return; | 986 | return; |
878 | 987 | ||
879 | Py_INCREF(&pyrf_evlist__type); | 988 | Py_INCREF(&pyrf_evlist__type); |
880 | PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); | 989 | PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); |
881 | 990 | ||
882 | Py_INCREF(&pyrf_evsel__type); | 991 | Py_INCREF(&pyrf_evsel__type); |
883 | PyModule_AddObject(module, "evsel", (PyObject*)&pyrf_evsel__type); | 992 | PyModule_AddObject(module, "evsel", (PyObject*)&pyrf_evsel__type); |
884 | 993 | ||
885 | Py_INCREF(&pyrf_thread_map__type); | 994 | Py_INCREF(&pyrf_thread_map__type); |
886 | PyModule_AddObject(module, "thread_map", (PyObject*)&pyrf_thread_map__type); | 995 | PyModule_AddObject(module, "thread_map", (PyObject*)&pyrf_thread_map__type); |
887 | 996 | ||
888 | Py_INCREF(&pyrf_cpu_map__type); | 997 | Py_INCREF(&pyrf_cpu_map__type); |
889 | PyModule_AddObject(module, "cpu_map", (PyObject*)&pyrf_cpu_map__type); | 998 | PyModule_AddObject(module, "cpu_map", (PyObject*)&pyrf_cpu_map__type); |
890 | 999 | ||
891 | dict = PyModule_GetDict(module); | 1000 | dict = PyModule_GetDict(module); |
892 | if (dict == NULL) | 1001 | if (dict == NULL) |
893 | goto error; | 1002 | goto error; |
894 | 1003 | ||
895 | for (i = 0; perf__constants[i].name != NULL; i++) { | 1004 | for (i = 0; perf__constants[i].name != NULL; i++) { |
896 | obj = PyInt_FromLong(perf__constants[i].value); | 1005 | obj = PyInt_FromLong(perf__constants[i].value); |
897 | if (obj == NULL) | 1006 | if (obj == NULL) |
898 | goto error; | 1007 | goto error; |
899 | PyDict_SetItemString(dict, perf__constants[i].name, obj); | 1008 | PyDict_SetItemString(dict, perf__constants[i].name, obj); |
900 | Py_DECREF(obj); | 1009 | Py_DECREF(obj); |
901 | } | 1010 | } |
902 | 1011 | ||
903 | error: | 1012 | error: |
904 | if (PyErr_Occurred()) | 1013 | if (PyErr_Occurred()) |
905 | PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); | 1014 | PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); |
906 | } | 1015 | } |
907 | 1016 |
tools/perf/util/setup.py
1 | #!/usr/bin/python2 | 1 | #!/usr/bin/python2 |
2 | 2 | ||
3 | from distutils.core import setup, Extension | 3 | from distutils.core import setup, Extension |
4 | from os import getenv | 4 | from os import getenv |
5 | 5 | ||
6 | from distutils.command.build_ext import build_ext as _build_ext | ||
7 | from distutils.command.install_lib import install_lib as _install_lib | ||
8 | |||
9 | class build_ext(_build_ext): | ||
10 | def finalize_options(self): | ||
11 | _build_ext.finalize_options(self) | ||
12 | self.build_lib = build_lib | ||
13 | self.build_temp = build_tmp | ||
14 | |||
15 | class install_lib(_install_lib): | ||
16 | def finalize_options(self): | ||
17 | _install_lib.finalize_options(self) | ||
18 | self.build_dir = build_lib | ||
19 | |||
20 | |||
6 | cflags = ['-fno-strict-aliasing', '-Wno-write-strings'] | 21 | cflags = ['-fno-strict-aliasing', '-Wno-write-strings'] |
7 | cflags += getenv('CFLAGS', '').split() | 22 | cflags += getenv('CFLAGS', '').split() |
8 | 23 | ||
24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | ||
25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | ||
26 | |||
9 | perf = Extension('perf', | 27 | perf = Extension('perf', |
10 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', | 28 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', |
11 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', | 29 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', |
12 | 'util/util.c', 'util/xyarray.c', 'util/cgroup.c'], | 30 | 'util/util.c', 'util/xyarray.c', 'util/cgroup.c'], |
13 | include_dirs = ['util/include'], | 31 | include_dirs = ['util/include'], |
14 | extra_compile_args = cflags, | 32 | extra_compile_args = cflags, |
15 | ) | 33 | ) |
16 | 34 | ||
17 | setup(name='perf', | 35 | setup(name='perf', |
18 | version='0.1', | 36 | version='0.1', |
19 | description='Interface with the Linux profiling infrastructure', | 37 | description='Interface with the Linux profiling infrastructure', |
20 | author='Arnaldo Carvalho de Melo', | 38 | author='Arnaldo Carvalho de Melo', |
21 | author_email='acme@redhat.com', | 39 | author_email='acme@redhat.com', |
22 | license='GPLv2', | 40 | license='GPLv2', |
23 | url='http://perf.wiki.kernel.org', | 41 | url='http://perf.wiki.kernel.org', |
24 | ext_modules=[perf]) | 42 | ext_modules=[perf], |
43 | cmdclass={'build_ext': build_ext, 'install_lib': install_lib}) | ||
25 | 44 |
tools/perf/util/symbol.c
1 | #define _GNU_SOURCE | 1 | #define _GNU_SOURCE |
2 | #include <ctype.h> | 2 | #include <ctype.h> |
3 | #include <dirent.h> | 3 | #include <dirent.h> |
4 | #include <errno.h> | 4 | #include <errno.h> |
5 | #include <libgen.h> | 5 | #include <libgen.h> |
6 | #include <stdlib.h> | 6 | #include <stdlib.h> |
7 | #include <stdio.h> | 7 | #include <stdio.h> |
8 | #include <string.h> | 8 | #include <string.h> |
9 | #include <sys/types.h> | 9 | #include <sys/types.h> |
10 | #include <sys/stat.h> | 10 | #include <sys/stat.h> |
11 | #include <sys/param.h> | 11 | #include <sys/param.h> |
12 | #include <fcntl.h> | 12 | #include <fcntl.h> |
13 | #include <unistd.h> | 13 | #include <unistd.h> |
14 | #include <inttypes.h> | 14 | #include <inttypes.h> |
15 | #include "build-id.h" | 15 | #include "build-id.h" |
16 | #include "debug.h" | 16 | #include "debug.h" |
17 | #include "symbol.h" | 17 | #include "symbol.h" |
18 | #include "strlist.h" | 18 | #include "strlist.h" |
19 | 19 | ||
20 | #include <libelf.h> | 20 | #include <libelf.h> |
21 | #include <gelf.h> | 21 | #include <gelf.h> |
22 | #include <elf.h> | 22 | #include <elf.h> |
23 | #include <limits.h> | 23 | #include <limits.h> |
24 | #include <sys/utsname.h> | 24 | #include <sys/utsname.h> |
25 | 25 | ||
26 | #ifndef KSYM_NAME_LEN | 26 | #ifndef KSYM_NAME_LEN |
27 | #define KSYM_NAME_LEN 128 | 27 | #define KSYM_NAME_LEN 128 |
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #ifndef NT_GNU_BUILD_ID | 30 | #ifndef NT_GNU_BUILD_ID |
31 | #define NT_GNU_BUILD_ID 3 | 31 | #define NT_GNU_BUILD_ID 3 |
32 | #endif | 32 | #endif |
33 | 33 | ||
34 | static bool dso__build_id_equal(const struct dso *dso, u8 *build_id); | 34 | static bool dso__build_id_equal(const struct dso *dso, u8 *build_id); |
35 | static int elf_read_build_id(Elf *elf, void *bf, size_t size); | 35 | static int elf_read_build_id(Elf *elf, void *bf, size_t size); |
36 | static void dsos__add(struct list_head *head, struct dso *dso); | 36 | static void dsos__add(struct list_head *head, struct dso *dso); |
37 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 37 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
38 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 38 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
39 | symbol_filter_t filter); | 39 | symbol_filter_t filter); |
40 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 40 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
41 | symbol_filter_t filter); | 41 | symbol_filter_t filter); |
42 | static int vmlinux_path__nr_entries; | 42 | static int vmlinux_path__nr_entries; |
43 | static char **vmlinux_path; | 43 | static char **vmlinux_path; |
44 | 44 | ||
45 | struct symbol_conf symbol_conf = { | 45 | struct symbol_conf symbol_conf = { |
46 | .exclude_other = true, | 46 | .exclude_other = true, |
47 | .use_modules = true, | 47 | .use_modules = true, |
48 | .try_vmlinux_path = true, | 48 | .try_vmlinux_path = true, |
49 | .symfs = "", | 49 | .symfs = "", |
50 | }; | 50 | }; |
51 | 51 | ||
52 | int dso__name_len(const struct dso *dso) | 52 | int dso__name_len(const struct dso *dso) |
53 | { | 53 | { |
54 | if (verbose) | 54 | if (verbose) |
55 | return dso->long_name_len; | 55 | return dso->long_name_len; |
56 | 56 | ||
57 | return dso->short_name_len; | 57 | return dso->short_name_len; |
58 | } | 58 | } |
59 | 59 | ||
60 | bool dso__loaded(const struct dso *dso, enum map_type type) | 60 | bool dso__loaded(const struct dso *dso, enum map_type type) |
61 | { | 61 | { |
62 | return dso->loaded & (1 << type); | 62 | return dso->loaded & (1 << type); |
63 | } | 63 | } |
64 | 64 | ||
65 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type) | 65 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type) |
66 | { | 66 | { |
67 | return dso->sorted_by_name & (1 << type); | 67 | return dso->sorted_by_name & (1 << type); |
68 | } | 68 | } |
69 | 69 | ||
70 | static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | 70 | static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) |
71 | { | 71 | { |
72 | dso->sorted_by_name |= (1 << type); | 72 | dso->sorted_by_name |= (1 << type); |
73 | } | 73 | } |
74 | 74 | ||
75 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 75 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
76 | { | 76 | { |
77 | switch (map_type) { | 77 | switch (map_type) { |
78 | case MAP__FUNCTION: | 78 | case MAP__FUNCTION: |
79 | return symbol_type == 'T' || symbol_type == 'W'; | 79 | return symbol_type == 'T' || symbol_type == 'W'; |
80 | case MAP__VARIABLE: | 80 | case MAP__VARIABLE: |
81 | return symbol_type == 'D' || symbol_type == 'd'; | 81 | return symbol_type == 'D' || symbol_type == 'd'; |
82 | default: | 82 | default: |
83 | return false; | 83 | return false; |
84 | } | 84 | } |
85 | } | 85 | } |
86 | 86 | ||
87 | static void symbols__fixup_end(struct rb_root *symbols) | 87 | static void symbols__fixup_end(struct rb_root *symbols) |
88 | { | 88 | { |
89 | struct rb_node *nd, *prevnd = rb_first(symbols); | 89 | struct rb_node *nd, *prevnd = rb_first(symbols); |
90 | struct symbol *curr, *prev; | 90 | struct symbol *curr, *prev; |
91 | 91 | ||
92 | if (prevnd == NULL) | 92 | if (prevnd == NULL) |
93 | return; | 93 | return; |
94 | 94 | ||
95 | curr = rb_entry(prevnd, struct symbol, rb_node); | 95 | curr = rb_entry(prevnd, struct symbol, rb_node); |
96 | 96 | ||
97 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | 97 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
98 | prev = curr; | 98 | prev = curr; |
99 | curr = rb_entry(nd, struct symbol, rb_node); | 99 | curr = rb_entry(nd, struct symbol, rb_node); |
100 | 100 | ||
101 | if (prev->end == prev->start && prev->end != curr->start) | 101 | if (prev->end == prev->start && prev->end != curr->start) |
102 | prev->end = curr->start - 1; | 102 | prev->end = curr->start - 1; |
103 | } | 103 | } |
104 | 104 | ||
105 | /* Last entry */ | 105 | /* Last entry */ |
106 | if (curr->end == curr->start) | 106 | if (curr->end == curr->start) |
107 | curr->end = roundup(curr->start, 4096); | 107 | curr->end = roundup(curr->start, 4096); |
108 | } | 108 | } |
109 | 109 | ||
110 | static void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | 110 | static void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) |
111 | { | 111 | { |
112 | struct map *prev, *curr; | 112 | struct map *prev, *curr; |
113 | struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); | 113 | struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); |
114 | 114 | ||
115 | if (prevnd == NULL) | 115 | if (prevnd == NULL) |
116 | return; | 116 | return; |
117 | 117 | ||
118 | curr = rb_entry(prevnd, struct map, rb_node); | 118 | curr = rb_entry(prevnd, struct map, rb_node); |
119 | 119 | ||
120 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | 120 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
121 | prev = curr; | 121 | prev = curr; |
122 | curr = rb_entry(nd, struct map, rb_node); | 122 | curr = rb_entry(nd, struct map, rb_node); |
123 | prev->end = curr->start - 1; | 123 | prev->end = curr->start - 1; |
124 | } | 124 | } |
125 | 125 | ||
126 | /* | 126 | /* |
127 | * We still haven't the actual symbols, so guess the | 127 | * We still haven't the actual symbols, so guess the |
128 | * last map final address. | 128 | * last map final address. |
129 | */ | 129 | */ |
130 | curr->end = ~0ULL; | 130 | curr->end = ~0ULL; |
131 | } | 131 | } |
132 | 132 | ||
133 | static void map_groups__fixup_end(struct map_groups *mg) | 133 | static void map_groups__fixup_end(struct map_groups *mg) |
134 | { | 134 | { |
135 | int i; | 135 | int i; |
136 | for (i = 0; i < MAP__NR_TYPES; ++i) | 136 | for (i = 0; i < MAP__NR_TYPES; ++i) |
137 | __map_groups__fixup_end(mg, i); | 137 | __map_groups__fixup_end(mg, i); |
138 | } | 138 | } |
139 | 139 | ||
140 | static struct symbol *symbol__new(u64 start, u64 len, u8 binding, | 140 | static struct symbol *symbol__new(u64 start, u64 len, u8 binding, |
141 | const char *name) | 141 | const char *name) |
142 | { | 142 | { |
143 | size_t namelen = strlen(name) + 1; | 143 | size_t namelen = strlen(name) + 1; |
144 | struct symbol *sym = calloc(1, (symbol_conf.priv_size + | 144 | struct symbol *sym = calloc(1, (symbol_conf.priv_size + |
145 | sizeof(*sym) + namelen)); | 145 | sizeof(*sym) + namelen)); |
146 | if (sym == NULL) | 146 | if (sym == NULL) |
147 | return NULL; | 147 | return NULL; |
148 | 148 | ||
149 | if (symbol_conf.priv_size) | 149 | if (symbol_conf.priv_size) |
150 | sym = ((void *)sym) + symbol_conf.priv_size; | 150 | sym = ((void *)sym) + symbol_conf.priv_size; |
151 | 151 | ||
152 | sym->start = start; | 152 | sym->start = start; |
153 | sym->end = len ? start + len - 1 : start; | 153 | sym->end = len ? start + len - 1 : start; |
154 | sym->binding = binding; | 154 | sym->binding = binding; |
155 | sym->namelen = namelen - 1; | 155 | sym->namelen = namelen - 1; |
156 | 156 | ||
157 | pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n", | 157 | pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n", |
158 | __func__, name, start, sym->end); | 158 | __func__, name, start, sym->end); |
159 | memcpy(sym->name, name, namelen); | 159 | memcpy(sym->name, name, namelen); |
160 | 160 | ||
161 | return sym; | 161 | return sym; |
162 | } | 162 | } |
163 | 163 | ||
164 | void symbol__delete(struct symbol *sym) | 164 | void symbol__delete(struct symbol *sym) |
165 | { | 165 | { |
166 | free(((void *)sym) - symbol_conf.priv_size); | 166 | free(((void *)sym) - symbol_conf.priv_size); |
167 | } | 167 | } |
168 | 168 | ||
169 | static size_t symbol__fprintf(struct symbol *sym, FILE *fp) | 169 | static size_t symbol__fprintf(struct symbol *sym, FILE *fp) |
170 | { | 170 | { |
171 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", | 171 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", |
172 | sym->start, sym->end, | 172 | sym->start, sym->end, |
173 | sym->binding == STB_GLOBAL ? 'g' : | 173 | sym->binding == STB_GLOBAL ? 'g' : |
174 | sym->binding == STB_LOCAL ? 'l' : 'w', | 174 | sym->binding == STB_LOCAL ? 'l' : 'w', |
175 | sym->name); | 175 | sym->name); |
176 | } | 176 | } |
177 | 177 | ||
178 | void dso__set_long_name(struct dso *dso, char *name) | 178 | void dso__set_long_name(struct dso *dso, char *name) |
179 | { | 179 | { |
180 | if (name == NULL) | 180 | if (name == NULL) |
181 | return; | 181 | return; |
182 | dso->long_name = name; | 182 | dso->long_name = name; |
183 | dso->long_name_len = strlen(name); | 183 | dso->long_name_len = strlen(name); |
184 | } | 184 | } |
185 | 185 | ||
186 | static void dso__set_short_name(struct dso *dso, const char *name) | 186 | static void dso__set_short_name(struct dso *dso, const char *name) |
187 | { | 187 | { |
188 | if (name == NULL) | 188 | if (name == NULL) |
189 | return; | 189 | return; |
190 | dso->short_name = name; | 190 | dso->short_name = name; |
191 | dso->short_name_len = strlen(name); | 191 | dso->short_name_len = strlen(name); |
192 | } | 192 | } |
193 | 193 | ||
194 | static void dso__set_basename(struct dso *dso) | 194 | static void dso__set_basename(struct dso *dso) |
195 | { | 195 | { |
196 | dso__set_short_name(dso, basename(dso->long_name)); | 196 | dso__set_short_name(dso, basename(dso->long_name)); |
197 | } | 197 | } |
198 | 198 | ||
199 | struct dso *dso__new(const char *name) | 199 | struct dso *dso__new(const char *name) |
200 | { | 200 | { |
201 | struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); | 201 | struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); |
202 | 202 | ||
203 | if (dso != NULL) { | 203 | if (dso != NULL) { |
204 | int i; | 204 | int i; |
205 | strcpy(dso->name, name); | 205 | strcpy(dso->name, name); |
206 | dso__set_long_name(dso, dso->name); | 206 | dso__set_long_name(dso, dso->name); |
207 | dso__set_short_name(dso, dso->name); | 207 | dso__set_short_name(dso, dso->name); |
208 | for (i = 0; i < MAP__NR_TYPES; ++i) | 208 | for (i = 0; i < MAP__NR_TYPES; ++i) |
209 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; | 209 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; |
210 | dso->symtab_type = SYMTAB__NOT_FOUND; | 210 | dso->symtab_type = SYMTAB__NOT_FOUND; |
211 | dso->loaded = 0; | 211 | dso->loaded = 0; |
212 | dso->sorted_by_name = 0; | 212 | dso->sorted_by_name = 0; |
213 | dso->has_build_id = 0; | 213 | dso->has_build_id = 0; |
214 | dso->kernel = DSO_TYPE_USER; | 214 | dso->kernel = DSO_TYPE_USER; |
215 | INIT_LIST_HEAD(&dso->node); | 215 | INIT_LIST_HEAD(&dso->node); |
216 | } | 216 | } |
217 | 217 | ||
218 | return dso; | 218 | return dso; |
219 | } | 219 | } |
220 | 220 | ||
221 | static void symbols__delete(struct rb_root *symbols) | 221 | static void symbols__delete(struct rb_root *symbols) |
222 | { | 222 | { |
223 | struct symbol *pos; | 223 | struct symbol *pos; |
224 | struct rb_node *next = rb_first(symbols); | 224 | struct rb_node *next = rb_first(symbols); |
225 | 225 | ||
226 | while (next) { | 226 | while (next) { |
227 | pos = rb_entry(next, struct symbol, rb_node); | 227 | pos = rb_entry(next, struct symbol, rb_node); |
228 | next = rb_next(&pos->rb_node); | 228 | next = rb_next(&pos->rb_node); |
229 | rb_erase(&pos->rb_node, symbols); | 229 | rb_erase(&pos->rb_node, symbols); |
230 | symbol__delete(pos); | 230 | symbol__delete(pos); |
231 | } | 231 | } |
232 | } | 232 | } |
233 | 233 | ||
234 | void dso__delete(struct dso *dso) | 234 | void dso__delete(struct dso *dso) |
235 | { | 235 | { |
236 | int i; | 236 | int i; |
237 | for (i = 0; i < MAP__NR_TYPES; ++i) | 237 | for (i = 0; i < MAP__NR_TYPES; ++i) |
238 | symbols__delete(&dso->symbols[i]); | 238 | symbols__delete(&dso->symbols[i]); |
239 | if (dso->sname_alloc) | 239 | if (dso->sname_alloc) |
240 | free((char *)dso->short_name); | 240 | free((char *)dso->short_name); |
241 | if (dso->lname_alloc) | 241 | if (dso->lname_alloc) |
242 | free(dso->long_name); | 242 | free(dso->long_name); |
243 | free(dso); | 243 | free(dso); |
244 | } | 244 | } |
245 | 245 | ||
246 | void dso__set_build_id(struct dso *dso, void *build_id) | 246 | void dso__set_build_id(struct dso *dso, void *build_id) |
247 | { | 247 | { |
248 | memcpy(dso->build_id, build_id, sizeof(dso->build_id)); | 248 | memcpy(dso->build_id, build_id, sizeof(dso->build_id)); |
249 | dso->has_build_id = 1; | 249 | dso->has_build_id = 1; |
250 | } | 250 | } |
251 | 251 | ||
252 | static void symbols__insert(struct rb_root *symbols, struct symbol *sym) | 252 | static void symbols__insert(struct rb_root *symbols, struct symbol *sym) |
253 | { | 253 | { |
254 | struct rb_node **p = &symbols->rb_node; | 254 | struct rb_node **p = &symbols->rb_node; |
255 | struct rb_node *parent = NULL; | 255 | struct rb_node *parent = NULL; |
256 | const u64 ip = sym->start; | 256 | const u64 ip = sym->start; |
257 | struct symbol *s; | 257 | struct symbol *s; |
258 | 258 | ||
259 | while (*p != NULL) { | 259 | while (*p != NULL) { |
260 | parent = *p; | 260 | parent = *p; |
261 | s = rb_entry(parent, struct symbol, rb_node); | 261 | s = rb_entry(parent, struct symbol, rb_node); |
262 | if (ip < s->start) | 262 | if (ip < s->start) |
263 | p = &(*p)->rb_left; | 263 | p = &(*p)->rb_left; |
264 | else | 264 | else |
265 | p = &(*p)->rb_right; | 265 | p = &(*p)->rb_right; |
266 | } | 266 | } |
267 | rb_link_node(&sym->rb_node, parent, p); | 267 | rb_link_node(&sym->rb_node, parent, p); |
268 | rb_insert_color(&sym->rb_node, symbols); | 268 | rb_insert_color(&sym->rb_node, symbols); |
269 | } | 269 | } |
270 | 270 | ||
271 | static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) | 271 | static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) |
272 | { | 272 | { |
273 | struct rb_node *n; | 273 | struct rb_node *n; |
274 | 274 | ||
275 | if (symbols == NULL) | 275 | if (symbols == NULL) |
276 | return NULL; | 276 | return NULL; |
277 | 277 | ||
278 | n = symbols->rb_node; | 278 | n = symbols->rb_node; |
279 | 279 | ||
280 | while (n) { | 280 | while (n) { |
281 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | 281 | struct symbol *s = rb_entry(n, struct symbol, rb_node); |
282 | 282 | ||
283 | if (ip < s->start) | 283 | if (ip < s->start) |
284 | n = n->rb_left; | 284 | n = n->rb_left; |
285 | else if (ip > s->end) | 285 | else if (ip > s->end) |
286 | n = n->rb_right; | 286 | n = n->rb_right; |
287 | else | 287 | else |
288 | return s; | 288 | return s; |
289 | } | 289 | } |
290 | 290 | ||
291 | return NULL; | 291 | return NULL; |
292 | } | 292 | } |
293 | 293 | ||
294 | struct symbol_name_rb_node { | 294 | struct symbol_name_rb_node { |
295 | struct rb_node rb_node; | 295 | struct rb_node rb_node; |
296 | struct symbol sym; | 296 | struct symbol sym; |
297 | }; | 297 | }; |
298 | 298 | ||
299 | static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym) | 299 | static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym) |
300 | { | 300 | { |
301 | struct rb_node **p = &symbols->rb_node; | 301 | struct rb_node **p = &symbols->rb_node; |
302 | struct rb_node *parent = NULL; | 302 | struct rb_node *parent = NULL; |
303 | struct symbol_name_rb_node *symn, *s; | 303 | struct symbol_name_rb_node *symn, *s; |
304 | 304 | ||
305 | symn = container_of(sym, struct symbol_name_rb_node, sym); | 305 | symn = container_of(sym, struct symbol_name_rb_node, sym); |
306 | 306 | ||
307 | while (*p != NULL) { | 307 | while (*p != NULL) { |
308 | parent = *p; | 308 | parent = *p; |
309 | s = rb_entry(parent, struct symbol_name_rb_node, rb_node); | 309 | s = rb_entry(parent, struct symbol_name_rb_node, rb_node); |
310 | if (strcmp(sym->name, s->sym.name) < 0) | 310 | if (strcmp(sym->name, s->sym.name) < 0) |
311 | p = &(*p)->rb_left; | 311 | p = &(*p)->rb_left; |
312 | else | 312 | else |
313 | p = &(*p)->rb_right; | 313 | p = &(*p)->rb_right; |
314 | } | 314 | } |
315 | rb_link_node(&symn->rb_node, parent, p); | 315 | rb_link_node(&symn->rb_node, parent, p); |
316 | rb_insert_color(&symn->rb_node, symbols); | 316 | rb_insert_color(&symn->rb_node, symbols); |
317 | } | 317 | } |
318 | 318 | ||
319 | static void symbols__sort_by_name(struct rb_root *symbols, | 319 | static void symbols__sort_by_name(struct rb_root *symbols, |
320 | struct rb_root *source) | 320 | struct rb_root *source) |
321 | { | 321 | { |
322 | struct rb_node *nd; | 322 | struct rb_node *nd; |
323 | 323 | ||
324 | for (nd = rb_first(source); nd; nd = rb_next(nd)) { | 324 | for (nd = rb_first(source); nd; nd = rb_next(nd)) { |
325 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 325 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
326 | symbols__insert_by_name(symbols, pos); | 326 | symbols__insert_by_name(symbols, pos); |
327 | } | 327 | } |
328 | } | 328 | } |
329 | 329 | ||
330 | static struct symbol *symbols__find_by_name(struct rb_root *symbols, | 330 | static struct symbol *symbols__find_by_name(struct rb_root *symbols, |
331 | const char *name) | 331 | const char *name) |
332 | { | 332 | { |
333 | struct rb_node *n; | 333 | struct rb_node *n; |
334 | 334 | ||
335 | if (symbols == NULL) | 335 | if (symbols == NULL) |
336 | return NULL; | 336 | return NULL; |
337 | 337 | ||
338 | n = symbols->rb_node; | 338 | n = symbols->rb_node; |
339 | 339 | ||
340 | while (n) { | 340 | while (n) { |
341 | struct symbol_name_rb_node *s; | 341 | struct symbol_name_rb_node *s; |
342 | int cmp; | 342 | int cmp; |
343 | 343 | ||
344 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); | 344 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); |
345 | cmp = strcmp(name, s->sym.name); | 345 | cmp = strcmp(name, s->sym.name); |
346 | 346 | ||
347 | if (cmp < 0) | 347 | if (cmp < 0) |
348 | n = n->rb_left; | 348 | n = n->rb_left; |
349 | else if (cmp > 0) | 349 | else if (cmp > 0) |
350 | n = n->rb_right; | 350 | n = n->rb_right; |
351 | else | 351 | else |
352 | return &s->sym; | 352 | return &s->sym; |
353 | } | 353 | } |
354 | 354 | ||
355 | return NULL; | 355 | return NULL; |
356 | } | 356 | } |
357 | 357 | ||
358 | struct symbol *dso__find_symbol(struct dso *dso, | 358 | struct symbol *dso__find_symbol(struct dso *dso, |
359 | enum map_type type, u64 addr) | 359 | enum map_type type, u64 addr) |
360 | { | 360 | { |
361 | return symbols__find(&dso->symbols[type], addr); | 361 | return symbols__find(&dso->symbols[type], addr); |
362 | } | 362 | } |
363 | 363 | ||
364 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | 364 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
365 | const char *name) | 365 | const char *name) |
366 | { | 366 | { |
367 | return symbols__find_by_name(&dso->symbol_names[type], name); | 367 | return symbols__find_by_name(&dso->symbol_names[type], name); |
368 | } | 368 | } |
369 | 369 | ||
370 | void dso__sort_by_name(struct dso *dso, enum map_type type) | 370 | void dso__sort_by_name(struct dso *dso, enum map_type type) |
371 | { | 371 | { |
372 | dso__set_sorted_by_name(dso, type); | 372 | dso__set_sorted_by_name(dso, type); |
373 | return symbols__sort_by_name(&dso->symbol_names[type], | 373 | return symbols__sort_by_name(&dso->symbol_names[type], |
374 | &dso->symbols[type]); | 374 | &dso->symbols[type]); |
375 | } | 375 | } |
376 | 376 | ||
377 | int build_id__sprintf(const u8 *build_id, int len, char *bf) | 377 | int build_id__sprintf(const u8 *build_id, int len, char *bf) |
378 | { | 378 | { |
379 | char *bid = bf; | 379 | char *bid = bf; |
380 | const u8 *raw = build_id; | 380 | const u8 *raw = build_id; |
381 | int i; | 381 | int i; |
382 | 382 | ||
383 | for (i = 0; i < len; ++i) { | 383 | for (i = 0; i < len; ++i) { |
384 | sprintf(bid, "%02x", *raw); | 384 | sprintf(bid, "%02x", *raw); |
385 | ++raw; | 385 | ++raw; |
386 | bid += 2; | 386 | bid += 2; |
387 | } | 387 | } |
388 | 388 | ||
389 | return raw - build_id; | 389 | return raw - build_id; |
390 | } | 390 | } |
391 | 391 | ||
392 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) | 392 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) |
393 | { | 393 | { |
394 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 394 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
395 | 395 | ||
396 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | 396 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); |
397 | return fprintf(fp, "%s", sbuild_id); | 397 | return fprintf(fp, "%s", sbuild_id); |
398 | } | 398 | } |
399 | 399 | ||
400 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | 400 | size_t dso__fprintf_symbols_by_name(struct dso *dso, |
401 | enum map_type type, FILE *fp) | 401 | enum map_type type, FILE *fp) |
402 | { | 402 | { |
403 | size_t ret = 0; | 403 | size_t ret = 0; |
404 | struct rb_node *nd; | 404 | struct rb_node *nd; |
405 | struct symbol_name_rb_node *pos; | 405 | struct symbol_name_rb_node *pos; |
406 | 406 | ||
407 | for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { | 407 | for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { |
408 | pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); | 408 | pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); |
409 | fprintf(fp, "%s\n", pos->sym.name); | 409 | fprintf(fp, "%s\n", pos->sym.name); |
410 | } | 410 | } |
411 | 411 | ||
412 | return ret; | 412 | return ret; |
413 | } | 413 | } |
414 | 414 | ||
415 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) | 415 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) |
416 | { | 416 | { |
417 | struct rb_node *nd; | 417 | struct rb_node *nd; |
418 | size_t ret = fprintf(fp, "dso: %s (", dso->short_name); | 418 | size_t ret = fprintf(fp, "dso: %s (", dso->short_name); |
419 | 419 | ||
420 | if (dso->short_name != dso->long_name) | 420 | if (dso->short_name != dso->long_name) |
421 | ret += fprintf(fp, "%s, ", dso->long_name); | 421 | ret += fprintf(fp, "%s, ", dso->long_name); |
422 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | 422 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], |
423 | dso->loaded ? "" : "NOT "); | 423 | dso->loaded ? "" : "NOT "); |
424 | ret += dso__fprintf_buildid(dso, fp); | 424 | ret += dso__fprintf_buildid(dso, fp); |
425 | ret += fprintf(fp, ")\n"); | 425 | ret += fprintf(fp, ")\n"); |
426 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { | 426 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { |
427 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 427 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
428 | ret += symbol__fprintf(pos, fp); | 428 | ret += symbol__fprintf(pos, fp); |
429 | } | 429 | } |
430 | 430 | ||
431 | return ret; | 431 | return ret; |
432 | } | 432 | } |
433 | 433 | ||
434 | int kallsyms__parse(const char *filename, void *arg, | 434 | int kallsyms__parse(const char *filename, void *arg, |
435 | int (*process_symbol)(void *arg, const char *name, | 435 | int (*process_symbol)(void *arg, const char *name, |
436 | char type, u64 start, u64 end)) | 436 | char type, u64 start, u64 end)) |
437 | { | 437 | { |
438 | char *line = NULL; | 438 | char *line = NULL; |
439 | size_t n; | 439 | size_t n; |
440 | int err = -1; | 440 | int err = -1; |
441 | u64 prev_start = 0; | 441 | u64 prev_start = 0; |
442 | char prev_symbol_type = 0; | 442 | char prev_symbol_type = 0; |
443 | char *prev_symbol_name; | 443 | char *prev_symbol_name; |
444 | FILE *file = fopen(filename, "r"); | 444 | FILE *file = fopen(filename, "r"); |
445 | 445 | ||
446 | if (file == NULL) | 446 | if (file == NULL) |
447 | goto out_failure; | 447 | goto out_failure; |
448 | 448 | ||
449 | prev_symbol_name = malloc(KSYM_NAME_LEN); | 449 | prev_symbol_name = malloc(KSYM_NAME_LEN); |
450 | if (prev_symbol_name == NULL) | 450 | if (prev_symbol_name == NULL) |
451 | goto out_close; | 451 | goto out_close; |
452 | 452 | ||
453 | err = 0; | 453 | err = 0; |
454 | 454 | ||
455 | while (!feof(file)) { | 455 | while (!feof(file)) { |
456 | u64 start; | 456 | u64 start; |
457 | int line_len, len; | 457 | int line_len, len; |
458 | char symbol_type; | 458 | char symbol_type; |
459 | char *symbol_name; | 459 | char *symbol_name; |
460 | 460 | ||
461 | line_len = getline(&line, &n, file); | 461 | line_len = getline(&line, &n, file); |
462 | if (line_len < 0 || !line) | 462 | if (line_len < 0 || !line) |
463 | break; | 463 | break; |
464 | 464 | ||
465 | line[--line_len] = '\0'; /* \n */ | 465 | line[--line_len] = '\0'; /* \n */ |
466 | 466 | ||
467 | len = hex2u64(line, &start); | 467 | len = hex2u64(line, &start); |
468 | 468 | ||
469 | len++; | 469 | len++; |
470 | if (len + 2 >= line_len) | 470 | if (len + 2 >= line_len) |
471 | continue; | 471 | continue; |
472 | 472 | ||
473 | symbol_type = toupper(line[len]); | 473 | symbol_type = toupper(line[len]); |
474 | len += 2; | 474 | len += 2; |
475 | symbol_name = line + len; | 475 | symbol_name = line + len; |
476 | len = line_len - len; | 476 | len = line_len - len; |
477 | 477 | ||
478 | if (len >= KSYM_NAME_LEN) { | 478 | if (len >= KSYM_NAME_LEN) { |
479 | err = -1; | 479 | err = -1; |
480 | break; | 480 | break; |
481 | } | 481 | } |
482 | 482 | ||
483 | if (prev_symbol_type) { | 483 | if (prev_symbol_type) { |
484 | u64 end = start; | 484 | u64 end = start; |
485 | if (end != prev_start) | 485 | if (end != prev_start) |
486 | --end; | 486 | --end; |
487 | err = process_symbol(arg, prev_symbol_name, | 487 | err = process_symbol(arg, prev_symbol_name, |
488 | prev_symbol_type, prev_start, end); | 488 | prev_symbol_type, prev_start, end); |
489 | if (err) | 489 | if (err) |
490 | break; | 490 | break; |
491 | } | 491 | } |
492 | 492 | ||
493 | memcpy(prev_symbol_name, symbol_name, len + 1); | 493 | memcpy(prev_symbol_name, symbol_name, len + 1); |
494 | prev_symbol_type = symbol_type; | 494 | prev_symbol_type = symbol_type; |
495 | prev_start = start; | 495 | prev_start = start; |
496 | } | 496 | } |
497 | 497 | ||
498 | free(prev_symbol_name); | 498 | free(prev_symbol_name); |
499 | free(line); | 499 | free(line); |
500 | out_close: | 500 | out_close: |
501 | fclose(file); | 501 | fclose(file); |
502 | return err; | 502 | return err; |
503 | 503 | ||
504 | out_failure: | 504 | out_failure: |
505 | return -1; | 505 | return -1; |
506 | } | 506 | } |
507 | 507 | ||
508 | struct process_kallsyms_args { | 508 | struct process_kallsyms_args { |
509 | struct map *map; | 509 | struct map *map; |
510 | struct dso *dso; | 510 | struct dso *dso; |
511 | }; | 511 | }; |
512 | 512 | ||
513 | static u8 kallsyms2elf_type(char type) | 513 | static u8 kallsyms2elf_type(char type) |
514 | { | 514 | { |
515 | if (type == 'W') | 515 | if (type == 'W') |
516 | return STB_WEAK; | 516 | return STB_WEAK; |
517 | 517 | ||
518 | return isupper(type) ? STB_GLOBAL : STB_LOCAL; | 518 | return isupper(type) ? STB_GLOBAL : STB_LOCAL; |
519 | } | 519 | } |
520 | 520 | ||
521 | static int map__process_kallsym_symbol(void *arg, const char *name, | 521 | static int map__process_kallsym_symbol(void *arg, const char *name, |
522 | char type, u64 start, u64 end) | 522 | char type, u64 start, u64 end) |
523 | { | 523 | { |
524 | struct symbol *sym; | 524 | struct symbol *sym; |
525 | struct process_kallsyms_args *a = arg; | 525 | struct process_kallsyms_args *a = arg; |
526 | struct rb_root *root = &a->dso->symbols[a->map->type]; | 526 | struct rb_root *root = &a->dso->symbols[a->map->type]; |
527 | 527 | ||
528 | if (!symbol_type__is_a(type, a->map->type)) | 528 | if (!symbol_type__is_a(type, a->map->type)) |
529 | return 0; | 529 | return 0; |
530 | 530 | ||
531 | sym = symbol__new(start, end - start + 1, | 531 | sym = symbol__new(start, end - start + 1, |
532 | kallsyms2elf_type(type), name); | 532 | kallsyms2elf_type(type), name); |
533 | if (sym == NULL) | 533 | if (sym == NULL) |
534 | return -ENOMEM; | 534 | return -ENOMEM; |
535 | /* | 535 | /* |
536 | * We will pass the symbols to the filter later, in | 536 | * We will pass the symbols to the filter later, in |
537 | * map__split_kallsyms, when we have split the maps per module | 537 | * map__split_kallsyms, when we have split the maps per module |
538 | */ | 538 | */ |
539 | symbols__insert(root, sym); | 539 | symbols__insert(root, sym); |
540 | 540 | ||
541 | return 0; | 541 | return 0; |
542 | } | 542 | } |
543 | 543 | ||
544 | /* | 544 | /* |
545 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, | 545 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, |
546 | * so that we can in the next step set the symbol ->end address and then | 546 | * so that we can in the next step set the symbol ->end address and then |
547 | * call kernel_maps__split_kallsyms. | 547 | * call kernel_maps__split_kallsyms. |
548 | */ | 548 | */ |
549 | static int dso__load_all_kallsyms(struct dso *dso, const char *filename, | 549 | static int dso__load_all_kallsyms(struct dso *dso, const char *filename, |
550 | struct map *map) | 550 | struct map *map) |
551 | { | 551 | { |
552 | struct process_kallsyms_args args = { .map = map, .dso = dso, }; | 552 | struct process_kallsyms_args args = { .map = map, .dso = dso, }; |
553 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | 553 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); |
554 | } | 554 | } |
555 | 555 | ||
556 | /* | 556 | /* |
557 | * Split the symbols into maps, making sure there are no overlaps, i.e. the | 557 | * Split the symbols into maps, making sure there are no overlaps, i.e. the |
558 | * kernel range is broken in several maps, named [kernel].N, as we don't have | 558 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
559 | * the original ELF section names vmlinux have. | 559 | * the original ELF section names vmlinux have. |
560 | */ | 560 | */ |
561 | static int dso__split_kallsyms(struct dso *dso, struct map *map, | 561 | static int dso__split_kallsyms(struct dso *dso, struct map *map, |
562 | symbol_filter_t filter) | 562 | symbol_filter_t filter) |
563 | { | 563 | { |
564 | struct map_groups *kmaps = map__kmap(map)->kmaps; | 564 | struct map_groups *kmaps = map__kmap(map)->kmaps; |
565 | struct machine *machine = kmaps->machine; | 565 | struct machine *machine = kmaps->machine; |
566 | struct map *curr_map = map; | 566 | struct map *curr_map = map; |
567 | struct symbol *pos; | 567 | struct symbol *pos; |
568 | int count = 0, moved = 0; | 568 | int count = 0, moved = 0; |
569 | struct rb_root *root = &dso->symbols[map->type]; | 569 | struct rb_root *root = &dso->symbols[map->type]; |
570 | struct rb_node *next = rb_first(root); | 570 | struct rb_node *next = rb_first(root); |
571 | int kernel_range = 0; | 571 | int kernel_range = 0; |
572 | 572 | ||
573 | while (next) { | 573 | while (next) { |
574 | char *module; | 574 | char *module; |
575 | 575 | ||
576 | pos = rb_entry(next, struct symbol, rb_node); | 576 | pos = rb_entry(next, struct symbol, rb_node); |
577 | next = rb_next(&pos->rb_node); | 577 | next = rb_next(&pos->rb_node); |
578 | 578 | ||
579 | module = strchr(pos->name, '\t'); | 579 | module = strchr(pos->name, '\t'); |
580 | if (module) { | 580 | if (module) { |
581 | if (!symbol_conf.use_modules) | 581 | if (!symbol_conf.use_modules) |
582 | goto discard_symbol; | 582 | goto discard_symbol; |
583 | 583 | ||
584 | *module++ = '\0'; | 584 | *module++ = '\0'; |
585 | 585 | ||
586 | if (strcmp(curr_map->dso->short_name, module)) { | 586 | if (strcmp(curr_map->dso->short_name, module)) { |
587 | if (curr_map != map && | 587 | if (curr_map != map && |
588 | dso->kernel == DSO_TYPE_GUEST_KERNEL && | 588 | dso->kernel == DSO_TYPE_GUEST_KERNEL && |
589 | machine__is_default_guest(machine)) { | 589 | machine__is_default_guest(machine)) { |
590 | /* | 590 | /* |
591 | * We assume all symbols of a module are | 591 | * We assume all symbols of a module are |
592 | * continuous in * kallsyms, so curr_map | 592 | * continuous in * kallsyms, so curr_map |
593 | * points to a module and all its | 593 | * points to a module and all its |
594 | * symbols are in its kmap. Mark it as | 594 | * symbols are in its kmap. Mark it as |
595 | * loaded. | 595 | * loaded. |
596 | */ | 596 | */ |
597 | dso__set_loaded(curr_map->dso, | 597 | dso__set_loaded(curr_map->dso, |
598 | curr_map->type); | 598 | curr_map->type); |
599 | } | 599 | } |
600 | 600 | ||
601 | curr_map = map_groups__find_by_name(kmaps, | 601 | curr_map = map_groups__find_by_name(kmaps, |
602 | map->type, module); | 602 | map->type, module); |
603 | if (curr_map == NULL) { | 603 | if (curr_map == NULL) { |
604 | pr_debug("%s/proc/{kallsyms,modules} " | 604 | pr_debug("%s/proc/{kallsyms,modules} " |
605 | "inconsistency while looking " | 605 | "inconsistency while looking " |
606 | "for \"%s\" module!\n", | 606 | "for \"%s\" module!\n", |
607 | machine->root_dir, module); | 607 | machine->root_dir, module); |
608 | curr_map = map; | 608 | curr_map = map; |
609 | goto discard_symbol; | 609 | goto discard_symbol; |
610 | } | 610 | } |
611 | 611 | ||
612 | if (curr_map->dso->loaded && | 612 | if (curr_map->dso->loaded && |
613 | !machine__is_default_guest(machine)) | 613 | !machine__is_default_guest(machine)) |
614 | goto discard_symbol; | 614 | goto discard_symbol; |
615 | } | 615 | } |
616 | /* | 616 | /* |
617 | * So that we look just like we get from .ko files, | 617 | * So that we look just like we get from .ko files, |
618 | * i.e. not prelinked, relative to map->start. | 618 | * i.e. not prelinked, relative to map->start. |
619 | */ | 619 | */ |
620 | pos->start = curr_map->map_ip(curr_map, pos->start); | 620 | pos->start = curr_map->map_ip(curr_map, pos->start); |
621 | pos->end = curr_map->map_ip(curr_map, pos->end); | 621 | pos->end = curr_map->map_ip(curr_map, pos->end); |
622 | } else if (curr_map != map) { | 622 | } else if (curr_map != map) { |
623 | char dso_name[PATH_MAX]; | 623 | char dso_name[PATH_MAX]; |
624 | struct dso *ndso; | 624 | struct dso *ndso; |
625 | 625 | ||
626 | if (count == 0) { | 626 | if (count == 0) { |
627 | curr_map = map; | 627 | curr_map = map; |
628 | goto filter_symbol; | 628 | goto filter_symbol; |
629 | } | 629 | } |
630 | 630 | ||
631 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 631 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
632 | snprintf(dso_name, sizeof(dso_name), | 632 | snprintf(dso_name, sizeof(dso_name), |
633 | "[guest.kernel].%d", | 633 | "[guest.kernel].%d", |
634 | kernel_range++); | 634 | kernel_range++); |
635 | else | 635 | else |
636 | snprintf(dso_name, sizeof(dso_name), | 636 | snprintf(dso_name, sizeof(dso_name), |
637 | "[kernel].%d", | 637 | "[kernel].%d", |
638 | kernel_range++); | 638 | kernel_range++); |
639 | 639 | ||
640 | ndso = dso__new(dso_name); | 640 | ndso = dso__new(dso_name); |
641 | if (ndso == NULL) | 641 | if (ndso == NULL) |
642 | return -1; | 642 | return -1; |
643 | 643 | ||
644 | ndso->kernel = dso->kernel; | 644 | ndso->kernel = dso->kernel; |
645 | 645 | ||
646 | curr_map = map__new2(pos->start, ndso, map->type); | 646 | curr_map = map__new2(pos->start, ndso, map->type); |
647 | if (curr_map == NULL) { | 647 | if (curr_map == NULL) { |
648 | dso__delete(ndso); | 648 | dso__delete(ndso); |
649 | return -1; | 649 | return -1; |
650 | } | 650 | } |
651 | 651 | ||
652 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | 652 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; |
653 | map_groups__insert(kmaps, curr_map); | 653 | map_groups__insert(kmaps, curr_map); |
654 | ++kernel_range; | 654 | ++kernel_range; |
655 | } | 655 | } |
656 | filter_symbol: | 656 | filter_symbol: |
657 | if (filter && filter(curr_map, pos)) { | 657 | if (filter && filter(curr_map, pos)) { |
658 | discard_symbol: rb_erase(&pos->rb_node, root); | 658 | discard_symbol: rb_erase(&pos->rb_node, root); |
659 | symbol__delete(pos); | 659 | symbol__delete(pos); |
660 | } else { | 660 | } else { |
661 | if (curr_map != map) { | 661 | if (curr_map != map) { |
662 | rb_erase(&pos->rb_node, root); | 662 | rb_erase(&pos->rb_node, root); |
663 | symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); | 663 | symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); |
664 | ++moved; | 664 | ++moved; |
665 | } else | 665 | } else |
666 | ++count; | 666 | ++count; |
667 | } | 667 | } |
668 | } | 668 | } |
669 | 669 | ||
670 | if (curr_map != map && | 670 | if (curr_map != map && |
671 | dso->kernel == DSO_TYPE_GUEST_KERNEL && | 671 | dso->kernel == DSO_TYPE_GUEST_KERNEL && |
672 | machine__is_default_guest(kmaps->machine)) { | 672 | machine__is_default_guest(kmaps->machine)) { |
673 | dso__set_loaded(curr_map->dso, curr_map->type); | 673 | dso__set_loaded(curr_map->dso, curr_map->type); |
674 | } | 674 | } |
675 | 675 | ||
676 | return count + moved; | 676 | return count + moved; |
677 | } | 677 | } |
678 | 678 | ||
679 | static bool symbol__restricted_filename(const char *filename, | 679 | static bool symbol__restricted_filename(const char *filename, |
680 | const char *restricted_filename) | 680 | const char *restricted_filename) |
681 | { | 681 | { |
682 | bool restricted = false; | 682 | bool restricted = false; |
683 | 683 | ||
684 | if (symbol_conf.kptr_restrict) { | 684 | if (symbol_conf.kptr_restrict) { |
685 | char *r = realpath(filename, NULL); | 685 | char *r = realpath(filename, NULL); |
686 | 686 | ||
687 | if (r != NULL) { | 687 | if (r != NULL) { |
688 | restricted = strcmp(r, restricted_filename) == 0; | 688 | restricted = strcmp(r, restricted_filename) == 0; |
689 | free(r); | 689 | free(r); |
690 | return restricted; | 690 | return restricted; |
691 | } | 691 | } |
692 | } | 692 | } |
693 | 693 | ||
694 | return restricted; | 694 | return restricted; |
695 | } | 695 | } |
696 | 696 | ||
697 | int dso__load_kallsyms(struct dso *dso, const char *filename, | 697 | int dso__load_kallsyms(struct dso *dso, const char *filename, |
698 | struct map *map, symbol_filter_t filter) | 698 | struct map *map, symbol_filter_t filter) |
699 | { | 699 | { |
700 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | 700 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) |
701 | return -1; | 701 | return -1; |
702 | 702 | ||
703 | if (dso__load_all_kallsyms(dso, filename, map) < 0) | 703 | if (dso__load_all_kallsyms(dso, filename, map) < 0) |
704 | return -1; | 704 | return -1; |
705 | 705 | ||
706 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 706 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
707 | dso->symtab_type = SYMTAB__GUEST_KALLSYMS; | 707 | dso->symtab_type = SYMTAB__GUEST_KALLSYMS; |
708 | else | 708 | else |
709 | dso->symtab_type = SYMTAB__KALLSYMS; | 709 | dso->symtab_type = SYMTAB__KALLSYMS; |
710 | 710 | ||
711 | return dso__split_kallsyms(dso, map, filter); | 711 | return dso__split_kallsyms(dso, map, filter); |
712 | } | 712 | } |
713 | 713 | ||
714 | static int dso__load_perf_map(struct dso *dso, struct map *map, | 714 | static int dso__load_perf_map(struct dso *dso, struct map *map, |
715 | symbol_filter_t filter) | 715 | symbol_filter_t filter) |
716 | { | 716 | { |
717 | char *line = NULL; | 717 | char *line = NULL; |
718 | size_t n; | 718 | size_t n; |
719 | FILE *file; | 719 | FILE *file; |
720 | int nr_syms = 0; | 720 | int nr_syms = 0; |
721 | 721 | ||
722 | file = fopen(dso->long_name, "r"); | 722 | file = fopen(dso->long_name, "r"); |
723 | if (file == NULL) | 723 | if (file == NULL) |
724 | goto out_failure; | 724 | goto out_failure; |
725 | 725 | ||
726 | while (!feof(file)) { | 726 | while (!feof(file)) { |
727 | u64 start, size; | 727 | u64 start, size; |
728 | struct symbol *sym; | 728 | struct symbol *sym; |
729 | int line_len, len; | 729 | int line_len, len; |
730 | 730 | ||
731 | line_len = getline(&line, &n, file); | 731 | line_len = getline(&line, &n, file); |
732 | if (line_len < 0) | 732 | if (line_len < 0) |
733 | break; | 733 | break; |
734 | 734 | ||
735 | if (!line) | 735 | if (!line) |
736 | goto out_failure; | 736 | goto out_failure; |
737 | 737 | ||
738 | line[--line_len] = '\0'; /* \n */ | 738 | line[--line_len] = '\0'; /* \n */ |
739 | 739 | ||
740 | len = hex2u64(line, &start); | 740 | len = hex2u64(line, &start); |
741 | 741 | ||
742 | len++; | 742 | len++; |
743 | if (len + 2 >= line_len) | 743 | if (len + 2 >= line_len) |
744 | continue; | 744 | continue; |
745 | 745 | ||
746 | len += hex2u64(line + len, &size); | 746 | len += hex2u64(line + len, &size); |
747 | 747 | ||
748 | len++; | 748 | len++; |
749 | if (len + 2 >= line_len) | 749 | if (len + 2 >= line_len) |
750 | continue; | 750 | continue; |
751 | 751 | ||
752 | sym = symbol__new(start, size, STB_GLOBAL, line + len); | 752 | sym = symbol__new(start, size, STB_GLOBAL, line + len); |
753 | 753 | ||
754 | if (sym == NULL) | 754 | if (sym == NULL) |
755 | goto out_delete_line; | 755 | goto out_delete_line; |
756 | 756 | ||
757 | if (filter && filter(map, sym)) | 757 | if (filter && filter(map, sym)) |
758 | symbol__delete(sym); | 758 | symbol__delete(sym); |
759 | else { | 759 | else { |
760 | symbols__insert(&dso->symbols[map->type], sym); | 760 | symbols__insert(&dso->symbols[map->type], sym); |
761 | nr_syms++; | 761 | nr_syms++; |
762 | } | 762 | } |
763 | } | 763 | } |
764 | 764 | ||
765 | free(line); | 765 | free(line); |
766 | fclose(file); | 766 | fclose(file); |
767 | 767 | ||
768 | return nr_syms; | 768 | return nr_syms; |
769 | 769 | ||
770 | out_delete_line: | 770 | out_delete_line: |
771 | free(line); | 771 | free(line); |
772 | out_failure: | 772 | out_failure: |
773 | return -1; | 773 | return -1; |
774 | } | 774 | } |
775 | 775 | ||
776 | /** | 776 | /** |
777 | * elf_symtab__for_each_symbol - iterate thru all the symbols | 777 | * elf_symtab__for_each_symbol - iterate thru all the symbols |
778 | * | 778 | * |
779 | * @syms: struct elf_symtab instance to iterate | 779 | * @syms: struct elf_symtab instance to iterate |
780 | * @idx: uint32_t idx | 780 | * @idx: uint32_t idx |
781 | * @sym: GElf_Sym iterator | 781 | * @sym: GElf_Sym iterator |
782 | */ | 782 | */ |
783 | #define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \ | 783 | #define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \ |
784 | for (idx = 0, gelf_getsym(syms, idx, &sym);\ | 784 | for (idx = 0, gelf_getsym(syms, idx, &sym);\ |
785 | idx < nr_syms; \ | 785 | idx < nr_syms; \ |
786 | idx++, gelf_getsym(syms, idx, &sym)) | 786 | idx++, gelf_getsym(syms, idx, &sym)) |
787 | 787 | ||
788 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) | 788 | static inline uint8_t elf_sym__type(const GElf_Sym *sym) |
789 | { | 789 | { |
790 | return GELF_ST_TYPE(sym->st_info); | 790 | return GELF_ST_TYPE(sym->st_info); |
791 | } | 791 | } |
792 | 792 | ||
793 | static inline int elf_sym__is_function(const GElf_Sym *sym) | 793 | static inline int elf_sym__is_function(const GElf_Sym *sym) |
794 | { | 794 | { |
795 | return elf_sym__type(sym) == STT_FUNC && | 795 | return elf_sym__type(sym) == STT_FUNC && |
796 | sym->st_name != 0 && | 796 | sym->st_name != 0 && |
797 | sym->st_shndx != SHN_UNDEF; | 797 | sym->st_shndx != SHN_UNDEF; |
798 | } | 798 | } |
799 | 799 | ||
800 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | 800 | static inline bool elf_sym__is_object(const GElf_Sym *sym) |
801 | { | 801 | { |
802 | return elf_sym__type(sym) == STT_OBJECT && | 802 | return elf_sym__type(sym) == STT_OBJECT && |
803 | sym->st_name != 0 && | 803 | sym->st_name != 0 && |
804 | sym->st_shndx != SHN_UNDEF; | 804 | sym->st_shndx != SHN_UNDEF; |
805 | } | 805 | } |
806 | 806 | ||
807 | static inline int elf_sym__is_label(const GElf_Sym *sym) | 807 | static inline int elf_sym__is_label(const GElf_Sym *sym) |
808 | { | 808 | { |
809 | return elf_sym__type(sym) == STT_NOTYPE && | 809 | return elf_sym__type(sym) == STT_NOTYPE && |
810 | sym->st_name != 0 && | 810 | sym->st_name != 0 && |
811 | sym->st_shndx != SHN_UNDEF && | 811 | sym->st_shndx != SHN_UNDEF && |
812 | sym->st_shndx != SHN_ABS; | 812 | sym->st_shndx != SHN_ABS; |
813 | } | 813 | } |
814 | 814 | ||
815 | static inline const char *elf_sec__name(const GElf_Shdr *shdr, | 815 | static inline const char *elf_sec__name(const GElf_Shdr *shdr, |
816 | const Elf_Data *secstrs) | 816 | const Elf_Data *secstrs) |
817 | { | 817 | { |
818 | return secstrs->d_buf + shdr->sh_name; | 818 | return secstrs->d_buf + shdr->sh_name; |
819 | } | 819 | } |
820 | 820 | ||
821 | static inline int elf_sec__is_text(const GElf_Shdr *shdr, | 821 | static inline int elf_sec__is_text(const GElf_Shdr *shdr, |
822 | const Elf_Data *secstrs) | 822 | const Elf_Data *secstrs) |
823 | { | 823 | { |
824 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | 824 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; |
825 | } | 825 | } |
826 | 826 | ||
827 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | 827 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, |
828 | const Elf_Data *secstrs) | 828 | const Elf_Data *secstrs) |
829 | { | 829 | { |
830 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | 830 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; |
831 | } | 831 | } |
832 | 832 | ||
833 | static inline const char *elf_sym__name(const GElf_Sym *sym, | 833 | static inline const char *elf_sym__name(const GElf_Sym *sym, |
834 | const Elf_Data *symstrs) | 834 | const Elf_Data *symstrs) |
835 | { | 835 | { |
836 | return symstrs->d_buf + sym->st_name; | 836 | return symstrs->d_buf + sym->st_name; |
837 | } | 837 | } |
838 | 838 | ||
839 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | 839 | static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, |
840 | GElf_Shdr *shp, const char *name, | 840 | GElf_Shdr *shp, const char *name, |
841 | size_t *idx) | 841 | size_t *idx) |
842 | { | 842 | { |
843 | Elf_Scn *sec = NULL; | 843 | Elf_Scn *sec = NULL; |
844 | size_t cnt = 1; | 844 | size_t cnt = 1; |
845 | 845 | ||
846 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | 846 | while ((sec = elf_nextscn(elf, sec)) != NULL) { |
847 | char *str; | 847 | char *str; |
848 | 848 | ||
849 | gelf_getshdr(sec, shp); | 849 | gelf_getshdr(sec, shp); |
850 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); | 850 | str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); |
851 | if (!strcmp(name, str)) { | 851 | if (!strcmp(name, str)) { |
852 | if (idx) | 852 | if (idx) |
853 | *idx = cnt; | 853 | *idx = cnt; |
854 | break; | 854 | break; |
855 | } | 855 | } |
856 | ++cnt; | 856 | ++cnt; |
857 | } | 857 | } |
858 | 858 | ||
859 | return sec; | 859 | return sec; |
860 | } | 860 | } |
861 | 861 | ||
862 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ | 862 | #define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \ |
863 | for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ | 863 | for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \ |
864 | idx < nr_entries; \ | 864 | idx < nr_entries; \ |
865 | ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) | 865 | ++idx, pos = gelf_getrel(reldata, idx, &pos_mem)) |
866 | 866 | ||
867 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ | 867 | #define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \ |
868 | for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ | 868 | for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \ |
869 | idx < nr_entries; \ | 869 | idx < nr_entries; \ |
870 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | 870 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) |
871 | 871 | ||
872 | /* | 872 | /* |
873 | * We need to check if we have a .dynsym, so that we can handle the | 873 | * We need to check if we have a .dynsym, so that we can handle the |
874 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | 874 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it |
875 | * .dynsym or .symtab). | 875 | * .dynsym or .symtab). |
876 | * And always look at the original dso, not at debuginfo packages, that | 876 | * And always look at the original dso, not at debuginfo packages, that |
877 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | 877 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). |
878 | */ | 878 | */ |
879 | static int dso__synthesize_plt_symbols(struct dso *dso, struct map *map, | 879 | static int dso__synthesize_plt_symbols(struct dso *dso, struct map *map, |
880 | symbol_filter_t filter) | 880 | symbol_filter_t filter) |
881 | { | 881 | { |
882 | uint32_t nr_rel_entries, idx; | 882 | uint32_t nr_rel_entries, idx; |
883 | GElf_Sym sym; | 883 | GElf_Sym sym; |
884 | u64 plt_offset; | 884 | u64 plt_offset; |
885 | GElf_Shdr shdr_plt; | 885 | GElf_Shdr shdr_plt; |
886 | struct symbol *f; | 886 | struct symbol *f; |
887 | GElf_Shdr shdr_rel_plt, shdr_dynsym; | 887 | GElf_Shdr shdr_rel_plt, shdr_dynsym; |
888 | Elf_Data *reldata, *syms, *symstrs; | 888 | Elf_Data *reldata, *syms, *symstrs; |
889 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; | 889 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; |
890 | size_t dynsym_idx; | 890 | size_t dynsym_idx; |
891 | GElf_Ehdr ehdr; | 891 | GElf_Ehdr ehdr; |
892 | char sympltname[1024]; | 892 | char sympltname[1024]; |
893 | Elf *elf; | 893 | Elf *elf; |
894 | int nr = 0, symidx, fd, err = 0; | 894 | int nr = 0, symidx, fd, err = 0; |
895 | char name[PATH_MAX]; | 895 | char name[PATH_MAX]; |
896 | 896 | ||
897 | snprintf(name, sizeof(name), "%s%s", | 897 | snprintf(name, sizeof(name), "%s%s", |
898 | symbol_conf.symfs, dso->long_name); | 898 | symbol_conf.symfs, dso->long_name); |
899 | fd = open(name, O_RDONLY); | 899 | fd = open(name, O_RDONLY); |
900 | if (fd < 0) | 900 | if (fd < 0) |
901 | goto out; | 901 | goto out; |
902 | 902 | ||
903 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 903 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
904 | if (elf == NULL) | 904 | if (elf == NULL) |
905 | goto out_close; | 905 | goto out_close; |
906 | 906 | ||
907 | if (gelf_getehdr(elf, &ehdr) == NULL) | 907 | if (gelf_getehdr(elf, &ehdr) == NULL) |
908 | goto out_elf_end; | 908 | goto out_elf_end; |
909 | 909 | ||
910 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | 910 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, |
911 | ".dynsym", &dynsym_idx); | 911 | ".dynsym", &dynsym_idx); |
912 | if (scn_dynsym == NULL) | 912 | if (scn_dynsym == NULL) |
913 | goto out_elf_end; | 913 | goto out_elf_end; |
914 | 914 | ||
915 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | 915 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
916 | ".rela.plt", NULL); | 916 | ".rela.plt", NULL); |
917 | if (scn_plt_rel == NULL) { | 917 | if (scn_plt_rel == NULL) { |
918 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, | 918 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
919 | ".rel.plt", NULL); | 919 | ".rel.plt", NULL); |
920 | if (scn_plt_rel == NULL) | 920 | if (scn_plt_rel == NULL) |
921 | goto out_elf_end; | 921 | goto out_elf_end; |
922 | } | 922 | } |
923 | 923 | ||
924 | err = -1; | 924 | err = -1; |
925 | 925 | ||
926 | if (shdr_rel_plt.sh_link != dynsym_idx) | 926 | if (shdr_rel_plt.sh_link != dynsym_idx) |
927 | goto out_elf_end; | 927 | goto out_elf_end; |
928 | 928 | ||
929 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) | 929 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) |
930 | goto out_elf_end; | 930 | goto out_elf_end; |
931 | 931 | ||
932 | /* | 932 | /* |
933 | * Fetch the relocation section to find the idxes to the GOT | 933 | * Fetch the relocation section to find the idxes to the GOT |
934 | * and the symbols in the .dynsym they refer to. | 934 | * and the symbols in the .dynsym they refer to. |
935 | */ | 935 | */ |
936 | reldata = elf_getdata(scn_plt_rel, NULL); | 936 | reldata = elf_getdata(scn_plt_rel, NULL); |
937 | if (reldata == NULL) | 937 | if (reldata == NULL) |
938 | goto out_elf_end; | 938 | goto out_elf_end; |
939 | 939 | ||
940 | syms = elf_getdata(scn_dynsym, NULL); | 940 | syms = elf_getdata(scn_dynsym, NULL); |
941 | if (syms == NULL) | 941 | if (syms == NULL) |
942 | goto out_elf_end; | 942 | goto out_elf_end; |
943 | 943 | ||
944 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); | 944 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); |
945 | if (scn_symstrs == NULL) | 945 | if (scn_symstrs == NULL) |
946 | goto out_elf_end; | 946 | goto out_elf_end; |
947 | 947 | ||
948 | symstrs = elf_getdata(scn_symstrs, NULL); | 948 | symstrs = elf_getdata(scn_symstrs, NULL); |
949 | if (symstrs == NULL) | 949 | if (symstrs == NULL) |
950 | goto out_elf_end; | 950 | goto out_elf_end; |
951 | 951 | ||
952 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | 952 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; |
953 | plt_offset = shdr_plt.sh_offset; | 953 | plt_offset = shdr_plt.sh_offset; |
954 | 954 | ||
955 | if (shdr_rel_plt.sh_type == SHT_RELA) { | 955 | if (shdr_rel_plt.sh_type == SHT_RELA) { |
956 | GElf_Rela pos_mem, *pos; | 956 | GElf_Rela pos_mem, *pos; |
957 | 957 | ||
958 | elf_section__for_each_rela(reldata, pos, pos_mem, idx, | 958 | elf_section__for_each_rela(reldata, pos, pos_mem, idx, |
959 | nr_rel_entries) { | 959 | nr_rel_entries) { |
960 | symidx = GELF_R_SYM(pos->r_info); | 960 | symidx = GELF_R_SYM(pos->r_info); |
961 | plt_offset += shdr_plt.sh_entsize; | 961 | plt_offset += shdr_plt.sh_entsize; |
962 | gelf_getsym(syms, symidx, &sym); | 962 | gelf_getsym(syms, symidx, &sym); |
963 | snprintf(sympltname, sizeof(sympltname), | 963 | snprintf(sympltname, sizeof(sympltname), |
964 | "%s@plt", elf_sym__name(&sym, symstrs)); | 964 | "%s@plt", elf_sym__name(&sym, symstrs)); |
965 | 965 | ||
966 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 966 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
967 | STB_GLOBAL, sympltname); | 967 | STB_GLOBAL, sympltname); |
968 | if (!f) | 968 | if (!f) |
969 | goto out_elf_end; | 969 | goto out_elf_end; |
970 | 970 | ||
971 | if (filter && filter(map, f)) | 971 | if (filter && filter(map, f)) |
972 | symbol__delete(f); | 972 | symbol__delete(f); |
973 | else { | 973 | else { |
974 | symbols__insert(&dso->symbols[map->type], f); | 974 | symbols__insert(&dso->symbols[map->type], f); |
975 | ++nr; | 975 | ++nr; |
976 | } | 976 | } |
977 | } | 977 | } |
978 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | 978 | } else if (shdr_rel_plt.sh_type == SHT_REL) { |
979 | GElf_Rel pos_mem, *pos; | 979 | GElf_Rel pos_mem, *pos; |
980 | elf_section__for_each_rel(reldata, pos, pos_mem, idx, | 980 | elf_section__for_each_rel(reldata, pos, pos_mem, idx, |
981 | nr_rel_entries) { | 981 | nr_rel_entries) { |
982 | symidx = GELF_R_SYM(pos->r_info); | 982 | symidx = GELF_R_SYM(pos->r_info); |
983 | plt_offset += shdr_plt.sh_entsize; | 983 | plt_offset += shdr_plt.sh_entsize; |
984 | gelf_getsym(syms, symidx, &sym); | 984 | gelf_getsym(syms, symidx, &sym); |
985 | snprintf(sympltname, sizeof(sympltname), | 985 | snprintf(sympltname, sizeof(sympltname), |
986 | "%s@plt", elf_sym__name(&sym, symstrs)); | 986 | "%s@plt", elf_sym__name(&sym, symstrs)); |
987 | 987 | ||
988 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 988 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
989 | STB_GLOBAL, sympltname); | 989 | STB_GLOBAL, sympltname); |
990 | if (!f) | 990 | if (!f) |
991 | goto out_elf_end; | 991 | goto out_elf_end; |
992 | 992 | ||
993 | if (filter && filter(map, f)) | 993 | if (filter && filter(map, f)) |
994 | symbol__delete(f); | 994 | symbol__delete(f); |
995 | else { | 995 | else { |
996 | symbols__insert(&dso->symbols[map->type], f); | 996 | symbols__insert(&dso->symbols[map->type], f); |
997 | ++nr; | 997 | ++nr; |
998 | } | 998 | } |
999 | } | 999 | } |
1000 | } | 1000 | } |
1001 | 1001 | ||
1002 | err = 0; | 1002 | err = 0; |
1003 | out_elf_end: | 1003 | out_elf_end: |
1004 | elf_end(elf); | 1004 | elf_end(elf); |
1005 | out_close: | 1005 | out_close: |
1006 | close(fd); | 1006 | close(fd); |
1007 | 1007 | ||
1008 | if (err == 0) | 1008 | if (err == 0) |
1009 | return nr; | 1009 | return nr; |
1010 | out: | 1010 | out: |
1011 | pr_debug("%s: problems reading %s PLT info.\n", | 1011 | pr_debug("%s: problems reading %s PLT info.\n", |
1012 | __func__, dso->long_name); | 1012 | __func__, dso->long_name); |
1013 | return 0; | 1013 | return 0; |
1014 | } | 1014 | } |
1015 | 1015 | ||
1016 | static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type) | 1016 | static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type) |
1017 | { | 1017 | { |
1018 | switch (type) { | 1018 | switch (type) { |
1019 | case MAP__FUNCTION: | 1019 | case MAP__FUNCTION: |
1020 | return elf_sym__is_function(sym); | 1020 | return elf_sym__is_function(sym); |
1021 | case MAP__VARIABLE: | 1021 | case MAP__VARIABLE: |
1022 | return elf_sym__is_object(sym); | 1022 | return elf_sym__is_object(sym); |
1023 | default: | 1023 | default: |
1024 | return false; | 1024 | return false; |
1025 | } | 1025 | } |
1026 | } | 1026 | } |
1027 | 1027 | ||
1028 | static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs, | 1028 | static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs, |
1029 | enum map_type type) | 1029 | enum map_type type) |
1030 | { | 1030 | { |
1031 | switch (type) { | 1031 | switch (type) { |
1032 | case MAP__FUNCTION: | 1032 | case MAP__FUNCTION: |
1033 | return elf_sec__is_text(shdr, secstrs); | 1033 | return elf_sec__is_text(shdr, secstrs); |
1034 | case MAP__VARIABLE: | 1034 | case MAP__VARIABLE: |
1035 | return elf_sec__is_data(shdr, secstrs); | 1035 | return elf_sec__is_data(shdr, secstrs); |
1036 | default: | 1036 | default: |
1037 | return false; | 1037 | return false; |
1038 | } | 1038 | } |
1039 | } | 1039 | } |
1040 | 1040 | ||
1041 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | 1041 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) |
1042 | { | 1042 | { |
1043 | Elf_Scn *sec = NULL; | 1043 | Elf_Scn *sec = NULL; |
1044 | GElf_Shdr shdr; | 1044 | GElf_Shdr shdr; |
1045 | size_t cnt = 1; | 1045 | size_t cnt = 1; |
1046 | 1046 | ||
1047 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | 1047 | while ((sec = elf_nextscn(elf, sec)) != NULL) { |
1048 | gelf_getshdr(sec, &shdr); | 1048 | gelf_getshdr(sec, &shdr); |
1049 | 1049 | ||
1050 | if ((addr >= shdr.sh_addr) && | 1050 | if ((addr >= shdr.sh_addr) && |
1051 | (addr < (shdr.sh_addr + shdr.sh_size))) | 1051 | (addr < (shdr.sh_addr + shdr.sh_size))) |
1052 | return cnt; | 1052 | return cnt; |
1053 | 1053 | ||
1054 | ++cnt; | 1054 | ++cnt; |
1055 | } | 1055 | } |
1056 | 1056 | ||
1057 | return -1; | 1057 | return -1; |
1058 | } | 1058 | } |
1059 | 1059 | ||
1060 | static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | 1060 | static int dso__load_sym(struct dso *dso, struct map *map, const char *name, |
1061 | int fd, symbol_filter_t filter, int kmodule, | 1061 | int fd, symbol_filter_t filter, int kmodule, |
1062 | int want_symtab) | 1062 | int want_symtab) |
1063 | { | 1063 | { |
1064 | struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL; | 1064 | struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL; |
1065 | struct map *curr_map = map; | 1065 | struct map *curr_map = map; |
1066 | struct dso *curr_dso = dso; | 1066 | struct dso *curr_dso = dso; |
1067 | Elf_Data *symstrs, *secstrs; | 1067 | Elf_Data *symstrs, *secstrs; |
1068 | uint32_t nr_syms; | 1068 | uint32_t nr_syms; |
1069 | int err = -1; | 1069 | int err = -1; |
1070 | uint32_t idx; | 1070 | uint32_t idx; |
1071 | GElf_Ehdr ehdr; | 1071 | GElf_Ehdr ehdr; |
1072 | GElf_Shdr shdr, opdshdr; | 1072 | GElf_Shdr shdr, opdshdr; |
1073 | Elf_Data *syms, *opddata = NULL; | 1073 | Elf_Data *syms, *opddata = NULL; |
1074 | GElf_Sym sym; | 1074 | GElf_Sym sym; |
1075 | Elf_Scn *sec, *sec_strndx, *opdsec; | 1075 | Elf_Scn *sec, *sec_strndx, *opdsec; |
1076 | Elf *elf; | 1076 | Elf *elf; |
1077 | int nr = 0; | 1077 | int nr = 0; |
1078 | size_t opdidx = 0; | 1078 | size_t opdidx = 0; |
1079 | 1079 | ||
1080 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 1080 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
1081 | if (elf == NULL) { | 1081 | if (elf == NULL) { |
1082 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); | 1082 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); |
1083 | goto out_close; | 1083 | goto out_close; |
1084 | } | 1084 | } |
1085 | 1085 | ||
1086 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 1086 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
1087 | pr_debug("%s: cannot get elf header.\n", __func__); | 1087 | pr_debug("%s: cannot get elf header.\n", __func__); |
1088 | goto out_elf_end; | 1088 | goto out_elf_end; |
1089 | } | 1089 | } |
1090 | 1090 | ||
1091 | /* Always reject images with a mismatched build-id: */ | 1091 | /* Always reject images with a mismatched build-id: */ |
1092 | if (dso->has_build_id) { | 1092 | if (dso->has_build_id) { |
1093 | u8 build_id[BUILD_ID_SIZE]; | 1093 | u8 build_id[BUILD_ID_SIZE]; |
1094 | 1094 | ||
1095 | if (elf_read_build_id(elf, build_id, | 1095 | if (elf_read_build_id(elf, build_id, |
1096 | BUILD_ID_SIZE) != BUILD_ID_SIZE) | 1096 | BUILD_ID_SIZE) != BUILD_ID_SIZE) |
1097 | goto out_elf_end; | 1097 | goto out_elf_end; |
1098 | 1098 | ||
1099 | if (!dso__build_id_equal(dso, build_id)) | 1099 | if (!dso__build_id_equal(dso, build_id)) |
1100 | goto out_elf_end; | 1100 | goto out_elf_end; |
1101 | } | 1101 | } |
1102 | 1102 | ||
1103 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 1103 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
1104 | if (sec == NULL) { | 1104 | if (sec == NULL) { |
1105 | if (want_symtab) | 1105 | if (want_symtab) |
1106 | goto out_elf_end; | 1106 | goto out_elf_end; |
1107 | 1107 | ||
1108 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | 1108 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
1109 | if (sec == NULL) | 1109 | if (sec == NULL) |
1110 | goto out_elf_end; | 1110 | goto out_elf_end; |
1111 | } | 1111 | } |
1112 | 1112 | ||
1113 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | 1113 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); |
1114 | if (opdsec) | 1114 | if (opdsec) |
1115 | opddata = elf_rawdata(opdsec, NULL); | 1115 | opddata = elf_rawdata(opdsec, NULL); |
1116 | 1116 | ||
1117 | syms = elf_getdata(sec, NULL); | 1117 | syms = elf_getdata(sec, NULL); |
1118 | if (syms == NULL) | 1118 | if (syms == NULL) |
1119 | goto out_elf_end; | 1119 | goto out_elf_end; |
1120 | 1120 | ||
1121 | sec = elf_getscn(elf, shdr.sh_link); | 1121 | sec = elf_getscn(elf, shdr.sh_link); |
1122 | if (sec == NULL) | 1122 | if (sec == NULL) |
1123 | goto out_elf_end; | 1123 | goto out_elf_end; |
1124 | 1124 | ||
1125 | symstrs = elf_getdata(sec, NULL); | 1125 | symstrs = elf_getdata(sec, NULL); |
1126 | if (symstrs == NULL) | 1126 | if (symstrs == NULL) |
1127 | goto out_elf_end; | 1127 | goto out_elf_end; |
1128 | 1128 | ||
1129 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); | 1129 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); |
1130 | if (sec_strndx == NULL) | 1130 | if (sec_strndx == NULL) |
1131 | goto out_elf_end; | 1131 | goto out_elf_end; |
1132 | 1132 | ||
1133 | secstrs = elf_getdata(sec_strndx, NULL); | 1133 | secstrs = elf_getdata(sec_strndx, NULL); |
1134 | if (secstrs == NULL) | 1134 | if (secstrs == NULL) |
1135 | goto out_elf_end; | 1135 | goto out_elf_end; |
1136 | 1136 | ||
1137 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 1137 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
1138 | 1138 | ||
1139 | memset(&sym, 0, sizeof(sym)); | 1139 | memset(&sym, 0, sizeof(sym)); |
1140 | if (dso->kernel == DSO_TYPE_USER) { | 1140 | if (dso->kernel == DSO_TYPE_USER) { |
1141 | dso->adjust_symbols = (ehdr.e_type == ET_EXEC || | 1141 | dso->adjust_symbols = (ehdr.e_type == ET_EXEC || |
1142 | elf_section_by_name(elf, &ehdr, &shdr, | 1142 | elf_section_by_name(elf, &ehdr, &shdr, |
1143 | ".gnu.prelink_undo", | 1143 | ".gnu.prelink_undo", |
1144 | NULL) != NULL); | 1144 | NULL) != NULL); |
1145 | } else { | 1145 | } else { |
1146 | dso->adjust_symbols = 0; | 1146 | dso->adjust_symbols = 0; |
1147 | } | 1147 | } |
1148 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | 1148 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { |
1149 | struct symbol *f; | 1149 | struct symbol *f; |
1150 | const char *elf_name = elf_sym__name(&sym, symstrs); | 1150 | const char *elf_name = elf_sym__name(&sym, symstrs); |
1151 | char *demangled = NULL; | 1151 | char *demangled = NULL; |
1152 | int is_label = elf_sym__is_label(&sym); | 1152 | int is_label = elf_sym__is_label(&sym); |
1153 | const char *section_name; | 1153 | const char *section_name; |
1154 | 1154 | ||
1155 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | 1155 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && |
1156 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | 1156 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) |
1157 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | 1157 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; |
1158 | 1158 | ||
1159 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | 1159 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
1160 | continue; | 1160 | continue; |
1161 | 1161 | ||
1162 | /* Reject ARM ELF "mapping symbols": these aren't unique and | 1162 | /* Reject ARM ELF "mapping symbols": these aren't unique and |
1163 | * don't identify functions, so will confuse the profile | 1163 | * don't identify functions, so will confuse the profile |
1164 | * output: */ | 1164 | * output: */ |
1165 | if (ehdr.e_machine == EM_ARM) { | 1165 | if (ehdr.e_machine == EM_ARM) { |
1166 | if (!strcmp(elf_name, "$a") || | 1166 | if (!strcmp(elf_name, "$a") || |
1167 | !strcmp(elf_name, "$d") || | 1167 | !strcmp(elf_name, "$d") || |
1168 | !strcmp(elf_name, "$t")) | 1168 | !strcmp(elf_name, "$t")) |
1169 | continue; | 1169 | continue; |
1170 | } | 1170 | } |
1171 | 1171 | ||
1172 | if (opdsec && sym.st_shndx == opdidx) { | 1172 | if (opdsec && sym.st_shndx == opdidx) { |
1173 | u32 offset = sym.st_value - opdshdr.sh_addr; | 1173 | u32 offset = sym.st_value - opdshdr.sh_addr; |
1174 | u64 *opd = opddata->d_buf + offset; | 1174 | u64 *opd = opddata->d_buf + offset; |
1175 | sym.st_value = *opd; | 1175 | sym.st_value = *opd; |
1176 | sym.st_shndx = elf_addr_to_index(elf, sym.st_value); | 1176 | sym.st_shndx = elf_addr_to_index(elf, sym.st_value); |
1177 | } | 1177 | } |
1178 | 1178 | ||
1179 | sec = elf_getscn(elf, sym.st_shndx); | 1179 | sec = elf_getscn(elf, sym.st_shndx); |
1180 | if (!sec) | 1180 | if (!sec) |
1181 | goto out_elf_end; | 1181 | goto out_elf_end; |
1182 | 1182 | ||
1183 | gelf_getshdr(sec, &shdr); | 1183 | gelf_getshdr(sec, &shdr); |
1184 | 1184 | ||
1185 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) | 1185 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) |
1186 | continue; | 1186 | continue; |
1187 | 1187 | ||
1188 | section_name = elf_sec__name(&shdr, secstrs); | 1188 | section_name = elf_sec__name(&shdr, secstrs); |
1189 | 1189 | ||
1190 | /* On ARM, symbols for thumb functions have 1 added to | 1190 | /* On ARM, symbols for thumb functions have 1 added to |
1191 | * the symbol address as a flag - remove it */ | 1191 | * the symbol address as a flag - remove it */ |
1192 | if ((ehdr.e_machine == EM_ARM) && | 1192 | if ((ehdr.e_machine == EM_ARM) && |
1193 | (map->type == MAP__FUNCTION) && | 1193 | (map->type == MAP__FUNCTION) && |
1194 | (sym.st_value & 1)) | 1194 | (sym.st_value & 1)) |
1195 | --sym.st_value; | 1195 | --sym.st_value; |
1196 | 1196 | ||
1197 | if (dso->kernel != DSO_TYPE_USER || kmodule) { | 1197 | if (dso->kernel != DSO_TYPE_USER || kmodule) { |
1198 | char dso_name[PATH_MAX]; | 1198 | char dso_name[PATH_MAX]; |
1199 | 1199 | ||
1200 | if (strcmp(section_name, | 1200 | if (strcmp(section_name, |
1201 | (curr_dso->short_name + | 1201 | (curr_dso->short_name + |
1202 | dso->short_name_len)) == 0) | 1202 | dso->short_name_len)) == 0) |
1203 | goto new_symbol; | 1203 | goto new_symbol; |
1204 | 1204 | ||
1205 | if (strcmp(section_name, ".text") == 0) { | 1205 | if (strcmp(section_name, ".text") == 0) { |
1206 | curr_map = map; | 1206 | curr_map = map; |
1207 | curr_dso = dso; | 1207 | curr_dso = dso; |
1208 | goto new_symbol; | 1208 | goto new_symbol; |
1209 | } | 1209 | } |
1210 | 1210 | ||
1211 | snprintf(dso_name, sizeof(dso_name), | 1211 | snprintf(dso_name, sizeof(dso_name), |
1212 | "%s%s", dso->short_name, section_name); | 1212 | "%s%s", dso->short_name, section_name); |
1213 | 1213 | ||
1214 | curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name); | 1214 | curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name); |
1215 | if (curr_map == NULL) { | 1215 | if (curr_map == NULL) { |
1216 | u64 start = sym.st_value; | 1216 | u64 start = sym.st_value; |
1217 | 1217 | ||
1218 | if (kmodule) | 1218 | if (kmodule) |
1219 | start += map->start + shdr.sh_offset; | 1219 | start += map->start + shdr.sh_offset; |
1220 | 1220 | ||
1221 | curr_dso = dso__new(dso_name); | 1221 | curr_dso = dso__new(dso_name); |
1222 | if (curr_dso == NULL) | 1222 | if (curr_dso == NULL) |
1223 | goto out_elf_end; | 1223 | goto out_elf_end; |
1224 | curr_dso->kernel = dso->kernel; | 1224 | curr_dso->kernel = dso->kernel; |
1225 | curr_dso->long_name = dso->long_name; | 1225 | curr_dso->long_name = dso->long_name; |
1226 | curr_dso->long_name_len = dso->long_name_len; | 1226 | curr_dso->long_name_len = dso->long_name_len; |
1227 | curr_map = map__new2(start, curr_dso, | 1227 | curr_map = map__new2(start, curr_dso, |
1228 | map->type); | 1228 | map->type); |
1229 | if (curr_map == NULL) { | 1229 | if (curr_map == NULL) { |
1230 | dso__delete(curr_dso); | 1230 | dso__delete(curr_dso); |
1231 | goto out_elf_end; | 1231 | goto out_elf_end; |
1232 | } | 1232 | } |
1233 | curr_map->map_ip = identity__map_ip; | 1233 | curr_map->map_ip = identity__map_ip; |
1234 | curr_map->unmap_ip = identity__map_ip; | 1234 | curr_map->unmap_ip = identity__map_ip; |
1235 | curr_dso->symtab_type = dso->symtab_type; | 1235 | curr_dso->symtab_type = dso->symtab_type; |
1236 | map_groups__insert(kmap->kmaps, curr_map); | 1236 | map_groups__insert(kmap->kmaps, curr_map); |
1237 | dsos__add(&dso->node, curr_dso); | 1237 | dsos__add(&dso->node, curr_dso); |
1238 | dso__set_loaded(curr_dso, map->type); | 1238 | dso__set_loaded(curr_dso, map->type); |
1239 | } else | 1239 | } else |
1240 | curr_dso = curr_map->dso; | 1240 | curr_dso = curr_map->dso; |
1241 | 1241 | ||
1242 | goto new_symbol; | 1242 | goto new_symbol; |
1243 | } | 1243 | } |
1244 | 1244 | ||
1245 | if (curr_dso->adjust_symbols) { | 1245 | if (curr_dso->adjust_symbols) { |
1246 | pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " " | 1246 | pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " " |
1247 | "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__, | 1247 | "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__, |
1248 | (u64)sym.st_value, (u64)shdr.sh_addr, | 1248 | (u64)sym.st_value, (u64)shdr.sh_addr, |
1249 | (u64)shdr.sh_offset); | 1249 | (u64)shdr.sh_offset); |
1250 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | 1250 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; |
1251 | } | 1251 | } |
1252 | /* | 1252 | /* |
1253 | * We need to figure out if the object was created from C++ sources | 1253 | * We need to figure out if the object was created from C++ sources |
1254 | * DWARF DW_compile_unit has this, but we don't always have access | 1254 | * DWARF DW_compile_unit has this, but we don't always have access |
1255 | * to it... | 1255 | * to it... |
1256 | */ | 1256 | */ |
1257 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | 1257 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); |
1258 | if (demangled != NULL) | 1258 | if (demangled != NULL) |
1259 | elf_name = demangled; | 1259 | elf_name = demangled; |
1260 | new_symbol: | 1260 | new_symbol: |
1261 | f = symbol__new(sym.st_value, sym.st_size, | 1261 | f = symbol__new(sym.st_value, sym.st_size, |
1262 | GELF_ST_BIND(sym.st_info), elf_name); | 1262 | GELF_ST_BIND(sym.st_info), elf_name); |
1263 | free(demangled); | 1263 | free(demangled); |
1264 | if (!f) | 1264 | if (!f) |
1265 | goto out_elf_end; | 1265 | goto out_elf_end; |
1266 | 1266 | ||
1267 | if (filter && filter(curr_map, f)) | 1267 | if (filter && filter(curr_map, f)) |
1268 | symbol__delete(f); | 1268 | symbol__delete(f); |
1269 | else { | 1269 | else { |
1270 | symbols__insert(&curr_dso->symbols[curr_map->type], f); | 1270 | symbols__insert(&curr_dso->symbols[curr_map->type], f); |
1271 | nr++; | 1271 | nr++; |
1272 | } | 1272 | } |
1273 | } | 1273 | } |
1274 | 1274 | ||
1275 | /* | 1275 | /* |
1276 | * For misannotated, zeroed, ASM function sizes. | 1276 | * For misannotated, zeroed, ASM function sizes. |
1277 | */ | 1277 | */ |
1278 | if (nr > 0) { | 1278 | if (nr > 0) { |
1279 | symbols__fixup_end(&dso->symbols[map->type]); | 1279 | symbols__fixup_end(&dso->symbols[map->type]); |
1280 | if (kmap) { | 1280 | if (kmap) { |
1281 | /* | 1281 | /* |
1282 | * We need to fixup this here too because we create new | 1282 | * We need to fixup this here too because we create new |
1283 | * maps here, for things like vsyscall sections. | 1283 | * maps here, for things like vsyscall sections. |
1284 | */ | 1284 | */ |
1285 | __map_groups__fixup_end(kmap->kmaps, map->type); | 1285 | __map_groups__fixup_end(kmap->kmaps, map->type); |
1286 | } | 1286 | } |
1287 | } | 1287 | } |
1288 | err = nr; | 1288 | err = nr; |
1289 | out_elf_end: | 1289 | out_elf_end: |
1290 | elf_end(elf); | 1290 | elf_end(elf); |
1291 | out_close: | 1291 | out_close: |
1292 | return err; | 1292 | return err; |
1293 | } | 1293 | } |
1294 | 1294 | ||
1295 | static bool dso__build_id_equal(const struct dso *dso, u8 *build_id) | 1295 | static bool dso__build_id_equal(const struct dso *dso, u8 *build_id) |
1296 | { | 1296 | { |
1297 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; | 1297 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; |
1298 | } | 1298 | } |
1299 | 1299 | ||
1300 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | 1300 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits) |
1301 | { | 1301 | { |
1302 | bool have_build_id = false; | 1302 | bool have_build_id = false; |
1303 | struct dso *pos; | 1303 | struct dso *pos; |
1304 | 1304 | ||
1305 | list_for_each_entry(pos, head, node) { | 1305 | list_for_each_entry(pos, head, node) { |
1306 | if (with_hits && !pos->hit) | 1306 | if (with_hits && !pos->hit) |
1307 | continue; | 1307 | continue; |
1308 | if (pos->has_build_id) { | 1308 | if (pos->has_build_id) { |
1309 | have_build_id = true; | 1309 | have_build_id = true; |
1310 | continue; | 1310 | continue; |
1311 | } | 1311 | } |
1312 | if (filename__read_build_id(pos->long_name, pos->build_id, | 1312 | if (filename__read_build_id(pos->long_name, pos->build_id, |
1313 | sizeof(pos->build_id)) > 0) { | 1313 | sizeof(pos->build_id)) > 0) { |
1314 | have_build_id = true; | 1314 | have_build_id = true; |
1315 | pos->has_build_id = true; | 1315 | pos->has_build_id = true; |
1316 | } | 1316 | } |
1317 | } | 1317 | } |
1318 | 1318 | ||
1319 | return have_build_id; | 1319 | return have_build_id; |
1320 | } | 1320 | } |
1321 | 1321 | ||
1322 | /* | 1322 | /* |
1323 | * Align offset to 4 bytes as needed for note name and descriptor data. | 1323 | * Align offset to 4 bytes as needed for note name and descriptor data. |
1324 | */ | 1324 | */ |
1325 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | 1325 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) |
1326 | 1326 | ||
1327 | static int elf_read_build_id(Elf *elf, void *bf, size_t size) | 1327 | static int elf_read_build_id(Elf *elf, void *bf, size_t size) |
1328 | { | 1328 | { |
1329 | int err = -1; | 1329 | int err = -1; |
1330 | GElf_Ehdr ehdr; | 1330 | GElf_Ehdr ehdr; |
1331 | GElf_Shdr shdr; | 1331 | GElf_Shdr shdr; |
1332 | Elf_Data *data; | 1332 | Elf_Data *data; |
1333 | Elf_Scn *sec; | 1333 | Elf_Scn *sec; |
1334 | Elf_Kind ek; | 1334 | Elf_Kind ek; |
1335 | void *ptr; | 1335 | void *ptr; |
1336 | 1336 | ||
1337 | if (size < BUILD_ID_SIZE) | 1337 | if (size < BUILD_ID_SIZE) |
1338 | goto out; | 1338 | goto out; |
1339 | 1339 | ||
1340 | ek = elf_kind(elf); | 1340 | ek = elf_kind(elf); |
1341 | if (ek != ELF_K_ELF) | 1341 | if (ek != ELF_K_ELF) |
1342 | goto out; | 1342 | goto out; |
1343 | 1343 | ||
1344 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 1344 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
1345 | pr_err("%s: cannot get elf header.\n", __func__); | 1345 | pr_err("%s: cannot get elf header.\n", __func__); |
1346 | goto out; | 1346 | goto out; |
1347 | } | 1347 | } |
1348 | 1348 | ||
1349 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 1349 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
1350 | ".note.gnu.build-id", NULL); | 1350 | ".note.gnu.build-id", NULL); |
1351 | if (sec == NULL) { | 1351 | if (sec == NULL) { |
1352 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 1352 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
1353 | ".notes", NULL); | 1353 | ".notes", NULL); |
1354 | if (sec == NULL) | 1354 | if (sec == NULL) |
1355 | goto out; | 1355 | goto out; |
1356 | } | 1356 | } |
1357 | 1357 | ||
1358 | data = elf_getdata(sec, NULL); | 1358 | data = elf_getdata(sec, NULL); |
1359 | if (data == NULL) | 1359 | if (data == NULL) |
1360 | goto out; | 1360 | goto out; |
1361 | 1361 | ||
1362 | ptr = data->d_buf; | 1362 | ptr = data->d_buf; |
1363 | while (ptr < (data->d_buf + data->d_size)) { | 1363 | while (ptr < (data->d_buf + data->d_size)) { |
1364 | GElf_Nhdr *nhdr = ptr; | 1364 | GElf_Nhdr *nhdr = ptr; |
1365 | int namesz = NOTE_ALIGN(nhdr->n_namesz), | 1365 | int namesz = NOTE_ALIGN(nhdr->n_namesz), |
1366 | descsz = NOTE_ALIGN(nhdr->n_descsz); | 1366 | descsz = NOTE_ALIGN(nhdr->n_descsz); |
1367 | const char *name; | 1367 | const char *name; |
1368 | 1368 | ||
1369 | ptr += sizeof(*nhdr); | 1369 | ptr += sizeof(*nhdr); |
1370 | name = ptr; | 1370 | name = ptr; |
1371 | ptr += namesz; | 1371 | ptr += namesz; |
1372 | if (nhdr->n_type == NT_GNU_BUILD_ID && | 1372 | if (nhdr->n_type == NT_GNU_BUILD_ID && |
1373 | nhdr->n_namesz == sizeof("GNU")) { | 1373 | nhdr->n_namesz == sizeof("GNU")) { |
1374 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | 1374 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { |
1375 | memcpy(bf, ptr, BUILD_ID_SIZE); | 1375 | memcpy(bf, ptr, BUILD_ID_SIZE); |
1376 | err = BUILD_ID_SIZE; | 1376 | err = BUILD_ID_SIZE; |
1377 | break; | 1377 | break; |
1378 | } | 1378 | } |
1379 | } | 1379 | } |
1380 | ptr += descsz; | 1380 | ptr += descsz; |
1381 | } | 1381 | } |
1382 | 1382 | ||
1383 | out: | 1383 | out: |
1384 | return err; | 1384 | return err; |
1385 | } | 1385 | } |
1386 | 1386 | ||
1387 | int filename__read_build_id(const char *filename, void *bf, size_t size) | 1387 | int filename__read_build_id(const char *filename, void *bf, size_t size) |
1388 | { | 1388 | { |
1389 | int fd, err = -1; | 1389 | int fd, err = -1; |
1390 | Elf *elf; | 1390 | Elf *elf; |
1391 | 1391 | ||
1392 | if (size < BUILD_ID_SIZE) | 1392 | if (size < BUILD_ID_SIZE) |
1393 | goto out; | 1393 | goto out; |
1394 | 1394 | ||
1395 | fd = open(filename, O_RDONLY); | 1395 | fd = open(filename, O_RDONLY); |
1396 | if (fd < 0) | 1396 | if (fd < 0) |
1397 | goto out; | 1397 | goto out; |
1398 | 1398 | ||
1399 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 1399 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
1400 | if (elf == NULL) { | 1400 | if (elf == NULL) { |
1401 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | 1401 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); |
1402 | goto out_close; | 1402 | goto out_close; |
1403 | } | 1403 | } |
1404 | 1404 | ||
1405 | err = elf_read_build_id(elf, bf, size); | 1405 | err = elf_read_build_id(elf, bf, size); |
1406 | 1406 | ||
1407 | elf_end(elf); | 1407 | elf_end(elf); |
1408 | out_close: | 1408 | out_close: |
1409 | close(fd); | 1409 | close(fd); |
1410 | out: | 1410 | out: |
1411 | return err; | 1411 | return err; |
1412 | } | 1412 | } |
1413 | 1413 | ||
1414 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | 1414 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) |
1415 | { | 1415 | { |
1416 | int fd, err = -1; | 1416 | int fd, err = -1; |
1417 | 1417 | ||
1418 | if (size < BUILD_ID_SIZE) | 1418 | if (size < BUILD_ID_SIZE) |
1419 | goto out; | 1419 | goto out; |
1420 | 1420 | ||
1421 | fd = open(filename, O_RDONLY); | 1421 | fd = open(filename, O_RDONLY); |
1422 | if (fd < 0) | 1422 | if (fd < 0) |
1423 | goto out; | 1423 | goto out; |
1424 | 1424 | ||
1425 | while (1) { | 1425 | while (1) { |
1426 | char bf[BUFSIZ]; | 1426 | char bf[BUFSIZ]; |
1427 | GElf_Nhdr nhdr; | 1427 | GElf_Nhdr nhdr; |
1428 | int namesz, descsz; | 1428 | int namesz, descsz; |
1429 | 1429 | ||
1430 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | 1430 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) |
1431 | break; | 1431 | break; |
1432 | 1432 | ||
1433 | namesz = NOTE_ALIGN(nhdr.n_namesz); | 1433 | namesz = NOTE_ALIGN(nhdr.n_namesz); |
1434 | descsz = NOTE_ALIGN(nhdr.n_descsz); | 1434 | descsz = NOTE_ALIGN(nhdr.n_descsz); |
1435 | if (nhdr.n_type == NT_GNU_BUILD_ID && | 1435 | if (nhdr.n_type == NT_GNU_BUILD_ID && |
1436 | nhdr.n_namesz == sizeof("GNU")) { | 1436 | nhdr.n_namesz == sizeof("GNU")) { |
1437 | if (read(fd, bf, namesz) != namesz) | 1437 | if (read(fd, bf, namesz) != namesz) |
1438 | break; | 1438 | break; |
1439 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | 1439 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { |
1440 | if (read(fd, build_id, | 1440 | if (read(fd, build_id, |
1441 | BUILD_ID_SIZE) == BUILD_ID_SIZE) { | 1441 | BUILD_ID_SIZE) == BUILD_ID_SIZE) { |
1442 | err = 0; | 1442 | err = 0; |
1443 | break; | 1443 | break; |
1444 | } | 1444 | } |
1445 | } else if (read(fd, bf, descsz) != descsz) | 1445 | } else if (read(fd, bf, descsz) != descsz) |
1446 | break; | 1446 | break; |
1447 | } else { | 1447 | } else { |
1448 | int n = namesz + descsz; | 1448 | int n = namesz + descsz; |
1449 | if (read(fd, bf, n) != n) | 1449 | if (read(fd, bf, n) != n) |
1450 | break; | 1450 | break; |
1451 | } | 1451 | } |
1452 | } | 1452 | } |
1453 | close(fd); | 1453 | close(fd); |
1454 | out: | 1454 | out: |
1455 | return err; | 1455 | return err; |
1456 | } | 1456 | } |
1457 | 1457 | ||
1458 | char dso__symtab_origin(const struct dso *dso) | 1458 | char dso__symtab_origin(const struct dso *dso) |
1459 | { | 1459 | { |
1460 | static const char origin[] = { | 1460 | static const char origin[] = { |
1461 | [SYMTAB__KALLSYMS] = 'k', | 1461 | [SYMTAB__KALLSYMS] = 'k', |
1462 | [SYMTAB__JAVA_JIT] = 'j', | 1462 | [SYMTAB__JAVA_JIT] = 'j', |
1463 | [SYMTAB__BUILD_ID_CACHE] = 'B', | 1463 | [SYMTAB__BUILD_ID_CACHE] = 'B', |
1464 | [SYMTAB__FEDORA_DEBUGINFO] = 'f', | 1464 | [SYMTAB__FEDORA_DEBUGINFO] = 'f', |
1465 | [SYMTAB__UBUNTU_DEBUGINFO] = 'u', | 1465 | [SYMTAB__UBUNTU_DEBUGINFO] = 'u', |
1466 | [SYMTAB__BUILDID_DEBUGINFO] = 'b', | 1466 | [SYMTAB__BUILDID_DEBUGINFO] = 'b', |
1467 | [SYMTAB__SYSTEM_PATH_DSO] = 'd', | 1467 | [SYMTAB__SYSTEM_PATH_DSO] = 'd', |
1468 | [SYMTAB__SYSTEM_PATH_KMODULE] = 'K', | 1468 | [SYMTAB__SYSTEM_PATH_KMODULE] = 'K', |
1469 | [SYMTAB__GUEST_KALLSYMS] = 'g', | 1469 | [SYMTAB__GUEST_KALLSYMS] = 'g', |
1470 | [SYMTAB__GUEST_KMODULE] = 'G', | 1470 | [SYMTAB__GUEST_KMODULE] = 'G', |
1471 | }; | 1471 | }; |
1472 | 1472 | ||
1473 | if (dso == NULL || dso->symtab_type == SYMTAB__NOT_FOUND) | 1473 | if (dso == NULL || dso->symtab_type == SYMTAB__NOT_FOUND) |
1474 | return '!'; | 1474 | return '!'; |
1475 | return origin[dso->symtab_type]; | 1475 | return origin[dso->symtab_type]; |
1476 | } | 1476 | } |
1477 | 1477 | ||
1478 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | 1478 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) |
1479 | { | 1479 | { |
1480 | int size = PATH_MAX; | 1480 | int size = PATH_MAX; |
1481 | char *name; | 1481 | char *name; |
1482 | int ret = -1; | 1482 | int ret = -1; |
1483 | int fd; | 1483 | int fd; |
1484 | struct machine *machine; | 1484 | struct machine *machine; |
1485 | const char *root_dir; | 1485 | const char *root_dir; |
1486 | int want_symtab; | 1486 | int want_symtab; |
1487 | 1487 | ||
1488 | dso__set_loaded(dso, map->type); | 1488 | dso__set_loaded(dso, map->type); |
1489 | 1489 | ||
1490 | if (dso->kernel == DSO_TYPE_KERNEL) | 1490 | if (dso->kernel == DSO_TYPE_KERNEL) |
1491 | return dso__load_kernel_sym(dso, map, filter); | 1491 | return dso__load_kernel_sym(dso, map, filter); |
1492 | else if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1492 | else if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1493 | return dso__load_guest_kernel_sym(dso, map, filter); | 1493 | return dso__load_guest_kernel_sym(dso, map, filter); |
1494 | 1494 | ||
1495 | if (map->groups && map->groups->machine) | 1495 | if (map->groups && map->groups->machine) |
1496 | machine = map->groups->machine; | 1496 | machine = map->groups->machine; |
1497 | else | 1497 | else |
1498 | machine = NULL; | 1498 | machine = NULL; |
1499 | 1499 | ||
1500 | name = malloc(size); | 1500 | name = malloc(size); |
1501 | if (!name) | 1501 | if (!name) |
1502 | return -1; | 1502 | return -1; |
1503 | 1503 | ||
1504 | dso->adjust_symbols = 0; | 1504 | dso->adjust_symbols = 0; |
1505 | 1505 | ||
1506 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { | 1506 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { |
1507 | struct stat st; | ||
1508 | |||
1509 | if (stat(dso->name, &st) < 0) | ||
1510 | return -1; | ||
1511 | |||
1512 | if (st.st_uid && (st.st_uid != geteuid())) { | ||
1513 | pr_warning("File %s not owned by current user or root, " | ||
1514 | "ignoring it.\n", dso->name); | ||
1515 | return -1; | ||
1516 | } | ||
1517 | |||
1507 | ret = dso__load_perf_map(dso, map, filter); | 1518 | ret = dso__load_perf_map(dso, map, filter); |
1508 | dso->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : | 1519 | dso->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : |
1509 | SYMTAB__NOT_FOUND; | 1520 | SYMTAB__NOT_FOUND; |
1510 | return ret; | 1521 | return ret; |
1511 | } | 1522 | } |
1512 | 1523 | ||
1513 | /* Iterate over candidate debug images. | 1524 | /* Iterate over candidate debug images. |
1514 | * On the first pass, only load images if they have a full symtab. | 1525 | * On the first pass, only load images if they have a full symtab. |
1515 | * Failing that, do a second pass where we accept .dynsym also | 1526 | * Failing that, do a second pass where we accept .dynsym also |
1516 | */ | 1527 | */ |
1517 | want_symtab = 1; | 1528 | want_symtab = 1; |
1518 | restart: | 1529 | restart: |
1519 | for (dso->symtab_type = SYMTAB__BUILD_ID_CACHE; | 1530 | for (dso->symtab_type = SYMTAB__BUILD_ID_CACHE; |
1520 | dso->symtab_type != SYMTAB__NOT_FOUND; | 1531 | dso->symtab_type != SYMTAB__NOT_FOUND; |
1521 | dso->symtab_type++) { | 1532 | dso->symtab_type++) { |
1522 | switch (dso->symtab_type) { | 1533 | switch (dso->symtab_type) { |
1523 | case SYMTAB__BUILD_ID_CACHE: | 1534 | case SYMTAB__BUILD_ID_CACHE: |
1524 | /* skip the locally configured cache if a symfs is given */ | 1535 | /* skip the locally configured cache if a symfs is given */ |
1525 | if (symbol_conf.symfs[0] || | 1536 | if (symbol_conf.symfs[0] || |
1526 | (dso__build_id_filename(dso, name, size) == NULL)) { | 1537 | (dso__build_id_filename(dso, name, size) == NULL)) { |
1527 | continue; | 1538 | continue; |
1528 | } | 1539 | } |
1529 | break; | 1540 | break; |
1530 | case SYMTAB__FEDORA_DEBUGINFO: | 1541 | case SYMTAB__FEDORA_DEBUGINFO: |
1531 | snprintf(name, size, "%s/usr/lib/debug%s.debug", | 1542 | snprintf(name, size, "%s/usr/lib/debug%s.debug", |
1532 | symbol_conf.symfs, dso->long_name); | 1543 | symbol_conf.symfs, dso->long_name); |
1533 | break; | 1544 | break; |
1534 | case SYMTAB__UBUNTU_DEBUGINFO: | 1545 | case SYMTAB__UBUNTU_DEBUGINFO: |
1535 | snprintf(name, size, "%s/usr/lib/debug%s", | 1546 | snprintf(name, size, "%s/usr/lib/debug%s", |
1536 | symbol_conf.symfs, dso->long_name); | 1547 | symbol_conf.symfs, dso->long_name); |
1537 | break; | 1548 | break; |
1538 | case SYMTAB__BUILDID_DEBUGINFO: { | 1549 | case SYMTAB__BUILDID_DEBUGINFO: { |
1539 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 1550 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
1540 | 1551 | ||
1541 | if (!dso->has_build_id) | 1552 | if (!dso->has_build_id) |
1542 | continue; | 1553 | continue; |
1543 | 1554 | ||
1544 | build_id__sprintf(dso->build_id, | 1555 | build_id__sprintf(dso->build_id, |
1545 | sizeof(dso->build_id), | 1556 | sizeof(dso->build_id), |
1546 | build_id_hex); | 1557 | build_id_hex); |
1547 | snprintf(name, size, | 1558 | snprintf(name, size, |
1548 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | 1559 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", |
1549 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | 1560 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); |
1550 | } | 1561 | } |
1551 | break; | 1562 | break; |
1552 | case SYMTAB__SYSTEM_PATH_DSO: | 1563 | case SYMTAB__SYSTEM_PATH_DSO: |
1553 | snprintf(name, size, "%s%s", | 1564 | snprintf(name, size, "%s%s", |
1554 | symbol_conf.symfs, dso->long_name); | 1565 | symbol_conf.symfs, dso->long_name); |
1555 | break; | 1566 | break; |
1556 | case SYMTAB__GUEST_KMODULE: | 1567 | case SYMTAB__GUEST_KMODULE: |
1557 | if (map->groups && machine) | 1568 | if (map->groups && machine) |
1558 | root_dir = machine->root_dir; | 1569 | root_dir = machine->root_dir; |
1559 | else | 1570 | else |
1560 | root_dir = ""; | 1571 | root_dir = ""; |
1561 | snprintf(name, size, "%s%s%s", symbol_conf.symfs, | 1572 | snprintf(name, size, "%s%s%s", symbol_conf.symfs, |
1562 | root_dir, dso->long_name); | 1573 | root_dir, dso->long_name); |
1563 | break; | 1574 | break; |
1564 | 1575 | ||
1565 | case SYMTAB__SYSTEM_PATH_KMODULE: | 1576 | case SYMTAB__SYSTEM_PATH_KMODULE: |
1566 | snprintf(name, size, "%s%s", symbol_conf.symfs, | 1577 | snprintf(name, size, "%s%s", symbol_conf.symfs, |
1567 | dso->long_name); | 1578 | dso->long_name); |
1568 | break; | 1579 | break; |
1569 | default:; | 1580 | default:; |
1570 | } | 1581 | } |
1571 | 1582 | ||
1572 | /* Name is now the name of the next image to try */ | 1583 | /* Name is now the name of the next image to try */ |
1573 | fd = open(name, O_RDONLY); | 1584 | fd = open(name, O_RDONLY); |
1574 | if (fd < 0) | 1585 | if (fd < 0) |
1575 | continue; | 1586 | continue; |
1576 | 1587 | ||
1577 | ret = dso__load_sym(dso, map, name, fd, filter, 0, | 1588 | ret = dso__load_sym(dso, map, name, fd, filter, 0, |
1578 | want_symtab); | 1589 | want_symtab); |
1579 | close(fd); | 1590 | close(fd); |
1580 | 1591 | ||
1581 | /* | 1592 | /* |
1582 | * Some people seem to have debuginfo files _WITHOUT_ debug | 1593 | * Some people seem to have debuginfo files _WITHOUT_ debug |
1583 | * info!?!? | 1594 | * info!?!? |
1584 | */ | 1595 | */ |
1585 | if (!ret) | 1596 | if (!ret) |
1586 | continue; | 1597 | continue; |
1587 | 1598 | ||
1588 | if (ret > 0) { | 1599 | if (ret > 0) { |
1589 | int nr_plt = dso__synthesize_plt_symbols(dso, map, | 1600 | int nr_plt = dso__synthesize_plt_symbols(dso, map, |
1590 | filter); | 1601 | filter); |
1591 | if (nr_plt > 0) | 1602 | if (nr_plt > 0) |
1592 | ret += nr_plt; | 1603 | ret += nr_plt; |
1593 | break; | 1604 | break; |
1594 | } | 1605 | } |
1595 | } | 1606 | } |
1596 | 1607 | ||
1597 | /* | 1608 | /* |
1598 | * If we wanted a full symtab but no image had one, | 1609 | * If we wanted a full symtab but no image had one, |
1599 | * relax our requirements and repeat the search. | 1610 | * relax our requirements and repeat the search. |
1600 | */ | 1611 | */ |
1601 | if (ret <= 0 && want_symtab) { | 1612 | if (ret <= 0 && want_symtab) { |
1602 | want_symtab = 0; | 1613 | want_symtab = 0; |
1603 | goto restart; | 1614 | goto restart; |
1604 | } | 1615 | } |
1605 | 1616 | ||
1606 | free(name); | 1617 | free(name); |
1607 | if (ret < 0 && strstr(dso->name, " (deleted)") != NULL) | 1618 | if (ret < 0 && strstr(dso->name, " (deleted)") != NULL) |
1608 | return 0; | 1619 | return 0; |
1609 | return ret; | 1620 | return ret; |
1610 | } | 1621 | } |
1611 | 1622 | ||
1612 | struct map *map_groups__find_by_name(struct map_groups *mg, | 1623 | struct map *map_groups__find_by_name(struct map_groups *mg, |
1613 | enum map_type type, const char *name) | 1624 | enum map_type type, const char *name) |
1614 | { | 1625 | { |
1615 | struct rb_node *nd; | 1626 | struct rb_node *nd; |
1616 | 1627 | ||
1617 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { | 1628 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { |
1618 | struct map *map = rb_entry(nd, struct map, rb_node); | 1629 | struct map *map = rb_entry(nd, struct map, rb_node); |
1619 | 1630 | ||
1620 | if (map->dso && strcmp(map->dso->short_name, name) == 0) | 1631 | if (map->dso && strcmp(map->dso->short_name, name) == 0) |
1621 | return map; | 1632 | return map; |
1622 | } | 1633 | } |
1623 | 1634 | ||
1624 | return NULL; | 1635 | return NULL; |
1625 | } | 1636 | } |
1626 | 1637 | ||
1627 | static int dso__kernel_module_get_build_id(struct dso *dso, | 1638 | static int dso__kernel_module_get_build_id(struct dso *dso, |
1628 | const char *root_dir) | 1639 | const char *root_dir) |
1629 | { | 1640 | { |
1630 | char filename[PATH_MAX]; | 1641 | char filename[PATH_MAX]; |
1631 | /* | 1642 | /* |
1632 | * kernel module short names are of the form "[module]" and | 1643 | * kernel module short names are of the form "[module]" and |
1633 | * we need just "module" here. | 1644 | * we need just "module" here. |
1634 | */ | 1645 | */ |
1635 | const char *name = dso->short_name + 1; | 1646 | const char *name = dso->short_name + 1; |
1636 | 1647 | ||
1637 | snprintf(filename, sizeof(filename), | 1648 | snprintf(filename, sizeof(filename), |
1638 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", | 1649 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", |
1639 | root_dir, (int)strlen(name) - 1, name); | 1650 | root_dir, (int)strlen(name) - 1, name); |
1640 | 1651 | ||
1641 | if (sysfs__read_build_id(filename, dso->build_id, | 1652 | if (sysfs__read_build_id(filename, dso->build_id, |
1642 | sizeof(dso->build_id)) == 0) | 1653 | sizeof(dso->build_id)) == 0) |
1643 | dso->has_build_id = true; | 1654 | dso->has_build_id = true; |
1644 | 1655 | ||
1645 | return 0; | 1656 | return 0; |
1646 | } | 1657 | } |
1647 | 1658 | ||
1648 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | 1659 | static int map_groups__set_modules_path_dir(struct map_groups *mg, |
1649 | const char *dir_name) | 1660 | const char *dir_name) |
1650 | { | 1661 | { |
1651 | struct dirent *dent; | 1662 | struct dirent *dent; |
1652 | DIR *dir = opendir(dir_name); | 1663 | DIR *dir = opendir(dir_name); |
1653 | int ret = 0; | 1664 | int ret = 0; |
1654 | 1665 | ||
1655 | if (!dir) { | 1666 | if (!dir) { |
1656 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); | 1667 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); |
1657 | return -1; | 1668 | return -1; |
1658 | } | 1669 | } |
1659 | 1670 | ||
1660 | while ((dent = readdir(dir)) != NULL) { | 1671 | while ((dent = readdir(dir)) != NULL) { |
1661 | char path[PATH_MAX]; | 1672 | char path[PATH_MAX]; |
1662 | struct stat st; | 1673 | struct stat st; |
1663 | 1674 | ||
1664 | /*sshfs might return bad dent->d_type, so we have to stat*/ | 1675 | /*sshfs might return bad dent->d_type, so we have to stat*/ |
1665 | sprintf(path, "%s/%s", dir_name, dent->d_name); | 1676 | sprintf(path, "%s/%s", dir_name, dent->d_name); |
1666 | if (stat(path, &st)) | 1677 | if (stat(path, &st)) |
1667 | continue; | 1678 | continue; |
1668 | 1679 | ||
1669 | if (S_ISDIR(st.st_mode)) { | 1680 | if (S_ISDIR(st.st_mode)) { |
1670 | if (!strcmp(dent->d_name, ".") || | 1681 | if (!strcmp(dent->d_name, ".") || |
1671 | !strcmp(dent->d_name, "..")) | 1682 | !strcmp(dent->d_name, "..")) |
1672 | continue; | 1683 | continue; |
1673 | 1684 | ||
1674 | snprintf(path, sizeof(path), "%s/%s", | 1685 | snprintf(path, sizeof(path), "%s/%s", |
1675 | dir_name, dent->d_name); | 1686 | dir_name, dent->d_name); |
1676 | ret = map_groups__set_modules_path_dir(mg, path); | 1687 | ret = map_groups__set_modules_path_dir(mg, path); |
1677 | if (ret < 0) | 1688 | if (ret < 0) |
1678 | goto out; | 1689 | goto out; |
1679 | } else { | 1690 | } else { |
1680 | char *dot = strrchr(dent->d_name, '.'), | 1691 | char *dot = strrchr(dent->d_name, '.'), |
1681 | dso_name[PATH_MAX]; | 1692 | dso_name[PATH_MAX]; |
1682 | struct map *map; | 1693 | struct map *map; |
1683 | char *long_name; | 1694 | char *long_name; |
1684 | 1695 | ||
1685 | if (dot == NULL || strcmp(dot, ".ko")) | 1696 | if (dot == NULL || strcmp(dot, ".ko")) |
1686 | continue; | 1697 | continue; |
1687 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | 1698 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", |
1688 | (int)(dot - dent->d_name), dent->d_name); | 1699 | (int)(dot - dent->d_name), dent->d_name); |
1689 | 1700 | ||
1690 | strxfrchar(dso_name, '-', '_'); | 1701 | strxfrchar(dso_name, '-', '_'); |
1691 | map = map_groups__find_by_name(mg, MAP__FUNCTION, | 1702 | map = map_groups__find_by_name(mg, MAP__FUNCTION, |
1692 | dso_name); | 1703 | dso_name); |
1693 | if (map == NULL) | 1704 | if (map == NULL) |
1694 | continue; | 1705 | continue; |
1695 | 1706 | ||
1696 | snprintf(path, sizeof(path), "%s/%s", | 1707 | snprintf(path, sizeof(path), "%s/%s", |
1697 | dir_name, dent->d_name); | 1708 | dir_name, dent->d_name); |
1698 | 1709 | ||
1699 | long_name = strdup(path); | 1710 | long_name = strdup(path); |
1700 | if (long_name == NULL) { | 1711 | if (long_name == NULL) { |
1701 | ret = -1; | 1712 | ret = -1; |
1702 | goto out; | 1713 | goto out; |
1703 | } | 1714 | } |
1704 | dso__set_long_name(map->dso, long_name); | 1715 | dso__set_long_name(map->dso, long_name); |
1705 | map->dso->lname_alloc = 1; | 1716 | map->dso->lname_alloc = 1; |
1706 | dso__kernel_module_get_build_id(map->dso, ""); | 1717 | dso__kernel_module_get_build_id(map->dso, ""); |
1707 | } | 1718 | } |
1708 | } | 1719 | } |
1709 | 1720 | ||
1710 | out: | 1721 | out: |
1711 | closedir(dir); | 1722 | closedir(dir); |
1712 | return ret; | 1723 | return ret; |
1713 | } | 1724 | } |
1714 | 1725 | ||
1715 | static char *get_kernel_version(const char *root_dir) | 1726 | static char *get_kernel_version(const char *root_dir) |
1716 | { | 1727 | { |
1717 | char version[PATH_MAX]; | 1728 | char version[PATH_MAX]; |
1718 | FILE *file; | 1729 | FILE *file; |
1719 | char *name, *tmp; | 1730 | char *name, *tmp; |
1720 | const char *prefix = "Linux version "; | 1731 | const char *prefix = "Linux version "; |
1721 | 1732 | ||
1722 | sprintf(version, "%s/proc/version", root_dir); | 1733 | sprintf(version, "%s/proc/version", root_dir); |
1723 | file = fopen(version, "r"); | 1734 | file = fopen(version, "r"); |
1724 | if (!file) | 1735 | if (!file) |
1725 | return NULL; | 1736 | return NULL; |
1726 | 1737 | ||
1727 | version[0] = '\0'; | 1738 | version[0] = '\0'; |
1728 | tmp = fgets(version, sizeof(version), file); | 1739 | tmp = fgets(version, sizeof(version), file); |
1729 | fclose(file); | 1740 | fclose(file); |
1730 | 1741 | ||
1731 | name = strstr(version, prefix); | 1742 | name = strstr(version, prefix); |
1732 | if (!name) | 1743 | if (!name) |
1733 | return NULL; | 1744 | return NULL; |
1734 | name += strlen(prefix); | 1745 | name += strlen(prefix); |
1735 | tmp = strchr(name, ' '); | 1746 | tmp = strchr(name, ' '); |
1736 | if (tmp) | 1747 | if (tmp) |
1737 | *tmp = '\0'; | 1748 | *tmp = '\0'; |
1738 | 1749 | ||
1739 | return strdup(name); | 1750 | return strdup(name); |
1740 | } | 1751 | } |
1741 | 1752 | ||
1742 | static int machine__set_modules_path(struct machine *machine) | 1753 | static int machine__set_modules_path(struct machine *machine) |
1743 | { | 1754 | { |
1744 | char *version; | 1755 | char *version; |
1745 | char modules_path[PATH_MAX]; | 1756 | char modules_path[PATH_MAX]; |
1746 | 1757 | ||
1747 | version = get_kernel_version(machine->root_dir); | 1758 | version = get_kernel_version(machine->root_dir); |
1748 | if (!version) | 1759 | if (!version) |
1749 | return -1; | 1760 | return -1; |
1750 | 1761 | ||
1751 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", | 1762 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", |
1752 | machine->root_dir, version); | 1763 | machine->root_dir, version); |
1753 | free(version); | 1764 | free(version); |
1754 | 1765 | ||
1755 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | 1766 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); |
1756 | } | 1767 | } |
1757 | 1768 | ||
1758 | /* | 1769 | /* |
1759 | * Constructor variant for modules (where we know from /proc/modules where | 1770 | * Constructor variant for modules (where we know from /proc/modules where |
1760 | * they are loaded) and for vmlinux, where only after we load all the | 1771 | * they are loaded) and for vmlinux, where only after we load all the |
1761 | * symbols we'll know where it starts and ends. | 1772 | * symbols we'll know where it starts and ends. |
1762 | */ | 1773 | */ |
1763 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | 1774 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) |
1764 | { | 1775 | { |
1765 | struct map *map = calloc(1, (sizeof(*map) + | 1776 | struct map *map = calloc(1, (sizeof(*map) + |
1766 | (dso->kernel ? sizeof(struct kmap) : 0))); | 1777 | (dso->kernel ? sizeof(struct kmap) : 0))); |
1767 | if (map != NULL) { | 1778 | if (map != NULL) { |
1768 | /* | 1779 | /* |
1769 | * ->end will be filled after we load all the symbols | 1780 | * ->end will be filled after we load all the symbols |
1770 | */ | 1781 | */ |
1771 | map__init(map, type, start, 0, 0, dso); | 1782 | map__init(map, type, start, 0, 0, dso); |
1772 | } | 1783 | } |
1773 | 1784 | ||
1774 | return map; | 1785 | return map; |
1775 | } | 1786 | } |
1776 | 1787 | ||
1777 | struct map *machine__new_module(struct machine *machine, u64 start, | 1788 | struct map *machine__new_module(struct machine *machine, u64 start, |
1778 | const char *filename) | 1789 | const char *filename) |
1779 | { | 1790 | { |
1780 | struct map *map; | 1791 | struct map *map; |
1781 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | 1792 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); |
1782 | 1793 | ||
1783 | if (dso == NULL) | 1794 | if (dso == NULL) |
1784 | return NULL; | 1795 | return NULL; |
1785 | 1796 | ||
1786 | map = map__new2(start, dso, MAP__FUNCTION); | 1797 | map = map__new2(start, dso, MAP__FUNCTION); |
1787 | if (map == NULL) | 1798 | if (map == NULL) |
1788 | return NULL; | 1799 | return NULL; |
1789 | 1800 | ||
1790 | if (machine__is_host(machine)) | 1801 | if (machine__is_host(machine)) |
1791 | dso->symtab_type = SYMTAB__SYSTEM_PATH_KMODULE; | 1802 | dso->symtab_type = SYMTAB__SYSTEM_PATH_KMODULE; |
1792 | else | 1803 | else |
1793 | dso->symtab_type = SYMTAB__GUEST_KMODULE; | 1804 | dso->symtab_type = SYMTAB__GUEST_KMODULE; |
1794 | map_groups__insert(&machine->kmaps, map); | 1805 | map_groups__insert(&machine->kmaps, map); |
1795 | return map; | 1806 | return map; |
1796 | } | 1807 | } |
1797 | 1808 | ||
1798 | static int machine__create_modules(struct machine *machine) | 1809 | static int machine__create_modules(struct machine *machine) |
1799 | { | 1810 | { |
1800 | char *line = NULL; | 1811 | char *line = NULL; |
1801 | size_t n; | 1812 | size_t n; |
1802 | FILE *file; | 1813 | FILE *file; |
1803 | struct map *map; | 1814 | struct map *map; |
1804 | const char *modules; | 1815 | const char *modules; |
1805 | char path[PATH_MAX]; | 1816 | char path[PATH_MAX]; |
1806 | 1817 | ||
1807 | if (machine__is_default_guest(machine)) | 1818 | if (machine__is_default_guest(machine)) |
1808 | modules = symbol_conf.default_guest_modules; | 1819 | modules = symbol_conf.default_guest_modules; |
1809 | else { | 1820 | else { |
1810 | sprintf(path, "%s/proc/modules", machine->root_dir); | 1821 | sprintf(path, "%s/proc/modules", machine->root_dir); |
1811 | modules = path; | 1822 | modules = path; |
1812 | } | 1823 | } |
1813 | 1824 | ||
1814 | if (symbol__restricted_filename(path, "/proc/modules")) | 1825 | if (symbol__restricted_filename(path, "/proc/modules")) |
1815 | return -1; | 1826 | return -1; |
1816 | 1827 | ||
1817 | file = fopen(modules, "r"); | 1828 | file = fopen(modules, "r"); |
1818 | if (file == NULL) | 1829 | if (file == NULL) |
1819 | return -1; | 1830 | return -1; |
1820 | 1831 | ||
1821 | while (!feof(file)) { | 1832 | while (!feof(file)) { |
1822 | char name[PATH_MAX]; | 1833 | char name[PATH_MAX]; |
1823 | u64 start; | 1834 | u64 start; |
1824 | char *sep; | 1835 | char *sep; |
1825 | int line_len; | 1836 | int line_len; |
1826 | 1837 | ||
1827 | line_len = getline(&line, &n, file); | 1838 | line_len = getline(&line, &n, file); |
1828 | if (line_len < 0) | 1839 | if (line_len < 0) |
1829 | break; | 1840 | break; |
1830 | 1841 | ||
1831 | if (!line) | 1842 | if (!line) |
1832 | goto out_failure; | 1843 | goto out_failure; |
1833 | 1844 | ||
1834 | line[--line_len] = '\0'; /* \n */ | 1845 | line[--line_len] = '\0'; /* \n */ |
1835 | 1846 | ||
1836 | sep = strrchr(line, 'x'); | 1847 | sep = strrchr(line, 'x'); |
1837 | if (sep == NULL) | 1848 | if (sep == NULL) |
1838 | continue; | 1849 | continue; |
1839 | 1850 | ||
1840 | hex2u64(sep + 1, &start); | 1851 | hex2u64(sep + 1, &start); |
1841 | 1852 | ||
1842 | sep = strchr(line, ' '); | 1853 | sep = strchr(line, ' '); |
1843 | if (sep == NULL) | 1854 | if (sep == NULL) |
1844 | continue; | 1855 | continue; |
1845 | 1856 | ||
1846 | *sep = '\0'; | 1857 | *sep = '\0'; |
1847 | 1858 | ||
1848 | snprintf(name, sizeof(name), "[%s]", line); | 1859 | snprintf(name, sizeof(name), "[%s]", line); |
1849 | map = machine__new_module(machine, start, name); | 1860 | map = machine__new_module(machine, start, name); |
1850 | if (map == NULL) | 1861 | if (map == NULL) |
1851 | goto out_delete_line; | 1862 | goto out_delete_line; |
1852 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | 1863 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); |
1853 | } | 1864 | } |
1854 | 1865 | ||
1855 | free(line); | 1866 | free(line); |
1856 | fclose(file); | 1867 | fclose(file); |
1857 | 1868 | ||
1858 | return machine__set_modules_path(machine); | 1869 | return machine__set_modules_path(machine); |
1859 | 1870 | ||
1860 | out_delete_line: | 1871 | out_delete_line: |
1861 | free(line); | 1872 | free(line); |
1862 | out_failure: | 1873 | out_failure: |
1863 | return -1; | 1874 | return -1; |
1864 | } | 1875 | } |
1865 | 1876 | ||
1866 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 1877 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
1867 | const char *vmlinux, symbol_filter_t filter) | 1878 | const char *vmlinux, symbol_filter_t filter) |
1868 | { | 1879 | { |
1869 | int err = -1, fd; | 1880 | int err = -1, fd; |
1870 | char symfs_vmlinux[PATH_MAX]; | 1881 | char symfs_vmlinux[PATH_MAX]; |
1871 | 1882 | ||
1872 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | 1883 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", |
1873 | symbol_conf.symfs, vmlinux); | 1884 | symbol_conf.symfs, vmlinux); |
1874 | fd = open(symfs_vmlinux, O_RDONLY); | 1885 | fd = open(symfs_vmlinux, O_RDONLY); |
1875 | if (fd < 0) | 1886 | if (fd < 0) |
1876 | return -1; | 1887 | return -1; |
1877 | 1888 | ||
1878 | dso__set_long_name(dso, (char *)vmlinux); | 1889 | dso__set_long_name(dso, (char *)vmlinux); |
1879 | dso__set_loaded(dso, map->type); | 1890 | dso__set_loaded(dso, map->type); |
1880 | err = dso__load_sym(dso, map, symfs_vmlinux, fd, filter, 0, 0); | 1891 | err = dso__load_sym(dso, map, symfs_vmlinux, fd, filter, 0, 0); |
1881 | close(fd); | 1892 | close(fd); |
1882 | 1893 | ||
1883 | if (err > 0) | 1894 | if (err > 0) |
1884 | pr_debug("Using %s for symbols\n", symfs_vmlinux); | 1895 | pr_debug("Using %s for symbols\n", symfs_vmlinux); |
1885 | 1896 | ||
1886 | return err; | 1897 | return err; |
1887 | } | 1898 | } |
1888 | 1899 | ||
1889 | int dso__load_vmlinux_path(struct dso *dso, struct map *map, | 1900 | int dso__load_vmlinux_path(struct dso *dso, struct map *map, |
1890 | symbol_filter_t filter) | 1901 | symbol_filter_t filter) |
1891 | { | 1902 | { |
1892 | int i, err = 0; | 1903 | int i, err = 0; |
1893 | char *filename; | 1904 | char *filename; |
1894 | 1905 | ||
1895 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | 1906 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", |
1896 | vmlinux_path__nr_entries + 1); | 1907 | vmlinux_path__nr_entries + 1); |
1897 | 1908 | ||
1898 | filename = dso__build_id_filename(dso, NULL, 0); | 1909 | filename = dso__build_id_filename(dso, NULL, 0); |
1899 | if (filename != NULL) { | 1910 | if (filename != NULL) { |
1900 | err = dso__load_vmlinux(dso, map, filename, filter); | 1911 | err = dso__load_vmlinux(dso, map, filename, filter); |
1901 | if (err > 0) { | 1912 | if (err > 0) { |
1902 | dso__set_long_name(dso, filename); | 1913 | dso__set_long_name(dso, filename); |
1903 | goto out; | 1914 | goto out; |
1904 | } | 1915 | } |
1905 | free(filename); | 1916 | free(filename); |
1906 | } | 1917 | } |
1907 | 1918 | ||
1908 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1919 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
1909 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); | 1920 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); |
1910 | if (err > 0) { | 1921 | if (err > 0) { |
1911 | dso__set_long_name(dso, strdup(vmlinux_path[i])); | 1922 | dso__set_long_name(dso, strdup(vmlinux_path[i])); |
1912 | break; | 1923 | break; |
1913 | } | 1924 | } |
1914 | } | 1925 | } |
1915 | out: | 1926 | out: |
1916 | return err; | 1927 | return err; |
1917 | } | 1928 | } |
1918 | 1929 | ||
1919 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 1930 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
1920 | symbol_filter_t filter) | 1931 | symbol_filter_t filter) |
1921 | { | 1932 | { |
1922 | int err; | 1933 | int err; |
1923 | const char *kallsyms_filename = NULL; | 1934 | const char *kallsyms_filename = NULL; |
1924 | char *kallsyms_allocated_filename = NULL; | 1935 | char *kallsyms_allocated_filename = NULL; |
1925 | /* | 1936 | /* |
1926 | * Step 1: if the user specified a kallsyms or vmlinux filename, use | 1937 | * Step 1: if the user specified a kallsyms or vmlinux filename, use |
1927 | * it and only it, reporting errors to the user if it cannot be used. | 1938 | * it and only it, reporting errors to the user if it cannot be used. |
1928 | * | 1939 | * |
1929 | * For instance, try to analyse an ARM perf.data file _without_ a | 1940 | * For instance, try to analyse an ARM perf.data file _without_ a |
1930 | * build-id, or if the user specifies the wrong path to the right | 1941 | * build-id, or if the user specifies the wrong path to the right |
1931 | * vmlinux file, obviously we can't fallback to another vmlinux (a | 1942 | * vmlinux file, obviously we can't fallback to another vmlinux (a |
1932 | * x86_86 one, on the machine where analysis is being performed, say), | 1943 | * x86_86 one, on the machine where analysis is being performed, say), |
1933 | * or worse, /proc/kallsyms. | 1944 | * or worse, /proc/kallsyms. |
1934 | * | 1945 | * |
1935 | * If the specified file _has_ a build-id and there is a build-id | 1946 | * If the specified file _has_ a build-id and there is a build-id |
1936 | * section in the perf.data file, we will still do the expected | 1947 | * section in the perf.data file, we will still do the expected |
1937 | * validation in dso__load_vmlinux and will bail out if they don't | 1948 | * validation in dso__load_vmlinux and will bail out if they don't |
1938 | * match. | 1949 | * match. |
1939 | */ | 1950 | */ |
1940 | if (symbol_conf.kallsyms_name != NULL) { | 1951 | if (symbol_conf.kallsyms_name != NULL) { |
1941 | kallsyms_filename = symbol_conf.kallsyms_name; | 1952 | kallsyms_filename = symbol_conf.kallsyms_name; |
1942 | goto do_kallsyms; | 1953 | goto do_kallsyms; |
1943 | } | 1954 | } |
1944 | 1955 | ||
1945 | if (symbol_conf.vmlinux_name != NULL) { | 1956 | if (symbol_conf.vmlinux_name != NULL) { |
1946 | err = dso__load_vmlinux(dso, map, | 1957 | err = dso__load_vmlinux(dso, map, |
1947 | symbol_conf.vmlinux_name, filter); | 1958 | symbol_conf.vmlinux_name, filter); |
1948 | if (err > 0) { | 1959 | if (err > 0) { |
1949 | dso__set_long_name(dso, | 1960 | dso__set_long_name(dso, |
1950 | strdup(symbol_conf.vmlinux_name)); | 1961 | strdup(symbol_conf.vmlinux_name)); |
1951 | goto out_fixup; | 1962 | goto out_fixup; |
1952 | } | 1963 | } |
1953 | return err; | 1964 | return err; |
1954 | } | 1965 | } |
1955 | 1966 | ||
1956 | if (vmlinux_path != NULL) { | 1967 | if (vmlinux_path != NULL) { |
1957 | err = dso__load_vmlinux_path(dso, map, filter); | 1968 | err = dso__load_vmlinux_path(dso, map, filter); |
1958 | if (err > 0) | 1969 | if (err > 0) |
1959 | goto out_fixup; | 1970 | goto out_fixup; |
1960 | } | 1971 | } |
1961 | 1972 | ||
1962 | /* do not try local files if a symfs was given */ | 1973 | /* do not try local files if a symfs was given */ |
1963 | if (symbol_conf.symfs[0] != 0) | 1974 | if (symbol_conf.symfs[0] != 0) |
1964 | return -1; | 1975 | return -1; |
1965 | 1976 | ||
1966 | /* | 1977 | /* |
1967 | * Say the kernel DSO was created when processing the build-id header table, | 1978 | * Say the kernel DSO was created when processing the build-id header table, |
1968 | * we have a build-id, so check if it is the same as the running kernel, | 1979 | * we have a build-id, so check if it is the same as the running kernel, |
1969 | * using it if it is. | 1980 | * using it if it is. |
1970 | */ | 1981 | */ |
1971 | if (dso->has_build_id) { | 1982 | if (dso->has_build_id) { |
1972 | u8 kallsyms_build_id[BUILD_ID_SIZE]; | 1983 | u8 kallsyms_build_id[BUILD_ID_SIZE]; |
1973 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 1984 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
1974 | 1985 | ||
1975 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | 1986 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, |
1976 | sizeof(kallsyms_build_id)) == 0) { | 1987 | sizeof(kallsyms_build_id)) == 0) { |
1977 | if (dso__build_id_equal(dso, kallsyms_build_id)) { | 1988 | if (dso__build_id_equal(dso, kallsyms_build_id)) { |
1978 | kallsyms_filename = "/proc/kallsyms"; | 1989 | kallsyms_filename = "/proc/kallsyms"; |
1979 | goto do_kallsyms; | 1990 | goto do_kallsyms; |
1980 | } | 1991 | } |
1981 | } | 1992 | } |
1982 | /* | 1993 | /* |
1983 | * Now look if we have it on the build-id cache in | 1994 | * Now look if we have it on the build-id cache in |
1984 | * $HOME/.debug/[kernel.kallsyms]. | 1995 | * $HOME/.debug/[kernel.kallsyms]. |
1985 | */ | 1996 | */ |
1986 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | 1997 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), |
1987 | sbuild_id); | 1998 | sbuild_id); |
1988 | 1999 | ||
1989 | if (asprintf(&kallsyms_allocated_filename, | 2000 | if (asprintf(&kallsyms_allocated_filename, |
1990 | "%s/.debug/[kernel.kallsyms]/%s", | 2001 | "%s/.debug/[kernel.kallsyms]/%s", |
1991 | getenv("HOME"), sbuild_id) == -1) { | 2002 | getenv("HOME"), sbuild_id) == -1) { |
1992 | pr_err("Not enough memory for kallsyms file lookup\n"); | 2003 | pr_err("Not enough memory for kallsyms file lookup\n"); |
1993 | return -1; | 2004 | return -1; |
1994 | } | 2005 | } |
1995 | 2006 | ||
1996 | kallsyms_filename = kallsyms_allocated_filename; | 2007 | kallsyms_filename = kallsyms_allocated_filename; |
1997 | 2008 | ||
1998 | if (access(kallsyms_filename, F_OK)) { | 2009 | if (access(kallsyms_filename, F_OK)) { |
1999 | pr_err("No kallsyms or vmlinux with build-id %s " | 2010 | pr_err("No kallsyms or vmlinux with build-id %s " |
2000 | "was found\n", sbuild_id); | 2011 | "was found\n", sbuild_id); |
2001 | free(kallsyms_allocated_filename); | 2012 | free(kallsyms_allocated_filename); |
2002 | return -1; | 2013 | return -1; |
2003 | } | 2014 | } |
2004 | } else { | 2015 | } else { |
2005 | /* | 2016 | /* |
2006 | * Last resort, if we don't have a build-id and couldn't find | 2017 | * Last resort, if we don't have a build-id and couldn't find |
2007 | * any vmlinux file, try the running kernel kallsyms table. | 2018 | * any vmlinux file, try the running kernel kallsyms table. |
2008 | */ | 2019 | */ |
2009 | kallsyms_filename = "/proc/kallsyms"; | 2020 | kallsyms_filename = "/proc/kallsyms"; |
2010 | } | 2021 | } |
2011 | 2022 | ||
2012 | do_kallsyms: | 2023 | do_kallsyms: |
2013 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); | 2024 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); |
2014 | if (err > 0) | 2025 | if (err > 0) |
2015 | pr_debug("Using %s for symbols\n", kallsyms_filename); | 2026 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
2016 | free(kallsyms_allocated_filename); | 2027 | free(kallsyms_allocated_filename); |
2017 | 2028 | ||
2018 | if (err > 0) { | 2029 | if (err > 0) { |
2019 | out_fixup: | 2030 | out_fixup: |
2020 | if (kallsyms_filename != NULL) | 2031 | if (kallsyms_filename != NULL) |
2021 | dso__set_long_name(dso, strdup("[kernel.kallsyms]")); | 2032 | dso__set_long_name(dso, strdup("[kernel.kallsyms]")); |
2022 | map__fixup_start(map); | 2033 | map__fixup_start(map); |
2023 | map__fixup_end(map); | 2034 | map__fixup_end(map); |
2024 | } | 2035 | } |
2025 | 2036 | ||
2026 | return err; | 2037 | return err; |
2027 | } | 2038 | } |
2028 | 2039 | ||
2029 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 2040 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
2030 | symbol_filter_t filter) | 2041 | symbol_filter_t filter) |
2031 | { | 2042 | { |
2032 | int err; | 2043 | int err; |
2033 | const char *kallsyms_filename = NULL; | 2044 | const char *kallsyms_filename = NULL; |
2034 | struct machine *machine; | 2045 | struct machine *machine; |
2035 | char path[PATH_MAX]; | 2046 | char path[PATH_MAX]; |
2036 | 2047 | ||
2037 | if (!map->groups) { | 2048 | if (!map->groups) { |
2038 | pr_debug("Guest kernel map hasn't the point to groups\n"); | 2049 | pr_debug("Guest kernel map hasn't the point to groups\n"); |
2039 | return -1; | 2050 | return -1; |
2040 | } | 2051 | } |
2041 | machine = map->groups->machine; | 2052 | machine = map->groups->machine; |
2042 | 2053 | ||
2043 | if (machine__is_default_guest(machine)) { | 2054 | if (machine__is_default_guest(machine)) { |
2044 | /* | 2055 | /* |
2045 | * if the user specified a vmlinux filename, use it and only | 2056 | * if the user specified a vmlinux filename, use it and only |
2046 | * it, reporting errors to the user if it cannot be used. | 2057 | * it, reporting errors to the user if it cannot be used. |
2047 | * Or use file guest_kallsyms inputted by user on commandline | 2058 | * Or use file guest_kallsyms inputted by user on commandline |
2048 | */ | 2059 | */ |
2049 | if (symbol_conf.default_guest_vmlinux_name != NULL) { | 2060 | if (symbol_conf.default_guest_vmlinux_name != NULL) { |
2050 | err = dso__load_vmlinux(dso, map, | 2061 | err = dso__load_vmlinux(dso, map, |
2051 | symbol_conf.default_guest_vmlinux_name, filter); | 2062 | symbol_conf.default_guest_vmlinux_name, filter); |
2052 | goto out_try_fixup; | 2063 | goto out_try_fixup; |
2053 | } | 2064 | } |
2054 | 2065 | ||
2055 | kallsyms_filename = symbol_conf.default_guest_kallsyms; | 2066 | kallsyms_filename = symbol_conf.default_guest_kallsyms; |
2056 | if (!kallsyms_filename) | 2067 | if (!kallsyms_filename) |
2057 | return -1; | 2068 | return -1; |
2058 | } else { | 2069 | } else { |
2059 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | 2070 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); |
2060 | kallsyms_filename = path; | 2071 | kallsyms_filename = path; |
2061 | } | 2072 | } |
2062 | 2073 | ||
2063 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); | 2074 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); |
2064 | if (err > 0) | 2075 | if (err > 0) |
2065 | pr_debug("Using %s for symbols\n", kallsyms_filename); | 2076 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
2066 | 2077 | ||
2067 | out_try_fixup: | 2078 | out_try_fixup: |
2068 | if (err > 0) { | 2079 | if (err > 0) { |
2069 | if (kallsyms_filename != NULL) { | 2080 | if (kallsyms_filename != NULL) { |
2070 | machine__mmap_name(machine, path, sizeof(path)); | 2081 | machine__mmap_name(machine, path, sizeof(path)); |
2071 | dso__set_long_name(dso, strdup(path)); | 2082 | dso__set_long_name(dso, strdup(path)); |
2072 | } | 2083 | } |
2073 | map__fixup_start(map); | 2084 | map__fixup_start(map); |
2074 | map__fixup_end(map); | 2085 | map__fixup_end(map); |
2075 | } | 2086 | } |
2076 | 2087 | ||
2077 | return err; | 2088 | return err; |
2078 | } | 2089 | } |
2079 | 2090 | ||
2080 | static void dsos__add(struct list_head *head, struct dso *dso) | 2091 | static void dsos__add(struct list_head *head, struct dso *dso) |
2081 | { | 2092 | { |
2082 | list_add_tail(&dso->node, head); | 2093 | list_add_tail(&dso->node, head); |
2083 | } | 2094 | } |
2084 | 2095 | ||
2085 | static struct dso *dsos__find(struct list_head *head, const char *name) | 2096 | static struct dso *dsos__find(struct list_head *head, const char *name) |
2086 | { | 2097 | { |
2087 | struct dso *pos; | 2098 | struct dso *pos; |
2088 | 2099 | ||
2089 | list_for_each_entry(pos, head, node) | 2100 | list_for_each_entry(pos, head, node) |
2090 | if (strcmp(pos->long_name, name) == 0) | 2101 | if (strcmp(pos->long_name, name) == 0) |
2091 | return pos; | 2102 | return pos; |
2092 | return NULL; | 2103 | return NULL; |
2093 | } | 2104 | } |
2094 | 2105 | ||
2095 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | 2106 | struct dso *__dsos__findnew(struct list_head *head, const char *name) |
2096 | { | 2107 | { |
2097 | struct dso *dso = dsos__find(head, name); | 2108 | struct dso *dso = dsos__find(head, name); |
2098 | 2109 | ||
2099 | if (!dso) { | 2110 | if (!dso) { |
2100 | dso = dso__new(name); | 2111 | dso = dso__new(name); |
2101 | if (dso != NULL) { | 2112 | if (dso != NULL) { |
2102 | dsos__add(head, dso); | 2113 | dsos__add(head, dso); |
2103 | dso__set_basename(dso); | 2114 | dso__set_basename(dso); |
2104 | } | 2115 | } |
2105 | } | 2116 | } |
2106 | 2117 | ||
2107 | return dso; | 2118 | return dso; |
2108 | } | 2119 | } |
2109 | 2120 | ||
2110 | size_t __dsos__fprintf(struct list_head *head, FILE *fp) | 2121 | size_t __dsos__fprintf(struct list_head *head, FILE *fp) |
2111 | { | 2122 | { |
2112 | struct dso *pos; | 2123 | struct dso *pos; |
2113 | size_t ret = 0; | 2124 | size_t ret = 0; |
2114 | 2125 | ||
2115 | list_for_each_entry(pos, head, node) { | 2126 | list_for_each_entry(pos, head, node) { |
2116 | int i; | 2127 | int i; |
2117 | for (i = 0; i < MAP__NR_TYPES; ++i) | 2128 | for (i = 0; i < MAP__NR_TYPES; ++i) |
2118 | ret += dso__fprintf(pos, i, fp); | 2129 | ret += dso__fprintf(pos, i, fp); |
2119 | } | 2130 | } |
2120 | 2131 | ||
2121 | return ret; | 2132 | return ret; |
2122 | } | 2133 | } |
2123 | 2134 | ||
2124 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) | 2135 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) |
2125 | { | 2136 | { |
2126 | struct rb_node *nd; | 2137 | struct rb_node *nd; |
2127 | size_t ret = 0; | 2138 | size_t ret = 0; |
2128 | 2139 | ||
2129 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | 2140 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { |
2130 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 2141 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
2131 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | 2142 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); |
2132 | ret += __dsos__fprintf(&pos->user_dsos, fp); | 2143 | ret += __dsos__fprintf(&pos->user_dsos, fp); |
2133 | } | 2144 | } |
2134 | 2145 | ||
2135 | return ret; | 2146 | return ret; |
2136 | } | 2147 | } |
2137 | 2148 | ||
2138 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | 2149 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
2139 | bool with_hits) | 2150 | bool with_hits) |
2140 | { | 2151 | { |
2141 | struct dso *pos; | 2152 | struct dso *pos; |
2142 | size_t ret = 0; | 2153 | size_t ret = 0; |
2143 | 2154 | ||
2144 | list_for_each_entry(pos, head, node) { | 2155 | list_for_each_entry(pos, head, node) { |
2145 | if (with_hits && !pos->hit) | 2156 | if (with_hits && !pos->hit) |
2146 | continue; | 2157 | continue; |
2147 | ret += dso__fprintf_buildid(pos, fp); | 2158 | ret += dso__fprintf_buildid(pos, fp); |
2148 | ret += fprintf(fp, " %s\n", pos->long_name); | 2159 | ret += fprintf(fp, " %s\n", pos->long_name); |
2149 | } | 2160 | } |
2150 | return ret; | 2161 | return ret; |
2151 | } | 2162 | } |
2152 | 2163 | ||
2153 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | 2164 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, |
2154 | bool with_hits) | 2165 | bool with_hits) |
2155 | { | 2166 | { |
2156 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, with_hits) + | 2167 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, with_hits) + |
2157 | __dsos__fprintf_buildid(&machine->user_dsos, fp, with_hits); | 2168 | __dsos__fprintf_buildid(&machine->user_dsos, fp, with_hits); |
2158 | } | 2169 | } |
2159 | 2170 | ||
2160 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | 2171 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, |
2161 | FILE *fp, bool with_hits) | 2172 | FILE *fp, bool with_hits) |
2162 | { | 2173 | { |
2163 | struct rb_node *nd; | 2174 | struct rb_node *nd; |
2164 | size_t ret = 0; | 2175 | size_t ret = 0; |
2165 | 2176 | ||
2166 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | 2177 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { |
2167 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 2178 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
2168 | ret += machine__fprintf_dsos_buildid(pos, fp, with_hits); | 2179 | ret += machine__fprintf_dsos_buildid(pos, fp, with_hits); |
2169 | } | 2180 | } |
2170 | return ret; | 2181 | return ret; |
2171 | } | 2182 | } |
2172 | 2183 | ||
2173 | struct dso *dso__new_kernel(const char *name) | 2184 | struct dso *dso__new_kernel(const char *name) |
2174 | { | 2185 | { |
2175 | struct dso *dso = dso__new(name ?: "[kernel.kallsyms]"); | 2186 | struct dso *dso = dso__new(name ?: "[kernel.kallsyms]"); |
2176 | 2187 | ||
2177 | if (dso != NULL) { | 2188 | if (dso != NULL) { |
2178 | dso__set_short_name(dso, "[kernel]"); | 2189 | dso__set_short_name(dso, "[kernel]"); |
2179 | dso->kernel = DSO_TYPE_KERNEL; | 2190 | dso->kernel = DSO_TYPE_KERNEL; |
2180 | } | 2191 | } |
2181 | 2192 | ||
2182 | return dso; | 2193 | return dso; |
2183 | } | 2194 | } |
2184 | 2195 | ||
2185 | static struct dso *dso__new_guest_kernel(struct machine *machine, | 2196 | static struct dso *dso__new_guest_kernel(struct machine *machine, |
2186 | const char *name) | 2197 | const char *name) |
2187 | { | 2198 | { |
2188 | char bf[PATH_MAX]; | 2199 | char bf[PATH_MAX]; |
2189 | struct dso *dso = dso__new(name ?: machine__mmap_name(machine, bf, | 2200 | struct dso *dso = dso__new(name ?: machine__mmap_name(machine, bf, |
2190 | sizeof(bf))); | 2201 | sizeof(bf))); |
2191 | if (dso != NULL) { | 2202 | if (dso != NULL) { |
2192 | dso__set_short_name(dso, "[guest.kernel]"); | 2203 | dso__set_short_name(dso, "[guest.kernel]"); |
2193 | dso->kernel = DSO_TYPE_GUEST_KERNEL; | 2204 | dso->kernel = DSO_TYPE_GUEST_KERNEL; |
2194 | } | 2205 | } |
2195 | 2206 | ||
2196 | return dso; | 2207 | return dso; |
2197 | } | 2208 | } |
2198 | 2209 | ||
2199 | void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) | 2210 | void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) |
2200 | { | 2211 | { |
2201 | char path[PATH_MAX]; | 2212 | char path[PATH_MAX]; |
2202 | 2213 | ||
2203 | if (machine__is_default_guest(machine)) | 2214 | if (machine__is_default_guest(machine)) |
2204 | return; | 2215 | return; |
2205 | sprintf(path, "%s/sys/kernel/notes", machine->root_dir); | 2216 | sprintf(path, "%s/sys/kernel/notes", machine->root_dir); |
2206 | if (sysfs__read_build_id(path, dso->build_id, | 2217 | if (sysfs__read_build_id(path, dso->build_id, |
2207 | sizeof(dso->build_id)) == 0) | 2218 | sizeof(dso->build_id)) == 0) |
2208 | dso->has_build_id = true; | 2219 | dso->has_build_id = true; |
2209 | } | 2220 | } |
2210 | 2221 | ||
2211 | static struct dso *machine__create_kernel(struct machine *machine) | 2222 | static struct dso *machine__create_kernel(struct machine *machine) |
2212 | { | 2223 | { |
2213 | const char *vmlinux_name = NULL; | 2224 | const char *vmlinux_name = NULL; |
2214 | struct dso *kernel; | 2225 | struct dso *kernel; |
2215 | 2226 | ||
2216 | if (machine__is_host(machine)) { | 2227 | if (machine__is_host(machine)) { |
2217 | vmlinux_name = symbol_conf.vmlinux_name; | 2228 | vmlinux_name = symbol_conf.vmlinux_name; |
2218 | kernel = dso__new_kernel(vmlinux_name); | 2229 | kernel = dso__new_kernel(vmlinux_name); |
2219 | } else { | 2230 | } else { |
2220 | if (machine__is_default_guest(machine)) | 2231 | if (machine__is_default_guest(machine)) |
2221 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | 2232 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; |
2222 | kernel = dso__new_guest_kernel(machine, vmlinux_name); | 2233 | kernel = dso__new_guest_kernel(machine, vmlinux_name); |
2223 | } | 2234 | } |
2224 | 2235 | ||
2225 | if (kernel != NULL) { | 2236 | if (kernel != NULL) { |
2226 | dso__read_running_kernel_build_id(kernel, machine); | 2237 | dso__read_running_kernel_build_id(kernel, machine); |
2227 | dsos__add(&machine->kernel_dsos, kernel); | 2238 | dsos__add(&machine->kernel_dsos, kernel); |
2228 | } | 2239 | } |
2229 | return kernel; | 2240 | return kernel; |
2230 | } | 2241 | } |
2231 | 2242 | ||
2232 | struct process_args { | 2243 | struct process_args { |
2233 | u64 start; | 2244 | u64 start; |
2234 | }; | 2245 | }; |
2235 | 2246 | ||
2236 | static int symbol__in_kernel(void *arg, const char *name, | 2247 | static int symbol__in_kernel(void *arg, const char *name, |
2237 | char type __used, u64 start, u64 end __used) | 2248 | char type __used, u64 start, u64 end __used) |
2238 | { | 2249 | { |
2239 | struct process_args *args = arg; | 2250 | struct process_args *args = arg; |
2240 | 2251 | ||
2241 | if (strchr(name, '[')) | 2252 | if (strchr(name, '[')) |
2242 | return 0; | 2253 | return 0; |
2243 | 2254 | ||
2244 | args->start = start; | 2255 | args->start = start; |
2245 | return 1; | 2256 | return 1; |
2246 | } | 2257 | } |
2247 | 2258 | ||
2248 | /* Figure out the start address of kernel map from /proc/kallsyms */ | 2259 | /* Figure out the start address of kernel map from /proc/kallsyms */ |
2249 | static u64 machine__get_kernel_start_addr(struct machine *machine) | 2260 | static u64 machine__get_kernel_start_addr(struct machine *machine) |
2250 | { | 2261 | { |
2251 | const char *filename; | 2262 | const char *filename; |
2252 | char path[PATH_MAX]; | 2263 | char path[PATH_MAX]; |
2253 | struct process_args args; | 2264 | struct process_args args; |
2254 | 2265 | ||
2255 | if (machine__is_host(machine)) { | 2266 | if (machine__is_host(machine)) { |
2256 | filename = "/proc/kallsyms"; | 2267 | filename = "/proc/kallsyms"; |
2257 | } else { | 2268 | } else { |
2258 | if (machine__is_default_guest(machine)) | 2269 | if (machine__is_default_guest(machine)) |
2259 | filename = (char *)symbol_conf.default_guest_kallsyms; | 2270 | filename = (char *)symbol_conf.default_guest_kallsyms; |
2260 | else { | 2271 | else { |
2261 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | 2272 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); |
2262 | filename = path; | 2273 | filename = path; |
2263 | } | 2274 | } |
2264 | } | 2275 | } |
2265 | 2276 | ||
2266 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | 2277 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) |
2267 | return 0; | 2278 | return 0; |
2268 | 2279 | ||
2269 | if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) | 2280 | if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) |
2270 | return 0; | 2281 | return 0; |
2271 | 2282 | ||
2272 | return args.start; | 2283 | return args.start; |
2273 | } | 2284 | } |
2274 | 2285 | ||
2275 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | 2286 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) |
2276 | { | 2287 | { |
2277 | enum map_type type; | 2288 | enum map_type type; |
2278 | u64 start = machine__get_kernel_start_addr(machine); | 2289 | u64 start = machine__get_kernel_start_addr(machine); |
2279 | 2290 | ||
2280 | for (type = 0; type < MAP__NR_TYPES; ++type) { | 2291 | for (type = 0; type < MAP__NR_TYPES; ++type) { |
2281 | struct kmap *kmap; | 2292 | struct kmap *kmap; |
2282 | 2293 | ||
2283 | machine->vmlinux_maps[type] = map__new2(start, kernel, type); | 2294 | machine->vmlinux_maps[type] = map__new2(start, kernel, type); |
2284 | if (machine->vmlinux_maps[type] == NULL) | 2295 | if (machine->vmlinux_maps[type] == NULL) |
2285 | return -1; | 2296 | return -1; |
2286 | 2297 | ||
2287 | machine->vmlinux_maps[type]->map_ip = | 2298 | machine->vmlinux_maps[type]->map_ip = |
2288 | machine->vmlinux_maps[type]->unmap_ip = | 2299 | machine->vmlinux_maps[type]->unmap_ip = |
2289 | identity__map_ip; | 2300 | identity__map_ip; |
2290 | kmap = map__kmap(machine->vmlinux_maps[type]); | 2301 | kmap = map__kmap(machine->vmlinux_maps[type]); |
2291 | kmap->kmaps = &machine->kmaps; | 2302 | kmap->kmaps = &machine->kmaps; |
2292 | map_groups__insert(&machine->kmaps, | 2303 | map_groups__insert(&machine->kmaps, |
2293 | machine->vmlinux_maps[type]); | 2304 | machine->vmlinux_maps[type]); |
2294 | } | 2305 | } |
2295 | 2306 | ||
2296 | return 0; | 2307 | return 0; |
2297 | } | 2308 | } |
2298 | 2309 | ||
2299 | void machine__destroy_kernel_maps(struct machine *machine) | 2310 | void machine__destroy_kernel_maps(struct machine *machine) |
2300 | { | 2311 | { |
2301 | enum map_type type; | 2312 | enum map_type type; |
2302 | 2313 | ||
2303 | for (type = 0; type < MAP__NR_TYPES; ++type) { | 2314 | for (type = 0; type < MAP__NR_TYPES; ++type) { |
2304 | struct kmap *kmap; | 2315 | struct kmap *kmap; |
2305 | 2316 | ||
2306 | if (machine->vmlinux_maps[type] == NULL) | 2317 | if (machine->vmlinux_maps[type] == NULL) |
2307 | continue; | 2318 | continue; |
2308 | 2319 | ||
2309 | kmap = map__kmap(machine->vmlinux_maps[type]); | 2320 | kmap = map__kmap(machine->vmlinux_maps[type]); |
2310 | map_groups__remove(&machine->kmaps, | 2321 | map_groups__remove(&machine->kmaps, |
2311 | machine->vmlinux_maps[type]); | 2322 | machine->vmlinux_maps[type]); |
2312 | if (kmap->ref_reloc_sym) { | 2323 | if (kmap->ref_reloc_sym) { |
2313 | /* | 2324 | /* |
2314 | * ref_reloc_sym is shared among all maps, so free just | 2325 | * ref_reloc_sym is shared among all maps, so free just |
2315 | * on one of them. | 2326 | * on one of them. |
2316 | */ | 2327 | */ |
2317 | if (type == MAP__FUNCTION) { | 2328 | if (type == MAP__FUNCTION) { |
2318 | free((char *)kmap->ref_reloc_sym->name); | 2329 | free((char *)kmap->ref_reloc_sym->name); |
2319 | kmap->ref_reloc_sym->name = NULL; | 2330 | kmap->ref_reloc_sym->name = NULL; |
2320 | free(kmap->ref_reloc_sym); | 2331 | free(kmap->ref_reloc_sym); |
2321 | } | 2332 | } |
2322 | kmap->ref_reloc_sym = NULL; | 2333 | kmap->ref_reloc_sym = NULL; |
2323 | } | 2334 | } |
2324 | 2335 | ||
2325 | map__delete(machine->vmlinux_maps[type]); | 2336 | map__delete(machine->vmlinux_maps[type]); |
2326 | machine->vmlinux_maps[type] = NULL; | 2337 | machine->vmlinux_maps[type] = NULL; |
2327 | } | 2338 | } |
2328 | } | 2339 | } |
2329 | 2340 | ||
2330 | int machine__create_kernel_maps(struct machine *machine) | 2341 | int machine__create_kernel_maps(struct machine *machine) |
2331 | { | 2342 | { |
2332 | struct dso *kernel = machine__create_kernel(machine); | 2343 | struct dso *kernel = machine__create_kernel(machine); |
2333 | 2344 | ||
2334 | if (kernel == NULL || | 2345 | if (kernel == NULL || |
2335 | __machine__create_kernel_maps(machine, kernel) < 0) | 2346 | __machine__create_kernel_maps(machine, kernel) < 0) |
2336 | return -1; | 2347 | return -1; |
2337 | 2348 | ||
2338 | if (symbol_conf.use_modules && machine__create_modules(machine) < 0) | 2349 | if (symbol_conf.use_modules && machine__create_modules(machine) < 0) |
2339 | pr_debug("Problems creating module maps, continuing anyway...\n"); | 2350 | pr_debug("Problems creating module maps, continuing anyway...\n"); |
2340 | /* | 2351 | /* |
2341 | * Now that we have all the maps created, just set the ->end of them: | 2352 | * Now that we have all the maps created, just set the ->end of them: |
2342 | */ | 2353 | */ |
2343 | map_groups__fixup_end(&machine->kmaps); | 2354 | map_groups__fixup_end(&machine->kmaps); |
2344 | return 0; | 2355 | return 0; |
2345 | } | 2356 | } |
2346 | 2357 | ||
2347 | static void vmlinux_path__exit(void) | 2358 | static void vmlinux_path__exit(void) |
2348 | { | 2359 | { |
2349 | while (--vmlinux_path__nr_entries >= 0) { | 2360 | while (--vmlinux_path__nr_entries >= 0) { |
2350 | free(vmlinux_path[vmlinux_path__nr_entries]); | 2361 | free(vmlinux_path[vmlinux_path__nr_entries]); |
2351 | vmlinux_path[vmlinux_path__nr_entries] = NULL; | 2362 | vmlinux_path[vmlinux_path__nr_entries] = NULL; |
2352 | } | 2363 | } |
2353 | 2364 | ||
2354 | free(vmlinux_path); | 2365 | free(vmlinux_path); |
2355 | vmlinux_path = NULL; | 2366 | vmlinux_path = NULL; |
2356 | } | 2367 | } |
2357 | 2368 | ||
2358 | static int vmlinux_path__init(void) | 2369 | static int vmlinux_path__init(void) |
2359 | { | 2370 | { |
2360 | struct utsname uts; | 2371 | struct utsname uts; |
2361 | char bf[PATH_MAX]; | 2372 | char bf[PATH_MAX]; |
2362 | 2373 | ||
2363 | vmlinux_path = malloc(sizeof(char *) * 5); | 2374 | vmlinux_path = malloc(sizeof(char *) * 5); |
2364 | if (vmlinux_path == NULL) | 2375 | if (vmlinux_path == NULL) |
2365 | return -1; | 2376 | return -1; |
2366 | 2377 | ||
2367 | vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux"); | 2378 | vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux"); |
2368 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 2379 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
2369 | goto out_fail; | 2380 | goto out_fail; |
2370 | ++vmlinux_path__nr_entries; | 2381 | ++vmlinux_path__nr_entries; |
2371 | vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux"); | 2382 | vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux"); |
2372 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 2383 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
2373 | goto out_fail; | 2384 | goto out_fail; |
2374 | ++vmlinux_path__nr_entries; | 2385 | ++vmlinux_path__nr_entries; |
2375 | 2386 | ||
2376 | /* only try running kernel version if no symfs was given */ | 2387 | /* only try running kernel version if no symfs was given */ |
2377 | if (symbol_conf.symfs[0] != 0) | 2388 | if (symbol_conf.symfs[0] != 0) |
2378 | return 0; | 2389 | return 0; |
2379 | 2390 | ||
2380 | if (uname(&uts) < 0) | 2391 | if (uname(&uts) < 0) |
2381 | return -1; | 2392 | return -1; |
2382 | 2393 | ||
2383 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); | 2394 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); |
2384 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 2395 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
2385 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 2396 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
2386 | goto out_fail; | 2397 | goto out_fail; |
2387 | ++vmlinux_path__nr_entries; | 2398 | ++vmlinux_path__nr_entries; |
2388 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release); | 2399 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release); |
2389 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 2400 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
2390 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 2401 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
2391 | goto out_fail; | 2402 | goto out_fail; |
2392 | ++vmlinux_path__nr_entries; | 2403 | ++vmlinux_path__nr_entries; |
2393 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", | 2404 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", |
2394 | uts.release); | 2405 | uts.release); |
2395 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 2406 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
2396 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 2407 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
2397 | goto out_fail; | 2408 | goto out_fail; |
2398 | ++vmlinux_path__nr_entries; | 2409 | ++vmlinux_path__nr_entries; |
2399 | 2410 | ||
2400 | return 0; | 2411 | return 0; |
2401 | 2412 | ||
2402 | out_fail: | 2413 | out_fail: |
2403 | vmlinux_path__exit(); | 2414 | vmlinux_path__exit(); |
2404 | return -1; | 2415 | return -1; |
2405 | } | 2416 | } |
2406 | 2417 | ||
2407 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) | 2418 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) |
2408 | { | 2419 | { |
2409 | int i; | 2420 | int i; |
2410 | size_t printed = 0; | 2421 | size_t printed = 0; |
2411 | struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; | 2422 | struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; |
2412 | 2423 | ||
2413 | if (kdso->has_build_id) { | 2424 | if (kdso->has_build_id) { |
2414 | char filename[PATH_MAX]; | 2425 | char filename[PATH_MAX]; |
2415 | if (dso__build_id_filename(kdso, filename, sizeof(filename))) | 2426 | if (dso__build_id_filename(kdso, filename, sizeof(filename))) |
2416 | printed += fprintf(fp, "[0] %s\n", filename); | 2427 | printed += fprintf(fp, "[0] %s\n", filename); |
2417 | } | 2428 | } |
2418 | 2429 | ||
2419 | for (i = 0; i < vmlinux_path__nr_entries; ++i) | 2430 | for (i = 0; i < vmlinux_path__nr_entries; ++i) |
2420 | printed += fprintf(fp, "[%d] %s\n", | 2431 | printed += fprintf(fp, "[%d] %s\n", |
2421 | i + kdso->has_build_id, vmlinux_path[i]); | 2432 | i + kdso->has_build_id, vmlinux_path[i]); |
2422 | 2433 | ||
2423 | return printed; | 2434 | return printed; |
2424 | } | 2435 | } |
2425 | 2436 | ||
2426 | static int setup_list(struct strlist **list, const char *list_str, | 2437 | static int setup_list(struct strlist **list, const char *list_str, |
2427 | const char *list_name) | 2438 | const char *list_name) |
2428 | { | 2439 | { |
2429 | if (list_str == NULL) | 2440 | if (list_str == NULL) |
2430 | return 0; | 2441 | return 0; |
2431 | 2442 | ||
2432 | *list = strlist__new(true, list_str); | 2443 | *list = strlist__new(true, list_str); |
2433 | if (!*list) { | 2444 | if (!*list) { |
2434 | pr_err("problems parsing %s list\n", list_name); | 2445 | pr_err("problems parsing %s list\n", list_name); |
2435 | return -1; | 2446 | return -1; |
2436 | } | 2447 | } |
2437 | return 0; | 2448 | return 0; |
2438 | } | 2449 | } |
2439 | 2450 | ||
2440 | static bool symbol__read_kptr_restrict(void) | 2451 | static bool symbol__read_kptr_restrict(void) |
2441 | { | 2452 | { |
2442 | bool value = false; | 2453 | bool value = false; |
2443 | 2454 | ||
2444 | if (geteuid() != 0) { | 2455 | if (geteuid() != 0) { |
2445 | FILE *fp = fopen("/proc/sys/kernel/kptr_restrict", "r"); | 2456 | FILE *fp = fopen("/proc/sys/kernel/kptr_restrict", "r"); |
2446 | if (fp != NULL) { | 2457 | if (fp != NULL) { |
2447 | char line[8]; | 2458 | char line[8]; |
2448 | 2459 | ||
2449 | if (fgets(line, sizeof(line), fp) != NULL) | 2460 | if (fgets(line, sizeof(line), fp) != NULL) |
2450 | value = atoi(line) != 0; | 2461 | value = atoi(line) != 0; |
2451 | 2462 | ||
2452 | fclose(fp); | 2463 | fclose(fp); |
2453 | } | 2464 | } |
2454 | } | 2465 | } |
2455 | 2466 | ||
2456 | return value; | 2467 | return value; |
2457 | } | 2468 | } |
2458 | 2469 | ||
2459 | int symbol__init(void) | 2470 | int symbol__init(void) |
2460 | { | 2471 | { |
2461 | const char *symfs; | 2472 | const char *symfs; |
2462 | 2473 | ||
2463 | if (symbol_conf.initialized) | 2474 | if (symbol_conf.initialized) |
2464 | return 0; | 2475 | return 0; |
2465 | 2476 | ||
2466 | symbol_conf.priv_size = ALIGN(symbol_conf.priv_size, sizeof(u64)); | 2477 | symbol_conf.priv_size = ALIGN(symbol_conf.priv_size, sizeof(u64)); |
2467 | 2478 | ||
2468 | elf_version(EV_CURRENT); | 2479 | elf_version(EV_CURRENT); |
2469 | if (symbol_conf.sort_by_name) | 2480 | if (symbol_conf.sort_by_name) |
2470 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | 2481 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
2471 | sizeof(struct symbol)); | 2482 | sizeof(struct symbol)); |
2472 | 2483 | ||
2473 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) | 2484 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) |
2474 | return -1; | 2485 | return -1; |
2475 | 2486 | ||
2476 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { | 2487 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { |
2477 | pr_err("'.' is the only non valid --field-separator argument\n"); | 2488 | pr_err("'.' is the only non valid --field-separator argument\n"); |
2478 | return -1; | 2489 | return -1; |
2479 | } | 2490 | } |
2480 | 2491 | ||
2481 | if (setup_list(&symbol_conf.dso_list, | 2492 | if (setup_list(&symbol_conf.dso_list, |
2482 | symbol_conf.dso_list_str, "dso") < 0) | 2493 | symbol_conf.dso_list_str, "dso") < 0) |
2483 | return -1; | 2494 | return -1; |
2484 | 2495 | ||
2485 | if (setup_list(&symbol_conf.comm_list, | 2496 | if (setup_list(&symbol_conf.comm_list, |
2486 | symbol_conf.comm_list_str, "comm") < 0) | 2497 | symbol_conf.comm_list_str, "comm") < 0) |
2487 | goto out_free_dso_list; | 2498 | goto out_free_dso_list; |
2488 | 2499 | ||
2489 | if (setup_list(&symbol_conf.sym_list, | 2500 | if (setup_list(&symbol_conf.sym_list, |
2490 | symbol_conf.sym_list_str, "symbol") < 0) | 2501 | symbol_conf.sym_list_str, "symbol") < 0) |
2491 | goto out_free_comm_list; | 2502 | goto out_free_comm_list; |
2492 | 2503 | ||
2493 | /* | 2504 | /* |
2494 | * A path to symbols of "/" is identical to "" | 2505 | * A path to symbols of "/" is identical to "" |
2495 | * reset here for simplicity. | 2506 | * reset here for simplicity. |
2496 | */ | 2507 | */ |
2497 | symfs = realpath(symbol_conf.symfs, NULL); | 2508 | symfs = realpath(symbol_conf.symfs, NULL); |
2498 | if (symfs == NULL) | 2509 | if (symfs == NULL) |
2499 | symfs = symbol_conf.symfs; | 2510 | symfs = symbol_conf.symfs; |
2500 | if (strcmp(symfs, "/") == 0) | 2511 | if (strcmp(symfs, "/") == 0) |
2501 | symbol_conf.symfs = ""; | 2512 | symbol_conf.symfs = ""; |
2502 | if (symfs != symbol_conf.symfs) | 2513 | if (symfs != symbol_conf.symfs) |
2503 | free((void *)symfs); | 2514 | free((void *)symfs); |
2504 | 2515 | ||
2505 | symbol_conf.kptr_restrict = symbol__read_kptr_restrict(); | 2516 | symbol_conf.kptr_restrict = symbol__read_kptr_restrict(); |
2506 | 2517 | ||
2507 | symbol_conf.initialized = true; | 2518 | symbol_conf.initialized = true; |
2508 | return 0; | 2519 | return 0; |
2509 | 2520 | ||
2510 | out_free_dso_list: | 2521 | out_free_dso_list: |
2511 | strlist__delete(symbol_conf.dso_list); | 2522 | strlist__delete(symbol_conf.dso_list); |
2512 | out_free_comm_list: | 2523 | out_free_comm_list: |
2513 | strlist__delete(symbol_conf.comm_list); | 2524 | strlist__delete(symbol_conf.comm_list); |
2514 | return -1; | 2525 | return -1; |
2515 | } | 2526 | } |
2516 | 2527 | ||
2517 | void symbol__exit(void) | 2528 | void symbol__exit(void) |
2518 | { | 2529 | { |
2519 | if (!symbol_conf.initialized) | 2530 | if (!symbol_conf.initialized) |
2520 | return; | 2531 | return; |
2521 | strlist__delete(symbol_conf.sym_list); | 2532 | strlist__delete(symbol_conf.sym_list); |
2522 | strlist__delete(symbol_conf.dso_list); | 2533 | strlist__delete(symbol_conf.dso_list); |
2523 | strlist__delete(symbol_conf.comm_list); | 2534 | strlist__delete(symbol_conf.comm_list); |
2524 | vmlinux_path__exit(); | 2535 | vmlinux_path__exit(); |
2525 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | 2536 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; |
2526 | symbol_conf.initialized = false; | 2537 | symbol_conf.initialized = false; |
2527 | } | 2538 | } |
2528 | 2539 | ||
2529 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid) | 2540 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid) |
2530 | { | 2541 | { |
2531 | struct machine *machine = machines__findnew(machines, pid); | 2542 | struct machine *machine = machines__findnew(machines, pid); |
2532 | 2543 | ||
2533 | if (machine == NULL) | 2544 | if (machine == NULL) |
2534 | return -1; | 2545 | return -1; |
2535 | 2546 | ||
2536 | return machine__create_kernel_maps(machine); | 2547 | return machine__create_kernel_maps(machine); |
2537 | } | 2548 | } |
2538 | 2549 | ||
2539 | static int hex(char ch) | 2550 | static int hex(char ch) |
2540 | { | 2551 | { |
2541 | if ((ch >= '0') && (ch <= '9')) | 2552 | if ((ch >= '0') && (ch <= '9')) |
2542 | return ch - '0'; | 2553 | return ch - '0'; |
2543 | if ((ch >= 'a') && (ch <= 'f')) | 2554 | if ((ch >= 'a') && (ch <= 'f')) |
2544 | return ch - 'a' + 10; | 2555 | return ch - 'a' + 10; |
2545 | if ((ch >= 'A') && (ch <= 'F')) | 2556 | if ((ch >= 'A') && (ch <= 'F')) |
2546 | return ch - 'A' + 10; | 2557 | return ch - 'A' + 10; |
2547 | return -1; | 2558 | return -1; |
2548 | } | 2559 | } |
2549 | 2560 | ||
2550 | /* | 2561 | /* |
2551 | * While we find nice hex chars, build a long_val. | 2562 | * While we find nice hex chars, build a long_val. |
2552 | * Return number of chars processed. | 2563 | * Return number of chars processed. |
2553 | */ | 2564 | */ |
2554 | int hex2u64(const char *ptr, u64 *long_val) | 2565 | int hex2u64(const char *ptr, u64 *long_val) |
2555 | { | 2566 | { |
2556 | const char *p = ptr; | 2567 | const char *p = ptr; |
2557 | *long_val = 0; | 2568 | *long_val = 0; |
2558 | 2569 | ||
2559 | while (*p) { | 2570 | while (*p) { |
2560 | const int hex_val = hex(*p); | 2571 | const int hex_val = hex(*p); |
2561 | 2572 | ||
2562 | if (hex_val < 0) | 2573 | if (hex_val < 0) |
2563 | break; | 2574 | break; |
2564 | 2575 | ||
2565 | *long_val = (*long_val << 4) | hex_val; | 2576 | *long_val = (*long_val << 4) | hex_val; |
2566 | p++; | 2577 | p++; |
2567 | } | 2578 | } |
2568 | 2579 | ||
2569 | return p - ptr; | 2580 | return p - ptr; |
2570 | } | 2581 | } |
2571 | 2582 | ||
2572 | char *strxfrchar(char *s, char from, char to) | 2583 | char *strxfrchar(char *s, char from, char to) |
2573 | { | 2584 | { |
2574 | char *p = s; | 2585 | char *p = s; |
2575 | 2586 | ||
2576 | while ((p = strchr(p, from)) != NULL) | 2587 | while ((p = strchr(p, from)) != NULL) |
2577 | *p++ = to; | 2588 | *p++ = to; |
2578 | 2589 | ||
2579 | return s; | 2590 | return s; |
2580 | } | 2591 | } |
2581 | 2592 | ||
2582 | int machines__create_guest_kernel_maps(struct rb_root *machines) | 2593 | int machines__create_guest_kernel_maps(struct rb_root *machines) |
2583 | { | 2594 | { |
2584 | int ret = 0; | 2595 | int ret = 0; |
2585 | struct dirent **namelist = NULL; | 2596 | struct dirent **namelist = NULL; |
2586 | int i, items = 0; | 2597 | int i, items = 0; |
2587 | char path[PATH_MAX]; | 2598 | char path[PATH_MAX]; |
2588 | pid_t pid; | 2599 | pid_t pid; |
2589 | 2600 | ||
2590 | if (symbol_conf.default_guest_vmlinux_name || | 2601 | if (symbol_conf.default_guest_vmlinux_name || |
2591 | symbol_conf.default_guest_modules || | 2602 | symbol_conf.default_guest_modules || |
2592 | symbol_conf.default_guest_kallsyms) { | 2603 | symbol_conf.default_guest_kallsyms) { |
2593 | machines__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); | 2604 | machines__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); |
2594 | } | 2605 | } |
2595 | 2606 | ||
2596 | if (symbol_conf.guestmount) { | 2607 | if (symbol_conf.guestmount) { |
2597 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); | 2608 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); |
2598 | if (items <= 0) | 2609 | if (items <= 0) |
2599 | return -ENOENT; | 2610 | return -ENOENT; |
2600 | for (i = 0; i < items; i++) { | 2611 | for (i = 0; i < items; i++) { |
2601 | if (!isdigit(namelist[i]->d_name[0])) { | 2612 | if (!isdigit(namelist[i]->d_name[0])) { |
2602 | /* Filter out . and .. */ | 2613 | /* Filter out . and .. */ |
2603 | continue; | 2614 | continue; |
2604 | } | 2615 | } |
2605 | pid = atoi(namelist[i]->d_name); | 2616 | pid = atoi(namelist[i]->d_name); |
2606 | sprintf(path, "%s/%s/proc/kallsyms", | 2617 | sprintf(path, "%s/%s/proc/kallsyms", |
2607 | symbol_conf.guestmount, | 2618 | symbol_conf.guestmount, |
2608 | namelist[i]->d_name); | 2619 | namelist[i]->d_name); |
2609 | ret = access(path, R_OK); | 2620 | ret = access(path, R_OK); |
2610 | if (ret) { | 2621 | if (ret) { |
2611 | pr_debug("Can't access file %s\n", path); | 2622 | pr_debug("Can't access file %s\n", path); |
2612 | goto failure; | 2623 | goto failure; |
2613 | } | 2624 | } |
2614 | machines__create_kernel_maps(machines, pid); | 2625 | machines__create_kernel_maps(machines, pid); |
2615 | } | 2626 | } |
2616 | failure: | 2627 | failure: |
2617 | free(namelist); | 2628 | free(namelist); |
2618 | } | 2629 | } |
2619 | 2630 | ||
2620 | return ret; | 2631 | return ret; |
2621 | } | 2632 | } |
2622 | 2633 | ||
2623 | void machines__destroy_guest_kernel_maps(struct rb_root *machines) | 2634 | void machines__destroy_guest_kernel_maps(struct rb_root *machines) |
2624 | { | 2635 | { |
2625 | struct rb_node *next = rb_first(machines); | 2636 | struct rb_node *next = rb_first(machines); |
2626 | 2637 | ||
2627 | while (next) { | 2638 | while (next) { |
2628 | struct machine *pos = rb_entry(next, struct machine, rb_node); | 2639 | struct machine *pos = rb_entry(next, struct machine, rb_node); |
2629 | 2640 | ||
2630 | next = rb_next(&pos->rb_node); | 2641 | next = rb_next(&pos->rb_node); |
2631 | rb_erase(&pos->rb_node, machines); | 2642 | rb_erase(&pos->rb_node, machines); |
2632 | machine__delete(pos); | 2643 | machine__delete(pos); |
2633 | } | 2644 | } |
2634 | } | 2645 | } |
2635 | 2646 | ||
2636 | int machine__load_kallsyms(struct machine *machine, const char *filename, | 2647 | int machine__load_kallsyms(struct machine *machine, const char *filename, |
2637 | enum map_type type, symbol_filter_t filter) | 2648 | enum map_type type, symbol_filter_t filter) |
2638 | { | 2649 | { |
2639 | struct map *map = machine->vmlinux_maps[type]; | 2650 | struct map *map = machine->vmlinux_maps[type]; |
2640 | int ret = dso__load_kallsyms(map->dso, filename, map, filter); | 2651 | int ret = dso__load_kallsyms(map->dso, filename, map, filter); |
2641 | 2652 | ||
2642 | if (ret > 0) { | 2653 | if (ret > 0) { |
2643 | dso__set_loaded(map->dso, type); | 2654 | dso__set_loaded(map->dso, type); |
2644 | /* | 2655 | /* |
2645 | * Since /proc/kallsyms will have multiple sessions for the | 2656 | * Since /proc/kallsyms will have multiple sessions for the |
2646 | * kernel, with modules between them, fixup the end of all | 2657 | * kernel, with modules between them, fixup the end of all |
2647 | * sections. | 2658 | * sections. |
2648 | */ | 2659 | */ |
2649 | __map_groups__fixup_end(&machine->kmaps, type); | 2660 | __map_groups__fixup_end(&machine->kmaps, type); |
2650 | } | 2661 | } |
2651 | 2662 | ||
2652 | return ret; | 2663 | return ret; |
2653 | } | 2664 | } |
2654 | 2665 | ||
2655 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | 2666 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, |
2656 | symbol_filter_t filter) | 2667 | symbol_filter_t filter) |
2657 | { | 2668 | { |
2658 | struct map *map = machine->vmlinux_maps[type]; | 2669 | struct map *map = machine->vmlinux_maps[type]; |
2659 | int ret = dso__load_vmlinux_path(map->dso, map, filter); | 2670 | int ret = dso__load_vmlinux_path(map->dso, map, filter); |
2660 | 2671 | ||
2661 | if (ret > 0) { | 2672 | if (ret > 0) { |
2662 | dso__set_loaded(map->dso, type); | 2673 | dso__set_loaded(map->dso, type); |
2663 | map__reloc_vmlinux(map); | 2674 | map__reloc_vmlinux(map); |
2664 | } | 2675 | } |
2665 | 2676 | ||
2666 | return ret; | 2677 | return ret; |
2667 | } | 2678 | } |
2668 | 2679 |