blob: e372e03ff480245b3ea5a88ee0f06b5b98dce9f3 [file] [log] [blame]
Jiri Olsaedbe9812015-02-20 23:17:00 +01001/*
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
37struct evsel_priv {
38 struct bt_ctf_event_class *event_class;
39};
40
41struct ctf_writer {
42 /* writer primitives */
43 struct bt_ctf_writer *writer;
44 struct bt_ctf_stream *stream;
45 struct bt_ctf_stream_class *stream_class;
46 struct bt_ctf_clock *clock;
47
48 /* data types */
49 union {
50 struct {
51 struct bt_ctf_field_type *s64;
52 struct bt_ctf_field_type *u64;
53 struct bt_ctf_field_type *s32;
54 struct bt_ctf_field_type *u32;
55 struct bt_ctf_field_type *string;
56 struct bt_ctf_field_type *u64_hex;
57 };
58 struct bt_ctf_field_type *array[6];
59 } data;
60};
61
62struct convert {
63 struct perf_tool tool;
64 struct ctf_writer writer;
65
66 u64 events_size;
67 u64 events_count;
68};
69
70static int value_set(struct bt_ctf_field_type *type,
71 struct bt_ctf_event *event,
72 const char *name, u64 val)
73{
74 struct bt_ctf_field *field;
75 bool sign = bt_ctf_field_type_integer_get_signed(type);
76 int ret;
77
78 field = bt_ctf_field_create(type);
79 if (!field) {
80 pr_err("failed to create a field %s\n", name);
81 return -1;
82 }
83
84 if (sign) {
85 ret = bt_ctf_field_signed_integer_set_value(field, val);
86 if (ret) {
87 pr_err("failed to set field value %s\n", name);
88 goto err;
89 }
90 } else {
91 ret = bt_ctf_field_unsigned_integer_set_value(field, val);
92 if (ret) {
93 pr_err("failed to set field value %s\n", name);
94 goto err;
95 }
96 }
97
98 ret = bt_ctf_event_set_payload(event, name, field);
99 if (ret) {
100 pr_err("failed to set payload %s\n", name);
101 goto err;
102 }
103
104 pr2(" SET [%s = %" PRIu64 "]\n", name, val);
105
106err:
107 bt_ctf_field_put(field);
108 return ret;
109}
110
111#define __FUNC_VALUE_SET(_name, _val_type) \
112static __maybe_unused int value_set_##_name(struct ctf_writer *cw, \
113 struct bt_ctf_event *event, \
114 const char *name, \
115 _val_type val) \
116{ \
117 struct bt_ctf_field_type *type = cw->data._name; \
118 return value_set(type, event, name, (u64) val); \
119}
120
121#define FUNC_VALUE_SET(_name) __FUNC_VALUE_SET(_name, _name)
122
123FUNC_VALUE_SET(s32)
124FUNC_VALUE_SET(u32)
125FUNC_VALUE_SET(s64)
126FUNC_VALUE_SET(u64)
127__FUNC_VALUE_SET(u64_hex, u64)
128
129static int add_generic_values(struct ctf_writer *cw,
130 struct bt_ctf_event *event,
131 struct perf_evsel *evsel,
132 struct perf_sample *sample)
133{
134 u64 type = evsel->attr.sample_type;
135 int ret;
136
137 /*
138 * missing:
139 * PERF_SAMPLE_TIME - not needed as we have it in
140 * ctf event header
141 * PERF_SAMPLE_READ - TODO
142 * PERF_SAMPLE_CALLCHAIN - TODO
143 * PERF_SAMPLE_RAW - tracepoint fields are handled separately
144 * PERF_SAMPLE_BRANCH_STACK - TODO
145 * PERF_SAMPLE_REGS_USER - TODO
146 * PERF_SAMPLE_STACK_USER - TODO
147 */
148
149 if (type & PERF_SAMPLE_IP) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100150 ret = value_set_u64_hex(cw, event, "perf_ip", sample->ip);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100151 if (ret)
152 return -1;
153 }
154
155 if (type & PERF_SAMPLE_TID) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100156 ret = value_set_s32(cw, event, "perf_tid", sample->tid);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100157 if (ret)
158 return -1;
159
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100160 ret = value_set_s32(cw, event, "perf_pid", sample->pid);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100161 if (ret)
162 return -1;
163 }
164
165 if ((type & PERF_SAMPLE_ID) ||
166 (type & PERF_SAMPLE_IDENTIFIER)) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100167 ret = value_set_u64(cw, event, "perf_id", sample->id);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100168 if (ret)
169 return -1;
170 }
171
172 if (type & PERF_SAMPLE_STREAM_ID) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100173 ret = value_set_u64(cw, event, "perf_stream_id", sample->stream_id);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100174 if (ret)
175 return -1;
176 }
177
178 if (type & PERF_SAMPLE_CPU) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100179 ret = value_set_u32(cw, event, "perf_cpu", sample->cpu);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100180 if (ret)
181 return -1;
182 }
183
184 if (type & PERF_SAMPLE_PERIOD) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100185 ret = value_set_u64(cw, event, "perf_period", sample->period);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100186 if (ret)
187 return -1;
188 }
189
190 if (type & PERF_SAMPLE_WEIGHT) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100191 ret = value_set_u64(cw, event, "perf_weight", sample->weight);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100192 if (ret)
193 return -1;
194 }
195
196 if (type & PERF_SAMPLE_DATA_SRC) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100197 ret = value_set_u64(cw, event, "perf_data_src",
198 sample->data_src);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100199 if (ret)
200 return -1;
201 }
202
203 if (type & PERF_SAMPLE_TRANSACTION) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100204 ret = value_set_u64(cw, event, "perf_transaction",
205 sample->transaction);
Jiri Olsaedbe9812015-02-20 23:17:00 +0100206 if (ret)
207 return -1;
208 }
209
210 return 0;
211}
212
213static int process_sample_event(struct perf_tool *tool,
214 union perf_event *_event __maybe_unused,
215 struct perf_sample *sample,
216 struct perf_evsel *evsel,
217 struct machine *machine __maybe_unused)
218{
219 struct convert *c = container_of(tool, struct convert, tool);
220 struct evsel_priv *priv = evsel->priv;
221 struct ctf_writer *cw = &c->writer;
222 struct bt_ctf_event_class *event_class;
223 struct bt_ctf_event *event;
224 int ret;
225
226 if (WARN_ONCE(!priv, "Failed to setup all events.\n"))
227 return 0;
228
229 event_class = priv->event_class;
230
231 /* update stats */
232 c->events_count++;
233 c->events_size += _event->header.size;
234
235 pr_time2(sample->time, "sample %" PRIu64 "\n", c->events_count);
236
237 event = bt_ctf_event_create(event_class);
238 if (!event) {
239 pr_err("Failed to create an CTF event\n");
240 return -1;
241 }
242
243 bt_ctf_clock_set_time(cw->clock, sample->time);
244
245 ret = add_generic_values(cw, event, evsel, sample);
246 if (ret)
247 return -1;
248
249 bt_ctf_stream_append_event(cw->stream, event);
250 bt_ctf_event_put(event);
251 return 0;
252}
253
254static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
255 struct bt_ctf_event_class *event_class)
256{
257 u64 type = evsel->attr.sample_type;
258
259 /*
260 * missing:
261 * PERF_SAMPLE_TIME - not needed as we have it in
262 * ctf event header
263 * PERF_SAMPLE_READ - TODO
264 * PERF_SAMPLE_CALLCHAIN - TODO
265 * PERF_SAMPLE_RAW - tracepoint fields are handled separately
266 * PERF_SAMPLE_BRANCH_STACK - TODO
267 * PERF_SAMPLE_REGS_USER - TODO
268 * PERF_SAMPLE_STACK_USER - TODO
269 */
270
271#define ADD_FIELD(cl, t, n) \
272 do { \
273 pr2(" field '%s'\n", n); \
274 if (bt_ctf_event_class_add_field(cl, t, n)) { \
275 pr_err("Failed to add field '%s;\n", n); \
276 return -1; \
277 } \
278 } while (0)
279
280 if (type & PERF_SAMPLE_IP)
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100281 ADD_FIELD(event_class, cw->data.u64_hex, "perf_ip");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100282
283 if (type & PERF_SAMPLE_TID) {
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100284 ADD_FIELD(event_class, cw->data.s32, "perf_tid");
285 ADD_FIELD(event_class, cw->data.s32, "perf_pid");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100286 }
287
288 if ((type & PERF_SAMPLE_ID) ||
289 (type & PERF_SAMPLE_IDENTIFIER))
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100290 ADD_FIELD(event_class, cw->data.u64, "perf_id");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100291
292 if (type & PERF_SAMPLE_STREAM_ID)
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100293 ADD_FIELD(event_class, cw->data.u64, "perf_stream_id");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100294
295 if (type & PERF_SAMPLE_CPU)
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100296 ADD_FIELD(event_class, cw->data.u32, "perf_cpu");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100297
298 if (type & PERF_SAMPLE_PERIOD)
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100299 ADD_FIELD(event_class, cw->data.u64, "perf_period");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100300
301 if (type & PERF_SAMPLE_WEIGHT)
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100302 ADD_FIELD(event_class, cw->data.u64, "perf_weight");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100303
304 if (type & PERF_SAMPLE_DATA_SRC)
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100305 ADD_FIELD(event_class, cw->data.u64, "perf_data_src");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100306
307 if (type & PERF_SAMPLE_TRANSACTION)
Sebastian Andrzej Siewior54cf7762015-02-20 23:17:01 +0100308 ADD_FIELD(event_class, cw->data.u64, "perf_transaction");
Jiri Olsaedbe9812015-02-20 23:17:00 +0100309
310#undef ADD_FIELD
311 return 0;
312}
313
314static int add_event(struct ctf_writer *cw, struct perf_evsel *evsel)
315{
316 struct bt_ctf_event_class *event_class;
317 struct evsel_priv *priv;
318 const char *name = perf_evsel__name(evsel);
319 int ret;
320
321 pr("Adding event '%s' (type %d)\n", name, evsel->attr.type);
322
323 event_class = bt_ctf_event_class_create(name);
324 if (!event_class)
325 return -1;
326
327 ret = add_generic_types(cw, evsel, event_class);
328 if (ret)
329 goto err;
330
331 ret = bt_ctf_stream_class_add_event_class(cw->stream_class, event_class);
332 if (ret) {
333 pr("Failed to add event class into stream.\n");
334 goto err;
335 }
336
337 priv = malloc(sizeof(*priv));
338 if (!priv)
339 goto err;
340
341 priv->event_class = event_class;
342 evsel->priv = priv;
343 return 0;
344
345err:
346 bt_ctf_event_class_put(event_class);
347 pr_err("Failed to add event '%s'.\n", name);
348 return -1;
349}
350
351static int setup_events(struct ctf_writer *cw, struct perf_session *session)
352{
353 struct perf_evlist *evlist = session->evlist;
354 struct perf_evsel *evsel;
355 int ret;
356
357 evlist__for_each(evlist, evsel) {
358 ret = add_event(cw, evsel);
359 if (ret)
360 return ret;
361 }
362 return 0;
363}
364
365static int ctf_writer__setup_env(struct ctf_writer *cw,
366 struct perf_session *session)
367{
368 struct perf_header *header = &session->header;
369 struct bt_ctf_writer *writer = cw->writer;
370
371#define ADD(__n, __v) \
372do { \
373 if (bt_ctf_writer_add_environment_field(writer, __n, __v)) \
374 return -1; \
375} while (0)
376
377 ADD("host", header->env.hostname);
378 ADD("sysname", "Linux");
379 ADD("release", header->env.os_release);
380 ADD("version", header->env.version);
381 ADD("machine", header->env.arch);
382 ADD("domain", "kernel");
383 ADD("tracer_name", "perf");
384
385#undef ADD
386 return 0;
387}
388
389static int ctf_writer__setup_clock(struct ctf_writer *cw)
390{
391 struct bt_ctf_clock *clock = cw->clock;
392
393 bt_ctf_clock_set_description(clock, "perf clock");
394
395#define SET(__n, __v) \
396do { \
397 if (bt_ctf_clock_set_##__n(clock, __v)) \
398 return -1; \
399} while (0)
400
401 SET(frequency, 1000000000);
402 SET(offset_s, 0);
403 SET(offset, 0);
404 SET(precision, 10);
405 SET(is_absolute, 0);
406
407#undef SET
408 return 0;
409}
410
411static struct bt_ctf_field_type *create_int_type(int size, bool sign, bool hex)
412{
413 struct bt_ctf_field_type *type;
414
415 type = bt_ctf_field_type_integer_create(size);
416 if (!type)
417 return NULL;
418
419 if (sign &&
420 bt_ctf_field_type_integer_set_signed(type, 1))
421 goto err;
422
423 if (hex &&
424 bt_ctf_field_type_integer_set_base(type, BT_CTF_INTEGER_BASE_HEXADECIMAL))
425 goto err;
426
427 pr2("Created type: INTEGER %d-bit %ssigned %s\n",
428 size, sign ? "un" : "", hex ? "hex" : "");
429 return type;
430
431err:
432 bt_ctf_field_type_put(type);
433 return NULL;
434}
435
436static void ctf_writer__cleanup_data(struct ctf_writer *cw)
437{
438 unsigned int i;
439
440 for (i = 0; i < ARRAY_SIZE(cw->data.array); i++)
441 bt_ctf_field_type_put(cw->data.array[i]);
442}
443
444static int ctf_writer__init_data(struct ctf_writer *cw)
445{
446#define CREATE_INT_TYPE(type, size, sign, hex) \
447do { \
448 (type) = create_int_type(size, sign, hex); \
449 if (!(type)) \
450 goto err; \
451} while (0)
452
453 CREATE_INT_TYPE(cw->data.s64, 64, true, false);
454 CREATE_INT_TYPE(cw->data.u64, 64, false, false);
455 CREATE_INT_TYPE(cw->data.s32, 32, true, false);
456 CREATE_INT_TYPE(cw->data.u32, 32, false, false);
457 CREATE_INT_TYPE(cw->data.u64_hex, 64, false, true);
458
459 cw->data.string = bt_ctf_field_type_string_create();
460 if (cw->data.string)
461 return 0;
462
463err:
464 ctf_writer__cleanup_data(cw);
465 pr_err("Failed to create data types.\n");
466 return -1;
467}
468
469static void ctf_writer__cleanup(struct ctf_writer *cw)
470{
471 ctf_writer__cleanup_data(cw);
472
473 bt_ctf_clock_put(cw->clock);
474 bt_ctf_stream_put(cw->stream);
475 bt_ctf_stream_class_put(cw->stream_class);
476 bt_ctf_writer_put(cw->writer);
477
478 /* and NULL all the pointers */
479 memset(cw, 0, sizeof(*cw));
480}
481
482static int ctf_writer__init(struct ctf_writer *cw, const char *path)
483{
484 struct bt_ctf_writer *writer;
485 struct bt_ctf_stream_class *stream_class;
486 struct bt_ctf_stream *stream;
487 struct bt_ctf_clock *clock;
488
489 /* CTF writer */
490 writer = bt_ctf_writer_create(path);
491 if (!writer)
492 goto err;
493
494 cw->writer = writer;
495
496 /* CTF clock */
497 clock = bt_ctf_clock_create("perf_clock");
498 if (!clock) {
499 pr("Failed to create CTF clock.\n");
500 goto err_cleanup;
501 }
502
503 cw->clock = clock;
504
505 if (ctf_writer__setup_clock(cw)) {
506 pr("Failed to setup CTF clock.\n");
507 goto err_cleanup;
508 }
509
510 /* CTF stream class */
511 stream_class = bt_ctf_stream_class_create("perf_stream");
512 if (!stream_class) {
513 pr("Failed to create CTF stream class.\n");
514 goto err_cleanup;
515 }
516
517 cw->stream_class = stream_class;
518
519 /* CTF clock stream setup */
520 if (bt_ctf_stream_class_set_clock(stream_class, clock)) {
521 pr("Failed to assign CTF clock to stream class.\n");
522 goto err_cleanup;
523 }
524
525 if (ctf_writer__init_data(cw))
526 goto err_cleanup;
527
528 /* CTF stream instance */
529 stream = bt_ctf_writer_create_stream(writer, stream_class);
530 if (!stream) {
531 pr("Failed to create CTF stream.\n");
532 goto err_cleanup;
533 }
534
535 cw->stream = stream;
536
537 /* CTF clock writer setup */
538 if (bt_ctf_writer_add_clock(writer, clock)) {
539 pr("Failed to assign CTF clock to writer.\n");
540 goto err_cleanup;
541 }
542
543 return 0;
544
545err_cleanup:
546 ctf_writer__cleanup(cw);
547err:
548 pr_err("Failed to setup CTF writer.\n");
549 return -1;
550}
551
552int bt_convert__perf2ctf(const char *input, const char *path)
553{
554 struct perf_session *session;
555 struct perf_data_file file = {
556 .path = input,
557 .mode = PERF_DATA_MODE_READ,
558 };
559 struct convert c = {
560 .tool = {
561 .sample = process_sample_event,
562 .mmap = perf_event__process_mmap,
563 .mmap2 = perf_event__process_mmap2,
564 .comm = perf_event__process_comm,
565 .exit = perf_event__process_exit,
566 .fork = perf_event__process_fork,
567 .lost = perf_event__process_lost,
568 .tracing_data = perf_event__process_tracing_data,
569 .build_id = perf_event__process_build_id,
570 .ordered_events = true,
571 .ordering_requires_timestamps = true,
572 },
573 };
574 struct ctf_writer *cw = &c.writer;
575 int err = -1;
576
577 /* CTF writer */
578 if (ctf_writer__init(cw, path))
579 return -1;
580
581 /* perf.data session */
582 session = perf_session__new(&file, 0, NULL);
583 if (!session)
584 goto free_writer;
585
586 /* CTF writer env/clock setup */
587 if (ctf_writer__setup_env(cw, session))
588 goto free_session;
589
590 /* CTF events setup */
591 if (setup_events(cw, session))
592 goto free_session;
593
594 err = perf_session__process_events(session, &c.tool);
595 if (!err)
596 err = bt_ctf_stream_flush(cw->stream);
597
598 fprintf(stderr,
599 "[ perf data convert: Converted '%s' into CTF data '%s' ]\n",
600 file.path, path);
601
602 fprintf(stderr,
603 "[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n",
604 (double) c.events_size / 1024.0 / 1024.0,
605 c.events_count);
606
607 /* its all good */
608free_session:
609 perf_session__delete(session);
610
611free_writer:
612 ctf_writer__cleanup(cw);
613 return err;
614}