lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Date:	Tue, 15 Dec 2015 12:57:13 +0100
From:	John Ogness <john.ogness@...utronix.de>
To:	peterz@...radead.org
Cc:	linux-kernel@...r.kernel.org
Subject: [RFC PATCH] perf tools: add on-the-fly ctf conversion

A new argument --format is added to specify an alternate output
format. If perf is compiled with libbabeltrace, support for the
ctf format is available. An example:

perf record --format ctf -e sched:sched_switch ls

Signed-off-by: John Ogness <john.ogness@...utronix.de>
---
Patch against next-20151215.

There definately could be some beautification so the output
is more useful. But I am interested if this approach is
generally how we should do it.

 tools/perf/builtin-record.c        |   86 ++++++++++++++---
 tools/perf/util/data-convert-bt.c  |  186 +++++++++++++++++++++++++++++++++++-
 tools/perf/util/format-converter.h |   17 ++++
 3 files changed, 272 insertions(+), 17 deletions(-)
 create mode 100644 tools/perf/util/format-converter.h

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 199fc31..49f555c 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -32,6 +32,7 @@
 #include "util/parse-branch-options.h"
 #include "util/parse-regs-options.h"
 #include "util/llvm-utils.h"
+#include "util/format-converter.h"
 
 #include <unistd.h>
 #include <sched.h>
