brintos

brintos / linux-shallow public Read only

0
0
Text · 11.5 KiB · 20bfb08 Raw
422 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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#ifdef HAVE_LIBTRACEEVENT31#include <traceevent/event-parse.h>32#endif33 34struct convert_json {35	struct perf_tool tool;36	FILE *out;37	bool first;38	u64 events_count;39};40 41// Outputs a JSON-encoded string surrounded by quotes with characters escaped.42static void output_json_string(FILE *out, const char *s)43{44	fputc('"', out);45	while (*s) {46		switch (*s) {47 48		// required escapes with special forms as per RFC 825949		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;56 57		default:58			// all other control characters must be escaped by hex code59			if (*s <= 0x1f)60				fprintf(out, "\\u%04x", *s);61			else62				fputc(*s, out);63			break;64		}65 66		++s;67	}68	fputc('"', out);69}70 71// Outputs an optional comma, newline and indentation to delimit a new value72// from the previous one in a JSON object or array.73static void output_json_delimiters(FILE *out, bool comma, int depth)74{75	int i;76 77	if (comma)78		fputc(',', out);79	fputc('\n', out);80	for (i = 0; i < depth; ++i)81		fputc('\t', out);82}83 84// Outputs a printf format string (with delimiter) as a JSON value.85__printf(4, 5)86static void output_json_format(FILE *out, bool comma, int depth, const char *format, ...)87{88	va_list args;89 90	output_json_delimiters(out, comma, depth);91	va_start(args, format);92	vfprintf(out,  format, args);93	va_end(args);94}95 96// Outputs a JSON key-value pair where the value is a string.97static void output_json_key_string(FILE *out, bool comma, int depth,98		const char *key, const char *value)99{100	output_json_delimiters(out, comma, depth);101	output_json_string(out, key);102	fputs(": ", out);103	output_json_string(out, value);104}105 106// Outputs a JSON key-value pair where the value is a printf format string.107__printf(5, 6)108static void output_json_key_format(FILE *out, bool comma, int depth,109		const char *key, const char *format, ...)110{111	va_list args;112 113	output_json_delimiters(out, comma, depth);114	output_json_string(out, key);115	fputs(": ", out);116	va_start(args, format);117	vfprintf(out,  format, args);118	va_end(args);119}120 121static void output_sample_callchain_entry(const struct perf_tool *tool,122		u64 ip, struct addr_location *al)123{124	struct convert_json *c = container_of(tool, struct convert_json, tool);125	FILE *out = c->out;126 127	output_json_format(out, false, 4, "{");128	output_json_key_format(out, false, 5, "ip", "\"0x%" PRIx64 "\"", ip);129 130	if (al && al->sym && al->sym->namelen) {131		struct dso *dso = al->map ? map__dso(al->map) : NULL;132 133		fputc(',', out);134		output_json_key_string(out, false, 5, "symbol", al->sym->name);135 136		if (dso) {137			const char *dso_name = dso__short_name(dso);138 139			if (dso_name && strlen(dso_name) > 0) {140				fputc(',', out);141				output_json_key_string(out, false, 5, "dso", dso_name);142			}143		}144	}145 146	output_json_format(out, false, 4, "}");147}148 149static 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)154{155	struct convert_json *c = container_of(tool, struct convert_json, tool);156	FILE *out = c->out;157	struct addr_location al;158	u64 sample_type = __evlist__combined_sample_type(evsel->evlist);159	u8 cpumode = PERF_RECORD_MISC_USER;160 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);165		return -1;166	}167 168	++c->events_count;169 170	if (c->first)171		c->first = false;172	else173		fputc(',', out);174	output_json_format(out, false, 2, "{");175 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));179 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));184 185	output_json_key_string(out, true, 3, "comm", thread__comm_str(al.thread));186 187	output_json_key_format(out, true, 3, "callchain", "[");188	if (sample->callchain) {189		unsigned int i;190		bool ok;191		bool first_callchain = true;192 193		for (i = 0; i < sample->callchain->nr; ++i) {194			u64 ip = sample->callchain->ips[i];195			struct addr_location tal;196 197			if (ip >= PERF_CONTEXT_MAX) {198				switch (ip) {199				case PERF_CONTEXT_HV:200					cpumode = PERF_RECORD_MISC_HYPERVISOR;201					break;202				case PERF_CONTEXT_KERNEL:203					cpumode = PERF_RECORD_MISC_KERNEL;204					break;205				case PERF_CONTEXT_USER:206					cpumode = PERF_RECORD_MISC_USER;207					break;208				default:209					pr_debug("invalid callchain context: %"210							PRId64 "\n", (s64) ip);211					break;212				}213				continue;214			}215 216			if (first_callchain)217				first_callchain = false;218			else219				fputc(',', out);220 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);225		}226	} else {227		output_sample_callchain_entry(tool, sample->ip, &al);228	}229	output_json_format(out, false, 3, "]");230 231#ifdef HAVE_LIBTRACEEVENT232	if (sample->raw_data) {233		int i;234		struct tep_format_field **fields;235 236		fields = tep_event_fields(evsel->tp_format);237		if (fields) {238			i = 0;239			while (fields[i]) {240				struct trace_seq s;241 242				trace_seq_init(&s);243				tep_print_field(&s, sample->raw_data, fields[i]);244				output_json_key_string(out, true, 3, fields[i]->name, s.buffer);245 246				i++;247			}248			free(fields);249		}250	}251#endif252	output_json_format(out, false, 2, "}");253	addr_location__exit(&al);254	return 0;255}256 257static void output_headers(struct perf_session *session, struct convert_json *c)258{259	struct stat st;260	struct perf_header *header = &session->header;261	int ret;262	int fd = perf_data__fd(session->data);263	int i;264	FILE *out = c->out;265 266	output_json_key_format(out, false, 2, "header-version", "%u", header->version);267 268	ret = fstat(fd, &st);269	if (ret >= 0) {270		time_t stctime = st.st_mtime;271		char buf[256];272 273		strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&stctime));274		output_json_key_string(out, true, 2, "captured-on", buf);275	} else {276		pr_debug("Failed to get mtime of source file, not writing captured-on");277	}278 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);282 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);286 287	if (header->env.cpu_desc)288		output_json_key_string(out, true, 2, "cpu-desc", header->env.cpu_desc);289 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);293 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);301	}302 303	output_json_key_string(out, true, 2, "perf-version", header->env.version);304 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]);309	}310	output_json_format(out, false, 2, "]");311}312 313int bt_convert__perf2json(const char *input_name, const char *output_name,314		struct perf_data_convert_opts *opts __maybe_unused)315{316	struct perf_session *session;317	int fd;318	int ret = -1;319	struct convert_json c = {320		.first = true,321		.events_count = 0,322	};323	struct perf_data data = {324		.mode = PERF_DATA_MODE_READ,325		.path = input_name,326		.force = opts->force,327	};328 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_LIBTRACEEVENT340	c.tool.tracing_data   = perf_event__process_tracing_data;341#endif342	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;348 349	if (opts->all) {350		pr_err("--all is currently unsupported for JSON output.\n");351		goto err;352	}353	if (opts->tod) {354		pr_err("--tod is currently unsupported for JSON output.\n");355		goto err;356	}357 358	fd = open(output_name, O_CREAT | O_WRONLY | (opts->force ? O_TRUNC : O_EXCL), 0666);359	if (fd == -1) {360		if (errno == EEXIST)361			pr_err("Output file exists. Use --force to overwrite it.\n");362		else363			pr_err("Error opening output file!\n");364		goto err;365	}366 367	c.out = fdopen(fd, "w");368	if (!c.out) {369		fprintf(stderr, "Error opening output file!\n");370		close(fd);371		goto err;372	}373 374	session = perf_session__new(&data, &c.tool);375	if (IS_ERR(session)) {376		fprintf(stderr, "Error creating perf session!\n");377		goto err_fclose;378	}379 380	if (symbol__init(&session->header.env) < 0) {381		fprintf(stderr, "Symbol init error!\n");382		goto err_session_delete;383	}384 385	// The opening brace is printed manually because it isn't delimited from a386	// previous value (i.e. we don't want a leading newline)387	fputc('{', c.out);388 389	// Version number for future-proofing. Most additions should be able to be390	// done in a backwards-compatible way so this should only need to be bumped391	// if some major breaking change must be made.392	output_json_format(c.out, false, 1, "\"linux-perf-json-version\": 1");393 394	// Output headers395	output_json_format(c.out, true, 1, "\"headers\": {");396	output_headers(session, &c);397	output_json_format(c.out, false, 1, "}");398 399	// Output samples400	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, "}");404	fputc('\n', c.out);405 406	fprintf(stderr,407			"[ perf data convert: Converted '%s' into JSON data '%s' ]\n",408			data.path, output_name);409 410	fprintf(stderr,411			"[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n",412			(ftell(c.out)) / 1024.0 / 1024.0, c.events_count);413 414	ret = 0;415err_session_delete:416	perf_session__delete(session);417err_fclose:418	fclose(c.out);419err:420	return ret;421}422