Merge branch 'perf/live' into perf/core
Conflicts: tools/perf/builtin-record.c Merge reason: add the live tracing feature, resolve conflict. Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Коммит
84b13fd596
|
@ -42,6 +42,7 @@ static unsigned int mmap_pages = 128;
|
|||
static unsigned int user_freq = UINT_MAX;
|
||||
static int freq = 1000;
|
||||
static int output;
|
||||
static int pipe_output = 0;
|
||||
static const char *output_name = "perf.data";
|
||||
static int group = 0;
|
||||
static unsigned int realtime_prio = 0;
|
||||
|
@ -109,6 +110,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) {
|
||||
|
@ -435,10 +441,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)
|
||||
|
@ -464,7 +479,9 @@ static int __cmd_record(int argc, const char **argv)
|
|||
exit(-1);
|
||||
}
|
||||
|
||||
if (!stat(output_name, &st) && st.st_size) {
|
||||
if (!strcmp(output_name, "-"))
|
||||
pipe_output = 1;
|
||||
else if (!stat(output_name, &st) && st.st_size) {
|
||||
if (write_mode == WRITE_FORCE) {
|
||||
char oldname[PATH_MAX];
|
||||
snprintf(oldname, sizeof(oldname), "%s.old",
|
||||
|
@ -482,7 +499,10 @@ static int __cmd_record(int argc, const char **argv)
|
|||
else
|
||||
flags |= O_TRUNC;
|
||||
|
||||
output = open(output_name, flags, S_IRUSR|S_IWUSR);
|
||||
if (pipe_output)
|
||||
output = STDOUT_FILENO;
|
||||
else
|
||||
output = open(output_name, flags, S_IRUSR | S_IWUSR);
|
||||
if (output < 0) {
|
||||
perror("failed to create output file");
|
||||
exit(-1);
|
||||
|
@ -496,7 +516,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;
|
||||
}
|
||||
|
@ -522,6 +542,8 @@ static int __cmd_record(int argc, const char **argv)
|
|||
}
|
||||
|
||||
if (!child_pid) {
|
||||
if (pipe_output)
|
||||
dup2(2, 1);
|
||||
close(child_ready_pipe[0]);
|
||||
close(go_pipe[1]);
|
||||
fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
|
||||
|
@ -573,7 +595,11 @@ static int __cmd_record(int argc, const char **argv)
|
|||
open_counters(cpumap[i]);
|
||||
}
|
||||
|
||||
if (file_new) {
|
||||
if (pipe_output) {
|
||||
err = perf_header__write_pipe(output);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else if (file_new) {
|
||||
err = perf_header__write(&session->header, output, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -581,6 +607,34 @@ 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_event_types(process_synthesized_event,
|
||||
session);
|
||||
if (err < 0) {
|
||||
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,
|
||||
session, "_text");
|
||||
if (err < 0)
|
||||
|
|
|
@ -267,8 +267,19 @@ static struct perf_event_ops event_ops = {
|
|||
.fork = event__process_task,
|
||||
.lost = event__process_lost,
|
||||
.read = process_read_event,
|
||||
.attr = event__process_attr,
|
||||
.event_type = event__process_event_type,
|
||||
.tracing_data = event__process_tracing_data,
|
||||
.build_id = event__process_build_id,
|
||||
};
|
||||
|
||||
extern volatile int session_done;
|
||||
|
||||
static void sig_handler(int sig __attribute__((__unused__)))
|
||||
{
|
||||
session_done = 1;
|
||||
}
|
||||
|
||||
static int __cmd_report(void)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
@ -276,6 +287,8 @@ static int __cmd_report(void)
|
|||
struct rb_node *next;
|
||||
const char *help = "For a higher level overview, try: perf report --sort comm,dso";
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, force);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
@ -465,7 +478,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
|||
{
|
||||
argc = parse_options(argc, argv, options, report_usage, 0);
|
||||
|
||||
setup_browser();
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
setup_browser();
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
|
|
@ -104,10 +104,23 @@ static int process_sample_event(event_t *event, struct perf_session *session)
|
|||
static struct perf_event_ops event_ops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = event__process_comm,
|
||||
.attr = event__process_attr,
|
||||
.event_type = event__process_event_type,
|
||||
.tracing_data = event__process_tracing_data,
|
||||
.build_id = event__process_build_id,
|
||||
};
|
||||
|
||||
extern volatile int session_done;
|
||||
|
||||
static void sig_handler(int sig __unused)
|
||||
{
|
||||
session_done = 1;
|
||||
}
|
||||
|
||||
static int __cmd_trace(struct perf_session *session)
|
||||
{
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
return perf_session__process_events(session, &event_ops);
|
||||
}
|
||||
|
||||
|
@ -548,6 +561,65 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
|||
suffix = REPORT_SUFFIX;
|
||||
}
|
||||
|
||||
if (!suffix && argc >= 2 && strncmp(argv[1], "-", strlen("-")) != 0) {
|
||||
char *record_script_path, *report_script_path;
|
||||
int live_pipe[2];
|
||||
pid_t pid;
|
||||
|
||||
record_script_path = get_script_path(argv[1], RECORD_SUFFIX);
|
||||
if (!record_script_path) {
|
||||
fprintf(stderr, "record script not found\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
report_script_path = get_script_path(argv[1], REPORT_SUFFIX);
|
||||
if (!report_script_path) {
|
||||
fprintf(stderr, "report script not found\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pipe(live_pipe) < 0) {
|
||||
perror("failed to create pipe");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("failed to fork");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!pid) {
|
||||
dup2(live_pipe[1], 1);
|
||||
close(live_pipe[0]);
|
||||
|
||||
__argv = malloc(5 * sizeof(const char *));
|
||||
__argv[0] = "/bin/sh";
|
||||
__argv[1] = record_script_path;
|
||||
__argv[2] = "-o";
|
||||
__argv[3] = "-";
|
||||
__argv[4] = NULL;
|
||||
|
||||
execvp("/bin/sh", (char **)__argv);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
dup2(live_pipe[0], 0);
|
||||
close(live_pipe[1]);
|
||||
|
||||
__argv = malloc((argc + 3) * sizeof(const char *));
|
||||
__argv[0] = "/bin/sh";
|
||||
__argv[1] = report_script_path;
|
||||
for (i = 2; i < argc; i++)
|
||||
__argv[i] = argv[i];
|
||||
__argv[i++] = "-i";
|
||||
__argv[i++] = "-";
|
||||
__argv[i++] = NULL;
|
||||
|
||||
execvp("/bin/sh", (char **)__argv);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (suffix) {
|
||||
script_path = get_script_path(argv[2], suffix);
|
||||
if (!script_path) {
|
||||
|
@ -580,7 +652,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used)
|
|||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!perf_session__has_traces(session, "record -R"))
|
||||
if (strcmp(input_name, "-") &&
|
||||
!perf_session__has_traces(session, "record -R"))
|
||||
return -EINVAL;
|
||||
|
||||
if (generate_script_lang) {
|
||||
|
|
|
@ -15,6 +15,7 @@ our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
|
|||
|
||||
our @EXPORT = qw(
|
||||
avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs
|
||||
clear_term
|
||||
);
|
||||
|
||||
our $VERSION = '0.01';
|
||||
|
@ -55,6 +56,11 @@ sub nsecs_str {
|
|||
return $str;
|
||||
}
|
||||
|
||||
sub clear_term
|
||||
{
|
||||
print "\x1b[H\x1b[2J";
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
||||
=head1 NAME
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit $@
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide failed syscalls
|
||||
# args: [comm]
|
||||
perf trace -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $1
|
||||
if [ $# -gt 0 ] ; then
|
||||
if ! expr match "$1" "-" ; then
|
||||
comm=$1
|
||||
shift
|
||||
fi
|
||||
fi
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $comm
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_enter_write
|
||||
perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_enter_write $@
|
||||
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
#!/bin/bash
|
||||
# description: r/w activity for a program, by file
|
||||
# args: <comm>
|
||||
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1
|
||||
if [ $# -lt 1 ] ; then
|
||||
echo "usage: rw-by-file <comm>"
|
||||
exit
|
||||
fi
|
||||
comm=$1
|
||||
shift
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $comm
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/bash
|
||||
perf record -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
|
||||
perf record -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 $@
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide r/w activity
|
||||
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
perf record -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 $@
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide r/w top
|
||||
# args: [interval]
|
||||
n_args=0
|
||||
for i in "$@"
|
||||
do
|
||||
if expr match "$i" "-" > /dev/null ; then
|
||||
break
|
||||
fi
|
||||
n_args=$(( $n_args + 1 ))
|
||||
done
|
||||
if [ "$n_args" -gt 1 ] ; then
|
||||
echo "usage: rwtop-report [interval]"
|
||||
exit
|
||||
fi
|
||||
if [ "$n_args" -gt 0 ] ; then
|
||||
interval=$1
|
||||
shift
|
||||
fi
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/perl/rwtop.pl $interval
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e sched:sched_switch -e sched:sched_wakeup
|
||||
perf record -c 1 -f -a -M -R -e sched:sched_switch -e sched:sched_wakeup $@
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide min/max/avg wakeup latency
|
||||
perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion
|
||||
perf record -c 1 -f -a -M -R -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# description: workqueue stats (ins/exe/create/destroy)
|
||||
perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
#!/usr/bin/perl -w
|
||||
# (c) 2010, Tom Zanussi <tzanussi@gmail.com>
|
||||
# Licensed under the terms of the GNU GPL License version 2
|
||||
|
||||
# read/write top
|
||||
#
|
||||
# Periodically displays system-wide r/w call activity, broken down by
|
||||
# pid. If an [interval] arg is specified, the display will be
|
||||
# refreshed every [interval] seconds. The default interval is 3
|
||||
# seconds.
|
||||
|
||||
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 $default_interval = 3;
|
||||
my $nlines = 20;
|
||||
my $print_thread;
|
||||
|
||||
my %reads;
|
||||
my %writes;
|
||||
|
||||
my $interval = shift;
|
||||
if (!$interval) {
|
||||
$interval = $default_interval;
|
||||
}
|
||||
|
||||
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 1;
|
||||
}
|
||||
|
||||
sub trace_end
|
||||
{
|
||||
print_unhandled();
|
||||
print_totals();
|
||||
}
|
||||
|
||||
sub print_totals
|
||||
{
|
||||
my $count;
|
||||
|
||||
$count = 0;
|
||||
|
||||
clear_term();
|
||||
|
||||
printf("\nread counts by pid:\n\n");
|
||||
|
||||
printf("%6s %20s %10s %10s %10s\n", "pid", "comm",
|
||||
"# reads", "bytes_req", "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 %13s\n", "pid", "comm",
|
||||
"# writes", "bytes_written");
|
||||
printf("%6s %-20s %10s %13s\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 %13s\n", $pid, $comm,
|
||||
$total_writes, $bytes_written);
|
||||
|
||||
if (++$count == $nlines) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
%reads = ();
|
||||
%writes = ();
|
||||
alarm $interval;
|
||||
}
|
||||
|
||||
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}++;
|
||||
}
|
|
@ -23,3 +23,6 @@ def nsecs_nsecs(nsecs):
|
|||
def nsecs_str(nsecs):
|
||||
str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)),
|
||||
return str
|
||||
|
||||
def clear_term():
|
||||
print("\x1b[H\x1b[2J")
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit $@
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide failed syscalls, by pid
|
||||
# args: [comm]
|
||||
perf trace -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $1
|
||||
if [ $# -gt 0 ] ; then
|
||||
if ! expr match "$1" "-" ; then
|
||||
comm=$1
|
||||
shift
|
||||
fi
|
||||
fi
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $comm
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
# description: syscall top
|
||||
# args: [comm] [interval]
|
||||
n_args=0
|
||||
for i in "$@"
|
||||
do
|
||||
if expr match "$i" "-" > /dev/null ; then
|
||||
break
|
||||
fi
|
||||
n_args=$(( $n_args + 1 ))
|
||||
done
|
||||
if [ "$n_args" -gt 2 ] ; then
|
||||
echo "usage: sctop-report [comm] [interval]"
|
||||
exit
|
||||
fi
|
||||
if [ "$n_args" -gt 1 ] ; then
|
||||
comm=$1
|
||||
interval=$2
|
||||
shift 2
|
||||
elif [ "$n_args" -gt 0 ] ; then
|
||||
interval=$1
|
||||
shift
|
||||
fi
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/python/sctop.py $comm $interval
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide syscall counts, by pid
|
||||
# args: [comm]
|
||||
perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $1
|
||||
if [ $# -gt 0 ] ; then
|
||||
if ! expr match "$1" "-" ; then
|
||||
comm=$1
|
||||
shift
|
||||
fi
|
||||
fi
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $comm
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/bash
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter
|
||||
perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter $@
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
#!/bin/bash
|
||||
# description: system-wide syscall counts
|
||||
# args: [comm]
|
||||
perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts.py $1
|
||||
if [ $# -gt 0 ] ; then
|
||||
if ! expr match "$1" "-" ; then
|
||||
comm=$1
|
||||
shift
|
||||
fi
|
||||
fi
|
||||
perf trace $@ -s ~/libexec/perf-core/scripts/python/syscall-counts.py $comm
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
# system call top
|
||||
# (c) 2010, Tom Zanussi <tzanussi@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. If an [interval] arg is specified, the display
|
||||
# will be refreshed every [interval] seconds. The default interval is
|
||||
# 3 seconds.
|
||||
|
||||
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] [interval]\n";
|
||||
|
||||
for_comm = None
|
||||
default_interval = 3
|
||||
interval = default_interval
|
||||
|
||||
if len(sys.argv) > 3:
|
||||
sys.exit(usage)
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
for_comm = sys.argv[1]
|
||||
interval = int(sys.argv[2])
|
||||
elif len(sys.argv) > 1:
|
||||
try:
|
||||
interval = int(sys.argv[1])
|
||||
except ValueError:
|
||||
for_comm = sys.argv[1]
|
||||
interval = default_interval
|
||||
|
||||
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:
|
||||
clear_term()
|
||||
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):
|
||||
try:
|
||||
print "%-40d %10d\n" % (id, val),
|
||||
except TypeError:
|
||||
pass
|
||||
syscalls.clear()
|
||||
time.sleep(interval)
|
|
@ -83,6 +83,37 @@ struct build_id_event {
|
|||
char filename[];
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
struct attr_event {
|
||||
struct perf_event_header header;
|
||||
struct perf_event_attr attr;
|
||||
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;
|
||||
};
|
||||
|
||||
struct tracing_data_event {
|
||||
struct perf_event_header header;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
typedef union event_union {
|
||||
struct perf_event_header header;
|
||||
struct ip_event ip;
|
||||
|
@ -92,6 +123,10 @@ typedef union event_union {
|
|||
struct lost_event lost;
|
||||
struct read_event read;
|
||||
struct sample_event sample;
|
||||
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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -427,6 +420,25 @@ out_free:
|
|||
return err;
|
||||
}
|
||||
|
||||
int perf_header__write_pipe(int fd)
|
||||
{
|
||||
struct perf_pipe_file_header f_header;
|
||||
int err;
|
||||
|
||||
f_header = (struct perf_pipe_file_header){
|
||||
.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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_header__write(struct perf_header *self, int fd, bool at_exit)
|
||||
{
|
||||
struct perf_file_header f_header;
|
||||
|
@ -518,25 +530,10 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit)
|
|||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int perf_header__getbuffer64(struct perf_header *self,
|
||||
int fd, void *buf, size_t size)
|
||||
{
|
||||
if (do_read(fd, buf, size))
|
||||
if (do_read(fd, buf, size) <= 0)
|
||||
return -1;
|
||||
|
||||
if (self->needs_swap)
|
||||
|
@ -592,7 +589,7 @@ int perf_file_header__read(struct perf_file_header *self,
|
|||
{
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
|
||||
if (do_read(fd, self, sizeof(*self)) ||
|
||||
if (do_read(fd, self, sizeof(*self)) <= 0 ||
|
||||
memcmp(&self->magic, __perf_magic, sizeof(self->magic)))
|
||||
return -1;
|
||||
|
||||
|
@ -662,13 +659,51 @@ 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_pipe_file_header *self,
|
||||
struct perf_header *ph, int fd)
|
||||
{
|
||||
if (do_read(fd, self, sizeof(*self)) <= 0 ||
|
||||
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_pipe_file_header f_header;
|
||||
|
||||
if (perf_file_header__read_pipe(&f_header, self, fd) < 0) {
|
||||
pr_debug("incompatible file format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
session->fd = fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -765,3 +800,279 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,11 @@ struct perf_file_header {
|
|||
DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
|
||||
};
|
||||
|
||||
struct perf_pipe_file_header {
|
||||
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(int fd);
|
||||
|
||||
int perf_header__add_attr(struct perf_header *self,
|
||||
struct perf_header_attr *attr);
|
||||
|
@ -89,4 +95,34 @@ 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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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 */
|
||||
|
|
|
@ -14,6 +14,16 @@ static int perf_session__open(struct perf_session *self, bool force)
|
|||
{
|
||||
struct stat input_stat;
|
||||
|
||||
if (!strcmp(self->filename, "-")) {
|
||||
self->fd_pipe = true;
|
||||
self->fd = STDIN_FILENO;
|
||||
|
||||
if (perf_header__read(self, self->fd) < 0)
|
||||
pr_err("incompatible file format");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
self->fd = open(self->filename, O_RDONLY);
|
||||
if (self->fd < 0) {
|
||||
pr_err("failed to open file: %s", self->filename);
|
||||
|
@ -38,7 +48,7 @@ static int perf_session__open(struct perf_session *self, bool force)
|
|||
goto out_close;
|
||||
}
|
||||
|
||||
if (perf_header__read(&self->header, self->fd) < 0) {
|
||||
if (perf_header__read(self, self->fd) < 0) {
|
||||
pr_err("incompatible file format");
|
||||
goto out_close;
|
||||
}
|
||||
|
@ -52,6 +62,11 @@ out_close:
|
|||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -85,7 +100,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:
|
||||
|
@ -185,6 +200,14 @@ 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;
|
||||
if (handler->event_type == NULL)
|
||||
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 const char *event__name[] = {
|
||||
|
@ -198,16 +221,23 @@ static const char *event__name[] = {
|
|||
[PERF_RECORD_FORK] = "FORK",
|
||||
[PERF_RECORD_READ] = "READ",
|
||||
[PERF_RECORD_SAMPLE] = "SAMPLE",
|
||||
[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_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)
|
||||
|
@ -261,6 +291,37 @@ 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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[] = {
|
||||
|
@ -271,7 +332,11 @@ 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_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,
|
||||
};
|
||||
|
||||
static int perf_session__process_event(struct perf_session *self,
|
||||
|
@ -281,7 +346,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]);
|
||||
|
@ -311,6 +376,16 @@ 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);
|
||||
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);
|
||||
case PERF_RECORD_HEADER_BUILD_ID:
|
||||
return ops->build_id(event, self);
|
||||
default:
|
||||
self->unknown_events++;
|
||||
return -1;
|
||||
|
@ -376,6 +451,101 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
|
|||
return thread;
|
||||
}
|
||||
|
||||
int do_read(int fd, void *buf, size_t size)
|
||||
{
|
||||
void *buf_start = buf;
|
||||
|
||||
while (size) {
|
||||
int ret = read(fd, buf, size);
|
||||
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
size -= ret;
|
||||
buf += ret;
|
||||
}
|
||||
|
||||
return buf - buf_start;
|
||||
}
|
||||
|
||||
#define session_done() (*(volatile int *)(&session_done))
|
||||
volatile int session_done;
|
||||
|
||||
static int __perf_session__process_pipe_events(struct perf_session *self,
|
||||
struct perf_event_ops *ops)
|
||||
{
|
||||
event_t event;
|
||||
uint32_t size;
|
||||
int skip = 0;
|
||||
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 <= 0) {
|
||||
if (err == 0)
|
||||
goto done;
|
||||
|
||||
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 <= 0) {
|
||||
if (err == 0) {
|
||||
pr_err("unexpected end of event stream\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
pr_err("failed to read event data\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (size == 0 ||
|
||||
(skip = 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 (skip > 0)
|
||||
head += skip;
|
||||
|
||||
if (!session_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)
|
||||
|
@ -499,9 +669,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_pipe_events(self, ops);
|
||||
out_err:
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ struct perf_session {
|
|||
u64 sample_type;
|
||||
struct ref_reloc_sym ref_reloc_sym;
|
||||
int fd;
|
||||
bool fd_pipe;
|
||||
int cwdlen;
|
||||
char *cwd;
|
||||
char filename[0];
|
||||
|
@ -43,7 +44,11 @@ struct perf_event_ops {
|
|||
lost,
|
||||
read,
|
||||
throttle,
|
||||
unthrottle;
|
||||
unthrottle,
|
||||
attr,
|
||||
event_type,
|
||||
tracing_data,
|
||||
build_id;
|
||||
};
|
||||
|
||||
struct perf_session *perf_session__new(const char *filename, int mode, bool force);
|
||||
|
@ -92,6 +97,9 @@ static inline struct map *
|
|||
return map_groups__new_module(&self->kmaps, start, filename);
|
||||
}
|
||||
|
||||
int do_read(int fd, void *buf, size_t size);
|
||||
void perf_session__update_sample_type(struct perf_session *self);
|
||||
|
||||
#ifdef NO_NEWT_SUPPORT
|
||||
static inline int perf_session__browse_hists(struct rb_root *hists __used,
|
||||
u64 nr_hists __used,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
buf[size++] = c;
|
||||
|
||||
if (!c)
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* trailing \0: */
|
||||
i++;
|
||||
if (calc_data_size)
|
||||
calc_data_size += size;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -163,7 +163,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);
|
||||
|
||||
|
@ -259,6 +259,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 {
|
||||
|
|
Загрузка…
Ссылка в новой задаче