1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright (C) 2021, CodeWeavers Inc. <nfraser@codeweavers.com>
8 #include "data-convert.h"
15 #include "linux/compiler.h"
16 #include "linux/err.h"
17 #include "util/auxtrace.h"
18 #include "util/debug.h"
20 #include "util/event.h"
21 #include "util/evsel.h"
22 #include "util/evlist.h"
23 #include "util/header.h"
25 #include "util/session.h"
26 #include "util/symbol.h"
27 #include "util/thread.h"
28 #include "util/tool.h"
30 #ifdef HAVE_LIBTRACEEVENT
31 #include <event-parse.h>
35 struct perf_tool tool
;
41 // Outputs a JSON-encoded string surrounded by quotes with characters escaped.
42 static void output_json_string(FILE *out
, const char *s
)
48 // required escapes with special forms as per RFC 8259
49 case '"': fputs("\\\"", out
); break;
50 case '\\': fputs("\\\\", out
); break;
51 case '\b': fputs("\\b", out
); break;
52 case '\f': fputs("\\f", out
); break;
53 case '\n': fputs("\\n", out
); break;
54 case '\r': fputs("\\r", out
); break;
55 case '\t': fputs("\\t", out
); break;
58 // all other control characters must be escaped by hex code
60 fprintf(out
, "\\u%04x", *s
);
71 // Outputs an optional comma, newline and indentation to delimit a new value
72 // from the previous one in a JSON object or array.
73 static void output_json_delimiters(FILE *out
, bool comma
, int depth
)
80 for (i
= 0; i
< depth
; ++i
)
84 // Outputs a printf format string (with delimiter) as a JSON value.
86 static void output_json_format(FILE *out
, bool comma
, int depth
, const char *format
, ...)
90 output_json_delimiters(out
, comma
, depth
);
91 va_start(args
, format
);
92 vfprintf(out
, format
, args
);
96 // Outputs a JSON key-value pair where the value is a string.
97 static void output_json_key_string(FILE *out
, bool comma
, int depth
,
98 const char *key
, const char *value
)
100 output_json_delimiters(out
, comma
, depth
);
101 output_json_string(out
, key
);
103 output_json_string(out
, value
);
106 // Outputs a JSON key-value pair where the value is a printf format string.
108 static void output_json_key_format(FILE *out
, bool comma
, int depth
,
109 const char *key
, const char *format
, ...)
113 output_json_delimiters(out
, comma
, depth
);
114 output_json_string(out
, key
);
116 va_start(args
, format
);
117 vfprintf(out
, format
, args
);
121 static void output_sample_callchain_entry(const struct perf_tool
*tool
,
122 u64 ip
, struct addr_location
*al
)
124 struct convert_json
*c
= container_of(tool
, struct convert_json
, tool
);
127 output_json_format(out
, false, 4, "{");
128 output_json_key_format(out
, false, 5, "ip", "\"0x%" PRIx64
"\"", ip
);
130 if (al
&& al
->sym
&& al
->sym
->namelen
) {
131 struct dso
*dso
= al
->map
? map__dso(al
->map
) : NULL
;
134 output_json_key_string(out
, false, 5, "symbol", al
->sym
->name
);
137 const char *dso_name
= dso__short_name(dso
);
139 if (dso_name
&& strlen(dso_name
) > 0) {
141 output_json_key_string(out
, false, 5, "dso", dso_name
);
146 output_json_format(out
, false, 4, "}");
149 static int process_sample_event(const struct perf_tool
*tool
,
150 union perf_event
*event __maybe_unused
,
151 struct perf_sample
*sample
,
152 struct evsel
*evsel __maybe_unused
,
153 struct machine
*machine
)
155 struct convert_json
*c
= container_of(tool
, struct convert_json
, tool
);
157 struct addr_location al
;
158 u64 sample_type
= __evlist__combined_sample_type(evsel
->evlist
);
159 u8 cpumode
= PERF_RECORD_MISC_USER
;
161 addr_location__init(&al
);
162 if (machine__resolve(machine
, &al
, sample
) < 0) {
163 pr_err("Sample resolution failed!\n");
164 addr_location__exit(&al
);
174 output_json_format(out
, false, 2, "{");
176 output_json_key_format(out
, false, 3, "timestamp", "%" PRIi64
, sample
->time
);
177 output_json_key_format(out
, true, 3, "pid", "%i", thread__pid(al
.thread
));
178 output_json_key_format(out
, true, 3, "tid", "%i", thread__tid(al
.thread
));
180 if ((sample_type
& PERF_SAMPLE_CPU
))
181 output_json_key_format(out
, true, 3, "cpu", "%i", sample
->cpu
);
182 else if (thread__cpu(al
.thread
) >= 0)
183 output_json_key_format(out
, true, 3, "cpu", "%i", thread__cpu(al
.thread
));
185 output_json_key_string(out
, true, 3, "comm", thread__comm_str(al
.thread
));
187 output_json_key_format(out
, true, 3, "callchain", "[");
188 if (sample
->callchain
) {
191 bool first_callchain
= true;
193 for (i
= 0; i
< sample
->callchain
->nr
; ++i
) {
194 u64 ip
= sample
->callchain
->ips
[i
];
195 struct addr_location tal
;
197 if (ip
>= PERF_CONTEXT_MAX
) {
199 case PERF_CONTEXT_HV
:
200 cpumode
= PERF_RECORD_MISC_HYPERVISOR
;
202 case PERF_CONTEXT_KERNEL
:
203 cpumode
= PERF_RECORD_MISC_KERNEL
;
205 case PERF_CONTEXT_USER
:
206 cpumode
= PERF_RECORD_MISC_USER
;
209 pr_debug("invalid callchain context: %"
210 PRId64
"\n", (s64
) ip
);
217 first_callchain
= false;
221 addr_location__init(&tal
);
222 ok
= thread__find_symbol(al
.thread
, cpumode
, ip
, &tal
);
223 output_sample_callchain_entry(tool
, ip
, ok
? &tal
: NULL
);
224 addr_location__exit(&tal
);
227 output_sample_callchain_entry(tool
, sample
->ip
, &al
);
229 output_json_format(out
, false, 3, "]");
231 #ifdef HAVE_LIBTRACEEVENT
232 if (sample
->raw_data
) {
234 struct tep_format_field
**fields
;
236 fields
= tep_event_fields(evsel
->tp_format
);
243 tep_print_field(&s
, sample
->raw_data
, fields
[i
]);
244 output_json_key_string(out
, true, 3, fields
[i
]->name
, s
.buffer
);
252 output_json_format(out
, false, 2, "}");
253 addr_location__exit(&al
);
257 static void output_headers(struct perf_session
*session
, struct convert_json
*c
)
260 struct perf_header
*header
= &session
->header
;
262 int fd
= perf_data__fd(session
->data
);
266 output_json_key_format(out
, false, 2, "header-version", "%u", header
->version
);
268 ret
= fstat(fd
, &st
);
270 time_t stctime
= st
.st_mtime
;
273 strftime(buf
, sizeof(buf
), "%FT%TZ", gmtime(&stctime
));
274 output_json_key_string(out
, true, 2, "captured-on", buf
);
276 pr_debug("Failed to get mtime of source file, not writing captured-on");
279 output_json_key_format(out
, true, 2, "data-offset", "%" PRIu64
, header
->data_offset
);
280 output_json_key_format(out
, true, 2, "data-size", "%" PRIu64
, header
->data_size
);
281 output_json_key_format(out
, true, 2, "feat-offset", "%" PRIu64
, header
->feat_offset
);
283 output_json_key_string(out
, true, 2, "hostname", header
->env
.hostname
);
284 output_json_key_string(out
, true, 2, "os-release", header
->env
.os_release
);
285 output_json_key_string(out
, true, 2, "arch", header
->env
.arch
);
287 if (header
->env
.cpu_desc
)
288 output_json_key_string(out
, true, 2, "cpu-desc", header
->env
.cpu_desc
);
290 output_json_key_string(out
, true, 2, "cpuid", header
->env
.cpuid
);
291 output_json_key_format(out
, true, 2, "nrcpus-online", "%u", header
->env
.nr_cpus_online
);
292 output_json_key_format(out
, true, 2, "nrcpus-avail", "%u", header
->env
.nr_cpus_avail
);
294 if (header
->env
.clock
.enabled
) {
295 output_json_key_format(out
, true, 2, "clockid",
296 "%u", header
->env
.clock
.clockid
);
297 output_json_key_format(out
, true, 2, "clock-time",
298 "%" PRIu64
, header
->env
.clock
.clockid_ns
);
299 output_json_key_format(out
, true, 2, "real-time",
300 "%" PRIu64
, header
->env
.clock
.tod_ns
);
303 output_json_key_string(out
, true, 2, "perf-version", header
->env
.version
);
305 output_json_key_format(out
, true, 2, "cmdline", "[");
306 for (i
= 0; i
< header
->env
.nr_cmdline
; i
++) {
307 output_json_delimiters(out
, i
!= 0, 3);
308 output_json_string(c
->out
, header
->env
.cmdline_argv
[i
]);
310 output_json_format(out
, false, 2, "]");
313 int bt_convert__perf2json(const char *input_name
, const char *output_name
,
314 struct perf_data_convert_opts
*opts __maybe_unused
)
316 struct perf_session
*session
;
319 struct convert_json c
= {
323 struct perf_data data
= {
324 .mode
= PERF_DATA_MODE_READ
,
326 .force
= opts
->force
,
329 perf_tool__init(&c
.tool
, /*ordered_events=*/true);
330 c
.tool
.sample
= process_sample_event
;
331 c
.tool
.mmap
= perf_event__process_mmap
;
332 c
.tool
.mmap2
= perf_event__process_mmap2
;
333 c
.tool
.comm
= perf_event__process_comm
;
334 c
.tool
.namespaces
= perf_event__process_namespaces
;
335 c
.tool
.cgroup
= perf_event__process_cgroup
;
336 c
.tool
.exit
= perf_event__process_exit
;
337 c
.tool
.fork
= perf_event__process_fork
;
338 c
.tool
.lost
= perf_event__process_lost
;
339 #ifdef HAVE_LIBTRACEEVENT
340 c
.tool
.tracing_data
= perf_event__process_tracing_data
;
342 c
.tool
.build_id
= perf_event__process_build_id
;
343 c
.tool
.id_index
= perf_event__process_id_index
;
344 c
.tool
.auxtrace_info
= perf_event__process_auxtrace_info
;
345 c
.tool
.auxtrace
= perf_event__process_auxtrace
;
346 c
.tool
.event_update
= perf_event__process_event_update
;
347 c
.tool
.ordering_requires_timestamps
= true;
350 pr_err("--all is currently unsupported for JSON output.\n");
354 pr_err("--tod is currently unsupported for JSON output.\n");
358 fd
= open(output_name
, O_CREAT
| O_WRONLY
| (opts
->force
? O_TRUNC
: O_EXCL
), 0666);
361 pr_err("Output file exists. Use --force to overwrite it.\n");
363 pr_err("Error opening output file!\n");
367 c
.out
= fdopen(fd
, "w");
369 fprintf(stderr
, "Error opening output file!\n");
374 session
= perf_session__new(&data
, &c
.tool
);
375 if (IS_ERR(session
)) {
376 fprintf(stderr
, "Error creating perf session!\n");
380 if (symbol__init(&session
->header
.env
) < 0) {
381 fprintf(stderr
, "Symbol init error!\n");
382 goto err_session_delete
;
385 // The opening brace is printed manually because it isn't delimited from a
386 // previous value (i.e. we don't want a leading newline)
389 // Version number for future-proofing. Most additions should be able to be
390 // done in a backwards-compatible way so this should only need to be bumped
391 // if some major breaking change must be made.
392 output_json_format(c
.out
, false, 1, "\"linux-perf-json-version\": 1");
395 output_json_format(c
.out
, true, 1, "\"headers\": {");
396 output_headers(session
, &c
);
397 output_json_format(c
.out
, false, 1, "}");
400 output_json_format(c
.out
, true, 1, "\"samples\": [");
401 perf_session__process_events(session
);
402 output_json_format(c
.out
, false, 1, "]");
403 output_json_format(c
.out
, false, 0, "}");
407 "[ perf data convert: Converted '%s' into JSON data '%s' ]\n",
408 data
.path
, output_name
);
411 "[ perf data convert: Converted and wrote %.3f MB (%" PRIu64
" samples) ]\n",
412 (ftell(c
.out
)) / 1024.0 / 1024.0, c
.events_count
);
416 perf_session__delete(session
);