diff options
Diffstat (limited to 'tools')
158 files changed, 18566 insertions, 5197 deletions
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 0854f110bf7f..e1d60d780784 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore | |||
@@ -12,6 +12,9 @@ perf*.1 | |||
12 | perf*.xml | 12 | perf*.xml |
13 | perf*.html | 13 | perf*.html |
14 | common-cmds.h | 14 | common-cmds.h |
15 | perf.data | ||
16 | perf.data.old | ||
17 | perf-archive | ||
15 | tags | 18 | tags |
16 | TAGS | 19 | TAGS |
17 | cscope* | 20 | cscope* |
diff --git a/tools/perf/Documentation/perf-archive.txt b/tools/perf/Documentation/perf-archive.txt new file mode 100644 index 000000000000..fae174dc7d01 --- /dev/null +++ b/tools/perf/Documentation/perf-archive.txt | |||
@@ -0,0 +1,22 @@ | |||
1 | perf-archive(1) | ||
2 | =============== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-archive - Create archive with object files with build-ids found in perf.data file | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf archive' [file] | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | This command runs runs perf-buildid-list --with-hits, and collects the files | ||
16 | with the buildids found so that analisys of perf.data contents can be possible | ||
17 | on another machine. | ||
18 | |||
19 | |||
20 | SEE ALSO | ||
21 | -------- | ||
22 | linkperf:perf-record[1], linkperf:perf-buildid-list[1], linkperf:perf-report[1] | ||
diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt new file mode 100644 index 000000000000..ae525ac5a2ce --- /dev/null +++ b/tools/perf/Documentation/perf-bench.txt | |||
@@ -0,0 +1,120 @@ | |||
1 | perf-bench(1) | ||
2 | ============ | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-bench - General framework for benchmark suites | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf bench' [<common options>] <subsystem> <suite> [<options>] | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | This 'perf bench' command is general framework for benchmark suites. | ||
16 | |||
17 | COMMON OPTIONS | ||
18 | -------------- | ||
19 | -f:: | ||
20 | --format=:: | ||
21 | Specify format style. | ||
22 | Current available format styles are, | ||
23 | |||
24 | 'default':: | ||
25 | Default style. This is mainly for human reading. | ||
26 | --------------------- | ||
27 | % perf bench sched pipe # with no style specify | ||
28 | (executing 1000000 pipe operations between two tasks) | ||
29 | Total time:5.855 sec | ||
30 | 5.855061 usecs/op | ||
31 | 170792 ops/sec | ||
32 | --------------------- | ||
33 | |||
34 | 'simple':: | ||
35 | This simple style is friendly for automated | ||
36 | processing by scripts. | ||
37 | --------------------- | ||
38 | % perf bench --format=simple sched pipe # specified simple | ||
39 | 5.988 | ||
40 | --------------------- | ||
41 | |||
42 | SUBSYSTEM | ||
43 | --------- | ||
44 | |||
45 | 'sched':: | ||
46 | Scheduler and IPC mechanisms. | ||
47 | |||
48 | SUITES FOR 'sched' | ||
49 | ~~~~~~~~~~~~~~~~~~ | ||
50 | *messaging*:: | ||
51 | Suite for evaluating performance of scheduler and IPC mechanisms. | ||
52 | Based on hackbench by Rusty Russell. | ||
53 | |||
54 | Options of *pipe* | ||
55 | ^^^^^^^^^^^^^^^^^ | ||
56 | -p:: | ||
57 | --pipe:: | ||
58 | Use pipe() instead of socketpair() | ||
59 | |||
60 | -t:: | ||
61 | --thread:: | ||
62 | Be multi thread instead of multi process | ||
63 | |||
64 | -g:: | ||
65 | --group=:: | ||
66 | Specify number of groups | ||
67 | |||
68 | -l:: | ||
69 | --loop=:: | ||
70 | Specify number of loops | ||
71 | |||
72 | Example of *messaging* | ||
73 | ^^^^^^^^^^^^^^^^^^^^^^ | ||
74 | |||
75 | --------------------- | ||
76 | % perf bench sched messaging # run with default | ||
77 | options (20 sender and receiver processes per group) | ||
78 | (10 groups == 400 processes run) | ||
79 | |||
80 | Total time:0.308 sec | ||
81 | |||
82 | % perf bench sched messaging -t -g 20 # be multi-thread,with 20 groups | ||
83 | (20 sender and receiver threads per group) | ||
84 | (20 groups == 800 threads run) | ||
85 | |||
86 | Total time:0.582 sec | ||
87 | --------------------- | ||
88 | |||
89 | *pipe*:: | ||
90 | Suite for pipe() system call. | ||
91 | Based on pipe-test-1m.c by Ingo Molnar. | ||
92 | |||
93 | Options of *pipe* | ||
94 | ^^^^^^^^^^^^^^^^^ | ||
95 | -l:: | ||
96 | --loop=:: | ||
97 | Specify number of loops. | ||
98 | |||
99 | Example of *pipe* | ||
100 | ^^^^^^^^^^^^^^^^^ | ||
101 | |||
102 | --------------------- | ||
103 | % perf bench sched pipe | ||
104 | (executing 1000000 pipe operations between two tasks) | ||
105 | |||
106 | Total time:8.091 sec | ||
107 | 8.091833 usecs/op | ||
108 | 123581 ops/sec | ||
109 | |||
110 | % perf bench sched pipe -l 1000 # loop 1000 | ||
111 | (executing 1000 pipe operations between two tasks) | ||
112 | |||
113 | Total time:0.016 sec | ||
114 | 16.948000 usecs/op | ||
115 | 59004 ops/sec | ||
116 | --------------------- | ||
117 | |||
118 | SEE ALSO | ||
119 | -------- | ||
120 | linkperf:perf[1] | ||
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt new file mode 100644 index 000000000000..88bc3b519746 --- /dev/null +++ b/tools/perf/Documentation/perf-buildid-cache.txt | |||
@@ -0,0 +1,33 @@ | |||
1 | perf-buildid-cache(1) | ||
2 | ===================== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-buildid-cache - Manage build-id cache. | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf buildid-list <options>' | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | This command manages the build-id cache. It can add and remove files to the | ||
16 | cache. In the future it should as well purge older entries, set upper limits | ||
17 | for the space used by the cache, etc. | ||
18 | |||
19 | OPTIONS | ||
20 | ------- | ||
21 | -a:: | ||
22 | --add=:: | ||
23 | Add specified file to the cache. | ||
24 | -r:: | ||
25 | --remove=:: | ||
26 | Remove specified file to the cache. | ||
27 | -v:: | ||
28 | --verbose:: | ||
29 | Be more verbose. | ||
30 | |||
31 | SEE ALSO | ||
32 | -------- | ||
33 | linkperf:perf-record[1], linkperf:perf-report[1] | ||
diff --git a/tools/perf/Documentation/perf-buildid-list.txt b/tools/perf/Documentation/perf-buildid-list.txt new file mode 100644 index 000000000000..01b642c0bf8f --- /dev/null +++ b/tools/perf/Documentation/perf-buildid-list.txt | |||
@@ -0,0 +1,34 @@ | |||
1 | perf-buildid-list(1) | ||
2 | ==================== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-buildid-list - List the buildids in a perf.data file | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf buildid-list <options>' | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | This command displays the buildids found in a perf.data file, so that other | ||
16 | tools can be used to fetch packages with matching symbol tables for use by | ||
17 | perf report. | ||
18 | |||
19 | OPTIONS | ||
20 | ------- | ||
21 | -i:: | ||
22 | --input=:: | ||
23 | Input file name. (default: perf.data) | ||
24 | -f:: | ||
25 | --force:: | ||
26 | Don't do ownership validation. | ||
27 | -v:: | ||
28 | --verbose:: | ||
29 | Be more verbose. | ||
30 | |||
31 | SEE ALSO | ||
32 | -------- | ||
33 | linkperf:perf-record[1], linkperf:perf-top[1], | ||
34 | linkperf:perf-report[1] | ||
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt new file mode 100644 index 000000000000..8974e208cba6 --- /dev/null +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -0,0 +1,55 @@ | |||
1 | perf-diff(1) | ||
2 | ============== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-diff - Read two perf.data files and display the differential profile | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf diff' [oldfile] [newfile] | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | This command displays the performance difference amongst two perf.data files | ||
16 | captured via perf record. | ||
17 | |||
18 | If no parameters are passed it will assume perf.data.old and perf.data. | ||
19 | |||
20 | OPTIONS | ||
21 | ------- | ||
22 | -d:: | ||
23 | --dsos=:: | ||
24 | Only consider symbols in these dsos. CSV that understands | ||
25 | file://filename entries. | ||
26 | |||
27 | -C:: | ||
28 | --comms=:: | ||
29 | Only consider symbols in these comms. CSV that understands | ||
30 | file://filename entries. | ||
31 | |||
32 | -S:: | ||
33 | --symbols=:: | ||
34 | Only consider these symbols. CSV that understands | ||
35 | file://filename entries. | ||
36 | |||
37 | -s:: | ||
38 | --sort=:: | ||
39 | Sort by key(s): pid, comm, dso, symbol. | ||
40 | |||
41 | -t:: | ||
42 | --field-separator=:: | ||
43 | |||
44 | Use a special separator character and don't pad with spaces, replacing | ||
45 | all occurances of this separator in symbol names (and other output) | ||
46 | with a '.' character, that thus it's the only non valid separator. | ||
47 | |||
48 | -v:: | ||
49 | --verbose:: | ||
50 | Be verbose, for instance, show the raw counts in addition to the | ||
51 | diff. | ||
52 | |||
53 | SEE ALSO | ||
54 | -------- | ||
55 | linkperf:perf-record[1] | ||
diff --git a/tools/perf/Documentation/perf-kmem.txt b/tools/perf/Documentation/perf-kmem.txt new file mode 100644 index 000000000000..eac4d852e7cd --- /dev/null +++ b/tools/perf/Documentation/perf-kmem.txt | |||
@@ -0,0 +1,47 @@ | |||
1 | perf-kmem(1) | ||
2 | ============== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-kmem - Tool to trace/measure kernel memory(slab) properties | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf kmem' {record|stat} [<options>] | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | There are two variants of perf kmem: | ||
16 | |||
17 | 'perf kmem record <command>' to record the kmem events | ||
18 | of an arbitrary workload. | ||
19 | |||
20 | 'perf kmem stat' to report kernel memory statistics. | ||
21 | |||
22 | OPTIONS | ||
23 | ------- | ||
24 | -i <file>:: | ||
25 | --input=<file>:: | ||
26 | Select the input file (default: perf.data) | ||
27 | |||
28 | --caller:: | ||
29 | Show per-callsite statistics | ||
30 | |||
31 | --alloc:: | ||
32 | Show per-allocation statistics | ||
33 | |||
34 | -s <key[,key2...]>:: | ||
35 | --sort=<key[,key2...]>:: | ||
36 | Sort the output (default: frag,hit,bytes) | ||
37 | |||
38 | -l <num>:: | ||
39 | --line=<num>:: | ||
40 | Print n lines only | ||
41 | |||
42 | --raw-ip:: | ||
43 | Print raw ip instead of symbol | ||
44 | |||
45 | SEE ALSO | ||
46 | -------- | ||
47 | linkperf:perf-record[1] | ||
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt new file mode 100644 index 000000000000..b317102138c8 --- /dev/null +++ b/tools/perf/Documentation/perf-lock.txt | |||
@@ -0,0 +1,29 @@ | |||
1 | perf-lock(1) | ||
2 | ============ | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-lock - Analyze lock events | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf lock' {record|report|trace} | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | You can analyze various lock behaviours | ||
16 | and statistics with this 'perf lock' command. | ||
17 | |||
18 | 'perf lock record <command>' records lock events | ||
19 | between start and end <command>. And this command | ||
20 | produces the file "perf.data" which contains tracing | ||
21 | results of lock events. | ||
22 | |||
23 | 'perf lock trace' shows raw lock events. | ||
24 | |||
25 | 'perf lock report' reports statistical data. | ||
26 | |||
27 | SEE ALSO | ||
28 | -------- | ||
29 | linkperf:perf[1] | ||
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt new file mode 100644 index 000000000000..34202b1be0bb --- /dev/null +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -0,0 +1,129 @@ | |||
1 | perf-probe(1) | ||
2 | ============= | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-probe - Define new dynamic tracepoints | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf probe' [options] --add='PROBE' [...] | ||
12 | or | ||
13 | 'perf probe' [options] PROBE | ||
14 | or | ||
15 | 'perf probe' [options] --del='[GROUP:]EVENT' [...] | ||
16 | or | ||
17 | 'perf probe' --list | ||
18 | or | ||
19 | 'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]' | ||
20 | |||
21 | DESCRIPTION | ||
22 | ----------- | ||
23 | This command defines dynamic tracepoint events, by symbol and registers | ||
24 | without debuginfo, or by C expressions (C line numbers, C function names, | ||
25 | and C local variables) with debuginfo. | ||
26 | |||
27 | |||
28 | OPTIONS | ||
29 | ------- | ||
30 | -k:: | ||
31 | --vmlinux=PATH:: | ||
32 | Specify vmlinux path which has debuginfo (Dwarf binary). | ||
33 | |||
34 | -v:: | ||
35 | --verbose:: | ||
36 | Be more verbose (show parsed arguments, etc). | ||
37 | |||
38 | -a:: | ||
39 | --add=:: | ||
40 | Define a probe event (see PROBE SYNTAX for detail). | ||
41 | |||
42 | -d:: | ||
43 | --del=:: | ||
44 | Delete probe events. This accepts glob wildcards('*', '?') and character | ||
45 | classes(e.g. [a-z], [!A-Z]). | ||
46 | |||
47 | -l:: | ||
48 | --list:: | ||
49 | List up current probe events. | ||
50 | |||
51 | -L:: | ||
52 | --line=:: | ||
53 | Show source code lines which can be probed. This needs an argument | ||
54 | which specifies a range of the source code. (see LINE SYNTAX for detail) | ||
55 | |||
56 | -f:: | ||
57 | --force:: | ||
58 | Forcibly add events with existing name. | ||
59 | |||
60 | PROBE SYNTAX | ||
61 | ------------ | ||
62 | Probe points are defined by following syntax. | ||
63 | |||
64 | 1) Define event based on function name | ||
65 | [EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...] | ||
66 | |||
67 | 2) Define event based on source file with line number | ||
68 | [EVENT=]SRC:ALN [ARG ...] | ||
69 | |||
70 | 3) Define event based on source file with lazy pattern | ||
71 | [EVENT=]SRC;PTN [ARG ...] | ||
72 | |||
73 | |||
74 | 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'. | ||
75 | 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function. | ||
76 | It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern. | ||
77 | 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). | ||
78 | |||
79 | LINE SYNTAX | ||
80 | ----------- | ||
81 | Line range is descripted by following syntax. | ||
82 | |||
83 | "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]" | ||
84 | |||
85 | FUNC specifies the function name of showing lines. 'RLN' is the start line | ||
86 | number from function entry line, and 'RLN2' is the end line number. As same as | ||
87 | probe syntax, 'SRC' means the source file path, 'ALN' is start line number, | ||
88 | and 'ALN2' is end line number in the file. It is also possible to specify how | ||
89 | many lines to show by using 'NUM'. | ||
90 | So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function. | ||
91 | |||
92 | LAZY MATCHING | ||
93 | ------------- | ||
94 | The lazy line matching is similar to glob matching but ignoring spaces in both of pattern and target. So this accepts wildcards('*', '?') and character classes(e.g. [a-z], [!A-Z]). | ||
95 | |||
96 | e.g. | ||
97 | 'a=*' can matches 'a=b', 'a = b', 'a == b' and so on. | ||
98 | |||
99 | This provides some sort of flexibility and robustness to probe point definitions against minor code changes. For example, actual 10th line of schedule() can be moved easily by modifying schedule(), but the same line matching 'rq=cpu_rq*' may still exist in the function.) | ||
100 | |||
101 | |||
102 | EXAMPLES | ||
103 | -------- | ||
104 | Display which lines in schedule() can be probed: | ||
105 | |||
106 | ./perf probe --line schedule | ||
107 | |||
108 | Add a probe on schedule() function 12th line with recording cpu local variable: | ||
109 | |||
110 | ./perf probe schedule:12 cpu | ||
111 | or | ||
112 | ./perf probe --add='schedule:12 cpu' | ||
113 | |||
114 | this will add one or more probes which has the name start with "schedule". | ||
115 | |||
116 | Add probes on lines in schedule() function which calls update_rq_clock(). | ||
117 | |||
118 | ./perf probe 'schedule;update_rq_clock*' | ||
119 | or | ||
120 | ./perf probe --add='schedule;update_rq_clock*' | ||
121 | |||
122 | Delete all probes on schedule(). | ||
123 | |||
124 | ./perf probe --del='schedule*' | ||
125 | |||
126 | |||
127 | SEE ALSO | ||
128 | -------- | ||
129 | linkperf:perf-trace[1], linkperf:perf-record[1] | ||
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 0ff23de9e453..fc46c0b40f6e 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -26,11 +26,19 @@ OPTIONS | |||
26 | 26 | ||
27 | -e:: | 27 | -e:: |
28 | --event=:: | 28 | --event=:: |
29 | Select the PMU event. Selection can be a symbolic event name | 29 | Select the PMU event. Selection can be: |
30 | (use 'perf list' to list all events) or a raw PMU | ||
31 | event (eventsel+umask) in the form of rNNN where NNN is a | ||
32 | hexadecimal event descriptor. | ||
33 | 30 | ||
31 | - a symbolic event name (use 'perf list' to list all events) | ||
32 | |||
33 | - a raw PMU event (eventsel+umask) in the form of rNNN where NNN is a | ||
34 | hexadecimal event descriptor. | ||
35 | |||
36 | - a hardware breakpoint event in the form of '\mem:addr[:access]' | ||
37 | where addr is the address in memory you want to break in. | ||
38 | Access is the memory access type (read, write, execute) it can | ||
39 | be passed as follows: '\mem:addr[:[r][w][x]]'. | ||
40 | If you want to profile read-write accesses in 0x1000, just set | ||
41 | 'mem:0x1000:rw'. | ||
34 | -a:: | 42 | -a:: |
35 | System-wide collection. | 43 | System-wide collection. |
36 | 44 | ||
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 59f0b846cd71..abfabe9147a4 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -24,11 +24,11 @@ OPTIONS | |||
24 | --dsos=:: | 24 | --dsos=:: |
25 | Only consider symbols in these dsos. CSV that understands | 25 | Only consider symbols in these dsos. CSV that understands |
26 | file://filename entries. | 26 | file://filename entries. |
27 | -n | 27 | -n:: |
28 | --show-nr-samples | 28 | --show-nr-samples:: |
29 | Show the number of samples for each symbol | 29 | Show the number of samples for each symbol |
30 | -T | 30 | -T:: |
31 | --threads | 31 | --threads:: |
32 | Show per-thread event counters | 32 | Show per-thread event counters |
33 | -C:: | 33 | -C:: |
34 | --comms=:: | 34 | --comms=:: |
@@ -39,6 +39,10 @@ OPTIONS | |||
39 | Only consider these symbols. CSV that understands | 39 | Only consider these symbols. CSV that understands |
40 | file://filename entries. | 40 | file://filename entries. |
41 | 41 | ||
42 | -s:: | ||
43 | --sort=:: | ||
44 | Sort by key(s): pid, comm, dso, symbol, parent. | ||
45 | |||
42 | -w:: | 46 | -w:: |
43 | --field-width=:: | 47 | --field-width=:: |
44 | Force each column width to the provided list, for large terminal | 48 | Force each column width to the provided list, for large terminal |
diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index a7910099d6fd..4b1788355eca 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt | |||
@@ -31,9 +31,12 @@ OPTIONS | |||
31 | -w:: | 31 | -w:: |
32 | --width=:: | 32 | --width=:: |
33 | Select the width of the SVG file (default: 1000) | 33 | Select the width of the SVG file (default: 1000) |
34 | -p:: | 34 | -P:: |
35 | --power-only:: | 35 | --power-only:: |
36 | Only output the CPU power section of the diagram | 36 | Only output the CPU power section of the diagram |
37 | -p:: | ||
38 | --process:: | ||
39 | Select the processes to display, by name or PID | ||
37 | 40 | ||
38 | 41 | ||
39 | SEE ALSO | 42 | SEE ALSO |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 4a7d558dc309..785b9fc32a46 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -74,7 +74,7 @@ OPTIONS | |||
74 | 74 | ||
75 | -s <symbol>:: | 75 | -s <symbol>:: |
76 | --sym-annotate=<symbol>:: | 76 | --sym-annotate=<symbol>:: |
77 | Annotate this symbol. Requires -k option. | 77 | Annotate this symbol. |
78 | 78 | ||
79 | -v:: | 79 | -v:: |
80 | --verbose:: | 80 | --verbose:: |
diff --git a/tools/perf/Documentation/perf-trace-perl.txt b/tools/perf/Documentation/perf-trace-perl.txt new file mode 100644 index 000000000000..d729cee8d987 --- /dev/null +++ b/tools/perf/Documentation/perf-trace-perl.txt | |||
@@ -0,0 +1,219 @@ | |||
1 | perf-trace-perl(1) | ||
2 | ================== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-trace-perl - Process trace data with a Perl script | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf trace' [-s [Perl]:script[.pl] ] | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | |||
16 | This perf trace option is used to process perf trace data using perf's | ||
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 | ||
19 | Perl script, if any. | ||
20 | |||
21 | STARTER SCRIPTS | ||
22 | --------------- | ||
23 | |||
24 | You can avoid reading the rest of this document by running 'perf trace | ||
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 | ||
27 | the event types in the trace file; it simply prints every available | ||
28 | field for each event in the trace file. | ||
29 | |||
30 | You can also look at the existing scripts in | ||
31 | ~/libexec/perf-core/scripts/perl for typical examples showing how to | ||
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, | ||
34 | attempts to exercise all of the main scripting features. | ||
35 | |||
36 | EVENT HANDLERS | ||
37 | -------------- | ||
38 | |||
39 | When perf trace is invoked using a trace script, a user-defined | ||
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 | ||
42 | ignored (or passed to a 'trace_handled' function, see below) and the | ||
43 | next event is processed. | ||
44 | |||
45 | Most of the event's field values are passed as arguments to the | ||
46 | handler function; some of the less common ones aren't - those are | ||
47 | available as calls back into the perf executable (see below). | ||
48 | |||
49 | As an example, the following perf record command can be used to record | ||
50 | all sched_wakeup events in the system: | ||
51 | |||
52 | # perf record -c 1 -f -a -M -R -e sched:sched_wakeup | ||
53 | |||
54 | Traces meant to be processed using a script should be recorded with | ||
55 | the above options: -c 1 says to sample every event, -a to enable | ||
56 | system-wide collection, -M to multiplex the output, and -R to collect | ||
57 | raw samples. | ||
58 | |||
59 | The format file for the sched_wakep event defines the following fields | ||
60 | (see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): | ||
61 | |||
62 | ---- | ||
63 | format: | ||
64 | field:unsigned short common_type; | ||
65 | field:unsigned char common_flags; | ||
66 | field:unsigned char common_preempt_count; | ||
67 | field:int common_pid; | ||
68 | field:int common_lock_depth; | ||
69 | |||
70 | field:char comm[TASK_COMM_LEN]; | ||
71 | field:pid_t pid; | ||
72 | field:int prio; | ||
73 | field:int success; | ||
74 | field:int target_cpu; | ||
75 | ---- | ||
76 | |||
77 | The handler function for this event would be defined as: | ||
78 | |||
79 | ---- | ||
80 | sub sched::sched_wakeup | ||
81 | { | ||
82 | my ($event_name, $context, $common_cpu, $common_secs, | ||
83 | $common_nsecs, $common_pid, $common_comm, | ||
84 | $comm, $pid, $prio, $success, $target_cpu) = @_; | ||
85 | } | ||
86 | ---- | ||
87 | |||
88 | The handler function takes the form subsystem::event_name. | ||
89 | |||
90 | The $common_* arguments in the handler's argument list are the set of | ||
91 | arguments passed to all event handlers; some of the fields correspond | ||
92 | to the common_* fields in the format file, but some are synthesized, | ||
93 | and some of the common_* fields aren't common enough to to be passed | ||
94 | to every event as arguments but are available as library functions. | ||
95 | |||
96 | Here's a brief description of each of the invariant event args: | ||
97 | |||
98 | $event_name the name of the event as text | ||
99 | $context an opaque 'cookie' used in calls back into perf | ||
100 | $common_cpu the cpu the event occurred on | ||
101 | $common_secs the secs portion of the event timestamp | ||
102 | $common_nsecs the nsecs portion of the event timestamp | ||
103 | $common_pid the pid of the current task | ||
104 | $common_comm the name of the current process | ||
105 | |||
106 | All of the remaining fields in the event's format file have | ||
107 | counterparts as handler function arguments of the same name, as can be | ||
108 | seen in the example above. | ||
109 | |||
110 | The above provides the basics needed to directly access every field of | ||
111 | every event in a trace, which covers 90% of what you need to know to | ||
112 | write a useful trace script. The sections below cover the rest. | ||
113 | |||
114 | SCRIPT LAYOUT | ||
115 | ------------- | ||
116 | |||
117 | Every perf trace Perl script should start by setting up a Perl module | ||
118 | search path and 'use'ing a few support modules (see module | ||
119 | descriptions below): | ||
120 | |||
121 | ---- | ||
122 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
123 | use lib "./Perf-Trace-Util/lib"; | ||
124 | use Perf::Trace::Core; | ||
125 | use Perf::Trace::Context; | ||
126 | use Perf::Trace::Util; | ||
127 | ---- | ||
128 | |||
129 | The rest of the script can contain handler functions and support | ||
130 | functions in any order. | ||
131 | |||
132 | Aside from the event handler functions discussed above, every script | ||
133 | can implement a set of optional functions: | ||
134 | |||
135 | *trace_begin*, if defined, is called before any event is processed and | ||
136 | gives scripts a chance to do setup tasks: | ||
137 | |||
138 | ---- | ||
139 | sub trace_begin | ||
140 | { | ||
141 | } | ||
142 | ---- | ||
143 | |||
144 | *trace_end*, if defined, is called after all events have been | ||
145 | processed and gives scripts a chance to do end-of-script tasks, such | ||
146 | as display results: | ||
147 | |||
148 | ---- | ||
149 | sub trace_end | ||
150 | { | ||
151 | } | ||
152 | ---- | ||
153 | |||
154 | *trace_unhandled*, if defined, is called after for any event that | ||
155 | doesn't have a handler explicitly defined for it. The standard set | ||
156 | of common arguments are passed into it: | ||
157 | |||
158 | ---- | ||
159 | sub trace_unhandled | ||
160 | { | ||
161 | my ($event_name, $context, $common_cpu, $common_secs, | ||
162 | $common_nsecs, $common_pid, $common_comm) = @_; | ||
163 | } | ||
164 | ---- | ||
165 | |||
166 | The remaining sections provide descriptions of each of the available | ||
167 | built-in perf trace Perl modules and their associated functions. | ||
168 | |||
169 | AVAILABLE MODULES AND FUNCTIONS | ||
170 | ------------------------------- | ||
171 | |||
172 | The following sections describe the functions and variables available | ||
173 | via the various Perf::Trace::* Perl modules. To use the functions and | ||
174 | variables from the given module, add the corresponding 'use | ||
175 | Perf::Trace::XXX' line to your perf trace script. | ||
176 | |||
177 | Perf::Trace::Core Module | ||
178 | ~~~~~~~~~~~~~~~~~~~~~~~~ | ||
179 | |||
180 | These functions provide some essential functions to user scripts. | ||
181 | |||
182 | The *flag_str* and *symbol_str* functions provide human-readable | ||
183 | strings for flag and symbolic fields. These correspond to the strings | ||
184 | and values parsed from the 'print fmt' fields of the event format | ||
185 | files: | ||
186 | |||
187 | flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name | ||
188 | symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name | ||
189 | |||
190 | Perf::Trace::Context Module | ||
191 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
192 | |||
193 | Some of the 'common' fields in the event format file aren't all that | ||
194 | common, but need to be made accessible to user scripts nonetheless. | ||
195 | |||
196 | Perf::Trace::Context defines a set of functions that can be used to | ||
197 | access this data in the context of the current event. Each of these | ||
198 | functions expects a $context variable, which is the same as the | ||
199 | $context variable passed into every event handler as the second | ||
200 | argument. | ||
201 | |||
202 | common_pc($context) - returns common_preempt count for the current event | ||
203 | common_flags($context) - returns common_flags for the current event | ||
204 | common_lock_depth($context) - returns common_lock_depth for the current event | ||
205 | |||
206 | Perf::Trace::Util Module | ||
207 | ~~~~~~~~~~~~~~~~~~~~~~~~ | ||
208 | |||
209 | Various utility functions for use with perf trace: | ||
210 | |||
211 | nsecs($secs, $nsecs) - returns total nsecs given secs/nsecs pair | ||
212 | nsecs_secs($nsecs) - returns whole secs portion given nsecs | ||
213 | nsecs_nsecs($nsecs) - returns nsecs remainder given nsecs | ||
214 | nsecs_str($nsecs) - returns printable string in the form secs.nsecs | ||
215 | avg($total, $n) - returns average given a sum and a total number of values | ||
216 | |||
217 | SEE ALSO | ||
218 | -------- | ||
219 | linkperf:perf-trace[1] | ||
diff --git a/tools/perf/Documentation/perf-trace-python.txt b/tools/perf/Documentation/perf-trace-python.txt new file mode 100644 index 000000000000..a241aca77184 --- /dev/null +++ b/tools/perf/Documentation/perf-trace-python.txt | |||
@@ -0,0 +1,625 @@ | |||
1 | perf-trace-python(1) | ||
2 | ================== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-trace-python - Process trace data with a Python script | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf trace' [-s [Python]:script[.py] ] | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | |||
16 | This perf trace option is used to process perf trace data using perf's | ||
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 | ||
19 | Python script, if any. | ||
20 | |||
21 | A QUICK EXAMPLE | ||
22 | --------------- | ||
23 | |||
24 | This section shows the process, start to finish, of creating a working | ||
25 | Python script that aggregates and extracts useful information from a | ||
26 | raw perf trace stream. You can avoid reading the rest of this | ||
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 | ||
29 | available to script writers. | ||
30 | |||
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 | ||
33 | scripts via 'perf trace -l'. As such, this script also shows how to | ||
34 | integrate your script into the list of general-purpose 'perf trace' | ||
35 | scripts listed by that command. | ||
36 | |||
37 | The syscall-counts script is a simple script, but demonstrates all the | ||
38 | basic ideas necessary to create a useful script. Here's an example | ||
39 | of its output (syscall names are not yet supported, they will appear | ||
40 | as numbers): | ||
41 | |||
42 | ---- | ||
43 | syscall events: | ||
44 | |||
45 | event count | ||
46 | ---------------------------------------- ----------- | ||
47 | sys_write 455067 | ||
48 | sys_getdents 4072 | ||
49 | sys_close 3037 | ||
50 | sys_swapoff 1769 | ||
51 | sys_read 923 | ||
52 | sys_sched_setparam 826 | ||
53 | sys_open 331 | ||
54 | sys_newfstat 326 | ||
55 | sys_mmap 217 | ||
56 | sys_munmap 216 | ||
57 | sys_futex 141 | ||
58 | sys_select 102 | ||
59 | sys_poll 84 | ||
60 | sys_setitimer 12 | ||
61 | sys_writev 8 | ||
62 | 15 8 | ||
63 | sys_lseek 7 | ||
64 | sys_rt_sigprocmask 6 | ||
65 | sys_wait4 3 | ||
66 | sys_ioctl 3 | ||
67 | sys_set_robust_list 1 | ||
68 | sys_exit 1 | ||
69 | 56 1 | ||
70 | sys_access 1 | ||
71 | ---- | ||
72 | |||
73 | Basically our task is to keep a per-syscall tally that gets updated | ||
74 | every time a system call occurs in the system. Our script will do | ||
75 | that, but first we need to record the data that will be processed by | ||
76 | that script. Theoretically, there are a couple of ways we could do | ||
77 | that: | ||
78 | |||
79 | - we could enable every event under the tracing/events/syscalls | ||
80 | directory, but this is over 600 syscalls, well beyond the number | ||
81 | allowable by perf. These individual syscall events will however be | ||
82 | useful if we want to later use the guidance we get from the | ||
83 | general-purpose scripts to drill down and get more detail about | ||
84 | individual syscalls of interest. | ||
85 | |||
86 | - we can enable the sys_enter and/or sys_exit syscalls found under | ||
87 | tracing/events/raw_syscalls. These are called for all syscalls; the | ||
88 | 'id' field can be used to distinguish between individual syscall | ||
89 | numbers. | ||
90 | |||
91 | For this script, we only need to know that a syscall was entered; we | ||
92 | don't care how it exited, so we'll use 'perf record' to record only | ||
93 | the sys_enter events: | ||
94 | |||
95 | ---- | ||
96 | # perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter | ||
97 | |||
98 | ^C[ perf record: Woken up 1 times to write data ] | ||
99 | [ perf record: Captured and wrote 56.545 MB perf.data (~2470503 samples) ] | ||
100 | ---- | ||
101 | |||
102 | The options basically say to collect data for every syscall event | ||
103 | system-wide and multiplex the per-cpu output into a single stream. | ||
104 | That single stream will be recorded in a file in the current directory | ||
105 | called perf.data. | ||
106 | |||
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 | ||
109 | callback handler for each event type found in the perf.data trace | ||
110 | stream (for more details, see the STARTER SCRIPTS section). | ||
111 | |||
112 | ---- | ||
113 | # perf trace -g python | ||
114 | generated Python script: perf-trace.py | ||
115 | |||
116 | The output file created also in the current directory is named | ||
117 | perf-trace.py. Here's the file in its entirety: | ||
118 | |||
119 | # perf trace event handlers, generated by perf trace -g python | ||
120 | # Licensed under the terms of the GNU GPL License version 2 | ||
121 | |||
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 | ||
124 | # in the format files. Those fields not available as handler params can | ||
125 | # be retrieved using Python functions of the form common_*(context). | ||
126 | # See the perf-trace-python Documentation for the list of available functions. | ||
127 | |||
128 | import os | ||
129 | import sys | ||
130 | |||
131 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
132 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
133 | |||
134 | from perf_trace_context import * | ||
135 | from Core import * | ||
136 | |||
137 | def trace_begin(): | ||
138 | print "in trace_begin" | ||
139 | |||
140 | def trace_end(): | ||
141 | print "in trace_end" | ||
142 | |||
143 | def raw_syscalls__sys_enter(event_name, context, common_cpu, | ||
144 | common_secs, common_nsecs, common_pid, common_comm, | ||
145 | id, args): | ||
146 | print_header(event_name, common_cpu, common_secs, common_nsecs, | ||
147 | common_pid, common_comm) | ||
148 | |||
149 | print "id=%d, args=%s\n" % \ | ||
150 | (id, args), | ||
151 | |||
152 | def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, | ||
153 | common_pid, common_comm): | ||
154 | print_header(event_name, common_cpu, common_secs, common_nsecs, | ||
155 | common_pid, common_comm) | ||
156 | |||
157 | def print_header(event_name, cpu, secs, nsecs, pid, comm): | ||
158 | print "%-20s %5u %05u.%09u %8u %-20s " % \ | ||
159 | (event_name, cpu, secs, nsecs, pid, comm), | ||
160 | ---- | ||
161 | |||
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. | ||
164 | |||
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 | ||
167 | script respectively (for more details, see the SCRIPT_LAYOUT section | ||
168 | below). | ||
169 | |||
170 | Following those are the 'event handler' functions generated one for | ||
171 | every event in the 'perf record' output. The handler functions take | ||
172 | the form subsystem__event_name, and contain named parameters, one for | ||
173 | each field in the event; in this case, there's only one event, | ||
174 | raw_syscalls__sys_enter(). (see the EVENT HANDLERS section below for | ||
175 | more info on event handlers). | ||
176 | |||
177 | The final couple of functions are, like the begin and end functions, | ||
178 | generated for every script. The first, trace_unhandled(), is called | ||
179 | every time the script finds an event in the perf.data file that | ||
180 | doesn't correspond to any event handler in the script. This could | ||
181 | mean either that the record step recorded event types that it wasn't | ||
182 | really interested in, or the script was run against a trace file that | ||
183 | doesn't correspond to the script. | ||
184 | |||
185 | The script generated by -g option option simply prints a line for each | ||
186 | event found in the trace stream i.e. it basically just dumps the event | ||
187 | and its parameter values to stdout. The print_header() function is | ||
188 | simply a utility function used for that purpose. Let's rename the | ||
189 | script and run it to see the default output: | ||
190 | |||
191 | ---- | ||
192 | # mv perf-trace.py syscall-counts.py | ||
193 | # perf trace -s syscall-counts.py | ||
194 | |||
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= | ||
197 | raw_syscalls__sys_enter 1 00840.847620860 7506 perf id=1, args= | ||
198 | raw_syscalls__sys_enter 1 00840.847710478 6533 npviewer.bin id=78, args= | ||
199 | raw_syscalls__sys_enter 1 00840.847719204 6533 npviewer.bin id=142, args= | ||
200 | raw_syscalls__sys_enter 1 00840.847755445 6533 npviewer.bin id=3, args= | ||
201 | raw_syscalls__sys_enter 1 00840.847775601 6533 npviewer.bin id=3, args= | ||
202 | raw_syscalls__sys_enter 1 00840.847781820 6533 npviewer.bin id=3, args= | ||
203 | . | ||
204 | . | ||
205 | . | ||
206 | ---- | ||
207 | |||
208 | Of course, for this script, we're not interested in printing every | ||
209 | trace event, but rather aggregating it in a useful way. So we'll get | ||
210 | rid of everything to do with printing as well as the trace_begin() and | ||
211 | trace_unhandled() functions, which we won't be using. That leaves us | ||
212 | with this minimalistic skeleton: | ||
213 | |||
214 | ---- | ||
215 | import os | ||
216 | import sys | ||
217 | |||
218 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
219 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
220 | |||
221 | from perf_trace_context import * | ||
222 | from Core import * | ||
223 | |||
224 | def trace_end(): | ||
225 | print "in trace_end" | ||
226 | |||
227 | def raw_syscalls__sys_enter(event_name, context, common_cpu, | ||
228 | common_secs, common_nsecs, common_pid, common_comm, | ||
229 | id, args): | ||
230 | ---- | ||
231 | |||
232 | In trace_end(), we'll simply print the results, but first we need to | ||
233 | generate some results to print. To do that we need to have our | ||
234 | sys_enter() handler do the necessary tallying until all events have | ||
235 | been counted. A hash table indexed by syscall id is a good way to | ||
236 | store that information; every time the sys_enter() handler is called, | ||
237 | we simply increment a count associated with that hash entry indexed by | ||
238 | that syscall id: | ||
239 | |||
240 | ---- | ||
241 | syscalls = autodict() | ||
242 | |||
243 | try: | ||
244 | syscalls[id] += 1 | ||
245 | except TypeError: | ||
246 | syscalls[id] = 1 | ||
247 | ---- | ||
248 | |||
249 | The syscalls 'autodict' object is a special kind of Python dictionary | ||
250 | (implemented in Core.py) that implements Perl's 'autovivifying' hashes | ||
251 | in Python i.e. with autovivifying hashes, you can assign nested hash | ||
252 | values without having to go to the trouble of creating intermediate | ||
253 | levels if they don't exist e.g syscalls[comm][pid][id] = 1 will create | ||
254 | the intermediate hash levels and finally assign the value 1 to the | ||
255 | hash entry for 'id' (because the value being assigned isn't a hash | ||
256 | object itself, the initial value is assigned in the TypeError | ||
257 | exception. Well, there may be a better way to do this in Python but | ||
258 | that's what works for now). | ||
259 | |||
260 | Putting that code into the raw_syscalls__sys_enter() handler, we | ||
261 | effectively end up with a single-level dictionary keyed on syscall id | ||
262 | and having the counts we've tallied as values. | ||
263 | |||
264 | The print_syscall_totals() function iterates over the entries in the | ||
265 | dictionary and displays a line for each entry containing the syscall | ||
266 | name (the dictonary keys contain the syscall ids, which are passed to | ||
267 | the Util function syscall_name(), which translates the raw syscall | ||
268 | numbers to the corresponding syscall name strings). The output is | ||
269 | displayed after all the events in the trace have been processed, by | ||
270 | calling the print_syscall_totals() function from the trace_end() | ||
271 | handler called at the end of script processing. | ||
272 | |||
273 | The final script producing the output shown above is shown in its | ||
274 | entirety below (syscall_name() helper is not yet available, you can | ||
275 | only deal with id's for now): | ||
276 | |||
277 | ---- | ||
278 | import os | ||
279 | import sys | ||
280 | |||
281 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
282 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
283 | |||
284 | from perf_trace_context import * | ||
285 | from Core import * | ||
286 | from Util import * | ||
287 | |||
288 | syscalls = autodict() | ||
289 | |||
290 | def trace_end(): | ||
291 | print_syscall_totals() | ||
292 | |||
293 | def raw_syscalls__sys_enter(event_name, context, common_cpu, | ||
294 | common_secs, common_nsecs, common_pid, common_comm, | ||
295 | id, args): | ||
296 | try: | ||
297 | syscalls[id] += 1 | ||
298 | except TypeError: | ||
299 | syscalls[id] = 1 | ||
300 | |||
301 | def print_syscall_totals(): | ||
302 | if for_comm is not None: | ||
303 | print "\nsyscall events for %s:\n\n" % (for_comm), | ||
304 | else: | ||
305 | print "\nsyscall events:\n\n", | ||
306 | |||
307 | print "%-40s %10s\n" % ("event", "count"), | ||
308 | print "%-40s %10s\n" % ("----------------------------------------", \ | ||
309 | "-----------"), | ||
310 | |||
311 | for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ | ||
312 | reverse = True): | ||
313 | print "%-40s %10d\n" % (syscall_name(id), val), | ||
314 | ---- | ||
315 | |||
316 | The script can be run just as before: | ||
317 | |||
318 | # perf trace -s syscall-counts.py | ||
319 | |||
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 | ||
322 | you're interested in - basically find the tracepoint(s) you're | ||
323 | 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 | ||
325 | detailed event and field info, record the corresponding trace data | ||
326 | using 'perf record', passing it the list of interesting events, | ||
327 | generate a skeleton script using 'perf trace -g python' and modify the | ||
328 | code to aggregate and display it for your particular needs. | ||
329 | |||
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 | ||
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 | ||
334 | scripts listed by the 'perf trace -l' command e.g.: | ||
335 | |||
336 | ---- | ||
337 | root@tropicana:~# perf trace -l | ||
338 | List of available trace scripts: | ||
339 | workqueue-stats workqueue stats (ins/exe/create/destroy) | ||
340 | wakeup-latency system-wide min/max/avg wakeup latency | ||
341 | rw-by-file <comm> r/w activity for a program, by file | ||
342 | rw-by-pid system-wide r/w activity | ||
343 | ---- | ||
344 | |||
345 | A nice side effect of doing this is that you also then capture the | ||
346 | probably lengthy 'perf record' command needed to record the events for | ||
347 | the script. | ||
348 | |||
349 | To have the script appear as a 'built-in' script, you write two simple | ||
350 | scripts, one for recording and one for 'reporting'. | ||
351 | |||
352 | The 'record' script is a shell script with the same base name as your | ||
353 | script, but with -record appended. The shell script should be put | ||
354 | into the perf/scripts/python/bin directory in the kernel source tree. | ||
355 | In that script, you write the 'perf record' command-line needed for | ||
356 | your script: | ||
357 | |||
358 | ---- | ||
359 | # cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-record | ||
360 | |||
361 | #!/bin/bash | ||
362 | perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter | ||
363 | ---- | ||
364 | |||
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 | ||
367 | the perf/scripts/python/bin directory. In that script, you write the | ||
368 | 'perf trace -s' command-line needed for running your script: | ||
369 | |||
370 | ---- | ||
371 | # cat kernel-source/tools/perf/scripts/python/bin/syscall-counts-report | ||
372 | |||
373 | #!/bin/bash | ||
374 | # description: system-wide syscall counts | ||
375 | perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts.py | ||
376 | ---- | ||
377 | |||
378 | Note that the location of the Python script given in the shell script | ||
379 | is in the libexec/perf-core/scripts/python directory - this is where | ||
380 | the script will be copied by 'make install' when you install perf. | ||
381 | For the installation to install your script there, your script needs | ||
382 | to be located in the perf/scripts/python directory in the kernel | ||
383 | source tree: | ||
384 | |||
385 | ---- | ||
386 | # ls -al kernel-source/tools/perf/scripts/python | ||
387 | |||
388 | root@tropicana:/home/trz/src/tip# ls -al tools/perf/scripts/python | ||
389 | total 32 | ||
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 .. | ||
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 | ||
394 | drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 Perf-Trace-Util | ||
395 | -rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py | ||
396 | ---- | ||
397 | |||
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' | ||
400 | should show a new entry for your script: | ||
401 | |||
402 | ---- | ||
403 | root@tropicana:~# perf trace -l | ||
404 | List of available trace scripts: | ||
405 | workqueue-stats workqueue stats (ins/exe/create/destroy) | ||
406 | wakeup-latency system-wide min/max/avg wakeup latency | ||
407 | rw-by-file <comm> r/w activity for a program, by file | ||
408 | rw-by-pid system-wide r/w activity | ||
409 | syscall-counts system-wide syscall counts | ||
410 | ---- | ||
411 | |||
412 | You can now perform the record step via 'perf trace record': | ||
413 | |||
414 | # perf trace record syscall-counts | ||
415 | |||
416 | and display the output using 'perf trace report': | ||
417 | |||
418 | # perf trace report syscall-counts | ||
419 | |||
420 | STARTER SCRIPTS | ||
421 | --------------- | ||
422 | |||
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 | ||
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 | ||
427 | the event types in the trace file; it simply prints every available | ||
428 | field for each event in the trace file. | ||
429 | |||
430 | You can also look at the existing scripts in | ||
431 | ~/libexec/perf-core/scripts/python for typical examples showing how to | ||
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, | ||
434 | attempts to exercise all of the main scripting features. | ||
435 | |||
436 | EVENT HANDLERS | ||
437 | -------------- | ||
438 | |||
439 | When perf trace is invoked using a trace script, a user-defined | ||
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 | ||
442 | ignored (or passed to a 'trace_handled' function, see below) and the | ||
443 | next event is processed. | ||
444 | |||
445 | Most of the event's field values are passed as arguments to the | ||
446 | handler function; some of the less common ones aren't - those are | ||
447 | available as calls back into the perf executable (see below). | ||
448 | |||
449 | As an example, the following perf record command can be used to record | ||
450 | all sched_wakeup events in the system: | ||
451 | |||
452 | # perf record -c 1 -f -a -M -R -e sched:sched_wakeup | ||
453 | |||
454 | Traces meant to be processed using a script should be recorded with | ||
455 | the above options: -c 1 says to sample every event, -a to enable | ||
456 | system-wide collection, -M to multiplex the output, and -R to collect | ||
457 | raw samples. | ||
458 | |||
459 | The format file for the sched_wakep event defines the following fields | ||
460 | (see /sys/kernel/debug/tracing/events/sched/sched_wakeup/format): | ||
461 | |||
462 | ---- | ||
463 | format: | ||
464 | field:unsigned short common_type; | ||
465 | field:unsigned char common_flags; | ||
466 | field:unsigned char common_preempt_count; | ||
467 | field:int common_pid; | ||
468 | field:int common_lock_depth; | ||
469 | |||
470 | field:char comm[TASK_COMM_LEN]; | ||
471 | field:pid_t pid; | ||
472 | field:int prio; | ||
473 | field:int success; | ||
474 | field:int target_cpu; | ||
475 | ---- | ||
476 | |||
477 | The handler function for this event would be defined as: | ||
478 | |||
479 | ---- | ||
480 | def sched__sched_wakeup(event_name, context, common_cpu, common_secs, | ||
481 | common_nsecs, common_pid, common_comm, | ||
482 | comm, pid, prio, success, target_cpu): | ||
483 | pass | ||
484 | ---- | ||
485 | |||
486 | The handler function takes the form subsystem__event_name. | ||
487 | |||
488 | The common_* arguments in the handler's argument list are the set of | ||
489 | arguments passed to all event handlers; some of the fields correspond | ||
490 | to the common_* fields in the format file, but some are synthesized, | ||
491 | and some of the common_* fields aren't common enough to to be passed | ||
492 | to every event as arguments but are available as library functions. | ||
493 | |||
494 | Here's a brief description of each of the invariant event args: | ||
495 | |||
496 | event_name the name of the event as text | ||
497 | context an opaque 'cookie' used in calls back into perf | ||
498 | common_cpu the cpu the event occurred on | ||
499 | common_secs the secs portion of the event timestamp | ||
500 | common_nsecs the nsecs portion of the event timestamp | ||
501 | common_pid the pid of the current task | ||
502 | common_comm the name of the current process | ||
503 | |||
504 | All of the remaining fields in the event's format file have | ||
505 | counterparts as handler function arguments of the same name, as can be | ||
506 | seen in the example above. | ||
507 | |||
508 | The above provides the basics needed to directly access every field of | ||
509 | every event in a trace, which covers 90% of what you need to know to | ||
510 | write a useful trace script. The sections below cover the rest. | ||
511 | |||
512 | SCRIPT LAYOUT | ||
513 | ------------- | ||
514 | |||
515 | Every perf trace Python script should start by setting up a Python | ||
516 | module search path and 'import'ing a few support modules (see module | ||
517 | descriptions below): | ||
518 | |||
519 | ---- | ||
520 | import os | ||
521 | import sys | ||
522 | |||
523 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
524 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
525 | |||
526 | from perf_trace_context import * | ||
527 | from Core import * | ||
528 | ---- | ||
529 | |||
530 | The rest of the script can contain handler functions and support | ||
531 | functions in any order. | ||
532 | |||
533 | Aside from the event handler functions discussed above, every script | ||
534 | can implement a set of optional functions: | ||
535 | |||
536 | *trace_begin*, if defined, is called before any event is processed and | ||
537 | gives scripts a chance to do setup tasks: | ||
538 | |||
539 | ---- | ||
540 | def trace_begin: | ||
541 | pass | ||
542 | ---- | ||
543 | |||
544 | *trace_end*, if defined, is called after all events have been | ||
545 | processed and gives scripts a chance to do end-of-script tasks, such | ||
546 | as display results: | ||
547 | |||
548 | ---- | ||
549 | def trace_end: | ||
550 | pass | ||
551 | ---- | ||
552 | |||
553 | *trace_unhandled*, if defined, is called after for any event that | ||
554 | doesn't have a handler explicitly defined for it. The standard set | ||
555 | of common arguments are passed into it: | ||
556 | |||
557 | ---- | ||
558 | def trace_unhandled(event_name, context, common_cpu, common_secs, | ||
559 | common_nsecs, common_pid, common_comm): | ||
560 | pass | ||
561 | ---- | ||
562 | |||
563 | The remaining sections provide descriptions of each of the available | ||
564 | built-in perf trace Python modules and their associated functions. | ||
565 | |||
566 | AVAILABLE MODULES AND FUNCTIONS | ||
567 | ------------------------------- | ||
568 | |||
569 | The following sections describe the functions and variables available | ||
570 | via the various perf trace Python modules. To use the functions and | ||
571 | variables from the given module, add the corresponding 'from XXXX | ||
572 | import' line to your perf trace script. | ||
573 | |||
574 | Core.py Module | ||
575 | ~~~~~~~~~~~~~~ | ||
576 | |||
577 | These functions provide some essential functions to user scripts. | ||
578 | |||
579 | The *flag_str* and *symbol_str* functions provide human-readable | ||
580 | strings for flag and symbolic fields. These correspond to the strings | ||
581 | and values parsed from the 'print fmt' fields of the event format | ||
582 | files: | ||
583 | |||
584 | flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name | ||
585 | symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name | ||
586 | |||
587 | The *autodict* function returns a special special kind of Python | ||
588 | dictionary that implements Perl's 'autovivifying' hashes in Python | ||
589 | i.e. with autovivifying hashes, you can assign nested hash values | ||
590 | without having to go to the trouble of creating intermediate levels if | ||
591 | they don't exist. | ||
592 | |||
593 | autodict() - returns an autovivifying dictionary instance | ||
594 | |||
595 | |||
596 | perf_trace_context Module | ||
597 | ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
598 | |||
599 | Some of the 'common' fields in the event format file aren't all that | ||
600 | common, but need to be made accessible to user scripts nonetheless. | ||
601 | |||
602 | perf_trace_context defines a set of functions that can be used to | ||
603 | access this data in the context of the current event. Each of these | ||
604 | functions expects a context variable, which is the same as the | ||
605 | context variable passed into every event handler as the second | ||
606 | argument. | ||
607 | |||
608 | common_pc(context) - returns common_preempt count for the current event | ||
609 | common_flags(context) - returns common_flags for the current event | ||
610 | common_lock_depth(context) - returns common_lock_depth for the current event | ||
611 | |||
612 | Util.py Module | ||
613 | ~~~~~~~~~~~~~~ | ||
614 | |||
615 | Various utility functions for use with perf trace: | ||
616 | |||
617 | nsecs(secs, nsecs) - returns total nsecs given secs/nsecs pair | ||
618 | nsecs_secs(nsecs) - returns whole secs portion given nsecs | ||
619 | nsecs_nsecs(nsecs) - returns nsecs remainder given nsecs | ||
620 | nsecs_str(nsecs) - returns printable string in the form secs.nsecs | ||
621 | avg(total, n) - returns average given a sum and a total number of values | ||
622 | |||
623 | SEE ALSO | ||
624 | -------- | ||
625 | linkperf:perf-trace[1] | ||
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 41ed75398ca9..8879299cd9df 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -8,18 +8,63 @@ perf-trace - Read perf.data (created by perf record) and display trace output | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf trace' [-i <file> | --input=file] symbol_name | 11 | 'perf trace' {record <script> | report <script> [args] } |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command reads the input file and displays the trace recorded. | 15 | This command reads the input file and displays the trace recorded. |
16 | 16 | ||
17 | There are several variants of perf trace: | ||
18 | |||
19 | 'perf trace' to see a detailed trace of the workload that was | ||
20 | recorded. | ||
21 | |||
22 | You can also run a set of pre-canned scripts that aggregate and | ||
23 | summarize the raw trace data in various ways (the list of scripts is | ||
24 | available via 'perf trace -l'). The following variants allow you to | ||
25 | record and run those scripts: | ||
26 | |||
27 | 'perf trace record <script>' to record the events required for 'perf | ||
28 | trace report'. <script> is the name displayed in the output of | ||
29 | 'perf trace --list' i.e. the actual script name minus any language | ||
30 | extension. | ||
31 | |||
32 | 'perf trace report <script>' to run and display the results of | ||
33 | <script>. <script> is the name displayed in the output of 'perf | ||
34 | trace --list' i.e. the actual script name minus any language | ||
35 | extension. The perf.data output from a previous run of 'perf trace | ||
36 | record <script>' is used and should be present for this command to | ||
37 | succeed. | ||
38 | |||
39 | See the 'SEE ALSO' section for links to language-specific | ||
40 | information on how to write and run your own trace scripts. | ||
41 | |||
17 | OPTIONS | 42 | OPTIONS |
18 | ------- | 43 | ------- |
19 | -D:: | 44 | -D:: |
20 | --dump-raw-trace=:: | 45 | --dump-raw-trace=:: |
21 | Display verbose dump of the trace data. | 46 | Display verbose dump of the trace data. |
22 | 47 | ||
48 | -L:: | ||
49 | --Latency=:: | ||
50 | Show latency attributes (irqs/preemption disabled, etc). | ||
51 | |||
52 | -l:: | ||
53 | --list=:: | ||
54 | Display a list of available trace scripts. | ||
55 | |||
56 | -s ['lang']:: | ||
57 | --script=:: | ||
58 | Process trace data with the given script ([lang]:script[.ext]). | ||
59 | If the string 'lang' is specified in place of a script name, a | ||
60 | list of supported languages will be displayed instead. | ||
61 | |||
62 | -g:: | ||
63 | --gen-script=:: | ||
64 | Generate perf-trace.[ext] starter script for given language, | ||
65 | using current perf.data. | ||
66 | |||
23 | SEE ALSO | 67 | SEE ALSO |
24 | -------- | 68 | -------- |
25 | linkperf:perf-record[1] | 69 | linkperf:perf-record[1], linkperf:perf-trace-perl[1], |
70 | linkperf:perf-trace-python[1] | ||
diff --git a/tools/perf/Documentation/perf.txt b/tools/perf/Documentation/perf.txt index 69c832557199..0eeb247dc7d2 100644 --- a/tools/perf/Documentation/perf.txt +++ b/tools/perf/Documentation/perf.txt | |||
@@ -12,7 +12,7 @@ SYNOPSIS | |||
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | Performance counters for Linux are are a new kernel-based subsystem | 15 | Performance counters for Linux are a new kernel-based subsystem |
16 | that provide a framework for all things performance analysis. It | 16 | that provide a framework for all things performance analysis. It |
17 | covers hardware level (CPU/PMU, Performance Monitoring Unit) features | 17 | covers hardware level (CPU/PMU, Performance Monitoring Unit) features |
18 | and software features (software counters, tracepoints) as well. | 18 | and software features (software counters, tracepoints) as well. |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7e190d522cd5..2d537382c686 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -2,6 +2,7 @@ | |||
2 | all:: | 2 | all:: |
3 | 3 | ||
4 | # Define V=1 to have a more verbose compile. | 4 | # Define V=1 to have a more verbose compile. |
5 | # Define V=2 to have an even more verbose compile. | ||
5 | # | 6 | # |
6 | # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf() | 7 | # Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf() |
7 | # or vsnprintf() return -1 instead of number of characters which would | 8 | # or vsnprintf() return -1 instead of number of characters which would |
@@ -145,6 +146,10 @@ all:: | |||
145 | # Define NO_EXTERNAL_GREP if you don't want "perf grep" to ever call | 146 | # Define NO_EXTERNAL_GREP if you don't want "perf grep" to ever call |
146 | # your external grep (e.g., if your system lacks grep, if its grep is | 147 | # your external grep (e.g., if your system lacks grep, if its grep is |
147 | # broken, or spawning external process is slower than built-in grep perf has). | 148 | # broken, or spawning external process is slower than built-in grep perf has). |
149 | # | ||
150 | # Define LDFLAGS=-static to build a static binary. | ||
151 | # | ||
152 | # Define EXTRA_CFLAGS=-m64 or EXTRA_CFLAGS=-m32 as appropriate for cross-builds. | ||
148 | 153 | ||
149 | PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 154 | PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
150 | @$(SHELL_PATH) util/PERF-VERSION-GEN | 155 | @$(SHELL_PATH) util/PERF-VERSION-GEN |
@@ -157,20 +162,6 @@ uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') | |||
157 | uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') | 162 | uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not') |
158 | uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') | 163 | uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not') |
159 | 164 | ||
160 | # | ||
161 | # Add -m32 for cross-builds: | ||
162 | # | ||
163 | ifdef NO_64BIT | ||
164 | MBITS := -m32 | ||
165 | else | ||
166 | # | ||
167 | # If we're on a 64-bit kernel, use -m64: | ||
168 | # | ||
169 | ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M)) | ||
170 | MBITS := -m64 | ||
171 | endif | ||
172 | endif | ||
173 | |||
174 | # CFLAGS and LDFLAGS are for the users to override from the command line. | 165 | # CFLAGS and LDFLAGS are for the users to override from the command line. |
175 | 166 | ||
176 | # | 167 | # |
@@ -200,8 +191,15 @@ EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition | |||
200 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes | 191 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes |
201 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement | 192 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement |
202 | 193 | ||
203 | CFLAGS = $(MBITS) -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -fstack-protector-all -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) | 194 | ifeq ("$(origin DEBUG)", "command line") |
204 | LDFLAGS = -lpthread -lrt -lelf -lm | 195 | PERF_DEBUG = $(DEBUG) |
196 | endif | ||
197 | ifndef PERF_DEBUG | ||
198 | CFLAGS_OPTIMIZE = -O6 | ||
199 | endif | ||
200 | |||
201 | CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) | ||
202 | EXTLIBS = -lpthread -lrt -lelf -lm | ||
205 | ALL_CFLAGS = $(CFLAGS) | 203 | ALL_CFLAGS = $(CFLAGS) |
206 | ALL_LDFLAGS = $(LDFLAGS) | 204 | ALL_LDFLAGS = $(LDFLAGS) |
207 | STRIP ?= strip | 205 | STRIP ?= strip |
@@ -239,8 +237,8 @@ lib = lib | |||
239 | 237 | ||
240 | export prefix bindir sharedir sysconfdir | 238 | export prefix bindir sharedir sysconfdir |
241 | 239 | ||
242 | CC = gcc | 240 | CC = $(CROSS_COMPILE)gcc |
243 | AR = ar | 241 | AR = $(CROSS_COMPILE)ar |
244 | RM = rm -f | 242 | RM = rm -f |
245 | TAR = tar | 243 | TAR = tar |
246 | FIND = find | 244 | FIND = find |
@@ -252,6 +250,21 @@ PTHREAD_LIBS = -lpthread | |||
252 | # explicitly what architecture to check for. Fix this up for yours.. | 250 | # explicitly what architecture to check for. Fix this up for yours.. |
253 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ | 251 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ |
254 | 252 | ||
253 | ifeq ($(V), 2) | ||
254 | QUIET_STDERR = ">/dev/null" | ||
255 | else | ||
256 | QUIET_STDERR = ">/dev/null 2>&1" | ||
257 | endif | ||
258 | |||
259 | BITBUCKET = "/dev/null" | ||
260 | |||
261 | ifneq ($(shell sh -c "(echo '\#include <stdio.h>'; echo 'int main(void) { return puts(\"hi\"); }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) "$(QUIET_STDERR)" && echo y"), y) | ||
262 | BITBUCKET = .perf.dev.null | ||
263 | endif | ||
264 | |||
265 | ifeq ($(shell sh -c "echo 'int foo(void) {char X[2]; return 3;}' | $(CC) -x c -c -Werror -fstack-protector-all - -o $(BITBUCKET) "$(QUIET_STDERR)" && echo y"), y) | ||
266 | CFLAGS := $(CFLAGS) -fstack-protector-all | ||
267 | endif | ||
255 | 268 | ||
256 | 269 | ||
257 | ### --- END CONFIGURATION SECTION --- | 270 | ### --- END CONFIGURATION SECTION --- |
@@ -273,11 +286,7 @@ SCRIPT_PERL = | |||
273 | SCRIPT_SH = | 286 | SCRIPT_SH = |
274 | TEST_PROGRAMS = | 287 | TEST_PROGRAMS = |
275 | 288 | ||
276 | # | 289 | SCRIPT_SH += perf-archive.sh |
277 | # No scripts right now: | ||
278 | # | ||
279 | |||
280 | # SCRIPT_SH += perf-am.sh | ||
281 | 290 | ||
282 | # | 291 | # |
283 | # No Perl scripts right now: | 292 | # No Perl scripts right now: |
@@ -302,9 +311,6 @@ PROGRAMS += perf | |||
302 | # List built-in command $C whose implementation cmd_$C() is not in | 311 | # List built-in command $C whose implementation cmd_$C() is not in |
303 | # builtin-$C.o but is linked in as part of some other command. | 312 | # builtin-$C.o but is linked in as part of some other command. |
304 | # | 313 | # |
305 | # None right now: | ||
306 | # | ||
307 | # BUILT_INS += perf-init $X | ||
308 | 314 | ||
309 | # what 'all' will build and 'install' will install, in perfexecdir | 315 | # what 'all' will build and 'install' will install, in perfexecdir |
310 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) | 316 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) |
@@ -327,30 +333,69 @@ LIB_FILE=libperf.a | |||
327 | LIB_H += ../../include/linux/perf_event.h | 333 | LIB_H += ../../include/linux/perf_event.h |
328 | LIB_H += ../../include/linux/rbtree.h | 334 | LIB_H += ../../include/linux/rbtree.h |
329 | LIB_H += ../../include/linux/list.h | 335 | LIB_H += ../../include/linux/list.h |
336 | LIB_H += ../../include/linux/hash.h | ||
337 | LIB_H += ../../include/linux/stringify.h | ||
338 | LIB_H += util/include/linux/bitmap.h | ||
339 | LIB_H += util/include/linux/bitops.h | ||
340 | LIB_H += util/include/linux/compiler.h | ||
341 | LIB_H += util/include/linux/ctype.h | ||
342 | LIB_H += util/include/linux/kernel.h | ||
330 | LIB_H += util/include/linux/list.h | 343 | LIB_H += util/include/linux/list.h |
344 | LIB_H += util/include/linux/module.h | ||
345 | LIB_H += util/include/linux/poison.h | ||
346 | LIB_H += util/include/linux/prefetch.h | ||
347 | LIB_H += util/include/linux/rbtree.h | ||
348 | LIB_H += util/include/linux/string.h | ||
349 | LIB_H += util/include/linux/types.h | ||
350 | LIB_H += util/include/asm/asm-offsets.h | ||
351 | LIB_H += util/include/asm/bitops.h | ||
352 | LIB_H += util/include/asm/bug.h | ||
353 | LIB_H += util/include/asm/byteorder.h | ||
354 | LIB_H += util/include/asm/swab.h | ||
355 | LIB_H += util/include/asm/system.h | ||
356 | LIB_H += util/include/asm/uaccess.h | ||
331 | LIB_H += perf.h | 357 | LIB_H += perf.h |
358 | LIB_H += util/cache.h | ||
359 | LIB_H += util/callchain.h | ||
360 | LIB_H += util/build-id.h | ||
361 | LIB_H += util/debug.h | ||
362 | LIB_H += util/debugfs.h | ||
363 | LIB_H += util/event.h | ||
364 | LIB_H += util/exec_cmd.h | ||
332 | LIB_H += util/types.h | 365 | LIB_H += util/types.h |
333 | LIB_H += util/levenshtein.h | 366 | LIB_H += util/levenshtein.h |
367 | LIB_H += util/map.h | ||
334 | LIB_H += util/parse-options.h | 368 | LIB_H += util/parse-options.h |
335 | LIB_H += util/parse-events.h | 369 | LIB_H += util/parse-events.h |
336 | LIB_H += util/quote.h | 370 | LIB_H += util/quote.h |
337 | LIB_H += util/util.h | 371 | LIB_H += util/util.h |
372 | LIB_H += util/header.h | ||
338 | LIB_H += util/help.h | 373 | LIB_H += util/help.h |
374 | LIB_H += util/session.h | ||
339 | LIB_H += util/strbuf.h | 375 | LIB_H += util/strbuf.h |
340 | LIB_H += util/string.h | 376 | LIB_H += util/string.h |
341 | LIB_H += util/strlist.h | 377 | LIB_H += util/strlist.h |
378 | LIB_H += util/svghelper.h | ||
342 | LIB_H += util/run-command.h | 379 | LIB_H += util/run-command.h |
343 | LIB_H += util/sigchain.h | 380 | LIB_H += util/sigchain.h |
344 | LIB_H += util/symbol.h | 381 | LIB_H += util/symbol.h |
345 | LIB_H += util/module.h | ||
346 | LIB_H += util/color.h | 382 | LIB_H += util/color.h |
347 | LIB_H += util/values.h | 383 | LIB_H += util/values.h |
384 | LIB_H += util/sort.h | ||
385 | LIB_H += util/hist.h | ||
386 | LIB_H += util/thread.h | ||
387 | LIB_H += util/trace-event.h | ||
388 | LIB_H += util/probe-finder.h | ||
389 | LIB_H += util/probe-event.h | ||
348 | 390 | ||
349 | LIB_OBJS += util/abspath.o | 391 | LIB_OBJS += util/abspath.o |
350 | LIB_OBJS += util/alias.o | 392 | LIB_OBJS += util/alias.o |
393 | LIB_OBJS += util/build-id.o | ||
351 | LIB_OBJS += util/config.o | 394 | LIB_OBJS += util/config.o |
352 | LIB_OBJS += util/ctype.o | 395 | LIB_OBJS += util/ctype.o |
396 | LIB_OBJS += util/debugfs.o | ||
353 | LIB_OBJS += util/environment.o | 397 | LIB_OBJS += util/environment.o |
398 | LIB_OBJS += util/event.o | ||
354 | LIB_OBJS += util/exec_cmd.o | 399 | LIB_OBJS += util/exec_cmd.o |
355 | LIB_OBJS += util/help.o | 400 | LIB_OBJS += util/help.o |
356 | LIB_OBJS += util/levenshtein.o | 401 | LIB_OBJS += util/levenshtein.o |
@@ -358,6 +403,9 @@ LIB_OBJS += util/parse-options.o | |||
358 | LIB_OBJS += util/parse-events.o | 403 | LIB_OBJS += util/parse-events.o |
359 | LIB_OBJS += util/path.o | 404 | LIB_OBJS += util/path.o |
360 | LIB_OBJS += util/rbtree.o | 405 | LIB_OBJS += util/rbtree.o |
406 | LIB_OBJS += util/bitmap.o | ||
407 | LIB_OBJS += util/hweight.o | ||
408 | LIB_OBJS += util/find_next_bit.o | ||
361 | LIB_OBJS += util/run-command.o | 409 | LIB_OBJS += util/run-command.o |
362 | LIB_OBJS += util/quote.o | 410 | LIB_OBJS += util/quote.o |
363 | LIB_OBJS += util/strbuf.o | 411 | LIB_OBJS += util/strbuf.o |
@@ -367,7 +415,6 @@ LIB_OBJS += util/usage.o | |||
367 | LIB_OBJS += util/wrapper.o | 415 | LIB_OBJS += util/wrapper.o |
368 | LIB_OBJS += util/sigchain.o | 416 | LIB_OBJS += util/sigchain.o |
369 | LIB_OBJS += util/symbol.o | 417 | LIB_OBJS += util/symbol.o |
370 | LIB_OBJS += util/module.o | ||
371 | LIB_OBJS += util/color.o | 418 | LIB_OBJS += util/color.o |
372 | LIB_OBJS += util/pager.o | 419 | LIB_OBJS += util/pager.o |
373 | LIB_OBJS += util/header.o | 420 | LIB_OBJS += util/header.o |
@@ -375,15 +422,32 @@ LIB_OBJS += util/callchain.o | |||
375 | LIB_OBJS += util/values.o | 422 | LIB_OBJS += util/values.o |
376 | LIB_OBJS += util/debug.o | 423 | LIB_OBJS += util/debug.o |
377 | LIB_OBJS += util/map.o | 424 | LIB_OBJS += util/map.o |
425 | LIB_OBJS += util/session.o | ||
378 | LIB_OBJS += util/thread.o | 426 | LIB_OBJS += util/thread.o |
379 | LIB_OBJS += util/trace-event-parse.o | 427 | LIB_OBJS += util/trace-event-parse.o |
380 | LIB_OBJS += util/trace-event-read.o | 428 | LIB_OBJS += util/trace-event-read.o |
381 | LIB_OBJS += util/trace-event-info.o | 429 | LIB_OBJS += util/trace-event-info.o |
430 | LIB_OBJS += util/trace-event-scripting.o | ||
382 | LIB_OBJS += util/svghelper.o | 431 | LIB_OBJS += util/svghelper.o |
432 | LIB_OBJS += util/sort.o | ||
433 | LIB_OBJS += util/hist.o | ||
434 | LIB_OBJS += util/probe-event.o | ||
435 | LIB_OBJS += util/util.o | ||
383 | 436 | ||
384 | BUILTIN_OBJS += builtin-annotate.o | 437 | BUILTIN_OBJS += builtin-annotate.o |
438 | |||
439 | BUILTIN_OBJS += builtin-bench.o | ||
440 | |||
441 | # Benchmark modules | ||
442 | BUILTIN_OBJS += bench/sched-messaging.o | ||
443 | BUILTIN_OBJS += bench/sched-pipe.o | ||
444 | BUILTIN_OBJS += bench/mem-memcpy.o | ||
445 | |||
446 | BUILTIN_OBJS += builtin-diff.o | ||
385 | BUILTIN_OBJS += builtin-help.o | 447 | BUILTIN_OBJS += builtin-help.o |
386 | BUILTIN_OBJS += builtin-sched.o | 448 | BUILTIN_OBJS += builtin-sched.o |
449 | BUILTIN_OBJS += builtin-buildid-list.o | ||
450 | BUILTIN_OBJS += builtin-buildid-cache.o | ||
387 | BUILTIN_OBJS += builtin-list.o | 451 | BUILTIN_OBJS += builtin-list.o |
388 | BUILTIN_OBJS += builtin-record.o | 452 | BUILTIN_OBJS += builtin-record.o |
389 | BUILTIN_OBJS += builtin-report.o | 453 | BUILTIN_OBJS += builtin-report.o |
@@ -391,6 +455,9 @@ BUILTIN_OBJS += builtin-stat.o | |||
391 | BUILTIN_OBJS += builtin-timechart.o | 455 | BUILTIN_OBJS += builtin-timechart.o |
392 | BUILTIN_OBJS += builtin-top.o | 456 | BUILTIN_OBJS += builtin-top.o |
393 | BUILTIN_OBJS += builtin-trace.o | 457 | BUILTIN_OBJS += builtin-trace.o |
458 | BUILTIN_OBJS += builtin-probe.o | ||
459 | BUILTIN_OBJS += builtin-kmem.o | ||
460 | BUILTIN_OBJS += builtin-lock.o | ||
394 | 461 | ||
395 | PERFLIBS = $(LIB_FILE) | 462 | PERFLIBS = $(LIB_FILE) |
396 | 463 | ||
@@ -421,36 +488,75 @@ ifeq ($(uname_S),Darwin) | |||
421 | PTHREAD_LIBS = | 488 | PTHREAD_LIBS = |
422 | endif | 489 | endif |
423 | 490 | ||
424 | ifeq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) | 491 | ifeq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) |
425 | ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) | 492 | ifneq ($(shell sh -c "(echo '\#include <gnu/libc-version.h>'; echo 'int main(void) { const char * version = gnu_get_libc_version(); return (long)version; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) |
493 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | ||
494 | endif | ||
495 | |||
496 | ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) | ||
426 | BASIC_CFLAGS += -DLIBELF_NO_MMAP | 497 | BASIC_CFLAGS += -DLIBELF_NO_MMAP |
427 | endif | 498 | endif |
428 | else | 499 | else |
429 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]); | 500 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]); |
430 | endif | 501 | endif |
431 | 502 | ||
503 | ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) | ||
504 | msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev); | ||
505 | BASIC_CFLAGS += -DNO_DWARF_SUPPORT | ||
506 | else | ||
507 | BASIC_CFLAGS += -I/usr/include/elfutils | ||
508 | EXTLIBS += -lelf -ldw | ||
509 | LIB_OBJS += util/probe-finder.o | ||
510 | endif | ||
511 | |||
512 | ifndef NO_LIBPERL | ||
513 | PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` | ||
514 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` | ||
515 | endif | ||
516 | |||
517 | ifneq ($(shell sh -c "(echo '\#include <EXTERN.h>'; echo '\#include <perl.h>'; echo 'int main(void) { perl_alloc(); return 0; }') | $(CC) -x c - $(PERL_EMBED_CCOPTS) -o $(BITBUCKET) $(PERL_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) | ||
518 | BASIC_CFLAGS += -DNO_LIBPERL | ||
519 | else | ||
520 | ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) | ||
521 | LIB_OBJS += util/scripting-engines/trace-event-perl.o | ||
522 | LIB_OBJS += scripts/perl/Perf-Trace-Util/Context.o | ||
523 | endif | ||
524 | |||
525 | ifndef NO_LIBPYTHON | ||
526 | PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` | ||
527 | PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` | ||
528 | endif | ||
529 | |||
530 | ifneq ($(shell sh -c "(echo '\#include <Python.h>'; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o /dev/null $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) | ||
531 | BASIC_CFLAGS += -DNO_LIBPYTHON | ||
532 | else | ||
533 | ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) | ||
534 | LIB_OBJS += util/scripting-engines/trace-event-python.o | ||
535 | LIB_OBJS += scripts/python/Perf-Trace-Util/Context.o | ||
536 | endif | ||
537 | |||
432 | ifdef NO_DEMANGLE | 538 | ifdef NO_DEMANGLE |
433 | BASIC_CFLAGS += -DNO_DEMANGLE | 539 | BASIC_CFLAGS += -DNO_DEMANGLE |
434 | else | 540 | else |
435 | has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd > /dev/null 2>&1 && echo y") | 541 | has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd "$(QUIET_STDERR)" && echo y") |
436 | 542 | ||
437 | ifeq ($(has_bfd),y) | 543 | ifeq ($(has_bfd),y) |
438 | EXTLIBS += -lbfd | 544 | EXTLIBS += -lbfd |
439 | else | 545 | else |
440 | has_bfd_iberty := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty > /dev/null 2>&1 && echo y") | 546 | has_bfd_iberty := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd -liberty "$(QUIET_STDERR)" && echo y") |
441 | ifeq ($(has_bfd_iberty),y) | 547 | ifeq ($(has_bfd_iberty),y) |
442 | EXTLIBS += -lbfd -liberty | 548 | EXTLIBS += -lbfd -liberty |
443 | else | 549 | else |
444 | has_bfd_iberty_z := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty -lz > /dev/null 2>&1 && echo y") | 550 | has_bfd_iberty_z := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd -liberty -lz "$(QUIET_STDERR)" && echo y") |
445 | ifeq ($(has_bfd_iberty_z),y) | 551 | ifeq ($(has_bfd_iberty_z),y) |
446 | EXTLIBS += -lbfd -liberty -lz | 552 | EXTLIBS += -lbfd -liberty -lz |
447 | else | 553 | else |
448 | has_cplus_demangle := $(shell sh -c "(echo 'extern char *cplus_demangle(const char *, int);'; echo 'int main(void) { cplus_demangle(0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -liberty > /dev/null 2>&1 && echo y") | 554 | has_cplus_demangle := $(shell sh -c "(echo 'extern char *cplus_demangle(const char *, int);'; echo 'int main(void) { cplus_demangle(0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -liberty "$(QUIET_STDERR)" && echo y") |
449 | ifeq ($(has_cplus_demangle),y) | 555 | ifeq ($(has_cplus_demangle),y) |
450 | EXTLIBS += -liberty | 556 | EXTLIBS += -liberty |
451 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 557 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
452 | else | 558 | else |
453 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el] to gain symbol demangling) | 559 | msg := $(warning No bfd.h/libbfd found, install binutils-dev[el]/zlib-static to gain symbol demangling) |
454 | BASIC_CFLAGS += -DNO_DEMANGLE | 560 | BASIC_CFLAGS += -DNO_DEMANGLE |
455 | endif | 561 | endif |
456 | endif | 562 | endif |
@@ -693,7 +799,7 @@ export TAR INSTALL DESTDIR SHELL_PATH | |||
693 | 799 | ||
694 | SHELL = $(SHELL_PATH) | 800 | SHELL = $(SHELL_PATH) |
695 | 801 | ||
696 | all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) PERF-BUILD-OPTIONS | 802 | all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) PERF-BUILD-OPTIONS |
697 | ifneq (,$X) | 803 | ifneq (,$X) |
698 | $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) | 804 | $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) |
699 | endif | 805 | endif |
@@ -787,6 +893,31 @@ util/config.o: util/config.c PERF-CFLAGS | |||
787 | util/rbtree.o: ../../lib/rbtree.c PERF-CFLAGS | 893 | util/rbtree.o: ../../lib/rbtree.c PERF-CFLAGS |
788 | $(QUIET_CC)$(CC) -o util/rbtree.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 894 | $(QUIET_CC)$(CC) -o util/rbtree.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
789 | 895 | ||
896 | # some perf warning policies can't fit to lib/bitmap.c, eg: it warns about variable shadowing | ||
897 | # from <string.h> that comes from kernel headers wrapping. | ||
898 | KBITMAP_FLAGS=`echo $(ALL_CFLAGS) | sed s/-Wshadow// | sed s/-Wswitch-default// | sed s/-Wextra//` | ||
899 | |||
900 | util/bitmap.o: ../../lib/bitmap.c PERF-CFLAGS | ||
901 | $(QUIET_CC)$(CC) -o util/bitmap.o -c $(KBITMAP_FLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | ||
902 | |||
903 | util/hweight.o: ../../lib/hweight.c PERF-CFLAGS | ||
904 | $(QUIET_CC)$(CC) -o util/hweight.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | ||
905 | |||
906 | util/find_next_bit.o: ../../lib/find_next_bit.c PERF-CFLAGS | ||
907 | $(QUIET_CC)$(CC) -o util/find_next_bit.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | ||
908 | |||
909 | util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c PERF-CFLAGS | ||
910 | $(QUIET_CC)$(CC) -o util/scripting-engines/trace-event-perl.o -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< | ||
911 | |||
912 | scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c PERF-CFLAGS | ||
913 | $(QUIET_CC)$(CC) -o scripts/perl/Perf-Trace-Util/Context.o -c $(ALL_CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< | ||
914 | |||
915 | util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c PERF-CFLAGS | ||
916 | $(QUIET_CC)$(CC) -o util/scripting-engines/trace-event-python.o -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< | ||
917 | |||
918 | scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Util/Context.c PERF-CFLAGS | ||
919 | $(QUIET_CC)$(CC) -o scripts/python/Perf-Trace-Util/Context.o -c $(ALL_CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< | ||
920 | |||
790 | perf-%$X: %.o $(PERFLIBS) | 921 | perf-%$X: %.o $(PERFLIBS) |
791 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) | 922 | $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) |
792 | 923 | ||
@@ -894,6 +1025,18 @@ export perfexec_instdir | |||
894 | install: all | 1025 | install: all |
895 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' | 1026 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' |
896 | $(INSTALL) perf$X '$(DESTDIR_SQ)$(bindir_SQ)' | 1027 | $(INSTALL) perf$X '$(DESTDIR_SQ)$(bindir_SQ)' |
1028 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | ||
1029 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | ||
1030 | $(INSTALL) perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
1031 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | ||
1032 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' | ||
1033 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | ||
1034 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' | ||
1035 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | ||
1036 | $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' | ||
1037 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' | ||
1038 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | ||
1039 | |||
897 | ifdef BUILT_INS | 1040 | ifdef BUILT_INS |
898 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 1041 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
899 | $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 1042 | $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
@@ -979,7 +1122,7 @@ distclean: clean | |||
979 | # $(RM) configure | 1122 | # $(RM) configure |
980 | 1123 | ||
981 | clean: | 1124 | clean: |
982 | $(RM) *.o */*.o $(LIB_FILE) | 1125 | $(RM) *.o */*.o */*/*.o */*/*/*.o $(LIB_FILE) |
983 | $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X | 1126 | $(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X |
984 | $(RM) $(TEST_PROGRAMS) | 1127 | $(RM) $(TEST_PROGRAMS) |
985 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope* | 1128 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope* |
@@ -996,6 +1139,11 @@ clean: | |||
996 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS | 1139 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS |
997 | .PHONY: .FORCE-PERF-BUILD-OPTIONS | 1140 | .PHONY: .FORCE-PERF-BUILD-OPTIONS |
998 | 1141 | ||
1142 | .perf.dev.null: | ||
1143 | touch .perf.dev.null | ||
1144 | |||
1145 | .INTERMEDIATE: .perf.dev.null | ||
1146 | |||
999 | ### Make sure built-ins do not have dups and listed in perf.c | 1147 | ### Make sure built-ins do not have dups and listed in perf.c |
1000 | # | 1148 | # |
1001 | check-builtins:: | 1149 | check-builtins:: |
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h new file mode 100644 index 000000000000..f7781c6267c0 --- /dev/null +++ b/tools/perf/bench/bench.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef BENCH_H | ||
2 | #define BENCH_H | ||
3 | |||
4 | extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); | ||
5 | extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); | ||
6 | extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used); | ||
7 | |||
8 | #define BENCH_FORMAT_DEFAULT_STR "default" | ||
9 | #define BENCH_FORMAT_DEFAULT 0 | ||
10 | #define BENCH_FORMAT_SIMPLE_STR "simple" | ||
11 | #define BENCH_FORMAT_SIMPLE 1 | ||
12 | |||
13 | #define BENCH_FORMAT_UNKNOWN -1 | ||
14 | |||
15 | extern int bench_format; | ||
16 | |||
17 | #endif | ||
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c new file mode 100644 index 000000000000..89773178e894 --- /dev/null +++ b/tools/perf/bench/mem-memcpy.c | |||
@@ -0,0 +1,193 @@ | |||
1 | /* | ||
2 | * mem-memcpy.c | ||
3 | * | ||
4 | * memcpy: Simple memory copy in various ways | ||
5 | * | ||
6 | * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> | ||
7 | */ | ||
8 | #include <ctype.h> | ||
9 | |||
10 | #include "../perf.h" | ||
11 | #include "../util/util.h" | ||
12 | #include "../util/parse-options.h" | ||
13 | #include "../util/string.h" | ||
14 | #include "../util/header.h" | ||
15 | #include "bench.h" | ||
16 | |||
17 | #include <stdio.h> | ||
18 | #include <stdlib.h> | ||
19 | #include <string.h> | ||
20 | #include <sys/time.h> | ||
21 | #include <errno.h> | ||
22 | |||
23 | #define K 1024 | ||
24 | |||
25 | static const char *length_str = "1MB"; | ||
26 | static const char *routine = "default"; | ||
27 | static int use_clock = 0; | ||
28 | static int clock_fd; | ||
29 | |||
30 | static const struct option options[] = { | ||
31 | OPT_STRING('l', "length", &length_str, "1MB", | ||
32 | "Specify length of memory to copy. " | ||
33 | "available unit: B, MB, GB (upper and lower)"), | ||
34 | OPT_STRING('r', "routine", &routine, "default", | ||
35 | "Specify routine to copy"), | ||
36 | OPT_BOOLEAN('c', "clock", &use_clock, | ||
37 | "Use CPU clock for measuring"), | ||
38 | OPT_END() | ||
39 | }; | ||
40 | |||
41 | struct routine { | ||
42 | const char *name; | ||
43 | const char *desc; | ||
44 | void * (*fn)(void *dst, const void *src, size_t len); | ||
45 | }; | ||
46 | |||
47 | struct routine routines[] = { | ||
48 | { "default", | ||
49 | "Default memcpy() provided by glibc", | ||
50 | memcpy }, | ||
51 | { NULL, | ||
52 | NULL, | ||
53 | NULL } | ||
54 | }; | ||
55 | |||
56 | static const char * const bench_mem_memcpy_usage[] = { | ||
57 | "perf bench mem memcpy <options>", | ||
58 | NULL | ||
59 | }; | ||
60 | |||
61 | static struct perf_event_attr clock_attr = { | ||
62 | .type = PERF_TYPE_HARDWARE, | ||
63 | .config = PERF_COUNT_HW_CPU_CYCLES | ||
64 | }; | ||
65 | |||
66 | static void init_clock(void) | ||
67 | { | ||
68 | clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); | ||
69 | |||
70 | if (clock_fd < 0 && errno == ENOSYS) | ||
71 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | ||
72 | else | ||
73 | BUG_ON(clock_fd < 0); | ||
74 | } | ||
75 | |||
76 | static u64 get_clock(void) | ||
77 | { | ||
78 | int ret; | ||
79 | u64 clk; | ||
80 | |||
81 | ret = read(clock_fd, &clk, sizeof(u64)); | ||
82 | BUG_ON(ret != sizeof(u64)); | ||
83 | |||
84 | return clk; | ||
85 | } | ||
86 | |||
87 | static double timeval2double(struct timeval *ts) | ||
88 | { | ||
89 | return (double)ts->tv_sec + | ||
90 | (double)ts->tv_usec / (double)1000000; | ||
91 | } | ||
92 | |||
93 | int bench_mem_memcpy(int argc, const char **argv, | ||
94 | const char *prefix __used) | ||
95 | { | ||
96 | int i; | ||
97 | void *dst, *src; | ||
98 | size_t length; | ||
99 | double bps = 0.0; | ||
100 | struct timeval tv_start, tv_end, tv_diff; | ||
101 | u64 clock_start, clock_end, clock_diff; | ||
102 | |||
103 | clock_start = clock_end = clock_diff = 0ULL; | ||
104 | argc = parse_options(argc, argv, options, | ||
105 | bench_mem_memcpy_usage, 0); | ||
106 | |||
107 | tv_diff.tv_sec = 0; | ||
108 | tv_diff.tv_usec = 0; | ||
109 | length = (size_t)perf_atoll((char *)length_str); | ||
110 | |||
111 | if ((s64)length <= 0) { | ||
112 | fprintf(stderr, "Invalid length:%s\n", length_str); | ||
113 | return 1; | ||
114 | } | ||
115 | |||
116 | for (i = 0; routines[i].name; i++) { | ||
117 | if (!strcmp(routines[i].name, routine)) | ||
118 | break; | ||
119 | } | ||
120 | if (!routines[i].name) { | ||
121 | printf("Unknown routine:%s\n", routine); | ||
122 | printf("Available routines...\n"); | ||
123 | for (i = 0; routines[i].name; i++) { | ||
124 | printf("\t%s ... %s\n", | ||
125 | routines[i].name, routines[i].desc); | ||
126 | } | ||
127 | return 1; | ||
128 | } | ||
129 | |||
130 | dst = zalloc(length); | ||
131 | if (!dst) | ||
132 | die("memory allocation failed - maybe length is too large?\n"); | ||
133 | |||
134 | src = zalloc(length); | ||
135 | if (!src) | ||
136 | die("memory allocation failed - maybe length is too large?\n"); | ||
137 | |||
138 | if (bench_format == BENCH_FORMAT_DEFAULT) { | ||
139 | printf("# Copying %s Bytes from %p to %p ...\n\n", | ||
140 | length_str, src, dst); | ||
141 | } | ||
142 | |||
143 | if (use_clock) { | ||
144 | init_clock(); | ||
145 | clock_start = get_clock(); | ||
146 | } else { | ||
147 | BUG_ON(gettimeofday(&tv_start, NULL)); | ||
148 | } | ||
149 | |||
150 | routines[i].fn(dst, src, length); | ||
151 | |||
152 | if (use_clock) { | ||
153 | clock_end = get_clock(); | ||
154 | clock_diff = clock_end - clock_start; | ||
155 | } else { | ||
156 | BUG_ON(gettimeofday(&tv_end, NULL)); | ||
157 | timersub(&tv_end, &tv_start, &tv_diff); | ||
158 | bps = (double)((double)length / timeval2double(&tv_diff)); | ||
159 | } | ||
160 | |||
161 | switch (bench_format) { | ||
162 | case BENCH_FORMAT_DEFAULT: | ||
163 | if (use_clock) { | ||
164 | printf(" %14lf Clock/Byte\n", | ||
165 | (double)clock_diff / (double)length); | ||
166 | } else { | ||
167 | if (bps < K) | ||
168 | printf(" %14lf B/Sec\n", bps); | ||
169 | else if (bps < K * K) | ||
170 | printf(" %14lfd KB/Sec\n", bps / 1024); | ||
171 | else if (bps < K * K * K) | ||
172 | printf(" %14lf MB/Sec\n", bps / 1024 / 1024); | ||
173 | else { | ||
174 | printf(" %14lf GB/Sec\n", | ||
175 | bps / 1024 / 1024 / 1024); | ||
176 | } | ||
177 | } | ||
178 | break; | ||
179 | case BENCH_FORMAT_SIMPLE: | ||
180 | if (use_clock) { | ||
181 | printf("%14lf\n", | ||
182 | (double)clock_diff / (double)length); | ||
183 | } else | ||
184 | printf("%lf\n", bps); | ||
185 | break; | ||
186 | default: | ||
187 | /* reaching this means there's some disaster: */ | ||
188 | die("unknown format: %d\n", bench_format); | ||
189 | break; | ||
190 | } | ||
191 | |||
192 | return 0; | ||
193 | } | ||
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c new file mode 100644 index 000000000000..81cee78181fa --- /dev/null +++ b/tools/perf/bench/sched-messaging.c | |||
@@ -0,0 +1,338 @@ | |||
1 | /* | ||
2 | * | ||
3 | * sched-messaging.c | ||
4 | * | ||
5 | * messaging: Benchmark for scheduler and IPC mechanisms | ||
6 | * | ||
7 | * Based on hackbench by Rusty Russell <rusty@rustcorp.com.au> | ||
8 | * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include "../perf.h" | ||
13 | #include "../util/util.h" | ||
14 | #include "../util/parse-options.h" | ||
15 | #include "../builtin.h" | ||
16 | #include "bench.h" | ||
17 | |||
18 | /* Test groups of 20 processes spraying to 20 receivers */ | ||
19 | #include <pthread.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <string.h> | ||
23 | #include <errno.h> | ||
24 | #include <unistd.h> | ||
25 | #include <sys/types.h> | ||
26 | #include <sys/socket.h> | ||
27 | #include <sys/wait.h> | ||
28 | #include <sys/time.h> | ||
29 | #include <sys/poll.h> | ||
30 | #include <limits.h> | ||
31 | |||
32 | #define DATASIZE 100 | ||
33 | |||
34 | static int use_pipes = 0; | ||
35 | static unsigned int loops = 100; | ||
36 | static unsigned int thread_mode = 0; | ||
37 | static unsigned int num_groups = 10; | ||
38 | |||
39 | struct sender_context { | ||
40 | unsigned int num_fds; | ||
41 | int ready_out; | ||
42 | int wakefd; | ||
43 | int out_fds[0]; | ||
44 | }; | ||
45 | |||
46 | struct receiver_context { | ||
47 | unsigned int num_packets; | ||
48 | int in_fds[2]; | ||
49 | int ready_out; | ||
50 | int wakefd; | ||
51 | }; | ||
52 | |||
53 | static void barf(const char *msg) | ||
54 | { | ||
55 | fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno)); | ||
56 | exit(1); | ||
57 | } | ||
58 | |||
59 | static void fdpair(int fds[2]) | ||
60 | { | ||
61 | if (use_pipes) { | ||
62 | if (pipe(fds) == 0) | ||
63 | return; | ||
64 | } else { | ||
65 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0) | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | barf(use_pipes ? "pipe()" : "socketpair()"); | ||
70 | } | ||
71 | |||
72 | /* Block until we're ready to go */ | ||
73 | static void ready(int ready_out, int wakefd) | ||
74 | { | ||
75 | char dummy; | ||
76 | struct pollfd pollfd = { .fd = wakefd, .events = POLLIN }; | ||
77 | |||
78 | /* Tell them we're ready. */ | ||
79 | if (write(ready_out, &dummy, 1) != 1) | ||
80 | barf("CLIENT: ready write"); | ||
81 | |||
82 | /* Wait for "GO" signal */ | ||
83 | if (poll(&pollfd, 1, -1) != 1) | ||
84 | barf("poll"); | ||
85 | } | ||
86 | |||
87 | /* Sender sprays loops messages down each file descriptor */ | ||
88 | static void *sender(struct sender_context *ctx) | ||
89 | { | ||
90 | char data[DATASIZE]; | ||
91 | unsigned int i, j; | ||
92 | |||
93 | ready(ctx->ready_out, ctx->wakefd); | ||
94 | |||
95 | /* Now pump to every receiver. */ | ||
96 | for (i = 0; i < loops; i++) { | ||
97 | for (j = 0; j < ctx->num_fds; j++) { | ||
98 | int ret, done = 0; | ||
99 | |||
100 | again: | ||
101 | ret = write(ctx->out_fds[j], data + done, | ||
102 | sizeof(data)-done); | ||
103 | if (ret < 0) | ||
104 | barf("SENDER: write"); | ||
105 | done += ret; | ||
106 | if (done < DATASIZE) | ||
107 | goto again; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | return NULL; | ||
112 | } | ||
113 | |||
114 | |||
115 | /* One receiver per fd */ | ||
116 | static void *receiver(struct receiver_context* ctx) | ||
117 | { | ||
118 | unsigned int i; | ||
119 | |||
120 | if (!thread_mode) | ||
121 | close(ctx->in_fds[1]); | ||
122 | |||
123 | /* Wait for start... */ | ||
124 | ready(ctx->ready_out, ctx->wakefd); | ||
125 | |||
126 | /* Receive them all */ | ||
127 | for (i = 0; i < ctx->num_packets; i++) { | ||
128 | char data[DATASIZE]; | ||
129 | int ret, done = 0; | ||
130 | |||
131 | again: | ||
132 | ret = read(ctx->in_fds[0], data + done, DATASIZE - done); | ||
133 | if (ret < 0) | ||
134 | barf("SERVER: read"); | ||
135 | done += ret; | ||
136 | if (done < DATASIZE) | ||
137 | goto again; | ||
138 | } | ||
139 | |||
140 | return NULL; | ||
141 | } | ||
142 | |||
143 | static pthread_t create_worker(void *ctx, void *(*func)(void *)) | ||
144 | { | ||
145 | pthread_attr_t attr; | ||
146 | pthread_t childid; | ||
147 | int err; | ||
148 | |||
149 | if (!thread_mode) { | ||
150 | /* process mode */ | ||
151 | /* Fork the receiver. */ | ||
152 | switch (fork()) { | ||
153 | case -1: | ||
154 | barf("fork()"); | ||
155 | break; | ||
156 | case 0: | ||
157 | (*func) (ctx); | ||
158 | exit(0); | ||
159 | break; | ||
160 | default: | ||
161 | break; | ||
162 | } | ||
163 | |||
164 | return (pthread_t)0; | ||
165 | } | ||
166 | |||
167 | if (pthread_attr_init(&attr) != 0) | ||
168 | barf("pthread_attr_init:"); | ||
169 | |||
170 | #ifndef __ia64__ | ||
171 | if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) | ||
172 | barf("pthread_attr_setstacksize"); | ||
173 | #endif | ||
174 | |||
175 | err = pthread_create(&childid, &attr, func, ctx); | ||
176 | if (err != 0) { | ||
177 | fprintf(stderr, "pthread_create failed: %s (%d)\n", | ||
178 | strerror(err), err); | ||
179 | exit(-1); | ||
180 | } | ||
181 | return childid; | ||
182 | } | ||
183 | |||
184 | static void reap_worker(pthread_t id) | ||
185 | { | ||
186 | int proc_status; | ||
187 | void *thread_status; | ||
188 | |||
189 | if (!thread_mode) { | ||
190 | /* process mode */ | ||
191 | wait(&proc_status); | ||
192 | if (!WIFEXITED(proc_status)) | ||
193 | exit(1); | ||
194 | } else { | ||
195 | pthread_join(id, &thread_status); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* One group of senders and receivers */ | ||
200 | static unsigned int group(pthread_t *pth, | ||
201 | unsigned int num_fds, | ||
202 | int ready_out, | ||
203 | int wakefd) | ||
204 | { | ||
205 | unsigned int i; | ||
206 | struct sender_context *snd_ctx = malloc(sizeof(struct sender_context) | ||
207 | + num_fds * sizeof(int)); | ||
208 | |||
209 | if (!snd_ctx) | ||
210 | barf("malloc()"); | ||
211 | |||
212 | for (i = 0; i < num_fds; i++) { | ||
213 | int fds[2]; | ||
214 | struct receiver_context *ctx = malloc(sizeof(*ctx)); | ||
215 | |||
216 | if (!ctx) | ||
217 | barf("malloc()"); | ||
218 | |||
219 | |||
220 | /* Create the pipe between client and server */ | ||
221 | fdpair(fds); | ||
222 | |||
223 | ctx->num_packets = num_fds * loops; | ||
224 | ctx->in_fds[0] = fds[0]; | ||
225 | ctx->in_fds[1] = fds[1]; | ||
226 | ctx->ready_out = ready_out; | ||
227 | ctx->wakefd = wakefd; | ||
228 | |||
229 | pth[i] = create_worker(ctx, (void *)receiver); | ||
230 | |||
231 | snd_ctx->out_fds[i] = fds[1]; | ||
232 | if (!thread_mode) | ||
233 | close(fds[0]); | ||
234 | } | ||
235 | |||
236 | /* Now we have all the fds, fork the senders */ | ||
237 | for (i = 0; i < num_fds; i++) { | ||
238 | snd_ctx->ready_out = ready_out; | ||
239 | snd_ctx->wakefd = wakefd; | ||
240 | snd_ctx->num_fds = num_fds; | ||
241 | |||
242 | pth[num_fds+i] = create_worker(snd_ctx, (void *)sender); | ||
243 | } | ||
244 | |||
245 | /* Close the fds we have left */ | ||
246 | if (!thread_mode) | ||
247 | for (i = 0; i < num_fds; i++) | ||
248 | close(snd_ctx->out_fds[i]); | ||
249 | |||
250 | /* Return number of children to reap */ | ||
251 | return num_fds * 2; | ||
252 | } | ||
253 | |||
254 | static const struct option options[] = { | ||
255 | OPT_BOOLEAN('p', "pipe", &use_pipes, | ||
256 | "Use pipe() instead of socketpair()"), | ||
257 | OPT_BOOLEAN('t', "thread", &thread_mode, | ||
258 | "Be multi thread instead of multi process"), | ||
259 | OPT_INTEGER('g', "group", &num_groups, | ||
260 | "Specify number of groups"), | ||
261 | OPT_INTEGER('l', "loop", &loops, | ||
262 | "Specify number of loops"), | ||
263 | OPT_END() | ||
264 | }; | ||
265 | |||
266 | static const char * const bench_sched_message_usage[] = { | ||
267 | "perf bench sched messaging <options>", | ||
268 | NULL | ||
269 | }; | ||
270 | |||
271 | int bench_sched_messaging(int argc, const char **argv, | ||
272 | const char *prefix __used) | ||
273 | { | ||
274 | unsigned int i, total_children; | ||
275 | struct timeval start, stop, diff; | ||
276 | unsigned int num_fds = 20; | ||
277 | int readyfds[2], wakefds[2]; | ||
278 | char dummy; | ||
279 | pthread_t *pth_tab; | ||
280 | |||
281 | argc = parse_options(argc, argv, options, | ||
282 | bench_sched_message_usage, 0); | ||
283 | |||
284 | pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t)); | ||
285 | if (!pth_tab) | ||
286 | barf("main:malloc()"); | ||
287 | |||
288 | fdpair(readyfds); | ||
289 | fdpair(wakefds); | ||
290 | |||
291 | total_children = 0; | ||
292 | for (i = 0; i < num_groups; i++) | ||
293 | total_children += group(pth_tab+total_children, num_fds, | ||
294 | readyfds[1], wakefds[0]); | ||
295 | |||
296 | /* Wait for everyone to be ready */ | ||
297 | for (i = 0; i < total_children; i++) | ||
298 | if (read(readyfds[0], &dummy, 1) != 1) | ||
299 | barf("Reading for readyfds"); | ||
300 | |||
301 | gettimeofday(&start, NULL); | ||
302 | |||
303 | /* Kick them off */ | ||
304 | if (write(wakefds[1], &dummy, 1) != 1) | ||
305 | barf("Writing to start them"); | ||
306 | |||
307 | /* Reap them all */ | ||
308 | for (i = 0; i < total_children; i++) | ||
309 | reap_worker(pth_tab[i]); | ||
310 | |||
311 | gettimeofday(&stop, NULL); | ||
312 | |||
313 | timersub(&stop, &start, &diff); | ||
314 | |||
315 | switch (bench_format) { | ||
316 | case BENCH_FORMAT_DEFAULT: | ||
317 | printf("# %d sender and receiver %s per group\n", | ||
318 | num_fds, thread_mode ? "threads" : "processes"); | ||
319 | printf("# %d groups == %d %s run\n\n", | ||
320 | num_groups, num_groups * 2 * num_fds, | ||
321 | thread_mode ? "threads" : "processes"); | ||
322 | printf(" %14s: %lu.%03lu [sec]\n", "Total time", | ||
323 | diff.tv_sec, | ||
324 | (unsigned long) (diff.tv_usec/1000)); | ||
325 | break; | ||
326 | case BENCH_FORMAT_SIMPLE: | ||
327 | printf("%lu.%03lu\n", diff.tv_sec, | ||
328 | (unsigned long) (diff.tv_usec/1000)); | ||
329 | break; | ||
330 | default: | ||
331 | /* reaching here is something disaster */ | ||
332 | fprintf(stderr, "Unknown format:%d\n", bench_format); | ||
333 | exit(1); | ||
334 | break; | ||
335 | } | ||
336 | |||
337 | return 0; | ||
338 | } | ||
diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c new file mode 100644 index 000000000000..4f77c7c27640 --- /dev/null +++ b/tools/perf/bench/sched-pipe.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * | ||
3 | * sched-pipe.c | ||
4 | * | ||
5 | * pipe: Benchmark for pipe() | ||
6 | * | ||
7 | * Based on pipe-test-1m.c by Ingo Molnar <mingo@redhat.com> | ||
8 | * http://people.redhat.com/mingo/cfs-scheduler/tools/pipe-test-1m.c | ||
9 | * Ported to perf by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include "../perf.h" | ||
14 | #include "../util/util.h" | ||
15 | #include "../util/parse-options.h" | ||
16 | #include "../builtin.h" | ||
17 | #include "bench.h" | ||
18 | |||
19 | #include <unistd.h> | ||
20 | #include <stdio.h> | ||
21 | #include <stdlib.h> | ||
22 | #include <signal.h> | ||
23 | #include <sys/wait.h> | ||
24 | #include <linux/unistd.h> | ||
25 | #include <string.h> | ||
26 | #include <errno.h> | ||
27 | #include <assert.h> | ||
28 | #include <sys/time.h> | ||
29 | #include <sys/types.h> | ||
30 | |||
31 | #define LOOPS_DEFAULT 1000000 | ||
32 | static int loops = LOOPS_DEFAULT; | ||
33 | |||
34 | static const struct option options[] = { | ||
35 | OPT_INTEGER('l', "loop", &loops, | ||
36 | "Specify number of loops"), | ||
37 | OPT_END() | ||
38 | }; | ||
39 | |||
40 | static const char * const bench_sched_pipe_usage[] = { | ||
41 | "perf bench sched pipe <options>", | ||
42 | NULL | ||
43 | }; | ||
44 | |||
45 | int bench_sched_pipe(int argc, const char **argv, | ||
46 | const char *prefix __used) | ||
47 | { | ||
48 | int pipe_1[2], pipe_2[2]; | ||
49 | int m = 0, i; | ||
50 | struct timeval start, stop, diff; | ||
51 | unsigned long long result_usec = 0; | ||
52 | |||
53 | /* | ||
54 | * why does "ret" exist? | ||
55 | * discarding returned value of read(), write() | ||
56 | * causes error in building environment for perf | ||
57 | */ | ||
58 | int ret, wait_stat; | ||
59 | pid_t pid, retpid; | ||
60 | |||
61 | argc = parse_options(argc, argv, options, | ||
62 | bench_sched_pipe_usage, 0); | ||
63 | |||
64 | assert(!pipe(pipe_1)); | ||
65 | assert(!pipe(pipe_2)); | ||
66 | |||
67 | pid = fork(); | ||
68 | assert(pid >= 0); | ||
69 | |||
70 | gettimeofday(&start, NULL); | ||
71 | |||
72 | if (!pid) { | ||
73 | for (i = 0; i < loops; i++) { | ||
74 | ret = read(pipe_1[0], &m, sizeof(int)); | ||
75 | ret = write(pipe_2[1], &m, sizeof(int)); | ||
76 | } | ||
77 | } else { | ||
78 | for (i = 0; i < loops; i++) { | ||
79 | ret = write(pipe_1[1], &m, sizeof(int)); | ||
80 | ret = read(pipe_2[0], &m, sizeof(int)); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | gettimeofday(&stop, NULL); | ||
85 | timersub(&stop, &start, &diff); | ||
86 | |||
87 | if (pid) { | ||
88 | retpid = waitpid(pid, &wait_stat, 0); | ||
89 | assert((retpid == pid) && WIFEXITED(wait_stat)); | ||
90 | } else { | ||
91 | exit(0); | ||
92 | } | ||
93 | |||
94 | switch (bench_format) { | ||
95 | case BENCH_FORMAT_DEFAULT: | ||
96 | printf("# Extecuted %d pipe operations between two tasks\n\n", | ||
97 | loops); | ||
98 | |||
99 | result_usec = diff.tv_sec * 1000000; | ||
100 | result_usec += diff.tv_usec; | ||
101 | |||
102 | printf(" %14s: %lu.%03lu [sec]\n\n", "Total time", | ||
103 | diff.tv_sec, | ||
104 | (unsigned long) (diff.tv_usec/1000)); | ||
105 | |||
106 | printf(" %14lf usecs/op\n", | ||
107 | (double)result_usec / (double)loops); | ||
108 | printf(" %14d ops/sec\n", | ||
109 | (int)((double)loops / | ||
110 | ((double)result_usec / (double)1000000))); | ||
111 | break; | ||
112 | |||
113 | case BENCH_FORMAT_SIMPLE: | ||
114 | printf("%lu.%03lu\n", | ||
115 | diff.tv_sec, | ||
116 | (unsigned long) (diff.tv_usec / 1000)); | ||
117 | break; | ||
118 | |||
119 | default: | ||
120 | /* reaching here is something disaster */ | ||
121 | fprintf(stderr, "Unknown format:%d\n", bench_format); | ||
122 | exit(1); | ||
123 | break; | ||
124 | } | ||
125 | |||
126 | return 0; | ||
127 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 1ec741615814..5ec5de995872 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -19,29 +19,26 @@ | |||
19 | #include "perf.h" | 19 | #include "perf.h" |
20 | #include "util/debug.h" | 20 | #include "util/debug.h" |
21 | 21 | ||
22 | #include "util/event.h" | ||
22 | #include "util/parse-options.h" | 23 | #include "util/parse-options.h" |
23 | #include "util/parse-events.h" | 24 | #include "util/parse-events.h" |
24 | #include "util/thread.h" | 25 | #include "util/thread.h" |
26 | #include "util/sort.h" | ||
27 | #include "util/hist.h" | ||
28 | #include "util/session.h" | ||
25 | 29 | ||
26 | static char const *input_name = "perf.data"; | 30 | static char const *input_name = "perf.data"; |
27 | 31 | ||
28 | static char default_sort_order[] = "comm,symbol"; | ||
29 | static char *sort_order = default_sort_order; | ||
30 | |||
31 | static int force; | 32 | static int force; |
32 | static int input; | ||
33 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | ||
34 | 33 | ||
35 | static int full_paths; | 34 | static int full_paths; |
36 | 35 | ||
37 | static int print_line; | 36 | static int print_line; |
38 | 37 | ||
39 | static unsigned long page_size; | 38 | struct sym_hist { |
40 | static unsigned long mmap_window = 32; | 39 | u64 sum; |
41 | 40 | u64 ip[0]; | |
42 | static struct rb_root threads; | 41 | }; |
43 | static struct thread *last_match; | ||
44 | |||
45 | 42 | ||
46 | struct sym_ext { | 43 | struct sym_ext { |
47 | struct rb_node node; | 44 | struct rb_node node; |
@@ -49,636 +46,157 @@ struct sym_ext { | |||
49 | char *path; | 46 | char *path; |
50 | }; | 47 | }; |
51 | 48 | ||
52 | /* | 49 | struct sym_priv { |
53 | * histogram, sorted on item, collects counts | 50 | struct sym_hist *hist; |
54 | */ | 51 | struct sym_ext *ext; |
55 | |||
56 | static struct rb_root hist; | ||
57 | |||
58 | struct hist_entry { | ||
59 | struct rb_node rb_node; | ||
60 | |||
61 | struct thread *thread; | ||
62 | struct map *map; | ||
63 | struct dso *dso; | ||
64 | struct symbol *sym; | ||
65 | u64 ip; | ||
66 | char level; | ||
67 | |||
68 | uint32_t count; | ||
69 | }; | ||
70 | |||
71 | /* | ||
72 | * configurable sorting bits | ||
73 | */ | ||
74 | |||
75 | struct sort_entry { | ||
76 | struct list_head list; | ||
77 | |||
78 | const char *header; | ||
79 | |||
80 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | ||
81 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | ||
82 | size_t (*print)(FILE *fp, struct hist_entry *); | ||
83 | }; | ||
84 | |||
85 | /* --sort pid */ | ||
86 | |||
87 | static int64_t | ||
88 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
89 | { | ||
90 | return right->thread->pid - left->thread->pid; | ||
91 | } | ||
92 | |||
93 | static size_t | ||
94 | sort__thread_print(FILE *fp, struct hist_entry *self) | ||
95 | { | ||
96 | return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); | ||
97 | } | ||
98 | |||
99 | static struct sort_entry sort_thread = { | ||
100 | .header = " Command: Pid", | ||
101 | .cmp = sort__thread_cmp, | ||
102 | .print = sort__thread_print, | ||
103 | }; | ||
104 | |||
105 | /* --sort comm */ | ||
106 | |||
107 | static int64_t | ||
108 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
109 | { | ||
110 | return right->thread->pid - left->thread->pid; | ||
111 | } | ||
112 | |||
113 | static int64_t | ||
114 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
115 | { | ||
116 | char *comm_l = left->thread->comm; | ||
117 | char *comm_r = right->thread->comm; | ||
118 | |||
119 | if (!comm_l || !comm_r) { | ||
120 | if (!comm_l && !comm_r) | ||
121 | return 0; | ||
122 | else if (!comm_l) | ||
123 | return -1; | ||
124 | else | ||
125 | return 1; | ||
126 | } | ||
127 | |||
128 | return strcmp(comm_l, comm_r); | ||
129 | } | ||
130 | |||
131 | static size_t | ||
132 | sort__comm_print(FILE *fp, struct hist_entry *self) | ||
133 | { | ||
134 | return fprintf(fp, "%16s", self->thread->comm); | ||
135 | } | ||
136 | |||
137 | static struct sort_entry sort_comm = { | ||
138 | .header = " Command", | ||
139 | .cmp = sort__comm_cmp, | ||
140 | .collapse = sort__comm_collapse, | ||
141 | .print = sort__comm_print, | ||
142 | }; | ||
143 | |||
144 | /* --sort dso */ | ||
145 | |||
146 | static int64_t | ||
147 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | ||
148 | { | ||
149 | struct dso *dso_l = left->dso; | ||
150 | struct dso *dso_r = right->dso; | ||
151 | |||
152 | if (!dso_l || !dso_r) { | ||
153 | if (!dso_l && !dso_r) | ||
154 | return 0; | ||
155 | else if (!dso_l) | ||
156 | return -1; | ||
157 | else | ||
158 | return 1; | ||
159 | } | ||
160 | |||
161 | return strcmp(dso_l->name, dso_r->name); | ||
162 | } | ||
163 | |||
164 | static size_t | ||
165 | sort__dso_print(FILE *fp, struct hist_entry *self) | ||
166 | { | ||
167 | if (self->dso) | ||
168 | return fprintf(fp, "%-25s", self->dso->name); | ||
169 | |||
170 | return fprintf(fp, "%016llx ", (u64)self->ip); | ||
171 | } | ||
172 | |||
173 | static struct sort_entry sort_dso = { | ||
174 | .header = "Shared Object ", | ||
175 | .cmp = sort__dso_cmp, | ||
176 | .print = sort__dso_print, | ||
177 | }; | ||
178 | |||
179 | /* --sort symbol */ | ||
180 | |||
181 | static int64_t | ||
182 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
183 | { | ||
184 | u64 ip_l, ip_r; | ||
185 | |||
186 | if (left->sym == right->sym) | ||
187 | return 0; | ||
188 | |||
189 | ip_l = left->sym ? left->sym->start : left->ip; | ||
190 | ip_r = right->sym ? right->sym->start : right->ip; | ||
191 | |||
192 | return (int64_t)(ip_r - ip_l); | ||
193 | } | ||
194 | |||
195 | static size_t | ||
196 | sort__sym_print(FILE *fp, struct hist_entry *self) | ||
197 | { | ||
198 | size_t ret = 0; | ||
199 | |||
200 | if (verbose) | ||
201 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); | ||
202 | |||
203 | if (self->sym) { | ||
204 | ret += fprintf(fp, "[%c] %s", | ||
205 | self->dso == kernel_dso ? 'k' : '.', self->sym->name); | ||
206 | } else { | ||
207 | ret += fprintf(fp, "%#016llx", (u64)self->ip); | ||
208 | } | ||
209 | |||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | static struct sort_entry sort_sym = { | ||
214 | .header = "Symbol", | ||
215 | .cmp = sort__sym_cmp, | ||
216 | .print = sort__sym_print, | ||
217 | }; | ||
218 | |||
219 | static int sort__need_collapse = 0; | ||
220 | |||
221 | struct sort_dimension { | ||
222 | const char *name; | ||
223 | struct sort_entry *entry; | ||
224 | int taken; | ||
225 | }; | 52 | }; |
226 | 53 | ||
227 | static struct sort_dimension sort_dimensions[] = { | 54 | static const char *sym_hist_filter; |
228 | { .name = "pid", .entry = &sort_thread, }, | ||
229 | { .name = "comm", .entry = &sort_comm, }, | ||
230 | { .name = "dso", .entry = &sort_dso, }, | ||
231 | { .name = "symbol", .entry = &sort_sym, }, | ||
232 | }; | ||
233 | |||
234 | static LIST_HEAD(hist_entry__sort_list); | ||
235 | |||
236 | static int sort_dimension__add(char *tok) | ||
237 | { | ||
238 | unsigned int i; | ||
239 | |||
240 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | ||
241 | struct sort_dimension *sd = &sort_dimensions[i]; | ||
242 | |||
243 | if (sd->taken) | ||
244 | continue; | ||
245 | |||
246 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
247 | continue; | ||
248 | |||
249 | if (sd->entry->collapse) | ||
250 | sort__need_collapse = 1; | ||
251 | |||
252 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
253 | sd->taken = 1; | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | return -ESRCH; | ||
259 | } | ||
260 | |||
261 | static int64_t | ||
262 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | ||
263 | { | ||
264 | struct sort_entry *se; | ||
265 | int64_t cmp = 0; | ||
266 | |||
267 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
268 | cmp = se->cmp(left, right); | ||
269 | if (cmp) | ||
270 | break; | ||
271 | } | ||
272 | |||
273 | return cmp; | ||
274 | } | ||
275 | 55 | ||
276 | static int64_t | 56 | static int sym__alloc_hist(struct symbol *self) |
277 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | ||
278 | { | 57 | { |
279 | struct sort_entry *se; | 58 | struct sym_priv *priv = symbol__priv(self); |
280 | int64_t cmp = 0; | 59 | const int size = (sizeof(*priv->hist) + |
60 | (self->end - self->start) * sizeof(u64)); | ||
281 | 61 | ||
282 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 62 | priv->hist = zalloc(size); |
283 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | 63 | return priv->hist == NULL ? -1 : 0; |
284 | |||
285 | f = se->collapse ?: se->cmp; | ||
286 | |||
287 | cmp = f(left, right); | ||
288 | if (cmp) | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | return cmp; | ||
293 | } | 64 | } |
294 | 65 | ||
295 | /* | 66 | /* |
296 | * collect histogram counts | 67 | * collect histogram counts |
297 | */ | 68 | */ |
298 | static void hist_hit(struct hist_entry *he, u64 ip) | 69 | static int annotate__hist_hit(struct hist_entry *he, u64 ip) |
299 | { | 70 | { |
300 | unsigned int sym_size, offset; | 71 | unsigned int sym_size, offset; |
301 | struct symbol *sym = he->sym; | 72 | struct symbol *sym = he->sym; |
73 | struct sym_priv *priv; | ||
74 | struct sym_hist *h; | ||
302 | 75 | ||
303 | he->count++; | 76 | he->count++; |
304 | 77 | ||
305 | if (!sym || !sym->hist) | 78 | if (!sym || !he->map) |
306 | return; | 79 | return 0; |
80 | |||
81 | priv = symbol__priv(sym); | ||
82 | if (priv->hist == NULL && sym__alloc_hist(sym) < 0) | ||
83 | return -ENOMEM; | ||
307 | 84 | ||
308 | sym_size = sym->end - sym->start; | 85 | sym_size = sym->end - sym->start; |
309 | offset = ip - sym->start; | 86 | offset = ip - sym->start; |
310 | 87 | ||
88 | pr_debug3("%s: ip=%#Lx\n", __func__, he->map->unmap_ip(he->map, ip)); | ||
89 | |||
311 | if (offset >= sym_size) | 90 | if (offset >= sym_size) |
312 | return; | 91 | return 0; |
313 | 92 | ||
314 | sym->hist_sum++; | 93 | h = priv->hist; |
315 | sym->hist[offset]++; | 94 | h->sum++; |
95 | h->ip[offset]++; | ||
316 | 96 | ||
317 | if (verbose >= 3) | 97 | pr_debug3("%#Lx %s: count++ [ip: %#Lx, %#Lx] => %Ld\n", he->sym->start, |
318 | printf("%p %s: count++ [ip: %p, %08Lx] => %Ld\n", | 98 | he->sym->name, ip, ip - he->sym->start, h->ip[offset]); |
319 | (void *)(unsigned long)he->sym->start, | 99 | return 0; |
320 | he->sym->name, | ||
321 | (void *)(unsigned long)ip, ip - he->sym->start, | ||
322 | sym->hist[offset]); | ||
323 | } | 100 | } |
324 | 101 | ||
325 | static int | 102 | static int perf_session__add_hist_entry(struct perf_session *self, |
326 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | 103 | struct addr_location *al, u64 count) |
327 | struct symbol *sym, u64 ip, char level) | ||
328 | { | 104 | { |
329 | struct rb_node **p = &hist.rb_node; | 105 | bool hit; |
330 | struct rb_node *parent = NULL; | ||
331 | struct hist_entry *he; | 106 | struct hist_entry *he; |
332 | struct hist_entry entry = { | ||
333 | .thread = thread, | ||
334 | .map = map, | ||
335 | .dso = dso, | ||
336 | .sym = sym, | ||
337 | .ip = ip, | ||
338 | .level = level, | ||
339 | .count = 1, | ||
340 | }; | ||
341 | int cmp; | ||
342 | |||
343 | while (*p != NULL) { | ||
344 | parent = *p; | ||
345 | he = rb_entry(parent, struct hist_entry, rb_node); | ||
346 | |||
347 | cmp = hist_entry__cmp(&entry, he); | ||
348 | |||
349 | if (!cmp) { | ||
350 | hist_hit(he, ip); | ||
351 | 107 | ||
352 | return 0; | 108 | if (sym_hist_filter != NULL && |
109 | (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) { | ||
110 | /* We're only interested in a symbol named sym_hist_filter */ | ||
111 | if (al->sym != NULL) { | ||
112 | rb_erase(&al->sym->rb_node, | ||
113 | &al->map->dso->symbols[al->map->type]); | ||
114 | symbol__delete(al->sym); | ||
353 | } | 115 | } |
354 | 116 | return 0; | |
355 | if (cmp < 0) | ||
356 | p = &(*p)->rb_left; | ||
357 | else | ||
358 | p = &(*p)->rb_right; | ||
359 | } | 117 | } |
360 | 118 | ||
361 | he = malloc(sizeof(*he)); | 119 | he = __perf_session__add_hist_entry(self, al, NULL, count, &hit); |
362 | if (!he) | 120 | if (he == NULL) |
363 | return -ENOMEM; | 121 | return -ENOMEM; |
364 | *he = entry; | ||
365 | rb_link_node(&he->rb_node, parent, p); | ||
366 | rb_insert_color(&he->rb_node, &hist); | ||
367 | |||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | static void hist_entry__free(struct hist_entry *he) | ||
372 | { | ||
373 | free(he); | ||
374 | } | ||
375 | |||
376 | /* | ||
377 | * collapse the histogram | ||
378 | */ | ||
379 | |||
380 | static struct rb_root collapse_hists; | ||
381 | |||
382 | static void collapse__insert_entry(struct hist_entry *he) | ||
383 | { | ||
384 | struct rb_node **p = &collapse_hists.rb_node; | ||
385 | struct rb_node *parent = NULL; | ||
386 | struct hist_entry *iter; | ||
387 | int64_t cmp; | ||
388 | |||
389 | while (*p != NULL) { | ||
390 | parent = *p; | ||
391 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
392 | |||
393 | cmp = hist_entry__collapse(iter, he); | ||
394 | |||
395 | if (!cmp) { | ||
396 | iter->count += he->count; | ||
397 | hist_entry__free(he); | ||
398 | return; | ||
399 | } | ||
400 | |||
401 | if (cmp < 0) | ||
402 | p = &(*p)->rb_left; | ||
403 | else | ||
404 | p = &(*p)->rb_right; | ||
405 | } | ||
406 | |||
407 | rb_link_node(&he->rb_node, parent, p); | ||
408 | rb_insert_color(&he->rb_node, &collapse_hists); | ||
409 | } | ||
410 | |||
411 | static void collapse__resort(void) | ||
412 | { | ||
413 | struct rb_node *next; | ||
414 | struct hist_entry *n; | ||
415 | |||
416 | if (!sort__need_collapse) | ||
417 | return; | ||
418 | |||
419 | next = rb_first(&hist); | ||
420 | while (next) { | ||
421 | n = rb_entry(next, struct hist_entry, rb_node); | ||
422 | next = rb_next(&n->rb_node); | ||
423 | |||
424 | rb_erase(&n->rb_node, &hist); | ||
425 | collapse__insert_entry(n); | ||
426 | } | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * reverse the map, sort on count. | ||
431 | */ | ||
432 | |||
433 | static struct rb_root output_hists; | ||
434 | |||
435 | static void output__insert_entry(struct hist_entry *he) | ||
436 | { | ||
437 | struct rb_node **p = &output_hists.rb_node; | ||
438 | struct rb_node *parent = NULL; | ||
439 | struct hist_entry *iter; | ||
440 | |||
441 | while (*p != NULL) { | ||
442 | parent = *p; | ||
443 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
444 | |||
445 | if (he->count > iter->count) | ||
446 | p = &(*p)->rb_left; | ||
447 | else | ||
448 | p = &(*p)->rb_right; | ||
449 | } | ||
450 | 122 | ||
451 | rb_link_node(&he->rb_node, parent, p); | 123 | return annotate__hist_hit(he, al->addr); |
452 | rb_insert_color(&he->rb_node, &output_hists); | ||
453 | } | 124 | } |
454 | 125 | ||
455 | static void output__resort(void) | 126 | static int process_sample_event(event_t *event, struct perf_session *session) |
456 | { | 127 | { |
457 | struct rb_node *next; | 128 | struct addr_location al; |
458 | struct hist_entry *n; | ||
459 | struct rb_root *tree = &hist; | ||
460 | |||
461 | if (sort__need_collapse) | ||
462 | tree = &collapse_hists; | ||
463 | |||
464 | next = rb_first(tree); | ||
465 | |||
466 | while (next) { | ||
467 | n = rb_entry(next, struct hist_entry, rb_node); | ||
468 | next = rb_next(&n->rb_node); | ||
469 | |||
470 | rb_erase(&n->rb_node, tree); | ||
471 | output__insert_entry(n); | ||
472 | } | ||
473 | } | ||
474 | 129 | ||
475 | static unsigned long total = 0, | 130 | dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, |
476 | total_mmap = 0, | 131 | event->ip.pid, event->ip.ip); |
477 | total_comm = 0, | ||
478 | total_fork = 0, | ||
479 | total_unknown = 0; | ||
480 | 132 | ||
481 | static int | 133 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { |
482 | process_sample_event(event_t *event, unsigned long offset, unsigned long head) | 134 | pr_warning("problem processing %d event, skipping it.\n", |
483 | { | 135 | event->header.type); |
484 | char level; | ||
485 | int show = 0; | ||
486 | struct dso *dso = NULL; | ||
487 | struct thread *thread; | ||
488 | u64 ip = event->ip.ip; | ||
489 | struct map *map = NULL; | ||
490 | |||
491 | thread = threads__findnew(event->ip.pid, &threads, &last_match); | ||
492 | |||
493 | dump_printf("%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", | ||
494 | (void *)(offset + head), | ||
495 | (void *)(long)(event->header.size), | ||
496 | event->header.misc, | ||
497 | event->ip.pid, | ||
498 | (void *)(long)ip); | ||
499 | |||
500 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | ||
501 | |||
502 | if (thread == NULL) { | ||
503 | fprintf(stderr, "problem processing %d event, skipping it.\n", | ||
504 | event->header.type); | ||
505 | return -1; | 136 | return -1; |
506 | } | 137 | } |
507 | 138 | ||
508 | if (event->header.misc & PERF_RECORD_MISC_KERNEL) { | 139 | if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) { |
509 | show = SHOW_KERNEL; | 140 | pr_warning("problem incrementing symbol count, " |
510 | level = 'k'; | 141 | "skipping event\n"); |
511 | 142 | return -1; | |
512 | dso = kernel_dso; | ||
513 | |||
514 | dump_printf(" ...... dso: %s\n", dso->name); | ||
515 | |||
516 | } else if (event->header.misc & PERF_RECORD_MISC_USER) { | ||
517 | |||
518 | show = SHOW_USER; | ||
519 | level = '.'; | ||
520 | |||
521 | map = thread__find_map(thread, ip); | ||
522 | if (map != NULL) { | ||
523 | ip = map->map_ip(map, ip); | ||
524 | dso = map->dso; | ||
525 | } else { | ||
526 | /* | ||
527 | * If this is outside of all known maps, | ||
528 | * and is a negative address, try to look it | ||
529 | * up in the kernel dso, as it might be a | ||
530 | * vsyscall (which executes in user-mode): | ||
531 | */ | ||
532 | if ((long long)ip < 0) | ||
533 | dso = kernel_dso; | ||
534 | } | ||
535 | dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); | ||
536 | |||
537 | } else { | ||
538 | show = SHOW_HV; | ||
539 | level = 'H'; | ||
540 | dump_printf(" ...... dso: [hypervisor]\n"); | ||
541 | } | ||
542 | |||
543 | if (show & show_mask) { | ||
544 | struct symbol *sym = NULL; | ||
545 | |||
546 | if (dso) | ||
547 | sym = dso->find_symbol(dso, ip); | ||
548 | |||
549 | if (hist_entry__add(thread, map, dso, sym, ip, level)) { | ||
550 | fprintf(stderr, | ||
551 | "problem incrementing symbol count, skipping event\n"); | ||
552 | return -1; | ||
553 | } | ||
554 | } | 143 | } |
555 | total++; | ||
556 | 144 | ||
557 | return 0; | 145 | return 0; |
558 | } | 146 | } |
559 | 147 | ||
560 | static int | 148 | struct objdump_line { |
561 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | 149 | struct list_head node; |
562 | { | 150 | s64 offset; |
563 | struct thread *thread; | 151 | char *line; |
564 | struct map *map = map__new(&event->mmap, NULL, 0); | 152 | }; |
565 | |||
566 | thread = threads__findnew(event->mmap.pid, &threads, &last_match); | ||
567 | |||
568 | dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n", | ||
569 | (void *)(offset + head), | ||
570 | (void *)(long)(event->header.size), | ||
571 | event->mmap.pid, | ||
572 | (void *)(long)event->mmap.start, | ||
573 | (void *)(long)event->mmap.len, | ||
574 | (void *)(long)event->mmap.pgoff, | ||
575 | event->mmap.filename); | ||
576 | |||
577 | if (thread == NULL || map == NULL) { | ||
578 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
579 | return 0; | ||
580 | } | ||
581 | |||
582 | thread__insert_map(thread, map); | ||
583 | total_mmap++; | ||
584 | |||
585 | return 0; | ||
586 | } | ||
587 | 153 | ||
588 | static int | 154 | static struct objdump_line *objdump_line__new(s64 offset, char *line) |
589 | process_comm_event(event_t *event, unsigned long offset, unsigned long head) | ||
590 | { | 155 | { |
591 | struct thread *thread; | 156 | struct objdump_line *self = malloc(sizeof(*self)); |
592 | 157 | ||
593 | thread = threads__findnew(event->comm.pid, &threads, &last_match); | 158 | if (self != NULL) { |
594 | dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n", | 159 | self->offset = offset; |
595 | (void *)(offset + head), | 160 | self->line = line; |
596 | (void *)(long)(event->header.size), | ||
597 | event->comm.comm, event->comm.pid); | ||
598 | |||
599 | if (thread == NULL || | ||
600 | thread__set_comm(thread, event->comm.comm)) { | ||
601 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
602 | return -1; | ||
603 | } | 161 | } |
604 | total_comm++; | ||
605 | 162 | ||
606 | return 0; | 163 | return self; |
607 | } | 164 | } |
608 | 165 | ||
609 | static int | 166 | static void objdump_line__free(struct objdump_line *self) |
610 | process_fork_event(event_t *event, unsigned long offset, unsigned long head) | ||
611 | { | 167 | { |
612 | struct thread *thread; | 168 | free(self->line); |
613 | struct thread *parent; | 169 | free(self); |
614 | |||
615 | thread = threads__findnew(event->fork.pid, &threads, &last_match); | ||
616 | parent = threads__findnew(event->fork.ppid, &threads, &last_match); | ||
617 | dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n", | ||
618 | (void *)(offset + head), | ||
619 | (void *)(long)(event->header.size), | ||
620 | event->fork.pid, event->fork.ppid); | ||
621 | |||
622 | /* | ||
623 | * A thread clone will have the same PID for both | ||
624 | * parent and child. | ||
625 | */ | ||
626 | if (thread == parent) | ||
627 | return 0; | ||
628 | |||
629 | if (!thread || !parent || thread__fork(thread, parent)) { | ||
630 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
631 | return -1; | ||
632 | } | ||
633 | total_fork++; | ||
634 | |||
635 | return 0; | ||
636 | } | 170 | } |
637 | 171 | ||
638 | static int | 172 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) |
639 | process_event(event_t *event, unsigned long offset, unsigned long head) | ||
640 | { | 173 | { |
641 | switch (event->header.type) { | 174 | list_add_tail(&line->node, head); |
642 | case PERF_RECORD_SAMPLE: | 175 | } |
643 | return process_sample_event(event, offset, head); | ||
644 | |||
645 | case PERF_RECORD_MMAP: | ||
646 | return process_mmap_event(event, offset, head); | ||
647 | |||
648 | case PERF_RECORD_COMM: | ||
649 | return process_comm_event(event, offset, head); | ||
650 | |||
651 | case PERF_RECORD_FORK: | ||
652 | return process_fork_event(event, offset, head); | ||
653 | /* | ||
654 | * We dont process them right now but they are fine: | ||
655 | */ | ||
656 | |||
657 | case PERF_RECORD_THROTTLE: | ||
658 | case PERF_RECORD_UNTHROTTLE: | ||
659 | return 0; | ||
660 | 176 | ||
661 | default: | 177 | static struct objdump_line *objdump__get_next_ip_line(struct list_head *head, |
662 | return -1; | 178 | struct objdump_line *pos) |
663 | } | 179 | { |
180 | list_for_each_entry_continue(pos, head, node) | ||
181 | if (pos->offset >= 0) | ||
182 | return pos; | ||
664 | 183 | ||
665 | return 0; | 184 | return NULL; |
666 | } | 185 | } |
667 | 186 | ||
668 | static int | 187 | static int parse_line(FILE *file, struct hist_entry *he, |
669 | parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) | 188 | struct list_head *head) |
670 | { | 189 | { |
190 | struct symbol *sym = he->sym; | ||
191 | struct objdump_line *objdump_line; | ||
671 | char *line = NULL, *tmp, *tmp2; | 192 | char *line = NULL, *tmp, *tmp2; |
672 | static const char *prev_line; | ||
673 | static const char *prev_color; | ||
674 | unsigned int offset; | ||
675 | size_t line_len; | 193 | size_t line_len; |
676 | s64 line_ip; | 194 | s64 line_ip, offset = -1; |
677 | int ret; | ||
678 | char *c; | 195 | char *c; |
679 | 196 | ||
680 | if (getline(&line, &line_len, file) < 0) | 197 | if (getline(&line, &line_len, file) < 0) |
681 | return -1; | 198 | return -1; |
199 | |||
682 | if (!line) | 200 | if (!line) |
683 | return -1; | 201 | return -1; |
684 | 202 | ||
@@ -687,8 +205,6 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) | |||
687 | *c = 0; | 205 | *c = 0; |
688 | 206 | ||
689 | line_ip = -1; | 207 | line_ip = -1; |
690 | offset = 0; | ||
691 | ret = -2; | ||
692 | 208 | ||
693 | /* | 209 | /* |
694 | * Strip leading spaces: | 210 | * Strip leading spaces: |
@@ -710,21 +226,53 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) | |||
710 | } | 226 | } |
711 | 227 | ||
712 | if (line_ip != -1) { | 228 | if (line_ip != -1) { |
229 | u64 start = map__rip_2objdump(he->map, sym->start); | ||
230 | offset = line_ip - start; | ||
231 | } | ||
232 | |||
233 | objdump_line = objdump_line__new(offset, line); | ||
234 | if (objdump_line == NULL) { | ||
235 | free(line); | ||
236 | return -1; | ||
237 | } | ||
238 | objdump__add_line(head, objdump_line); | ||
239 | |||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int objdump_line__print(struct objdump_line *self, | ||
244 | struct list_head *head, | ||
245 | struct hist_entry *he, u64 len) | ||
246 | { | ||
247 | struct symbol *sym = he->sym; | ||
248 | static const char *prev_line; | ||
249 | static const char *prev_color; | ||
250 | |||
251 | if (self->offset != -1) { | ||
713 | const char *path = NULL; | 252 | const char *path = NULL; |
714 | unsigned int hits = 0; | 253 | unsigned int hits = 0; |
715 | double percent = 0.0; | 254 | double percent = 0.0; |
716 | const char *color; | 255 | const char *color; |
717 | struct sym_ext *sym_ext = sym->priv; | 256 | struct sym_priv *priv = symbol__priv(sym); |
718 | 257 | struct sym_ext *sym_ext = priv->ext; | |
719 | offset = line_ip - start; | 258 | struct sym_hist *h = priv->hist; |
720 | if (offset < len) | 259 | s64 offset = self->offset; |
721 | hits = sym->hist[offset]; | 260 | struct objdump_line *next = objdump__get_next_ip_line(head, self); |
261 | |||
262 | while (offset < (s64)len && | ||
263 | (next == NULL || offset < next->offset)) { | ||
264 | if (sym_ext) { | ||
265 | if (path == NULL) | ||
266 | path = sym_ext[offset].path; | ||
267 | percent += sym_ext[offset].percent; | ||
268 | } else | ||
269 | hits += h->ip[offset]; | ||
270 | |||
271 | ++offset; | ||
272 | } | ||
722 | 273 | ||
723 | if (offset < len && sym_ext) { | 274 | if (sym_ext == NULL && h->sum) |
724 | path = sym_ext[offset].path; | 275 | percent = 100.0 * hits / h->sum; |
725 | percent = sym_ext[offset].percent; | ||
726 | } else if (sym->hist_sum) | ||
727 | percent = 100.0 * hits / sym->hist_sum; | ||
728 | 276 | ||
729 | color = get_percent_color(percent); | 277 | color = get_percent_color(percent); |
730 | 278 | ||
@@ -744,12 +292,12 @@ parse_line(FILE *file, struct symbol *sym, u64 start, u64 len) | |||
744 | 292 | ||
745 | color_fprintf(stdout, color, " %7.2f", percent); | 293 | color_fprintf(stdout, color, " %7.2f", percent); |
746 | printf(" : "); | 294 | printf(" : "); |
747 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", line); | 295 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line); |
748 | } else { | 296 | } else { |
749 | if (!*line) | 297 | if (!*self->line) |
750 | printf(" :\n"); | 298 | printf(" :\n"); |
751 | else | 299 | else |
752 | printf(" : %s\n", line); | 300 | printf(" : %s\n", self->line); |
753 | } | 301 | } |
754 | 302 | ||
755 | return 0; | 303 | return 0; |
@@ -777,9 +325,10 @@ static void insert_source_line(struct sym_ext *sym_ext) | |||
777 | rb_insert_color(&sym_ext->node, &root_sym_ext); | 325 | rb_insert_color(&sym_ext->node, &root_sym_ext); |
778 | } | 326 | } |
779 | 327 | ||
780 | static void free_source_line(struct symbol *sym, int len) | 328 | static void free_source_line(struct hist_entry *he, int len) |
781 | { | 329 | { |
782 | struct sym_ext *sym_ext = sym->priv; | 330 | struct sym_priv *priv = symbol__priv(he->sym); |
331 | struct sym_ext *sym_ext = priv->ext; | ||
783 | int i; | 332 | int i; |
784 | 333 | ||
785 | if (!sym_ext) | 334 | if (!sym_ext) |
@@ -789,26 +338,30 @@ static void free_source_line(struct symbol *sym, int len) | |||
789 | free(sym_ext[i].path); | 338 | free(sym_ext[i].path); |
790 | free(sym_ext); | 339 | free(sym_ext); |
791 | 340 | ||
792 | sym->priv = NULL; | 341 | priv->ext = NULL; |
793 | root_sym_ext = RB_ROOT; | 342 | root_sym_ext = RB_ROOT; |
794 | } | 343 | } |
795 | 344 | ||
796 | /* Get the filename:line for the colored entries */ | 345 | /* Get the filename:line for the colored entries */ |
797 | static void | 346 | static void |
798 | get_source_line(struct symbol *sym, u64 start, int len, const char *filename) | 347 | get_source_line(struct hist_entry *he, int len, const char *filename) |
799 | { | 348 | { |
349 | struct symbol *sym = he->sym; | ||
350 | u64 start; | ||
800 | int i; | 351 | int i; |
801 | char cmd[PATH_MAX * 2]; | 352 | char cmd[PATH_MAX * 2]; |
802 | struct sym_ext *sym_ext; | 353 | struct sym_ext *sym_ext; |
354 | struct sym_priv *priv = symbol__priv(sym); | ||
355 | struct sym_hist *h = priv->hist; | ||
803 | 356 | ||
804 | if (!sym->hist_sum) | 357 | if (!h->sum) |
805 | return; | 358 | return; |
806 | 359 | ||
807 | sym->priv = calloc(len, sizeof(struct sym_ext)); | 360 | sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext)); |
808 | if (!sym->priv) | 361 | if (!priv->ext) |
809 | return; | 362 | return; |
810 | 363 | ||
811 | sym_ext = sym->priv; | 364 | start = he->map->unmap_ip(he->map, sym->start); |
812 | 365 | ||
813 | for (i = 0; i < len; i++) { | 366 | for (i = 0; i < len; i++) { |
814 | char *path = NULL; | 367 | char *path = NULL; |
@@ -816,7 +369,7 @@ get_source_line(struct symbol *sym, u64 start, int len, const char *filename) | |||
816 | u64 offset; | 369 | u64 offset; |
817 | FILE *fp; | 370 | FILE *fp; |
818 | 371 | ||
819 | sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum; | 372 | sym_ext[i].percent = 100.0 * h->ip[i] / h->sum; |
820 | if (sym_ext[i].percent <= 0.5) | 373 | if (sym_ext[i].percent <= 0.5) |
821 | continue; | 374 | continue; |
822 | 375 | ||
@@ -870,33 +423,48 @@ static void print_summary(const char *filename) | |||
870 | } | 423 | } |
871 | } | 424 | } |
872 | 425 | ||
873 | static void annotate_sym(struct dso *dso, struct symbol *sym) | 426 | static void hist_entry__print_hits(struct hist_entry *self) |
427 | { | ||
428 | struct symbol *sym = self->sym; | ||
429 | struct sym_priv *priv = symbol__priv(sym); | ||
430 | struct sym_hist *h = priv->hist; | ||
431 | u64 len = sym->end - sym->start, offset; | ||
432 | |||
433 | for (offset = 0; offset < len; ++offset) | ||
434 | if (h->ip[offset] != 0) | ||
435 | printf("%*Lx: %Lu\n", BITS_PER_LONG / 2, | ||
436 | sym->start + offset, h->ip[offset]); | ||
437 | printf("%*s: %Lu\n", BITS_PER_LONG / 2, "h->sum", h->sum); | ||
438 | } | ||
439 | |||
440 | static void annotate_sym(struct hist_entry *he) | ||
874 | { | 441 | { |
875 | const char *filename = dso->name, *d_filename; | 442 | struct map *map = he->map; |
876 | u64 start, end, len; | 443 | struct dso *dso = map->dso; |
444 | struct symbol *sym = he->sym; | ||
445 | const char *filename = dso->long_name, *d_filename; | ||
446 | u64 len; | ||
877 | char command[PATH_MAX*2]; | 447 | char command[PATH_MAX*2]; |
878 | FILE *file; | 448 | FILE *file; |
449 | LIST_HEAD(head); | ||
450 | struct objdump_line *pos, *n; | ||
879 | 451 | ||
880 | if (!filename) | 452 | if (!filename) |
881 | return; | 453 | return; |
882 | if (sym->module) | 454 | |
883 | filename = sym->module->path; | 455 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, |
884 | else if (dso == kernel_dso) | 456 | filename, sym->name, map->unmap_ip(map, sym->start), |
885 | filename = vmlinux_name; | 457 | map->unmap_ip(map, sym->end)); |
886 | 458 | ||
887 | start = sym->obj_start; | ||
888 | if (!start) | ||
889 | start = sym->start; | ||
890 | if (full_paths) | 459 | if (full_paths) |
891 | d_filename = filename; | 460 | d_filename = filename; |
892 | else | 461 | else |
893 | d_filename = basename(filename); | 462 | d_filename = basename(filename); |
894 | 463 | ||
895 | end = start + sym->end - sym->start + 1; | ||
896 | len = sym->end - sym->start; | 464 | len = sym->end - sym->start; |
897 | 465 | ||
898 | if (print_line) { | 466 | if (print_line) { |
899 | get_source_line(sym, start, len, filename); | 467 | get_source_line(he, len, filename); |
900 | print_summary(filename); | 468 | print_summary(filename); |
901 | } | 469 | } |
902 | 470 | ||
@@ -905,10 +473,13 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
905 | printf("------------------------------------------------\n"); | 473 | printf("------------------------------------------------\n"); |
906 | 474 | ||
907 | if (verbose >= 2) | 475 | if (verbose >= 2) |
908 | printf("annotating [%p] %30s : [%p] %30s\n", dso, dso->name, sym, sym->name); | 476 | printf("annotating [%p] %30s : [%p] %30s\n", |
477 | dso, dso->long_name, sym, sym->name); | ||
909 | 478 | ||
910 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", | 479 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s", |
911 | (u64)start, (u64)end, filename, filename); | 480 | map__rip_2objdump(map, sym->start), |
481 | map__rip_2objdump(map, sym->end), | ||
482 | filename, filename); | ||
912 | 483 | ||
913 | if (verbose >= 3) | 484 | if (verbose >= 3) |
914 | printf("doing: %s\n", command); | 485 | printf("doing: %s\n", command); |
@@ -918,159 +489,88 @@ static void annotate_sym(struct dso *dso, struct symbol *sym) | |||
918 | return; | 489 | return; |
919 | 490 | ||
920 | while (!feof(file)) { | 491 | while (!feof(file)) { |
921 | if (parse_line(file, sym, start, len) < 0) | 492 | if (parse_line(file, he, &head) < 0) |
922 | break; | 493 | break; |
923 | } | 494 | } |
924 | 495 | ||
925 | pclose(file); | 496 | pclose(file); |
926 | if (print_line) | ||
927 | free_source_line(sym, len); | ||
928 | } | ||
929 | 497 | ||
930 | static void find_annotations(void) | 498 | if (verbose) |
931 | { | 499 | hist_entry__print_hits(he); |
932 | struct rb_node *nd; | ||
933 | struct dso *dso; | ||
934 | int count = 0; | ||
935 | |||
936 | list_for_each_entry(dso, &dsos, node) { | ||
937 | |||
938 | for (nd = rb_first(&dso->syms); nd; nd = rb_next(nd)) { | ||
939 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | ||
940 | 500 | ||
941 | if (sym->hist) { | 501 | list_for_each_entry_safe(pos, n, &head, node) { |
942 | annotate_sym(dso, sym); | 502 | objdump_line__print(pos, &head, he, len); |
943 | count++; | 503 | list_del(&pos->node); |
944 | } | 504 | objdump_line__free(pos); |
945 | } | ||
946 | } | 505 | } |
947 | 506 | ||
948 | if (!count) | 507 | if (print_line) |
949 | printf(" Error: symbol '%s' not present amongst the samples.\n", sym_hist_filter); | 508 | free_source_line(he, len); |
950 | } | 509 | } |
951 | 510 | ||
952 | static int __cmd_annotate(void) | 511 | static void perf_session__find_annotations(struct perf_session *self) |
953 | { | 512 | { |
954 | int ret, rc = EXIT_FAILURE; | 513 | struct rb_node *nd; |
955 | unsigned long offset = 0; | ||
956 | unsigned long head = 0; | ||
957 | struct stat input_stat; | ||
958 | event_t *event; | ||
959 | uint32_t size; | ||
960 | char *buf; | ||
961 | |||
962 | register_idle_thread(&threads, &last_match); | ||
963 | |||
964 | input = open(input_name, O_RDONLY); | ||
965 | if (input < 0) { | ||
966 | perror("failed to open file"); | ||
967 | exit(-1); | ||
968 | } | ||
969 | |||
970 | ret = fstat(input, &input_stat); | ||
971 | if (ret < 0) { | ||
972 | perror("failed to stat file"); | ||
973 | exit(-1); | ||
974 | } | ||
975 | |||
976 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
977 | fprintf(stderr, "file: %s not owned by current user or root\n", input_name); | ||
978 | exit(-1); | ||
979 | } | ||
980 | |||
981 | if (!input_stat.st_size) { | ||
982 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | ||
983 | exit(0); | ||
984 | } | ||
985 | |||
986 | if (load_kernel() < 0) { | ||
987 | perror("failed to load kernel symbols"); | ||
988 | return EXIT_FAILURE; | ||
989 | } | ||
990 | |||
991 | remap: | ||
992 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | ||
993 | MAP_SHARED, input, offset); | ||
994 | if (buf == MAP_FAILED) { | ||
995 | perror("failed to mmap file"); | ||
996 | exit(-1); | ||
997 | } | ||
998 | |||
999 | more: | ||
1000 | event = (event_t *)(buf + head); | ||
1001 | |||
1002 | size = event->header.size; | ||
1003 | if (!size) | ||
1004 | size = 8; | ||
1005 | |||
1006 | if (head + event->header.size >= page_size * mmap_window) { | ||
1007 | unsigned long shift = page_size * (head / page_size); | ||
1008 | int munmap_ret; | ||
1009 | |||
1010 | munmap_ret = munmap(buf, page_size * mmap_window); | ||
1011 | assert(munmap_ret == 0); | ||
1012 | |||
1013 | offset += shift; | ||
1014 | head -= shift; | ||
1015 | goto remap; | ||
1016 | } | ||
1017 | |||
1018 | size = event->header.size; | ||
1019 | |||
1020 | dump_printf("%p [%p]: event: %d\n", | ||
1021 | (void *)(offset + head), | ||
1022 | (void *)(long)event->header.size, | ||
1023 | event->header.type); | ||
1024 | 514 | ||
1025 | if (!size || process_event(event, offset, head) < 0) { | 515 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { |
516 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); | ||
517 | struct sym_priv *priv; | ||
1026 | 518 | ||
1027 | dump_printf("%p [%p]: skipping unknown header type: %d\n", | 519 | if (he->sym == NULL) |
1028 | (void *)(offset + head), | 520 | continue; |
1029 | (void *)(long)(event->header.size), | ||
1030 | event->header.type); | ||
1031 | 521 | ||
1032 | total_unknown++; | 522 | priv = symbol__priv(he->sym); |
523 | if (priv->hist == NULL) | ||
524 | continue; | ||
1033 | 525 | ||
526 | annotate_sym(he); | ||
1034 | /* | 527 | /* |
1035 | * assume we lost track of the stream, check alignment, and | 528 | * Since we have a hist_entry per IP for the same symbol, free |
1036 | * increment a single u64 in the hope to catch on again 'soon'. | 529 | * he->sym->hist to signal we already processed this symbol. |
1037 | */ | 530 | */ |
1038 | 531 | free(priv->hist); | |
1039 | if (unlikely(head & 7)) | 532 | priv->hist = NULL; |
1040 | head &= ~7ULL; | ||
1041 | |||
1042 | size = 8; | ||
1043 | } | 533 | } |
534 | } | ||
1044 | 535 | ||
1045 | head += size; | 536 | static struct perf_event_ops event_ops = { |
537 | .sample = process_sample_event, | ||
538 | .mmap = event__process_mmap, | ||
539 | .comm = event__process_comm, | ||
540 | .fork = event__process_task, | ||
541 | }; | ||
1046 | 542 | ||
1047 | if (offset + head < (unsigned long)input_stat.st_size) | 543 | static int __cmd_annotate(void) |
1048 | goto more; | 544 | { |
545 | int ret; | ||
546 | struct perf_session *session; | ||
1049 | 547 | ||
1050 | rc = EXIT_SUCCESS; | 548 | session = perf_session__new(input_name, O_RDONLY, force); |
1051 | close(input); | 549 | if (session == NULL) |
550 | return -ENOMEM; | ||
1052 | 551 | ||
1053 | dump_printf(" IP events: %10ld\n", total); | 552 | ret = perf_session__process_events(session, &event_ops); |
1054 | dump_printf(" mmap events: %10ld\n", total_mmap); | 553 | if (ret) |
1055 | dump_printf(" comm events: %10ld\n", total_comm); | 554 | goto out_delete; |
1056 | dump_printf(" fork events: %10ld\n", total_fork); | ||
1057 | dump_printf(" unknown events: %10ld\n", total_unknown); | ||
1058 | 555 | ||
1059 | if (dump_trace) | 556 | if (dump_trace) { |
1060 | return 0; | 557 | event__print_totals(); |
558 | goto out_delete; | ||
559 | } | ||
1061 | 560 | ||
1062 | if (verbose >= 3) | 561 | if (verbose > 3) |
1063 | threads__fprintf(stdout, &threads); | 562 | perf_session__fprintf(session, stdout); |
1064 | 563 | ||
1065 | if (verbose >= 2) | 564 | if (verbose > 2) |
1066 | dsos__fprintf(stdout); | 565 | dsos__fprintf(stdout); |
1067 | 566 | ||
1068 | collapse__resort(); | 567 | perf_session__collapse_resort(session); |
1069 | output__resort(); | 568 | perf_session__output_resort(session, session->event_total[0]); |
569 | perf_session__find_annotations(session); | ||
570 | out_delete: | ||
571 | perf_session__delete(session); | ||
1070 | 572 | ||
1071 | find_annotations(); | 573 | return ret; |
1072 | |||
1073 | return rc; | ||
1074 | } | 574 | } |
1075 | 575 | ||
1076 | static const char * const annotate_usage[] = { | 576 | static const char * const annotate_usage[] = { |
@@ -1088,8 +588,9 @@ static const struct option options[] = { | |||
1088 | "be more verbose (show symbol address, etc)"), | 588 | "be more verbose (show symbol address, etc)"), |
1089 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 589 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1090 | "dump raw trace in ASCII"), | 590 | "dump raw trace in ASCII"), |
1091 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), | 591 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1092 | OPT_BOOLEAN('m', "modules", &modules, | 592 | "file", "vmlinux pathname"), |
593 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | ||
1093 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 594 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
1094 | OPT_BOOLEAN('l', "print-line", &print_line, | 595 | OPT_BOOLEAN('l', "print-line", &print_line, |
1095 | "print matching source lines (may be slow)"), | 596 | "print matching source lines (may be slow)"), |
@@ -1098,30 +599,17 @@ static const struct option options[] = { | |||
1098 | OPT_END() | 599 | OPT_END() |
1099 | }; | 600 | }; |
1100 | 601 | ||
1101 | static void setup_sorting(void) | ||
1102 | { | ||
1103 | char *tmp, *tok, *str = strdup(sort_order); | ||
1104 | |||
1105 | for (tok = strtok_r(str, ", ", &tmp); | ||
1106 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
1107 | if (sort_dimension__add(tok) < 0) { | ||
1108 | error("Unknown --sort key: `%s'", tok); | ||
1109 | usage_with_options(annotate_usage, options); | ||
1110 | } | ||
1111 | } | ||
1112 | |||
1113 | free(str); | ||
1114 | } | ||
1115 | |||
1116 | int cmd_annotate(int argc, const char **argv, const char *prefix __used) | 602 | int cmd_annotate(int argc, const char **argv, const char *prefix __used) |
1117 | { | 603 | { |
1118 | symbol__init(); | 604 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
1119 | 605 | ||
1120 | page_size = getpagesize(); | 606 | symbol_conf.priv_size = sizeof(struct sym_priv); |
607 | symbol_conf.try_vmlinux_path = true; | ||
1121 | 608 | ||
1122 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 609 | if (symbol__init() < 0) |
610 | return -1; | ||
1123 | 611 | ||
1124 | setup_sorting(); | 612 | setup_sorting(annotate_usage, options); |
1125 | 613 | ||
1126 | if (argc) { | 614 | if (argc) { |
1127 | /* | 615 | /* |
@@ -1134,10 +622,12 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) | |||
1134 | sym_hist_filter = argv[0]; | 622 | sym_hist_filter = argv[0]; |
1135 | } | 623 | } |
1136 | 624 | ||
1137 | if (!sym_hist_filter) | ||
1138 | usage_with_options(annotate_usage, options); | ||
1139 | |||
1140 | setup_pager(); | 625 | setup_pager(); |
1141 | 626 | ||
627 | if (field_sep && *field_sep == '.') { | ||
628 | pr_err("'.' is the only non valid --field-separator argument\n"); | ||
629 | return -1; | ||
630 | } | ||
631 | |||
1142 | return __cmd_annotate(); | 632 | return __cmd_annotate(); |
1143 | } | 633 | } |
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c new file mode 100644 index 000000000000..46996774e559 --- /dev/null +++ b/tools/perf/builtin-bench.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | * | ||
3 | * builtin-bench.c | ||
4 | * | ||
5 | * General benchmarking subsystem provided by perf | ||
6 | * | ||
7 | * Copyright (C) 2009, Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * | ||
13 | * Available subsystem list: | ||
14 | * sched ... scheduler and IPC mechanism | ||
15 | * mem ... memory access performance | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include "perf.h" | ||
20 | #include "util/util.h" | ||
21 | #include "util/parse-options.h" | ||
22 | #include "builtin.h" | ||
23 | #include "bench/bench.h" | ||
24 | |||
25 | #include <stdio.h> | ||
26 | #include <stdlib.h> | ||
27 | #include <string.h> | ||
28 | |||
29 | struct bench_suite { | ||
30 | const char *name; | ||
31 | const char *summary; | ||
32 | int (*fn)(int, const char **, const char *); | ||
33 | }; | ||
34 | \ | ||
35 | /* sentinel: easy for help */ | ||
36 | #define suite_all { "all", "test all suite (pseudo suite)", NULL } | ||
37 | |||
38 | static struct bench_suite sched_suites[] = { | ||
39 | { "messaging", | ||
40 | "Benchmark for scheduler and IPC mechanisms", | ||
41 | bench_sched_messaging }, | ||
42 | { "pipe", | ||
43 | "Flood of communication over pipe() between two processes", | ||
44 | bench_sched_pipe }, | ||
45 | suite_all, | ||
46 | { NULL, | ||
47 | NULL, | ||
48 | NULL } | ||
49 | }; | ||
50 | |||
51 | static struct bench_suite mem_suites[] = { | ||
52 | { "memcpy", | ||
53 | "Simple memory copy in various ways", | ||
54 | bench_mem_memcpy }, | ||
55 | suite_all, | ||
56 | { NULL, | ||
57 | NULL, | ||
58 | NULL } | ||
59 | }; | ||
60 | |||
61 | struct bench_subsys { | ||
62 | const char *name; | ||
63 | const char *summary; | ||
64 | struct bench_suite *suites; | ||
65 | }; | ||
66 | |||
67 | static struct bench_subsys subsystems[] = { | ||
68 | { "sched", | ||
69 | "scheduler and IPC mechanism", | ||
70 | sched_suites }, | ||
71 | { "mem", | ||
72 | "memory access performance", | ||
73 | mem_suites }, | ||
74 | { "all", /* sentinel: easy for help */ | ||
75 | "test all subsystem (pseudo subsystem)", | ||
76 | NULL }, | ||
77 | { NULL, | ||
78 | NULL, | ||
79 | NULL } | ||
80 | }; | ||
81 | |||
82 | static void dump_suites(int subsys_index) | ||
83 | { | ||
84 | int i; | ||
85 | |||
86 | printf("# List of available suites for %s...\n\n", | ||
87 | subsystems[subsys_index].name); | ||
88 | |||
89 | for (i = 0; subsystems[subsys_index].suites[i].name; i++) | ||
90 | printf("%14s: %s\n", | ||
91 | subsystems[subsys_index].suites[i].name, | ||
92 | subsystems[subsys_index].suites[i].summary); | ||
93 | |||
94 | printf("\n"); | ||
95 | return; | ||
96 | } | ||
97 | |||
98 | static char *bench_format_str; | ||
99 | int bench_format = BENCH_FORMAT_DEFAULT; | ||
100 | |||
101 | static const struct option bench_options[] = { | ||
102 | OPT_STRING('f', "format", &bench_format_str, "default", | ||
103 | "Specify format style"), | ||
104 | OPT_END() | ||
105 | }; | ||
106 | |||
107 | static const char * const bench_usage[] = { | ||
108 | "perf bench [<common options>] <subsystem> <suite> [<options>]", | ||
109 | NULL | ||
110 | }; | ||
111 | |||
112 | static void print_usage(void) | ||
113 | { | ||
114 | int i; | ||
115 | |||
116 | printf("Usage: \n"); | ||
117 | for (i = 0; bench_usage[i]; i++) | ||
118 | printf("\t%s\n", bench_usage[i]); | ||
119 | printf("\n"); | ||
120 | |||
121 | printf("# List of available subsystems...\n\n"); | ||
122 | |||
123 | for (i = 0; subsystems[i].name; i++) | ||
124 | printf("%14s: %s\n", | ||
125 | subsystems[i].name, subsystems[i].summary); | ||
126 | printf("\n"); | ||
127 | } | ||
128 | |||
129 | static int bench_str2int(char *str) | ||
130 | { | ||
131 | if (!str) | ||
132 | return BENCH_FORMAT_DEFAULT; | ||
133 | |||
134 | if (!strcmp(str, BENCH_FORMAT_DEFAULT_STR)) | ||
135 | return BENCH_FORMAT_DEFAULT; | ||
136 | else if (!strcmp(str, BENCH_FORMAT_SIMPLE_STR)) | ||
137 | return BENCH_FORMAT_SIMPLE; | ||
138 | |||
139 | return BENCH_FORMAT_UNKNOWN; | ||
140 | } | ||
141 | |||
142 | static void all_suite(struct bench_subsys *subsys) /* FROM HERE */ | ||
143 | { | ||
144 | int i; | ||
145 | const char *argv[2]; | ||
146 | struct bench_suite *suites = subsys->suites; | ||
147 | |||
148 | argv[1] = NULL; | ||
149 | /* | ||
150 | * TODO: | ||
151 | * preparing preset parameters for | ||
152 | * embedded, ordinary PC, HPC, etc... | ||
153 | * will be helpful | ||
154 | */ | ||
155 | for (i = 0; suites[i].fn; i++) { | ||
156 | printf("# Running %s/%s benchmark...\n", | ||
157 | subsys->name, | ||
158 | suites[i].name); | ||
159 | |||
160 | argv[1] = suites[i].name; | ||
161 | suites[i].fn(1, argv, NULL); | ||
162 | printf("\n"); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | static void all_subsystem(void) | ||
167 | { | ||
168 | int i; | ||
169 | for (i = 0; subsystems[i].suites; i++) | ||
170 | all_suite(&subsystems[i]); | ||
171 | } | ||
172 | |||
173 | int cmd_bench(int argc, const char **argv, const char *prefix __used) | ||
174 | { | ||
175 | int i, j, status = 0; | ||
176 | |||
177 | if (argc < 2) { | ||
178 | /* No subsystem specified. */ | ||
179 | print_usage(); | ||
180 | goto end; | ||
181 | } | ||
182 | |||
183 | argc = parse_options(argc, argv, bench_options, bench_usage, | ||
184 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
185 | |||
186 | bench_format = bench_str2int(bench_format_str); | ||
187 | if (bench_format == BENCH_FORMAT_UNKNOWN) { | ||
188 | printf("Unknown format descriptor:%s\n", bench_format_str); | ||
189 | goto end; | ||
190 | } | ||
191 | |||
192 | if (argc < 1) { | ||
193 | print_usage(); | ||
194 | goto end; | ||
195 | } | ||
196 | |||
197 | if (!strcmp(argv[0], "all")) { | ||
198 | all_subsystem(); | ||
199 | goto end; | ||
200 | } | ||
201 | |||
202 | for (i = 0; subsystems[i].name; i++) { | ||
203 | if (strcmp(subsystems[i].name, argv[0])) | ||
204 | continue; | ||
205 | |||
206 | if (argc < 2) { | ||
207 | /* No suite specified. */ | ||
208 | dump_suites(i); | ||
209 | goto end; | ||
210 | } | ||
211 | |||
212 | if (!strcmp(argv[1], "all")) { | ||
213 | all_suite(&subsystems[i]); | ||
214 | goto end; | ||
215 | } | ||
216 | |||
217 | for (j = 0; subsystems[i].suites[j].name; j++) { | ||
218 | if (strcmp(subsystems[i].suites[j].name, argv[1])) | ||
219 | continue; | ||
220 | |||
221 | if (bench_format == BENCH_FORMAT_DEFAULT) | ||
222 | printf("# Running %s/%s benchmark...\n", | ||
223 | subsystems[i].name, | ||
224 | subsystems[i].suites[j].name); | ||
225 | status = subsystems[i].suites[j].fn(argc - 1, | ||
226 | argv + 1, prefix); | ||
227 | goto end; | ||
228 | } | ||
229 | |||
230 | if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { | ||
231 | dump_suites(i); | ||
232 | goto end; | ||
233 | } | ||
234 | |||
235 | printf("Unknown suite:%s for %s\n", argv[1], argv[0]); | ||
236 | status = 1; | ||
237 | goto end; | ||
238 | } | ||
239 | |||
240 | printf("Unknown subsystem:%s\n", argv[0]); | ||
241 | status = 1; | ||
242 | |||
243 | end: | ||
244 | return status; | ||
245 | } | ||
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c new file mode 100644 index 000000000000..30a05f552c96 --- /dev/null +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * builtin-buildid-cache.c | ||
3 | * | ||
4 | * Builtin buildid-cache command: Manages build-id cache | ||
5 | * | ||
6 | * Copyright (C) 2010, Red Hat Inc. | ||
7 | * Copyright (C) 2010, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | */ | ||
9 | #include "builtin.h" | ||
10 | #include "perf.h" | ||
11 | #include "util/cache.h" | ||
12 | #include "util/debug.h" | ||
13 | #include "util/header.h" | ||
14 | #include "util/parse-options.h" | ||
15 | #include "util/strlist.h" | ||
16 | #include "util/symbol.h" | ||
17 | |||
18 | static char const *add_name_list_str, *remove_name_list_str; | ||
19 | |||
20 | static const char * const buildid_cache_usage[] = { | ||
21 | "perf buildid-cache [<options>]", | ||
22 | NULL | ||
23 | }; | ||
24 | |||
25 | static const struct option buildid_cache_options[] = { | ||
26 | OPT_STRING('a', "add", &add_name_list_str, | ||
27 | "file list", "file(s) to add"), | ||
28 | OPT_STRING('r', "remove", &remove_name_list_str, "file list", | ||
29 | "file(s) to remove"), | ||
30 | OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose"), | ||
31 | OPT_END() | ||
32 | }; | ||
33 | |||
34 | static int build_id_cache__add_file(const char *filename, const char *debugdir) | ||
35 | { | ||
36 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
37 | u8 build_id[BUILD_ID_SIZE]; | ||
38 | int err; | ||
39 | |||
40 | if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) { | ||
41 | pr_debug("Couldn't read a build-id in %s\n", filename); | ||
42 | return -1; | ||
43 | } | ||
44 | |||
45 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | ||
46 | err = build_id_cache__add_s(sbuild_id, debugdir, filename, false); | ||
47 | if (verbose) | ||
48 | pr_info("Adding %s %s: %s\n", sbuild_id, filename, | ||
49 | err ? "FAIL" : "Ok"); | ||
50 | return err; | ||
51 | } | ||
52 | |||
53 | static int build_id_cache__remove_file(const char *filename __used, | ||
54 | const char *debugdir __used) | ||
55 | { | ||
56 | u8 build_id[BUILD_ID_SIZE]; | ||
57 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
58 | |||
59 | int err; | ||
60 | |||
61 | if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) { | ||
62 | pr_debug("Couldn't read a build-id in %s\n", filename); | ||
63 | return -1; | ||
64 | } | ||
65 | |||
66 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | ||
67 | err = build_id_cache__remove_s(sbuild_id, debugdir); | ||
68 | if (verbose) | ||
69 | pr_info("Removing %s %s: %s\n", sbuild_id, filename, | ||
70 | err ? "FAIL" : "Ok"); | ||
71 | |||
72 | return err; | ||
73 | } | ||
74 | |||
75 | static int __cmd_buildid_cache(void) | ||
76 | { | ||
77 | struct strlist *list; | ||
78 | struct str_node *pos; | ||
79 | char debugdir[PATH_MAX]; | ||
80 | |||
81 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | ||
82 | DEBUG_CACHE_DIR); | ||
83 | |||
84 | if (add_name_list_str) { | ||
85 | list = strlist__new(true, add_name_list_str); | ||
86 | if (list) { | ||
87 | strlist__for_each(pos, list) | ||
88 | if (build_id_cache__add_file(pos->s, debugdir)) { | ||
89 | if (errno == EEXIST) { | ||
90 | pr_debug("%s already in the cache\n", | ||
91 | pos->s); | ||
92 | continue; | ||
93 | } | ||
94 | pr_warning("Couldn't add %s: %s\n", | ||
95 | pos->s, strerror(errno)); | ||
96 | } | ||
97 | |||
98 | strlist__delete(list); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | if (remove_name_list_str) { | ||
103 | list = strlist__new(true, remove_name_list_str); | ||
104 | if (list) { | ||
105 | strlist__for_each(pos, list) | ||
106 | if (build_id_cache__remove_file(pos->s, debugdir)) { | ||
107 | if (errno == ENOENT) { | ||
108 | pr_debug("%s wasn't in the cache\n", | ||
109 | pos->s); | ||
110 | continue; | ||
111 | } | ||
112 | pr_warning("Couldn't remove %s: %s\n", | ||
113 | pos->s, strerror(errno)); | ||
114 | } | ||
115 | |||
116 | strlist__delete(list); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | int cmd_buildid_cache(int argc, const char **argv, const char *prefix __used) | ||
124 | { | ||
125 | argc = parse_options(argc, argv, buildid_cache_options, | ||
126 | buildid_cache_usage, 0); | ||
127 | |||
128 | if (symbol__init() < 0) | ||
129 | return -1; | ||
130 | |||
131 | setup_pager(); | ||
132 | return __cmd_buildid_cache(); | ||
133 | } | ||
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c new file mode 100644 index 000000000000..d0675c02f81e --- /dev/null +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -0,0 +1,60 @@ | |||
1 | /* | ||
2 | * builtin-buildid-list.c | ||
3 | * | ||
4 | * Builtin buildid-list command: list buildids in perf.data | ||
5 | * | ||
6 | * Copyright (C) 2009, Red Hat Inc. | ||
7 | * Copyright (C) 2009, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | */ | ||
9 | #include "builtin.h" | ||
10 | #include "perf.h" | ||
11 | #include "util/build-id.h" | ||
12 | #include "util/cache.h" | ||
13 | #include "util/debug.h" | ||
14 | #include "util/parse-options.h" | ||
15 | #include "util/session.h" | ||
16 | #include "util/symbol.h" | ||
17 | |||
18 | static char const *input_name = "perf.data"; | ||
19 | static int force; | ||
20 | static bool with_hits; | ||
21 | |||
22 | static const char * const buildid_list_usage[] = { | ||
23 | "perf buildid-list [<options>]", | ||
24 | NULL | ||
25 | }; | ||
26 | |||
27 | static const struct option options[] = { | ||
28 | OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"), | ||
29 | OPT_STRING('i', "input", &input_name, "file", | ||
30 | "input file name"), | ||
31 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
32 | OPT_BOOLEAN('v', "verbose", &verbose, | ||
33 | "be more verbose"), | ||
34 | OPT_END() | ||
35 | }; | ||
36 | |||
37 | static int __cmd_buildid_list(void) | ||
38 | { | ||
39 | int err = -1; | ||
40 | struct perf_session *session; | ||
41 | |||
42 | session = perf_session__new(input_name, O_RDONLY, force); | ||
43 | if (session == NULL) | ||
44 | return -1; | ||
45 | |||
46 | if (with_hits) | ||
47 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); | ||
48 | |||
49 | dsos__fprintf_buildid(stdout, with_hits); | ||
50 | |||
51 | perf_session__delete(session); | ||
52 | return err; | ||
53 | } | ||
54 | |||
55 | int cmd_buildid_list(int argc, const char **argv, const char *prefix __used) | ||
56 | { | ||
57 | argc = parse_options(argc, argv, options, buildid_list_usage, 0); | ||
58 | setup_pager(); | ||
59 | return __cmd_buildid_list(); | ||
60 | } | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c new file mode 100644 index 000000000000..18b3f505f9db --- /dev/null +++ b/tools/perf/builtin-diff.c | |||
@@ -0,0 +1,239 @@ | |||
1 | /* | ||
2 | * builtin-diff.c | ||
3 | * | ||
4 | * Builtin diff command: Analyze two perf.data input files, look up and read | ||
5 | * DSOs and symbol information, sort them and produce a diff. | ||
6 | */ | ||
7 | #include "builtin.h" | ||
8 | |||
9 | #include "util/debug.h" | ||
10 | #include "util/event.h" | ||
11 | #include "util/hist.h" | ||
12 | #include "util/session.h" | ||
13 | #include "util/sort.h" | ||
14 | #include "util/symbol.h" | ||
15 | #include "util/util.h" | ||
16 | |||
17 | #include <stdlib.h> | ||
18 | |||
19 | static char const *input_old = "perf.data.old", | ||
20 | *input_new = "perf.data"; | ||
21 | static char diff__default_sort_order[] = "dso,symbol"; | ||
22 | static int force; | ||
23 | static bool show_displacement; | ||
24 | |||
25 | static int perf_session__add_hist_entry(struct perf_session *self, | ||
26 | struct addr_location *al, u64 count) | ||
27 | { | ||
28 | bool hit; | ||
29 | struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL, | ||
30 | count, &hit); | ||
31 | if (he == NULL) | ||
32 | return -ENOMEM; | ||
33 | |||
34 | if (hit) | ||
35 | he->count += count; | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static int diff__process_sample_event(event_t *event, struct perf_session *session) | ||
41 | { | ||
42 | struct addr_location al; | ||
43 | struct sample_data data = { .period = 1, }; | ||
44 | |||
45 | dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, | ||
46 | event->ip.pid, event->ip.ip); | ||
47 | |||
48 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { | ||
49 | pr_warning("problem processing %d event, skipping it.\n", | ||
50 | event->header.type); | ||
51 | return -1; | ||
52 | } | ||
53 | |||
54 | if (al.filtered || al.sym == NULL) | ||
55 | return 0; | ||
56 | |||
57 | event__parse_sample(event, session->sample_type, &data); | ||
58 | |||
59 | if (perf_session__add_hist_entry(session, &al, data.period)) { | ||
60 | pr_warning("problem incrementing symbol count, skipping event\n"); | ||
61 | return -1; | ||
62 | } | ||
63 | |||
64 | session->events_stats.total += data.period; | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | static struct perf_event_ops event_ops = { | ||
69 | .sample = diff__process_sample_event, | ||
70 | .mmap = event__process_mmap, | ||
71 | .comm = event__process_comm, | ||
72 | .exit = event__process_task, | ||
73 | .fork = event__process_task, | ||
74 | .lost = event__process_lost, | ||
75 | }; | ||
76 | |||
77 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, | ||
78 | struct hist_entry *he) | ||
79 | { | ||
80 | struct rb_node **p = &root->rb_node; | ||
81 | struct rb_node *parent = NULL; | ||
82 | struct hist_entry *iter; | ||
83 | |||
84 | while (*p != NULL) { | ||
85 | parent = *p; | ||
86 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
87 | if (hist_entry__cmp(he, iter) < 0) | ||
88 | p = &(*p)->rb_left; | ||
89 | else | ||
90 | p = &(*p)->rb_right; | ||
91 | } | ||
92 | |||
93 | rb_link_node(&he->rb_node, parent, p); | ||
94 | rb_insert_color(&he->rb_node, root); | ||
95 | } | ||
96 | |||
97 | static void perf_session__resort_hist_entries(struct perf_session *self) | ||
98 | { | ||
99 | unsigned long position = 1; | ||
100 | struct rb_root tmp = RB_ROOT; | ||
101 | struct rb_node *next = rb_first(&self->hists); | ||
102 | |||
103 | while (next != NULL) { | ||
104 | struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); | ||
105 | |||
106 | next = rb_next(&n->rb_node); | ||
107 | rb_erase(&n->rb_node, &self->hists); | ||
108 | n->position = position++; | ||
109 | perf_session__insert_hist_entry_by_name(&tmp, n); | ||
110 | } | ||
111 | |||
112 | self->hists = tmp; | ||
113 | } | ||
114 | |||
115 | static void perf_session__set_hist_entries_positions(struct perf_session *self) | ||
116 | { | ||
117 | perf_session__output_resort(self, self->events_stats.total); | ||
118 | perf_session__resort_hist_entries(self); | ||
119 | } | ||
120 | |||
121 | static struct hist_entry * | ||
122 | perf_session__find_hist_entry(struct perf_session *self, | ||
123 | struct hist_entry *he) | ||
124 | { | ||
125 | struct rb_node *n = self->hists.rb_node; | ||
126 | |||
127 | while (n) { | ||
128 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); | ||
129 | int64_t cmp = hist_entry__cmp(he, iter); | ||
130 | |||
131 | if (cmp < 0) | ||
132 | n = n->rb_left; | ||
133 | else if (cmp > 0) | ||
134 | n = n->rb_right; | ||
135 | else | ||
136 | return iter; | ||
137 | } | ||
138 | |||
139 | return NULL; | ||
140 | } | ||
141 | |||
142 | static void perf_session__match_hists(struct perf_session *old_session, | ||
143 | struct perf_session *new_session) | ||
144 | { | ||
145 | struct rb_node *nd; | ||
146 | |||
147 | for (nd = rb_first(&new_session->hists); nd; nd = rb_next(nd)) { | ||
148 | struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); | ||
149 | pos->pair = perf_session__find_hist_entry(old_session, pos); | ||
150 | } | ||
151 | } | ||
152 | |||
153 | static int __cmd_diff(void) | ||
154 | { | ||
155 | int ret, i; | ||
156 | struct perf_session *session[2]; | ||
157 | |||
158 | session[0] = perf_session__new(input_old, O_RDONLY, force); | ||
159 | session[1] = perf_session__new(input_new, O_RDONLY, force); | ||
160 | if (session[0] == NULL || session[1] == NULL) | ||
161 | return -ENOMEM; | ||
162 | |||
163 | for (i = 0; i < 2; ++i) { | ||
164 | ret = perf_session__process_events(session[i], &event_ops); | ||
165 | if (ret) | ||
166 | goto out_delete; | ||
167 | } | ||
168 | |||
169 | perf_session__output_resort(session[1], session[1]->events_stats.total); | ||
170 | if (show_displacement) | ||
171 | perf_session__set_hist_entries_positions(session[0]); | ||
172 | |||
173 | perf_session__match_hists(session[0], session[1]); | ||
174 | perf_session__fprintf_hists(session[1], session[0], | ||
175 | show_displacement, stdout); | ||
176 | out_delete: | ||
177 | for (i = 0; i < 2; ++i) | ||
178 | perf_session__delete(session[i]); | ||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | static const char * const diff_usage[] = { | ||
183 | "perf diff [<options>] [old_file] [new_file]", | ||
184 | NULL, | ||
185 | }; | ||
186 | |||
187 | static const struct option options[] = { | ||
188 | OPT_BOOLEAN('v', "verbose", &verbose, | ||
189 | "be more verbose (show symbol address, etc)"), | ||
190 | OPT_BOOLEAN('m', "displacement", &show_displacement, | ||
191 | "Show position displacement relative to baseline"), | ||
192 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||
193 | "dump raw trace in ASCII"), | ||
194 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
195 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | ||
196 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | ||
197 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, | ||
198 | "Don't shorten the pathnames taking into account the cwd"), | ||
199 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | ||
200 | "only consider symbols in these dsos"), | ||
201 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | ||
202 | "only consider symbols in these comms"), | ||
203 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | ||
204 | "only consider these symbols"), | ||
205 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | ||
206 | "sort by key(s): pid, comm, dso, symbol, parent"), | ||
207 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", | ||
208 | "separator for columns, no spaces will be added between " | ||
209 | "columns '.' is reserved."), | ||
210 | OPT_END() | ||
211 | }; | ||
212 | |||
213 | int cmd_diff(int argc, const char **argv, const char *prefix __used) | ||
214 | { | ||
215 | sort_order = diff__default_sort_order; | ||
216 | argc = parse_options(argc, argv, options, diff_usage, 0); | ||
217 | if (argc) { | ||
218 | if (argc > 2) | ||
219 | usage_with_options(diff_usage, options); | ||
220 | if (argc == 2) { | ||
221 | input_old = argv[0]; | ||
222 | input_new = argv[1]; | ||
223 | } else | ||
224 | input_new = argv[0]; | ||
225 | } | ||
226 | |||
227 | symbol_conf.exclude_other = false; | ||
228 | if (symbol__init() < 0) | ||
229 | return -1; | ||
230 | |||
231 | setup_sorting(diff_usage, options); | ||
232 | setup_pager(); | ||
233 | |||
234 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); | ||
235 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", NULL); | ||
236 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", NULL); | ||
237 | |||
238 | return __cmd_diff(); | ||
239 | } | ||
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 4fb8734a796e..215b584007b1 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c | |||
@@ -61,8 +61,7 @@ static const char *get_man_viewer_info(const char *name) | |||
61 | { | 61 | { |
62 | struct man_viewer_info_list *viewer; | 62 | struct man_viewer_info_list *viewer; |
63 | 63 | ||
64 | for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) | 64 | for (viewer = man_viewer_info_list; viewer; viewer = viewer->next) { |
65 | { | ||
66 | if (!strcasecmp(name, viewer->name)) | 65 | if (!strcasecmp(name, viewer->name)) |
67 | return viewer->info; | 66 | return viewer->info; |
68 | } | 67 | } |
@@ -115,7 +114,7 @@ static int check_emacsclient_version(void) | |||
115 | return 0; | 114 | return 0; |
116 | } | 115 | } |
117 | 116 | ||
118 | static void exec_woman_emacs(const char* path, const char *page) | 117 | static void exec_woman_emacs(const char *path, const char *page) |
119 | { | 118 | { |
120 | if (!check_emacsclient_version()) { | 119 | if (!check_emacsclient_version()) { |
121 | /* This works only with emacsclient version >= 22. */ | 120 | /* This works only with emacsclient version >= 22. */ |
@@ -129,7 +128,7 @@ static void exec_woman_emacs(const char* path, const char *page) | |||
129 | } | 128 | } |
130 | } | 129 | } |
131 | 130 | ||
132 | static void exec_man_konqueror(const char* path, const char *page) | 131 | static void exec_man_konqueror(const char *path, const char *page) |
133 | { | 132 | { |
134 | const char *display = getenv("DISPLAY"); | 133 | const char *display = getenv("DISPLAY"); |
135 | if (display && *display) { | 134 | if (display && *display) { |
@@ -157,7 +156,7 @@ static void exec_man_konqueror(const char* path, const char *page) | |||
157 | } | 156 | } |
158 | } | 157 | } |
159 | 158 | ||
160 | static void exec_man_man(const char* path, const char *page) | 159 | static void exec_man_man(const char *path, const char *page) |
161 | { | 160 | { |
162 | if (!path) | 161 | if (!path) |
163 | path = "man"; | 162 | path = "man"; |
@@ -180,7 +179,7 @@ static void add_man_viewer(const char *name) | |||
180 | 179 | ||
181 | while (*p) | 180 | while (*p) |
182 | p = &((*p)->next); | 181 | p = &((*p)->next); |
183 | *p = calloc(1, (sizeof(**p) + len + 1)); | 182 | *p = zalloc(sizeof(**p) + len + 1); |
184 | strncpy((*p)->name, name, len); | 183 | strncpy((*p)->name, name, len); |
185 | } | 184 | } |
186 | 185 | ||
@@ -195,7 +194,7 @@ static void do_add_man_viewer_info(const char *name, | |||
195 | size_t len, | 194 | size_t len, |
196 | const char *value) | 195 | const char *value) |
197 | { | 196 | { |
198 | struct man_viewer_info_list *new = calloc(1, sizeof(*new) + len + 1); | 197 | struct man_viewer_info_list *new = zalloc(sizeof(*new) + len + 1); |
199 | 198 | ||
200 | strncpy(new->name, name, len); | 199 | strncpy(new->name, name, len); |
201 | new->info = strdup(value); | 200 | new->info = strdup(value); |
@@ -287,8 +286,7 @@ void list_common_cmds_help(void) | |||
287 | 286 | ||
288 | puts(" The most commonly used perf commands are:"); | 287 | puts(" The most commonly used perf commands are:"); |
289 | for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { | 288 | for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { |
290 | printf(" %s ", common_cmds[i].name); | 289 | printf(" %-*s ", longest, common_cmds[i].name); |
291 | mput_char(' ', longest - strlen(common_cmds[i].name)); | ||
292 | puts(common_cmds[i].help); | 290 | puts(common_cmds[i].help); |
293 | } | 291 | } |
294 | } | 292 | } |
@@ -315,8 +313,6 @@ static const char *cmd_to_page(const char *perf_cmd) | |||
315 | return "perf"; | 313 | return "perf"; |
316 | else if (!prefixcmp(perf_cmd, "perf")) | 314 | else if (!prefixcmp(perf_cmd, "perf")) |
317 | return perf_cmd; | 315 | return perf_cmd; |
318 | else if (is_perf_command(perf_cmd)) | ||
319 | return prepend("perf-", perf_cmd); | ||
320 | else | 316 | else |
321 | return prepend("perf-", perf_cmd); | 317 | return prepend("perf-", perf_cmd); |
322 | } | 318 | } |
@@ -364,9 +360,8 @@ static void show_man_page(const char *perf_cmd) | |||
364 | 360 | ||
365 | setup_man_path(); | 361 | setup_man_path(); |
366 | for (viewer = man_viewer_list; viewer; viewer = viewer->next) | 362 | for (viewer = man_viewer_list; viewer; viewer = viewer->next) |
367 | { | ||
368 | exec_viewer(viewer->name, page); /* will return when unable */ | 363 | exec_viewer(viewer->name, page); /* will return when unable */ |
369 | } | 364 | |
370 | if (fallback) | 365 | if (fallback) |
371 | exec_viewer(fallback, page); | 366 | exec_viewer(fallback, page); |
372 | exec_viewer("man", page); | 367 | exec_viewer("man", page); |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c new file mode 100644 index 000000000000..924a9518931a --- /dev/null +++ b/tools/perf/builtin-kmem.c | |||
@@ -0,0 +1,774 @@ | |||
1 | #include "builtin.h" | ||
2 | #include "perf.h" | ||
3 | |||
4 | #include "util/util.h" | ||
5 | #include "util/cache.h" | ||
6 | #include "util/symbol.h" | ||
7 | #include "util/thread.h" | ||
8 | #include "util/header.h" | ||
9 | #include "util/session.h" | ||
10 | |||
11 | #include "util/parse-options.h" | ||
12 | #include "util/trace-event.h" | ||
13 | |||
14 | #include "util/debug.h" | ||
15 | |||
16 | #include <linux/rbtree.h> | ||
17 | |||
18 | struct alloc_stat; | ||
19 | typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); | ||
20 | |||
21 | static char const *input_name = "perf.data"; | ||
22 | |||
23 | static int alloc_flag; | ||
24 | static int caller_flag; | ||
25 | |||
26 | static int alloc_lines = -1; | ||
27 | static int caller_lines = -1; | ||
28 | |||
29 | static bool raw_ip; | ||
30 | |||
31 | static char default_sort_order[] = "frag,hit,bytes"; | ||
32 | |||
33 | static int *cpunode_map; | ||
34 | static int max_cpu_num; | ||
35 | |||
36 | struct alloc_stat { | ||
37 | u64 call_site; | ||
38 | u64 ptr; | ||
39 | u64 bytes_req; | ||
40 | u64 bytes_alloc; | ||
41 | u32 hit; | ||
42 | u32 pingpong; | ||
43 | |||
44 | short alloc_cpu; | ||
45 | |||
46 | struct rb_node node; | ||
47 | }; | ||
48 | |||
49 | static struct rb_root root_alloc_stat; | ||
50 | static struct rb_root root_alloc_sorted; | ||
51 | static struct rb_root root_caller_stat; | ||
52 | static struct rb_root root_caller_sorted; | ||
53 | |||
54 | static unsigned long total_requested, total_allocated; | ||
55 | static unsigned long nr_allocs, nr_cross_allocs; | ||
56 | |||
57 | #define PATH_SYS_NODE "/sys/devices/system/node" | ||
58 | |||
59 | static void init_cpunode_map(void) | ||
60 | { | ||
61 | FILE *fp; | ||
62 | int i; | ||
63 | |||
64 | fp = fopen("/sys/devices/system/cpu/kernel_max", "r"); | ||
65 | if (!fp) { | ||
66 | max_cpu_num = 4096; | ||
67 | return; | ||
68 | } | ||
69 | |||
70 | if (fscanf(fp, "%d", &max_cpu_num) < 1) | ||
71 | die("Failed to read 'kernel_max' from sysfs"); | ||
72 | max_cpu_num++; | ||
73 | |||
74 | cpunode_map = calloc(max_cpu_num, sizeof(int)); | ||
75 | if (!cpunode_map) | ||
76 | die("calloc"); | ||
77 | for (i = 0; i < max_cpu_num; i++) | ||
78 | cpunode_map[i] = -1; | ||
79 | fclose(fp); | ||
80 | } | ||
81 | |||
82 | static void setup_cpunode_map(void) | ||
83 | { | ||
84 | struct dirent *dent1, *dent2; | ||
85 | DIR *dir1, *dir2; | ||
86 | unsigned int cpu, mem; | ||
87 | char buf[PATH_MAX]; | ||
88 | |||
89 | init_cpunode_map(); | ||
90 | |||
91 | dir1 = opendir(PATH_SYS_NODE); | ||
92 | if (!dir1) | ||
93 | return; | ||
94 | |||
95 | while ((dent1 = readdir(dir1)) != NULL) { | ||
96 | if (dent1->d_type != DT_DIR || | ||
97 | sscanf(dent1->d_name, "node%u", &mem) < 1) | ||
98 | continue; | ||
99 | |||
100 | snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name); | ||
101 | dir2 = opendir(buf); | ||
102 | if (!dir2) | ||
103 | continue; | ||
104 | while ((dent2 = readdir(dir2)) != NULL) { | ||
105 | if (dent2->d_type != DT_LNK || | ||
106 | sscanf(dent2->d_name, "cpu%u", &cpu) < 1) | ||
107 | continue; | ||
108 | cpunode_map[cpu] = mem; | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | |||
113 | static void insert_alloc_stat(unsigned long call_site, unsigned long ptr, | ||
114 | int bytes_req, int bytes_alloc, int cpu) | ||
115 | { | ||
116 | struct rb_node **node = &root_alloc_stat.rb_node; | ||
117 | struct rb_node *parent = NULL; | ||
118 | struct alloc_stat *data = NULL; | ||
119 | |||
120 | while (*node) { | ||
121 | parent = *node; | ||
122 | data = rb_entry(*node, struct alloc_stat, node); | ||
123 | |||
124 | if (ptr > data->ptr) | ||
125 | node = &(*node)->rb_right; | ||
126 | else if (ptr < data->ptr) | ||
127 | node = &(*node)->rb_left; | ||
128 | else | ||
129 | break; | ||
130 | } | ||
131 | |||
132 | if (data && data->ptr == ptr) { | ||
133 | data->hit++; | ||
134 | data->bytes_req += bytes_req; | ||
135 | data->bytes_alloc += bytes_alloc; | ||
136 | } else { | ||
137 | data = malloc(sizeof(*data)); | ||
138 | if (!data) | ||
139 | die("malloc"); | ||
140 | data->ptr = ptr; | ||
141 | data->pingpong = 0; | ||
142 | data->hit = 1; | ||
143 | data->bytes_req = bytes_req; | ||
144 | data->bytes_alloc = bytes_alloc; | ||
145 | |||
146 | rb_link_node(&data->node, parent, node); | ||
147 | rb_insert_color(&data->node, &root_alloc_stat); | ||
148 | } | ||
149 | data->call_site = call_site; | ||
150 | data->alloc_cpu = cpu; | ||
151 | } | ||
152 | |||
153 | static void insert_caller_stat(unsigned long call_site, | ||
154 | int bytes_req, int bytes_alloc) | ||
155 | { | ||
156 | struct rb_node **node = &root_caller_stat.rb_node; | ||
157 | struct rb_node *parent = NULL; | ||
158 | struct alloc_stat *data = NULL; | ||
159 | |||
160 | while (*node) { | ||
161 | parent = *node; | ||
162 | data = rb_entry(*node, struct alloc_stat, node); | ||
163 | |||
164 | if (call_site > data->call_site) | ||
165 | node = &(*node)->rb_right; | ||
166 | else if (call_site < data->call_site) | ||
167 | node = &(*node)->rb_left; | ||
168 | else | ||
169 | break; | ||
170 | } | ||
171 | |||
172 | if (data && data->call_site == call_site) { | ||
173 | data->hit++; | ||
174 | data->bytes_req += bytes_req; | ||
175 | data->bytes_alloc += bytes_alloc; | ||
176 | } else { | ||
177 | data = malloc(sizeof(*data)); | ||
178 | if (!data) | ||
179 | die("malloc"); | ||
180 | data->call_site = call_site; | ||
181 | data->pingpong = 0; | ||
182 | data->hit = 1; | ||
183 | data->bytes_req = bytes_req; | ||
184 | data->bytes_alloc = bytes_alloc; | ||
185 | |||
186 | rb_link_node(&data->node, parent, node); | ||
187 | rb_insert_color(&data->node, &root_caller_stat); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | static void process_alloc_event(void *data, | ||
192 | struct event *event, | ||
193 | int cpu, | ||
194 | u64 timestamp __used, | ||
195 | struct thread *thread __used, | ||
196 | int node) | ||
197 | { | ||
198 | unsigned long call_site; | ||
199 | unsigned long ptr; | ||
200 | int bytes_req; | ||
201 | int bytes_alloc; | ||
202 | int node1, node2; | ||
203 | |||
204 | ptr = raw_field_value(event, "ptr", data); | ||
205 | call_site = raw_field_value(event, "call_site", data); | ||
206 | bytes_req = raw_field_value(event, "bytes_req", data); | ||
207 | bytes_alloc = raw_field_value(event, "bytes_alloc", data); | ||
208 | |||
209 | insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu); | ||
210 | insert_caller_stat(call_site, bytes_req, bytes_alloc); | ||
211 | |||
212 | total_requested += bytes_req; | ||
213 | total_allocated += bytes_alloc; | ||
214 | |||
215 | if (node) { | ||
216 | node1 = cpunode_map[cpu]; | ||
217 | node2 = raw_field_value(event, "node", data); | ||
218 | if (node1 != node2) | ||
219 | nr_cross_allocs++; | ||
220 | } | ||
221 | nr_allocs++; | ||
222 | } | ||
223 | |||
224 | static int ptr_cmp(struct alloc_stat *, struct alloc_stat *); | ||
225 | static int callsite_cmp(struct alloc_stat *, struct alloc_stat *); | ||
226 | |||
227 | static struct alloc_stat *search_alloc_stat(unsigned long ptr, | ||
228 | unsigned long call_site, | ||
229 | struct rb_root *root, | ||
230 | sort_fn_t sort_fn) | ||
231 | { | ||
232 | struct rb_node *node = root->rb_node; | ||
233 | struct alloc_stat key = { .ptr = ptr, .call_site = call_site }; | ||
234 | |||
235 | while (node) { | ||
236 | struct alloc_stat *data; | ||
237 | int cmp; | ||
238 | |||
239 | data = rb_entry(node, struct alloc_stat, node); | ||
240 | |||
241 | cmp = sort_fn(&key, data); | ||
242 | if (cmp < 0) | ||
243 | node = node->rb_left; | ||
244 | else if (cmp > 0) | ||
245 | node = node->rb_right; | ||
246 | else | ||
247 | return data; | ||
248 | } | ||
249 | return NULL; | ||
250 | } | ||
251 | |||
252 | static void process_free_event(void *data, | ||
253 | struct event *event, | ||
254 | int cpu, | ||
255 | u64 timestamp __used, | ||
256 | struct thread *thread __used) | ||
257 | { | ||
258 | unsigned long ptr; | ||
259 | struct alloc_stat *s_alloc, *s_caller; | ||
260 | |||
261 | ptr = raw_field_value(event, "ptr", data); | ||
262 | |||
263 | s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp); | ||
264 | if (!s_alloc) | ||
265 | return; | ||
266 | |||
267 | if (cpu != s_alloc->alloc_cpu) { | ||
268 | s_alloc->pingpong++; | ||
269 | |||
270 | s_caller = search_alloc_stat(0, s_alloc->call_site, | ||
271 | &root_caller_stat, callsite_cmp); | ||
272 | assert(s_caller); | ||
273 | s_caller->pingpong++; | ||
274 | } | ||
275 | s_alloc->alloc_cpu = -1; | ||
276 | } | ||
277 | |||
278 | static void | ||
279 | process_raw_event(event_t *raw_event __used, void *data, | ||
280 | int cpu, u64 timestamp, struct thread *thread) | ||
281 | { | ||
282 | struct event *event; | ||
283 | int type; | ||
284 | |||
285 | type = trace_parse_common_type(data); | ||
286 | event = trace_find_event(type); | ||
287 | |||
288 | if (!strcmp(event->name, "kmalloc") || | ||
289 | !strcmp(event->name, "kmem_cache_alloc")) { | ||
290 | process_alloc_event(data, event, cpu, timestamp, thread, 0); | ||
291 | return; | ||
292 | } | ||
293 | |||
294 | if (!strcmp(event->name, "kmalloc_node") || | ||
295 | !strcmp(event->name, "kmem_cache_alloc_node")) { | ||
296 | process_alloc_event(data, event, cpu, timestamp, thread, 1); | ||
297 | return; | ||
298 | } | ||
299 | |||
300 | if (!strcmp(event->name, "kfree") || | ||
301 | !strcmp(event->name, "kmem_cache_free")) { | ||
302 | process_free_event(data, event, cpu, timestamp, thread); | ||
303 | return; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | static int process_sample_event(event_t *event, struct perf_session *session) | ||
308 | { | ||
309 | struct sample_data data; | ||
310 | struct thread *thread; | ||
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) { | ||
324 | pr_debug("problem processing %d event, skipping it.\n", | ||
325 | event->header.type); | ||
326 | return -1; | ||
327 | } | ||
328 | |||
329 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | ||
330 | |||
331 | process_raw_event(event, data.raw_data, data.cpu, | ||
332 | data.time, thread); | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static struct perf_event_ops event_ops = { | ||
338 | .sample = process_sample_event, | ||
339 | .comm = event__process_comm, | ||
340 | }; | ||
341 | |||
342 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) | ||
343 | { | ||
344 | if (n_alloc == 0) | ||
345 | return 0.0; | ||
346 | else | ||
347 | return 100.0 - (100.0 * n_req / n_alloc); | ||
348 | } | ||
349 | |||
350 | static void __print_result(struct rb_root *root, struct perf_session *session, | ||
351 | int n_lines, int is_caller) | ||
352 | { | ||
353 | struct rb_node *next; | ||
354 | |||
355 | printf("%.102s\n", graph_dotted_line); | ||
356 | printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); | ||
357 | printf(" Total_alloc/Per | Total_req/Per | Hit | Ping-pong | Frag\n"); | ||
358 | printf("%.102s\n", graph_dotted_line); | ||
359 | |||
360 | next = rb_first(root); | ||
361 | |||
362 | while (next && n_lines--) { | ||
363 | struct alloc_stat *data = rb_entry(next, struct alloc_stat, | ||
364 | node); | ||
365 | struct symbol *sym = NULL; | ||
366 | char buf[BUFSIZ]; | ||
367 | u64 addr; | ||
368 | |||
369 | if (is_caller) { | ||
370 | addr = data->call_site; | ||
371 | if (!raw_ip) | ||
372 | sym = map_groups__find_function(&session->kmaps, addr, NULL); | ||
373 | } else | ||
374 | addr = data->ptr; | ||
375 | |||
376 | if (sym != NULL) | ||
377 | snprintf(buf, sizeof(buf), "%s+%Lx", sym->name, | ||
378 | addr - sym->start); | ||
379 | else | ||
380 | snprintf(buf, sizeof(buf), "%#Lx", addr); | ||
381 | printf(" %-34s |", buf); | ||
382 | |||
383 | printf(" %9llu/%-5lu | %9llu/%-5lu | %8lu | %8lu | %6.3f%%\n", | ||
384 | (unsigned long long)data->bytes_alloc, | ||
385 | (unsigned long)data->bytes_alloc / data->hit, | ||
386 | (unsigned long long)data->bytes_req, | ||
387 | (unsigned long)data->bytes_req / data->hit, | ||
388 | (unsigned long)data->hit, | ||
389 | (unsigned long)data->pingpong, | ||
390 | fragmentation(data->bytes_req, data->bytes_alloc)); | ||
391 | |||
392 | next = rb_next(next); | ||
393 | } | ||
394 | |||
395 | if (n_lines == -1) | ||
396 | printf(" ... | ... | ... | ... | ... | ... \n"); | ||
397 | |||
398 | printf("%.102s\n", graph_dotted_line); | ||
399 | } | ||
400 | |||
401 | static void print_summary(void) | ||
402 | { | ||
403 | printf("\nSUMMARY\n=======\n"); | ||
404 | printf("Total bytes requested: %lu\n", total_requested); | ||
405 | printf("Total bytes allocated: %lu\n", total_allocated); | ||
406 | printf("Total bytes wasted on internal fragmentation: %lu\n", | ||
407 | total_allocated - total_requested); | ||
408 | printf("Internal fragmentation: %f%%\n", | ||
409 | fragmentation(total_requested, total_allocated)); | ||
410 | printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs); | ||
411 | } | ||
412 | |||
413 | static void print_result(struct perf_session *session) | ||
414 | { | ||
415 | if (caller_flag) | ||
416 | __print_result(&root_caller_sorted, session, caller_lines, 1); | ||
417 | if (alloc_flag) | ||
418 | __print_result(&root_alloc_sorted, session, alloc_lines, 0); | ||
419 | print_summary(); | ||
420 | } | ||
421 | |||
422 | struct sort_dimension { | ||
423 | const char name[20]; | ||
424 | sort_fn_t cmp; | ||
425 | struct list_head list; | ||
426 | }; | ||
427 | |||
428 | static LIST_HEAD(caller_sort); | ||
429 | static LIST_HEAD(alloc_sort); | ||
430 | |||
431 | static void sort_insert(struct rb_root *root, struct alloc_stat *data, | ||
432 | struct list_head *sort_list) | ||
433 | { | ||
434 | struct rb_node **new = &(root->rb_node); | ||
435 | struct rb_node *parent = NULL; | ||
436 | struct sort_dimension *sort; | ||
437 | |||
438 | while (*new) { | ||
439 | struct alloc_stat *this; | ||
440 | int cmp = 0; | ||
441 | |||
442 | this = rb_entry(*new, struct alloc_stat, node); | ||
443 | parent = *new; | ||
444 | |||
445 | list_for_each_entry(sort, sort_list, list) { | ||
446 | cmp = sort->cmp(data, this); | ||
447 | if (cmp) | ||
448 | break; | ||
449 | } | ||
450 | |||
451 | if (cmp > 0) | ||
452 | new = &((*new)->rb_left); | ||
453 | else | ||
454 | new = &((*new)->rb_right); | ||
455 | } | ||
456 | |||
457 | rb_link_node(&data->node, parent, new); | ||
458 | rb_insert_color(&data->node, root); | ||
459 | } | ||
460 | |||
461 | static void __sort_result(struct rb_root *root, struct rb_root *root_sorted, | ||
462 | struct list_head *sort_list) | ||
463 | { | ||
464 | struct rb_node *node; | ||
465 | struct alloc_stat *data; | ||
466 | |||
467 | for (;;) { | ||
468 | node = rb_first(root); | ||
469 | if (!node) | ||
470 | break; | ||
471 | |||
472 | rb_erase(node, root); | ||
473 | data = rb_entry(node, struct alloc_stat, node); | ||
474 | sort_insert(root_sorted, data, sort_list); | ||
475 | } | ||
476 | } | ||
477 | |||
478 | static void sort_result(void) | ||
479 | { | ||
480 | __sort_result(&root_alloc_stat, &root_alloc_sorted, &alloc_sort); | ||
481 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); | ||
482 | } | ||
483 | |||
484 | static int __cmd_kmem(void) | ||
485 | { | ||
486 | int err = -EINVAL; | ||
487 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); | ||
488 | if (session == NULL) | ||
489 | return -ENOMEM; | ||
490 | |||
491 | if (!perf_session__has_traces(session, "kmem record")) | ||
492 | goto out_delete; | ||
493 | |||
494 | setup_pager(); | ||
495 | err = perf_session__process_events(session, &event_ops); | ||
496 | if (err != 0) | ||
497 | goto out_delete; | ||
498 | sort_result(); | ||
499 | print_result(session); | ||
500 | out_delete: | ||
501 | perf_session__delete(session); | ||
502 | return err; | ||
503 | } | ||
504 | |||
505 | static const char * const kmem_usage[] = { | ||
506 | "perf kmem [<options>] {record|stat}", | ||
507 | NULL | ||
508 | }; | ||
509 | |||
510 | static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r) | ||
511 | { | ||
512 | if (l->ptr < r->ptr) | ||
513 | return -1; | ||
514 | else if (l->ptr > r->ptr) | ||
515 | return 1; | ||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | static struct sort_dimension ptr_sort_dimension = { | ||
520 | .name = "ptr", | ||
521 | .cmp = ptr_cmp, | ||
522 | }; | ||
523 | |||
524 | static int callsite_cmp(struct alloc_stat *l, struct alloc_stat *r) | ||
525 | { | ||
526 | if (l->call_site < r->call_site) | ||
527 | return -1; | ||
528 | else if (l->call_site > r->call_site) | ||
529 | return 1; | ||
530 | return 0; | ||
531 | } | ||
532 | |||
533 | static struct sort_dimension callsite_sort_dimension = { | ||
534 | .name = "callsite", | ||
535 | .cmp = callsite_cmp, | ||
536 | }; | ||
537 | |||
538 | static int hit_cmp(struct alloc_stat *l, struct alloc_stat *r) | ||
539 | { | ||
540 | if (l->hit < r->hit) | ||
541 | return -1; | ||
542 | else if (l->hit > r->hit) | ||
543 | return 1; | ||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static struct sort_dimension hit_sort_dimension = { | ||
548 | .name = "hit", | ||
549 | .cmp = hit_cmp, | ||
550 | }; | ||
551 | |||
552 | static int bytes_cmp(struct alloc_stat *l, struct alloc_stat *r) | ||
553 | { | ||
554 | if (l->bytes_alloc < r->bytes_alloc) | ||
555 | return -1; | ||
556 | else if (l->bytes_alloc > r->bytes_alloc) | ||
557 | return 1; | ||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | static struct sort_dimension bytes_sort_dimension = { | ||
562 | .name = "bytes", | ||
563 | .cmp = bytes_cmp, | ||
564 | }; | ||
565 | |||
566 | static int frag_cmp(struct alloc_stat *l, struct alloc_stat *r) | ||
567 | { | ||
568 | double x, y; | ||
569 | |||
570 | x = fragmentation(l->bytes_req, l->bytes_alloc); | ||
571 | y = fragmentation(r->bytes_req, r->bytes_alloc); | ||
572 | |||
573 | if (x < y) | ||
574 | return -1; | ||
575 | else if (x > y) | ||
576 | return 1; | ||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static struct sort_dimension frag_sort_dimension = { | ||
581 | .name = "frag", | ||
582 | .cmp = frag_cmp, | ||
583 | }; | ||
584 | |||
585 | static int pingpong_cmp(struct alloc_stat *l, struct alloc_stat *r) | ||
586 | { | ||
587 | if (l->pingpong < r->pingpong) | ||
588 | return -1; | ||
589 | else if (l->pingpong > r->pingpong) | ||
590 | return 1; | ||
591 | return 0; | ||
592 | } | ||
593 | |||
594 | static struct sort_dimension pingpong_sort_dimension = { | ||
595 | .name = "pingpong", | ||
596 | .cmp = pingpong_cmp, | ||
597 | }; | ||
598 | |||
599 | static struct sort_dimension *avail_sorts[] = { | ||
600 | &ptr_sort_dimension, | ||
601 | &callsite_sort_dimension, | ||
602 | &hit_sort_dimension, | ||
603 | &bytes_sort_dimension, | ||
604 | &frag_sort_dimension, | ||
605 | &pingpong_sort_dimension, | ||
606 | }; | ||
607 | |||
608 | #define NUM_AVAIL_SORTS \ | ||
609 | (int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *)) | ||
610 | |||
611 | static int sort_dimension__add(const char *tok, struct list_head *list) | ||
612 | { | ||
613 | struct sort_dimension *sort; | ||
614 | int i; | ||
615 | |||
616 | for (i = 0; i < NUM_AVAIL_SORTS; i++) { | ||
617 | if (!strcmp(avail_sorts[i]->name, tok)) { | ||
618 | sort = malloc(sizeof(*sort)); | ||
619 | if (!sort) | ||
620 | die("malloc"); | ||
621 | memcpy(sort, avail_sorts[i], sizeof(*sort)); | ||
622 | list_add_tail(&sort->list, list); | ||
623 | return 0; | ||
624 | } | ||
625 | } | ||
626 | |||
627 | return -1; | ||
628 | } | ||
629 | |||
630 | static int setup_sorting(struct list_head *sort_list, const char *arg) | ||
631 | { | ||
632 | char *tok; | ||
633 | char *str = strdup(arg); | ||
634 | |||
635 | if (!str) | ||
636 | die("strdup"); | ||
637 | |||
638 | while (true) { | ||
639 | tok = strsep(&str, ","); | ||
640 | if (!tok) | ||
641 | break; | ||
642 | if (sort_dimension__add(tok, sort_list) < 0) { | ||
643 | error("Unknown --sort key: '%s'", tok); | ||
644 | return -1; | ||
645 | } | ||
646 | } | ||
647 | |||
648 | free(str); | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | static int parse_sort_opt(const struct option *opt __used, | ||
653 | const char *arg, int unset __used) | ||
654 | { | ||
655 | if (!arg) | ||
656 | return -1; | ||
657 | |||
658 | if (caller_flag > alloc_flag) | ||
659 | return setup_sorting(&caller_sort, arg); | ||
660 | else | ||
661 | return setup_sorting(&alloc_sort, arg); | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static int parse_caller_opt(const struct option *opt __used, | ||
667 | const char *arg __used, int unset __used) | ||
668 | { | ||
669 | caller_flag = (alloc_flag + 1); | ||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static int parse_alloc_opt(const struct option *opt __used, | ||
674 | const char *arg __used, int unset __used) | ||
675 | { | ||
676 | alloc_flag = (caller_flag + 1); | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | static int parse_line_opt(const struct option *opt __used, | ||
681 | const char *arg, int unset __used) | ||
682 | { | ||
683 | int lines; | ||
684 | |||
685 | if (!arg) | ||
686 | return -1; | ||
687 | |||
688 | lines = strtoul(arg, NULL, 10); | ||
689 | |||
690 | if (caller_flag > alloc_flag) | ||
691 | caller_lines = lines; | ||
692 | else | ||
693 | alloc_lines = lines; | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | static const struct option kmem_options[] = { | ||
699 | OPT_STRING('i', "input", &input_name, "file", | ||
700 | "input file name"), | ||
701 | OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, | ||
702 | "show per-callsite statistics", | ||
703 | parse_caller_opt), | ||
704 | OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL, | ||
705 | "show per-allocation statistics", | ||
706 | parse_alloc_opt), | ||
707 | OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", | ||
708 | "sort by keys: ptr, call_site, bytes, hit, pingpong, frag", | ||
709 | parse_sort_opt), | ||
710 | OPT_CALLBACK('l', "line", NULL, "num", | ||
711 | "show n lines", | ||
712 | parse_line_opt), | ||
713 | OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), | ||
714 | OPT_END() | ||
715 | }; | ||
716 | |||
717 | static const char *record_args[] = { | ||
718 | "record", | ||
719 | "-a", | ||
720 | "-R", | ||
721 | "-M", | ||
722 | "-f", | ||
723 | "-c", "1", | ||
724 | "-e", "kmem:kmalloc", | ||
725 | "-e", "kmem:kmalloc_node", | ||
726 | "-e", "kmem:kfree", | ||
727 | "-e", "kmem:kmem_cache_alloc", | ||
728 | "-e", "kmem:kmem_cache_alloc_node", | ||
729 | "-e", "kmem:kmem_cache_free", | ||
730 | }; | ||
731 | |||
732 | static int __cmd_record(int argc, const char **argv) | ||
733 | { | ||
734 | unsigned int rec_argc, i, j; | ||
735 | const char **rec_argv; | ||
736 | |||
737 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | ||
738 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
739 | |||
740 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | ||
741 | rec_argv[i] = strdup(record_args[i]); | ||
742 | |||
743 | for (j = 1; j < (unsigned int)argc; j++, i++) | ||
744 | rec_argv[i] = argv[j]; | ||
745 | |||
746 | return cmd_record(i, rec_argv, NULL); | ||
747 | } | ||
748 | |||
749 | int cmd_kmem(int argc, const char **argv, const char *prefix __used) | ||
750 | { | ||
751 | argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); | ||
752 | |||
753 | if (!argc) | ||
754 | usage_with_options(kmem_usage, kmem_options); | ||
755 | |||
756 | symbol__init(); | ||
757 | |||
758 | if (!strncmp(argv[0], "rec", 3)) { | ||
759 | return __cmd_record(argc, argv); | ||
760 | } else if (!strcmp(argv[0], "stat")) { | ||
761 | setup_cpunode_map(); | ||
762 | |||
763 | if (list_empty(&caller_sort)) | ||
764 | setup_sorting(&caller_sort, default_sort_order); | ||
765 | if (list_empty(&alloc_sort)) | ||
766 | setup_sorting(&alloc_sort, default_sort_order); | ||
767 | |||
768 | return __cmd_kmem(); | ||
769 | } else | ||
770 | usage_with_options(kmem_usage, kmem_options); | ||
771 | |||
772 | return 0; | ||
773 | } | ||
774 | |||
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c new file mode 100644 index 000000000000..e12c844df1e2 --- /dev/null +++ b/tools/perf/builtin-lock.c | |||
@@ -0,0 +1,822 @@ | |||
1 | #include "builtin.h" | ||
2 | #include "perf.h" | ||
3 | |||
4 | #include "util/util.h" | ||
5 | #include "util/cache.h" | ||
6 | #include "util/symbol.h" | ||
7 | #include "util/thread.h" | ||
8 | #include "util/header.h" | ||
9 | |||
10 | #include "util/parse-options.h" | ||
11 | #include "util/trace-event.h" | ||
12 | |||
13 | #include "util/debug.h" | ||
14 | #include "util/session.h" | ||
15 | |||
16 | #include <sys/types.h> | ||
17 | #include <sys/prctl.h> | ||
18 | #include <semaphore.h> | ||
19 | #include <pthread.h> | ||
20 | #include <math.h> | ||
21 | #include <limits.h> | ||
22 | |||
23 | #include <linux/list.h> | ||
24 | #include <linux/hash.h> | ||
25 | |||
26 | /* based on kernel/lockdep.c */ | ||
27 | #define LOCKHASH_BITS 12 | ||
28 | #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) | ||
29 | |||
30 | static struct list_head lockhash_table[LOCKHASH_SIZE]; | ||
31 | |||
32 | #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) | ||
33 | #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) | ||
34 | |||
35 | #define LOCK_STATE_UNLOCKED 0 /* initial state */ | ||
36 | #define LOCK_STATE_LOCKED 1 | ||
37 | |||
38 | struct lock_stat { | ||
39 | struct list_head hash_entry; | ||
40 | struct rb_node rb; /* used for sorting */ | ||
41 | |||
42 | /* | ||
43 | * FIXME: raw_field_value() returns unsigned long long, | ||
44 | * so address of lockdep_map should be dealed as 64bit. | ||
45 | * Is there more better solution? | ||
46 | */ | ||
47 | void *addr; /* address of lockdep_map, used as ID */ | ||
48 | char *name; /* for strcpy(), we cannot use const */ | ||
49 | |||
50 | int state; | ||
51 | u64 prev_event_time; /* timestamp of previous event */ | ||
52 | |||
53 | unsigned int nr_acquired; | ||
54 | unsigned int nr_acquire; | ||
55 | unsigned int nr_contended; | ||
56 | unsigned int nr_release; | ||
57 | |||
58 | /* these times are in nano sec. */ | ||
59 | u64 wait_time_total; | ||
60 | u64 wait_time_min; | ||
61 | u64 wait_time_max; | ||
62 | }; | ||
63 | |||
64 | /* build simple key function one is bigger than two */ | ||
65 | #define SINGLE_KEY(member) \ | ||
66 | static int lock_stat_key_ ## member(struct lock_stat *one, \ | ||
67 | struct lock_stat *two) \ | ||
68 | { \ | ||
69 | return one->member > two->member; \ | ||
70 | } | ||
71 | |||
72 | SINGLE_KEY(nr_acquired) | ||
73 | SINGLE_KEY(nr_contended) | ||
74 | SINGLE_KEY(wait_time_total) | ||
75 | SINGLE_KEY(wait_time_min) | ||
76 | SINGLE_KEY(wait_time_max) | ||
77 | |||
78 | struct lock_key { | ||
79 | /* | ||
80 | * name: the value for specify by user | ||
81 | * this should be simpler than raw name of member | ||
82 | * e.g. nr_acquired -> acquired, wait_time_total -> wait_total | ||
83 | */ | ||
84 | const char *name; | ||
85 | int (*key)(struct lock_stat*, struct lock_stat*); | ||
86 | }; | ||
87 | |||
88 | static const char *sort_key = "acquired"; | ||
89 | |||
90 | static int (*compare)(struct lock_stat *, struct lock_stat *); | ||
91 | |||
92 | static struct rb_root result; /* place to store sorted data */ | ||
93 | |||
94 | #define DEF_KEY_LOCK(name, fn_suffix) \ | ||
95 | { #name, lock_stat_key_ ## fn_suffix } | ||
96 | struct lock_key keys[] = { | ||
97 | DEF_KEY_LOCK(acquired, nr_acquired), | ||
98 | DEF_KEY_LOCK(contended, nr_contended), | ||
99 | DEF_KEY_LOCK(wait_total, wait_time_total), | ||
100 | DEF_KEY_LOCK(wait_min, wait_time_min), | ||
101 | DEF_KEY_LOCK(wait_max, wait_time_max), | ||
102 | |||
103 | /* extra comparisons much complicated should be here */ | ||
104 | |||
105 | { NULL, NULL } | ||
106 | }; | ||
107 | |||
108 | static void select_key(void) | ||
109 | { | ||
110 | int i; | ||
111 | |||
112 | for (i = 0; keys[i].name; i++) { | ||
113 | if (!strcmp(keys[i].name, sort_key)) { | ||
114 | compare = keys[i].key; | ||
115 | return; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | die("Unknown compare key:%s\n", sort_key); | ||
120 | } | ||
121 | |||
122 | static void insert_to_result(struct lock_stat *st, | ||
123 | int (*bigger)(struct lock_stat *, struct lock_stat *)) | ||
124 | { | ||
125 | struct rb_node **rb = &result.rb_node; | ||
126 | struct rb_node *parent = NULL; | ||
127 | struct lock_stat *p; | ||
128 | |||
129 | while (*rb) { | ||
130 | p = container_of(*rb, struct lock_stat, rb); | ||
131 | parent = *rb; | ||
132 | |||
133 | if (bigger(st, p)) | ||
134 | rb = &(*rb)->rb_left; | ||
135 | else | ||
136 | rb = &(*rb)->rb_right; | ||
137 | } | ||
138 | |||
139 | rb_link_node(&st->rb, parent, rb); | ||
140 | rb_insert_color(&st->rb, &result); | ||
141 | } | ||
142 | |||
143 | /* returns left most element of result, and erase it */ | ||
144 | static struct lock_stat *pop_from_result(void) | ||
145 | { | ||
146 | struct rb_node *node = result.rb_node; | ||
147 | |||
148 | if (!node) | ||
149 | return NULL; | ||
150 | |||
151 | while (node->rb_left) | ||
152 | node = node->rb_left; | ||
153 | |||
154 | rb_erase(node, &result); | ||
155 | return container_of(node, struct lock_stat, rb); | ||
156 | } | ||
157 | |||
158 | static struct lock_stat *lock_stat_findnew(void *addr, const char *name) | ||
159 | { | ||
160 | struct list_head *entry = lockhashentry(addr); | ||
161 | struct lock_stat *ret, *new; | ||
162 | |||
163 | list_for_each_entry(ret, entry, hash_entry) { | ||
164 | if (ret->addr == addr) | ||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | new = zalloc(sizeof(struct lock_stat)); | ||
169 | if (!new) | ||
170 | goto alloc_failed; | ||
171 | |||
172 | new->addr = addr; | ||
173 | new->name = zalloc(sizeof(char) * strlen(name) + 1); | ||
174 | if (!new->name) | ||
175 | goto alloc_failed; | ||
176 | strcpy(new->name, name); | ||
177 | |||
178 | /* LOCK_STATE_UNLOCKED == 0 isn't guaranteed forever */ | ||
179 | new->state = LOCK_STATE_UNLOCKED; | ||
180 | new->wait_time_min = ULLONG_MAX; | ||
181 | |||
182 | list_add(&new->hash_entry, entry); | ||
183 | return new; | ||
184 | |||
185 | alloc_failed: | ||
186 | die("memory allocation failed\n"); | ||
187 | } | ||
188 | |||
189 | static char const *input_name = "perf.data"; | ||
190 | |||
191 | static int profile_cpu = -1; | ||
192 | |||
193 | struct raw_event_sample { | ||
194 | u32 size; | ||
195 | char data[0]; | ||
196 | }; | ||
197 | |||
198 | struct trace_acquire_event { | ||
199 | void *addr; | ||
200 | const char *name; | ||
201 | }; | ||
202 | |||
203 | struct trace_acquired_event { | ||
204 | void *addr; | ||
205 | const char *name; | ||
206 | }; | ||
207 | |||
208 | struct trace_contended_event { | ||
209 | void *addr; | ||
210 | const char *name; | ||
211 | }; | ||
212 | |||
213 | struct trace_release_event { | ||
214 | void *addr; | ||
215 | const char *name; | ||
216 | }; | ||
217 | |||
218 | struct trace_lock_handler { | ||
219 | void (*acquire_event)(struct trace_acquire_event *, | ||
220 | struct event *, | ||
221 | int cpu, | ||
222 | u64 timestamp, | ||
223 | struct thread *thread); | ||
224 | |||
225 | void (*acquired_event)(struct trace_acquired_event *, | ||
226 | struct event *, | ||
227 | int cpu, | ||
228 | u64 timestamp, | ||
229 | struct thread *thread); | ||
230 | |||
231 | void (*contended_event)(struct trace_contended_event *, | ||
232 | struct event *, | ||
233 | int cpu, | ||
234 | u64 timestamp, | ||
235 | struct thread *thread); | ||
236 | |||
237 | void (*release_event)(struct trace_release_event *, | ||
238 | struct event *, | ||
239 | int cpu, | ||
240 | u64 timestamp, | ||
241 | struct thread *thread); | ||
242 | }; | ||
243 | |||
244 | static void | ||
245 | report_lock_acquire_event(struct trace_acquire_event *acquire_event, | ||
246 | struct event *__event __used, | ||
247 | int cpu __used, | ||
248 | u64 timestamp, | ||
249 | struct thread *thread __used) | ||
250 | { | ||
251 | struct lock_stat *st; | ||
252 | |||
253 | st = lock_stat_findnew(acquire_event->addr, acquire_event->name); | ||
254 | |||
255 | switch (st->state) { | ||
256 | case LOCK_STATE_UNLOCKED: | ||
257 | break; | ||
258 | case LOCK_STATE_LOCKED: | ||
259 | break; | ||
260 | default: | ||
261 | BUG_ON(1); | ||
262 | break; | ||
263 | } | ||
264 | |||
265 | st->prev_event_time = timestamp; | ||
266 | } | ||
267 | |||
268 | static void | ||
269 | report_lock_acquired_event(struct trace_acquired_event *acquired_event, | ||
270 | struct event *__event __used, | ||
271 | int cpu __used, | ||
272 | u64 timestamp, | ||
273 | struct thread *thread __used) | ||
274 | { | ||
275 | struct lock_stat *st; | ||
276 | |||
277 | st = lock_stat_findnew(acquired_event->addr, acquired_event->name); | ||
278 | |||
279 | switch (st->state) { | ||
280 | case LOCK_STATE_UNLOCKED: | ||
281 | st->state = LOCK_STATE_LOCKED; | ||
282 | st->nr_acquired++; | ||
283 | break; | ||
284 | case LOCK_STATE_LOCKED: | ||
285 | break; | ||
286 | default: | ||
287 | BUG_ON(1); | ||
288 | break; | ||
289 | } | ||
290 | |||
291 | st->prev_event_time = timestamp; | ||
292 | } | ||
293 | |||
294 | static void | ||
295 | report_lock_contended_event(struct trace_contended_event *contended_event, | ||
296 | struct event *__event __used, | ||
297 | int cpu __used, | ||
298 | u64 timestamp, | ||
299 | struct thread *thread __used) | ||
300 | { | ||
301 | struct lock_stat *st; | ||
302 | |||
303 | st = lock_stat_findnew(contended_event->addr, contended_event->name); | ||
304 | |||
305 | switch (st->state) { | ||
306 | case LOCK_STATE_UNLOCKED: | ||
307 | break; | ||
308 | case LOCK_STATE_LOCKED: | ||
309 | st->nr_contended++; | ||
310 | break; | ||
311 | default: | ||
312 | BUG_ON(1); | ||
313 | break; | ||
314 | } | ||
315 | |||
316 | st->prev_event_time = timestamp; | ||
317 | } | ||
318 | |||
319 | static void | ||
320 | report_lock_release_event(struct trace_release_event *release_event, | ||
321 | struct event *__event __used, | ||
322 | int cpu __used, | ||
323 | u64 timestamp, | ||
324 | struct thread *thread __used) | ||
325 | { | ||
326 | struct lock_stat *st; | ||
327 | u64 hold_time; | ||
328 | |||
329 | st = lock_stat_findnew(release_event->addr, release_event->name); | ||
330 | |||
331 | switch (st->state) { | ||
332 | case LOCK_STATE_UNLOCKED: | ||
333 | break; | ||
334 | case LOCK_STATE_LOCKED: | ||
335 | st->state = LOCK_STATE_UNLOCKED; | ||
336 | hold_time = timestamp - st->prev_event_time; | ||
337 | |||
338 | if (timestamp < st->prev_event_time) { | ||
339 | /* terribly, this can happen... */ | ||
340 | goto end; | ||
341 | } | ||
342 | |||
343 | if (st->wait_time_min > hold_time) | ||
344 | st->wait_time_min = hold_time; | ||
345 | if (st->wait_time_max < hold_time) | ||
346 | st->wait_time_max = hold_time; | ||
347 | st->wait_time_total += hold_time; | ||
348 | |||
349 | st->nr_release++; | ||
350 | break; | ||
351 | default: | ||
352 | BUG_ON(1); | ||
353 | break; | ||
354 | } | ||
355 | |||
356 | end: | ||
357 | st->prev_event_time = timestamp; | ||
358 | } | ||
359 | |||
360 | /* lock oriented handlers */ | ||
361 | /* TODO: handlers for CPU oriented, thread oriented */ | ||
362 | static struct trace_lock_handler report_lock_ops = { | ||
363 | .acquire_event = report_lock_acquire_event, | ||
364 | .acquired_event = report_lock_acquired_event, | ||
365 | .contended_event = report_lock_contended_event, | ||
366 | .release_event = report_lock_release_event, | ||
367 | }; | ||
368 | |||
369 | static struct trace_lock_handler *trace_handler; | ||
370 | |||
371 | static void | ||
372 | process_lock_acquire_event(void *data, | ||
373 | struct event *event __used, | ||
374 | int cpu __used, | ||
375 | u64 timestamp __used, | ||
376 | struct thread *thread __used) | ||
377 | { | ||
378 | struct trace_acquire_event acquire_event; | ||
379 | u64 tmp; /* this is required for casting... */ | ||
380 | |||
381 | tmp = raw_field_value(event, "lockdep_addr", data); | ||
382 | memcpy(&acquire_event.addr, &tmp, sizeof(void *)); | ||
383 | acquire_event.name = (char *)raw_field_ptr(event, "name", data); | ||
384 | |||
385 | if (trace_handler->acquire_event) | ||
386 | trace_handler->acquire_event(&acquire_event, event, cpu, timestamp, thread); | ||
387 | } | ||
388 | |||
389 | static void | ||
390 | process_lock_acquired_event(void *data, | ||
391 | struct event *event __used, | ||
392 | int cpu __used, | ||
393 | u64 timestamp __used, | ||
394 | struct thread *thread __used) | ||
395 | { | ||
396 | struct trace_acquired_event acquired_event; | ||
397 | u64 tmp; /* this is required for casting... */ | ||
398 | |||
399 | tmp = raw_field_value(event, "lockdep_addr", data); | ||
400 | memcpy(&acquired_event.addr, &tmp, sizeof(void *)); | ||
401 | acquired_event.name = (char *)raw_field_ptr(event, "name", data); | ||
402 | |||
403 | if (trace_handler->acquire_event) | ||
404 | trace_handler->acquired_event(&acquired_event, event, cpu, timestamp, thread); | ||
405 | } | ||
406 | |||
407 | static void | ||
408 | process_lock_contended_event(void *data, | ||
409 | struct event *event __used, | ||
410 | int cpu __used, | ||
411 | u64 timestamp __used, | ||
412 | struct thread *thread __used) | ||
413 | { | ||
414 | struct trace_contended_event contended_event; | ||
415 | u64 tmp; /* this is required for casting... */ | ||
416 | |||
417 | tmp = raw_field_value(event, "lockdep_addr", data); | ||
418 | memcpy(&contended_event.addr, &tmp, sizeof(void *)); | ||
419 | contended_event.name = (char *)raw_field_ptr(event, "name", data); | ||
420 | |||
421 | if (trace_handler->acquire_event) | ||
422 | trace_handler->contended_event(&contended_event, event, cpu, timestamp, thread); | ||
423 | } | ||
424 | |||
425 | static void | ||
426 | process_lock_release_event(void *data, | ||
427 | struct event *event __used, | ||
428 | int cpu __used, | ||
429 | u64 timestamp __used, | ||
430 | struct thread *thread __used) | ||
431 | { | ||
432 | struct trace_release_event release_event; | ||
433 | u64 tmp; /* this is required for casting... */ | ||
434 | |||
435 | tmp = raw_field_value(event, "lockdep_addr", data); | ||
436 | memcpy(&release_event.addr, &tmp, sizeof(void *)); | ||
437 | release_event.name = (char *)raw_field_ptr(event, "name", data); | ||
438 | |||
439 | if (trace_handler->acquire_event) | ||
440 | trace_handler->release_event(&release_event, event, cpu, timestamp, thread); | ||
441 | } | ||
442 | |||
443 | static void | ||
444 | process_raw_event(void *data, int cpu, | ||
445 | u64 timestamp, struct thread *thread) | ||
446 | { | ||
447 | struct event *event; | ||
448 | int type; | ||
449 | |||
450 | type = trace_parse_common_type(data); | ||
451 | event = trace_find_event(type); | ||
452 | |||
453 | if (!strcmp(event->name, "lock_acquire")) | ||
454 | process_lock_acquire_event(data, event, cpu, timestamp, thread); | ||
455 | if (!strcmp(event->name, "lock_acquired")) | ||
456 | process_lock_acquired_event(data, event, cpu, timestamp, thread); | ||
457 | if (!strcmp(event->name, "lock_contended")) | ||
458 | process_lock_contended_event(data, event, cpu, timestamp, thread); | ||
459 | if (!strcmp(event->name, "lock_release")) | ||
460 | process_lock_release_event(data, event, cpu, timestamp, thread); | ||
461 | } | ||
462 | |||
463 | struct raw_event_queue { | ||
464 | u64 timestamp; | ||
465 | int cpu; | ||
466 | void *data; | ||
467 | struct thread *thread; | ||
468 | struct list_head list; | ||
469 | }; | ||
470 | |||
471 | static LIST_HEAD(raw_event_head); | ||
472 | |||
473 | #define FLUSH_PERIOD (5 * NSEC_PER_SEC) | ||
474 | |||
475 | static u64 flush_limit = ULLONG_MAX; | ||
476 | static u64 last_flush = 0; | ||
477 | struct raw_event_queue *last_inserted; | ||
478 | |||
479 | static void flush_raw_event_queue(u64 limit) | ||
480 | { | ||
481 | struct raw_event_queue *tmp, *iter; | ||
482 | |||
483 | list_for_each_entry_safe(iter, tmp, &raw_event_head, list) { | ||
484 | if (iter->timestamp > limit) | ||
485 | return; | ||
486 | |||
487 | if (iter == last_inserted) | ||
488 | last_inserted = NULL; | ||
489 | |||
490 | process_raw_event(iter->data, iter->cpu, iter->timestamp, | ||
491 | iter->thread); | ||
492 | |||
493 | last_flush = iter->timestamp; | ||
494 | list_del(&iter->list); | ||
495 | free(iter->data); | ||
496 | free(iter); | ||
497 | } | ||
498 | } | ||
499 | |||
500 | static void __queue_raw_event_end(struct raw_event_queue *new) | ||
501 | { | ||
502 | struct raw_event_queue *iter; | ||
503 | |||
504 | list_for_each_entry_reverse(iter, &raw_event_head, list) { | ||
505 | if (iter->timestamp < new->timestamp) { | ||
506 | list_add(&new->list, &iter->list); | ||
507 | return; | ||
508 | } | ||
509 | } | ||
510 | |||
511 | list_add(&new->list, &raw_event_head); | ||
512 | } | ||
513 | |||
514 | static void __queue_raw_event_before(struct raw_event_queue *new, | ||
515 | struct raw_event_queue *iter) | ||
516 | { | ||
517 | list_for_each_entry_continue_reverse(iter, &raw_event_head, list) { | ||
518 | if (iter->timestamp < new->timestamp) { | ||
519 | list_add(&new->list, &iter->list); | ||
520 | return; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | list_add(&new->list, &raw_event_head); | ||
525 | } | ||
526 | |||
527 | static void __queue_raw_event_after(struct raw_event_queue *new, | ||
528 | struct raw_event_queue *iter) | ||
529 | { | ||
530 | list_for_each_entry_continue(iter, &raw_event_head, list) { | ||
531 | if (iter->timestamp > new->timestamp) { | ||
532 | list_add_tail(&new->list, &iter->list); | ||
533 | return; | ||
534 | } | ||
535 | } | ||
536 | list_add_tail(&new->list, &raw_event_head); | ||
537 | } | ||
538 | |||
539 | /* The queue is ordered by time */ | ||
540 | static void __queue_raw_event(struct raw_event_queue *new) | ||
541 | { | ||
542 | if (!last_inserted) { | ||
543 | __queue_raw_event_end(new); | ||
544 | return; | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | * Most of the time the current event has a timestamp | ||
549 | * very close to the last event inserted, unless we just switched | ||
550 | * to another event buffer. Having a sorting based on a list and | ||
551 | * on the last inserted event that is close to the current one is | ||
552 | * probably more efficient than an rbtree based sorting. | ||
553 | */ | ||
554 | if (last_inserted->timestamp >= new->timestamp) | ||
555 | __queue_raw_event_before(new, last_inserted); | ||
556 | else | ||
557 | __queue_raw_event_after(new, last_inserted); | ||
558 | } | ||
559 | |||
560 | static void queue_raw_event(void *data, int raw_size, int cpu, | ||
561 | u64 timestamp, struct thread *thread) | ||
562 | { | ||
563 | struct raw_event_queue *new; | ||
564 | |||
565 | if (flush_limit == ULLONG_MAX) | ||
566 | flush_limit = timestamp + FLUSH_PERIOD; | ||
567 | |||
568 | if (timestamp < last_flush) { | ||
569 | printf("Warning: Timestamp below last timeslice flush\n"); | ||
570 | return; | ||
571 | } | ||
572 | |||
573 | new = malloc(sizeof(*new)); | ||
574 | if (!new) | ||
575 | die("Not enough memory\n"); | ||
576 | |||
577 | new->timestamp = timestamp; | ||
578 | new->cpu = cpu; | ||
579 | new->thread = thread; | ||
580 | |||
581 | new->data = malloc(raw_size); | ||
582 | if (!new->data) | ||
583 | die("Not enough memory\n"); | ||
584 | |||
585 | memcpy(new->data, data, raw_size); | ||
586 | |||
587 | __queue_raw_event(new); | ||
588 | last_inserted = new; | ||
589 | |||
590 | /* | ||
591 | * We want to have a slice of events covering 2 * FLUSH_PERIOD | ||
592 | * If FLUSH_PERIOD is big enough, it ensures every events that occured | ||
593 | * in the first half of the timeslice have all been buffered and there | ||
594 | * are none remaining (we need that because of the weakly ordered | ||
595 | * event recording we have). Then once we reach the 2 * FLUSH_PERIOD | ||
596 | * timeslice, we flush the first half to be gentle with the memory | ||
597 | * (the second half can still get new events in the middle, so wait | ||
598 | * another period to flush it) | ||
599 | */ | ||
600 | if (new->timestamp > flush_limit && | ||
601 | new->timestamp - flush_limit > FLUSH_PERIOD) { | ||
602 | flush_limit += FLUSH_PERIOD; | ||
603 | flush_raw_event_queue(flush_limit); | ||
604 | } | ||
605 | } | ||
606 | |||
607 | static int process_sample_event(event_t *event, struct perf_session *session) | ||
608 | { | ||
609 | struct thread *thread; | ||
610 | struct sample_data data; | ||
611 | |||
612 | bzero(&data, sizeof(struct sample_data)); | ||
613 | event__parse_sample(event, session->sample_type, &data); | ||
614 | thread = perf_session__findnew(session, data.pid); | ||
615 | |||
616 | if (thread == NULL) { | ||
617 | pr_debug("problem processing %d event, skipping it.\n", | ||
618 | event->header.type); | ||
619 | return -1; | ||
620 | } | ||
621 | |||
622 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | ||
623 | |||
624 | if (profile_cpu != -1 && profile_cpu != (int) data.cpu) | ||
625 | return 0; | ||
626 | |||
627 | queue_raw_event(data.raw_data, data.raw_size, data.cpu, data.time, thread); | ||
628 | |||
629 | return 0; | ||
630 | } | ||
631 | |||
632 | /* TODO: various way to print, coloring, nano or milli sec */ | ||
633 | static void print_result(void) | ||
634 | { | ||
635 | struct lock_stat *st; | ||
636 | char cut_name[20]; | ||
637 | |||
638 | printf("%18s ", "ID"); | ||
639 | printf("%20s ", "Name"); | ||
640 | printf("%10s ", "acquired"); | ||
641 | printf("%10s ", "contended"); | ||
642 | |||
643 | printf("%15s ", "total wait (ns)"); | ||
644 | printf("%15s ", "max wait (ns)"); | ||
645 | printf("%15s ", "min wait (ns)"); | ||
646 | |||
647 | printf("\n\n"); | ||
648 | |||
649 | while ((st = pop_from_result())) { | ||
650 | bzero(cut_name, 20); | ||
651 | |||
652 | printf("%p ", st->addr); | ||
653 | |||
654 | if (strlen(st->name) < 16) { | ||
655 | /* output raw name */ | ||
656 | printf("%20s ", st->name); | ||
657 | } else { | ||
658 | strncpy(cut_name, st->name, 16); | ||
659 | cut_name[16] = '.'; | ||
660 | cut_name[17] = '.'; | ||
661 | cut_name[18] = '.'; | ||
662 | cut_name[19] = '\0'; | ||
663 | /* cut off name for saving output style */ | ||
664 | printf("%20s ", cut_name); | ||
665 | } | ||
666 | |||
667 | printf("%10u ", st->nr_acquired); | ||
668 | printf("%10u ", st->nr_contended); | ||
669 | |||
670 | printf("%15llu ", st->wait_time_total); | ||
671 | printf("%15llu ", st->wait_time_max); | ||
672 | printf("%15llu ", st->wait_time_min == ULLONG_MAX ? | ||
673 | 0 : st->wait_time_min); | ||
674 | printf("\n"); | ||
675 | } | ||
676 | } | ||
677 | |||
678 | static void dump_map(void) | ||
679 | { | ||
680 | unsigned int i; | ||
681 | struct lock_stat *st; | ||
682 | |||
683 | for (i = 0; i < LOCKHASH_SIZE; i++) { | ||
684 | list_for_each_entry(st, &lockhash_table[i], hash_entry) { | ||
685 | printf("%p: %s\n", st->addr, st->name); | ||
686 | } | ||
687 | } | ||
688 | } | ||
689 | |||
690 | static struct perf_event_ops eops = { | ||
691 | .sample = process_sample_event, | ||
692 | .comm = event__process_comm, | ||
693 | }; | ||
694 | |||
695 | static struct perf_session *session; | ||
696 | |||
697 | static int read_events(void) | ||
698 | { | ||
699 | session = perf_session__new(input_name, O_RDONLY, 0); | ||
700 | if (!session) | ||
701 | die("Initializing perf session failed\n"); | ||
702 | |||
703 | return perf_session__process_events(session, &eops); | ||
704 | } | ||
705 | |||
706 | static void sort_result(void) | ||
707 | { | ||
708 | unsigned int i; | ||
709 | struct lock_stat *st; | ||
710 | |||
711 | for (i = 0; i < LOCKHASH_SIZE; i++) { | ||
712 | list_for_each_entry(st, &lockhash_table[i], hash_entry) { | ||
713 | insert_to_result(st, compare); | ||
714 | } | ||
715 | } | ||
716 | } | ||
717 | |||
718 | static void __cmd_report(void) | ||
719 | { | ||
720 | setup_pager(); | ||
721 | select_key(); | ||
722 | read_events(); | ||
723 | flush_raw_event_queue(ULLONG_MAX); | ||
724 | sort_result(); | ||
725 | print_result(); | ||
726 | } | ||
727 | |||
728 | static const char * const report_usage[] = { | ||
729 | "perf lock report [<options>]", | ||
730 | NULL | ||
731 | }; | ||
732 | |||
733 | static const struct option report_options[] = { | ||
734 | OPT_STRING('k', "key", &sort_key, "acquired", | ||
735 | "key for sorting"), | ||
736 | /* TODO: type */ | ||
737 | OPT_END() | ||
738 | }; | ||
739 | |||
740 | static const char * const lock_usage[] = { | ||
741 | "perf lock [<options>] {record|trace|report}", | ||
742 | NULL | ||
743 | }; | ||
744 | |||
745 | static const struct option lock_options[] = { | ||
746 | OPT_STRING('i', "input", &input_name, "file", "input file name"), | ||
747 | OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), | ||
748 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), | ||
749 | OPT_END() | ||
750 | }; | ||
751 | |||
752 | static const char *record_args[] = { | ||
753 | "record", | ||
754 | "-a", | ||
755 | "-R", | ||
756 | "-f", | ||
757 | "-m", "1024", | ||
758 | "-c", "1", | ||
759 | "-e", "lock:lock_acquire:r", | ||
760 | "-e", "lock:lock_acquired:r", | ||
761 | "-e", "lock:lock_contended:r", | ||
762 | "-e", "lock:lock_release:r", | ||
763 | }; | ||
764 | |||
765 | static int __cmd_record(int argc, const char **argv) | ||
766 | { | ||
767 | unsigned int rec_argc, i, j; | ||
768 | const char **rec_argv; | ||
769 | |||
770 | rec_argc = ARRAY_SIZE(record_args) + argc - 1; | ||
771 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
772 | |||
773 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | ||
774 | rec_argv[i] = strdup(record_args[i]); | ||
775 | |||
776 | for (j = 1; j < (unsigned int)argc; j++, i++) | ||
777 | rec_argv[i] = argv[j]; | ||
778 | |||
779 | BUG_ON(i != rec_argc); | ||
780 | |||
781 | return cmd_record(i, rec_argv, NULL); | ||
782 | } | ||
783 | |||
784 | int cmd_lock(int argc, const char **argv, const char *prefix __used) | ||
785 | { | ||
786 | unsigned int i; | ||
787 | |||
788 | symbol__init(); | ||
789 | for (i = 0; i < LOCKHASH_SIZE; i++) | ||
790 | INIT_LIST_HEAD(lockhash_table + i); | ||
791 | |||
792 | argc = parse_options(argc, argv, lock_options, lock_usage, | ||
793 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
794 | if (!argc) | ||
795 | usage_with_options(lock_usage, lock_options); | ||
796 | |||
797 | if (!strncmp(argv[0], "rec", 3)) { | ||
798 | return __cmd_record(argc, argv); | ||
799 | } else if (!strncmp(argv[0], "report", 6)) { | ||
800 | trace_handler = &report_lock_ops; | ||
801 | if (argc) { | ||
802 | argc = parse_options(argc, argv, | ||
803 | report_options, report_usage, 0); | ||
804 | if (argc) | ||
805 | usage_with_options(report_usage, report_options); | ||
806 | } | ||
807 | __cmd_report(); | ||
808 | } else if (!strcmp(argv[0], "trace")) { | ||
809 | /* Aliased to 'perf trace' */ | ||
810 | return cmd_trace(argc, argv, prefix); | ||
811 | } else if (!strcmp(argv[0], "map")) { | ||
812 | /* recycling report_lock_ops */ | ||
813 | trace_handler = &report_lock_ops; | ||
814 | setup_pager(); | ||
815 | read_events(); | ||
816 | dump_map(); | ||
817 | } else { | ||
818 | usage_with_options(lock_usage, lock_options); | ||
819 | } | ||
820 | |||
821 | return 0; | ||
822 | } | ||
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c new file mode 100644 index 000000000000..c30a33592340 --- /dev/null +++ b/tools/perf/builtin-probe.c | |||
@@ -0,0 +1,361 @@ | |||
1 | /* | ||
2 | * builtin-probe.c | ||
3 | * | ||
4 | * Builtin probe command: Set up probe events by C expression | ||
5 | * | ||
6 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
21 | * | ||
22 | */ | ||
23 | #define _GNU_SOURCE | ||
24 | #include <sys/utsname.h> | ||
25 | #include <sys/types.h> | ||
26 | #include <sys/stat.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <errno.h> | ||
29 | #include <stdio.h> | ||
30 | #include <unistd.h> | ||
31 | #include <stdlib.h> | ||
32 | #include <string.h> | ||
33 | |||
34 | #undef _GNU_SOURCE | ||
35 | #include "perf.h" | ||
36 | #include "builtin.h" | ||
37 | #include "util/util.h" | ||
38 | #include "util/strlist.h" | ||
39 | #include "util/event.h" | ||
40 | #include "util/debug.h" | ||
41 | #include "util/debugfs.h" | ||
42 | #include "util/symbol.h" | ||
43 | #include "util/thread.h" | ||
44 | #include "util/parse-options.h" | ||
45 | #include "util/parse-events.h" /* For debugfs_path */ | ||
46 | #include "util/probe-finder.h" | ||
47 | #include "util/probe-event.h" | ||
48 | |||
49 | #define MAX_PATH_LEN 256 | ||
50 | #define MAX_PROBES 128 | ||
51 | |||
52 | /* Session management structure */ | ||
53 | static struct { | ||
54 | bool need_dwarf; | ||
55 | bool list_events; | ||
56 | bool force_add; | ||
57 | bool show_lines; | ||
58 | int nr_probe; | ||
59 | struct probe_point probes[MAX_PROBES]; | ||
60 | struct strlist *dellist; | ||
61 | struct map_groups kmap_groups; | ||
62 | struct map *kmaps[MAP__NR_TYPES]; | ||
63 | struct line_range line_range; | ||
64 | } session; | ||
65 | |||
66 | |||
67 | /* Parse an event definition. Note that any error must die. */ | ||
68 | static void parse_probe_event(const char *str) | ||
69 | { | ||
70 | struct probe_point *pp = &session.probes[session.nr_probe]; | ||
71 | |||
72 | pr_debug("probe-definition(%d): %s\n", session.nr_probe, str); | ||
73 | if (++session.nr_probe == MAX_PROBES) | ||
74 | die("Too many probes (> %d) are specified.", MAX_PROBES); | ||
75 | |||
76 | /* Parse perf-probe event into probe_point */ | ||
77 | parse_perf_probe_event(str, pp, &session.need_dwarf); | ||
78 | |||
79 | pr_debug("%d arguments\n", pp->nr_args); | ||
80 | } | ||
81 | |||
82 | static void parse_probe_event_argv(int argc, const char **argv) | ||
83 | { | ||
84 | int i, len; | ||
85 | char *buf; | ||
86 | |||
87 | /* Bind up rest arguments */ | ||
88 | len = 0; | ||
89 | for (i = 0; i < argc; i++) | ||
90 | len += strlen(argv[i]) + 1; | ||
91 | buf = zalloc(len + 1); | ||
92 | if (!buf) | ||
93 | die("Failed to allocate memory for binding arguments."); | ||
94 | len = 0; | ||
95 | for (i = 0; i < argc; i++) | ||
96 | len += sprintf(&buf[len], "%s ", argv[i]); | ||
97 | parse_probe_event(buf); | ||
98 | free(buf); | ||
99 | } | ||
100 | |||
101 | static int opt_add_probe_event(const struct option *opt __used, | ||
102 | const char *str, int unset __used) | ||
103 | { | ||
104 | if (str) | ||
105 | parse_probe_event(str); | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int opt_del_probe_event(const struct option *opt __used, | ||
110 | const char *str, int unset __used) | ||
111 | { | ||
112 | if (str) { | ||
113 | if (!session.dellist) | ||
114 | session.dellist = strlist__new(true, NULL); | ||
115 | strlist__add(session.dellist, str); | ||
116 | } | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | /* Currently just checking function name from symbol map */ | ||
121 | static void evaluate_probe_point(struct probe_point *pp) | ||
122 | { | ||
123 | struct symbol *sym; | ||
124 | sym = map__find_symbol_by_name(session.kmaps[MAP__FUNCTION], | ||
125 | pp->function, NULL); | ||
126 | if (!sym) | ||
127 | die("Kernel symbol \'%s\' not found - probe not added.", | ||
128 | pp->function); | ||
129 | } | ||
130 | |||
131 | #ifndef NO_DWARF_SUPPORT | ||
132 | static int open_vmlinux(void) | ||
133 | { | ||
134 | if (map__load(session.kmaps[MAP__FUNCTION], NULL) < 0) { | ||
135 | pr_debug("Failed to load kernel map.\n"); | ||
136 | return -EINVAL; | ||
137 | } | ||
138 | pr_debug("Try to open %s\n", | ||
139 | session.kmaps[MAP__FUNCTION]->dso->long_name); | ||
140 | return open(session.kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); | ||
141 | } | ||
142 | |||
143 | static int opt_show_lines(const struct option *opt __used, | ||
144 | const char *str, int unset __used) | ||
145 | { | ||
146 | if (str) | ||
147 | parse_line_range_desc(str, &session.line_range); | ||
148 | INIT_LIST_HEAD(&session.line_range.line_list); | ||
149 | session.show_lines = true; | ||
150 | return 0; | ||
151 | } | ||
152 | #endif | ||
153 | |||
154 | static const char * const probe_usage[] = { | ||
155 | "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", | ||
156 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", | ||
157 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", | ||
158 | "perf probe --list", | ||
159 | #ifndef NO_DWARF_SUPPORT | ||
160 | "perf probe --line 'LINEDESC'", | ||
161 | #endif | ||
162 | NULL | ||
163 | }; | ||
164 | |||
165 | static const struct option options[] = { | ||
166 | OPT_BOOLEAN('v', "verbose", &verbose, | ||
167 | "be more verbose (show parsed arguments, etc)"), | ||
168 | #ifndef NO_DWARF_SUPPORT | ||
169 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | ||
170 | "file", "vmlinux pathname"), | ||
171 | #endif | ||
172 | OPT_BOOLEAN('l', "list", &session.list_events, | ||
173 | "list up current probe events"), | ||
174 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", | ||
175 | opt_del_probe_event), | ||
176 | OPT_CALLBACK('a', "add", NULL, | ||
177 | #ifdef NO_DWARF_SUPPORT | ||
178 | "[EVENT=]FUNC[+OFF|%return] [ARG ...]", | ||
179 | #else | ||
180 | "[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT" | ||
181 | " [ARG ...]", | ||
182 | #endif | ||
183 | "probe point definition, where\n" | ||
184 | "\t\tGROUP:\tGroup name (optional)\n" | ||
185 | "\t\tEVENT:\tEvent name\n" | ||
186 | "\t\tFUNC:\tFunction name\n" | ||
187 | "\t\tOFF:\tOffset from function entry (in byte)\n" | ||
188 | "\t\t%return:\tPut the probe at function return\n" | ||
189 | #ifdef NO_DWARF_SUPPORT | ||
190 | "\t\tARG:\tProbe argument (only \n" | ||
191 | #else | ||
192 | "\t\tSRC:\tSource code path\n" | ||
193 | "\t\tRL:\tRelative line number from function entry.\n" | ||
194 | "\t\tAL:\tAbsolute line number in file.\n" | ||
195 | "\t\tPT:\tLazy expression of line code.\n" | ||
196 | "\t\tARG:\tProbe argument (local variable name or\n" | ||
197 | #endif | ||
198 | "\t\t\tkprobe-tracer argument format.)\n", | ||
199 | opt_add_probe_event), | ||
200 | OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" | ||
201 | " with existing name"), | ||
202 | #ifndef NO_DWARF_SUPPORT | ||
203 | OPT_CALLBACK('L', "line", NULL, | ||
204 | "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]", | ||
205 | "Show source code lines.", opt_show_lines), | ||
206 | #endif | ||
207 | OPT_END() | ||
208 | }; | ||
209 | |||
210 | /* Initialize symbol maps for vmlinux */ | ||
211 | static void init_vmlinux(void) | ||
212 | { | ||
213 | symbol_conf.sort_by_name = true; | ||
214 | if (symbol_conf.vmlinux_name == NULL) | ||
215 | symbol_conf.try_vmlinux_path = true; | ||
216 | else | ||
217 | pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); | ||
218 | if (symbol__init() < 0) | ||
219 | die("Failed to init symbol map."); | ||
220 | |||
221 | map_groups__init(&session.kmap_groups); | ||
222 | if (map_groups__create_kernel_maps(&session.kmap_groups, | ||
223 | session.kmaps) < 0) | ||
224 | die("Failed to create kernel maps."); | ||
225 | } | ||
226 | |||
227 | int cmd_probe(int argc, const char **argv, const char *prefix __used) | ||
228 | { | ||
229 | int i, ret; | ||
230 | #ifndef NO_DWARF_SUPPORT | ||
231 | int fd; | ||
232 | #endif | ||
233 | struct probe_point *pp; | ||
234 | |||
235 | argc = parse_options(argc, argv, options, probe_usage, | ||
236 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
237 | if (argc > 0) { | ||
238 | if (strcmp(argv[0], "-") == 0) { | ||
239 | pr_warning(" Error: '-' is not supported.\n"); | ||
240 | usage_with_options(probe_usage, options); | ||
241 | } | ||
242 | parse_probe_event_argv(argc, argv); | ||
243 | } | ||
244 | |||
245 | if ((!session.nr_probe && !session.dellist && !session.list_events && | ||
246 | !session.show_lines)) | ||
247 | usage_with_options(probe_usage, options); | ||
248 | |||
249 | if (debugfs_valid_mountpoint(debugfs_path) < 0) | ||
250 | die("Failed to find debugfs path."); | ||
251 | |||
252 | if (session.list_events) { | ||
253 | if (session.nr_probe != 0 || session.dellist) { | ||
254 | pr_warning(" Error: Don't use --list with" | ||
255 | " --add/--del.\n"); | ||
256 | usage_with_options(probe_usage, options); | ||
257 | } | ||
258 | if (session.show_lines) { | ||
259 | pr_warning(" Error: Don't use --list with --line.\n"); | ||
260 | usage_with_options(probe_usage, options); | ||
261 | } | ||
262 | show_perf_probe_events(); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | #ifndef NO_DWARF_SUPPORT | ||
267 | if (session.show_lines) { | ||
268 | if (session.nr_probe != 0 || session.dellist) { | ||
269 | pr_warning(" Error: Don't use --line with" | ||
270 | " --add/--del.\n"); | ||
271 | usage_with_options(probe_usage, options); | ||
272 | } | ||
273 | init_vmlinux(); | ||
274 | fd = open_vmlinux(); | ||
275 | if (fd < 0) | ||
276 | die("Could not open debuginfo file."); | ||
277 | ret = find_line_range(fd, &session.line_range); | ||
278 | if (ret <= 0) | ||
279 | die("Source line is not found.\n"); | ||
280 | close(fd); | ||
281 | show_line_range(&session.line_range); | ||
282 | return 0; | ||
283 | } | ||
284 | #endif | ||
285 | |||
286 | if (session.dellist) { | ||
287 | del_trace_kprobe_events(session.dellist); | ||
288 | strlist__delete(session.dellist); | ||
289 | if (session.nr_probe == 0) | ||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | /* Add probes */ | ||
294 | init_vmlinux(); | ||
295 | |||
296 | if (session.need_dwarf) | ||
297 | #ifdef NO_DWARF_SUPPORT | ||
298 | die("Debuginfo-analysis is not supported"); | ||
299 | #else /* !NO_DWARF_SUPPORT */ | ||
300 | pr_debug("Some probes require debuginfo.\n"); | ||
301 | |||
302 | fd = open_vmlinux(); | ||
303 | if (fd < 0) { | ||
304 | if (session.need_dwarf) | ||
305 | die("Could not open debuginfo file."); | ||
306 | |||
307 | pr_debug("Could not open vmlinux/module file." | ||
308 | " Try to use symbols.\n"); | ||
309 | goto end_dwarf; | ||
310 | } | ||
311 | |||
312 | /* Searching probe points */ | ||
313 | for (i = 0; i < session.nr_probe; i++) { | ||
314 | pp = &session.probes[i]; | ||
315 | if (pp->found) | ||
316 | continue; | ||
317 | |||
318 | lseek(fd, SEEK_SET, 0); | ||
319 | ret = find_probe_point(fd, pp); | ||
320 | if (ret > 0) | ||
321 | continue; | ||
322 | if (ret == 0) { /* No error but failed to find probe point. */ | ||
323 | synthesize_perf_probe_point(pp); | ||
324 | die("Probe point '%s' not found. - probe not added.", | ||
325 | pp->probes[0]); | ||
326 | } | ||
327 | /* Error path */ | ||
328 | if (session.need_dwarf) { | ||
329 | if (ret == -ENOENT) | ||
330 | pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
331 | die("Could not analyze debuginfo."); | ||
332 | } | ||
333 | pr_debug("An error occurred in debuginfo analysis." | ||
334 | " Try to use symbols.\n"); | ||
335 | break; | ||
336 | } | ||
337 | close(fd); | ||
338 | |||
339 | end_dwarf: | ||
340 | #endif /* !NO_DWARF_SUPPORT */ | ||
341 | |||
342 | /* Synthesize probes without dwarf */ | ||
343 | for (i = 0; i < session.nr_probe; i++) { | ||
344 | pp = &session.probes[i]; | ||
345 | if (pp->found) /* This probe is already found. */ | ||
346 | continue; | ||
347 | |||
348 | evaluate_probe_point(pp); | ||
349 | ret = synthesize_trace_kprobe_event(pp); | ||
350 | if (ret == -E2BIG) | ||
351 | die("probe point definition becomes too long."); | ||
352 | else if (ret < 0) | ||
353 | die("Failed to synthesize a probe point."); | ||
354 | } | ||
355 | |||
356 | /* Settng up probe points */ | ||
357 | add_trace_kprobe_events(session.probes, session.nr_probe, | ||
358 | session.force_add); | ||
359 | return 0; | ||
360 | } | ||
361 | |||
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a4be453fc8a9..771533ced6a8 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -5,10 +5,13 @@ | |||
5 | * (or a CPU, or a PID) into the perf.data output file - for | 5 | * (or a CPU, or a PID) into the perf.data output file - for |
6 | * later analysis via perf report. | 6 | * later analysis via perf report. |
7 | */ | 7 | */ |
8 | #define _FILE_OFFSET_BITS 64 | ||
9 | |||
8 | #include "builtin.h" | 10 | #include "builtin.h" |
9 | 11 | ||
10 | #include "perf.h" | 12 | #include "perf.h" |
11 | 13 | ||
14 | #include "util/build-id.h" | ||
12 | #include "util/util.h" | 15 | #include "util/util.h" |
13 | #include "util/parse-options.h" | 16 | #include "util/parse-options.h" |
14 | #include "util/parse-events.h" | 17 | #include "util/parse-events.h" |
@@ -17,55 +20,54 @@ | |||
17 | #include "util/header.h" | 20 | #include "util/header.h" |
18 | #include "util/event.h" | 21 | #include "util/event.h" |
19 | #include "util/debug.h" | 22 | #include "util/debug.h" |
20 | #include "util/trace-event.h" | 23 | #include "util/session.h" |
24 | #include "util/symbol.h" | ||
21 | 25 | ||
22 | #include <unistd.h> | 26 | #include <unistd.h> |
23 | #include <sched.h> | 27 | #include <sched.h> |
24 | 28 | ||
25 | #define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) | ||
26 | #define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) | ||
27 | |||
28 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | 29 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; |
29 | 30 | ||
30 | static long default_interval = 100000; | 31 | static long default_interval = 0; |
31 | 32 | ||
32 | static int nr_cpus = 0; | 33 | static int nr_cpus = 0; |
33 | static unsigned int page_size; | 34 | static unsigned int page_size; |
34 | static unsigned int mmap_pages = 128; | 35 | static unsigned int mmap_pages = 128; |
35 | static int freq = 0; | 36 | static int freq = 1000; |
36 | static int output; | 37 | static int output; |
37 | static const char *output_name = "perf.data"; | 38 | static const char *output_name = "perf.data"; |
38 | static int group = 0; | 39 | static int group = 0; |
39 | static unsigned int realtime_prio = 0; | 40 | static unsigned int realtime_prio = 0; |
40 | static int raw_samples = 0; | 41 | static int raw_samples = 0; |
41 | static int system_wide = 0; | 42 | static int system_wide = 0; |
42 | static int profile_cpu = -1; | 43 | static int profile_cpu = -1; |
43 | static pid_t target_pid = -1; | 44 | static pid_t target_pid = -1; |
44 | static pid_t child_pid = -1; | 45 | static pid_t child_pid = -1; |
45 | static int inherit = 1; | 46 | static int inherit = 1; |
46 | static int force = 0; | 47 | static int force = 0; |
47 | static int append_file = 0; | 48 | static int append_file = 0; |
48 | static int call_graph = 0; | 49 | static int call_graph = 0; |
49 | static int inherit_stat = 0; | 50 | static int inherit_stat = 0; |
50 | static int no_samples = 0; | 51 | static int no_samples = 0; |
51 | static int sample_address = 0; | 52 | static int sample_address = 0; |
52 | static int multiplex = 0; | 53 | static int multiplex = 0; |
53 | static int multiplex_fd = -1; | 54 | static int multiplex_fd = -1; |
54 | 55 | ||
55 | static long samples; | 56 | static long samples = 0; |
56 | static struct timeval last_read; | 57 | static struct timeval last_read; |
57 | static struct timeval this_read; | 58 | static struct timeval this_read; |
58 | 59 | ||
59 | static u64 bytes_written; | 60 | static u64 bytes_written = 0; |
60 | 61 | ||
61 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; | 62 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; |
62 | 63 | ||
63 | static int nr_poll; | 64 | static int nr_poll = 0; |
64 | static int nr_cpu; | 65 | static int nr_cpu = 0; |
65 | 66 | ||
66 | static int file_new = 1; | 67 | static int file_new = 1; |
68 | static off_t post_processing_offset; | ||
67 | 69 | ||
68 | struct perf_header *header; | 70 | static struct perf_session *session; |
69 | 71 | ||
70 | struct mmap_data { | 72 | struct mmap_data { |
71 | int counter; | 73 | int counter; |
@@ -113,6 +115,13 @@ static void write_output(void *buf, size_t size) | |||
113 | } | 115 | } |
114 | } | 116 | } |
115 | 117 | ||
118 | static int process_synthesized_event(event_t *event, | ||
119 | struct perf_session *self __used) | ||
120 | { | ||
121 | write_output(event, event->header.size); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
116 | static void mmap_read(struct mmap_data *md) | 125 | static void mmap_read(struct mmap_data *md) |
117 | { | 126 | { |
118 | unsigned int head = mmap_read_head(md); | 127 | unsigned int head = mmap_read_head(md); |
@@ -195,179 +204,21 @@ static void sig_atexit(void) | |||
195 | kill(getpid(), signr); | 204 | kill(getpid(), signr); |
196 | } | 205 | } |
197 | 206 | ||
198 | static pid_t pid_synthesize_comm_event(pid_t pid, int full) | ||
199 | { | ||
200 | struct comm_event comm_ev; | ||
201 | char filename[PATH_MAX]; | ||
202 | char bf[BUFSIZ]; | ||
203 | FILE *fp; | ||
204 | size_t size = 0; | ||
205 | DIR *tasks; | ||
206 | struct dirent dirent, *next; | ||
207 | pid_t tgid = 0; | ||
208 | |||
209 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); | ||
210 | |||
211 | fp = fopen(filename, "r"); | ||
212 | if (fp == NULL) { | ||
213 | /* | ||
214 | * We raced with a task exiting - just return: | ||
215 | */ | ||
216 | if (verbose) | ||
217 | fprintf(stderr, "couldn't open %s\n", filename); | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | memset(&comm_ev, 0, sizeof(comm_ev)); | ||
222 | while (!comm_ev.comm[0] || !comm_ev.pid) { | ||
223 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
224 | goto out_failure; | ||
225 | |||
226 | if (memcmp(bf, "Name:", 5) == 0) { | ||
227 | char *name = bf + 5; | ||
228 | while (*name && isspace(*name)) | ||
229 | ++name; | ||
230 | size = strlen(name) - 1; | ||
231 | memcpy(comm_ev.comm, name, size++); | ||
232 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
233 | char *tgids = bf + 5; | ||
234 | while (*tgids && isspace(*tgids)) | ||
235 | ++tgids; | ||
236 | tgid = comm_ev.pid = atoi(tgids); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | comm_ev.header.type = PERF_RECORD_COMM; | ||
241 | size = ALIGN(size, sizeof(u64)); | ||
242 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); | ||
243 | |||
244 | if (!full) { | ||
245 | comm_ev.tid = pid; | ||
246 | |||
247 | write_output(&comm_ev, comm_ev.header.size); | ||
248 | goto out_fclose; | ||
249 | } | ||
250 | |||
251 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | ||
252 | |||
253 | tasks = opendir(filename); | ||
254 | while (!readdir_r(tasks, &dirent, &next) && next) { | ||
255 | char *end; | ||
256 | pid = strtol(dirent.d_name, &end, 10); | ||
257 | if (*end) | ||
258 | continue; | ||
259 | |||
260 | comm_ev.tid = pid; | ||
261 | |||
262 | write_output(&comm_ev, comm_ev.header.size); | ||
263 | } | ||
264 | closedir(tasks); | ||
265 | |||
266 | out_fclose: | ||
267 | fclose(fp); | ||
268 | return tgid; | ||
269 | |||
270 | out_failure: | ||
271 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", | ||
272 | filename); | ||
273 | exit(EXIT_FAILURE); | ||
274 | } | ||
275 | |||
276 | static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid) | ||
277 | { | ||
278 | char filename[PATH_MAX]; | ||
279 | FILE *fp; | ||
280 | |||
281 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); | ||
282 | |||
283 | fp = fopen(filename, "r"); | ||
284 | if (fp == NULL) { | ||
285 | /* | ||
286 | * We raced with a task exiting - just return: | ||
287 | */ | ||
288 | if (verbose) | ||
289 | fprintf(stderr, "couldn't open %s\n", filename); | ||
290 | return; | ||
291 | } | ||
292 | while (1) { | ||
293 | char bf[BUFSIZ], *pbf = bf; | ||
294 | struct mmap_event mmap_ev = { | ||
295 | .header = { .type = PERF_RECORD_MMAP }, | ||
296 | }; | ||
297 | int n; | ||
298 | size_t size; | ||
299 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
300 | break; | ||
301 | |||
302 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | ||
303 | n = hex2u64(pbf, &mmap_ev.start); | ||
304 | if (n < 0) | ||
305 | continue; | ||
306 | pbf += n + 1; | ||
307 | n = hex2u64(pbf, &mmap_ev.len); | ||
308 | if (n < 0) | ||
309 | continue; | ||
310 | pbf += n + 3; | ||
311 | if (*pbf == 'x') { /* vm_exec */ | ||
312 | char *execname = strchr(bf, '/'); | ||
313 | |||
314 | /* Catch VDSO */ | ||
315 | if (execname == NULL) | ||
316 | execname = strstr(bf, "[vdso]"); | ||
317 | |||
318 | if (execname == NULL) | ||
319 | continue; | ||
320 | |||
321 | size = strlen(execname); | ||
322 | execname[size - 1] = '\0'; /* Remove \n */ | ||
323 | memcpy(mmap_ev.filename, execname, size); | ||
324 | size = ALIGN(size, sizeof(u64)); | ||
325 | mmap_ev.len -= mmap_ev.start; | ||
326 | mmap_ev.header.size = (sizeof(mmap_ev) - | ||
327 | (sizeof(mmap_ev.filename) - size)); | ||
328 | mmap_ev.pid = tgid; | ||
329 | mmap_ev.tid = pid; | ||
330 | |||
331 | write_output(&mmap_ev, mmap_ev.header.size); | ||
332 | } | ||
333 | } | ||
334 | |||
335 | fclose(fp); | ||
336 | } | ||
337 | |||
338 | static void synthesize_all(void) | ||
339 | { | ||
340 | DIR *proc; | ||
341 | struct dirent dirent, *next; | ||
342 | |||
343 | proc = opendir("/proc"); | ||
344 | |||
345 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
346 | char *end; | ||
347 | pid_t pid, tgid; | ||
348 | |||
349 | pid = strtol(dirent.d_name, &end, 10); | ||
350 | if (*end) /* only interested in proper numerical dirents */ | ||
351 | continue; | ||
352 | |||
353 | tgid = pid_synthesize_comm_event(pid, 1); | ||
354 | pid_synthesize_mmap_samples(pid, tgid); | ||
355 | } | ||
356 | |||
357 | closedir(proc); | ||
358 | } | ||
359 | |||
360 | static int group_fd; | 207 | static int group_fd; |
361 | 208 | ||
362 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) | 209 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) |
363 | { | 210 | { |
364 | struct perf_header_attr *h_attr; | 211 | struct perf_header_attr *h_attr; |
365 | 212 | ||
366 | if (nr < header->attrs) { | 213 | if (nr < session->header.attrs) { |
367 | h_attr = header->attr[nr]; | 214 | h_attr = session->header.attr[nr]; |
368 | } else { | 215 | } else { |
369 | h_attr = perf_header_attr__new(a); | 216 | h_attr = perf_header_attr__new(a); |
370 | perf_header__add_attr(header, h_attr); | 217 | if (h_attr != NULL) |
218 | if (perf_header__add_attr(&session->header, h_attr) < 0) { | ||
219 | perf_header_attr__delete(h_attr); | ||
220 | h_attr = NULL; | ||
221 | } | ||
371 | } | 222 | } |
372 | 223 | ||
373 | return h_attr; | 224 | return h_attr; |
@@ -375,9 +226,11 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n | |||
375 | 226 | ||
376 | static void create_counter(int counter, int cpu, pid_t pid) | 227 | static void create_counter(int counter, int cpu, pid_t pid) |
377 | { | 228 | { |
229 | char *filter = filters[counter]; | ||
378 | struct perf_event_attr *attr = attrs + counter; | 230 | struct perf_event_attr *attr = attrs + counter; |
379 | struct perf_header_attr *h_attr; | 231 | struct perf_header_attr *h_attr; |
380 | int track = !counter; /* only the first counter needs these */ | 232 | int track = !counter; /* only the first counter needs these */ |
233 | int ret; | ||
381 | struct { | 234 | struct { |
382 | u64 count; | 235 | u64 count; |
383 | u64 time_enabled; | 236 | u64 time_enabled; |
@@ -417,7 +270,7 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
417 | 270 | ||
418 | attr->mmap = track; | 271 | attr->mmap = track; |
419 | attr->comm = track; | 272 | attr->comm = track; |
420 | attr->inherit = (cpu < 0) && inherit; | 273 | attr->inherit = inherit; |
421 | attr->disabled = 1; | 274 | attr->disabled = 1; |
422 | 275 | ||
423 | try_again: | 276 | try_again: |
@@ -448,11 +301,19 @@ try_again: | |||
448 | printf("\n"); | 301 | printf("\n"); |
449 | error("perfcounter syscall returned with %d (%s)\n", | 302 | error("perfcounter syscall returned with %d (%s)\n", |
450 | fd[nr_cpu][counter], strerror(err)); | 303 | fd[nr_cpu][counter], strerror(err)); |
304 | |||
305 | #if defined(__i386__) || defined(__x86_64__) | ||
306 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | ||
307 | die("No hardware sampling interrupt available. No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.\n"); | ||
308 | #endif | ||
309 | |||
451 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 310 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
452 | exit(-1); | 311 | exit(-1); |
453 | } | 312 | } |
454 | 313 | ||
455 | h_attr = get_header_attr(attr, counter); | 314 | h_attr = get_header_attr(attr, counter); |
315 | if (h_attr == NULL) | ||
316 | die("nomem\n"); | ||
456 | 317 | ||
457 | if (!file_new) { | 318 | if (!file_new) { |
458 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | 319 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { |
@@ -466,7 +327,10 @@ try_again: | |||
466 | exit(-1); | 327 | exit(-1); |
467 | } | 328 | } |
468 | 329 | ||
469 | perf_header_attr__add_id(h_attr, read_data.id); | 330 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { |
331 | pr_warning("Not enough memory to add id\n"); | ||
332 | exit(-1); | ||
333 | } | ||
470 | 334 | ||
471 | assert(fd[nr_cpu][counter] >= 0); | 335 | assert(fd[nr_cpu][counter] >= 0); |
472 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); | 336 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); |
@@ -480,7 +344,6 @@ try_again: | |||
480 | multiplex_fd = fd[nr_cpu][counter]; | 344 | multiplex_fd = fd[nr_cpu][counter]; |
481 | 345 | ||
482 | if (multiplex && fd[nr_cpu][counter] != multiplex_fd) { | 346 | if (multiplex && fd[nr_cpu][counter] != multiplex_fd) { |
483 | int ret; | ||
484 | 347 | ||
485 | ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); | 348 | ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); |
486 | assert(ret != -1); | 349 | assert(ret != -1); |
@@ -500,6 +363,16 @@ try_again: | |||
500 | } | 363 | } |
501 | } | 364 | } |
502 | 365 | ||
366 | if (filter != NULL) { | ||
367 | ret = ioctl(fd[nr_cpu][counter], | ||
368 | PERF_EVENT_IOC_SET_FILTER, filter); | ||
369 | if (ret) { | ||
370 | error("failed to set filter with %d (%s)\n", errno, | ||
371 | strerror(errno)); | ||
372 | exit(-1); | ||
373 | } | ||
374 | } | ||
375 | |||
503 | ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE); | 376 | ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE); |
504 | } | 377 | } |
505 | 378 | ||
@@ -514,11 +387,22 @@ static void open_counters(int cpu, pid_t pid) | |||
514 | nr_cpu++; | 387 | nr_cpu++; |
515 | } | 388 | } |
516 | 389 | ||
390 | static int process_buildids(void) | ||
391 | { | ||
392 | u64 size = lseek(output, 0, SEEK_CUR); | ||
393 | |||
394 | session->fd = output; | ||
395 | return __perf_session__process_events(session, post_processing_offset, | ||
396 | size - post_processing_offset, | ||
397 | size, &build_id__mark_dso_hit_ops); | ||
398 | } | ||
399 | |||
517 | static void atexit_header(void) | 400 | static void atexit_header(void) |
518 | { | 401 | { |
519 | header->data_size += bytes_written; | 402 | session->header.data_size += bytes_written; |
520 | 403 | ||
521 | perf_header__write(header, output); | 404 | process_buildids(); |
405 | perf_header__write(&session->header, output, true); | ||
522 | } | 406 | } |
523 | 407 | ||
524 | static int __cmd_record(int argc, const char **argv) | 408 | static int __cmd_record(int argc, const char **argv) |
@@ -527,8 +411,11 @@ static int __cmd_record(int argc, const char **argv) | |||
527 | struct stat st; | 411 | struct stat st; |
528 | pid_t pid = 0; | 412 | pid_t pid = 0; |
529 | int flags; | 413 | int flags; |
530 | int ret; | 414 | int err; |
531 | unsigned long waking = 0; | 415 | unsigned long waking = 0; |
416 | int child_ready_pipe[2], go_pipe[2]; | ||
417 | const bool forks = target_pid == -1 && argc > 0; | ||
418 | char buf; | ||
532 | 419 | ||
533 | page_size = sysconf(_SC_PAGE_SIZE); | 420 | page_size = sysconf(_SC_PAGE_SIZE); |
534 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | 421 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); |
@@ -539,11 +426,25 @@ static int __cmd_record(int argc, const char **argv) | |||
539 | signal(SIGCHLD, sig_handler); | 426 | signal(SIGCHLD, sig_handler); |
540 | signal(SIGINT, sig_handler); | 427 | signal(SIGINT, sig_handler); |
541 | 428 | ||
429 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { | ||
430 | perror("failed to create pipes"); | ||
431 | exit(-1); | ||
432 | } | ||
433 | |||
542 | if (!stat(output_name, &st) && st.st_size) { | 434 | if (!stat(output_name, &st) && st.st_size) { |
543 | if (!force && !append_file) { | 435 | if (!force) { |
544 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", | 436 | if (!append_file) { |
545 | output_name); | 437 | pr_err("Error, output file %s exists, use -A " |
546 | exit(-1); | 438 | "to append or -f to overwrite.\n", |
439 | output_name); | ||
440 | exit(-1); | ||
441 | } | ||
442 | } else { | ||
443 | char oldname[PATH_MAX]; | ||
444 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
445 | output_name); | ||
446 | unlink(oldname); | ||
447 | rename(output_name, oldname); | ||
547 | } | 448 | } |
548 | } else { | 449 | } else { |
549 | append_file = 0; | 450 | append_file = 0; |
@@ -561,73 +462,135 @@ static int __cmd_record(int argc, const char **argv) | |||
561 | exit(-1); | 462 | exit(-1); |
562 | } | 463 | } |
563 | 464 | ||
564 | if (!file_new) | 465 | session = perf_session__new(output_name, O_WRONLY, force); |
565 | header = perf_header__read(output); | 466 | if (session == NULL) { |
566 | else | 467 | pr_err("Not enough memory for reading perf file header\n"); |
567 | header = perf_header__new(); | 468 | return -1; |
469 | } | ||
568 | 470 | ||
471 | if (!file_new) { | ||
472 | err = perf_header__read(&session->header, output); | ||
473 | if (err < 0) | ||
474 | return err; | ||
475 | } | ||
569 | 476 | ||
570 | if (raw_samples) { | 477 | if (raw_samples) { |
571 | read_tracing_data(attrs, nr_counters); | 478 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
572 | } else { | 479 | } else { |
573 | for (i = 0; i < nr_counters; i++) { | 480 | for (i = 0; i < nr_counters; i++) { |
574 | if (attrs[i].sample_type & PERF_SAMPLE_RAW) { | 481 | if (attrs[i].sample_type & PERF_SAMPLE_RAW) { |
575 | read_tracing_data(attrs, nr_counters); | 482 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
576 | break; | 483 | break; |
577 | } | 484 | } |
578 | } | 485 | } |
579 | } | 486 | } |
487 | |||
580 | atexit(atexit_header); | 488 | atexit(atexit_header); |
581 | 489 | ||
582 | if (!system_wide) { | 490 | if (forks) { |
583 | pid = target_pid; | 491 | pid = fork(); |
584 | if (pid == -1) | 492 | if (pid < 0) { |
585 | pid = getpid(); | 493 | perror("failed to fork"); |
494 | exit(-1); | ||
495 | } | ||
496 | |||
497 | if (!pid) { | ||
498 | close(child_ready_pipe[0]); | ||
499 | close(go_pipe[1]); | ||
500 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
501 | |||
502 | /* | ||
503 | * Do a dummy execvp to get the PLT entry resolved, | ||
504 | * so we avoid the resolver overhead on the real | ||
505 | * execvp call. | ||
506 | */ | ||
507 | execvp("", (char **)argv); | ||
508 | |||
509 | /* | ||
510 | * Tell the parent we're ready to go | ||
511 | */ | ||
512 | close(child_ready_pipe[1]); | ||
513 | |||
514 | /* | ||
515 | * Wait until the parent tells us to go. | ||
516 | */ | ||
517 | if (read(go_pipe[0], &buf, 1) == -1) | ||
518 | perror("unable to read pipe"); | ||
519 | |||
520 | execvp(argv[0], (char **)argv); | ||
521 | |||
522 | perror(argv[0]); | ||
523 | exit(-1); | ||
524 | } | ||
586 | 525 | ||
587 | open_counters(profile_cpu, pid); | 526 | child_pid = pid; |
588 | } else { | 527 | |
589 | if (profile_cpu != -1) { | 528 | if (!system_wide) |
590 | open_counters(profile_cpu, target_pid); | 529 | target_pid = pid; |
591 | } else { | 530 | |
592 | for (i = 0; i < nr_cpus; i++) | 531 | close(child_ready_pipe[1]); |
593 | open_counters(i, target_pid); | 532 | close(go_pipe[0]); |
533 | /* | ||
534 | * wait for child to settle | ||
535 | */ | ||
536 | if (read(child_ready_pipe[0], &buf, 1) == -1) { | ||
537 | perror("unable to read pipe"); | ||
538 | exit(-1); | ||
594 | } | 539 | } |
540 | close(child_ready_pipe[0]); | ||
595 | } | 541 | } |
596 | 542 | ||
597 | if (file_new) | ||
598 | perf_header__write(header, output); | ||
599 | 543 | ||
600 | if (!system_wide) { | 544 | if ((!system_wide && !inherit) || profile_cpu != -1) { |
601 | pid_t tgid = pid_synthesize_comm_event(pid, 0); | 545 | open_counters(profile_cpu, target_pid); |
602 | pid_synthesize_mmap_samples(pid, tgid); | 546 | } else { |
603 | } else | 547 | for (i = 0; i < nr_cpus; i++) |
604 | synthesize_all(); | 548 | open_counters(i, target_pid); |
549 | } | ||
605 | 550 | ||
606 | if (target_pid == -1 && argc) { | 551 | if (file_new) { |
607 | pid = fork(); | 552 | err = perf_header__write(&session->header, output, false); |
608 | if (pid < 0) | 553 | if (err < 0) |
609 | perror("failed to fork"); | 554 | return err; |
555 | } | ||
610 | 556 | ||
611 | if (!pid) { | 557 | post_processing_offset = lseek(output, 0, SEEK_CUR); |
612 | if (execvp(argv[0], (char **)argv)) { | ||
613 | perror(argv[0]); | ||
614 | exit(-1); | ||
615 | } | ||
616 | } | ||
617 | 558 | ||
618 | child_pid = pid; | 559 | err = event__synthesize_kernel_mmap(process_synthesized_event, |
560 | session, "_text"); | ||
561 | if (err < 0) { | ||
562 | pr_err("Couldn't record kernel reference relocation symbol.\n"); | ||
563 | return err; | ||
564 | } | ||
565 | |||
566 | err = event__synthesize_modules(process_synthesized_event, session); | ||
567 | if (err < 0) { | ||
568 | pr_err("Couldn't record kernel reference relocation symbol.\n"); | ||
569 | return err; | ||
619 | } | 570 | } |
620 | 571 | ||
572 | if (!system_wide && profile_cpu == -1) | ||
573 | event__synthesize_thread(target_pid, process_synthesized_event, | ||
574 | session); | ||
575 | else | ||
576 | event__synthesize_threads(process_synthesized_event, session); | ||
577 | |||
621 | if (realtime_prio) { | 578 | if (realtime_prio) { |
622 | struct sched_param param; | 579 | struct sched_param param; |
623 | 580 | ||
624 | param.sched_priority = realtime_prio; | 581 | param.sched_priority = realtime_prio; |
625 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 582 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
626 | printf("Could not set realtime priority.\n"); | 583 | pr_err("Could not set realtime priority.\n"); |
627 | exit(-1); | 584 | exit(-1); |
628 | } | 585 | } |
629 | } | 586 | } |
630 | 587 | ||
588 | /* | ||
589 | * Let the child rip | ||
590 | */ | ||
591 | if (forks) | ||
592 | close(go_pipe[1]); | ||
593 | |||
631 | for (;;) { | 594 | for (;;) { |
632 | int hits = samples; | 595 | int hits = samples; |
633 | 596 | ||
@@ -641,7 +604,7 @@ static int __cmd_record(int argc, const char **argv) | |||
641 | if (hits == samples) { | 604 | if (hits == samples) { |
642 | if (done) | 605 | if (done) |
643 | break; | 606 | break; |
644 | ret = poll(event_array, nr_poll, -1); | 607 | err = poll(event_array, nr_poll, -1); |
645 | waking++; | 608 | waking++; |
646 | } | 609 | } |
647 | 610 | ||
@@ -677,6 +640,8 @@ static const struct option options[] = { | |||
677 | OPT_CALLBACK('e', "event", NULL, "event", | 640 | OPT_CALLBACK('e', "event", NULL, "event", |
678 | "event selector. use 'perf list' to list available events", | 641 | "event selector. use 'perf list' to list available events", |
679 | parse_events), | 642 | parse_events), |
643 | OPT_CALLBACK(0, "filter", NULL, "filter", | ||
644 | "event filter", parse_filter), | ||
680 | OPT_INTEGER('p', "pid", &target_pid, | 645 | OPT_INTEGER('p', "pid", &target_pid, |
681 | "record events on existing pid"), | 646 | "record events on existing pid"), |
682 | OPT_INTEGER('r', "realtime", &realtime_prio, | 647 | OPT_INTEGER('r', "realtime", &realtime_prio, |
@@ -721,16 +686,30 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
721 | int counter; | 686 | int counter; |
722 | 687 | ||
723 | argc = parse_options(argc, argv, options, record_usage, | 688 | argc = parse_options(argc, argv, options, record_usage, |
724 | PARSE_OPT_STOP_AT_NON_OPTION); | 689 | PARSE_OPT_STOP_AT_NON_OPTION); |
725 | if (!argc && target_pid == -1 && !system_wide) | 690 | if (!argc && target_pid == -1 && !system_wide && profile_cpu == -1) |
726 | usage_with_options(record_usage, options); | 691 | usage_with_options(record_usage, options); |
727 | 692 | ||
693 | symbol__init(); | ||
694 | |||
728 | if (!nr_counters) { | 695 | if (!nr_counters) { |
729 | nr_counters = 1; | 696 | nr_counters = 1; |
730 | attrs[0].type = PERF_TYPE_HARDWARE; | 697 | attrs[0].type = PERF_TYPE_HARDWARE; |
731 | attrs[0].config = PERF_COUNT_HW_CPU_CYCLES; | 698 | attrs[0].config = PERF_COUNT_HW_CPU_CYCLES; |
732 | } | 699 | } |
733 | 700 | ||
701 | /* | ||
702 | * User specified count overrides default frequency. | ||
703 | */ | ||
704 | if (default_interval) | ||
705 | freq = 0; | ||
706 | else if (freq) { | ||
707 | default_interval = freq; | ||
708 | } else { | ||
709 | fprintf(stderr, "frequency and count are zero, aborting\n"); | ||
710 | exit(EXIT_FAILURE); | ||
711 | } | ||
712 | |||
734 | for (counter = 0; counter < nr_counters; counter++) { | 713 | for (counter = 0; counter < nr_counters; counter++) { |
735 | if (attrs[counter].sample_period) | 714 | if (attrs[counter].sample_period) |
736 | continue; | 715 | continue; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 19669c20088e..cfc655d40bb7 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -22,27 +22,20 @@ | |||
22 | #include "perf.h" | 22 | #include "perf.h" |
23 | #include "util/debug.h" | 23 | #include "util/debug.h" |
24 | #include "util/header.h" | 24 | #include "util/header.h" |
25 | #include "util/session.h" | ||
25 | 26 | ||
26 | #include "util/parse-options.h" | 27 | #include "util/parse-options.h" |
27 | #include "util/parse-events.h" | 28 | #include "util/parse-events.h" |
28 | 29 | ||
29 | #include "util/thread.h" | 30 | #include "util/thread.h" |
31 | #include "util/sort.h" | ||
32 | #include "util/hist.h" | ||
30 | 33 | ||
31 | static char const *input_name = "perf.data"; | 34 | static char const *input_name = "perf.data"; |
32 | 35 | ||
33 | static char default_sort_order[] = "comm,dso,symbol"; | ||
34 | static char *sort_order = default_sort_order; | ||
35 | static char *dso_list_str, *comm_list_str, *sym_list_str, | ||
36 | *col_width_list_str; | ||
37 | static struct strlist *dso_list, *comm_list, *sym_list; | ||
38 | static char *field_sep; | ||
39 | |||
40 | static int force; | 36 | static int force; |
41 | static int input; | 37 | static bool hide_unresolved; |
42 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 38 | static bool dont_use_callchains; |
43 | |||
44 | static int full_paths; | ||
45 | static int show_nr_samples; | ||
46 | 39 | ||
47 | static int show_threads; | 40 | static int show_threads; |
48 | static struct perf_read_values show_threads_values; | 41 | static struct perf_read_values show_threads_values; |
@@ -50,1043 +43,36 @@ static struct perf_read_values show_threads_values; | |||
50 | static char default_pretty_printing_style[] = "normal"; | 43 | static char default_pretty_printing_style[] = "normal"; |
51 | static char *pretty_printing_style = default_pretty_printing_style; | 44 | static char *pretty_printing_style = default_pretty_printing_style; |
52 | 45 | ||
53 | static unsigned long page_size; | ||
54 | static unsigned long mmap_window = 32; | ||
55 | |||
56 | static char default_parent_pattern[] = "^sys_|^do_page_fault"; | ||
57 | static char *parent_pattern = default_parent_pattern; | ||
58 | static regex_t parent_regex; | ||
59 | |||
60 | static int exclude_other = 1; | ||
61 | |||
62 | static char callchain_default_opt[] = "fractal,0.5"; | 46 | static char callchain_default_opt[] = "fractal,0.5"; |
63 | 47 | ||
64 | static int callchain; | 48 | static int perf_session__add_hist_entry(struct perf_session *self, |
65 | 49 | struct addr_location *al, | |
66 | static char __cwd[PATH_MAX]; | 50 | struct ip_callchain *chain, u64 count) |
67 | static char *cwd = __cwd; | ||
68 | static int cwdlen; | ||
69 | |||
70 | static struct rb_root threads; | ||
71 | static struct thread *last_match; | ||
72 | |||
73 | static struct perf_header *header; | ||
74 | |||
75 | static | ||
76 | struct callchain_param callchain_param = { | ||
77 | .mode = CHAIN_GRAPH_REL, | ||
78 | .min_percent = 0.5 | ||
79 | }; | ||
80 | |||
81 | static u64 sample_type; | ||
82 | |||
83 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
84 | { | ||
85 | int n; | ||
86 | va_list ap; | ||
87 | |||
88 | va_start(ap, fmt); | ||
89 | if (!field_sep) | ||
90 | n = vfprintf(fp, fmt, ap); | ||
91 | else { | ||
92 | char *bf = NULL; | ||
93 | n = vasprintf(&bf, fmt, ap); | ||
94 | if (n > 0) { | ||
95 | char *sep = bf; | ||
96 | |||
97 | while (1) { | ||
98 | sep = strchr(sep, *field_sep); | ||
99 | if (sep == NULL) | ||
100 | break; | ||
101 | *sep = '.'; | ||
102 | } | ||
103 | } | ||
104 | fputs(bf, fp); | ||
105 | free(bf); | ||
106 | } | ||
107 | va_end(ap); | ||
108 | return n; | ||
109 | } | ||
110 | |||
111 | static unsigned int dsos__col_width, | ||
112 | comms__col_width, | ||
113 | threads__col_width; | ||
114 | |||
115 | /* | ||
116 | * histogram, sorted on item, collects counts | ||
117 | */ | ||
118 | |||
119 | static struct rb_root hist; | ||
120 | |||
121 | struct hist_entry { | ||
122 | struct rb_node rb_node; | ||
123 | |||
124 | struct thread *thread; | ||
125 | struct map *map; | ||
126 | struct dso *dso; | ||
127 | struct symbol *sym; | ||
128 | struct symbol *parent; | ||
129 | u64 ip; | ||
130 | char level; | ||
131 | struct callchain_node callchain; | ||
132 | struct rb_root sorted_chain; | ||
133 | |||
134 | u64 count; | ||
135 | }; | ||
136 | |||
137 | /* | ||
138 | * configurable sorting bits | ||
139 | */ | ||
140 | |||
141 | struct sort_entry { | ||
142 | struct list_head list; | ||
143 | |||
144 | const char *header; | ||
145 | |||
146 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | ||
147 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | ||
148 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); | ||
149 | unsigned int *width; | ||
150 | bool elide; | ||
151 | }; | ||
152 | |||
153 | static int64_t cmp_null(void *l, void *r) | ||
154 | { | ||
155 | if (!l && !r) | ||
156 | return 0; | ||
157 | else if (!l) | ||
158 | return -1; | ||
159 | else | ||
160 | return 1; | ||
161 | } | ||
162 | |||
163 | /* --sort pid */ | ||
164 | |||
165 | static int64_t | ||
166 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
167 | { | ||
168 | return right->thread->pid - left->thread->pid; | ||
169 | } | ||
170 | |||
171 | static size_t | ||
172 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
173 | { | ||
174 | return repsep_fprintf(fp, "%*s:%5d", width - 6, | ||
175 | self->thread->comm ?: "", self->thread->pid); | ||
176 | } | ||
177 | |||
178 | static struct sort_entry sort_thread = { | ||
179 | .header = "Command: Pid", | ||
180 | .cmp = sort__thread_cmp, | ||
181 | .print = sort__thread_print, | ||
182 | .width = &threads__col_width, | ||
183 | }; | ||
184 | |||
185 | /* --sort comm */ | ||
186 | |||
187 | static int64_t | ||
188 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
189 | { | ||
190 | return right->thread->pid - left->thread->pid; | ||
191 | } | ||
192 | |||
193 | static int64_t | ||
194 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
195 | { | ||
196 | char *comm_l = left->thread->comm; | ||
197 | char *comm_r = right->thread->comm; | ||
198 | |||
199 | if (!comm_l || !comm_r) | ||
200 | return cmp_null(comm_l, comm_r); | ||
201 | |||
202 | return strcmp(comm_l, comm_r); | ||
203 | } | ||
204 | |||
205 | static size_t | ||
206 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
207 | { | ||
208 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); | ||
209 | } | ||
210 | |||
211 | static struct sort_entry sort_comm = { | ||
212 | .header = "Command", | ||
213 | .cmp = sort__comm_cmp, | ||
214 | .collapse = sort__comm_collapse, | ||
215 | .print = sort__comm_print, | ||
216 | .width = &comms__col_width, | ||
217 | }; | ||
218 | |||
219 | /* --sort dso */ | ||
220 | |||
221 | static int64_t | ||
222 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | ||
223 | { | ||
224 | struct dso *dso_l = left->dso; | ||
225 | struct dso *dso_r = right->dso; | ||
226 | |||
227 | if (!dso_l || !dso_r) | ||
228 | return cmp_null(dso_l, dso_r); | ||
229 | |||
230 | return strcmp(dso_l->name, dso_r->name); | ||
231 | } | ||
232 | |||
233 | static size_t | ||
234 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
235 | { | ||
236 | if (self->dso) | ||
237 | return repsep_fprintf(fp, "%-*s", width, self->dso->name); | ||
238 | |||
239 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); | ||
240 | } | ||
241 | |||
242 | static struct sort_entry sort_dso = { | ||
243 | .header = "Shared Object", | ||
244 | .cmp = sort__dso_cmp, | ||
245 | .print = sort__dso_print, | ||
246 | .width = &dsos__col_width, | ||
247 | }; | ||
248 | |||
249 | /* --sort symbol */ | ||
250 | |||
251 | static int64_t | ||
252 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
253 | { | ||
254 | u64 ip_l, ip_r; | ||
255 | |||
256 | if (left->sym == right->sym) | ||
257 | return 0; | ||
258 | |||
259 | ip_l = left->sym ? left->sym->start : left->ip; | ||
260 | ip_r = right->sym ? right->sym->start : right->ip; | ||
261 | |||
262 | return (int64_t)(ip_r - ip_l); | ||
263 | } | ||
264 | |||
265 | static size_t | ||
266 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | ||
267 | { | ||
268 | size_t ret = 0; | ||
269 | |||
270 | if (verbose) | ||
271 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, | ||
272 | dso__symtab_origin(self->dso)); | ||
273 | |||
274 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
275 | if (self->sym) { | ||
276 | ret += repsep_fprintf(fp, "%s", self->sym->name); | ||
277 | |||
278 | if (self->sym->module) | ||
279 | ret += repsep_fprintf(fp, "\t[%s]", | ||
280 | self->sym->module->name); | ||
281 | } else { | ||
282 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); | ||
283 | } | ||
284 | |||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | static struct sort_entry sort_sym = { | ||
289 | .header = "Symbol", | ||
290 | .cmp = sort__sym_cmp, | ||
291 | .print = sort__sym_print, | ||
292 | }; | ||
293 | |||
294 | /* --sort parent */ | ||
295 | |||
296 | static int64_t | ||
297 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | ||
298 | { | ||
299 | struct symbol *sym_l = left->parent; | ||
300 | struct symbol *sym_r = right->parent; | ||
301 | |||
302 | if (!sym_l || !sym_r) | ||
303 | return cmp_null(sym_l, sym_r); | ||
304 | |||
305 | return strcmp(sym_l->name, sym_r->name); | ||
306 | } | ||
307 | |||
308 | static size_t | ||
309 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
310 | { | ||
311 | return repsep_fprintf(fp, "%-*s", width, | ||
312 | self->parent ? self->parent->name : "[other]"); | ||
313 | } | ||
314 | |||
315 | static unsigned int parent_symbol__col_width; | ||
316 | |||
317 | static struct sort_entry sort_parent = { | ||
318 | .header = "Parent symbol", | ||
319 | .cmp = sort__parent_cmp, | ||
320 | .print = sort__parent_print, | ||
321 | .width = &parent_symbol__col_width, | ||
322 | }; | ||
323 | |||
324 | static int sort__need_collapse = 0; | ||
325 | static int sort__has_parent = 0; | ||
326 | |||
327 | struct sort_dimension { | ||
328 | const char *name; | ||
329 | struct sort_entry *entry; | ||
330 | int taken; | ||
331 | }; | ||
332 | |||
333 | static struct sort_dimension sort_dimensions[] = { | ||
334 | { .name = "pid", .entry = &sort_thread, }, | ||
335 | { .name = "comm", .entry = &sort_comm, }, | ||
336 | { .name = "dso", .entry = &sort_dso, }, | ||
337 | { .name = "symbol", .entry = &sort_sym, }, | ||
338 | { .name = "parent", .entry = &sort_parent, }, | ||
339 | }; | ||
340 | |||
341 | static LIST_HEAD(hist_entry__sort_list); | ||
342 | |||
343 | static int sort_dimension__add(const char *tok) | ||
344 | { | ||
345 | unsigned int i; | ||
346 | |||
347 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | ||
348 | struct sort_dimension *sd = &sort_dimensions[i]; | ||
349 | |||
350 | if (sd->taken) | ||
351 | continue; | ||
352 | |||
353 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
354 | continue; | ||
355 | |||
356 | if (sd->entry->collapse) | ||
357 | sort__need_collapse = 1; | ||
358 | |||
359 | if (sd->entry == &sort_parent) { | ||
360 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | ||
361 | if (ret) { | ||
362 | char err[BUFSIZ]; | ||
363 | |||
364 | regerror(ret, &parent_regex, err, sizeof(err)); | ||
365 | fprintf(stderr, "Invalid regex: %s\n%s", | ||
366 | parent_pattern, err); | ||
367 | exit(-1); | ||
368 | } | ||
369 | sort__has_parent = 1; | ||
370 | } | ||
371 | |||
372 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
373 | sd->taken = 1; | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | return -ESRCH; | ||
379 | } | ||
380 | |||
381 | static int64_t | ||
382 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | ||
383 | { | ||
384 | struct sort_entry *se; | ||
385 | int64_t cmp = 0; | ||
386 | |||
387 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
388 | cmp = se->cmp(left, right); | ||
389 | if (cmp) | ||
390 | break; | ||
391 | } | ||
392 | |||
393 | return cmp; | ||
394 | } | ||
395 | |||
396 | static int64_t | ||
397 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | ||
398 | { | ||
399 | struct sort_entry *se; | ||
400 | int64_t cmp = 0; | ||
401 | |||
402 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
403 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | ||
404 | |||
405 | f = se->collapse ?: se->cmp; | ||
406 | |||
407 | cmp = f(left, right); | ||
408 | if (cmp) | ||
409 | break; | ||
410 | } | ||
411 | |||
412 | return cmp; | ||
413 | } | ||
414 | |||
415 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask) | ||
416 | { | ||
417 | int i; | ||
418 | size_t ret = 0; | ||
419 | |||
420 | ret += fprintf(fp, "%s", " "); | ||
421 | |||
422 | for (i = 0; i < depth; i++) | ||
423 | if (depth_mask & (1 << i)) | ||
424 | ret += fprintf(fp, "| "); | ||
425 | else | ||
426 | ret += fprintf(fp, " "); | ||
427 | |||
428 | ret += fprintf(fp, "\n"); | ||
429 | |||
430 | return ret; | ||
431 | } | ||
432 | static size_t | ||
433 | ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | ||
434 | int depth_mask, int count, u64 total_samples, | ||
435 | int hits) | ||
436 | { | ||
437 | int i; | ||
438 | size_t ret = 0; | ||
439 | |||
440 | ret += fprintf(fp, "%s", " "); | ||
441 | for (i = 0; i < depth; i++) { | ||
442 | if (depth_mask & (1 << i)) | ||
443 | ret += fprintf(fp, "|"); | ||
444 | else | ||
445 | ret += fprintf(fp, " "); | ||
446 | if (!count && i == depth - 1) { | ||
447 | double percent; | ||
448 | |||
449 | percent = hits * 100.0 / total_samples; | ||
450 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
451 | } else | ||
452 | ret += fprintf(fp, "%s", " "); | ||
453 | } | ||
454 | if (chain->sym) | ||
455 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
456 | else | ||
457 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
458 | |||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | static struct symbol *rem_sq_bracket; | ||
463 | static struct callchain_list rem_hits; | ||
464 | |||
465 | static void init_rem_hits(void) | ||
466 | { | ||
467 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
468 | if (!rem_sq_bracket) { | ||
469 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
470 | return; | ||
471 | } | ||
472 | |||
473 | strcpy(rem_sq_bracket->name, "[...]"); | ||
474 | rem_hits.sym = rem_sq_bracket; | ||
475 | } | ||
476 | |||
477 | static size_t | ||
478 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
479 | u64 total_samples, int depth, int depth_mask) | ||
480 | { | ||
481 | struct rb_node *node, *next; | ||
482 | struct callchain_node *child; | ||
483 | struct callchain_list *chain; | ||
484 | int new_depth_mask = depth_mask; | ||
485 | u64 new_total; | ||
486 | u64 remaining; | ||
487 | size_t ret = 0; | ||
488 | int i; | ||
489 | |||
490 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
491 | new_total = self->children_hit; | ||
492 | else | ||
493 | new_total = total_samples; | ||
494 | |||
495 | remaining = new_total; | ||
496 | |||
497 | node = rb_first(&self->rb_root); | ||
498 | while (node) { | ||
499 | u64 cumul; | ||
500 | |||
501 | child = rb_entry(node, struct callchain_node, rb_node); | ||
502 | cumul = cumul_hits(child); | ||
503 | remaining -= cumul; | ||
504 | |||
505 | /* | ||
506 | * The depth mask manages the output of pipes that show | ||
507 | * the depth. We don't want to keep the pipes of the current | ||
508 | * level for the last child of this depth. | ||
509 | * Except if we have remaining filtered hits. They will | ||
510 | * supersede the last child | ||
511 | */ | ||
512 | next = rb_next(node); | ||
513 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
514 | new_depth_mask &= ~(1 << (depth - 1)); | ||
515 | |||
516 | /* | ||
517 | * But we keep the older depth mask for the line seperator | ||
518 | * to keep the level link until we reach the last child | ||
519 | */ | ||
520 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask); | ||
521 | i = 0; | ||
522 | list_for_each_entry(chain, &child->val, list) { | ||
523 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
524 | continue; | ||
525 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
526 | new_depth_mask, i++, | ||
527 | new_total, | ||
528 | cumul); | ||
529 | } | ||
530 | ret += callchain__fprintf_graph(fp, child, new_total, | ||
531 | depth + 1, | ||
532 | new_depth_mask | (1 << depth)); | ||
533 | node = next; | ||
534 | } | ||
535 | |||
536 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
537 | remaining && remaining != new_total) { | ||
538 | |||
539 | if (!rem_sq_bracket) | ||
540 | return ret; | ||
541 | |||
542 | new_depth_mask &= ~(1 << (depth - 1)); | ||
543 | |||
544 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
545 | new_depth_mask, 0, new_total, | ||
546 | remaining); | ||
547 | } | ||
548 | |||
549 | return ret; | ||
550 | } | ||
551 | |||
552 | static size_t | ||
553 | callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
554 | u64 total_samples) | ||
555 | { | ||
556 | struct callchain_list *chain; | ||
557 | size_t ret = 0; | ||
558 | |||
559 | if (!self) | ||
560 | return 0; | ||
561 | |||
562 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
563 | |||
564 | |||
565 | list_for_each_entry(chain, &self->val, list) { | ||
566 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
567 | continue; | ||
568 | if (chain->sym) | ||
569 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
570 | else | ||
571 | ret += fprintf(fp, " %p\n", | ||
572 | (void *)(long)chain->ip); | ||
573 | } | ||
574 | |||
575 | return ret; | ||
576 | } | ||
577 | |||
578 | static size_t | ||
579 | hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
580 | u64 total_samples) | ||
581 | { | ||
582 | struct rb_node *rb_node; | ||
583 | struct callchain_node *chain; | ||
584 | size_t ret = 0; | ||
585 | |||
586 | rb_node = rb_first(&self->sorted_chain); | ||
587 | while (rb_node) { | ||
588 | double percent; | ||
589 | |||
590 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
591 | percent = chain->hit * 100.0 / total_samples; | ||
592 | switch (callchain_param.mode) { | ||
593 | case CHAIN_FLAT: | ||
594 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
595 | percent); | ||
596 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
597 | break; | ||
598 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
599 | case CHAIN_GRAPH_REL: | ||
600 | ret += callchain__fprintf_graph(fp, chain, | ||
601 | total_samples, 1, 1); | ||
602 | case CHAIN_NONE: | ||
603 | default: | ||
604 | break; | ||
605 | } | ||
606 | ret += fprintf(fp, "\n"); | ||
607 | rb_node = rb_next(rb_node); | ||
608 | } | ||
609 | |||
610 | return ret; | ||
611 | } | ||
612 | |||
613 | |||
614 | static size_t | ||
615 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | ||
616 | { | ||
617 | struct sort_entry *se; | ||
618 | size_t ret; | ||
619 | |||
620 | if (exclude_other && !self->parent) | ||
621 | return 0; | ||
622 | |||
623 | if (total_samples) | ||
624 | ret = percent_color_fprintf(fp, | ||
625 | field_sep ? "%.2f" : " %6.2f%%", | ||
626 | (self->count * 100.0) / total_samples); | ||
627 | else | ||
628 | ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); | ||
629 | |||
630 | if (show_nr_samples) { | ||
631 | if (field_sep) | ||
632 | fprintf(fp, "%c%lld", *field_sep, self->count); | ||
633 | else | ||
634 | fprintf(fp, "%11lld", self->count); | ||
635 | } | ||
636 | |||
637 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
638 | if (se->elide) | ||
639 | continue; | ||
640 | |||
641 | fprintf(fp, "%s", field_sep ?: " "); | ||
642 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
643 | } | ||
644 | |||
645 | ret += fprintf(fp, "\n"); | ||
646 | |||
647 | if (callchain) | ||
648 | hist_entry_callchain__fprintf(fp, self, total_samples); | ||
649 | |||
650 | return ret; | ||
651 | } | ||
652 | |||
653 | /* | ||
654 | * | ||
655 | */ | ||
656 | |||
657 | static void dso__calc_col_width(struct dso *self) | ||
658 | { | ||
659 | if (!col_width_list_str && !field_sep && | ||
660 | (!dso_list || strlist__has_entry(dso_list, self->name))) { | ||
661 | unsigned int slen = strlen(self->name); | ||
662 | if (slen > dsos__col_width) | ||
663 | dsos__col_width = slen; | ||
664 | } | ||
665 | |||
666 | self->slen_calculated = 1; | ||
667 | } | ||
668 | |||
669 | static void thread__comm_adjust(struct thread *self) | ||
670 | { | ||
671 | char *comm = self->comm; | ||
672 | |||
673 | if (!col_width_list_str && !field_sep && | ||
674 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
675 | unsigned int slen = strlen(comm); | ||
676 | |||
677 | if (slen > comms__col_width) { | ||
678 | comms__col_width = slen; | ||
679 | threads__col_width = slen + 6; | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | |||
684 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | ||
685 | { | ||
686 | int ret = thread__set_comm(self, comm); | ||
687 | |||
688 | if (ret) | ||
689 | return ret; | ||
690 | |||
691 | thread__comm_adjust(self); | ||
692 | |||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | |||
697 | static struct symbol * | ||
698 | resolve_symbol(struct thread *thread, struct map **mapp, | ||
699 | struct dso **dsop, u64 *ipp) | ||
700 | { | ||
701 | struct dso *dso = dsop ? *dsop : NULL; | ||
702 | struct map *map = mapp ? *mapp : NULL; | ||
703 | u64 ip = *ipp; | ||
704 | |||
705 | if (!thread) | ||
706 | return NULL; | ||
707 | |||
708 | if (dso) | ||
709 | goto got_dso; | ||
710 | |||
711 | if (map) | ||
712 | goto got_map; | ||
713 | |||
714 | map = thread__find_map(thread, ip); | ||
715 | if (map != NULL) { | ||
716 | /* | ||
717 | * We have to do this here as we may have a dso | ||
718 | * with no symbol hit that has a name longer than | ||
719 | * the ones with symbols sampled. | ||
720 | */ | ||
721 | if (!sort_dso.elide && !map->dso->slen_calculated) | ||
722 | dso__calc_col_width(map->dso); | ||
723 | |||
724 | if (mapp) | ||
725 | *mapp = map; | ||
726 | got_map: | ||
727 | ip = map->map_ip(map, ip); | ||
728 | |||
729 | dso = map->dso; | ||
730 | } else { | ||
731 | /* | ||
732 | * If this is outside of all known maps, | ||
733 | * and is a negative address, try to look it | ||
734 | * up in the kernel dso, as it might be a | ||
735 | * vsyscall (which executes in user-mode): | ||
736 | */ | ||
737 | if ((long long)ip < 0) | ||
738 | dso = kernel_dso; | ||
739 | } | ||
740 | dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); | ||
741 | dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip); | ||
742 | *ipp = ip; | ||
743 | |||
744 | if (dsop) | ||
745 | *dsop = dso; | ||
746 | |||
747 | if (!dso) | ||
748 | return NULL; | ||
749 | got_dso: | ||
750 | return dso->find_symbol(dso, ip); | ||
751 | } | ||
752 | |||
753 | static int call__match(struct symbol *sym) | ||
754 | { | ||
755 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
756 | return 1; | ||
757 | |||
758 | return 0; | ||
759 | } | ||
760 | |||
761 | static struct symbol ** | ||
762 | resolve_callchain(struct thread *thread, struct map *map __used, | ||
763 | struct ip_callchain *chain, struct hist_entry *entry) | ||
764 | { | ||
765 | u64 context = PERF_CONTEXT_MAX; | ||
766 | struct symbol **syms = NULL; | ||
767 | unsigned int i; | ||
768 | |||
769 | if (callchain) { | ||
770 | syms = calloc(chain->nr, sizeof(*syms)); | ||
771 | if (!syms) { | ||
772 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
773 | exit(-1); | ||
774 | } | ||
775 | } | ||
776 | |||
777 | for (i = 0; i < chain->nr; i++) { | ||
778 | u64 ip = chain->ips[i]; | ||
779 | struct dso *dso = NULL; | ||
780 | struct symbol *sym; | ||
781 | |||
782 | if (ip >= PERF_CONTEXT_MAX) { | ||
783 | context = ip; | ||
784 | continue; | ||
785 | } | ||
786 | |||
787 | switch (context) { | ||
788 | case PERF_CONTEXT_HV: | ||
789 | dso = hypervisor_dso; | ||
790 | break; | ||
791 | case PERF_CONTEXT_KERNEL: | ||
792 | dso = kernel_dso; | ||
793 | break; | ||
794 | default: | ||
795 | break; | ||
796 | } | ||
797 | |||
798 | sym = resolve_symbol(thread, NULL, &dso, &ip); | ||
799 | |||
800 | if (sym) { | ||
801 | if (sort__has_parent && call__match(sym) && | ||
802 | !entry->parent) | ||
803 | entry->parent = sym; | ||
804 | if (!callchain) | ||
805 | break; | ||
806 | syms[i] = sym; | ||
807 | } | ||
808 | } | ||
809 | |||
810 | return syms; | ||
811 | } | ||
812 | |||
813 | /* | ||
814 | * collect histogram counts | ||
815 | */ | ||
816 | |||
817 | static int | ||
818 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | ||
819 | struct symbol *sym, u64 ip, struct ip_callchain *chain, | ||
820 | char level, u64 count) | ||
821 | { | 51 | { |
822 | struct rb_node **p = &hist.rb_node; | 52 | struct symbol **syms = NULL, *parent = NULL; |
823 | struct rb_node *parent = NULL; | 53 | bool hit; |
824 | struct hist_entry *he; | 54 | struct hist_entry *he; |
825 | struct symbol **syms = NULL; | ||
826 | struct hist_entry entry = { | ||
827 | .thread = thread, | ||
828 | .map = map, | ||
829 | .dso = dso, | ||
830 | .sym = sym, | ||
831 | .ip = ip, | ||
832 | .level = level, | ||
833 | .count = count, | ||
834 | .parent = NULL, | ||
835 | .sorted_chain = RB_ROOT | ||
836 | }; | ||
837 | int cmp; | ||
838 | |||
839 | if ((sort__has_parent || callchain) && chain) | ||
840 | syms = resolve_callchain(thread, map, chain, &entry); | ||
841 | |||
842 | while (*p != NULL) { | ||
843 | parent = *p; | ||
844 | he = rb_entry(parent, struct hist_entry, rb_node); | ||
845 | |||
846 | cmp = hist_entry__cmp(&entry, he); | ||
847 | |||
848 | if (!cmp) { | ||
849 | he->count += count; | ||
850 | if (callchain) { | ||
851 | append_chain(&he->callchain, chain, syms); | ||
852 | free(syms); | ||
853 | } | ||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | if (cmp < 0) | ||
858 | p = &(*p)->rb_left; | ||
859 | else | ||
860 | p = &(*p)->rb_right; | ||
861 | } | ||
862 | 55 | ||
863 | he = malloc(sizeof(*he)); | 56 | if ((sort__has_parent || symbol_conf.use_callchain) && chain) |
864 | if (!he) | 57 | syms = perf_session__resolve_callchain(self, al->thread, |
58 | chain, &parent); | ||
59 | he = __perf_session__add_hist_entry(self, al, parent, count, &hit); | ||
60 | if (he == NULL) | ||
865 | return -ENOMEM; | 61 | return -ENOMEM; |
866 | *he = entry; | 62 | |
867 | if (callchain) { | 63 | if (hit) |
868 | callchain_init(&he->callchain); | 64 | he->count += count; |
65 | |||
66 | if (symbol_conf.use_callchain) { | ||
67 | if (!hit) | ||
68 | callchain_init(&he->callchain); | ||
869 | append_chain(&he->callchain, chain, syms); | 69 | append_chain(&he->callchain, chain, syms); |
870 | free(syms); | 70 | free(syms); |
871 | } | 71 | } |
872 | rb_link_node(&he->rb_node, parent, p); | ||
873 | rb_insert_color(&he->rb_node, &hist); | ||
874 | 72 | ||
875 | return 0; | 73 | return 0; |
876 | } | 74 | } |
877 | 75 | ||
878 | static void hist_entry__free(struct hist_entry *he) | ||
879 | { | ||
880 | free(he); | ||
881 | } | ||
882 | |||
883 | /* | ||
884 | * collapse the histogram | ||
885 | */ | ||
886 | |||
887 | static struct rb_root collapse_hists; | ||
888 | |||
889 | static void collapse__insert_entry(struct hist_entry *he) | ||
890 | { | ||
891 | struct rb_node **p = &collapse_hists.rb_node; | ||
892 | struct rb_node *parent = NULL; | ||
893 | struct hist_entry *iter; | ||
894 | int64_t cmp; | ||
895 | |||
896 | while (*p != NULL) { | ||
897 | parent = *p; | ||
898 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
899 | |||
900 | cmp = hist_entry__collapse(iter, he); | ||
901 | |||
902 | if (!cmp) { | ||
903 | iter->count += he->count; | ||
904 | hist_entry__free(he); | ||
905 | return; | ||
906 | } | ||
907 | |||
908 | if (cmp < 0) | ||
909 | p = &(*p)->rb_left; | ||
910 | else | ||
911 | p = &(*p)->rb_right; | ||
912 | } | ||
913 | |||
914 | rb_link_node(&he->rb_node, parent, p); | ||
915 | rb_insert_color(&he->rb_node, &collapse_hists); | ||
916 | } | ||
917 | |||
918 | static void collapse__resort(void) | ||
919 | { | ||
920 | struct rb_node *next; | ||
921 | struct hist_entry *n; | ||
922 | |||
923 | if (!sort__need_collapse) | ||
924 | return; | ||
925 | |||
926 | next = rb_first(&hist); | ||
927 | while (next) { | ||
928 | n = rb_entry(next, struct hist_entry, rb_node); | ||
929 | next = rb_next(&n->rb_node); | ||
930 | |||
931 | rb_erase(&n->rb_node, &hist); | ||
932 | collapse__insert_entry(n); | ||
933 | } | ||
934 | } | ||
935 | |||
936 | /* | ||
937 | * reverse the map, sort on count. | ||
938 | */ | ||
939 | |||
940 | static struct rb_root output_hists; | ||
941 | |||
942 | static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) | ||
943 | { | ||
944 | struct rb_node **p = &output_hists.rb_node; | ||
945 | struct rb_node *parent = NULL; | ||
946 | struct hist_entry *iter; | ||
947 | |||
948 | if (callchain) | ||
949 | callchain_param.sort(&he->sorted_chain, &he->callchain, | ||
950 | min_callchain_hits, &callchain_param); | ||
951 | |||
952 | while (*p != NULL) { | ||
953 | parent = *p; | ||
954 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
955 | |||
956 | if (he->count > iter->count) | ||
957 | p = &(*p)->rb_left; | ||
958 | else | ||
959 | p = &(*p)->rb_right; | ||
960 | } | ||
961 | |||
962 | rb_link_node(&he->rb_node, parent, p); | ||
963 | rb_insert_color(&he->rb_node, &output_hists); | ||
964 | } | ||
965 | |||
966 | static void output__resort(u64 total_samples) | ||
967 | { | ||
968 | struct rb_node *next; | ||
969 | struct hist_entry *n; | ||
970 | struct rb_root *tree = &hist; | ||
971 | u64 min_callchain_hits; | ||
972 | |||
973 | min_callchain_hits = total_samples * (callchain_param.min_percent / 100); | ||
974 | |||
975 | if (sort__need_collapse) | ||
976 | tree = &collapse_hists; | ||
977 | |||
978 | next = rb_first(tree); | ||
979 | |||
980 | while (next) { | ||
981 | n = rb_entry(next, struct hist_entry, rb_node); | ||
982 | next = rb_next(&n->rb_node); | ||
983 | |||
984 | rb_erase(&n->rb_node, tree); | ||
985 | output__insert_entry(n, min_callchain_hits); | ||
986 | } | ||
987 | } | ||
988 | |||
989 | static size_t output__fprintf(FILE *fp, u64 total_samples) | ||
990 | { | ||
991 | struct hist_entry *pos; | ||
992 | struct sort_entry *se; | ||
993 | struct rb_node *nd; | ||
994 | size_t ret = 0; | ||
995 | unsigned int width; | ||
996 | char *col_width = col_width_list_str; | ||
997 | int raw_printing_style; | ||
998 | |||
999 | raw_printing_style = !strcmp(pretty_printing_style, "raw"); | ||
1000 | |||
1001 | init_rem_hits(); | ||
1002 | |||
1003 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); | ||
1004 | fprintf(fp, "#\n"); | ||
1005 | |||
1006 | fprintf(fp, "# Overhead"); | ||
1007 | if (show_nr_samples) { | ||
1008 | if (field_sep) | ||
1009 | fprintf(fp, "%cSamples", *field_sep); | ||
1010 | else | ||
1011 | fputs(" Samples ", fp); | ||
1012 | } | ||
1013 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
1014 | if (se->elide) | ||
1015 | continue; | ||
1016 | if (field_sep) { | ||
1017 | fprintf(fp, "%c%s", *field_sep, se->header); | ||
1018 | continue; | ||
1019 | } | ||
1020 | width = strlen(se->header); | ||
1021 | if (se->width) { | ||
1022 | if (col_width_list_str) { | ||
1023 | if (col_width) { | ||
1024 | *se->width = atoi(col_width); | ||
1025 | col_width = strchr(col_width, ','); | ||
1026 | if (col_width) | ||
1027 | ++col_width; | ||
1028 | } | ||
1029 | } | ||
1030 | width = *se->width = max(*se->width, width); | ||
1031 | } | ||
1032 | fprintf(fp, " %*s", width, se->header); | ||
1033 | } | ||
1034 | fprintf(fp, "\n"); | ||
1035 | |||
1036 | if (field_sep) | ||
1037 | goto print_entries; | ||
1038 | |||
1039 | fprintf(fp, "# ........"); | ||
1040 | if (show_nr_samples) | ||
1041 | fprintf(fp, " .........."); | ||
1042 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
1043 | unsigned int i; | ||
1044 | |||
1045 | if (se->elide) | ||
1046 | continue; | ||
1047 | |||
1048 | fprintf(fp, " "); | ||
1049 | if (se->width) | ||
1050 | width = *se->width; | ||
1051 | else | ||
1052 | width = strlen(se->header); | ||
1053 | for (i = 0; i < width; i++) | ||
1054 | fprintf(fp, "."); | ||
1055 | } | ||
1056 | fprintf(fp, "\n"); | ||
1057 | |||
1058 | fprintf(fp, "#\n"); | ||
1059 | |||
1060 | print_entries: | ||
1061 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | ||
1062 | pos = rb_entry(nd, struct hist_entry, rb_node); | ||
1063 | ret += hist_entry__fprintf(fp, pos, total_samples); | ||
1064 | } | ||
1065 | |||
1066 | if (sort_order == default_sort_order && | ||
1067 | parent_pattern == default_parent_pattern) { | ||
1068 | fprintf(fp, "#\n"); | ||
1069 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); | ||
1070 | fprintf(fp, "#\n"); | ||
1071 | } | ||
1072 | fprintf(fp, "\n"); | ||
1073 | |||
1074 | free(rem_sq_bracket); | ||
1075 | |||
1076 | if (show_threads) | ||
1077 | perf_read_values_display(fp, &show_threads_values, | ||
1078 | raw_printing_style); | ||
1079 | |||
1080 | return ret; | ||
1081 | } | ||
1082 | |||
1083 | static unsigned long total = 0, | ||
1084 | total_mmap = 0, | ||
1085 | total_comm = 0, | ||
1086 | total_fork = 0, | ||
1087 | total_unknown = 0, | ||
1088 | total_lost = 0; | ||
1089 | |||
1090 | static int validate_chain(struct ip_callchain *chain, event_t *event) | 76 | static int validate_chain(struct ip_callchain *chain, event_t *event) |
1091 | { | 77 | { |
1092 | unsigned int chain_size; | 78 | unsigned int chain_size; |
@@ -1100,214 +86,57 @@ static int validate_chain(struct ip_callchain *chain, event_t *event) | |||
1100 | return 0; | 86 | return 0; |
1101 | } | 87 | } |
1102 | 88 | ||
1103 | static int | 89 | static int process_sample_event(event_t *event, struct perf_session *session) |
1104 | process_sample_event(event_t *event, unsigned long offset, unsigned long head) | ||
1105 | { | 90 | { |
1106 | char level; | 91 | struct sample_data data = { .period = 1, }; |
1107 | int show = 0; | 92 | struct addr_location al; |
1108 | struct dso *dso = NULL; | ||
1109 | struct thread *thread; | ||
1110 | u64 ip = event->ip.ip; | ||
1111 | u64 period = 1; | ||
1112 | struct map *map = NULL; | ||
1113 | void *more_data = event->ip.__more_data; | ||
1114 | struct ip_callchain *chain = NULL; | ||
1115 | int cpumode; | ||
1116 | |||
1117 | thread = threads__findnew(event->ip.pid, &threads, &last_match); | ||
1118 | |||
1119 | if (sample_type & PERF_SAMPLE_PERIOD) { | ||
1120 | period = *(u64 *)more_data; | ||
1121 | more_data += sizeof(u64); | ||
1122 | } | ||
1123 | 93 | ||
1124 | dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", | 94 | event__parse_sample(event, session->sample_type, &data); |
1125 | (void *)(offset + head), | ||
1126 | (void *)(long)(event->header.size), | ||
1127 | event->header.misc, | ||
1128 | event->ip.pid, event->ip.tid, | ||
1129 | (void *)(long)ip, | ||
1130 | (long long)period); | ||
1131 | 95 | ||
1132 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { | 96 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
1133 | unsigned int i; | 97 | data.pid, data.tid, data.ip, data.period); |
1134 | 98 | ||
1135 | chain = (void *)more_data; | 99 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { |
100 | unsigned int i; | ||
1136 | 101 | ||
1137 | dump_printf("... chain: nr:%Lu\n", chain->nr); | 102 | dump_printf("... chain: nr:%Lu\n", data.callchain->nr); |
1138 | 103 | ||
1139 | if (validate_chain(chain, event) < 0) { | 104 | if (validate_chain(data.callchain, event) < 0) { |
1140 | eprintf("call-chain problem with event, skipping it.\n"); | 105 | pr_debug("call-chain problem with event, " |
106 | "skipping it.\n"); | ||
1141 | return 0; | 107 | return 0; |
1142 | } | 108 | } |
1143 | 109 | ||
1144 | if (dump_trace) { | 110 | if (dump_trace) { |
1145 | for (i = 0; i < chain->nr; i++) | 111 | for (i = 0; i < data.callchain->nr; i++) |
1146 | dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]); | 112 | dump_printf("..... %2d: %016Lx\n", |
113 | i, data.callchain->ips[i]); | ||
1147 | } | 114 | } |
1148 | } | 115 | } |
1149 | 116 | ||
1150 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 117 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { |
1151 | 118 | fprintf(stderr, "problem processing %d event, skipping it.\n", | |
1152 | if (thread == NULL) { | ||
1153 | eprintf("problem processing %d event, skipping it.\n", | ||
1154 | event->header.type); | 119 | event->header.type); |
1155 | return -1; | 120 | return -1; |
1156 | } | 121 | } |
1157 | 122 | ||
1158 | if (comm_list && !strlist__has_entry(comm_list, thread->comm)) | 123 | if (al.filtered || (hide_unresolved && al.sym == NULL)) |
1159 | return 0; | ||
1160 | |||
1161 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1162 | |||
1163 | if (cpumode == PERF_RECORD_MISC_KERNEL) { | ||
1164 | show = SHOW_KERNEL; | ||
1165 | level = 'k'; | ||
1166 | |||
1167 | dso = kernel_dso; | ||
1168 | |||
1169 | dump_printf(" ...... dso: %s\n", dso->name); | ||
1170 | |||
1171 | } else if (cpumode == PERF_RECORD_MISC_USER) { | ||
1172 | |||
1173 | show = SHOW_USER; | ||
1174 | level = '.'; | ||
1175 | |||
1176 | } else { | ||
1177 | show = SHOW_HV; | ||
1178 | level = 'H'; | ||
1179 | |||
1180 | dso = hypervisor_dso; | ||
1181 | |||
1182 | dump_printf(" ...... dso: [hypervisor]\n"); | ||
1183 | } | ||
1184 | |||
1185 | if (show & show_mask) { | ||
1186 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); | ||
1187 | |||
1188 | if (dso_list && (!dso || !dso->name || | ||
1189 | !strlist__has_entry(dso_list, dso->name))) | ||
1190 | return 0; | ||
1191 | |||
1192 | if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name))) | ||
1193 | return 0; | ||
1194 | |||
1195 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { | ||
1196 | eprintf("problem incrementing symbol count, skipping event\n"); | ||
1197 | return -1; | ||
1198 | } | ||
1199 | } | ||
1200 | total += period; | ||
1201 | |||
1202 | return 0; | ||
1203 | } | ||
1204 | |||
1205 | static int | ||
1206 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | ||
1207 | { | ||
1208 | struct thread *thread; | ||
1209 | struct map *map = map__new(&event->mmap, cwd, cwdlen); | ||
1210 | |||
1211 | thread = threads__findnew(event->mmap.pid, &threads, &last_match); | ||
1212 | |||
1213 | dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n", | ||
1214 | (void *)(offset + head), | ||
1215 | (void *)(long)(event->header.size), | ||
1216 | event->mmap.pid, | ||
1217 | event->mmap.tid, | ||
1218 | (void *)(long)event->mmap.start, | ||
1219 | (void *)(long)event->mmap.len, | ||
1220 | (void *)(long)event->mmap.pgoff, | ||
1221 | event->mmap.filename); | ||
1222 | |||
1223 | if (thread == NULL || map == NULL) { | ||
1224 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
1225 | return 0; | 124 | return 0; |
1226 | } | ||
1227 | |||
1228 | thread__insert_map(thread, map); | ||
1229 | total_mmap++; | ||
1230 | |||
1231 | return 0; | ||
1232 | } | ||
1233 | |||
1234 | static int | ||
1235 | process_comm_event(event_t *event, unsigned long offset, unsigned long head) | ||
1236 | { | ||
1237 | struct thread *thread; | ||
1238 | |||
1239 | thread = threads__findnew(event->comm.pid, &threads, &last_match); | ||
1240 | 125 | ||
1241 | dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n", | 126 | if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) { |
1242 | (void *)(offset + head), | 127 | pr_debug("problem incrementing symbol count, skipping event\n"); |
1243 | (void *)(long)(event->header.size), | ||
1244 | event->comm.comm, event->comm.pid); | ||
1245 | |||
1246 | if (thread == NULL || | ||
1247 | thread__set_comm_adjust(thread, event->comm.comm)) { | ||
1248 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
1249 | return -1; | 128 | return -1; |
1250 | } | 129 | } |
1251 | total_comm++; | ||
1252 | 130 | ||
131 | session->events_stats.total += data.period; | ||
1253 | return 0; | 132 | return 0; |
1254 | } | 133 | } |
1255 | 134 | ||
1256 | static int | 135 | static int process_read_event(event_t *event, struct perf_session *session __used) |
1257 | process_task_event(event_t *event, unsigned long offset, unsigned long head) | ||
1258 | { | ||
1259 | struct thread *thread; | ||
1260 | struct thread *parent; | ||
1261 | |||
1262 | thread = threads__findnew(event->fork.pid, &threads, &last_match); | ||
1263 | parent = threads__findnew(event->fork.ppid, &threads, &last_match); | ||
1264 | |||
1265 | dump_printf("%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n", | ||
1266 | (void *)(offset + head), | ||
1267 | (void *)(long)(event->header.size), | ||
1268 | event->header.type == PERF_RECORD_FORK ? "FORK" : "EXIT", | ||
1269 | event->fork.pid, event->fork.tid, | ||
1270 | event->fork.ppid, event->fork.ptid); | ||
1271 | |||
1272 | /* | ||
1273 | * A thread clone will have the same PID for both | ||
1274 | * parent and child. | ||
1275 | */ | ||
1276 | if (thread == parent) | ||
1277 | return 0; | ||
1278 | |||
1279 | if (event->header.type == PERF_RECORD_EXIT) | ||
1280 | return 0; | ||
1281 | |||
1282 | if (!thread || !parent || thread__fork(thread, parent)) { | ||
1283 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
1284 | return -1; | ||
1285 | } | ||
1286 | total_fork++; | ||
1287 | |||
1288 | return 0; | ||
1289 | } | ||
1290 | |||
1291 | static int | ||
1292 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) | ||
1293 | { | ||
1294 | dump_printf("%p [%p]: PERF_RECORD_LOST: id:%Ld: lost:%Ld\n", | ||
1295 | (void *)(offset + head), | ||
1296 | (void *)(long)(event->header.size), | ||
1297 | event->lost.id, | ||
1298 | event->lost.lost); | ||
1299 | |||
1300 | total_lost += event->lost.lost; | ||
1301 | |||
1302 | return 0; | ||
1303 | } | ||
1304 | |||
1305 | static int | ||
1306 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | ||
1307 | { | 136 | { |
1308 | struct perf_event_attr *attr; | 137 | struct perf_event_attr *attr; |
1309 | 138 | ||
1310 | attr = perf_header__find_attr(event->read.id, header); | 139 | attr = perf_header__find_attr(event->read.id, &session->header); |
1311 | 140 | ||
1312 | if (show_threads) { | 141 | if (show_threads) { |
1313 | const char *name = attr ? __event_name(attr->type, attr->config) | 142 | const char *name = attr ? __event_name(attr->type, attr->config) |
@@ -1319,248 +148,117 @@ process_read_event(event_t *event, unsigned long offset, unsigned long head) | |||
1319 | event->read.value); | 148 | event->read.value); |
1320 | } | 149 | } |
1321 | 150 | ||
1322 | dump_printf("%p [%p]: PERF_RECORD_READ: %d %d %s %Lu\n", | 151 | dump_printf(": %d %d %s %Lu\n", event->read.pid, event->read.tid, |
1323 | (void *)(offset + head), | 152 | attr ? __event_name(attr->type, attr->config) : "FAIL", |
1324 | (void *)(long)(event->header.size), | 153 | event->read.value); |
1325 | event->read.pid, | ||
1326 | event->read.tid, | ||
1327 | attr ? __event_name(attr->type, attr->config) | ||
1328 | : "FAIL", | ||
1329 | event->read.value); | ||
1330 | |||
1331 | return 0; | ||
1332 | } | ||
1333 | |||
1334 | static int | ||
1335 | process_event(event_t *event, unsigned long offset, unsigned long head) | ||
1336 | { | ||
1337 | trace_event(event); | ||
1338 | |||
1339 | switch (event->header.type) { | ||
1340 | case PERF_RECORD_SAMPLE: | ||
1341 | return process_sample_event(event, offset, head); | ||
1342 | |||
1343 | case PERF_RECORD_MMAP: | ||
1344 | return process_mmap_event(event, offset, head); | ||
1345 | |||
1346 | case PERF_RECORD_COMM: | ||
1347 | return process_comm_event(event, offset, head); | ||
1348 | |||
1349 | case PERF_RECORD_FORK: | ||
1350 | case PERF_RECORD_EXIT: | ||
1351 | return process_task_event(event, offset, head); | ||
1352 | |||
1353 | case PERF_RECORD_LOST: | ||
1354 | return process_lost_event(event, offset, head); | ||
1355 | |||
1356 | case PERF_RECORD_READ: | ||
1357 | return process_read_event(event, offset, head); | ||
1358 | |||
1359 | /* | ||
1360 | * We dont process them right now but they are fine: | ||
1361 | */ | ||
1362 | |||
1363 | case PERF_RECORD_THROTTLE: | ||
1364 | case PERF_RECORD_UNTHROTTLE: | ||
1365 | return 0; | ||
1366 | |||
1367 | default: | ||
1368 | return -1; | ||
1369 | } | ||
1370 | 154 | ||
1371 | return 0; | 155 | return 0; |
1372 | } | 156 | } |
1373 | 157 | ||
1374 | static int __cmd_report(void) | 158 | static int perf_session__setup_sample_type(struct perf_session *self) |
1375 | { | 159 | { |
1376 | int ret, rc = EXIT_FAILURE; | 160 | if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { |
1377 | unsigned long offset = 0; | ||
1378 | unsigned long head, shift; | ||
1379 | struct stat input_stat; | ||
1380 | struct thread *idle; | ||
1381 | event_t *event; | ||
1382 | uint32_t size; | ||
1383 | char *buf; | ||
1384 | |||
1385 | idle = register_idle_thread(&threads, &last_match); | ||
1386 | thread__comm_adjust(idle); | ||
1387 | |||
1388 | if (show_threads) | ||
1389 | perf_read_values_init(&show_threads_values); | ||
1390 | |||
1391 | input = open(input_name, O_RDONLY); | ||
1392 | if (input < 0) { | ||
1393 | fprintf(stderr, " failed to open file: %s", input_name); | ||
1394 | if (!strcmp(input_name, "perf.data")) | ||
1395 | fprintf(stderr, " (try 'perf record' first)"); | ||
1396 | fprintf(stderr, "\n"); | ||
1397 | exit(-1); | ||
1398 | } | ||
1399 | |||
1400 | ret = fstat(input, &input_stat); | ||
1401 | if (ret < 0) { | ||
1402 | perror("failed to stat file"); | ||
1403 | exit(-1); | ||
1404 | } | ||
1405 | |||
1406 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
1407 | fprintf(stderr, "file: %s not owned by current user or root\n", input_name); | ||
1408 | exit(-1); | ||
1409 | } | ||
1410 | |||
1411 | if (!input_stat.st_size) { | ||
1412 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | ||
1413 | exit(0); | ||
1414 | } | ||
1415 | |||
1416 | header = perf_header__read(input); | ||
1417 | head = header->data_offset; | ||
1418 | |||
1419 | sample_type = perf_header__sample_type(header); | ||
1420 | |||
1421 | if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { | ||
1422 | if (sort__has_parent) { | 161 | if (sort__has_parent) { |
1423 | fprintf(stderr, "selected --sort parent, but no" | 162 | fprintf(stderr, "selected --sort parent, but no" |
1424 | " callchain data. Did you call" | 163 | " callchain data. Did you call" |
1425 | " perf record without -g?\n"); | 164 | " perf record without -g?\n"); |
1426 | exit(-1); | 165 | return -EINVAL; |
1427 | } | 166 | } |
1428 | if (callchain) { | 167 | if (symbol_conf.use_callchain) { |
1429 | fprintf(stderr, "selected -g but no callchain data." | 168 | fprintf(stderr, "selected -g but no callchain data." |
1430 | " Did you call perf record without" | 169 | " Did you call perf record without" |
1431 | " -g?\n"); | 170 | " -g?\n"); |
1432 | exit(-1); | 171 | return -1; |
1433 | } | 172 | } |
1434 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { | 173 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
1435 | callchain = 1; | 174 | !symbol_conf.use_callchain) { |
175 | symbol_conf.use_callchain = true; | ||
1436 | if (register_callchain_param(&callchain_param) < 0) { | 176 | if (register_callchain_param(&callchain_param) < 0) { |
1437 | fprintf(stderr, "Can't register callchain" | 177 | fprintf(stderr, "Can't register callchain" |
1438 | " params\n"); | 178 | " params\n"); |
1439 | exit(-1); | 179 | return -EINVAL; |
1440 | } | 180 | } |
1441 | } | 181 | } |
1442 | 182 | ||
1443 | if (load_kernel() < 0) { | 183 | return 0; |
1444 | perror("failed to load kernel symbols"); | 184 | } |
1445 | return EXIT_FAILURE; | ||
1446 | } | ||
1447 | |||
1448 | if (!full_paths) { | ||
1449 | if (getcwd(__cwd, sizeof(__cwd)) == NULL) { | ||
1450 | perror("failed to get the current directory"); | ||
1451 | return EXIT_FAILURE; | ||
1452 | } | ||
1453 | cwdlen = strlen(cwd); | ||
1454 | } else { | ||
1455 | cwd = NULL; | ||
1456 | cwdlen = 0; | ||
1457 | } | ||
1458 | |||
1459 | shift = page_size * (head / page_size); | ||
1460 | offset += shift; | ||
1461 | head -= shift; | ||
1462 | |||
1463 | remap: | ||
1464 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | ||
1465 | MAP_SHARED, input, offset); | ||
1466 | if (buf == MAP_FAILED) { | ||
1467 | perror("failed to mmap file"); | ||
1468 | exit(-1); | ||
1469 | } | ||
1470 | |||
1471 | more: | ||
1472 | event = (event_t *)(buf + head); | ||
1473 | |||
1474 | size = event->header.size; | ||
1475 | if (!size) | ||
1476 | size = 8; | ||
1477 | |||
1478 | if (head + event->header.size >= page_size * mmap_window) { | ||
1479 | int munmap_ret; | ||
1480 | |||
1481 | shift = page_size * (head / page_size); | ||
1482 | |||
1483 | munmap_ret = munmap(buf, page_size * mmap_window); | ||
1484 | assert(munmap_ret == 0); | ||
1485 | |||
1486 | offset += shift; | ||
1487 | head -= shift; | ||
1488 | goto remap; | ||
1489 | } | ||
1490 | |||
1491 | size = event->header.size; | ||
1492 | 185 | ||
1493 | dump_printf("\n%p [%p]: event: %d\n", | 186 | static struct perf_event_ops event_ops = { |
1494 | (void *)(offset + head), | 187 | .sample = process_sample_event, |
1495 | (void *)(long)event->header.size, | 188 | .mmap = event__process_mmap, |
1496 | event->header.type); | 189 | .comm = event__process_comm, |
190 | .exit = event__process_task, | ||
191 | .fork = event__process_task, | ||
192 | .lost = event__process_lost, | ||
193 | .read = process_read_event, | ||
194 | }; | ||
1497 | 195 | ||
1498 | if (!size || process_event(event, offset, head) < 0) { | 196 | static int __cmd_report(void) |
197 | { | ||
198 | int ret = -EINVAL; | ||
199 | struct perf_session *session; | ||
1499 | 200 | ||
1500 | dump_printf("%p [%p]: skipping unknown header type: %d\n", | 201 | session = perf_session__new(input_name, O_RDONLY, force); |
1501 | (void *)(offset + head), | 202 | if (session == NULL) |
1502 | (void *)(long)(event->header.size), | 203 | return -ENOMEM; |
1503 | event->header.type); | ||
1504 | 204 | ||
1505 | total_unknown++; | 205 | if (show_threads) |
206 | perf_read_values_init(&show_threads_values); | ||
1506 | 207 | ||
1507 | /* | 208 | ret = perf_session__setup_sample_type(session); |
1508 | * assume we lost track of the stream, check alignment, and | 209 | if (ret) |
1509 | * increment a single u64 in the hope to catch on again 'soon'. | 210 | goto out_delete; |
1510 | */ | ||
1511 | 211 | ||
1512 | if (unlikely(head & 7)) | 212 | ret = perf_session__process_events(session, &event_ops); |
1513 | head &= ~7ULL; | 213 | if (ret) |
214 | goto out_delete; | ||
1514 | 215 | ||
1515 | size = 8; | 216 | if (dump_trace) { |
217 | event__print_totals(); | ||
218 | goto out_delete; | ||
1516 | } | 219 | } |
1517 | 220 | ||
1518 | head += size; | 221 | if (verbose > 3) |
1519 | 222 | perf_session__fprintf(session, stdout); | |
1520 | if (offset + head >= header->data_offset + header->data_size) | ||
1521 | goto done; | ||
1522 | 223 | ||
1523 | if (offset + head < (unsigned long)input_stat.st_size) | 224 | if (verbose > 2) |
1524 | goto more; | ||
1525 | |||
1526 | done: | ||
1527 | rc = EXIT_SUCCESS; | ||
1528 | close(input); | ||
1529 | |||
1530 | dump_printf(" IP events: %10ld\n", total); | ||
1531 | dump_printf(" mmap events: %10ld\n", total_mmap); | ||
1532 | dump_printf(" comm events: %10ld\n", total_comm); | ||
1533 | dump_printf(" fork events: %10ld\n", total_fork); | ||
1534 | dump_printf(" lost events: %10ld\n", total_lost); | ||
1535 | dump_printf(" unknown events: %10ld\n", total_unknown); | ||
1536 | |||
1537 | if (dump_trace) | ||
1538 | return 0; | ||
1539 | |||
1540 | if (verbose >= 3) | ||
1541 | threads__fprintf(stdout, &threads); | ||
1542 | |||
1543 | if (verbose >= 2) | ||
1544 | dsos__fprintf(stdout); | 225 | dsos__fprintf(stdout); |
1545 | 226 | ||
1546 | collapse__resort(); | 227 | perf_session__collapse_resort(session); |
1547 | output__resort(total); | 228 | perf_session__output_resort(session, session->events_stats.total); |
1548 | output__fprintf(stdout, total); | 229 | fprintf(stdout, "# Samples: %Ld\n#\n", session->events_stats.total); |
230 | perf_session__fprintf_hists(session, NULL, false, stdout); | ||
231 | if (sort_order == default_sort_order && | ||
232 | parent_pattern == default_parent_pattern) | ||
233 | fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); | ||
1549 | 234 | ||
1550 | if (show_threads) | 235 | if (show_threads) { |
236 | bool raw_printing_style = !strcmp(pretty_printing_style, "raw"); | ||
237 | perf_read_values_display(stdout, &show_threads_values, | ||
238 | raw_printing_style); | ||
1551 | perf_read_values_destroy(&show_threads_values); | 239 | perf_read_values_destroy(&show_threads_values); |
1552 | 240 | } | |
1553 | return rc; | 241 | out_delete: |
242 | perf_session__delete(session); | ||
243 | return ret; | ||
1554 | } | 244 | } |
1555 | 245 | ||
1556 | static int | 246 | static int |
1557 | parse_callchain_opt(const struct option *opt __used, const char *arg, | 247 | parse_callchain_opt(const struct option *opt __used, const char *arg, |
1558 | int unset __used) | 248 | int unset) |
1559 | { | 249 | { |
1560 | char *tok; | 250 | char *tok; |
1561 | char *endptr; | 251 | char *endptr; |
1562 | 252 | ||
1563 | callchain = 1; | 253 | /* |
254 | * --no-call-graph | ||
255 | */ | ||
256 | if (unset) { | ||
257 | dont_use_callchains = true; | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | symbol_conf.use_callchain = true; | ||
1564 | 262 | ||
1565 | if (!arg) | 263 | if (!arg) |
1566 | return 0; | 264 | return 0; |
@@ -1581,7 +279,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
1581 | 279 | ||
1582 | else if (!strncmp(tok, "none", strlen(arg))) { | 280 | else if (!strncmp(tok, "none", strlen(arg))) { |
1583 | callchain_param.mode = CHAIN_NONE; | 281 | callchain_param.mode = CHAIN_NONE; |
1584 | callchain = 0; | 282 | symbol_conf.use_callchain = false; |
1585 | 283 | ||
1586 | return 0; | 284 | return 0; |
1587 | } | 285 | } |
@@ -1618,11 +316,12 @@ static const struct option options[] = { | |||
1618 | "be more verbose (show symbol address, etc)"), | 316 | "be more verbose (show symbol address, etc)"), |
1619 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 317 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1620 | "dump raw trace in ASCII"), | 318 | "dump raw trace in ASCII"), |
1621 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), | 319 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
320 | "file", "vmlinux pathname"), | ||
1622 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 321 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
1623 | OPT_BOOLEAN('m', "modules", &modules, | 322 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, |
1624 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 323 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
1625 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | 324 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
1626 | "Show a column with the number of samples"), | 325 | "Show a column with the number of samples"), |
1627 | OPT_BOOLEAN('T', "threads", &show_threads, | 326 | OPT_BOOLEAN('T', "threads", &show_threads, |
1628 | "Show per-thread event counters"), | 327 | "Show per-thread event counters"), |
@@ -1630,79 +329,48 @@ static const struct option options[] = { | |||
1630 | "pretty printing style key: normal raw"), | 329 | "pretty printing style key: normal raw"), |
1631 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 330 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
1632 | "sort by key(s): pid, comm, dso, symbol, parent"), | 331 | "sort by key(s): pid, comm, dso, symbol, parent"), |
1633 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 332 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, |
1634 | "Don't shorten the pathnames taking into account the cwd"), | 333 | "Don't shorten the pathnames taking into account the cwd"), |
1635 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 334 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
1636 | "regex filter to identify parent, see: '--sort parent'"), | 335 | "regex filter to identify parent, see: '--sort parent'"), |
1637 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 336 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
1638 | "Only display entries with parent-match"), | 337 | "Only display entries with parent-match"), |
1639 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", | 338 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", |
1640 | "Display callchains using output_type and min percent threshold. " | 339 | "Display callchains using output_type and min percent threshold. " |
1641 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), | 340 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), |
1642 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", | 341 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
1643 | "only consider symbols in these dsos"), | 342 | "only consider symbols in these dsos"), |
1644 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", | 343 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
1645 | "only consider symbols in these comms"), | 344 | "only consider symbols in these comms"), |
1646 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", | 345 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
1647 | "only consider these symbols"), | 346 | "only consider these symbols"), |
1648 | OPT_STRING('w', "column-widths", &col_width_list_str, | 347 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, |
1649 | "width[,width...]", | 348 | "width[,width...]", |
1650 | "don't try to adjust column width, use these fixed values"), | 349 | "don't try to adjust column width, use these fixed values"), |
1651 | OPT_STRING('t', "field-separator", &field_sep, "separator", | 350 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", |
1652 | "separator for columns, no spaces will be added between " | 351 | "separator for columns, no spaces will be added between " |
1653 | "columns '.' is reserved."), | 352 | "columns '.' is reserved."), |
353 | OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved, | ||
354 | "Only display entries resolved to a symbol"), | ||
1654 | OPT_END() | 355 | OPT_END() |
1655 | }; | 356 | }; |
1656 | 357 | ||
1657 | static void setup_sorting(void) | ||
1658 | { | ||
1659 | char *tmp, *tok, *str = strdup(sort_order); | ||
1660 | |||
1661 | for (tok = strtok_r(str, ", ", &tmp); | ||
1662 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
1663 | if (sort_dimension__add(tok) < 0) { | ||
1664 | error("Unknown --sort key: `%s'", tok); | ||
1665 | usage_with_options(report_usage, options); | ||
1666 | } | ||
1667 | } | ||
1668 | |||
1669 | free(str); | ||
1670 | } | ||
1671 | |||
1672 | static void setup_list(struct strlist **list, const char *list_str, | ||
1673 | struct sort_entry *se, const char *list_name, | ||
1674 | FILE *fp) | ||
1675 | { | ||
1676 | if (list_str) { | ||
1677 | *list = strlist__new(true, list_str); | ||
1678 | if (!*list) { | ||
1679 | fprintf(stderr, "problems parsing %s list\n", | ||
1680 | list_name); | ||
1681 | exit(129); | ||
1682 | } | ||
1683 | if (strlist__nr_entries(*list) == 1) { | ||
1684 | fprintf(fp, "# %s: %s\n", list_name, | ||
1685 | strlist__entry(*list, 0)->s); | ||
1686 | se->elide = true; | ||
1687 | } | ||
1688 | } | ||
1689 | } | ||
1690 | |||
1691 | int cmd_report(int argc, const char **argv, const char *prefix __used) | 358 | int cmd_report(int argc, const char **argv, const char *prefix __used) |
1692 | { | 359 | { |
1693 | symbol__init(); | 360 | argc = parse_options(argc, argv, options, report_usage, 0); |
1694 | 361 | ||
1695 | page_size = getpagesize(); | 362 | setup_pager(); |
1696 | 363 | ||
1697 | argc = parse_options(argc, argv, options, report_usage, 0); | 364 | if (symbol__init() < 0) |
365 | return -1; | ||
1698 | 366 | ||
1699 | setup_sorting(); | 367 | setup_sorting(report_usage, options); |
1700 | 368 | ||
1701 | if (parent_pattern != default_parent_pattern) { | 369 | if (parent_pattern != default_parent_pattern) { |
1702 | sort_dimension__add("parent"); | 370 | sort_dimension__add("parent"); |
1703 | sort_parent.elide = 1; | 371 | sort_parent.elide = 1; |
1704 | } else | 372 | } else |
1705 | exclude_other = 0; | 373 | symbol_conf.exclude_other = false; |
1706 | 374 | ||
1707 | /* | 375 | /* |
1708 | * Any (unrecognized) arguments left? | 376 | * Any (unrecognized) arguments left? |
@@ -1710,17 +378,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
1710 | if (argc) | 378 | if (argc) |
1711 | usage_with_options(report_usage, options); | 379 | usage_with_options(report_usage, options); |
1712 | 380 | ||
1713 | setup_pager(); | 381 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); |
1714 | 382 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | |
1715 | setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); | 383 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); |
1716 | setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); | ||
1717 | setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); | ||
1718 | |||
1719 | if (field_sep && *field_sep == '.') { | ||
1720 | fputs("'.' is the only non valid --field-separator argument\n", | ||
1721 | stderr); | ||
1722 | exit(129); | ||
1723 | } | ||
1724 | 384 | ||
1725 | return __cmd_report(); | 385 | return __cmd_report(); |
1726 | } | 386 | } |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index ce2d5be4f30e..4f5a03e43444 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -6,13 +6,13 @@ | |||
6 | #include "util/symbol.h" | 6 | #include "util/symbol.h" |
7 | #include "util/thread.h" | 7 | #include "util/thread.h" |
8 | #include "util/header.h" | 8 | #include "util/header.h" |
9 | #include "util/session.h" | ||
9 | 10 | ||
10 | #include "util/parse-options.h" | 11 | #include "util/parse-options.h" |
11 | #include "util/trace-event.h" | 12 | #include "util/trace-event.h" |
12 | 13 | ||
13 | #include "util/debug.h" | 14 | #include "util/debug.h" |
14 | 15 | ||
15 | #include <sys/types.h> | ||
16 | #include <sys/prctl.h> | 16 | #include <sys/prctl.h> |
17 | 17 | ||
18 | #include <semaphore.h> | 18 | #include <semaphore.h> |
@@ -20,26 +20,15 @@ | |||
20 | #include <math.h> | 20 | #include <math.h> |
21 | 21 | ||
22 | static char const *input_name = "perf.data"; | 22 | static char const *input_name = "perf.data"; |
23 | static int input; | ||
24 | static unsigned long page_size; | ||
25 | static unsigned long mmap_window = 32; | ||
26 | |||
27 | static unsigned long total_comm = 0; | ||
28 | |||
29 | static struct rb_root threads; | ||
30 | static struct thread *last_match; | ||
31 | |||
32 | static struct perf_header *header; | ||
33 | static u64 sample_type; | ||
34 | 23 | ||
35 | static char default_sort_order[] = "avg, max, switch, runtime"; | 24 | static char default_sort_order[] = "avg, max, switch, runtime"; |
36 | static char *sort_order = default_sort_order; | 25 | static char *sort_order = default_sort_order; |
37 | 26 | ||
27 | static int profile_cpu = -1; | ||
28 | |||
38 | #define PR_SET_NAME 15 /* Set process name */ | 29 | #define PR_SET_NAME 15 /* Set process name */ |
39 | #define MAX_CPUS 4096 | 30 | #define MAX_CPUS 4096 |
40 | 31 | ||
41 | #define BUG_ON(x) assert(!(x)) | ||
42 | |||
43 | static u64 run_measurement_overhead; | 32 | static u64 run_measurement_overhead; |
44 | static u64 sleep_measurement_overhead; | 33 | static u64 sleep_measurement_overhead; |
45 | 34 | ||
@@ -74,6 +63,7 @@ enum sched_event_type { | |||
74 | SCHED_EVENT_RUN, | 63 | SCHED_EVENT_RUN, |
75 | SCHED_EVENT_SLEEP, | 64 | SCHED_EVENT_SLEEP, |
76 | SCHED_EVENT_WAKEUP, | 65 | SCHED_EVENT_WAKEUP, |
66 | SCHED_EVENT_MIGRATION, | ||
77 | }; | 67 | }; |
78 | 68 | ||
79 | struct sched_atom { | 69 | struct sched_atom { |
@@ -147,6 +137,7 @@ struct work_atoms { | |||
147 | struct thread *thread; | 137 | struct thread *thread; |
148 | struct rb_node node; | 138 | struct rb_node node; |
149 | u64 max_lat; | 139 | u64 max_lat; |
140 | u64 max_lat_at; | ||
150 | u64 total_lat; | 141 | u64 total_lat; |
151 | u64 nb_atoms; | 142 | u64 nb_atoms; |
152 | u64 total_runtime; | 143 | u64 total_runtime; |
@@ -226,7 +217,7 @@ static void calibrate_sleep_measurement_overhead(void) | |||
226 | static struct sched_atom * | 217 | static struct sched_atom * |
227 | get_new_event(struct task_desc *task, u64 timestamp) | 218 | get_new_event(struct task_desc *task, u64 timestamp) |
228 | { | 219 | { |
229 | struct sched_atom *event = calloc(1, sizeof(*event)); | 220 | struct sched_atom *event = zalloc(sizeof(*event)); |
230 | unsigned long idx = task->nr_events; | 221 | unsigned long idx = task->nr_events; |
231 | size_t size; | 222 | size_t size; |
232 | 223 | ||
@@ -294,7 +285,7 @@ add_sched_event_wakeup(struct task_desc *task, u64 timestamp, | |||
294 | return; | 285 | return; |
295 | } | 286 | } |
296 | 287 | ||
297 | wakee_event->wait_sem = calloc(1, sizeof(*wakee_event->wait_sem)); | 288 | wakee_event->wait_sem = zalloc(sizeof(*wakee_event->wait_sem)); |
298 | sem_init(wakee_event->wait_sem, 0, 0); | 289 | sem_init(wakee_event->wait_sem, 0, 0); |
299 | wakee_event->specific_wait = 1; | 290 | wakee_event->specific_wait = 1; |
300 | event->wait_sem = wakee_event->wait_sem; | 291 | event->wait_sem = wakee_event->wait_sem; |
@@ -324,7 +315,7 @@ static struct task_desc *register_pid(unsigned long pid, const char *comm) | |||
324 | if (task) | 315 | if (task) |
325 | return task; | 316 | return task; |
326 | 317 | ||
327 | task = calloc(1, sizeof(*task)); | 318 | task = zalloc(sizeof(*task)); |
328 | task->pid = pid; | 319 | task->pid = pid; |
329 | task->nr = nr_tasks; | 320 | task->nr = nr_tasks; |
330 | strcpy(task->comm, comm); | 321 | strcpy(task->comm, comm); |
@@ -398,6 +389,8 @@ process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom) | |||
398 | ret = sem_post(atom->wait_sem); | 389 | ret = sem_post(atom->wait_sem); |
399 | BUG_ON(ret); | 390 | BUG_ON(ret); |
400 | break; | 391 | break; |
392 | case SCHED_EVENT_MIGRATION: | ||
393 | break; | ||
401 | default: | 394 | default: |
402 | BUG_ON(1); | 395 | BUG_ON(1); |
403 | } | 396 | } |
@@ -418,34 +411,33 @@ static u64 get_cpu_usage_nsec_parent(void) | |||
418 | return sum; | 411 | return sum; |
419 | } | 412 | } |
420 | 413 | ||
421 | static u64 get_cpu_usage_nsec_self(void) | 414 | static int self_open_counters(void) |
422 | { | 415 | { |
423 | char filename [] = "/proc/1234567890/sched"; | 416 | struct perf_event_attr attr; |
424 | unsigned long msecs, nsecs; | 417 | int fd; |
425 | char *line = NULL; | ||
426 | u64 total = 0; | ||
427 | size_t len = 0; | ||
428 | ssize_t chars; | ||
429 | FILE *file; | ||
430 | int ret; | ||
431 | 418 | ||
432 | sprintf(filename, "/proc/%d/sched", getpid()); | 419 | memset(&attr, 0, sizeof(attr)); |
433 | file = fopen(filename, "r"); | ||
434 | BUG_ON(!file); | ||
435 | 420 | ||
436 | while ((chars = getline(&line, &len, file)) != -1) { | 421 | attr.type = PERF_TYPE_SOFTWARE; |
437 | ret = sscanf(line, "se.sum_exec_runtime : %ld.%06ld\n", | 422 | attr.config = PERF_COUNT_SW_TASK_CLOCK; |
438 | &msecs, &nsecs); | 423 | |
439 | if (ret == 2) { | 424 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); |
440 | total = msecs*1e6 + nsecs; | ||
441 | break; | ||
442 | } | ||
443 | } | ||
444 | if (line) | ||
445 | free(line); | ||
446 | fclose(file); | ||
447 | 425 | ||
448 | return total; | 426 | if (fd < 0) |
427 | die("Error: sys_perf_event_open() syscall returned" | ||
428 | "with %d (%s)\n", fd, strerror(errno)); | ||
429 | return fd; | ||
430 | } | ||
431 | |||
432 | static u64 get_cpu_usage_nsec_self(int fd) | ||
433 | { | ||
434 | u64 runtime; | ||
435 | int ret; | ||
436 | |||
437 | ret = read(fd, &runtime, sizeof(runtime)); | ||
438 | BUG_ON(ret != sizeof(runtime)); | ||
439 | |||
440 | return runtime; | ||
449 | } | 441 | } |
450 | 442 | ||
451 | static void *thread_func(void *ctx) | 443 | static void *thread_func(void *ctx) |
@@ -454,9 +446,11 @@ static void *thread_func(void *ctx) | |||
454 | u64 cpu_usage_0, cpu_usage_1; | 446 | u64 cpu_usage_0, cpu_usage_1; |
455 | unsigned long i, ret; | 447 | unsigned long i, ret; |
456 | char comm2[22]; | 448 | char comm2[22]; |
449 | int fd; | ||
457 | 450 | ||
458 | sprintf(comm2, ":%s", this_task->comm); | 451 | sprintf(comm2, ":%s", this_task->comm); |
459 | prctl(PR_SET_NAME, comm2); | 452 | prctl(PR_SET_NAME, comm2); |
453 | fd = self_open_counters(); | ||
460 | 454 | ||
461 | again: | 455 | again: |
462 | ret = sem_post(&this_task->ready_for_work); | 456 | ret = sem_post(&this_task->ready_for_work); |
@@ -466,16 +460,15 @@ again: | |||
466 | ret = pthread_mutex_unlock(&start_work_mutex); | 460 | ret = pthread_mutex_unlock(&start_work_mutex); |
467 | BUG_ON(ret); | 461 | BUG_ON(ret); |
468 | 462 | ||
469 | cpu_usage_0 = get_cpu_usage_nsec_self(); | 463 | cpu_usage_0 = get_cpu_usage_nsec_self(fd); |
470 | 464 | ||
471 | for (i = 0; i < this_task->nr_events; i++) { | 465 | for (i = 0; i < this_task->nr_events; i++) { |
472 | this_task->curr_event = i; | 466 | this_task->curr_event = i; |
473 | process_sched_event(this_task, this_task->atoms[i]); | 467 | process_sched_event(this_task, this_task->atoms[i]); |
474 | } | 468 | } |
475 | 469 | ||
476 | cpu_usage_1 = get_cpu_usage_nsec_self(); | 470 | cpu_usage_1 = get_cpu_usage_nsec_self(fd); |
477 | this_task->cpu_usage = cpu_usage_1 - cpu_usage_0; | 471 | this_task->cpu_usage = cpu_usage_1 - cpu_usage_0; |
478 | |||
479 | ret = sem_post(&this_task->work_done_sem); | 472 | ret = sem_post(&this_task->work_done_sem); |
480 | BUG_ON(ret); | 473 | BUG_ON(ret); |
481 | 474 | ||
@@ -632,34 +625,6 @@ static void test_calibrations(void) | |||
632 | printf("the sleep test took %Ld nsecs\n", T1-T0); | 625 | printf("the sleep test took %Ld nsecs\n", T1-T0); |
633 | } | 626 | } |
634 | 627 | ||
635 | static int | ||
636 | process_comm_event(event_t *event, unsigned long offset, unsigned long head) | ||
637 | { | ||
638 | struct thread *thread; | ||
639 | |||
640 | thread = threads__findnew(event->comm.pid, &threads, &last_match); | ||
641 | |||
642 | dump_printf("%p [%p]: perf_event_comm: %s:%d\n", | ||
643 | (void *)(offset + head), | ||
644 | (void *)(long)(event->header.size), | ||
645 | event->comm.comm, event->comm.pid); | ||
646 | |||
647 | if (thread == NULL || | ||
648 | thread__set_comm(thread, event->comm.comm)) { | ||
649 | dump_printf("problem processing perf_event_comm, skipping event.\n"); | ||
650 | return -1; | ||
651 | } | ||
652 | total_comm++; | ||
653 | |||
654 | return 0; | ||
655 | } | ||
656 | |||
657 | |||
658 | struct raw_event_sample { | ||
659 | u32 size; | ||
660 | char data[0]; | ||
661 | }; | ||
662 | |||
663 | #define FILL_FIELD(ptr, field, event, data) \ | 628 | #define FILL_FIELD(ptr, field, event, data) \ |
664 | ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data) | 629 | ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data) |
665 | 630 | ||
@@ -745,20 +710,39 @@ struct trace_fork_event { | |||
745 | u32 child_pid; | 710 | u32 child_pid; |
746 | }; | 711 | }; |
747 | 712 | ||
713 | struct trace_migrate_task_event { | ||
714 | u32 size; | ||
715 | |||
716 | u16 common_type; | ||
717 | u8 common_flags; | ||
718 | u8 common_preempt_count; | ||
719 | u32 common_pid; | ||
720 | u32 common_tgid; | ||
721 | |||
722 | char comm[16]; | ||
723 | u32 pid; | ||
724 | |||
725 | u32 prio; | ||
726 | u32 cpu; | ||
727 | }; | ||
728 | |||
748 | struct trace_sched_handler { | 729 | struct trace_sched_handler { |
749 | void (*switch_event)(struct trace_switch_event *, | 730 | void (*switch_event)(struct trace_switch_event *, |
731 | struct perf_session *, | ||
750 | struct event *, | 732 | struct event *, |
751 | int cpu, | 733 | int cpu, |
752 | u64 timestamp, | 734 | u64 timestamp, |
753 | struct thread *thread); | 735 | struct thread *thread); |
754 | 736 | ||
755 | void (*runtime_event)(struct trace_runtime_event *, | 737 | void (*runtime_event)(struct trace_runtime_event *, |
738 | struct perf_session *, | ||
756 | struct event *, | 739 | struct event *, |
757 | int cpu, | 740 | int cpu, |
758 | u64 timestamp, | 741 | u64 timestamp, |
759 | struct thread *thread); | 742 | struct thread *thread); |
760 | 743 | ||
761 | void (*wakeup_event)(struct trace_wakeup_event *, | 744 | void (*wakeup_event)(struct trace_wakeup_event *, |
745 | struct perf_session *, | ||
762 | struct event *, | 746 | struct event *, |
763 | int cpu, | 747 | int cpu, |
764 | u64 timestamp, | 748 | u64 timestamp, |
@@ -769,11 +753,19 @@ struct trace_sched_handler { | |||
769 | int cpu, | 753 | int cpu, |
770 | u64 timestamp, | 754 | u64 timestamp, |
771 | struct thread *thread); | 755 | struct thread *thread); |
756 | |||
757 | void (*migrate_task_event)(struct trace_migrate_task_event *, | ||
758 | struct perf_session *session, | ||
759 | struct event *, | ||
760 | int cpu, | ||
761 | u64 timestamp, | ||
762 | struct thread *thread); | ||
772 | }; | 763 | }; |
773 | 764 | ||
774 | 765 | ||
775 | static void | 766 | static void |
776 | replay_wakeup_event(struct trace_wakeup_event *wakeup_event, | 767 | replay_wakeup_event(struct trace_wakeup_event *wakeup_event, |
768 | struct perf_session *session __used, | ||
777 | struct event *event, | 769 | struct event *event, |
778 | int cpu __used, | 770 | int cpu __used, |
779 | u64 timestamp __used, | 771 | u64 timestamp __used, |
@@ -800,6 +792,7 @@ static u64 cpu_last_switched[MAX_CPUS]; | |||
800 | 792 | ||
801 | static void | 793 | static void |
802 | replay_switch_event(struct trace_switch_event *switch_event, | 794 | replay_switch_event(struct trace_switch_event *switch_event, |
795 | struct perf_session *session __used, | ||
803 | struct event *event, | 796 | struct event *event, |
804 | int cpu, | 797 | int cpu, |
805 | u64 timestamp, | 798 | u64 timestamp, |
@@ -941,9 +934,7 @@ __thread_latency_insert(struct rb_root *root, struct work_atoms *data, | |||
941 | 934 | ||
942 | static void thread_atoms_insert(struct thread *thread) | 935 | static void thread_atoms_insert(struct thread *thread) |
943 | { | 936 | { |
944 | struct work_atoms *atoms; | 937 | struct work_atoms *atoms = zalloc(sizeof(*atoms)); |
945 | |||
946 | atoms = calloc(sizeof(*atoms), 1); | ||
947 | if (!atoms) | 938 | if (!atoms) |
948 | die("No memory"); | 939 | die("No memory"); |
949 | 940 | ||
@@ -975,9 +966,7 @@ add_sched_out_event(struct work_atoms *atoms, | |||
975 | char run_state, | 966 | char run_state, |
976 | u64 timestamp) | 967 | u64 timestamp) |
977 | { | 968 | { |
978 | struct work_atom *atom; | 969 | struct work_atom *atom = zalloc(sizeof(*atom)); |
979 | |||
980 | atom = calloc(sizeof(*atom), 1); | ||
981 | if (!atom) | 970 | if (!atom) |
982 | die("Non memory"); | 971 | die("Non memory"); |
983 | 972 | ||
@@ -1028,13 +1017,16 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp) | |||
1028 | 1017 | ||
1029 | delta = atom->sched_in_time - atom->wake_up_time; | 1018 | delta = atom->sched_in_time - atom->wake_up_time; |
1030 | atoms->total_lat += delta; | 1019 | atoms->total_lat += delta; |
1031 | if (delta > atoms->max_lat) | 1020 | if (delta > atoms->max_lat) { |
1032 | atoms->max_lat = delta; | 1021 | atoms->max_lat = delta; |
1022 | atoms->max_lat_at = timestamp; | ||
1023 | } | ||
1033 | atoms->nb_atoms++; | 1024 | atoms->nb_atoms++; |
1034 | } | 1025 | } |
1035 | 1026 | ||
1036 | static void | 1027 | static void |
1037 | latency_switch_event(struct trace_switch_event *switch_event, | 1028 | latency_switch_event(struct trace_switch_event *switch_event, |
1029 | struct perf_session *session, | ||
1038 | struct event *event __used, | 1030 | struct event *event __used, |
1039 | int cpu, | 1031 | int cpu, |
1040 | u64 timestamp, | 1032 | u64 timestamp, |
@@ -1058,8 +1050,8 @@ latency_switch_event(struct trace_switch_event *switch_event, | |||
1058 | die("hm, delta: %Ld < 0 ?\n", delta); | 1050 | die("hm, delta: %Ld < 0 ?\n", delta); |
1059 | 1051 | ||
1060 | 1052 | ||
1061 | sched_out = threads__findnew(switch_event->prev_pid, &threads, &last_match); | 1053 | sched_out = perf_session__findnew(session, switch_event->prev_pid); |
1062 | sched_in = threads__findnew(switch_event->next_pid, &threads, &last_match); | 1054 | sched_in = perf_session__findnew(session, switch_event->next_pid); |
1063 | 1055 | ||
1064 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); | 1056 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); |
1065 | if (!out_events) { | 1057 | if (!out_events) { |
@@ -1087,18 +1079,16 @@ latency_switch_event(struct trace_switch_event *switch_event, | |||
1087 | 1079 | ||
1088 | static void | 1080 | static void |
1089 | latency_runtime_event(struct trace_runtime_event *runtime_event, | 1081 | latency_runtime_event(struct trace_runtime_event *runtime_event, |
1082 | struct perf_session *session, | ||
1090 | struct event *event __used, | 1083 | struct event *event __used, |
1091 | int cpu, | 1084 | int cpu, |
1092 | u64 timestamp, | 1085 | u64 timestamp, |
1093 | struct thread *this_thread __used) | 1086 | struct thread *this_thread __used) |
1094 | { | 1087 | { |
1095 | struct work_atoms *atoms; | 1088 | struct thread *thread = perf_session__findnew(session, runtime_event->pid); |
1096 | struct thread *thread; | 1089 | struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); |
1097 | 1090 | ||
1098 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); | 1091 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); |
1099 | |||
1100 | thread = threads__findnew(runtime_event->pid, &threads, &last_match); | ||
1101 | atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); | ||
1102 | if (!atoms) { | 1092 | if (!atoms) { |
1103 | thread_atoms_insert(thread); | 1093 | thread_atoms_insert(thread); |
1104 | atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); | 1094 | atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); |
@@ -1112,6 +1102,7 @@ latency_runtime_event(struct trace_runtime_event *runtime_event, | |||
1112 | 1102 | ||
1113 | static void | 1103 | static void |
1114 | latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | 1104 | latency_wakeup_event(struct trace_wakeup_event *wakeup_event, |
1105 | struct perf_session *session, | ||
1115 | struct event *__event __used, | 1106 | struct event *__event __used, |
1116 | int cpu __used, | 1107 | int cpu __used, |
1117 | u64 timestamp, | 1108 | u64 timestamp, |
@@ -1125,7 +1116,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | |||
1125 | if (!wakeup_event->success) | 1116 | if (!wakeup_event->success) |
1126 | return; | 1117 | return; |
1127 | 1118 | ||
1128 | wakee = threads__findnew(wakeup_event->pid, &threads, &last_match); | 1119 | wakee = perf_session__findnew(session, wakeup_event->pid); |
1129 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); | 1120 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); |
1130 | if (!atoms) { | 1121 | if (!atoms) { |
1131 | thread_atoms_insert(wakee); | 1122 | thread_atoms_insert(wakee); |
@@ -1139,7 +1130,12 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | |||
1139 | 1130 | ||
1140 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); | 1131 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); |
1141 | 1132 | ||
1142 | if (atom->state != THREAD_SLEEPING) | 1133 | /* |
1134 | * You WILL be missing events if you've recorded only | ||
1135 | * one CPU, or are only looking at only one, so don't | ||
1136 | * make useless noise. | ||
1137 | */ | ||
1138 | if (profile_cpu == -1 && atom->state != THREAD_SLEEPING) | ||
1143 | nr_state_machine_bugs++; | 1139 | nr_state_machine_bugs++; |
1144 | 1140 | ||
1145 | nr_timestamps++; | 1141 | nr_timestamps++; |
@@ -1152,11 +1148,52 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | |||
1152 | atom->wake_up_time = timestamp; | 1148 | atom->wake_up_time = timestamp; |
1153 | } | 1149 | } |
1154 | 1150 | ||
1151 | static void | ||
1152 | latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, | ||
1153 | struct perf_session *session, | ||
1154 | struct event *__event __used, | ||
1155 | int cpu __used, | ||
1156 | u64 timestamp, | ||
1157 | struct thread *thread __used) | ||
1158 | { | ||
1159 | struct work_atoms *atoms; | ||
1160 | struct work_atom *atom; | ||
1161 | struct thread *migrant; | ||
1162 | |||
1163 | /* | ||
1164 | * Only need to worry about migration when profiling one CPU. | ||
1165 | */ | ||
1166 | if (profile_cpu == -1) | ||
1167 | return; | ||
1168 | |||
1169 | migrant = perf_session__findnew(session, migrate_task_event->pid); | ||
1170 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); | ||
1171 | if (!atoms) { | ||
1172 | thread_atoms_insert(migrant); | ||
1173 | register_pid(migrant->pid, migrant->comm); | ||
1174 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); | ||
1175 | if (!atoms) | ||
1176 | die("migration-event: Internal tree error"); | ||
1177 | add_sched_out_event(atoms, 'R', timestamp); | ||
1178 | } | ||
1179 | |||
1180 | BUG_ON(list_empty(&atoms->work_list)); | ||
1181 | |||
1182 | atom = list_entry(atoms->work_list.prev, struct work_atom, list); | ||
1183 | atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp; | ||
1184 | |||
1185 | nr_timestamps++; | ||
1186 | |||
1187 | if (atom->sched_out_time > timestamp) | ||
1188 | nr_unordered_timestamps++; | ||
1189 | } | ||
1190 | |||
1155 | static struct trace_sched_handler lat_ops = { | 1191 | static struct trace_sched_handler lat_ops = { |
1156 | .wakeup_event = latency_wakeup_event, | 1192 | .wakeup_event = latency_wakeup_event, |
1157 | .switch_event = latency_switch_event, | 1193 | .switch_event = latency_switch_event, |
1158 | .runtime_event = latency_runtime_event, | 1194 | .runtime_event = latency_runtime_event, |
1159 | .fork_event = latency_fork_event, | 1195 | .fork_event = latency_fork_event, |
1196 | .migrate_task_event = latency_migrate_task_event, | ||
1160 | }; | 1197 | }; |
1161 | 1198 | ||
1162 | static void output_lat_thread(struct work_atoms *work_list) | 1199 | static void output_lat_thread(struct work_atoms *work_list) |
@@ -1183,10 +1220,11 @@ static void output_lat_thread(struct work_atoms *work_list) | |||
1183 | 1220 | ||
1184 | avg = work_list->total_lat / work_list->nb_atoms; | 1221 | avg = work_list->total_lat / work_list->nb_atoms; |
1185 | 1222 | ||
1186 | printf("|%11.3f ms |%9llu | avg:%9.3f ms | max:%9.3f ms |\n", | 1223 | printf("|%11.3f ms |%9llu | avg:%9.3f ms | max:%9.3f ms | max at: %9.6f s\n", |
1187 | (double)work_list->total_runtime / 1e6, | 1224 | (double)work_list->total_runtime / 1e6, |
1188 | work_list->nb_atoms, (double)avg / 1e6, | 1225 | work_list->nb_atoms, (double)avg / 1e6, |
1189 | (double)work_list->max_lat / 1e6); | 1226 | (double)work_list->max_lat / 1e6, |
1227 | (double)work_list->max_lat_at / 1e9); | ||
1190 | } | 1228 | } |
1191 | 1229 | ||
1192 | static int pid_cmp(struct work_atoms *l, struct work_atoms *r) | 1230 | static int pid_cmp(struct work_atoms *l, struct work_atoms *r) |
@@ -1323,7 +1361,7 @@ static void sort_lat(void) | |||
1323 | static struct trace_sched_handler *trace_handler; | 1361 | static struct trace_sched_handler *trace_handler; |
1324 | 1362 | ||
1325 | static void | 1363 | static void |
1326 | process_sched_wakeup_event(struct raw_event_sample *raw, | 1364 | process_sched_wakeup_event(void *data, struct perf_session *session, |
1327 | struct event *event, | 1365 | struct event *event, |
1328 | int cpu __used, | 1366 | int cpu __used, |
1329 | u64 timestamp __used, | 1367 | u64 timestamp __used, |
@@ -1331,16 +1369,17 @@ process_sched_wakeup_event(struct raw_event_sample *raw, | |||
1331 | { | 1369 | { |
1332 | struct trace_wakeup_event wakeup_event; | 1370 | struct trace_wakeup_event wakeup_event; |
1333 | 1371 | ||
1334 | FILL_COMMON_FIELDS(wakeup_event, event, raw->data); | 1372 | FILL_COMMON_FIELDS(wakeup_event, event, data); |
1335 | 1373 | ||
1336 | FILL_ARRAY(wakeup_event, comm, event, raw->data); | 1374 | FILL_ARRAY(wakeup_event, comm, event, data); |
1337 | FILL_FIELD(wakeup_event, pid, event, raw->data); | 1375 | FILL_FIELD(wakeup_event, pid, event, data); |
1338 | FILL_FIELD(wakeup_event, prio, event, raw->data); | 1376 | FILL_FIELD(wakeup_event, prio, event, data); |
1339 | FILL_FIELD(wakeup_event, success, event, raw->data); | 1377 | FILL_FIELD(wakeup_event, success, event, data); |
1340 | FILL_FIELD(wakeup_event, cpu, event, raw->data); | 1378 | FILL_FIELD(wakeup_event, cpu, event, data); |
1341 | 1379 | ||
1342 | if (trace_handler->wakeup_event) | 1380 | if (trace_handler->wakeup_event) |
1343 | trace_handler->wakeup_event(&wakeup_event, event, cpu, timestamp, thread); | 1381 | trace_handler->wakeup_event(&wakeup_event, session, event, |
1382 | cpu, timestamp, thread); | ||
1344 | } | 1383 | } |
1345 | 1384 | ||
1346 | /* | 1385 | /* |
@@ -1358,6 +1397,7 @@ static char next_shortname2 = '0'; | |||
1358 | 1397 | ||
1359 | static void | 1398 | static void |
1360 | map_switch_event(struct trace_switch_event *switch_event, | 1399 | map_switch_event(struct trace_switch_event *switch_event, |
1400 | struct perf_session *session, | ||
1361 | struct event *event __used, | 1401 | struct event *event __used, |
1362 | int this_cpu, | 1402 | int this_cpu, |
1363 | u64 timestamp, | 1403 | u64 timestamp, |
@@ -1385,8 +1425,8 @@ map_switch_event(struct trace_switch_event *switch_event, | |||
1385 | die("hm, delta: %Ld < 0 ?\n", delta); | 1425 | die("hm, delta: %Ld < 0 ?\n", delta); |
1386 | 1426 | ||
1387 | 1427 | ||
1388 | sched_out = threads__findnew(switch_event->prev_pid, &threads, &last_match); | 1428 | sched_out = perf_session__findnew(session, switch_event->prev_pid); |
1389 | sched_in = threads__findnew(switch_event->next_pid, &threads, &last_match); | 1429 | sched_in = perf_session__findnew(session, switch_event->next_pid); |
1390 | 1430 | ||
1391 | curr_thread[this_cpu] = sched_in; | 1431 | curr_thread[this_cpu] = sched_in; |
1392 | 1432 | ||
@@ -1436,7 +1476,7 @@ map_switch_event(struct trace_switch_event *switch_event, | |||
1436 | 1476 | ||
1437 | 1477 | ||
1438 | static void | 1478 | static void |
1439 | process_sched_switch_event(struct raw_event_sample *raw, | 1479 | process_sched_switch_event(void *data, struct perf_session *session, |
1440 | struct event *event, | 1480 | struct event *event, |
1441 | int this_cpu, | 1481 | int this_cpu, |
1442 | u64 timestamp __used, | 1482 | u64 timestamp __used, |
@@ -1444,15 +1484,15 @@ process_sched_switch_event(struct raw_event_sample *raw, | |||
1444 | { | 1484 | { |
1445 | struct trace_switch_event switch_event; | 1485 | struct trace_switch_event switch_event; |
1446 | 1486 | ||
1447 | FILL_COMMON_FIELDS(switch_event, event, raw->data); | 1487 | FILL_COMMON_FIELDS(switch_event, event, data); |
1448 | 1488 | ||
1449 | FILL_ARRAY(switch_event, prev_comm, event, raw->data); | 1489 | FILL_ARRAY(switch_event, prev_comm, event, data); |
1450 | FILL_FIELD(switch_event, prev_pid, event, raw->data); | 1490 | FILL_FIELD(switch_event, prev_pid, event, data); |
1451 | FILL_FIELD(switch_event, prev_prio, event, raw->data); | 1491 | FILL_FIELD(switch_event, prev_prio, event, data); |
1452 | FILL_FIELD(switch_event, prev_state, event, raw->data); | 1492 | FILL_FIELD(switch_event, prev_state, event, data); |
1453 | FILL_ARRAY(switch_event, next_comm, event, raw->data); | 1493 | FILL_ARRAY(switch_event, next_comm, event, data); |
1454 | FILL_FIELD(switch_event, next_pid, event, raw->data); | 1494 | FILL_FIELD(switch_event, next_pid, event, data); |
1455 | FILL_FIELD(switch_event, next_prio, event, raw->data); | 1495 | FILL_FIELD(switch_event, next_prio, event, data); |
1456 | 1496 | ||
1457 | if (curr_pid[this_cpu] != (u32)-1) { | 1497 | if (curr_pid[this_cpu] != (u32)-1) { |
1458 | /* | 1498 | /* |
@@ -1463,13 +1503,14 @@ process_sched_switch_event(struct raw_event_sample *raw, | |||
1463 | nr_context_switch_bugs++; | 1503 | nr_context_switch_bugs++; |
1464 | } | 1504 | } |
1465 | if (trace_handler->switch_event) | 1505 | if (trace_handler->switch_event) |
1466 | trace_handler->switch_event(&switch_event, event, this_cpu, timestamp, thread); | 1506 | trace_handler->switch_event(&switch_event, session, event, |
1507 | this_cpu, timestamp, thread); | ||
1467 | 1508 | ||
1468 | curr_pid[this_cpu] = switch_event.next_pid; | 1509 | curr_pid[this_cpu] = switch_event.next_pid; |
1469 | } | 1510 | } |
1470 | 1511 | ||
1471 | static void | 1512 | static void |
1472 | process_sched_runtime_event(struct raw_event_sample *raw, | 1513 | process_sched_runtime_event(void *data, struct perf_session *session, |
1473 | struct event *event, | 1514 | struct event *event, |
1474 | int cpu __used, | 1515 | int cpu __used, |
1475 | u64 timestamp __used, | 1516 | u64 timestamp __used, |
@@ -1477,17 +1518,17 @@ process_sched_runtime_event(struct raw_event_sample *raw, | |||
1477 | { | 1518 | { |
1478 | struct trace_runtime_event runtime_event; | 1519 | struct trace_runtime_event runtime_event; |
1479 | 1520 | ||
1480 | FILL_ARRAY(runtime_event, comm, event, raw->data); | 1521 | FILL_ARRAY(runtime_event, comm, event, data); |
1481 | FILL_FIELD(runtime_event, pid, event, raw->data); | 1522 | FILL_FIELD(runtime_event, pid, event, data); |
1482 | FILL_FIELD(runtime_event, runtime, event, raw->data); | 1523 | FILL_FIELD(runtime_event, runtime, event, data); |
1483 | FILL_FIELD(runtime_event, vruntime, event, raw->data); | 1524 | FILL_FIELD(runtime_event, vruntime, event, data); |
1484 | 1525 | ||
1485 | if (trace_handler->runtime_event) | 1526 | if (trace_handler->runtime_event) |
1486 | trace_handler->runtime_event(&runtime_event, event, cpu, timestamp, thread); | 1527 | trace_handler->runtime_event(&runtime_event, session, event, cpu, timestamp, thread); |
1487 | } | 1528 | } |
1488 | 1529 | ||
1489 | static void | 1530 | static void |
1490 | process_sched_fork_event(struct raw_event_sample *raw, | 1531 | process_sched_fork_event(void *data, |
1491 | struct event *event, | 1532 | struct event *event, |
1492 | int cpu __used, | 1533 | int cpu __used, |
1493 | u64 timestamp __used, | 1534 | u64 timestamp __used, |
@@ -1495,15 +1536,16 @@ process_sched_fork_event(struct raw_event_sample *raw, | |||
1495 | { | 1536 | { |
1496 | struct trace_fork_event fork_event; | 1537 | struct trace_fork_event fork_event; |
1497 | 1538 | ||
1498 | FILL_COMMON_FIELDS(fork_event, event, raw->data); | 1539 | FILL_COMMON_FIELDS(fork_event, event, data); |
1499 | 1540 | ||
1500 | FILL_ARRAY(fork_event, parent_comm, event, raw->data); | 1541 | FILL_ARRAY(fork_event, parent_comm, event, data); |
1501 | FILL_FIELD(fork_event, parent_pid, event, raw->data); | 1542 | FILL_FIELD(fork_event, parent_pid, event, data); |
1502 | FILL_ARRAY(fork_event, child_comm, event, raw->data); | 1543 | FILL_ARRAY(fork_event, child_comm, event, data); |
1503 | FILL_FIELD(fork_event, child_pid, event, raw->data); | 1544 | FILL_FIELD(fork_event, child_pid, event, data); |
1504 | 1545 | ||
1505 | if (trace_handler->fork_event) | 1546 | if (trace_handler->fork_event) |
1506 | trace_handler->fork_event(&fork_event, event, cpu, timestamp, thread); | 1547 | trace_handler->fork_event(&fork_event, event, |
1548 | cpu, timestamp, thread); | ||
1507 | } | 1549 | } |
1508 | 1550 | ||
1509 | static void | 1551 | static void |
@@ -1517,233 +1559,115 @@ process_sched_exit_event(struct event *event, | |||
1517 | } | 1559 | } |
1518 | 1560 | ||
1519 | static void | 1561 | static void |
1520 | process_raw_event(event_t *raw_event __used, void *more_data, | 1562 | process_sched_migrate_task_event(void *data, struct perf_session *session, |
1521 | int cpu, u64 timestamp, struct thread *thread) | 1563 | struct event *event, |
1564 | int cpu __used, | ||
1565 | u64 timestamp __used, | ||
1566 | struct thread *thread __used) | ||
1567 | { | ||
1568 | struct trace_migrate_task_event migrate_task_event; | ||
1569 | |||
1570 | FILL_COMMON_FIELDS(migrate_task_event, event, data); | ||
1571 | |||
1572 | FILL_ARRAY(migrate_task_event, comm, event, data); | ||
1573 | FILL_FIELD(migrate_task_event, pid, event, data); | ||
1574 | FILL_FIELD(migrate_task_event, prio, event, data); | ||
1575 | FILL_FIELD(migrate_task_event, cpu, event, data); | ||
1576 | |||
1577 | if (trace_handler->migrate_task_event) | ||
1578 | trace_handler->migrate_task_event(&migrate_task_event, session, | ||
1579 | event, cpu, timestamp, thread); | ||
1580 | } | ||
1581 | |||
1582 | static void | ||
1583 | process_raw_event(event_t *raw_event __used, struct perf_session *session, | ||
1584 | void *data, int cpu, u64 timestamp, struct thread *thread) | ||
1522 | { | 1585 | { |
1523 | struct raw_event_sample *raw = more_data; | ||
1524 | struct event *event; | 1586 | struct event *event; |
1525 | int type; | 1587 | int type; |
1526 | 1588 | ||
1527 | type = trace_parse_common_type(raw->data); | 1589 | |
1590 | type = trace_parse_common_type(data); | ||
1528 | event = trace_find_event(type); | 1591 | event = trace_find_event(type); |
1529 | 1592 | ||
1530 | if (!strcmp(event->name, "sched_switch")) | 1593 | if (!strcmp(event->name, "sched_switch")) |
1531 | process_sched_switch_event(raw, event, cpu, timestamp, thread); | 1594 | process_sched_switch_event(data, session, event, cpu, timestamp, thread); |
1532 | if (!strcmp(event->name, "sched_stat_runtime")) | 1595 | if (!strcmp(event->name, "sched_stat_runtime")) |
1533 | process_sched_runtime_event(raw, event, cpu, timestamp, thread); | 1596 | process_sched_runtime_event(data, session, event, cpu, timestamp, thread); |
1534 | if (!strcmp(event->name, "sched_wakeup")) | 1597 | if (!strcmp(event->name, "sched_wakeup")) |
1535 | process_sched_wakeup_event(raw, event, cpu, timestamp, thread); | 1598 | process_sched_wakeup_event(data, session, event, cpu, timestamp, thread); |
1536 | if (!strcmp(event->name, "sched_wakeup_new")) | 1599 | if (!strcmp(event->name, "sched_wakeup_new")) |
1537 | process_sched_wakeup_event(raw, event, cpu, timestamp, thread); | 1600 | process_sched_wakeup_event(data, session, event, cpu, timestamp, thread); |
1538 | if (!strcmp(event->name, "sched_process_fork")) | 1601 | if (!strcmp(event->name, "sched_process_fork")) |
1539 | process_sched_fork_event(raw, event, cpu, timestamp, thread); | 1602 | process_sched_fork_event(data, event, cpu, timestamp, thread); |
1540 | if (!strcmp(event->name, "sched_process_exit")) | 1603 | if (!strcmp(event->name, "sched_process_exit")) |
1541 | process_sched_exit_event(event, cpu, timestamp, thread); | 1604 | process_sched_exit_event(event, cpu, timestamp, thread); |
1605 | if (!strcmp(event->name, "sched_migrate_task")) | ||
1606 | process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); | ||
1542 | } | 1607 | } |
1543 | 1608 | ||
1544 | static int | 1609 | static int process_sample_event(event_t *event, struct perf_session *session) |
1545 | process_sample_event(event_t *event, unsigned long offset, unsigned long head) | ||
1546 | { | 1610 | { |
1547 | char level; | 1611 | struct sample_data data; |
1548 | int show = 0; | ||
1549 | struct dso *dso = NULL; | ||
1550 | struct thread *thread; | 1612 | struct thread *thread; |
1551 | u64 ip = event->ip.ip; | ||
1552 | u64 timestamp = -1; | ||
1553 | u32 cpu = -1; | ||
1554 | u64 period = 1; | ||
1555 | void *more_data = event->ip.__more_data; | ||
1556 | int cpumode; | ||
1557 | |||
1558 | thread = threads__findnew(event->ip.pid, &threads, &last_match); | ||
1559 | |||
1560 | if (sample_type & PERF_SAMPLE_TIME) { | ||
1561 | timestamp = *(u64 *)more_data; | ||
1562 | more_data += sizeof(u64); | ||
1563 | } | ||
1564 | 1613 | ||
1565 | if (sample_type & PERF_SAMPLE_CPU) { | 1614 | if (!(session->sample_type & PERF_SAMPLE_RAW)) |
1566 | cpu = *(u32 *)more_data; | 1615 | return 0; |
1567 | more_data += sizeof(u32); | ||
1568 | more_data += sizeof(u32); /* reserved */ | ||
1569 | } | ||
1570 | 1616 | ||
1571 | if (sample_type & PERF_SAMPLE_PERIOD) { | 1617 | memset(&data, 0, sizeof(data)); |
1572 | period = *(u64 *)more_data; | 1618 | data.time = -1; |
1573 | more_data += sizeof(u64); | 1619 | data.cpu = -1; |
1574 | } | 1620 | data.period = -1; |
1575 | 1621 | ||
1576 | dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", | 1622 | event__parse_sample(event, session->sample_type, &data); |
1577 | (void *)(offset + head), | ||
1578 | (void *)(long)(event->header.size), | ||
1579 | event->header.misc, | ||
1580 | event->ip.pid, event->ip.tid, | ||
1581 | (void *)(long)ip, | ||
1582 | (long long)period); | ||
1583 | 1623 | ||
1584 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 1624 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
1625 | data.pid, data.tid, data.ip, data.period); | ||
1585 | 1626 | ||
1627 | thread = perf_session__findnew(session, data.pid); | ||
1586 | if (thread == NULL) { | 1628 | if (thread == NULL) { |
1587 | eprintf("problem processing %d event, skipping it.\n", | 1629 | pr_debug("problem processing %d event, skipping it.\n", |
1588 | event->header.type); | 1630 | event->header.type); |
1589 | return -1; | 1631 | return -1; |
1590 | } | 1632 | } |
1591 | 1633 | ||
1592 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 1634 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
1593 | |||
1594 | if (cpumode == PERF_RECORD_MISC_KERNEL) { | ||
1595 | show = SHOW_KERNEL; | ||
1596 | level = 'k'; | ||
1597 | |||
1598 | dso = kernel_dso; | ||
1599 | |||
1600 | dump_printf(" ...... dso: %s\n", dso->name); | ||
1601 | |||
1602 | } else if (cpumode == PERF_RECORD_MISC_USER) { | ||
1603 | |||
1604 | show = SHOW_USER; | ||
1605 | level = '.'; | ||
1606 | |||
1607 | } else { | ||
1608 | show = SHOW_HV; | ||
1609 | level = 'H'; | ||
1610 | |||
1611 | dso = hypervisor_dso; | ||
1612 | 1635 | ||
1613 | dump_printf(" ...... dso: [hypervisor]\n"); | 1636 | if (profile_cpu != -1 && profile_cpu != (int)data.cpu) |
1614 | } | 1637 | return 0; |
1615 | 1638 | ||
1616 | if (sample_type & PERF_SAMPLE_RAW) | 1639 | process_raw_event(event, session, data.raw_data, data.cpu, data.time, thread); |
1617 | process_raw_event(event, more_data, cpu, timestamp, thread); | ||
1618 | 1640 | ||
1619 | return 0; | 1641 | return 0; |
1620 | } | 1642 | } |
1621 | 1643 | ||
1622 | static int | 1644 | static int process_lost_event(event_t *event __used, |
1623 | process_event(event_t *event, unsigned long offset, unsigned long head) | 1645 | struct perf_session *session __used) |
1624 | { | 1646 | { |
1625 | trace_event(event); | 1647 | nr_lost_chunks++; |
1626 | 1648 | nr_lost_events += event->lost.lost; | |
1627 | nr_events++; | ||
1628 | switch (event->header.type) { | ||
1629 | case PERF_RECORD_MMAP: | ||
1630 | return 0; | ||
1631 | case PERF_RECORD_LOST: | ||
1632 | nr_lost_chunks++; | ||
1633 | nr_lost_events += event->lost.lost; | ||
1634 | return 0; | ||
1635 | |||
1636 | case PERF_RECORD_COMM: | ||
1637 | return process_comm_event(event, offset, head); | ||
1638 | |||
1639 | case PERF_RECORD_EXIT ... PERF_RECORD_READ: | ||
1640 | return 0; | ||
1641 | |||
1642 | case PERF_RECORD_SAMPLE: | ||
1643 | return process_sample_event(event, offset, head); | ||
1644 | |||
1645 | case PERF_RECORD_MAX: | ||
1646 | default: | ||
1647 | return -1; | ||
1648 | } | ||
1649 | 1649 | ||
1650 | return 0; | 1650 | return 0; |
1651 | } | 1651 | } |
1652 | 1652 | ||
1653 | static struct perf_event_ops event_ops = { | ||
1654 | .sample = process_sample_event, | ||
1655 | .comm = event__process_comm, | ||
1656 | .lost = process_lost_event, | ||
1657 | }; | ||
1658 | |||
1653 | static int read_events(void) | 1659 | static int read_events(void) |
1654 | { | 1660 | { |
1655 | int ret, rc = EXIT_FAILURE; | 1661 | int err = -EINVAL; |
1656 | unsigned long offset = 0; | 1662 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); |
1657 | unsigned long head = 0; | 1663 | if (session == NULL) |
1658 | struct stat perf_stat; | 1664 | return -ENOMEM; |
1659 | event_t *event; | ||
1660 | uint32_t size; | ||
1661 | char *buf; | ||
1662 | |||
1663 | trace_report(); | ||
1664 | register_idle_thread(&threads, &last_match); | ||
1665 | |||
1666 | input = open(input_name, O_RDONLY); | ||
1667 | if (input < 0) { | ||
1668 | perror("failed to open file"); | ||
1669 | exit(-1); | ||
1670 | } | ||
1671 | |||
1672 | ret = fstat(input, &perf_stat); | ||
1673 | if (ret < 0) { | ||
1674 | perror("failed to stat file"); | ||
1675 | exit(-1); | ||
1676 | } | ||
1677 | |||
1678 | if (!perf_stat.st_size) { | ||
1679 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | ||
1680 | exit(0); | ||
1681 | } | ||
1682 | header = perf_header__read(input); | ||
1683 | head = header->data_offset; | ||
1684 | sample_type = perf_header__sample_type(header); | ||
1685 | |||
1686 | if (!(sample_type & PERF_SAMPLE_RAW)) | ||
1687 | die("No trace sample to read. Did you call perf record " | ||
1688 | "without -R?"); | ||
1689 | |||
1690 | if (load_kernel() < 0) { | ||
1691 | perror("failed to load kernel symbols"); | ||
1692 | return EXIT_FAILURE; | ||
1693 | } | ||
1694 | |||
1695 | remap: | ||
1696 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | ||
1697 | MAP_SHARED, input, offset); | ||
1698 | if (buf == MAP_FAILED) { | ||
1699 | perror("failed to mmap file"); | ||
1700 | exit(-1); | ||
1701 | } | ||
1702 | |||
1703 | more: | ||
1704 | event = (event_t *)(buf + head); | ||
1705 | |||
1706 | size = event->header.size; | ||
1707 | if (!size) | ||
1708 | size = 8; | ||
1709 | |||
1710 | if (head + event->header.size >= page_size * mmap_window) { | ||
1711 | unsigned long shift = page_size * (head / page_size); | ||
1712 | int res; | ||
1713 | |||
1714 | res = munmap(buf, page_size * mmap_window); | ||
1715 | assert(res == 0); | ||
1716 | |||
1717 | offset += shift; | ||
1718 | head -= shift; | ||
1719 | goto remap; | ||
1720 | } | ||
1721 | |||
1722 | size = event->header.size; | ||
1723 | 1665 | ||
1666 | if (perf_session__has_traces(session, "record -R")) | ||
1667 | err = perf_session__process_events(session, &event_ops); | ||
1724 | 1668 | ||
1725 | if (!size || process_event(event, offset, head) < 0) { | 1669 | perf_session__delete(session); |
1726 | 1670 | return err; | |
1727 | /* | ||
1728 | * assume we lost track of the stream, check alignment, and | ||
1729 | * increment a single u64 in the hope to catch on again 'soon'. | ||
1730 | */ | ||
1731 | |||
1732 | if (unlikely(head & 7)) | ||
1733 | head &= ~7ULL; | ||
1734 | |||
1735 | size = 8; | ||
1736 | } | ||
1737 | |||
1738 | head += size; | ||
1739 | |||
1740 | if (offset + head < (unsigned long)perf_stat.st_size) | ||
1741 | goto more; | ||
1742 | |||
1743 | rc = EXIT_SUCCESS; | ||
1744 | close(input); | ||
1745 | |||
1746 | return rc; | ||
1747 | } | 1671 | } |
1748 | 1672 | ||
1749 | static void print_bad_events(void) | 1673 | static void print_bad_events(void) |
@@ -1784,9 +1708,9 @@ static void __cmd_lat(void) | |||
1784 | read_events(); | 1708 | read_events(); |
1785 | sort_lat(); | 1709 | sort_lat(); |
1786 | 1710 | ||
1787 | printf("\n -----------------------------------------------------------------------------------------\n"); | 1711 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); |
1788 | printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms |\n"); | 1712 | printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n"); |
1789 | printf(" -----------------------------------------------------------------------------------------\n"); | 1713 | printf(" ---------------------------------------------------------------------------------------------------------------\n"); |
1790 | 1714 | ||
1791 | next = rb_first(&sorted_atom_root); | 1715 | next = rb_first(&sorted_atom_root); |
1792 | 1716 | ||
@@ -1883,6 +1807,8 @@ static const struct option latency_options[] = { | |||
1883 | "sort by key(s): runtime, switch, avg, max"), | 1807 | "sort by key(s): runtime, switch, avg, max"), |
1884 | OPT_BOOLEAN('v', "verbose", &verbose, | 1808 | OPT_BOOLEAN('v', "verbose", &verbose, |
1885 | "be more verbose (show symbol address, etc)"), | 1809 | "be more verbose (show symbol address, etc)"), |
1810 | OPT_INTEGER('C', "CPU", &profile_cpu, | ||
1811 | "CPU to profile on"), | ||
1886 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1812 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1887 | "dump raw trace in ASCII"), | 1813 | "dump raw trace in ASCII"), |
1888 | OPT_END() | 1814 | OPT_END() |
@@ -1960,14 +1886,18 @@ static int __cmd_record(int argc, const char **argv) | |||
1960 | 1886 | ||
1961 | int cmd_sched(int argc, const char **argv, const char *prefix __used) | 1887 | int cmd_sched(int argc, const char **argv, const char *prefix __used) |
1962 | { | 1888 | { |
1963 | symbol__init(); | ||
1964 | page_size = getpagesize(); | ||
1965 | |||
1966 | argc = parse_options(argc, argv, sched_options, sched_usage, | 1889 | argc = parse_options(argc, argv, sched_options, sched_usage, |
1967 | PARSE_OPT_STOP_AT_NON_OPTION); | 1890 | PARSE_OPT_STOP_AT_NON_OPTION); |
1968 | if (!argc) | 1891 | if (!argc) |
1969 | usage_with_options(sched_usage, sched_options); | 1892 | usage_with_options(sched_usage, sched_options); |
1970 | 1893 | ||
1894 | /* | ||
1895 | * Aliased to 'perf trace' for now: | ||
1896 | */ | ||
1897 | if (!strcmp(argv[0], "trace")) | ||
1898 | return cmd_trace(argc, argv, prefix); | ||
1899 | |||
1900 | symbol__init(); | ||
1971 | if (!strncmp(argv[0], "rec", 3)) { | 1901 | if (!strncmp(argv[0], "rec", 3)) { |
1972 | return __cmd_record(argc, argv); | 1902 | return __cmd_record(argc, argv); |
1973 | } else if (!strncmp(argv[0], "lat", 3)) { | 1903 | } else if (!strncmp(argv[0], "lat", 3)) { |
@@ -1991,11 +1921,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used) | |||
1991 | usage_with_options(replay_usage, replay_options); | 1921 | usage_with_options(replay_usage, replay_options); |
1992 | } | 1922 | } |
1993 | __cmd_replay(); | 1923 | __cmd_replay(); |
1994 | } else if (!strcmp(argv[0], "trace")) { | ||
1995 | /* | ||
1996 | * Aliased to 'perf trace' for now: | ||
1997 | */ | ||
1998 | return cmd_trace(argc, argv, prefix); | ||
1999 | } else { | 1924 | } else { |
2000 | usage_with_options(sched_usage, sched_options); | 1925 | usage_with_options(sched_usage, sched_options); |
2001 | } | 1926 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 3db31e7bf173..e8c85d5aec41 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -44,21 +44,24 @@ | |||
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/debug.h" | 46 | #include "util/debug.h" |
47 | #include "util/header.h" | ||
47 | 48 | ||
48 | #include <sys/prctl.h> | 49 | #include <sys/prctl.h> |
49 | #include <math.h> | 50 | #include <math.h> |
50 | 51 | ||
51 | static struct perf_event_attr default_attrs[] = { | 52 | static struct perf_event_attr default_attrs[] = { |
52 | 53 | ||
53 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, | 54 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, |
54 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES}, | 55 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, |
55 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS }, | 56 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS }, |
56 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, | 57 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, |
57 | 58 | ||
58 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, | 59 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, |
59 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, | 60 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, |
60 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES}, | 61 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, |
61 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES }, | 62 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, |
63 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES }, | ||
64 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES }, | ||
62 | 65 | ||
63 | }; | 66 | }; |
64 | 67 | ||
@@ -77,6 +80,8 @@ static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | |||
77 | 80 | ||
78 | static int event_scaled[MAX_COUNTERS]; | 81 | static int event_scaled[MAX_COUNTERS]; |
79 | 82 | ||
83 | static volatile int done = 0; | ||
84 | |||
80 | struct stats | 85 | struct stats |
81 | { | 86 | { |
82 | double n, mean, M2; | 87 | double n, mean, M2; |
@@ -125,6 +130,7 @@ struct stats event_res_stats[MAX_COUNTERS][3]; | |||
125 | struct stats runtime_nsecs_stats; | 130 | struct stats runtime_nsecs_stats; |
126 | struct stats walltime_nsecs_stats; | 131 | struct stats walltime_nsecs_stats; |
127 | struct stats runtime_cycles_stats; | 132 | struct stats runtime_cycles_stats; |
133 | struct stats runtime_branches_stats; | ||
128 | 134 | ||
129 | #define MATCH_EVENT(t, c, counter) \ | 135 | #define MATCH_EVENT(t, c, counter) \ |
130 | (attrs[counter].type == PERF_TYPE_##t && \ | 136 | (attrs[counter].type == PERF_TYPE_##t && \ |
@@ -235,6 +241,8 @@ static void read_counter(int counter) | |||
235 | update_stats(&runtime_nsecs_stats, count[0]); | 241 | update_stats(&runtime_nsecs_stats, count[0]); |
236 | if (MATCH_EVENT(HARDWARE, HW_CPU_CYCLES, counter)) | 242 | if (MATCH_EVENT(HARDWARE, HW_CPU_CYCLES, counter)) |
237 | update_stats(&runtime_cycles_stats, count[0]); | 243 | update_stats(&runtime_cycles_stats, count[0]); |
244 | if (MATCH_EVENT(HARDWARE, HW_BRANCH_INSTRUCTIONS, counter)) | ||
245 | update_stats(&runtime_branches_stats, count[0]); | ||
238 | } | 246 | } |
239 | 247 | ||
240 | static int run_perf_stat(int argc __used, const char **argv) | 248 | static int run_perf_stat(int argc __used, const char **argv) |
@@ -242,61 +250,64 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
242 | unsigned long long t0, t1; | 250 | unsigned long long t0, t1; |
243 | int status = 0; | 251 | int status = 0; |
244 | int counter; | 252 | int counter; |
245 | int pid; | 253 | int pid = target_pid; |
246 | int child_ready_pipe[2], go_pipe[2]; | 254 | int child_ready_pipe[2], go_pipe[2]; |
255 | const bool forks = (target_pid == -1 && argc > 0); | ||
247 | char buf; | 256 | char buf; |
248 | 257 | ||
249 | if (!system_wide) | 258 | if (!system_wide) |
250 | nr_cpus = 1; | 259 | nr_cpus = 1; |
251 | 260 | ||
252 | if (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0) { | 261 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { |
253 | perror("failed to create pipes"); | 262 | perror("failed to create pipes"); |
254 | exit(1); | 263 | exit(1); |
255 | } | 264 | } |
256 | 265 | ||
257 | if ((pid = fork()) < 0) | 266 | if (forks) { |
258 | perror("failed to fork"); | 267 | if ((pid = fork()) < 0) |
259 | 268 | perror("failed to fork"); | |
260 | if (!pid) { | 269 | |
261 | close(child_ready_pipe[0]); | 270 | if (!pid) { |
262 | close(go_pipe[1]); | 271 | close(child_ready_pipe[0]); |
263 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | 272 | close(go_pipe[1]); |
273 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
274 | |||
275 | /* | ||
276 | * Do a dummy execvp to get the PLT entry resolved, | ||
277 | * so we avoid the resolver overhead on the real | ||
278 | * execvp call. | ||
279 | */ | ||
280 | execvp("", (char **)argv); | ||
281 | |||
282 | /* | ||
283 | * Tell the parent we're ready to go | ||
284 | */ | ||
285 | close(child_ready_pipe[1]); | ||
286 | |||
287 | /* | ||
288 | * Wait until the parent tells us to go. | ||
289 | */ | ||
290 | if (read(go_pipe[0], &buf, 1) == -1) | ||
291 | perror("unable to read pipe"); | ||
292 | |||
293 | execvp(argv[0], (char **)argv); | ||
294 | |||
295 | perror(argv[0]); | ||
296 | exit(-1); | ||
297 | } | ||
264 | 298 | ||
265 | /* | 299 | child_pid = pid; |
266 | * Do a dummy execvp to get the PLT entry resolved, | ||
267 | * so we avoid the resolver overhead on the real | ||
268 | * execvp call. | ||
269 | */ | ||
270 | execvp("", (char **)argv); | ||
271 | 300 | ||
272 | /* | 301 | /* |
273 | * Tell the parent we're ready to go | 302 | * Wait for the child to be ready to exec. |
274 | */ | 303 | */ |
275 | close(child_ready_pipe[1]); | 304 | close(child_ready_pipe[1]); |
276 | 305 | close(go_pipe[0]); | |
277 | /* | 306 | if (read(child_ready_pipe[0], &buf, 1) == -1) |
278 | * Wait until the parent tells us to go. | ||
279 | */ | ||
280 | if (read(go_pipe[0], &buf, 1) == -1) | ||
281 | perror("unable to read pipe"); | 307 | perror("unable to read pipe"); |
282 | 308 | close(child_ready_pipe[0]); | |
283 | execvp(argv[0], (char **)argv); | ||
284 | |||
285 | perror(argv[0]); | ||
286 | exit(-1); | ||
287 | } | 309 | } |
288 | 310 | ||
289 | child_pid = pid; | ||
290 | |||
291 | /* | ||
292 | * Wait for the child to be ready to exec. | ||
293 | */ | ||
294 | close(child_ready_pipe[1]); | ||
295 | close(go_pipe[0]); | ||
296 | if (read(child_ready_pipe[0], &buf, 1) == -1) | ||
297 | perror("unable to read pipe"); | ||
298 | close(child_ready_pipe[0]); | ||
299 | |||
300 | for (counter = 0; counter < nr_counters; counter++) | 311 | for (counter = 0; counter < nr_counters; counter++) |
301 | create_perf_stat_counter(counter, pid); | 312 | create_perf_stat_counter(counter, pid); |
302 | 313 | ||
@@ -305,8 +316,12 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
305 | */ | 316 | */ |
306 | t0 = rdclock(); | 317 | t0 = rdclock(); |
307 | 318 | ||
308 | close(go_pipe[1]); | 319 | if (forks) { |
309 | wait(&status); | 320 | close(go_pipe[1]); |
321 | wait(&status); | ||
322 | } else { | ||
323 | while(!done); | ||
324 | } | ||
310 | 325 | ||
311 | t1 = rdclock(); | 326 | t1 = rdclock(); |
312 | 327 | ||
@@ -352,7 +367,16 @@ static void abs_printout(int counter, double avg) | |||
352 | ratio = avg / total; | 367 | ratio = avg / total; |
353 | 368 | ||
354 | fprintf(stderr, " # %10.3f IPC ", ratio); | 369 | fprintf(stderr, " # %10.3f IPC ", ratio); |
355 | } else { | 370 | } else if (MATCH_EVENT(HARDWARE, HW_BRANCH_MISSES, counter) && |
371 | runtime_branches_stats.n != 0) { | ||
372 | total = avg_stats(&runtime_branches_stats); | ||
373 | |||
374 | if (total) | ||
375 | ratio = avg * 100 / total; | ||
376 | |||
377 | fprintf(stderr, " # %10.3f %% ", ratio); | ||
378 | |||
379 | } else if (runtime_nsecs_stats.n != 0) { | ||
356 | total = avg_stats(&runtime_nsecs_stats); | 380 | total = avg_stats(&runtime_nsecs_stats); |
357 | 381 | ||
358 | if (total) | 382 | if (total) |
@@ -403,10 +427,13 @@ static void print_stat(int argc, const char **argv) | |||
403 | fflush(stdout); | 427 | fflush(stdout); |
404 | 428 | ||
405 | fprintf(stderr, "\n"); | 429 | fprintf(stderr, "\n"); |
406 | fprintf(stderr, " Performance counter stats for \'%s", argv[0]); | 430 | fprintf(stderr, " Performance counter stats for "); |
407 | 431 | if(target_pid == -1) { | |
408 | for (i = 1; i < argc; i++) | 432 | fprintf(stderr, "\'%s", argv[0]); |
409 | fprintf(stderr, " %s", argv[i]); | 433 | for (i = 1; i < argc; i++) |
434 | fprintf(stderr, " %s", argv[i]); | ||
435 | }else | ||
436 | fprintf(stderr, "task pid \'%d", target_pid); | ||
410 | 437 | ||
411 | fprintf(stderr, "\'"); | 438 | fprintf(stderr, "\'"); |
412 | if (run_count > 1) | 439 | if (run_count > 1) |
@@ -431,6 +458,9 @@ static volatile int signr = -1; | |||
431 | 458 | ||
432 | static void skip_signal(int signo) | 459 | static void skip_signal(int signo) |
433 | { | 460 | { |
461 | if(target_pid != -1) | ||
462 | done = 1; | ||
463 | |||
434 | signr = signo; | 464 | signr = signo; |
435 | } | 465 | } |
436 | 466 | ||
@@ -447,7 +477,7 @@ static void sig_atexit(void) | |||
447 | } | 477 | } |
448 | 478 | ||
449 | static const char * const stat_usage[] = { | 479 | static const char * const stat_usage[] = { |
450 | "perf stat [<options>] <command>", | 480 | "perf stat [<options>] [<command>]", |
451 | NULL | 481 | NULL |
452 | }; | 482 | }; |
453 | 483 | ||
@@ -478,7 +508,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
478 | 508 | ||
479 | argc = parse_options(argc, argv, options, stat_usage, | 509 | argc = parse_options(argc, argv, options, stat_usage, |
480 | PARSE_OPT_STOP_AT_NON_OPTION); | 510 | PARSE_OPT_STOP_AT_NON_OPTION); |
481 | if (!argc) | 511 | if (!argc && target_pid == -1) |
482 | usage_with_options(stat_usage, options); | 512 | usage_with_options(stat_usage, options); |
483 | if (run_count <= 0) | 513 | if (run_count <= 0) |
484 | usage_with_options(stat_usage, options); | 514 | usage_with_options(stat_usage, options); |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index e8a510d935e5..0d4d8ff7914b 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -29,16 +29,13 @@ | |||
29 | #include "util/header.h" | 29 | #include "util/header.h" |
30 | #include "util/parse-options.h" | 30 | #include "util/parse-options.h" |
31 | #include "util/parse-events.h" | 31 | #include "util/parse-events.h" |
32 | #include "util/event.h" | ||
33 | #include "util/session.h" | ||
32 | #include "util/svghelper.h" | 34 | #include "util/svghelper.h" |
33 | 35 | ||
34 | static char const *input_name = "perf.data"; | 36 | static char const *input_name = "perf.data"; |
35 | static char const *output_name = "output.svg"; | 37 | static char const *output_name = "output.svg"; |
36 | 38 | ||
37 | |||
38 | static unsigned long page_size; | ||
39 | static unsigned long mmap_window = 32; | ||
40 | static u64 sample_type; | ||
41 | |||
42 | static unsigned int numcpus; | 39 | static unsigned int numcpus; |
43 | static u64 min_freq; /* Lowest CPU frequency seen */ | 40 | static u64 min_freq; /* Lowest CPU frequency seen */ |
44 | static u64 max_freq; /* Highest CPU frequency seen */ | 41 | static u64 max_freq; /* Highest CPU frequency seen */ |
@@ -49,8 +46,6 @@ static u64 first_time, last_time; | |||
49 | static int power_only; | 46 | static int power_only; |
50 | 47 | ||
51 | 48 | ||
52 | static struct perf_header *header; | ||
53 | |||
54 | struct per_pid; | 49 | struct per_pid; |
55 | struct per_pidcomm; | 50 | struct per_pidcomm; |
56 | 51 | ||
@@ -153,6 +148,17 @@ static struct wake_event *wake_events; | |||
153 | 148 | ||
154 | struct sample_wrapper *all_samples; | 149 | struct sample_wrapper *all_samples; |
155 | 150 | ||
151 | |||
152 | struct process_filter; | ||
153 | struct process_filter { | ||
154 | char *name; | ||
155 | int pid; | ||
156 | struct process_filter *next; | ||
157 | }; | ||
158 | |||
159 | static struct process_filter *process_filter; | ||
160 | |||
161 | |||
156 | static struct per_pid *find_create_pid(int pid) | 162 | static struct per_pid *find_create_pid(int pid) |
157 | { | 163 | { |
158 | struct per_pid *cursor = all_data; | 164 | struct per_pid *cursor = all_data; |
@@ -272,33 +278,30 @@ static int cpus_cstate_state[MAX_CPUS]; | |||
272 | static u64 cpus_pstate_start_times[MAX_CPUS]; | 278 | static u64 cpus_pstate_start_times[MAX_CPUS]; |
273 | static u64 cpus_pstate_state[MAX_CPUS]; | 279 | static u64 cpus_pstate_state[MAX_CPUS]; |
274 | 280 | ||
275 | static int | 281 | static int process_comm_event(event_t *event, struct perf_session *session __used) |
276 | process_comm_event(event_t *event) | ||
277 | { | 282 | { |
278 | pid_set_comm(event->comm.pid, event->comm.comm); | 283 | pid_set_comm(event->comm.tid, event->comm.comm); |
279 | return 0; | 284 | return 0; |
280 | } | 285 | } |
281 | static int | 286 | |
282 | process_fork_event(event_t *event) | 287 | static int process_fork_event(event_t *event, struct perf_session *session __used) |
283 | { | 288 | { |
284 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); | 289 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); |
285 | return 0; | 290 | return 0; |
286 | } | 291 | } |
287 | 292 | ||
288 | static int | 293 | static int process_exit_event(event_t *event, struct perf_session *session __used) |
289 | process_exit_event(event_t *event) | ||
290 | { | 294 | { |
291 | pid_exit(event->fork.pid, event->fork.time); | 295 | pid_exit(event->fork.pid, event->fork.time); |
292 | return 0; | 296 | return 0; |
293 | } | 297 | } |
294 | 298 | ||
295 | struct trace_entry { | 299 | struct trace_entry { |
296 | u32 size; | ||
297 | unsigned short type; | 300 | unsigned short type; |
298 | unsigned char flags; | 301 | unsigned char flags; |
299 | unsigned char preempt_count; | 302 | unsigned char preempt_count; |
300 | int pid; | 303 | int pid; |
301 | int tgid; | 304 | int lock_depth; |
302 | }; | 305 | }; |
303 | 306 | ||
304 | struct power_entry { | 307 | struct power_entry { |
@@ -472,46 +475,24 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) | |||
472 | } | 475 | } |
473 | 476 | ||
474 | 477 | ||
475 | static int | 478 | static int process_sample_event(event_t *event, struct perf_session *session) |
476 | process_sample_event(event_t *event) | ||
477 | { | 479 | { |
478 | int cursor = 0; | 480 | struct sample_data data; |
479 | u64 addr = 0; | ||
480 | u64 stamp = 0; | ||
481 | u32 cpu = 0; | ||
482 | u32 pid = 0; | ||
483 | struct trace_entry *te; | 481 | struct trace_entry *te; |
484 | 482 | ||
485 | if (sample_type & PERF_SAMPLE_IP) | 483 | memset(&data, 0, sizeof(data)); |
486 | cursor++; | ||
487 | |||
488 | if (sample_type & PERF_SAMPLE_TID) { | ||
489 | pid = event->sample.array[cursor]>>32; | ||
490 | cursor++; | ||
491 | } | ||
492 | if (sample_type & PERF_SAMPLE_TIME) { | ||
493 | stamp = event->sample.array[cursor++]; | ||
494 | 484 | ||
495 | if (!first_time || first_time > stamp) | 485 | event__parse_sample(event, session->sample_type, &data); |
496 | first_time = stamp; | ||
497 | if (last_time < stamp) | ||
498 | last_time = stamp; | ||
499 | 486 | ||
487 | if (session->sample_type & PERF_SAMPLE_TIME) { | ||
488 | if (!first_time || first_time > data.time) | ||
489 | first_time = data.time; | ||
490 | if (last_time < data.time) | ||
491 | last_time = data.time; | ||
500 | } | 492 | } |
501 | if (sample_type & PERF_SAMPLE_ADDR) | ||
502 | addr = event->sample.array[cursor++]; | ||
503 | if (sample_type & PERF_SAMPLE_ID) | ||
504 | cursor++; | ||
505 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
506 | cursor++; | ||
507 | if (sample_type & PERF_SAMPLE_CPU) | ||
508 | cpu = event->sample.array[cursor++] & 0xFFFFFFFF; | ||
509 | if (sample_type & PERF_SAMPLE_PERIOD) | ||
510 | cursor++; | ||
511 | |||
512 | te = (void *)&event->sample.array[cursor]; | ||
513 | 493 | ||
514 | if (sample_type & PERF_SAMPLE_RAW && te->size > 0) { | 494 | te = (void *)data.raw_data; |
495 | if (session->sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) { | ||
515 | char *event_str; | 496 | char *event_str; |
516 | struct power_entry *pe; | 497 | struct power_entry *pe; |
517 | 498 | ||
@@ -523,19 +504,19 @@ process_sample_event(event_t *event) | |||
523 | return 0; | 504 | return 0; |
524 | 505 | ||
525 | if (strcmp(event_str, "power:power_start") == 0) | 506 | if (strcmp(event_str, "power:power_start") == 0) |
526 | c_state_start(cpu, stamp, pe->value); | 507 | c_state_start(data.cpu, data.time, pe->value); |
527 | 508 | ||
528 | if (strcmp(event_str, "power:power_end") == 0) | 509 | if (strcmp(event_str, "power:power_end") == 0) |
529 | c_state_end(cpu, stamp); | 510 | c_state_end(data.cpu, data.time); |
530 | 511 | ||
531 | if (strcmp(event_str, "power:power_frequency") == 0) | 512 | if (strcmp(event_str, "power:power_frequency") == 0) |
532 | p_state_change(cpu, stamp, pe->value); | 513 | p_state_change(data.cpu, data.time, pe->value); |
533 | 514 | ||
534 | if (strcmp(event_str, "sched:sched_wakeup") == 0) | 515 | if (strcmp(event_str, "sched:sched_wakeup") == 0) |
535 | sched_wakeup(cpu, stamp, pid, te); | 516 | sched_wakeup(data.cpu, data.time, data.pid, te); |
536 | 517 | ||
537 | if (strcmp(event_str, "sched:sched_switch") == 0) | 518 | if (strcmp(event_str, "sched:sched_switch") == 0) |
538 | sched_switch(cpu, stamp, te); | 519 | sched_switch(data.cpu, data.time, te); |
539 | } | 520 | } |
540 | return 0; | 521 | return 0; |
541 | } | 522 | } |
@@ -588,16 +569,16 @@ static void end_sample_processing(void) | |||
588 | } | 569 | } |
589 | } | 570 | } |
590 | 571 | ||
591 | static u64 sample_time(event_t *event) | 572 | static u64 sample_time(event_t *event, const struct perf_session *session) |
592 | { | 573 | { |
593 | int cursor; | 574 | int cursor; |
594 | 575 | ||
595 | cursor = 0; | 576 | cursor = 0; |
596 | if (sample_type & PERF_SAMPLE_IP) | 577 | if (session->sample_type & PERF_SAMPLE_IP) |
597 | cursor++; | 578 | cursor++; |
598 | if (sample_type & PERF_SAMPLE_TID) | 579 | if (session->sample_type & PERF_SAMPLE_TID) |
599 | cursor++; | 580 | cursor++; |
600 | if (sample_type & PERF_SAMPLE_TIME) | 581 | if (session->sample_type & PERF_SAMPLE_TIME) |
601 | return event->sample.array[cursor]; | 582 | return event->sample.array[cursor]; |
602 | return 0; | 583 | return 0; |
603 | } | 584 | } |
@@ -607,8 +588,7 @@ static u64 sample_time(event_t *event) | |||
607 | * We first queue all events, sorted backwards by insertion. | 588 | * We first queue all events, sorted backwards by insertion. |
608 | * The order will get flipped later. | 589 | * The order will get flipped later. |
609 | */ | 590 | */ |
610 | static int | 591 | static int queue_sample_event(event_t *event, struct perf_session *session) |
611 | queue_sample_event(event_t *event) | ||
612 | { | 592 | { |
613 | struct sample_wrapper *copy, *prev; | 593 | struct sample_wrapper *copy, *prev; |
614 | int size; | 594 | int size; |
@@ -622,7 +602,7 @@ queue_sample_event(event_t *event) | |||
622 | memset(copy, 0, size); | 602 | memset(copy, 0, size); |
623 | 603 | ||
624 | copy->next = NULL; | 604 | copy->next = NULL; |
625 | copy->timestamp = sample_time(event); | 605 | copy->timestamp = sample_time(event, session); |
626 | 606 | ||
627 | memcpy(©->data, event, event->sample.header.size); | 607 | memcpy(©->data, event, event->sample.header.size); |
628 | 608 | ||
@@ -763,11 +743,11 @@ static void draw_wakeups(void) | |||
763 | c = p->all; | 743 | c = p->all; |
764 | while (c) { | 744 | while (c) { |
765 | if (c->Y && c->start_time <= we->time && c->end_time >= we->time) { | 745 | if (c->Y && c->start_time <= we->time && c->end_time >= we->time) { |
766 | if (p->pid == we->waker) { | 746 | if (p->pid == we->waker && !from) { |
767 | from = c->Y; | 747 | from = c->Y; |
768 | task_from = strdup(c->comm); | 748 | task_from = strdup(c->comm); |
769 | } | 749 | } |
770 | if (p->pid == we->wakee) { | 750 | if (p->pid == we->wakee && !to) { |
771 | to = c->Y; | 751 | to = c->Y; |
772 | task_to = strdup(c->comm); | 752 | task_to = strdup(c->comm); |
773 | } | 753 | } |
@@ -882,12 +862,89 @@ static void draw_process_bars(void) | |||
882 | } | 862 | } |
883 | } | 863 | } |
884 | 864 | ||
865 | static void add_process_filter(const char *string) | ||
866 | { | ||
867 | struct process_filter *filt; | ||
868 | int pid; | ||
869 | |||
870 | pid = strtoull(string, NULL, 10); | ||
871 | filt = malloc(sizeof(struct process_filter)); | ||
872 | if (!filt) | ||
873 | return; | ||
874 | |||
875 | filt->name = strdup(string); | ||
876 | filt->pid = pid; | ||
877 | filt->next = process_filter; | ||
878 | |||
879 | process_filter = filt; | ||
880 | } | ||
881 | |||
882 | static int passes_filter(struct per_pid *p, struct per_pidcomm *c) | ||
883 | { | ||
884 | struct process_filter *filt; | ||
885 | if (!process_filter) | ||
886 | return 1; | ||
887 | |||
888 | filt = process_filter; | ||
889 | while (filt) { | ||
890 | if (filt->pid && p->pid == filt->pid) | ||
891 | return 1; | ||
892 | if (strcmp(filt->name, c->comm) == 0) | ||
893 | return 1; | ||
894 | filt = filt->next; | ||
895 | } | ||
896 | return 0; | ||
897 | } | ||
898 | |||
899 | static int determine_display_tasks_filtered(void) | ||
900 | { | ||
901 | struct per_pid *p; | ||
902 | struct per_pidcomm *c; | ||
903 | int count = 0; | ||
904 | |||
905 | p = all_data; | ||
906 | while (p) { | ||
907 | p->display = 0; | ||
908 | if (p->start_time == 1) | ||
909 | p->start_time = first_time; | ||
910 | |||
911 | /* no exit marker, task kept running to the end */ | ||
912 | if (p->end_time == 0) | ||
913 | p->end_time = last_time; | ||
914 | |||
915 | c = p->all; | ||
916 | |||
917 | while (c) { | ||
918 | c->display = 0; | ||
919 | |||
920 | if (c->start_time == 1) | ||
921 | c->start_time = first_time; | ||
922 | |||
923 | if (passes_filter(p, c)) { | ||
924 | c->display = 1; | ||
925 | p->display = 1; | ||
926 | count++; | ||
927 | } | ||
928 | |||
929 | if (c->end_time == 0) | ||
930 | c->end_time = last_time; | ||
931 | |||
932 | c = c->next; | ||
933 | } | ||
934 | p = p->next; | ||
935 | } | ||
936 | return count; | ||
937 | } | ||
938 | |||
885 | static int determine_display_tasks(u64 threshold) | 939 | static int determine_display_tasks(u64 threshold) |
886 | { | 940 | { |
887 | struct per_pid *p; | 941 | struct per_pid *p; |
888 | struct per_pidcomm *c; | 942 | struct per_pidcomm *c; |
889 | int count = 0; | 943 | int count = 0; |
890 | 944 | ||
945 | if (process_filter) | ||
946 | return determine_display_tasks_filtered(); | ||
947 | |||
891 | p = all_data; | 948 | p = all_data; |
892 | while (p) { | 949 | while (p) { |
893 | p->display = 0; | 950 | p->display = 0; |
@@ -957,37 +1014,7 @@ static void write_svg_file(const char *filename) | |||
957 | svg_close(); | 1014 | svg_close(); |
958 | } | 1015 | } |
959 | 1016 | ||
960 | static int | 1017 | static void process_samples(struct perf_session *session) |
961 | process_event(event_t *event) | ||
962 | { | ||
963 | |||
964 | switch (event->header.type) { | ||
965 | |||
966 | case PERF_RECORD_COMM: | ||
967 | return process_comm_event(event); | ||
968 | case PERF_RECORD_FORK: | ||
969 | return process_fork_event(event); | ||
970 | case PERF_RECORD_EXIT: | ||
971 | return process_exit_event(event); | ||
972 | case PERF_RECORD_SAMPLE: | ||
973 | return queue_sample_event(event); | ||
974 | |||
975 | /* | ||
976 | * We dont process them right now but they are fine: | ||
977 | */ | ||
978 | case PERF_RECORD_MMAP: | ||
979 | case PERF_RECORD_THROTTLE: | ||
980 | case PERF_RECORD_UNTHROTTLE: | ||
981 | return 0; | ||
982 | |||
983 | default: | ||
984 | return -1; | ||
985 | } | ||
986 | |||
987 | return 0; | ||
988 | } | ||
989 | |||
990 | static void process_samples(void) | ||
991 | { | 1018 | { |
992 | struct sample_wrapper *cursor; | 1019 | struct sample_wrapper *cursor; |
993 | event_t *event; | 1020 | event_t *event; |
@@ -998,113 +1025,33 @@ static void process_samples(void) | |||
998 | while (cursor) { | 1025 | while (cursor) { |
999 | event = (void *)&cursor->data; | 1026 | event = (void *)&cursor->data; |
1000 | cursor = cursor->next; | 1027 | cursor = cursor->next; |
1001 | process_sample_event(event); | 1028 | process_sample_event(event, session); |
1002 | } | 1029 | } |
1003 | } | 1030 | } |
1004 | 1031 | ||
1032 | static struct perf_event_ops event_ops = { | ||
1033 | .comm = process_comm_event, | ||
1034 | .fork = process_fork_event, | ||
1035 | .exit = process_exit_event, | ||
1036 | .sample = queue_sample_event, | ||
1037 | }; | ||
1005 | 1038 | ||
1006 | static int __cmd_timechart(void) | 1039 | static int __cmd_timechart(void) |
1007 | { | 1040 | { |
1008 | int ret, rc = EXIT_FAILURE; | 1041 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); |
1009 | unsigned long offset = 0; | 1042 | int ret = -EINVAL; |
1010 | unsigned long head, shift; | ||
1011 | struct stat statbuf; | ||
1012 | event_t *event; | ||
1013 | uint32_t size; | ||
1014 | char *buf; | ||
1015 | int input; | ||
1016 | |||
1017 | input = open(input_name, O_RDONLY); | ||
1018 | if (input < 0) { | ||
1019 | fprintf(stderr, " failed to open file: %s", input_name); | ||
1020 | if (!strcmp(input_name, "perf.data")) | ||
1021 | fprintf(stderr, " (try 'perf record' first)"); | ||
1022 | fprintf(stderr, "\n"); | ||
1023 | exit(-1); | ||
1024 | } | ||
1025 | |||
1026 | ret = fstat(input, &statbuf); | ||
1027 | if (ret < 0) { | ||
1028 | perror("failed to stat file"); | ||
1029 | exit(-1); | ||
1030 | } | ||
1031 | |||
1032 | if (!statbuf.st_size) { | ||
1033 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | ||
1034 | exit(0); | ||
1035 | } | ||
1036 | 1043 | ||
1037 | header = perf_header__read(input); | 1044 | if (session == NULL) |
1038 | head = header->data_offset; | 1045 | return -ENOMEM; |
1039 | 1046 | ||
1040 | sample_type = perf_header__sample_type(header); | 1047 | if (!perf_session__has_traces(session, "timechart record")) |
1048 | goto out_delete; | ||
1041 | 1049 | ||
1042 | shift = page_size * (head / page_size); | 1050 | ret = perf_session__process_events(session, &event_ops); |
1043 | offset += shift; | 1051 | if (ret) |
1044 | head -= shift; | 1052 | goto out_delete; |
1045 | 1053 | ||
1046 | remap: | 1054 | process_samples(session); |
1047 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | ||
1048 | MAP_SHARED, input, offset); | ||
1049 | if (buf == MAP_FAILED) { | ||
1050 | perror("failed to mmap file"); | ||
1051 | exit(-1); | ||
1052 | } | ||
1053 | |||
1054 | more: | ||
1055 | event = (event_t *)(buf + head); | ||
1056 | |||
1057 | size = event->header.size; | ||
1058 | if (!size) | ||
1059 | size = 8; | ||
1060 | |||
1061 | if (head + event->header.size >= page_size * mmap_window) { | ||
1062 | int ret2; | ||
1063 | |||
1064 | shift = page_size * (head / page_size); | ||
1065 | |||
1066 | ret2 = munmap(buf, page_size * mmap_window); | ||
1067 | assert(ret2 == 0); | ||
1068 | |||
1069 | offset += shift; | ||
1070 | head -= shift; | ||
1071 | goto remap; | ||
1072 | } | ||
1073 | |||
1074 | size = event->header.size; | ||
1075 | |||
1076 | if (!size || process_event(event) < 0) { | ||
1077 | |||
1078 | printf("%p [%p]: skipping unknown header type: %d\n", | ||
1079 | (void *)(offset + head), | ||
1080 | (void *)(long)(event->header.size), | ||
1081 | event->header.type); | ||
1082 | |||
1083 | /* | ||
1084 | * assume we lost track of the stream, check alignment, and | ||
1085 | * increment a single u64 in the hope to catch on again 'soon'. | ||
1086 | */ | ||
1087 | |||
1088 | if (unlikely(head & 7)) | ||
1089 | head &= ~7ULL; | ||
1090 | |||
1091 | size = 8; | ||
1092 | } | ||
1093 | |||
1094 | head += size; | ||
1095 | |||
1096 | if (offset + head >= header->data_offset + header->data_size) | ||
1097 | goto done; | ||
1098 | |||
1099 | if (offset + head < (unsigned long)statbuf.st_size) | ||
1100 | goto more; | ||
1101 | |||
1102 | done: | ||
1103 | rc = EXIT_SUCCESS; | ||
1104 | close(input); | ||
1105 | |||
1106 | |||
1107 | process_samples(); | ||
1108 | 1055 | ||
1109 | end_sample_processing(); | 1056 | end_sample_processing(); |
1110 | 1057 | ||
@@ -1112,9 +1059,11 @@ done: | |||
1112 | 1059 | ||
1113 | write_svg_file(output_name); | 1060 | write_svg_file(output_name); |
1114 | 1061 | ||
1115 | printf("Written %2.1f seconds of trace to %s.\n", (last_time - first_time) / 1000000000.0, output_name); | 1062 | pr_info("Written %2.1f seconds of trace to %s.\n", |
1116 | 1063 | (last_time - first_time) / 1000000000.0, output_name); | |
1117 | return rc; | 1064 | out_delete: |
1065 | perf_session__delete(session); | ||
1066 | return ret; | ||
1118 | } | 1067 | } |
1119 | 1068 | ||
1120 | static const char * const timechart_usage[] = { | 1069 | static const char * const timechart_usage[] = { |
@@ -1153,6 +1102,14 @@ static int __cmd_record(int argc, const char **argv) | |||
1153 | return cmd_record(i, rec_argv, NULL); | 1102 | return cmd_record(i, rec_argv, NULL); |
1154 | } | 1103 | } |
1155 | 1104 | ||
1105 | static int | ||
1106 | parse_process(const struct option *opt __used, const char *arg, int __used unset) | ||
1107 | { | ||
1108 | if (arg) | ||
1109 | add_process_filter(arg); | ||
1110 | return 0; | ||
1111 | } | ||
1112 | |||
1156 | static const struct option options[] = { | 1113 | static const struct option options[] = { |
1157 | OPT_STRING('i', "input", &input_name, "file", | 1114 | OPT_STRING('i', "input", &input_name, "file", |
1158 | "input file name"), | 1115 | "input file name"), |
@@ -1160,21 +1117,22 @@ static const struct option options[] = { | |||
1160 | "output file name"), | 1117 | "output file name"), |
1161 | OPT_INTEGER('w', "width", &svg_page_width, | 1118 | OPT_INTEGER('w', "width", &svg_page_width, |
1162 | "page width"), | 1119 | "page width"), |
1163 | OPT_BOOLEAN('p', "power-only", &power_only, | 1120 | OPT_BOOLEAN('P', "power-only", &power_only, |
1164 | "output power data only"), | 1121 | "output power data only"), |
1122 | OPT_CALLBACK('p', "process", NULL, "process", | ||
1123 | "process selector. Pass a pid or process name.", | ||
1124 | parse_process), | ||
1165 | OPT_END() | 1125 | OPT_END() |
1166 | }; | 1126 | }; |
1167 | 1127 | ||
1168 | 1128 | ||
1169 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) | 1129 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) |
1170 | { | 1130 | { |
1171 | symbol__init(); | ||
1172 | |||
1173 | page_size = getpagesize(); | ||
1174 | |||
1175 | argc = parse_options(argc, argv, options, timechart_usage, | 1131 | argc = parse_options(argc, argv, options, timechart_usage, |
1176 | PARSE_OPT_STOP_AT_NON_OPTION); | 1132 | PARSE_OPT_STOP_AT_NON_OPTION); |
1177 | 1133 | ||
1134 | symbol__init(); | ||
1135 | |||
1178 | if (argc && !strncmp(argv[0], "rec", 3)) | 1136 | if (argc && !strncmp(argv[0], "rec", 3)) |
1179 | return __cmd_record(argc, argv); | 1137 | return __cmd_record(argc, argv); |
1180 | else if (argc) | 1138 | else if (argc) |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e23bc74e734f..31f2e597800c 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -20,8 +20,10 @@ | |||
20 | 20 | ||
21 | #include "perf.h" | 21 | #include "perf.h" |
22 | 22 | ||
23 | #include "util/symbol.h" | ||
24 | #include "util/color.h" | 23 | #include "util/color.h" |
24 | #include "util/session.h" | ||
25 | #include "util/symbol.h" | ||
26 | #include "util/thread.h" | ||
25 | #include "util/util.h" | 27 | #include "util/util.h" |
26 | #include <linux/rbtree.h> | 28 | #include <linux/rbtree.h> |
27 | #include "util/parse-options.h" | 29 | #include "util/parse-options.h" |
@@ -54,26 +56,30 @@ | |||
54 | 56 | ||
55 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | 57 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; |
56 | 58 | ||
57 | static int system_wide = 0; | 59 | static int system_wide = 0; |
58 | 60 | ||
59 | static int default_interval = 100000; | 61 | static int default_interval = 0; |
60 | 62 | ||
61 | static int count_filter = 5; | 63 | static int count_filter = 5; |
62 | static int print_entries = 15; | 64 | static int print_entries; |
63 | 65 | ||
64 | static int target_pid = -1; | 66 | static int target_pid = -1; |
65 | static int inherit = 0; | 67 | static int inherit = 0; |
66 | static int profile_cpu = -1; | 68 | static int profile_cpu = -1; |
67 | static int nr_cpus = 0; | 69 | static int nr_cpus = 0; |
68 | static unsigned int realtime_prio = 0; | 70 | static unsigned int realtime_prio = 0; |
69 | static int group = 0; | 71 | static int group = 0; |
70 | static unsigned int page_size; | 72 | static unsigned int page_size; |
71 | static unsigned int mmap_pages = 16; | 73 | static unsigned int mmap_pages = 16; |
72 | static int freq = 0; | 74 | static int freq = 1000; /* 1 KHz */ |
73 | 75 | ||
74 | static int delay_secs = 2; | 76 | static int delay_secs = 2; |
75 | static int zero; | 77 | static int zero = 0; |
76 | static int dump_symtab; | 78 | static int dump_symtab = 0; |
79 | |||
80 | static bool hide_kernel_symbols = false; | ||
81 | static bool hide_user_symbols = false; | ||
82 | static struct winsize winsize; | ||
77 | 83 | ||
78 | /* | 84 | /* |
79 | * Source | 85 | * Source |
@@ -86,87 +92,130 @@ struct source_line { | |||
86 | struct source_line *next; | 92 | struct source_line *next; |
87 | }; | 93 | }; |
88 | 94 | ||
89 | static char *sym_filter = NULL; | 95 | static char *sym_filter = NULL; |
90 | struct sym_entry *sym_filter_entry = NULL; | 96 | struct sym_entry *sym_filter_entry = NULL; |
91 | static int sym_pcnt_filter = 5; | 97 | struct sym_entry *sym_filter_entry_sched = NULL; |
92 | static int sym_counter = 0; | 98 | static int sym_pcnt_filter = 5; |
93 | static int display_weighted = -1; | 99 | static int sym_counter = 0; |
100 | static int display_weighted = -1; | ||
94 | 101 | ||
95 | /* | 102 | /* |
96 | * Symbols | 103 | * Symbols |
97 | */ | 104 | */ |
98 | 105 | ||
99 | static u64 min_ip; | 106 | struct sym_entry_source { |
100 | static u64 max_ip = -1ll; | 107 | struct source_line *source; |
108 | struct source_line *lines; | ||
109 | struct source_line **lines_tail; | ||
110 | pthread_mutex_t lock; | ||
111 | }; | ||
101 | 112 | ||
102 | struct sym_entry { | 113 | struct sym_entry { |
103 | struct rb_node rb_node; | 114 | struct rb_node rb_node; |
104 | struct list_head node; | 115 | struct list_head node; |
105 | unsigned long count[MAX_COUNTERS]; | ||
106 | unsigned long snap_count; | 116 | unsigned long snap_count; |
107 | double weight; | 117 | double weight; |
108 | int skip; | 118 | int skip; |
109 | struct source_line *source; | 119 | u16 name_len; |
110 | struct source_line *lines; | 120 | u8 origin; |
111 | struct source_line **lines_tail; | 121 | struct map *map; |
112 | pthread_mutex_t source_lock; | 122 | struct sym_entry_source *src; |
123 | unsigned long count[0]; | ||
113 | }; | 124 | }; |
114 | 125 | ||
115 | /* | 126 | /* |
116 | * Source functions | 127 | * Source functions |
117 | */ | 128 | */ |
118 | 129 | ||
130 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
131 | { | ||
132 | return ((void *)self) + symbol_conf.priv_size; | ||
133 | } | ||
134 | |||
135 | static void get_term_dimensions(struct winsize *ws) | ||
136 | { | ||
137 | char *s = getenv("LINES"); | ||
138 | |||
139 | if (s != NULL) { | ||
140 | ws->ws_row = atoi(s); | ||
141 | s = getenv("COLUMNS"); | ||
142 | if (s != NULL) { | ||
143 | ws->ws_col = atoi(s); | ||
144 | if (ws->ws_row && ws->ws_col) | ||
145 | return; | ||
146 | } | ||
147 | } | ||
148 | #ifdef TIOCGWINSZ | ||
149 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && | ||
150 | ws->ws_row && ws->ws_col) | ||
151 | return; | ||
152 | #endif | ||
153 | ws->ws_row = 25; | ||
154 | ws->ws_col = 80; | ||
155 | } | ||
156 | |||
157 | static void update_print_entries(struct winsize *ws) | ||
158 | { | ||
159 | print_entries = ws->ws_row; | ||
160 | |||
161 | if (print_entries > 9) | ||
162 | print_entries -= 9; | ||
163 | } | ||
164 | |||
165 | static void sig_winch_handler(int sig __used) | ||
166 | { | ||
167 | get_term_dimensions(&winsize); | ||
168 | update_print_entries(&winsize); | ||
169 | } | ||
170 | |||
119 | static void parse_source(struct sym_entry *syme) | 171 | static void parse_source(struct sym_entry *syme) |
120 | { | 172 | { |
121 | struct symbol *sym; | 173 | struct symbol *sym; |
122 | struct module *module; | 174 | struct sym_entry_source *source; |
123 | struct section *section = NULL; | 175 | struct map *map; |
124 | FILE *file; | 176 | FILE *file; |
125 | char command[PATH_MAX*2]; | 177 | char command[PATH_MAX*2]; |
126 | const char *path = vmlinux_name; | 178 | const char *path; |
127 | u64 start, end, len; | 179 | u64 len; |
128 | 180 | ||
129 | if (!syme) | 181 | if (!syme) |
130 | return; | 182 | return; |
131 | 183 | ||
132 | if (syme->lines) { | 184 | if (syme->src == NULL) { |
133 | pthread_mutex_lock(&syme->source_lock); | 185 | syme->src = zalloc(sizeof(*source)); |
134 | goto out_assign; | 186 | if (syme->src == NULL) |
187 | return; | ||
188 | pthread_mutex_init(&syme->src->lock, NULL); | ||
135 | } | 189 | } |
136 | 190 | ||
137 | sym = (struct symbol *)(syme + 1); | 191 | source = syme->src; |
138 | module = sym->module; | ||
139 | |||
140 | if (module) | ||
141 | path = module->path; | ||
142 | if (!path) | ||
143 | return; | ||
144 | 192 | ||
145 | start = sym->obj_start; | 193 | if (source->lines) { |
146 | if (!start) | 194 | pthread_mutex_lock(&source->lock); |
147 | start = sym->start; | 195 | goto out_assign; |
148 | |||
149 | if (module) { | ||
150 | section = module->sections->find_section(module->sections, ".text"); | ||
151 | if (section) | ||
152 | start -= section->vma; | ||
153 | } | 196 | } |
154 | 197 | ||
155 | end = start + sym->end - sym->start + 1; | 198 | sym = sym_entry__symbol(syme); |
199 | map = syme->map; | ||
200 | path = map->dso->long_name; | ||
201 | |||
156 | len = sym->end - sym->start; | 202 | len = sym->end - sym->start; |
157 | 203 | ||
158 | sprintf(command, "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s", start, end, path); | 204 | sprintf(command, |
205 | "objdump --start-address=%#0*Lx --stop-address=%#0*Lx -dS %s", | ||
206 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start), | ||
207 | BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path); | ||
159 | 208 | ||
160 | file = popen(command, "r"); | 209 | file = popen(command, "r"); |
161 | if (!file) | 210 | if (!file) |
162 | return; | 211 | return; |
163 | 212 | ||
164 | pthread_mutex_lock(&syme->source_lock); | 213 | pthread_mutex_lock(&source->lock); |
165 | syme->lines_tail = &syme->lines; | 214 | source->lines_tail = &source->lines; |
166 | while (!feof(file)) { | 215 | while (!feof(file)) { |
167 | struct source_line *src; | 216 | struct source_line *src; |
168 | size_t dummy = 0; | 217 | size_t dummy = 0; |
169 | char *c; | 218 | char *c, *sep; |
170 | 219 | ||
171 | src = malloc(sizeof(struct source_line)); | 220 | src = malloc(sizeof(struct source_line)); |
172 | assert(src != NULL); | 221 | assert(src != NULL); |
@@ -182,24 +231,19 @@ static void parse_source(struct sym_entry *syme) | |||
182 | *c = 0; | 231 | *c = 0; |
183 | 232 | ||
184 | src->next = NULL; | 233 | src->next = NULL; |
185 | *syme->lines_tail = src; | 234 | *source->lines_tail = src; |
186 | syme->lines_tail = &src->next; | 235 | source->lines_tail = &src->next; |
187 | 236 | ||
188 | if (strlen(src->line)>8 && src->line[8] == ':') { | 237 | src->eip = strtoull(src->line, &sep, 16); |
189 | src->eip = strtoull(src->line, NULL, 16); | 238 | if (*sep == ':') |
190 | if (section) | 239 | src->eip = map__objdump_2ip(map, src->eip); |
191 | src->eip += section->vma; | 240 | else /* this line has no ip info (e.g. source line) */ |
192 | } | 241 | src->eip = 0; |
193 | if (strlen(src->line)>8 && src->line[16] == ':') { | ||
194 | src->eip = strtoull(src->line, NULL, 16); | ||
195 | if (section) | ||
196 | src->eip += section->vma; | ||
197 | } | ||
198 | } | 242 | } |
199 | pclose(file); | 243 | pclose(file); |
200 | out_assign: | 244 | out_assign: |
201 | sym_filter_entry = syme; | 245 | sym_filter_entry = syme; |
202 | pthread_mutex_unlock(&syme->source_lock); | 246 | pthread_mutex_unlock(&source->lock); |
203 | } | 247 | } |
204 | 248 | ||
205 | static void __zero_source_counters(struct sym_entry *syme) | 249 | static void __zero_source_counters(struct sym_entry *syme) |
@@ -207,7 +251,7 @@ static void __zero_source_counters(struct sym_entry *syme) | |||
207 | int i; | 251 | int i; |
208 | struct source_line *line; | 252 | struct source_line *line; |
209 | 253 | ||
210 | line = syme->lines; | 254 | line = syme->src->lines; |
211 | while (line) { | 255 | while (line) { |
212 | for (i = 0; i < nr_counters; i++) | 256 | for (i = 0; i < nr_counters; i++) |
213 | line->count[i] = 0; | 257 | line->count[i] = 0; |
@@ -222,13 +266,16 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
222 | if (syme != sym_filter_entry) | 266 | if (syme != sym_filter_entry) |
223 | return; | 267 | return; |
224 | 268 | ||
225 | if (pthread_mutex_trylock(&syme->source_lock)) | 269 | if (pthread_mutex_trylock(&syme->src->lock)) |
226 | return; | 270 | return; |
227 | 271 | ||
228 | if (!syme->source) | 272 | if (syme->src == NULL || syme->src->source == NULL) |
229 | goto out_unlock; | 273 | goto out_unlock; |
230 | 274 | ||
231 | for (line = syme->lines; line; line = line->next) { | 275 | for (line = syme->src->lines; line; line = line->next) { |
276 | /* skip lines without IP info */ | ||
277 | if (line->eip == 0) | ||
278 | continue; | ||
232 | if (line->eip == ip) { | 279 | if (line->eip == ip) { |
233 | line->count[counter]++; | 280 | line->count[counter]++; |
234 | break; | 281 | break; |
@@ -237,32 +284,28 @@ static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip) | |||
237 | break; | 284 | break; |
238 | } | 285 | } |
239 | out_unlock: | 286 | out_unlock: |
240 | pthread_mutex_unlock(&syme->source_lock); | 287 | pthread_mutex_unlock(&syme->src->lock); |
241 | } | 288 | } |
242 | 289 | ||
290 | #define PATTERN_LEN (BITS_PER_LONG / 4 + 2) | ||
291 | |||
243 | static void lookup_sym_source(struct sym_entry *syme) | 292 | static void lookup_sym_source(struct sym_entry *syme) |
244 | { | 293 | { |
245 | struct symbol *symbol = (struct symbol *)(syme + 1); | 294 | struct symbol *symbol = sym_entry__symbol(syme); |
246 | struct source_line *line; | 295 | struct source_line *line; |
247 | char pattern[PATH_MAX]; | 296 | char pattern[PATTERN_LEN + 1]; |
248 | char *idx; | ||
249 | 297 | ||
250 | sprintf(pattern, "<%s>:", symbol->name); | 298 | sprintf(pattern, "%0*Lx <", BITS_PER_LONG / 4, |
299 | map__rip_2objdump(syme->map, symbol->start)); | ||
251 | 300 | ||
252 | if (symbol->module) { | 301 | pthread_mutex_lock(&syme->src->lock); |
253 | idx = strstr(pattern, "\t"); | 302 | for (line = syme->src->lines; line; line = line->next) { |
254 | if (idx) | 303 | if (memcmp(line->line, pattern, PATTERN_LEN) == 0) { |
255 | *idx = 0; | 304 | syme->src->source = line; |
256 | } | ||
257 | |||
258 | pthread_mutex_lock(&syme->source_lock); | ||
259 | for (line = syme->lines; line; line = line->next) { | ||
260 | if (strstr(line->line, pattern)) { | ||
261 | syme->source = line; | ||
262 | break; | 305 | break; |
263 | } | 306 | } |
264 | } | 307 | } |
265 | pthread_mutex_unlock(&syme->source_lock); | 308 | pthread_mutex_unlock(&syme->src->lock); |
266 | } | 309 | } |
267 | 310 | ||
268 | static void show_lines(struct source_line *queue, int count, int total) | 311 | static void show_lines(struct source_line *queue, int count, int total) |
@@ -292,24 +335,24 @@ static void show_details(struct sym_entry *syme) | |||
292 | if (!syme) | 335 | if (!syme) |
293 | return; | 336 | return; |
294 | 337 | ||
295 | if (!syme->source) | 338 | if (!syme->src->source) |
296 | lookup_sym_source(syme); | 339 | lookup_sym_source(syme); |
297 | 340 | ||
298 | if (!syme->source) | 341 | if (!syme->src->source) |
299 | return; | 342 | return; |
300 | 343 | ||
301 | symbol = (struct symbol *)(syme + 1); | 344 | symbol = sym_entry__symbol(syme); |
302 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); | 345 | printf("Showing %s for %s\n", event_name(sym_counter), symbol->name); |
303 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); | 346 | printf(" Events Pcnt (>=%d%%)\n", sym_pcnt_filter); |
304 | 347 | ||
305 | pthread_mutex_lock(&syme->source_lock); | 348 | pthread_mutex_lock(&syme->src->lock); |
306 | line = syme->source; | 349 | line = syme->src->source; |
307 | while (line) { | 350 | while (line) { |
308 | total += line->count[sym_counter]; | 351 | total += line->count[sym_counter]; |
309 | line = line->next; | 352 | line = line->next; |
310 | } | 353 | } |
311 | 354 | ||
312 | line = syme->source; | 355 | line = syme->src->source; |
313 | while (line) { | 356 | while (line) { |
314 | float pcnt = 0.0; | 357 | float pcnt = 0.0; |
315 | 358 | ||
@@ -334,13 +377,13 @@ static void show_details(struct sym_entry *syme) | |||
334 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; | 377 | line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8; |
335 | line = line->next; | 378 | line = line->next; |
336 | } | 379 | } |
337 | pthread_mutex_unlock(&syme->source_lock); | 380 | pthread_mutex_unlock(&syme->src->lock); |
338 | if (more) | 381 | if (more) |
339 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | 382 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); |
340 | } | 383 | } |
341 | 384 | ||
342 | /* | 385 | /* |
343 | * Symbols will be added here in record_ip and will get out | 386 | * Symbols will be added here in event__process_sample and will get out |
344 | * after decayed. | 387 | * after decayed. |
345 | */ | 388 | */ |
346 | static LIST_HEAD(active_symbols); | 389 | static LIST_HEAD(active_symbols); |
@@ -411,6 +454,8 @@ static void print_sym_table(void) | |||
411 | struct sym_entry *syme, *n; | 454 | struct sym_entry *syme, *n; |
412 | struct rb_root tmp = RB_ROOT; | 455 | struct rb_root tmp = RB_ROOT; |
413 | struct rb_node *nd; | 456 | struct rb_node *nd; |
457 | int sym_width = 0, dso_width = 0, max_dso_width; | ||
458 | const int win_width = winsize.ws_col - 1; | ||
414 | 459 | ||
415 | samples = userspace_samples = 0; | 460 | samples = userspace_samples = 0; |
416 | 461 | ||
@@ -422,6 +467,14 @@ static void print_sym_table(void) | |||
422 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 467 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
423 | syme->snap_count = syme->count[snap]; | 468 | syme->snap_count = syme->count[snap]; |
424 | if (syme->snap_count != 0) { | 469 | if (syme->snap_count != 0) { |
470 | |||
471 | if ((hide_user_symbols && | ||
472 | syme->origin == PERF_RECORD_MISC_USER) || | ||
473 | (hide_kernel_symbols && | ||
474 | syme->origin == PERF_RECORD_MISC_KERNEL)) { | ||
475 | list_remove_active_sym(syme); | ||
476 | continue; | ||
477 | } | ||
425 | syme->weight = sym_weight(syme); | 478 | syme->weight = sym_weight(syme); |
426 | rb_insert_active_sym(&tmp, syme); | 479 | rb_insert_active_sym(&tmp, syme); |
427 | sum_ksamples += syme->snap_count; | 480 | sum_ksamples += syme->snap_count; |
@@ -434,8 +487,7 @@ static void print_sym_table(void) | |||
434 | 487 | ||
435 | puts(CONSOLE_CLEAR); | 488 | puts(CONSOLE_CLEAR); |
436 | 489 | ||
437 | printf( | 490 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
438 | "------------------------------------------------------------------------------\n"); | ||
439 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", | 491 | printf( " PerfTop:%8.0f irqs/sec kernel:%4.1f%% [", |
440 | samples_per_sec, | 492 | samples_per_sec, |
441 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); | 493 | 100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec))); |
@@ -473,33 +525,57 @@ static void print_sym_table(void) | |||
473 | printf(", %d CPUs)\n", nr_cpus); | 525 | printf(", %d CPUs)\n", nr_cpus); |
474 | } | 526 | } |
475 | 527 | ||
476 | printf("------------------------------------------------------------------------------\n\n"); | 528 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
477 | 529 | ||
478 | if (sym_filter_entry) { | 530 | if (sym_filter_entry) { |
479 | show_details(sym_filter_entry); | 531 | show_details(sym_filter_entry); |
480 | return; | 532 | return; |
481 | } | 533 | } |
482 | 534 | ||
535 | /* | ||
536 | * Find the longest symbol name that will be displayed | ||
537 | */ | ||
538 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | ||
539 | syme = rb_entry(nd, struct sym_entry, rb_node); | ||
540 | if (++printed > print_entries || | ||
541 | (int)syme->snap_count < count_filter) | ||
542 | continue; | ||
543 | |||
544 | if (syme->map->dso->long_name_len > dso_width) | ||
545 | dso_width = syme->map->dso->long_name_len; | ||
546 | |||
547 | if (syme->name_len > sym_width) | ||
548 | sym_width = syme->name_len; | ||
549 | } | ||
550 | |||
551 | printed = 0; | ||
552 | |||
553 | max_dso_width = winsize.ws_col - sym_width - 29; | ||
554 | if (dso_width > max_dso_width) | ||
555 | dso_width = max_dso_width; | ||
556 | putchar('\n'); | ||
483 | if (nr_counters == 1) | 557 | if (nr_counters == 1) |
484 | printf(" samples pcnt"); | 558 | printf(" samples pcnt"); |
485 | else | 559 | else |
486 | printf(" weight samples pcnt"); | 560 | printf(" weight samples pcnt"); |
487 | 561 | ||
488 | if (verbose) | 562 | if (verbose) |
489 | printf(" RIP "); | 563 | printf(" RIP "); |
490 | printf(" kernel function\n"); | 564 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); |
491 | printf(" %s _______ _____", | 565 | printf(" %s _______ _____", |
492 | nr_counters == 1 ? " " : "______"); | 566 | nr_counters == 1 ? " " : "______"); |
493 | if (verbose) | 567 | if (verbose) |
494 | printf(" ________________"); | 568 | printf(" ________________"); |
495 | printf(" _______________\n\n"); | 569 | printf(" %-*.*s", sym_width, sym_width, graph_line); |
570 | printf(" %-*.*s", dso_width, dso_width, graph_line); | ||
571 | puts("\n"); | ||
496 | 572 | ||
497 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | 573 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
498 | struct symbol *sym; | 574 | struct symbol *sym; |
499 | double pcnt; | 575 | double pcnt; |
500 | 576 | ||
501 | syme = rb_entry(nd, struct sym_entry, rb_node); | 577 | syme = rb_entry(nd, struct sym_entry, rb_node); |
502 | sym = (struct symbol *)(syme + 1); | 578 | sym = sym_entry__symbol(syme); |
503 | 579 | ||
504 | if (++printed > print_entries || (int)syme->snap_count < count_filter) | 580 | if (++printed > print_entries || (int)syme->snap_count < count_filter) |
505 | continue; | 581 | continue; |
@@ -508,17 +584,18 @@ static void print_sym_table(void) | |||
508 | sum_ksamples)); | 584 | sum_ksamples)); |
509 | 585 | ||
510 | if (nr_counters == 1 || !display_weighted) | 586 | if (nr_counters == 1 || !display_weighted) |
511 | printf("%20.2f - ", syme->weight); | 587 | printf("%20.2f ", syme->weight); |
512 | else | 588 | else |
513 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); | 589 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
514 | 590 | ||
515 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); | 591 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); |
516 | if (verbose) | 592 | if (verbose) |
517 | printf(" - %016llx", sym->start); | 593 | printf(" %016llx", sym->start); |
518 | printf(" : %s", sym->name); | 594 | printf(" %-*.*s", sym_width, sym_width, sym->name); |
519 | if (sym->module) | 595 | printf(" %-*.*s\n", dso_width, dso_width, |
520 | printf("\t[%s]", sym->module->name); | 596 | dso_width >= syme->map->dso->long_name_len ? |
521 | printf("\n"); | 597 | syme->map->dso->long_name : |
598 | syme->map->dso->short_name); | ||
522 | } | 599 | } |
523 | } | 600 | } |
524 | 601 | ||
@@ -565,10 +642,10 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
565 | 642 | ||
566 | /* zero counters of active symbol */ | 643 | /* zero counters of active symbol */ |
567 | if (syme) { | 644 | if (syme) { |
568 | pthread_mutex_lock(&syme->source_lock); | 645 | pthread_mutex_lock(&syme->src->lock); |
569 | __zero_source_counters(syme); | 646 | __zero_source_counters(syme); |
570 | *target = NULL; | 647 | *target = NULL; |
571 | pthread_mutex_unlock(&syme->source_lock); | 648 | pthread_mutex_unlock(&syme->src->lock); |
572 | } | 649 | } |
573 | 650 | ||
574 | fprintf(stdout, "\n%s: ", msg); | 651 | fprintf(stdout, "\n%s: ", msg); |
@@ -584,7 +661,7 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
584 | pthread_mutex_unlock(&active_symbols_lock); | 661 | pthread_mutex_unlock(&active_symbols_lock); |
585 | 662 | ||
586 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 663 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
587 | struct symbol *sym = (struct symbol *)(syme + 1); | 664 | struct symbol *sym = sym_entry__symbol(syme); |
588 | 665 | ||
589 | if (!strcmp(buf, sym->name)) { | 666 | if (!strcmp(buf, sym->name)) { |
590 | found = syme; | 667 | found = syme; |
@@ -593,7 +670,7 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
593 | } | 670 | } |
594 | 671 | ||
595 | if (!found) { | 672 | if (!found) { |
596 | fprintf(stderr, "Sorry, %s is not active.\n", sym_filter); | 673 | fprintf(stderr, "Sorry, %s is not active.\n", buf); |
597 | sleep(1); | 674 | sleep(1); |
598 | return; | 675 | return; |
599 | } else | 676 | } else |
@@ -608,7 +685,7 @@ static void print_mapped_keys(void) | |||
608 | char *name = NULL; | 685 | char *name = NULL; |
609 | 686 | ||
610 | if (sym_filter_entry) { | 687 | if (sym_filter_entry) { |
611 | struct symbol *sym = (struct symbol *)(sym_filter_entry+1); | 688 | struct symbol *sym = sym_entry__symbol(sym_filter_entry); |
612 | name = sym->name; | 689 | name = sym->name; |
613 | } | 690 | } |
614 | 691 | ||
@@ -621,15 +698,19 @@ static void print_mapped_keys(void) | |||
621 | 698 | ||
622 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 699 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); |
623 | 700 | ||
624 | if (vmlinux_name) { | 701 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); |
625 | fprintf(stdout, "\t[F] annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter); | 702 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
626 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 703 | fprintf(stdout, "\t[S] stop annotation.\n"); |
627 | fprintf(stdout, "\t[S] stop annotation.\n"); | ||
628 | } | ||
629 | 704 | ||
630 | if (nr_counters > 1) | 705 | if (nr_counters > 1) |
631 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 706 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
632 | 707 | ||
708 | fprintf(stdout, | ||
709 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | ||
710 | hide_kernel_symbols ? "yes" : "no"); | ||
711 | fprintf(stdout, | ||
712 | "\t[U] hide user symbols. \t(%s)\n", | ||
713 | hide_user_symbols ? "yes" : "no"); | ||
633 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); | 714 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); |
634 | fprintf(stdout, "\t[qQ] quit.\n"); | 715 | fprintf(stdout, "\t[qQ] quit.\n"); |
635 | } | 716 | } |
@@ -643,14 +724,15 @@ static int key_mapped(int c) | |||
643 | case 'z': | 724 | case 'z': |
644 | case 'q': | 725 | case 'q': |
645 | case 'Q': | 726 | case 'Q': |
727 | case 'K': | ||
728 | case 'U': | ||
729 | case 'F': | ||
730 | case 's': | ||
731 | case 'S': | ||
646 | return 1; | 732 | return 1; |
647 | case 'E': | 733 | case 'E': |
648 | case 'w': | 734 | case 'w': |
649 | return nr_counters > 1 ? 1 : 0; | 735 | return nr_counters > 1 ? 1 : 0; |
650 | case 'F': | ||
651 | case 's': | ||
652 | case 'S': | ||
653 | return vmlinux_name ? 1 : 0; | ||
654 | default: | 736 | default: |
655 | break; | 737 | break; |
656 | } | 738 | } |
@@ -691,6 +773,11 @@ static void handle_keypress(int c) | |||
691 | break; | 773 | break; |
692 | case 'e': | 774 | case 'e': |
693 | prompt_integer(&print_entries, "Enter display entries (lines)"); | 775 | prompt_integer(&print_entries, "Enter display entries (lines)"); |
776 | if (print_entries == 0) { | ||
777 | sig_winch_handler(SIGWINCH); | ||
778 | signal(SIGWINCH, sig_winch_handler); | ||
779 | } else | ||
780 | signal(SIGWINCH, SIG_DFL); | ||
694 | break; | 781 | break; |
695 | case 'E': | 782 | case 'E': |
696 | if (nr_counters > 1) { | 783 | if (nr_counters > 1) { |
@@ -715,9 +802,14 @@ static void handle_keypress(int c) | |||
715 | case 'F': | 802 | case 'F': |
716 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | 803 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); |
717 | break; | 804 | break; |
805 | case 'K': | ||
806 | hide_kernel_symbols = !hide_kernel_symbols; | ||
807 | break; | ||
718 | case 'q': | 808 | case 'q': |
719 | case 'Q': | 809 | case 'Q': |
720 | printf("exiting.\n"); | 810 | printf("exiting.\n"); |
811 | if (dump_symtab) | ||
812 | dsos__fprintf(stderr); | ||
721 | exit(0); | 813 | exit(0); |
722 | case 's': | 814 | case 's': |
723 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); | 815 | prompt_symbol(&sym_filter_entry, "Enter details symbol"); |
@@ -728,12 +820,15 @@ static void handle_keypress(int c) | |||
728 | else { | 820 | else { |
729 | struct sym_entry *syme = sym_filter_entry; | 821 | struct sym_entry *syme = sym_filter_entry; |
730 | 822 | ||
731 | pthread_mutex_lock(&syme->source_lock); | 823 | pthread_mutex_lock(&syme->src->lock); |
732 | sym_filter_entry = NULL; | 824 | sym_filter_entry = NULL; |
733 | __zero_source_counters(syme); | 825 | __zero_source_counters(syme); |
734 | pthread_mutex_unlock(&syme->source_lock); | 826 | pthread_mutex_unlock(&syme->src->lock); |
735 | } | 827 | } |
736 | break; | 828 | break; |
829 | case 'U': | ||
830 | hide_user_symbols = !hide_user_symbols; | ||
831 | break; | ||
737 | case 'w': | 832 | case 'w': |
738 | display_weighted = ~display_weighted; | 833 | display_weighted = ~display_weighted; |
739 | break; | 834 | break; |
@@ -790,7 +885,7 @@ static const char *skip_symbols[] = { | |||
790 | NULL | 885 | NULL |
791 | }; | 886 | }; |
792 | 887 | ||
793 | static int symbol_filter(struct dso *self, struct symbol *sym) | 888 | static int symbol_filter(struct map *map, struct symbol *sym) |
794 | { | 889 | { |
795 | struct sym_entry *syme; | 890 | struct sym_entry *syme; |
796 | const char *name = sym->name; | 891 | const char *name = sym->name; |
@@ -812,10 +907,15 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
812 | strstr(name, "_text_end")) | 907 | strstr(name, "_text_end")) |
813 | return 1; | 908 | return 1; |
814 | 909 | ||
815 | syme = dso__sym_priv(self, sym); | 910 | syme = symbol__priv(sym); |
816 | pthread_mutex_init(&syme->source_lock, NULL); | 911 | syme->map = map; |
817 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) | 912 | syme->src = NULL; |
818 | sym_filter_entry = syme; | 913 | |
914 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) { | ||
915 | /* schedule initial sym_filter_entry setup */ | ||
916 | sym_filter_entry_sched = syme; | ||
917 | sym_filter = NULL; | ||
918 | } | ||
819 | 919 | ||
820 | for (i = 0; skip_symbols[i]; i++) { | 920 | for (i = 0; skip_symbols[i]; i++) { |
821 | if (!strcmp(skip_symbols[i], name)) { | 921 | if (!strcmp(skip_symbols[i], name)) { |
@@ -824,75 +924,99 @@ static int symbol_filter(struct dso *self, struct symbol *sym) | |||
824 | } | 924 | } |
825 | } | 925 | } |
826 | 926 | ||
927 | if (!syme->skip) | ||
928 | syme->name_len = strlen(sym->name); | ||
929 | |||
827 | return 0; | 930 | return 0; |
828 | } | 931 | } |
829 | 932 | ||
830 | static int parse_symbols(void) | 933 | static void event__process_sample(const event_t *self, |
934 | struct perf_session *session, int counter) | ||
831 | { | 935 | { |
832 | struct rb_node *node; | 936 | u64 ip = self->ip.ip; |
833 | struct symbol *sym; | 937 | struct sym_entry *syme; |
834 | int use_modules = vmlinux_name ? 1 : 0; | 938 | struct addr_location al; |
835 | 939 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | |
836 | kernel_dso = dso__new("[kernel]", sizeof(struct sym_entry)); | ||
837 | if (kernel_dso == NULL) | ||
838 | return -1; | ||
839 | |||
840 | if (dso__load_kernel(kernel_dso, vmlinux_name, symbol_filter, verbose, use_modules) <= 0) | ||
841 | goto out_delete_dso; | ||
842 | 940 | ||
843 | node = rb_first(&kernel_dso->syms); | 941 | ++samples; |
844 | sym = rb_entry(node, struct symbol, rb_node); | ||
845 | min_ip = sym->start; | ||
846 | 942 | ||
847 | node = rb_last(&kernel_dso->syms); | 943 | switch (origin) { |
848 | sym = rb_entry(node, struct symbol, rb_node); | 944 | case PERF_RECORD_MISC_USER: |
849 | max_ip = sym->end; | 945 | ++userspace_samples; |
946 | if (hide_user_symbols) | ||
947 | return; | ||
948 | break; | ||
949 | case PERF_RECORD_MISC_KERNEL: | ||
950 | if (hide_kernel_symbols) | ||
951 | return; | ||
952 | break; | ||
953 | default: | ||
954 | return; | ||
955 | } | ||
850 | 956 | ||
851 | if (dump_symtab) | 957 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || |
852 | dso__fprintf(kernel_dso, stderr); | 958 | al.filtered) |
959 | return; | ||
853 | 960 | ||
854 | return 0; | 961 | if (al.sym == NULL) { |
962 | /* | ||
963 | * As we do lazy loading of symtabs we only will know if the | ||
964 | * specified vmlinux file is invalid when we actually have a | ||
965 | * hit in kernel space and then try to load it. So if we get | ||
966 | * here and there are _no_ symbols in the DSO backing the | ||
967 | * kernel map, bail out. | ||
968 | * | ||
969 | * We may never get here, for instance, if we use -K/ | ||
970 | * --hide-kernel-symbols, even if the user specifies an | ||
971 | * invalid --vmlinux ;-) | ||
972 | */ | ||
973 | if (al.map == session->vmlinux_maps[MAP__FUNCTION] && | ||
974 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | ||
975 | pr_err("The %s file can't be used\n", | ||
976 | symbol_conf.vmlinux_name); | ||
977 | exit(1); | ||
978 | } | ||
855 | 979 | ||
856 | out_delete_dso: | 980 | return; |
857 | dso__delete(kernel_dso); | 981 | } |
858 | kernel_dso = NULL; | ||
859 | return -1; | ||
860 | } | ||
861 | 982 | ||
862 | /* | 983 | /* let's see, whether we need to install initial sym_filter_entry */ |
863 | * Binary search in the histogram table and record the hit: | 984 | if (sym_filter_entry_sched) { |
864 | */ | 985 | sym_filter_entry = sym_filter_entry_sched; |
865 | static void record_ip(u64 ip, int counter) | 986 | sym_filter_entry_sched = NULL; |
866 | { | 987 | parse_source(sym_filter_entry); |
867 | struct symbol *sym = dso__find_symbol(kernel_dso, ip); | ||
868 | |||
869 | if (sym != NULL) { | ||
870 | struct sym_entry *syme = dso__sym_priv(kernel_dso, sym); | ||
871 | |||
872 | if (!syme->skip) { | ||
873 | syme->count[counter]++; | ||
874 | record_precise_ip(syme, counter, ip); | ||
875 | pthread_mutex_lock(&active_symbols_lock); | ||
876 | if (list_empty(&syme->node) || !syme->node.next) | ||
877 | __list_insert_active_sym(syme); | ||
878 | pthread_mutex_unlock(&active_symbols_lock); | ||
879 | return; | ||
880 | } | ||
881 | } | 988 | } |
882 | 989 | ||
883 | samples--; | 990 | syme = symbol__priv(al.sym); |
991 | if (!syme->skip) { | ||
992 | syme->count[counter]++; | ||
993 | syme->origin = origin; | ||
994 | record_precise_ip(syme, counter, ip); | ||
995 | pthread_mutex_lock(&active_symbols_lock); | ||
996 | if (list_empty(&syme->node) || !syme->node.next) | ||
997 | __list_insert_active_sym(syme); | ||
998 | pthread_mutex_unlock(&active_symbols_lock); | ||
999 | } | ||
884 | } | 1000 | } |
885 | 1001 | ||
886 | static void process_event(u64 ip, int counter, int user) | 1002 | static int event__process(event_t *event, struct perf_session *session) |
887 | { | 1003 | { |
888 | samples++; | 1004 | switch (event->header.type) { |
889 | 1005 | case PERF_RECORD_COMM: | |
890 | if (user) { | 1006 | event__process_comm(event, session); |
891 | userspace_samples++; | 1007 | break; |
892 | return; | 1008 | case PERF_RECORD_MMAP: |
1009 | event__process_mmap(event, session); | ||
1010 | break; | ||
1011 | case PERF_RECORD_FORK: | ||
1012 | case PERF_RECORD_EXIT: | ||
1013 | event__process_task(event, session); | ||
1014 | break; | ||
1015 | default: | ||
1016 | break; | ||
893 | } | 1017 | } |
894 | 1018 | ||
895 | record_ip(ip, counter); | 1019 | return 0; |
896 | } | 1020 | } |
897 | 1021 | ||
898 | struct mmap_data { | 1022 | struct mmap_data { |
@@ -913,17 +1037,14 @@ static unsigned int mmap_read_head(struct mmap_data *md) | |||
913 | return head; | 1037 | return head; |
914 | } | 1038 | } |
915 | 1039 | ||
916 | struct timeval last_read, this_read; | 1040 | static void perf_session__mmap_read_counter(struct perf_session *self, |
917 | 1041 | struct mmap_data *md) | |
918 | static void mmap_read_counter(struct mmap_data *md) | ||
919 | { | 1042 | { |
920 | unsigned int head = mmap_read_head(md); | 1043 | unsigned int head = mmap_read_head(md); |
921 | unsigned int old = md->prev; | 1044 | unsigned int old = md->prev; |
922 | unsigned char *data = md->base + page_size; | 1045 | unsigned char *data = md->base + page_size; |
923 | int diff; | 1046 | int diff; |
924 | 1047 | ||
925 | gettimeofday(&this_read, NULL); | ||
926 | |||
927 | /* | 1048 | /* |
928 | * If we're further behind than half the buffer, there's a chance | 1049 | * If we're further behind than half the buffer, there's a chance |
929 | * the writer will bite our tail and mess up the samples under us. | 1050 | * the writer will bite our tail and mess up the samples under us. |
@@ -934,14 +1055,7 @@ static void mmap_read_counter(struct mmap_data *md) | |||
934 | */ | 1055 | */ |
935 | diff = head - old; | 1056 | diff = head - old; |
936 | if (diff > md->mask / 2 || diff < 0) { | 1057 | if (diff > md->mask / 2 || diff < 0) { |
937 | struct timeval iv; | 1058 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); |
938 | unsigned long msecs; | ||
939 | |||
940 | timersub(&this_read, &last_read, &iv); | ||
941 | msecs = iv.tv_sec*1000 + iv.tv_usec/1000; | ||
942 | |||
943 | fprintf(stderr, "WARNING: failed to keep up with mmap data." | ||
944 | " Last read %lu msecs ago.\n", msecs); | ||
945 | 1059 | ||
946 | /* | 1060 | /* |
947 | * head points to a known good entry, start there. | 1061 | * head points to a known good entry, start there. |
@@ -949,8 +1063,6 @@ static void mmap_read_counter(struct mmap_data *md) | |||
949 | old = head; | 1063 | old = head; |
950 | } | 1064 | } |
951 | 1065 | ||
952 | last_read = this_read; | ||
953 | |||
954 | for (; old != head;) { | 1066 | for (; old != head;) { |
955 | event_t *event = (event_t *)&data[old & md->mask]; | 1067 | event_t *event = (event_t *)&data[old & md->mask]; |
956 | 1068 | ||
@@ -978,13 +1090,11 @@ static void mmap_read_counter(struct mmap_data *md) | |||
978 | event = &event_copy; | 1090 | event = &event_copy; |
979 | } | 1091 | } |
980 | 1092 | ||
1093 | if (event->header.type == PERF_RECORD_SAMPLE) | ||
1094 | event__process_sample(event, self, md->counter); | ||
1095 | else | ||
1096 | event__process(event, self); | ||
981 | old += size; | 1097 | old += size; |
982 | |||
983 | if (event->header.type == PERF_RECORD_SAMPLE) { | ||
984 | int user = | ||
985 | (event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER; | ||
986 | process_event(event->ip.ip, md->counter, user); | ||
987 | } | ||
988 | } | 1098 | } |
989 | 1099 | ||
990 | md->prev = old; | 1100 | md->prev = old; |
@@ -993,13 +1103,13 @@ static void mmap_read_counter(struct mmap_data *md) | |||
993 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; | 1103 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; |
994 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | 1104 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; |
995 | 1105 | ||
996 | static void mmap_read(void) | 1106 | static void perf_session__mmap_read(struct perf_session *self) |
997 | { | 1107 | { |
998 | int i, counter; | 1108 | int i, counter; |
999 | 1109 | ||
1000 | for (i = 0; i < nr_cpus; i++) { | 1110 | for (i = 0; i < nr_cpus; i++) { |
1001 | for (counter = 0; counter < nr_counters; counter++) | 1111 | for (counter = 0; counter < nr_counters; counter++) |
1002 | mmap_read_counter(&mmap_array[i][counter]); | 1112 | perf_session__mmap_read_counter(self, &mmap_array[i][counter]); |
1003 | } | 1113 | } |
1004 | } | 1114 | } |
1005 | 1115 | ||
@@ -1018,8 +1128,15 @@ static void start_counter(int i, int counter) | |||
1018 | attr = attrs + counter; | 1128 | attr = attrs + counter; |
1019 | 1129 | ||
1020 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 1130 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
1021 | attr->freq = freq; | 1131 | |
1132 | if (freq) { | ||
1133 | attr->sample_type |= PERF_SAMPLE_PERIOD; | ||
1134 | attr->freq = 1; | ||
1135 | attr->sample_freq = freq; | ||
1136 | } | ||
1137 | |||
1022 | attr->inherit = (cpu < 0) && inherit; | 1138 | attr->inherit = (cpu < 0) && inherit; |
1139 | attr->mmap = 1; | ||
1023 | 1140 | ||
1024 | try_again: | 1141 | try_again: |
1025 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); | 1142 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); |
@@ -1077,6 +1194,18 @@ static int __cmd_top(void) | |||
1077 | pthread_t thread; | 1194 | pthread_t thread; |
1078 | int i, counter; | 1195 | int i, counter; |
1079 | int ret; | 1196 | int ret; |
1197 | /* | ||
1198 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | ||
1199 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | ||
1200 | */ | ||
1201 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false); | ||
1202 | if (session == NULL) | ||
1203 | return -ENOMEM; | ||
1204 | |||
1205 | if (target_pid != -1) | ||
1206 | event__synthesize_thread(target_pid, event__process, session); | ||
1207 | else | ||
1208 | event__synthesize_threads(event__process, session); | ||
1080 | 1209 | ||
1081 | for (i = 0; i < nr_cpus; i++) { | 1210 | for (i = 0; i < nr_cpus; i++) { |
1082 | group_fd = -1; | 1211 | group_fd = -1; |
@@ -1087,7 +1216,7 @@ static int __cmd_top(void) | |||
1087 | /* Wait for a minimal set of events before starting the snapshot */ | 1216 | /* Wait for a minimal set of events before starting the snapshot */ |
1088 | poll(event_array, nr_poll, 100); | 1217 | poll(event_array, nr_poll, 100); |
1089 | 1218 | ||
1090 | mmap_read(); | 1219 | perf_session__mmap_read(session); |
1091 | 1220 | ||
1092 | if (pthread_create(&thread, NULL, display_thread, NULL)) { | 1221 | if (pthread_create(&thread, NULL, display_thread, NULL)) { |
1093 | printf("Could not create display thread.\n"); | 1222 | printf("Could not create display thread.\n"); |
@@ -1107,7 +1236,7 @@ static int __cmd_top(void) | |||
1107 | while (1) { | 1236 | while (1) { |
1108 | int hits = samples; | 1237 | int hits = samples; |
1109 | 1238 | ||
1110 | mmap_read(); | 1239 | perf_session__mmap_read(session); |
1111 | 1240 | ||
1112 | if (hits == samples) | 1241 | if (hits == samples) |
1113 | ret = poll(event_array, nr_poll, 100); | 1242 | ret = poll(event_array, nr_poll, 100); |
@@ -1133,7 +1262,10 @@ static const struct option options[] = { | |||
1133 | "system-wide collection from all CPUs"), | 1262 | "system-wide collection from all CPUs"), |
1134 | OPT_INTEGER('C', "CPU", &profile_cpu, | 1263 | OPT_INTEGER('C', "CPU", &profile_cpu, |
1135 | "CPU to profile on"), | 1264 | "CPU to profile on"), |
1136 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), | 1265 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1266 | "file", "vmlinux pathname"), | ||
1267 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | ||
1268 | "hide kernel symbols"), | ||
1137 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | 1269 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, |
1138 | "number of mmap data pages"), | 1270 | "number of mmap data pages"), |
1139 | OPT_INTEGER('r', "realtime", &realtime_prio, | 1271 | OPT_INTEGER('r', "realtime", &realtime_prio, |
@@ -1149,13 +1281,15 @@ static const struct option options[] = { | |||
1149 | OPT_BOOLEAN('i', "inherit", &inherit, | 1281 | OPT_BOOLEAN('i', "inherit", &inherit, |
1150 | "child tasks inherit counters"), | 1282 | "child tasks inherit counters"), |
1151 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", | 1283 | OPT_STRING('s', "sym-annotate", &sym_filter, "symbol name", |
1152 | "symbol to annotate - requires -k option"), | 1284 | "symbol to annotate"), |
1153 | OPT_BOOLEAN('z', "zero", &zero, | 1285 | OPT_BOOLEAN('z', "zero", &zero, |
1154 | "zero history across updates"), | 1286 | "zero history across updates"), |
1155 | OPT_INTEGER('F', "freq", &freq, | 1287 | OPT_INTEGER('F', "freq", &freq, |
1156 | "profile at this frequency"), | 1288 | "profile at this frequency"), |
1157 | OPT_INTEGER('E', "entries", &print_entries, | 1289 | OPT_INTEGER('E', "entries", &print_entries, |
1158 | "display this many functions"), | 1290 | "display this many functions"), |
1291 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, | ||
1292 | "hide user symbols"), | ||
1159 | OPT_BOOLEAN('v', "verbose", &verbose, | 1293 | OPT_BOOLEAN('v', "verbose", &verbose, |
1160 | "be more verbose (show counter open errors, etc)"), | 1294 | "be more verbose (show counter open errors, etc)"), |
1161 | OPT_END() | 1295 | OPT_END() |
@@ -1165,19 +1299,12 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1165 | { | 1299 | { |
1166 | int counter; | 1300 | int counter; |
1167 | 1301 | ||
1168 | symbol__init(); | ||
1169 | |||
1170 | page_size = sysconf(_SC_PAGE_SIZE); | 1302 | page_size = sysconf(_SC_PAGE_SIZE); |
1171 | 1303 | ||
1172 | argc = parse_options(argc, argv, options, top_usage, 0); | 1304 | argc = parse_options(argc, argv, options, top_usage, 0); |
1173 | if (argc) | 1305 | if (argc) |
1174 | usage_with_options(top_usage, options); | 1306 | usage_with_options(top_usage, options); |
1175 | 1307 | ||
1176 | if (freq) { | ||
1177 | default_interval = freq; | ||
1178 | freq = 1; | ||
1179 | } | ||
1180 | |||
1181 | /* CPU and PID are mutually exclusive */ | 1308 | /* CPU and PID are mutually exclusive */ |
1182 | if (target_pid != -1 && profile_cpu != -1) { | 1309 | if (target_pid != -1 && profile_cpu != -1) { |
1183 | printf("WARNING: PID switch overriding CPU\n"); | 1310 | printf("WARNING: PID switch overriding CPU\n"); |
@@ -1188,11 +1315,27 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1188 | if (!nr_counters) | 1315 | if (!nr_counters) |
1189 | nr_counters = 1; | 1316 | nr_counters = 1; |
1190 | 1317 | ||
1318 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | ||
1319 | (nr_counters + 1) * sizeof(unsigned long)); | ||
1320 | |||
1321 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | ||
1322 | if (symbol__init() < 0) | ||
1323 | return -1; | ||
1324 | |||
1191 | if (delay_secs < 1) | 1325 | if (delay_secs < 1) |
1192 | delay_secs = 1; | 1326 | delay_secs = 1; |
1193 | 1327 | ||
1194 | parse_symbols(); | 1328 | /* |
1195 | parse_source(sym_filter_entry); | 1329 | * User specified count overrides default frequency. |
1330 | */ | ||
1331 | if (default_interval) | ||
1332 | freq = 0; | ||
1333 | else if (freq) { | ||
1334 | default_interval = freq; | ||
1335 | } else { | ||
1336 | fprintf(stderr, "frequency and count are zero, aborting\n"); | ||
1337 | exit(EXIT_FAILURE); | ||
1338 | } | ||
1196 | 1339 | ||
1197 | /* | 1340 | /* |
1198 | * Fill in the ones not specifically initialized via -c: | 1341 | * Fill in the ones not specifically initialized via -c: |
@@ -1211,5 +1354,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1211 | if (target_pid != -1 || profile_cpu != -1) | 1354 | if (target_pid != -1 || profile_cpu != -1) |
1212 | nr_cpus = 1; | 1355 | nr_cpus = 1; |
1213 | 1356 | ||
1357 | get_term_dimensions(&winsize); | ||
1358 | if (print_entries == 0) { | ||
1359 | update_print_entries(&winsize); | ||
1360 | signal(SIGWINCH, sig_winch_handler); | ||
1361 | } | ||
1362 | |||
1214 | return __cmd_top(); | 1363 | return __cmd_top(); |
1215 | } | 1364 | } |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 0c5e4f72f2ba..407041d20de0 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -5,259 +5,499 @@ | |||
5 | #include "util/symbol.h" | 5 | #include "util/symbol.h" |
6 | #include "util/thread.h" | 6 | #include "util/thread.h" |
7 | #include "util/header.h" | 7 | #include "util/header.h" |
8 | #include "util/exec_cmd.h" | ||
9 | #include "util/trace-event.h" | ||
10 | #include "util/session.h" | ||
8 | 11 | ||
9 | #include "util/parse-options.h" | 12 | static char const *script_name; |
13 | static char const *generate_script_lang; | ||
10 | 14 | ||
11 | #include "perf.h" | 15 | static int default_start_script(const char *script __unused, |
12 | #include "util/debug.h" | 16 | int argc __unused, |
17 | const char **argv __unused) | ||
18 | { | ||
19 | return 0; | ||
20 | } | ||
13 | 21 | ||
14 | #include "util/trace-event.h" | 22 | static int default_stop_script(void) |
23 | { | ||
24 | return 0; | ||
25 | } | ||
26 | |||
27 | static int default_generate_script(const char *outfile __unused) | ||
28 | { | ||
29 | return 0; | ||
30 | } | ||
31 | |||
32 | static struct scripting_ops default_scripting_ops = { | ||
33 | .start_script = default_start_script, | ||
34 | .stop_script = default_stop_script, | ||
35 | .process_event = print_event, | ||
36 | .generate_script = default_generate_script, | ||
37 | }; | ||
38 | |||
39 | static struct scripting_ops *scripting_ops; | ||
40 | |||
41 | static void setup_scripting(void) | ||
42 | { | ||
43 | /* make sure PERF_EXEC_PATH is set for scripts */ | ||
44 | perf_set_argv_exec_path(perf_exec_path()); | ||
45 | |||
46 | setup_perl_scripting(); | ||
47 | setup_python_scripting(); | ||
15 | 48 | ||
16 | static char const *input_name = "perf.data"; | 49 | scripting_ops = &default_scripting_ops; |
17 | static int input; | 50 | } |
18 | static unsigned long page_size; | 51 | |
19 | static unsigned long mmap_window = 32; | 52 | static int cleanup_scripting(void) |
53 | { | ||
54 | return scripting_ops->stop_script(); | ||
55 | } | ||
20 | 56 | ||
21 | static unsigned long total = 0; | 57 | #include "util/parse-options.h" |
22 | static unsigned long total_comm = 0; | ||
23 | 58 | ||
24 | static struct rb_root threads; | 59 | #include "perf.h" |
25 | static struct thread *last_match; | 60 | #include "util/debug.h" |
26 | 61 | ||
27 | static struct perf_header *header; | 62 | #include "util/trace-event.h" |
28 | static u64 sample_type; | 63 | #include "util/exec_cmd.h" |
29 | 64 | ||
65 | static char const *input_name = "perf.data"; | ||
30 | 66 | ||
31 | static int | 67 | static int process_sample_event(event_t *event, struct perf_session *session) |
32 | process_comm_event(event_t *event, unsigned long offset, unsigned long head) | ||
33 | { | 68 | { |
69 | struct sample_data data; | ||
34 | struct thread *thread; | 70 | struct thread *thread; |
35 | 71 | ||
36 | thread = threads__findnew(event->comm.pid, &threads, &last_match); | 72 | memset(&data, 0, sizeof(data)); |
73 | data.time = -1; | ||
74 | data.cpu = -1; | ||
75 | data.period = 1; | ||
37 | 76 | ||
38 | dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n", | 77 | event__parse_sample(event, session->sample_type, &data); |
39 | (void *)(offset + head), | ||
40 | (void *)(long)(event->header.size), | ||
41 | event->comm.comm, event->comm.pid); | ||
42 | 78 | ||
43 | if (thread == NULL || | 79 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
44 | thread__set_comm(thread, event->comm.comm)) { | 80 | data.pid, data.tid, data.ip, data.period); |
45 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 81 | |
82 | thread = perf_session__findnew(session, event->ip.pid); | ||
83 | if (thread == NULL) { | ||
84 | pr_debug("problem processing %d event, skipping it.\n", | ||
85 | event->header.type); | ||
46 | return -1; | 86 | return -1; |
47 | } | 87 | } |
48 | total_comm++; | ||
49 | 88 | ||
89 | if (session->sample_type & PERF_SAMPLE_RAW) { | ||
90 | /* | ||
91 | * FIXME: better resolve from pid from the struct trace_entry | ||
92 | * field, although it should be the same than this perf | ||
93 | * event pid | ||
94 | */ | ||
95 | scripting_ops->process_event(data.cpu, data.raw_data, | ||
96 | data.raw_size, | ||
97 | data.time, thread->comm); | ||
98 | } | ||
99 | |||
100 | session->events_stats.total += data.period; | ||
50 | return 0; | 101 | return 0; |
51 | } | 102 | } |
52 | 103 | ||
53 | static int | 104 | static struct perf_event_ops event_ops = { |
54 | process_sample_event(event_t *event, unsigned long offset, unsigned long head) | 105 | .sample = process_sample_event, |
106 | .comm = event__process_comm, | ||
107 | }; | ||
108 | |||
109 | static int __cmd_trace(struct perf_session *session) | ||
55 | { | 110 | { |
56 | char level; | 111 | return perf_session__process_events(session, &event_ops); |
57 | int show = 0; | 112 | } |
58 | struct dso *dso = NULL; | ||
59 | struct thread *thread; | ||
60 | u64 ip = event->ip.ip; | ||
61 | u64 timestamp = -1; | ||
62 | u32 cpu = -1; | ||
63 | u64 period = 1; | ||
64 | void *more_data = event->ip.__more_data; | ||
65 | int cpumode; | ||
66 | |||
67 | thread = threads__findnew(event->ip.pid, &threads, &last_match); | ||
68 | |||
69 | if (sample_type & PERF_SAMPLE_TIME) { | ||
70 | timestamp = *(u64 *)more_data; | ||
71 | more_data += sizeof(u64); | ||
72 | } | ||
73 | 113 | ||
74 | if (sample_type & PERF_SAMPLE_CPU) { | 114 | struct script_spec { |
75 | cpu = *(u32 *)more_data; | 115 | struct list_head node; |
76 | more_data += sizeof(u32); | 116 | struct scripting_ops *ops; |
77 | more_data += sizeof(u32); /* reserved */ | 117 | char spec[0]; |
78 | } | 118 | }; |
119 | |||
120 | LIST_HEAD(script_specs); | ||
121 | |||
122 | static struct script_spec *script_spec__new(const char *spec, | ||
123 | struct scripting_ops *ops) | ||
124 | { | ||
125 | struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1); | ||
79 | 126 | ||
80 | if (sample_type & PERF_SAMPLE_PERIOD) { | 127 | if (s != NULL) { |
81 | period = *(u64 *)more_data; | 128 | strcpy(s->spec, spec); |
82 | more_data += sizeof(u64); | 129 | s->ops = ops; |
83 | } | 130 | } |
84 | 131 | ||
85 | dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", | 132 | return s; |
86 | (void *)(offset + head), | 133 | } |
87 | (void *)(long)(event->header.size), | ||
88 | event->header.misc, | ||
89 | event->ip.pid, event->ip.tid, | ||
90 | (void *)(long)ip, | ||
91 | (long long)period); | ||
92 | 134 | ||
93 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 135 | static void script_spec__delete(struct script_spec *s) |
136 | { | ||
137 | free(s->spec); | ||
138 | free(s); | ||
139 | } | ||
94 | 140 | ||
95 | if (thread == NULL) { | 141 | static void script_spec__add(struct script_spec *s) |
96 | eprintf("problem processing %d event, skipping it.\n", | 142 | { |
97 | event->header.type); | 143 | list_add_tail(&s->node, &script_specs); |
98 | return -1; | 144 | } |
99 | } | 145 | |
146 | static struct script_spec *script_spec__find(const char *spec) | ||
147 | { | ||
148 | struct script_spec *s; | ||
100 | 149 | ||
101 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 150 | list_for_each_entry(s, &script_specs, node) |
151 | if (strcasecmp(s->spec, spec) == 0) | ||
152 | return s; | ||
153 | return NULL; | ||
154 | } | ||
102 | 155 | ||
103 | if (cpumode == PERF_RECORD_MISC_KERNEL) { | 156 | static struct script_spec *script_spec__findnew(const char *spec, |
104 | show = SHOW_KERNEL; | 157 | struct scripting_ops *ops) |
105 | level = 'k'; | 158 | { |
159 | struct script_spec *s = script_spec__find(spec); | ||
106 | 160 | ||
107 | dso = kernel_dso; | 161 | if (s) |
162 | return s; | ||
108 | 163 | ||
109 | dump_printf(" ...... dso: %s\n", dso->name); | 164 | s = script_spec__new(spec, ops); |
165 | if (!s) | ||
166 | goto out_delete_spec; | ||
110 | 167 | ||
111 | } else if (cpumode == PERF_RECORD_MISC_USER) { | 168 | script_spec__add(s); |
112 | 169 | ||
113 | show = SHOW_USER; | 170 | return s; |
114 | level = '.'; | ||
115 | 171 | ||
116 | } else { | 172 | out_delete_spec: |
117 | show = SHOW_HV; | 173 | script_spec__delete(s); |
118 | level = 'H'; | ||
119 | 174 | ||
120 | dso = hypervisor_dso; | 175 | return NULL; |
176 | } | ||
121 | 177 | ||
122 | dump_printf(" ...... dso: [hypervisor]\n"); | 178 | int script_spec_register(const char *spec, struct scripting_ops *ops) |
123 | } | 179 | { |
180 | struct script_spec *s; | ||
124 | 181 | ||
125 | if (sample_type & PERF_SAMPLE_RAW) { | 182 | s = script_spec__find(spec); |
126 | struct { | 183 | if (s) |
127 | u32 size; | 184 | return -1; |
128 | char data[0]; | ||
129 | } *raw = more_data; | ||
130 | 185 | ||
131 | /* | 186 | s = script_spec__findnew(spec, ops); |
132 | * FIXME: better resolve from pid from the struct trace_entry | 187 | if (!s) |
133 | * field, although it should be the same than this perf | 188 | return -1; |
134 | * event pid | ||
135 | */ | ||
136 | print_event(cpu, raw->data, raw->size, timestamp, thread->comm); | ||
137 | } | ||
138 | total += period; | ||
139 | 189 | ||
140 | return 0; | 190 | return 0; |
141 | } | 191 | } |
142 | 192 | ||
143 | static int | 193 | static struct scripting_ops *script_spec__lookup(const char *spec) |
144 | process_event(event_t *event, unsigned long offset, unsigned long head) | ||
145 | { | 194 | { |
146 | trace_event(event); | 195 | struct script_spec *s = script_spec__find(spec); |
196 | if (!s) | ||
197 | return NULL; | ||
147 | 198 | ||
148 | switch (event->header.type) { | 199 | return s->ops; |
149 | case PERF_RECORD_MMAP ... PERF_RECORD_LOST: | 200 | } |
150 | return 0; | ||
151 | 201 | ||
152 | case PERF_RECORD_COMM: | 202 | static void list_available_languages(void) |
153 | return process_comm_event(event, offset, head); | 203 | { |
204 | struct script_spec *s; | ||
154 | 205 | ||
155 | case PERF_RECORD_EXIT ... PERF_RECORD_READ: | 206 | fprintf(stderr, "\n"); |
156 | return 0; | 207 | fprintf(stderr, "Scripting language extensions (used in " |
208 | "perf trace -s [spec:]script.[spec]):\n\n"); | ||
157 | 209 | ||
158 | case PERF_RECORD_SAMPLE: | 210 | list_for_each_entry(s, &script_specs, node) |
159 | return process_sample_event(event, offset, head); | 211 | fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name); |
160 | 212 | ||
161 | case PERF_RECORD_MAX: | 213 | fprintf(stderr, "\n"); |
162 | default: | 214 | } |
163 | return -1; | 215 | |
216 | static int parse_scriptname(const struct option *opt __used, | ||
217 | const char *str, int unset __used) | ||
218 | { | ||
219 | char spec[PATH_MAX]; | ||
220 | const char *script, *ext; | ||
221 | int len; | ||
222 | |||
223 | if (strcmp(str, "lang") == 0) { | ||
224 | list_available_languages(); | ||
225 | exit(0); | ||
226 | } | ||
227 | |||
228 | script = strchr(str, ':'); | ||
229 | if (script) { | ||
230 | len = script - str; | ||
231 | if (len >= PATH_MAX) { | ||
232 | fprintf(stderr, "invalid language specifier"); | ||
233 | return -1; | ||
234 | } | ||
235 | strncpy(spec, str, len); | ||
236 | spec[len] = '\0'; | ||
237 | scripting_ops = script_spec__lookup(spec); | ||
238 | if (!scripting_ops) { | ||
239 | fprintf(stderr, "invalid language specifier"); | ||
240 | return -1; | ||
241 | } | ||
242 | script++; | ||
243 | } else { | ||
244 | script = str; | ||
245 | ext = strchr(script, '.'); | ||
246 | if (!ext) { | ||
247 | fprintf(stderr, "invalid script extension"); | ||
248 | return -1; | ||
249 | } | ||
250 | scripting_ops = script_spec__lookup(++ext); | ||
251 | if (!scripting_ops) { | ||
252 | fprintf(stderr, "invalid script extension"); | ||
253 | return -1; | ||
254 | } | ||
164 | } | 255 | } |
165 | 256 | ||
257 | script_name = strdup(script); | ||
258 | |||
166 | return 0; | 259 | return 0; |
167 | } | 260 | } |
168 | 261 | ||
169 | static int __cmd_trace(void) | 262 | #define for_each_lang(scripts_dir, lang_dirent, lang_next) \ |
263 | while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ | ||
264 | lang_next) \ | ||
265 | if (lang_dirent.d_type == DT_DIR && \ | ||
266 | (strcmp(lang_dirent.d_name, ".")) && \ | ||
267 | (strcmp(lang_dirent.d_name, ".."))) | ||
268 | |||
269 | #define for_each_script(lang_dir, script_dirent, script_next) \ | ||
270 | while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ | ||
271 | script_next) \ | ||
272 | if (script_dirent.d_type != DT_DIR) | ||
273 | |||
274 | |||
275 | #define RECORD_SUFFIX "-record" | ||
276 | #define REPORT_SUFFIX "-report" | ||
277 | |||
278 | struct script_desc { | ||
279 | struct list_head node; | ||
280 | char *name; | ||
281 | char *half_liner; | ||
282 | char *args; | ||
283 | }; | ||
284 | |||
285 | LIST_HEAD(script_descs); | ||
286 | |||
287 | static struct script_desc *script_desc__new(const char *name) | ||
170 | { | 288 | { |
171 | int ret, rc = EXIT_FAILURE; | 289 | struct script_desc *s = zalloc(sizeof(*s)); |
172 | unsigned long offset = 0; | ||
173 | unsigned long head = 0; | ||
174 | struct stat perf_stat; | ||
175 | event_t *event; | ||
176 | uint32_t size; | ||
177 | char *buf; | ||
178 | |||
179 | trace_report(); | ||
180 | register_idle_thread(&threads, &last_match); | ||
181 | |||
182 | input = open(input_name, O_RDONLY); | ||
183 | if (input < 0) { | ||
184 | perror("failed to open file"); | ||
185 | exit(-1); | ||
186 | } | ||
187 | 290 | ||
188 | ret = fstat(input, &perf_stat); | 291 | if (s != NULL) |
189 | if (ret < 0) { | 292 | s->name = strdup(name); |
190 | perror("failed to stat file"); | ||
191 | exit(-1); | ||
192 | } | ||
193 | 293 | ||
194 | if (!perf_stat.st_size) { | 294 | return s; |
195 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 295 | } |
196 | exit(0); | 296 | |
197 | } | 297 | static void script_desc__delete(struct script_desc *s) |
198 | header = perf_header__read(input); | 298 | { |
199 | head = header->data_offset; | 299 | free(s->name); |
200 | sample_type = perf_header__sample_type(header); | 300 | free(s); |
301 | } | ||
302 | |||
303 | static void script_desc__add(struct script_desc *s) | ||
304 | { | ||
305 | list_add_tail(&s->node, &script_descs); | ||
306 | } | ||
307 | |||
308 | static struct script_desc *script_desc__find(const char *name) | ||
309 | { | ||
310 | struct script_desc *s; | ||
311 | |||
312 | list_for_each_entry(s, &script_descs, node) | ||
313 | if (strcasecmp(s->name, name) == 0) | ||
314 | return s; | ||
315 | return NULL; | ||
316 | } | ||
317 | |||
318 | static struct script_desc *script_desc__findnew(const char *name) | ||
319 | { | ||
320 | struct script_desc *s = script_desc__find(name); | ||
321 | |||
322 | if (s) | ||
323 | return s; | ||
324 | |||
325 | s = script_desc__new(name); | ||
326 | if (!s) | ||
327 | goto out_delete_desc; | ||
328 | |||
329 | script_desc__add(s); | ||
330 | |||
331 | return s; | ||
201 | 332 | ||
202 | if (!(sample_type & PERF_SAMPLE_RAW)) | 333 | out_delete_desc: |
203 | die("No trace sample to read. Did you call perf record " | 334 | script_desc__delete(s); |
204 | "without -R?"); | ||
205 | 335 | ||
206 | if (load_kernel() < 0) { | 336 | return NULL; |
207 | perror("failed to load kernel symbols"); | 337 | } |
208 | return EXIT_FAILURE; | 338 | |
339 | static char *ends_with(char *str, const char *suffix) | ||
340 | { | ||
341 | size_t suffix_len = strlen(suffix); | ||
342 | char *p = str; | ||
343 | |||
344 | if (strlen(str) > suffix_len) { | ||
345 | p = str + strlen(str) - suffix_len; | ||
346 | if (!strncmp(p, suffix, suffix_len)) | ||
347 | return p; | ||
209 | } | 348 | } |
210 | 349 | ||
211 | remap: | 350 | return NULL; |
212 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | 351 | } |
213 | MAP_SHARED, input, offset); | 352 | |
214 | if (buf == MAP_FAILED) { | 353 | static char *ltrim(char *str) |
215 | perror("failed to mmap file"); | 354 | { |
216 | exit(-1); | 355 | int len = strlen(str); |
356 | |||
357 | while (len && isspace(*str)) { | ||
358 | len--; | ||
359 | str++; | ||
217 | } | 360 | } |
218 | 361 | ||
219 | more: | 362 | return str; |
220 | event = (event_t *)(buf + head); | 363 | } |
221 | 364 | ||
222 | if (head + event->header.size >= page_size * mmap_window) { | 365 | static int read_script_info(struct script_desc *desc, const char *filename) |
223 | unsigned long shift = page_size * (head / page_size); | 366 | { |
224 | int res; | 367 | char line[BUFSIZ], *p; |
368 | FILE *fp; | ||
225 | 369 | ||
226 | res = munmap(buf, page_size * mmap_window); | 370 | fp = fopen(filename, "r"); |
227 | assert(res == 0); | 371 | if (!fp) |
372 | return -1; | ||
228 | 373 | ||
229 | offset += shift; | 374 | while (fgets(line, sizeof(line), fp)) { |
230 | head -= shift; | 375 | p = ltrim(line); |
231 | goto remap; | 376 | if (strlen(p) == 0) |
377 | continue; | ||
378 | if (*p != '#') | ||
379 | continue; | ||
380 | p++; | ||
381 | if (strlen(p) && *p == '!') | ||
382 | continue; | ||
383 | |||
384 | p = ltrim(p); | ||
385 | if (strlen(p) && p[strlen(p) - 1] == '\n') | ||
386 | p[strlen(p) - 1] = '\0'; | ||
387 | |||
388 | if (!strncmp(p, "description:", strlen("description:"))) { | ||
389 | p += strlen("description:"); | ||
390 | desc->half_liner = strdup(ltrim(p)); | ||
391 | continue; | ||
392 | } | ||
393 | |||
394 | if (!strncmp(p, "args:", strlen("args:"))) { | ||
395 | p += strlen("args:"); | ||
396 | desc->args = strdup(ltrim(p)); | ||
397 | continue; | ||
398 | } | ||
232 | } | 399 | } |
233 | 400 | ||
234 | size = event->header.size; | 401 | fclose(fp); |
235 | |||
236 | if (!size || process_event(event, offset, head) < 0) { | ||
237 | 402 | ||
238 | /* | 403 | return 0; |
239 | * assume we lost track of the stream, check alignment, and | 404 | } |
240 | * increment a single u64 in the hope to catch on again 'soon'. | ||
241 | */ | ||
242 | 405 | ||
243 | if (unlikely(head & 7)) | 406 | static int list_available_scripts(const struct option *opt __used, |
244 | head &= ~7ULL; | 407 | const char *s __used, int unset __used) |
408 | { | ||
409 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | ||
410 | char scripts_path[MAXPATHLEN]; | ||
411 | DIR *scripts_dir, *lang_dir; | ||
412 | char script_path[MAXPATHLEN]; | ||
413 | char lang_path[MAXPATHLEN]; | ||
414 | struct script_desc *desc; | ||
415 | char first_half[BUFSIZ]; | ||
416 | char *script_root; | ||
417 | char *str; | ||
418 | |||
419 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | ||
420 | |||
421 | scripts_dir = opendir(scripts_path); | ||
422 | if (!scripts_dir) | ||
423 | return -1; | ||
245 | 424 | ||
246 | size = 8; | 425 | for_each_lang(scripts_dir, lang_dirent, lang_next) { |
426 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | ||
427 | lang_dirent.d_name); | ||
428 | lang_dir = opendir(lang_path); | ||
429 | if (!lang_dir) | ||
430 | continue; | ||
431 | |||
432 | for_each_script(lang_dir, script_dirent, script_next) { | ||
433 | script_root = strdup(script_dirent.d_name); | ||
434 | str = ends_with(script_root, REPORT_SUFFIX); | ||
435 | if (str) { | ||
436 | *str = '\0'; | ||
437 | desc = script_desc__findnew(script_root); | ||
438 | snprintf(script_path, MAXPATHLEN, "%s/%s", | ||
439 | lang_path, script_dirent.d_name); | ||
440 | read_script_info(desc, script_path); | ||
441 | } | ||
442 | free(script_root); | ||
443 | } | ||
247 | } | 444 | } |
248 | 445 | ||
249 | head += size; | 446 | fprintf(stdout, "List of available trace scripts:\n"); |
447 | list_for_each_entry(desc, &script_descs, node) { | ||
448 | sprintf(first_half, "%s %s", desc->name, | ||
449 | desc->args ? desc->args : ""); | ||
450 | fprintf(stdout, " %-36s %s\n", first_half, | ||
451 | desc->half_liner ? desc->half_liner : ""); | ||
452 | } | ||
250 | 453 | ||
251 | if (offset + head < (unsigned long)perf_stat.st_size) | 454 | exit(0); |
252 | goto more; | 455 | } |
253 | 456 | ||
254 | rc = EXIT_SUCCESS; | 457 | static char *get_script_path(const char *script_root, const char *suffix) |
255 | close(input); | 458 | { |
459 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | ||
460 | char scripts_path[MAXPATHLEN]; | ||
461 | char script_path[MAXPATHLEN]; | ||
462 | DIR *scripts_dir, *lang_dir; | ||
463 | char lang_path[MAXPATHLEN]; | ||
464 | char *str, *__script_root; | ||
465 | char *path = NULL; | ||
466 | |||
467 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | ||
468 | |||
469 | scripts_dir = opendir(scripts_path); | ||
470 | if (!scripts_dir) | ||
471 | return NULL; | ||
472 | |||
473 | for_each_lang(scripts_dir, lang_dirent, lang_next) { | ||
474 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | ||
475 | lang_dirent.d_name); | ||
476 | lang_dir = opendir(lang_path); | ||
477 | if (!lang_dir) | ||
478 | continue; | ||
479 | |||
480 | for_each_script(lang_dir, script_dirent, script_next) { | ||
481 | __script_root = strdup(script_dirent.d_name); | ||
482 | str = ends_with(__script_root, suffix); | ||
483 | if (str) { | ||
484 | *str = '\0'; | ||
485 | if (strcmp(__script_root, script_root)) | ||
486 | continue; | ||
487 | snprintf(script_path, MAXPATHLEN, "%s/%s", | ||
488 | lang_path, script_dirent.d_name); | ||
489 | path = strdup(script_path); | ||
490 | free(__script_root); | ||
491 | break; | ||
492 | } | ||
493 | free(__script_root); | ||
494 | } | ||
495 | } | ||
256 | 496 | ||
257 | return rc; | 497 | return path; |
258 | } | 498 | } |
259 | 499 | ||
260 | static const char * const annotate_usage[] = { | 500 | static const char * const trace_usage[] = { |
261 | "perf trace [<options>] <command>", | 501 | "perf trace [<options>] <command>", |
262 | NULL | 502 | NULL |
263 | }; | 503 | }; |
@@ -267,25 +507,122 @@ static const struct option options[] = { | |||
267 | "dump raw trace in ASCII"), | 507 | "dump raw trace in ASCII"), |
268 | OPT_BOOLEAN('v', "verbose", &verbose, | 508 | OPT_BOOLEAN('v', "verbose", &verbose, |
269 | "be more verbose (show symbol address, etc)"), | 509 | "be more verbose (show symbol address, etc)"), |
510 | OPT_BOOLEAN('L', "Latency", &latency_format, | ||
511 | "show latency attributes (irqs/preemption disabled, etc)"), | ||
512 | OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts", | ||
513 | list_available_scripts), | ||
514 | OPT_CALLBACK('s', "script", NULL, "name", | ||
515 | "script file name (lang:script name, script name, or *)", | ||
516 | parse_scriptname), | ||
517 | OPT_STRING('g', "gen-script", &generate_script_lang, "lang", | ||
518 | "generate perf-trace.xx script in specified language"), | ||
519 | OPT_STRING('i', "input", &input_name, "file", | ||
520 | "input file name"), | ||
521 | |||
270 | OPT_END() | 522 | OPT_END() |
271 | }; | 523 | }; |
272 | 524 | ||
273 | int cmd_trace(int argc, const char **argv, const char *prefix __used) | 525 | int cmd_trace(int argc, const char **argv, const char *prefix __used) |
274 | { | 526 | { |
275 | symbol__init(); | 527 | struct perf_session *session; |
276 | page_size = getpagesize(); | 528 | const char *suffix = NULL; |
529 | const char **__argv; | ||
530 | char *script_path; | ||
531 | int i, err; | ||
532 | |||
533 | if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) { | ||
534 | if (argc < 3) { | ||
535 | fprintf(stderr, | ||
536 | "Please specify a record script\n"); | ||
537 | return -1; | ||
538 | } | ||
539 | suffix = RECORD_SUFFIX; | ||
540 | } | ||
277 | 541 | ||
278 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 542 | if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) { |
279 | if (argc) { | 543 | if (argc < 3) { |
280 | /* | 544 | fprintf(stderr, |
281 | * Special case: if there's an argument left then assume tha | 545 | "Please specify a report script\n"); |
282 | * it's a symbol filter: | 546 | return -1; |
283 | */ | 547 | } |
284 | if (argc > 1) | 548 | suffix = REPORT_SUFFIX; |
285 | usage_with_options(annotate_usage, options); | 549 | } |
550 | |||
551 | if (suffix) { | ||
552 | script_path = get_script_path(argv[2], suffix); | ||
553 | if (!script_path) { | ||
554 | fprintf(stderr, "script not found\n"); | ||
555 | return -1; | ||
556 | } | ||
557 | |||
558 | __argv = malloc((argc + 1) * sizeof(const char *)); | ||
559 | __argv[0] = "/bin/sh"; | ||
560 | __argv[1] = script_path; | ||
561 | for (i = 3; i < argc; i++) | ||
562 | __argv[i - 1] = argv[i]; | ||
563 | __argv[argc - 1] = NULL; | ||
564 | |||
565 | execvp("/bin/sh", (char **)__argv); | ||
566 | exit(-1); | ||
567 | } | ||
568 | |||
569 | setup_scripting(); | ||
570 | |||
571 | argc = parse_options(argc, argv, options, trace_usage, | ||
572 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
573 | |||
574 | if (symbol__init() < 0) | ||
575 | return -1; | ||
576 | if (!script_name) | ||
577 | setup_pager(); | ||
578 | |||
579 | session = perf_session__new(input_name, O_RDONLY, 0); | ||
580 | if (session == NULL) | ||
581 | return -ENOMEM; | ||
582 | |||
583 | if (!perf_session__has_traces(session, "record -R")) | ||
584 | return -EINVAL; | ||
585 | |||
586 | if (generate_script_lang) { | ||
587 | struct stat perf_stat; | ||
588 | |||
589 | int input = open(input_name, O_RDONLY); | ||
590 | if (input < 0) { | ||
591 | perror("failed to open file"); | ||
592 | exit(-1); | ||
593 | } | ||
594 | |||
595 | err = fstat(input, &perf_stat); | ||
596 | if (err < 0) { | ||
597 | perror("failed to stat file"); | ||
598 | exit(-1); | ||
599 | } | ||
600 | |||
601 | if (!perf_stat.st_size) { | ||
602 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | ||
603 | exit(0); | ||
604 | } | ||
605 | |||
606 | scripting_ops = script_spec__lookup(generate_script_lang); | ||
607 | if (!scripting_ops) { | ||
608 | fprintf(stderr, "invalid language specifier"); | ||
609 | return -1; | ||
610 | } | ||
611 | |||
612 | err = scripting_ops->generate_script("perf-trace"); | ||
613 | goto out; | ||
614 | } | ||
615 | |||
616 | if (script_name) { | ||
617 | err = scripting_ops->start_script(script_name, argc, argv); | ||
618 | if (err) | ||
619 | goto out; | ||
286 | } | 620 | } |
287 | 621 | ||
288 | setup_pager(); | 622 | err = __cmd_trace(session); |
289 | 623 | ||
290 | return __cmd_trace(); | 624 | perf_session__delete(session); |
625 | cleanup_scripting(); | ||
626 | out: | ||
627 | return err; | ||
291 | } | 628 | } |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index e11d8d231c3b..10fe49e7048a 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -15,6 +15,10 @@ extern int read_line_with_nul(char *buf, int size, FILE *file); | |||
15 | extern int check_pager_config(const char *cmd); | 15 | extern int check_pager_config(const char *cmd); |
16 | 16 | ||
17 | extern int cmd_annotate(int argc, const char **argv, const char *prefix); | 17 | extern int cmd_annotate(int argc, const char **argv, const char *prefix); |
18 | extern int cmd_bench(int argc, const char **argv, const char *prefix); | ||
19 | extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); | ||
20 | extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); | ||
21 | extern int cmd_diff(int argc, const char **argv, const char *prefix); | ||
18 | extern int cmd_help(int argc, const char **argv, const char *prefix); | 22 | extern int cmd_help(int argc, const char **argv, const char *prefix); |
19 | extern int cmd_sched(int argc, const char **argv, const char *prefix); | 23 | extern int cmd_sched(int argc, const char **argv, const char *prefix); |
20 | extern int cmd_list(int argc, const char **argv, const char *prefix); | 24 | extern int cmd_list(int argc, const char **argv, const char *prefix); |
@@ -25,5 +29,8 @@ extern int cmd_timechart(int argc, const char **argv, const char *prefix); | |||
25 | 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); |
26 | extern int cmd_trace(int argc, const char **argv, const char *prefix); | 30 | extern int cmd_trace(int argc, const char **argv, const char *prefix); |
27 | 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); | ||
33 | extern int cmd_kmem(int argc, const char **argv, const char *prefix); | ||
34 | extern int cmd_lock(int argc, const char **argv, const char *prefix); | ||
28 | 35 | ||
29 | #endif | 36 | #endif |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 00326e230d87..db6ee94d4a8e 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -3,6 +3,11 @@ | |||
3 | # command name category [deprecated] [common] | 3 | # command name category [deprecated] [common] |
4 | # | 4 | # |
5 | perf-annotate mainporcelain common | 5 | perf-annotate mainporcelain common |
6 | perf-archive mainporcelain common | ||
7 | perf-bench mainporcelain common | ||
8 | perf-buildid-cache mainporcelain common | ||
9 | perf-buildid-list mainporcelain common | ||
10 | perf-diff mainporcelain common | ||
6 | perf-list mainporcelain common | 11 | perf-list mainporcelain common |
7 | perf-sched mainporcelain common | 12 | perf-sched mainporcelain common |
8 | perf-record mainporcelain common | 13 | perf-record mainporcelain common |
@@ -11,3 +16,6 @@ perf-stat mainporcelain common | |||
11 | perf-timechart mainporcelain common | 16 | perf-timechart mainporcelain common |
12 | perf-top mainporcelain common | 17 | perf-top mainporcelain common |
13 | perf-trace mainporcelain common | 18 | perf-trace mainporcelain common |
19 | perf-probe mainporcelain common | ||
20 | perf-kmem mainporcelain common | ||
21 | perf-lock mainporcelain common | ||
diff --git a/tools/perf/design.txt b/tools/perf/design.txt index fdd42a824c98..bd0bb1b1279b 100644 --- a/tools/perf/design.txt +++ b/tools/perf/design.txt | |||
@@ -21,7 +21,7 @@ There's one file descriptor per virtual counter used. | |||
21 | The special file descriptor is opened via the perf_event_open() | 21 | The special file descriptor is opened via the perf_event_open() |
22 | system call: | 22 | system call: |
23 | 23 | ||
24 | int sys_perf_event_open(struct perf_event_hw_event *hw_event_uptr, | 24 | int sys_perf_event_open(struct perf_event_attr *hw_event_uptr, |
25 | pid_t pid, int cpu, int group_fd, | 25 | pid_t pid, int cpu, int group_fd, |
26 | unsigned long flags); | 26 | unsigned long flags); |
27 | 27 | ||
@@ -32,9 +32,9 @@ can be used to set the blocking mode, etc. | |||
32 | Multiple counters can be kept open at a time, and the counters | 32 | Multiple counters can be kept open at a time, and the counters |
33 | can be poll()ed. | 33 | can be poll()ed. |
34 | 34 | ||
35 | When creating a new counter fd, 'perf_event_hw_event' is: | 35 | When creating a new counter fd, 'perf_event_attr' is: |
36 | 36 | ||
37 | struct perf_event_hw_event { | 37 | struct perf_event_attr { |
38 | /* | 38 | /* |
39 | * The MSB of the config word signifies if the rest contains cpu | 39 | * The MSB of the config word signifies if the rest contains cpu |
40 | * specific (raw) counter configuration data, if unset, the next | 40 | * specific (raw) counter configuration data, if unset, the next |
@@ -101,10 +101,10 @@ enum hw_event_ids { | |||
101 | */ | 101 | */ |
102 | PERF_COUNT_HW_CPU_CYCLES = 0, | 102 | PERF_COUNT_HW_CPU_CYCLES = 0, |
103 | PERF_COUNT_HW_INSTRUCTIONS = 1, | 103 | PERF_COUNT_HW_INSTRUCTIONS = 1, |
104 | PERF_COUNT_HW_CACHE_REFERENCES = 2, | 104 | PERF_COUNT_HW_CACHE_REFERENCES = 2, |
105 | PERF_COUNT_HW_CACHE_MISSES = 3, | 105 | PERF_COUNT_HW_CACHE_MISSES = 3, |
106 | PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, | 106 | PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, |
107 | PERF_COUNT_HW_BRANCH_MISSES = 5, | 107 | PERF_COUNT_HW_BRANCH_MISSES = 5, |
108 | PERF_COUNT_HW_BUS_CYCLES = 6, | 108 | PERF_COUNT_HW_BUS_CYCLES = 6, |
109 | }; | 109 | }; |
110 | 110 | ||
@@ -131,12 +131,14 @@ software events, selected by 'event_id': | |||
131 | */ | 131 | */ |
132 | enum sw_event_ids { | 132 | enum sw_event_ids { |
133 | PERF_COUNT_SW_CPU_CLOCK = 0, | 133 | PERF_COUNT_SW_CPU_CLOCK = 0, |
134 | PERF_COUNT_SW_TASK_CLOCK = 1, | 134 | PERF_COUNT_SW_TASK_CLOCK = 1, |
135 | PERF_COUNT_SW_PAGE_FAULTS = 2, | 135 | PERF_COUNT_SW_PAGE_FAULTS = 2, |
136 | PERF_COUNT_SW_CONTEXT_SWITCHES = 3, | 136 | PERF_COUNT_SW_CONTEXT_SWITCHES = 3, |
137 | PERF_COUNT_SW_CPU_MIGRATIONS = 4, | 137 | PERF_COUNT_SW_CPU_MIGRATIONS = 4, |
138 | PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, | 138 | PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, |
139 | PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, | 139 | PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, |
140 | PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, | ||
141 | PERF_COUNT_SW_EMULATION_FAULTS = 8, | ||
140 | }; | 142 | }; |
141 | 143 | ||
142 | Counters of the type PERF_TYPE_TRACEPOINT are available when the ftrace event | 144 | Counters of the type PERF_TYPE_TRACEPOINT are available when the ftrace event |
@@ -397,7 +399,7 @@ Notification of new events is possible through poll()/select()/epoll() and | |||
397 | fcntl() managing signals. | 399 | fcntl() managing signals. |
398 | 400 | ||
399 | Normally a notification is generated for every page filled, however one can | 401 | Normally a notification is generated for every page filled, however one can |
400 | additionally set perf_event_hw_event.wakeup_events to generate one every | 402 | additionally set perf_event_attr.wakeup_events to generate one every |
401 | so many counter overflow events. | 403 | so many counter overflow events. |
402 | 404 | ||
403 | Future work will include a splice() interface to the ring-buffer. | 405 | Future work will include a splice() interface to the ring-buffer. |
diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh new file mode 100644 index 000000000000..910468e6e01c --- /dev/null +++ b/tools/perf/perf-archive.sh | |||
@@ -0,0 +1,33 @@ | |||
1 | #!/bin/bash | ||
2 | # perf archive | ||
3 | # Arnaldo Carvalho de Melo <acme@redhat.com> | ||
4 | |||
5 | PERF_DATA=perf.data | ||
6 | if [ $# -ne 0 ] ; then | ||
7 | PERF_DATA=$1 | ||
8 | fi | ||
9 | |||
10 | DEBUGDIR=~/.debug/ | ||
11 | BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX) | ||
12 | NOBUILDID=0000000000000000000000000000000000000000 | ||
13 | |||
14 | perf buildid-list -i $PERF_DATA --with-hits | grep -v "^$NOBUILDID " > $BUILDIDS | ||
15 | if [ ! -s $BUILDIDS ] ; then | ||
16 | echo "perf archive: no build-ids found" | ||
17 | rm -f $BUILDIDS | ||
18 | exit 1 | ||
19 | fi | ||
20 | |||
21 | MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX) | ||
22 | |||
23 | cut -d ' ' -f 1 $BUILDIDS | \ | ||
24 | while read build_id ; do | ||
25 | linkname=$DEBUGDIR.build-id/${build_id:0:2}/${build_id:2} | ||
26 | filename=$(readlink -f $linkname) | ||
27 | echo ${linkname#$DEBUGDIR} >> $MANIFEST | ||
28 | echo ${filename#$DEBUGDIR} >> $MANIFEST | ||
29 | done | ||
30 | |||
31 | tar cfj $PERF_DATA.tar.bz2 -C $DEBUGDIR -T $MANIFEST | ||
32 | rm -f $MANIFEST $BUILDIDS | ||
33 | exit 0 | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 19fc7feb9d59..cd32c200cdb3 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "util/run-command.h" | 14 | #include "util/run-command.h" |
15 | #include "util/parse-events.h" | 15 | #include "util/parse-events.h" |
16 | #include "util/string.h" | 16 | #include "util/string.h" |
17 | #include "util/debugfs.h" | ||
17 | 18 | ||
18 | const char perf_usage_string[] = | 19 | const char perf_usage_string[] = |
19 | "perf [--version] [--help] COMMAND [ARGS]"; | 20 | "perf [--version] [--help] COMMAND [ARGS]"; |
@@ -47,7 +48,8 @@ int check_pager_config(const char *cmd) | |||
47 | return c.val; | 48 | return c.val; |
48 | } | 49 | } |
49 | 50 | ||
50 | static void commit_pager_choice(void) { | 51 | static void commit_pager_choice(void) |
52 | { | ||
51 | switch (use_pager) { | 53 | switch (use_pager) { |
52 | case 0: | 54 | case 0: |
53 | setenv("PERF_PAGER", "cat", 1); | 55 | setenv("PERF_PAGER", "cat", 1); |
@@ -69,7 +71,7 @@ static void set_debugfs_path(void) | |||
69 | "tracing/events"); | 71 | "tracing/events"); |
70 | } | 72 | } |
71 | 73 | ||
72 | static int handle_options(const char*** argv, int* argc, int* envchanged) | 74 | static int handle_options(const char ***argv, int *argc, int *envchanged) |
73 | { | 75 | { |
74 | int handled = 0; | 76 | int handled = 0; |
75 | 77 | ||
@@ -89,8 +91,8 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) | |||
89 | /* | 91 | /* |
90 | * Check remaining flags. | 92 | * Check remaining flags. |
91 | */ | 93 | */ |
92 | if (!prefixcmp(cmd, "--exec-path")) { | 94 | if (!prefixcmp(cmd, CMD_EXEC_PATH)) { |
93 | cmd += 11; | 95 | cmd += strlen(CMD_EXEC_PATH); |
94 | if (*cmd == '=') | 96 | if (*cmd == '=') |
95 | perf_set_argv_exec_path(cmd + 1); | 97 | perf_set_argv_exec_path(cmd + 1); |
96 | else { | 98 | else { |
@@ -108,7 +110,7 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) | |||
108 | *envchanged = 1; | 110 | *envchanged = 1; |
109 | } else if (!strcmp(cmd, "--perf-dir")) { | 111 | } else if (!strcmp(cmd, "--perf-dir")) { |
110 | if (*argc < 2) { | 112 | if (*argc < 2) { |
111 | fprintf(stderr, "No directory given for --perf-dir.\n" ); | 113 | fprintf(stderr, "No directory given for --perf-dir.\n"); |
112 | usage(perf_usage_string); | 114 | usage(perf_usage_string); |
113 | } | 115 | } |
114 | setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1); | 116 | setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1); |
@@ -117,13 +119,13 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) | |||
117 | (*argv)++; | 119 | (*argv)++; |
118 | (*argc)--; | 120 | (*argc)--; |
119 | handled++; | 121 | handled++; |
120 | } else if (!prefixcmp(cmd, "--perf-dir=")) { | 122 | } else if (!prefixcmp(cmd, CMD_PERF_DIR)) { |
121 | setenv(PERF_DIR_ENVIRONMENT, cmd + 10, 1); | 123 | setenv(PERF_DIR_ENVIRONMENT, cmd + strlen(CMD_PERF_DIR), 1); |
122 | if (envchanged) | 124 | if (envchanged) |
123 | *envchanged = 1; | 125 | *envchanged = 1; |
124 | } else if (!strcmp(cmd, "--work-tree")) { | 126 | } else if (!strcmp(cmd, "--work-tree")) { |
125 | if (*argc < 2) { | 127 | if (*argc < 2) { |
126 | fprintf(stderr, "No directory given for --work-tree.\n" ); | 128 | fprintf(stderr, "No directory given for --work-tree.\n"); |
127 | usage(perf_usage_string); | 129 | usage(perf_usage_string); |
128 | } | 130 | } |
129 | setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1); | 131 | setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1); |
@@ -131,8 +133,8 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) | |||
131 | *envchanged = 1; | 133 | *envchanged = 1; |
132 | (*argv)++; | 134 | (*argv)++; |
133 | (*argc)--; | 135 | (*argc)--; |
134 | } else if (!prefixcmp(cmd, "--work-tree=")) { | 136 | } else if (!prefixcmp(cmd, CMD_WORK_TREE)) { |
135 | setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); | 137 | setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + strlen(CMD_WORK_TREE), 1); |
136 | if (envchanged) | 138 | if (envchanged) |
137 | *envchanged = 1; | 139 | *envchanged = 1; |
138 | } else if (!strcmp(cmd, "--debugfs-dir")) { | 140 | } else if (!strcmp(cmd, "--debugfs-dir")) { |
@@ -146,8 +148,8 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) | |||
146 | *envchanged = 1; | 148 | *envchanged = 1; |
147 | (*argv)++; | 149 | (*argv)++; |
148 | (*argc)--; | 150 | (*argc)--; |
149 | } else if (!prefixcmp(cmd, "--debugfs-dir=")) { | 151 | } else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) { |
150 | strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN); | 152 | strncpy(debugfs_mntpt, cmd + strlen(CMD_DEBUGFS_DIR), MAXPATHLEN); |
151 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | 153 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; |
152 | if (envchanged) | 154 | if (envchanged) |
153 | *envchanged = 1; | 155 | *envchanged = 1; |
@@ -167,7 +169,7 @@ static int handle_alias(int *argcp, const char ***argv) | |||
167 | { | 169 | { |
168 | int envchanged = 0, ret = 0, saved_errno = errno; | 170 | int envchanged = 0, ret = 0, saved_errno = errno; |
169 | int count, option_count; | 171 | int count, option_count; |
170 | const char** new_argv; | 172 | const char **new_argv; |
171 | const char *alias_command; | 173 | const char *alias_command; |
172 | char *alias_string; | 174 | char *alias_string; |
173 | 175 | ||
@@ -209,11 +211,11 @@ static int handle_alias(int *argcp, const char ***argv) | |||
209 | if (!strcmp(alias_command, new_argv[0])) | 211 | if (!strcmp(alias_command, new_argv[0])) |
210 | die("recursive alias: %s", alias_command); | 212 | die("recursive alias: %s", alias_command); |
211 | 213 | ||
212 | new_argv = realloc(new_argv, sizeof(char*) * | 214 | new_argv = realloc(new_argv, sizeof(char *) * |
213 | (count + *argcp + 1)); | 215 | (count + *argcp + 1)); |
214 | /* insert after command name */ | 216 | /* insert after command name */ |
215 | memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp); | 217 | memcpy(new_argv + count, *argv + 1, sizeof(char *) * *argcp); |
216 | new_argv[count+*argcp] = NULL; | 218 | new_argv[count + *argcp] = NULL; |
217 | 219 | ||
218 | *argv = new_argv; | 220 | *argv = new_argv; |
219 | *argcp += count - 1; | 221 | *argcp += count - 1; |
@@ -284,17 +286,24 @@ static void handle_internal_command(int argc, const char **argv) | |||
284 | { | 286 | { |
285 | const char *cmd = argv[0]; | 287 | const char *cmd = argv[0]; |
286 | static struct cmd_struct commands[] = { | 288 | static struct cmd_struct commands[] = { |
287 | { "help", cmd_help, 0 }, | 289 | { "buildid-cache", cmd_buildid_cache, 0 }, |
288 | { "list", cmd_list, 0 }, | 290 | { "buildid-list", cmd_buildid_list, 0 }, |
289 | { "record", cmd_record, 0 }, | 291 | { "diff", cmd_diff, 0 }, |
290 | { "report", cmd_report, 0 }, | 292 | { "help", cmd_help, 0 }, |
291 | { "stat", cmd_stat, 0 }, | 293 | { "list", cmd_list, 0 }, |
292 | { "timechart", cmd_timechart, 0 }, | 294 | { "record", cmd_record, 0 }, |
293 | { "top", cmd_top, 0 }, | 295 | { "report", cmd_report, 0 }, |
294 | { "annotate", cmd_annotate, 0 }, | 296 | { "bench", cmd_bench, 0 }, |
295 | { "version", cmd_version, 0 }, | 297 | { "stat", cmd_stat, 0 }, |
296 | { "trace", cmd_trace, 0 }, | 298 | { "timechart", cmd_timechart, 0 }, |
297 | { "sched", cmd_sched, 0 }, | 299 | { "top", cmd_top, 0 }, |
300 | { "annotate", cmd_annotate, 0 }, | ||
301 | { "version", cmd_version, 0 }, | ||
302 | { "trace", cmd_trace, 0 }, | ||
303 | { "sched", cmd_sched, 0 }, | ||
304 | { "probe", cmd_probe, 0 }, | ||
305 | { "kmem", cmd_kmem, 0 }, | ||
306 | { "lock", cmd_lock, 0 }, | ||
298 | }; | 307 | }; |
299 | unsigned int i; | 308 | unsigned int i; |
300 | static const char ext[] = STRIP_EXTENSION; | 309 | static const char ext[] = STRIP_EXTENSION; |
@@ -382,45 +391,12 @@ static int run_argv(int *argcp, const char ***argv) | |||
382 | /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ | 391 | /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ |
383 | static void get_debugfs_mntpt(void) | 392 | static void get_debugfs_mntpt(void) |
384 | { | 393 | { |
385 | FILE *file; | 394 | const char *path = debugfs_mount(NULL); |
386 | char fs_type[100]; | ||
387 | char debugfs[MAXPATHLEN]; | ||
388 | |||
389 | /* | ||
390 | * try the standard location | ||
391 | */ | ||
392 | if (valid_debugfs_mount("/sys/kernel/debug/") == 0) { | ||
393 | strcpy(debugfs_mntpt, "/sys/kernel/debug/"); | ||
394 | return; | ||
395 | } | ||
396 | |||
397 | /* | ||
398 | * try the sane location | ||
399 | */ | ||
400 | if (valid_debugfs_mount("/debug/") == 0) { | ||
401 | strcpy(debugfs_mntpt, "/debug/"); | ||
402 | return; | ||
403 | } | ||
404 | 395 | ||
405 | /* | 396 | if (path) |
406 | * give up and parse /proc/mounts | 397 | strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt)); |
407 | */ | 398 | else |
408 | file = fopen("/proc/mounts", "r"); | 399 | debugfs_mntpt[0] = '\0'; |
409 | if (file == NULL) | ||
410 | return; | ||
411 | |||
412 | while (fscanf(file, "%*s %" | ||
413 | STR(MAXPATHLEN) | ||
414 | "s %99s %*s %*d %*d\n", | ||
415 | debugfs, fs_type) == 2) { | ||
416 | if (strcmp(fs_type, "debugfs") == 0) | ||
417 | break; | ||
418 | } | ||
419 | fclose(file); | ||
420 | if (strcmp(fs_type, "debugfs") == 0) { | ||
421 | strncpy(debugfs_mntpt, debugfs, MAXPATHLEN); | ||
422 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | ||
423 | } | ||
424 | } | 400 | } |
425 | 401 | ||
426 | int main(int argc, const char **argv) | 402 | int main(int argc, const char **argv) |
@@ -469,15 +445,15 @@ int main(int argc, const char **argv) | |||
469 | 445 | ||
470 | /* | 446 | /* |
471 | * We use PATH to find perf commands, but we prepend some higher | 447 | * We use PATH to find perf commands, but we prepend some higher |
472 | * precidence paths: the "--exec-path" option, the PERF_EXEC_PATH | 448 | * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH |
473 | * environment, and the $(perfexecdir) from the Makefile at build | 449 | * environment, and the $(perfexecdir) from the Makefile at build |
474 | * time. | 450 | * time. |
475 | */ | 451 | */ |
476 | setup_path(); | 452 | setup_path(); |
477 | 453 | ||
478 | while (1) { | 454 | while (1) { |
479 | static int done_help = 0; | 455 | static int done_help; |
480 | static int was_alias = 0; | 456 | static int was_alias; |
481 | 457 | ||
482 | was_alias = run_argv(&argc, &argv); | 458 | was_alias = run_argv(&argc, &argv); |
483 | if (errno != ENOENT) | 459 | if (errno != ENOENT) |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 8cc4623afd6f..6fb379bc1d1f 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -47,6 +47,28 @@ | |||
47 | #define cpu_relax() asm volatile("":::"memory") | 47 | #define cpu_relax() asm volatile("":::"memory") |
48 | #endif | 48 | #endif |
49 | 49 | ||
50 | #ifdef __alpha__ | ||
51 | #include "../../arch/alpha/include/asm/unistd.h" | ||
52 | #define rmb() asm volatile("mb" ::: "memory") | ||
53 | #define cpu_relax() asm volatile("" ::: "memory") | ||
54 | #endif | ||
55 | |||
56 | #ifdef __ia64__ | ||
57 | #include "../../arch/ia64/include/asm/unistd.h" | ||
58 | #define rmb() asm volatile ("mf" ::: "memory") | ||
59 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") | ||
60 | #endif | ||
61 | |||
62 | #ifdef __arm__ | ||
63 | #include "../../arch/arm/include/asm/unistd.h" | ||
64 | /* | ||
65 | * Use the __kuser_memory_barrier helper in the CPU helper page. See | ||
66 | * arch/arm/kernel/entry-armv.S in the kernel source for details. | ||
67 | */ | ||
68 | #define rmb() ((void(*)(void))0xffff0fa0)() | ||
69 | #define cpu_relax() asm volatile("":::"memory") | ||
70 | #endif | ||
71 | |||
50 | #include <time.h> | 72 | #include <time.h> |
51 | #include <unistd.h> | 73 | #include <unistd.h> |
52 | #include <sys/types.h> | 74 | #include <sys/types.h> |
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.c b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c new file mode 100644 index 000000000000..01a64ad693f2 --- /dev/null +++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * This file was generated automatically by ExtUtils::ParseXS version 2.18_02 from the | ||
3 | * contents of Context.xs. Do not edit this file, edit Context.xs instead. | ||
4 | * | ||
5 | * ANY CHANGES MADE HERE WILL BE LOST! | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #line 1 "Context.xs" | ||
10 | /* | ||
11 | * Context.xs. XS interfaces for perf trace. | ||
12 | * | ||
13 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License as published by | ||
17 | * the Free Software Foundation; either version 2 of the License, or | ||
18 | * (at your option) any later version. | ||
19 | * | ||
20 | * This program is distributed in the hope that it will be useful, | ||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | * GNU General Public License for more details. | ||
24 | * | ||
25 | * You should have received a copy of the GNU General Public License | ||
26 | * along with this program; if not, write to the Free Software | ||
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include "EXTERN.h" | ||
32 | #include "perl.h" | ||
33 | #include "XSUB.h" | ||
34 | #include "../../../perf.h" | ||
35 | #include "../../../util/trace-event.h" | ||
36 | |||
37 | #ifndef PERL_UNUSED_VAR | ||
38 | # define PERL_UNUSED_VAR(var) if (0) var = var | ||
39 | #endif | ||
40 | |||
41 | #line 42 "Context.c" | ||
42 | |||
43 | XS(XS_Perf__Trace__Context_common_pc); /* prototype to pass -Wmissing-prototypes */ | ||
44 | XS(XS_Perf__Trace__Context_common_pc) | ||
45 | { | ||
46 | #ifdef dVAR | ||
47 | dVAR; dXSARGS; | ||
48 | #else | ||
49 | dXSARGS; | ||
50 | #endif | ||
51 | if (items != 1) | ||
52 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_pc", "context"); | ||
53 | PERL_UNUSED_VAR(cv); /* -W */ | ||
54 | { | ||
55 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); | ||
56 | int RETVAL; | ||
57 | dXSTARG; | ||
58 | |||
59 | RETVAL = common_pc(context); | ||
60 | XSprePUSH; PUSHi((IV)RETVAL); | ||
61 | } | ||
62 | XSRETURN(1); | ||
63 | } | ||
64 | |||
65 | |||
66 | XS(XS_Perf__Trace__Context_common_flags); /* prototype to pass -Wmissing-prototypes */ | ||
67 | XS(XS_Perf__Trace__Context_common_flags) | ||
68 | { | ||
69 | #ifdef dVAR | ||
70 | dVAR; dXSARGS; | ||
71 | #else | ||
72 | dXSARGS; | ||
73 | #endif | ||
74 | if (items != 1) | ||
75 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_flags", "context"); | ||
76 | PERL_UNUSED_VAR(cv); /* -W */ | ||
77 | { | ||
78 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); | ||
79 | int RETVAL; | ||
80 | dXSTARG; | ||
81 | |||
82 | RETVAL = common_flags(context); | ||
83 | XSprePUSH; PUSHi((IV)RETVAL); | ||
84 | } | ||
85 | XSRETURN(1); | ||
86 | } | ||
87 | |||
88 | |||
89 | XS(XS_Perf__Trace__Context_common_lock_depth); /* prototype to pass -Wmissing-prototypes */ | ||
90 | XS(XS_Perf__Trace__Context_common_lock_depth) | ||
91 | { | ||
92 | #ifdef dVAR | ||
93 | dVAR; dXSARGS; | ||
94 | #else | ||
95 | dXSARGS; | ||
96 | #endif | ||
97 | if (items != 1) | ||
98 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_lock_depth", "context"); | ||
99 | PERL_UNUSED_VAR(cv); /* -W */ | ||
100 | { | ||
101 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); | ||
102 | int RETVAL; | ||
103 | dXSTARG; | ||
104 | |||
105 | RETVAL = common_lock_depth(context); | ||
106 | XSprePUSH; PUSHi((IV)RETVAL); | ||
107 | } | ||
108 | XSRETURN(1); | ||
109 | } | ||
110 | |||
111 | #ifdef __cplusplus | ||
112 | extern "C" | ||
113 | #endif | ||
114 | XS(boot_Perf__Trace__Context); /* prototype to pass -Wmissing-prototypes */ | ||
115 | XS(boot_Perf__Trace__Context) | ||
116 | { | ||
117 | #ifdef dVAR | ||
118 | dVAR; dXSARGS; | ||
119 | #else | ||
120 | dXSARGS; | ||
121 | #endif | ||
122 | const char* file = __FILE__; | ||
123 | |||
124 | PERL_UNUSED_VAR(cv); /* -W */ | ||
125 | PERL_UNUSED_VAR(items); /* -W */ | ||
126 | XS_VERSION_BOOTCHECK ; | ||
127 | |||
128 | newXSproto("Perf::Trace::Context::common_pc", XS_Perf__Trace__Context_common_pc, file, "$"); | ||
129 | newXSproto("Perf::Trace::Context::common_flags", XS_Perf__Trace__Context_common_flags, file, "$"); | ||
130 | newXSproto("Perf::Trace::Context::common_lock_depth", XS_Perf__Trace__Context_common_lock_depth, file, "$"); | ||
131 | if (PL_unitcheckav) | ||
132 | call_list(PL_scopestack_ix, PL_unitcheckav); | ||
133 | XSRETURN_YES; | ||
134 | } | ||
135 | |||
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs new file mode 100644 index 000000000000..549cf0467d30 --- /dev/null +++ b/tools/perf/scripts/perl/Perf-Trace-Util/Context.xs | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * Context.xs. XS interfaces for perf trace. | ||
3 | * | ||
4 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include "EXTERN.h" | ||
23 | #include "perl.h" | ||
24 | #include "XSUB.h" | ||
25 | #include "../../../perf.h" | ||
26 | #include "../../../util/trace-event.h" | ||
27 | |||
28 | MODULE = Perf::Trace::Context PACKAGE = Perf::Trace::Context | ||
29 | PROTOTYPES: ENABLE | ||
30 | |||
31 | int | ||
32 | common_pc(context) | ||
33 | struct scripting_context * context | ||
34 | |||
35 | int | ||
36 | common_flags(context) | ||
37 | struct scripting_context * context | ||
38 | |||
39 | int | ||
40 | common_lock_depth(context) | ||
41 | struct scripting_context * context | ||
42 | |||
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL b/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL new file mode 100644 index 000000000000..decdeb0f6789 --- /dev/null +++ b/tools/perf/scripts/perl/Perf-Trace-Util/Makefile.PL | |||
@@ -0,0 +1,17 @@ | |||
1 | use 5.010000; | ||
2 | use ExtUtils::MakeMaker; | ||
3 | # See lib/ExtUtils/MakeMaker.pm for details of how to influence | ||
4 | # the contents of the Makefile that is written. | ||
5 | WriteMakefile( | ||
6 | NAME => 'Perf::Trace::Context', | ||
7 | VERSION_FROM => 'lib/Perf/Trace/Context.pm', # finds $VERSION | ||
8 | PREREQ_PM => {}, # e.g., Module::Name => 1.1 | ||
9 | ($] >= 5.005 ? ## Add these new keywords supported since 5.005 | ||
10 | (ABSTRACT_FROM => 'lib/Perf/Trace/Context.pm', # retrieve abstract from module | ||
11 | AUTHOR => 'Tom Zanussi <tzanussi@gmail.com>') : ()), | ||
12 | LIBS => [''], # e.g., '-lm' | ||
13 | DEFINE => '-I ../..', # e.g., '-DHAVE_SOMETHING' | ||
14 | INC => '-I.', # e.g., '-I. -I/usr/include/other' | ||
15 | # Un-comment this if you add C files to link with later: | ||
16 | OBJECT => 'Context.o', # link all the C files too | ||
17 | ); | ||
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/README b/tools/perf/scripts/perl/Perf-Trace-Util/README new file mode 100644 index 000000000000..9a9707630791 --- /dev/null +++ b/tools/perf/scripts/perl/Perf-Trace-Util/README | |||
@@ -0,0 +1,59 @@ | |||
1 | Perf-Trace-Util version 0.01 | ||
2 | ============================ | ||
3 | |||
4 | This module contains utility functions for use with perf trace. | ||
5 | |||
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 | ||
8 | 'used', while Util.pm contains useful but optional utility functions | ||
9 | that scripts may want to use. Context.pm contains the Perl->C | ||
10 | interface that allows scripts to access data in the embedding perf | ||
11 | executable; scripts wishing to do that should 'use Context.pm'. | ||
12 | |||
13 | The Perl->C perf interface is completely driven by Context.xs. If you | ||
14 | want to add new Perl functions that end up accessing C data in the | ||
15 | perf executable, you add desciptions of the new functions here. | ||
16 | scripting_context is a pointer to the perf data in the perf executable | ||
17 | that you want to access - it's passed as the second parameter, | ||
18 | $context, to all handler functions. | ||
19 | |||
20 | After you do that: | ||
21 | |||
22 | perl Makefile.PL # to create a Makefile for the next step | ||
23 | make # to create Context.c | ||
24 | |||
25 | edit Context.c to add const to the char* file = __FILE__ line in | ||
26 | XS(boot_Perf__Trace__Context) to silence a warning/error. | ||
27 | |||
28 | You can delete the Makefile, object files and anything else that was | ||
29 | generated e.g. blib and shared library, etc, except for of course | ||
30 | Context.c | ||
31 | |||
32 | You should then be able to run the normal perf make as usual. | ||
33 | |||
34 | INSTALLATION | ||
35 | |||
36 | Building perf with perf trace Perl scripting should install this | ||
37 | module in the right place. | ||
38 | |||
39 | You should make sure libperl and ExtUtils/Embed.pm are installed first | ||
40 | e.g. apt-get install libperl-dev or yum install perl-ExtUtils-Embed. | ||
41 | |||
42 | DEPENDENCIES | ||
43 | |||
44 | This module requires these other modules and libraries: | ||
45 | |||
46 | None | ||
47 | |||
48 | COPYRIGHT AND LICENCE | ||
49 | |||
50 | Copyright (C) 2009 by Tom Zanussi <tzanussi@gmail.com> | ||
51 | |||
52 | This library is free software; you can redistribute it and/or modify | ||
53 | it under the same terms as Perl itself, either Perl version 5.10.0 or, | ||
54 | at your option, any later version of Perl 5 you may have available. | ||
55 | |||
56 | Alternatively, this software may be distributed under the terms of the | ||
57 | GNU General Public License ("GPL") version 2 as published by the Free | ||
58 | Software Foundation. | ||
59 | |||
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 new file mode 100644 index 000000000000..6c7f3659cb17 --- /dev/null +++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Context.pm | |||
@@ -0,0 +1,55 @@ | |||
1 | package Perf::Trace::Context; | ||
2 | |||
3 | use 5.010000; | ||
4 | use strict; | ||
5 | use warnings; | ||
6 | |||
7 | require Exporter; | ||
8 | |||
9 | our @ISA = qw(Exporter); | ||
10 | |||
11 | our %EXPORT_TAGS = ( 'all' => [ qw( | ||
12 | ) ] ); | ||
13 | |||
14 | our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | ||
15 | |||
16 | our @EXPORT = qw( | ||
17 | common_pc common_flags common_lock_depth | ||
18 | ); | ||
19 | |||
20 | our $VERSION = '0.01'; | ||
21 | |||
22 | require XSLoader; | ||
23 | XSLoader::load('Perf::Trace::Context', $VERSION); | ||
24 | |||
25 | 1; | ||
26 | __END__ | ||
27 | =head1 NAME | ||
28 | |||
29 | Perf::Trace::Context - Perl extension for accessing functions in perf. | ||
30 | |||
31 | =head1 SYNOPSIS | ||
32 | |||
33 | use Perf::Trace::Context; | ||
34 | |||
35 | =head1 SEE ALSO | ||
36 | |||
37 | Perf (trace) documentation | ||
38 | |||
39 | =head1 AUTHOR | ||
40 | |||
41 | Tom Zanussi, E<lt>tzanussi@gmail.com<gt> | ||
42 | |||
43 | =head1 COPYRIGHT AND LICENSE | ||
44 | |||
45 | Copyright (C) 2009 by Tom Zanussi | ||
46 | |||
47 | This library is free software; you can redistribute it and/or modify | ||
48 | it under the same terms as Perl itself, either Perl version 5.10.0 or, | ||
49 | at your option, any later version of Perl 5 you may have available. | ||
50 | |||
51 | Alternatively, this software may be distributed under the terms of the | ||
52 | GNU General Public License ("GPL") version 2 as published by the Free | ||
53 | Software Foundation. | ||
54 | |||
55 | =cut | ||
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 new file mode 100644 index 000000000000..9df376a9f629 --- /dev/null +++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Core.pm | |||
@@ -0,0 +1,192 @@ | |||
1 | package Perf::Trace::Core; | ||
2 | |||
3 | use 5.010000; | ||
4 | use strict; | ||
5 | use warnings; | ||
6 | |||
7 | require Exporter; | ||
8 | |||
9 | our @ISA = qw(Exporter); | ||
10 | |||
11 | our %EXPORT_TAGS = ( 'all' => [ qw( | ||
12 | ) ] ); | ||
13 | |||
14 | our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | ||
15 | |||
16 | our @EXPORT = qw( | ||
17 | define_flag_field define_flag_value flag_str dump_flag_fields | ||
18 | define_symbolic_field define_symbolic_value symbol_str dump_symbolic_fields | ||
19 | trace_flag_str | ||
20 | ); | ||
21 | |||
22 | our $VERSION = '0.01'; | ||
23 | |||
24 | my %trace_flags = (0x00 => "NONE", | ||
25 | 0x01 => "IRQS_OFF", | ||
26 | 0x02 => "IRQS_NOSUPPORT", | ||
27 | 0x04 => "NEED_RESCHED", | ||
28 | 0x08 => "HARDIRQ", | ||
29 | 0x10 => "SOFTIRQ"); | ||
30 | |||
31 | sub trace_flag_str | ||
32 | { | ||
33 | my ($value) = @_; | ||
34 | |||
35 | my $string; | ||
36 | |||
37 | my $print_delim = 0; | ||
38 | |||
39 | foreach my $idx (sort {$a <=> $b} keys %trace_flags) { | ||
40 | if (!$value && !$idx) { | ||
41 | $string .= "NONE"; | ||
42 | last; | ||
43 | } | ||
44 | |||
45 | if ($idx && ($value & $idx) == $idx) { | ||
46 | if ($print_delim) { | ||
47 | $string .= " | "; | ||
48 | } | ||
49 | $string .= "$trace_flags{$idx}"; | ||
50 | $print_delim = 1; | ||
51 | $value &= ~$idx; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | return $string; | ||
56 | } | ||
57 | |||
58 | my %flag_fields; | ||
59 | my %symbolic_fields; | ||
60 | |||
61 | sub flag_str | ||
62 | { | ||
63 | my ($event_name, $field_name, $value) = @_; | ||
64 | |||
65 | my $string; | ||
66 | |||
67 | if ($flag_fields{$event_name}{$field_name}) { | ||
68 | my $print_delim = 0; | ||
69 | foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event_name}{$field_name}{"values"}}) { | ||
70 | if (!$value && !$idx) { | ||
71 | $string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}"; | ||
72 | last; | ||
73 | } | ||
74 | if ($idx && ($value & $idx) == $idx) { | ||
75 | if ($print_delim && $flag_fields{$event_name}{$field_name}{'delim'}) { | ||
76 | $string .= " $flag_fields{$event_name}{$field_name}{'delim'} "; | ||
77 | } | ||
78 | $string .= "$flag_fields{$event_name}{$field_name}{'values'}{$idx}"; | ||
79 | $print_delim = 1; | ||
80 | $value &= ~$idx; | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | |||
85 | return $string; | ||
86 | } | ||
87 | |||
88 | sub define_flag_field | ||
89 | { | ||
90 | my ($event_name, $field_name, $delim) = @_; | ||
91 | |||
92 | $flag_fields{$event_name}{$field_name}{"delim"} = $delim; | ||
93 | } | ||
94 | |||
95 | sub define_flag_value | ||
96 | { | ||
97 | my ($event_name, $field_name, $value, $field_str) = @_; | ||
98 | |||
99 | $flag_fields{$event_name}{$field_name}{"values"}{$value} = $field_str; | ||
100 | } | ||
101 | |||
102 | sub dump_flag_fields | ||
103 | { | ||
104 | for my $event (keys %flag_fields) { | ||
105 | print "event $event:\n"; | ||
106 | for my $field (keys %{$flag_fields{$event}}) { | ||
107 | print " field: $field:\n"; | ||
108 | print " delim: $flag_fields{$event}{$field}{'delim'}\n"; | ||
109 | foreach my $idx (sort {$a <=> $b} keys %{$flag_fields{$event}{$field}{"values"}}) { | ||
110 | print " value $idx: $flag_fields{$event}{$field}{'values'}{$idx}\n"; | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | sub symbol_str | ||
117 | { | ||
118 | my ($event_name, $field_name, $value) = @_; | ||
119 | |||
120 | if ($symbolic_fields{$event_name}{$field_name}) { | ||
121 | foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event_name}{$field_name}{"values"}}) { | ||
122 | if (!$value && !$idx) { | ||
123 | return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}"; | ||
124 | last; | ||
125 | } | ||
126 | if ($value == $idx) { | ||
127 | return "$symbolic_fields{$event_name}{$field_name}{'values'}{$idx}"; | ||
128 | } | ||
129 | } | ||
130 | } | ||
131 | |||
132 | return undef; | ||
133 | } | ||
134 | |||
135 | sub define_symbolic_field | ||
136 | { | ||
137 | my ($event_name, $field_name) = @_; | ||
138 | |||
139 | # nothing to do, really | ||
140 | } | ||
141 | |||
142 | sub define_symbolic_value | ||
143 | { | ||
144 | my ($event_name, $field_name, $value, $field_str) = @_; | ||
145 | |||
146 | $symbolic_fields{$event_name}{$field_name}{"values"}{$value} = $field_str; | ||
147 | } | ||
148 | |||
149 | sub dump_symbolic_fields | ||
150 | { | ||
151 | for my $event (keys %symbolic_fields) { | ||
152 | print "event $event:\n"; | ||
153 | for my $field (keys %{$symbolic_fields{$event}}) { | ||
154 | print " field: $field:\n"; | ||
155 | foreach my $idx (sort {$a <=> $b} keys %{$symbolic_fields{$event}{$field}{"values"}}) { | ||
156 | print " value $idx: $symbolic_fields{$event}{$field}{'values'}{$idx}\n"; | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | } | ||
161 | |||
162 | 1; | ||
163 | __END__ | ||
164 | =head1 NAME | ||
165 | |||
166 | Perf::Trace::Core - Perl extension for perf trace | ||
167 | |||
168 | =head1 SYNOPSIS | ||
169 | |||
170 | use Perf::Trace::Core | ||
171 | |||
172 | =head1 SEE ALSO | ||
173 | |||
174 | Perf (trace) documentation | ||
175 | |||
176 | =head1 AUTHOR | ||
177 | |||
178 | Tom Zanussi, E<lt>tzanussi@gmail.com<gt> | ||
179 | |||
180 | =head1 COPYRIGHT AND LICENSE | ||
181 | |||
182 | Copyright (C) 2009 by Tom Zanussi | ||
183 | |||
184 | This library is free software; you can redistribute it and/or modify | ||
185 | it under the same terms as Perl itself, either Perl version 5.10.0 or, | ||
186 | at your option, any later version of Perl 5 you may have available. | ||
187 | |||
188 | Alternatively, this software may be distributed under the terms of the | ||
189 | GNU General Public License ("GPL") version 2 as published by the Free | ||
190 | Software Foundation. | ||
191 | |||
192 | =cut | ||
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 new file mode 100644 index 000000000000..f869c48dc9b0 --- /dev/null +++ b/tools/perf/scripts/perl/Perf-Trace-Util/lib/Perf/Trace/Util.pm | |||
@@ -0,0 +1,88 @@ | |||
1 | package Perf::Trace::Util; | ||
2 | |||
3 | use 5.010000; | ||
4 | use strict; | ||
5 | use warnings; | ||
6 | |||
7 | require Exporter; | ||
8 | |||
9 | our @ISA = qw(Exporter); | ||
10 | |||
11 | our %EXPORT_TAGS = ( 'all' => [ qw( | ||
12 | ) ] ); | ||
13 | |||
14 | our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | ||
15 | |||
16 | our @EXPORT = qw( | ||
17 | avg nsecs nsecs_secs nsecs_nsecs nsecs_usecs print_nsecs | ||
18 | ); | ||
19 | |||
20 | our $VERSION = '0.01'; | ||
21 | |||
22 | sub avg | ||
23 | { | ||
24 | my ($total, $n) = @_; | ||
25 | |||
26 | return $total / $n; | ||
27 | } | ||
28 | |||
29 | my $NSECS_PER_SEC = 1000000000; | ||
30 | |||
31 | sub nsecs | ||
32 | { | ||
33 | my ($secs, $nsecs) = @_; | ||
34 | |||
35 | return $secs * $NSECS_PER_SEC + $nsecs; | ||
36 | } | ||
37 | |||
38 | sub nsecs_secs { | ||
39 | my ($nsecs) = @_; | ||
40 | |||
41 | return $nsecs / $NSECS_PER_SEC; | ||
42 | } | ||
43 | |||
44 | sub nsecs_nsecs { | ||
45 | my ($nsecs) = @_; | ||
46 | |||
47 | return $nsecs % $NSECS_PER_SEC; | ||
48 | } | ||
49 | |||
50 | sub nsecs_str { | ||
51 | my ($nsecs) = @_; | ||
52 | |||
53 | my $str = sprintf("%5u.%09u", nsecs_secs($nsecs), nsecs_nsecs($nsecs)); | ||
54 | |||
55 | return $str; | ||
56 | } | ||
57 | |||
58 | 1; | ||
59 | __END__ | ||
60 | =head1 NAME | ||
61 | |||
62 | Perf::Trace::Util - Perl extension for perf trace | ||
63 | |||
64 | =head1 SYNOPSIS | ||
65 | |||
66 | use Perf::Trace::Util; | ||
67 | |||
68 | =head1 SEE ALSO | ||
69 | |||
70 | Perf (trace) documentation | ||
71 | |||
72 | =head1 AUTHOR | ||
73 | |||
74 | Tom Zanussi, E<lt>tzanussi@gmail.com<gt> | ||
75 | |||
76 | =head1 COPYRIGHT AND LICENSE | ||
77 | |||
78 | Copyright (C) 2009 by Tom Zanussi | ||
79 | |||
80 | This library is free software; you can redistribute it and/or modify | ||
81 | it under the same terms as Perl itself, either Perl version 5.10.0 or, | ||
82 | at your option, any later version of Perl 5 you may have available. | ||
83 | |||
84 | Alternatively, this software may be distributed under the terms of the | ||
85 | GNU General Public License ("GPL") version 2 as published by the Free | ||
86 | Software Foundation. | ||
87 | |||
88 | =cut | ||
diff --git a/tools/perf/scripts/perl/Perf-Trace-Util/typemap b/tools/perf/scripts/perl/Perf-Trace-Util/typemap new file mode 100644 index 000000000000..840836804aa7 --- /dev/null +++ b/tools/perf/scripts/perl/Perf-Trace-Util/typemap | |||
@@ -0,0 +1 @@ | |||
struct scripting_context * T_PTR | |||
diff --git a/tools/perf/scripts/perl/bin/check-perf-trace-record b/tools/perf/scripts/perl/bin/check-perf-trace-record new file mode 100644 index 000000000000..e6cb1474f8e8 --- /dev/null +++ b/tools/perf/scripts/perl/bin/check-perf-trace-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e kmem:kmalloc -e irq:softirq_entry -e kmem:kfree | ||
diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-record b/tools/perf/scripts/perl/bin/failed-syscalls-record new file mode 100644 index 000000000000..f8885d389e6f --- /dev/null +++ b/tools/perf/scripts/perl/bin/failed-syscalls-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit | ||
diff --git a/tools/perf/scripts/perl/bin/failed-syscalls-report b/tools/perf/scripts/perl/bin/failed-syscalls-report new file mode 100644 index 000000000000..8bfc660e5056 --- /dev/null +++ b/tools/perf/scripts/perl/bin/failed-syscalls-report | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/bash | ||
2 | # description: system-wide failed syscalls | ||
3 | # args: [comm] | ||
4 | perf trace -s ~/libexec/perf-core/scripts/perl/failed-syscalls.pl $1 | ||
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-record b/tools/perf/scripts/perl/bin/rw-by-file-record new file mode 100644 index 000000000000..b25056ebf963 --- /dev/null +++ b/tools/perf/scripts/perl/bin/rw-by-file-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_enter_write | ||
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report new file mode 100644 index 000000000000..eddb9ccce6a5 --- /dev/null +++ b/tools/perf/scripts/perl/bin/rw-by-file-report | |||
@@ -0,0 +1,7 @@ | |||
1 | #!/bin/bash | ||
2 | # description: r/w activity for a program, by file | ||
3 | # args: <comm> | ||
4 | perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1 | ||
5 | |||
6 | |||
7 | |||
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-record b/tools/perf/scripts/perl/bin/rw-by-pid-record new file mode 100644 index 000000000000..8903979c5b6c --- /dev/null +++ b/tools/perf/scripts/perl/bin/rw-by-pid-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e syscalls:sys_enter_read -e syscalls:sys_exit_read -e syscalls:sys_enter_write -e syscalls:sys_exit_write | ||
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report new file mode 100644 index 000000000000..7f44c25cc857 --- /dev/null +++ b/tools/perf/scripts/perl/bin/rw-by-pid-report | |||
@@ -0,0 +1,6 @@ | |||
1 | #!/bin/bash | ||
2 | # description: system-wide r/w activity | ||
3 | perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl | ||
4 | |||
5 | |||
6 | |||
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-record b/tools/perf/scripts/perl/bin/wakeup-latency-record new file mode 100644 index 000000000000..6abedda911a4 --- /dev/null +++ b/tools/perf/scripts/perl/bin/wakeup-latency-record | |||
@@ -0,0 +1,6 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e sched:sched_switch -e sched:sched_wakeup | ||
3 | |||
4 | |||
5 | |||
6 | |||
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report new file mode 100644 index 000000000000..fce3adcb3249 --- /dev/null +++ b/tools/perf/scripts/perl/bin/wakeup-latency-report | |||
@@ -0,0 +1,6 @@ | |||
1 | #!/bin/bash | ||
2 | # description: system-wide min/max/avg wakeup latency | ||
3 | perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl | ||
4 | |||
5 | |||
6 | |||
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-record b/tools/perf/scripts/perl/bin/workqueue-stats-record new file mode 100644 index 000000000000..fce6637b19ba --- /dev/null +++ b/tools/perf/scripts/perl/bin/workqueue-stats-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion | ||
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report new file mode 100644 index 000000000000..71cfbd182fb9 --- /dev/null +++ b/tools/perf/scripts/perl/bin/workqueue-stats-report | |||
@@ -0,0 +1,7 @@ | |||
1 | #!/bin/bash | ||
2 | # description: workqueue stats (ins/exe/create/destroy) | ||
3 | perf trace -s ~/libexec/perf-core/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 new file mode 100644 index 000000000000..4e7dc0a407a5 --- /dev/null +++ b/tools/perf/scripts/perl/check-perf-trace.pl | |||
@@ -0,0 +1,106 @@ | |||
1 | # perf trace event handlers, generated by perf trace -g perl | ||
2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | |||
5 | # This script tests basic functionality such as flag and symbol | ||
6 | # strings, common_xxx() calls back into perf, begin, end, unhandled | ||
7 | # events, etc. Basically, if this script runs successfully and | ||
8 | # displays expected results, perl scripting support should be ok. | ||
9 | |||
10 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
11 | use lib "./Perf-Trace-Util/lib"; | ||
12 | use Perf::Trace::Core; | ||
13 | use Perf::Trace::Context; | ||
14 | use Perf::Trace::Util; | ||
15 | |||
16 | sub trace_begin | ||
17 | { | ||
18 | print "trace_begin\n"; | ||
19 | } | ||
20 | |||
21 | sub trace_end | ||
22 | { | ||
23 | print "trace_end\n"; | ||
24 | |||
25 | print_unhandled(); | ||
26 | } | ||
27 | |||
28 | sub irq::softirq_entry | ||
29 | { | ||
30 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
31 | $common_pid, $common_comm, | ||
32 | $vec) = @_; | ||
33 | |||
34 | print_header($event_name, $common_cpu, $common_secs, $common_nsecs, | ||
35 | $common_pid, $common_comm); | ||
36 | |||
37 | print_uncommon($context); | ||
38 | |||
39 | printf("vec=%s\n", | ||
40 | symbol_str("irq::softirq_entry", "vec", $vec)); | ||
41 | } | ||
42 | |||
43 | sub kmem::kmalloc | ||
44 | { | ||
45 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
46 | $common_pid, $common_comm, | ||
47 | $call_site, $ptr, $bytes_req, $bytes_alloc, | ||
48 | $gfp_flags) = @_; | ||
49 | |||
50 | print_header($event_name, $common_cpu, $common_secs, $common_nsecs, | ||
51 | $common_pid, $common_comm); | ||
52 | |||
53 | print_uncommon($context); | ||
54 | |||
55 | printf("call_site=%p, ptr=%p, bytes_req=%u, bytes_alloc=%u, ". | ||
56 | "gfp_flags=%s\n", | ||
57 | $call_site, $ptr, $bytes_req, $bytes_alloc, | ||
58 | |||
59 | flag_str("kmem::kmalloc", "gfp_flags", $gfp_flags)); | ||
60 | } | ||
61 | |||
62 | # print trace fields not included in handler args | ||
63 | sub print_uncommon | ||
64 | { | ||
65 | my ($context) = @_; | ||
66 | |||
67 | printf("common_preempt_count=%d, common_flags=%s, common_lock_depth=%d, ", | ||
68 | common_pc($context), trace_flag_str(common_flags($context)), | ||
69 | common_lock_depth($context)); | ||
70 | |||
71 | } | ||
72 | |||
73 | my %unhandled; | ||
74 | |||
75 | sub print_unhandled | ||
76 | { | ||
77 | if ((scalar keys %unhandled) == 0) { | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | print "\nunhandled events:\n\n"; | ||
82 | |||
83 | printf("%-40s %10s\n", "event", "count"); | ||
84 | printf("%-40s %10s\n", "----------------------------------------", | ||
85 | "-----------"); | ||
86 | |||
87 | foreach my $event_name (keys %unhandled) { | ||
88 | printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | sub trace_unhandled | ||
93 | { | ||
94 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
95 | $common_pid, $common_comm) = @_; | ||
96 | |||
97 | $unhandled{$event_name}++; | ||
98 | } | ||
99 | |||
100 | sub print_header | ||
101 | { | ||
102 | my ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_; | ||
103 | |||
104 | printf("%-20s %5u %05u.%09u %8u %-20s ", | ||
105 | $event_name, $cpu, $secs, $nsecs, $pid, $comm); | ||
106 | } | ||
diff --git a/tools/perf/scripts/perl/failed-syscalls.pl b/tools/perf/scripts/perl/failed-syscalls.pl new file mode 100644 index 000000000000..c18e7e27a84b --- /dev/null +++ b/tools/perf/scripts/perl/failed-syscalls.pl | |||
@@ -0,0 +1,38 @@ | |||
1 | # failed system call counts | ||
2 | # (c) 2010, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | # | ||
5 | # Displays system-wide failed system call totals | ||
6 | # If a [comm] arg is specified, only syscalls called by [comm] are displayed. | ||
7 | |||
8 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
9 | use lib "./Perf-Trace-Util/lib"; | ||
10 | use Perf::Trace::Core; | ||
11 | use Perf::Trace::Context; | ||
12 | use Perf::Trace::Util; | ||
13 | |||
14 | my %failed_syscalls; | ||
15 | |||
16 | sub raw_syscalls::sys_exit | ||
17 | { | ||
18 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
19 | $common_pid, $common_comm, | ||
20 | $id, $ret) = @_; | ||
21 | |||
22 | if ($ret < 0) { | ||
23 | $failed_syscalls{$common_comm}++; | ||
24 | } | ||
25 | } | ||
26 | |||
27 | sub trace_end | ||
28 | { | ||
29 | printf("\nfailed syscalls by comm:\n\n"); | ||
30 | |||
31 | printf("%-20s %10s\n", "comm", "# errors"); | ||
32 | printf("%-20s %6s %10s\n", "--------------------", "----------"); | ||
33 | |||
34 | foreach my $comm (sort {$failed_syscalls{$b} <=> $failed_syscalls{$a}} | ||
35 | keys %failed_syscalls) { | ||
36 | printf("%-20s %10s\n", $comm, $failed_syscalls{$comm}); | ||
37 | } | ||
38 | } | ||
diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/perl/rw-by-file.pl new file mode 100644 index 000000000000..2a39097687b9 --- /dev/null +++ b/tools/perf/scripts/perl/rw-by-file.pl | |||
@@ -0,0 +1,106 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | |||
5 | # Display r/w activity for files read/written to for a given program | ||
6 | |||
7 | # The common_* event handler fields are the most useful fields common to | ||
8 | # all events. They don't necessarily correspond to the 'common_*' fields | ||
9 | # in the status files. Those fields not available as handler params can | ||
10 | # be retrieved via script functions of the form get_common_*(). | ||
11 | |||
12 | use 5.010000; | ||
13 | use strict; | ||
14 | use warnings; | ||
15 | |||
16 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
17 | use lib "./Perf-Trace-Util/lib"; | ||
18 | use Perf::Trace::Core; | ||
19 | use Perf::Trace::Util; | ||
20 | |||
21 | my $usage = "perf trace -s rw-by-file.pl <comm>\n"; | ||
22 | |||
23 | my $for_comm = shift or die $usage; | ||
24 | |||
25 | my %reads; | ||
26 | my %writes; | ||
27 | |||
28 | sub syscalls::sys_enter_read | ||
29 | { | ||
30 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
31 | $common_pid, $common_comm, $nr, $fd, $buf, $count) = @_; | ||
32 | |||
33 | if ($common_comm eq $for_comm) { | ||
34 | $reads{$fd}{bytes_requested} += $count; | ||
35 | $reads{$fd}{total_reads}++; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | sub syscalls::sys_enter_write | ||
40 | { | ||
41 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
42 | $common_pid, $common_comm, $nr, $fd, $buf, $count) = @_; | ||
43 | |||
44 | if ($common_comm eq $for_comm) { | ||
45 | $writes{$fd}{bytes_written} += $count; | ||
46 | $writes{$fd}{total_writes}++; | ||
47 | } | ||
48 | } | ||
49 | |||
50 | sub trace_end | ||
51 | { | ||
52 | printf("file read counts for $for_comm:\n\n"); | ||
53 | |||
54 | printf("%6s %10s %10s\n", "fd", "# reads", "bytes_requested"); | ||
55 | printf("%6s %10s %10s\n", "------", "----------", "-----------"); | ||
56 | |||
57 | foreach my $fd (sort {$reads{$b}{bytes_requested} <=> | ||
58 | $reads{$a}{bytes_requested}} keys %reads) { | ||
59 | my $total_reads = $reads{$fd}{total_reads}; | ||
60 | my $bytes_requested = $reads{$fd}{bytes_requested}; | ||
61 | printf("%6u %10u %10u\n", $fd, $total_reads, $bytes_requested); | ||
62 | } | ||
63 | |||
64 | printf("\nfile write counts for $for_comm:\n\n"); | ||
65 | |||
66 | printf("%6s %10s %10s\n", "fd", "# writes", "bytes_written"); | ||
67 | printf("%6s %10s %10s\n", "------", "----------", "-----------"); | ||
68 | |||
69 | foreach my $fd (sort {$writes{$b}{bytes_written} <=> | ||
70 | $writes{$a}{bytes_written}} keys %writes) { | ||
71 | my $total_writes = $writes{$fd}{total_writes}; | ||
72 | my $bytes_written = $writes{$fd}{bytes_written}; | ||
73 | printf("%6u %10u %10u\n", $fd, $total_writes, $bytes_written); | ||
74 | } | ||
75 | |||
76 | print_unhandled(); | ||
77 | } | ||
78 | |||
79 | my %unhandled; | ||
80 | |||
81 | sub print_unhandled | ||
82 | { | ||
83 | if ((scalar keys %unhandled) == 0) { | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | print "\nunhandled events:\n\n"; | ||
88 | |||
89 | printf("%-40s %10s\n", "event", "count"); | ||
90 | printf("%-40s %10s\n", "----------------------------------------", | ||
91 | "-----------"); | ||
92 | |||
93 | foreach my $event_name (keys %unhandled) { | ||
94 | printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | sub trace_unhandled | ||
99 | { | ||
100 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
101 | $common_pid, $common_comm) = @_; | ||
102 | |||
103 | $unhandled{$event_name}++; | ||
104 | } | ||
105 | |||
106 | |||
diff --git a/tools/perf/scripts/perl/rw-by-pid.pl b/tools/perf/scripts/perl/rw-by-pid.pl new file mode 100644 index 000000000000..da601fae1a00 --- /dev/null +++ b/tools/perf/scripts/perl/rw-by-pid.pl | |||
@@ -0,0 +1,170 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | |||
5 | # Display r/w activity for all processes | ||
6 | |||
7 | # The common_* event handler fields are the most useful fields common to | ||
8 | # all events. They don't necessarily correspond to the 'common_*' fields | ||
9 | # in the status files. Those fields not available as handler params can | ||
10 | # be retrieved via script functions of the form get_common_*(). | ||
11 | |||
12 | use 5.010000; | ||
13 | use strict; | ||
14 | use warnings; | ||
15 | |||
16 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
17 | use lib "./Perf-Trace-Util/lib"; | ||
18 | use Perf::Trace::Core; | ||
19 | use Perf::Trace::Util; | ||
20 | |||
21 | my %reads; | ||
22 | my %writes; | ||
23 | |||
24 | sub syscalls::sys_exit_read | ||
25 | { | ||
26 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
27 | $common_pid, $common_comm, | ||
28 | $nr, $ret) = @_; | ||
29 | |||
30 | if ($ret > 0) { | ||
31 | $reads{$common_pid}{bytes_read} += $ret; | ||
32 | } else { | ||
33 | if (!defined ($reads{$common_pid}{bytes_read})) { | ||
34 | $reads{$common_pid}{bytes_read} = 0; | ||
35 | } | ||
36 | $reads{$common_pid}{errors}{$ret}++; | ||
37 | } | ||
38 | } | ||
39 | |||
40 | sub syscalls::sys_enter_read | ||
41 | { | ||
42 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
43 | $common_pid, $common_comm, | ||
44 | $nr, $fd, $buf, $count) = @_; | ||
45 | |||
46 | $reads{$common_pid}{bytes_requested} += $count; | ||
47 | $reads{$common_pid}{total_reads}++; | ||
48 | $reads{$common_pid}{comm} = $common_comm; | ||
49 | } | ||
50 | |||
51 | sub syscalls::sys_exit_write | ||
52 | { | ||
53 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
54 | $common_pid, $common_comm, | ||
55 | $nr, $ret) = @_; | ||
56 | |||
57 | if ($ret <= 0) { | ||
58 | $writes{$common_pid}{errors}{$ret}++; | ||
59 | } | ||
60 | } | ||
61 | |||
62 | sub syscalls::sys_enter_write | ||
63 | { | ||
64 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
65 | $common_pid, $common_comm, | ||
66 | $nr, $fd, $buf, $count) = @_; | ||
67 | |||
68 | $writes{$common_pid}{bytes_written} += $count; | ||
69 | $writes{$common_pid}{total_writes}++; | ||
70 | $writes{$common_pid}{comm} = $common_comm; | ||
71 | } | ||
72 | |||
73 | sub trace_end | ||
74 | { | ||
75 | printf("read counts by pid:\n\n"); | ||
76 | |||
77 | printf("%6s %20s %10s %10s %10s\n", "pid", "comm", | ||
78 | "# reads", "bytes_requested", "bytes_read"); | ||
79 | printf("%6s %-20s %10s %10s %10s\n", "------", "--------------------", | ||
80 | "-----------", "----------", "----------"); | ||
81 | |||
82 | foreach my $pid (sort {$reads{$b}{bytes_read} <=> | ||
83 | $reads{$a}{bytes_read}} keys %reads) { | ||
84 | my $comm = $reads{$pid}{comm}; | ||
85 | my $total_reads = $reads{$pid}{total_reads}; | ||
86 | my $bytes_requested = $reads{$pid}{bytes_requested}; | ||
87 | my $bytes_read = $reads{$pid}{bytes_read}; | ||
88 | |||
89 | printf("%6s %-20s %10s %10s %10s\n", $pid, $comm, | ||
90 | $total_reads, $bytes_requested, $bytes_read); | ||
91 | } | ||
92 | |||
93 | printf("\nfailed reads by pid:\n\n"); | ||
94 | |||
95 | printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors"); | ||
96 | printf("%6s %20s %6s %10s\n", "------", "--------------------", | ||
97 | "------", "----------"); | ||
98 | |||
99 | foreach my $pid (keys %reads) { | ||
100 | my $comm = $reads{$pid}{comm}; | ||
101 | foreach my $err (sort {$reads{$b}{comm} cmp $reads{$a}{comm}} | ||
102 | keys %{$reads{$pid}{errors}}) { | ||
103 | my $errors = $reads{$pid}{errors}{$err}; | ||
104 | |||
105 | printf("%6d %-20s %6d %10s\n", $pid, $comm, $err, $errors); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | printf("\nwrite counts by pid:\n\n"); | ||
110 | |||
111 | printf("%6s %20s %10s %10s\n", "pid", "comm", | ||
112 | "# writes", "bytes_written"); | ||
113 | printf("%6s %-20s %10s %10s\n", "------", "--------------------", | ||
114 | "-----------", "----------"); | ||
115 | |||
116 | foreach my $pid (sort {$writes{$b}{bytes_written} <=> | ||
117 | $writes{$a}{bytes_written}} keys %writes) { | ||
118 | my $comm = $writes{$pid}{comm}; | ||
119 | my $total_writes = $writes{$pid}{total_writes}; | ||
120 | my $bytes_written = $writes{$pid}{bytes_written}; | ||
121 | |||
122 | printf("%6s %-20s %10s %10s\n", $pid, $comm, | ||
123 | $total_writes, $bytes_written); | ||
124 | } | ||
125 | |||
126 | printf("\nfailed writes by pid:\n\n"); | ||
127 | |||
128 | printf("%6s %20s %6s %10s\n", "pid", "comm", "error #", "# errors"); | ||
129 | printf("%6s %20s %6s %10s\n", "------", "--------------------", | ||
130 | "------", "----------"); | ||
131 | |||
132 | foreach my $pid (keys %writes) { | ||
133 | my $comm = $writes{$pid}{comm}; | ||
134 | foreach my $err (sort {$writes{$b}{comm} cmp $writes{$a}{comm}} | ||
135 | keys %{$writes{$pid}{errors}}) { | ||
136 | my $errors = $writes{$pid}{errors}{$err}; | ||
137 | |||
138 | printf("%6d %-20s %6d %10s\n", $pid, $comm, $err, $errors); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | print_unhandled(); | ||
143 | } | ||
144 | |||
145 | my %unhandled; | ||
146 | |||
147 | sub print_unhandled | ||
148 | { | ||
149 | if ((scalar keys %unhandled) == 0) { | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | print "\nunhandled events:\n\n"; | ||
154 | |||
155 | printf("%-40s %10s\n", "event", "count"); | ||
156 | printf("%-40s %10s\n", "----------------------------------------", | ||
157 | "-----------"); | ||
158 | |||
159 | foreach my $event_name (keys %unhandled) { | ||
160 | printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | sub trace_unhandled | ||
165 | { | ||
166 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
167 | $common_pid, $common_comm) = @_; | ||
168 | |||
169 | $unhandled{$event_name}++; | ||
170 | } | ||
diff --git a/tools/perf/scripts/perl/wakeup-latency.pl b/tools/perf/scripts/perl/wakeup-latency.pl new file mode 100644 index 000000000000..ed58ef284e23 --- /dev/null +++ b/tools/perf/scripts/perl/wakeup-latency.pl | |||
@@ -0,0 +1,103 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | |||
5 | # Display avg/min/max wakeup latency | ||
6 | |||
7 | # The common_* event handler fields are the most useful fields common to | ||
8 | # all events. They don't necessarily correspond to the 'common_*' fields | ||
9 | # in the status files. Those fields not available as handler params can | ||
10 | # be retrieved via script functions of the form get_common_*(). | ||
11 | |||
12 | use 5.010000; | ||
13 | use strict; | ||
14 | use warnings; | ||
15 | |||
16 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
17 | use lib "./Perf-Trace-Util/lib"; | ||
18 | use Perf::Trace::Core; | ||
19 | use Perf::Trace::Util; | ||
20 | |||
21 | my %last_wakeup; | ||
22 | |||
23 | my $max_wakeup_latency; | ||
24 | my $min_wakeup_latency; | ||
25 | my $total_wakeup_latency; | ||
26 | my $total_wakeups; | ||
27 | |||
28 | sub sched::sched_switch | ||
29 | { | ||
30 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
31 | $common_pid, $common_comm, | ||
32 | $prev_comm, $prev_pid, $prev_prio, $prev_state, $next_comm, $next_pid, | ||
33 | $next_prio) = @_; | ||
34 | |||
35 | my $wakeup_ts = $last_wakeup{$common_cpu}{ts}; | ||
36 | if ($wakeup_ts) { | ||
37 | my $switch_ts = nsecs($common_secs, $common_nsecs); | ||
38 | my $wakeup_latency = $switch_ts - $wakeup_ts; | ||
39 | if ($wakeup_latency > $max_wakeup_latency) { | ||
40 | $max_wakeup_latency = $wakeup_latency; | ||
41 | } | ||
42 | if ($wakeup_latency < $min_wakeup_latency) { | ||
43 | $min_wakeup_latency = $wakeup_latency; | ||
44 | } | ||
45 | $total_wakeup_latency += $wakeup_latency; | ||
46 | $total_wakeups++; | ||
47 | } | ||
48 | $last_wakeup{$common_cpu}{ts} = 0; | ||
49 | } | ||
50 | |||
51 | sub sched::sched_wakeup | ||
52 | { | ||
53 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
54 | $common_pid, $common_comm, | ||
55 | $comm, $pid, $prio, $success, $target_cpu) = @_; | ||
56 | |||
57 | $last_wakeup{$target_cpu}{ts} = nsecs($common_secs, $common_nsecs); | ||
58 | } | ||
59 | |||
60 | sub trace_begin | ||
61 | { | ||
62 | $min_wakeup_latency = 1000000000; | ||
63 | $max_wakeup_latency = 0; | ||
64 | } | ||
65 | |||
66 | sub trace_end | ||
67 | { | ||
68 | printf("wakeup_latency stats:\n\n"); | ||
69 | print "total_wakeups: $total_wakeups\n"; | ||
70 | printf("avg_wakeup_latency (ns): %u\n", | ||
71 | avg($total_wakeup_latency, $total_wakeups)); | ||
72 | printf("min_wakeup_latency (ns): %u\n", $min_wakeup_latency); | ||
73 | printf("max_wakeup_latency (ns): %u\n", $max_wakeup_latency); | ||
74 | |||
75 | print_unhandled(); | ||
76 | } | ||
77 | |||
78 | my %unhandled; | ||
79 | |||
80 | sub print_unhandled | ||
81 | { | ||
82 | if ((scalar keys %unhandled) == 0) { | ||
83 | return; | ||
84 | } | ||
85 | |||
86 | print "\nunhandled events:\n\n"; | ||
87 | |||
88 | printf("%-40s %10s\n", "event", "count"); | ||
89 | printf("%-40s %10s\n", "----------------------------------------", | ||
90 | "-----------"); | ||
91 | |||
92 | foreach my $event_name (keys %unhandled) { | ||
93 | printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | sub trace_unhandled | ||
98 | { | ||
99 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
100 | $common_pid, $common_comm) = @_; | ||
101 | |||
102 | $unhandled{$event_name}++; | ||
103 | } | ||
diff --git a/tools/perf/scripts/perl/workqueue-stats.pl b/tools/perf/scripts/perl/workqueue-stats.pl new file mode 100644 index 000000000000..511302c8a494 --- /dev/null +++ b/tools/perf/scripts/perl/workqueue-stats.pl | |||
@@ -0,0 +1,129 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | |||
5 | # Displays workqueue stats | ||
6 | # | ||
7 | # Usage: | ||
8 | # | ||
9 | # perf record -c 1 -f -a -R -e workqueue:workqueue_creation -e | ||
10 | # workqueue:workqueue_destruction -e workqueue:workqueue_execution | ||
11 | # -e workqueue:workqueue_insertion | ||
12 | # | ||
13 | # perf trace -p -s tools/perf/scripts/perl/workqueue-stats.pl | ||
14 | |||
15 | use 5.010000; | ||
16 | use strict; | ||
17 | use warnings; | ||
18 | |||
19 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
20 | use lib "./Perf-Trace-Util/lib"; | ||
21 | use Perf::Trace::Core; | ||
22 | use Perf::Trace::Util; | ||
23 | |||
24 | my @cpus; | ||
25 | |||
26 | sub workqueue::workqueue_destruction | ||
27 | { | ||
28 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
29 | $common_pid, $common_comm, | ||
30 | $thread_comm, $thread_pid) = @_; | ||
31 | |||
32 | $cpus[$common_cpu]{$thread_pid}{destroyed}++; | ||
33 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
34 | } | ||
35 | |||
36 | sub workqueue::workqueue_creation | ||
37 | { | ||
38 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
39 | $common_pid, $common_comm, | ||
40 | $thread_comm, $thread_pid, $cpu) = @_; | ||
41 | |||
42 | $cpus[$common_cpu]{$thread_pid}{created}++; | ||
43 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
44 | } | ||
45 | |||
46 | sub workqueue::workqueue_execution | ||
47 | { | ||
48 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
49 | $common_pid, $common_comm, | ||
50 | $thread_comm, $thread_pid, $func) = @_; | ||
51 | |||
52 | $cpus[$common_cpu]{$thread_pid}{executed}++; | ||
53 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
54 | } | ||
55 | |||
56 | sub workqueue::workqueue_insertion | ||
57 | { | ||
58 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
59 | $common_pid, $common_comm, | ||
60 | $thread_comm, $thread_pid, $func) = @_; | ||
61 | |||
62 | $cpus[$common_cpu]{$thread_pid}{inserted}++; | ||
63 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
64 | } | ||
65 | |||
66 | sub trace_end | ||
67 | { | ||
68 | print "workqueue work stats:\n\n"; | ||
69 | my $cpu = 0; | ||
70 | printf("%3s %6s %6s\t%-20s\n", "cpu", "ins", "exec", "name"); | ||
71 | printf("%3s %6s %6s\t%-20s\n", "---", "---", "----", "----"); | ||
72 | foreach my $pidhash (@cpus) { | ||
73 | while ((my $pid, my $wqhash) = each %$pidhash) { | ||
74 | my $ins = $$wqhash{'inserted'}; | ||
75 | my $exe = $$wqhash{'executed'}; | ||
76 | my $comm = $$wqhash{'comm'}; | ||
77 | if ($ins || $exe) { | ||
78 | printf("%3u %6u %6u\t%-20s\n", $cpu, $ins, $exe, $comm); | ||
79 | } | ||
80 | } | ||
81 | $cpu++; | ||
82 | } | ||
83 | |||
84 | $cpu = 0; | ||
85 | print "\nworkqueue lifecycle stats:\n\n"; | ||
86 | printf("%3s %6s %6s\t%-20s\n", "cpu", "created", "destroyed", "name"); | ||
87 | printf("%3s %6s %6s\t%-20s\n", "---", "-------", "---------", "----"); | ||
88 | foreach my $pidhash (@cpus) { | ||
89 | while ((my $pid, my $wqhash) = each %$pidhash) { | ||
90 | my $created = $$wqhash{'created'}; | ||
91 | my $destroyed = $$wqhash{'destroyed'}; | ||
92 | my $comm = $$wqhash{'comm'}; | ||
93 | if ($created || $destroyed) { | ||
94 | printf("%3u %6u %6u\t%-20s\n", $cpu, $created, $destroyed, | ||
95 | $comm); | ||
96 | } | ||
97 | } | ||
98 | $cpu++; | ||
99 | } | ||
100 | |||
101 | print_unhandled(); | ||
102 | } | ||
103 | |||
104 | my %unhandled; | ||
105 | |||
106 | sub print_unhandled | ||
107 | { | ||
108 | if ((scalar keys %unhandled) == 0) { | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | print "\nunhandled events:\n\n"; | ||
113 | |||
114 | printf("%-40s %10s\n", "event", "count"); | ||
115 | printf("%-40s %10s\n", "----------------------------------------", | ||
116 | "-----------"); | ||
117 | |||
118 | foreach my $event_name (keys %unhandled) { | ||
119 | printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | sub trace_unhandled | ||
124 | { | ||
125 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
126 | $common_pid, $common_comm) = @_; | ||
127 | |||
128 | $unhandled{$event_name}++; | ||
129 | } | ||
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/Context.c b/tools/perf/scripts/python/Perf-Trace-Util/Context.c new file mode 100644 index 000000000000..957085dd5d8d --- /dev/null +++ b/tools/perf/scripts/python/Perf-Trace-Util/Context.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Context.c. Python interfaces for perf trace. | ||
3 | * | ||
4 | * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <Python.h> | ||
23 | #include "../../../perf.h" | ||
24 | #include "../../../util/trace-event.h" | ||
25 | |||
26 | PyMODINIT_FUNC initperf_trace_context(void); | ||
27 | |||
28 | static PyObject *perf_trace_context_common_pc(PyObject *self, PyObject *args) | ||
29 | { | ||
30 | static struct scripting_context *scripting_context; | ||
31 | PyObject *context; | ||
32 | int retval; | ||
33 | |||
34 | if (!PyArg_ParseTuple(args, "O", &context)) | ||
35 | return NULL; | ||
36 | |||
37 | scripting_context = PyCObject_AsVoidPtr(context); | ||
38 | retval = common_pc(scripting_context); | ||
39 | |||
40 | return Py_BuildValue("i", retval); | ||
41 | } | ||
42 | |||
43 | static PyObject *perf_trace_context_common_flags(PyObject *self, | ||
44 | PyObject *args) | ||
45 | { | ||
46 | static struct scripting_context *scripting_context; | ||
47 | PyObject *context; | ||
48 | int retval; | ||
49 | |||
50 | if (!PyArg_ParseTuple(args, "O", &context)) | ||
51 | return NULL; | ||
52 | |||
53 | scripting_context = PyCObject_AsVoidPtr(context); | ||
54 | retval = common_flags(scripting_context); | ||
55 | |||
56 | return Py_BuildValue("i", retval); | ||
57 | } | ||
58 | |||
59 | static PyObject *perf_trace_context_common_lock_depth(PyObject *self, | ||
60 | PyObject *args) | ||
61 | { | ||
62 | static struct scripting_context *scripting_context; | ||
63 | PyObject *context; | ||
64 | int retval; | ||
65 | |||
66 | if (!PyArg_ParseTuple(args, "O", &context)) | ||
67 | return NULL; | ||
68 | |||
69 | scripting_context = PyCObject_AsVoidPtr(context); | ||
70 | retval = common_lock_depth(scripting_context); | ||
71 | |||
72 | return Py_BuildValue("i", retval); | ||
73 | } | ||
74 | |||
75 | static PyMethodDef ContextMethods[] = { | ||
76 | { "common_pc", perf_trace_context_common_pc, METH_VARARGS, | ||
77 | "Get the common preempt count event field value."}, | ||
78 | { "common_flags", perf_trace_context_common_flags, METH_VARARGS, | ||
79 | "Get the common flags event field value."}, | ||
80 | { "common_lock_depth", perf_trace_context_common_lock_depth, | ||
81 | METH_VARARGS, "Get the common lock depth event field value."}, | ||
82 | { NULL, NULL, 0, NULL} | ||
83 | }; | ||
84 | |||
85 | PyMODINIT_FUNC initperf_trace_context(void) | ||
86 | { | ||
87 | (void) Py_InitModule("perf_trace_context", ContextMethods); | ||
88 | } | ||
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 new file mode 100644 index 000000000000..1dc464ee2ca8 --- /dev/null +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py | |||
@@ -0,0 +1,91 @@ | |||
1 | # Core.py - Python extension for perf trace, core functions | ||
2 | # | ||
3 | # Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com> | ||
4 | # | ||
5 | # This software may be distributed under the terms of the GNU General | ||
6 | # Public License ("GPL") version 2 as published by the Free Software | ||
7 | # Foundation. | ||
8 | |||
9 | from collections import defaultdict | ||
10 | |||
11 | def autodict(): | ||
12 | return defaultdict(autodict) | ||
13 | |||
14 | flag_fields = autodict() | ||
15 | symbolic_fields = autodict() | ||
16 | |||
17 | def define_flag_field(event_name, field_name, delim): | ||
18 | flag_fields[event_name][field_name]['delim'] = delim | ||
19 | |||
20 | def define_flag_value(event_name, field_name, value, field_str): | ||
21 | flag_fields[event_name][field_name]['values'][value] = field_str | ||
22 | |||
23 | def define_symbolic_field(event_name, field_name): | ||
24 | # nothing to do, really | ||
25 | pass | ||
26 | |||
27 | def define_symbolic_value(event_name, field_name, value, field_str): | ||
28 | symbolic_fields[event_name][field_name]['values'][value] = field_str | ||
29 | |||
30 | def flag_str(event_name, field_name, value): | ||
31 | string = "" | ||
32 | |||
33 | if flag_fields[event_name][field_name]: | ||
34 | print_delim = 0 | ||
35 | keys = flag_fields[event_name][field_name]['values'].keys() | ||
36 | keys.sort() | ||
37 | for idx in keys: | ||
38 | if not value and not idx: | ||
39 | string += flag_fields[event_name][field_name]['values'][idx] | ||
40 | break | ||
41 | if idx and (value & idx) == idx: | ||
42 | if print_delim and flag_fields[event_name][field_name]['delim']: | ||
43 | string += " " + flag_fields[event_name][field_name]['delim'] + " " | ||
44 | string += flag_fields[event_name][field_name]['values'][idx] | ||
45 | print_delim = 1 | ||
46 | value &= ~idx | ||
47 | |||
48 | return string | ||
49 | |||
50 | def symbol_str(event_name, field_name, value): | ||
51 | string = "" | ||
52 | |||
53 | if symbolic_fields[event_name][field_name]: | ||
54 | keys = symbolic_fields[event_name][field_name]['values'].keys() | ||
55 | keys.sort() | ||
56 | for idx in keys: | ||
57 | if not value and not idx: | ||
58 | string = symbolic_fields[event_name][field_name]['values'][idx] | ||
59 | break | ||
60 | if (value == idx): | ||
61 | string = symbolic_fields[event_name][field_name]['values'][idx] | ||
62 | break | ||
63 | |||
64 | return string | ||
65 | |||
66 | trace_flags = { 0x00: "NONE", \ | ||
67 | 0x01: "IRQS_OFF", \ | ||
68 | 0x02: "IRQS_NOSUPPORT", \ | ||
69 | 0x04: "NEED_RESCHED", \ | ||
70 | 0x08: "HARDIRQ", \ | ||
71 | 0x10: "SOFTIRQ" } | ||
72 | |||
73 | def trace_flag_str(value): | ||
74 | string = "" | ||
75 | print_delim = 0 | ||
76 | |||
77 | keys = trace_flags.keys() | ||
78 | |||
79 | for idx in keys: | ||
80 | if not value and not idx: | ||
81 | string += "NONE" | ||
82 | break | ||
83 | |||
84 | if idx and (value & idx) == idx: | ||
85 | if print_delim: | ||
86 | string += " | "; | ||
87 | string += trace_flags[idx] | ||
88 | print_delim = 1 | ||
89 | value &= ~idx | ||
90 | |||
91 | return string | ||
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 new file mode 100644 index 000000000000..83e91435ed09 --- /dev/null +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Util.py | |||
@@ -0,0 +1,25 @@ | |||
1 | # Util.py - Python extension for perf trace, miscellaneous utility code | ||
2 | # | ||
3 | # Copyright (C) 2010 by Tom Zanussi <tzanussi@gmail.com> | ||
4 | # | ||
5 | # This software may be distributed under the terms of the GNU General | ||
6 | # Public License ("GPL") version 2 as published by the Free Software | ||
7 | # Foundation. | ||
8 | |||
9 | NSECS_PER_SEC = 1000000000 | ||
10 | |||
11 | def avg(total, n): | ||
12 | return total / n | ||
13 | |||
14 | def nsecs(secs, nsecs): | ||
15 | return secs * NSECS_PER_SEC + nsecs | ||
16 | |||
17 | def nsecs_secs(nsecs): | ||
18 | return nsecs / NSECS_PER_SEC | ||
19 | |||
20 | def nsecs_nsecs(nsecs): | ||
21 | return nsecs % NSECS_PER_SEC | ||
22 | |||
23 | def nsecs_str(nsecs): | ||
24 | str = "%5u.%09u" % (nsecs_secs(nsecs), nsecs_nsecs(nsecs)), | ||
25 | return str | ||
diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record new file mode 100644 index 000000000000..f8885d389e6f --- /dev/null +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e raw_syscalls:sys_exit | ||
diff --git a/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report new file mode 100644 index 000000000000..1e0c0a860c87 --- /dev/null +++ b/tools/perf/scripts/python/bin/failed-syscalls-by-pid-report | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/bash | ||
2 | # description: system-wide failed syscalls, by pid | ||
3 | # args: [comm] | ||
4 | perf trace -s ~/libexec/perf-core/scripts/python/failed-syscalls-by-pid.py $1 | ||
diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-record b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record new file mode 100644 index 000000000000..45a8c50359da --- /dev/null +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter | ||
diff --git a/tools/perf/scripts/python/bin/syscall-counts-by-pid-report b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report new file mode 100644 index 000000000000..f8044d192271 --- /dev/null +++ b/tools/perf/scripts/python/bin/syscall-counts-by-pid-report | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/bash | ||
2 | # description: system-wide syscall counts, by pid | ||
3 | # args: [comm] | ||
4 | perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts-by-pid.py $1 | ||
diff --git a/tools/perf/scripts/python/bin/syscall-counts-record b/tools/perf/scripts/python/bin/syscall-counts-record new file mode 100644 index 000000000000..45a8c50359da --- /dev/null +++ b/tools/perf/scripts/python/bin/syscall-counts-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -c 1 -f -a -M -R -e raw_syscalls:sys_enter | ||
diff --git a/tools/perf/scripts/python/bin/syscall-counts-report b/tools/perf/scripts/python/bin/syscall-counts-report new file mode 100644 index 000000000000..a366aa61612f --- /dev/null +++ b/tools/perf/scripts/python/bin/syscall-counts-report | |||
@@ -0,0 +1,4 @@ | |||
1 | #!/bin/bash | ||
2 | # description: system-wide syscall counts | ||
3 | # args: [comm] | ||
4 | perf trace -s ~/libexec/perf-core/scripts/python/syscall-counts.py $1 | ||
diff --git a/tools/perf/scripts/python/check-perf-trace.py b/tools/perf/scripts/python/check-perf-trace.py new file mode 100644 index 000000000000..964d934395ff --- /dev/null +++ b/tools/perf/scripts/python/check-perf-trace.py | |||
@@ -0,0 +1,83 @@ | |||
1 | # perf trace event handlers, generated by perf trace -g python | ||
2 | # (c) 2010, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | # | ||
5 | # This script tests basic functionality such as flag and symbol | ||
6 | # strings, common_xxx() calls back into perf, begin, end, unhandled | ||
7 | # events, etc. Basically, if this script runs successfully and | ||
8 | # displays expected results, Python scripting support should be ok. | ||
9 | |||
10 | import os | ||
11 | import sys | ||
12 | |||
13 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
14 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
15 | |||
16 | from Core import * | ||
17 | from perf_trace_context import * | ||
18 | |||
19 | unhandled = autodict() | ||
20 | |||
21 | def trace_begin(): | ||
22 | print "trace_begin" | ||
23 | pass | ||
24 | |||
25 | def trace_end(): | ||
26 | print_unhandled() | ||
27 | |||
28 | def irq__softirq_entry(event_name, context, common_cpu, | ||
29 | common_secs, common_nsecs, common_pid, common_comm, | ||
30 | vec): | ||
31 | print_header(event_name, common_cpu, common_secs, common_nsecs, | ||
32 | common_pid, common_comm) | ||
33 | |||
34 | print_uncommon(context) | ||
35 | |||
36 | print "vec=%s\n" % \ | ||
37 | (symbol_str("irq__softirq_entry", "vec", vec)), | ||
38 | |||
39 | def kmem__kmalloc(event_name, context, common_cpu, | ||
40 | common_secs, common_nsecs, common_pid, common_comm, | ||
41 | call_site, ptr, bytes_req, bytes_alloc, | ||
42 | gfp_flags): | ||
43 | print_header(event_name, common_cpu, common_secs, common_nsecs, | ||
44 | common_pid, common_comm) | ||
45 | |||
46 | print_uncommon(context) | ||
47 | |||
48 | print "call_site=%u, ptr=%u, bytes_req=%u, " \ | ||
49 | "bytes_alloc=%u, gfp_flags=%s\n" % \ | ||
50 | (call_site, ptr, bytes_req, bytes_alloc, | ||
51 | |||
52 | flag_str("kmem__kmalloc", "gfp_flags", gfp_flags)), | ||
53 | |||
54 | def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, | ||
55 | common_pid, common_comm): | ||
56 | try: | ||
57 | unhandled[event_name] += 1 | ||
58 | except TypeError: | ||
59 | unhandled[event_name] = 1 | ||
60 | |||
61 | def print_header(event_name, cpu, secs, nsecs, pid, comm): | ||
62 | print "%-20s %5u %05u.%09u %8u %-20s " % \ | ||
63 | (event_name, cpu, secs, nsecs, pid, comm), | ||
64 | |||
65 | # print trace fields not included in handler args | ||
66 | def print_uncommon(context): | ||
67 | print "common_preempt_count=%d, common_flags=%s, common_lock_depth=%d, " \ | ||
68 | % (common_pc(context), trace_flag_str(common_flags(context)), \ | ||
69 | common_lock_depth(context)) | ||
70 | |||
71 | def print_unhandled(): | ||
72 | keys = unhandled.keys() | ||
73 | if not keys: | ||
74 | return | ||
75 | |||
76 | print "\nunhandled events:\n\n", | ||
77 | |||
78 | print "%-40s %10s\n" % ("event", "count"), | ||
79 | print "%-40s %10s\n" % ("----------------------------------------", \ | ||
80 | "-----------"), | ||
81 | |||
82 | for event_name in keys: | ||
83 | print "%-40s %10d\n" % (event_name, unhandled[event_name]) | ||
diff --git a/tools/perf/scripts/python/failed-syscalls-by-pid.py b/tools/perf/scripts/python/failed-syscalls-by-pid.py new file mode 100644 index 000000000000..0ca02278fe69 --- /dev/null +++ b/tools/perf/scripts/python/failed-syscalls-by-pid.py | |||
@@ -0,0 +1,68 @@ | |||
1 | # failed system call counts, by pid | ||
2 | # (c) 2010, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | # | ||
5 | # Displays system-wide failed system call totals, broken down by pid. | ||
6 | # If a [comm] arg is specified, only syscalls called by [comm] are displayed. | ||
7 | |||
8 | import os | ||
9 | import sys | ||
10 | |||
11 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
12 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
13 | |||
14 | from perf_trace_context import * | ||
15 | from Core import * | ||
16 | |||
17 | usage = "perf trace -s syscall-counts-by-pid.py [comm]\n"; | ||
18 | |||
19 | for_comm = None | ||
20 | |||
21 | if len(sys.argv) > 2: | ||
22 | sys.exit(usage) | ||
23 | |||
24 | if len(sys.argv) > 1: | ||
25 | for_comm = sys.argv[1] | ||
26 | |||
27 | syscalls = autodict() | ||
28 | |||
29 | def trace_begin(): | ||
30 | pass | ||
31 | |||
32 | def trace_end(): | ||
33 | print_error_totals() | ||
34 | |||
35 | def raw_syscalls__sys_exit(event_name, context, common_cpu, | ||
36 | common_secs, common_nsecs, common_pid, common_comm, | ||
37 | id, ret): | ||
38 | if for_comm is not None: | ||
39 | if common_comm != for_comm: | ||
40 | return | ||
41 | |||
42 | if ret < 0: | ||
43 | try: | ||
44 | syscalls[common_comm][common_pid][id][ret] += 1 | ||
45 | except TypeError: | ||
46 | syscalls[common_comm][common_pid][id][ret] = 1 | ||
47 | |||
48 | def print_error_totals(): | ||
49 | if for_comm is not None: | ||
50 | print "\nsyscall errors for %s:\n\n" % (for_comm), | ||
51 | else: | ||
52 | print "\nsyscall errors:\n\n", | ||
53 | |||
54 | print "%-30s %10s\n" % ("comm [pid]", "count"), | ||
55 | print "%-30s %10s\n" % ("------------------------------", \ | ||
56 | "----------"), | ||
57 | |||
58 | comm_keys = syscalls.keys() | ||
59 | for comm in comm_keys: | ||
60 | pid_keys = syscalls[comm].keys() | ||
61 | for pid in pid_keys: | ||
62 | print "\n%s [%d]\n" % (comm, pid), | ||
63 | id_keys = syscalls[comm][pid].keys() | ||
64 | for id in id_keys: | ||
65 | print " syscall: %-16d\n" % (id), | ||
66 | ret_keys = syscalls[comm][pid][id].keys() | ||
67 | for ret, val in sorted(syscalls[comm][pid][id].iteritems(), key = lambda(k, v): (v, k), reverse = True): | ||
68 | print " err = %-20d %10d\n" % (ret, val), | ||
diff --git a/tools/perf/scripts/python/syscall-counts-by-pid.py b/tools/perf/scripts/python/syscall-counts-by-pid.py new file mode 100644 index 000000000000..af722d6a4b3f --- /dev/null +++ b/tools/perf/scripts/python/syscall-counts-by-pid.py | |||
@@ -0,0 +1,64 @@ | |||
1 | # system call counts, by pid | ||
2 | # (c) 2010, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | # | ||
5 | # Displays system-wide system call totals, broken down by syscall. | ||
6 | # If a [comm] arg is specified, only syscalls called by [comm] are displayed. | ||
7 | |||
8 | import os | ||
9 | import sys | ||
10 | |||
11 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
12 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
13 | |||
14 | from perf_trace_context import * | ||
15 | from Core import * | ||
16 | |||
17 | usage = "perf trace -s syscall-counts-by-pid.py [comm]\n"; | ||
18 | |||
19 | for_comm = None | ||
20 | |||
21 | if len(sys.argv) > 2: | ||
22 | sys.exit(usage) | ||
23 | |||
24 | if len(sys.argv) > 1: | ||
25 | for_comm = sys.argv[1] | ||
26 | |||
27 | syscalls = autodict() | ||
28 | |||
29 | def trace_begin(): | ||
30 | pass | ||
31 | |||
32 | def trace_end(): | ||
33 | print_syscall_totals() | ||
34 | |||
35 | def raw_syscalls__sys_enter(event_name, context, common_cpu, | ||
36 | common_secs, common_nsecs, common_pid, common_comm, | ||
37 | id, args): | ||
38 | if for_comm is not None: | ||
39 | if common_comm != for_comm: | ||
40 | return | ||
41 | try: | ||
42 | syscalls[common_comm][common_pid][id] += 1 | ||
43 | except TypeError: | ||
44 | syscalls[common_comm][common_pid][id] = 1 | ||
45 | |||
46 | def print_syscall_totals(): | ||
47 | if for_comm is not None: | ||
48 | print "\nsyscall events for %s:\n\n" % (for_comm), | ||
49 | else: | ||
50 | print "\nsyscall events by comm/pid:\n\n", | ||
51 | |||
52 | print "%-40s %10s\n" % ("comm [pid]/syscalls", "count"), | ||
53 | print "%-40s %10s\n" % ("----------------------------------------", \ | ||
54 | "----------"), | ||
55 | |||
56 | comm_keys = syscalls.keys() | ||
57 | for comm in comm_keys: | ||
58 | pid_keys = syscalls[comm].keys() | ||
59 | for pid in pid_keys: | ||
60 | print "\n%s [%d]\n" % (comm, pid), | ||
61 | id_keys = syscalls[comm][pid].keys() | ||
62 | for id, val in sorted(syscalls[comm][pid].iteritems(), \ | ||
63 | key = lambda(k, v): (v, k), reverse = True): | ||
64 | print " %-38d %10d\n" % (id, val), | ||
diff --git a/tools/perf/scripts/python/syscall-counts.py b/tools/perf/scripts/python/syscall-counts.py new file mode 100644 index 000000000000..f977e85ff049 --- /dev/null +++ b/tools/perf/scripts/python/syscall-counts.py | |||
@@ -0,0 +1,58 @@ | |||
1 | # system call counts | ||
2 | # (c) 2010, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | # | ||
5 | # Displays system-wide system call totals, broken down by syscall. | ||
6 | # If a [comm] arg is specified, only syscalls called by [comm] are displayed. | ||
7 | |||
8 | import os | ||
9 | import sys | ||
10 | |||
11 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
12 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
13 | |||
14 | from perf_trace_context import * | ||
15 | from Core import * | ||
16 | |||
17 | usage = "perf trace -s syscall-counts.py [comm]\n"; | ||
18 | |||
19 | for_comm = None | ||
20 | |||
21 | if len(sys.argv) > 2: | ||
22 | sys.exit(usage) | ||
23 | |||
24 | if len(sys.argv) > 1: | ||
25 | for_comm = sys.argv[1] | ||
26 | |||
27 | syscalls = autodict() | ||
28 | |||
29 | def trace_begin(): | ||
30 | pass | ||
31 | |||
32 | def trace_end(): | ||
33 | print_syscall_totals() | ||
34 | |||
35 | def raw_syscalls__sys_enter(event_name, context, common_cpu, | ||
36 | common_secs, common_nsecs, common_pid, common_comm, | ||
37 | id, args): | ||
38 | if for_comm is not None: | ||
39 | if common_comm != for_comm: | ||
40 | return | ||
41 | try: | ||
42 | syscalls[id] += 1 | ||
43 | except TypeError: | ||
44 | syscalls[id] = 1 | ||
45 | |||
46 | def print_syscall_totals(): | ||
47 | if for_comm is not None: | ||
48 | print "\nsyscall events for %s:\n\n" % (for_comm), | ||
49 | else: | ||
50 | print "\nsyscall events:\n\n", | ||
51 | |||
52 | print "%-40s %10s\n" % ("event", "count"), | ||
53 | print "%-40s %10s\n" % ("----------------------------------------", \ | ||
54 | "-----------"), | ||
55 | |||
56 | for id, val in sorted(syscalls.iteritems(), key = lambda(k, v): (v, k), \ | ||
57 | reverse = True): | ||
58 | print "%-40d %10d\n" % (id, val), | ||
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c new file mode 100644 index 000000000000..04904b35ba81 --- /dev/null +++ b/tools/perf/util/build-id.c | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * build-id.c | ||
3 | * | ||
4 | * build-id support | ||
5 | * | ||
6 | * Copyright (C) 2009, 2010 Red Hat Inc. | ||
7 | * Copyright (C) 2009, 2010 Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | */ | ||
9 | #include "build-id.h" | ||
10 | #include "event.h" | ||
11 | #include "symbol.h" | ||
12 | #include <linux/kernel.h> | ||
13 | |||
14 | static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | ||
15 | { | ||
16 | struct addr_location al; | ||
17 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
18 | struct thread *thread = perf_session__findnew(session, event->ip.pid); | ||
19 | |||
20 | if (thread == NULL) { | ||
21 | pr_err("problem processing %d event, skipping it.\n", | ||
22 | event->header.type); | ||
23 | return -1; | ||
24 | } | ||
25 | |||
26 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | ||
27 | event->ip.ip, &al); | ||
28 | |||
29 | if (al.map != NULL) | ||
30 | al.map->dso->hit = 1; | ||
31 | |||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | struct perf_event_ops build_id__mark_dso_hit_ops = { | ||
36 | .sample = build_id__mark_dso_hit, | ||
37 | .mmap = event__process_mmap, | ||
38 | .fork = event__process_task, | ||
39 | }; | ||
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h new file mode 100644 index 000000000000..1d981d63cf9a --- /dev/null +++ b/tools/perf/util/build-id.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef PERF_BUILD_ID_H_ | ||
2 | #define PERF_BUILD_ID_H_ 1 | ||
3 | |||
4 | #include "session.h" | ||
5 | |||
6 | extern struct perf_event_ops build_id__mark_dso_hit_ops; | ||
7 | |||
8 | #endif | ||
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 6f8ea9d210b6..918eb376abe3 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -1,10 +1,15 @@ | |||
1 | #ifndef CACHE_H | 1 | #ifndef __PERF_CACHE_H |
2 | #define CACHE_H | 2 | #define __PERF_CACHE_H |
3 | 3 | ||
4 | #include "util.h" | 4 | #include "util.h" |
5 | #include "strbuf.h" | 5 | #include "strbuf.h" |
6 | #include "../perf.h" | 6 | #include "../perf.h" |
7 | 7 | ||
8 | #define CMD_EXEC_PATH "--exec-path" | ||
9 | #define CMD_PERF_DIR "--perf-dir=" | ||
10 | #define CMD_WORK_TREE "--work-tree=" | ||
11 | #define CMD_DEBUGFS_DIR "--debugfs-dir=" | ||
12 | |||
8 | #define PERF_DIR_ENVIRONMENT "PERF_DIR" | 13 | #define PERF_DIR_ENVIRONMENT "PERF_DIR" |
9 | #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" | 14 | #define PERF_WORK_TREE_ENVIRONMENT "PERF_WORK_TREE" |
10 | #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" | 15 | #define DEFAULT_PERF_DIR_ENVIRONMENT ".perf" |
@@ -117,4 +122,4 @@ extern char *perf_pathdup(const char *fmt, ...) | |||
117 | 122 | ||
118 | extern size_t strlcpy(char *dest, const char *src, size_t size); | 123 | extern size_t strlcpy(char *dest, const char *src, size_t size); |
119 | 124 | ||
120 | #endif /* CACHE_H */ | 125 | #endif /* __PERF_CACHE_H */ |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 3b8380f1b478..b3b71258272a 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -206,7 +206,7 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, | |||
206 | } | 206 | } |
207 | node->val_nr = chain->nr - start; | 207 | node->val_nr = chain->nr - start; |
208 | if (!node->val_nr) | 208 | if (!node->val_nr) |
209 | printf("Warning: empty node in callchain tree\n"); | 209 | pr_warning("Warning: empty node in callchain tree\n"); |
210 | } | 210 | } |
211 | 211 | ||
212 | static void | 212 | static void |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 43cf3ea9e088..ad4626de4c2b 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -58,4 +58,4 @@ static inline u64 cumul_hits(struct callchain_node *node) | |||
58 | int register_callchain_param(struct callchain_param *param); | 58 | int register_callchain_param(struct callchain_param *param); |
59 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 59 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, |
60 | struct symbol **syms); | 60 | struct symbol **syms); |
61 | #endif | 61 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 58d597564b99..24e8809210bb 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef COLOR_H | 1 | #ifndef __PERF_COLOR_H |
2 | #define COLOR_H | 2 | #define __PERF_COLOR_H |
3 | 3 | ||
4 | /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ | 4 | /* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */ |
5 | #define COLOR_MAXLEN 24 | 5 | #define COLOR_MAXLEN 24 |
@@ -39,4 +39,4 @@ int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *bu | |||
39 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); | 39 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); |
40 | const char *get_percent_color(double percent); | 40 | const char *get_percent_color(double percent); |
41 | 41 | ||
42 | #endif /* COLOR_H */ | 42 | #endif /* __PERF_COLOR_H */ |
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c index 0b791bd346bc..35073621e5de 100644 --- a/tools/perf/util/ctype.c +++ b/tools/perf/util/ctype.c | |||
@@ -29,3 +29,11 @@ unsigned char sane_ctype[256] = { | |||
29 | A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */ | 29 | A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0, /* 112..127 */ |
30 | /* Nothing in the 128.. range */ | 30 | /* Nothing in the 128.. range */ |
31 | }; | 31 | }; |
32 | |||
33 | const char *graph_line = | ||
34 | "_____________________________________________________________________" | ||
35 | "_____________________________________________________________________"; | ||
36 | const char *graph_dotted_line = | ||
37 | "---------------------------------------------------------------------" | ||
38 | "---------------------------------------------------------------------" | ||
39 | "---------------------------------------------------------------------"; | ||
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index e8ca98fe0bd4..0905600c3851 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -9,16 +9,17 @@ | |||
9 | #include "color.h" | 9 | #include "color.h" |
10 | #include "event.h" | 10 | #include "event.h" |
11 | #include "debug.h" | 11 | #include "debug.h" |
12 | #include "util.h" | ||
12 | 13 | ||
13 | int verbose = 0; | 14 | int verbose = 0; |
14 | int dump_trace = 0; | 15 | int dump_trace = 0; |
15 | 16 | ||
16 | int eprintf(const char *fmt, ...) | 17 | int eprintf(int level, const char *fmt, ...) |
17 | { | 18 | { |
18 | va_list args; | 19 | va_list args; |
19 | int ret = 0; | 20 | int ret = 0; |
20 | 21 | ||
21 | if (verbose) { | 22 | if (verbose >= level) { |
22 | va_start(args, fmt); | 23 | va_start(args, fmt); |
23 | ret = vfprintf(stderr, fmt, args); | 24 | ret = vfprintf(stderr, fmt, args); |
24 | va_end(args); | 25 | va_end(args); |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 437eea58ce40..c6c24c522dea 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -1,8 +1,15 @@ | |||
1 | /* For debugging general purposes */ | 1 | /* For debugging general purposes */ |
2 | #ifndef __PERF_DEBUG_H | ||
3 | #define __PERF_DEBUG_H | ||
4 | |||
5 | #include "event.h" | ||
2 | 6 | ||
3 | extern int verbose; | 7 | extern int verbose; |
4 | extern int dump_trace; | 8 | extern int dump_trace; |
5 | 9 | ||
6 | int eprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 10 | int eprintf(int level, |
11 | const char *fmt, ...) __attribute__((format(printf, 2, 3))); | ||
7 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 12 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
8 | void trace_event(event_t *event); | 13 | void trace_event(event_t *event); |
14 | |||
15 | #endif /* __PERF_DEBUG_H */ | ||
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c new file mode 100644 index 000000000000..a88fefc0cc0a --- /dev/null +++ b/tools/perf/util/debugfs.c | |||
@@ -0,0 +1,240 @@ | |||
1 | #include "util.h" | ||
2 | #include "debugfs.h" | ||
3 | #include "cache.h" | ||
4 | |||
5 | static int debugfs_premounted; | ||
6 | static char debugfs_mountpoint[MAX_PATH+1]; | ||
7 | |||
8 | static const char *debugfs_known_mountpoints[] = { | ||
9 | "/sys/kernel/debug/", | ||
10 | "/debug/", | ||
11 | 0, | ||
12 | }; | ||
13 | |||
14 | /* use this to force a umount */ | ||
15 | void debugfs_force_cleanup(void) | ||
16 | { | ||
17 | debugfs_find_mountpoint(); | ||
18 | debugfs_premounted = 0; | ||
19 | debugfs_umount(); | ||
20 | } | ||
21 | |||
22 | /* construct a full path to a debugfs element */ | ||
23 | int debugfs_make_path(const char *element, char *buffer, int size) | ||
24 | { | ||
25 | int len; | ||
26 | |||
27 | if (strlen(debugfs_mountpoint) == 0) { | ||
28 | buffer[0] = '\0'; | ||
29 | return -1; | ||
30 | } | ||
31 | |||
32 | len = strlen(debugfs_mountpoint) + strlen(element) + 1; | ||
33 | if (len >= size) | ||
34 | return len+1; | ||
35 | |||
36 | snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element); | ||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static int debugfs_found; | ||
41 | |||
42 | /* find the path to the mounted debugfs */ | ||
43 | const char *debugfs_find_mountpoint(void) | ||
44 | { | ||
45 | const char **ptr; | ||
46 | char type[100]; | ||
47 | FILE *fp; | ||
48 | |||
49 | if (debugfs_found) | ||
50 | return (const char *) debugfs_mountpoint; | ||
51 | |||
52 | ptr = debugfs_known_mountpoints; | ||
53 | while (*ptr) { | ||
54 | if (debugfs_valid_mountpoint(*ptr) == 0) { | ||
55 | debugfs_found = 1; | ||
56 | strcpy(debugfs_mountpoint, *ptr); | ||
57 | return debugfs_mountpoint; | ||
58 | } | ||
59 | ptr++; | ||
60 | } | ||
61 | |||
62 | /* give up and parse /proc/mounts */ | ||
63 | fp = fopen("/proc/mounts", "r"); | ||
64 | if (fp == NULL) | ||
65 | die("Can't open /proc/mounts for read"); | ||
66 | |||
67 | while (fscanf(fp, "%*s %" | ||
68 | STR(MAX_PATH) | ||
69 | "s %99s %*s %*d %*d\n", | ||
70 | debugfs_mountpoint, type) == 2) { | ||
71 | if (strcmp(type, "debugfs") == 0) | ||
72 | break; | ||
73 | } | ||
74 | fclose(fp); | ||
75 | |||
76 | if (strcmp(type, "debugfs") != 0) | ||
77 | return NULL; | ||
78 | |||
79 | debugfs_found = 1; | ||
80 | |||
81 | return debugfs_mountpoint; | ||
82 | } | ||
83 | |||
84 | /* verify that a mountpoint is actually a debugfs instance */ | ||
85 | |||
86 | int debugfs_valid_mountpoint(const char *debugfs) | ||
87 | { | ||
88 | struct statfs st_fs; | ||
89 | |||
90 | if (statfs(debugfs, &st_fs) < 0) | ||
91 | return -ENOENT; | ||
92 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
93 | return -ENOENT; | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | |||
99 | int debugfs_valid_entry(const char *path) | ||
100 | { | ||
101 | struct stat st; | ||
102 | |||
103 | if (stat(path, &st)) | ||
104 | return -errno; | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | /* mount the debugfs somewhere if it's not mounted */ | ||
110 | |||
111 | char *debugfs_mount(const char *mountpoint) | ||
112 | { | ||
113 | /* see if it's already mounted */ | ||
114 | if (debugfs_find_mountpoint()) { | ||
115 | debugfs_premounted = 1; | ||
116 | return debugfs_mountpoint; | ||
117 | } | ||
118 | |||
119 | /* if not mounted and no argument */ | ||
120 | if (mountpoint == NULL) { | ||
121 | /* see if environment variable set */ | ||
122 | mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT); | ||
123 | /* if no environment variable, use default */ | ||
124 | if (mountpoint == NULL) | ||
125 | mountpoint = "/sys/kernel/debug"; | ||
126 | } | ||
127 | |||
128 | if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) | ||
129 | return NULL; | ||
130 | |||
131 | /* save the mountpoint */ | ||
132 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); | ||
133 | debugfs_found = 1; | ||
134 | |||
135 | return debugfs_mountpoint; | ||
136 | } | ||
137 | |||
138 | /* umount the debugfs */ | ||
139 | |||
140 | int debugfs_umount(void) | ||
141 | { | ||
142 | char umountcmd[128]; | ||
143 | int ret; | ||
144 | |||
145 | /* if it was already mounted, leave it */ | ||
146 | if (debugfs_premounted) | ||
147 | return 0; | ||
148 | |||
149 | /* make sure it's a valid mount point */ | ||
150 | ret = debugfs_valid_mountpoint(debugfs_mountpoint); | ||
151 | if (ret) | ||
152 | return ret; | ||
153 | |||
154 | snprintf(umountcmd, sizeof(umountcmd), | ||
155 | "/bin/umount %s", debugfs_mountpoint); | ||
156 | return system(umountcmd); | ||
157 | } | ||
158 | |||
159 | int debugfs_write(const char *entry, const char *value) | ||
160 | { | ||
161 | char path[MAX_PATH+1]; | ||
162 | int ret, count; | ||
163 | int fd; | ||
164 | |||
165 | /* construct the path */ | ||
166 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
167 | |||
168 | /* verify that it exists */ | ||
169 | ret = debugfs_valid_entry(path); | ||
170 | if (ret) | ||
171 | return ret; | ||
172 | |||
173 | /* get how many chars we're going to write */ | ||
174 | count = strlen(value); | ||
175 | |||
176 | /* open the debugfs entry */ | ||
177 | fd = open(path, O_RDWR); | ||
178 | if (fd < 0) | ||
179 | return -errno; | ||
180 | |||
181 | while (count > 0) { | ||
182 | /* write it */ | ||
183 | ret = write(fd, value, count); | ||
184 | if (ret <= 0) { | ||
185 | if (ret == EAGAIN) | ||
186 | continue; | ||
187 | close(fd); | ||
188 | return -errno; | ||
189 | } | ||
190 | count -= ret; | ||
191 | } | ||
192 | |||
193 | /* close it */ | ||
194 | close(fd); | ||
195 | |||
196 | /* return success */ | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * read a debugfs entry | ||
202 | * returns the number of chars read or a negative errno | ||
203 | */ | ||
204 | int debugfs_read(const char *entry, char *buffer, size_t size) | ||
205 | { | ||
206 | char path[MAX_PATH+1]; | ||
207 | int ret; | ||
208 | int fd; | ||
209 | |||
210 | /* construct the path */ | ||
211 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
212 | |||
213 | /* verify that it exists */ | ||
214 | ret = debugfs_valid_entry(path); | ||
215 | if (ret) | ||
216 | return ret; | ||
217 | |||
218 | /* open the debugfs entry */ | ||
219 | fd = open(path, O_RDONLY); | ||
220 | if (fd < 0) | ||
221 | return -errno; | ||
222 | |||
223 | do { | ||
224 | /* read it */ | ||
225 | ret = read(fd, buffer, size); | ||
226 | if (ret == 0) { | ||
227 | close(fd); | ||
228 | return EOF; | ||
229 | } | ||
230 | } while (ret < 0 && errno == EAGAIN); | ||
231 | |||
232 | /* close it */ | ||
233 | close(fd); | ||
234 | |||
235 | /* make *sure* there's a null character at the end */ | ||
236 | buffer[ret] = '\0'; | ||
237 | |||
238 | /* return the number of chars read */ | ||
239 | return ret; | ||
240 | } | ||
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h new file mode 100644 index 000000000000..83a02879745f --- /dev/null +++ b/tools/perf/util/debugfs.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef __DEBUGFS_H__ | ||
2 | #define __DEBUGFS_H__ | ||
3 | |||
4 | #include <sys/mount.h> | ||
5 | |||
6 | #ifndef MAX_PATH | ||
7 | # define MAX_PATH 256 | ||
8 | #endif | ||
9 | |||
10 | #ifndef STR | ||
11 | # define _STR(x) #x | ||
12 | # define STR(x) _STR(x) | ||
13 | #endif | ||
14 | |||
15 | extern const char *debugfs_find_mountpoint(void); | ||
16 | extern int debugfs_valid_mountpoint(const char *debugfs); | ||
17 | extern int debugfs_valid_entry(const char *path); | ||
18 | extern char *debugfs_mount(const char *mountpoint); | ||
19 | extern int debugfs_umount(void); | ||
20 | extern int debugfs_write(const char *entry, const char *value); | ||
21 | extern int debugfs_read(const char *entry, char *buffer, size_t size); | ||
22 | extern void debugfs_force_cleanup(void); | ||
23 | extern int debugfs_make_path(const char *element, char *buffer, int size); | ||
24 | |||
25 | #endif /* __DEBUGFS_H__ */ | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c new file mode 100644 index 000000000000..705ec63548b4 --- /dev/null +++ b/tools/perf/util/event.c | |||
@@ -0,0 +1,612 @@ | |||
1 | #include <linux/types.h> | ||
2 | #include "event.h" | ||
3 | #include "debug.h" | ||
4 | #include "session.h" | ||
5 | #include "sort.h" | ||
6 | #include "string.h" | ||
7 | #include "strlist.h" | ||
8 | #include "thread.h" | ||
9 | |||
10 | static pid_t event__synthesize_comm(pid_t pid, int full, | ||
11 | event__handler_t process, | ||
12 | struct perf_session *session) | ||
13 | { | ||
14 | event_t ev; | ||
15 | char filename[PATH_MAX]; | ||
16 | char bf[BUFSIZ]; | ||
17 | FILE *fp; | ||
18 | size_t size = 0; | ||
19 | DIR *tasks; | ||
20 | struct dirent dirent, *next; | ||
21 | pid_t tgid = 0; | ||
22 | |||
23 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); | ||
24 | |||
25 | fp = fopen(filename, "r"); | ||
26 | if (fp == NULL) { | ||
27 | out_race: | ||
28 | /* | ||
29 | * We raced with a task exiting - just return: | ||
30 | */ | ||
31 | pr_debug("couldn't open %s\n", filename); | ||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | memset(&ev.comm, 0, sizeof(ev.comm)); | ||
36 | while (!ev.comm.comm[0] || !ev.comm.pid) { | ||
37 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
38 | goto out_failure; | ||
39 | |||
40 | if (memcmp(bf, "Name:", 5) == 0) { | ||
41 | char *name = bf + 5; | ||
42 | while (*name && isspace(*name)) | ||
43 | ++name; | ||
44 | size = strlen(name) - 1; | ||
45 | memcpy(ev.comm.comm, name, size++); | ||
46 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
47 | char *tgids = bf + 5; | ||
48 | while (*tgids && isspace(*tgids)) | ||
49 | ++tgids; | ||
50 | tgid = ev.comm.pid = atoi(tgids); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | ev.comm.header.type = PERF_RECORD_COMM; | ||
55 | size = ALIGN(size, sizeof(u64)); | ||
56 | ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); | ||
57 | |||
58 | if (!full) { | ||
59 | ev.comm.tid = pid; | ||
60 | |||
61 | process(&ev, session); | ||
62 | goto out_fclose; | ||
63 | } | ||
64 | |||
65 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | ||
66 | |||
67 | tasks = opendir(filename); | ||
68 | if (tasks == NULL) | ||
69 | goto out_race; | ||
70 | |||
71 | while (!readdir_r(tasks, &dirent, &next) && next) { | ||
72 | char *end; | ||
73 | pid = strtol(dirent.d_name, &end, 10); | ||
74 | if (*end) | ||
75 | continue; | ||
76 | |||
77 | ev.comm.tid = pid; | ||
78 | |||
79 | process(&ev, session); | ||
80 | } | ||
81 | closedir(tasks); | ||
82 | |||
83 | out_fclose: | ||
84 | fclose(fp); | ||
85 | return tgid; | ||
86 | |||
87 | out_failure: | ||
88 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
89 | return -1; | ||
90 | } | ||
91 | |||
92 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | ||
93 | event__handler_t process, | ||
94 | struct perf_session *session) | ||
95 | { | ||
96 | char filename[PATH_MAX]; | ||
97 | FILE *fp; | ||
98 | |||
99 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); | ||
100 | |||
101 | fp = fopen(filename, "r"); | ||
102 | if (fp == NULL) { | ||
103 | /* | ||
104 | * We raced with a task exiting - just return: | ||
105 | */ | ||
106 | pr_debug("couldn't open %s\n", filename); | ||
107 | return -1; | ||
108 | } | ||
109 | |||
110 | while (1) { | ||
111 | char bf[BUFSIZ], *pbf = bf; | ||
112 | event_t ev = { | ||
113 | .header = { | ||
114 | .type = PERF_RECORD_MMAP, | ||
115 | .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ | ||
116 | }, | ||
117 | }; | ||
118 | int n; | ||
119 | size_t size; | ||
120 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
121 | break; | ||
122 | |||
123 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | ||
124 | n = hex2u64(pbf, &ev.mmap.start); | ||
125 | if (n < 0) | ||
126 | continue; | ||
127 | pbf += n + 1; | ||
128 | n = hex2u64(pbf, &ev.mmap.len); | ||
129 | if (n < 0) | ||
130 | continue; | ||
131 | pbf += n + 3; | ||
132 | if (*pbf == 'x') { /* vm_exec */ | ||
133 | char *execname = strchr(bf, '/'); | ||
134 | |||
135 | /* Catch VDSO */ | ||
136 | if (execname == NULL) | ||
137 | execname = strstr(bf, "[vdso]"); | ||
138 | |||
139 | if (execname == NULL) | ||
140 | continue; | ||
141 | |||
142 | size = strlen(execname); | ||
143 | execname[size - 1] = '\0'; /* Remove \n */ | ||
144 | memcpy(ev.mmap.filename, execname, size); | ||
145 | size = ALIGN(size, sizeof(u64)); | ||
146 | ev.mmap.len -= ev.mmap.start; | ||
147 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
148 | (sizeof(ev.mmap.filename) - size)); | ||
149 | ev.mmap.pid = tgid; | ||
150 | ev.mmap.tid = pid; | ||
151 | |||
152 | process(&ev, session); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | fclose(fp); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | int event__synthesize_modules(event__handler_t process, | ||
161 | struct perf_session *session) | ||
162 | { | ||
163 | struct rb_node *nd; | ||
164 | |||
165 | for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); | ||
166 | nd; nd = rb_next(nd)) { | ||
167 | event_t ev; | ||
168 | size_t size; | ||
169 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
170 | |||
171 | if (pos->dso->kernel) | ||
172 | continue; | ||
173 | |||
174 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); | ||
175 | memset(&ev, 0, sizeof(ev)); | ||
176 | ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
177 | ev.mmap.header.type = PERF_RECORD_MMAP; | ||
178 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
179 | (sizeof(ev.mmap.filename) - size)); | ||
180 | ev.mmap.start = pos->start; | ||
181 | ev.mmap.len = pos->end - pos->start; | ||
182 | |||
183 | memcpy(ev.mmap.filename, pos->dso->long_name, | ||
184 | pos->dso->long_name_len + 1); | ||
185 | process(&ev, session); | ||
186 | } | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | int event__synthesize_thread(pid_t pid, event__handler_t process, | ||
192 | struct perf_session *session) | ||
193 | { | ||
194 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); | ||
195 | if (tgid == -1) | ||
196 | return -1; | ||
197 | return event__synthesize_mmap_events(pid, tgid, process, session); | ||
198 | } | ||
199 | |||
200 | void event__synthesize_threads(event__handler_t process, | ||
201 | struct perf_session *session) | ||
202 | { | ||
203 | DIR *proc; | ||
204 | struct dirent dirent, *next; | ||
205 | |||
206 | proc = opendir("/proc"); | ||
207 | |||
208 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
209 | char *end; | ||
210 | pid_t pid = strtol(dirent.d_name, &end, 10); | ||
211 | |||
212 | if (*end) /* only interested in proper numerical dirents */ | ||
213 | continue; | ||
214 | |||
215 | event__synthesize_thread(pid, process, session); | ||
216 | } | ||
217 | |||
218 | closedir(proc); | ||
219 | } | ||
220 | |||
221 | struct process_symbol_args { | ||
222 | const char *name; | ||
223 | u64 start; | ||
224 | }; | ||
225 | |||
226 | static int find_symbol_cb(void *arg, const char *name, char type, u64 start) | ||
227 | { | ||
228 | struct process_symbol_args *args = arg; | ||
229 | |||
230 | /* | ||
231 | * Must be a function or at least an alias, as in PARISC64, where "_text" is | ||
232 | * an 'A' to the same address as "_stext". | ||
233 | */ | ||
234 | if (!(symbol_type__is_a(type, MAP__FUNCTION) || | ||
235 | type == 'A') || strcmp(name, args->name)) | ||
236 | return 0; | ||
237 | |||
238 | args->start = start; | ||
239 | return 1; | ||
240 | } | ||
241 | |||
242 | int event__synthesize_kernel_mmap(event__handler_t process, | ||
243 | struct perf_session *session, | ||
244 | const char *symbol_name) | ||
245 | { | ||
246 | size_t size; | ||
247 | event_t ev = { | ||
248 | .header = { | ||
249 | .type = PERF_RECORD_MMAP, | ||
250 | .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
251 | }, | ||
252 | }; | ||
253 | /* | ||
254 | * We should get this from /sys/kernel/sections/.text, but till that is | ||
255 | * available use this, and after it is use this as a fallback for older | ||
256 | * kernels. | ||
257 | */ | ||
258 | struct process_symbol_args args = { .name = symbol_name, }; | ||
259 | |||
260 | if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) | ||
261 | return -ENOENT; | ||
262 | |||
263 | size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | ||
264 | "[kernel.kallsyms.%s]", symbol_name) + 1; | ||
265 | size = ALIGN(size, sizeof(u64)); | ||
266 | ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); | ||
267 | ev.mmap.pgoff = args.start; | ||
268 | ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; | ||
269 | ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; | ||
270 | |||
271 | return process(&ev, session); | ||
272 | } | ||
273 | |||
274 | static void thread__comm_adjust(struct thread *self) | ||
275 | { | ||
276 | char *comm = self->comm; | ||
277 | |||
278 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
279 | (!symbol_conf.comm_list || | ||
280 | strlist__has_entry(symbol_conf.comm_list, comm))) { | ||
281 | unsigned int slen = strlen(comm); | ||
282 | |||
283 | if (slen > comms__col_width) { | ||
284 | comms__col_width = slen; | ||
285 | threads__col_width = slen + 6; | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | |||
290 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | ||
291 | { | ||
292 | int ret = thread__set_comm(self, comm); | ||
293 | |||
294 | if (ret) | ||
295 | return ret; | ||
296 | |||
297 | thread__comm_adjust(self); | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | int event__process_comm(event_t *self, struct perf_session *session) | ||
303 | { | ||
304 | struct thread *thread = perf_session__findnew(session, self->comm.pid); | ||
305 | |||
306 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); | ||
307 | |||
308 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { | ||
309 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
310 | return -1; | ||
311 | } | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | int event__process_lost(event_t *self, struct perf_session *session) | ||
317 | { | ||
318 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); | ||
319 | session->events_stats.lost += self->lost.lost; | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | int event__process_mmap(event_t *self, struct perf_session *session) | ||
324 | { | ||
325 | struct thread *thread; | ||
326 | struct map *map; | ||
327 | |||
328 | dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", | ||
329 | self->mmap.pid, self->mmap.tid, self->mmap.start, | ||
330 | self->mmap.len, self->mmap.pgoff, self->mmap.filename); | ||
331 | |||
332 | if (self->mmap.pid == 0) { | ||
333 | static const char kmmap_prefix[] = "[kernel.kallsyms."; | ||
334 | |||
335 | if (self->mmap.filename[0] == '/') { | ||
336 | char short_module_name[1024]; | ||
337 | char *name = strrchr(self->mmap.filename, '/'), *dot; | ||
338 | |||
339 | if (name == NULL) | ||
340 | goto out_problem; | ||
341 | |||
342 | ++name; /* skip / */ | ||
343 | dot = strrchr(name, '.'); | ||
344 | if (dot == NULL) | ||
345 | goto out_problem; | ||
346 | |||
347 | snprintf(short_module_name, sizeof(short_module_name), | ||
348 | "[%.*s]", (int)(dot - name), name); | ||
349 | strxfrchar(short_module_name, '-', '_'); | ||
350 | |||
351 | map = perf_session__new_module_map(session, | ||
352 | self->mmap.start, | ||
353 | self->mmap.filename); | ||
354 | if (map == NULL) | ||
355 | goto out_problem; | ||
356 | |||
357 | name = strdup(short_module_name); | ||
358 | if (name == NULL) | ||
359 | goto out_problem; | ||
360 | |||
361 | map->dso->short_name = name; | ||
362 | map->end = map->start + self->mmap.len; | ||
363 | } else if (memcmp(self->mmap.filename, kmmap_prefix, | ||
364 | sizeof(kmmap_prefix) - 1) == 0) { | ||
365 | const char *symbol_name = (self->mmap.filename + | ||
366 | sizeof(kmmap_prefix) - 1); | ||
367 | /* | ||
368 | * Should be there already, from the build-id table in | ||
369 | * the header. | ||
370 | */ | ||
371 | struct dso *kernel = __dsos__findnew(&dsos__kernel, | ||
372 | "[kernel.kallsyms]"); | ||
373 | if (kernel == NULL) | ||
374 | goto out_problem; | ||
375 | |||
376 | kernel->kernel = 1; | ||
377 | if (__perf_session__create_kernel_maps(session, kernel) < 0) | ||
378 | goto out_problem; | ||
379 | |||
380 | session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; | ||
381 | session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; | ||
382 | /* | ||
383 | * Be a bit paranoid here, some perf.data file came with | ||
384 | * a zero sized synthesized MMAP event for the kernel. | ||
385 | */ | ||
386 | if (session->vmlinux_maps[MAP__FUNCTION]->end == 0) | ||
387 | session->vmlinux_maps[MAP__FUNCTION]->end = ~0UL; | ||
388 | |||
389 | perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, | ||
390 | self->mmap.pgoff); | ||
391 | } | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | thread = perf_session__findnew(session, self->mmap.pid); | ||
396 | map = map__new(&self->mmap, MAP__FUNCTION, | ||
397 | session->cwd, session->cwdlen); | ||
398 | |||
399 | if (thread == NULL || map == NULL) | ||
400 | goto out_problem; | ||
401 | |||
402 | thread__insert_map(thread, map); | ||
403 | return 0; | ||
404 | |||
405 | out_problem: | ||
406 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | int event__process_task(event_t *self, struct perf_session *session) | ||
411 | { | ||
412 | struct thread *thread = perf_session__findnew(session, self->fork.pid); | ||
413 | struct thread *parent = perf_session__findnew(session, self->fork.ppid); | ||
414 | |||
415 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | ||
416 | self->fork.ppid, self->fork.ptid); | ||
417 | /* | ||
418 | * A thread clone will have the same PID for both parent and child. | ||
419 | */ | ||
420 | if (thread == parent) | ||
421 | return 0; | ||
422 | |||
423 | if (self->header.type == PERF_RECORD_EXIT) | ||
424 | return 0; | ||
425 | |||
426 | if (thread == NULL || parent == NULL || | ||
427 | thread__fork(thread, parent) < 0) { | ||
428 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
429 | return -1; | ||
430 | } | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | void thread__find_addr_map(struct thread *self, | ||
436 | struct perf_session *session, u8 cpumode, | ||
437 | enum map_type type, u64 addr, | ||
438 | struct addr_location *al) | ||
439 | { | ||
440 | struct map_groups *mg = &self->mg; | ||
441 | |||
442 | al->thread = self; | ||
443 | al->addr = addr; | ||
444 | |||
445 | if (cpumode == PERF_RECORD_MISC_KERNEL) { | ||
446 | al->level = 'k'; | ||
447 | mg = &session->kmaps; | ||
448 | } else if (cpumode == PERF_RECORD_MISC_USER) | ||
449 | al->level = '.'; | ||
450 | else { | ||
451 | al->level = 'H'; | ||
452 | al->map = NULL; | ||
453 | return; | ||
454 | } | ||
455 | try_again: | ||
456 | al->map = map_groups__find(mg, type, al->addr); | ||
457 | if (al->map == NULL) { | ||
458 | /* | ||
459 | * If this is outside of all known maps, and is a negative | ||
460 | * address, try to look it up in the kernel dso, as it might be | ||
461 | * a vsyscall or vdso (which executes in user-mode). | ||
462 | * | ||
463 | * XXX This is nasty, we should have a symbol list in the | ||
464 | * "[vdso]" dso, but for now lets use the old trick of looking | ||
465 | * in the whole kernel symbol list. | ||
466 | */ | ||
467 | if ((long long)al->addr < 0 && mg != &session->kmaps) { | ||
468 | mg = &session->kmaps; | ||
469 | goto try_again; | ||
470 | } | ||
471 | } else | ||
472 | al->addr = al->map->map_ip(al->map, al->addr); | ||
473 | } | ||
474 | |||
475 | void thread__find_addr_location(struct thread *self, | ||
476 | struct perf_session *session, u8 cpumode, | ||
477 | enum map_type type, u64 addr, | ||
478 | struct addr_location *al, | ||
479 | symbol_filter_t filter) | ||
480 | { | ||
481 | thread__find_addr_map(self, session, cpumode, type, addr, al); | ||
482 | if (al->map != NULL) | ||
483 | al->sym = map__find_symbol(al->map, al->addr, filter); | ||
484 | else | ||
485 | al->sym = NULL; | ||
486 | } | ||
487 | |||
488 | static void dso__calc_col_width(struct dso *self) | ||
489 | { | ||
490 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
491 | (!symbol_conf.dso_list || | ||
492 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | ||
493 | unsigned int slen = strlen(self->name); | ||
494 | if (slen > dsos__col_width) | ||
495 | dsos__col_width = slen; | ||
496 | } | ||
497 | |||
498 | self->slen_calculated = 1; | ||
499 | } | ||
500 | |||
501 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | ||
502 | struct addr_location *al, symbol_filter_t filter) | ||
503 | { | ||
504 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
505 | struct thread *thread = perf_session__findnew(session, self->ip.pid); | ||
506 | |||
507 | if (thread == NULL) | ||
508 | return -1; | ||
509 | |||
510 | if (symbol_conf.comm_list && | ||
511 | !strlist__has_entry(symbol_conf.comm_list, thread->comm)) | ||
512 | goto out_filtered; | ||
513 | |||
514 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | ||
515 | |||
516 | thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, | ||
517 | self->ip.ip, al, filter); | ||
518 | dump_printf(" ...... dso: %s\n", | ||
519 | al->map ? al->map->dso->long_name : | ||
520 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | ||
521 | /* | ||
522 | * We have to do this here as we may have a dso with no symbol hit that | ||
523 | * has a name longer than the ones with symbols sampled. | ||
524 | */ | ||
525 | if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) | ||
526 | dso__calc_col_width(al->map->dso); | ||
527 | |||
528 | if (symbol_conf.dso_list && | ||
529 | (!al->map || !al->map->dso || | ||
530 | !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || | ||
531 | (al->map->dso->short_name != al->map->dso->long_name && | ||
532 | strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) | ||
533 | goto out_filtered; | ||
534 | |||
535 | if (symbol_conf.sym_list && al->sym && | ||
536 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) | ||
537 | goto out_filtered; | ||
538 | |||
539 | al->filtered = false; | ||
540 | return 0; | ||
541 | |||
542 | out_filtered: | ||
543 | al->filtered = true; | ||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | ||
548 | { | ||
549 | u64 *array = event->sample.array; | ||
550 | |||
551 | if (type & PERF_SAMPLE_IP) { | ||
552 | data->ip = event->ip.ip; | ||
553 | array++; | ||
554 | } | ||
555 | |||
556 | if (type & PERF_SAMPLE_TID) { | ||
557 | u32 *p = (u32 *)array; | ||
558 | data->pid = p[0]; | ||
559 | data->tid = p[1]; | ||
560 | array++; | ||
561 | } | ||
562 | |||
563 | if (type & PERF_SAMPLE_TIME) { | ||
564 | data->time = *array; | ||
565 | array++; | ||
566 | } | ||
567 | |||
568 | if (type & PERF_SAMPLE_ADDR) { | ||
569 | data->addr = *array; | ||
570 | array++; | ||
571 | } | ||
572 | |||
573 | if (type & PERF_SAMPLE_ID) { | ||
574 | data->id = *array; | ||
575 | array++; | ||
576 | } | ||
577 | |||
578 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
579 | data->stream_id = *array; | ||
580 | array++; | ||
581 | } | ||
582 | |||
583 | if (type & PERF_SAMPLE_CPU) { | ||
584 | u32 *p = (u32 *)array; | ||
585 | data->cpu = *p; | ||
586 | array++; | ||
587 | } | ||
588 | |||
589 | if (type & PERF_SAMPLE_PERIOD) { | ||
590 | data->period = *array; | ||
591 | array++; | ||
592 | } | ||
593 | |||
594 | if (type & PERF_SAMPLE_READ) { | ||
595 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
596 | return -1; | ||
597 | } | ||
598 | |||
599 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
600 | data->callchain = (struct ip_callchain *)array; | ||
601 | array += 1 + data->callchain->nr; | ||
602 | } | ||
603 | |||
604 | if (type & PERF_SAMPLE_RAW) { | ||
605 | u32 *p = (u32 *)array; | ||
606 | data->raw_size = *p; | ||
607 | p++; | ||
608 | data->raw_data = p; | ||
609 | } | ||
610 | |||
611 | return 0; | ||
612 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 2c9c26d6ded0..50a7132887f5 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -1,14 +1,10 @@ | |||
1 | #ifndef __PERF_RECORD_H | 1 | #ifndef __PERF_RECORD_H |
2 | #define __PERF_RECORD_H | 2 | #define __PERF_RECORD_H |
3 | #include "../perf.h" | ||
4 | #include "util.h" | ||
5 | #include <linux/list.h> | ||
6 | 3 | ||
7 | enum { | 4 | #include <limits.h> |
8 | SHOW_KERNEL = 1, | 5 | |
9 | SHOW_USER = 2, | 6 | #include "../perf.h" |
10 | SHOW_HV = 4, | 7 | #include "map.h" |
11 | }; | ||
12 | 8 | ||
13 | /* | 9 | /* |
14 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | 10 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * |
@@ -60,11 +56,32 @@ struct read_event { | |||
60 | u64 id; | 56 | u64 id; |
61 | }; | 57 | }; |
62 | 58 | ||
63 | struct sample_event{ | 59 | struct sample_event { |
64 | struct perf_event_header header; | 60 | struct perf_event_header header; |
65 | u64 array[]; | 61 | u64 array[]; |
66 | }; | 62 | }; |
67 | 63 | ||
64 | struct sample_data { | ||
65 | u64 ip; | ||
66 | u32 pid, tid; | ||
67 | u64 time; | ||
68 | u64 addr; | ||
69 | u64 id; | ||
70 | u64 stream_id; | ||
71 | u32 cpu; | ||
72 | u64 period; | ||
73 | struct ip_callchain *callchain; | ||
74 | u32 raw_size; | ||
75 | void *raw_data; | ||
76 | }; | ||
77 | |||
78 | #define BUILD_ID_SIZE 20 | ||
79 | |||
80 | struct build_id_event { | ||
81 | struct perf_event_header header; | ||
82 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
83 | char filename[]; | ||
84 | }; | ||
68 | 85 | ||
69 | typedef union event_union { | 86 | typedef union event_union { |
70 | struct perf_event_header header; | 87 | struct perf_event_header header; |
@@ -77,28 +94,35 @@ typedef union event_union { | |||
77 | struct sample_event sample; | 94 | struct sample_event sample; |
78 | } event_t; | 95 | } event_t; |
79 | 96 | ||
80 | struct map { | 97 | struct events_stats { |
81 | struct list_head node; | 98 | u64 total; |
82 | u64 start; | 99 | u64 lost; |
83 | u64 end; | ||
84 | u64 pgoff; | ||
85 | u64 (*map_ip)(struct map *, u64); | ||
86 | struct dso *dso; | ||
87 | }; | 100 | }; |
88 | 101 | ||
89 | static inline u64 map__map_ip(struct map *map, u64 ip) | 102 | void event__print_totals(void); |
90 | { | 103 | |
91 | return ip - map->start + map->pgoff; | 104 | struct perf_session; |
92 | } | 105 | |
106 | typedef int (*event__handler_t)(event_t *event, struct perf_session *session); | ||
107 | |||
108 | int event__synthesize_thread(pid_t pid, event__handler_t process, | ||
109 | struct perf_session *session); | ||
110 | void event__synthesize_threads(event__handler_t process, | ||
111 | struct perf_session *session); | ||
112 | int event__synthesize_kernel_mmap(event__handler_t process, | ||
113 | struct perf_session *session, | ||
114 | const char *symbol_name); | ||
115 | int event__synthesize_modules(event__handler_t process, | ||
116 | struct perf_session *session); | ||
93 | 117 | ||
94 | static inline u64 vdso__map_ip(struct map *map __used, u64 ip) | 118 | int event__process_comm(event_t *self, struct perf_session *session); |
95 | { | 119 | int event__process_lost(event_t *self, struct perf_session *session); |
96 | return ip; | 120 | int event__process_mmap(event_t *self, struct perf_session *session); |
97 | } | 121 | int event__process_task(event_t *self, struct perf_session *session); |
98 | 122 | ||
99 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen); | 123 | struct addr_location; |
100 | struct map *map__clone(struct map *self); | 124 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
101 | int map__overlap(struct map *l, struct map *r); | 125 | struct addr_location *al, symbol_filter_t filter); |
102 | size_t map__fprintf(struct map *self, FILE *fp); | 126 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); |
103 | 127 | ||
104 | #endif | 128 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/exec_cmd.h b/tools/perf/util/exec_cmd.h index effe25eb1545..31647ac92ed1 100644 --- a/tools/perf/util/exec_cmd.h +++ b/tools/perf/util/exec_cmd.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef PERF_EXEC_CMD_H | 1 | #ifndef __PERF_EXEC_CMD_H |
2 | #define PERF_EXEC_CMD_H | 2 | #define __PERF_EXEC_CMD_H |
3 | 3 | ||
4 | extern void perf_set_argv_exec_path(const char *exec_path); | 4 | extern void perf_set_argv_exec_path(const char *exec_path); |
5 | extern const char *perf_extract_argv0_path(const char *path); | 5 | extern const char *perf_extract_argv0_path(const char *path); |
@@ -10,4 +10,4 @@ extern int execv_perf_cmd(const char **argv); /* NULL terminated */ | |||
10 | extern int execl_perf_cmd(const char *cmd, ...); | 10 | extern int execl_perf_cmd(const char *cmd, ...); |
11 | extern const char *system_path(const char *path); | 11 | extern const char *system_path(const char *path); |
12 | 12 | ||
13 | #endif /* PERF_EXEC_CMD_H */ | 13 | #endif /* __PERF_EXEC_CMD_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index e306857b2c2b..6c9aa16ee51f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1,10 +1,20 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
1 | #include <sys/types.h> | 3 | #include <sys/types.h> |
4 | #include <byteswap.h> | ||
2 | #include <unistd.h> | 5 | #include <unistd.h> |
3 | #include <stdio.h> | 6 | #include <stdio.h> |
4 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <linux/list.h> | ||
9 | #include <linux/kernel.h> | ||
5 | 10 | ||
6 | #include "util.h" | 11 | #include "util.h" |
7 | #include "header.h" | 12 | #include "header.h" |
13 | #include "../perf.h" | ||
14 | #include "trace-event.h" | ||
15 | #include "session.h" | ||
16 | #include "symbol.h" | ||
17 | #include "debug.h" | ||
8 | 18 | ||
9 | /* | 19 | /* |
10 | * Create new perf.data header attribute: | 20 | * Create new perf.data header attribute: |
@@ -13,75 +23,80 @@ struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) | |||
13 | { | 23 | { |
14 | struct perf_header_attr *self = malloc(sizeof(*self)); | 24 | struct perf_header_attr *self = malloc(sizeof(*self)); |
15 | 25 | ||
16 | if (!self) | 26 | if (self != NULL) { |
17 | die("nomem"); | 27 | self->attr = *attr; |
18 | 28 | self->ids = 0; | |
19 | self->attr = *attr; | 29 | self->size = 1; |
20 | self->ids = 0; | 30 | self->id = malloc(sizeof(u64)); |
21 | self->size = 1; | 31 | if (self->id == NULL) { |
22 | self->id = malloc(sizeof(u64)); | 32 | free(self); |
23 | 33 | self = NULL; | |
24 | if (!self->id) | 34 | } |
25 | die("nomem"); | 35 | } |
26 | 36 | ||
27 | return self; | 37 | return self; |
28 | } | 38 | } |
29 | 39 | ||
30 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | 40 | void perf_header_attr__delete(struct perf_header_attr *self) |
41 | { | ||
42 | free(self->id); | ||
43 | free(self); | ||
44 | } | ||
45 | |||
46 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | ||
31 | { | 47 | { |
32 | int pos = self->ids; | 48 | int pos = self->ids; |
33 | 49 | ||
34 | self->ids++; | 50 | self->ids++; |
35 | if (self->ids > self->size) { | 51 | if (self->ids > self->size) { |
36 | self->size *= 2; | 52 | int nsize = self->size * 2; |
37 | self->id = realloc(self->id, self->size * sizeof(u64)); | 53 | u64 *nid = realloc(self->id, nsize * sizeof(u64)); |
38 | if (!self->id) | 54 | |
39 | die("nomem"); | 55 | if (nid == NULL) |
56 | return -1; | ||
57 | |||
58 | self->size = nsize; | ||
59 | self->id = nid; | ||
40 | } | 60 | } |
41 | self->id[pos] = id; | 61 | self->id[pos] = id; |
62 | return 0; | ||
42 | } | 63 | } |
43 | 64 | ||
44 | /* | 65 | int perf_header__init(struct perf_header *self) |
45 | * Create new perf.data header: | ||
46 | */ | ||
47 | struct perf_header *perf_header__new(void) | ||
48 | { | 66 | { |
49 | struct perf_header *self = malloc(sizeof(*self)); | ||
50 | |||
51 | if (!self) | ||
52 | die("nomem"); | ||
53 | |||
54 | self->frozen = 0; | ||
55 | |||
56 | self->attrs = 0; | ||
57 | self->size = 1; | 67 | self->size = 1; |
58 | self->attr = malloc(sizeof(void *)); | 68 | self->attr = malloc(sizeof(void *)); |
59 | 69 | return self->attr == NULL ? -ENOMEM : 0; | |
60 | if (!self->attr) | ||
61 | die("nomem"); | ||
62 | |||
63 | self->data_offset = 0; | ||
64 | self->data_size = 0; | ||
65 | |||
66 | return self; | ||
67 | } | 70 | } |
68 | 71 | ||
69 | void perf_header__add_attr(struct perf_header *self, | 72 | void perf_header__exit(struct perf_header *self) |
70 | struct perf_header_attr *attr) | ||
71 | { | 73 | { |
72 | int pos = self->attrs; | 74 | int i; |
75 | for (i = 0; i < self->attrs; ++i) | ||
76 | perf_header_attr__delete(self->attr[i]); | ||
77 | free(self->attr); | ||
78 | } | ||
73 | 79 | ||
80 | int perf_header__add_attr(struct perf_header *self, | ||
81 | struct perf_header_attr *attr) | ||
82 | { | ||
74 | if (self->frozen) | 83 | if (self->frozen) |
75 | die("frozen"); | 84 | return -1; |
76 | 85 | ||
77 | self->attrs++; | 86 | if (self->attrs == self->size) { |
78 | if (self->attrs > self->size) { | 87 | int nsize = self->size * 2; |
79 | self->size *= 2; | 88 | struct perf_header_attr **nattr; |
80 | self->attr = realloc(self->attr, self->size * sizeof(void *)); | 89 | |
81 | if (!self->attr) | 90 | nattr = realloc(self->attr, nsize * sizeof(void *)); |
82 | die("nomem"); | 91 | if (nattr == NULL) |
92 | return -1; | ||
93 | |||
94 | self->size = nsize; | ||
95 | self->attr = nattr; | ||
83 | } | 96 | } |
84 | self->attr[pos] = attr; | 97 | |
98 | self->attr[self->attrs++] = attr; | ||
99 | return 0; | ||
85 | } | 100 | } |
86 | 101 | ||
87 | #define MAX_EVENT_NAME 64 | 102 | #define MAX_EVENT_NAME 64 |
@@ -94,24 +109,28 @@ struct perf_trace_event_type { | |||
94 | static int event_count; | 109 | static int event_count; |
95 | static struct perf_trace_event_type *events; | 110 | static struct perf_trace_event_type *events; |
96 | 111 | ||
97 | void perf_header__push_event(u64 id, const char *name) | 112 | int perf_header__push_event(u64 id, const char *name) |
98 | { | 113 | { |
99 | if (strlen(name) > MAX_EVENT_NAME) | 114 | if (strlen(name) > MAX_EVENT_NAME) |
100 | printf("Event %s will be truncated\n", name); | 115 | pr_warning("Event %s will be truncated\n", name); |
101 | 116 | ||
102 | if (!events) { | 117 | if (!events) { |
103 | events = malloc(sizeof(struct perf_trace_event_type)); | 118 | events = malloc(sizeof(struct perf_trace_event_type)); |
104 | if (!events) | 119 | if (events == NULL) |
105 | die("nomem"); | 120 | return -ENOMEM; |
106 | } else { | 121 | } else { |
107 | events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type)); | 122 | struct perf_trace_event_type *nevents; |
108 | if (!events) | 123 | |
109 | die("nomem"); | 124 | nevents = realloc(events, (event_count + 1) * sizeof(*events)); |
125 | if (nevents == NULL) | ||
126 | return -ENOMEM; | ||
127 | events = nevents; | ||
110 | } | 128 | } |
111 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); | 129 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); |
112 | events[event_count].event_id = id; | 130 | events[event_count].event_id = id; |
113 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); | 131 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); |
114 | event_count++; | 132 | event_count++; |
133 | return 0; | ||
115 | } | 134 | } |
116 | 135 | ||
117 | char *perf_header__find_event(u64 id) | 136 | char *perf_header__find_event(u64 id) |
@@ -128,44 +147,292 @@ static const char *__perf_magic = "PERFFILE"; | |||
128 | 147 | ||
129 | #define PERF_MAGIC (*(u64 *)__perf_magic) | 148 | #define PERF_MAGIC (*(u64 *)__perf_magic) |
130 | 149 | ||
131 | struct perf_file_section { | ||
132 | u64 offset; | ||
133 | u64 size; | ||
134 | }; | ||
135 | |||
136 | struct perf_file_attr { | 150 | struct perf_file_attr { |
137 | struct perf_event_attr attr; | 151 | struct perf_event_attr attr; |
138 | struct perf_file_section ids; | 152 | struct perf_file_section ids; |
139 | }; | 153 | }; |
140 | 154 | ||
141 | struct perf_file_header { | 155 | void perf_header__set_feat(struct perf_header *self, int feat) |
142 | u64 magic; | 156 | { |
143 | u64 size; | 157 | set_bit(feat, self->adds_features); |
144 | u64 attr_size; | 158 | } |
145 | struct perf_file_section attrs; | ||
146 | struct perf_file_section data; | ||
147 | struct perf_file_section event_types; | ||
148 | }; | ||
149 | 159 | ||
150 | static void do_write(int fd, void *buf, size_t size) | 160 | bool perf_header__has_feat(const struct perf_header *self, int feat) |
161 | { | ||
162 | return test_bit(feat, self->adds_features); | ||
163 | } | ||
164 | |||
165 | static int do_write(int fd, const void *buf, size_t size) | ||
151 | { | 166 | { |
152 | while (size) { | 167 | while (size) { |
153 | int ret = write(fd, buf, size); | 168 | int ret = write(fd, buf, size); |
154 | 169 | ||
155 | if (ret < 0) | 170 | if (ret < 0) |
156 | die("failed to write"); | 171 | return -errno; |
157 | 172 | ||
158 | size -= ret; | 173 | size -= ret; |
159 | buf += ret; | 174 | buf += ret; |
160 | } | 175 | } |
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | #define NAME_ALIGN 64 | ||
181 | |||
182 | static int write_padded(int fd, const void *bf, size_t count, | ||
183 | size_t count_aligned) | ||
184 | { | ||
185 | static const char zero_buf[NAME_ALIGN]; | ||
186 | int err = do_write(fd, bf, count); | ||
187 | |||
188 | if (!err) | ||
189 | err = do_write(fd, zero_buf, count_aligned - count); | ||
190 | |||
191 | return err; | ||
192 | } | ||
193 | |||
194 | #define dsos__for_each_with_build_id(pos, head) \ | ||
195 | list_for_each_entry(pos, head, node) \ | ||
196 | if (!pos->has_build_id) \ | ||
197 | continue; \ | ||
198 | else | ||
199 | |||
200 | static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) | ||
201 | { | ||
202 | struct dso *pos; | ||
203 | |||
204 | dsos__for_each_with_build_id(pos, head) { | ||
205 | int err; | ||
206 | struct build_id_event b; | ||
207 | size_t len; | ||
208 | |||
209 | if (!pos->hit) | ||
210 | continue; | ||
211 | len = pos->long_name_len + 1; | ||
212 | len = ALIGN(len, NAME_ALIGN); | ||
213 | memset(&b, 0, sizeof(b)); | ||
214 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | ||
215 | b.header.misc = misc; | ||
216 | b.header.size = sizeof(b) + len; | ||
217 | err = do_write(fd, &b, sizeof(b)); | ||
218 | if (err < 0) | ||
219 | return err; | ||
220 | err = write_padded(fd, pos->long_name, | ||
221 | pos->long_name_len + 1, len); | ||
222 | if (err < 0) | ||
223 | return err; | ||
224 | } | ||
225 | |||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int dsos__write_buildid_table(int fd) | ||
230 | { | ||
231 | int err = __dsos__write_buildid_table(&dsos__kernel, | ||
232 | PERF_RECORD_MISC_KERNEL, fd); | ||
233 | if (err == 0) | ||
234 | err = __dsos__write_buildid_table(&dsos__user, | ||
235 | PERF_RECORD_MISC_USER, fd); | ||
236 | return err; | ||
237 | } | ||
238 | |||
239 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
240 | const char *name, bool is_kallsyms) | ||
241 | { | ||
242 | const size_t size = PATH_MAX; | ||
243 | char *filename = malloc(size), | ||
244 | *linkname = malloc(size), *targetname; | ||
245 | int len, err = -1; | ||
246 | |||
247 | if (filename == NULL || linkname == NULL) | ||
248 | goto out_free; | ||
249 | |||
250 | len = snprintf(filename, size, "%s%s%s", | ||
251 | debugdir, is_kallsyms ? "/" : "", name); | ||
252 | if (mkdir_p(filename, 0755)) | ||
253 | goto out_free; | ||
254 | |||
255 | snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); | ||
256 | |||
257 | if (access(filename, F_OK)) { | ||
258 | if (is_kallsyms) { | ||
259 | if (copyfile("/proc/kallsyms", filename)) | ||
260 | goto out_free; | ||
261 | } else if (link(name, filename) && copyfile(name, filename)) | ||
262 | goto out_free; | ||
263 | } | ||
264 | |||
265 | len = snprintf(linkname, size, "%s/.build-id/%.2s", | ||
266 | debugdir, sbuild_id); | ||
267 | |||
268 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
269 | goto out_free; | ||
270 | |||
271 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
272 | targetname = filename + strlen(debugdir) - 5; | ||
273 | memcpy(targetname, "../..", 5); | ||
274 | |||
275 | if (symlink(targetname, linkname) == 0) | ||
276 | err = 0; | ||
277 | out_free: | ||
278 | free(filename); | ||
279 | free(linkname); | ||
280 | return err; | ||
281 | } | ||
282 | |||
283 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
284 | const char *name, const char *debugdir, | ||
285 | bool is_kallsyms) | ||
286 | { | ||
287 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
288 | |||
289 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
290 | |||
291 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); | ||
292 | } | ||
293 | |||
294 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
295 | { | ||
296 | const size_t size = PATH_MAX; | ||
297 | char *filename = malloc(size), | ||
298 | *linkname = malloc(size); | ||
299 | int err = -1; | ||
300 | |||
301 | if (filename == NULL || linkname == NULL) | ||
302 | goto out_free; | ||
303 | |||
304 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
305 | debugdir, sbuild_id, sbuild_id + 2); | ||
306 | |||
307 | if (access(linkname, F_OK)) | ||
308 | goto out_free; | ||
309 | |||
310 | if (readlink(linkname, filename, size) < 0) | ||
311 | goto out_free; | ||
312 | |||
313 | if (unlink(linkname)) | ||
314 | goto out_free; | ||
315 | |||
316 | /* | ||
317 | * Since the link is relative, we must make it absolute: | ||
318 | */ | ||
319 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
320 | debugdir, sbuild_id, filename); | ||
321 | |||
322 | if (unlink(linkname)) | ||
323 | goto out_free; | ||
324 | |||
325 | err = 0; | ||
326 | out_free: | ||
327 | free(filename); | ||
328 | free(linkname); | ||
329 | return err; | ||
330 | } | ||
331 | |||
332 | static int dso__cache_build_id(struct dso *self, const char *debugdir) | ||
333 | { | ||
334 | bool is_kallsyms = self->kernel && self->long_name[0] != '/'; | ||
335 | |||
336 | return build_id_cache__add_b(self->build_id, sizeof(self->build_id), | ||
337 | self->long_name, debugdir, is_kallsyms); | ||
338 | } | ||
339 | |||
340 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | ||
341 | { | ||
342 | struct dso *pos; | ||
343 | int err = 0; | ||
344 | |||
345 | dsos__for_each_with_build_id(pos, head) | ||
346 | if (dso__cache_build_id(pos, debugdir)) | ||
347 | err = -1; | ||
348 | |||
349 | return err; | ||
350 | } | ||
351 | |||
352 | static int dsos__cache_build_ids(void) | ||
353 | { | ||
354 | int err_kernel, err_user; | ||
355 | char debugdir[PATH_MAX]; | ||
356 | |||
357 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | ||
358 | DEBUG_CACHE_DIR); | ||
359 | |||
360 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
361 | return -1; | ||
362 | |||
363 | err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); | ||
364 | err_user = __dsos__cache_build_ids(&dsos__user, debugdir); | ||
365 | return err_kernel || err_user ? -1 : 0; | ||
366 | } | ||
367 | |||
368 | static int perf_header__adds_write(struct perf_header *self, int fd) | ||
369 | { | ||
370 | int nr_sections; | ||
371 | struct perf_file_section *feat_sec; | ||
372 | int sec_size; | ||
373 | u64 sec_start; | ||
374 | int idx = 0, err; | ||
375 | |||
376 | if (dsos__read_build_ids(true)) | ||
377 | perf_header__set_feat(self, HEADER_BUILD_ID); | ||
378 | |||
379 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
380 | if (!nr_sections) | ||
381 | return 0; | ||
382 | |||
383 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
384 | if (feat_sec == NULL) | ||
385 | return -ENOMEM; | ||
386 | |||
387 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
388 | |||
389 | sec_start = self->data_offset + self->data_size; | ||
390 | lseek(fd, sec_start + sec_size, SEEK_SET); | ||
391 | |||
392 | if (perf_header__has_feat(self, HEADER_TRACE_INFO)) { | ||
393 | struct perf_file_section *trace_sec; | ||
394 | |||
395 | trace_sec = &feat_sec[idx++]; | ||
396 | |||
397 | /* Write trace info */ | ||
398 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
399 | read_tracing_data(fd, attrs, nr_counters); | ||
400 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | ||
401 | } | ||
402 | |||
403 | |||
404 | if (perf_header__has_feat(self, HEADER_BUILD_ID)) { | ||
405 | struct perf_file_section *buildid_sec; | ||
406 | |||
407 | buildid_sec = &feat_sec[idx++]; | ||
408 | |||
409 | /* Write build-ids */ | ||
410 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
411 | err = dsos__write_buildid_table(fd); | ||
412 | if (err < 0) { | ||
413 | pr_debug("failed to write buildid table\n"); | ||
414 | goto out_free; | ||
415 | } | ||
416 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | ||
417 | buildid_sec->offset; | ||
418 | dsos__cache_build_ids(); | ||
419 | } | ||
420 | |||
421 | lseek(fd, sec_start, SEEK_SET); | ||
422 | err = do_write(fd, feat_sec, sec_size); | ||
423 | if (err < 0) | ||
424 | pr_debug("failed to write feature section\n"); | ||
425 | out_free: | ||
426 | free(feat_sec); | ||
427 | return err; | ||
161 | } | 428 | } |
162 | 429 | ||
163 | void perf_header__write(struct perf_header *self, int fd) | 430 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) |
164 | { | 431 | { |
165 | struct perf_file_header f_header; | 432 | struct perf_file_header f_header; |
166 | struct perf_file_attr f_attr; | 433 | struct perf_file_attr f_attr; |
167 | struct perf_header_attr *attr; | 434 | struct perf_header_attr *attr; |
168 | int i; | 435 | int i, err; |
169 | 436 | ||
170 | lseek(fd, sizeof(f_header), SEEK_SET); | 437 | lseek(fd, sizeof(f_header), SEEK_SET); |
171 | 438 | ||
@@ -174,7 +441,11 @@ void perf_header__write(struct perf_header *self, int fd) | |||
174 | attr = self->attr[i]; | 441 | attr = self->attr[i]; |
175 | 442 | ||
176 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 443 | attr->id_offset = lseek(fd, 0, SEEK_CUR); |
177 | do_write(fd, attr->id, attr->ids * sizeof(u64)); | 444 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); |
445 | if (err < 0) { | ||
446 | pr_debug("failed to write perf header\n"); | ||
447 | return err; | ||
448 | } | ||
178 | } | 449 | } |
179 | 450 | ||
180 | 451 | ||
@@ -190,17 +461,31 @@ void perf_header__write(struct perf_header *self, int fd) | |||
190 | .size = attr->ids * sizeof(u64), | 461 | .size = attr->ids * sizeof(u64), |
191 | } | 462 | } |
192 | }; | 463 | }; |
193 | do_write(fd, &f_attr, sizeof(f_attr)); | 464 | err = do_write(fd, &f_attr, sizeof(f_attr)); |
465 | if (err < 0) { | ||
466 | pr_debug("failed to write perf header attribute\n"); | ||
467 | return err; | ||
468 | } | ||
194 | } | 469 | } |
195 | 470 | ||
196 | self->event_offset = lseek(fd, 0, SEEK_CUR); | 471 | self->event_offset = lseek(fd, 0, SEEK_CUR); |
197 | self->event_size = event_count * sizeof(struct perf_trace_event_type); | 472 | self->event_size = event_count * sizeof(struct perf_trace_event_type); |
198 | if (events) | 473 | if (events) { |
199 | do_write(fd, events, self->event_size); | 474 | err = do_write(fd, events, self->event_size); |
200 | 475 | if (err < 0) { | |
476 | pr_debug("failed to write perf header events\n"); | ||
477 | return err; | ||
478 | } | ||
479 | } | ||
201 | 480 | ||
202 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 481 | self->data_offset = lseek(fd, 0, SEEK_CUR); |
203 | 482 | ||
483 | if (at_exit) { | ||
484 | err = perf_header__adds_write(self, fd); | ||
485 | if (err < 0) | ||
486 | return err; | ||
487 | } | ||
488 | |||
204 | f_header = (struct perf_file_header){ | 489 | f_header = (struct perf_file_header){ |
205 | .magic = PERF_MAGIC, | 490 | .magic = PERF_MAGIC, |
206 | .size = sizeof(f_header), | 491 | .size = sizeof(f_header), |
@@ -219,44 +504,175 @@ void perf_header__write(struct perf_header *self, int fd) | |||
219 | }, | 504 | }, |
220 | }; | 505 | }; |
221 | 506 | ||
507 | memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); | ||
508 | |||
222 | lseek(fd, 0, SEEK_SET); | 509 | lseek(fd, 0, SEEK_SET); |
223 | do_write(fd, &f_header, sizeof(f_header)); | 510 | err = do_write(fd, &f_header, sizeof(f_header)); |
511 | if (err < 0) { | ||
512 | pr_debug("failed to write perf header\n"); | ||
513 | return err; | ||
514 | } | ||
224 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 515 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); |
225 | 516 | ||
226 | self->frozen = 1; | 517 | self->frozen = 1; |
518 | return 0; | ||
227 | } | 519 | } |
228 | 520 | ||
229 | static void do_read(int fd, void *buf, size_t size) | 521 | static int do_read(int fd, void *buf, size_t size) |
230 | { | 522 | { |
231 | while (size) { | 523 | while (size) { |
232 | int ret = read(fd, buf, size); | 524 | int ret = read(fd, buf, size); |
233 | 525 | ||
234 | if (ret < 0) | 526 | if (ret <= 0) |
235 | die("failed to read"); | 527 | return -1; |
236 | if (ret == 0) | ||
237 | die("failed to read: missing data"); | ||
238 | 528 | ||
239 | size -= ret; | 529 | size -= ret; |
240 | buf += ret; | 530 | buf += ret; |
241 | } | 531 | } |
532 | |||
533 | return 0; | ||
242 | } | 534 | } |
243 | 535 | ||
244 | struct perf_header *perf_header__read(int fd) | 536 | static int perf_header__getbuffer64(struct perf_header *self, |
537 | int fd, void *buf, size_t size) | ||
245 | { | 538 | { |
246 | struct perf_header *self = perf_header__new(); | 539 | if (do_read(fd, buf, size)) |
247 | struct perf_file_header f_header; | 540 | return -1; |
248 | struct perf_file_attr f_attr; | ||
249 | u64 f_id; | ||
250 | 541 | ||
251 | int nr_attrs, nr_ids, i, j; | 542 | if (self->needs_swap) |
543 | mem_bswap_64(buf, size); | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
549 | int (*process)(struct perf_file_section *self, | ||
550 | struct perf_header *ph, | ||
551 | int feat, int fd)) | ||
552 | { | ||
553 | struct perf_file_section *feat_sec; | ||
554 | int nr_sections; | ||
555 | int sec_size; | ||
556 | int idx = 0; | ||
557 | int err = -1, feat = 1; | ||
558 | |||
559 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
560 | if (!nr_sections) | ||
561 | return 0; | ||
562 | |||
563 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
564 | if (!feat_sec) | ||
565 | return -1; | ||
566 | |||
567 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
568 | |||
569 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | ||
570 | |||
571 | if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) | ||
572 | goto out_free; | ||
573 | |||
574 | err = 0; | ||
575 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | ||
576 | if (perf_header__has_feat(self, feat)) { | ||
577 | struct perf_file_section *sec = &feat_sec[idx++]; | ||
578 | |||
579 | err = process(sec, self, feat, fd); | ||
580 | if (err < 0) | ||
581 | break; | ||
582 | } | ||
583 | ++feat; | ||
584 | } | ||
585 | out_free: | ||
586 | free(feat_sec); | ||
587 | return err; | ||
588 | } | ||
252 | 589 | ||
590 | int perf_file_header__read(struct perf_file_header *self, | ||
591 | struct perf_header *ph, int fd) | ||
592 | { | ||
253 | lseek(fd, 0, SEEK_SET); | 593 | lseek(fd, 0, SEEK_SET); |
254 | do_read(fd, &f_header, sizeof(f_header)); | ||
255 | 594 | ||
256 | if (f_header.magic != PERF_MAGIC || | 595 | if (do_read(fd, self, sizeof(*self)) || |
257 | f_header.size != sizeof(f_header) || | 596 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) |
258 | f_header.attr_size != sizeof(f_attr)) | 597 | return -1; |
259 | die("incompatible file format"); | 598 | |
599 | if (self->attr_size != sizeof(struct perf_file_attr)) { | ||
600 | u64 attr_size = bswap_64(self->attr_size); | ||
601 | |||
602 | if (attr_size != sizeof(struct perf_file_attr)) | ||
603 | return -1; | ||
604 | |||
605 | mem_bswap_64(self, offsetof(struct perf_file_header, | ||
606 | adds_features)); | ||
607 | ph->needs_swap = true; | ||
608 | } | ||
609 | |||
610 | if (self->size != sizeof(*self)) { | ||
611 | /* Support the previous format */ | ||
612 | if (self->size == offsetof(typeof(*self), adds_features)) | ||
613 | bitmap_zero(self->adds_features, HEADER_FEAT_BITS); | ||
614 | else | ||
615 | return -1; | ||
616 | } | ||
617 | |||
618 | memcpy(&ph->adds_features, &self->adds_features, | ||
619 | sizeof(ph->adds_features)); | ||
620 | /* | ||
621 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
622 | * may be coming from an arch with a different word-size, ergo different | ||
623 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
624 | * safe to assume that we have a build-id section. Trace files probably | ||
625 | * have several other issues in this realm anyway... | ||
626 | */ | ||
627 | if (ph->needs_swap) { | ||
628 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
629 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
630 | } | ||
631 | |||
632 | ph->event_offset = self->event_types.offset; | ||
633 | ph->event_size = self->event_types.size; | ||
634 | ph->data_offset = self->data.offset; | ||
635 | ph->data_size = self->data.size; | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int perf_file_section__process(struct perf_file_section *self, | ||
640 | struct perf_header *ph, | ||
641 | int feat, int fd) | ||
642 | { | ||
643 | if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) { | ||
644 | pr_debug("Failed to lseek to %Ld offset for feature %d, " | ||
645 | "continuing...\n", self->offset, feat); | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | switch (feat) { | ||
650 | case HEADER_TRACE_INFO: | ||
651 | trace_report(fd); | ||
652 | break; | ||
653 | |||
654 | case HEADER_BUILD_ID: | ||
655 | if (perf_header__read_build_ids(ph, fd, self->offset, self->size)) | ||
656 | pr_debug("Failed to read buildids, continuing...\n"); | ||
657 | break; | ||
658 | default: | ||
659 | pr_debug("unknown feature %d, continuing...\n", feat); | ||
660 | } | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | int perf_header__read(struct perf_header *self, int fd) | ||
666 | { | ||
667 | struct perf_file_header f_header; | ||
668 | struct perf_file_attr f_attr; | ||
669 | u64 f_id; | ||
670 | int nr_attrs, nr_ids, i, j; | ||
671 | |||
672 | if (perf_file_header__read(&f_header, self, fd) < 0) { | ||
673 | pr_debug("incompatible file format\n"); | ||
674 | return -EINVAL; | ||
675 | } | ||
260 | 676 | ||
261 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | 677 | nr_attrs = f_header.attrs.size / sizeof(f_attr); |
262 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 678 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
@@ -265,42 +681,54 @@ struct perf_header *perf_header__read(int fd) | |||
265 | struct perf_header_attr *attr; | 681 | struct perf_header_attr *attr; |
266 | off_t tmp; | 682 | off_t tmp; |
267 | 683 | ||
268 | do_read(fd, &f_attr, sizeof(f_attr)); | 684 | if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) |
685 | goto out_errno; | ||
686 | |||
269 | tmp = lseek(fd, 0, SEEK_CUR); | 687 | tmp = lseek(fd, 0, SEEK_CUR); |
270 | 688 | ||
271 | attr = perf_header_attr__new(&f_attr.attr); | 689 | attr = perf_header_attr__new(&f_attr.attr); |
690 | if (attr == NULL) | ||
691 | return -ENOMEM; | ||
272 | 692 | ||
273 | nr_ids = f_attr.ids.size / sizeof(u64); | 693 | nr_ids = f_attr.ids.size / sizeof(u64); |
274 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 694 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
275 | 695 | ||
276 | for (j = 0; j < nr_ids; j++) { | 696 | for (j = 0; j < nr_ids; j++) { |
277 | do_read(fd, &f_id, sizeof(f_id)); | 697 | if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) |
698 | goto out_errno; | ||
278 | 699 | ||
279 | perf_header_attr__add_id(attr, f_id); | 700 | if (perf_header_attr__add_id(attr, f_id) < 0) { |
701 | perf_header_attr__delete(attr); | ||
702 | return -ENOMEM; | ||
703 | } | ||
704 | } | ||
705 | if (perf_header__add_attr(self, attr) < 0) { | ||
706 | perf_header_attr__delete(attr); | ||
707 | return -ENOMEM; | ||
280 | } | 708 | } |
281 | perf_header__add_attr(self, attr); | 709 | |
282 | lseek(fd, tmp, SEEK_SET); | 710 | lseek(fd, tmp, SEEK_SET); |
283 | } | 711 | } |
284 | 712 | ||
285 | if (f_header.event_types.size) { | 713 | if (f_header.event_types.size) { |
286 | lseek(fd, f_header.event_types.offset, SEEK_SET); | 714 | lseek(fd, f_header.event_types.offset, SEEK_SET); |
287 | events = malloc(f_header.event_types.size); | 715 | events = malloc(f_header.event_types.size); |
288 | if (!events) | 716 | if (events == NULL) |
289 | die("nomem"); | 717 | return -ENOMEM; |
290 | do_read(fd, events, f_header.event_types.size); | 718 | if (perf_header__getbuffer64(self, fd, events, |
719 | f_header.event_types.size)) | ||
720 | goto out_errno; | ||
291 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 721 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
292 | } | 722 | } |
293 | self->event_offset = f_header.event_types.offset; | ||
294 | self->event_size = f_header.event_types.size; | ||
295 | 723 | ||
296 | self->data_offset = f_header.data.offset; | 724 | perf_header__process_sections(self, fd, perf_file_section__process); |
297 | self->data_size = f_header.data.size; | ||
298 | 725 | ||
299 | lseek(fd, self->data_offset, SEEK_SET); | 726 | lseek(fd, self->data_offset, SEEK_SET); |
300 | 727 | ||
301 | self->frozen = 1; | 728 | self->frozen = 1; |
302 | 729 | return 0; | |
303 | return self; | 730 | out_errno: |
731 | return -errno; | ||
304 | } | 732 | } |
305 | 733 | ||
306 | u64 perf_header__sample_type(struct perf_header *header) | 734 | u64 perf_header__sample_type(struct perf_header *header) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index a0761bc7863c..82a6af72d4cc 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -1,9 +1,13 @@ | |||
1 | #ifndef _PERF_HEADER_H | 1 | #ifndef __PERF_HEADER_H |
2 | #define _PERF_HEADER_H | 2 | #define __PERF_HEADER_H |
3 | 3 | ||
4 | #include "../../../include/linux/perf_event.h" | 4 | #include "../../../include/linux/perf_event.h" |
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | #include <stdbool.h> | ||
6 | #include "types.h" | 7 | #include "types.h" |
8 | #include "event.h" | ||
9 | |||
10 | #include <linux/bitmap.h> | ||
7 | 11 | ||
8 | struct perf_header_attr { | 12 | struct perf_header_attr { |
9 | struct perf_event_attr attr; | 13 | struct perf_event_attr attr; |
@@ -12,36 +16,77 @@ struct perf_header_attr { | |||
12 | off_t id_offset; | 16 | off_t id_offset; |
13 | }; | 17 | }; |
14 | 18 | ||
19 | enum { | ||
20 | HEADER_TRACE_INFO = 1, | ||
21 | HEADER_BUILD_ID, | ||
22 | HEADER_LAST_FEATURE, | ||
23 | }; | ||
24 | |||
25 | #define HEADER_FEAT_BITS 256 | ||
26 | |||
27 | struct perf_file_section { | ||
28 | u64 offset; | ||
29 | u64 size; | ||
30 | }; | ||
31 | |||
32 | struct perf_file_header { | ||
33 | u64 magic; | ||
34 | u64 size; | ||
35 | u64 attr_size; | ||
36 | struct perf_file_section attrs; | ||
37 | struct perf_file_section data; | ||
38 | struct perf_file_section event_types; | ||
39 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | ||
40 | }; | ||
41 | |||
42 | struct perf_header; | ||
43 | |||
44 | int perf_file_header__read(struct perf_file_header *self, | ||
45 | struct perf_header *ph, int fd); | ||
46 | |||
15 | struct perf_header { | 47 | struct perf_header { |
16 | int frozen; | 48 | int frozen; |
17 | int attrs, size; | 49 | int attrs, size; |
18 | struct perf_header_attr **attr; | 50 | struct perf_header_attr **attr; |
19 | s64 attr_offset; | 51 | s64 attr_offset; |
20 | u64 data_offset; | 52 | u64 data_offset; |
21 | u64 data_size; | 53 | u64 data_size; |
22 | u64 event_offset; | 54 | u64 event_offset; |
23 | u64 event_size; | 55 | u64 event_size; |
56 | bool needs_swap; | ||
57 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | ||
24 | }; | 58 | }; |
25 | 59 | ||
26 | struct perf_header *perf_header__read(int fd); | 60 | int perf_header__init(struct perf_header *self); |
27 | void perf_header__write(struct perf_header *self, int fd); | 61 | void perf_header__exit(struct perf_header *self); |
62 | |||
63 | int perf_header__read(struct perf_header *self, int fd); | ||
64 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | ||
28 | 65 | ||
29 | void perf_header__add_attr(struct perf_header *self, | 66 | int perf_header__add_attr(struct perf_header *self, |
30 | struct perf_header_attr *attr); | 67 | struct perf_header_attr *attr); |
31 | 68 | ||
32 | void perf_header__push_event(u64 id, const char *name); | 69 | int perf_header__push_event(u64 id, const char *name); |
33 | char *perf_header__find_event(u64 id); | 70 | char *perf_header__find_event(u64 id); |
34 | 71 | ||
72 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); | ||
73 | void perf_header_attr__delete(struct perf_header_attr *self); | ||
35 | 74 | ||
36 | struct perf_header_attr * | 75 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); |
37 | perf_header_attr__new(struct perf_event_attr *attr); | ||
38 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | ||
39 | 76 | ||
40 | u64 perf_header__sample_type(struct perf_header *header); | 77 | u64 perf_header__sample_type(struct perf_header *header); |
41 | struct perf_event_attr * | 78 | struct perf_event_attr * |
42 | perf_header__find_attr(u64 id, struct perf_header *header); | 79 | perf_header__find_attr(u64 id, struct perf_header *header); |
80 | void perf_header__set_feat(struct perf_header *self, int feat); | ||
81 | bool perf_header__has_feat(const struct perf_header *self, int feat); | ||
43 | 82 | ||
83 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
84 | int (*process)(struct perf_file_section *self, | ||
85 | struct perf_header *ph, | ||
86 | int feat, int fd)); | ||
44 | 87 | ||
45 | struct perf_header *perf_header__new(void); | 88 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
89 | const char *name, bool is_kallsyms); | ||
90 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
46 | 91 | ||
47 | #endif /* _PERF_HEADER_H */ | 92 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/help.h b/tools/perf/util/help.h index 7128783637b4..7f5c6dedd714 100644 --- a/tools/perf/util/help.h +++ b/tools/perf/util/help.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef HELP_H | 1 | #ifndef __PERF_HELP_H |
2 | #define HELP_H | 2 | #define __PERF_HELP_H |
3 | 3 | ||
4 | struct cmdnames { | 4 | struct cmdnames { |
5 | size_t alloc; | 5 | size_t alloc; |
@@ -26,4 +26,4 @@ int is_in_cmdlist(struct cmdnames *c, const char *s); | |||
26 | void list_commands(const char *title, struct cmdnames *main_cmds, | 26 | void list_commands(const char *title, struct cmdnames *main_cmds, |
27 | struct cmdnames *other_cmds); | 27 | struct cmdnames *other_cmds); |
28 | 28 | ||
29 | #endif /* HELP_H */ | 29 | #endif /* __PERF_HELP_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c new file mode 100644 index 000000000000..44408c2621cf --- /dev/null +++ b/tools/perf/util/hist.c | |||
@@ -0,0 +1,662 @@ | |||
1 | #include "hist.h" | ||
2 | #include "session.h" | ||
3 | #include "sort.h" | ||
4 | #include <math.h> | ||
5 | |||
6 | struct callchain_param callchain_param = { | ||
7 | .mode = CHAIN_GRAPH_REL, | ||
8 | .min_percent = 0.5 | ||
9 | }; | ||
10 | |||
11 | /* | ||
12 | * histogram, sorted on item, collects counts | ||
13 | */ | ||
14 | |||
15 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, | ||
16 | struct addr_location *al, | ||
17 | struct symbol *sym_parent, | ||
18 | u64 count, bool *hit) | ||
19 | { | ||
20 | struct rb_node **p = &self->hists.rb_node; | ||
21 | struct rb_node *parent = NULL; | ||
22 | struct hist_entry *he; | ||
23 | struct hist_entry entry = { | ||
24 | .thread = al->thread, | ||
25 | .map = al->map, | ||
26 | .sym = al->sym, | ||
27 | .ip = al->addr, | ||
28 | .level = al->level, | ||
29 | .count = count, | ||
30 | .parent = sym_parent, | ||
31 | }; | ||
32 | int cmp; | ||
33 | |||
34 | while (*p != NULL) { | ||
35 | parent = *p; | ||
36 | he = rb_entry(parent, struct hist_entry, rb_node); | ||
37 | |||
38 | cmp = hist_entry__cmp(&entry, he); | ||
39 | |||
40 | if (!cmp) { | ||
41 | *hit = true; | ||
42 | return he; | ||
43 | } | ||
44 | |||
45 | if (cmp < 0) | ||
46 | p = &(*p)->rb_left; | ||
47 | else | ||
48 | p = &(*p)->rb_right; | ||
49 | } | ||
50 | |||
51 | he = malloc(sizeof(*he)); | ||
52 | if (!he) | ||
53 | return NULL; | ||
54 | *he = entry; | ||
55 | rb_link_node(&he->rb_node, parent, p); | ||
56 | rb_insert_color(&he->rb_node, &self->hists); | ||
57 | *hit = false; | ||
58 | return he; | ||
59 | } | ||
60 | |||
61 | int64_t | ||
62 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | ||
63 | { | ||
64 | struct sort_entry *se; | ||
65 | int64_t cmp = 0; | ||
66 | |||
67 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
68 | cmp = se->cmp(left, right); | ||
69 | if (cmp) | ||
70 | break; | ||
71 | } | ||
72 | |||
73 | return cmp; | ||
74 | } | ||
75 | |||
76 | int64_t | ||
77 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | ||
78 | { | ||
79 | struct sort_entry *se; | ||
80 | int64_t cmp = 0; | ||
81 | |||
82 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
83 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | ||
84 | |||
85 | f = se->collapse ?: se->cmp; | ||
86 | |||
87 | cmp = f(left, right); | ||
88 | if (cmp) | ||
89 | break; | ||
90 | } | ||
91 | |||
92 | return cmp; | ||
93 | } | ||
94 | |||
95 | void hist_entry__free(struct hist_entry *he) | ||
96 | { | ||
97 | free(he); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * collapse the histogram | ||
102 | */ | ||
103 | |||
104 | static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | ||
105 | { | ||
106 | struct rb_node **p = &root->rb_node; | ||
107 | struct rb_node *parent = NULL; | ||
108 | struct hist_entry *iter; | ||
109 | int64_t cmp; | ||
110 | |||
111 | while (*p != NULL) { | ||
112 | parent = *p; | ||
113 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
114 | |||
115 | cmp = hist_entry__collapse(iter, he); | ||
116 | |||
117 | if (!cmp) { | ||
118 | iter->count += he->count; | ||
119 | hist_entry__free(he); | ||
120 | return; | ||
121 | } | ||
122 | |||
123 | if (cmp < 0) | ||
124 | p = &(*p)->rb_left; | ||
125 | else | ||
126 | p = &(*p)->rb_right; | ||
127 | } | ||
128 | |||
129 | rb_link_node(&he->rb_node, parent, p); | ||
130 | rb_insert_color(&he->rb_node, root); | ||
131 | } | ||
132 | |||
133 | void perf_session__collapse_resort(struct perf_session *self) | ||
134 | { | ||
135 | struct rb_root tmp; | ||
136 | struct rb_node *next; | ||
137 | struct hist_entry *n; | ||
138 | |||
139 | if (!sort__need_collapse) | ||
140 | return; | ||
141 | |||
142 | tmp = RB_ROOT; | ||
143 | next = rb_first(&self->hists); | ||
144 | |||
145 | while (next) { | ||
146 | n = rb_entry(next, struct hist_entry, rb_node); | ||
147 | next = rb_next(&n->rb_node); | ||
148 | |||
149 | rb_erase(&n->rb_node, &self->hists); | ||
150 | collapse__insert_entry(&tmp, n); | ||
151 | } | ||
152 | |||
153 | self->hists = tmp; | ||
154 | } | ||
155 | |||
156 | /* | ||
157 | * reverse the map, sort on count. | ||
158 | */ | ||
159 | |||
160 | static void perf_session__insert_output_hist_entry(struct rb_root *root, | ||
161 | struct hist_entry *he, | ||
162 | u64 min_callchain_hits) | ||
163 | { | ||
164 | struct rb_node **p = &root->rb_node; | ||
165 | struct rb_node *parent = NULL; | ||
166 | struct hist_entry *iter; | ||
167 | |||
168 | if (symbol_conf.use_callchain) | ||
169 | callchain_param.sort(&he->sorted_chain, &he->callchain, | ||
170 | min_callchain_hits, &callchain_param); | ||
171 | |||
172 | while (*p != NULL) { | ||
173 | parent = *p; | ||
174 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
175 | |||
176 | if (he->count > iter->count) | ||
177 | p = &(*p)->rb_left; | ||
178 | else | ||
179 | p = &(*p)->rb_right; | ||
180 | } | ||
181 | |||
182 | rb_link_node(&he->rb_node, parent, p); | ||
183 | rb_insert_color(&he->rb_node, root); | ||
184 | } | ||
185 | |||
186 | void perf_session__output_resort(struct perf_session *self, u64 total_samples) | ||
187 | { | ||
188 | struct rb_root tmp; | ||
189 | struct rb_node *next; | ||
190 | struct hist_entry *n; | ||
191 | u64 min_callchain_hits; | ||
192 | |||
193 | min_callchain_hits = | ||
194 | total_samples * (callchain_param.min_percent / 100); | ||
195 | |||
196 | tmp = RB_ROOT; | ||
197 | next = rb_first(&self->hists); | ||
198 | |||
199 | while (next) { | ||
200 | n = rb_entry(next, struct hist_entry, rb_node); | ||
201 | next = rb_next(&n->rb_node); | ||
202 | |||
203 | rb_erase(&n->rb_node, &self->hists); | ||
204 | perf_session__insert_output_hist_entry(&tmp, n, | ||
205 | min_callchain_hits); | ||
206 | } | ||
207 | |||
208 | self->hists = tmp; | ||
209 | } | ||
210 | |||
211 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
212 | { | ||
213 | int i; | ||
214 | int ret = fprintf(fp, " "); | ||
215 | |||
216 | for (i = 0; i < left_margin; i++) | ||
217 | ret += fprintf(fp, " "); | ||
218 | |||
219 | return ret; | ||
220 | } | ||
221 | |||
222 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
223 | int left_margin) | ||
224 | { | ||
225 | int i; | ||
226 | size_t ret = callchain__fprintf_left_margin(fp, left_margin); | ||
227 | |||
228 | for (i = 0; i < depth; i++) | ||
229 | if (depth_mask & (1 << i)) | ||
230 | ret += fprintf(fp, "| "); | ||
231 | else | ||
232 | ret += fprintf(fp, " "); | ||
233 | |||
234 | ret += fprintf(fp, "\n"); | ||
235 | |||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | ||
240 | int depth, int depth_mask, int count, | ||
241 | u64 total_samples, int hits, | ||
242 | int left_margin) | ||
243 | { | ||
244 | int i; | ||
245 | size_t ret = 0; | ||
246 | |||
247 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
248 | for (i = 0; i < depth; i++) { | ||
249 | if (depth_mask & (1 << i)) | ||
250 | ret += fprintf(fp, "|"); | ||
251 | else | ||
252 | ret += fprintf(fp, " "); | ||
253 | if (!count && i == depth - 1) { | ||
254 | double percent; | ||
255 | |||
256 | percent = hits * 100.0 / total_samples; | ||
257 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
258 | } else | ||
259 | ret += fprintf(fp, "%s", " "); | ||
260 | } | ||
261 | if (chain->sym) | ||
262 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
263 | else | ||
264 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
265 | |||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | static struct symbol *rem_sq_bracket; | ||
270 | static struct callchain_list rem_hits; | ||
271 | |||
272 | static void init_rem_hits(void) | ||
273 | { | ||
274 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
275 | if (!rem_sq_bracket) { | ||
276 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
277 | return; | ||
278 | } | ||
279 | |||
280 | strcpy(rem_sq_bracket->name, "[...]"); | ||
281 | rem_hits.sym = rem_sq_bracket; | ||
282 | } | ||
283 | |||
284 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
285 | u64 total_samples, int depth, | ||
286 | int depth_mask, int left_margin) | ||
287 | { | ||
288 | struct rb_node *node, *next; | ||
289 | struct callchain_node *child; | ||
290 | struct callchain_list *chain; | ||
291 | int new_depth_mask = depth_mask; | ||
292 | u64 new_total; | ||
293 | u64 remaining; | ||
294 | size_t ret = 0; | ||
295 | int i; | ||
296 | |||
297 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
298 | new_total = self->children_hit; | ||
299 | else | ||
300 | new_total = total_samples; | ||
301 | |||
302 | remaining = new_total; | ||
303 | |||
304 | node = rb_first(&self->rb_root); | ||
305 | while (node) { | ||
306 | u64 cumul; | ||
307 | |||
308 | child = rb_entry(node, struct callchain_node, rb_node); | ||
309 | cumul = cumul_hits(child); | ||
310 | remaining -= cumul; | ||
311 | |||
312 | /* | ||
313 | * The depth mask manages the output of pipes that show | ||
314 | * the depth. We don't want to keep the pipes of the current | ||
315 | * level for the last child of this depth. | ||
316 | * Except if we have remaining filtered hits. They will | ||
317 | * supersede the last child | ||
318 | */ | ||
319 | next = rb_next(node); | ||
320 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
321 | new_depth_mask &= ~(1 << (depth - 1)); | ||
322 | |||
323 | /* | ||
324 | * But we keep the older depth mask for the line separator | ||
325 | * to keep the level link until we reach the last child | ||
326 | */ | ||
327 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
328 | left_margin); | ||
329 | i = 0; | ||
330 | list_for_each_entry(chain, &child->val, list) { | ||
331 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
332 | continue; | ||
333 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
334 | new_depth_mask, i++, | ||
335 | new_total, | ||
336 | cumul, | ||
337 | left_margin); | ||
338 | } | ||
339 | ret += __callchain__fprintf_graph(fp, child, new_total, | ||
340 | depth + 1, | ||
341 | new_depth_mask | (1 << depth), | ||
342 | left_margin); | ||
343 | node = next; | ||
344 | } | ||
345 | |||
346 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
347 | remaining && remaining != new_total) { | ||
348 | |||
349 | if (!rem_sq_bracket) | ||
350 | return ret; | ||
351 | |||
352 | new_depth_mask &= ~(1 << (depth - 1)); | ||
353 | |||
354 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
355 | new_depth_mask, 0, new_total, | ||
356 | remaining, left_margin); | ||
357 | } | ||
358 | |||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
363 | u64 total_samples, int left_margin) | ||
364 | { | ||
365 | struct callchain_list *chain; | ||
366 | bool printed = false; | ||
367 | int i = 0; | ||
368 | int ret = 0; | ||
369 | |||
370 | list_for_each_entry(chain, &self->val, list) { | ||
371 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
372 | continue; | ||
373 | |||
374 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
375 | continue; | ||
376 | |||
377 | if (!printed) { | ||
378 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
379 | ret += fprintf(fp, "|\n"); | ||
380 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
381 | ret += fprintf(fp, "---"); | ||
382 | |||
383 | left_margin += 3; | ||
384 | printed = true; | ||
385 | } else | ||
386 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
387 | |||
388 | if (chain->sym) | ||
389 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
390 | else | ||
391 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
392 | } | ||
393 | |||
394 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | ||
395 | |||
396 | return ret; | ||
397 | } | ||
398 | |||
399 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
400 | u64 total_samples) | ||
401 | { | ||
402 | struct callchain_list *chain; | ||
403 | size_t ret = 0; | ||
404 | |||
405 | if (!self) | ||
406 | return 0; | ||
407 | |||
408 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
409 | |||
410 | |||
411 | list_for_each_entry(chain, &self->val, list) { | ||
412 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
413 | continue; | ||
414 | if (chain->sym) | ||
415 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
416 | else | ||
417 | ret += fprintf(fp, " %p\n", | ||
418 | (void *)(long)chain->ip); | ||
419 | } | ||
420 | |||
421 | return ret; | ||
422 | } | ||
423 | |||
424 | static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
425 | u64 total_samples, int left_margin) | ||
426 | { | ||
427 | struct rb_node *rb_node; | ||
428 | struct callchain_node *chain; | ||
429 | size_t ret = 0; | ||
430 | |||
431 | rb_node = rb_first(&self->sorted_chain); | ||
432 | while (rb_node) { | ||
433 | double percent; | ||
434 | |||
435 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
436 | percent = chain->hit * 100.0 / total_samples; | ||
437 | switch (callchain_param.mode) { | ||
438 | case CHAIN_FLAT: | ||
439 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
440 | percent); | ||
441 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
442 | break; | ||
443 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
444 | case CHAIN_GRAPH_REL: | ||
445 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
446 | left_margin); | ||
447 | case CHAIN_NONE: | ||
448 | default: | ||
449 | break; | ||
450 | } | ||
451 | ret += fprintf(fp, "\n"); | ||
452 | rb_node = rb_next(rb_node); | ||
453 | } | ||
454 | |||
455 | return ret; | ||
456 | } | ||
457 | |||
458 | static size_t hist_entry__fprintf(struct hist_entry *self, | ||
459 | struct perf_session *session, | ||
460 | struct perf_session *pair_session, | ||
461 | bool show_displacement, | ||
462 | long displacement, FILE *fp) | ||
463 | { | ||
464 | struct sort_entry *se; | ||
465 | u64 count, total; | ||
466 | const char *sep = symbol_conf.field_sep; | ||
467 | size_t ret; | ||
468 | |||
469 | if (symbol_conf.exclude_other && !self->parent) | ||
470 | return 0; | ||
471 | |||
472 | if (pair_session) { | ||
473 | count = self->pair ? self->pair->count : 0; | ||
474 | total = pair_session->events_stats.total; | ||
475 | } else { | ||
476 | count = self->count; | ||
477 | total = session->events_stats.total; | ||
478 | } | ||
479 | |||
480 | if (total) | ||
481 | ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", | ||
482 | (count * 100.0) / total); | ||
483 | else | ||
484 | ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); | ||
485 | |||
486 | if (symbol_conf.show_nr_samples) { | ||
487 | if (sep) | ||
488 | fprintf(fp, "%c%lld", *sep, count); | ||
489 | else | ||
490 | fprintf(fp, "%11lld", count); | ||
491 | } | ||
492 | |||
493 | if (pair_session) { | ||
494 | char bf[32]; | ||
495 | double old_percent = 0, new_percent = 0, diff; | ||
496 | |||
497 | if (total > 0) | ||
498 | old_percent = (count * 100.0) / total; | ||
499 | if (session->events_stats.total > 0) | ||
500 | new_percent = (self->count * 100.0) / session->events_stats.total; | ||
501 | |||
502 | diff = new_percent - old_percent; | ||
503 | |||
504 | if (fabs(diff) >= 0.01) | ||
505 | snprintf(bf, sizeof(bf), "%+4.2F%%", diff); | ||
506 | else | ||
507 | snprintf(bf, sizeof(bf), " "); | ||
508 | |||
509 | if (sep) | ||
510 | ret += fprintf(fp, "%c%s", *sep, bf); | ||
511 | else | ||
512 | ret += fprintf(fp, "%11.11s", bf); | ||
513 | |||
514 | if (show_displacement) { | ||
515 | if (displacement) | ||
516 | snprintf(bf, sizeof(bf), "%+4ld", displacement); | ||
517 | else | ||
518 | snprintf(bf, sizeof(bf), " "); | ||
519 | |||
520 | if (sep) | ||
521 | fprintf(fp, "%c%s", *sep, bf); | ||
522 | else | ||
523 | fprintf(fp, "%6.6s", bf); | ||
524 | } | ||
525 | } | ||
526 | |||
527 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
528 | if (se->elide) | ||
529 | continue; | ||
530 | |||
531 | fprintf(fp, "%s", sep ?: " "); | ||
532 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
533 | } | ||
534 | |||
535 | ret += fprintf(fp, "\n"); | ||
536 | |||
537 | if (symbol_conf.use_callchain) { | ||
538 | int left_margin = 0; | ||
539 | |||
540 | if (sort__first_dimension == SORT_COMM) { | ||
541 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | ||
542 | list); | ||
543 | left_margin = se->width ? *se->width : 0; | ||
544 | left_margin -= thread__comm_len(self->thread); | ||
545 | } | ||
546 | |||
547 | hist_entry_callchain__fprintf(fp, self, session->events_stats.total, | ||
548 | left_margin); | ||
549 | } | ||
550 | |||
551 | return ret; | ||
552 | } | ||
553 | |||
554 | size_t perf_session__fprintf_hists(struct perf_session *self, | ||
555 | struct perf_session *pair, | ||
556 | bool show_displacement, FILE *fp) | ||
557 | { | ||
558 | struct sort_entry *se; | ||
559 | struct rb_node *nd; | ||
560 | size_t ret = 0; | ||
561 | unsigned long position = 1; | ||
562 | long displacement = 0; | ||
563 | unsigned int width; | ||
564 | const char *sep = symbol_conf.field_sep; | ||
565 | char *col_width = symbol_conf.col_width_list_str; | ||
566 | |||
567 | init_rem_hits(); | ||
568 | |||
569 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | ||
570 | |||
571 | if (symbol_conf.show_nr_samples) { | ||
572 | if (sep) | ||
573 | fprintf(fp, "%cSamples", *sep); | ||
574 | else | ||
575 | fputs(" Samples ", fp); | ||
576 | } | ||
577 | |||
578 | if (pair) { | ||
579 | if (sep) | ||
580 | ret += fprintf(fp, "%cDelta", *sep); | ||
581 | else | ||
582 | ret += fprintf(fp, " Delta "); | ||
583 | |||
584 | if (show_displacement) { | ||
585 | if (sep) | ||
586 | ret += fprintf(fp, "%cDisplacement", *sep); | ||
587 | else | ||
588 | ret += fprintf(fp, " Displ"); | ||
589 | } | ||
590 | } | ||
591 | |||
592 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
593 | if (se->elide) | ||
594 | continue; | ||
595 | if (sep) { | ||
596 | fprintf(fp, "%c%s", *sep, se->header); | ||
597 | continue; | ||
598 | } | ||
599 | width = strlen(se->header); | ||
600 | if (se->width) { | ||
601 | if (symbol_conf.col_width_list_str) { | ||
602 | if (col_width) { | ||
603 | *se->width = atoi(col_width); | ||
604 | col_width = strchr(col_width, ','); | ||
605 | if (col_width) | ||
606 | ++col_width; | ||
607 | } | ||
608 | } | ||
609 | width = *se->width = max(*se->width, width); | ||
610 | } | ||
611 | fprintf(fp, " %*s", width, se->header); | ||
612 | } | ||
613 | fprintf(fp, "\n"); | ||
614 | |||
615 | if (sep) | ||
616 | goto print_entries; | ||
617 | |||
618 | fprintf(fp, "# ........"); | ||
619 | if (symbol_conf.show_nr_samples) | ||
620 | fprintf(fp, " .........."); | ||
621 | if (pair) { | ||
622 | fprintf(fp, " .........."); | ||
623 | if (show_displacement) | ||
624 | fprintf(fp, " ....."); | ||
625 | } | ||
626 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
627 | unsigned int i; | ||
628 | |||
629 | if (se->elide) | ||
630 | continue; | ||
631 | |||
632 | fprintf(fp, " "); | ||
633 | if (se->width) | ||
634 | width = *se->width; | ||
635 | else | ||
636 | width = strlen(se->header); | ||
637 | for (i = 0; i < width; i++) | ||
638 | fprintf(fp, "."); | ||
639 | } | ||
640 | |||
641 | fprintf(fp, "\n#\n"); | ||
642 | |||
643 | print_entries: | ||
644 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { | ||
645 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
646 | |||
647 | if (show_displacement) { | ||
648 | if (h->pair != NULL) | ||
649 | displacement = ((long)h->pair->position - | ||
650 | (long)position); | ||
651 | else | ||
652 | displacement = 0; | ||
653 | ++position; | ||
654 | } | ||
655 | ret += hist_entry__fprintf(h, self, pair, show_displacement, | ||
656 | displacement, fp); | ||
657 | } | ||
658 | |||
659 | free(rem_sq_bracket); | ||
660 | |||
661 | return ret; | ||
662 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h new file mode 100644 index 000000000000..e5f99b24048b --- /dev/null +++ b/tools/perf/util/hist.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #ifndef __PERF_HIST_H | ||
2 | #define __PERF_HIST_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include "callchain.h" | ||
6 | |||
7 | extern struct callchain_param callchain_param; | ||
8 | |||
9 | struct perf_session; | ||
10 | struct hist_entry; | ||
11 | struct addr_location; | ||
12 | struct symbol; | ||
13 | |||
14 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, | ||
15 | struct addr_location *al, | ||
16 | struct symbol *parent, | ||
17 | u64 count, bool *hit); | ||
18 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | ||
19 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | ||
20 | void hist_entry__free(struct hist_entry *); | ||
21 | |||
22 | void perf_session__output_resort(struct perf_session *self, u64 total_samples); | ||
23 | void perf_session__collapse_resort(struct perf_session *self); | ||
24 | size_t perf_session__fprintf_hists(struct perf_session *self, | ||
25 | struct perf_session *pair, | ||
26 | bool show_displacement, FILE *fp); | ||
27 | #endif /* __PERF_HIST_H */ | ||
diff --git a/tools/perf/util/include/asm/asm-offsets.h b/tools/perf/util/include/asm/asm-offsets.h new file mode 100644 index 000000000000..ed538942523d --- /dev/null +++ b/tools/perf/util/include/asm/asm-offsets.h | |||
@@ -0,0 +1 @@ | |||
/* stub */ | |||
diff --git a/tools/perf/util/include/asm/bitops.h b/tools/perf/util/include/asm/bitops.h new file mode 100644 index 000000000000..58e9817ffae0 --- /dev/null +++ b/tools/perf/util/include/asm/bitops.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef _PERF_ASM_BITOPS_H_ | ||
2 | #define _PERF_ASM_BITOPS_H_ | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | #include "../../types.h" | ||
6 | #include <linux/compiler.h> | ||
7 | |||
8 | /* CHECKME: Not sure both always match */ | ||
9 | #define BITS_PER_LONG __WORDSIZE | ||
10 | |||
11 | #include "../../../../include/asm-generic/bitops/__fls.h" | ||
12 | #include "../../../../include/asm-generic/bitops/fls.h" | ||
13 | #include "../../../../include/asm-generic/bitops/fls64.h" | ||
14 | #include "../../../../include/asm-generic/bitops/__ffs.h" | ||
15 | #include "../../../../include/asm-generic/bitops/ffz.h" | ||
16 | #include "../../../../include/asm-generic/bitops/hweight.h" | ||
17 | |||
18 | #endif | ||
diff --git a/tools/perf/util/include/asm/bug.h b/tools/perf/util/include/asm/bug.h new file mode 100644 index 000000000000..7fcc6810adc2 --- /dev/null +++ b/tools/perf/util/include/asm/bug.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #ifndef _PERF_ASM_GENERIC_BUG_H | ||
2 | #define _PERF_ASM_GENERIC_BUG_H | ||
3 | |||
4 | #define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0) | ||
5 | |||
6 | #define WARN(condition, format...) ({ \ | ||
7 | int __ret_warn_on = !!(condition); \ | ||
8 | if (unlikely(__ret_warn_on)) \ | ||
9 | __WARN_printf(format); \ | ||
10 | unlikely(__ret_warn_on); \ | ||
11 | }) | ||
12 | |||
13 | #define WARN_ONCE(condition, format...) ({ \ | ||
14 | static int __warned; \ | ||
15 | int __ret_warn_once = !!(condition); \ | ||
16 | \ | ||
17 | if (unlikely(__ret_warn_once)) \ | ||
18 | if (WARN(!__warned, format)) \ | ||
19 | __warned = 1; \ | ||
20 | unlikely(__ret_warn_once); \ | ||
21 | }) | ||
22 | #endif | ||
diff --git a/tools/perf/util/include/asm/byteorder.h b/tools/perf/util/include/asm/byteorder.h new file mode 100644 index 000000000000..b722abe3a626 --- /dev/null +++ b/tools/perf/util/include/asm/byteorder.h | |||
@@ -0,0 +1,2 @@ | |||
1 | #include <asm/types.h> | ||
2 | #include "../../../../include/linux/swab.h" | ||
diff --git a/tools/perf/util/include/asm/swab.h b/tools/perf/util/include/asm/swab.h new file mode 100644 index 000000000000..ed538942523d --- /dev/null +++ b/tools/perf/util/include/asm/swab.h | |||
@@ -0,0 +1 @@ | |||
/* stub */ | |||
diff --git a/tools/perf/util/include/asm/uaccess.h b/tools/perf/util/include/asm/uaccess.h new file mode 100644 index 000000000000..d0f72b8fcc35 --- /dev/null +++ b/tools/perf/util/include/asm/uaccess.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef _PERF_ASM_UACCESS_H_ | ||
2 | #define _PERF_ASM_UACCESS_H_ | ||
3 | |||
4 | #define __get_user(src, dest) \ | ||
5 | ({ \ | ||
6 | (src) = *dest; \ | ||
7 | 0; \ | ||
8 | }) | ||
9 | |||
10 | #define get_user __get_user | ||
11 | |||
12 | #define access_ok(type, addr, size) 1 | ||
13 | |||
14 | #endif | ||
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h new file mode 100644 index 000000000000..94507639a8c4 --- /dev/null +++ b/tools/perf/util/include/linux/bitmap.h | |||
@@ -0,0 +1,3 @@ | |||
1 | #include "../../../../include/linux/bitmap.h" | ||
2 | #include "../../../../include/asm-generic/bitops/find.h" | ||
3 | #include <linux/errno.h> | ||
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h new file mode 100644 index 000000000000..8d63116e9435 --- /dev/null +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -0,0 +1,29 @@ | |||
1 | #ifndef _PERF_LINUX_BITOPS_H_ | ||
2 | #define _PERF_LINUX_BITOPS_H_ | ||
3 | |||
4 | #define __KERNEL__ | ||
5 | |||
6 | #define CONFIG_GENERIC_FIND_NEXT_BIT | ||
7 | #define CONFIG_GENERIC_FIND_FIRST_BIT | ||
8 | #include "../../../../include/linux/bitops.h" | ||
9 | |||
10 | #undef __KERNEL__ | ||
11 | |||
12 | static inline void set_bit(int nr, unsigned long *addr) | ||
13 | { | ||
14 | addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG); | ||
15 | } | ||
16 | |||
17 | static __always_inline int test_bit(unsigned int nr, const unsigned long *addr) | ||
18 | { | ||
19 | return ((1UL << (nr % BITS_PER_LONG)) & | ||
20 | (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0; | ||
21 | } | ||
22 | |||
23 | unsigned long generic_find_next_zero_le_bit(const unsigned long *addr, unsigned | ||
24 | long size, unsigned long offset); | ||
25 | |||
26 | unsigned long generic_find_next_le_bit(const unsigned long *addr, unsigned | ||
27 | long size, unsigned long offset); | ||
28 | |||
29 | #endif | ||
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h new file mode 100644 index 000000000000..dfb0713ed47f --- /dev/null +++ b/tools/perf/util/include/linux/compiler.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef _PERF_LINUX_COMPILER_H_ | ||
2 | #define _PERF_LINUX_COMPILER_H_ | ||
3 | |||
4 | #ifndef __always_inline | ||
5 | #define __always_inline inline | ||
6 | #endif | ||
7 | #define __user | ||
8 | #define __attribute_const__ | ||
9 | |||
10 | #endif | ||
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h new file mode 100644 index 000000000000..a53d4ee1e0b7 --- /dev/null +++ b/tools/perf/util/include/linux/ctype.h | |||
@@ -0,0 +1 @@ | |||
#include "../util.h" | |||
diff --git a/tools/perf/util/include/linux/hash.h b/tools/perf/util/include/linux/hash.h new file mode 100644 index 000000000000..201f57397997 --- /dev/null +++ b/tools/perf/util/include/linux/hash.h | |||
@@ -0,0 +1,5 @@ | |||
1 | #include "../../../../include/linux/hash.h" | ||
2 | |||
3 | #ifndef PERF_HASH_H | ||
4 | #define PERF_HASH_H | ||
5 | #endif | ||
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index a6b87390cb52..f2611655ab51 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
@@ -1,6 +1,16 @@ | |||
1 | #ifndef PERF_LINUX_KERNEL_H_ | 1 | #ifndef PERF_LINUX_KERNEL_H_ |
2 | #define PERF_LINUX_KERNEL_H_ | 2 | #define PERF_LINUX_KERNEL_H_ |
3 | 3 | ||
4 | #include <stdarg.h> | ||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <assert.h> | ||
8 | |||
9 | #define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) | ||
10 | |||
11 | #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) | ||
12 | #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) | ||
13 | |||
4 | #ifndef offsetof | 14 | #ifndef offsetof |
5 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | 15 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) |
6 | #endif | 16 | #endif |
@@ -26,4 +36,71 @@ | |||
26 | _max1 > _max2 ? _max1 : _max2; }) | 36 | _max1 > _max2 ? _max1 : _max2; }) |
27 | #endif | 37 | #endif |
28 | 38 | ||
39 | #ifndef min | ||
40 | #define min(x, y) ({ \ | ||
41 | typeof(x) _min1 = (x); \ | ||
42 | typeof(y) _min2 = (y); \ | ||
43 | (void) (&_min1 == &_min2); \ | ||
44 | _min1 < _min2 ? _min1 : _min2; }) | ||
45 | #endif | ||
46 | |||
47 | #ifndef BUG_ON | ||
48 | #define BUG_ON(cond) assert(!(cond)) | ||
49 | #endif | ||
50 | |||
51 | /* | ||
52 | * Both need more care to handle endianness | ||
53 | * (Don't use bitmap_copy_le() for now) | ||
54 | */ | ||
55 | #define cpu_to_le64(x) (x) | ||
56 | #define cpu_to_le32(x) (x) | ||
57 | |||
58 | static inline int | ||
59 | vscnprintf(char *buf, size_t size, const char *fmt, va_list args) | ||
60 | { | ||
61 | int i; | ||
62 | ssize_t ssize = size; | ||
63 | |||
64 | i = vsnprintf(buf, size, fmt, args); | ||
65 | |||
66 | return (i >= ssize) ? (ssize - 1) : i; | ||
67 | } | ||
68 | |||
69 | static inline int scnprintf(char * buf, size_t size, const char * fmt, ...) | ||
70 | { | ||
71 | va_list args; | ||
72 | ssize_t ssize = size; | ||
73 | int i; | ||
74 | |||
75 | va_start(args, fmt); | ||
76 | i = vsnprintf(buf, size, fmt, args); | ||
77 | va_end(args); | ||
78 | |||
79 | return (i >= ssize) ? (ssize - 1) : i; | ||
80 | } | ||
81 | |||
82 | static inline unsigned long | ||
83 | simple_strtoul(const char *nptr, char **endptr, int base) | ||
84 | { | ||
85 | return strtoul(nptr, endptr, base); | ||
86 | } | ||
87 | |||
88 | #ifndef pr_fmt | ||
89 | #define pr_fmt(fmt) fmt | ||
90 | #endif | ||
91 | |||
92 | #define pr_err(fmt, ...) \ | ||
93 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
94 | #define pr_warning(fmt, ...) \ | ||
95 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
96 | #define pr_info(fmt, ...) \ | ||
97 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | ||
98 | #define pr_debug(fmt, ...) \ | ||
99 | eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) | ||
100 | #define pr_debugN(n, fmt, ...) \ | ||
101 | eprintf(n, pr_fmt(fmt), ##__VA_ARGS__) | ||
102 | #define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__) | ||
103 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) | ||
104 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) | ||
105 | |||
29 | #endif | 106 | #endif |
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h new file mode 100644 index 000000000000..3b2f5900276f --- /dev/null +++ b/tools/perf/util/include/linux/string.h | |||
@@ -0,0 +1 @@ | |||
#include <string.h> | |||
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h new file mode 100644 index 000000000000..196862a81a21 --- /dev/null +++ b/tools/perf/util/include/linux/types.h | |||
@@ -0,0 +1,9 @@ | |||
1 | #ifndef _PERF_LINUX_TYPES_H_ | ||
2 | #define _PERF_LINUX_TYPES_H_ | ||
3 | |||
4 | #include <asm/types.h> | ||
5 | |||
6 | #define DECLARE_BITMAP(name,bits) \ | ||
7 | unsigned long name[BITS_TO_LONGS(bits)] | ||
8 | |||
9 | #endif | ||
diff --git a/tools/perf/util/levenshtein.h b/tools/perf/util/levenshtein.h index 0173abeef52c..b0fcb6d8a881 100644 --- a/tools/perf/util/levenshtein.h +++ b/tools/perf/util/levenshtein.h | |||
@@ -1,8 +1,8 @@ | |||
1 | #ifndef LEVENSHTEIN_H | 1 | #ifndef __PERF_LEVENSHTEIN_H |
2 | #define LEVENSHTEIN_H | 2 | #define __PERF_LEVENSHTEIN_H |
3 | 3 | ||
4 | int levenshtein(const char *string1, const char *string2, | 4 | int levenshtein(const char *string1, const char *string2, |
5 | int swap_penalty, int substition_penalty, | 5 | int swap_penalty, int substition_penalty, |
6 | int insertion_penalty, int deletion_penalty); | 6 | int insertion_penalty, int deletion_penalty); |
7 | 7 | ||
8 | #endif | 8 | #endif /* __PERF_LEVENSHTEIN_H */ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 804e02382739..e509cd59c67d 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -3,6 +3,12 @@ | |||
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include "debug.h" | ||
7 | |||
8 | const char *map_type__name[MAP__NR_TYPES] = { | ||
9 | [MAP__FUNCTION] = "Functions", | ||
10 | [MAP__VARIABLE] = "Variables", | ||
11 | }; | ||
6 | 12 | ||
7 | static inline int is_anon_memory(const char *filename) | 13 | static inline int is_anon_memory(const char *filename) |
8 | { | 14 | { |
@@ -19,13 +25,28 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen) | |||
19 | return n; | 25 | return n; |
20 | } | 26 | } |
21 | 27 | ||
22 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen) | 28 | void map__init(struct map *self, enum map_type type, |
29 | u64 start, u64 end, u64 pgoff, struct dso *dso) | ||
30 | { | ||
31 | self->type = type; | ||
32 | self->start = start; | ||
33 | self->end = end; | ||
34 | self->pgoff = pgoff; | ||
35 | self->dso = dso; | ||
36 | self->map_ip = map__map_ip; | ||
37 | self->unmap_ip = map__unmap_ip; | ||
38 | RB_CLEAR_NODE(&self->rb_node); | ||
39 | } | ||
40 | |||
41 | struct map *map__new(struct mmap_event *event, enum map_type type, | ||
42 | char *cwd, int cwdlen) | ||
23 | { | 43 | { |
24 | struct map *self = malloc(sizeof(*self)); | 44 | struct map *self = malloc(sizeof(*self)); |
25 | 45 | ||
26 | if (self != NULL) { | 46 | if (self != NULL) { |
27 | const char *filename = event->filename; | 47 | const char *filename = event->filename; |
28 | char newfilename[PATH_MAX]; | 48 | char newfilename[PATH_MAX]; |
49 | struct dso *dso; | ||
29 | int anon; | 50 | int anon; |
30 | 51 | ||
31 | if (cwd) { | 52 | if (cwd) { |
@@ -45,18 +66,20 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen) | |||
45 | filename = newfilename; | 66 | filename = newfilename; |
46 | } | 67 | } |
47 | 68 | ||
48 | self->start = event->start; | 69 | dso = dsos__findnew(filename); |
49 | self->end = event->start + event->len; | 70 | if (dso == NULL) |
50 | self->pgoff = event->pgoff; | ||
51 | |||
52 | self->dso = dsos__findnew(filename); | ||
53 | if (self->dso == NULL) | ||
54 | goto out_delete; | 71 | goto out_delete; |
55 | 72 | ||
56 | if (self->dso == vdso || anon) | 73 | map__init(self, type, event->start, event->start + event->len, |
57 | self->map_ip = vdso__map_ip; | 74 | event->pgoff, dso); |
58 | else | 75 | |
59 | self->map_ip = map__map_ip; | 76 | if (anon) { |
77 | set_identity: | ||
78 | self->map_ip = self->unmap_ip = identity__map_ip; | ||
79 | } else if (strcmp(filename, "[vdso]") == 0) { | ||
80 | dso__set_loaded(dso, self->type); | ||
81 | goto set_identity; | ||
82 | } | ||
60 | } | 83 | } |
61 | return self; | 84 | return self; |
62 | out_delete: | 85 | out_delete: |
@@ -64,6 +87,103 @@ out_delete: | |||
64 | return NULL; | 87 | return NULL; |
65 | } | 88 | } |
66 | 89 | ||
90 | void map__delete(struct map *self) | ||
91 | { | ||
92 | free(self); | ||
93 | } | ||
94 | |||
95 | void map__fixup_start(struct map *self) | ||
96 | { | ||
97 | struct rb_root *symbols = &self->dso->symbols[self->type]; | ||
98 | struct rb_node *nd = rb_first(symbols); | ||
99 | if (nd != NULL) { | ||
100 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | ||
101 | self->start = sym->start; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | void map__fixup_end(struct map *self) | ||
106 | { | ||
107 | struct rb_root *symbols = &self->dso->symbols[self->type]; | ||
108 | struct rb_node *nd = rb_last(symbols); | ||
109 | if (nd != NULL) { | ||
110 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | ||
111 | self->end = sym->end; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | #define DSO__DELETED "(deleted)" | ||
116 | |||
117 | int map__load(struct map *self, symbol_filter_t filter) | ||
118 | { | ||
119 | const char *name = self->dso->long_name; | ||
120 | int nr; | ||
121 | |||
122 | if (dso__loaded(self->dso, self->type)) | ||
123 | return 0; | ||
124 | |||
125 | nr = dso__load(self->dso, self, filter); | ||
126 | if (nr < 0) { | ||
127 | if (self->dso->has_build_id) { | ||
128 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
129 | |||
130 | build_id__sprintf(self->dso->build_id, | ||
131 | sizeof(self->dso->build_id), | ||
132 | sbuild_id); | ||
133 | pr_warning("%s with build id %s not found", | ||
134 | name, sbuild_id); | ||
135 | } else | ||
136 | pr_warning("Failed to open %s", name); | ||
137 | |||
138 | pr_warning(", continuing without symbols\n"); | ||
139 | return -1; | ||
140 | } else if (nr == 0) { | ||
141 | const size_t len = strlen(name); | ||
142 | const size_t real_len = len - sizeof(DSO__DELETED); | ||
143 | |||
144 | if (len > sizeof(DSO__DELETED) && | ||
145 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | ||
146 | pr_warning("%.*s was updated, restart the long " | ||
147 | "running apps that use it!\n", | ||
148 | (int)real_len, name); | ||
149 | } else { | ||
150 | pr_warning("no symbols found in %s, maybe install " | ||
151 | "a debug package?\n", name); | ||
152 | } | ||
153 | |||
154 | return -1; | ||
155 | } | ||
156 | /* | ||
157 | * Only applies to the kernel, as its symtabs aren't relative like the | ||
158 | * module ones. | ||
159 | */ | ||
160 | if (self->dso->kernel) | ||
161 | map__reloc_vmlinux(self); | ||
162 | |||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | struct symbol *map__find_symbol(struct map *self, u64 addr, | ||
167 | symbol_filter_t filter) | ||
168 | { | ||
169 | if (map__load(self, filter) < 0) | ||
170 | return NULL; | ||
171 | |||
172 | return dso__find_symbol(self->dso, self->type, addr); | ||
173 | } | ||
174 | |||
175 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
176 | symbol_filter_t filter) | ||
177 | { | ||
178 | if (map__load(self, filter) < 0) | ||
179 | return NULL; | ||
180 | |||
181 | if (!dso__sorted_by_name(self->dso, self->type)) | ||
182 | dso__sort_by_name(self->dso, self->type); | ||
183 | |||
184 | return dso__find_symbol_by_name(self->dso, self->type, name); | ||
185 | } | ||
186 | |||
67 | struct map *map__clone(struct map *self) | 187 | struct map *map__clone(struct map *self) |
68 | { | 188 | { |
69 | struct map *map = malloc(sizeof(*self)); | 189 | struct map *map = malloc(sizeof(*self)); |
@@ -95,3 +215,23 @@ size_t map__fprintf(struct map *self, FILE *fp) | |||
95 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", | 215 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", |
96 | self->start, self->end, self->pgoff, self->dso->name); | 216 | self->start, self->end, self->pgoff, self->dso->name); |
97 | } | 217 | } |
218 | |||
219 | /* | ||
220 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. | ||
221 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. | ||
222 | */ | ||
223 | u64 map__rip_2objdump(struct map *map, u64 rip) | ||
224 | { | ||
225 | u64 addr = map->dso->adjust_symbols ? | ||
226 | map->unmap_ip(map, rip) : /* RIP -> IP */ | ||
227 | rip; | ||
228 | return addr; | ||
229 | } | ||
230 | |||
231 | u64 map__objdump_2ip(struct map *map, u64 addr) | ||
232 | { | ||
233 | u64 ip = map->dso->adjust_symbols ? | ||
234 | addr : | ||
235 | map->unmap_ip(map, addr); /* RIP -> IP */ | ||
236 | return ip; | ||
237 | } | ||
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h new file mode 100644 index 000000000000..b756368076c6 --- /dev/null +++ b/tools/perf/util/map.h | |||
@@ -0,0 +1,94 @@ | |||
1 | #ifndef __PERF_MAP_H | ||
2 | #define __PERF_MAP_H | ||
3 | |||
4 | #include <linux/compiler.h> | ||
5 | #include <linux/list.h> | ||
6 | #include <linux/rbtree.h> | ||
7 | #include <linux/types.h> | ||
8 | |||
9 | enum map_type { | ||
10 | MAP__FUNCTION = 0, | ||
11 | MAP__VARIABLE, | ||
12 | }; | ||
13 | |||
14 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | ||
15 | |||
16 | extern const char *map_type__name[MAP__NR_TYPES]; | ||
17 | |||
18 | struct dso; | ||
19 | struct ref_reloc_sym; | ||
20 | struct map_groups; | ||
21 | |||
22 | struct map { | ||
23 | union { | ||
24 | struct rb_node rb_node; | ||
25 | struct list_head node; | ||
26 | }; | ||
27 | u64 start; | ||
28 | u64 end; | ||
29 | enum map_type type; | ||
30 | u64 pgoff; | ||
31 | |||
32 | /* ip -> dso rip */ | ||
33 | u64 (*map_ip)(struct map *, u64); | ||
34 | /* dso rip -> ip */ | ||
35 | u64 (*unmap_ip)(struct map *, u64); | ||
36 | |||
37 | struct dso *dso; | ||
38 | }; | ||
39 | |||
40 | struct kmap { | ||
41 | struct ref_reloc_sym *ref_reloc_sym; | ||
42 | struct map_groups *kmaps; | ||
43 | }; | ||
44 | |||
45 | static inline struct kmap *map__kmap(struct map *self) | ||
46 | { | ||
47 | return (struct kmap *)(self + 1); | ||
48 | } | ||
49 | |||
50 | static inline u64 map__map_ip(struct map *map, u64 ip) | ||
51 | { | ||
52 | return ip - map->start + map->pgoff; | ||
53 | } | ||
54 | |||
55 | static inline u64 map__unmap_ip(struct map *map, u64 ip) | ||
56 | { | ||
57 | return ip + map->start - map->pgoff; | ||
58 | } | ||
59 | |||
60 | static inline u64 identity__map_ip(struct map *map __used, u64 ip) | ||
61 | { | ||
62 | return ip; | ||
63 | } | ||
64 | |||
65 | |||
66 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ | ||
67 | u64 map__rip_2objdump(struct map *map, u64 rip); | ||
68 | u64 map__objdump_2ip(struct map *map, u64 addr); | ||
69 | |||
70 | struct symbol; | ||
71 | struct mmap_event; | ||
72 | |||
73 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | ||
74 | |||
75 | void map__init(struct map *self, enum map_type type, | ||
76 | u64 start, u64 end, u64 pgoff, struct dso *dso); | ||
77 | struct map *map__new(struct mmap_event *event, enum map_type, | ||
78 | char *cwd, int cwdlen); | ||
79 | void map__delete(struct map *self); | ||
80 | struct map *map__clone(struct map *self); | ||
81 | int map__overlap(struct map *l, struct map *r); | ||
82 | size_t map__fprintf(struct map *self, FILE *fp); | ||
83 | |||
84 | int map__load(struct map *self, symbol_filter_t filter); | ||
85 | struct symbol *map__find_symbol(struct map *self, | ||
86 | u64 addr, symbol_filter_t filter); | ||
87 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
88 | symbol_filter_t filter); | ||
89 | void map__fixup_start(struct map *self); | ||
90 | void map__fixup_end(struct map *self); | ||
91 | |||
92 | void map__reloc_vmlinux(struct map *self); | ||
93 | |||
94 | #endif /* __PERF_MAP_H */ | ||
diff --git a/tools/perf/util/module.c b/tools/perf/util/module.c deleted file mode 100644 index 0d8c85defcd2..000000000000 --- a/tools/perf/util/module.c +++ /dev/null | |||
@@ -1,545 +0,0 @@ | |||
1 | #include "util.h" | ||
2 | #include "../perf.h" | ||
3 | #include "string.h" | ||
4 | #include "module.h" | ||
5 | |||
6 | #include <libelf.h> | ||
7 | #include <libgen.h> | ||
8 | #include <gelf.h> | ||
9 | #include <elf.h> | ||
10 | #include <dirent.h> | ||
11 | #include <sys/utsname.h> | ||
12 | |||
13 | static unsigned int crc32(const char *p, unsigned int len) | ||
14 | { | ||
15 | int i; | ||
16 | unsigned int crc = 0; | ||
17 | |||
18 | while (len--) { | ||
19 | crc ^= *p++; | ||
20 | for (i = 0; i < 8; i++) | ||
21 | crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0); | ||
22 | } | ||
23 | return crc; | ||
24 | } | ||
25 | |||
26 | /* module section methods */ | ||
27 | |||
28 | struct sec_dso *sec_dso__new_dso(const char *name) | ||
29 | { | ||
30 | struct sec_dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
31 | |||
32 | if (self != NULL) { | ||
33 | strcpy(self->name, name); | ||
34 | self->secs = RB_ROOT; | ||
35 | self->find_section = sec_dso__find_section; | ||
36 | } | ||
37 | |||
38 | return self; | ||
39 | } | ||
40 | |||
41 | static void sec_dso__delete_section(struct section *self) | ||
42 | { | ||
43 | free(((void *)self)); | ||
44 | } | ||
45 | |||
46 | void sec_dso__delete_sections(struct sec_dso *self) | ||
47 | { | ||
48 | struct section *pos; | ||
49 | struct rb_node *next = rb_first(&self->secs); | ||
50 | |||
51 | while (next) { | ||
52 | pos = rb_entry(next, struct section, rb_node); | ||
53 | next = rb_next(&pos->rb_node); | ||
54 | rb_erase(&pos->rb_node, &self->secs); | ||
55 | sec_dso__delete_section(pos); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | void sec_dso__delete_self(struct sec_dso *self) | ||
60 | { | ||
61 | sec_dso__delete_sections(self); | ||
62 | free(self); | ||
63 | } | ||
64 | |||
65 | static void sec_dso__insert_section(struct sec_dso *self, struct section *sec) | ||
66 | { | ||
67 | struct rb_node **p = &self->secs.rb_node; | ||
68 | struct rb_node *parent = NULL; | ||
69 | const u64 hash = sec->hash; | ||
70 | struct section *s; | ||
71 | |||
72 | while (*p != NULL) { | ||
73 | parent = *p; | ||
74 | s = rb_entry(parent, struct section, rb_node); | ||
75 | if (hash < s->hash) | ||
76 | p = &(*p)->rb_left; | ||
77 | else | ||
78 | p = &(*p)->rb_right; | ||
79 | } | ||
80 | rb_link_node(&sec->rb_node, parent, p); | ||
81 | rb_insert_color(&sec->rb_node, &self->secs); | ||
82 | } | ||
83 | |||
84 | struct section *sec_dso__find_section(struct sec_dso *self, const char *name) | ||
85 | { | ||
86 | struct rb_node *n; | ||
87 | u64 hash; | ||
88 | int len; | ||
89 | |||
90 | if (self == NULL) | ||
91 | return NULL; | ||
92 | |||
93 | len = strlen(name); | ||
94 | hash = crc32(name, len); | ||
95 | |||
96 | n = self->secs.rb_node; | ||
97 | |||
98 | while (n) { | ||
99 | struct section *s = rb_entry(n, struct section, rb_node); | ||
100 | |||
101 | if (hash < s->hash) | ||
102 | n = n->rb_left; | ||
103 | else if (hash > s->hash) | ||
104 | n = n->rb_right; | ||
105 | else { | ||
106 | if (!strcmp(name, s->name)) | ||
107 | return s; | ||
108 | else | ||
109 | n = rb_next(&s->rb_node); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | static size_t sec_dso__fprintf_section(struct section *self, FILE *fp) | ||
117 | { | ||
118 | return fprintf(fp, "name:%s vma:%llx path:%s\n", | ||
119 | self->name, self->vma, self->path); | ||
120 | } | ||
121 | |||
122 | size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp) | ||
123 | { | ||
124 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | ||
125 | |||
126 | struct rb_node *nd; | ||
127 | for (nd = rb_first(&self->secs); nd; nd = rb_next(nd)) { | ||
128 | struct section *pos = rb_entry(nd, struct section, rb_node); | ||
129 | ret += sec_dso__fprintf_section(pos, fp); | ||
130 | } | ||
131 | |||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | static struct section *section__new(const char *name, const char *path) | ||
136 | { | ||
137 | struct section *self = calloc(1, sizeof(*self)); | ||
138 | |||
139 | if (!self) | ||
140 | goto out_failure; | ||
141 | |||
142 | self->name = calloc(1, strlen(name) + 1); | ||
143 | if (!self->name) | ||
144 | goto out_failure; | ||
145 | |||
146 | self->path = calloc(1, strlen(path) + 1); | ||
147 | if (!self->path) | ||
148 | goto out_failure; | ||
149 | |||
150 | strcpy(self->name, name); | ||
151 | strcpy(self->path, path); | ||
152 | self->hash = crc32(self->name, strlen(name)); | ||
153 | |||
154 | return self; | ||
155 | |||
156 | out_failure: | ||
157 | if (self) { | ||
158 | if (self->name) | ||
159 | free(self->name); | ||
160 | if (self->path) | ||
161 | free(self->path); | ||
162 | free(self); | ||
163 | } | ||
164 | |||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | /* module methods */ | ||
169 | |||
170 | struct mod_dso *mod_dso__new_dso(const char *name) | ||
171 | { | ||
172 | struct mod_dso *self = malloc(sizeof(*self) + strlen(name) + 1); | ||
173 | |||
174 | if (self != NULL) { | ||
175 | strcpy(self->name, name); | ||
176 | self->mods = RB_ROOT; | ||
177 | self->find_module = mod_dso__find_module; | ||
178 | } | ||
179 | |||
180 | return self; | ||
181 | } | ||
182 | |||
183 | static void mod_dso__delete_module(struct module *self) | ||
184 | { | ||
185 | free(((void *)self)); | ||
186 | } | ||
187 | |||
188 | void mod_dso__delete_modules(struct mod_dso *self) | ||
189 | { | ||
190 | struct module *pos; | ||
191 | struct rb_node *next = rb_first(&self->mods); | ||
192 | |||
193 | while (next) { | ||
194 | pos = rb_entry(next, struct module, rb_node); | ||
195 | next = rb_next(&pos->rb_node); | ||
196 | rb_erase(&pos->rb_node, &self->mods); | ||
197 | mod_dso__delete_module(pos); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | void mod_dso__delete_self(struct mod_dso *self) | ||
202 | { | ||
203 | mod_dso__delete_modules(self); | ||
204 | free(self); | ||
205 | } | ||
206 | |||
207 | static void mod_dso__insert_module(struct mod_dso *self, struct module *mod) | ||
208 | { | ||
209 | struct rb_node **p = &self->mods.rb_node; | ||
210 | struct rb_node *parent = NULL; | ||
211 | const u64 hash = mod->hash; | ||
212 | struct module *m; | ||
213 | |||
214 | while (*p != NULL) { | ||
215 | parent = *p; | ||
216 | m = rb_entry(parent, struct module, rb_node); | ||
217 | if (hash < m->hash) | ||
218 | p = &(*p)->rb_left; | ||
219 | else | ||
220 | p = &(*p)->rb_right; | ||
221 | } | ||
222 | rb_link_node(&mod->rb_node, parent, p); | ||
223 | rb_insert_color(&mod->rb_node, &self->mods); | ||
224 | } | ||
225 | |||
226 | struct module *mod_dso__find_module(struct mod_dso *self, const char *name) | ||
227 | { | ||
228 | struct rb_node *n; | ||
229 | u64 hash; | ||
230 | int len; | ||
231 | |||
232 | if (self == NULL) | ||
233 | return NULL; | ||
234 | |||
235 | len = strlen(name); | ||
236 | hash = crc32(name, len); | ||
237 | |||
238 | n = self->mods.rb_node; | ||
239 | |||
240 | while (n) { | ||
241 | struct module *m = rb_entry(n, struct module, rb_node); | ||
242 | |||
243 | if (hash < m->hash) | ||
244 | n = n->rb_left; | ||
245 | else if (hash > m->hash) | ||
246 | n = n->rb_right; | ||
247 | else { | ||
248 | if (!strcmp(name, m->name)) | ||
249 | return m; | ||
250 | else | ||
251 | n = rb_next(&m->rb_node); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | return NULL; | ||
256 | } | ||
257 | |||
258 | static size_t mod_dso__fprintf_module(struct module *self, FILE *fp) | ||
259 | { | ||
260 | return fprintf(fp, "name:%s path:%s\n", self->name, self->path); | ||
261 | } | ||
262 | |||
263 | size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp) | ||
264 | { | ||
265 | struct rb_node *nd; | ||
266 | size_t ret; | ||
267 | |||
268 | ret = fprintf(fp, "dso: %s\n", self->name); | ||
269 | |||
270 | for (nd = rb_first(&self->mods); nd; nd = rb_next(nd)) { | ||
271 | struct module *pos = rb_entry(nd, struct module, rb_node); | ||
272 | |||
273 | ret += mod_dso__fprintf_module(pos, fp); | ||
274 | } | ||
275 | |||
276 | return ret; | ||
277 | } | ||
278 | |||
279 | static struct module *module__new(const char *name, const char *path) | ||
280 | { | ||
281 | struct module *self = calloc(1, sizeof(*self)); | ||
282 | |||
283 | if (!self) | ||
284 | goto out_failure; | ||
285 | |||
286 | self->name = calloc(1, strlen(name) + 1); | ||
287 | if (!self->name) | ||
288 | goto out_failure; | ||
289 | |||
290 | self->path = calloc(1, strlen(path) + 1); | ||
291 | if (!self->path) | ||
292 | goto out_failure; | ||
293 | |||
294 | strcpy(self->name, name); | ||
295 | strcpy(self->path, path); | ||
296 | self->hash = crc32(self->name, strlen(name)); | ||
297 | |||
298 | return self; | ||
299 | |||
300 | out_failure: | ||
301 | if (self) { | ||
302 | if (self->name) | ||
303 | free(self->name); | ||
304 | if (self->path) | ||
305 | free(self->path); | ||
306 | free(self); | ||
307 | } | ||
308 | |||
309 | return NULL; | ||
310 | } | ||
311 | |||
312 | static int mod_dso__load_sections(struct module *mod) | ||
313 | { | ||
314 | int count = 0, path_len; | ||
315 | struct dirent *entry; | ||
316 | char *line = NULL; | ||
317 | char *dir_path; | ||
318 | DIR *dir; | ||
319 | size_t n; | ||
320 | |||
321 | path_len = strlen("/sys/module/"); | ||
322 | path_len += strlen(mod->name); | ||
323 | path_len += strlen("/sections/"); | ||
324 | |||
325 | dir_path = calloc(1, path_len + 1); | ||
326 | if (dir_path == NULL) | ||
327 | goto out_failure; | ||
328 | |||
329 | strcat(dir_path, "/sys/module/"); | ||
330 | strcat(dir_path, mod->name); | ||
331 | strcat(dir_path, "/sections/"); | ||
332 | |||
333 | dir = opendir(dir_path); | ||
334 | if (dir == NULL) | ||
335 | goto out_free; | ||
336 | |||
337 | while ((entry = readdir(dir))) { | ||
338 | struct section *section; | ||
339 | char *path, *vma; | ||
340 | int line_len; | ||
341 | FILE *file; | ||
342 | |||
343 | if (!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) | ||
344 | continue; | ||
345 | |||
346 | path = calloc(1, path_len + strlen(entry->d_name) + 1); | ||
347 | if (path == NULL) | ||
348 | break; | ||
349 | strcat(path, dir_path); | ||
350 | strcat(path, entry->d_name); | ||
351 | |||
352 | file = fopen(path, "r"); | ||
353 | if (file == NULL) { | ||
354 | free(path); | ||
355 | break; | ||
356 | } | ||
357 | |||
358 | line_len = getline(&line, &n, file); | ||
359 | if (line_len < 0) { | ||
360 | free(path); | ||
361 | fclose(file); | ||
362 | break; | ||
363 | } | ||
364 | |||
365 | if (!line) { | ||
366 | free(path); | ||
367 | fclose(file); | ||
368 | break; | ||
369 | } | ||
370 | |||
371 | line[--line_len] = '\0'; /* \n */ | ||
372 | |||
373 | vma = strstr(line, "0x"); | ||
374 | if (!vma) { | ||
375 | free(path); | ||
376 | fclose(file); | ||
377 | break; | ||
378 | } | ||
379 | vma += 2; | ||
380 | |||
381 | section = section__new(entry->d_name, path); | ||
382 | if (!section) { | ||
383 | fprintf(stderr, "load_sections: allocation error\n"); | ||
384 | free(path); | ||
385 | fclose(file); | ||
386 | break; | ||
387 | } | ||
388 | |||
389 | hex2u64(vma, §ion->vma); | ||
390 | sec_dso__insert_section(mod->sections, section); | ||
391 | |||
392 | free(path); | ||
393 | fclose(file); | ||
394 | count++; | ||
395 | } | ||
396 | |||
397 | closedir(dir); | ||
398 | free(line); | ||
399 | free(dir_path); | ||
400 | |||
401 | return count; | ||
402 | |||
403 | out_free: | ||
404 | free(dir_path); | ||
405 | |||
406 | out_failure: | ||
407 | return count; | ||
408 | } | ||
409 | |||
410 | static int mod_dso__load_module_paths(struct mod_dso *self) | ||
411 | { | ||
412 | struct utsname uts; | ||
413 | int count = 0, len, err = -1; | ||
414 | char *line = NULL; | ||
415 | FILE *file; | ||
416 | char *dpath, *dir; | ||
417 | size_t n; | ||
418 | |||
419 | if (uname(&uts) < 0) | ||
420 | return err; | ||
421 | |||
422 | len = strlen("/lib/modules/"); | ||
423 | len += strlen(uts.release); | ||
424 | len += strlen("/modules.dep"); | ||
425 | |||
426 | dpath = calloc(1, len + 1); | ||
427 | if (dpath == NULL) | ||
428 | return err; | ||
429 | |||
430 | strcat(dpath, "/lib/modules/"); | ||
431 | strcat(dpath, uts.release); | ||
432 | strcat(dpath, "/modules.dep"); | ||
433 | |||
434 | file = fopen(dpath, "r"); | ||
435 | if (file == NULL) | ||
436 | goto out_failure; | ||
437 | |||
438 | dir = dirname(dpath); | ||
439 | if (!dir) | ||
440 | goto out_failure; | ||
441 | strcat(dir, "/"); | ||
442 | |||
443 | while (!feof(file)) { | ||
444 | struct module *module; | ||
445 | char *name, *path, *tmp; | ||
446 | FILE *modfile; | ||
447 | int line_len; | ||
448 | |||
449 | line_len = getline(&line, &n, file); | ||
450 | if (line_len < 0) | ||
451 | break; | ||
452 | |||
453 | if (!line) | ||
454 | break; | ||
455 | |||
456 | line[--line_len] = '\0'; /* \n */ | ||
457 | |||
458 | path = strchr(line, ':'); | ||
459 | if (!path) | ||
460 | break; | ||
461 | *path = '\0'; | ||
462 | |||
463 | path = strdup(line); | ||
464 | if (!path) | ||
465 | break; | ||
466 | |||
467 | if (!strstr(path, dir)) { | ||
468 | if (strncmp(path, "kernel/", 7)) | ||
469 | break; | ||
470 | |||
471 | free(path); | ||
472 | path = calloc(1, strlen(dir) + strlen(line) + 1); | ||
473 | if (!path) | ||
474 | break; | ||
475 | strcat(path, dir); | ||
476 | strcat(path, line); | ||
477 | } | ||
478 | |||
479 | modfile = fopen(path, "r"); | ||
480 | if (modfile == NULL) | ||
481 | break; | ||
482 | fclose(modfile); | ||
483 | |||
484 | name = strdup(path); | ||
485 | if (!name) | ||
486 | break; | ||
487 | |||
488 | name = strtok(name, "/"); | ||
489 | tmp = name; | ||
490 | |||
491 | while (tmp) { | ||
492 | tmp = strtok(NULL, "/"); | ||
493 | if (tmp) | ||
494 | name = tmp; | ||
495 | } | ||
496 | |||
497 | name = strsep(&name, "."); | ||
498 | if (!name) | ||
499 | break; | ||
500 | |||
501 | /* Quirk: replace '-' with '_' in all modules */ | ||
502 | for (len = strlen(name); len; len--) { | ||
503 | if (*(name+len) == '-') | ||
504 | *(name+len) = '_'; | ||
505 | } | ||
506 | |||
507 | module = module__new(name, path); | ||
508 | if (!module) | ||
509 | break; | ||
510 | mod_dso__insert_module(self, module); | ||
511 | |||
512 | module->sections = sec_dso__new_dso("sections"); | ||
513 | if (!module->sections) | ||
514 | break; | ||
515 | |||
516 | module->active = mod_dso__load_sections(module); | ||
517 | |||
518 | if (module->active > 0) | ||
519 | count++; | ||
520 | } | ||
521 | |||
522 | if (feof(file)) | ||
523 | err = count; | ||
524 | else | ||
525 | fprintf(stderr, "load_module_paths: modules.dep parsing failure!\n"); | ||
526 | |||
527 | out_failure: | ||
528 | if (dpath) | ||
529 | free(dpath); | ||
530 | if (file) | ||
531 | fclose(file); | ||
532 | if (line) | ||
533 | free(line); | ||
534 | |||
535 | return err; | ||
536 | } | ||
537 | |||
538 | int mod_dso__load_modules(struct mod_dso *dso) | ||
539 | { | ||
540 | int err; | ||
541 | |||
542 | err = mod_dso__load_module_paths(dso); | ||
543 | |||
544 | return err; | ||
545 | } | ||
diff --git a/tools/perf/util/module.h b/tools/perf/util/module.h deleted file mode 100644 index 8a592ef641ca..000000000000 --- a/tools/perf/util/module.h +++ /dev/null | |||
@@ -1,53 +0,0 @@ | |||
1 | #ifndef _PERF_MODULE_ | ||
2 | #define _PERF_MODULE_ 1 | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include "../types.h" | ||
6 | #include <linux/list.h> | ||
7 | #include <linux/rbtree.h> | ||
8 | |||
9 | struct section { | ||
10 | struct rb_node rb_node; | ||
11 | u64 hash; | ||
12 | u64 vma; | ||
13 | char *name; | ||
14 | char *path; | ||
15 | }; | ||
16 | |||
17 | struct sec_dso { | ||
18 | struct list_head node; | ||
19 | struct rb_root secs; | ||
20 | struct section *(*find_section)(struct sec_dso *, const char *name); | ||
21 | char name[0]; | ||
22 | }; | ||
23 | |||
24 | struct module { | ||
25 | struct rb_node rb_node; | ||
26 | u64 hash; | ||
27 | char *name; | ||
28 | char *path; | ||
29 | struct sec_dso *sections; | ||
30 | int active; | ||
31 | }; | ||
32 | |||
33 | struct mod_dso { | ||
34 | struct list_head node; | ||
35 | struct rb_root mods; | ||
36 | struct module *(*find_module)(struct mod_dso *, const char *name); | ||
37 | char name[0]; | ||
38 | }; | ||
39 | |||
40 | struct sec_dso *sec_dso__new_dso(const char *name); | ||
41 | void sec_dso__delete_sections(struct sec_dso *self); | ||
42 | void sec_dso__delete_self(struct sec_dso *self); | ||
43 | size_t sec_dso__fprintf(struct sec_dso *self, FILE *fp); | ||
44 | struct section *sec_dso__find_section(struct sec_dso *self, const char *name); | ||
45 | |||
46 | struct mod_dso *mod_dso__new_dso(const char *name); | ||
47 | void mod_dso__delete_modules(struct mod_dso *self); | ||
48 | void mod_dso__delete_self(struct mod_dso *self); | ||
49 | size_t mod_dso__fprintf(struct mod_dso *self, FILE *fp); | ||
50 | struct module *mod_dso__find_module(struct mod_dso *self, const char *name); | ||
51 | int mod_dso__load_modules(struct mod_dso *dso); | ||
52 | |||
53 | #endif /* _PERF_MODULE_ */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 8cfb48cbbea0..05d0c5c2030c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1,4 +1,4 @@ | |||
1 | 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 "parse-options.h" | 4 | #include "parse-options.h" |
@@ -7,10 +7,12 @@ | |||
7 | #include "string.h" | 7 | #include "string.h" |
8 | #include "cache.h" | 8 | #include "cache.h" |
9 | #include "header.h" | 9 | #include "header.h" |
10 | #include "debugfs.h" | ||
10 | 11 | ||
11 | int nr_counters; | 12 | int nr_counters; |
12 | 13 | ||
13 | struct perf_event_attr attrs[MAX_COUNTERS]; | 14 | struct perf_event_attr attrs[MAX_COUNTERS]; |
15 | char *filters[MAX_COUNTERS]; | ||
14 | 16 | ||
15 | struct event_symbol { | 17 | struct event_symbol { |
16 | u8 type; | 18 | u8 type; |
@@ -46,6 +48,8 @@ static struct event_symbol event_symbols[] = { | |||
46 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, | 48 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, |
47 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, | 49 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, |
48 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, | 50 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, |
51 | { CSW(ALIGNMENT_FAULTS), "alignment-faults", "" }, | ||
52 | { CSW(EMULATION_FAULTS), "emulation-faults", "" }, | ||
49 | }; | 53 | }; |
50 | 54 | ||
51 | #define __PERF_EVENT_FIELD(config, name) \ | 55 | #define __PERF_EVENT_FIELD(config, name) \ |
@@ -74,6 +78,8 @@ static const char *sw_event_names[] = { | |||
74 | "CPU-migrations", | 78 | "CPU-migrations", |
75 | "minor-faults", | 79 | "minor-faults", |
76 | "major-faults", | 80 | "major-faults", |
81 | "alignment-faults", | ||
82 | "emulation-faults", | ||
77 | }; | 83 | }; |
78 | 84 | ||
79 | #define MAX_ALIASES 8 | 85 | #define MAX_ALIASES 8 |
@@ -148,16 +154,6 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | |||
148 | 154 | ||
149 | #define MAX_EVENT_LENGTH 512 | 155 | #define MAX_EVENT_LENGTH 512 |
150 | 156 | ||
151 | int valid_debugfs_mount(const char *debugfs) | ||
152 | { | ||
153 | struct statfs st_fs; | ||
154 | |||
155 | if (statfs(debugfs, &st_fs) < 0) | ||
156 | return -ENOENT; | ||
157 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
158 | return -ENOENT; | ||
159 | return 0; | ||
160 | } | ||
161 | 157 | ||
162 | struct tracepoint_path *tracepoint_id_to_path(u64 config) | 158 | struct tracepoint_path *tracepoint_id_to_path(u64 config) |
163 | { | 159 | { |
@@ -170,7 +166,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
170 | char evt_path[MAXPATHLEN]; | 166 | char evt_path[MAXPATHLEN]; |
171 | char dir_path[MAXPATHLEN]; | 167 | char dir_path[MAXPATHLEN]; |
172 | 168 | ||
173 | if (valid_debugfs_mount(debugfs_path)) | 169 | if (debugfs_valid_mountpoint(debugfs_path)) |
174 | return NULL; | 170 | return NULL; |
175 | 171 | ||
176 | sys_dir = opendir(debugfs_path); | 172 | sys_dir = opendir(debugfs_path); |
@@ -201,7 +197,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
201 | if (id == config) { | 197 | if (id == config) { |
202 | closedir(evt_dir); | 198 | closedir(evt_dir); |
203 | closedir(sys_dir); | 199 | closedir(sys_dir); |
204 | path = calloc(1, sizeof(path)); | 200 | path = zalloc(sizeof(*path)); |
205 | path->system = malloc(MAX_EVENT_LENGTH); | 201 | path->system = malloc(MAX_EVENT_LENGTH); |
206 | if (!path->system) { | 202 | if (!path->system) { |
207 | free(path); | 203 | free(path); |
@@ -454,7 +450,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
454 | /* sys + ':' + event + ':' + flags*/ | 450 | /* sys + ':' + event + ':' + flags*/ |
455 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 451 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
456 | static enum event_result | 452 | static enum event_result |
457 | parse_subsystem_tracepoint_event(char *sys_name, char *flags) | 453 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, |
454 | char *flags) | ||
458 | { | 455 | { |
459 | char evt_path[MAXPATHLEN]; | 456 | char evt_path[MAXPATHLEN]; |
460 | struct dirent *evt_ent; | 457 | struct dirent *evt_ent; |
@@ -471,7 +468,6 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
471 | while ((evt_ent = readdir(evt_dir))) { | 468 | while ((evt_ent = readdir(evt_dir))) { |
472 | char event_opt[MAX_EVOPT_LEN + 1]; | 469 | char event_opt[MAX_EVOPT_LEN + 1]; |
473 | int len; | 470 | int len; |
474 | unsigned int rem = MAX_EVOPT_LEN; | ||
475 | 471 | ||
476 | if (!strcmp(evt_ent->d_name, ".") | 472 | if (!strcmp(evt_ent->d_name, ".") |
477 | || !strcmp(evt_ent->d_name, "..") | 473 | || !strcmp(evt_ent->d_name, "..") |
@@ -479,20 +475,15 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
479 | || !strcmp(evt_ent->d_name, "filter")) | 475 | || !strcmp(evt_ent->d_name, "filter")) |
480 | continue; | 476 | continue; |
481 | 477 | ||
482 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name, | 478 | if (!strglobmatch(evt_ent->d_name, evt_exp)) |
483 | evt_ent->d_name); | 479 | continue; |
480 | |||
481 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | ||
482 | evt_ent->d_name, flags ? ":" : "", | ||
483 | flags ?: ""); | ||
484 | if (len < 0) | 484 | if (len < 0) |
485 | return EVT_FAILED; | 485 | return EVT_FAILED; |
486 | 486 | ||
487 | rem -= len; | ||
488 | if (flags) { | ||
489 | if (rem < strlen(flags) + 1) | ||
490 | return EVT_FAILED; | ||
491 | |||
492 | strcat(event_opt, ":"); | ||
493 | strcat(event_opt, flags); | ||
494 | } | ||
495 | |||
496 | if (parse_events(NULL, event_opt, 0)) | 487 | if (parse_events(NULL, event_opt, 0)) |
497 | return EVT_FAILED; | 488 | return EVT_FAILED; |
498 | } | 489 | } |
@@ -509,7 +500,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
509 | char sys_name[MAX_EVENT_LENGTH]; | 500 | char sys_name[MAX_EVENT_LENGTH]; |
510 | unsigned int sys_length, evt_length; | 501 | unsigned int sys_length, evt_length; |
511 | 502 | ||
512 | if (valid_debugfs_mount(debugfs_path)) | 503 | if (debugfs_valid_mountpoint(debugfs_path)) |
513 | return 0; | 504 | return 0; |
514 | 505 | ||
515 | evt_name = strchr(*strp, ':'); | 506 | evt_name = strchr(*strp, ':'); |
@@ -535,15 +526,91 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
535 | if (evt_length >= MAX_EVENT_LENGTH) | 526 | if (evt_length >= MAX_EVENT_LENGTH) |
536 | return EVT_FAILED; | 527 | return EVT_FAILED; |
537 | 528 | ||
538 | if (!strcmp(evt_name, "*")) { | 529 | if (strpbrk(evt_name, "*?")) { |
539 | *strp = evt_name + evt_length; | 530 | *strp = evt_name + evt_length; |
540 | return parse_subsystem_tracepoint_event(sys_name, flags); | 531 | return parse_multiple_tracepoint_event(sys_name, evt_name, |
532 | flags); | ||
541 | } else | 533 | } else |
542 | return parse_single_tracepoint_event(sys_name, evt_name, | 534 | return parse_single_tracepoint_event(sys_name, evt_name, |
543 | evt_length, flags, | 535 | evt_length, flags, |
544 | attr, strp); | 536 | attr, strp); |
545 | } | 537 | } |
546 | 538 | ||
539 | static enum event_result | ||
540 | parse_breakpoint_type(const char *type, const char **strp, | ||
541 | struct perf_event_attr *attr) | ||
542 | { | ||
543 | int i; | ||
544 | |||
545 | for (i = 0; i < 3; i++) { | ||
546 | if (!type[i]) | ||
547 | break; | ||
548 | |||
549 | switch (type[i]) { | ||
550 | case 'r': | ||
551 | attr->bp_type |= HW_BREAKPOINT_R; | ||
552 | break; | ||
553 | case 'w': | ||
554 | attr->bp_type |= HW_BREAKPOINT_W; | ||
555 | break; | ||
556 | case 'x': | ||
557 | attr->bp_type |= HW_BREAKPOINT_X; | ||
558 | break; | ||
559 | default: | ||
560 | return EVT_FAILED; | ||
561 | } | ||
562 | } | ||
563 | if (!attr->bp_type) /* Default */ | ||
564 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
565 | |||
566 | *strp = type + i; | ||
567 | |||
568 | return EVT_HANDLED; | ||
569 | } | ||
570 | |||
571 | static enum event_result | ||
572 | parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | ||
573 | { | ||
574 | const char *target; | ||
575 | const char *type; | ||
576 | char *endaddr; | ||
577 | u64 addr; | ||
578 | enum event_result err; | ||
579 | |||
580 | target = strchr(*strp, ':'); | ||
581 | if (!target) | ||
582 | return EVT_FAILED; | ||
583 | |||
584 | if (strncmp(*strp, "mem", target - *strp) != 0) | ||
585 | return EVT_FAILED; | ||
586 | |||
587 | target++; | ||
588 | |||
589 | addr = strtoull(target, &endaddr, 0); | ||
590 | if (target == endaddr) | ||
591 | return EVT_FAILED; | ||
592 | |||
593 | attr->bp_addr = addr; | ||
594 | *strp = endaddr; | ||
595 | |||
596 | type = strchr(target, ':'); | ||
597 | |||
598 | /* If no type is defined, just rw as default */ | ||
599 | if (!type) { | ||
600 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
601 | } else { | ||
602 | err = parse_breakpoint_type(++type, strp, attr); | ||
603 | if (err == EVT_FAILED) | ||
604 | return EVT_FAILED; | ||
605 | } | ||
606 | |||
607 | /* We should find a nice way to override the access type */ | ||
608 | attr->bp_len = HW_BREAKPOINT_LEN_4; | ||
609 | attr->type = PERF_TYPE_BREAKPOINT; | ||
610 | |||
611 | return EVT_HANDLED; | ||
612 | } | ||
613 | |||
547 | static int check_events(const char *str, unsigned int i) | 614 | static int check_events(const char *str, unsigned int i) |
548 | { | 615 | { |
549 | int n; | 616 | int n; |
@@ -677,6 +744,12 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr) | |||
677 | if (ret != EVT_FAILED) | 744 | if (ret != EVT_FAILED) |
678 | goto modifier; | 745 | goto modifier; |
679 | 746 | ||
747 | ret = parse_breakpoint_event(str, attr); | ||
748 | if (ret != EVT_FAILED) | ||
749 | goto modifier; | ||
750 | |||
751 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | ||
752 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
680 | return EVT_FAILED; | 753 | return EVT_FAILED; |
681 | 754 | ||
682 | modifier: | 755 | modifier: |
@@ -685,11 +758,11 @@ modifier: | |||
685 | return ret; | 758 | return ret; |
686 | } | 759 | } |
687 | 760 | ||
688 | static void store_event_type(const char *orgname) | 761 | static int store_event_type(const char *orgname) |
689 | { | 762 | { |
690 | char filename[PATH_MAX], *c; | 763 | char filename[PATH_MAX], *c; |
691 | FILE *file; | 764 | FILE *file; |
692 | int id; | 765 | int id, n; |
693 | 766 | ||
694 | sprintf(filename, "%s/", debugfs_path); | 767 | sprintf(filename, "%s/", debugfs_path); |
695 | strncat(filename, orgname, strlen(orgname)); | 768 | strncat(filename, orgname, strlen(orgname)); |
@@ -701,21 +774,24 @@ static void store_event_type(const char *orgname) | |||
701 | 774 | ||
702 | file = fopen(filename, "r"); | 775 | file = fopen(filename, "r"); |
703 | if (!file) | 776 | if (!file) |
704 | return; | 777 | return 0; |
705 | if (fscanf(file, "%i", &id) < 1) | 778 | n = fscanf(file, "%i", &id); |
706 | die("cannot store event ID"); | ||
707 | fclose(file); | 779 | fclose(file); |
708 | perf_header__push_event(id, orgname); | 780 | if (n < 1) { |
781 | pr_err("cannot store event ID\n"); | ||
782 | return -EINVAL; | ||
783 | } | ||
784 | return perf_header__push_event(id, orgname); | ||
709 | } | 785 | } |
710 | 786 | ||
711 | |||
712 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 787 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
713 | { | 788 | { |
714 | struct perf_event_attr attr; | 789 | struct perf_event_attr attr; |
715 | enum event_result ret; | 790 | enum event_result ret; |
716 | 791 | ||
717 | if (strchr(str, ':')) | 792 | if (strchr(str, ':')) |
718 | store_event_type(str); | 793 | if (store_event_type(str) < 0) |
794 | return -1; | ||
719 | 795 | ||
720 | for (;;) { | 796 | for (;;) { |
721 | if (nr_counters == MAX_COUNTERS) | 797 | if (nr_counters == MAX_COUNTERS) |
@@ -745,12 +821,35 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
745 | return 0; | 821 | return 0; |
746 | } | 822 | } |
747 | 823 | ||
824 | int parse_filter(const struct option *opt __used, const char *str, | ||
825 | int unset __used) | ||
826 | { | ||
827 | int i = nr_counters - 1; | ||
828 | int len = strlen(str); | ||
829 | |||
830 | if (i < 0 || attrs[i].type != PERF_TYPE_TRACEPOINT) { | ||
831 | fprintf(stderr, | ||
832 | "-F option should follow a -e tracepoint option\n"); | ||
833 | return -1; | ||
834 | } | ||
835 | |||
836 | filters[i] = malloc(len + 1); | ||
837 | if (!filters[i]) { | ||
838 | fprintf(stderr, "not enough memory to hold filter string\n"); | ||
839 | return -1; | ||
840 | } | ||
841 | strcpy(filters[i], str); | ||
842 | |||
843 | return 0; | ||
844 | } | ||
845 | |||
748 | static const char * const event_type_descriptors[] = { | 846 | static const char * const event_type_descriptors[] = { |
749 | "", | ||
750 | "Hardware event", | 847 | "Hardware event", |
751 | "Software event", | 848 | "Software event", |
752 | "Tracepoint event", | 849 | "Tracepoint event", |
753 | "Hardware cache event", | 850 | "Hardware cache event", |
851 | "Raw hardware event descriptor", | ||
852 | "Hardware breakpoint", | ||
754 | }; | 853 | }; |
755 | 854 | ||
756 | /* | 855 | /* |
@@ -764,7 +863,7 @@ static void print_tracepoint_events(void) | |||
764 | char evt_path[MAXPATHLEN]; | 863 | char evt_path[MAXPATHLEN]; |
765 | char dir_path[MAXPATHLEN]; | 864 | char dir_path[MAXPATHLEN]; |
766 | 865 | ||
767 | if (valid_debugfs_mount(debugfs_path)) | 866 | if (debugfs_valid_mountpoint(debugfs_path)) |
768 | return; | 867 | return; |
769 | 868 | ||
770 | sys_dir = opendir(debugfs_path); | 869 | sys_dir = opendir(debugfs_path); |
@@ -782,8 +881,8 @@ static void print_tracepoint_events(void) | |||
782 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | 881 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { |
783 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 882 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
784 | sys_dirent.d_name, evt_dirent.d_name); | 883 | sys_dirent.d_name, evt_dirent.d_name); |
785 | fprintf(stderr, " %-42s [%s]\n", evt_path, | 884 | printf(" %-42s [%s]\n", evt_path, |
786 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | 885 | event_type_descriptors[PERF_TYPE_TRACEPOINT]); |
787 | } | 886 | } |
788 | closedir(evt_dir); | 887 | closedir(evt_dir); |
789 | } | 888 | } |
@@ -799,28 +898,26 @@ void print_events(void) | |||
799 | unsigned int i, type, op, prev_type = -1; | 898 | unsigned int i, type, op, prev_type = -1; |
800 | char name[40]; | 899 | char name[40]; |
801 | 900 | ||
802 | fprintf(stderr, "\n"); | 901 | printf("\n"); |
803 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | 902 | printf("List of pre-defined events (to be used in -e):\n"); |
804 | 903 | ||
805 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 904 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
806 | type = syms->type + 1; | 905 | type = syms->type; |
807 | if (type >= ARRAY_SIZE(event_type_descriptors)) | ||
808 | type = 0; | ||
809 | 906 | ||
810 | if (type != prev_type) | 907 | if (type != prev_type) |
811 | fprintf(stderr, "\n"); | 908 | printf("\n"); |
812 | 909 | ||
813 | if (strlen(syms->alias)) | 910 | if (strlen(syms->alias)) |
814 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | 911 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); |
815 | else | 912 | else |
816 | strcpy(name, syms->symbol); | 913 | strcpy(name, syms->symbol); |
817 | fprintf(stderr, " %-42s [%s]\n", name, | 914 | printf(" %-42s [%s]\n", name, |
818 | event_type_descriptors[type]); | 915 | event_type_descriptors[type]); |
819 | 916 | ||
820 | prev_type = type; | 917 | prev_type = type; |
821 | } | 918 | } |
822 | 919 | ||
823 | fprintf(stderr, "\n"); | 920 | printf("\n"); |
824 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | 921 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { |
825 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | 922 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { |
826 | /* skip invalid cache type */ | 923 | /* skip invalid cache type */ |
@@ -828,17 +925,22 @@ void print_events(void) | |||
828 | continue; | 925 | continue; |
829 | 926 | ||
830 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 927 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
831 | fprintf(stderr, " %-42s [%s]\n", | 928 | printf(" %-42s [%s]\n", |
832 | event_cache_name(type, op, i), | 929 | event_cache_name(type, op, i), |
833 | event_type_descriptors[4]); | 930 | event_type_descriptors[PERF_TYPE_HW_CACHE]); |
834 | } | 931 | } |
835 | } | 932 | } |
836 | } | 933 | } |
837 | 934 | ||
838 | fprintf(stderr, "\n"); | 935 | printf("\n"); |
839 | fprintf(stderr, " %-42s [raw hardware event descriptor]\n", | 936 | printf(" %-42s [%s]\n", |
840 | "rNNN"); | 937 | "rNNN", event_type_descriptors[PERF_TYPE_RAW]); |
841 | fprintf(stderr, "\n"); | 938 | printf("\n"); |
939 | |||
940 | printf(" %-42s [%s]\n", | ||
941 | "mem:<addr>[:access]", | ||
942 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | ||
943 | printf("\n"); | ||
842 | 944 | ||
843 | print_tracepoint_events(); | 945 | print_tracepoint_events(); |
844 | 946 | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 30c608112845..b8c1f64bc935 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _PARSE_EVENTS_H | 1 | #ifndef __PERF_PARSE_EVENTS_H |
2 | #define _PARSE_EVENTS_H | 2 | #define __PERF_PARSE_EVENTS_H |
3 | /* | 3 | /* |
4 | * Parse symbolic events/counts passed in as options: | 4 | * Parse symbolic events/counts passed in as options: |
5 | */ | 5 | */ |
@@ -17,11 +17,13 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | |||
17 | extern int nr_counters; | 17 | extern int nr_counters; |
18 | 18 | ||
19 | extern struct perf_event_attr attrs[MAX_COUNTERS]; | 19 | extern struct perf_event_attr attrs[MAX_COUNTERS]; |
20 | extern char *filters[MAX_COUNTERS]; | ||
20 | 21 | ||
21 | extern const char *event_name(int ctr); | 22 | extern const char *event_name(int ctr); |
22 | extern const char *__event_name(int type, u64 config); | 23 | extern const char *__event_name(int type, u64 config); |
23 | 24 | ||
24 | extern int parse_events(const struct option *opt, const char *str, int unset); | 25 | extern int parse_events(const struct option *opt, const char *str, int unset); |
26 | extern int parse_filter(const struct option *opt, const char *str, int unset); | ||
25 | 27 | ||
26 | #define EVENTS_HELP_MAX (128*1024) | 28 | #define EVENTS_HELP_MAX (128*1024) |
27 | 29 | ||
@@ -31,4 +33,4 @@ extern char debugfs_path[]; | |||
31 | extern int valid_debugfs_mount(const char *debugfs); | 33 | extern int valid_debugfs_mount(const char *debugfs); |
32 | 34 | ||
33 | 35 | ||
34 | #endif /* _PARSE_EVENTS_H */ | 36 | #endif /* __PERF_PARSE_EVENTS_H */ |
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 6d8af48c925e..efebd5b476b3 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
@@ -430,6 +430,9 @@ int usage_with_options_internal(const char * const *usagestr, | |||
430 | pos = fprintf(stderr, " "); | 430 | pos = fprintf(stderr, " "); |
431 | if (opts->short_name) | 431 | if (opts->short_name) |
432 | pos += fprintf(stderr, "-%c", opts->short_name); | 432 | pos += fprintf(stderr, "-%c", opts->short_name); |
433 | else | ||
434 | pos += fprintf(stderr, " "); | ||
435 | |||
433 | if (opts->long_name && opts->short_name) | 436 | if (opts->long_name && opts->short_name) |
434 | pos += fprintf(stderr, ", "); | 437 | pos += fprintf(stderr, ", "); |
435 | if (opts->long_name) | 438 | if (opts->long_name) |
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index 2ee248ff27e5..948805af43c2 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef PARSE_OPTIONS_H | 1 | #ifndef __PERF_PARSE_OPTIONS_H |
2 | #define PARSE_OPTIONS_H | 2 | #define __PERF_PARSE_OPTIONS_H |
3 | 3 | ||
4 | enum parse_opt_type { | 4 | enum parse_opt_type { |
5 | /* special types */ | 5 | /* special types */ |
@@ -174,4 +174,4 @@ extern int parse_opt_verbosity_cb(const struct option *, const char *, int); | |||
174 | 174 | ||
175 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); | 175 | extern const char *parse_options_fix_filename(const char *prefix, const char *file); |
176 | 176 | ||
177 | #endif | 177 | #endif /* __PERF_PARSE_OPTIONS_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c new file mode 100644 index 000000000000..53181dbfe4a8 --- /dev/null +++ b/tools/perf/util/probe-event.c | |||
@@ -0,0 +1,802 @@ | |||
1 | /* | ||
2 | * probe-event.c : perf-probe definition to kprobe_events format converter | ||
3 | * | ||
4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #define _GNU_SOURCE | ||
23 | #include <sys/utsname.h> | ||
24 | #include <sys/types.h> | ||
25 | #include <sys/stat.h> | ||
26 | #include <fcntl.h> | ||
27 | #include <errno.h> | ||
28 | #include <stdio.h> | ||
29 | #include <unistd.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #include <stdarg.h> | ||
33 | #include <limits.h> | ||
34 | |||
35 | #undef _GNU_SOURCE | ||
36 | #include "event.h" | ||
37 | #include "string.h" | ||
38 | #include "strlist.h" | ||
39 | #include "debug.h" | ||
40 | #include "cache.h" | ||
41 | #include "color.h" | ||
42 | #include "parse-events.h" /* For debugfs_path */ | ||
43 | #include "probe-event.h" | ||
44 | |||
45 | #define MAX_CMDLEN 256 | ||
46 | #define MAX_PROBE_ARGS 128 | ||
47 | #define PERFPROBE_GROUP "probe" | ||
48 | |||
49 | #define semantic_error(msg ...) die("Semantic error :" msg) | ||
50 | |||
51 | /* If there is no space to write, returns -E2BIG. */ | ||
52 | static int e_snprintf(char *str, size_t size, const char *format, ...) | ||
53 | __attribute__((format(printf, 3, 4))); | ||
54 | |||
55 | static int e_snprintf(char *str, size_t size, const char *format, ...) | ||
56 | { | ||
57 | int ret; | ||
58 | va_list ap; | ||
59 | va_start(ap, format); | ||
60 | ret = vsnprintf(str, size, format, ap); | ||
61 | va_end(ap); | ||
62 | if (ret >= (int)size) | ||
63 | ret = -E2BIG; | ||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | void parse_line_range_desc(const char *arg, struct line_range *lr) | ||
68 | { | ||
69 | const char *ptr; | ||
70 | char *tmp; | ||
71 | /* | ||
72 | * <Syntax> | ||
73 | * SRC:SLN[+NUM|-ELN] | ||
74 | * FUNC[:SLN[+NUM|-ELN]] | ||
75 | */ | ||
76 | ptr = strchr(arg, ':'); | ||
77 | if (ptr) { | ||
78 | lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); | ||
79 | if (*tmp == '+') | ||
80 | lr->end = lr->start + (unsigned int)strtoul(tmp + 1, | ||
81 | &tmp, 0); | ||
82 | else if (*tmp == '-') | ||
83 | lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); | ||
84 | else | ||
85 | lr->end = 0; | ||
86 | pr_debug("Line range is %u to %u\n", lr->start, lr->end); | ||
87 | if (lr->end && lr->start > lr->end) | ||
88 | semantic_error("Start line must be smaller" | ||
89 | " than end line."); | ||
90 | if (*tmp != '\0') | ||
91 | semantic_error("Tailing with invalid character '%d'.", | ||
92 | *tmp); | ||
93 | tmp = strndup(arg, (ptr - arg)); | ||
94 | } else | ||
95 | tmp = strdup(arg); | ||
96 | |||
97 | if (strchr(tmp, '.')) | ||
98 | lr->file = tmp; | ||
99 | else | ||
100 | lr->function = tmp; | ||
101 | } | ||
102 | |||
103 | /* Check the name is good for event/group */ | ||
104 | static bool check_event_name(const char *name) | ||
105 | { | ||
106 | if (!isalpha(*name) && *name != '_') | ||
107 | return false; | ||
108 | while (*++name != '\0') { | ||
109 | if (!isalpha(*name) && !isdigit(*name) && *name != '_') | ||
110 | return false; | ||
111 | } | ||
112 | return true; | ||
113 | } | ||
114 | |||
115 | /* Parse probepoint definition. */ | ||
116 | static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | ||
117 | { | ||
118 | char *ptr, *tmp; | ||
119 | char c, nc = 0; | ||
120 | /* | ||
121 | * <Syntax> | ||
122 | * perf probe [EVENT=]SRC[:LN|;PTN] | ||
123 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] | ||
124 | * | ||
125 | * TODO:Group name support | ||
126 | */ | ||
127 | |||
128 | ptr = strpbrk(arg, ";=@+%"); | ||
129 | if (ptr && *ptr == '=') { /* Event name */ | ||
130 | *ptr = '\0'; | ||
131 | tmp = ptr + 1; | ||
132 | ptr = strchr(arg, ':'); | ||
133 | if (ptr) /* Group name is not supported yet. */ | ||
134 | semantic_error("Group name is not supported yet."); | ||
135 | if (!check_event_name(arg)) | ||
136 | semantic_error("%s is bad for event name -it must " | ||
137 | "follow C symbol-naming rule.", arg); | ||
138 | pp->event = strdup(arg); | ||
139 | arg = tmp; | ||
140 | } | ||
141 | |||
142 | ptr = strpbrk(arg, ";:+@%"); | ||
143 | if (ptr) { | ||
144 | nc = *ptr; | ||
145 | *ptr++ = '\0'; | ||
146 | } | ||
147 | |||
148 | /* Check arg is function or file and copy it */ | ||
149 | if (strchr(arg, '.')) /* File */ | ||
150 | pp->file = strdup(arg); | ||
151 | else /* Function */ | ||
152 | pp->function = strdup(arg); | ||
153 | DIE_IF(pp->file == NULL && pp->function == NULL); | ||
154 | |||
155 | /* Parse other options */ | ||
156 | while (ptr) { | ||
157 | arg = ptr; | ||
158 | c = nc; | ||
159 | if (c == ';') { /* Lazy pattern must be the last part */ | ||
160 | pp->lazy_line = strdup(arg); | ||
161 | break; | ||
162 | } | ||
163 | ptr = strpbrk(arg, ";:+@%"); | ||
164 | if (ptr) { | ||
165 | nc = *ptr; | ||
166 | *ptr++ = '\0'; | ||
167 | } | ||
168 | switch (c) { | ||
169 | case ':': /* Line number */ | ||
170 | pp->line = strtoul(arg, &tmp, 0); | ||
171 | if (*tmp != '\0') | ||
172 | semantic_error("There is non-digit char" | ||
173 | " in line number."); | ||
174 | break; | ||
175 | case '+': /* Byte offset from a symbol */ | ||
176 | pp->offset = strtoul(arg, &tmp, 0); | ||
177 | if (*tmp != '\0') | ||
178 | semantic_error("There is non-digit character" | ||
179 | " in offset."); | ||
180 | break; | ||
181 | case '@': /* File name */ | ||
182 | if (pp->file) | ||
183 | semantic_error("SRC@SRC is not allowed."); | ||
184 | pp->file = strdup(arg); | ||
185 | DIE_IF(pp->file == NULL); | ||
186 | break; | ||
187 | case '%': /* Probe places */ | ||
188 | if (strcmp(arg, "return") == 0) { | ||
189 | pp->retprobe = 1; | ||
190 | } else /* Others not supported yet */ | ||
191 | semantic_error("%%%s is not supported.", arg); | ||
192 | break; | ||
193 | default: | ||
194 | DIE_IF("Program has a bug."); | ||
195 | break; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | /* Exclusion check */ | ||
200 | if (pp->lazy_line && pp->line) | ||
201 | semantic_error("Lazy pattern can't be used with line number."); | ||
202 | |||
203 | if (pp->lazy_line && pp->offset) | ||
204 | semantic_error("Lazy pattern can't be used with offset."); | ||
205 | |||
206 | if (pp->line && pp->offset) | ||
207 | semantic_error("Offset can't be used with line number."); | ||
208 | |||
209 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) | ||
210 | semantic_error("File always requires line number or " | ||
211 | "lazy pattern."); | ||
212 | |||
213 | if (pp->offset && !pp->function) | ||
214 | semantic_error("Offset requires an entry function."); | ||
215 | |||
216 | if (pp->retprobe && !pp->function) | ||
217 | semantic_error("Return probe requires an entry function."); | ||
218 | |||
219 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) | ||
220 | semantic_error("Offset/Line/Lazy pattern can't be used with " | ||
221 | "return probe."); | ||
222 | |||
223 | pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", | ||
224 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, | ||
225 | pp->lazy_line); | ||
226 | } | ||
227 | |||
228 | /* Parse perf-probe event definition */ | ||
229 | void parse_perf_probe_event(const char *str, struct probe_point *pp, | ||
230 | bool *need_dwarf) | ||
231 | { | ||
232 | char **argv; | ||
233 | int argc, i; | ||
234 | |||
235 | *need_dwarf = false; | ||
236 | |||
237 | argv = argv_split(str, &argc); | ||
238 | if (!argv) | ||
239 | die("argv_split failed."); | ||
240 | if (argc > MAX_PROBE_ARGS + 1) | ||
241 | semantic_error("Too many arguments"); | ||
242 | |||
243 | /* Parse probe point */ | ||
244 | parse_perf_probe_probepoint(argv[0], pp); | ||
245 | if (pp->file || pp->line) | ||
246 | *need_dwarf = true; | ||
247 | |||
248 | /* Copy arguments and ensure return probe has no C argument */ | ||
249 | pp->nr_args = argc - 1; | ||
250 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | ||
251 | for (i = 0; i < pp->nr_args; i++) { | ||
252 | pp->args[i] = strdup(argv[i + 1]); | ||
253 | if (!pp->args[i]) | ||
254 | die("Failed to copy argument."); | ||
255 | if (is_c_varname(pp->args[i])) { | ||
256 | if (pp->retprobe) | ||
257 | semantic_error("You can't specify local" | ||
258 | " variable for kretprobe"); | ||
259 | *need_dwarf = true; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | argv_free(argv); | ||
264 | } | ||
265 | |||
266 | /* Parse kprobe_events event into struct probe_point */ | ||
267 | void parse_trace_kprobe_event(const char *str, struct probe_point *pp) | ||
268 | { | ||
269 | char pr; | ||
270 | char *p; | ||
271 | int ret, i, argc; | ||
272 | char **argv; | ||
273 | |||
274 | pr_debug("Parsing kprobe_events: %s\n", str); | ||
275 | argv = argv_split(str, &argc); | ||
276 | if (!argv) | ||
277 | die("argv_split failed."); | ||
278 | if (argc < 2) | ||
279 | semantic_error("Too less arguments."); | ||
280 | |||
281 | /* Scan event and group name. */ | ||
282 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", | ||
283 | &pr, (float *)(void *)&pp->group, | ||
284 | (float *)(void *)&pp->event); | ||
285 | if (ret != 3) | ||
286 | semantic_error("Failed to parse event name: %s", argv[0]); | ||
287 | pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); | ||
288 | |||
289 | pp->retprobe = (pr == 'r'); | ||
290 | |||
291 | /* Scan function name and offset */ | ||
292 | ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, | ||
293 | &pp->offset); | ||
294 | if (ret == 1) | ||
295 | pp->offset = 0; | ||
296 | |||
297 | /* kprobe_events doesn't have this information */ | ||
298 | pp->line = 0; | ||
299 | pp->file = NULL; | ||
300 | |||
301 | pp->nr_args = argc - 2; | ||
302 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | ||
303 | for (i = 0; i < pp->nr_args; i++) { | ||
304 | p = strchr(argv[i + 2], '='); | ||
305 | if (p) /* We don't need which register is assigned. */ | ||
306 | *p = '\0'; | ||
307 | pp->args[i] = strdup(argv[i + 2]); | ||
308 | if (!pp->args[i]) | ||
309 | die("Failed to copy argument."); | ||
310 | } | ||
311 | |||
312 | argv_free(argv); | ||
313 | } | ||
314 | |||
315 | /* Synthesize only probe point (not argument) */ | ||
316 | int synthesize_perf_probe_point(struct probe_point *pp) | ||
317 | { | ||
318 | char *buf; | ||
319 | char offs[64] = "", line[64] = ""; | ||
320 | int ret; | ||
321 | |||
322 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | ||
323 | pp->found = 1; | ||
324 | if (!buf) | ||
325 | die("Failed to allocate memory by zalloc."); | ||
326 | if (pp->offset) { | ||
327 | ret = e_snprintf(offs, 64, "+%d", pp->offset); | ||
328 | if (ret <= 0) | ||
329 | goto error; | ||
330 | } | ||
331 | if (pp->line) { | ||
332 | ret = e_snprintf(line, 64, ":%d", pp->line); | ||
333 | if (ret <= 0) | ||
334 | goto error; | ||
335 | } | ||
336 | |||
337 | if (pp->function) | ||
338 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, | ||
339 | offs, pp->retprobe ? "%return" : "", line); | ||
340 | else | ||
341 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); | ||
342 | if (ret <= 0) { | ||
343 | error: | ||
344 | free(pp->probes[0]); | ||
345 | pp->probes[0] = NULL; | ||
346 | pp->found = 0; | ||
347 | } | ||
348 | return ret; | ||
349 | } | ||
350 | |||
351 | int synthesize_perf_probe_event(struct probe_point *pp) | ||
352 | { | ||
353 | char *buf; | ||
354 | int i, len, ret; | ||
355 | |||
356 | len = synthesize_perf_probe_point(pp); | ||
357 | if (len < 0) | ||
358 | return 0; | ||
359 | |||
360 | buf = pp->probes[0]; | ||
361 | for (i = 0; i < pp->nr_args; i++) { | ||
362 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | ||
363 | pp->args[i]); | ||
364 | if (ret <= 0) | ||
365 | goto error; | ||
366 | len += ret; | ||
367 | } | ||
368 | pp->found = 1; | ||
369 | |||
370 | return pp->found; | ||
371 | error: | ||
372 | free(pp->probes[0]); | ||
373 | pp->probes[0] = NULL; | ||
374 | |||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | int synthesize_trace_kprobe_event(struct probe_point *pp) | ||
379 | { | ||
380 | char *buf; | ||
381 | int i, len, ret; | ||
382 | |||
383 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | ||
384 | if (!buf) | ||
385 | die("Failed to allocate memory by zalloc."); | ||
386 | ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); | ||
387 | if (ret <= 0) | ||
388 | goto error; | ||
389 | len = ret; | ||
390 | |||
391 | for (i = 0; i < pp->nr_args; i++) { | ||
392 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | ||
393 | pp->args[i]); | ||
394 | if (ret <= 0) | ||
395 | goto error; | ||
396 | len += ret; | ||
397 | } | ||
398 | pp->found = 1; | ||
399 | |||
400 | return pp->found; | ||
401 | error: | ||
402 | free(pp->probes[0]); | ||
403 | pp->probes[0] = NULL; | ||
404 | |||
405 | return ret; | ||
406 | } | ||
407 | |||
408 | static int open_kprobe_events(int flags, int mode) | ||
409 | { | ||
410 | char buf[PATH_MAX]; | ||
411 | int ret; | ||
412 | |||
413 | ret = e_snprintf(buf, PATH_MAX, "%s/../kprobe_events", debugfs_path); | ||
414 | if (ret < 0) | ||
415 | die("Failed to make kprobe_events path."); | ||
416 | |||
417 | ret = open(buf, flags, mode); | ||
418 | if (ret < 0) { | ||
419 | if (errno == ENOENT) | ||
420 | die("kprobe_events file does not exist -" | ||
421 | " please rebuild with CONFIG_KPROBE_EVENT."); | ||
422 | else | ||
423 | die("Could not open kprobe_events file: %s", | ||
424 | strerror(errno)); | ||
425 | } | ||
426 | return ret; | ||
427 | } | ||
428 | |||
429 | /* Get raw string list of current kprobe_events */ | ||
430 | static struct strlist *get_trace_kprobe_event_rawlist(int fd) | ||
431 | { | ||
432 | int ret, idx; | ||
433 | FILE *fp; | ||
434 | char buf[MAX_CMDLEN]; | ||
435 | char *p; | ||
436 | struct strlist *sl; | ||
437 | |||
438 | sl = strlist__new(true, NULL); | ||
439 | |||
440 | fp = fdopen(dup(fd), "r"); | ||
441 | while (!feof(fp)) { | ||
442 | p = fgets(buf, MAX_CMDLEN, fp); | ||
443 | if (!p) | ||
444 | break; | ||
445 | |||
446 | idx = strlen(p) - 1; | ||
447 | if (p[idx] == '\n') | ||
448 | p[idx] = '\0'; | ||
449 | ret = strlist__add(sl, buf); | ||
450 | if (ret < 0) | ||
451 | die("strlist__add failed: %s", strerror(-ret)); | ||
452 | } | ||
453 | fclose(fp); | ||
454 | |||
455 | return sl; | ||
456 | } | ||
457 | |||
458 | /* Free and zero clear probe_point */ | ||
459 | static void clear_probe_point(struct probe_point *pp) | ||
460 | { | ||
461 | int i; | ||
462 | |||
463 | if (pp->event) | ||
464 | free(pp->event); | ||
465 | if (pp->group) | ||
466 | free(pp->group); | ||
467 | if (pp->function) | ||
468 | free(pp->function); | ||
469 | if (pp->file) | ||
470 | free(pp->file); | ||
471 | if (pp->lazy_line) | ||
472 | free(pp->lazy_line); | ||
473 | for (i = 0; i < pp->nr_args; i++) | ||
474 | free(pp->args[i]); | ||
475 | if (pp->args) | ||
476 | free(pp->args); | ||
477 | for (i = 0; i < pp->found; i++) | ||
478 | free(pp->probes[i]); | ||
479 | memset(pp, 0, sizeof(*pp)); | ||
480 | } | ||
481 | |||
482 | /* Show an event */ | ||
483 | static void show_perf_probe_event(const char *event, const char *place, | ||
484 | struct probe_point *pp) | ||
485 | { | ||
486 | int i, ret; | ||
487 | char buf[128]; | ||
488 | |||
489 | ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); | ||
490 | if (ret < 0) | ||
491 | die("Failed to copy event: %s", strerror(-ret)); | ||
492 | printf(" %-40s (on %s", buf, place); | ||
493 | |||
494 | if (pp->nr_args > 0) { | ||
495 | printf(" with"); | ||
496 | for (i = 0; i < pp->nr_args; i++) | ||
497 | printf(" %s", pp->args[i]); | ||
498 | } | ||
499 | printf(")\n"); | ||
500 | } | ||
501 | |||
502 | /* List up current perf-probe events */ | ||
503 | void show_perf_probe_events(void) | ||
504 | { | ||
505 | int fd; | ||
506 | struct probe_point pp; | ||
507 | struct strlist *rawlist; | ||
508 | struct str_node *ent; | ||
509 | |||
510 | setup_pager(); | ||
511 | memset(&pp, 0, sizeof(pp)); | ||
512 | |||
513 | fd = open_kprobe_events(O_RDONLY, 0); | ||
514 | rawlist = get_trace_kprobe_event_rawlist(fd); | ||
515 | close(fd); | ||
516 | |||
517 | strlist__for_each(ent, rawlist) { | ||
518 | parse_trace_kprobe_event(ent->s, &pp); | ||
519 | /* Synthesize only event probe point */ | ||
520 | synthesize_perf_probe_point(&pp); | ||
521 | /* Show an event */ | ||
522 | show_perf_probe_event(pp.event, pp.probes[0], &pp); | ||
523 | clear_probe_point(&pp); | ||
524 | } | ||
525 | |||
526 | strlist__delete(rawlist); | ||
527 | } | ||
528 | |||
529 | /* Get current perf-probe event names */ | ||
530 | static struct strlist *get_perf_event_names(int fd, bool include_group) | ||
531 | { | ||
532 | char buf[128]; | ||
533 | struct strlist *sl, *rawlist; | ||
534 | struct str_node *ent; | ||
535 | struct probe_point pp; | ||
536 | |||
537 | memset(&pp, 0, sizeof(pp)); | ||
538 | rawlist = get_trace_kprobe_event_rawlist(fd); | ||
539 | |||
540 | sl = strlist__new(true, NULL); | ||
541 | strlist__for_each(ent, rawlist) { | ||
542 | parse_trace_kprobe_event(ent->s, &pp); | ||
543 | if (include_group) { | ||
544 | if (e_snprintf(buf, 128, "%s:%s", pp.group, | ||
545 | pp.event) < 0) | ||
546 | die("Failed to copy group:event name."); | ||
547 | strlist__add(sl, buf); | ||
548 | } else | ||
549 | strlist__add(sl, pp.event); | ||
550 | clear_probe_point(&pp); | ||
551 | } | ||
552 | |||
553 | strlist__delete(rawlist); | ||
554 | |||
555 | return sl; | ||
556 | } | ||
557 | |||
558 | static void write_trace_kprobe_event(int fd, const char *buf) | ||
559 | { | ||
560 | int ret; | ||
561 | |||
562 | pr_debug("Writing event: %s\n", buf); | ||
563 | ret = write(fd, buf, strlen(buf)); | ||
564 | if (ret <= 0) | ||
565 | die("Failed to write event: %s", strerror(errno)); | ||
566 | } | ||
567 | |||
568 | static void get_new_event_name(char *buf, size_t len, const char *base, | ||
569 | struct strlist *namelist, bool allow_suffix) | ||
570 | { | ||
571 | int i, ret; | ||
572 | |||
573 | /* Try no suffix */ | ||
574 | ret = e_snprintf(buf, len, "%s", base); | ||
575 | if (ret < 0) | ||
576 | die("snprintf() failed: %s", strerror(-ret)); | ||
577 | if (!strlist__has_entry(namelist, buf)) | ||
578 | return; | ||
579 | |||
580 | if (!allow_suffix) { | ||
581 | pr_warning("Error: event \"%s\" already exists. " | ||
582 | "(Use -f to force duplicates.)\n", base); | ||
583 | die("Can't add new event."); | ||
584 | } | ||
585 | |||
586 | /* Try to add suffix */ | ||
587 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | ||
588 | ret = e_snprintf(buf, len, "%s_%d", base, i); | ||
589 | if (ret < 0) | ||
590 | die("snprintf() failed: %s", strerror(-ret)); | ||
591 | if (!strlist__has_entry(namelist, buf)) | ||
592 | break; | ||
593 | } | ||
594 | if (i == MAX_EVENT_INDEX) | ||
595 | die("Too many events are on the same function."); | ||
596 | } | ||
597 | |||
598 | void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | ||
599 | bool force_add) | ||
600 | { | ||
601 | int i, j, fd; | ||
602 | struct probe_point *pp; | ||
603 | char buf[MAX_CMDLEN]; | ||
604 | char event[64]; | ||
605 | struct strlist *namelist; | ||
606 | bool allow_suffix; | ||
607 | |||
608 | fd = open_kprobe_events(O_RDWR, O_APPEND); | ||
609 | /* Get current event names */ | ||
610 | namelist = get_perf_event_names(fd, false); | ||
611 | |||
612 | for (j = 0; j < nr_probes; j++) { | ||
613 | pp = probes + j; | ||
614 | if (!pp->event) | ||
615 | pp->event = strdup(pp->function); | ||
616 | if (!pp->group) | ||
617 | pp->group = strdup(PERFPROBE_GROUP); | ||
618 | DIE_IF(!pp->event || !pp->group); | ||
619 | /* If force_add is true, suffix search is allowed */ | ||
620 | allow_suffix = force_add; | ||
621 | for (i = 0; i < pp->found; i++) { | ||
622 | /* Get an unused new event name */ | ||
623 | get_new_event_name(event, 64, pp->event, namelist, | ||
624 | allow_suffix); | ||
625 | snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", | ||
626 | pp->retprobe ? 'r' : 'p', | ||
627 | pp->group, event, | ||
628 | pp->probes[i]); | ||
629 | write_trace_kprobe_event(fd, buf); | ||
630 | printf("Added new event:\n"); | ||
631 | /* Get the first parameter (probe-point) */ | ||
632 | sscanf(pp->probes[i], "%s", buf); | ||
633 | show_perf_probe_event(event, buf, pp); | ||
634 | /* Add added event name to namelist */ | ||
635 | strlist__add(namelist, event); | ||
636 | /* | ||
637 | * Probes after the first probe which comes from same | ||
638 | * user input are always allowed to add suffix, because | ||
639 | * there might be several addresses corresponding to | ||
640 | * one code line. | ||
641 | */ | ||
642 | allow_suffix = true; | ||
643 | } | ||
644 | } | ||
645 | /* Show how to use the event. */ | ||
646 | printf("\nYou can now use it on all perf tools, such as:\n\n"); | ||
647 | printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); | ||
648 | |||
649 | strlist__delete(namelist); | ||
650 | close(fd); | ||
651 | } | ||
652 | |||
653 | static void __del_trace_kprobe_event(int fd, struct str_node *ent) | ||
654 | { | ||
655 | char *p; | ||
656 | char buf[128]; | ||
657 | |||
658 | /* Convert from perf-probe event to trace-kprobe event */ | ||
659 | if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) | ||
660 | die("Failed to copy event."); | ||
661 | p = strchr(buf + 2, ':'); | ||
662 | if (!p) | ||
663 | die("Internal error: %s should have ':' but not.", ent->s); | ||
664 | *p = '/'; | ||
665 | |||
666 | write_trace_kprobe_event(fd, buf); | ||
667 | printf("Remove event: %s\n", ent->s); | ||
668 | } | ||
669 | |||
670 | static void del_trace_kprobe_event(int fd, const char *group, | ||
671 | const char *event, struct strlist *namelist) | ||
672 | { | ||
673 | char buf[128]; | ||
674 | struct str_node *ent, *n; | ||
675 | int found = 0; | ||
676 | |||
677 | if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) | ||
678 | die("Failed to copy event."); | ||
679 | |||
680 | if (strpbrk(buf, "*?")) { /* Glob-exp */ | ||
681 | strlist__for_each_safe(ent, n, namelist) | ||
682 | if (strglobmatch(ent->s, buf)) { | ||
683 | found++; | ||
684 | __del_trace_kprobe_event(fd, ent); | ||
685 | strlist__remove(namelist, ent); | ||
686 | } | ||
687 | } else { | ||
688 | ent = strlist__find(namelist, buf); | ||
689 | if (ent) { | ||
690 | found++; | ||
691 | __del_trace_kprobe_event(fd, ent); | ||
692 | strlist__remove(namelist, ent); | ||
693 | } | ||
694 | } | ||
695 | if (found == 0) | ||
696 | pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); | ||
697 | } | ||
698 | |||
699 | void del_trace_kprobe_events(struct strlist *dellist) | ||
700 | { | ||
701 | int fd; | ||
702 | const char *group, *event; | ||
703 | char *p, *str; | ||
704 | struct str_node *ent; | ||
705 | struct strlist *namelist; | ||
706 | |||
707 | fd = open_kprobe_events(O_RDWR, O_APPEND); | ||
708 | /* Get current event names */ | ||
709 | namelist = get_perf_event_names(fd, true); | ||
710 | |||
711 | strlist__for_each(ent, dellist) { | ||
712 | str = strdup(ent->s); | ||
713 | if (!str) | ||
714 | die("Failed to copy event."); | ||
715 | pr_debug("Parsing: %s\n", str); | ||
716 | p = strchr(str, ':'); | ||
717 | if (p) { | ||
718 | group = str; | ||
719 | *p = '\0'; | ||
720 | event = p + 1; | ||
721 | } else { | ||
722 | group = "*"; | ||
723 | event = str; | ||
724 | } | ||
725 | pr_debug("Group: %s, Event: %s\n", group, event); | ||
726 | del_trace_kprobe_event(fd, group, event, namelist); | ||
727 | free(str); | ||
728 | } | ||
729 | strlist__delete(namelist); | ||
730 | close(fd); | ||
731 | } | ||
732 | |||
733 | #define LINEBUF_SIZE 256 | ||
734 | #define NR_ADDITIONAL_LINES 2 | ||
735 | |||
736 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | ||
737 | { | ||
738 | char buf[LINEBUF_SIZE]; | ||
739 | const char *color = PERF_COLOR_BLUE; | ||
740 | |||
741 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
742 | goto error; | ||
743 | if (!skip) { | ||
744 | if (show_num) | ||
745 | fprintf(stdout, "%7u %s", l, buf); | ||
746 | else | ||
747 | color_fprintf(stdout, color, " %s", buf); | ||
748 | } | ||
749 | |||
750 | while (strlen(buf) == LINEBUF_SIZE - 1 && | ||
751 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
752 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
753 | goto error; | ||
754 | if (!skip) { | ||
755 | if (show_num) | ||
756 | fprintf(stdout, "%s", buf); | ||
757 | else | ||
758 | color_fprintf(stdout, color, "%s", buf); | ||
759 | } | ||
760 | } | ||
761 | return; | ||
762 | error: | ||
763 | if (feof(fp)) | ||
764 | die("Source file is shorter than expected."); | ||
765 | else | ||
766 | die("File read error: %s", strerror(errno)); | ||
767 | } | ||
768 | |||
769 | void show_line_range(struct line_range *lr) | ||
770 | { | ||
771 | unsigned int l = 1; | ||
772 | struct line_node *ln; | ||
773 | FILE *fp; | ||
774 | |||
775 | setup_pager(); | ||
776 | |||
777 | if (lr->function) | ||
778 | fprintf(stdout, "<%s:%d>\n", lr->function, | ||
779 | lr->start - lr->offset); | ||
780 | else | ||
781 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | ||
782 | |||
783 | fp = fopen(lr->path, "r"); | ||
784 | if (fp == NULL) | ||
785 | die("Failed to open %s: %s", lr->path, strerror(errno)); | ||
786 | /* Skip to starting line number */ | ||
787 | while (l < lr->start) | ||
788 | show_one_line(fp, l++, true, false); | ||
789 | |||
790 | list_for_each_entry(ln, &lr->line_list, list) { | ||
791 | while (ln->line > l) | ||
792 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
793 | show_one_line(fp, (l++) - lr->offset, false, true); | ||
794 | } | ||
795 | |||
796 | if (lr->end == INT_MAX) | ||
797 | lr->end = l + NR_ADDITIONAL_LINES; | ||
798 | while (l < lr->end && !feof(fp)) | ||
799 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
800 | |||
801 | fclose(fp); | ||
802 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h new file mode 100644 index 000000000000..711287d4baea --- /dev/null +++ b/tools/perf/util/probe-event.h | |||
@@ -0,0 +1,24 @@ | |||
1 | #ifndef _PROBE_EVENT_H | ||
2 | #define _PROBE_EVENT_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include "probe-finder.h" | ||
6 | #include "strlist.h" | ||
7 | |||
8 | extern void parse_line_range_desc(const char *arg, struct line_range *lr); | ||
9 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, | ||
10 | bool *need_dwarf); | ||
11 | extern int synthesize_perf_probe_point(struct probe_point *pp); | ||
12 | extern int synthesize_perf_probe_event(struct probe_point *pp); | ||
13 | extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); | ||
14 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); | ||
15 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | ||
16 | bool force_add); | ||
17 | extern void del_trace_kprobe_events(struct strlist *dellist); | ||
18 | extern void show_perf_probe_events(void); | ||
19 | extern void show_line_range(struct line_range *lr); | ||
20 | |||
21 | /* Maximum index number of event-name postfix */ | ||
22 | #define MAX_EVENT_INDEX 1024 | ||
23 | |||
24 | #endif /*_PROBE_EVENT_H */ | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c new file mode 100644 index 000000000000..e77dc886760e --- /dev/null +++ b/tools/perf/util/probe-finder.c | |||
@@ -0,0 +1,833 @@ | |||
1 | /* | ||
2 | * probe-finder.c : C expression to kprobe event converter | ||
3 | * | ||
4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <sys/utsname.h> | ||
23 | #include <sys/types.h> | ||
24 | #include <sys/stat.h> | ||
25 | #include <fcntl.h> | ||
26 | #include <errno.h> | ||
27 | #include <stdio.h> | ||
28 | #include <unistd.h> | ||
29 | #include <getopt.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <string.h> | ||
32 | #include <stdarg.h> | ||
33 | #include <ctype.h> | ||
34 | |||
35 | #include "string.h" | ||
36 | #include "event.h" | ||
37 | #include "debug.h" | ||
38 | #include "util.h" | ||
39 | #include "probe-finder.h" | ||
40 | |||
41 | |||
42 | /* | ||
43 | * Generic dwarf analysis helpers | ||
44 | */ | ||
45 | |||
46 | #define X86_32_MAX_REGS 8 | ||
47 | const char *x86_32_regs_table[X86_32_MAX_REGS] = { | ||
48 | "%ax", | ||
49 | "%cx", | ||
50 | "%dx", | ||
51 | "%bx", | ||
52 | "$stack", /* Stack address instead of %sp */ | ||
53 | "%bp", | ||
54 | "%si", | ||
55 | "%di", | ||
56 | }; | ||
57 | |||
58 | #define X86_64_MAX_REGS 16 | ||
59 | const char *x86_64_regs_table[X86_64_MAX_REGS] = { | ||
60 | "%ax", | ||
61 | "%dx", | ||
62 | "%cx", | ||
63 | "%bx", | ||
64 | "%si", | ||
65 | "%di", | ||
66 | "%bp", | ||
67 | "%sp", | ||
68 | "%r8", | ||
69 | "%r9", | ||
70 | "%r10", | ||
71 | "%r11", | ||
72 | "%r12", | ||
73 | "%r13", | ||
74 | "%r14", | ||
75 | "%r15", | ||
76 | }; | ||
77 | |||
78 | /* TODO: switching by dwarf address size */ | ||
79 | #ifdef __x86_64__ | ||
80 | #define ARCH_MAX_REGS X86_64_MAX_REGS | ||
81 | #define arch_regs_table x86_64_regs_table | ||
82 | #else | ||
83 | #define ARCH_MAX_REGS X86_32_MAX_REGS | ||
84 | #define arch_regs_table x86_32_regs_table | ||
85 | #endif | ||
86 | |||
87 | /* Return architecture dependent register string (for kprobe-tracer) */ | ||
88 | static const char *get_arch_regstr(unsigned int n) | ||
89 | { | ||
90 | return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Compare the tail of two strings. | ||
95 | * Return 0 if whole of either string is same as another's tail part. | ||
96 | */ | ||
97 | static int strtailcmp(const char *s1, const char *s2) | ||
98 | { | ||
99 | int i1 = strlen(s1); | ||
100 | int i2 = strlen(s2); | ||
101 | while (--i1 >= 0 && --i2 >= 0) { | ||
102 | if (s1[i1] != s2[i2]) | ||
103 | return s1[i1] - s2[i2]; | ||
104 | } | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /* Line number list operations */ | ||
109 | |||
110 | /* Add a line to line number list */ | ||
111 | static void line_list__add_line(struct list_head *head, unsigned int line) | ||
112 | { | ||
113 | struct line_node *ln; | ||
114 | struct list_head *p; | ||
115 | |||
116 | /* Reverse search, because new line will be the last one */ | ||
117 | list_for_each_entry_reverse(ln, head, list) { | ||
118 | if (ln->line < line) { | ||
119 | p = &ln->list; | ||
120 | goto found; | ||
121 | } else if (ln->line == line) /* Already exist */ | ||
122 | return ; | ||
123 | } | ||
124 | /* List is empty, or the smallest entry */ | ||
125 | p = head; | ||
126 | found: | ||
127 | pr_debug("line list: add a line %u\n", line); | ||
128 | ln = zalloc(sizeof(struct line_node)); | ||
129 | DIE_IF(ln == NULL); | ||
130 | ln->line = line; | ||
131 | INIT_LIST_HEAD(&ln->list); | ||
132 | list_add(&ln->list, p); | ||
133 | } | ||
134 | |||
135 | /* Check if the line in line number list */ | ||
136 | static int line_list__has_line(struct list_head *head, unsigned int line) | ||
137 | { | ||
138 | struct line_node *ln; | ||
139 | |||
140 | /* Reverse search, because new line will be the last one */ | ||
141 | list_for_each_entry(ln, head, list) | ||
142 | if (ln->line == line) | ||
143 | return 1; | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | /* Init line number list */ | ||
149 | static void line_list__init(struct list_head *head) | ||
150 | { | ||
151 | INIT_LIST_HEAD(head); | ||
152 | } | ||
153 | |||
154 | /* Free line number list */ | ||
155 | static void line_list__free(struct list_head *head) | ||
156 | { | ||
157 | struct line_node *ln; | ||
158 | while (!list_empty(head)) { | ||
159 | ln = list_first_entry(head, struct line_node, list); | ||
160 | list_del(&ln->list); | ||
161 | free(ln); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | /* Dwarf wrappers */ | ||
166 | |||
167 | /* Find the realpath of the target file. */ | ||
168 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
169 | { | ||
170 | Dwarf_Files *files; | ||
171 | size_t nfiles, i; | ||
172 | const char *src; | ||
173 | int ret; | ||
174 | |||
175 | if (!fname) | ||
176 | return NULL; | ||
177 | |||
178 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | ||
179 | if (ret != 0) | ||
180 | return NULL; | ||
181 | |||
182 | for (i = 0; i < nfiles; i++) { | ||
183 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
184 | if (strtailcmp(src, fname) == 0) | ||
185 | break; | ||
186 | } | ||
187 | return src; | ||
188 | } | ||
189 | |||
190 | struct __addr_die_search_param { | ||
191 | Dwarf_Addr addr; | ||
192 | Dwarf_Die *die_mem; | ||
193 | }; | ||
194 | |||
195 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
196 | { | ||
197 | struct __addr_die_search_param *ad = data; | ||
198 | |||
199 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
200 | dwarf_haspc(fn_die, ad->addr)) { | ||
201 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
202 | return DWARF_CB_ABORT; | ||
203 | } | ||
204 | return DWARF_CB_OK; | ||
205 | } | ||
206 | |||
207 | /* Search a real subprogram including this line, */ | ||
208 | static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
209 | Dwarf_Die *die_mem) | ||
210 | { | ||
211 | struct __addr_die_search_param ad; | ||
212 | ad.addr = addr; | ||
213 | ad.die_mem = die_mem; | ||
214 | /* dwarf_getscopes can't find subprogram. */ | ||
215 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
216 | return NULL; | ||
217 | else | ||
218 | return die_mem; | ||
219 | } | ||
220 | |||
221 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ | ||
222 | static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
223 | Dwarf_Die *die_mem) | ||
224 | { | ||
225 | Dwarf_Die child_die; | ||
226 | int ret; | ||
227 | |||
228 | ret = dwarf_child(sp_die, die_mem); | ||
229 | if (ret != 0) | ||
230 | return NULL; | ||
231 | |||
232 | do { | ||
233 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | ||
234 | dwarf_haspc(die_mem, addr)) | ||
235 | return die_mem; | ||
236 | |||
237 | if (die_get_inlinefunc(die_mem, addr, &child_die)) { | ||
238 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
239 | return die_mem; | ||
240 | } | ||
241 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
242 | |||
243 | return NULL; | ||
244 | } | ||
245 | |||
246 | /* Compare diename and tname */ | ||
247 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
248 | { | ||
249 | const char *name; | ||
250 | name = dwarf_diename(dw_die); | ||
251 | DIE_IF(name == NULL); | ||
252 | return strcmp(tname, name); | ||
253 | } | ||
254 | |||
255 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | ||
256 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | ||
257 | { | ||
258 | Dwarf_Addr epc; | ||
259 | int ret; | ||
260 | |||
261 | ret = dwarf_entrypc(dw_die, &epc); | ||
262 | DIE_IF(ret == -1); | ||
263 | return epc; | ||
264 | } | ||
265 | |||
266 | /* Get a variable die */ | ||
267 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, | ||
268 | Dwarf_Die *die_mem) | ||
269 | { | ||
270 | Dwarf_Die child_die; | ||
271 | int tag; | ||
272 | int ret; | ||
273 | |||
274 | ret = dwarf_child(sp_die, die_mem); | ||
275 | if (ret != 0) | ||
276 | return NULL; | ||
277 | |||
278 | do { | ||
279 | tag = dwarf_tag(die_mem); | ||
280 | if ((tag == DW_TAG_formal_parameter || | ||
281 | tag == DW_TAG_variable) && | ||
282 | (die_compare_name(die_mem, name) == 0)) | ||
283 | return die_mem; | ||
284 | |||
285 | if (die_find_variable(die_mem, name, &child_die)) { | ||
286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
287 | return die_mem; | ||
288 | } | ||
289 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
290 | |||
291 | return NULL; | ||
292 | } | ||
293 | |||
294 | /* | ||
295 | * Probe finder related functions | ||
296 | */ | ||
297 | |||
298 | /* Show a location */ | ||
299 | static void show_location(Dwarf_Op *op, struct probe_finder *pf) | ||
300 | { | ||
301 | unsigned int regn; | ||
302 | Dwarf_Word offs = 0; | ||
303 | int deref = 0, ret; | ||
304 | const char *regs; | ||
305 | |||
306 | /* TODO: support CFA */ | ||
307 | /* If this is based on frame buffer, set the offset */ | ||
308 | if (op->atom == DW_OP_fbreg) { | ||
309 | if (pf->fb_ops == NULL) | ||
310 | die("The attribute of frame base is not supported.\n"); | ||
311 | deref = 1; | ||
312 | offs = op->number; | ||
313 | op = &pf->fb_ops[0]; | ||
314 | } | ||
315 | |||
316 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { | ||
317 | regn = op->atom - DW_OP_breg0; | ||
318 | offs += op->number; | ||
319 | deref = 1; | ||
320 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { | ||
321 | regn = op->atom - DW_OP_reg0; | ||
322 | } else if (op->atom == DW_OP_bregx) { | ||
323 | regn = op->number; | ||
324 | offs += op->number2; | ||
325 | deref = 1; | ||
326 | } else if (op->atom == DW_OP_regx) { | ||
327 | regn = op->number; | ||
328 | } else | ||
329 | die("DW_OP %d is not supported.", op->atom); | ||
330 | |||
331 | regs = get_arch_regstr(regn); | ||
332 | if (!regs) | ||
333 | die("%u exceeds max register number.", regn); | ||
334 | |||
335 | if (deref) | ||
336 | ret = snprintf(pf->buf, pf->len, " %s=+%ju(%s)", | ||
337 | pf->var, (uintmax_t)offs, regs); | ||
338 | else | ||
339 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | ||
340 | DIE_IF(ret < 0); | ||
341 | DIE_IF(ret >= pf->len); | ||
342 | } | ||
343 | |||
344 | /* Show a variables in kprobe event format */ | ||
345 | static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | ||
346 | { | ||
347 | Dwarf_Attribute attr; | ||
348 | Dwarf_Op *expr; | ||
349 | size_t nexpr; | ||
350 | int ret; | ||
351 | |||
352 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) | ||
353 | goto error; | ||
354 | /* TODO: handle more than 1 exprs */ | ||
355 | ret = dwarf_getlocation_addr(&attr, (pf->addr - pf->cu_base), | ||
356 | &expr, &nexpr, 1); | ||
357 | if (ret <= 0 || nexpr == 0) | ||
358 | goto error; | ||
359 | |||
360 | show_location(expr, pf); | ||
361 | /* *expr will be cached in libdw. Don't free it. */ | ||
362 | return ; | ||
363 | error: | ||
364 | /* TODO: Support const_value */ | ||
365 | die("Failed to find the location of %s at this address.\n" | ||
366 | " Perhaps, it has been optimized out.", pf->var); | ||
367 | } | ||
368 | |||
369 | /* Find a variable in a subprogram die */ | ||
370 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
371 | { | ||
372 | int ret; | ||
373 | Dwarf_Die vr_die; | ||
374 | |||
375 | /* TODO: Support struct members and arrays */ | ||
376 | if (!is_c_varname(pf->var)) { | ||
377 | /* Output raw parameters */ | ||
378 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | ||
379 | DIE_IF(ret < 0); | ||
380 | DIE_IF(ret >= pf->len); | ||
381 | return ; | ||
382 | } | ||
383 | |||
384 | pr_debug("Searching '%s' variable in context.\n", pf->var); | ||
385 | /* Search child die for local variables and parameters. */ | ||
386 | if (!die_find_variable(sp_die, pf->var, &vr_die)) | ||
387 | die("Failed to find '%s' in this function.", pf->var); | ||
388 | |||
389 | show_variable(&vr_die, pf); | ||
390 | } | ||
391 | |||
392 | /* Show a probe point to output buffer */ | ||
393 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
394 | { | ||
395 | struct probe_point *pp = pf->pp; | ||
396 | Dwarf_Addr eaddr; | ||
397 | Dwarf_Die die_mem; | ||
398 | const char *name; | ||
399 | char tmp[MAX_PROBE_BUFFER]; | ||
400 | int ret, i, len; | ||
401 | Dwarf_Attribute fb_attr; | ||
402 | size_t nops; | ||
403 | |||
404 | /* If no real subprogram, find a real one */ | ||
405 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | ||
406 | sp_die = die_get_real_subprogram(&pf->cu_die, | ||
407 | pf->addr, &die_mem); | ||
408 | if (!sp_die) | ||
409 | die("Probe point is not found in subprograms."); | ||
410 | } | ||
411 | |||
412 | /* Output name of probe point */ | ||
413 | name = dwarf_diename(sp_die); | ||
414 | if (name) { | ||
415 | dwarf_entrypc(sp_die, &eaddr); | ||
416 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, | ||
417 | (unsigned long)(pf->addr - eaddr)); | ||
418 | /* Copy the function name if possible */ | ||
419 | if (!pp->function) { | ||
420 | pp->function = strdup(name); | ||
421 | pp->offset = (size_t)(pf->addr - eaddr); | ||
422 | } | ||
423 | } else { | ||
424 | /* This function has no name. */ | ||
425 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", | ||
426 | (uintmax_t)pf->addr); | ||
427 | if (!pp->function) { | ||
428 | /* TODO: Use _stext */ | ||
429 | pp->function = strdup(""); | ||
430 | pp->offset = (size_t)pf->addr; | ||
431 | } | ||
432 | } | ||
433 | DIE_IF(ret < 0); | ||
434 | DIE_IF(ret >= MAX_PROBE_BUFFER); | ||
435 | len = ret; | ||
436 | pr_debug("Probe point found: %s\n", tmp); | ||
437 | |||
438 | /* Get the frame base attribute/ops */ | ||
439 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | ||
440 | ret = dwarf_getlocation_addr(&fb_attr, (pf->addr - pf->cu_base), | ||
441 | &pf->fb_ops, &nops, 1); | ||
442 | if (ret <= 0 || nops == 0) | ||
443 | pf->fb_ops = NULL; | ||
444 | |||
445 | /* Find each argument */ | ||
446 | /* TODO: use dwarf_cfi_addrframe */ | ||
447 | for (i = 0; i < pp->nr_args; i++) { | ||
448 | pf->var = pp->args[i]; | ||
449 | pf->buf = &tmp[len]; | ||
450 | pf->len = MAX_PROBE_BUFFER - len; | ||
451 | find_variable(sp_die, pf); | ||
452 | len += strlen(pf->buf); | ||
453 | } | ||
454 | |||
455 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | ||
456 | pf->fb_ops = NULL; | ||
457 | |||
458 | pp->probes[pp->found] = strdup(tmp); | ||
459 | pp->found++; | ||
460 | } | ||
461 | |||
462 | /* Find probe point from its line number */ | ||
463 | static void find_probe_point_by_line(struct probe_finder *pf) | ||
464 | { | ||
465 | Dwarf_Lines *lines; | ||
466 | Dwarf_Line *line; | ||
467 | size_t nlines, i; | ||
468 | Dwarf_Addr addr; | ||
469 | int lineno; | ||
470 | int ret; | ||
471 | |||
472 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | ||
473 | DIE_IF(ret != 0); | ||
474 | |||
475 | for (i = 0; i < nlines; i++) { | ||
476 | line = dwarf_onesrcline(lines, i); | ||
477 | dwarf_lineno(line, &lineno); | ||
478 | if (lineno != pf->lno) | ||
479 | continue; | ||
480 | |||
481 | /* TODO: Get fileno from line, but how? */ | ||
482 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
483 | continue; | ||
484 | |||
485 | ret = dwarf_lineaddr(line, &addr); | ||
486 | DIE_IF(ret != 0); | ||
487 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
488 | (int)i, lineno, (uintmax_t)addr); | ||
489 | pf->addr = addr; | ||
490 | |||
491 | show_probe_point(NULL, pf); | ||
492 | /* Continuing, because target line might be inlined. */ | ||
493 | } | ||
494 | } | ||
495 | |||
496 | /* Find lines which match lazy pattern */ | ||
497 | static int find_lazy_match_lines(struct list_head *head, | ||
498 | const char *fname, const char *pat) | ||
499 | { | ||
500 | char *fbuf, *p1, *p2; | ||
501 | int fd, line, nlines = 0; | ||
502 | struct stat st; | ||
503 | |||
504 | fd = open(fname, O_RDONLY); | ||
505 | if (fd < 0) | ||
506 | die("failed to open %s", fname); | ||
507 | DIE_IF(fstat(fd, &st) < 0); | ||
508 | fbuf = malloc(st.st_size + 2); | ||
509 | DIE_IF(fbuf == NULL); | ||
510 | DIE_IF(read(fd, fbuf, st.st_size) < 0); | ||
511 | close(fd); | ||
512 | fbuf[st.st_size] = '\n'; /* Dummy line */ | ||
513 | fbuf[st.st_size + 1] = '\0'; | ||
514 | p1 = fbuf; | ||
515 | line = 1; | ||
516 | while ((p2 = strchr(p1, '\n')) != NULL) { | ||
517 | *p2 = '\0'; | ||
518 | if (strlazymatch(p1, pat)) { | ||
519 | line_list__add_line(head, line); | ||
520 | nlines++; | ||
521 | } | ||
522 | line++; | ||
523 | p1 = p2 + 1; | ||
524 | } | ||
525 | free(fbuf); | ||
526 | return nlines; | ||
527 | } | ||
528 | |||
529 | /* Find probe points from lazy pattern */ | ||
530 | static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
531 | { | ||
532 | Dwarf_Lines *lines; | ||
533 | Dwarf_Line *line; | ||
534 | size_t nlines, i; | ||
535 | Dwarf_Addr addr; | ||
536 | Dwarf_Die die_mem; | ||
537 | int lineno; | ||
538 | int ret; | ||
539 | |||
540 | if (list_empty(&pf->lcache)) { | ||
541 | /* Matching lazy line pattern */ | ||
542 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | ||
543 | pf->pp->lazy_line); | ||
544 | if (ret <= 0) | ||
545 | die("No matched lines found in %s.", pf->fname); | ||
546 | } | ||
547 | |||
548 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); | ||
549 | DIE_IF(ret != 0); | ||
550 | for (i = 0; i < nlines; i++) { | ||
551 | line = dwarf_onesrcline(lines, i); | ||
552 | |||
553 | dwarf_lineno(line, &lineno); | ||
554 | if (!line_list__has_line(&pf->lcache, lineno)) | ||
555 | continue; | ||
556 | |||
557 | /* TODO: Get fileno from line, but how? */ | ||
558 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
559 | continue; | ||
560 | |||
561 | ret = dwarf_lineaddr(line, &addr); | ||
562 | DIE_IF(ret != 0); | ||
563 | if (sp_die) { | ||
564 | /* Address filtering 1: does sp_die include addr? */ | ||
565 | if (!dwarf_haspc(sp_die, addr)) | ||
566 | continue; | ||
567 | /* Address filtering 2: No child include addr? */ | ||
568 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
569 | continue; | ||
570 | } | ||
571 | |||
572 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", | ||
573 | (int)i, lineno, (unsigned long long)addr); | ||
574 | pf->addr = addr; | ||
575 | |||
576 | show_probe_point(sp_die, pf); | ||
577 | /* Continuing, because target line might be inlined. */ | ||
578 | } | ||
579 | /* TODO: deallocate lines, but how? */ | ||
580 | } | ||
581 | |||
582 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | ||
583 | { | ||
584 | struct probe_finder *pf = (struct probe_finder *)data; | ||
585 | struct probe_point *pp = pf->pp; | ||
586 | |||
587 | if (pp->lazy_line) | ||
588 | find_probe_point_lazy(in_die, pf); | ||
589 | else { | ||
590 | /* Get probe address */ | ||
591 | pf->addr = die_get_entrypc(in_die); | ||
592 | pf->addr += pp->offset; | ||
593 | pr_debug("found inline addr: 0x%jx\n", | ||
594 | (uintmax_t)pf->addr); | ||
595 | |||
596 | show_probe_point(in_die, pf); | ||
597 | } | ||
598 | |||
599 | return DWARF_CB_OK; | ||
600 | } | ||
601 | |||
602 | /* Search function from function name */ | ||
603 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | ||
604 | { | ||
605 | struct probe_finder *pf = (struct probe_finder *)data; | ||
606 | struct probe_point *pp = pf->pp; | ||
607 | |||
608 | /* Check tag and diename */ | ||
609 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | ||
610 | die_compare_name(sp_die, pp->function) != 0) | ||
611 | return 0; | ||
612 | |||
613 | pf->fname = dwarf_decl_file(sp_die); | ||
614 | if (pp->line) { /* Function relative line */ | ||
615 | dwarf_decl_line(sp_die, &pf->lno); | ||
616 | pf->lno += pp->line; | ||
617 | find_probe_point_by_line(pf); | ||
618 | } else if (!dwarf_func_inline(sp_die)) { | ||
619 | /* Real function */ | ||
620 | if (pp->lazy_line) | ||
621 | find_probe_point_lazy(sp_die, pf); | ||
622 | else { | ||
623 | pf->addr = die_get_entrypc(sp_die); | ||
624 | pf->addr += pp->offset; | ||
625 | /* TODO: Check the address in this function */ | ||
626 | show_probe_point(sp_die, pf); | ||
627 | } | ||
628 | } else | ||
629 | /* Inlined function: search instances */ | ||
630 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); | ||
631 | |||
632 | return 1; /* Exit; no same symbol in this CU. */ | ||
633 | } | ||
634 | |||
635 | static void find_probe_point_by_func(struct probe_finder *pf) | ||
636 | { | ||
637 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); | ||
638 | } | ||
639 | |||
640 | /* Find a probe point */ | ||
641 | int find_probe_point(int fd, struct probe_point *pp) | ||
642 | { | ||
643 | struct probe_finder pf = {.pp = pp}; | ||
644 | int ret; | ||
645 | Dwarf_Off off, noff; | ||
646 | size_t cuhl; | ||
647 | Dwarf_Die *diep; | ||
648 | Dwarf *dbg; | ||
649 | |||
650 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
651 | if (!dbg) | ||
652 | return -ENOENT; | ||
653 | |||
654 | pp->found = 0; | ||
655 | off = 0; | ||
656 | line_list__init(&pf.lcache); | ||
657 | /* Loop on CUs (Compilation Unit) */ | ||
658 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | ||
659 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
660 | diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); | ||
661 | if (!diep) | ||
662 | continue; | ||
663 | |||
664 | /* Check if target file is included. */ | ||
665 | if (pp->file) | ||
666 | pf.fname = cu_find_realpath(&pf.cu_die, pp->file); | ||
667 | else | ||
668 | pf.fname = NULL; | ||
669 | |||
670 | if (!pp->file || pf.fname) { | ||
671 | /* Save CU base address (for frame_base) */ | ||
672 | ret = dwarf_lowpc(&pf.cu_die, &pf.cu_base); | ||
673 | if (ret != 0) | ||
674 | pf.cu_base = 0; | ||
675 | if (pp->function) | ||
676 | find_probe_point_by_func(&pf); | ||
677 | else if (pp->lazy_line) | ||
678 | find_probe_point_lazy(NULL, &pf); | ||
679 | else { | ||
680 | pf.lno = pp->line; | ||
681 | find_probe_point_by_line(&pf); | ||
682 | } | ||
683 | } | ||
684 | off = noff; | ||
685 | } | ||
686 | line_list__free(&pf.lcache); | ||
687 | dwarf_end(dbg); | ||
688 | |||
689 | return pp->found; | ||
690 | } | ||
691 | |||
692 | /* Find line range from its line number */ | ||
693 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | ||
694 | { | ||
695 | Dwarf_Lines *lines; | ||
696 | Dwarf_Line *line; | ||
697 | size_t nlines, i; | ||
698 | Dwarf_Addr addr; | ||
699 | int lineno; | ||
700 | int ret; | ||
701 | const char *src; | ||
702 | Dwarf_Die die_mem; | ||
703 | |||
704 | line_list__init(&lf->lr->line_list); | ||
705 | ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); | ||
706 | DIE_IF(ret != 0); | ||
707 | |||
708 | for (i = 0; i < nlines; i++) { | ||
709 | line = dwarf_onesrcline(lines, i); | ||
710 | ret = dwarf_lineno(line, &lineno); | ||
711 | DIE_IF(ret != 0); | ||
712 | if (lf->lno_s > lineno || lf->lno_e < lineno) | ||
713 | continue; | ||
714 | |||
715 | if (sp_die) { | ||
716 | /* Address filtering 1: does sp_die include addr? */ | ||
717 | ret = dwarf_lineaddr(line, &addr); | ||
718 | DIE_IF(ret != 0); | ||
719 | if (!dwarf_haspc(sp_die, addr)) | ||
720 | continue; | ||
721 | |||
722 | /* Address filtering 2: No child include addr? */ | ||
723 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
724 | continue; | ||
725 | } | ||
726 | |||
727 | /* TODO: Get fileno from line, but how? */ | ||
728 | src = dwarf_linesrc(line, NULL, NULL); | ||
729 | if (strtailcmp(src, lf->fname) != 0) | ||
730 | continue; | ||
731 | |||
732 | /* Copy real path */ | ||
733 | if (!lf->lr->path) | ||
734 | lf->lr->path = strdup(src); | ||
735 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); | ||
736 | } | ||
737 | /* Update status */ | ||
738 | if (!list_empty(&lf->lr->line_list)) | ||
739 | lf->found = 1; | ||
740 | else { | ||
741 | free(lf->lr->path); | ||
742 | lf->lr->path = NULL; | ||
743 | } | ||
744 | } | ||
745 | |||
746 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | ||
747 | { | ||
748 | find_line_range_by_line(in_die, (struct line_finder *)data); | ||
749 | return DWARF_CB_ABORT; /* No need to find other instances */ | ||
750 | } | ||
751 | |||
752 | /* Search function from function name */ | ||
753 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | ||
754 | { | ||
755 | struct line_finder *lf = (struct line_finder *)data; | ||
756 | struct line_range *lr = lf->lr; | ||
757 | |||
758 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | ||
759 | die_compare_name(sp_die, lr->function) == 0) { | ||
760 | lf->fname = dwarf_decl_file(sp_die); | ||
761 | dwarf_decl_line(sp_die, &lr->offset); | ||
762 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | ||
763 | lf->lno_s = lr->offset + lr->start; | ||
764 | if (!lr->end) | ||
765 | lf->lno_e = INT_MAX; | ||
766 | else | ||
767 | lf->lno_e = lr->offset + lr->end; | ||
768 | lr->start = lf->lno_s; | ||
769 | lr->end = lf->lno_e; | ||
770 | if (dwarf_func_inline(sp_die)) | ||
771 | dwarf_func_inline_instances(sp_die, | ||
772 | line_range_inline_cb, lf); | ||
773 | else | ||
774 | find_line_range_by_line(sp_die, lf); | ||
775 | return 1; | ||
776 | } | ||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | static void find_line_range_by_func(struct line_finder *lf) | ||
781 | { | ||
782 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); | ||
783 | } | ||
784 | |||
785 | int find_line_range(int fd, struct line_range *lr) | ||
786 | { | ||
787 | struct line_finder lf = {.lr = lr, .found = 0}; | ||
788 | int ret; | ||
789 | Dwarf_Off off = 0, noff; | ||
790 | size_t cuhl; | ||
791 | Dwarf_Die *diep; | ||
792 | Dwarf *dbg; | ||
793 | |||
794 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
795 | if (!dbg) | ||
796 | return -ENOENT; | ||
797 | |||
798 | /* Loop on CUs (Compilation Unit) */ | ||
799 | while (!lf.found) { | ||
800 | ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); | ||
801 | if (ret != 0) | ||
802 | break; | ||
803 | |||
804 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
805 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); | ||
806 | if (!diep) | ||
807 | continue; | ||
808 | |||
809 | /* Check if target file is included. */ | ||
810 | if (lr->file) | ||
811 | lf.fname = cu_find_realpath(&lf.cu_die, lr->file); | ||
812 | else | ||
813 | lf.fname = 0; | ||
814 | |||
815 | if (!lr->file || lf.fname) { | ||
816 | if (lr->function) | ||
817 | find_line_range_by_func(&lf); | ||
818 | else { | ||
819 | lf.lno_s = lr->start; | ||
820 | if (!lr->end) | ||
821 | lf.lno_e = INT_MAX; | ||
822 | else | ||
823 | lf.lno_e = lr->end; | ||
824 | find_line_range_by_line(NULL, &lf); | ||
825 | } | ||
826 | } | ||
827 | off = noff; | ||
828 | } | ||
829 | pr_debug("path: %lx\n", (unsigned long)lr->path); | ||
830 | dwarf_end(dbg); | ||
831 | return lf.found; | ||
832 | } | ||
833 | |||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h new file mode 100644 index 000000000000..d1a651793ba6 --- /dev/null +++ b/tools/perf/util/probe-finder.h | |||
@@ -0,0 +1,93 @@ | |||
1 | #ifndef _PROBE_FINDER_H | ||
2 | #define _PROBE_FINDER_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include "util.h" | ||
6 | |||
7 | #define MAX_PATH_LEN 256 | ||
8 | #define MAX_PROBE_BUFFER 1024 | ||
9 | #define MAX_PROBES 128 | ||
10 | |||
11 | static inline int is_c_varname(const char *name) | ||
12 | { | ||
13 | /* TODO */ | ||
14 | return isalpha(name[0]) || name[0] == '_'; | ||
15 | } | ||
16 | |||
17 | struct probe_point { | ||
18 | char *event; /* Event name */ | ||
19 | char *group; /* Event group */ | ||
20 | |||
21 | /* Inputs */ | ||
22 | char *file; /* File name */ | ||
23 | int line; /* Line number */ | ||
24 | char *lazy_line; /* Lazy line pattern */ | ||
25 | |||
26 | char *function; /* Function name */ | ||
27 | int offset; /* Offset bytes */ | ||
28 | |||
29 | int nr_args; /* Number of arguments */ | ||
30 | char **args; /* Arguments */ | ||
31 | |||
32 | int retprobe; /* Return probe */ | ||
33 | |||
34 | /* Output */ | ||
35 | int found; /* Number of found probe points */ | ||
36 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ | ||
37 | }; | ||
38 | |||
39 | /* Line number container */ | ||
40 | struct line_node { | ||
41 | struct list_head list; | ||
42 | unsigned int line; | ||
43 | }; | ||
44 | |||
45 | /* Line range */ | ||
46 | struct line_range { | ||
47 | char *file; /* File name */ | ||
48 | char *function; /* Function name */ | ||
49 | unsigned int start; /* Start line number */ | ||
50 | unsigned int end; /* End line number */ | ||
51 | int offset; /* Start line offset */ | ||
52 | char *path; /* Real path name */ | ||
53 | struct list_head line_list; /* Visible lines */ | ||
54 | }; | ||
55 | |||
56 | #ifndef NO_DWARF_SUPPORT | ||
57 | extern int find_probe_point(int fd, struct probe_point *pp); | ||
58 | extern int find_line_range(int fd, struct line_range *lr); | ||
59 | |||
60 | #include <dwarf.h> | ||
61 | #include <libdw.h> | ||
62 | |||
63 | struct probe_finder { | ||
64 | struct probe_point *pp; /* Target probe point */ | ||
65 | |||
66 | /* For function searching */ | ||
67 | Dwarf_Addr addr; /* Address */ | ||
68 | const char *fname; /* File name */ | ||
69 | int lno; /* Line number */ | ||
70 | Dwarf_Die cu_die; /* Current CU */ | ||
71 | |||
72 | /* For variable searching */ | ||
73 | Dwarf_Op *fb_ops; /* Frame base attribute */ | ||
74 | Dwarf_Addr cu_base; /* Current CU base address */ | ||
75 | const char *var; /* Current variable name */ | ||
76 | char *buf; /* Current output buffer */ | ||
77 | int len; /* Length of output buffer */ | ||
78 | struct list_head lcache; /* Line cache for lazy match */ | ||
79 | }; | ||
80 | |||
81 | struct line_finder { | ||
82 | struct line_range *lr; /* Target line range */ | ||
83 | |||
84 | const char *fname; /* File name */ | ||
85 | int lno_s; /* Start line number */ | ||
86 | int lno_e; /* End line number */ | ||
87 | Dwarf_Die cu_die; /* Current CU */ | ||
88 | int found; | ||
89 | }; | ||
90 | |||
91 | #endif /* NO_DWARF_SUPPORT */ | ||
92 | |||
93 | #endif /*_PROBE_FINDER_H */ | ||
diff --git a/tools/perf/util/quote.h b/tools/perf/util/quote.h index a5454a1d1c13..b6a019733919 100644 --- a/tools/perf/util/quote.h +++ b/tools/perf/util/quote.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef QUOTE_H | 1 | #ifndef __PERF_QUOTE_H |
2 | #define QUOTE_H | 2 | #define __PERF_QUOTE_H |
3 | 3 | ||
4 | #include <stddef.h> | 4 | #include <stddef.h> |
5 | #include <stdio.h> | 5 | #include <stdio.h> |
@@ -65,4 +65,4 @@ extern void perl_quote_print(FILE *stream, const char *src); | |||
65 | extern void python_quote_print(FILE *stream, const char *src); | 65 | extern void python_quote_print(FILE *stream, const char *src); |
66 | extern void tcl_quote_print(FILE *stream, const char *src); | 66 | extern void tcl_quote_print(FILE *stream, const char *src); |
67 | 67 | ||
68 | #endif | 68 | #endif /* __PERF_QUOTE_H */ |
diff --git a/tools/perf/util/run-command.h b/tools/perf/util/run-command.h index cc1837deba88..d79028727ce2 100644 --- a/tools/perf/util/run-command.h +++ b/tools/perf/util/run-command.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef RUN_COMMAND_H | 1 | #ifndef __PERF_RUN_COMMAND_H |
2 | #define RUN_COMMAND_H | 2 | #define __PERF_RUN_COMMAND_H |
3 | 3 | ||
4 | enum { | 4 | enum { |
5 | ERR_RUN_COMMAND_FORK = 10000, | 5 | ERR_RUN_COMMAND_FORK = 10000, |
@@ -85,4 +85,4 @@ struct async { | |||
85 | int start_async(struct async *async); | 85 | int start_async(struct async *async); |
86 | int finish_async(struct async *async); | 86 | int finish_async(struct async *async); |
87 | 87 | ||
88 | #endif | 88 | #endif /* __PERF_RUN_COMMAND_H */ |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c new file mode 100644 index 000000000000..5376378e0cfc --- /dev/null +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -0,0 +1,568 @@ | |||
1 | /* | ||
2 | * trace-event-perl. Feed perf trace events to an embedded Perl interpreter. | ||
3 | * | ||
4 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <ctype.h> | ||
26 | #include <errno.h> | ||
27 | |||
28 | #include "../../perf.h" | ||
29 | #include "../util.h" | ||
30 | #include "../trace-event.h" | ||
31 | |||
32 | #include <EXTERN.h> | ||
33 | #include <perl.h> | ||
34 | |||
35 | void boot_Perf__Trace__Context(pTHX_ CV *cv); | ||
36 | void boot_DynaLoader(pTHX_ CV *cv); | ||
37 | typedef PerlInterpreter * INTERP; | ||
38 | |||
39 | void xs_init(pTHX); | ||
40 | |||
41 | void xs_init(pTHX) | ||
42 | { | ||
43 | const char *file = __FILE__; | ||
44 | dXSUB_SYS; | ||
45 | |||
46 | newXS("Perf::Trace::Context::bootstrap", boot_Perf__Trace__Context, | ||
47 | file); | ||
48 | newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file); | ||
49 | } | ||
50 | |||
51 | INTERP my_perl; | ||
52 | |||
53 | #define FTRACE_MAX_EVENT \ | ||
54 | ((1 << (sizeof(unsigned short) * 8)) - 1) | ||
55 | |||
56 | struct event *events[FTRACE_MAX_EVENT]; | ||
57 | |||
58 | extern struct scripting_context *scripting_context; | ||
59 | |||
60 | static char *cur_field_name; | ||
61 | static int zero_flag_atom; | ||
62 | |||
63 | static void define_symbolic_value(const char *ev_name, | ||
64 | const char *field_name, | ||
65 | const char *field_value, | ||
66 | const char *field_str) | ||
67 | { | ||
68 | unsigned long long value; | ||
69 | dSP; | ||
70 | |||
71 | value = eval_flag(field_value); | ||
72 | |||
73 | ENTER; | ||
74 | SAVETMPS; | ||
75 | PUSHMARK(SP); | ||
76 | |||
77 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
78 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
79 | XPUSHs(sv_2mortal(newSVuv(value))); | ||
80 | XPUSHs(sv_2mortal(newSVpv(field_str, 0))); | ||
81 | |||
82 | PUTBACK; | ||
83 | if (get_cv("main::define_symbolic_value", 0)) | ||
84 | call_pv("main::define_symbolic_value", G_SCALAR); | ||
85 | SPAGAIN; | ||
86 | PUTBACK; | ||
87 | FREETMPS; | ||
88 | LEAVE; | ||
89 | } | ||
90 | |||
91 | static void define_symbolic_values(struct print_flag_sym *field, | ||
92 | const char *ev_name, | ||
93 | const char *field_name) | ||
94 | { | ||
95 | define_symbolic_value(ev_name, field_name, field->value, field->str); | ||
96 | if (field->next) | ||
97 | define_symbolic_values(field->next, ev_name, field_name); | ||
98 | } | ||
99 | |||
100 | static void define_symbolic_field(const char *ev_name, | ||
101 | const char *field_name) | ||
102 | { | ||
103 | dSP; | ||
104 | |||
105 | ENTER; | ||
106 | SAVETMPS; | ||
107 | PUSHMARK(SP); | ||
108 | |||
109 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
110 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
111 | |||
112 | PUTBACK; | ||
113 | if (get_cv("main::define_symbolic_field", 0)) | ||
114 | call_pv("main::define_symbolic_field", G_SCALAR); | ||
115 | SPAGAIN; | ||
116 | PUTBACK; | ||
117 | FREETMPS; | ||
118 | LEAVE; | ||
119 | } | ||
120 | |||
121 | static void define_flag_value(const char *ev_name, | ||
122 | const char *field_name, | ||
123 | const char *field_value, | ||
124 | const char *field_str) | ||
125 | { | ||
126 | unsigned long long value; | ||
127 | dSP; | ||
128 | |||
129 | value = eval_flag(field_value); | ||
130 | |||
131 | ENTER; | ||
132 | SAVETMPS; | ||
133 | PUSHMARK(SP); | ||
134 | |||
135 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
136 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
137 | XPUSHs(sv_2mortal(newSVuv(value))); | ||
138 | XPUSHs(sv_2mortal(newSVpv(field_str, 0))); | ||
139 | |||
140 | PUTBACK; | ||
141 | if (get_cv("main::define_flag_value", 0)) | ||
142 | call_pv("main::define_flag_value", G_SCALAR); | ||
143 | SPAGAIN; | ||
144 | PUTBACK; | ||
145 | FREETMPS; | ||
146 | LEAVE; | ||
147 | } | ||
148 | |||
149 | static void define_flag_values(struct print_flag_sym *field, | ||
150 | const char *ev_name, | ||
151 | const char *field_name) | ||
152 | { | ||
153 | define_flag_value(ev_name, field_name, field->value, field->str); | ||
154 | if (field->next) | ||
155 | define_flag_values(field->next, ev_name, field_name); | ||
156 | } | ||
157 | |||
158 | static void define_flag_field(const char *ev_name, | ||
159 | const char *field_name, | ||
160 | const char *delim) | ||
161 | { | ||
162 | dSP; | ||
163 | |||
164 | ENTER; | ||
165 | SAVETMPS; | ||
166 | PUSHMARK(SP); | ||
167 | |||
168 | XPUSHs(sv_2mortal(newSVpv(ev_name, 0))); | ||
169 | XPUSHs(sv_2mortal(newSVpv(field_name, 0))); | ||
170 | XPUSHs(sv_2mortal(newSVpv(delim, 0))); | ||
171 | |||
172 | PUTBACK; | ||
173 | if (get_cv("main::define_flag_field", 0)) | ||
174 | call_pv("main::define_flag_field", G_SCALAR); | ||
175 | SPAGAIN; | ||
176 | PUTBACK; | ||
177 | FREETMPS; | ||
178 | LEAVE; | ||
179 | } | ||
180 | |||
181 | static void define_event_symbols(struct event *event, | ||
182 | const char *ev_name, | ||
183 | struct print_arg *args) | ||
184 | { | ||
185 | switch (args->type) { | ||
186 | case PRINT_NULL: | ||
187 | break; | ||
188 | case PRINT_ATOM: | ||
189 | define_flag_value(ev_name, cur_field_name, "0", | ||
190 | args->atom.atom); | ||
191 | zero_flag_atom = 0; | ||
192 | break; | ||
193 | case PRINT_FIELD: | ||
194 | if (cur_field_name) | ||
195 | free(cur_field_name); | ||
196 | cur_field_name = strdup(args->field.name); | ||
197 | break; | ||
198 | case PRINT_FLAGS: | ||
199 | define_event_symbols(event, ev_name, args->flags.field); | ||
200 | define_flag_field(ev_name, cur_field_name, args->flags.delim); | ||
201 | define_flag_values(args->flags.flags, ev_name, cur_field_name); | ||
202 | break; | ||
203 | case PRINT_SYMBOL: | ||
204 | define_event_symbols(event, ev_name, args->symbol.field); | ||
205 | define_symbolic_field(ev_name, cur_field_name); | ||
206 | define_symbolic_values(args->symbol.symbols, ev_name, | ||
207 | cur_field_name); | ||
208 | break; | ||
209 | case PRINT_STRING: | ||
210 | break; | ||
211 | case PRINT_TYPE: | ||
212 | define_event_symbols(event, ev_name, args->typecast.item); | ||
213 | break; | ||
214 | case PRINT_OP: | ||
215 | if (strcmp(args->op.op, ":") == 0) | ||
216 | zero_flag_atom = 1; | ||
217 | define_event_symbols(event, ev_name, args->op.left); | ||
218 | define_event_symbols(event, ev_name, args->op.right); | ||
219 | break; | ||
220 | default: | ||
221 | /* we should warn... */ | ||
222 | return; | ||
223 | } | ||
224 | |||
225 | if (args->next) | ||
226 | define_event_symbols(event, ev_name, args->next); | ||
227 | } | ||
228 | |||
229 | static inline struct event *find_cache_event(int type) | ||
230 | { | ||
231 | static char ev_name[256]; | ||
232 | struct event *event; | ||
233 | |||
234 | if (events[type]) | ||
235 | return events[type]; | ||
236 | |||
237 | events[type] = event = trace_find_event(type); | ||
238 | if (!event) | ||
239 | return NULL; | ||
240 | |||
241 | sprintf(ev_name, "%s::%s", event->system, event->name); | ||
242 | |||
243 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
244 | |||
245 | return event; | ||
246 | } | ||
247 | |||
248 | static void perl_process_event(int cpu, void *data, | ||
249 | int size __unused, | ||
250 | unsigned long long nsecs, char *comm) | ||
251 | { | ||
252 | struct format_field *field; | ||
253 | static char handler[256]; | ||
254 | unsigned long long val; | ||
255 | unsigned long s, ns; | ||
256 | struct event *event; | ||
257 | int type; | ||
258 | int pid; | ||
259 | |||
260 | dSP; | ||
261 | |||
262 | type = trace_parse_common_type(data); | ||
263 | |||
264 | event = find_cache_event(type); | ||
265 | if (!event) | ||
266 | die("ug! no event found for type %d", type); | ||
267 | |||
268 | pid = trace_parse_common_pid(data); | ||
269 | |||
270 | sprintf(handler, "%s::%s", event->system, event->name); | ||
271 | |||
272 | s = nsecs / NSECS_PER_SEC; | ||
273 | ns = nsecs - s * NSECS_PER_SEC; | ||
274 | |||
275 | scripting_context->event_data = data; | ||
276 | |||
277 | ENTER; | ||
278 | SAVETMPS; | ||
279 | PUSHMARK(SP); | ||
280 | |||
281 | XPUSHs(sv_2mortal(newSVpv(handler, 0))); | ||
282 | XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context)))); | ||
283 | XPUSHs(sv_2mortal(newSVuv(cpu))); | ||
284 | XPUSHs(sv_2mortal(newSVuv(s))); | ||
285 | XPUSHs(sv_2mortal(newSVuv(ns))); | ||
286 | XPUSHs(sv_2mortal(newSViv(pid))); | ||
287 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); | ||
288 | |||
289 | /* common fields other than pid can be accessed via xsub fns */ | ||
290 | |||
291 | for (field = event->format.fields; field; field = field->next) { | ||
292 | if (field->flags & FIELD_IS_STRING) { | ||
293 | int offset; | ||
294 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
295 | offset = *(int *)(data + field->offset); | ||
296 | offset &= 0xffff; | ||
297 | } else | ||
298 | offset = field->offset; | ||
299 | XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0))); | ||
300 | } else { /* FIELD_IS_NUMERIC */ | ||
301 | val = read_size(data + field->offset, field->size); | ||
302 | if (field->flags & FIELD_IS_SIGNED) { | ||
303 | XPUSHs(sv_2mortal(newSViv(val))); | ||
304 | } else { | ||
305 | XPUSHs(sv_2mortal(newSVuv(val))); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
310 | PUTBACK; | ||
311 | |||
312 | if (get_cv(handler, 0)) | ||
313 | call_pv(handler, G_SCALAR); | ||
314 | else if (get_cv("main::trace_unhandled", 0)) { | ||
315 | XPUSHs(sv_2mortal(newSVpv(handler, 0))); | ||
316 | XPUSHs(sv_2mortal(newSViv(PTR2IV(scripting_context)))); | ||
317 | XPUSHs(sv_2mortal(newSVuv(cpu))); | ||
318 | XPUSHs(sv_2mortal(newSVuv(nsecs))); | ||
319 | XPUSHs(sv_2mortal(newSViv(pid))); | ||
320 | XPUSHs(sv_2mortal(newSVpv(comm, 0))); | ||
321 | call_pv("main::trace_unhandled", G_SCALAR); | ||
322 | } | ||
323 | SPAGAIN; | ||
324 | PUTBACK; | ||
325 | FREETMPS; | ||
326 | LEAVE; | ||
327 | } | ||
328 | |||
329 | static void run_start_sub(void) | ||
330 | { | ||
331 | dSP; /* access to Perl stack */ | ||
332 | PUSHMARK(SP); | ||
333 | |||
334 | if (get_cv("main::trace_begin", 0)) | ||
335 | call_pv("main::trace_begin", G_DISCARD | G_NOARGS); | ||
336 | } | ||
337 | |||
338 | /* | ||
339 | * Start trace script | ||
340 | */ | ||
341 | static int perl_start_script(const char *script, int argc, const char **argv) | ||
342 | { | ||
343 | const char **command_line; | ||
344 | int i, err = 0; | ||
345 | |||
346 | command_line = malloc((argc + 2) * sizeof(const char *)); | ||
347 | command_line[0] = ""; | ||
348 | command_line[1] = script; | ||
349 | for (i = 2; i < argc + 2; i++) | ||
350 | command_line[i] = argv[i - 2]; | ||
351 | |||
352 | my_perl = perl_alloc(); | ||
353 | perl_construct(my_perl); | ||
354 | |||
355 | if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line, | ||
356 | (char **)NULL)) { | ||
357 | err = -1; | ||
358 | goto error; | ||
359 | } | ||
360 | |||
361 | if (perl_run(my_perl)) { | ||
362 | err = -1; | ||
363 | goto error; | ||
364 | } | ||
365 | |||
366 | if (SvTRUE(ERRSV)) { | ||
367 | err = -1; | ||
368 | goto error; | ||
369 | } | ||
370 | |||
371 | run_start_sub(); | ||
372 | |||
373 | free(command_line); | ||
374 | fprintf(stderr, "perf trace started with Perl script %s\n\n", script); | ||
375 | return 0; | ||
376 | error: | ||
377 | perl_free(my_perl); | ||
378 | free(command_line); | ||
379 | |||
380 | return err; | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * Stop trace script | ||
385 | */ | ||
386 | static int perl_stop_script(void) | ||
387 | { | ||
388 | dSP; /* access to Perl stack */ | ||
389 | PUSHMARK(SP); | ||
390 | |||
391 | if (get_cv("main::trace_end", 0)) | ||
392 | call_pv("main::trace_end", G_DISCARD | G_NOARGS); | ||
393 | |||
394 | perl_destruct(my_perl); | ||
395 | perl_free(my_perl); | ||
396 | |||
397 | fprintf(stderr, "\nperf trace Perl script stopped\n"); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int perl_generate_script(const char *outfile) | ||
403 | { | ||
404 | struct event *event = NULL; | ||
405 | struct format_field *f; | ||
406 | char fname[PATH_MAX]; | ||
407 | int not_first, count; | ||
408 | FILE *ofp; | ||
409 | |||
410 | sprintf(fname, "%s.pl", outfile); | ||
411 | ofp = fopen(fname, "w"); | ||
412 | if (ofp == NULL) { | ||
413 | fprintf(stderr, "couldn't open %s\n", fname); | ||
414 | return -1; | ||
415 | } | ||
416 | |||
417 | fprintf(ofp, "# perf trace event handlers, " | ||
418 | "generated by perf trace -g perl\n"); | ||
419 | |||
420 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" | ||
421 | " License version 2\n\n"); | ||
422 | |||
423 | fprintf(ofp, "# The common_* event handler fields are the most useful " | ||
424 | "fields common to\n"); | ||
425 | |||
426 | fprintf(ofp, "# all events. They don't necessarily correspond to " | ||
427 | "the 'common_*' fields\n"); | ||
428 | |||
429 | fprintf(ofp, "# in the format files. Those fields not available as " | ||
430 | "handler params can\n"); | ||
431 | |||
432 | fprintf(ofp, "# be retrieved using Perl functions of the form " | ||
433 | "common_*($context).\n"); | ||
434 | |||
435 | fprintf(ofp, "# See Context.pm for the list of available " | ||
436 | "functions.\n\n"); | ||
437 | |||
438 | fprintf(ofp, "use lib \"$ENV{'PERF_EXEC_PATH'}/scripts/perl/" | ||
439 | "Perf-Trace-Util/lib\";\n"); | ||
440 | |||
441 | fprintf(ofp, "use lib \"./Perf-Trace-Util/lib\";\n"); | ||
442 | fprintf(ofp, "use Perf::Trace::Core;\n"); | ||
443 | fprintf(ofp, "use Perf::Trace::Context;\n"); | ||
444 | fprintf(ofp, "use Perf::Trace::Util;\n\n"); | ||
445 | |||
446 | fprintf(ofp, "sub trace_begin\n{\n\t# optional\n}\n\n"); | ||
447 | fprintf(ofp, "sub trace_end\n{\n\t# optional\n}\n\n"); | ||
448 | |||
449 | while ((event = trace_find_next_event(event))) { | ||
450 | fprintf(ofp, "sub %s::%s\n{\n", event->system, event->name); | ||
451 | fprintf(ofp, "\tmy ("); | ||
452 | |||
453 | fprintf(ofp, "$event_name, "); | ||
454 | fprintf(ofp, "$context, "); | ||
455 | fprintf(ofp, "$common_cpu, "); | ||
456 | fprintf(ofp, "$common_secs, "); | ||
457 | fprintf(ofp, "$common_nsecs,\n"); | ||
458 | fprintf(ofp, "\t $common_pid, "); | ||
459 | fprintf(ofp, "$common_comm,\n\t "); | ||
460 | |||
461 | not_first = 0; | ||
462 | count = 0; | ||
463 | |||
464 | for (f = event->format.fields; f; f = f->next) { | ||
465 | if (not_first++) | ||
466 | fprintf(ofp, ", "); | ||
467 | if (++count % 5 == 0) | ||
468 | fprintf(ofp, "\n\t "); | ||
469 | |||
470 | fprintf(ofp, "$%s", f->name); | ||
471 | } | ||
472 | fprintf(ofp, ") = @_;\n\n"); | ||
473 | |||
474 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " | ||
475 | "$common_secs, $common_nsecs,\n\t " | ||
476 | "$common_pid, $common_comm);\n\n"); | ||
477 | |||
478 | fprintf(ofp, "\tprintf(\""); | ||
479 | |||
480 | not_first = 0; | ||
481 | count = 0; | ||
482 | |||
483 | for (f = event->format.fields; f; f = f->next) { | ||
484 | if (not_first++) | ||
485 | fprintf(ofp, ", "); | ||
486 | if (count && count % 4 == 0) { | ||
487 | fprintf(ofp, "\".\n\t \""); | ||
488 | } | ||
489 | count++; | ||
490 | |||
491 | fprintf(ofp, "%s=", f->name); | ||
492 | if (f->flags & FIELD_IS_STRING || | ||
493 | f->flags & FIELD_IS_FLAG || | ||
494 | f->flags & FIELD_IS_SYMBOLIC) | ||
495 | fprintf(ofp, "%%s"); | ||
496 | else if (f->flags & FIELD_IS_SIGNED) | ||
497 | fprintf(ofp, "%%d"); | ||
498 | else | ||
499 | fprintf(ofp, "%%u"); | ||
500 | } | ||
501 | |||
502 | fprintf(ofp, "\\n\",\n\t "); | ||
503 | |||
504 | not_first = 0; | ||
505 | count = 0; | ||
506 | |||
507 | for (f = event->format.fields; f; f = f->next) { | ||
508 | if (not_first++) | ||
509 | fprintf(ofp, ", "); | ||
510 | |||
511 | if (++count % 5 == 0) | ||
512 | fprintf(ofp, "\n\t "); | ||
513 | |||
514 | if (f->flags & FIELD_IS_FLAG) { | ||
515 | if ((count - 1) % 5 != 0) { | ||
516 | fprintf(ofp, "\n\t "); | ||
517 | count = 4; | ||
518 | } | ||
519 | fprintf(ofp, "flag_str(\""); | ||
520 | fprintf(ofp, "%s::%s\", ", event->system, | ||
521 | event->name); | ||
522 | fprintf(ofp, "\"%s\", $%s)", f->name, | ||
523 | f->name); | ||
524 | } else if (f->flags & FIELD_IS_SYMBOLIC) { | ||
525 | if ((count - 1) % 5 != 0) { | ||
526 | fprintf(ofp, "\n\t "); | ||
527 | count = 4; | ||
528 | } | ||
529 | fprintf(ofp, "symbol_str(\""); | ||
530 | fprintf(ofp, "%s::%s\", ", event->system, | ||
531 | event->name); | ||
532 | fprintf(ofp, "\"%s\", $%s)", f->name, | ||
533 | f->name); | ||
534 | } else | ||
535 | fprintf(ofp, "$%s", f->name); | ||
536 | } | ||
537 | |||
538 | fprintf(ofp, ");\n"); | ||
539 | fprintf(ofp, "}\n\n"); | ||
540 | } | ||
541 | |||
542 | fprintf(ofp, "sub trace_unhandled\n{\n\tmy ($event_name, $context, " | ||
543 | "$common_cpu, $common_secs, $common_nsecs,\n\t " | ||
544 | "$common_pid, $common_comm) = @_;\n\n"); | ||
545 | |||
546 | fprintf(ofp, "\tprint_header($event_name, $common_cpu, " | ||
547 | "$common_secs, $common_nsecs,\n\t $common_pid, " | ||
548 | "$common_comm);\n}\n\n"); | ||
549 | |||
550 | fprintf(ofp, "sub print_header\n{\n" | ||
551 | "\tmy ($event_name, $cpu, $secs, $nsecs, $pid, $comm) = @_;\n\n" | ||
552 | "\tprintf(\"%%-20s %%5u %%05u.%%09u %%8u %%-20s \",\n\t " | ||
553 | "$event_name, $cpu, $secs, $nsecs, $pid, $comm);\n}"); | ||
554 | |||
555 | fclose(ofp); | ||
556 | |||
557 | fprintf(stderr, "generated Perl script: %s\n", fname); | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | struct scripting_ops perl_scripting_ops = { | ||
563 | .name = "Perl", | ||
564 | .start_script = perl_start_script, | ||
565 | .stop_script = perl_stop_script, | ||
566 | .process_event = perl_process_event, | ||
567 | .generate_script = perl_generate_script, | ||
568 | }; | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c new file mode 100644 index 000000000000..33a414bbba3e --- /dev/null +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -0,0 +1,573 @@ | |||
1 | /* | ||
2 | * trace-event-python. Feed trace events to an embedded Python interpreter. | ||
3 | * | ||
4 | * Copyright (C) 2010 Tom Zanussi <tzanussi@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <Python.h> | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include <ctype.h> | ||
28 | #include <errno.h> | ||
29 | |||
30 | #include "../../perf.h" | ||
31 | #include "../util.h" | ||
32 | #include "../trace-event.h" | ||
33 | |||
34 | PyMODINIT_FUNC initperf_trace_context(void); | ||
35 | |||
36 | #define FTRACE_MAX_EVENT \ | ||
37 | ((1 << (sizeof(unsigned short) * 8)) - 1) | ||
38 | |||
39 | struct event *events[FTRACE_MAX_EVENT]; | ||
40 | |||
41 | #define MAX_FIELDS 64 | ||
42 | #define N_COMMON_FIELDS 7 | ||
43 | |||
44 | extern struct scripting_context *scripting_context; | ||
45 | |||
46 | static char *cur_field_name; | ||
47 | static int zero_flag_atom; | ||
48 | |||
49 | static PyObject *main_module, *main_dict; | ||
50 | |||
51 | static void handler_call_die(const char *handler_name) | ||
52 | { | ||
53 | PyErr_Print(); | ||
54 | Py_FatalError("problem in Python trace event handler"); | ||
55 | } | ||
56 | |||
57 | static void define_value(enum print_arg_type field_type, | ||
58 | const char *ev_name, | ||
59 | const char *field_name, | ||
60 | const char *field_value, | ||
61 | const char *field_str) | ||
62 | { | ||
63 | const char *handler_name = "define_flag_value"; | ||
64 | PyObject *handler, *t, *retval; | ||
65 | unsigned long long value; | ||
66 | unsigned n = 0; | ||
67 | |||
68 | if (field_type == PRINT_SYMBOL) | ||
69 | handler_name = "define_symbolic_value"; | ||
70 | |||
71 | t = PyTuple_New(4); | ||
72 | if (!t) | ||
73 | Py_FatalError("couldn't create Python tuple"); | ||
74 | |||
75 | value = eval_flag(field_value); | ||
76 | |||
77 | PyTuple_SetItem(t, n++, PyString_FromString(ev_name)); | ||
78 | PyTuple_SetItem(t, n++, PyString_FromString(field_name)); | ||
79 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); | ||
80 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); | ||
81 | |||
82 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
83 | if (handler && PyCallable_Check(handler)) { | ||
84 | retval = PyObject_CallObject(handler, t); | ||
85 | if (retval == NULL) | ||
86 | handler_call_die(handler_name); | ||
87 | } | ||
88 | |||
89 | Py_DECREF(t); | ||
90 | } | ||
91 | |||
92 | static void define_values(enum print_arg_type field_type, | ||
93 | struct print_flag_sym *field, | ||
94 | const char *ev_name, | ||
95 | const char *field_name) | ||
96 | { | ||
97 | define_value(field_type, ev_name, field_name, field->value, | ||
98 | field->str); | ||
99 | |||
100 | if (field->next) | ||
101 | define_values(field_type, field->next, ev_name, field_name); | ||
102 | } | ||
103 | |||
104 | static void define_field(enum print_arg_type field_type, | ||
105 | const char *ev_name, | ||
106 | const char *field_name, | ||
107 | const char *delim) | ||
108 | { | ||
109 | const char *handler_name = "define_flag_field"; | ||
110 | PyObject *handler, *t, *retval; | ||
111 | unsigned n = 0; | ||
112 | |||
113 | if (field_type == PRINT_SYMBOL) | ||
114 | handler_name = "define_symbolic_field"; | ||
115 | |||
116 | if (field_type == PRINT_FLAGS) | ||
117 | t = PyTuple_New(3); | ||
118 | else | ||
119 | t = PyTuple_New(2); | ||
120 | if (!t) | ||
121 | Py_FatalError("couldn't create Python tuple"); | ||
122 | |||
123 | PyTuple_SetItem(t, n++, PyString_FromString(ev_name)); | ||
124 | PyTuple_SetItem(t, n++, PyString_FromString(field_name)); | ||
125 | if (field_type == PRINT_FLAGS) | ||
126 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); | ||
127 | |||
128 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
129 | if (handler && PyCallable_Check(handler)) { | ||
130 | retval = PyObject_CallObject(handler, t); | ||
131 | if (retval == NULL) | ||
132 | handler_call_die(handler_name); | ||
133 | } | ||
134 | |||
135 | Py_DECREF(t); | ||
136 | } | ||
137 | |||
138 | static void define_event_symbols(struct event *event, | ||
139 | const char *ev_name, | ||
140 | struct print_arg *args) | ||
141 | { | ||
142 | switch (args->type) { | ||
143 | case PRINT_NULL: | ||
144 | break; | ||
145 | case PRINT_ATOM: | ||
146 | define_value(PRINT_FLAGS, ev_name, cur_field_name, "0", | ||
147 | args->atom.atom); | ||
148 | zero_flag_atom = 0; | ||
149 | break; | ||
150 | case PRINT_FIELD: | ||
151 | if (cur_field_name) | ||
152 | free(cur_field_name); | ||
153 | cur_field_name = strdup(args->field.name); | ||
154 | break; | ||
155 | case PRINT_FLAGS: | ||
156 | define_event_symbols(event, ev_name, args->flags.field); | ||
157 | define_field(PRINT_FLAGS, ev_name, cur_field_name, | ||
158 | args->flags.delim); | ||
159 | define_values(PRINT_FLAGS, args->flags.flags, ev_name, | ||
160 | cur_field_name); | ||
161 | break; | ||
162 | case PRINT_SYMBOL: | ||
163 | define_event_symbols(event, ev_name, args->symbol.field); | ||
164 | define_field(PRINT_SYMBOL, ev_name, cur_field_name, NULL); | ||
165 | define_values(PRINT_SYMBOL, args->symbol.symbols, ev_name, | ||
166 | cur_field_name); | ||
167 | break; | ||
168 | case PRINT_STRING: | ||
169 | break; | ||
170 | case PRINT_TYPE: | ||
171 | define_event_symbols(event, ev_name, args->typecast.item); | ||
172 | break; | ||
173 | case PRINT_OP: | ||
174 | if (strcmp(args->op.op, ":") == 0) | ||
175 | zero_flag_atom = 1; | ||
176 | define_event_symbols(event, ev_name, args->op.left); | ||
177 | define_event_symbols(event, ev_name, args->op.right); | ||
178 | break; | ||
179 | default: | ||
180 | /* we should warn... */ | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | if (args->next) | ||
185 | define_event_symbols(event, ev_name, args->next); | ||
186 | } | ||
187 | |||
188 | static inline struct event *find_cache_event(int type) | ||
189 | { | ||
190 | static char ev_name[256]; | ||
191 | struct event *event; | ||
192 | |||
193 | if (events[type]) | ||
194 | return events[type]; | ||
195 | |||
196 | events[type] = event = trace_find_event(type); | ||
197 | if (!event) | ||
198 | return NULL; | ||
199 | |||
200 | sprintf(ev_name, "%s__%s", event->system, event->name); | ||
201 | |||
202 | define_event_symbols(event, ev_name, event->print_fmt.args); | ||
203 | |||
204 | return event; | ||
205 | } | ||
206 | |||
207 | static void python_process_event(int cpu, void *data, | ||
208 | int size __unused, | ||
209 | unsigned long long nsecs, char *comm) | ||
210 | { | ||
211 | PyObject *handler, *retval, *context, *t; | ||
212 | static char handler_name[256]; | ||
213 | struct format_field *field; | ||
214 | unsigned long long val; | ||
215 | unsigned long s, ns; | ||
216 | struct event *event; | ||
217 | unsigned n = 0; | ||
218 | int type; | ||
219 | int pid; | ||
220 | |||
221 | t = PyTuple_New(MAX_FIELDS); | ||
222 | if (!t) | ||
223 | Py_FatalError("couldn't create Python tuple"); | ||
224 | |||
225 | type = trace_parse_common_type(data); | ||
226 | |||
227 | event = find_cache_event(type); | ||
228 | if (!event) | ||
229 | die("ug! no event found for type %d", type); | ||
230 | |||
231 | pid = trace_parse_common_pid(data); | ||
232 | |||
233 | sprintf(handler_name, "%s__%s", event->system, event->name); | ||
234 | |||
235 | s = nsecs / NSECS_PER_SEC; | ||
236 | ns = nsecs - s * NSECS_PER_SEC; | ||
237 | |||
238 | scripting_context->event_data = data; | ||
239 | |||
240 | context = PyCObject_FromVoidPtr(scripting_context, NULL); | ||
241 | |||
242 | PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); | ||
243 | PyTuple_SetItem(t, n++, | ||
244 | PyCObject_FromVoidPtr(scripting_context, NULL)); | ||
245 | PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); | ||
246 | PyTuple_SetItem(t, n++, PyInt_FromLong(s)); | ||
247 | PyTuple_SetItem(t, n++, PyInt_FromLong(ns)); | ||
248 | PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); | ||
249 | PyTuple_SetItem(t, n++, PyString_FromString(comm)); | ||
250 | |||
251 | for (field = event->format.fields; field; field = field->next) { | ||
252 | if (field->flags & FIELD_IS_STRING) { | ||
253 | int offset; | ||
254 | if (field->flags & FIELD_IS_DYNAMIC) { | ||
255 | offset = *(int *)(data + field->offset); | ||
256 | offset &= 0xffff; | ||
257 | } else | ||
258 | offset = field->offset; | ||
259 | PyTuple_SetItem(t, n++, | ||
260 | PyString_FromString((char *)data + offset)); | ||
261 | } else { /* FIELD_IS_NUMERIC */ | ||
262 | val = read_size(data + field->offset, field->size); | ||
263 | if (field->flags & FIELD_IS_SIGNED) { | ||
264 | PyTuple_SetItem(t, n++, PyInt_FromLong(val)); | ||
265 | } else { | ||
266 | PyTuple_SetItem(t, n++, PyInt_FromLong(val)); | ||
267 | } | ||
268 | } | ||
269 | } | ||
270 | |||
271 | if (_PyTuple_Resize(&t, n) == -1) | ||
272 | Py_FatalError("error resizing Python tuple"); | ||
273 | |||
274 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
275 | if (handler && PyCallable_Check(handler)) { | ||
276 | retval = PyObject_CallObject(handler, t); | ||
277 | if (retval == NULL) | ||
278 | handler_call_die(handler_name); | ||
279 | } else { | ||
280 | handler = PyDict_GetItemString(main_dict, "trace_unhandled"); | ||
281 | if (handler && PyCallable_Check(handler)) { | ||
282 | if (_PyTuple_Resize(&t, N_COMMON_FIELDS) == -1) | ||
283 | Py_FatalError("error resizing Python tuple"); | ||
284 | |||
285 | retval = PyObject_CallObject(handler, t); | ||
286 | if (retval == NULL) | ||
287 | handler_call_die("trace_unhandled"); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | Py_DECREF(t); | ||
292 | } | ||
293 | |||
294 | static int run_start_sub(void) | ||
295 | { | ||
296 | PyObject *handler, *retval; | ||
297 | int err = 0; | ||
298 | |||
299 | main_module = PyImport_AddModule("__main__"); | ||
300 | if (main_module == NULL) | ||
301 | return -1; | ||
302 | Py_INCREF(main_module); | ||
303 | |||
304 | main_dict = PyModule_GetDict(main_module); | ||
305 | if (main_dict == NULL) { | ||
306 | err = -1; | ||
307 | goto error; | ||
308 | } | ||
309 | Py_INCREF(main_dict); | ||
310 | |||
311 | handler = PyDict_GetItemString(main_dict, "trace_begin"); | ||
312 | if (handler == NULL || !PyCallable_Check(handler)) | ||
313 | goto out; | ||
314 | |||
315 | retval = PyObject_CallObject(handler, NULL); | ||
316 | if (retval == NULL) | ||
317 | handler_call_die("trace_begin"); | ||
318 | |||
319 | Py_DECREF(retval); | ||
320 | return err; | ||
321 | error: | ||
322 | Py_XDECREF(main_dict); | ||
323 | Py_XDECREF(main_module); | ||
324 | out: | ||
325 | return err; | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * Start trace script | ||
330 | */ | ||
331 | static int python_start_script(const char *script, int argc, const char **argv) | ||
332 | { | ||
333 | const char **command_line; | ||
334 | char buf[PATH_MAX]; | ||
335 | int i, err = 0; | ||
336 | FILE *fp; | ||
337 | |||
338 | command_line = malloc((argc + 1) * sizeof(const char *)); | ||
339 | command_line[0] = script; | ||
340 | for (i = 1; i < argc + 1; i++) | ||
341 | command_line[i] = argv[i - 1]; | ||
342 | |||
343 | Py_Initialize(); | ||
344 | |||
345 | initperf_trace_context(); | ||
346 | |||
347 | PySys_SetArgv(argc + 1, (char **)command_line); | ||
348 | |||
349 | fp = fopen(script, "r"); | ||
350 | if (!fp) { | ||
351 | sprintf(buf, "Can't open python script \"%s\"", script); | ||
352 | perror(buf); | ||
353 | err = -1; | ||
354 | goto error; | ||
355 | } | ||
356 | |||
357 | err = PyRun_SimpleFile(fp, script); | ||
358 | if (err) { | ||
359 | fprintf(stderr, "Error running python script %s\n", script); | ||
360 | goto error; | ||
361 | } | ||
362 | |||
363 | err = run_start_sub(); | ||
364 | if (err) { | ||
365 | fprintf(stderr, "Error starting python script %s\n", script); | ||
366 | goto error; | ||
367 | } | ||
368 | |||
369 | free(command_line); | ||
370 | fprintf(stderr, "perf trace started with Python script %s\n\n", | ||
371 | script); | ||
372 | |||
373 | return err; | ||
374 | error: | ||
375 | Py_Finalize(); | ||
376 | free(command_line); | ||
377 | |||
378 | return err; | ||
379 | } | ||
380 | |||
381 | /* | ||
382 | * Stop trace script | ||
383 | */ | ||
384 | static int python_stop_script(void) | ||
385 | { | ||
386 | PyObject *handler, *retval; | ||
387 | int err = 0; | ||
388 | |||
389 | handler = PyDict_GetItemString(main_dict, "trace_end"); | ||
390 | if (handler == NULL || !PyCallable_Check(handler)) | ||
391 | goto out; | ||
392 | |||
393 | retval = PyObject_CallObject(handler, NULL); | ||
394 | if (retval == NULL) | ||
395 | handler_call_die("trace_end"); | ||
396 | else | ||
397 | Py_DECREF(retval); | ||
398 | out: | ||
399 | Py_XDECREF(main_dict); | ||
400 | Py_XDECREF(main_module); | ||
401 | Py_Finalize(); | ||
402 | |||
403 | fprintf(stderr, "\nperf trace Python script stopped\n"); | ||
404 | |||
405 | return err; | ||
406 | } | ||
407 | |||
408 | static int python_generate_script(const char *outfile) | ||
409 | { | ||
410 | struct event *event = NULL; | ||
411 | struct format_field *f; | ||
412 | char fname[PATH_MAX]; | ||
413 | int not_first, count; | ||
414 | FILE *ofp; | ||
415 | |||
416 | sprintf(fname, "%s.py", outfile); | ||
417 | ofp = fopen(fname, "w"); | ||
418 | if (ofp == NULL) { | ||
419 | fprintf(stderr, "couldn't open %s\n", fname); | ||
420 | return -1; | ||
421 | } | ||
422 | fprintf(ofp, "# perf trace event handlers, " | ||
423 | "generated by perf trace -g python\n"); | ||
424 | |||
425 | fprintf(ofp, "# Licensed under the terms of the GNU GPL" | ||
426 | " License version 2\n\n"); | ||
427 | |||
428 | fprintf(ofp, "# The common_* event handler fields are the most useful " | ||
429 | "fields common to\n"); | ||
430 | |||
431 | fprintf(ofp, "# all events. They don't necessarily correspond to " | ||
432 | "the 'common_*' fields\n"); | ||
433 | |||
434 | fprintf(ofp, "# in the format files. Those fields not available as " | ||
435 | "handler params can\n"); | ||
436 | |||
437 | fprintf(ofp, "# be retrieved using Python functions of the form " | ||
438 | "common_*(context).\n"); | ||
439 | |||
440 | fprintf(ofp, "# See the perf-trace-python Documentation for the list " | ||
441 | "of available functions.\n\n"); | ||
442 | |||
443 | fprintf(ofp, "import os\n"); | ||
444 | fprintf(ofp, "import sys\n\n"); | ||
445 | |||
446 | fprintf(ofp, "sys.path.append(os.environ['PERF_EXEC_PATH'] + \\\n"); | ||
447 | fprintf(ofp, "\t'/scripts/python/Perf-Trace-Util/lib/Perf/Trace')\n"); | ||
448 | fprintf(ofp, "\nfrom perf_trace_context import *\n"); | ||
449 | fprintf(ofp, "from Core import *\n\n\n"); | ||
450 | |||
451 | fprintf(ofp, "def trace_begin():\n"); | ||
452 | fprintf(ofp, "\tprint \"in trace_begin\"\n\n"); | ||
453 | |||
454 | fprintf(ofp, "def trace_end():\n"); | ||
455 | fprintf(ofp, "\tprint \"in trace_end\"\n\n"); | ||
456 | |||
457 | while ((event = trace_find_next_event(event))) { | ||
458 | fprintf(ofp, "def %s__%s(", event->system, event->name); | ||
459 | fprintf(ofp, "event_name, "); | ||
460 | fprintf(ofp, "context, "); | ||
461 | fprintf(ofp, "common_cpu,\n"); | ||
462 | fprintf(ofp, "\tcommon_secs, "); | ||
463 | fprintf(ofp, "common_nsecs, "); | ||
464 | fprintf(ofp, "common_pid, "); | ||
465 | fprintf(ofp, "common_comm,\n\t"); | ||
466 | |||
467 | not_first = 0; | ||
468 | count = 0; | ||
469 | |||
470 | for (f = event->format.fields; f; f = f->next) { | ||
471 | if (not_first++) | ||
472 | fprintf(ofp, ", "); | ||
473 | if (++count % 5 == 0) | ||
474 | fprintf(ofp, "\n\t"); | ||
475 | |||
476 | fprintf(ofp, "%s", f->name); | ||
477 | } | ||
478 | fprintf(ofp, "):\n"); | ||
479 | |||
480 | fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " | ||
481 | "common_secs, common_nsecs,\n\t\t\t" | ||
482 | "common_pid, common_comm)\n\n"); | ||
483 | |||
484 | fprintf(ofp, "\t\tprint \""); | ||
485 | |||
486 | not_first = 0; | ||
487 | count = 0; | ||
488 | |||
489 | for (f = event->format.fields; f; f = f->next) { | ||
490 | if (not_first++) | ||
491 | fprintf(ofp, ", "); | ||
492 | if (count && count % 3 == 0) { | ||
493 | fprintf(ofp, "\" \\\n\t\t\""); | ||
494 | } | ||
495 | count++; | ||
496 | |||
497 | fprintf(ofp, "%s=", f->name); | ||
498 | if (f->flags & FIELD_IS_STRING || | ||
499 | f->flags & FIELD_IS_FLAG || | ||
500 | f->flags & FIELD_IS_SYMBOLIC) | ||
501 | fprintf(ofp, "%%s"); | ||
502 | else if (f->flags & FIELD_IS_SIGNED) | ||
503 | fprintf(ofp, "%%d"); | ||
504 | else | ||
505 | fprintf(ofp, "%%u"); | ||
506 | } | ||
507 | |||
508 | fprintf(ofp, "\\n\" %% \\\n\t\t("); | ||
509 | |||
510 | not_first = 0; | ||
511 | count = 0; | ||
512 | |||
513 | for (f = event->format.fields; f; f = f->next) { | ||
514 | if (not_first++) | ||
515 | fprintf(ofp, ", "); | ||
516 | |||
517 | if (++count % 5 == 0) | ||
518 | fprintf(ofp, "\n\t\t"); | ||
519 | |||
520 | if (f->flags & FIELD_IS_FLAG) { | ||
521 | if ((count - 1) % 5 != 0) { | ||
522 | fprintf(ofp, "\n\t\t"); | ||
523 | count = 4; | ||
524 | } | ||
525 | fprintf(ofp, "flag_str(\""); | ||
526 | fprintf(ofp, "%s__%s\", ", event->system, | ||
527 | event->name); | ||
528 | fprintf(ofp, "\"%s\", %s)", f->name, | ||
529 | f->name); | ||
530 | } else if (f->flags & FIELD_IS_SYMBOLIC) { | ||
531 | if ((count - 1) % 5 != 0) { | ||
532 | fprintf(ofp, "\n\t\t"); | ||
533 | count = 4; | ||
534 | } | ||
535 | fprintf(ofp, "symbol_str(\""); | ||
536 | fprintf(ofp, "%s__%s\", ", event->system, | ||
537 | event->name); | ||
538 | fprintf(ofp, "\"%s\", %s)", f->name, | ||
539 | f->name); | ||
540 | } else | ||
541 | fprintf(ofp, "%s", f->name); | ||
542 | } | ||
543 | |||
544 | fprintf(ofp, "),\n\n"); | ||
545 | } | ||
546 | |||
547 | fprintf(ofp, "def trace_unhandled(event_name, context, " | ||
548 | "common_cpu, common_secs, common_nsecs,\n\t\t" | ||
549 | "common_pid, common_comm):\n"); | ||
550 | |||
551 | fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " | ||
552 | "common_secs, common_nsecs,\n\t\tcommon_pid, " | ||
553 | "common_comm)\n\n"); | ||
554 | |||
555 | fprintf(ofp, "def print_header(" | ||
556 | "event_name, cpu, secs, nsecs, pid, comm):\n" | ||
557 | "\tprint \"%%-20s %%5u %%05u.%%09u %%8u %%-20s \" %% \\\n\t" | ||
558 | "(event_name, cpu, secs, nsecs, pid, comm),\n"); | ||
559 | |||
560 | fclose(ofp); | ||
561 | |||
562 | fprintf(stderr, "generated Python script: %s\n", fname); | ||
563 | |||
564 | return 0; | ||
565 | } | ||
566 | |||
567 | struct scripting_ops python_scripting_ops = { | ||
568 | .name = "Python", | ||
569 | .start_script = python_start_script, | ||
570 | .stop_script = python_stop_script, | ||
571 | .process_event = python_process_event, | ||
572 | .generate_script = python_generate_script, | ||
573 | }; | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c new file mode 100644 index 000000000000..0de7258e70a5 --- /dev/null +++ b/tools/perf/util/session.c | |||
@@ -0,0 +1,573 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | ||
2 | |||
3 | #include <linux/kernel.h> | ||
4 | |||
5 | #include <byteswap.h> | ||
6 | #include <unistd.h> | ||
7 | #include <sys/types.h> | ||
8 | |||
9 | #include "session.h" | ||
10 | #include "sort.h" | ||
11 | #include "util.h" | ||
12 | |||
13 | static int perf_session__open(struct perf_session *self, bool force) | ||
14 | { | ||
15 | struct stat input_stat; | ||
16 | |||
17 | self->fd = open(self->filename, O_RDONLY); | ||
18 | if (self->fd < 0) { | ||
19 | pr_err("failed to open file: %s", self->filename); | ||
20 | if (!strcmp(self->filename, "perf.data")) | ||
21 | pr_err(" (try 'perf record' first)"); | ||
22 | pr_err("\n"); | ||
23 | return -errno; | ||
24 | } | ||
25 | |||
26 | if (fstat(self->fd, &input_stat) < 0) | ||
27 | goto out_close; | ||
28 | |||
29 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
30 | pr_err("file %s not owned by current user or root\n", | ||
31 | self->filename); | ||
32 | goto out_close; | ||
33 | } | ||
34 | |||
35 | if (!input_stat.st_size) { | ||
36 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
37 | self->filename); | ||
38 | goto out_close; | ||
39 | } | ||
40 | |||
41 | if (perf_header__read(&self->header, self->fd) < 0) { | ||
42 | pr_err("incompatible file format"); | ||
43 | goto out_close; | ||
44 | } | ||
45 | |||
46 | self->size = input_stat.st_size; | ||
47 | return 0; | ||
48 | |||
49 | out_close: | ||
50 | close(self->fd); | ||
51 | self->fd = -1; | ||
52 | return -1; | ||
53 | } | ||
54 | |||
55 | static inline int perf_session__create_kernel_maps(struct perf_session *self) | ||
56 | { | ||
57 | return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps); | ||
58 | } | ||
59 | |||
60 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | ||
61 | { | ||
62 | size_t len = filename ? strlen(filename) + 1 : 0; | ||
63 | struct perf_session *self = zalloc(sizeof(*self) + len); | ||
64 | |||
65 | if (self == NULL) | ||
66 | goto out; | ||
67 | |||
68 | if (perf_header__init(&self->header) < 0) | ||
69 | goto out_free; | ||
70 | |||
71 | memcpy(self->filename, filename, len); | ||
72 | self->threads = RB_ROOT; | ||
73 | self->last_match = NULL; | ||
74 | self->mmap_window = 32; | ||
75 | self->cwd = NULL; | ||
76 | self->cwdlen = 0; | ||
77 | self->unknown_events = 0; | ||
78 | map_groups__init(&self->kmaps); | ||
79 | |||
80 | if (mode == O_RDONLY) { | ||
81 | if (perf_session__open(self, force) < 0) | ||
82 | goto out_delete; | ||
83 | } else if (mode == O_WRONLY) { | ||
84 | /* | ||
85 | * In O_RDONLY mode this will be performed when reading the | ||
86 | * kernel MMAP event, in event__process_mmap(). | ||
87 | */ | ||
88 | if (perf_session__create_kernel_maps(self) < 0) | ||
89 | goto out_delete; | ||
90 | } | ||
91 | |||
92 | self->sample_type = perf_header__sample_type(&self->header); | ||
93 | out: | ||
94 | return self; | ||
95 | out_free: | ||
96 | free(self); | ||
97 | return NULL; | ||
98 | out_delete: | ||
99 | perf_session__delete(self); | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
103 | void perf_session__delete(struct perf_session *self) | ||
104 | { | ||
105 | perf_header__exit(&self->header); | ||
106 | close(self->fd); | ||
107 | free(self->cwd); | ||
108 | free(self); | ||
109 | } | ||
110 | |||
111 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
112 | { | ||
113 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
114 | return 1; | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | ||
120 | struct thread *thread, | ||
121 | struct ip_callchain *chain, | ||
122 | struct symbol **parent) | ||
123 | { | ||
124 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
125 | struct symbol **syms = NULL; | ||
126 | unsigned int i; | ||
127 | |||
128 | if (symbol_conf.use_callchain) { | ||
129 | syms = calloc(chain->nr, sizeof(*syms)); | ||
130 | if (!syms) { | ||
131 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
132 | exit(-1); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | for (i = 0; i < chain->nr; i++) { | ||
137 | u64 ip = chain->ips[i]; | ||
138 | struct addr_location al; | ||
139 | |||
140 | if (ip >= PERF_CONTEXT_MAX) { | ||
141 | switch (ip) { | ||
142 | case PERF_CONTEXT_HV: | ||
143 | cpumode = PERF_RECORD_MISC_HYPERVISOR; break; | ||
144 | case PERF_CONTEXT_KERNEL: | ||
145 | cpumode = PERF_RECORD_MISC_KERNEL; break; | ||
146 | case PERF_CONTEXT_USER: | ||
147 | cpumode = PERF_RECORD_MISC_USER; break; | ||
148 | default: | ||
149 | break; | ||
150 | } | ||
151 | continue; | ||
152 | } | ||
153 | |||
154 | thread__find_addr_location(thread, self, cpumode, | ||
155 | MAP__FUNCTION, ip, &al, NULL); | ||
156 | if (al.sym != NULL) { | ||
157 | if (sort__has_parent && !*parent && | ||
158 | symbol__match_parent_regex(al.sym)) | ||
159 | *parent = al.sym; | ||
160 | if (!symbol_conf.use_callchain) | ||
161 | break; | ||
162 | syms[i] = al.sym; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | return syms; | ||
167 | } | ||
168 | |||
169 | static int process_event_stub(event_t *event __used, | ||
170 | struct perf_session *session __used) | ||
171 | { | ||
172 | dump_printf(": unhandled!\n"); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | ||
177 | { | ||
178 | if (handler->sample == NULL) | ||
179 | handler->sample = process_event_stub; | ||
180 | if (handler->mmap == NULL) | ||
181 | handler->mmap = process_event_stub; | ||
182 | if (handler->comm == NULL) | ||
183 | handler->comm = process_event_stub; | ||
184 | if (handler->fork == NULL) | ||
185 | handler->fork = process_event_stub; | ||
186 | if (handler->exit == NULL) | ||
187 | handler->exit = process_event_stub; | ||
188 | if (handler->lost == NULL) | ||
189 | handler->lost = process_event_stub; | ||
190 | if (handler->read == NULL) | ||
191 | handler->read = process_event_stub; | ||
192 | if (handler->throttle == NULL) | ||
193 | handler->throttle = process_event_stub; | ||
194 | if (handler->unthrottle == NULL) | ||
195 | handler->unthrottle = process_event_stub; | ||
196 | } | ||
197 | |||
198 | static const char *event__name[] = { | ||
199 | [0] = "TOTAL", | ||
200 | [PERF_RECORD_MMAP] = "MMAP", | ||
201 | [PERF_RECORD_LOST] = "LOST", | ||
202 | [PERF_RECORD_COMM] = "COMM", | ||
203 | [PERF_RECORD_EXIT] = "EXIT", | ||
204 | [PERF_RECORD_THROTTLE] = "THROTTLE", | ||
205 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", | ||
206 | [PERF_RECORD_FORK] = "FORK", | ||
207 | [PERF_RECORD_READ] = "READ", | ||
208 | [PERF_RECORD_SAMPLE] = "SAMPLE", | ||
209 | }; | ||
210 | |||
211 | unsigned long event__total[PERF_RECORD_MAX]; | ||
212 | |||
213 | void event__print_totals(void) | ||
214 | { | ||
215 | int i; | ||
216 | for (i = 0; i < PERF_RECORD_MAX; ++i) | ||
217 | pr_info("%10s events: %10ld\n", | ||
218 | event__name[i], event__total[i]); | ||
219 | } | ||
220 | |||
221 | void mem_bswap_64(void *src, int byte_size) | ||
222 | { | ||
223 | u64 *m = src; | ||
224 | |||
225 | while (byte_size > 0) { | ||
226 | *m = bswap_64(*m); | ||
227 | byte_size -= sizeof(u64); | ||
228 | ++m; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | static void event__all64_swap(event_t *self) | ||
233 | { | ||
234 | struct perf_event_header *hdr = &self->header; | ||
235 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | ||
236 | } | ||
237 | |||
238 | static void event__comm_swap(event_t *self) | ||
239 | { | ||
240 | self->comm.pid = bswap_32(self->comm.pid); | ||
241 | self->comm.tid = bswap_32(self->comm.tid); | ||
242 | } | ||
243 | |||
244 | static void event__mmap_swap(event_t *self) | ||
245 | { | ||
246 | self->mmap.pid = bswap_32(self->mmap.pid); | ||
247 | self->mmap.tid = bswap_32(self->mmap.tid); | ||
248 | self->mmap.start = bswap_64(self->mmap.start); | ||
249 | self->mmap.len = bswap_64(self->mmap.len); | ||
250 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | ||
251 | } | ||
252 | |||
253 | static void event__task_swap(event_t *self) | ||
254 | { | ||
255 | self->fork.pid = bswap_32(self->fork.pid); | ||
256 | self->fork.tid = bswap_32(self->fork.tid); | ||
257 | self->fork.ppid = bswap_32(self->fork.ppid); | ||
258 | self->fork.ptid = bswap_32(self->fork.ptid); | ||
259 | self->fork.time = bswap_64(self->fork.time); | ||
260 | } | ||
261 | |||
262 | static void event__read_swap(event_t *self) | ||
263 | { | ||
264 | self->read.pid = bswap_32(self->read.pid); | ||
265 | self->read.tid = bswap_32(self->read.tid); | ||
266 | self->read.value = bswap_64(self->read.value); | ||
267 | self->read.time_enabled = bswap_64(self->read.time_enabled); | ||
268 | self->read.time_running = bswap_64(self->read.time_running); | ||
269 | self->read.id = bswap_64(self->read.id); | ||
270 | } | ||
271 | |||
272 | typedef void (*event__swap_op)(event_t *self); | ||
273 | |||
274 | static event__swap_op event__swap_ops[] = { | ||
275 | [PERF_RECORD_MMAP] = event__mmap_swap, | ||
276 | [PERF_RECORD_COMM] = event__comm_swap, | ||
277 | [PERF_RECORD_FORK] = event__task_swap, | ||
278 | [PERF_RECORD_EXIT] = event__task_swap, | ||
279 | [PERF_RECORD_LOST] = event__all64_swap, | ||
280 | [PERF_RECORD_READ] = event__read_swap, | ||
281 | [PERF_RECORD_SAMPLE] = event__all64_swap, | ||
282 | [PERF_RECORD_MAX] = NULL, | ||
283 | }; | ||
284 | |||
285 | static int perf_session__process_event(struct perf_session *self, | ||
286 | event_t *event, | ||
287 | struct perf_event_ops *ops, | ||
288 | u64 offset, u64 head) | ||
289 | { | ||
290 | trace_event(event); | ||
291 | |||
292 | if (event->header.type < PERF_RECORD_MAX) { | ||
293 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | ||
294 | offset + head, event->header.size, | ||
295 | event__name[event->header.type]); | ||
296 | ++event__total[0]; | ||
297 | ++event__total[event->header.type]; | ||
298 | } | ||
299 | |||
300 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | ||
301 | event__swap_ops[event->header.type](event); | ||
302 | |||
303 | switch (event->header.type) { | ||
304 | case PERF_RECORD_SAMPLE: | ||
305 | return ops->sample(event, self); | ||
306 | case PERF_RECORD_MMAP: | ||
307 | return ops->mmap(event, self); | ||
308 | case PERF_RECORD_COMM: | ||
309 | return ops->comm(event, self); | ||
310 | case PERF_RECORD_FORK: | ||
311 | return ops->fork(event, self); | ||
312 | case PERF_RECORD_EXIT: | ||
313 | return ops->exit(event, self); | ||
314 | case PERF_RECORD_LOST: | ||
315 | return ops->lost(event, self); | ||
316 | case PERF_RECORD_READ: | ||
317 | return ops->read(event, self); | ||
318 | case PERF_RECORD_THROTTLE: | ||
319 | return ops->throttle(event, self); | ||
320 | case PERF_RECORD_UNTHROTTLE: | ||
321 | return ops->unthrottle(event, self); | ||
322 | default: | ||
323 | self->unknown_events++; | ||
324 | return -1; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | void perf_event_header__bswap(struct perf_event_header *self) | ||
329 | { | ||
330 | self->type = bswap_32(self->type); | ||
331 | self->misc = bswap_16(self->misc); | ||
332 | self->size = bswap_16(self->size); | ||
333 | } | ||
334 | |||
335 | int perf_header__read_build_ids(struct perf_header *self, | ||
336 | int input, u64 offset, u64 size) | ||
337 | { | ||
338 | struct build_id_event bev; | ||
339 | char filename[PATH_MAX]; | ||
340 | u64 limit = offset + size; | ||
341 | int err = -1; | ||
342 | |||
343 | while (offset < limit) { | ||
344 | struct dso *dso; | ||
345 | ssize_t len; | ||
346 | struct list_head *head = &dsos__user; | ||
347 | |||
348 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
349 | goto out; | ||
350 | |||
351 | if (self->needs_swap) | ||
352 | perf_event_header__bswap(&bev.header); | ||
353 | |||
354 | len = bev.header.size - sizeof(bev); | ||
355 | if (read(input, filename, len) != len) | ||
356 | goto out; | ||
357 | |||
358 | if (bev.header.misc & PERF_RECORD_MISC_KERNEL) | ||
359 | head = &dsos__kernel; | ||
360 | |||
361 | dso = __dsos__findnew(head, filename); | ||
362 | if (dso != NULL) { | ||
363 | dso__set_build_id(dso, &bev.build_id); | ||
364 | if (head == &dsos__kernel && filename[0] == '[') | ||
365 | dso->kernel = 1; | ||
366 | } | ||
367 | |||
368 | offset += bev.header.size; | ||
369 | } | ||
370 | err = 0; | ||
371 | out: | ||
372 | return err; | ||
373 | } | ||
374 | |||
375 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | ||
376 | { | ||
377 | struct thread *thread = perf_session__findnew(self, 0); | ||
378 | |||
379 | if (thread == NULL || thread__set_comm(thread, "swapper")) { | ||
380 | pr_err("problem inserting idle task.\n"); | ||
381 | thread = NULL; | ||
382 | } | ||
383 | |||
384 | return thread; | ||
385 | } | ||
386 | |||
387 | int __perf_session__process_events(struct perf_session *self, | ||
388 | u64 data_offset, u64 data_size, | ||
389 | u64 file_size, struct perf_event_ops *ops) | ||
390 | { | ||
391 | int err, mmap_prot, mmap_flags; | ||
392 | u64 head, shift; | ||
393 | u64 offset = 0; | ||
394 | size_t page_size; | ||
395 | event_t *event; | ||
396 | uint32_t size; | ||
397 | char *buf; | ||
398 | |||
399 | perf_event_ops__fill_defaults(ops); | ||
400 | |||
401 | page_size = sysconf(_SC_PAGESIZE); | ||
402 | |||
403 | head = data_offset; | ||
404 | shift = page_size * (head / page_size); | ||
405 | offset += shift; | ||
406 | head -= shift; | ||
407 | |||
408 | mmap_prot = PROT_READ; | ||
409 | mmap_flags = MAP_SHARED; | ||
410 | |||
411 | if (self->header.needs_swap) { | ||
412 | mmap_prot |= PROT_WRITE; | ||
413 | mmap_flags = MAP_PRIVATE; | ||
414 | } | ||
415 | remap: | ||
416 | buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, | ||
417 | mmap_flags, self->fd, offset); | ||
418 | if (buf == MAP_FAILED) { | ||
419 | pr_err("failed to mmap file\n"); | ||
420 | err = -errno; | ||
421 | goto out_err; | ||
422 | } | ||
423 | |||
424 | more: | ||
425 | event = (event_t *)(buf + head); | ||
426 | |||
427 | if (self->header.needs_swap) | ||
428 | perf_event_header__bswap(&event->header); | ||
429 | size = event->header.size; | ||
430 | if (size == 0) | ||
431 | size = 8; | ||
432 | |||
433 | if (head + event->header.size >= page_size * self->mmap_window) { | ||
434 | int munmap_ret; | ||
435 | |||
436 | shift = page_size * (head / page_size); | ||
437 | |||
438 | munmap_ret = munmap(buf, page_size * self->mmap_window); | ||
439 | assert(munmap_ret == 0); | ||
440 | |||
441 | offset += shift; | ||
442 | head -= shift; | ||
443 | goto remap; | ||
444 | } | ||
445 | |||
446 | size = event->header.size; | ||
447 | |||
448 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
449 | offset + head, event->header.size, event->header.type); | ||
450 | |||
451 | if (size == 0 || | ||
452 | perf_session__process_event(self, event, ops, offset, head) < 0) { | ||
453 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | ||
454 | offset + head, event->header.size, | ||
455 | event->header.type); | ||
456 | /* | ||
457 | * assume we lost track of the stream, check alignment, and | ||
458 | * increment a single u64 in the hope to catch on again 'soon'. | ||
459 | */ | ||
460 | if (unlikely(head & 7)) | ||
461 | head &= ~7ULL; | ||
462 | |||
463 | size = 8; | ||
464 | } | ||
465 | |||
466 | head += size; | ||
467 | |||
468 | if (offset + head >= data_offset + data_size) | ||
469 | goto done; | ||
470 | |||
471 | if (offset + head < file_size) | ||
472 | goto more; | ||
473 | done: | ||
474 | err = 0; | ||
475 | out_err: | ||
476 | return err; | ||
477 | } | ||
478 | |||
479 | int perf_session__process_events(struct perf_session *self, | ||
480 | struct perf_event_ops *ops) | ||
481 | { | ||
482 | int err; | ||
483 | |||
484 | if (perf_session__register_idle_thread(self) == NULL) | ||
485 | return -ENOMEM; | ||
486 | |||
487 | if (!symbol_conf.full_paths) { | ||
488 | char bf[PATH_MAX]; | ||
489 | |||
490 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
491 | err = -errno; | ||
492 | out_getcwd_err: | ||
493 | pr_err("failed to get the current directory\n"); | ||
494 | goto out_err; | ||
495 | } | ||
496 | self->cwd = strdup(bf); | ||
497 | if (self->cwd == NULL) { | ||
498 | err = -ENOMEM; | ||
499 | goto out_getcwd_err; | ||
500 | } | ||
501 | self->cwdlen = strlen(self->cwd); | ||
502 | } | ||
503 | |||
504 | err = __perf_session__process_events(self, self->header.data_offset, | ||
505 | self->header.data_size, | ||
506 | self->size, ops); | ||
507 | out_err: | ||
508 | return err; | ||
509 | } | ||
510 | |||
511 | bool perf_session__has_traces(struct perf_session *self, const char *msg) | ||
512 | { | ||
513 | if (!(self->sample_type & PERF_SAMPLE_RAW)) { | ||
514 | pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); | ||
515 | return false; | ||
516 | } | ||
517 | |||
518 | return true; | ||
519 | } | ||
520 | |||
521 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
522 | const char *symbol_name, | ||
523 | u64 addr) | ||
524 | { | ||
525 | char *bracket; | ||
526 | enum map_type i; | ||
527 | |||
528 | self->ref_reloc_sym.name = strdup(symbol_name); | ||
529 | if (self->ref_reloc_sym.name == NULL) | ||
530 | return -ENOMEM; | ||
531 | |||
532 | bracket = strchr(self->ref_reloc_sym.name, ']'); | ||
533 | if (bracket) | ||
534 | *bracket = '\0'; | ||
535 | |||
536 | self->ref_reloc_sym.addr = addr; | ||
537 | |||
538 | for (i = 0; i < MAP__NR_TYPES; ++i) { | ||
539 | struct kmap *kmap = map__kmap(self->vmlinux_maps[i]); | ||
540 | kmap->ref_reloc_sym = &self->ref_reloc_sym; | ||
541 | } | ||
542 | |||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static u64 map__reloc_map_ip(struct map *map, u64 ip) | ||
547 | { | ||
548 | return ip + (s64)map->pgoff; | ||
549 | } | ||
550 | |||
551 | static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | ||
552 | { | ||
553 | return ip - (s64)map->pgoff; | ||
554 | } | ||
555 | |||
556 | void map__reloc_vmlinux(struct map *self) | ||
557 | { | ||
558 | struct kmap *kmap = map__kmap(self); | ||
559 | s64 reloc; | ||
560 | |||
561 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) | ||
562 | return; | ||
563 | |||
564 | reloc = (kmap->ref_reloc_sym->unrelocated_addr - | ||
565 | kmap->ref_reloc_sym->addr); | ||
566 | |||
567 | if (!reloc) | ||
568 | return; | ||
569 | |||
570 | self->map_ip = map__reloc_map_ip; | ||
571 | self->unmap_ip = map__reloc_unmap_ip; | ||
572 | self->pgoff = reloc; | ||
573 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h new file mode 100644 index 000000000000..31950fcd8a4d --- /dev/null +++ b/tools/perf/util/session.h | |||
@@ -0,0 +1,88 @@ | |||
1 | #ifndef __PERF_SESSION_H | ||
2 | #define __PERF_SESSION_H | ||
3 | |||
4 | #include "event.h" | ||
5 | #include "header.h" | ||
6 | #include "symbol.h" | ||
7 | #include "thread.h" | ||
8 | #include <linux/rbtree.h> | ||
9 | #include "../../../include/linux/perf_event.h" | ||
10 | |||
11 | struct ip_callchain; | ||
12 | struct thread; | ||
13 | |||
14 | struct perf_session { | ||
15 | struct perf_header header; | ||
16 | unsigned long size; | ||
17 | unsigned long mmap_window; | ||
18 | struct map_groups kmaps; | ||
19 | struct rb_root threads; | ||
20 | struct thread *last_match; | ||
21 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
22 | struct events_stats events_stats; | ||
23 | unsigned long event_total[PERF_RECORD_MAX]; | ||
24 | unsigned long unknown_events; | ||
25 | struct rb_root hists; | ||
26 | u64 sample_type; | ||
27 | struct ref_reloc_sym ref_reloc_sym; | ||
28 | int fd; | ||
29 | int cwdlen; | ||
30 | char *cwd; | ||
31 | char filename[0]; | ||
32 | }; | ||
33 | |||
34 | typedef int (*event_op)(event_t *self, struct perf_session *session); | ||
35 | |||
36 | struct perf_event_ops { | ||
37 | event_op sample, | ||
38 | mmap, | ||
39 | comm, | ||
40 | fork, | ||
41 | exit, | ||
42 | lost, | ||
43 | read, | ||
44 | throttle, | ||
45 | unthrottle; | ||
46 | }; | ||
47 | |||
48 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); | ||
49 | void perf_session__delete(struct perf_session *self); | ||
50 | |||
51 | void perf_event_header__bswap(struct perf_event_header *self); | ||
52 | |||
53 | int __perf_session__process_events(struct perf_session *self, | ||
54 | u64 data_offset, u64 data_size, u64 size, | ||
55 | struct perf_event_ops *ops); | ||
56 | int perf_session__process_events(struct perf_session *self, | ||
57 | struct perf_event_ops *event_ops); | ||
58 | |||
59 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | ||
60 | struct thread *thread, | ||
61 | struct ip_callchain *chain, | ||
62 | struct symbol **parent); | ||
63 | |||
64 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | ||
65 | |||
66 | int perf_header__read_build_ids(struct perf_header *self, int input, | ||
67 | u64 offset, u64 file_size); | ||
68 | |||
69 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
70 | const char *symbol_name, | ||
71 | u64 addr); | ||
72 | |||
73 | void mem_bswap_64(void *src, int byte_size); | ||
74 | |||
75 | static inline int __perf_session__create_kernel_maps(struct perf_session *self, | ||
76 | struct dso *kernel) | ||
77 | { | ||
78 | return __map_groups__create_kernel_maps(&self->kmaps, | ||
79 | self->vmlinux_maps, kernel); | ||
80 | } | ||
81 | |||
82 | static inline struct map * | ||
83 | perf_session__new_module_map(struct perf_session *self, | ||
84 | u64 start, const char *filename) | ||
85 | { | ||
86 | return map_groups__new_module(&self->kmaps, start, filename); | ||
87 | } | ||
88 | #endif /* __PERF_SESSION_H */ | ||
diff --git a/tools/perf/util/sigchain.h b/tools/perf/util/sigchain.h index 618083bce0c6..1a53c11265fd 100644 --- a/tools/perf/util/sigchain.h +++ b/tools/perf/util/sigchain.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef SIGCHAIN_H | 1 | #ifndef __PERF_SIGCHAIN_H |
2 | #define SIGCHAIN_H | 2 | #define __PERF_SIGCHAIN_H |
3 | 3 | ||
4 | typedef void (*sigchain_fun)(int); | 4 | typedef void (*sigchain_fun)(int); |
5 | 5 | ||
@@ -8,4 +8,4 @@ int sigchain_pop(int sig); | |||
8 | 8 | ||
9 | void sigchain_push_common(sigchain_fun f); | 9 | void sigchain_push_common(sigchain_fun f); |
10 | 10 | ||
11 | #endif /* SIGCHAIN_H */ | 11 | #endif /* __PERF_SIGCHAIN_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c new file mode 100644 index 000000000000..cb0f327de9e8 --- /dev/null +++ b/tools/perf/util/sort.c | |||
@@ -0,0 +1,316 @@ | |||
1 | #include "sort.h" | ||
2 | |||
3 | regex_t parent_regex; | ||
4 | char default_parent_pattern[] = "^sys_|^do_page_fault"; | ||
5 | char *parent_pattern = default_parent_pattern; | ||
6 | char default_sort_order[] = "comm,dso,symbol"; | ||
7 | char *sort_order = default_sort_order; | ||
8 | int sort__need_collapse = 0; | ||
9 | int sort__has_parent = 0; | ||
10 | |||
11 | enum sort_type sort__first_dimension; | ||
12 | |||
13 | unsigned int dsos__col_width; | ||
14 | unsigned int comms__col_width; | ||
15 | unsigned int threads__col_width; | ||
16 | static unsigned int parent_symbol__col_width; | ||
17 | char * field_sep; | ||
18 | |||
19 | LIST_HEAD(hist_entry__sort_list); | ||
20 | |||
21 | struct sort_entry sort_thread = { | ||
22 | .header = "Command: Pid", | ||
23 | .cmp = sort__thread_cmp, | ||
24 | .print = sort__thread_print, | ||
25 | .width = &threads__col_width, | ||
26 | }; | ||
27 | |||
28 | struct sort_entry sort_comm = { | ||
29 | .header = "Command", | ||
30 | .cmp = sort__comm_cmp, | ||
31 | .collapse = sort__comm_collapse, | ||
32 | .print = sort__comm_print, | ||
33 | .width = &comms__col_width, | ||
34 | }; | ||
35 | |||
36 | struct sort_entry sort_dso = { | ||
37 | .header = "Shared Object", | ||
38 | .cmp = sort__dso_cmp, | ||
39 | .print = sort__dso_print, | ||
40 | .width = &dsos__col_width, | ||
41 | }; | ||
42 | |||
43 | struct sort_entry sort_sym = { | ||
44 | .header = "Symbol", | ||
45 | .cmp = sort__sym_cmp, | ||
46 | .print = sort__sym_print, | ||
47 | }; | ||
48 | |||
49 | struct sort_entry sort_parent = { | ||
50 | .header = "Parent symbol", | ||
51 | .cmp = sort__parent_cmp, | ||
52 | .print = sort__parent_print, | ||
53 | .width = &parent_symbol__col_width, | ||
54 | }; | ||
55 | |||
56 | struct sort_dimension { | ||
57 | const char *name; | ||
58 | struct sort_entry *entry; | ||
59 | int taken; | ||
60 | }; | ||
61 | |||
62 | static struct sort_dimension sort_dimensions[] = { | ||
63 | { .name = "pid", .entry = &sort_thread, }, | ||
64 | { .name = "comm", .entry = &sort_comm, }, | ||
65 | { .name = "dso", .entry = &sort_dso, }, | ||
66 | { .name = "symbol", .entry = &sort_sym, }, | ||
67 | { .name = "parent", .entry = &sort_parent, }, | ||
68 | }; | ||
69 | |||
70 | int64_t cmp_null(void *l, void *r) | ||
71 | { | ||
72 | if (!l && !r) | ||
73 | return 0; | ||
74 | else if (!l) | ||
75 | return -1; | ||
76 | else | ||
77 | return 1; | ||
78 | } | ||
79 | |||
80 | /* --sort pid */ | ||
81 | |||
82 | int64_t | ||
83 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
84 | { | ||
85 | return right->thread->pid - left->thread->pid; | ||
86 | } | ||
87 | |||
88 | int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
89 | { | ||
90 | int n; | ||
91 | va_list ap; | ||
92 | |||
93 | va_start(ap, fmt); | ||
94 | if (!field_sep) | ||
95 | n = vfprintf(fp, fmt, ap); | ||
96 | else { | ||
97 | char *bf = NULL; | ||
98 | n = vasprintf(&bf, fmt, ap); | ||
99 | if (n > 0) { | ||
100 | char *sep = bf; | ||
101 | |||
102 | while (1) { | ||
103 | sep = strchr(sep, *field_sep); | ||
104 | if (sep == NULL) | ||
105 | break; | ||
106 | *sep = '.'; | ||
107 | } | ||
108 | } | ||
109 | fputs(bf, fp); | ||
110 | free(bf); | ||
111 | } | ||
112 | va_end(ap); | ||
113 | return n; | ||
114 | } | ||
115 | |||
116 | size_t | ||
117 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
118 | { | ||
119 | return repsep_fprintf(fp, "%*s:%5d", width - 6, | ||
120 | self->thread->comm ?: "", self->thread->pid); | ||
121 | } | ||
122 | |||
123 | size_t | ||
124 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
125 | { | ||
126 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); | ||
127 | } | ||
128 | |||
129 | /* --sort dso */ | ||
130 | |||
131 | int64_t | ||
132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | ||
133 | { | ||
134 | struct dso *dso_l = left->map ? left->map->dso : NULL; | ||
135 | struct dso *dso_r = right->map ? right->map->dso : NULL; | ||
136 | const char *dso_name_l, *dso_name_r; | ||
137 | |||
138 | if (!dso_l || !dso_r) | ||
139 | return cmp_null(dso_l, dso_r); | ||
140 | |||
141 | if (verbose) { | ||
142 | dso_name_l = dso_l->long_name; | ||
143 | dso_name_r = dso_r->long_name; | ||
144 | } else { | ||
145 | dso_name_l = dso_l->short_name; | ||
146 | dso_name_r = dso_r->short_name; | ||
147 | } | ||
148 | |||
149 | return strcmp(dso_name_l, dso_name_r); | ||
150 | } | ||
151 | |||
152 | size_t | ||
153 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
154 | { | ||
155 | if (self->map && self->map->dso) { | ||
156 | const char *dso_name = !verbose ? self->map->dso->short_name : | ||
157 | self->map->dso->long_name; | ||
158 | return repsep_fprintf(fp, "%-*s", width, dso_name); | ||
159 | } | ||
160 | |||
161 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); | ||
162 | } | ||
163 | |||
164 | /* --sort symbol */ | ||
165 | |||
166 | int64_t | ||
167 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
168 | { | ||
169 | u64 ip_l, ip_r; | ||
170 | |||
171 | if (left->sym == right->sym) | ||
172 | return 0; | ||
173 | |||
174 | ip_l = left->sym ? left->sym->start : left->ip; | ||
175 | ip_r = right->sym ? right->sym->start : right->ip; | ||
176 | |||
177 | return (int64_t)(ip_r - ip_l); | ||
178 | } | ||
179 | |||
180 | |||
181 | size_t | ||
182 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | ||
183 | { | ||
184 | size_t ret = 0; | ||
185 | |||
186 | if (verbose) { | ||
187 | char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; | ||
188 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); | ||
189 | } | ||
190 | |||
191 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
192 | if (self->sym) | ||
193 | ret += repsep_fprintf(fp, "%s", self->sym->name); | ||
194 | else | ||
195 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); | ||
196 | |||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | /* --sort comm */ | ||
201 | |||
202 | int64_t | ||
203 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
204 | { | ||
205 | return right->thread->pid - left->thread->pid; | ||
206 | } | ||
207 | |||
208 | int64_t | ||
209 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
210 | { | ||
211 | char *comm_l = left->thread->comm; | ||
212 | char *comm_r = right->thread->comm; | ||
213 | |||
214 | if (!comm_l || !comm_r) | ||
215 | return cmp_null(comm_l, comm_r); | ||
216 | |||
217 | return strcmp(comm_l, comm_r); | ||
218 | } | ||
219 | |||
220 | /* --sort parent */ | ||
221 | |||
222 | int64_t | ||
223 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | ||
224 | { | ||
225 | struct symbol *sym_l = left->parent; | ||
226 | struct symbol *sym_r = right->parent; | ||
227 | |||
228 | if (!sym_l || !sym_r) | ||
229 | return cmp_null(sym_l, sym_r); | ||
230 | |||
231 | return strcmp(sym_l->name, sym_r->name); | ||
232 | } | ||
233 | |||
234 | size_t | ||
235 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) | ||
236 | { | ||
237 | return repsep_fprintf(fp, "%-*s", width, | ||
238 | self->parent ? self->parent->name : "[other]"); | ||
239 | } | ||
240 | |||
241 | int sort_dimension__add(const char *tok) | ||
242 | { | ||
243 | unsigned int i; | ||
244 | |||
245 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | ||
246 | struct sort_dimension *sd = &sort_dimensions[i]; | ||
247 | |||
248 | if (sd->taken) | ||
249 | continue; | ||
250 | |||
251 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
252 | continue; | ||
253 | |||
254 | if (sd->entry->collapse) | ||
255 | sort__need_collapse = 1; | ||
256 | |||
257 | if (sd->entry == &sort_parent) { | ||
258 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | ||
259 | if (ret) { | ||
260 | char err[BUFSIZ]; | ||
261 | |||
262 | regerror(ret, &parent_regex, err, sizeof(err)); | ||
263 | fprintf(stderr, "Invalid regex: %s\n%s", | ||
264 | parent_pattern, err); | ||
265 | exit(-1); | ||
266 | } | ||
267 | sort__has_parent = 1; | ||
268 | } | ||
269 | |||
270 | if (list_empty(&hist_entry__sort_list)) { | ||
271 | if (!strcmp(sd->name, "pid")) | ||
272 | sort__first_dimension = SORT_PID; | ||
273 | else if (!strcmp(sd->name, "comm")) | ||
274 | sort__first_dimension = SORT_COMM; | ||
275 | else if (!strcmp(sd->name, "dso")) | ||
276 | sort__first_dimension = SORT_DSO; | ||
277 | else if (!strcmp(sd->name, "symbol")) | ||
278 | sort__first_dimension = SORT_SYM; | ||
279 | else if (!strcmp(sd->name, "parent")) | ||
280 | sort__first_dimension = SORT_PARENT; | ||
281 | } | ||
282 | |||
283 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
284 | sd->taken = 1; | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | return -ESRCH; | ||
290 | } | ||
291 | |||
292 | void setup_sorting(const char * const usagestr[], const struct option *opts) | ||
293 | { | ||
294 | char *tmp, *tok, *str = strdup(sort_order); | ||
295 | |||
296 | for (tok = strtok_r(str, ", ", &tmp); | ||
297 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
298 | if (sort_dimension__add(tok) < 0) { | ||
299 | error("Unknown --sort key: `%s'", tok); | ||
300 | usage_with_options(usagestr, opts); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | free(str); | ||
305 | } | ||
306 | |||
307 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | ||
308 | const char *list_name, FILE *fp) | ||
309 | { | ||
310 | if (list && strlist__nr_entries(list) == 1) { | ||
311 | if (fp != NULL) | ||
312 | fprintf(fp, "# %s: %s\n", list_name, | ||
313 | strlist__entry(list, 0)->s); | ||
314 | self->elide = true; | ||
315 | } | ||
316 | } | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h new file mode 100644 index 000000000000..753f9ea99fb0 --- /dev/null +++ b/tools/perf/util/sort.h | |||
@@ -0,0 +1,107 @@ | |||
1 | #ifndef __PERF_SORT_H | ||
2 | #define __PERF_SORT_H | ||
3 | #include "../builtin.h" | ||
4 | |||
5 | #include "util.h" | ||
6 | |||
7 | #include "color.h" | ||
8 | #include <linux/list.h> | ||
9 | #include "cache.h" | ||
10 | #include <linux/rbtree.h> | ||
11 | #include "symbol.h" | ||
12 | #include "string.h" | ||
13 | #include "callchain.h" | ||
14 | #include "strlist.h" | ||
15 | #include "values.h" | ||
16 | |||
17 | #include "../perf.h" | ||
18 | #include "debug.h" | ||
19 | #include "header.h" | ||
20 | |||
21 | #include "parse-options.h" | ||
22 | #include "parse-events.h" | ||
23 | |||
24 | #include "thread.h" | ||
25 | #include "sort.h" | ||
26 | |||
27 | extern regex_t parent_regex; | ||
28 | extern char *sort_order; | ||
29 | extern char default_parent_pattern[]; | ||
30 | extern char *parent_pattern; | ||
31 | extern char default_sort_order[]; | ||
32 | extern int sort__need_collapse; | ||
33 | extern int sort__has_parent; | ||
34 | extern char *field_sep; | ||
35 | extern struct sort_entry sort_comm; | ||
36 | extern struct sort_entry sort_dso; | ||
37 | extern struct sort_entry sort_sym; | ||
38 | extern struct sort_entry sort_parent; | ||
39 | extern unsigned int dsos__col_width; | ||
40 | extern unsigned int comms__col_width; | ||
41 | extern unsigned int threads__col_width; | ||
42 | extern enum sort_type sort__first_dimension; | ||
43 | |||
44 | struct hist_entry { | ||
45 | struct rb_node rb_node; | ||
46 | u64 count; | ||
47 | struct thread *thread; | ||
48 | struct map *map; | ||
49 | struct symbol *sym; | ||
50 | u64 ip; | ||
51 | char level; | ||
52 | struct symbol *parent; | ||
53 | struct callchain_node callchain; | ||
54 | union { | ||
55 | unsigned long position; | ||
56 | struct hist_entry *pair; | ||
57 | struct rb_root sorted_chain; | ||
58 | }; | ||
59 | }; | ||
60 | |||
61 | enum sort_type { | ||
62 | SORT_PID, | ||
63 | SORT_COMM, | ||
64 | SORT_DSO, | ||
65 | SORT_SYM, | ||
66 | SORT_PARENT | ||
67 | }; | ||
68 | |||
69 | /* | ||
70 | * configurable sorting bits | ||
71 | */ | ||
72 | |||
73 | struct sort_entry { | ||
74 | struct list_head list; | ||
75 | |||
76 | const char *header; | ||
77 | |||
78 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | ||
79 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | ||
80 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); | ||
81 | unsigned int *width; | ||
82 | bool elide; | ||
83 | }; | ||
84 | |||
85 | extern struct sort_entry sort_thread; | ||
86 | extern struct list_head hist_entry__sort_list; | ||
87 | |||
88 | void setup_sorting(const char * const usagestr[], const struct option *opts); | ||
89 | |||
90 | extern int repsep_fprintf(FILE *fp, const char *fmt, ...); | ||
91 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | ||
92 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | ||
93 | extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); | ||
94 | extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used); | ||
95 | extern int64_t cmp_null(void *, void *); | ||
96 | extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *); | ||
97 | extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *); | ||
98 | extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | ||
99 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | ||
100 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | ||
101 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | ||
102 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | ||
103 | extern int sort_dimension__add(const char *); | ||
104 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | ||
105 | const char *list_name, FILE *fp); | ||
106 | |||
107 | #endif /* __PERF_SORT_H */ | ||
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h index d2aa86c014c1..a3d121d6c83e 100644 --- a/tools/perf/util/strbuf.h +++ b/tools/perf/util/strbuf.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef STRBUF_H | 1 | #ifndef __PERF_STRBUF_H |
2 | #define STRBUF_H | 2 | #define __PERF_STRBUF_H |
3 | 3 | ||
4 | /* | 4 | /* |
5 | * Strbuf's can be use in many ways: as a byte array, or to store arbitrary | 5 | * Strbuf's can be use in many ways: as a byte array, or to store arbitrary |
@@ -134,4 +134,4 @@ extern int launch_editor(const char *path, struct strbuf *buffer, const char *co | |||
134 | extern int strbuf_branchname(struct strbuf *sb, const char *name); | 134 | extern int strbuf_branchname(struct strbuf *sb, const char *name); |
135 | extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); | 135 | extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name); |
136 | 136 | ||
137 | #endif /* STRBUF_H */ | 137 | #endif /* __PERF_STRBUF_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index c93eca9a7be3..a175949ed216 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "string.h" | 1 | #include "string.h" |
2 | #include "util.h" | ||
2 | 3 | ||
3 | static int hex(char ch) | 4 | static int hex(char ch) |
4 | { | 5 | { |
@@ -32,3 +33,307 @@ int hex2u64(const char *ptr, u64 *long_val) | |||
32 | 33 | ||
33 | return p - ptr; | 34 | return p - ptr; |
34 | } | 35 | } |
36 | |||
37 | char *strxfrchar(char *s, char from, char to) | ||
38 | { | ||
39 | char *p = s; | ||
40 | |||
41 | while ((p = strchr(p, from)) != NULL) | ||
42 | *p++ = to; | ||
43 | |||
44 | return s; | ||
45 | } | ||
46 | |||
47 | #define K 1024LL | ||
48 | /* | ||
49 | * perf_atoll() | ||
50 | * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB") | ||
51 | * and return its numeric value | ||
52 | */ | ||
53 | s64 perf_atoll(const char *str) | ||
54 | { | ||
55 | unsigned int i; | ||
56 | s64 length = -1, unit = 1; | ||
57 | |||
58 | if (!isdigit(str[0])) | ||
59 | goto out_err; | ||
60 | |||
61 | for (i = 1; i < strlen(str); i++) { | ||
62 | switch (str[i]) { | ||
63 | case 'B': | ||
64 | case 'b': | ||
65 | break; | ||
66 | case 'K': | ||
67 | if (str[i + 1] != 'B') | ||
68 | goto out_err; | ||
69 | else | ||
70 | goto kilo; | ||
71 | case 'k': | ||
72 | if (str[i + 1] != 'b') | ||
73 | goto out_err; | ||
74 | kilo: | ||
75 | unit = K; | ||
76 | break; | ||
77 | case 'M': | ||
78 | if (str[i + 1] != 'B') | ||
79 | goto out_err; | ||
80 | else | ||
81 | goto mega; | ||
82 | case 'm': | ||
83 | if (str[i + 1] != 'b') | ||
84 | goto out_err; | ||
85 | mega: | ||
86 | unit = K * K; | ||
87 | break; | ||
88 | case 'G': | ||
89 | if (str[i + 1] != 'B') | ||
90 | goto out_err; | ||
91 | else | ||
92 | goto giga; | ||
93 | case 'g': | ||
94 | if (str[i + 1] != 'b') | ||
95 | goto out_err; | ||
96 | giga: | ||
97 | unit = K * K * K; | ||
98 | break; | ||
99 | case 'T': | ||
100 | if (str[i + 1] != 'B') | ||
101 | goto out_err; | ||
102 | else | ||
103 | goto tera; | ||
104 | case 't': | ||
105 | if (str[i + 1] != 'b') | ||
106 | goto out_err; | ||
107 | tera: | ||
108 | unit = K * K * K * K; | ||
109 | break; | ||
110 | case '\0': /* only specified figures */ | ||
111 | unit = 1; | ||
112 | break; | ||
113 | default: | ||
114 | if (!isdigit(str[i])) | ||
115 | goto out_err; | ||
116 | break; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | length = atoll(str) * unit; | ||
121 | goto out; | ||
122 | |||
123 | out_err: | ||
124 | length = -1; | ||
125 | out: | ||
126 | return length; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Helper function for splitting a string into an argv-like array. | ||
131 | * originaly copied from lib/argv_split.c | ||
132 | */ | ||
133 | static const char *skip_sep(const char *cp) | ||
134 | { | ||
135 | while (*cp && isspace(*cp)) | ||
136 | cp++; | ||
137 | |||
138 | return cp; | ||
139 | } | ||
140 | |||
141 | static const char *skip_arg(const char *cp) | ||
142 | { | ||
143 | while (*cp && !isspace(*cp)) | ||
144 | cp++; | ||
145 | |||
146 | return cp; | ||
147 | } | ||
148 | |||
149 | static int count_argc(const char *str) | ||
150 | { | ||
151 | int count = 0; | ||
152 | |||
153 | while (*str) { | ||
154 | str = skip_sep(str); | ||
155 | if (*str) { | ||
156 | count++; | ||
157 | str = skip_arg(str); | ||
158 | } | ||
159 | } | ||
160 | |||
161 | return count; | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * argv_free - free an argv | ||
166 | * @argv - the argument vector to be freed | ||
167 | * | ||
168 | * Frees an argv and the strings it points to. | ||
169 | */ | ||
170 | void argv_free(char **argv) | ||
171 | { | ||
172 | char **p; | ||
173 | for (p = argv; *p; p++) | ||
174 | free(*p); | ||
175 | |||
176 | free(argv); | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * argv_split - split a string at whitespace, returning an argv | ||
181 | * @str: the string to be split | ||
182 | * @argcp: returned argument count | ||
183 | * | ||
184 | * Returns an array of pointers to strings which are split out from | ||
185 | * @str. This is performed by strictly splitting on white-space; no | ||
186 | * quote processing is performed. Multiple whitespace characters are | ||
187 | * considered to be a single argument separator. The returned array | ||
188 | * is always NULL-terminated. Returns NULL on memory allocation | ||
189 | * failure. | ||
190 | */ | ||
191 | char **argv_split(const char *str, int *argcp) | ||
192 | { | ||
193 | int argc = count_argc(str); | ||
194 | char **argv = zalloc(sizeof(*argv) * (argc+1)); | ||
195 | char **argvp; | ||
196 | |||
197 | if (argv == NULL) | ||
198 | goto out; | ||
199 | |||
200 | if (argcp) | ||
201 | *argcp = argc; | ||
202 | |||
203 | argvp = argv; | ||
204 | |||
205 | while (*str) { | ||
206 | str = skip_sep(str); | ||
207 | |||
208 | if (*str) { | ||
209 | const char *p = str; | ||
210 | char *t; | ||
211 | |||
212 | str = skip_arg(str); | ||
213 | |||
214 | t = strndup(p, str-p); | ||
215 | if (t == NULL) | ||
216 | goto fail; | ||
217 | *argvp++ = t; | ||
218 | } | ||
219 | } | ||
220 | *argvp = NULL; | ||
221 | |||
222 | out: | ||
223 | return argv; | ||
224 | |||
225 | fail: | ||
226 | argv_free(argv); | ||
227 | return NULL; | ||
228 | } | ||
229 | |||
230 | /* Character class matching */ | ||
231 | static bool __match_charclass(const char *pat, char c, const char **npat) | ||
232 | { | ||
233 | bool complement = false, ret = true; | ||
234 | |||
235 | if (*pat == '!') { | ||
236 | complement = true; | ||
237 | pat++; | ||
238 | } | ||
239 | if (*pat++ == c) /* First character is special */ | ||
240 | goto end; | ||
241 | |||
242 | while (*pat && *pat != ']') { /* Matching */ | ||
243 | if (*pat == '-' && *(pat + 1) != ']') { /* Range */ | ||
244 | if (*(pat - 1) <= c && c <= *(pat + 1)) | ||
245 | goto end; | ||
246 | if (*(pat - 1) > *(pat + 1)) | ||
247 | goto error; | ||
248 | pat += 2; | ||
249 | } else if (*pat++ == c) | ||
250 | goto end; | ||
251 | } | ||
252 | if (!*pat) | ||
253 | goto error; | ||
254 | ret = false; | ||
255 | |||
256 | end: | ||
257 | while (*pat && *pat != ']') /* Searching closing */ | ||
258 | pat++; | ||
259 | if (!*pat) | ||
260 | goto error; | ||
261 | *npat = pat + 1; | ||
262 | return complement ? !ret : ret; | ||
263 | |||
264 | error: | ||
265 | return false; | ||
266 | } | ||
267 | |||
268 | /* Glob/lazy pattern matching */ | ||
269 | static bool __match_glob(const char *str, const char *pat, bool ignore_space) | ||
270 | { | ||
271 | while (*str && *pat && *pat != '*') { | ||
272 | if (ignore_space) { | ||
273 | /* Ignore spaces for lazy matching */ | ||
274 | if (isspace(*str)) { | ||
275 | str++; | ||
276 | continue; | ||
277 | } | ||
278 | if (isspace(*pat)) { | ||
279 | pat++; | ||
280 | continue; | ||
281 | } | ||
282 | } | ||
283 | if (*pat == '?') { /* Matches any single character */ | ||
284 | str++; | ||
285 | pat++; | ||
286 | continue; | ||
287 | } else if (*pat == '[') /* Character classes/Ranges */ | ||
288 | if (__match_charclass(pat + 1, *str, &pat)) { | ||
289 | str++; | ||
290 | continue; | ||
291 | } else | ||
292 | return false; | ||
293 | else if (*pat == '\\') /* Escaped char match as normal char */ | ||
294 | pat++; | ||
295 | if (*str++ != *pat++) | ||
296 | return false; | ||
297 | } | ||
298 | /* Check wild card */ | ||
299 | if (*pat == '*') { | ||
300 | while (*pat == '*') | ||
301 | pat++; | ||
302 | if (!*pat) /* Tail wild card matches all */ | ||
303 | return true; | ||
304 | while (*str) | ||
305 | if (strglobmatch(str++, pat)) | ||
306 | return true; | ||
307 | } | ||
308 | return !*str && !*pat; | ||
309 | } | ||
310 | |||
311 | /** | ||
312 | * strglobmatch - glob expression pattern matching | ||
313 | * @str: the target string to match | ||
314 | * @pat: the pattern string to match | ||
315 | * | ||
316 | * This returns true if the @str matches @pat. @pat can includes wildcards | ||
317 | * ('*','?') and character classes ([CHARS], complementation and ranges are | ||
318 | * also supported). Also, this supports escape character ('\') to use special | ||
319 | * characters as normal character. | ||
320 | * | ||
321 | * Note: if @pat syntax is broken, this always returns false. | ||
322 | */ | ||
323 | bool strglobmatch(const char *str, const char *pat) | ||
324 | { | ||
325 | return __match_glob(str, pat, false); | ||
326 | } | ||
327 | |||
328 | /** | ||
329 | * strlazymatch - matching pattern strings lazily with glob pattern | ||
330 | * @str: the target string to match | ||
331 | * @pat: the pattern string to match | ||
332 | * | ||
333 | * This is similar to strglobmatch, except this ignores spaces in | ||
334 | * the target string. | ||
335 | */ | ||
336 | bool strlazymatch(const char *str, const char *pat) | ||
337 | { | ||
338 | return __match_glob(str, pat, true); | ||
339 | } | ||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index bf39dfadfd24..542e44de3719 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
@@ -1,11 +1,18 @@ | |||
1 | #ifndef _PERF_STRING_H_ | 1 | #ifndef __PERF_STRING_H_ |
2 | #define _PERF_STRING_H_ | 2 | #define __PERF_STRING_H_ |
3 | 3 | ||
4 | #include <stdbool.h> | ||
4 | #include "types.h" | 5 | #include "types.h" |
5 | 6 | ||
6 | int hex2u64(const char *ptr, u64 *val); | 7 | int hex2u64(const char *ptr, u64 *val); |
8 | char *strxfrchar(char *s, char from, char to); | ||
9 | s64 perf_atoll(const char *str); | ||
10 | char **argv_split(const char *str, int *argcp); | ||
11 | void argv_free(char **argv); | ||
12 | bool strglobmatch(const char *str, const char *pat); | ||
13 | bool strlazymatch(const char *str, const char *pat); | ||
7 | 14 | ||
8 | #define _STR(x) #x | 15 | #define _STR(x) #x |
9 | #define STR(x) _STR(x) | 16 | #define STR(x) _STR(x) |
10 | 17 | ||
11 | #endif | 18 | #endif /* __PERF_STRING_H */ |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 7ad38171dc2b..6783a2043555 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
@@ -102,7 +102,7 @@ void strlist__remove(struct strlist *self, struct str_node *sn) | |||
102 | str_node__delete(sn, self->dupstr); | 102 | str_node__delete(sn, self->dupstr); |
103 | } | 103 | } |
104 | 104 | ||
105 | bool strlist__has_entry(struct strlist *self, const char *entry) | 105 | struct str_node *strlist__find(struct strlist *self, const char *entry) |
106 | { | 106 | { |
107 | struct rb_node **p = &self->entries.rb_node; | 107 | struct rb_node **p = &self->entries.rb_node; |
108 | struct rb_node *parent = NULL; | 108 | struct rb_node *parent = NULL; |
@@ -120,10 +120,10 @@ bool strlist__has_entry(struct strlist *self, const char *entry) | |||
120 | else if (rc < 0) | 120 | else if (rc < 0) |
121 | p = &(*p)->rb_right; | 121 | p = &(*p)->rb_right; |
122 | else | 122 | else |
123 | return true; | 123 | return sn; |
124 | } | 124 | } |
125 | 125 | ||
126 | return false; | 126 | return NULL; |
127 | } | 127 | } |
128 | 128 | ||
129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) | 129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) |
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 921818e44a54..3ba839007d2c 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef STRLIST_H_ | 1 | #ifndef __PERF_STRLIST_H |
2 | #define STRLIST_H_ | 2 | #define __PERF_STRLIST_H |
3 | 3 | ||
4 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
@@ -23,7 +23,12 @@ int strlist__load(struct strlist *self, const char *filename); | |||
23 | int strlist__add(struct strlist *self, const char *str); | 23 | int strlist__add(struct strlist *self, const char *str); |
24 | 24 | ||
25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); |
26 | bool strlist__has_entry(struct strlist *self, const char *entry); | 26 | struct str_node *strlist__find(struct strlist *self, const char *entry); |
27 | |||
28 | static inline bool strlist__has_entry(struct strlist *self, const char *entry) | ||
29 | { | ||
30 | return strlist__find(self, entry) != NULL; | ||
31 | } | ||
27 | 32 | ||
28 | static inline bool strlist__empty(const struct strlist *self) | 33 | static inline bool strlist__empty(const struct strlist *self) |
29 | { | 34 | { |
@@ -35,5 +40,39 @@ static inline unsigned int strlist__nr_entries(const struct strlist *self) | |||
35 | return self->nr_entries; | 40 | return self->nr_entries; |
36 | } | 41 | } |
37 | 42 | ||
43 | /* For strlist iteration */ | ||
44 | static inline struct str_node *strlist__first(struct strlist *self) | ||
45 | { | ||
46 | struct rb_node *rn = rb_first(&self->entries); | ||
47 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | ||
48 | } | ||
49 | static inline struct str_node *strlist__next(struct str_node *sn) | ||
50 | { | ||
51 | struct rb_node *rn; | ||
52 | if (!sn) | ||
53 | return NULL; | ||
54 | rn = rb_next(&sn->rb_node); | ||
55 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | ||
56 | } | ||
57 | |||
58 | /** | ||
59 | * strlist_for_each - iterate over a strlist | ||
60 | * @pos: the &struct str_node to use as a loop cursor. | ||
61 | * @self: the &struct strlist for loop. | ||
62 | */ | ||
63 | #define strlist__for_each(pos, self) \ | ||
64 | for (pos = strlist__first(self); pos; pos = strlist__next(pos)) | ||
65 | |||
66 | /** | ||
67 | * strlist_for_each_safe - iterate over a strlist safe against removal of | ||
68 | * str_node | ||
69 | * @pos: the &struct str_node to use as a loop cursor. | ||
70 | * @n: another &struct str_node to use as temporary storage. | ||
71 | * @self: the &struct strlist for loop. | ||
72 | */ | ||
73 | #define strlist__for_each_safe(pos, n, self) \ | ||
74 | for (pos = strlist__first(self), n = strlist__next(pos); pos;\ | ||
75 | pos = n, n = strlist__next(n)) | ||
76 | |||
38 | int strlist__parse_list(struct strlist *self, const char *s); | 77 | int strlist__parse_list(struct strlist *self, const char *s); |
39 | #endif /* STRLIST_H_ */ | 78 | #endif /* __PERF_STRLIST_H */ |
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index cd93195aedb3..e0781989cc31 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _INCLUDE_GUARD_SVG_HELPER_ | 1 | #ifndef __PERF_SVGHELPER_H |
2 | #define _INCLUDE_GUARD_SVG_HELPER_ | 2 | #define __PERF_SVGHELPER_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include "types.h" |
5 | 5 | ||
@@ -25,4 +25,4 @@ extern void svg_close(void); | |||
25 | 25 | ||
26 | extern int svg_page_width; | 26 | extern int svg_page_width; |
27 | 27 | ||
28 | #endif | 28 | #endif /* __PERF_SVGHELPER_H */ |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 226f44a2357d..323c0aea0a91 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1,111 +1,226 @@ | |||
1 | #include "util.h" | 1 | #include "util.h" |
2 | #include "../perf.h" | 2 | #include "../perf.h" |
3 | #include "sort.h" | ||
3 | #include "string.h" | 4 | #include "string.h" |
4 | #include "symbol.h" | 5 | #include "symbol.h" |
6 | #include "thread.h" | ||
5 | 7 | ||
6 | #include "debug.h" | 8 | #include "debug.h" |
7 | 9 | ||
10 | #include <asm/bug.h> | ||
8 | #include <libelf.h> | 11 | #include <libelf.h> |
9 | #include <gelf.h> | 12 | #include <gelf.h> |
10 | #include <elf.h> | 13 | #include <elf.h> |
14 | #include <limits.h> | ||
15 | #include <sys/utsname.h> | ||
11 | 16 | ||
12 | const char *sym_hist_filter; | 17 | #ifndef NT_GNU_BUILD_ID |
18 | #define NT_GNU_BUILD_ID 3 | ||
19 | #endif | ||
13 | 20 | ||
14 | enum dso_origin { | 21 | enum dso_origin { |
15 | DSO__ORIG_KERNEL = 0, | 22 | DSO__ORIG_KERNEL = 0, |
16 | DSO__ORIG_JAVA_JIT, | 23 | DSO__ORIG_JAVA_JIT, |
24 | DSO__ORIG_BUILD_ID_CACHE, | ||
17 | DSO__ORIG_FEDORA, | 25 | DSO__ORIG_FEDORA, |
18 | DSO__ORIG_UBUNTU, | 26 | DSO__ORIG_UBUNTU, |
19 | DSO__ORIG_BUILDID, | 27 | DSO__ORIG_BUILDID, |
20 | DSO__ORIG_DSO, | 28 | DSO__ORIG_DSO, |
29 | DSO__ORIG_KMODULE, | ||
21 | DSO__ORIG_NOT_FOUND, | 30 | DSO__ORIG_NOT_FOUND, |
22 | }; | 31 | }; |
23 | 32 | ||
24 | static struct symbol *symbol__new(u64 start, u64 len, | 33 | static void dsos__add(struct list_head *head, struct dso *dso); |
25 | const char *name, unsigned int priv_size, | 34 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
26 | u64 obj_start, int v) | 35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
36 | symbol_filter_t filter); | ||
37 | static int vmlinux_path__nr_entries; | ||
38 | static char **vmlinux_path; | ||
39 | |||
40 | struct symbol_conf symbol_conf = { | ||
41 | .exclude_other = true, | ||
42 | .use_modules = true, | ||
43 | .try_vmlinux_path = true, | ||
44 | }; | ||
45 | |||
46 | bool dso__loaded(const struct dso *self, enum map_type type) | ||
27 | { | 47 | { |
28 | size_t namelen = strlen(name) + 1; | 48 | return self->loaded & (1 << type); |
29 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); | 49 | } |
30 | 50 | ||
31 | if (!self) | 51 | bool dso__sorted_by_name(const struct dso *self, enum map_type type) |
32 | return NULL; | 52 | { |
53 | return self->sorted_by_name & (1 << type); | ||
54 | } | ||
55 | |||
56 | static void dso__set_sorted_by_name(struct dso *self, enum map_type type) | ||
57 | { | ||
58 | self->sorted_by_name |= (1 << type); | ||
59 | } | ||
60 | |||
61 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | ||
62 | { | ||
63 | switch (map_type) { | ||
64 | case MAP__FUNCTION: | ||
65 | return symbol_type == 'T' || symbol_type == 'W'; | ||
66 | case MAP__VARIABLE: | ||
67 | return symbol_type == 'D' || symbol_type == 'd'; | ||
68 | default: | ||
69 | return false; | ||
70 | } | ||
71 | } | ||
33 | 72 | ||
34 | if (v >= 2) | 73 | static void symbols__fixup_end(struct rb_root *self) |
35 | printf("new symbol: %016Lx [%08lx]: %s, hist: %p, obj_start: %p\n", | 74 | { |
36 | (u64)start, (unsigned long)len, name, self->hist, (void *)(unsigned long)obj_start); | 75 | struct rb_node *nd, *prevnd = rb_first(self); |
76 | struct symbol *curr, *prev; | ||
37 | 77 | ||
38 | self->obj_start= obj_start; | 78 | if (prevnd == NULL) |
39 | self->hist = NULL; | 79 | return; |
40 | self->hist_sum = 0; | ||
41 | 80 | ||
42 | if (sym_hist_filter && !strcmp(name, sym_hist_filter)) | 81 | curr = rb_entry(prevnd, struct symbol, rb_node); |
43 | self->hist = calloc(sizeof(u64), len); | ||
44 | 82 | ||
45 | if (priv_size) { | 83 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
46 | memset(self, 0, priv_size); | 84 | prev = curr; |
47 | self = ((void *)self) + priv_size; | 85 | curr = rb_entry(nd, struct symbol, rb_node); |
86 | |||
87 | if (prev->end == prev->start) | ||
88 | prev->end = curr->start - 1; | ||
48 | } | 89 | } |
90 | |||
91 | /* Last entry */ | ||
92 | if (curr->end == curr->start) | ||
93 | curr->end = roundup(curr->start, 4096); | ||
94 | } | ||
95 | |||
96 | static void __map_groups__fixup_end(struct map_groups *self, enum map_type type) | ||
97 | { | ||
98 | struct map *prev, *curr; | ||
99 | struct rb_node *nd, *prevnd = rb_first(&self->maps[type]); | ||
100 | |||
101 | if (prevnd == NULL) | ||
102 | return; | ||
103 | |||
104 | curr = rb_entry(prevnd, struct map, rb_node); | ||
105 | |||
106 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | ||
107 | prev = curr; | ||
108 | curr = rb_entry(nd, struct map, rb_node); | ||
109 | prev->end = curr->start - 1; | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * We still haven't the actual symbols, so guess the | ||
114 | * last map final address. | ||
115 | */ | ||
116 | curr->end = ~0UL; | ||
117 | } | ||
118 | |||
119 | static void map_groups__fixup_end(struct map_groups *self) | ||
120 | { | ||
121 | int i; | ||
122 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
123 | __map_groups__fixup_end(self, i); | ||
124 | } | ||
125 | |||
126 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) | ||
127 | { | ||
128 | size_t namelen = strlen(name) + 1; | ||
129 | struct symbol *self = zalloc(symbol_conf.priv_size + | ||
130 | sizeof(*self) + namelen); | ||
131 | if (self == NULL) | ||
132 | return NULL; | ||
133 | |||
134 | if (symbol_conf.priv_size) | ||
135 | self = ((void *)self) + symbol_conf.priv_size; | ||
136 | |||
49 | self->start = start; | 137 | self->start = start; |
50 | self->end = len ? start + len - 1 : start; | 138 | self->end = len ? start + len - 1 : start; |
139 | |||
140 | pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); | ||
141 | |||
51 | memcpy(self->name, name, namelen); | 142 | memcpy(self->name, name, namelen); |
52 | 143 | ||
53 | return self; | 144 | return self; |
54 | } | 145 | } |
55 | 146 | ||
56 | static void symbol__delete(struct symbol *self, unsigned int priv_size) | 147 | void symbol__delete(struct symbol *self) |
57 | { | 148 | { |
58 | free(((void *)self) - priv_size); | 149 | free(((void *)self) - symbol_conf.priv_size); |
59 | } | 150 | } |
60 | 151 | ||
61 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | 152 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) |
62 | { | 153 | { |
63 | if (!self->module) | 154 | return fprintf(fp, " %llx-%llx %s\n", |
64 | return fprintf(fp, " %llx-%llx %s\n", | ||
65 | self->start, self->end, self->name); | 155 | self->start, self->end, self->name); |
66 | else | ||
67 | return fprintf(fp, " %llx-%llx %s \t[%s]\n", | ||
68 | self->start, self->end, self->name, self->module->name); | ||
69 | } | 156 | } |
70 | 157 | ||
71 | struct dso *dso__new(const char *name, unsigned int sym_priv_size) | 158 | void dso__set_long_name(struct dso *self, char *name) |
72 | { | 159 | { |
73 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | 160 | if (name == NULL) |
161 | return; | ||
162 | self->long_name = name; | ||
163 | self->long_name_len = strlen(name); | ||
164 | } | ||
165 | |||
166 | static void dso__set_basename(struct dso *self) | ||
167 | { | ||
168 | self->short_name = basename(self->long_name); | ||
169 | } | ||
170 | |||
171 | struct dso *dso__new(const char *name) | ||
172 | { | ||
173 | struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1); | ||
74 | 174 | ||
75 | if (self != NULL) { | 175 | if (self != NULL) { |
176 | int i; | ||
76 | strcpy(self->name, name); | 177 | strcpy(self->name, name); |
77 | self->syms = RB_ROOT; | 178 | dso__set_long_name(self, self->name); |
78 | self->sym_priv_size = sym_priv_size; | 179 | self->short_name = self->name; |
79 | self->find_symbol = dso__find_symbol; | 180 | for (i = 0; i < MAP__NR_TYPES; ++i) |
181 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; | ||
80 | self->slen_calculated = 0; | 182 | self->slen_calculated = 0; |
81 | self->origin = DSO__ORIG_NOT_FOUND; | 183 | self->origin = DSO__ORIG_NOT_FOUND; |
184 | self->loaded = 0; | ||
185 | self->sorted_by_name = 0; | ||
186 | self->has_build_id = 0; | ||
82 | } | 187 | } |
83 | 188 | ||
84 | return self; | 189 | return self; |
85 | } | 190 | } |
86 | 191 | ||
87 | static void dso__delete_symbols(struct dso *self) | 192 | static void symbols__delete(struct rb_root *self) |
88 | { | 193 | { |
89 | struct symbol *pos; | 194 | struct symbol *pos; |
90 | struct rb_node *next = rb_first(&self->syms); | 195 | struct rb_node *next = rb_first(self); |
91 | 196 | ||
92 | while (next) { | 197 | while (next) { |
93 | pos = rb_entry(next, struct symbol, rb_node); | 198 | pos = rb_entry(next, struct symbol, rb_node); |
94 | next = rb_next(&pos->rb_node); | 199 | next = rb_next(&pos->rb_node); |
95 | rb_erase(&pos->rb_node, &self->syms); | 200 | rb_erase(&pos->rb_node, self); |
96 | symbol__delete(pos, self->sym_priv_size); | 201 | symbol__delete(pos); |
97 | } | 202 | } |
98 | } | 203 | } |
99 | 204 | ||
100 | void dso__delete(struct dso *self) | 205 | void dso__delete(struct dso *self) |
101 | { | 206 | { |
102 | dso__delete_symbols(self); | 207 | int i; |
208 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
209 | symbols__delete(&self->symbols[i]); | ||
210 | if (self->long_name != self->name) | ||
211 | free(self->long_name); | ||
103 | free(self); | 212 | free(self); |
104 | } | 213 | } |
105 | 214 | ||
106 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | 215 | void dso__set_build_id(struct dso *self, void *build_id) |
107 | { | 216 | { |
108 | struct rb_node **p = &self->syms.rb_node; | 217 | memcpy(self->build_id, build_id, sizeof(self->build_id)); |
218 | self->has_build_id = 1; | ||
219 | } | ||
220 | |||
221 | static void symbols__insert(struct rb_root *self, struct symbol *sym) | ||
222 | { | ||
223 | struct rb_node **p = &self->rb_node; | ||
109 | struct rb_node *parent = NULL; | 224 | struct rb_node *parent = NULL; |
110 | const u64 ip = sym->start; | 225 | const u64 ip = sym->start; |
111 | struct symbol *s; | 226 | struct symbol *s; |
@@ -119,17 +234,17 @@ static void dso__insert_symbol(struct dso *self, struct symbol *sym) | |||
119 | p = &(*p)->rb_right; | 234 | p = &(*p)->rb_right; |
120 | } | 235 | } |
121 | rb_link_node(&sym->rb_node, parent, p); | 236 | rb_link_node(&sym->rb_node, parent, p); |
122 | rb_insert_color(&sym->rb_node, &self->syms); | 237 | rb_insert_color(&sym->rb_node, self); |
123 | } | 238 | } |
124 | 239 | ||
125 | struct symbol *dso__find_symbol(struct dso *self, u64 ip) | 240 | static struct symbol *symbols__find(struct rb_root *self, u64 ip) |
126 | { | 241 | { |
127 | struct rb_node *n; | 242 | struct rb_node *n; |
128 | 243 | ||
129 | if (self == NULL) | 244 | if (self == NULL) |
130 | return NULL; | 245 | return NULL; |
131 | 246 | ||
132 | n = self->syms.rb_node; | 247 | n = self->rb_node; |
133 | 248 | ||
134 | while (n) { | 249 | while (n) { |
135 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | 250 | struct symbol *s = rb_entry(n, struct symbol, rb_node); |
@@ -145,12 +260,120 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip) | |||
145 | return NULL; | 260 | return NULL; |
146 | } | 261 | } |
147 | 262 | ||
148 | size_t dso__fprintf(struct dso *self, FILE *fp) | 263 | struct symbol_name_rb_node { |
264 | struct rb_node rb_node; | ||
265 | struct symbol sym; | ||
266 | }; | ||
267 | |||
268 | static void symbols__insert_by_name(struct rb_root *self, struct symbol *sym) | ||
149 | { | 269 | { |
150 | size_t ret = fprintf(fp, "dso: %s\n", self->name); | 270 | struct rb_node **p = &self->rb_node; |
271 | struct rb_node *parent = NULL; | ||
272 | struct symbol_name_rb_node *symn = ((void *)sym) - sizeof(*parent), *s; | ||
151 | 273 | ||
274 | while (*p != NULL) { | ||
275 | parent = *p; | ||
276 | s = rb_entry(parent, struct symbol_name_rb_node, rb_node); | ||
277 | if (strcmp(sym->name, s->sym.name) < 0) | ||
278 | p = &(*p)->rb_left; | ||
279 | else | ||
280 | p = &(*p)->rb_right; | ||
281 | } | ||
282 | rb_link_node(&symn->rb_node, parent, p); | ||
283 | rb_insert_color(&symn->rb_node, self); | ||
284 | } | ||
285 | |||
286 | static void symbols__sort_by_name(struct rb_root *self, struct rb_root *source) | ||
287 | { | ||
288 | struct rb_node *nd; | ||
289 | |||
290 | for (nd = rb_first(source); nd; nd = rb_next(nd)) { | ||
291 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
292 | symbols__insert_by_name(self, pos); | ||
293 | } | ||
294 | } | ||
295 | |||
296 | static struct symbol *symbols__find_by_name(struct rb_root *self, const char *name) | ||
297 | { | ||
298 | struct rb_node *n; | ||
299 | |||
300 | if (self == NULL) | ||
301 | return NULL; | ||
302 | |||
303 | n = self->rb_node; | ||
304 | |||
305 | while (n) { | ||
306 | struct symbol_name_rb_node *s; | ||
307 | int cmp; | ||
308 | |||
309 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); | ||
310 | cmp = strcmp(name, s->sym.name); | ||
311 | |||
312 | if (cmp < 0) | ||
313 | n = n->rb_left; | ||
314 | else if (cmp > 0) | ||
315 | n = n->rb_right; | ||
316 | else | ||
317 | return &s->sym; | ||
318 | } | ||
319 | |||
320 | return NULL; | ||
321 | } | ||
322 | |||
323 | struct symbol *dso__find_symbol(struct dso *self, | ||
324 | enum map_type type, u64 addr) | ||
325 | { | ||
326 | return symbols__find(&self->symbols[type], addr); | ||
327 | } | ||
328 | |||
329 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | ||
330 | const char *name) | ||
331 | { | ||
332 | return symbols__find_by_name(&self->symbol_names[type], name); | ||
333 | } | ||
334 | |||
335 | void dso__sort_by_name(struct dso *self, enum map_type type) | ||
336 | { | ||
337 | dso__set_sorted_by_name(self, type); | ||
338 | return symbols__sort_by_name(&self->symbol_names[type], | ||
339 | &self->symbols[type]); | ||
340 | } | ||
341 | |||
342 | int build_id__sprintf(const u8 *self, int len, char *bf) | ||
343 | { | ||
344 | char *bid = bf; | ||
345 | const u8 *raw = self; | ||
346 | int i; | ||
347 | |||
348 | for (i = 0; i < len; ++i) { | ||
349 | sprintf(bid, "%02x", *raw); | ||
350 | ++raw; | ||
351 | bid += 2; | ||
352 | } | ||
353 | |||
354 | return raw - self; | ||
355 | } | ||
356 | |||
357 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp) | ||
358 | { | ||
359 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
360 | |||
361 | build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id); | ||
362 | return fprintf(fp, "%s", sbuild_id); | ||
363 | } | ||
364 | |||
365 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) | ||
366 | { | ||
152 | struct rb_node *nd; | 367 | struct rb_node *nd; |
153 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | 368 | size_t ret = fprintf(fp, "dso: %s (", self->short_name); |
369 | |||
370 | if (self->short_name != self->long_name) | ||
371 | ret += fprintf(fp, "%s, ", self->long_name); | ||
372 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | ||
373 | self->loaded ? "" : "NOT "); | ||
374 | ret += dso__fprintf_buildid(self, fp); | ||
375 | ret += fprintf(fp, ")\n"); | ||
376 | for (nd = rb_first(&self->symbols[type]); nd; nd = rb_next(nd)) { | ||
154 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 377 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
155 | ret += symbol__fprintf(pos, fp); | 378 | ret += symbol__fprintf(pos, fp); |
156 | } | 379 | } |
@@ -158,22 +381,23 @@ size_t dso__fprintf(struct dso *self, FILE *fp) | |||
158 | return ret; | 381 | return ret; |
159 | } | 382 | } |
160 | 383 | ||
161 | static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | 384 | int kallsyms__parse(const char *filename, void *arg, |
385 | int (*process_symbol)(void *arg, const char *name, | ||
386 | char type, u64 start)) | ||
162 | { | 387 | { |
163 | struct rb_node *nd, *prevnd; | ||
164 | char *line = NULL; | 388 | char *line = NULL; |
165 | size_t n; | 389 | size_t n; |
166 | FILE *file = fopen("/proc/kallsyms", "r"); | 390 | int err = 0; |
167 | int count = 0; | 391 | FILE *file = fopen(filename, "r"); |
168 | 392 | ||
169 | if (file == NULL) | 393 | if (file == NULL) |
170 | goto out_failure; | 394 | goto out_failure; |
171 | 395 | ||
172 | while (!feof(file)) { | 396 | while (!feof(file)) { |
173 | u64 start; | 397 | u64 start; |
174 | struct symbol *sym; | ||
175 | int line_len, len; | 398 | int line_len, len; |
176 | char symbol_type; | 399 | char symbol_type; |
400 | char *symbol_name; | ||
177 | 401 | ||
178 | line_len = getline(&line, &n, file); | 402 | line_len = getline(&line, &n, file); |
179 | if (line_len < 0) | 403 | if (line_len < 0) |
@@ -191,64 +415,168 @@ static int dso__load_kallsyms(struct dso *self, symbol_filter_t filter, int v) | |||
191 | continue; | 415 | continue; |
192 | 416 | ||
193 | symbol_type = toupper(line[len]); | 417 | symbol_type = toupper(line[len]); |
194 | /* | 418 | symbol_name = line + len + 2; |
195 | * We're interested only in code ('T'ext) | ||
196 | */ | ||
197 | if (symbol_type != 'T' && symbol_type != 'W') | ||
198 | continue; | ||
199 | /* | ||
200 | * Well fix up the end later, when we have all sorted. | ||
201 | */ | ||
202 | sym = symbol__new(start, 0xdead, line + len + 2, | ||
203 | self->sym_priv_size, 0, v); | ||
204 | |||
205 | if (sym == NULL) | ||
206 | goto out_delete_line; | ||
207 | 419 | ||
208 | if (filter && filter(self, sym)) | 420 | err = process_symbol(arg, symbol_name, symbol_type, start); |
209 | symbol__delete(sym, self->sym_priv_size); | 421 | if (err) |
210 | else { | 422 | break; |
211 | dso__insert_symbol(self, sym); | ||
212 | count++; | ||
213 | } | ||
214 | } | 423 | } |
215 | 424 | ||
425 | free(line); | ||
426 | fclose(file); | ||
427 | return err; | ||
428 | |||
429 | out_failure: | ||
430 | return -1; | ||
431 | } | ||
432 | |||
433 | struct process_kallsyms_args { | ||
434 | struct map *map; | ||
435 | struct dso *dso; | ||
436 | }; | ||
437 | |||
438 | static int map__process_kallsym_symbol(void *arg, const char *name, | ||
439 | char type, u64 start) | ||
440 | { | ||
441 | struct symbol *sym; | ||
442 | struct process_kallsyms_args *a = arg; | ||
443 | struct rb_root *root = &a->dso->symbols[a->map->type]; | ||
444 | |||
445 | if (!symbol_type__is_a(type, a->map->type)) | ||
446 | return 0; | ||
447 | |||
216 | /* | 448 | /* |
217 | * Now that we have all sorted out, just set the ->end of all | 449 | * Will fix up the end later, when we have all symbols sorted. |
218 | * symbols | ||
219 | */ | 450 | */ |
220 | prevnd = rb_first(&self->syms); | 451 | sym = symbol__new(start, 0, name); |
221 | 452 | ||
222 | if (prevnd == NULL) | 453 | if (sym == NULL) |
223 | goto out_delete_line; | 454 | return -ENOMEM; |
455 | /* | ||
456 | * We will pass the symbols to the filter later, in | ||
457 | * map__split_kallsyms, when we have split the maps per module | ||
458 | */ | ||
459 | symbols__insert(root, sym); | ||
460 | return 0; | ||
461 | } | ||
224 | 462 | ||
225 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | 463 | /* |
226 | struct symbol *prev = rb_entry(prevnd, struct symbol, rb_node), | 464 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, |
227 | *curr = rb_entry(nd, struct symbol, rb_node); | 465 | * so that we can in the next step set the symbol ->end address and then |
466 | * call kernel_maps__split_kallsyms. | ||
467 | */ | ||
468 | static int dso__load_all_kallsyms(struct dso *self, const char *filename, | ||
469 | struct map *map) | ||
470 | { | ||
471 | struct process_kallsyms_args args = { .map = map, .dso = self, }; | ||
472 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | ||
473 | } | ||
228 | 474 | ||
229 | prev->end = curr->start - 1; | 475 | /* |
230 | prevnd = nd; | 476 | * Split the symbols into maps, making sure there are no overlaps, i.e. the |
231 | } | 477 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
478 | * the original ELF section names vmlinux have. | ||
479 | */ | ||
480 | static int dso__split_kallsyms(struct dso *self, struct map *map, | ||
481 | symbol_filter_t filter) | ||
482 | { | ||
483 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
484 | struct map *curr_map = map; | ||
485 | struct symbol *pos; | ||
486 | int count = 0; | ||
487 | struct rb_root *root = &self->symbols[map->type]; | ||
488 | struct rb_node *next = rb_first(root); | ||
489 | int kernel_range = 0; | ||
232 | 490 | ||
233 | free(line); | 491 | while (next) { |
234 | fclose(file); | 492 | char *module; |
493 | |||
494 | pos = rb_entry(next, struct symbol, rb_node); | ||
495 | next = rb_next(&pos->rb_node); | ||
496 | |||
497 | module = strchr(pos->name, '\t'); | ||
498 | if (module) { | ||
499 | if (!symbol_conf.use_modules) | ||
500 | goto discard_symbol; | ||
501 | |||
502 | *module++ = '\0'; | ||
503 | |||
504 | if (strcmp(curr_map->dso->short_name, module)) { | ||
505 | curr_map = map_groups__find_by_name(kmaps, map->type, module); | ||
506 | if (curr_map == NULL) { | ||
507 | pr_debug("/proc/{kallsyms,modules} " | ||
508 | "inconsistency while looking " | ||
509 | "for \"%s\" module!\n", module); | ||
510 | return -1; | ||
511 | } | ||
512 | |||
513 | if (curr_map->dso->loaded) | ||
514 | goto discard_symbol; | ||
515 | } | ||
516 | /* | ||
517 | * So that we look just like we get from .ko files, | ||
518 | * i.e. not prelinked, relative to map->start. | ||
519 | */ | ||
520 | pos->start = curr_map->map_ip(curr_map, pos->start); | ||
521 | pos->end = curr_map->map_ip(curr_map, pos->end); | ||
522 | } else if (curr_map != map) { | ||
523 | char dso_name[PATH_MAX]; | ||
524 | struct dso *dso; | ||
525 | |||
526 | snprintf(dso_name, sizeof(dso_name), "[kernel].%d", | ||
527 | kernel_range++); | ||
528 | |||
529 | dso = dso__new(dso_name); | ||
530 | if (dso == NULL) | ||
531 | return -1; | ||
532 | |||
533 | curr_map = map__new2(pos->start, dso, map->type); | ||
534 | if (curr_map == NULL) { | ||
535 | dso__delete(dso); | ||
536 | return -1; | ||
537 | } | ||
538 | |||
539 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | ||
540 | map_groups__insert(kmaps, curr_map); | ||
541 | ++kernel_range; | ||
542 | } | ||
543 | |||
544 | if (filter && filter(curr_map, pos)) { | ||
545 | discard_symbol: rb_erase(&pos->rb_node, root); | ||
546 | symbol__delete(pos); | ||
547 | } else { | ||
548 | if (curr_map != map) { | ||
549 | rb_erase(&pos->rb_node, root); | ||
550 | symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); | ||
551 | } | ||
552 | count++; | ||
553 | } | ||
554 | } | ||
235 | 555 | ||
236 | return count; | 556 | return count; |
557 | } | ||
237 | 558 | ||
238 | out_delete_line: | 559 | int dso__load_kallsyms(struct dso *self, const char *filename, |
239 | free(line); | 560 | struct map *map, symbol_filter_t filter) |
240 | out_failure: | 561 | { |
241 | return -1; | 562 | if (dso__load_all_kallsyms(self, filename, map) < 0) |
563 | return -1; | ||
564 | |||
565 | symbols__fixup_end(&self->symbols[map->type]); | ||
566 | self->origin = DSO__ORIG_KERNEL; | ||
567 | |||
568 | return dso__split_kallsyms(self, map, filter); | ||
242 | } | 569 | } |
243 | 570 | ||
244 | static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) | 571 | static int dso__load_perf_map(struct dso *self, struct map *map, |
572 | symbol_filter_t filter) | ||
245 | { | 573 | { |
246 | char *line = NULL; | 574 | char *line = NULL; |
247 | size_t n; | 575 | size_t n; |
248 | FILE *file; | 576 | FILE *file; |
249 | int nr_syms = 0; | 577 | int nr_syms = 0; |
250 | 578 | ||
251 | file = fopen(self->name, "r"); | 579 | file = fopen(self->long_name, "r"); |
252 | if (file == NULL) | 580 | if (file == NULL) |
253 | goto out_failure; | 581 | goto out_failure; |
254 | 582 | ||
@@ -278,16 +606,15 @@ static int dso__load_perf_map(struct dso *self, symbol_filter_t filter, int v) | |||
278 | if (len + 2 >= line_len) | 606 | if (len + 2 >= line_len) |
279 | continue; | 607 | continue; |
280 | 608 | ||
281 | sym = symbol__new(start, size, line + len, | 609 | sym = symbol__new(start, size, line + len); |
282 | self->sym_priv_size, start, v); | ||
283 | 610 | ||
284 | if (sym == NULL) | 611 | if (sym == NULL) |
285 | goto out_delete_line; | 612 | goto out_delete_line; |
286 | 613 | ||
287 | if (filter && filter(self, sym)) | 614 | if (filter && filter(map, sym)) |
288 | symbol__delete(sym, self->sym_priv_size); | 615 | symbol__delete(sym); |
289 | else { | 616 | else { |
290 | dso__insert_symbol(self, sym); | 617 | symbols__insert(&self->symbols[map->type], sym); |
291 | nr_syms++; | 618 | nr_syms++; |
292 | } | 619 | } |
293 | } | 620 | } |
@@ -327,6 +654,13 @@ static inline int elf_sym__is_function(const GElf_Sym *sym) | |||
327 | sym->st_shndx != SHN_UNDEF; | 654 | sym->st_shndx != SHN_UNDEF; |
328 | } | 655 | } |
329 | 656 | ||
657 | static inline bool elf_sym__is_object(const GElf_Sym *sym) | ||
658 | { | ||
659 | return elf_sym__type(sym) == STT_OBJECT && | ||
660 | sym->st_name != 0 && | ||
661 | sym->st_shndx != SHN_UNDEF; | ||
662 | } | ||
663 | |||
330 | static inline int elf_sym__is_label(const GElf_Sym *sym) | 664 | static inline int elf_sym__is_label(const GElf_Sym *sym) |
331 | { | 665 | { |
332 | return elf_sym__type(sym) == STT_NOTYPE && | 666 | return elf_sym__type(sym) == STT_NOTYPE && |
@@ -347,6 +681,12 @@ static inline int elf_sec__is_text(const GElf_Shdr *shdr, | |||
347 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; | 681 | return strstr(elf_sec__name(shdr, secstrs), "text") != NULL; |
348 | } | 682 | } |
349 | 683 | ||
684 | static inline bool elf_sec__is_data(const GElf_Shdr *shdr, | ||
685 | const Elf_Data *secstrs) | ||
686 | { | ||
687 | return strstr(elf_sec__name(shdr, secstrs), "data") != NULL; | ||
688 | } | ||
689 | |||
350 | static inline const char *elf_sym__name(const GElf_Sym *sym, | 690 | static inline const char *elf_sym__name(const GElf_Sym *sym, |
351 | const Elf_Data *symstrs) | 691 | const Elf_Data *symstrs) |
352 | { | 692 | { |
@@ -393,7 +733,8 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
393 | * And always look at the original dso, not at debuginfo packages, that | 733 | * And always look at the original dso, not at debuginfo packages, that |
394 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | 734 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). |
395 | */ | 735 | */ |
396 | static int dso__synthesize_plt_symbols(struct dso *self, int v) | 736 | static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, |
737 | symbol_filter_t filter) | ||
397 | { | 738 | { |
398 | uint32_t nr_rel_entries, idx; | 739 | uint32_t nr_rel_entries, idx; |
399 | GElf_Sym sym; | 740 | GElf_Sym sym; |
@@ -409,7 +750,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
409 | Elf *elf; | 750 | Elf *elf; |
410 | int nr = 0, symidx, fd, err = 0; | 751 | int nr = 0, symidx, fd, err = 0; |
411 | 752 | ||
412 | fd = open(self->name, O_RDONLY); | 753 | fd = open(self->long_name, O_RDONLY); |
413 | if (fd < 0) | 754 | if (fd < 0) |
414 | goto out; | 755 | goto out; |
415 | 756 | ||
@@ -477,12 +818,16 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
477 | "%s@plt", elf_sym__name(&sym, symstrs)); | 818 | "%s@plt", elf_sym__name(&sym, symstrs)); |
478 | 819 | ||
479 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 820 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
480 | sympltname, self->sym_priv_size, 0, v); | 821 | sympltname); |
481 | if (!f) | 822 | if (!f) |
482 | goto out_elf_end; | 823 | goto out_elf_end; |
483 | 824 | ||
484 | dso__insert_symbol(self, f); | 825 | if (filter && filter(map, f)) |
485 | ++nr; | 826 | symbol__delete(f); |
827 | else { | ||
828 | symbols__insert(&self->symbols[map->type], f); | ||
829 | ++nr; | ||
830 | } | ||
486 | } | 831 | } |
487 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | 832 | } else if (shdr_rel_plt.sh_type == SHT_REL) { |
488 | GElf_Rel pos_mem, *pos; | 833 | GElf_Rel pos_mem, *pos; |
@@ -495,12 +840,16 @@ static int dso__synthesize_plt_symbols(struct dso *self, int v) | |||
495 | "%s@plt", elf_sym__name(&sym, symstrs)); | 840 | "%s@plt", elf_sym__name(&sym, symstrs)); |
496 | 841 | ||
497 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 842 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
498 | sympltname, self->sym_priv_size, 0, v); | 843 | sympltname); |
499 | if (!f) | 844 | if (!f) |
500 | goto out_elf_end; | 845 | goto out_elf_end; |
501 | 846 | ||
502 | dso__insert_symbol(self, f); | 847 | if (filter && filter(map, f)) |
503 | ++nr; | 848 | symbol__delete(f); |
849 | else { | ||
850 | symbols__insert(&self->symbols[map->type], f); | ||
851 | ++nr; | ||
852 | } | ||
504 | } | 853 | } |
505 | } | 854 | } |
506 | 855 | ||
@@ -513,14 +862,42 @@ out_close: | |||
513 | if (err == 0) | 862 | if (err == 0) |
514 | return nr; | 863 | return nr; |
515 | out: | 864 | out: |
516 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | 865 | pr_warning("%s: problems reading %s PLT info.\n", |
517 | __func__, self->name); | 866 | __func__, self->long_name); |
518 | return 0; | 867 | return 0; |
519 | } | 868 | } |
520 | 869 | ||
521 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 870 | static bool elf_sym__is_a(GElf_Sym *self, enum map_type type) |
522 | symbol_filter_t filter, int v, struct module *mod) | 871 | { |
872 | switch (type) { | ||
873 | case MAP__FUNCTION: | ||
874 | return elf_sym__is_function(self); | ||
875 | case MAP__VARIABLE: | ||
876 | return elf_sym__is_object(self); | ||
877 | default: | ||
878 | return false; | ||
879 | } | ||
880 | } | ||
881 | |||
882 | static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type) | ||
523 | { | 883 | { |
884 | switch (type) { | ||
885 | case MAP__FUNCTION: | ||
886 | return elf_sec__is_text(self, secstrs); | ||
887 | case MAP__VARIABLE: | ||
888 | return elf_sec__is_data(self, secstrs); | ||
889 | default: | ||
890 | return false; | ||
891 | } | ||
892 | } | ||
893 | |||
894 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, | ||
895 | int fd, symbol_filter_t filter, int kmodule) | ||
896 | { | ||
897 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; | ||
898 | struct map *curr_map = map; | ||
899 | struct dso *curr_dso = self; | ||
900 | size_t dso_name_len = strlen(self->short_name); | ||
524 | Elf_Data *symstrs, *secstrs; | 901 | Elf_Data *symstrs, *secstrs; |
525 | uint32_t nr_syms; | 902 | uint32_t nr_syms; |
526 | int err = -1; | 903 | int err = -1; |
@@ -531,19 +908,16 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
531 | GElf_Sym sym; | 908 | GElf_Sym sym; |
532 | Elf_Scn *sec, *sec_strndx; | 909 | Elf_Scn *sec, *sec_strndx; |
533 | Elf *elf; | 910 | Elf *elf; |
534 | int nr = 0, kernel = !strcmp("[kernel]", self->name); | 911 | int nr = 0; |
535 | 912 | ||
536 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 913 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
537 | if (elf == NULL) { | 914 | if (elf == NULL) { |
538 | if (v) | 915 | pr_err("%s: cannot read %s ELF file.\n", __func__, name); |
539 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
540 | __func__, name); | ||
541 | goto out_close; | 916 | goto out_close; |
542 | } | 917 | } |
543 | 918 | ||
544 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 919 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
545 | if (v) | 920 | pr_err("%s: cannot get elf header.\n", __func__); |
546 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
547 | goto out_elf_end; | 921 | goto out_elf_end; |
548 | } | 922 | } |
549 | 923 | ||
@@ -577,7 +951,7 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
577 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 951 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
578 | 952 | ||
579 | memset(&sym, 0, sizeof(sym)); | 953 | memset(&sym, 0, sizeof(sym)); |
580 | if (!kernel) { | 954 | if (!self->kernel) { |
581 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 955 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || |
582 | elf_section_by_name(elf, &ehdr, &shdr, | 956 | elf_section_by_name(elf, &ehdr, &shdr, |
583 | ".gnu.prelink_undo", | 957 | ".gnu.prelink_undo", |
@@ -586,14 +960,16 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
586 | 960 | ||
587 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | 961 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { |
588 | struct symbol *f; | 962 | struct symbol *f; |
589 | const char *elf_name; | 963 | const char *elf_name = elf_sym__name(&sym, symstrs); |
590 | char *demangled; | 964 | char *demangled = NULL; |
591 | u64 obj_start; | ||
592 | struct section *section = NULL; | ||
593 | int is_label = elf_sym__is_label(&sym); | 965 | int is_label = elf_sym__is_label(&sym); |
594 | const char *section_name; | 966 | const char *section_name; |
595 | 967 | ||
596 | if (!is_label && !elf_sym__is_function(&sym)) | 968 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && |
969 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | ||
970 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
971 | |||
972 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | ||
597 | continue; | 973 | continue; |
598 | 974 | ||
599 | sec = elf_getscn(elf, sym.st_shndx); | 975 | sec = elf_getscn(elf, sym.st_shndx); |
@@ -602,55 +978,97 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
602 | 978 | ||
603 | gelf_getshdr(sec, &shdr); | 979 | gelf_getshdr(sec, &shdr); |
604 | 980 | ||
605 | if (is_label && !elf_sec__is_text(&shdr, secstrs)) | 981 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) |
606 | continue; | 982 | continue; |
607 | 983 | ||
608 | section_name = elf_sec__name(&shdr, secstrs); | 984 | section_name = elf_sec__name(&shdr, secstrs); |
609 | obj_start = sym.st_value; | ||
610 | 985 | ||
611 | if (self->adjust_symbols) { | 986 | if (self->kernel || kmodule) { |
612 | if (v >= 2) | 987 | char dso_name[PATH_MAX]; |
613 | printf("adjusting symbol: st_value: %Lx sh_addr: %Lx sh_offset: %Lx\n", | ||
614 | (u64)sym.st_value, (u64)shdr.sh_addr, (u64)shdr.sh_offset); | ||
615 | 988 | ||
616 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | 989 | if (strcmp(section_name, |
617 | } | 990 | curr_dso->short_name + dso_name_len) == 0) |
991 | goto new_symbol; | ||
618 | 992 | ||
619 | if (mod) { | 993 | if (strcmp(section_name, ".text") == 0) { |
620 | section = mod->sections->find_section(mod->sections, section_name); | 994 | curr_map = map; |
621 | if (section) | 995 | curr_dso = self; |
622 | sym.st_value += section->vma; | 996 | goto new_symbol; |
623 | else { | ||
624 | fprintf(stderr, "dso__load_sym() module %s lookup of %s failed\n", | ||
625 | mod->name, section_name); | ||
626 | goto out_elf_end; | ||
627 | } | 997 | } |
998 | |||
999 | snprintf(dso_name, sizeof(dso_name), | ||
1000 | "%s%s", self->short_name, section_name); | ||
1001 | |||
1002 | curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name); | ||
1003 | if (curr_map == NULL) { | ||
1004 | u64 start = sym.st_value; | ||
1005 | |||
1006 | if (kmodule) | ||
1007 | start += map->start + shdr.sh_offset; | ||
1008 | |||
1009 | curr_dso = dso__new(dso_name); | ||
1010 | if (curr_dso == NULL) | ||
1011 | goto out_elf_end; | ||
1012 | curr_map = map__new2(start, curr_dso, | ||
1013 | map->type); | ||
1014 | if (curr_map == NULL) { | ||
1015 | dso__delete(curr_dso); | ||
1016 | goto out_elf_end; | ||
1017 | } | ||
1018 | curr_map->map_ip = identity__map_ip; | ||
1019 | curr_map->unmap_ip = identity__map_ip; | ||
1020 | curr_dso->origin = DSO__ORIG_KERNEL; | ||
1021 | map_groups__insert(kmap->kmaps, curr_map); | ||
1022 | dsos__add(&dsos__kernel, curr_dso); | ||
1023 | dso__set_loaded(curr_dso, map->type); | ||
1024 | } else | ||
1025 | curr_dso = curr_map->dso; | ||
1026 | |||
1027 | goto new_symbol; | ||
1028 | } | ||
1029 | |||
1030 | if (curr_dso->adjust_symbols) { | ||
1031 | pr_debug4("%s: adjusting symbol: st_value: %#Lx " | ||
1032 | "sh_addr: %#Lx sh_offset: %#Lx\n", __func__, | ||
1033 | (u64)sym.st_value, (u64)shdr.sh_addr, | ||
1034 | (u64)shdr.sh_offset); | ||
1035 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
628 | } | 1036 | } |
629 | /* | 1037 | /* |
630 | * We need to figure out if the object was created from C++ sources | 1038 | * We need to figure out if the object was created from C++ sources |
631 | * DWARF DW_compile_unit has this, but we don't always have access | 1039 | * DWARF DW_compile_unit has this, but we don't always have access |
632 | * to it... | 1040 | * to it... |
633 | */ | 1041 | */ |
634 | elf_name = elf_sym__name(&sym, symstrs); | ||
635 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); | 1042 | demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI); |
636 | if (demangled != NULL) | 1043 | if (demangled != NULL) |
637 | elf_name = demangled; | 1044 | elf_name = demangled; |
638 | 1045 | new_symbol: | |
639 | f = symbol__new(sym.st_value, sym.st_size, elf_name, | 1046 | f = symbol__new(sym.st_value, sym.st_size, elf_name); |
640 | self->sym_priv_size, obj_start, v); | ||
641 | free(demangled); | 1047 | free(demangled); |
642 | if (!f) | 1048 | if (!f) |
643 | goto out_elf_end; | 1049 | goto out_elf_end; |
644 | 1050 | ||
645 | if (filter && filter(self, f)) | 1051 | if (filter && filter(curr_map, f)) |
646 | symbol__delete(f, self->sym_priv_size); | 1052 | symbol__delete(f); |
647 | else { | 1053 | else { |
648 | f->module = mod; | 1054 | symbols__insert(&curr_dso->symbols[curr_map->type], f); |
649 | dso__insert_symbol(self, f); | ||
650 | nr++; | 1055 | nr++; |
651 | } | 1056 | } |
652 | } | 1057 | } |
653 | 1058 | ||
1059 | /* | ||
1060 | * For misannotated, zeroed, ASM function sizes. | ||
1061 | */ | ||
1062 | if (nr > 0) { | ||
1063 | symbols__fixup_end(&self->symbols[map->type]); | ||
1064 | if (kmap) { | ||
1065 | /* | ||
1066 | * We need to fixup this here too because we create new | ||
1067 | * maps here, for things like vsyscall sections. | ||
1068 | */ | ||
1069 | __map_groups__fixup_end(kmap->kmaps, map->type); | ||
1070 | } | ||
1071 | } | ||
654 | err = nr; | 1072 | err = nr; |
655 | out_elf_end: | 1073 | out_elf_end: |
656 | elf_end(elf); | 1074 | elf_end(elf); |
@@ -658,63 +1076,157 @@ out_close: | |||
658 | return err; | 1076 | return err; |
659 | } | 1077 | } |
660 | 1078 | ||
661 | #define BUILD_ID_SIZE 128 | 1079 | static bool dso__build_id_equal(const struct dso *self, u8 *build_id) |
1080 | { | ||
1081 | return memcmp(self->build_id, build_id, sizeof(self->build_id)) == 0; | ||
1082 | } | ||
662 | 1083 | ||
663 | static char *dso__read_build_id(struct dso *self, int v) | 1084 | static bool __dsos__read_build_ids(struct list_head *head, bool with_hits) |
664 | { | 1085 | { |
665 | int i; | 1086 | bool have_build_id = false; |
1087 | struct dso *pos; | ||
1088 | |||
1089 | list_for_each_entry(pos, head, node) { | ||
1090 | if (with_hits && !pos->hit) | ||
1091 | continue; | ||
1092 | if (filename__read_build_id(pos->long_name, pos->build_id, | ||
1093 | sizeof(pos->build_id)) > 0) { | ||
1094 | have_build_id = true; | ||
1095 | pos->has_build_id = true; | ||
1096 | } | ||
1097 | } | ||
1098 | |||
1099 | return have_build_id; | ||
1100 | } | ||
1101 | |||
1102 | bool dsos__read_build_ids(bool with_hits) | ||
1103 | { | ||
1104 | bool kbuildids = __dsos__read_build_ids(&dsos__kernel, with_hits), | ||
1105 | ubuildids = __dsos__read_build_ids(&dsos__user, with_hits); | ||
1106 | return kbuildids || ubuildids; | ||
1107 | } | ||
1108 | |||
1109 | /* | ||
1110 | * Align offset to 4 bytes as needed for note name and descriptor data. | ||
1111 | */ | ||
1112 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | ||
1113 | |||
1114 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
1115 | { | ||
1116 | int fd, err = -1; | ||
666 | GElf_Ehdr ehdr; | 1117 | GElf_Ehdr ehdr; |
667 | GElf_Shdr shdr; | 1118 | GElf_Shdr shdr; |
668 | Elf_Data *build_id_data; | 1119 | Elf_Data *data; |
669 | Elf_Scn *sec; | 1120 | Elf_Scn *sec; |
670 | char *build_id = NULL, *bid; | 1121 | Elf_Kind ek; |
671 | unsigned char *raw; | 1122 | void *ptr; |
672 | Elf *elf; | 1123 | Elf *elf; |
673 | int fd = open(self->name, O_RDONLY); | ||
674 | 1124 | ||
1125 | if (size < BUILD_ID_SIZE) | ||
1126 | goto out; | ||
1127 | |||
1128 | fd = open(filename, O_RDONLY); | ||
675 | if (fd < 0) | 1129 | if (fd < 0) |
676 | goto out; | 1130 | goto out; |
677 | 1131 | ||
678 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 1132 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
679 | if (elf == NULL) { | 1133 | if (elf == NULL) { |
680 | if (v) | 1134 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); |
681 | fprintf(stderr, "%s: cannot read %s ELF file.\n", | ||
682 | __func__, self->name); | ||
683 | goto out_close; | 1135 | goto out_close; |
684 | } | 1136 | } |
685 | 1137 | ||
1138 | ek = elf_kind(elf); | ||
1139 | if (ek != ELF_K_ELF) | ||
1140 | goto out_elf_end; | ||
1141 | |||
686 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 1142 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
687 | if (v) | 1143 | pr_err("%s: cannot get elf header.\n", __func__); |
688 | fprintf(stderr, "%s: cannot get elf header.\n", __func__); | ||
689 | goto out_elf_end; | 1144 | goto out_elf_end; |
690 | } | 1145 | } |
691 | 1146 | ||
692 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | 1147 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
693 | if (sec == NULL) | 1148 | ".note.gnu.build-id", NULL); |
694 | goto out_elf_end; | 1149 | if (sec == NULL) { |
1150 | sec = elf_section_by_name(elf, &ehdr, &shdr, | ||
1151 | ".notes", NULL); | ||
1152 | if (sec == NULL) | ||
1153 | goto out_elf_end; | ||
1154 | } | ||
695 | 1155 | ||
696 | build_id_data = elf_getdata(sec, NULL); | 1156 | data = elf_getdata(sec, NULL); |
697 | if (build_id_data == NULL) | 1157 | if (data == NULL) |
698 | goto out_elf_end; | ||
699 | build_id = malloc(BUILD_ID_SIZE); | ||
700 | if (build_id == NULL) | ||
701 | goto out_elf_end; | 1158 | goto out_elf_end; |
702 | raw = build_id_data->d_buf + 16; | ||
703 | bid = build_id; | ||
704 | 1159 | ||
705 | for (i = 0; i < 20; ++i) { | 1160 | ptr = data->d_buf; |
706 | sprintf(bid, "%02x", *raw); | 1161 | while (ptr < (data->d_buf + data->d_size)) { |
707 | ++raw; | 1162 | GElf_Nhdr *nhdr = ptr; |
708 | bid += 2; | 1163 | int namesz = NOTE_ALIGN(nhdr->n_namesz), |
1164 | descsz = NOTE_ALIGN(nhdr->n_descsz); | ||
1165 | const char *name; | ||
1166 | |||
1167 | ptr += sizeof(*nhdr); | ||
1168 | name = ptr; | ||
1169 | ptr += namesz; | ||
1170 | if (nhdr->n_type == NT_GNU_BUILD_ID && | ||
1171 | nhdr->n_namesz == sizeof("GNU")) { | ||
1172 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | ||
1173 | memcpy(bf, ptr, BUILD_ID_SIZE); | ||
1174 | err = BUILD_ID_SIZE; | ||
1175 | break; | ||
1176 | } | ||
1177 | } | ||
1178 | ptr += descsz; | ||
709 | } | 1179 | } |
710 | if (v >= 2) | ||
711 | printf("%s(%s): %s\n", __func__, self->name, build_id); | ||
712 | out_elf_end: | 1180 | out_elf_end: |
713 | elf_end(elf); | 1181 | elf_end(elf); |
714 | out_close: | 1182 | out_close: |
715 | close(fd); | 1183 | close(fd); |
716 | out: | 1184 | out: |
717 | return build_id; | 1185 | return err; |
1186 | } | ||
1187 | |||
1188 | int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | ||
1189 | { | ||
1190 | int fd, err = -1; | ||
1191 | |||
1192 | if (size < BUILD_ID_SIZE) | ||
1193 | goto out; | ||
1194 | |||
1195 | fd = open(filename, O_RDONLY); | ||
1196 | if (fd < 0) | ||
1197 | goto out; | ||
1198 | |||
1199 | while (1) { | ||
1200 | char bf[BUFSIZ]; | ||
1201 | GElf_Nhdr nhdr; | ||
1202 | int namesz, descsz; | ||
1203 | |||
1204 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | ||
1205 | break; | ||
1206 | |||
1207 | namesz = NOTE_ALIGN(nhdr.n_namesz); | ||
1208 | descsz = NOTE_ALIGN(nhdr.n_descsz); | ||
1209 | if (nhdr.n_type == NT_GNU_BUILD_ID && | ||
1210 | nhdr.n_namesz == sizeof("GNU")) { | ||
1211 | if (read(fd, bf, namesz) != namesz) | ||
1212 | break; | ||
1213 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | ||
1214 | if (read(fd, build_id, | ||
1215 | BUILD_ID_SIZE) == BUILD_ID_SIZE) { | ||
1216 | err = 0; | ||
1217 | break; | ||
1218 | } | ||
1219 | } else if (read(fd, bf, descsz) != descsz) | ||
1220 | break; | ||
1221 | } else { | ||
1222 | int n = namesz + descsz; | ||
1223 | if (read(fd, bf, n) != n) | ||
1224 | break; | ||
1225 | } | ||
1226 | } | ||
1227 | close(fd); | ||
1228 | out: | ||
1229 | return err; | ||
718 | } | 1230 | } |
719 | 1231 | ||
720 | char dso__symtab_origin(const struct dso *self) | 1232 | char dso__symtab_origin(const struct dso *self) |
@@ -722,10 +1234,12 @@ char dso__symtab_origin(const struct dso *self) | |||
722 | static const char origin[] = { | 1234 | static const char origin[] = { |
723 | [DSO__ORIG_KERNEL] = 'k', | 1235 | [DSO__ORIG_KERNEL] = 'k', |
724 | [DSO__ORIG_JAVA_JIT] = 'j', | 1236 | [DSO__ORIG_JAVA_JIT] = 'j', |
1237 | [DSO__ORIG_BUILD_ID_CACHE] = 'B', | ||
725 | [DSO__ORIG_FEDORA] = 'f', | 1238 | [DSO__ORIG_FEDORA] = 'f', |
726 | [DSO__ORIG_UBUNTU] = 'u', | 1239 | [DSO__ORIG_UBUNTU] = 'u', |
727 | [DSO__ORIG_BUILDID] = 'b', | 1240 | [DSO__ORIG_BUILDID] = 'b', |
728 | [DSO__ORIG_DSO] = 'd', | 1241 | [DSO__ORIG_DSO] = 'd', |
1242 | [DSO__ORIG_KMODULE] = 'K', | ||
729 | }; | 1243 | }; |
730 | 1244 | ||
731 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | 1245 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) |
@@ -733,60 +1247,90 @@ char dso__symtab_origin(const struct dso *self) | |||
733 | return origin[self->origin]; | 1247 | return origin[self->origin]; |
734 | } | 1248 | } |
735 | 1249 | ||
736 | int dso__load(struct dso *self, symbol_filter_t filter, int v) | 1250 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) |
737 | { | 1251 | { |
738 | int size = PATH_MAX; | 1252 | int size = PATH_MAX; |
739 | char *name = malloc(size), *build_id = NULL; | 1253 | char *name; |
1254 | u8 build_id[BUILD_ID_SIZE]; | ||
1255 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
740 | int ret = -1; | 1256 | int ret = -1; |
741 | int fd; | 1257 | int fd; |
742 | 1258 | ||
1259 | dso__set_loaded(self, map->type); | ||
1260 | |||
1261 | if (self->kernel) | ||
1262 | return dso__load_kernel_sym(self, map, filter); | ||
1263 | |||
1264 | name = malloc(size); | ||
743 | if (!name) | 1265 | if (!name) |
744 | return -1; | 1266 | return -1; |
745 | 1267 | ||
746 | self->adjust_symbols = 0; | 1268 | self->adjust_symbols = 0; |
747 | 1269 | ||
748 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { | 1270 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
749 | ret = dso__load_perf_map(self, filter, v); | 1271 | ret = dso__load_perf_map(self, map, filter); |
750 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | 1272 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : |
751 | DSO__ORIG_NOT_FOUND; | 1273 | DSO__ORIG_NOT_FOUND; |
752 | return ret; | 1274 | return ret; |
753 | } | 1275 | } |
754 | 1276 | ||
755 | self->origin = DSO__ORIG_FEDORA - 1; | 1277 | self->origin = DSO__ORIG_BUILD_ID_CACHE; |
756 | 1278 | ||
1279 | if (self->has_build_id) { | ||
1280 | build_id__sprintf(self->build_id, sizeof(self->build_id), | ||
1281 | build_id_hex); | ||
1282 | snprintf(name, size, "%s/%s/.build-id/%.2s/%s", | ||
1283 | getenv("HOME"), DEBUG_CACHE_DIR, | ||
1284 | build_id_hex, build_id_hex + 2); | ||
1285 | goto open_file; | ||
1286 | } | ||
757 | more: | 1287 | more: |
758 | do { | 1288 | do { |
759 | self->origin++; | 1289 | self->origin++; |
760 | switch (self->origin) { | 1290 | switch (self->origin) { |
761 | case DSO__ORIG_FEDORA: | 1291 | case DSO__ORIG_FEDORA: |
762 | snprintf(name, size, "/usr/lib/debug%s.debug", self->name); | 1292 | snprintf(name, size, "/usr/lib/debug%s.debug", |
1293 | self->long_name); | ||
763 | break; | 1294 | break; |
764 | case DSO__ORIG_UBUNTU: | 1295 | case DSO__ORIG_UBUNTU: |
765 | snprintf(name, size, "/usr/lib/debug%s", self->name); | 1296 | snprintf(name, size, "/usr/lib/debug%s", |
1297 | self->long_name); | ||
766 | break; | 1298 | break; |
767 | case DSO__ORIG_BUILDID: | 1299 | case DSO__ORIG_BUILDID: |
768 | build_id = dso__read_build_id(self, v); | 1300 | if (filename__read_build_id(self->long_name, build_id, |
769 | if (build_id != NULL) { | 1301 | sizeof(build_id))) { |
1302 | build_id__sprintf(build_id, sizeof(build_id), | ||
1303 | build_id_hex); | ||
770 | snprintf(name, size, | 1304 | snprintf(name, size, |
771 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 1305 | "/usr/lib/debug/.build-id/%.2s/%s.debug", |
772 | build_id, build_id + 2); | 1306 | build_id_hex, build_id_hex + 2); |
773 | free(build_id); | 1307 | if (self->has_build_id) |
1308 | goto compare_build_id; | ||
774 | break; | 1309 | break; |
775 | } | 1310 | } |
776 | self->origin++; | 1311 | self->origin++; |
777 | /* Fall thru */ | 1312 | /* Fall thru */ |
778 | case DSO__ORIG_DSO: | 1313 | case DSO__ORIG_DSO: |
779 | snprintf(name, size, "%s", self->name); | 1314 | snprintf(name, size, "%s", self->long_name); |
780 | break; | 1315 | break; |
781 | 1316 | ||
782 | default: | 1317 | default: |
783 | goto out; | 1318 | goto out; |
784 | } | 1319 | } |
785 | 1320 | ||
1321 | if (self->has_build_id) { | ||
1322 | if (filename__read_build_id(name, build_id, | ||
1323 | sizeof(build_id)) < 0) | ||
1324 | goto more; | ||
1325 | compare_build_id: | ||
1326 | if (!dso__build_id_equal(self, build_id)) | ||
1327 | goto more; | ||
1328 | } | ||
1329 | open_file: | ||
786 | fd = open(name, O_RDONLY); | 1330 | fd = open(name, O_RDONLY); |
787 | } while (fd < 0); | 1331 | } while (fd < 0); |
788 | 1332 | ||
789 | ret = dso__load_sym(self, fd, name, filter, v, NULL); | 1333 | ret = dso__load_sym(self, map, name, fd, filter, 0); |
790 | close(fd); | 1334 | close(fd); |
791 | 1335 | ||
792 | /* | 1336 | /* |
@@ -796,7 +1340,7 @@ more: | |||
796 | goto more; | 1340 | goto more; |
797 | 1341 | ||
798 | if (ret > 0) { | 1342 | if (ret > 0) { |
799 | int nr_plt = dso__synthesize_plt_symbols(self, v); | 1343 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); |
800 | if (nr_plt > 0) | 1344 | if (nr_plt > 0) |
801 | ret += nr_plt; | 1345 | ret += nr_plt; |
802 | } | 1346 | } |
@@ -807,231 +1351,608 @@ out: | |||
807 | return ret; | 1351 | return ret; |
808 | } | 1352 | } |
809 | 1353 | ||
810 | static int dso__load_module(struct dso *self, struct mod_dso *mods, const char *name, | 1354 | struct map *map_groups__find_by_name(struct map_groups *self, |
811 | symbol_filter_t filter, int v) | 1355 | enum map_type type, const char *name) |
812 | { | 1356 | { |
813 | struct module *mod = mod_dso__find_module(mods, name); | 1357 | struct rb_node *nd; |
814 | int err = 0, fd; | ||
815 | 1358 | ||
816 | if (mod == NULL || !mod->active) | 1359 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
817 | return err; | 1360 | struct map *map = rb_entry(nd, struct map, rb_node); |
818 | 1361 | ||
819 | fd = open(mod->path, O_RDONLY); | 1362 | if (map->dso && strcmp(map->dso->short_name, name) == 0) |
1363 | return map; | ||
1364 | } | ||
820 | 1365 | ||
821 | if (fd < 0) | 1366 | return NULL; |
822 | return err; | 1367 | } |
823 | 1368 | ||
824 | err = dso__load_sym(self, fd, name, filter, v, mod); | 1369 | static int dso__kernel_module_get_build_id(struct dso *self) |
825 | close(fd); | 1370 | { |
1371 | char filename[PATH_MAX]; | ||
1372 | /* | ||
1373 | * kernel module short names are of the form "[module]" and | ||
1374 | * we need just "module" here. | ||
1375 | */ | ||
1376 | const char *name = self->short_name + 1; | ||
826 | 1377 | ||
827 | return err; | 1378 | snprintf(filename, sizeof(filename), |
1379 | "/sys/module/%.*s/notes/.note.gnu.build-id", | ||
1380 | (int)strlen(name - 1), name); | ||
1381 | |||
1382 | if (sysfs__read_build_id(filename, self->build_id, | ||
1383 | sizeof(self->build_id)) == 0) | ||
1384 | self->has_build_id = true; | ||
1385 | |||
1386 | return 0; | ||
828 | } | 1387 | } |
829 | 1388 | ||
830 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int v) | 1389 | static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirname) |
831 | { | 1390 | { |
832 | struct mod_dso *mods = mod_dso__new_dso("modules"); | 1391 | struct dirent *dent; |
833 | struct module *pos; | 1392 | DIR *dir = opendir(dirname); |
834 | struct rb_node *next; | ||
835 | int err, count = 0; | ||
836 | 1393 | ||
837 | err = mod_dso__load_modules(mods); | 1394 | if (!dir) { |
1395 | pr_debug("%s: cannot open %s dir\n", __func__, dirname); | ||
1396 | return -1; | ||
1397 | } | ||
838 | 1398 | ||
839 | if (err <= 0) | 1399 | while ((dent = readdir(dir)) != NULL) { |
840 | return err; | 1400 | char path[PATH_MAX]; |
1401 | |||
1402 | if (dent->d_type == DT_DIR) { | ||
1403 | if (!strcmp(dent->d_name, ".") || | ||
1404 | !strcmp(dent->d_name, "..")) | ||
1405 | continue; | ||
1406 | |||
1407 | snprintf(path, sizeof(path), "%s/%s", | ||
1408 | dirname, dent->d_name); | ||
1409 | if (map_groups__set_modules_path_dir(self, path) < 0) | ||
1410 | goto failure; | ||
1411 | } else { | ||
1412 | char *dot = strrchr(dent->d_name, '.'), | ||
1413 | dso_name[PATH_MAX]; | ||
1414 | struct map *map; | ||
1415 | char *long_name; | ||
1416 | |||
1417 | if (dot == NULL || strcmp(dot, ".ko")) | ||
1418 | continue; | ||
1419 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
1420 | (int)(dot - dent->d_name), dent->d_name); | ||
1421 | |||
1422 | strxfrchar(dso_name, '-', '_'); | ||
1423 | map = map_groups__find_by_name(self, MAP__FUNCTION, dso_name); | ||
1424 | if (map == NULL) | ||
1425 | continue; | ||
1426 | |||
1427 | snprintf(path, sizeof(path), "%s/%s", | ||
1428 | dirname, dent->d_name); | ||
1429 | |||
1430 | long_name = strdup(path); | ||
1431 | if (long_name == NULL) | ||
1432 | goto failure; | ||
1433 | dso__set_long_name(map->dso, long_name); | ||
1434 | dso__kernel_module_get_build_id(map->dso); | ||
1435 | } | ||
1436 | } | ||
841 | 1437 | ||
842 | /* | 1438 | return 0; |
843 | * Iterate over modules, and load active symbols. | 1439 | failure: |
844 | */ | 1440 | closedir(dir); |
845 | next = rb_first(&mods->mods); | 1441 | return -1; |
846 | while (next) { | 1442 | } |
847 | pos = rb_entry(next, struct module, rb_node); | ||
848 | err = dso__load_module(self, mods, pos->name, filter, v); | ||
849 | 1443 | ||
850 | if (err < 0) | 1444 | static int map_groups__set_modules_path(struct map_groups *self) |
851 | break; | 1445 | { |
1446 | struct utsname uts; | ||
1447 | char modules_path[PATH_MAX]; | ||
852 | 1448 | ||
853 | next = rb_next(&pos->rb_node); | 1449 | if (uname(&uts) < 0) |
854 | count += err; | 1450 | return -1; |
855 | } | ||
856 | 1451 | ||
857 | if (err < 0) { | 1452 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", |
858 | mod_dso__delete_modules(mods); | 1453 | uts.release); |
859 | mod_dso__delete_self(mods); | 1454 | |
860 | return err; | 1455 | return map_groups__set_modules_path_dir(self, modules_path); |
1456 | } | ||
1457 | |||
1458 | /* | ||
1459 | * Constructor variant for modules (where we know from /proc/modules where | ||
1460 | * they are loaded) and for vmlinux, where only after we load all the | ||
1461 | * symbols we'll know where it starts and ends. | ||
1462 | */ | ||
1463 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | ||
1464 | { | ||
1465 | struct map *self = zalloc(sizeof(*self) + | ||
1466 | (dso->kernel ? sizeof(struct kmap) : 0)); | ||
1467 | if (self != NULL) { | ||
1468 | /* | ||
1469 | * ->end will be filled after we load all the symbols | ||
1470 | */ | ||
1471 | map__init(self, type, start, 0, 0, dso); | ||
861 | } | 1472 | } |
862 | 1473 | ||
863 | return count; | 1474 | return self; |
864 | } | 1475 | } |
865 | 1476 | ||
866 | static inline void dso__fill_symbol_holes(struct dso *self) | 1477 | struct map *map_groups__new_module(struct map_groups *self, u64 start, |
1478 | const char *filename) | ||
867 | { | 1479 | { |
868 | struct symbol *prev = NULL; | 1480 | struct map *map; |
869 | struct rb_node *nd; | 1481 | struct dso *dso = __dsos__findnew(&dsos__kernel, filename); |
870 | 1482 | ||
871 | for (nd = rb_last(&self->syms); nd; nd = rb_prev(nd)) { | 1483 | if (dso == NULL) |
872 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 1484 | return NULL; |
873 | 1485 | ||
874 | if (prev) { | 1486 | map = map__new2(start, dso, MAP__FUNCTION); |
875 | u64 hole = 0; | 1487 | if (map == NULL) |
876 | int alias = pos->start == prev->start; | 1488 | return NULL; |
877 | 1489 | ||
878 | if (!alias) | 1490 | dso->origin = DSO__ORIG_KMODULE; |
879 | hole = prev->start - pos->end - 1; | 1491 | map_groups__insert(self, map); |
1492 | return map; | ||
1493 | } | ||
880 | 1494 | ||
881 | if (hole || alias) { | 1495 | static int map_groups__create_modules(struct map_groups *self) |
882 | if (alias) | 1496 | { |
883 | pos->end = prev->end; | 1497 | char *line = NULL; |
884 | else if (hole) | 1498 | size_t n; |
885 | pos->end = prev->start - 1; | 1499 | FILE *file = fopen("/proc/modules", "r"); |
886 | } | 1500 | struct map *map; |
887 | } | 1501 | |
888 | prev = pos; | 1502 | if (file == NULL) |
1503 | return -1; | ||
1504 | |||
1505 | while (!feof(file)) { | ||
1506 | char name[PATH_MAX]; | ||
1507 | u64 start; | ||
1508 | char *sep; | ||
1509 | int line_len; | ||
1510 | |||
1511 | line_len = getline(&line, &n, file); | ||
1512 | if (line_len < 0) | ||
1513 | break; | ||
1514 | |||
1515 | if (!line) | ||
1516 | goto out_failure; | ||
1517 | |||
1518 | line[--line_len] = '\0'; /* \n */ | ||
1519 | |||
1520 | sep = strrchr(line, 'x'); | ||
1521 | if (sep == NULL) | ||
1522 | continue; | ||
1523 | |||
1524 | hex2u64(sep + 1, &start); | ||
1525 | |||
1526 | sep = strchr(line, ' '); | ||
1527 | if (sep == NULL) | ||
1528 | continue; | ||
1529 | |||
1530 | *sep = '\0'; | ||
1531 | |||
1532 | snprintf(name, sizeof(name), "[%s]", line); | ||
1533 | map = map_groups__new_module(self, start, name); | ||
1534 | if (map == NULL) | ||
1535 | goto out_delete_line; | ||
1536 | dso__kernel_module_get_build_id(map->dso); | ||
889 | } | 1537 | } |
1538 | |||
1539 | free(line); | ||
1540 | fclose(file); | ||
1541 | |||
1542 | return map_groups__set_modules_path(self); | ||
1543 | |||
1544 | out_delete_line: | ||
1545 | free(line); | ||
1546 | out_failure: | ||
1547 | return -1; | ||
890 | } | 1548 | } |
891 | 1549 | ||
892 | static int dso__load_vmlinux(struct dso *self, const char *vmlinux, | 1550 | static int dso__load_vmlinux(struct dso *self, struct map *map, |
893 | symbol_filter_t filter, int v) | 1551 | const char *vmlinux, symbol_filter_t filter) |
894 | { | 1552 | { |
895 | int err, fd = open(vmlinux, O_RDONLY); | 1553 | int err = -1, fd; |
1554 | |||
1555 | if (self->has_build_id) { | ||
1556 | u8 build_id[BUILD_ID_SIZE]; | ||
1557 | |||
1558 | if (filename__read_build_id(vmlinux, build_id, | ||
1559 | sizeof(build_id)) < 0) { | ||
1560 | pr_debug("No build_id in %s, ignoring it\n", vmlinux); | ||
1561 | return -1; | ||
1562 | } | ||
1563 | if (!dso__build_id_equal(self, build_id)) { | ||
1564 | char expected_build_id[BUILD_ID_SIZE * 2 + 1], | ||
1565 | vmlinux_build_id[BUILD_ID_SIZE * 2 + 1]; | ||
1566 | |||
1567 | build_id__sprintf(self->build_id, | ||
1568 | sizeof(self->build_id), | ||
1569 | expected_build_id); | ||
1570 | build_id__sprintf(build_id, sizeof(build_id), | ||
1571 | vmlinux_build_id); | ||
1572 | pr_debug("build_id in %s is %s while expected is %s, " | ||
1573 | "ignoring it\n", vmlinux, vmlinux_build_id, | ||
1574 | expected_build_id); | ||
1575 | return -1; | ||
1576 | } | ||
1577 | } | ||
896 | 1578 | ||
1579 | fd = open(vmlinux, O_RDONLY); | ||
897 | if (fd < 0) | 1580 | if (fd < 0) |
898 | return -1; | 1581 | return -1; |
899 | 1582 | ||
900 | err = dso__load_sym(self, fd, vmlinux, filter, v, NULL); | 1583 | dso__set_loaded(self, map->type); |
1584 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0); | ||
1585 | close(fd); | ||
901 | 1586 | ||
902 | if (err > 0) | 1587 | if (err > 0) |
903 | dso__fill_symbol_holes(self); | 1588 | pr_debug("Using %s for symbols\n", vmlinux); |
904 | 1589 | ||
905 | close(fd); | 1590 | return err; |
1591 | } | ||
1592 | |||
1593 | int dso__load_vmlinux_path(struct dso *self, struct map *map, | ||
1594 | symbol_filter_t filter) | ||
1595 | { | ||
1596 | int i, err = 0; | ||
1597 | |||
1598 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | ||
1599 | vmlinux_path__nr_entries); | ||
1600 | |||
1601 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | ||
1602 | err = dso__load_vmlinux(self, map, vmlinux_path[i], filter); | ||
1603 | if (err > 0) { | ||
1604 | dso__set_long_name(self, strdup(vmlinux_path[i])); | ||
1605 | break; | ||
1606 | } | ||
1607 | } | ||
906 | 1608 | ||
907 | return err; | 1609 | return err; |
908 | } | 1610 | } |
909 | 1611 | ||
910 | int dso__load_kernel(struct dso *self, const char *vmlinux, | 1612 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
911 | symbol_filter_t filter, int v, int use_modules) | 1613 | symbol_filter_t filter) |
912 | { | 1614 | { |
913 | int err = -1; | 1615 | int err; |
1616 | const char *kallsyms_filename = NULL; | ||
1617 | char *kallsyms_allocated_filename = NULL; | ||
1618 | /* | ||
1619 | * Step 1: if the user specified a vmlinux filename, use it and only | ||
1620 | * it, reporting errors to the user if it cannot be used. | ||
1621 | * | ||
1622 | * For instance, try to analyse an ARM perf.data file _without_ a | ||
1623 | * build-id, or if the user specifies the wrong path to the right | ||
1624 | * vmlinux file, obviously we can't fallback to another vmlinux (a | ||
1625 | * x86_86 one, on the machine where analysis is being performed, say), | ||
1626 | * or worse, /proc/kallsyms. | ||
1627 | * | ||
1628 | * If the specified file _has_ a build-id and there is a build-id | ||
1629 | * section in the perf.data file, we will still do the expected | ||
1630 | * validation in dso__load_vmlinux and will bail out if they don't | ||
1631 | * match. | ||
1632 | */ | ||
1633 | if (symbol_conf.vmlinux_name != NULL) { | ||
1634 | err = dso__load_vmlinux(self, map, | ||
1635 | symbol_conf.vmlinux_name, filter); | ||
1636 | goto out_try_fixup; | ||
1637 | } | ||
914 | 1638 | ||
915 | if (vmlinux) { | 1639 | if (vmlinux_path != NULL) { |
916 | err = dso__load_vmlinux(self, vmlinux, filter, v); | 1640 | err = dso__load_vmlinux_path(self, map, filter); |
917 | if (err > 0 && use_modules) { | 1641 | if (err > 0) |
918 | int syms = dso__load_modules(self, filter, v); | 1642 | goto out_fixup; |
1643 | } | ||
919 | 1644 | ||
920 | if (syms < 0) { | 1645 | /* |
921 | fprintf(stderr, "dso__load_modules failed!\n"); | 1646 | * Say the kernel DSO was created when processing the build-id header table, |
922 | return syms; | 1647 | * we have a build-id, so check if it is the same as the running kernel, |
1648 | * using it if it is. | ||
1649 | */ | ||
1650 | if (self->has_build_id) { | ||
1651 | u8 kallsyms_build_id[BUILD_ID_SIZE]; | ||
1652 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1653 | |||
1654 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||
1655 | sizeof(kallsyms_build_id)) == 0) { | ||
1656 | if (dso__build_id_equal(self, kallsyms_build_id)) { | ||
1657 | kallsyms_filename = "/proc/kallsyms"; | ||
1658 | goto do_kallsyms; | ||
923 | } | 1659 | } |
924 | err += syms; | ||
925 | } | 1660 | } |
926 | } | 1661 | /* |
1662 | * Now look if we have it on the build-id cache in | ||
1663 | * $HOME/.debug/[kernel.kallsyms]. | ||
1664 | */ | ||
1665 | build_id__sprintf(self->build_id, sizeof(self->build_id), | ||
1666 | sbuild_id); | ||
1667 | |||
1668 | if (asprintf(&kallsyms_allocated_filename, | ||
1669 | "%s/.debug/[kernel.kallsyms]/%s", | ||
1670 | getenv("HOME"), sbuild_id) == -1) { | ||
1671 | pr_err("Not enough memory for kallsyms file lookup\n"); | ||
1672 | return -1; | ||
1673 | } | ||
1674 | |||
1675 | kallsyms_filename = kallsyms_allocated_filename; | ||
927 | 1676 | ||
928 | if (err <= 0) | 1677 | if (access(kallsyms_filename, F_OK)) { |
929 | err = dso__load_kallsyms(self, filter, v); | 1678 | pr_err("No kallsyms or vmlinux with build-id %s " |
1679 | "was found\n", sbuild_id); | ||
1680 | free(kallsyms_allocated_filename); | ||
1681 | return -1; | ||
1682 | } | ||
1683 | } else { | ||
1684 | /* | ||
1685 | * Last resort, if we don't have a build-id and couldn't find | ||
1686 | * any vmlinux file, try the running kernel kallsyms table. | ||
1687 | */ | ||
1688 | kallsyms_filename = "/proc/kallsyms"; | ||
1689 | } | ||
930 | 1690 | ||
1691 | do_kallsyms: | ||
1692 | err = dso__load_kallsyms(self, kallsyms_filename, map, filter); | ||
931 | if (err > 0) | 1693 | if (err > 0) |
932 | self->origin = DSO__ORIG_KERNEL; | 1694 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
1695 | free(kallsyms_allocated_filename); | ||
1696 | |||
1697 | out_try_fixup: | ||
1698 | if (err > 0) { | ||
1699 | out_fixup: | ||
1700 | if (kallsyms_filename != NULL) | ||
1701 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | ||
1702 | map__fixup_start(map); | ||
1703 | map__fixup_end(map); | ||
1704 | } | ||
933 | 1705 | ||
934 | return err; | 1706 | return err; |
935 | } | 1707 | } |
936 | 1708 | ||
937 | LIST_HEAD(dsos); | 1709 | LIST_HEAD(dsos__user); |
938 | struct dso *kernel_dso; | 1710 | LIST_HEAD(dsos__kernel); |
939 | struct dso *vdso; | ||
940 | struct dso *hypervisor_dso; | ||
941 | |||
942 | const char *vmlinux_name = "vmlinux"; | ||
943 | int modules; | ||
944 | 1711 | ||
945 | static void dsos__add(struct dso *dso) | 1712 | static void dsos__add(struct list_head *head, struct dso *dso) |
946 | { | 1713 | { |
947 | list_add_tail(&dso->node, &dsos); | 1714 | list_add_tail(&dso->node, head); |
948 | } | 1715 | } |
949 | 1716 | ||
950 | static struct dso *dsos__find(const char *name) | 1717 | static struct dso *dsos__find(struct list_head *head, const char *name) |
951 | { | 1718 | { |
952 | struct dso *pos; | 1719 | struct dso *pos; |
953 | 1720 | ||
954 | list_for_each_entry(pos, &dsos, node) | 1721 | list_for_each_entry(pos, head, node) |
955 | if (strcmp(pos->name, name) == 0) | 1722 | if (strcmp(pos->long_name, name) == 0) |
956 | return pos; | 1723 | return pos; |
957 | return NULL; | 1724 | return NULL; |
958 | } | 1725 | } |
959 | 1726 | ||
960 | struct dso *dsos__findnew(const char *name) | 1727 | struct dso *__dsos__findnew(struct list_head *head, const char *name) |
961 | { | 1728 | { |
962 | struct dso *dso = dsos__find(name); | 1729 | struct dso *dso = dsos__find(head, name); |
963 | int nr; | ||
964 | |||
965 | if (dso) | ||
966 | return dso; | ||
967 | |||
968 | dso = dso__new(name, 0); | ||
969 | if (!dso) | ||
970 | goto out_delete_dso; | ||
971 | 1730 | ||
972 | nr = dso__load(dso, NULL, verbose); | 1731 | if (!dso) { |
973 | if (nr < 0) { | 1732 | dso = dso__new(name); |
974 | eprintf("Failed to open: %s\n", name); | 1733 | if (dso != NULL) { |
975 | goto out_delete_dso; | 1734 | dsos__add(head, dso); |
1735 | dso__set_basename(dso); | ||
1736 | } | ||
976 | } | 1737 | } |
977 | if (!nr) | ||
978 | eprintf("No symbols found in: %s, maybe install a debug package?\n", name); | ||
979 | |||
980 | dsos__add(dso); | ||
981 | 1738 | ||
982 | return dso; | 1739 | return dso; |
1740 | } | ||
983 | 1741 | ||
984 | out_delete_dso: | 1742 | static void __dsos__fprintf(struct list_head *head, FILE *fp) |
985 | dso__delete(dso); | 1743 | { |
986 | return NULL; | 1744 | struct dso *pos; |
1745 | |||
1746 | list_for_each_entry(pos, head, node) { | ||
1747 | int i; | ||
1748 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
1749 | dso__fprintf(pos, i, fp); | ||
1750 | } | ||
987 | } | 1751 | } |
988 | 1752 | ||
989 | void dsos__fprintf(FILE *fp) | 1753 | void dsos__fprintf(FILE *fp) |
990 | { | 1754 | { |
1755 | __dsos__fprintf(&dsos__kernel, fp); | ||
1756 | __dsos__fprintf(&dsos__user, fp); | ||
1757 | } | ||
1758 | |||
1759 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
1760 | bool with_hits) | ||
1761 | { | ||
991 | struct dso *pos; | 1762 | struct dso *pos; |
1763 | size_t ret = 0; | ||
992 | 1764 | ||
993 | list_for_each_entry(pos, &dsos, node) | 1765 | list_for_each_entry(pos, head, node) { |
994 | dso__fprintf(pos, fp); | 1766 | if (with_hits && !pos->hit) |
1767 | continue; | ||
1768 | ret += dso__fprintf_buildid(pos, fp); | ||
1769 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
1770 | } | ||
1771 | return ret; | ||
995 | } | 1772 | } |
996 | 1773 | ||
997 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | 1774 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) |
998 | { | 1775 | { |
999 | return dso__find_symbol(dso, ip); | 1776 | return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + |
1777 | __dsos__fprintf_buildid(&dsos__user, fp, with_hits)); | ||
1000 | } | 1778 | } |
1001 | 1779 | ||
1002 | int load_kernel(void) | 1780 | struct dso *dso__new_kernel(const char *name) |
1003 | { | 1781 | { |
1004 | int err; | 1782 | struct dso *self = dso__new(name ?: "[kernel.kallsyms]"); |
1005 | 1783 | ||
1006 | kernel_dso = dso__new("[kernel]", 0); | 1784 | if (self != NULL) { |
1007 | if (!kernel_dso) | 1785 | self->short_name = "[kernel]"; |
1008 | return -1; | 1786 | self->kernel = 1; |
1787 | } | ||
1009 | 1788 | ||
1010 | err = dso__load_kernel(kernel_dso, vmlinux_name, NULL, verbose, modules); | 1789 | return self; |
1011 | if (err <= 0) { | 1790 | } |
1012 | dso__delete(kernel_dso); | ||
1013 | kernel_dso = NULL; | ||
1014 | } else | ||
1015 | dsos__add(kernel_dso); | ||
1016 | 1791 | ||
1017 | vdso = dso__new("[vdso]", 0); | 1792 | void dso__read_running_kernel_build_id(struct dso *self) |
1018 | if (!vdso) | 1793 | { |
1019 | return -1; | 1794 | if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, |
1795 | sizeof(self->build_id)) == 0) | ||
1796 | self->has_build_id = true; | ||
1797 | } | ||
1798 | |||
1799 | static struct dso *dsos__create_kernel(const char *vmlinux) | ||
1800 | { | ||
1801 | struct dso *kernel = dso__new_kernel(vmlinux); | ||
1802 | |||
1803 | if (kernel != NULL) { | ||
1804 | dso__read_running_kernel_build_id(kernel); | ||
1805 | dsos__add(&dsos__kernel, kernel); | ||
1806 | } | ||
1807 | |||
1808 | return kernel; | ||
1809 | } | ||
1810 | |||
1811 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
1812 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
1813 | struct dso *kernel) | ||
1814 | { | ||
1815 | enum map_type type; | ||
1816 | |||
1817 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
1818 | struct kmap *kmap; | ||
1819 | |||
1820 | vmlinux_maps[type] = map__new2(0, kernel, type); | ||
1821 | if (vmlinux_maps[type] == NULL) | ||
1822 | return -1; | ||
1823 | |||
1824 | vmlinux_maps[type]->map_ip = | ||
1825 | vmlinux_maps[type]->unmap_ip = identity__map_ip; | ||
1826 | |||
1827 | kmap = map__kmap(vmlinux_maps[type]); | ||
1828 | kmap->kmaps = self; | ||
1829 | map_groups__insert(self, vmlinux_maps[type]); | ||
1830 | } | ||
1831 | |||
1832 | return 0; | ||
1833 | } | ||
1834 | |||
1835 | static void vmlinux_path__exit(void) | ||
1836 | { | ||
1837 | while (--vmlinux_path__nr_entries >= 0) { | ||
1838 | free(vmlinux_path[vmlinux_path__nr_entries]); | ||
1839 | vmlinux_path[vmlinux_path__nr_entries] = NULL; | ||
1840 | } | ||
1020 | 1841 | ||
1021 | vdso->find_symbol = vdso__find_symbol; | 1842 | free(vmlinux_path); |
1843 | vmlinux_path = NULL; | ||
1844 | } | ||
1022 | 1845 | ||
1023 | dsos__add(vdso); | 1846 | static int vmlinux_path__init(void) |
1847 | { | ||
1848 | struct utsname uts; | ||
1849 | char bf[PATH_MAX]; | ||
1024 | 1850 | ||
1025 | hypervisor_dso = dso__new("[hypervisor]", 0); | 1851 | if (uname(&uts) < 0) |
1026 | if (!hypervisor_dso) | ||
1027 | return -1; | 1852 | return -1; |
1028 | dsos__add(hypervisor_dso); | ||
1029 | 1853 | ||
1030 | return err; | 1854 | vmlinux_path = malloc(sizeof(char *) * 5); |
1855 | if (vmlinux_path == NULL) | ||
1856 | return -1; | ||
1857 | |||
1858 | vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux"); | ||
1859 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1860 | goto out_fail; | ||
1861 | ++vmlinux_path__nr_entries; | ||
1862 | vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux"); | ||
1863 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1864 | goto out_fail; | ||
1865 | ++vmlinux_path__nr_entries; | ||
1866 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); | ||
1867 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
1868 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1869 | goto out_fail; | ||
1870 | ++vmlinux_path__nr_entries; | ||
1871 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release); | ||
1872 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
1873 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1874 | goto out_fail; | ||
1875 | ++vmlinux_path__nr_entries; | ||
1876 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", | ||
1877 | uts.release); | ||
1878 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
1879 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
1880 | goto out_fail; | ||
1881 | ++vmlinux_path__nr_entries; | ||
1882 | |||
1883 | return 0; | ||
1884 | |||
1885 | out_fail: | ||
1886 | vmlinux_path__exit(); | ||
1887 | return -1; | ||
1031 | } | 1888 | } |
1032 | 1889 | ||
1890 | static int setup_list(struct strlist **list, const char *list_str, | ||
1891 | const char *list_name) | ||
1892 | { | ||
1893 | if (list_str == NULL) | ||
1894 | return 0; | ||
1895 | |||
1896 | *list = strlist__new(true, list_str); | ||
1897 | if (!*list) { | ||
1898 | pr_err("problems parsing %s list\n", list_name); | ||
1899 | return -1; | ||
1900 | } | ||
1901 | return 0; | ||
1902 | } | ||
1033 | 1903 | ||
1034 | void symbol__init(void) | 1904 | int symbol__init(void) |
1035 | { | 1905 | { |
1036 | elf_version(EV_CURRENT); | 1906 | elf_version(EV_CURRENT); |
1907 | if (symbol_conf.sort_by_name) | ||
1908 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | ||
1909 | sizeof(struct symbol)); | ||
1910 | |||
1911 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) | ||
1912 | return -1; | ||
1913 | |||
1914 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { | ||
1915 | pr_err("'.' is the only non valid --field-separator argument\n"); | ||
1916 | return -1; | ||
1917 | } | ||
1918 | |||
1919 | if (setup_list(&symbol_conf.dso_list, | ||
1920 | symbol_conf.dso_list_str, "dso") < 0) | ||
1921 | return -1; | ||
1922 | |||
1923 | if (setup_list(&symbol_conf.comm_list, | ||
1924 | symbol_conf.comm_list_str, "comm") < 0) | ||
1925 | goto out_free_dso_list; | ||
1926 | |||
1927 | if (setup_list(&symbol_conf.sym_list, | ||
1928 | symbol_conf.sym_list_str, "symbol") < 0) | ||
1929 | goto out_free_comm_list; | ||
1930 | |||
1931 | return 0; | ||
1932 | |||
1933 | out_free_dso_list: | ||
1934 | strlist__delete(symbol_conf.dso_list); | ||
1935 | out_free_comm_list: | ||
1936 | strlist__delete(symbol_conf.comm_list); | ||
1937 | return -1; | ||
1938 | } | ||
1939 | |||
1940 | int map_groups__create_kernel_maps(struct map_groups *self, | ||
1941 | struct map *vmlinux_maps[MAP__NR_TYPES]) | ||
1942 | { | ||
1943 | struct dso *kernel = dsos__create_kernel(symbol_conf.vmlinux_name); | ||
1944 | |||
1945 | if (kernel == NULL) | ||
1946 | return -1; | ||
1947 | |||
1948 | if (__map_groups__create_kernel_maps(self, vmlinux_maps, kernel) < 0) | ||
1949 | return -1; | ||
1950 | |||
1951 | if (symbol_conf.use_modules && map_groups__create_modules(self) < 0) | ||
1952 | pr_debug("Problems creating module maps, continuing anyway...\n"); | ||
1953 | /* | ||
1954 | * Now that we have all the maps created, just set the ->end of them: | ||
1955 | */ | ||
1956 | map_groups__fixup_end(self); | ||
1957 | return 0; | ||
1037 | } | 1958 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 829da9edba64..280dadd32a08 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -1,13 +1,15 @@ | |||
1 | #ifndef _PERF_SYMBOL_ | 1 | #ifndef __PERF_SYMBOL |
2 | #define _PERF_SYMBOL_ 1 | 2 | #define __PERF_SYMBOL 1 |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <stdbool.h> | ||
5 | #include "types.h" | 6 | #include "types.h" |
6 | #include <linux/list.h> | 7 | #include <linux/list.h> |
7 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
8 | #include "module.h" | ||
9 | #include "event.h" | 9 | #include "event.h" |
10 | 10 | ||
11 | #define DEBUG_CACHE_DIR ".debug" | ||
12 | |||
11 | #ifdef HAVE_CPLUS_DEMANGLE | 13 | #ifdef HAVE_CPLUS_DEMANGLE |
12 | extern char *cplus_demangle(const char *, int); | 14 | extern char *cplus_demangle(const char *, int); |
13 | 15 | ||
@@ -46,57 +48,124 @@ struct symbol { | |||
46 | struct rb_node rb_node; | 48 | struct rb_node rb_node; |
47 | u64 start; | 49 | u64 start; |
48 | u64 end; | 50 | u64 end; |
49 | u64 obj_start; | ||
50 | u64 hist_sum; | ||
51 | u64 *hist; | ||
52 | struct module *module; | ||
53 | void *priv; | ||
54 | char name[0]; | 51 | char name[0]; |
55 | }; | 52 | }; |
56 | 53 | ||
54 | void symbol__delete(struct symbol *self); | ||
55 | |||
56 | struct strlist; | ||
57 | |||
58 | struct symbol_conf { | ||
59 | unsigned short priv_size; | ||
60 | bool try_vmlinux_path, | ||
61 | use_modules, | ||
62 | sort_by_name, | ||
63 | show_nr_samples, | ||
64 | use_callchain, | ||
65 | exclude_other, | ||
66 | full_paths; | ||
67 | const char *vmlinux_name, | ||
68 | *field_sep; | ||
69 | char *dso_list_str, | ||
70 | *comm_list_str, | ||
71 | *sym_list_str, | ||
72 | *col_width_list_str; | ||
73 | struct strlist *dso_list, | ||
74 | *comm_list, | ||
75 | *sym_list; | ||
76 | }; | ||
77 | |||
78 | extern struct symbol_conf symbol_conf; | ||
79 | |||
80 | static inline void *symbol__priv(struct symbol *self) | ||
81 | { | ||
82 | return ((void *)self) - symbol_conf.priv_size; | ||
83 | } | ||
84 | |||
85 | struct ref_reloc_sym { | ||
86 | const char *name; | ||
87 | u64 addr; | ||
88 | u64 unrelocated_addr; | ||
89 | }; | ||
90 | |||
91 | struct addr_location { | ||
92 | struct thread *thread; | ||
93 | struct map *map; | ||
94 | struct symbol *sym; | ||
95 | u64 addr; | ||
96 | char level; | ||
97 | bool filtered; | ||
98 | }; | ||
99 | |||
57 | struct dso { | 100 | struct dso { |
58 | struct list_head node; | 101 | struct list_head node; |
59 | struct rb_root syms; | 102 | struct rb_root symbols[MAP__NR_TYPES]; |
60 | struct symbol *(*find_symbol)(struct dso *, u64 ip); | 103 | struct rb_root symbol_names[MAP__NR_TYPES]; |
61 | unsigned int sym_priv_size; | 104 | u8 adjust_symbols:1; |
62 | unsigned char adjust_symbols; | 105 | u8 slen_calculated:1; |
63 | unsigned char slen_calculated; | 106 | u8 has_build_id:1; |
107 | u8 kernel:1; | ||
108 | u8 hit:1; | ||
64 | unsigned char origin; | 109 | unsigned char origin; |
110 | u8 sorted_by_name; | ||
111 | u8 loaded; | ||
112 | u8 build_id[BUILD_ID_SIZE]; | ||
113 | u16 long_name_len; | ||
114 | const char *short_name; | ||
115 | char *long_name; | ||
65 | char name[0]; | 116 | char name[0]; |
66 | }; | 117 | }; |
67 | 118 | ||
68 | extern const char *sym_hist_filter; | 119 | struct dso *dso__new(const char *name); |
69 | 120 | struct dso *dso__new_kernel(const char *name); | |
70 | typedef int (*symbol_filter_t)(struct dso *self, struct symbol *sym); | ||
71 | |||
72 | struct dso *dso__new(const char *name, unsigned int sym_priv_size); | ||
73 | void dso__delete(struct dso *self); | 121 | void dso__delete(struct dso *self); |
74 | 122 | ||
75 | static inline void *dso__sym_priv(struct dso *self, struct symbol *sym) | 123 | bool dso__loaded(const struct dso *self, enum map_type type); |
124 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); | ||
125 | |||
126 | static inline void dso__set_loaded(struct dso *self, enum map_type type) | ||
76 | { | 127 | { |
77 | return ((void *)sym) - self->sym_priv_size; | 128 | self->loaded |= (1 << type); |
78 | } | 129 | } |
79 | 130 | ||
80 | struct symbol *dso__find_symbol(struct dso *self, u64 ip); | 131 | void dso__sort_by_name(struct dso *self, enum map_type type); |
81 | 132 | ||
82 | int dso__load_kernel(struct dso *self, const char *vmlinux, | 133 | extern struct list_head dsos__user, dsos__kernel; |
83 | symbol_filter_t filter, int verbose, int modules); | ||
84 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); | ||
85 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); | ||
86 | struct dso *dsos__findnew(const char *name); | ||
87 | void dsos__fprintf(FILE *fp); | ||
88 | 134 | ||
89 | size_t dso__fprintf(struct dso *self, FILE *fp); | 135 | struct dso *__dsos__findnew(struct list_head *head, const char *name); |
90 | char dso__symtab_origin(const struct dso *self); | ||
91 | 136 | ||
92 | int load_kernel(void); | 137 | static inline struct dso *dsos__findnew(const char *name) |
138 | { | ||
139 | return __dsos__findnew(&dsos__user, name); | ||
140 | } | ||
93 | 141 | ||
94 | void symbol__init(void); | 142 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); |
143 | int dso__load_vmlinux_path(struct dso *self, struct map *map, | ||
144 | symbol_filter_t filter); | ||
145 | int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, | ||
146 | symbol_filter_t filter); | ||
147 | void dsos__fprintf(FILE *fp); | ||
148 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); | ||
95 | 149 | ||
96 | extern struct list_head dsos; | 150 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); |
97 | extern struct dso *kernel_dso; | 151 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
98 | extern struct dso *vdso; | 152 | char dso__symtab_origin(const struct dso *self); |
99 | extern struct dso *hypervisor_dso; | 153 | void dso__set_long_name(struct dso *self, char *name); |
100 | extern const char *vmlinux_name; | 154 | void dso__set_build_id(struct dso *self, void *build_id); |
101 | extern int modules; | 155 | void dso__read_running_kernel_build_id(struct dso *self); |
102 | #endif /* _PERF_SYMBOL_ */ | 156 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); |
157 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | ||
158 | const char *name); | ||
159 | |||
160 | int filename__read_build_id(const char *filename, void *bf, size_t size); | ||
161 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | ||
162 | bool dsos__read_build_ids(bool with_hits); | ||
163 | int build_id__sprintf(const u8 *self, int len, char *bf); | ||
164 | int kallsyms__parse(const char *filename, void *arg, | ||
165 | int (*process_symbol)(void *arg, const char *name, | ||
166 | char type, u64 start)); | ||
167 | |||
168 | int symbol__init(void); | ||
169 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | ||
170 | |||
171 | #endif /* __PERF_SYMBOL */ | ||
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 45efb5db0d19..21b92162282b 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -2,48 +2,151 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <stdio.h> | 3 | #include <stdio.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include "session.h" | ||
5 | #include "thread.h" | 6 | #include "thread.h" |
6 | #include "util.h" | 7 | #include "util.h" |
7 | #include "debug.h" | 8 | #include "debug.h" |
8 | 9 | ||
10 | void map_groups__init(struct map_groups *self) | ||
11 | { | ||
12 | int i; | ||
13 | for (i = 0; i < MAP__NR_TYPES; ++i) { | ||
14 | self->maps[i] = RB_ROOT; | ||
15 | INIT_LIST_HEAD(&self->removed_maps[i]); | ||
16 | } | ||
17 | } | ||
18 | |||
9 | static struct thread *thread__new(pid_t pid) | 19 | static struct thread *thread__new(pid_t pid) |
10 | { | 20 | { |
11 | struct thread *self = calloc(1, sizeof(*self)); | 21 | struct thread *self = zalloc(sizeof(*self)); |
12 | 22 | ||
13 | if (self != NULL) { | 23 | if (self != NULL) { |
24 | map_groups__init(&self->mg); | ||
14 | self->pid = pid; | 25 | self->pid = pid; |
15 | self->comm = malloc(32); | 26 | self->comm = malloc(32); |
16 | if (self->comm) | 27 | if (self->comm) |
17 | snprintf(self->comm, 32, ":%d", self->pid); | 28 | snprintf(self->comm, 32, ":%d", self->pid); |
18 | INIT_LIST_HEAD(&self->maps); | ||
19 | } | 29 | } |
20 | 30 | ||
21 | return self; | 31 | return self; |
22 | } | 32 | } |
23 | 33 | ||
34 | static void map_groups__flush(struct map_groups *self) | ||
35 | { | ||
36 | int type; | ||
37 | |||
38 | for (type = 0; type < MAP__NR_TYPES; type++) { | ||
39 | struct rb_root *root = &self->maps[type]; | ||
40 | struct rb_node *next = rb_first(root); | ||
41 | |||
42 | while (next) { | ||
43 | struct map *pos = rb_entry(next, struct map, rb_node); | ||
44 | next = rb_next(&pos->rb_node); | ||
45 | rb_erase(&pos->rb_node, root); | ||
46 | /* | ||
47 | * We may have references to this map, for | ||
48 | * instance in some hist_entry instances, so | ||
49 | * just move them to a separate list. | ||
50 | */ | ||
51 | list_add_tail(&pos->node, &self->removed_maps[pos->type]); | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
24 | int thread__set_comm(struct thread *self, const char *comm) | 56 | int thread__set_comm(struct thread *self, const char *comm) |
25 | { | 57 | { |
58 | int err; | ||
59 | |||
26 | if (self->comm) | 60 | if (self->comm) |
27 | free(self->comm); | 61 | free(self->comm); |
28 | self->comm = strdup(comm); | 62 | self->comm = strdup(comm); |
29 | return self->comm ? 0 : -ENOMEM; | 63 | err = self->comm == NULL ? -ENOMEM : 0; |
64 | if (!err) { | ||
65 | self->comm_set = true; | ||
66 | map_groups__flush(&self->mg); | ||
67 | } | ||
68 | return err; | ||
30 | } | 69 | } |
31 | 70 | ||
32 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 71 | int thread__comm_len(struct thread *self) |
72 | { | ||
73 | if (!self->comm_len) { | ||
74 | if (!self->comm) | ||
75 | return 0; | ||
76 | self->comm_len = strlen(self->comm); | ||
77 | } | ||
78 | |||
79 | return self->comm_len; | ||
80 | } | ||
81 | |||
82 | static size_t __map_groups__fprintf_maps(struct map_groups *self, | ||
83 | enum map_type type, FILE *fp) | ||
84 | { | ||
85 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | ||
86 | struct rb_node *nd; | ||
87 | |||
88 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | ||
89 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
90 | printed += fprintf(fp, "Map:"); | ||
91 | printed += map__fprintf(pos, fp); | ||
92 | if (verbose > 1) { | ||
93 | printed += dso__fprintf(pos->dso, type, fp); | ||
94 | printed += fprintf(fp, "--\n"); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | return printed; | ||
99 | } | ||
100 | |||
101 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp) | ||
102 | { | ||
103 | size_t printed = 0, i; | ||
104 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
105 | printed += __map_groups__fprintf_maps(self, i, fp); | ||
106 | return printed; | ||
107 | } | ||
108 | |||
109 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | ||
110 | enum map_type type, FILE *fp) | ||
33 | { | 111 | { |
34 | struct map *pos; | 112 | struct map *pos; |
35 | size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); | 113 | size_t printed = 0; |
114 | |||
115 | list_for_each_entry(pos, &self->removed_maps[type], node) { | ||
116 | printed += fprintf(fp, "Map:"); | ||
117 | printed += map__fprintf(pos, fp); | ||
118 | if (verbose > 1) { | ||
119 | printed += dso__fprintf(pos->dso, type, fp); | ||
120 | printed += fprintf(fp, "--\n"); | ||
121 | } | ||
122 | } | ||
123 | return printed; | ||
124 | } | ||
125 | |||
126 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, FILE *fp) | ||
127 | { | ||
128 | size_t printed = 0, i; | ||
129 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
130 | printed += __map_groups__fprintf_removed_maps(self, i, fp); | ||
131 | return printed; | ||
132 | } | ||
36 | 133 | ||
37 | list_for_each_entry(pos, &self->maps, node) | 134 | static size_t map_groups__fprintf(struct map_groups *self, FILE *fp) |
38 | ret += map__fprintf(pos, fp); | 135 | { |
136 | size_t printed = map_groups__fprintf_maps(self, fp); | ||
137 | printed += fprintf(fp, "Removed maps:\n"); | ||
138 | return printed + map_groups__fprintf_removed_maps(self, fp); | ||
139 | } | ||
39 | 140 | ||
40 | return ret; | 141 | static size_t thread__fprintf(struct thread *self, FILE *fp) |
142 | { | ||
143 | return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + | ||
144 | map_groups__fprintf(&self->mg, fp); | ||
41 | } | 145 | } |
42 | 146 | ||
43 | struct thread * | 147 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) |
44 | threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | ||
45 | { | 148 | { |
46 | struct rb_node **p = &threads->rb_node; | 149 | struct rb_node **p = &self->threads.rb_node; |
47 | struct rb_node *parent = NULL; | 150 | struct rb_node *parent = NULL; |
48 | struct thread *th; | 151 | struct thread *th; |
49 | 152 | ||
@@ -52,15 +155,15 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | |||
52 | * so most of the time we dont have to look up | 155 | * so most of the time we dont have to look up |
53 | * the full rbtree: | 156 | * the full rbtree: |
54 | */ | 157 | */ |
55 | if (*last_match && (*last_match)->pid == pid) | 158 | if (self->last_match && self->last_match->pid == pid) |
56 | return *last_match; | 159 | return self->last_match; |
57 | 160 | ||
58 | while (*p != NULL) { | 161 | while (*p != NULL) { |
59 | parent = *p; | 162 | parent = *p; |
60 | th = rb_entry(parent, struct thread, rb_node); | 163 | th = rb_entry(parent, struct thread, rb_node); |
61 | 164 | ||
62 | if (th->pid == pid) { | 165 | if (th->pid == pid) { |
63 | *last_match = th; | 166 | self->last_match = th; |
64 | return th; | 167 | return th; |
65 | } | 168 | } |
66 | 169 | ||
@@ -73,99 +176,130 @@ threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match) | |||
73 | th = thread__new(pid); | 176 | th = thread__new(pid); |
74 | if (th != NULL) { | 177 | if (th != NULL) { |
75 | rb_link_node(&th->rb_node, parent, p); | 178 | rb_link_node(&th->rb_node, parent, p); |
76 | rb_insert_color(&th->rb_node, threads); | 179 | rb_insert_color(&th->rb_node, &self->threads); |
77 | *last_match = th; | 180 | self->last_match = th; |
78 | } | 181 | } |
79 | 182 | ||
80 | return th; | 183 | return th; |
81 | } | 184 | } |
82 | 185 | ||
83 | struct thread * | 186 | static void map_groups__remove_overlappings(struct map_groups *self, |
84 | register_idle_thread(struct rb_root *threads, struct thread **last_match) | 187 | struct map *map) |
85 | { | 188 | { |
86 | struct thread *thread = threads__findnew(0, threads, last_match); | 189 | struct rb_root *root = &self->maps[map->type]; |
190 | struct rb_node *next = rb_first(root); | ||
87 | 191 | ||
88 | if (!thread || thread__set_comm(thread, "swapper")) { | 192 | while (next) { |
89 | fprintf(stderr, "problem inserting idle task.\n"); | 193 | struct map *pos = rb_entry(next, struct map, rb_node); |
90 | exit(-1); | 194 | next = rb_next(&pos->rb_node); |
91 | } | 195 | |
196 | if (!map__overlap(pos, map)) | ||
197 | continue; | ||
92 | 198 | ||
93 | return thread; | 199 | if (verbose >= 2) { |
200 | fputs("overlapping maps:\n", stderr); | ||
201 | map__fprintf(map, stderr); | ||
202 | map__fprintf(pos, stderr); | ||
203 | } | ||
204 | |||
205 | rb_erase(&pos->rb_node, root); | ||
206 | /* | ||
207 | * We may have references to this map, for instance in some | ||
208 | * hist_entry instances, so just move them to a separate | ||
209 | * list. | ||
210 | */ | ||
211 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | ||
212 | } | ||
94 | } | 213 | } |
95 | 214 | ||
96 | void thread__insert_map(struct thread *self, struct map *map) | 215 | void maps__insert(struct rb_root *maps, struct map *map) |
97 | { | 216 | { |
98 | struct map *pos, *tmp; | 217 | struct rb_node **p = &maps->rb_node; |
99 | 218 | struct rb_node *parent = NULL; | |
100 | list_for_each_entry_safe(pos, tmp, &self->maps, node) { | 219 | const u64 ip = map->start; |
101 | if (map__overlap(pos, map)) { | 220 | struct map *m; |
102 | if (verbose >= 2) { | ||
103 | printf("overlapping maps:\n"); | ||
104 | map__fprintf(map, stdout); | ||
105 | map__fprintf(pos, stdout); | ||
106 | } | ||
107 | 221 | ||
108 | if (map->start <= pos->start && map->end > pos->start) | 222 | while (*p != NULL) { |
109 | pos->start = map->end; | 223 | parent = *p; |
224 | m = rb_entry(parent, struct map, rb_node); | ||
225 | if (ip < m->start) | ||
226 | p = &(*p)->rb_left; | ||
227 | else | ||
228 | p = &(*p)->rb_right; | ||
229 | } | ||
110 | 230 | ||
111 | if (map->end >= pos->end && map->start < pos->end) | 231 | rb_link_node(&map->rb_node, parent, p); |
112 | pos->end = map->start; | 232 | rb_insert_color(&map->rb_node, maps); |
233 | } | ||
113 | 234 | ||
114 | if (verbose >= 2) { | 235 | struct map *maps__find(struct rb_root *maps, u64 ip) |
115 | printf("after collision:\n"); | 236 | { |
116 | map__fprintf(pos, stdout); | 237 | struct rb_node **p = &maps->rb_node; |
117 | } | 238 | struct rb_node *parent = NULL; |
239 | struct map *m; | ||
118 | 240 | ||
119 | if (pos->start >= pos->end) { | 241 | while (*p != NULL) { |
120 | list_del_init(&pos->node); | 242 | parent = *p; |
121 | free(pos); | 243 | m = rb_entry(parent, struct map, rb_node); |
122 | } | 244 | if (ip < m->start) |
123 | } | 245 | p = &(*p)->rb_left; |
246 | else if (ip > m->end) | ||
247 | p = &(*p)->rb_right; | ||
248 | else | ||
249 | return m; | ||
124 | } | 250 | } |
125 | 251 | ||
126 | list_add_tail(&map->node, &self->maps); | 252 | return NULL; |
127 | } | 253 | } |
128 | 254 | ||
129 | int thread__fork(struct thread *self, struct thread *parent) | 255 | void thread__insert_map(struct thread *self, struct map *map) |
130 | { | 256 | { |
131 | struct map *map; | 257 | map_groups__remove_overlappings(&self->mg, map); |
132 | 258 | map_groups__insert(&self->mg, map); | |
133 | if (self->comm) | 259 | } |
134 | free(self->comm); | ||
135 | self->comm = strdup(parent->comm); | ||
136 | if (!self->comm) | ||
137 | return -ENOMEM; | ||
138 | 260 | ||
139 | list_for_each_entry(map, &parent->maps, node) { | 261 | /* |
262 | * XXX This should not really _copy_ te maps, but refcount them. | ||
263 | */ | ||
264 | static int map_groups__clone(struct map_groups *self, | ||
265 | struct map_groups *parent, enum map_type type) | ||
266 | { | ||
267 | struct rb_node *nd; | ||
268 | for (nd = rb_first(&parent->maps[type]); nd; nd = rb_next(nd)) { | ||
269 | struct map *map = rb_entry(nd, struct map, rb_node); | ||
140 | struct map *new = map__clone(map); | 270 | struct map *new = map__clone(map); |
141 | if (!new) | 271 | if (new == NULL) |
142 | return -ENOMEM; | 272 | return -ENOMEM; |
143 | thread__insert_map(self, new); | 273 | map_groups__insert(self, new); |
144 | } | 274 | } |
145 | |||
146 | return 0; | 275 | return 0; |
147 | } | 276 | } |
148 | 277 | ||
149 | struct map *thread__find_map(struct thread *self, u64 ip) | 278 | int thread__fork(struct thread *self, struct thread *parent) |
150 | { | 279 | { |
151 | struct map *pos; | 280 | int i; |
152 | |||
153 | if (self == NULL) | ||
154 | return NULL; | ||
155 | 281 | ||
156 | list_for_each_entry(pos, &self->maps, node) | 282 | if (parent->comm_set) { |
157 | if (ip >= pos->start && ip <= pos->end) | 283 | if (self->comm) |
158 | return pos; | 284 | free(self->comm); |
285 | self->comm = strdup(parent->comm); | ||
286 | if (!self->comm) | ||
287 | return -ENOMEM; | ||
288 | self->comm_set = true; | ||
289 | } | ||
159 | 290 | ||
160 | return NULL; | 291 | for (i = 0; i < MAP__NR_TYPES; ++i) |
292 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) | ||
293 | return -ENOMEM; | ||
294 | return 0; | ||
161 | } | 295 | } |
162 | 296 | ||
163 | size_t threads__fprintf(FILE *fp, struct rb_root *threads) | 297 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp) |
164 | { | 298 | { |
165 | size_t ret = 0; | 299 | size_t ret = 0; |
166 | struct rb_node *nd; | 300 | struct rb_node *nd; |
167 | 301 | ||
168 | for (nd = rb_first(threads); nd; nd = rb_next(nd)) { | 302 | for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) { |
169 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | 303 | struct thread *pos = rb_entry(nd, struct thread, rb_node); |
170 | 304 | ||
171 | ret += thread__fprintf(pos, fp); | 305 | ret += thread__fprintf(pos, fp); |
@@ -173,3 +307,15 @@ size_t threads__fprintf(FILE *fp, struct rb_root *threads) | |||
173 | 307 | ||
174 | return ret; | 308 | return ret; |
175 | } | 309 | } |
310 | |||
311 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
312 | enum map_type type, u64 addr, | ||
313 | symbol_filter_t filter) | ||
314 | { | ||
315 | struct map *map = map_groups__find(self, type, addr); | ||
316 | |||
317 | if (map != NULL) | ||
318 | return map__find_symbol(map, map->map_ip(map, addr), filter); | ||
319 | |||
320 | return NULL; | ||
321 | } | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 32aea3c1c2ad..0a28f39de545 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -1,22 +1,84 @@ | |||
1 | #ifndef __PERF_THREAD_H | ||
2 | #define __PERF_THREAD_H | ||
3 | |||
1 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
2 | #include <linux/list.h> | ||
3 | #include <unistd.h> | 5 | #include <unistd.h> |
4 | #include "symbol.h" | 6 | #include "symbol.h" |
5 | 7 | ||
8 | struct map_groups { | ||
9 | struct rb_root maps[MAP__NR_TYPES]; | ||
10 | struct list_head removed_maps[MAP__NR_TYPES]; | ||
11 | }; | ||
12 | |||
6 | struct thread { | 13 | struct thread { |
7 | struct rb_node rb_node; | 14 | struct rb_node rb_node; |
8 | struct list_head maps; | 15 | struct map_groups mg; |
9 | pid_t pid; | 16 | pid_t pid; |
10 | char shortname[3]; | 17 | char shortname[3]; |
18 | bool comm_set; | ||
11 | char *comm; | 19 | char *comm; |
20 | int comm_len; | ||
12 | }; | 21 | }; |
13 | 22 | ||
23 | void map_groups__init(struct map_groups *self); | ||
14 | int thread__set_comm(struct thread *self, const char *comm); | 24 | int thread__set_comm(struct thread *self, const char *comm); |
15 | struct thread * | 25 | int thread__comm_len(struct thread *self); |
16 | threads__findnew(pid_t pid, struct rb_root *threads, struct thread **last_match); | 26 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
17 | struct thread * | ||
18 | register_idle_thread(struct rb_root *threads, struct thread **last_match); | ||
19 | void thread__insert_map(struct thread *self, struct map *map); | 27 | void thread__insert_map(struct thread *self, struct map *map); |
20 | int thread__fork(struct thread *self, struct thread *parent); | 28 | int thread__fork(struct thread *self, struct thread *parent); |
21 | struct map *thread__find_map(struct thread *self, u64 ip); | 29 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); |
22 | size_t threads__fprintf(FILE *fp, struct rb_root *threads); | 30 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); |
31 | |||
32 | void maps__insert(struct rb_root *maps, struct map *map); | ||
33 | struct map *maps__find(struct rb_root *maps, u64 addr); | ||
34 | |||
35 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | ||
36 | { | ||
37 | maps__insert(&self->maps[map->type], map); | ||
38 | } | ||
39 | |||
40 | static inline struct map *map_groups__find(struct map_groups *self, | ||
41 | enum map_type type, u64 addr) | ||
42 | { | ||
43 | return maps__find(&self->maps[type], addr); | ||
44 | } | ||
45 | |||
46 | static inline struct map *thread__find_map(struct thread *self, | ||
47 | enum map_type type, u64 addr) | ||
48 | { | ||
49 | return self ? map_groups__find(&self->mg, type, addr) : NULL; | ||
50 | } | ||
51 | |||
52 | void thread__find_addr_map(struct thread *self, | ||
53 | struct perf_session *session, u8 cpumode, | ||
54 | enum map_type type, u64 addr, | ||
55 | struct addr_location *al); | ||
56 | |||
57 | void thread__find_addr_location(struct thread *self, | ||
58 | struct perf_session *session, u8 cpumode, | ||
59 | enum map_type type, u64 addr, | ||
60 | struct addr_location *al, | ||
61 | symbol_filter_t filter); | ||
62 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
63 | enum map_type type, u64 addr, | ||
64 | symbol_filter_t filter); | ||
65 | |||
66 | static inline struct symbol *map_groups__find_function(struct map_groups *self, | ||
67 | u64 addr, | ||
68 | symbol_filter_t filter) | ||
69 | { | ||
70 | return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); | ||
71 | } | ||
72 | |||
73 | struct map *map_groups__find_by_name(struct map_groups *self, | ||
74 | enum map_type type, const char *name); | ||
75 | |||
76 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
77 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
78 | struct dso *kernel); | ||
79 | int map_groups__create_kernel_maps(struct map_groups *self, | ||
80 | struct map *vmlinux_maps[MAP__NR_TYPES]); | ||
81 | |||
82 | struct map *map_groups__new_module(struct map_groups *self, u64 start, | ||
83 | const char *filename); | ||
84 | #endif /* __PERF_THREAD_H */ | ||
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index af4b0573b37f..5ea8973ad331 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -20,6 +20,7 @@ | |||
20 | */ | 20 | */ |
21 | #define _GNU_SOURCE | 21 | #define _GNU_SOURCE |
22 | #include <dirent.h> | 22 | #include <dirent.h> |
23 | #include <mntent.h> | ||
23 | #include <stdio.h> | 24 | #include <stdio.h> |
24 | #include <stdlib.h> | 25 | #include <stdlib.h> |
25 | #include <string.h> | 26 | #include <string.h> |
@@ -33,10 +34,11 @@ | |||
33 | #include <ctype.h> | 34 | #include <ctype.h> |
34 | #include <errno.h> | 35 | #include <errno.h> |
35 | #include <stdbool.h> | 36 | #include <stdbool.h> |
37 | #include <linux/kernel.h> | ||
36 | 38 | ||
37 | #include "../perf.h" | 39 | #include "../perf.h" |
38 | #include "trace-event.h" | 40 | #include "trace-event.h" |
39 | 41 | #include "debugfs.h" | |
40 | 42 | ||
41 | #define VERSION "0.5" | 43 | #define VERSION "0.5" |
42 | 44 | ||
@@ -101,32 +103,12 @@ void *malloc_or_die(unsigned int size) | |||
101 | 103 | ||
102 | static const char *find_debugfs(void) | 104 | static const char *find_debugfs(void) |
103 | { | 105 | { |
104 | static char debugfs[MAX_PATH+1]; | 106 | const char *path = debugfs_mount(NULL); |
105 | static int debugfs_found; | ||
106 | char type[100]; | ||
107 | FILE *fp; | ||
108 | |||
109 | if (debugfs_found) | ||
110 | return debugfs; | ||
111 | |||
112 | if ((fp = fopen("/proc/mounts","r")) == NULL) | ||
113 | die("Can't open /proc/mounts for read"); | ||
114 | |||
115 | while (fscanf(fp, "%*s %" | ||
116 | STR(MAX_PATH) | ||
117 | "s %99s %*s %*d %*d\n", | ||
118 | debugfs, type) == 2) { | ||
119 | if (strcmp(type, "debugfs") == 0) | ||
120 | break; | ||
121 | } | ||
122 | fclose(fp); | ||
123 | |||
124 | if (strcmp(type, "debugfs") != 0) | ||
125 | die("debugfs not mounted, please mount"); | ||
126 | 107 | ||
127 | debugfs_found = 1; | 108 | if (!path) |
109 | die("Your kernel not support debugfs filesystem"); | ||
128 | 110 | ||
129 | return debugfs; | 111 | return path; |
130 | } | 112 | } |
131 | 113 | ||
132 | /* | 114 | /* |
@@ -271,6 +253,8 @@ static void read_header_files(void) | |||
271 | write_or_die("header_page", 12); | 253 | write_or_die("header_page", 12); |
272 | write_or_die(&size, 8); | 254 | write_or_die(&size, 8); |
273 | check_size = copy_file_fd(fd); | 255 | check_size = copy_file_fd(fd); |
256 | close(fd); | ||
257 | |||
274 | if (size != check_size) | 258 | if (size != check_size) |
275 | die("wrong size for '%s' size=%lld read=%lld", | 259 | die("wrong size for '%s' size=%lld read=%lld", |
276 | path, size, check_size); | 260 | path, size, check_size); |
@@ -289,6 +273,7 @@ static void read_header_files(void) | |||
289 | if (size != check_size) | 273 | if (size != check_size) |
290 | die("wrong size for '%s'", path); | 274 | die("wrong size for '%s'", path); |
291 | put_tracing_file(path); | 275 | put_tracing_file(path); |
276 | close(fd); | ||
292 | } | 277 | } |
293 | 278 | ||
294 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 279 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -317,7 +302,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
317 | die("can't read directory '%s'", sys); | 302 | die("can't read directory '%s'", sys); |
318 | 303 | ||
319 | while ((dent = readdir(dir))) { | 304 | while ((dent = readdir(dir))) { |
320 | if (strcmp(dent->d_name, ".") == 0 || | 305 | if (dent->d_type != DT_DIR || |
306 | strcmp(dent->d_name, ".") == 0 || | ||
321 | strcmp(dent->d_name, "..") == 0 || | 307 | strcmp(dent->d_name, "..") == 0 || |
322 | !name_in_tp_list(dent->d_name, tps)) | 308 | !name_in_tp_list(dent->d_name, tps)) |
323 | continue; | 309 | continue; |
@@ -334,7 +320,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
334 | 320 | ||
335 | rewinddir(dir); | 321 | rewinddir(dir); |
336 | while ((dent = readdir(dir))) { | 322 | while ((dent = readdir(dir))) { |
337 | if (strcmp(dent->d_name, ".") == 0 || | 323 | if (dent->d_type != DT_DIR || |
324 | strcmp(dent->d_name, ".") == 0 || | ||
338 | strcmp(dent->d_name, "..") == 0 || | 325 | strcmp(dent->d_name, "..") == 0 || |
339 | !name_in_tp_list(dent->d_name, tps)) | 326 | !name_in_tp_list(dent->d_name, tps)) |
340 | continue; | 327 | continue; |
@@ -353,6 +340,7 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
353 | 340 | ||
354 | free(format); | 341 | free(format); |
355 | } | 342 | } |
343 | closedir(dir); | ||
356 | } | 344 | } |
357 | 345 | ||
358 | static void read_ftrace_files(struct tracepoint_path *tps) | 346 | static void read_ftrace_files(struct tracepoint_path *tps) |
@@ -394,26 +382,21 @@ static void read_event_files(struct tracepoint_path *tps) | |||
394 | die("can't read directory '%s'", path); | 382 | die("can't read directory '%s'", path); |
395 | 383 | ||
396 | while ((dent = readdir(dir))) { | 384 | while ((dent = readdir(dir))) { |
397 | if (strcmp(dent->d_name, ".") == 0 || | 385 | if (dent->d_type != DT_DIR || |
386 | strcmp(dent->d_name, ".") == 0 || | ||
398 | strcmp(dent->d_name, "..") == 0 || | 387 | strcmp(dent->d_name, "..") == 0 || |
399 | strcmp(dent->d_name, "ftrace") == 0 || | 388 | strcmp(dent->d_name, "ftrace") == 0 || |
400 | !system_in_tp_list(dent->d_name, tps)) | 389 | !system_in_tp_list(dent->d_name, tps)) |
401 | continue; | 390 | continue; |
402 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | 391 | count++; |
403 | sprintf(sys, "%s/%s", path, dent->d_name); | ||
404 | ret = stat(sys, &st); | ||
405 | free(sys); | ||
406 | if (ret < 0) | ||
407 | continue; | ||
408 | if (S_ISDIR(st.st_mode)) | ||
409 | count++; | ||
410 | } | 392 | } |
411 | 393 | ||
412 | write_or_die(&count, 4); | 394 | write_or_die(&count, 4); |
413 | 395 | ||
414 | rewinddir(dir); | 396 | rewinddir(dir); |
415 | while ((dent = readdir(dir))) { | 397 | while ((dent = readdir(dir))) { |
416 | if (strcmp(dent->d_name, ".") == 0 || | 398 | if (dent->d_type != DT_DIR || |
399 | strcmp(dent->d_name, ".") == 0 || | ||
417 | strcmp(dent->d_name, "..") == 0 || | 400 | strcmp(dent->d_name, "..") == 0 || |
418 | strcmp(dent->d_name, "ftrace") == 0 || | 401 | strcmp(dent->d_name, "ftrace") == 0 || |
419 | !system_in_tp_list(dent->d_name, tps)) | 402 | !system_in_tp_list(dent->d_name, tps)) |
@@ -422,14 +405,13 @@ static void read_event_files(struct tracepoint_path *tps) | |||
422 | sprintf(sys, "%s/%s", path, dent->d_name); | 405 | sprintf(sys, "%s/%s", path, dent->d_name); |
423 | ret = stat(sys, &st); | 406 | ret = stat(sys, &st); |
424 | if (ret >= 0) { | 407 | if (ret >= 0) { |
425 | if (S_ISDIR(st.st_mode)) { | 408 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); |
426 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); | 409 | copy_event_system(sys, tps); |
427 | copy_event_system(sys, tps); | ||
428 | } | ||
429 | } | 410 | } |
430 | free(sys); | 411 | free(sys); |
431 | } | 412 | } |
432 | 413 | ||
414 | closedir(dir); | ||
433 | put_tracing_file(path); | 415 | put_tracing_file(path); |
434 | } | 416 | } |
435 | 417 | ||
@@ -483,27 +465,33 @@ static struct tracepoint_path * | |||
483 | get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) | 465 | get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) |
484 | { | 466 | { |
485 | struct tracepoint_path path, *ppath = &path; | 467 | struct tracepoint_path path, *ppath = &path; |
486 | int i; | 468 | int i, nr_tracepoints = 0; |
487 | 469 | ||
488 | for (i = 0; i < nb_events; i++) { | 470 | for (i = 0; i < nb_events; i++) { |
489 | if (pattrs[i].type != PERF_TYPE_TRACEPOINT) | 471 | if (pattrs[i].type != PERF_TYPE_TRACEPOINT) |
490 | continue; | 472 | continue; |
473 | ++nr_tracepoints; | ||
491 | ppath->next = tracepoint_id_to_path(pattrs[i].config); | 474 | ppath->next = tracepoint_id_to_path(pattrs[i].config); |
492 | if (!ppath->next) | 475 | if (!ppath->next) |
493 | die("%s\n", "No memory to alloc tracepoints list"); | 476 | die("%s\n", "No memory to alloc tracepoints list"); |
494 | ppath = ppath->next; | 477 | ppath = ppath->next; |
495 | } | 478 | } |
496 | 479 | ||
497 | return path.next; | 480 | return nr_tracepoints > 0 ? path.next : NULL; |
498 | } | 481 | } |
499 | void read_tracing_data(struct perf_event_attr *pattrs, int nb_events) | 482 | |
483 | int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) | ||
500 | { | 484 | { |
501 | char buf[BUFSIZ]; | 485 | char buf[BUFSIZ]; |
502 | struct tracepoint_path *tps; | 486 | struct tracepoint_path *tps = get_tracepoints_path(pattrs, nb_events); |
487 | |||
488 | /* | ||
489 | * What? No tracepoints? No sense writing anything here, bail out. | ||
490 | */ | ||
491 | if (tps == NULL) | ||
492 | return -1; | ||
503 | 493 | ||
504 | output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); | 494 | output_fd = fd; |
505 | if (output_fd < 0) | ||
506 | die("creating file '%s'", output_file); | ||
507 | 495 | ||
508 | buf[0] = 23; | 496 | buf[0] = 23; |
509 | buf[1] = 8; | 497 | buf[1] = 8; |
@@ -527,14 +515,14 @@ void read_tracing_data(struct perf_event_attr *pattrs, int nb_events) | |||
527 | write_or_die(buf, 1); | 515 | write_or_die(buf, 1); |
528 | 516 | ||
529 | /* save page_size */ | 517 | /* save page_size */ |
530 | page_size = getpagesize(); | 518 | page_size = sysconf(_SC_PAGESIZE); |
531 | write_or_die(&page_size, 4); | 519 | write_or_die(&page_size, 4); |
532 | 520 | ||
533 | tps = get_tracepoints_path(pattrs, nb_events); | ||
534 | |||
535 | read_header_files(); | 521 | read_header_files(); |
536 | read_ftrace_files(tps); | 522 | read_ftrace_files(tps); |
537 | read_event_files(tps); | 523 | read_event_files(tps); |
538 | read_proc_kallsyms(); | 524 | read_proc_kallsyms(); |
539 | read_ftrace_printk(); | 525 | read_ftrace_printk(); |
526 | |||
527 | return 0; | ||
540 | } | 528 | } |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 55c9659a56e2..9b3c20f42f98 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -40,12 +40,19 @@ int header_page_size_size; | |||
40 | int header_page_data_offset; | 40 | int header_page_data_offset; |
41 | int header_page_data_size; | 41 | int header_page_data_size; |
42 | 42 | ||
43 | int latency_format; | ||
44 | |||
43 | static char *input_buf; | 45 | static char *input_buf; |
44 | static unsigned long long input_buf_ptr; | 46 | static unsigned long long input_buf_ptr; |
45 | static unsigned long long input_buf_siz; | 47 | static unsigned long long input_buf_siz; |
46 | 48 | ||
47 | static int cpus; | 49 | static int cpus; |
48 | static int long_size; | 50 | static int long_size; |
51 | static int is_flag_field; | ||
52 | static int is_symbolic_field; | ||
53 | |||
54 | static struct format_field * | ||
55 | find_any_field(struct event *event, const char *name); | ||
49 | 56 | ||
50 | static void init_input_buf(char *buf, unsigned long long size) | 57 | static void init_input_buf(char *buf, unsigned long long size) |
51 | { | 58 | { |
@@ -170,7 +177,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) | |||
170 | func_count++; | 177 | func_count++; |
171 | } | 178 | } |
172 | 179 | ||
173 | func_list = malloc_or_die(sizeof(*func_list) * func_count + 1); | 180 | func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1)); |
174 | 181 | ||
175 | i = 0; | 182 | i = 0; |
176 | while (list) { | 183 | while (list) { |
@@ -284,18 +291,19 @@ void parse_ftrace_printk(char *file, unsigned int size __unused) | |||
284 | char *line; | 291 | char *line; |
285 | char *next = NULL; | 292 | char *next = NULL; |
286 | char *addr_str; | 293 | char *addr_str; |
287 | int ret; | ||
288 | int i; | 294 | int i; |
289 | 295 | ||
290 | line = strtok_r(file, "\n", &next); | 296 | line = strtok_r(file, "\n", &next); |
291 | while (line) { | 297 | while (line) { |
298 | addr_str = strsep(&line, ":"); | ||
299 | if (!line) { | ||
300 | warning("error parsing print strings"); | ||
301 | break; | ||
302 | } | ||
292 | item = malloc_or_die(sizeof(*item)); | 303 | item = malloc_or_die(sizeof(*item)); |
293 | ret = sscanf(line, "%as : %as", | ||
294 | (float *)(void *)&addr_str, /* workaround gcc warning */ | ||
295 | (float *)(void *)&item->printk); | ||
296 | item->addr = strtoull(addr_str, NULL, 16); | 304 | item->addr = strtoull(addr_str, NULL, 16); |
297 | free(addr_str); | 305 | /* fmt still has a space, skip it */ |
298 | 306 | item->printk = strdup(line+1); | |
299 | item->next = list; | 307 | item->next = list; |
300 | list = item; | 308 | list = item; |
301 | line = strtok_r(NULL, "\n", &next); | 309 | line = strtok_r(NULL, "\n", &next); |
@@ -522,7 +530,10 @@ static enum event_type __read_token(char **tok) | |||
522 | last_ch = ch; | 530 | last_ch = ch; |
523 | ch = __read_char(); | 531 | ch = __read_char(); |
524 | buf[i++] = ch; | 532 | buf[i++] = ch; |
525 | } while (ch != quote_ch && last_ch != '\\'); | 533 | /* the '\' '\' will cancel itself */ |
534 | if (ch == '\\' && last_ch == '\\') | ||
535 | last_ch = 0; | ||
536 | } while (ch != quote_ch || last_ch == '\\'); | ||
526 | /* remove the last quote */ | 537 | /* remove the last quote */ |
527 | i--; | 538 | i--; |
528 | goto out; | 539 | goto out; |
@@ -610,7 +621,7 @@ static enum event_type read_token_item(char **tok) | |||
610 | static int test_type(enum event_type type, enum event_type expect) | 621 | static int test_type(enum event_type type, enum event_type expect) |
611 | { | 622 | { |
612 | if (type != expect) { | 623 | if (type != expect) { |
613 | die("Error: expected type %d but read %d", | 624 | warning("Error: expected type %d but read %d", |
614 | expect, type); | 625 | expect, type); |
615 | return -1; | 626 | return -1; |
616 | } | 627 | } |
@@ -621,13 +632,13 @@ static int test_type_token(enum event_type type, char *token, | |||
621 | enum event_type expect, const char *expect_tok) | 632 | enum event_type expect, const char *expect_tok) |
622 | { | 633 | { |
623 | if (type != expect) { | 634 | if (type != expect) { |
624 | die("Error: expected type %d but read %d", | 635 | warning("Error: expected type %d but read %d", |
625 | expect, type); | 636 | expect, type); |
626 | return -1; | 637 | return -1; |
627 | } | 638 | } |
628 | 639 | ||
629 | if (strcmp(token, expect_tok) != 0) { | 640 | if (strcmp(token, expect_tok) != 0) { |
630 | die("Error: expected '%s' but read '%s'", | 641 | warning("Error: expected '%s' but read '%s'", |
631 | expect_tok, token); | 642 | expect_tok, token); |
632 | return -1; | 643 | return -1; |
633 | } | 644 | } |
@@ -665,7 +676,7 @@ static int __read_expected(enum event_type expect, const char *str, int newline_ | |||
665 | 676 | ||
666 | free_token(token); | 677 | free_token(token); |
667 | 678 | ||
668 | return 0; | 679 | return ret; |
669 | } | 680 | } |
670 | 681 | ||
671 | static int read_expected(enum event_type expect, const char *str) | 682 | static int read_expected(enum event_type expect, const char *str) |
@@ -682,10 +693,10 @@ static char *event_read_name(void) | |||
682 | { | 693 | { |
683 | char *token; | 694 | char *token; |
684 | 695 | ||
685 | if (read_expected(EVENT_ITEM, (char *)"name") < 0) | 696 | if (read_expected(EVENT_ITEM, "name") < 0) |
686 | return NULL; | 697 | return NULL; |
687 | 698 | ||
688 | if (read_expected(EVENT_OP, (char *)":") < 0) | 699 | if (read_expected(EVENT_OP, ":") < 0) |
689 | return NULL; | 700 | return NULL; |
690 | 701 | ||
691 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 702 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -703,10 +714,10 @@ static int event_read_id(void) | |||
703 | char *token; | 714 | char *token; |
704 | int id; | 715 | int id; |
705 | 716 | ||
706 | if (read_expected_item(EVENT_ITEM, (char *)"ID") < 0) | 717 | if (read_expected_item(EVENT_ITEM, "ID") < 0) |
707 | return -1; | 718 | return -1; |
708 | 719 | ||
709 | if (read_expected(EVENT_OP, (char *)":") < 0) | 720 | if (read_expected(EVENT_OP, ":") < 0) |
710 | return -1; | 721 | return -1; |
711 | 722 | ||
712 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 723 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -721,6 +732,24 @@ static int event_read_id(void) | |||
721 | return -1; | 732 | return -1; |
722 | } | 733 | } |
723 | 734 | ||
735 | static int field_is_string(struct format_field *field) | ||
736 | { | ||
737 | if ((field->flags & FIELD_IS_ARRAY) && | ||
738 | (!strstr(field->type, "char") || !strstr(field->type, "u8") || | ||
739 | !strstr(field->type, "s8"))) | ||
740 | return 1; | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | static int field_is_dynamic(struct format_field *field) | ||
746 | { | ||
747 | if (!strcmp(field->type, "__data_loc")) | ||
748 | return 1; | ||
749 | |||
750 | return 0; | ||
751 | } | ||
752 | |||
724 | static int event_read_fields(struct event *event, struct format_field **fields) | 753 | static int event_read_fields(struct event *event, struct format_field **fields) |
725 | { | 754 | { |
726 | struct format_field *field = NULL; | 755 | struct format_field *field = NULL; |
@@ -738,7 +767,7 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
738 | 767 | ||
739 | count++; | 768 | count++; |
740 | 769 | ||
741 | if (test_type_token(type, token, EVENT_ITEM, (char *)"field")) | 770 | if (test_type_token(type, token, EVENT_ITEM, "field")) |
742 | goto fail; | 771 | goto fail; |
743 | free_token(token); | 772 | free_token(token); |
744 | 773 | ||
@@ -753,7 +782,7 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
753 | type = read_token(&token); | 782 | type = read_token(&token); |
754 | } | 783 | } |
755 | 784 | ||
756 | if (test_type_token(type, token, EVENT_OP, (char *)":") < 0) | 785 | if (test_type_token(type, token, EVENT_OP, ":") < 0) |
757 | return -1; | 786 | return -1; |
758 | 787 | ||
759 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 788 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -865,14 +894,20 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
865 | free(brackets); | 894 | free(brackets); |
866 | } | 895 | } |
867 | 896 | ||
868 | if (test_type_token(type, token, EVENT_OP, (char *)";")) | 897 | if (field_is_string(field)) { |
898 | field->flags |= FIELD_IS_STRING; | ||
899 | if (field_is_dynamic(field)) | ||
900 | field->flags |= FIELD_IS_DYNAMIC; | ||
901 | } | ||
902 | |||
903 | if (test_type_token(type, token, EVENT_OP, ";")) | ||
869 | goto fail; | 904 | goto fail; |
870 | free_token(token); | 905 | free_token(token); |
871 | 906 | ||
872 | if (read_expected(EVENT_ITEM, (char *)"offset") < 0) | 907 | if (read_expected(EVENT_ITEM, "offset") < 0) |
873 | goto fail_expect; | 908 | goto fail_expect; |
874 | 909 | ||
875 | if (read_expected(EVENT_OP, (char *)":") < 0) | 910 | if (read_expected(EVENT_OP, ":") < 0) |
876 | goto fail_expect; | 911 | goto fail_expect; |
877 | 912 | ||
878 | if (read_expect_type(EVENT_ITEM, &token)) | 913 | if (read_expect_type(EVENT_ITEM, &token)) |
@@ -880,13 +915,13 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
880 | field->offset = strtoul(token, NULL, 0); | 915 | field->offset = strtoul(token, NULL, 0); |
881 | free_token(token); | 916 | free_token(token); |
882 | 917 | ||
883 | if (read_expected(EVENT_OP, (char *)";") < 0) | 918 | if (read_expected(EVENT_OP, ";") < 0) |
884 | goto fail_expect; | 919 | goto fail_expect; |
885 | 920 | ||
886 | if (read_expected(EVENT_ITEM, (char *)"size") < 0) | 921 | if (read_expected(EVENT_ITEM, "size") < 0) |
887 | goto fail_expect; | 922 | goto fail_expect; |
888 | 923 | ||
889 | if (read_expected(EVENT_OP, (char *)":") < 0) | 924 | if (read_expected(EVENT_OP, ":") < 0) |
890 | goto fail_expect; | 925 | goto fail_expect; |
891 | 926 | ||
892 | if (read_expect_type(EVENT_ITEM, &token)) | 927 | if (read_expect_type(EVENT_ITEM, &token)) |
@@ -894,11 +929,34 @@ static int event_read_fields(struct event *event, struct format_field **fields) | |||
894 | field->size = strtoul(token, NULL, 0); | 929 | field->size = strtoul(token, NULL, 0); |
895 | free_token(token); | 930 | free_token(token); |
896 | 931 | ||
897 | if (read_expected(EVENT_OP, (char *)";") < 0) | 932 | if (read_expected(EVENT_OP, ";") < 0) |
898 | goto fail_expect; | 933 | goto fail_expect; |
899 | 934 | ||
900 | if (read_expect_type(EVENT_NEWLINE, &token) < 0) | 935 | type = read_token(&token); |
901 | goto fail; | 936 | if (type != EVENT_NEWLINE) { |
937 | /* newer versions of the kernel have a "signed" type */ | ||
938 | if (test_type_token(type, token, EVENT_ITEM, "signed")) | ||
939 | goto fail; | ||
940 | |||
941 | free_token(token); | ||
942 | |||
943 | if (read_expected(EVENT_OP, ":") < 0) | ||
944 | goto fail_expect; | ||
945 | |||
946 | if (read_expect_type(EVENT_ITEM, &token)) | ||
947 | goto fail; | ||
948 | |||
949 | if (strtoul(token, NULL, 0)) | ||
950 | field->flags |= FIELD_IS_SIGNED; | ||
951 | |||
952 | free_token(token); | ||
953 | if (read_expected(EVENT_OP, ";") < 0) | ||
954 | goto fail_expect; | ||
955 | |||
956 | if (read_expect_type(EVENT_NEWLINE, &token)) | ||
957 | goto fail; | ||
958 | } | ||
959 | |||
902 | free_token(token); | 960 | free_token(token); |
903 | 961 | ||
904 | *fields = field; | 962 | *fields = field; |
@@ -921,10 +979,10 @@ static int event_read_format(struct event *event) | |||
921 | char *token; | 979 | char *token; |
922 | int ret; | 980 | int ret; |
923 | 981 | ||
924 | if (read_expected_item(EVENT_ITEM, (char *)"format") < 0) | 982 | if (read_expected_item(EVENT_ITEM, "format") < 0) |
925 | return -1; | 983 | return -1; |
926 | 984 | ||
927 | if (read_expected(EVENT_OP, (char *)":") < 0) | 985 | if (read_expected(EVENT_OP, ":") < 0) |
928 | return -1; | 986 | return -1; |
929 | 987 | ||
930 | if (read_expect_type(EVENT_NEWLINE, &token)) | 988 | if (read_expect_type(EVENT_NEWLINE, &token)) |
@@ -984,7 +1042,7 @@ process_cond(struct event *event, struct print_arg *top, char **tok) | |||
984 | 1042 | ||
985 | *tok = NULL; | 1043 | *tok = NULL; |
986 | type = process_arg(event, left, &token); | 1044 | type = process_arg(event, left, &token); |
987 | if (test_type_token(type, token, EVENT_OP, (char *)":")) | 1045 | if (test_type_token(type, token, EVENT_OP, ":")) |
988 | goto out_free; | 1046 | goto out_free; |
989 | 1047 | ||
990 | arg->op.op = token; | 1048 | arg->op.op = token; |
@@ -1004,6 +1062,35 @@ out_free: | |||
1004 | return EVENT_ERROR; | 1062 | return EVENT_ERROR; |
1005 | } | 1063 | } |
1006 | 1064 | ||
1065 | static enum event_type | ||
1066 | process_array(struct event *event, struct print_arg *top, char **tok) | ||
1067 | { | ||
1068 | struct print_arg *arg; | ||
1069 | enum event_type type; | ||
1070 | char *token = NULL; | ||
1071 | |||
1072 | arg = malloc_or_die(sizeof(*arg)); | ||
1073 | memset(arg, 0, sizeof(*arg)); | ||
1074 | |||
1075 | *tok = NULL; | ||
1076 | type = process_arg(event, arg, &token); | ||
1077 | if (test_type_token(type, token, EVENT_OP, "]")) | ||
1078 | goto out_free; | ||
1079 | |||
1080 | top->op.right = arg; | ||
1081 | |||
1082 | free_token(token); | ||
1083 | type = read_token_item(&token); | ||
1084 | *tok = token; | ||
1085 | |||
1086 | return type; | ||
1087 | |||
1088 | out_free: | ||
1089 | free_token(*tok); | ||
1090 | free_arg(arg); | ||
1091 | return EVENT_ERROR; | ||
1092 | } | ||
1093 | |||
1007 | static int get_op_prio(char *op) | 1094 | static int get_op_prio(char *op) |
1008 | { | 1095 | { |
1009 | if (!op[1]) { | 1096 | if (!op[1]) { |
@@ -1128,6 +1215,8 @@ process_op(struct event *event, struct print_arg *arg, char **tok) | |||
1128 | strcmp(token, "*") == 0 || | 1215 | strcmp(token, "*") == 0 || |
1129 | strcmp(token, "^") == 0 || | 1216 | strcmp(token, "^") == 0 || |
1130 | strcmp(token, "/") == 0 || | 1217 | strcmp(token, "/") == 0 || |
1218 | strcmp(token, "<") == 0 || | ||
1219 | strcmp(token, ">") == 0 || | ||
1131 | strcmp(token, "==") == 0 || | 1220 | strcmp(token, "==") == 0 || |
1132 | strcmp(token, "!=") == 0) { | 1221 | strcmp(token, "!=") == 0) { |
1133 | 1222 | ||
@@ -1144,17 +1233,46 @@ process_op(struct event *event, struct print_arg *arg, char **tok) | |||
1144 | 1233 | ||
1145 | right = malloc_or_die(sizeof(*right)); | 1234 | right = malloc_or_die(sizeof(*right)); |
1146 | 1235 | ||
1147 | type = process_arg(event, right, tok); | 1236 | type = read_token_item(&token); |
1237 | *tok = token; | ||
1238 | |||
1239 | /* could just be a type pointer */ | ||
1240 | if ((strcmp(arg->op.op, "*") == 0) && | ||
1241 | type == EVENT_DELIM && (strcmp(token, ")") == 0)) { | ||
1242 | if (left->type != PRINT_ATOM) | ||
1243 | die("bad pointer type"); | ||
1244 | left->atom.atom = realloc(left->atom.atom, | ||
1245 | sizeof(left->atom.atom) + 3); | ||
1246 | strcat(left->atom.atom, " *"); | ||
1247 | *arg = *left; | ||
1248 | free(arg); | ||
1249 | |||
1250 | return type; | ||
1251 | } | ||
1252 | |||
1253 | type = process_arg_token(event, right, tok, type); | ||
1148 | 1254 | ||
1149 | arg->op.right = right; | 1255 | arg->op.right = right; |
1150 | 1256 | ||
1257 | } else if (strcmp(token, "[") == 0) { | ||
1258 | |||
1259 | left = malloc_or_die(sizeof(*left)); | ||
1260 | *left = *arg; | ||
1261 | |||
1262 | arg->type = PRINT_OP; | ||
1263 | arg->op.op = token; | ||
1264 | arg->op.left = left; | ||
1265 | |||
1266 | arg->op.prio = 0; | ||
1267 | type = process_array(event, arg, tok); | ||
1268 | |||
1151 | } else { | 1269 | } else { |
1152 | die("unknown op '%s'", token); | 1270 | warning("unknown op '%s'", token); |
1271 | event->flags |= EVENT_FL_FAILED; | ||
1153 | /* the arg is now the left side */ | 1272 | /* the arg is now the left side */ |
1154 | return EVENT_NONE; | 1273 | return EVENT_NONE; |
1155 | } | 1274 | } |
1156 | 1275 | ||
1157 | |||
1158 | if (type == EVENT_OP) { | 1276 | if (type == EVENT_OP) { |
1159 | int prio; | 1277 | int prio; |
1160 | 1278 | ||
@@ -1178,7 +1296,7 @@ process_entry(struct event *event __unused, struct print_arg *arg, | |||
1178 | char *field; | 1296 | char *field; |
1179 | char *token; | 1297 | char *token; |
1180 | 1298 | ||
1181 | if (read_expected(EVENT_OP, (char *)"->") < 0) | 1299 | if (read_expected(EVENT_OP, "->") < 0) |
1182 | return EVENT_ERROR; | 1300 | return EVENT_ERROR; |
1183 | 1301 | ||
1184 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 1302 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -1188,6 +1306,16 @@ process_entry(struct event *event __unused, struct print_arg *arg, | |||
1188 | arg->type = PRINT_FIELD; | 1306 | arg->type = PRINT_FIELD; |
1189 | arg->field.name = field; | 1307 | arg->field.name = field; |
1190 | 1308 | ||
1309 | if (is_flag_field) { | ||
1310 | arg->field.field = find_any_field(event, arg->field.name); | ||
1311 | arg->field.field->flags |= FIELD_IS_FLAG; | ||
1312 | is_flag_field = 0; | ||
1313 | } else if (is_symbolic_field) { | ||
1314 | arg->field.field = find_any_field(event, arg->field.name); | ||
1315 | arg->field.field->flags |= FIELD_IS_SYMBOLIC; | ||
1316 | is_symbolic_field = 0; | ||
1317 | } | ||
1318 | |||
1191 | type = read_token(&token); | 1319 | type = read_token(&token); |
1192 | *tok = token; | 1320 | *tok = token; |
1193 | 1321 | ||
@@ -1338,25 +1466,25 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok) | |||
1338 | do { | 1466 | do { |
1339 | free_token(token); | 1467 | free_token(token); |
1340 | type = read_token_item(&token); | 1468 | type = read_token_item(&token); |
1341 | if (test_type_token(type, token, EVENT_OP, (char *)"{")) | 1469 | if (test_type_token(type, token, EVENT_OP, "{")) |
1342 | break; | 1470 | break; |
1343 | 1471 | ||
1344 | arg = malloc_or_die(sizeof(*arg)); | 1472 | arg = malloc_or_die(sizeof(*arg)); |
1345 | 1473 | ||
1346 | free_token(token); | 1474 | free_token(token); |
1347 | type = process_arg(event, arg, &token); | 1475 | type = process_arg(event, arg, &token); |
1348 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1476 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1349 | goto out_free; | 1477 | goto out_free; |
1350 | 1478 | ||
1351 | field = malloc_or_die(sizeof(*field)); | 1479 | field = malloc_or_die(sizeof(*field)); |
1352 | memset(field, 0, sizeof(field)); | 1480 | memset(field, 0, sizeof(*field)); |
1353 | 1481 | ||
1354 | value = arg_eval(arg); | 1482 | value = arg_eval(arg); |
1355 | field->value = strdup(value); | 1483 | field->value = strdup(value); |
1356 | 1484 | ||
1357 | free_token(token); | 1485 | free_token(token); |
1358 | type = process_arg(event, arg, &token); | 1486 | type = process_arg(event, arg, &token); |
1359 | if (test_type_token(type, token, EVENT_OP, (char *)"}")) | 1487 | if (test_type_token(type, token, EVENT_OP, "}")) |
1360 | goto out_free; | 1488 | goto out_free; |
1361 | 1489 | ||
1362 | value = arg_eval(arg); | 1490 | value = arg_eval(arg); |
@@ -1391,13 +1519,13 @@ process_flags(struct event *event, struct print_arg *arg, char **tok) | |||
1391 | memset(arg, 0, sizeof(*arg)); | 1519 | memset(arg, 0, sizeof(*arg)); |
1392 | arg->type = PRINT_FLAGS; | 1520 | arg->type = PRINT_FLAGS; |
1393 | 1521 | ||
1394 | if (read_expected_item(EVENT_DELIM, (char *)"(") < 0) | 1522 | if (read_expected_item(EVENT_DELIM, "(") < 0) |
1395 | return EVENT_ERROR; | 1523 | return EVENT_ERROR; |
1396 | 1524 | ||
1397 | field = malloc_or_die(sizeof(*field)); | 1525 | field = malloc_or_die(sizeof(*field)); |
1398 | 1526 | ||
1399 | type = process_arg(event, field, &token); | 1527 | type = process_arg(event, field, &token); |
1400 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1528 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1401 | goto out_free; | 1529 | goto out_free; |
1402 | 1530 | ||
1403 | arg->flags.field = field; | 1531 | arg->flags.field = field; |
@@ -1408,11 +1536,11 @@ process_flags(struct event *event, struct print_arg *arg, char **tok) | |||
1408 | type = read_token_item(&token); | 1536 | type = read_token_item(&token); |
1409 | } | 1537 | } |
1410 | 1538 | ||
1411 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1539 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1412 | goto out_free; | 1540 | goto out_free; |
1413 | 1541 | ||
1414 | type = process_fields(event, &arg->flags.flags, &token); | 1542 | type = process_fields(event, &arg->flags.flags, &token); |
1415 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) | 1543 | if (test_type_token(type, token, EVENT_DELIM, ")")) |
1416 | goto out_free; | 1544 | goto out_free; |
1417 | 1545 | ||
1418 | free_token(token); | 1546 | free_token(token); |
@@ -1434,19 +1562,19 @@ process_symbols(struct event *event, struct print_arg *arg, char **tok) | |||
1434 | memset(arg, 0, sizeof(*arg)); | 1562 | memset(arg, 0, sizeof(*arg)); |
1435 | arg->type = PRINT_SYMBOL; | 1563 | arg->type = PRINT_SYMBOL; |
1436 | 1564 | ||
1437 | if (read_expected_item(EVENT_DELIM, (char *)"(") < 0) | 1565 | if (read_expected_item(EVENT_DELIM, "(") < 0) |
1438 | return EVENT_ERROR; | 1566 | return EVENT_ERROR; |
1439 | 1567 | ||
1440 | field = malloc_or_die(sizeof(*field)); | 1568 | field = malloc_or_die(sizeof(*field)); |
1441 | 1569 | ||
1442 | type = process_arg(event, field, &token); | 1570 | type = process_arg(event, field, &token); |
1443 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1571 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1444 | goto out_free; | 1572 | goto out_free; |
1445 | 1573 | ||
1446 | arg->symbol.field = field; | 1574 | arg->symbol.field = field; |
1447 | 1575 | ||
1448 | type = process_fields(event, &arg->symbol.symbols, &token); | 1576 | type = process_fields(event, &arg->symbol.symbols, &token); |
1449 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) | 1577 | if (test_type_token(type, token, EVENT_DELIM, ")")) |
1450 | goto out_free; | 1578 | goto out_free; |
1451 | 1579 | ||
1452 | free_token(token); | 1580 | free_token(token); |
@@ -1463,7 +1591,6 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
1463 | { | 1591 | { |
1464 | struct print_arg *item_arg; | 1592 | struct print_arg *item_arg; |
1465 | enum event_type type; | 1593 | enum event_type type; |
1466 | int ptr_cast = 0; | ||
1467 | char *token; | 1594 | char *token; |
1468 | 1595 | ||
1469 | type = process_arg(event, arg, &token); | 1596 | type = process_arg(event, arg, &token); |
@@ -1471,28 +1598,13 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
1471 | if (type == EVENT_ERROR) | 1598 | if (type == EVENT_ERROR) |
1472 | return EVENT_ERROR; | 1599 | return EVENT_ERROR; |
1473 | 1600 | ||
1474 | if (type == EVENT_OP) { | 1601 | if (type == EVENT_OP) |
1475 | /* handle the ptr casts */ | 1602 | type = process_op(event, arg, &token); |
1476 | if (!strcmp(token, "*")) { | ||
1477 | /* | ||
1478 | * FIXME: should we zapp whitespaces before ')' ? | ||
1479 | * (may require a peek_token_item()) | ||
1480 | */ | ||
1481 | if (__peek_char() == ')') { | ||
1482 | ptr_cast = 1; | ||
1483 | free_token(token); | ||
1484 | type = read_token_item(&token); | ||
1485 | } | ||
1486 | } | ||
1487 | if (!ptr_cast) { | ||
1488 | type = process_op(event, arg, &token); | ||
1489 | 1603 | ||
1490 | if (type == EVENT_ERROR) | 1604 | if (type == EVENT_ERROR) |
1491 | return EVENT_ERROR; | 1605 | return EVENT_ERROR; |
1492 | } | ||
1493 | } | ||
1494 | 1606 | ||
1495 | if (test_type_token(type, token, EVENT_DELIM, (char *)")")) { | 1607 | if (test_type_token(type, token, EVENT_DELIM, ")")) { |
1496 | free_token(token); | 1608 | free_token(token); |
1497 | return EVENT_ERROR; | 1609 | return EVENT_ERROR; |
1498 | } | 1610 | } |
@@ -1516,13 +1628,6 @@ process_paren(struct event *event, struct print_arg *arg, char **tok) | |||
1516 | item_arg = malloc_or_die(sizeof(*item_arg)); | 1628 | item_arg = malloc_or_die(sizeof(*item_arg)); |
1517 | 1629 | ||
1518 | arg->type = PRINT_TYPE; | 1630 | arg->type = PRINT_TYPE; |
1519 | if (ptr_cast) { | ||
1520 | char *old = arg->atom.atom; | ||
1521 | |||
1522 | arg->atom.atom = malloc_or_die(strlen(old + 3)); | ||
1523 | sprintf(arg->atom.atom, "%s *", old); | ||
1524 | free(old); | ||
1525 | } | ||
1526 | arg->typecast.type = arg->atom.atom; | 1631 | arg->typecast.type = arg->atom.atom; |
1527 | arg->typecast.item = item_arg; | 1632 | arg->typecast.item = item_arg; |
1528 | type = process_arg_token(event, item_arg, &token, type); | 1633 | type = process_arg_token(event, item_arg, &token, type); |
@@ -1540,7 +1645,7 @@ process_str(struct event *event __unused, struct print_arg *arg, char **tok) | |||
1540 | enum event_type type; | 1645 | enum event_type type; |
1541 | char *token; | 1646 | char *token; |
1542 | 1647 | ||
1543 | if (read_expected(EVENT_DELIM, (char *)"(") < 0) | 1648 | if (read_expected(EVENT_DELIM, "(") < 0) |
1544 | return EVENT_ERROR; | 1649 | return EVENT_ERROR; |
1545 | 1650 | ||
1546 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 1651 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
@@ -1550,7 +1655,7 @@ process_str(struct event *event __unused, struct print_arg *arg, char **tok) | |||
1550 | arg->string.string = token; | 1655 | arg->string.string = token; |
1551 | arg->string.offset = -1; | 1656 | arg->string.offset = -1; |
1552 | 1657 | ||
1553 | if (read_expected(EVENT_DELIM, (char *)")") < 0) | 1658 | if (read_expected(EVENT_DELIM, ")") < 0) |
1554 | return EVENT_ERROR; | 1659 | return EVENT_ERROR; |
1555 | 1660 | ||
1556 | type = read_token(&token); | 1661 | type = read_token(&token); |
@@ -1578,9 +1683,11 @@ process_arg_token(struct event *event, struct print_arg *arg, | |||
1578 | type = process_entry(event, arg, &token); | 1683 | type = process_entry(event, arg, &token); |
1579 | } else if (strcmp(token, "__print_flags") == 0) { | 1684 | } else if (strcmp(token, "__print_flags") == 0) { |
1580 | free_token(token); | 1685 | free_token(token); |
1686 | is_flag_field = 1; | ||
1581 | type = process_flags(event, arg, &token); | 1687 | type = process_flags(event, arg, &token); |
1582 | } else if (strcmp(token, "__print_symbolic") == 0) { | 1688 | } else if (strcmp(token, "__print_symbolic") == 0) { |
1583 | free_token(token); | 1689 | free_token(token); |
1690 | is_symbolic_field = 1; | ||
1584 | type = process_symbols(event, arg, &token); | 1691 | type = process_symbols(event, arg, &token); |
1585 | } else if (strcmp(token, "__get_str") == 0) { | 1692 | } else if (strcmp(token, "__get_str") == 0) { |
1586 | free_token(token); | 1693 | free_token(token); |
@@ -1637,12 +1744,18 @@ process_arg_token(struct event *event, struct print_arg *arg, | |||
1637 | 1744 | ||
1638 | static int event_read_print_args(struct event *event, struct print_arg **list) | 1745 | static int event_read_print_args(struct event *event, struct print_arg **list) |
1639 | { | 1746 | { |
1640 | enum event_type type; | 1747 | enum event_type type = EVENT_ERROR; |
1641 | struct print_arg *arg; | 1748 | struct print_arg *arg; |
1642 | char *token; | 1749 | char *token; |
1643 | int args = 0; | 1750 | int args = 0; |
1644 | 1751 | ||
1645 | do { | 1752 | do { |
1753 | if (type == EVENT_NEWLINE) { | ||
1754 | free_token(token); | ||
1755 | type = read_token_item(&token); | ||
1756 | continue; | ||
1757 | } | ||
1758 | |||
1646 | arg = malloc_or_die(sizeof(*arg)); | 1759 | arg = malloc_or_die(sizeof(*arg)); |
1647 | memset(arg, 0, sizeof(*arg)); | 1760 | memset(arg, 0, sizeof(*arg)); |
1648 | 1761 | ||
@@ -1683,18 +1796,19 @@ static int event_read_print(struct event *event) | |||
1683 | char *token; | 1796 | char *token; |
1684 | int ret; | 1797 | int ret; |
1685 | 1798 | ||
1686 | if (read_expected_item(EVENT_ITEM, (char *)"print") < 0) | 1799 | if (read_expected_item(EVENT_ITEM, "print") < 0) |
1687 | return -1; | 1800 | return -1; |
1688 | 1801 | ||
1689 | if (read_expected(EVENT_ITEM, (char *)"fmt") < 0) | 1802 | if (read_expected(EVENT_ITEM, "fmt") < 0) |
1690 | return -1; | 1803 | return -1; |
1691 | 1804 | ||
1692 | if (read_expected(EVENT_OP, (char *)":") < 0) | 1805 | if (read_expected(EVENT_OP, ":") < 0) |
1693 | return -1; | 1806 | return -1; |
1694 | 1807 | ||
1695 | if (read_expect_type(EVENT_DQUOTE, &token) < 0) | 1808 | if (read_expect_type(EVENT_DQUOTE, &token) < 0) |
1696 | goto fail; | 1809 | goto fail; |
1697 | 1810 | ||
1811 | concat: | ||
1698 | event->print_fmt.format = token; | 1812 | event->print_fmt.format = token; |
1699 | event->print_fmt.args = NULL; | 1813 | event->print_fmt.args = NULL; |
1700 | 1814 | ||
@@ -1704,7 +1818,22 @@ static int event_read_print(struct event *event) | |||
1704 | if (type == EVENT_NONE) | 1818 | if (type == EVENT_NONE) |
1705 | return 0; | 1819 | return 0; |
1706 | 1820 | ||
1707 | if (test_type_token(type, token, EVENT_DELIM, (char *)",")) | 1821 | /* Handle concatination of print lines */ |
1822 | if (type == EVENT_DQUOTE) { | ||
1823 | char *cat; | ||
1824 | |||
1825 | cat = malloc_or_die(strlen(event->print_fmt.format) + | ||
1826 | strlen(token) + 1); | ||
1827 | strcpy(cat, event->print_fmt.format); | ||
1828 | strcat(cat, token); | ||
1829 | free_token(token); | ||
1830 | free_token(event->print_fmt.format); | ||
1831 | event->print_fmt.format = NULL; | ||
1832 | token = cat; | ||
1833 | goto concat; | ||
1834 | } | ||
1835 | |||
1836 | if (test_type_token(type, token, EVENT_DELIM, ",")) | ||
1708 | goto fail; | 1837 | goto fail; |
1709 | 1838 | ||
1710 | free_token(token); | 1839 | free_token(token); |
@@ -1713,7 +1842,7 @@ static int event_read_print(struct event *event) | |||
1713 | if (ret < 0) | 1842 | if (ret < 0) |
1714 | return -1; | 1843 | return -1; |
1715 | 1844 | ||
1716 | return 0; | 1845 | return ret; |
1717 | 1846 | ||
1718 | fail: | 1847 | fail: |
1719 | free_token(token); | 1848 | free_token(token); |
@@ -1759,7 +1888,7 @@ find_any_field(struct event *event, const char *name) | |||
1759 | return find_field(event, name); | 1888 | return find_field(event, name); |
1760 | } | 1889 | } |
1761 | 1890 | ||
1762 | static unsigned long long read_size(void *ptr, int size) | 1891 | unsigned long long read_size(void *ptr, int size) |
1763 | { | 1892 | { |
1764 | switch (size) { | 1893 | switch (size) { |
1765 | case 1: | 1894 | case 1: |
@@ -1796,6 +1925,15 @@ void *raw_field_ptr(struct event *event, const char *name, void *data) | |||
1796 | if (!field) | 1925 | if (!field) |
1797 | return NULL; | 1926 | return NULL; |
1798 | 1927 | ||
1928 | if (field->flags & FIELD_IS_STRING) { | ||
1929 | int offset; | ||
1930 | |||
1931 | offset = *(int *)(data + field->offset); | ||
1932 | offset &= 0xffff; | ||
1933 | |||
1934 | return data + offset; | ||
1935 | } | ||
1936 | |||
1799 | return data + field->offset; | 1937 | return data + field->offset; |
1800 | } | 1938 | } |
1801 | 1939 | ||
@@ -1822,37 +1960,67 @@ static int get_common_info(const char *type, int *offset, int *size) | |||
1822 | return 0; | 1960 | return 0; |
1823 | } | 1961 | } |
1824 | 1962 | ||
1825 | int trace_parse_common_type(void *data) | 1963 | static int __parse_common(void *data, int *size, int *offset, |
1964 | const char *name) | ||
1826 | { | 1965 | { |
1827 | static int type_offset; | ||
1828 | static int type_size; | ||
1829 | int ret; | 1966 | int ret; |
1830 | 1967 | ||
1831 | if (!type_size) { | 1968 | if (!*size) { |
1832 | ret = get_common_info("common_type", | 1969 | ret = get_common_info(name, offset, size); |
1833 | &type_offset, | ||
1834 | &type_size); | ||
1835 | if (ret < 0) | 1970 | if (ret < 0) |
1836 | return ret; | 1971 | return ret; |
1837 | } | 1972 | } |
1838 | return read_size(data + type_offset, type_size); | 1973 | return read_size(data + *offset, *size); |
1839 | } | 1974 | } |
1840 | 1975 | ||
1841 | static int parse_common_pid(void *data) | 1976 | int trace_parse_common_type(void *data) |
1977 | { | ||
1978 | static int type_offset; | ||
1979 | static int type_size; | ||
1980 | |||
1981 | return __parse_common(data, &type_size, &type_offset, | ||
1982 | "common_type"); | ||
1983 | } | ||
1984 | |||
1985 | int trace_parse_common_pid(void *data) | ||
1842 | { | 1986 | { |
1843 | static int pid_offset; | 1987 | static int pid_offset; |
1844 | static int pid_size; | 1988 | static int pid_size; |
1989 | |||
1990 | return __parse_common(data, &pid_size, &pid_offset, | ||
1991 | "common_pid"); | ||
1992 | } | ||
1993 | |||
1994 | int parse_common_pc(void *data) | ||
1995 | { | ||
1996 | static int pc_offset; | ||
1997 | static int pc_size; | ||
1998 | |||
1999 | return __parse_common(data, &pc_size, &pc_offset, | ||
2000 | "common_preempt_count"); | ||
2001 | } | ||
2002 | |||
2003 | int parse_common_flags(void *data) | ||
2004 | { | ||
2005 | static int flags_offset; | ||
2006 | static int flags_size; | ||
2007 | |||
2008 | return __parse_common(data, &flags_size, &flags_offset, | ||
2009 | "common_flags"); | ||
2010 | } | ||
2011 | |||
2012 | int parse_common_lock_depth(void *data) | ||
2013 | { | ||
2014 | static int ld_offset; | ||
2015 | static int ld_size; | ||
1845 | int ret; | 2016 | int ret; |
1846 | 2017 | ||
1847 | if (!pid_size) { | 2018 | ret = __parse_common(data, &ld_size, &ld_offset, |
1848 | ret = get_common_info("common_pid", | 2019 | "common_lock_depth"); |
1849 | &pid_offset, | 2020 | if (ret < 0) |
1850 | &pid_size); | 2021 | return -1; |
1851 | if (ret < 0) | ||
1852 | return ret; | ||
1853 | } | ||
1854 | 2022 | ||
1855 | return read_size(data + pid_offset, pid_size); | 2023 | return ret; |
1856 | } | 2024 | } |
1857 | 2025 | ||
1858 | struct event *trace_find_event(int id) | 2026 | struct event *trace_find_event(int id) |
@@ -1866,11 +2034,20 @@ struct event *trace_find_event(int id) | |||
1866 | return event; | 2034 | return event; |
1867 | } | 2035 | } |
1868 | 2036 | ||
2037 | struct event *trace_find_next_event(struct event *event) | ||
2038 | { | ||
2039 | if (!event) | ||
2040 | return event_list; | ||
2041 | |||
2042 | return event->next; | ||
2043 | } | ||
2044 | |||
1869 | static unsigned long long eval_num_arg(void *data, int size, | 2045 | static unsigned long long eval_num_arg(void *data, int size, |
1870 | struct event *event, struct print_arg *arg) | 2046 | struct event *event, struct print_arg *arg) |
1871 | { | 2047 | { |
1872 | unsigned long long val = 0; | 2048 | unsigned long long val = 0; |
1873 | unsigned long long left, right; | 2049 | unsigned long long left, right; |
2050 | struct print_arg *larg; | ||
1874 | 2051 | ||
1875 | switch (arg->type) { | 2052 | switch (arg->type) { |
1876 | case PRINT_NULL: | 2053 | case PRINT_NULL: |
@@ -1897,6 +2074,26 @@ static unsigned long long eval_num_arg(void *data, int size, | |||
1897 | return 0; | 2074 | return 0; |
1898 | break; | 2075 | break; |
1899 | case PRINT_OP: | 2076 | case PRINT_OP: |
2077 | if (strcmp(arg->op.op, "[") == 0) { | ||
2078 | /* | ||
2079 | * Arrays are special, since we don't want | ||
2080 | * to read the arg as is. | ||
2081 | */ | ||
2082 | if (arg->op.left->type != PRINT_FIELD) | ||
2083 | goto default_op; /* oops, all bets off */ | ||
2084 | larg = arg->op.left; | ||
2085 | if (!larg->field.field) { | ||
2086 | larg->field.field = | ||
2087 | find_any_field(event, larg->field.name); | ||
2088 | if (!larg->field.field) | ||
2089 | die("field %s not found", larg->field.name); | ||
2090 | } | ||
2091 | right = eval_num_arg(data, size, event, arg->op.right); | ||
2092 | val = read_size(data + larg->field.field->offset + | ||
2093 | right * long_size, long_size); | ||
2094 | break; | ||
2095 | } | ||
2096 | default_op: | ||
1900 | left = eval_num_arg(data, size, event, arg->op.left); | 2097 | left = eval_num_arg(data, size, event, arg->op.left); |
1901 | right = eval_num_arg(data, size, event, arg->op.right); | 2098 | right = eval_num_arg(data, size, event, arg->op.right); |
1902 | switch (arg->op.op[0]) { | 2099 | switch (arg->op.op[0]) { |
@@ -1947,6 +2144,12 @@ static unsigned long long eval_num_arg(void *data, int size, | |||
1947 | die("unknown op '%s'", arg->op.op); | 2144 | die("unknown op '%s'", arg->op.op); |
1948 | val = left == right; | 2145 | val = left == right; |
1949 | break; | 2146 | break; |
2147 | case '-': | ||
2148 | val = left - right; | ||
2149 | break; | ||
2150 | case '+': | ||
2151 | val = left + right; | ||
2152 | break; | ||
1950 | default: | 2153 | default: |
1951 | die("unknown op '%s'", arg->op.op); | 2154 | die("unknown op '%s'", arg->op.op); |
1952 | } | 2155 | } |
@@ -1978,7 +2181,7 @@ static const struct flag flags[] = { | |||
1978 | { "HRTIMER_RESTART", 1 }, | 2181 | { "HRTIMER_RESTART", 1 }, |
1979 | }; | 2182 | }; |
1980 | 2183 | ||
1981 | static unsigned long long eval_flag(const char *flag) | 2184 | unsigned long long eval_flag(const char *flag) |
1982 | { | 2185 | { |
1983 | int i; | 2186 | int i; |
1984 | 2187 | ||
@@ -2145,8 +2348,9 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc | |||
2145 | case 'u': | 2348 | case 'u': |
2146 | case 'x': | 2349 | case 'x': |
2147 | case 'i': | 2350 | case 'i': |
2148 | bptr = (void *)(((unsigned long)bptr + (long_size - 1)) & | 2351 | /* the pointers are always 4 bytes aligned */ |
2149 | ~(long_size - 1)); | 2352 | bptr = (void *)(((unsigned long)bptr + 3) & |
2353 | ~3); | ||
2150 | switch (ls) { | 2354 | switch (ls) { |
2151 | case 0: | 2355 | case 0: |
2152 | case 1: | 2356 | case 1: |
@@ -2270,7 +2474,27 @@ static void pretty_print(void *data, int size, struct event *event) | |||
2270 | 2474 | ||
2271 | for (; *ptr; ptr++) { | 2475 | for (; *ptr; ptr++) { |
2272 | ls = 0; | 2476 | ls = 0; |
2273 | if (*ptr == '%') { | 2477 | if (*ptr == '\\') { |
2478 | ptr++; | ||
2479 | switch (*ptr) { | ||
2480 | case 'n': | ||
2481 | printf("\n"); | ||
2482 | break; | ||
2483 | case 't': | ||
2484 | printf("\t"); | ||
2485 | break; | ||
2486 | case 'r': | ||
2487 | printf("\r"); | ||
2488 | break; | ||
2489 | case '\\': | ||
2490 | printf("\\"); | ||
2491 | break; | ||
2492 | default: | ||
2493 | printf("%c", *ptr); | ||
2494 | break; | ||
2495 | } | ||
2496 | |||
2497 | } else if (*ptr == '%') { | ||
2274 | saveptr = ptr; | 2498 | saveptr = ptr; |
2275 | show_func = 0; | 2499 | show_func = 0; |
2276 | cont_process: | 2500 | cont_process: |
@@ -2377,6 +2601,41 @@ static inline int log10_cpu(int nb) | |||
2377 | return 1; | 2601 | return 1; |
2378 | } | 2602 | } |
2379 | 2603 | ||
2604 | static void print_lat_fmt(void *data, int size __unused) | ||
2605 | { | ||
2606 | unsigned int lat_flags; | ||
2607 | unsigned int pc; | ||
2608 | int lock_depth; | ||
2609 | int hardirq; | ||
2610 | int softirq; | ||
2611 | |||
2612 | lat_flags = parse_common_flags(data); | ||
2613 | pc = parse_common_pc(data); | ||
2614 | lock_depth = parse_common_lock_depth(data); | ||
2615 | |||
2616 | hardirq = lat_flags & TRACE_FLAG_HARDIRQ; | ||
2617 | softirq = lat_flags & TRACE_FLAG_SOFTIRQ; | ||
2618 | |||
2619 | printf("%c%c%c", | ||
2620 | (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : | ||
2621 | (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? | ||
2622 | 'X' : '.', | ||
2623 | (lat_flags & TRACE_FLAG_NEED_RESCHED) ? | ||
2624 | 'N' : '.', | ||
2625 | (hardirq && softirq) ? 'H' : | ||
2626 | hardirq ? 'h' : softirq ? 's' : '.'); | ||
2627 | |||
2628 | if (pc) | ||
2629 | printf("%x", pc); | ||
2630 | else | ||
2631 | printf("."); | ||
2632 | |||
2633 | if (lock_depth < 0) | ||
2634 | printf("."); | ||
2635 | else | ||
2636 | printf("%d", lock_depth); | ||
2637 | } | ||
2638 | |||
2380 | /* taken from Linux, written by Frederic Weisbecker */ | 2639 | /* taken from Linux, written by Frederic Weisbecker */ |
2381 | static void print_graph_cpu(int cpu) | 2640 | static void print_graph_cpu(int cpu) |
2382 | { | 2641 | { |
@@ -2452,7 +2711,7 @@ get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, | |||
2452 | if (!(event->flags & EVENT_FL_ISFUNCRET)) | 2711 | if (!(event->flags & EVENT_FL_ISFUNCRET)) |
2453 | return NULL; | 2712 | return NULL; |
2454 | 2713 | ||
2455 | pid = parse_common_pid(next->data); | 2714 | pid = trace_parse_common_pid(next->data); |
2456 | field = find_field(event, "func"); | 2715 | field = find_field(event, "func"); |
2457 | if (!field) | 2716 | if (!field) |
2458 | die("function return does not have field func"); | 2717 | die("function return does not have field func"); |
@@ -2620,6 +2879,11 @@ pretty_print_func_ent(void *data, int size, struct event *event, | |||
2620 | 2879 | ||
2621 | printf(" | "); | 2880 | printf(" | "); |
2622 | 2881 | ||
2882 | if (latency_format) { | ||
2883 | print_lat_fmt(data, size); | ||
2884 | printf(" | "); | ||
2885 | } | ||
2886 | |||
2623 | field = find_field(event, "func"); | 2887 | field = find_field(event, "func"); |
2624 | if (!field) | 2888 | if (!field) |
2625 | die("function entry does not have func field"); | 2889 | die("function entry does not have func field"); |
@@ -2663,6 +2927,11 @@ pretty_print_func_ret(void *data, int size __unused, struct event *event, | |||
2663 | 2927 | ||
2664 | printf(" | "); | 2928 | printf(" | "); |
2665 | 2929 | ||
2930 | if (latency_format) { | ||
2931 | print_lat_fmt(data, size); | ||
2932 | printf(" | "); | ||
2933 | } | ||
2934 | |||
2666 | field = find_field(event, "rettime"); | 2935 | field = find_field(event, "rettime"); |
2667 | if (!field) | 2936 | if (!field) |
2668 | die("can't find rettime in return graph"); | 2937 | die("can't find rettime in return graph"); |
@@ -2724,19 +2993,30 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, | |||
2724 | 2993 | ||
2725 | event = trace_find_event(type); | 2994 | event = trace_find_event(type); |
2726 | if (!event) { | 2995 | if (!event) { |
2727 | printf("ug! no event found for type %d\n", type); | 2996 | warning("ug! no event found for type %d", type); |
2728 | return; | 2997 | return; |
2729 | } | 2998 | } |
2730 | 2999 | ||
2731 | pid = parse_common_pid(data); | 3000 | pid = trace_parse_common_pid(data); |
2732 | 3001 | ||
2733 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) | 3002 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) |
2734 | return pretty_print_func_graph(data, size, event, cpu, | 3003 | return pretty_print_func_graph(data, size, event, cpu, |
2735 | pid, comm, secs, usecs); | 3004 | pid, comm, secs, usecs); |
2736 | 3005 | ||
2737 | printf("%16s-%-5d [%03d] %5lu.%09Lu: %s: ", | 3006 | if (latency_format) { |
2738 | comm, pid, cpu, | 3007 | printf("%8.8s-%-5d %3d", |
2739 | secs, nsecs, event->name); | 3008 | comm, pid, cpu); |
3009 | print_lat_fmt(data, size); | ||
3010 | } else | ||
3011 | printf("%16s-%-5d [%03d]", comm, pid, cpu); | ||
3012 | |||
3013 | printf(" %5lu.%06lu: %s: ", secs, usecs, event->name); | ||
3014 | |||
3015 | if (event->flags & EVENT_FL_FAILED) { | ||
3016 | printf("EVENT '%s' FAILED TO PARSE\n", | ||
3017 | event->name); | ||
3018 | return; | ||
3019 | } | ||
2740 | 3020 | ||
2741 | pretty_print(data, size, event); | 3021 | pretty_print(data, size, event); |
2742 | printf("\n"); | 3022 | printf("\n"); |
@@ -2807,46 +3087,71 @@ static void print_args(struct print_arg *args) | |||
2807 | } | 3087 | } |
2808 | } | 3088 | } |
2809 | 3089 | ||
2810 | static void parse_header_field(char *type, | 3090 | static void parse_header_field(const char *field, |
2811 | int *offset, int *size) | 3091 | int *offset, int *size) |
2812 | { | 3092 | { |
2813 | char *token; | 3093 | char *token; |
3094 | int type; | ||
2814 | 3095 | ||
2815 | if (read_expected(EVENT_ITEM, (char *)"field") < 0) | 3096 | if (read_expected(EVENT_ITEM, "field") < 0) |
2816 | return; | 3097 | return; |
2817 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3098 | if (read_expected(EVENT_OP, ":") < 0) |
2818 | return; | 3099 | return; |
3100 | |||
2819 | /* type */ | 3101 | /* type */ |
2820 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3102 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
2821 | return; | 3103 | goto fail; |
2822 | free_token(token); | 3104 | free_token(token); |
2823 | 3105 | ||
2824 | if (read_expected(EVENT_ITEM, type) < 0) | 3106 | if (read_expected(EVENT_ITEM, field) < 0) |
2825 | return; | 3107 | return; |
2826 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3108 | if (read_expected(EVENT_OP, ";") < 0) |
2827 | return; | 3109 | return; |
2828 | if (read_expected(EVENT_ITEM, (char *)"offset") < 0) | 3110 | if (read_expected(EVENT_ITEM, "offset") < 0) |
2829 | return; | 3111 | return; |
2830 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3112 | if (read_expected(EVENT_OP, ":") < 0) |
2831 | return; | 3113 | return; |
2832 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3114 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
2833 | return; | 3115 | goto fail; |
2834 | *offset = atoi(token); | 3116 | *offset = atoi(token); |
2835 | free_token(token); | 3117 | free_token(token); |
2836 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3118 | if (read_expected(EVENT_OP, ";") < 0) |
2837 | return; | 3119 | return; |
2838 | if (read_expected(EVENT_ITEM, (char *)"size") < 0) | 3120 | if (read_expected(EVENT_ITEM, "size") < 0) |
2839 | return; | 3121 | return; |
2840 | if (read_expected(EVENT_OP, (char *)":") < 0) | 3122 | if (read_expected(EVENT_OP, ":") < 0) |
2841 | return; | 3123 | return; |
2842 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 3124 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
2843 | return; | 3125 | goto fail; |
2844 | *size = atoi(token); | 3126 | *size = atoi(token); |
2845 | free_token(token); | 3127 | free_token(token); |
2846 | if (read_expected(EVENT_OP, (char *)";") < 0) | 3128 | if (read_expected(EVENT_OP, ";") < 0) |
2847 | return; | ||
2848 | if (read_expect_type(EVENT_NEWLINE, &token) < 0) | ||
2849 | return; | 3129 | return; |
3130 | type = read_token(&token); | ||
3131 | if (type != EVENT_NEWLINE) { | ||
3132 | /* newer versions of the kernel have a "signed" type */ | ||
3133 | if (type != EVENT_ITEM) | ||
3134 | goto fail; | ||
3135 | |||
3136 | if (strcmp(token, "signed") != 0) | ||
3137 | goto fail; | ||
3138 | |||
3139 | free_token(token); | ||
3140 | |||
3141 | if (read_expected(EVENT_OP, ":") < 0) | ||
3142 | return; | ||
3143 | |||
3144 | if (read_expect_type(EVENT_ITEM, &token)) | ||
3145 | goto fail; | ||
3146 | |||
3147 | free_token(token); | ||
3148 | if (read_expected(EVENT_OP, ";") < 0) | ||
3149 | return; | ||
3150 | |||
3151 | if (read_expect_type(EVENT_NEWLINE, &token)) | ||
3152 | goto fail; | ||
3153 | } | ||
3154 | fail: | ||
2850 | free_token(token); | 3155 | free_token(token); |
2851 | } | 3156 | } |
2852 | 3157 | ||
@@ -2854,11 +3159,11 @@ int parse_header_page(char *buf, unsigned long size) | |||
2854 | { | 3159 | { |
2855 | init_input_buf(buf, size); | 3160 | init_input_buf(buf, size); |
2856 | 3161 | ||
2857 | parse_header_field((char *)"timestamp", &header_page_ts_offset, | 3162 | parse_header_field("timestamp", &header_page_ts_offset, |
2858 | &header_page_ts_size); | 3163 | &header_page_ts_size); |
2859 | parse_header_field((char *)"commit", &header_page_size_offset, | 3164 | parse_header_field("commit", &header_page_size_offset, |
2860 | &header_page_size_size); | 3165 | &header_page_size_size); |
2861 | parse_header_field((char *)"data", &header_page_data_offset, | 3166 | parse_header_field("data", &header_page_data_offset, |
2862 | &header_page_data_size); | 3167 | &header_page_data_size); |
2863 | 3168 | ||
2864 | return 0; | 3169 | return 0; |
@@ -2909,6 +3214,9 @@ int parse_ftrace_file(char *buf, unsigned long size) | |||
2909 | if (ret < 0) | 3214 | if (ret < 0) |
2910 | die("failed to read ftrace event print fmt"); | 3215 | die("failed to read ftrace event print fmt"); |
2911 | 3216 | ||
3217 | /* New ftrace handles args */ | ||
3218 | if (ret > 0) | ||
3219 | return 0; | ||
2912 | /* | 3220 | /* |
2913 | * The arguments for ftrace files are parsed by the fields. | 3221 | * The arguments for ftrace files are parsed by the fields. |
2914 | * Set up the fields as their arguments. | 3222 | * Set up the fields as their arguments. |
@@ -2926,7 +3234,7 @@ int parse_ftrace_file(char *buf, unsigned long size) | |||
2926 | return 0; | 3234 | return 0; |
2927 | } | 3235 | } |
2928 | 3236 | ||
2929 | int parse_event_file(char *buf, unsigned long size, char *system__unused __unused) | 3237 | int parse_event_file(char *buf, unsigned long size, char *sys) |
2930 | { | 3238 | { |
2931 | struct event *event; | 3239 | struct event *event; |
2932 | int ret; | 3240 | int ret; |
@@ -2946,12 +3254,18 @@ int parse_event_file(char *buf, unsigned long size, char *system__unused __unuse | |||
2946 | die("failed to read event id"); | 3254 | die("failed to read event id"); |
2947 | 3255 | ||
2948 | ret = event_read_format(event); | 3256 | ret = event_read_format(event); |
2949 | if (ret < 0) | 3257 | if (ret < 0) { |
2950 | die("failed to read event format"); | 3258 | warning("failed to read event format for %s", event->name); |
3259 | goto event_failed; | ||
3260 | } | ||
2951 | 3261 | ||
2952 | ret = event_read_print(event); | 3262 | ret = event_read_print(event); |
2953 | if (ret < 0) | 3263 | if (ret < 0) { |
2954 | die("failed to read event print fmt"); | 3264 | warning("failed to read event print fmt for %s", event->name); |
3265 | goto event_failed; | ||
3266 | } | ||
3267 | |||
3268 | event->system = strdup(sys); | ||
2955 | 3269 | ||
2956 | #define PRINT_ARGS 0 | 3270 | #define PRINT_ARGS 0 |
2957 | if (PRINT_ARGS && event->print_fmt.args) | 3271 | if (PRINT_ARGS && event->print_fmt.args) |
@@ -2959,6 +3273,12 @@ int parse_event_file(char *buf, unsigned long size, char *system__unused __unuse | |||
2959 | 3273 | ||
2960 | add_event(event); | 3274 | add_event(event); |
2961 | return 0; | 3275 | return 0; |
3276 | |||
3277 | event_failed: | ||
3278 | event->flags |= EVENT_FL_FAILED; | ||
3279 | /* still add it even if it failed */ | ||
3280 | add_event(event); | ||
3281 | return -1; | ||
2962 | } | 3282 | } |
2963 | 3283 | ||
2964 | void parse_set_info(int nr_cpus, int long_sz) | 3284 | void parse_set_info(int nr_cpus, int long_sz) |
@@ -2966,3 +3286,18 @@ void parse_set_info(int nr_cpus, int long_sz) | |||
2966 | cpus = nr_cpus; | 3286 | cpus = nr_cpus; |
2967 | long_size = long_sz; | 3287 | long_size = long_sz; |
2968 | } | 3288 | } |
3289 | |||
3290 | int common_pc(struct scripting_context *context) | ||
3291 | { | ||
3292 | return parse_common_pc(context->event_data); | ||
3293 | } | ||
3294 | |||
3295 | int common_flags(struct scripting_context *context) | ||
3296 | { | ||
3297 | return parse_common_flags(context->event_data); | ||
3298 | } | ||
3299 | |||
3300 | int common_lock_depth(struct scripting_context *context) | ||
3301 | { | ||
3302 | return parse_common_lock_depth(context->event_data); | ||
3303 | } | ||
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 1b5c847d2c22..7cd1193918c7 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -18,7 +18,7 @@ | |||
18 | * | 18 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 20 | */ |
21 | #define _LARGEFILE64_SOURCE | 21 | #define _FILE_OFFSET_BITS 64 |
22 | 22 | ||
23 | #include <dirent.h> | 23 | #include <dirent.h> |
24 | #include <stdio.h> | 24 | #include <stdio.h> |
@@ -83,7 +83,7 @@ static char *read_string(void) | |||
83 | char *str = NULL; | 83 | char *str = NULL; |
84 | int size = 0; | 84 | int size = 0; |
85 | int i; | 85 | int i; |
86 | int r; | 86 | off_t r; |
87 | 87 | ||
88 | for (;;) { | 88 | for (;;) { |
89 | r = read(input_fd, buf, BUFSIZ); | 89 | r = read(input_fd, buf, BUFSIZ); |
@@ -118,7 +118,7 @@ static char *read_string(void) | |||
118 | 118 | ||
119 | /* move the file descriptor to the end of the string */ | 119 | /* move the file descriptor to the end of the string */ |
120 | r = lseek(input_fd, -(r - i), SEEK_CUR); | 120 | r = lseek(input_fd, -(r - i), SEEK_CUR); |
121 | if (r < 0) | 121 | if (r == (off_t)-1) |
122 | die("lseek"); | 122 | die("lseek"); |
123 | 123 | ||
124 | if (str) { | 124 | if (str) { |
@@ -145,8 +145,9 @@ static void read_proc_kallsyms(void) | |||
145 | if (!size) | 145 | if (!size) |
146 | return; | 146 | return; |
147 | 147 | ||
148 | buf = malloc_or_die(size); | 148 | buf = malloc_or_die(size + 1); |
149 | read_or_die(buf, size); | 149 | read_or_die(buf, size); |
150 | buf[size] = '\0'; | ||
150 | 151 | ||
151 | parse_proc_kallsyms(buf, size); | 152 | parse_proc_kallsyms(buf, size); |
152 | 153 | ||
@@ -281,8 +282,8 @@ static void update_cpu_data_index(int cpu) | |||
281 | 282 | ||
282 | static void get_next_page(int cpu) | 283 | static void get_next_page(int cpu) |
283 | { | 284 | { |
284 | off64_t save_seek; | 285 | off_t save_seek; |
285 | off64_t ret; | 286 | off_t ret; |
286 | 287 | ||
287 | if (!cpu_data[cpu].page) | 288 | if (!cpu_data[cpu].page) |
288 | return; | 289 | return; |
@@ -297,17 +298,17 @@ static void get_next_page(int cpu) | |||
297 | update_cpu_data_index(cpu); | 298 | update_cpu_data_index(cpu); |
298 | 299 | ||
299 | /* other parts of the code may expect the pointer to not move */ | 300 | /* other parts of the code may expect the pointer to not move */ |
300 | save_seek = lseek64(input_fd, 0, SEEK_CUR); | 301 | save_seek = lseek(input_fd, 0, SEEK_CUR); |
301 | 302 | ||
302 | ret = lseek64(input_fd, cpu_data[cpu].offset, SEEK_SET); | 303 | ret = lseek(input_fd, cpu_data[cpu].offset, SEEK_SET); |
303 | if (ret < 0) | 304 | if (ret == (off_t)-1) |
304 | die("failed to lseek"); | 305 | die("failed to lseek"); |
305 | ret = read(input_fd, cpu_data[cpu].page, page_size); | 306 | ret = read(input_fd, cpu_data[cpu].page, page_size); |
306 | if (ret < 0) | 307 | if (ret < 0) |
307 | die("failed to read page"); | 308 | die("failed to read page"); |
308 | 309 | ||
309 | /* reset the file pointer back */ | 310 | /* reset the file pointer back */ |
310 | lseek64(input_fd, save_seek, SEEK_SET); | 311 | lseek(input_fd, save_seek, SEEK_SET); |
311 | 312 | ||
312 | return; | 313 | return; |
313 | } | 314 | } |
@@ -458,9 +459,8 @@ struct record *trace_read_data(int cpu) | |||
458 | return data; | 459 | return data; |
459 | } | 460 | } |
460 | 461 | ||
461 | void trace_report(void) | 462 | void trace_report(int fd) |
462 | { | 463 | { |
463 | const char *input_file = "trace.info"; | ||
464 | char buf[BUFSIZ]; | 464 | char buf[BUFSIZ]; |
465 | char test[] = { 23, 8, 68 }; | 465 | char test[] = { 23, 8, 68 }; |
466 | char *version; | 466 | char *version; |
@@ -468,17 +468,15 @@ void trace_report(void) | |||
468 | int show_funcs = 0; | 468 | int show_funcs = 0; |
469 | int show_printk = 0; | 469 | int show_printk = 0; |
470 | 470 | ||
471 | input_fd = open(input_file, O_RDONLY); | 471 | input_fd = fd; |
472 | if (input_fd < 0) | ||
473 | die("opening '%s'\n", input_file); | ||
474 | 472 | ||
475 | read_or_die(buf, 3); | 473 | read_or_die(buf, 3); |
476 | if (memcmp(buf, test, 3) != 0) | 474 | if (memcmp(buf, test, 3) != 0) |
477 | die("not an trace data file"); | 475 | die("no trace data in the file"); |
478 | 476 | ||
479 | read_or_die(buf, 7); | 477 | read_or_die(buf, 7); |
480 | if (memcmp(buf, "tracing", 7) != 0) | 478 | if (memcmp(buf, "tracing", 7) != 0) |
481 | die("not a trace file (missing tracing)"); | 479 | die("not a trace file (missing 'tracing' tag)"); |
482 | 480 | ||
483 | version = read_string(); | 481 | version = read_string(); |
484 | if (show_version) | 482 | if (show_version) |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c new file mode 100644 index 000000000000..7ea983acfaea --- /dev/null +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -0,0 +1,167 @@ | |||
1 | /* | ||
2 | * trace-event-scripting. Scripting engine common and initialization code. | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Tom Zanussi <tzanussi@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <stdio.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <string.h> | ||
25 | #include <ctype.h> | ||
26 | #include <errno.h> | ||
27 | |||
28 | #include "../perf.h" | ||
29 | #include "util.h" | ||
30 | #include "trace-event.h" | ||
31 | |||
32 | struct scripting_context *scripting_context; | ||
33 | |||
34 | static int stop_script_unsupported(void) | ||
35 | { | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | static void process_event_unsupported(int cpu __unused, | ||
40 | void *data __unused, | ||
41 | int size __unused, | ||
42 | unsigned long long nsecs __unused, | ||
43 | char *comm __unused) | ||
44 | { | ||
45 | } | ||
46 | |||
47 | static void print_python_unsupported_msg(void) | ||
48 | { | ||
49 | fprintf(stderr, "Python scripting not supported." | ||
50 | " Install libpython and rebuild perf to enable it.\n" | ||
51 | "For example:\n # apt-get install python-dev (ubuntu)" | ||
52 | "\n # yum install python-devel (Fedora)" | ||
53 | "\n etc.\n"); | ||
54 | } | ||
55 | |||
56 | static int python_start_script_unsupported(const char *script __unused, | ||
57 | int argc __unused, | ||
58 | const char **argv __unused) | ||
59 | { | ||
60 | print_python_unsupported_msg(); | ||
61 | |||
62 | return -1; | ||
63 | } | ||
64 | |||
65 | static int python_generate_script_unsupported(const char *outfile __unused) | ||
66 | { | ||
67 | print_python_unsupported_msg(); | ||
68 | |||
69 | return -1; | ||
70 | } | ||
71 | |||
72 | struct scripting_ops python_scripting_unsupported_ops = { | ||
73 | .name = "Python", | ||
74 | .start_script = python_start_script_unsupported, | ||
75 | .stop_script = stop_script_unsupported, | ||
76 | .process_event = process_event_unsupported, | ||
77 | .generate_script = python_generate_script_unsupported, | ||
78 | }; | ||
79 | |||
80 | static void register_python_scripting(struct scripting_ops *scripting_ops) | ||
81 | { | ||
82 | int err; | ||
83 | err = script_spec_register("Python", scripting_ops); | ||
84 | if (err) | ||
85 | die("error registering Python script extension"); | ||
86 | |||
87 | err = script_spec_register("py", scripting_ops); | ||
88 | if (err) | ||
89 | die("error registering py script extension"); | ||
90 | |||
91 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
92 | } | ||
93 | |||
94 | #ifdef NO_LIBPYTHON | ||
95 | void setup_python_scripting(void) | ||
96 | { | ||
97 | register_python_scripting(&python_scripting_unsupported_ops); | ||
98 | } | ||
99 | #else | ||
100 | struct scripting_ops python_scripting_ops; | ||
101 | |||
102 | void setup_python_scripting(void) | ||
103 | { | ||
104 | register_python_scripting(&python_scripting_ops); | ||
105 | } | ||
106 | #endif | ||
107 | |||
108 | static void print_perl_unsupported_msg(void) | ||
109 | { | ||
110 | fprintf(stderr, "Perl scripting not supported." | ||
111 | " Install libperl and rebuild perf to enable it.\n" | ||
112 | "For example:\n # apt-get install libperl-dev (ubuntu)" | ||
113 | "\n # yum install 'perl(ExtUtils::Embed)' (Fedora)" | ||
114 | "\n etc.\n"); | ||
115 | } | ||
116 | |||
117 | static int perl_start_script_unsupported(const char *script __unused, | ||
118 | int argc __unused, | ||
119 | const char **argv __unused) | ||
120 | { | ||
121 | print_perl_unsupported_msg(); | ||
122 | |||
123 | return -1; | ||
124 | } | ||
125 | |||
126 | static int perl_generate_script_unsupported(const char *outfile __unused) | ||
127 | { | ||
128 | print_perl_unsupported_msg(); | ||
129 | |||
130 | return -1; | ||
131 | } | ||
132 | |||
133 | struct scripting_ops perl_scripting_unsupported_ops = { | ||
134 | .name = "Perl", | ||
135 | .start_script = perl_start_script_unsupported, | ||
136 | .stop_script = stop_script_unsupported, | ||
137 | .process_event = process_event_unsupported, | ||
138 | .generate_script = perl_generate_script_unsupported, | ||
139 | }; | ||
140 | |||
141 | static void register_perl_scripting(struct scripting_ops *scripting_ops) | ||
142 | { | ||
143 | int err; | ||
144 | err = script_spec_register("Perl", scripting_ops); | ||
145 | if (err) | ||
146 | die("error registering Perl script extension"); | ||
147 | |||
148 | err = script_spec_register("pl", scripting_ops); | ||
149 | if (err) | ||
150 | die("error registering pl script extension"); | ||
151 | |||
152 | scripting_context = malloc(sizeof(struct scripting_context)); | ||
153 | } | ||
154 | |||
155 | #ifdef NO_LIBPERL | ||
156 | void setup_perl_scripting(void) | ||
157 | { | ||
158 | register_perl_scripting(&perl_scripting_unsupported_ops); | ||
159 | } | ||
160 | #else | ||
161 | struct scripting_ops perl_scripting_ops; | ||
162 | |||
163 | void setup_perl_scripting(void) | ||
164 | { | ||
165 | register_perl_scripting(&perl_scripting_ops); | ||
166 | } | ||
167 | #endif | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 693f815c9429..c3269b937db4 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _TRACE_EVENTS_H | 1 | #ifndef __PERF_TRACE_EVENTS_H |
2 | #define _TRACE_EVENTS_H | 2 | #define __PERF_TRACE_EVENTS_H |
3 | 3 | ||
4 | #include "parse-events.h" | 4 | #include "parse-events.h" |
5 | 5 | ||
@@ -26,6 +26,11 @@ enum { | |||
26 | enum format_flags { | 26 | enum format_flags { |
27 | FIELD_IS_ARRAY = 1, | 27 | FIELD_IS_ARRAY = 1, |
28 | FIELD_IS_POINTER = 2, | 28 | FIELD_IS_POINTER = 2, |
29 | FIELD_IS_SIGNED = 4, | ||
30 | FIELD_IS_STRING = 8, | ||
31 | FIELD_IS_DYNAMIC = 16, | ||
32 | FIELD_IS_FLAG = 32, | ||
33 | FIELD_IS_SYMBOLIC = 64, | ||
29 | }; | 34 | }; |
30 | 35 | ||
31 | struct format_field { | 36 | struct format_field { |
@@ -132,15 +137,18 @@ struct event { | |||
132 | int flags; | 137 | int flags; |
133 | struct format format; | 138 | struct format format; |
134 | struct print_fmt print_fmt; | 139 | struct print_fmt print_fmt; |
140 | char *system; | ||
135 | }; | 141 | }; |
136 | 142 | ||
137 | enum { | 143 | enum { |
138 | EVENT_FL_ISFTRACE = 1, | 144 | EVENT_FL_ISFTRACE = 0x01, |
139 | EVENT_FL_ISPRINT = 2, | 145 | EVENT_FL_ISPRINT = 0x02, |
140 | EVENT_FL_ISBPRINT = 4, | 146 | EVENT_FL_ISBPRINT = 0x04, |
141 | EVENT_FL_ISFUNC = 8, | 147 | EVENT_FL_ISFUNC = 0x08, |
142 | EVENT_FL_ISFUNCENT = 16, | 148 | EVENT_FL_ISFUNCENT = 0x10, |
143 | EVENT_FL_ISFUNCRET = 32, | 149 | EVENT_FL_ISFUNCRET = 0x20, |
150 | |||
151 | EVENT_FL_FAILED = 0x80000000 | ||
144 | }; | 152 | }; |
145 | 153 | ||
146 | struct record { | 154 | struct record { |
@@ -154,7 +162,7 @@ struct record *trace_read_data(int cpu); | |||
154 | 162 | ||
155 | void parse_set_info(int nr_cpus, int long_sz); | 163 | void parse_set_info(int nr_cpus, int long_sz); |
156 | 164 | ||
157 | void trace_report(void); | 165 | void trace_report(int fd); |
158 | 166 | ||
159 | void *malloc_or_die(unsigned int size); | 167 | void *malloc_or_die(unsigned int size); |
160 | 168 | ||
@@ -166,7 +174,7 @@ void print_funcs(void); | |||
166 | void print_printk(void); | 174 | void print_printk(void); |
167 | 175 | ||
168 | int parse_ftrace_file(char *buf, unsigned long size); | 176 | int parse_ftrace_file(char *buf, unsigned long size); |
169 | int parse_event_file(char *buf, unsigned long size, char *system); | 177 | int parse_event_file(char *buf, unsigned long size, char *sys); |
170 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, | 178 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, |
171 | char *comm); | 179 | char *comm); |
172 | 180 | ||
@@ -233,13 +241,53 @@ extern int header_page_size_size; | |||
233 | extern int header_page_data_offset; | 241 | extern int header_page_data_offset; |
234 | extern int header_page_data_size; | 242 | extern int header_page_data_size; |
235 | 243 | ||
244 | extern int latency_format; | ||
245 | |||
236 | int parse_header_page(char *buf, unsigned long size); | 246 | int parse_header_page(char *buf, unsigned long size); |
237 | int trace_parse_common_type(void *data); | 247 | int trace_parse_common_type(void *data); |
248 | int trace_parse_common_pid(void *data); | ||
249 | int parse_common_pc(void *data); | ||
250 | int parse_common_flags(void *data); | ||
251 | int parse_common_lock_depth(void *data); | ||
238 | struct event *trace_find_event(int id); | 252 | struct event *trace_find_event(int id); |
253 | struct event *trace_find_next_event(struct event *event); | ||
254 | unsigned long long read_size(void *ptr, int size); | ||
239 | unsigned long long | 255 | unsigned long long |
240 | raw_field_value(struct event *event, const char *name, void *data); | 256 | raw_field_value(struct event *event, const char *name, void *data); |
241 | void *raw_field_ptr(struct event *event, const char *name, void *data); | 257 | void *raw_field_ptr(struct event *event, const char *name, void *data); |
258 | unsigned long long eval_flag(const char *flag); | ||
259 | |||
260 | int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events); | ||
261 | |||
262 | /* taken from kernel/trace/trace.h */ | ||
263 | enum trace_flag_type { | ||
264 | TRACE_FLAG_IRQS_OFF = 0x01, | ||
265 | TRACE_FLAG_IRQS_NOSUPPORT = 0x02, | ||
266 | TRACE_FLAG_NEED_RESCHED = 0x04, | ||
267 | TRACE_FLAG_HARDIRQ = 0x08, | ||
268 | TRACE_FLAG_SOFTIRQ = 0x10, | ||
269 | }; | ||
270 | |||
271 | struct scripting_ops { | ||
272 | const char *name; | ||
273 | int (*start_script) (const char *script, int argc, const char **argv); | ||
274 | int (*stop_script) (void); | ||
275 | void (*process_event) (int cpu, void *data, int size, | ||
276 | unsigned long long nsecs, char *comm); | ||
277 | int (*generate_script) (const char *outfile); | ||
278 | }; | ||
279 | |||
280 | int script_spec_register(const char *spec, struct scripting_ops *ops); | ||
281 | |||
282 | void setup_perl_scripting(void); | ||
283 | void setup_python_scripting(void); | ||
284 | |||
285 | struct scripting_context { | ||
286 | void *event_data; | ||
287 | }; | ||
242 | 288 | ||
243 | void read_tracing_data(struct perf_event_attr *pattrs, int nb_events); | 289 | int common_pc(struct scripting_context *context); |
290 | int common_flags(struct scripting_context *context); | ||
291 | int common_lock_depth(struct scripting_context *context); | ||
244 | 292 | ||
245 | #endif /* _TRACE_EVENTS_H */ | 293 | #endif /* __PERF_TRACE_EVENTS_H */ |
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h index 5e75f9005940..7d6b8331f898 100644 --- a/tools/perf/util/types.h +++ b/tools/perf/util/types.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _PERF_TYPES_H | 1 | #ifndef __PERF_TYPES_H |
2 | #define _PERF_TYPES_H | 2 | #define __PERF_TYPES_H |
3 | 3 | ||
4 | /* | 4 | /* |
5 | * We define u64 as unsigned long long for every architecture | 5 | * We define u64 as unsigned long long for every architecture |
@@ -14,4 +14,4 @@ typedef signed short s16; | |||
14 | typedef unsigned char u8; | 14 | typedef unsigned char u8; |
15 | typedef signed char s8; | 15 | typedef signed char s8; |
16 | 16 | ||
17 | #endif /* _PERF_TYPES_H */ | 17 | #endif /* __PERF_TYPES_H */ |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c new file mode 100644 index 000000000000..f9b890fde681 --- /dev/null +++ b/tools/perf/util/util.c | |||
@@ -0,0 +1,94 @@ | |||
1 | #include "util.h" | ||
2 | #include <sys/mman.h> | ||
3 | |||
4 | int mkdir_p(char *path, mode_t mode) | ||
5 | { | ||
6 | struct stat st; | ||
7 | int err; | ||
8 | char *d = path; | ||
9 | |||
10 | if (*d != '/') | ||
11 | return -1; | ||
12 | |||
13 | if (stat(path, &st) == 0) | ||
14 | return 0; | ||
15 | |||
16 | while (*++d == '/'); | ||
17 | |||
18 | while ((d = strchr(d, '/'))) { | ||
19 | *d = '\0'; | ||
20 | err = stat(path, &st) && mkdir(path, mode); | ||
21 | *d++ = '/'; | ||
22 | if (err) | ||
23 | return -1; | ||
24 | while (*d == '/') | ||
25 | ++d; | ||
26 | } | ||
27 | return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; | ||
28 | } | ||
29 | |||
30 | static int slow_copyfile(const char *from, const char *to) | ||
31 | { | ||
32 | int err = 0; | ||
33 | char *line = NULL; | ||
34 | size_t n; | ||
35 | FILE *from_fp = fopen(from, "r"), *to_fp; | ||
36 | |||
37 | if (from_fp == NULL) | ||
38 | goto out; | ||
39 | |||
40 | to_fp = fopen(to, "w"); | ||
41 | if (to_fp == NULL) | ||
42 | goto out_fclose_from; | ||
43 | |||
44 | while (getline(&line, &n, from_fp) > 0) | ||
45 | if (fputs(line, to_fp) == EOF) | ||
46 | goto out_fclose_to; | ||
47 | err = 0; | ||
48 | out_fclose_to: | ||
49 | fclose(to_fp); | ||
50 | free(line); | ||
51 | out_fclose_from: | ||
52 | fclose(from_fp); | ||
53 | out: | ||
54 | return err; | ||
55 | } | ||
56 | |||
57 | int copyfile(const char *from, const char *to) | ||
58 | { | ||
59 | int fromfd, tofd; | ||
60 | struct stat st; | ||
61 | void *addr; | ||
62 | int err = -1; | ||
63 | |||
64 | if (stat(from, &st)) | ||
65 | goto out; | ||
66 | |||
67 | if (st.st_size == 0) /* /proc? do it slowly... */ | ||
68 | return slow_copyfile(from, to); | ||
69 | |||
70 | fromfd = open(from, O_RDONLY); | ||
71 | if (fromfd < 0) | ||
72 | goto out; | ||
73 | |||
74 | tofd = creat(to, 0755); | ||
75 | if (tofd < 0) | ||
76 | goto out_close_from; | ||
77 | |||
78 | addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0); | ||
79 | if (addr == MAP_FAILED) | ||
80 | goto out_close_to; | ||
81 | |||
82 | if (write(tofd, addr, st.st_size) == st.st_size) | ||
83 | err = 0; | ||
84 | |||
85 | munmap(addr, st.st_size); | ||
86 | out_close_to: | ||
87 | close(tofd); | ||
88 | if (err) | ||
89 | unlink(to); | ||
90 | out_close_from: | ||
91 | close(fromfd); | ||
92 | out: | ||
93 | return err; | ||
94 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 9de2329dd44d..0f5b2a6f1080 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -84,6 +84,9 @@ | |||
84 | #include <iconv.h> | 84 | #include <iconv.h> |
85 | #endif | 85 | #endif |
86 | 86 | ||
87 | extern const char *graph_line; | ||
88 | extern const char *graph_dotted_line; | ||
89 | |||
87 | /* On most systems <limits.h> would have given us this, but | 90 | /* On most systems <limits.h> would have given us this, but |
88 | * not on some systems (e.g. GNU/Hurd). | 91 | * not on some systems (e.g. GNU/Hurd). |
89 | */ | 92 | */ |
@@ -134,6 +137,15 @@ extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, | |||
134 | extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); | 137 | extern int error(const char *err, ...) __attribute__((format (printf, 1, 2))); |
135 | extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); | 138 | extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))); |
136 | 139 | ||
140 | #include "../../../include/linux/stringify.h" | ||
141 | |||
142 | #define DIE_IF(cnd) \ | ||
143 | do { if (cnd) \ | ||
144 | die(" at (" __FILE__ ":" __stringify(__LINE__) "): " \ | ||
145 | __stringify(cnd) "\n"); \ | ||
146 | } while (0) | ||
147 | |||
148 | |||
137 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | 149 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); |
138 | 150 | ||
139 | extern int prefixcmp(const char *str, const char *prefix); | 151 | extern int prefixcmp(const char *str, const char *prefix); |
@@ -278,17 +290,15 @@ static inline char *gitstrchrnul(const char *s, int c) | |||
278 | * Wrappers: | 290 | * Wrappers: |
279 | */ | 291 | */ |
280 | extern char *xstrdup(const char *str); | 292 | extern char *xstrdup(const char *str); |
281 | extern void *xmalloc(size_t size); | 293 | extern void *xmalloc(size_t size) __attribute__((weak)); |
282 | extern void *xmemdupz(const void *data, size_t len); | 294 | extern void *xmemdupz(const void *data, size_t len); |
283 | extern char *xstrndup(const char *str, size_t len); | 295 | extern char *xstrndup(const char *str, size_t len); |
284 | extern void *xrealloc(void *ptr, size_t size); | 296 | extern void *xrealloc(void *ptr, size_t size) __attribute__((weak)); |
285 | extern void *xcalloc(size_t nmemb, size_t size); | 297 | |
286 | extern void *xmmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); | 298 | static inline void *zalloc(size_t size) |
287 | extern ssize_t xread(int fd, void *buf, size_t len); | 299 | { |
288 | extern ssize_t xwrite(int fd, const void *buf, size_t len); | 300 | return calloc(1, size); |
289 | extern int xdup(int fd); | 301 | } |
290 | extern FILE *xfdopen(int fd, const char *mode); | ||
291 | extern int xmkstemp(char *template); | ||
292 | 302 | ||
293 | static inline size_t xsize_t(off_t len) | 303 | static inline size_t xsize_t(off_t len) |
294 | { | 304 | { |
@@ -306,6 +316,7 @@ static inline int has_extension(const char *filename, const char *ext) | |||
306 | #undef isascii | 316 | #undef isascii |
307 | #undef isspace | 317 | #undef isspace |
308 | #undef isdigit | 318 | #undef isdigit |
319 | #undef isxdigit | ||
309 | #undef isalpha | 320 | #undef isalpha |
310 | #undef isprint | 321 | #undef isprint |
311 | #undef isalnum | 322 | #undef isalnum |
@@ -323,6 +334,8 @@ extern unsigned char sane_ctype[256]; | |||
323 | #define isascii(x) (((x) & ~0x7f) == 0) | 334 | #define isascii(x) (((x) & ~0x7f) == 0) |
324 | #define isspace(x) sane_istest(x,GIT_SPACE) | 335 | #define isspace(x) sane_istest(x,GIT_SPACE) |
325 | #define isdigit(x) sane_istest(x,GIT_DIGIT) | 336 | #define isdigit(x) sane_istest(x,GIT_DIGIT) |
337 | #define isxdigit(x) \ | ||
338 | (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G') | ||
326 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | 339 | #define isalpha(x) sane_istest(x,GIT_ALPHA) |
327 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | 340 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) |
328 | #define isprint(x) sane_istest(x,GIT_PRINT) | 341 | #define isprint(x) sane_istest(x,GIT_PRINT) |
@@ -390,4 +403,7 @@ void git_qsort(void *base, size_t nmemb, size_t size, | |||
390 | #endif | 403 | #endif |
391 | #endif | 404 | #endif |
392 | 405 | ||
406 | int mkdir_p(char *path, mode_t mode); | ||
407 | int copyfile(const char *from, const char *to); | ||
408 | |||
393 | #endif | 409 | #endif |
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 1c15e39f99e3..cfa55d686e3b 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c | |||
@@ -169,6 +169,7 @@ static void perf_read_values__display_pretty(FILE *fp, | |||
169 | counterwidth[j], values->value[i][j]); | 169 | counterwidth[j], values->value[i][j]); |
170 | fprintf(fp, "\n"); | 170 | fprintf(fp, "\n"); |
171 | } | 171 | } |
172 | free(counterwidth); | ||
172 | } | 173 | } |
173 | 174 | ||
174 | static void perf_read_values__display_raw(FILE *fp, | 175 | static void perf_read_values__display_raw(FILE *fp, |
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h index cadf8cf2a590..2fa967e1a88a 100644 --- a/tools/perf/util/values.h +++ b/tools/perf/util/values.h | |||
@@ -1,5 +1,5 @@ | |||
1 | #ifndef _PERF_VALUES_H | 1 | #ifndef __PERF_VALUES_H |
2 | #define _PERF_VALUES_H | 2 | #define __PERF_VALUES_H |
3 | 3 | ||
4 | #include "types.h" | 4 | #include "types.h" |
5 | 5 | ||
@@ -24,4 +24,4 @@ void perf_read_values_add_value(struct perf_read_values *values, | |||
24 | void perf_read_values_display(FILE *fp, struct perf_read_values *values, | 24 | void perf_read_values_display(FILE *fp, struct perf_read_values *values, |
25 | int raw); | 25 | int raw); |
26 | 26 | ||
27 | #endif /* _PERF_VALUES_H */ | 27 | #endif /* __PERF_VALUES_H */ |
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c index 4574ac28396f..bf44ca85d23b 100644 --- a/tools/perf/util/wrapper.c +++ b/tools/perf/util/wrapper.c | |||
@@ -79,43 +79,12 @@ void *xrealloc(void *ptr, size_t size) | |||
79 | return ret; | 79 | return ret; |
80 | } | 80 | } |
81 | 81 | ||
82 | void *xcalloc(size_t nmemb, size_t size) | ||
83 | { | ||
84 | void *ret = calloc(nmemb, size); | ||
85 | if (!ret && (!nmemb || !size)) | ||
86 | ret = calloc(1, 1); | ||
87 | if (!ret) { | ||
88 | release_pack_memory(nmemb * size, -1); | ||
89 | ret = calloc(nmemb, size); | ||
90 | if (!ret && (!nmemb || !size)) | ||
91 | ret = calloc(1, 1); | ||
92 | if (!ret) | ||
93 | die("Out of memory, calloc failed"); | ||
94 | } | ||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | void *xmmap(void *start, size_t length, | ||
99 | int prot, int flags, int fd, off_t offset) | ||
100 | { | ||
101 | void *ret = mmap(start, length, prot, flags, fd, offset); | ||
102 | if (ret == MAP_FAILED) { | ||
103 | if (!length) | ||
104 | return NULL; | ||
105 | release_pack_memory(length, fd); | ||
106 | ret = mmap(start, length, prot, flags, fd, offset); | ||
107 | if (ret == MAP_FAILED) | ||
108 | die("Out of memory? mmap failed: %s", strerror(errno)); | ||
109 | } | ||
110 | return ret; | ||
111 | } | ||
112 | |||
113 | /* | 82 | /* |
114 | * xread() is the same a read(), but it automatically restarts read() | 83 | * xread() is the same a read(), but it automatically restarts read() |
115 | * operations with a recoverable error (EAGAIN and EINTR). xread() | 84 | * operations with a recoverable error (EAGAIN and EINTR). xread() |
116 | * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. | 85 | * DOES NOT GUARANTEE that "len" bytes is read even if the data is available. |
117 | */ | 86 | */ |
118 | ssize_t xread(int fd, void *buf, size_t len) | 87 | static ssize_t xread(int fd, void *buf, size_t len) |
119 | { | 88 | { |
120 | ssize_t nr; | 89 | ssize_t nr; |
121 | while (1) { | 90 | while (1) { |
@@ -131,7 +100,7 @@ ssize_t xread(int fd, void *buf, size_t len) | |||
131 | * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT | 100 | * operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT |
132 | * GUARANTEE that "len" bytes is written even if the operation is successful. | 101 | * GUARANTEE that "len" bytes is written even if the operation is successful. |
133 | */ | 102 | */ |
134 | ssize_t xwrite(int fd, const void *buf, size_t len) | 103 | static ssize_t xwrite(int fd, const void *buf, size_t len) |
135 | { | 104 | { |
136 | ssize_t nr; | 105 | ssize_t nr; |
137 | while (1) { | 106 | while (1) { |
@@ -179,29 +148,3 @@ ssize_t write_in_full(int fd, const void *buf, size_t count) | |||
179 | 148 | ||
180 | return total; | 149 | return total; |
181 | } | 150 | } |
182 | |||
183 | int xdup(int fd) | ||
184 | { | ||
185 | int ret = dup(fd); | ||
186 | if (ret < 0) | ||
187 | die("dup failed: %s", strerror(errno)); | ||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | FILE *xfdopen(int fd, const char *mode) | ||
192 | { | ||
193 | FILE *stream = fdopen(fd, mode); | ||
194 | if (stream == NULL) | ||
195 | die("Out of memory? fdopen failed: %s", strerror(errno)); | ||
196 | return stream; | ||
197 | } | ||
198 | |||
199 | int xmkstemp(char *template) | ||
200 | { | ||
201 | int fd; | ||
202 | |||
203 | fd = mkstemp(template); | ||
204 | if (fd < 0) | ||
205 | die("Unable to create temporary file: %s", strerror(errno)); | ||
206 | return fd; | ||
207 | } | ||