blob: ff4826c1745fa0b00fd617f5362c032b64d396ac [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) {
150 ret = value_set_u64_hex(cw, event, "ip", sample->ip);
151 if (ret)
152 return -1;
153 }
154
155 if (type & PERF_SAMPLE_TID) {
156 ret = value_set_s32(cw, event, "tid", sample->tid);
157 if (ret)
158 return -1;
159
160 ret = value_set_s32(cw, event, "pid", sample->pid);
161 if (ret)
162 return -1;
163 }
164
165 if ((type & PERF_SAMPLE_ID) ||
166 (type & PERF_SAMPLE_IDENTIFIER)) {
167 ret = value_set_u64(cw, event, "id", sample->id);
168 if (ret)
169 return -1;
170 }
171
172 if (type & PERF_SAMPLE_STREAM_ID) {
173 ret = value_set_u64(cw, event, "stream_id", sample->stream_id);
174 if (ret)
175 return -1;
176 }
177
178 if (type & PERF_SAMPLE_CPU) {
179 ret = value_set_u32(cw, event, "cpu", sample->cpu);
180 if (ret)
181 return -1;
182 }
183
184 if (type & PERF_SAMPLE_PERIOD) {
185 ret = value_set_u64(cw, event, "period", sample->period);
186 if (ret)
187 return -1;
188 }
189
190 if (type & PERF_SAMPLE_WEIGHT) {
191 ret = value_set_u64(cw, event, "weight", sample->weight);
192 if (ret)
193 return -1;
194 }
195
196 if (type & PERF_SAMPLE_DATA_SRC) {
197 ret = value_set_u64(cw, event, "data_src", sample->data_src);
198 if (ret)
199 return -1;
200 }
201
202 if (type & PERF_SAMPLE_TRANSACTION) {
203 ret = value_set_u64(cw, event, "transaction", sample->transaction);
204 if (ret)
205 return -1;
206 }
207
208 return 0;
209}
210
211static int process_sample_event(struct perf_tool *tool,
212 union perf_event *_event __maybe_unused,
213 struct perf_sample *sample,
214 struct perf_evsel *evsel,
215 struct machine *machine __maybe_unused)
216{
217 struct convert *c = container_of(tool, struct convert, tool);
218 struct evsel_priv *priv = evsel->priv;
219 struct ctf_writer *cw = &c->writer;
220 struct bt_ctf_event_class *event_class;
221 struct bt_ctf_event *event;
222 int ret;
223
224 if (WARN_ONCE(!priv, "Failed to setup all events.\n"))
225 return 0;
226
227 event_class = priv->event_class;
228
229 /* update stats */
230 c->events_count++;
231 c->events_size += _event->header.size;
232
233 pr_time2(sample->time, "sample %" PRIu64 "\n", c->events_count);
234
235 event = bt_ctf_event_create(event_class);
236 if (!event) {
237 pr_err("Failed to create an CTF event\n");
238 return -1;
239 }
240
241 bt_ctf_clock_set_time(cw->clock, sample->time);
242
243 ret = add_generic_values(cw, event, evsel, sample);
244 if (ret)
245 return -1;
246
247 bt_ctf_stream_append_event(cw->stream, event);
248 bt_ctf_event_put(event);
249 return 0;
250}
251
252static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
253 struct bt_ctf_event_class *event_class)
254{
255 u64 type = evsel->attr.sample_type;
256
257 /*
258 * missing:
259 * PERF_SAMPLE_TIME - not needed as we have it in
260 * ctf event header
261 * PERF_SAMPLE_READ - TODO
262 * PERF_SAMPLE_CALLCHAIN - TODO
263 * PERF_SAMPLE_RAW - tracepoint fields are handled separately
264 * PERF_SAMPLE_BRANCH_STACK - TODO
265 * PERF_SAMPLE_REGS_USER - TODO
266 * PERF_SAMPLE_STACK_USER - TODO
267 */
268
269#define ADD_FIELD(cl, t, n) \
270 do { \
271 pr2(" field '%s'\n", n); \
272 if (bt_ctf_event_class_add_field(cl, t, n)) { \
273 pr_err("Failed to add field '%s;\n", n); \
274 return -1; \
275 } \
276 } while (0)
277
278 if (type & PERF_SAMPLE_IP)
279 ADD_FIELD(event_class, cw->data.u64_hex, "ip");
280
281 if (type & PERF_SAMPLE_TID) {
282 ADD_FIELD(event_class, cw->data.s32, "tid");
283 ADD_FIELD(event_class, cw->data.s32, "pid");
284 }
285
286 if ((type & PERF_SAMPLE_ID) ||
287 (type & PERF_SAMPLE_IDENTIFIER))
288 ADD_FIELD(event_class, cw->data.u64, "id");
289
290 if (type & PERF_SAMPLE_STREAM_ID)
291 ADD_FIELD(event_class, cw->data.u64, "stream_id");
292
293 if (type & PERF_SAMPLE_CPU)
294 ADD_FIELD(event_class, cw->data.u32, "cpu");
295
296 if (type & PERF_SAMPLE_PERIOD)
297 ADD_FIELD(event_class, cw->data.u64, "period");
298
299 if (type & PERF_SAMPLE_WEIGHT)
300 ADD_FIELD(event_class, cw->data.u64, "weight");
301
302 if (type & PERF_SAMPLE_DATA_SRC)
303 ADD_FIELD(event_class, cw->data.u64, "data_src");
304
305 if (type & PERF_SAMPLE_TRANSACTION)
306 ADD_FIELD(event_class, cw->data.u64, "transaction");
307
308#undef ADD_FIELD
309 return 0;
310}
311
312static int add_event(struct ctf_writer *cw, struct perf_evsel *evsel)
313{
314 struct bt_ctf_event_class *event_class;
315 struct evsel_priv *priv;
316 const char *name = perf_evsel__name(evsel);
317 int ret;
318
319 pr("Adding event '%s' (type %d)\n", name, evsel->attr.type);
320
321 event_class = bt_ctf_event_class_create(name);
322 if (!event_class)
323 return -1;
324
325 ret = add_generic_types(cw, evsel, event_class);
326 if (ret)
327 goto err;
328
329 ret = bt_ctf_stream_class_add_event_class(cw->stream_class, event_class);
330 if (ret) {
331 pr("Failed to add event class into stream.\n");
332 goto err;
333 }
334
335 priv = malloc(sizeof(*priv));
336 if (!priv)
337 goto err;
338
339 priv->event_class = event_class;
340 evsel->priv = priv;
341 return 0;
342
343err:
344 bt_ctf_event_class_put(event_class);
345 pr_err("Failed to add event '%s'.\n", name);
346 return -1;
347}
348
349static int setup_events(struct ctf_writer *cw, struct perf_session *session)
350{
351 struct perf_evlist *evlist = session->evlist;
352 struct perf_evsel *evsel;
353 int ret;
354
355 evlist__for_each(evlist, evsel) {
356 ret = add_event(cw, evsel);
357 if (ret)
358 return ret;
359 }
360 return 0;
361}
362
363static int ctf_writer__setup_env(struct ctf_writer *cw,
364 struct perf_session *session)
365{
366 struct perf_header *header = &session->header;
367 struct bt_ctf_writer *writer = cw->writer;
368
369#define ADD(__n, __v) \
370do { \
371 if (bt_ctf_writer_add_environment_field(writer, __n, __v)) \
372 return -1; \
373} while (0)
374
375 ADD("host", header->env.hostname);
376 ADD("sysname", "Linux");
377 ADD("release", header->env.os_release);
378 ADD("version", header->env.version);
379 ADD("machine", header->env.arch);
380 ADD("domain", "kernel");
381 ADD("tracer_name", "perf");
382
383#undef ADD
384 return 0;
385}
386
387static int ctf_writer__setup_clock(struct ctf_writer *cw)
388{
389 struct bt_ctf_clock *clock = cw->clock;
390
391 bt_ctf_clock_set_description(clock, "perf clock");
392
393#define SET(__n, __v) \
394do { \
395 if (bt_ctf_clock_set_##__n(clock, __v)) \
396 return -1; \
397} while (0)
398
399 SET(frequency, 1000000000);
400 SET(offset_s, 0);
401 SET(offset, 0);
402 SET(precision, 10);
403 SET(is_absolute, 0);
404
405#undef SET
406 return 0;
407}
408
409static struct bt_ctf_field_type *create_int_type(int size, bool sign, bool hex)
410{
411 struct bt_ctf_field_type *type;
412
413 type = bt_ctf_field_type_integer_create(size);
414 if (!type)
415 return NULL;
416
417 if (sign &&
418 bt_ctf_field_type_integer_set_signed(type, 1))
419 goto err;
420
421 if (hex &&
422 bt_ctf_field_type_integer_set_base(type, BT_CTF_INTEGER_BASE_HEXADECIMAL))
423 goto err;
424
425 pr2("Created type: INTEGER %d-bit %ssigned %s\n",
426 size, sign ? "un" : "", hex ? "hex" : "");
427 return type;
428
429err:
430 bt_ctf_field_type_put(type);
431 return NULL;
432}
433
434static void ctf_writer__cleanup_data(struct ctf_writer *cw)
435{
436 unsigned int i;
437
438 for (i = 0; i < ARRAY_SIZE(cw->data.array); i++)
439 bt_ctf_field_type_put(cw->data.array[i]);
440}
441
442static int ctf_writer__init_data(struct ctf_writer *cw)
443{
444#define CREATE_INT_TYPE(type, size, sign, hex) \
445do { \
446 (type) = create_int_type(size, sign, hex); \
447 if (!(type)) \
448 goto err; \
449} while (0)
450
451 CREATE_INT_TYPE(cw->data.s64, 64, true, false);
452 CREATE_INT_TYPE(cw->data.u64, 64, false, false);
453 CREATE_INT_TYPE(cw->data.s32, 32, true, false);
454 CREATE_INT_TYPE(cw->data.u32, 32, false, false);
455 CREATE_INT_TYPE(cw->data.u64_hex, 64, false, true);
456
457 cw->data.string = bt_ctf_field_type_string_create();
458 if (cw->data.string)
459 return 0;
460
461err:
462 ctf_writer__cleanup_data(cw);
463 pr_err("Failed to create data types.\n");
464 return -1;
465}
466
467static void ctf_writer__cleanup(struct ctf_writer *cw)
468{
469 ctf_writer__cleanup_data(cw);
470
471 bt_ctf_clock_put(cw->clock);
472 bt_ctf_stream_put(cw->stream);
473 bt_ctf_stream_class_put(cw->stream_class);
474 bt_ctf_writer_put(cw->writer);
475
476 /* and NULL all the pointers */
477 memset(cw, 0, sizeof(*cw));
478}
479
480static int ctf_writer__init(struct ctf_writer *cw, const char *path)
481{
482 struct bt_ctf_writer *writer;
483 struct bt_ctf_stream_class *stream_class;
484 struct bt_ctf_stream *stream;
485 struct bt_ctf_clock *clock;
486
487 /* CTF writer */
488 writer = bt_ctf_writer_create(path);
489 if (!writer)
490 goto err;
491
492 cw->writer = writer;
493
494 /* CTF clock */
495 clock = bt_ctf_clock_create("perf_clock");
496 if (!clock) {
497 pr("Failed to create CTF clock.\n");
498 goto err_cleanup;
499 }
500
501 cw->clock = clock;
502
503 if (ctf_writer__setup_clock(cw)) {
504 pr("Failed to setup CTF clock.\n");
505 goto err_cleanup;
506 }
507
508 /* CTF stream class */
509 stream_class = bt_ctf_stream_class_create("perf_stream");
510 if (!stream_class) {
511 pr("Failed to create CTF stream class.\n");
512 goto err_cleanup;
513 }
514
515 cw->stream_class = stream_class;
516
517 /* CTF clock stream setup */
518 if (bt_ctf_stream_class_set_clock(stream_class, clock)) {
519 pr("Failed to assign CTF clock to stream class.\n");
520 goto err_cleanup;
521 }
522
523 if (ctf_writer__init_data(cw))
524 goto err_cleanup;
525
526 /* CTF stream instance */
527 stream = bt_ctf_writer_create_stream(writer, stream_class);
528 if (!stream) {
529 pr("Failed to create CTF stream.\n");
530 goto err_cleanup;
531 }
532
533 cw->stream = stream;
534
535 /* CTF clock writer setup */
536 if (bt_ctf_writer_add_clock(writer, clock)) {
537 pr("Failed to assign CTF clock to writer.\n");
538 goto err_cleanup;
539 }
540
541 return 0;
542
543err_cleanup:
544 ctf_writer__cleanup(cw);
545err:
546 pr_err("Failed to setup CTF writer.\n");
547 return -1;
548}
549
550int bt_convert__perf2ctf(const char *input, const char *path)
551{
552 struct perf_session *session;
553 struct perf_data_file file = {
554 .path = input,
555 .mode = PERF_DATA_MODE_READ,
556 };
557 struct convert c = {
558 .tool = {
559 .sample = process_sample_event,
560 .mmap = perf_event__process_mmap,
561 .mmap2 = perf_event__process_mmap2,
562 .comm = perf_event__process_comm,
563 .exit = perf_event__process_exit,
564 .fork = perf_event__process_fork,
565 .lost = perf_event__process_lost,
566 .tracing_data = perf_event__process_tracing_data,
567 .build_id = perf_event__process_build_id,
568 .ordered_events = true,
569 .ordering_requires_timestamps = true,
570 },
571 };
572 struct ctf_writer *cw = &c.writer;
573 int err = -1;
574
575 /* CTF writer */
576 if (ctf_writer__init(cw, path))
577 return -1;
578
579 /* perf.data session */
580 session = perf_session__new(&file, 0, NULL);
581 if (!session)
582 goto free_writer;
583
584 /* CTF writer env/clock setup */
585 if (ctf_writer__setup_env(cw, session))
586 goto free_session;
587
588 /* CTF events setup */
589 if (setup_events(cw, session))
590 goto free_session;
591
592 err = perf_session__process_events(session, &c.tool);
593 if (!err)
594 err = bt_ctf_stream_flush(cw->stream);
595
596 fprintf(stderr,
597 "[ perf data convert: Converted '%s' into CTF data '%s' ]\n",
598 file.path, path);
599
600 fprintf(stderr,
601 "[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n",
602 (double) c.events_size / 1024.0 / 1024.0,
603 c.events_count);
604
605 /* its all good */
606free_session:
607 perf_session__delete(session);
608
609free_writer:
610 ctf_writer__cleanup(cw);
611 return err;
612}