Commit | Line | Data |
---|---|---|
edbe9817 JO |
1 | /* |
2 | * CTF writing support via babeltrace. | |
3 | * | |
4 | * Copyright (C) 2014, Jiri Olsa <jolsa@redhat.com> | |
5 | * Copyright (C) 2014, Sebastian Andrzej Siewior <bigeasy@linutronix.de> | |
6 | * | |
7 | * Released under the GPL v2. (and only v2, not any later version) | |
8 | */ | |
9 | ||
10 | #include <linux/compiler.h> | |
11 | #include <babeltrace/ctf-writer/writer.h> | |
12 | #include <babeltrace/ctf-writer/clock.h> | |
13 | #include <babeltrace/ctf-writer/stream.h> | |
14 | #include <babeltrace/ctf-writer/event.h> | |
15 | #include <babeltrace/ctf-writer/event-types.h> | |
16 | #include <babeltrace/ctf-writer/event-fields.h> | |
17 | #include <babeltrace/ctf/events.h> | |
18 | #include <traceevent/event-parse.h> | |
19 | #include "asm/bug.h" | |
20 | #include "data-convert-bt.h" | |
21 | #include "session.h" | |
22 | #include "util.h" | |
23 | #include "debug.h" | |
24 | #include "tool.h" | |
25 | #include "evlist.h" | |
26 | #include "evsel.h" | |
27 | #include "machine.h" | |
28 | ||
29 | #define pr_N(n, fmt, ...) \ | |
30 | eprintf(n, debug_data_convert, fmt, ##__VA_ARGS__) | |
31 | ||
32 | #define pr(fmt, ...) pr_N(1, pr_fmt(fmt), ##__VA_ARGS__) | |
33 | #define pr2(fmt, ...) pr_N(2, pr_fmt(fmt), ##__VA_ARGS__) | |
34 | ||
35 | #define pr_time2(t, fmt, ...) pr_time_N(2, debug_data_convert, t, pr_fmt(fmt), ##__VA_ARGS__) | |
36 | ||
37 | struct evsel_priv { | |
38 | struct bt_ctf_event_class *event_class; | |
39 | }; | |
40 | ||
90e129ff SAS |
41 | #define MAX_CPUS 4096 |
42 | ||
43 | struct ctf_stream { | |
44 | struct bt_ctf_stream *stream; | |
45 | int cpu; | |
89e5fa88 | 46 | u32 count; |
90e129ff SAS |
47 | }; |
48 | ||
edbe9817 JO |
49 | struct ctf_writer { |
50 | /* writer primitives */ | |
90e129ff SAS |
51 | struct bt_ctf_writer *writer; |
52 | struct ctf_stream **stream; | |
53 | int stream_cnt; | |
54 | struct bt_ctf_stream_class *stream_class; | |
55 | struct bt_ctf_clock *clock; | |
edbe9817 JO |
56 | |
57 | /* data types */ | |
58 | union { | |
59 | struct { | |
60 | struct bt_ctf_field_type *s64; | |
61 | struct bt_ctf_field_type *u64; | |
62 | struct bt_ctf_field_type *s32; | |
63 | struct bt_ctf_field_type *u32; | |
64 | struct bt_ctf_field_type *string; | |
65 | struct bt_ctf_field_type *u64_hex; | |
66 | }; | |
67 | struct bt_ctf_field_type *array[6]; | |
68 | } data; | |
69 | }; | |
70 | ||
71 | struct convert { | |
72 | struct perf_tool tool; | |
73 | struct ctf_writer writer; | |
74 | ||
75 | u64 events_size; | |
76 | u64 events_count; | |
77 | }; | |
78 | ||
79 | static int value_set(struct bt_ctf_field_type *type, | |
80 | struct bt_ctf_event *event, | |
81 | const char *name, u64 val) | |
82 | { | |
83 | struct bt_ctf_field *field; | |
84 | bool sign = bt_ctf_field_type_integer_get_signed(type); | |
85 | int ret; | |
86 | ||
87 | field = bt_ctf_field_create(type); | |
88 | if (!field) { | |
89 | pr_err("failed to create a field %s\n", name); | |
90 | return -1; | |
91 | } | |
92 | ||
93 | if (sign) { | |
94 | ret = bt_ctf_field_signed_integer_set_value(field, val); | |
95 | if (ret) { | |
96 | pr_err("failed to set field value %s\n", name); | |
97 | goto err; | |
98 | } | |
99 | } else { | |
100 | ret = bt_ctf_field_unsigned_integer_set_value(field, val); | |
101 | if (ret) { | |
102 | pr_err("failed to set field value %s\n", name); | |
103 | goto err; | |
104 | } | |
105 | } | |
106 | ||
107 | ret = bt_ctf_event_set_payload(event, name, field); | |
108 | if (ret) { | |
109 | pr_err("failed to set payload %s\n", name); | |
110 | goto err; | |
111 | } | |
112 | ||
113 | pr2(" SET [%s = %" PRIu64 "]\n", name, val); | |
114 | ||
115 | err: | |
116 | bt_ctf_field_put(field); | |
117 | return ret; | |
118 | } | |
119 | ||
120 | #define __FUNC_VALUE_SET(_name, _val_type) \ | |
121 | static __maybe_unused int value_set_##_name(struct ctf_writer *cw, \ | |
122 | struct bt_ctf_event *event, \ | |
123 | const char *name, \ | |
124 | _val_type val) \ | |
125 | { \ | |
126 | struct bt_ctf_field_type *type = cw->data._name; \ | |
127 | return value_set(type, event, name, (u64) val); \ | |
128 | } | |
129 | ||
130 | #define FUNC_VALUE_SET(_name) __FUNC_VALUE_SET(_name, _name) | |
131 | ||
132 | FUNC_VALUE_SET(s32) | |
133 | FUNC_VALUE_SET(u32) | |
134 | FUNC_VALUE_SET(s64) | |
135 | FUNC_VALUE_SET(u64) | |
136 | __FUNC_VALUE_SET(u64_hex, u64) | |
137 | ||
69364727 SAS |
138 | static struct bt_ctf_field_type* |
139 | get_tracepoint_field_type(struct ctf_writer *cw, struct format_field *field) | |
140 | { | |
141 | unsigned long flags = field->flags; | |
142 | ||
143 | if (flags & FIELD_IS_STRING) | |
144 | return cw->data.string; | |
145 | ||
146 | if (!(flags & FIELD_IS_SIGNED)) { | |
147 | /* unsigned long are mostly pointers */ | |
148 | if (flags & FIELD_IS_LONG || flags & FIELD_IS_POINTER) | |
149 | return cw->data.u64_hex; | |
150 | } | |
151 | ||
152 | if (flags & FIELD_IS_SIGNED) { | |
153 | if (field->size == 8) | |
154 | return cw->data.s64; | |
155 | else | |
156 | return cw->data.s32; | |
157 | } | |
158 | ||
159 | if (field->size == 8) | |
160 | return cw->data.u64; | |
161 | else | |
162 | return cw->data.u32; | |
163 | } | |
164 | ||
165 | static int add_tracepoint_field_value(struct ctf_writer *cw, | |
166 | struct bt_ctf_event_class *event_class, | |
167 | struct bt_ctf_event *event, | |
168 | struct perf_sample *sample, | |
169 | struct format_field *fmtf) | |
170 | { | |
171 | struct bt_ctf_field_type *type; | |
172 | struct bt_ctf_field *array_field; | |
173 | struct bt_ctf_field *field; | |
174 | const char *name = fmtf->name; | |
175 | void *data = sample->raw_data; | |
176 | unsigned long long value_int; | |
177 | unsigned long flags = fmtf->flags; | |
178 | unsigned int n_items; | |
179 | unsigned int i; | |
180 | unsigned int offset; | |
181 | unsigned int len; | |
182 | int ret; | |
183 | ||
184 | offset = fmtf->offset; | |
185 | len = fmtf->size; | |
186 | if (flags & FIELD_IS_STRING) | |
187 | flags &= ~FIELD_IS_ARRAY; | |
188 | ||
189 | if (flags & FIELD_IS_DYNAMIC) { | |
190 | unsigned long long tmp_val; | |
191 | ||
192 | tmp_val = pevent_read_number(fmtf->event->pevent, | |
193 | data + offset, len); | |
194 | offset = tmp_val; | |
195 | len = offset >> 16; | |
196 | offset &= 0xffff; | |
197 | } | |
198 | ||
199 | if (flags & FIELD_IS_ARRAY) { | |
200 | ||
201 | type = bt_ctf_event_class_get_field_by_name( | |
202 | event_class, name); | |
203 | array_field = bt_ctf_field_create(type); | |
204 | bt_ctf_field_type_put(type); | |
205 | if (!array_field) { | |
206 | pr_err("Failed to create array type %s\n", name); | |
207 | return -1; | |
208 | } | |
209 | ||
210 | len = fmtf->size / fmtf->arraylen; | |
211 | n_items = fmtf->arraylen; | |
212 | } else { | |
213 | n_items = 1; | |
214 | array_field = NULL; | |
215 | } | |
216 | ||
217 | type = get_tracepoint_field_type(cw, fmtf); | |
218 | ||
219 | for (i = 0; i < n_items; i++) { | |
220 | if (!(flags & FIELD_IS_STRING)) | |
221 | value_int = pevent_read_number( | |
222 | fmtf->event->pevent, | |
223 | data + offset + i * len, len); | |
224 | ||
225 | if (flags & FIELD_IS_ARRAY) | |
226 | field = bt_ctf_field_array_get_field(array_field, i); | |
227 | else | |
228 | field = bt_ctf_field_create(type); | |
229 | ||
230 | if (!field) { | |
231 | pr_err("failed to create a field %s\n", name); | |
232 | return -1; | |
233 | } | |
234 | ||
235 | if (flags & FIELD_IS_STRING) | |
236 | ret = bt_ctf_field_string_set_value(field, | |
237 | data + offset + i * len); | |
238 | else if (!(flags & FIELD_IS_SIGNED)) | |
239 | ret = bt_ctf_field_unsigned_integer_set_value( | |
240 | field, value_int); | |
241 | else | |
242 | ret = bt_ctf_field_signed_integer_set_value( | |
243 | field, value_int); | |
244 | if (ret) { | |
245 | pr_err("failed to set file value %s\n", name); | |
246 | goto err_put_field; | |
247 | } | |
248 | if (!(flags & FIELD_IS_ARRAY)) { | |
249 | ret = bt_ctf_event_set_payload(event, name, field); | |
250 | if (ret) { | |
251 | pr_err("failed to set payload %s\n", name); | |
252 | goto err_put_field; | |
253 | } | |
254 | } | |
255 | bt_ctf_field_put(field); | |
256 | } | |
257 | if (flags & FIELD_IS_ARRAY) { | |
258 | ret = bt_ctf_event_set_payload(event, name, array_field); | |
259 | if (ret) { | |
260 | pr_err("Failed add payload array %s\n", name); | |
261 | return -1; | |
262 | } | |
263 | bt_ctf_field_put(array_field); | |
264 | } | |
265 | return 0; | |
266 | ||
267 | err_put_field: | |
268 | bt_ctf_field_put(field); | |
269 | return -1; | |
270 | } | |
271 | ||
272 | static int add_tracepoint_fields_values(struct ctf_writer *cw, | |
273 | struct bt_ctf_event_class *event_class, | |
274 | struct bt_ctf_event *event, | |
275 | struct format_field *fields, | |
276 | struct perf_sample *sample) | |
277 | { | |
278 | struct format_field *field; | |
279 | int ret; | |
280 | ||
281 | for (field = fields; field; field = field->next) { | |
282 | ret = add_tracepoint_field_value(cw, event_class, event, sample, | |
283 | field); | |
284 | if (ret) | |
285 | return -1; | |
286 | } | |
287 | return 0; | |
288 | } | |
289 | ||
290 | static int add_tracepoint_values(struct ctf_writer *cw, | |
291 | struct bt_ctf_event_class *event_class, | |
292 | struct bt_ctf_event *event, | |
293 | struct perf_evsel *evsel, | |
294 | struct perf_sample *sample) | |
295 | { | |
296 | struct format_field *common_fields = evsel->tp_format->format.common_fields; | |
297 | struct format_field *fields = evsel->tp_format->format.fields; | |
298 | int ret; | |
299 | ||
300 | ret = add_tracepoint_fields_values(cw, event_class, event, | |
301 | common_fields, sample); | |
302 | if (!ret) | |
303 | ret = add_tracepoint_fields_values(cw, event_class, event, | |
304 | fields, sample); | |
305 | ||
306 | return ret; | |
307 | } | |
308 | ||
edbe9817 JO |
309 | static int add_generic_values(struct ctf_writer *cw, |
310 | struct bt_ctf_event *event, | |
311 | struct perf_evsel *evsel, | |
312 | struct perf_sample *sample) | |
313 | { | |
314 | u64 type = evsel->attr.sample_type; | |
315 | int ret; | |
316 | ||
317 | /* | |
318 | * missing: | |
319 | * PERF_SAMPLE_TIME - not needed as we have it in | |
320 | * ctf event header | |
321 | * PERF_SAMPLE_READ - TODO | |
322 | * PERF_SAMPLE_CALLCHAIN - TODO | |
323 | * PERF_SAMPLE_RAW - tracepoint fields are handled separately | |
324 | * PERF_SAMPLE_BRANCH_STACK - TODO | |
325 | * PERF_SAMPLE_REGS_USER - TODO | |
326 | * PERF_SAMPLE_STACK_USER - TODO | |
327 | */ | |
328 | ||
329 | if (type & PERF_SAMPLE_IP) { | |
54cf776a | 330 | ret = value_set_u64_hex(cw, event, "perf_ip", sample->ip); |
edbe9817 JO |
331 | if (ret) |
332 | return -1; | |
333 | } | |
334 | ||
335 | if (type & PERF_SAMPLE_TID) { | |
54cf776a | 336 | ret = value_set_s32(cw, event, "perf_tid", sample->tid); |
edbe9817 JO |
337 | if (ret) |
338 | return -1; | |
339 | ||
54cf776a | 340 | ret = value_set_s32(cw, event, "perf_pid", sample->pid); |
edbe9817 JO |
341 | if (ret) |
342 | return -1; | |
343 | } | |
344 | ||
345 | if ((type & PERF_SAMPLE_ID) || | |
346 | (type & PERF_SAMPLE_IDENTIFIER)) { | |
54cf776a | 347 | ret = value_set_u64(cw, event, "perf_id", sample->id); |
edbe9817 JO |
348 | if (ret) |
349 | return -1; | |
350 | } | |
351 | ||
352 | if (type & PERF_SAMPLE_STREAM_ID) { | |
54cf776a | 353 | ret = value_set_u64(cw, event, "perf_stream_id", sample->stream_id); |
edbe9817 JO |
354 | if (ret) |
355 | return -1; | |
356 | } | |
357 | ||
edbe9817 | 358 | if (type & PERF_SAMPLE_PERIOD) { |
54cf776a | 359 | ret = value_set_u64(cw, event, "perf_period", sample->period); |
edbe9817 JO |
360 | if (ret) |
361 | return -1; | |
362 | } | |
363 | ||
364 | if (type & PERF_SAMPLE_WEIGHT) { | |
54cf776a | 365 | ret = value_set_u64(cw, event, "perf_weight", sample->weight); |
edbe9817 JO |
366 | if (ret) |
367 | return -1; | |
368 | } | |
369 | ||
370 | if (type & PERF_SAMPLE_DATA_SRC) { | |
54cf776a SAS |
371 | ret = value_set_u64(cw, event, "perf_data_src", |
372 | sample->data_src); | |
edbe9817 JO |
373 | if (ret) |
374 | return -1; | |
375 | } | |
376 | ||
377 | if (type & PERF_SAMPLE_TRANSACTION) { | |
54cf776a SAS |
378 | ret = value_set_u64(cw, event, "perf_transaction", |
379 | sample->transaction); | |
edbe9817 JO |
380 | if (ret) |
381 | return -1; | |
382 | } | |
383 | ||
384 | return 0; | |
385 | } | |
386 | ||
90e129ff SAS |
387 | static int ctf_stream__flush(struct ctf_stream *cs) |
388 | { | |
389 | int err = 0; | |
390 | ||
391 | if (cs) { | |
392 | err = bt_ctf_stream_flush(cs->stream); | |
393 | if (err) | |
394 | pr_err("CTF stream %d flush failed\n", cs->cpu); | |
395 | ||
89e5fa88 JO |
396 | pr("Flush stream for cpu %d (%u samples)\n", |
397 | cs->cpu, cs->count); | |
398 | ||
399 | cs->count = 0; | |
90e129ff SAS |
400 | } |
401 | ||
402 | return err; | |
403 | } | |
404 | ||
405 | static struct ctf_stream *ctf_stream__create(struct ctf_writer *cw, int cpu) | |
406 | { | |
407 | struct ctf_stream *cs; | |
408 | struct bt_ctf_field *pkt_ctx = NULL; | |
409 | struct bt_ctf_field *cpu_field = NULL; | |
410 | struct bt_ctf_stream *stream = NULL; | |
411 | int ret; | |
412 | ||
413 | cs = zalloc(sizeof(*cs)); | |
414 | if (!cs) { | |
415 | pr_err("Failed to allocate ctf stream\n"); | |
416 | return NULL; | |
417 | } | |
418 | ||
419 | stream = bt_ctf_writer_create_stream(cw->writer, cw->stream_class); | |
420 | if (!stream) { | |
421 | pr_err("Failed to create CTF stream\n"); | |
422 | goto out; | |
423 | } | |
424 | ||
425 | pkt_ctx = bt_ctf_stream_get_packet_context(stream); | |
426 | if (!pkt_ctx) { | |
427 | pr_err("Failed to obtain packet context\n"); | |
428 | goto out; | |
429 | } | |
430 | ||
431 | cpu_field = bt_ctf_field_structure_get_field(pkt_ctx, "cpu_id"); | |
432 | bt_ctf_field_put(pkt_ctx); | |
433 | if (!cpu_field) { | |
434 | pr_err("Failed to obtain cpu field\n"); | |
435 | goto out; | |
436 | } | |
437 | ||
438 | ret = bt_ctf_field_unsigned_integer_set_value(cpu_field, (u32) cpu); | |
439 | if (ret) { | |
440 | pr_err("Failed to update CPU number\n"); | |
441 | goto out; | |
442 | } | |
443 | ||
444 | bt_ctf_field_put(cpu_field); | |
445 | ||
446 | cs->cpu = cpu; | |
447 | cs->stream = stream; | |
448 | return cs; | |
449 | ||
450 | out: | |
451 | if (cpu_field) | |
452 | bt_ctf_field_put(cpu_field); | |
453 | if (stream) | |
454 | bt_ctf_stream_put(stream); | |
455 | ||
456 | free(cs); | |
457 | return NULL; | |
458 | } | |
459 | ||
460 | static void ctf_stream__delete(struct ctf_stream *cs) | |
461 | { | |
462 | if (cs) { | |
463 | bt_ctf_stream_put(cs->stream); | |
464 | free(cs); | |
465 | } | |
466 | } | |
467 | ||
468 | static struct ctf_stream *ctf_stream(struct ctf_writer *cw, int cpu) | |
469 | { | |
470 | struct ctf_stream *cs = cw->stream[cpu]; | |
471 | ||
472 | if (!cs) { | |
473 | cs = ctf_stream__create(cw, cpu); | |
474 | cw->stream[cpu] = cs; | |
475 | } | |
476 | ||
477 | return cs; | |
478 | } | |
479 | ||
480 | static int get_sample_cpu(struct ctf_writer *cw, struct perf_sample *sample, | |
481 | struct perf_evsel *evsel) | |
482 | { | |
483 | int cpu = 0; | |
484 | ||
485 | if (evsel->attr.sample_type & PERF_SAMPLE_CPU) | |
486 | cpu = sample->cpu; | |
487 | ||
488 | if (cpu > cw->stream_cnt) { | |
489 | pr_err("Event was recorded for CPU %d, limit is at %d.\n", | |
490 | cpu, cw->stream_cnt); | |
491 | cpu = 0; | |
492 | } | |
493 | ||
494 | return cpu; | |
495 | } | |
496 | ||
89e5fa88 JO |
497 | #define STREAM_FLUSH_COUNT 100000 |
498 | ||
499 | /* | |
500 | * Currently we have no other way to determine the | |
501 | * time for the stream flush other than keep track | |
502 | * of the number of events and check it against | |
503 | * threshold. | |
504 | */ | |
505 | static bool is_flush_needed(struct ctf_stream *cs) | |
506 | { | |
507 | return cs->count >= STREAM_FLUSH_COUNT; | |
508 | } | |
509 | ||
edbe9817 JO |
510 | static int process_sample_event(struct perf_tool *tool, |
511 | union perf_event *_event __maybe_unused, | |
512 | struct perf_sample *sample, | |
513 | struct perf_evsel *evsel, | |
514 | struct machine *machine __maybe_unused) | |
515 | { | |
516 | struct convert *c = container_of(tool, struct convert, tool); | |
517 | struct evsel_priv *priv = evsel->priv; | |
518 | struct ctf_writer *cw = &c->writer; | |
90e129ff | 519 | struct ctf_stream *cs; |
edbe9817 JO |
520 | struct bt_ctf_event_class *event_class; |
521 | struct bt_ctf_event *event; | |
522 | int ret; | |
523 | ||
524 | if (WARN_ONCE(!priv, "Failed to setup all events.\n")) | |
525 | return 0; | |
526 | ||
527 | event_class = priv->event_class; | |
528 | ||
529 | /* update stats */ | |
530 | c->events_count++; | |
531 | c->events_size += _event->header.size; | |
532 | ||
533 | pr_time2(sample->time, "sample %" PRIu64 "\n", c->events_count); | |
534 | ||
535 | event = bt_ctf_event_create(event_class); | |
536 | if (!event) { | |
537 | pr_err("Failed to create an CTF event\n"); | |
538 | return -1; | |
539 | } | |
540 | ||
541 | bt_ctf_clock_set_time(cw->clock, sample->time); | |
542 | ||
543 | ret = add_generic_values(cw, event, evsel, sample); | |
544 | if (ret) | |
545 | return -1; | |
546 | ||
69364727 SAS |
547 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT) { |
548 | ret = add_tracepoint_values(cw, event_class, event, | |
549 | evsel, sample); | |
550 | if (ret) | |
551 | return -1; | |
552 | } | |
553 | ||
90e129ff | 554 | cs = ctf_stream(cw, get_sample_cpu(cw, sample, evsel)); |
89e5fa88 JO |
555 | if (cs) { |
556 | if (is_flush_needed(cs)) | |
557 | ctf_stream__flush(cs); | |
558 | ||
559 | cs->count++; | |
90e129ff | 560 | bt_ctf_stream_append_event(cs->stream, event); |
89e5fa88 | 561 | } |
90e129ff | 562 | |
edbe9817 | 563 | bt_ctf_event_put(event); |
90e129ff | 564 | return cs ? 0 : -1; |
edbe9817 JO |
565 | } |
566 | ||
69364727 SAS |
567 | static int add_tracepoint_fields_types(struct ctf_writer *cw, |
568 | struct format_field *fields, | |
569 | struct bt_ctf_event_class *event_class) | |
570 | { | |
571 | struct format_field *field; | |
572 | int ret; | |
573 | ||
574 | for (field = fields; field; field = field->next) { | |
575 | struct bt_ctf_field_type *type; | |
576 | unsigned long flags = field->flags; | |
577 | ||
578 | pr2(" field '%s'\n", field->name); | |
579 | ||
580 | type = get_tracepoint_field_type(cw, field); | |
581 | if (!type) | |
582 | return -1; | |
583 | ||
584 | /* | |
585 | * A string is an array of chars. For this we use the string | |
586 | * type and don't care that it is an array. What we don't | |
587 | * support is an array of strings. | |
588 | */ | |
589 | if (flags & FIELD_IS_STRING) | |
590 | flags &= ~FIELD_IS_ARRAY; | |
591 | ||
592 | if (flags & FIELD_IS_ARRAY) | |
593 | type = bt_ctf_field_type_array_create(type, field->arraylen); | |
594 | ||
595 | ret = bt_ctf_event_class_add_field(event_class, type, | |
596 | field->name); | |
597 | ||
598 | if (flags & FIELD_IS_ARRAY) | |
599 | bt_ctf_field_type_put(type); | |
600 | ||
601 | if (ret) { | |
602 | pr_err("Failed to add field '%s\n", field->name); | |
603 | return -1; | |
604 | } | |
605 | } | |
606 | ||
607 | return 0; | |
608 | } | |
609 | ||
610 | static int add_tracepoint_types(struct ctf_writer *cw, | |
611 | struct perf_evsel *evsel, | |
612 | struct bt_ctf_event_class *class) | |
613 | { | |
614 | struct format_field *common_fields = evsel->tp_format->format.common_fields; | |
615 | struct format_field *fields = evsel->tp_format->format.fields; | |
616 | int ret; | |
617 | ||
618 | ret = add_tracepoint_fields_types(cw, common_fields, class); | |
619 | if (!ret) | |
620 | ret = add_tracepoint_fields_types(cw, fields, class); | |
621 | ||
622 | return ret; | |
623 | } | |
624 | ||
edbe9817 JO |
625 | static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel, |
626 | struct bt_ctf_event_class *event_class) | |
627 | { | |
628 | u64 type = evsel->attr.sample_type; | |
629 | ||
630 | /* | |
631 | * missing: | |
632 | * PERF_SAMPLE_TIME - not needed as we have it in | |
633 | * ctf event header | |
634 | * PERF_SAMPLE_READ - TODO | |
635 | * PERF_SAMPLE_CALLCHAIN - TODO | |
636 | * PERF_SAMPLE_RAW - tracepoint fields are handled separately | |
637 | * PERF_SAMPLE_BRANCH_STACK - TODO | |
638 | * PERF_SAMPLE_REGS_USER - TODO | |
639 | * PERF_SAMPLE_STACK_USER - TODO | |
640 | */ | |
641 | ||
642 | #define ADD_FIELD(cl, t, n) \ | |
643 | do { \ | |
644 | pr2(" field '%s'\n", n); \ | |
645 | if (bt_ctf_event_class_add_field(cl, t, n)) { \ | |
646 | pr_err("Failed to add field '%s;\n", n); \ | |
647 | return -1; \ | |
648 | } \ | |
649 | } while (0) | |
650 | ||
651 | if (type & PERF_SAMPLE_IP) | |
54cf776a | 652 | ADD_FIELD(event_class, cw->data.u64_hex, "perf_ip"); |
edbe9817 JO |
653 | |
654 | if (type & PERF_SAMPLE_TID) { | |
54cf776a SAS |
655 | ADD_FIELD(event_class, cw->data.s32, "perf_tid"); |
656 | ADD_FIELD(event_class, cw->data.s32, "perf_pid"); | |
edbe9817 JO |
657 | } |
658 | ||
659 | if ((type & PERF_SAMPLE_ID) || | |
660 | (type & PERF_SAMPLE_IDENTIFIER)) | |
54cf776a | 661 | ADD_FIELD(event_class, cw->data.u64, "perf_id"); |
edbe9817 JO |
662 | |
663 | if (type & PERF_SAMPLE_STREAM_ID) | |
54cf776a | 664 | ADD_FIELD(event_class, cw->data.u64, "perf_stream_id"); |
edbe9817 | 665 | |
edbe9817 | 666 | if (type & PERF_SAMPLE_PERIOD) |
54cf776a | 667 | ADD_FIELD(event_class, cw->data.u64, "perf_period"); |
edbe9817 JO |
668 | |
669 | if (type & PERF_SAMPLE_WEIGHT) | |
54cf776a | 670 | ADD_FIELD(event_class, cw->data.u64, "perf_weight"); |
edbe9817 JO |
671 | |
672 | if (type & PERF_SAMPLE_DATA_SRC) | |
54cf776a | 673 | ADD_FIELD(event_class, cw->data.u64, "perf_data_src"); |
edbe9817 JO |
674 | |
675 | if (type & PERF_SAMPLE_TRANSACTION) | |
54cf776a | 676 | ADD_FIELD(event_class, cw->data.u64, "perf_transaction"); |
edbe9817 JO |
677 | |
678 | #undef ADD_FIELD | |
679 | return 0; | |
680 | } | |
681 | ||
682 | static int add_event(struct ctf_writer *cw, struct perf_evsel *evsel) | |
683 | { | |
684 | struct bt_ctf_event_class *event_class; | |
685 | struct evsel_priv *priv; | |
686 | const char *name = perf_evsel__name(evsel); | |
687 | int ret; | |
688 | ||
689 | pr("Adding event '%s' (type %d)\n", name, evsel->attr.type); | |
690 | ||
691 | event_class = bt_ctf_event_class_create(name); | |
692 | if (!event_class) | |
693 | return -1; | |
694 | ||
695 | ret = add_generic_types(cw, evsel, event_class); | |
696 | if (ret) | |
697 | goto err; | |
698 | ||
69364727 SAS |
699 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT) { |
700 | ret = add_tracepoint_types(cw, evsel, event_class); | |
701 | if (ret) | |
702 | goto err; | |
703 | } | |
704 | ||
edbe9817 JO |
705 | ret = bt_ctf_stream_class_add_event_class(cw->stream_class, event_class); |
706 | if (ret) { | |
707 | pr("Failed to add event class into stream.\n"); | |
708 | goto err; | |
709 | } | |
710 | ||
711 | priv = malloc(sizeof(*priv)); | |
712 | if (!priv) | |
713 | goto err; | |
714 | ||
715 | priv->event_class = event_class; | |
716 | evsel->priv = priv; | |
717 | return 0; | |
718 | ||
719 | err: | |
720 | bt_ctf_event_class_put(event_class); | |
721 | pr_err("Failed to add event '%s'.\n", name); | |
722 | return -1; | |
723 | } | |
724 | ||
725 | static int setup_events(struct ctf_writer *cw, struct perf_session *session) | |
726 | { | |
727 | struct perf_evlist *evlist = session->evlist; | |
728 | struct perf_evsel *evsel; | |
729 | int ret; | |
730 | ||
731 | evlist__for_each(evlist, evsel) { | |
732 | ret = add_event(cw, evsel); | |
733 | if (ret) | |
734 | return ret; | |
735 | } | |
736 | return 0; | |
737 | } | |
738 | ||
90e129ff SAS |
739 | static int setup_streams(struct ctf_writer *cw, struct perf_session *session) |
740 | { | |
741 | struct ctf_stream **stream; | |
742 | struct perf_header *ph = &session->header; | |
743 | int ncpus; | |
744 | ||
745 | /* | |
746 | * Try to get the number of cpus used in the data file, | |
747 | * if not present fallback to the MAX_CPUS. | |
748 | */ | |
749 | ncpus = ph->env.nr_cpus_avail ?: MAX_CPUS; | |
750 | ||
751 | stream = zalloc(sizeof(*stream) * ncpus); | |
752 | if (!stream) { | |
753 | pr_err("Failed to allocate streams.\n"); | |
754 | return -ENOMEM; | |
755 | } | |
756 | ||
757 | cw->stream = stream; | |
758 | cw->stream_cnt = ncpus; | |
759 | return 0; | |
760 | } | |
761 | ||
762 | static void free_streams(struct ctf_writer *cw) | |
763 | { | |
764 | int cpu; | |
765 | ||
766 | for (cpu = 0; cpu < cw->stream_cnt; cpu++) | |
767 | ctf_stream__delete(cw->stream[cpu]); | |
768 | ||
769 | free(cw->stream); | |
770 | } | |
771 | ||
edbe9817 JO |
772 | static int ctf_writer__setup_env(struct ctf_writer *cw, |
773 | struct perf_session *session) | |
774 | { | |
775 | struct perf_header *header = &session->header; | |
776 | struct bt_ctf_writer *writer = cw->writer; | |
777 | ||
778 | #define ADD(__n, __v) \ | |
779 | do { \ | |
780 | if (bt_ctf_writer_add_environment_field(writer, __n, __v)) \ | |
781 | return -1; \ | |
782 | } while (0) | |
783 | ||
784 | ADD("host", header->env.hostname); | |
785 | ADD("sysname", "Linux"); | |
786 | ADD("release", header->env.os_release); | |
787 | ADD("version", header->env.version); | |
788 | ADD("machine", header->env.arch); | |
789 | ADD("domain", "kernel"); | |
790 | ADD("tracer_name", "perf"); | |
791 | ||
792 | #undef ADD | |
793 | return 0; | |
794 | } | |
795 | ||
796 | static int ctf_writer__setup_clock(struct ctf_writer *cw) | |
797 | { | |
798 | struct bt_ctf_clock *clock = cw->clock; | |
799 | ||
800 | bt_ctf_clock_set_description(clock, "perf clock"); | |
801 | ||
802 | #define SET(__n, __v) \ | |
803 | do { \ | |
804 | if (bt_ctf_clock_set_##__n(clock, __v)) \ | |
805 | return -1; \ | |
806 | } while (0) | |
807 | ||
808 | SET(frequency, 1000000000); | |
809 | SET(offset_s, 0); | |
810 | SET(offset, 0); | |
811 | SET(precision, 10); | |
812 | SET(is_absolute, 0); | |
813 | ||
814 | #undef SET | |
815 | return 0; | |
816 | } | |
817 | ||
818 | static struct bt_ctf_field_type *create_int_type(int size, bool sign, bool hex) | |
819 | { | |
820 | struct bt_ctf_field_type *type; | |
821 | ||
822 | type = bt_ctf_field_type_integer_create(size); | |
823 | if (!type) | |
824 | return NULL; | |
825 | ||
826 | if (sign && | |
827 | bt_ctf_field_type_integer_set_signed(type, 1)) | |
828 | goto err; | |
829 | ||
830 | if (hex && | |
831 | bt_ctf_field_type_integer_set_base(type, BT_CTF_INTEGER_BASE_HEXADECIMAL)) | |
832 | goto err; | |
833 | ||
834 | pr2("Created type: INTEGER %d-bit %ssigned %s\n", | |
835 | size, sign ? "un" : "", hex ? "hex" : ""); | |
836 | return type; | |
837 | ||
838 | err: | |
839 | bt_ctf_field_type_put(type); | |
840 | return NULL; | |
841 | } | |
842 | ||
843 | static void ctf_writer__cleanup_data(struct ctf_writer *cw) | |
844 | { | |
845 | unsigned int i; | |
846 | ||
847 | for (i = 0; i < ARRAY_SIZE(cw->data.array); i++) | |
848 | bt_ctf_field_type_put(cw->data.array[i]); | |
849 | } | |
850 | ||
851 | static int ctf_writer__init_data(struct ctf_writer *cw) | |
852 | { | |
853 | #define CREATE_INT_TYPE(type, size, sign, hex) \ | |
854 | do { \ | |
855 | (type) = create_int_type(size, sign, hex); \ | |
856 | if (!(type)) \ | |
857 | goto err; \ | |
858 | } while (0) | |
859 | ||
860 | CREATE_INT_TYPE(cw->data.s64, 64, true, false); | |
861 | CREATE_INT_TYPE(cw->data.u64, 64, false, false); | |
862 | CREATE_INT_TYPE(cw->data.s32, 32, true, false); | |
863 | CREATE_INT_TYPE(cw->data.u32, 32, false, false); | |
864 | CREATE_INT_TYPE(cw->data.u64_hex, 64, false, true); | |
865 | ||
866 | cw->data.string = bt_ctf_field_type_string_create(); | |
867 | if (cw->data.string) | |
868 | return 0; | |
869 | ||
870 | err: | |
871 | ctf_writer__cleanup_data(cw); | |
872 | pr_err("Failed to create data types.\n"); | |
873 | return -1; | |
874 | } | |
875 | ||
876 | static void ctf_writer__cleanup(struct ctf_writer *cw) | |
877 | { | |
878 | ctf_writer__cleanup_data(cw); | |
879 | ||
880 | bt_ctf_clock_put(cw->clock); | |
90e129ff | 881 | free_streams(cw); |
edbe9817 JO |
882 | bt_ctf_stream_class_put(cw->stream_class); |
883 | bt_ctf_writer_put(cw->writer); | |
884 | ||
885 | /* and NULL all the pointers */ | |
886 | memset(cw, 0, sizeof(*cw)); | |
887 | } | |
888 | ||
889 | static int ctf_writer__init(struct ctf_writer *cw, const char *path) | |
890 | { | |
891 | struct bt_ctf_writer *writer; | |
892 | struct bt_ctf_stream_class *stream_class; | |
edbe9817 | 893 | struct bt_ctf_clock *clock; |
90e129ff SAS |
894 | struct bt_ctf_field_type *pkt_ctx_type; |
895 | int ret; | |
edbe9817 JO |
896 | |
897 | /* CTF writer */ | |
898 | writer = bt_ctf_writer_create(path); | |
899 | if (!writer) | |
900 | goto err; | |
901 | ||
902 | cw->writer = writer; | |
903 | ||
904 | /* CTF clock */ | |
905 | clock = bt_ctf_clock_create("perf_clock"); | |
906 | if (!clock) { | |
907 | pr("Failed to create CTF clock.\n"); | |
908 | goto err_cleanup; | |
909 | } | |
910 | ||
911 | cw->clock = clock; | |
912 | ||
913 | if (ctf_writer__setup_clock(cw)) { | |
914 | pr("Failed to setup CTF clock.\n"); | |
915 | goto err_cleanup; | |
916 | } | |
917 | ||
918 | /* CTF stream class */ | |
919 | stream_class = bt_ctf_stream_class_create("perf_stream"); | |
920 | if (!stream_class) { | |
921 | pr("Failed to create CTF stream class.\n"); | |
922 | goto err_cleanup; | |
923 | } | |
924 | ||
925 | cw->stream_class = stream_class; | |
926 | ||
927 | /* CTF clock stream setup */ | |
928 | if (bt_ctf_stream_class_set_clock(stream_class, clock)) { | |
929 | pr("Failed to assign CTF clock to stream class.\n"); | |
930 | goto err_cleanup; | |
931 | } | |
932 | ||
933 | if (ctf_writer__init_data(cw)) | |
934 | goto err_cleanup; | |
935 | ||
90e129ff SAS |
936 | /* Add cpu_id for packet context */ |
937 | pkt_ctx_type = bt_ctf_stream_class_get_packet_context_type(stream_class); | |
938 | if (!pkt_ctx_type) | |
edbe9817 | 939 | goto err_cleanup; |
edbe9817 | 940 | |
90e129ff SAS |
941 | ret = bt_ctf_field_type_structure_add_field(pkt_ctx_type, cw->data.u32, "cpu_id"); |
942 | bt_ctf_field_type_put(pkt_ctx_type); | |
943 | if (ret) | |
944 | goto err_cleanup; | |
edbe9817 JO |
945 | |
946 | /* CTF clock writer setup */ | |
947 | if (bt_ctf_writer_add_clock(writer, clock)) { | |
948 | pr("Failed to assign CTF clock to writer.\n"); | |
949 | goto err_cleanup; | |
950 | } | |
951 | ||
952 | return 0; | |
953 | ||
954 | err_cleanup: | |
955 | ctf_writer__cleanup(cw); | |
956 | err: | |
957 | pr_err("Failed to setup CTF writer.\n"); | |
958 | return -1; | |
959 | } | |
960 | ||
90e129ff SAS |
961 | static int ctf_writer__flush_streams(struct ctf_writer *cw) |
962 | { | |
963 | int cpu, ret = 0; | |
964 | ||
965 | for (cpu = 0; cpu < cw->stream_cnt && !ret; cpu++) | |
966 | ret = ctf_stream__flush(cw->stream[cpu]); | |
967 | ||
968 | return ret; | |
969 | } | |
970 | ||
bd05954b | 971 | int bt_convert__perf2ctf(const char *input, const char *path, bool force) |
edbe9817 JO |
972 | { |
973 | struct perf_session *session; | |
974 | struct perf_data_file file = { | |
975 | .path = input, | |
976 | .mode = PERF_DATA_MODE_READ, | |
bd05954b | 977 | .force = force, |
edbe9817 JO |
978 | }; |
979 | struct convert c = { | |
980 | .tool = { | |
981 | .sample = process_sample_event, | |
982 | .mmap = perf_event__process_mmap, | |
983 | .mmap2 = perf_event__process_mmap2, | |
984 | .comm = perf_event__process_comm, | |
985 | .exit = perf_event__process_exit, | |
986 | .fork = perf_event__process_fork, | |
987 | .lost = perf_event__process_lost, | |
988 | .tracing_data = perf_event__process_tracing_data, | |
989 | .build_id = perf_event__process_build_id, | |
990 | .ordered_events = true, | |
991 | .ordering_requires_timestamps = true, | |
992 | }, | |
993 | }; | |
994 | struct ctf_writer *cw = &c.writer; | |
995 | int err = -1; | |
996 | ||
997 | /* CTF writer */ | |
998 | if (ctf_writer__init(cw, path)) | |
999 | return -1; | |
1000 | ||
1001 | /* perf.data session */ | |
b7b61cbe | 1002 | session = perf_session__new(&file, 0, &c.tool); |
edbe9817 JO |
1003 | if (!session) |
1004 | goto free_writer; | |
1005 | ||
1006 | /* CTF writer env/clock setup */ | |
1007 | if (ctf_writer__setup_env(cw, session)) | |
1008 | goto free_session; | |
1009 | ||
1010 | /* CTF events setup */ | |
1011 | if (setup_events(cw, session)) | |
1012 | goto free_session; | |
1013 | ||
90e129ff SAS |
1014 | if (setup_streams(cw, session)) |
1015 | goto free_session; | |
1016 | ||
b7b61cbe | 1017 | err = perf_session__process_events(session); |
edbe9817 | 1018 | if (!err) |
90e129ff | 1019 | err = ctf_writer__flush_streams(cw); |
c2141055 HK |
1020 | else |
1021 | pr_err("Error during conversion.\n"); | |
edbe9817 JO |
1022 | |
1023 | fprintf(stderr, | |
1024 | "[ perf data convert: Converted '%s' into CTF data '%s' ]\n", | |
1025 | file.path, path); | |
1026 | ||
1027 | fprintf(stderr, | |
1028 | "[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n", | |
1029 | (double) c.events_size / 1024.0 / 1024.0, | |
1030 | c.events_count); | |
1031 | ||
edbe9817 | 1032 | perf_session__delete(session); |
c2141055 | 1033 | ctf_writer__cleanup(cw); |
edbe9817 | 1034 | |
c2141055 HK |
1035 | return err; |
1036 | ||
1037 | free_session: | |
1038 | perf_session__delete(session); | |
edbe9817 JO |
1039 | free_writer: |
1040 | ctf_writer__cleanup(cw); | |
c2141055 | 1041 | pr_err("Error during conversion setup.\n"); |
edbe9817 JO |
1042 | return err; |
1043 | } |