0001
0002
0003
0004
0005
0006
0007
0008 #include "data-convert.h"
0009
0010 #include <fcntl.h>
0011 #include <inttypes.h>
0012 #include <sys/stat.h>
0013 #include <unistd.h>
0014
0015 #include "linux/compiler.h"
0016 #include "linux/err.h"
0017 #include "util/auxtrace.h"
0018 #include "util/debug.h"
0019 #include "util/dso.h"
0020 #include "util/event.h"
0021 #include "util/evsel.h"
0022 #include "util/evlist.h"
0023 #include "util/header.h"
0024 #include "util/map.h"
0025 #include "util/session.h"
0026 #include "util/symbol.h"
0027 #include "util/thread.h"
0028 #include "util/tool.h"
0029
0030 struct convert_json {
0031 struct perf_tool tool;
0032 FILE *out;
0033 bool first;
0034 u64 events_count;
0035 };
0036
0037
0038 static void output_json_string(FILE *out, const char *s)
0039 {
0040 fputc('"', out);
0041 while (*s) {
0042 switch (*s) {
0043
0044
0045 case '"': fputs("\\\"", out); break;
0046 case '\\': fputs("\\\\", out); break;
0047 case '\b': fputs("\\b", out); break;
0048 case '\f': fputs("\\f", out); break;
0049 case '\n': fputs("\\n", out); break;
0050 case '\r': fputs("\\r", out); break;
0051 case '\t': fputs("\\t", out); break;
0052
0053 default:
0054
0055 if (*s <= 0x1f)
0056 fprintf(out, "\\u%04x", *s);
0057 else
0058 fputc(*s, out);
0059 break;
0060 }
0061
0062 ++s;
0063 }
0064 fputc('"', out);
0065 }
0066
0067
0068
0069 static void output_json_delimiters(FILE *out, bool comma, int depth)
0070 {
0071 int i;
0072
0073 if (comma)
0074 fputc(',', out);
0075 fputc('\n', out);
0076 for (i = 0; i < depth; ++i)
0077 fputc('\t', out);
0078 }
0079
0080
0081 __printf(4, 5)
0082 static void output_json_format(FILE *out, bool comma, int depth, const char *format, ...)
0083 {
0084 va_list args;
0085
0086 output_json_delimiters(out, comma, depth);
0087 va_start(args, format);
0088 vfprintf(out, format, args);
0089 va_end(args);
0090 }
0091
0092
0093 static void output_json_key_string(FILE *out, bool comma, int depth,
0094 const char *key, const char *value)
0095 {
0096 output_json_delimiters(out, comma, depth);
0097 output_json_string(out, key);
0098 fputs(": ", out);
0099 output_json_string(out, value);
0100 }
0101
0102
0103 __printf(5, 6)
0104 static void output_json_key_format(FILE *out, bool comma, int depth,
0105 const char *key, const char *format, ...)
0106 {
0107 va_list args;
0108
0109 output_json_delimiters(out, comma, depth);
0110 output_json_string(out, key);
0111 fputs(": ", out);
0112 va_start(args, format);
0113 vfprintf(out, format, args);
0114 va_end(args);
0115 }
0116
0117 static void output_sample_callchain_entry(struct perf_tool *tool,
0118 u64 ip, struct addr_location *al)
0119 {
0120 struct convert_json *c = container_of(tool, struct convert_json, tool);
0121 FILE *out = c->out;
0122
0123 output_json_format(out, false, 4, "{");
0124 output_json_key_format(out, false, 5, "ip", "\"0x%" PRIx64 "\"", ip);
0125
0126 if (al && al->sym && al->sym->namelen) {
0127 fputc(',', out);
0128 output_json_key_string(out, false, 5, "symbol", al->sym->name);
0129
0130 if (al->map && al->map->dso) {
0131 const char *dso = al->map->dso->short_name;
0132
0133 if (dso && strlen(dso) > 0) {
0134 fputc(',', out);
0135 output_json_key_string(out, false, 5, "dso", dso);
0136 }
0137 }
0138 }
0139
0140 output_json_format(out, false, 4, "}");
0141 }
0142
0143 static int process_sample_event(struct perf_tool *tool,
0144 union perf_event *event __maybe_unused,
0145 struct perf_sample *sample,
0146 struct evsel *evsel __maybe_unused,
0147 struct machine *machine)
0148 {
0149 struct convert_json *c = container_of(tool, struct convert_json, tool);
0150 FILE *out = c->out;
0151 struct addr_location al, tal;
0152 u64 sample_type = __evlist__combined_sample_type(evsel->evlist);
0153 u8 cpumode = PERF_RECORD_MISC_USER;
0154
0155 if (machine__resolve(machine, &al, sample) < 0) {
0156 pr_err("Sample resolution failed!\n");
0157 return -1;
0158 }
0159
0160 ++c->events_count;
0161
0162 if (c->first)
0163 c->first = false;
0164 else
0165 fputc(',', out);
0166 output_json_format(out, false, 2, "{");
0167
0168 output_json_key_format(out, false, 3, "timestamp", "%" PRIi64, sample->time);
0169 output_json_key_format(out, true, 3, "pid", "%i", al.thread->pid_);
0170 output_json_key_format(out, true, 3, "tid", "%i", al.thread->tid);
0171
0172 if ((sample_type & PERF_SAMPLE_CPU))
0173 output_json_key_format(out, true, 3, "cpu", "%i", sample->cpu);
0174 else if (al.thread->cpu >= 0)
0175 output_json_key_format(out, true, 3, "cpu", "%i", al.thread->cpu);
0176
0177 output_json_key_string(out, true, 3, "comm", thread__comm_str(al.thread));
0178
0179 output_json_key_format(out, true, 3, "callchain", "[");
0180 if (sample->callchain) {
0181 unsigned int i;
0182 bool ok;
0183 bool first_callchain = true;
0184
0185 for (i = 0; i < sample->callchain->nr; ++i) {
0186 u64 ip = sample->callchain->ips[i];
0187
0188 if (ip >= PERF_CONTEXT_MAX) {
0189 switch (ip) {
0190 case PERF_CONTEXT_HV:
0191 cpumode = PERF_RECORD_MISC_HYPERVISOR;
0192 break;
0193 case PERF_CONTEXT_KERNEL:
0194 cpumode = PERF_RECORD_MISC_KERNEL;
0195 break;
0196 case PERF_CONTEXT_USER:
0197 cpumode = PERF_RECORD_MISC_USER;
0198 break;
0199 default:
0200 pr_debug("invalid callchain context: %"
0201 PRId64 "\n", (s64) ip);
0202 break;
0203 }
0204 continue;
0205 }
0206
0207 if (first_callchain)
0208 first_callchain = false;
0209 else
0210 fputc(',', out);
0211
0212 ok = thread__find_symbol(al.thread, cpumode, ip, &tal);
0213 output_sample_callchain_entry(tool, ip, ok ? &tal : NULL);
0214 }
0215 } else {
0216 output_sample_callchain_entry(tool, sample->ip, &al);
0217 }
0218 output_json_format(out, false, 3, "]");
0219
0220 output_json_format(out, false, 2, "}");
0221 return 0;
0222 }
0223
0224 static void output_headers(struct perf_session *session, struct convert_json *c)
0225 {
0226 struct stat st;
0227 struct perf_header *header = &session->header;
0228 int ret;
0229 int fd = perf_data__fd(session->data);
0230 int i;
0231 FILE *out = c->out;
0232
0233 output_json_key_format(out, false, 2, "header-version", "%u", header->version);
0234
0235 ret = fstat(fd, &st);
0236 if (ret >= 0) {
0237 time_t stctime = st.st_mtime;
0238 char buf[256];
0239
0240 strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&stctime));
0241 output_json_key_string(out, true, 2, "captured-on", buf);
0242 } else {
0243 pr_debug("Failed to get mtime of source file, not writing captured-on");
0244 }
0245
0246 output_json_key_format(out, true, 2, "data-offset", "%" PRIu64, header->data_offset);
0247 output_json_key_format(out, true, 2, "data-size", "%" PRIu64, header->data_size);
0248 output_json_key_format(out, true, 2, "feat-offset", "%" PRIu64, header->feat_offset);
0249
0250 output_json_key_string(out, true, 2, "hostname", header->env.hostname);
0251 output_json_key_string(out, true, 2, "os-release", header->env.os_release);
0252 output_json_key_string(out, true, 2, "arch", header->env.arch);
0253
0254 output_json_key_string(out, true, 2, "cpu-desc", header->env.cpu_desc);
0255 output_json_key_string(out, true, 2, "cpuid", header->env.cpuid);
0256 output_json_key_format(out, true, 2, "nrcpus-online", "%u", header->env.nr_cpus_online);
0257 output_json_key_format(out, true, 2, "nrcpus-avail", "%u", header->env.nr_cpus_avail);
0258
0259 if (header->env.clock.enabled) {
0260 output_json_key_format(out, true, 2, "clockid",
0261 "%u", header->env.clock.clockid);
0262 output_json_key_format(out, true, 2, "clock-time",
0263 "%" PRIu64, header->env.clock.clockid_ns);
0264 output_json_key_format(out, true, 2, "real-time",
0265 "%" PRIu64, header->env.clock.tod_ns);
0266 }
0267
0268 output_json_key_string(out, true, 2, "perf-version", header->env.version);
0269
0270 output_json_key_format(out, true, 2, "cmdline", "[");
0271 for (i = 0; i < header->env.nr_cmdline; i++) {
0272 output_json_delimiters(out, i != 0, 3);
0273 output_json_string(c->out, header->env.cmdline_argv[i]);
0274 }
0275 output_json_format(out, false, 2, "]");
0276 }
0277
0278 int bt_convert__perf2json(const char *input_name, const char *output_name,
0279 struct perf_data_convert_opts *opts __maybe_unused)
0280 {
0281 struct perf_session *session;
0282 int fd;
0283 int ret = -1;
0284
0285 struct convert_json c = {
0286 .tool = {
0287 .sample = process_sample_event,
0288 .mmap = perf_event__process_mmap,
0289 .mmap2 = perf_event__process_mmap2,
0290 .comm = perf_event__process_comm,
0291 .namespaces = perf_event__process_namespaces,
0292 .cgroup = perf_event__process_cgroup,
0293 .exit = perf_event__process_exit,
0294 .fork = perf_event__process_fork,
0295 .lost = perf_event__process_lost,
0296 .tracing_data = perf_event__process_tracing_data,
0297 .build_id = perf_event__process_build_id,
0298 .id_index = perf_event__process_id_index,
0299 .auxtrace_info = perf_event__process_auxtrace_info,
0300 .auxtrace = perf_event__process_auxtrace,
0301 .event_update = perf_event__process_event_update,
0302 .ordered_events = true,
0303 .ordering_requires_timestamps = true,
0304 },
0305 .first = true,
0306 .events_count = 0,
0307 };
0308
0309 struct perf_data data = {
0310 .mode = PERF_DATA_MODE_READ,
0311 .path = input_name,
0312 .force = opts->force,
0313 };
0314
0315 if (opts->all) {
0316 pr_err("--all is currently unsupported for JSON output.\n");
0317 goto err;
0318 }
0319 if (opts->tod) {
0320 pr_err("--tod is currently unsupported for JSON output.\n");
0321 goto err;
0322 }
0323
0324 fd = open(output_name, O_CREAT | O_WRONLY | (opts->force ? O_TRUNC : O_EXCL), 0666);
0325 if (fd == -1) {
0326 if (errno == EEXIST)
0327 pr_err("Output file exists. Use --force to overwrite it.\n");
0328 else
0329 pr_err("Error opening output file!\n");
0330 goto err;
0331 }
0332
0333 c.out = fdopen(fd, "w");
0334 if (!c.out) {
0335 fprintf(stderr, "Error opening output file!\n");
0336 close(fd);
0337 goto err;
0338 }
0339
0340 session = perf_session__new(&data, &c.tool);
0341 if (IS_ERR(session)) {
0342 fprintf(stderr, "Error creating perf session!\n");
0343 goto err_fclose;
0344 }
0345
0346 if (symbol__init(&session->header.env) < 0) {
0347 fprintf(stderr, "Symbol init error!\n");
0348 goto err_session_delete;
0349 }
0350
0351
0352
0353 fputc('{', c.out);
0354
0355
0356
0357
0358 output_json_format(c.out, false, 1, "\"linux-perf-json-version\": 1");
0359
0360
0361 output_json_format(c.out, true, 1, "\"headers\": {");
0362 output_headers(session, &c);
0363 output_json_format(c.out, false, 1, "}");
0364
0365
0366 output_json_format(c.out, true, 1, "\"samples\": [");
0367 perf_session__process_events(session);
0368 output_json_format(c.out, false, 1, "]");
0369 output_json_format(c.out, false, 0, "}");
0370 fputc('\n', c.out);
0371
0372 fprintf(stderr,
0373 "[ perf data convert: Converted '%s' into JSON data '%s' ]\n",
0374 data.path, output_name);
0375
0376 fprintf(stderr,
0377 "[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n",
0378 (ftell(c.out)) / 1024.0 / 1024.0, c.events_count);
0379
0380 ret = 0;
0381 err_session_delete:
0382 perf_session__delete(session);
0383 err_fclose:
0384 fclose(c.out);
0385 err:
0386 return ret;
0387 }