@@ -41,6 +42,7 @@
 struct record {
 	struct perf_tool	tool;
 	struct record_opts	opts;
+	struct format_converter	*format;
 	u64			bytes_written;
 	struct perf_data_file	file;
 	struct auxtrace_record	*itr;
@@ -53,9 +55,18 @@ struct record {
 	unsigned long long	samples;
 };
 
+
 static int record__write(struct record *rec, void *bf, size_t size)
 {
-	if (perf_data_file__write(rec->session->file, bf, size) < 0) {
+	if (rec->format) {
+		if (rec->format->write &&
+		    rec->format->write(rec->evlist, bf, size,
+				       rec->format->priv)) {
+			pr_err("failed to write alternate perf data format\n");
+			return -1;
+		}
+
+	} else if (perf_data_file__write(rec->session->file, bf, size) < 0) {
 		pr_err("failed to write perf data, error: %m\n");
 		return -1;
 	}
@@ -153,7 +164,7 @@ static int record__process_auxtrace(struct perf_tool *tool,
 	size_t padding;
 	u8 pad[8] = {0};
 
-	if (!perf_data_file__is_pipe(file)) {
+	if (!perf_data_file__is_pipe(file) && !rec->format) {
 		off_t file_offset;
 		int fd = perf_data_file__fd(file);
 		int err;
@@ -497,7 +508,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 	else
 		signal(SIGUSR2, SIG_IGN);
 
-	session = perf_session__new(file, false, tool);
+	if (rec->format)
+		session = perf_session__new(NULL, false, tool);
+	else
+		session = perf_session__new(file, false, tool);
 	if (session == NULL) {
 		pr_err("Perf session creation failed.\n");
 		return -1;
@@ -536,7 +550,17 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 	if (!rec->evlist->nr_groups)
 		perf_header__clear_feat(&session->header, HEADER_GROUP_DESC);
 
-	if (file->is_pipe) {
+	if (rec->format) {
+		if (!file->path)
+			file->path = "perf.data";
+		if (rec->format->init &&
+		    rec->format->init(rec->evlist, file->path,
+				      &rec->format->priv)) {
+			pr_err("Failed to initialize alternate format.\n");
+			err = -1;
+			goto out_child;
+		}
+	} else if (file->is_pipe) {
 		err = perf_header__write_pipe(fd);
 		if (err < 0)
 			goto out_child;
@@ -561,7 +585,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 						   process_synthesized_event);
 		if (err < 0) {
 			pr_err("Couldn't synthesize attrs.\n");
-			goto out_child;
+			goto out_format;
 		}
 
 		if (have_tracepoints(&rec->evlist->entries)) {
@@ -577,7 +601,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 								  process_synthesized_event);
 			if (err <= 0) {
 				pr_err("Couldn't record tracing data.\n");
-				goto out_child;
+				goto out_format;
 			}
 			rec->bytes_written += err;
 		}
@@ -587,7 +611,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 		err = perf_event__synthesize_auxtrace_info(rec->itr, tool,
 					session, process_synthesized_event);
 		if (err)
-			goto out_delete_session;
+			goto out_format;
 	}
 
 	err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
@@ -613,7 +637,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 					    process_synthesized_event, opts->sample_address,
 					    opts->proc_map_timeout);
 	if (err != 0)
-		goto out_child;
+		goto out_format;
 
 	if (rec->realtime_prio) {
 		struct sched_param param;
@@ -622,7 +646,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 		if (sched_setscheduler(0, SCHED_FIFO, &param)) {
 			pr_err("Could not set realtime priority.\n");
 			err = -1;
-			goto out_child;
+			goto out_format;
 		}
 	}
 
@@ -673,7 +697,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 		if (record__mmap_read_all(rec) < 0) {
 			auxtrace_snapshot_enabled = 0;
 			err = -1;
-			goto out_child;
+			goto out_format;
 		}
 
 		if (auxtrace_record__snapshot_started) {
@@ -683,7 +707,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 			if (auxtrace_snapshot_err) {
 				pr_err("AUX area tracing snapshot failed\n");
 				err = -1;
-				goto out_child;
+				goto out_format;
 			}
 		}
 
@@ -721,12 +745,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
 		const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg));
 		pr_err("Workload failed: %s\n", emsg);
 		err = -1;
-		goto out_child;
+		goto out_format;
 	}
 
 	if (!quiet)
 		fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
 
+out_format:
+	if (rec->format && rec->format->cleanup)
+		rec->format->cleanup(rec->evlist, rec->format->priv);
 out_child:
 	if (forks) {
 		int exit_status;
@@ -748,7 +775,7 @@ out_child:
 	/* this will be recalculated during process_buildids() */
 	rec->samples = 0;
 
-	if (!err && !file->is_pipe) {
+	if (!err && !file->is_pipe && !rec->format) {
 		rec->session->header.data_size += rec->bytes_written;
 		file->size = lseek(perf_data_file__fd(file), 0, SEEK_CUR);
 
@@ -927,6 +954,37 @@ static int parse_clockid(const struct option *opt, const char *str, int unset)
 	return -1;
 }
 
+static int parse_format(const struct option *opt, const char *str, int unset)
+{
+	struct record *rec = opt->value;
+
+	if (unset) {
+		rec->format = NULL;
+		return 0;
+	}
+
+	/* no arg passed */
+	if (!str) {
+		ui__warning("missing format argument\n");
+		return -1;
+	}
+
+	/* no setting it twice */
+	if (rec->format) {
+		ui__warning("format specified multiple times\n");
+		return -1;
+	}
+
+#ifdef HAVE_LIBBABELTRACE_SUPPORT
+	if (strcasecmp(str, "ctf") == 0) {
+		rec->format = &ctf_format;
+		return 0;
+	}
+#endif
+	ui__warning("unknown format %s, check man page\n", str);
+	return -1;
+}
+
 static int record__parse_mmap_pages(const struct option *opt,
 				    const char *str,
 				    int unset __maybe_unused)
@@ -1113,6 +1171,8 @@ struct option __record_options[] = {
 			"per thread proc mmap processing timeout in ms"),
 	OPT_BOOLEAN(0, "switch-events", &record.opts.record_switch_events,
 		    "Record context switch events"),
+	OPT_CALLBACK(0, "format", &record, "format", "alternate output format",
+		     parse_format),
 #ifdef HAVE_LIBBPF_SUPPORT
 	OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path",
 		   "clang binary to use for compiling BPF scriptlets"),
diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index 34cd1e4..eb40d74 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -7,6 +7,7 @@
  * Released under the GPL v2. (and only v2, not any later version)
  */
 
+#include <sys/utsname.h>
 #include <linux/compiler.h>
 #include <babeltrace/ctf-writer/writer.h>
 #include <babeltrace/ctf-writer/clock.h>
@@ -26,6 +27,7 @@
 #include "evlist.h"
 #include "evsel.h"
 #include "machine.h"
+#include "format-converter.h"
 
 #define pr_N(n, fmt, ...) \
 	eprintf(n, debug_data_convert, fmt, ##__VA_ARGS__)
@@ -896,6 +898,20 @@ static int ctf_writer__setup_env(struct ctf_writer *cw,
 {
 	struct perf_header *header = &session->header;
 	struct bt_ctf_writer *writer = cw->writer;
+	struct utsname uts;
+	int ret;
+
+	ret = uname(&uts);
+	if (ret == 0) {
+		if (!header->env.hostname)
+			header->env.hostname = strdup(uts.nodename);
+		if (!header->env.os_release)
+			header->env.os_release = strdup(uts.release);
+		if (!header->env.version)
+			header->env.version = strdup(perf_version_string);
+		if (!header->env.arch)
+			header->env.arch = strdup(uts.machine);
+	}
 
 #define ADD(__n, __v)							\
 do {									\
@@ -903,11 +919,11 @@ do {									\
 		return -1;						\
 } while (0)
 
-	ADD("host",    header->env.hostname);
+	ADD("host",    header->env.hostname ?: "unknown");
 	ADD("sysname", "Linux");
-	ADD("release", header->env.os_release);
-	ADD("version", header->env.version);
-	ADD("machine", header->env.arch);
+	ADD("release", header->env.os_release ?: "unknown");
+	ADD("version", header->env.version ?: "unknown");
+	ADD("machine", header->env.arch ?: "unknown");
 	ADD("domain", "kernel");
 	ADD("tracer_name", "perf");
 
@@ -1183,3 +1199,165 @@ free_writer:
 	pr_err("Error during conversion setup.\n");
 	return err;
 }
+
+struct ctf_fc {
+	struct convert c;
+
+	void *partial_buf;
+	size_t partial_buf_size;
+
+	void *partial_buf_pos;
+	size_t partial_buf_rem;
+};
+
+static int ctf_fc_init(struct perf_evlist *evlist, const char *path,
+		       void **priv)
+{
+	struct perf_session dummy_session;
+	struct ctf_writer *cw;
+	struct ctf_fc *fc;
+	int err = -1;
+
+	fc = calloc(1, sizeof(*fc));
+	if (!fc)
+		goto nomem;
+
+	cw = &fc->c.writer;
+
+	memset(&dummy_session, 0, sizeof(dummy_session));
+
+        if (ctf_writer__init(cw, path))
+                goto free_mem;
+
+	if (ctf_writer__setup_env(cw, &dummy_session))
+		goto free_writer;
+
+	dummy_session.evlist = evlist;
+	if (setup_events(cw, &dummy_session))
+		goto free_writer;
+
+	if (setup_streams(cw, &dummy_session))
+		goto free_writer;
+
+	*priv = fc;
+
+	return 0;
+
+free_writer:
+	ctf_writer__cleanup(cw);
+free_mem:
+	free(fc);
+nomem:
+	pr_err("Error during conversion setup.\n");
+	return err;
+}
+
+static union perf_event *get_next_event(struct ctf_fc *fc, void *buf,
+					size_t size, size_t *inc)
+{
+	union perf_event *event = buf;
+
+	/* deal with existing partial event first */
+	if (fc->partial_buf_pos) {
+		if (size >= fc->partial_buf_rem)
+			size = fc->partial_buf_rem;
+
+		memcpy(fc->partial_buf_pos, buf, size);
+		fc->partial_buf_pos += size;
+		fc->partial_buf_rem -= size;
+		*inc = size;
+
+		/* event still partial */
+		if (fc->partial_buf_rem)
+			return NULL;
+
+		/* we have a full event */
+		fc->partial_buf_pos = NULL;
+		return fc->partial_buf;
+	}
+
+	/* deal with new paritial event */
+	if (size < event->header.size) {
+		/* realloc larger partial buffer if necessary */
+		if (fc->partial_buf_size < event->header.size) {
+			if (fc->partial_buf) {
+				free(fc->partial_buf);
+				fc->partial_buf_size = 0;
+			}
+			fc->partial_buf = malloc(event->header.size);
+			if (!fc->partial_buf) {
+				*inc = 0;
+				return NULL;
+			}
+			fc->partial_buf_size = event->header.size;
+		}
+
+		/* copy over the part of the event we have */
+		memcpy(fc->partial_buf, buf, size);
+		fc->partial_buf_pos = fc->partial_buf + size;
+		fc->partial_buf_rem = event->header.size - size;
+		*inc = size;
+
+		/* we now have a partial event */
+		return NULL;
+	}
+
+	/* full event available */
+	*inc = event->header.size;
+	return event;
+}
+
+static int ctf_fc_write(struct perf_evlist *evlist, void *buf, size_t size,
+			void *priv)
+{
+	struct ctf_fc *fc = priv;
+	struct convert *c = &fc->c;
+	struct perf_sample sample;
+	struct perf_evsel *evsel;
+	union perf_event *event;
+	size_t inc;
+
+	while (size) {
+		event = get_next_event(fc, buf, size, &inc);
+
+		if (!event || event->header.type != PERF_RECORD_SAMPLE)
+			goto skip_event;
+
+		if (perf_evlist__parse_sample(evlist, event, &sample)) {
+			pr_err("Failed to parse event.\n");
+			goto skip_event;
+		}
+
+		evsel = perf_evlist__id2evsel(evlist, sample.id);
+		if (!evsel) {
+			pr_err("Failed to identify event.\n");
+			goto skip_event;
+		}
+
+		if (process_sample_event(&c->tool, event, &sample, evsel, NULL))
+			pr_err("Failed to process event.\n");
+skip_event:
+		size -= inc;
+		buf += inc;
+	}
+
+	return 0;
+}
+
+static int ctf_fc_cleanup(struct perf_evlist *evlist __maybe_unused, void *priv)
+{
+	struct ctf_fc *fc = priv;
+	struct ctf_writer *cw = &fc->c.writer;
+
+	if (ctf_writer__flush_streams(cw))
+		pr_err("Failed to flush events.\n");
+	ctf_writer__cleanup(cw);
+	free(fc);
+	return 0;
+}
+
+struct format_converter ctf_format = {
+	.init = ctf_fc_init,
+	.write = ctf_fc_write,
+	.cleanup = ctf_fc_cleanup,
+};
diff --git a/tools/perf/util/format-converter.h b/tools/perf/util/format-converter.h
new file mode 100644
index 0000000..bb204b1
--- /dev/null
+++ b/tools/perf/util/format-converter.h
@@ -0,0 +1,17 @@
+#ifndef __PERF_FORMAT_CONVERTER_H
+#define __PERF_FORMAT_CONVERTER_H
+
+#include "evlist.h"
+
+struct format_converter {
+	int (*init)(struct perf_evlist *evlist, const char *path, void **priv);
+	int (*write)(struct perf_evlist *evlist, void *buf, size_t size, void *priv);
+	int (*cleanup)(struct perf_evlist *evlist, void *priv);
+	void *priv;
+};
+
+#ifdef HAVE_LIBBABELTRACE_SUPPORT
+extern struct format_converter ctf_format;
+#endif
+
+#endif /* __PERF_FORMAT_CONVERTER_H */
-- 
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