diff options
Diffstat (limited to 'tools/perf/util/cs-etm.c')
-rw-r--r-- | tools/perf/util/cs-etm.c | 1023 |
1 files changed, 1023 insertions, 0 deletions
diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c new file mode 100644 index 000000000000..b9f0a53dfa65 --- /dev/null +++ b/tools/perf/util/cs-etm.c | |||
@@ -0,0 +1,1023 @@ | |||
1 | /* | ||
2 | * SPDX-License-Identifier: GPL-2.0 | ||
3 | * | ||
4 | * Copyright(C) 2015-2018 Linaro Limited. | ||
5 | * | ||
6 | * Author: Tor Jeremiassen <tor@ti.com> | ||
7 | * Author: Mathieu Poirier <mathieu.poirier@linaro.org> | ||
8 | */ | ||
9 | |||
10 | #include <linux/bitops.h> | ||
11 | #include <linux/err.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/log2.h> | ||
14 | #include <linux/types.h> | ||
15 | |||
16 | #include <stdlib.h> | ||
17 | |||
18 | #include "auxtrace.h" | ||
19 | #include "color.h" | ||
20 | #include "cs-etm.h" | ||
21 | #include "cs-etm-decoder/cs-etm-decoder.h" | ||
22 | #include "debug.h" | ||
23 | #include "evlist.h" | ||
24 | #include "intlist.h" | ||
25 | #include "machine.h" | ||
26 | #include "map.h" | ||
27 | #include "perf.h" | ||
28 | #include "thread.h" | ||
29 | #include "thread_map.h" | ||
30 | #include "thread-stack.h" | ||
31 | #include "util.h" | ||
32 | |||
33 | #define MAX_TIMESTAMP (~0ULL) | ||
34 | |||
35 | struct cs_etm_auxtrace { | ||
36 | struct auxtrace auxtrace; | ||
37 | struct auxtrace_queues queues; | ||
38 | struct auxtrace_heap heap; | ||
39 | struct itrace_synth_opts synth_opts; | ||
40 | struct perf_session *session; | ||
41 | struct machine *machine; | ||
42 | struct thread *unknown_thread; | ||
43 | |||
44 | u8 timeless_decoding; | ||
45 | u8 snapshot_mode; | ||
46 | u8 data_queued; | ||
47 | u8 sample_branches; | ||
48 | |||
49 | int num_cpu; | ||
50 | u32 auxtrace_type; | ||
51 | u64 branches_sample_type; | ||
52 | u64 branches_id; | ||
53 | u64 **metadata; | ||
54 | u64 kernel_start; | ||
55 | unsigned int pmu_type; | ||
56 | }; | ||
57 | |||
58 | struct cs_etm_queue { | ||
59 | struct cs_etm_auxtrace *etm; | ||
60 | struct thread *thread; | ||
61 | struct cs_etm_decoder *decoder; | ||
62 | struct auxtrace_buffer *buffer; | ||
63 | const struct cs_etm_state *state; | ||
64 | union perf_event *event_buf; | ||
65 | unsigned int queue_nr; | ||
66 | pid_t pid, tid; | ||
67 | int cpu; | ||
68 | u64 time; | ||
69 | u64 timestamp; | ||
70 | u64 offset; | ||
71 | }; | ||
72 | |||
73 | static int cs_etm__update_queues(struct cs_etm_auxtrace *etm); | ||
74 | static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm, | ||
75 | pid_t tid, u64 time_); | ||
76 | |||
77 | static void cs_etm__packet_dump(const char *pkt_string) | ||
78 | { | ||
79 | const char *color = PERF_COLOR_BLUE; | ||
80 | int len = strlen(pkt_string); | ||
81 | |||
82 | if (len && (pkt_string[len-1] == '\n')) | ||
83 | color_fprintf(stdout, color, " %s", pkt_string); | ||
84 | else | ||
85 | color_fprintf(stdout, color, " %s\n", pkt_string); | ||
86 | |||
87 | fflush(stdout); | ||
88 | } | ||
89 | |||
90 | static void cs_etm__dump_event(struct cs_etm_auxtrace *etm, | ||
91 | struct auxtrace_buffer *buffer) | ||
92 | { | ||
93 | int i, ret; | ||
94 | const char *color = PERF_COLOR_BLUE; | ||
95 | struct cs_etm_decoder_params d_params; | ||
96 | struct cs_etm_trace_params *t_params; | ||
97 | struct cs_etm_decoder *decoder; | ||
98 | size_t buffer_used = 0; | ||
99 | |||
100 | fprintf(stdout, "\n"); | ||
101 | color_fprintf(stdout, color, | ||
102 | ". ... CoreSight ETM Trace data: size %zu bytes\n", | ||
103 | buffer->size); | ||
104 | |||
105 | /* Use metadata to fill in trace parameters for trace decoder */ | ||
106 | t_params = zalloc(sizeof(*t_params) * etm->num_cpu); | ||
107 | for (i = 0; i < etm->num_cpu; i++) { | ||
108 | t_params[i].protocol = CS_ETM_PROTO_ETMV4i; | ||
109 | t_params[i].etmv4.reg_idr0 = etm->metadata[i][CS_ETMV4_TRCIDR0]; | ||
110 | t_params[i].etmv4.reg_idr1 = etm->metadata[i][CS_ETMV4_TRCIDR1]; | ||
111 | t_params[i].etmv4.reg_idr2 = etm->metadata[i][CS_ETMV4_TRCIDR2]; | ||
112 | t_params[i].etmv4.reg_idr8 = etm->metadata[i][CS_ETMV4_TRCIDR8]; | ||
113 | t_params[i].etmv4.reg_configr = | ||
114 | etm->metadata[i][CS_ETMV4_TRCCONFIGR]; | ||
115 | t_params[i].etmv4.reg_traceidr = | ||
116 | etm->metadata[i][CS_ETMV4_TRCTRACEIDR]; | ||
117 | } | ||
118 | |||
119 | /* Set decoder parameters to simply print the trace packets */ | ||
120 | d_params.packet_printer = cs_etm__packet_dump; | ||
121 | d_params.operation = CS_ETM_OPERATION_PRINT; | ||
122 | d_params.formatted = true; | ||
123 | d_params.fsyncs = false; | ||
124 | d_params.hsyncs = false; | ||
125 | d_params.frame_aligned = true; | ||
126 | |||
127 | decoder = cs_etm_decoder__new(etm->num_cpu, &d_params, t_params); | ||
128 | |||
129 | zfree(&t_params); | ||
130 | |||
131 | if (!decoder) | ||
132 | return; | ||
133 | do { | ||
134 | size_t consumed; | ||
135 | |||
136 | ret = cs_etm_decoder__process_data_block( | ||
137 | decoder, buffer->offset, | ||
138 | &((u8 *)buffer->data)[buffer_used], | ||
139 | buffer->size - buffer_used, &consumed); | ||
140 | if (ret) | ||
141 | break; | ||
142 | |||
143 | buffer_used += consumed; | ||
144 | } while (buffer_used < buffer->size); | ||
145 | |||
146 | cs_etm_decoder__free(decoder); | ||
147 | } | ||
148 | |||
149 | static int cs_etm__flush_events(struct perf_session *session, | ||
150 | struct perf_tool *tool) | ||
151 | { | ||
152 | int ret; | ||
153 | struct cs_etm_auxtrace *etm = container_of(session->auxtrace, | ||
154 | struct cs_etm_auxtrace, | ||
155 | auxtrace); | ||
156 | if (dump_trace) | ||
157 | return 0; | ||
158 | |||
159 | if (!tool->ordered_events) | ||
160 | return -EINVAL; | ||
161 | |||
162 | if (!etm->timeless_decoding) | ||
163 | return -EINVAL; | ||
164 | |||
165 | ret = cs_etm__update_queues(etm); | ||
166 | |||
167 | if (ret < 0) | ||
168 | return ret; | ||
169 | |||
170 | return cs_etm__process_timeless_queues(etm, -1, MAX_TIMESTAMP - 1); | ||
171 | } | ||
172 | |||
173 | static void cs_etm__free_queue(void *priv) | ||
174 | { | ||
175 | struct cs_etm_queue *etmq = priv; | ||
176 | |||
177 | free(etmq); | ||
178 | } | ||
179 | |||
180 | static void cs_etm__free_events(struct perf_session *session) | ||
181 | { | ||
182 | unsigned int i; | ||
183 | struct cs_etm_auxtrace *aux = container_of(session->auxtrace, | ||
184 | struct cs_etm_auxtrace, | ||
185 | auxtrace); | ||
186 | struct auxtrace_queues *queues = &aux->queues; | ||
187 | |||
188 | for (i = 0; i < queues->nr_queues; i++) { | ||
189 | cs_etm__free_queue(queues->queue_array[i].priv); | ||
190 | queues->queue_array[i].priv = NULL; | ||
191 | } | ||
192 | |||
193 | auxtrace_queues__free(queues); | ||
194 | } | ||
195 | |||
196 | static void cs_etm__free(struct perf_session *session) | ||
197 | { | ||
198 | int i; | ||
199 | struct int_node *inode, *tmp; | ||
200 | struct cs_etm_auxtrace *aux = container_of(session->auxtrace, | ||
201 | struct cs_etm_auxtrace, | ||
202 | auxtrace); | ||
203 | cs_etm__free_events(session); | ||
204 | session->auxtrace = NULL; | ||
205 | |||
206 | /* First remove all traceID/CPU# nodes for the RB tree */ | ||
207 | intlist__for_each_entry_safe(inode, tmp, traceid_list) | ||
208 | intlist__remove(traceid_list, inode); | ||
209 | /* Then the RB tree itself */ | ||
210 | intlist__delete(traceid_list); | ||
211 | |||
212 | for (i = 0; i < aux->num_cpu; i++) | ||
213 | zfree(&aux->metadata[i]); | ||
214 | |||
215 | zfree(&aux->metadata); | ||
216 | zfree(&aux); | ||
217 | } | ||
218 | |||
219 | static u32 cs_etm__mem_access(struct cs_etm_queue *etmq, u64 address, | ||
220 | size_t size, u8 *buffer) | ||
221 | { | ||
222 | u8 cpumode; | ||
223 | u64 offset; | ||
224 | int len; | ||
225 | struct thread *thread; | ||
226 | struct machine *machine; | ||
227 | struct addr_location al; | ||
228 | |||
229 | if (!etmq) | ||
230 | return -1; | ||
231 | |||
232 | machine = etmq->etm->machine; | ||
233 | if (address >= etmq->etm->kernel_start) | ||
234 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
235 | else | ||
236 | cpumode = PERF_RECORD_MISC_USER; | ||
237 | |||
238 | thread = etmq->thread; | ||
239 | if (!thread) { | ||
240 | if (cpumode != PERF_RECORD_MISC_KERNEL) | ||
241 | return -EINVAL; | ||
242 | thread = etmq->etm->unknown_thread; | ||
243 | } | ||
244 | |||
245 | thread__find_addr_map(thread, cpumode, MAP__FUNCTION, address, &al); | ||
246 | |||
247 | if (!al.map || !al.map->dso) | ||
248 | return 0; | ||
249 | |||
250 | if (al.map->dso->data.status == DSO_DATA_STATUS_ERROR && | ||
251 | dso__data_status_seen(al.map->dso, DSO_DATA_STATUS_SEEN_ITRACE)) | ||
252 | return 0; | ||
253 | |||
254 | offset = al.map->map_ip(al.map, address); | ||
255 | |||
256 | map__load(al.map); | ||
257 | |||
258 | len = dso__data_read_offset(al.map->dso, machine, offset, buffer, size); | ||
259 | |||
260 | if (len <= 0) | ||
261 | return 0; | ||
262 | |||
263 | return len; | ||
264 | } | ||
265 | |||
266 | static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm, | ||
267 | unsigned int queue_nr) | ||
268 | { | ||
269 | int i; | ||
270 | struct cs_etm_decoder_params d_params; | ||
271 | struct cs_etm_trace_params *t_params; | ||
272 | struct cs_etm_queue *etmq; | ||
273 | |||
274 | etmq = zalloc(sizeof(*etmq)); | ||
275 | if (!etmq) | ||
276 | return NULL; | ||
277 | |||
278 | etmq->event_buf = malloc(PERF_SAMPLE_MAX_SIZE); | ||
279 | if (!etmq->event_buf) | ||
280 | goto out_free; | ||
281 | |||
282 | etmq->etm = etm; | ||
283 | etmq->queue_nr = queue_nr; | ||
284 | etmq->pid = -1; | ||
285 | etmq->tid = -1; | ||
286 | etmq->cpu = -1; | ||
287 | |||
288 | /* Use metadata to fill in trace parameters for trace decoder */ | ||
289 | t_params = zalloc(sizeof(*t_params) * etm->num_cpu); | ||
290 | |||
291 | if (!t_params) | ||
292 | goto out_free; | ||
293 | |||
294 | for (i = 0; i < etm->num_cpu; i++) { | ||
295 | t_params[i].protocol = CS_ETM_PROTO_ETMV4i; | ||
296 | t_params[i].etmv4.reg_idr0 = etm->metadata[i][CS_ETMV4_TRCIDR0]; | ||
297 | t_params[i].etmv4.reg_idr1 = etm->metadata[i][CS_ETMV4_TRCIDR1]; | ||
298 | t_params[i].etmv4.reg_idr2 = etm->metadata[i][CS_ETMV4_TRCIDR2]; | ||
299 | t_params[i].etmv4.reg_idr8 = etm->metadata[i][CS_ETMV4_TRCIDR8]; | ||
300 | t_params[i].etmv4.reg_configr = | ||
301 | etm->metadata[i][CS_ETMV4_TRCCONFIGR]; | ||
302 | t_params[i].etmv4.reg_traceidr = | ||
303 | etm->metadata[i][CS_ETMV4_TRCTRACEIDR]; | ||
304 | } | ||
305 | |||
306 | /* Set decoder parameters to simply print the trace packets */ | ||
307 | d_params.packet_printer = cs_etm__packet_dump; | ||
308 | d_params.operation = CS_ETM_OPERATION_DECODE; | ||
309 | d_params.formatted = true; | ||
310 | d_params.fsyncs = false; | ||
311 | d_params.hsyncs = false; | ||
312 | d_params.frame_aligned = true; | ||
313 | d_params.data = etmq; | ||
314 | |||
315 | etmq->decoder = cs_etm_decoder__new(etm->num_cpu, &d_params, t_params); | ||
316 | |||
317 | zfree(&t_params); | ||
318 | |||
319 | if (!etmq->decoder) | ||
320 | goto out_free; | ||
321 | |||
322 | /* | ||
323 | * Register a function to handle all memory accesses required by | ||
324 | * the trace decoder library. | ||
325 | */ | ||
326 | if (cs_etm_decoder__add_mem_access_cb(etmq->decoder, | ||
327 | 0x0L, ((u64) -1L), | ||
328 | cs_etm__mem_access)) | ||
329 | goto out_free_decoder; | ||
330 | |||
331 | etmq->offset = 0; | ||
332 | |||
333 | return etmq; | ||
334 | |||
335 | out_free_decoder: | ||
336 | cs_etm_decoder__free(etmq->decoder); | ||
337 | out_free: | ||
338 | zfree(&etmq->event_buf); | ||
339 | free(etmq); | ||
340 | |||
341 | return NULL; | ||
342 | } | ||
343 | |||
344 | static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm, | ||
345 | struct auxtrace_queue *queue, | ||
346 | unsigned int queue_nr) | ||
347 | { | ||
348 | struct cs_etm_queue *etmq = queue->priv; | ||
349 | |||
350 | if (list_empty(&queue->head) || etmq) | ||
351 | return 0; | ||
352 | |||
353 | etmq = cs_etm__alloc_queue(etm, queue_nr); | ||
354 | |||
355 | if (!etmq) | ||
356 | return -ENOMEM; | ||
357 | |||
358 | queue->priv = etmq; | ||
359 | |||
360 | if (queue->cpu != -1) | ||
361 | etmq->cpu = queue->cpu; | ||
362 | |||
363 | etmq->tid = queue->tid; | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static int cs_etm__setup_queues(struct cs_etm_auxtrace *etm) | ||
369 | { | ||
370 | unsigned int i; | ||
371 | int ret; | ||
372 | |||
373 | for (i = 0; i < etm->queues.nr_queues; i++) { | ||
374 | ret = cs_etm__setup_queue(etm, &etm->queues.queue_array[i], i); | ||
375 | if (ret) | ||
376 | return ret; | ||
377 | } | ||
378 | |||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | static int cs_etm__update_queues(struct cs_etm_auxtrace *etm) | ||
383 | { | ||
384 | if (etm->queues.new_data) { | ||
385 | etm->queues.new_data = false; | ||
386 | return cs_etm__setup_queues(etm); | ||
387 | } | ||
388 | |||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | static int | ||
393 | cs_etm__get_trace(struct cs_etm_buffer *buff, struct cs_etm_queue *etmq) | ||
394 | { | ||
395 | struct auxtrace_buffer *aux_buffer = etmq->buffer; | ||
396 | struct auxtrace_buffer *old_buffer = aux_buffer; | ||
397 | struct auxtrace_queue *queue; | ||
398 | |||
399 | queue = &etmq->etm->queues.queue_array[etmq->queue_nr]; | ||
400 | |||
401 | aux_buffer = auxtrace_buffer__next(queue, aux_buffer); | ||
402 | |||
403 | /* If no more data, drop the previous auxtrace_buffer and return */ | ||
404 | if (!aux_buffer) { | ||
405 | if (old_buffer) | ||
406 | auxtrace_buffer__drop_data(old_buffer); | ||
407 | buff->len = 0; | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | etmq->buffer = aux_buffer; | ||
412 | |||
413 | /* If the aux_buffer doesn't have data associated, try to load it */ | ||
414 | if (!aux_buffer->data) { | ||
415 | /* get the file desc associated with the perf data file */ | ||
416 | int fd = perf_data__fd(etmq->etm->session->data); | ||
417 | |||
418 | aux_buffer->data = auxtrace_buffer__get_data(aux_buffer, fd); | ||
419 | if (!aux_buffer->data) | ||
420 | return -ENOMEM; | ||
421 | } | ||
422 | |||
423 | /* If valid, drop the previous buffer */ | ||
424 | if (old_buffer) | ||
425 | auxtrace_buffer__drop_data(old_buffer); | ||
426 | |||
427 | buff->offset = aux_buffer->offset; | ||
428 | buff->len = aux_buffer->size; | ||
429 | buff->buf = aux_buffer->data; | ||
430 | |||
431 | buff->ref_timestamp = aux_buffer->reference; | ||
432 | |||
433 | return buff->len; | ||
434 | } | ||
435 | |||
436 | static void cs_etm__set_pid_tid_cpu(struct cs_etm_auxtrace *etm, | ||
437 | struct auxtrace_queue *queue) | ||
438 | { | ||
439 | struct cs_etm_queue *etmq = queue->priv; | ||
440 | |||
441 | /* CPU-wide tracing isn't supported yet */ | ||
442 | if (queue->tid == -1) | ||
443 | return; | ||
444 | |||
445 | if ((!etmq->thread) && (etmq->tid != -1)) | ||
446 | etmq->thread = machine__find_thread(etm->machine, -1, | ||
447 | etmq->tid); | ||
448 | |||
449 | if (etmq->thread) { | ||
450 | etmq->pid = etmq->thread->pid_; | ||
451 | if (queue->cpu == -1) | ||
452 | etmq->cpu = etmq->thread->cpu; | ||
453 | } | ||
454 | } | ||
455 | |||
456 | /* | ||
457 | * The cs etm packet encodes an instruction range between a branch target | ||
458 | * and the next taken branch. Generate sample accordingly. | ||
459 | */ | ||
460 | static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, | ||
461 | struct cs_etm_packet *packet) | ||
462 | { | ||
463 | int ret = 0; | ||
464 | struct cs_etm_auxtrace *etm = etmq->etm; | ||
465 | struct perf_sample sample = {.ip = 0,}; | ||
466 | union perf_event *event = etmq->event_buf; | ||
467 | u64 start_addr = packet->start_addr; | ||
468 | u64 end_addr = packet->end_addr; | ||
469 | |||
470 | event->sample.header.type = PERF_RECORD_SAMPLE; | ||
471 | event->sample.header.misc = PERF_RECORD_MISC_USER; | ||
472 | event->sample.header.size = sizeof(struct perf_event_header); | ||
473 | |||
474 | sample.ip = start_addr; | ||
475 | sample.pid = etmq->pid; | ||
476 | sample.tid = etmq->tid; | ||
477 | sample.addr = end_addr; | ||
478 | sample.id = etmq->etm->branches_id; | ||
479 | sample.stream_id = etmq->etm->branches_id; | ||
480 | sample.period = 1; | ||
481 | sample.cpu = packet->cpu; | ||
482 | sample.flags = 0; | ||
483 | sample.cpumode = PERF_RECORD_MISC_USER; | ||
484 | |||
485 | ret = perf_session__deliver_synth_event(etm->session, event, &sample); | ||
486 | |||
487 | if (ret) | ||
488 | pr_err( | ||
489 | "CS ETM Trace: failed to deliver instruction event, error %d\n", | ||
490 | ret); | ||
491 | |||
492 | return ret; | ||
493 | } | ||
494 | |||
495 | struct cs_etm_synth { | ||
496 | struct perf_tool dummy_tool; | ||
497 | struct perf_session *session; | ||
498 | }; | ||
499 | |||
500 | static int cs_etm__event_synth(struct perf_tool *tool, | ||
501 | union perf_event *event, | ||
502 | struct perf_sample *sample __maybe_unused, | ||
503 | struct machine *machine __maybe_unused) | ||
504 | { | ||
505 | struct cs_etm_synth *cs_etm_synth = | ||
506 | container_of(tool, struct cs_etm_synth, dummy_tool); | ||
507 | |||
508 | return perf_session__deliver_synth_event(cs_etm_synth->session, | ||
509 | event, NULL); | ||
510 | } | ||
511 | |||
512 | static int cs_etm__synth_event(struct perf_session *session, | ||
513 | struct perf_event_attr *attr, u64 id) | ||
514 | { | ||
515 | struct cs_etm_synth cs_etm_synth; | ||
516 | |||
517 | memset(&cs_etm_synth, 0, sizeof(struct cs_etm_synth)); | ||
518 | cs_etm_synth.session = session; | ||
519 | |||
520 | return perf_event__synthesize_attr(&cs_etm_synth.dummy_tool, attr, 1, | ||
521 | &id, cs_etm__event_synth); | ||
522 | } | ||
523 | |||
524 | static int cs_etm__synth_events(struct cs_etm_auxtrace *etm, | ||
525 | struct perf_session *session) | ||
526 | { | ||
527 | struct perf_evlist *evlist = session->evlist; | ||
528 | struct perf_evsel *evsel; | ||
529 | struct perf_event_attr attr; | ||
530 | bool found = false; | ||
531 | u64 id; | ||
532 | int err; | ||
533 | |||
534 | evlist__for_each_entry(evlist, evsel) { | ||
535 | if (evsel->attr.type == etm->pmu_type) { | ||
536 | found = true; | ||
537 | break; | ||
538 | } | ||
539 | } | ||
540 | |||
541 | if (!found) { | ||
542 | pr_debug("No selected events with CoreSight Trace data\n"); | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | memset(&attr, 0, sizeof(struct perf_event_attr)); | ||
547 | attr.size = sizeof(struct perf_event_attr); | ||
548 | attr.type = PERF_TYPE_HARDWARE; | ||
549 | attr.sample_type = evsel->attr.sample_type & PERF_SAMPLE_MASK; | ||
550 | attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID | | ||
551 | PERF_SAMPLE_PERIOD; | ||
552 | if (etm->timeless_decoding) | ||
553 | attr.sample_type &= ~(u64)PERF_SAMPLE_TIME; | ||
554 | else | ||
555 | attr.sample_type |= PERF_SAMPLE_TIME; | ||
556 | |||
557 | attr.exclude_user = evsel->attr.exclude_user; | ||
558 | attr.exclude_kernel = evsel->attr.exclude_kernel; | ||
559 | attr.exclude_hv = evsel->attr.exclude_hv; | ||
560 | attr.exclude_host = evsel->attr.exclude_host; | ||
561 | attr.exclude_guest = evsel->attr.exclude_guest; | ||
562 | attr.sample_id_all = evsel->attr.sample_id_all; | ||
563 | attr.read_format = evsel->attr.read_format; | ||
564 | |||
565 | /* create new id val to be a fixed offset from evsel id */ | ||
566 | id = evsel->id[0] + 1000000000; | ||
567 | |||
568 | if (!id) | ||
569 | id = 1; | ||
570 | |||
571 | if (etm->synth_opts.branches) { | ||
572 | attr.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS; | ||
573 | attr.sample_period = 1; | ||
574 | attr.sample_type |= PERF_SAMPLE_ADDR; | ||
575 | err = cs_etm__synth_event(session, &attr, id); | ||
576 | if (err) | ||
577 | return err; | ||
578 | etm->sample_branches = true; | ||
579 | etm->branches_sample_type = attr.sample_type; | ||
580 | etm->branches_id = id; | ||
581 | } | ||
582 | |||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | static int cs_etm__sample(struct cs_etm_queue *etmq) | ||
587 | { | ||
588 | int ret; | ||
589 | struct cs_etm_packet packet; | ||
590 | |||
591 | while (1) { | ||
592 | ret = cs_etm_decoder__get_packet(etmq->decoder, &packet); | ||
593 | if (ret <= 0) | ||
594 | return ret; | ||
595 | |||
596 | /* | ||
597 | * If the packet contains an instruction range, generate an | ||
598 | * instruction sequence event. | ||
599 | */ | ||
600 | if (packet.sample_type & CS_ETM_RANGE) | ||
601 | cs_etm__synth_branch_sample(etmq, &packet); | ||
602 | } | ||
603 | |||
604 | return 0; | ||
605 | } | ||
606 | |||
607 | static int cs_etm__run_decoder(struct cs_etm_queue *etmq) | ||
608 | { | ||
609 | struct cs_etm_auxtrace *etm = etmq->etm; | ||
610 | struct cs_etm_buffer buffer; | ||
611 | size_t buffer_used, processed; | ||
612 | int err = 0; | ||
613 | |||
614 | if (!etm->kernel_start) | ||
615 | etm->kernel_start = machine__kernel_start(etm->machine); | ||
616 | |||
617 | /* Go through each buffer in the queue and decode them one by one */ | ||
618 | more: | ||
619 | buffer_used = 0; | ||
620 | memset(&buffer, 0, sizeof(buffer)); | ||
621 | err = cs_etm__get_trace(&buffer, etmq); | ||
622 | if (err <= 0) | ||
623 | return err; | ||
624 | /* | ||
625 | * We cannot assume consecutive blocks in the data file are contiguous, | ||
626 | * reset the decoder to force re-sync. | ||
627 | */ | ||
628 | err = cs_etm_decoder__reset(etmq->decoder); | ||
629 | if (err != 0) | ||
630 | return err; | ||
631 | |||
632 | /* Run trace decoder until buffer consumed or end of trace */ | ||
633 | do { | ||
634 | processed = 0; | ||
635 | |||
636 | err = cs_etm_decoder__process_data_block( | ||
637 | etmq->decoder, | ||
638 | etmq->offset, | ||
639 | &buffer.buf[buffer_used], | ||
640 | buffer.len - buffer_used, | ||
641 | &processed); | ||
642 | |||
643 | if (err) | ||
644 | return err; | ||
645 | |||
646 | etmq->offset += processed; | ||
647 | buffer_used += processed; | ||
648 | |||
649 | /* | ||
650 | * Nothing to do with an error condition, let's hope the next | ||
651 | * chunk will be better. | ||
652 | */ | ||
653 | err = cs_etm__sample(etmq); | ||
654 | } while (buffer.len > buffer_used); | ||
655 | |||
656 | goto more; | ||
657 | |||
658 | return err; | ||
659 | } | ||
660 | |||
661 | static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm, | ||
662 | pid_t tid, u64 time_) | ||
663 | { | ||
664 | unsigned int i; | ||
665 | struct auxtrace_queues *queues = &etm->queues; | ||
666 | |||
667 | for (i = 0; i < queues->nr_queues; i++) { | ||
668 | struct auxtrace_queue *queue = &etm->queues.queue_array[i]; | ||
669 | struct cs_etm_queue *etmq = queue->priv; | ||
670 | |||
671 | if (etmq && ((tid == -1) || (etmq->tid == tid))) { | ||
672 | etmq->time = time_; | ||
673 | cs_etm__set_pid_tid_cpu(etm, queue); | ||
674 | cs_etm__run_decoder(etmq); | ||
675 | } | ||
676 | } | ||
677 | |||
678 | return 0; | ||
679 | } | ||
680 | |||
681 | static int cs_etm__process_event(struct perf_session *session, | ||
682 | union perf_event *event, | ||
683 | struct perf_sample *sample, | ||
684 | struct perf_tool *tool) | ||
685 | { | ||
686 | int err = 0; | ||
687 | u64 timestamp; | ||
688 | struct cs_etm_auxtrace *etm = container_of(session->auxtrace, | ||
689 | struct cs_etm_auxtrace, | ||
690 | auxtrace); | ||
691 | |||
692 | if (dump_trace) | ||
693 | return 0; | ||
694 | |||
695 | if (!tool->ordered_events) { | ||
696 | pr_err("CoreSight ETM Trace requires ordered events\n"); | ||
697 | return -EINVAL; | ||
698 | } | ||
699 | |||
700 | if (!etm->timeless_decoding) | ||
701 | return -EINVAL; | ||
702 | |||
703 | if (sample->time && (sample->time != (u64) -1)) | ||
704 | timestamp = sample->time; | ||
705 | else | ||
706 | timestamp = 0; | ||
707 | |||
708 | if (timestamp || etm->timeless_decoding) { | ||
709 | err = cs_etm__update_queues(etm); | ||
710 | if (err) | ||
711 | return err; | ||
712 | } | ||
713 | |||
714 | if (event->header.type == PERF_RECORD_EXIT) | ||
715 | return cs_etm__process_timeless_queues(etm, | ||
716 | event->fork.tid, | ||
717 | sample->time); | ||
718 | |||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | static int cs_etm__process_auxtrace_event(struct perf_session *session, | ||
723 | union perf_event *event, | ||
724 | struct perf_tool *tool __maybe_unused) | ||
725 | { | ||
726 | struct cs_etm_auxtrace *etm = container_of(session->auxtrace, | ||
727 | struct cs_etm_auxtrace, | ||
728 | auxtrace); | ||
729 | if (!etm->data_queued) { | ||
730 | struct auxtrace_buffer *buffer; | ||
731 | off_t data_offset; | ||
732 | int fd = perf_data__fd(session->data); | ||
733 | bool is_pipe = perf_data__is_pipe(session->data); | ||
734 | int err; | ||
735 | |||
736 | if (is_pipe) | ||
737 | data_offset = 0; | ||
738 | else { | ||
739 | data_offset = lseek(fd, 0, SEEK_CUR); | ||
740 | if (data_offset == -1) | ||
741 | return -errno; | ||
742 | } | ||
743 | |||
744 | err = auxtrace_queues__add_event(&etm->queues, session, | ||
745 | event, data_offset, &buffer); | ||
746 | if (err) | ||
747 | return err; | ||
748 | |||
749 | if (dump_trace) | ||
750 | if (auxtrace_buffer__get_data(buffer, fd)) { | ||
751 | cs_etm__dump_event(etm, buffer); | ||
752 | auxtrace_buffer__put_data(buffer); | ||
753 | } | ||
754 | } | ||
755 | |||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static bool cs_etm__is_timeless_decoding(struct cs_etm_auxtrace *etm) | ||
760 | { | ||
761 | struct perf_evsel *evsel; | ||
762 | struct perf_evlist *evlist = etm->session->evlist; | ||
763 | bool timeless_decoding = true; | ||
764 | |||
765 | /* | ||
766 | * Circle through the list of event and complain if we find one | ||
767 | * with the time bit set. | ||
768 | */ | ||
769 | evlist__for_each_entry(evlist, evsel) { | ||
770 | if ((evsel->attr.sample_type & PERF_SAMPLE_TIME)) | ||
771 | timeless_decoding = false; | ||
772 | } | ||
773 | |||
774 | return timeless_decoding; | ||
775 | } | ||
776 | |||
777 | static const char * const cs_etm_global_header_fmts[] = { | ||
778 | [CS_HEADER_VERSION_0] = " Header version %llx\n", | ||
779 | [CS_PMU_TYPE_CPUS] = " PMU type/num cpus %llx\n", | ||
780 | [CS_ETM_SNAPSHOT] = " Snapshot %llx\n", | ||
781 | }; | ||
782 | |||
783 | static const char * const cs_etm_priv_fmts[] = { | ||
784 | [CS_ETM_MAGIC] = " Magic number %llx\n", | ||
785 | [CS_ETM_CPU] = " CPU %lld\n", | ||
786 | [CS_ETM_ETMCR] = " ETMCR %llx\n", | ||
787 | [CS_ETM_ETMTRACEIDR] = " ETMTRACEIDR %llx\n", | ||
788 | [CS_ETM_ETMCCER] = " ETMCCER %llx\n", | ||
789 | [CS_ETM_ETMIDR] = " ETMIDR %llx\n", | ||
790 | }; | ||
791 | |||
792 | static const char * const cs_etmv4_priv_fmts[] = { | ||
793 | [CS_ETM_MAGIC] = " Magic number %llx\n", | ||
794 | [CS_ETM_CPU] = " CPU %lld\n", | ||
795 | [CS_ETMV4_TRCCONFIGR] = " TRCCONFIGR %llx\n", | ||
796 | [CS_ETMV4_TRCTRACEIDR] = " TRCTRACEIDR %llx\n", | ||
797 | [CS_ETMV4_TRCIDR0] = " TRCIDR0 %llx\n", | ||
798 | [CS_ETMV4_TRCIDR1] = " TRCIDR1 %llx\n", | ||
799 | [CS_ETMV4_TRCIDR2] = " TRCIDR2 %llx\n", | ||
800 | [CS_ETMV4_TRCIDR8] = " TRCIDR8 %llx\n", | ||
801 | [CS_ETMV4_TRCAUTHSTATUS] = " TRCAUTHSTATUS %llx\n", | ||
802 | }; | ||
803 | |||
804 | static void cs_etm__print_auxtrace_info(u64 *val, int num) | ||
805 | { | ||
806 | int i, j, cpu = 0; | ||
807 | |||
808 | for (i = 0; i < CS_HEADER_VERSION_0_MAX; i++) | ||
809 | fprintf(stdout, cs_etm_global_header_fmts[i], val[i]); | ||
810 | |||
811 | for (i = CS_HEADER_VERSION_0_MAX; cpu < num; cpu++) { | ||
812 | if (val[i] == __perf_cs_etmv3_magic) | ||
813 | for (j = 0; j < CS_ETM_PRIV_MAX; j++, i++) | ||
814 | fprintf(stdout, cs_etm_priv_fmts[j], val[i]); | ||
815 | else if (val[i] == __perf_cs_etmv4_magic) | ||
816 | for (j = 0; j < CS_ETMV4_PRIV_MAX; j++, i++) | ||
817 | fprintf(stdout, cs_etmv4_priv_fmts[j], val[i]); | ||
818 | else | ||
819 | /* failure.. return */ | ||
820 | return; | ||
821 | } | ||
822 | } | ||
823 | |||
824 | int cs_etm__process_auxtrace_info(union perf_event *event, | ||
825 | struct perf_session *session) | ||
826 | { | ||
827 | struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info; | ||
828 | struct cs_etm_auxtrace *etm = NULL; | ||
829 | struct int_node *inode; | ||
830 | unsigned int pmu_type; | ||
831 | int event_header_size = sizeof(struct perf_event_header); | ||
832 | int info_header_size; | ||
833 | int total_size = auxtrace_info->header.size; | ||
834 | int priv_size = 0; | ||
835 | int num_cpu; | ||
836 | int err = 0, idx = -1; | ||
837 | int i, j, k; | ||
838 | u64 *ptr, *hdr = NULL; | ||
839 | u64 **metadata = NULL; | ||
840 | |||
841 | /* | ||
842 | * sizeof(auxtrace_info_event::type) + | ||
843 | * sizeof(auxtrace_info_event::reserved) == 8 | ||
844 | */ | ||
845 | info_header_size = 8; | ||
846 | |||
847 | if (total_size < (event_header_size + info_header_size)) | ||
848 | return -EINVAL; | ||
849 | |||
850 | priv_size = total_size - event_header_size - info_header_size; | ||
851 | |||
852 | /* First the global part */ | ||
853 | ptr = (u64 *) auxtrace_info->priv; | ||
854 | |||
855 | /* Look for version '0' of the header */ | ||
856 | if (ptr[0] != 0) | ||
857 | return -EINVAL; | ||
858 | |||
859 | hdr = zalloc(sizeof(*hdr) * CS_HEADER_VERSION_0_MAX); | ||
860 | if (!hdr) | ||
861 | return -ENOMEM; | ||
862 | |||
863 | /* Extract header information - see cs-etm.h for format */ | ||
864 | for (i = 0; i < CS_HEADER_VERSION_0_MAX; i++) | ||
865 | hdr[i] = ptr[i]; | ||
866 | num_cpu = hdr[CS_PMU_TYPE_CPUS] & 0xffffffff; | ||
867 | pmu_type = (unsigned int) ((hdr[CS_PMU_TYPE_CPUS] >> 32) & | ||
868 | 0xffffffff); | ||
869 | |||
870 | /* | ||
871 | * Create an RB tree for traceID-CPU# tuple. Since the conversion has | ||
872 | * to be made for each packet that gets decoded, optimizing access in | ||
873 | * anything other than a sequential array is worth doing. | ||
874 | */ | ||
875 | traceid_list = intlist__new(NULL); | ||
876 | if (!traceid_list) { | ||
877 | err = -ENOMEM; | ||
878 | goto err_free_hdr; | ||
879 | } | ||
880 | |||
881 | metadata = zalloc(sizeof(*metadata) * num_cpu); | ||
882 | if (!metadata) { | ||
883 | err = -ENOMEM; | ||
884 | goto err_free_traceid_list; | ||
885 | } | ||
886 | |||
887 | /* | ||
888 | * The metadata is stored in the auxtrace_info section and encodes | ||
889 | * the configuration of the ARM embedded trace macrocell which is | ||
890 | * required by the trace decoder to properly decode the trace due | ||
891 | * to its highly compressed nature. | ||
892 | */ | ||
893 | for (j = 0; j < num_cpu; j++) { | ||
894 | if (ptr[i] == __perf_cs_etmv3_magic) { | ||
895 | metadata[j] = zalloc(sizeof(*metadata[j]) * | ||
896 | CS_ETM_PRIV_MAX); | ||
897 | if (!metadata[j]) { | ||
898 | err = -ENOMEM; | ||
899 | goto err_free_metadata; | ||
900 | } | ||
901 | for (k = 0; k < CS_ETM_PRIV_MAX; k++) | ||
902 | metadata[j][k] = ptr[i + k]; | ||
903 | |||
904 | /* The traceID is our handle */ | ||
905 | idx = metadata[j][CS_ETM_ETMTRACEIDR]; | ||
906 | i += CS_ETM_PRIV_MAX; | ||
907 | } else if (ptr[i] == __perf_cs_etmv4_magic) { | ||
908 | metadata[j] = zalloc(sizeof(*metadata[j]) * | ||
909 | CS_ETMV4_PRIV_MAX); | ||
910 | if (!metadata[j]) { | ||
911 | err = -ENOMEM; | ||
912 | goto err_free_metadata; | ||
913 | } | ||
914 | for (k = 0; k < CS_ETMV4_PRIV_MAX; k++) | ||
915 | metadata[j][k] = ptr[i + k]; | ||
916 | |||
917 | /* The traceID is our handle */ | ||
918 | idx = metadata[j][CS_ETMV4_TRCTRACEIDR]; | ||
919 | i += CS_ETMV4_PRIV_MAX; | ||
920 | } | ||
921 | |||
922 | /* Get an RB node for this CPU */ | ||
923 | inode = intlist__findnew(traceid_list, idx); | ||
924 | |||
925 | /* Something went wrong, no need to continue */ | ||
926 | if (!inode) { | ||
927 | err = PTR_ERR(inode); | ||
928 | goto err_free_metadata; | ||
929 | } | ||
930 | |||
931 | /* | ||
932 | * The node for that CPU should not be taken. | ||
933 | * Back out if that's the case. | ||
934 | */ | ||
935 | if (inode->priv) { | ||
936 | err = -EINVAL; | ||
937 | goto err_free_metadata; | ||
938 | } | ||
939 | /* All good, associate the traceID with the CPU# */ | ||
940 | inode->priv = &metadata[j][CS_ETM_CPU]; | ||
941 | } | ||
942 | |||
943 | /* | ||
944 | * Each of CS_HEADER_VERSION_0_MAX, CS_ETM_PRIV_MAX and | ||
945 | * CS_ETMV4_PRIV_MAX mark how many double words are in the | ||
946 | * global metadata, and each cpu's metadata respectively. | ||
947 | * The following tests if the correct number of double words was | ||
948 | * present in the auxtrace info section. | ||
949 | */ | ||
950 | if (i * 8 != priv_size) { | ||
951 | err = -EINVAL; | ||
952 | goto err_free_metadata; | ||
953 | } | ||
954 | |||
955 | etm = zalloc(sizeof(*etm)); | ||
956 | |||
957 | if (!etm) { | ||
958 | err = -ENOMEM; | ||
959 | goto err_free_metadata; | ||
960 | } | ||
961 | |||
962 | err = auxtrace_queues__init(&etm->queues); | ||
963 | if (err) | ||
964 | goto err_free_etm; | ||
965 | |||
966 | etm->session = session; | ||
967 | etm->machine = &session->machines.host; | ||
968 | |||
969 | etm->num_cpu = num_cpu; | ||
970 | etm->pmu_type = pmu_type; | ||
971 | etm->snapshot_mode = (hdr[CS_ETM_SNAPSHOT] != 0); | ||
972 | etm->metadata = metadata; | ||
973 | etm->auxtrace_type = auxtrace_info->type; | ||
974 | etm->timeless_decoding = cs_etm__is_timeless_decoding(etm); | ||
975 | |||
976 | etm->auxtrace.process_event = cs_etm__process_event; | ||
977 | etm->auxtrace.process_auxtrace_event = cs_etm__process_auxtrace_event; | ||
978 | etm->auxtrace.flush_events = cs_etm__flush_events; | ||
979 | etm->auxtrace.free_events = cs_etm__free_events; | ||
980 | etm->auxtrace.free = cs_etm__free; | ||
981 | session->auxtrace = &etm->auxtrace; | ||
982 | |||
983 | if (dump_trace) { | ||
984 | cs_etm__print_auxtrace_info(auxtrace_info->priv, num_cpu); | ||
985 | return 0; | ||
986 | } | ||
987 | |||
988 | if (session->itrace_synth_opts && session->itrace_synth_opts->set) { | ||
989 | etm->synth_opts = *session->itrace_synth_opts; | ||
990 | } else { | ||
991 | itrace_synth_opts__set_default(&etm->synth_opts); | ||
992 | etm->synth_opts.callchain = false; | ||
993 | } | ||
994 | |||
995 | err = cs_etm__synth_events(etm, session); | ||
996 | if (err) | ||
997 | goto err_free_queues; | ||
998 | |||
999 | err = auxtrace_queues__process_index(&etm->queues, session); | ||
1000 | if (err) | ||
1001 | goto err_free_queues; | ||
1002 | |||
1003 | etm->data_queued = etm->queues.populated; | ||
1004 | |||
1005 | return 0; | ||
1006 | |||
1007 | err_free_queues: | ||
1008 | auxtrace_queues__free(&etm->queues); | ||
1009 | session->auxtrace = NULL; | ||
1010 | err_free_etm: | ||
1011 | zfree(&etm); | ||
1012 | err_free_metadata: | ||
1013 | /* No need to check @metadata[j], free(NULL) is supported */ | ||
1014 | for (j = 0; j < num_cpu; j++) | ||
1015 | free(metadata[j]); | ||
1016 | zfree(&metadata); | ||
1017 | err_free_traceid_list: | ||
1018 | intlist__delete(traceid_list); | ||
1019 | err_free_hdr: | ||
1020 | zfree(&hdr); | ||
1021 | |||
1022 | return -EINVAL; | ||
1023 | } | ||