Merge branch 'akpm' (patches from Andrew)
[linux-2.6-microblaze.git] / tools / perf / util / data-convert-json.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * JSON export.
4  *
5  * Copyright (C) 2021, CodeWeavers Inc. <nfraser@codeweavers.com>
6  */
7
8 #include "data-convert.h"
9
10 #include <fcntl.h>
11 #include <inttypes.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14
15 #include "linux/compiler.h"
16 #include "linux/err.h"
17 #include "util/auxtrace.h"
18 #include "util/debug.h"
19 #include "util/dso.h"
20 #include "util/event.h"
21 #include "util/evsel.h"
22 #include "util/evlist.h"
23 #include "util/header.h"
24 #include "util/map.h"
25 #include "util/session.h"
26 #include "util/symbol.h"
27 #include "util/thread.h"
28 #include "util/tool.h"
29
30 struct convert_json {
31         struct perf_tool tool;
32         FILE *out;
33         bool first;
34         u64 events_count;
35 };
36
37 // Outputs a JSON-encoded string surrounded by quotes with characters escaped.
38 static void output_json_string(FILE *out, const char *s)
39 {
40         fputc('"', out);
41         while (*s) {
42                 switch (*s) {
43
44                 // required escapes with special forms as per RFC 8259
45                 case '"':  fputs("\\\"", out); break;
46                 case '\\': fputs("\\\\", out); break;
47                 case '\b': fputs("\\b", out);  break;
48                 case '\f': fputs("\\f", out);  break;
49                 case '\n': fputs("\\n", out);  break;
50                 case '\r': fputs("\\r", out);  break;
51                 case '\t': fputs("\\t", out);  break;
52
53                 default:
54                         // all other control characters must be escaped by hex code
55                         if (*s <= 0x1f)
56                                 fprintf(out, "\\u%04x", *s);
57                         else
58                                 fputc(*s, out);
59                         break;
60                 }
61
62                 ++s;
63         }
64         fputc('"', out);
65 }
66
67 // Outputs an optional comma, newline and indentation to delimit a new value
68 // from the previous one in a JSON object or array.
69 static void output_json_delimiters(FILE *out, bool comma, int depth)
70 {
71         int i;
72
73         if (comma)
74                 fputc(',', out);
75         fputc('\n', out);
76         for (i = 0; i < depth; ++i)
77                 fputc('\t', out);
78 }
79
80 // Outputs a printf format string (with delimiter) as a JSON value.
81 __printf(4, 5)
82 static void output_json_format(FILE *out, bool comma, int depth, const char *format, ...)
83 {
84         va_list args;
85
86         output_json_delimiters(out, comma, depth);
87         va_start(args, format);
88         vfprintf(out,  format, args);
89         va_end(args);
90 }
91
92 // Outputs a JSON key-value pair where the value is a string.
93 static void output_json_key_string(FILE *out, bool comma, int depth,
94                 const char *key, const char *value)
95 {
96         output_json_delimiters(out, comma, depth);
97         output_json_string(out, key);
98         fputs(": ", out);
99         output_json_string(out, value);
100 }
101
102 // Outputs a JSON key-value pair where the value is a printf format string.
103 __printf(5, 6)
104 static void output_json_key_format(FILE *out, bool comma, int depth,
105                 const char *key, const char *format, ...)
106 {
107         va_list args;
108
109         output_json_delimiters(out, comma, depth);
110         output_json_string(out, key);
111         fputs(": ", out);
112         va_start(args, format);
113         vfprintf(out,  format, args);
114         va_end(args);
115 }
116
117 static void output_sample_callchain_entry(struct perf_tool *tool,
118                 u64 ip, struct addr_location *al)
119 {
120         struct convert_json *c = container_of(tool, struct convert_json, tool);
121         FILE *out = c->out;
122
123         output_json_format(out, false, 4, "{");
124         output_json_key_format(out, false, 5, "ip", "\"0x%" PRIx64 "\"", ip);
125
126         if (al && al->sym && al->sym->namelen) {
127                 fputc(',', out);
128                 output_json_key_string(out, false, 5, "symbol", al->sym->name);
129
130                 if (al->map && al->map->dso) {
131                         const char *dso = al->map->dso->short_name;
132
133                         if (dso && strlen(dso) > 0) {
134                                 fputc(',', out);
135                                 output_json_key_string(out, false, 5, "dso", dso);
136                         }
137                 }
138         }
139
140         output_json_format(out, false, 4, "}");
141 }
142
143 static int process_sample_event(struct perf_tool *tool,
144                                 union perf_event *event __maybe_unused,
145                                 struct perf_sample *sample,
146                                 struct evsel *evsel __maybe_unused,
147                                 struct machine *machine)
148 {
149         struct convert_json *c = container_of(tool, struct convert_json, tool);
150         FILE *out = c->out;
151         struct addr_location al, tal;
152         u8 cpumode = PERF_RECORD_MISC_USER;
153
154         if (machine__resolve(machine, &al, sample) < 0) {
155                 pr_err("Sample resolution failed!\n");
156                 return -1;
157         }
158
159         ++c->events_count;
160
161         if (c->first)
162                 c->first = false;
163         else
164                 fputc(',', out);
165         output_json_format(out, false, 2, "{");
166
167         output_json_key_format(out, false, 3, "timestamp", "%" PRIi64, sample->time);
168         output_json_key_format(out, true, 3, "pid", "%i", al.thread->pid_);
169         output_json_key_format(out, true, 3, "tid", "%i", al.thread->tid);
170
171         if (al.thread->cpu >= 0)
172                 output_json_key_format(out, true, 3, "cpu", "%i", al.thread->cpu);
173
174         output_json_key_string(out, true, 3, "comm", thread__comm_str(al.thread));
175
176         output_json_key_format(out, true, 3, "callchain", "[");
177         if (sample->callchain) {
178                 unsigned int i;
179                 bool ok;
180                 bool first_callchain = true;
181
182                 for (i = 0; i < sample->callchain->nr; ++i) {
183                         u64 ip = sample->callchain->ips[i];
184
185                         if (ip >= PERF_CONTEXT_MAX) {
186                                 switch (ip) {
187                                 case PERF_CONTEXT_HV:
188                                         cpumode = PERF_RECORD_MISC_HYPERVISOR;
189                                         break;
190                                 case PERF_CONTEXT_KERNEL:
191                                         cpumode = PERF_RECORD_MISC_KERNEL;
192                                         break;
193                                 case PERF_CONTEXT_USER:
194                                         cpumode = PERF_RECORD_MISC_USER;
195                                         break;
196                                 default:
197                                         pr_debug("invalid callchain context: %"
198                                                         PRId64 "\n", (s64) ip);
199                                         break;
200                                 }
201                                 continue;
202                         }
203
204                         if (first_callchain)
205                                 first_callchain = false;
206                         else
207                                 fputc(',', out);
208
209                         ok = thread__find_symbol(al.thread, cpumode, ip, &tal);
210                         output_sample_callchain_entry(tool, ip, ok ? &tal : NULL);
211                 }
212         } else {
213                 output_sample_callchain_entry(tool, sample->ip, &al);
214         }
215         output_json_format(out, false, 3, "]");
216
217         output_json_format(out, false, 2, "}");
218         return 0;
219 }
220
221 static void output_headers(struct perf_session *session, struct convert_json *c)
222 {
223         struct stat st;
224         struct perf_header *header = &session->header;
225         int ret;
226         int fd = perf_data__fd(session->data);
227         int i;
228         FILE *out = c->out;
229
230         output_json_key_format(out, false, 2, "header-version", "%u", header->version);
231
232         ret = fstat(fd, &st);
233         if (ret >= 0) {
234                 time_t stctime = st.st_mtime;
235                 char buf[256];
236
237                 strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&stctime));
238                 output_json_key_string(out, true, 2, "captured-on", buf);
239         } else {
240                 pr_debug("Failed to get mtime of source file, not writing captured-on");
241         }
242
243         output_json_key_format(out, true, 2, "data-offset", "%" PRIu64, header->data_offset);
244         output_json_key_format(out, true, 2, "data-size", "%" PRIu64, header->data_size);
245         output_json_key_format(out, true, 2, "feat-offset", "%" PRIu64, header->feat_offset);
246
247         output_json_key_string(out, true, 2, "hostname", header->env.hostname);
248         output_json_key_string(out, true, 2, "os-release", header->env.os_release);
249         output_json_key_string(out, true, 2, "arch", header->env.arch);
250
251         output_json_key_string(out, true, 2, "cpu-desc", header->env.cpu_desc);
252         output_json_key_string(out, true, 2, "cpuid", header->env.cpuid);
253         output_json_key_format(out, true, 2, "nrcpus-online", "%u", header->env.nr_cpus_online);
254         output_json_key_format(out, true, 2, "nrcpus-avail", "%u", header->env.nr_cpus_avail);
255
256         if (header->env.clock.enabled) {
257                 output_json_key_format(out, true, 2, "clockid",
258                                 "%u", header->env.clock.clockid);
259                 output_json_key_format(out, true, 2, "clock-time",
260                                 "%" PRIu64, header->env.clock.clockid_ns);
261                 output_json_key_format(out, true, 2, "real-time",
262                                 "%" PRIu64, header->env.clock.tod_ns);
263         }
264
265         output_json_key_string(out, true, 2, "perf-version", header->env.version);
266
267         output_json_key_format(out, true, 2, "cmdline", "[");
268         for (i = 0; i < header->env.nr_cmdline; i++) {
269                 output_json_delimiters(out, i != 0, 3);
270                 output_json_string(c->out, header->env.cmdline_argv[i]);
271         }
272         output_json_format(out, false, 2, "]");
273 }
274
275 int bt_convert__perf2json(const char *input_name, const char *output_name,
276                 struct perf_data_convert_opts *opts __maybe_unused)
277 {
278         struct perf_session *session;
279         int fd;
280         int ret = -1;
281
282         struct convert_json c = {
283                 .tool = {
284                         .sample         = process_sample_event,
285                         .mmap           = perf_event__process_mmap,
286                         .mmap2          = perf_event__process_mmap2,
287                         .comm           = perf_event__process_comm,
288                         .namespaces     = perf_event__process_namespaces,
289                         .cgroup         = perf_event__process_cgroup,
290                         .exit           = perf_event__process_exit,
291                         .fork           = perf_event__process_fork,
292                         .lost           = perf_event__process_lost,
293                         .tracing_data   = perf_event__process_tracing_data,
294                         .build_id       = perf_event__process_build_id,
295                         .id_index       = perf_event__process_id_index,
296                         .auxtrace_info  = perf_event__process_auxtrace_info,
297                         .auxtrace       = perf_event__process_auxtrace,
298                         .event_update   = perf_event__process_event_update,
299                         .ordered_events = true,
300                         .ordering_requires_timestamps = true,
301                 },
302                 .first = true,
303                 .events_count = 0,
304         };
305
306         struct perf_data data = {
307                 .mode = PERF_DATA_MODE_READ,
308                 .path = input_name,
309                 .force = opts->force,
310         };
311
312         if (opts->all) {
313                 pr_err("--all is currently unsupported for JSON output.\n");
314                 goto err;
315         }
316         if (opts->tod) {
317                 pr_err("--tod is currently unsupported for JSON output.\n");
318                 goto err;
319         }
320
321         fd = open(output_name, O_CREAT | O_WRONLY | (opts->force ? O_TRUNC : O_EXCL), 0666);
322         if (fd == -1) {
323                 if (errno == EEXIST)
324                         pr_err("Output file exists. Use --force to overwrite it.\n");
325                 else
326                         pr_err("Error opening output file!\n");
327                 goto err;
328         }
329
330         c.out = fdopen(fd, "w");
331         if (!c.out) {
332                 fprintf(stderr, "Error opening output file!\n");
333                 close(fd);
334                 goto err;
335         }
336
337         session = perf_session__new(&data, &c.tool);
338         if (IS_ERR(session)) {
339                 fprintf(stderr, "Error creating perf session!\n");
340                 goto err_fclose;
341         }
342
343         if (symbol__init(&session->header.env) < 0) {
344                 fprintf(stderr, "Symbol init error!\n");
345                 goto err_session_delete;
346         }
347
348         // The opening brace is printed manually because it isn't delimited from a
349         // previous value (i.e. we don't want a leading newline)
350         fputc('{', c.out);
351
352         // Version number for future-proofing. Most additions should be able to be
353         // done in a backwards-compatible way so this should only need to be bumped
354         // if some major breaking change must be made.
355         output_json_format(c.out, false, 1, "\"linux-perf-json-version\": 1");
356
357         // Output headers
358         output_json_format(c.out, true, 1, "\"headers\": {");
359         output_headers(session, &c);
360         output_json_format(c.out, false, 1, "}");
361
362         // Output samples
363         output_json_format(c.out, true, 1, "\"samples\": [");
364         perf_session__process_events(session);
365         output_json_format(c.out, false, 1, "]");
366         output_json_format(c.out, false, 0, "}");
367         fputc('\n', c.out);
368
369         fprintf(stderr,
370                         "[ perf data convert: Converted '%s' into JSON data '%s' ]\n",
371                         data.path, output_name);
372
373         fprintf(stderr,
374                         "[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n",
375                         (ftell(c.out)) / 1024.0 / 1024.0, c.events_count);
376
377         ret = 0;
378 err_session_delete:
379         perf_session__delete(session);
380 err_fclose:
381         fclose(c.out);
382 err:
383         return ret;
384 }