From: Ingo Molnar <mingo@elte.hu> Date: Tue, 16 Nov 2010 17:45:39 +0000 (+0100) Subject: perf: Rename 'perf trace' to 'perf script' X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=133dc4c39c57eeef2577ca5b4ed24765b7a78ce2;p=GitHub%2FLineageOS%2Fandroid_kernel_samsung_universal7580.git 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> --- diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt new file mode 100644 index 00000000000..5bb41e55a3a --- /dev/null +++ b/tools/perf/Documentation/perf-script-perl.txt @@ -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] diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt new file mode 100644 index 00000000000..36b38277422 --- /dev/null +++ b/tools/perf/Documentation/perf-script-python.txt @@ -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] diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt new file mode 100644 index 00000000000..f442acc105b --- /dev/null +++ b/tools/perf/Documentation/perf-script.txt @@ -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] diff --git a/tools/perf/Documentation/perf-trace-perl.txt b/tools/perf/Documentation/perf-trace-perl.txt deleted file mode 100644 index ee6525ee6d6..00000000000 --- a/tools/perf/Documentation/perf-trace-perl.txt +++ /dev/null @@ -1,217 +0,0 @@ -perf-trace-perl(1) -================== - -NAME ----- -perf-trace-perl - Process trace data with a Perl script - -SYNOPSIS --------- -[verse] -'perf trace' [-s [Perl]:script[.pl] ] - -DESCRIPTION ------------ - -This perf trace option is used to process perf trace 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 trace --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-trace.pl script, while not interesting for its results, -attempts to exercise all of the main scripting features. - -EVENT HANDLERS --------------- - -When perf trace 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 trace 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-Trace-Util/lib"; - use lib "./Perf-Trace-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 trace 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 trace 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 trace: - - 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-trace[1] diff --git a/tools/perf/Documentation/perf-trace-python.txt b/tools/perf/Documentation/perf-trace-python.txt deleted file mode 100644 index 693be804dd3..00000000000 --- a/tools/perf/Documentation/perf-trace-python.txt +++ /dev/null @@ -1,623 +0,0 @@ -perf-trace-python(1) -==================== - -NAME ----- -perf-trace-python - Process trace data with a Python script - -SYNOPSIS --------- -[verse] -'perf trace' [-s [Python]:script[.py] ] - -DESCRIPTION ------------ - -This perf trace option is used to process perf trace 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 trace 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 trace -scripts via 'perf trace -l'. As such, this script also shows how to -integrate your script into the list of general-purpose 'perf trace' -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 trace' 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 trace -g python -generated Python script: perf-trace.py - -The output file created also in the current directory is named -perf-trace.py. Here's the file in its entirety: - -# perf trace event handlers, generated by perf trace -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-trace-python Documentation for the list of available functions. - -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 * - -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 trace 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-trace.py syscall-counts.py -# perf trace -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-Trace-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-Trace-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 trace -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 trace -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 trace -l' command e.g.: - ----- -root@tropicana:~# perf trace -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 trace -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 trace -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-trace.py -drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 Perf-Trace-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 trace -l' -should show a new entry for your script: - ----- -root@tropicana:~# perf trace -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 trace record': - - # perf trace record syscall-counts - -and display the output using 'perf trace report': - - # perf trace 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 trace -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-trace.py script, while not interesting for its results, -attempts to exercise all of the main scripting features. - -EVENT HANDLERS --------------- - -When perf trace 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 trace 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-Trace-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 trace Python modules and their associated functions. - -AVAILABLE MODULES AND FUNCTIONS -------------------------------- - -The following sections describe the functions and variables available -via the various perf trace Python modules. To use the functions and -variables from the given module, add the corresponding 'from XXXX -import' line to your perf trace 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 trace: - - 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-trace[1] diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt deleted file mode 100644 index 26aff6bf9e5..00000000000 --- a/tools/perf/Documentation/perf-trace.txt +++ /dev/null @@ -1,111 +0,0 @@ -perf-trace(1) -============= - -NAME ----- -perf-trace - Read perf.data (created by perf record) and display trace output - -SYNOPSIS --------- -[verse] -'perf trace' [<options>] -'perf trace' [<options>] record <script> [<record-options>] <command> -'perf trace' [<options>] report <script> [script-args] -'perf trace' [<options>] <script> <required-script-args> [<record-options>] <command> -'perf trace' [<options>] <top-script> [script-args] - -DESCRIPTION ------------ -This command reads the input file and displays the trace recorded. - -There are several variants of perf trace: - - 'perf trace' 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 trace -l'). The following variants allow you to - record and run those scripts: - - 'perf trace record <script> <command>' to record the events required - for 'perf trace report'. <script> is the name displayed in the - output of 'perf trace --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 trace 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 trace - 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 trace <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 trace --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 trace record' - and 'perf trace 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 trace <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 trace --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 trace - record' and 'live-mode' variants; this isn't possible however for - <top-script> 'live-mode' or 'perf trace 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-trace=:: - 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-trace.[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-trace-perl[1], -linkperf:perf-trace-python[1] diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d1db0f676a4..a9c19d01314 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -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 diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 821c1586a22..8452a2ae219 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -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, diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 55f3b5dcc73..26523c93979 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -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)) { diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c new file mode 100644 index 00000000000..492d19d8653 --- /dev/null +++ b/tools/perf/builtin-script.c @@ -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; +} diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c deleted file mode 100644 index 86cfe3800e6..00000000000 --- a/tools/perf/builtin-trace.c +++ /dev/null @@ -1,826 +0,0 @@ -#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 trace 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_trace(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 trace -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 trace_usage[] = { - "perf trace [<options>]", - "perf trace [<options>] record <script> [<record-options>] <command>", - "perf trace [<options>] report <script> [script-args]", - "perf trace [<options>] <script> [<record-options>] <command>", - "perf trace [<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-trace.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_trace(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, trace_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 trace -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" - " trace -l for available scripts.\n", argv[0]); - usage_with_options(trace_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 trace -l for available " - "scripts and options.\n", argv[0]); - usage_with_options(trace_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-trace"); - goto out; - } - - if (script_name) { - err = scripting_ops->start_script(script_name, argc, argv); - if (err) - goto out; - pr_debug("perf trace started with script %s\n\n", script_name); - } - - err = __cmd_trace(session); - - perf_session__delete(session); - cleanup_scripting(); -out: - return err; -} diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 921245b2858..c7798c7f24e 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -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); diff --git a/tools/perf/perf.c b/tools/perf/perf.c index cdd6c03f1e1..595d0f4a710 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -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 }, diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c index 957085dd5d8..315067b8f55 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/Context.c +++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c @@ -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> * diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index b059dc50cc2..93680818e24 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -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"); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 33a63252374..c6d99334bdf 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -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");