Signed-off-by: Tom Zanussi <tzan...@gmail.com>
---
tools/perf/builtin-record.c | 7 +++++
tools/perf/util/event.h | 14 +++++++++
tools/perf/util/header.c | 63 ++++++++++++++++++++++++++++++++++++++-----
tools/perf/util/header.h | 9 ++++++
tools/perf/util/session.c | 13 +++++++++
tools/perf/util/session.h | 3 +-
6 files changed, 101 insertions(+), 8 deletions(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 3ba466f..0e3d4cd 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -574,6 +574,13 @@ static int __cmd_record(int argc, const char **argv)
pr_err("Couldn't synthesize attrs.\n");
return err;
}
+
+ err = event__synthesize_event_types(process_synthesized_event,
+ session);
+ if (err < 0) {
+ pr_err("Couldn't synthesize event_types.\n");
+ return err;
+ }
}
err = event__synthesize_kernel_mmap(process_synthesized_event,
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 83e5d08..dc6d27b 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -85,6 +85,7 @@ struct build_id_event {
enum perf_header_event_type { /* above any possible kernel type */
PERF_RECORD_HEADER_ATTR = 64,
+ PERF_RECORD_HEADER_EVENT_TYPE = 65,
PERF_RECORD_HEADER_MAX
};
@@ -94,6 +95,18 @@ struct attr_event {
u64 id[];
};
+#define MAX_EVENT_NAME 64
+
+struct perf_trace_event_type {
+ u64 event_id;
+ char name[MAX_EVENT_NAME];
+};
+
+struct event_type_event {
+ struct perf_event_header header;
+ struct perf_trace_event_type event_type;
+};
+
typedef union event_union {
struct perf_event_header header;
struct ip_event ip;
@@ -104,6 +117,7 @@ typedef union event_union {
struct read_event read;
struct sample_event sample;
struct attr_event attr;
+ struct event_type_event event_type;
} event_t;
struct events_stats {
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 6397ad9..cb75e66 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -99,13 +99,6 @@ int perf_header__add_attr(struct perf_header *self,
return 0;
}
-#define MAX_EVENT_NAME 64
-
-struct perf_trace_event_type {
- u64 event_id;
- char name[MAX_EVENT_NAME];
-};
-
static int event_count;
static struct perf_trace_event_type *events;
@@ -714,6 +707,7 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
perf_event_ops__fill_stop(&header_ops);
header_ops.attr = event__process_attr;
+ header_ops.event_type = event__process_event_type;
session->fd = fd;
return perf_session__process_events(session, &header_ops);
@@ -905,3 +899,58 @@ int event__process_attr(event_t *self, struct perf_session *session)
return 0;
}
+
+int event__synthesize_event_type(u64 event_id, char *name,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t ev;
+ size_t size = 0;
+ int err = 0;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.event_type.event_type.event_id = event_id;
+ memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME);
+ strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1);
+
+ ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
+ size = strlen(name);
+ size = ALIGN(size, sizeof(u64));
+ ev.event_type.header.size = sizeof(ev.event_type) -
+ (sizeof(ev.event_type.event_type.name) - size);
+
+ err = process(&ev, session);
+
+ return err;
+}
+
+int event__synthesize_event_types(event__handler_t process,
+ struct perf_session *session)
+{
+ struct perf_trace_event_type *type;
+ int i, err = 0;
+
+ for (i = 0; i < event_count; i++) {
+ type = &events[i];
+
+ err = event__synthesize_event_type(type->event_id, type->name,
+ process, session);
+ if (err) {
+ pr_debug("failed to create perf header event type\n");
+ return err;
+ }
+ }
+
+ return err;
+}
+
+int event__process_event_type(event_t *self,
+ struct perf_session *session __unused)
+{
+ if (perf_header__push_event(self->event_type.event_type.event_id,
+ self->event_type.event_type.name) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index f5fd82c..9d18c9b 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -103,4 +103,13 @@ int event__synthesize_attrs(struct perf_header *self,
struct perf_session *session);
int event__process_attr(event_t *self, struct perf_session *session);
+int event__synthesize_event_type(u64 event_id, char *name,
+ event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_event_types(event__handler_t process,
+ struct perf_session *session);
+int event__process_event_type(event_t *self,
+ struct perf_session *session);
+
+
#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index ff51105..d704bdf 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -211,6 +211,8 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
handler->unthrottle = process_event_stub;
if (handler->attr == NULL)
handler->attr = process_event_stub;
+ if (handler->event_type == NULL)
+ handler->event_type = process_event_stub;
}
static int process_event_stop(event_t *event __used,
@@ -232,6 +234,7 @@ void perf_event_ops__fill_stop(struct perf_event_ops *handler)
handler->throttle = process_event_stop;
handler->unthrottle = process_event_stop;
handler->attr = process_event_stop;
+ handler->event_type = process_event_stop;
}
static const char *event__name[] = {
@@ -246,6 +249,7 @@ static const char *event__name[] = {
[PERF_RECORD_READ] = "READ",
[PERF_RECORD_SAMPLE] = "SAMPLE",
[PERF_RECORD_HEADER_ATTR] = "ATTR",
+ [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
};
unsigned long event__total[PERF_RECORD_HEADER_MAX];
@@ -332,6 +336,12 @@ static void event__attr_swap(event_t *self)
mem_bswap_64(self->attr.id, size);
}
+static void event__event_type_swap(event_t *self)
+{
+ self->event_type.event_type.event_id =
+ bswap_64(self->event_type.event_type.event_id);
+}
+
typedef void (*event__swap_op)(event_t *self);
static event__swap_op event__swap_ops[] = {
@@ -343,6 +353,7 @@ static event__swap_op event__swap_ops[] = {
[PERF_RECORD_READ] = event__read_swap,
[PERF_RECORD_SAMPLE] = event__all64_swap,
[PERF_RECORD_HEADER_ATTR] = event__attr_swap,
+ [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap,
[PERF_RECORD_HEADER_MAX] = NULL,
};
@@ -385,6 +396,8 @@ static int perf_session__process_event(struct perf_session *self,
return ops->unthrottle(event, self);
case PERF_RECORD_HEADER_ATTR:
return ops->attr(event, self);
+ case PERF_RECORD_HEADER_EVENT_TYPE:
+ return ops->event_type(event, self);
default:
self->unknown_events++;
return -1;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 7365a24..ea9eb4a 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -44,7 +44,8 @@ struct perf_event_ops {
read,
throttle,
unthrottle,
- attr;
+ attr,
+ event_type;
};
/*
--
1.6.4.GIT
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
To run the scripts, simply pipe the output of the 'perf trace record'
step as input to the corresponding 'perf trace report' step:
$ perf trace record sctop | perf trace report sctop
A future patch should probably automatically do this i.e. the user
should be able to just run 'perf trace sctop' and get the same thing,
and the output should do better than just continuously scroll, but
these are just demo scripts for now...
Signed-off-by: Tom Zanussi <tzan...@gmail.com>
---
tools/perf/scripts/perl/bin/rwtop-record | 2 +
tools/perf/scripts/perl/bin/rwtop-report | 6 +
tools/perf/scripts/perl/rwtop.pl | 165 ++++++++++++++++++++++++++++
tools/perf/scripts/python/bin/sctop-record | 2 +
tools/perf/scripts/python/bin/sctop-report | 4 +
tools/perf/scripts/python/sctop.py | 64 +++++++++++
6 files changed, 243 insertions(+), 0 deletions(-)
create mode 100644 tools/perf/scripts/perl/bin/rwtop-record
create mode 100644 tools/perf/scripts/perl/bin/rwtop-report
create mode 100644 tools/perf/scripts/perl/rwtop.pl
create mode 100644 tools/perf/scripts/python/bin/sctop-record
create mode 100644 tools/perf/scripts/python/bin/sctop-report
create mode 100644 tools/perf/scripts/python/sctop.py
diff --git a/tools/perf/scripts/perl/bin/rwtop-record b/tools/perf/scripts/perl/bin/rwtop-record
new file mode 100644
index 0000000..0993193
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/rwtop-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -o - -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write
diff --git a/tools/perf/scripts/perl/bin/rwtop-report b/tools/perf/scripts/perl/bin/rwtop-report
new file mode 100644
index 0000000..3d06c98
--- /dev/null
+++ b/tools/perf/scripts/perl/bin/rwtop-report
@@ -0,0 +1,6 @@
+#!/bin/bash
+# description: system-wide r/w top
+perf trace -i - -s ~/libexec/perf-core/scripts/perl/rwtop.pl
+
+
+
diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl
new file mode 100644
index 0000000..4e0d1fd
--- /dev/null
+++ b/tools/perf/scripts/perl/rwtop.pl
@@ -0,0 +1,165 @@
+#!/usr/bin/perl -w
+# (c) 2009, Tom Zanussi <tzan...@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+
+# Display r/w activity for all processes
+
+use 5.010000;
+use strict;
+use warnings;
+
+use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+use lib "./Perf-Trace-Util/lib";
+use Perf::Trace::Core;
+use Perf::Trace::Util;
+
+my $interval = 5;
+my $nlines = 10;
+my $print_thread;
+
+my %reads;
+my %writes;
+
+sub syscalls::sys_exit_read
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $ret) = @_;
+
+ if ($ret > 0) {
+ $reads{$common_pid}{bytes_read} += $ret;
+ } else {
+ if (!defined ($reads{$common_pid}{bytes_read})) {
+ $reads{$common_pid}{bytes_read} = 0;
+ }
+ $reads{$common_pid}{errors}{$ret}++;
+ }
+}
+
+sub syscalls::sys_enter_read
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $fd, $buf, $count) = @_;
+
+ $reads{$common_pid}{bytes_requested} += $count;
+ $reads{$common_pid}{total_reads}++;
+ $reads{$common_pid}{comm} = $common_comm;
+}
+
+sub syscalls::sys_exit_write
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $ret) = @_;
+
+ if ($ret <= 0) {
+ $writes{$common_pid}{errors}{$ret}++;
+ }
+}
+
+sub syscalls::sys_enter_write
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm,
+ $nr, $fd, $buf, $count) = @_;
+
+ $writes{$common_pid}{bytes_written} += $count;
+ $writes{$common_pid}{total_writes}++;
+ $writes{$common_pid}{comm} = $common_comm;
+}
+
+sub trace_begin
+{
+ $SIG{ALRM} = \&print_totals;
+ alarm 5;
+}
+
+sub trace_end
+{
+ print_unhandled();
+ print_totals();
+}
+
+sub print_totals
+{
+ my $count;
+
+ $count = 0;
+
+ printf("\nread counts by pid:\n\n");
+
+ printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
+ "# reads", "bytes_requested", "bytes_read");
+ printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------",
+ "-----------", "----------", "----------");
+
+ foreach my $pid (sort {$reads{$b}{bytes_read} <=>
+ $reads{$a}{bytes_read}} keys %reads) {
+ my $comm = $reads{$pid}{comm};
+ my $total_reads = $reads{$pid}{total_reads};
+ my $bytes_requested = $reads{$pid}{bytes_requested};
+ my $bytes_read = $reads{$pid}{bytes_read};
+
+ printf("%6s %-20s %10s %10s %10s\n", $pid, $comm,
+ $total_reads, $bytes_requested, $bytes_read);
+
+ if (++$count == $nlines) {
+ last;
+ }
+ }
+
+ $count = 0;
+
+ printf("\nwrite counts by pid:\n\n");
+
+ printf("%6s %20s %10s %10s\n", "pid", "comm",
+ "# writes", "bytes_written");
+ printf("%6s %-20s %10s %10s\n", "------", "--------------------",
+ "-----------", "----------");
+
+ foreach my $pid (sort {$writes{$b}{bytes_written} <=>
+ $writes{$a}{bytes_written}} keys %writes) {
+ my $comm = $writes{$pid}{comm};
+ my $total_writes = $writes{$pid}{total_writes};
+ my $bytes_written = $writes{$pid}{bytes_written};
+
+ printf("%6s %-20s %10s %10s\n", $pid, $comm,
+ $total_writes, $bytes_written);
+
+ if (++$count == $nlines) {
+ last;
+ }
+ }
+
+ %reads = ();
+ %writes = ();
+ alarm 5;
+}
+
+my %unhandled;
+
+sub print_unhandled
+{
+ if ((scalar keys %unhandled) == 0) {
+ return;
+ }
+
+ print "\nunhandled events:\n\n";
+
+ printf("%-40s %10s\n", "event", "count");
+ printf("%-40s %10s\n", "----------------------------------------",
+ "-----------");
+
+ foreach my $event_name (keys %unhandled) {
+ printf("%-40s %10d\n", $event_name, $unhandled{$event_name});
+ }
+}
+
+sub trace_unhandled
+{
+ my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs,
+ $common_pid, $common_comm) = @_;
+
+ $unhandled{$event_name}++;
+}
diff --git a/tools/perf/scripts/python/bin/sctop-record b/tools/perf/scripts/python/bin/sctop-record
new file mode 100644
index 0000000..1a46fb6
--- /dev/null
+++ b/tools/perf/scripts/python/bin/sctop-record
@@ -0,0 +1,2 @@
+#!/bin/bash
+perf record -o - -c 1 -f -a -M -R -e raw_syscalls:sys_enter
diff --git a/tools/perf/scripts/python/bin/sctop-report b/tools/perf/scripts/python/bin/sctop-report
new file mode 100644
index 0000000..4ca46c0
--- /dev/null
+++ b/tools/perf/scripts/python/bin/sctop-report
@@ -0,0 +1,4 @@
+#!/bin/bash
+# description: syscall top
+# args: [comm]
+perf trace -i - -s ~/libexec/perf-core/scripts/python/sctop.py $1
diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python/sctop.py
new file mode 100644
index 0000000..a44de25
--- /dev/null
+++ b/tools/perf/scripts/python/sctop.py
@@ -0,0 +1,64 @@
+# system call top
+# (c) 2010, Tom Zanussi <tzan...@gmail.com>
+# Licensed under the terms of the GNU GPL License version 2
+#
+# Periodically displays system-wide system call totals, broken down by
+# syscall. If a [comm] arg is specified, only syscalls called by
+# [comm] are displayed.
+
+import thread
+import time
+import os
+import sys
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from Core import *
+from Util import *
+
+usage = "perf trace -s syscall-counts.py [comm]\n";
+
+for_comm = None
+interval = 5
+
+if len(sys.argv) > 2:
+ sys.exit(usage)
+
+if len(sys.argv) > 1:
+ for_comm = sys.argv[1]
+
+syscalls = autodict()
+
+def trace_begin():
+ thread.start_new_thread(print_syscall_totals, (interval,))
+ pass
+
+def raw_syscalls__sys_enter(event_name, context, common_cpu,
+ common_secs, common_nsecs, common_pid, common_comm,
+ id, args):
+ if for_comm is not None:
+ if common_comm != for_comm:
+ return
+ try:
+ syscalls[id] += 1
+ except TypeError:
+ syscalls[id] = 1
+
+def print_syscall_totals(interval):
+ while 1:
+ if for_comm is not None:
+ print "\nsyscall events for %s:\n\n" % (for_comm),
+ else:
+ print "\nsyscall events:\n\n",
+
+ print "%-40s %10s\n" % ("event", "count"),
+ print "%-40s %10s\n" % ("----------------------------------------", \
+ "-----------"),
+
+ for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \
+ reverse = True):
+ print "%-40d %10d\n" % (id, val),
+ syscalls.clear()
+ time.sleep(interval)
Signed-off-by: Tom Zanussi <tzan...@gmail.com>
---
tools/perf/builtin-record.c | 15 ++++++-
tools/perf/util/event.h | 2 +
tools/perf/util/header.c | 90 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/header.h | 7 +++
tools/perf/util/session.c | 7 +++
tools/perf/util/session.h | 3 +-
6 files changed, 120 insertions(+), 4 deletions(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 95bccdc..0ab9ffa 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -405,10 +405,19 @@ static int process_buildids(void)
static void atexit_header(void)
{
- session->header.data_size += bytes_written;
+ if (!pipe_output) {
+ session->header.data_size += bytes_written;
- process_buildids();
- perf_header__write(&session->header, output, true);
+ process_buildids();
+ perf_header__write(&session->header, output, true);
+ } else {
+ int err;
+
+ err = event__synthesize_build_ids(process_synthesized_event,
+ session);
+ if (err < 0)
+ pr_err("Couldn't synthesize build ids.\n");
+ }
}
static int __cmd_record(int argc, const char **argv)
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index c111358..0705e75 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -87,6 +87,7 @@ enum perf_header_event_type { /* above any possible kernel type */
PERF_RECORD_HEADER_ATTR = 64,
PERF_RECORD_HEADER_EVENT_TYPE = 65,
PERF_RECORD_HEADER_TRACING_DATA = 66,
+ PERF_RECORD_HEADER_BUILD_ID = 67,
PERF_RECORD_HEADER_MAX
};
@@ -125,6 +126,7 @@ typedef union event_union {
struct attr_event attr;
struct event_type_event event_type;
struct tracing_data_event tracing_data;
+ struct build_id_event build_id;
} event_t;
struct events_stats {
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 5910aee..c380264 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1007,3 +1007,93 @@ int event__process_tracing_data(event_t *self,
return size_read + padding;
}
+
+int event__synthesize_build_id(struct dso *pos, u16 misc,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t ev;
+ size_t len;
+ int err = 0;
+
+ if (!pos->hit)
+ return err;
+
+ memset(&ev, 0, sizeof(ev));
+
+ len = pos->long_name_len + 1;
+ len = ALIGN(len, NAME_ALIGN);
+ memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
+ ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
+ ev.build_id.header.misc = misc;
+ ev.build_id.header.size = sizeof(ev.build_id) + len;
+ memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
+
+ err = process(&ev, session);
+
+ return err;
+}
+
+static int __event_synthesize_build_ids(struct list_head *head, u16 misc,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ struct dso *pos;
+
+ dsos__for_each_with_build_id(pos, head) {
+ int err;
+ if (!pos->hit)
+ continue;
+
+ err = event__synthesize_build_id(pos, misc, process, session);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int event__synthesize_build_ids(event__handler_t process,
+ struct perf_session *session)
+{
+ int err;
+
+ if (!dsos__read_build_ids(true))
+ return 0;
+
+ err = __event_synthesize_build_ids(&dsos__kernel,
+ PERF_RECORD_MISC_KERNEL,
+ process, session);
+ if (err == 0)
+ err = __event_synthesize_build_ids(&dsos__user,
+ PERF_RECORD_MISC_USER,
+ process, session);
+
+ if (err < 0) {
+ pr_debug("failed to synthesize build ids\n");
+ return err;
+ }
+
+ dsos__cache_build_ids();
+
+ return 0;
+}
+
+int event__process_build_id(event_t *self,
+ struct perf_session *session __unused)
+{
+ struct list_head *head = &dsos__user;
+ struct dso *dso;
+
+ if (self->build_id.header.misc & PERF_RECORD_MISC_KERNEL)
+ head = &dsos__kernel;
+
+ dso = __dsos__findnew(head, self->build_id.filename);
+ if (dso != NULL) {
+ dso__set_build_id(dso, &self->build_id.build_id);
+ if (head == &dsos__kernel && self->build_id.filename[0] == '[')
+ dso->kernel = 1;
+ }
+
+ return 0;
+}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index a83f375..cfb807f 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -118,4 +118,11 @@ int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs,
int event__process_tracing_data(event_t *self,
struct perf_session *session);
+int event__synthesize_build_id(struct dso *pos, u16 misc,
+ event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_build_ids(event__handler_t process,
+ struct perf_session *session);
+int event__process_build_id(event_t *self, struct perf_session *session);
+
#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 74e9646..679274d 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -215,6 +215,8 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
handler->event_type = process_event_stub;
if (handler->tracing_data == NULL)
handler->tracing_data = process_event_stub;
+ if (handler->build_id == NULL)
+ handler->build_id = process_event_stub;
}
static int process_event_stop(event_t *event __used,
@@ -238,6 +240,7 @@ void perf_event_ops__fill_stop(struct perf_event_ops *handler)
handler->attr = process_event_stop;
handler->event_type = process_event_stop;
handler->tracing_data = process_event_stop;
+ handler->build_id = process_event_stop;
}
static const char *event__name[] = {
@@ -254,6 +257,7 @@ static const char *event__name[] = {
[PERF_RECORD_HEADER_ATTR] = "ATTR",
[PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
[PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
+ [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
};
unsigned long event__total[PERF_RECORD_HEADER_MAX];
@@ -364,6 +368,7 @@ static event__swap_op event__swap_ops[] = {
[PERF_RECORD_HEADER_ATTR] = event__attr_swap,
[PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap,
[PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap,
+ [PERF_RECORD_HEADER_BUILD_ID] = NULL,
[PERF_RECORD_HEADER_MAX] = NULL,
};
@@ -412,6 +417,8 @@ static int perf_session__process_event(struct perf_session *self,
/* setup for reading amidst mmap */
lseek(self->fd, offset + head, SEEK_SET);
return ops->tracing_data(event, self);
+ case PERF_RECORD_HEADER_BUILD_ID:
+ return ops->build_id(event, self);
default:
self->unknown_events++;
return -1;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index a648af0..87821a9 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -46,7 +46,8 @@ struct perf_event_ops {
unthrottle,
attr,
event_type,
- tracing_data;
+ tracing_data,
+ build_id;
};
/*
The tracing data is pretty large, and this patch doesn't attempt to
break it down into component events. The tracing_data event itself
doesn't actually contain the tracing data, rather it arranges for the
event processing code to skip over it after it's read, using the
'action' added in a previous patch; in this case the action is 'skip'.
Signed-off-by: Tom Zanussi <tzan...@gmail.com>
---
tools/perf/builtin-record.c | 16 ++++++
tools/perf/util/event.h | 7 +++
tools/perf/util/header.c | 53 +++++++++++++++++++++
tools/perf/util/header.h | 6 ++
tools/perf/util/session.c | 14 ++++++
tools/perf/util/session.h | 3 +-
tools/perf/util/trace-event-info.c | 24 ++++++++++
tools/perf/util/trace-event-read.c | 89 ++++++++++++++++++------------------
tools/perf/util/trace-event.h | 4 +-
9 files changed, 170 insertions(+), 46 deletions(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 0e3d4cd..95bccdc 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -101,6 +101,11 @@ static void mmap_write_tail(struct mmap_data *md, unsigned long tail)
pc->data_tail = tail;
}
+static void advance_output(size_t size)
+{
+ bytes_written += size;
+}
+
static void write_output(void *buf, size_t size)
{
while (size) {
@@ -581,6 +586,17 @@ static int __cmd_record(int argc, const char **argv)
pr_err("Couldn't synthesize event_types.\n");
return err;
}
+
+ err = event__synthesize_tracing_data(output, attrs,
+ nr_counters,
+ process_synthesized_event,
+ session);
+ if (err <= 0) {
+ pr_err("Couldn't record tracing data.\n");
+ return err;
+ }
+
+ advance_output(err);
}
err = event__synthesize_kernel_mmap(process_synthesized_event,
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index dc6d27b..c111358 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -86,6 +86,7 @@ struct build_id_event {
enum perf_header_event_type { /* above any possible kernel type */
PERF_RECORD_HEADER_ATTR = 64,
PERF_RECORD_HEADER_EVENT_TYPE = 65,
+ PERF_RECORD_HEADER_TRACING_DATA = 66,
PERF_RECORD_HEADER_MAX
};
@@ -107,6 +108,11 @@ struct event_type_event {
struct perf_trace_event_type event_type;
};
+struct tracing_data_event {
+ struct perf_event_header header;
+ u32 size;
+};
+
typedef union event_union {
struct perf_event_header header;
struct ip_event ip;
@@ -118,6 +124,7 @@ typedef union event_union {
struct sample_event sample;
struct attr_event attr;
struct event_type_event event_type;
+ struct tracing_data_event tracing_data;
} event_t;
struct events_stats {
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index cb75e66..5910aee 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -708,6 +708,7 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
perf_event_ops__fill_stop(&header_ops);
header_ops.attr = event__process_attr;
header_ops.event_type = event__process_event_type;
+ header_ops.tracing_data = event__process_tracing_data;
session->fd = fd;
return perf_session__process_events(session, &header_ops);
@@ -954,3 +955,55 @@ int event__process_event_type(event_t *self,
return 0;
}
+
+int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs,
+ int nb_events,
+ event__handler_t process,
+ struct perf_session *session __unused)
+{
+ event_t ev;
+ ssize_t size = 0, aligned_size = 0, padding;
+ int err = 0;
+
+ memset(&ev, 0, sizeof(ev));
+
+ ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
+ size = read_tracing_data_size(fd, pattrs, nb_events);
+ if (size <= 0)
+ return size;
+ aligned_size = ALIGN(size, sizeof(u64));
+ padding = aligned_size - size;
+ ev.tracing_data.header.size = sizeof(ev.tracing_data);
+ ev.tracing_data.size = aligned_size;
+
+ process(&ev, session);
+
+ err = read_tracing_data(fd, pattrs, nb_events);
+ write_padded(fd, NULL, 0, padding);
+
+ return aligned_size;
+}
+
+int event__process_tracing_data(event_t *self,
+ struct perf_session *session)
+{
+ ssize_t size_read, padding, size = self->tracing_data.size;
+ off_t offset = lseek(session->fd, 0, SEEK_CUR);
+ char buf[BUFSIZ];
+
+ /* setup for reading amidst mmap */
+ lseek(session->fd, offset + sizeof(struct tracing_data_event),
+ SEEK_SET);
+
+ size_read = trace_report(session->fd);
+
+ padding = ALIGN(size_read, sizeof(u64)) - size_read;
+
+ if (read(session->fd, buf, padding) < 0)
+ die("reading input file");
+
+ if (size_read + padding != size)
+ die("tracing data size mismatch");
+
+ return size_read + padding;
+}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 9d18c9b..a83f375 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -111,5 +111,11 @@ int event__synthesize_event_types(event__handler_t process,
int event__process_event_type(event_t *self,
struct perf_session *session);
+int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs,
+ int nb_events,
+ event__handler_t process,
+ struct perf_session *session);
+int event__process_tracing_data(event_t *self,
+ struct perf_session *session);
#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index d704bdf..74e9646 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -213,6 +213,8 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
handler->attr = process_event_stub;
if (handler->event_type == NULL)
handler->event_type = process_event_stub;
+ if (handler->tracing_data == NULL)
+ handler->tracing_data = process_event_stub;
}
static int process_event_stop(event_t *event __used,
@@ -235,6 +237,7 @@ void perf_event_ops__fill_stop(struct perf_event_ops *handler)
handler->unthrottle = process_event_stop;
handler->attr = process_event_stop;
handler->event_type = process_event_stop;
+ handler->tracing_data = process_event_stop;
}
static const char *event__name[] = {
@@ -250,6 +253,7 @@ static const char *event__name[] = {
[PERF_RECORD_SAMPLE] = "SAMPLE",
[PERF_RECORD_HEADER_ATTR] = "ATTR",
[PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE",
+ [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
};
unsigned long event__total[PERF_RECORD_HEADER_MAX];
@@ -342,6 +346,11 @@ static void event__event_type_swap(event_t *self)
bswap_64(self->event_type.event_type.event_id);
}
+static void event__tracing_data_swap(event_t *self)
+{
+ self->tracing_data.size = bswap_32(self->tracing_data.size);
+}
+
typedef void (*event__swap_op)(event_t *self);
static event__swap_op event__swap_ops[] = {
@@ -354,6 +363,7 @@ static event__swap_op event__swap_ops[] = {
[PERF_RECORD_SAMPLE] = event__all64_swap,
[PERF_RECORD_HEADER_ATTR] = event__attr_swap,
[PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap,
+ [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap,
[PERF_RECORD_HEADER_MAX] = NULL,
};
@@ -398,6 +408,10 @@ static int perf_session__process_event(struct perf_session *self,
return ops->attr(event, self);
case PERF_RECORD_HEADER_EVENT_TYPE:
return ops->event_type(event, self);
+ case PERF_RECORD_HEADER_TRACING_DATA:
+ /* setup for reading amidst mmap */
+ lseek(self->fd, offset + head, SEEK_SET);
+ return ops->tracing_data(event, self);
default:
self->unknown_events++;
return -1;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index ea9eb4a..a648af0 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -45,7 +45,8 @@ struct perf_event_ops {
throttle,
unthrottle,
attr,
- event_type;
+ event_type,
+ tracing_data;
};
/*
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 5ea8973..30cd9b5 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -154,10 +154,17 @@ static void put_tracing_file(char *file)
free(file);
}
+static ssize_t calc_data_size;
+
static ssize_t write_or_die(const void *buf, size_t len)
{
int ret;
+ if (calc_data_size) {
+ calc_data_size += len;
+ return len;
+ }
+
ret = write(output_fd, buf, len);
if (ret < 0)
die("writing to '%s'", output_file);
@@ -526,3 +533,20 @@ int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events)
return 0;
}
+
+ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs,
+ int nb_events)
+{
+ ssize_t size;
+ int err = 0;
+
+ calc_data_size = 1;
+ err = read_tracing_data(fd, pattrs, nb_events);
+ size = calc_data_size - 1;
+ calc_data_size = 0;
+
+ if (err < 0)
+ return err;
+
+ return size;
+}
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c
index 7cd1193..44889c9 100644
--- a/tools/perf/util/trace-event-read.c
+++ b/tools/perf/util/trace-event-read.c
@@ -50,14 +50,37 @@ static int long_size;
static unsigned long page_size;
+static ssize_t calc_data_size;
+
+static int do_read(int fd, void *buf, int size)
+{
+ int rsize = size;
+
+ while (size) {
+ int ret = read(fd, buf, size);
+
+ if (ret <= 0)
+ return -1;
+
+ size -= ret;
+ buf += ret;
+ }
+
+ return rsize;
+}
+
static int read_or_die(void *data, int size)
{
int r;
- r = read(input_fd, data, size);
- if (r != size)
+ r = do_read(input_fd, data, size);
+ if (r <= 0)
die("reading input file (size expected=%d received=%d)",
size, r);
+
+ if (calc_data_size)
+ calc_data_size += r;
+
return r;
}
@@ -82,56 +105,28 @@ static char *read_string(void)
char buf[BUFSIZ];
char *str = NULL;
int size = 0;
- int i;
off_t r;
+ char c;
for (;;) {
- r = read(input_fd, buf, BUFSIZ);
+ r = read(input_fd, &c, 1);
if (r < 0)
die("reading input file");
if (!r)
die("no data");
- for (i = 0; i < r; i++) {
- if (!buf[i])
- break;
- }
- if (i < r)
- break;
+ buf[size++] = c;
- if (str) {
- size += BUFSIZ;
- str = realloc(str, size);
- if (!str)
- die("malloc of size %d", size);
- memcpy(str + (size - BUFSIZ), buf, BUFSIZ);
- } else {
- size = BUFSIZ;
- str = malloc_or_die(size);
- memcpy(str, buf, size);
- }
+ if (!c)
+ break;
}
- /* trailing \0: */
- i++;
-
- /* move the file descriptor to the end of the string */
- r = lseek(input_fd, -(r - i), SEEK_CUR);
- if (r == (off_t)-1)
- die("lseek");
-
- if (str) {
- size += i;
- str = realloc(str, size);
- if (!str)
- die("malloc of size %d", size);
- memcpy(str + (size - i), buf, i);
- } else {
- size = i;
- str = malloc_or_die(i);
- memcpy(str, buf, i);
- }
+ if (calc_data_size)
+ calc_data_size += size;
+
+ str = malloc_or_die(size);
+ memcpy(str, buf, size);
return str;
}
@@ -459,7 +454,7 @@ struct record *trace_read_data(int cpu)
return data;
}
-void trace_report(int fd)
+ssize_t trace_report(int fd)
{
char buf[BUFSIZ];
char test[] = { 23, 8, 68 };
@@ -467,6 +462,9 @@ void trace_report(int fd)
int show_version = 0;
int show_funcs = 0;
int show_printk = 0;
+ ssize_t size;
+
+ calc_data_size = 1;
input_fd = fd;
@@ -499,14 +497,17 @@ void trace_report(int fd)
read_proc_kallsyms();
read_ftrace_printk();
+ size = calc_data_size - 1;
+ calc_data_size = 0;
+
if (show_funcs) {
print_funcs();
- return;
+ return size;
}
if (show_printk) {
print_printk();
- return;
+ return size;
}
- return;
+ return size;
}
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index c3269b9..0cb5866 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -162,7 +162,7 @@ struct record *trace_read_data(int cpu);
void parse_set_info(int nr_cpus, int long_sz);
-void trace_report(int fd);
+ssize_t trace_report(int fd);
void *malloc_or_die(unsigned int size);
@@ -258,6 +258,8 @@ void *raw_field_ptr(struct event *event, const char *name, void *data);
unsigned long long eval_flag(const char *flag);
int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events);
+ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs,
+ int nb_events);
/* taken from kernel/trace/trace.h */
enum trace_flag_type {
- adds pipe-specific versions of the header read/write code
- adds pipe-specific version of the event processing code
- adds a range of event types to be used for header or other pseudo
events, above the range used by the kernel
- adds code to interpret return values >0 from event processing
functions as 'actions'. The first action defined is for stopping
the event-processing loop at the first non-header event. So
initially there's still a restriction that header events come first
in the event stream.
Note that none of this affects the existing perf data file format or
processing - this code only comes into play if perf output is sent to
stdout (or is read from stdin).
Signed-off-by: Tom Zanussi <tzan...@gmail.com>
---
tools/perf/builtin-record.c | 8 ++-
tools/perf/builtin-report.c | 10 +++
tools/perf/builtin-trace.c | 11 +++
tools/perf/util/event.h | 4 +
tools/perf/util/header.c | 62 ++++++++++++++++++-
tools/perf/util/header.h | 8 ++-
tools/perf/util/session.c | 145 +++++++++++++++++++++++++++++++++++++++---
tools/perf/util/session.h | 16 +++++
8 files changed, 249 insertions(+), 15 deletions(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 4fe87e3..b676689 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -475,7 +475,7 @@ static int __cmd_record(int argc, const char **argv)
}
if (!file_new) {
- err = perf_header__read(&session->header, output);
+ err = perf_header__read(session, output);
if (err < 0)
return err;
}
@@ -554,7 +554,11 @@ static int __cmd_record(int argc, const char **argv)
open_counters(i, target_pid);
}
- if (file_new) {
+ if (pipe_output) {
+ err = perf_header__write_pipe(&session->header, output);
+ if (err < 0)
+ return err;
+ } else if (file_new) {
err = perf_header__write(&session->header, output, false);
if (err < 0)
return err;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index ca12efa..603f705 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -193,11 +193,21 @@ static struct perf_event_ops event_ops = {
.read = process_read_event,
};
+extern volatile int done;
+
+static void sig_handler(int sig __attribute__((__unused__)))
+{
+ done = 1;
+}
+
static int __cmd_report(void)
{
int ret = -EINVAL;
struct perf_session *session;
+ signal(SIGCHLD, sig_handler);
+ signal(SIGINT, sig_handler);
+
session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL)
return -ENOMEM;
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 407041d..2a56dda 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -106,8 +106,18 @@ static struct perf_event_ops event_ops = {
.comm = event__process_comm,
};
+extern volatile int done;
+
+static void sig_handler(int sig __unused)
+{
+ done = 1;
+}
+
static int __cmd_trace(struct perf_session *session)
{
+ signal(SIGCHLD, sig_handler);
+ signal(SIGINT, sig_handler);
+
return perf_session__process_events(session, &event_ops);
}
@@ -609,6 +619,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
return -1;
}
+ perf_header__read(session, input);
err = scripting_ops->generate_script("perf-trace");
goto out;
}
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 50a7132..cec3067 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -83,6 +83,10 @@ struct build_id_event {
char filename[];
};
+enum perf_header_event_type { /* above any possible kernel type */
+ PERF_RECORD_HEADER_MAX = 64,
+};
+
typedef union event_union {
struct perf_event_header header;
struct ip_event ip;
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 6c9aa16..75b8a4c 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -427,6 +427,26 @@ out_free:
return err;
}
+int perf_header__write_pipe(struct perf_header *self, int fd)
+{
+ struct perf_file_header_pipe f_header;
+ int err;
+
+ f_header = (struct perf_file_header_pipe){
+ .magic = PERF_MAGIC,
+ .size = sizeof(f_header),
+ };
+
+ err = do_write(fd, &f_header, sizeof(f_header));
+ if (err < 0) {
+ pr_debug("failed to write perf pipe header\n");
+ return err;
+ }
+
+ self->frozen = 1;
+ return 0;
+}
+
int perf_header__write(struct perf_header *self, int fd, bool at_exit)
{
struct perf_file_header f_header;
@@ -662,13 +682,53 @@ static int perf_file_section__process(struct perf_file_section *self,
return 0;
}
-int perf_header__read(struct perf_header *self, int fd)
+static int perf_file_header__read_pipe(struct perf_file_header_pipe *self,
+ struct perf_header *ph, int fd)
+{
+ if (do_read(fd, self, sizeof(*self)) ||
+ memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
+ return -1;
+
+ if (self->size != sizeof(*self)) {
+ u64 size = bswap_64(self->size);
+
+ if (size != sizeof(*self))
+ return -1;
+
+ ph->needs_swap = true;
+ }
+
+ return 0;
+}
+
+static int perf_header__read_pipe(struct perf_session *session, int fd)
{
+ struct perf_header *self = &session->header;
+ struct perf_file_header_pipe f_header;
+ struct perf_event_ops header_ops;
+
+ if (perf_file_header__read_pipe(&f_header, self, fd) < 0) {
+ pr_debug("incompatible file format\n");
+ return -EINVAL;
+ }
+
+ perf_event_ops__fill_stop(&header_ops);
+ session->fd = fd;
+
+ return perf_session__process_events(session, &header_ops);
+}
+
+int perf_header__read(struct perf_session *session, int fd)
+{
+ struct perf_header *self = &session->header;
struct perf_file_header f_header;
struct perf_file_attr f_attr;
u64 f_id;
int nr_attrs, nr_ids, i, j;
+ if (session->fd_pipe)
+ return perf_header__read_pipe(session, fd);
+
if (perf_file_header__read(&f_header, self, fd) < 0) {
pr_debug("incompatible file format\n");
return -EINVAL;
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 82a6af7..714b1d5 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -39,6 +39,11 @@ struct perf_file_header {
DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
};
+struct perf_file_header_pipe {
+ u64 magic;
+ u64 size;
+};
+
struct perf_header;
int perf_file_header__read(struct perf_file_header *self,
@@ -60,8 +65,9 @@ struct perf_header {
int perf_header__init(struct perf_header *self);
void perf_header__exit(struct perf_header *self);
-int perf_header__read(struct perf_header *self, int fd);
+int perf_header__read(struct perf_session *session, int fd);
int perf_header__write(struct perf_header *self, int fd, bool at_exit);
+int perf_header__write_pipe(struct perf_header *self, int fd);
int perf_header__add_attr(struct perf_header *self,
struct perf_header_attr *attr);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index dd1e923..3a3db33 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -18,7 +18,7 @@ static int perf_session__open(struct perf_session *self, bool force)
self->fd_pipe = true;
self->fd = STDIN_FILENO;
- if (perf_header__read(&self->header, self->fd) < 0)
+ if (perf_header__read(self, self->fd) < 0)
pr_err("incompatible file format");
return 0;
@@ -48,12 +48,13 @@ static int perf_session__open(struct perf_session *self, bool force)
goto out_close;
}
- if (perf_header__read(&self->header, self->fd) < 0) {
+ self->size = input_stat.st_size;
+
+ if (perf_header__read(self, self->fd) < 0) {
pr_err("incompatible file format");
goto out_close;
}
- self->size = input_stat.st_size;
return 0;
out_close:
@@ -67,6 +68,11 @@ static inline int perf_session__create_kernel_maps(struct perf_session *self)
return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps);
}
+void perf_session__update_sample_type(struct perf_session *self)
+{
+ self->sample_type = perf_header__sample_type(&self->header);
+}
+
struct perf_session *perf_session__new(const char *filename, int mode, bool force)
{
size_t len = filename ? strlen(filename) + 1 : 0;
@@ -99,7 +105,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
goto out_delete;
}
- self->sample_type = perf_header__sample_type(&self->header);
+ perf_session__update_sample_type(self);
out:
return self;
out_free:
@@ -205,6 +211,26 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
handler->unthrottle = process_event_stub;
}
+static int process_event_stop(event_t *event __used,
+ struct perf_session *session __used)
+{
+ dump_printf(": stop!\n");
+ return PERF_PROCESS_EVENT_STOP;
+}
+
+void perf_event_ops__fill_stop(struct perf_event_ops *handler)
+{
+ handler->sample = process_event_stop;
+ handler->mmap = process_event_stop;
+ handler->comm = process_event_stop;
+ handler->fork = process_event_stop;
+ handler->exit = process_event_stop;
+ handler->lost = process_event_stop;
+ handler->read = process_event_stop;
+ handler->throttle = process_event_stop;
+ handler->unthrottle = process_event_stop;
+}
+
static const char *event__name[] = {
[0] = "TOTAL",
[PERF_RECORD_MMAP] = "MMAP",
@@ -218,14 +244,17 @@ static const char *event__name[] = {
[PERF_RECORD_SAMPLE] = "SAMPLE",
};
-unsigned long event__total[PERF_RECORD_MAX];
+unsigned long event__total[PERF_RECORD_HEADER_MAX];
void event__print_totals(void)
{
int i;
- for (i = 0; i < PERF_RECORD_MAX; ++i)
+ for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
+ if (!event__name[i])
+ continue;
pr_info("%10s events: %10ld\n",
event__name[i], event__total[i]);
+ }
}
void mem_bswap_64(void *src, int byte_size)
@@ -289,7 +318,7 @@ static event__swap_op event__swap_ops[] = {
[PERF_RECORD_LOST] = event__all64_swap,
[PERF_RECORD_READ] = event__read_swap,
[PERF_RECORD_SAMPLE] = event__all64_swap,
- [PERF_RECORD_MAX] = NULL,
+ [PERF_RECORD_HEADER_MAX] = NULL,
};
static int perf_session__process_event(struct perf_session *self,
@@ -299,7 +328,7 @@ static int perf_session__process_event(struct perf_session *self,
{
trace_event(event);
- if (event->header.type < PERF_RECORD_MAX) {
+ if (event->header.type < PERF_RECORD_HEADER_MAX) {
dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
offset + head, event->header.size,
event__name[event->header.type]);
@@ -394,6 +423,96 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
return thread;
}
+static int do_read(int fd, void *buf, size_t size)
+{
+ while (size) {
+ int ret = read(fd, buf, size);
+
+ if (ret <= 0)
+ return -1;
+
+ size -= ret;
+ buf += ret;
+ }
+
+ return 0;
+}
+
+#define is_done() (*(volatile int *)(&done))
+volatile int done;
+
+static int __perf_session__process_events_pipe(struct perf_session *self,
+ struct perf_event_ops *ops)
+{
+ int action = 0;
+ event_t event;
+ uint32_t size;
+ u64 head;
+ int err;
+ void *p;
+
+ perf_event_ops__fill_defaults(ops);
+
+ head = 0;
+more:
+ err = do_read(self->fd, &event, sizeof(struct perf_event_header));
+ if (err) {
+ pr_err("failed to read event header\n");
+ goto out_err;
+ }
+
+ if (self->header.needs_swap)
+ perf_event_header__bswap(&event.header);
+
+ size = event.header.size;
+ if (size == 0)
+ size = 8;
+
+ p = &event;
+ p += sizeof(struct perf_event_header);
+
+ err = do_read(self->fd, p, size - sizeof(struct perf_event_header));
+ if (err) {
+ pr_err("failed to read event payload\n");
+ goto out_err;
+ }
+
+ if (size == 0 ||
+ (action = perf_session__process_event(self, &event, ops,
+ 0, head)) < 0) {
+ dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
+ head, event.header.size, event.header.type);
+ /*
+ * assume we lost track of the stream, check alignment, and
+ * increment a single u64 in the hope to catch on again 'soon'.
+ */
+ if (unlikely(head & 7))
+ head &= ~7ULL;
+
+ size = 8;
+ }
+
+ head += size;
+
+ dump_printf("\n%#Lx [%#x]: event: %d\n",
+ head, event.header.size, event.header.type);
+
+ if (action == PERF_PROCESS_EVENT_STOP) {
+ self->header.data_offset = head;
+ goto done;
+ }
+
+ if (action >= PERF_PROCESS_EVENT_MAX)
+ head += action;
+
+ if (!is_done())
+ goto more;
+done:
+ err = 0;
+out_err:
+ return err;
+}
+
int __perf_session__process_events(struct perf_session *self,
u64 data_offset, u64 data_size,
u64 file_size, struct perf_event_ops *ops)
@@ -511,9 +630,13 @@ out_getcwd_err:
self->cwdlen = strlen(self->cwd);
}
- err = __perf_session__process_events(self, self->header.data_offset,
- self->header.data_size,
- self->size, ops);
+ if (!self->fd_pipe)
+ err = __perf_session__process_events(self,
+ self->header.data_offset,
+ self->header.data_size,
+ self->size, ops);
+ else
+ err = __perf_session__process_events_pipe(self, ops);
out_err:
return err;
}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index dbfb74a..ab1fcbf 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -46,6 +46,18 @@ struct perf_event_ops {
unthrottle;
};
+/*
+ * interpret process_event return values as actions.
+ * PERF_PROCESS_EVENT_MAX or greater is interpreted as 'skip ahead
+ * that many bytes'. Since that can never be less than
+ * sizeof(perf_event_header), 1 through sizeof(perf_event_header) can
+ * be interpreted as 'actions'.
+*/
+enum perf_process_event_action {
+ PERF_PROCESS_EVENT_STOP = 1,
+ PERF_PROCESS_EVENT_MAX
+};
+
struct perf_session *perf_session__new(const char *filename, int mode, bool force);
void perf_session__delete(struct perf_session *self);
@@ -86,4 +98,8 @@ static inline struct map *
{
return map_groups__new_module(&self->kmaps, start, filename);
}
+
+void perf_session__update_sample_type(struct perf_session *self);
+void perf_event_ops__fill_stop(struct perf_event_ops *handler);
+
#endif /* __PERF_SESSION_H */
Making the attrs into events allows them to be streamed over a pipe
along with the rest of the header data (in later patches). It also
paves the way to allowing events to be added and removed from perf
sessions dynamically.
Signed-off-by: Tom Zanussi <tzan...@gmail.com>
---
tools/perf/builtin-record.c | 10 +++++
tools/perf/util/event.h | 10 +++++-
tools/perf/util/header.c | 80 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/header.h | 8 ++++
tools/perf/util/session.c | 27 ++++++++++++++
tools/perf/util/session.h | 3 +-
6 files changed, 136 insertions(+), 2 deletions(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index b676689..3ba466f 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -566,6 +566,16 @@ static int __cmd_record(int argc, const char **argv)
post_processing_offset = lseek(output, 0, SEEK_CUR);
+ if (pipe_output) {
+ err = event__synthesize_attrs(&session->header,
+ process_synthesized_event,
+ session);
+ if (err < 0) {
+ pr_err("Couldn't synthesize attrs.\n");
+ return err;
+ }
+ }
+
err = event__synthesize_kernel_mmap(process_synthesized_event,
session, "_text");
if (err < 0) {
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index cec3067..83e5d08 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -84,7 +84,14 @@ struct build_id_event {
};
enum perf_header_event_type { /* above any possible kernel type */
- PERF_RECORD_HEADER_MAX = 64,
+ PERF_RECORD_HEADER_ATTR = 64,
+ PERF_RECORD_HEADER_MAX
+};
+
+struct attr_event {
+ struct perf_event_header header;
+ struct perf_event_attr attr;
+ u64 id[];
};
typedef union event_union {
@@ -96,6 +103,7 @@ typedef union event_union {
struct lost_event lost;
struct read_event read;
struct sample_event sample;
+ struct attr_event attr;
} event_t;
struct events_stats {
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 75b8a4c..6397ad9 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -713,6 +713,7 @@ static int perf_header__read_pipe(struct perf_session *session, int fd)
}
perf_event_ops__fill_stop(&header_ops);
+ header_ops.attr = event__process_attr;
session->fd = fd;
return perf_session__process_events(session, &header_ops);
@@ -825,3 +826,82 @@ perf_header__find_attr(u64 id, struct perf_header *header)
return NULL;
}
+
+int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ event_t *ev;
+ size_t size;
+ int err;
+
+ size = sizeof(struct perf_event_attr);
+ size = ALIGN(size, sizeof(u64));
+ size += sizeof(struct perf_event_header);
+ size += ids * sizeof(u64);
+
+ ev = malloc(size);
+
+ ev->attr.attr = *attr;
+ memcpy(ev->attr.id, id, ids * sizeof(u64));
+
+ ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
+ ev->attr.header.size = size;
+
+ err = process(ev, session);
+
+ free(ev);
+
+ return err;
+}
+
+int event__synthesize_attrs(struct perf_header *self,
+ event__handler_t process,
+ struct perf_session *session)
+{
+ struct perf_header_attr *attr;
+ int i, err = 0;
+
+ for (i = 0; i < self->attrs; i++) {
+ attr = self->attr[i];
+
+ err = event__synthesize_attr(&attr->attr, attr->ids, attr->id,
+ process, session);
+ if (err) {
+ pr_debug("failed to create perf header attribute\n");
+ return err;
+ }
+ }
+
+ return err;
+}
+
+int event__process_attr(event_t *self, struct perf_session *session)
+{
+ struct perf_header_attr *attr;
+ unsigned int i, ids, n_ids;
+
+ attr = perf_header_attr__new(&self->attr.attr);
+ if (attr == NULL)
+ return -ENOMEM;
+
+ ids = self->header.size;
+ ids -= (void *)&self->attr.id - (void *)self;
+ n_ids = ids / sizeof(u64);
+
+ for (i = 0; i < n_ids; i++) {
+ if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+ }
+
+ if (perf_header__add_attr(&session->header, attr) < 0) {
+ perf_header_attr__delete(attr);
+ return -ENOMEM;
+ }
+
+ perf_session__update_sample_type(session);
+
+ return 0;
+}
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 714b1d5..f5fd82c 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -95,4 +95,12 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
const char *name, bool is_kallsyms);
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
+int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
+ event__handler_t process,
+ struct perf_session *session);
+int event__synthesize_attrs(struct perf_header *self,
+ event__handler_t process,
+ struct perf_session *session);
+int event__process_attr(event_t *self, struct perf_session *session);
+
#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 3a3db33..ff51105 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -209,6 +209,8 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
handler->throttle = process_event_stub;
if (handler->unthrottle == NULL)
handler->unthrottle = process_event_stub;
+ if (handler->attr == NULL)
+ handler->attr = process_event_stub;
}
static int process_event_stop(event_t *event __used,
@@ -229,6 +231,7 @@ void perf_event_ops__fill_stop(struct perf_event_ops *handler)
handler->read = process_event_stop;
handler->throttle = process_event_stop;
handler->unthrottle = process_event_stop;
+ handler->attr = process_event_stop;
}
static const char *event__name[] = {
@@ -242,6 +245,7 @@ static const char *event__name[] = {
[PERF_RECORD_FORK] = "FORK",
[PERF_RECORD_READ] = "READ",
[PERF_RECORD_SAMPLE] = "SAMPLE",
+ [PERF_RECORD_HEADER_ATTR] = "ATTR",
};
unsigned long event__total[PERF_RECORD_HEADER_MAX];
@@ -308,6 +312,26 @@ static void event__read_swap(event_t *self)
self->read.id = bswap_64(self->read.id);
}
+static void event__attr_swap(event_t *self)
+{
+ size_t size;
+
+ self->attr.attr.type = bswap_32(self->attr.attr.type);
+ self->attr.attr.size = bswap_32(self->attr.attr.size);
+ self->attr.attr.config = bswap_64(self->attr.attr.config);
+ self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period);
+ self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type);
+ self->attr.attr.read_format = bswap_64(self->attr.attr.read_format);
+ self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events);
+ self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type);
+ self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr);
+ self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len);
+
+ size = self->header.size;
+ size -= (void *)&self->attr.id - (void *)self;
+ mem_bswap_64(self->attr.id, size);
+}
+
typedef void (*event__swap_op)(event_t *self);
static event__swap_op event__swap_ops[] = {
@@ -318,6 +342,7 @@ static event__swap_op event__swap_ops[] = {
[PERF_RECORD_LOST] = event__all64_swap,
[PERF_RECORD_READ] = event__read_swap,
[PERF_RECORD_SAMPLE] = event__all64_swap,
+ [PERF_RECORD_HEADER_ATTR] = event__attr_swap,
[PERF_RECORD_HEADER_MAX] = NULL,
};
@@ -358,6 +383,8 @@ static int perf_session__process_event(struct perf_session *self,
return ops->throttle(event, self);
case PERF_RECORD_UNTHROTTLE:
return ops->unthrottle(event, self);
+ case PERF_RECORD_HEADER_ATTR:
+ return ops->attr(event, self);
default:
self->unknown_events++;
return -1;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index ab1fcbf..7365a24 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -43,7 +43,8 @@ struct perf_event_ops {
lost,
read,
throttle,
- unthrottle;
+ unthrottle,
+ attr;
};
/*
I actually wonder why you bother with a header in the pipe mode,
since it only contains MAGIC and its size.
> I actually wonder why you bother with a header in the pipe mode,
> since it only contains MAGIC and its size.
Because he wants to detect endianness :-)
- ARnaldo
Elaborating: piping over netcat from a big endian to a little endian
machine works because he used the ->needs_swap logic I implemented to
allow cross platform analysis.
Its just a matter of having the DSOS with matching build-ids in the
cache of the machine running 'perf report'.
- Arnaldo
Ok, thanks for the explanations :-)
> Ok, thanks for the explanations :-)
Humm, now that I read this again, the build-ids now are in the headers,
which will not be pushed as it is the last step in 'perf record', after
we stop recording events, so only if we make sure that the binaries are
the same is that we will be able to make sense of it over the network.
One more reason for the loader of DSOs to read the pre calculated
build-id ELF session to tell the kernel the 20 bytes build id that in
turn will inject it in the PERF_RECORD_MMAP event.
- Arnaldo