perf: Rename 'perf trace' to 'perf script'
Free the perf trace name space and rename the trace to 'script' which is a better match for the scripting engine. Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
e53beacd23
commit
133dc4c39c
12 changed files with 120 additions and 120 deletions
217
tools/perf/Documentation/perf-script-perl.txt
Normal file
217
tools/perf/Documentation/perf-script-perl.txt
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
perf-script-perl(1)
|
||||
==================
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-script-perl - Process trace data with a Perl script
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf script' [-s [Perl]:script[.pl] ]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
This perf script option is used to process perf script data using perf's
|
||||
built-in Perl interpreter. It reads and processes the input file and
|
||||
displays the results of the trace analysis implemented in the given
|
||||
Perl script, if any.
|
||||
|
||||
STARTER SCRIPTS
|
||||
---------------
|
||||
|
||||
You can avoid reading the rest of this document by running 'perf script
|
||||
-g perl' in the same directory as an existing perf.data trace file.
|
||||
That will generate a starter script containing a handler for each of
|
||||
the event types in the trace file; it simply prints every available
|
||||
field for each event in the trace file.
|
||||
|
||||
You can also look at the existing scripts in
|
||||
~/libexec/perf-core/scripts/perl for typical examples showing how to
|
||||
do basic things like aggregate event data, print results, etc. Also,
|
||||
the check-perf-script.pl script, while not interesting for its results,
|
||||
attempts to exercise all of the main scripting features.
|
||||
|
||||
EVENT HANDLERS
|
||||
--------------
|
||||
|
||||
When perf script is invoked using a trace script, a user-defined
|
||||
'handler function' is called for each event in the trace. If there's
|
||||
no handler function defined for a given event type, the event is
|
||||
ignored (or passed to a 'trace_handled' function, see below) and the
|
||||
next event is processed.
|
||||
|
||||
Most of the event's field values are passed as arguments to the
|
||||
handler function; some of the less common ones aren't - those are
|
||||
available as calls back into the perf executable (see below).
|
||||
|
||||
As an example, the following perf record command can be used to record
|
||||
all sched_wakeup events in the system:
|
||||
|
||||
# perf record -a -e sched:sched_wakeup
|
||||
|
||||
Traces meant to be processed using a script should be recorded with
|
||||
the above option: -a to enable system-wide collection.
|
||||
|
||||
The format file for the sched_wakep event defines the following fields
|
||||
(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
|
||||
|
||||
----
|
||||
format:
|
||||
field:unsigned short common_type;
|
||||
field:unsigned char common_flags;
|
||||
field:unsigned char common_preempt_count;
|
||||
field:int common_pid;
|
||||
field:int common_lock_depth;
|
||||
|
||||
field:char comm[TASK_COMM_LEN];
|
||||
field:pid_t pid;
|
||||
field:int prio;
|
||||
field:int success;
|
||||
field:int target_cpu;
|
||||
----
|
||||
|
||||
The handler function for this event would be defined as:
|
||||
|
||||
----
|
||||
sub sched::sched_wakeup
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs,
|
||||
$common_nsecs, $common_pid, $common_comm,
|
||||
$comm, $pid, $prio, $success, $target_cpu) = @_;
|
||||
}
|
||||
----
|
||||
|
||||
The handler function takes the form subsystem::event_name.
|
||||
|
||||
The $common_* arguments in the handler's argument list are the set of
|
||||
arguments passed to all event handlers; some of the fields correspond
|
||||
to the common_* fields in the format file, but some are synthesized,
|
||||
and some of the common_* fields aren't common enough to to be passed
|
||||
to every event as arguments but are available as library functions.
|
||||
|
||||
Here's a brief description of each of the invariant event args:
|
||||
|
||||
$event_name the name of the event as text
|
||||
$context an opaque 'cookie' used in calls back into perf
|
||||
$common_cpu the cpu the event occurred on
|
||||
$common_secs the secs portion of the event timestamp
|
||||
$common_nsecs the nsecs portion of the event timestamp
|
||||
$common_pid the pid of the current task
|
||||
$common_comm the name of the current process
|
||||
|
||||
All of the remaining fields in the event's format file have
|
||||
counterparts as handler function arguments of the same name, as can be
|
||||
seen in the example above.
|
||||
|
||||
The above provides the basics needed to directly access every field of
|
||||
every event in a trace, which covers 90% of what you need to know to
|
||||
write a useful trace script. The sections below cover the rest.
|
||||
|
||||
SCRIPT LAYOUT
|
||||
-------------
|
||||
|
||||
Every perf script Perl script should start by setting up a Perl module
|
||||
search path and 'use'ing a few support modules (see module
|
||||
descriptions below):
|
||||
|
||||
----
|
||||
use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/perf-script-Util/lib";
|
||||
use lib "./perf-script-Util/lib";
|
||||
use Perf::Trace::Core;
|
||||
use Perf::Trace::Context;
|
||||
use Perf::Trace::Util;
|
||||
----
|
||||
|
||||
The rest of the script can contain handler functions and support
|
||||
functions in any order.
|
||||
|
||||
Aside from the event handler functions discussed above, every script
|
||||
can implement a set of optional functions:
|
||||
|
||||
*trace_begin*, if defined, is called before any event is processed and
|
||||
gives scripts a chance to do setup tasks:
|
||||
|
||||
----
|
||||
sub trace_begin
|
||||
{
|
||||
}
|
||||
----
|
||||
|
||||
*trace_end*, if defined, is called after all events have been
|
||||
processed and gives scripts a chance to do end-of-script tasks, such
|
||||
as display results:
|
||||
|
||||
----
|
||||
sub trace_end
|
||||
{
|
||||
}
|
||||
----
|
||||
|
||||
*trace_unhandled*, if defined, is called after for any event that
|
||||
doesn't have a handler explicitly defined for it. The standard set
|
||||
of common arguments are passed into it:
|
||||
|
||||
----
|
||||
sub trace_unhandled
|
||||
{
|
||||
my ($event_name, $context, $common_cpu, $common_secs,
|
||||
$common_nsecs, $common_pid, $common_comm) = @_;
|
||||
}
|
||||
----
|
||||
|
||||
The remaining sections provide descriptions of each of the available
|
||||
built-in perf script Perl modules and their associated functions.
|
||||
|
||||
AVAILABLE MODULES AND FUNCTIONS
|
||||
-------------------------------
|
||||
|
||||
The following sections describe the functions and variables available
|
||||
via the various Perf::Trace::* Perl modules. To use the functions and
|
||||
variables from the given module, add the corresponding 'use
|
||||
Perf::Trace::XXX' line to your perf script script.
|
||||
|
||||
Perf::Trace::Core Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These functions provide some essential functions to user scripts.
|
||||
|
||||
The *flag_str* and *symbol_str* functions provide human-readable
|
||||
strings for flag and symbolic fields. These correspond to the strings
|
||||
and values parsed from the 'print fmt' fields of the event format
|
||||
files:
|
||||
|
||||
flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name
|
||||
symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name
|
||||
|
||||
Perf::Trace::Context Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some of the 'common' fields in the event format file aren't all that
|
||||
common, but need to be made accessible to user scripts nonetheless.
|
||||
|
||||
Perf::Trace::Context defines a set of functions that can be used to
|
||||
access this data in the context of the current event. Each of these
|
||||
functions expects a $context variable, which is the same as the
|
||||
$context variable passed into every event handler as the second
|
||||
argument.
|
||||
|
||||
common_pc($context) - returns common_preempt count for the current event
|
||||
common_flags($context) - returns common_flags for the current event
|
||||
common_lock_depth($context) - returns common_lock_depth for the current event
|
||||
|
||||
Perf::Trace::Util Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Various utility functions for use with perf script:
|
||||
|
||||
nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair
|
||||
nsecs_secs($nsecs) - returns whole secs portion given nsecs
|
||||
nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs
|
||||
nsecs_str($nsecs) - returns printable string in the form secs.nsecs
|
||||
avg($total, $n) - returns average given a sum and a total number of values
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-script[1]
|
||||
623
tools/perf/Documentation/perf-script-python.txt
Normal file
623
tools/perf/Documentation/perf-script-python.txt
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
perf-script-python(1)
|
||||
====================
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-script-python - Process trace data with a Python script
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf script' [-s [Python]:script[.py] ]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
||||
This perf script option is used to process perf script data using perf's
|
||||
built-in Python interpreter. It reads and processes the input file and
|
||||
displays the results of the trace analysis implemented in the given
|
||||
Python script, if any.
|
||||
|
||||
A QUICK EXAMPLE
|
||||
---------------
|
||||
|
||||
This section shows the process, start to finish, of creating a working
|
||||
Python script that aggregates and extracts useful information from a
|
||||
raw perf script stream. You can avoid reading the rest of this
|
||||
document if an example is enough for you; the rest of the document
|
||||
provides more details on each step and lists the library functions
|
||||
available to script writers.
|
||||
|
||||
This example actually details the steps that were used to create the
|
||||
'syscall-counts' script you see when you list the available perf script
|
||||
scripts via 'perf script -l'. As such, this script also shows how to
|
||||
integrate your script into the list of general-purpose 'perf script'
|
||||
scripts listed by that command.
|
||||
|
||||
The syscall-counts script is a simple script, but demonstrates all the
|
||||
basic ideas necessary to create a useful script. Here's an example
|
||||
of its output (syscall names are not yet supported, they will appear
|
||||
as numbers):
|
||||
|
||||
----
|
||||
syscall events:
|
||||
|
||||
event count
|
||||
---------------------------------------- -----------
|
||||
sys_write 455067
|
||||
sys_getdents 4072
|
||||
sys_close 3037
|
||||
sys_swapoff 1769
|
||||
sys_read 923
|
||||
sys_sched_setparam 826
|
||||
sys_open 331
|
||||
sys_newfstat 326
|
||||
sys_mmap 217
|
||||
sys_munmap 216
|
||||
sys_futex 141
|
||||
sys_select 102
|
||||
sys_poll 84
|
||||
sys_setitimer 12
|
||||
sys_writev 8
|
||||
15 8
|
||||
sys_lseek 7
|
||||
sys_rt_sigprocmask 6
|
||||
sys_wait4 3
|
||||
sys_ioctl 3
|
||||
sys_set_robust_list 1
|
||||
sys_exit 1
|
||||
56 1
|
||||
sys_access 1
|
||||
----
|
||||
|
||||
Basically our task is to keep a per-syscall tally that gets updated
|
||||
every time a system call occurs in the system. Our script will do
|
||||
that, but first we need to record the data that will be processed by
|
||||
that script. Theoretically, there are a couple of ways we could do
|
||||
that:
|
||||
|
||||
- we could enable every event under the tracing/events/syscalls
|
||||
directory, but this is over 600 syscalls, well beyond the number
|
||||
allowable by perf. These individual syscall events will however be
|
||||
useful if we want to later use the guidance we get from the
|
||||
general-purpose scripts to drill down and get more detail about
|
||||
individual syscalls of interest.
|
||||
|
||||
- we can enable the sys_enter and/or sys_exit syscalls found under
|
||||
tracing/events/raw_syscalls. These are called for all syscalls; the
|
||||
'id' field can be used to distinguish between individual syscall
|
||||
numbers.
|
||||
|
||||
For this script, we only need to know that a syscall was entered; we
|
||||
don't care how it exited, so we'll use 'perf record' to record only
|
||||
the sys_enter events:
|
||||
|
||||
----
|
||||
# perf record -a -e raw_syscalls:sys_enter
|
||||
|
||||
^C[ perf record: Woken up 1 times to write data ]
|
||||
[ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ]
|
||||
----
|
||||
|
||||
The options basically say to collect data for every syscall event
|
||||
system-wide and multiplex the per-cpu output into a single stream.
|
||||
That single stream will be recorded in a file in the current directory
|
||||
called perf.data.
|
||||
|
||||
Once we have a perf.data file containing our data, we can use the -g
|
||||
'perf script' option to generate a Python script that will contain a
|
||||
callback handler for each event type found in the perf.data trace
|
||||
stream (for more details, see the STARTER SCRIPTS section).
|
||||
|
||||
----
|
||||
# perf script -g python
|
||||
generated Python script: perf-script.py
|
||||
|
||||
The output file created also in the current directory is named
|
||||
perf-script.py. Here's the file in its entirety:
|
||||
|
||||
# perf script event handlers, generated by perf script -g python
|
||||
# Licensed under the terms of the GNU GPL License version 2
|
||||
|
||||
# The common_* event handler fields are the most useful fields common to
|
||||
# all events. They don't necessarily correspond to the 'common_*' fields
|
||||
# in the format files. Those fields not available as handler params can
|
||||
# be retrieved using Python functions of the form common_*(context).
|
||||
# See the perf-script-python Documentation for the list of available functions.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/perf-script-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
|
||||
def trace_begin():
|
||||
print "in trace_begin"
|
||||
|
||||
def trace_end():
|
||||
print "in trace_end"
|
||||
|
||||
def raw_syscalls__sys_enter(event_name, context, common_cpu,
|
||||
common_secs, common_nsecs, common_pid, common_comm,
|
||||
id, args):
|
||||
print_header(event_name, common_cpu, common_secs, common_nsecs,
|
||||
common_pid, common_comm)
|
||||
|
||||
print "id=%d, args=%s\n" % \
|
||||
(id, args),
|
||||
|
||||
def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs,
|
||||
common_pid, common_comm):
|
||||
print_header(event_name, common_cpu, common_secs, common_nsecs,
|
||||
common_pid, common_comm)
|
||||
|
||||
def print_header(event_name, cpu, secs, nsecs, pid, comm):
|
||||
print "%-20s %5u %05u.%09u %8u %-20s " % \
|
||||
(event_name, cpu, secs, nsecs, pid, comm),
|
||||
----
|
||||
|
||||
At the top is a comment block followed by some import statements and a
|
||||
path append which every perf script script should include.
|
||||
|
||||
Following that are a couple generated functions, trace_begin() and
|
||||
trace_end(), which are called at the beginning and the end of the
|
||||
script respectively (for more details, see the SCRIPT_LAYOUT section
|
||||
below).
|
||||
|
||||
Following those are the 'event handler' functions generated one for
|
||||
every event in the 'perf record' output. The handler functions take
|
||||
the form subsystem__event_name, and contain named parameters, one for
|
||||
each field in the event; in this case, there's only one event,
|
||||
raw_syscalls__sys_enter(). (see the EVENT HANDLERS section below for
|
||||
more info on event handlers).
|
||||
|
||||
The final couple of functions are, like the begin and end functions,
|
||||
generated for every script. The first, trace_unhandled(), is called
|
||||
every time the script finds an event in the perf.data file that
|
||||
doesn't correspond to any event handler in the script. This could
|
||||
mean either that the record step recorded event types that it wasn't
|
||||
really interested in, or the script was run against a trace file that
|
||||
doesn't correspond to the script.
|
||||
|
||||
The script generated by -g option simply prints a line for each
|
||||
event found in the trace stream i.e. it basically just dumps the event
|
||||
and its parameter values to stdout. The print_header() function is
|
||||
simply a utility function used for that purpose. Let's rename the
|
||||
script and run it to see the default output:
|
||||
|
||||
----
|
||||
# mv perf-script.py syscall-counts.py
|
||||
# perf script -s syscall-counts.py
|
||||
|
||||
raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args=
|
||||
raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args=
|
||||
raw_syscalls__sys_enter 1 00840.847620860 7506 perf id=1, args=
|
||||
raw_syscalls__sys_enter 1 00840.847710478 6533 npviewer.bin id=78, args=
|
||||
raw_syscalls__sys_enter 1 00840.847719204 6533 npviewer.bin id=142, args=
|
||||
raw_syscalls__sys_enter 1 00840.847755445 6533 npviewer.bin id=3, args=
|
||||
raw_syscalls__sys_enter 1 00840.847775601 6533 npviewer.bin id=3, args=
|
||||
raw_syscalls__sys_enter 1 00840.847781820 6533 npviewer.bin id=3, args=
|
||||
.
|
||||
.
|
||||
.
|
||||
----
|
||||
|
||||
Of course, for this script, we're not interested in printing every
|
||||
trace event, but rather aggregating it in a useful way. So we'll get
|
||||
rid of everything to do with printing as well as the trace_begin() and
|
||||
trace_unhandled() functions, which we won't be using. That leaves us
|
||||
with this minimalistic skeleton:
|
||||
|
||||
----
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/perf-script-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
|
||||
def trace_end():
|
||||
print "in trace_end"
|
||||
|
||||
def raw_syscalls__sys_enter(event_name, context, common_cpu,
|
||||
common_secs, common_nsecs, common_pid, common_comm,
|
||||
id, args):
|
||||
----
|
||||
|
||||
In trace_end(), we'll simply print the results, but first we need to
|
||||
generate some results to print. To do that we need to have our
|
||||
sys_enter() handler do the necessary tallying until all events have
|
||||
been counted. A hash table indexed by syscall id is a good way to
|
||||
store that information; every time the sys_enter() handler is called,
|
||||
we simply increment a count associated with that hash entry indexed by
|
||||
that syscall id:
|
||||
|
||||
----
|
||||
syscalls = autodict()
|
||||
|
||||
try:
|
||||
syscalls[id] += 1
|
||||
except TypeError:
|
||||
syscalls[id] = 1
|
||||
----
|
||||
|
||||
The syscalls 'autodict' object is a special kind of Python dictionary
|
||||
(implemented in Core.py) that implements Perl's 'autovivifying' hashes
|
||||
in Python i.e. with autovivifying hashes, you can assign nested hash
|
||||
values without having to go to the trouble of creating intermediate
|
||||
levels if they don't exist e.g syscalls[comm][pid][id] = 1 will create
|
||||
the intermediate hash levels and finally assign the value 1 to the
|
||||
hash entry for 'id' (because the value being assigned isn't a hash
|
||||
object itself, the initial value is assigned in the TypeError
|
||||
exception. Well, there may be a better way to do this in Python but
|
||||
that's what works for now).
|
||||
|
||||
Putting that code into the raw_syscalls__sys_enter() handler, we
|
||||
effectively end up with a single-level dictionary keyed on syscall id
|
||||
and having the counts we've tallied as values.
|
||||
|
||||
The print_syscall_totals() function iterates over the entries in the
|
||||
dictionary and displays a line for each entry containing the syscall
|
||||
name (the dictonary keys contain the syscall ids, which are passed to
|
||||
the Util function syscall_name(), which translates the raw syscall
|
||||
numbers to the corresponding syscall name strings). The output is
|
||||
displayed after all the events in the trace have been processed, by
|
||||
calling the print_syscall_totals() function from the trace_end()
|
||||
handler called at the end of script processing.
|
||||
|
||||
The final script producing the output shown above is shown in its
|
||||
entirety below (syscall_name() helper is not yet available, you can
|
||||
only deal with id's for now):
|
||||
|
||||
----
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/perf-script-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
from Util import *
|
||||
|
||||
syscalls = autodict()
|
||||
|
||||
def trace_end():
|
||||
print_syscall_totals()
|
||||
|
||||
def raw_syscalls__sys_enter(event_name, context, common_cpu,
|
||||
common_secs, common_nsecs, common_pid, common_comm,
|
||||
id, args):
|
||||
try:
|
||||
syscalls[id] += 1
|
||||
except TypeError:
|
||||
syscalls[id] = 1
|
||||
|
||||
def print_syscall_totals():
|
||||
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 "%-40s %10d\n" % (syscall_name(id), val),
|
||||
----
|
||||
|
||||
The script can be run just as before:
|
||||
|
||||
# perf script -s syscall-counts.py
|
||||
|
||||
So those are the essential steps in writing and running a script. The
|
||||
process can be generalized to any tracepoint or set of tracepoints
|
||||
you're interested in - basically find the tracepoint(s) you're
|
||||
interested in by looking at the list of available events shown by
|
||||
'perf list' and/or look in /sys/kernel/debug/tracing events for
|
||||
detailed event and field info, record the corresponding trace data
|
||||
using 'perf record', passing it the list of interesting events,
|
||||
generate a skeleton script using 'perf script -g python' and modify the
|
||||
code to aggregate and display it for your particular needs.
|
||||
|
||||
After you've done that you may end up with a general-purpose script
|
||||
that you want to keep around and have available for future use. By
|
||||
writing a couple of very simple shell scripts and putting them in the
|
||||
right place, you can have your script listed alongside the other
|
||||
scripts listed by the 'perf script -l' command e.g.:
|
||||
|
||||
----
|
||||
root@tropicana:~# perf script -l
|
||||
List of available trace scripts:
|
||||
workqueue-stats workqueue stats (ins/exe/create/destroy)
|
||||
wakeup-latency system-wide min/max/avg wakeup latency
|
||||
rw-by-file <comm> r/w activity for a program, by file
|
||||
rw-by-pid system-wide r/w activity
|
||||
----
|
||||
|
||||
A nice side effect of doing this is that you also then capture the
|
||||
probably lengthy 'perf record' command needed to record the events for
|
||||
the script.
|
||||
|
||||
To have the script appear as a 'built-in' script, you write two simple
|
||||
scripts, one for recording and one for 'reporting'.
|
||||
|
||||
The 'record' script is a shell script with the same base name as your
|
||||
script, but with -record appended. The shell script should be put
|
||||
into the perf/scripts/python/bin directory in the kernel source tree.
|
||||
In that script, you write the 'perf record' command-line needed for
|
||||
your script:
|
||||
|
||||
----
|
||||
# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record
|
||||
|
||||
#!/bin/bash
|
||||
perf record -a -e raw_syscalls:sys_enter
|
||||
----
|
||||
|
||||
The 'report' script is also a shell script with the same base name as
|
||||
your script, but with -report appended. It should also be located in
|
||||
the perf/scripts/python/bin directory. In that script, you write the
|
||||
'perf script -s' command-line needed for running your script:
|
||||
|
||||
----
|
||||
# cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report
|
||||
|
||||
#!/bin/bash
|
||||
# description: system-wide syscall counts
|
||||
perf script -s ~/libexec/perf-core/scripts/python/syscall-counts.py
|
||||
----
|
||||
|
||||
Note that the location of the Python script given in the shell script
|
||||
is in the libexec/perf-core/scripts/python directory - this is where
|
||||
the script will be copied by 'make install' when you install perf.
|
||||
For the installation to install your script there, your script needs
|
||||
to be located in the perf/scripts/python directory in the kernel
|
||||
source tree:
|
||||
|
||||
----
|
||||
# ls -al kernel-source/tools/perf/scripts/python
|
||||
|
||||
root@tropicana:/home/trz/src/tip# ls -al tools/perf/scripts/python
|
||||
total 32
|
||||
drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 .
|
||||
drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 ..
|
||||
drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin
|
||||
-rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-script.py
|
||||
drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 perf-script-Util
|
||||
-rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py
|
||||
----
|
||||
|
||||
Once you've done that (don't forget to do a new 'make install',
|
||||
otherwise your script won't show up at run-time), 'perf script -l'
|
||||
should show a new entry for your script:
|
||||
|
||||
----
|
||||
root@tropicana:~# perf script -l
|
||||
List of available trace scripts:
|
||||
workqueue-stats workqueue stats (ins/exe/create/destroy)
|
||||
wakeup-latency system-wide min/max/avg wakeup latency
|
||||
rw-by-file <comm> r/w activity for a program, by file
|
||||
rw-by-pid system-wide r/w activity
|
||||
syscall-counts system-wide syscall counts
|
||||
----
|
||||
|
||||
You can now perform the record step via 'perf script record':
|
||||
|
||||
# perf script record syscall-counts
|
||||
|
||||
and display the output using 'perf script report':
|
||||
|
||||
# perf script report syscall-counts
|
||||
|
||||
STARTER SCRIPTS
|
||||
---------------
|
||||
|
||||
You can quickly get started writing a script for a particular set of
|
||||
trace data by generating a skeleton script using 'perf script -g
|
||||
python' in the same directory as an existing perf.data trace file.
|
||||
That will generate a starter script containing a handler for each of
|
||||
the event types in the trace file; it simply prints every available
|
||||
field for each event in the trace file.
|
||||
|
||||
You can also look at the existing scripts in
|
||||
~/libexec/perf-core/scripts/python for typical examples showing how to
|
||||
do basic things like aggregate event data, print results, etc. Also,
|
||||
the check-perf-script.py script, while not interesting for its results,
|
||||
attempts to exercise all of the main scripting features.
|
||||
|
||||
EVENT HANDLERS
|
||||
--------------
|
||||
|
||||
When perf script is invoked using a trace script, a user-defined
|
||||
'handler function' is called for each event in the trace. If there's
|
||||
no handler function defined for a given event type, the event is
|
||||
ignored (or passed to a 'trace_handled' function, see below) and the
|
||||
next event is processed.
|
||||
|
||||
Most of the event's field values are passed as arguments to the
|
||||
handler function; some of the less common ones aren't - those are
|
||||
available as calls back into the perf executable (see below).
|
||||
|
||||
As an example, the following perf record command can be used to record
|
||||
all sched_wakeup events in the system:
|
||||
|
||||
# perf record -a -e sched:sched_wakeup
|
||||
|
||||
Traces meant to be processed using a script should be recorded with
|
||||
the above option: -a to enable system-wide collection.
|
||||
|
||||
The format file for the sched_wakep event defines the following fields
|
||||
(see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format):
|
||||
|
||||
----
|
||||
format:
|
||||
field:unsigned short common_type;
|
||||
field:unsigned char common_flags;
|
||||
field:unsigned char common_preempt_count;
|
||||
field:int common_pid;
|
||||
field:int common_lock_depth;
|
||||
|
||||
field:char comm[TASK_COMM_LEN];
|
||||
field:pid_t pid;
|
||||
field:int prio;
|
||||
field:int success;
|
||||
field:int target_cpu;
|
||||
----
|
||||
|
||||
The handler function for this event would be defined as:
|
||||
|
||||
----
|
||||
def sched__sched_wakeup(event_name, context, common_cpu, common_secs,
|
||||
common_nsecs, common_pid, common_comm,
|
||||
comm, pid, prio, success, target_cpu):
|
||||
pass
|
||||
----
|
||||
|
||||
The handler function takes the form subsystem__event_name.
|
||||
|
||||
The common_* arguments in the handler's argument list are the set of
|
||||
arguments passed to all event handlers; some of the fields correspond
|
||||
to the common_* fields in the format file, but some are synthesized,
|
||||
and some of the common_* fields aren't common enough to to be passed
|
||||
to every event as arguments but are available as library functions.
|
||||
|
||||
Here's a brief description of each of the invariant event args:
|
||||
|
||||
event_name the name of the event as text
|
||||
context an opaque 'cookie' used in calls back into perf
|
||||
common_cpu the cpu the event occurred on
|
||||
common_secs the secs portion of the event timestamp
|
||||
common_nsecs the nsecs portion of the event timestamp
|
||||
common_pid the pid of the current task
|
||||
common_comm the name of the current process
|
||||
|
||||
All of the remaining fields in the event's format file have
|
||||
counterparts as handler function arguments of the same name, as can be
|
||||
seen in the example above.
|
||||
|
||||
The above provides the basics needed to directly access every field of
|
||||
every event in a trace, which covers 90% of what you need to know to
|
||||
write a useful trace script. The sections below cover the rest.
|
||||
|
||||
SCRIPT LAYOUT
|
||||
-------------
|
||||
|
||||
Every perf script Python script should start by setting up a Python
|
||||
module search path and 'import'ing a few support modules (see module
|
||||
descriptions below):
|
||||
|
||||
----
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
'/scripts/python/perf-script-Util/lib/Perf/Trace')
|
||||
|
||||
from perf_trace_context import *
|
||||
from Core import *
|
||||
----
|
||||
|
||||
The rest of the script can contain handler functions and support
|
||||
functions in any order.
|
||||
|
||||
Aside from the event handler functions discussed above, every script
|
||||
can implement a set of optional functions:
|
||||
|
||||
*trace_begin*, if defined, is called before any event is processed and
|
||||
gives scripts a chance to do setup tasks:
|
||||
|
||||
----
|
||||
def trace_begin:
|
||||
pass
|
||||
----
|
||||
|
||||
*trace_end*, if defined, is called after all events have been
|
||||
processed and gives scripts a chance to do end-of-script tasks, such
|
||||
as display results:
|
||||
|
||||
----
|
||||
def trace_end:
|
||||
pass
|
||||
----
|
||||
|
||||
*trace_unhandled*, if defined, is called after for any event that
|
||||
doesn't have a handler explicitly defined for it. The standard set
|
||||
of common arguments are passed into it:
|
||||
|
||||
----
|
||||
def trace_unhandled(event_name, context, common_cpu, common_secs,
|
||||
common_nsecs, common_pid, common_comm):
|
||||
pass
|
||||
----
|
||||
|
||||
The remaining sections provide descriptions of each of the available
|
||||
built-in perf script Python modules and their associated functions.
|
||||
|
||||
AVAILABLE MODULES AND FUNCTIONS
|
||||
-------------------------------
|
||||
|
||||
The following sections describe the functions and variables available
|
||||
via the various perf script Python modules. To use the functions and
|
||||
variables from the given module, add the corresponding 'from XXXX
|
||||
import' line to your perf script script.
|
||||
|
||||
Core.py Module
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
These functions provide some essential functions to user scripts.
|
||||
|
||||
The *flag_str* and *symbol_str* functions provide human-readable
|
||||
strings for flag and symbolic fields. These correspond to the strings
|
||||
and values parsed from the 'print fmt' fields of the event format
|
||||
files:
|
||||
|
||||
flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name
|
||||
symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name
|
||||
|
||||
The *autodict* function returns a special kind of Python
|
||||
dictionary that implements Perl's 'autovivifying' hashes in Python
|
||||
i.e. with autovivifying hashes, you can assign nested hash values
|
||||
without having to go to the trouble of creating intermediate levels if
|
||||
they don't exist.
|
||||
|
||||
autodict() - returns an autovivifying dictionary instance
|
||||
|
||||
|
||||
perf_trace_context Module
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Some of the 'common' fields in the event format file aren't all that
|
||||
common, but need to be made accessible to user scripts nonetheless.
|
||||
|
||||
perf_trace_context defines a set of functions that can be used to
|
||||
access this data in the context of the current event. Each of these
|
||||
functions expects a context variable, which is the same as the
|
||||
context variable passed into every event handler as the second
|
||||
argument.
|
||||
|
||||
common_pc(context) - returns common_preempt count for the current event
|
||||
common_flags(context) - returns common_flags for the current event
|
||||
common_lock_depth(context) - returns common_lock_depth for the current event
|
||||
|
||||
Util.py Module
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Various utility functions for use with perf script:
|
||||
|
||||
nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair
|
||||
nsecs_secs(nsecs) - returns whole secs portion given nsecs
|
||||
nsecs_nsecs(nsecs) - returns nsecs remainder given nsecs
|
||||
nsecs_str(nsecs) - returns printable string in the form secs.nsecs
|
||||
avg(total, n) - returns average given a sum and a total number of values
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-script[1]
|
||||
111
tools/perf/Documentation/perf-script.txt
Normal file
111
tools/perf/Documentation/perf-script.txt
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
perf-script(1)
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-script - Read perf.data (created by perf record) and display trace output
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf script' [<options>]
|
||||
'perf script' [<options>] record <script> [<record-options>] <command>
|
||||
'perf script' [<options>] report <script> [script-args]
|
||||
'perf script' [<options>] <script> <required-script-args> [<record-options>] <command>
|
||||
'perf script' [<options>] <top-script> [script-args]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command reads the input file and displays the trace recorded.
|
||||
|
||||
There are several variants of perf script:
|
||||
|
||||
'perf script' to see a detailed trace of the workload that was
|
||||
recorded.
|
||||
|
||||
You can also run a set of pre-canned scripts that aggregate and
|
||||
summarize the raw trace data in various ways (the list of scripts is
|
||||
available via 'perf script -l'). The following variants allow you to
|
||||
record and run those scripts:
|
||||
|
||||
'perf script record <script> <command>' to record the events required
|
||||
for 'perf script report'. <script> is the name displayed in the
|
||||
output of 'perf script --list' i.e. the actual script name minus any
|
||||
language extension. If <command> is not specified, the events are
|
||||
recorded using the -a (system-wide) 'perf record' option.
|
||||
|
||||
'perf script report <script> [args]' to run and display the results
|
||||
of <script>. <script> is the name displayed in the output of 'perf
|
||||
trace --list' i.e. the actual script name minus any language
|
||||
extension. The perf.data output from a previous run of 'perf script
|
||||
record <script>' is used and should be present for this command to
|
||||
succeed. [args] refers to the (mainly optional) args expected by
|
||||
the script.
|
||||
|
||||
'perf script <script> <required-script-args> <command>' to both
|
||||
record the events required for <script> and to run the <script>
|
||||
using 'live-mode' i.e. without writing anything to disk. <script>
|
||||
is the name displayed in the output of 'perf script --list' i.e. the
|
||||
actual script name minus any language extension. If <command> is
|
||||
not specified, the events are recorded using the -a (system-wide)
|
||||
'perf record' option. If <script> has any required args, they
|
||||
should be specified before <command>. This mode doesn't allow for
|
||||
optional script args to be specified; if optional script args are
|
||||
desired, they can be specified using separate 'perf script record'
|
||||
and 'perf script report' commands, with the stdout of the record step
|
||||
piped to the stdin of the report script, using the '-o -' and '-i -'
|
||||
options of the corresponding commands.
|
||||
|
||||
'perf script <top-script>' to both record the events required for
|
||||
<top-script> and to run the <top-script> using 'live-mode'
|
||||
i.e. without writing anything to disk. <top-script> is the name
|
||||
displayed in the output of 'perf script --list' i.e. the actual
|
||||
script name minus any language extension; a <top-script> is defined
|
||||
as any script name ending with the string 'top'.
|
||||
|
||||
[<record-options>] can be passed to the record steps of 'perf script
|
||||
record' and 'live-mode' variants; this isn't possible however for
|
||||
<top-script> 'live-mode' or 'perf script report' variants.
|
||||
|
||||
See the 'SEE ALSO' section for links to language-specific
|
||||
information on how to write and run your own trace scripts.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<command>...::
|
||||
Any command you can specify in a shell.
|
||||
|
||||
-D::
|
||||
--dump-raw-script=::
|
||||
Display verbose dump of the trace data.
|
||||
|
||||
-L::
|
||||
--Latency=::
|
||||
Show latency attributes (irqs/preemption disabled, etc).
|
||||
|
||||
-l::
|
||||
--list=::
|
||||
Display a list of available trace scripts.
|
||||
|
||||
-s ['lang']::
|
||||
--script=::
|
||||
Process trace data with the given script ([lang]:script[.ext]).
|
||||
If the string 'lang' is specified in place of a script name, a
|
||||
list of supported languages will be displayed instead.
|
||||
|
||||
-g::
|
||||
--gen-script=::
|
||||
Generate perf-script.[ext] starter script for given language,
|
||||
using current perf.data.
|
||||
|
||||
-a::
|
||||
Force system-wide collection. Scripts run without a <command>
|
||||
normally use -a by default, while scripts run with a <command>
|
||||
normally don't - this option allows the latter to be run in
|
||||
system-wide mode.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script-perl[1],
|
||||
linkperf:perf-script-python[1]
|
||||
|
|
@ -485,7 +485,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-report.o
|
|||
BUILTIN_OBJS += $(OUTPUT)builtin-stat.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-top.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-script.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-probe.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-lock.o
|
||||
|
|
|
|||
|
|
@ -982,9 +982,9 @@ int cmd_lock(int argc, const char **argv, const char *prefix __used)
|
|||
usage_with_options(report_usage, report_options);
|
||||
}
|
||||
__cmd_report();
|
||||
} else if (!strcmp(argv[0], "trace")) {
|
||||
/* Aliased to 'perf trace' */
|
||||
return cmd_trace(argc, argv, prefix);
|
||||
} else if (!strcmp(argv[0], "script")) {
|
||||
/* Aliased to 'perf script' */
|
||||
return cmd_script(argc, argv, prefix);
|
||||
} else if (!strcmp(argv[0], "info")) {
|
||||
if (argc) {
|
||||
argc = parse_options(argc, argv,
|
||||
|
|
|
|||
|
|
@ -1888,10 +1888,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used)
|
|||
usage_with_options(sched_usage, sched_options);
|
||||
|
||||
/*
|
||||
* Aliased to 'perf trace' for now:
|
||||
* Aliased to 'perf script' for now:
|
||||
*/
|
||||
if (!strcmp(argv[0], "trace"))
|
||||
return cmd_trace(argc, argv, prefix);
|
||||
if (!strcmp(argv[0], "script"))
|
||||
return cmd_script(argc, argv, prefix);
|
||||
|
||||
symbol__init();
|
||||
if (!strncmp(argv[0], "rec", 3)) {
|
||||
|
|
|
|||
826
tools/perf/builtin-script.c
Normal file
826
tools/perf/builtin-script.c
Normal file
|
|
@ -0,0 +1,826 @@
|
|||
#include "builtin.h"
|
||||
|
||||
#include "perf.h"
|
||||
#include "util/cache.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/exec_cmd.h"
|
||||
#include "util/header.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/session.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/trace-event.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/util.h"
|
||||
|
||||
static char const *script_name;
|
||||
static char const *generate_script_lang;
|
||||
static bool debug_mode;
|
||||
static u64 last_timestamp;
|
||||
static u64 nr_unordered;
|
||||
extern const struct option record_options[];
|
||||
|
||||
static int default_start_script(const char *script __unused,
|
||||
int argc __unused,
|
||||
const char **argv __unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int default_stop_script(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int default_generate_script(const char *outfile __unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct scripting_ops default_scripting_ops = {
|
||||
.start_script = default_start_script,
|
||||
.stop_script = default_stop_script,
|
||||
.process_event = print_event,
|
||||
.generate_script = default_generate_script,
|
||||
};
|
||||
|
||||
static struct scripting_ops *scripting_ops;
|
||||
|
||||
static void setup_scripting(void)
|
||||
{
|
||||
setup_perl_scripting();
|
||||
setup_python_scripting();
|
||||
|
||||
scripting_ops = &default_scripting_ops;
|
||||
}
|
||||
|
||||
static int cleanup_scripting(void)
|
||||
{
|
||||
pr_debug("\nperf script stopped\n");
|
||||
|
||||
return scripting_ops->stop_script();
|
||||
}
|
||||
|
||||
static char const *input_name = "perf.data";
|
||||
|
||||
static int process_sample_event(event_t *event, struct perf_session *session)
|
||||
{
|
||||
struct sample_data data;
|
||||
struct thread *thread;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.time = -1;
|
||||
data.cpu = -1;
|
||||
data.period = 1;
|
||||
|
||||
event__parse_sample(event, session->sample_type, &data);
|
||||
|
||||
dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc,
|
||||
data.pid, data.tid, data.ip, data.period);
|
||||
|
||||
thread = perf_session__findnew(session, event->ip.pid);
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session->sample_type & PERF_SAMPLE_RAW) {
|
||||
if (debug_mode) {
|
||||
if (data.time < last_timestamp) {
|
||||
pr_err("Samples misordered, previous: %llu "
|
||||
"this: %llu\n", last_timestamp,
|
||||
data.time);
|
||||
nr_unordered++;
|
||||
}
|
||||
last_timestamp = data.time;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* FIXME: better resolve from pid from the struct trace_entry
|
||||
* field, although it should be the same than this perf
|
||||
* event pid
|
||||
*/
|
||||
scripting_ops->process_event(data.cpu, data.raw_data,
|
||||
data.raw_size,
|
||||
data.time, thread->comm);
|
||||
}
|
||||
|
||||
session->hists.stats.total_period += data.period;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 nr_lost;
|
||||
|
||||
static int process_lost_event(event_t *event, struct perf_session *session __used)
|
||||
{
|
||||
nr_lost += event->lost.lost;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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,
|
||||
.lost = process_lost_event,
|
||||
.ordered_samples = true,
|
||||
};
|
||||
|
||||
extern volatile int session_done;
|
||||
|
||||
static void sig_handler(int sig __unused)
|
||||
{
|
||||
session_done = 1;
|
||||
}
|
||||
|
||||
static int __cmd_script(struct perf_session *session)
|
||||
{
|
||||
int ret;
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
ret = perf_session__process_events(session, &event_ops);
|
||||
|
||||
if (debug_mode) {
|
||||
pr_err("Misordered timestamps: %llu\n", nr_unordered);
|
||||
pr_err("Lost events: %llu\n", nr_lost);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct script_spec {
|
||||
struct list_head node;
|
||||
struct scripting_ops *ops;
|
||||
char spec[0];
|
||||
};
|
||||
|
||||
LIST_HEAD(script_specs);
|
||||
|
||||
static struct script_spec *script_spec__new(const char *spec,
|
||||
struct scripting_ops *ops)
|
||||
{
|
||||
struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1);
|
||||
|
||||
if (s != NULL) {
|
||||
strcpy(s->spec, spec);
|
||||
s->ops = ops;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void script_spec__delete(struct script_spec *s)
|
||||
{
|
||||
free(s->spec);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void script_spec__add(struct script_spec *s)
|
||||
{
|
||||
list_add_tail(&s->node, &script_specs);
|
||||
}
|
||||
|
||||
static struct script_spec *script_spec__find(const char *spec)
|
||||
{
|
||||
struct script_spec *s;
|
||||
|
||||
list_for_each_entry(s, &script_specs, node)
|
||||
if (strcasecmp(s->spec, spec) == 0)
|
||||
return s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct script_spec *script_spec__findnew(const char *spec,
|
||||
struct scripting_ops *ops)
|
||||
{
|
||||
struct script_spec *s = script_spec__find(spec);
|
||||
|
||||
if (s)
|
||||
return s;
|
||||
|
||||
s = script_spec__new(spec, ops);
|
||||
if (!s)
|
||||
goto out_delete_spec;
|
||||
|
||||
script_spec__add(s);
|
||||
|
||||
return s;
|
||||
|
||||
out_delete_spec:
|
||||
script_spec__delete(s);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int script_spec_register(const char *spec, struct scripting_ops *ops)
|
||||
{
|
||||
struct script_spec *s;
|
||||
|
||||
s = script_spec__find(spec);
|
||||
if (s)
|
||||
return -1;
|
||||
|
||||
s = script_spec__findnew(spec, ops);
|
||||
if (!s)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct scripting_ops *script_spec__lookup(const char *spec)
|
||||
{
|
||||
struct script_spec *s = script_spec__find(spec);
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
return s->ops;
|
||||
}
|
||||
|
||||
static void list_available_languages(void)
|
||||
{
|
||||
struct script_spec *s;
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Scripting language extensions (used in "
|
||||
"perf script -s [spec:]script.[spec]):\n\n");
|
||||
|
||||
list_for_each_entry(s, &script_specs, node)
|
||||
fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
static int parse_scriptname(const struct option *opt __used,
|
||||
const char *str, int unset __used)
|
||||
{
|
||||
char spec[PATH_MAX];
|
||||
const char *script, *ext;
|
||||
int len;
|
||||
|
||||
if (strcmp(str, "lang") == 0) {
|
||||
list_available_languages();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
script = strchr(str, ':');
|
||||
if (script) {
|
||||
len = script - str;
|
||||
if (len >= PATH_MAX) {
|
||||
fprintf(stderr, "invalid language specifier");
|
||||
return -1;
|
||||
}
|
||||
strncpy(spec, str, len);
|
||||
spec[len] = '\0';
|
||||
scripting_ops = script_spec__lookup(spec);
|
||||
if (!scripting_ops) {
|
||||
fprintf(stderr, "invalid language specifier");
|
||||
return -1;
|
||||
}
|
||||
script++;
|
||||
} else {
|
||||
script = str;
|
||||
ext = strrchr(script, '.');
|
||||
if (!ext) {
|
||||
fprintf(stderr, "invalid script extension");
|
||||
return -1;
|
||||
}
|
||||
scripting_ops = script_spec__lookup(++ext);
|
||||
if (!scripting_ops) {
|
||||
fprintf(stderr, "invalid script extension");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
script_name = strdup(script);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define for_each_lang(scripts_dir, lang_dirent, lang_next) \
|
||||
while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \
|
||||
lang_next) \
|
||||
if (lang_dirent.d_type == DT_DIR && \
|
||||
(strcmp(lang_dirent.d_name, ".")) && \
|
||||
(strcmp(lang_dirent.d_name, "..")))
|
||||
|
||||
#define for_each_script(lang_dir, script_dirent, script_next) \
|
||||
while (!readdir_r(lang_dir, &script_dirent, &script_next) && \
|
||||
script_next) \
|
||||
if (script_dirent.d_type != DT_DIR)
|
||||
|
||||
|
||||
#define RECORD_SUFFIX "-record"
|
||||
#define REPORT_SUFFIX "-report"
|
||||
|
||||
struct script_desc {
|
||||
struct list_head node;
|
||||
char *name;
|
||||
char *half_liner;
|
||||
char *args;
|
||||
};
|
||||
|
||||
LIST_HEAD(script_descs);
|
||||
|
||||
static struct script_desc *script_desc__new(const char *name)
|
||||
{
|
||||
struct script_desc *s = zalloc(sizeof(*s));
|
||||
|
||||
if (s != NULL && name)
|
||||
s->name = strdup(name);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void script_desc__delete(struct script_desc *s)
|
||||
{
|
||||
free(s->name);
|
||||
free(s->half_liner);
|
||||
free(s->args);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static void script_desc__add(struct script_desc *s)
|
||||
{
|
||||
list_add_tail(&s->node, &script_descs);
|
||||
}
|
||||
|
||||
static struct script_desc *script_desc__find(const char *name)
|
||||
{
|
||||
struct script_desc *s;
|
||||
|
||||
list_for_each_entry(s, &script_descs, node)
|
||||
if (strcasecmp(s->name, name) == 0)
|
||||
return s;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct script_desc *script_desc__findnew(const char *name)
|
||||
{
|
||||
struct script_desc *s = script_desc__find(name);
|
||||
|
||||
if (s)
|
||||
return s;
|
||||
|
||||
s = script_desc__new(name);
|
||||
if (!s)
|
||||
goto out_delete_desc;
|
||||
|
||||
script_desc__add(s);
|
||||
|
||||
return s;
|
||||
|
||||
out_delete_desc:
|
||||
script_desc__delete(s);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *ends_with(char *str, const char *suffix)
|
||||
{
|
||||
size_t suffix_len = strlen(suffix);
|
||||
char *p = str;
|
||||
|
||||
if (strlen(str) > suffix_len) {
|
||||
p = str + strlen(str) - suffix_len;
|
||||
if (!strncmp(p, suffix, suffix_len))
|
||||
return p;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *ltrim(char *str)
|
||||
{
|
||||
int len = strlen(str);
|
||||
|
||||
while (len && isspace(*str)) {
|
||||
len--;
|
||||
str++;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static int read_script_info(struct script_desc *desc, const char *filename)
|
||||
{
|
||||
char line[BUFSIZ], *p;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
p = ltrim(line);
|
||||
if (strlen(p) == 0)
|
||||
continue;
|
||||
if (*p != '#')
|
||||
continue;
|
||||
p++;
|
||||
if (strlen(p) && *p == '!')
|
||||
continue;
|
||||
|
||||
p = ltrim(p);
|
||||
if (strlen(p) && p[strlen(p) - 1] == '\n')
|
||||
p[strlen(p) - 1] = '\0';
|
||||
|
||||
if (!strncmp(p, "description:", strlen("description:"))) {
|
||||
p += strlen("description:");
|
||||
desc->half_liner = strdup(ltrim(p));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncmp(p, "args:", strlen("args:"))) {
|
||||
p += strlen("args:");
|
||||
desc->args = strdup(ltrim(p));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int list_available_scripts(const struct option *opt __used,
|
||||
const char *s __used, int unset __used)
|
||||
{
|
||||
struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
|
||||
char scripts_path[MAXPATHLEN];
|
||||
DIR *scripts_dir, *lang_dir;
|
||||
char script_path[MAXPATHLEN];
|
||||
char lang_path[MAXPATHLEN];
|
||||
struct script_desc *desc;
|
||||
char first_half[BUFSIZ];
|
||||
char *script_root;
|
||||
char *str;
|
||||
|
||||
snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
|
||||
|
||||
scripts_dir = opendir(scripts_path);
|
||||
if (!scripts_dir)
|
||||
return -1;
|
||||
|
||||
for_each_lang(scripts_dir, lang_dirent, lang_next) {
|
||||
snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
|
||||
lang_dirent.d_name);
|
||||
lang_dir = opendir(lang_path);
|
||||
if (!lang_dir)
|
||||
continue;
|
||||
|
||||
for_each_script(lang_dir, script_dirent, script_next) {
|
||||
script_root = strdup(script_dirent.d_name);
|
||||
str = ends_with(script_root, REPORT_SUFFIX);
|
||||
if (str) {
|
||||
*str = '\0';
|
||||
desc = script_desc__findnew(script_root);
|
||||
snprintf(script_path, MAXPATHLEN, "%s/%s",
|
||||
lang_path, script_dirent.d_name);
|
||||
read_script_info(desc, script_path);
|
||||
}
|
||||
free(script_root);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stdout, "List of available trace scripts:\n");
|
||||
list_for_each_entry(desc, &script_descs, node) {
|
||||
sprintf(first_half, "%s %s", desc->name,
|
||||
desc->args ? desc->args : "");
|
||||
fprintf(stdout, " %-36s %s\n", first_half,
|
||||
desc->half_liner ? desc->half_liner : "");
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static char *get_script_path(const char *script_root, const char *suffix)
|
||||
{
|
||||
struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
|
||||
char scripts_path[MAXPATHLEN];
|
||||
char script_path[MAXPATHLEN];
|
||||
DIR *scripts_dir, *lang_dir;
|
||||
char lang_path[MAXPATHLEN];
|
||||
char *str, *__script_root;
|
||||
char *path = NULL;
|
||||
|
||||
snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
|
||||
|
||||
scripts_dir = opendir(scripts_path);
|
||||
if (!scripts_dir)
|
||||
return NULL;
|
||||
|
||||
for_each_lang(scripts_dir, lang_dirent, lang_next) {
|
||||
snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
|
||||
lang_dirent.d_name);
|
||||
lang_dir = opendir(lang_path);
|
||||
if (!lang_dir)
|
||||
continue;
|
||||
|
||||
for_each_script(lang_dir, script_dirent, script_next) {
|
||||
__script_root = strdup(script_dirent.d_name);
|
||||
str = ends_with(__script_root, suffix);
|
||||
if (str) {
|
||||
*str = '\0';
|
||||
if (strcmp(__script_root, script_root))
|
||||
continue;
|
||||
snprintf(script_path, MAXPATHLEN, "%s/%s",
|
||||
lang_path, script_dirent.d_name);
|
||||
path = strdup(script_path);
|
||||
free(__script_root);
|
||||
break;
|
||||
}
|
||||
free(__script_root);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static bool is_top_script(const char *script_path)
|
||||
{
|
||||
return ends_with((char *)script_path, "top") == NULL ? false : true;
|
||||
}
|
||||
|
||||
static int has_required_arg(char *script_path)
|
||||
{
|
||||
struct script_desc *desc;
|
||||
int n_args = 0;
|
||||
char *p;
|
||||
|
||||
desc = script_desc__new(NULL);
|
||||
|
||||
if (read_script_info(desc, script_path))
|
||||
goto out;
|
||||
|
||||
if (!desc->args)
|
||||
goto out;
|
||||
|
||||
for (p = desc->args; *p; p++)
|
||||
if (*p == '<')
|
||||
n_args++;
|
||||
out:
|
||||
script_desc__delete(desc);
|
||||
|
||||
return n_args;
|
||||
}
|
||||
|
||||
static const char * const script_usage[] = {
|
||||
"perf script [<options>]",
|
||||
"perf script [<options>] record <script> [<record-options>] <command>",
|
||||
"perf script [<options>] report <script> [script-args]",
|
||||
"perf script [<options>] <script> [<record-options>] <command>",
|
||||
"perf script [<options>] <top-script> [script-args]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('L', "Latency", &latency_format,
|
||||
"show latency attributes (irqs/preemption disabled, etc)"),
|
||||
OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts",
|
||||
list_available_scripts),
|
||||
OPT_CALLBACK('s', "script", NULL, "name",
|
||||
"script file name (lang:script name, script name, or *)",
|
||||
parse_scriptname),
|
||||
OPT_STRING('g', "gen-script", &generate_script_lang, "lang",
|
||||
"generate perf-script.xx script in specified language"),
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_BOOLEAN('d', "debug-mode", &debug_mode,
|
||||
"do various checks like samples ordering and lost events"),
|
||||
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static bool have_cmd(int argc, const char **argv)
|
||||
{
|
||||
char **__argv = malloc(sizeof(const char *) * argc);
|
||||
|
||||
if (!__argv)
|
||||
die("malloc");
|
||||
memcpy(__argv, argv, sizeof(const char *) * argc);
|
||||
argc = parse_options(argc, (const char **)__argv, record_options,
|
||||
NULL, PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
free(__argv);
|
||||
|
||||
return argc != 0;
|
||||
}
|
||||
|
||||
int cmd_script(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
char *rec_script_path = NULL;
|
||||
char *rep_script_path = NULL;
|
||||
struct perf_session *session;
|
||||
char *script_path = NULL;
|
||||
const char **__argv;
|
||||
bool system_wide;
|
||||
int i, j, err;
|
||||
|
||||
setup_scripting();
|
||||
|
||||
argc = parse_options(argc, argv, options, script_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
|
||||
rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
|
||||
if (!rec_script_path)
|
||||
return cmd_record(argc, argv, NULL);
|
||||
}
|
||||
|
||||
if (argc > 1 && !strncmp(argv[0], "rep", strlen("rep"))) {
|
||||
rep_script_path = get_script_path(argv[1], REPORT_SUFFIX);
|
||||
if (!rep_script_path) {
|
||||
fprintf(stderr,
|
||||
"Please specify a valid report script"
|
||||
"(see 'perf script -l' for listing)\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* make sure PERF_EXEC_PATH is set for scripts */
|
||||
perf_set_argv_exec_path(perf_exec_path());
|
||||
|
||||
if (argc && !script_name && !rec_script_path && !rep_script_path) {
|
||||
int live_pipe[2];
|
||||
int rep_args;
|
||||
pid_t pid;
|
||||
|
||||
rec_script_path = get_script_path(argv[0], RECORD_SUFFIX);
|
||||
rep_script_path = get_script_path(argv[0], REPORT_SUFFIX);
|
||||
|
||||
if (!rec_script_path && !rep_script_path) {
|
||||
fprintf(stderr, " Couldn't find script %s\n\n See perf"
|
||||
" script -l for available scripts.\n", argv[0]);
|
||||
usage_with_options(script_usage, options);
|
||||
}
|
||||
|
||||
if (is_top_script(argv[0])) {
|
||||
rep_args = argc - 1;
|
||||
} else {
|
||||
int rec_args;
|
||||
|
||||
rep_args = has_required_arg(rep_script_path);
|
||||
rec_args = (argc - 1) - rep_args;
|
||||
if (rec_args < 0) {
|
||||
fprintf(stderr, " %s script requires options."
|
||||
"\n\n See perf script -l for available "
|
||||
"scripts and options.\n", argv[0]);
|
||||
usage_with_options(script_usage, options);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
system_wide = true;
|
||||
j = 0;
|
||||
|
||||
dup2(live_pipe[1], 1);
|
||||
close(live_pipe[0]);
|
||||
|
||||
if (!is_top_script(argv[0]))
|
||||
system_wide = !have_cmd(argc - rep_args,
|
||||
&argv[rep_args]);
|
||||
|
||||
__argv = malloc((argc + 6) * sizeof(const char *));
|
||||
if (!__argv)
|
||||
die("malloc");
|
||||
|
||||
__argv[j++] = "/bin/sh";
|
||||
__argv[j++] = rec_script_path;
|
||||
if (system_wide)
|
||||
__argv[j++] = "-a";
|
||||
__argv[j++] = "-q";
|
||||
__argv[j++] = "-o";
|
||||
__argv[j++] = "-";
|
||||
for (i = rep_args + 1; i < argc; i++)
|
||||
__argv[j++] = argv[i];
|
||||
__argv[j++] = NULL;
|
||||
|
||||
execvp("/bin/sh", (char **)__argv);
|
||||
free(__argv);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
dup2(live_pipe[0], 0);
|
||||
close(live_pipe[1]);
|
||||
|
||||
__argv = malloc((argc + 4) * sizeof(const char *));
|
||||
if (!__argv)
|
||||
die("malloc");
|
||||
j = 0;
|
||||
__argv[j++] = "/bin/sh";
|
||||
__argv[j++] = rep_script_path;
|
||||
for (i = 1; i < rep_args + 1; i++)
|
||||
__argv[j++] = argv[i];
|
||||
__argv[j++] = "-i";
|
||||
__argv[j++] = "-";
|
||||
__argv[j++] = NULL;
|
||||
|
||||
execvp("/bin/sh", (char **)__argv);
|
||||
free(__argv);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (rec_script_path)
|
||||
script_path = rec_script_path;
|
||||
if (rep_script_path)
|
||||
script_path = rep_script_path;
|
||||
|
||||
if (script_path) {
|
||||
system_wide = false;
|
||||
j = 0;
|
||||
|
||||
if (rec_script_path)
|
||||
system_wide = !have_cmd(argc - 1, &argv[1]);
|
||||
|
||||
__argv = malloc((argc + 2) * sizeof(const char *));
|
||||
if (!__argv)
|
||||
die("malloc");
|
||||
__argv[j++] = "/bin/sh";
|
||||
__argv[j++] = script_path;
|
||||
if (system_wide)
|
||||
__argv[j++] = "-a";
|
||||
for (i = 2; i < argc; i++)
|
||||
__argv[j++] = argv[i];
|
||||
__argv[j++] = NULL;
|
||||
|
||||
execvp("/bin/sh", (char **)__argv);
|
||||
free(__argv);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
if (!script_name)
|
||||
setup_pager();
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (strcmp(input_name, "-") &&
|
||||
!perf_session__has_traces(session, "record -R"))
|
||||
return -EINVAL;
|
||||
|
||||
if (generate_script_lang) {
|
||||
struct stat perf_stat;
|
||||
|
||||
int input = open(input_name, O_RDONLY);
|
||||
if (input < 0) {
|
||||
perror("failed to open file");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
err = fstat(input, &perf_stat);
|
||||
if (err < 0) {
|
||||
perror("failed to stat file");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!perf_stat.st_size) {
|
||||
fprintf(stderr, "zero-sized file, nothing to do!\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
scripting_ops = script_spec__lookup(generate_script_lang);
|
||||
if (!scripting_ops) {
|
||||
fprintf(stderr, "invalid language specifier");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = scripting_ops->generate_script("perf-script");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (script_name) {
|
||||
err = scripting_ops->start_script(script_name, argc, argv);
|
||||
if (err)
|
||||
goto out;
|
||||
pr_debug("perf script started with script %s\n\n", script_name);
|
||||
}
|
||||
|
||||
err = __cmd_script(session);
|
||||
|
||||
perf_session__delete(session);
|
||||
cleanup_scripting();
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ extern int cmd_report(int argc, const char **argv, const char *prefix);
|
|||
extern int cmd_stat(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_timechart(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_top(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_trace(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_script(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_version(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_probe(int argc, const char **argv, const char *prefix);
|
||||
extern int cmd_kmem(int argc, const char **argv, const char *prefix);
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ static void handle_internal_command(int argc, const char **argv)
|
|||
{ "top", cmd_top, 0 },
|
||||
{ "annotate", cmd_annotate, 0 },
|
||||
{ "version", cmd_version, 0 },
|
||||
{ "trace", cmd_trace, 0 },
|
||||
{ "script", cmd_script, 0 },
|
||||
{ "sched", cmd_sched, 0 },
|
||||
{ "probe", cmd_probe, 0 },
|
||||
{ "kmem", cmd_kmem, 0 },
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Context.c. Python interfaces for perf trace.
|
||||
* Context.c. Python interfaces for perf script.
|
||||
*
|
||||
* Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com>
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* trace-event-perl. Feed perf trace events to an embedded Perl interpreter.
|
||||
* trace-event-perl. Feed perf script events to an embedded Perl interpreter.
|
||||
*
|
||||
* Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com>
|
||||
*
|
||||
|
|
@ -411,8 +411,8 @@ static int perl_generate_script(const char *outfile)
|
|||
return -1;
|
||||
}
|
||||
|
||||
fprintf(ofp, "# perf trace event handlers, "
|
||||
"generated by perf trace -g perl\n");
|
||||
fprintf(ofp, "# perf script event handlers, "
|
||||
"generated by perf script -g perl\n");
|
||||
|
||||
fprintf(ofp, "# Licensed under the terms of the GNU GPL"
|
||||
" License version 2\n\n");
|
||||
|
|
|
|||
|
|
@ -442,8 +442,8 @@ static int python_generate_script(const char *outfile)
|
|||
fprintf(stderr, "couldn't open %s\n", fname);
|
||||
return -1;
|
||||
}
|
||||
fprintf(ofp, "# perf trace event handlers, "
|
||||
"generated by perf trace -g python\n");
|
||||
fprintf(ofp, "# perf script event handlers, "
|
||||
"generated by perf script -g python\n");
|
||||
|
||||
fprintf(ofp, "# Licensed under the terms of the GNU GPL"
|
||||
" License version 2\n\n");
|
||||
|
|
|
|||
Loading…
Reference in a new issue