diff options
Diffstat (limited to 'tools/perf')
41 files changed, 2413 insertions, 567 deletions
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-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-report.txt b/tools/perf/Documentation/perf-report.txt index 59f0b846cd71..9dccb180b7af 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=:: |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 147e3cf035d3..3dbb5c5bb8c6 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -177,8 +177,7 @@ endif | |||
177 | # Include saner warnings here, which can catch bugs: | 177 | # Include saner warnings here, which can catch bugs: |
178 | # | 178 | # |
179 | 179 | ||
180 | EXTRA_WARNINGS := -Wcast-align | 180 | EXTRA_WARNINGS := -Wformat |
181 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat | ||
182 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security | 181 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security |
183 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k | 182 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k |
184 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow | 183 | EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow |
@@ -208,7 +207,7 @@ ifndef PERF_DEBUG | |||
208 | CFLAGS_OPTIMIZE = -O6 | 207 | CFLAGS_OPTIMIZE = -O6 |
209 | endif | 208 | endif |
210 | 209 | ||
211 | CFLAGS = $(MBITS) -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -fstack-protector-all -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) | 210 | CFLAGS = $(MBITS) -ggdb3 -Wall -Wextra -std=gnu99 -Werror $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) |
212 | LDFLAGS = -lpthread -lrt -lelf -lm | 211 | LDFLAGS = -lpthread -lrt -lelf -lm |
213 | ALL_CFLAGS = $(CFLAGS) | 212 | ALL_CFLAGS = $(CFLAGS) |
214 | ALL_LDFLAGS = $(LDFLAGS) | 213 | ALL_LDFLAGS = $(LDFLAGS) |
@@ -260,6 +259,9 @@ PTHREAD_LIBS = -lpthread | |||
260 | # explicitly what architecture to check for. Fix this up for yours.. | 259 | # explicitly what architecture to check for. Fix this up for yours.. |
261 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ | 260 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ |
262 | 261 | ||
262 | ifeq ($(shell sh -c "echo 'int foo(void) {char X[2]; return 3;}' | $(CC) -x c -c -Werror -fstack-protector-all - -o /dev/null >/dev/null 2>&1 && echo y"), y) | ||
263 | CFLAGS := $(CFLAGS) -fstack-protector-all | ||
264 | endif | ||
263 | 265 | ||
264 | 266 | ||
265 | ### --- END CONFIGURATION SECTION --- | 267 | ### --- END CONFIGURATION SECTION --- |
@@ -355,6 +357,7 @@ LIB_H += util/include/asm/swab.h | |||
355 | LIB_H += util/include/asm/system.h | 357 | LIB_H += util/include/asm/system.h |
356 | LIB_H += util/include/asm/uaccess.h | 358 | LIB_H += util/include/asm/uaccess.h |
357 | LIB_H += perf.h | 359 | LIB_H += perf.h |
360 | LIB_H += util/debugfs.h | ||
358 | LIB_H += util/event.h | 361 | LIB_H += util/event.h |
359 | LIB_H += util/types.h | 362 | LIB_H += util/types.h |
360 | LIB_H += util/levenshtein.h | 363 | LIB_H += util/levenshtein.h |
@@ -380,7 +383,9 @@ LIB_OBJS += util/abspath.o | |||
380 | LIB_OBJS += util/alias.o | 383 | LIB_OBJS += util/alias.o |
381 | LIB_OBJS += util/config.o | 384 | LIB_OBJS += util/config.o |
382 | LIB_OBJS += util/ctype.o | 385 | LIB_OBJS += util/ctype.o |
386 | LIB_OBJS += util/debugfs.o | ||
383 | LIB_OBJS += util/environment.o | 387 | LIB_OBJS += util/environment.o |
388 | LIB_OBJS += util/event.o | ||
384 | LIB_OBJS += util/exec_cmd.o | 389 | LIB_OBJS += util/exec_cmd.o |
385 | LIB_OBJS += util/help.o | 390 | LIB_OBJS += util/help.o |
386 | LIB_OBJS += util/levenshtein.o | 391 | LIB_OBJS += util/levenshtein.o |
@@ -417,8 +422,16 @@ LIB_OBJS += util/hist.o | |||
417 | LIB_OBJS += util/data_map.o | 422 | LIB_OBJS += util/data_map.o |
418 | 423 | ||
419 | BUILTIN_OBJS += builtin-annotate.o | 424 | BUILTIN_OBJS += builtin-annotate.o |
425 | |||
426 | BUILTIN_OBJS += builtin-bench.o | ||
427 | |||
428 | # Benchmark modules | ||
429 | BUILTIN_OBJS += bench/sched-messaging.o | ||
430 | BUILTIN_OBJS += bench/sched-pipe.o | ||
431 | |||
420 | BUILTIN_OBJS += builtin-help.o | 432 | BUILTIN_OBJS += builtin-help.o |
421 | BUILTIN_OBJS += builtin-sched.o | 433 | BUILTIN_OBJS += builtin-sched.o |
434 | BUILTIN_OBJS += builtin-buildid-list.o | ||
422 | BUILTIN_OBJS += builtin-list.o | 435 | BUILTIN_OBJS += builtin-list.o |
423 | BUILTIN_OBJS += builtin-record.o | 436 | BUILTIN_OBJS += builtin-record.o |
424 | BUILTIN_OBJS += builtin-report.o | 437 | BUILTIN_OBJS += builtin-report.o |
@@ -457,12 +470,16 @@ ifeq ($(uname_S),Darwin) | |||
457 | PTHREAD_LIBS = | 470 | PTHREAD_LIBS = |
458 | endif | 471 | endif |
459 | 472 | ||
473 | 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) | ||
460 | 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 /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) | 474 | 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 /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) |
461 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]); | 475 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]); |
462 | endif | 476 | endif |
463 | 477 | ||
464 | 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) | 478 | 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) |
465 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel); | 479 | BASIC_CFLAGS += -DLIBELF_NO_MMAP |
480 | endif | ||
481 | else | ||
482 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]); | ||
466 | endif | 483 | endif |
467 | 484 | ||
468 | ifneq ($(shell sh -c "(echo '\#include <libdwarf/dwarf.h>'; echo '\#include <libdwarf/libdwarf.h>'; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; Dwarf_Ranges *rng; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); dwarf_get_ranges(dbg, 0, &rng, 0, 0, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -ldwarf -lelf -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) | 485 | ifneq ($(shell sh -c "(echo '\#include <libdwarf/dwarf.h>'; echo '\#include <libdwarf/libdwarf.h>'; echo 'int main(void) { Dwarf_Debug dbg; Dwarf_Error err; Dwarf_Ranges *rng; dwarf_init(0, DW_DLC_READ, 0, 0, &dbg, &err); dwarf_get_ranges(dbg, 0, &rng, 0, 0, &err); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -ldwarf -lelf -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&1 && echo y"), y) |
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h new file mode 100644 index 000000000000..9fbd8d745fa1 --- /dev/null +++ b/tools/perf/bench/bench.h | |||
@@ -0,0 +1,16 @@ | |||
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 | |||
7 | #define BENCH_FORMAT_DEFAULT_STR "default" | ||
8 | #define BENCH_FORMAT_DEFAULT 0 | ||
9 | #define BENCH_FORMAT_SIMPLE_STR "simple" | ||
10 | #define BENCH_FORMAT_SIMPLE 1 | ||
11 | |||
12 | #define BENCH_FORMAT_UNKNOWN -1 | ||
13 | |||
14 | extern int bench_format; | ||
15 | |||
16 | #endif | ||
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c new file mode 100644 index 000000000000..605a2a959aa8 --- /dev/null +++ b/tools/perf/bench/sched-messaging.c | |||
@@ -0,0 +1,336 @@ | |||
1 | /* | ||
2 | * | ||
3 | * builtin-bench-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, diff.tv_usec/1000); | ||
324 | break; | ||
325 | case BENCH_FORMAT_SIMPLE: | ||
326 | printf("%lu.%03lu\n", diff.tv_sec, diff.tv_usec/1000); | ||
327 | break; | ||
328 | default: | ||
329 | /* reaching here is something disaster */ | ||
330 | fprintf(stderr, "Unknown format:%d\n", bench_format); | ||
331 | exit(1); | ||
332 | break; | ||
333 | } | ||
334 | |||
335 | return 0; | ||
336 | } | ||
diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c new file mode 100644 index 000000000000..238185f97977 --- /dev/null +++ b/tools/perf/bench/sched-pipe.c | |||
@@ -0,0 +1,124 @@ | |||
1 | /* | ||
2 | * | ||
3 | * builtin-bench-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 | return 0; | ||
91 | } | ||
92 | |||
93 | switch (bench_format) { | ||
94 | case BENCH_FORMAT_DEFAULT: | ||
95 | printf("# Extecuted %d pipe operations between two tasks\n\n", | ||
96 | loops); | ||
97 | |||
98 | result_usec = diff.tv_sec * 1000000; | ||
99 | result_usec += diff.tv_usec; | ||
100 | |||
101 | printf(" %14s: %lu.%03lu [sec]\n\n", "Total time", | ||
102 | diff.tv_sec, diff.tv_usec/1000); | ||
103 | |||
104 | printf(" %14lf usecs/op\n", | ||
105 | (double)result_usec / (double)loops); | ||
106 | printf(" %14d ops/sec\n", | ||
107 | (int)((double)loops / | ||
108 | ((double)result_usec / (double)1000000))); | ||
109 | break; | ||
110 | |||
111 | case BENCH_FORMAT_SIMPLE: | ||
112 | printf("%lu.%03lu\n", | ||
113 | diff.tv_sec, diff.tv_usec / 1000); | ||
114 | break; | ||
115 | |||
116 | default: | ||
117 | /* reaching here is something disaster */ | ||
118 | fprintf(stderr, "Unknown format:%d\n", bench_format); | ||
119 | exit(1); | ||
120 | break; | ||
121 | } | ||
122 | |||
123 | return 0; | ||
124 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 6d63c2eea2c7..77d50a6d6802 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -55,11 +55,11 @@ struct sym_priv { | |||
55 | 55 | ||
56 | static const char *sym_hist_filter; | 56 | static const char *sym_hist_filter; |
57 | 57 | ||
58 | static int symbol_filter(struct map *map, struct symbol *sym) | 58 | static int symbol_filter(struct map *map __used, struct symbol *sym) |
59 | { | 59 | { |
60 | if (sym_hist_filter == NULL || | 60 | if (sym_hist_filter == NULL || |
61 | strcmp(sym->name, sym_hist_filter) == 0) { | 61 | strcmp(sym->name, sym_hist_filter) == 0) { |
62 | struct sym_priv *priv = dso__sym_priv(map->dso, sym); | 62 | struct sym_priv *priv = symbol__priv(sym); |
63 | const int size = (sizeof(*priv->hist) + | 63 | const int size = (sizeof(*priv->hist) + |
64 | (sym->end - sym->start) * sizeof(u64)); | 64 | (sym->end - sym->start) * sizeof(u64)); |
65 | 65 | ||
@@ -92,7 +92,7 @@ static void hist_hit(struct hist_entry *he, u64 ip) | |||
92 | if (!sym || !he->map) | 92 | if (!sym || !he->map) |
93 | return; | 93 | return; |
94 | 94 | ||
95 | priv = dso__sym_priv(he->map->dso, sym); | 95 | priv = symbol__priv(sym); |
96 | if (!priv->hist) | 96 | if (!priv->hist) |
97 | return; | 97 | return; |
98 | 98 | ||
@@ -165,7 +165,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
165 | if (map != NULL) { | 165 | if (map != NULL) { |
166 | got_map: | 166 | got_map: |
167 | ip = map->map_ip(map, ip); | 167 | ip = map->map_ip(map, ip); |
168 | sym = map->dso->find_symbol(map->dso, ip); | 168 | sym = map__find_symbol(map, ip, symbol_filter); |
169 | } else { | 169 | } else { |
170 | /* | 170 | /* |
171 | * If this is outside of all known maps, | 171 | * If this is outside of all known maps, |
@@ -202,8 +202,7 @@ got_map: | |||
202 | static int | 202 | static int |
203 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | 203 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) |
204 | { | 204 | { |
205 | struct map *map = map__new(&event->mmap, NULL, 0, | 205 | struct map *map = map__new(&event->mmap, NULL, 0); |
206 | sizeof(struct sym_priv), symbol_filter); | ||
207 | struct thread *thread = threads__findnew(event->mmap.pid); | 206 | struct thread *thread = threads__findnew(event->mmap.pid); |
208 | 207 | ||
209 | dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n", | 208 | dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n", |
@@ -355,7 +354,7 @@ static int parse_line(FILE *file, struct hist_entry *he, u64 len) | |||
355 | unsigned int hits = 0; | 354 | unsigned int hits = 0; |
356 | double percent = 0.0; | 355 | double percent = 0.0; |
357 | const char *color; | 356 | const char *color; |
358 | struct sym_priv *priv = dso__sym_priv(he->map->dso, sym); | 357 | struct sym_priv *priv = symbol__priv(sym); |
359 | struct sym_ext *sym_ext = priv->ext; | 358 | struct sym_ext *sym_ext = priv->ext; |
360 | struct sym_hist *h = priv->hist; | 359 | struct sym_hist *h = priv->hist; |
361 | 360 | ||
@@ -422,7 +421,7 @@ static void insert_source_line(struct sym_ext *sym_ext) | |||
422 | 421 | ||
423 | static void free_source_line(struct hist_entry *he, int len) | 422 | static void free_source_line(struct hist_entry *he, int len) |
424 | { | 423 | { |
425 | struct sym_priv *priv = dso__sym_priv(he->map->dso, he->sym); | 424 | struct sym_priv *priv = symbol__priv(he->sym); |
426 | struct sym_ext *sym_ext = priv->ext; | 425 | struct sym_ext *sym_ext = priv->ext; |
427 | int i; | 426 | int i; |
428 | 427 | ||
@@ -446,7 +445,7 @@ get_source_line(struct hist_entry *he, int len, const char *filename) | |||
446 | int i; | 445 | int i; |
447 | char cmd[PATH_MAX * 2]; | 446 | char cmd[PATH_MAX * 2]; |
448 | struct sym_ext *sym_ext; | 447 | struct sym_ext *sym_ext; |
449 | struct sym_priv *priv = dso__sym_priv(he->map->dso, sym); | 448 | struct sym_priv *priv = symbol__priv(sym); |
450 | struct sym_hist *h = priv->hist; | 449 | struct sym_hist *h = priv->hist; |
451 | 450 | ||
452 | if (!h->sum) | 451 | if (!h->sum) |
@@ -589,7 +588,7 @@ static void find_annotations(void) | |||
589 | if (he->sym == NULL) | 588 | if (he->sym == NULL) |
590 | continue; | 589 | continue; |
591 | 590 | ||
592 | priv = dso__sym_priv(he->map->dso, he->sym); | 591 | priv = symbol__priv(he->sym); |
593 | if (priv->hist == NULL) | 592 | if (priv->hist == NULL) |
594 | continue; | 593 | continue; |
595 | 594 | ||
@@ -637,7 +636,7 @@ static int __cmd_annotate(void) | |||
637 | exit(0); | 636 | exit(0); |
638 | } | 637 | } |
639 | 638 | ||
640 | if (load_kernel(sizeof(struct sym_priv), symbol_filter) < 0) { | 639 | if (load_kernel(symbol_filter) < 0) { |
641 | perror("failed to load kernel symbols"); | 640 | perror("failed to load kernel symbols"); |
642 | return EXIT_FAILURE; | 641 | return EXIT_FAILURE; |
643 | } | 642 | } |
@@ -769,7 +768,7 @@ static void setup_sorting(void) | |||
769 | 768 | ||
770 | int cmd_annotate(int argc, const char **argv, const char *prefix __used) | 769 | int cmd_annotate(int argc, const char **argv, const char *prefix __used) |
771 | { | 770 | { |
772 | symbol__init(); | 771 | symbol__init(sizeof(struct sym_priv)); |
773 | 772 | ||
774 | page_size = getpagesize(); | 773 | page_size = getpagesize(); |
775 | 774 | ||
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c new file mode 100644 index 000000000000..90c39baae0de --- /dev/null +++ b/tools/perf/builtin-bench.c | |||
@@ -0,0 +1,183 @@ | |||
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 | * | ||
16 | */ | ||
17 | |||
18 | #include "perf.h" | ||
19 | #include "util/util.h" | ||
20 | #include "util/parse-options.h" | ||
21 | #include "builtin.h" | ||
22 | #include "bench/bench.h" | ||
23 | |||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | |||
28 | struct bench_suite { | ||
29 | const char *name; | ||
30 | const char *summary; | ||
31 | int (*fn)(int, const char **, const char *); | ||
32 | }; | ||
33 | |||
34 | static struct bench_suite sched_suites[] = { | ||
35 | { "messaging", | ||
36 | "Benchmark for scheduler and IPC mechanisms", | ||
37 | bench_sched_messaging }, | ||
38 | { "pipe", | ||
39 | "Flood of communication over pipe() between two processes", | ||
40 | bench_sched_pipe }, | ||
41 | { NULL, | ||
42 | NULL, | ||
43 | NULL } | ||
44 | }; | ||
45 | |||
46 | struct bench_subsys { | ||
47 | const char *name; | ||
48 | const char *summary; | ||
49 | struct bench_suite *suites; | ||
50 | }; | ||
51 | |||
52 | static struct bench_subsys subsystems[] = { | ||
53 | { "sched", | ||
54 | "scheduler and IPC mechanism", | ||
55 | sched_suites }, | ||
56 | { NULL, | ||
57 | NULL, | ||
58 | NULL } | ||
59 | }; | ||
60 | |||
61 | static void dump_suites(int subsys_index) | ||
62 | { | ||
63 | int i; | ||
64 | |||
65 | printf("List of available suites for %s...\n\n", | ||
66 | subsystems[subsys_index].name); | ||
67 | |||
68 | for (i = 0; subsystems[subsys_index].suites[i].name; i++) | ||
69 | printf("\t%s: %s\n", | ||
70 | subsystems[subsys_index].suites[i].name, | ||
71 | subsystems[subsys_index].suites[i].summary); | ||
72 | |||
73 | printf("\n"); | ||
74 | return; | ||
75 | } | ||
76 | |||
77 | static char *bench_format_str; | ||
78 | int bench_format = BENCH_FORMAT_DEFAULT; | ||
79 | |||
80 | static const struct option bench_options[] = { | ||
81 | OPT_STRING('f', "format", &bench_format_str, "default", | ||
82 | "Specify format style"), | ||
83 | OPT_END() | ||
84 | }; | ||
85 | |||
86 | static const char * const bench_usage[] = { | ||
87 | "perf bench [<common options>] <subsystem> <suite> [<options>]", | ||
88 | NULL | ||
89 | }; | ||
90 | |||
91 | static void print_usage(void) | ||
92 | { | ||
93 | int i; | ||
94 | |||
95 | printf("Usage: \n"); | ||
96 | for (i = 0; bench_usage[i]; i++) | ||
97 | printf("\t%s\n", bench_usage[i]); | ||
98 | printf("\n"); | ||
99 | |||
100 | printf("List of available subsystems...\n\n"); | ||
101 | |||
102 | for (i = 0; subsystems[i].name; i++) | ||
103 | printf("\t%s: %s\n", | ||
104 | subsystems[i].name, subsystems[i].summary); | ||
105 | printf("\n"); | ||
106 | } | ||
107 | |||
108 | static int bench_str2int(char *str) | ||
109 | { | ||
110 | if (!str) | ||
111 | return BENCH_FORMAT_DEFAULT; | ||
112 | |||
113 | if (!strcmp(str, BENCH_FORMAT_DEFAULT_STR)) | ||
114 | return BENCH_FORMAT_DEFAULT; | ||
115 | else if (!strcmp(str, BENCH_FORMAT_SIMPLE_STR)) | ||
116 | return BENCH_FORMAT_SIMPLE; | ||
117 | |||
118 | return BENCH_FORMAT_UNKNOWN; | ||
119 | } | ||
120 | |||
121 | int cmd_bench(int argc, const char **argv, const char *prefix __used) | ||
122 | { | ||
123 | int i, j, status = 0; | ||
124 | |||
125 | if (argc < 2) { | ||
126 | /* No subsystem specified. */ | ||
127 | print_usage(); | ||
128 | goto end; | ||
129 | } | ||
130 | |||
131 | argc = parse_options(argc, argv, bench_options, bench_usage, | ||
132 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
133 | |||
134 | bench_format = bench_str2int(bench_format_str); | ||
135 | if (bench_format == BENCH_FORMAT_UNKNOWN) { | ||
136 | printf("Unknown format descriptor:%s\n", bench_format_str); | ||
137 | goto end; | ||
138 | } | ||
139 | |||
140 | if (argc < 1) { | ||
141 | print_usage(); | ||
142 | goto end; | ||
143 | } | ||
144 | |||
145 | for (i = 0; subsystems[i].name; i++) { | ||
146 | if (strcmp(subsystems[i].name, argv[0])) | ||
147 | continue; | ||
148 | |||
149 | if (argc < 2) { | ||
150 | /* No suite specified. */ | ||
151 | dump_suites(i); | ||
152 | goto end; | ||
153 | } | ||
154 | |||
155 | for (j = 0; subsystems[i].suites[j].name; j++) { | ||
156 | if (strcmp(subsystems[i].suites[j].name, argv[1])) | ||
157 | continue; | ||
158 | |||
159 | if (bench_format == BENCH_FORMAT_DEFAULT) | ||
160 | printf("# Running %s/%s benchmark...\n", | ||
161 | subsystems[i].name, | ||
162 | subsystems[i].suites[j].name); | ||
163 | status = subsystems[i].suites[j].fn(argc - 1, | ||
164 | argv + 1, prefix); | ||
165 | goto end; | ||
166 | } | ||
167 | |||
168 | if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { | ||
169 | dump_suites(i); | ||
170 | goto end; | ||
171 | } | ||
172 | |||
173 | printf("Unknown suite:%s for %s\n", argv[1], argv[0]); | ||
174 | status = 1; | ||
175 | goto end; | ||
176 | } | ||
177 | |||
178 | printf("Unknown subsystem:%s\n", argv[0]); | ||
179 | status = 1; | ||
180 | |||
181 | end: | ||
182 | return status; | ||
183 | } | ||
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c new file mode 100644 index 000000000000..7dee9d19ab7a --- /dev/null +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -0,0 +1,116 @@ | |||
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/cache.h" | ||
12 | #include "util/data_map.h" | ||
13 | #include "util/debug.h" | ||
14 | #include "util/header.h" | ||
15 | #include "util/parse-options.h" | ||
16 | #include "util/symbol.h" | ||
17 | |||
18 | static char const *input_name = "perf.data"; | ||
19 | static int force; | ||
20 | |||
21 | static const char *const buildid_list_usage[] = { | ||
22 | "perf report [<options>]", | ||
23 | NULL | ||
24 | }; | ||
25 | |||
26 | static const struct option options[] = { | ||
27 | OPT_STRING('i', "input", &input_name, "file", | ||
28 | "input file name"), | ||
29 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
30 | OPT_BOOLEAN('v', "verbose", &verbose, | ||
31 | "be more verbose"), | ||
32 | OPT_END() | ||
33 | }; | ||
34 | |||
35 | static int perf_file_section__process_buildids(struct perf_file_section *self, | ||
36 | int feat, int fd) | ||
37 | { | ||
38 | if (feat != HEADER_BUILD_ID) | ||
39 | return 0; | ||
40 | |||
41 | if (lseek(fd, self->offset, SEEK_SET) < 0) { | ||
42 | pr_warning("Failed to lseek to %Ld offset for buildids!\n", | ||
43 | self->offset); | ||
44 | return -1; | ||
45 | } | ||
46 | |||
47 | if (perf_header__read_build_ids(fd, self->offset, self->size)) { | ||
48 | pr_warning("Failed to read buildids!\n"); | ||
49 | return -1; | ||
50 | } | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int __cmd_buildid_list(void) | ||
56 | { | ||
57 | int err = -1; | ||
58 | struct perf_header *header; | ||
59 | struct perf_file_header f_header; | ||
60 | struct stat input_stat; | ||
61 | int input = open(input_name, O_RDONLY); | ||
62 | |||
63 | if (input < 0) { | ||
64 | pr_err("failed to open file: %s", input_name); | ||
65 | if (!strcmp(input_name, "perf.data")) | ||
66 | pr_err(" (try 'perf record' first)"); | ||
67 | pr_err("\n"); | ||
68 | goto out; | ||
69 | } | ||
70 | |||
71 | err = fstat(input, &input_stat); | ||
72 | if (err < 0) { | ||
73 | perror("failed to stat file"); | ||
74 | goto out_close; | ||
75 | } | ||
76 | |||
77 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
78 | pr_err("file %s not owned by current user or root\n", | ||
79 | input_name); | ||
80 | goto out_close; | ||
81 | } | ||
82 | |||
83 | if (!input_stat.st_size) { | ||
84 | pr_info("zero-sized file, nothing to do!\n"); | ||
85 | goto out_close; | ||
86 | } | ||
87 | |||
88 | err = -1; | ||
89 | header = perf_header__new(); | ||
90 | if (header == NULL) | ||
91 | goto out_close; | ||
92 | |||
93 | if (perf_file_header__read(&f_header, header, input) < 0) { | ||
94 | pr_warning("incompatible file format"); | ||
95 | goto out_close; | ||
96 | } | ||
97 | |||
98 | err = perf_header__process_sections(header, input, | ||
99 | perf_file_section__process_buildids); | ||
100 | |||
101 | if (err < 0) | ||
102 | goto out_close; | ||
103 | |||
104 | dsos__fprintf_buildid(stdout); | ||
105 | out_close: | ||
106 | close(input); | ||
107 | out: | ||
108 | return err; | ||
109 | } | ||
110 | |||
111 | int cmd_buildid_list(int argc, const char **argv, const char *prefix __used) | ||
112 | { | ||
113 | argc = parse_options(argc, argv, options, buildid_list_usage, 0); | ||
114 | setup_pager(); | ||
115 | return __cmd_buildid_list(); | ||
116 | } | ||
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 4fb8734a796e..768f9c826312 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"; |
@@ -364,9 +363,8 @@ static void show_man_page(const char *perf_cmd) | |||
364 | 363 | ||
365 | setup_man_path(); | 364 | setup_man_path(); |
366 | for (viewer = man_viewer_list; viewer; viewer = viewer->next) | 365 | for (viewer = man_viewer_list; viewer; viewer = viewer->next) |
367 | { | ||
368 | exec_viewer(viewer->name, page); /* will return when unable */ | 366 | exec_viewer(viewer->name, page); /* will return when unable */ |
369 | } | 367 | |
370 | if (fallback) | 368 | if (fallback) |
371 | exec_viewer(fallback, page); | 369 | exec_viewer(fallback, page); |
372 | exec_viewer("man", page); | 370 | exec_viewer("man", page); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index ac5ddfff4456..82260c56db3d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "util/header.h" | 17 | #include "util/header.h" |
18 | #include "util/event.h" | 18 | #include "util/event.h" |
19 | #include "util/debug.h" | 19 | #include "util/debug.h" |
20 | #include "util/symbol.h" | ||
20 | 21 | ||
21 | #include <unistd.h> | 22 | #include <unistd.h> |
22 | #include <sched.h> | 23 | #include <sched.h> |
@@ -109,6 +110,24 @@ static void write_output(void *buf, size_t size) | |||
109 | } | 110 | } |
110 | } | 111 | } |
111 | 112 | ||
113 | static void write_event(event_t *buf, size_t size) | ||
114 | { | ||
115 | /* | ||
116 | * Add it to the list of DSOs, so that when we finish this | ||
117 | * record session we can pick the available build-ids. | ||
118 | */ | ||
119 | if (buf->header.type == PERF_RECORD_MMAP) | ||
120 | dsos__findnew(buf->mmap.filename); | ||
121 | |||
122 | write_output(buf, size); | ||
123 | } | ||
124 | |||
125 | static int process_synthesized_event(event_t *event) | ||
126 | { | ||
127 | write_event(event, event->header.size); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
112 | static void mmap_read(struct mmap_data *md) | 131 | static void mmap_read(struct mmap_data *md) |
113 | { | 132 | { |
114 | unsigned int head = mmap_read_head(md); | 133 | unsigned int head = mmap_read_head(md); |
@@ -157,14 +176,14 @@ static void mmap_read(struct mmap_data *md) | |||
157 | size = md->mask + 1 - (old & md->mask); | 176 | size = md->mask + 1 - (old & md->mask); |
158 | old += size; | 177 | old += size; |
159 | 178 | ||
160 | write_output(buf, size); | 179 | write_event(buf, size); |
161 | } | 180 | } |
162 | 181 | ||
163 | buf = &data[old & md->mask]; | 182 | buf = &data[old & md->mask]; |
164 | size = head - old; | 183 | size = head - old; |
165 | old += size; | 184 | old += size; |
166 | 185 | ||
167 | write_output(buf, size); | 186 | write_event(buf, size); |
168 | 187 | ||
169 | md->prev = old; | 188 | md->prev = old; |
170 | mmap_write_tail(md, old); | 189 | mmap_write_tail(md, old); |
@@ -191,168 +210,6 @@ static void sig_atexit(void) | |||
191 | kill(getpid(), signr); | 210 | kill(getpid(), signr); |
192 | } | 211 | } |
193 | 212 | ||
194 | static pid_t pid_synthesize_comm_event(pid_t pid, int full) | ||
195 | { | ||
196 | struct comm_event comm_ev; | ||
197 | char filename[PATH_MAX]; | ||
198 | char bf[BUFSIZ]; | ||
199 | FILE *fp; | ||
200 | size_t size = 0; | ||
201 | DIR *tasks; | ||
202 | struct dirent dirent, *next; | ||
203 | pid_t tgid = 0; | ||
204 | |||
205 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); | ||
206 | |||
207 | fp = fopen(filename, "r"); | ||
208 | if (fp == NULL) { | ||
209 | /* | ||
210 | * We raced with a task exiting - just return: | ||
211 | */ | ||
212 | if (verbose) | ||
213 | fprintf(stderr, "couldn't open %s\n", filename); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | memset(&comm_ev, 0, sizeof(comm_ev)); | ||
218 | while (!comm_ev.comm[0] || !comm_ev.pid) { | ||
219 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
220 | goto out_failure; | ||
221 | |||
222 | if (memcmp(bf, "Name:", 5) == 0) { | ||
223 | char *name = bf + 5; | ||
224 | while (*name && isspace(*name)) | ||
225 | ++name; | ||
226 | size = strlen(name) - 1; | ||
227 | memcpy(comm_ev.comm, name, size++); | ||
228 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
229 | char *tgids = bf + 5; | ||
230 | while (*tgids && isspace(*tgids)) | ||
231 | ++tgids; | ||
232 | tgid = comm_ev.pid = atoi(tgids); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | comm_ev.header.type = PERF_RECORD_COMM; | ||
237 | size = ALIGN(size, sizeof(u64)); | ||
238 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); | ||
239 | |||
240 | if (!full) { | ||
241 | comm_ev.tid = pid; | ||
242 | |||
243 | write_output(&comm_ev, comm_ev.header.size); | ||
244 | goto out_fclose; | ||
245 | } | ||
246 | |||
247 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | ||
248 | |||
249 | tasks = opendir(filename); | ||
250 | while (!readdir_r(tasks, &dirent, &next) && next) { | ||
251 | char *end; | ||
252 | pid = strtol(dirent.d_name, &end, 10); | ||
253 | if (*end) | ||
254 | continue; | ||
255 | |||
256 | comm_ev.tid = pid; | ||
257 | |||
258 | write_output(&comm_ev, comm_ev.header.size); | ||
259 | } | ||
260 | closedir(tasks); | ||
261 | |||
262 | out_fclose: | ||
263 | fclose(fp); | ||
264 | return tgid; | ||
265 | |||
266 | out_failure: | ||
267 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", | ||
268 | filename); | ||
269 | exit(EXIT_FAILURE); | ||
270 | } | ||
271 | |||
272 | static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid) | ||
273 | { | ||
274 | char filename[PATH_MAX]; | ||
275 | FILE *fp; | ||
276 | |||
277 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); | ||
278 | |||
279 | fp = fopen(filename, "r"); | ||
280 | if (fp == NULL) { | ||
281 | /* | ||
282 | * We raced with a task exiting - just return: | ||
283 | */ | ||
284 | if (verbose) | ||
285 | fprintf(stderr, "couldn't open %s\n", filename); | ||
286 | return; | ||
287 | } | ||
288 | while (1) { | ||
289 | char bf[BUFSIZ], *pbf = bf; | ||
290 | struct mmap_event mmap_ev = { | ||
291 | .header = { .type = PERF_RECORD_MMAP }, | ||
292 | }; | ||
293 | int n; | ||
294 | size_t size; | ||
295 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
296 | break; | ||
297 | |||
298 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | ||
299 | n = hex2u64(pbf, &mmap_ev.start); | ||
300 | if (n < 0) | ||
301 | continue; | ||
302 | pbf += n + 1; | ||
303 | n = hex2u64(pbf, &mmap_ev.len); | ||
304 | if (n < 0) | ||
305 | continue; | ||
306 | pbf += n + 3; | ||
307 | if (*pbf == 'x') { /* vm_exec */ | ||
308 | char *execname = strchr(bf, '/'); | ||
309 | |||
310 | /* Catch VDSO */ | ||
311 | if (execname == NULL) | ||
312 | execname = strstr(bf, "[vdso]"); | ||
313 | |||
314 | if (execname == NULL) | ||
315 | continue; | ||
316 | |||
317 | size = strlen(execname); | ||
318 | execname[size - 1] = '\0'; /* Remove \n */ | ||
319 | memcpy(mmap_ev.filename, execname, size); | ||
320 | size = ALIGN(size, sizeof(u64)); | ||
321 | mmap_ev.len -= mmap_ev.start; | ||
322 | mmap_ev.header.size = (sizeof(mmap_ev) - | ||
323 | (sizeof(mmap_ev.filename) - size)); | ||
324 | mmap_ev.pid = tgid; | ||
325 | mmap_ev.tid = pid; | ||
326 | |||
327 | write_output(&mmap_ev, mmap_ev.header.size); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | fclose(fp); | ||
332 | } | ||
333 | |||
334 | static void synthesize_all(void) | ||
335 | { | ||
336 | DIR *proc; | ||
337 | struct dirent dirent, *next; | ||
338 | |||
339 | proc = opendir("/proc"); | ||
340 | |||
341 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
342 | char *end; | ||
343 | pid_t pid, tgid; | ||
344 | |||
345 | pid = strtol(dirent.d_name, &end, 10); | ||
346 | if (*end) /* only interested in proper numerical dirents */ | ||
347 | continue; | ||
348 | |||
349 | tgid = pid_synthesize_comm_event(pid, 1); | ||
350 | pid_synthesize_mmap_samples(pid, tgid); | ||
351 | } | ||
352 | |||
353 | closedir(proc); | ||
354 | } | ||
355 | |||
356 | static int group_fd; | 213 | static int group_fd; |
357 | 214 | ||
358 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) | 215 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) |
@@ -363,7 +220,11 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n | |||
363 | h_attr = header->attr[nr]; | 220 | h_attr = header->attr[nr]; |
364 | } else { | 221 | } else { |
365 | h_attr = perf_header_attr__new(a); | 222 | h_attr = perf_header_attr__new(a); |
366 | perf_header__add_attr(header, h_attr); | 223 | if (h_attr != NULL) |
224 | if (perf_header__add_attr(header, h_attr) < 0) { | ||
225 | perf_header_attr__delete(h_attr); | ||
226 | h_attr = NULL; | ||
227 | } | ||
367 | } | 228 | } |
368 | 229 | ||
369 | return h_attr; | 230 | return h_attr; |
@@ -424,7 +285,7 @@ try_again: | |||
424 | if (fd[nr_cpu][counter] < 0) { | 285 | if (fd[nr_cpu][counter] < 0) { |
425 | int err = errno; | 286 | int err = errno; |
426 | 287 | ||
427 | if (err == EPERM) | 288 | if (err == EPERM || err == EACCES) |
428 | die("Permission error - are you root?\n"); | 289 | die("Permission error - are you root?\n"); |
429 | else if (err == ENODEV && profile_cpu != -1) | 290 | else if (err == ENODEV && profile_cpu != -1) |
430 | die("No such device - did you specify an out-of-range profile CPU?\n"); | 291 | die("No such device - did you specify an out-of-range profile CPU?\n"); |
@@ -451,6 +312,8 @@ try_again: | |||
451 | } | 312 | } |
452 | 313 | ||
453 | h_attr = get_header_attr(attr, counter); | 314 | h_attr = get_header_attr(attr, counter); |
315 | if (h_attr == NULL) | ||
316 | die("nomem\n"); | ||
454 | 317 | ||
455 | if (!file_new) { | 318 | if (!file_new) { |
456 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | 319 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { |
@@ -464,7 +327,10 @@ try_again: | |||
464 | exit(-1); | 327 | exit(-1); |
465 | } | 328 | } |
466 | 329 | ||
467 | 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 | } | ||
468 | 334 | ||
469 | assert(fd[nr_cpu][counter] >= 0); | 335 | assert(fd[nr_cpu][counter] >= 0); |
470 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); | 336 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); |
@@ -525,7 +391,7 @@ static void atexit_header(void) | |||
525 | { | 391 | { |
526 | header->data_size += bytes_written; | 392 | header->data_size += bytes_written; |
527 | 393 | ||
528 | perf_header__write(header, output); | 394 | perf_header__write(header, output, true); |
529 | } | 395 | } |
530 | 396 | ||
531 | static int __cmd_record(int argc, const char **argv) | 397 | static int __cmd_record(int argc, const char **argv) |
@@ -573,12 +439,17 @@ static int __cmd_record(int argc, const char **argv) | |||
573 | else | 439 | else |
574 | header = perf_header__new(); | 440 | header = perf_header__new(); |
575 | 441 | ||
442 | if (header == NULL) { | ||
443 | pr_err("Not enough memory for reading perf file header\n"); | ||
444 | return -1; | ||
445 | } | ||
446 | |||
576 | if (raw_samples) { | 447 | if (raw_samples) { |
577 | perf_header__feat_trace_info(header); | 448 | perf_header__set_feat(header, HEADER_TRACE_INFO); |
578 | } else { | 449 | } else { |
579 | for (i = 0; i < nr_counters; i++) { | 450 | for (i = 0; i < nr_counters; i++) { |
580 | if (attrs[i].sample_type & PERF_SAMPLE_RAW) { | 451 | if (attrs[i].sample_type & PERF_SAMPLE_RAW) { |
581 | perf_header__feat_trace_info(header); | 452 | perf_header__set_feat(header, HEADER_TRACE_INFO); |
582 | break; | 453 | break; |
583 | } | 454 | } |
584 | } | 455 | } |
@@ -602,24 +473,32 @@ static int __cmd_record(int argc, const char **argv) | |||
602 | } | 473 | } |
603 | 474 | ||
604 | if (file_new) | 475 | if (file_new) |
605 | perf_header__write(header, output); | 476 | perf_header__write(header, output, false); |
606 | 477 | ||
607 | if (!system_wide) { | 478 | if (!system_wide) |
608 | pid_t tgid = pid_synthesize_comm_event(pid, 0); | 479 | event__synthesize_thread(pid, process_synthesized_event); |
609 | pid_synthesize_mmap_samples(pid, tgid); | 480 | else |
610 | } else | 481 | event__synthesize_threads(process_synthesized_event); |
611 | synthesize_all(); | ||
612 | 482 | ||
613 | if (target_pid == -1 && argc) { | 483 | if (target_pid == -1 && argc) { |
614 | pid = fork(); | 484 | pid = fork(); |
615 | if (pid < 0) | 485 | if (pid < 0) |
616 | perror("failed to fork"); | 486 | die("failed to fork"); |
617 | 487 | ||
618 | if (!pid) { | 488 | if (!pid) { |
619 | if (execvp(argv[0], (char **)argv)) { | 489 | if (execvp(argv[0], (char **)argv)) { |
620 | perror(argv[0]); | 490 | perror(argv[0]); |
621 | exit(-1); | 491 | exit(-1); |
622 | } | 492 | } |
493 | } else { | ||
494 | /* | ||
495 | * Wait a bit for the execv'ed child to appear | ||
496 | * and be updated in /proc | ||
497 | * FIXME: Do you know a less heuristical solution? | ||
498 | */ | ||
499 | usleep(1000); | ||
500 | event__synthesize_thread(pid, | ||
501 | process_synthesized_event); | ||
623 | } | 502 | } |
624 | 503 | ||
625 | child_pid = pid; | 504 | child_pid = pid; |
@@ -729,6 +608,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
729 | { | 608 | { |
730 | int counter; | 609 | int counter; |
731 | 610 | ||
611 | symbol__init(0); | ||
612 | |||
732 | argc = parse_options(argc, argv, options, record_usage, | 613 | argc = parse_options(argc, argv, options, record_usage, |
733 | PARSE_OPT_STOP_AT_NON_OPTION); | 614 | PARSE_OPT_STOP_AT_NON_OPTION); |
734 | if (!argc && target_pid == -1 && !system_wide) | 615 | if (!argc && target_pid == -1 && !system_wide) |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b3d814b54555..1a806d5f05cf 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -455,7 +455,7 @@ got_map: | |||
455 | dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip); | 455 | dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip); |
456 | *ipp = ip; | 456 | *ipp = ip; |
457 | 457 | ||
458 | return map ? map->dso->find_symbol(map->dso, ip) : NULL; | 458 | return map ? map__find_symbol(map, ip, NULL) : NULL; |
459 | } | 459 | } |
460 | 460 | ||
461 | static int call__match(struct symbol *sym) | 461 | static int call__match(struct symbol *sym) |
@@ -751,7 +751,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
751 | static int | 751 | static int |
752 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | 752 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) |
753 | { | 753 | { |
754 | struct map *map = map__new(&event->mmap, cwd, cwdlen, 0, NULL); | 754 | struct map *map = map__new(&event->mmap, cwd, cwdlen); |
755 | struct thread *thread = threads__findnew(event->mmap.pid); | 755 | struct thread *thread = threads__findnew(event->mmap.pid); |
756 | 756 | ||
757 | dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n", | 757 | dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n", |
@@ -1093,7 +1093,7 @@ static void setup_list(struct strlist **list, const char *list_str, | |||
1093 | 1093 | ||
1094 | int cmd_report(int argc, const char **argv, const char *prefix __used) | 1094 | int cmd_report(int argc, const char **argv, const char *prefix __used) |
1095 | { | 1095 | { |
1096 | symbol__init(); | 1096 | symbol__init(0); |
1097 | 1097 | ||
1098 | argc = parse_options(argc, argv, options, report_usage, 0); | 1098 | argc = parse_options(argc, argv, options, report_usage, 0); |
1099 | 1099 | ||
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 9a48d9626be4..df44b756cecc 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -1937,7 +1937,7 @@ static int __cmd_record(int argc, const char **argv) | |||
1937 | 1937 | ||
1938 | int cmd_sched(int argc, const char **argv, const char *prefix __used) | 1938 | int cmd_sched(int argc, const char **argv, const char *prefix __used) |
1939 | { | 1939 | { |
1940 | symbol__init(); | 1940 | symbol__init(0); |
1941 | 1941 | ||
1942 | argc = parse_options(argc, argv, sched_options, sched_usage, | 1942 | argc = parse_options(argc, argv, sched_options, sched_usage, |
1943 | PARSE_OPT_STOP_AT_NON_OPTION); | 1943 | PARSE_OPT_STOP_AT_NON_OPTION); |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c6df3770b87e..c70d72003557 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -357,7 +357,8 @@ static void abs_printout(int counter, double avg) | |||
357 | ratio = avg / total; | 357 | ratio = avg / total; |
358 | 358 | ||
359 | fprintf(stderr, " # %10.3f IPC ", ratio); | 359 | fprintf(stderr, " # %10.3f IPC ", ratio); |
360 | } else if (MATCH_EVENT(HARDWARE, HW_BRANCH_MISSES, counter)) { | 360 | } else if (MATCH_EVENT(HARDWARE, HW_BRANCH_MISSES, counter) && |
361 | runtime_branches_stats.n != 0) { | ||
361 | total = avg_stats(&runtime_branches_stats); | 362 | total = avg_stats(&runtime_branches_stats); |
362 | 363 | ||
363 | if (total) | 364 | if (total) |
@@ -365,7 +366,7 @@ static void abs_printout(int counter, double avg) | |||
365 | 366 | ||
366 | fprintf(stderr, " # %10.3f %% ", ratio); | 367 | fprintf(stderr, " # %10.3f %% ", ratio); |
367 | 368 | ||
368 | } else { | 369 | } else if (runtime_nsecs_stats.n != 0) { |
369 | total = avg_stats(&runtime_nsecs_stats); | 370 | total = avg_stats(&runtime_nsecs_stats); |
370 | 371 | ||
371 | if (total) | 372 | if (total) |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 0a2f22261c3a..665877e4a944 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -1266,7 +1266,7 @@ static const struct option options[] = { | |||
1266 | 1266 | ||
1267 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) | 1267 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) |
1268 | { | 1268 | { |
1269 | symbol__init(); | 1269 | symbol__init(0); |
1270 | 1270 | ||
1271 | page_size = getpagesize(); | 1271 | page_size = getpagesize(); |
1272 | 1272 | ||
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 4a9fe228be2a..89b7f68a1799 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -60,7 +60,7 @@ static int system_wide = 0; | |||
60 | static int default_interval = 0; | 60 | static int default_interval = 0; |
61 | 61 | ||
62 | static int count_filter = 5; | 62 | static int count_filter = 5; |
63 | static int print_entries = 15; | 63 | static int print_entries; |
64 | 64 | ||
65 | static int target_pid = -1; | 65 | static int target_pid = -1; |
66 | static int inherit = 0; | 66 | static int inherit = 0; |
@@ -76,6 +76,9 @@ static int delay_secs = 2; | |||
76 | static int zero = 0; | 76 | static int zero = 0; |
77 | static int dump_symtab = 0; | 77 | static int dump_symtab = 0; |
78 | 78 | ||
79 | static bool hide_kernel_symbols = false; | ||
80 | static bool hide_user_symbols = false; | ||
81 | |||
79 | /* | 82 | /* |
80 | * Source | 83 | * Source |
81 | */ | 84 | */ |
@@ -104,6 +107,7 @@ struct sym_entry { | |||
104 | unsigned long snap_count; | 107 | unsigned long snap_count; |
105 | double weight; | 108 | double weight; |
106 | int skip; | 109 | int skip; |
110 | u8 origin; | ||
107 | struct map *map; | 111 | struct map *map; |
108 | struct source_line *source; | 112 | struct source_line *source; |
109 | struct source_line *lines; | 113 | struct source_line *lines; |
@@ -115,6 +119,36 @@ struct sym_entry { | |||
115 | * Source functions | 119 | * Source functions |
116 | */ | 120 | */ |
117 | 121 | ||
122 | /* most GUI terminals set LINES (although some don't export it) */ | ||
123 | static int term_rows(void) | ||
124 | { | ||
125 | char *lines_string = getenv("LINES"); | ||
126 | int n_lines; | ||
127 | |||
128 | if (lines_string && (n_lines = atoi(lines_string)) > 0) | ||
129 | return n_lines; | ||
130 | #ifdef TIOCGWINSZ | ||
131 | else { | ||
132 | struct winsize ws; | ||
133 | if (!ioctl(1, TIOCGWINSZ, &ws) && ws.ws_row) | ||
134 | return ws.ws_row; | ||
135 | } | ||
136 | #endif | ||
137 | return 25; | ||
138 | } | ||
139 | |||
140 | static void update_print_entries(void) | ||
141 | { | ||
142 | print_entries = term_rows(); | ||
143 | if (print_entries > 9) | ||
144 | print_entries -= 9; | ||
145 | } | ||
146 | |||
147 | static void sig_winch_handler(int sig __used) | ||
148 | { | ||
149 | update_print_entries(); | ||
150 | } | ||
151 | |||
118 | static void parse_source(struct sym_entry *syme) | 152 | static void parse_source(struct sym_entry *syme) |
119 | { | 153 | { |
120 | struct symbol *sym; | 154 | struct symbol *sym; |
@@ -318,7 +352,7 @@ static void show_details(struct sym_entry *syme) | |||
318 | } | 352 | } |
319 | 353 | ||
320 | /* | 354 | /* |
321 | * Symbols will be added here in record_ip and will get out | 355 | * Symbols will be added here in event__process_sample and will get out |
322 | * after decayed. | 356 | * after decayed. |
323 | */ | 357 | */ |
324 | static LIST_HEAD(active_symbols); | 358 | static LIST_HEAD(active_symbols); |
@@ -400,6 +434,13 @@ static void print_sym_table(void) | |||
400 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { | 434 | list_for_each_entry_safe_from(syme, n, &active_symbols, node) { |
401 | syme->snap_count = syme->count[snap]; | 435 | syme->snap_count = syme->count[snap]; |
402 | if (syme->snap_count != 0) { | 436 | if (syme->snap_count != 0) { |
437 | if ((hide_user_symbols && | ||
438 | syme->origin == PERF_RECORD_MISC_USER) || | ||
439 | (hide_kernel_symbols && | ||
440 | syme->origin == PERF_RECORD_MISC_KERNEL)) { | ||
441 | list_remove_active_sym(syme); | ||
442 | continue; | ||
443 | } | ||
403 | syme->weight = sym_weight(syme); | 444 | syme->weight = sym_weight(syme); |
404 | rb_insert_active_sym(&tmp, syme); | 445 | rb_insert_active_sym(&tmp, syme); |
405 | sum_ksamples += syme->snap_count; | 446 | sum_ksamples += syme->snap_count; |
@@ -459,18 +500,18 @@ static void print_sym_table(void) | |||
459 | } | 500 | } |
460 | 501 | ||
461 | if (nr_counters == 1) | 502 | if (nr_counters == 1) |
462 | printf(" samples pcnt"); | 503 | printf(" samples pcnt"); |
463 | else | 504 | else |
464 | printf(" weight samples pcnt"); | 505 | printf(" weight samples pcnt"); |
465 | 506 | ||
466 | if (verbose) | 507 | if (verbose) |
467 | printf(" RIP "); | 508 | printf(" RIP "); |
468 | printf(" kernel function\n"); | 509 | printf(" function DSO\n"); |
469 | printf(" %s _______ _____", | 510 | printf(" %s _______ _____", |
470 | nr_counters == 1 ? " " : "______"); | 511 | nr_counters == 1 ? " " : "______"); |
471 | if (verbose) | 512 | if (verbose) |
472 | printf(" ________________"); | 513 | printf(" ________________"); |
473 | printf(" _______________\n\n"); | 514 | printf(" ________________________________ ________________\n\n"); |
474 | 515 | ||
475 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { | 516 | for (nd = rb_first(&tmp); nd; nd = rb_next(nd)) { |
476 | struct symbol *sym; | 517 | struct symbol *sym; |
@@ -486,16 +527,15 @@ static void print_sym_table(void) | |||
486 | sum_ksamples)); | 527 | sum_ksamples)); |
487 | 528 | ||
488 | if (nr_counters == 1 || !display_weighted) | 529 | if (nr_counters == 1 || !display_weighted) |
489 | printf("%20.2f - ", syme->weight); | 530 | printf("%20.2f ", syme->weight); |
490 | else | 531 | else |
491 | printf("%9.1f %10ld - ", syme->weight, syme->snap_count); | 532 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
492 | 533 | ||
493 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); | 534 | percent_color_fprintf(stdout, "%4.1f%%", pcnt); |
494 | if (verbose) | 535 | if (verbose) |
495 | printf(" - %016llx", sym->start); | 536 | printf(" %016llx", sym->start); |
496 | printf(" : %s", sym->name); | 537 | printf(" %-32s", sym->name); |
497 | if (syme->map->dso->name[0] == '[') | 538 | printf(" %s", syme->map->dso->short_name); |
498 | printf(" \t%s", syme->map->dso->name); | ||
499 | printf("\n"); | 539 | printf("\n"); |
500 | } | 540 | } |
501 | } | 541 | } |
@@ -608,6 +648,12 @@ static void print_mapped_keys(void) | |||
608 | if (nr_counters > 1) | 648 | if (nr_counters > 1) |
609 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 649 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
610 | 650 | ||
651 | fprintf(stdout, | ||
652 | "\t[K] hide kernel_symbols symbols. \t(%s)\n", | ||
653 | hide_kernel_symbols ? "yes" : "no"); | ||
654 | fprintf(stdout, | ||
655 | "\t[U] hide user symbols. \t(%s)\n", | ||
656 | hide_user_symbols ? "yes" : "no"); | ||
611 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); | 657 | fprintf(stdout, "\t[z] toggle sample zeroing. \t(%d)\n", zero ? 1 : 0); |
612 | fprintf(stdout, "\t[qQ] quit.\n"); | 658 | fprintf(stdout, "\t[qQ] quit.\n"); |
613 | } | 659 | } |
@@ -621,6 +667,8 @@ static int key_mapped(int c) | |||
621 | case 'z': | 667 | case 'z': |
622 | case 'q': | 668 | case 'q': |
623 | case 'Q': | 669 | case 'Q': |
670 | case 'K': | ||
671 | case 'U': | ||
624 | return 1; | 672 | return 1; |
625 | case 'E': | 673 | case 'E': |
626 | case 'w': | 674 | case 'w': |
@@ -669,6 +717,11 @@ static void handle_keypress(int c) | |||
669 | break; | 717 | break; |
670 | case 'e': | 718 | case 'e': |
671 | prompt_integer(&print_entries, "Enter display entries (lines)"); | 719 | prompt_integer(&print_entries, "Enter display entries (lines)"); |
720 | if (print_entries == 0) { | ||
721 | update_print_entries(); | ||
722 | signal(SIGWINCH, sig_winch_handler); | ||
723 | } else | ||
724 | signal(SIGWINCH, SIG_DFL); | ||
672 | break; | 725 | break; |
673 | case 'E': | 726 | case 'E': |
674 | if (nr_counters > 1) { | 727 | if (nr_counters > 1) { |
@@ -693,6 +746,9 @@ static void handle_keypress(int c) | |||
693 | case 'F': | 746 | case 'F': |
694 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); | 747 | prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)"); |
695 | break; | 748 | break; |
749 | case 'K': | ||
750 | hide_kernel_symbols = !hide_kernel_symbols; | ||
751 | break; | ||
696 | case 'q': | 752 | case 'q': |
697 | case 'Q': | 753 | case 'Q': |
698 | printf("exiting.\n"); | 754 | printf("exiting.\n"); |
@@ -712,6 +768,9 @@ static void handle_keypress(int c) | |||
712 | pthread_mutex_unlock(&syme->source_lock); | 768 | pthread_mutex_unlock(&syme->source_lock); |
713 | } | 769 | } |
714 | break; | 770 | break; |
771 | case 'U': | ||
772 | hide_user_symbols = !hide_user_symbols; | ||
773 | break; | ||
715 | case 'w': | 774 | case 'w': |
716 | display_weighted = ~display_weighted; | 775 | display_weighted = ~display_weighted; |
717 | break; | 776 | break; |
@@ -790,7 +849,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
790 | strstr(name, "_text_end")) | 849 | strstr(name, "_text_end")) |
791 | return 1; | 850 | return 1; |
792 | 851 | ||
793 | syme = dso__sym_priv(map->dso, sym); | 852 | syme = symbol__priv(sym); |
794 | syme->map = map; | 853 | syme->map = map; |
795 | pthread_mutex_init(&syme->source_lock, NULL); | 854 | pthread_mutex_init(&syme->source_lock, NULL); |
796 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) | 855 | if (!sym_filter_entry && sym_filter && !strcmp(name, sym_filter)) |
@@ -808,8 +867,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
808 | 867 | ||
809 | static int parse_symbols(void) | 868 | static int parse_symbols(void) |
810 | { | 869 | { |
811 | if (dsos__load_kernel(vmlinux_name, sizeof(struct sym_entry), | 870 | if (dsos__load_kernel(vmlinux_name, symbol_filter, 1) <= 0) |
812 | symbol_filter, 1) <= 0) | ||
813 | return -1; | 871 | return -1; |
814 | 872 | ||
815 | if (dump_symtab) | 873 | if (dump_symtab) |
@@ -818,41 +876,104 @@ static int parse_symbols(void) | |||
818 | return 0; | 876 | return 0; |
819 | } | 877 | } |
820 | 878 | ||
821 | /* | 879 | static void event__process_sample(const event_t *self, int counter) |
822 | * Binary search in the histogram table and record the hit: | ||
823 | */ | ||
824 | static void record_ip(u64 ip, int counter) | ||
825 | { | 880 | { |
881 | u64 ip = self->ip.ip; | ||
826 | struct map *map; | 882 | struct map *map; |
827 | struct symbol *sym = kernel_maps__find_symbol(ip, &map); | 883 | struct sym_entry *syme; |
828 | 884 | struct symbol *sym; | |
829 | if (sym != NULL) { | 885 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
830 | struct sym_entry *syme = dso__sym_priv(map->dso, sym); | 886 | |
831 | 887 | switch (origin) { | |
832 | if (!syme->skip) { | 888 | case PERF_RECORD_MISC_USER: { |
833 | syme->count[counter]++; | 889 | struct thread *thread; |
834 | record_precise_ip(syme, counter, ip); | 890 | |
835 | pthread_mutex_lock(&active_symbols_lock); | 891 | if (hide_user_symbols) |
836 | if (list_empty(&syme->node) || !syme->node.next) | 892 | return; |
837 | __list_insert_active_sym(syme); | 893 | |
838 | pthread_mutex_unlock(&active_symbols_lock); | 894 | thread = threads__findnew(self->ip.pid); |
895 | if (thread == NULL) | ||
839 | return; | 896 | return; |
897 | |||
898 | map = thread__find_map(thread, ip); | ||
899 | if (map != NULL) { | ||
900 | ip = map->map_ip(map, ip); | ||
901 | sym = map__find_symbol(map, ip, symbol_filter); | ||
902 | if (sym == NULL) | ||
903 | return; | ||
904 | userspace_samples++; | ||
905 | break; | ||
840 | } | 906 | } |
841 | } | 907 | } |
908 | /* | ||
909 | * If this is outside of all known maps, | ||
910 | * and is a negative address, try to look it | ||
911 | * up in the kernel dso, as it might be a | ||
912 | * vsyscall or vdso (which executes in user-mode). | ||
913 | */ | ||
914 | if ((long long)ip >= 0) | ||
915 | return; | ||
916 | /* Fall thru */ | ||
917 | case PERF_RECORD_MISC_KERNEL: | ||
918 | if (hide_kernel_symbols) | ||
919 | return; | ||
920 | |||
921 | sym = kernel_maps__find_symbol(ip, &map); | ||
922 | if (sym == NULL) | ||
923 | return; | ||
924 | break; | ||
925 | default: | ||
926 | return; | ||
927 | } | ||
842 | 928 | ||
843 | samples--; | 929 | syme = symbol__priv(sym); |
930 | |||
931 | if (!syme->skip) { | ||
932 | syme->count[counter]++; | ||
933 | syme->origin = origin; | ||
934 | record_precise_ip(syme, counter, ip); | ||
935 | pthread_mutex_lock(&active_symbols_lock); | ||
936 | if (list_empty(&syme->node) || !syme->node.next) | ||
937 | __list_insert_active_sym(syme); | ||
938 | pthread_mutex_unlock(&active_symbols_lock); | ||
939 | ++samples; | ||
940 | return; | ||
941 | } | ||
844 | } | 942 | } |
845 | 943 | ||
846 | static void process_event(u64 ip, int counter, int user) | 944 | static void event__process_mmap(event_t *self) |
847 | { | 945 | { |
848 | samples++; | 946 | struct thread *thread = threads__findnew(self->mmap.pid); |
849 | 947 | ||
850 | if (user) { | 948 | if (thread != NULL) { |
851 | userspace_samples++; | 949 | struct map *map = map__new(&self->mmap, NULL, 0); |
852 | return; | 950 | if (map != NULL) |
951 | thread__insert_map(thread, map); | ||
853 | } | 952 | } |
953 | } | ||
854 | 954 | ||
855 | record_ip(ip, counter); | 955 | static void event__process_comm(event_t *self) |
956 | { | ||
957 | struct thread *thread = threads__findnew(self->comm.pid); | ||
958 | |||
959 | if (thread != NULL) | ||
960 | thread__set_comm(thread, self->comm.comm); | ||
961 | } | ||
962 | |||
963 | static int event__process(event_t *event) | ||
964 | { | ||
965 | switch (event->header.type) { | ||
966 | case PERF_RECORD_COMM: | ||
967 | event__process_comm(event); | ||
968 | break; | ||
969 | case PERF_RECORD_MMAP: | ||
970 | event__process_mmap(event); | ||
971 | break; | ||
972 | default: | ||
973 | break; | ||
974 | } | ||
975 | |||
976 | return 0; | ||
856 | } | 977 | } |
857 | 978 | ||
858 | struct mmap_data { | 979 | struct mmap_data { |
@@ -925,13 +1046,11 @@ static void mmap_read_counter(struct mmap_data *md) | |||
925 | event = &event_copy; | 1046 | event = &event_copy; |
926 | } | 1047 | } |
927 | 1048 | ||
1049 | if (event->header.type == PERF_RECORD_SAMPLE) | ||
1050 | event__process_sample(event, md->counter); | ||
1051 | else | ||
1052 | event__process(event); | ||
928 | old += size; | 1053 | old += size; |
929 | |||
930 | if (event->header.type == PERF_RECORD_SAMPLE) { | ||
931 | int user = | ||
932 | (event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER; | ||
933 | process_event(event->ip.ip, md->counter, user); | ||
934 | } | ||
935 | } | 1054 | } |
936 | 1055 | ||
937 | md->prev = old; | 1056 | md->prev = old; |
@@ -973,6 +1092,7 @@ static void start_counter(int i, int counter) | |||
973 | } | 1092 | } |
974 | 1093 | ||
975 | attr->inherit = (cpu < 0) && inherit; | 1094 | attr->inherit = (cpu < 0) && inherit; |
1095 | attr->mmap = 1; | ||
976 | 1096 | ||
977 | try_again: | 1097 | try_again: |
978 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); | 1098 | fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); |
@@ -980,7 +1100,7 @@ try_again: | |||
980 | if (fd[i][counter] < 0) { | 1100 | if (fd[i][counter] < 0) { |
981 | int err = errno; | 1101 | int err = errno; |
982 | 1102 | ||
983 | if (err == EPERM) | 1103 | if (err == EPERM || err == EACCES) |
984 | die("No permission - are you root?\n"); | 1104 | die("No permission - are you root?\n"); |
985 | /* | 1105 | /* |
986 | * If it's cycles then fall back to hrtimer | 1106 | * If it's cycles then fall back to hrtimer |
@@ -1031,6 +1151,11 @@ static int __cmd_top(void) | |||
1031 | int i, counter; | 1151 | int i, counter; |
1032 | int ret; | 1152 | int ret; |
1033 | 1153 | ||
1154 | if (target_pid != -1) | ||
1155 | event__synthesize_thread(target_pid, event__process); | ||
1156 | else | ||
1157 | event__synthesize_threads(event__process); | ||
1158 | |||
1034 | for (i = 0; i < nr_cpus; i++) { | 1159 | for (i = 0; i < nr_cpus; i++) { |
1035 | group_fd = -1; | 1160 | group_fd = -1; |
1036 | for (counter = 0; counter < nr_counters; counter++) | 1161 | for (counter = 0; counter < nr_counters; counter++) |
@@ -1087,6 +1212,8 @@ static const struct option options[] = { | |||
1087 | OPT_INTEGER('C', "CPU", &profile_cpu, | 1212 | OPT_INTEGER('C', "CPU", &profile_cpu, |
1088 | "CPU to profile on"), | 1213 | "CPU to profile on"), |
1089 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), | 1214 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), |
1215 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | ||
1216 | "hide kernel symbols"), | ||
1090 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, | 1217 | OPT_INTEGER('m', "mmap-pages", &mmap_pages, |
1091 | "number of mmap data pages"), | 1218 | "number of mmap data pages"), |
1092 | OPT_INTEGER('r', "realtime", &realtime_prio, | 1219 | OPT_INTEGER('r', "realtime", &realtime_prio, |
@@ -1109,6 +1236,8 @@ static const struct option options[] = { | |||
1109 | "profile at this frequency"), | 1236 | "profile at this frequency"), |
1110 | OPT_INTEGER('E', "entries", &print_entries, | 1237 | OPT_INTEGER('E', "entries", &print_entries, |
1111 | "display this many functions"), | 1238 | "display this many functions"), |
1239 | OPT_BOOLEAN('U', "hide_user_symbols", &hide_user_symbols, | ||
1240 | "hide user symbols"), | ||
1112 | OPT_BOOLEAN('v', "verbose", &verbose, | 1241 | OPT_BOOLEAN('v', "verbose", &verbose, |
1113 | "be more verbose (show counter open errors, etc)"), | 1242 | "be more verbose (show counter open errors, etc)"), |
1114 | OPT_END() | 1243 | OPT_END() |
@@ -1118,7 +1247,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1118 | { | 1247 | { |
1119 | int counter; | 1248 | int counter; |
1120 | 1249 | ||
1121 | symbol__init(); | 1250 | symbol__init(sizeof(struct sym_entry)); |
1122 | 1251 | ||
1123 | page_size = sysconf(_SC_PAGE_SIZE); | 1252 | page_size = sysconf(_SC_PAGE_SIZE); |
1124 | 1253 | ||
@@ -1172,5 +1301,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1172 | if (target_pid != -1 || profile_cpu != -1) | 1301 | if (target_pid != -1 || profile_cpu != -1) |
1173 | nr_cpus = 1; | 1302 | nr_cpus = 1; |
1174 | 1303 | ||
1304 | if (print_entries == 0) { | ||
1305 | update_print_entries(); | ||
1306 | signal(SIGWINCH, sig_winch_handler); | ||
1307 | } | ||
1308 | |||
1175 | return __cmd_top(); | 1309 | return __cmd_top(); |
1176 | } | 1310 | } |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e566bbe3f22d..d042d656c561 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -151,7 +151,7 @@ static const struct option options[] = { | |||
151 | 151 | ||
152 | int cmd_trace(int argc, const char **argv, const char *prefix __used) | 152 | int cmd_trace(int argc, const char **argv, const char *prefix __used) |
153 | { | 153 | { |
154 | symbol__init(); | 154 | symbol__init(0); |
155 | 155 | ||
156 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 156 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
157 | if (argc) { | 157 | if (argc) { |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index ad5f0f4c49ee..9b02d85091fe 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -15,6 +15,8 @@ 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_list(int argc, const char **argv, const char *prefix); | ||
18 | extern int cmd_help(int argc, const char **argv, const char *prefix); | 20 | 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); | 21 | 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); | 22 | extern int cmd_list(int argc, const char **argv, const char *prefix); |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 6475db4f194c..d3a6e18e4a5e 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -3,6 +3,8 @@ | |||
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-bench mainporcelain common | ||
7 | perf-buildid-list mainporcelain common | ||
6 | perf-list mainporcelain common | 8 | perf-list mainporcelain common |
7 | perf-sched mainporcelain common | 9 | perf-sched mainporcelain common |
8 | perf-record mainporcelain common | 10 | perf-record mainporcelain common |
diff --git a/tools/perf/design.txt b/tools/perf/design.txt index fdd42a824c98..f000c30877ac 100644 --- a/tools/perf/design.txt +++ b/tools/perf/design.txt | |||
@@ -137,6 +137,8 @@ enum sw_event_ids { | |||
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 |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 9cafe5463266..89b82acac7d9 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]"; |
@@ -286,8 +287,10 @@ static void handle_internal_command(int argc, const char **argv) | |||
286 | static struct cmd_struct commands[] = { | 287 | static struct cmd_struct commands[] = { |
287 | { "help", cmd_help, 0 }, | 288 | { "help", cmd_help, 0 }, |
288 | { "list", cmd_list, 0 }, | 289 | { "list", cmd_list, 0 }, |
290 | { "buildid-list", cmd_buildid_list, 0 }, | ||
289 | { "record", cmd_record, 0 }, | 291 | { "record", cmd_record, 0 }, |
290 | { "report", cmd_report, 0 }, | 292 | { "report", cmd_report, 0 }, |
293 | { "bench", cmd_bench, 0 }, | ||
291 | { "stat", cmd_stat, 0 }, | 294 | { "stat", cmd_stat, 0 }, |
292 | { "timechart", cmd_timechart, 0 }, | 295 | { "timechart", cmd_timechart, 0 }, |
293 | { "top", cmd_top, 0 }, | 296 | { "top", cmd_top, 0 }, |
@@ -383,45 +386,12 @@ static int run_argv(int *argcp, const char ***argv) | |||
383 | /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ | 386 | /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ |
384 | static void get_debugfs_mntpt(void) | 387 | static void get_debugfs_mntpt(void) |
385 | { | 388 | { |
386 | FILE *file; | 389 | const char *path = debugfs_find_mountpoint(); |
387 | char fs_type[100]; | ||
388 | char debugfs[MAXPATHLEN]; | ||
389 | 390 | ||
390 | /* | 391 | if (path) |
391 | * try the standard location | 392 | strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt)); |
392 | */ | 393 | else |
393 | if (valid_debugfs_mount("/sys/kernel/debug/") == 0) { | 394 | debugfs_mntpt[0] = '\0'; |
394 | strcpy(debugfs_mntpt, "/sys/kernel/debug/"); | ||
395 | return; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * try the sane location | ||
400 | */ | ||
401 | if (valid_debugfs_mount("/debug/") == 0) { | ||
402 | strcpy(debugfs_mntpt, "/debug/"); | ||
403 | return; | ||
404 | } | ||
405 | |||
406 | /* | ||
407 | * give up and parse /proc/mounts | ||
408 | */ | ||
409 | file = fopen("/proc/mounts", "r"); | ||
410 | if (file == NULL) | ||
411 | return; | ||
412 | |||
413 | while (fscanf(file, "%*s %" | ||
414 | STR(MAXPATHLEN) | ||
415 | "s %99s %*s %*d %*d\n", | ||
416 | debugfs, fs_type) == 2) { | ||
417 | if (strcmp(fs_type, "debugfs") == 0) | ||
418 | break; | ||
419 | } | ||
420 | fclose(file); | ||
421 | if (strcmp(fs_type, "debugfs") == 0) { | ||
422 | strncpy(debugfs_mntpt, debugfs, MAXPATHLEN); | ||
423 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | ||
424 | } | ||
425 | } | 395 | } |
426 | 396 | ||
427 | int main(int argc, const char **argv) | 397 | int main(int argc, const char **argv) |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 8cc4623afd6f..216bdb223f63 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -47,6 +47,12 @@ | |||
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 | |||
50 | #include <time.h> | 56 | #include <time.h> |
51 | #include <unistd.h> | 57 | #include <unistd.h> |
52 | #include <sys/types.h> | 58 | #include <sys/types.h> |
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c index 18accb8fee4d..14cb8465eb08 100644 --- a/tools/perf/util/data_map.c +++ b/tools/perf/util/data_map.c | |||
@@ -70,6 +70,35 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
70 | } | 70 | } |
71 | } | 71 | } |
72 | 72 | ||
73 | int perf_header__read_build_ids(int input, off_t offset, off_t size) | ||
74 | { | ||
75 | struct build_id_event bev; | ||
76 | char filename[PATH_MAX]; | ||
77 | off_t limit = offset + size; | ||
78 | int err = -1; | ||
79 | |||
80 | while (offset < limit) { | ||
81 | struct dso *dso; | ||
82 | ssize_t len; | ||
83 | |||
84 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
85 | goto out; | ||
86 | |||
87 | len = bev.header.size - sizeof(bev); | ||
88 | if (read(input, filename, len) != len) | ||
89 | goto out; | ||
90 | |||
91 | dso = dsos__findnew(filename); | ||
92 | if (dso != NULL) | ||
93 | dso__set_build_id(dso, &bev.build_id); | ||
94 | |||
95 | offset += bev.header.size; | ||
96 | } | ||
97 | err = 0; | ||
98 | out: | ||
99 | return err; | ||
100 | } | ||
101 | |||
73 | int mmap_dispatch_perf_file(struct perf_header **pheader, | 102 | int mmap_dispatch_perf_file(struct perf_header **pheader, |
74 | const char *input_name, | 103 | const char *input_name, |
75 | int force, | 104 | int force, |
@@ -130,7 +159,7 @@ int mmap_dispatch_perf_file(struct perf_header **pheader, | |||
130 | if (curr_handler->sample_type_check(sample_type) < 0) | 159 | if (curr_handler->sample_type_check(sample_type) < 0) |
131 | exit(-1); | 160 | exit(-1); |
132 | 161 | ||
133 | if (load_kernel(0, NULL) < 0) { | 162 | if (load_kernel(NULL) < 0) { |
134 | perror("failed to load kernel symbols"); | 163 | perror("failed to load kernel symbols"); |
135 | return EXIT_FAILURE; | 164 | return EXIT_FAILURE; |
136 | } | 165 | } |
diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h index 716d1053b074..ae036ecd7625 100644 --- a/tools/perf/util/data_map.h +++ b/tools/perf/util/data_map.h | |||
@@ -27,5 +27,6 @@ int mmap_dispatch_perf_file(struct perf_header **pheader, | |||
27 | int full_paths, | 27 | int full_paths, |
28 | int *cwdlen, | 28 | int *cwdlen, |
29 | char **cwd); | 29 | char **cwd); |
30 | int perf_header__read_build_ids(int input, off_t offset, off_t file_size); | ||
30 | 31 | ||
31 | #endif | 32 | #endif |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index e8b18a1f87a4..c6c24c522dea 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -2,6 +2,8 @@ | |||
2 | #ifndef __PERF_DEBUG_H | 2 | #ifndef __PERF_DEBUG_H |
3 | #define __PERF_DEBUG_H | 3 | #define __PERF_DEBUG_H |
4 | 4 | ||
5 | #include "event.h" | ||
6 | |||
5 | extern int verbose; | 7 | extern int verbose; |
6 | extern int dump_trace; | 8 | extern int dump_trace; |
7 | 9 | ||
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c new file mode 100644 index 000000000000..06b73ee02c49 --- /dev/null +++ b/tools/perf/util/debugfs.c | |||
@@ -0,0 +1,241 @@ | |||
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 */ | ||
110 | |||
111 | int debugfs_mount(const char *mountpoint) | ||
112 | { | ||
113 | char mountcmd[128]; | ||
114 | |||
115 | /* see if it's already mounted */ | ||
116 | if (debugfs_find_mountpoint()) { | ||
117 | debugfs_premounted = 1; | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | /* if not mounted and no argument */ | ||
122 | if (mountpoint == NULL) { | ||
123 | /* see if environment variable set */ | ||
124 | mountpoint = getenv(PERF_DEBUGFS_ENVIRONMENT); | ||
125 | /* if no environment variable, use default */ | ||
126 | if (mountpoint == NULL) | ||
127 | mountpoint = "/sys/kernel/debug"; | ||
128 | } | ||
129 | |||
130 | /* save the mountpoint */ | ||
131 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); | ||
132 | |||
133 | /* mount it */ | ||
134 | snprintf(mountcmd, sizeof(mountcmd), | ||
135 | "/bin/mount -t debugfs debugfs %s", mountpoint); | ||
136 | return system(mountcmd); | ||
137 | } | ||
138 | |||
139 | /* umount the debugfs */ | ||
140 | |||
141 | int debugfs_umount(void) | ||
142 | { | ||
143 | char umountcmd[128]; | ||
144 | int ret; | ||
145 | |||
146 | /* if it was already mounted, leave it */ | ||
147 | if (debugfs_premounted) | ||
148 | return 0; | ||
149 | |||
150 | /* make sure it's a valid mount point */ | ||
151 | ret = debugfs_valid_mountpoint(debugfs_mountpoint); | ||
152 | if (ret) | ||
153 | return ret; | ||
154 | |||
155 | snprintf(umountcmd, sizeof(umountcmd), | ||
156 | "/bin/umount %s", debugfs_mountpoint); | ||
157 | return system(umountcmd); | ||
158 | } | ||
159 | |||
160 | int debugfs_write(const char *entry, const char *value) | ||
161 | { | ||
162 | char path[MAX_PATH+1]; | ||
163 | int ret, count; | ||
164 | int fd; | ||
165 | |||
166 | /* construct the path */ | ||
167 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
168 | |||
169 | /* verify that it exists */ | ||
170 | ret = debugfs_valid_entry(path); | ||
171 | if (ret) | ||
172 | return ret; | ||
173 | |||
174 | /* get how many chars we're going to write */ | ||
175 | count = strlen(value); | ||
176 | |||
177 | /* open the debugfs entry */ | ||
178 | fd = open(path, O_RDWR); | ||
179 | if (fd < 0) | ||
180 | return -errno; | ||
181 | |||
182 | while (count > 0) { | ||
183 | /* write it */ | ||
184 | ret = write(fd, value, count); | ||
185 | if (ret <= 0) { | ||
186 | if (ret == EAGAIN) | ||
187 | continue; | ||
188 | close(fd); | ||
189 | return -errno; | ||
190 | } | ||
191 | count -= ret; | ||
192 | } | ||
193 | |||
194 | /* close it */ | ||
195 | close(fd); | ||
196 | |||
197 | /* return success */ | ||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * read a debugfs entry | ||
203 | * returns the number of chars read or a negative errno | ||
204 | */ | ||
205 | int debugfs_read(const char *entry, char *buffer, size_t size) | ||
206 | { | ||
207 | char path[MAX_PATH+1]; | ||
208 | int ret; | ||
209 | int fd; | ||
210 | |||
211 | /* construct the path */ | ||
212 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
213 | |||
214 | /* verify that it exists */ | ||
215 | ret = debugfs_valid_entry(path); | ||
216 | if (ret) | ||
217 | return ret; | ||
218 | |||
219 | /* open the debugfs entry */ | ||
220 | fd = open(path, O_RDONLY); | ||
221 | if (fd < 0) | ||
222 | return -errno; | ||
223 | |||
224 | do { | ||
225 | /* read it */ | ||
226 | ret = read(fd, buffer, size); | ||
227 | if (ret == 0) { | ||
228 | close(fd); | ||
229 | return EOF; | ||
230 | } | ||
231 | } while (ret < 0 && errno == EAGAIN); | ||
232 | |||
233 | /* close it */ | ||
234 | close(fd); | ||
235 | |||
236 | /* make *sure* there's a null character at the end */ | ||
237 | buffer[ret] = '\0'; | ||
238 | |||
239 | /* return the number of chars read */ | ||
240 | return ret; | ||
241 | } | ||
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h new file mode 100644 index 000000000000..3cd14f9ae784 --- /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 int 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..1dae7e3b400d --- /dev/null +++ b/tools/perf/util/event.c | |||
@@ -0,0 +1,177 @@ | |||
1 | #include <linux/types.h> | ||
2 | #include "event.h" | ||
3 | #include "debug.h" | ||
4 | #include "string.h" | ||
5 | |||
6 | static pid_t event__synthesize_comm(pid_t pid, int full, | ||
7 | int (*process)(event_t *event)) | ||
8 | { | ||
9 | event_t ev; | ||
10 | char filename[PATH_MAX]; | ||
11 | char bf[BUFSIZ]; | ||
12 | FILE *fp; | ||
13 | size_t size = 0; | ||
14 | DIR *tasks; | ||
15 | struct dirent dirent, *next; | ||
16 | pid_t tgid = 0; | ||
17 | |||
18 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); | ||
19 | |||
20 | fp = fopen(filename, "r"); | ||
21 | if (fp == NULL) { | ||
22 | out_race: | ||
23 | /* | ||
24 | * We raced with a task exiting - just return: | ||
25 | */ | ||
26 | pr_debug("couldn't open %s\n", filename); | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | memset(&ev.comm, 0, sizeof(ev.comm)); | ||
31 | while (!ev.comm.comm[0] || !ev.comm.pid) { | ||
32 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
33 | goto out_failure; | ||
34 | |||
35 | if (memcmp(bf, "Name:", 5) == 0) { | ||
36 | char *name = bf + 5; | ||
37 | while (*name && isspace(*name)) | ||
38 | ++name; | ||
39 | size = strlen(name) - 1; | ||
40 | memcpy(ev.comm.comm, name, size++); | ||
41 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | ||
42 | char *tgids = bf + 5; | ||
43 | while (*tgids && isspace(*tgids)) | ||
44 | ++tgids; | ||
45 | tgid = ev.comm.pid = atoi(tgids); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | ev.comm.header.type = PERF_RECORD_COMM; | ||
50 | size = ALIGN(size, sizeof(u64)); | ||
51 | ev.comm.header.size = sizeof(ev.comm) - (sizeof(ev.comm.comm) - size); | ||
52 | |||
53 | if (!full) { | ||
54 | ev.comm.tid = pid; | ||
55 | |||
56 | process(&ev); | ||
57 | goto out_fclose; | ||
58 | } | ||
59 | |||
60 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | ||
61 | |||
62 | tasks = opendir(filename); | ||
63 | if (tasks == NULL) | ||
64 | goto out_race; | ||
65 | |||
66 | while (!readdir_r(tasks, &dirent, &next) && next) { | ||
67 | char *end; | ||
68 | pid = strtol(dirent.d_name, &end, 10); | ||
69 | if (*end) | ||
70 | continue; | ||
71 | |||
72 | ev.comm.tid = pid; | ||
73 | |||
74 | process(&ev); | ||
75 | } | ||
76 | closedir(tasks); | ||
77 | |||
78 | out_fclose: | ||
79 | fclose(fp); | ||
80 | return tgid; | ||
81 | |||
82 | out_failure: | ||
83 | pr_warning("couldn't get COMM and pgid, malformed %s\n", filename); | ||
84 | return -1; | ||
85 | } | ||
86 | |||
87 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | ||
88 | int (*process)(event_t *event)) | ||
89 | { | ||
90 | char filename[PATH_MAX]; | ||
91 | FILE *fp; | ||
92 | |||
93 | snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); | ||
94 | |||
95 | fp = fopen(filename, "r"); | ||
96 | if (fp == NULL) { | ||
97 | /* | ||
98 | * We raced with a task exiting - just return: | ||
99 | */ | ||
100 | pr_debug("couldn't open %s\n", filename); | ||
101 | return -1; | ||
102 | } | ||
103 | |||
104 | while (1) { | ||
105 | char bf[BUFSIZ], *pbf = bf; | ||
106 | event_t ev = { | ||
107 | .header = { .type = PERF_RECORD_MMAP }, | ||
108 | }; | ||
109 | int n; | ||
110 | size_t size; | ||
111 | if (fgets(bf, sizeof(bf), fp) == NULL) | ||
112 | break; | ||
113 | |||
114 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | ||
115 | n = hex2u64(pbf, &ev.mmap.start); | ||
116 | if (n < 0) | ||
117 | continue; | ||
118 | pbf += n + 1; | ||
119 | n = hex2u64(pbf, &ev.mmap.len); | ||
120 | if (n < 0) | ||
121 | continue; | ||
122 | pbf += n + 3; | ||
123 | if (*pbf == 'x') { /* vm_exec */ | ||
124 | char *execname = strchr(bf, '/'); | ||
125 | |||
126 | /* Catch VDSO */ | ||
127 | if (execname == NULL) | ||
128 | execname = strstr(bf, "[vdso]"); | ||
129 | |||
130 | if (execname == NULL) | ||
131 | continue; | ||
132 | |||
133 | size = strlen(execname); | ||
134 | execname[size - 1] = '\0'; /* Remove \n */ | ||
135 | memcpy(ev.mmap.filename, execname, size); | ||
136 | size = ALIGN(size, sizeof(u64)); | ||
137 | ev.mmap.len -= ev.mmap.start; | ||
138 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
139 | (sizeof(ev.mmap.filename) - size)); | ||
140 | ev.mmap.pid = tgid; | ||
141 | ev.mmap.tid = pid; | ||
142 | |||
143 | process(&ev); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | fclose(fp); | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)) | ||
152 | { | ||
153 | pid_t tgid = event__synthesize_comm(pid, 1, process); | ||
154 | if (tgid == -1) | ||
155 | return -1; | ||
156 | return event__synthesize_mmap_events(pid, tgid, process); | ||
157 | } | ||
158 | |||
159 | void event__synthesize_threads(int (*process)(event_t *event)) | ||
160 | { | ||
161 | DIR *proc; | ||
162 | struct dirent dirent, *next; | ||
163 | |||
164 | proc = opendir("/proc"); | ||
165 | |||
166 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
167 | char *end; | ||
168 | pid_t pid = strtol(dirent.d_name, &end, 10); | ||
169 | |||
170 | if (*end) /* only interested in proper numerical dirents */ | ||
171 | continue; | ||
172 | |||
173 | event__synthesize_thread(pid, process); | ||
174 | } | ||
175 | |||
176 | closedir(proc); | ||
177 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index d972b4b0d38c..1f771ce3a957 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -61,6 +61,20 @@ struct sample_event{ | |||
61 | u64 array[]; | 61 | u64 array[]; |
62 | }; | 62 | }; |
63 | 63 | ||
64 | #define BUILD_ID_SIZE 20 | ||
65 | |||
66 | struct build_id_event { | ||
67 | struct perf_event_header header; | ||
68 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
69 | char filename[]; | ||
70 | }; | ||
71 | |||
72 | struct build_id_list { | ||
73 | struct build_id_event event; | ||
74 | struct list_head list; | ||
75 | const char *dso_name; | ||
76 | int len; | ||
77 | }; | ||
64 | 78 | ||
65 | typedef union event_union { | 79 | typedef union event_union { |
66 | struct perf_event_header header; | 80 | struct perf_event_header header; |
@@ -105,10 +119,15 @@ struct symbol; | |||
105 | 119 | ||
106 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | 120 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); |
107 | 121 | ||
108 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen, | 122 | void map__init(struct map *self, u64 start, u64 end, u64 pgoff, |
109 | unsigned int sym_priv_size, symbol_filter_t filter); | 123 | struct dso *dso); |
124 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen); | ||
110 | struct map *map__clone(struct map *self); | 125 | struct map *map__clone(struct map *self); |
111 | int map__overlap(struct map *l, struct map *r); | 126 | int map__overlap(struct map *l, struct map *r); |
112 | size_t map__fprintf(struct map *self, FILE *fp); | 127 | size_t map__fprintf(struct map *self, FILE *fp); |
128 | struct symbol *map__find_symbol(struct map *self, u64 ip, symbol_filter_t filter); | ||
129 | |||
130 | int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)); | ||
131 | void event__synthesize_threads(int (*process)(event_t *event)); | ||
113 | 132 | ||
114 | #endif /* __PERF_RECORD_H */ | 133 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 7d26659b806c..b01a9537977f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -2,11 +2,15 @@ | |||
2 | #include <unistd.h> | 2 | #include <unistd.h> |
3 | #include <stdio.h> | 3 | #include <stdio.h> |
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include <linux/list.h> | ||
5 | 6 | ||
6 | #include "util.h" | 7 | #include "util.h" |
7 | #include "header.h" | 8 | #include "header.h" |
8 | #include "../perf.h" | 9 | #include "../perf.h" |
9 | #include "trace-event.h" | 10 | #include "trace-event.h" |
11 | #include "symbol.h" | ||
12 | #include "data_map.h" | ||
13 | #include "debug.h" | ||
10 | 14 | ||
11 | /* | 15 | /* |
12 | * Create new perf.data header attribute: | 16 | * Create new perf.data header attribute: |
@@ -15,32 +19,43 @@ struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) | |||
15 | { | 19 | { |
16 | struct perf_header_attr *self = malloc(sizeof(*self)); | 20 | struct perf_header_attr *self = malloc(sizeof(*self)); |
17 | 21 | ||
18 | if (!self) | 22 | if (self != NULL) { |
19 | die("nomem"); | 23 | self->attr = *attr; |
20 | 24 | self->ids = 0; | |
21 | self->attr = *attr; | 25 | self->size = 1; |
22 | self->ids = 0; | 26 | self->id = malloc(sizeof(u64)); |
23 | self->size = 1; | 27 | if (self->id == NULL) { |
24 | self->id = malloc(sizeof(u64)); | 28 | free(self); |
25 | 29 | self = NULL; | |
26 | if (!self->id) | 30 | } |
27 | die("nomem"); | 31 | } |
28 | 32 | ||
29 | return self; | 33 | return self; |
30 | } | 34 | } |
31 | 35 | ||
32 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | 36 | void perf_header_attr__delete(struct perf_header_attr *self) |
37 | { | ||
38 | free(self->id); | ||
39 | free(self); | ||
40 | } | ||
41 | |||
42 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | ||
33 | { | 43 | { |
34 | int pos = self->ids; | 44 | int pos = self->ids; |
35 | 45 | ||
36 | self->ids++; | 46 | self->ids++; |
37 | if (self->ids > self->size) { | 47 | if (self->ids > self->size) { |
38 | self->size *= 2; | 48 | int nsize = self->size * 2; |
39 | self->id = realloc(self->id, self->size * sizeof(u64)); | 49 | u64 *nid = realloc(self->id, nsize * sizeof(u64)); |
40 | if (!self->id) | 50 | |
41 | die("nomem"); | 51 | if (nid == NULL) |
52 | return -1; | ||
53 | |||
54 | self->size = nsize; | ||
55 | self->id = nid; | ||
42 | } | 56 | } |
43 | self->id[pos] = id; | 57 | self->id[pos] = id; |
58 | return 0; | ||
44 | } | 59 | } |
45 | 60 | ||
46 | /* | 61 | /* |
@@ -50,34 +65,41 @@ struct perf_header *perf_header__new(void) | |||
50 | { | 65 | { |
51 | struct perf_header *self = calloc(sizeof(*self), 1); | 66 | struct perf_header *self = calloc(sizeof(*self), 1); |
52 | 67 | ||
53 | if (!self) | 68 | if (self != NULL) { |
54 | die("nomem"); | 69 | self->size = 1; |
55 | 70 | self->attr = malloc(sizeof(void *)); | |
56 | self->size = 1; | ||
57 | self->attr = malloc(sizeof(void *)); | ||
58 | 71 | ||
59 | if (!self->attr) | 72 | if (self->attr == NULL) { |
60 | die("nomem"); | 73 | free(self); |
74 | self = NULL; | ||
75 | } | ||
76 | } | ||
61 | 77 | ||
62 | return self; | 78 | return self; |
63 | } | 79 | } |
64 | 80 | ||
65 | void perf_header__add_attr(struct perf_header *self, | 81 | int perf_header__add_attr(struct perf_header *self, |
66 | struct perf_header_attr *attr) | 82 | struct perf_header_attr *attr) |
67 | { | 83 | { |
68 | int pos = self->attrs; | 84 | int pos = self->attrs; |
69 | 85 | ||
70 | if (self->frozen) | 86 | if (self->frozen) |
71 | die("frozen"); | 87 | return -1; |
72 | 88 | ||
73 | self->attrs++; | 89 | self->attrs++; |
74 | if (self->attrs > self->size) { | 90 | if (self->attrs > self->size) { |
75 | self->size *= 2; | 91 | int nsize = self->size * 2; |
76 | self->attr = realloc(self->attr, self->size * sizeof(void *)); | 92 | struct perf_header_attr **nattr; |
77 | if (!self->attr) | 93 | |
78 | die("nomem"); | 94 | nattr = realloc(self->attr, nsize * sizeof(void *)); |
95 | if (nattr == NULL) | ||
96 | return -1; | ||
97 | |||
98 | self->size = nsize; | ||
99 | self->attr = nattr; | ||
79 | } | 100 | } |
80 | self->attr[pos] = attr; | 101 | self->attr[pos] = attr; |
102 | return 0; | ||
81 | } | 103 | } |
82 | 104 | ||
83 | #define MAX_EVENT_NAME 64 | 105 | #define MAX_EVENT_NAME 64 |
@@ -124,71 +146,110 @@ static const char *__perf_magic = "PERFFILE"; | |||
124 | 146 | ||
125 | #define PERF_MAGIC (*(u64 *)__perf_magic) | 147 | #define PERF_MAGIC (*(u64 *)__perf_magic) |
126 | 148 | ||
127 | struct perf_file_section { | ||
128 | u64 offset; | ||
129 | u64 size; | ||
130 | }; | ||
131 | |||
132 | struct perf_file_attr { | 149 | struct perf_file_attr { |
133 | struct perf_event_attr attr; | 150 | struct perf_event_attr attr; |
134 | struct perf_file_section ids; | 151 | struct perf_file_section ids; |
135 | }; | 152 | }; |
136 | 153 | ||
137 | struct perf_file_header { | 154 | void perf_header__set_feat(struct perf_header *self, int feat) |
138 | u64 magic; | 155 | { |
139 | u64 size; | 156 | set_bit(feat, self->adds_features); |
140 | u64 attr_size; | 157 | } |
141 | struct perf_file_section attrs; | ||
142 | struct perf_file_section data; | ||
143 | struct perf_file_section event_types; | ||
144 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | ||
145 | }; | ||
146 | 158 | ||
147 | void perf_header__feat_trace_info(struct perf_header *header) | 159 | bool perf_header__has_feat(const struct perf_header *self, int feat) |
148 | { | 160 | { |
149 | set_bit(HEADER_TRACE_INFO, header->adds_features); | 161 | return test_bit(feat, self->adds_features); |
150 | } | 162 | } |
151 | 163 | ||
152 | static void do_write(int fd, void *buf, size_t size) | 164 | static int do_write(int fd, const void *buf, size_t size) |
153 | { | 165 | { |
154 | while (size) { | 166 | while (size) { |
155 | int ret = write(fd, buf, size); | 167 | int ret = write(fd, buf, size); |
156 | 168 | ||
157 | if (ret < 0) | 169 | if (ret < 0) |
158 | die("failed to write"); | 170 | return -1; |
159 | 171 | ||
160 | size -= ret; | 172 | size -= ret; |
161 | buf += ret; | 173 | buf += ret; |
162 | } | 174 | } |
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static int write_buildid_table(int fd, struct list_head *id_head) | ||
180 | { | ||
181 | struct build_id_list *iter, *next; | ||
182 | |||
183 | list_for_each_entry_safe(iter, next, id_head, list) { | ||
184 | struct build_id_event *b = &iter->event; | ||
185 | |||
186 | if (do_write(fd, b, sizeof(*b)) < 0 || | ||
187 | do_write(fd, iter->dso_name, iter->len) < 0) | ||
188 | return -1; | ||
189 | list_del(&iter->list); | ||
190 | free(iter); | ||
191 | } | ||
192 | |||
193 | return 0; | ||
163 | } | 194 | } |
164 | 195 | ||
165 | static void perf_header__adds_write(struct perf_header *self, int fd) | 196 | static void |
197 | perf_header__adds_write(struct perf_header *self, int fd) | ||
166 | { | 198 | { |
167 | struct perf_file_section trace_sec; | 199 | LIST_HEAD(id_list); |
168 | u64 cur_offset = lseek(fd, 0, SEEK_CUR); | 200 | int nr_sections; |
169 | unsigned long *feat_mask = self->adds_features; | 201 | struct perf_file_section *feat_sec; |
202 | int sec_size; | ||
203 | u64 sec_start; | ||
204 | int idx = 0; | ||
205 | |||
206 | if (fetch_build_id_table(&id_list)) | ||
207 | perf_header__set_feat(self, HEADER_BUILD_ID); | ||
208 | |||
209 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | ||
210 | if (!nr_sections) | ||
211 | return; | ||
212 | |||
213 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | ||
214 | if (!feat_sec) | ||
215 | die("No memory"); | ||
216 | |||
217 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
218 | |||
219 | sec_start = self->data_offset + self->data_size; | ||
220 | lseek(fd, sec_start + sec_size, SEEK_SET); | ||
221 | |||
222 | if (perf_header__has_feat(self, HEADER_TRACE_INFO)) { | ||
223 | struct perf_file_section *trace_sec; | ||
224 | |||
225 | trace_sec = &feat_sec[idx++]; | ||
170 | 226 | ||
171 | if (test_bit(HEADER_TRACE_INFO, feat_mask)) { | ||
172 | /* Write trace info */ | 227 | /* Write trace info */ |
173 | trace_sec.offset = lseek(fd, sizeof(trace_sec), SEEK_CUR); | 228 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); |
174 | read_tracing_data(fd, attrs, nr_counters); | 229 | read_tracing_data(fd, attrs, nr_counters); |
175 | trace_sec.size = lseek(fd, 0, SEEK_CUR) - trace_sec.offset; | 230 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; |
176 | |||
177 | /* Write trace info headers */ | ||
178 | lseek(fd, cur_offset, SEEK_SET); | ||
179 | do_write(fd, &trace_sec, sizeof(trace_sec)); | ||
180 | |||
181 | /* | ||
182 | * Update cur_offset. So that other (future) | ||
183 | * features can set their own infos in this place. But if we are | ||
184 | * the only feature, at least that seeks to the place the data | ||
185 | * should begin. | ||
186 | */ | ||
187 | cur_offset = lseek(fd, trace_sec.offset + trace_sec.size, SEEK_SET); | ||
188 | } | 231 | } |
189 | }; | ||
190 | 232 | ||
191 | void perf_header__write(struct perf_header *self, int fd) | 233 | |
234 | if (perf_header__has_feat(self, HEADER_BUILD_ID)) { | ||
235 | struct perf_file_section *buildid_sec; | ||
236 | |||
237 | buildid_sec = &feat_sec[idx++]; | ||
238 | |||
239 | /* Write build-ids */ | ||
240 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | ||
241 | if (write_buildid_table(fd, &id_list) < 0) | ||
242 | die("failed to write buildid table"); | ||
243 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; | ||
244 | } | ||
245 | |||
246 | lseek(fd, sec_start, SEEK_SET); | ||
247 | if (do_write(fd, feat_sec, sec_size) < 0) | ||
248 | die("failed to write feature section"); | ||
249 | free(feat_sec); | ||
250 | } | ||
251 | |||
252 | void perf_header__write(struct perf_header *self, int fd, bool at_exit) | ||
192 | { | 253 | { |
193 | struct perf_file_header f_header; | 254 | struct perf_file_header f_header; |
194 | struct perf_file_attr f_attr; | 255 | struct perf_file_attr f_attr; |
@@ -202,7 +263,8 @@ void perf_header__write(struct perf_header *self, int fd) | |||
202 | attr = self->attr[i]; | 263 | attr = self->attr[i]; |
203 | 264 | ||
204 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 265 | attr->id_offset = lseek(fd, 0, SEEK_CUR); |
205 | do_write(fd, attr->id, attr->ids * sizeof(u64)); | 266 | if (do_write(fd, attr->id, attr->ids * sizeof(u64)) < 0) |
267 | die("failed to write perf header"); | ||
206 | } | 268 | } |
207 | 269 | ||
208 | 270 | ||
@@ -218,18 +280,21 @@ void perf_header__write(struct perf_header *self, int fd) | |||
218 | .size = attr->ids * sizeof(u64), | 280 | .size = attr->ids * sizeof(u64), |
219 | } | 281 | } |
220 | }; | 282 | }; |
221 | do_write(fd, &f_attr, sizeof(f_attr)); | 283 | if (do_write(fd, &f_attr, sizeof(f_attr)) < 0) |
284 | die("failed to write perf header attribute"); | ||
222 | } | 285 | } |
223 | 286 | ||
224 | self->event_offset = lseek(fd, 0, SEEK_CUR); | 287 | self->event_offset = lseek(fd, 0, SEEK_CUR); |
225 | self->event_size = event_count * sizeof(struct perf_trace_event_type); | 288 | self->event_size = event_count * sizeof(struct perf_trace_event_type); |
226 | if (events) | 289 | if (events) |
227 | do_write(fd, events, self->event_size); | 290 | if (do_write(fd, events, self->event_size) < 0) |
228 | 291 | die("failed to write perf header events"); | |
229 | perf_header__adds_write(self, fd); | ||
230 | 292 | ||
231 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 293 | self->data_offset = lseek(fd, 0, SEEK_CUR); |
232 | 294 | ||
295 | if (at_exit) | ||
296 | perf_header__adds_write(self, fd); | ||
297 | |||
233 | f_header = (struct perf_file_header){ | 298 | f_header = (struct perf_file_header){ |
234 | .magic = PERF_MAGIC, | 299 | .magic = PERF_MAGIC, |
235 | .size = sizeof(f_header), | 300 | .size = sizeof(f_header), |
@@ -251,7 +316,8 @@ void perf_header__write(struct perf_header *self, int fd) | |||
251 | memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); | 316 | memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); |
252 | 317 | ||
253 | lseek(fd, 0, SEEK_SET); | 318 | lseek(fd, 0, SEEK_SET); |
254 | do_write(fd, &f_header, sizeof(f_header)); | 319 | if (do_write(fd, &f_header, sizeof(f_header)) < 0) |
320 | die("failed to write perf header"); | ||
255 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 321 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); |
256 | 322 | ||
257 | self->frozen = 1; | 323 | self->frozen = 1; |
@@ -272,43 +338,112 @@ static void do_read(int fd, void *buf, size_t size) | |||
272 | } | 338 | } |
273 | } | 339 | } |
274 | 340 | ||
275 | static void perf_header__adds_read(struct perf_header *self, int fd) | 341 | int perf_header__process_sections(struct perf_header *self, int fd, |
342 | int (*process)(struct perf_file_section *self, | ||
343 | int feat, int fd)) | ||
276 | { | 344 | { |
277 | const unsigned long *feat_mask = self->adds_features; | 345 | struct perf_file_section *feat_sec; |
346 | int nr_sections; | ||
347 | int sec_size; | ||
348 | int idx = 0; | ||
349 | int err = 0, feat = 1; | ||
278 | 350 | ||
279 | if (test_bit(HEADER_TRACE_INFO, feat_mask)) { | 351 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); |
280 | struct perf_file_section trace_sec; | 352 | if (!nr_sections) |
353 | return 0; | ||
281 | 354 | ||
282 | do_read(fd, &trace_sec, sizeof(trace_sec)); | 355 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); |
283 | lseek(fd, trace_sec.offset, SEEK_SET); | 356 | if (!feat_sec) |
284 | trace_report(fd); | 357 | return -1; |
285 | lseek(fd, trace_sec.offset + trace_sec.size, SEEK_SET); | 358 | |
359 | sec_size = sizeof(*feat_sec) * nr_sections; | ||
360 | |||
361 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | ||
362 | |||
363 | do_read(fd, feat_sec, sec_size); | ||
364 | |||
365 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | ||
366 | if (perf_header__has_feat(self, feat)) { | ||
367 | struct perf_file_section *sec = &feat_sec[idx++]; | ||
368 | |||
369 | err = process(sec, feat, fd); | ||
370 | if (err < 0) | ||
371 | break; | ||
372 | } | ||
373 | ++feat; | ||
286 | } | 374 | } |
375 | |||
376 | free(feat_sec); | ||
377 | return err; | ||
287 | }; | 378 | }; |
288 | 379 | ||
380 | int perf_file_header__read(struct perf_file_header *self, | ||
381 | struct perf_header *ph, int fd) | ||
382 | { | ||
383 | lseek(fd, 0, SEEK_SET); | ||
384 | do_read(fd, self, sizeof(*self)); | ||
385 | |||
386 | if (self->magic != PERF_MAGIC || | ||
387 | self->attr_size != sizeof(struct perf_file_attr)) | ||
388 | return -1; | ||
389 | |||
390 | if (self->size != sizeof(*self)) { | ||
391 | /* Support the previous format */ | ||
392 | if (self->size == offsetof(typeof(*self), adds_features)) | ||
393 | bitmap_zero(self->adds_features, HEADER_FEAT_BITS); | ||
394 | else | ||
395 | return -1; | ||
396 | } | ||
397 | |||
398 | memcpy(&ph->adds_features, &self->adds_features, | ||
399 | sizeof(self->adds_features)); | ||
400 | |||
401 | ph->event_offset = self->event_types.offset; | ||
402 | ph->event_size = self->event_types.size; | ||
403 | ph->data_offset = self->data.offset; | ||
404 | ph->data_size = self->data.size; | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static int perf_file_section__process(struct perf_file_section *self, | ||
409 | int feat, int fd) | ||
410 | { | ||
411 | if (lseek(fd, self->offset, SEEK_SET) < 0) { | ||
412 | pr_debug("Failed to lseek to %Ld offset for feature %d, " | ||
413 | "continuing...\n", self->offset, feat); | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | switch (feat) { | ||
418 | case HEADER_TRACE_INFO: | ||
419 | trace_report(fd); | ||
420 | break; | ||
421 | |||
422 | case HEADER_BUILD_ID: | ||
423 | if (perf_header__read_build_ids(fd, self->offset, self->size)) | ||
424 | pr_debug("Failed to read buildids, continuing...\n"); | ||
425 | break; | ||
426 | default: | ||
427 | pr_debug("unknown feature %d, continuing...\n", feat); | ||
428 | } | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
289 | struct perf_header *perf_header__read(int fd) | 433 | struct perf_header *perf_header__read(int fd) |
290 | { | 434 | { |
291 | struct perf_header *self = perf_header__new(); | 435 | struct perf_header *self = perf_header__new(); |
292 | struct perf_file_header f_header; | 436 | struct perf_file_header f_header; |
293 | struct perf_file_attr f_attr; | 437 | struct perf_file_attr f_attr; |
294 | u64 f_id; | 438 | u64 f_id; |
295 | |||
296 | int nr_attrs, nr_ids, i, j; | 439 | int nr_attrs, nr_ids, i, j; |
297 | 440 | ||
298 | lseek(fd, 0, SEEK_SET); | 441 | if (self == NULL) |
299 | do_read(fd, &f_header, sizeof(f_header)); | 442 | die("nomem"); |
300 | 443 | ||
301 | if (f_header.magic != PERF_MAGIC || | 444 | if (perf_file_header__read(&f_header, self, fd) < 0) |
302 | f_header.attr_size != sizeof(f_attr)) | ||
303 | die("incompatible file format"); | 445 | die("incompatible file format"); |
304 | 446 | ||
305 | if (f_header.size != sizeof(f_header)) { | ||
306 | /* Support the previous format */ | ||
307 | if (f_header.size == offsetof(typeof(f_header), adds_features)) | ||
308 | bitmap_zero(f_header.adds_features, HEADER_FEAT_BITS); | ||
309 | else | ||
310 | die("incompatible file format"); | ||
311 | } | ||
312 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | 447 | nr_attrs = f_header.attrs.size / sizeof(f_attr); |
313 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 448 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
314 | 449 | ||
@@ -320,6 +455,8 @@ struct perf_header *perf_header__read(int fd) | |||
320 | tmp = lseek(fd, 0, SEEK_CUR); | 455 | tmp = lseek(fd, 0, SEEK_CUR); |
321 | 456 | ||
322 | attr = perf_header_attr__new(&f_attr.attr); | 457 | attr = perf_header_attr__new(&f_attr.attr); |
458 | if (attr == NULL) | ||
459 | die("nomem"); | ||
323 | 460 | ||
324 | nr_ids = f_attr.ids.size / sizeof(u64); | 461 | nr_ids = f_attr.ids.size / sizeof(u64); |
325 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 462 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
@@ -327,9 +464,12 @@ struct perf_header *perf_header__read(int fd) | |||
327 | for (j = 0; j < nr_ids; j++) { | 464 | for (j = 0; j < nr_ids; j++) { |
328 | do_read(fd, &f_id, sizeof(f_id)); | 465 | do_read(fd, &f_id, sizeof(f_id)); |
329 | 466 | ||
330 | perf_header_attr__add_id(attr, f_id); | 467 | if (perf_header_attr__add_id(attr, f_id) < 0) |
468 | die("nomem"); | ||
331 | } | 469 | } |
332 | perf_header__add_attr(self, attr); | 470 | if (perf_header__add_attr(self, attr) < 0) |
471 | die("nomem"); | ||
472 | |||
333 | lseek(fd, tmp, SEEK_SET); | 473 | lseek(fd, tmp, SEEK_SET); |
334 | } | 474 | } |
335 | 475 | ||
@@ -342,15 +482,7 @@ struct perf_header *perf_header__read(int fd) | |||
342 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 482 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
343 | } | 483 | } |
344 | 484 | ||
345 | memcpy(&self->adds_features, &f_header.adds_features, sizeof(f_header.adds_features)); | 485 | perf_header__process_sections(self, fd, perf_file_section__process); |
346 | |||
347 | perf_header__adds_read(self, fd); | ||
348 | |||
349 | self->event_offset = f_header.event_types.offset; | ||
350 | self->event_size = f_header.event_types.size; | ||
351 | |||
352 | self->data_offset = f_header.data.offset; | ||
353 | self->data_size = f_header.data.size; | ||
354 | 486 | ||
355 | lseek(fd, self->data_offset, SEEK_SET); | 487 | lseek(fd, self->data_offset, SEEK_SET); |
356 | 488 | ||
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 2ea9dfb1236a..f46a94e09eea 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -3,6 +3,7 @@ | |||
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" |
7 | 8 | ||
8 | #include <linux/bitmap.h> | 9 | #include <linux/bitmap.h> |
@@ -14,10 +15,34 @@ struct perf_header_attr { | |||
14 | off_t id_offset; | 15 | off_t id_offset; |
15 | }; | 16 | }; |
16 | 17 | ||
17 | #define HEADER_TRACE_INFO 1 | 18 | enum { |
19 | HEADER_TRACE_INFO = 1, | ||
20 | HEADER_BUILD_ID, | ||
21 | HEADER_LAST_FEATURE, | ||
22 | }; | ||
18 | 23 | ||
19 | #define HEADER_FEAT_BITS 256 | 24 | #define HEADER_FEAT_BITS 256 |
20 | 25 | ||
26 | struct perf_file_section { | ||
27 | u64 offset; | ||
28 | u64 size; | ||
29 | }; | ||
30 | |||
31 | struct perf_file_header { | ||
32 | u64 magic; | ||
33 | u64 size; | ||
34 | u64 attr_size; | ||
35 | struct perf_file_section attrs; | ||
36 | struct perf_file_section data; | ||
37 | struct perf_file_section event_types; | ||
38 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | ||
39 | }; | ||
40 | |||
41 | struct perf_header; | ||
42 | |||
43 | int perf_file_header__read(struct perf_file_header *self, | ||
44 | struct perf_header *ph, int fd); | ||
45 | |||
21 | struct perf_header { | 46 | struct perf_header { |
22 | int frozen; | 47 | int frozen; |
23 | int attrs, size; | 48 | int attrs, size; |
@@ -31,24 +56,29 @@ struct perf_header { | |||
31 | }; | 56 | }; |
32 | 57 | ||
33 | struct perf_header *perf_header__read(int fd); | 58 | struct perf_header *perf_header__read(int fd); |
34 | void perf_header__write(struct perf_header *self, int fd); | 59 | void perf_header__write(struct perf_header *self, int fd, bool at_exit); |
35 | 60 | ||
36 | void perf_header__add_attr(struct perf_header *self, | 61 | int perf_header__add_attr(struct perf_header *self, |
37 | struct perf_header_attr *attr); | 62 | struct perf_header_attr *attr); |
38 | 63 | ||
39 | void perf_header__push_event(u64 id, const char *name); | 64 | void perf_header__push_event(u64 id, const char *name); |
40 | char *perf_header__find_event(u64 id); | 65 | char *perf_header__find_event(u64 id); |
41 | 66 | ||
67 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); | ||
68 | void perf_header_attr__delete(struct perf_header_attr *self); | ||
42 | 69 | ||
43 | struct perf_header_attr * | 70 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); |
44 | perf_header_attr__new(struct perf_event_attr *attr); | ||
45 | void perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | ||
46 | 71 | ||
47 | u64 perf_header__sample_type(struct perf_header *header); | 72 | u64 perf_header__sample_type(struct perf_header *header); |
48 | struct perf_event_attr * | 73 | struct perf_event_attr * |
49 | perf_header__find_attr(u64 id, struct perf_header *header); | 74 | perf_header__find_attr(u64 id, struct perf_header *header); |
50 | void perf_header__feat_trace_info(struct perf_header *header); | 75 | void perf_header__set_feat(struct perf_header *self, int feat); |
76 | bool perf_header__has_feat(const struct perf_header *self, int feat); | ||
51 | 77 | ||
52 | struct perf_header *perf_header__new(void); | 78 | struct perf_header *perf_header__new(void); |
53 | 79 | ||
80 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
81 | int (*process)(struct perf_file_section *self, | ||
82 | int feat, int fd)); | ||
83 | |||
54 | #endif /* __PERF_HEADER_H */ | 84 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index 821c1033bccf..94507639a8c4 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h | |||
@@ -1,2 +1,3 @@ | |||
1 | #include "../../../../include/linux/bitmap.h" | 1 | #include "../../../../include/linux/bitmap.h" |
2 | #include "../../../../include/asm-generic/bitops/find.h" | 2 | #include "../../../../include/asm-generic/bitops/find.h" |
3 | #include <linux/errno.h> | ||
diff --git a/tools/perf/util/include/linux/ctype.h b/tools/perf/util/include/linux/ctype.h index bae5783282ef..a53d4ee1e0b7 100644 --- a/tools/perf/util/include/linux/ctype.h +++ b/tools/perf/util/include/linux/ctype.h | |||
@@ -1 +1 @@ | |||
#include "../../../../include/linux/ctype.h" | #include "../util.h" | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index c1c556825343..94ca95073c40 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -20,16 +20,27 @@ static int strcommon(const char *pathname, char *cwd, int cwdlen) | |||
20 | return n; | 20 | return n; |
21 | } | 21 | } |
22 | 22 | ||
23 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen, | 23 | void map__init(struct map *self, u64 start, u64 end, u64 pgoff, |
24 | unsigned int sym_priv_size, symbol_filter_t filter) | 24 | struct dso *dso) |
25 | { | ||
26 | self->start = start; | ||
27 | self->end = end; | ||
28 | self->pgoff = pgoff; | ||
29 | self->dso = dso; | ||
30 | self->map_ip = map__map_ip; | ||
31 | self->unmap_ip = map__unmap_ip; | ||
32 | RB_CLEAR_NODE(&self->rb_node); | ||
33 | } | ||
34 | |||
35 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen) | ||
25 | { | 36 | { |
26 | struct map *self = malloc(sizeof(*self)); | 37 | struct map *self = malloc(sizeof(*self)); |
27 | 38 | ||
28 | if (self != NULL) { | 39 | if (self != NULL) { |
29 | const char *filename = event->filename; | 40 | const char *filename = event->filename; |
30 | char newfilename[PATH_MAX]; | 41 | char newfilename[PATH_MAX]; |
42 | struct dso *dso; | ||
31 | int anon; | 43 | int anon; |
32 | bool new_dso; | ||
33 | 44 | ||
34 | if (cwd) { | 45 | if (cwd) { |
35 | int n = strcommon(filename, cwd, cwdlen); | 46 | int n = strcommon(filename, cwd, cwdlen); |
@@ -48,33 +59,15 @@ struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen, | |||
48 | filename = newfilename; | 59 | filename = newfilename; |
49 | } | 60 | } |
50 | 61 | ||
51 | self->start = event->start; | 62 | dso = dsos__findnew(filename); |
52 | self->end = event->start + event->len; | 63 | if (dso == NULL) |
53 | self->pgoff = event->pgoff; | ||
54 | |||
55 | self->dso = dsos__findnew(filename, sym_priv_size, &new_dso); | ||
56 | if (self->dso == NULL) | ||
57 | goto out_delete; | 64 | goto out_delete; |
58 | 65 | ||
59 | if (new_dso) { | 66 | map__init(self, event->start, event->start + event->len, |
60 | int nr = dso__load(self->dso, self, filter); | 67 | event->pgoff, dso); |
61 | |||
62 | if (nr < 0) | ||
63 | pr_warning("Failed to open %s, continuing " | ||
64 | "without symbols\n", | ||
65 | self->dso->long_name); | ||
66 | else if (nr == 0) | ||
67 | pr_warning("No symbols found in %s, maybe " | ||
68 | "install a debug package?\n", | ||
69 | self->dso->long_name); | ||
70 | } | ||
71 | 68 | ||
72 | if (self->dso == vdso || anon) | 69 | if (self->dso == vdso || anon) |
73 | self->map_ip = self->unmap_ip = identity__map_ip; | 70 | self->map_ip = self->unmap_ip = identity__map_ip; |
74 | else { | ||
75 | self->map_ip = map__map_ip; | ||
76 | self->unmap_ip = map__unmap_ip; | ||
77 | } | ||
78 | } | 71 | } |
79 | return self; | 72 | return self; |
80 | out_delete: | 73 | out_delete: |
@@ -82,6 +75,47 @@ out_delete: | |||
82 | return NULL; | 75 | return NULL; |
83 | } | 76 | } |
84 | 77 | ||
78 | #define DSO__DELETED "(deleted)" | ||
79 | |||
80 | struct symbol * | ||
81 | map__find_symbol(struct map *self, u64 ip, symbol_filter_t filter) | ||
82 | { | ||
83 | if (!self->dso->loaded) { | ||
84 | int nr = dso__load(self->dso, self, filter); | ||
85 | |||
86 | if (nr < 0) { | ||
87 | if (self->dso->has_build_id) { | ||
88 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
89 | |||
90 | build_id__sprintf(self->dso->build_id, | ||
91 | sizeof(self->dso->build_id), | ||
92 | sbuild_id); | ||
93 | pr_warning("%s with build id %s not found", | ||
94 | self->dso->long_name, sbuild_id); | ||
95 | } else | ||
96 | pr_warning("Failed to open %s", | ||
97 | self->dso->long_name); | ||
98 | pr_warning(", continuing without symbols\n"); | ||
99 | return NULL; | ||
100 | } else if (nr == 0) { | ||
101 | const char *name = self->dso->long_name; | ||
102 | const size_t len = strlen(name); | ||
103 | const size_t real_len = len - sizeof(DSO__DELETED); | ||
104 | |||
105 | if (len > sizeof(DSO__DELETED) && | ||
106 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | ||
107 | pr_warning("%.*s was updated, restart the long running apps that use it!\n", | ||
108 | (int)real_len, name); | ||
109 | } else { | ||
110 | pr_warning("no symbols found in %s, maybe install a debug package?\n", name); | ||
111 | } | ||
112 | return NULL; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | return self->dso->find_symbol(self->dso, ip); | ||
117 | } | ||
118 | |||
85 | struct map *map__clone(struct map *self) | 119 | struct map *map__clone(struct map *self) |
86 | { | 120 | { |
87 | struct map *map = malloc(sizeof(*self)); | 121 | struct map *map = malloc(sizeof(*self)); |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index b097570e9623..0faf4f2bb5ca 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -7,6 +7,7 @@ | |||
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 | ||
@@ -47,6 +48,8 @@ static struct event_symbol event_symbols[] = { | |||
47 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, | 48 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, |
48 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, | 49 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, |
49 | { 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", "" }, | ||
50 | }; | 53 | }; |
51 | 54 | ||
52 | #define __PERF_EVENT_FIELD(config, name) \ | 55 | #define __PERF_EVENT_FIELD(config, name) \ |
@@ -75,6 +78,8 @@ static const char *sw_event_names[] = { | |||
75 | "CPU-migrations", | 78 | "CPU-migrations", |
76 | "minor-faults", | 79 | "minor-faults", |
77 | "major-faults", | 80 | "major-faults", |
81 | "alignment-faults", | ||
82 | "emulation-faults", | ||
78 | }; | 83 | }; |
79 | 84 | ||
80 | #define MAX_ALIASES 8 | 85 | #define MAX_ALIASES 8 |
@@ -149,16 +154,6 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) | |||
149 | 154 | ||
150 | #define MAX_EVENT_LENGTH 512 | 155 | #define MAX_EVENT_LENGTH 512 |
151 | 156 | ||
152 | int valid_debugfs_mount(const char *debugfs) | ||
153 | { | ||
154 | struct statfs st_fs; | ||
155 | |||
156 | if (statfs(debugfs, &st_fs) < 0) | ||
157 | return -ENOENT; | ||
158 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
159 | return -ENOENT; | ||
160 | return 0; | ||
161 | } | ||
162 | 157 | ||
163 | struct tracepoint_path *tracepoint_id_to_path(u64 config) | 158 | struct tracepoint_path *tracepoint_id_to_path(u64 config) |
164 | { | 159 | { |
@@ -171,7 +166,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
171 | char evt_path[MAXPATHLEN]; | 166 | char evt_path[MAXPATHLEN]; |
172 | char dir_path[MAXPATHLEN]; | 167 | char dir_path[MAXPATHLEN]; |
173 | 168 | ||
174 | if (valid_debugfs_mount(debugfs_path)) | 169 | if (debugfs_valid_mountpoint(debugfs_path)) |
175 | return NULL; | 170 | return NULL; |
176 | 171 | ||
177 | sys_dir = opendir(debugfs_path); | 172 | sys_dir = opendir(debugfs_path); |
@@ -510,7 +505,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
510 | char sys_name[MAX_EVENT_LENGTH]; | 505 | char sys_name[MAX_EVENT_LENGTH]; |
511 | unsigned int sys_length, evt_length; | 506 | unsigned int sys_length, evt_length; |
512 | 507 | ||
513 | if (valid_debugfs_mount(debugfs_path)) | 508 | if (debugfs_valid_mountpoint(debugfs_path)) |
514 | return 0; | 509 | return 0; |
515 | 510 | ||
516 | evt_name = strchr(*strp, ':'); | 511 | evt_name = strchr(*strp, ':'); |
@@ -678,6 +673,8 @@ parse_event_symbols(const char **str, struct perf_event_attr *attr) | |||
678 | if (ret != EVT_FAILED) | 673 | if (ret != EVT_FAILED) |
679 | goto modifier; | 674 | goto modifier; |
680 | 675 | ||
676 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | ||
677 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
681 | return EVT_FAILED; | 678 | return EVT_FAILED; |
682 | 679 | ||
683 | modifier: | 680 | modifier: |
@@ -786,7 +783,7 @@ static void print_tracepoint_events(void) | |||
786 | char evt_path[MAXPATHLEN]; | 783 | char evt_path[MAXPATHLEN]; |
787 | char dir_path[MAXPATHLEN]; | 784 | char dir_path[MAXPATHLEN]; |
788 | 785 | ||
789 | if (valid_debugfs_mount(debugfs_path)) | 786 | if (debugfs_valid_mountpoint(debugfs_path)) |
790 | return; | 787 | return; |
791 | 788 | ||
792 | sys_dir = opendir(debugfs_path); | 789 | sys_dir = opendir(debugfs_path); |
@@ -804,7 +801,7 @@ static void print_tracepoint_events(void) | |||
804 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | 801 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { |
805 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 802 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
806 | sys_dirent.d_name, evt_dirent.d_name); | 803 | sys_dirent.d_name, evt_dirent.d_name); |
807 | fprintf(stderr, " %-42s [%s]\n", evt_path, | 804 | printf(" %-42s [%s]\n", evt_path, |
808 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | 805 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); |
809 | } | 806 | } |
810 | closedir(evt_dir); | 807 | closedir(evt_dir); |
@@ -821,8 +818,8 @@ void print_events(void) | |||
821 | unsigned int i, type, op, prev_type = -1; | 818 | unsigned int i, type, op, prev_type = -1; |
822 | char name[40]; | 819 | char name[40]; |
823 | 820 | ||
824 | fprintf(stderr, "\n"); | 821 | printf("\n"); |
825 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | 822 | printf("List of pre-defined events (to be used in -e):\n"); |
826 | 823 | ||
827 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 824 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
828 | type = syms->type + 1; | 825 | type = syms->type + 1; |
@@ -830,19 +827,19 @@ void print_events(void) | |||
830 | type = 0; | 827 | type = 0; |
831 | 828 | ||
832 | if (type != prev_type) | 829 | if (type != prev_type) |
833 | fprintf(stderr, "\n"); | 830 | printf("\n"); |
834 | 831 | ||
835 | if (strlen(syms->alias)) | 832 | if (strlen(syms->alias)) |
836 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | 833 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); |
837 | else | 834 | else |
838 | strcpy(name, syms->symbol); | 835 | strcpy(name, syms->symbol); |
839 | fprintf(stderr, " %-42s [%s]\n", name, | 836 | printf(" %-42s [%s]\n", name, |
840 | event_type_descriptors[type]); | 837 | event_type_descriptors[type]); |
841 | 838 | ||
842 | prev_type = type; | 839 | prev_type = type; |
843 | } | 840 | } |
844 | 841 | ||
845 | fprintf(stderr, "\n"); | 842 | printf("\n"); |
846 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | 843 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { |
847 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | 844 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { |
848 | /* skip invalid cache type */ | 845 | /* skip invalid cache type */ |
@@ -850,17 +847,17 @@ void print_events(void) | |||
850 | continue; | 847 | continue; |
851 | 848 | ||
852 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 849 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
853 | fprintf(stderr, " %-42s [%s]\n", | 850 | printf(" %-42s [%s]\n", |
854 | event_cache_name(type, op, i), | 851 | event_cache_name(type, op, i), |
855 | event_type_descriptors[4]); | 852 | event_type_descriptors[4]); |
856 | } | 853 | } |
857 | } | 854 | } |
858 | } | 855 | } |
859 | 856 | ||
860 | fprintf(stderr, "\n"); | 857 | printf("\n"); |
861 | fprintf(stderr, " %-42s [raw hardware event descriptor]\n", | 858 | printf(" %-42s [raw hardware event descriptor]\n", |
862 | "rNNN"); | 859 | "rNNN"); |
863 | fprintf(stderr, "\n"); | 860 | printf("\n"); |
864 | 861 | ||
865 | print_tracepoint_events(); | 862 | print_tracepoint_events(); |
866 | 863 | ||
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 04743d3e9039..227043577e06 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -1,5 +1,7 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | #include <stdlib.h> | ||
2 | #include "string.h" | 3 | #include "string.h" |
4 | #include "util.h" | ||
3 | 5 | ||
4 | static int hex(char ch) | 6 | static int hex(char ch) |
5 | { | 7 | { |
@@ -43,3 +45,85 @@ char *strxfrchar(char *s, char from, char to) | |||
43 | 45 | ||
44 | return s; | 46 | return s; |
45 | } | 47 | } |
48 | |||
49 | #define K 1024LL | ||
50 | /* | ||
51 | * perf_atoll() | ||
52 | * Parse (\d+)(b|B|kb|KB|mb|MB|gb|GB|tb|TB) (e.g. "256MB") | ||
53 | * and return its numeric value | ||
54 | */ | ||
55 | s64 perf_atoll(const char *str) | ||
56 | { | ||
57 | unsigned int i; | ||
58 | s64 length = -1, unit = 1; | ||
59 | |||
60 | if (!isdigit(str[0])) | ||
61 | goto out_err; | ||
62 | |||
63 | for (i = 1; i < strlen(str); i++) { | ||
64 | switch (str[i]) { | ||
65 | case 'B': | ||
66 | case 'b': | ||
67 | break; | ||
68 | case 'K': | ||
69 | if (str[i + 1] != 'B') | ||
70 | goto out_err; | ||
71 | else | ||
72 | goto kilo; | ||
73 | case 'k': | ||
74 | if (str[i + 1] != 'b') | ||
75 | goto out_err; | ||
76 | kilo: | ||
77 | unit = K; | ||
78 | break; | ||
79 | case 'M': | ||
80 | if (str[i + 1] != 'B') | ||
81 | goto out_err; | ||
82 | else | ||
83 | goto mega; | ||
84 | case 'm': | ||
85 | if (str[i + 1] != 'b') | ||
86 | goto out_err; | ||
87 | mega: | ||
88 | unit = K * K; | ||
89 | break; | ||
90 | case 'G': | ||
91 | if (str[i + 1] != 'B') | ||
92 | goto out_err; | ||
93 | else | ||
94 | goto giga; | ||
95 | case 'g': | ||
96 | if (str[i + 1] != 'b') | ||
97 | goto out_err; | ||
98 | giga: | ||
99 | unit = K * K * K; | ||
100 | break; | ||
101 | case 'T': | ||
102 | if (str[i + 1] != 'B') | ||
103 | goto out_err; | ||
104 | else | ||
105 | goto tera; | ||
106 | case 't': | ||
107 | if (str[i + 1] != 'b') | ||
108 | goto out_err; | ||
109 | tera: | ||
110 | unit = K * K * K * K; | ||
111 | break; | ||
112 | case '\0': /* only specified figures */ | ||
113 | unit = 1; | ||
114 | break; | ||
115 | default: | ||
116 | if (!isdigit(str[i])) | ||
117 | goto out_err; | ||
118 | break; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | length = atoll(str) * unit; | ||
123 | goto out; | ||
124 | |||
125 | out_err: | ||
126 | length = -1; | ||
127 | out: | ||
128 | return length; | ||
129 | } | ||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 2c84bf65ba0f..e50b07f80827 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | int hex2u64(const char *ptr, u64 *val); | 6 | int hex2u64(const char *ptr, u64 *val); |
7 | char *strxfrchar(char *s, char from, char to); | 7 | char *strxfrchar(char *s, char from, char to); |
8 | s64 perf_atoll(const char *str); | ||
8 | 9 | ||
9 | #define _STR(x) #x | 10 | #define _STR(x) #x |
10 | #define STR(x) _STR(x) | 11 | #define STR(x) _STR(x) |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 8f0208ce237a..1b77e81b38de 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -26,6 +26,7 @@ static void dsos__add(struct dso *dso); | |||
26 | static struct dso *dsos__find(const char *name); | 26 | static struct dso *dsos__find(const char *name); |
27 | static struct map *map__new2(u64 start, struct dso *dso); | 27 | static struct map *map__new2(u64 start, struct dso *dso); |
28 | static void kernel_maps__insert(struct map *map); | 28 | static void kernel_maps__insert(struct map *map); |
29 | unsigned int symbol__priv_size; | ||
29 | 30 | ||
30 | static struct rb_root kernel_maps; | 31 | static struct rb_root kernel_maps; |
31 | 32 | ||
@@ -75,18 +76,17 @@ static void kernel_maps__fixup_end(void) | |||
75 | } | 76 | } |
76 | } | 77 | } |
77 | 78 | ||
78 | static struct symbol *symbol__new(u64 start, u64 len, const char *name, | 79 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) |
79 | unsigned int priv_size) | ||
80 | { | 80 | { |
81 | size_t namelen = strlen(name) + 1; | 81 | size_t namelen = strlen(name) + 1; |
82 | struct symbol *self = calloc(1, priv_size + sizeof(*self) + namelen); | 82 | struct symbol *self = calloc(1, (symbol__priv_size + |
83 | 83 | sizeof(*self) + namelen)); | |
84 | if (!self) | 84 | if (!self) |
85 | return NULL; | 85 | return NULL; |
86 | 86 | ||
87 | if (priv_size) { | 87 | if (symbol__priv_size) { |
88 | memset(self, 0, priv_size); | 88 | memset(self, 0, symbol__priv_size); |
89 | self = ((void *)self) + priv_size; | 89 | self = ((void *)self) + symbol__priv_size; |
90 | } | 90 | } |
91 | self->start = start; | 91 | self->start = start; |
92 | self->end = len ? start + len - 1 : start; | 92 | self->end = len ? start + len - 1 : start; |
@@ -98,9 +98,9 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name, | |||
98 | return self; | 98 | return self; |
99 | } | 99 | } |
100 | 100 | ||
101 | static void symbol__delete(struct symbol *self, unsigned int priv_size) | 101 | static void symbol__delete(struct symbol *self) |
102 | { | 102 | { |
103 | free(((void *)self) - priv_size); | 103 | free(((void *)self) - symbol__priv_size); |
104 | } | 104 | } |
105 | 105 | ||
106 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | 106 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) |
@@ -109,7 +109,7 @@ static size_t symbol__fprintf(struct symbol *self, FILE *fp) | |||
109 | self->start, self->end, self->name); | 109 | self->start, self->end, self->name); |
110 | } | 110 | } |
111 | 111 | ||
112 | struct dso *dso__new(const char *name, unsigned int sym_priv_size) | 112 | struct dso *dso__new(const char *name) |
113 | { | 113 | { |
114 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | 114 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); |
115 | 115 | ||
@@ -118,10 +118,11 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size) | |||
118 | self->long_name = self->name; | 118 | self->long_name = self->name; |
119 | self->short_name = self->name; | 119 | self->short_name = self->name; |
120 | self->syms = RB_ROOT; | 120 | self->syms = RB_ROOT; |
121 | self->sym_priv_size = sym_priv_size; | ||
122 | self->find_symbol = dso__find_symbol; | 121 | self->find_symbol = dso__find_symbol; |
123 | self->slen_calculated = 0; | 122 | self->slen_calculated = 0; |
124 | self->origin = DSO__ORIG_NOT_FOUND; | 123 | self->origin = DSO__ORIG_NOT_FOUND; |
124 | self->loaded = 0; | ||
125 | self->has_build_id = 0; | ||
125 | } | 126 | } |
126 | 127 | ||
127 | return self; | 128 | return self; |
@@ -136,7 +137,7 @@ static void dso__delete_symbols(struct dso *self) | |||
136 | pos = rb_entry(next, struct symbol, rb_node); | 137 | pos = rb_entry(next, struct symbol, rb_node); |
137 | next = rb_next(&pos->rb_node); | 138 | next = rb_next(&pos->rb_node); |
138 | rb_erase(&pos->rb_node, &self->syms); | 139 | rb_erase(&pos->rb_node, &self->syms); |
139 | symbol__delete(pos, self->sym_priv_size); | 140 | symbol__delete(pos); |
140 | } | 141 | } |
141 | } | 142 | } |
142 | 143 | ||
@@ -148,6 +149,12 @@ void dso__delete(struct dso *self) | |||
148 | free(self); | 149 | free(self); |
149 | } | 150 | } |
150 | 151 | ||
152 | void dso__set_build_id(struct dso *self, void *build_id) | ||
153 | { | ||
154 | memcpy(self->build_id, build_id, sizeof(self->build_id)); | ||
155 | self->has_build_id = 1; | ||
156 | } | ||
157 | |||
151 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) | 158 | static void dso__insert_symbol(struct dso *self, struct symbol *sym) |
152 | { | 159 | { |
153 | struct rb_node **p = &self->syms.rb_node; | 160 | struct rb_node **p = &self->syms.rb_node; |
@@ -190,11 +197,37 @@ struct symbol *dso__find_symbol(struct dso *self, u64 ip) | |||
190 | return NULL; | 197 | return NULL; |
191 | } | 198 | } |
192 | 199 | ||
193 | size_t dso__fprintf(struct dso *self, FILE *fp) | 200 | int build_id__sprintf(u8 *self, int len, char *bf) |
194 | { | 201 | { |
195 | size_t ret = fprintf(fp, "dso: %s\n", self->short_name); | 202 | char *bid = bf; |
203 | u8 *raw = self; | ||
204 | int i; | ||
196 | 205 | ||
206 | for (i = 0; i < len; ++i) { | ||
207 | sprintf(bid, "%02x", *raw); | ||
208 | ++raw; | ||
209 | bid += 2; | ||
210 | } | ||
211 | |||
212 | return raw - self; | ||
213 | } | ||
214 | |||
215 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp) | ||
216 | { | ||
217 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
218 | |||
219 | build_id__sprintf(self->build_id, sizeof(self->build_id), sbuild_id); | ||
220 | return fprintf(fp, "%s", sbuild_id); | ||
221 | } | ||
222 | |||
223 | size_t dso__fprintf(struct dso *self, FILE *fp) | ||
224 | { | ||
197 | struct rb_node *nd; | 225 | struct rb_node *nd; |
226 | size_t ret = fprintf(fp, "dso: %s (", self->short_name); | ||
227 | |||
228 | ret += dso__fprintf_buildid(self, fp); | ||
229 | ret += fprintf(fp, ")\n"); | ||
230 | |||
198 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { | 231 | for (nd = rb_first(&self->syms); nd; nd = rb_next(nd)) { |
199 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 232 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
200 | ret += symbol__fprintf(pos, fp); | 233 | ret += symbol__fprintf(pos, fp); |
@@ -250,12 +283,16 @@ static int kernel_maps__load_all_kallsyms(void) | |||
250 | /* | 283 | /* |
251 | * Will fix up the end later, when we have all symbols sorted. | 284 | * Will fix up the end later, when we have all symbols sorted. |
252 | */ | 285 | */ |
253 | sym = symbol__new(start, 0, symbol_name, | 286 | sym = symbol__new(start, 0, symbol_name); |
254 | kernel_map->dso->sym_priv_size); | ||
255 | 287 | ||
256 | if (sym == NULL) | 288 | if (sym == NULL) |
257 | goto out_delete_line; | 289 | goto out_delete_line; |
258 | 290 | ||
291 | /* | ||
292 | * We will pass the symbols to the filter later, in | ||
293 | * kernel_maps__split_kallsyms, when we have split the | ||
294 | * maps per module | ||
295 | */ | ||
259 | dso__insert_symbol(kernel_map->dso, sym); | 296 | dso__insert_symbol(kernel_map->dso, sym); |
260 | } | 297 | } |
261 | 298 | ||
@@ -317,8 +354,7 @@ static int kernel_maps__split_kallsyms(symbol_filter_t filter, int use_modules) | |||
317 | snprintf(dso_name, sizeof(dso_name), "[kernel].%d", | 354 | snprintf(dso_name, sizeof(dso_name), "[kernel].%d", |
318 | kernel_range++); | 355 | kernel_range++); |
319 | 356 | ||
320 | dso = dso__new(dso_name, | 357 | dso = dso__new(dso_name); |
321 | kernel_map->dso->sym_priv_size); | ||
322 | if (dso == NULL) | 358 | if (dso == NULL) |
323 | return -1; | 359 | return -1; |
324 | 360 | ||
@@ -336,7 +372,7 @@ static int kernel_maps__split_kallsyms(symbol_filter_t filter, int use_modules) | |||
336 | if (filter && filter(map, pos)) { | 372 | if (filter && filter(map, pos)) { |
337 | delete_symbol: | 373 | delete_symbol: |
338 | rb_erase(&pos->rb_node, &kernel_map->dso->syms); | 374 | rb_erase(&pos->rb_node, &kernel_map->dso->syms); |
339 | symbol__delete(pos, kernel_map->dso->sym_priv_size); | 375 | symbol__delete(pos); |
340 | } else { | 376 | } else { |
341 | if (map != kernel_map) { | 377 | if (map != kernel_map) { |
342 | rb_erase(&pos->rb_node, &kernel_map->dso->syms); | 378 | rb_erase(&pos->rb_node, &kernel_map->dso->syms); |
@@ -417,14 +453,13 @@ static int dso__load_perf_map(struct dso *self, struct map *map, | |||
417 | if (len + 2 >= line_len) | 453 | if (len + 2 >= line_len) |
418 | continue; | 454 | continue; |
419 | 455 | ||
420 | sym = symbol__new(start, size, line + len, | 456 | sym = symbol__new(start, size, line + len); |
421 | self->sym_priv_size); | ||
422 | 457 | ||
423 | if (sym == NULL) | 458 | if (sym == NULL) |
424 | goto out_delete_line; | 459 | goto out_delete_line; |
425 | 460 | ||
426 | if (filter && filter(map, sym)) | 461 | if (filter && filter(map, sym)) |
427 | symbol__delete(sym, self->sym_priv_size); | 462 | symbol__delete(sym); |
428 | else { | 463 | else { |
429 | dso__insert_symbol(self, sym); | 464 | dso__insert_symbol(self, sym); |
430 | nr_syms++; | 465 | nr_syms++; |
@@ -532,7 +567,8 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
532 | * And always look at the original dso, not at debuginfo packages, that | 567 | * And always look at the original dso, not at debuginfo packages, that |
533 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | 568 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). |
534 | */ | 569 | */ |
535 | static int dso__synthesize_plt_symbols(struct dso *self) | 570 | static int dso__synthesize_plt_symbols(struct dso *self, struct map *map, |
571 | symbol_filter_t filter) | ||
536 | { | 572 | { |
537 | uint32_t nr_rel_entries, idx; | 573 | uint32_t nr_rel_entries, idx; |
538 | GElf_Sym sym; | 574 | GElf_Sym sym; |
@@ -552,7 +588,7 @@ static int dso__synthesize_plt_symbols(struct dso *self) | |||
552 | if (fd < 0) | 588 | if (fd < 0) |
553 | goto out; | 589 | goto out; |
554 | 590 | ||
555 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 591 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
556 | if (elf == NULL) | 592 | if (elf == NULL) |
557 | goto out_close; | 593 | goto out_close; |
558 | 594 | ||
@@ -616,12 +652,16 @@ static int dso__synthesize_plt_symbols(struct dso *self) | |||
616 | "%s@plt", elf_sym__name(&sym, symstrs)); | 652 | "%s@plt", elf_sym__name(&sym, symstrs)); |
617 | 653 | ||
618 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 654 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
619 | sympltname, self->sym_priv_size); | 655 | sympltname); |
620 | if (!f) | 656 | if (!f) |
621 | goto out_elf_end; | 657 | goto out_elf_end; |
622 | 658 | ||
623 | dso__insert_symbol(self, f); | 659 | if (filter && filter(map, f)) |
624 | ++nr; | 660 | symbol__delete(f); |
661 | else { | ||
662 | dso__insert_symbol(self, f); | ||
663 | ++nr; | ||
664 | } | ||
625 | } | 665 | } |
626 | } else if (shdr_rel_plt.sh_type == SHT_REL) { | 666 | } else if (shdr_rel_plt.sh_type == SHT_REL) { |
627 | GElf_Rel pos_mem, *pos; | 667 | GElf_Rel pos_mem, *pos; |
@@ -634,12 +674,16 @@ static int dso__synthesize_plt_symbols(struct dso *self) | |||
634 | "%s@plt", elf_sym__name(&sym, symstrs)); | 674 | "%s@plt", elf_sym__name(&sym, symstrs)); |
635 | 675 | ||
636 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 676 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
637 | sympltname, self->sym_priv_size); | 677 | sympltname); |
638 | if (!f) | 678 | if (!f) |
639 | goto out_elf_end; | 679 | goto out_elf_end; |
640 | 680 | ||
641 | dso__insert_symbol(self, f); | 681 | if (filter && filter(map, f)) |
642 | ++nr; | 682 | symbol__delete(f); |
683 | else { | ||
684 | dso__insert_symbol(self, f); | ||
685 | ++nr; | ||
686 | } | ||
643 | } | 687 | } |
644 | } | 688 | } |
645 | 689 | ||
@@ -676,7 +720,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
676 | Elf *elf; | 720 | Elf *elf; |
677 | int nr = 0; | 721 | int nr = 0; |
678 | 722 | ||
679 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 723 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
680 | if (elf == NULL) { | 724 | if (elf == NULL) { |
681 | pr_err("%s: cannot read %s ELF file.\n", __func__, name); | 725 | pr_err("%s: cannot read %s ELF file.\n", __func__, name); |
682 | goto out_close; | 726 | goto out_close; |
@@ -769,7 +813,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
769 | if (kmodule) | 813 | if (kmodule) |
770 | start += map->start + shdr.sh_offset; | 814 | start += map->start + shdr.sh_offset; |
771 | 815 | ||
772 | curr_dso = dso__new(dso_name, self->sym_priv_size); | 816 | curr_dso = dso__new(dso_name); |
773 | if (curr_dso == NULL) | 817 | if (curr_dso == NULL) |
774 | goto out_elf_end; | 818 | goto out_elf_end; |
775 | curr_map = map__new2(start, curr_dso); | 819 | curr_map = map__new2(start, curr_dso); |
@@ -803,14 +847,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
803 | if (demangled != NULL) | 847 | if (demangled != NULL) |
804 | elf_name = demangled; | 848 | elf_name = demangled; |
805 | new_symbol: | 849 | new_symbol: |
806 | f = symbol__new(sym.st_value, sym.st_size, elf_name, | 850 | f = symbol__new(sym.st_value, sym.st_size, elf_name); |
807 | curr_dso->sym_priv_size); | ||
808 | free(demangled); | 851 | free(demangled); |
809 | if (!f) | 852 | if (!f) |
810 | goto out_elf_end; | 853 | goto out_elf_end; |
811 | 854 | ||
812 | if (filter && filter(curr_map, f)) | 855 | if (filter && filter(curr_map, f)) |
813 | symbol__delete(f, curr_dso->sym_priv_size); | 856 | symbol__delete(f); |
814 | else { | 857 | else { |
815 | dso__insert_symbol(curr_dso, f); | 858 | dso__insert_symbol(curr_dso, f); |
816 | nr++; | 859 | nr++; |
@@ -829,27 +872,59 @@ out_close: | |||
829 | return err; | 872 | return err; |
830 | } | 873 | } |
831 | 874 | ||
832 | #define BUILD_ID_SIZE 128 | 875 | bool fetch_build_id_table(struct list_head *head) |
876 | { | ||
877 | bool have_buildid = false; | ||
878 | struct dso *pos; | ||
833 | 879 | ||
834 | static char *dso__read_build_id(struct dso *self) | 880 | list_for_each_entry(pos, &dsos, node) { |
881 | struct build_id_list *new; | ||
882 | struct build_id_event b; | ||
883 | size_t len; | ||
884 | |||
885 | if (filename__read_build_id(pos->long_name, | ||
886 | &b.build_id, | ||
887 | sizeof(b.build_id)) < 0) | ||
888 | continue; | ||
889 | have_buildid = true; | ||
890 | memset(&b.header, 0, sizeof(b.header)); | ||
891 | len = strlen(pos->long_name) + 1; | ||
892 | len = ALIGN(len, 64); | ||
893 | b.header.size = sizeof(b) + len; | ||
894 | |||
895 | new = malloc(sizeof(*new)); | ||
896 | if (!new) | ||
897 | die("No memory\n"); | ||
898 | |||
899 | memcpy(&new->event, &b, sizeof(b)); | ||
900 | new->dso_name = pos->long_name; | ||
901 | new->len = len; | ||
902 | |||
903 | list_add_tail(&new->list, head); | ||
904 | } | ||
905 | |||
906 | return have_buildid; | ||
907 | } | ||
908 | |||
909 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
835 | { | 910 | { |
836 | int i; | 911 | int fd, err = -1; |
837 | GElf_Ehdr ehdr; | 912 | GElf_Ehdr ehdr; |
838 | GElf_Shdr shdr; | 913 | GElf_Shdr shdr; |
839 | Elf_Data *build_id_data; | 914 | Elf_Data *build_id_data; |
840 | Elf_Scn *sec; | 915 | Elf_Scn *sec; |
841 | char *build_id = NULL, *bid; | ||
842 | unsigned char *raw; | ||
843 | Elf *elf; | 916 | Elf *elf; |
844 | int fd = open(self->long_name, O_RDONLY); | ||
845 | 917 | ||
918 | if (size < BUILD_ID_SIZE) | ||
919 | goto out; | ||
920 | |||
921 | fd = open(filename, O_RDONLY); | ||
846 | if (fd < 0) | 922 | if (fd < 0) |
847 | goto out; | 923 | goto out; |
848 | 924 | ||
849 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 925 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
850 | if (elf == NULL) { | 926 | if (elf == NULL) { |
851 | pr_err("%s: cannot read %s ELF file.\n", __func__, | 927 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); |
852 | self->long_name); | ||
853 | goto out_close; | 928 | goto out_close; |
854 | } | 929 | } |
855 | 930 | ||
@@ -858,30 +933,40 @@ static char *dso__read_build_id(struct dso *self) | |||
858 | goto out_elf_end; | 933 | goto out_elf_end; |
859 | } | 934 | } |
860 | 935 | ||
861 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".note.gnu.build-id", NULL); | 936 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
937 | ".note.gnu.build-id", NULL); | ||
862 | if (sec == NULL) | 938 | if (sec == NULL) |
863 | goto out_elf_end; | 939 | goto out_elf_end; |
864 | 940 | ||
865 | build_id_data = elf_getdata(sec, NULL); | 941 | build_id_data = elf_getdata(sec, NULL); |
866 | if (build_id_data == NULL) | 942 | if (build_id_data == NULL) |
867 | goto out_elf_end; | 943 | goto out_elf_end; |
868 | build_id = malloc(BUILD_ID_SIZE); | 944 | memcpy(bf, build_id_data->d_buf + 16, BUILD_ID_SIZE); |
869 | if (build_id == NULL) | 945 | err = BUILD_ID_SIZE; |
870 | goto out_elf_end; | ||
871 | raw = build_id_data->d_buf + 16; | ||
872 | bid = build_id; | ||
873 | |||
874 | for (i = 0; i < 20; ++i) { | ||
875 | sprintf(bid, "%02x", *raw); | ||
876 | ++raw; | ||
877 | bid += 2; | ||
878 | } | ||
879 | pr_debug2("%s(%s): %s\n", __func__, self->long_name, build_id); | ||
880 | out_elf_end: | 946 | out_elf_end: |
881 | elf_end(elf); | 947 | elf_end(elf); |
882 | out_close: | 948 | out_close: |
883 | close(fd); | 949 | close(fd); |
884 | out: | 950 | out: |
951 | return err; | ||
952 | } | ||
953 | |||
954 | static char *dso__read_build_id(struct dso *self) | ||
955 | { | ||
956 | int len; | ||
957 | char *build_id = NULL; | ||
958 | unsigned char rawbf[BUILD_ID_SIZE]; | ||
959 | |||
960 | len = filename__read_build_id(self->long_name, rawbf, sizeof(rawbf)); | ||
961 | if (len < 0) | ||
962 | goto out; | ||
963 | |||
964 | build_id = malloc(len * 2 + 1); | ||
965 | if (build_id == NULL) | ||
966 | goto out; | ||
967 | |||
968 | build_id__sprintf(rawbf, len, build_id); | ||
969 | out: | ||
885 | return build_id; | 970 | return build_id; |
886 | } | 971 | } |
887 | 972 | ||
@@ -909,6 +994,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
909 | int ret = -1; | 994 | int ret = -1; |
910 | int fd; | 995 | int fd; |
911 | 996 | ||
997 | self->loaded = 1; | ||
998 | |||
912 | if (!name) | 999 | if (!name) |
913 | return -1; | 1000 | return -1; |
914 | 1001 | ||
@@ -925,6 +1012,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
925 | 1012 | ||
926 | more: | 1013 | more: |
927 | do { | 1014 | do { |
1015 | int berr = 0; | ||
1016 | |||
928 | self->origin++; | 1017 | self->origin++; |
929 | switch (self->origin) { | 1018 | switch (self->origin) { |
930 | case DSO__ORIG_FEDORA: | 1019 | case DSO__ORIG_FEDORA: |
@@ -941,8 +1030,7 @@ more: | |||
941 | snprintf(name, size, | 1030 | snprintf(name, size, |
942 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 1031 | "/usr/lib/debug/.build-id/%.2s/%s.debug", |
943 | build_id, build_id + 2); | 1032 | build_id, build_id + 2); |
944 | free(build_id); | 1033 | goto compare_build_id; |
945 | break; | ||
946 | } | 1034 | } |
947 | self->origin++; | 1035 | self->origin++; |
948 | /* Fall thru */ | 1036 | /* Fall thru */ |
@@ -954,6 +1042,22 @@ more: | |||
954 | goto out; | 1042 | goto out; |
955 | } | 1043 | } |
956 | 1044 | ||
1045 | if (self->has_build_id) { | ||
1046 | bool match; | ||
1047 | build_id = malloc(BUILD_ID_SIZE); | ||
1048 | if (build_id == NULL) | ||
1049 | goto more; | ||
1050 | berr = filename__read_build_id(name, build_id, | ||
1051 | BUILD_ID_SIZE); | ||
1052 | compare_build_id: | ||
1053 | match = berr > 0 && memcmp(build_id, self->build_id, | ||
1054 | sizeof(self->build_id)) == 0; | ||
1055 | free(build_id); | ||
1056 | build_id = NULL; | ||
1057 | if (!match) | ||
1058 | goto more; | ||
1059 | } | ||
1060 | |||
957 | fd = open(name, O_RDONLY); | 1061 | fd = open(name, O_RDONLY); |
958 | } while (fd < 0); | 1062 | } while (fd < 0); |
959 | 1063 | ||
@@ -967,7 +1071,7 @@ more: | |||
967 | goto more; | 1071 | goto more; |
968 | 1072 | ||
969 | if (ret > 0) { | 1073 | if (ret > 0) { |
970 | int nr_plt = dso__synthesize_plt_symbols(self); | 1074 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); |
971 | if (nr_plt > 0) | 1075 | if (nr_plt > 0) |
972 | ret += nr_plt; | 1076 | ret += nr_plt; |
973 | } | 1077 | } |
@@ -1019,6 +1123,8 @@ static int dso__load_module_sym(struct dso *self, struct map *map, | |||
1019 | { | 1123 | { |
1020 | int err = 0, fd = open(self->long_name, O_RDONLY); | 1124 | int err = 0, fd = open(self->long_name, O_RDONLY); |
1021 | 1125 | ||
1126 | self->loaded = 1; | ||
1127 | |||
1022 | if (fd < 0) { | 1128 | if (fd < 0) { |
1023 | pr_err("%s: cannot open %s\n", __func__, self->long_name); | 1129 | pr_err("%s: cannot open %s\n", __func__, self->long_name); |
1024 | return err; | 1130 | return err; |
@@ -1128,22 +1234,16 @@ static struct map *map__new2(u64 start, struct dso *dso) | |||
1128 | struct map *self = malloc(sizeof(*self)); | 1234 | struct map *self = malloc(sizeof(*self)); |
1129 | 1235 | ||
1130 | if (self != NULL) { | 1236 | if (self != NULL) { |
1131 | self->start = start; | ||
1132 | /* | 1237 | /* |
1133 | * Will be filled after we load all the symbols | 1238 | * ->end will be filled after we load all the symbols |
1134 | */ | 1239 | */ |
1135 | self->end = 0; | 1240 | map__init(self, start, 0, 0, dso); |
1136 | |||
1137 | self->pgoff = 0; | ||
1138 | self->dso = dso; | ||
1139 | self->map_ip = map__map_ip; | ||
1140 | self->unmap_ip = map__unmap_ip; | ||
1141 | RB_CLEAR_NODE(&self->rb_node); | ||
1142 | } | 1241 | } |
1242 | |||
1143 | return self; | 1243 | return self; |
1144 | } | 1244 | } |
1145 | 1245 | ||
1146 | static int dsos__load_modules(unsigned int sym_priv_size) | 1246 | static int dsos__load_modules(void) |
1147 | { | 1247 | { |
1148 | char *line = NULL; | 1248 | char *line = NULL; |
1149 | size_t n; | 1249 | size_t n; |
@@ -1182,7 +1282,7 @@ static int dsos__load_modules(unsigned int sym_priv_size) | |||
1182 | *sep = '\0'; | 1282 | *sep = '\0'; |
1183 | 1283 | ||
1184 | snprintf(name, sizeof(name), "[%s]", line); | 1284 | snprintf(name, sizeof(name), "[%s]", line); |
1185 | dso = dso__new(name, sym_priv_size); | 1285 | dso = dso__new(name); |
1186 | 1286 | ||
1187 | if (dso == NULL) | 1287 | if (dso == NULL) |
1188 | goto out_delete_line; | 1288 | goto out_delete_line; |
@@ -1214,6 +1314,8 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1214 | { | 1314 | { |
1215 | int err, fd = open(vmlinux, O_RDONLY); | 1315 | int err, fd = open(vmlinux, O_RDONLY); |
1216 | 1316 | ||
1317 | self->loaded = 1; | ||
1318 | |||
1217 | if (fd < 0) | 1319 | if (fd < 0) |
1218 | return -1; | 1320 | return -1; |
1219 | 1321 | ||
@@ -1224,11 +1326,11 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1224 | return err; | 1326 | return err; |
1225 | } | 1327 | } |
1226 | 1328 | ||
1227 | int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size, | 1329 | int dsos__load_kernel(const char *vmlinux, symbol_filter_t filter, |
1228 | symbol_filter_t filter, int use_modules) | 1330 | int use_modules) |
1229 | { | 1331 | { |
1230 | int err = -1; | 1332 | int err = -1; |
1231 | struct dso *dso = dso__new(vmlinux, sym_priv_size); | 1333 | struct dso *dso = dso__new(vmlinux); |
1232 | 1334 | ||
1233 | if (dso == NULL) | 1335 | if (dso == NULL) |
1234 | return -1; | 1336 | return -1; |
@@ -1240,7 +1342,7 @@ int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size, | |||
1240 | 1342 | ||
1241 | kernel_map->map_ip = kernel_map->unmap_ip = identity__map_ip; | 1343 | kernel_map->map_ip = kernel_map->unmap_ip = identity__map_ip; |
1242 | 1344 | ||
1243 | if (use_modules && dsos__load_modules(sym_priv_size) < 0) { | 1345 | if (use_modules && dsos__load_modules() < 0) { |
1244 | pr_warning("Failed to load list of modules in use! " | 1346 | pr_warning("Failed to load list of modules in use! " |
1245 | "Continuing...\n"); | 1347 | "Continuing...\n"); |
1246 | use_modules = 0; | 1348 | use_modules = 0; |
@@ -1312,19 +1414,15 @@ static struct dso *dsos__find(const char *name) | |||
1312 | return NULL; | 1414 | return NULL; |
1313 | } | 1415 | } |
1314 | 1416 | ||
1315 | struct dso *dsos__findnew(const char *name, unsigned int sym_priv_size, | 1417 | struct dso *dsos__findnew(const char *name) |
1316 | bool *is_new) | ||
1317 | { | 1418 | { |
1318 | struct dso *dso = dsos__find(name); | 1419 | struct dso *dso = dsos__find(name); |
1319 | 1420 | ||
1320 | if (!dso) { | 1421 | if (!dso) { |
1321 | dso = dso__new(name, sym_priv_size); | 1422 | dso = dso__new(name); |
1322 | if (dso) { | 1423 | if (dso != NULL) |
1323 | dsos__add(dso); | 1424 | dsos__add(dso); |
1324 | *is_new = true; | 1425 | } |
1325 | } | ||
1326 | } else | ||
1327 | *is_new = false; | ||
1328 | 1426 | ||
1329 | return dso; | 1427 | return dso; |
1330 | } | 1428 | } |
@@ -1337,13 +1435,24 @@ void dsos__fprintf(FILE *fp) | |||
1337 | dso__fprintf(pos, fp); | 1435 | dso__fprintf(pos, fp); |
1338 | } | 1436 | } |
1339 | 1437 | ||
1340 | int load_kernel(unsigned int sym_priv_size, symbol_filter_t filter) | 1438 | size_t dsos__fprintf_buildid(FILE *fp) |
1439 | { | ||
1440 | struct dso *pos; | ||
1441 | size_t ret = 0; | ||
1442 | |||
1443 | list_for_each_entry(pos, &dsos, node) { | ||
1444 | ret += dso__fprintf_buildid(pos, fp); | ||
1445 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
1446 | } | ||
1447 | return ret; | ||
1448 | } | ||
1449 | |||
1450 | int load_kernel(symbol_filter_t filter) | ||
1341 | { | 1451 | { |
1342 | if (dsos__load_kernel(vmlinux_name, sym_priv_size, filter, | 1452 | if (dsos__load_kernel(vmlinux_name, filter, modules) <= 0) |
1343 | modules) <= 0) | ||
1344 | return -1; | 1453 | return -1; |
1345 | 1454 | ||
1346 | vdso = dso__new("[vdso]", 0); | 1455 | vdso = dso__new("[vdso]"); |
1347 | if (!vdso) | 1456 | if (!vdso) |
1348 | return -1; | 1457 | return -1; |
1349 | 1458 | ||
@@ -1352,7 +1461,8 @@ int load_kernel(unsigned int sym_priv_size, symbol_filter_t filter) | |||
1352 | return 0; | 1461 | return 0; |
1353 | } | 1462 | } |
1354 | 1463 | ||
1355 | void symbol__init(void) | 1464 | void symbol__init(unsigned int priv_size) |
1356 | { | 1465 | { |
1357 | elf_version(EV_CURRENT); | 1466 | elf_version(EV_CURRENT); |
1467 | symbol__priv_size = priv_size; | ||
1358 | } | 1468 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 77b7b3e42417..51c5a4a08133 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -27,6 +27,16 @@ static inline char *bfd_demangle(void __used *v, const char __used *c, | |||
27 | #endif | 27 | #endif |
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | /* | ||
31 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; | ||
32 | * for newer versions we can use mmap to reduce memory usage: | ||
33 | */ | ||
34 | #ifdef LIBELF_NO_MMAP | ||
35 | # define PERF_ELF_C_READ_MMAP ELF_C_READ | ||
36 | #else | ||
37 | # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP | ||
38 | #endif | ||
39 | |||
30 | #ifndef DMGL_PARAMS | 40 | #ifndef DMGL_PARAMS |
31 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | 41 | #define DMGL_PARAMS (1 << 0) /* Include function args */ |
32 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 42 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
@@ -39,42 +49,51 @@ struct symbol { | |||
39 | char name[0]; | 49 | char name[0]; |
40 | }; | 50 | }; |
41 | 51 | ||
52 | extern unsigned int symbol__priv_size; | ||
53 | |||
54 | static inline void *symbol__priv(struct symbol *self) | ||
55 | { | ||
56 | return ((void *)self) - symbol__priv_size; | ||
57 | } | ||
58 | |||
42 | struct dso { | 59 | struct dso { |
43 | struct list_head node; | 60 | struct list_head node; |
44 | struct rb_root syms; | 61 | struct rb_root syms; |
45 | struct symbol *(*find_symbol)(struct dso *, u64 ip); | 62 | struct symbol *(*find_symbol)(struct dso *, u64 ip); |
46 | unsigned int sym_priv_size; | 63 | u8 adjust_symbols:1; |
47 | unsigned char adjust_symbols; | 64 | u8 slen_calculated:1; |
48 | unsigned char slen_calculated; | 65 | u8 loaded:1; |
66 | u8 has_build_id:1; | ||
49 | unsigned char origin; | 67 | unsigned char origin; |
68 | u8 build_id[BUILD_ID_SIZE]; | ||
50 | const char *short_name; | 69 | const char *short_name; |
51 | char *long_name; | 70 | char *long_name; |
52 | char name[0]; | 71 | char name[0]; |
53 | }; | 72 | }; |
54 | 73 | ||
55 | struct dso *dso__new(const char *name, unsigned int sym_priv_size); | 74 | struct dso *dso__new(const char *name); |
56 | void dso__delete(struct dso *self); | 75 | void dso__delete(struct dso *self); |
57 | 76 | ||
58 | static inline void *dso__sym_priv(struct dso *self, struct symbol *sym) | ||
59 | { | ||
60 | return ((void *)sym) - self->sym_priv_size; | ||
61 | } | ||
62 | |||
63 | struct symbol *dso__find_symbol(struct dso *self, u64 ip); | 77 | struct symbol *dso__find_symbol(struct dso *self, u64 ip); |
64 | 78 | ||
65 | int dsos__load_kernel(const char *vmlinux, unsigned int sym_priv_size, | 79 | int dsos__load_kernel(const char *vmlinux, symbol_filter_t filter, int modules); |
66 | symbol_filter_t filter, int modules); | 80 | struct dso *dsos__findnew(const char *name); |
67 | struct dso *dsos__findnew(const char *name, unsigned int sym_priv_size, | ||
68 | bool *is_new); | ||
69 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); | 81 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); |
70 | void dsos__fprintf(FILE *fp); | 82 | void dsos__fprintf(FILE *fp); |
83 | size_t dsos__fprintf_buildid(FILE *fp); | ||
71 | 84 | ||
85 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | ||
72 | size_t dso__fprintf(struct dso *self, FILE *fp); | 86 | size_t dso__fprintf(struct dso *self, FILE *fp); |
73 | char dso__symtab_origin(const struct dso *self); | 87 | char dso__symtab_origin(const struct dso *self); |
88 | void dso__set_build_id(struct dso *self, void *build_id); | ||
89 | |||
90 | int filename__read_build_id(const char *filename, void *bf, size_t size); | ||
91 | bool fetch_build_id_table(struct list_head *head); | ||
92 | int build_id__sprintf(u8 *self, int len, char *bf); | ||
74 | 93 | ||
75 | int load_kernel(unsigned int sym_priv_size, symbol_filter_t filter); | 94 | int load_kernel(symbol_filter_t filter); |
76 | 95 | ||
77 | void symbol__init(void); | 96 | void symbol__init(unsigned int priv_size); |
78 | 97 | ||
79 | extern struct list_head dsos; | 98 | extern struct list_head dsos; |
80 | extern struct map *kernel_map; | 99 | extern struct map *kernel_map; |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 0daa341734f9..f2203a0946bc 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -315,6 +315,7 @@ static inline int has_extension(const char *filename, const char *ext) | |||
315 | #undef isascii | 315 | #undef isascii |
316 | #undef isspace | 316 | #undef isspace |
317 | #undef isdigit | 317 | #undef isdigit |
318 | #undef isxdigit | ||
318 | #undef isalpha | 319 | #undef isalpha |
319 | #undef isprint | 320 | #undef isprint |
320 | #undef isalnum | 321 | #undef isalnum |
@@ -332,6 +333,8 @@ extern unsigned char sane_ctype[256]; | |||
332 | #define isascii(x) (((x) & ~0x7f) == 0) | 333 | #define isascii(x) (((x) & ~0x7f) == 0) |
333 | #define isspace(x) sane_istest(x,GIT_SPACE) | 334 | #define isspace(x) sane_istest(x,GIT_SPACE) |
334 | #define isdigit(x) sane_istest(x,GIT_DIGIT) | 335 | #define isdigit(x) sane_istest(x,GIT_DIGIT) |
336 | #define isxdigit(x) \ | ||
337 | (sane_istest(toupper(x), GIT_ALPHA | GIT_DIGIT) && toupper(x) < 'G') | ||
335 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | 338 | #define isalpha(x) sane_istest(x,GIT_ALPHA) |
336 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | 339 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) |
337 | #define isprint(x) sane_istest(x,GIT_PRINT) | 340 | #define isprint(x) sane_istest(x,GIT_PRINT) |