diff options
Diffstat (limited to 'tools')
124 files changed, 7962 insertions, 1480 deletions
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index b2c63309a651..6f5a498608b2 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
@@ -24,12 +24,47 @@ OPTIONS | |||
24 | --input=:: | 24 | --input=:: |
25 | Input file name. (default: perf.data) | 25 | Input file name. (default: perf.data) |
26 | 26 | ||
27 | -d:: | ||
28 | --dsos=<dso[,dso...]>:: | ||
29 | Only consider symbols in these dsos. | ||
30 | -s:: | ||
31 | --symbol=<symbol>:: | ||
32 | Symbol to annotate. | ||
33 | |||
34 | -f:: | ||
35 | --force:: | ||
36 | Don't complain, do it. | ||
37 | |||
38 | -v:: | ||
39 | --verbose:: | ||
40 | Be more verbose. (Show symbol address, etc) | ||
41 | |||
42 | -D:: | ||
43 | --dump-raw-trace:: | ||
44 | Dump raw trace in ASCII. | ||
45 | |||
46 | -k:: | ||
47 | --vmlinux=<file>:: | ||
48 | vmlinux pathname. | ||
49 | |||
50 | -m:: | ||
51 | --modules:: | ||
52 | Load module symbols. WARNING: use only with -k and LIVE kernel. | ||
53 | |||
54 | -l:: | ||
55 | --print-line:: | ||
56 | Print matching source lines (may be slow). | ||
57 | |||
58 | -P:: | ||
59 | --full-paths:: | ||
60 | Don't shorten the displayed pathnames. | ||
61 | |||
27 | --stdio:: Use the stdio interface. | 62 | --stdio:: Use the stdio interface. |
28 | 63 | ||
29 | --tui:: Use the TUI interface Use of --tui requires a tty, if one is not | 64 | --tui:: Use the TUI interface Use of --tui requires a tty, if one is not |
30 | present, as when piping to other commands, the stdio interface is | 65 | present, as when piping to other commands, the stdio interface is |
31 | used. This interfaces starts by centering on the line with more | 66 | used. This interfaces starts by centering on the line with more |
32 | samples, TAB/UNTAB cycles thru the lines with more samples. | 67 | samples, TAB/UNTAB cycles through the lines with more samples. |
33 | 68 | ||
34 | SEE ALSO | 69 | SEE ALSO |
35 | -------- | 70 | -------- |
diff --git a/tools/perf/Documentation/perf-buildid-list.txt b/tools/perf/Documentation/perf-buildid-list.txt index 01b642c0bf8f..5eaac6f26d51 100644 --- a/tools/perf/Documentation/perf-buildid-list.txt +++ b/tools/perf/Documentation/perf-buildid-list.txt | |||
@@ -18,6 +18,9 @@ perf report. | |||
18 | 18 | ||
19 | OPTIONS | 19 | OPTIONS |
20 | ------- | 20 | ------- |
21 | -H:: | ||
22 | --with-hits:: | ||
23 | Show only DSOs with hits. | ||
21 | -i:: | 24 | -i:: |
22 | --input=:: | 25 | --input=:: |
23 | Input file name. (default: perf.data) | 26 | Input file name. (default: perf.data) |
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 20d97d84ea1c..74d7481ed7a6 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -19,6 +19,18 @@ If no parameters are passed it will assume perf.data.old and perf.data. | |||
19 | 19 | ||
20 | OPTIONS | 20 | OPTIONS |
21 | ------- | 21 | ------- |
22 | -M:: | ||
23 | --displacement:: | ||
24 | Show position displacement relative to baseline. | ||
25 | |||
26 | -D:: | ||
27 | --dump-raw-trace:: | ||
28 | Dump raw trace in ASCII. | ||
29 | |||
30 | -m:: | ||
31 | --modules:: | ||
32 | Load module symbols. WARNING: use only with -k and LIVE kernel | ||
33 | |||
22 | -d:: | 34 | -d:: |
23 | --dsos=:: | 35 | --dsos=:: |
24 | Only consider symbols in these dsos. CSV that understands | 36 | Only consider symbols in these dsos. CSV that understands |
@@ -42,7 +54,7 @@ OPTIONS | |||
42 | --field-separator=:: | 54 | --field-separator=:: |
43 | 55 | ||
44 | Use a special separator character and don't pad with spaces, replacing | 56 | Use a special separator character and don't pad with spaces, replacing |
45 | all occurances of this separator in symbol names (and other output) | 57 | all occurrences of this separator in symbol names (and other output) |
46 | with a '.' character, that thus it's the only non valid separator. | 58 | with a '.' character, that thus it's the only non valid separator. |
47 | 59 | ||
48 | -v:: | 60 | -v:: |
@@ -50,6 +62,13 @@ OPTIONS | |||
50 | Be verbose, for instance, show the raw counts in addition to the | 62 | Be verbose, for instance, show the raw counts in addition to the |
51 | diff. | 63 | diff. |
52 | 64 | ||
65 | -f:: | ||
66 | --force:: | ||
67 | Don't complain, do it. | ||
68 | |||
69 | --symfs=<directory>:: | ||
70 | Look for files with symbols relative to this directory. | ||
71 | |||
53 | SEE ALSO | 72 | SEE ALSO |
54 | -------- | 73 | -------- |
55 | linkperf:perf-record[1] | 74 | linkperf:perf-record[1] |
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index d004e19fe6d6..dd84cb2f0a88 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt | |||
@@ -22,7 +22,7 @@ There are a couple of variants of perf kvm: | |||
22 | a performance counter profile of guest os in realtime | 22 | a performance counter profile of guest os in realtime |
23 | of an arbitrary workload. | 23 | of an arbitrary workload. |
24 | 24 | ||
25 | 'perf kvm record <command>' to record the performance couinter profile | 25 | 'perf kvm record <command>' to record the performance counter profile |
26 | of an arbitrary workload and save it into a perf data file. If both | 26 | of an arbitrary workload and save it into a perf data file. If both |
27 | --host and --guest are input, the perf data file name is perf.data.kvm. | 27 | --host and --guest are input, the perf data file name is perf.data.kvm. |
28 | If there is no --host but --guest, the file name is perf.data.guest. | 28 | If there is no --host but --guest, the file name is perf.data.guest. |
@@ -40,6 +40,12 @@ There are a couple of variants of perf kvm: | |||
40 | 40 | ||
41 | OPTIONS | 41 | OPTIONS |
42 | ------- | 42 | ------- |
43 | -i:: | ||
44 | --input=:: | ||
45 | Input file name. | ||
46 | -o:: | ||
47 | --output:: | ||
48 | Output file name. | ||
43 | --host=:: | 49 | --host=:: |
44 | Collect host side performance profile. | 50 | Collect host side performance profile. |
45 | --guest=:: | 51 | --guest=:: |
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index b317102138c8..921de259ea10 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt | |||
@@ -24,6 +24,21 @@ and statistics with this 'perf lock' command. | |||
24 | 24 | ||
25 | 'perf lock report' reports statistical data. | 25 | 'perf lock report' reports statistical data. |
26 | 26 | ||
27 | OPTIONS | ||
28 | ------- | ||
29 | |||
30 | -i:: | ||
31 | --input=<file>:: | ||
32 | Input file name. | ||
33 | |||
34 | -v:: | ||
35 | --verbose:: | ||
36 | Be more verbose (show symbol address, etc). | ||
37 | |||
38 | -D:: | ||
39 | --dump-raw-trace:: | ||
40 | Dump raw trace in ASCII. | ||
41 | |||
27 | SEE ALSO | 42 | SEE ALSO |
28 | -------- | 43 | -------- |
29 | linkperf:perf[1] | 44 | linkperf:perf[1] |
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 62de1b7f4e76..86b797a35aa6 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -115,9 +115,9 @@ Each probe argument follows below syntax. | |||
115 | 115 | ||
116 | LINE SYNTAX | 116 | LINE SYNTAX |
117 | ----------- | 117 | ----------- |
118 | Line range is descripted by following syntax. | 118 | Line range is described by following syntax. |
119 | 119 | ||
120 | "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]" | 120 | "FUNC[:RLN[+NUM|-RLN2]]|SRC[:ALN[+NUM|-ALN2]]" |
121 | 121 | ||
122 | FUNC specifies the function name of showing lines. 'RLN' is the start line | 122 | FUNC specifies the function name of showing lines. 'RLN' is the start line |
123 | number from function entry line, and 'RLN2' is the end line number. As same as | 123 | number from function entry line, and 'RLN2' is the end line number. As same as |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index a91f9f9e6e5c..52462ae26455 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -39,15 +39,24 @@ OPTIONS | |||
39 | be passed as follows: '\mem:addr[:[r][w][x]]'. | 39 | be passed as follows: '\mem:addr[:[r][w][x]]'. |
40 | If you want to profile read-write accesses in 0x1000, just set | 40 | If you want to profile read-write accesses in 0x1000, just set |
41 | 'mem:0x1000:rw'. | 41 | 'mem:0x1000:rw'. |
42 | |||
43 | --filter=<filter>:: | ||
44 | Event filter. | ||
45 | |||
42 | -a:: | 46 | -a:: |
43 | System-wide collection. | 47 | --all-cpus:: |
48 | System-wide collection from all CPUs. | ||
44 | 49 | ||
45 | -l:: | 50 | -l:: |
46 | Scale counter values. | 51 | Scale counter values. |
47 | 52 | ||
48 | -p:: | 53 | -p:: |
49 | --pid=:: | 54 | --pid=:: |
50 | Record events on existing pid. | 55 | Record events on existing process ID. |
56 | |||
57 | -t:: | ||
58 | --tid=:: | ||
59 | Record events on existing thread ID. | ||
51 | 60 | ||
52 | -r:: | 61 | -r:: |
53 | --realtime=:: | 62 | --realtime=:: |
@@ -99,6 +108,11 @@ OPTIONS | |||
99 | --data:: | 108 | --data:: |
100 | Sample addresses. | 109 | Sample addresses. |
101 | 110 | ||
111 | -T:: | ||
112 | --timestamp:: | ||
113 | Sample timestamps. Use it with 'perf report -D' to see the timestamps, | ||
114 | for instance. | ||
115 | |||
102 | -n:: | 116 | -n:: |
103 | --no-samples:: | 117 | --no-samples:: |
104 | Don't sample. | 118 | Don't sample. |
@@ -109,8 +123,8 @@ Collect raw sample records from all opened counters (default for tracepoint coun | |||
109 | 123 | ||
110 | -C:: | 124 | -C:: |
111 | --cpu:: | 125 | --cpu:: |
112 | Collect samples only on the list of cpus provided. Multiple CPUs can be provided as a | 126 | Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a |
113 | comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | 127 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. |
114 | In per-thread mode with inheritance mode on (default), samples are captured only when | 128 | In per-thread mode with inheritance mode on (default), samples are captured only when |
115 | the thread executes on the designated CPUs. Default is to monitor all CPUs. | 129 | the thread executes on the designated CPUs. Default is to monitor all CPUs. |
116 | 130 | ||
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 12052c9ed0ba..8ba03d6e5398 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -20,6 +20,11 @@ OPTIONS | |||
20 | -i:: | 20 | -i:: |
21 | --input=:: | 21 | --input=:: |
22 | Input file name. (default: perf.data) | 22 | Input file name. (default: perf.data) |
23 | |||
24 | -v:: | ||
25 | --verbose:: | ||
26 | Be more verbose. (show symbol address, etc) | ||
27 | |||
23 | -d:: | 28 | -d:: |
24 | --dsos=:: | 29 | --dsos=:: |
25 | Only consider symbols in these dsos. CSV that understands | 30 | Only consider symbols in these dsos. CSV that understands |
@@ -27,6 +32,10 @@ OPTIONS | |||
27 | -n:: | 32 | -n:: |
28 | --show-nr-samples:: | 33 | --show-nr-samples:: |
29 | Show the number of samples for each symbol | 34 | Show the number of samples for each symbol |
35 | |||
36 | --showcpuutilization:: | ||
37 | Show sample percentage for different cpu modes. | ||
38 | |||
30 | -T:: | 39 | -T:: |
31 | --threads:: | 40 | --threads:: |
32 | Show per-thread event counters | 41 | Show per-thread event counters |
@@ -39,12 +48,24 @@ OPTIONS | |||
39 | Only consider these symbols. CSV that understands | 48 | Only consider these symbols. CSV that understands |
40 | file://filename entries. | 49 | file://filename entries. |
41 | 50 | ||
51 | -U:: | ||
52 | --hide-unresolved:: | ||
53 | Only display entries resolved to a symbol. | ||
54 | |||
42 | -s:: | 55 | -s:: |
43 | --sort=:: | 56 | --sort=:: |
44 | Sort by key(s): pid, comm, dso, symbol, parent. | 57 | Sort by key(s): pid, comm, dso, symbol, parent. |
45 | 58 | ||
59 | -p:: | ||
60 | --parent=<regex>:: | ||
61 | regex filter to identify parent, see: '--sort parent' | ||
62 | |||
63 | -x:: | ||
64 | --exclude-other:: | ||
65 | Only display entries with parent-match. | ||
66 | |||
46 | -w:: | 67 | -w:: |
47 | --field-width=:: | 68 | --column-widths=<width[,width...]>:: |
48 | Force each column width to the provided list, for large terminal | 69 | Force each column width to the provided list, for large terminal |
49 | readability. | 70 | readability. |
50 | 71 | ||
@@ -52,19 +73,26 @@ OPTIONS | |||
52 | --field-separator=:: | 73 | --field-separator=:: |
53 | 74 | ||
54 | Use a special separator character and don't pad with spaces, replacing | 75 | Use a special separator character and don't pad with spaces, replacing |
55 | all occurances of this separator in symbol names (and other output) | 76 | all occurrences of this separator in symbol names (and other output) |
56 | with a '.' character, that thus it's the only non valid separator. | 77 | with a '.' character, that thus it's the only non valid separator. |
57 | 78 | ||
79 | -D:: | ||
80 | --dump-raw-trace:: | ||
81 | Dump raw trace in ASCII. | ||
82 | |||
58 | -g [type,min]:: | 83 | -g [type,min]:: |
59 | --call-graph:: | 84 | --call-graph:: |
60 | Display callchains using type and min percent threshold. | 85 | Display call chains using type and min percent threshold. |
61 | type can be either: | 86 | type can be either: |
62 | - flat: single column, linear exposure of callchains. | 87 | - flat: single column, linear exposure of call chains. |
63 | - graph: use a graph tree, displaying absolute overhead rates. | 88 | - graph: use a graph tree, displaying absolute overhead rates. |
64 | - fractal: like graph, but displays relative rates. Each branch of | 89 | - fractal: like graph, but displays relative rates. Each branch of |
65 | the tree is considered as a new profiled object. + | 90 | the tree is considered as a new profiled object. + |
66 | Default: fractal,0.5. | 91 | Default: fractal,0.5. |
67 | 92 | ||
93 | --pretty=<key>:: | ||
94 | Pretty printing style. key: normal, raw | ||
95 | |||
68 | --stdio:: Use the stdio interface. | 96 | --stdio:: Use the stdio interface. |
69 | 97 | ||
70 | --tui:: Use the TUI interface, that is integrated with annotate and allows | 98 | --tui:: Use the TUI interface, that is integrated with annotate and allows |
@@ -72,6 +100,25 @@ OPTIONS | |||
72 | requires a tty, if one is not present, as when piping to other | 100 | requires a tty, if one is not present, as when piping to other |
73 | commands, the stdio interface is used. | 101 | commands, the stdio interface is used. |
74 | 102 | ||
103 | -k:: | ||
104 | --vmlinux=<file>:: | ||
105 | vmlinux pathname | ||
106 | |||
107 | --kallsyms=<file>:: | ||
108 | kallsyms pathname | ||
109 | |||
110 | -m:: | ||
111 | --modules:: | ||
112 | Load module symbols. WARNING: This should only be used with -k and | ||
113 | a LIVE kernel. | ||
114 | |||
115 | -f:: | ||
116 | --force:: | ||
117 | Don't complain, do it. | ||
118 | |||
119 | --symfs=<directory>:: | ||
120 | Look for files with symbols relative to this directory. | ||
121 | |||
75 | SEE ALSO | 122 | SEE ALSO |
76 | -------- | 123 | -------- |
77 | linkperf:perf-stat[1] | 124 | linkperf:perf-stat[1] |
diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 8417644a6166..46822d5fde1c 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt | |||
@@ -8,11 +8,11 @@ perf-sched - Tool to trace/measure scheduler properties (latencies) | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf sched' {record|latency|replay|trace} | 11 | 'perf sched' {record|latency|map|replay|trace} |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | There are four variants of perf sched: | 15 | There are five variants of perf sched: |
16 | 16 | ||
17 | 'perf sched record <command>' to record the scheduling events | 17 | 'perf sched record <command>' to record the scheduling events |
18 | of an arbitrary workload. | 18 | of an arbitrary workload. |
@@ -30,8 +30,22 @@ There are four variants of perf sched: | |||
30 | of the workload as it occurred when it was recorded - and can repeat | 30 | of the workload as it occurred when it was recorded - and can repeat |
31 | it a number of times, measuring its performance.) | 31 | it a number of times, measuring its performance.) |
32 | 32 | ||
33 | 'perf sched map' to print a textual context-switching outline of | ||
34 | workload captured via perf sched record. Columns stand for | ||
35 | individual CPUs, and the two-letter shortcuts stand for tasks that | ||
36 | are running on a CPU. A '*' denotes the CPU that had the event, and | ||
37 | a dot signals an idle CPU. | ||
38 | |||
33 | OPTIONS | 39 | OPTIONS |
34 | ------- | 40 | ------- |
41 | -i:: | ||
42 | --input=<file>:: | ||
43 | Input file name. (default: perf.data) | ||
44 | |||
45 | -v:: | ||
46 | --verbose:: | ||
47 | Be more verbose. (show symbol address, etc) | ||
48 | |||
35 | -D:: | 49 | -D:: |
36 | --dump-raw-trace=:: | 50 | --dump-raw-trace=:: |
37 | Display verbose dump of the sched data. | 51 | Display verbose dump of the sched data. |
diff --git a/tools/perf/Documentation/perf-trace-perl.txt b/tools/perf/Documentation/perf-script-perl.txt index ee6525ee6d69..5bb41e55a3ac 100644 --- a/tools/perf/Documentation/perf-trace-perl.txt +++ b/tools/perf/Documentation/perf-script-perl.txt | |||
@@ -1,19 +1,19 @@ | |||
1 | perf-trace-perl(1) | 1 | perf-script-perl(1) |
2 | ================== | 2 | ================== |
3 | 3 | ||
4 | NAME | 4 | NAME |
5 | ---- | 5 | ---- |
6 | perf-trace-perl - Process trace data with a Perl script | 6 | perf-script-perl - Process trace data with a Perl script |
7 | 7 | ||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf trace' [-s [Perl]:script[.pl] ] | 11 | 'perf script' [-s [Perl]:script[.pl] ] |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | 15 | ||
16 | This perf trace option is used to process perf trace data using perf's | 16 | This perf script option is used to process perf script data using perf's |
17 | built-in Perl interpreter. It reads and processes the input file and | 17 | built-in Perl interpreter. It reads and processes the input file and |
18 | displays the results of the trace analysis implemented in the given | 18 | displays the results of the trace analysis implemented in the given |
19 | Perl script, if any. | 19 | Perl script, if any. |
@@ -21,7 +21,7 @@ Perl script, if any. | |||
21 | STARTER SCRIPTS | 21 | STARTER SCRIPTS |
22 | --------------- | 22 | --------------- |
23 | 23 | ||
24 | You can avoid reading the rest of this document by running 'perf trace | 24 | You can avoid reading the rest of this document by running 'perf script |
25 | -g perl' in the same directory as an existing perf.data trace file. | 25 | -g perl' in the same directory as an existing perf.data trace file. |
26 | That will generate a starter script containing a handler for each of | 26 | That will generate a starter script containing a handler for each of |
27 | the event types in the trace file; it simply prints every available | 27 | the event types in the trace file; it simply prints every available |
@@ -30,13 +30,13 @@ field for each event in the trace file. | |||
30 | You can also look at the existing scripts in | 30 | You can also look at the existing scripts in |
31 | ~/libexec/perf-core/scripts/perl for typical examples showing how to | 31 | ~/libexec/perf-core/scripts/perl for typical examples showing how to |
32 | do basic things like aggregate event data, print results, etc. Also, | 32 | do basic things like aggregate event data, print results, etc. Also, |
33 | the check-perf-trace.pl script, while not interesting for its results, | 33 | the check-perf-script.pl script, while not interesting for its results, |
34 | attempts to exercise all of the main scripting features. | 34 | attempts to exercise all of the main scripting features. |
35 | 35 | ||
36 | EVENT HANDLERS | 36 | EVENT HANDLERS |
37 | -------------- | 37 | -------------- |
38 | 38 | ||
39 | When perf trace is invoked using a trace script, a user-defined | 39 | When perf script is invoked using a trace script, a user-defined |
40 | 'handler function' is called for each event in the trace. If there's | 40 | 'handler function' is called for each event in the trace. If there's |
41 | no handler function defined for a given event type, the event is | 41 | no handler function defined for a given event type, the event is |
42 | ignored (or passed to a 'trace_handled' function, see below) and the | 42 | ignored (or passed to a 'trace_handled' function, see below) and the |
@@ -112,13 +112,13 @@ write a useful trace script. The sections below cover the rest. | |||
112 | SCRIPT LAYOUT | 112 | SCRIPT LAYOUT |
113 | ------------- | 113 | ------------- |
114 | 114 | ||
115 | Every perf trace Perl script should start by setting up a Perl module | 115 | Every perf script Perl script should start by setting up a Perl module |
116 | search path and 'use'ing a few support modules (see module | 116 | search path and 'use'ing a few support modules (see module |
117 | descriptions below): | 117 | descriptions below): |
118 | 118 | ||
119 | ---- | 119 | ---- |
120 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | 120 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/perf-script-Util/lib"; |
121 | use lib "./Perf-Trace-Util/lib"; | 121 | use lib "./perf-script-Util/lib"; |
122 | use Perf::Trace::Core; | 122 | use Perf::Trace::Core; |
123 | use Perf::Trace::Context; | 123 | use Perf::Trace::Context; |
124 | use Perf::Trace::Util; | 124 | use Perf::Trace::Util; |
@@ -162,7 +162,7 @@ sub trace_unhandled | |||
162 | ---- | 162 | ---- |
163 | 163 | ||
164 | The remaining sections provide descriptions of each of the available | 164 | The remaining sections provide descriptions of each of the available |
165 | built-in perf trace Perl modules and their associated functions. | 165 | built-in perf script Perl modules and their associated functions. |
166 | 166 | ||
167 | AVAILABLE MODULES AND FUNCTIONS | 167 | AVAILABLE MODULES AND FUNCTIONS |
168 | ------------------------------- | 168 | ------------------------------- |
@@ -170,7 +170,7 @@ AVAILABLE MODULES AND FUNCTIONS | |||
170 | The following sections describe the functions and variables available | 170 | The following sections describe the functions and variables available |
171 | via the various Perf::Trace::* Perl modules. To use the functions and | 171 | via the various Perf::Trace::* Perl modules. To use the functions and |
172 | variables from the given module, add the corresponding 'use | 172 | variables from the given module, add the corresponding 'use |
173 | Perf::Trace::XXX' line to your perf trace script. | 173 | Perf::Trace::XXX' line to your perf script script. |
174 | 174 | ||
175 | Perf::Trace::Core Module | 175 | Perf::Trace::Core Module |
176 | ~~~~~~~~~~~~~~~~~~~~~~~~ | 176 | ~~~~~~~~~~~~~~~~~~~~~~~~ |
@@ -204,7 +204,7 @@ argument. | |||
204 | Perf::Trace::Util Module | 204 | Perf::Trace::Util Module |
205 | ~~~~~~~~~~~~~~~~~~~~~~~~ | 205 | ~~~~~~~~~~~~~~~~~~~~~~~~ |
206 | 206 | ||
207 | Various utility functions for use with perf trace: | 207 | Various utility functions for use with perf script: |
208 | 208 | ||
209 | nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair | 209 | nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair |
210 | nsecs_secs($nsecs) - returns whole secs portion given nsecs | 210 | nsecs_secs($nsecs) - returns whole secs portion given nsecs |
@@ -214,4 +214,4 @@ Various utility functions for use with perf trace: | |||
214 | 214 | ||
215 | SEE ALSO | 215 | SEE ALSO |
216 | -------- | 216 | -------- |
217 | linkperf:perf-trace[1] | 217 | linkperf:perf-script[1] |
diff --git a/tools/perf/Documentation/perf-trace-python.txt b/tools/perf/Documentation/perf-script-python.txt index 693be804dd3d..36b38277422c 100644 --- a/tools/perf/Documentation/perf-trace-python.txt +++ b/tools/perf/Documentation/perf-script-python.txt | |||
@@ -1,19 +1,19 @@ | |||
1 | perf-trace-python(1) | 1 | perf-script-python(1) |
2 | ==================== | 2 | ==================== |
3 | 3 | ||
4 | NAME | 4 | NAME |
5 | ---- | 5 | ---- |
6 | perf-trace-python - Process trace data with a Python script | 6 | perf-script-python - Process trace data with a Python script |
7 | 7 | ||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf trace' [-s [Python]:script[.py] ] | 11 | 'perf script' [-s [Python]:script[.py] ] |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | 15 | ||
16 | This perf trace option is used to process perf trace data using perf's | 16 | This perf script option is used to process perf script data using perf's |
17 | built-in Python interpreter. It reads and processes the input file and | 17 | built-in Python interpreter. It reads and processes the input file and |
18 | displays the results of the trace analysis implemented in the given | 18 | displays the results of the trace analysis implemented in the given |
19 | Python script, if any. | 19 | Python script, if any. |
@@ -23,15 +23,15 @@ A QUICK EXAMPLE | |||
23 | 23 | ||
24 | This section shows the process, start to finish, of creating a working | 24 | This section shows the process, start to finish, of creating a working |
25 | Python script that aggregates and extracts useful information from a | 25 | Python script that aggregates and extracts useful information from a |
26 | raw perf trace stream. You can avoid reading the rest of this | 26 | raw perf script stream. You can avoid reading the rest of this |
27 | document if an example is enough for you; the rest of the document | 27 | document if an example is enough for you; the rest of the document |
28 | provides more details on each step and lists the library functions | 28 | provides more details on each step and lists the library functions |
29 | available to script writers. | 29 | available to script writers. |
30 | 30 | ||
31 | This example actually details the steps that were used to create the | 31 | This example actually details the steps that were used to create the |
32 | 'syscall-counts' script you see when you list the available perf trace | 32 | 'syscall-counts' script you see when you list the available perf script |
33 | scripts via 'perf trace -l'. As such, this script also shows how to | 33 | scripts via 'perf script -l'. As such, this script also shows how to |
34 | integrate your script into the list of general-purpose 'perf trace' | 34 | integrate your script into the list of general-purpose 'perf script' |
35 | scripts listed by that command. | 35 | scripts listed by that command. |
36 | 36 | ||
37 | The syscall-counts script is a simple script, but demonstrates all the | 37 | The syscall-counts script is a simple script, but demonstrates all the |
@@ -105,31 +105,31 @@ That single stream will be recorded in a file in the current directory | |||
105 | called perf.data. | 105 | called perf.data. |
106 | 106 | ||
107 | Once we have a perf.data file containing our data, we can use the -g | 107 | Once we have a perf.data file containing our data, we can use the -g |
108 | 'perf trace' option to generate a Python script that will contain a | 108 | 'perf script' option to generate a Python script that will contain a |
109 | callback handler for each event type found in the perf.data trace | 109 | callback handler for each event type found in the perf.data trace |
110 | stream (for more details, see the STARTER SCRIPTS section). | 110 | stream (for more details, see the STARTER SCRIPTS section). |
111 | 111 | ||
112 | ---- | 112 | ---- |
113 | # perf trace -g python | 113 | # perf script -g python |
114 | generated Python script: perf-trace.py | 114 | generated Python script: perf-script.py |
115 | 115 | ||
116 | The output file created also in the current directory is named | 116 | The output file created also in the current directory is named |
117 | perf-trace.py. Here's the file in its entirety: | 117 | perf-script.py. Here's the file in its entirety: |
118 | 118 | ||
119 | # perf trace event handlers, generated by perf trace -g python | 119 | # perf script event handlers, generated by perf script -g python |
120 | # Licensed under the terms of the GNU GPL License version 2 | 120 | # Licensed under the terms of the GNU GPL License version 2 |
121 | 121 | ||
122 | # The common_* event handler fields are the most useful fields common to | 122 | # The common_* event handler fields are the most useful fields common to |
123 | # all events. They don't necessarily correspond to the 'common_*' fields | 123 | # all events. They don't necessarily correspond to the 'common_*' fields |
124 | # in the format files. Those fields not available as handler params can | 124 | # in the format files. Those fields not available as handler params can |
125 | # be retrieved using Python functions of the form common_*(context). | 125 | # be retrieved using Python functions of the form common_*(context). |
126 | # See the perf-trace-python Documentation for the list of available functions. | 126 | # See the perf-script-python Documentation for the list of available functions. |
127 | 127 | ||
128 | import os | 128 | import os |
129 | import sys | 129 | import sys |
130 | 130 | ||
131 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | 131 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ |
132 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | 132 | '/scripts/python/perf-script-Util/lib/Perf/Trace') |
133 | 133 | ||
134 | from perf_trace_context import * | 134 | from perf_trace_context import * |
135 | from Core import * | 135 | from Core import * |
@@ -160,7 +160,7 @@ def print_header(event_name, cpu, secs, nsecs, pid, comm): | |||
160 | ---- | 160 | ---- |
161 | 161 | ||
162 | At the top is a comment block followed by some import statements and a | 162 | At the top is a comment block followed by some import statements and a |
163 | path append which every perf trace script should include. | 163 | path append which every perf script script should include. |
164 | 164 | ||
165 | Following that are a couple generated functions, trace_begin() and | 165 | Following that are a couple generated functions, trace_begin() and |
166 | trace_end(), which are called at the beginning and the end of the | 166 | trace_end(), which are called at the beginning and the end of the |
@@ -189,8 +189,8 @@ simply a utility function used for that purpose. Let's rename the | |||
189 | script and run it to see the default output: | 189 | script and run it to see the default output: |
190 | 190 | ||
191 | ---- | 191 | ---- |
192 | # mv perf-trace.py syscall-counts.py | 192 | # mv perf-script.py syscall-counts.py |
193 | # perf trace -s syscall-counts.py | 193 | # perf script -s syscall-counts.py |
194 | 194 | ||
195 | raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args= | 195 | raw_syscalls__sys_enter 1 00840.847582083 7506 perf id=1, args= |
196 | raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args= | 196 | raw_syscalls__sys_enter 1 00840.847595764 7506 perf id=1, args= |
@@ -216,7 +216,7 @@ import os | |||
216 | import sys | 216 | import sys |
217 | 217 | ||
218 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | 218 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ |
219 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | 219 | '/scripts/python/perf-script-Util/lib/Perf/Trace') |
220 | 220 | ||
221 | from perf_trace_context import * | 221 | from perf_trace_context import * |
222 | from Core import * | 222 | from Core import * |
@@ -279,7 +279,7 @@ import os | |||
279 | import sys | 279 | import sys |
280 | 280 | ||
281 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | 281 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ |
282 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | 282 | '/scripts/python/perf-script-Util/lib/Perf/Trace') |
283 | 283 | ||
284 | from perf_trace_context import * | 284 | from perf_trace_context import * |
285 | from Core import * | 285 | from Core import * |
@@ -315,7 +315,7 @@ def print_syscall_totals(): | |||
315 | 315 | ||
316 | The script can be run just as before: | 316 | The script can be run just as before: |
317 | 317 | ||
318 | # perf trace -s syscall-counts.py | 318 | # perf script -s syscall-counts.py |
319 | 319 | ||
320 | So those are the essential steps in writing and running a script. The | 320 | So those are the essential steps in writing and running a script. The |
321 | process can be generalized to any tracepoint or set of tracepoints | 321 | process can be generalized to any tracepoint or set of tracepoints |
@@ -324,17 +324,17 @@ interested in by looking at the list of available events shown by | |||
324 | 'perf list' and/or look in /sys/kernel/debug/tracing events for | 324 | 'perf list' and/or look in /sys/kernel/debug/tracing events for |
325 | detailed event and field info, record the corresponding trace data | 325 | detailed event and field info, record the corresponding trace data |
326 | using 'perf record', passing it the list of interesting events, | 326 | using 'perf record', passing it the list of interesting events, |
327 | generate a skeleton script using 'perf trace -g python' and modify the | 327 | generate a skeleton script using 'perf script -g python' and modify the |
328 | code to aggregate and display it for your particular needs. | 328 | code to aggregate and display it for your particular needs. |
329 | 329 | ||
330 | After you've done that you may end up with a general-purpose script | 330 | After you've done that you may end up with a general-purpose script |
331 | that you want to keep around and have available for future use. By | 331 | that you want to keep around and have available for future use. By |
332 | writing a couple of very simple shell scripts and putting them in the | 332 | writing a couple of very simple shell scripts and putting them in the |
333 | right place, you can have your script listed alongside the other | 333 | right place, you can have your script listed alongside the other |
334 | scripts listed by the 'perf trace -l' command e.g.: | 334 | scripts listed by the 'perf script -l' command e.g.: |
335 | 335 | ||
336 | ---- | 336 | ---- |
337 | root@tropicana:~# perf trace -l | 337 | root@tropicana:~# perf script -l |
338 | List of available trace scripts: | 338 | List of available trace scripts: |
339 | workqueue-stats workqueue stats (ins/exe/create/destroy) | 339 | workqueue-stats workqueue stats (ins/exe/create/destroy) |
340 | wakeup-latency system-wide min/max/avg wakeup latency | 340 | wakeup-latency system-wide min/max/avg wakeup latency |
@@ -365,14 +365,14 @@ perf record -a -e raw_syscalls:sys_enter | |||
365 | The 'report' script is also a shell script with the same base name as | 365 | The 'report' script is also a shell script with the same base name as |
366 | your script, but with -report appended. It should also be located in | 366 | your script, but with -report appended. It should also be located in |
367 | the perf/scripts/python/bin directory. In that script, you write the | 367 | the perf/scripts/python/bin directory. In that script, you write the |
368 | 'perf trace -s' command-line needed for running your script: | 368 | 'perf script -s' command-line needed for running your script: |
369 | 369 | ||
370 | ---- | 370 | ---- |
371 | # cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report | 371 | # cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report |
372 | 372 | ||
373 | #!/bin/bash | 373 | #!/bin/bash |
374 | # description: system-wide syscall counts | 374 | # description: system-wide syscall counts |
375 | perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts.py | 375 | perf script -s ~/libexec/perf-core/scripts/python/syscall-counts.py |
376 | ---- | 376 | ---- |
377 | 377 | ||
378 | Note that the location of the Python script given in the shell script | 378 | Note that the location of the Python script given in the shell script |
@@ -390,17 +390,17 @@ total 32 | |||
390 | drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 . | 390 | drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 . |
391 | drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 .. | 391 | drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 .. |
392 | drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin | 392 | drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin |
393 | -rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-trace.py | 393 | -rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-script.py |
394 | drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 Perf-Trace-Util | 394 | drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 perf-script-Util |
395 | -rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py | 395 | -rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py |
396 | ---- | 396 | ---- |
397 | 397 | ||
398 | Once you've done that (don't forget to do a new 'make install', | 398 | Once you've done that (don't forget to do a new 'make install', |
399 | otherwise your script won't show up at run-time), 'perf trace -l' | 399 | otherwise your script won't show up at run-time), 'perf script -l' |
400 | should show a new entry for your script: | 400 | should show a new entry for your script: |
401 | 401 | ||
402 | ---- | 402 | ---- |
403 | root@tropicana:~# perf trace -l | 403 | root@tropicana:~# perf script -l |
404 | List of available trace scripts: | 404 | List of available trace scripts: |
405 | workqueue-stats workqueue stats (ins/exe/create/destroy) | 405 | workqueue-stats workqueue stats (ins/exe/create/destroy) |
406 | wakeup-latency system-wide min/max/avg wakeup latency | 406 | wakeup-latency system-wide min/max/avg wakeup latency |
@@ -409,19 +409,19 @@ List of available trace scripts: | |||
409 | syscall-counts system-wide syscall counts | 409 | syscall-counts system-wide syscall counts |
410 | ---- | 410 | ---- |
411 | 411 | ||
412 | You can now perform the record step via 'perf trace record': | 412 | You can now perform the record step via 'perf script record': |
413 | 413 | ||
414 | # perf trace record syscall-counts | 414 | # perf script record syscall-counts |
415 | 415 | ||
416 | and display the output using 'perf trace report': | 416 | and display the output using 'perf script report': |
417 | 417 | ||
418 | # perf trace report syscall-counts | 418 | # perf script report syscall-counts |
419 | 419 | ||
420 | STARTER SCRIPTS | 420 | STARTER SCRIPTS |
421 | --------------- | 421 | --------------- |
422 | 422 | ||
423 | You can quickly get started writing a script for a particular set of | 423 | You can quickly get started writing a script for a particular set of |
424 | trace data by generating a skeleton script using 'perf trace -g | 424 | trace data by generating a skeleton script using 'perf script -g |
425 | python' in the same directory as an existing perf.data trace file. | 425 | python' in the same directory as an existing perf.data trace file. |
426 | That will generate a starter script containing a handler for each of | 426 | That will generate a starter script containing a handler for each of |
427 | the event types in the trace file; it simply prints every available | 427 | the event types in the trace file; it simply prints every available |
@@ -430,13 +430,13 @@ field for each event in the trace file. | |||
430 | You can also look at the existing scripts in | 430 | You can also look at the existing scripts in |
431 | ~/libexec/perf-core/scripts/python for typical examples showing how to | 431 | ~/libexec/perf-core/scripts/python for typical examples showing how to |
432 | do basic things like aggregate event data, print results, etc. Also, | 432 | do basic things like aggregate event data, print results, etc. Also, |
433 | the check-perf-trace.py script, while not interesting for its results, | 433 | the check-perf-script.py script, while not interesting for its results, |
434 | attempts to exercise all of the main scripting features. | 434 | attempts to exercise all of the main scripting features. |
435 | 435 | ||
436 | EVENT HANDLERS | 436 | EVENT HANDLERS |
437 | -------------- | 437 | -------------- |
438 | 438 | ||
439 | When perf trace is invoked using a trace script, a user-defined | 439 | When perf script is invoked using a trace script, a user-defined |
440 | 'handler function' is called for each event in the trace. If there's | 440 | 'handler function' is called for each event in the trace. If there's |
441 | no handler function defined for a given event type, the event is | 441 | no handler function defined for a given event type, the event is |
442 | ignored (or passed to a 'trace_handled' function, see below) and the | 442 | ignored (or passed to a 'trace_handled' function, see below) and the |
@@ -510,7 +510,7 @@ write a useful trace script. The sections below cover the rest. | |||
510 | SCRIPT LAYOUT | 510 | SCRIPT LAYOUT |
511 | ------------- | 511 | ------------- |
512 | 512 | ||
513 | Every perf trace Python script should start by setting up a Python | 513 | Every perf script Python script should start by setting up a Python |
514 | module search path and 'import'ing a few support modules (see module | 514 | module search path and 'import'ing a few support modules (see module |
515 | descriptions below): | 515 | descriptions below): |
516 | 516 | ||
@@ -519,7 +519,7 @@ descriptions below): | |||
519 | import sys | 519 | import sys |
520 | 520 | ||
521 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | 521 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ |
522 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | 522 | '/scripts/python/perf-script-Util/lib/Perf/Trace') |
523 | 523 | ||
524 | from perf_trace_context import * | 524 | from perf_trace_context import * |
525 | from Core import * | 525 | from Core import * |
@@ -559,15 +559,15 @@ def trace_unhandled(event_name, context, common_cpu, common_secs, | |||
559 | ---- | 559 | ---- |
560 | 560 | ||
561 | The remaining sections provide descriptions of each of the available | 561 | The remaining sections provide descriptions of each of the available |
562 | built-in perf trace Python modules and their associated functions. | 562 | built-in perf script Python modules and their associated functions. |
563 | 563 | ||
564 | AVAILABLE MODULES AND FUNCTIONS | 564 | AVAILABLE MODULES AND FUNCTIONS |
565 | ------------------------------- | 565 | ------------------------------- |
566 | 566 | ||
567 | The following sections describe the functions and variables available | 567 | The following sections describe the functions and variables available |
568 | via the various perf trace Python modules. To use the functions and | 568 | via the various perf script Python modules. To use the functions and |
569 | variables from the given module, add the corresponding 'from XXXX | 569 | variables from the given module, add the corresponding 'from XXXX |
570 | import' line to your perf trace script. | 570 | import' line to your perf script script. |
571 | 571 | ||
572 | Core.py Module | 572 | Core.py Module |
573 | ~~~~~~~~~~~~~~ | 573 | ~~~~~~~~~~~~~~ |
@@ -610,7 +610,7 @@ argument. | |||
610 | Util.py Module | 610 | Util.py Module |
611 | ~~~~~~~~~~~~~~ | 611 | ~~~~~~~~~~~~~~ |
612 | 612 | ||
613 | Various utility functions for use with perf trace: | 613 | Various utility functions for use with perf script: |
614 | 614 | ||
615 | nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair | 615 | nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair |
616 | nsecs_secs(nsecs) - returns whole secs portion given nsecs | 616 | nsecs_secs(nsecs) - returns whole secs portion given nsecs |
@@ -620,4 +620,4 @@ Various utility functions for use with perf trace: | |||
620 | 620 | ||
621 | SEE ALSO | 621 | SEE ALSO |
622 | -------- | 622 | -------- |
623 | linkperf:perf-trace[1] | 623 | linkperf:perf-script[1] |
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-script.txt index 26aff6bf9e50..29ad94293cd2 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -1,71 +1,71 @@ | |||
1 | perf-trace(1) | 1 | perf-script(1) |
2 | ============= | 2 | ============= |
3 | 3 | ||
4 | NAME | 4 | NAME |
5 | ---- | 5 | ---- |
6 | perf-trace - Read perf.data (created by perf record) and display trace output | 6 | perf-script - Read perf.data (created by perf record) and display trace output |
7 | 7 | ||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf trace' [<options>] | 11 | 'perf script' [<options>] |
12 | 'perf trace' [<options>] record <script> [<record-options>] <command> | 12 | 'perf script' [<options>] record <script> [<record-options>] <command> |
13 | 'perf trace' [<options>] report <script> [script-args] | 13 | 'perf script' [<options>] report <script> [script-args] |
14 | 'perf trace' [<options>] <script> <required-script-args> [<record-options>] <command> | 14 | 'perf script' [<options>] <script> <required-script-args> [<record-options>] <command> |
15 | 'perf trace' [<options>] <top-script> [script-args] | 15 | 'perf script' [<options>] <top-script> [script-args] |
16 | 16 | ||
17 | DESCRIPTION | 17 | DESCRIPTION |
18 | ----------- | 18 | ----------- |
19 | This command reads the input file and displays the trace recorded. | 19 | This command reads the input file and displays the trace recorded. |
20 | 20 | ||
21 | There are several variants of perf trace: | 21 | There are several variants of perf script: |
22 | 22 | ||
23 | 'perf trace' to see a detailed trace of the workload that was | 23 | 'perf script' to see a detailed trace of the workload that was |
24 | recorded. | 24 | recorded. |
25 | 25 | ||
26 | You can also run a set of pre-canned scripts that aggregate and | 26 | You can also run a set of pre-canned scripts that aggregate and |
27 | summarize the raw trace data in various ways (the list of scripts is | 27 | summarize the raw trace data in various ways (the list of scripts is |
28 | available via 'perf trace -l'). The following variants allow you to | 28 | available via 'perf script -l'). The following variants allow you to |
29 | record and run those scripts: | 29 | record and run those scripts: |
30 | 30 | ||
31 | 'perf trace record <script> <command>' to record the events required | 31 | 'perf script record <script> <command>' to record the events required |
32 | for 'perf trace report'. <script> is the name displayed in the | 32 | for 'perf script report'. <script> is the name displayed in the |
33 | output of 'perf trace --list' i.e. the actual script name minus any | 33 | output of 'perf script --list' i.e. the actual script name minus any |
34 | language extension. If <command> is not specified, the events are | 34 | language extension. If <command> is not specified, the events are |
35 | recorded using the -a (system-wide) 'perf record' option. | 35 | recorded using the -a (system-wide) 'perf record' option. |
36 | 36 | ||
37 | 'perf trace report <script> [args]' to run and display the results | 37 | 'perf script report <script> [args]' to run and display the results |
38 | of <script>. <script> is the name displayed in the output of 'perf | 38 | of <script>. <script> is the name displayed in the output of 'perf |
39 | trace --list' i.e. the actual script name minus any language | 39 | trace --list' i.e. the actual script name minus any language |
40 | extension. The perf.data output from a previous run of 'perf trace | 40 | extension. The perf.data output from a previous run of 'perf script |
41 | record <script>' is used and should be present for this command to | 41 | record <script>' is used and should be present for this command to |
42 | succeed. [args] refers to the (mainly optional) args expected by | 42 | succeed. [args] refers to the (mainly optional) args expected by |
43 | the script. | 43 | the script. |
44 | 44 | ||
45 | 'perf trace <script> <required-script-args> <command>' to both | 45 | 'perf script <script> <required-script-args> <command>' to both |
46 | record the events required for <script> and to run the <script> | 46 | record the events required for <script> and to run the <script> |
47 | using 'live-mode' i.e. without writing anything to disk. <script> | 47 | using 'live-mode' i.e. without writing anything to disk. <script> |
48 | is the name displayed in the output of 'perf trace --list' i.e. the | 48 | is the name displayed in the output of 'perf script --list' i.e. the |
49 | actual script name minus any language extension. If <command> is | 49 | actual script name minus any language extension. If <command> is |
50 | not specified, the events are recorded using the -a (system-wide) | 50 | not specified, the events are recorded using the -a (system-wide) |
51 | 'perf record' option. If <script> has any required args, they | 51 | 'perf record' option. If <script> has any required args, they |
52 | should be specified before <command>. This mode doesn't allow for | 52 | should be specified before <command>. This mode doesn't allow for |
53 | optional script args to be specified; if optional script args are | 53 | optional script args to be specified; if optional script args are |
54 | desired, they can be specified using separate 'perf trace record' | 54 | desired, they can be specified using separate 'perf script record' |
55 | and 'perf trace report' commands, with the stdout of the record step | 55 | and 'perf script report' commands, with the stdout of the record step |
56 | piped to the stdin of the report script, using the '-o -' and '-i -' | 56 | piped to the stdin of the report script, using the '-o -' and '-i -' |
57 | options of the corresponding commands. | 57 | options of the corresponding commands. |
58 | 58 | ||
59 | 'perf trace <top-script>' to both record the events required for | 59 | 'perf script <top-script>' to both record the events required for |
60 | <top-script> and to run the <top-script> using 'live-mode' | 60 | <top-script> and to run the <top-script> using 'live-mode' |
61 | i.e. without writing anything to disk. <top-script> is the name | 61 | i.e. without writing anything to disk. <top-script> is the name |
62 | displayed in the output of 'perf trace --list' i.e. the actual | 62 | displayed in the output of 'perf script --list' i.e. the actual |
63 | script name minus any language extension; a <top-script> is defined | 63 | script name minus any language extension; a <top-script> is defined |
64 | as any script name ending with the string 'top'. | 64 | as any script name ending with the string 'top'. |
65 | 65 | ||
66 | [<record-options>] can be passed to the record steps of 'perf trace | 66 | [<record-options>] can be passed to the record steps of 'perf script |
67 | record' and 'live-mode' variants; this isn't possible however for | 67 | record' and 'live-mode' variants; this isn't possible however for |
68 | <top-script> 'live-mode' or 'perf trace report' variants. | 68 | <top-script> 'live-mode' or 'perf script report' variants. |
69 | 69 | ||
70 | See the 'SEE ALSO' section for links to language-specific | 70 | See the 'SEE ALSO' section for links to language-specific |
71 | information on how to write and run your own trace scripts. | 71 | information on how to write and run your own trace scripts. |
@@ -76,7 +76,7 @@ OPTIONS | |||
76 | Any command you can specify in a shell. | 76 | Any command you can specify in a shell. |
77 | 77 | ||
78 | -D:: | 78 | -D:: |
79 | --dump-raw-trace=:: | 79 | --dump-raw-script=:: |
80 | Display verbose dump of the trace data. | 80 | Display verbose dump of the trace data. |
81 | 81 | ||
82 | -L:: | 82 | -L:: |
@@ -95,7 +95,7 @@ OPTIONS | |||
95 | 95 | ||
96 | -g:: | 96 | -g:: |
97 | --gen-script=:: | 97 | --gen-script=:: |
98 | Generate perf-trace.[ext] starter script for given language, | 98 | Generate perf-script.[ext] starter script for given language, |
99 | using current perf.data. | 99 | using current perf.data. |
100 | 100 | ||
101 | -a:: | 101 | -a:: |
@@ -104,8 +104,15 @@ OPTIONS | |||
104 | normally don't - this option allows the latter to be run in | 104 | normally don't - this option allows the latter to be run in |
105 | system-wide mode. | 105 | system-wide mode. |
106 | 106 | ||
107 | -i:: | ||
108 | --input=:: | ||
109 | Input file name. | ||
110 | |||
111 | -d:: | ||
112 | --debug-mode:: | ||
113 | Do various checks like samples ordering and lost events. | ||
107 | 114 | ||
108 | SEE ALSO | 115 | SEE ALSO |
109 | -------- | 116 | -------- |
110 | linkperf:perf-record[1], linkperf:perf-trace-perl[1], | 117 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
111 | linkperf:perf-trace-python[1] | 118 | linkperf:perf-script-python[1] |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 4b3a2d46b437..b6da7affbbee 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -8,8 +8,8 @@ perf-stat - Run a command and gather performance counter statistics | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf stat' [-e <EVENT> | --event=EVENT] [-S] [-a] <command> | 11 | 'perf stat' [-e <EVENT> | --event=EVENT] [-a] <command> |
12 | 'perf stat' [-e <EVENT> | --event=EVENT] [-S] [-a] -- <command> [<options>] | 12 | 'perf stat' [-e <EVENT> | --event=EVENT] [-a] -- <command> [<options>] |
13 | 13 | ||
14 | DESCRIPTION | 14 | DESCRIPTION |
15 | ----------- | 15 | ----------- |
@@ -35,24 +35,54 @@ OPTIONS | |||
35 | child tasks do not inherit counters | 35 | child tasks do not inherit counters |
36 | -p:: | 36 | -p:: |
37 | --pid=<pid>:: | 37 | --pid=<pid>:: |
38 | stat events on existing pid | 38 | stat events on existing process id |
39 | |||
40 | -t:: | ||
41 | --tid=<tid>:: | ||
42 | stat events on existing thread id | ||
43 | |||
39 | 44 | ||
40 | -a:: | 45 | -a:: |
41 | system-wide collection | 46 | --all-cpus:: |
47 | system-wide collection from all CPUs | ||
42 | 48 | ||
43 | -c:: | 49 | -c:: |
44 | scale counter values | 50 | --scale:: |
51 | scale/normalize counter values | ||
52 | |||
53 | -r:: | ||
54 | --repeat=<n>:: | ||
55 | repeat command and print average + stddev (max: 100) | ||
45 | 56 | ||
46 | -B:: | 57 | -B:: |
58 | --big-num:: | ||
47 | print large numbers with thousands' separators according to locale | 59 | print large numbers with thousands' separators according to locale |
48 | 60 | ||
49 | -C:: | 61 | -C:: |
50 | --cpu=:: | 62 | --cpu=:: |
51 | Count only on the list of cpus provided. Multiple CPUs can be provided as a | 63 | Count only on the list of CPUs provided. Multiple CPUs can be provided as a |
52 | comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | 64 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. |
53 | In per-thread mode, this option is ignored. The -a option is still necessary | 65 | In per-thread mode, this option is ignored. The -a option is still necessary |
54 | to activate system-wide monitoring. Default is to count on all CPUs. | 66 | to activate system-wide monitoring. Default is to count on all CPUs. |
55 | 67 | ||
68 | -A:: | ||
69 | --no-aggr:: | ||
70 | Do not aggregate counts across all monitored CPUs in system-wide mode (-a). | ||
71 | This option is only valid in system-wide mode. | ||
72 | |||
73 | -n:: | ||
74 | --null:: | ||
75 | null run - don't start any counters | ||
76 | |||
77 | -v:: | ||
78 | --verbose:: | ||
79 | be more verbose (show counter open errors, etc) | ||
80 | |||
81 | -x SEP:: | ||
82 | --field-separator SEP:: | ||
83 | print counts using a CSV-style output to make it easy to import directly into | ||
84 | spreadsheets. Columns are separated by the string specified in SEP. | ||
85 | |||
56 | EXAMPLES | 86 | EXAMPLES |
57 | -------- | 87 | -------- |
58 | 88 | ||
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index 1c4b5f5b7f71..2c3b462f64b0 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt | |||
@@ -12,7 +12,7 @@ SYNOPSIS | |||
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command does assorted sanity tests, initially thru linked routines but | 15 | This command does assorted sanity tests, initially through linked routines but |
16 | also will look for a directory with more tests in the form of scripts. | 16 | also will look for a directory with more tests in the form of scripts. |
17 | 17 | ||
18 | OPTIONS | 18 | OPTIONS |
diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index 4b1788355eca..d7b79e2ba2ad 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt | |||
@@ -38,6 +38,8 @@ OPTIONS | |||
38 | --process:: | 38 | --process:: |
39 | Select the processes to display, by name or PID | 39 | Select the processes to display, by name or PID |
40 | 40 | ||
41 | --symfs=<directory>:: | ||
42 | Look for files with symbols relative to this directory. | ||
41 | 43 | ||
42 | SEE ALSO | 44 | SEE ALSO |
43 | -------- | 45 | -------- |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 1f9687663f2a..f6eb1cdafb77 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -12,7 +12,7 @@ SYNOPSIS | |||
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command generates and displays a performance counter profile in realtime. | 15 | This command generates and displays a performance counter profile in real time. |
16 | 16 | ||
17 | 17 | ||
18 | OPTIONS | 18 | OPTIONS |
@@ -27,8 +27,8 @@ OPTIONS | |||
27 | 27 | ||
28 | -C <cpu-list>:: | 28 | -C <cpu-list>:: |
29 | --cpu=<cpu>:: | 29 | --cpu=<cpu>:: |
30 | Monitor only on the list of cpus provided. Multiple CPUs can be provided as a | 30 | Monitor only on the list of CPUs provided. Multiple CPUs can be provided as a |
31 | comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | 31 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. |
32 | Default is to monitor all CPUS. | 32 | Default is to monitor all CPUS. |
33 | 33 | ||
34 | -d <seconds>:: | 34 | -d <seconds>:: |
@@ -50,6 +50,10 @@ Default is to monitor all CPUS. | |||
50 | --count-filter=<count>:: | 50 | --count-filter=<count>:: |
51 | Only display functions with more events than this. | 51 | Only display functions with more events than this. |
52 | 52 | ||
53 | -g:: | ||
54 | --group:: | ||
55 | Put the counters into a counter group. | ||
56 | |||
53 | -F <freq>:: | 57 | -F <freq>:: |
54 | --freq=<freq>:: | 58 | --freq=<freq>:: |
55 | Profile at this frequency. | 59 | Profile at this frequency. |
@@ -68,7 +72,11 @@ Default is to monitor all CPUS. | |||
68 | 72 | ||
69 | -p <pid>:: | 73 | -p <pid>:: |
70 | --pid=<pid>:: | 74 | --pid=<pid>:: |
71 | Profile events on existing pid. | 75 | Profile events on existing Process ID. |
76 | |||
77 | -t <tid>:: | ||
78 | --tid=<tid>:: | ||
79 | Profile events on existing thread ID. | ||
72 | 80 | ||
73 | -r <priority>:: | 81 | -r <priority>:: |
74 | --realtime=<priority>:: | 82 | --realtime=<priority>:: |
@@ -78,6 +86,18 @@ Default is to monitor all CPUS. | |||
78 | --sym-annotate=<symbol>:: | 86 | --sym-annotate=<symbol>:: |
79 | Annotate this symbol. | 87 | Annotate this symbol. |
80 | 88 | ||
89 | -K:: | ||
90 | --hide_kernel_symbols:: | ||
91 | Hide kernel symbols. | ||
92 | |||
93 | -U:: | ||
94 | --hide_user_symbols:: | ||
95 | Hide user symbols. | ||
96 | |||
97 | -D:: | ||
98 | --dump-symtab:: | ||
99 | Dump the symbol table used for profiling. | ||
100 | |||
81 | -v:: | 101 | -v:: |
82 | --verbose:: | 102 | --verbose:: |
83 | Be more verbose (show counter open errors, etc). | 103 | Be more verbose (show counter open errors, etc). |
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 8c7fc0c8f0b8..c12659d8cb26 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
@@ -7,6 +7,7 @@ include/linux/stringify.h | |||
7 | lib/rbtree.c | 7 | lib/rbtree.c |
8 | include/linux/swab.h | 8 | include/linux/swab.h |
9 | arch/*/include/asm/unistd*.h | 9 | arch/*/include/asm/unistd*.h |
10 | arch/*/lib/memcpy*.S | ||
10 | include/linux/poison.h | 11 | include/linux/poison.h |
11 | include/linux/magic.h | 12 | include/linux/magic.h |
12 | include/linux/hw_breakpoint.h | 13 | include/linux/hw_breakpoint.h |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d1db0f676a4b..2b5387d53ba5 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -185,7 +185,10 @@ ifeq ($(ARCH),i386) | |||
185 | ARCH := x86 | 185 | ARCH := x86 |
186 | endif | 186 | endif |
187 | ifeq ($(ARCH),x86_64) | 187 | ifeq ($(ARCH),x86_64) |
188 | RAW_ARCH := x86_64 | ||
188 | ARCH := x86 | 189 | ARCH := x86 |
190 | ARCH_CFLAGS := -DARCH_X86_64 | ||
191 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S | ||
189 | endif | 192 | endif |
190 | 193 | ||
191 | # CFLAGS and LDFLAGS are for the users to override from the command line. | 194 | # CFLAGS and LDFLAGS are for the users to override from the command line. |
@@ -224,7 +227,7 @@ ifndef PERF_DEBUG | |||
224 | CFLAGS_OPTIMIZE = -O6 | 227 | CFLAGS_OPTIMIZE = -O6 |
225 | endif | 228 | endif |
226 | 229 | ||
227 | CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) | 230 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) |
228 | EXTLIBS = -lpthread -lrt -lelf -lm | 231 | EXTLIBS = -lpthread -lrt -lelf -lm |
229 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 | 232 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 |
230 | ALL_LDFLAGS = $(LDFLAGS) | 233 | ALL_LDFLAGS = $(LDFLAGS) |
@@ -375,6 +378,7 @@ LIB_H += util/include/linux/prefetch.h | |||
375 | LIB_H += util/include/linux/rbtree.h | 378 | LIB_H += util/include/linux/rbtree.h |
376 | LIB_H += util/include/linux/string.h | 379 | LIB_H += util/include/linux/string.h |
377 | LIB_H += util/include/linux/types.h | 380 | LIB_H += util/include/linux/types.h |
381 | LIB_H += util/include/linux/linkage.h | ||
378 | LIB_H += util/include/asm/asm-offsets.h | 382 | LIB_H += util/include/asm/asm-offsets.h |
379 | LIB_H += util/include/asm/bug.h | 383 | LIB_H += util/include/asm/bug.h |
380 | LIB_H += util/include/asm/byteorder.h | 384 | LIB_H += util/include/asm/byteorder.h |
@@ -383,6 +387,8 @@ LIB_H += util/include/asm/swab.h | |||
383 | LIB_H += util/include/asm/system.h | 387 | LIB_H += util/include/asm/system.h |
384 | LIB_H += util/include/asm/uaccess.h | 388 | LIB_H += util/include/asm/uaccess.h |
385 | LIB_H += util/include/dwarf-regs.h | 389 | LIB_H += util/include/dwarf-regs.h |
390 | LIB_H += util/include/asm/dwarf2.h | ||
391 | LIB_H += util/include/asm/cpufeature.h | ||
386 | LIB_H += perf.h | 392 | LIB_H += perf.h |
387 | LIB_H += util/cache.h | 393 | LIB_H += util/cache.h |
388 | LIB_H += util/callchain.h | 394 | LIB_H += util/callchain.h |
@@ -390,6 +396,7 @@ LIB_H += util/build-id.h | |||
390 | LIB_H += util/debug.h | 396 | LIB_H += util/debug.h |
391 | LIB_H += util/debugfs.h | 397 | LIB_H += util/debugfs.h |
392 | LIB_H += util/event.h | 398 | LIB_H += util/event.h |
399 | LIB_H += util/evsel.h | ||
393 | LIB_H += util/exec_cmd.h | 400 | LIB_H += util/exec_cmd.h |
394 | LIB_H += util/types.h | 401 | LIB_H += util/types.h |
395 | LIB_H += util/levenshtein.h | 402 | LIB_H += util/levenshtein.h |
@@ -398,6 +405,7 @@ LIB_H += util/parse-options.h | |||
398 | LIB_H += util/parse-events.h | 405 | LIB_H += util/parse-events.h |
399 | LIB_H += util/quote.h | 406 | LIB_H += util/quote.h |
400 | LIB_H += util/util.h | 407 | LIB_H += util/util.h |
408 | LIB_H += util/xyarray.h | ||
401 | LIB_H += util/header.h | 409 | LIB_H += util/header.h |
402 | LIB_H += util/help.h | 410 | LIB_H += util/help.h |
403 | LIB_H += util/session.h | 411 | LIB_H += util/session.h |
@@ -417,6 +425,7 @@ LIB_H += util/probe-finder.h | |||
417 | LIB_H += util/probe-event.h | 425 | LIB_H += util/probe-event.h |
418 | LIB_H += util/pstack.h | 426 | LIB_H += util/pstack.h |
419 | LIB_H += util/cpumap.h | 427 | LIB_H += util/cpumap.h |
428 | LIB_H += $(ARCH_INCLUDE) | ||
420 | 429 | ||
421 | LIB_OBJS += $(OUTPUT)util/abspath.o | 430 | LIB_OBJS += $(OUTPUT)util/abspath.o |
422 | LIB_OBJS += $(OUTPUT)util/alias.o | 431 | LIB_OBJS += $(OUTPUT)util/alias.o |
@@ -426,6 +435,7 @@ LIB_OBJS += $(OUTPUT)util/ctype.o | |||
426 | LIB_OBJS += $(OUTPUT)util/debugfs.o | 435 | LIB_OBJS += $(OUTPUT)util/debugfs.o |
427 | LIB_OBJS += $(OUTPUT)util/environment.o | 436 | LIB_OBJS += $(OUTPUT)util/environment.o |
428 | LIB_OBJS += $(OUTPUT)util/event.o | 437 | LIB_OBJS += $(OUTPUT)util/event.o |
438 | LIB_OBJS += $(OUTPUT)util/evsel.o | ||
429 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o | 439 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o |
430 | LIB_OBJS += $(OUTPUT)util/help.o | 440 | LIB_OBJS += $(OUTPUT)util/help.o |
431 | LIB_OBJS += $(OUTPUT)util/levenshtein.o | 441 | LIB_OBJS += $(OUTPUT)util/levenshtein.o |
@@ -463,6 +473,7 @@ LIB_OBJS += $(OUTPUT)util/sort.o | |||
463 | LIB_OBJS += $(OUTPUT)util/hist.o | 473 | LIB_OBJS += $(OUTPUT)util/hist.o |
464 | LIB_OBJS += $(OUTPUT)util/probe-event.o | 474 | LIB_OBJS += $(OUTPUT)util/probe-event.o |
465 | LIB_OBJS += $(OUTPUT)util/util.o | 475 | LIB_OBJS += $(OUTPUT)util/util.o |
476 | LIB_OBJS += $(OUTPUT)util/xyarray.o | ||
466 | LIB_OBJS += $(OUTPUT)util/cpumap.o | 477 | LIB_OBJS += $(OUTPUT)util/cpumap.o |
467 | 478 | ||
468 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 479 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
@@ -472,6 +483,9 @@ BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | |||
472 | # Benchmark modules | 483 | # Benchmark modules |
473 | BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o | 484 | BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o |
474 | BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o | 485 | BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o |
486 | ifeq ($(RAW_ARCH),x86_64) | ||
487 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o | ||
488 | endif | ||
475 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o | 489 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o |
476 | 490 | ||
477 | BUILTIN_OBJS += $(OUTPUT)builtin-diff.o | 491 | BUILTIN_OBJS += $(OUTPUT)builtin-diff.o |
@@ -485,7 +499,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-report.o | |||
485 | BUILTIN_OBJS += $(OUTPUT)builtin-stat.o | 499 | BUILTIN_OBJS += $(OUTPUT)builtin-stat.o |
486 | BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o | 500 | BUILTIN_OBJS += $(OUTPUT)builtin-timechart.o |
487 | BUILTIN_OBJS += $(OUTPUT)builtin-top.o | 501 | BUILTIN_OBJS += $(OUTPUT)builtin-top.o |
488 | BUILTIN_OBJS += $(OUTPUT)builtin-trace.o | 502 | BUILTIN_OBJS += $(OUTPUT)builtin-script.o |
489 | BUILTIN_OBJS += $(OUTPUT)builtin-probe.o | 503 | BUILTIN_OBJS += $(OUTPUT)builtin-probe.o |
490 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o | 504 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o |
491 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o | 505 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o |
@@ -507,7 +521,7 @@ PERFLIBS = $(LIB_FILE) | |||
507 | -include config.mak | 521 | -include config.mak |
508 | 522 | ||
509 | ifndef NO_DWARF | 523 | ifndef NO_DWARF |
510 | FLAGS_DWARF=$(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) | 524 | FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) |
511 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) | 525 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) |
512 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); | 526 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); |
513 | NO_DWARF := 1 | 527 | NO_DWARF := 1 |
@@ -554,7 +568,7 @@ ifndef NO_DWARF | |||
554 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) | 568 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) |
555 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); | 569 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); |
556 | else | 570 | else |
557 | BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT | 571 | BASIC_CFLAGS += -DDWARF_SUPPORT |
558 | EXTLIBS += -lelf -ldw | 572 | EXTLIBS += -lelf -ldw |
559 | LIB_OBJS += $(OUTPUT)util/probe-finder.o | 573 | LIB_OBJS += $(OUTPUT)util/probe-finder.o |
560 | endif # PERF_HAVE_DWARF_REGS | 574 | endif # PERF_HAVE_DWARF_REGS |
@@ -891,13 +905,14 @@ prefix_SQ = $(subst ','\'',$(prefix)) | |||
891 | SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) | 905 | SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) |
892 | PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) | 906 | PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) |
893 | 907 | ||
894 | LIBS = $(PERFLIBS) $(EXTLIBS) | 908 | LIBS = -Wl,--whole-archive $(PERFLIBS) -Wl,--no-whole-archive $(EXTLIBS) |
895 | 909 | ||
896 | BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \ | 910 | BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \ |
897 | $(COMPAT_CFLAGS) | 911 | $(COMPAT_CFLAGS) |
898 | LIB_OBJS += $(COMPAT_OBJS) | 912 | LIB_OBJS += $(COMPAT_OBJS) |
899 | 913 | ||
900 | ALL_CFLAGS += $(BASIC_CFLAGS) | 914 | ALL_CFLAGS += $(BASIC_CFLAGS) |
915 | ALL_CFLAGS += $(ARCH_CFLAGS) | ||
901 | ALL_LDFLAGS += $(BASIC_LDFLAGS) | 916 | ALL_LDFLAGS += $(BASIC_LDFLAGS) |
902 | 917 | ||
903 | export TAR INSTALL DESTDIR SHELL_PATH | 918 | export TAR INSTALL DESTDIR SHELL_PATH |
diff --git a/tools/perf/arch/s390/Makefile b/tools/perf/arch/s390/Makefile new file mode 100644 index 000000000000..15130b50dfe3 --- /dev/null +++ b/tools/perf/arch/s390/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | ifndef NO_DWARF | ||
2 | PERF_HAVE_DWARF_REGS := 1 | ||
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | ||
4 | endif | ||
diff --git a/tools/perf/arch/s390/util/dwarf-regs.c b/tools/perf/arch/s390/util/dwarf-regs.c new file mode 100644 index 000000000000..e19653e025fa --- /dev/null +++ b/tools/perf/arch/s390/util/dwarf-regs.c | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * Mapping of DWARF debug register numbers into register names. | ||
3 | * | ||
4 | * Copyright IBM Corp. 2010 | ||
5 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #include <libio.h> | ||
10 | #include <dwarf-regs.h> | ||
11 | |||
12 | #define NUM_GPRS 16 | ||
13 | |||
14 | static const char *gpr_names[NUM_GPRS] = { | ||
15 | "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", | ||
16 | "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", | ||
17 | }; | ||
18 | |||
19 | const char *get_arch_regstr(unsigned int n) | ||
20 | { | ||
21 | return (n >= NUM_GPRS) ? NULL : gpr_names[n]; | ||
22 | } | ||
diff --git a/tools/perf/bench/mem-memcpy-arch.h b/tools/perf/bench/mem-memcpy-arch.h new file mode 100644 index 000000000000..a72e36cb5394 --- /dev/null +++ b/tools/perf/bench/mem-memcpy-arch.h | |||
@@ -0,0 +1,12 @@ | |||
1 | |||
2 | #ifdef ARCH_X86_64 | ||
3 | |||
4 | #define MEMCPY_FN(fn, name, desc) \ | ||
5 | extern void *fn(void *, const void *, size_t); | ||
6 | |||
7 | #include "mem-memcpy-x86-64-asm-def.h" | ||
8 | |||
9 | #undef MEMCPY_FN | ||
10 | |||
11 | #endif | ||
12 | |||
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm-def.h b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h new file mode 100644 index 000000000000..d588b87696fc --- /dev/null +++ b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h | |||
@@ -0,0 +1,4 @@ | |||
1 | |||
2 | MEMCPY_FN(__memcpy, | ||
3 | "x86-64-unrolled", | ||
4 | "unrolled memcpy() in arch/x86/lib/memcpy_64.S") | ||
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm.S b/tools/perf/bench/mem-memcpy-x86-64-asm.S new file mode 100644 index 000000000000..a57b66e853c2 --- /dev/null +++ b/tools/perf/bench/mem-memcpy-x86-64-asm.S | |||
@@ -0,0 +1,2 @@ | |||
1 | |||
2 | #include "../../../arch/x86/lib/memcpy_64.S" | ||
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 38dae7465142..db82021f4b91 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "../util/parse-options.h" | 12 | #include "../util/parse-options.h" |
13 | #include "../util/header.h" | 13 | #include "../util/header.h" |
14 | #include "bench.h" | 14 | #include "bench.h" |
15 | #include "mem-memcpy-arch.h" | ||
15 | 16 | ||
16 | #include <stdio.h> | 17 | #include <stdio.h> |
17 | #include <stdlib.h> | 18 | #include <stdlib.h> |
@@ -23,8 +24,10 @@ | |||
23 | 24 | ||
24 | static const char *length_str = "1MB"; | 25 | static const char *length_str = "1MB"; |
25 | static const char *routine = "default"; | 26 | static const char *routine = "default"; |
26 | static bool use_clock = false; | 27 | static bool use_clock; |
27 | static int clock_fd; | 28 | static int clock_fd; |
29 | static bool only_prefault; | ||
30 | static bool no_prefault; | ||
28 | 31 | ||
29 | static const struct option options[] = { | 32 | static const struct option options[] = { |
30 | OPT_STRING('l', "length", &length_str, "1MB", | 33 | OPT_STRING('l', "length", &length_str, "1MB", |
@@ -34,19 +37,33 @@ static const struct option options[] = { | |||
34 | "Specify routine to copy"), | 37 | "Specify routine to copy"), |
35 | OPT_BOOLEAN('c', "clock", &use_clock, | 38 | OPT_BOOLEAN('c', "clock", &use_clock, |
36 | "Use CPU clock for measuring"), | 39 | "Use CPU clock for measuring"), |
40 | OPT_BOOLEAN('o', "only-prefault", &only_prefault, | ||
41 | "Show only the result with page faults before memcpy()"), | ||
42 | OPT_BOOLEAN('n', "no-prefault", &no_prefault, | ||
43 | "Show only the result without page faults before memcpy()"), | ||
37 | OPT_END() | 44 | OPT_END() |
38 | }; | 45 | }; |
39 | 46 | ||
47 | typedef void *(*memcpy_t)(void *, const void *, size_t); | ||
48 | |||
40 | struct routine { | 49 | struct routine { |
41 | const char *name; | 50 | const char *name; |
42 | const char *desc; | 51 | const char *desc; |
43 | void * (*fn)(void *dst, const void *src, size_t len); | 52 | memcpy_t fn; |
44 | }; | 53 | }; |
45 | 54 | ||
46 | struct routine routines[] = { | 55 | struct routine routines[] = { |
47 | { "default", | 56 | { "default", |
48 | "Default memcpy() provided by glibc", | 57 | "Default memcpy() provided by glibc", |
49 | memcpy }, | 58 | memcpy }, |
59 | #ifdef ARCH_X86_64 | ||
60 | |||
61 | #define MEMCPY_FN(fn, name, desc) { name, desc, fn }, | ||
62 | #include "mem-memcpy-x86-64-asm-def.h" | ||
63 | #undef MEMCPY_FN | ||
64 | |||
65 | #endif | ||
66 | |||
50 | { NULL, | 67 | { NULL, |
51 | NULL, | 68 | NULL, |
52 | NULL } | 69 | NULL } |
@@ -89,29 +106,98 @@ static double timeval2double(struct timeval *ts) | |||
89 | (double)ts->tv_usec / (double)1000000; | 106 | (double)ts->tv_usec / (double)1000000; |
90 | } | 107 | } |
91 | 108 | ||
109 | static void alloc_mem(void **dst, void **src, size_t length) | ||
110 | { | ||
111 | *dst = zalloc(length); | ||
112 | if (!dst) | ||
113 | die("memory allocation failed - maybe length is too large?\n"); | ||
114 | |||
115 | *src = zalloc(length); | ||
116 | if (!src) | ||
117 | die("memory allocation failed - maybe length is too large?\n"); | ||
118 | } | ||
119 | |||
120 | static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault) | ||
121 | { | ||
122 | u64 clock_start = 0ULL, clock_end = 0ULL; | ||
123 | void *src = NULL, *dst = NULL; | ||
124 | |||
125 | alloc_mem(&src, &dst, len); | ||
126 | |||
127 | if (prefault) | ||
128 | fn(dst, src, len); | ||
129 | |||
130 | clock_start = get_clock(); | ||
131 | fn(dst, src, len); | ||
132 | clock_end = get_clock(); | ||
133 | |||
134 | free(src); | ||
135 | free(dst); | ||
136 | return clock_end - clock_start; | ||
137 | } | ||
138 | |||
139 | static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) | ||
140 | { | ||
141 | struct timeval tv_start, tv_end, tv_diff; | ||
142 | void *src = NULL, *dst = NULL; | ||
143 | |||
144 | alloc_mem(&src, &dst, len); | ||
145 | |||
146 | if (prefault) | ||
147 | fn(dst, src, len); | ||
148 | |||
149 | BUG_ON(gettimeofday(&tv_start, NULL)); | ||
150 | fn(dst, src, len); | ||
151 | BUG_ON(gettimeofday(&tv_end, NULL)); | ||
152 | |||
153 | timersub(&tv_end, &tv_start, &tv_diff); | ||
154 | |||
155 | free(src); | ||
156 | free(dst); | ||
157 | return (double)((double)len / timeval2double(&tv_diff)); | ||
158 | } | ||
159 | |||
160 | #define pf (no_prefault ? 0 : 1) | ||
161 | |||
162 | #define print_bps(x) do { \ | ||
163 | if (x < K) \ | ||
164 | printf(" %14lf B/Sec", x); \ | ||
165 | else if (x < K * K) \ | ||
166 | printf(" %14lfd KB/Sec", x / K); \ | ||
167 | else if (x < K * K * K) \ | ||
168 | printf(" %14lf MB/Sec", x / K / K); \ | ||
169 | else \ | ||
170 | printf(" %14lf GB/Sec", x / K / K / K); \ | ||
171 | } while (0) | ||
172 | |||
92 | int bench_mem_memcpy(int argc, const char **argv, | 173 | int bench_mem_memcpy(int argc, const char **argv, |
93 | const char *prefix __used) | 174 | const char *prefix __used) |
94 | { | 175 | { |
95 | int i; | 176 | int i; |
96 | void *dst, *src; | 177 | size_t len; |
97 | size_t length; | 178 | double result_bps[2]; |
98 | double bps = 0.0; | 179 | u64 result_clock[2]; |
99 | struct timeval tv_start, tv_end, tv_diff; | ||
100 | u64 clock_start, clock_end, clock_diff; | ||
101 | 180 | ||
102 | clock_start = clock_end = clock_diff = 0ULL; | ||
103 | argc = parse_options(argc, argv, options, | 181 | argc = parse_options(argc, argv, options, |
104 | bench_mem_memcpy_usage, 0); | 182 | bench_mem_memcpy_usage, 0); |
105 | 183 | ||
106 | tv_diff.tv_sec = 0; | 184 | if (use_clock) |
107 | tv_diff.tv_usec = 0; | 185 | init_clock(); |
108 | length = (size_t)perf_atoll((char *)length_str); | 186 | |
187 | len = (size_t)perf_atoll((char *)length_str); | ||
109 | 188 | ||
110 | if ((s64)length <= 0) { | 189 | result_clock[0] = result_clock[1] = 0ULL; |
190 | result_bps[0] = result_bps[1] = 0.0; | ||
191 | |||
192 | if ((s64)len <= 0) { | ||
111 | fprintf(stderr, "Invalid length:%s\n", length_str); | 193 | fprintf(stderr, "Invalid length:%s\n", length_str); |
112 | return 1; | 194 | return 1; |
113 | } | 195 | } |
114 | 196 | ||
197 | /* same to without specifying either of prefault and no-prefault */ | ||
198 | if (only_prefault && no_prefault) | ||
199 | only_prefault = no_prefault = false; | ||
200 | |||
115 | for (i = 0; routines[i].name; i++) { | 201 | for (i = 0; routines[i].name; i++) { |
116 | if (!strcmp(routines[i].name, routine)) | 202 | if (!strcmp(routines[i].name, routine)) |
117 | break; | 203 | break; |
@@ -126,61 +212,80 @@ int bench_mem_memcpy(int argc, const char **argv, | |||
126 | return 1; | 212 | return 1; |
127 | } | 213 | } |
128 | 214 | ||
129 | dst = zalloc(length); | 215 | if (bench_format == BENCH_FORMAT_DEFAULT) |
130 | if (!dst) | 216 | printf("# Copying %s Bytes ...\n\n", length_str); |
131 | die("memory allocation failed - maybe length is too large?\n"); | ||
132 | |||
133 | src = zalloc(length); | ||
134 | if (!src) | ||
135 | die("memory allocation failed - maybe length is too large?\n"); | ||
136 | |||
137 | if (bench_format == BENCH_FORMAT_DEFAULT) { | ||
138 | printf("# Copying %s Bytes from %p to %p ...\n\n", | ||
139 | length_str, src, dst); | ||
140 | } | ||
141 | |||
142 | if (use_clock) { | ||
143 | init_clock(); | ||
144 | clock_start = get_clock(); | ||
145 | } else { | ||
146 | BUG_ON(gettimeofday(&tv_start, NULL)); | ||
147 | } | ||
148 | |||
149 | routines[i].fn(dst, src, length); | ||
150 | 217 | ||
151 | if (use_clock) { | 218 | if (!only_prefault && !no_prefault) { |
152 | clock_end = get_clock(); | 219 | /* show both of results */ |
153 | clock_diff = clock_end - clock_start; | 220 | if (use_clock) { |
221 | result_clock[0] = | ||
222 | do_memcpy_clock(routines[i].fn, len, false); | ||
223 | result_clock[1] = | ||
224 | do_memcpy_clock(routines[i].fn, len, true); | ||
225 | } else { | ||
226 | result_bps[0] = | ||
227 | do_memcpy_gettimeofday(routines[i].fn, | ||
228 | len, false); | ||
229 | result_bps[1] = | ||
230 | do_memcpy_gettimeofday(routines[i].fn, | ||
231 | len, true); | ||
232 | } | ||
154 | } else { | 233 | } else { |
155 | BUG_ON(gettimeofday(&tv_end, NULL)); | 234 | if (use_clock) { |
156 | timersub(&tv_end, &tv_start, &tv_diff); | 235 | result_clock[pf] = |
157 | bps = (double)((double)length / timeval2double(&tv_diff)); | 236 | do_memcpy_clock(routines[i].fn, |
237 | len, only_prefault); | ||
238 | } else { | ||
239 | result_bps[pf] = | ||
240 | do_memcpy_gettimeofday(routines[i].fn, | ||
241 | len, only_prefault); | ||
242 | } | ||
158 | } | 243 | } |
159 | 244 | ||
160 | switch (bench_format) { | 245 | switch (bench_format) { |
161 | case BENCH_FORMAT_DEFAULT: | 246 | case BENCH_FORMAT_DEFAULT: |
162 | if (use_clock) { | 247 | if (!only_prefault && !no_prefault) { |
163 | printf(" %14lf Clock/Byte\n", | 248 | if (use_clock) { |
164 | (double)clock_diff / (double)length); | 249 | printf(" %14lf Clock/Byte\n", |
165 | } else { | 250 | (double)result_clock[0] |
166 | if (bps < K) | 251 | / (double)len); |
167 | printf(" %14lf B/Sec\n", bps); | 252 | printf(" %14lf Clock/Byte (with prefault)\n", |
168 | else if (bps < K * K) | 253 | (double)result_clock[1] |
169 | printf(" %14lfd KB/Sec\n", bps / 1024); | 254 | / (double)len); |
170 | else if (bps < K * K * K) | 255 | } else { |
171 | printf(" %14lf MB/Sec\n", bps / 1024 / 1024); | 256 | print_bps(result_bps[0]); |
172 | else { | 257 | printf("\n"); |
173 | printf(" %14lf GB/Sec\n", | 258 | print_bps(result_bps[1]); |
174 | bps / 1024 / 1024 / 1024); | 259 | printf(" (with prefault)\n"); |
175 | } | 260 | } |
261 | } else { | ||
262 | if (use_clock) { | ||
263 | printf(" %14lf Clock/Byte", | ||
264 | (double)result_clock[pf] | ||
265 | / (double)len); | ||
266 | } else | ||
267 | print_bps(result_bps[pf]); | ||
268 | |||
269 | printf("%s\n", only_prefault ? " (with prefault)" : ""); | ||
176 | } | 270 | } |
177 | break; | 271 | break; |
178 | case BENCH_FORMAT_SIMPLE: | 272 | case BENCH_FORMAT_SIMPLE: |
179 | if (use_clock) { | 273 | if (!only_prefault && !no_prefault) { |
180 | printf("%14lf\n", | 274 | if (use_clock) { |
181 | (double)clock_diff / (double)length); | 275 | printf("%lf %lf\n", |
182 | } else | 276 | (double)result_clock[0] / (double)len, |
183 | printf("%lf\n", bps); | 277 | (double)result_clock[1] / (double)len); |
278 | } else { | ||
279 | printf("%lf %lf\n", | ||
280 | result_bps[0], result_bps[1]); | ||
281 | } | ||
282 | } else { | ||
283 | if (use_clock) { | ||
284 | printf("%lf\n", (double)result_clock[pf] | ||
285 | / (double)len); | ||
286 | } else | ||
287 | printf("%lf\n", result_bps[pf]); | ||
288 | } | ||
184 | break; | 289 | break; |
185 | default: | 290 | default: |
186 | /* reaching this means there's some disaster: */ | 291 | /* reaching this means there's some disaster: */ |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 6d5604d8df95..c056cdc06912 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -58,12 +58,12 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) | |||
58 | return hist_entry__inc_addr_samples(he, al->addr); | 58 | return hist_entry__inc_addr_samples(he, al->addr); |
59 | } | 59 | } |
60 | 60 | ||
61 | static int process_sample_event(event_t *event, struct perf_session *session) | 61 | static int process_sample_event(event_t *event, struct sample_data *sample, |
62 | struct perf_session *session) | ||
62 | { | 63 | { |
63 | struct addr_location al; | 64 | struct addr_location al; |
64 | struct sample_data data; | ||
65 | 65 | ||
66 | if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { | 66 | if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { |
67 | pr_warning("problem processing %d event, skipping it.\n", | 67 | pr_warning("problem processing %d event, skipping it.\n", |
68 | event->header.type); | 68 | event->header.type); |
69 | return -1; | 69 | return -1; |
@@ -375,6 +375,8 @@ static struct perf_event_ops event_ops = { | |||
375 | .mmap = event__process_mmap, | 375 | .mmap = event__process_mmap, |
376 | .comm = event__process_comm, | 376 | .comm = event__process_comm, |
377 | .fork = event__process_task, | 377 | .fork = event__process_task, |
378 | .ordered_samples = true, | ||
379 | .ordering_requires_timestamps = true, | ||
378 | }; | 380 | }; |
379 | 381 | ||
380 | static int __cmd_annotate(void) | 382 | static int __cmd_annotate(void) |
@@ -382,7 +384,7 @@ static int __cmd_annotate(void) | |||
382 | int ret; | 384 | int ret; |
383 | struct perf_session *session; | 385 | struct perf_session *session; |
384 | 386 | ||
385 | session = perf_session__new(input_name, O_RDONLY, force, false); | 387 | session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); |
386 | if (session == NULL) | 388 | if (session == NULL) |
387 | return -ENOMEM; | 389 | return -ENOMEM; |
388 | 390 | ||
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index c49837de7d3f..5af32ae9031e 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -38,7 +38,8 @@ static int __cmd_buildid_list(void) | |||
38 | { | 38 | { |
39 | struct perf_session *session; | 39 | struct perf_session *session; |
40 | 40 | ||
41 | session = perf_session__new(input_name, O_RDONLY, force, false); | 41 | session = perf_session__new(input_name, O_RDONLY, force, false, |
42 | &build_id__mark_dso_hit_ops); | ||
42 | if (session == NULL) | 43 | if (session == NULL) |
43 | return -1; | 44 | return -1; |
44 | 45 | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index fca1d4402910..3153e492dbcc 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -30,12 +30,13 @@ static int hists__add_entry(struct hists *self, | |||
30 | return -ENOMEM; | 30 | return -ENOMEM; |
31 | } | 31 | } |
32 | 32 | ||
33 | static int diff__process_sample_event(event_t *event, struct perf_session *session) | 33 | static int diff__process_sample_event(event_t *event, |
34 | struct sample_data *sample, | ||
35 | struct perf_session *session) | ||
34 | { | 36 | { |
35 | struct addr_location al; | 37 | struct addr_location al; |
36 | struct sample_data data = { .period = 1, }; | ||
37 | 38 | ||
38 | if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { | 39 | if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { |
39 | pr_warning("problem processing %d event, skipping it.\n", | 40 | pr_warning("problem processing %d event, skipping it.\n", |
40 | event->header.type); | 41 | event->header.type); |
41 | return -1; | 42 | return -1; |
@@ -44,12 +45,12 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi | |||
44 | if (al.filtered || al.sym == NULL) | 45 | if (al.filtered || al.sym == NULL) |
45 | return 0; | 46 | return 0; |
46 | 47 | ||
47 | if (hists__add_entry(&session->hists, &al, data.period)) { | 48 | if (hists__add_entry(&session->hists, &al, sample->period)) { |
48 | pr_warning("problem incrementing symbol period, skipping event\n"); | 49 | pr_warning("problem incrementing symbol period, skipping event\n"); |
49 | return -1; | 50 | return -1; |
50 | } | 51 | } |
51 | 52 | ||
52 | session->hists.stats.total_period += data.period; | 53 | session->hists.stats.total_period += sample->period; |
53 | return 0; | 54 | return 0; |
54 | } | 55 | } |
55 | 56 | ||
@@ -60,6 +61,8 @@ static struct perf_event_ops event_ops = { | |||
60 | .exit = event__process_task, | 61 | .exit = event__process_task, |
61 | .fork = event__process_task, | 62 | .fork = event__process_task, |
62 | .lost = event__process_lost, | 63 | .lost = event__process_lost, |
64 | .ordered_samples = true, | ||
65 | .ordering_requires_timestamps = true, | ||
63 | }; | 66 | }; |
64 | 67 | ||
65 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, | 68 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, |
@@ -141,8 +144,8 @@ static int __cmd_diff(void) | |||
141 | int ret, i; | 144 | int ret, i; |
142 | struct perf_session *session[2]; | 145 | struct perf_session *session[2]; |
143 | 146 | ||
144 | session[0] = perf_session__new(input_old, O_RDONLY, force, false); | 147 | session[0] = perf_session__new(input_old, O_RDONLY, force, false, &event_ops); |
145 | session[1] = perf_session__new(input_new, O_RDONLY, force, false); | 148 | session[1] = perf_session__new(input_new, O_RDONLY, force, false, &event_ops); |
146 | if (session[0] == NULL || session[1] == NULL) | 149 | if (session[0] == NULL || session[1] == NULL) |
147 | return -ENOMEM; | 150 | return -ENOMEM; |
148 | 151 | ||
@@ -173,7 +176,7 @@ static const char * const diff_usage[] = { | |||
173 | static const struct option options[] = { | 176 | static const struct option options[] = { |
174 | OPT_INCR('v', "verbose", &verbose, | 177 | OPT_INCR('v', "verbose", &verbose, |
175 | "be more verbose (show symbol address, etc)"), | 178 | "be more verbose (show symbol address, etc)"), |
176 | OPT_BOOLEAN('m', "displacement", &show_displacement, | 179 | OPT_BOOLEAN('M', "displacement", &show_displacement, |
177 | "Show position displacement relative to baseline"), | 180 | "Show position displacement relative to baseline"), |
178 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 181 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
179 | "dump raw trace in ASCII"), | 182 | "dump raw trace in ASCII"), |
@@ -191,6 +194,8 @@ static const struct option options[] = { | |||
191 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", | 194 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", |
192 | "separator for columns, no spaces will be added between " | 195 | "separator for columns, no spaces will be added between " |
193 | "columns '.' is reserved."), | 196 | "columns '.' is reserved."), |
197 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | ||
198 | "Look for files with symbols relative to this directory"), | ||
194 | OPT_END() | 199 | OPT_END() |
195 | }; | 200 | }; |
196 | 201 | ||
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 8e3e47b064ce..0c78ffa7bf67 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -16,8 +16,8 @@ | |||
16 | static char const *input_name = "-"; | 16 | static char const *input_name = "-"; |
17 | static bool inject_build_ids; | 17 | static bool inject_build_ids; |
18 | 18 | ||
19 | static int event__repipe(event_t *event __used, | 19 | static int event__repipe_synth(event_t *event, |
20 | struct perf_session *session __used) | 20 | struct perf_session *session __used) |
21 | { | 21 | { |
22 | uint32_t size; | 22 | uint32_t size; |
23 | void *buf = event; | 23 | void *buf = event; |
@@ -36,22 +36,30 @@ static int event__repipe(event_t *event __used, | |||
36 | return 0; | 36 | return 0; |
37 | } | 37 | } |
38 | 38 | ||
39 | static int event__repipe_mmap(event_t *self, struct perf_session *session) | 39 | static int event__repipe(event_t *event, struct sample_data *sample __used, |
40 | struct perf_session *session) | ||
41 | { | ||
42 | return event__repipe_synth(event, session); | ||
43 | } | ||
44 | |||
45 | static int event__repipe_mmap(event_t *self, struct sample_data *sample, | ||
46 | struct perf_session *session) | ||
40 | { | 47 | { |
41 | int err; | 48 | int err; |
42 | 49 | ||
43 | err = event__process_mmap(self, session); | 50 | err = event__process_mmap(self, sample, session); |
44 | event__repipe(self, session); | 51 | event__repipe(self, sample, session); |
45 | 52 | ||
46 | return err; | 53 | return err; |
47 | } | 54 | } |
48 | 55 | ||
49 | static int event__repipe_task(event_t *self, struct perf_session *session) | 56 | static int event__repipe_task(event_t *self, struct sample_data *sample, |
57 | struct perf_session *session) | ||
50 | { | 58 | { |
51 | int err; | 59 | int err; |
52 | 60 | ||
53 | err = event__process_task(self, session); | 61 | err = event__process_task(self, sample, session); |
54 | event__repipe(self, session); | 62 | event__repipe(self, sample, session); |
55 | 63 | ||
56 | return err; | 64 | return err; |
57 | } | 65 | } |
@@ -61,7 +69,7 @@ static int event__repipe_tracing_data(event_t *self, | |||
61 | { | 69 | { |
62 | int err; | 70 | int err; |
63 | 71 | ||
64 | event__repipe(self, session); | 72 | event__repipe_synth(self, session); |
65 | err = event__process_tracing_data(self, session); | 73 | err = event__process_tracing_data(self, session); |
66 | 74 | ||
67 | return err; | 75 | return err; |
@@ -111,7 +119,8 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session) | |||
111 | return 0; | 119 | return 0; |
112 | } | 120 | } |
113 | 121 | ||
114 | static int event__inject_buildid(event_t *event, struct perf_session *session) | 122 | static int event__inject_buildid(event_t *event, struct sample_data *sample, |
123 | struct perf_session *session) | ||
115 | { | 124 | { |
116 | struct addr_location al; | 125 | struct addr_location al; |
117 | struct thread *thread; | 126 | struct thread *thread; |
@@ -146,7 +155,7 @@ static int event__inject_buildid(event_t *event, struct perf_session *session) | |||
146 | } | 155 | } |
147 | 156 | ||
148 | repipe: | 157 | repipe: |
149 | event__repipe(event, session); | 158 | event__repipe(event, sample, session); |
150 | return 0; | 159 | return 0; |
151 | } | 160 | } |
152 | 161 | ||
@@ -160,10 +169,10 @@ struct perf_event_ops inject_ops = { | |||
160 | .read = event__repipe, | 169 | .read = event__repipe, |
161 | .throttle = event__repipe, | 170 | .throttle = event__repipe, |
162 | .unthrottle = event__repipe, | 171 | .unthrottle = event__repipe, |
163 | .attr = event__repipe, | 172 | .attr = event__repipe_synth, |
164 | .event_type = event__repipe, | 173 | .event_type = event__repipe_synth, |
165 | .tracing_data = event__repipe, | 174 | .tracing_data = event__repipe_synth, |
166 | .build_id = event__repipe, | 175 | .build_id = event__repipe_synth, |
167 | }; | 176 | }; |
168 | 177 | ||
169 | extern volatile int session_done; | 178 | extern volatile int session_done; |
@@ -187,7 +196,7 @@ static int __cmd_inject(void) | |||
187 | inject_ops.tracing_data = event__repipe_tracing_data; | 196 | inject_ops.tracing_data = event__repipe_tracing_data; |
188 | } | 197 | } |
189 | 198 | ||
190 | session = perf_session__new(input_name, O_RDONLY, false, true); | 199 | session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops); |
191 | if (session == NULL) | 200 | if (session == NULL) |
192 | return -ENOMEM; | 201 | return -ENOMEM; |
193 | 202 | ||
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 31f60a2535e0..def7ddc2fd4f 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -304,22 +304,11 @@ process_raw_event(event_t *raw_event __used, void *data, | |||
304 | } | 304 | } |
305 | } | 305 | } |
306 | 306 | ||
307 | static int process_sample_event(event_t *event, struct perf_session *session) | 307 | static int process_sample_event(event_t *event, struct sample_data *sample, |
308 | struct perf_session *session) | ||
308 | { | 309 | { |
309 | struct sample_data data; | 310 | struct thread *thread = perf_session__findnew(session, event->ip.pid); |
310 | struct thread *thread; | ||
311 | 311 | ||
312 | memset(&data, 0, sizeof(data)); | ||
313 | data.time = -1; | ||
314 | data.cpu = -1; | ||
315 | data.period = 1; | ||
316 | |||
317 | event__parse_sample(event, session->sample_type, &data); | ||
318 | |||
319 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, | ||
320 | data.pid, data.tid, data.ip, data.period); | ||
321 | |||
322 | thread = perf_session__findnew(session, event->ip.pid); | ||
323 | if (thread == NULL) { | 312 | if (thread == NULL) { |
324 | pr_debug("problem processing %d event, skipping it.\n", | 313 | pr_debug("problem processing %d event, skipping it.\n", |
325 | event->header.type); | 314 | event->header.type); |
@@ -328,8 +317,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
328 | 317 | ||
329 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 318 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
330 | 319 | ||
331 | process_raw_event(event, data.raw_data, data.cpu, | 320 | process_raw_event(event, sample->raw_data, sample->cpu, |
332 | data.time, thread); | 321 | sample->time, thread); |
333 | 322 | ||
334 | return 0; | 323 | return 0; |
335 | } | 324 | } |
@@ -492,7 +481,8 @@ static void sort_result(void) | |||
492 | static int __cmd_kmem(void) | 481 | static int __cmd_kmem(void) |
493 | { | 482 | { |
494 | int err = -EINVAL; | 483 | int err = -EINVAL; |
495 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); | 484 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, |
485 | 0, false, &event_ops); | ||
496 | if (session == NULL) | 486 | if (session == NULL) |
497 | return -ENOMEM; | 487 | return -ENOMEM; |
498 | 488 | ||
@@ -747,6 +737,9 @@ static int __cmd_record(int argc, const char **argv) | |||
747 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | 737 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; |
748 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 738 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
749 | 739 | ||
740 | if (rec_argv == NULL) | ||
741 | return -ENOMEM; | ||
742 | |||
750 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 743 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
751 | rec_argv[i] = strdup(record_args[i]); | 744 | rec_argv[i] = strdup(record_args[i]); |
752 | 745 | ||
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 821c1586a22b..b9c6e5432971 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -834,22 +834,18 @@ static void dump_info(void) | |||
834 | die("Unknown type of information\n"); | 834 | die("Unknown type of information\n"); |
835 | } | 835 | } |
836 | 836 | ||
837 | static int process_sample_event(event_t *self, struct perf_session *s) | 837 | static int process_sample_event(event_t *self, struct sample_data *sample, |
838 | struct perf_session *s) | ||
838 | { | 839 | { |
839 | struct sample_data data; | 840 | struct thread *thread = perf_session__findnew(s, sample->tid); |
840 | struct thread *thread; | ||
841 | 841 | ||
842 | bzero(&data, sizeof(data)); | ||
843 | event__parse_sample(self, s->sample_type, &data); | ||
844 | |||
845 | thread = perf_session__findnew(s, data.tid); | ||
846 | if (thread == NULL) { | 842 | if (thread == NULL) { |
847 | pr_debug("problem processing %d event, skipping it.\n", | 843 | pr_debug("problem processing %d event, skipping it.\n", |
848 | self->header.type); | 844 | self->header.type); |
849 | return -1; | 845 | return -1; |
850 | } | 846 | } |
851 | 847 | ||
852 | process_raw_event(data.raw_data, data.cpu, data.time, thread); | 848 | process_raw_event(sample->raw_data, sample->cpu, sample->time, thread); |
853 | 849 | ||
854 | return 0; | 850 | return 0; |
855 | } | 851 | } |
@@ -862,7 +858,7 @@ static struct perf_event_ops eops = { | |||
862 | 858 | ||
863 | static int read_events(void) | 859 | static int read_events(void) |
864 | { | 860 | { |
865 | session = perf_session__new(input_name, O_RDONLY, 0, false); | 861 | session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); |
866 | if (!session) | 862 | if (!session) |
867 | die("Initializing perf session failed\n"); | 863 | die("Initializing perf session failed\n"); |
868 | 864 | ||
@@ -947,6 +943,9 @@ static int __cmd_record(int argc, const char **argv) | |||
947 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | 943 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; |
948 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 944 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
949 | 945 | ||
946 | if (rec_argv == NULL) | ||
947 | return -ENOMEM; | ||
948 | |||
950 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 949 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
951 | rec_argv[i] = strdup(record_args[i]); | 950 | rec_argv[i] = strdup(record_args[i]); |
952 | 951 | ||
@@ -982,9 +981,9 @@ int cmd_lock(int argc, const char **argv, const char *prefix __used) | |||
982 | usage_with_options(report_usage, report_options); | 981 | usage_with_options(report_usage, report_options); |
983 | } | 982 | } |
984 | __cmd_report(); | 983 | __cmd_report(); |
985 | } else if (!strcmp(argv[0], "trace")) { | 984 | } else if (!strcmp(argv[0], "script")) { |
986 | /* Aliased to 'perf trace' */ | 985 | /* Aliased to 'perf script' */ |
987 | return cmd_trace(argc, argv, prefix); | 986 | return cmd_script(argc, argv, prefix); |
988 | } else if (!strcmp(argv[0], "info")) { | 987 | } else if (!strcmp(argv[0], "info")) { |
989 | if (argc) { | 988 | if (argc) { |
990 | argc = parse_options(argc, argv, | 989 | argc = parse_options(argc, argv, |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 564491fa18b2..7069bd3e90b3 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -18,6 +18,7 @@ | |||
18 | 18 | ||
19 | #include "util/header.h" | 19 | #include "util/header.h" |
20 | #include "util/event.h" | 20 | #include "util/event.h" |
21 | #include "util/evsel.h" | ||
21 | #include "util/debug.h" | 22 | #include "util/debug.h" |
22 | #include "util/session.h" | 23 | #include "util/session.h" |
23 | #include "util/symbol.h" | 24 | #include "util/symbol.h" |
@@ -27,17 +28,18 @@ | |||
27 | #include <sched.h> | 28 | #include <sched.h> |
28 | #include <sys/mman.h> | 29 | #include <sys/mman.h> |
29 | 30 | ||
31 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
32 | |||
30 | enum write_mode_t { | 33 | enum write_mode_t { |
31 | WRITE_FORCE, | 34 | WRITE_FORCE, |
32 | WRITE_APPEND | 35 | WRITE_APPEND |
33 | }; | 36 | }; |
34 | 37 | ||
35 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; | ||
36 | |||
37 | static u64 user_interval = ULLONG_MAX; | 38 | static u64 user_interval = ULLONG_MAX; |
38 | static u64 default_interval = 0; | 39 | static u64 default_interval = 0; |
40 | static u64 sample_type; | ||
39 | 41 | ||
40 | static int nr_cpus = 0; | 42 | static struct cpu_map *cpus; |
41 | static unsigned int page_size; | 43 | static unsigned int page_size; |
42 | static unsigned int mmap_pages = 128; | 44 | static unsigned int mmap_pages = 128; |
43 | static unsigned int user_freq = UINT_MAX; | 45 | static unsigned int user_freq = UINT_MAX; |
@@ -48,11 +50,11 @@ static const char *output_name = "perf.data"; | |||
48 | static int group = 0; | 50 | static int group = 0; |
49 | static int realtime_prio = 0; | 51 | static int realtime_prio = 0; |
50 | static bool raw_samples = false; | 52 | static bool raw_samples = false; |
53 | static bool sample_id_all_avail = true; | ||
51 | static bool system_wide = false; | 54 | static bool system_wide = false; |
52 | static pid_t target_pid = -1; | 55 | static pid_t target_pid = -1; |
53 | static pid_t target_tid = -1; | 56 | static pid_t target_tid = -1; |
54 | static pid_t *all_tids = NULL; | 57 | static struct thread_map *threads; |
55 | static int thread_num = 0; | ||
56 | static pid_t child_pid = -1; | 58 | static pid_t child_pid = -1; |
57 | static bool no_inherit = false; | 59 | static bool no_inherit = false; |
58 | static enum write_mode_t write_mode = WRITE_FORCE; | 60 | static enum write_mode_t write_mode = WRITE_FORCE; |
@@ -60,7 +62,9 @@ static bool call_graph = false; | |||
60 | static bool inherit_stat = false; | 62 | static bool inherit_stat = false; |
61 | static bool no_samples = false; | 63 | static bool no_samples = false; |
62 | static bool sample_address = false; | 64 | static bool sample_address = false; |
65 | static bool sample_time = false; | ||
63 | static bool no_buildid = false; | 66 | static bool no_buildid = false; |
67 | static bool no_buildid_cache = false; | ||
64 | 68 | ||
65 | static long samples = 0; | 69 | static long samples = 0; |
66 | static u64 bytes_written = 0; | 70 | static u64 bytes_written = 0; |
@@ -77,7 +81,6 @@ static struct perf_session *session; | |||
77 | static const char *cpu_list; | 81 | static const char *cpu_list; |
78 | 82 | ||
79 | struct mmap_data { | 83 | struct mmap_data { |
80 | int counter; | ||
81 | void *base; | 84 | void *base; |
82 | unsigned int mask; | 85 | unsigned int mask; |
83 | unsigned int prev; | 86 | unsigned int prev; |
@@ -128,6 +131,7 @@ static void write_output(void *buf, size_t size) | |||
128 | } | 131 | } |
129 | 132 | ||
130 | static int process_synthesized_event(event_t *event, | 133 | static int process_synthesized_event(event_t *event, |
134 | struct sample_data *sample __used, | ||
131 | struct perf_session *self __used) | 135 | struct perf_session *self __used) |
132 | { | 136 | { |
133 | write_output(event, event->header.size); | 137 | write_output(event, event->header.size); |
@@ -224,12 +228,12 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n | |||
224 | return h_attr; | 228 | return h_attr; |
225 | } | 229 | } |
226 | 230 | ||
227 | static void create_counter(int counter, int cpu) | 231 | static void create_counter(struct perf_evsel *evsel, int cpu) |
228 | { | 232 | { |
229 | char *filter = filters[counter]; | 233 | char *filter = evsel->filter; |
230 | struct perf_event_attr *attr = attrs + counter; | 234 | struct perf_event_attr *attr = &evsel->attr; |
231 | struct perf_header_attr *h_attr; | 235 | struct perf_header_attr *h_attr; |
232 | int track = !counter; /* only the first counter needs these */ | 236 | int track = !evsel->idx; /* only the first counter needs these */ |
233 | int thread_index; | 237 | int thread_index; |
234 | int ret; | 238 | int ret; |
235 | struct { | 239 | struct { |
@@ -238,6 +242,19 @@ static void create_counter(int counter, int cpu) | |||
238 | u64 time_running; | 242 | u64 time_running; |
239 | u64 id; | 243 | u64 id; |
240 | } read_data; | 244 | } read_data; |
245 | /* | ||
246 | * Check if parse_single_tracepoint_event has already asked for | ||
247 | * PERF_SAMPLE_TIME. | ||
248 | * | ||
249 | * XXX this is kludgy but short term fix for problems introduced by | ||
250 | * eac23d1c that broke 'perf script' by having different sample_types | ||
251 | * when using multiple tracepoint events when we use a perf binary | ||
252 | * that tries to use sample_id_all on an older kernel. | ||
253 | * | ||
254 | * We need to move counter creation to perf_session, support | ||
255 | * different sample_types, etc. | ||
256 | */ | ||
257 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
241 | 258 | ||
242 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 259 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
243 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 260 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
@@ -280,6 +297,10 @@ static void create_counter(int counter, int cpu) | |||
280 | if (system_wide) | 297 | if (system_wide) |
281 | attr->sample_type |= PERF_SAMPLE_CPU; | 298 | attr->sample_type |= PERF_SAMPLE_CPU; |
282 | 299 | ||
300 | if (sample_id_all_avail && | ||
301 | (sample_time || system_wide || !no_inherit || cpu_list)) | ||
302 | attr->sample_type |= PERF_SAMPLE_TIME; | ||
303 | |||
283 | if (raw_samples) { | 304 | if (raw_samples) { |
284 | attr->sample_type |= PERF_SAMPLE_TIME; | 305 | attr->sample_type |= PERF_SAMPLE_TIME; |
285 | attr->sample_type |= PERF_SAMPLE_RAW; | 306 | attr->sample_type |= PERF_SAMPLE_RAW; |
@@ -293,13 +314,14 @@ static void create_counter(int counter, int cpu) | |||
293 | attr->disabled = 1; | 314 | attr->disabled = 1; |
294 | attr->enable_on_exec = 1; | 315 | attr->enable_on_exec = 1; |
295 | } | 316 | } |
317 | retry_sample_id: | ||
318 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
296 | 319 | ||
297 | for (thread_index = 0; thread_index < thread_num; thread_index++) { | 320 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { |
298 | try_again: | 321 | try_again: |
299 | fd[nr_cpu][counter][thread_index] = sys_perf_event_open(attr, | 322 | FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); |
300 | all_tids[thread_index], cpu, group_fd, 0); | ||
301 | 323 | ||
302 | if (fd[nr_cpu][counter][thread_index] < 0) { | 324 | if (FD(evsel, nr_cpu, thread_index) < 0) { |
303 | int err = errno; | 325 | int err = errno; |
304 | 326 | ||
305 | if (err == EPERM || err == EACCES) | 327 | if (err == EPERM || err == EACCES) |
@@ -309,6 +331,18 @@ try_again: | |||
309 | else if (err == ENODEV && cpu_list) { | 331 | else if (err == ENODEV && cpu_list) { |
310 | die("No such device - did you specify" | 332 | die("No such device - did you specify" |
311 | " an out-of-range profile CPU?\n"); | 333 | " an out-of-range profile CPU?\n"); |
334 | } else if (err == ENOENT) { | ||
335 | die("%s event is not supported. ", | ||
336 | event_name(evsel)); | ||
337 | } else if (err == EINVAL && sample_id_all_avail) { | ||
338 | /* | ||
339 | * Old kernel, no attr->sample_id_type_all field | ||
340 | */ | ||
341 | sample_id_all_avail = false; | ||
342 | if (!sample_time && !raw_samples && !time_needed) | ||
343 | attr->sample_type &= ~PERF_SAMPLE_TIME; | ||
344 | |||
345 | goto retry_sample_id; | ||
312 | } | 346 | } |
313 | 347 | ||
314 | /* | 348 | /* |
@@ -326,8 +360,8 @@ try_again: | |||
326 | goto try_again; | 360 | goto try_again; |
327 | } | 361 | } |
328 | printf("\n"); | 362 | printf("\n"); |
329 | error("perfcounter syscall returned with %d (%s)\n", | 363 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", |
330 | fd[nr_cpu][counter][thread_index], strerror(err)); | 364 | FD(evsel, nr_cpu, thread_index), strerror(err)); |
331 | 365 | ||
332 | #if defined(__i386__) || defined(__x86_64__) | 366 | #if defined(__i386__) || defined(__x86_64__) |
333 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 367 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
@@ -341,7 +375,7 @@ try_again: | |||
341 | exit(-1); | 375 | exit(-1); |
342 | } | 376 | } |
343 | 377 | ||
344 | h_attr = get_header_attr(attr, counter); | 378 | h_attr = get_header_attr(attr, evsel->idx); |
345 | if (h_attr == NULL) | 379 | if (h_attr == NULL) |
346 | die("nomem\n"); | 380 | die("nomem\n"); |
347 | 381 | ||
@@ -352,7 +386,7 @@ try_again: | |||
352 | } | 386 | } |
353 | } | 387 | } |
354 | 388 | ||
355 | if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) { | 389 | if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { |
356 | perror("Unable to read perf file descriptor"); | 390 | perror("Unable to read perf file descriptor"); |
357 | exit(-1); | 391 | exit(-1); |
358 | } | 392 | } |
@@ -362,43 +396,44 @@ try_again: | |||
362 | exit(-1); | 396 | exit(-1); |
363 | } | 397 | } |
364 | 398 | ||
365 | assert(fd[nr_cpu][counter][thread_index] >= 0); | 399 | assert(FD(evsel, nr_cpu, thread_index) >= 0); |
366 | fcntl(fd[nr_cpu][counter][thread_index], F_SETFL, O_NONBLOCK); | 400 | fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK); |
367 | 401 | ||
368 | /* | 402 | /* |
369 | * First counter acts as the group leader: | 403 | * First counter acts as the group leader: |
370 | */ | 404 | */ |
371 | if (group && group_fd == -1) | 405 | if (group && group_fd == -1) |
372 | group_fd = fd[nr_cpu][counter][thread_index]; | 406 | group_fd = FD(evsel, nr_cpu, thread_index); |
373 | 407 | ||
374 | if (counter || thread_index) { | 408 | if (evsel->idx || thread_index) { |
375 | ret = ioctl(fd[nr_cpu][counter][thread_index], | 409 | struct perf_evsel *first; |
376 | PERF_EVENT_IOC_SET_OUTPUT, | 410 | first = list_entry(evsel_list.next, struct perf_evsel, node); |
377 | fd[nr_cpu][0][0]); | 411 | ret = ioctl(FD(evsel, nr_cpu, thread_index), |
412 | PERF_EVENT_IOC_SET_OUTPUT, | ||
413 | FD(first, nr_cpu, 0)); | ||
378 | if (ret) { | 414 | if (ret) { |
379 | error("failed to set output: %d (%s)\n", errno, | 415 | error("failed to set output: %d (%s)\n", errno, |
380 | strerror(errno)); | 416 | strerror(errno)); |
381 | exit(-1); | 417 | exit(-1); |
382 | } | 418 | } |
383 | } else { | 419 | } else { |
384 | mmap_array[nr_cpu].counter = counter; | ||
385 | mmap_array[nr_cpu].prev = 0; | 420 | mmap_array[nr_cpu].prev = 0; |
386 | mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; | 421 | mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; |
387 | mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, | 422 | mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, |
388 | PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter][thread_index], 0); | 423 | PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0); |
389 | if (mmap_array[nr_cpu].base == MAP_FAILED) { | 424 | if (mmap_array[nr_cpu].base == MAP_FAILED) { |
390 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 425 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
391 | exit(-1); | 426 | exit(-1); |
392 | } | 427 | } |
393 | 428 | ||
394 | event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index]; | 429 | event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index); |
395 | event_array[nr_poll].events = POLLIN; | 430 | event_array[nr_poll].events = POLLIN; |
396 | nr_poll++; | 431 | nr_poll++; |
397 | } | 432 | } |
398 | 433 | ||
399 | if (filter != NULL) { | 434 | if (filter != NULL) { |
400 | ret = ioctl(fd[nr_cpu][counter][thread_index], | 435 | ret = ioctl(FD(evsel, nr_cpu, thread_index), |
401 | PERF_EVENT_IOC_SET_FILTER, filter); | 436 | PERF_EVENT_IOC_SET_FILTER, filter); |
402 | if (ret) { | 437 | if (ret) { |
403 | error("failed to set filter with %d (%s)\n", errno, | 438 | error("failed to set filter with %d (%s)\n", errno, |
404 | strerror(errno)); | 439 | strerror(errno)); |
@@ -406,15 +441,19 @@ try_again: | |||
406 | } | 441 | } |
407 | } | 442 | } |
408 | } | 443 | } |
444 | |||
445 | if (!sample_type) | ||
446 | sample_type = attr->sample_type; | ||
409 | } | 447 | } |
410 | 448 | ||
411 | static void open_counters(int cpu) | 449 | static void open_counters(int cpu) |
412 | { | 450 | { |
413 | int counter; | 451 | struct perf_evsel *pos; |
414 | 452 | ||
415 | group_fd = -1; | 453 | group_fd = -1; |
416 | for (counter = 0; counter < nr_counters; counter++) | 454 | |
417 | create_counter(counter, cpu); | 455 | list_for_each_entry(pos, &evsel_list, node) |
456 | create_counter(pos, cpu); | ||
418 | 457 | ||
419 | nr_cpu++; | 458 | nr_cpu++; |
420 | } | 459 | } |
@@ -437,7 +476,8 @@ static void atexit_header(void) | |||
437 | if (!pipe_output) { | 476 | if (!pipe_output) { |
438 | session->header.data_size += bytes_written; | 477 | session->header.data_size += bytes_written; |
439 | 478 | ||
440 | process_buildids(); | 479 | if (!no_buildid) |
480 | process_buildids(); | ||
441 | perf_header__write(&session->header, output, true); | 481 | perf_header__write(&session->header, output, true); |
442 | perf_session__delete(session); | 482 | perf_session__delete(session); |
443 | symbol__exit(); | 483 | symbol__exit(); |
@@ -500,7 +540,7 @@ static void mmap_read_all(void) | |||
500 | 540 | ||
501 | static int __cmd_record(int argc, const char **argv) | 541 | static int __cmd_record(int argc, const char **argv) |
502 | { | 542 | { |
503 | int i, counter; | 543 | int i; |
504 | struct stat st; | 544 | struct stat st; |
505 | int flags; | 545 | int flags; |
506 | int err; | 546 | int err; |
@@ -552,19 +592,22 @@ static int __cmd_record(int argc, const char **argv) | |||
552 | } | 592 | } |
553 | 593 | ||
554 | session = perf_session__new(output_name, O_WRONLY, | 594 | session = perf_session__new(output_name, O_WRONLY, |
555 | write_mode == WRITE_FORCE, false); | 595 | write_mode == WRITE_FORCE, false, NULL); |
556 | if (session == NULL) { | 596 | if (session == NULL) { |
557 | pr_err("Not enough memory for reading perf file header\n"); | 597 | pr_err("Not enough memory for reading perf file header\n"); |
558 | return -1; | 598 | return -1; |
559 | } | 599 | } |
560 | 600 | ||
601 | if (!no_buildid) | ||
602 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); | ||
603 | |||
561 | if (!file_new) { | 604 | if (!file_new) { |
562 | err = perf_header__read(session, output); | 605 | err = perf_header__read(session, output); |
563 | if (err < 0) | 606 | if (err < 0) |
564 | goto out_delete_session; | 607 | goto out_delete_session; |
565 | } | 608 | } |
566 | 609 | ||
567 | if (have_tracepoints(attrs, nr_counters)) | 610 | if (have_tracepoints(&evsel_list)) |
568 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 611 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
569 | 612 | ||
570 | /* | 613 | /* |
@@ -612,7 +655,7 @@ static int __cmd_record(int argc, const char **argv) | |||
612 | } | 655 | } |
613 | 656 | ||
614 | if (!system_wide && target_tid == -1 && target_pid == -1) | 657 | if (!system_wide && target_tid == -1 && target_pid == -1) |
615 | all_tids[0] = child_pid; | 658 | threads->map[0] = child_pid; |
616 | 659 | ||
617 | close(child_ready_pipe[1]); | 660 | close(child_ready_pipe[1]); |
618 | close(go_pipe[0]); | 661 | close(go_pipe[0]); |
@@ -626,19 +669,15 @@ static int __cmd_record(int argc, const char **argv) | |||
626 | close(child_ready_pipe[0]); | 669 | close(child_ready_pipe[0]); |
627 | } | 670 | } |
628 | 671 | ||
629 | nr_cpus = read_cpu_map(cpu_list); | ||
630 | if (nr_cpus < 1) { | ||
631 | perror("failed to collect number of CPUs"); | ||
632 | return -1; | ||
633 | } | ||
634 | |||
635 | if (!system_wide && no_inherit && !cpu_list) { | 672 | if (!system_wide && no_inherit && !cpu_list) { |
636 | open_counters(-1); | 673 | open_counters(-1); |
637 | } else { | 674 | } else { |
638 | for (i = 0; i < nr_cpus; i++) | 675 | for (i = 0; i < cpus->nr; i++) |
639 | open_counters(cpumap[i]); | 676 | open_counters(cpus->map[i]); |
640 | } | 677 | } |
641 | 678 | ||
679 | perf_session__set_sample_type(session, sample_type); | ||
680 | |||
642 | if (pipe_output) { | 681 | if (pipe_output) { |
643 | err = perf_header__write_pipe(output); | 682 | err = perf_header__write_pipe(output); |
644 | if (err < 0) | 683 | if (err < 0) |
@@ -651,6 +690,8 @@ static int __cmd_record(int argc, const char **argv) | |||
651 | 690 | ||
652 | post_processing_offset = lseek(output, 0, SEEK_CUR); | 691 | post_processing_offset = lseek(output, 0, SEEK_CUR); |
653 | 692 | ||
693 | perf_session__set_sample_id_all(session, sample_id_all_avail); | ||
694 | |||
654 | if (pipe_output) { | 695 | if (pipe_output) { |
655 | err = event__synthesize_attrs(&session->header, | 696 | err = event__synthesize_attrs(&session->header, |
656 | process_synthesized_event, | 697 | process_synthesized_event, |
@@ -667,7 +708,7 @@ static int __cmd_record(int argc, const char **argv) | |||
667 | return err; | 708 | return err; |
668 | } | 709 | } |
669 | 710 | ||
670 | if (have_tracepoints(attrs, nr_counters)) { | 711 | if (have_tracepoints(&evsel_list)) { |
671 | /* | 712 | /* |
672 | * FIXME err <= 0 here actually means that | 713 | * FIXME err <= 0 here actually means that |
673 | * there were no tracepoints so its not really | 714 | * there were no tracepoints so its not really |
@@ -676,8 +717,7 @@ static int __cmd_record(int argc, const char **argv) | |||
676 | * return this more properly and also | 717 | * return this more properly and also |
677 | * propagate errors that now are calling die() | 718 | * propagate errors that now are calling die() |
678 | */ | 719 | */ |
679 | err = event__synthesize_tracing_data(output, attrs, | 720 | err = event__synthesize_tracing_data(output, &evsel_list, |
680 | nr_counters, | ||
681 | process_synthesized_event, | 721 | process_synthesized_event, |
682 | session); | 722 | session); |
683 | if (err <= 0) { | 723 | if (err <= 0) { |
@@ -751,13 +791,13 @@ static int __cmd_record(int argc, const char **argv) | |||
751 | 791 | ||
752 | if (done) { | 792 | if (done) { |
753 | for (i = 0; i < nr_cpu; i++) { | 793 | for (i = 0; i < nr_cpu; i++) { |
754 | for (counter = 0; | 794 | struct perf_evsel *pos; |
755 | counter < nr_counters; | 795 | |
756 | counter++) { | 796 | list_for_each_entry(pos, &evsel_list, node) { |
757 | for (thread = 0; | 797 | for (thread = 0; |
758 | thread < thread_num; | 798 | thread < threads->nr; |
759 | thread++) | 799 | thread++) |
760 | ioctl(fd[i][counter][thread], | 800 | ioctl(FD(pos, i, thread), |
761 | PERF_EVENT_IOC_DISABLE); | 801 | PERF_EVENT_IOC_DISABLE); |
762 | } | 802 | } |
763 | } | 803 | } |
@@ -831,16 +871,20 @@ const struct option record_options[] = { | |||
831 | "per thread counts"), | 871 | "per thread counts"), |
832 | OPT_BOOLEAN('d', "data", &sample_address, | 872 | OPT_BOOLEAN('d', "data", &sample_address, |
833 | "Sample addresses"), | 873 | "Sample addresses"), |
874 | OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"), | ||
834 | OPT_BOOLEAN('n', "no-samples", &no_samples, | 875 | OPT_BOOLEAN('n', "no-samples", &no_samples, |
835 | "don't sample"), | 876 | "don't sample"), |
836 | OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid, | 877 | OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache, |
837 | "do not update the buildid cache"), | 878 | "do not update the buildid cache"), |
879 | OPT_BOOLEAN('B', "no-buildid", &no_buildid, | ||
880 | "do not collect buildids in perf.data"), | ||
838 | OPT_END() | 881 | OPT_END() |
839 | }; | 882 | }; |
840 | 883 | ||
841 | int cmd_record(int argc, const char **argv, const char *prefix __used) | 884 | int cmd_record(int argc, const char **argv, const char *prefix __used) |
842 | { | 885 | { |
843 | int i, j, err = -ENOMEM; | 886 | int err = -ENOMEM; |
887 | struct perf_evsel *pos; | ||
844 | 888 | ||
845 | argc = parse_options(argc, argv, record_options, record_usage, | 889 | argc = parse_options(argc, argv, record_options, record_usage, |
846 | PARSE_OPT_STOP_AT_NON_OPTION); | 890 | PARSE_OPT_STOP_AT_NON_OPTION); |
@@ -859,41 +903,36 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
859 | } | 903 | } |
860 | 904 | ||
861 | symbol__init(); | 905 | symbol__init(); |
862 | if (no_buildid) | 906 | |
907 | if (no_buildid_cache || no_buildid) | ||
863 | disable_buildid_cache(); | 908 | disable_buildid_cache(); |
864 | 909 | ||
865 | if (!nr_counters) { | 910 | if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) { |
866 | nr_counters = 1; | 911 | pr_err("Not enough memory for event selector list\n"); |
867 | attrs[0].type = PERF_TYPE_HARDWARE; | 912 | goto out_symbol_exit; |
868 | attrs[0].config = PERF_COUNT_HW_CPU_CYCLES; | ||
869 | } | 913 | } |
870 | 914 | ||
871 | if (target_pid != -1) { | 915 | if (target_pid != -1) |
872 | target_tid = target_pid; | 916 | target_tid = target_pid; |
873 | thread_num = find_all_tid(target_pid, &all_tids); | ||
874 | if (thread_num <= 0) { | ||
875 | fprintf(stderr, "Can't find all threads of pid %d\n", | ||
876 | target_pid); | ||
877 | usage_with_options(record_usage, record_options); | ||
878 | } | ||
879 | } else { | ||
880 | all_tids=malloc(sizeof(pid_t)); | ||
881 | if (!all_tids) | ||
882 | goto out_symbol_exit; | ||
883 | 917 | ||
884 | all_tids[0] = target_tid; | 918 | threads = thread_map__new(target_pid, target_tid); |
885 | thread_num = 1; | 919 | if (threads == NULL) { |
920 | pr_err("Problems finding threads of monitor\n"); | ||
921 | usage_with_options(record_usage, record_options); | ||
886 | } | 922 | } |
887 | 923 | ||
888 | for (i = 0; i < MAX_NR_CPUS; i++) { | 924 | cpus = cpu_map__new(cpu_list); |
889 | for (j = 0; j < MAX_COUNTERS; j++) { | 925 | if (cpus == NULL) { |
890 | fd[i][j] = malloc(sizeof(int)*thread_num); | 926 | perror("failed to parse CPUs map"); |
891 | if (!fd[i][j]) | 927 | return -1; |
892 | goto out_free_fd; | ||
893 | } | ||
894 | } | 928 | } |
895 | event_array = malloc( | 929 | |
896 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); | 930 | list_for_each_entry(pos, &evsel_list, node) { |
931 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | ||
932 | goto out_free_fd; | ||
933 | } | ||
934 | event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS * | ||
935 | MAX_COUNTERS * threads->nr)); | ||
897 | if (!event_array) | 936 | if (!event_array) |
898 | goto out_free_fd; | 937 | goto out_free_fd; |
899 | 938 | ||
@@ -920,12 +959,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
920 | out_free_event_array: | 959 | out_free_event_array: |
921 | free(event_array); | 960 | free(event_array); |
922 | out_free_fd: | 961 | out_free_fd: |
923 | for (i = 0; i < MAX_NR_CPUS; i++) { | 962 | thread_map__delete(threads); |
924 | for (j = 0; j < MAX_COUNTERS; j++) | 963 | threads = NULL; |
925 | free(fd[i][j]); | ||
926 | } | ||
927 | free(all_tids); | ||
928 | all_tids = NULL; | ||
929 | out_symbol_exit: | 964 | out_symbol_exit: |
930 | symbol__exit(); | 965 | symbol__exit(); |
931 | return err; | 966 | return err; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5de405d45230..75183a4518e6 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -150,13 +150,13 @@ static int add_event_total(struct perf_session *session, | |||
150 | return 0; | 150 | return 0; |
151 | } | 151 | } |
152 | 152 | ||
153 | static int process_sample_event(event_t *event, struct perf_session *session) | 153 | static int process_sample_event(event_t *event, struct sample_data *sample, |
154 | struct perf_session *session) | ||
154 | { | 155 | { |
155 | struct sample_data data = { .period = 1, }; | ||
156 | struct addr_location al; | 156 | struct addr_location al; |
157 | struct perf_event_attr *attr; | 157 | struct perf_event_attr *attr; |
158 | 158 | ||
159 | if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { | 159 | if (event__preprocess_sample(event, session, &al, sample, NULL) < 0) { |
160 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 160 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
161 | event->header.type); | 161 | event->header.type); |
162 | return -1; | 162 | return -1; |
@@ -165,14 +165,14 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
165 | if (al.filtered || (hide_unresolved && al.sym == NULL)) | 165 | if (al.filtered || (hide_unresolved && al.sym == NULL)) |
166 | return 0; | 166 | return 0; |
167 | 167 | ||
168 | if (perf_session__add_hist_entry(session, &al, &data)) { | 168 | if (perf_session__add_hist_entry(session, &al, sample)) { |
169 | pr_debug("problem incrementing symbol period, skipping event\n"); | 169 | pr_debug("problem incrementing symbol period, skipping event\n"); |
170 | return -1; | 170 | return -1; |
171 | } | 171 | } |
172 | 172 | ||
173 | attr = perf_header__find_attr(data.id, &session->header); | 173 | attr = perf_header__find_attr(sample->id, &session->header); |
174 | 174 | ||
175 | if (add_event_total(session, &data, attr)) { | 175 | if (add_event_total(session, sample, attr)) { |
176 | pr_debug("problem adding event period\n"); | 176 | pr_debug("problem adding event period\n"); |
177 | return -1; | 177 | return -1; |
178 | } | 178 | } |
@@ -180,7 +180,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
180 | return 0; | 180 | return 0; |
181 | } | 181 | } |
182 | 182 | ||
183 | static int process_read_event(event_t *event, struct perf_session *session __used) | 183 | static int process_read_event(event_t *event, struct sample_data *sample __used, |
184 | struct perf_session *session __used) | ||
184 | { | 185 | { |
185 | struct perf_event_attr *attr; | 186 | struct perf_event_attr *attr; |
186 | 187 | ||
@@ -243,6 +244,8 @@ static struct perf_event_ops event_ops = { | |||
243 | .event_type = event__process_event_type, | 244 | .event_type = event__process_event_type, |
244 | .tracing_data = event__process_tracing_data, | 245 | .tracing_data = event__process_tracing_data, |
245 | .build_id = event__process_build_id, | 246 | .build_id = event__process_build_id, |
247 | .ordered_samples = true, | ||
248 | .ordering_requires_timestamps = true, | ||
246 | }; | 249 | }; |
247 | 250 | ||
248 | extern volatile int session_done; | 251 | extern volatile int session_done; |
@@ -307,7 +310,7 @@ static int __cmd_report(void) | |||
307 | 310 | ||
308 | signal(SIGINT, sig_handler); | 311 | signal(SIGINT, sig_handler); |
309 | 312 | ||
310 | session = perf_session__new(input_name, O_RDONLY, force, false); | 313 | session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops); |
311 | if (session == NULL) | 314 | if (session == NULL) |
312 | return -ENOMEM; | 315 | return -ENOMEM; |
313 | 316 | ||
@@ -442,6 +445,8 @@ static const struct option options[] = { | |||
442 | "dump raw trace in ASCII"), | 445 | "dump raw trace in ASCII"), |
443 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 446 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
444 | "file", "vmlinux pathname"), | 447 | "file", "vmlinux pathname"), |
448 | OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, | ||
449 | "file", "kallsyms pathname"), | ||
445 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 450 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
446 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | 451 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, |
447 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 452 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
@@ -478,6 +483,8 @@ static const struct option options[] = { | |||
478 | "columns '.' is reserved."), | 483 | "columns '.' is reserved."), |
479 | OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved, | 484 | OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved, |
480 | "Only display entries resolved to a symbol"), | 485 | "Only display entries resolved to a symbol"), |
486 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | ||
487 | "Look for files with symbols relative to this directory"), | ||
481 | OPT_END() | 488 | OPT_END() |
482 | }; | 489 | }; |
483 | 490 | ||
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 55f3b5dcc731..abd4b8497bc4 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -489,7 +489,8 @@ static void create_tasks(void) | |||
489 | 489 | ||
490 | err = pthread_attr_init(&attr); | 490 | err = pthread_attr_init(&attr); |
491 | BUG_ON(err); | 491 | BUG_ON(err); |
492 | err = pthread_attr_setstacksize(&attr, (size_t)(16*1024)); | 492 | err = pthread_attr_setstacksize(&attr, |
493 | (size_t) max(16 * 1024, PTHREAD_STACK_MIN)); | ||
493 | BUG_ON(err); | 494 | BUG_ON(err); |
494 | err = pthread_mutex_lock(&start_work_mutex); | 495 | err = pthread_mutex_lock(&start_work_mutex); |
495 | BUG_ON(err); | 496 | BUG_ON(err); |
@@ -1606,25 +1607,15 @@ process_raw_event(event_t *raw_event __used, struct perf_session *session, | |||
1606 | process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); | 1607 | process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); |
1607 | } | 1608 | } |
1608 | 1609 | ||
1609 | static int process_sample_event(event_t *event, struct perf_session *session) | 1610 | static int process_sample_event(event_t *event, struct sample_data *sample, |
1611 | struct perf_session *session) | ||
1610 | { | 1612 | { |
1611 | struct sample_data data; | ||
1612 | struct thread *thread; | 1613 | struct thread *thread; |
1613 | 1614 | ||
1614 | if (!(session->sample_type & PERF_SAMPLE_RAW)) | 1615 | if (!(session->sample_type & PERF_SAMPLE_RAW)) |
1615 | return 0; | 1616 | return 0; |
1616 | 1617 | ||
1617 | memset(&data, 0, sizeof(data)); | 1618 | thread = perf_session__findnew(session, sample->pid); |
1618 | data.time = -1; | ||
1619 | data.cpu = -1; | ||
1620 | data.period = -1; | ||
1621 | |||
1622 | event__parse_sample(event, session->sample_type, &data); | ||
1623 | |||
1624 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, | ||
1625 | data.pid, data.tid, data.ip, data.period); | ||
1626 | |||
1627 | thread = perf_session__findnew(session, data.pid); | ||
1628 | if (thread == NULL) { | 1619 | if (thread == NULL) { |
1629 | pr_debug("problem processing %d event, skipping it.\n", | 1620 | pr_debug("problem processing %d event, skipping it.\n", |
1630 | event->header.type); | 1621 | event->header.type); |
@@ -1633,10 +1624,11 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
1633 | 1624 | ||
1634 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 1625 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
1635 | 1626 | ||
1636 | if (profile_cpu != -1 && profile_cpu != (int)data.cpu) | 1627 | if (profile_cpu != -1 && profile_cpu != (int)sample->cpu) |
1637 | return 0; | 1628 | return 0; |
1638 | 1629 | ||
1639 | process_raw_event(event, session, data.raw_data, data.cpu, data.time, thread); | 1630 | process_raw_event(event, session, sample->raw_data, sample->cpu, |
1631 | sample->time, thread); | ||
1640 | 1632 | ||
1641 | return 0; | 1633 | return 0; |
1642 | } | 1634 | } |
@@ -1652,7 +1644,8 @@ static struct perf_event_ops event_ops = { | |||
1652 | static int read_events(void) | 1644 | static int read_events(void) |
1653 | { | 1645 | { |
1654 | int err = -EINVAL; | 1646 | int err = -EINVAL; |
1655 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); | 1647 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, |
1648 | 0, false, &event_ops); | ||
1656 | if (session == NULL) | 1649 | if (session == NULL) |
1657 | return -ENOMEM; | 1650 | return -ENOMEM; |
1658 | 1651 | ||
@@ -1869,6 +1862,9 @@ static int __cmd_record(int argc, const char **argv) | |||
1869 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | 1862 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; |
1870 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1863 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
1871 | 1864 | ||
1865 | if (rec_argv == NULL) | ||
1866 | return -ENOMEM; | ||
1867 | |||
1872 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 1868 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
1873 | rec_argv[i] = strdup(record_args[i]); | 1869 | rec_argv[i] = strdup(record_args[i]); |
1874 | 1870 | ||
@@ -1888,10 +1884,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used) | |||
1888 | usage_with_options(sched_usage, sched_options); | 1884 | usage_with_options(sched_usage, sched_options); |
1889 | 1885 | ||
1890 | /* | 1886 | /* |
1891 | * Aliased to 'perf trace' for now: | 1887 | * Aliased to 'perf script' for now: |
1892 | */ | 1888 | */ |
1893 | if (!strcmp(argv[0], "trace")) | 1889 | if (!strcmp(argv[0], "script")) |
1894 | return cmd_trace(argc, argv, prefix); | 1890 | return cmd_script(argc, argv, prefix); |
1895 | 1891 | ||
1896 | symbol__init(); | 1892 | symbol__init(); |
1897 | if (!strncmp(argv[0], "rec", 3)) { | 1893 | if (!strncmp(argv[0], "rec", 3)) { |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-script.c index 86cfe3800e6b..150a606002eb 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-script.c | |||
@@ -56,29 +56,18 @@ static void setup_scripting(void) | |||
56 | 56 | ||
57 | static int cleanup_scripting(void) | 57 | static int cleanup_scripting(void) |
58 | { | 58 | { |
59 | pr_debug("\nperf trace script stopped\n"); | 59 | pr_debug("\nperf script stopped\n"); |
60 | 60 | ||
61 | return scripting_ops->stop_script(); | 61 | return scripting_ops->stop_script(); |
62 | } | 62 | } |
63 | 63 | ||
64 | static char const *input_name = "perf.data"; | 64 | static char const *input_name = "perf.data"; |
65 | 65 | ||
66 | static int process_sample_event(event_t *event, struct perf_session *session) | 66 | static int process_sample_event(event_t *event, struct sample_data *sample, |
67 | struct perf_session *session) | ||
67 | { | 68 | { |
68 | struct sample_data data; | 69 | struct thread *thread = perf_session__findnew(session, event->ip.pid); |
69 | struct thread *thread; | ||
70 | 70 | ||
71 | memset(&data, 0, sizeof(data)); | ||
72 | data.time = -1; | ||
73 | data.cpu = -1; | ||
74 | data.period = 1; | ||
75 | |||
76 | event__parse_sample(event, session->sample_type, &data); | ||
77 | |||
78 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, | ||
79 | data.pid, data.tid, data.ip, data.period); | ||
80 | |||
81 | thread = perf_session__findnew(session, event->ip.pid); | ||
82 | if (thread == NULL) { | 71 | if (thread == NULL) { |
83 | pr_debug("problem processing %d event, skipping it.\n", | 72 | pr_debug("problem processing %d event, skipping it.\n", |
84 | event->header.type); | 73 | event->header.type); |
@@ -87,13 +76,13 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
87 | 76 | ||
88 | if (session->sample_type & PERF_SAMPLE_RAW) { | 77 | if (session->sample_type & PERF_SAMPLE_RAW) { |
89 | if (debug_mode) { | 78 | if (debug_mode) { |
90 | if (data.time < last_timestamp) { | 79 | if (sample->time < last_timestamp) { |
91 | pr_err("Samples misordered, previous: %llu " | 80 | pr_err("Samples misordered, previous: %llu " |
92 | "this: %llu\n", last_timestamp, | 81 | "this: %llu\n", last_timestamp, |
93 | data.time); | 82 | sample->time); |
94 | nr_unordered++; | 83 | nr_unordered++; |
95 | } | 84 | } |
96 | last_timestamp = data.time; | 85 | last_timestamp = sample->time; |
97 | return 0; | 86 | return 0; |
98 | } | 87 | } |
99 | /* | 88 | /* |
@@ -101,21 +90,12 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
101 | * field, although it should be the same than this perf | 90 | * field, although it should be the same than this perf |
102 | * event pid | 91 | * event pid |
103 | */ | 92 | */ |
104 | scripting_ops->process_event(data.cpu, data.raw_data, | 93 | scripting_ops->process_event(sample->cpu, sample->raw_data, |
105 | data.raw_size, | 94 | sample->raw_size, |
106 | data.time, thread->comm); | 95 | sample->time, thread->comm); |
107 | } | 96 | } |
108 | 97 | ||
109 | session->hists.stats.total_period += data.period; | 98 | session->hists.stats.total_period += sample->period; |
110 | return 0; | ||
111 | } | ||
112 | |||
113 | static u64 nr_lost; | ||
114 | |||
115 | static int process_lost_event(event_t *event, struct perf_session *session __used) | ||
116 | { | ||
117 | nr_lost += event->lost.lost; | ||
118 | |||
119 | return 0; | 99 | return 0; |
120 | } | 100 | } |
121 | 101 | ||
@@ -126,7 +106,7 @@ static struct perf_event_ops event_ops = { | |||
126 | .event_type = event__process_event_type, | 106 | .event_type = event__process_event_type, |
127 | .tracing_data = event__process_tracing_data, | 107 | .tracing_data = event__process_tracing_data, |
128 | .build_id = event__process_build_id, | 108 | .build_id = event__process_build_id, |
129 | .lost = process_lost_event, | 109 | .ordering_requires_timestamps = true, |
130 | .ordered_samples = true, | 110 | .ordered_samples = true, |
131 | }; | 111 | }; |
132 | 112 | ||
@@ -137,7 +117,7 @@ static void sig_handler(int sig __unused) | |||
137 | session_done = 1; | 117 | session_done = 1; |
138 | } | 118 | } |
139 | 119 | ||
140 | static int __cmd_trace(struct perf_session *session) | 120 | static int __cmd_script(struct perf_session *session) |
141 | { | 121 | { |
142 | int ret; | 122 | int ret; |
143 | 123 | ||
@@ -145,10 +125,8 @@ static int __cmd_trace(struct perf_session *session) | |||
145 | 125 | ||
146 | ret = perf_session__process_events(session, &event_ops); | 126 | ret = perf_session__process_events(session, &event_ops); |
147 | 127 | ||
148 | if (debug_mode) { | 128 | if (debug_mode) |
149 | pr_err("Misordered timestamps: %llu\n", nr_unordered); | 129 | pr_err("Misordered timestamps: %llu\n", nr_unordered); |
150 | pr_err("Lost events: %llu\n", nr_lost); | ||
151 | } | ||
152 | 130 | ||
153 | return ret; | 131 | return ret; |
154 | } | 132 | } |
@@ -159,7 +137,7 @@ struct script_spec { | |||
159 | char spec[0]; | 137 | char spec[0]; |
160 | }; | 138 | }; |
161 | 139 | ||
162 | LIST_HEAD(script_specs); | 140 | static LIST_HEAD(script_specs); |
163 | 141 | ||
164 | static struct script_spec *script_spec__new(const char *spec, | 142 | static struct script_spec *script_spec__new(const char *spec, |
165 | struct scripting_ops *ops) | 143 | struct scripting_ops *ops) |
@@ -247,7 +225,7 @@ static void list_available_languages(void) | |||
247 | 225 | ||
248 | fprintf(stderr, "\n"); | 226 | fprintf(stderr, "\n"); |
249 | fprintf(stderr, "Scripting language extensions (used in " | 227 | fprintf(stderr, "Scripting language extensions (used in " |
250 | "perf trace -s [spec:]script.[spec]):\n\n"); | 228 | "perf script -s [spec:]script.[spec]):\n\n"); |
251 | 229 | ||
252 | list_for_each_entry(s, &script_specs, node) | 230 | list_for_each_entry(s, &script_specs, node) |
253 | fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name); | 231 | fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name); |
@@ -301,17 +279,34 @@ static int parse_scriptname(const struct option *opt __used, | |||
301 | return 0; | 279 | return 0; |
302 | } | 280 | } |
303 | 281 | ||
304 | #define for_each_lang(scripts_dir, lang_dirent, lang_next) \ | 282 | /* Helper function for filesystems that return a dent->d_type DT_UNKNOWN */ |
283 | static int is_directory(const char *base_path, const struct dirent *dent) | ||
284 | { | ||
285 | char path[PATH_MAX]; | ||
286 | struct stat st; | ||
287 | |||
288 | sprintf(path, "%s/%s", base_path, dent->d_name); | ||
289 | if (stat(path, &st)) | ||
290 | return 0; | ||
291 | |||
292 | return S_ISDIR(st.st_mode); | ||
293 | } | ||
294 | |||
295 | #define for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next)\ | ||
305 | while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ | 296 | while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ |
306 | lang_next) \ | 297 | lang_next) \ |
307 | if (lang_dirent.d_type == DT_DIR && \ | 298 | if ((lang_dirent.d_type == DT_DIR || \ |
299 | (lang_dirent.d_type == DT_UNKNOWN && \ | ||
300 | is_directory(scripts_path, &lang_dirent))) && \ | ||
308 | (strcmp(lang_dirent.d_name, ".")) && \ | 301 | (strcmp(lang_dirent.d_name, ".")) && \ |
309 | (strcmp(lang_dirent.d_name, ".."))) | 302 | (strcmp(lang_dirent.d_name, ".."))) |
310 | 303 | ||
311 | #define for_each_script(lang_dir, script_dirent, script_next) \ | 304 | #define for_each_script(lang_path, lang_dir, script_dirent, script_next)\ |
312 | while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ | 305 | while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ |
313 | script_next) \ | 306 | script_next) \ |
314 | if (script_dirent.d_type != DT_DIR) | 307 | if (script_dirent.d_type != DT_DIR && \ |
308 | (script_dirent.d_type != DT_UNKNOWN || \ | ||
309 | !is_directory(lang_path, &script_dirent))) | ||
315 | 310 | ||
316 | 311 | ||
317 | #define RECORD_SUFFIX "-record" | 312 | #define RECORD_SUFFIX "-record" |
@@ -324,7 +319,7 @@ struct script_desc { | |||
324 | char *args; | 319 | char *args; |
325 | }; | 320 | }; |
326 | 321 | ||
327 | LIST_HEAD(script_descs); | 322 | static LIST_HEAD(script_descs); |
328 | 323 | ||
329 | static struct script_desc *script_desc__new(const char *name) | 324 | static struct script_desc *script_desc__new(const char *name) |
330 | { | 325 | { |
@@ -380,10 +375,10 @@ out_delete_desc: | |||
380 | return NULL; | 375 | return NULL; |
381 | } | 376 | } |
382 | 377 | ||
383 | static char *ends_with(char *str, const char *suffix) | 378 | static const char *ends_with(const char *str, const char *suffix) |
384 | { | 379 | { |
385 | size_t suffix_len = strlen(suffix); | 380 | size_t suffix_len = strlen(suffix); |
386 | char *p = str; | 381 | const char *p = str; |
387 | 382 | ||
388 | if (strlen(str) > suffix_len) { | 383 | if (strlen(str) > suffix_len) { |
389 | p = str + strlen(str) - suffix_len; | 384 | p = str + strlen(str) - suffix_len; |
@@ -466,16 +461,16 @@ static int list_available_scripts(const struct option *opt __used, | |||
466 | if (!scripts_dir) | 461 | if (!scripts_dir) |
467 | return -1; | 462 | return -1; |
468 | 463 | ||
469 | for_each_lang(scripts_dir, lang_dirent, lang_next) { | 464 | for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { |
470 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | 465 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, |
471 | lang_dirent.d_name); | 466 | lang_dirent.d_name); |
472 | lang_dir = opendir(lang_path); | 467 | lang_dir = opendir(lang_path); |
473 | if (!lang_dir) | 468 | if (!lang_dir) |
474 | continue; | 469 | continue; |
475 | 470 | ||
476 | for_each_script(lang_dir, script_dirent, script_next) { | 471 | for_each_script(lang_path, lang_dir, script_dirent, script_next) { |
477 | script_root = strdup(script_dirent.d_name); | 472 | script_root = strdup(script_dirent.d_name); |
478 | str = ends_with(script_root, REPORT_SUFFIX); | 473 | str = (char *)ends_with(script_root, REPORT_SUFFIX); |
479 | if (str) { | 474 | if (str) { |
480 | *str = '\0'; | 475 | *str = '\0'; |
481 | desc = script_desc__findnew(script_root); | 476 | desc = script_desc__findnew(script_root); |
@@ -514,16 +509,16 @@ static char *get_script_path(const char *script_root, const char *suffix) | |||
514 | if (!scripts_dir) | 509 | if (!scripts_dir) |
515 | return NULL; | 510 | return NULL; |
516 | 511 | ||
517 | for_each_lang(scripts_dir, lang_dirent, lang_next) { | 512 | for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { |
518 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | 513 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, |
519 | lang_dirent.d_name); | 514 | lang_dirent.d_name); |
520 | lang_dir = opendir(lang_path); | 515 | lang_dir = opendir(lang_path); |
521 | if (!lang_dir) | 516 | if (!lang_dir) |
522 | continue; | 517 | continue; |
523 | 518 | ||
524 | for_each_script(lang_dir, script_dirent, script_next) { | 519 | for_each_script(lang_path, lang_dir, script_dirent, script_next) { |
525 | __script_root = strdup(script_dirent.d_name); | 520 | __script_root = strdup(script_dirent.d_name); |
526 | str = ends_with(__script_root, suffix); | 521 | str = (char *)ends_with(__script_root, suffix); |
527 | if (str) { | 522 | if (str) { |
528 | *str = '\0'; | 523 | *str = '\0'; |
529 | if (strcmp(__script_root, script_root)) | 524 | if (strcmp(__script_root, script_root)) |
@@ -543,7 +538,7 @@ static char *get_script_path(const char *script_root, const char *suffix) | |||
543 | 538 | ||
544 | static bool is_top_script(const char *script_path) | 539 | static bool is_top_script(const char *script_path) |
545 | { | 540 | { |
546 | return ends_with((char *)script_path, "top") == NULL ? false : true; | 541 | return ends_with(script_path, "top") == NULL ? false : true; |
547 | } | 542 | } |
548 | 543 | ||
549 | static int has_required_arg(char *script_path) | 544 | static int has_required_arg(char *script_path) |
@@ -569,12 +564,12 @@ out: | |||
569 | return n_args; | 564 | return n_args; |
570 | } | 565 | } |
571 | 566 | ||
572 | static const char * const trace_usage[] = { | 567 | static const char * const script_usage[] = { |
573 | "perf trace [<options>]", | 568 | "perf script [<options>]", |
574 | "perf trace [<options>] record <script> [<record-options>] <command>", | 569 | "perf script [<options>] record <script> [<record-options>] <command>", |
575 | "perf trace [<options>] report <script> [script-args]", | 570 | "perf script [<options>] report <script> [script-args]", |
576 | "perf trace [<options>] <script> [<record-options>] <command>", | 571 | "perf script [<options>] <script> [<record-options>] <command>", |
577 | "perf trace [<options>] <top-script> [script-args]", | 572 | "perf script [<options>] <top-script> [script-args]", |
578 | NULL | 573 | NULL |
579 | }; | 574 | }; |
580 | 575 | ||
@@ -591,7 +586,7 @@ static const struct option options[] = { | |||
591 | "script file name (lang:script name, script name, or *)", | 586 | "script file name (lang:script name, script name, or *)", |
592 | parse_scriptname), | 587 | parse_scriptname), |
593 | OPT_STRING('g', "gen-script", &generate_script_lang, "lang", | 588 | OPT_STRING('g', "gen-script", &generate_script_lang, "lang", |
594 | "generate perf-trace.xx script in specified language"), | 589 | "generate perf-script.xx script in specified language"), |
595 | OPT_STRING('i', "input", &input_name, "file", | 590 | OPT_STRING('i', "input", &input_name, "file", |
596 | "input file name"), | 591 | "input file name"), |
597 | OPT_BOOLEAN('d', "debug-mode", &debug_mode, | 592 | OPT_BOOLEAN('d', "debug-mode", &debug_mode, |
@@ -614,7 +609,7 @@ static bool have_cmd(int argc, const char **argv) | |||
614 | return argc != 0; | 609 | return argc != 0; |
615 | } | 610 | } |
616 | 611 | ||
617 | int cmd_trace(int argc, const char **argv, const char *prefix __used) | 612 | int cmd_script(int argc, const char **argv, const char *prefix __used) |
618 | { | 613 | { |
619 | char *rec_script_path = NULL; | 614 | char *rec_script_path = NULL; |
620 | char *rep_script_path = NULL; | 615 | char *rep_script_path = NULL; |
@@ -626,7 +621,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
626 | 621 | ||
627 | setup_scripting(); | 622 | setup_scripting(); |
628 | 623 | ||
629 | argc = parse_options(argc, argv, options, trace_usage, | 624 | argc = parse_options(argc, argv, options, script_usage, |
630 | PARSE_OPT_STOP_AT_NON_OPTION); | 625 | PARSE_OPT_STOP_AT_NON_OPTION); |
631 | 626 | ||
632 | if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { | 627 | if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { |
@@ -640,7 +635,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
640 | if (!rep_script_path) { | 635 | if (!rep_script_path) { |
641 | fprintf(stderr, | 636 | fprintf(stderr, |
642 | "Please specify a valid report script" | 637 | "Please specify a valid report script" |
643 | "(see 'perf trace -l' for listing)\n"); | 638 | "(see 'perf script -l' for listing)\n"); |
644 | return -1; | 639 | return -1; |
645 | } | 640 | } |
646 | } | 641 | } |
@@ -658,8 +653,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
658 | 653 | ||
659 | if (!rec_script_path && !rep_script_path) { | 654 | if (!rec_script_path && !rep_script_path) { |
660 | fprintf(stderr, " Couldn't find script %s\n\n See perf" | 655 | fprintf(stderr, " Couldn't find script %s\n\n See perf" |
661 | " trace -l for available scripts.\n", argv[0]); | 656 | " script -l for available scripts.\n", argv[0]); |
662 | usage_with_options(trace_usage, options); | 657 | usage_with_options(script_usage, options); |
663 | } | 658 | } |
664 | 659 | ||
665 | if (is_top_script(argv[0])) { | 660 | if (is_top_script(argv[0])) { |
@@ -671,9 +666,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
671 | rec_args = (argc - 1) - rep_args; | 666 | rec_args = (argc - 1) - rep_args; |
672 | if (rec_args < 0) { | 667 | if (rec_args < 0) { |
673 | fprintf(stderr, " %s script requires options." | 668 | fprintf(stderr, " %s script requires options." |
674 | "\n\n See perf trace -l for available " | 669 | "\n\n See perf script -l for available " |
675 | "scripts and options.\n", argv[0]); | 670 | "scripts and options.\n", argv[0]); |
676 | usage_with_options(trace_usage, options); | 671 | usage_with_options(script_usage, options); |
677 | } | 672 | } |
678 | } | 673 | } |
679 | 674 | ||
@@ -772,7 +767,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
772 | if (!script_name) | 767 | if (!script_name) |
773 | setup_pager(); | 768 | setup_pager(); |
774 | 769 | ||
775 | session = perf_session__new(input_name, O_RDONLY, 0, false); | 770 | session = perf_session__new(input_name, O_RDONLY, 0, false, &event_ops); |
776 | if (session == NULL) | 771 | if (session == NULL) |
777 | return -ENOMEM; | 772 | return -ENOMEM; |
778 | 773 | ||
@@ -806,7 +801,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
806 | return -1; | 801 | return -1; |
807 | } | 802 | } |
808 | 803 | ||
809 | err = scripting_ops->generate_script("perf-trace"); | 804 | err = scripting_ops->generate_script("perf-script"); |
810 | goto out; | 805 | goto out; |
811 | } | 806 | } |
812 | 807 | ||
@@ -814,10 +809,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
814 | err = scripting_ops->start_script(script_name, argc, argv); | 809 | err = scripting_ops->start_script(script_name, argc, argv); |
815 | if (err) | 810 | if (err) |
816 | goto out; | 811 | goto out; |
817 | pr_debug("perf trace started with script %s\n\n", script_name); | 812 | pr_debug("perf script started with script %s\n\n", script_name); |
818 | } | 813 | } |
819 | 814 | ||
820 | err = __cmd_trace(session); | 815 | err = __cmd_script(session); |
821 | 816 | ||
822 | perf_session__delete(session); | 817 | perf_session__delete(session); |
823 | cleanup_scripting(); | 818 | cleanup_scripting(); |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index a6b4d44f9502..c385a63ebfd1 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include "util/parse-options.h" | 43 | #include "util/parse-options.h" |
44 | #include "util/parse-events.h" | 44 | #include "util/parse-events.h" |
45 | #include "util/event.h" | 45 | #include "util/event.h" |
46 | #include "util/evsel.h" | ||
46 | #include "util/debug.h" | 47 | #include "util/debug.h" |
47 | #include "util/header.h" | 48 | #include "util/header.h" |
48 | #include "util/cpumap.h" | 49 | #include "util/cpumap.h" |
@@ -52,6 +53,8 @@ | |||
52 | #include <math.h> | 53 | #include <math.h> |
53 | #include <locale.h> | 54 | #include <locale.h> |
54 | 55 | ||
56 | #define DEFAULT_SEPARATOR " " | ||
57 | |||
55 | static struct perf_event_attr default_attrs[] = { | 58 | static struct perf_event_attr default_attrs[] = { |
56 | 59 | ||
57 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, | 60 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, |
@@ -69,25 +72,23 @@ static struct perf_event_attr default_attrs[] = { | |||
69 | }; | 72 | }; |
70 | 73 | ||
71 | static bool system_wide = false; | 74 | static bool system_wide = false; |
72 | static int nr_cpus = 0; | 75 | static struct cpu_map *cpus; |
73 | static int run_idx = 0; | 76 | static int run_idx = 0; |
74 | 77 | ||
75 | static int run_count = 1; | 78 | static int run_count = 1; |
76 | static bool no_inherit = false; | 79 | static bool no_inherit = false; |
77 | static bool scale = true; | 80 | static bool scale = true; |
81 | static bool no_aggr = false; | ||
78 | static pid_t target_pid = -1; | 82 | static pid_t target_pid = -1; |
79 | static pid_t target_tid = -1; | 83 | static pid_t target_tid = -1; |
80 | static pid_t *all_tids = NULL; | 84 | static struct thread_map *threads; |
81 | static int thread_num = 0; | ||
82 | static pid_t child_pid = -1; | 85 | static pid_t child_pid = -1; |
83 | static bool null_run = false; | 86 | static bool null_run = false; |
84 | static bool big_num = false; | 87 | static bool big_num = true; |
88 | static int big_num_opt = -1; | ||
85 | static const char *cpu_list; | 89 | static const char *cpu_list; |
86 | 90 | static const char *csv_sep = NULL; | |
87 | 91 | static bool csv_output = false; | |
88 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; | ||
89 | |||
90 | static int event_scaled[MAX_COUNTERS]; | ||
91 | 92 | ||
92 | static volatile int done = 0; | 93 | static volatile int done = 0; |
93 | 94 | ||
@@ -96,6 +97,22 @@ struct stats | |||
96 | double n, mean, M2; | 97 | double n, mean, M2; |
97 | }; | 98 | }; |
98 | 99 | ||
100 | struct perf_stat { | ||
101 | struct stats res_stats[3]; | ||
102 | }; | ||
103 | |||
104 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | ||
105 | { | ||
106 | evsel->priv = zalloc(sizeof(struct perf_stat)); | ||
107 | return evsel->priv == NULL ? -ENOMEM : 0; | ||
108 | } | ||
109 | |||
110 | static void perf_evsel__free_stat_priv(struct perf_evsel *evsel) | ||
111 | { | ||
112 | free(evsel->priv); | ||
113 | evsel->priv = NULL; | ||
114 | } | ||
115 | |||
99 | static void update_stats(struct stats *stats, u64 val) | 116 | static void update_stats(struct stats *stats, u64 val) |
100 | { | 117 | { |
101 | double delta; | 118 | double delta; |
@@ -135,69 +152,38 @@ static double stddev_stats(struct stats *stats) | |||
135 | return sqrt(variance_mean); | 152 | return sqrt(variance_mean); |
136 | } | 153 | } |
137 | 154 | ||
138 | struct stats event_res_stats[MAX_COUNTERS][3]; | 155 | struct stats runtime_nsecs_stats[MAX_NR_CPUS]; |
139 | struct stats runtime_nsecs_stats; | 156 | struct stats runtime_cycles_stats[MAX_NR_CPUS]; |
157 | struct stats runtime_branches_stats[MAX_NR_CPUS]; | ||
140 | struct stats walltime_nsecs_stats; | 158 | struct stats walltime_nsecs_stats; |
141 | struct stats runtime_cycles_stats; | ||
142 | struct stats runtime_branches_stats; | ||
143 | 159 | ||
144 | #define MATCH_EVENT(t, c, counter) \ | 160 | static int create_perf_stat_counter(struct perf_evsel *evsel) |
145 | (attrs[counter].type == PERF_TYPE_##t && \ | ||
146 | attrs[counter].config == PERF_COUNT_##c) | ||
147 | |||
148 | #define ERR_PERF_OPEN \ | ||
149 | "Error: counter %d, sys_perf_event_open() syscall returned with %d (%s)\n" | ||
150 | |||
151 | static int create_perf_stat_counter(int counter) | ||
152 | { | 161 | { |
153 | struct perf_event_attr *attr = attrs + counter; | 162 | struct perf_event_attr *attr = &evsel->attr; |
154 | int thread; | ||
155 | int ncreated = 0; | ||
156 | 163 | ||
157 | if (scale) | 164 | if (scale) |
158 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 165 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
159 | PERF_FORMAT_TOTAL_TIME_RUNNING; | 166 | PERF_FORMAT_TOTAL_TIME_RUNNING; |
160 | 167 | ||
161 | if (system_wide) { | 168 | if (system_wide) |
162 | int cpu; | 169 | return perf_evsel__open_per_cpu(evsel, cpus); |
163 | 170 | ||
164 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 171 | attr->inherit = !no_inherit; |
165 | fd[cpu][counter][0] = sys_perf_event_open(attr, | 172 | if (target_pid == -1 && target_tid == -1) { |
166 | -1, cpumap[cpu], -1, 0); | 173 | attr->disabled = 1; |
167 | if (fd[cpu][counter][0] < 0) | 174 | attr->enable_on_exec = 1; |
168 | pr_debug(ERR_PERF_OPEN, counter, | ||
169 | fd[cpu][counter][0], strerror(errno)); | ||
170 | else | ||
171 | ++ncreated; | ||
172 | } | ||
173 | } else { | ||
174 | attr->inherit = !no_inherit; | ||
175 | if (target_pid == -1 && target_tid == -1) { | ||
176 | attr->disabled = 1; | ||
177 | attr->enable_on_exec = 1; | ||
178 | } | ||
179 | for (thread = 0; thread < thread_num; thread++) { | ||
180 | fd[0][counter][thread] = sys_perf_event_open(attr, | ||
181 | all_tids[thread], -1, -1, 0); | ||
182 | if (fd[0][counter][thread] < 0) | ||
183 | pr_debug(ERR_PERF_OPEN, counter, | ||
184 | fd[0][counter][thread], | ||
185 | strerror(errno)); | ||
186 | else | ||
187 | ++ncreated; | ||
188 | } | ||
189 | } | 175 | } |
190 | 176 | ||
191 | return ncreated; | 177 | return perf_evsel__open_per_thread(evsel, threads); |
192 | } | 178 | } |
193 | 179 | ||
194 | /* | 180 | /* |
195 | * Does the counter have nsecs as a unit? | 181 | * Does the counter have nsecs as a unit? |
196 | */ | 182 | */ |
197 | static inline int nsec_counter(int counter) | 183 | static inline int nsec_counter(struct perf_evsel *evsel) |
198 | { | 184 | { |
199 | if (MATCH_EVENT(SOFTWARE, SW_CPU_CLOCK, counter) || | 185 | if (perf_evsel__match(evsel, SOFTWARE, SW_CPU_CLOCK) || |
200 | MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) | 186 | perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) |
201 | return 1; | 187 | return 1; |
202 | 188 | ||
203 | return 0; | 189 | return 0; |
@@ -205,55 +191,19 @@ static inline int nsec_counter(int counter) | |||
205 | 191 | ||
206 | /* | 192 | /* |
207 | * Read out the results of a single counter: | 193 | * Read out the results of a single counter: |
194 | * aggregate counts across CPUs in system-wide mode | ||
208 | */ | 195 | */ |
209 | static void read_counter(int counter) | 196 | static int read_counter_aggr(struct perf_evsel *counter) |
210 | { | 197 | { |
211 | u64 count[3], single_count[3]; | 198 | struct perf_stat *ps = counter->priv; |
212 | int cpu; | 199 | u64 *count = counter->counts->aggr.values; |
213 | size_t res, nv; | 200 | int i; |
214 | int scaled; | ||
215 | int i, thread; | ||
216 | |||
217 | count[0] = count[1] = count[2] = 0; | ||
218 | |||
219 | nv = scale ? 3 : 1; | ||
220 | for (cpu = 0; cpu < nr_cpus; cpu++) { | ||
221 | for (thread = 0; thread < thread_num; thread++) { | ||
222 | if (fd[cpu][counter][thread] < 0) | ||
223 | continue; | ||
224 | |||
225 | res = read(fd[cpu][counter][thread], | ||
226 | single_count, nv * sizeof(u64)); | ||
227 | assert(res == nv * sizeof(u64)); | ||
228 | |||
229 | close(fd[cpu][counter][thread]); | ||
230 | fd[cpu][counter][thread] = -1; | ||
231 | |||
232 | count[0] += single_count[0]; | ||
233 | if (scale) { | ||
234 | count[1] += single_count[1]; | ||
235 | count[2] += single_count[2]; | ||
236 | } | ||
237 | } | ||
238 | } | ||
239 | |||
240 | scaled = 0; | ||
241 | if (scale) { | ||
242 | if (count[2] == 0) { | ||
243 | event_scaled[counter] = -1; | ||
244 | count[0] = 0; | ||
245 | return; | ||
246 | } | ||
247 | 201 | ||
248 | if (count[2] < count[1]) { | 202 | if (__perf_evsel__read(counter, cpus->nr, threads->nr, scale) < 0) |
249 | event_scaled[counter] = 1; | 203 | return -1; |
250 | count[0] = (unsigned long long) | ||
251 | ((double)count[0] * count[1] / count[2] + 0.5); | ||
252 | } | ||
253 | } | ||
254 | 204 | ||
255 | for (i = 0; i < 3; i++) | 205 | for (i = 0; i < 3; i++) |
256 | update_stats(&event_res_stats[counter][i], count[i]); | 206 | update_stats(&ps->res_stats[i], count[i]); |
257 | 207 | ||
258 | if (verbose) { | 208 | if (verbose) { |
259 | fprintf(stderr, "%s: %Ld %Ld %Ld\n", event_name(counter), | 209 | fprintf(stderr, "%s: %Ld %Ld %Ld\n", event_name(counter), |
@@ -263,26 +213,51 @@ static void read_counter(int counter) | |||
263 | /* | 213 | /* |
264 | * Save the full runtime - to allow normalization during printout: | 214 | * Save the full runtime - to allow normalization during printout: |
265 | */ | 215 | */ |
266 | if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) | 216 | if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK)) |
267 | update_stats(&runtime_nsecs_stats, count[0]); | 217 | update_stats(&runtime_nsecs_stats[0], count[0]); |
268 | if (MATCH_EVENT(HARDWARE, HW_CPU_CYCLES, counter)) | 218 | if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES)) |
269 | update_stats(&runtime_cycles_stats, count[0]); | 219 | update_stats(&runtime_cycles_stats[0], count[0]); |
270 | if (MATCH_EVENT(HARDWARE, HW_BRANCH_INSTRUCTIONS, counter)) | 220 | if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS)) |
271 | update_stats(&runtime_branches_stats, count[0]); | 221 | update_stats(&runtime_branches_stats[0], count[0]); |
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * Read out the results of a single counter: | ||
228 | * do not aggregate counts across CPUs in system-wide mode | ||
229 | */ | ||
230 | static int read_counter(struct perf_evsel *counter) | ||
231 | { | ||
232 | u64 *count; | ||
233 | int cpu; | ||
234 | |||
235 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
236 | if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0) | ||
237 | return -1; | ||
238 | |||
239 | count = counter->counts->cpu[cpu].values; | ||
240 | |||
241 | if (perf_evsel__match(counter, SOFTWARE, SW_TASK_CLOCK)) | ||
242 | update_stats(&runtime_nsecs_stats[cpu], count[0]); | ||
243 | if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES)) | ||
244 | update_stats(&runtime_cycles_stats[cpu], count[0]); | ||
245 | if (perf_evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS)) | ||
246 | update_stats(&runtime_branches_stats[cpu], count[0]); | ||
247 | } | ||
248 | |||
249 | return 0; | ||
272 | } | 250 | } |
273 | 251 | ||
274 | static int run_perf_stat(int argc __used, const char **argv) | 252 | static int run_perf_stat(int argc __used, const char **argv) |
275 | { | 253 | { |
276 | unsigned long long t0, t1; | 254 | unsigned long long t0, t1; |
255 | struct perf_evsel *counter; | ||
277 | int status = 0; | 256 | int status = 0; |
278 | int counter, ncreated = 0; | ||
279 | int child_ready_pipe[2], go_pipe[2]; | 257 | int child_ready_pipe[2], go_pipe[2]; |
280 | const bool forks = (argc > 0); | 258 | const bool forks = (argc > 0); |
281 | char buf; | 259 | char buf; |
282 | 260 | ||
283 | if (!system_wide) | ||
284 | nr_cpus = 1; | ||
285 | |||
286 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { | 261 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { |
287 | perror("failed to create pipes"); | 262 | perror("failed to create pipes"); |
288 | exit(1); | 263 | exit(1); |
@@ -322,7 +297,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
322 | } | 297 | } |
323 | 298 | ||
324 | if (target_tid == -1 && target_pid == -1 && !system_wide) | 299 | if (target_tid == -1 && target_pid == -1 && !system_wide) |
325 | all_tids[0] = child_pid; | 300 | threads->map[0] = child_pid; |
326 | 301 | ||
327 | /* | 302 | /* |
328 | * Wait for the child to be ready to exec. | 303 | * Wait for the child to be ready to exec. |
@@ -334,16 +309,25 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
334 | close(child_ready_pipe[0]); | 309 | close(child_ready_pipe[0]); |
335 | } | 310 | } |
336 | 311 | ||
337 | for (counter = 0; counter < nr_counters; counter++) | 312 | list_for_each_entry(counter, &evsel_list, node) { |
338 | ncreated += create_perf_stat_counter(counter); | 313 | if (create_perf_stat_counter(counter) < 0) { |
339 | 314 | if (errno == -EPERM || errno == -EACCES) { | |
340 | if (ncreated == 0) { | 315 | error("You may not have permission to collect %sstats.\n" |
341 | pr_err("No permission to collect %sstats.\n" | 316 | "\t Consider tweaking" |
342 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid.\n", | 317 | " /proc/sys/kernel/perf_event_paranoid or running as root.", |
343 | system_wide ? "system-wide " : ""); | 318 | system_wide ? "system-wide " : ""); |
344 | if (child_pid != -1) | 319 | } else if (errno == ENOENT) { |
345 | kill(child_pid, SIGTERM); | 320 | error("%s event is not supported. ", event_name(counter)); |
346 | return -1; | 321 | } else { |
322 | error("open_counter returned with %d (%s). " | ||
323 | "/bin/dmesg may provide additional information.\n", | ||
324 | errno, strerror(errno)); | ||
325 | } | ||
326 | if (child_pid != -1) | ||
327 | kill(child_pid, SIGTERM); | ||
328 | die("Not all events could be opened.\n"); | ||
329 | return -1; | ||
330 | } | ||
347 | } | 331 | } |
348 | 332 | ||
349 | /* | 333 | /* |
@@ -362,60 +346,97 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
362 | 346 | ||
363 | update_stats(&walltime_nsecs_stats, t1 - t0); | 347 | update_stats(&walltime_nsecs_stats, t1 - t0); |
364 | 348 | ||
365 | for (counter = 0; counter < nr_counters; counter++) | 349 | if (no_aggr) { |
366 | read_counter(counter); | 350 | list_for_each_entry(counter, &evsel_list, node) { |
351 | read_counter(counter); | ||
352 | perf_evsel__close_fd(counter, cpus->nr, 1); | ||
353 | } | ||
354 | } else { | ||
355 | list_for_each_entry(counter, &evsel_list, node) { | ||
356 | read_counter_aggr(counter); | ||
357 | perf_evsel__close_fd(counter, cpus->nr, threads->nr); | ||
358 | } | ||
359 | } | ||
367 | 360 | ||
368 | return WEXITSTATUS(status); | 361 | return WEXITSTATUS(status); |
369 | } | 362 | } |
370 | 363 | ||
371 | static void print_noise(int counter, double avg) | 364 | static void print_noise(struct perf_evsel *evsel, double avg) |
372 | { | 365 | { |
366 | struct perf_stat *ps; | ||
367 | |||
373 | if (run_count == 1) | 368 | if (run_count == 1) |
374 | return; | 369 | return; |
375 | 370 | ||
371 | ps = evsel->priv; | ||
376 | fprintf(stderr, " ( +- %7.3f%% )", | 372 | fprintf(stderr, " ( +- %7.3f%% )", |
377 | 100 * stddev_stats(&event_res_stats[counter][0]) / avg); | 373 | 100 * stddev_stats(&ps->res_stats[0]) / avg); |
378 | } | 374 | } |
379 | 375 | ||
380 | static void nsec_printout(int counter, double avg) | 376 | static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) |
381 | { | 377 | { |
382 | double msecs = avg / 1e6; | 378 | double msecs = avg / 1e6; |
379 | char cpustr[16] = { '\0', }; | ||
380 | const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-24s"; | ||
383 | 381 | ||
384 | fprintf(stderr, " %18.6f %-24s", msecs, event_name(counter)); | 382 | if (no_aggr) |
383 | sprintf(cpustr, "CPU%*d%s", | ||
384 | csv_output ? 0 : -4, | ||
385 | cpus->map[cpu], csv_sep); | ||
386 | |||
387 | fprintf(stderr, fmt, cpustr, msecs, csv_sep, event_name(evsel)); | ||
388 | |||
389 | if (csv_output) | ||
390 | return; | ||
385 | 391 | ||
386 | if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) { | 392 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) |
387 | fprintf(stderr, " # %10.3f CPUs ", | 393 | fprintf(stderr, " # %10.3f CPUs ", |
388 | avg / avg_stats(&walltime_nsecs_stats)); | 394 | avg / avg_stats(&walltime_nsecs_stats)); |
389 | } | ||
390 | } | 395 | } |
391 | 396 | ||
392 | static void abs_printout(int counter, double avg) | 397 | static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) |
393 | { | 398 | { |
394 | double total, ratio = 0.0; | 399 | double total, ratio = 0.0; |
400 | char cpustr[16] = { '\0', }; | ||
401 | const char *fmt; | ||
402 | |||
403 | if (csv_output) | ||
404 | fmt = "%s%.0f%s%s"; | ||
405 | else if (big_num) | ||
406 | fmt = "%s%'18.0f%s%-24s"; | ||
407 | else | ||
408 | fmt = "%s%18.0f%s%-24s"; | ||
395 | 409 | ||
396 | if (big_num) | 410 | if (no_aggr) |
397 | fprintf(stderr, " %'18.0f %-24s", avg, event_name(counter)); | 411 | sprintf(cpustr, "CPU%*d%s", |
412 | csv_output ? 0 : -4, | ||
413 | cpus->map[cpu], csv_sep); | ||
398 | else | 414 | else |
399 | fprintf(stderr, " %18.0f %-24s", avg, event_name(counter)); | 415 | cpu = 0; |
416 | |||
417 | fprintf(stderr, fmt, cpustr, avg, csv_sep, event_name(evsel)); | ||
400 | 418 | ||
401 | if (MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) { | 419 | if (csv_output) |
402 | total = avg_stats(&runtime_cycles_stats); | 420 | return; |
421 | |||
422 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { | ||
423 | total = avg_stats(&runtime_cycles_stats[cpu]); | ||
403 | 424 | ||
404 | if (total) | 425 | if (total) |
405 | ratio = avg / total; | 426 | ratio = avg / total; |
406 | 427 | ||
407 | fprintf(stderr, " # %10.3f IPC ", ratio); | 428 | fprintf(stderr, " # %10.3f IPC ", ratio); |
408 | } else if (MATCH_EVENT(HARDWARE, HW_BRANCH_MISSES, counter) && | 429 | } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && |
409 | runtime_branches_stats.n != 0) { | 430 | runtime_branches_stats[cpu].n != 0) { |
410 | total = avg_stats(&runtime_branches_stats); | 431 | total = avg_stats(&runtime_branches_stats[cpu]); |
411 | 432 | ||
412 | if (total) | 433 | if (total) |
413 | ratio = avg * 100 / total; | 434 | ratio = avg * 100 / total; |
414 | 435 | ||
415 | fprintf(stderr, " # %10.3f %% ", ratio); | 436 | fprintf(stderr, " # %10.3f %% ", ratio); |
416 | 437 | ||
417 | } else if (runtime_nsecs_stats.n != 0) { | 438 | } else if (runtime_nsecs_stats[cpu].n != 0) { |
418 | total = avg_stats(&runtime_nsecs_stats); | 439 | total = avg_stats(&runtime_nsecs_stats[cpu]); |
419 | 440 | ||
420 | if (total) | 441 | if (total) |
421 | ratio = 1000.0 * avg / total; | 442 | ratio = 1000.0 * avg / total; |
@@ -426,30 +447,38 @@ static void abs_printout(int counter, double avg) | |||
426 | 447 | ||
427 | /* | 448 | /* |
428 | * Print out the results of a single counter: | 449 | * Print out the results of a single counter: |
450 | * aggregated counts in system-wide mode | ||
429 | */ | 451 | */ |
430 | static void print_counter(int counter) | 452 | static void print_counter_aggr(struct perf_evsel *counter) |
431 | { | 453 | { |
432 | double avg = avg_stats(&event_res_stats[counter][0]); | 454 | struct perf_stat *ps = counter->priv; |
433 | int scaled = event_scaled[counter]; | 455 | double avg = avg_stats(&ps->res_stats[0]); |
456 | int scaled = counter->counts->scaled; | ||
434 | 457 | ||
435 | if (scaled == -1) { | 458 | if (scaled == -1) { |
436 | fprintf(stderr, " %18s %-24s\n", | 459 | fprintf(stderr, "%*s%s%-24s\n", |
437 | "<not counted>", event_name(counter)); | 460 | csv_output ? 0 : 18, |
461 | "<not counted>", csv_sep, event_name(counter)); | ||
438 | return; | 462 | return; |
439 | } | 463 | } |
440 | 464 | ||
441 | if (nsec_counter(counter)) | 465 | if (nsec_counter(counter)) |
442 | nsec_printout(counter, avg); | 466 | nsec_printout(-1, counter, avg); |
443 | else | 467 | else |
444 | abs_printout(counter, avg); | 468 | abs_printout(-1, counter, avg); |
469 | |||
470 | if (csv_output) { | ||
471 | fputc('\n', stderr); | ||
472 | return; | ||
473 | } | ||
445 | 474 | ||
446 | print_noise(counter, avg); | 475 | print_noise(counter, avg); |
447 | 476 | ||
448 | if (scaled) { | 477 | if (scaled) { |
449 | double avg_enabled, avg_running; | 478 | double avg_enabled, avg_running; |
450 | 479 | ||
451 | avg_enabled = avg_stats(&event_res_stats[counter][1]); | 480 | avg_enabled = avg_stats(&ps->res_stats[1]); |
452 | avg_running = avg_stats(&event_res_stats[counter][2]); | 481 | avg_running = avg_stats(&ps->res_stats[2]); |
453 | 482 | ||
454 | fprintf(stderr, " (scaled from %.2f%%)", | 483 | fprintf(stderr, " (scaled from %.2f%%)", |
455 | 100 * avg_running / avg_enabled); | 484 | 100 * avg_running / avg_enabled); |
@@ -458,40 +487,92 @@ static void print_counter(int counter) | |||
458 | fprintf(stderr, "\n"); | 487 | fprintf(stderr, "\n"); |
459 | } | 488 | } |
460 | 489 | ||
490 | /* | ||
491 | * Print out the results of a single counter: | ||
492 | * does not use aggregated count in system-wide | ||
493 | */ | ||
494 | static void print_counter(struct perf_evsel *counter) | ||
495 | { | ||
496 | u64 ena, run, val; | ||
497 | int cpu; | ||
498 | |||
499 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
500 | val = counter->counts->cpu[cpu].val; | ||
501 | ena = counter->counts->cpu[cpu].ena; | ||
502 | run = counter->counts->cpu[cpu].run; | ||
503 | if (run == 0 || ena == 0) { | ||
504 | fprintf(stderr, "CPU%*d%s%*s%s%-24s", | ||
505 | csv_output ? 0 : -4, | ||
506 | cpus->map[cpu], csv_sep, | ||
507 | csv_output ? 0 : 18, | ||
508 | "<not counted>", csv_sep, | ||
509 | event_name(counter)); | ||
510 | |||
511 | fprintf(stderr, "\n"); | ||
512 | continue; | ||
513 | } | ||
514 | |||
515 | if (nsec_counter(counter)) | ||
516 | nsec_printout(cpu, counter, val); | ||
517 | else | ||
518 | abs_printout(cpu, counter, val); | ||
519 | |||
520 | if (!csv_output) { | ||
521 | print_noise(counter, 1.0); | ||
522 | |||
523 | if (run != ena) { | ||
524 | fprintf(stderr, " (scaled from %.2f%%)", | ||
525 | 100.0 * run / ena); | ||
526 | } | ||
527 | } | ||
528 | fprintf(stderr, "\n"); | ||
529 | } | ||
530 | } | ||
531 | |||
461 | static void print_stat(int argc, const char **argv) | 532 | static void print_stat(int argc, const char **argv) |
462 | { | 533 | { |
463 | int i, counter; | 534 | struct perf_evsel *counter; |
535 | int i; | ||
464 | 536 | ||
465 | fflush(stdout); | 537 | fflush(stdout); |
466 | 538 | ||
467 | fprintf(stderr, "\n"); | 539 | if (!csv_output) { |
468 | fprintf(stderr, " Performance counter stats for "); | 540 | fprintf(stderr, "\n"); |
469 | if(target_pid == -1 && target_tid == -1) { | 541 | fprintf(stderr, " Performance counter stats for "); |
470 | fprintf(stderr, "\'%s", argv[0]); | 542 | if(target_pid == -1 && target_tid == -1) { |
471 | for (i = 1; i < argc; i++) | 543 | fprintf(stderr, "\'%s", argv[0]); |
472 | fprintf(stderr, " %s", argv[i]); | 544 | for (i = 1; i < argc; i++) |
473 | } else if (target_pid != -1) | 545 | fprintf(stderr, " %s", argv[i]); |
474 | fprintf(stderr, "process id \'%d", target_pid); | 546 | } else if (target_pid != -1) |
475 | else | 547 | fprintf(stderr, "process id \'%d", target_pid); |
476 | fprintf(stderr, "thread id \'%d", target_tid); | 548 | else |
477 | 549 | fprintf(stderr, "thread id \'%d", target_tid); | |
478 | fprintf(stderr, "\'"); | 550 | |
479 | if (run_count > 1) | 551 | fprintf(stderr, "\'"); |
480 | fprintf(stderr, " (%d runs)", run_count); | 552 | if (run_count > 1) |
481 | fprintf(stderr, ":\n\n"); | 553 | fprintf(stderr, " (%d runs)", run_count); |
554 | fprintf(stderr, ":\n\n"); | ||
555 | } | ||
482 | 556 | ||
483 | for (counter = 0; counter < nr_counters; counter++) | 557 | if (no_aggr) { |
484 | print_counter(counter); | 558 | list_for_each_entry(counter, &evsel_list, node) |
559 | print_counter(counter); | ||
560 | } else { | ||
561 | list_for_each_entry(counter, &evsel_list, node) | ||
562 | print_counter_aggr(counter); | ||
563 | } | ||
485 | 564 | ||
486 | fprintf(stderr, "\n"); | 565 | if (!csv_output) { |
487 | fprintf(stderr, " %18.9f seconds time elapsed", | 566 | fprintf(stderr, "\n"); |
488 | avg_stats(&walltime_nsecs_stats)/1e9); | 567 | fprintf(stderr, " %18.9f seconds time elapsed", |
489 | if (run_count > 1) { | 568 | avg_stats(&walltime_nsecs_stats)/1e9); |
490 | fprintf(stderr, " ( +- %7.3f%% )", | 569 | if (run_count > 1) { |
570 | fprintf(stderr, " ( +- %7.3f%% )", | ||
491 | 100*stddev_stats(&walltime_nsecs_stats) / | 571 | 100*stddev_stats(&walltime_nsecs_stats) / |
492 | avg_stats(&walltime_nsecs_stats)); | 572 | avg_stats(&walltime_nsecs_stats)); |
573 | } | ||
574 | fprintf(stderr, "\n\n"); | ||
493 | } | 575 | } |
494 | fprintf(stderr, "\n\n"); | ||
495 | } | 576 | } |
496 | 577 | ||
497 | static volatile int signr = -1; | 578 | static volatile int signr = -1; |
@@ -521,6 +602,13 @@ static const char * const stat_usage[] = { | |||
521 | NULL | 602 | NULL |
522 | }; | 603 | }; |
523 | 604 | ||
605 | static int stat__set_big_num(const struct option *opt __used, | ||
606 | const char *s __used, int unset) | ||
607 | { | ||
608 | big_num_opt = unset ? 0 : 1; | ||
609 | return 0; | ||
610 | } | ||
611 | |||
524 | static const struct option options[] = { | 612 | static const struct option options[] = { |
525 | OPT_CALLBACK('e', "event", NULL, "event", | 613 | OPT_CALLBACK('e', "event", NULL, "event", |
526 | "event selector. use 'perf list' to list available events", | 614 | "event selector. use 'perf list' to list available events", |
@@ -541,64 +629,95 @@ static const struct option options[] = { | |||
541 | "repeat command and print average + stddev (max: 100)"), | 629 | "repeat command and print average + stddev (max: 100)"), |
542 | OPT_BOOLEAN('n', "null", &null_run, | 630 | OPT_BOOLEAN('n', "null", &null_run, |
543 | "null run - dont start any counters"), | 631 | "null run - dont start any counters"), |
544 | OPT_BOOLEAN('B', "big-num", &big_num, | 632 | OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, |
545 | "print large numbers with thousands\' separators"), | 633 | "print large numbers with thousands\' separators", |
634 | stat__set_big_num), | ||
546 | OPT_STRING('C', "cpu", &cpu_list, "cpu", | 635 | OPT_STRING('C', "cpu", &cpu_list, "cpu", |
547 | "list of cpus to monitor in system-wide"), | 636 | "list of cpus to monitor in system-wide"), |
637 | OPT_BOOLEAN('A', "no-aggr", &no_aggr, | ||
638 | "disable CPU count aggregation"), | ||
639 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | ||
640 | "print counts with custom separator"), | ||
548 | OPT_END() | 641 | OPT_END() |
549 | }; | 642 | }; |
550 | 643 | ||
551 | int cmd_stat(int argc, const char **argv, const char *prefix __used) | 644 | int cmd_stat(int argc, const char **argv, const char *prefix __used) |
552 | { | 645 | { |
553 | int status; | 646 | struct perf_evsel *pos; |
554 | int i,j; | 647 | int status = -ENOMEM; |
555 | 648 | ||
556 | setlocale(LC_ALL, ""); | 649 | setlocale(LC_ALL, ""); |
557 | 650 | ||
558 | argc = parse_options(argc, argv, options, stat_usage, | 651 | argc = parse_options(argc, argv, options, stat_usage, |
559 | PARSE_OPT_STOP_AT_NON_OPTION); | 652 | PARSE_OPT_STOP_AT_NON_OPTION); |
653 | |||
654 | if (csv_sep) | ||
655 | csv_output = true; | ||
656 | else | ||
657 | csv_sep = DEFAULT_SEPARATOR; | ||
658 | |||
659 | /* | ||
660 | * let the spreadsheet do the pretty-printing | ||
661 | */ | ||
662 | if (csv_output) { | ||
663 | /* User explicitely passed -B? */ | ||
664 | if (big_num_opt == 1) { | ||
665 | fprintf(stderr, "-B option not supported with -x\n"); | ||
666 | usage_with_options(stat_usage, options); | ||
667 | } else /* Nope, so disable big number formatting */ | ||
668 | big_num = false; | ||
669 | } else if (big_num_opt == 0) /* User passed --no-big-num */ | ||
670 | big_num = false; | ||
671 | |||
560 | if (!argc && target_pid == -1 && target_tid == -1) | 672 | if (!argc && target_pid == -1 && target_tid == -1) |
561 | usage_with_options(stat_usage, options); | 673 | usage_with_options(stat_usage, options); |
562 | if (run_count <= 0) | 674 | if (run_count <= 0) |
563 | usage_with_options(stat_usage, options); | 675 | usage_with_options(stat_usage, options); |
564 | 676 | ||
677 | /* no_aggr is for system-wide only */ | ||
678 | if (no_aggr && !system_wide) | ||
679 | usage_with_options(stat_usage, options); | ||
680 | |||
565 | /* Set attrs and nr_counters if no event is selected and !null_run */ | 681 | /* Set attrs and nr_counters if no event is selected and !null_run */ |
566 | if (!null_run && !nr_counters) { | 682 | if (!null_run && !nr_counters) { |
567 | memcpy(attrs, default_attrs, sizeof(default_attrs)); | 683 | size_t c; |
684 | |||
568 | nr_counters = ARRAY_SIZE(default_attrs); | 685 | nr_counters = ARRAY_SIZE(default_attrs); |
686 | |||
687 | for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { | ||
688 | pos = perf_evsel__new(&default_attrs[c], | ||
689 | nr_counters); | ||
690 | if (pos == NULL) | ||
691 | goto out; | ||
692 | list_add(&pos->node, &evsel_list); | ||
693 | } | ||
569 | } | 694 | } |
570 | 695 | ||
571 | if (system_wide) | 696 | if (target_pid != -1) |
572 | nr_cpus = read_cpu_map(cpu_list); | 697 | target_tid = target_pid; |
573 | else | ||
574 | nr_cpus = 1; | ||
575 | 698 | ||
576 | if (nr_cpus < 1) | 699 | threads = thread_map__new(target_pid, target_tid); |
700 | if (threads == NULL) { | ||
701 | pr_err("Problems finding threads of monitor\n"); | ||
577 | usage_with_options(stat_usage, options); | 702 | usage_with_options(stat_usage, options); |
703 | } | ||
578 | 704 | ||
579 | if (target_pid != -1) { | 705 | if (system_wide) |
580 | target_tid = target_pid; | 706 | cpus = cpu_map__new(cpu_list); |
581 | thread_num = find_all_tid(target_pid, &all_tids); | 707 | else |
582 | if (thread_num <= 0) { | 708 | cpus = cpu_map__dummy_new(); |
583 | fprintf(stderr, "Can't find all threads of pid %d\n", | ||
584 | target_pid); | ||
585 | usage_with_options(stat_usage, options); | ||
586 | } | ||
587 | } else { | ||
588 | all_tids=malloc(sizeof(pid_t)); | ||
589 | if (!all_tids) | ||
590 | return -ENOMEM; | ||
591 | 709 | ||
592 | all_tids[0] = target_tid; | 710 | if (cpus == NULL) { |
593 | thread_num = 1; | 711 | perror("failed to parse CPUs map"); |
712 | usage_with_options(stat_usage, options); | ||
713 | return -1; | ||
594 | } | 714 | } |
595 | 715 | ||
596 | for (i = 0; i < MAX_NR_CPUS; i++) { | 716 | list_for_each_entry(pos, &evsel_list, node) { |
597 | for (j = 0; j < MAX_COUNTERS; j++) { | 717 | if (perf_evsel__alloc_stat_priv(pos) < 0 || |
598 | fd[i][j] = malloc(sizeof(int)*thread_num); | 718 | perf_evsel__alloc_counts(pos, cpus->nr) < 0 || |
599 | if (!fd[i][j]) | 719 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) |
600 | return -ENOMEM; | 720 | goto out_free_fd; |
601 | } | ||
602 | } | 721 | } |
603 | 722 | ||
604 | /* | 723 | /* |
@@ -621,6 +740,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
621 | 740 | ||
622 | if (status != -1) | 741 | if (status != -1) |
623 | print_stat(argc, argv); | 742 | print_stat(argc, argv); |
624 | 743 | out_free_fd: | |
744 | list_for_each_entry(pos, &evsel_list, node) | ||
745 | perf_evsel__free_stat_priv(pos); | ||
746 | out: | ||
747 | thread_map__delete(threads); | ||
748 | threads = NULL; | ||
625 | return status; | 749 | return status; |
626 | } | 750 | } |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 035b9fa063a9..ed5696198d3d 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -119,10 +119,16 @@ static int test__vmlinux_matches_kallsyms(void) | |||
119 | * end addresses too. | 119 | * end addresses too. |
120 | */ | 120 | */ |
121 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { | 121 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { |
122 | struct symbol *pair; | 122 | struct symbol *pair, *first_pair; |
123 | bool backwards = true; | ||
123 | 124 | ||
124 | sym = rb_entry(nd, struct symbol, rb_node); | 125 | sym = rb_entry(nd, struct symbol, rb_node); |
125 | pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); | 126 | |
127 | if (sym->start == sym->end) | ||
128 | continue; | ||
129 | |||
130 | first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); | ||
131 | pair = first_pair; | ||
126 | 132 | ||
127 | if (pair && pair->start == sym->start) { | 133 | if (pair && pair->start == sym->start) { |
128 | next_pair: | 134 | next_pair: |
@@ -143,8 +149,10 @@ next_pair: | |||
143 | pr_debug("%#Lx: diff end addr for %s v: %#Lx k: %#Lx\n", | 149 | pr_debug("%#Lx: diff end addr for %s v: %#Lx k: %#Lx\n", |
144 | sym->start, sym->name, sym->end, pair->end); | 150 | sym->start, sym->name, sym->end, pair->end); |
145 | } else { | 151 | } else { |
146 | struct rb_node *nnd = rb_prev(&pair->rb_node); | 152 | struct rb_node *nnd; |
147 | 153 | detour: | |
154 | nnd = backwards ? rb_prev(&pair->rb_node) : | ||
155 | rb_next(&pair->rb_node); | ||
148 | if (nnd) { | 156 | if (nnd) { |
149 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); | 157 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); |
150 | 158 | ||
@@ -153,6 +161,13 @@ next_pair: | |||
153 | goto next_pair; | 161 | goto next_pair; |
154 | } | 162 | } |
155 | } | 163 | } |
164 | |||
165 | if (backwards) { | ||
166 | backwards = false; | ||
167 | pair = first_pair; | ||
168 | goto detour; | ||
169 | } | ||
170 | |||
156 | pr_debug("%#Lx: diff name v: %s k: %s\n", | 171 | pr_debug("%#Lx: diff name v: %s k: %s\n", |
157 | sym->start, sym->name, pair->name); | 172 | sym->start, sym->name, pair->name); |
158 | } | 173 | } |
@@ -219,6 +234,199 @@ out: | |||
219 | return err; | 234 | return err; |
220 | } | 235 | } |
221 | 236 | ||
237 | #include "util/cpumap.h" | ||
238 | #include "util/evsel.h" | ||
239 | #include <sys/types.h> | ||
240 | |||
241 | static int trace_event__id(const char *event_name) | ||
242 | { | ||
243 | char *filename; | ||
244 | int err = -1, fd; | ||
245 | |||
246 | if (asprintf(&filename, | ||
247 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", | ||
248 | event_name) < 0) | ||
249 | return -1; | ||
250 | |||
251 | fd = open(filename, O_RDONLY); | ||
252 | if (fd >= 0) { | ||
253 | char id[16]; | ||
254 | if (read(fd, id, sizeof(id)) > 0) | ||
255 | err = atoi(id); | ||
256 | close(fd); | ||
257 | } | ||
258 | |||
259 | free(filename); | ||
260 | return err; | ||
261 | } | ||
262 | |||
263 | static int test__open_syscall_event(void) | ||
264 | { | ||
265 | int err = -1, fd; | ||
266 | struct thread_map *threads; | ||
267 | struct perf_evsel *evsel; | ||
268 | struct perf_event_attr attr; | ||
269 | unsigned int nr_open_calls = 111, i; | ||
270 | int id = trace_event__id("sys_enter_open"); | ||
271 | |||
272 | if (id < 0) { | ||
273 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
274 | return -1; | ||
275 | } | ||
276 | |||
277 | threads = thread_map__new(-1, getpid()); | ||
278 | if (threads == NULL) { | ||
279 | pr_debug("thread_map__new\n"); | ||
280 | return -1; | ||
281 | } | ||
282 | |||
283 | memset(&attr, 0, sizeof(attr)); | ||
284 | attr.type = PERF_TYPE_TRACEPOINT; | ||
285 | attr.config = id; | ||
286 | evsel = perf_evsel__new(&attr, 0); | ||
287 | if (evsel == NULL) { | ||
288 | pr_debug("perf_evsel__new\n"); | ||
289 | goto out_thread_map_delete; | ||
290 | } | ||
291 | |||
292 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | ||
293 | pr_debug("failed to open counter: %s, " | ||
294 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
295 | strerror(errno)); | ||
296 | goto out_evsel_delete; | ||
297 | } | ||
298 | |||
299 | for (i = 0; i < nr_open_calls; ++i) { | ||
300 | fd = open("/etc/passwd", O_RDONLY); | ||
301 | close(fd); | ||
302 | } | ||
303 | |||
304 | if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) { | ||
305 | pr_debug("perf_evsel__open_read_on_cpu\n"); | ||
306 | goto out_close_fd; | ||
307 | } | ||
308 | |||
309 | if (evsel->counts->cpu[0].val != nr_open_calls) { | ||
310 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %Ld\n", | ||
311 | nr_open_calls, evsel->counts->cpu[0].val); | ||
312 | goto out_close_fd; | ||
313 | } | ||
314 | |||
315 | err = 0; | ||
316 | out_close_fd: | ||
317 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
318 | out_evsel_delete: | ||
319 | perf_evsel__delete(evsel); | ||
320 | out_thread_map_delete: | ||
321 | thread_map__delete(threads); | ||
322 | return err; | ||
323 | } | ||
324 | |||
325 | #include <sched.h> | ||
326 | |||
327 | static int test__open_syscall_event_on_all_cpus(void) | ||
328 | { | ||
329 | int err = -1, fd, cpu; | ||
330 | struct thread_map *threads; | ||
331 | struct cpu_map *cpus; | ||
332 | struct perf_evsel *evsel; | ||
333 | struct perf_event_attr attr; | ||
334 | unsigned int nr_open_calls = 111, i; | ||
335 | cpu_set_t *cpu_set; | ||
336 | size_t cpu_set_size; | ||
337 | int id = trace_event__id("sys_enter_open"); | ||
338 | |||
339 | if (id < 0) { | ||
340 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
341 | return -1; | ||
342 | } | ||
343 | |||
344 | threads = thread_map__new(-1, getpid()); | ||
345 | if (threads == NULL) { | ||
346 | pr_debug("thread_map__new\n"); | ||
347 | return -1; | ||
348 | } | ||
349 | |||
350 | cpus = cpu_map__new(NULL); | ||
351 | if (threads == NULL) { | ||
352 | pr_debug("thread_map__new\n"); | ||
353 | return -1; | ||
354 | } | ||
355 | |||
356 | cpu_set = CPU_ALLOC(cpus->nr); | ||
357 | |||
358 | if (cpu_set == NULL) | ||
359 | goto out_thread_map_delete; | ||
360 | |||
361 | cpu_set_size = CPU_ALLOC_SIZE(cpus->nr); | ||
362 | CPU_ZERO_S(cpu_set_size, cpu_set); | ||
363 | |||
364 | memset(&attr, 0, sizeof(attr)); | ||
365 | attr.type = PERF_TYPE_TRACEPOINT; | ||
366 | attr.config = id; | ||
367 | evsel = perf_evsel__new(&attr, 0); | ||
368 | if (evsel == NULL) { | ||
369 | pr_debug("perf_evsel__new\n"); | ||
370 | goto out_cpu_free; | ||
371 | } | ||
372 | |||
373 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | ||
374 | pr_debug("failed to open counter: %s, " | ||
375 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
376 | strerror(errno)); | ||
377 | goto out_evsel_delete; | ||
378 | } | ||
379 | |||
380 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
381 | unsigned int ncalls = nr_open_calls + cpu; | ||
382 | |||
383 | CPU_SET(cpu, cpu_set); | ||
384 | sched_setaffinity(0, cpu_set_size, cpu_set); | ||
385 | for (i = 0; i < ncalls; ++i) { | ||
386 | fd = open("/etc/passwd", O_RDONLY); | ||
387 | close(fd); | ||
388 | } | ||
389 | CPU_CLR(cpu, cpu_set); | ||
390 | } | ||
391 | |||
392 | /* | ||
393 | * Here we need to explicitely preallocate the counts, as if | ||
394 | * we use the auto allocation it will allocate just for 1 cpu, | ||
395 | * as we start by cpu 0. | ||
396 | */ | ||
397 | if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) { | ||
398 | pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr); | ||
399 | goto out_close_fd; | ||
400 | } | ||
401 | |||
402 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
403 | unsigned int expected; | ||
404 | |||
405 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { | ||
406 | pr_debug("perf_evsel__open_read_on_cpu\n"); | ||
407 | goto out_close_fd; | ||
408 | } | ||
409 | |||
410 | expected = nr_open_calls + cpu; | ||
411 | if (evsel->counts->cpu[cpu].val != expected) { | ||
412 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %Ld\n", | ||
413 | expected, cpu, evsel->counts->cpu[cpu].val); | ||
414 | goto out_close_fd; | ||
415 | } | ||
416 | } | ||
417 | |||
418 | err = 0; | ||
419 | out_close_fd: | ||
420 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
421 | out_evsel_delete: | ||
422 | perf_evsel__delete(evsel); | ||
423 | out_cpu_free: | ||
424 | CPU_FREE(cpu_set); | ||
425 | out_thread_map_delete: | ||
426 | thread_map__delete(threads); | ||
427 | return err; | ||
428 | } | ||
429 | |||
222 | static struct test { | 430 | static struct test { |
223 | const char *desc; | 431 | const char *desc; |
224 | int (*func)(void); | 432 | int (*func)(void); |
@@ -228,6 +436,14 @@ static struct test { | |||
228 | .func = test__vmlinux_matches_kallsyms, | 436 | .func = test__vmlinux_matches_kallsyms, |
229 | }, | 437 | }, |
230 | { | 438 | { |
439 | .desc = "detect open syscall event", | ||
440 | .func = test__open_syscall_event, | ||
441 | }, | ||
442 | { | ||
443 | .desc = "detect open syscall event on all cpus", | ||
444 | .func = test__open_syscall_event_on_all_cpus, | ||
445 | }, | ||
446 | { | ||
231 | .func = NULL, | 447 | .func = NULL, |
232 | }, | 448 | }, |
233 | }; | 449 | }; |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 9bcc38f0b706..746cf03cb05d 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -32,6 +32,10 @@ | |||
32 | #include "util/session.h" | 32 | #include "util/session.h" |
33 | #include "util/svghelper.h" | 33 | #include "util/svghelper.h" |
34 | 34 | ||
35 | #define SUPPORT_OLD_POWER_EVENTS 1 | ||
36 | #define PWR_EVENT_EXIT -1 | ||
37 | |||
38 | |||
35 | static char const *input_name = "perf.data"; | 39 | static char const *input_name = "perf.data"; |
36 | static char const *output_name = "output.svg"; | 40 | static char const *output_name = "output.svg"; |
37 | 41 | ||
@@ -272,19 +276,22 @@ static int cpus_cstate_state[MAX_CPUS]; | |||
272 | static u64 cpus_pstate_start_times[MAX_CPUS]; | 276 | static u64 cpus_pstate_start_times[MAX_CPUS]; |
273 | static u64 cpus_pstate_state[MAX_CPUS]; | 277 | static u64 cpus_pstate_state[MAX_CPUS]; |
274 | 278 | ||
275 | static int process_comm_event(event_t *event, struct perf_session *session __used) | 279 | static int process_comm_event(event_t *event, struct sample_data *sample __used, |
280 | struct perf_session *session __used) | ||
276 | { | 281 | { |
277 | pid_set_comm(event->comm.tid, event->comm.comm); | 282 | pid_set_comm(event->comm.tid, event->comm.comm); |
278 | return 0; | 283 | return 0; |
279 | } | 284 | } |
280 | 285 | ||
281 | static int process_fork_event(event_t *event, struct perf_session *session __used) | 286 | static int process_fork_event(event_t *event, struct sample_data *sample __used, |
287 | struct perf_session *session __used) | ||
282 | { | 288 | { |
283 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); | 289 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); |
284 | return 0; | 290 | return 0; |
285 | } | 291 | } |
286 | 292 | ||
287 | static int process_exit_event(event_t *event, struct perf_session *session __used) | 293 | static int process_exit_event(event_t *event, struct sample_data *sample __used, |
294 | struct perf_session *session __used) | ||
288 | { | 295 | { |
289 | pid_exit(event->fork.pid, event->fork.time); | 296 | pid_exit(event->fork.pid, event->fork.time); |
290 | return 0; | 297 | return 0; |
@@ -298,12 +305,21 @@ struct trace_entry { | |||
298 | int lock_depth; | 305 | int lock_depth; |
299 | }; | 306 | }; |
300 | 307 | ||
301 | struct power_entry { | 308 | #ifdef SUPPORT_OLD_POWER_EVENTS |
309 | static int use_old_power_events; | ||
310 | struct power_entry_old { | ||
302 | struct trace_entry te; | 311 | struct trace_entry te; |
303 | u64 type; | 312 | u64 type; |
304 | u64 value; | 313 | u64 value; |
305 | u64 cpu_id; | 314 | u64 cpu_id; |
306 | }; | 315 | }; |
316 | #endif | ||
317 | |||
318 | struct power_processor_entry { | ||
319 | struct trace_entry te; | ||
320 | u32 state; | ||
321 | u32 cpu_id; | ||
322 | }; | ||
307 | 323 | ||
308 | #define TASK_COMM_LEN 16 | 324 | #define TASK_COMM_LEN 16 |
309 | struct wakeup_entry { | 325 | struct wakeup_entry { |
@@ -470,48 +486,65 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) | |||
470 | } | 486 | } |
471 | 487 | ||
472 | 488 | ||
473 | static int process_sample_event(event_t *event, struct perf_session *session) | 489 | static int process_sample_event(event_t *event __used, |
490 | struct sample_data *sample, | ||
491 | struct perf_session *session) | ||
474 | { | 492 | { |
475 | struct sample_data data; | ||
476 | struct trace_entry *te; | 493 | struct trace_entry *te; |
477 | 494 | ||
478 | memset(&data, 0, sizeof(data)); | ||
479 | |||
480 | event__parse_sample(event, session->sample_type, &data); | ||
481 | |||
482 | if (session->sample_type & PERF_SAMPLE_TIME) { | 495 | if (session->sample_type & PERF_SAMPLE_TIME) { |
483 | if (!first_time || first_time > data.time) | 496 | if (!first_time || first_time > sample->time) |
484 | first_time = data.time; | 497 | first_time = sample->time; |
485 | if (last_time < data.time) | 498 | if (last_time < sample->time) |
486 | last_time = data.time; | 499 | last_time = sample->time; |
487 | } | 500 | } |
488 | 501 | ||
489 | te = (void *)data.raw_data; | 502 | te = (void *)sample->raw_data; |
490 | if (session->sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) { | 503 | if (session->sample_type & PERF_SAMPLE_RAW && sample->raw_size > 0) { |
491 | char *event_str; | 504 | char *event_str; |
492 | struct power_entry *pe; | 505 | #ifdef SUPPORT_OLD_POWER_EVENTS |
493 | 506 | struct power_entry_old *peo; | |
494 | pe = (void *)te; | 507 | peo = (void *)te; |
495 | 508 | #endif | |
496 | event_str = perf_header__find_event(te->type); | 509 | event_str = perf_header__find_event(te->type); |
497 | 510 | ||
498 | if (!event_str) | 511 | if (!event_str) |
499 | return 0; | 512 | return 0; |
500 | 513 | ||
501 | if (strcmp(event_str, "power:power_start") == 0) | 514 | if (strcmp(event_str, "power:cpu_idle") == 0) { |
502 | c_state_start(pe->cpu_id, data.time, pe->value); | 515 | struct power_processor_entry *ppe = (void *)te; |
516 | if (ppe->state == (u32)PWR_EVENT_EXIT) | ||
517 | c_state_end(ppe->cpu_id, sample->time); | ||
518 | else | ||
519 | c_state_start(ppe->cpu_id, sample->time, | ||
520 | ppe->state); | ||
521 | } | ||
522 | else if (strcmp(event_str, "power:cpu_frequency") == 0) { | ||
523 | struct power_processor_entry *ppe = (void *)te; | ||
524 | p_state_change(ppe->cpu_id, sample->time, ppe->state); | ||
525 | } | ||
526 | |||
527 | else if (strcmp(event_str, "sched:sched_wakeup") == 0) | ||
528 | sched_wakeup(sample->cpu, sample->time, sample->pid, te); | ||
503 | 529 | ||
504 | if (strcmp(event_str, "power:power_end") == 0) | 530 | else if (strcmp(event_str, "sched:sched_switch") == 0) |
505 | c_state_end(pe->cpu_id, data.time); | 531 | sched_switch(sample->cpu, sample->time, te); |
506 | 532 | ||
507 | if (strcmp(event_str, "power:power_frequency") == 0) | 533 | #ifdef SUPPORT_OLD_POWER_EVENTS |
508 | p_state_change(pe->cpu_id, data.time, pe->value); | 534 | if (use_old_power_events) { |
535 | if (strcmp(event_str, "power:power_start") == 0) | ||
536 | c_state_start(peo->cpu_id, sample->time, | ||
537 | peo->value); | ||
509 | 538 | ||
510 | if (strcmp(event_str, "sched:sched_wakeup") == 0) | 539 | else if (strcmp(event_str, "power:power_end") == 0) |
511 | sched_wakeup(data.cpu, data.time, data.pid, te); | 540 | c_state_end(sample->cpu, sample->time); |
512 | 541 | ||
513 | if (strcmp(event_str, "sched:sched_switch") == 0) | 542 | else if (strcmp(event_str, |
514 | sched_switch(data.cpu, data.time, te); | 543 | "power:power_frequency") == 0) |
544 | p_state_change(peo->cpu_id, sample->time, | ||
545 | peo->value); | ||
546 | } | ||
547 | #endif | ||
515 | } | 548 | } |
516 | return 0; | 549 | return 0; |
517 | } | 550 | } |
@@ -937,7 +970,8 @@ static struct perf_event_ops event_ops = { | |||
937 | 970 | ||
938 | static int __cmd_timechart(void) | 971 | static int __cmd_timechart(void) |
939 | { | 972 | { |
940 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false); | 973 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, |
974 | 0, false, &event_ops); | ||
941 | int ret = -EINVAL; | 975 | int ret = -EINVAL; |
942 | 976 | ||
943 | if (session == NULL) | 977 | if (session == NULL) |
@@ -968,7 +1002,8 @@ static const char * const timechart_usage[] = { | |||
968 | NULL | 1002 | NULL |
969 | }; | 1003 | }; |
970 | 1004 | ||
971 | static const char *record_args[] = { | 1005 | #ifdef SUPPORT_OLD_POWER_EVENTS |
1006 | static const char * const record_old_args[] = { | ||
972 | "record", | 1007 | "record", |
973 | "-a", | 1008 | "-a", |
974 | "-R", | 1009 | "-R", |
@@ -980,16 +1015,43 @@ static const char *record_args[] = { | |||
980 | "-e", "sched:sched_wakeup", | 1015 | "-e", "sched:sched_wakeup", |
981 | "-e", "sched:sched_switch", | 1016 | "-e", "sched:sched_switch", |
982 | }; | 1017 | }; |
1018 | #endif | ||
1019 | |||
1020 | static const char * const record_new_args[] = { | ||
1021 | "record", | ||
1022 | "-a", | ||
1023 | "-R", | ||
1024 | "-f", | ||
1025 | "-c", "1", | ||
1026 | "-e", "power:cpu_frequency", | ||
1027 | "-e", "power:cpu_idle", | ||
1028 | "-e", "sched:sched_wakeup", | ||
1029 | "-e", "sched:sched_switch", | ||
1030 | }; | ||
983 | 1031 | ||
984 | static int __cmd_record(int argc, const char **argv) | 1032 | static int __cmd_record(int argc, const char **argv) |
985 | { | 1033 | { |
986 | unsigned int rec_argc, i, j; | 1034 | unsigned int rec_argc, i, j; |
987 | const char **rec_argv; | 1035 | const char **rec_argv; |
1036 | const char * const *record_args = record_new_args; | ||
1037 | unsigned int record_elems = ARRAY_SIZE(record_new_args); | ||
1038 | |||
1039 | #ifdef SUPPORT_OLD_POWER_EVENTS | ||
1040 | if (!is_valid_tracepoint("power:cpu_idle") && | ||
1041 | is_valid_tracepoint("power:power_start")) { | ||
1042 | use_old_power_events = 1; | ||
1043 | record_args = record_old_args; | ||
1044 | record_elems = ARRAY_SIZE(record_old_args); | ||
1045 | } | ||
1046 | #endif | ||
988 | 1047 | ||
989 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | 1048 | rec_argc = record_elems + argc - 1; |
990 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1049 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
991 | 1050 | ||
992 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 1051 | if (rec_argv == NULL) |
1052 | return -ENOMEM; | ||
1053 | |||
1054 | for (i = 0; i < record_elems; i++) | ||
993 | rec_argv[i] = strdup(record_args[i]); | 1055 | rec_argv[i] = strdup(record_args[i]); |
994 | 1056 | ||
995 | for (j = 1; j < (unsigned int)argc; j++, i++) | 1057 | for (j = 1; j < (unsigned int)argc; j++, i++) |
@@ -1018,6 +1080,8 @@ static const struct option options[] = { | |||
1018 | OPT_CALLBACK('p', "process", NULL, "process", | 1080 | OPT_CALLBACK('p', "process", NULL, "process", |
1019 | "process selector. Pass a pid or process name.", | 1081 | "process selector. Pass a pid or process name.", |
1020 | parse_process), | 1082 | parse_process), |
1083 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | ||
1084 | "Look for files with symbols relative to this directory"), | ||
1021 | OPT_END() | 1085 | OPT_END() |
1022 | }; | 1086 | }; |
1023 | 1087 | ||
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index dd625808c2a5..6ce4042421bd 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include "perf.h" | 21 | #include "perf.h" |
22 | 22 | ||
23 | #include "util/color.h" | 23 | #include "util/color.h" |
24 | #include "util/evsel.h" | ||
24 | #include "util/session.h" | 25 | #include "util/session.h" |
25 | #include "util/symbol.h" | 26 | #include "util/symbol.h" |
26 | #include "util/thread.h" | 27 | #include "util/thread.h" |
@@ -29,6 +30,7 @@ | |||
29 | #include "util/parse-options.h" | 30 | #include "util/parse-options.h" |
30 | #include "util/parse-events.h" | 31 | #include "util/parse-events.h" |
31 | #include "util/cpumap.h" | 32 | #include "util/cpumap.h" |
33 | #include "util/xyarray.h" | ||
32 | 34 | ||
33 | #include "util/debug.h" | 35 | #include "util/debug.h" |
34 | 36 | ||
@@ -55,7 +57,7 @@ | |||
55 | #include <linux/unistd.h> | 57 | #include <linux/unistd.h> |
56 | #include <linux/types.h> | 58 | #include <linux/types.h> |
57 | 59 | ||
58 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; | 60 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
59 | 61 | ||
60 | static bool system_wide = false; | 62 | static bool system_wide = false; |
61 | 63 | ||
@@ -66,10 +68,9 @@ static int print_entries; | |||
66 | 68 | ||
67 | static int target_pid = -1; | 69 | static int target_pid = -1; |
68 | static int target_tid = -1; | 70 | static int target_tid = -1; |
69 | static pid_t *all_tids = NULL; | 71 | static struct thread_map *threads; |
70 | static int thread_num = 0; | ||
71 | static bool inherit = false; | 72 | static bool inherit = false; |
72 | static int nr_cpus = 0; | 73 | static struct cpu_map *cpus; |
73 | static int realtime_prio = 0; | 74 | static int realtime_prio = 0; |
74 | static bool group = false; | 75 | static bool group = false; |
75 | static unsigned int page_size; | 76 | static unsigned int page_size; |
@@ -100,6 +101,7 @@ struct sym_entry *sym_filter_entry = NULL; | |||
100 | struct sym_entry *sym_filter_entry_sched = NULL; | 101 | struct sym_entry *sym_filter_entry_sched = NULL; |
101 | static int sym_pcnt_filter = 5; | 102 | static int sym_pcnt_filter = 5; |
102 | static int sym_counter = 0; | 103 | static int sym_counter = 0; |
104 | static struct perf_evsel *sym_evsel = NULL; | ||
103 | static int display_weighted = -1; | 105 | static int display_weighted = -1; |
104 | static const char *cpu_list; | 106 | static const char *cpu_list; |
105 | 107 | ||
@@ -353,7 +355,7 @@ static void show_details(struct sym_entry *syme) | |||
353 | return; | 355 | return; |
354 | 356 | ||
355 | symbol = sym_entry__symbol(syme); | 357 | symbol = sym_entry__symbol(syme); |
356 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); | 358 | printf("Showing %s for %s\n", event_name(sym_evsel), symbol->name); |
357 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 359 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
358 | 360 | ||
359 | pthread_mutex_lock(&syme->src->lock); | 361 | pthread_mutex_lock(&syme->src->lock); |
@@ -460,7 +462,8 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | |||
460 | static void print_sym_table(void) | 462 | static void print_sym_table(void) |
461 | { | 463 | { |
462 | int printed = 0, j; | 464 | int printed = 0, j; |
463 | int counter, snap = !display_weighted ? sym_counter : 0; | 465 | struct perf_evsel *counter; |
466 | int snap = !display_weighted ? sym_counter : 0; | ||
464 | float samples_per_sec = samples/delay_secs; | 467 | float samples_per_sec = samples/delay_secs; |
465 | float ksamples_per_sec = kernel_samples/delay_secs; | 468 | float ksamples_per_sec = kernel_samples/delay_secs; |
466 | float us_samples_per_sec = (us_samples)/delay_secs; | 469 | float us_samples_per_sec = (us_samples)/delay_secs; |
@@ -532,7 +535,9 @@ static void print_sym_table(void) | |||
532 | } | 535 | } |
533 | 536 | ||
534 | if (nr_counters == 1 || !display_weighted) { | 537 | if (nr_counters == 1 || !display_weighted) { |
535 | printf("%Ld", (u64)attrs[0].sample_period); | 538 | struct perf_evsel *first; |
539 | first = list_entry(evsel_list.next, struct perf_evsel, node); | ||
540 | printf("%Ld", first->attr.sample_period); | ||
536 | if (freq) | 541 | if (freq) |
537 | printf("Hz "); | 542 | printf("Hz "); |
538 | else | 543 | else |
@@ -540,9 +545,9 @@ static void print_sym_table(void) | |||
540 | } | 545 | } |
541 | 546 | ||
542 | if (!display_weighted) | 547 | if (!display_weighted) |
543 | printf("%s", event_name(sym_counter)); | 548 | printf("%s", event_name(sym_evsel)); |
544 | else for (counter = 0; counter < nr_counters; counter++) { | 549 | else list_for_each_entry(counter, &evsel_list, node) { |
545 | if (counter) | 550 | if (counter->idx) |
546 | printf("/"); | 551 | printf("/"); |
547 | 552 | ||
548 | printf("%s", event_name(counter)); | 553 | printf("%s", event_name(counter)); |
@@ -558,12 +563,12 @@ static void print_sym_table(void) | |||
558 | printf(" (all"); | 563 | printf(" (all"); |
559 | 564 | ||
560 | if (cpu_list) | 565 | if (cpu_list) |
561 | printf(", CPU%s: %s)\n", nr_cpus > 1 ? "s" : "", cpu_list); | 566 | printf(", CPU%s: %s)\n", cpus->nr > 1 ? "s" : "", cpu_list); |
562 | else { | 567 | else { |
563 | if (target_tid != -1) | 568 | if (target_tid != -1) |
564 | printf(")\n"); | 569 | printf(")\n"); |
565 | else | 570 | else |
566 | printf(", %d CPU%s)\n", nr_cpus, nr_cpus > 1 ? "s" : ""); | 571 | printf(", %d CPU%s)\n", cpus->nr, cpus->nr > 1 ? "s" : ""); |
567 | } | 572 | } |
568 | 573 | ||
569 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 574 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
@@ -739,7 +744,7 @@ static void print_mapped_keys(void) | |||
739 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); | 744 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); |
740 | 745 | ||
741 | if (nr_counters > 1) | 746 | if (nr_counters > 1) |
742 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_counter)); | 747 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); |
743 | 748 | ||
744 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 749 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); |
745 | 750 | ||
@@ -826,19 +831,23 @@ static void handle_keypress(struct perf_session *session, int c) | |||
826 | break; | 831 | break; |
827 | case 'E': | 832 | case 'E': |
828 | if (nr_counters > 1) { | 833 | if (nr_counters > 1) { |
829 | int i; | ||
830 | |||
831 | fprintf(stderr, "\nAvailable events:"); | 834 | fprintf(stderr, "\nAvailable events:"); |
832 | for (i = 0; i < nr_counters; i++) | 835 | |
833 | fprintf(stderr, "\n\t%d %s", i, event_name(i)); | 836 | list_for_each_entry(sym_evsel, &evsel_list, node) |
837 | fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); | ||
834 | 838 | ||
835 | prompt_integer(&sym_counter, "Enter details event counter"); | 839 | prompt_integer(&sym_counter, "Enter details event counter"); |
836 | 840 | ||
837 | if (sym_counter >= nr_counters) { | 841 | if (sym_counter >= nr_counters) { |
838 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(0)); | 842 | sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); |
839 | sym_counter = 0; | 843 | sym_counter = 0; |
844 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); | ||
840 | sleep(1); | 845 | sleep(1); |
846 | break; | ||
841 | } | 847 | } |
848 | list_for_each_entry(sym_evsel, &evsel_list, node) | ||
849 | if (sym_evsel->idx == sym_counter) | ||
850 | break; | ||
842 | } else sym_counter = 0; | 851 | } else sym_counter = 0; |
843 | break; | 852 | break; |
844 | case 'f': | 853 | case 'f': |
@@ -977,12 +986,13 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
977 | } | 986 | } |
978 | 987 | ||
979 | static void event__process_sample(const event_t *self, | 988 | static void event__process_sample(const event_t *self, |
980 | struct perf_session *session, int counter) | 989 | struct sample_data *sample, |
990 | struct perf_session *session, | ||
991 | struct perf_evsel *evsel) | ||
981 | { | 992 | { |
982 | u64 ip = self->ip.ip; | 993 | u64 ip = self->ip.ip; |
983 | struct sym_entry *syme; | 994 | struct sym_entry *syme; |
984 | struct addr_location al; | 995 | struct addr_location al; |
985 | struct sample_data data; | ||
986 | struct machine *machine; | 996 | struct machine *machine; |
987 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 997 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
988 | 998 | ||
@@ -1025,7 +1035,7 @@ static void event__process_sample(const event_t *self, | |||
1025 | if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) | 1035 | if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) |
1026 | exact_samples++; | 1036 | exact_samples++; |
1027 | 1037 | ||
1028 | if (event__preprocess_sample(self, session, &al, &data, | 1038 | if (event__preprocess_sample(self, session, &al, sample, |
1029 | symbol_filter) < 0 || | 1039 | symbol_filter) < 0 || |
1030 | al.filtered) | 1040 | al.filtered) |
1031 | return; | 1041 | return; |
@@ -1071,9 +1081,9 @@ static void event__process_sample(const event_t *self, | |||
1071 | 1081 | ||
1072 | syme = symbol__priv(al.sym); | 1082 | syme = symbol__priv(al.sym); |
1073 | if (!syme->skip) { | 1083 | if (!syme->skip) { |
1074 | syme->count[counter]++; | 1084 | syme->count[evsel->idx]++; |
1075 | syme->origin = origin; | 1085 | syme->origin = origin; |
1076 | record_precise_ip(syme, counter, ip); | 1086 | record_precise_ip(syme, evsel->idx, ip); |
1077 | pthread_mutex_lock(&active_symbols_lock); | 1087 | pthread_mutex_lock(&active_symbols_lock); |
1078 | if (list_empty(&syme->node) || !syme->node.next) | 1088 | if (list_empty(&syme->node) || !syme->node.next) |
1079 | __list_insert_active_sym(syme); | 1089 | __list_insert_active_sym(syme); |
@@ -1082,12 +1092,24 @@ static void event__process_sample(const event_t *self, | |||
1082 | } | 1092 | } |
1083 | 1093 | ||
1084 | struct mmap_data { | 1094 | struct mmap_data { |
1085 | int counter; | ||
1086 | void *base; | 1095 | void *base; |
1087 | int mask; | 1096 | int mask; |
1088 | unsigned int prev; | 1097 | unsigned int prev; |
1089 | }; | 1098 | }; |
1090 | 1099 | ||
1100 | static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel, | ||
1101 | int ncpus, int nthreads) | ||
1102 | { | ||
1103 | evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data)); | ||
1104 | return evsel->priv != NULL ? 0 : -ENOMEM; | ||
1105 | } | ||
1106 | |||
1107 | static void perf_evsel__free_mmap(struct perf_evsel *evsel) | ||
1108 | { | ||
1109 | xyarray__delete(evsel->priv); | ||
1110 | evsel->priv = NULL; | ||
1111 | } | ||
1112 | |||
1091 | static unsigned int mmap_read_head(struct mmap_data *md) | 1113 | static unsigned int mmap_read_head(struct mmap_data *md) |
1092 | { | 1114 | { |
1093 | struct perf_event_mmap_page *pc = md->base; | 1115 | struct perf_event_mmap_page *pc = md->base; |
@@ -1100,11 +1122,15 @@ static unsigned int mmap_read_head(struct mmap_data *md) | |||
1100 | } | 1122 | } |
1101 | 1123 | ||
1102 | static void perf_session__mmap_read_counter(struct perf_session *self, | 1124 | static void perf_session__mmap_read_counter(struct perf_session *self, |
1103 | struct mmap_data *md) | 1125 | struct perf_evsel *evsel, |
1126 | int cpu, int thread_idx) | ||
1104 | { | 1127 | { |
1128 | struct xyarray *mmap_array = evsel->priv; | ||
1129 | struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx); | ||
1105 | unsigned int head = mmap_read_head(md); | 1130 | unsigned int head = mmap_read_head(md); |
1106 | unsigned int old = md->prev; | 1131 | unsigned int old = md->prev; |
1107 | unsigned char *data = md->base + page_size; | 1132 | unsigned char *data = md->base + page_size; |
1133 | struct sample_data sample; | ||
1108 | int diff; | 1134 | int diff; |
1109 | 1135 | ||
1110 | /* | 1136 | /* |
@@ -1152,10 +1178,11 @@ static void perf_session__mmap_read_counter(struct perf_session *self, | |||
1152 | event = &event_copy; | 1178 | event = &event_copy; |
1153 | } | 1179 | } |
1154 | 1180 | ||
1181 | event__parse_sample(event, self, &sample); | ||
1155 | if (event->header.type == PERF_RECORD_SAMPLE) | 1182 | if (event->header.type == PERF_RECORD_SAMPLE) |
1156 | event__process_sample(event, self, md->counter); | 1183 | event__process_sample(event, &sample, self, evsel); |
1157 | else | 1184 | else |
1158 | event__process(event, self); | 1185 | event__process(event, &sample, self); |
1159 | old += size; | 1186 | old += size; |
1160 | } | 1187 | } |
1161 | 1188 | ||
@@ -1163,36 +1190,39 @@ static void perf_session__mmap_read_counter(struct perf_session *self, | |||
1163 | } | 1190 | } |
1164 | 1191 | ||
1165 | static struct pollfd *event_array; | 1192 | static struct pollfd *event_array; |
1166 | static struct mmap_data *mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | ||
1167 | 1193 | ||
1168 | static void perf_session__mmap_read(struct perf_session *self) | 1194 | static void perf_session__mmap_read(struct perf_session *self) |
1169 | { | 1195 | { |
1170 | int i, counter, thread_index; | 1196 | struct perf_evsel *counter; |
1197 | int i, thread_index; | ||
1171 | 1198 | ||
1172 | for (i = 0; i < nr_cpus; i++) { | 1199 | for (i = 0; i < cpus->nr; i++) { |
1173 | for (counter = 0; counter < nr_counters; counter++) | 1200 | list_for_each_entry(counter, &evsel_list, node) { |
1174 | for (thread_index = 0; | 1201 | for (thread_index = 0; |
1175 | thread_index < thread_num; | 1202 | thread_index < threads->nr; |
1176 | thread_index++) { | 1203 | thread_index++) { |
1177 | perf_session__mmap_read_counter(self, | 1204 | perf_session__mmap_read_counter(self, |
1178 | &mmap_array[i][counter][thread_index]); | 1205 | counter, i, thread_index); |
1179 | } | 1206 | } |
1207 | } | ||
1180 | } | 1208 | } |
1181 | } | 1209 | } |
1182 | 1210 | ||
1183 | int nr_poll; | 1211 | int nr_poll; |
1184 | int group_fd; | 1212 | int group_fd; |
1185 | 1213 | ||
1186 | static void start_counter(int i, int counter) | 1214 | static void start_counter(int i, struct perf_evsel *evsel) |
1187 | { | 1215 | { |
1216 | struct xyarray *mmap_array = evsel->priv; | ||
1217 | struct mmap_data *mm; | ||
1188 | struct perf_event_attr *attr; | 1218 | struct perf_event_attr *attr; |
1189 | int cpu = -1; | 1219 | int cpu = -1; |
1190 | int thread_index; | 1220 | int thread_index; |
1191 | 1221 | ||
1192 | if (target_tid == -1) | 1222 | if (target_tid == -1) |
1193 | cpu = cpumap[i]; | 1223 | cpu = cpus->map[i]; |
1194 | 1224 | ||
1195 | attr = attrs + counter; | 1225 | attr = &evsel->attr; |
1196 | 1226 | ||
1197 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 1227 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
1198 | 1228 | ||
@@ -1205,16 +1235,20 @@ static void start_counter(int i, int counter) | |||
1205 | attr->inherit = (cpu < 0) && inherit; | 1235 | attr->inherit = (cpu < 0) && inherit; |
1206 | attr->mmap = 1; | 1236 | attr->mmap = 1; |
1207 | 1237 | ||
1208 | for (thread_index = 0; thread_index < thread_num; thread_index++) { | 1238 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { |
1209 | try_again: | 1239 | try_again: |
1210 | fd[i][counter][thread_index] = sys_perf_event_open(attr, | 1240 | FD(evsel, i, thread_index) = sys_perf_event_open(attr, |
1211 | all_tids[thread_index], cpu, group_fd, 0); | 1241 | threads->map[thread_index], cpu, group_fd, 0); |
1212 | 1242 | ||
1213 | if (fd[i][counter][thread_index] < 0) { | 1243 | if (FD(evsel, i, thread_index) < 0) { |
1214 | int err = errno; | 1244 | int err = errno; |
1215 | 1245 | ||
1216 | if (err == EPERM || err == EACCES) | 1246 | if (err == EPERM || err == EACCES) |
1217 | die("No permission - are you root?\n"); | 1247 | die("Permission error - are you root?\n" |
1248 | "\t Consider tweaking" | ||
1249 | " /proc/sys/kernel/perf_event_paranoid.\n"); | ||
1250 | if (err == ENOENT) | ||
1251 | die("%s event is not supported. ", event_name(evsel)); | ||
1218 | /* | 1252 | /* |
1219 | * If it's cycles then fall back to hrtimer | 1253 | * If it's cycles then fall back to hrtimer |
1220 | * based cpu-clock-tick sw counter, which | 1254 | * based cpu-clock-tick sw counter, which |
@@ -1231,30 +1265,30 @@ try_again: | |||
1231 | goto try_again; | 1265 | goto try_again; |
1232 | } | 1266 | } |
1233 | printf("\n"); | 1267 | printf("\n"); |
1234 | error("perfcounter syscall returned with %d (%s)\n", | 1268 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", |
1235 | fd[i][counter][thread_index], strerror(err)); | 1269 | FD(evsel, i, thread_index), strerror(err)); |
1236 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 1270 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
1237 | exit(-1); | 1271 | exit(-1); |
1238 | } | 1272 | } |
1239 | assert(fd[i][counter][thread_index] >= 0); | 1273 | assert(FD(evsel, i, thread_index) >= 0); |
1240 | fcntl(fd[i][counter][thread_index], F_SETFL, O_NONBLOCK); | 1274 | fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK); |
1241 | 1275 | ||
1242 | /* | 1276 | /* |
1243 | * First counter acts as the group leader: | 1277 | * First counter acts as the group leader: |
1244 | */ | 1278 | */ |
1245 | if (group && group_fd == -1) | 1279 | if (group && group_fd == -1) |
1246 | group_fd = fd[i][counter][thread_index]; | 1280 | group_fd = FD(evsel, i, thread_index); |
1247 | 1281 | ||
1248 | event_array[nr_poll].fd = fd[i][counter][thread_index]; | 1282 | event_array[nr_poll].fd = FD(evsel, i, thread_index); |
1249 | event_array[nr_poll].events = POLLIN; | 1283 | event_array[nr_poll].events = POLLIN; |
1250 | nr_poll++; | 1284 | nr_poll++; |
1251 | 1285 | ||
1252 | mmap_array[i][counter][thread_index].counter = counter; | 1286 | mm = xyarray__entry(mmap_array, i, thread_index); |
1253 | mmap_array[i][counter][thread_index].prev = 0; | 1287 | mm->prev = 0; |
1254 | mmap_array[i][counter][thread_index].mask = mmap_pages*page_size - 1; | 1288 | mm->mask = mmap_pages*page_size - 1; |
1255 | mmap_array[i][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size, | 1289 | mm->base = mmap(NULL, (mmap_pages+1)*page_size, |
1256 | PROT_READ, MAP_SHARED, fd[i][counter][thread_index], 0); | 1290 | PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0); |
1257 | if (mmap_array[i][counter][thread_index].base == MAP_FAILED) | 1291 | if (mm->base == MAP_FAILED) |
1258 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 1292 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
1259 | } | 1293 | } |
1260 | } | 1294 | } |
@@ -1262,13 +1296,13 @@ try_again: | |||
1262 | static int __cmd_top(void) | 1296 | static int __cmd_top(void) |
1263 | { | 1297 | { |
1264 | pthread_t thread; | 1298 | pthread_t thread; |
1265 | int i, counter; | 1299 | struct perf_evsel *counter; |
1266 | int ret; | 1300 | int i, ret; |
1267 | /* | 1301 | /* |
1268 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 1302 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
1269 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 1303 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
1270 | */ | 1304 | */ |
1271 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false); | 1305 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false, false, NULL); |
1272 | if (session == NULL) | 1306 | if (session == NULL) |
1273 | return -ENOMEM; | 1307 | return -ENOMEM; |
1274 | 1308 | ||
@@ -1277,9 +1311,9 @@ static int __cmd_top(void) | |||
1277 | else | 1311 | else |
1278 | event__synthesize_threads(event__process, session); | 1312 | event__synthesize_threads(event__process, session); |
1279 | 1313 | ||
1280 | for (i = 0; i < nr_cpus; i++) { | 1314 | for (i = 0; i < cpus->nr; i++) { |
1281 | group_fd = -1; | 1315 | group_fd = -1; |
1282 | for (counter = 0; counter < nr_counters; counter++) | 1316 | list_for_each_entry(counter, &evsel_list, node) |
1283 | start_counter(i, counter); | 1317 | start_counter(i, counter); |
1284 | } | 1318 | } |
1285 | 1319 | ||
@@ -1368,8 +1402,8 @@ static const struct option options[] = { | |||
1368 | 1402 | ||
1369 | int cmd_top(int argc, const char **argv, const char *prefix __used) | 1403 | int cmd_top(int argc, const char **argv, const char *prefix __used) |
1370 | { | 1404 | { |
1371 | int counter; | 1405 | struct perf_evsel *pos; |
1372 | int i,j; | 1406 | int status = -ENOMEM; |
1373 | 1407 | ||
1374 | page_size = sysconf(_SC_PAGE_SIZE); | 1408 | page_size = sysconf(_SC_PAGE_SIZE); |
1375 | 1409 | ||
@@ -1377,34 +1411,17 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1377 | if (argc) | 1411 | if (argc) |
1378 | usage_with_options(top_usage, options); | 1412 | usage_with_options(top_usage, options); |
1379 | 1413 | ||
1380 | if (target_pid != -1) { | 1414 | if (target_pid != -1) |
1381 | target_tid = target_pid; | 1415 | target_tid = target_pid; |
1382 | thread_num = find_all_tid(target_pid, &all_tids); | ||
1383 | if (thread_num <= 0) { | ||
1384 | fprintf(stderr, "Can't find all threads of pid %d\n", | ||
1385 | target_pid); | ||
1386 | usage_with_options(top_usage, options); | ||
1387 | } | ||
1388 | } else { | ||
1389 | all_tids=malloc(sizeof(pid_t)); | ||
1390 | if (!all_tids) | ||
1391 | return -ENOMEM; | ||
1392 | 1416 | ||
1393 | all_tids[0] = target_tid; | 1417 | threads = thread_map__new(target_pid, target_tid); |
1394 | thread_num = 1; | 1418 | if (threads == NULL) { |
1419 | pr_err("Problems finding threads of monitor\n"); | ||
1420 | usage_with_options(top_usage, options); | ||
1395 | } | 1421 | } |
1396 | 1422 | ||
1397 | for (i = 0; i < MAX_NR_CPUS; i++) { | 1423 | event_array = malloc((sizeof(struct pollfd) * |
1398 | for (j = 0; j < MAX_COUNTERS; j++) { | 1424 | MAX_NR_CPUS * MAX_COUNTERS * threads->nr)); |
1399 | fd[i][j] = malloc(sizeof(int)*thread_num); | ||
1400 | mmap_array[i][j] = zalloc( | ||
1401 | sizeof(struct mmap_data)*thread_num); | ||
1402 | if (!fd[i][j] || !mmap_array[i][j]) | ||
1403 | return -ENOMEM; | ||
1404 | } | ||
1405 | } | ||
1406 | event_array = malloc( | ||
1407 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); | ||
1408 | if (!event_array) | 1425 | if (!event_array) |
1409 | return -ENOMEM; | 1426 | return -ENOMEM; |
1410 | 1427 | ||
@@ -1415,15 +1432,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1415 | cpu_list = NULL; | 1432 | cpu_list = NULL; |
1416 | } | 1433 | } |
1417 | 1434 | ||
1418 | if (!nr_counters) | 1435 | if (!nr_counters && perf_evsel_list__create_default() < 0) { |
1419 | nr_counters = 1; | 1436 | pr_err("Not enough memory for event selector list\n"); |
1420 | 1437 | return -ENOMEM; | |
1421 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | 1438 | } |
1422 | (nr_counters + 1) * sizeof(unsigned long)); | ||
1423 | |||
1424 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | ||
1425 | if (symbol__init() < 0) | ||
1426 | return -1; | ||
1427 | 1439 | ||
1428 | if (delay_secs < 1) | 1440 | if (delay_secs < 1) |
1429 | delay_secs = 1; | 1441 | delay_secs = 1; |
@@ -1440,23 +1452,33 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1440 | exit(EXIT_FAILURE); | 1452 | exit(EXIT_FAILURE); |
1441 | } | 1453 | } |
1442 | 1454 | ||
1443 | /* | 1455 | if (target_tid != -1) |
1444 | * Fill in the ones not specifically initialized via -c: | 1456 | cpus = cpu_map__dummy_new(); |
1445 | */ | 1457 | else |
1446 | for (counter = 0; counter < nr_counters; counter++) { | 1458 | cpus = cpu_map__new(cpu_list); |
1447 | if (attrs[counter].sample_period) | 1459 | |
1460 | if (cpus == NULL) | ||
1461 | usage_with_options(top_usage, options); | ||
1462 | |||
1463 | list_for_each_entry(pos, &evsel_list, node) { | ||
1464 | if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 || | ||
1465 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | ||
1466 | goto out_free_fd; | ||
1467 | /* | ||
1468 | * Fill in the ones not specifically initialized via -c: | ||
1469 | */ | ||
1470 | if (pos->attr.sample_period) | ||
1448 | continue; | 1471 | continue; |
1449 | 1472 | ||
1450 | attrs[counter].sample_period = default_interval; | 1473 | pos->attr.sample_period = default_interval; |
1451 | } | 1474 | } |
1452 | 1475 | ||
1453 | if (target_tid != -1) | 1476 | symbol_conf.priv_size = (sizeof(struct sym_entry) + |
1454 | nr_cpus = 1; | 1477 | (nr_counters + 1) * sizeof(unsigned long)); |
1455 | else | ||
1456 | nr_cpus = read_cpu_map(cpu_list); | ||
1457 | 1478 | ||
1458 | if (nr_cpus < 1) | 1479 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1459 | usage_with_options(top_usage, options); | 1480 | if (symbol__init() < 0) |
1481 | return -1; | ||
1460 | 1482 | ||
1461 | get_term_dimensions(&winsize); | 1483 | get_term_dimensions(&winsize); |
1462 | if (print_entries == 0) { | 1484 | if (print_entries == 0) { |
@@ -1464,5 +1486,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1464 | signal(SIGWINCH, sig_winch_handler); | 1486 | signal(SIGWINCH, sig_winch_handler); |
1465 | } | 1487 | } |
1466 | 1488 | ||
1467 | return __cmd_top(); | 1489 | status = __cmd_top(); |
1490 | out_free_fd: | ||
1491 | list_for_each_entry(pos, &evsel_list, node) | ||
1492 | perf_evsel__free_mmap(pos); | ||
1493 | |||
1494 | return status; | ||
1468 | } | 1495 | } |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 921245b28583..c7798c7f24ed 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); | |||
27 | extern int cmd_stat(int argc, const char **argv, const char *prefix); | 27 | extern int cmd_stat(int argc, const char **argv, const char *prefix); |
28 | extern int cmd_timechart(int argc, const char **argv, const char *prefix); | 28 | extern int cmd_timechart(int argc, const char **argv, const char *prefix); |
29 | extern int cmd_top(int argc, const char **argv, const char *prefix); | 29 | extern int cmd_top(int argc, const char **argv, const char *prefix); |
30 | extern int cmd_trace(int argc, const char **argv, const char *prefix); | 30 | extern int cmd_script(int argc, const char **argv, const char *prefix); |
31 | extern int cmd_version(int argc, const char **argv, const char *prefix); | 31 | extern int cmd_version(int argc, const char **argv, const char *prefix); |
32 | extern int cmd_probe(int argc, const char **argv, const char *prefix); | 32 | extern int cmd_probe(int argc, const char **argv, const char *prefix); |
33 | extern int cmd_kmem(int argc, const char **argv, const char *prefix); | 33 | extern int cmd_kmem(int argc, const char **argv, const char *prefix); |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 949d77fc0b97..16b5088cf8f4 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -16,7 +16,7 @@ perf-report mainporcelain common | |||
16 | perf-stat mainporcelain common | 16 | perf-stat mainporcelain common |
17 | perf-timechart mainporcelain common | 17 | perf-timechart mainporcelain common |
18 | perf-top mainporcelain common | 18 | perf-top mainporcelain common |
19 | perf-trace mainporcelain common | 19 | perf-script mainporcelain common |
20 | perf-probe mainporcelain common | 20 | perf-probe mainporcelain common |
21 | perf-kmem mainporcelain common | 21 | perf-kmem mainporcelain common |
22 | perf-lock mainporcelain common | 22 | perf-lock mainporcelain common |
diff --git a/tools/perf/feature-tests.mak b/tools/perf/feature-tests.mak index b253db634f04..b041ca67a2cb 100644 --- a/tools/perf/feature-tests.mak +++ b/tools/perf/feature-tests.mak | |||
@@ -9,8 +9,8 @@ endef | |||
9 | ifndef NO_DWARF | 9 | ifndef NO_DWARF |
10 | define SOURCE_DWARF | 10 | define SOURCE_DWARF |
11 | #include <dwarf.h> | 11 | #include <dwarf.h> |
12 | #include <libdw.h> | 12 | #include <elfutils/libdw.h> |
13 | #include <version.h> | 13 | #include <elfutils/version.h> |
14 | #ifndef _ELFUTILS_PREREQ | 14 | #ifndef _ELFUTILS_PREREQ |
15 | #error | 15 | #error |
16 | #endif | 16 | #endif |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index cdd6c03f1e14..5b1ecd66bb36 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -286,6 +286,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
286 | status = p->fn(argc, argv, prefix); | 286 | status = p->fn(argc, argv, prefix); |
287 | exit_browser(status); | 287 | exit_browser(status); |
288 | 288 | ||
289 | perf_evsel_list__delete(); | ||
290 | |||
289 | if (status) | 291 | if (status) |
290 | return status & 0xff; | 292 | return status & 0xff; |
291 | 293 | ||
@@ -323,7 +325,7 @@ static void handle_internal_command(int argc, const char **argv) | |||
323 | { "top", cmd_top, 0 }, | 325 | { "top", cmd_top, 0 }, |
324 | { "annotate", cmd_annotate, 0 }, | 326 | { "annotate", cmd_annotate, 0 }, |
325 | { "version", cmd_version, 0 }, | 327 | { "version", cmd_version, 0 }, |
326 | { "trace", cmd_trace, 0 }, | 328 | { "script", cmd_script, 0 }, |
327 | { "sched", cmd_sched, 0 }, | 329 | { "sched", cmd_sched, 0 }, |
328 | { "probe", cmd_probe, 0 }, | 330 | { "probe", cmd_probe, 0 }, |
329 | { "kmem", cmd_kmem, 0 }, | 331 | { "kmem", cmd_kmem, 0 }, |
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c index 01a64ad693f2..790ceba6ad3f 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c +++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c | |||
@@ -8,7 +8,7 @@ | |||
8 | 8 | ||
9 | #line 1 "Context.xs" | 9 | #line 1 "Context.xs" |
10 | /* | 10 | /* |
11 | * Context.xs. XS interfaces for perf trace. | 11 | * Context.xs. XS interfaces for perf script. |
12 | * | 12 | * |
13 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | 13 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> |
14 | * | 14 | * |
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs index 549cf0467d30..c1e2ed1ed34e 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs +++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Context.xs. XS interfaces for perf trace. | 2 | * Context.xs. XS interfaces for perf script. |
3 | * | 3 | * |
4 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | 4 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> |
5 | * | 5 | * |
@@ -23,7 +23,7 @@ | |||
23 | #include "perl.h" | 23 | #include "perl.h" |
24 | #include "XSUB.h" | 24 | #include "XSUB.h" |
25 | #include "../../../perf.h" | 25 | #include "../../../perf.h" |
26 | #include "../../../util/trace-event.h" | 26 | #include "../../../util/script-event.h" |
27 | 27 | ||
28 | MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context | 28 | MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context |
29 | PROTOTYPES: ENABLE | 29 | PROTOTYPES: ENABLE |
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/README b/tools/perf/scripts/perl/Perf-Trace-Util/README index 9a9707630791..2f0c7f3043ee 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/README +++ b/tools/perf/scripts/perl/Perf-Trace-Util/README | |||
@@ -1,7 +1,7 @@ | |||
1 | Perf-Trace-Util version 0.01 | 1 | Perf-Trace-Util version 0.01 |
2 | ============================ | 2 | ============================ |
3 | 3 | ||
4 | This module contains utility functions for use with perf trace. | 4 | This module contains utility functions for use with perf script. |
5 | 5 | ||
6 | Core.pm and Util.pm are pure Perl modules; Core.pm contains routines | 6 | Core.pm and Util.pm are pure Perl modules; Core.pm contains routines |
7 | that the core perf support for Perl calls on and should always be | 7 | that the core perf support for Perl calls on and should always be |
@@ -33,7 +33,7 @@ After you do that: | |||
33 | 33 | ||
34 | INSTALLATION | 34 | INSTALLATION |
35 | 35 | ||
36 | Building perf with perf trace Perl scripting should install this | 36 | Building perf with perf script Perl scripting should install this |
37 | module in the right place. | 37 | module in the right place. |
38 | 38 | ||
39 | You should make sure libperl and ExtUtils/Embed.pm are installed first | 39 | You should make sure libperl and ExtUtils/Embed.pm are installed first |
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm index 6c7f3659cb17..4e2f6039ac92 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm +++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm | |||
@@ -34,7 +34,7 @@ Perf::Trace::Context - Perl extension for accessing functions in perf. | |||
34 | 34 | ||
35 | =head1 SEE ALSO | 35 | =head1 SEE ALSO |
36 | 36 | ||
37 | Perf (trace) documentation | 37 | Perf (script) documentation |
38 | 38 | ||
39 | =head1 AUTHOR | 39 | =head1 AUTHOR |
40 | 40 | ||
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm index 9df376a9f629..9158458d3eeb 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm +++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm | |||
@@ -163,7 +163,7 @@ sub dump_symbolic_fields | |||
163 | __END__ | 163 | __END__ |
164 | =head1 NAME | 164 | =head1 NAME |
165 | 165 | ||
166 | Perf::Trace::Core - Perl extension for perf trace | 166 | Perf::Trace::Core - Perl extension for perf script |
167 | 167 | ||
168 | =head1 SYNOPSIS | 168 | =head1 SYNOPSIS |
169 | 169 | ||
@@ -171,7 +171,7 @@ Perf::Trace::Core - Perl extension for perf trace | |||
171 | 171 | ||
172 | =head1 SEE ALSO | 172 | =head1 SEE ALSO |
173 | 173 | ||
174 | Perf (trace) documentation | 174 | Perf (script) documentation |
175 | 175 | ||
176 | =head1 AUTHOR | 176 | =head1 AUTHOR |
177 | 177 | ||
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm index d94b40c8ac85..053500114625 100644 --- a/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm +++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm | |||
@@ -65,7 +65,7 @@ sub clear_term | |||
65 | __END__ | 65 | __END__ |
66 | =head1 NAME | 66 | =head1 NAME |
67 | 67 | ||
68 | Perf::Trace::Util - Perl extension for perf trace | 68 | Perf::Trace::Util - Perl extension for perf script |
69 | 69 | ||
70 | =head1 SYNOPSIS | 70 | =head1 SYNOPSIS |
71 | 71 | ||
@@ -73,7 +73,7 @@ Perf::Trace::Util - Perl extension for perf trace | |||
73 | 73 | ||
74 | =head1 SEE ALSO | 74 | =head1 SEE ALSO |
75 | 75 | ||
76 | Perf (trace) documentation | 76 | Perf (script) documentation |
77 | 77 | ||
78 | =head1 AUTHOR | 78 | =head1 AUTHOR |
79 | 79 | ||
diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-report b/tools/perf/scripts/perl/bin/failed-syscalls-report index 4028d92dc4ae..9f83cc1ad8ba 100644 --- a/tools/perf/scripts/perl/bin/failed-syscalls-report +++ b/tools/perf/scripts/perl/bin/failed-syscalls-report | |||
@@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then | |||
7 | shift | 7 | shift |
8 | fi | 8 | fi |
9 | fi | 9 | fi |
10 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/failed-syscalls.pl $comm | 10 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/failed-syscalls.pl $comm |
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report index ba25f4d41fb0..77200b3f3100 100644 --- a/tools/perf/scripts/perl/bin/rw-by-file-report +++ b/tools/perf/scripts/perl/bin/rw-by-file-report | |||
@@ -7,7 +7,4 @@ if [ $# -lt 1 ] ; then | |||
7 | fi | 7 | fi |
8 | comm=$1 | 8 | comm=$1 |
9 | shift | 9 | shift |
10 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm | 10 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-file.pl $comm |
11 | |||
12 | |||
13 | |||
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report index 641a3f5d085c..a27b9f311f95 100644 --- a/tools/perf/scripts/perl/bin/rw-by-pid-report +++ b/tools/perf/scripts/perl/bin/rw-by-pid-report | |||
@@ -1,6 +1,3 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | # description: system-wide r/w activity | 2 | # description: system-wide r/w activity |
3 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl | 3 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rw-by-pid.pl |
4 | |||
5 | |||
6 | |||
diff --git a/tools/perf/scripts/perl/bin/rwtop-report b/tools/perf/scripts/perl/bin/rwtop-report index 4918dba77021..83e11ec2e190 100644 --- a/tools/perf/scripts/perl/bin/rwtop-report +++ b/tools/perf/scripts/perl/bin/rwtop-report | |||
@@ -17,7 +17,4 @@ if [ "$n_args" -gt 0 ] ; then | |||
17 | interval=$1 | 17 | interval=$1 |
18 | shift | 18 | shift |
19 | fi | 19 | fi |
20 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval | 20 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/rwtop.pl $interval |
21 | |||
22 | |||
23 | |||
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report index 49052ebcb632..889e8130cca5 100644 --- a/tools/perf/scripts/perl/bin/wakeup-latency-report +++ b/tools/perf/scripts/perl/bin/wakeup-latency-report | |||
@@ -1,6 +1,3 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | # description: system-wide min/max/avg wakeup latency | 2 | # description: system-wide min/max/avg wakeup latency |
3 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl | 3 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/wakeup-latency.pl |
4 | |||
5 | |||
6 | |||
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report index df0c65f4ca93..6d91411d248c 100644 --- a/tools/perf/scripts/perl/bin/workqueue-stats-report +++ b/tools/perf/scripts/perl/bin/workqueue-stats-report | |||
@@ -1,7 +1,3 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | # description: workqueue stats (ins/exe/create/destroy) | 2 | # description: workqueue stats (ins/exe/create/destroy) |
3 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl | 3 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl |
4 | |||
5 | |||
6 | |||
7 | |||
diff --git a/tools/perf/scripts/perl/check-perf-trace.pl b/tools/perf/scripts/perl/check-perf-trace.pl index 4e7dc0a407a5..4e7076c20616 100644 --- a/tools/perf/scripts/perl/check-perf-trace.pl +++ b/tools/perf/scripts/perl/check-perf-trace.pl | |||
@@ -1,4 +1,4 @@ | |||
1 | # perf trace event handlers, generated by perf trace -g perl | 1 | # perf script event handlers, generated by perf script -g perl |
2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> | 2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> |
3 | # Licensed under the terms of the GNU GPL License version 2 | 3 | # Licensed under the terms of the GNU GPL License version 2 |
4 | 4 | ||
diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/perl/rw-by-file.pl index 2a39097687b9..74844ee2be3e 100644 --- a/tools/perf/scripts/perl/rw-by-file.pl +++ b/tools/perf/scripts/perl/rw-by-file.pl | |||
@@ -18,7 +18,7 @@ use lib "./Perf-Trace-Util/lib"; | |||
18 | use Perf::Trace::Core; | 18 | use Perf::Trace::Core; |
19 | use Perf::Trace::Util; | 19 | use Perf::Trace::Util; |
20 | 20 | ||
21 | my $usage = "perf trace -s rw-by-file.pl <comm>\n"; | 21 | my $usage = "perf script -s rw-by-file.pl <comm>\n"; |
22 | 22 | ||
23 | my $for_comm = shift or die $usage; | 23 | my $for_comm = shift or die $usage; |
24 | 24 | ||
diff --git a/tools/perf/scripts/perl/workqueue-stats.pl b/tools/perf/scripts/perl/workqueue-stats.pl index b84b12699b70..a8eaff5119e0 100644 --- a/tools/perf/scripts/perl/workqueue-stats.pl +++ b/tools/perf/scripts/perl/workqueue-stats.pl | |||
@@ -10,7 +10,7 @@ | |||
10 | # workqueue:workqueue_destruction -e workqueue:workqueue_execution | 10 | # workqueue:workqueue_destruction -e workqueue:workqueue_execution |
11 | # -e workqueue:workqueue_insertion | 11 | # -e workqueue:workqueue_insertion |
12 | # | 12 | # |
13 | # perf trace -p -s tools/perf/scripts/perl/workqueue-stats.pl | 13 | # perf script -p -s tools/perf/scripts/perl/workqueue-stats.pl |
14 | 14 | ||
15 | use 5.010000; | 15 | use 5.010000; |
16 | use strict; | 16 | use strict; |
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c index 957085dd5d8d..315067b8f552 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 @@ | |||
1 | /* | 1 | /* |
2 | * Context.c. Python interfaces for perf trace. | 2 | * Context.c. Python interfaces for perf script. |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com> | 4 | * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com> |
5 | * | 5 | * |
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py index aad7525bca1d..de7211e4fa47 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py | |||
@@ -1,4 +1,4 @@ | |||
1 | # Core.py - Python extension for perf trace, core functions | 1 | # Core.py - Python extension for perf script, core functions |
2 | # | 2 | # |
3 | # Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com> | 3 | # Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com> |
4 | # | 4 | # |
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py index ae9a56e43e05..fdd92f699055 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py | |||
@@ -1,4 +1,4 @@ | |||
1 | # SchedGui.py - Python extension for perf trace, basic GUI code for | 1 | # SchedGui.py - Python extension for perf script, basic GUI code for |
2 | # traces drawing and overview. | 2 | # traces drawing and overview. |
3 | # | 3 | # |
4 | # Copyright (C) 2010 by Frederic Weisbecker <fweisbec@gmail.com> | 4 | # Copyright (C) 2010 by Frederic Weisbecker <fweisbec@gmail.com> |
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py index 13cc02b5893a..15c8400240fd 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py | |||
@@ -1,4 +1,4 @@ | |||
1 | # Util.py - Python extension for perf trace, miscellaneous utility code | 1 | # Util.py - Python extension for perf script, miscellaneous utility code |
2 | # | 2 | # |
3 | # Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com> | 3 | # Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com> |
4 | # | 4 | # |
diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report index 03587021463d..fda5096d0cbf 100644 --- a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report | |||
@@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then | |||
7 | shift | 7 | shift |
8 | fi | 8 | fi |
9 | fi | 9 | fi |
10 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/failed-syscalls-by-pid.py $comm | 10 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/failed-syscalls-by-pid.py $comm |
diff --git a/tools/perf/scripts/python/bin/futex-contention-report b/tools/perf/scripts/python/bin/futex-contention-report index c8268138fb7e..6c44271091ab 100644 --- a/tools/perf/scripts/python/bin/futex-contention-report +++ b/tools/perf/scripts/python/bin/futex-contention-report | |||
@@ -1,4 +1,4 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | # description: futext contention measurement | 2 | # description: futext contention measurement |
3 | 3 | ||
4 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/futex-contention.py | 4 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/futex-contention.py |
diff --git a/tools/perf/scripts/python/bin/netdev-times-report b/tools/perf/scripts/python/bin/netdev-times-report index 4ad361b31249..8f759291da86 100644 --- a/tools/perf/scripts/python/bin/netdev-times-report +++ b/tools/perf/scripts/python/bin/netdev-times-report | |||
@@ -2,4 +2,4 @@ | |||
2 | # description: display a process of packet and processing time | 2 | # description: display a process of packet and processing time |
3 | # args: [tx] [rx] [dev=] [debug] | 3 | # args: [tx] [rx] [dev=] [debug] |
4 | 4 | ||
5 | perf trace -s "$PERF_EXEC_PATH"/scripts/python/netdev-times.py $@ | 5 | perf script -s "$PERF_EXEC_PATH"/scripts/python/netdev-times.py $@ |
diff --git a/tools/perf/scripts/python/bin/sched-migration-report b/tools/perf/scripts/python/bin/sched-migration-report index df1791f07c24..68b037a1849b 100644 --- a/tools/perf/scripts/python/bin/sched-migration-report +++ b/tools/perf/scripts/python/bin/sched-migration-report | |||
@@ -1,3 +1,3 @@ | |||
1 | #!/bin/bash | 1 | #!/bin/bash |
2 | # description: sched migration overview | 2 | # description: sched migration overview |
3 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/sched-migration.py | 3 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/sched-migration.py |
diff --git a/tools/perf/scripts/python/bin/sctop-report b/tools/perf/scripts/python/bin/sctop-report index 36b409c05e50..c32db294124d 100644 --- a/tools/perf/scripts/python/bin/sctop-report +++ b/tools/perf/scripts/python/bin/sctop-report | |||
@@ -21,4 +21,4 @@ elif [ "$n_args" -gt 0 ] ; then | |||
21 | interval=$1 | 21 | interval=$1 |
22 | shift | 22 | shift |
23 | fi | 23 | fi |
24 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/sctop.py $comm $interval | 24 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/sctop.py $comm $interval |
diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report index 4eb88c9fc83c..16eb8d65c543 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report | |||
@@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then | |||
7 | shift | 7 | shift |
8 | fi | 8 | fi |
9 | fi | 9 | fi |
10 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts-by-pid.py $comm | 10 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts-by-pid.py $comm |
diff --git a/tools/perf/scripts/python/bin/syscall-counts-report b/tools/perf/scripts/python/bin/syscall-counts-report index cb2f9c5cf17e..0f0e9d453bb4 100644 --- a/tools/perf/scripts/python/bin/syscall-counts-report +++ b/tools/perf/scripts/python/bin/syscall-counts-report | |||
@@ -7,4 +7,4 @@ if [ $# -gt 0 ] ; then | |||
7 | shift | 7 | shift |
8 | fi | 8 | fi |
9 | fi | 9 | fi |
10 | perf trace $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts.py $comm | 10 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/syscall-counts.py $comm |
diff --git a/tools/perf/scripts/python/check-perf-trace.py b/tools/perf/scripts/python/check-perf-trace.py index d9f7893e315c..4647a7694cf6 100644 --- a/tools/perf/scripts/python/check-perf-trace.py +++ b/tools/perf/scripts/python/check-perf-trace.py | |||
@@ -1,4 +1,4 @@ | |||
1 | # perf trace event handlers, generated by perf trace -g python | 1 | # perf script event handlers, generated by perf script -g python |
2 | # (c) 2010, Tom Zanussi <tzanussi@gmail.com> | 2 | # (c) 2010, Tom Zanussi <tzanussi@gmail.com> |
3 | # Licensed under the terms of the GNU GPL License version 2 | 3 | # Licensed under the terms of the GNU GPL License version 2 |
4 | # | 4 | # |
diff --git a/tools/perf/scripts/python/failed-syscalls-by-pid.py b/tools/perf/scripts/python/failed-syscalls-by-pid.py index acd7848717b3..85805fac4116 100644 --- a/tools/perf/scripts/python/failed-syscalls-by-pid.py +++ b/tools/perf/scripts/python/failed-syscalls-by-pid.py | |||
@@ -15,7 +15,7 @@ from perf_trace_context import * | |||
15 | from Core import * | 15 | from Core import * |
16 | from Util import * | 16 | from Util import * |
17 | 17 | ||
18 | usage = "perf trace -s syscall-counts-by-pid.py [comm|pid]\n"; | 18 | usage = "perf script -s syscall-counts-by-pid.py [comm|pid]\n"; |
19 | 19 | ||
20 | for_comm = None | 20 | for_comm = None |
21 | for_pid = None | 21 | for_pid = None |
diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index b934383c3364..74d55ec08aed 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py | |||
@@ -4,7 +4,7 @@ | |||
4 | # | 4 | # |
5 | # Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com> | 5 | # Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com> |
6 | # | 6 | # |
7 | # perf trace event handlers have been generated by perf trace -g python | 7 | # perf script event handlers have been generated by perf script -g python |
8 | # | 8 | # |
9 | # This software is distributed under the terms of the GNU General | 9 | # This software is distributed under the terms of the GNU General |
10 | # Public License ("GPL") version 2 as published by the Free Software | 10 | # Public License ("GPL") version 2 as published by the Free Software |
diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python/sctop.py index 7a6ec2c7d8ab..42c267e292fa 100644 --- a/tools/perf/scripts/python/sctop.py +++ b/tools/perf/scripts/python/sctop.py | |||
@@ -17,7 +17,7 @@ from perf_trace_context import * | |||
17 | from Core import * | 17 | from Core import * |
18 | from Util import * | 18 | from Util import * |
19 | 19 | ||
20 | usage = "perf trace -s sctop.py [comm] [interval]\n"; | 20 | usage = "perf script -s sctop.py [comm] [interval]\n"; |
21 | 21 | ||
22 | for_comm = None | 22 | for_comm = None |
23 | default_interval = 3 | 23 | default_interval = 3 |
diff --git a/tools/perf/scripts/python/syscall-counts-by-pid.py b/tools/perf/scripts/python/syscall-counts-by-pid.py index d1ee3ec10cf2..c64d1c55d745 100644 --- a/tools/perf/scripts/python/syscall-counts-by-pid.py +++ b/tools/perf/scripts/python/syscall-counts-by-pid.py | |||
@@ -14,7 +14,7 @@ from perf_trace_context import * | |||
14 | from Core import * | 14 | from Core import * |
15 | from Util import syscall_name | 15 | from Util import syscall_name |
16 | 16 | ||
17 | usage = "perf trace -s syscall-counts-by-pid.py [comm]\n"; | 17 | usage = "perf script -s syscall-counts-by-pid.py [comm]\n"; |
18 | 18 | ||
19 | for_comm = None | 19 | for_comm = None |
20 | for_pid = None | 20 | for_pid = None |
diff --git a/tools/perf/scripts/python/syscall-counts.py b/tools/perf/scripts/python/syscall-counts.py index ea183dc82d29..b435d3f188e8 100644 --- a/tools/perf/scripts/python/syscall-counts.py +++ b/tools/perf/scripts/python/syscall-counts.py | |||
@@ -15,7 +15,7 @@ from perf_trace_context import * | |||
15 | from Core import * | 15 | from Core import * |
16 | from Util import syscall_name | 16 | from Util import syscall_name |
17 | 17 | ||
18 | usage = "perf trace -s syscall-counts.py [comm]\n"; | 18 | usage = "perf script -s syscall-counts.py [comm]\n"; |
19 | 19 | ||
20 | for_comm = None | 20 | for_comm = None |
21 | 21 | ||
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index e437edb72417..deffb8c96071 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -14,7 +14,9 @@ | |||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | 16 | ||
17 | static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | 17 | static int build_id__mark_dso_hit(event_t *event, |
18 | struct sample_data *sample __used, | ||
19 | struct perf_session *session) | ||
18 | { | 20 | { |
19 | struct addr_location al; | 21 | struct addr_location al; |
20 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 22 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
@@ -35,7 +37,8 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | |||
35 | return 0; | 37 | return 0; |
36 | } | 38 | } |
37 | 39 | ||
38 | static int event__exit_del_thread(event_t *self, struct perf_session *session) | 40 | static int event__exit_del_thread(event_t *self, struct sample_data *sample __used, |
41 | struct perf_session *session) | ||
39 | { | 42 | { |
40 | struct thread *thread = perf_session__findnew(session, self->fork.tid); | 43 | struct thread *thread = perf_session__findnew(session, self->fork.tid); |
41 | 44 | ||
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 0f9b8d7a7d7e..3ccaa1043383 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -4,32 +4,53 @@ | |||
4 | #include <assert.h> | 4 | #include <assert.h> |
5 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | 6 | ||
7 | int cpumap[MAX_NR_CPUS]; | 7 | static struct cpu_map *cpu_map__default_new(void) |
8 | |||
9 | static int default_cpu_map(void) | ||
10 | { | 8 | { |
11 | int nr_cpus, i; | 9 | struct cpu_map *cpus; |
10 | int nr_cpus; | ||
12 | 11 | ||
13 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | 12 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); |
14 | assert(nr_cpus <= MAX_NR_CPUS); | 13 | if (nr_cpus < 0) |
15 | assert((int)nr_cpus >= 0); | 14 | return NULL; |
15 | |||
16 | cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int)); | ||
17 | if (cpus != NULL) { | ||
18 | int i; | ||
19 | for (i = 0; i < nr_cpus; ++i) | ||
20 | cpus->map[i] = i; | ||
16 | 21 | ||
17 | for (i = 0; i < nr_cpus; ++i) | 22 | cpus->nr = nr_cpus; |
18 | cpumap[i] = i; | 23 | } |
19 | 24 | ||
20 | return nr_cpus; | 25 | return cpus; |
21 | } | 26 | } |
22 | 27 | ||
23 | static int read_all_cpu_map(void) | 28 | static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus) |
24 | { | 29 | { |
30 | size_t payload_size = nr_cpus * sizeof(int); | ||
31 | struct cpu_map *cpus = malloc(sizeof(*cpus) + payload_size); | ||
32 | |||
33 | if (cpus != NULL) { | ||
34 | cpus->nr = nr_cpus; | ||
35 | memcpy(cpus->map, tmp_cpus, payload_size); | ||
36 | } | ||
37 | |||
38 | return cpus; | ||
39 | } | ||
40 | |||
41 | static struct cpu_map *cpu_map__read_all_cpu_map(void) | ||
42 | { | ||
43 | struct cpu_map *cpus = NULL; | ||
25 | FILE *onlnf; | 44 | FILE *onlnf; |
26 | int nr_cpus = 0; | 45 | int nr_cpus = 0; |
46 | int *tmp_cpus = NULL, *tmp; | ||
47 | int max_entries = 0; | ||
27 | int n, cpu, prev; | 48 | int n, cpu, prev; |
28 | char sep; | 49 | char sep; |
29 | 50 | ||
30 | onlnf = fopen("/sys/devices/system/cpu/online", "r"); | 51 | onlnf = fopen("/sys/devices/system/cpu/online", "r"); |
31 | if (!onlnf) | 52 | if (!onlnf) |
32 | return default_cpu_map(); | 53 | return cpu_map__default_new(); |
33 | 54 | ||
34 | sep = 0; | 55 | sep = 0; |
35 | prev = -1; | 56 | prev = -1; |
@@ -38,12 +59,28 @@ static int read_all_cpu_map(void) | |||
38 | if (n <= 0) | 59 | if (n <= 0) |
39 | break; | 60 | break; |
40 | if (prev >= 0) { | 61 | if (prev >= 0) { |
41 | assert(nr_cpus + cpu - prev - 1 < MAX_NR_CPUS); | 62 | int new_max = nr_cpus + cpu - prev - 1; |
63 | |||
64 | if (new_max >= max_entries) { | ||
65 | max_entries = new_max + MAX_NR_CPUS / 2; | ||
66 | tmp = realloc(tmp_cpus, max_entries * sizeof(int)); | ||
67 | if (tmp == NULL) | ||
68 | goto out_free_tmp; | ||
69 | tmp_cpus = tmp; | ||
70 | } | ||
71 | |||
42 | while (++prev < cpu) | 72 | while (++prev < cpu) |
43 | cpumap[nr_cpus++] = prev; | 73 | tmp_cpus[nr_cpus++] = prev; |
74 | } | ||
75 | if (nr_cpus == max_entries) { | ||
76 | max_entries += MAX_NR_CPUS; | ||
77 | tmp = realloc(tmp_cpus, max_entries * sizeof(int)); | ||
78 | if (tmp == NULL) | ||
79 | goto out_free_tmp; | ||
80 | tmp_cpus = tmp; | ||
44 | } | 81 | } |
45 | assert (nr_cpus < MAX_NR_CPUS); | 82 | |
46 | cpumap[nr_cpus++] = cpu; | 83 | tmp_cpus[nr_cpus++] = cpu; |
47 | if (n == 2 && sep == '-') | 84 | if (n == 2 && sep == '-') |
48 | prev = cpu; | 85 | prev = cpu; |
49 | else | 86 | else |
@@ -51,24 +88,31 @@ static int read_all_cpu_map(void) | |||
51 | if (n == 1 || sep == '\n') | 88 | if (n == 1 || sep == '\n') |
52 | break; | 89 | break; |
53 | } | 90 | } |
54 | fclose(onlnf); | ||
55 | if (nr_cpus > 0) | ||
56 | return nr_cpus; | ||
57 | 91 | ||
58 | return default_cpu_map(); | 92 | if (nr_cpus > 0) |
93 | cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); | ||
94 | else | ||
95 | cpus = cpu_map__default_new(); | ||
96 | out_free_tmp: | ||
97 | free(tmp_cpus); | ||
98 | fclose(onlnf); | ||
99 | return cpus; | ||
59 | } | 100 | } |
60 | 101 | ||
61 | int read_cpu_map(const char *cpu_list) | 102 | struct cpu_map *cpu_map__new(const char *cpu_list) |
62 | { | 103 | { |
104 | struct cpu_map *cpus = NULL; | ||
63 | unsigned long start_cpu, end_cpu = 0; | 105 | unsigned long start_cpu, end_cpu = 0; |
64 | char *p = NULL; | 106 | char *p = NULL; |
65 | int i, nr_cpus = 0; | 107 | int i, nr_cpus = 0; |
108 | int *tmp_cpus = NULL, *tmp; | ||
109 | int max_entries = 0; | ||
66 | 110 | ||
67 | if (!cpu_list) | 111 | if (!cpu_list) |
68 | return read_all_cpu_map(); | 112 | return cpu_map__read_all_cpu_map(); |
69 | 113 | ||
70 | if (!isdigit(*cpu_list)) | 114 | if (!isdigit(*cpu_list)) |
71 | goto invalid; | 115 | goto out; |
72 | 116 | ||
73 | while (isdigit(*cpu_list)) { | 117 | while (isdigit(*cpu_list)) { |
74 | p = NULL; | 118 | p = NULL; |
@@ -94,21 +138,42 @@ int read_cpu_map(const char *cpu_list) | |||
94 | for (; start_cpu <= end_cpu; start_cpu++) { | 138 | for (; start_cpu <= end_cpu; start_cpu++) { |
95 | /* check for duplicates */ | 139 | /* check for duplicates */ |
96 | for (i = 0; i < nr_cpus; i++) | 140 | for (i = 0; i < nr_cpus; i++) |
97 | if (cpumap[i] == (int)start_cpu) | 141 | if (tmp_cpus[i] == (int)start_cpu) |
98 | goto invalid; | 142 | goto invalid; |
99 | 143 | ||
100 | assert(nr_cpus < MAX_NR_CPUS); | 144 | if (nr_cpus == max_entries) { |
101 | cpumap[nr_cpus++] = (int)start_cpu; | 145 | max_entries += MAX_NR_CPUS; |
146 | tmp = realloc(tmp_cpus, max_entries * sizeof(int)); | ||
147 | if (tmp == NULL) | ||
148 | goto invalid; | ||
149 | tmp_cpus = tmp; | ||
150 | } | ||
151 | tmp_cpus[nr_cpus++] = (int)start_cpu; | ||
102 | } | 152 | } |
103 | if (*p) | 153 | if (*p) |
104 | ++p; | 154 | ++p; |
105 | 155 | ||
106 | cpu_list = p; | 156 | cpu_list = p; |
107 | } | 157 | } |
108 | if (nr_cpus > 0) | ||
109 | return nr_cpus; | ||
110 | 158 | ||
111 | return default_cpu_map(); | 159 | if (nr_cpus > 0) |
160 | cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); | ||
161 | else | ||
162 | cpus = cpu_map__default_new(); | ||
112 | invalid: | 163 | invalid: |
113 | return -1; | 164 | free(tmp_cpus); |
165 | out: | ||
166 | return cpus; | ||
167 | } | ||
168 | |||
169 | struct cpu_map *cpu_map__dummy_new(void) | ||
170 | { | ||
171 | struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); | ||
172 | |||
173 | if (cpus != NULL) { | ||
174 | cpus->nr = 1; | ||
175 | cpus->map[0] = -1; | ||
176 | } | ||
177 | |||
178 | return cpus; | ||
114 | } | 179 | } |
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 3e60f56e490e..f7a4f42f6307 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -1,7 +1,13 @@ | |||
1 | #ifndef __PERF_CPUMAP_H | 1 | #ifndef __PERF_CPUMAP_H |
2 | #define __PERF_CPUMAP_H | 2 | #define __PERF_CPUMAP_H |
3 | 3 | ||
4 | extern int read_cpu_map(const char *cpu_list); | 4 | struct cpu_map { |
5 | extern int cpumap[]; | 5 | int nr; |
6 | int map[]; | ||
7 | }; | ||
8 | |||
9 | struct cpu_map *cpu_map__new(const char *cpu_list); | ||
10 | struct cpu_map *cpu_map__dummy_new(void); | ||
11 | void *cpu_map__delete(struct cpu_map *map); | ||
6 | 12 | ||
7 | #endif /* __PERF_CPUMAP_H */ | 13 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index c8d81b00089d..01bbe8ecec3f 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -46,20 +46,16 @@ int dump_printf(const char *fmt, ...) | |||
46 | return ret; | 46 | return ret; |
47 | } | 47 | } |
48 | 48 | ||
49 | static int dump_printf_color(const char *fmt, const char *color, ...) | 49 | #ifdef NO_NEWT_SUPPORT |
50 | void ui__warning(const char *format, ...) | ||
50 | { | 51 | { |
51 | va_list args; | 52 | va_list args; |
52 | int ret = 0; | ||
53 | 53 | ||
54 | if (dump_trace) { | 54 | va_start(args, format); |
55 | va_start(args, color); | 55 | vfprintf(stderr, format, args); |
56 | ret = color_vfprintf(stdout, color, fmt, args); | 56 | va_end(args); |
57 | va_end(args); | ||
58 | } | ||
59 | |||
60 | return ret; | ||
61 | } | 57 | } |
62 | 58 | #endif | |
63 | 59 | ||
64 | void trace_event(event_t *event) | 60 | void trace_event(event_t *event) |
65 | { | 61 | { |
@@ -70,29 +66,29 @@ void trace_event(event_t *event) | |||
70 | if (!dump_trace) | 66 | if (!dump_trace) |
71 | return; | 67 | return; |
72 | 68 | ||
73 | dump_printf("."); | 69 | printf("."); |
74 | dump_printf_color("\n. ... raw event: size %d bytes\n", color, | 70 | color_fprintf(stdout, color, "\n. ... raw event: size %d bytes\n", |
75 | event->header.size); | 71 | event->header.size); |
76 | 72 | ||
77 | for (i = 0; i < event->header.size; i++) { | 73 | for (i = 0; i < event->header.size; i++) { |
78 | if ((i & 15) == 0) { | 74 | if ((i & 15) == 0) { |
79 | dump_printf("."); | 75 | printf("."); |
80 | dump_printf_color(" %04x: ", color, i); | 76 | color_fprintf(stdout, color, " %04x: ", i); |
81 | } | 77 | } |
82 | 78 | ||
83 | dump_printf_color(" %02x", color, raw_event[i]); | 79 | color_fprintf(stdout, color, " %02x", raw_event[i]); |
84 | 80 | ||
85 | if (((i & 15) == 15) || i == event->header.size-1) { | 81 | if (((i & 15) == 15) || i == event->header.size-1) { |
86 | dump_printf_color(" ", color); | 82 | color_fprintf(stdout, color, " "); |
87 | for (j = 0; j < 15-(i & 15); j++) | 83 | for (j = 0; j < 15-(i & 15); j++) |
88 | dump_printf_color(" ", color); | 84 | color_fprintf(stdout, color, " "); |
89 | for (j = i & ~15; j <= i; j++) { | 85 | for (j = i & ~15; j <= i; j++) { |
90 | dump_printf_color("%c", color, | 86 | color_fprintf(stdout, color, "%c", |
91 | isprint(raw_event[j]) ? | 87 | isprint(raw_event[j]) ? |
92 | raw_event[j] : '.'); | 88 | raw_event[j] : '.'); |
93 | } | 89 | } |
94 | dump_printf_color("\n", color); | 90 | color_fprintf(stdout, color, "\n"); |
95 | } | 91 | } |
96 | } | 92 | } |
97 | dump_printf(".\n"); | 93 | printf(".\n"); |
98 | } | 94 | } |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 7b514082bbaf..ca35fd66b5df 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -35,4 +35,6 @@ int ui_helpline__show_help(const char *format, va_list ap); | |||
35 | #include "ui/progress.h" | 35 | #include "ui/progress.h" |
36 | #endif | 36 | #endif |
37 | 37 | ||
38 | void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | ||
39 | |||
38 | #endif /* __PERF_DEBUG_H */ | 40 | #endif /* __PERF_DEBUG_H */ |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index dab9e754a281..2302ec051bb4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -7,7 +7,7 @@ | |||
7 | #include "strlist.h" | 7 | #include "strlist.h" |
8 | #include "thread.h" | 8 | #include "thread.h" |
9 | 9 | ||
10 | const char *event__name[] = { | 10 | static const char *event__name[] = { |
11 | [0] = "TOTAL", | 11 | [0] = "TOTAL", |
12 | [PERF_RECORD_MMAP] = "MMAP", | 12 | [PERF_RECORD_MMAP] = "MMAP", |
13 | [PERF_RECORD_LOST] = "LOST", | 13 | [PERF_RECORD_LOST] = "LOST", |
@@ -22,13 +22,31 @@ const char *event__name[] = { | |||
22 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", | 22 | [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", |
23 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", | 23 | [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA", |
24 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", | 24 | [PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID", |
25 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", | ||
25 | }; | 26 | }; |
26 | 27 | ||
27 | static pid_t event__synthesize_comm(pid_t pid, int full, | 28 | const char *event__get_event_name(unsigned int id) |
29 | { | ||
30 | if (id >= ARRAY_SIZE(event__name)) | ||
31 | return "INVALID"; | ||
32 | if (!event__name[id]) | ||
33 | return "UNKNOWN"; | ||
34 | return event__name[id]; | ||
35 | } | ||
36 | |||
37 | static struct sample_data synth_sample = { | ||
38 | .pid = -1, | ||
39 | .tid = -1, | ||
40 | .time = -1, | ||
41 | .stream_id = -1, | ||
42 | .cpu = -1, | ||
43 | .period = 1, | ||
44 | }; | ||
45 | |||
46 | static pid_t event__synthesize_comm(event_t *event, pid_t pid, int full, | ||
28 | event__handler_t process, | 47 | event__handler_t process, |
29 | struct perf_session *session) | 48 | struct perf_session *session) |
30 | { | 49 | { |
31 | event_t ev; | ||
32 | char filename[PATH_MAX]; | 50 | char filename[PATH_MAX]; |
33 | char bf[BUFSIZ]; | 51 | char bf[BUFSIZ]; |
34 | FILE *fp; | 52 | FILE *fp; |
@@ -49,34 +67,39 @@ out_race: | |||
49 | return 0; | 67 | return 0; |
50 | } | 68 | } |
51 | 69 | ||
52 | memset(&ev.comm, 0, sizeof(ev.comm)); | 70 | memset(&event->comm, 0, sizeof(event->comm)); |
53 | while (!ev.comm.comm[0] || !ev.comm.pid) { | 71 | |
54 | if (fgets(bf, sizeof(bf), fp) == NULL) | 72 | while (!event->comm.comm[0] || !event->comm.pid) { |
55 | goto out_failure; | 73 | if (fgets(bf, sizeof(bf), fp) == NULL) { |
74 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
75 | goto out; | ||
76 | } | ||
56 | 77 | ||
57 | if (memcmp(bf, "Name:", 5) == 0) { | 78 | if (memcmp(bf, "Name:", 5) == 0) { |
58 | char *name = bf + 5; | 79 | char *name = bf + 5; |
59 | while (*name && isspace(*name)) | 80 | while (*name && isspace(*name)) |
60 | ++name; | 81 | ++name; |
61 | size = strlen(name) - 1; | 82 | size = strlen(name) - 1; |
62 | memcpy(ev.comm.comm, name, size++); | 83 | memcpy(event->comm.comm, name, size++); |
63 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | 84 | } else if (memcmp(bf, "Tgid:", 5) == 0) { |
64 | char *tgids = bf + 5; | 85 | char *tgids = bf + 5; |
65 | while (*tgids && isspace(*tgids)) | 86 | while (*tgids && isspace(*tgids)) |
66 | ++tgids; | 87 | ++tgids; |
67 | tgid = ev.comm.pid = atoi(tgids); | 88 | tgid = event->comm.pid = atoi(tgids); |
68 | } | 89 | } |
69 | } | 90 | } |
70 | 91 | ||
71 | ev.comm.header.type = PERF_RECORD_COMM; | 92 | event->comm.header.type = PERF_RECORD_COMM; |
72 | size = ALIGN(size, sizeof(u64)); | 93 | size = ALIGN(size, sizeof(u64)); |
73 | ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); | 94 | memset(event->comm.comm + size, 0, session->id_hdr_size); |
74 | 95 | event->comm.header.size = (sizeof(event->comm) - | |
96 | (sizeof(event->comm.comm) - size) + | ||
97 | session->id_hdr_size); | ||
75 | if (!full) { | 98 | if (!full) { |
76 | ev.comm.tid = pid; | 99 | event->comm.tid = pid; |
77 | 100 | ||
78 | process(&ev, session); | 101 | process(event, &synth_sample, session); |
79 | goto out_fclose; | 102 | goto out; |
80 | } | 103 | } |
81 | 104 | ||
82 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | 105 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); |
@@ -91,22 +114,19 @@ out_race: | |||
91 | if (*end) | 114 | if (*end) |
92 | continue; | 115 | continue; |
93 | 116 | ||
94 | ev.comm.tid = pid; | 117 | event->comm.tid = pid; |
95 | 118 | ||
96 | process(&ev, session); | 119 | process(event, &synth_sample, session); |
97 | } | 120 | } |
98 | closedir(tasks); | ||
99 | 121 | ||
100 | out_fclose: | 122 | closedir(tasks); |
123 | out: | ||
101 | fclose(fp); | 124 | fclose(fp); |
102 | return tgid; | ||
103 | 125 | ||
104 | out_failure: | 126 | return tgid; |
105 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
106 | return -1; | ||
107 | } | 127 | } |
108 | 128 | ||
109 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | 129 | static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, |
110 | event__handler_t process, | 130 | event__handler_t process, |
111 | struct perf_session *session) | 131 | struct perf_session *session) |
112 | { | 132 | { |
@@ -124,29 +144,25 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
124 | return -1; | 144 | return -1; |
125 | } | 145 | } |
126 | 146 | ||
147 | event->header.type = PERF_RECORD_MMAP; | ||
148 | /* | ||
149 | * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c | ||
150 | */ | ||
151 | event->header.misc = PERF_RECORD_MISC_USER; | ||
152 | |||
127 | while (1) { | 153 | while (1) { |
128 | char bf[BUFSIZ], *pbf = bf; | 154 | char bf[BUFSIZ], *pbf = bf; |
129 | event_t ev = { | ||
130 | .header = { | ||
131 | .type = PERF_RECORD_MMAP, | ||
132 | /* | ||
133 | * Just like the kernel, see __perf_event_mmap | ||
134 | * in kernel/perf_event.c | ||
135 | */ | ||
136 | .misc = PERF_RECORD_MISC_USER, | ||
137 | }, | ||
138 | }; | ||
139 | int n; | 155 | int n; |
140 | size_t size; | 156 | size_t size; |
141 | if (fgets(bf, sizeof(bf), fp) == NULL) | 157 | if (fgets(bf, sizeof(bf), fp) == NULL) |
142 | break; | 158 | break; |
143 | 159 | ||
144 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 160 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
145 | n = hex2u64(pbf, &ev.mmap.start); | 161 | n = hex2u64(pbf, &event->mmap.start); |
146 | if (n < 0) | 162 | if (n < 0) |
147 | continue; | 163 | continue; |
148 | pbf += n + 1; | 164 | pbf += n + 1; |
149 | n = hex2u64(pbf, &ev.mmap.len); | 165 | n = hex2u64(pbf, &event->mmap.len); |
150 | if (n < 0) | 166 | if (n < 0) |
151 | continue; | 167 | continue; |
152 | pbf += n + 3; | 168 | pbf += n + 3; |
@@ -161,19 +177,21 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
161 | continue; | 177 | continue; |
162 | 178 | ||
163 | pbf += 3; | 179 | pbf += 3; |
164 | n = hex2u64(pbf, &ev.mmap.pgoff); | 180 | n = hex2u64(pbf, &event->mmap.pgoff); |
165 | 181 | ||
166 | size = strlen(execname); | 182 | size = strlen(execname); |
167 | execname[size - 1] = '\0'; /* Remove \n */ | 183 | execname[size - 1] = '\0'; /* Remove \n */ |
168 | memcpy(ev.mmap.filename, execname, size); | 184 | memcpy(event->mmap.filename, execname, size); |
169 | size = ALIGN(size, sizeof(u64)); | 185 | size = ALIGN(size, sizeof(u64)); |
170 | ev.mmap.len -= ev.mmap.start; | 186 | event->mmap.len -= event->mmap.start; |
171 | ev.mmap.header.size = (sizeof(ev.mmap) - | 187 | event->mmap.header.size = (sizeof(event->mmap) - |
172 | (sizeof(ev.mmap.filename) - size)); | 188 | (sizeof(event->mmap.filename) - size)); |
173 | ev.mmap.pid = tgid; | 189 | memset(event->mmap.filename + size, 0, session->id_hdr_size); |
174 | ev.mmap.tid = pid; | 190 | event->mmap.header.size += session->id_hdr_size; |
175 | 191 | event->mmap.pid = tgid; | |
176 | process(&ev, session); | 192 | event->mmap.tid = pid; |
193 | |||
194 | process(event, &synth_sample, session); | ||
177 | } | 195 | } |
178 | } | 196 | } |
179 | 197 | ||
@@ -187,20 +205,27 @@ int event__synthesize_modules(event__handler_t process, | |||
187 | { | 205 | { |
188 | struct rb_node *nd; | 206 | struct rb_node *nd; |
189 | struct map_groups *kmaps = &machine->kmaps; | 207 | struct map_groups *kmaps = &machine->kmaps; |
190 | u16 misc; | 208 | event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); |
209 | |||
210 | if (event == NULL) { | ||
211 | pr_debug("Not enough memory synthesizing mmap event " | ||
212 | "for kernel modules\n"); | ||
213 | return -1; | ||
214 | } | ||
215 | |||
216 | event->header.type = PERF_RECORD_MMAP; | ||
191 | 217 | ||
192 | /* | 218 | /* |
193 | * kernel uses 0 for user space maps, see kernel/perf_event.c | 219 | * kernel uses 0 for user space maps, see kernel/perf_event.c |
194 | * __perf_event_mmap | 220 | * __perf_event_mmap |
195 | */ | 221 | */ |
196 | if (machine__is_host(machine)) | 222 | if (machine__is_host(machine)) |
197 | misc = PERF_RECORD_MISC_KERNEL; | 223 | event->header.misc = PERF_RECORD_MISC_KERNEL; |
198 | else | 224 | else |
199 | misc = PERF_RECORD_MISC_GUEST_KERNEL; | 225 | event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; |
200 | 226 | ||
201 | for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); | 227 | for (nd = rb_first(&kmaps->maps[MAP__FUNCTION]); |
202 | nd; nd = rb_next(nd)) { | 228 | nd; nd = rb_next(nd)) { |
203 | event_t ev; | ||
204 | size_t size; | 229 | size_t size; |
205 | struct map *pos = rb_entry(nd, struct map, rb_node); | 230 | struct map *pos = rb_entry(nd, struct map, rb_node); |
206 | 231 | ||
@@ -208,39 +233,78 @@ int event__synthesize_modules(event__handler_t process, | |||
208 | continue; | 233 | continue; |
209 | 234 | ||
210 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); | 235 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); |
211 | memset(&ev, 0, sizeof(ev)); | 236 | event->mmap.header.type = PERF_RECORD_MMAP; |
212 | ev.mmap.header.misc = misc; | 237 | event->mmap.header.size = (sizeof(event->mmap) - |
213 | ev.mmap.header.type = PERF_RECORD_MMAP; | 238 | (sizeof(event->mmap.filename) - size)); |
214 | ev.mmap.header.size = (sizeof(ev.mmap) - | 239 | memset(event->mmap.filename + size, 0, session->id_hdr_size); |
215 | (sizeof(ev.mmap.filename) - size)); | 240 | event->mmap.header.size += session->id_hdr_size; |
216 | ev.mmap.start = pos->start; | 241 | event->mmap.start = pos->start; |
217 | ev.mmap.len = pos->end - pos->start; | 242 | event->mmap.len = pos->end - pos->start; |
218 | ev.mmap.pid = machine->pid; | 243 | event->mmap.pid = machine->pid; |
219 | 244 | ||
220 | memcpy(ev.mmap.filename, pos->dso->long_name, | 245 | memcpy(event->mmap.filename, pos->dso->long_name, |
221 | pos->dso->long_name_len + 1); | 246 | pos->dso->long_name_len + 1); |
222 | process(&ev, session); | 247 | process(event, &synth_sample, session); |
223 | } | 248 | } |
224 | 249 | ||
250 | free(event); | ||
225 | return 0; | 251 | return 0; |
226 | } | 252 | } |
227 | 253 | ||
228 | int event__synthesize_thread(pid_t pid, event__handler_t process, | 254 | static int __event__synthesize_thread(event_t *comm_event, event_t *mmap_event, |
229 | struct perf_session *session) | 255 | pid_t pid, event__handler_t process, |
256 | struct perf_session *session) | ||
230 | { | 257 | { |
231 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); | 258 | pid_t tgid = event__synthesize_comm(comm_event, pid, 1, process, |
259 | session); | ||
232 | if (tgid == -1) | 260 | if (tgid == -1) |
233 | return -1; | 261 | return -1; |
234 | return event__synthesize_mmap_events(pid, tgid, process, session); | 262 | return event__synthesize_mmap_events(mmap_event, pid, tgid, |
263 | process, session); | ||
264 | } | ||
265 | |||
266 | int event__synthesize_thread(pid_t pid, event__handler_t process, | ||
267 | struct perf_session *session) | ||
268 | { | ||
269 | event_t *comm_event, *mmap_event; | ||
270 | int err = -1; | ||
271 | |||
272 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); | ||
273 | if (comm_event == NULL) | ||
274 | goto out; | ||
275 | |||
276 | mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size); | ||
277 | if (mmap_event == NULL) | ||
278 | goto out_free_comm; | ||
279 | |||
280 | err = __event__synthesize_thread(comm_event, mmap_event, pid, | ||
281 | process, session); | ||
282 | free(mmap_event); | ||
283 | out_free_comm: | ||
284 | free(comm_event); | ||
285 | out: | ||
286 | return err; | ||
235 | } | 287 | } |
236 | 288 | ||
237 | void event__synthesize_threads(event__handler_t process, | 289 | int event__synthesize_threads(event__handler_t process, |
238 | struct perf_session *session) | 290 | struct perf_session *session) |
239 | { | 291 | { |
240 | DIR *proc; | 292 | DIR *proc; |
241 | struct dirent dirent, *next; | 293 | struct dirent dirent, *next; |
294 | event_t *comm_event, *mmap_event; | ||
295 | int err = -1; | ||
296 | |||
297 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); | ||
298 | if (comm_event == NULL) | ||
299 | goto out; | ||
300 | |||
301 | mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size); | ||
302 | if (mmap_event == NULL) | ||
303 | goto out_free_comm; | ||
242 | 304 | ||
243 | proc = opendir("/proc"); | 305 | proc = opendir("/proc"); |
306 | if (proc == NULL) | ||
307 | goto out_free_mmap; | ||
244 | 308 | ||
245 | while (!readdir_r(proc, &dirent, &next) && next) { | 309 | while (!readdir_r(proc, &dirent, &next) && next) { |
246 | char *end; | 310 | char *end; |
@@ -249,10 +313,18 @@ void event__synthesize_threads(event__handler_t process, | |||
249 | if (*end) /* only interested in proper numerical dirents */ | 313 | if (*end) /* only interested in proper numerical dirents */ |
250 | continue; | 314 | continue; |
251 | 315 | ||
252 | event__synthesize_thread(pid, process, session); | 316 | __event__synthesize_thread(comm_event, mmap_event, pid, |
317 | process, session); | ||
253 | } | 318 | } |
254 | 319 | ||
255 | closedir(proc); | 320 | closedir(proc); |
321 | err = 0; | ||
322 | out_free_mmap: | ||
323 | free(mmap_event); | ||
324 | out_free_comm: | ||
325 | free(comm_event); | ||
326 | out: | ||
327 | return err; | ||
256 | } | 328 | } |
257 | 329 | ||
258 | struct process_symbol_args { | 330 | struct process_symbol_args { |
@@ -260,7 +332,8 @@ struct process_symbol_args { | |||
260 | u64 start; | 332 | u64 start; |
261 | }; | 333 | }; |
262 | 334 | ||
263 | static int find_symbol_cb(void *arg, const char *name, char type, u64 start) | 335 | static int find_symbol_cb(void *arg, const char *name, char type, |
336 | u64 start, u64 end __used) | ||
264 | { | 337 | { |
265 | struct process_symbol_args *args = arg; | 338 | struct process_symbol_args *args = arg; |
266 | 339 | ||
@@ -286,18 +359,20 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
286 | char path[PATH_MAX]; | 359 | char path[PATH_MAX]; |
287 | char name_buff[PATH_MAX]; | 360 | char name_buff[PATH_MAX]; |
288 | struct map *map; | 361 | struct map *map; |
289 | 362 | int err; | |
290 | event_t ev = { | ||
291 | .header = { | ||
292 | .type = PERF_RECORD_MMAP, | ||
293 | }, | ||
294 | }; | ||
295 | /* | 363 | /* |
296 | * We should get this from /sys/kernel/sections/.text, but till that is | 364 | * We should get this from /sys/kernel/sections/.text, but till that is |
297 | * available use this, and after it is use this as a fallback for older | 365 | * available use this, and after it is use this as a fallback for older |
298 | * kernels. | 366 | * kernels. |
299 | */ | 367 | */ |
300 | struct process_symbol_args args = { .name = symbol_name, }; | 368 | struct process_symbol_args args = { .name = symbol_name, }; |
369 | event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); | ||
370 | |||
371 | if (event == NULL) { | ||
372 | pr_debug("Not enough memory synthesizing mmap event " | ||
373 | "for kernel modules\n"); | ||
374 | return -1; | ||
375 | } | ||
301 | 376 | ||
302 | mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); | 377 | mmap_name = machine__mmap_name(machine, name_buff, sizeof(name_buff)); |
303 | if (machine__is_host(machine)) { | 378 | if (machine__is_host(machine)) { |
@@ -305,10 +380,10 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
305 | * kernel uses PERF_RECORD_MISC_USER for user space maps, | 380 | * kernel uses PERF_RECORD_MISC_USER for user space maps, |
306 | * see kernel/perf_event.c __perf_event_mmap | 381 | * see kernel/perf_event.c __perf_event_mmap |
307 | */ | 382 | */ |
308 | ev.header.misc = PERF_RECORD_MISC_KERNEL; | 383 | event->header.misc = PERF_RECORD_MISC_KERNEL; |
309 | filename = "/proc/kallsyms"; | 384 | filename = "/proc/kallsyms"; |
310 | } else { | 385 | } else { |
311 | ev.header.misc = PERF_RECORD_MISC_GUEST_KERNEL; | 386 | event->header.misc = PERF_RECORD_MISC_GUEST_KERNEL; |
312 | if (machine__is_default_guest(machine)) | 387 | if (machine__is_default_guest(machine)) |
313 | filename = (char *) symbol_conf.default_guest_kallsyms; | 388 | filename = (char *) symbol_conf.default_guest_kallsyms; |
314 | else { | 389 | else { |
@@ -321,17 +396,21 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
321 | return -ENOENT; | 396 | return -ENOENT; |
322 | 397 | ||
323 | map = machine->vmlinux_maps[MAP__FUNCTION]; | 398 | map = machine->vmlinux_maps[MAP__FUNCTION]; |
324 | size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | 399 | size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), |
325 | "%s%s", mmap_name, symbol_name) + 1; | 400 | "%s%s", mmap_name, symbol_name) + 1; |
326 | size = ALIGN(size, sizeof(u64)); | 401 | size = ALIGN(size, sizeof(u64)); |
327 | ev.mmap.header.size = (sizeof(ev.mmap) - | 402 | event->mmap.header.type = PERF_RECORD_MMAP; |
328 | (sizeof(ev.mmap.filename) - size)); | 403 | event->mmap.header.size = (sizeof(event->mmap) - |
329 | ev.mmap.pgoff = args.start; | 404 | (sizeof(event->mmap.filename) - size) + session->id_hdr_size); |
330 | ev.mmap.start = map->start; | 405 | event->mmap.pgoff = args.start; |
331 | ev.mmap.len = map->end - ev.mmap.start; | 406 | event->mmap.start = map->start; |
332 | ev.mmap.pid = machine->pid; | 407 | event->mmap.len = map->end - event->mmap.start; |
333 | 408 | event->mmap.pid = machine->pid; | |
334 | return process(&ev, session); | 409 | |
410 | err = process(event, &synth_sample, session); | ||
411 | free(event); | ||
412 | |||
413 | return err; | ||
335 | } | 414 | } |
336 | 415 | ||
337 | static void thread__comm_adjust(struct thread *self, struct hists *hists) | 416 | static void thread__comm_adjust(struct thread *self, struct hists *hists) |
@@ -361,7 +440,8 @@ static int thread__set_comm_adjust(struct thread *self, const char *comm, | |||
361 | return 0; | 440 | return 0; |
362 | } | 441 | } |
363 | 442 | ||
364 | int event__process_comm(event_t *self, struct perf_session *session) | 443 | int event__process_comm(event_t *self, struct sample_data *sample __used, |
444 | struct perf_session *session) | ||
365 | { | 445 | { |
366 | struct thread *thread = perf_session__findnew(session, self->comm.tid); | 446 | struct thread *thread = perf_session__findnew(session, self->comm.tid); |
367 | 447 | ||
@@ -376,7 +456,8 @@ int event__process_comm(event_t *self, struct perf_session *session) | |||
376 | return 0; | 456 | return 0; |
377 | } | 457 | } |
378 | 458 | ||
379 | int event__process_lost(event_t *self, struct perf_session *session) | 459 | int event__process_lost(event_t *self, struct sample_data *sample __used, |
460 | struct perf_session *session) | ||
380 | { | 461 | { |
381 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); | 462 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); |
382 | session->hists.stats.total_lost += self->lost.lost; | 463 | session->hists.stats.total_lost += self->lost.lost; |
@@ -392,7 +473,7 @@ static void event_set_kernel_mmap_len(struct map **maps, event_t *self) | |||
392 | * a zero sized synthesized MMAP event for the kernel. | 473 | * a zero sized synthesized MMAP event for the kernel. |
393 | */ | 474 | */ |
394 | if (maps[MAP__FUNCTION]->end == 0) | 475 | if (maps[MAP__FUNCTION]->end == 0) |
395 | maps[MAP__FUNCTION]->end = ~0UL; | 476 | maps[MAP__FUNCTION]->end = ~0ULL; |
396 | } | 477 | } |
397 | 478 | ||
398 | static int event__process_kernel_mmap(event_t *self, | 479 | static int event__process_kernel_mmap(event_t *self, |
@@ -485,7 +566,8 @@ out_problem: | |||
485 | return -1; | 566 | return -1; |
486 | } | 567 | } |
487 | 568 | ||
488 | int event__process_mmap(event_t *self, struct perf_session *session) | 569 | int event__process_mmap(event_t *self, struct sample_data *sample __used, |
570 | struct perf_session *session) | ||
489 | { | 571 | { |
490 | struct machine *machine; | 572 | struct machine *machine; |
491 | struct thread *thread; | 573 | struct thread *thread; |
@@ -526,7 +608,8 @@ out_problem: | |||
526 | return 0; | 608 | return 0; |
527 | } | 609 | } |
528 | 610 | ||
529 | int event__process_task(event_t *self, struct perf_session *session) | 611 | int event__process_task(event_t *self, struct sample_data *sample __used, |
612 | struct perf_session *session) | ||
530 | { | 613 | { |
531 | struct thread *thread = perf_session__findnew(session, self->fork.tid); | 614 | struct thread *thread = perf_session__findnew(session, self->fork.tid); |
532 | struct thread *parent = perf_session__findnew(session, self->fork.ptid); | 615 | struct thread *parent = perf_session__findnew(session, self->fork.ptid); |
@@ -548,18 +631,19 @@ int event__process_task(event_t *self, struct perf_session *session) | |||
548 | return 0; | 631 | return 0; |
549 | } | 632 | } |
550 | 633 | ||
551 | int event__process(event_t *event, struct perf_session *session) | 634 | int event__process(event_t *event, struct sample_data *sample, |
635 | struct perf_session *session) | ||
552 | { | 636 | { |
553 | switch (event->header.type) { | 637 | switch (event->header.type) { |
554 | case PERF_RECORD_COMM: | 638 | case PERF_RECORD_COMM: |
555 | event__process_comm(event, session); | 639 | event__process_comm(event, sample, session); |
556 | break; | 640 | break; |
557 | case PERF_RECORD_MMAP: | 641 | case PERF_RECORD_MMAP: |
558 | event__process_mmap(event, session); | 642 | event__process_mmap(event, sample, session); |
559 | break; | 643 | break; |
560 | case PERF_RECORD_FORK: | 644 | case PERF_RECORD_FORK: |
561 | case PERF_RECORD_EXIT: | 645 | case PERF_RECORD_EXIT: |
562 | event__process_task(event, session); | 646 | event__process_task(event, sample, session); |
563 | break; | 647 | break; |
564 | default: | 648 | default: |
565 | break; | 649 | break; |
@@ -674,32 +758,8 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
674 | symbol_filter_t filter) | 758 | symbol_filter_t filter) |
675 | { | 759 | { |
676 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 760 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
677 | struct thread *thread; | 761 | struct thread *thread = perf_session__findnew(session, self->ip.pid); |
678 | |||
679 | event__parse_sample(self, session->sample_type, data); | ||
680 | |||
681 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n", | ||
682 | self->header.misc, data->pid, data->tid, data->ip, | ||
683 | data->period, data->cpu); | ||
684 | |||
685 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { | ||
686 | unsigned int i; | ||
687 | |||
688 | dump_printf("... chain: nr:%Lu\n", data->callchain->nr); | ||
689 | 762 | ||
690 | if (!ip_callchain__valid(data->callchain, self)) { | ||
691 | pr_debug("call-chain problem with event, " | ||
692 | "skipping it.\n"); | ||
693 | goto out_filtered; | ||
694 | } | ||
695 | |||
696 | if (dump_trace) { | ||
697 | for (i = 0; i < data->callchain->nr; i++) | ||
698 | dump_printf("..... %2d: %016Lx\n", | ||
699 | i, data->callchain->ips[i]); | ||
700 | } | ||
701 | } | ||
702 | thread = perf_session__findnew(session, self->ip.pid); | ||
703 | if (thread == NULL) | 763 | if (thread == NULL) |
704 | return -1; | 764 | return -1; |
705 | 765 | ||
@@ -766,9 +826,65 @@ out_filtered: | |||
766 | return 0; | 826 | return 0; |
767 | } | 827 | } |
768 | 828 | ||
769 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data) | 829 | static int event__parse_id_sample(const event_t *event, |
830 | struct perf_session *session, | ||
831 | struct sample_data *sample) | ||
770 | { | 832 | { |
771 | const u64 *array = event->sample.array; | 833 | const u64 *array; |
834 | u64 type; | ||
835 | |||
836 | sample->cpu = sample->pid = sample->tid = -1; | ||
837 | sample->stream_id = sample->id = sample->time = -1ULL; | ||
838 | |||
839 | if (!session->sample_id_all) | ||
840 | return 0; | ||
841 | |||
842 | array = event->sample.array; | ||
843 | array += ((event->header.size - | ||
844 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
845 | type = session->sample_type; | ||
846 | |||
847 | if (type & PERF_SAMPLE_CPU) { | ||
848 | u32 *p = (u32 *)array; | ||
849 | sample->cpu = *p; | ||
850 | array--; | ||
851 | } | ||
852 | |||
853 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
854 | sample->stream_id = *array; | ||
855 | array--; | ||
856 | } | ||
857 | |||
858 | if (type & PERF_SAMPLE_ID) { | ||
859 | sample->id = *array; | ||
860 | array--; | ||
861 | } | ||
862 | |||
863 | if (type & PERF_SAMPLE_TIME) { | ||
864 | sample->time = *array; | ||
865 | array--; | ||
866 | } | ||
867 | |||
868 | if (type & PERF_SAMPLE_TID) { | ||
869 | u32 *p = (u32 *)array; | ||
870 | sample->pid = p[0]; | ||
871 | sample->tid = p[1]; | ||
872 | } | ||
873 | |||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
878 | struct sample_data *data) | ||
879 | { | ||
880 | const u64 *array; | ||
881 | u64 type; | ||
882 | |||
883 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
884 | return event__parse_id_sample(event, session, data); | ||
885 | |||
886 | array = event->sample.array; | ||
887 | type = session->sample_type; | ||
772 | 888 | ||
773 | if (type & PERF_SAMPLE_IP) { | 889 | if (type & PERF_SAMPLE_IP) { |
774 | data->ip = event->ip.ip; | 890 | data->ip = event->ip.ip; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8e790dae7026..2b7e91902f10 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -85,6 +85,7 @@ struct build_id_event { | |||
85 | }; | 85 | }; |
86 | 86 | ||
87 | enum perf_user_event_type { /* above any possible kernel type */ | 87 | enum perf_user_event_type { /* above any possible kernel type */ |
88 | PERF_RECORD_USER_TYPE_START = 64, | ||
88 | PERF_RECORD_HEADER_ATTR = 64, | 89 | PERF_RECORD_HEADER_ATTR = 64, |
89 | PERF_RECORD_HEADER_EVENT_TYPE = 65, | 90 | PERF_RECORD_HEADER_EVENT_TYPE = 65, |
90 | PERF_RECORD_HEADER_TRACING_DATA = 66, | 91 | PERF_RECORD_HEADER_TRACING_DATA = 66, |
@@ -135,12 +136,15 @@ void event__print_totals(void); | |||
135 | 136 | ||
136 | struct perf_session; | 137 | struct perf_session; |
137 | 138 | ||
138 | typedef int (*event__handler_t)(event_t *event, struct perf_session *session); | 139 | typedef int (*event__handler_synth_t)(event_t *event, |
140 | struct perf_session *session); | ||
141 | typedef int (*event__handler_t)(event_t *event, struct sample_data *sample, | ||
142 | struct perf_session *session); | ||
139 | 143 | ||
140 | int event__synthesize_thread(pid_t pid, event__handler_t process, | 144 | int event__synthesize_thread(pid_t pid, event__handler_t process, |
141 | struct perf_session *session); | 145 | struct perf_session *session); |
142 | void event__synthesize_threads(event__handler_t process, | 146 | int event__synthesize_threads(event__handler_t process, |
143 | struct perf_session *session); | 147 | struct perf_session *session); |
144 | int event__synthesize_kernel_mmap(event__handler_t process, | 148 | int event__synthesize_kernel_mmap(event__handler_t process, |
145 | struct perf_session *session, | 149 | struct perf_session *session, |
146 | struct machine *machine, | 150 | struct machine *machine, |
@@ -150,18 +154,24 @@ int event__synthesize_modules(event__handler_t process, | |||
150 | struct perf_session *session, | 154 | struct perf_session *session, |
151 | struct machine *machine); | 155 | struct machine *machine); |
152 | 156 | ||
153 | int event__process_comm(event_t *self, struct perf_session *session); | 157 | int event__process_comm(event_t *self, struct sample_data *sample, |
154 | int event__process_lost(event_t *self, struct perf_session *session); | 158 | struct perf_session *session); |
155 | int event__process_mmap(event_t *self, struct perf_session *session); | 159 | int event__process_lost(event_t *self, struct sample_data *sample, |
156 | int event__process_task(event_t *self, struct perf_session *session); | 160 | struct perf_session *session); |
157 | int event__process(event_t *event, struct perf_session *session); | 161 | int event__process_mmap(event_t *self, struct sample_data *sample, |
162 | struct perf_session *session); | ||
163 | int event__process_task(event_t *self, struct sample_data *sample, | ||
164 | struct perf_session *session); | ||
165 | int event__process(event_t *event, struct sample_data *sample, | ||
166 | struct perf_session *session); | ||
158 | 167 | ||
159 | struct addr_location; | 168 | struct addr_location; |
160 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 169 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
161 | struct addr_location *al, struct sample_data *data, | 170 | struct addr_location *al, struct sample_data *data, |
162 | symbol_filter_t filter); | 171 | symbol_filter_t filter); |
163 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data); | 172 | int event__parse_sample(const event_t *event, struct perf_session *session, |
173 | struct sample_data *sample); | ||
164 | 174 | ||
165 | extern const char *event__name[]; | 175 | const char *event__get_event_name(unsigned int id); |
166 | 176 | ||
167 | #endif /* __PERF_RECORD_H */ | 177 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c new file mode 100644 index 000000000000..f5cfed60af98 --- /dev/null +++ b/tools/perf/util/evsel.c | |||
@@ -0,0 +1,201 @@ | |||
1 | #include "evsel.h" | ||
2 | #include "../perf.h" | ||
3 | #include "util.h" | ||
4 | #include "cpumap.h" | ||
5 | #include "thread.h" | ||
6 | |||
7 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
8 | |||
9 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | ||
10 | { | ||
11 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | ||
12 | |||
13 | if (evsel != NULL) { | ||
14 | evsel->idx = idx; | ||
15 | evsel->attr = *attr; | ||
16 | INIT_LIST_HEAD(&evsel->node); | ||
17 | } | ||
18 | |||
19 | return evsel; | ||
20 | } | ||
21 | |||
22 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
23 | { | ||
24 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); | ||
25 | return evsel->fd != NULL ? 0 : -ENOMEM; | ||
26 | } | ||
27 | |||
28 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | ||
29 | { | ||
30 | evsel->counts = zalloc((sizeof(*evsel->counts) + | ||
31 | (ncpus * sizeof(struct perf_counts_values)))); | ||
32 | return evsel->counts != NULL ? 0 : -ENOMEM; | ||
33 | } | ||
34 | |||
35 | void perf_evsel__free_fd(struct perf_evsel *evsel) | ||
36 | { | ||
37 | xyarray__delete(evsel->fd); | ||
38 | evsel->fd = NULL; | ||
39 | } | ||
40 | |||
41 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
42 | { | ||
43 | int cpu, thread; | ||
44 | |||
45 | for (cpu = 0; cpu < ncpus; cpu++) | ||
46 | for (thread = 0; thread < nthreads; ++thread) { | ||
47 | close(FD(evsel, cpu, thread)); | ||
48 | FD(evsel, cpu, thread) = -1; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
53 | { | ||
54 | assert(list_empty(&evsel->node)); | ||
55 | xyarray__delete(evsel->fd); | ||
56 | free(evsel); | ||
57 | } | ||
58 | |||
59 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | ||
60 | int cpu, int thread, bool scale) | ||
61 | { | ||
62 | struct perf_counts_values count; | ||
63 | size_t nv = scale ? 3 : 1; | ||
64 | |||
65 | if (FD(evsel, cpu, thread) < 0) | ||
66 | return -EINVAL; | ||
67 | |||
68 | if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0) | ||
69 | return -ENOMEM; | ||
70 | |||
71 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) | ||
72 | return -errno; | ||
73 | |||
74 | if (scale) { | ||
75 | if (count.run == 0) | ||
76 | count.val = 0; | ||
77 | else if (count.run < count.ena) | ||
78 | count.val = (u64)((double)count.val * count.ena / count.run + 0.5); | ||
79 | } else | ||
80 | count.ena = count.run = 0; | ||
81 | |||
82 | evsel->counts->cpu[cpu] = count; | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | int __perf_evsel__read(struct perf_evsel *evsel, | ||
87 | int ncpus, int nthreads, bool scale) | ||
88 | { | ||
89 | size_t nv = scale ? 3 : 1; | ||
90 | int cpu, thread; | ||
91 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; | ||
92 | |||
93 | aggr->val = 0; | ||
94 | |||
95 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
96 | for (thread = 0; thread < nthreads; thread++) { | ||
97 | if (FD(evsel, cpu, thread) < 0) | ||
98 | continue; | ||
99 | |||
100 | if (readn(FD(evsel, cpu, thread), | ||
101 | &count, nv * sizeof(u64)) < 0) | ||
102 | return -errno; | ||
103 | |||
104 | aggr->val += count.val; | ||
105 | if (scale) { | ||
106 | aggr->ena += count.ena; | ||
107 | aggr->run += count.run; | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | evsel->counts->scaled = 0; | ||
113 | if (scale) { | ||
114 | if (aggr->run == 0) { | ||
115 | evsel->counts->scaled = -1; | ||
116 | aggr->val = 0; | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | if (aggr->run < aggr->ena) { | ||
121 | evsel->counts->scaled = 1; | ||
122 | aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5); | ||
123 | } | ||
124 | } else | ||
125 | aggr->ena = aggr->run = 0; | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||
131 | struct thread_map *threads) | ||
132 | { | ||
133 | int cpu, thread; | ||
134 | |||
135 | if (evsel->fd == NULL && | ||
136 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | ||
137 | return -1; | ||
138 | |||
139 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
140 | for (thread = 0; thread < threads->nr; thread++) { | ||
141 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | ||
142 | threads->map[thread], | ||
143 | cpus->map[cpu], -1, 0); | ||
144 | if (FD(evsel, cpu, thread) < 0) | ||
145 | goto out_close; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | return 0; | ||
150 | |||
151 | out_close: | ||
152 | do { | ||
153 | while (--thread >= 0) { | ||
154 | close(FD(evsel, cpu, thread)); | ||
155 | FD(evsel, cpu, thread) = -1; | ||
156 | } | ||
157 | thread = threads->nr; | ||
158 | } while (--cpu >= 0); | ||
159 | return -1; | ||
160 | } | ||
161 | |||
162 | static struct { | ||
163 | struct cpu_map map; | ||
164 | int cpus[1]; | ||
165 | } empty_cpu_map = { | ||
166 | .map.nr = 1, | ||
167 | .cpus = { -1, }, | ||
168 | }; | ||
169 | |||
170 | static struct { | ||
171 | struct thread_map map; | ||
172 | int threads[1]; | ||
173 | } empty_thread_map = { | ||
174 | .map.nr = 1, | ||
175 | .threads = { -1, }, | ||
176 | }; | ||
177 | |||
178 | int perf_evsel__open(struct perf_evsel *evsel, | ||
179 | struct cpu_map *cpus, struct thread_map *threads) | ||
180 | { | ||
181 | |||
182 | if (cpus == NULL) { | ||
183 | /* Work around old compiler warnings about strict aliasing */ | ||
184 | cpus = &empty_cpu_map.map; | ||
185 | } | ||
186 | |||
187 | if (threads == NULL) | ||
188 | threads = &empty_thread_map.map; | ||
189 | |||
190 | return __perf_evsel__open(evsel, cpus, threads); | ||
191 | } | ||
192 | |||
193 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) | ||
194 | { | ||
195 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); | ||
196 | } | ||
197 | |||
198 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) | ||
199 | { | ||
200 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); | ||
201 | } | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h new file mode 100644 index 000000000000..b2d755fe88a5 --- /dev/null +++ b/tools/perf/util/evsel.h | |||
@@ -0,0 +1,115 @@ | |||
1 | #ifndef __PERF_EVSEL_H | ||
2 | #define __PERF_EVSEL_H 1 | ||
3 | |||
4 | #include <linux/list.h> | ||
5 | #include <stdbool.h> | ||
6 | #include "../../../include/linux/perf_event.h" | ||
7 | #include "types.h" | ||
8 | #include "xyarray.h" | ||
9 | |||
10 | struct perf_counts_values { | ||
11 | union { | ||
12 | struct { | ||
13 | u64 val; | ||
14 | u64 ena; | ||
15 | u64 run; | ||
16 | }; | ||
17 | u64 values[3]; | ||
18 | }; | ||
19 | }; | ||
20 | |||
21 | struct perf_counts { | ||
22 | s8 scaled; | ||
23 | struct perf_counts_values aggr; | ||
24 | struct perf_counts_values cpu[]; | ||
25 | }; | ||
26 | |||
27 | struct perf_evsel { | ||
28 | struct list_head node; | ||
29 | struct perf_event_attr attr; | ||
30 | char *filter; | ||
31 | struct xyarray *fd; | ||
32 | struct perf_counts *counts; | ||
33 | int idx; | ||
34 | void *priv; | ||
35 | }; | ||
36 | |||
37 | struct cpu_map; | ||
38 | struct thread_map; | ||
39 | |||
40 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | ||
41 | void perf_evsel__delete(struct perf_evsel *evsel); | ||
42 | |||
43 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
44 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | ||
45 | void perf_evsel__free_fd(struct perf_evsel *evsel); | ||
46 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
47 | |||
48 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); | ||
49 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads); | ||
50 | int perf_evsel__open(struct perf_evsel *evsel, | ||
51 | struct cpu_map *cpus, struct thread_map *threads); | ||
52 | |||
53 | #define perf_evsel__match(evsel, t, c) \ | ||
54 | (evsel->attr.type == PERF_TYPE_##t && \ | ||
55 | evsel->attr.config == PERF_COUNT_##c) | ||
56 | |||
57 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | ||
58 | int cpu, int thread, bool scale); | ||
59 | |||
60 | /** | ||
61 | * perf_evsel__read_on_cpu - Read out the results on a CPU and thread | ||
62 | * | ||
63 | * @evsel - event selector to read value | ||
64 | * @cpu - CPU of interest | ||
65 | * @thread - thread of interest | ||
66 | */ | ||
67 | static inline int perf_evsel__read_on_cpu(struct perf_evsel *evsel, | ||
68 | int cpu, int thread) | ||
69 | { | ||
70 | return __perf_evsel__read_on_cpu(evsel, cpu, thread, false); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * perf_evsel__read_on_cpu_scaled - Read out the results on a CPU and thread, scaled | ||
75 | * | ||
76 | * @evsel - event selector to read value | ||
77 | * @cpu - CPU of interest | ||
78 | * @thread - thread of interest | ||
79 | */ | ||
80 | static inline int perf_evsel__read_on_cpu_scaled(struct perf_evsel *evsel, | ||
81 | int cpu, int thread) | ||
82 | { | ||
83 | return __perf_evsel__read_on_cpu(evsel, cpu, thread, true); | ||
84 | } | ||
85 | |||
86 | int __perf_evsel__read(struct perf_evsel *evsel, int ncpus, int nthreads, | ||
87 | bool scale); | ||
88 | |||
89 | /** | ||
90 | * perf_evsel__read - Read the aggregate results on all CPUs | ||
91 | * | ||
92 | * @evsel - event selector to read value | ||
93 | * @ncpus - Number of cpus affected, from zero | ||
94 | * @nthreads - Number of threads affected, from zero | ||
95 | */ | ||
96 | static inline int perf_evsel__read(struct perf_evsel *evsel, | ||
97 | int ncpus, int nthreads) | ||
98 | { | ||
99 | return __perf_evsel__read(evsel, ncpus, nthreads, false); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * perf_evsel__read_scaled - Read the aggregate results on all CPUs, scaled | ||
104 | * | ||
105 | * @evsel - event selector to read value | ||
106 | * @ncpus - Number of cpus affected, from zero | ||
107 | * @nthreads - Number of threads affected, from zero | ||
108 | */ | ||
109 | static inline int perf_evsel__read_scaled(struct perf_evsel *evsel, | ||
110 | int ncpus, int nthreads) | ||
111 | { | ||
112 | return __perf_evsel__read(evsel, ncpus, nthreads, true); | ||
113 | } | ||
114 | |||
115 | #endif /* __PERF_EVSEL_H */ | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 7cba0551a565..989fa2dee2fd 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -152,6 +152,11 @@ void perf_header__set_feat(struct perf_header *self, int feat) | |||
152 | set_bit(feat, self->adds_features); | 152 | set_bit(feat, self->adds_features); |
153 | } | 153 | } |
154 | 154 | ||
155 | void perf_header__clear_feat(struct perf_header *self, int feat) | ||
156 | { | ||
157 | clear_bit(feat, self->adds_features); | ||
158 | } | ||
159 | |||
155 | bool perf_header__has_feat(const struct perf_header *self, int feat) | 160 | bool perf_header__has_feat(const struct perf_header *self, int feat) |
156 | { | 161 | { |
157 | return test_bit(feat, self->adds_features); | 162 | return test_bit(feat, self->adds_features); |
@@ -433,8 +438,10 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
433 | int idx = 0, err; | 438 | int idx = 0, err; |
434 | 439 | ||
435 | session = container_of(self, struct perf_session, header); | 440 | session = container_of(self, struct perf_session, header); |
436 | if (perf_session__read_build_ids(session, true)) | 441 | |
437 | perf_header__set_feat(self, HEADER_BUILD_ID); | 442 | if (perf_header__has_feat(self, HEADER_BUILD_ID && |
443 | !perf_session__read_build_ids(session, true))) | ||
444 | perf_header__clear_feat(self, HEADER_BUILD_ID); | ||
438 | 445 | ||
439 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 446 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); |
440 | if (!nr_sections) | 447 | if (!nr_sections) |
@@ -456,7 +463,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
456 | 463 | ||
457 | /* Write trace info */ | 464 | /* Write trace info */ |
458 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 465 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); |
459 | read_tracing_data(fd, attrs, nr_counters); | 466 | read_tracing_data(fd, &evsel_list); |
460 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 467 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; |
461 | } | 468 | } |
462 | 469 | ||
@@ -599,7 +606,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
599 | static int perf_header__getbuffer64(struct perf_header *self, | 606 | static int perf_header__getbuffer64(struct perf_header *self, |
600 | int fd, void *buf, size_t size) | 607 | int fd, void *buf, size_t size) |
601 | { | 608 | { |
602 | if (do_read(fd, buf, size) <= 0) | 609 | if (readn(fd, buf, size) <= 0) |
603 | return -1; | 610 | return -1; |
604 | 611 | ||
605 | if (self->needs_swap) | 612 | if (self->needs_swap) |
@@ -655,7 +662,7 @@ int perf_file_header__read(struct perf_file_header *self, | |||
655 | { | 662 | { |
656 | lseek(fd, 0, SEEK_SET); | 663 | lseek(fd, 0, SEEK_SET); |
657 | 664 | ||
658 | if (do_read(fd, self, sizeof(*self)) <= 0 || | 665 | if (readn(fd, self, sizeof(*self)) <= 0 || |
659 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) | 666 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) |
660 | return -1; | 667 | return -1; |
661 | 668 | ||
@@ -816,7 +823,7 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, | |||
816 | struct perf_header *ph, int fd, | 823 | struct perf_header *ph, int fd, |
817 | bool repipe) | 824 | bool repipe) |
818 | { | 825 | { |
819 | if (do_read(fd, self, sizeof(*self)) <= 0 || | 826 | if (readn(fd, self, sizeof(*self)) <= 0 || |
820 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) | 827 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) |
821 | return -1; | 828 | return -1; |
822 | 829 | ||
@@ -941,6 +948,24 @@ u64 perf_header__sample_type(struct perf_header *header) | |||
941 | return type; | 948 | return type; |
942 | } | 949 | } |
943 | 950 | ||
951 | bool perf_header__sample_id_all(const struct perf_header *header) | ||
952 | { | ||
953 | bool value = false, first = true; | ||
954 | int i; | ||
955 | |||
956 | for (i = 0; i < header->attrs; i++) { | ||
957 | struct perf_header_attr *attr = header->attr[i]; | ||
958 | |||
959 | if (first) { | ||
960 | value = attr->attr.sample_id_all; | ||
961 | first = false; | ||
962 | } else if (value != attr->attr.sample_id_all) | ||
963 | die("non matching sample_id_all"); | ||
964 | } | ||
965 | |||
966 | return value; | ||
967 | } | ||
968 | |||
944 | struct perf_event_attr * | 969 | struct perf_event_attr * |
945 | perf_header__find_attr(u64 id, struct perf_header *header) | 970 | perf_header__find_attr(u64 id, struct perf_header *header) |
946 | { | 971 | { |
@@ -987,21 +1012,23 @@ int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | |||
987 | 1012 | ||
988 | ev = malloc(size); | 1013 | ev = malloc(size); |
989 | 1014 | ||
1015 | if (ev == NULL) | ||
1016 | return -ENOMEM; | ||
1017 | |||
990 | ev->attr.attr = *attr; | 1018 | ev->attr.attr = *attr; |
991 | memcpy(ev->attr.id, id, ids * sizeof(u64)); | 1019 | memcpy(ev->attr.id, id, ids * sizeof(u64)); |
992 | 1020 | ||
993 | ev->attr.header.type = PERF_RECORD_HEADER_ATTR; | 1021 | ev->attr.header.type = PERF_RECORD_HEADER_ATTR; |
994 | ev->attr.header.size = size; | 1022 | ev->attr.header.size = size; |
995 | 1023 | ||
996 | err = process(ev, session); | 1024 | err = process(ev, NULL, session); |
997 | 1025 | ||
998 | free(ev); | 1026 | free(ev); |
999 | 1027 | ||
1000 | return err; | 1028 | return err; |
1001 | } | 1029 | } |
1002 | 1030 | ||
1003 | int event__synthesize_attrs(struct perf_header *self, | 1031 | int event__synthesize_attrs(struct perf_header *self, event__handler_t process, |
1004 | event__handler_t process, | ||
1005 | struct perf_session *session) | 1032 | struct perf_session *session) |
1006 | { | 1033 | { |
1007 | struct perf_header_attr *attr; | 1034 | struct perf_header_attr *attr; |
@@ -1071,7 +1098,7 @@ int event__synthesize_event_type(u64 event_id, char *name, | |||
1071 | ev.event_type.header.size = sizeof(ev.event_type) - | 1098 | ev.event_type.header.size = sizeof(ev.event_type) - |
1072 | (sizeof(ev.event_type.event_type.name) - size); | 1099 | (sizeof(ev.event_type.event_type.name) - size); |
1073 | 1100 | ||
1074 | err = process(&ev, session); | 1101 | err = process(&ev, NULL, session); |
1075 | 1102 | ||
1076 | return err; | 1103 | return err; |
1077 | } | 1104 | } |
@@ -1106,8 +1133,7 @@ int event__process_event_type(event_t *self, | |||
1106 | return 0; | 1133 | return 0; |
1107 | } | 1134 | } |
1108 | 1135 | ||
1109 | int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, | 1136 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, |
1110 | int nb_events, | ||
1111 | event__handler_t process, | 1137 | event__handler_t process, |
1112 | struct perf_session *session __unused) | 1138 | struct perf_session *session __unused) |
1113 | { | 1139 | { |
@@ -1118,7 +1144,7 @@ int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, | |||
1118 | memset(&ev, 0, sizeof(ev)); | 1144 | memset(&ev, 0, sizeof(ev)); |
1119 | 1145 | ||
1120 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 1146 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
1121 | size = read_tracing_data_size(fd, pattrs, nb_events); | 1147 | size = read_tracing_data_size(fd, pattrs); |
1122 | if (size <= 0) | 1148 | if (size <= 0) |
1123 | return size; | 1149 | return size; |
1124 | aligned_size = ALIGN(size, sizeof(u64)); | 1150 | aligned_size = ALIGN(size, sizeof(u64)); |
@@ -1126,9 +1152,9 @@ int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, | |||
1126 | ev.tracing_data.header.size = sizeof(ev.tracing_data); | 1152 | ev.tracing_data.header.size = sizeof(ev.tracing_data); |
1127 | ev.tracing_data.size = aligned_size; | 1153 | ev.tracing_data.size = aligned_size; |
1128 | 1154 | ||
1129 | process(&ev, session); | 1155 | process(&ev, NULL, session); |
1130 | 1156 | ||
1131 | err = read_tracing_data(fd, pattrs, nb_events); | 1157 | err = read_tracing_data(fd, pattrs); |
1132 | write_padded(fd, NULL, 0, padding); | 1158 | write_padded(fd, NULL, 0, padding); |
1133 | 1159 | ||
1134 | return aligned_size; | 1160 | return aligned_size; |
@@ -1186,7 +1212,7 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, | |||
1186 | ev.build_id.header.size = sizeof(ev.build_id) + len; | 1212 | ev.build_id.header.size = sizeof(ev.build_id) + len; |
1187 | memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); | 1213 | memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len); |
1188 | 1214 | ||
1189 | err = process(&ev, session); | 1215 | err = process(&ev, NULL, session); |
1190 | 1216 | ||
1191 | return err; | 1217 | return err; |
1192 | } | 1218 | } |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 402ac2454cf8..33f16be7b72f 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -81,9 +81,11 @@ void perf_header_attr__delete(struct perf_header_attr *self); | |||
81 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | 81 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); |
82 | 82 | ||
83 | u64 perf_header__sample_type(struct perf_header *header); | 83 | u64 perf_header__sample_type(struct perf_header *header); |
84 | bool perf_header__sample_id_all(const struct perf_header *header); | ||
84 | struct perf_event_attr * | 85 | struct perf_event_attr * |
85 | perf_header__find_attr(u64 id, struct perf_header *header); | 86 | perf_header__find_attr(u64 id, struct perf_header *header); |
86 | void perf_header__set_feat(struct perf_header *self, int feat); | 87 | void perf_header__set_feat(struct perf_header *self, int feat); |
88 | void perf_header__clear_feat(struct perf_header *self, int feat); | ||
87 | bool perf_header__has_feat(const struct perf_header *self, int feat); | 89 | bool perf_header__has_feat(const struct perf_header *self, int feat); |
88 | 90 | ||
89 | int perf_header__process_sections(struct perf_header *self, int fd, | 91 | int perf_header__process_sections(struct perf_header *self, int fd, |
@@ -111,8 +113,7 @@ int event__synthesize_event_types(event__handler_t process, | |||
111 | int event__process_event_type(event_t *self, | 113 | int event__process_event_type(event_t *self, |
112 | struct perf_session *session); | 114 | struct perf_session *session); |
113 | 115 | ||
114 | int event__synthesize_tracing_data(int fd, struct perf_event_attr *pattrs, | 116 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, |
115 | int nb_events, | ||
116 | event__handler_t process, | 117 | event__handler_t process, |
117 | struct perf_session *session); | 118 | struct perf_session *session); |
118 | int event__process_tracing_data(event_t *self, | 119 | int event__process_tracing_data(event_t *self, |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 76bcc35cf9b1..c749ba6136a0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -1092,6 +1092,12 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | |||
1092 | FILE *file; | 1092 | FILE *file; |
1093 | int err = 0; | 1093 | int err = 0; |
1094 | u64 len; | 1094 | u64 len; |
1095 | char symfs_filename[PATH_MAX]; | ||
1096 | |||
1097 | if (filename) { | ||
1098 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
1099 | symbol_conf.symfs, filename); | ||
1100 | } | ||
1095 | 1101 | ||
1096 | if (filename == NULL) { | 1102 | if (filename == NULL) { |
1097 | if (dso->has_build_id) { | 1103 | if (dso->has_build_id) { |
@@ -1100,9 +1106,9 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | |||
1100 | return -ENOMEM; | 1106 | return -ENOMEM; |
1101 | } | 1107 | } |
1102 | goto fallback; | 1108 | goto fallback; |
1103 | } else if (readlink(filename, command, sizeof(command)) < 0 || | 1109 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || |
1104 | strstr(command, "[kernel.kallsyms]") || | 1110 | strstr(command, "[kernel.kallsyms]") || |
1105 | access(filename, R_OK)) { | 1111 | access(symfs_filename, R_OK)) { |
1106 | free(filename); | 1112 | free(filename); |
1107 | fallback: | 1113 | fallback: |
1108 | /* | 1114 | /* |
@@ -1111,6 +1117,8 @@ fallback: | |||
1111 | * DSO is the same as when 'perf record' ran. | 1117 | * DSO is the same as when 'perf record' ran. |
1112 | */ | 1118 | */ |
1113 | filename = dso->long_name; | 1119 | filename = dso->long_name; |
1120 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
1121 | symbol_conf.symfs, filename); | ||
1114 | free_filename = false; | 1122 | free_filename = false; |
1115 | } | 1123 | } |
1116 | 1124 | ||
@@ -1137,7 +1145,7 @@ fallback: | |||
1137 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", | 1145 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", |
1138 | map__rip_2objdump(map, sym->start), | 1146 | map__rip_2objdump(map, sym->start), |
1139 | map__rip_2objdump(map, sym->end), | 1147 | map__rip_2objdump(map, sym->end), |
1140 | filename, filename); | 1148 | symfs_filename, filename); |
1141 | 1149 | ||
1142 | pr_debug("Executing: %s\n", command); | 1150 | pr_debug("Executing: %s\n", command); |
1143 | 1151 | ||
@@ -1168,10 +1176,13 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
1168 | size_t ret = 0; | 1176 | size_t ret = 0; |
1169 | 1177 | ||
1170 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 1178 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
1171 | if (!event__name[i]) | 1179 | const char *name = event__get_event_name(i); |
1180 | |||
1181 | if (!strcmp(name, "UNKNOWN")) | ||
1172 | continue; | 1182 | continue; |
1173 | ret += fprintf(fp, "%10s events: %10d\n", | 1183 | |
1174 | event__name[i], self->stats.nr_events[i]); | 1184 | ret += fprintf(fp, "%16s events: %10d\n", name, |
1185 | self->stats.nr_events[i]); | ||
1175 | } | 1186 | } |
1176 | 1187 | ||
1177 | return ret; | 1188 | return ret; |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 587d375d3430..ee789856a8c9 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -52,8 +52,10 @@ struct sym_priv { | |||
52 | struct events_stats { | 52 | struct events_stats { |
53 | u64 total_period; | 53 | u64 total_period; |
54 | u64 total_lost; | 54 | u64 total_lost; |
55 | u64 total_invalid_chains; | ||
55 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | 56 | u32 nr_events[PERF_RECORD_HEADER_MAX]; |
56 | u32 nr_unknown_events; | 57 | u32 nr_unknown_events; |
58 | u32 nr_invalid_chains; | ||
57 | }; | 59 | }; |
58 | 60 | ||
59 | enum hist_column { | 61 | enum hist_column { |
diff --git a/tools/perf/util/include/asm/cpufeature.h b/tools/perf/util/include/asm/cpufeature.h new file mode 100644 index 000000000000..acffd5e4d1d4 --- /dev/null +++ b/tools/perf/util/include/asm/cpufeature.h | |||
@@ -0,0 +1,9 @@ | |||
1 | |||
2 | #ifndef PERF_CPUFEATURE_H | ||
3 | #define PERF_CPUFEATURE_H | ||
4 | |||
5 | /* cpufeature.h ... dummy header file for including arch/x86/lib/memcpy_64.S */ | ||
6 | |||
7 | #define X86_FEATURE_REP_GOOD 0 | ||
8 | |||
9 | #endif /* PERF_CPUFEATURE_H */ | ||
diff --git a/tools/perf/util/include/asm/dwarf2.h b/tools/perf/util/include/asm/dwarf2.h new file mode 100644 index 000000000000..bb4198e7837a --- /dev/null +++ b/tools/perf/util/include/asm/dwarf2.h | |||
@@ -0,0 +1,11 @@ | |||
1 | |||
2 | #ifndef PERF_DWARF2_H | ||
3 | #define PERF_DWARF2_H | ||
4 | |||
5 | /* dwarf2.h ... dummy header file for including arch/x86/lib/memcpy_64.S */ | ||
6 | |||
7 | #define CFI_STARTPROC | ||
8 | #define CFI_ENDPROC | ||
9 | |||
10 | #endif /* PERF_DWARF2_H */ | ||
11 | |||
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index bb4ac2e05385..8be0b968ca0b 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -13,6 +13,11 @@ static inline void set_bit(int nr, unsigned long *addr) | |||
13 | addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG); | 13 | addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG); |
14 | } | 14 | } |
15 | 15 | ||
16 | static inline void clear_bit(int nr, unsigned long *addr) | ||
17 | { | ||
18 | addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG)); | ||
19 | } | ||
20 | |||
16 | static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) | 21 | static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) |
17 | { | 22 | { |
18 | return ((1UL << (nr % BITS_PER_LONG)) & | 23 | return ((1UL << (nr % BITS_PER_LONG)) & |
diff --git a/tools/perf/util/include/linux/linkage.h b/tools/perf/util/include/linux/linkage.h new file mode 100644 index 000000000000..06387cffe125 --- /dev/null +++ b/tools/perf/util/include/linux/linkage.h | |||
@@ -0,0 +1,13 @@ | |||
1 | |||
2 | #ifndef PERF_LINUX_LINKAGE_H_ | ||
3 | #define PERF_LINUX_LINKAGE_H_ | ||
4 | |||
5 | /* linkage.h ... for including arch/x86/lib/memcpy_64.S */ | ||
6 | |||
7 | #define ENTRY(name) \ | ||
8 | .globl name; \ | ||
9 | name: | ||
10 | |||
11 | #define ENDPROC(name) | ||
12 | |||
13 | #endif /* PERF_LINUX_LINKAGE_H_ */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 4af5bd59cfd1..5cb6f4bde905 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "../../../include/linux/hw_breakpoint.h" | 1 | #include "../../../include/linux/hw_breakpoint.h" |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include "../perf.h" | 3 | #include "../perf.h" |
4 | #include "evsel.h" | ||
4 | #include "parse-options.h" | 5 | #include "parse-options.h" |
5 | #include "parse-events.h" | 6 | #include "parse-events.h" |
6 | #include "exec_cmd.h" | 7 | #include "exec_cmd.h" |
@@ -12,8 +13,7 @@ | |||
12 | 13 | ||
13 | int nr_counters; | 14 | int nr_counters; |
14 | 15 | ||
15 | struct perf_event_attr attrs[MAX_COUNTERS]; | 16 | LIST_HEAD(evsel_list); |
16 | char *filters[MAX_COUNTERS]; | ||
17 | 17 | ||
18 | struct event_symbol { | 18 | struct event_symbol { |
19 | u8 type; | 19 | u8 type; |
@@ -266,10 +266,10 @@ static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) | |||
266 | return name; | 266 | return name; |
267 | } | 267 | } |
268 | 268 | ||
269 | const char *event_name(int counter) | 269 | const char *event_name(struct perf_evsel *evsel) |
270 | { | 270 | { |
271 | u64 config = attrs[counter].config; | 271 | u64 config = evsel->attr.config; |
272 | int type = attrs[counter].type; | 272 | int type = evsel->attr.type; |
273 | 273 | ||
274 | return __event_name(type, config); | 274 | return __event_name(type, config); |
275 | } | 275 | } |
@@ -434,7 +434,7 @@ parse_single_tracepoint_event(char *sys_name, | |||
434 | id = atoll(id_buf); | 434 | id = atoll(id_buf); |
435 | attr->config = id; | 435 | attr->config = id; |
436 | attr->type = PERF_TYPE_TRACEPOINT; | 436 | attr->type = PERF_TYPE_TRACEPOINT; |
437 | *strp = evt_name + evt_length; | 437 | *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */ |
438 | 438 | ||
439 | attr->sample_type |= PERF_SAMPLE_RAW; | 439 | attr->sample_type |= PERF_SAMPLE_RAW; |
440 | attr->sample_type |= PERF_SAMPLE_TIME; | 440 | attr->sample_type |= PERF_SAMPLE_TIME; |
@@ -490,12 +490,37 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | |||
490 | return EVT_HANDLED_ALL; | 490 | return EVT_HANDLED_ALL; |
491 | } | 491 | } |
492 | 492 | ||
493 | static int store_event_type(const char *orgname) | ||
494 | { | ||
495 | char filename[PATH_MAX], *c; | ||
496 | FILE *file; | ||
497 | int id, n; | ||
498 | |||
499 | sprintf(filename, "%s/", debugfs_path); | ||
500 | strncat(filename, orgname, strlen(orgname)); | ||
501 | strcat(filename, "/id"); | ||
502 | |||
503 | c = strchr(filename, ':'); | ||
504 | if (c) | ||
505 | *c = '/'; | ||
506 | |||
507 | file = fopen(filename, "r"); | ||
508 | if (!file) | ||
509 | return 0; | ||
510 | n = fscanf(file, "%i", &id); | ||
511 | fclose(file); | ||
512 | if (n < 1) { | ||
513 | pr_err("cannot store event ID\n"); | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | return perf_header__push_event(id, orgname); | ||
517 | } | ||
493 | 518 | ||
494 | static enum event_result parse_tracepoint_event(const char **strp, | 519 | static enum event_result parse_tracepoint_event(const char **strp, |
495 | struct perf_event_attr *attr) | 520 | struct perf_event_attr *attr) |
496 | { | 521 | { |
497 | const char *evt_name; | 522 | const char *evt_name; |
498 | char *flags; | 523 | char *flags = NULL, *comma_loc; |
499 | char sys_name[MAX_EVENT_LENGTH]; | 524 | char sys_name[MAX_EVENT_LENGTH]; |
500 | unsigned int sys_length, evt_length; | 525 | unsigned int sys_length, evt_length; |
501 | 526 | ||
@@ -514,6 +539,11 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
514 | sys_name[sys_length] = '\0'; | 539 | sys_name[sys_length] = '\0'; |
515 | evt_name = evt_name + 1; | 540 | evt_name = evt_name + 1; |
516 | 541 | ||
542 | comma_loc = strchr(evt_name, ','); | ||
543 | if (comma_loc) { | ||
544 | /* take the event name up to the comma */ | ||
545 | evt_name = strndup(evt_name, comma_loc - evt_name); | ||
546 | } | ||
517 | flags = strchr(evt_name, ':'); | 547 | flags = strchr(evt_name, ':'); |
518 | if (flags) { | 548 | if (flags) { |
519 | /* split it out: */ | 549 | /* split it out: */ |
@@ -524,14 +554,17 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
524 | evt_length = strlen(evt_name); | 554 | evt_length = strlen(evt_name); |
525 | if (evt_length >= MAX_EVENT_LENGTH) | 555 | if (evt_length >= MAX_EVENT_LENGTH) |
526 | return EVT_FAILED; | 556 | return EVT_FAILED; |
527 | |||
528 | if (strpbrk(evt_name, "*?")) { | 557 | if (strpbrk(evt_name, "*?")) { |
529 | *strp = evt_name + evt_length; | 558 | *strp += strlen(sys_name) + evt_length; |
530 | return parse_multiple_tracepoint_event(sys_name, evt_name, | 559 | return parse_multiple_tracepoint_event(sys_name, evt_name, |
531 | flags); | 560 | flags); |
532 | } else | 561 | } else { |
562 | if (store_event_type(evt_name) < 0) | ||
563 | return EVT_FAILED; | ||
564 | |||
533 | return parse_single_tracepoint_event(sys_name, evt_name, | 565 | return parse_single_tracepoint_event(sys_name, evt_name, |
534 | evt_length, attr, strp); | 566 | evt_length, attr, strp); |
567 | } | ||
535 | } | 568 | } |
536 | 569 | ||
537 | static enum event_result | 570 | static enum event_result |
@@ -774,45 +807,12 @@ modifier: | |||
774 | return ret; | 807 | return ret; |
775 | } | 808 | } |
776 | 809 | ||
777 | static int store_event_type(const char *orgname) | ||
778 | { | ||
779 | char filename[PATH_MAX], *c; | ||
780 | FILE *file; | ||
781 | int id, n; | ||
782 | |||
783 | sprintf(filename, "%s/", debugfs_path); | ||
784 | strncat(filename, orgname, strlen(orgname)); | ||
785 | strcat(filename, "/id"); | ||
786 | |||
787 | c = strchr(filename, ':'); | ||
788 | if (c) | ||
789 | *c = '/'; | ||
790 | |||
791 | file = fopen(filename, "r"); | ||
792 | if (!file) | ||
793 | return 0; | ||
794 | n = fscanf(file, "%i", &id); | ||
795 | fclose(file); | ||
796 | if (n < 1) { | ||
797 | pr_err("cannot store event ID\n"); | ||
798 | return -EINVAL; | ||
799 | } | ||
800 | return perf_header__push_event(id, orgname); | ||
801 | } | ||
802 | |||
803 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 810 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
804 | { | 811 | { |
805 | struct perf_event_attr attr; | 812 | struct perf_event_attr attr; |
806 | enum event_result ret; | 813 | enum event_result ret; |
807 | 814 | ||
808 | if (strchr(str, ':')) | ||
809 | if (store_event_type(str) < 0) | ||
810 | return -1; | ||
811 | |||
812 | for (;;) { | 815 | for (;;) { |
813 | if (nr_counters == MAX_COUNTERS) | ||
814 | return -1; | ||
815 | |||
816 | memset(&attr, 0, sizeof(attr)); | 816 | memset(&attr, 0, sizeof(attr)); |
817 | ret = parse_event_symbols(&str, &attr); | 817 | ret = parse_event_symbols(&str, &attr); |
818 | if (ret == EVT_FAILED) | 818 | if (ret == EVT_FAILED) |
@@ -822,8 +822,13 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
822 | return -1; | 822 | return -1; |
823 | 823 | ||
824 | if (ret != EVT_HANDLED_ALL) { | 824 | if (ret != EVT_HANDLED_ALL) { |
825 | attrs[nr_counters] = attr; | 825 | struct perf_evsel *evsel; |
826 | nr_counters++; | 826 | evsel = perf_evsel__new(&attr, |
827 | nr_counters); | ||
828 | if (evsel == NULL) | ||
829 | return -1; | ||
830 | list_add_tail(&evsel->node, &evsel_list); | ||
831 | ++nr_counters; | ||
827 | } | 832 | } |
828 | 833 | ||
829 | if (*str == 0) | 834 | if (*str == 0) |
@@ -840,21 +845,22 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
840 | int parse_filter(const struct option *opt __used, const char *str, | 845 | int parse_filter(const struct option *opt __used, const char *str, |
841 | int unset __used) | 846 | int unset __used) |
842 | { | 847 | { |
843 | int i = nr_counters - 1; | 848 | struct perf_evsel *last = NULL; |
844 | int len = strlen(str); | 849 | |
850 | if (!list_empty(&evsel_list)) | ||
851 | last = list_entry(evsel_list.prev, struct perf_evsel, node); | ||
845 | 852 | ||
846 | if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) { | 853 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
847 | fprintf(stderr, | 854 | fprintf(stderr, |
848 | "-F option should follow a -e tracepoint option\n"); | 855 | "-F option should follow a -e tracepoint option\n"); |
849 | return -1; | 856 | return -1; |
850 | } | 857 | } |
851 | 858 | ||
852 | filters[i] = malloc(len + 1); | 859 | last->filter = strdup(str); |
853 | if (!filters[i]) { | 860 | if (last->filter == NULL) { |
854 | fprintf(stderr, "not enough memory to hold filter string\n"); | 861 | fprintf(stderr, "not enough memory to hold filter string\n"); |
855 | return -1; | 862 | return -1; |
856 | } | 863 | } |
857 | strcpy(filters[i], str); | ||
858 | 864 | ||
859 | return 0; | 865 | return 0; |
860 | } | 866 | } |
@@ -906,6 +912,47 @@ static void print_tracepoint_events(void) | |||
906 | } | 912 | } |
907 | 913 | ||
908 | /* | 914 | /* |
915 | * Check whether event is in <debugfs_mount_point>/tracing/events | ||
916 | */ | ||
917 | |||
918 | int is_valid_tracepoint(const char *event_string) | ||
919 | { | ||
920 | DIR *sys_dir, *evt_dir; | ||
921 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
922 | char evt_path[MAXPATHLEN]; | ||
923 | char dir_path[MAXPATHLEN]; | ||
924 | |||
925 | if (debugfs_valid_mountpoint(debugfs_path)) | ||
926 | return 0; | ||
927 | |||
928 | sys_dir = opendir(debugfs_path); | ||
929 | if (!sys_dir) | ||
930 | return 0; | ||
931 | |||
932 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { | ||
933 | |||
934 | snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, | ||
935 | sys_dirent.d_name); | ||
936 | evt_dir = opendir(dir_path); | ||
937 | if (!evt_dir) | ||
938 | continue; | ||
939 | |||
940 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | ||
941 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | ||
942 | sys_dirent.d_name, evt_dirent.d_name); | ||
943 | if (!strcmp(evt_path, event_string)) { | ||
944 | closedir(evt_dir); | ||
945 | closedir(sys_dir); | ||
946 | return 1; | ||
947 | } | ||
948 | } | ||
949 | closedir(evt_dir); | ||
950 | } | ||
951 | closedir(sys_dir); | ||
952 | return 0; | ||
953 | } | ||
954 | |||
955 | /* | ||
909 | * Print the help text for the event symbols: | 956 | * Print the help text for the event symbols: |
910 | */ | 957 | */ |
911 | void print_events(void) | 958 | void print_events(void) |
@@ -963,3 +1010,33 @@ void print_events(void) | |||
963 | 1010 | ||
964 | exit(129); | 1011 | exit(129); |
965 | } | 1012 | } |
1013 | |||
1014 | int perf_evsel_list__create_default(void) | ||
1015 | { | ||
1016 | struct perf_evsel *evsel; | ||
1017 | struct perf_event_attr attr; | ||
1018 | |||
1019 | memset(&attr, 0, sizeof(attr)); | ||
1020 | attr.type = PERF_TYPE_HARDWARE; | ||
1021 | attr.config = PERF_COUNT_HW_CPU_CYCLES; | ||
1022 | |||
1023 | evsel = perf_evsel__new(&attr, 0); | ||
1024 | |||
1025 | if (evsel == NULL) | ||
1026 | return -ENOMEM; | ||
1027 | |||
1028 | list_add(&evsel->node, &evsel_list); | ||
1029 | ++nr_counters; | ||
1030 | return 0; | ||
1031 | } | ||
1032 | |||
1033 | void perf_evsel_list__delete(void) | ||
1034 | { | ||
1035 | struct perf_evsel *pos, *n; | ||
1036 | |||
1037 | list_for_each_entry_safe(pos, n, &evsel_list, node) { | ||
1038 | list_del_init(&pos->node); | ||
1039 | perf_evsel__delete(pos); | ||
1040 | } | ||
1041 | nr_counters = 0; | ||
1042 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index fc4ab3fe877a..b82cafb83772 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -4,6 +4,16 @@ | |||
4 | * Parse symbolic events/counts passed in as options: | 4 | * Parse symbolic events/counts passed in as options: |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include "../../../include/linux/perf_event.h" | ||
8 | |||
9 | struct list_head; | ||
10 | struct perf_evsel; | ||
11 | |||
12 | extern struct list_head evsel_list; | ||
13 | |||
14 | int perf_evsel_list__create_default(void); | ||
15 | void perf_evsel_list__delete(void); | ||
16 | |||
7 | struct option; | 17 | struct option; |
8 | 18 | ||
9 | struct tracepoint_path { | 19 | struct tracepoint_path { |
@@ -13,14 +23,11 @@ struct tracepoint_path { | |||
13 | }; | 23 | }; |
14 | 24 | ||
15 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | 25 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); |
16 | extern bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events); | 26 | extern bool have_tracepoints(struct list_head *evsel_list); |
17 | 27 | ||
18 | extern int nr_counters; | 28 | extern int nr_counters; |
19 | 29 | ||
20 | extern struct perf_event_attr attrs[MAX_COUNTERS]; | 30 | const char *event_name(struct perf_evsel *event); |
21 | extern char *filters[MAX_COUNTERS]; | ||
22 | |||
23 | extern const char *event_name(int ctr); | ||
24 | extern const char *__event_name(int type, u64 config); | 31 | extern const char *__event_name(int type, u64 config); |
25 | 32 | ||
26 | extern int parse_events(const struct option *opt, const char *str, int unset); | 33 | extern int parse_events(const struct option *opt, const char *str, int unset); |
@@ -29,9 +36,9 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); | |||
29 | #define EVENTS_HELP_MAX (128*1024) | 36 | #define EVENTS_HELP_MAX (128*1024) |
30 | 37 | ||
31 | extern void print_events(void); | 38 | extern void print_events(void); |
39 | extern int is_valid_tracepoint(const char *event_string); | ||
32 | 40 | ||
33 | extern char debugfs_path[]; | 41 | extern char debugfs_path[]; |
34 | extern int valid_debugfs_mount(const char *debugfs); | 42 | extern int valid_debugfs_mount(const char *debugfs); |
35 | 43 | ||
36 | |||
37 | #endif /* __PERF_PARSE_EVENTS_H */ | 44 | #endif /* __PERF_PARSE_EVENTS_H */ |
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index c7d72dce54b2..abc31a1dac1a 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
@@ -119,6 +119,10 @@ struct option { | |||
119 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } | 119 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG } |
120 | #define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ | 120 | #define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ |
121 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } | 121 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } |
122 | #define OPT_CALLBACK_DEFAULT_NOOPT(s, l, v, a, h, f, d) \ | ||
123 | { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\ | ||
124 | .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ | ||
125 | .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG} | ||
122 | 126 | ||
123 | /* parse_options() will filter out the processed options and leave the | 127 | /* parse_options() will filter out the processed options and leave the |
124 | * non-option argments in argv[]. | 128 | * non-option argments in argv[]. |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 61191c6cbe7a..128aaab0aeda 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -95,7 +95,7 @@ static int init_vmlinux(void) | |||
95 | goto out; | 95 | goto out; |
96 | 96 | ||
97 | if (machine__create_kernel_maps(&machine) < 0) { | 97 | if (machine__create_kernel_maps(&machine) < 0) { |
98 | pr_debug("machine__create_kernel_maps "); | 98 | pr_debug("machine__create_kernel_maps() failed.\n"); |
99 | goto out; | 99 | goto out; |
100 | } | 100 | } |
101 | out: | 101 | out: |
@@ -149,7 +149,8 @@ static int open_vmlinux(const char *module) | |||
149 | { | 149 | { |
150 | const char *path = kernel_get_module_path(module); | 150 | const char *path = kernel_get_module_path(module); |
151 | if (!path) { | 151 | if (!path) { |
152 | pr_err("Failed to find path of %s module", module ?: "kernel"); | 152 | pr_err("Failed to find path of %s module.\n", |
153 | module ?: "kernel"); | ||
153 | return -ENOENT; | 154 | return -ENOENT; |
154 | } | 155 | } |
155 | pr_debug("Try to open %s\n", path); | 156 | pr_debug("Try to open %s\n", path); |
@@ -226,7 +227,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
226 | pr_warning("Warning: No dwarf info found in the vmlinux - " | 227 | pr_warning("Warning: No dwarf info found in the vmlinux - " |
227 | "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); | 228 | "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); |
228 | if (!need_dwarf) { | 229 | if (!need_dwarf) { |
229 | pr_debug("Trying to use symbols.\nn"); | 230 | pr_debug("Trying to use symbols.\n"); |
230 | return 0; | 231 | return 0; |
231 | } | 232 | } |
232 | } | 233 | } |
@@ -295,42 +296,49 @@ static int get_real_path(const char *raw_path, const char *comp_dir, | |||
295 | #define LINEBUF_SIZE 256 | 296 | #define LINEBUF_SIZE 256 |
296 | #define NR_ADDITIONAL_LINES 2 | 297 | #define NR_ADDITIONAL_LINES 2 |
297 | 298 | ||
298 | static int show_one_line(FILE *fp, int l, bool skip, bool show_num) | 299 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) |
299 | { | 300 | { |
300 | char buf[LINEBUF_SIZE]; | 301 | char buf[LINEBUF_SIZE]; |
301 | const char *color = PERF_COLOR_BLUE; | 302 | const char *color = show_num ? "" : PERF_COLOR_BLUE; |
303 | const char *prefix = NULL; | ||
302 | 304 | ||
303 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | 305 | do { |
304 | goto error; | ||
305 | if (!skip) { | ||
306 | if (show_num) | ||
307 | fprintf(stdout, "%7d %s", l, buf); | ||
308 | else | ||
309 | color_fprintf(stdout, color, " %s", buf); | ||
310 | } | ||
311 | |||
312 | while (strlen(buf) == LINEBUF_SIZE - 1 && | ||
313 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
314 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | 306 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) |
315 | goto error; | 307 | goto error; |
316 | if (!skip) { | 308 | if (skip) |
317 | if (show_num) | 309 | continue; |
318 | fprintf(stdout, "%s", buf); | 310 | if (!prefix) { |
319 | else | 311 | prefix = show_num ? "%7d " : " "; |
320 | color_fprintf(stdout, color, "%s", buf); | 312 | color_fprintf(stdout, color, prefix, l); |
321 | } | 313 | } |
322 | } | 314 | color_fprintf(stdout, color, "%s", buf); |
323 | 315 | ||
324 | return 0; | 316 | } while (strchr(buf, '\n') == NULL); |
317 | |||
318 | return 1; | ||
325 | error: | 319 | error: |
326 | if (feof(fp)) | 320 | if (ferror(fp)) { |
327 | pr_warning("Source file is shorter than expected.\n"); | ||
328 | else | ||
329 | pr_warning("File read error: %s\n", strerror(errno)); | 321 | pr_warning("File read error: %s\n", strerror(errno)); |
322 | return -1; | ||
323 | } | ||
324 | return 0; | ||
325 | } | ||
330 | 326 | ||
331 | return -1; | 327 | static int _show_one_line(FILE *fp, int l, bool skip, bool show_num) |
328 | { | ||
329 | int rv = __show_one_line(fp, l, skip, show_num); | ||
330 | if (rv == 0) { | ||
331 | pr_warning("Source file is shorter than expected.\n"); | ||
332 | rv = -1; | ||
333 | } | ||
334 | return rv; | ||
332 | } | 335 | } |
333 | 336 | ||
337 | #define show_one_line_with_num(f,l) _show_one_line(f,l,false,true) | ||
338 | #define show_one_line(f,l) _show_one_line(f,l,false,false) | ||
339 | #define skip_one_line(f,l) _show_one_line(f,l,true,false) | ||
340 | #define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false) | ||
341 | |||
334 | /* | 342 | /* |
335 | * Show line-range always requires debuginfo to find source file and | 343 | * Show line-range always requires debuginfo to find source file and |
336 | * line number. | 344 | * line number. |
@@ -379,7 +387,7 @@ int show_line_range(struct line_range *lr, const char *module) | |||
379 | fprintf(stdout, "<%s:%d>\n", lr->function, | 387 | fprintf(stdout, "<%s:%d>\n", lr->function, |
380 | lr->start - lr->offset); | 388 | lr->start - lr->offset); |
381 | else | 389 | else |
382 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | 390 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); |
383 | 391 | ||
384 | fp = fopen(lr->path, "r"); | 392 | fp = fopen(lr->path, "r"); |
385 | if (fp == NULL) { | 393 | if (fp == NULL) { |
@@ -388,26 +396,30 @@ int show_line_range(struct line_range *lr, const char *module) | |||
388 | return -errno; | 396 | return -errno; |
389 | } | 397 | } |
390 | /* Skip to starting line number */ | 398 | /* Skip to starting line number */ |
391 | while (l < lr->start && ret >= 0) | 399 | while (l < lr->start) { |
392 | ret = show_one_line(fp, l++, true, false); | 400 | ret = skip_one_line(fp, l++); |
393 | if (ret < 0) | 401 | if (ret < 0) |
394 | goto end; | 402 | goto end; |
403 | } | ||
395 | 404 | ||
396 | list_for_each_entry(ln, &lr->line_list, list) { | 405 | list_for_each_entry(ln, &lr->line_list, list) { |
397 | while (ln->line > l && ret >= 0) | 406 | for (; ln->line > l; l++) { |
398 | ret = show_one_line(fp, (l++) - lr->offset, | 407 | ret = show_one_line(fp, l - lr->offset); |
399 | false, false); | 408 | if (ret < 0) |
400 | if (ret >= 0) | 409 | goto end; |
401 | ret = show_one_line(fp, (l++) - lr->offset, | 410 | } |
402 | false, true); | 411 | ret = show_one_line_with_num(fp, l++ - lr->offset); |
403 | if (ret < 0) | 412 | if (ret < 0) |
404 | goto end; | 413 | goto end; |
405 | } | 414 | } |
406 | 415 | ||
407 | if (lr->end == INT_MAX) | 416 | if (lr->end == INT_MAX) |
408 | lr->end = l + NR_ADDITIONAL_LINES; | 417 | lr->end = l + NR_ADDITIONAL_LINES; |
409 | while (l <= lr->end && !feof(fp) && ret >= 0) | 418 | while (l <= lr->end) { |
410 | ret = show_one_line(fp, (l++) - lr->offset, false, false); | 419 | ret = show_one_line_or_eof(fp, l++ - lr->offset); |
420 | if (ret <= 0) | ||
421 | break; | ||
422 | } | ||
411 | end: | 423 | end: |
412 | fclose(fp); | 424 | fclose(fp); |
413 | return ret; | 425 | return ret; |
@@ -466,7 +478,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
466 | 478 | ||
467 | fd = open_vmlinux(module); | 479 | fd = open_vmlinux(module); |
468 | if (fd < 0) { | 480 | if (fd < 0) { |
469 | pr_warning("Failed to open debuginfo file.\n"); | 481 | pr_warning("Failed to open debug information file.\n"); |
470 | return fd; | 482 | return fd; |
471 | } | 483 | } |
472 | 484 | ||
@@ -526,56 +538,87 @@ int show_available_vars(struct perf_probe_event *pevs __unused, | |||
526 | } | 538 | } |
527 | #endif | 539 | #endif |
528 | 540 | ||
541 | static int parse_line_num(char **ptr, int *val, const char *what) | ||
542 | { | ||
543 | const char *start = *ptr; | ||
544 | |||
545 | errno = 0; | ||
546 | *val = strtol(*ptr, ptr, 0); | ||
547 | if (errno || *ptr == start) { | ||
548 | semantic_error("'%s' is not a valid number.\n", what); | ||
549 | return -EINVAL; | ||
550 | } | ||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | /* | ||
555 | * Stuff 'lr' according to the line range described by 'arg'. | ||
556 | * The line range syntax is described by: | ||
557 | * | ||
558 | * SRC[:SLN[+NUM|-ELN]] | ||
559 | * FNC[:SLN[+NUM|-ELN]] | ||
560 | */ | ||
529 | int parse_line_range_desc(const char *arg, struct line_range *lr) | 561 | int parse_line_range_desc(const char *arg, struct line_range *lr) |
530 | { | 562 | { |
531 | const char *ptr; | 563 | char *range, *name = strdup(arg); |
532 | char *tmp; | 564 | int err; |
533 | /* | 565 | |
534 | * <Syntax> | 566 | if (!name) |
535 | * SRC:SLN[+NUM|-ELN] | 567 | return -ENOMEM; |
536 | * FUNC[:SLN[+NUM|-ELN]] | 568 | |
537 | */ | 569 | lr->start = 0; |
538 | ptr = strchr(arg, ':'); | 570 | lr->end = INT_MAX; |
539 | if (ptr) { | 571 | |
540 | lr->start = (int)strtoul(ptr + 1, &tmp, 0); | 572 | range = strchr(name, ':'); |
541 | if (*tmp == '+') { | 573 | if (range) { |
542 | lr->end = lr->start + (int)strtoul(tmp + 1, &tmp, 0); | 574 | *range++ = '\0'; |
543 | lr->end--; /* | 575 | |
544 | * Adjust the number of lines here. | 576 | err = parse_line_num(&range, &lr->start, "start line"); |
545 | * If the number of lines == 1, the | 577 | if (err) |
546 | * the end of line should be equal to | 578 | goto err; |
547 | * the start of line. | 579 | |
548 | */ | 580 | if (*range == '+' || *range == '-') { |
549 | } else if (*tmp == '-') | 581 | const char c = *range++; |
550 | lr->end = (int)strtoul(tmp + 1, &tmp, 0); | 582 | |
551 | else | 583 | err = parse_line_num(&range, &lr->end, "end line"); |
552 | lr->end = INT_MAX; | 584 | if (err) |
585 | goto err; | ||
586 | |||
587 | if (c == '+') { | ||
588 | lr->end += lr->start; | ||
589 | /* | ||
590 | * Adjust the number of lines here. | ||
591 | * If the number of lines == 1, the | ||
592 | * the end of line should be equal to | ||
593 | * the start of line. | ||
594 | */ | ||
595 | lr->end--; | ||
596 | } | ||
597 | } | ||
598 | |||
553 | pr_debug("Line range is %d to %d\n", lr->start, lr->end); | 599 | pr_debug("Line range is %d to %d\n", lr->start, lr->end); |
600 | |||
601 | err = -EINVAL; | ||
554 | if (lr->start > lr->end) { | 602 | if (lr->start > lr->end) { |
555 | semantic_error("Start line must be smaller" | 603 | semantic_error("Start line must be smaller" |
556 | " than end line.\n"); | 604 | " than end line.\n"); |
557 | return -EINVAL; | 605 | goto err; |
558 | } | 606 | } |
559 | if (*tmp != '\0') { | 607 | if (*range != '\0') { |
560 | semantic_error("Tailing with invalid character '%d'.\n", | 608 | semantic_error("Tailing with invalid str '%s'.\n", range); |
561 | *tmp); | 609 | goto err; |
562 | return -EINVAL; | ||
563 | } | 610 | } |
564 | tmp = strndup(arg, (ptr - arg)); | ||
565 | } else { | ||
566 | tmp = strdup(arg); | ||
567 | lr->end = INT_MAX; | ||
568 | } | 611 | } |
569 | 612 | ||
570 | if (tmp == NULL) | 613 | if (strchr(name, '.')) |
571 | return -ENOMEM; | 614 | lr->file = name; |
572 | |||
573 | if (strchr(tmp, '.')) | ||
574 | lr->file = tmp; | ||
575 | else | 615 | else |
576 | lr->function = tmp; | 616 | lr->function = name; |
577 | 617 | ||
578 | return 0; | 618 | return 0; |
619 | err: | ||
620 | free(name); | ||
621 | return err; | ||
579 | } | 622 | } |
580 | 623 | ||
581 | /* Check the name is good for event/group */ | 624 | /* Check the name is good for event/group */ |
@@ -699,39 +742,40 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
699 | 742 | ||
700 | /* Exclusion check */ | 743 | /* Exclusion check */ |
701 | if (pp->lazy_line && pp->line) { | 744 | if (pp->lazy_line && pp->line) { |
702 | semantic_error("Lazy pattern can't be used with line number."); | 745 | semantic_error("Lazy pattern can't be used with" |
746 | " line number.\n"); | ||
703 | return -EINVAL; | 747 | return -EINVAL; |
704 | } | 748 | } |
705 | 749 | ||
706 | if (pp->lazy_line && pp->offset) { | 750 | if (pp->lazy_line && pp->offset) { |
707 | semantic_error("Lazy pattern can't be used with offset."); | 751 | semantic_error("Lazy pattern can't be used with offset.\n"); |
708 | return -EINVAL; | 752 | return -EINVAL; |
709 | } | 753 | } |
710 | 754 | ||
711 | if (pp->line && pp->offset) { | 755 | if (pp->line && pp->offset) { |
712 | semantic_error("Offset can't be used with line number."); | 756 | semantic_error("Offset can't be used with line number.\n"); |
713 | return -EINVAL; | 757 | return -EINVAL; |
714 | } | 758 | } |
715 | 759 | ||
716 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { | 760 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { |
717 | semantic_error("File always requires line number or " | 761 | semantic_error("File always requires line number or " |
718 | "lazy pattern."); | 762 | "lazy pattern.\n"); |
719 | return -EINVAL; | 763 | return -EINVAL; |
720 | } | 764 | } |
721 | 765 | ||
722 | if (pp->offset && !pp->function) { | 766 | if (pp->offset && !pp->function) { |
723 | semantic_error("Offset requires an entry function."); | 767 | semantic_error("Offset requires an entry function.\n"); |
724 | return -EINVAL; | 768 | return -EINVAL; |
725 | } | 769 | } |
726 | 770 | ||
727 | if (pp->retprobe && !pp->function) { | 771 | if (pp->retprobe && !pp->function) { |
728 | semantic_error("Return probe requires an entry function."); | 772 | semantic_error("Return probe requires an entry function.\n"); |
729 | return -EINVAL; | 773 | return -EINVAL; |
730 | } | 774 | } |
731 | 775 | ||
732 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { | 776 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { |
733 | semantic_error("Offset/Line/Lazy pattern can't be used with " | 777 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
734 | "return probe."); | 778 | "return probe.\n"); |
735 | return -EINVAL; | 779 | return -EINVAL; |
736 | } | 780 | } |
737 | 781 | ||
@@ -1005,7 +1049,7 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | |||
1005 | 1049 | ||
1006 | return tmp - buf; | 1050 | return tmp - buf; |
1007 | error: | 1051 | error: |
1008 | pr_debug("Failed to synthesize perf probe argument: %s", | 1052 | pr_debug("Failed to synthesize perf probe argument: %s\n", |
1009 | strerror(-ret)); | 1053 | strerror(-ret)); |
1010 | return ret; | 1054 | return ret; |
1011 | } | 1055 | } |
@@ -1033,13 +1077,13 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | |||
1033 | goto error; | 1077 | goto error; |
1034 | } | 1078 | } |
1035 | if (pp->file) { | 1079 | if (pp->file) { |
1036 | len = strlen(pp->file) - 31; | 1080 | tmp = pp->file; |
1037 | if (len < 0) | 1081 | len = strlen(tmp); |
1038 | len = 0; | 1082 | if (len > 30) { |
1039 | tmp = strchr(pp->file + len, '/'); | 1083 | tmp = strchr(pp->file + len - 30, '/'); |
1040 | if (!tmp) | 1084 | tmp = tmp ? tmp + 1 : pp->file + len - 30; |
1041 | tmp = pp->file + len; | 1085 | } |
1042 | ret = e_snprintf(file, 32, "@%s", tmp + 1); | 1086 | ret = e_snprintf(file, 32, "@%s", tmp); |
1043 | if (ret <= 0) | 1087 | if (ret <= 0) |
1044 | goto error; | 1088 | goto error; |
1045 | } | 1089 | } |
@@ -1055,7 +1099,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | |||
1055 | 1099 | ||
1056 | return buf; | 1100 | return buf; |
1057 | error: | 1101 | error: |
1058 | pr_debug("Failed to synthesize perf probe point: %s", | 1102 | pr_debug("Failed to synthesize perf probe point: %s\n", |
1059 | strerror(-ret)); | 1103 | strerror(-ret)); |
1060 | if (buf) | 1104 | if (buf) |
1061 | free(buf); | 1105 | free(buf); |
@@ -1796,7 +1840,7 @@ static int del_trace_probe_event(int fd, const char *group, | |||
1796 | 1840 | ||
1797 | ret = e_snprintf(buf, 128, "%s:%s", group, event); | 1841 | ret = e_snprintf(buf, 128, "%s:%s", group, event); |
1798 | if (ret < 0) { | 1842 | if (ret < 0) { |
1799 | pr_err("Failed to copy event."); | 1843 | pr_err("Failed to copy event.\n"); |
1800 | return ret; | 1844 | return ret; |
1801 | } | 1845 | } |
1802 | 1846 | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ddf4d4556321..ab83b6ac5d65 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -652,8 +652,8 @@ static_var: | |||
652 | regs = get_arch_regstr(regn); | 652 | regs = get_arch_regstr(regn); |
653 | if (!regs) { | 653 | if (!regs) { |
654 | /* This should be a bug in DWARF or this tool */ | 654 | /* This should be a bug in DWARF or this tool */ |
655 | pr_warning("Mapping for DWARF register number %u " | 655 | pr_warning("Mapping for the register number %u " |
656 | "missing on this architecture.", regn); | 656 | "missing on this architecture.\n", regn); |
657 | return -ERANGE; | 657 | return -ERANGE; |
658 | } | 658 | } |
659 | 659 | ||
@@ -699,13 +699,14 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
699 | if (ret != DW_TAG_pointer_type && | 699 | if (ret != DW_TAG_pointer_type && |
700 | ret != DW_TAG_array_type) { | 700 | ret != DW_TAG_array_type) { |
701 | pr_warning("Failed to cast into string: " | 701 | pr_warning("Failed to cast into string: " |
702 | "%s(%s) is not a pointer nor array.", | 702 | "%s(%s) is not a pointer nor array.\n", |
703 | dwarf_diename(vr_die), dwarf_diename(&type)); | 703 | dwarf_diename(vr_die), dwarf_diename(&type)); |
704 | return -EINVAL; | 704 | return -EINVAL; |
705 | } | 705 | } |
706 | if (ret == DW_TAG_pointer_type) { | 706 | if (ret == DW_TAG_pointer_type) { |
707 | if (die_get_real_type(&type, &type) == NULL) { | 707 | if (die_get_real_type(&type, &type) == NULL) { |
708 | pr_warning("Failed to get a type information."); | 708 | pr_warning("Failed to get a type" |
709 | " information.\n"); | ||
709 | return -ENOENT; | 710 | return -ENOENT; |
710 | } | 711 | } |
711 | while (*ref_ptr) | 712 | while (*ref_ptr) |
@@ -720,7 +721,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
720 | if (!die_compare_name(&type, "char") && | 721 | if (!die_compare_name(&type, "char") && |
721 | !die_compare_name(&type, "unsigned char")) { | 722 | !die_compare_name(&type, "unsigned char")) { |
722 | pr_warning("Failed to cast into string: " | 723 | pr_warning("Failed to cast into string: " |
723 | "%s is not (unsigned) char *.", | 724 | "%s is not (unsigned) char *.\n", |
724 | dwarf_diename(vr_die)); | 725 | dwarf_diename(vr_die)); |
725 | return -EINVAL; | 726 | return -EINVAL; |
726 | } | 727 | } |
@@ -830,8 +831,8 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
830 | return -EINVAL; | 831 | return -EINVAL; |
831 | } | 832 | } |
832 | if (field->name[0] == '[') { | 833 | if (field->name[0] == '[') { |
833 | pr_err("Semantic error: %s is not a pointor nor array.", | 834 | pr_err("Semantic error: %s is not a pointor" |
834 | varname); | 835 | " nor array.\n", varname); |
835 | return -EINVAL; | 836 | return -EINVAL; |
836 | } | 837 | } |
837 | if (field->ref) { | 838 | if (field->ref) { |
@@ -978,7 +979,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | |||
978 | name = dwarf_diename(sp_die); | 979 | name = dwarf_diename(sp_die); |
979 | if (name) { | 980 | if (name) { |
980 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { | 981 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { |
981 | pr_warning("Failed to get entry pc of %s\n", | 982 | pr_warning("Failed to get entry address of %s\n", |
982 | dwarf_diename(sp_die)); | 983 | dwarf_diename(sp_die)); |
983 | return -ENOENT; | 984 | return -ENOENT; |
984 | } | 985 | } |
@@ -994,7 +995,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | |||
994 | if (retprobe) { | 995 | if (retprobe) { |
995 | if (eaddr != paddr) { | 996 | if (eaddr != paddr) { |
996 | pr_warning("Return probe must be on the head of" | 997 | pr_warning("Return probe must be on the head of" |
997 | " a real function\n"); | 998 | " a real function.\n"); |
998 | return -EINVAL; | 999 | return -EINVAL; |
999 | } | 1000 | } |
1000 | tp->retprobe = true; | 1001 | tp->retprobe = true; |
@@ -1033,7 +1034,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1033 | Dwarf_Frame *frame; | 1034 | Dwarf_Frame *frame; |
1034 | if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || | 1035 | if (dwarf_cfi_addrframe(pf->cfi, pf->addr, &frame) != 0 || |
1035 | dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { | 1036 | dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) { |
1036 | pr_warning("Failed to get CFA on 0x%jx\n", | 1037 | pr_warning("Failed to get call frame on 0x%jx\n", |
1037 | (uintmax_t)pf->addr); | 1038 | (uintmax_t)pf->addr); |
1038 | return -ENOENT; | 1039 | return -ENOENT; |
1039 | } | 1040 | } |
@@ -1060,7 +1061,7 @@ static int find_probe_point_by_line(struct probe_finder *pf) | |||
1060 | int ret = 0; | 1061 | int ret = 0; |
1061 | 1062 | ||
1062 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1063 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { |
1063 | pr_warning("No source lines found in this CU.\n"); | 1064 | pr_warning("No source lines found.\n"); |
1064 | return -ENOENT; | 1065 | return -ENOENT; |
1065 | } | 1066 | } |
1066 | 1067 | ||
@@ -1162,7 +1163,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1162 | } | 1163 | } |
1163 | 1164 | ||
1164 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1165 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { |
1165 | pr_warning("No source lines found in this CU.\n"); | 1166 | pr_warning("No source lines found.\n"); |
1166 | return -ENOENT; | 1167 | return -ENOENT; |
1167 | } | 1168 | } |
1168 | 1169 | ||
@@ -1220,7 +1221,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | |||
1220 | else { | 1221 | else { |
1221 | /* Get probe address */ | 1222 | /* Get probe address */ |
1222 | if (dwarf_entrypc(in_die, &addr) != 0) { | 1223 | if (dwarf_entrypc(in_die, &addr) != 0) { |
1223 | pr_warning("Failed to get entry pc of %s.\n", | 1224 | pr_warning("Failed to get entry address of %s.\n", |
1224 | dwarf_diename(in_die)); | 1225 | dwarf_diename(in_die)); |
1225 | param->retval = -ENOENT; | 1226 | param->retval = -ENOENT; |
1226 | return DWARF_CB_ABORT; | 1227 | return DWARF_CB_ABORT; |
@@ -1261,8 +1262,8 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
1261 | param->retval = find_probe_point_lazy(sp_die, pf); | 1262 | param->retval = find_probe_point_lazy(sp_die, pf); |
1262 | else { | 1263 | else { |
1263 | if (dwarf_entrypc(sp_die, &pf->addr) != 0) { | 1264 | if (dwarf_entrypc(sp_die, &pf->addr) != 0) { |
1264 | pr_warning("Failed to get entry pc of %s.\n", | 1265 | pr_warning("Failed to get entry address of " |
1265 | dwarf_diename(sp_die)); | 1266 | "%s.\n", dwarf_diename(sp_die)); |
1266 | param->retval = -ENOENT; | 1267 | param->retval = -ENOENT; |
1267 | return DWARF_CB_ABORT; | 1268 | return DWARF_CB_ABORT; |
1268 | } | 1269 | } |
@@ -1304,7 +1305,7 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1304 | 1305 | ||
1305 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | 1306 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); |
1306 | if (!dbg) { | 1307 | if (!dbg) { |
1307 | pr_warning("No dwarf info found in the vmlinux - " | 1308 | pr_warning("No debug information found in the vmlinux - " |
1308 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | 1309 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
1309 | return -EBADF; | 1310 | return -EBADF; |
1310 | } | 1311 | } |
@@ -1549,7 +1550,7 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | |||
1549 | /* Open the live linux kernel */ | 1550 | /* Open the live linux kernel */ |
1550 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); | 1551 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); |
1551 | if (!dbg) { | 1552 | if (!dbg) { |
1552 | pr_warning("No dwarf info found in the vmlinux - " | 1553 | pr_warning("No debug information found in the vmlinux - " |
1553 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | 1554 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
1554 | ret = -EINVAL; | 1555 | ret = -EINVAL; |
1555 | goto end; | 1556 | goto end; |
@@ -1559,7 +1560,8 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | |||
1559 | addr += bias; | 1560 | addr += bias; |
1560 | /* Find cu die */ | 1561 | /* Find cu die */ |
1561 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { | 1562 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { |
1562 | pr_warning("No CU DIE is found at %lx\n", addr); | 1563 | pr_warning("Failed to find debug information for address %lx\n", |
1564 | addr); | ||
1563 | ret = -EINVAL; | 1565 | ret = -EINVAL; |
1564 | goto end; | 1566 | goto end; |
1565 | } | 1567 | } |
@@ -1684,7 +1686,7 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | |||
1684 | 1686 | ||
1685 | line_list__init(&lf->lr->line_list); | 1687 | line_list__init(&lf->lr->line_list); |
1686 | if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { | 1688 | if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { |
1687 | pr_warning("No source lines found in this CU.\n"); | 1689 | pr_warning("No source lines found.\n"); |
1688 | return -ENOENT; | 1690 | return -ENOENT; |
1689 | } | 1691 | } |
1690 | 1692 | ||
@@ -1809,7 +1811,7 @@ int find_line_range(int fd, struct line_range *lr) | |||
1809 | 1811 | ||
1810 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | 1812 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); |
1811 | if (!dbg) { | 1813 | if (!dbg) { |
1812 | pr_warning("No dwarf info found in the vmlinux - " | 1814 | pr_warning("No debug information found in the vmlinux - " |
1813 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | 1815 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
1814 | return -EBADF; | 1816 | return -EBADF; |
1815 | } | 1817 | } |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index bba69d455699..beaefc3c1223 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -34,9 +34,9 @@ extern int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
34 | bool externs); | 34 | bool externs); |
35 | 35 | ||
36 | #include <dwarf.h> | 36 | #include <dwarf.h> |
37 | #include <libdw.h> | 37 | #include <elfutils/libdw.h> |
38 | #include <libdwfl.h> | 38 | #include <elfutils/libdwfl.h> |
39 | #include <version.h> | 39 | #include <elfutils/version.h> |
40 | 40 | ||
41 | struct probe_finder { | 41 | struct probe_finder { |
42 | struct perf_probe_event *pev; /* Target probe event */ | 42 | struct perf_probe_event *pev; /* Target probe event */ |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index b059dc50cc2d..93680818e244 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 @@ | |||
1 | /* | 1 | /* |
2 | * trace-event-perl. Feed perf trace events to an embedded Perl interpreter. | 2 | * trace-event-perl. Feed perf script events to an embedded Perl interpreter. |
3 | * | 3 | * |
4 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | 4 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> |
5 | * | 5 | * |
@@ -411,8 +411,8 @@ static int perl_generate_script(const char *outfile) | |||
411 | return -1; | 411 | return -1; |
412 | } | 412 | } |
413 | 413 | ||
414 | fprintf(ofp, "# perf trace event handlers, " | 414 | fprintf(ofp, "# perf script event handlers, " |
415 | "generated by perf trace -g perl\n"); | 415 | "generated by perf script -g perl\n"); |
416 | 416 | ||
417 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" | 417 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" |
418 | " License version 2\n\n"); | 418 | " 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 33a632523743..c6d99334bdfa 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) | |||
442 | fprintf(stderr, "couldn't open %s\n", fname); | 442 | fprintf(stderr, "couldn't open %s\n", fname); |
443 | return -1; | 443 | return -1; |
444 | } | 444 | } |
445 | fprintf(ofp, "# perf trace event handlers, " | 445 | fprintf(ofp, "# perf script event handlers, " |
446 | "generated by perf trace -g python\n"); | 446 | "generated by perf script -g python\n"); |
447 | 447 | ||
448 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" | 448 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" |
449 | " License version 2\n\n"); | 449 | " License version 2\n\n"); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index fa9d652c2dc3..313dac2d94ce 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -65,9 +65,49 @@ out_close: | |||
65 | return -1; | 65 | return -1; |
66 | } | 66 | } |
67 | 67 | ||
68 | static void perf_session__id_header_size(struct perf_session *session) | ||
69 | { | ||
70 | struct sample_data *data; | ||
71 | u64 sample_type = session->sample_type; | ||
72 | u16 size = 0; | ||
73 | |||
74 | if (!session->sample_id_all) | ||
75 | goto out; | ||
76 | |||
77 | if (sample_type & PERF_SAMPLE_TID) | ||
78 | size += sizeof(data->tid) * 2; | ||
79 | |||
80 | if (sample_type & PERF_SAMPLE_TIME) | ||
81 | size += sizeof(data->time); | ||
82 | |||
83 | if (sample_type & PERF_SAMPLE_ID) | ||
84 | size += sizeof(data->id); | ||
85 | |||
86 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
87 | size += sizeof(data->stream_id); | ||
88 | |||
89 | if (sample_type & PERF_SAMPLE_CPU) | ||
90 | size += sizeof(data->cpu) * 2; | ||
91 | out: | ||
92 | session->id_hdr_size = size; | ||
93 | } | ||
94 | |||
95 | void perf_session__set_sample_id_all(struct perf_session *session, bool value) | ||
96 | { | ||
97 | session->sample_id_all = value; | ||
98 | perf_session__id_header_size(session); | ||
99 | } | ||
100 | |||
101 | void perf_session__set_sample_type(struct perf_session *session, u64 type) | ||
102 | { | ||
103 | session->sample_type = type; | ||
104 | } | ||
105 | |||
68 | void perf_session__update_sample_type(struct perf_session *self) | 106 | void perf_session__update_sample_type(struct perf_session *self) |
69 | { | 107 | { |
70 | self->sample_type = perf_header__sample_type(&self->header); | 108 | self->sample_type = perf_header__sample_type(&self->header); |
109 | self->sample_id_all = perf_header__sample_id_all(&self->header); | ||
110 | perf_session__id_header_size(self); | ||
71 | } | 111 | } |
72 | 112 | ||
73 | int perf_session__create_kernel_maps(struct perf_session *self) | 113 | int perf_session__create_kernel_maps(struct perf_session *self) |
@@ -85,7 +125,9 @@ static void perf_session__destroy_kernel_maps(struct perf_session *self) | |||
85 | machines__destroy_guest_kernel_maps(&self->machines); | 125 | machines__destroy_guest_kernel_maps(&self->machines); |
86 | } | 126 | } |
87 | 127 | ||
88 | struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) | 128 | struct perf_session *perf_session__new(const char *filename, int mode, |
129 | bool force, bool repipe, | ||
130 | struct perf_event_ops *ops) | ||
89 | { | 131 | { |
90 | size_t len = filename ? strlen(filename) + 1 : 0; | 132 | size_t len = filename ? strlen(filename) + 1 : 0; |
91 | struct perf_session *self = zalloc(sizeof(*self) + len); | 133 | struct perf_session *self = zalloc(sizeof(*self) + len); |
@@ -101,10 +143,20 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
101 | INIT_LIST_HEAD(&self->dead_threads); | 143 | INIT_LIST_HEAD(&self->dead_threads); |
102 | self->hists_tree = RB_ROOT; | 144 | self->hists_tree = RB_ROOT; |
103 | self->last_match = NULL; | 145 | self->last_match = NULL; |
104 | self->mmap_window = 32; | 146 | /* |
147 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | ||
148 | * slices. On 32bit we use 32MB. | ||
149 | */ | ||
150 | #if BITS_PER_LONG == 64 | ||
151 | self->mmap_window = ULLONG_MAX; | ||
152 | #else | ||
153 | self->mmap_window = 32 * 1024 * 1024ULL; | ||
154 | #endif | ||
105 | self->machines = RB_ROOT; | 155 | self->machines = RB_ROOT; |
106 | self->repipe = repipe; | 156 | self->repipe = repipe; |
107 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); | 157 | INIT_LIST_HEAD(&self->ordered_samples.samples); |
158 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | ||
159 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | ||
108 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 160 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); |
109 | 161 | ||
110 | if (mode == O_RDONLY) { | 162 | if (mode == O_RDONLY) { |
@@ -120,6 +172,13 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
120 | } | 172 | } |
121 | 173 | ||
122 | perf_session__update_sample_type(self); | 174 | perf_session__update_sample_type(self); |
175 | |||
176 | if (ops && ops->ordering_requires_timestamps && | ||
177 | ops->ordered_samples && !self->sample_id_all) { | ||
178 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | ||
179 | ops->ordered_samples = false; | ||
180 | } | ||
181 | |||
123 | out: | 182 | out: |
124 | return self; | 183 | return self; |
125 | out_free: | 184 | out_free: |
@@ -230,7 +289,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
230 | return syms; | 289 | return syms; |
231 | } | 290 | } |
232 | 291 | ||
292 | static int process_event_synth_stub(event_t *event __used, | ||
293 | struct perf_session *session __used) | ||
294 | { | ||
295 | dump_printf(": unhandled!\n"); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
233 | static int process_event_stub(event_t *event __used, | 299 | static int process_event_stub(event_t *event __used, |
300 | struct sample_data *sample __used, | ||
234 | struct perf_session *session __used) | 301 | struct perf_session *session __used) |
235 | { | 302 | { |
236 | dump_printf(": unhandled!\n"); | 303 | dump_printf(": unhandled!\n"); |
@@ -262,7 +329,7 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | |||
262 | if (handler->exit == NULL) | 329 | if (handler->exit == NULL) |
263 | handler->exit = process_event_stub; | 330 | handler->exit = process_event_stub; |
264 | if (handler->lost == NULL) | 331 | if (handler->lost == NULL) |
265 | handler->lost = process_event_stub; | 332 | handler->lost = event__process_lost; |
266 | if (handler->read == NULL) | 333 | if (handler->read == NULL) |
267 | handler->read = process_event_stub; | 334 | handler->read = process_event_stub; |
268 | if (handler->throttle == NULL) | 335 | if (handler->throttle == NULL) |
@@ -270,13 +337,13 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | |||
270 | if (handler->unthrottle == NULL) | 337 | if (handler->unthrottle == NULL) |
271 | handler->unthrottle = process_event_stub; | 338 | handler->unthrottle = process_event_stub; |
272 | if (handler->attr == NULL) | 339 | if (handler->attr == NULL) |
273 | handler->attr = process_event_stub; | 340 | handler->attr = process_event_synth_stub; |
274 | if (handler->event_type == NULL) | 341 | if (handler->event_type == NULL) |
275 | handler->event_type = process_event_stub; | 342 | handler->event_type = process_event_synth_stub; |
276 | if (handler->tracing_data == NULL) | 343 | if (handler->tracing_data == NULL) |
277 | handler->tracing_data = process_event_stub; | 344 | handler->tracing_data = process_event_synth_stub; |
278 | if (handler->build_id == NULL) | 345 | if (handler->build_id == NULL) |
279 | handler->build_id = process_event_stub; | 346 | handler->build_id = process_event_synth_stub; |
280 | if (handler->finished_round == NULL) { | 347 | if (handler->finished_round == NULL) { |
281 | if (handler->ordered_samples) | 348 | if (handler->ordered_samples) |
282 | handler->finished_round = process_finished_round; | 349 | handler->finished_round = process_finished_round; |
@@ -386,33 +453,61 @@ static event__swap_op event__swap_ops[] = { | |||
386 | 453 | ||
387 | struct sample_queue { | 454 | struct sample_queue { |
388 | u64 timestamp; | 455 | u64 timestamp; |
389 | struct sample_event *event; | 456 | u64 file_offset; |
457 | event_t *event; | ||
390 | struct list_head list; | 458 | struct list_head list; |
391 | }; | 459 | }; |
392 | 460 | ||
461 | static void perf_session_free_sample_buffers(struct perf_session *session) | ||
462 | { | ||
463 | struct ordered_samples *os = &session->ordered_samples; | ||
464 | |||
465 | while (!list_empty(&os->to_free)) { | ||
466 | struct sample_queue *sq; | ||
467 | |||
468 | sq = list_entry(os->to_free.next, struct sample_queue, list); | ||
469 | list_del(&sq->list); | ||
470 | free(sq); | ||
471 | } | ||
472 | } | ||
473 | |||
474 | static int perf_session_deliver_event(struct perf_session *session, | ||
475 | event_t *event, | ||
476 | struct sample_data *sample, | ||
477 | struct perf_event_ops *ops, | ||
478 | u64 file_offset); | ||
479 | |||
393 | static void flush_sample_queue(struct perf_session *s, | 480 | static void flush_sample_queue(struct perf_session *s, |
394 | struct perf_event_ops *ops) | 481 | struct perf_event_ops *ops) |
395 | { | 482 | { |
396 | struct list_head *head = &s->ordered_samples.samples_head; | 483 | struct ordered_samples *os = &s->ordered_samples; |
397 | u64 limit = s->ordered_samples.next_flush; | 484 | struct list_head *head = &os->samples; |
398 | struct sample_queue *tmp, *iter; | 485 | struct sample_queue *tmp, *iter; |
486 | struct sample_data sample; | ||
487 | u64 limit = os->next_flush; | ||
488 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | ||
399 | 489 | ||
400 | if (!ops->ordered_samples || !limit) | 490 | if (!ops->ordered_samples || !limit) |
401 | return; | 491 | return; |
402 | 492 | ||
403 | list_for_each_entry_safe(iter, tmp, head, list) { | 493 | list_for_each_entry_safe(iter, tmp, head, list) { |
404 | if (iter->timestamp > limit) | 494 | if (iter->timestamp > limit) |
405 | return; | 495 | break; |
406 | 496 | ||
407 | if (iter == s->ordered_samples.last_inserted) | 497 | event__parse_sample(iter->event, s, &sample); |
408 | s->ordered_samples.last_inserted = NULL; | 498 | perf_session_deliver_event(s, iter->event, &sample, ops, |
499 | iter->file_offset); | ||
409 | 500 | ||
410 | ops->sample((event_t *)iter->event, s); | 501 | os->last_flush = iter->timestamp; |
411 | |||
412 | s->ordered_samples.last_flush = iter->timestamp; | ||
413 | list_del(&iter->list); | 502 | list_del(&iter->list); |
414 | free(iter->event); | 503 | list_add(&iter->list, &os->sample_cache); |
415 | free(iter); | 504 | } |
505 | |||
506 | if (list_empty(head)) { | ||
507 | os->last_sample = NULL; | ||
508 | } else if (last_ts <= limit) { | ||
509 | os->last_sample = | ||
510 | list_entry(head->prev, struct sample_queue, list); | ||
416 | } | 511 | } |
417 | } | 512 | } |
418 | 513 | ||
@@ -465,178 +560,265 @@ static int process_finished_round(event_t *event __used, | |||
465 | return 0; | 560 | return 0; |
466 | } | 561 | } |
467 | 562 | ||
468 | static void __queue_sample_end(struct sample_queue *new, struct list_head *head) | ||
469 | { | ||
470 | struct sample_queue *iter; | ||
471 | |||
472 | list_for_each_entry_reverse(iter, head, list) { | ||
473 | if (iter->timestamp < new->timestamp) { | ||
474 | list_add(&new->list, &iter->list); | ||
475 | return; | ||
476 | } | ||
477 | } | ||
478 | |||
479 | list_add(&new->list, head); | ||
480 | } | ||
481 | |||
482 | static void __queue_sample_before(struct sample_queue *new, | ||
483 | struct sample_queue *iter, | ||
484 | struct list_head *head) | ||
485 | { | ||
486 | list_for_each_entry_continue_reverse(iter, head, list) { | ||
487 | if (iter->timestamp < new->timestamp) { | ||
488 | list_add(&new->list, &iter->list); | ||
489 | return; | ||
490 | } | ||
491 | } | ||
492 | |||
493 | list_add(&new->list, head); | ||
494 | } | ||
495 | |||
496 | static void __queue_sample_after(struct sample_queue *new, | ||
497 | struct sample_queue *iter, | ||
498 | struct list_head *head) | ||
499 | { | ||
500 | list_for_each_entry_continue(iter, head, list) { | ||
501 | if (iter->timestamp > new->timestamp) { | ||
502 | list_add_tail(&new->list, &iter->list); | ||
503 | return; | ||
504 | } | ||
505 | } | ||
506 | list_add_tail(&new->list, head); | ||
507 | } | ||
508 | |||
509 | /* The queue is ordered by time */ | 563 | /* The queue is ordered by time */ |
510 | static void __queue_sample_event(struct sample_queue *new, | 564 | static void __queue_event(struct sample_queue *new, struct perf_session *s) |
511 | struct perf_session *s) | ||
512 | { | 565 | { |
513 | struct sample_queue *last_inserted = s->ordered_samples.last_inserted; | 566 | struct ordered_samples *os = &s->ordered_samples; |
514 | struct list_head *head = &s->ordered_samples.samples_head; | 567 | struct sample_queue *sample = os->last_sample; |
568 | u64 timestamp = new->timestamp; | ||
569 | struct list_head *p; | ||
515 | 570 | ||
571 | os->last_sample = new; | ||
516 | 572 | ||
517 | if (!last_inserted) { | 573 | if (!sample) { |
518 | __queue_sample_end(new, head); | 574 | list_add(&new->list, &os->samples); |
575 | os->max_timestamp = timestamp; | ||
519 | return; | 576 | return; |
520 | } | 577 | } |
521 | 578 | ||
522 | /* | 579 | /* |
523 | * Most of the time the current event has a timestamp | 580 | * last_sample might point to some random place in the list as it's |
524 | * very close to the last event inserted, unless we just switched | 581 | * the last queued event. We expect that the new event is close to |
525 | * to another event buffer. Having a sorting based on a list and | 582 | * this. |
526 | * on the last inserted event that is close to the current one is | ||
527 | * probably more efficient than an rbtree based sorting. | ||
528 | */ | 583 | */ |
529 | if (last_inserted->timestamp >= new->timestamp) | 584 | if (sample->timestamp <= timestamp) { |
530 | __queue_sample_before(new, last_inserted, head); | 585 | while (sample->timestamp <= timestamp) { |
531 | else | 586 | p = sample->list.next; |
532 | __queue_sample_after(new, last_inserted, head); | 587 | if (p == &os->samples) { |
588 | list_add_tail(&new->list, &os->samples); | ||
589 | os->max_timestamp = timestamp; | ||
590 | return; | ||
591 | } | ||
592 | sample = list_entry(p, struct sample_queue, list); | ||
593 | } | ||
594 | list_add_tail(&new->list, &sample->list); | ||
595 | } else { | ||
596 | while (sample->timestamp > timestamp) { | ||
597 | p = sample->list.prev; | ||
598 | if (p == &os->samples) { | ||
599 | list_add(&new->list, &os->samples); | ||
600 | return; | ||
601 | } | ||
602 | sample = list_entry(p, struct sample_queue, list); | ||
603 | } | ||
604 | list_add(&new->list, &sample->list); | ||
605 | } | ||
533 | } | 606 | } |
534 | 607 | ||
535 | static int queue_sample_event(event_t *event, struct sample_data *data, | 608 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) |
536 | struct perf_session *s) | 609 | |
610 | static int perf_session_queue_event(struct perf_session *s, event_t *event, | ||
611 | struct sample_data *data, u64 file_offset) | ||
537 | { | 612 | { |
613 | struct ordered_samples *os = &s->ordered_samples; | ||
614 | struct list_head *sc = &os->sample_cache; | ||
538 | u64 timestamp = data->time; | 615 | u64 timestamp = data->time; |
539 | struct sample_queue *new; | 616 | struct sample_queue *new; |
540 | 617 | ||
618 | if (!timestamp || timestamp == ~0ULL) | ||
619 | return -ETIME; | ||
541 | 620 | ||
542 | if (timestamp < s->ordered_samples.last_flush) { | 621 | if (timestamp < s->ordered_samples.last_flush) { |
543 | printf("Warning: Timestamp below last timeslice flush\n"); | 622 | printf("Warning: Timestamp below last timeslice flush\n"); |
544 | return -EINVAL; | 623 | return -EINVAL; |
545 | } | 624 | } |
546 | 625 | ||
547 | new = malloc(sizeof(*new)); | 626 | if (!list_empty(sc)) { |
548 | if (!new) | 627 | new = list_entry(sc->next, struct sample_queue, list); |
549 | return -ENOMEM; | 628 | list_del(&new->list); |
629 | } else if (os->sample_buffer) { | ||
630 | new = os->sample_buffer + os->sample_buffer_idx; | ||
631 | if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER) | ||
632 | os->sample_buffer = NULL; | ||
633 | } else { | ||
634 | os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new)); | ||
635 | if (!os->sample_buffer) | ||
636 | return -ENOMEM; | ||
637 | list_add(&os->sample_buffer->list, &os->to_free); | ||
638 | os->sample_buffer_idx = 2; | ||
639 | new = os->sample_buffer + 1; | ||
640 | } | ||
550 | 641 | ||
551 | new->timestamp = timestamp; | 642 | new->timestamp = timestamp; |
643 | new->file_offset = file_offset; | ||
644 | new->event = event; | ||
552 | 645 | ||
553 | new->event = malloc(event->header.size); | 646 | __queue_event(new, s); |
554 | if (!new->event) { | ||
555 | free(new); | ||
556 | return -ENOMEM; | ||
557 | } | ||
558 | 647 | ||
559 | memcpy(new->event, event, event->header.size); | 648 | return 0; |
649 | } | ||
560 | 650 | ||
561 | __queue_sample_event(new, s); | 651 | static void callchain__printf(struct sample_data *sample) |
562 | s->ordered_samples.last_inserted = new; | 652 | { |
653 | unsigned int i; | ||
563 | 654 | ||
564 | if (new->timestamp > s->ordered_samples.max_timestamp) | 655 | printf("... chain: nr:%Lu\n", sample->callchain->nr); |
565 | s->ordered_samples.max_timestamp = new->timestamp; | ||
566 | 656 | ||
567 | return 0; | 657 | for (i = 0; i < sample->callchain->nr; i++) |
658 | printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]); | ||
568 | } | 659 | } |
569 | 660 | ||
570 | static int perf_session__process_sample(event_t *event, struct perf_session *s, | 661 | static void perf_session__print_tstamp(struct perf_session *session, |
571 | struct perf_event_ops *ops) | 662 | event_t *event, |
663 | struct sample_data *sample) | ||
572 | { | 664 | { |
573 | struct sample_data data; | 665 | if (event->header.type != PERF_RECORD_SAMPLE && |
666 | !session->sample_id_all) { | ||
667 | fputs("-1 -1 ", stdout); | ||
668 | return; | ||
669 | } | ||
574 | 670 | ||
575 | if (!ops->ordered_samples) | 671 | if ((session->sample_type & PERF_SAMPLE_CPU)) |
576 | return ops->sample(event, s); | 672 | printf("%u ", sample->cpu); |
577 | 673 | ||
578 | bzero(&data, sizeof(struct sample_data)); | 674 | if (session->sample_type & PERF_SAMPLE_TIME) |
579 | event__parse_sample(event, s->sample_type, &data); | 675 | printf("%Lu ", sample->time); |
676 | } | ||
580 | 677 | ||
581 | queue_sample_event(event, &data, s); | 678 | static void dump_event(struct perf_session *session, event_t *event, |
679 | u64 file_offset, struct sample_data *sample) | ||
680 | { | ||
681 | if (!dump_trace) | ||
682 | return; | ||
582 | 683 | ||
583 | return 0; | 684 | printf("\n%#Lx [%#x]: event: %d\n", file_offset, event->header.size, |
685 | event->header.type); | ||
686 | |||
687 | trace_event(event); | ||
688 | |||
689 | if (sample) | ||
690 | perf_session__print_tstamp(session, event, sample); | ||
691 | |||
692 | printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size, | ||
693 | event__get_event_name(event->header.type)); | ||
584 | } | 694 | } |
585 | 695 | ||
586 | static int perf_session__process_event(struct perf_session *self, | 696 | static void dump_sample(struct perf_session *session, event_t *event, |
587 | event_t *event, | 697 | struct sample_data *sample) |
588 | struct perf_event_ops *ops, | ||
589 | u64 offset, u64 head) | ||
590 | { | 698 | { |
591 | trace_event(event); | 699 | if (!dump_trace) |
700 | return; | ||
592 | 701 | ||
593 | if (event->header.type < PERF_RECORD_HEADER_MAX) { | 702 | printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
594 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | 703 | sample->pid, sample->tid, sample->ip, sample->period); |
595 | offset + head, event->header.size, | ||
596 | event__name[event->header.type]); | ||
597 | hists__inc_nr_events(&self->hists, event->header.type); | ||
598 | } | ||
599 | 704 | ||
600 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | 705 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) |
601 | event__swap_ops[event->header.type](event); | 706 | callchain__printf(sample); |
707 | } | ||
708 | |||
709 | static int perf_session_deliver_event(struct perf_session *session, | ||
710 | event_t *event, | ||
711 | struct sample_data *sample, | ||
712 | struct perf_event_ops *ops, | ||
713 | u64 file_offset) | ||
714 | { | ||
715 | dump_event(session, event, file_offset, sample); | ||
602 | 716 | ||
603 | switch (event->header.type) { | 717 | switch (event->header.type) { |
604 | case PERF_RECORD_SAMPLE: | 718 | case PERF_RECORD_SAMPLE: |
605 | return perf_session__process_sample(event, self, ops); | 719 | dump_sample(session, event, sample); |
720 | return ops->sample(event, sample, session); | ||
606 | case PERF_RECORD_MMAP: | 721 | case PERF_RECORD_MMAP: |
607 | return ops->mmap(event, self); | 722 | return ops->mmap(event, sample, session); |
608 | case PERF_RECORD_COMM: | 723 | case PERF_RECORD_COMM: |
609 | return ops->comm(event, self); | 724 | return ops->comm(event, sample, session); |
610 | case PERF_RECORD_FORK: | 725 | case PERF_RECORD_FORK: |
611 | return ops->fork(event, self); | 726 | return ops->fork(event, sample, session); |
612 | case PERF_RECORD_EXIT: | 727 | case PERF_RECORD_EXIT: |
613 | return ops->exit(event, self); | 728 | return ops->exit(event, sample, session); |
614 | case PERF_RECORD_LOST: | 729 | case PERF_RECORD_LOST: |
615 | return ops->lost(event, self); | 730 | return ops->lost(event, sample, session); |
616 | case PERF_RECORD_READ: | 731 | case PERF_RECORD_READ: |
617 | return ops->read(event, self); | 732 | return ops->read(event, sample, session); |
618 | case PERF_RECORD_THROTTLE: | 733 | case PERF_RECORD_THROTTLE: |
619 | return ops->throttle(event, self); | 734 | return ops->throttle(event, sample, session); |
620 | case PERF_RECORD_UNTHROTTLE: | 735 | case PERF_RECORD_UNTHROTTLE: |
621 | return ops->unthrottle(event, self); | 736 | return ops->unthrottle(event, sample, session); |
737 | default: | ||
738 | ++session->hists.stats.nr_unknown_events; | ||
739 | return -1; | ||
740 | } | ||
741 | } | ||
742 | |||
743 | static int perf_session__preprocess_sample(struct perf_session *session, | ||
744 | event_t *event, struct sample_data *sample) | ||
745 | { | ||
746 | if (event->header.type != PERF_RECORD_SAMPLE || | ||
747 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) | ||
748 | return 0; | ||
749 | |||
750 | if (!ip_callchain__valid(sample->callchain, event)) { | ||
751 | pr_debug("call-chain problem with event, skipping it.\n"); | ||
752 | ++session->hists.stats.nr_invalid_chains; | ||
753 | session->hists.stats.total_invalid_chains += sample->period; | ||
754 | return -EINVAL; | ||
755 | } | ||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static int perf_session__process_user_event(struct perf_session *session, event_t *event, | ||
760 | struct perf_event_ops *ops, u64 file_offset) | ||
761 | { | ||
762 | dump_event(session, event, file_offset, NULL); | ||
763 | |||
764 | /* These events are processed right away */ | ||
765 | switch (event->header.type) { | ||
622 | case PERF_RECORD_HEADER_ATTR: | 766 | case PERF_RECORD_HEADER_ATTR: |
623 | return ops->attr(event, self); | 767 | return ops->attr(event, session); |
624 | case PERF_RECORD_HEADER_EVENT_TYPE: | 768 | case PERF_RECORD_HEADER_EVENT_TYPE: |
625 | return ops->event_type(event, self); | 769 | return ops->event_type(event, session); |
626 | case PERF_RECORD_HEADER_TRACING_DATA: | 770 | case PERF_RECORD_HEADER_TRACING_DATA: |
627 | /* setup for reading amidst mmap */ | 771 | /* setup for reading amidst mmap */ |
628 | lseek(self->fd, offset + head, SEEK_SET); | 772 | lseek(session->fd, file_offset, SEEK_SET); |
629 | return ops->tracing_data(event, self); | 773 | return ops->tracing_data(event, session); |
630 | case PERF_RECORD_HEADER_BUILD_ID: | 774 | case PERF_RECORD_HEADER_BUILD_ID: |
631 | return ops->build_id(event, self); | 775 | return ops->build_id(event, session); |
632 | case PERF_RECORD_FINISHED_ROUND: | 776 | case PERF_RECORD_FINISHED_ROUND: |
633 | return ops->finished_round(event, self, ops); | 777 | return ops->finished_round(event, session, ops); |
634 | default: | 778 | default: |
635 | ++self->hists.stats.nr_unknown_events; | 779 | return -EINVAL; |
636 | return -1; | ||
637 | } | 780 | } |
638 | } | 781 | } |
639 | 782 | ||
783 | static int perf_session__process_event(struct perf_session *session, | ||
784 | event_t *event, | ||
785 | struct perf_event_ops *ops, | ||
786 | u64 file_offset) | ||
787 | { | ||
788 | struct sample_data sample; | ||
789 | int ret; | ||
790 | |||
791 | if (session->header.needs_swap && event__swap_ops[event->header.type]) | ||
792 | event__swap_ops[event->header.type](event); | ||
793 | |||
794 | if (event->header.type >= PERF_RECORD_HEADER_MAX) | ||
795 | return -EINVAL; | ||
796 | |||
797 | hists__inc_nr_events(&session->hists, event->header.type); | ||
798 | |||
799 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) | ||
800 | return perf_session__process_user_event(session, event, ops, file_offset); | ||
801 | |||
802 | /* | ||
803 | * For all kernel events we get the sample data | ||
804 | */ | ||
805 | event__parse_sample(event, session, &sample); | ||
806 | |||
807 | /* Preprocess sample records - precheck callchains */ | ||
808 | if (perf_session__preprocess_sample(session, event, &sample)) | ||
809 | return 0; | ||
810 | |||
811 | if (ops->ordered_samples) { | ||
812 | ret = perf_session_queue_event(session, event, &sample, | ||
813 | file_offset); | ||
814 | if (ret != -ETIME) | ||
815 | return ret; | ||
816 | } | ||
817 | |||
818 | return perf_session_deliver_event(session, event, &sample, ops, | ||
819 | file_offset); | ||
820 | } | ||
821 | |||
640 | void perf_event_header__bswap(struct perf_event_header *self) | 822 | void perf_event_header__bswap(struct perf_event_header *self) |
641 | { | 823 | { |
642 | self->type = bswap_32(self->type); | 824 | self->type = bswap_32(self->type); |
@@ -656,21 +838,33 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se | |||
656 | return thread; | 838 | return thread; |
657 | } | 839 | } |
658 | 840 | ||
659 | int do_read(int fd, void *buf, size_t size) | 841 | static void perf_session__warn_about_errors(const struct perf_session *session, |
842 | const struct perf_event_ops *ops) | ||
660 | { | 843 | { |
661 | void *buf_start = buf; | 844 | if (ops->lost == event__process_lost && |
662 | 845 | session->hists.stats.total_lost != 0) { | |
663 | while (size) { | 846 | ui__warning("Processed %Lu events and LOST %Lu!\n\n" |
664 | int ret = read(fd, buf, size); | 847 | "Check IO/CPU overload!\n\n", |
665 | 848 | session->hists.stats.total_period, | |
666 | if (ret <= 0) | 849 | session->hists.stats.total_lost); |
667 | return ret; | 850 | } |
668 | 851 | ||
669 | size -= ret; | 852 | if (session->hists.stats.nr_unknown_events != 0) { |
670 | buf += ret; | 853 | ui__warning("Found %u unknown events!\n\n" |
854 | "Is this an older tool processing a perf.data " | ||
855 | "file generated by a more recent tool?\n\n" | ||
856 | "If that is not the case, consider " | ||
857 | "reporting to linux-kernel@vger.kernel.org.\n\n", | ||
858 | session->hists.stats.nr_unknown_events); | ||
671 | } | 859 | } |
672 | 860 | ||
673 | return buf - buf_start; | 861 | if (session->hists.stats.nr_invalid_chains != 0) { |
862 | ui__warning("Found invalid callchains!\n\n" | ||
863 | "%u out of %u events were discarded for this reason.\n\n" | ||
864 | "Consider reporting to linux-kernel@vger.kernel.org.\n\n", | ||
865 | session->hists.stats.nr_invalid_chains, | ||
866 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); | ||
867 | } | ||
674 | } | 868 | } |
675 | 869 | ||
676 | #define session_done() (*(volatile int *)(&session_done)) | 870 | #define session_done() (*(volatile int *)(&session_done)) |
@@ -690,7 +884,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self, | |||
690 | 884 | ||
691 | head = 0; | 885 | head = 0; |
692 | more: | 886 | more: |
693 | err = do_read(self->fd, &event, sizeof(struct perf_event_header)); | 887 | err = readn(self->fd, &event, sizeof(struct perf_event_header)); |
694 | if (err <= 0) { | 888 | if (err <= 0) { |
695 | if (err == 0) | 889 | if (err == 0) |
696 | goto done; | 890 | goto done; |
@@ -710,8 +904,7 @@ more: | |||
710 | p += sizeof(struct perf_event_header); | 904 | p += sizeof(struct perf_event_header); |
711 | 905 | ||
712 | if (size - sizeof(struct perf_event_header)) { | 906 | if (size - sizeof(struct perf_event_header)) { |
713 | err = do_read(self->fd, p, | 907 | err = readn(self->fd, p, size - sizeof(struct perf_event_header)); |
714 | size - sizeof(struct perf_event_header)); | ||
715 | if (err <= 0) { | 908 | if (err <= 0) { |
716 | if (err == 0) { | 909 | if (err == 0) { |
717 | pr_err("unexpected end of event stream\n"); | 910 | pr_err("unexpected end of event stream\n"); |
@@ -724,8 +917,7 @@ more: | |||
724 | } | 917 | } |
725 | 918 | ||
726 | if (size == 0 || | 919 | if (size == 0 || |
727 | (skip = perf_session__process_event(self, &event, ops, | 920 | (skip = perf_session__process_event(self, &event, ops, head)) < 0) { |
728 | 0, head)) < 0) { | ||
729 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | 921 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", |
730 | head, event.header.size, event.header.type); | 922 | head, event.header.size, event.header.type); |
731 | /* | 923 | /* |
@@ -740,9 +932,6 @@ more: | |||
740 | 932 | ||
741 | head += size; | 933 | head += size; |
742 | 934 | ||
743 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
744 | head, event.header.size, event.header.type); | ||
745 | |||
746 | if (skip > 0) | 935 | if (skip > 0) |
747 | head += skip; | 936 | head += skip; |
748 | 937 | ||
@@ -751,82 +940,91 @@ more: | |||
751 | done: | 940 | done: |
752 | err = 0; | 941 | err = 0; |
753 | out_err: | 942 | out_err: |
943 | perf_session__warn_about_errors(self, ops); | ||
944 | perf_session_free_sample_buffers(self); | ||
754 | return err; | 945 | return err; |
755 | } | 946 | } |
756 | 947 | ||
757 | int __perf_session__process_events(struct perf_session *self, | 948 | int __perf_session__process_events(struct perf_session *session, |
758 | u64 data_offset, u64 data_size, | 949 | u64 data_offset, u64 data_size, |
759 | u64 file_size, struct perf_event_ops *ops) | 950 | u64 file_size, struct perf_event_ops *ops) |
760 | { | 951 | { |
761 | int err, mmap_prot, mmap_flags; | 952 | u64 head, page_offset, file_offset, file_pos, progress_next; |
762 | u64 head, shift; | 953 | int err, mmap_prot, mmap_flags, map_idx = 0; |
763 | u64 offset = 0; | 954 | struct ui_progress *progress; |
764 | size_t page_size; | 955 | size_t page_size, mmap_size; |
956 | char *buf, *mmaps[8]; | ||
765 | event_t *event; | 957 | event_t *event; |
766 | uint32_t size; | 958 | uint32_t size; |
767 | char *buf; | ||
768 | struct ui_progress *progress = ui_progress__new("Processing events...", | ||
769 | self->size); | ||
770 | if (progress == NULL) | ||
771 | return -1; | ||
772 | 959 | ||
773 | perf_event_ops__fill_defaults(ops); | 960 | perf_event_ops__fill_defaults(ops); |
774 | 961 | ||
775 | page_size = sysconf(_SC_PAGESIZE); | 962 | page_size = sysconf(_SC_PAGESIZE); |
776 | 963 | ||
777 | head = data_offset; | 964 | page_offset = page_size * (data_offset / page_size); |
778 | shift = page_size * (head / page_size); | 965 | file_offset = page_offset; |
779 | offset += shift; | 966 | head = data_offset - page_offset; |
780 | head -= shift; | 967 | |
968 | if (data_offset + data_size < file_size) | ||
969 | file_size = data_offset + data_size; | ||
970 | |||
971 | progress_next = file_size / 16; | ||
972 | progress = ui_progress__new("Processing events...", file_size); | ||
973 | if (progress == NULL) | ||
974 | return -1; | ||
975 | |||
976 | mmap_size = session->mmap_window; | ||
977 | if (mmap_size > file_size) | ||
978 | mmap_size = file_size; | ||
979 | |||
980 | memset(mmaps, 0, sizeof(mmaps)); | ||
781 | 981 | ||
782 | mmap_prot = PROT_READ; | 982 | mmap_prot = PROT_READ; |
783 | mmap_flags = MAP_SHARED; | 983 | mmap_flags = MAP_SHARED; |
784 | 984 | ||
785 | if (self->header.needs_swap) { | 985 | if (session->header.needs_swap) { |
786 | mmap_prot |= PROT_WRITE; | 986 | mmap_prot |= PROT_WRITE; |
787 | mmap_flags = MAP_PRIVATE; | 987 | mmap_flags = MAP_PRIVATE; |
788 | } | 988 | } |
789 | remap: | 989 | remap: |
790 | buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, | 990 | buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, |
791 | mmap_flags, self->fd, offset); | 991 | file_offset); |
792 | if (buf == MAP_FAILED) { | 992 | if (buf == MAP_FAILED) { |
793 | pr_err("failed to mmap file\n"); | 993 | pr_err("failed to mmap file\n"); |
794 | err = -errno; | 994 | err = -errno; |
795 | goto out_err; | 995 | goto out_err; |
796 | } | 996 | } |
997 | mmaps[map_idx] = buf; | ||
998 | map_idx = (map_idx + 1) & (ARRAY_SIZE(mmaps) - 1); | ||
999 | file_pos = file_offset + head; | ||
797 | 1000 | ||
798 | more: | 1001 | more: |
799 | event = (event_t *)(buf + head); | 1002 | event = (event_t *)(buf + head); |
800 | ui_progress__update(progress, offset); | ||
801 | 1003 | ||
802 | if (self->header.needs_swap) | 1004 | if (session->header.needs_swap) |
803 | perf_event_header__bswap(&event->header); | 1005 | perf_event_header__bswap(&event->header); |
804 | size = event->header.size; | 1006 | size = event->header.size; |
805 | if (size == 0) | 1007 | if (size == 0) |
806 | size = 8; | 1008 | size = 8; |
807 | 1009 | ||
808 | if (head + event->header.size >= page_size * self->mmap_window) { | 1010 | if (head + event->header.size > mmap_size) { |
809 | int munmap_ret; | 1011 | if (mmaps[map_idx]) { |
810 | 1012 | munmap(mmaps[map_idx], mmap_size); | |
811 | shift = page_size * (head / page_size); | 1013 | mmaps[map_idx] = NULL; |
812 | 1014 | } | |
813 | munmap_ret = munmap(buf, page_size * self->mmap_window); | ||
814 | assert(munmap_ret == 0); | ||
815 | 1015 | ||
816 | offset += shift; | 1016 | page_offset = page_size * (head / page_size); |
817 | head -= shift; | 1017 | file_offset += page_offset; |
1018 | head -= page_offset; | ||
818 | goto remap; | 1019 | goto remap; |
819 | } | 1020 | } |
820 | 1021 | ||
821 | size = event->header.size; | 1022 | size = event->header.size; |
822 | 1023 | ||
823 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
824 | offset + head, event->header.size, event->header.type); | ||
825 | |||
826 | if (size == 0 || | 1024 | if (size == 0 || |
827 | perf_session__process_event(self, event, ops, offset, head) < 0) { | 1025 | perf_session__process_event(session, event, ops, file_pos) < 0) { |
828 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | 1026 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", |
829 | offset + head, event->header.size, | 1027 | file_offset + head, event->header.size, |
830 | event->header.type); | 1028 | event->header.type); |
831 | /* | 1029 | /* |
832 | * assume we lost track of the stream, check alignment, and | 1030 | * assume we lost track of the stream, check alignment, and |
@@ -839,19 +1037,24 @@ more: | |||
839 | } | 1037 | } |
840 | 1038 | ||
841 | head += size; | 1039 | head += size; |
1040 | file_pos += size; | ||
842 | 1041 | ||
843 | if (offset + head >= data_offset + data_size) | 1042 | if (file_pos >= progress_next) { |
844 | goto done; | 1043 | progress_next += file_size / 16; |
1044 | ui_progress__update(progress, file_pos); | ||
1045 | } | ||
845 | 1046 | ||
846 | if (offset + head < file_size) | 1047 | if (file_pos < file_size) |
847 | goto more; | 1048 | goto more; |
848 | done: | 1049 | |
849 | err = 0; | 1050 | err = 0; |
850 | /* do the final flush for ordered samples */ | 1051 | /* do the final flush for ordered samples */ |
851 | self->ordered_samples.next_flush = ULLONG_MAX; | 1052 | session->ordered_samples.next_flush = ULLONG_MAX; |
852 | flush_sample_queue(self, ops); | 1053 | flush_sample_queue(session, ops); |
853 | out_err: | 1054 | out_err: |
854 | ui_progress__delete(progress); | 1055 | ui_progress__delete(progress); |
1056 | perf_session__warn_about_errors(session, ops); | ||
1057 | perf_session_free_sample_buffers(session); | ||
855 | return err; | 1058 | return err; |
856 | } | 1059 | } |
857 | 1060 | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 9fa0fc2a863f..decd83f274fd 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -17,8 +17,12 @@ struct ordered_samples { | |||
17 | u64 last_flush; | 17 | u64 last_flush; |
18 | u64 next_flush; | 18 | u64 next_flush; |
19 | u64 max_timestamp; | 19 | u64 max_timestamp; |
20 | struct list_head samples_head; | 20 | struct list_head samples; |
21 | struct sample_queue *last_inserted; | 21 | struct list_head sample_cache; |
22 | struct list_head to_free; | ||
23 | struct sample_queue *sample_buffer; | ||
24 | struct sample_queue *last_sample; | ||
25 | int sample_buffer_idx; | ||
22 | }; | 26 | }; |
23 | 27 | ||
24 | struct perf_session { | 28 | struct perf_session { |
@@ -42,6 +46,8 @@ struct perf_session { | |||
42 | int fd; | 46 | int fd; |
43 | bool fd_pipe; | 47 | bool fd_pipe; |
44 | bool repipe; | 48 | bool repipe; |
49 | bool sample_id_all; | ||
50 | u16 id_hdr_size; | ||
45 | int cwdlen; | 51 | int cwdlen; |
46 | char *cwd; | 52 | char *cwd; |
47 | struct ordered_samples ordered_samples; | 53 | struct ordered_samples ordered_samples; |
@@ -50,7 +56,9 @@ struct perf_session { | |||
50 | 56 | ||
51 | struct perf_event_ops; | 57 | struct perf_event_ops; |
52 | 58 | ||
53 | typedef int (*event_op)(event_t *self, struct perf_session *session); | 59 | typedef int (*event_op)(event_t *self, struct sample_data *sample, |
60 | struct perf_session *session); | ||
61 | typedef int (*event_synth_op)(event_t *self, struct perf_session *session); | ||
54 | typedef int (*event_op2)(event_t *self, struct perf_session *session, | 62 | typedef int (*event_op2)(event_t *self, struct perf_session *session, |
55 | struct perf_event_ops *ops); | 63 | struct perf_event_ops *ops); |
56 | 64 | ||
@@ -63,16 +71,19 @@ struct perf_event_ops { | |||
63 | lost, | 71 | lost, |
64 | read, | 72 | read, |
65 | throttle, | 73 | throttle, |
66 | unthrottle, | 74 | unthrottle; |
67 | attr, | 75 | event_synth_op attr, |
68 | event_type, | 76 | event_type, |
69 | tracing_data, | 77 | tracing_data, |
70 | build_id; | 78 | build_id; |
71 | event_op2 finished_round; | 79 | event_op2 finished_round; |
72 | bool ordered_samples; | 80 | bool ordered_samples; |
81 | bool ordering_requires_timestamps; | ||
73 | }; | 82 | }; |
74 | 83 | ||
75 | struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe); | 84 | struct perf_session *perf_session__new(const char *filename, int mode, |
85 | bool force, bool repipe, | ||
86 | struct perf_event_ops *ops); | ||
76 | void perf_session__delete(struct perf_session *self); | 87 | void perf_session__delete(struct perf_session *self); |
77 | 88 | ||
78 | void perf_event_header__bswap(struct perf_event_header *self); | 89 | void perf_event_header__bswap(struct perf_event_header *self); |
@@ -98,8 +109,9 @@ void mem_bswap_64(void *src, int byte_size); | |||
98 | 109 | ||
99 | int perf_session__create_kernel_maps(struct perf_session *self); | 110 | int perf_session__create_kernel_maps(struct perf_session *self); |
100 | 111 | ||
101 | int do_read(int fd, void *buf, size_t size); | ||
102 | void perf_session__update_sample_type(struct perf_session *self); | 112 | void perf_session__update_sample_type(struct perf_session *self); |
113 | void perf_session__set_sample_id_all(struct perf_session *session, bool value); | ||
114 | void perf_session__set_sample_type(struct perf_session *session, u64 type); | ||
103 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); | 115 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); |
104 | 116 | ||
105 | static inline | 117 | static inline |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b62a553cc67d..f44fa541d56e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -170,7 +170,7 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |||
170 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); | 170 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); |
171 | } | 171 | } |
172 | 172 | ||
173 | return repsep_snprintf(bf, size, "%*Lx", width, self->ip); | 173 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); |
174 | } | 174 | } |
175 | 175 | ||
176 | /* --sort symbol */ | 176 | /* --sort symbol */ |
@@ -196,7 +196,7 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
196 | 196 | ||
197 | if (verbose) { | 197 | if (verbose) { |
198 | char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; | 198 | char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; |
199 | ret += repsep_snprintf(bf, size, "%*Lx %c ", | 199 | ret += repsep_snprintf(bf, size, "%-#*llx %c ", |
200 | BITS_PER_LONG / 4, self->ip, o); | 200 | BITS_PER_LONG / 4, self->ip, o); |
201 | } | 201 | } |
202 | 202 | ||
@@ -205,7 +205,7 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
205 | ret += repsep_snprintf(bf + ret, size - ret, "%s", | 205 | ret += repsep_snprintf(bf + ret, size - ret, "%s", |
206 | self->ms.sym->name); | 206 | self->ms.sym->name); |
207 | else | 207 | else |
208 | ret += repsep_snprintf(bf + ret, size - ret, "%*Lx", | 208 | ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx", |
209 | BITS_PER_LONG / 4, self->ip); | 209 | BITS_PER_LONG / 4, self->ip); |
210 | 210 | ||
211 | return ret; | 211 | return ret; |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 439ab947daf4..15ccfba8cdf8 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -22,6 +22,10 @@ | |||
22 | #include <limits.h> | 22 | #include <limits.h> |
23 | #include <sys/utsname.h> | 23 | #include <sys/utsname.h> |
24 | 24 | ||
25 | #ifndef KSYM_NAME_LEN | ||
26 | #define KSYM_NAME_LEN 128 | ||
27 | #endif | ||
28 | |||
25 | #ifndef NT_GNU_BUILD_ID | 29 | #ifndef NT_GNU_BUILD_ID |
26 | #define NT_GNU_BUILD_ID 3 | 30 | #define NT_GNU_BUILD_ID 3 |
27 | #endif | 31 | #endif |
@@ -41,6 +45,7 @@ struct symbol_conf symbol_conf = { | |||
41 | .exclude_other = true, | 45 | .exclude_other = true, |
42 | .use_modules = true, | 46 | .use_modules = true, |
43 | .try_vmlinux_path = true, | 47 | .try_vmlinux_path = true, |
48 | .symfs = "", | ||
44 | }; | 49 | }; |
45 | 50 | ||
46 | int dso__name_len(const struct dso *self) | 51 | int dso__name_len(const struct dso *self) |
@@ -92,7 +97,7 @@ static void symbols__fixup_end(struct rb_root *self) | |||
92 | prev = curr; | 97 | prev = curr; |
93 | curr = rb_entry(nd, struct symbol, rb_node); | 98 | curr = rb_entry(nd, struct symbol, rb_node); |
94 | 99 | ||
95 | if (prev->end == prev->start) | 100 | if (prev->end == prev->start && prev->end != curr->start) |
96 | prev->end = curr->start - 1; | 101 | prev->end = curr->start - 1; |
97 | } | 102 | } |
98 | 103 | ||
@@ -121,7 +126,7 @@ static void __map_groups__fixup_end(struct map_groups *self, enum map_type type) | |||
121 | * We still haven't the actual symbols, so guess the | 126 | * We still haven't the actual symbols, so guess the |
122 | * last map final address. | 127 | * last map final address. |
123 | */ | 128 | */ |
124 | curr->end = ~0UL; | 129 | curr->end = ~0ULL; |
125 | } | 130 | } |
126 | 131 | ||
127 | static void map_groups__fixup_end(struct map_groups *self) | 132 | static void map_groups__fixup_end(struct map_groups *self) |
@@ -425,16 +430,25 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) | |||
425 | 430 | ||
426 | int kallsyms__parse(const char *filename, void *arg, | 431 | int kallsyms__parse(const char *filename, void *arg, |
427 | int (*process_symbol)(void *arg, const char *name, | 432 | int (*process_symbol)(void *arg, const char *name, |
428 | char type, u64 start)) | 433 | char type, u64 start, u64 end)) |
429 | { | 434 | { |
430 | char *line = NULL; | 435 | char *line = NULL; |
431 | size_t n; | 436 | size_t n; |
432 | int err = 0; | 437 | int err = -1; |
438 | u64 prev_start = 0; | ||
439 | char prev_symbol_type = 0; | ||
440 | char *prev_symbol_name; | ||
433 | FILE *file = fopen(filename, "r"); | 441 | FILE *file = fopen(filename, "r"); |
434 | 442 | ||
435 | if (file == NULL) | 443 | if (file == NULL) |
436 | goto out_failure; | 444 | goto out_failure; |
437 | 445 | ||
446 | prev_symbol_name = malloc(KSYM_NAME_LEN); | ||
447 | if (prev_symbol_name == NULL) | ||
448 | goto out_close; | ||
449 | |||
450 | err = 0; | ||
451 | |||
438 | while (!feof(file)) { | 452 | while (!feof(file)) { |
439 | u64 start; | 453 | u64 start; |
440 | int line_len, len; | 454 | int line_len, len; |
@@ -454,14 +468,33 @@ int kallsyms__parse(const char *filename, void *arg, | |||
454 | continue; | 468 | continue; |
455 | 469 | ||
456 | symbol_type = toupper(line[len]); | 470 | symbol_type = toupper(line[len]); |
457 | symbol_name = line + len + 2; | 471 | len += 2; |
472 | symbol_name = line + len; | ||
473 | len = line_len - len; | ||
458 | 474 | ||
459 | err = process_symbol(arg, symbol_name, symbol_type, start); | 475 | if (len >= KSYM_NAME_LEN) { |
460 | if (err) | 476 | err = -1; |
461 | break; | 477 | break; |
478 | } | ||
479 | |||
480 | if (prev_symbol_type) { | ||
481 | u64 end = start; | ||
482 | if (end != prev_start) | ||
483 | --end; | ||
484 | err = process_symbol(arg, prev_symbol_name, | ||
485 | prev_symbol_type, prev_start, end); | ||
486 | if (err) | ||
487 | break; | ||
488 | } | ||
489 | |||
490 | memcpy(prev_symbol_name, symbol_name, len + 1); | ||
491 | prev_symbol_type = symbol_type; | ||
492 | prev_start = start; | ||
462 | } | 493 | } |
463 | 494 | ||
495 | free(prev_symbol_name); | ||
464 | free(line); | 496 | free(line); |
497 | out_close: | ||
465 | fclose(file); | 498 | fclose(file); |
466 | return err; | 499 | return err; |
467 | 500 | ||
@@ -483,7 +516,7 @@ static u8 kallsyms2elf_type(char type) | |||
483 | } | 516 | } |
484 | 517 | ||
485 | static int map__process_kallsym_symbol(void *arg, const char *name, | 518 | static int map__process_kallsym_symbol(void *arg, const char *name, |
486 | char type, u64 start) | 519 | char type, u64 start, u64 end) |
487 | { | 520 | { |
488 | struct symbol *sym; | 521 | struct symbol *sym; |
489 | struct process_kallsyms_args *a = arg; | 522 | struct process_kallsyms_args *a = arg; |
@@ -492,11 +525,8 @@ static int map__process_kallsym_symbol(void *arg, const char *name, | |||
492 | if (!symbol_type__is_a(type, a->map->type)) | 525 | if (!symbol_type__is_a(type, a->map->type)) |
493 | return 0; | 526 | return 0; |
494 | 527 | ||
495 | /* | 528 | sym = symbol__new(start, end - start + 1, |
496 | * Will fix up the end later, when we have all symbols sorted. | 529 | kallsyms2elf_type(type), name); |
497 | */ | ||
498 | sym = symbol__new(start, 0, kallsyms2elf_type(type), name); | ||
499 | |||
500 | if (sym == NULL) | 530 | if (sym == NULL) |
501 | return -ENOMEM; | 531 | return -ENOMEM; |
502 | /* | 532 | /* |
@@ -649,7 +679,6 @@ int dso__load_kallsyms(struct dso *self, const char *filename, | |||
649 | if (dso__load_all_kallsyms(self, filename, map) < 0) | 679 | if (dso__load_all_kallsyms(self, filename, map) < 0) |
650 | return -1; | 680 | return -1; |
651 | 681 | ||
652 | symbols__fixup_end(&self->symbols[map->type]); | ||
653 | if (self->kernel == DSO_TYPE_GUEST_KERNEL) | 682 | if (self->kernel == DSO_TYPE_GUEST_KERNEL) |
654 | self->origin = DSO__ORIG_GUEST_KERNEL; | 683 | self->origin = DSO__ORIG_GUEST_KERNEL; |
655 | else | 684 | else |
@@ -839,8 +868,11 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, | |||
839 | char sympltname[1024]; | 868 | char sympltname[1024]; |
840 | Elf *elf; | 869 | Elf *elf; |
841 | int nr = 0, symidx, fd, err = 0; | 870 | int nr = 0, symidx, fd, err = 0; |
871 | char name[PATH_MAX]; | ||
842 | 872 | ||
843 | fd = open(self->long_name, O_RDONLY); | 873 | snprintf(name, sizeof(name), "%s%s", |
874 | symbol_conf.symfs, self->long_name); | ||
875 | fd = open(name, O_RDONLY); | ||
844 | if (fd < 0) | 876 | if (fd < 0) |
845 | goto out; | 877 | goto out; |
846 | 878 | ||
@@ -1452,16 +1484,19 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1452 | self->origin++) { | 1484 | self->origin++) { |
1453 | switch (self->origin) { | 1485 | switch (self->origin) { |
1454 | case DSO__ORIG_BUILD_ID_CACHE: | 1486 | case DSO__ORIG_BUILD_ID_CACHE: |
1455 | if (dso__build_id_filename(self, name, size) == NULL) | 1487 | /* skip the locally configured cache if a symfs is given */ |
1488 | if (symbol_conf.symfs[0] || | ||
1489 | (dso__build_id_filename(self, name, size) == NULL)) { | ||
1456 | continue; | 1490 | continue; |
1491 | } | ||
1457 | break; | 1492 | break; |
1458 | case DSO__ORIG_FEDORA: | 1493 | case DSO__ORIG_FEDORA: |
1459 | snprintf(name, size, "/usr/lib/debug%s.debug", | 1494 | snprintf(name, size, "%s/usr/lib/debug%s.debug", |
1460 | self->long_name); | 1495 | symbol_conf.symfs, self->long_name); |
1461 | break; | 1496 | break; |
1462 | case DSO__ORIG_UBUNTU: | 1497 | case DSO__ORIG_UBUNTU: |
1463 | snprintf(name, size, "/usr/lib/debug%s", | 1498 | snprintf(name, size, "%s/usr/lib/debug%s", |
1464 | self->long_name); | 1499 | symbol_conf.symfs, self->long_name); |
1465 | break; | 1500 | break; |
1466 | case DSO__ORIG_BUILDID: { | 1501 | case DSO__ORIG_BUILDID: { |
1467 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 1502 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
@@ -1473,19 +1508,26 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1473 | sizeof(self->build_id), | 1508 | sizeof(self->build_id), |
1474 | build_id_hex); | 1509 | build_id_hex); |
1475 | snprintf(name, size, | 1510 | snprintf(name, size, |
1476 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 1511 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", |
1477 | build_id_hex, build_id_hex + 2); | 1512 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); |
1478 | } | 1513 | } |
1479 | break; | 1514 | break; |
1480 | case DSO__ORIG_DSO: | 1515 | case DSO__ORIG_DSO: |
1481 | snprintf(name, size, "%s", self->long_name); | 1516 | snprintf(name, size, "%s%s", |
1517 | symbol_conf.symfs, self->long_name); | ||
1482 | break; | 1518 | break; |
1483 | case DSO__ORIG_GUEST_KMODULE: | 1519 | case DSO__ORIG_GUEST_KMODULE: |
1484 | if (map->groups && map->groups->machine) | 1520 | if (map->groups && map->groups->machine) |
1485 | root_dir = map->groups->machine->root_dir; | 1521 | root_dir = map->groups->machine->root_dir; |
1486 | else | 1522 | else |
1487 | root_dir = ""; | 1523 | root_dir = ""; |
1488 | snprintf(name, size, "%s%s", root_dir, self->long_name); | 1524 | snprintf(name, size, "%s%s%s", symbol_conf.symfs, |
1525 | root_dir, self->long_name); | ||
1526 | break; | ||
1527 | |||
1528 | case DSO__ORIG_KMODULE: | ||
1529 | snprintf(name, size, "%s%s", symbol_conf.symfs, | ||
1530 | self->long_name); | ||
1489 | break; | 1531 | break; |
1490 | 1532 | ||
1491 | default: | 1533 | default: |
@@ -1784,17 +1826,20 @@ int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1784 | const char *vmlinux, symbol_filter_t filter) | 1826 | const char *vmlinux, symbol_filter_t filter) |
1785 | { | 1827 | { |
1786 | int err = -1, fd; | 1828 | int err = -1, fd; |
1829 | char symfs_vmlinux[PATH_MAX]; | ||
1787 | 1830 | ||
1788 | fd = open(vmlinux, O_RDONLY); | 1831 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s", |
1832 | symbol_conf.symfs, vmlinux); | ||
1833 | fd = open(symfs_vmlinux, O_RDONLY); | ||
1789 | if (fd < 0) | 1834 | if (fd < 0) |
1790 | return -1; | 1835 | return -1; |
1791 | 1836 | ||
1792 | dso__set_loaded(self, map->type); | 1837 | dso__set_loaded(self, map->type); |
1793 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0); | 1838 | err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0); |
1794 | close(fd); | 1839 | close(fd); |
1795 | 1840 | ||
1796 | if (err > 0) | 1841 | if (err > 0) |
1797 | pr_debug("Using %s for symbols\n", vmlinux); | 1842 | pr_debug("Using %s for symbols\n", symfs_vmlinux); |
1798 | 1843 | ||
1799 | return err; | 1844 | return err; |
1800 | } | 1845 | } |
@@ -1836,8 +1881,8 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | |||
1836 | const char *kallsyms_filename = NULL; | 1881 | const char *kallsyms_filename = NULL; |
1837 | char *kallsyms_allocated_filename = NULL; | 1882 | char *kallsyms_allocated_filename = NULL; |
1838 | /* | 1883 | /* |
1839 | * Step 1: if the user specified a vmlinux filename, use it and only | 1884 | * Step 1: if the user specified a kallsyms or vmlinux filename, use |
1840 | * it, reporting errors to the user if it cannot be used. | 1885 | * it and only it, reporting errors to the user if it cannot be used. |
1841 | * | 1886 | * |
1842 | * For instance, try to analyse an ARM perf.data file _without_ a | 1887 | * For instance, try to analyse an ARM perf.data file _without_ a |
1843 | * build-id, or if the user specifies the wrong path to the right | 1888 | * build-id, or if the user specifies the wrong path to the right |
@@ -1850,6 +1895,11 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | |||
1850 | * validation in dso__load_vmlinux and will bail out if they don't | 1895 | * validation in dso__load_vmlinux and will bail out if they don't |
1851 | * match. | 1896 | * match. |
1852 | */ | 1897 | */ |
1898 | if (symbol_conf.kallsyms_name != NULL) { | ||
1899 | kallsyms_filename = symbol_conf.kallsyms_name; | ||
1900 | goto do_kallsyms; | ||
1901 | } | ||
1902 | |||
1853 | if (symbol_conf.vmlinux_name != NULL) { | 1903 | if (symbol_conf.vmlinux_name != NULL) { |
1854 | err = dso__load_vmlinux(self, map, | 1904 | err = dso__load_vmlinux(self, map, |
1855 | symbol_conf.vmlinux_name, filter); | 1905 | symbol_conf.vmlinux_name, filter); |
@@ -1867,6 +1917,10 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | |||
1867 | goto out_fixup; | 1917 | goto out_fixup; |
1868 | } | 1918 | } |
1869 | 1919 | ||
1920 | /* do not try local files if a symfs was given */ | ||
1921 | if (symbol_conf.symfs[0] != 0) | ||
1922 | return -1; | ||
1923 | |||
1870 | /* | 1924 | /* |
1871 | * Say the kernel DSO was created when processing the build-id header table, | 1925 | * Say the kernel DSO was created when processing the build-id header table, |
1872 | * we have a build-id, so check if it is the same as the running kernel, | 1926 | * we have a build-id, so check if it is the same as the running kernel, |
@@ -2136,7 +2190,7 @@ struct process_args { | |||
2136 | }; | 2190 | }; |
2137 | 2191 | ||
2138 | static int symbol__in_kernel(void *arg, const char *name, | 2192 | static int symbol__in_kernel(void *arg, const char *name, |
2139 | char type __used, u64 start) | 2193 | char type __used, u64 start, u64 end __used) |
2140 | { | 2194 | { |
2141 | struct process_args *args = arg; | 2195 | struct process_args *args = arg; |
2142 | 2196 | ||
@@ -2257,9 +2311,6 @@ static int vmlinux_path__init(void) | |||
2257 | struct utsname uts; | 2311 | struct utsname uts; |
2258 | char bf[PATH_MAX]; | 2312 | char bf[PATH_MAX]; |
2259 | 2313 | ||
2260 | if (uname(&uts) < 0) | ||
2261 | return -1; | ||
2262 | |||
2263 | vmlinux_path = malloc(sizeof(char *) * 5); | 2314 | vmlinux_path = malloc(sizeof(char *) * 5); |
2264 | if (vmlinux_path == NULL) | 2315 | if (vmlinux_path == NULL) |
2265 | return -1; | 2316 | return -1; |
@@ -2272,6 +2323,14 @@ static int vmlinux_path__init(void) | |||
2272 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 2323 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
2273 | goto out_fail; | 2324 | goto out_fail; |
2274 | ++vmlinux_path__nr_entries; | 2325 | ++vmlinux_path__nr_entries; |
2326 | |||
2327 | /* only try running kernel version if no symfs was given */ | ||
2328 | if (symbol_conf.symfs[0] != 0) | ||
2329 | return 0; | ||
2330 | |||
2331 | if (uname(&uts) < 0) | ||
2332 | return -1; | ||
2333 | |||
2275 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); | 2334 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); |
2276 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 2335 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
2277 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 2336 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
@@ -2331,6 +2390,8 @@ static int setup_list(struct strlist **list, const char *list_str, | |||
2331 | 2390 | ||
2332 | int symbol__init(void) | 2391 | int symbol__init(void) |
2333 | { | 2392 | { |
2393 | const char *symfs; | ||
2394 | |||
2334 | if (symbol_conf.initialized) | 2395 | if (symbol_conf.initialized) |
2335 | return 0; | 2396 | return 0; |
2336 | 2397 | ||
@@ -2359,6 +2420,18 @@ int symbol__init(void) | |||
2359 | symbol_conf.sym_list_str, "symbol") < 0) | 2420 | symbol_conf.sym_list_str, "symbol") < 0) |
2360 | goto out_free_comm_list; | 2421 | goto out_free_comm_list; |
2361 | 2422 | ||
2423 | /* | ||
2424 | * A path to symbols of "/" is identical to "" | ||
2425 | * reset here for simplicity. | ||
2426 | */ | ||
2427 | symfs = realpath(symbol_conf.symfs, NULL); | ||
2428 | if (symfs == NULL) | ||
2429 | symfs = symbol_conf.symfs; | ||
2430 | if (strcmp(symfs, "/") == 0) | ||
2431 | symbol_conf.symfs = ""; | ||
2432 | if (symfs != symbol_conf.symfs) | ||
2433 | free((void *)symfs); | ||
2434 | |||
2362 | symbol_conf.initialized = true; | 2435 | symbol_conf.initialized = true; |
2363 | return 0; | 2436 | return 0; |
2364 | 2437 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 6c6eafdb932d..670cd1c88f54 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -72,6 +72,7 @@ struct symbol_conf { | |||
72 | show_cpu_utilization, | 72 | show_cpu_utilization, |
73 | initialized; | 73 | initialized; |
74 | const char *vmlinux_name, | 74 | const char *vmlinux_name, |
75 | *kallsyms_name, | ||
75 | *source_prefix, | 76 | *source_prefix, |
76 | *field_sep; | 77 | *field_sep; |
77 | const char *default_guest_vmlinux_name, | 78 | const char *default_guest_vmlinux_name, |
@@ -85,6 +86,7 @@ struct symbol_conf { | |||
85 | struct strlist *dso_list, | 86 | struct strlist *dso_list, |
86 | *comm_list, | 87 | *comm_list, |
87 | *sym_list; | 88 | *sym_list; |
89 | const char *symfs; | ||
88 | }; | 90 | }; |
89 | 91 | ||
90 | extern struct symbol_conf symbol_conf; | 92 | extern struct symbol_conf symbol_conf; |
@@ -215,7 +217,7 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | |||
215 | int build_id__sprintf(const u8 *self, int len, char *bf); | 217 | int build_id__sprintf(const u8 *self, int len, char *bf); |
216 | int kallsyms__parse(const char *filename, void *arg, | 218 | int kallsyms__parse(const char *filename, void *arg, |
217 | int (*process_symbol)(void *arg, const char *name, | 219 | int (*process_symbol)(void *arg, const char *name, |
218 | char type, u64 start)); | 220 | char type, u64 start, u64 end)); |
219 | 221 | ||
220 | void machine__destroy_kernel_maps(struct machine *self); | 222 | void machine__destroy_kernel_maps(struct machine *self); |
221 | int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); | 223 | int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 8c72d888e449..00f4eade2e3e 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -16,35 +16,50 @@ static int filter(const struct dirent *dir) | |||
16 | return 1; | 16 | return 1; |
17 | } | 17 | } |
18 | 18 | ||
19 | int find_all_tid(int pid, pid_t ** all_tid) | 19 | struct thread_map *thread_map__new_by_pid(pid_t pid) |
20 | { | 20 | { |
21 | struct thread_map *threads; | ||
21 | char name[256]; | 22 | char name[256]; |
22 | int items; | 23 | int items; |
23 | struct dirent **namelist = NULL; | 24 | struct dirent **namelist = NULL; |
24 | int ret = 0; | ||
25 | int i; | 25 | int i; |
26 | 26 | ||
27 | sprintf(name, "/proc/%d/task", pid); | 27 | sprintf(name, "/proc/%d/task", pid); |
28 | items = scandir(name, &namelist, filter, NULL); | 28 | items = scandir(name, &namelist, filter, NULL); |
29 | if (items <= 0) | 29 | if (items <= 0) |
30 | return -ENOENT; | 30 | return NULL; |
31 | *all_tid = malloc(sizeof(pid_t) * items); | ||
32 | if (!*all_tid) { | ||
33 | ret = -ENOMEM; | ||
34 | goto failure; | ||
35 | } | ||
36 | |||
37 | for (i = 0; i < items; i++) | ||
38 | (*all_tid)[i] = atoi(namelist[i]->d_name); | ||
39 | 31 | ||
40 | ret = items; | 32 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); |
33 | if (threads != NULL) { | ||
34 | for (i = 0; i < items; i++) | ||
35 | threads->map[i] = atoi(namelist[i]->d_name); | ||
36 | threads->nr = items; | ||
37 | } | ||
41 | 38 | ||
42 | failure: | ||
43 | for (i=0; i<items; i++) | 39 | for (i=0; i<items; i++) |
44 | free(namelist[i]); | 40 | free(namelist[i]); |
45 | free(namelist); | 41 | free(namelist); |
46 | 42 | ||
47 | return ret; | 43 | return threads; |
44 | } | ||
45 | |||
46 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
47 | { | ||
48 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
49 | |||
50 | if (threads != NULL) { | ||
51 | threads->map[0] = tid; | ||
52 | threads->nr = 1; | ||
53 | } | ||
54 | |||
55 | return threads; | ||
56 | } | ||
57 | |||
58 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
59 | { | ||
60 | if (pid != -1) | ||
61 | return thread_map__new_by_pid(pid); | ||
62 | return thread_map__new_by_tid(tid); | ||
48 | } | 63 | } |
49 | 64 | ||
50 | static struct thread *thread__new(pid_t pid) | 65 | static struct thread *thread__new(pid_t pid) |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 688500ff826f..d7574101054a 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -18,11 +18,24 @@ struct thread { | |||
18 | int comm_len; | 18 | int comm_len; |
19 | }; | 19 | }; |
20 | 20 | ||
21 | struct thread_map { | ||
22 | int nr; | ||
23 | int map[]; | ||
24 | }; | ||
25 | |||
21 | struct perf_session; | 26 | struct perf_session; |
22 | 27 | ||
23 | void thread__delete(struct thread *self); | 28 | void thread__delete(struct thread *self); |
24 | 29 | ||
25 | int find_all_tid(int pid, pid_t ** all_tid); | 30 | struct thread_map *thread_map__new_by_pid(pid_t pid); |
31 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
32 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
33 | |||
34 | static inline void thread_map__delete(struct thread_map *threads) | ||
35 | { | ||
36 | free(threads); | ||
37 | } | ||
38 | |||
26 | int thread__set_comm(struct thread *self, const char *comm); | 39 | int thread__set_comm(struct thread *self, const char *comm); |
27 | int thread__comm_len(struct thread *self); | 40 | int thread__comm_len(struct thread *self); |
28 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 41 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index b1572601286c..35729f4c40cb 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -34,11 +34,13 @@ | |||
34 | #include <ctype.h> | 34 | #include <ctype.h> |
35 | #include <errno.h> | 35 | #include <errno.h> |
36 | #include <stdbool.h> | 36 | #include <stdbool.h> |
37 | #include <linux/list.h> | ||
37 | #include <linux/kernel.h> | 38 | #include <linux/kernel.h> |
38 | 39 | ||
39 | #include "../perf.h" | 40 | #include "../perf.h" |
40 | #include "trace-event.h" | 41 | #include "trace-event.h" |
41 | #include "debugfs.h" | 42 | #include "debugfs.h" |
43 | #include "evsel.h" | ||
42 | 44 | ||
43 | #define VERSION "0.5" | 45 | #define VERSION "0.5" |
44 | 46 | ||
@@ -469,16 +471,17 @@ out: | |||
469 | } | 471 | } |
470 | 472 | ||
471 | static struct tracepoint_path * | 473 | static struct tracepoint_path * |
472 | get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) | 474 | get_tracepoints_path(struct list_head *pattrs) |
473 | { | 475 | { |
474 | struct tracepoint_path path, *ppath = &path; | 476 | struct tracepoint_path path, *ppath = &path; |
475 | int i, nr_tracepoints = 0; | 477 | struct perf_evsel *pos; |
478 | int nr_tracepoints = 0; | ||
476 | 479 | ||
477 | for (i = 0; i < nb_events; i++) { | 480 | list_for_each_entry(pos, pattrs, node) { |
478 | if (pattrs[i].type != PERF_TYPE_TRACEPOINT) | 481 | if (pos->attr.type != PERF_TYPE_TRACEPOINT) |
479 | continue; | 482 | continue; |
480 | ++nr_tracepoints; | 483 | ++nr_tracepoints; |
481 | ppath->next = tracepoint_id_to_path(pattrs[i].config); | 484 | ppath->next = tracepoint_id_to_path(pos->attr.config); |
482 | if (!ppath->next) | 485 | if (!ppath->next) |
483 | die("%s\n", "No memory to alloc tracepoints list"); | 486 | die("%s\n", "No memory to alloc tracepoints list"); |
484 | ppath = ppath->next; | 487 | ppath = ppath->next; |
@@ -487,21 +490,21 @@ get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) | |||
487 | return nr_tracepoints > 0 ? path.next : NULL; | 490 | return nr_tracepoints > 0 ? path.next : NULL; |
488 | } | 491 | } |
489 | 492 | ||
490 | bool have_tracepoints(struct perf_event_attr *pattrs, int nb_events) | 493 | bool have_tracepoints(struct list_head *pattrs) |
491 | { | 494 | { |
492 | int i; | 495 | struct perf_evsel *pos; |
493 | 496 | ||
494 | for (i = 0; i < nb_events; i++) | 497 | list_for_each_entry(pos, pattrs, node) |
495 | if (pattrs[i].type == PERF_TYPE_TRACEPOINT) | 498 | if (pos->attr.type == PERF_TYPE_TRACEPOINT) |
496 | return true; | 499 | return true; |
497 | 500 | ||
498 | return false; | 501 | return false; |
499 | } | 502 | } |
500 | 503 | ||
501 | int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) | 504 | int read_tracing_data(int fd, struct list_head *pattrs) |
502 | { | 505 | { |
503 | char buf[BUFSIZ]; | 506 | char buf[BUFSIZ]; |
504 | struct tracepoint_path *tps = get_tracepoints_path(pattrs, nb_events); | 507 | struct tracepoint_path *tps = get_tracepoints_path(pattrs); |
505 | 508 | ||
506 | /* | 509 | /* |
507 | * What? No tracepoints? No sense writing anything here, bail out. | 510 | * What? No tracepoints? No sense writing anything here, bail out. |
@@ -545,14 +548,13 @@ int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) | |||
545 | return 0; | 548 | return 0; |
546 | } | 549 | } |
547 | 550 | ||
548 | ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs, | 551 | ssize_t read_tracing_data_size(int fd, struct list_head *pattrs) |
549 | int nb_events) | ||
550 | { | 552 | { |
551 | ssize_t size; | 553 | ssize_t size; |
552 | int err = 0; | 554 | int err = 0; |
553 | 555 | ||
554 | calc_data_size = 1; | 556 | calc_data_size = 1; |
555 | err = read_tracing_data(fd, pattrs, nb_events); | 557 | err = read_tracing_data(fd, pattrs); |
556 | size = calc_data_size - 1; | 558 | size = calc_data_size - 1; |
557 | calc_data_size = 0; | 559 | calc_data_size = 0; |
558 | 560 | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index b3e86b1e4444..b5f12ca24d99 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -262,9 +262,8 @@ raw_field_value(struct event *event, const char *name, void *data); | |||
262 | void *raw_field_ptr(struct event *event, const char *name, void *data); | 262 | void *raw_field_ptr(struct event *event, const char *name, void *data); |
263 | unsigned long long eval_flag(const char *flag); | 263 | unsigned long long eval_flag(const char *flag); |
264 | 264 | ||
265 | int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events); | 265 | int read_tracing_data(int fd, struct list_head *pattrs); |
266 | ssize_t read_tracing_data_size(int fd, struct perf_event_attr *pattrs, | 266 | ssize_t read_tracing_data_size(int fd, struct list_head *pattrs); |
267 | int nb_events); | ||
268 | 267 | ||
269 | /* taken from kernel/trace/trace.h */ | 268 | /* taken from kernel/trace/trace.h */ |
270 | enum trace_flag_type { | 269 | enum trace_flag_type { |
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 056c69521a38..7b5a8926624e 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
@@ -104,10 +104,24 @@ out_destroy_form: | |||
104 | return rc; | 104 | return rc; |
105 | } | 105 | } |
106 | 106 | ||
107 | static const char yes[] = "Yes", no[] = "No"; | 107 | static const char yes[] = "Yes", no[] = "No", |
108 | warning_str[] = "Warning!", ok[] = "Ok"; | ||
108 | 109 | ||
109 | bool ui__dialog_yesno(const char *msg) | 110 | bool ui__dialog_yesno(const char *msg) |
110 | { | 111 | { |
111 | /* newtWinChoice should really be accepting const char pointers... */ | 112 | /* newtWinChoice should really be accepting const char pointers... */ |
112 | return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1; | 113 | return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1; |
113 | } | 114 | } |
115 | |||
116 | void ui__warning(const char *format, ...) | ||
117 | { | ||
118 | va_list args; | ||
119 | |||
120 | va_start(args, format); | ||
121 | if (use_browser > 0) | ||
122 | newtWinMessagev((char *)warning_str, (char *)ok, | ||
123 | (char *)format, args); | ||
124 | else | ||
125 | vfprintf(stderr, format, args); | ||
126 | va_end(args); | ||
127 | } | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 214265674ddd..5b3ea49aa63e 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -114,3 +114,20 @@ unsigned long convert_unit(unsigned long value, char *unit) | |||
114 | 114 | ||
115 | return value; | 115 | return value; |
116 | } | 116 | } |
117 | |||
118 | int readn(int fd, void *buf, size_t n) | ||
119 | { | ||
120 | void *buf_start = buf; | ||
121 | |||
122 | while (n) { | ||
123 | int ret = read(fd, buf, n); | ||
124 | |||
125 | if (ret <= 0) | ||
126 | return ret; | ||
127 | |||
128 | n -= ret; | ||
129 | buf += ret; | ||
130 | } | ||
131 | |||
132 | return buf - buf_start; | ||
133 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 7562707ddd1c..e833f26f3bfc 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -265,6 +265,7 @@ void argv_free(char **argv); | |||
265 | bool strglobmatch(const char *str, const char *pat); | 265 | bool strglobmatch(const char *str, const char *pat); |
266 | bool strlazymatch(const char *str, const char *pat); | 266 | bool strlazymatch(const char *str, const char *pat); |
267 | unsigned long convert_unit(unsigned long value, char *unit); | 267 | unsigned long convert_unit(unsigned long value, char *unit); |
268 | int readn(int fd, void *buf, size_t size); | ||
268 | 269 | ||
269 | #define _STR(x) #x | 270 | #define _STR(x) #x |
270 | #define STR(x) _STR(x) | 271 | #define STR(x) _STR(x) |
diff --git a/tools/perf/util/xyarray.c b/tools/perf/util/xyarray.c new file mode 100644 index 000000000000..22afbf6c536a --- /dev/null +++ b/tools/perf/util/xyarray.c | |||
@@ -0,0 +1,20 @@ | |||
1 | #include "xyarray.h" | ||
2 | #include "util.h" | ||
3 | |||
4 | struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size) | ||
5 | { | ||
6 | size_t row_size = ylen * entry_size; | ||
7 | struct xyarray *xy = zalloc(sizeof(*xy) + xlen * row_size); | ||
8 | |||
9 | if (xy != NULL) { | ||
10 | xy->entry_size = entry_size; | ||
11 | xy->row_size = row_size; | ||
12 | } | ||
13 | |||
14 | return xy; | ||
15 | } | ||
16 | |||
17 | void xyarray__delete(struct xyarray *xy) | ||
18 | { | ||
19 | free(xy); | ||
20 | } | ||
diff --git a/tools/perf/util/xyarray.h b/tools/perf/util/xyarray.h new file mode 100644 index 000000000000..c488a07275dd --- /dev/null +++ b/tools/perf/util/xyarray.h | |||
@@ -0,0 +1,20 @@ | |||
1 | #ifndef _PERF_XYARRAY_H_ | ||
2 | #define _PERF_XYARRAY_H_ 1 | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | |||
6 | struct xyarray { | ||
7 | size_t row_size; | ||
8 | size_t entry_size; | ||
9 | char contents[]; | ||
10 | }; | ||
11 | |||
12 | struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size); | ||
13 | void xyarray__delete(struct xyarray *xy); | ||
14 | |||
15 | static inline void *xyarray__entry(struct xyarray *xy, int x, int y) | ||
16 | { | ||
17 | return &xy->contents[x * xy->row_size + y * xy->entry_size]; | ||
18 | } | ||
19 | |||
20 | #endif /* _PERF_XYARRAY_H_ */ | ||
diff --git a/tools/slub/slabinfo.c b/tools/slub/slabinfo.c new file mode 100644 index 000000000000..516551c9f172 --- /dev/null +++ b/tools/slub/slabinfo.c | |||
@@ -0,0 +1,1364 @@ | |||
1 | /* | ||
2 | * Slabinfo: Tool to get reports about slabs | ||
3 | * | ||
4 | * (C) 2007 sgi, Christoph Lameter | ||
5 | * | ||
6 | * Compile by: | ||
7 | * | ||
8 | * gcc -o slabinfo slabinfo.c | ||
9 | */ | ||
10 | #include <stdio.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <sys/types.h> | ||
13 | #include <dirent.h> | ||
14 | #include <strings.h> | ||
15 | #include <string.h> | ||
16 | #include <unistd.h> | ||
17 | #include <stdarg.h> | ||
18 | #include <getopt.h> | ||
19 | #include <regex.h> | ||
20 | #include <errno.h> | ||
21 | |||
22 | #define MAX_SLABS 500 | ||
23 | #define MAX_ALIASES 500 | ||
24 | #define MAX_NODES 1024 | ||
25 | |||
26 | struct slabinfo { | ||
27 | char *name; | ||
28 | int alias; | ||
29 | int refs; | ||
30 | int aliases, align, cache_dma, cpu_slabs, destroy_by_rcu; | ||
31 | int hwcache_align, object_size, objs_per_slab; | ||
32 | int sanity_checks, slab_size, store_user, trace; | ||
33 | int order, poison, reclaim_account, red_zone; | ||
34 | unsigned long partial, objects, slabs, objects_partial, objects_total; | ||
35 | unsigned long alloc_fastpath, alloc_slowpath; | ||
36 | unsigned long free_fastpath, free_slowpath; | ||
37 | unsigned long free_frozen, free_add_partial, free_remove_partial; | ||
38 | unsigned long alloc_from_partial, alloc_slab, free_slab, alloc_refill; | ||
39 | unsigned long cpuslab_flush, deactivate_full, deactivate_empty; | ||
40 | unsigned long deactivate_to_head, deactivate_to_tail; | ||
41 | unsigned long deactivate_remote_frees, order_fallback; | ||
42 | int numa[MAX_NODES]; | ||
43 | int numa_partial[MAX_NODES]; | ||
44 | } slabinfo[MAX_SLABS]; | ||
45 | |||
46 | struct aliasinfo { | ||
47 | char *name; | ||
48 | char *ref; | ||
49 | struct slabinfo *slab; | ||
50 | } aliasinfo[MAX_ALIASES]; | ||
51 | |||
52 | int slabs = 0; | ||
53 | int actual_slabs = 0; | ||
54 | int aliases = 0; | ||
55 | int alias_targets = 0; | ||
56 | int highest_node = 0; | ||
57 | |||
58 | char buffer[4096]; | ||
59 | |||
60 | int show_empty = 0; | ||
61 | int show_report = 0; | ||
62 | int show_alias = 0; | ||
63 | int show_slab = 0; | ||
64 | int skip_zero = 1; | ||
65 | int show_numa = 0; | ||
66 | int show_track = 0; | ||
67 | int show_first_alias = 0; | ||
68 | int validate = 0; | ||
69 | int shrink = 0; | ||
70 | int show_inverted = 0; | ||
71 | int show_single_ref = 0; | ||
72 | int show_totals = 0; | ||
73 | int sort_size = 0; | ||
74 | int sort_active = 0; | ||
75 | int set_debug = 0; | ||
76 | int show_ops = 0; | ||
77 | int show_activity = 0; | ||
78 | |||
79 | /* Debug options */ | ||
80 | int sanity = 0; | ||
81 | int redzone = 0; | ||
82 | int poison = 0; | ||
83 | int tracking = 0; | ||
84 | int tracing = 0; | ||
85 | |||
86 | int page_size; | ||
87 | |||
88 | regex_t pattern; | ||
89 | |||
90 | static void fatal(const char *x, ...) | ||
91 | { | ||
92 | va_list ap; | ||
93 | |||
94 | va_start(ap, x); | ||
95 | vfprintf(stderr, x, ap); | ||
96 | va_end(ap); | ||
97 | exit(EXIT_FAILURE); | ||
98 | } | ||
99 | |||
100 | static void usage(void) | ||
101 | { | ||
102 | printf("slabinfo 5/7/2007. (c) 2007 sgi.\n\n" | ||
103 | "slabinfo [-ahnpvtsz] [-d debugopts] [slab-regexp]\n" | ||
104 | "-a|--aliases Show aliases\n" | ||
105 | "-A|--activity Most active slabs first\n" | ||
106 | "-d<options>|--debug=<options> Set/Clear Debug options\n" | ||
107 | "-D|--display-active Switch line format to activity\n" | ||
108 | "-e|--empty Show empty slabs\n" | ||
109 | "-f|--first-alias Show first alias\n" | ||
110 | "-h|--help Show usage information\n" | ||
111 | "-i|--inverted Inverted list\n" | ||
112 | "-l|--slabs Show slabs\n" | ||
113 | "-n|--numa Show NUMA information\n" | ||
114 | "-o|--ops Show kmem_cache_ops\n" | ||
115 | "-s|--shrink Shrink slabs\n" | ||
116 | "-r|--report Detailed report on single slabs\n" | ||
117 | "-S|--Size Sort by size\n" | ||
118 | "-t|--tracking Show alloc/free information\n" | ||
119 | "-T|--Totals Show summary information\n" | ||
120 | "-v|--validate Validate slabs\n" | ||
121 | "-z|--zero Include empty slabs\n" | ||
122 | "-1|--1ref Single reference\n" | ||
123 | "\nValid debug options (FZPUT may be combined)\n" | ||
124 | "a / A Switch on all debug options (=FZUP)\n" | ||
125 | "- Switch off all debug options\n" | ||
126 | "f / F Sanity Checks (SLAB_DEBUG_FREE)\n" | ||
127 | "z / Z Redzoning\n" | ||
128 | "p / P Poisoning\n" | ||
129 | "u / U Tracking\n" | ||
130 | "t / T Tracing\n" | ||
131 | ); | ||
132 | } | ||
133 | |||
134 | static unsigned long read_obj(const char *name) | ||
135 | { | ||
136 | FILE *f = fopen(name, "r"); | ||
137 | |||
138 | if (!f) | ||
139 | buffer[0] = 0; | ||
140 | else { | ||
141 | if (!fgets(buffer, sizeof(buffer), f)) | ||
142 | buffer[0] = 0; | ||
143 | fclose(f); | ||
144 | if (buffer[strlen(buffer)] == '\n') | ||
145 | buffer[strlen(buffer)] = 0; | ||
146 | } | ||
147 | return strlen(buffer); | ||
148 | } | ||
149 | |||
150 | |||
151 | /* | ||
152 | * Get the contents of an attribute | ||
153 | */ | ||
154 | static unsigned long get_obj(const char *name) | ||
155 | { | ||
156 | if (!read_obj(name)) | ||
157 | return 0; | ||
158 | |||
159 | return atol(buffer); | ||
160 | } | ||
161 | |||
162 | static unsigned long get_obj_and_str(const char *name, char **x) | ||
163 | { | ||
164 | unsigned long result = 0; | ||
165 | char *p; | ||
166 | |||
167 | *x = NULL; | ||
168 | |||
169 | if (!read_obj(name)) { | ||
170 | x = NULL; | ||
171 | return 0; | ||
172 | } | ||
173 | result = strtoul(buffer, &p, 10); | ||
174 | while (*p == ' ') | ||
175 | p++; | ||
176 | if (*p) | ||
177 | *x = strdup(p); | ||
178 | return result; | ||
179 | } | ||
180 | |||
181 | static void set_obj(struct slabinfo *s, const char *name, int n) | ||
182 | { | ||
183 | char x[100]; | ||
184 | FILE *f; | ||
185 | |||
186 | snprintf(x, 100, "%s/%s", s->name, name); | ||
187 | f = fopen(x, "w"); | ||
188 | if (!f) | ||
189 | fatal("Cannot write to %s\n", x); | ||
190 | |||
191 | fprintf(f, "%d\n", n); | ||
192 | fclose(f); | ||
193 | } | ||
194 | |||
195 | static unsigned long read_slab_obj(struct slabinfo *s, const char *name) | ||
196 | { | ||
197 | char x[100]; | ||
198 | FILE *f; | ||
199 | size_t l; | ||
200 | |||
201 | snprintf(x, 100, "%s/%s", s->name, name); | ||
202 | f = fopen(x, "r"); | ||
203 | if (!f) { | ||
204 | buffer[0] = 0; | ||
205 | l = 0; | ||
206 | } else { | ||
207 | l = fread(buffer, 1, sizeof(buffer), f); | ||
208 | buffer[l] = 0; | ||
209 | fclose(f); | ||
210 | } | ||
211 | return l; | ||
212 | } | ||
213 | |||
214 | |||
215 | /* | ||
216 | * Put a size string together | ||
217 | */ | ||
218 | static int store_size(char *buffer, unsigned long value) | ||
219 | { | ||
220 | unsigned long divisor = 1; | ||
221 | char trailer = 0; | ||
222 | int n; | ||
223 | |||
224 | if (value > 1000000000UL) { | ||
225 | divisor = 100000000UL; | ||
226 | trailer = 'G'; | ||
227 | } else if (value > 1000000UL) { | ||
228 | divisor = 100000UL; | ||
229 | trailer = 'M'; | ||
230 | } else if (value > 1000UL) { | ||
231 | divisor = 100; | ||
232 | trailer = 'K'; | ||
233 | } | ||
234 | |||
235 | value /= divisor; | ||
236 | n = sprintf(buffer, "%ld",value); | ||
237 | if (trailer) { | ||
238 | buffer[n] = trailer; | ||
239 | n++; | ||
240 | buffer[n] = 0; | ||
241 | } | ||
242 | if (divisor != 1) { | ||
243 | memmove(buffer + n - 2, buffer + n - 3, 4); | ||
244 | buffer[n-2] = '.'; | ||
245 | n++; | ||
246 | } | ||
247 | return n; | ||
248 | } | ||
249 | |||
250 | static void decode_numa_list(int *numa, char *t) | ||
251 | { | ||
252 | int node; | ||
253 | int nr; | ||
254 | |||
255 | memset(numa, 0, MAX_NODES * sizeof(int)); | ||
256 | |||
257 | if (!t) | ||
258 | return; | ||
259 | |||
260 | while (*t == 'N') { | ||
261 | t++; | ||
262 | node = strtoul(t, &t, 10); | ||
263 | if (*t == '=') { | ||
264 | t++; | ||
265 | nr = strtoul(t, &t, 10); | ||
266 | numa[node] = nr; | ||
267 | if (node > highest_node) | ||
268 | highest_node = node; | ||
269 | } | ||
270 | while (*t == ' ') | ||
271 | t++; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | static void slab_validate(struct slabinfo *s) | ||
276 | { | ||
277 | if (strcmp(s->name, "*") == 0) | ||
278 | return; | ||
279 | |||
280 | set_obj(s, "validate", 1); | ||
281 | } | ||
282 | |||
283 | static void slab_shrink(struct slabinfo *s) | ||
284 | { | ||
285 | if (strcmp(s->name, "*") == 0) | ||
286 | return; | ||
287 | |||
288 | set_obj(s, "shrink", 1); | ||
289 | } | ||
290 | |||
291 | int line = 0; | ||
292 | |||
293 | static void first_line(void) | ||
294 | { | ||
295 | if (show_activity) | ||
296 | printf("Name Objects Alloc Free %%Fast Fallb O\n"); | ||
297 | else | ||
298 | printf("Name Objects Objsize Space " | ||
299 | "Slabs/Part/Cpu O/S O %%Fr %%Ef Flg\n"); | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Find the shortest alias of a slab | ||
304 | */ | ||
305 | static struct aliasinfo *find_one_alias(struct slabinfo *find) | ||
306 | { | ||
307 | struct aliasinfo *a; | ||
308 | struct aliasinfo *best = NULL; | ||
309 | |||
310 | for(a = aliasinfo;a < aliasinfo + aliases; a++) { | ||
311 | if (a->slab == find && | ||
312 | (!best || strlen(best->name) < strlen(a->name))) { | ||
313 | best = a; | ||
314 | if (strncmp(a->name,"kmall", 5) == 0) | ||
315 | return best; | ||
316 | } | ||
317 | } | ||
318 | return best; | ||
319 | } | ||
320 | |||
321 | static unsigned long slab_size(struct slabinfo *s) | ||
322 | { | ||
323 | return s->slabs * (page_size << s->order); | ||
324 | } | ||
325 | |||
326 | static unsigned long slab_activity(struct slabinfo *s) | ||
327 | { | ||
328 | return s->alloc_fastpath + s->free_fastpath + | ||
329 | s->alloc_slowpath + s->free_slowpath; | ||
330 | } | ||
331 | |||
332 | static void slab_numa(struct slabinfo *s, int mode) | ||
333 | { | ||
334 | int node; | ||
335 | |||
336 | if (strcmp(s->name, "*") == 0) | ||
337 | return; | ||
338 | |||
339 | if (!highest_node) { | ||
340 | printf("\n%s: No NUMA information available.\n", s->name); | ||
341 | return; | ||
342 | } | ||
343 | |||
344 | if (skip_zero && !s->slabs) | ||
345 | return; | ||
346 | |||
347 | if (!line) { | ||
348 | printf("\n%-21s:", mode ? "NUMA nodes" : "Slab"); | ||
349 | for(node = 0; node <= highest_node; node++) | ||
350 | printf(" %4d", node); | ||
351 | printf("\n----------------------"); | ||
352 | for(node = 0; node <= highest_node; node++) | ||
353 | printf("-----"); | ||
354 | printf("\n"); | ||
355 | } | ||
356 | printf("%-21s ", mode ? "All slabs" : s->name); | ||
357 | for(node = 0; node <= highest_node; node++) { | ||
358 | char b[20]; | ||
359 | |||
360 | store_size(b, s->numa[node]); | ||
361 | printf(" %4s", b); | ||
362 | } | ||
363 | printf("\n"); | ||
364 | if (mode) { | ||
365 | printf("%-21s ", "Partial slabs"); | ||
366 | for(node = 0; node <= highest_node; node++) { | ||
367 | char b[20]; | ||
368 | |||
369 | store_size(b, s->numa_partial[node]); | ||
370 | printf(" %4s", b); | ||
371 | } | ||
372 | printf("\n"); | ||
373 | } | ||
374 | line++; | ||
375 | } | ||
376 | |||
377 | static void show_tracking(struct slabinfo *s) | ||
378 | { | ||
379 | printf("\n%s: Kernel object allocation\n", s->name); | ||
380 | printf("-----------------------------------------------------------------------\n"); | ||
381 | if (read_slab_obj(s, "alloc_calls")) | ||
382 | printf(buffer); | ||
383 | else | ||
384 | printf("No Data\n"); | ||
385 | |||
386 | printf("\n%s: Kernel object freeing\n", s->name); | ||
387 | printf("------------------------------------------------------------------------\n"); | ||
388 | if (read_slab_obj(s, "free_calls")) | ||
389 | printf(buffer); | ||
390 | else | ||
391 | printf("No Data\n"); | ||
392 | |||
393 | } | ||
394 | |||
395 | static void ops(struct slabinfo *s) | ||
396 | { | ||
397 | if (strcmp(s->name, "*") == 0) | ||
398 | return; | ||
399 | |||
400 | if (read_slab_obj(s, "ops")) { | ||
401 | printf("\n%s: kmem_cache operations\n", s->name); | ||
402 | printf("--------------------------------------------\n"); | ||
403 | printf(buffer); | ||
404 | } else | ||
405 | printf("\n%s has no kmem_cache operations\n", s->name); | ||
406 | } | ||
407 | |||
408 | static const char *onoff(int x) | ||
409 | { | ||
410 | if (x) | ||
411 | return "On "; | ||
412 | return "Off"; | ||
413 | } | ||
414 | |||
415 | static void slab_stats(struct slabinfo *s) | ||
416 | { | ||
417 | unsigned long total_alloc; | ||
418 | unsigned long total_free; | ||
419 | unsigned long total; | ||
420 | |||
421 | if (!s->alloc_slab) | ||
422 | return; | ||
423 | |||
424 | total_alloc = s->alloc_fastpath + s->alloc_slowpath; | ||
425 | total_free = s->free_fastpath + s->free_slowpath; | ||
426 | |||
427 | if (!total_alloc) | ||
428 | return; | ||
429 | |||
430 | printf("\n"); | ||
431 | printf("Slab Perf Counter Alloc Free %%Al %%Fr\n"); | ||
432 | printf("--------------------------------------------------\n"); | ||
433 | printf("Fastpath %8lu %8lu %3lu %3lu\n", | ||
434 | s->alloc_fastpath, s->free_fastpath, | ||
435 | s->alloc_fastpath * 100 / total_alloc, | ||
436 | s->free_fastpath * 100 / total_free); | ||
437 | printf("Slowpath %8lu %8lu %3lu %3lu\n", | ||
438 | total_alloc - s->alloc_fastpath, s->free_slowpath, | ||
439 | (total_alloc - s->alloc_fastpath) * 100 / total_alloc, | ||
440 | s->free_slowpath * 100 / total_free); | ||
441 | printf("Page Alloc %8lu %8lu %3lu %3lu\n", | ||
442 | s->alloc_slab, s->free_slab, | ||
443 | s->alloc_slab * 100 / total_alloc, | ||
444 | s->free_slab * 100 / total_free); | ||
445 | printf("Add partial %8lu %8lu %3lu %3lu\n", | ||
446 | s->deactivate_to_head + s->deactivate_to_tail, | ||
447 | s->free_add_partial, | ||
448 | (s->deactivate_to_head + s->deactivate_to_tail) * 100 / total_alloc, | ||
449 | s->free_add_partial * 100 / total_free); | ||
450 | printf("Remove partial %8lu %8lu %3lu %3lu\n", | ||
451 | s->alloc_from_partial, s->free_remove_partial, | ||
452 | s->alloc_from_partial * 100 / total_alloc, | ||
453 | s->free_remove_partial * 100 / total_free); | ||
454 | |||
455 | printf("RemoteObj/SlabFrozen %8lu %8lu %3lu %3lu\n", | ||
456 | s->deactivate_remote_frees, s->free_frozen, | ||
457 | s->deactivate_remote_frees * 100 / total_alloc, | ||
458 | s->free_frozen * 100 / total_free); | ||
459 | |||
460 | printf("Total %8lu %8lu\n\n", total_alloc, total_free); | ||
461 | |||
462 | if (s->cpuslab_flush) | ||
463 | printf("Flushes %8lu\n", s->cpuslab_flush); | ||
464 | |||
465 | if (s->alloc_refill) | ||
466 | printf("Refill %8lu\n", s->alloc_refill); | ||
467 | |||
468 | total = s->deactivate_full + s->deactivate_empty + | ||
469 | s->deactivate_to_head + s->deactivate_to_tail; | ||
470 | |||
471 | if (total) | ||
472 | printf("Deactivate Full=%lu(%lu%%) Empty=%lu(%lu%%) " | ||
473 | "ToHead=%lu(%lu%%) ToTail=%lu(%lu%%)\n", | ||
474 | s->deactivate_full, (s->deactivate_full * 100) / total, | ||
475 | s->deactivate_empty, (s->deactivate_empty * 100) / total, | ||
476 | s->deactivate_to_head, (s->deactivate_to_head * 100) / total, | ||
477 | s->deactivate_to_tail, (s->deactivate_to_tail * 100) / total); | ||
478 | } | ||
479 | |||
480 | static void report(struct slabinfo *s) | ||
481 | { | ||
482 | if (strcmp(s->name, "*") == 0) | ||
483 | return; | ||
484 | |||
485 | printf("\nSlabcache: %-20s Aliases: %2d Order : %2d Objects: %lu\n", | ||
486 | s->name, s->aliases, s->order, s->objects); | ||
487 | if (s->hwcache_align) | ||
488 | printf("** Hardware cacheline aligned\n"); | ||
489 | if (s->cache_dma) | ||
490 | printf("** Memory is allocated in a special DMA zone\n"); | ||
491 | if (s->destroy_by_rcu) | ||
492 | printf("** Slabs are destroyed via RCU\n"); | ||
493 | if (s->reclaim_account) | ||
494 | printf("** Reclaim accounting active\n"); | ||
495 | |||
496 | printf("\nSizes (bytes) Slabs Debug Memory\n"); | ||
497 | printf("------------------------------------------------------------------------\n"); | ||
498 | printf("Object : %7d Total : %7ld Sanity Checks : %s Total: %7ld\n", | ||
499 | s->object_size, s->slabs, onoff(s->sanity_checks), | ||
500 | s->slabs * (page_size << s->order)); | ||
501 | printf("SlabObj: %7d Full : %7ld Redzoning : %s Used : %7ld\n", | ||
502 | s->slab_size, s->slabs - s->partial - s->cpu_slabs, | ||
503 | onoff(s->red_zone), s->objects * s->object_size); | ||
504 | printf("SlabSiz: %7d Partial: %7ld Poisoning : %s Loss : %7ld\n", | ||
505 | page_size << s->order, s->partial, onoff(s->poison), | ||
506 | s->slabs * (page_size << s->order) - s->objects * s->object_size); | ||
507 | printf("Loss : %7d CpuSlab: %7d Tracking : %s Lalig: %7ld\n", | ||
508 | s->slab_size - s->object_size, s->cpu_slabs, onoff(s->store_user), | ||
509 | (s->slab_size - s->object_size) * s->objects); | ||
510 | printf("Align : %7d Objects: %7d Tracing : %s Lpadd: %7ld\n", | ||
511 | s->align, s->objs_per_slab, onoff(s->trace), | ||
512 | ((page_size << s->order) - s->objs_per_slab * s->slab_size) * | ||
513 | s->slabs); | ||
514 | |||
515 | ops(s); | ||
516 | show_tracking(s); | ||
517 | slab_numa(s, 1); | ||
518 | slab_stats(s); | ||
519 | } | ||
520 | |||
521 | static void slabcache(struct slabinfo *s) | ||
522 | { | ||
523 | char size_str[20]; | ||
524 | char dist_str[40]; | ||
525 | char flags[20]; | ||
526 | char *p = flags; | ||
527 | |||
528 | if (strcmp(s->name, "*") == 0) | ||
529 | return; | ||
530 | |||
531 | if (actual_slabs == 1) { | ||
532 | report(s); | ||
533 | return; | ||
534 | } | ||
535 | |||
536 | if (skip_zero && !show_empty && !s->slabs) | ||
537 | return; | ||
538 | |||
539 | if (show_empty && s->slabs) | ||
540 | return; | ||
541 | |||
542 | store_size(size_str, slab_size(s)); | ||
543 | snprintf(dist_str, 40, "%lu/%lu/%d", s->slabs - s->cpu_slabs, | ||
544 | s->partial, s->cpu_slabs); | ||
545 | |||
546 | if (!line++) | ||
547 | first_line(); | ||
548 | |||
549 | if (s->aliases) | ||
550 | *p++ = '*'; | ||
551 | if (s->cache_dma) | ||
552 | *p++ = 'd'; | ||
553 | if (s->hwcache_align) | ||
554 | *p++ = 'A'; | ||
555 | if (s->poison) | ||
556 | *p++ = 'P'; | ||
557 | if (s->reclaim_account) | ||
558 | *p++ = 'a'; | ||
559 | if (s->red_zone) | ||
560 | *p++ = 'Z'; | ||
561 | if (s->sanity_checks) | ||
562 | *p++ = 'F'; | ||
563 | if (s->store_user) | ||
564 | *p++ = 'U'; | ||
565 | if (s->trace) | ||
566 | *p++ = 'T'; | ||
567 | |||
568 | *p = 0; | ||
569 | if (show_activity) { | ||
570 | unsigned long total_alloc; | ||
571 | unsigned long total_free; | ||
572 | |||
573 | total_alloc = s->alloc_fastpath + s->alloc_slowpath; | ||
574 | total_free = s->free_fastpath + s->free_slowpath; | ||
575 | |||
576 | printf("%-21s %8ld %10ld %10ld %3ld %3ld %5ld %1d\n", | ||
577 | s->name, s->objects, | ||
578 | total_alloc, total_free, | ||
579 | total_alloc ? (s->alloc_fastpath * 100 / total_alloc) : 0, | ||
580 | total_free ? (s->free_fastpath * 100 / total_free) : 0, | ||
581 | s->order_fallback, s->order); | ||
582 | } | ||
583 | else | ||
584 | printf("%-21s %8ld %7d %8s %14s %4d %1d %3ld %3ld %s\n", | ||
585 | s->name, s->objects, s->object_size, size_str, dist_str, | ||
586 | s->objs_per_slab, s->order, | ||
587 | s->slabs ? (s->partial * 100) / s->slabs : 100, | ||
588 | s->slabs ? (s->objects * s->object_size * 100) / | ||
589 | (s->slabs * (page_size << s->order)) : 100, | ||
590 | flags); | ||
591 | } | ||
592 | |||
593 | /* | ||
594 | * Analyze debug options. Return false if something is amiss. | ||
595 | */ | ||
596 | static int debug_opt_scan(char *opt) | ||
597 | { | ||
598 | if (!opt || !opt[0] || strcmp(opt, "-") == 0) | ||
599 | return 1; | ||
600 | |||
601 | if (strcasecmp(opt, "a") == 0) { | ||
602 | sanity = 1; | ||
603 | poison = 1; | ||
604 | redzone = 1; | ||
605 | tracking = 1; | ||
606 | return 1; | ||
607 | } | ||
608 | |||
609 | for ( ; *opt; opt++) | ||
610 | switch (*opt) { | ||
611 | case 'F' : case 'f': | ||
612 | if (sanity) | ||
613 | return 0; | ||
614 | sanity = 1; | ||
615 | break; | ||
616 | case 'P' : case 'p': | ||
617 | if (poison) | ||
618 | return 0; | ||
619 | poison = 1; | ||
620 | break; | ||
621 | |||
622 | case 'Z' : case 'z': | ||
623 | if (redzone) | ||
624 | return 0; | ||
625 | redzone = 1; | ||
626 | break; | ||
627 | |||
628 | case 'U' : case 'u': | ||
629 | if (tracking) | ||
630 | return 0; | ||
631 | tracking = 1; | ||
632 | break; | ||
633 | |||
634 | case 'T' : case 't': | ||
635 | if (tracing) | ||
636 | return 0; | ||
637 | tracing = 1; | ||
638 | break; | ||
639 | default: | ||
640 | return 0; | ||
641 | } | ||
642 | return 1; | ||
643 | } | ||
644 | |||
645 | static int slab_empty(struct slabinfo *s) | ||
646 | { | ||
647 | if (s->objects > 0) | ||
648 | return 0; | ||
649 | |||
650 | /* | ||
651 | * We may still have slabs even if there are no objects. Shrinking will | ||
652 | * remove them. | ||
653 | */ | ||
654 | if (s->slabs != 0) | ||
655 | set_obj(s, "shrink", 1); | ||
656 | |||
657 | return 1; | ||
658 | } | ||
659 | |||
660 | static void slab_debug(struct slabinfo *s) | ||
661 | { | ||
662 | if (strcmp(s->name, "*") == 0) | ||
663 | return; | ||
664 | |||
665 | if (sanity && !s->sanity_checks) { | ||
666 | set_obj(s, "sanity", 1); | ||
667 | } | ||
668 | if (!sanity && s->sanity_checks) { | ||
669 | if (slab_empty(s)) | ||
670 | set_obj(s, "sanity", 0); | ||
671 | else | ||
672 | fprintf(stderr, "%s not empty cannot disable sanity checks\n", s->name); | ||
673 | } | ||
674 | if (redzone && !s->red_zone) { | ||
675 | if (slab_empty(s)) | ||
676 | set_obj(s, "red_zone", 1); | ||
677 | else | ||
678 | fprintf(stderr, "%s not empty cannot enable redzoning\n", s->name); | ||
679 | } | ||
680 | if (!redzone && s->red_zone) { | ||
681 | if (slab_empty(s)) | ||
682 | set_obj(s, "red_zone", 0); | ||
683 | else | ||
684 | fprintf(stderr, "%s not empty cannot disable redzoning\n", s->name); | ||
685 | } | ||
686 | if (poison && !s->poison) { | ||
687 | if (slab_empty(s)) | ||
688 | set_obj(s, "poison", 1); | ||
689 | else | ||
690 | fprintf(stderr, "%s not empty cannot enable poisoning\n", s->name); | ||
691 | } | ||
692 | if (!poison && s->poison) { | ||
693 | if (slab_empty(s)) | ||
694 | set_obj(s, "poison", 0); | ||
695 | else | ||
696 | fprintf(stderr, "%s not empty cannot disable poisoning\n", s->name); | ||
697 | } | ||
698 | if (tracking && !s->store_user) { | ||
699 | if (slab_empty(s)) | ||
700 | set_obj(s, "store_user", 1); | ||
701 | else | ||
702 | fprintf(stderr, "%s not empty cannot enable tracking\n", s->name); | ||
703 | } | ||
704 | if (!tracking && s->store_user) { | ||
705 | if (slab_empty(s)) | ||
706 | set_obj(s, "store_user", 0); | ||
707 | else | ||
708 | fprintf(stderr, "%s not empty cannot disable tracking\n", s->name); | ||
709 | } | ||
710 | if (tracing && !s->trace) { | ||
711 | if (slabs == 1) | ||
712 | set_obj(s, "trace", 1); | ||
713 | else | ||
714 | fprintf(stderr, "%s can only enable trace for one slab at a time\n", s->name); | ||
715 | } | ||
716 | if (!tracing && s->trace) | ||
717 | set_obj(s, "trace", 1); | ||
718 | } | ||
719 | |||
720 | static void totals(void) | ||
721 | { | ||
722 | struct slabinfo *s; | ||
723 | |||
724 | int used_slabs = 0; | ||
725 | char b1[20], b2[20], b3[20], b4[20]; | ||
726 | unsigned long long max = 1ULL << 63; | ||
727 | |||
728 | /* Object size */ | ||
729 | unsigned long long min_objsize = max, max_objsize = 0, avg_objsize; | ||
730 | |||
731 | /* Number of partial slabs in a slabcache */ | ||
732 | unsigned long long min_partial = max, max_partial = 0, | ||
733 | avg_partial, total_partial = 0; | ||
734 | |||
735 | /* Number of slabs in a slab cache */ | ||
736 | unsigned long long min_slabs = max, max_slabs = 0, | ||
737 | avg_slabs, total_slabs = 0; | ||
738 | |||
739 | /* Size of the whole slab */ | ||
740 | unsigned long long min_size = max, max_size = 0, | ||
741 | avg_size, total_size = 0; | ||
742 | |||
743 | /* Bytes used for object storage in a slab */ | ||
744 | unsigned long long min_used = max, max_used = 0, | ||
745 | avg_used, total_used = 0; | ||
746 | |||
747 | /* Waste: Bytes used for alignment and padding */ | ||
748 | unsigned long long min_waste = max, max_waste = 0, | ||
749 | avg_waste, total_waste = 0; | ||
750 | /* Number of objects in a slab */ | ||
751 | unsigned long long min_objects = max, max_objects = 0, | ||
752 | avg_objects, total_objects = 0; | ||
753 | /* Waste per object */ | ||
754 | unsigned long long min_objwaste = max, | ||
755 | max_objwaste = 0, avg_objwaste, | ||
756 | total_objwaste = 0; | ||
757 | |||
758 | /* Memory per object */ | ||
759 | unsigned long long min_memobj = max, | ||
760 | max_memobj = 0, avg_memobj, | ||
761 | total_objsize = 0; | ||
762 | |||
763 | /* Percentage of partial slabs per slab */ | ||
764 | unsigned long min_ppart = 100, max_ppart = 0, | ||
765 | avg_ppart, total_ppart = 0; | ||
766 | |||
767 | /* Number of objects in partial slabs */ | ||
768 | unsigned long min_partobj = max, max_partobj = 0, | ||
769 | avg_partobj, total_partobj = 0; | ||
770 | |||
771 | /* Percentage of partial objects of all objects in a slab */ | ||
772 | unsigned long min_ppartobj = 100, max_ppartobj = 0, | ||
773 | avg_ppartobj, total_ppartobj = 0; | ||
774 | |||
775 | |||
776 | for (s = slabinfo; s < slabinfo + slabs; s++) { | ||
777 | unsigned long long size; | ||
778 | unsigned long used; | ||
779 | unsigned long long wasted; | ||
780 | unsigned long long objwaste; | ||
781 | unsigned long percentage_partial_slabs; | ||
782 | unsigned long percentage_partial_objs; | ||
783 | |||
784 | if (!s->slabs || !s->objects) | ||
785 | continue; | ||
786 | |||
787 | used_slabs++; | ||
788 | |||
789 | size = slab_size(s); | ||
790 | used = s->objects * s->object_size; | ||
791 | wasted = size - used; | ||
792 | objwaste = s->slab_size - s->object_size; | ||
793 | |||
794 | percentage_partial_slabs = s->partial * 100 / s->slabs; | ||
795 | if (percentage_partial_slabs > 100) | ||
796 | percentage_partial_slabs = 100; | ||
797 | |||
798 | percentage_partial_objs = s->objects_partial * 100 | ||
799 | / s->objects; | ||
800 | |||
801 | if (percentage_partial_objs > 100) | ||
802 | percentage_partial_objs = 100; | ||
803 | |||
804 | if (s->object_size < min_objsize) | ||
805 | min_objsize = s->object_size; | ||
806 | if (s->partial < min_partial) | ||
807 | min_partial = s->partial; | ||
808 | if (s->slabs < min_slabs) | ||
809 | min_slabs = s->slabs; | ||
810 | if (size < min_size) | ||
811 | min_size = size; | ||
812 | if (wasted < min_waste) | ||
813 | min_waste = wasted; | ||
814 | if (objwaste < min_objwaste) | ||
815 | min_objwaste = objwaste; | ||
816 | if (s->objects < min_objects) | ||
817 | min_objects = s->objects; | ||
818 | if (used < min_used) | ||
819 | min_used = used; | ||
820 | if (s->objects_partial < min_partobj) | ||
821 | min_partobj = s->objects_partial; | ||
822 | if (percentage_partial_slabs < min_ppart) | ||
823 | min_ppart = percentage_partial_slabs; | ||
824 | if (percentage_partial_objs < min_ppartobj) | ||
825 | min_ppartobj = percentage_partial_objs; | ||
826 | if (s->slab_size < min_memobj) | ||
827 | min_memobj = s->slab_size; | ||
828 | |||
829 | if (s->object_size > max_objsize) | ||
830 | max_objsize = s->object_size; | ||
831 | if (s->partial > max_partial) | ||
832 | max_partial = s->partial; | ||
833 | if (s->slabs > max_slabs) | ||
834 | max_slabs = s->slabs; | ||
835 | if (size > max_size) | ||
836 | max_size = size; | ||
837 | if (wasted > max_waste) | ||
838 | max_waste = wasted; | ||
839 | if (objwaste > max_objwaste) | ||
840 | max_objwaste = objwaste; | ||
841 | if (s->objects > max_objects) | ||
842 | max_objects = s->objects; | ||
843 | if (used > max_used) | ||
844 | max_used = used; | ||
845 | if (s->objects_partial > max_partobj) | ||
846 | max_partobj = s->objects_partial; | ||
847 | if (percentage_partial_slabs > max_ppart) | ||
848 | max_ppart = percentage_partial_slabs; | ||
849 | if (percentage_partial_objs > max_ppartobj) | ||
850 | max_ppartobj = percentage_partial_objs; | ||
851 | if (s->slab_size > max_memobj) | ||
852 | max_memobj = s->slab_size; | ||
853 | |||
854 | total_partial += s->partial; | ||
855 | total_slabs += s->slabs; | ||
856 | total_size += size; | ||
857 | total_waste += wasted; | ||
858 | |||
859 | total_objects += s->objects; | ||
860 | total_used += used; | ||
861 | total_partobj += s->objects_partial; | ||
862 | total_ppart += percentage_partial_slabs; | ||
863 | total_ppartobj += percentage_partial_objs; | ||
864 | |||
865 | total_objwaste += s->objects * objwaste; | ||
866 | total_objsize += s->objects * s->slab_size; | ||
867 | } | ||
868 | |||
869 | if (!total_objects) { | ||
870 | printf("No objects\n"); | ||
871 | return; | ||
872 | } | ||
873 | if (!used_slabs) { | ||
874 | printf("No slabs\n"); | ||
875 | return; | ||
876 | } | ||
877 | |||
878 | /* Per slab averages */ | ||
879 | avg_partial = total_partial / used_slabs; | ||
880 | avg_slabs = total_slabs / used_slabs; | ||
881 | avg_size = total_size / used_slabs; | ||
882 | avg_waste = total_waste / used_slabs; | ||
883 | |||
884 | avg_objects = total_objects / used_slabs; | ||
885 | avg_used = total_used / used_slabs; | ||
886 | avg_partobj = total_partobj / used_slabs; | ||
887 | avg_ppart = total_ppart / used_slabs; | ||
888 | avg_ppartobj = total_ppartobj / used_slabs; | ||
889 | |||
890 | /* Per object object sizes */ | ||
891 | avg_objsize = total_used / total_objects; | ||
892 | avg_objwaste = total_objwaste / total_objects; | ||
893 | avg_partobj = total_partobj * 100 / total_objects; | ||
894 | avg_memobj = total_objsize / total_objects; | ||
895 | |||
896 | printf("Slabcache Totals\n"); | ||
897 | printf("----------------\n"); | ||
898 | printf("Slabcaches : %3d Aliases : %3d->%-3d Active: %3d\n", | ||
899 | slabs, aliases, alias_targets, used_slabs); | ||
900 | |||
901 | store_size(b1, total_size);store_size(b2, total_waste); | ||
902 | store_size(b3, total_waste * 100 / total_used); | ||
903 | printf("Memory used: %6s # Loss : %6s MRatio:%6s%%\n", b1, b2, b3); | ||
904 | |||
905 | store_size(b1, total_objects);store_size(b2, total_partobj); | ||
906 | store_size(b3, total_partobj * 100 / total_objects); | ||
907 | printf("# Objects : %6s # PartObj: %6s ORatio:%6s%%\n", b1, b2, b3); | ||
908 | |||
909 | printf("\n"); | ||
910 | printf("Per Cache Average Min Max Total\n"); | ||
911 | printf("---------------------------------------------------------\n"); | ||
912 | |||
913 | store_size(b1, avg_objects);store_size(b2, min_objects); | ||
914 | store_size(b3, max_objects);store_size(b4, total_objects); | ||
915 | printf("#Objects %10s %10s %10s %10s\n", | ||
916 | b1, b2, b3, b4); | ||
917 | |||
918 | store_size(b1, avg_slabs);store_size(b2, min_slabs); | ||
919 | store_size(b3, max_slabs);store_size(b4, total_slabs); | ||
920 | printf("#Slabs %10s %10s %10s %10s\n", | ||
921 | b1, b2, b3, b4); | ||
922 | |||
923 | store_size(b1, avg_partial);store_size(b2, min_partial); | ||
924 | store_size(b3, max_partial);store_size(b4, total_partial); | ||
925 | printf("#PartSlab %10s %10s %10s %10s\n", | ||
926 | b1, b2, b3, b4); | ||
927 | store_size(b1, avg_ppart);store_size(b2, min_ppart); | ||
928 | store_size(b3, max_ppart); | ||
929 | store_size(b4, total_partial * 100 / total_slabs); | ||
930 | printf("%%PartSlab%10s%% %10s%% %10s%% %10s%%\n", | ||
931 | b1, b2, b3, b4); | ||
932 | |||
933 | store_size(b1, avg_partobj);store_size(b2, min_partobj); | ||
934 | store_size(b3, max_partobj); | ||
935 | store_size(b4, total_partobj); | ||
936 | printf("PartObjs %10s %10s %10s %10s\n", | ||
937 | b1, b2, b3, b4); | ||
938 | |||
939 | store_size(b1, avg_ppartobj);store_size(b2, min_ppartobj); | ||
940 | store_size(b3, max_ppartobj); | ||
941 | store_size(b4, total_partobj * 100 / total_objects); | ||
942 | printf("%% PartObj%10s%% %10s%% %10s%% %10s%%\n", | ||
943 | b1, b2, b3, b4); | ||
944 | |||
945 | store_size(b1, avg_size);store_size(b2, min_size); | ||
946 | store_size(b3, max_size);store_size(b4, total_size); | ||
947 | printf("Memory %10s %10s %10s %10s\n", | ||
948 | b1, b2, b3, b4); | ||
949 | |||
950 | store_size(b1, avg_used);store_size(b2, min_used); | ||
951 | store_size(b3, max_used);store_size(b4, total_used); | ||
952 | printf("Used %10s %10s %10s %10s\n", | ||
953 | b1, b2, b3, b4); | ||
954 | |||
955 | store_size(b1, avg_waste);store_size(b2, min_waste); | ||
956 | store_size(b3, max_waste);store_size(b4, total_waste); | ||
957 | printf("Loss %10s %10s %10s %10s\n", | ||
958 | b1, b2, b3, b4); | ||
959 | |||
960 | printf("\n"); | ||
961 | printf("Per Object Average Min Max\n"); | ||
962 | printf("---------------------------------------------\n"); | ||
963 | |||
964 | store_size(b1, avg_memobj);store_size(b2, min_memobj); | ||
965 | store_size(b3, max_memobj); | ||
966 | printf("Memory %10s %10s %10s\n", | ||
967 | b1, b2, b3); | ||
968 | store_size(b1, avg_objsize);store_size(b2, min_objsize); | ||
969 | store_size(b3, max_objsize); | ||
970 | printf("User %10s %10s %10s\n", | ||
971 | b1, b2, b3); | ||
972 | |||
973 | store_size(b1, avg_objwaste);store_size(b2, min_objwaste); | ||
974 | store_size(b3, max_objwaste); | ||
975 | printf("Loss %10s %10s %10s\n", | ||
976 | b1, b2, b3); | ||
977 | } | ||
978 | |||
979 | static void sort_slabs(void) | ||
980 | { | ||
981 | struct slabinfo *s1,*s2; | ||
982 | |||
983 | for (s1 = slabinfo; s1 < slabinfo + slabs; s1++) { | ||
984 | for (s2 = s1 + 1; s2 < slabinfo + slabs; s2++) { | ||
985 | int result; | ||
986 | |||
987 | if (sort_size) | ||
988 | result = slab_size(s1) < slab_size(s2); | ||
989 | else if (sort_active) | ||
990 | result = slab_activity(s1) < slab_activity(s2); | ||
991 | else | ||
992 | result = strcasecmp(s1->name, s2->name); | ||
993 | |||
994 | if (show_inverted) | ||
995 | result = -result; | ||
996 | |||
997 | if (result > 0) { | ||
998 | struct slabinfo t; | ||
999 | |||
1000 | memcpy(&t, s1, sizeof(struct slabinfo)); | ||
1001 | memcpy(s1, s2, sizeof(struct slabinfo)); | ||
1002 | memcpy(s2, &t, sizeof(struct slabinfo)); | ||
1003 | } | ||
1004 | } | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | static void sort_aliases(void) | ||
1009 | { | ||
1010 | struct aliasinfo *a1,*a2; | ||
1011 | |||
1012 | for (a1 = aliasinfo; a1 < aliasinfo + aliases; a1++) { | ||
1013 | for (a2 = a1 + 1; a2 < aliasinfo + aliases; a2++) { | ||
1014 | char *n1, *n2; | ||
1015 | |||
1016 | n1 = a1->name; | ||
1017 | n2 = a2->name; | ||
1018 | if (show_alias && !show_inverted) { | ||
1019 | n1 = a1->ref; | ||
1020 | n2 = a2->ref; | ||
1021 | } | ||
1022 | if (strcasecmp(n1, n2) > 0) { | ||
1023 | struct aliasinfo t; | ||
1024 | |||
1025 | memcpy(&t, a1, sizeof(struct aliasinfo)); | ||
1026 | memcpy(a1, a2, sizeof(struct aliasinfo)); | ||
1027 | memcpy(a2, &t, sizeof(struct aliasinfo)); | ||
1028 | } | ||
1029 | } | ||
1030 | } | ||
1031 | } | ||
1032 | |||
1033 | static void link_slabs(void) | ||
1034 | { | ||
1035 | struct aliasinfo *a; | ||
1036 | struct slabinfo *s; | ||
1037 | |||
1038 | for (a = aliasinfo; a < aliasinfo + aliases; a++) { | ||
1039 | |||
1040 | for (s = slabinfo; s < slabinfo + slabs; s++) | ||
1041 | if (strcmp(a->ref, s->name) == 0) { | ||
1042 | a->slab = s; | ||
1043 | s->refs++; | ||
1044 | break; | ||
1045 | } | ||
1046 | if (s == slabinfo + slabs) | ||
1047 | fatal("Unresolved alias %s\n", a->ref); | ||
1048 | } | ||
1049 | } | ||
1050 | |||
1051 | static void alias(void) | ||
1052 | { | ||
1053 | struct aliasinfo *a; | ||
1054 | char *active = NULL; | ||
1055 | |||
1056 | sort_aliases(); | ||
1057 | link_slabs(); | ||
1058 | |||
1059 | for(a = aliasinfo; a < aliasinfo + aliases; a++) { | ||
1060 | |||
1061 | if (!show_single_ref && a->slab->refs == 1) | ||
1062 | continue; | ||
1063 | |||
1064 | if (!show_inverted) { | ||
1065 | if (active) { | ||
1066 | if (strcmp(a->slab->name, active) == 0) { | ||
1067 | printf(" %s", a->name); | ||
1068 | continue; | ||
1069 | } | ||
1070 | } | ||
1071 | printf("\n%-12s <- %s", a->slab->name, a->name); | ||
1072 | active = a->slab->name; | ||
1073 | } | ||
1074 | else | ||
1075 | printf("%-20s -> %s\n", a->name, a->slab->name); | ||
1076 | } | ||
1077 | if (active) | ||
1078 | printf("\n"); | ||
1079 | } | ||
1080 | |||
1081 | |||
1082 | static void rename_slabs(void) | ||
1083 | { | ||
1084 | struct slabinfo *s; | ||
1085 | struct aliasinfo *a; | ||
1086 | |||
1087 | for (s = slabinfo; s < slabinfo + slabs; s++) { | ||
1088 | if (*s->name != ':') | ||
1089 | continue; | ||
1090 | |||
1091 | if (s->refs > 1 && !show_first_alias) | ||
1092 | continue; | ||
1093 | |||
1094 | a = find_one_alias(s); | ||
1095 | |||
1096 | if (a) | ||
1097 | s->name = a->name; | ||
1098 | else { | ||
1099 | s->name = "*"; | ||
1100 | actual_slabs--; | ||
1101 | } | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | static int slab_mismatch(char *slab) | ||
1106 | { | ||
1107 | return regexec(&pattern, slab, 0, NULL, 0); | ||
1108 | } | ||
1109 | |||
1110 | static void read_slab_dir(void) | ||
1111 | { | ||
1112 | DIR *dir; | ||
1113 | struct dirent *de; | ||
1114 | struct slabinfo *slab = slabinfo; | ||
1115 | struct aliasinfo *alias = aliasinfo; | ||
1116 | char *p; | ||
1117 | char *t; | ||
1118 | int count; | ||
1119 | |||
1120 | if (chdir("/sys/kernel/slab") && chdir("/sys/slab")) | ||
1121 | fatal("SYSFS support for SLUB not active\n"); | ||
1122 | |||
1123 | dir = opendir("."); | ||
1124 | while ((de = readdir(dir))) { | ||
1125 | if (de->d_name[0] == '.' || | ||
1126 | (de->d_name[0] != ':' && slab_mismatch(de->d_name))) | ||
1127 | continue; | ||
1128 | switch (de->d_type) { | ||
1129 | case DT_LNK: | ||
1130 | alias->name = strdup(de->d_name); | ||
1131 | count = readlink(de->d_name, buffer, sizeof(buffer)); | ||
1132 | |||
1133 | if (count < 0) | ||
1134 | fatal("Cannot read symlink %s\n", de->d_name); | ||
1135 | |||
1136 | buffer[count] = 0; | ||
1137 | p = buffer + count; | ||
1138 | while (p > buffer && p[-1] != '/') | ||
1139 | p--; | ||
1140 | alias->ref = strdup(p); | ||
1141 | alias++; | ||
1142 | break; | ||
1143 | case DT_DIR: | ||
1144 | if (chdir(de->d_name)) | ||
1145 | fatal("Unable to access slab %s\n", slab->name); | ||
1146 | slab->name = strdup(de->d_name); | ||
1147 | slab->alias = 0; | ||
1148 | slab->refs = 0; | ||
1149 | slab->aliases = get_obj("aliases"); | ||
1150 | slab->align = get_obj("align"); | ||
1151 | slab->cache_dma = get_obj("cache_dma"); | ||
1152 | slab->cpu_slabs = get_obj("cpu_slabs"); | ||
1153 | slab->destroy_by_rcu = get_obj("destroy_by_rcu"); | ||
1154 | slab->hwcache_align = get_obj("hwcache_align"); | ||
1155 | slab->object_size = get_obj("object_size"); | ||
1156 | slab->objects = get_obj("objects"); | ||
1157 | slab->objects_partial = get_obj("objects_partial"); | ||
1158 | slab->objects_total = get_obj("objects_total"); | ||
1159 | slab->objs_per_slab = get_obj("objs_per_slab"); | ||
1160 | slab->order = get_obj("order"); | ||
1161 | slab->partial = get_obj("partial"); | ||
1162 | slab->partial = get_obj_and_str("partial", &t); | ||
1163 | decode_numa_list(slab->numa_partial, t); | ||
1164 | free(t); | ||
1165 | slab->poison = get_obj("poison"); | ||
1166 | slab->reclaim_account = get_obj("reclaim_account"); | ||
1167 | slab->red_zone = get_obj("red_zone"); | ||
1168 | slab->sanity_checks = get_obj("sanity_checks"); | ||
1169 | slab->slab_size = get_obj("slab_size"); | ||
1170 | slab->slabs = get_obj_and_str("slabs", &t); | ||
1171 | decode_numa_list(slab->numa, t); | ||
1172 | free(t); | ||
1173 | slab->store_user = get_obj("store_user"); | ||
1174 | slab->trace = get_obj("trace"); | ||
1175 | slab->alloc_fastpath = get_obj("alloc_fastpath"); | ||
1176 | slab->alloc_slowpath = get_obj("alloc_slowpath"); | ||
1177 | slab->free_fastpath = get_obj("free_fastpath"); | ||
1178 | slab->free_slowpath = get_obj("free_slowpath"); | ||
1179 | slab->free_frozen= get_obj("free_frozen"); | ||
1180 | slab->free_add_partial = get_obj("free_add_partial"); | ||
1181 | slab->free_remove_partial = get_obj("free_remove_partial"); | ||
1182 | slab->alloc_from_partial = get_obj("alloc_from_partial"); | ||
1183 | slab->alloc_slab = get_obj("alloc_slab"); | ||
1184 | slab->alloc_refill = get_obj("alloc_refill"); | ||
1185 | slab->free_slab = get_obj("free_slab"); | ||
1186 | slab->cpuslab_flush = get_obj("cpuslab_flush"); | ||
1187 | slab->deactivate_full = get_obj("deactivate_full"); | ||
1188 | slab->deactivate_empty = get_obj("deactivate_empty"); | ||
1189 | slab->deactivate_to_head = get_obj("deactivate_to_head"); | ||
1190 | slab->deactivate_to_tail = get_obj("deactivate_to_tail"); | ||
1191 | slab->deactivate_remote_frees = get_obj("deactivate_remote_frees"); | ||
1192 | slab->order_fallback = get_obj("order_fallback"); | ||
1193 | chdir(".."); | ||
1194 | if (slab->name[0] == ':') | ||
1195 | alias_targets++; | ||
1196 | slab++; | ||
1197 | break; | ||
1198 | default : | ||
1199 | fatal("Unknown file type %lx\n", de->d_type); | ||
1200 | } | ||
1201 | } | ||
1202 | closedir(dir); | ||
1203 | slabs = slab - slabinfo; | ||
1204 | actual_slabs = slabs; | ||
1205 | aliases = alias - aliasinfo; | ||
1206 | if (slabs > MAX_SLABS) | ||
1207 | fatal("Too many slabs\n"); | ||
1208 | if (aliases > MAX_ALIASES) | ||
1209 | fatal("Too many aliases\n"); | ||
1210 | } | ||
1211 | |||
1212 | static void output_slabs(void) | ||
1213 | { | ||
1214 | struct slabinfo *slab; | ||
1215 | |||
1216 | for (slab = slabinfo; slab < slabinfo + slabs; slab++) { | ||
1217 | |||
1218 | if (slab->alias) | ||
1219 | continue; | ||
1220 | |||
1221 | |||
1222 | if (show_numa) | ||
1223 | slab_numa(slab, 0); | ||
1224 | else if (show_track) | ||
1225 | show_tracking(slab); | ||
1226 | else if (validate) | ||
1227 | slab_validate(slab); | ||
1228 | else if (shrink) | ||
1229 | slab_shrink(slab); | ||
1230 | else if (set_debug) | ||
1231 | slab_debug(slab); | ||
1232 | else if (show_ops) | ||
1233 | ops(slab); | ||
1234 | else if (show_slab) | ||
1235 | slabcache(slab); | ||
1236 | else if (show_report) | ||
1237 | report(slab); | ||
1238 | } | ||
1239 | } | ||
1240 | |||
1241 | struct option opts[] = { | ||
1242 | { "aliases", 0, NULL, 'a' }, | ||
1243 | { "activity", 0, NULL, 'A' }, | ||
1244 | { "debug", 2, NULL, 'd' }, | ||
1245 | { "display-activity", 0, NULL, 'D' }, | ||
1246 | { "empty", 0, NULL, 'e' }, | ||
1247 | { "first-alias", 0, NULL, 'f' }, | ||
1248 | { "help", 0, NULL, 'h' }, | ||
1249 | { "inverted", 0, NULL, 'i'}, | ||
1250 | { "numa", 0, NULL, 'n' }, | ||
1251 | { "ops", 0, NULL, 'o' }, | ||
1252 | { "report", 0, NULL, 'r' }, | ||
1253 | { "shrink", 0, NULL, 's' }, | ||
1254 | { "slabs", 0, NULL, 'l' }, | ||
1255 | { "track", 0, NULL, 't'}, | ||
1256 | { "validate", 0, NULL, 'v' }, | ||
1257 | { "zero", 0, NULL, 'z' }, | ||
1258 | { "1ref", 0, NULL, '1'}, | ||
1259 | { NULL, 0, NULL, 0 } | ||
1260 | }; | ||
1261 | |||
1262 | int main(int argc, char *argv[]) | ||
1263 | { | ||
1264 | int c; | ||
1265 | int err; | ||
1266 | char *pattern_source; | ||
1267 | |||
1268 | page_size = getpagesize(); | ||
1269 | |||
1270 | while ((c = getopt_long(argc, argv, "aAd::Defhil1noprstvzTS", | ||
1271 | opts, NULL)) != -1) | ||
1272 | switch (c) { | ||
1273 | case '1': | ||
1274 | show_single_ref = 1; | ||
1275 | break; | ||
1276 | case 'a': | ||
1277 | show_alias = 1; | ||
1278 | break; | ||
1279 | case 'A': | ||
1280 | sort_active = 1; | ||
1281 | break; | ||
1282 | case 'd': | ||
1283 | set_debug = 1; | ||
1284 | if (!debug_opt_scan(optarg)) | ||
1285 | fatal("Invalid debug option '%s'\n", optarg); | ||
1286 | break; | ||
1287 | case 'D': | ||
1288 | show_activity = 1; | ||
1289 | break; | ||
1290 | case 'e': | ||
1291 | show_empty = 1; | ||
1292 | break; | ||
1293 | case 'f': | ||
1294 | show_first_alias = 1; | ||
1295 | break; | ||
1296 | case 'h': | ||
1297 | usage(); | ||
1298 | return 0; | ||
1299 | case 'i': | ||
1300 | show_inverted = 1; | ||
1301 | break; | ||
1302 | case 'n': | ||
1303 | show_numa = 1; | ||
1304 | break; | ||
1305 | case 'o': | ||
1306 | show_ops = 1; | ||
1307 | break; | ||
1308 | case 'r': | ||
1309 | show_report = 1; | ||
1310 | break; | ||
1311 | case 's': | ||
1312 | shrink = 1; | ||
1313 | break; | ||
1314 | case 'l': | ||
1315 | show_slab = 1; | ||
1316 | break; | ||
1317 | case 't': | ||
1318 | show_track = 1; | ||
1319 | break; | ||
1320 | case 'v': | ||
1321 | validate = 1; | ||
1322 | break; | ||
1323 | case 'z': | ||
1324 | skip_zero = 0; | ||
1325 | break; | ||
1326 | case 'T': | ||
1327 | show_totals = 1; | ||
1328 | break; | ||
1329 | case 'S': | ||
1330 | sort_size = 1; | ||
1331 | break; | ||
1332 | |||
1333 | default: | ||
1334 | fatal("%s: Invalid option '%c'\n", argv[0], optopt); | ||
1335 | |||
1336 | } | ||
1337 | |||
1338 | if (!show_slab && !show_alias && !show_track && !show_report | ||
1339 | && !validate && !shrink && !set_debug && !show_ops) | ||
1340 | show_slab = 1; | ||
1341 | |||
1342 | if (argc > optind) | ||
1343 | pattern_source = argv[optind]; | ||
1344 | else | ||
1345 | pattern_source = ".*"; | ||
1346 | |||
1347 | err = regcomp(&pattern, pattern_source, REG_ICASE|REG_NOSUB); | ||
1348 | if (err) | ||
1349 | fatal("%s: Invalid pattern '%s' code %d\n", | ||
1350 | argv[0], pattern_source, err); | ||
1351 | read_slab_dir(); | ||
1352 | if (show_alias) | ||
1353 | alias(); | ||
1354 | else | ||
1355 | if (show_totals) | ||
1356 | totals(); | ||
1357 | else { | ||
1358 | link_slabs(); | ||
1359 | rename_slabs(); | ||
1360 | sort_slabs(); | ||
1361 | output_slabs(); | ||
1362 | } | ||
1363 | return 0; | ||
1364 | } | ||
diff --git a/tools/testing/ktest/compare-ktest-sample.pl b/tools/testing/ktest/compare-ktest-sample.pl new file mode 100755 index 000000000000..9a571e71683c --- /dev/null +++ b/tools/testing/ktest/compare-ktest-sample.pl | |||
@@ -0,0 +1,30 @@ | |||
1 | #!/usr/bin/perl | ||
2 | |||
3 | open (IN,"ktest.pl"); | ||
4 | while (<IN>) { | ||
5 | if (/\$opt\{"?([A-Z].*?)(\[.*\])?"?\}/ || | ||
6 | /set_test_option\("(.*?)"/) { | ||
7 | $opt{$1} = 1; | ||
8 | } | ||
9 | } | ||
10 | close IN; | ||
11 | |||
12 | open (IN, "sample.conf"); | ||
13 | while (<IN>) { | ||
14 | if (/^\s*#?\s*(\S+)\s*=/) { | ||
15 | $samp{$1} = 1; | ||
16 | } | ||
17 | } | ||
18 | close IN; | ||
19 | |||
20 | foreach $opt (keys %opt) { | ||
21 | if (!defined($samp{$opt})) { | ||
22 | print "opt = $opt\n"; | ||
23 | } | ||
24 | } | ||
25 | |||
26 | foreach $samp (keys %samp) { | ||
27 | if (!defined($opt{$samp})) { | ||
28 | print "samp = $samp\n"; | ||
29 | } | ||
30 | } | ||
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl new file mode 100755 index 000000000000..e1c62eeb88f5 --- /dev/null +++ b/tools/testing/ktest/ktest.pl | |||
@@ -0,0 +1,2023 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | # | ||
3 | # Copywrite 2010 - Steven Rostedt <srostedt@redhat.com>, Red Hat Inc. | ||
4 | # Licensed under the terms of the GNU GPL License version 2 | ||
5 | # | ||
6 | |||
7 | use strict; | ||
8 | use IPC::Open2; | ||
9 | use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); | ||
10 | use File::Path qw(mkpath); | ||
11 | use File::Copy qw(cp); | ||
12 | use FileHandle; | ||
13 | |||
14 | my $VERSION = "0.2"; | ||
15 | |||
16 | $| = 1; | ||
17 | |||
18 | my %opt; | ||
19 | my %repeat_tests; | ||
20 | my %repeats; | ||
21 | my %default; | ||
22 | |||
23 | #default opts | ||
24 | $default{"NUM_TESTS"} = 1; | ||
25 | $default{"REBOOT_TYPE"} = "grub"; | ||
26 | $default{"TEST_TYPE"} = "test"; | ||
27 | $default{"BUILD_TYPE"} = "randconfig"; | ||
28 | $default{"MAKE_CMD"} = "make"; | ||
29 | $default{"TIMEOUT"} = 120; | ||
30 | $default{"TMP_DIR"} = "/tmp/ktest"; | ||
31 | $default{"SLEEP_TIME"} = 60; # sleep time between tests | ||
32 | $default{"BUILD_NOCLEAN"} = 0; | ||
33 | $default{"REBOOT_ON_ERROR"} = 0; | ||
34 | $default{"POWEROFF_ON_ERROR"} = 0; | ||
35 | $default{"REBOOT_ON_SUCCESS"} = 1; | ||
36 | $default{"POWEROFF_ON_SUCCESS"} = 0; | ||
37 | $default{"BUILD_OPTIONS"} = ""; | ||
38 | $default{"BISECT_SLEEP_TIME"} = 60; # sleep time between bisects | ||
39 | $default{"CLEAR_LOG"} = 0; | ||
40 | $default{"SUCCESS_LINE"} = "login:"; | ||
41 | $default{"BOOTED_TIMEOUT"} = 1; | ||
42 | $default{"DIE_ON_FAILURE"} = 1; | ||
43 | $default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND"; | ||
44 | $default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE"; | ||
45 | $default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot"; | ||
46 | $default{"STOP_AFTER_SUCCESS"} = 10; | ||
47 | $default{"STOP_AFTER_FAILURE"} = 60; | ||
48 | $default{"LOCALVERSION"} = "-test"; | ||
49 | |||
50 | my $ktest_config; | ||
51 | my $version; | ||
52 | my $machine; | ||
53 | my $ssh_user; | ||
54 | my $tmpdir; | ||
55 | my $builddir; | ||
56 | my $outputdir; | ||
57 | my $output_config; | ||
58 | my $test_type; | ||
59 | my $build_type; | ||
60 | my $build_options; | ||
61 | my $reboot_type; | ||
62 | my $reboot_script; | ||
63 | my $power_cycle; | ||
64 | my $reboot; | ||
65 | my $reboot_on_error; | ||
66 | my $poweroff_on_error; | ||
67 | my $die_on_failure; | ||
68 | my $powercycle_after_reboot; | ||
69 | my $poweroff_after_halt; | ||
70 | my $ssh_exec; | ||
71 | my $scp_to_target; | ||
72 | my $power_off; | ||
73 | my $grub_menu; | ||
74 | my $grub_number; | ||
75 | my $target; | ||
76 | my $make; | ||
77 | my $post_install; | ||
78 | my $noclean; | ||
79 | my $minconfig; | ||
80 | my $addconfig; | ||
81 | my $in_bisect = 0; | ||
82 | my $bisect_bad = ""; | ||
83 | my $reverse_bisect; | ||
84 | my $in_patchcheck = 0; | ||
85 | my $run_test; | ||
86 | my $redirect; | ||
87 | my $buildlog; | ||
88 | my $dmesg; | ||
89 | my $monitor_fp; | ||
90 | my $monitor_pid; | ||
91 | my $monitor_cnt = 0; | ||
92 | my $sleep_time; | ||
93 | my $bisect_sleep_time; | ||
94 | my $store_failures; | ||
95 | my $timeout; | ||
96 | my $booted_timeout; | ||
97 | my $console; | ||
98 | my $success_line; | ||
99 | my $stop_after_success; | ||
100 | my $stop_after_failure; | ||
101 | my $build_target; | ||
102 | my $target_image; | ||
103 | my $localversion; | ||
104 | my $iteration = 0; | ||
105 | my $successes = 0; | ||
106 | |||
107 | my %entered_configs; | ||
108 | my %config_help; | ||
109 | |||
110 | $config_help{"MACHINE"} = << "EOF" | ||
111 | The machine hostname that you will test. | ||
112 | EOF | ||
113 | ; | ||
114 | $config_help{"SSH_USER"} = << "EOF" | ||
115 | The box is expected to have ssh on normal bootup, provide the user | ||
116 | (most likely root, since you need privileged operations) | ||
117 | EOF | ||
118 | ; | ||
119 | $config_help{"BUILD_DIR"} = << "EOF" | ||
120 | The directory that contains the Linux source code (full path). | ||
121 | EOF | ||
122 | ; | ||
123 | $config_help{"OUTPUT_DIR"} = << "EOF" | ||
124 | The directory that the objects will be built (full path). | ||
125 | (can not be same as BUILD_DIR) | ||
126 | EOF | ||
127 | ; | ||
128 | $config_help{"BUILD_TARGET"} = << "EOF" | ||
129 | The location of the compiled file to copy to the target. | ||
130 | (relative to OUTPUT_DIR) | ||
131 | EOF | ||
132 | ; | ||
133 | $config_help{"TARGET_IMAGE"} = << "EOF" | ||
134 | The place to put your image on the test machine. | ||
135 | EOF | ||
136 | ; | ||
137 | $config_help{"POWER_CYCLE"} = << "EOF" | ||
138 | A script or command to reboot the box. | ||
139 | |||
140 | Here is a digital loggers power switch example | ||
141 | POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin\@power/outlet?5=CCL' | ||
142 | |||
143 | Here is an example to reboot a virtual box on the current host | ||
144 | with the name "Guest". | ||
145 | POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest | ||
146 | EOF | ||
147 | ; | ||
148 | $config_help{"CONSOLE"} = << "EOF" | ||
149 | The script or command that reads the console | ||
150 | |||
151 | If you use ttywatch server, something like the following would work. | ||
152 | CONSOLE = nc -d localhost 3001 | ||
153 | |||
154 | For a virtual machine with guest name "Guest". | ||
155 | CONSOLE = virsh console Guest | ||
156 | EOF | ||
157 | ; | ||
158 | $config_help{"LOCALVERSION"} = << "EOF" | ||
159 | Required version ending to differentiate the test | ||
160 | from other linux builds on the system. | ||
161 | EOF | ||
162 | ; | ||
163 | $config_help{"REBOOT_TYPE"} = << "EOF" | ||
164 | Way to reboot the box to the test kernel. | ||
165 | Only valid options so far are "grub" and "script". | ||
166 | |||
167 | If you specify grub, it will assume grub version 1 | ||
168 | and will search in /boot/grub/menu.lst for the title \$GRUB_MENU | ||
169 | and select that target to reboot to the kernel. If this is not | ||
170 | your setup, then specify "script" and have a command or script | ||
171 | specified in REBOOT_SCRIPT to boot to the target. | ||
172 | |||
173 | The entry in /boot/grub/menu.lst must be entered in manually. | ||
174 | The test will not modify that file. | ||
175 | EOF | ||
176 | ; | ||
177 | $config_help{"GRUB_MENU"} = << "EOF" | ||
178 | The grub title name for the test kernel to boot | ||
179 | (Only mandatory if REBOOT_TYPE = grub) | ||
180 | |||
181 | Note, ktest.pl will not update the grub menu.lst, you need to | ||
182 | manually add an option for the test. ktest.pl will search | ||
183 | the grub menu.lst for this option to find what kernel to | ||
184 | reboot into. | ||
185 | |||
186 | For example, if in the /boot/grub/menu.lst the test kernel title has: | ||
187 | title Test Kernel | ||
188 | kernel vmlinuz-test | ||
189 | GRUB_MENU = Test Kernel | ||
190 | EOF | ||
191 | ; | ||
192 | $config_help{"REBOOT_SCRIPT"} = << "EOF" | ||
193 | A script to reboot the target into the test kernel | ||
194 | (Only mandatory if REBOOT_TYPE = script) | ||
195 | EOF | ||
196 | ; | ||
197 | |||
198 | |||
199 | sub get_ktest_config { | ||
200 | my ($config) = @_; | ||
201 | |||
202 | return if (defined($opt{$config})); | ||
203 | |||
204 | if (defined($config_help{$config})) { | ||
205 | print "\n"; | ||
206 | print $config_help{$config}; | ||
207 | } | ||
208 | |||
209 | for (;;) { | ||
210 | print "$config = "; | ||
211 | if (defined($default{$config})) { | ||
212 | print "\[$default{$config}\] "; | ||
213 | } | ||
214 | $entered_configs{$config} = <STDIN>; | ||
215 | $entered_configs{$config} =~ s/^\s*(.*\S)\s*$/$1/; | ||
216 | if ($entered_configs{$config} =~ /^\s*$/) { | ||
217 | if ($default{$config}) { | ||
218 | $entered_configs{$config} = $default{$config}; | ||
219 | } else { | ||
220 | print "Your answer can not be blank\n"; | ||
221 | next; | ||
222 | } | ||
223 | } | ||
224 | last; | ||
225 | } | ||
226 | } | ||
227 | |||
228 | sub get_ktest_configs { | ||
229 | get_ktest_config("MACHINE"); | ||
230 | get_ktest_config("SSH_USER"); | ||
231 | get_ktest_config("BUILD_DIR"); | ||
232 | get_ktest_config("OUTPUT_DIR"); | ||
233 | get_ktest_config("BUILD_TARGET"); | ||
234 | get_ktest_config("TARGET_IMAGE"); | ||
235 | get_ktest_config("POWER_CYCLE"); | ||
236 | get_ktest_config("CONSOLE"); | ||
237 | get_ktest_config("LOCALVERSION"); | ||
238 | |||
239 | my $rtype = $opt{"REBOOT_TYPE"}; | ||
240 | |||
241 | if (!defined($rtype)) { | ||
242 | if (!defined($opt{"GRUB_MENU"})) { | ||
243 | get_ktest_config("REBOOT_TYPE"); | ||
244 | $rtype = $entered_configs{"REBOOT_TYPE"}; | ||
245 | } else { | ||
246 | $rtype = "grub"; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | if ($rtype eq "grub") { | ||
251 | get_ktest_config("GRUB_MENU"); | ||
252 | } else { | ||
253 | get_ktest_config("REBOOT_SCRIPT"); | ||
254 | } | ||
255 | } | ||
256 | |||
257 | sub set_value { | ||
258 | my ($lvalue, $rvalue) = @_; | ||
259 | |||
260 | if (defined($opt{$lvalue})) { | ||
261 | die "Error: Option $lvalue defined more than once!\n"; | ||
262 | } | ||
263 | if ($rvalue =~ /^\s*$/) { | ||
264 | delete $opt{$lvalue}; | ||
265 | } else { | ||
266 | $opt{$lvalue} = $rvalue; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | sub read_config { | ||
271 | my ($config) = @_; | ||
272 | |||
273 | open(IN, $config) || die "can't read file $config"; | ||
274 | |||
275 | my $name = $config; | ||
276 | $name =~ s,.*/(.*),$1,; | ||
277 | |||
278 | my $test_num = 0; | ||
279 | my $default = 1; | ||
280 | my $repeat = 1; | ||
281 | my $num_tests_set = 0; | ||
282 | my $skip = 0; | ||
283 | my $rest; | ||
284 | |||
285 | while (<IN>) { | ||
286 | |||
287 | # ignore blank lines and comments | ||
288 | next if (/^\s*$/ || /\s*\#/); | ||
289 | |||
290 | if (/^\s*TEST_START(.*)/) { | ||
291 | |||
292 | $rest = $1; | ||
293 | |||
294 | if ($num_tests_set) { | ||
295 | die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; | ||
296 | } | ||
297 | |||
298 | my $old_test_num = $test_num; | ||
299 | my $old_repeat = $repeat; | ||
300 | |||
301 | $test_num += $repeat; | ||
302 | $default = 0; | ||
303 | $repeat = 1; | ||
304 | |||
305 | if ($rest =~ /\s+SKIP(.*)/) { | ||
306 | $rest = $1; | ||
307 | $skip = 1; | ||
308 | } else { | ||
309 | $skip = 0; | ||
310 | } | ||
311 | |||
312 | if ($rest =~ /\s+ITERATE\s+(\d+)(.*)$/) { | ||
313 | $repeat = $1; | ||
314 | $rest = $2; | ||
315 | $repeat_tests{"$test_num"} = $repeat; | ||
316 | } | ||
317 | |||
318 | if ($rest =~ /\s+SKIP(.*)/) { | ||
319 | $rest = $1; | ||
320 | $skip = 1; | ||
321 | } | ||
322 | |||
323 | if ($rest !~ /^\s*$/) { | ||
324 | die "$name: $.: Gargbage found after TEST_START\n$_"; | ||
325 | } | ||
326 | |||
327 | if ($skip) { | ||
328 | $test_num = $old_test_num; | ||
329 | $repeat = $old_repeat; | ||
330 | } | ||
331 | |||
332 | } elsif (/^\s*DEFAULTS(.*)$/) { | ||
333 | $default = 1; | ||
334 | |||
335 | $rest = $1; | ||
336 | |||
337 | if ($rest =~ /\s+SKIP(.*)/) { | ||
338 | $rest = $1; | ||
339 | $skip = 1; | ||
340 | } else { | ||
341 | $skip = 0; | ||
342 | } | ||
343 | |||
344 | if ($rest !~ /^\s*$/) { | ||
345 | die "$name: $.: Gargbage found after DEFAULTS\n$_"; | ||
346 | } | ||
347 | |||
348 | } elsif (/^\s*([A-Z_\[\]\d]+)\s*=\s*(.*?)\s*$/) { | ||
349 | |||
350 | next if ($skip); | ||
351 | |||
352 | my $lvalue = $1; | ||
353 | my $rvalue = $2; | ||
354 | |||
355 | if (!$default && | ||
356 | ($lvalue eq "NUM_TESTS" || | ||
357 | $lvalue eq "LOG_FILE" || | ||
358 | $lvalue eq "CLEAR_LOG")) { | ||
359 | die "$name: $.: $lvalue must be set in DEFAULTS section\n"; | ||
360 | } | ||
361 | |||
362 | if ($lvalue eq "NUM_TESTS") { | ||
363 | if ($test_num) { | ||
364 | die "$name: $.: Can not specify both NUM_TESTS and TEST_START\n"; | ||
365 | } | ||
366 | if (!$default) { | ||
367 | die "$name: $.: NUM_TESTS must be set in default section\n"; | ||
368 | } | ||
369 | $num_tests_set = 1; | ||
370 | } | ||
371 | |||
372 | if ($default || $lvalue =~ /\[\d+\]$/) { | ||
373 | set_value($lvalue, $rvalue); | ||
374 | } else { | ||
375 | my $val = "$lvalue\[$test_num\]"; | ||
376 | set_value($val, $rvalue); | ||
377 | |||
378 | if ($repeat > 1) { | ||
379 | $repeats{$val} = $repeat; | ||
380 | } | ||
381 | } | ||
382 | } else { | ||
383 | die "$name: $.: Garbage found in config\n$_"; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | close(IN); | ||
388 | |||
389 | if ($test_num) { | ||
390 | $test_num += $repeat - 1; | ||
391 | $opt{"NUM_TESTS"} = $test_num; | ||
392 | } | ||
393 | |||
394 | # make sure we have all mandatory configs | ||
395 | get_ktest_configs; | ||
396 | |||
397 | # set any defaults | ||
398 | |||
399 | foreach my $default (keys %default) { | ||
400 | if (!defined($opt{$default})) { | ||
401 | $opt{$default} = $default{$default}; | ||
402 | } | ||
403 | } | ||
404 | } | ||
405 | |||
406 | sub _logit { | ||
407 | if (defined($opt{"LOG_FILE"})) { | ||
408 | open(OUT, ">> $opt{LOG_FILE}") or die "Can't write to $opt{LOG_FILE}"; | ||
409 | print OUT @_; | ||
410 | close(OUT); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | sub logit { | ||
415 | if (defined($opt{"LOG_FILE"})) { | ||
416 | _logit @_; | ||
417 | } else { | ||
418 | print @_; | ||
419 | } | ||
420 | } | ||
421 | |||
422 | sub doprint { | ||
423 | print @_; | ||
424 | _logit @_; | ||
425 | } | ||
426 | |||
427 | sub run_command; | ||
428 | |||
429 | sub reboot { | ||
430 | # try to reboot normally | ||
431 | if (run_command $reboot) { | ||
432 | if (defined($powercycle_after_reboot)) { | ||
433 | sleep $powercycle_after_reboot; | ||
434 | run_command "$power_cycle"; | ||
435 | } | ||
436 | } else { | ||
437 | # nope? power cycle it. | ||
438 | run_command "$power_cycle"; | ||
439 | } | ||
440 | } | ||
441 | |||
442 | sub do_not_reboot { | ||
443 | my $i = $iteration; | ||
444 | |||
445 | return $test_type eq "build" || | ||
446 | ($test_type eq "patchcheck" && $opt{"PATCHCHECK_TYPE[$i]"} eq "build") || | ||
447 | ($test_type eq "bisect" && $opt{"BISECT_TYPE[$i]"} eq "build"); | ||
448 | } | ||
449 | |||
450 | sub dodie { | ||
451 | doprint "CRITICAL FAILURE... ", @_, "\n"; | ||
452 | |||
453 | my $i = $iteration; | ||
454 | |||
455 | if ($reboot_on_error && !do_not_reboot) { | ||
456 | |||
457 | doprint "REBOOTING\n"; | ||
458 | reboot; | ||
459 | |||
460 | } elsif ($poweroff_on_error && defined($power_off)) { | ||
461 | doprint "POWERING OFF\n"; | ||
462 | `$power_off`; | ||
463 | } | ||
464 | |||
465 | die @_, "\n"; | ||
466 | } | ||
467 | |||
468 | sub open_console { | ||
469 | my ($fp) = @_; | ||
470 | |||
471 | my $flags; | ||
472 | |||
473 | my $pid = open($fp, "$console|") or | ||
474 | dodie "Can't open console $console"; | ||
475 | |||
476 | $flags = fcntl($fp, F_GETFL, 0) or | ||
477 | dodie "Can't get flags for the socket: $!"; | ||
478 | $flags = fcntl($fp, F_SETFL, $flags | O_NONBLOCK) or | ||
479 | dodie "Can't set flags for the socket: $!"; | ||
480 | |||
481 | return $pid; | ||
482 | } | ||
483 | |||
484 | sub close_console { | ||
485 | my ($fp, $pid) = @_; | ||
486 | |||
487 | doprint "kill child process $pid\n"; | ||
488 | kill 2, $pid; | ||
489 | |||
490 | print "closing!\n"; | ||
491 | close($fp); | ||
492 | } | ||
493 | |||
494 | sub start_monitor { | ||
495 | if ($monitor_cnt++) { | ||
496 | return; | ||
497 | } | ||
498 | $monitor_fp = \*MONFD; | ||
499 | $monitor_pid = open_console $monitor_fp; | ||
500 | |||
501 | return; | ||
502 | |||
503 | open(MONFD, "Stop perl from warning about single use of MONFD"); | ||
504 | } | ||
505 | |||
506 | sub end_monitor { | ||
507 | if (--$monitor_cnt) { | ||
508 | return; | ||
509 | } | ||
510 | close_console($monitor_fp, $monitor_pid); | ||
511 | } | ||
512 | |||
513 | sub wait_for_monitor { | ||
514 | my ($time) = @_; | ||
515 | my $line; | ||
516 | |||
517 | doprint "** Wait for monitor to settle down **\n"; | ||
518 | |||
519 | # read the monitor and wait for the system to calm down | ||
520 | do { | ||
521 | $line = wait_for_input($monitor_fp, $time); | ||
522 | print "$line" if (defined($line)); | ||
523 | } while (defined($line)); | ||
524 | print "** Monitor flushed **\n"; | ||
525 | } | ||
526 | |||
527 | sub fail { | ||
528 | |||
529 | if ($die_on_failure) { | ||
530 | dodie @_; | ||
531 | } | ||
532 | |||
533 | doprint "FAILED\n"; | ||
534 | |||
535 | my $i = $iteration; | ||
536 | |||
537 | # no need to reboot for just building. | ||
538 | if (!do_not_reboot) { | ||
539 | doprint "REBOOTING\n"; | ||
540 | reboot; | ||
541 | start_monitor; | ||
542 | wait_for_monitor $sleep_time; | ||
543 | end_monitor; | ||
544 | } | ||
545 | |||
546 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | ||
547 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | ||
548 | doprint "KTEST RESULT: TEST $i Failed: ", @_, "\n"; | ||
549 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | ||
550 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | ||
551 | |||
552 | return 1 if (!defined($store_failures)); | ||
553 | |||
554 | my @t = localtime; | ||
555 | my $date = sprintf "%04d%02d%02d%02d%02d%02d", | ||
556 | 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; | ||
557 | |||
558 | my $type = $build_type; | ||
559 | if ($type =~ /useconfig/) { | ||
560 | $type = "useconfig"; | ||
561 | } | ||
562 | |||
563 | my $dir = "$machine-$test_type-$type-fail-$date"; | ||
564 | my $faildir = "$store_failures/$dir"; | ||
565 | |||
566 | if (!-d $faildir) { | ||
567 | mkpath($faildir) or | ||
568 | die "can't create $faildir"; | ||
569 | } | ||
570 | if (-f "$output_config") { | ||
571 | cp "$output_config", "$faildir/config" or | ||
572 | die "failed to copy .config"; | ||
573 | } | ||
574 | if (-f $buildlog) { | ||
575 | cp $buildlog, "$faildir/buildlog" or | ||
576 | die "failed to move $buildlog"; | ||
577 | } | ||
578 | if (-f $dmesg) { | ||
579 | cp $dmesg, "$faildir/dmesg" or | ||
580 | die "failed to move $dmesg"; | ||
581 | } | ||
582 | |||
583 | doprint "*** Saved info to $faildir ***\n"; | ||
584 | |||
585 | return 1; | ||
586 | } | ||
587 | |||
588 | sub run_command { | ||
589 | my ($command) = @_; | ||
590 | my $dolog = 0; | ||
591 | my $dord = 0; | ||
592 | my $pid; | ||
593 | |||
594 | $command =~ s/\$SSH_USER/$ssh_user/g; | ||
595 | $command =~ s/\$MACHINE/$machine/g; | ||
596 | |||
597 | doprint("$command ... "); | ||
598 | |||
599 | $pid = open(CMD, "$command 2>&1 |") or | ||
600 | (fail "unable to exec $command" and return 0); | ||
601 | |||
602 | if (defined($opt{"LOG_FILE"})) { | ||
603 | open(LOG, ">>$opt{LOG_FILE}") or | ||
604 | dodie "failed to write to log"; | ||
605 | $dolog = 1; | ||
606 | } | ||
607 | |||
608 | if (defined($redirect)) { | ||
609 | open (RD, ">$redirect") or | ||
610 | dodie "failed to write to redirect $redirect"; | ||
611 | $dord = 1; | ||
612 | } | ||
613 | |||
614 | while (<CMD>) { | ||
615 | print LOG if ($dolog); | ||
616 | print RD if ($dord); | ||
617 | } | ||
618 | |||
619 | waitpid($pid, 0); | ||
620 | my $failed = $?; | ||
621 | |||
622 | close(CMD); | ||
623 | close(LOG) if ($dolog); | ||
624 | close(RD) if ($dord); | ||
625 | |||
626 | if ($failed) { | ||
627 | doprint "FAILED!\n"; | ||
628 | } else { | ||
629 | doprint "SUCCESS\n"; | ||
630 | } | ||
631 | |||
632 | return !$failed; | ||
633 | } | ||
634 | |||
635 | sub run_ssh { | ||
636 | my ($cmd) = @_; | ||
637 | my $cp_exec = $ssh_exec; | ||
638 | |||
639 | $cp_exec =~ s/\$SSH_COMMAND/$cmd/g; | ||
640 | return run_command "$cp_exec"; | ||
641 | } | ||
642 | |||
643 | sub run_scp { | ||
644 | my ($src, $dst) = @_; | ||
645 | my $cp_scp = $scp_to_target; | ||
646 | |||
647 | $cp_scp =~ s/\$SRC_FILE/$src/g; | ||
648 | $cp_scp =~ s/\$DST_FILE/$dst/g; | ||
649 | |||
650 | return run_command "$cp_scp"; | ||
651 | } | ||
652 | |||
653 | sub get_grub_index { | ||
654 | |||
655 | if ($reboot_type ne "grub") { | ||
656 | return; | ||
657 | } | ||
658 | return if (defined($grub_number)); | ||
659 | |||
660 | doprint "Find grub menu ... "; | ||
661 | $grub_number = -1; | ||
662 | |||
663 | my $ssh_grub = $ssh_exec; | ||
664 | $ssh_grub =~ s,\$SSH_COMMAND,cat /boot/grub/menu.lst,g; | ||
665 | |||
666 | open(IN, "$ssh_grub |") | ||
667 | or die "unable to get menu.lst"; | ||
668 | |||
669 | while (<IN>) { | ||
670 | if (/^\s*title\s+$grub_menu\s*$/) { | ||
671 | $grub_number++; | ||
672 | last; | ||
673 | } elsif (/^\s*title\s/) { | ||
674 | $grub_number++; | ||
675 | } | ||
676 | } | ||
677 | close(IN); | ||
678 | |||
679 | die "Could not find '$grub_menu' in /boot/grub/menu on $machine" | ||
680 | if ($grub_number < 0); | ||
681 | doprint "$grub_number\n"; | ||
682 | } | ||
683 | |||
684 | sub wait_for_input | ||
685 | { | ||
686 | my ($fp, $time) = @_; | ||
687 | my $rin; | ||
688 | my $ready; | ||
689 | my $line; | ||
690 | my $ch; | ||
691 | |||
692 | if (!defined($time)) { | ||
693 | $time = $timeout; | ||
694 | } | ||
695 | |||
696 | $rin = ''; | ||
697 | vec($rin, fileno($fp), 1) = 1; | ||
698 | $ready = select($rin, undef, undef, $time); | ||
699 | |||
700 | $line = ""; | ||
701 | |||
702 | # try to read one char at a time | ||
703 | while (sysread $fp, $ch, 1) { | ||
704 | $line .= $ch; | ||
705 | last if ($ch eq "\n"); | ||
706 | } | ||
707 | |||
708 | if (!length($line)) { | ||
709 | return undef; | ||
710 | } | ||
711 | |||
712 | return $line; | ||
713 | } | ||
714 | |||
715 | sub reboot_to { | ||
716 | if ($reboot_type eq "grub") { | ||
717 | run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch; reboot)'"; | ||
718 | return; | ||
719 | } | ||
720 | |||
721 | run_command "$reboot_script"; | ||
722 | } | ||
723 | |||
724 | sub get_sha1 { | ||
725 | my ($commit) = @_; | ||
726 | |||
727 | doprint "git rev-list --max-count=1 $commit ... "; | ||
728 | my $sha1 = `git rev-list --max-count=1 $commit`; | ||
729 | my $ret = $?; | ||
730 | |||
731 | logit $sha1; | ||
732 | |||
733 | if ($ret) { | ||
734 | doprint "FAILED\n"; | ||
735 | dodie "Failed to get git $commit"; | ||
736 | } | ||
737 | |||
738 | print "SUCCESS\n"; | ||
739 | |||
740 | chomp $sha1; | ||
741 | |||
742 | return $sha1; | ||
743 | } | ||
744 | |||
745 | sub monitor { | ||
746 | my $booted = 0; | ||
747 | my $bug = 0; | ||
748 | my $skip_call_trace = 0; | ||
749 | my $loops; | ||
750 | |||
751 | wait_for_monitor 5; | ||
752 | |||
753 | my $line; | ||
754 | my $full_line = ""; | ||
755 | |||
756 | open(DMESG, "> $dmesg") or | ||
757 | die "unable to write to $dmesg"; | ||
758 | |||
759 | reboot_to; | ||
760 | |||
761 | my $success_start; | ||
762 | my $failure_start; | ||
763 | |||
764 | for (;;) { | ||
765 | |||
766 | if ($booted) { | ||
767 | $line = wait_for_input($monitor_fp, $booted_timeout); | ||
768 | } else { | ||
769 | $line = wait_for_input($monitor_fp); | ||
770 | } | ||
771 | |||
772 | last if (!defined($line)); | ||
773 | |||
774 | doprint $line; | ||
775 | print DMESG $line; | ||
776 | |||
777 | # we are not guaranteed to get a full line | ||
778 | $full_line .= $line; | ||
779 | |||
780 | if ($full_line =~ /$success_line/) { | ||
781 | $booted = 1; | ||
782 | $success_start = time; | ||
783 | } | ||
784 | |||
785 | if ($booted && defined($stop_after_success) && | ||
786 | $stop_after_success >= 0) { | ||
787 | my $now = time; | ||
788 | if ($now - $success_start >= $stop_after_success) { | ||
789 | doprint "Test forced to stop after $stop_after_success seconds after success\n"; | ||
790 | last; | ||
791 | } | ||
792 | } | ||
793 | |||
794 | if ($full_line =~ /\[ backtrace testing \]/) { | ||
795 | $skip_call_trace = 1; | ||
796 | } | ||
797 | |||
798 | if ($full_line =~ /call trace:/i) { | ||
799 | if (!$skip_call_trace) { | ||
800 | $bug = 1; | ||
801 | $failure_start = time; | ||
802 | } | ||
803 | } | ||
804 | |||
805 | if ($bug && defined($stop_after_failure) && | ||
806 | $stop_after_failure >= 0) { | ||
807 | my $now = time; | ||
808 | if ($now - $failure_start >= $stop_after_failure) { | ||
809 | doprint "Test forced to stop after $stop_after_failure seconds after failure\n"; | ||
810 | last; | ||
811 | } | ||
812 | } | ||
813 | |||
814 | if ($full_line =~ /\[ end of backtrace testing \]/) { | ||
815 | $skip_call_trace = 0; | ||
816 | } | ||
817 | |||
818 | if ($full_line =~ /Kernel panic -/) { | ||
819 | $bug = 1; | ||
820 | } | ||
821 | |||
822 | if ($line =~ /\n/) { | ||
823 | $full_line = ""; | ||
824 | } | ||
825 | } | ||
826 | |||
827 | close(DMESG); | ||
828 | |||
829 | if ($bug) { | ||
830 | return 0 if ($in_bisect); | ||
831 | fail "failed - got a bug report" and return 0; | ||
832 | } | ||
833 | |||
834 | if (!$booted) { | ||
835 | return 0 if ($in_bisect); | ||
836 | fail "failed - never got a boot prompt." and return 0; | ||
837 | } | ||
838 | |||
839 | return 1; | ||
840 | } | ||
841 | |||
842 | sub install { | ||
843 | |||
844 | run_scp "$outputdir/$build_target", "$target_image" or | ||
845 | dodie "failed to copy image"; | ||
846 | |||
847 | my $install_mods = 0; | ||
848 | |||
849 | # should we process modules? | ||
850 | $install_mods = 0; | ||
851 | open(IN, "$output_config") or dodie("Can't read config file"); | ||
852 | while (<IN>) { | ||
853 | if (/CONFIG_MODULES(=y)?/) { | ||
854 | $install_mods = 1 if (defined($1)); | ||
855 | last; | ||
856 | } | ||
857 | } | ||
858 | close(IN); | ||
859 | |||
860 | if (!$install_mods) { | ||
861 | doprint "No modules needed\n"; | ||
862 | return; | ||
863 | } | ||
864 | |||
865 | run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or | ||
866 | dodie "Failed to install modules"; | ||
867 | |||
868 | my $modlib = "/lib/modules/$version"; | ||
869 | my $modtar = "ktest-mods.tar.bz2"; | ||
870 | |||
871 | run_ssh "rm -rf $modlib" or | ||
872 | dodie "failed to remove old mods: $modlib"; | ||
873 | |||
874 | # would be nice if scp -r did not follow symbolic links | ||
875 | run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or | ||
876 | dodie "making tarball"; | ||
877 | |||
878 | run_scp "$tmpdir/$modtar", "/tmp" or | ||
879 | dodie "failed to copy modules"; | ||
880 | |||
881 | unlink "$tmpdir/$modtar"; | ||
882 | |||
883 | run_ssh "'(cd / && tar xf /tmp/$modtar)'" or | ||
884 | dodie "failed to tar modules"; | ||
885 | |||
886 | run_ssh "rm -f /tmp/$modtar"; | ||
887 | |||
888 | return if (!defined($post_install)); | ||
889 | |||
890 | my $cp_post_install = $post_install; | ||
891 | $cp_post_install = s/\$KERNEL_VERSION/$version/g; | ||
892 | run_command "$cp_post_install" or | ||
893 | dodie "Failed to run post install"; | ||
894 | } | ||
895 | |||
896 | sub check_buildlog { | ||
897 | my ($patch) = @_; | ||
898 | |||
899 | my @files = `git show $patch | diffstat -l`; | ||
900 | |||
901 | open(IN, "git show $patch |") or | ||
902 | dodie "failed to show $patch"; | ||
903 | while (<IN>) { | ||
904 | if (m,^--- a/(.*),) { | ||
905 | chomp $1; | ||
906 | $files[$#files] = $1; | ||
907 | } | ||
908 | } | ||
909 | close(IN); | ||
910 | |||
911 | open(IN, $buildlog) or dodie "Can't open $buildlog"; | ||
912 | while (<IN>) { | ||
913 | if (/^\s*(.*?):.*(warning|error)/) { | ||
914 | my $err = $1; | ||
915 | foreach my $file (@files) { | ||
916 | my $fullpath = "$builddir/$file"; | ||
917 | if ($file eq $err || $fullpath eq $err) { | ||
918 | fail "$file built with warnings" and return 0; | ||
919 | } | ||
920 | } | ||
921 | } | ||
922 | } | ||
923 | close(IN); | ||
924 | |||
925 | return 1; | ||
926 | } | ||
927 | |||
928 | sub build { | ||
929 | my ($type) = @_; | ||
930 | my $defconfig = ""; | ||
931 | |||
932 | unlink $buildlog; | ||
933 | |||
934 | if ($type =~ /^useconfig:(.*)/) { | ||
935 | run_command "cp $1 $output_config" or | ||
936 | dodie "could not copy $1 to .config"; | ||
937 | |||
938 | $type = "oldconfig"; | ||
939 | } | ||
940 | |||
941 | # old config can ask questions | ||
942 | if ($type eq "oldconfig") { | ||
943 | $type = "oldnoconfig"; | ||
944 | |||
945 | # allow for empty configs | ||
946 | run_command "touch $output_config"; | ||
947 | |||
948 | run_command "mv $output_config $outputdir/config_temp" or | ||
949 | dodie "moving .config"; | ||
950 | |||
951 | if (!$noclean && !run_command "$make mrproper") { | ||
952 | dodie "make mrproper"; | ||
953 | } | ||
954 | |||
955 | run_command "mv $outputdir/config_temp $output_config" or | ||
956 | dodie "moving config_temp"; | ||
957 | |||
958 | } elsif (!$noclean) { | ||
959 | unlink "$output_config"; | ||
960 | run_command "$make mrproper" or | ||
961 | dodie "make mrproper"; | ||
962 | } | ||
963 | |||
964 | # add something to distinguish this build | ||
965 | open(OUT, "> $outputdir/localversion") or dodie("Can't make localversion file"); | ||
966 | print OUT "$localversion\n"; | ||
967 | close(OUT); | ||
968 | |||
969 | if (defined($minconfig)) { | ||
970 | $defconfig = "KCONFIG_ALLCONFIG=$minconfig"; | ||
971 | } | ||
972 | |||
973 | run_command "$defconfig $make $type" or | ||
974 | dodie "failed make config"; | ||
975 | |||
976 | $redirect = "$buildlog"; | ||
977 | if (!run_command "$make $build_options") { | ||
978 | undef $redirect; | ||
979 | # bisect may need this to pass | ||
980 | return 0 if ($in_bisect); | ||
981 | fail "failed build" and return 0; | ||
982 | } | ||
983 | undef $redirect; | ||
984 | |||
985 | return 1; | ||
986 | } | ||
987 | |||
988 | sub halt { | ||
989 | if (!run_ssh "halt" or defined($power_off)) { | ||
990 | if (defined($poweroff_after_halt)) { | ||
991 | sleep $poweroff_after_halt; | ||
992 | run_command "$power_off"; | ||
993 | } | ||
994 | } else { | ||
995 | # nope? the zap it! | ||
996 | run_command "$power_off"; | ||
997 | } | ||
998 | } | ||
999 | |||
1000 | sub success { | ||
1001 | my ($i) = @_; | ||
1002 | |||
1003 | $successes++; | ||
1004 | |||
1005 | doprint "\n\n*******************************************\n"; | ||
1006 | doprint "*******************************************\n"; | ||
1007 | doprint "KTEST RESULT: TEST $i SUCCESS!!!! **\n"; | ||
1008 | doprint "*******************************************\n"; | ||
1009 | doprint "*******************************************\n"; | ||
1010 | |||
1011 | if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) { | ||
1012 | doprint "Reboot and wait $sleep_time seconds\n"; | ||
1013 | reboot; | ||
1014 | start_monitor; | ||
1015 | wait_for_monitor $sleep_time; | ||
1016 | end_monitor; | ||
1017 | } | ||
1018 | } | ||
1019 | |||
1020 | sub get_version { | ||
1021 | # get the release name | ||
1022 | doprint "$make kernelrelease ... "; | ||
1023 | $version = `$make kernelrelease | tail -1`; | ||
1024 | chomp($version); | ||
1025 | doprint "$version\n"; | ||
1026 | } | ||
1027 | |||
1028 | sub child_run_test { | ||
1029 | my $failed = 0; | ||
1030 | |||
1031 | # child should have no power | ||
1032 | $reboot_on_error = 0; | ||
1033 | $poweroff_on_error = 0; | ||
1034 | $die_on_failure = 1; | ||
1035 | |||
1036 | run_command $run_test or $failed = 1; | ||
1037 | exit $failed; | ||
1038 | } | ||
1039 | |||
1040 | my $child_done; | ||
1041 | |||
1042 | sub child_finished { | ||
1043 | $child_done = 1; | ||
1044 | } | ||
1045 | |||
1046 | sub do_run_test { | ||
1047 | my $child_pid; | ||
1048 | my $child_exit; | ||
1049 | my $line; | ||
1050 | my $full_line; | ||
1051 | my $bug = 0; | ||
1052 | |||
1053 | wait_for_monitor 1; | ||
1054 | |||
1055 | doprint "run test $run_test\n"; | ||
1056 | |||
1057 | $child_done = 0; | ||
1058 | |||
1059 | $SIG{CHLD} = qw(child_finished); | ||
1060 | |||
1061 | $child_pid = fork; | ||
1062 | |||
1063 | child_run_test if (!$child_pid); | ||
1064 | |||
1065 | $full_line = ""; | ||
1066 | |||
1067 | do { | ||
1068 | $line = wait_for_input($monitor_fp, 1); | ||
1069 | if (defined($line)) { | ||
1070 | |||
1071 | # we are not guaranteed to get a full line | ||
1072 | $full_line .= $line; | ||
1073 | |||
1074 | if ($full_line =~ /call trace:/i) { | ||
1075 | $bug = 1; | ||
1076 | } | ||
1077 | |||
1078 | if ($full_line =~ /Kernel panic -/) { | ||
1079 | $bug = 1; | ||
1080 | } | ||
1081 | |||
1082 | if ($line =~ /\n/) { | ||
1083 | $full_line = ""; | ||
1084 | } | ||
1085 | } | ||
1086 | } while (!$child_done && !$bug); | ||
1087 | |||
1088 | if ($bug) { | ||
1089 | doprint "Detected kernel crash!\n"; | ||
1090 | # kill the child with extreme prejudice | ||
1091 | kill 9, $child_pid; | ||
1092 | } | ||
1093 | |||
1094 | waitpid $child_pid, 0; | ||
1095 | $child_exit = $?; | ||
1096 | |||
1097 | if ($bug || $child_exit) { | ||
1098 | return 0 if $in_bisect; | ||
1099 | fail "test failed" and return 0; | ||
1100 | } | ||
1101 | return 1; | ||
1102 | } | ||
1103 | |||
1104 | sub run_git_bisect { | ||
1105 | my ($command) = @_; | ||
1106 | |||
1107 | doprint "$command ... "; | ||
1108 | |||
1109 | my $output = `$command 2>&1`; | ||
1110 | my $ret = $?; | ||
1111 | |||
1112 | logit $output; | ||
1113 | |||
1114 | if ($ret) { | ||
1115 | doprint "FAILED\n"; | ||
1116 | dodie "Failed to git bisect"; | ||
1117 | } | ||
1118 | |||
1119 | doprint "SUCCESS\n"; | ||
1120 | if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { | ||
1121 | doprint "$1 [$2]\n"; | ||
1122 | } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { | ||
1123 | $bisect_bad = $1; | ||
1124 | doprint "Found bad commit... $1\n"; | ||
1125 | return 0; | ||
1126 | } else { | ||
1127 | # we already logged it, just print it now. | ||
1128 | print $output; | ||
1129 | } | ||
1130 | |||
1131 | return 1; | ||
1132 | } | ||
1133 | |||
1134 | # returns 1 on success, 0 on failure | ||
1135 | sub run_bisect_test { | ||
1136 | my ($type, $buildtype) = @_; | ||
1137 | |||
1138 | my $failed = 0; | ||
1139 | my $result; | ||
1140 | my $output; | ||
1141 | my $ret; | ||
1142 | |||
1143 | $in_bisect = 1; | ||
1144 | |||
1145 | build $buildtype or $failed = 1; | ||
1146 | |||
1147 | if ($type ne "build") { | ||
1148 | dodie "Failed on build" if $failed; | ||
1149 | |||
1150 | # Now boot the box | ||
1151 | get_grub_index; | ||
1152 | get_version; | ||
1153 | install; | ||
1154 | |||
1155 | start_monitor; | ||
1156 | monitor or $failed = 1; | ||
1157 | |||
1158 | if ($type ne "boot") { | ||
1159 | dodie "Failed on boot" if $failed; | ||
1160 | |||
1161 | do_run_test or $failed = 1; | ||
1162 | } | ||
1163 | end_monitor; | ||
1164 | } | ||
1165 | |||
1166 | if ($failed) { | ||
1167 | $result = 0; | ||
1168 | |||
1169 | # reboot the box to a good kernel | ||
1170 | if ($type ne "build") { | ||
1171 | doprint "Reboot and sleep $bisect_sleep_time seconds\n"; | ||
1172 | reboot; | ||
1173 | start_monitor; | ||
1174 | wait_for_monitor $bisect_sleep_time; | ||
1175 | end_monitor; | ||
1176 | } | ||
1177 | } else { | ||
1178 | $result = 1; | ||
1179 | } | ||
1180 | $in_bisect = 0; | ||
1181 | |||
1182 | return $result; | ||
1183 | } | ||
1184 | |||
1185 | sub run_bisect { | ||
1186 | my ($type) = @_; | ||
1187 | my $buildtype = "oldconfig"; | ||
1188 | |||
1189 | # We should have a minconfig to use? | ||
1190 | if (defined($minconfig)) { | ||
1191 | $buildtype = "useconfig:$minconfig"; | ||
1192 | } | ||
1193 | |||
1194 | my $ret = run_bisect_test $type, $buildtype; | ||
1195 | |||
1196 | |||
1197 | # Are we looking for where it worked, not failed? | ||
1198 | if ($reverse_bisect) { | ||
1199 | $ret = !$ret; | ||
1200 | } | ||
1201 | |||
1202 | if ($ret) { | ||
1203 | return "good"; | ||
1204 | } else { | ||
1205 | return "bad"; | ||
1206 | } | ||
1207 | } | ||
1208 | |||
1209 | sub bisect { | ||
1210 | my ($i) = @_; | ||
1211 | |||
1212 | my $result; | ||
1213 | |||
1214 | die "BISECT_GOOD[$i] not defined\n" if (!defined($opt{"BISECT_GOOD[$i]"})); | ||
1215 | die "BISECT_BAD[$i] not defined\n" if (!defined($opt{"BISECT_BAD[$i]"})); | ||
1216 | die "BISECT_TYPE[$i] not defined\n" if (!defined($opt{"BISECT_TYPE[$i]"})); | ||
1217 | |||
1218 | my $good = $opt{"BISECT_GOOD[$i]"}; | ||
1219 | my $bad = $opt{"BISECT_BAD[$i]"}; | ||
1220 | my $type = $opt{"BISECT_TYPE[$i]"}; | ||
1221 | my $start = $opt{"BISECT_START[$i]"}; | ||
1222 | my $replay = $opt{"BISECT_REPLAY[$i]"}; | ||
1223 | |||
1224 | # convert to true sha1's | ||
1225 | $good = get_sha1($good); | ||
1226 | $bad = get_sha1($bad); | ||
1227 | |||
1228 | if (defined($opt{"BISECT_REVERSE[$i]"}) && | ||
1229 | $opt{"BISECT_REVERSE[$i]"} == 1) { | ||
1230 | doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; | ||
1231 | $reverse_bisect = 1; | ||
1232 | } else { | ||
1233 | $reverse_bisect = 0; | ||
1234 | } | ||
1235 | |||
1236 | # Can't have a test without having a test to run | ||
1237 | if ($type eq "test" && !defined($run_test)) { | ||
1238 | $type = "boot"; | ||
1239 | } | ||
1240 | |||
1241 | my $check = $opt{"BISECT_CHECK[$i]"}; | ||
1242 | if (defined($check) && $check ne "0") { | ||
1243 | |||
1244 | # get current HEAD | ||
1245 | my $head = get_sha1("HEAD"); | ||
1246 | |||
1247 | if ($check ne "good") { | ||
1248 | doprint "TESTING BISECT BAD [$bad]\n"; | ||
1249 | run_command "git checkout $bad" or | ||
1250 | die "Failed to checkout $bad"; | ||
1251 | |||
1252 | $result = run_bisect $type; | ||
1253 | |||
1254 | if ($result ne "bad") { | ||
1255 | fail "Tested BISECT_BAD [$bad] and it succeeded" and return 0; | ||
1256 | } | ||
1257 | } | ||
1258 | |||
1259 | if ($check ne "bad") { | ||
1260 | doprint "TESTING BISECT GOOD [$good]\n"; | ||
1261 | run_command "git checkout $good" or | ||
1262 | die "Failed to checkout $good"; | ||
1263 | |||
1264 | $result = run_bisect $type; | ||
1265 | |||
1266 | if ($result ne "good") { | ||
1267 | fail "Tested BISECT_GOOD [$good] and it failed" and return 0; | ||
1268 | } | ||
1269 | } | ||
1270 | |||
1271 | # checkout where we started | ||
1272 | run_command "git checkout $head" or | ||
1273 | die "Failed to checkout $head"; | ||
1274 | } | ||
1275 | |||
1276 | run_command "git bisect start" or | ||
1277 | dodie "could not start bisect"; | ||
1278 | |||
1279 | run_command "git bisect good $good" or | ||
1280 | dodie "could not set bisect good to $good"; | ||
1281 | |||
1282 | run_git_bisect "git bisect bad $bad" or | ||
1283 | dodie "could not set bisect bad to $bad"; | ||
1284 | |||
1285 | if (defined($replay)) { | ||
1286 | run_command "git bisect replay $replay" or | ||
1287 | dodie "failed to run replay"; | ||
1288 | } | ||
1289 | |||
1290 | if (defined($start)) { | ||
1291 | run_command "git checkout $start" or | ||
1292 | dodie "failed to checkout $start"; | ||
1293 | } | ||
1294 | |||
1295 | my $test; | ||
1296 | do { | ||
1297 | $result = run_bisect $type; | ||
1298 | $test = run_git_bisect "git bisect $result"; | ||
1299 | } while ($test); | ||
1300 | |||
1301 | run_command "git bisect log" or | ||
1302 | dodie "could not capture git bisect log"; | ||
1303 | |||
1304 | run_command "git bisect reset" or | ||
1305 | dodie "could not reset git bisect"; | ||
1306 | |||
1307 | doprint "Bad commit was [$bisect_bad]\n"; | ||
1308 | |||
1309 | success $i; | ||
1310 | } | ||
1311 | |||
1312 | my %config_ignore; | ||
1313 | my %config_set; | ||
1314 | |||
1315 | my %config_list; | ||
1316 | my %null_config; | ||
1317 | |||
1318 | my %dependency; | ||
1319 | |||
1320 | sub process_config_ignore { | ||
1321 | my ($config) = @_; | ||
1322 | |||
1323 | open (IN, $config) | ||
1324 | or dodie "Failed to read $config"; | ||
1325 | |||
1326 | while (<IN>) { | ||
1327 | if (/^(.*?(CONFIG\S*)(=.*| is not set))/) { | ||
1328 | $config_ignore{$2} = $1; | ||
1329 | } | ||
1330 | } | ||
1331 | |||
1332 | close(IN); | ||
1333 | } | ||
1334 | |||
1335 | sub read_current_config { | ||
1336 | my ($config_ref) = @_; | ||
1337 | |||
1338 | %{$config_ref} = (); | ||
1339 | undef %{$config_ref}; | ||
1340 | |||
1341 | my @key = keys %{$config_ref}; | ||
1342 | if ($#key >= 0) { | ||
1343 | print "did not delete!\n"; | ||
1344 | exit; | ||
1345 | } | ||
1346 | open (IN, "$output_config"); | ||
1347 | |||
1348 | while (<IN>) { | ||
1349 | if (/^(CONFIG\S+)=(.*)/) { | ||
1350 | ${$config_ref}{$1} = $2; | ||
1351 | } | ||
1352 | } | ||
1353 | close(IN); | ||
1354 | } | ||
1355 | |||
1356 | sub get_dependencies { | ||
1357 | my ($config) = @_; | ||
1358 | |||
1359 | my $arr = $dependency{$config}; | ||
1360 | if (!defined($arr)) { | ||
1361 | return (); | ||
1362 | } | ||
1363 | |||
1364 | my @deps = @{$arr}; | ||
1365 | |||
1366 | foreach my $dep (@{$arr}) { | ||
1367 | print "ADD DEP $dep\n"; | ||
1368 | @deps = (@deps, get_dependencies $dep); | ||
1369 | } | ||
1370 | |||
1371 | return @deps; | ||
1372 | } | ||
1373 | |||
1374 | sub create_config { | ||
1375 | my @configs = @_; | ||
1376 | |||
1377 | open(OUT, ">$output_config") or dodie "Can not write to $output_config"; | ||
1378 | |||
1379 | foreach my $config (@configs) { | ||
1380 | print OUT "$config_set{$config}\n"; | ||
1381 | my @deps = get_dependencies $config; | ||
1382 | foreach my $dep (@deps) { | ||
1383 | print OUT "$config_set{$dep}\n"; | ||
1384 | } | ||
1385 | } | ||
1386 | |||
1387 | foreach my $config (keys %config_ignore) { | ||
1388 | print OUT "$config_ignore{$config}\n"; | ||
1389 | } | ||
1390 | close(OUT); | ||
1391 | |||
1392 | # exit; | ||
1393 | run_command "$make oldnoconfig" or | ||
1394 | dodie "failed make config oldconfig"; | ||
1395 | |||
1396 | } | ||
1397 | |||
1398 | sub compare_configs { | ||
1399 | my (%a, %b) = @_; | ||
1400 | |||
1401 | foreach my $item (keys %a) { | ||
1402 | if (!defined($b{$item})) { | ||
1403 | print "diff $item\n"; | ||
1404 | return 1; | ||
1405 | } | ||
1406 | delete $b{$item}; | ||
1407 | } | ||
1408 | |||
1409 | my @keys = keys %b; | ||
1410 | if ($#keys) { | ||
1411 | print "diff2 $keys[0]\n"; | ||
1412 | } | ||
1413 | return -1 if ($#keys >= 0); | ||
1414 | |||
1415 | return 0; | ||
1416 | } | ||
1417 | |||
1418 | sub run_config_bisect_test { | ||
1419 | my ($type) = @_; | ||
1420 | |||
1421 | return run_bisect_test $type, "oldconfig"; | ||
1422 | } | ||
1423 | |||
1424 | sub process_passed { | ||
1425 | my (%configs) = @_; | ||
1426 | |||
1427 | doprint "These configs had no failure: (Enabling them for further compiles)\n"; | ||
1428 | # Passed! All these configs are part of a good compile. | ||
1429 | # Add them to the min options. | ||
1430 | foreach my $config (keys %configs) { | ||
1431 | if (defined($config_list{$config})) { | ||
1432 | doprint " removing $config\n"; | ||
1433 | $config_ignore{$config} = $config_list{$config}; | ||
1434 | delete $config_list{$config}; | ||
1435 | } | ||
1436 | } | ||
1437 | doprint "config copied to $outputdir/config_good\n"; | ||
1438 | run_command "cp -f $output_config $outputdir/config_good"; | ||
1439 | } | ||
1440 | |||
1441 | sub process_failed { | ||
1442 | my ($config) = @_; | ||
1443 | |||
1444 | doprint "\n\n***************************************\n"; | ||
1445 | doprint "Found bad config: $config\n"; | ||
1446 | doprint "***************************************\n\n"; | ||
1447 | } | ||
1448 | |||
1449 | sub run_config_bisect { | ||
1450 | |||
1451 | my @start_list = keys %config_list; | ||
1452 | |||
1453 | if ($#start_list < 0) { | ||
1454 | doprint "No more configs to test!!!\n"; | ||
1455 | return -1; | ||
1456 | } | ||
1457 | |||
1458 | doprint "***** RUN TEST ***\n"; | ||
1459 | my $type = $opt{"CONFIG_BISECT_TYPE[$iteration]"}; | ||
1460 | my $ret; | ||
1461 | my %current_config; | ||
1462 | |||
1463 | my $count = $#start_list + 1; | ||
1464 | doprint " $count configs to test\n"; | ||
1465 | |||
1466 | my $half = int($#start_list / 2); | ||
1467 | |||
1468 | do { | ||
1469 | my @tophalf = @start_list[0 .. $half]; | ||
1470 | |||
1471 | create_config @tophalf; | ||
1472 | read_current_config \%current_config; | ||
1473 | |||
1474 | $count = $#tophalf + 1; | ||
1475 | doprint "Testing $count configs\n"; | ||
1476 | my $found = 0; | ||
1477 | # make sure we test something | ||
1478 | foreach my $config (@tophalf) { | ||
1479 | if (defined($current_config{$config})) { | ||
1480 | logit " $config\n"; | ||
1481 | $found = 1; | ||
1482 | } | ||
1483 | } | ||
1484 | if (!$found) { | ||
1485 | # try the other half | ||
1486 | doprint "Top half produced no set configs, trying bottom half\n"; | ||
1487 | @tophalf = @start_list[$half .. $#start_list]; | ||
1488 | create_config @tophalf; | ||
1489 | read_current_config \%current_config; | ||
1490 | foreach my $config (@tophalf) { | ||
1491 | if (defined($current_config{$config})) { | ||
1492 | logit " $config\n"; | ||
1493 | $found = 1; | ||
1494 | } | ||
1495 | } | ||
1496 | if (!$found) { | ||
1497 | doprint "Failed: Can't make new config with current configs\n"; | ||
1498 | foreach my $config (@start_list) { | ||
1499 | doprint " CONFIG: $config\n"; | ||
1500 | } | ||
1501 | return -1; | ||
1502 | } | ||
1503 | $count = $#tophalf + 1; | ||
1504 | doprint "Testing $count configs\n"; | ||
1505 | } | ||
1506 | |||
1507 | $ret = run_config_bisect_test $type; | ||
1508 | |||
1509 | if ($ret) { | ||
1510 | process_passed %current_config; | ||
1511 | return 0; | ||
1512 | } | ||
1513 | |||
1514 | doprint "This config had a failure.\n"; | ||
1515 | doprint "Removing these configs that were not set in this config:\n"; | ||
1516 | doprint "config copied to $outputdir/config_bad\n"; | ||
1517 | run_command "cp -f $output_config $outputdir/config_bad"; | ||
1518 | |||
1519 | # A config exists in this group that was bad. | ||
1520 | foreach my $config (keys %config_list) { | ||
1521 | if (!defined($current_config{$config})) { | ||
1522 | doprint " removing $config\n"; | ||
1523 | delete $config_list{$config}; | ||
1524 | } | ||
1525 | } | ||
1526 | |||
1527 | @start_list = @tophalf; | ||
1528 | |||
1529 | if ($#start_list == 0) { | ||
1530 | process_failed $start_list[0]; | ||
1531 | return 1; | ||
1532 | } | ||
1533 | |||
1534 | # remove half the configs we are looking at and see if | ||
1535 | # they are good. | ||
1536 | $half = int($#start_list / 2); | ||
1537 | } while ($half > 0); | ||
1538 | |||
1539 | # we found a single config, try it again | ||
1540 | my @tophalf = @start_list[0 .. 0]; | ||
1541 | |||
1542 | $ret = run_config_bisect_test $type; | ||
1543 | if ($ret) { | ||
1544 | process_passed %current_config; | ||
1545 | return 0; | ||
1546 | } | ||
1547 | |||
1548 | process_failed $start_list[0]; | ||
1549 | return 1; | ||
1550 | } | ||
1551 | |||
1552 | sub config_bisect { | ||
1553 | my ($i) = @_; | ||
1554 | |||
1555 | my $start_config = $opt{"CONFIG_BISECT[$i]"}; | ||
1556 | |||
1557 | my $tmpconfig = "$tmpdir/use_config"; | ||
1558 | |||
1559 | # Make the file with the bad config and the min config | ||
1560 | if (defined($minconfig)) { | ||
1561 | # read the min config for things to ignore | ||
1562 | run_command "cp $minconfig $tmpconfig" or | ||
1563 | dodie "failed to copy $minconfig to $tmpconfig"; | ||
1564 | } else { | ||
1565 | unlink $tmpconfig; | ||
1566 | } | ||
1567 | |||
1568 | # Add other configs | ||
1569 | if (defined($addconfig)) { | ||
1570 | run_command "cat $addconfig >> $tmpconfig" or | ||
1571 | dodie "failed to append $addconfig"; | ||
1572 | } | ||
1573 | |||
1574 | my $defconfig = ""; | ||
1575 | if (-f $tmpconfig) { | ||
1576 | $defconfig = "KCONFIG_ALLCONFIG=$tmpconfig"; | ||
1577 | process_config_ignore $tmpconfig; | ||
1578 | } | ||
1579 | |||
1580 | # now process the start config | ||
1581 | run_command "cp $start_config $output_config" or | ||
1582 | dodie "failed to copy $start_config to $output_config"; | ||
1583 | |||
1584 | # read directly what we want to check | ||
1585 | my %config_check; | ||
1586 | open (IN, $output_config) | ||
1587 | or dodie "faied to open $output_config"; | ||
1588 | |||
1589 | while (<IN>) { | ||
1590 | if (/^((CONFIG\S*)=.*)/) { | ||
1591 | $config_check{$2} = $1; | ||
1592 | } | ||
1593 | } | ||
1594 | close(IN); | ||
1595 | |||
1596 | # Now run oldconfig with the minconfig (and addconfigs) | ||
1597 | run_command "$defconfig $make oldnoconfig" or | ||
1598 | dodie "failed make config oldconfig"; | ||
1599 | |||
1600 | # check to see what we lost (or gained) | ||
1601 | open (IN, $output_config) | ||
1602 | or dodie "Failed to read $start_config"; | ||
1603 | |||
1604 | my %removed_configs; | ||
1605 | my %added_configs; | ||
1606 | |||
1607 | while (<IN>) { | ||
1608 | if (/^((CONFIG\S*)=.*)/) { | ||
1609 | # save off all options | ||
1610 | $config_set{$2} = $1; | ||
1611 | if (defined($config_check{$2})) { | ||
1612 | if (defined($config_ignore{$2})) { | ||
1613 | $removed_configs{$2} = $1; | ||
1614 | } else { | ||
1615 | $config_list{$2} = $1; | ||
1616 | } | ||
1617 | } elsif (!defined($config_ignore{$2})) { | ||
1618 | $added_configs{$2} = $1; | ||
1619 | $config_list{$2} = $1; | ||
1620 | } | ||
1621 | } | ||
1622 | } | ||
1623 | close(IN); | ||
1624 | |||
1625 | my @confs = keys %removed_configs; | ||
1626 | if ($#confs >= 0) { | ||
1627 | doprint "Configs overridden by default configs and removed from check:\n"; | ||
1628 | foreach my $config (@confs) { | ||
1629 | doprint " $config\n"; | ||
1630 | } | ||
1631 | } | ||
1632 | @confs = keys %added_configs; | ||
1633 | if ($#confs >= 0) { | ||
1634 | doprint "Configs appearing in make oldconfig and added:\n"; | ||
1635 | foreach my $config (@confs) { | ||
1636 | doprint " $config\n"; | ||
1637 | } | ||
1638 | } | ||
1639 | |||
1640 | my %config_test; | ||
1641 | my $once = 0; | ||
1642 | |||
1643 | # Sometimes kconfig does weird things. We must make sure | ||
1644 | # that the config we autocreate has everything we need | ||
1645 | # to test, otherwise we may miss testing configs, or | ||
1646 | # may not be able to create a new config. | ||
1647 | # Here we create a config with everything set. | ||
1648 | create_config (keys %config_list); | ||
1649 | read_current_config \%config_test; | ||
1650 | foreach my $config (keys %config_list) { | ||
1651 | if (!defined($config_test{$config})) { | ||
1652 | if (!$once) { | ||
1653 | $once = 1; | ||
1654 | doprint "Configs not produced by kconfig (will not be checked):\n"; | ||
1655 | } | ||
1656 | doprint " $config\n"; | ||
1657 | delete $config_list{$config}; | ||
1658 | } | ||
1659 | } | ||
1660 | my $ret; | ||
1661 | do { | ||
1662 | $ret = run_config_bisect; | ||
1663 | } while (!$ret); | ||
1664 | |||
1665 | return $ret if ($ret < 0); | ||
1666 | |||
1667 | success $i; | ||
1668 | } | ||
1669 | |||
1670 | sub patchcheck { | ||
1671 | my ($i) = @_; | ||
1672 | |||
1673 | die "PATCHCHECK_START[$i] not defined\n" | ||
1674 | if (!defined($opt{"PATCHCHECK_START[$i]"})); | ||
1675 | die "PATCHCHECK_TYPE[$i] not defined\n" | ||
1676 | if (!defined($opt{"PATCHCHECK_TYPE[$i]"})); | ||
1677 | |||
1678 | my $start = $opt{"PATCHCHECK_START[$i]"}; | ||
1679 | |||
1680 | my $end = "HEAD"; | ||
1681 | if (defined($opt{"PATCHCHECK_END[$i]"})) { | ||
1682 | $end = $opt{"PATCHCHECK_END[$i]"}; | ||
1683 | } | ||
1684 | |||
1685 | # Get the true sha1's since we can use things like HEAD~3 | ||
1686 | $start = get_sha1($start); | ||
1687 | $end = get_sha1($end); | ||
1688 | |||
1689 | my $type = $opt{"PATCHCHECK_TYPE[$i]"}; | ||
1690 | |||
1691 | # Can't have a test without having a test to run | ||
1692 | if ($type eq "test" && !defined($run_test)) { | ||
1693 | $type = "boot"; | ||
1694 | } | ||
1695 | |||
1696 | open (IN, "git log --pretty=oneline $end|") or | ||
1697 | dodie "could not get git list"; | ||
1698 | |||
1699 | my @list; | ||
1700 | |||
1701 | while (<IN>) { | ||
1702 | chomp; | ||
1703 | $list[$#list+1] = $_; | ||
1704 | last if (/^$start/); | ||
1705 | } | ||
1706 | close(IN); | ||
1707 | |||
1708 | if ($list[$#list] !~ /^$start/) { | ||
1709 | fail "SHA1 $start not found"; | ||
1710 | } | ||
1711 | |||
1712 | # go backwards in the list | ||
1713 | @list = reverse @list; | ||
1714 | |||
1715 | my $save_clean = $noclean; | ||
1716 | |||
1717 | $in_patchcheck = 1; | ||
1718 | foreach my $item (@list) { | ||
1719 | my $sha1 = $item; | ||
1720 | $sha1 =~ s/^([[:xdigit:]]+).*/$1/; | ||
1721 | |||
1722 | doprint "\nProcessing commit $item\n\n"; | ||
1723 | |||
1724 | run_command "git checkout $sha1" or | ||
1725 | die "Failed to checkout $sha1"; | ||
1726 | |||
1727 | # only clean on the first and last patch | ||
1728 | if ($item eq $list[0] || | ||
1729 | $item eq $list[$#list]) { | ||
1730 | $noclean = $save_clean; | ||
1731 | } else { | ||
1732 | $noclean = 1; | ||
1733 | } | ||
1734 | |||
1735 | if (defined($minconfig)) { | ||
1736 | build "useconfig:$minconfig" or return 0; | ||
1737 | } else { | ||
1738 | # ?? no config to use? | ||
1739 | build "oldconfig" or return 0; | ||
1740 | } | ||
1741 | |||
1742 | check_buildlog $sha1 or return 0; | ||
1743 | |||
1744 | next if ($type eq "build"); | ||
1745 | |||
1746 | get_grub_index; | ||
1747 | get_version; | ||
1748 | install; | ||
1749 | |||
1750 | my $failed = 0; | ||
1751 | |||
1752 | start_monitor; | ||
1753 | monitor or $failed = 1; | ||
1754 | |||
1755 | if (!$failed && $type ne "boot"){ | ||
1756 | do_run_test or $failed = 1; | ||
1757 | } | ||
1758 | end_monitor; | ||
1759 | return 0 if ($failed); | ||
1760 | |||
1761 | } | ||
1762 | $in_patchcheck = 0; | ||
1763 | success $i; | ||
1764 | |||
1765 | return 1; | ||
1766 | } | ||
1767 | |||
1768 | $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; | ||
1769 | |||
1770 | if ($#ARGV == 0) { | ||
1771 | $ktest_config = $ARGV[0]; | ||
1772 | if (! -f $ktest_config) { | ||
1773 | print "$ktest_config does not exist.\n"; | ||
1774 | my $ans; | ||
1775 | for (;;) { | ||
1776 | print "Create it? [Y/n] "; | ||
1777 | $ans = <STDIN>; | ||
1778 | chomp $ans; | ||
1779 | if ($ans =~ /^\s*$/) { | ||
1780 | $ans = "y"; | ||
1781 | } | ||
1782 | last if ($ans =~ /^y$/i || $ans =~ /^n$/i); | ||
1783 | print "Please answer either 'y' or 'n'.\n"; | ||
1784 | } | ||
1785 | if ($ans !~ /^y$/i) { | ||
1786 | exit 0; | ||
1787 | } | ||
1788 | } | ||
1789 | } else { | ||
1790 | $ktest_config = "ktest.conf"; | ||
1791 | } | ||
1792 | |||
1793 | if (! -f $ktest_config) { | ||
1794 | open(OUT, ">$ktest_config") or die "Can not create $ktest_config"; | ||
1795 | print OUT << "EOF" | ||
1796 | # Generated by ktest.pl | ||
1797 | # | ||
1798 | # Define each test with TEST_START | ||
1799 | # The config options below it will override the defaults | ||
1800 | TEST_START | ||
1801 | |||
1802 | DEFAULTS | ||
1803 | EOF | ||
1804 | ; | ||
1805 | close(OUT); | ||
1806 | } | ||
1807 | read_config $ktest_config; | ||
1808 | |||
1809 | # Append any configs entered in manually to the config file. | ||
1810 | my @new_configs = keys %entered_configs; | ||
1811 | if ($#new_configs >= 0) { | ||
1812 | print "\nAppending entered in configs to $ktest_config\n"; | ||
1813 | open(OUT, ">>$ktest_config") or die "Can not append to $ktest_config"; | ||
1814 | foreach my $config (@new_configs) { | ||
1815 | print OUT "$config = $entered_configs{$config}\n"; | ||
1816 | $opt{$config} = $entered_configs{$config}; | ||
1817 | } | ||
1818 | } | ||
1819 | |||
1820 | if ($opt{"CLEAR_LOG"} && defined($opt{"LOG_FILE"})) { | ||
1821 | unlink $opt{"LOG_FILE"}; | ||
1822 | } | ||
1823 | |||
1824 | doprint "\n\nSTARTING AUTOMATED TESTS\n\n"; | ||
1825 | |||
1826 | for (my $i = 0, my $repeat = 1; $i <= $opt{"NUM_TESTS"}; $i += $repeat) { | ||
1827 | |||
1828 | if (!$i) { | ||
1829 | doprint "DEFAULT OPTIONS:\n"; | ||
1830 | } else { | ||
1831 | doprint "\nTEST $i OPTIONS"; | ||
1832 | if (defined($repeat_tests{$i})) { | ||
1833 | $repeat = $repeat_tests{$i}; | ||
1834 | doprint " ITERATE $repeat"; | ||
1835 | } | ||
1836 | doprint "\n"; | ||
1837 | } | ||
1838 | |||
1839 | foreach my $option (sort keys %opt) { | ||
1840 | |||
1841 | if ($option =~ /\[(\d+)\]$/) { | ||
1842 | next if ($i != $1); | ||
1843 | } else { | ||
1844 | next if ($i); | ||
1845 | } | ||
1846 | |||
1847 | doprint "$option = $opt{$option}\n"; | ||
1848 | } | ||
1849 | } | ||
1850 | |||
1851 | sub set_test_option { | ||
1852 | my ($name, $i) = @_; | ||
1853 | |||
1854 | my $option = "$name\[$i\]"; | ||
1855 | |||
1856 | if (defined($opt{$option})) { | ||
1857 | return $opt{$option}; | ||
1858 | } | ||
1859 | |||
1860 | foreach my $test (keys %repeat_tests) { | ||
1861 | if ($i >= $test && | ||
1862 | $i < $test + $repeat_tests{$test}) { | ||
1863 | $option = "$name\[$test\]"; | ||
1864 | if (defined($opt{$option})) { | ||
1865 | return $opt{$option}; | ||
1866 | } | ||
1867 | } | ||
1868 | } | ||
1869 | |||
1870 | if (defined($opt{$name})) { | ||
1871 | return $opt{$name}; | ||
1872 | } | ||
1873 | |||
1874 | return undef; | ||
1875 | } | ||
1876 | |||
1877 | # First we need to do is the builds | ||
1878 | for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | ||
1879 | |||
1880 | $iteration = $i; | ||
1881 | |||
1882 | my $makecmd = set_test_option("MAKE_CMD", $i); | ||
1883 | |||
1884 | $machine = set_test_option("MACHINE", $i); | ||
1885 | $ssh_user = set_test_option("SSH_USER", $i); | ||
1886 | $tmpdir = set_test_option("TMP_DIR", $i); | ||
1887 | $outputdir = set_test_option("OUTPUT_DIR", $i); | ||
1888 | $builddir = set_test_option("BUILD_DIR", $i); | ||
1889 | $test_type = set_test_option("TEST_TYPE", $i); | ||
1890 | $build_type = set_test_option("BUILD_TYPE", $i); | ||
1891 | $build_options = set_test_option("BUILD_OPTIONS", $i); | ||
1892 | $power_cycle = set_test_option("POWER_CYCLE", $i); | ||
1893 | $reboot = set_test_option("REBOOT", $i); | ||
1894 | $noclean = set_test_option("BUILD_NOCLEAN", $i); | ||
1895 | $minconfig = set_test_option("MIN_CONFIG", $i); | ||
1896 | $run_test = set_test_option("TEST", $i); | ||
1897 | $addconfig = set_test_option("ADD_CONFIG", $i); | ||
1898 | $reboot_type = set_test_option("REBOOT_TYPE", $i); | ||
1899 | $grub_menu = set_test_option("GRUB_MENU", $i); | ||
1900 | $post_install = set_test_option("POST_INSTALL", $i); | ||
1901 | $reboot_script = set_test_option("REBOOT_SCRIPT", $i); | ||
1902 | $reboot_on_error = set_test_option("REBOOT_ON_ERROR", $i); | ||
1903 | $poweroff_on_error = set_test_option("POWEROFF_ON_ERROR", $i); | ||
1904 | $die_on_failure = set_test_option("DIE_ON_FAILURE", $i); | ||
1905 | $power_off = set_test_option("POWER_OFF", $i); | ||
1906 | $powercycle_after_reboot = set_test_option("POWERCYCLE_AFTER_REBOOT", $i); | ||
1907 | $poweroff_after_halt = set_test_option("POWEROFF_AFTER_HALT", $i); | ||
1908 | $sleep_time = set_test_option("SLEEP_TIME", $i); | ||
1909 | $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i); | ||
1910 | $store_failures = set_test_option("STORE_FAILURES", $i); | ||
1911 | $timeout = set_test_option("TIMEOUT", $i); | ||
1912 | $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i); | ||
1913 | $console = set_test_option("CONSOLE", $i); | ||
1914 | $success_line = set_test_option("SUCCESS_LINE", $i); | ||
1915 | $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i); | ||
1916 | $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i); | ||
1917 | $build_target = set_test_option("BUILD_TARGET", $i); | ||
1918 | $ssh_exec = set_test_option("SSH_EXEC", $i); | ||
1919 | $scp_to_target = set_test_option("SCP_TO_TARGET", $i); | ||
1920 | $target_image = set_test_option("TARGET_IMAGE", $i); | ||
1921 | $localversion = set_test_option("LOCALVERSION", $i); | ||
1922 | |||
1923 | chdir $builddir || die "can't change directory to $builddir"; | ||
1924 | |||
1925 | if (!-d $tmpdir) { | ||
1926 | mkpath($tmpdir) or | ||
1927 | die "can't create $tmpdir"; | ||
1928 | } | ||
1929 | |||
1930 | $ENV{"SSH_USER"} = $ssh_user; | ||
1931 | $ENV{"MACHINE"} = $machine; | ||
1932 | |||
1933 | $target = "$ssh_user\@$machine"; | ||
1934 | |||
1935 | $buildlog = "$tmpdir/buildlog-$machine"; | ||
1936 | $dmesg = "$tmpdir/dmesg-$machine"; | ||
1937 | $make = "$makecmd O=$outputdir"; | ||
1938 | $output_config = "$outputdir/.config"; | ||
1939 | |||
1940 | if ($reboot_type eq "grub") { | ||
1941 | dodie "GRUB_MENU not defined" if (!defined($grub_menu)); | ||
1942 | } elsif (!defined($reboot_script)) { | ||
1943 | dodie "REBOOT_SCRIPT not defined" | ||
1944 | } | ||
1945 | |||
1946 | my $run_type = $build_type; | ||
1947 | if ($test_type eq "patchcheck") { | ||
1948 | $run_type = $opt{"PATCHCHECK_TYPE[$i]"}; | ||
1949 | } elsif ($test_type eq "bisect") { | ||
1950 | $run_type = $opt{"BISECT_TYPE[$i]"}; | ||
1951 | } elsif ($test_type eq "config_bisect") { | ||
1952 | $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"}; | ||
1953 | } | ||
1954 | |||
1955 | # mistake in config file? | ||
1956 | if (!defined($run_type)) { | ||
1957 | $run_type = "ERROR"; | ||
1958 | } | ||
1959 | |||
1960 | doprint "\n\n"; | ||
1961 | doprint "RUNNING TEST $i of $opt{NUM_TESTS} with option $test_type $run_type\n\n"; | ||
1962 | |||
1963 | unlink $dmesg; | ||
1964 | unlink $buildlog; | ||
1965 | |||
1966 | if (!defined($minconfig)) { | ||
1967 | $minconfig = $addconfig; | ||
1968 | |||
1969 | } elsif (defined($addconfig)) { | ||
1970 | run_command "cat $addconfig $minconfig > $tmpdir/add_config" or | ||
1971 | dodie "Failed to create temp config"; | ||
1972 | $minconfig = "$tmpdir/add_config"; | ||
1973 | } | ||
1974 | |||
1975 | my $checkout = $opt{"CHECKOUT[$i]"}; | ||
1976 | if (defined($checkout)) { | ||
1977 | run_command "git checkout $checkout" or | ||
1978 | die "failed to checkout $checkout"; | ||
1979 | } | ||
1980 | |||
1981 | if ($test_type eq "bisect") { | ||
1982 | bisect $i; | ||
1983 | next; | ||
1984 | } elsif ($test_type eq "config_bisect") { | ||
1985 | config_bisect $i; | ||
1986 | next; | ||
1987 | } elsif ($test_type eq "patchcheck") { | ||
1988 | patchcheck $i; | ||
1989 | next; | ||
1990 | } | ||
1991 | |||
1992 | if ($build_type ne "nobuild") { | ||
1993 | build $build_type or next; | ||
1994 | } | ||
1995 | |||
1996 | if ($test_type ne "build") { | ||
1997 | get_grub_index; | ||
1998 | get_version; | ||
1999 | install; | ||
2000 | |||
2001 | my $failed = 0; | ||
2002 | start_monitor; | ||
2003 | monitor or $failed = 1;; | ||
2004 | |||
2005 | if (!$failed && $test_type ne "boot" && defined($run_test)) { | ||
2006 | do_run_test or $failed = 1; | ||
2007 | } | ||
2008 | end_monitor; | ||
2009 | next if ($failed); | ||
2010 | } | ||
2011 | |||
2012 | success $i; | ||
2013 | } | ||
2014 | |||
2015 | if ($opt{"POWEROFF_ON_SUCCESS"}) { | ||
2016 | halt; | ||
2017 | } elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) { | ||
2018 | reboot; | ||
2019 | } | ||
2020 | |||
2021 | doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; | ||
2022 | |||
2023 | exit 0; | ||
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf new file mode 100644 index 000000000000..3408c594b2de --- /dev/null +++ b/tools/testing/ktest/sample.conf | |||
@@ -0,0 +1,622 @@ | |||
1 | # | ||
2 | # Config file for ktest.pl | ||
3 | # | ||
4 | # Note, all paths must be absolute | ||
5 | # | ||
6 | |||
7 | # Options set in the beginning of the file are considered to be | ||
8 | # default options. These options can be overriden by test specific | ||
9 | # options, with the following exceptions: | ||
10 | # | ||
11 | # LOG_FILE | ||
12 | # CLEAR_LOG | ||
13 | # POWEROFF_ON_SUCCESS | ||
14 | # REBOOT_ON_SUCCESS | ||
15 | # | ||
16 | # Test specific options are set after the label: | ||
17 | # | ||
18 | # TEST_START | ||
19 | # | ||
20 | # The options after a TEST_START label are specific to that test. | ||
21 | # Each TEST_START label will set up a new test. If you want to | ||
22 | # perform a test more than once, you can add the ITERATE label | ||
23 | # to it followed by the number of times you want that test | ||
24 | # to iterate. If the ITERATE is left off, the test will only | ||
25 | # be performed once. | ||
26 | # | ||
27 | # TEST_START ITERATE 10 | ||
28 | # | ||
29 | # You can skip a test by adding SKIP (before or after the ITERATE | ||
30 | # and number) | ||
31 | # | ||
32 | # TEST_START SKIP | ||
33 | # | ||
34 | # TEST_START SKIP ITERATE 10 | ||
35 | # | ||
36 | # TEST_START ITERATE 10 SKIP | ||
37 | # | ||
38 | # The SKIP label causes the options and the test itself to be ignored. | ||
39 | # This is useful to set up several different tests in one config file, and | ||
40 | # only enabling the ones you want to use for a current test run. | ||
41 | # | ||
42 | # You can add default options anywhere in the file as well | ||
43 | # with the DEFAULTS tag. This allows you to have default options | ||
44 | # after the test options to keep the test options at the top | ||
45 | # of the file. You can even place the DEFAULTS tag between | ||
46 | # test cases (but not in the middle of a single test case) | ||
47 | # | ||
48 | # TEST_START | ||
49 | # MIN_CONFIG = /home/test/config-test1 | ||
50 | # | ||
51 | # DEFAULTS | ||
52 | # MIN_CONFIG = /home/test/config-default | ||
53 | # | ||
54 | # TEST_START ITERATE 10 | ||
55 | # | ||
56 | # The above will run the first test with MIN_CONFIG set to | ||
57 | # /home/test/config-test-1. Then 10 tests will be executed | ||
58 | # with MIN_CONFIG with /home/test/config-default. | ||
59 | # | ||
60 | # You can also disable defaults with the SKIP option | ||
61 | # | ||
62 | # DEFAULTS SKIP | ||
63 | # MIN_CONFIG = /home/test/config-use-sometimes | ||
64 | # | ||
65 | # DEFAULTS | ||
66 | # MIN_CONFIG = /home/test/config-most-times | ||
67 | # | ||
68 | # The above will ignore the first MIN_CONFIG. If you want to | ||
69 | # use the first MIN_CONFIG, remove the SKIP from the first | ||
70 | # DEFAULTS tag and add it to the second. Be careful, options | ||
71 | # may only be declared once per test or default. If you have | ||
72 | # the same option name under the same test or as default | ||
73 | # ktest will fail to execute, and no tests will run. | ||
74 | # | ||
75 | |||
76 | |||
77 | #### Mandatory Default Options #### | ||
78 | |||
79 | # These options must be in the default section, although most | ||
80 | # may be overridden by test options. | ||
81 | |||
82 | # The machine hostname that you will test | ||
83 | #MACHINE = target | ||
84 | |||
85 | # The box is expected to have ssh on normal bootup, provide the user | ||
86 | # (most likely root, since you need privileged operations) | ||
87 | #SSH_USER = root | ||
88 | |||
89 | # The directory that contains the Linux source code | ||
90 | #BUILD_DIR = /home/test/linux.git | ||
91 | |||
92 | # The directory that the objects will be built | ||
93 | # (can not be same as BUILD_DIR) | ||
94 | #OUTPUT_DIR = /home/test/build/target | ||
95 | |||
96 | # The location of the compiled file to copy to the target | ||
97 | # (relative to OUTPUT_DIR) | ||
98 | #BUILD_TARGET = arch/x86/boot/bzImage | ||
99 | |||
100 | # The place to put your image on the test machine | ||
101 | #TARGET_IMAGE = /boot/vmlinuz-test | ||
102 | |||
103 | # A script or command to reboot the box | ||
104 | # | ||
105 | # Here is a digital loggers power switch example | ||
106 | #POWER_CYCLE = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=CCL' | ||
107 | # | ||
108 | # Here is an example to reboot a virtual box on the current host | ||
109 | # with the name "Guest". | ||
110 | #POWER_CYCLE = virsh destroy Guest; sleep 5; virsh start Guest | ||
111 | |||
112 | # The script or command that reads the console | ||
113 | # | ||
114 | # If you use ttywatch server, something like the following would work. | ||
115 | #CONSOLE = nc -d localhost 3001 | ||
116 | # | ||
117 | # For a virtual machine with guest name "Guest". | ||
118 | #CONSOLE = virsh console Guest | ||
119 | |||
120 | # Required version ending to differentiate the test | ||
121 | # from other linux builds on the system. | ||
122 | #LOCALVERSION = -test | ||
123 | |||
124 | # The grub title name for the test kernel to boot | ||
125 | # (Only mandatory if REBOOT_TYPE = grub) | ||
126 | # | ||
127 | # Note, ktest.pl will not update the grub menu.lst, you need to | ||
128 | # manually add an option for the test. ktest.pl will search | ||
129 | # the grub menu.lst for this option to find what kernel to | ||
130 | # reboot into. | ||
131 | # | ||
132 | # For example, if in the /boot/grub/menu.lst the test kernel title has: | ||
133 | # title Test Kernel | ||
134 | # kernel vmlinuz-test | ||
135 | #GRUB_MENU = Test Kernel | ||
136 | |||
137 | # A script to reboot the target into the test kernel | ||
138 | # (Only mandatory if REBOOT_TYPE = script) | ||
139 | #REBOOT_SCRIPT = | ||
140 | |||
141 | #### Optional Config Options (all have defaults) #### | ||
142 | |||
143 | # Start a test setup. If you leave this off, all options | ||
144 | # will be default and the test will run once. | ||
145 | # This is a label and not really an option (it takes no value). | ||
146 | # You can append ITERATE and a number after it to iterate the | ||
147 | # test a number of times, or SKIP to ignore this test. | ||
148 | # | ||
149 | #TEST_START | ||
150 | #TEST_START ITERATE 5 | ||
151 | #TEST_START SKIP | ||
152 | |||
153 | # Have the following options as default again. Used after tests | ||
154 | # have already been defined by TEST_START. Optionally, you can | ||
155 | # just define all default options before the first TEST_START | ||
156 | # and you do not need this option. | ||
157 | # | ||
158 | # This is a label and not really an option (it takes no value). | ||
159 | # You can append SKIP to this label and the options within this | ||
160 | # section will be ignored. | ||
161 | # | ||
162 | # DEFAULTS | ||
163 | # DEFAULTS SKIP | ||
164 | |||
165 | # The default test type (default test) | ||
166 | # The test types may be: | ||
167 | # build - only build the kernel, do nothing else | ||
168 | # boot - build and boot the kernel | ||
169 | # test - build, boot and if TEST is set, run the test script | ||
170 | # (If TEST is not set, it defaults back to boot) | ||
171 | # bisect - Perform a bisect on the kernel (see BISECT_TYPE below) | ||
172 | # patchcheck - Do a test on a series of commits in git (see PATCHCHECK below) | ||
173 | #TEST_TYPE = test | ||
174 | |||
175 | # Test to run if there is a successful boot and TEST_TYPE is test. | ||
176 | # Must exit with 0 on success and non zero on error | ||
177 | # default (undefined) | ||
178 | #TEST = ssh user@machine /root/run_test | ||
179 | |||
180 | # The build type is any make config type or special command | ||
181 | # (default randconfig) | ||
182 | # nobuild - skip the clean and build step | ||
183 | # useconfig:/path/to/config - use the given config and run | ||
184 | # oldconfig on it. | ||
185 | # This option is ignored if TEST_TYPE is patchcheck or bisect | ||
186 | #BUILD_TYPE = randconfig | ||
187 | |||
188 | # The make command (default make) | ||
189 | # If you are building a 32bit x86 on a 64 bit host | ||
190 | #MAKE_CMD = CC=i386-gcc AS=i386-as make ARCH=i386 | ||
191 | |||
192 | # Any build options for the make of the kernel (not for other makes, like configs) | ||
193 | # (default "") | ||
194 | #BUILD_OPTIONS = -j20 | ||
195 | |||
196 | # If you need an initrd, you can add a script or code here to install | ||
197 | # it. The environment variable KERNEL_VERSION will be set to the | ||
198 | # kernel version that is used. Remember to add the initrd line | ||
199 | # to your grub menu.lst file. | ||
200 | # | ||
201 | # Here's a couple of examples to use: | ||
202 | #POST_INSTALL = ssh user@target /sbin/mkinitrd --allow-missing -f /boot/initramfs-test.img $KERNEL_VERSION | ||
203 | # | ||
204 | # or on some systems: | ||
205 | #POST_INSTALL = ssh user@target /sbin/dracut -f /boot/initramfs-test.img $KERNEL_VERSION | ||
206 | |||
207 | # Way to reboot the box to the test kernel. | ||
208 | # Only valid options so far are "grub" and "script" | ||
209 | # (default grub) | ||
210 | # If you specify grub, it will assume grub version 1 | ||
211 | # and will search in /boot/grub/menu.lst for the title $GRUB_MENU | ||
212 | # and select that target to reboot to the kernel. If this is not | ||
213 | # your setup, then specify "script" and have a command or script | ||
214 | # specified in REBOOT_SCRIPT to boot to the target. | ||
215 | # | ||
216 | # The entry in /boot/grub/menu.lst must be entered in manually. | ||
217 | # The test will not modify that file. | ||
218 | #REBOOT_TYPE = grub | ||
219 | |||
220 | # The min config that is needed to build for the machine | ||
221 | # A nice way to create this is with the following: | ||
222 | # | ||
223 | # $ ssh target | ||
224 | # $ lsmod > mymods | ||
225 | # $ scp mymods host:/tmp | ||
226 | # $ exit | ||
227 | # $ cd linux.git | ||
228 | # $ rm .config | ||
229 | # $ make LSMOD=mymods localyesconfig | ||
230 | # $ grep '^CONFIG' .config > /home/test/config-min | ||
231 | # | ||
232 | # If you want even less configs: | ||
233 | # | ||
234 | # log in directly to target (do not ssh) | ||
235 | # | ||
236 | # $ su | ||
237 | # # lsmod | cut -d' ' -f1 | xargs rmmod | ||
238 | # | ||
239 | # repeat the above several times | ||
240 | # | ||
241 | # # lsmod > mymods | ||
242 | # # reboot | ||
243 | # | ||
244 | # May need to reboot to get your network back to copy the mymods | ||
245 | # to the host, and then remove the previous .config and run the | ||
246 | # localyesconfig again. The CONFIG_MIN generated like this will | ||
247 | # not guarantee network activity to the box so the TEST_TYPE of | ||
248 | # test may fail. | ||
249 | # | ||
250 | # You might also want to set: | ||
251 | # CONFIG_CMDLINE="<your options here>" | ||
252 | # randconfig may set the above and override your real command | ||
253 | # line options. | ||
254 | # (default undefined) | ||
255 | #MIN_CONFIG = /home/test/config-min | ||
256 | |||
257 | # Sometimes there's options that just break the boot and | ||
258 | # you do not care about. Here are a few: | ||
259 | # # CONFIG_STAGING is not set | ||
260 | # Staging drivers are horrible, and can break the build. | ||
261 | # # CONFIG_SCSI_DEBUG is not set | ||
262 | # SCSI_DEBUG may change your root partition | ||
263 | # # CONFIG_KGDB_SERIAL_CONSOLE is not set | ||
264 | # KGDB may cause oops waiting for a connection that's not there. | ||
265 | # This option points to the file containing config options that will be prepended | ||
266 | # to the MIN_CONFIG (or be the MIN_CONFIG if it is not set) | ||
267 | # | ||
268 | # Note, config options in MIN_CONFIG will override these options. | ||
269 | # | ||
270 | # (default undefined) | ||
271 | #ADD_CONFIG = /home/test/config-broken | ||
272 | |||
273 | # The location on the host where to write temp files | ||
274 | # (default /tmp/ktest) | ||
275 | #TMP_DIR = /tmp/ktest | ||
276 | |||
277 | # Optional log file to write the status (recommended) | ||
278 | # Note, this is a DEFAULT section only option. | ||
279 | # (default undefined) | ||
280 | #LOG_FILE = /home/test/logfiles/target.log | ||
281 | |||
282 | # Remove old logfile if it exists before starting all tests. | ||
283 | # Note, this is a DEFAULT section only option. | ||
284 | # (default 0) | ||
285 | #CLEAR_LOG = 0 | ||
286 | |||
287 | # Line to define a successful boot up in console output. | ||
288 | # This is what the line contains, not the entire line. If you need | ||
289 | # the entire line to match, then use regural expression syntax like: | ||
290 | # (do not add any quotes around it) | ||
291 | # | ||
292 | # SUCCESS_LINE = ^MyBox Login:$ | ||
293 | # | ||
294 | # (default "login:") | ||
295 | #SUCCESS_LINE = login: | ||
296 | |||
297 | # In case the console constantly fills the screen, having | ||
298 | # a specified time to stop the test after success is recommended. | ||
299 | # (in seconds) | ||
300 | # (default 10) | ||
301 | #STOP_AFTER_SUCCESS = 10 | ||
302 | |||
303 | # In case the console constantly fills the screen, having | ||
304 | # a specified time to stop the test after failure is recommended. | ||
305 | # (in seconds) | ||
306 | # (default 60) | ||
307 | #STOP_AFTER_FAILURE = 60 | ||
308 | |||
309 | # Stop testing if a build fails. If set, the script will end if | ||
310 | # a failure is detected, otherwise it will save off the .config, | ||
311 | # dmesg and bootlog in a directory called | ||
312 | # MACHINE-TEST_TYPE_BUILD_TYPE-fail-yyyymmddhhmmss | ||
313 | # if the STORE_FAILURES directory is set. | ||
314 | # (default 1) | ||
315 | # Note, even if this is set to zero, there are some errors that still | ||
316 | # stop the tests. | ||
317 | #DIE_ON_FAILURE = 1 | ||
318 | |||
319 | # Directory to store failure directories on failure. If this is not | ||
320 | # set, DIE_ON_FAILURE=0 will not save off the .config, dmesg and | ||
321 | # bootlog. This option is ignored if DIE_ON_FAILURE is not set. | ||
322 | # (default undefined) | ||
323 | #STORE_FAILURES = /home/test/failures | ||
324 | |||
325 | # Build without doing a make mrproper, or removing .config | ||
326 | # (default 0) | ||
327 | #BUILD_NOCLEAN = 0 | ||
328 | |||
329 | # As the test reads the console, after it hits the SUCCESS_LINE | ||
330 | # the time it waits for the monitor to settle down between reads | ||
331 | # can usually be lowered. | ||
332 | # (in seconds) (default 1) | ||
333 | #BOOTED_TIMEOUT = 1 | ||
334 | |||
335 | # The timeout in seconds when we consider the box hung after | ||
336 | # the console stop producing output. Be sure to leave enough | ||
337 | # time here to get pass a reboot. Some machines may not produce | ||
338 | # any console output for a long time during a reboot. You do | ||
339 | # not want the test to fail just because the system was in | ||
340 | # the process of rebooting to the test kernel. | ||
341 | # (default 120) | ||
342 | #TIMEOUT = 120 | ||
343 | |||
344 | # In between tests, a reboot of the box may occur, and this | ||
345 | # is the time to wait for the console after it stops producing | ||
346 | # output. Some machines may not produce a large lag on reboot | ||
347 | # so this should accommodate it. | ||
348 | # The difference between this and TIMEOUT, is that TIMEOUT happens | ||
349 | # when rebooting to the test kernel. This sleep time happens | ||
350 | # after a test has completed and we are about to start running | ||
351 | # another test. If a reboot to the reliable kernel happens, | ||
352 | # we wait SLEEP_TIME for the console to stop producing output | ||
353 | # before starting the next test. | ||
354 | # (default 60) | ||
355 | #SLEEP_TIME = 60 | ||
356 | |||
357 | # The time in between bisects to sleep (in seconds) | ||
358 | # (default 60) | ||
359 | #BISECT_SLEEP_TIME = 60 | ||
360 | |||
361 | # Reboot the target box on error (default 0) | ||
362 | #REBOOT_ON_ERROR = 0 | ||
363 | |||
364 | # Power off the target on error (ignored if REBOOT_ON_ERROR is set) | ||
365 | # Note, this is a DEFAULT section only option. | ||
366 | # (default 0) | ||
367 | #POWEROFF_ON_ERROR = 0 | ||
368 | |||
369 | # Power off the target after all tests have completed successfully | ||
370 | # Note, this is a DEFAULT section only option. | ||
371 | # (default 0) | ||
372 | #POWEROFF_ON_SUCCESS = 0 | ||
373 | |||
374 | # Reboot the target after all test completed successfully (default 1) | ||
375 | # (ignored if POWEROFF_ON_SUCCESS is set) | ||
376 | #REBOOT_ON_SUCCESS = 1 | ||
377 | |||
378 | # In case there are isses with rebooting, you can specify this | ||
379 | # to always powercycle after this amount of time after calling | ||
380 | # reboot. | ||
381 | # Note, POWERCYCLE_AFTER_REBOOT = 0 does NOT disable it. It just | ||
382 | # makes it powercycle immediately after rebooting. Do not define | ||
383 | # it if you do not want it. | ||
384 | # (default undefined) | ||
385 | #POWERCYCLE_AFTER_REBOOT = 5 | ||
386 | |||
387 | # In case there's isses with halting, you can specify this | ||
388 | # to always poweroff after this amount of time after calling | ||
389 | # halt. | ||
390 | # Note, POWEROFF_AFTER_HALT = 0 does NOT disable it. It just | ||
391 | # makes it poweroff immediately after halting. Do not define | ||
392 | # it if you do not want it. | ||
393 | # (default undefined) | ||
394 | #POWEROFF_AFTER_HALT = 20 | ||
395 | |||
396 | # A script or command to power off the box (default undefined) | ||
397 | # Needed for POWEROFF_ON_ERROR and SUCCESS | ||
398 | # | ||
399 | # Example for digital loggers power switch: | ||
400 | #POWER_OFF = wget --no-proxy -O /dev/null -q --auth-no-challenge 'http://admin:admin@power/outlet?5=OFF' | ||
401 | # | ||
402 | # Example for a virtual guest call "Guest". | ||
403 | #POWER_OFF = virsh destroy Guest | ||
404 | |||
405 | # The way to execute a command on the target | ||
406 | # (default ssh $SSH_USER@$MACHINE $SSH_COMMAND";) | ||
407 | # The variables SSH_USER, MACHINE and SSH_COMMAND are defined | ||
408 | #SSH_EXEC = ssh $SSH_USER@$MACHINE $SSH_COMMAND"; | ||
409 | |||
410 | # The way to copy a file to the target | ||
411 | # (default scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE) | ||
412 | # The variables SSH_USER, MACHINE, SRC_FILE and DST_FILE are defined. | ||
413 | #SCP_TO_TARGET = scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE | ||
414 | |||
415 | # The nice way to reboot the target | ||
416 | # (default ssh $SSH_USER@$MACHINE reboot) | ||
417 | # The variables SSH_USER and MACHINE are defined. | ||
418 | #REBOOT = ssh $SSH_USER@$MACHINE reboot | ||
419 | |||
420 | #### Per test run options #### | ||
421 | # The following options are only allowed in TEST_START sections. | ||
422 | # They are ignored in the DEFAULTS sections. | ||
423 | # | ||
424 | # All of these are optional and undefined by default, although | ||
425 | # some of these options are required for TEST_TYPE of patchcheck | ||
426 | # and bisect. | ||
427 | # | ||
428 | # | ||
429 | # CHECKOUT = branch | ||
430 | # | ||
431 | # If the BUILD_DIR is a git repository, then you can set this option | ||
432 | # to checkout the given branch before running the TEST. If you | ||
433 | # specify this for the first run, that branch will be used for | ||
434 | # all preceding tests until a new CHECKOUT is set. | ||
435 | # | ||
436 | # | ||
437 | # | ||
438 | # For TEST_TYPE = patchcheck | ||
439 | # | ||
440 | # This expects the BUILD_DIR to be a git repository, and | ||
441 | # will checkout the PATCHCHECK_START commit. | ||
442 | # | ||
443 | # The option BUILD_TYPE will be ignored. | ||
444 | # | ||
445 | # The MIN_CONFIG will be used for all builds of the patchcheck. The build type | ||
446 | # used for patchcheck is oldconfig. | ||
447 | # | ||
448 | # PATCHCHECK_START is required and is the first patch to | ||
449 | # test (the SHA1 of the commit). You may also specify anything | ||
450 | # that git checkout allows (branch name, tage, HEAD~3). | ||
451 | # | ||
452 | # PATCHCHECK_END is the last patch to check (default HEAD) | ||
453 | # | ||
454 | # PATCHCHECK_TYPE is required and is the type of test to run: | ||
455 | # build, boot, test. | ||
456 | # | ||
457 | # Note, the build test will look for warnings, if a warning occurred | ||
458 | # in a file that a commit touches, the build will fail. | ||
459 | # | ||
460 | # If BUILD_NOCLEAN is set, then make mrproper will not be run on | ||
461 | # any of the builds, just like all other TEST_TYPE tests. But | ||
462 | # what makes patchcheck different from the other tests, is if | ||
463 | # BUILD_NOCLEAN is not set, only the first and last patch run | ||
464 | # make mrproper. This helps speed up the test. | ||
465 | # | ||
466 | # Example: | ||
467 | # TEST_START | ||
468 | # TEST_TYPE = patchcheck | ||
469 | # CHECKOUT = mybranch | ||
470 | # PATCHCHECK_TYPE = boot | ||
471 | # PATCHCHECK_START = 747e94ae3d1b4c9bf5380e569f614eb9040b79e7 | ||
472 | # PATCHCHECK_END = HEAD~2 | ||
473 | # | ||
474 | # | ||
475 | # | ||
476 | # For TEST_TYPE = bisect | ||
477 | # | ||
478 | # You can specify a git bisect if the BUILD_DIR is a git repository. | ||
479 | # The MIN_CONFIG will be used for all builds of the bisect. The build type | ||
480 | # used for bisecting is oldconfig. | ||
481 | # | ||
482 | # The option BUILD_TYPE will be ignored. | ||
483 | # | ||
484 | # BISECT_TYPE is the type of test to perform: | ||
485 | # build - bad fails to build | ||
486 | # boot - bad builds but fails to boot | ||
487 | # test - bad boots but fails a test | ||
488 | # | ||
489 | # BISECT_GOOD is the commit (SHA1) to label as good (accepts all git good commit types) | ||
490 | # BISECT_BAD is the commit to label as bad (accepts all git bad commit types) | ||
491 | # | ||
492 | # The above three options are required for a bisect operation. | ||
493 | # | ||
494 | # BISECT_REPLAY = /path/to/replay/file (optional, default undefined) | ||
495 | # | ||
496 | # If an operation failed in the bisect that was not expected to | ||
497 | # fail. Then the test ends. The state of the BUILD_DIR will be | ||
498 | # left off at where the failure occurred. You can examine the | ||
499 | # reason for the failure, and perhaps even find a git commit | ||
500 | # that would work to continue with. You can run: | ||
501 | # | ||
502 | # git bisect log > /path/to/replay/file | ||
503 | # | ||
504 | # The adding: | ||
505 | # | ||
506 | # BISECT_REPLAY= /path/to/replay/file | ||
507 | # | ||
508 | # And running the test again. The test will perform the initial | ||
509 | # git bisect start, git bisect good, and git bisect bad, and | ||
510 | # then it will run git bisect replay on this file, before | ||
511 | # continuing with the bisect. | ||
512 | # | ||
513 | # BISECT_START = commit (optional, default undefined) | ||
514 | # | ||
515 | # As with BISECT_REPLAY, if the test failed on a commit that | ||
516 | # just happen to have a bad commit in the middle of the bisect, | ||
517 | # and you need to skip it. If BISECT_START is defined, it | ||
518 | # will checkout that commit after doing the initial git bisect start, | ||
519 | # git bisect good, git bisect bad, and running the git bisect replay | ||
520 | # if the BISECT_REPLAY is set. | ||
521 | # | ||
522 | # BISECT_REVERSE = 1 (optional, default 0) | ||
523 | # | ||
524 | # In those strange instances where it was broken forever | ||
525 | # and you are trying to find where it started to work! | ||
526 | # Set BISECT_GOOD to the commit that was last known to fail | ||
527 | # Set BISECT_BAD to the commit that is known to start working. | ||
528 | # With BISECT_REVERSE = 1, The test will consider failures as | ||
529 | # good, and success as bad. | ||
530 | # | ||
531 | # BISECT_CHECK = 1 (optional, default 0) | ||
532 | # | ||
533 | # Just to be sure the good is good and bad is bad, setting | ||
534 | # BISECT_CHECK to 1 will start the bisect by first checking | ||
535 | # out BISECT_BAD and makes sure it fails, then it will check | ||
536 | # out BISECT_GOOD and makes sure it succeeds before starting | ||
537 | # the bisect (it works for BISECT_REVERSE too). | ||
538 | # | ||
539 | # You can limit the test to just check BISECT_GOOD or | ||
540 | # BISECT_BAD with BISECT_CHECK = good or | ||
541 | # BISECT_CHECK = bad, respectively. | ||
542 | # | ||
543 | # Example: | ||
544 | # TEST_START | ||
545 | # TEST_TYPE = bisect | ||
546 | # BISECT_GOOD = v2.6.36 | ||
547 | # BISECT_BAD = b5153163ed580e00c67bdfecb02b2e3843817b3e | ||
548 | # BISECT_TYPE = build | ||
549 | # MIN_CONFIG = /home/test/config-bisect | ||
550 | # | ||
551 | # | ||
552 | # | ||
553 | # For TEST_TYPE = config_bisect | ||
554 | # | ||
555 | # In those cases that you have two different configs. One of them | ||
556 | # work, the other does not, and you do not know what config causes | ||
557 | # the problem. | ||
558 | # The TEST_TYPE config_bisect will bisect the bad config looking for | ||
559 | # what config causes the failure. | ||
560 | # | ||
561 | # The way it works is this: | ||
562 | # | ||
563 | # First it finds a config to work with. Since a different version, or | ||
564 | # MIN_CONFIG may cause different dependecies, it must run through this | ||
565 | # preparation. | ||
566 | # | ||
567 | # Overwrites any config set in the bad config with a config set in | ||
568 | # either the MIN_CONFIG or ADD_CONFIG. Thus, make sure these configs | ||
569 | # are minimal and do not disable configs you want to test: | ||
570 | # (ie. # CONFIG_FOO is not set). | ||
571 | # | ||
572 | # An oldconfig is run on the bad config and any new config that | ||
573 | # appears will be added to the configs to test. | ||
574 | # | ||
575 | # Finally, it generates a config with the above result and runs it | ||
576 | # again through make oldconfig to produce a config that should be | ||
577 | # satisfied by kconfig. | ||
578 | # | ||
579 | # Then it starts the bisect. | ||
580 | # | ||
581 | # The configs to test are cut in half. If all the configs in this | ||
582 | # half depend on a config in the other half, then the other half | ||
583 | # is tested instead. If no configs are enabled by either half, then | ||
584 | # this means a circular dependency exists and the test fails. | ||
585 | # | ||
586 | # A config is created with the test half, and the bisect test is run. | ||
587 | # | ||
588 | # If the bisect succeeds, then all configs in the generated config | ||
589 | # are removed from the configs to test and added to the configs that | ||
590 | # will be enabled for all builds (they will be enabled, but not be part | ||
591 | # of the configs to examine). | ||
592 | # | ||
593 | # If the bisect fails, then all test configs that were not enabled by | ||
594 | # the config file are removed from the test. These configs will not | ||
595 | # be enabled in future tests. Since current config failed, we consider | ||
596 | # this to be a subset of the config that we started with. | ||
597 | # | ||
598 | # When we are down to one config, it is considered the bad config. | ||
599 | # | ||
600 | # Note, the config chosen may not be the true bad config. Due to | ||
601 | # dependencies and selections of the kbuild system, mulitple | ||
602 | # configs may be needed to cause a failure. If you disable the | ||
603 | # config that was found and restart the test, if the test fails | ||
604 | # again, it is recommended to rerun the config_bisect with a new | ||
605 | # bad config without the found config enabled. | ||
606 | # | ||
607 | # The option BUILD_TYPE will be ignored. | ||
608 | # | ||
609 | # CONFIG_BISECT_TYPE is the type of test to perform: | ||
610 | # build - bad fails to build | ||
611 | # boot - bad builds but fails to boot | ||
612 | # test - bad boots but fails a test | ||
613 | # | ||
614 | # CONFIG_BISECT is the config that failed to boot | ||
615 | # | ||
616 | # Example: | ||
617 | # TEST_START | ||
618 | # TEST_TYPE = config_bisect | ||
619 | # CONFIG_BISECT_TYPE = build | ||
620 | # CONFIG_BISECT = /home/test/¢onfig-bad | ||
621 | # MIN_CONFIG = /home/test/config-min | ||
622 | # | ||
diff --git a/tools/virtio/Makefile b/tools/virtio/Makefile new file mode 100644 index 000000000000..d1d442ed106a --- /dev/null +++ b/tools/virtio/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | all: test mod | ||
2 | test: virtio_test | ||
3 | virtio_test: virtio_ring.o virtio_test.o | ||
4 | CFLAGS += -g -O2 -Wall -I. -I ../../usr/include/ -Wno-pointer-sign -fno-strict-overflow -MMD | ||
5 | vpath %.c ../../drivers/virtio | ||
6 | mod: | ||
7 | ${MAKE} -C `pwd`/../.. M=`pwd`/vhost_test | ||
8 | .PHONY: all test mod clean | ||
9 | clean: | ||
10 | ${RM} *.o vhost_test/*.o vhost_test/.*.cmd \ | ||
11 | vhost_test/Module.symvers vhost_test/modules.order *.d | ||
12 | -include *.d | ||
diff --git a/tools/virtio/linux/device.h b/tools/virtio/linux/device.h new file mode 100644 index 000000000000..4ad7e1df0db5 --- /dev/null +++ b/tools/virtio/linux/device.h | |||
@@ -0,0 +1,2 @@ | |||
1 | #ifndef LINUX_DEVICE_H | ||
2 | #endif | ||
diff --git a/tools/virtio/linux/slab.h b/tools/virtio/linux/slab.h new file mode 100644 index 000000000000..81baeac8ae40 --- /dev/null +++ b/tools/virtio/linux/slab.h | |||
@@ -0,0 +1,2 @@ | |||
1 | #ifndef LINUX_SLAB_H | ||
2 | #endif | ||
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h new file mode 100644 index 000000000000..669bcdd45805 --- /dev/null +++ b/tools/virtio/linux/virtio.h | |||
@@ -0,0 +1,223 @@ | |||
1 | #ifndef LINUX_VIRTIO_H | ||
2 | #define LINUX_VIRTIO_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <stddef.h> | ||
7 | #include <stdio.h> | ||
8 | #include <string.h> | ||
9 | #include <assert.h> | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <errno.h> | ||
13 | |||
14 | typedef unsigned long long dma_addr_t; | ||
15 | |||
16 | struct scatterlist { | ||
17 | unsigned long page_link; | ||
18 | unsigned int offset; | ||
19 | unsigned int length; | ||
20 | dma_addr_t dma_address; | ||
21 | }; | ||
22 | |||
23 | struct page { | ||
24 | unsigned long long dummy; | ||
25 | }; | ||
26 | |||
27 | #define BUG_ON(__BUG_ON_cond) assert(!(__BUG_ON_cond)) | ||
28 | |||
29 | /* Physical == Virtual */ | ||
30 | #define virt_to_phys(p) ((unsigned long)p) | ||
31 | #define phys_to_virt(a) ((void *)(unsigned long)(a)) | ||
32 | /* Page address: Virtual / 4K */ | ||
33 | #define virt_to_page(p) ((struct page*)((virt_to_phys(p) / 4096) * \ | ||
34 | sizeof(struct page))) | ||
35 | #define offset_in_page(p) (((unsigned long)p) % 4096) | ||
36 | #define sg_phys(sg) ((sg->page_link & ~0x3) / sizeof(struct page) * 4096 + \ | ||
37 | sg->offset) | ||
38 | static inline void sg_mark_end(struct scatterlist *sg) | ||
39 | { | ||
40 | /* | ||
41 | * Set termination bit, clear potential chain bit | ||
42 | */ | ||
43 | sg->page_link |= 0x02; | ||
44 | sg->page_link &= ~0x01; | ||
45 | } | ||
46 | static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents) | ||
47 | { | ||
48 | memset(sgl, 0, sizeof(*sgl) * nents); | ||
49 | sg_mark_end(&sgl[nents - 1]); | ||
50 | } | ||
51 | static inline void sg_assign_page(struct scatterlist *sg, struct page *page) | ||
52 | { | ||
53 | unsigned long page_link = sg->page_link & 0x3; | ||
54 | |||
55 | /* | ||
56 | * In order for the low bit stealing approach to work, pages | ||
57 | * must be aligned at a 32-bit boundary as a minimum. | ||
58 | */ | ||
59 | BUG_ON((unsigned long) page & 0x03); | ||
60 | sg->page_link = page_link | (unsigned long) page; | ||
61 | } | ||
62 | |||
63 | static inline void sg_set_page(struct scatterlist *sg, struct page *page, | ||
64 | unsigned int len, unsigned int offset) | ||
65 | { | ||
66 | sg_assign_page(sg, page); | ||
67 | sg->offset = offset; | ||
68 | sg->length = len; | ||
69 | } | ||
70 | |||
71 | static inline void sg_set_buf(struct scatterlist *sg, const void *buf, | ||
72 | unsigned int buflen) | ||
73 | { | ||
74 | sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf)); | ||
75 | } | ||
76 | |||
77 | static inline void sg_init_one(struct scatterlist *sg, const void *buf, unsigned int buflen) | ||
78 | { | ||
79 | sg_init_table(sg, 1); | ||
80 | sg_set_buf(sg, buf, buflen); | ||
81 | } | ||
82 | |||
83 | typedef __u16 u16; | ||
84 | |||
85 | typedef enum { | ||
86 | GFP_KERNEL, | ||
87 | GFP_ATOMIC, | ||
88 | } gfp_t; | ||
89 | typedef enum { | ||
90 | IRQ_NONE, | ||
91 | IRQ_HANDLED | ||
92 | } irqreturn_t; | ||
93 | |||
94 | static inline void *kmalloc(size_t s, gfp_t gfp) | ||
95 | { | ||
96 | return malloc(s); | ||
97 | } | ||
98 | |||
99 | static inline void kfree(void *p) | ||
100 | { | ||
101 | free(p); | ||
102 | } | ||
103 | |||
104 | #define container_of(ptr, type, member) ({ \ | ||
105 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ | ||
106 | (type *)( (char *)__mptr - offsetof(type,member) );}) | ||
107 | |||
108 | #define uninitialized_var(x) x = x | ||
109 | |||
110 | # ifndef likely | ||
111 | # define likely(x) (__builtin_expect(!!(x), 1)) | ||
112 | # endif | ||
113 | # ifndef unlikely | ||
114 | # define unlikely(x) (__builtin_expect(!!(x), 0)) | ||
115 | # endif | ||
116 | |||
117 | #define pr_err(format, ...) fprintf (stderr, format, ## __VA_ARGS__) | ||
118 | #ifdef DEBUG | ||
119 | #define pr_debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__) | ||
120 | #else | ||
121 | #define pr_debug(format, ...) do {} while (0) | ||
122 | #endif | ||
123 | #define dev_err(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) | ||
124 | #define dev_warn(dev, format, ...) fprintf (stderr, format, ## __VA_ARGS__) | ||
125 | |||
126 | /* TODO: empty stubs for now. Broken but enough for virtio_ring.c */ | ||
127 | #define list_add_tail(a, b) do {} while (0) | ||
128 | #define list_del(a) do {} while (0) | ||
129 | |||
130 | #define BIT_WORD(nr) ((nr) / BITS_PER_LONG) | ||
131 | #define BITS_PER_BYTE 8 | ||
132 | #define BITS_PER_LONG (sizeof(long) * BITS_PER_BYTE) | ||
133 | #define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) | ||
134 | /* TODO: Not atomic as it should be: | ||
135 | * we don't use this for anything important. */ | ||
136 | static inline void clear_bit(int nr, volatile unsigned long *addr) | ||
137 | { | ||
138 | unsigned long mask = BIT_MASK(nr); | ||
139 | unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr); | ||
140 | |||
141 | *p &= ~mask; | ||
142 | } | ||
143 | |||
144 | static inline int test_bit(int nr, const volatile unsigned long *addr) | ||
145 | { | ||
146 | return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1))); | ||
147 | } | ||
148 | |||
149 | /* The only feature we care to support */ | ||
150 | #define virtio_has_feature(dev, feature) \ | ||
151 | test_bit((feature), (dev)->features) | ||
152 | /* end of stubs */ | ||
153 | |||
154 | struct virtio_device { | ||
155 | void *dev; | ||
156 | unsigned long features[1]; | ||
157 | }; | ||
158 | |||
159 | struct virtqueue { | ||
160 | /* TODO: commented as list macros are empty stubs for now. | ||
161 | * Broken but enough for virtio_ring.c | ||
162 | * struct list_head list; */ | ||
163 | void (*callback)(struct virtqueue *vq); | ||
164 | const char *name; | ||
165 | struct virtio_device *vdev; | ||
166 | void *priv; | ||
167 | }; | ||
168 | |||
169 | #define EXPORT_SYMBOL_GPL(__EXPORT_SYMBOL_GPL_name) \ | ||
170 | void __EXPORT_SYMBOL_GPL##__EXPORT_SYMBOL_GPL_name() { \ | ||
171 | } | ||
172 | #define MODULE_LICENSE(__MODULE_LICENSE_value) \ | ||
173 | const char *__MODULE_LICENSE_name = __MODULE_LICENSE_value | ||
174 | |||
175 | #define CONFIG_SMP | ||
176 | |||
177 | #if defined(__i386__) || defined(__x86_64__) | ||
178 | #define barrier() asm volatile("" ::: "memory") | ||
179 | #define mb() __sync_synchronize() | ||
180 | |||
181 | #define smp_mb() mb() | ||
182 | # define smp_rmb() barrier() | ||
183 | # define smp_wmb() barrier() | ||
184 | #else | ||
185 | #error Please fill in barrier macros | ||
186 | #endif | ||
187 | |||
188 | /* Interfaces exported by virtio_ring. */ | ||
189 | int virtqueue_add_buf_gfp(struct virtqueue *vq, | ||
190 | struct scatterlist sg[], | ||
191 | unsigned int out_num, | ||
192 | unsigned int in_num, | ||
193 | void *data, | ||
194 | gfp_t gfp); | ||
195 | |||
196 | static inline int virtqueue_add_buf(struct virtqueue *vq, | ||
197 | struct scatterlist sg[], | ||
198 | unsigned int out_num, | ||
199 | unsigned int in_num, | ||
200 | void *data) | ||
201 | { | ||
202 | return virtqueue_add_buf_gfp(vq, sg, out_num, in_num, data, GFP_ATOMIC); | ||
203 | } | ||
204 | |||
205 | void virtqueue_kick(struct virtqueue *vq); | ||
206 | |||
207 | void *virtqueue_get_buf(struct virtqueue *vq, unsigned int *len); | ||
208 | |||
209 | void virtqueue_disable_cb(struct virtqueue *vq); | ||
210 | |||
211 | bool virtqueue_enable_cb(struct virtqueue *vq); | ||
212 | |||
213 | void *virtqueue_detach_unused_buf(struct virtqueue *vq); | ||
214 | struct virtqueue *vring_new_virtqueue(unsigned int num, | ||
215 | unsigned int vring_align, | ||
216 | struct virtio_device *vdev, | ||
217 | void *pages, | ||
218 | void (*notify)(struct virtqueue *vq), | ||
219 | void (*callback)(struct virtqueue *vq), | ||
220 | const char *name); | ||
221 | void vring_del_virtqueue(struct virtqueue *vq); | ||
222 | |||
223 | #endif | ||
diff --git a/tools/virtio/vhost_test/Makefile b/tools/virtio/vhost_test/Makefile new file mode 100644 index 000000000000..a1d35b81b314 --- /dev/null +++ b/tools/virtio/vhost_test/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | obj-m += vhost_test.o | ||
2 | EXTRA_CFLAGS += -Idrivers/vhost | ||
diff --git a/tools/virtio/vhost_test/vhost_test.c b/tools/virtio/vhost_test/vhost_test.c new file mode 100644 index 000000000000..18735189e62b --- /dev/null +++ b/tools/virtio/vhost_test/vhost_test.c | |||
@@ -0,0 +1 @@ | |||
#include "test.c" | |||
diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c new file mode 100644 index 000000000000..df0c6d2c3860 --- /dev/null +++ b/tools/virtio/virtio_test.c | |||
@@ -0,0 +1,248 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | #include <getopt.h> | ||
3 | #include <string.h> | ||
4 | #include <poll.h> | ||
5 | #include <sys/eventfd.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <assert.h> | ||
8 | #include <unistd.h> | ||
9 | #include <sys/ioctl.h> | ||
10 | #include <sys/stat.h> | ||
11 | #include <sys/types.h> | ||
12 | #include <fcntl.h> | ||
13 | #include <linux/vhost.h> | ||
14 | #include <linux/virtio.h> | ||
15 | #include <linux/virtio_ring.h> | ||
16 | #include "../../drivers/vhost/test.h" | ||
17 | |||
18 | struct vq_info { | ||
19 | int kick; | ||
20 | int call; | ||
21 | int num; | ||
22 | int idx; | ||
23 | void *ring; | ||
24 | /* copy used for control */ | ||
25 | struct vring vring; | ||
26 | struct virtqueue *vq; | ||
27 | }; | ||
28 | |||
29 | struct vdev_info { | ||
30 | struct virtio_device vdev; | ||
31 | int control; | ||
32 | struct pollfd fds[1]; | ||
33 | struct vq_info vqs[1]; | ||
34 | int nvqs; | ||
35 | void *buf; | ||
36 | size_t buf_size; | ||
37 | struct vhost_memory *mem; | ||
38 | }; | ||
39 | |||
40 | void vq_notify(struct virtqueue *vq) | ||
41 | { | ||
42 | struct vq_info *info = vq->priv; | ||
43 | unsigned long long v = 1; | ||
44 | int r; | ||
45 | r = write(info->kick, &v, sizeof v); | ||
46 | assert(r == sizeof v); | ||
47 | } | ||
48 | |||
49 | void vq_callback(struct virtqueue *vq) | ||
50 | { | ||
51 | } | ||
52 | |||
53 | |||
54 | void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info) | ||
55 | { | ||
56 | struct vhost_vring_state state = { .index = info->idx }; | ||
57 | struct vhost_vring_file file = { .index = info->idx }; | ||
58 | unsigned long long features = dev->vdev.features[0]; | ||
59 | struct vhost_vring_addr addr = { | ||
60 | .index = info->idx, | ||
61 | .desc_user_addr = (uint64_t)(unsigned long)info->vring.desc, | ||
62 | .avail_user_addr = (uint64_t)(unsigned long)info->vring.avail, | ||
63 | .used_user_addr = (uint64_t)(unsigned long)info->vring.used, | ||
64 | }; | ||
65 | int r; | ||
66 | r = ioctl(dev->control, VHOST_SET_FEATURES, &features); | ||
67 | assert(r >= 0); | ||
68 | state.num = info->vring.num; | ||
69 | r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state); | ||
70 | assert(r >= 0); | ||
71 | state.num = 0; | ||
72 | r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state); | ||
73 | assert(r >= 0); | ||
74 | r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr); | ||
75 | assert(r >= 0); | ||
76 | file.fd = info->kick; | ||
77 | r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file); | ||
78 | assert(r >= 0); | ||
79 | file.fd = info->call; | ||
80 | r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file); | ||
81 | assert(r >= 0); | ||
82 | } | ||
83 | |||
84 | static void vq_info_add(struct vdev_info *dev, int num) | ||
85 | { | ||
86 | struct vq_info *info = &dev->vqs[dev->nvqs]; | ||
87 | int r; | ||
88 | info->idx = dev->nvqs; | ||
89 | info->kick = eventfd(0, EFD_NONBLOCK); | ||
90 | info->call = eventfd(0, EFD_NONBLOCK); | ||
91 | r = posix_memalign(&info->ring, 4096, vring_size(num, 4096)); | ||
92 | assert(r >= 0); | ||
93 | memset(info->ring, 0, vring_size(num, 4096)); | ||
94 | vring_init(&info->vring, num, info->ring, 4096); | ||
95 | info->vq = vring_new_virtqueue(info->vring.num, 4096, &dev->vdev, info->ring, | ||
96 | vq_notify, vq_callback, "test"); | ||
97 | assert(info->vq); | ||
98 | info->vq->priv = info; | ||
99 | vhost_vq_setup(dev, info); | ||
100 | dev->fds[info->idx].fd = info->call; | ||
101 | dev->fds[info->idx].events = POLLIN; | ||
102 | dev->nvqs++; | ||
103 | } | ||
104 | |||
105 | static void vdev_info_init(struct vdev_info* dev, unsigned long long features) | ||
106 | { | ||
107 | int r; | ||
108 | memset(dev, 0, sizeof *dev); | ||
109 | dev->vdev.features[0] = features; | ||
110 | dev->vdev.features[1] = features >> 32; | ||
111 | dev->buf_size = 1024; | ||
112 | dev->buf = malloc(dev->buf_size); | ||
113 | assert(dev->buf); | ||
114 | dev->control = open("/dev/vhost-test", O_RDWR); | ||
115 | assert(dev->control >= 0); | ||
116 | r = ioctl(dev->control, VHOST_SET_OWNER, NULL); | ||
117 | assert(r >= 0); | ||
118 | dev->mem = malloc(offsetof(struct vhost_memory, regions) + | ||
119 | sizeof dev->mem->regions[0]); | ||
120 | assert(dev->mem); | ||
121 | memset(dev->mem, 0, offsetof(struct vhost_memory, regions) + | ||
122 | sizeof dev->mem->regions[0]); | ||
123 | dev->mem->nregions = 1; | ||
124 | dev->mem->regions[0].guest_phys_addr = (long)dev->buf; | ||
125 | dev->mem->regions[0].userspace_addr = (long)dev->buf; | ||
126 | dev->mem->regions[0].memory_size = dev->buf_size; | ||
127 | r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem); | ||
128 | assert(r >= 0); | ||
129 | } | ||
130 | |||
131 | /* TODO: this is pretty bad: we get a cache line bounce | ||
132 | * for the wait queue on poll and another one on read, | ||
133 | * plus the read which is there just to clear the | ||
134 | * current state. */ | ||
135 | static void wait_for_interrupt(struct vdev_info *dev) | ||
136 | { | ||
137 | int i; | ||
138 | unsigned long long val; | ||
139 | poll(dev->fds, dev->nvqs, -1); | ||
140 | for (i = 0; i < dev->nvqs; ++i) | ||
141 | if (dev->fds[i].revents & POLLIN) { | ||
142 | read(dev->fds[i].fd, &val, sizeof val); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | static void run_test(struct vdev_info *dev, struct vq_info *vq, int bufs) | ||
147 | { | ||
148 | struct scatterlist sl; | ||
149 | long started = 0, completed = 0; | ||
150 | long completed_before; | ||
151 | int r, test = 1; | ||
152 | unsigned len; | ||
153 | long long spurious = 0; | ||
154 | r = ioctl(dev->control, VHOST_TEST_RUN, &test); | ||
155 | assert(r >= 0); | ||
156 | for (;;) { | ||
157 | virtqueue_disable_cb(vq->vq); | ||
158 | completed_before = completed; | ||
159 | do { | ||
160 | if (started < bufs) { | ||
161 | sg_init_one(&sl, dev->buf, dev->buf_size); | ||
162 | r = virtqueue_add_buf(vq->vq, &sl, 1, 0, | ||
163 | dev->buf + started); | ||
164 | if (likely(r >= 0)) { | ||
165 | ++started; | ||
166 | virtqueue_kick(vq->vq); | ||
167 | } | ||
168 | } else | ||
169 | r = -1; | ||
170 | |||
171 | /* Flush out completed bufs if any */ | ||
172 | if (virtqueue_get_buf(vq->vq, &len)) { | ||
173 | ++completed; | ||
174 | r = 0; | ||
175 | } | ||
176 | |||
177 | } while (r >= 0); | ||
178 | if (completed == completed_before) | ||
179 | ++spurious; | ||
180 | assert(completed <= bufs); | ||
181 | assert(started <= bufs); | ||
182 | if (completed == bufs) | ||
183 | break; | ||
184 | if (virtqueue_enable_cb(vq->vq)) { | ||
185 | wait_for_interrupt(dev); | ||
186 | } | ||
187 | } | ||
188 | test = 0; | ||
189 | r = ioctl(dev->control, VHOST_TEST_RUN, &test); | ||
190 | assert(r >= 0); | ||
191 | fprintf(stderr, "spurious wakeus: 0x%llx\n", spurious); | ||
192 | } | ||
193 | |||
194 | const char optstring[] = "h"; | ||
195 | const struct option longopts[] = { | ||
196 | { | ||
197 | .name = "help", | ||
198 | .val = 'h', | ||
199 | }, | ||
200 | { | ||
201 | .name = "indirect", | ||
202 | .val = 'I', | ||
203 | }, | ||
204 | { | ||
205 | .name = "no-indirect", | ||
206 | .val = 'i', | ||
207 | }, | ||
208 | { | ||
209 | } | ||
210 | }; | ||
211 | |||
212 | static void help() | ||
213 | { | ||
214 | fprintf(stderr, "Usage: virtio_test [--help] [--no-indirect]\n"); | ||
215 | } | ||
216 | |||
217 | int main(int argc, char **argv) | ||
218 | { | ||
219 | struct vdev_info dev; | ||
220 | unsigned long long features = 1ULL << VIRTIO_RING_F_INDIRECT_DESC; | ||
221 | int o; | ||
222 | |||
223 | for (;;) { | ||
224 | o = getopt_long(argc, argv, optstring, longopts, NULL); | ||
225 | switch (o) { | ||
226 | case -1: | ||
227 | goto done; | ||
228 | case '?': | ||
229 | help(); | ||
230 | exit(2); | ||
231 | case 'h': | ||
232 | help(); | ||
233 | goto done; | ||
234 | case 'i': | ||
235 | features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC); | ||
236 | break; | ||
237 | default: | ||
238 | assert(0); | ||
239 | break; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | done: | ||
244 | vdev_info_init(&dev, features); | ||
245 | vq_info_add(&dev, 256); | ||
246 | run_test(&dev, &dev.vqs[0], 0x100000); | ||
247 | return 0; | ||
248 | } | ||