diff options
Diffstat (limited to 'tools')
48 files changed, 2386 insertions, 892 deletions
diff --git a/tools/perf/Documentation/perf-archive.txt b/tools/perf/Documentation/perf-archive.txt new file mode 100644 index 000000000000..fae174dc7d01 --- /dev/null +++ b/tools/perf/Documentation/perf-archive.txt | |||
@@ -0,0 +1,22 @@ | |||
1 | perf-archive(1) | ||
2 | =============== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-archive - Create archive with object files with build-ids found in perf.data file | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf archive' [file] | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | This command runs runs perf-buildid-list --with-hits, and collects the files | ||
16 | with the buildids found so that analisys of perf.data contents can be possible | ||
17 | on another machine. | ||
18 | |||
19 | |||
20 | SEE ALSO | ||
21 | -------- | ||
22 | linkperf:perf-record[1], linkperf:perf-buildid-list[1], linkperf:perf-report[1] | ||
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt new file mode 100644 index 000000000000..88bc3b519746 --- /dev/null +++ b/tools/perf/Documentation/perf-buildid-cache.txt | |||
@@ -0,0 +1,33 @@ | |||
1 | perf-buildid-cache(1) | ||
2 | ===================== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-buildid-cache - Manage build-id cache. | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf buildid-list <options>' | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | This command manages the build-id cache. It can add and remove files to the | ||
16 | cache. In the future it should as well purge older entries, set upper limits | ||
17 | for the space used by the cache, etc. | ||
18 | |||
19 | OPTIONS | ||
20 | ------- | ||
21 | -a:: | ||
22 | --add=:: | ||
23 | Add specified file to the cache. | ||
24 | -r:: | ||
25 | --remove=:: | ||
26 | Remove specified file to the cache. | ||
27 | -v:: | ||
28 | --verbose:: | ||
29 | Be more verbose. | ||
30 | |||
31 | SEE ALSO | ||
32 | -------- | ||
33 | linkperf:perf-record[1], linkperf:perf-report[1] | ||
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 250e391b4bc8..2de34075f6a4 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -15,6 +15,8 @@ or | |||
15 | 'perf probe' [options] --del='[GROUP:]EVENT' [...] | 15 | 'perf probe' [options] --del='[GROUP:]EVENT' [...] |
16 | or | 16 | or |
17 | 'perf probe' --list | 17 | 'perf probe' --list |
18 | or | ||
19 | 'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]' | ||
18 | 20 | ||
19 | DESCRIPTION | 21 | DESCRIPTION |
20 | ----------- | 22 | ----------- |
@@ -45,6 +47,11 @@ OPTIONS | |||
45 | --list:: | 47 | --list:: |
46 | List up current probe events. | 48 | List up current probe events. |
47 | 49 | ||
50 | -L:: | ||
51 | --line=:: | ||
52 | Show source code lines which can be probed. This needs an argument | ||
53 | which specifies a range of the source code. | ||
54 | |||
48 | PROBE SYNTAX | 55 | PROBE SYNTAX |
49 | ------------ | 56 | ------------ |
50 | Probe points are defined by following syntax. | 57 | Probe points are defined by following syntax. |
@@ -56,6 +63,19 @@ Probe points are defined by following syntax. | |||
56 | It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number. | 63 | It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number. |
57 | 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). | 64 | 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc). |
58 | 65 | ||
66 | LINE SYNTAX | ||
67 | ----------- | ||
68 | Line range is descripted by following syntax. | ||
69 | |||
70 | "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]" | ||
71 | |||
72 | FUNC specifies the function name of showing lines. 'RLN' is the start line | ||
73 | number from function entry line, and 'RLN2' is the end line number. As same as | ||
74 | probe syntax, 'SRC' means the source file path, 'ALN' is start line number, | ||
75 | and 'ALN2' is end line number in the file. It is also possible to specify how | ||
76 | many lines to show by using 'NUM'. | ||
77 | So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function. | ||
78 | |||
59 | SEE ALSO | 79 | SEE ALSO |
60 | -------- | 80 | -------- |
61 | linkperf:perf-trace[1], linkperf:perf-record[1] | 81 | linkperf:perf-trace[1], linkperf:perf-record[1] |
diff --git a/tools/perf/Documentation/perf.txt b/tools/perf/Documentation/perf.txt index 69c832557199..0eeb247dc7d2 100644 --- a/tools/perf/Documentation/perf.txt +++ b/tools/perf/Documentation/perf.txt | |||
@@ -12,7 +12,7 @@ SYNOPSIS | |||
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | Performance counters for Linux are are a new kernel-based subsystem | 15 | Performance counters for Linux are a new kernel-based subsystem |
16 | that provide a framework for all things performance analysis. It | 16 | that provide a framework for all things performance analysis. It |
17 | covers hardware level (CPU/PMU, Performance Monitoring Unit) features | 17 | covers hardware level (CPU/PMU, Performance Monitoring Unit) features |
18 | and software features (software counters, tracepoints) as well. | 18 | and software features (software counters, tracepoints) as well. |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 2e7fa3a06806..9b173e66fb41 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -286,11 +286,7 @@ SCRIPT_PERL = | |||
286 | SCRIPT_SH = | 286 | SCRIPT_SH = |
287 | TEST_PROGRAMS = | 287 | TEST_PROGRAMS = |
288 | 288 | ||
289 | # | 289 | SCRIPT_SH += perf-archive.sh |
290 | # No scripts right now: | ||
291 | # | ||
292 | |||
293 | # SCRIPT_SH += perf-am.sh | ||
294 | 290 | ||
295 | # | 291 | # |
296 | # No Perl scripts right now: | 292 | # No Perl scripts right now: |
@@ -315,9 +311,6 @@ PROGRAMS += perf | |||
315 | # List built-in command $C whose implementation cmd_$C() is not in | 311 | # List built-in command $C whose implementation cmd_$C() is not in |
316 | # builtin-$C.o but is linked in as part of some other command. | 312 | # builtin-$C.o but is linked in as part of some other command. |
317 | # | 313 | # |
318 | # None right now: | ||
319 | # | ||
320 | # BUILT_INS += perf-init $X | ||
321 | 314 | ||
322 | # what 'all' will build and 'install' will install, in perfexecdir | 315 | # what 'all' will build and 'install' will install, in perfexecdir |
323 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) | 316 | ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS) |
@@ -369,6 +362,7 @@ LIB_H += util/event.h | |||
369 | LIB_H += util/exec_cmd.h | 362 | LIB_H += util/exec_cmd.h |
370 | LIB_H += util/types.h | 363 | LIB_H += util/types.h |
371 | LIB_H += util/levenshtein.h | 364 | LIB_H += util/levenshtein.h |
365 | LIB_H += util/map.h | ||
372 | LIB_H += util/parse-options.h | 366 | LIB_H += util/parse-options.h |
373 | LIB_H += util/parse-events.h | 367 | LIB_H += util/parse-events.h |
374 | LIB_H += util/quote.h | 368 | LIB_H += util/quote.h |
@@ -435,8 +429,8 @@ LIB_OBJS += util/trace-event-perl.o | |||
435 | LIB_OBJS += util/svghelper.o | 429 | LIB_OBJS += util/svghelper.o |
436 | LIB_OBJS += util/sort.o | 430 | LIB_OBJS += util/sort.o |
437 | LIB_OBJS += util/hist.o | 431 | LIB_OBJS += util/hist.o |
438 | LIB_OBJS += util/data_map.o | ||
439 | LIB_OBJS += util/probe-event.o | 432 | LIB_OBJS += util/probe-event.o |
433 | LIB_OBJS += util/util.o | ||
440 | 434 | ||
441 | BUILTIN_OBJS += builtin-annotate.o | 435 | BUILTIN_OBJS += builtin-annotate.o |
442 | 436 | ||
@@ -451,6 +445,7 @@ BUILTIN_OBJS += builtin-diff.o | |||
451 | BUILTIN_OBJS += builtin-help.o | 445 | BUILTIN_OBJS += builtin-help.o |
452 | BUILTIN_OBJS += builtin-sched.o | 446 | BUILTIN_OBJS += builtin-sched.o |
453 | BUILTIN_OBJS += builtin-buildid-list.o | 447 | BUILTIN_OBJS += builtin-buildid-list.o |
448 | BUILTIN_OBJS += builtin-buildid-cache.o | ||
454 | BUILTIN_OBJS += builtin-list.o | 449 | BUILTIN_OBJS += builtin-list.o |
455 | BUILTIN_OBJS += builtin-record.o | 450 | BUILTIN_OBJS += builtin-record.o |
456 | BUILTIN_OBJS += builtin-report.o | 451 | BUILTIN_OBJS += builtin-report.o |
@@ -1009,6 +1004,7 @@ install: all | |||
1009 | $(INSTALL) perf$X '$(DESTDIR_SQ)$(bindir_SQ)' | 1004 | $(INSTALL) perf$X '$(DESTDIR_SQ)$(bindir_SQ)' |
1010 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 1005 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
1011 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 1006 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
1007 | $(INSTALL) perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
1012 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 1008 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
1013 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' | 1009 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' |
1014 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 1010 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 593ff25006de..73c202ee0882 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -132,8 +132,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
132 | { | 132 | { |
133 | struct addr_location al; | 133 | struct addr_location al; |
134 | 134 | ||
135 | dump_printf("(IP, %d): %d: %p\n", event->header.misc, | 135 | dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, |
136 | event->ip.pid, (void *)(long)event->ip.ip); | 136 | event->ip.pid, event->ip.ip); |
137 | 137 | ||
138 | if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) { | 138 | if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) { |
139 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 139 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
@@ -451,10 +451,10 @@ static void perf_session__find_annotations(struct perf_session *self) | |||
451 | } | 451 | } |
452 | 452 | ||
453 | static struct perf_event_ops event_ops = { | 453 | static struct perf_event_ops event_ops = { |
454 | .process_sample_event = process_sample_event, | 454 | .sample = process_sample_event, |
455 | .process_mmap_event = event__process_mmap, | 455 | .mmap = event__process_mmap, |
456 | .process_comm_event = event__process_comm, | 456 | .comm = event__process_comm, |
457 | .process_fork_event = event__process_task, | 457 | .fork = event__process_task, |
458 | }; | 458 | }; |
459 | 459 | ||
460 | static int __cmd_annotate(void) | 460 | static int __cmd_annotate(void) |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c new file mode 100644 index 000000000000..30a05f552c96 --- /dev/null +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -0,0 +1,133 @@ | |||
1 | /* | ||
2 | * builtin-buildid-cache.c | ||
3 | * | ||
4 | * Builtin buildid-cache command: Manages build-id cache | ||
5 | * | ||
6 | * Copyright (C) 2010, Red Hat Inc. | ||
7 | * Copyright (C) 2010, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
8 | */ | ||
9 | #include "builtin.h" | ||
10 | #include "perf.h" | ||
11 | #include "util/cache.h" | ||
12 | #include "util/debug.h" | ||
13 | #include "util/header.h" | ||
14 | #include "util/parse-options.h" | ||
15 | #include "util/strlist.h" | ||
16 | #include "util/symbol.h" | ||
17 | |||
18 | static char const *add_name_list_str, *remove_name_list_str; | ||
19 | |||
20 | static const char * const buildid_cache_usage[] = { | ||
21 | "perf buildid-cache [<options>]", | ||
22 | NULL | ||
23 | }; | ||
24 | |||
25 | static const struct option buildid_cache_options[] = { | ||
26 | OPT_STRING('a', "add", &add_name_list_str, | ||
27 | "file list", "file(s) to add"), | ||
28 | OPT_STRING('r', "remove", &remove_name_list_str, "file list", | ||
29 | "file(s) to remove"), | ||
30 | OPT_BOOLEAN('v', "verbose", &verbose, "be more verbose"), | ||
31 | OPT_END() | ||
32 | }; | ||
33 | |||
34 | static int build_id_cache__add_file(const char *filename, const char *debugdir) | ||
35 | { | ||
36 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
37 | u8 build_id[BUILD_ID_SIZE]; | ||
38 | int err; | ||
39 | |||
40 | if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) { | ||
41 | pr_debug("Couldn't read a build-id in %s\n", filename); | ||
42 | return -1; | ||
43 | } | ||
44 | |||
45 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | ||
46 | err = build_id_cache__add_s(sbuild_id, debugdir, filename, false); | ||
47 | if (verbose) | ||
48 | pr_info("Adding %s %s: %s\n", sbuild_id, filename, | ||
49 | err ? "FAIL" : "Ok"); | ||
50 | return err; | ||
51 | } | ||
52 | |||
53 | static int build_id_cache__remove_file(const char *filename __used, | ||
54 | const char *debugdir __used) | ||
55 | { | ||
56 | u8 build_id[BUILD_ID_SIZE]; | ||
57 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
58 | |||
59 | int err; | ||
60 | |||
61 | if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) { | ||
62 | pr_debug("Couldn't read a build-id in %s\n", filename); | ||
63 | return -1; | ||
64 | } | ||
65 | |||
66 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | ||
67 | err = build_id_cache__remove_s(sbuild_id, debugdir); | ||
68 | if (verbose) | ||
69 | pr_info("Removing %s %s: %s\n", sbuild_id, filename, | ||
70 | err ? "FAIL" : "Ok"); | ||
71 | |||
72 | return err; | ||
73 | } | ||
74 | |||
75 | static int __cmd_buildid_cache(void) | ||
76 | { | ||
77 | struct strlist *list; | ||
78 | struct str_node *pos; | ||
79 | char debugdir[PATH_MAX]; | ||
80 | |||
81 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | ||
82 | DEBUG_CACHE_DIR); | ||
83 | |||
84 | if (add_name_list_str) { | ||
85 | list = strlist__new(true, add_name_list_str); | ||
86 | if (list) { | ||
87 | strlist__for_each(pos, list) | ||
88 | if (build_id_cache__add_file(pos->s, debugdir)) { | ||
89 | if (errno == EEXIST) { | ||
90 | pr_debug("%s already in the cache\n", | ||
91 | pos->s); | ||
92 | continue; | ||
93 | } | ||
94 | pr_warning("Couldn't add %s: %s\n", | ||
95 | pos->s, strerror(errno)); | ||
96 | } | ||
97 | |||
98 | strlist__delete(list); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | if (remove_name_list_str) { | ||
103 | list = strlist__new(true, remove_name_list_str); | ||
104 | if (list) { | ||
105 | strlist__for_each(pos, list) | ||
106 | if (build_id_cache__remove_file(pos->s, debugdir)) { | ||
107 | if (errno == ENOENT) { | ||
108 | pr_debug("%s wasn't in the cache\n", | ||
109 | pos->s); | ||
110 | continue; | ||
111 | } | ||
112 | pr_warning("Couldn't remove %s: %s\n", | ||
113 | pos->s, strerror(errno)); | ||
114 | } | ||
115 | |||
116 | strlist__delete(list); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | int cmd_buildid_cache(int argc, const char **argv, const char *prefix __used) | ||
124 | { | ||
125 | argc = parse_options(argc, argv, buildid_cache_options, | ||
126 | buildid_cache_usage, 0); | ||
127 | |||
128 | if (symbol__init() < 0) | ||
129 | return -1; | ||
130 | |||
131 | setup_pager(); | ||
132 | return __cmd_buildid_cache(); | ||
133 | } | ||
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 1e99ac806913..431f204bde64 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -16,6 +16,7 @@ | |||
16 | 16 | ||
17 | static char const *input_name = "perf.data"; | 17 | static char const *input_name = "perf.data"; |
18 | static int force; | 18 | static int force; |
19 | static bool with_hits; | ||
19 | 20 | ||
20 | static const char * const buildid_list_usage[] = { | 21 | static const char * const buildid_list_usage[] = { |
21 | "perf buildid-list [<options>]", | 22 | "perf buildid-list [<options>]", |
@@ -23,6 +24,7 @@ static const char * const buildid_list_usage[] = { | |||
23 | }; | 24 | }; |
24 | 25 | ||
25 | static const struct option options[] = { | 26 | static const struct option options[] = { |
27 | OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"), | ||
26 | OPT_STRING('i', "input", &input_name, "file", | 28 | OPT_STRING('i', "input", &input_name, "file", |
27 | "input file name"), | 29 | "input file name"), |
28 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 30 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
@@ -31,26 +33,34 @@ static const struct option options[] = { | |||
31 | OPT_END() | 33 | OPT_END() |
32 | }; | 34 | }; |
33 | 35 | ||
34 | static int perf_file_section__process_buildids(struct perf_file_section *self, | 36 | static int build_id_list__process_event(event_t *event, |
35 | int feat, int fd) | 37 | struct perf_session *session) |
36 | { | 38 | { |
37 | if (feat != HEADER_BUILD_ID) | 39 | struct addr_location al; |
38 | return 0; | 40 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
41 | struct thread *thread = perf_session__findnew(session, event->ip.pid); | ||
39 | 42 | ||
40 | if (lseek(fd, self->offset, SEEK_SET) < 0) { | 43 | if (thread == NULL) { |
41 | pr_warning("Failed to lseek to %Ld offset for buildids!\n", | 44 | pr_err("problem processing %d event, skipping it.\n", |
42 | self->offset); | 45 | event->header.type); |
43 | return -1; | 46 | return -1; |
44 | } | 47 | } |
45 | 48 | ||
46 | if (perf_header__read_build_ids(fd, self->offset, self->size)) { | 49 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, |
47 | pr_warning("Failed to read buildids!\n"); | 50 | event->ip.ip, &al); |
48 | return -1; | 51 | |
49 | } | 52 | if (al.map != NULL) |
53 | al.map->dso->hit = 1; | ||
50 | 54 | ||
51 | return 0; | 55 | return 0; |
52 | } | 56 | } |
53 | 57 | ||
58 | static struct perf_event_ops build_id_list__event_ops = { | ||
59 | .sample = build_id_list__process_event, | ||
60 | .mmap = event__process_mmap, | ||
61 | .fork = event__process_task, | ||
62 | }; | ||
63 | |||
54 | static int __cmd_buildid_list(void) | 64 | static int __cmd_buildid_list(void) |
55 | { | 65 | { |
56 | int err = -1; | 66 | int err = -1; |
@@ -60,10 +70,10 @@ static int __cmd_buildid_list(void) | |||
60 | if (session == NULL) | 70 | if (session == NULL) |
61 | return -1; | 71 | return -1; |
62 | 72 | ||
63 | err = perf_header__process_sections(&session->header, session->fd, | 73 | if (with_hits) |
64 | perf_file_section__process_buildids); | 74 | perf_session__process_events(session, &build_id_list__event_ops); |
65 | if (err >= 0) | 75 | |
66 | dsos__fprintf_buildid(stdout); | 76 | dsos__fprintf_buildid(stdout, with_hits); |
67 | 77 | ||
68 | perf_session__delete(session); | 78 | perf_session__delete(session); |
69 | return err; | 79 | return err; |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index bd71b8ceafb7..18b3f505f9db 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -42,8 +42,8 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi | |||
42 | struct addr_location al; | 42 | struct addr_location al; |
43 | struct sample_data data = { .period = 1, }; | 43 | struct sample_data data = { .period = 1, }; |
44 | 44 | ||
45 | dump_printf("(IP, %d): %d: %p\n", event->header.misc, | 45 | dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, |
46 | event->ip.pid, (void *)(long)event->ip.ip); | 46 | event->ip.pid, event->ip.ip); |
47 | 47 | ||
48 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { | 48 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { |
49 | pr_warning("problem processing %d event, skipping it.\n", | 49 | pr_warning("problem processing %d event, skipping it.\n", |
@@ -51,12 +51,12 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi | |||
51 | return -1; | 51 | return -1; |
52 | } | 52 | } |
53 | 53 | ||
54 | if (al.filtered) | 54 | if (al.filtered || al.sym == NULL) |
55 | return 0; | 55 | return 0; |
56 | 56 | ||
57 | event__parse_sample(event, session->sample_type, &data); | 57 | event__parse_sample(event, session->sample_type, &data); |
58 | 58 | ||
59 | if (al.sym && perf_session__add_hist_entry(session, &al, data.period)) { | 59 | if (perf_session__add_hist_entry(session, &al, data.period)) { |
60 | pr_warning("problem incrementing symbol count, skipping event\n"); | 60 | pr_warning("problem incrementing symbol count, skipping event\n"); |
61 | return -1; | 61 | return -1; |
62 | } | 62 | } |
@@ -66,12 +66,12 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi | |||
66 | } | 66 | } |
67 | 67 | ||
68 | static struct perf_event_ops event_ops = { | 68 | static struct perf_event_ops event_ops = { |
69 | .process_sample_event = diff__process_sample_event, | 69 | .sample = diff__process_sample_event, |
70 | .process_mmap_event = event__process_mmap, | 70 | .mmap = event__process_mmap, |
71 | .process_comm_event = event__process_comm, | 71 | .comm = event__process_comm, |
72 | .process_exit_event = event__process_task, | 72 | .exit = event__process_task, |
73 | .process_fork_event = event__process_task, | 73 | .fork = event__process_task, |
74 | .process_lost_event = event__process_lost, | 74 | .lost = event__process_lost, |
75 | }; | 75 | }; |
76 | 76 | ||
77 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, | 77 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, |
@@ -82,29 +82,19 @@ static void perf_session__insert_hist_entry_by_name(struct rb_root *root, | |||
82 | struct hist_entry *iter; | 82 | struct hist_entry *iter; |
83 | 83 | ||
84 | while (*p != NULL) { | 84 | while (*p != NULL) { |
85 | int cmp; | ||
86 | parent = *p; | 85 | parent = *p; |
87 | iter = rb_entry(parent, struct hist_entry, rb_node); | 86 | iter = rb_entry(parent, struct hist_entry, rb_node); |
88 | 87 | if (hist_entry__cmp(he, iter) < 0) | |
89 | cmp = strcmp(he->map->dso->name, iter->map->dso->name); | ||
90 | if (cmp > 0) | ||
91 | p = &(*p)->rb_left; | 88 | p = &(*p)->rb_left; |
92 | else if (cmp < 0) | 89 | else |
93 | p = &(*p)->rb_right; | 90 | p = &(*p)->rb_right; |
94 | else { | ||
95 | cmp = strcmp(he->sym->name, iter->sym->name); | ||
96 | if (cmp > 0) | ||
97 | p = &(*p)->rb_left; | ||
98 | else | ||
99 | p = &(*p)->rb_right; | ||
100 | } | ||
101 | } | 91 | } |
102 | 92 | ||
103 | rb_link_node(&he->rb_node, parent, p); | 93 | rb_link_node(&he->rb_node, parent, p); |
104 | rb_insert_color(&he->rb_node, root); | 94 | rb_insert_color(&he->rb_node, root); |
105 | } | 95 | } |
106 | 96 | ||
107 | static void perf_session__resort_by_name(struct perf_session *self) | 97 | static void perf_session__resort_hist_entries(struct perf_session *self) |
108 | { | 98 | { |
109 | unsigned long position = 1; | 99 | unsigned long position = 1; |
110 | struct rb_root tmp = RB_ROOT; | 100 | struct rb_root tmp = RB_ROOT; |
@@ -122,29 +112,28 @@ static void perf_session__resort_by_name(struct perf_session *self) | |||
122 | self->hists = tmp; | 112 | self->hists = tmp; |
123 | } | 113 | } |
124 | 114 | ||
115 | static void perf_session__set_hist_entries_positions(struct perf_session *self) | ||
116 | { | ||
117 | perf_session__output_resort(self, self->events_stats.total); | ||
118 | perf_session__resort_hist_entries(self); | ||
119 | } | ||
120 | |||
125 | static struct hist_entry * | 121 | static struct hist_entry * |
126 | perf_session__find_hist_entry_by_name(struct perf_session *self, | 122 | perf_session__find_hist_entry(struct perf_session *self, |
127 | struct hist_entry *he) | 123 | struct hist_entry *he) |
128 | { | 124 | { |
129 | struct rb_node *n = self->hists.rb_node; | 125 | struct rb_node *n = self->hists.rb_node; |
130 | 126 | ||
131 | while (n) { | 127 | while (n) { |
132 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); | 128 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); |
133 | int cmp = strcmp(he->map->dso->name, iter->map->dso->name); | 129 | int64_t cmp = hist_entry__cmp(he, iter); |
134 | 130 | ||
135 | if (cmp > 0) | 131 | if (cmp < 0) |
136 | n = n->rb_left; | 132 | n = n->rb_left; |
137 | else if (cmp < 0) | 133 | else if (cmp > 0) |
138 | n = n->rb_right; | 134 | n = n->rb_right; |
139 | else { | 135 | else |
140 | cmp = strcmp(he->sym->name, iter->sym->name); | 136 | return iter; |
141 | if (cmp > 0) | ||
142 | n = n->rb_left; | ||
143 | else if (cmp < 0) | ||
144 | n = n->rb_right; | ||
145 | else | ||
146 | return iter; | ||
147 | } | ||
148 | } | 137 | } |
149 | 138 | ||
150 | return NULL; | 139 | return NULL; |
@@ -155,11 +144,9 @@ static void perf_session__match_hists(struct perf_session *old_session, | |||
155 | { | 144 | { |
156 | struct rb_node *nd; | 145 | struct rb_node *nd; |
157 | 146 | ||
158 | perf_session__resort_by_name(old_session); | ||
159 | |||
160 | for (nd = rb_first(&new_session->hists); nd; nd = rb_next(nd)) { | 147 | for (nd = rb_first(&new_session->hists); nd; nd = rb_next(nd)) { |
161 | struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); | 148 | struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); |
162 | pos->pair = perf_session__find_hist_entry_by_name(old_session, pos); | 149 | pos->pair = perf_session__find_hist_entry(old_session, pos); |
163 | } | 150 | } |
164 | } | 151 | } |
165 | 152 | ||
@@ -177,9 +164,12 @@ static int __cmd_diff(void) | |||
177 | ret = perf_session__process_events(session[i], &event_ops); | 164 | ret = perf_session__process_events(session[i], &event_ops); |
178 | if (ret) | 165 | if (ret) |
179 | goto out_delete; | 166 | goto out_delete; |
180 | perf_session__output_resort(session[i], session[i]->events_stats.total); | ||
181 | } | 167 | } |
182 | 168 | ||
169 | perf_session__output_resort(session[1], session[1]->events_stats.total); | ||
170 | if (show_displacement) | ||
171 | perf_session__set_hist_entries_positions(session[0]); | ||
172 | |||
183 | perf_session__match_hists(session[0], session[1]); | 173 | perf_session__match_hists(session[0], session[1]); |
184 | perf_session__fprintf_hists(session[1], session[0], | 174 | perf_session__fprintf_hists(session[1], session[0], |
185 | show_displacement, stdout); | 175 | show_displacement, stdout); |
@@ -204,7 +194,7 @@ static const struct option options[] = { | |||
204 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 194 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
205 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | 195 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, |
206 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 196 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
207 | OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths, | 197 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, |
208 | "Don't shorten the pathnames taking into account the cwd"), | 198 | "Don't shorten the pathnames taking into account the cwd"), |
209 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 199 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
210 | "only consider symbols in these dsos"), | 200 | "only consider symbols in these dsos"), |
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 9f810b17c25c..215b584007b1 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c | |||
@@ -286,8 +286,7 @@ void list_common_cmds_help(void) | |||
286 | 286 | ||
287 | puts(" The most commonly used perf commands are:"); | 287 | puts(" The most commonly used perf commands are:"); |
288 | for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { | 288 | for (i = 0; i < ARRAY_SIZE(common_cmds); i++) { |
289 | printf(" %s ", common_cmds[i].name); | 289 | printf(" %-*s ", longest, common_cmds[i].name); |
290 | mput_char(' ', longest - strlen(common_cmds[i].name)); | ||
291 | puts(common_cmds[i].help); | 290 | puts(common_cmds[i].help); |
292 | } | 291 | } |
293 | } | 292 | } |
@@ -314,8 +313,6 @@ static const char *cmd_to_page(const char *perf_cmd) | |||
314 | return "perf"; | 313 | return "perf"; |
315 | else if (!prefixcmp(perf_cmd, "perf")) | 314 | else if (!prefixcmp(perf_cmd, "perf")) |
316 | return perf_cmd; | 315 | return perf_cmd; |
317 | else if (is_perf_command(perf_cmd)) | ||
318 | return prepend("perf-", perf_cmd); | ||
319 | else | 316 | else |
320 | return prepend("perf-", perf_cmd); | 317 | return prepend("perf-", perf_cmd); |
321 | } | 318 | } |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 93c67bf53d2c..5d5dc6b09617 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -92,23 +92,18 @@ static void setup_cpunode_map(void) | |||
92 | if (!dir1) | 92 | if (!dir1) |
93 | return; | 93 | return; |
94 | 94 | ||
95 | while (true) { | 95 | while ((dent1 = readdir(dir1)) != NULL) { |
96 | dent1 = readdir(dir1); | 96 | if (dent1->d_type != DT_DIR || |
97 | if (!dent1) | 97 | sscanf(dent1->d_name, "node%u", &mem) < 1) |
98 | break; | ||
99 | |||
100 | if (sscanf(dent1->d_name, "node%u", &mem) < 1) | ||
101 | continue; | 98 | continue; |
102 | 99 | ||
103 | snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name); | 100 | snprintf(buf, PATH_MAX, "%s/%s", PATH_SYS_NODE, dent1->d_name); |
104 | dir2 = opendir(buf); | 101 | dir2 = opendir(buf); |
105 | if (!dir2) | 102 | if (!dir2) |
106 | continue; | 103 | continue; |
107 | while (true) { | 104 | while ((dent2 = readdir(dir2)) != NULL) { |
108 | dent2 = readdir(dir2); | 105 | if (dent2->d_type != DT_LNK || |
109 | if (!dent2) | 106 | sscanf(dent2->d_name, "cpu%u", &cpu) < 1) |
110 | break; | ||
111 | if (sscanf(dent2->d_name, "cpu%u", &cpu) < 1) | ||
112 | continue; | 107 | continue; |
113 | cpunode_map[cpu] = mem; | 108 | cpunode_map[cpu] = mem; |
114 | } | 109 | } |
@@ -321,11 +316,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
321 | 316 | ||
322 | event__parse_sample(event, session->sample_type, &data); | 317 | event__parse_sample(event, session->sample_type, &data); |
323 | 318 | ||
324 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 319 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
325 | event->header.misc, | 320 | data.pid, data.tid, data.ip, data.period); |
326 | data.pid, data.tid, | ||
327 | (void *)(long)data.ip, | ||
328 | (long long)data.period); | ||
329 | 321 | ||
330 | thread = perf_session__findnew(session, event->ip.pid); | 322 | thread = perf_session__findnew(session, event->ip.pid); |
331 | if (thread == NULL) { | 323 | if (thread == NULL) { |
@@ -342,22 +334,9 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
342 | return 0; | 334 | return 0; |
343 | } | 335 | } |
344 | 336 | ||
345 | static int sample_type_check(struct perf_session *session) | ||
346 | { | ||
347 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { | ||
348 | fprintf(stderr, | ||
349 | "No trace sample to read. Did you call perf record " | ||
350 | "without -R?"); | ||
351 | return -1; | ||
352 | } | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static struct perf_event_ops event_ops = { | 337 | static struct perf_event_ops event_ops = { |
358 | .process_sample_event = process_sample_event, | 338 | .sample = process_sample_event, |
359 | .process_comm_event = event__process_comm, | 339 | .comm = event__process_comm, |
360 | .sample_type_check = sample_type_check, | ||
361 | }; | 340 | }; |
362 | 341 | ||
363 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) | 342 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) |
@@ -504,11 +483,14 @@ static void sort_result(void) | |||
504 | 483 | ||
505 | static int __cmd_kmem(void) | 484 | static int __cmd_kmem(void) |
506 | { | 485 | { |
507 | int err; | 486 | int err = -EINVAL; |
508 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); | 487 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); |
509 | if (session == NULL) | 488 | if (session == NULL) |
510 | return -ENOMEM; | 489 | return -ENOMEM; |
511 | 490 | ||
491 | if (!perf_session__has_traces(session, "kmem record")) | ||
492 | goto out_delete; | ||
493 | |||
512 | setup_pager(); | 494 | setup_pager(); |
513 | err = perf_session__process_events(session, &event_ops); | 495 | err = perf_session__process_events(session, &event_ops); |
514 | if (err != 0) | 496 | if (err != 0) |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index c1e6774fd3ed..34f2acb1cc88 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -55,11 +55,13 @@ static struct { | |||
55 | bool need_dwarf; | 55 | bool need_dwarf; |
56 | bool list_events; | 56 | bool list_events; |
57 | bool force_add; | 57 | bool force_add; |
58 | bool show_lines; | ||
58 | int nr_probe; | 59 | int nr_probe; |
59 | struct probe_point probes[MAX_PROBES]; | 60 | struct probe_point probes[MAX_PROBES]; |
60 | struct strlist *dellist; | 61 | struct strlist *dellist; |
61 | struct perf_session *psession; | 62 | struct perf_session *psession; |
62 | struct map *kmap; | 63 | struct map *kmap; |
64 | struct line_range line_range; | ||
63 | } session; | 65 | } session; |
64 | 66 | ||
65 | 67 | ||
@@ -137,6 +139,16 @@ static int open_vmlinux(void) | |||
137 | pr_debug("Try to open %s\n", session.kmap->dso->long_name); | 139 | pr_debug("Try to open %s\n", session.kmap->dso->long_name); |
138 | return open(session.kmap->dso->long_name, O_RDONLY); | 140 | return open(session.kmap->dso->long_name, O_RDONLY); |
139 | } | 141 | } |
142 | |||
143 | static int opt_show_lines(const struct option *opt __used, | ||
144 | const char *str, int unset __used) | ||
145 | { | ||
146 | if (str) | ||
147 | parse_line_range_desc(str, &session.line_range); | ||
148 | INIT_LIST_HEAD(&session.line_range.line_list); | ||
149 | session.show_lines = true; | ||
150 | return 0; | ||
151 | } | ||
140 | #endif | 152 | #endif |
141 | 153 | ||
142 | static const char * const probe_usage[] = { | 154 | static const char * const probe_usage[] = { |
@@ -144,6 +156,7 @@ static const char * const probe_usage[] = { | |||
144 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", | 156 | "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", |
145 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", | 157 | "perf probe [<options>] --del '[GROUP:]EVENT' ...", |
146 | "perf probe --list", | 158 | "perf probe --list", |
159 | "perf probe --line 'LINEDESC'", | ||
147 | NULL | 160 | NULL |
148 | }; | 161 | }; |
149 | 162 | ||
@@ -182,9 +195,32 @@ static const struct option options[] = { | |||
182 | opt_add_probe_event), | 195 | opt_add_probe_event), |
183 | OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" | 196 | OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" |
184 | " with existing name"), | 197 | " with existing name"), |
198 | #ifndef NO_LIBDWARF | ||
199 | OPT_CALLBACK('L', "line", NULL, | ||
200 | "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]", | ||
201 | "Show source code lines.", opt_show_lines), | ||
202 | #endif | ||
185 | OPT_END() | 203 | OPT_END() |
186 | }; | 204 | }; |
187 | 205 | ||
206 | /* Initialize symbol maps for vmlinux */ | ||
207 | static void init_vmlinux(void) | ||
208 | { | ||
209 | symbol_conf.sort_by_name = true; | ||
210 | if (symbol_conf.vmlinux_name == NULL) | ||
211 | symbol_conf.try_vmlinux_path = true; | ||
212 | else | ||
213 | pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); | ||
214 | if (symbol__init() < 0) | ||
215 | die("Failed to init symbol map."); | ||
216 | session.psession = perf_session__new(NULL, O_WRONLY, false); | ||
217 | if (session.psession == NULL) | ||
218 | die("Failed to init perf_session."); | ||
219 | session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION]; | ||
220 | if (!session.kmap) | ||
221 | die("Could not find kernel map.\n"); | ||
222 | } | ||
223 | |||
188 | int cmd_probe(int argc, const char **argv, const char *prefix __used) | 224 | int cmd_probe(int argc, const char **argv, const char *prefix __used) |
189 | { | 225 | { |
190 | int i, ret; | 226 | int i, ret; |
@@ -203,7 +239,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
203 | parse_probe_event_argv(argc, argv); | 239 | parse_probe_event_argv(argc, argv); |
204 | } | 240 | } |
205 | 241 | ||
206 | if ((!session.nr_probe && !session.dellist && !session.list_events)) | 242 | if ((!session.nr_probe && !session.dellist && !session.list_events && |
243 | !session.show_lines)) | ||
207 | usage_with_options(probe_usage, options); | 244 | usage_with_options(probe_usage, options); |
208 | 245 | ||
209 | if (debugfs_valid_mountpoint(debugfs_path) < 0) | 246 | if (debugfs_valid_mountpoint(debugfs_path) < 0) |
@@ -215,10 +252,34 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
215 | " --add/--del.\n"); | 252 | " --add/--del.\n"); |
216 | usage_with_options(probe_usage, options); | 253 | usage_with_options(probe_usage, options); |
217 | } | 254 | } |
255 | if (session.show_lines) { | ||
256 | pr_warning(" Error: Don't use --list with --line.\n"); | ||
257 | usage_with_options(probe_usage, options); | ||
258 | } | ||
218 | show_perf_probe_events(); | 259 | show_perf_probe_events(); |
219 | return 0; | 260 | return 0; |
220 | } | 261 | } |
221 | 262 | ||
263 | #ifndef NO_LIBDWARF | ||
264 | if (session.show_lines) { | ||
265 | if (session.nr_probe != 0 || session.dellist) { | ||
266 | pr_warning(" Error: Don't use --line with" | ||
267 | " --add/--del.\n"); | ||
268 | usage_with_options(probe_usage, options); | ||
269 | } | ||
270 | init_vmlinux(); | ||
271 | fd = open_vmlinux(); | ||
272 | if (fd < 0) | ||
273 | die("Could not open debuginfo file."); | ||
274 | ret = find_line_range(fd, &session.line_range); | ||
275 | if (ret <= 0) | ||
276 | die("Source line is not found.\n"); | ||
277 | close(fd); | ||
278 | show_line_range(&session.line_range); | ||
279 | return 0; | ||
280 | } | ||
281 | #endif | ||
282 | |||
222 | if (session.dellist) { | 283 | if (session.dellist) { |
223 | del_trace_kprobe_events(session.dellist); | 284 | del_trace_kprobe_events(session.dellist); |
224 | strlist__delete(session.dellist); | 285 | strlist__delete(session.dellist); |
@@ -226,20 +287,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
226 | return 0; | 287 | return 0; |
227 | } | 288 | } |
228 | 289 | ||
229 | /* Initialize symbol maps for vmlinux */ | 290 | /* Add probes */ |
230 | symbol_conf.sort_by_name = true; | 291 | init_vmlinux(); |
231 | if (symbol_conf.vmlinux_name == NULL) | ||
232 | symbol_conf.try_vmlinux_path = true; | ||
233 | if (symbol__init() < 0) | ||
234 | die("Failed to init symbol map."); | ||
235 | session.psession = perf_session__new(NULL, O_WRONLY, false); | ||
236 | if (session.psession == NULL) | ||
237 | die("Failed to init perf_session."); | ||
238 | session.kmap = map_groups__find_by_name(&session.psession->kmaps, | ||
239 | MAP__FUNCTION, | ||
240 | "[kernel.kallsyms]"); | ||
241 | if (!session.kmap) | ||
242 | die("Could not find kernel map.\n"); | ||
243 | 292 | ||
244 | if (session.need_dwarf) | 293 | if (session.need_dwarf) |
245 | #ifdef NO_LIBDWARF | 294 | #ifdef NO_LIBDWARF |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 265425322734..90345223908c 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -113,12 +113,24 @@ static void write_output(void *buf, size_t size) | |||
113 | 113 | ||
114 | static void write_event(event_t *buf, size_t size) | 114 | static void write_event(event_t *buf, size_t size) |
115 | { | 115 | { |
116 | /* | 116 | size_t processed_size = buf->header.size; |
117 | * Add it to the list of DSOs, so that when we finish this | 117 | event_t *ev = buf; |
118 | * record session we can pick the available build-ids. | 118 | |
119 | */ | 119 | do { |
120 | if (buf->header.type == PERF_RECORD_MMAP) | 120 | /* |
121 | dsos__findnew(buf->mmap.filename); | 121 | * Add it to the list of DSOs, so that when we finish this |
122 | * record session we can pick the available build-ids. | ||
123 | */ | ||
124 | if (ev->header.type == PERF_RECORD_MMAP) { | ||
125 | struct list_head *head = &dsos__user; | ||
126 | if (ev->header.misc == 1) | ||
127 | head = &dsos__kernel; | ||
128 | __dsos__findnew(head, ev->mmap.filename); | ||
129 | } | ||
130 | |||
131 | ev = ((void *)ev) + ev->header.size; | ||
132 | processed_size += ev->header.size; | ||
133 | } while (processed_size < size); | ||
122 | 134 | ||
123 | write_output(buf, size); | 135 | write_output(buf, size); |
124 | } | 136 | } |
@@ -551,6 +563,19 @@ static int __cmd_record(int argc, const char **argv) | |||
551 | return err; | 563 | return err; |
552 | } | 564 | } |
553 | 565 | ||
566 | err = event__synthesize_kernel_mmap(process_synthesized_event, | ||
567 | session, "_text"); | ||
568 | if (err < 0) { | ||
569 | pr_err("Couldn't record kernel reference relocation symbol.\n"); | ||
570 | return err; | ||
571 | } | ||
572 | |||
573 | err = event__synthesize_modules(process_synthesized_event, session); | ||
574 | if (err < 0) { | ||
575 | pr_err("Couldn't record kernel reference relocation symbol.\n"); | ||
576 | return err; | ||
577 | } | ||
578 | |||
554 | if (!system_wide && profile_cpu == -1) | 579 | if (!system_wide && profile_cpu == -1) |
555 | event__synthesize_thread(pid, process_synthesized_event, | 580 | event__synthesize_thread(pid, process_synthesized_event, |
556 | session); | 581 | session); |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 860f1eeeea7d..cfc655d40bb7 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -34,6 +34,8 @@ | |||
34 | static char const *input_name = "perf.data"; | 34 | static char const *input_name = "perf.data"; |
35 | 35 | ||
36 | static int force; | 36 | static int force; |
37 | static bool hide_unresolved; | ||
38 | static bool dont_use_callchains; | ||
37 | 39 | ||
38 | static int show_threads; | 40 | static int show_threads; |
39 | static struct perf_read_values show_threads_values; | 41 | static struct perf_read_values show_threads_values; |
@@ -91,11 +93,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
91 | 93 | ||
92 | event__parse_sample(event, session->sample_type, &data); | 94 | event__parse_sample(event, session->sample_type, &data); |
93 | 95 | ||
94 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 96 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
95 | event->header.misc, | 97 | data.pid, data.tid, data.ip, data.period); |
96 | data.pid, data.tid, | ||
97 | (void *)(long)data.ip, | ||
98 | (long long)data.period); | ||
99 | 98 | ||
100 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { | 99 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { |
101 | unsigned int i; | 100 | unsigned int i; |
@@ -121,7 +120,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
121 | return -1; | 120 | return -1; |
122 | } | 121 | } |
123 | 122 | ||
124 | if (al.filtered) | 123 | if (al.filtered || (hide_unresolved && al.sym == NULL)) |
125 | return 0; | 124 | return 0; |
126 | 125 | ||
127 | if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) { | 126 | if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) { |
@@ -156,14 +155,14 @@ static int process_read_event(event_t *event, struct perf_session *session __use | |||
156 | return 0; | 155 | return 0; |
157 | } | 156 | } |
158 | 157 | ||
159 | static int sample_type_check(struct perf_session *session) | 158 | static int perf_session__setup_sample_type(struct perf_session *self) |
160 | { | 159 | { |
161 | if (!(session->sample_type & PERF_SAMPLE_CALLCHAIN)) { | 160 | if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) { |
162 | if (sort__has_parent) { | 161 | if (sort__has_parent) { |
163 | fprintf(stderr, "selected --sort parent, but no" | 162 | fprintf(stderr, "selected --sort parent, but no" |
164 | " callchain data. Did you call" | 163 | " callchain data. Did you call" |
165 | " perf record without -g?\n"); | 164 | " perf record without -g?\n"); |
166 | return -1; | 165 | return -EINVAL; |
167 | } | 166 | } |
168 | if (symbol_conf.use_callchain) { | 167 | if (symbol_conf.use_callchain) { |
169 | fprintf(stderr, "selected -g but no callchain data." | 168 | fprintf(stderr, "selected -g but no callchain data." |
@@ -171,12 +170,13 @@ static int sample_type_check(struct perf_session *session) | |||
171 | " -g?\n"); | 170 | " -g?\n"); |
172 | return -1; | 171 | return -1; |
173 | } | 172 | } |
174 | } else if (callchain_param.mode != CHAIN_NONE && !symbol_conf.use_callchain) { | 173 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
174 | !symbol_conf.use_callchain) { | ||
175 | symbol_conf.use_callchain = true; | 175 | symbol_conf.use_callchain = true; |
176 | if (register_callchain_param(&callchain_param) < 0) { | 176 | if (register_callchain_param(&callchain_param) < 0) { |
177 | fprintf(stderr, "Can't register callchain" | 177 | fprintf(stderr, "Can't register callchain" |
178 | " params\n"); | 178 | " params\n"); |
179 | return -1; | 179 | return -EINVAL; |
180 | } | 180 | } |
181 | } | 181 | } |
182 | 182 | ||
@@ -184,20 +184,18 @@ static int sample_type_check(struct perf_session *session) | |||
184 | } | 184 | } |
185 | 185 | ||
186 | static struct perf_event_ops event_ops = { | 186 | static struct perf_event_ops event_ops = { |
187 | .process_sample_event = process_sample_event, | 187 | .sample = process_sample_event, |
188 | .process_mmap_event = event__process_mmap, | 188 | .mmap = event__process_mmap, |
189 | .process_comm_event = event__process_comm, | 189 | .comm = event__process_comm, |
190 | .process_exit_event = event__process_task, | 190 | .exit = event__process_task, |
191 | .process_fork_event = event__process_task, | 191 | .fork = event__process_task, |
192 | .process_lost_event = event__process_lost, | 192 | .lost = event__process_lost, |
193 | .process_read_event = process_read_event, | 193 | .read = process_read_event, |
194 | .sample_type_check = sample_type_check, | ||
195 | }; | 194 | }; |
196 | 195 | ||
197 | |||
198 | static int __cmd_report(void) | 196 | static int __cmd_report(void) |
199 | { | 197 | { |
200 | int ret; | 198 | int ret = -EINVAL; |
201 | struct perf_session *session; | 199 | struct perf_session *session; |
202 | 200 | ||
203 | session = perf_session__new(input_name, O_RDONLY, force); | 201 | session = perf_session__new(input_name, O_RDONLY, force); |
@@ -207,6 +205,10 @@ static int __cmd_report(void) | |||
207 | if (show_threads) | 205 | if (show_threads) |
208 | perf_read_values_init(&show_threads_values); | 206 | perf_read_values_init(&show_threads_values); |
209 | 207 | ||
208 | ret = perf_session__setup_sample_type(session); | ||
209 | if (ret) | ||
210 | goto out_delete; | ||
211 | |||
210 | ret = perf_session__process_events(session, &event_ops); | 212 | ret = perf_session__process_events(session, &event_ops); |
211 | if (ret) | 213 | if (ret) |
212 | goto out_delete; | 214 | goto out_delete; |
@@ -243,11 +245,19 @@ out_delete: | |||
243 | 245 | ||
244 | static int | 246 | static int |
245 | parse_callchain_opt(const struct option *opt __used, const char *arg, | 247 | parse_callchain_opt(const struct option *opt __used, const char *arg, |
246 | int unset __used) | 248 | int unset) |
247 | { | 249 | { |
248 | char *tok; | 250 | char *tok; |
249 | char *endptr; | 251 | char *endptr; |
250 | 252 | ||
253 | /* | ||
254 | * --no-call-graph | ||
255 | */ | ||
256 | if (unset) { | ||
257 | dont_use_callchains = true; | ||
258 | return 0; | ||
259 | } | ||
260 | |||
251 | symbol_conf.use_callchain = true; | 261 | symbol_conf.use_callchain = true; |
252 | 262 | ||
253 | if (!arg) | 263 | if (!arg) |
@@ -319,7 +329,7 @@ static const struct option options[] = { | |||
319 | "pretty printing style key: normal raw"), | 329 | "pretty printing style key: normal raw"), |
320 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 330 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
321 | "sort by key(s): pid, comm, dso, symbol, parent"), | 331 | "sort by key(s): pid, comm, dso, symbol, parent"), |
322 | OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths, | 332 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, |
323 | "Don't shorten the pathnames taking into account the cwd"), | 333 | "Don't shorten the pathnames taking into account the cwd"), |
324 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 334 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
325 | "regex filter to identify parent, see: '--sort parent'"), | 335 | "regex filter to identify parent, see: '--sort parent'"), |
@@ -340,6 +350,8 @@ static const struct option options[] = { | |||
340 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", | 350 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", |
341 | "separator for columns, no spaces will be added between " | 351 | "separator for columns, no spaces will be added between " |
342 | "columns '.' is reserved."), | 352 | "columns '.' is reserved."), |
353 | OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved, | ||
354 | "Only display entries resolved to a symbol"), | ||
343 | OPT_END() | 355 | OPT_END() |
344 | }; | 356 | }; |
345 | 357 | ||
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 80209df6cfe8..4f5a03e43444 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -1621,11 +1621,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
1621 | 1621 | ||
1622 | event__parse_sample(event, session->sample_type, &data); | 1622 | event__parse_sample(event, session->sample_type, &data); |
1623 | 1623 | ||
1624 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 1624 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
1625 | event->header.misc, | 1625 | data.pid, data.tid, data.ip, data.period); |
1626 | data.pid, data.tid, | ||
1627 | (void *)(long)data.ip, | ||
1628 | (long long)data.period); | ||
1629 | 1626 | ||
1630 | thread = perf_session__findnew(session, data.pid); | 1627 | thread = perf_session__findnew(session, data.pid); |
1631 | if (thread == NULL) { | 1628 | if (thread == NULL) { |
@@ -1653,33 +1650,22 @@ static int process_lost_event(event_t *event __used, | |||
1653 | return 0; | 1650 | return 0; |
1654 | } | 1651 | } |
1655 | 1652 | ||
1656 | static int sample_type_check(struct perf_session *session __used) | ||
1657 | { | ||
1658 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { | ||
1659 | fprintf(stderr, | ||
1660 | "No trace sample to read. Did you call perf record " | ||
1661 | "without -R?"); | ||
1662 | return -1; | ||
1663 | } | ||
1664 | |||
1665 | return 0; | ||
1666 | } | ||
1667 | |||
1668 | static struct perf_event_ops event_ops = { | 1653 | static struct perf_event_ops event_ops = { |
1669 | .process_sample_event = process_sample_event, | 1654 | .sample = process_sample_event, |
1670 | .process_comm_event = event__process_comm, | 1655 | .comm = event__process_comm, |
1671 | .process_lost_event = process_lost_event, | 1656 | .lost = process_lost_event, |
1672 | .sample_type_check = sample_type_check, | ||
1673 | }; | 1657 | }; |
1674 | 1658 | ||
1675 | static int read_events(void) | 1659 | static int read_events(void) |
1676 | { | 1660 | { |
1677 | int err; | 1661 | int err = -EINVAL; |
1678 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); | 1662 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); |
1679 | if (session == NULL) | 1663 | if (session == NULL) |
1680 | return -ENOMEM; | 1664 | return -ENOMEM; |
1681 | 1665 | ||
1682 | err = perf_session__process_events(session, &event_ops); | 1666 | if (perf_session__has_traces(session, "record -R")) |
1667 | err = perf_session__process_events(session, &event_ops); | ||
1668 | |||
1683 | perf_session__delete(session); | 1669 | perf_session__delete(session); |
1684 | return err; | 1670 | return err; |
1685 | } | 1671 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c70d72003557..e8c85d5aec41 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -44,6 +44,7 @@ | |||
44 | #include "util/parse-events.h" | 44 | #include "util/parse-events.h" |
45 | #include "util/event.h" | 45 | #include "util/event.h" |
46 | #include "util/debug.h" | 46 | #include "util/debug.h" |
47 | #include "util/header.h" | ||
47 | 48 | ||
48 | #include <sys/prctl.h> | 49 | #include <sys/prctl.h> |
49 | #include <math.h> | 50 | #include <math.h> |
@@ -79,6 +80,8 @@ static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | |||
79 | 80 | ||
80 | static int event_scaled[MAX_COUNTERS]; | 81 | static int event_scaled[MAX_COUNTERS]; |
81 | 82 | ||
83 | static volatile int done = 0; | ||
84 | |||
82 | struct stats | 85 | struct stats |
83 | { | 86 | { |
84 | double n, mean, M2; | 87 | double n, mean, M2; |
@@ -247,61 +250,64 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
247 | unsigned long long t0, t1; | 250 | unsigned long long t0, t1; |
248 | int status = 0; | 251 | int status = 0; |
249 | int counter; | 252 | int counter; |
250 | int pid; | 253 | int pid = target_pid; |
251 | int child_ready_pipe[2], go_pipe[2]; | 254 | int child_ready_pipe[2], go_pipe[2]; |
255 | const bool forks = (target_pid == -1 && argc > 0); | ||
252 | char buf; | 256 | char buf; |
253 | 257 | ||
254 | if (!system_wide) | 258 | if (!system_wide) |
255 | nr_cpus = 1; | 259 | nr_cpus = 1; |
256 | 260 | ||
257 | if (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0) { | 261 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { |
258 | perror("failed to create pipes"); | 262 | perror("failed to create pipes"); |
259 | exit(1); | 263 | exit(1); |
260 | } | 264 | } |
261 | 265 | ||
262 | if ((pid = fork()) < 0) | 266 | if (forks) { |
263 | perror("failed to fork"); | 267 | if ((pid = fork()) < 0) |
268 | perror("failed to fork"); | ||
269 | |||
270 | if (!pid) { | ||
271 | close(child_ready_pipe[0]); | ||
272 | close(go_pipe[1]); | ||
273 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
274 | |||
275 | /* | ||
276 | * Do a dummy execvp to get the PLT entry resolved, | ||
277 | * so we avoid the resolver overhead on the real | ||
278 | * execvp call. | ||
279 | */ | ||
280 | execvp("", (char **)argv); | ||
281 | |||
282 | /* | ||
283 | * Tell the parent we're ready to go | ||
284 | */ | ||
285 | close(child_ready_pipe[1]); | ||
286 | |||
287 | /* | ||
288 | * Wait until the parent tells us to go. | ||
289 | */ | ||
290 | if (read(go_pipe[0], &buf, 1) == -1) | ||
291 | perror("unable to read pipe"); | ||
292 | |||
293 | execvp(argv[0], (char **)argv); | ||
294 | |||
295 | perror(argv[0]); | ||
296 | exit(-1); | ||
297 | } | ||
264 | 298 | ||
265 | if (!pid) { | 299 | child_pid = pid; |
266 | close(child_ready_pipe[0]); | ||
267 | close(go_pipe[1]); | ||
268 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
269 | 300 | ||
270 | /* | 301 | /* |
271 | * Do a dummy execvp to get the PLT entry resolved, | 302 | * Wait for the child to be ready to exec. |
272 | * so we avoid the resolver overhead on the real | ||
273 | * execvp call. | ||
274 | */ | ||
275 | execvp("", (char **)argv); | ||
276 | |||
277 | /* | ||
278 | * Tell the parent we're ready to go | ||
279 | */ | 303 | */ |
280 | close(child_ready_pipe[1]); | 304 | close(child_ready_pipe[1]); |
281 | 305 | close(go_pipe[0]); | |
282 | /* | 306 | if (read(child_ready_pipe[0], &buf, 1) == -1) |
283 | * Wait until the parent tells us to go. | ||
284 | */ | ||
285 | if (read(go_pipe[0], &buf, 1) == -1) | ||
286 | perror("unable to read pipe"); | 307 | perror("unable to read pipe"); |
287 | 308 | close(child_ready_pipe[0]); | |
288 | execvp(argv[0], (char **)argv); | ||
289 | |||
290 | perror(argv[0]); | ||
291 | exit(-1); | ||
292 | } | 309 | } |
293 | 310 | ||
294 | child_pid = pid; | ||
295 | |||
296 | /* | ||
297 | * Wait for the child to be ready to exec. | ||
298 | */ | ||
299 | close(child_ready_pipe[1]); | ||
300 | close(go_pipe[0]); | ||
301 | if (read(child_ready_pipe[0], &buf, 1) == -1) | ||
302 | perror("unable to read pipe"); | ||
303 | close(child_ready_pipe[0]); | ||
304 | |||
305 | for (counter = 0; counter < nr_counters; counter++) | 311 | for (counter = 0; counter < nr_counters; counter++) |
306 | create_perf_stat_counter(counter, pid); | 312 | create_perf_stat_counter(counter, pid); |
307 | 313 | ||
@@ -310,8 +316,12 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
310 | */ | 316 | */ |
311 | t0 = rdclock(); | 317 | t0 = rdclock(); |
312 | 318 | ||
313 | close(go_pipe[1]); | 319 | if (forks) { |
314 | wait(&status); | 320 | close(go_pipe[1]); |
321 | wait(&status); | ||
322 | } else { | ||
323 | while(!done); | ||
324 | } | ||
315 | 325 | ||
316 | t1 = rdclock(); | 326 | t1 = rdclock(); |
317 | 327 | ||
@@ -417,10 +427,13 @@ static void print_stat(int argc, const char **argv) | |||
417 | fflush(stdout); | 427 | fflush(stdout); |
418 | 428 | ||
419 | fprintf(stderr, "\n"); | 429 | fprintf(stderr, "\n"); |
420 | fprintf(stderr, " Performance counter stats for \'%s", argv[0]); | 430 | fprintf(stderr, " Performance counter stats for "); |
421 | 431 | if(target_pid == -1) { | |
422 | for (i = 1; i < argc; i++) | 432 | fprintf(stderr, "\'%s", argv[0]); |
423 | fprintf(stderr, " %s", argv[i]); | 433 | for (i = 1; i < argc; i++) |
434 | fprintf(stderr, " %s", argv[i]); | ||
435 | }else | ||
436 | fprintf(stderr, "task pid \'%d", target_pid); | ||
424 | 437 | ||
425 | fprintf(stderr, "\'"); | 438 | fprintf(stderr, "\'"); |
426 | if (run_count > 1) | 439 | if (run_count > 1) |
@@ -445,6 +458,9 @@ static volatile int signr = -1; | |||
445 | 458 | ||
446 | static void skip_signal(int signo) | 459 | static void skip_signal(int signo) |
447 | { | 460 | { |
461 | if(target_pid != -1) | ||
462 | done = 1; | ||
463 | |||
448 | signr = signo; | 464 | signr = signo; |
449 | } | 465 | } |
450 | 466 | ||
@@ -461,7 +477,7 @@ static void sig_atexit(void) | |||
461 | } | 477 | } |
462 | 478 | ||
463 | static const char * const stat_usage[] = { | 479 | static const char * const stat_usage[] = { |
464 | "perf stat [<options>] <command>", | 480 | "perf stat [<options>] [<command>]", |
465 | NULL | 481 | NULL |
466 | }; | 482 | }; |
467 | 483 | ||
@@ -492,7 +508,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
492 | 508 | ||
493 | argc = parse_options(argc, argv, options, stat_usage, | 509 | argc = parse_options(argc, argv, options, stat_usage, |
494 | PARSE_OPT_STOP_AT_NON_OPTION); | 510 | PARSE_OPT_STOP_AT_NON_OPTION); |
495 | if (!argc) | 511 | if (!argc && target_pid == -1) |
496 | usage_with_options(stat_usage, options); | 512 | usage_with_options(stat_usage, options); |
497 | if (run_count <= 0) | 513 | if (run_count <= 0) |
498 | usage_with_options(stat_usage, options); | 514 | usage_with_options(stat_usage, options); |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 3f8bbcfb1e9b..0d4d8ff7914b 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -1029,33 +1029,24 @@ static void process_samples(struct perf_session *session) | |||
1029 | } | 1029 | } |
1030 | } | 1030 | } |
1031 | 1031 | ||
1032 | static int sample_type_check(struct perf_session *session) | ||
1033 | { | ||
1034 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { | ||
1035 | fprintf(stderr, "No trace samples found in the file.\n" | ||
1036 | "Have you used 'perf timechart record' to record it?\n"); | ||
1037 | return -1; | ||
1038 | } | ||
1039 | |||
1040 | return 0; | ||
1041 | } | ||
1042 | |||
1043 | static struct perf_event_ops event_ops = { | 1032 | static struct perf_event_ops event_ops = { |
1044 | .process_comm_event = process_comm_event, | 1033 | .comm = process_comm_event, |
1045 | .process_fork_event = process_fork_event, | 1034 | .fork = process_fork_event, |
1046 | .process_exit_event = process_exit_event, | 1035 | .exit = process_exit_event, |
1047 | .process_sample_event = queue_sample_event, | 1036 | .sample = queue_sample_event, |
1048 | .sample_type_check = sample_type_check, | ||
1049 | }; | 1037 | }; |
1050 | 1038 | ||
1051 | static int __cmd_timechart(void) | 1039 | static int __cmd_timechart(void) |
1052 | { | 1040 | { |
1053 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); | 1041 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); |
1054 | int ret; | 1042 | int ret = -EINVAL; |
1055 | 1043 | ||
1056 | if (session == NULL) | 1044 | if (session == NULL) |
1057 | return -ENOMEM; | 1045 | return -ENOMEM; |
1058 | 1046 | ||
1047 | if (!perf_session__has_traces(session, "timechart record")) | ||
1048 | goto out_delete; | ||
1049 | |||
1059 | ret = perf_session__process_events(session, &event_ops); | 1050 | ret = perf_session__process_events(session, &event_ops); |
1060 | if (ret) | 1051 | if (ret) |
1061 | goto out_delete; | 1052 | goto out_delete; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index ddc584b64871..1fc018e048e1 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -667,7 +667,7 @@ static void prompt_symbol(struct sym_entry **target, const char *msg) | |||
667 | } | 667 | } |
668 | 668 | ||
669 | if (!found) { | 669 | if (!found) { |
670 | fprintf(stderr, "Sorry, %s is not active.\n", sym_filter); | 670 | fprintf(stderr, "Sorry, %s is not active.\n", buf); |
671 | sleep(1); | 671 | sleep(1); |
672 | return; | 672 | return; |
673 | } else | 673 | } else |
@@ -934,8 +934,11 @@ static void event__process_sample(const event_t *self, | |||
934 | struct addr_location al; | 934 | struct addr_location al; |
935 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 935 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
936 | 936 | ||
937 | ++samples; | ||
938 | |||
937 | switch (origin) { | 939 | switch (origin) { |
938 | case PERF_RECORD_MISC_USER: | 940 | case PERF_RECORD_MISC_USER: |
941 | ++userspace_samples; | ||
939 | if (hide_user_symbols) | 942 | if (hide_user_symbols) |
940 | return; | 943 | return; |
941 | break; | 944 | break; |
@@ -948,9 +951,31 @@ static void event__process_sample(const event_t *self, | |||
948 | } | 951 | } |
949 | 952 | ||
950 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || | 953 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || |
951 | al.sym == NULL || al.filtered) | 954 | al.filtered) |
952 | return; | 955 | return; |
953 | 956 | ||
957 | if (al.sym == NULL) { | ||
958 | /* | ||
959 | * As we do lazy loading of symtabs we only will know if the | ||
960 | * specified vmlinux file is invalid when we actually have a | ||
961 | * hit in kernel space and then try to load it. So if we get | ||
962 | * here and there are _no_ symbols in the DSO backing the | ||
963 | * kernel map, bail out. | ||
964 | * | ||
965 | * We may never get here, for instance, if we use -K/ | ||
966 | * --hide-kernel-symbols, even if the user specifies an | ||
967 | * invalid --vmlinux ;-) | ||
968 | */ | ||
969 | if (al.map == session->vmlinux_maps[MAP__FUNCTION] && | ||
970 | RB_EMPTY_ROOT(&al.map->dso->symbols[MAP__FUNCTION])) { | ||
971 | pr_err("The %s file can't be used\n", | ||
972 | symbol_conf.vmlinux_name); | ||
973 | exit(1); | ||
974 | } | ||
975 | |||
976 | return; | ||
977 | } | ||
978 | |||
954 | syme = symbol__priv(al.sym); | 979 | syme = symbol__priv(al.sym); |
955 | if (!syme->skip) { | 980 | if (!syme->skip) { |
956 | syme->count[counter]++; | 981 | syme->count[counter]++; |
@@ -960,9 +985,6 @@ static void event__process_sample(const event_t *self, | |||
960 | if (list_empty(&syme->node) || !syme->node.next) | 985 | if (list_empty(&syme->node) || !syme->node.next) |
961 | __list_insert_active_sym(syme); | 986 | __list_insert_active_sym(syme); |
962 | pthread_mutex_unlock(&active_symbols_lock); | 987 | pthread_mutex_unlock(&active_symbols_lock); |
963 | if (origin == PERF_RECORD_MISC_USER) | ||
964 | ++userspace_samples; | ||
965 | ++samples; | ||
966 | } | 988 | } |
967 | } | 989 | } |
968 | 990 | ||
@@ -975,6 +997,10 @@ static int event__process(event_t *event, struct perf_session *session) | |||
975 | case PERF_RECORD_MMAP: | 997 | case PERF_RECORD_MMAP: |
976 | event__process_mmap(event, session); | 998 | event__process_mmap(event, session); |
977 | break; | 999 | break; |
1000 | case PERF_RECORD_FORK: | ||
1001 | case PERF_RECORD_EXIT: | ||
1002 | event__process_task(event, session); | ||
1003 | break; | ||
978 | default: | 1004 | default: |
979 | break; | 1005 | break; |
980 | } | 1006 | } |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 574a215e800b..0b65779e3c10 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -75,11 +75,8 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
75 | 75 | ||
76 | event__parse_sample(event, session->sample_type, &data); | 76 | event__parse_sample(event, session->sample_type, &data); |
77 | 77 | ||
78 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 78 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, |
79 | event->header.misc, | 79 | data.pid, data.tid, data.ip, data.period); |
80 | data.pid, data.tid, | ||
81 | (void *)(long)data.ip, | ||
82 | (long long)data.period); | ||
83 | 80 | ||
84 | thread = perf_session__findnew(session, event->ip.pid); | 81 | thread = perf_session__findnew(session, event->ip.pid); |
85 | if (thread == NULL) { | 82 | if (thread == NULL) { |
@@ -103,22 +100,9 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
103 | return 0; | 100 | return 0; |
104 | } | 101 | } |
105 | 102 | ||
106 | static int sample_type_check(struct perf_session *session) | ||
107 | { | ||
108 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { | ||
109 | fprintf(stderr, | ||
110 | "No trace sample to read. Did you call perf record " | ||
111 | "without -R?"); | ||
112 | return -1; | ||
113 | } | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static struct perf_event_ops event_ops = { | 103 | static struct perf_event_ops event_ops = { |
119 | .process_sample_event = process_sample_event, | 104 | .sample = process_sample_event, |
120 | .process_comm_event = event__process_comm, | 105 | .comm = event__process_comm, |
121 | .sample_type_check = sample_type_check, | ||
122 | }; | 106 | }; |
123 | 107 | ||
124 | static int __cmd_trace(struct perf_session *session) | 108 | static int __cmd_trace(struct perf_session *session) |
@@ -531,6 +515,8 @@ static const struct option options[] = { | |||
531 | parse_scriptname), | 515 | parse_scriptname), |
532 | OPT_STRING('g', "gen-script", &generate_script_lang, "lang", | 516 | OPT_STRING('g', "gen-script", &generate_script_lang, "lang", |
533 | "generate perf-trace.xx script in specified language"), | 517 | "generate perf-trace.xx script in specified language"), |
518 | OPT_STRING('i', "input", &input_name, "file", | ||
519 | "input file name"), | ||
534 | 520 | ||
535 | OPT_END() | 521 | OPT_END() |
536 | }; | 522 | }; |
@@ -592,6 +578,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
592 | if (session == NULL) | 578 | if (session == NULL) |
593 | return -ENOMEM; | 579 | return -ENOMEM; |
594 | 580 | ||
581 | if (!perf_session__has_traces(session, "record -R")) | ||
582 | return -EINVAL; | ||
583 | |||
595 | if (generate_script_lang) { | 584 | if (generate_script_lang) { |
596 | struct stat perf_stat; | 585 | struct stat perf_stat; |
597 | 586 | ||
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 18035b1f16c7..dee97cfe3794 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -16,6 +16,7 @@ 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); | 18 | extern int cmd_bench(int argc, const char **argv, const char *prefix); |
19 | extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); | ||
19 | extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); | 20 | extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); |
20 | extern int cmd_diff(int argc, const char **argv, const char *prefix); | 21 | extern int cmd_diff(int argc, const char **argv, const char *prefix); |
21 | extern int cmd_help(int argc, const char **argv, const char *prefix); | 22 | extern int cmd_help(int argc, const char **argv, const char *prefix); |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 71dc7c3fe7b2..9afcff2e3ae5 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -3,7 +3,9 @@ | |||
3 | # command name category [deprecated] [common] | 3 | # command name category [deprecated] [common] |
4 | # | 4 | # |
5 | perf-annotate mainporcelain common | 5 | perf-annotate mainporcelain common |
6 | perf-archive mainporcelain common | ||
6 | perf-bench mainporcelain common | 7 | perf-bench mainporcelain common |
8 | perf-buildid-cache mainporcelain common | ||
7 | perf-buildid-list mainporcelain common | 9 | perf-buildid-list mainporcelain common |
8 | perf-diff mainporcelain common | 10 | perf-diff mainporcelain common |
9 | perf-list mainporcelain common | 11 | perf-list mainporcelain common |
diff --git a/tools/perf/design.txt b/tools/perf/design.txt index 8d0de5130db3..bd0bb1b1279b 100644 --- a/tools/perf/design.txt +++ b/tools/perf/design.txt | |||
@@ -101,10 +101,10 @@ enum hw_event_ids { | |||
101 | */ | 101 | */ |
102 | PERF_COUNT_HW_CPU_CYCLES = 0, | 102 | PERF_COUNT_HW_CPU_CYCLES = 0, |
103 | PERF_COUNT_HW_INSTRUCTIONS = 1, | 103 | PERF_COUNT_HW_INSTRUCTIONS = 1, |
104 | PERF_COUNT_HW_CACHE_REFERENCES = 2, | 104 | PERF_COUNT_HW_CACHE_REFERENCES = 2, |
105 | PERF_COUNT_HW_CACHE_MISSES = 3, | 105 | PERF_COUNT_HW_CACHE_MISSES = 3, |
106 | PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, | 106 | PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, |
107 | PERF_COUNT_HW_BRANCH_MISSES = 5, | 107 | PERF_COUNT_HW_BRANCH_MISSES = 5, |
108 | PERF_COUNT_HW_BUS_CYCLES = 6, | 108 | PERF_COUNT_HW_BUS_CYCLES = 6, |
109 | }; | 109 | }; |
110 | 110 | ||
@@ -131,8 +131,8 @@ software events, selected by 'event_id': | |||
131 | */ | 131 | */ |
132 | enum sw_event_ids { | 132 | enum sw_event_ids { |
133 | PERF_COUNT_SW_CPU_CLOCK = 0, | 133 | PERF_COUNT_SW_CPU_CLOCK = 0, |
134 | PERF_COUNT_SW_TASK_CLOCK = 1, | 134 | PERF_COUNT_SW_TASK_CLOCK = 1, |
135 | PERF_COUNT_SW_PAGE_FAULTS = 2, | 135 | PERF_COUNT_SW_PAGE_FAULTS = 2, |
136 | PERF_COUNT_SW_CONTEXT_SWITCHES = 3, | 136 | PERF_COUNT_SW_CONTEXT_SWITCHES = 3, |
137 | PERF_COUNT_SW_CPU_MIGRATIONS = 4, | 137 | PERF_COUNT_SW_CPU_MIGRATIONS = 4, |
138 | PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, | 138 | PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, |
diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh new file mode 100644 index 000000000000..45fbe2f07b15 --- /dev/null +++ b/tools/perf/perf-archive.sh | |||
@@ -0,0 +1,32 @@ | |||
1 | #!/bin/bash | ||
2 | # perf archive | ||
3 | # Arnaldo Carvalho de Melo <acme@redhat.com> | ||
4 | |||
5 | PERF_DATA=perf.data | ||
6 | if [ $# -ne 0 ] ; then | ||
7 | PERF_DATA=$1 | ||
8 | fi | ||
9 | |||
10 | DEBUGDIR=~/.debug/ | ||
11 | BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX) | ||
12 | |||
13 | perf buildid-list -i $PERF_DATA --with-hits > $BUILDIDS | ||
14 | if [ ! -s $BUILDIDS ] ; then | ||
15 | echo "perf archive: no build-ids found" | ||
16 | rm -f $BUILDIDS | ||
17 | exit 1 | ||
18 | fi | ||
19 | |||
20 | MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX) | ||
21 | |||
22 | cut -d ' ' -f 1 $BUILDIDS | \ | ||
23 | while read build_id ; do | ||
24 | linkname=$DEBUGDIR.build-id/${build_id:0:2}/${build_id:2} | ||
25 | filename=$(readlink -f $linkname) | ||
26 | echo ${linkname#$DEBUGDIR} >> $MANIFEST | ||
27 | echo ${filename#$DEBUGDIR} >> $MANIFEST | ||
28 | done | ||
29 | |||
30 | tar cfj $PERF_DATA.tar.bz2 -C $DEBUGDIR -T $MANIFEST | ||
31 | rm -f $MANIFEST $BUILDIDS | ||
32 | exit 0 | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 873e55fab375..05c861c045d5 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -285,6 +285,7 @@ static void handle_internal_command(int argc, const char **argv) | |||
285 | { | 285 | { |
286 | const char *cmd = argv[0]; | 286 | const char *cmd = argv[0]; |
287 | static struct cmd_struct commands[] = { | 287 | static struct cmd_struct commands[] = { |
288 | { "buildid-cache", cmd_buildid_cache, 0 }, | ||
288 | { "buildid-list", cmd_buildid_list, 0 }, | 289 | { "buildid-list", cmd_buildid_list, 0 }, |
289 | { "diff", cmd_diff, 0 }, | 290 | { "diff", cmd_diff, 0 }, |
290 | { "help", cmd_help, 0 }, | 291 | { "help", cmd_help, 0 }, |
@@ -388,7 +389,7 @@ static int run_argv(int *argcp, const char ***argv) | |||
388 | /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ | 389 | /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ |
389 | static void get_debugfs_mntpt(void) | 390 | static void get_debugfs_mntpt(void) |
390 | { | 391 | { |
391 | const char *path = debugfs_find_mountpoint(); | 392 | const char *path = debugfs_mount(NULL); |
392 | 393 | ||
393 | if (path) | 394 | if (path) |
394 | strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt)); | 395 | strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt)); |
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c deleted file mode 100644 index b557b836de3d..000000000000 --- a/tools/perf/util/data_map.c +++ /dev/null | |||
@@ -1,252 +0,0 @@ | |||
1 | #include "symbol.h" | ||
2 | #include "util.h" | ||
3 | #include "debug.h" | ||
4 | #include "thread.h" | ||
5 | #include "session.h" | ||
6 | |||
7 | static int process_event_stub(event_t *event __used, | ||
8 | struct perf_session *session __used) | ||
9 | { | ||
10 | dump_printf(": unhandled!\n"); | ||
11 | return 0; | ||
12 | } | ||
13 | |||
14 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | ||
15 | { | ||
16 | if (!handler->process_sample_event) | ||
17 | handler->process_sample_event = process_event_stub; | ||
18 | if (!handler->process_mmap_event) | ||
19 | handler->process_mmap_event = process_event_stub; | ||
20 | if (!handler->process_comm_event) | ||
21 | handler->process_comm_event = process_event_stub; | ||
22 | if (!handler->process_fork_event) | ||
23 | handler->process_fork_event = process_event_stub; | ||
24 | if (!handler->process_exit_event) | ||
25 | handler->process_exit_event = process_event_stub; | ||
26 | if (!handler->process_lost_event) | ||
27 | handler->process_lost_event = process_event_stub; | ||
28 | if (!handler->process_read_event) | ||
29 | handler->process_read_event = process_event_stub; | ||
30 | if (!handler->process_throttle_event) | ||
31 | handler->process_throttle_event = process_event_stub; | ||
32 | if (!handler->process_unthrottle_event) | ||
33 | handler->process_unthrottle_event = process_event_stub; | ||
34 | } | ||
35 | |||
36 | static const char *event__name[] = { | ||
37 | [0] = "TOTAL", | ||
38 | [PERF_RECORD_MMAP] = "MMAP", | ||
39 | [PERF_RECORD_LOST] = "LOST", | ||
40 | [PERF_RECORD_COMM] = "COMM", | ||
41 | [PERF_RECORD_EXIT] = "EXIT", | ||
42 | [PERF_RECORD_THROTTLE] = "THROTTLE", | ||
43 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", | ||
44 | [PERF_RECORD_FORK] = "FORK", | ||
45 | [PERF_RECORD_READ] = "READ", | ||
46 | [PERF_RECORD_SAMPLE] = "SAMPLE", | ||
47 | }; | ||
48 | |||
49 | unsigned long event__total[PERF_RECORD_MAX]; | ||
50 | |||
51 | void event__print_totals(void) | ||
52 | { | ||
53 | int i; | ||
54 | for (i = 0; i < PERF_RECORD_MAX; ++i) | ||
55 | pr_info("%10s events: %10ld\n", | ||
56 | event__name[i], event__total[i]); | ||
57 | } | ||
58 | |||
59 | static int process_event(event_t *event, struct perf_session *session, | ||
60 | struct perf_event_ops *ops, | ||
61 | unsigned long offset, unsigned long head) | ||
62 | { | ||
63 | trace_event(event); | ||
64 | |||
65 | if (event->header.type < PERF_RECORD_MAX) { | ||
66 | dump_printf("%p [%p]: PERF_RECORD_%s", | ||
67 | (void *)(offset + head), | ||
68 | (void *)(long)(event->header.size), | ||
69 | event__name[event->header.type]); | ||
70 | ++event__total[0]; | ||
71 | ++event__total[event->header.type]; | ||
72 | } | ||
73 | |||
74 | switch (event->header.type) { | ||
75 | case PERF_RECORD_SAMPLE: | ||
76 | return ops->process_sample_event(event, session); | ||
77 | case PERF_RECORD_MMAP: | ||
78 | return ops->process_mmap_event(event, session); | ||
79 | case PERF_RECORD_COMM: | ||
80 | return ops->process_comm_event(event, session); | ||
81 | case PERF_RECORD_FORK: | ||
82 | return ops->process_fork_event(event, session); | ||
83 | case PERF_RECORD_EXIT: | ||
84 | return ops->process_exit_event(event, session); | ||
85 | case PERF_RECORD_LOST: | ||
86 | return ops->process_lost_event(event, session); | ||
87 | case PERF_RECORD_READ: | ||
88 | return ops->process_read_event(event, session); | ||
89 | case PERF_RECORD_THROTTLE: | ||
90 | return ops->process_throttle_event(event, session); | ||
91 | case PERF_RECORD_UNTHROTTLE: | ||
92 | return ops->process_unthrottle_event(event, session); | ||
93 | default: | ||
94 | ops->total_unknown++; | ||
95 | return -1; | ||
96 | } | ||
97 | } | ||
98 | |||
99 | int perf_header__read_build_ids(int input, u64 offset, u64 size) | ||
100 | { | ||
101 | struct build_id_event bev; | ||
102 | char filename[PATH_MAX]; | ||
103 | u64 limit = offset + size; | ||
104 | int err = -1; | ||
105 | |||
106 | while (offset < limit) { | ||
107 | struct dso *dso; | ||
108 | ssize_t len; | ||
109 | |||
110 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
111 | goto out; | ||
112 | |||
113 | len = bev.header.size - sizeof(bev); | ||
114 | if (read(input, filename, len) != len) | ||
115 | goto out; | ||
116 | |||
117 | dso = dsos__findnew(filename); | ||
118 | if (dso != NULL) | ||
119 | dso__set_build_id(dso, &bev.build_id); | ||
120 | |||
121 | offset += bev.header.size; | ||
122 | } | ||
123 | err = 0; | ||
124 | out: | ||
125 | return err; | ||
126 | } | ||
127 | |||
128 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | ||
129 | { | ||
130 | struct thread *thread = perf_session__findnew(self, 0); | ||
131 | |||
132 | if (!thread || thread__set_comm(thread, "swapper")) { | ||
133 | pr_err("problem inserting idle task.\n"); | ||
134 | thread = NULL; | ||
135 | } | ||
136 | |||
137 | return thread; | ||
138 | } | ||
139 | |||
140 | int perf_session__process_events(struct perf_session *self, | ||
141 | struct perf_event_ops *ops) | ||
142 | { | ||
143 | int err; | ||
144 | unsigned long head, shift; | ||
145 | unsigned long offset = 0; | ||
146 | size_t page_size; | ||
147 | event_t *event; | ||
148 | uint32_t size; | ||
149 | char *buf; | ||
150 | |||
151 | if (perf_session__register_idle_thread(self) == NULL) | ||
152 | return -ENOMEM; | ||
153 | |||
154 | perf_event_ops__fill_defaults(ops); | ||
155 | |||
156 | page_size = getpagesize(); | ||
157 | |||
158 | head = self->header.data_offset; | ||
159 | self->sample_type = perf_header__sample_type(&self->header); | ||
160 | |||
161 | err = -EINVAL; | ||
162 | if (ops->sample_type_check && ops->sample_type_check(self) < 0) | ||
163 | goto out_err; | ||
164 | |||
165 | if (!ops->full_paths) { | ||
166 | char bf[PATH_MAX]; | ||
167 | |||
168 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
169 | err = -errno; | ||
170 | out_getcwd_err: | ||
171 | pr_err("failed to get the current directory\n"); | ||
172 | goto out_err; | ||
173 | } | ||
174 | self->cwd = strdup(bf); | ||
175 | if (self->cwd == NULL) { | ||
176 | err = -ENOMEM; | ||
177 | goto out_getcwd_err; | ||
178 | } | ||
179 | self->cwdlen = strlen(self->cwd); | ||
180 | } | ||
181 | |||
182 | shift = page_size * (head / page_size); | ||
183 | offset += shift; | ||
184 | head -= shift; | ||
185 | |||
186 | remap: | ||
187 | buf = mmap(NULL, page_size * self->mmap_window, PROT_READ, | ||
188 | MAP_SHARED, self->fd, offset); | ||
189 | if (buf == MAP_FAILED) { | ||
190 | pr_err("failed to mmap file\n"); | ||
191 | err = -errno; | ||
192 | goto out_err; | ||
193 | } | ||
194 | |||
195 | more: | ||
196 | event = (event_t *)(buf + head); | ||
197 | |||
198 | size = event->header.size; | ||
199 | if (!size) | ||
200 | size = 8; | ||
201 | |||
202 | if (head + event->header.size >= page_size * self->mmap_window) { | ||
203 | int munmap_ret; | ||
204 | |||
205 | shift = page_size * (head / page_size); | ||
206 | |||
207 | munmap_ret = munmap(buf, page_size * self->mmap_window); | ||
208 | assert(munmap_ret == 0); | ||
209 | |||
210 | offset += shift; | ||
211 | head -= shift; | ||
212 | goto remap; | ||
213 | } | ||
214 | |||
215 | size = event->header.size; | ||
216 | |||
217 | dump_printf("\n%p [%p]: event: %d\n", | ||
218 | (void *)(offset + head), | ||
219 | (void *)(long)event->header.size, | ||
220 | event->header.type); | ||
221 | |||
222 | if (!size || process_event(event, self, ops, offset, head) < 0) { | ||
223 | |||
224 | dump_printf("%p [%p]: skipping unknown header type: %d\n", | ||
225 | (void *)(offset + head), | ||
226 | (void *)(long)(event->header.size), | ||
227 | event->header.type); | ||
228 | |||
229 | /* | ||
230 | * assume we lost track of the stream, check alignment, and | ||
231 | * increment a single u64 in the hope to catch on again 'soon'. | ||
232 | */ | ||
233 | |||
234 | if (unlikely(head & 7)) | ||
235 | head &= ~7ULL; | ||
236 | |||
237 | size = 8; | ||
238 | } | ||
239 | |||
240 | head += size; | ||
241 | |||
242 | if (offset + head >= self->header.data_offset + self->header.data_size) | ||
243 | goto done; | ||
244 | |||
245 | if (offset + head < self->size) | ||
246 | goto more; | ||
247 | |||
248 | done: | ||
249 | err = 0; | ||
250 | out_err: | ||
251 | return err; | ||
252 | } | ||
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 28d520d5a1fb..0905600c3851 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "color.h" | 9 | #include "color.h" |
10 | #include "event.h" | 10 | #include "event.h" |
11 | #include "debug.h" | 11 | #include "debug.h" |
12 | #include "util.h" | ||
12 | 13 | ||
13 | int verbose = 0; | 14 | int verbose = 0; |
14 | int dump_trace = 0; | 15 | int dump_trace = 0; |
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c index 06b73ee02c49..a88fefc0cc0a 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/perf/util/debugfs.c | |||
@@ -106,16 +106,14 @@ int debugfs_valid_entry(const char *path) | |||
106 | return 0; | 106 | return 0; |
107 | } | 107 | } |
108 | 108 | ||
109 | /* mount the debugfs somewhere */ | 109 | /* mount the debugfs somewhere if it's not mounted */ |
110 | 110 | ||
111 | int debugfs_mount(const char *mountpoint) | 111 | char *debugfs_mount(const char *mountpoint) |
112 | { | 112 | { |
113 | char mountcmd[128]; | ||
114 | |||
115 | /* see if it's already mounted */ | 113 | /* see if it's already mounted */ |
116 | if (debugfs_find_mountpoint()) { | 114 | if (debugfs_find_mountpoint()) { |
117 | debugfs_premounted = 1; | 115 | debugfs_premounted = 1; |
118 | return 0; | 116 | return debugfs_mountpoint; |
119 | } | 117 | } |
120 | 118 | ||
121 | /* if not mounted and no argument */ | 119 | /* if not mounted and no argument */ |
@@ -127,13 +125,14 @@ int debugfs_mount(const char *mountpoint) | |||
127 | mountpoint = "/sys/kernel/debug"; | 125 | mountpoint = "/sys/kernel/debug"; |
128 | } | 126 | } |
129 | 127 | ||
128 | if (mount(NULL, mountpoint, "debugfs", 0, NULL) < 0) | ||
129 | return NULL; | ||
130 | |||
130 | /* save the mountpoint */ | 131 | /* save the mountpoint */ |
131 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); | 132 | strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint)); |
133 | debugfs_found = 1; | ||
132 | 134 | ||
133 | /* mount it */ | 135 | return debugfs_mountpoint; |
134 | snprintf(mountcmd, sizeof(mountcmd), | ||
135 | "/bin/mount -t debugfs debugfs %s", mountpoint); | ||
136 | return system(mountcmd); | ||
137 | } | 136 | } |
138 | 137 | ||
139 | /* umount the debugfs */ | 138 | /* umount the debugfs */ |
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h index 3cd14f9ae784..83a02879745f 100644 --- a/tools/perf/util/debugfs.h +++ b/tools/perf/util/debugfs.h | |||
@@ -15,7 +15,7 @@ | |||
15 | extern const char *debugfs_find_mountpoint(void); | 15 | extern const char *debugfs_find_mountpoint(void); |
16 | extern int debugfs_valid_mountpoint(const char *debugfs); | 16 | extern int debugfs_valid_mountpoint(const char *debugfs); |
17 | extern int debugfs_valid_entry(const char *path); | 17 | extern int debugfs_valid_entry(const char *path); |
18 | extern int debugfs_mount(const char *mountpoint); | 18 | extern char *debugfs_mount(const char *mountpoint); |
19 | extern int debugfs_umount(void); | 19 | extern int debugfs_umount(void); |
20 | extern int debugfs_write(const char *entry, const char *value); | 20 | extern int debugfs_write(const char *entry, const char *value); |
21 | extern int debugfs_read(const char *entry, char *buffer, size_t size); | 21 | extern int debugfs_read(const char *entry, char *buffer, size_t size); |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index bb0fd6da2d56..bbaee61c1683 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -8,8 +8,7 @@ | |||
8 | #include "thread.h" | 8 | #include "thread.h" |
9 | 9 | ||
10 | static pid_t event__synthesize_comm(pid_t pid, int full, | 10 | static pid_t event__synthesize_comm(pid_t pid, int full, |
11 | int (*process)(event_t *event, | 11 | event__handler_t process, |
12 | struct perf_session *session), | ||
13 | struct perf_session *session) | 12 | struct perf_session *session) |
14 | { | 13 | { |
15 | event_t ev; | 14 | event_t ev; |
@@ -91,8 +90,7 @@ out_failure: | |||
91 | } | 90 | } |
92 | 91 | ||
93 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | 92 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, |
94 | int (*process)(event_t *event, | 93 | event__handler_t process, |
95 | struct perf_session *session), | ||
96 | struct perf_session *session) | 94 | struct perf_session *session) |
97 | { | 95 | { |
98 | char filename[PATH_MAX]; | 96 | char filename[PATH_MAX]; |
@@ -112,7 +110,10 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
112 | while (1) { | 110 | while (1) { |
113 | char bf[BUFSIZ], *pbf = bf; | 111 | char bf[BUFSIZ], *pbf = bf; |
114 | event_t ev = { | 112 | event_t ev = { |
115 | .header = { .type = PERF_RECORD_MMAP }, | 113 | .header = { |
114 | .type = PERF_RECORD_MMAP, | ||
115 | .misc = 0, /* Just like the kernel, see kernel/perf_event.c __perf_event_mmap */ | ||
116 | }, | ||
116 | }; | 117 | }; |
117 | int n; | 118 | int n; |
118 | size_t size; | 119 | size_t size; |
@@ -156,9 +157,38 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
156 | return 0; | 157 | return 0; |
157 | } | 158 | } |
158 | 159 | ||
159 | int event__synthesize_thread(pid_t pid, | 160 | int event__synthesize_modules(event__handler_t process, |
160 | int (*process)(event_t *event, | 161 | struct perf_session *session) |
161 | struct perf_session *session), | 162 | { |
163 | struct rb_node *nd; | ||
164 | |||
165 | for (nd = rb_first(&session->kmaps.maps[MAP__FUNCTION]); | ||
166 | nd; nd = rb_next(nd)) { | ||
167 | event_t ev; | ||
168 | size_t size; | ||
169 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
170 | |||
171 | if (pos->dso->kernel) | ||
172 | continue; | ||
173 | |||
174 | size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64)); | ||
175 | memset(&ev, 0, sizeof(ev)); | ||
176 | ev.mmap.header.misc = 1; /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
177 | ev.mmap.header.type = PERF_RECORD_MMAP; | ||
178 | ev.mmap.header.size = (sizeof(ev.mmap) - | ||
179 | (sizeof(ev.mmap.filename) - size)); | ||
180 | ev.mmap.start = pos->start; | ||
181 | ev.mmap.len = pos->end - pos->start; | ||
182 | |||
183 | memcpy(ev.mmap.filename, pos->dso->long_name, | ||
184 | pos->dso->long_name_len + 1); | ||
185 | process(&ev, session); | ||
186 | } | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | int event__synthesize_thread(pid_t pid, event__handler_t process, | ||
162 | struct perf_session *session) | 192 | struct perf_session *session) |
163 | { | 193 | { |
164 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); | 194 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); |
@@ -167,8 +197,7 @@ int event__synthesize_thread(pid_t pid, | |||
167 | return event__synthesize_mmap_events(pid, tgid, process, session); | 197 | return event__synthesize_mmap_events(pid, tgid, process, session); |
168 | } | 198 | } |
169 | 199 | ||
170 | void event__synthesize_threads(int (*process)(event_t *event, | 200 | void event__synthesize_threads(event__handler_t process, |
171 | struct perf_session *session), | ||
172 | struct perf_session *session) | 201 | struct perf_session *session) |
173 | { | 202 | { |
174 | DIR *proc; | 203 | DIR *proc; |
@@ -189,6 +218,59 @@ void event__synthesize_threads(int (*process)(event_t *event, | |||
189 | closedir(proc); | 218 | closedir(proc); |
190 | } | 219 | } |
191 | 220 | ||
221 | struct process_symbol_args { | ||
222 | const char *name; | ||
223 | u64 start; | ||
224 | }; | ||
225 | |||
226 | static int find_symbol_cb(void *arg, const char *name, char type, u64 start) | ||
227 | { | ||
228 | struct process_symbol_args *args = arg; | ||
229 | |||
230 | /* | ||
231 | * Must be a function or at least an alias, as in PARISC64, where "_text" is | ||
232 | * an 'A' to the same address as "_stext". | ||
233 | */ | ||
234 | if (!(symbol_type__is_a(type, MAP__FUNCTION) || | ||
235 | type == 'A') || strcmp(name, args->name)) | ||
236 | return 0; | ||
237 | |||
238 | args->start = start; | ||
239 | return 1; | ||
240 | } | ||
241 | |||
242 | int event__synthesize_kernel_mmap(event__handler_t process, | ||
243 | struct perf_session *session, | ||
244 | const char *symbol_name) | ||
245 | { | ||
246 | size_t size; | ||
247 | event_t ev = { | ||
248 | .header = { | ||
249 | .type = PERF_RECORD_MMAP, | ||
250 | .misc = 1, /* kernel uses 0 for user space maps, see kernel/perf_event.c __perf_event_mmap */ | ||
251 | }, | ||
252 | }; | ||
253 | /* | ||
254 | * We should get this from /sys/kernel/sections/.text, but till that is | ||
255 | * available use this, and after it is use this as a fallback for older | ||
256 | * kernels. | ||
257 | */ | ||
258 | struct process_symbol_args args = { .name = symbol_name, }; | ||
259 | |||
260 | if (kallsyms__parse("/proc/kallsyms", &args, find_symbol_cb) <= 0) | ||
261 | return -ENOENT; | ||
262 | |||
263 | size = snprintf(ev.mmap.filename, sizeof(ev.mmap.filename), | ||
264 | "[kernel.kallsyms.%s]", symbol_name) + 1; | ||
265 | size = ALIGN(size, sizeof(u64)); | ||
266 | ev.mmap.header.size = (sizeof(ev.mmap) - (sizeof(ev.mmap.filename) - size)); | ||
267 | ev.mmap.pgoff = args.start; | ||
268 | ev.mmap.start = session->vmlinux_maps[MAP__FUNCTION]->start; | ||
269 | ev.mmap.len = session->vmlinux_maps[MAP__FUNCTION]->end - ev.mmap.start ; | ||
270 | |||
271 | return process(&ev, session); | ||
272 | } | ||
273 | |||
192 | static void thread__comm_adjust(struct thread *self) | 274 | static void thread__comm_adjust(struct thread *self) |
193 | { | 275 | { |
194 | char *comm = self->comm; | 276 | char *comm = self->comm; |
@@ -240,22 +322,84 @@ int event__process_lost(event_t *self, struct perf_session *session) | |||
240 | 322 | ||
241 | int event__process_mmap(event_t *self, struct perf_session *session) | 323 | int event__process_mmap(event_t *self, struct perf_session *session) |
242 | { | 324 | { |
243 | struct thread *thread = perf_session__findnew(session, self->mmap.pid); | 325 | struct thread *thread; |
244 | struct map *map = map__new(&self->mmap, MAP__FUNCTION, | 326 | struct map *map; |
245 | session->cwd, session->cwdlen); | 327 | |
328 | dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", | ||
329 | self->mmap.pid, self->mmap.tid, self->mmap.start, | ||
330 | self->mmap.len, self->mmap.pgoff, self->mmap.filename); | ||
331 | |||
332 | if (self->mmap.pid == 0) { | ||
333 | static const char kmmap_prefix[] = "[kernel.kallsyms."; | ||
334 | |||
335 | if (self->mmap.filename[0] == '/') { | ||
336 | char short_module_name[1024]; | ||
337 | char *name = strrchr(self->mmap.filename, '/'), *dot; | ||
338 | |||
339 | if (name == NULL) | ||
340 | goto out_problem; | ||
341 | |||
342 | ++name; /* skip / */ | ||
343 | dot = strrchr(name, '.'); | ||
344 | if (dot == NULL) | ||
345 | goto out_problem; | ||
346 | |||
347 | snprintf(short_module_name, sizeof(short_module_name), | ||
348 | "[%.*s]", (int)(dot - name), name); | ||
349 | strxfrchar(short_module_name, '-', '_'); | ||
350 | |||
351 | map = perf_session__new_module_map(session, | ||
352 | self->mmap.start, | ||
353 | self->mmap.filename); | ||
354 | if (map == NULL) | ||
355 | goto out_problem; | ||
356 | |||
357 | name = strdup(short_module_name); | ||
358 | if (name == NULL) | ||
359 | goto out_problem; | ||
360 | |||
361 | map->dso->short_name = name; | ||
362 | map->end = map->start + self->mmap.len; | ||
363 | } else if (memcmp(self->mmap.filename, kmmap_prefix, | ||
364 | sizeof(kmmap_prefix) - 1) == 0) { | ||
365 | const char *symbol_name = (self->mmap.filename + | ||
366 | sizeof(kmmap_prefix) - 1); | ||
367 | /* | ||
368 | * Should be there already, from the build-id table in | ||
369 | * the header. | ||
370 | */ | ||
371 | struct dso *kernel = __dsos__findnew(&dsos__kernel, | ||
372 | "[kernel.kallsyms]"); | ||
373 | if (kernel == NULL) | ||
374 | goto out_problem; | ||
375 | |||
376 | kernel->kernel = 1; | ||
377 | if (__map_groups__create_kernel_maps(&session->kmaps, | ||
378 | session->vmlinux_maps, | ||
379 | kernel) < 0) | ||
380 | goto out_problem; | ||
381 | |||
382 | session->vmlinux_maps[MAP__FUNCTION]->start = self->mmap.start; | ||
383 | session->vmlinux_maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; | ||
384 | |||
385 | perf_session__set_kallsyms_ref_reloc_sym(session, symbol_name, | ||
386 | self->mmap.pgoff); | ||
387 | } | ||
388 | return 0; | ||
389 | } | ||
246 | 390 | ||
247 | dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", | 391 | thread = perf_session__findnew(session, self->mmap.pid); |
248 | self->mmap.pid, self->mmap.tid, | 392 | map = map__new(&self->mmap, MAP__FUNCTION, |
249 | (void *)(long)self->mmap.start, | 393 | session->cwd, session->cwdlen); |
250 | (void *)(long)self->mmap.len, | ||
251 | (void *)(long)self->mmap.pgoff, | ||
252 | self->mmap.filename); | ||
253 | 394 | ||
254 | if (thread == NULL || map == NULL) | 395 | if (thread == NULL || map == NULL) |
255 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | 396 | goto out_problem; |
256 | else | ||
257 | thread__insert_map(thread, map); | ||
258 | 397 | ||
398 | thread__insert_map(thread, map); | ||
399 | return 0; | ||
400 | |||
401 | out_problem: | ||
402 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
259 | return 0; | 403 | return 0; |
260 | } | 404 | } |
261 | 405 | ||
@@ -284,11 +428,10 @@ int event__process_task(event_t *self, struct perf_session *session) | |||
284 | return 0; | 428 | return 0; |
285 | } | 429 | } |
286 | 430 | ||
287 | void thread__find_addr_location(struct thread *self, | 431 | void thread__find_addr_map(struct thread *self, |
288 | struct perf_session *session, u8 cpumode, | 432 | struct perf_session *session, u8 cpumode, |
289 | enum map_type type, u64 addr, | 433 | enum map_type type, u64 addr, |
290 | struct addr_location *al, | 434 | struct addr_location *al) |
291 | symbol_filter_t filter) | ||
292 | { | 435 | { |
293 | struct map_groups *mg = &self->mg; | 436 | struct map_groups *mg = &self->mg; |
294 | 437 | ||
@@ -303,7 +446,6 @@ void thread__find_addr_location(struct thread *self, | |||
303 | else { | 446 | else { |
304 | al->level = 'H'; | 447 | al->level = 'H'; |
305 | al->map = NULL; | 448 | al->map = NULL; |
306 | al->sym = NULL; | ||
307 | return; | 449 | return; |
308 | } | 450 | } |
309 | try_again: | 451 | try_again: |
@@ -322,11 +464,21 @@ try_again: | |||
322 | mg = &session->kmaps; | 464 | mg = &session->kmaps; |
323 | goto try_again; | 465 | goto try_again; |
324 | } | 466 | } |
325 | al->sym = NULL; | 467 | } else |
326 | } else { | ||
327 | al->addr = al->map->map_ip(al->map, al->addr); | 468 | al->addr = al->map->map_ip(al->map, al->addr); |
469 | } | ||
470 | |||
471 | void thread__find_addr_location(struct thread *self, | ||
472 | struct perf_session *session, u8 cpumode, | ||
473 | enum map_type type, u64 addr, | ||
474 | struct addr_location *al, | ||
475 | symbol_filter_t filter) | ||
476 | { | ||
477 | thread__find_addr_map(self, session, cpumode, type, addr, al); | ||
478 | if (al->map != NULL) | ||
328 | al->sym = map__find_symbol(al->map, session, al->addr, filter); | 479 | al->sym = map__find_symbol(al->map, session, al->addr, filter); |
329 | } | 480 | else |
481 | al->sym = NULL; | ||
330 | } | 482 | } |
331 | 483 | ||
332 | static void dso__calc_col_width(struct dso *self) | 484 | static void dso__calc_col_width(struct dso *self) |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 690a96d0467c..50a7132887f5 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -1,10 +1,10 @@ | |||
1 | #ifndef __PERF_RECORD_H | 1 | #ifndef __PERF_RECORD_H |
2 | #define __PERF_RECORD_H | 2 | #define __PERF_RECORD_H |
3 | 3 | ||
4 | #include <limits.h> | ||
5 | |||
4 | #include "../perf.h" | 6 | #include "../perf.h" |
5 | #include "util.h" | 7 | #include "map.h" |
6 | #include <linux/list.h> | ||
7 | #include <linux/rbtree.h> | ||
8 | 8 | ||
9 | /* | 9 | /* |
10 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | 10 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * |
@@ -101,74 +101,19 @@ struct events_stats { | |||
101 | 101 | ||
102 | void event__print_totals(void); | 102 | void event__print_totals(void); |
103 | 103 | ||
104 | enum map_type { | ||
105 | MAP__FUNCTION = 0, | ||
106 | MAP__VARIABLE, | ||
107 | }; | ||
108 | |||
109 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | ||
110 | |||
111 | struct map { | ||
112 | union { | ||
113 | struct rb_node rb_node; | ||
114 | struct list_head node; | ||
115 | }; | ||
116 | u64 start; | ||
117 | u64 end; | ||
118 | enum map_type type; | ||
119 | u64 pgoff; | ||
120 | u64 (*map_ip)(struct map *, u64); | ||
121 | u64 (*unmap_ip)(struct map *, u64); | ||
122 | struct dso *dso; | ||
123 | }; | ||
124 | |||
125 | static inline u64 map__map_ip(struct map *map, u64 ip) | ||
126 | { | ||
127 | return ip - map->start + map->pgoff; | ||
128 | } | ||
129 | |||
130 | static inline u64 map__unmap_ip(struct map *map, u64 ip) | ||
131 | { | ||
132 | return ip + map->start - map->pgoff; | ||
133 | } | ||
134 | |||
135 | static inline u64 identity__map_ip(struct map *map __used, u64 ip) | ||
136 | { | ||
137 | return ip; | ||
138 | } | ||
139 | |||
140 | struct symbol; | ||
141 | |||
142 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | ||
143 | |||
144 | void map__init(struct map *self, enum map_type type, | ||
145 | u64 start, u64 end, u64 pgoff, struct dso *dso); | ||
146 | struct map *map__new(struct mmap_event *event, enum map_type, | ||
147 | char *cwd, int cwdlen); | ||
148 | void map__delete(struct map *self); | ||
149 | struct map *map__clone(struct map *self); | ||
150 | int map__overlap(struct map *l, struct map *r); | ||
151 | size_t map__fprintf(struct map *self, FILE *fp); | ||
152 | |||
153 | struct perf_session; | 104 | struct perf_session; |
154 | 105 | ||
155 | int map__load(struct map *self, struct perf_session *session, | 106 | typedef int (*event__handler_t)(event_t *event, struct perf_session *session); |
156 | symbol_filter_t filter); | 107 | |
157 | struct symbol *map__find_symbol(struct map *self, struct perf_session *session, | 108 | int event__synthesize_thread(pid_t pid, event__handler_t process, |
158 | u64 addr, symbol_filter_t filter); | ||
159 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
160 | struct perf_session *session, | ||
161 | symbol_filter_t filter); | ||
162 | void map__fixup_start(struct map *self); | ||
163 | void map__fixup_end(struct map *self); | ||
164 | |||
165 | int event__synthesize_thread(pid_t pid, | ||
166 | int (*process)(event_t *event, | ||
167 | struct perf_session *session), | ||
168 | struct perf_session *session); | 109 | struct perf_session *session); |
169 | void event__synthesize_threads(int (*process)(event_t *event, | 110 | void event__synthesize_threads(event__handler_t process, |
170 | struct perf_session *session), | ||
171 | struct perf_session *session); | 111 | struct perf_session *session); |
112 | int event__synthesize_kernel_mmap(event__handler_t process, | ||
113 | struct perf_session *session, | ||
114 | const char *symbol_name); | ||
115 | int event__synthesize_modules(event__handler_t process, | ||
116 | struct perf_session *session); | ||
172 | 117 | ||
173 | int event__process_comm(event_t *self, struct perf_session *session); | 118 | int event__process_comm(event_t *self, struct perf_session *session); |
174 | int event__process_lost(event_t *self, struct perf_session *session); | 119 | int event__process_lost(event_t *self, struct perf_session *session); |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 8a0bca55106f..2bb2bdb1f456 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1,8 +1,10 @@ | |||
1 | #include <sys/types.h> | 1 | #include <sys/types.h> |
2 | #include <byteswap.h> | ||
2 | #include <unistd.h> | 3 | #include <unistd.h> |
3 | #include <stdio.h> | 4 | #include <stdio.h> |
4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
5 | #include <linux/list.h> | 6 | #include <linux/list.h> |
7 | #include <linux/kernel.h> | ||
6 | 8 | ||
7 | #include "util.h" | 9 | #include "util.h" |
8 | #include "header.h" | 10 | #include "header.h" |
@@ -105,24 +107,28 @@ struct perf_trace_event_type { | |||
105 | static int event_count; | 107 | static int event_count; |
106 | static struct perf_trace_event_type *events; | 108 | static struct perf_trace_event_type *events; |
107 | 109 | ||
108 | void perf_header__push_event(u64 id, const char *name) | 110 | int perf_header__push_event(u64 id, const char *name) |
109 | { | 111 | { |
110 | if (strlen(name) > MAX_EVENT_NAME) | 112 | if (strlen(name) > MAX_EVENT_NAME) |
111 | pr_warning("Event %s will be truncated\n", name); | 113 | pr_warning("Event %s will be truncated\n", name); |
112 | 114 | ||
113 | if (!events) { | 115 | if (!events) { |
114 | events = malloc(sizeof(struct perf_trace_event_type)); | 116 | events = malloc(sizeof(struct perf_trace_event_type)); |
115 | if (!events) | 117 | if (events == NULL) |
116 | die("nomem"); | 118 | return -ENOMEM; |
117 | } else { | 119 | } else { |
118 | events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type)); | 120 | struct perf_trace_event_type *nevents; |
119 | if (!events) | 121 | |
120 | die("nomem"); | 122 | nevents = realloc(events, (event_count + 1) * sizeof(*events)); |
123 | if (nevents == NULL) | ||
124 | return -ENOMEM; | ||
125 | events = nevents; | ||
121 | } | 126 | } |
122 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); | 127 | memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); |
123 | events[event_count].event_id = id; | 128 | events[event_count].event_id = id; |
124 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); | 129 | strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); |
125 | event_count++; | 130 | event_count++; |
131 | return 0; | ||
126 | } | 132 | } |
127 | 133 | ||
128 | char *perf_header__find_event(u64 id) | 134 | char *perf_header__find_event(u64 id) |
@@ -169,31 +175,45 @@ static int do_write(int fd, const void *buf, size_t size) | |||
169 | return 0; | 175 | return 0; |
170 | } | 176 | } |
171 | 177 | ||
172 | static int __dsos__write_buildid_table(struct list_head *head, int fd) | 178 | #define NAME_ALIGN 64 |
179 | |||
180 | static int write_padded(int fd, const void *bf, size_t count, | ||
181 | size_t count_aligned) | ||
173 | { | 182 | { |
174 | #define NAME_ALIGN 64 | ||
175 | struct dso *pos; | ||
176 | static const char zero_buf[NAME_ALIGN]; | 183 | static const char zero_buf[NAME_ALIGN]; |
184 | int err = do_write(fd, bf, count); | ||
185 | |||
186 | if (!err) | ||
187 | err = do_write(fd, zero_buf, count_aligned - count); | ||
188 | |||
189 | return err; | ||
190 | } | ||
177 | 191 | ||
178 | list_for_each_entry(pos, head, node) { | 192 | #define dsos__for_each_with_build_id(pos, head) \ |
193 | list_for_each_entry(pos, head, node) \ | ||
194 | if (!pos->has_build_id) \ | ||
195 | continue; \ | ||
196 | else | ||
197 | |||
198 | static int __dsos__write_buildid_table(struct list_head *head, u16 misc, int fd) | ||
199 | { | ||
200 | struct dso *pos; | ||
201 | |||
202 | dsos__for_each_with_build_id(pos, head) { | ||
179 | int err; | 203 | int err; |
180 | struct build_id_event b; | 204 | struct build_id_event b; |
181 | size_t len; | 205 | size_t len = pos->long_name_len + 1; |
182 | 206 | ||
183 | if (!pos->has_build_id) | ||
184 | continue; | ||
185 | len = pos->long_name_len + 1; | ||
186 | len = ALIGN(len, NAME_ALIGN); | 207 | len = ALIGN(len, NAME_ALIGN); |
187 | memset(&b, 0, sizeof(b)); | 208 | memset(&b, 0, sizeof(b)); |
188 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); | 209 | memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id)); |
210 | b.header.misc = misc; | ||
189 | b.header.size = sizeof(b) + len; | 211 | b.header.size = sizeof(b) + len; |
190 | err = do_write(fd, &b, sizeof(b)); | 212 | err = do_write(fd, &b, sizeof(b)); |
191 | if (err < 0) | 213 | if (err < 0) |
192 | return err; | 214 | return err; |
193 | err = do_write(fd, pos->long_name, pos->long_name_len + 1); | 215 | err = write_padded(fd, pos->long_name, |
194 | if (err < 0) | 216 | pos->long_name_len + 1, len); |
195 | return err; | ||
196 | err = do_write(fd, zero_buf, len - pos->long_name_len - 1); | ||
197 | if (err < 0) | 217 | if (err < 0) |
198 | return err; | 218 | return err; |
199 | } | 219 | } |
@@ -203,12 +223,143 @@ static int __dsos__write_buildid_table(struct list_head *head, int fd) | |||
203 | 223 | ||
204 | static int dsos__write_buildid_table(int fd) | 224 | static int dsos__write_buildid_table(int fd) |
205 | { | 225 | { |
206 | int err = __dsos__write_buildid_table(&dsos__kernel, fd); | 226 | int err = __dsos__write_buildid_table(&dsos__kernel, |
227 | PERF_RECORD_MISC_KERNEL, fd); | ||
207 | if (err == 0) | 228 | if (err == 0) |
208 | err = __dsos__write_buildid_table(&dsos__user, fd); | 229 | err = __dsos__write_buildid_table(&dsos__user, |
230 | PERF_RECORD_MISC_USER, fd); | ||
231 | return err; | ||
232 | } | ||
233 | |||
234 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
235 | const char *name, bool is_kallsyms) | ||
236 | { | ||
237 | const size_t size = PATH_MAX; | ||
238 | char *filename = malloc(size), | ||
239 | *linkname = malloc(size), *targetname; | ||
240 | int len, err = -1; | ||
241 | |||
242 | if (filename == NULL || linkname == NULL) | ||
243 | goto out_free; | ||
244 | |||
245 | len = snprintf(filename, size, "%s%s%s", | ||
246 | debugdir, is_kallsyms ? "/" : "", name); | ||
247 | if (mkdir_p(filename, 0755)) | ||
248 | goto out_free; | ||
249 | |||
250 | snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); | ||
251 | |||
252 | if (access(filename, F_OK)) { | ||
253 | if (is_kallsyms) { | ||
254 | if (copyfile("/proc/kallsyms", filename)) | ||
255 | goto out_free; | ||
256 | } else if (link(name, filename) && copyfile(name, filename)) | ||
257 | goto out_free; | ||
258 | } | ||
259 | |||
260 | len = snprintf(linkname, size, "%s/.build-id/%.2s", | ||
261 | debugdir, sbuild_id); | ||
262 | |||
263 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | ||
264 | goto out_free; | ||
265 | |||
266 | snprintf(linkname + len, size - len, "/%s", sbuild_id + 2); | ||
267 | targetname = filename + strlen(debugdir) - 5; | ||
268 | memcpy(targetname, "../..", 5); | ||
269 | |||
270 | if (symlink(targetname, linkname) == 0) | ||
271 | err = 0; | ||
272 | out_free: | ||
273 | free(filename); | ||
274 | free(linkname); | ||
275 | return err; | ||
276 | } | ||
277 | |||
278 | static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | ||
279 | const char *name, const char *debugdir, | ||
280 | bool is_kallsyms) | ||
281 | { | ||
282 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
283 | |||
284 | build_id__sprintf(build_id, build_id_size, sbuild_id); | ||
285 | |||
286 | return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms); | ||
287 | } | ||
288 | |||
289 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | ||
290 | { | ||
291 | const size_t size = PATH_MAX; | ||
292 | char *filename = malloc(size), | ||
293 | *linkname = malloc(size); | ||
294 | int err = -1; | ||
295 | |||
296 | if (filename == NULL || linkname == NULL) | ||
297 | goto out_free; | ||
298 | |||
299 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
300 | debugdir, sbuild_id, sbuild_id + 2); | ||
301 | |||
302 | if (access(linkname, F_OK)) | ||
303 | goto out_free; | ||
304 | |||
305 | if (readlink(linkname, filename, size) < 0) | ||
306 | goto out_free; | ||
307 | |||
308 | if (unlink(linkname)) | ||
309 | goto out_free; | ||
310 | |||
311 | /* | ||
312 | * Since the link is relative, we must make it absolute: | ||
313 | */ | ||
314 | snprintf(linkname, size, "%s/.build-id/%.2s/%s", | ||
315 | debugdir, sbuild_id, filename); | ||
316 | |||
317 | if (unlink(linkname)) | ||
318 | goto out_free; | ||
319 | |||
320 | err = 0; | ||
321 | out_free: | ||
322 | free(filename); | ||
323 | free(linkname); | ||
324 | return err; | ||
325 | } | ||
326 | |||
327 | static int dso__cache_build_id(struct dso *self, const char *debugdir) | ||
328 | { | ||
329 | bool is_kallsyms = self->kernel && self->long_name[0] != '/'; | ||
330 | |||
331 | return build_id_cache__add_b(self->build_id, sizeof(self->build_id), | ||
332 | self->long_name, debugdir, is_kallsyms); | ||
333 | } | ||
334 | |||
335 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | ||
336 | { | ||
337 | struct dso *pos; | ||
338 | int err = 0; | ||
339 | |||
340 | dsos__for_each_with_build_id(pos, head) | ||
341 | if (dso__cache_build_id(pos, debugdir)) | ||
342 | err = -1; | ||
343 | |||
209 | return err; | 344 | return err; |
210 | } | 345 | } |
211 | 346 | ||
347 | static int dsos__cache_build_ids(void) | ||
348 | { | ||
349 | int err_kernel, err_user; | ||
350 | char debugdir[PATH_MAX]; | ||
351 | |||
352 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | ||
353 | DEBUG_CACHE_DIR); | ||
354 | |||
355 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | ||
356 | return -1; | ||
357 | |||
358 | err_kernel = __dsos__cache_build_ids(&dsos__kernel, debugdir); | ||
359 | err_user = __dsos__cache_build_ids(&dsos__user, debugdir); | ||
360 | return err_kernel || err_user ? -1 : 0; | ||
361 | } | ||
362 | |||
212 | static int perf_header__adds_write(struct perf_header *self, int fd) | 363 | static int perf_header__adds_write(struct perf_header *self, int fd) |
213 | { | 364 | { |
214 | int nr_sections; | 365 | int nr_sections; |
@@ -258,6 +409,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
258 | goto out_free; | 409 | goto out_free; |
259 | } | 410 | } |
260 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; | 411 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - buildid_sec->offset; |
412 | dsos__cache_build_ids(); | ||
261 | } | 413 | } |
262 | 414 | ||
263 | lseek(fd, sec_start, SEEK_SET); | 415 | lseek(fd, sec_start, SEEK_SET); |
@@ -360,30 +512,43 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
360 | return 0; | 512 | return 0; |
361 | } | 513 | } |
362 | 514 | ||
363 | static void do_read(int fd, void *buf, size_t size) | 515 | static int do_read(int fd, void *buf, size_t size) |
364 | { | 516 | { |
365 | while (size) { | 517 | while (size) { |
366 | int ret = read(fd, buf, size); | 518 | int ret = read(fd, buf, size); |
367 | 519 | ||
368 | if (ret < 0) | 520 | if (ret <= 0) |
369 | die("failed to read"); | 521 | return -1; |
370 | if (ret == 0) | ||
371 | die("failed to read: missing data"); | ||
372 | 522 | ||
373 | size -= ret; | 523 | size -= ret; |
374 | buf += ret; | 524 | buf += ret; |
375 | } | 525 | } |
526 | |||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | static int perf_header__getbuffer64(struct perf_header *self, | ||
531 | int fd, void *buf, size_t size) | ||
532 | { | ||
533 | if (do_read(fd, buf, size)) | ||
534 | return -1; | ||
535 | |||
536 | if (self->needs_swap) | ||
537 | mem_bswap_64(buf, size); | ||
538 | |||
539 | return 0; | ||
376 | } | 540 | } |
377 | 541 | ||
378 | int perf_header__process_sections(struct perf_header *self, int fd, | 542 | int perf_header__process_sections(struct perf_header *self, int fd, |
379 | int (*process)(struct perf_file_section *self, | 543 | int (*process)(struct perf_file_section *self, |
544 | struct perf_header *ph, | ||
380 | int feat, int fd)) | 545 | int feat, int fd)) |
381 | { | 546 | { |
382 | struct perf_file_section *feat_sec; | 547 | struct perf_file_section *feat_sec; |
383 | int nr_sections; | 548 | int nr_sections; |
384 | int sec_size; | 549 | int sec_size; |
385 | int idx = 0; | 550 | int idx = 0; |
386 | int err = 0, feat = 1; | 551 | int err = -1, feat = 1; |
387 | 552 | ||
388 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 553 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); |
389 | if (!nr_sections) | 554 | if (!nr_sections) |
@@ -397,33 +562,45 @@ int perf_header__process_sections(struct perf_header *self, int fd, | |||
397 | 562 | ||
398 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 563 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); |
399 | 564 | ||
400 | do_read(fd, feat_sec, sec_size); | 565 | if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) |
566 | goto out_free; | ||
401 | 567 | ||
568 | err = 0; | ||
402 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | 569 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { |
403 | if (perf_header__has_feat(self, feat)) { | 570 | if (perf_header__has_feat(self, feat)) { |
404 | struct perf_file_section *sec = &feat_sec[idx++]; | 571 | struct perf_file_section *sec = &feat_sec[idx++]; |
405 | 572 | ||
406 | err = process(sec, feat, fd); | 573 | err = process(sec, self, feat, fd); |
407 | if (err < 0) | 574 | if (err < 0) |
408 | break; | 575 | break; |
409 | } | 576 | } |
410 | ++feat; | 577 | ++feat; |
411 | } | 578 | } |
412 | 579 | out_free: | |
413 | free(feat_sec); | 580 | free(feat_sec); |
414 | return err; | 581 | return err; |
415 | }; | 582 | } |
416 | 583 | ||
417 | int perf_file_header__read(struct perf_file_header *self, | 584 | int perf_file_header__read(struct perf_file_header *self, |
418 | struct perf_header *ph, int fd) | 585 | struct perf_header *ph, int fd) |
419 | { | 586 | { |
420 | lseek(fd, 0, SEEK_SET); | 587 | lseek(fd, 0, SEEK_SET); |
421 | do_read(fd, self, sizeof(*self)); | ||
422 | 588 | ||
423 | if (self->magic != PERF_MAGIC || | 589 | if (do_read(fd, self, sizeof(*self)) || |
424 | self->attr_size != sizeof(struct perf_file_attr)) | 590 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) |
425 | return -1; | 591 | return -1; |
426 | 592 | ||
593 | if (self->attr_size != sizeof(struct perf_file_attr)) { | ||
594 | u64 attr_size = bswap_64(self->attr_size); | ||
595 | |||
596 | if (attr_size != sizeof(struct perf_file_attr)) | ||
597 | return -1; | ||
598 | |||
599 | mem_bswap_64(self, offsetof(struct perf_file_header, | ||
600 | adds_features)); | ||
601 | ph->needs_swap = true; | ||
602 | } | ||
603 | |||
427 | if (self->size != sizeof(*self)) { | 604 | if (self->size != sizeof(*self)) { |
428 | /* Support the previous format */ | 605 | /* Support the previous format */ |
429 | if (self->size == offsetof(typeof(*self), adds_features)) | 606 | if (self->size == offsetof(typeof(*self), adds_features)) |
@@ -433,16 +610,28 @@ int perf_file_header__read(struct perf_file_header *self, | |||
433 | } | 610 | } |
434 | 611 | ||
435 | memcpy(&ph->adds_features, &self->adds_features, | 612 | memcpy(&ph->adds_features, &self->adds_features, |
436 | sizeof(self->adds_features)); | 613 | sizeof(ph->adds_features)); |
614 | /* | ||
615 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
616 | * may be coming from an arch with a different word-size, ergo different | ||
617 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
618 | * safe to assume that we have a build-id section. Trace files probably | ||
619 | * have several other issues in this realm anyway... | ||
620 | */ | ||
621 | if (ph->needs_swap) { | ||
622 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
623 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
624 | } | ||
437 | 625 | ||
438 | ph->event_offset = self->event_types.offset; | 626 | ph->event_offset = self->event_types.offset; |
439 | ph->event_size = self->event_types.size; | 627 | ph->event_size = self->event_types.size; |
440 | ph->data_offset = self->data.offset; | 628 | ph->data_offset = self->data.offset; |
441 | ph->data_size = self->data.size; | 629 | ph->data_size = self->data.size; |
442 | return 0; | 630 | return 0; |
443 | } | 631 | } |
444 | 632 | ||
445 | static int perf_file_section__process(struct perf_file_section *self, | 633 | static int perf_file_section__process(struct perf_file_section *self, |
634 | struct perf_header *ph, | ||
446 | int feat, int fd) | 635 | int feat, int fd) |
447 | { | 636 | { |
448 | if (lseek(fd, self->offset, SEEK_SET) < 0) { | 637 | if (lseek(fd, self->offset, SEEK_SET) < 0) { |
@@ -457,7 +646,7 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
457 | break; | 646 | break; |
458 | 647 | ||
459 | case HEADER_BUILD_ID: | 648 | case HEADER_BUILD_ID: |
460 | if (perf_header__read_build_ids(fd, self->offset, self->size)) | 649 | if (perf_header__read_build_ids(ph, fd, self->offset, self->size)) |
461 | pr_debug("Failed to read buildids, continuing...\n"); | 650 | pr_debug("Failed to read buildids, continuing...\n"); |
462 | break; | 651 | break; |
463 | default: | 652 | default: |
@@ -469,7 +658,7 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
469 | 658 | ||
470 | int perf_header__read(struct perf_header *self, int fd) | 659 | int perf_header__read(struct perf_header *self, int fd) |
471 | { | 660 | { |
472 | struct perf_file_header f_header; | 661 | struct perf_file_header f_header; |
473 | struct perf_file_attr f_attr; | 662 | struct perf_file_attr f_attr; |
474 | u64 f_id; | 663 | u64 f_id; |
475 | int nr_attrs, nr_ids, i, j; | 664 | int nr_attrs, nr_ids, i, j; |
@@ -486,7 +675,9 @@ int perf_header__read(struct perf_header *self, int fd) | |||
486 | struct perf_header_attr *attr; | 675 | struct perf_header_attr *attr; |
487 | off_t tmp; | 676 | off_t tmp; |
488 | 677 | ||
489 | do_read(fd, &f_attr, sizeof(f_attr)); | 678 | if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) |
679 | goto out_errno; | ||
680 | |||
490 | tmp = lseek(fd, 0, SEEK_CUR); | 681 | tmp = lseek(fd, 0, SEEK_CUR); |
491 | 682 | ||
492 | attr = perf_header_attr__new(&f_attr.attr); | 683 | attr = perf_header_attr__new(&f_attr.attr); |
@@ -497,7 +688,8 @@ int perf_header__read(struct perf_header *self, int fd) | |||
497 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 688 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
498 | 689 | ||
499 | for (j = 0; j < nr_ids; j++) { | 690 | for (j = 0; j < nr_ids; j++) { |
500 | do_read(fd, &f_id, sizeof(f_id)); | 691 | if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) |
692 | goto out_errno; | ||
501 | 693 | ||
502 | if (perf_header_attr__add_id(attr, f_id) < 0) { | 694 | if (perf_header_attr__add_id(attr, f_id) < 0) { |
503 | perf_header_attr__delete(attr); | 695 | perf_header_attr__delete(attr); |
@@ -517,7 +709,9 @@ int perf_header__read(struct perf_header *self, int fd) | |||
517 | events = malloc(f_header.event_types.size); | 709 | events = malloc(f_header.event_types.size); |
518 | if (events == NULL) | 710 | if (events == NULL) |
519 | return -ENOMEM; | 711 | return -ENOMEM; |
520 | do_read(fd, events, f_header.event_types.size); | 712 | if (perf_header__getbuffer64(self, fd, events, |
713 | f_header.event_types.size)) | ||
714 | goto out_errno; | ||
521 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 715 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
522 | } | 716 | } |
523 | 717 | ||
@@ -527,6 +721,8 @@ int perf_header__read(struct perf_header *self, int fd) | |||
527 | 721 | ||
528 | self->frozen = 1; | 722 | self->frozen = 1; |
529 | return 0; | 723 | return 0; |
724 | out_errno: | ||
725 | return -errno; | ||
530 | } | 726 | } |
531 | 727 | ||
532 | u64 perf_header__sample_type(struct perf_header *header) | 728 | u64 perf_header__sample_type(struct perf_header *header) |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index d118d05d3abe..82a6af72d4cc 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | #include "types.h" | 7 | #include "types.h" |
8 | #include "event.h" | ||
8 | 9 | ||
9 | #include <linux/bitmap.h> | 10 | #include <linux/bitmap.h> |
10 | 11 | ||
@@ -52,6 +53,7 @@ struct perf_header { | |||
52 | u64 data_size; | 53 | u64 data_size; |
53 | u64 event_offset; | 54 | u64 event_offset; |
54 | u64 event_size; | 55 | u64 event_size; |
56 | bool needs_swap; | ||
55 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 57 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
56 | }; | 58 | }; |
57 | 59 | ||
@@ -64,7 +66,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit); | |||
64 | int perf_header__add_attr(struct perf_header *self, | 66 | int perf_header__add_attr(struct perf_header *self, |
65 | struct perf_header_attr *attr); | 67 | struct perf_header_attr *attr); |
66 | 68 | ||
67 | void perf_header__push_event(u64 id, const char *name); | 69 | int perf_header__push_event(u64 id, const char *name); |
68 | char *perf_header__find_event(u64 id); | 70 | char *perf_header__find_event(u64 id); |
69 | 71 | ||
70 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); | 72 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); |
@@ -80,6 +82,11 @@ bool perf_header__has_feat(const struct perf_header *self, int feat); | |||
80 | 82 | ||
81 | int perf_header__process_sections(struct perf_header *self, int fd, | 83 | int perf_header__process_sections(struct perf_header *self, int fd, |
82 | int (*process)(struct perf_file_section *self, | 84 | int (*process)(struct perf_file_section *self, |
85 | struct perf_header *ph, | ||
83 | int feat, int fd)); | 86 | int feat, int fd)); |
84 | 87 | ||
88 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | ||
89 | const char *name, bool is_kallsyms); | ||
90 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | ||
91 | |||
85 | #endif /* __PERF_HEADER_H */ | 92 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h new file mode 100644 index 000000000000..72f0b6ab5ea5 --- /dev/null +++ b/tools/perf/util/map.h | |||
@@ -0,0 +1,73 @@ | |||
1 | #ifndef __PERF_MAP_H | ||
2 | #define __PERF_MAP_H | ||
3 | |||
4 | #include <linux/compiler.h> | ||
5 | #include <linux/list.h> | ||
6 | #include <linux/rbtree.h> | ||
7 | #include <linux/types.h> | ||
8 | |||
9 | enum map_type { | ||
10 | MAP__FUNCTION = 0, | ||
11 | MAP__VARIABLE, | ||
12 | }; | ||
13 | |||
14 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | ||
15 | |||
16 | struct dso; | ||
17 | |||
18 | struct map { | ||
19 | union { | ||
20 | struct rb_node rb_node; | ||
21 | struct list_head node; | ||
22 | }; | ||
23 | u64 start; | ||
24 | u64 end; | ||
25 | enum map_type type; | ||
26 | u64 pgoff; | ||
27 | u64 (*map_ip)(struct map *, u64); | ||
28 | u64 (*unmap_ip)(struct map *, u64); | ||
29 | struct dso *dso; | ||
30 | }; | ||
31 | |||
32 | static inline u64 map__map_ip(struct map *map, u64 ip) | ||
33 | { | ||
34 | return ip - map->start + map->pgoff; | ||
35 | } | ||
36 | |||
37 | static inline u64 map__unmap_ip(struct map *map, u64 ip) | ||
38 | { | ||
39 | return ip + map->start - map->pgoff; | ||
40 | } | ||
41 | |||
42 | static inline u64 identity__map_ip(struct map *map __used, u64 ip) | ||
43 | { | ||
44 | return ip; | ||
45 | } | ||
46 | |||
47 | struct symbol; | ||
48 | struct mmap_event; | ||
49 | |||
50 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | ||
51 | |||
52 | void map__init(struct map *self, enum map_type type, | ||
53 | u64 start, u64 end, u64 pgoff, struct dso *dso); | ||
54 | struct map *map__new(struct mmap_event *event, enum map_type, | ||
55 | char *cwd, int cwdlen); | ||
56 | void map__delete(struct map *self); | ||
57 | struct map *map__clone(struct map *self); | ||
58 | int map__overlap(struct map *l, struct map *r); | ||
59 | size_t map__fprintf(struct map *self, FILE *fp); | ||
60 | |||
61 | struct perf_session; | ||
62 | |||
63 | int map__load(struct map *self, struct perf_session *session, | ||
64 | symbol_filter_t filter); | ||
65 | struct symbol *map__find_symbol(struct map *self, struct perf_session *session, | ||
66 | u64 addr, symbol_filter_t filter); | ||
67 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | ||
68 | struct perf_session *session, | ||
69 | symbol_filter_t filter); | ||
70 | void map__fixup_start(struct map *self); | ||
71 | void map__fixup_end(struct map *self); | ||
72 | |||
73 | #endif /* __PERF_MAP_H */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index e5bc0fb016b2..05d0c5c2030c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -450,7 +450,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
450 | /* sys + ':' + event + ':' + flags*/ | 450 | /* sys + ':' + event + ':' + flags*/ |
451 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 451 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
452 | static enum event_result | 452 | static enum event_result |
453 | parse_subsystem_tracepoint_event(char *sys_name, char *flags) | 453 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, |
454 | char *flags) | ||
454 | { | 455 | { |
455 | char evt_path[MAXPATHLEN]; | 456 | char evt_path[MAXPATHLEN]; |
456 | struct dirent *evt_ent; | 457 | struct dirent *evt_ent; |
@@ -474,6 +475,9 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) | |||
474 | || !strcmp(evt_ent->d_name, "filter")) | 475 | || !strcmp(evt_ent->d_name, "filter")) |
475 | continue; | 476 | continue; |
476 | 477 | ||
478 | if (!strglobmatch(evt_ent->d_name, evt_exp)) | ||
479 | continue; | ||
480 | |||
477 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | 481 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, |
478 | evt_ent->d_name, flags ? ":" : "", | 482 | evt_ent->d_name, flags ? ":" : "", |
479 | flags ?: ""); | 483 | flags ?: ""); |
@@ -522,9 +526,10 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
522 | if (evt_length >= MAX_EVENT_LENGTH) | 526 | if (evt_length >= MAX_EVENT_LENGTH) |
523 | return EVT_FAILED; | 527 | return EVT_FAILED; |
524 | 528 | ||
525 | if (!strcmp(evt_name, "*")) { | 529 | if (strpbrk(evt_name, "*?")) { |
526 | *strp = evt_name + evt_length; | 530 | *strp = evt_name + evt_length; |
527 | return parse_subsystem_tracepoint_event(sys_name, flags); | 531 | return parse_multiple_tracepoint_event(sys_name, evt_name, |
532 | flags); | ||
528 | } else | 533 | } else |
529 | return parse_single_tracepoint_event(sys_name, evt_name, | 534 | return parse_single_tracepoint_event(sys_name, evt_name, |
530 | evt_length, flags, | 535 | evt_length, flags, |
@@ -753,11 +758,11 @@ modifier: | |||
753 | return ret; | 758 | return ret; |
754 | } | 759 | } |
755 | 760 | ||
756 | static void store_event_type(const char *orgname) | 761 | static int store_event_type(const char *orgname) |
757 | { | 762 | { |
758 | char filename[PATH_MAX], *c; | 763 | char filename[PATH_MAX], *c; |
759 | FILE *file; | 764 | FILE *file; |
760 | int id; | 765 | int id, n; |
761 | 766 | ||
762 | sprintf(filename, "%s/", debugfs_path); | 767 | sprintf(filename, "%s/", debugfs_path); |
763 | strncat(filename, orgname, strlen(orgname)); | 768 | strncat(filename, orgname, strlen(orgname)); |
@@ -769,11 +774,14 @@ static void store_event_type(const char *orgname) | |||
769 | 774 | ||
770 | file = fopen(filename, "r"); | 775 | file = fopen(filename, "r"); |
771 | if (!file) | 776 | if (!file) |
772 | return; | 777 | return 0; |
773 | if (fscanf(file, "%i", &id) < 1) | 778 | n = fscanf(file, "%i", &id); |
774 | die("cannot store event ID"); | ||
775 | fclose(file); | 779 | fclose(file); |
776 | perf_header__push_event(id, orgname); | 780 | if (n < 1) { |
781 | pr_err("cannot store event ID\n"); | ||
782 | return -EINVAL; | ||
783 | } | ||
784 | return perf_header__push_event(id, orgname); | ||
777 | } | 785 | } |
778 | 786 | ||
779 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 787 | int parse_events(const struct option *opt __used, const char *str, int unset __used) |
@@ -782,7 +790,8 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
782 | enum event_result ret; | 790 | enum event_result ret; |
783 | 791 | ||
784 | if (strchr(str, ':')) | 792 | if (strchr(str, ':')) |
785 | store_event_type(str); | 793 | if (store_event_type(str) < 0) |
794 | return -1; | ||
786 | 795 | ||
787 | for (;;) { | 796 | for (;;) { |
788 | if (nr_counters == MAX_COUNTERS) | 797 | if (nr_counters == MAX_COUNTERS) |
@@ -835,11 +844,12 @@ int parse_filter(const struct option *opt __used, const char *str, | |||
835 | } | 844 | } |
836 | 845 | ||
837 | static const char * const event_type_descriptors[] = { | 846 | static const char * const event_type_descriptors[] = { |
838 | "", | ||
839 | "Hardware event", | 847 | "Hardware event", |
840 | "Software event", | 848 | "Software event", |
841 | "Tracepoint event", | 849 | "Tracepoint event", |
842 | "Hardware cache event", | 850 | "Hardware cache event", |
851 | "Raw hardware event descriptor", | ||
852 | "Hardware breakpoint", | ||
843 | }; | 853 | }; |
844 | 854 | ||
845 | /* | 855 | /* |
@@ -872,7 +882,7 @@ static void print_tracepoint_events(void) | |||
872 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 882 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
873 | sys_dirent.d_name, evt_dirent.d_name); | 883 | sys_dirent.d_name, evt_dirent.d_name); |
874 | printf(" %-42s [%s]\n", evt_path, | 884 | printf(" %-42s [%s]\n", evt_path, |
875 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | 885 | event_type_descriptors[PERF_TYPE_TRACEPOINT]); |
876 | } | 886 | } |
877 | closedir(evt_dir); | 887 | closedir(evt_dir); |
878 | } | 888 | } |
@@ -892,9 +902,7 @@ void print_events(void) | |||
892 | printf("List of pre-defined events (to be used in -e):\n"); | 902 | printf("List of pre-defined events (to be used in -e):\n"); |
893 | 903 | ||
894 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 904 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
895 | type = syms->type + 1; | 905 | type = syms->type; |
896 | if (type >= ARRAY_SIZE(event_type_descriptors)) | ||
897 | type = 0; | ||
898 | 906 | ||
899 | if (type != prev_type) | 907 | if (type != prev_type) |
900 | printf("\n"); | 908 | printf("\n"); |
@@ -919,17 +927,19 @@ void print_events(void) | |||
919 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | 927 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { |
920 | printf(" %-42s [%s]\n", | 928 | printf(" %-42s [%s]\n", |
921 | event_cache_name(type, op, i), | 929 | event_cache_name(type, op, i), |
922 | event_type_descriptors[4]); | 930 | event_type_descriptors[PERF_TYPE_HW_CACHE]); |
923 | } | 931 | } |
924 | } | 932 | } |
925 | } | 933 | } |
926 | 934 | ||
927 | printf("\n"); | 935 | printf("\n"); |
928 | printf(" %-42s [raw hardware event descriptor]\n", | 936 | printf(" %-42s [%s]\n", |
929 | "rNNN"); | 937 | "rNNN", event_type_descriptors[PERF_TYPE_RAW]); |
930 | printf("\n"); | 938 | printf("\n"); |
931 | 939 | ||
932 | printf(" %-42s [hardware breakpoint]\n", "mem:<addr>[:access]"); | 940 | printf(" %-42s [%s]\n", |
941 | "mem:<addr>[:access]", | ||
942 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | ||
933 | printf("\n"); | 943 | printf("\n"); |
934 | 944 | ||
935 | print_tracepoint_events(); | 945 | print_tracepoint_events(); |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 29465d440043..71b0dd590a37 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -37,6 +37,8 @@ | |||
37 | #include "string.h" | 37 | #include "string.h" |
38 | #include "strlist.h" | 38 | #include "strlist.h" |
39 | #include "debug.h" | 39 | #include "debug.h" |
40 | #include "cache.h" | ||
41 | #include "color.h" | ||
40 | #include "parse-events.h" /* For debugfs_path */ | 42 | #include "parse-events.h" /* For debugfs_path */ |
41 | #include "probe-event.h" | 43 | #include "probe-event.h" |
42 | 44 | ||
@@ -62,6 +64,42 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) | |||
62 | return ret; | 64 | return ret; |
63 | } | 65 | } |
64 | 66 | ||
67 | void parse_line_range_desc(const char *arg, struct line_range *lr) | ||
68 | { | ||
69 | const char *ptr; | ||
70 | char *tmp; | ||
71 | /* | ||
72 | * <Syntax> | ||
73 | * SRC:SLN[+NUM|-ELN] | ||
74 | * FUNC[:SLN[+NUM|-ELN]] | ||
75 | */ | ||
76 | ptr = strchr(arg, ':'); | ||
77 | if (ptr) { | ||
78 | lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0); | ||
79 | if (*tmp == '+') | ||
80 | lr->end = lr->start + (unsigned int)strtoul(tmp + 1, | ||
81 | &tmp, 0); | ||
82 | else if (*tmp == '-') | ||
83 | lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0); | ||
84 | else | ||
85 | lr->end = 0; | ||
86 | pr_debug("Line range is %u to %u\n", lr->start, lr->end); | ||
87 | if (lr->end && lr->start > lr->end) | ||
88 | semantic_error("Start line must be smaller" | ||
89 | " than end line."); | ||
90 | if (*tmp != '\0') | ||
91 | semantic_error("Tailing with invalid character '%d'.", | ||
92 | *tmp); | ||
93 | tmp = strndup(arg, (ptr - arg)); | ||
94 | } else | ||
95 | tmp = strdup(arg); | ||
96 | |||
97 | if (strchr(tmp, '.')) | ||
98 | lr->file = tmp; | ||
99 | else | ||
100 | lr->function = tmp; | ||
101 | } | ||
102 | |||
65 | /* Check the name is good for event/group */ | 103 | /* Check the name is good for event/group */ |
66 | static bool check_event_name(const char *name) | 104 | static bool check_event_name(const char *name) |
67 | { | 105 | { |
@@ -368,7 +406,7 @@ static int open_kprobe_events(int flags, int mode) | |||
368 | if (ret < 0) { | 406 | if (ret < 0) { |
369 | if (errno == ENOENT) | 407 | if (errno == ENOENT) |
370 | die("kprobe_events file does not exist -" | 408 | die("kprobe_events file does not exist -" |
371 | " please rebuild with CONFIG_KPROBE_TRACER."); | 409 | " please rebuild with CONFIG_KPROBE_EVENT."); |
372 | else | 410 | else |
373 | die("Could not open kprobe_events file: %s", | 411 | die("Could not open kprobe_events file: %s", |
374 | strerror(errno)); | 412 | strerror(errno)); |
@@ -455,6 +493,8 @@ void show_perf_probe_events(void) | |||
455 | struct strlist *rawlist; | 493 | struct strlist *rawlist; |
456 | struct str_node *ent; | 494 | struct str_node *ent; |
457 | 495 | ||
496 | setup_pager(); | ||
497 | |||
458 | fd = open_kprobe_events(O_RDONLY, 0); | 498 | fd = open_kprobe_events(O_RDONLY, 0); |
459 | rawlist = get_trace_kprobe_event_rawlist(fd); | 499 | rawlist = get_trace_kprobe_event_rawlist(fd); |
460 | close(fd); | 500 | close(fd); |
@@ -675,3 +715,66 @@ void del_trace_kprobe_events(struct strlist *dellist) | |||
675 | close(fd); | 715 | close(fd); |
676 | } | 716 | } |
677 | 717 | ||
718 | #define LINEBUF_SIZE 256 | ||
719 | |||
720 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | ||
721 | { | ||
722 | char buf[LINEBUF_SIZE]; | ||
723 | const char *color = PERF_COLOR_BLUE; | ||
724 | |||
725 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
726 | goto error; | ||
727 | if (!skip) { | ||
728 | if (show_num) | ||
729 | fprintf(stdout, "%7u %s", l, buf); | ||
730 | else | ||
731 | color_fprintf(stdout, color, " %s", buf); | ||
732 | } | ||
733 | |||
734 | while (strlen(buf) == LINEBUF_SIZE - 1 && | ||
735 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
736 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
737 | goto error; | ||
738 | if (!skip) { | ||
739 | if (show_num) | ||
740 | fprintf(stdout, "%s", buf); | ||
741 | else | ||
742 | color_fprintf(stdout, color, "%s", buf); | ||
743 | } | ||
744 | } | ||
745 | return; | ||
746 | error: | ||
747 | if (feof(fp)) | ||
748 | die("Source file is shorter than expected."); | ||
749 | else | ||
750 | die("File read error: %s", strerror(errno)); | ||
751 | } | ||
752 | |||
753 | void show_line_range(struct line_range *lr) | ||
754 | { | ||
755 | unsigned int l = 1; | ||
756 | struct line_node *ln; | ||
757 | FILE *fp; | ||
758 | |||
759 | setup_pager(); | ||
760 | |||
761 | if (lr->function) | ||
762 | fprintf(stdout, "<%s:%d>\n", lr->function, | ||
763 | lr->start - lr->offset); | ||
764 | else | ||
765 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | ||
766 | |||
767 | fp = fopen(lr->path, "r"); | ||
768 | if (fp == NULL) | ||
769 | die("Failed to open %s: %s", lr->path, strerror(errno)); | ||
770 | /* Skip to starting line number */ | ||
771 | while (l < lr->start) | ||
772 | show_one_line(fp, l++, true, false); | ||
773 | |||
774 | list_for_each_entry(ln, &lr->line_list, list) { | ||
775 | while (ln->line > l) | ||
776 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
777 | show_one_line(fp, (l++) - lr->offset, false, true); | ||
778 | } | ||
779 | fclose(fp); | ||
780 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 7f1d499118c0..711287d4baea 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "probe-finder.h" | 5 | #include "probe-finder.h" |
6 | #include "strlist.h" | 6 | #include "strlist.h" |
7 | 7 | ||
8 | extern void parse_line_range_desc(const char *arg, struct line_range *lr); | ||
8 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, | 9 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, |
9 | bool *need_dwarf); | 10 | bool *need_dwarf); |
10 | extern int synthesize_perf_probe_point(struct probe_point *pp); | 11 | extern int synthesize_perf_probe_point(struct probe_point *pp); |
@@ -15,6 +16,7 @@ extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | |||
15 | bool force_add); | 16 | bool force_add); |
16 | extern void del_trace_kprobe_events(struct strlist *dellist); | 17 | extern void del_trace_kprobe_events(struct strlist *dellist); |
17 | extern void show_perf_probe_events(void); | 18 | extern void show_perf_probe_events(void); |
19 | extern void show_line_range(struct line_range *lr); | ||
18 | 20 | ||
19 | /* Maximum index number of event-name postfix */ | 21 | /* Maximum index number of event-name postfix */ |
20 | #define MAX_EVENT_INDEX 1024 | 22 | #define MAX_EVENT_INDEX 1024 |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 4b852c0d16a5..1b2124d12f68 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -140,6 +140,31 @@ static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname) | |||
140 | return found; | 140 | return found; |
141 | } | 141 | } |
142 | 142 | ||
143 | static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf) | ||
144 | { | ||
145 | Dwarf_Signed cnt, i; | ||
146 | char **srcs; | ||
147 | int ret = 0; | ||
148 | |||
149 | if (!buf || !fno) | ||
150 | return -EINVAL; | ||
151 | |||
152 | ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); | ||
153 | if (ret == DW_DLV_OK) { | ||
154 | if ((Dwarf_Unsigned)cnt > fno - 1) { | ||
155 | *buf = strdup(srcs[fno - 1]); | ||
156 | ret = 0; | ||
157 | pr_debug("found filename: %s\n", *buf); | ||
158 | } else | ||
159 | ret = -ENOENT; | ||
160 | for (i = 0; i < cnt; i++) | ||
161 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | ||
162 | dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); | ||
163 | } else | ||
164 | ret = -EINVAL; | ||
165 | return ret; | ||
166 | } | ||
167 | |||
143 | /* Compare diename and tname */ | 168 | /* Compare diename and tname */ |
144 | static int die_compare_name(Dwarf_Die dw_die, const char *tname) | 169 | static int die_compare_name(Dwarf_Die dw_die, const char *tname) |
145 | { | 170 | { |
@@ -402,11 +427,11 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | |||
402 | } else if (op == DW_OP_regx) { | 427 | } else if (op == DW_OP_regx) { |
403 | regn = loc->lr_number; | 428 | regn = loc->lr_number; |
404 | } else | 429 | } else |
405 | die("Dwarf_OP %d is not supported.\n", op); | 430 | die("Dwarf_OP %d is not supported.", op); |
406 | 431 | ||
407 | regs = get_arch_regstr(regn); | 432 | regs = get_arch_regstr(regn); |
408 | if (!regs) | 433 | if (!regs) |
409 | die("%lld exceeds max register number.\n", regn); | 434 | die("%lld exceeds max register number.", regn); |
410 | 435 | ||
411 | if (deref) | 436 | if (deref) |
412 | ret = snprintf(pf->buf, pf->len, | 437 | ret = snprintf(pf->buf, pf->len, |
@@ -438,7 +463,7 @@ static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf) | |||
438 | return ; | 463 | return ; |
439 | error: | 464 | error: |
440 | die("Failed to find the location of %s at this address.\n" | 465 | die("Failed to find the location of %s at this address.\n" |
441 | " Perhaps, it has been optimized out.\n", pf->var); | 466 | " Perhaps, it has been optimized out.", pf->var); |
442 | } | 467 | } |
443 | 468 | ||
444 | static int variable_callback(struct die_link *dlink, void *data) | 469 | static int variable_callback(struct die_link *dlink, void *data) |
@@ -476,7 +501,7 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | |||
476 | /* Search child die for local variables and parameters. */ | 501 | /* Search child die for local variables and parameters. */ |
477 | ret = search_die_from_children(sp_die, variable_callback, pf); | 502 | ret = search_die_from_children(sp_die, variable_callback, pf); |
478 | if (!ret) | 503 | if (!ret) |
479 | die("Failed to find '%s' in this function.\n", pf->var); | 504 | die("Failed to find '%s' in this function.", pf->var); |
480 | } | 505 | } |
481 | 506 | ||
482 | /* Get a frame base on the address */ | 507 | /* Get a frame base on the address */ |
@@ -567,7 +592,7 @@ static int probeaddr_callback(struct die_link *dlink, void *data) | |||
567 | } | 592 | } |
568 | 593 | ||
569 | /* Find probe point from its line number */ | 594 | /* Find probe point from its line number */ |
570 | static void find_by_line(struct probe_finder *pf) | 595 | static void find_probe_point_by_line(struct probe_finder *pf) |
571 | { | 596 | { |
572 | Dwarf_Signed cnt, i, clm; | 597 | Dwarf_Signed cnt, i, clm; |
573 | Dwarf_Line *lines; | 598 | Dwarf_Line *lines; |
@@ -602,7 +627,7 @@ static void find_by_line(struct probe_finder *pf) | |||
602 | ret = search_die_from_children(pf->cu_die, | 627 | ret = search_die_from_children(pf->cu_die, |
603 | probeaddr_callback, pf); | 628 | probeaddr_callback, pf); |
604 | if (ret == 0) | 629 | if (ret == 0) |
605 | die("Probe point is not found in subprograms.\n"); | 630 | die("Probe point is not found in subprograms."); |
606 | /* Continuing, because target line might be inlined. */ | 631 | /* Continuing, because target line might be inlined. */ |
607 | } | 632 | } |
608 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | 633 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); |
@@ -626,7 +651,7 @@ static int probefunc_callback(struct die_link *dlink, void *data) | |||
626 | pf->fno = die_get_decl_file(dlink->die); | 651 | pf->fno = die_get_decl_file(dlink->die); |
627 | pf->lno = die_get_decl_line(dlink->die) | 652 | pf->lno = die_get_decl_line(dlink->die) |
628 | + pp->line; | 653 | + pp->line; |
629 | find_by_line(pf); | 654 | find_probe_point_by_line(pf); |
630 | return 1; | 655 | return 1; |
631 | } | 656 | } |
632 | if (die_inlined_subprogram(dlink->die)) { | 657 | if (die_inlined_subprogram(dlink->die)) { |
@@ -661,7 +686,7 @@ static int probefunc_callback(struct die_link *dlink, void *data) | |||
661 | !die_inlined_subprogram(lk->die)) | 686 | !die_inlined_subprogram(lk->die)) |
662 | goto found; | 687 | goto found; |
663 | } | 688 | } |
664 | die("Failed to find real subprogram.\n"); | 689 | die("Failed to find real subprogram."); |
665 | found: | 690 | found: |
666 | /* Get offset from subprogram */ | 691 | /* Get offset from subprogram */ |
667 | ret = die_within_subprogram(lk->die, pf->addr, &offs); | 692 | ret = die_within_subprogram(lk->die, pf->addr, &offs); |
@@ -673,7 +698,7 @@ found: | |||
673 | return 0; | 698 | return 0; |
674 | } | 699 | } |
675 | 700 | ||
676 | static void find_by_func(struct probe_finder *pf) | 701 | static void find_probe_point_by_func(struct probe_finder *pf) |
677 | { | 702 | { |
678 | search_die_from_children(pf->cu_die, probefunc_callback, pf); | 703 | search_die_from_children(pf->cu_die, probefunc_callback, pf); |
679 | } | 704 | } |
@@ -714,10 +739,10 @@ int find_probepoint(int fd, struct probe_point *pp) | |||
714 | if (ret == DW_DLV_NO_ENTRY) | 739 | if (ret == DW_DLV_NO_ENTRY) |
715 | pf.cu_base = 0; | 740 | pf.cu_base = 0; |
716 | if (pp->function) | 741 | if (pp->function) |
717 | find_by_func(&pf); | 742 | find_probe_point_by_func(&pf); |
718 | else { | 743 | else { |
719 | pf.lno = pp->line; | 744 | pf.lno = pp->line; |
720 | find_by_line(&pf); | 745 | find_probe_point_by_line(&pf); |
721 | } | 746 | } |
722 | } | 747 | } |
723 | dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); | 748 | dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); |
@@ -728,3 +753,159 @@ int find_probepoint(int fd, struct probe_point *pp) | |||
728 | return pp->found; | 753 | return pp->found; |
729 | } | 754 | } |
730 | 755 | ||
756 | |||
757 | static void line_range_add_line(struct line_range *lr, unsigned int line) | ||
758 | { | ||
759 | struct line_node *ln; | ||
760 | struct list_head *p; | ||
761 | |||
762 | /* Reverse search, because new line will be the last one */ | ||
763 | list_for_each_entry_reverse(ln, &lr->line_list, list) { | ||
764 | if (ln->line < line) { | ||
765 | p = &ln->list; | ||
766 | goto found; | ||
767 | } else if (ln->line == line) /* Already exist */ | ||
768 | return ; | ||
769 | } | ||
770 | /* List is empty, or the smallest entry */ | ||
771 | p = &lr->line_list; | ||
772 | found: | ||
773 | pr_debug("Debug: add a line %u\n", line); | ||
774 | ln = zalloc(sizeof(struct line_node)); | ||
775 | DIE_IF(ln == NULL); | ||
776 | ln->line = line; | ||
777 | INIT_LIST_HEAD(&ln->list); | ||
778 | list_add(&ln->list, p); | ||
779 | } | ||
780 | |||
781 | /* Find line range from its line number */ | ||
782 | static void find_line_range_by_line(struct line_finder *lf) | ||
783 | { | ||
784 | Dwarf_Signed cnt, i; | ||
785 | Dwarf_Line *lines; | ||
786 | Dwarf_Unsigned lineno = 0; | ||
787 | Dwarf_Unsigned fno; | ||
788 | Dwarf_Addr addr; | ||
789 | int ret; | ||
790 | |||
791 | ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error); | ||
792 | DIE_IF(ret != DW_DLV_OK); | ||
793 | |||
794 | for (i = 0; i < cnt; i++) { | ||
795 | ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); | ||
796 | DIE_IF(ret != DW_DLV_OK); | ||
797 | if (fno != lf->fno) | ||
798 | continue; | ||
799 | |||
800 | ret = dwarf_lineno(lines[i], &lineno, &__dw_error); | ||
801 | DIE_IF(ret != DW_DLV_OK); | ||
802 | if (lf->lno_s > lineno || lf->lno_e < lineno) | ||
803 | continue; | ||
804 | |||
805 | /* Filter line in the function address range */ | ||
806 | if (lf->addr_s && lf->addr_e) { | ||
807 | ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); | ||
808 | DIE_IF(ret != DW_DLV_OK); | ||
809 | if (lf->addr_s > addr || lf->addr_e <= addr) | ||
810 | continue; | ||
811 | } | ||
812 | line_range_add_line(lf->lr, (unsigned int)lineno); | ||
813 | } | ||
814 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | ||
815 | if (!list_empty(&lf->lr->line_list)) | ||
816 | lf->found = 1; | ||
817 | } | ||
818 | |||
819 | /* Search function from function name */ | ||
820 | static int linefunc_callback(struct die_link *dlink, void *data) | ||
821 | { | ||
822 | struct line_finder *lf = (struct line_finder *)data; | ||
823 | struct line_range *lr = lf->lr; | ||
824 | Dwarf_Half tag; | ||
825 | int ret; | ||
826 | |||
827 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | ||
828 | DIE_IF(ret == DW_DLV_ERROR); | ||
829 | if (tag == DW_TAG_subprogram && | ||
830 | die_compare_name(dlink->die, lr->function) == 0) { | ||
831 | /* Get the address range of this function */ | ||
832 | ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error); | ||
833 | if (ret == DW_DLV_OK) | ||
834 | ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error); | ||
835 | DIE_IF(ret == DW_DLV_ERROR); | ||
836 | if (ret == DW_DLV_NO_ENTRY) { | ||
837 | lf->addr_s = 0; | ||
838 | lf->addr_e = 0; | ||
839 | } | ||
840 | |||
841 | lf->fno = die_get_decl_file(dlink->die); | ||
842 | lr->offset = die_get_decl_line(dlink->die);; | ||
843 | lf->lno_s = lr->offset + lr->start; | ||
844 | if (!lr->end) | ||
845 | lf->lno_e = (Dwarf_Unsigned)-1; | ||
846 | else | ||
847 | lf->lno_e = lr->offset + lr->end; | ||
848 | lr->start = lf->lno_s; | ||
849 | lr->end = lf->lno_e; | ||
850 | find_line_range_by_line(lf); | ||
851 | /* If we find a target function, this should be end. */ | ||
852 | lf->found = 1; | ||
853 | return 1; | ||
854 | } | ||
855 | return 0; | ||
856 | } | ||
857 | |||
858 | static void find_line_range_by_func(struct line_finder *lf) | ||
859 | { | ||
860 | search_die_from_children(lf->cu_die, linefunc_callback, lf); | ||
861 | } | ||
862 | |||
863 | int find_line_range(int fd, struct line_range *lr) | ||
864 | { | ||
865 | Dwarf_Half addr_size = 0; | ||
866 | Dwarf_Unsigned next_cuh = 0; | ||
867 | int ret; | ||
868 | struct line_finder lf = {.lr = lr}; | ||
869 | |||
870 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | ||
871 | if (ret != DW_DLV_OK) | ||
872 | return -ENOENT; | ||
873 | |||
874 | while (!lf.found) { | ||
875 | /* Search CU (Compilation Unit) */ | ||
876 | ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, | ||
877 | &addr_size, &next_cuh, &__dw_error); | ||
878 | DIE_IF(ret == DW_DLV_ERROR); | ||
879 | if (ret == DW_DLV_NO_ENTRY) | ||
880 | break; | ||
881 | |||
882 | /* Get the DIE(Debugging Information Entry) of this CU */ | ||
883 | ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error); | ||
884 | DIE_IF(ret != DW_DLV_OK); | ||
885 | |||
886 | /* Check if target file is included. */ | ||
887 | if (lr->file) | ||
888 | lf.fno = cu_find_fileno(lf.cu_die, lr->file); | ||
889 | |||
890 | if (!lr->file || lf.fno) { | ||
891 | if (lr->function) | ||
892 | find_line_range_by_func(&lf); | ||
893 | else { | ||
894 | lf.lno_s = lr->start; | ||
895 | if (!lr->end) | ||
896 | lf.lno_e = (Dwarf_Unsigned)-1; | ||
897 | else | ||
898 | lf.lno_e = lr->end; | ||
899 | find_line_range_by_line(&lf); | ||
900 | } | ||
901 | /* Get the real file path */ | ||
902 | if (lf.found) | ||
903 | cu_get_filename(lf.cu_die, lf.fno, &lr->path); | ||
904 | } | ||
905 | dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE); | ||
906 | } | ||
907 | ret = dwarf_finish(__dw_debug, &__dw_error); | ||
908 | DIE_IF(ret != DW_DLV_OK); | ||
909 | return lf.found; | ||
910 | } | ||
911 | |||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index a4086aaddb73..972b386116f1 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef _PROBE_FINDER_H | 1 | #ifndef _PROBE_FINDER_H |
2 | #define _PROBE_FINDER_H | 2 | #define _PROBE_FINDER_H |
3 | 3 | ||
4 | #include "util.h" | ||
5 | |||
4 | #define MAX_PATH_LEN 256 | 6 | #define MAX_PATH_LEN 256 |
5 | #define MAX_PROBE_BUFFER 1024 | 7 | #define MAX_PROBE_BUFFER 1024 |
6 | #define MAX_PROBES 128 | 8 | #define MAX_PROBES 128 |
@@ -32,8 +34,26 @@ struct probe_point { | |||
32 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ | 34 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ |
33 | }; | 35 | }; |
34 | 36 | ||
37 | /* Line number container */ | ||
38 | struct line_node { | ||
39 | struct list_head list; | ||
40 | unsigned int line; | ||
41 | }; | ||
42 | |||
43 | /* Line range */ | ||
44 | struct line_range { | ||
45 | char *file; /* File name */ | ||
46 | char *function; /* Function name */ | ||
47 | unsigned int start; /* Start line number */ | ||
48 | unsigned int end; /* End line number */ | ||
49 | unsigned int offset; /* Start line offset */ | ||
50 | char *path; /* Real path name */ | ||
51 | struct list_head line_list; /* Visible lines */ | ||
52 | }; | ||
53 | |||
35 | #ifndef NO_LIBDWARF | 54 | #ifndef NO_LIBDWARF |
36 | extern int find_probepoint(int fd, struct probe_point *pp); | 55 | extern int find_probepoint(int fd, struct probe_point *pp); |
56 | extern int find_line_range(int fd, struct line_range *lr); | ||
37 | 57 | ||
38 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ | 58 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ |
39 | #ifndef _MIPS_SZLONG | 59 | #ifndef _MIPS_SZLONG |
@@ -60,6 +80,19 @@ struct probe_finder { | |||
60 | char *buf; /* Current output buffer */ | 80 | char *buf; /* Current output buffer */ |
61 | int len; /* Length of output buffer */ | 81 | int len; /* Length of output buffer */ |
62 | }; | 82 | }; |
83 | |||
84 | struct line_finder { | ||
85 | struct line_range *lr; /* Target line range */ | ||
86 | |||
87 | Dwarf_Unsigned fno; /* File number */ | ||
88 | Dwarf_Unsigned lno_s; /* Start line number */ | ||
89 | Dwarf_Unsigned lno_e; /* End line number */ | ||
90 | Dwarf_Addr addr_s; /* Start address */ | ||
91 | Dwarf_Addr addr_e; /* End address */ | ||
92 | Dwarf_Die cu_die; /* Current CU */ | ||
93 | int found; | ||
94 | }; | ||
95 | |||
63 | #endif /* NO_LIBDWARF */ | 96 | #endif /* NO_LIBDWARF */ |
64 | 97 | ||
65 | #endif /*_PROBE_FINDER_H */ | 98 | #endif /*_PROBE_FINDER_H */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index ce3a6c8abe76..8e7c1896eaa2 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
2 | 2 | ||
3 | #include <byteswap.h> | ||
3 | #include <unistd.h> | 4 | #include <unistd.h> |
4 | #include <sys/types.h> | 5 | #include <sys/types.h> |
5 | 6 | ||
@@ -66,13 +67,22 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
66 | self->mmap_window = 32; | 67 | self->mmap_window = 32; |
67 | self->cwd = NULL; | 68 | self->cwd = NULL; |
68 | self->cwdlen = 0; | 69 | self->cwdlen = 0; |
70 | self->unknown_events = 0; | ||
69 | map_groups__init(&self->kmaps); | 71 | map_groups__init(&self->kmaps); |
70 | 72 | ||
71 | if (perf_session__create_kernel_maps(self) < 0) | 73 | if (mode == O_RDONLY) { |
72 | goto out_delete; | 74 | if (perf_session__open(self, force) < 0) |
75 | goto out_delete; | ||
76 | } else if (mode == O_WRONLY) { | ||
77 | /* | ||
78 | * In O_RDONLY mode this will be performed when reading the | ||
79 | * kernel MMAP event, in event__process_mmap(). | ||
80 | */ | ||
81 | if (perf_session__create_kernel_maps(self) < 0) | ||
82 | goto out_delete; | ||
83 | } | ||
73 | 84 | ||
74 | if (mode == O_RDONLY && perf_session__open(self, force) < 0) | 85 | self->sample_type = perf_header__sample_type(&self->header); |
75 | goto out_delete; | ||
76 | out: | 86 | out: |
77 | return self; | 87 | return self; |
78 | out_free: | 88 | out_free: |
@@ -148,3 +158,389 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, | |||
148 | 158 | ||
149 | return syms; | 159 | return syms; |
150 | } | 160 | } |
161 | |||
162 | static int process_event_stub(event_t *event __used, | ||
163 | struct perf_session *session __used) | ||
164 | { | ||
165 | dump_printf(": unhandled!\n"); | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | ||
170 | { | ||
171 | if (handler->sample == NULL) | ||
172 | handler->sample = process_event_stub; | ||
173 | if (handler->mmap == NULL) | ||
174 | handler->mmap = process_event_stub; | ||
175 | if (handler->comm == NULL) | ||
176 | handler->comm = process_event_stub; | ||
177 | if (handler->fork == NULL) | ||
178 | handler->fork = process_event_stub; | ||
179 | if (handler->exit == NULL) | ||
180 | handler->exit = process_event_stub; | ||
181 | if (handler->lost == NULL) | ||
182 | handler->lost = process_event_stub; | ||
183 | if (handler->read == NULL) | ||
184 | handler->read = process_event_stub; | ||
185 | if (handler->throttle == NULL) | ||
186 | handler->throttle = process_event_stub; | ||
187 | if (handler->unthrottle == NULL) | ||
188 | handler->unthrottle = process_event_stub; | ||
189 | } | ||
190 | |||
191 | static const char *event__name[] = { | ||
192 | [0] = "TOTAL", | ||
193 | [PERF_RECORD_MMAP] = "MMAP", | ||
194 | [PERF_RECORD_LOST] = "LOST", | ||
195 | [PERF_RECORD_COMM] = "COMM", | ||
196 | [PERF_RECORD_EXIT] = "EXIT", | ||
197 | [PERF_RECORD_THROTTLE] = "THROTTLE", | ||
198 | [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE", | ||
199 | [PERF_RECORD_FORK] = "FORK", | ||
200 | [PERF_RECORD_READ] = "READ", | ||
201 | [PERF_RECORD_SAMPLE] = "SAMPLE", | ||
202 | }; | ||
203 | |||
204 | unsigned long event__total[PERF_RECORD_MAX]; | ||
205 | |||
206 | void event__print_totals(void) | ||
207 | { | ||
208 | int i; | ||
209 | for (i = 0; i < PERF_RECORD_MAX; ++i) | ||
210 | pr_info("%10s events: %10ld\n", | ||
211 | event__name[i], event__total[i]); | ||
212 | } | ||
213 | |||
214 | void mem_bswap_64(void *src, int byte_size) | ||
215 | { | ||
216 | u64 *m = src; | ||
217 | |||
218 | while (byte_size > 0) { | ||
219 | *m = bswap_64(*m); | ||
220 | byte_size -= sizeof(u64); | ||
221 | ++m; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | static void event__all64_swap(event_t *self) | ||
226 | { | ||
227 | struct perf_event_header *hdr = &self->header; | ||
228 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | ||
229 | } | ||
230 | |||
231 | static void event__comm_swap(event_t *self) | ||
232 | { | ||
233 | self->comm.pid = bswap_32(self->comm.pid); | ||
234 | self->comm.tid = bswap_32(self->comm.tid); | ||
235 | } | ||
236 | |||
237 | static void event__mmap_swap(event_t *self) | ||
238 | { | ||
239 | self->mmap.pid = bswap_32(self->mmap.pid); | ||
240 | self->mmap.tid = bswap_32(self->mmap.tid); | ||
241 | self->mmap.start = bswap_64(self->mmap.start); | ||
242 | self->mmap.len = bswap_64(self->mmap.len); | ||
243 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | ||
244 | } | ||
245 | |||
246 | static void event__task_swap(event_t *self) | ||
247 | { | ||
248 | self->fork.pid = bswap_32(self->fork.pid); | ||
249 | self->fork.tid = bswap_32(self->fork.tid); | ||
250 | self->fork.ppid = bswap_32(self->fork.ppid); | ||
251 | self->fork.ptid = bswap_32(self->fork.ptid); | ||
252 | self->fork.time = bswap_64(self->fork.time); | ||
253 | } | ||
254 | |||
255 | static void event__read_swap(event_t *self) | ||
256 | { | ||
257 | self->read.pid = bswap_32(self->read.pid); | ||
258 | self->read.tid = bswap_32(self->read.tid); | ||
259 | self->read.value = bswap_64(self->read.value); | ||
260 | self->read.time_enabled = bswap_64(self->read.time_enabled); | ||
261 | self->read.time_running = bswap_64(self->read.time_running); | ||
262 | self->read.id = bswap_64(self->read.id); | ||
263 | } | ||
264 | |||
265 | typedef void (*event__swap_op)(event_t *self); | ||
266 | |||
267 | static event__swap_op event__swap_ops[] = { | ||
268 | [PERF_RECORD_MMAP] = event__mmap_swap, | ||
269 | [PERF_RECORD_COMM] = event__comm_swap, | ||
270 | [PERF_RECORD_FORK] = event__task_swap, | ||
271 | [PERF_RECORD_EXIT] = event__task_swap, | ||
272 | [PERF_RECORD_LOST] = event__all64_swap, | ||
273 | [PERF_RECORD_READ] = event__read_swap, | ||
274 | [PERF_RECORD_SAMPLE] = event__all64_swap, | ||
275 | [PERF_RECORD_MAX] = NULL, | ||
276 | }; | ||
277 | |||
278 | static int perf_session__process_event(struct perf_session *self, | ||
279 | event_t *event, | ||
280 | struct perf_event_ops *ops, | ||
281 | u64 offset, u64 head) | ||
282 | { | ||
283 | trace_event(event); | ||
284 | |||
285 | if (event->header.type < PERF_RECORD_MAX) { | ||
286 | dump_printf("%#Lx [%#x]: PERF_RECORD_%s", | ||
287 | offset + head, event->header.size, | ||
288 | event__name[event->header.type]); | ||
289 | ++event__total[0]; | ||
290 | ++event__total[event->header.type]; | ||
291 | } | ||
292 | |||
293 | if (self->header.needs_swap && event__swap_ops[event->header.type]) | ||
294 | event__swap_ops[event->header.type](event); | ||
295 | |||
296 | switch (event->header.type) { | ||
297 | case PERF_RECORD_SAMPLE: | ||
298 | return ops->sample(event, self); | ||
299 | case PERF_RECORD_MMAP: | ||
300 | return ops->mmap(event, self); | ||
301 | case PERF_RECORD_COMM: | ||
302 | return ops->comm(event, self); | ||
303 | case PERF_RECORD_FORK: | ||
304 | return ops->fork(event, self); | ||
305 | case PERF_RECORD_EXIT: | ||
306 | return ops->exit(event, self); | ||
307 | case PERF_RECORD_LOST: | ||
308 | return ops->lost(event, self); | ||
309 | case PERF_RECORD_READ: | ||
310 | return ops->read(event, self); | ||
311 | case PERF_RECORD_THROTTLE: | ||
312 | return ops->throttle(event, self); | ||
313 | case PERF_RECORD_UNTHROTTLE: | ||
314 | return ops->unthrottle(event, self); | ||
315 | default: | ||
316 | self->unknown_events++; | ||
317 | return -1; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | void perf_event_header__bswap(struct perf_event_header *self) | ||
322 | { | ||
323 | self->type = bswap_32(self->type); | ||
324 | self->misc = bswap_16(self->misc); | ||
325 | self->size = bswap_16(self->size); | ||
326 | } | ||
327 | |||
328 | int perf_header__read_build_ids(struct perf_header *self, | ||
329 | int input, u64 offset, u64 size) | ||
330 | { | ||
331 | struct build_id_event bev; | ||
332 | char filename[PATH_MAX]; | ||
333 | u64 limit = offset + size; | ||
334 | int err = -1; | ||
335 | |||
336 | while (offset < limit) { | ||
337 | struct dso *dso; | ||
338 | ssize_t len; | ||
339 | struct list_head *head = &dsos__user; | ||
340 | |||
341 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
342 | goto out; | ||
343 | |||
344 | if (self->needs_swap) | ||
345 | perf_event_header__bswap(&bev.header); | ||
346 | |||
347 | len = bev.header.size - sizeof(bev); | ||
348 | if (read(input, filename, len) != len) | ||
349 | goto out; | ||
350 | |||
351 | if (bev.header.misc & PERF_RECORD_MISC_KERNEL) | ||
352 | head = &dsos__kernel; | ||
353 | |||
354 | dso = __dsos__findnew(head, filename); | ||
355 | if (dso != NULL) { | ||
356 | dso__set_build_id(dso, &bev.build_id); | ||
357 | if (head == &dsos__kernel && filename[0] == '[') | ||
358 | dso->kernel = 1; | ||
359 | } | ||
360 | |||
361 | offset += bev.header.size; | ||
362 | } | ||
363 | err = 0; | ||
364 | out: | ||
365 | return err; | ||
366 | } | ||
367 | |||
368 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | ||
369 | { | ||
370 | struct thread *thread = perf_session__findnew(self, 0); | ||
371 | |||
372 | if (thread == NULL || thread__set_comm(thread, "swapper")) { | ||
373 | pr_err("problem inserting idle task.\n"); | ||
374 | thread = NULL; | ||
375 | } | ||
376 | |||
377 | return thread; | ||
378 | } | ||
379 | |||
380 | int perf_session__process_events(struct perf_session *self, | ||
381 | struct perf_event_ops *ops) | ||
382 | { | ||
383 | int err, mmap_prot, mmap_flags; | ||
384 | u64 head, shift; | ||
385 | u64 offset = 0; | ||
386 | size_t page_size; | ||
387 | event_t *event; | ||
388 | uint32_t size; | ||
389 | char *buf; | ||
390 | |||
391 | if (perf_session__register_idle_thread(self) == NULL) | ||
392 | return -ENOMEM; | ||
393 | |||
394 | perf_event_ops__fill_defaults(ops); | ||
395 | |||
396 | page_size = sysconf(_SC_PAGESIZE); | ||
397 | |||
398 | head = self->header.data_offset; | ||
399 | |||
400 | if (!symbol_conf.full_paths) { | ||
401 | char bf[PATH_MAX]; | ||
402 | |||
403 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
404 | err = -errno; | ||
405 | out_getcwd_err: | ||
406 | pr_err("failed to get the current directory\n"); | ||
407 | goto out_err; | ||
408 | } | ||
409 | self->cwd = strdup(bf); | ||
410 | if (self->cwd == NULL) { | ||
411 | err = -ENOMEM; | ||
412 | goto out_getcwd_err; | ||
413 | } | ||
414 | self->cwdlen = strlen(self->cwd); | ||
415 | } | ||
416 | |||
417 | shift = page_size * (head / page_size); | ||
418 | offset += shift; | ||
419 | head -= shift; | ||
420 | |||
421 | mmap_prot = PROT_READ; | ||
422 | mmap_flags = MAP_SHARED; | ||
423 | |||
424 | if (self->header.needs_swap) { | ||
425 | mmap_prot |= PROT_WRITE; | ||
426 | mmap_flags = MAP_PRIVATE; | ||
427 | } | ||
428 | remap: | ||
429 | buf = mmap(NULL, page_size * self->mmap_window, mmap_prot, | ||
430 | mmap_flags, self->fd, offset); | ||
431 | if (buf == MAP_FAILED) { | ||
432 | pr_err("failed to mmap file\n"); | ||
433 | err = -errno; | ||
434 | goto out_err; | ||
435 | } | ||
436 | |||
437 | more: | ||
438 | event = (event_t *)(buf + head); | ||
439 | |||
440 | if (self->header.needs_swap) | ||
441 | perf_event_header__bswap(&event->header); | ||
442 | size = event->header.size; | ||
443 | if (size == 0) | ||
444 | size = 8; | ||
445 | |||
446 | if (head + event->header.size >= page_size * self->mmap_window) { | ||
447 | int munmap_ret; | ||
448 | |||
449 | shift = page_size * (head / page_size); | ||
450 | |||
451 | munmap_ret = munmap(buf, page_size * self->mmap_window); | ||
452 | assert(munmap_ret == 0); | ||
453 | |||
454 | offset += shift; | ||
455 | head -= shift; | ||
456 | goto remap; | ||
457 | } | ||
458 | |||
459 | size = event->header.size; | ||
460 | |||
461 | dump_printf("\n%#Lx [%#x]: event: %d\n", | ||
462 | offset + head, event->header.size, event->header.type); | ||
463 | |||
464 | if (size == 0 || | ||
465 | perf_session__process_event(self, event, ops, offset, head) < 0) { | ||
466 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | ||
467 | offset + head, event->header.size, | ||
468 | event->header.type); | ||
469 | /* | ||
470 | * assume we lost track of the stream, check alignment, and | ||
471 | * increment a single u64 in the hope to catch on again 'soon'. | ||
472 | */ | ||
473 | if (unlikely(head & 7)) | ||
474 | head &= ~7ULL; | ||
475 | |||
476 | size = 8; | ||
477 | } | ||
478 | |||
479 | head += size; | ||
480 | |||
481 | if (offset + head >= self->header.data_offset + self->header.data_size) | ||
482 | goto done; | ||
483 | |||
484 | if (offset + head < self->size) | ||
485 | goto more; | ||
486 | done: | ||
487 | err = 0; | ||
488 | out_err: | ||
489 | return err; | ||
490 | } | ||
491 | |||
492 | bool perf_session__has_traces(struct perf_session *self, const char *msg) | ||
493 | { | ||
494 | if (!(self->sample_type & PERF_SAMPLE_RAW)) { | ||
495 | pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); | ||
496 | return false; | ||
497 | } | ||
498 | |||
499 | return true; | ||
500 | } | ||
501 | |||
502 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
503 | const char *symbol_name, | ||
504 | u64 addr) | ||
505 | { | ||
506 | char *bracket; | ||
507 | |||
508 | self->ref_reloc_sym.name = strdup(symbol_name); | ||
509 | if (self->ref_reloc_sym.name == NULL) | ||
510 | return -ENOMEM; | ||
511 | |||
512 | bracket = strchr(self->ref_reloc_sym.name, ']'); | ||
513 | if (bracket) | ||
514 | *bracket = '\0'; | ||
515 | |||
516 | self->ref_reloc_sym.addr = addr; | ||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static u64 map__reloc_map_ip(struct map *map, u64 ip) | ||
521 | { | ||
522 | return ip + (s64)map->pgoff; | ||
523 | } | ||
524 | |||
525 | static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | ||
526 | { | ||
527 | return ip - (s64)map->pgoff; | ||
528 | } | ||
529 | |||
530 | void perf_session__reloc_vmlinux_maps(struct perf_session *self, | ||
531 | u64 unrelocated_addr) | ||
532 | { | ||
533 | enum map_type type; | ||
534 | s64 reloc = unrelocated_addr - self->ref_reloc_sym.addr; | ||
535 | |||
536 | if (!reloc) | ||
537 | return; | ||
538 | |||
539 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
540 | struct map *map = self->vmlinux_maps[type]; | ||
541 | |||
542 | map->map_ip = map__reloc_map_ip; | ||
543 | map->unmap_ip = map__reloc_unmap_ip; | ||
544 | map->pgoff = reloc; | ||
545 | } | ||
546 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 32eaa1bada06..36d1a80c0b6c 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -18,10 +18,16 @@ struct perf_session { | |||
18 | struct map_groups kmaps; | 18 | struct map_groups kmaps; |
19 | struct rb_root threads; | 19 | struct rb_root threads; |
20 | struct thread *last_match; | 20 | struct thread *last_match; |
21 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
21 | struct events_stats events_stats; | 22 | struct events_stats events_stats; |
22 | unsigned long event_total[PERF_RECORD_MAX]; | 23 | unsigned long event_total[PERF_RECORD_MAX]; |
24 | unsigned long unknown_events; | ||
23 | struct rb_root hists; | 25 | struct rb_root hists; |
24 | u64 sample_type; | 26 | u64 sample_type; |
27 | struct { | ||
28 | const char *name; | ||
29 | u64 addr; | ||
30 | } ref_reloc_sym; | ||
25 | int fd; | 31 | int fd; |
26 | int cwdlen; | 32 | int cwdlen; |
27 | char *cwd; | 33 | char *cwd; |
@@ -31,23 +37,22 @@ struct perf_session { | |||
31 | typedef int (*event_op)(event_t *self, struct perf_session *session); | 37 | typedef int (*event_op)(event_t *self, struct perf_session *session); |
32 | 38 | ||
33 | struct perf_event_ops { | 39 | struct perf_event_ops { |
34 | event_op process_sample_event; | 40 | event_op sample, |
35 | event_op process_mmap_event; | 41 | mmap, |
36 | event_op process_comm_event; | 42 | comm, |
37 | event_op process_fork_event; | 43 | fork, |
38 | event_op process_exit_event; | 44 | exit, |
39 | event_op process_lost_event; | 45 | lost, |
40 | event_op process_read_event; | 46 | read, |
41 | event_op process_throttle_event; | 47 | throttle, |
42 | event_op process_unthrottle_event; | 48 | unthrottle; |
43 | int (*sample_type_check)(struct perf_session *session); | ||
44 | unsigned long total_unknown; | ||
45 | bool full_paths; | ||
46 | }; | 49 | }; |
47 | 50 | ||
48 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); | 51 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); |
49 | void perf_session__delete(struct perf_session *self); | 52 | void perf_session__delete(struct perf_session *self); |
50 | 53 | ||
54 | void perf_event_header__bswap(struct perf_event_header *self); | ||
55 | |||
51 | int perf_session__process_events(struct perf_session *self, | 56 | int perf_session__process_events(struct perf_session *self, |
52 | struct perf_event_ops *event_ops); | 57 | struct perf_event_ops *event_ops); |
53 | 58 | ||
@@ -56,6 +61,17 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, | |||
56 | struct ip_callchain *chain, | 61 | struct ip_callchain *chain, |
57 | struct symbol **parent); | 62 | struct symbol **parent); |
58 | 63 | ||
59 | int perf_header__read_build_ids(int input, u64 offset, u64 file_size); | 64 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
65 | |||
66 | int perf_header__read_build_ids(struct perf_header *self, int input, | ||
67 | u64 offset, u64 file_size); | ||
68 | |||
69 | int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | ||
70 | const char *symbol_name, | ||
71 | u64 addr); | ||
72 | void perf_session__reloc_vmlinux_maps(struct perf_session *self, | ||
73 | u64 unrelocated_addr); | ||
74 | |||
75 | void mem_bswap_64(void *src, int byte_size); | ||
60 | 76 | ||
61 | #endif /* __PERF_SESSION_H */ | 77 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 5352d7dccc61..c397d4f6f748 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -227,16 +227,73 @@ fail: | |||
227 | return NULL; | 227 | return NULL; |
228 | } | 228 | } |
229 | 229 | ||
230 | /* Glob expression pattern matching */ | 230 | /* Character class matching */ |
231 | static bool __match_charclass(const char *pat, char c, const char **npat) | ||
232 | { | ||
233 | bool complement = false, ret = true; | ||
234 | |||
235 | if (*pat == '!') { | ||
236 | complement = true; | ||
237 | pat++; | ||
238 | } | ||
239 | if (*pat++ == c) /* First character is special */ | ||
240 | goto end; | ||
241 | |||
242 | while (*pat && *pat != ']') { /* Matching */ | ||
243 | if (*pat == '-' && *(pat + 1) != ']') { /* Range */ | ||
244 | if (*(pat - 1) <= c && c <= *(pat + 1)) | ||
245 | goto end; | ||
246 | if (*(pat - 1) > *(pat + 1)) | ||
247 | goto error; | ||
248 | pat += 2; | ||
249 | } else if (*pat++ == c) | ||
250 | goto end; | ||
251 | } | ||
252 | if (!*pat) | ||
253 | goto error; | ||
254 | ret = false; | ||
255 | |||
256 | end: | ||
257 | while (*pat && *pat != ']') /* Searching closing */ | ||
258 | pat++; | ||
259 | if (!*pat) | ||
260 | goto error; | ||
261 | *npat = pat + 1; | ||
262 | return complement ? !ret : ret; | ||
263 | |||
264 | error: | ||
265 | return false; | ||
266 | } | ||
267 | |||
268 | /** | ||
269 | * strglobmatch - glob expression pattern matching | ||
270 | * @str: the target string to match | ||
271 | * @pat: the pattern string to match | ||
272 | * | ||
273 | * This returns true if the @str matches @pat. @pat can includes wildcards | ||
274 | * ('*','?') and character classes ([CHARS], complementation and ranges are | ||
275 | * also supported). Also, this supports escape character ('\') to use special | ||
276 | * characters as normal character. | ||
277 | * | ||
278 | * Note: if @pat syntax is broken, this always returns false. | ||
279 | */ | ||
231 | bool strglobmatch(const char *str, const char *pat) | 280 | bool strglobmatch(const char *str, const char *pat) |
232 | { | 281 | { |
233 | while (*str && *pat && *pat != '*') { | 282 | while (*str && *pat && *pat != '*') { |
234 | if (*pat == '?') { | 283 | if (*pat == '?') { /* Matches any single character */ |
235 | str++; | 284 | str++; |
236 | pat++; | 285 | pat++; |
237 | } else | 286 | continue; |
238 | if (*str++ != *pat++) | 287 | } else if (*pat == '[') /* Character classes/Ranges */ |
288 | if (__match_charclass(pat + 1, *str, &pat)) { | ||
289 | str++; | ||
290 | continue; | ||
291 | } else | ||
239 | return false; | 292 | return false; |
293 | else if (*pat == '\\') /* Escaped char match as normal char */ | ||
294 | pat++; | ||
295 | if (*str++ != *pat++) | ||
296 | return false; | ||
240 | } | 297 | } |
241 | /* Check wild card */ | 298 | /* Check wild card */ |
242 | if (*pat == '*') { | 299 | if (*pat == '*') { |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index ab92763edb03..f9049d12ead6 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -22,6 +22,7 @@ | |||
22 | enum dso_origin { | 22 | enum dso_origin { |
23 | DSO__ORIG_KERNEL = 0, | 23 | DSO__ORIG_KERNEL = 0, |
24 | DSO__ORIG_JAVA_JIT, | 24 | DSO__ORIG_JAVA_JIT, |
25 | DSO__ORIG_BUILD_ID_CACHE, | ||
25 | DSO__ORIG_FEDORA, | 26 | DSO__ORIG_FEDORA, |
26 | DSO__ORIG_UBUNTU, | 27 | DSO__ORIG_UBUNTU, |
27 | DSO__ORIG_BUILDID, | 28 | DSO__ORIG_BUILDID, |
@@ -63,7 +64,7 @@ static void dso__set_sorted_by_name(struct dso *self, enum map_type type) | |||
63 | self->sorted_by_name |= (1 << type); | 64 | self->sorted_by_name |= (1 << type); |
64 | } | 65 | } |
65 | 66 | ||
66 | static bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 67 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
67 | { | 68 | { |
68 | switch (map_type) { | 69 | switch (map_type) { |
69 | case MAP__FUNCTION: | 70 | case MAP__FUNCTION: |
@@ -160,7 +161,7 @@ static size_t symbol__fprintf(struct symbol *self, FILE *fp) | |||
160 | self->start, self->end, self->name); | 161 | self->start, self->end, self->name); |
161 | } | 162 | } |
162 | 163 | ||
163 | static void dso__set_long_name(struct dso *self, char *name) | 164 | void dso__set_long_name(struct dso *self, char *name) |
164 | { | 165 | { |
165 | if (name == NULL) | 166 | if (name == NULL) |
166 | return; | 167 | return; |
@@ -175,7 +176,7 @@ static void dso__set_basename(struct dso *self) | |||
175 | 176 | ||
176 | struct dso *dso__new(const char *name) | 177 | struct dso *dso__new(const char *name) |
177 | { | 178 | { |
178 | struct dso *self = malloc(sizeof(*self) + strlen(name) + 1); | 179 | struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1); |
179 | 180 | ||
180 | if (self != NULL) { | 181 | if (self != NULL) { |
181 | int i; | 182 | int i; |
@@ -344,10 +345,10 @@ void dso__sort_by_name(struct dso *self, enum map_type type) | |||
344 | &self->symbols[type]); | 345 | &self->symbols[type]); |
345 | } | 346 | } |
346 | 347 | ||
347 | int build_id__sprintf(u8 *self, int len, char *bf) | 348 | int build_id__sprintf(const u8 *self, int len, char *bf) |
348 | { | 349 | { |
349 | char *bid = bf; | 350 | char *bid = bf; |
350 | u8 *raw = self; | 351 | const u8 *raw = self; |
351 | int i; | 352 | int i; |
352 | 353 | ||
353 | for (i = 0; i < len; ++i) { | 354 | for (i = 0; i < len; ++i) { |
@@ -382,24 +383,20 @@ size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp) | |||
382 | return ret; | 383 | return ret; |
383 | } | 384 | } |
384 | 385 | ||
385 | /* | 386 | int kallsyms__parse(const char *filename, void *arg, |
386 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, | 387 | int (*process_symbol)(void *arg, const char *name, |
387 | * so that we can in the next step set the symbol ->end address and then | 388 | char type, u64 start)) |
388 | * call kernel_maps__split_kallsyms. | ||
389 | */ | ||
390 | static int dso__load_all_kallsyms(struct dso *self, struct map *map) | ||
391 | { | 389 | { |
392 | char *line = NULL; | 390 | char *line = NULL; |
393 | size_t n; | 391 | size_t n; |
394 | struct rb_root *root = &self->symbols[map->type]; | 392 | int err = 0; |
395 | FILE *file = fopen("/proc/kallsyms", "r"); | 393 | FILE *file = fopen(filename, "r"); |
396 | 394 | ||
397 | if (file == NULL) | 395 | if (file == NULL) |
398 | goto out_failure; | 396 | goto out_failure; |
399 | 397 | ||
400 | while (!feof(file)) { | 398 | while (!feof(file)) { |
401 | u64 start; | 399 | u64 start; |
402 | struct symbol *sym; | ||
403 | int line_len, len; | 400 | int line_len, len; |
404 | char symbol_type; | 401 | char symbol_type; |
405 | char *symbol_name; | 402 | char *symbol_name; |
@@ -420,35 +417,63 @@ static int dso__load_all_kallsyms(struct dso *self, struct map *map) | |||
420 | continue; | 417 | continue; |
421 | 418 | ||
422 | symbol_type = toupper(line[len]); | 419 | symbol_type = toupper(line[len]); |
423 | if (!symbol_type__is_a(symbol_type, map->type)) | ||
424 | continue; | ||
425 | |||
426 | symbol_name = line + len + 2; | 420 | symbol_name = line + len + 2; |
427 | /* | ||
428 | * Will fix up the end later, when we have all symbols sorted. | ||
429 | */ | ||
430 | sym = symbol__new(start, 0, symbol_name); | ||
431 | 421 | ||
432 | if (sym == NULL) | 422 | err = process_symbol(arg, symbol_name, symbol_type, start); |
433 | goto out_delete_line; | 423 | if (err) |
434 | /* | 424 | break; |
435 | * We will pass the symbols to the filter later, in | ||
436 | * map__split_kallsyms, when we have split the maps per module | ||
437 | */ | ||
438 | symbols__insert(root, sym); | ||
439 | } | 425 | } |
440 | 426 | ||
441 | free(line); | 427 | free(line); |
442 | fclose(file); | 428 | fclose(file); |
429 | return err; | ||
443 | 430 | ||
444 | return 0; | ||
445 | |||
446 | out_delete_line: | ||
447 | free(line); | ||
448 | out_failure: | 431 | out_failure: |
449 | return -1; | 432 | return -1; |
450 | } | 433 | } |
451 | 434 | ||
435 | struct process_kallsyms_args { | ||
436 | struct map *map; | ||
437 | struct dso *dso; | ||
438 | }; | ||
439 | |||
440 | static int map__process_kallsym_symbol(void *arg, const char *name, | ||
441 | char type, u64 start) | ||
442 | { | ||
443 | struct symbol *sym; | ||
444 | struct process_kallsyms_args *a = arg; | ||
445 | struct rb_root *root = &a->dso->symbols[a->map->type]; | ||
446 | |||
447 | if (!symbol_type__is_a(type, a->map->type)) | ||
448 | return 0; | ||
449 | |||
450 | /* | ||
451 | * Will fix up the end later, when we have all symbols sorted. | ||
452 | */ | ||
453 | sym = symbol__new(start, 0, name); | ||
454 | |||
455 | if (sym == NULL) | ||
456 | return -ENOMEM; | ||
457 | /* | ||
458 | * We will pass the symbols to the filter later, in | ||
459 | * map__split_kallsyms, when we have split the maps per module | ||
460 | */ | ||
461 | symbols__insert(root, sym); | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | /* | ||
466 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, | ||
467 | * so that we can in the next step set the symbol ->end address and then | ||
468 | * call kernel_maps__split_kallsyms. | ||
469 | */ | ||
470 | static int dso__load_all_kallsyms(struct dso *self, const char *filename, | ||
471 | struct map *map) | ||
472 | { | ||
473 | struct process_kallsyms_args args = { .map = map, .dso = self, }; | ||
474 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | ||
475 | } | ||
476 | |||
452 | /* | 477 | /* |
453 | * Split the symbols into maps, making sure there are no overlaps, i.e. the | 478 | * Split the symbols into maps, making sure there are no overlaps, i.e. the |
454 | * kernel range is broken in several maps, named [kernel].N, as we don't have | 479 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
@@ -477,13 +502,17 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, | |||
477 | 502 | ||
478 | *module++ = '\0'; | 503 | *module++ = '\0'; |
479 | 504 | ||
480 | if (strcmp(self->name, module)) { | 505 | if (strcmp(curr_map->dso->short_name, module)) { |
481 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); | 506 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); |
482 | if (curr_map == NULL) { | 507 | if (curr_map == NULL) { |
483 | pr_debug("/proc/{kallsyms,modules} " | 508 | pr_debug("/proc/{kallsyms,modules} " |
484 | "inconsistency!\n"); | 509 | "inconsistency while looking " |
510 | "for \"%s\" module!\n", module); | ||
485 | return -1; | 511 | return -1; |
486 | } | 512 | } |
513 | |||
514 | if (curr_map->dso->loaded) | ||
515 | goto discard_symbol; | ||
487 | } | 516 | } |
488 | /* | 517 | /* |
489 | * So that we look just like we get from .ko files, | 518 | * So that we look just like we get from .ko files, |
@@ -529,10 +558,10 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
529 | } | 558 | } |
530 | 559 | ||
531 | 560 | ||
532 | static int dso__load_kallsyms(struct dso *self, struct map *map, | 561 | static int dso__load_kallsyms(struct dso *self, const char *filename, struct map *map, |
533 | struct perf_session *session, symbol_filter_t filter) | 562 | struct perf_session *session, symbol_filter_t filter) |
534 | { | 563 | { |
535 | if (dso__load_all_kallsyms(self, map) < 0) | 564 | if (dso__load_all_kallsyms(self, filename, map) < 0) |
536 | return -1; | 565 | return -1; |
537 | 566 | ||
538 | symbols__fixup_end(&self->symbols[map->type]); | 567 | symbols__fixup_end(&self->symbols[map->type]); |
@@ -933,11 +962,15 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
933 | 962 | ||
934 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | 963 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { |
935 | struct symbol *f; | 964 | struct symbol *f; |
936 | const char *elf_name; | 965 | const char *elf_name = elf_sym__name(&sym, symstrs); |
937 | char *demangled = NULL; | 966 | char *demangled = NULL; |
938 | int is_label = elf_sym__is_label(&sym); | 967 | int is_label = elf_sym__is_label(&sym); |
939 | const char *section_name; | 968 | const char *section_name; |
940 | 969 | ||
970 | if (kernel && session->ref_reloc_sym.name != NULL && | ||
971 | strcmp(elf_name, session->ref_reloc_sym.name) == 0) | ||
972 | perf_session__reloc_vmlinux_maps(session, sym.st_value); | ||
973 | |||
941 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | 974 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
942 | continue; | 975 | continue; |
943 | 976 | ||
@@ -950,7 +983,6 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
950 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) | 983 | if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type)) |
951 | continue; | 984 | continue; |
952 | 985 | ||
953 | elf_name = elf_sym__name(&sym, symstrs); | ||
954 | section_name = elf_sec__name(&shdr, secstrs); | 986 | section_name = elf_sec__name(&shdr, secstrs); |
955 | 987 | ||
956 | if (kernel || kmodule) { | 988 | if (kernel || kmodule) { |
@@ -1191,6 +1223,7 @@ char dso__symtab_origin(const struct dso *self) | |||
1191 | static const char origin[] = { | 1223 | static const char origin[] = { |
1192 | [DSO__ORIG_KERNEL] = 'k', | 1224 | [DSO__ORIG_KERNEL] = 'k', |
1193 | [DSO__ORIG_JAVA_JIT] = 'j', | 1225 | [DSO__ORIG_JAVA_JIT] = 'j', |
1226 | [DSO__ORIG_BUILD_ID_CACHE] = 'B', | ||
1194 | [DSO__ORIG_FEDORA] = 'f', | 1227 | [DSO__ORIG_FEDORA] = 'f', |
1195 | [DSO__ORIG_UBUNTU] = 'u', | 1228 | [DSO__ORIG_UBUNTU] = 'u', |
1196 | [DSO__ORIG_BUILDID] = 'b', | 1229 | [DSO__ORIG_BUILDID] = 'b', |
@@ -1209,6 +1242,7 @@ int dso__load(struct dso *self, struct map *map, struct perf_session *session, | |||
1209 | int size = PATH_MAX; | 1242 | int size = PATH_MAX; |
1210 | char *name; | 1243 | char *name; |
1211 | u8 build_id[BUILD_ID_SIZE]; | 1244 | u8 build_id[BUILD_ID_SIZE]; |
1245 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
1212 | int ret = -1; | 1246 | int ret = -1; |
1213 | int fd; | 1247 | int fd; |
1214 | 1248 | ||
@@ -1230,8 +1264,16 @@ int dso__load(struct dso *self, struct map *map, struct perf_session *session, | |||
1230 | return ret; | 1264 | return ret; |
1231 | } | 1265 | } |
1232 | 1266 | ||
1233 | self->origin = DSO__ORIG_FEDORA - 1; | 1267 | self->origin = DSO__ORIG_BUILD_ID_CACHE; |
1234 | 1268 | ||
1269 | if (self->has_build_id) { | ||
1270 | build_id__sprintf(self->build_id, sizeof(self->build_id), | ||
1271 | build_id_hex); | ||
1272 | snprintf(name, size, "%s/%s/.build-id/%.2s/%s", | ||
1273 | getenv("HOME"), DEBUG_CACHE_DIR, | ||
1274 | build_id_hex, build_id_hex + 2); | ||
1275 | goto open_file; | ||
1276 | } | ||
1235 | more: | 1277 | more: |
1236 | do { | 1278 | do { |
1237 | self->origin++; | 1279 | self->origin++; |
@@ -1247,8 +1289,6 @@ more: | |||
1247 | case DSO__ORIG_BUILDID: | 1289 | case DSO__ORIG_BUILDID: |
1248 | if (filename__read_build_id(self->long_name, build_id, | 1290 | if (filename__read_build_id(self->long_name, build_id, |
1249 | sizeof(build_id))) { | 1291 | sizeof(build_id))) { |
1250 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
1251 | |||
1252 | build_id__sprintf(build_id, sizeof(build_id), | 1292 | build_id__sprintf(build_id, sizeof(build_id), |
1253 | build_id_hex); | 1293 | build_id_hex); |
1254 | snprintf(name, size, | 1294 | snprintf(name, size, |
@@ -1276,7 +1316,7 @@ compare_build_id: | |||
1276 | if (!dso__build_id_equal(self, build_id)) | 1316 | if (!dso__build_id_equal(self, build_id)) |
1277 | goto more; | 1317 | goto more; |
1278 | } | 1318 | } |
1279 | 1319 | open_file: | |
1280 | fd = open(name, O_RDONLY); | 1320 | fd = open(name, O_RDONLY); |
1281 | } while (fd < 0); | 1321 | } while (fd < 0); |
1282 | 1322 | ||
@@ -1309,13 +1349,33 @@ struct map *map_groups__find_by_name(struct map_groups *self, | |||
1309 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | 1349 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { |
1310 | struct map *map = rb_entry(nd, struct map, rb_node); | 1350 | struct map *map = rb_entry(nd, struct map, rb_node); |
1311 | 1351 | ||
1312 | if (map->dso && strcmp(map->dso->name, name) == 0) | 1352 | if (map->dso && strcmp(map->dso->short_name, name) == 0) |
1313 | return map; | 1353 | return map; |
1314 | } | 1354 | } |
1315 | 1355 | ||
1316 | return NULL; | 1356 | return NULL; |
1317 | } | 1357 | } |
1318 | 1358 | ||
1359 | static int dso__kernel_module_get_build_id(struct dso *self) | ||
1360 | { | ||
1361 | char filename[PATH_MAX]; | ||
1362 | /* | ||
1363 | * kernel module short names are of the form "[module]" and | ||
1364 | * we need just "module" here. | ||
1365 | */ | ||
1366 | const char *name = self->short_name + 1; | ||
1367 | |||
1368 | snprintf(filename, sizeof(filename), | ||
1369 | "/sys/module/%.*s/notes/.note.gnu.build-id", | ||
1370 | (int)strlen(name - 1), name); | ||
1371 | |||
1372 | if (sysfs__read_build_id(filename, self->build_id, | ||
1373 | sizeof(self->build_id)) == 0) | ||
1374 | self->has_build_id = true; | ||
1375 | |||
1376 | return 0; | ||
1377 | } | ||
1378 | |||
1319 | static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) | 1379 | static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) |
1320 | { | 1380 | { |
1321 | struct dirent *dent; | 1381 | struct dirent *dent; |
@@ -1361,6 +1421,7 @@ static int perf_session__set_modules_path_dir(struct perf_session *self, char *d | |||
1361 | if (long_name == NULL) | 1421 | if (long_name == NULL) |
1362 | goto failure; | 1422 | goto failure; |
1363 | dso__set_long_name(map->dso, long_name); | 1423 | dso__set_long_name(map->dso, long_name); |
1424 | dso__kernel_module_get_build_id(map->dso); | ||
1364 | } | 1425 | } |
1365 | } | 1426 | } |
1366 | 1427 | ||
@@ -1403,6 +1464,24 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | |||
1403 | return self; | 1464 | return self; |
1404 | } | 1465 | } |
1405 | 1466 | ||
1467 | struct map *perf_session__new_module_map(struct perf_session *self, u64 start, | ||
1468 | const char *filename) | ||
1469 | { | ||
1470 | struct map *map; | ||
1471 | struct dso *dso = __dsos__findnew(&dsos__kernel, filename); | ||
1472 | |||
1473 | if (dso == NULL) | ||
1474 | return NULL; | ||
1475 | |||
1476 | map = map__new2(start, dso, MAP__FUNCTION); | ||
1477 | if (map == NULL) | ||
1478 | return NULL; | ||
1479 | |||
1480 | dso->origin = DSO__ORIG_KMODULE; | ||
1481 | map_groups__insert(&self->kmaps, map); | ||
1482 | return map; | ||
1483 | } | ||
1484 | |||
1406 | static int perf_session__create_module_maps(struct perf_session *self) | 1485 | static int perf_session__create_module_maps(struct perf_session *self) |
1407 | { | 1486 | { |
1408 | char *line = NULL; | 1487 | char *line = NULL; |
@@ -1416,7 +1495,6 @@ static int perf_session__create_module_maps(struct perf_session *self) | |||
1416 | while (!feof(file)) { | 1495 | while (!feof(file)) { |
1417 | char name[PATH_MAX]; | 1496 | char name[PATH_MAX]; |
1418 | u64 start; | 1497 | u64 start; |
1419 | struct dso *dso; | ||
1420 | char *sep; | 1498 | char *sep; |
1421 | int line_len; | 1499 | int line_len; |
1422 | 1500 | ||
@@ -1442,26 +1520,10 @@ static int perf_session__create_module_maps(struct perf_session *self) | |||
1442 | *sep = '\0'; | 1520 | *sep = '\0'; |
1443 | 1521 | ||
1444 | snprintf(name, sizeof(name), "[%s]", line); | 1522 | snprintf(name, sizeof(name), "[%s]", line); |
1445 | dso = dso__new(name); | 1523 | map = perf_session__new_module_map(self, start, name); |
1446 | 1524 | if (map == NULL) | |
1447 | if (dso == NULL) | ||
1448 | goto out_delete_line; | 1525 | goto out_delete_line; |
1449 | 1526 | dso__kernel_module_get_build_id(map->dso); | |
1450 | map = map__new2(start, dso, MAP__FUNCTION); | ||
1451 | if (map == NULL) { | ||
1452 | dso__delete(dso); | ||
1453 | goto out_delete_line; | ||
1454 | } | ||
1455 | |||
1456 | snprintf(name, sizeof(name), | ||
1457 | "/sys/module/%s/notes/.note.gnu.build-id", line); | ||
1458 | if (sysfs__read_build_id(name, dso->build_id, | ||
1459 | sizeof(dso->build_id)) == 0) | ||
1460 | dso->has_build_id = true; | ||
1461 | |||
1462 | dso->origin = DSO__ORIG_KMODULE; | ||
1463 | map_groups__insert(&self->kmaps, map); | ||
1464 | dsos__add(&dsos__kernel, dso); | ||
1465 | } | 1527 | } |
1466 | 1528 | ||
1467 | free(line); | 1529 | free(line); |
@@ -1510,51 +1572,117 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1510 | return -1; | 1572 | return -1; |
1511 | 1573 | ||
1512 | dso__set_loaded(self, map->type); | 1574 | dso__set_loaded(self, map->type); |
1513 | err = dso__load_sym(self, map, session, self->long_name, fd, filter, 1, 0); | 1575 | err = dso__load_sym(self, map, session, vmlinux, fd, filter, 1, 0); |
1514 | close(fd); | 1576 | close(fd); |
1515 | 1577 | ||
1516 | return err; | 1578 | return err; |
1517 | } | 1579 | } |
1518 | 1580 | ||
1581 | int dso__load_vmlinux_path(struct dso *self, struct map *map, | ||
1582 | struct perf_session *session, symbol_filter_t filter) | ||
1583 | { | ||
1584 | int i, err = 0; | ||
1585 | |||
1586 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | ||
1587 | vmlinux_path__nr_entries); | ||
1588 | |||
1589 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | ||
1590 | err = dso__load_vmlinux(self, map, session, vmlinux_path[i], | ||
1591 | filter); | ||
1592 | if (err > 0) { | ||
1593 | pr_debug("Using %s for symbols\n", vmlinux_path[i]); | ||
1594 | dso__set_long_name(self, strdup(vmlinux_path[i])); | ||
1595 | break; | ||
1596 | } | ||
1597 | } | ||
1598 | |||
1599 | return err; | ||
1600 | } | ||
1601 | |||
1519 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 1602 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
1520 | struct perf_session *session, symbol_filter_t filter) | 1603 | struct perf_session *session, symbol_filter_t filter) |
1521 | { | 1604 | { |
1522 | int err; | 1605 | int err; |
1523 | bool is_kallsyms; | 1606 | const char *kallsyms_filename = NULL; |
1607 | char *kallsyms_allocated_filename = NULL; | ||
1608 | /* | ||
1609 | * Step 1: if the user specified a vmlinux filename, use it and only | ||
1610 | * it, reporting errors to the user if it cannot be used. | ||
1611 | * | ||
1612 | * For instance, try to analyse an ARM perf.data file _without_ a | ||
1613 | * build-id, or if the user specifies the wrong path to the right | ||
1614 | * vmlinux file, obviously we can't fallback to another vmlinux (a | ||
1615 | * x86_86 one, on the machine where analysis is being performed, say), | ||
1616 | * or worse, /proc/kallsyms. | ||
1617 | * | ||
1618 | * If the specified file _has_ a build-id and there is a build-id | ||
1619 | * section in the perf.data file, we will still do the expected | ||
1620 | * validation in dso__load_vmlinux and will bail out if they don't | ||
1621 | * match. | ||
1622 | */ | ||
1623 | if (symbol_conf.vmlinux_name != NULL) { | ||
1624 | err = dso__load_vmlinux(self, map, session, | ||
1625 | symbol_conf.vmlinux_name, filter); | ||
1626 | goto out_try_fixup; | ||
1627 | } | ||
1524 | 1628 | ||
1525 | if (vmlinux_path != NULL) { | 1629 | if (vmlinux_path != NULL) { |
1526 | int i; | 1630 | err = dso__load_vmlinux_path(self, map, session, filter); |
1527 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | 1631 | if (err > 0) |
1528 | vmlinux_path__nr_entries); | 1632 | goto out_fixup; |
1529 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1633 | } |
1530 | err = dso__load_vmlinux(self, map, session, | 1634 | |
1531 | vmlinux_path[i], filter); | 1635 | /* |
1532 | if (err > 0) { | 1636 | * Say the kernel DSO was created when processing the build-id header table, |
1533 | pr_debug("Using %s for symbols\n", | 1637 | * we have a build-id, so check if it is the same as the running kernel, |
1534 | vmlinux_path[i]); | 1638 | * using it if it is. |
1535 | dso__set_long_name(self, | 1639 | */ |
1536 | strdup(vmlinux_path[i])); | 1640 | if (self->has_build_id) { |
1537 | goto out_fixup; | 1641 | u8 kallsyms_build_id[BUILD_ID_SIZE]; |
1642 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1643 | |||
1644 | if (sysfs__read_build_id("/sys/kernel/notes", kallsyms_build_id, | ||
1645 | sizeof(kallsyms_build_id)) == 0) { | ||
1646 | if (dso__build_id_equal(self, kallsyms_build_id)) { | ||
1647 | kallsyms_filename = "/proc/kallsyms"; | ||
1648 | goto do_kallsyms; | ||
1538 | } | 1649 | } |
1539 | } | 1650 | } |
1540 | } | 1651 | /* |
1652 | * Now look if we have it on the build-id cache in | ||
1653 | * $HOME/.debug/[kernel.kallsyms]. | ||
1654 | */ | ||
1655 | build_id__sprintf(self->build_id, sizeof(self->build_id), | ||
1656 | sbuild_id); | ||
1657 | |||
1658 | if (asprintf(&kallsyms_allocated_filename, | ||
1659 | "%s/.debug/[kernel.kallsyms]/%s", | ||
1660 | getenv("HOME"), sbuild_id) == -1) | ||
1661 | return -1; | ||
1541 | 1662 | ||
1542 | is_kallsyms = self->long_name[0] == '['; | 1663 | kallsyms_filename = kallsyms_allocated_filename; |
1543 | if (is_kallsyms) | ||
1544 | goto do_kallsyms; | ||
1545 | 1664 | ||
1546 | err = dso__load_vmlinux(self, map, session, self->long_name, filter); | 1665 | if (access(kallsyms_filename, F_OK)) { |
1547 | if (err <= 0) { | 1666 | free(kallsyms_allocated_filename); |
1548 | pr_info("The file %s cannot be used, " | 1667 | return -1; |
1549 | "trying to use /proc/kallsyms...", self->long_name); | 1668 | } |
1550 | do_kallsyms: | 1669 | } else { |
1551 | err = dso__load_kallsyms(self, map, session, filter); | 1670 | /* |
1552 | if (err > 0 && !is_kallsyms) | 1671 | * Last resort, if we don't have a build-id and couldn't find |
1553 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | 1672 | * any vmlinux file, try the running kernel kallsyms table. |
1673 | */ | ||
1674 | kallsyms_filename = "/proc/kallsyms"; | ||
1554 | } | 1675 | } |
1555 | 1676 | ||
1677 | do_kallsyms: | ||
1678 | err = dso__load_kallsyms(self, kallsyms_filename, map, session, filter); | ||
1679 | free(kallsyms_allocated_filename); | ||
1680 | |||
1681 | out_try_fixup: | ||
1556 | if (err > 0) { | 1682 | if (err > 0) { |
1557 | out_fixup: | 1683 | out_fixup: |
1684 | if (kallsyms_filename != NULL) | ||
1685 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | ||
1558 | map__fixup_start(map); | 1686 | map__fixup_start(map); |
1559 | map__fixup_end(map); | 1687 | map__fixup_end(map); |
1560 | } | 1688 | } |
@@ -1576,19 +1704,19 @@ static struct dso *dsos__find(struct list_head *head, const char *name) | |||
1576 | struct dso *pos; | 1704 | struct dso *pos; |
1577 | 1705 | ||
1578 | list_for_each_entry(pos, head, node) | 1706 | list_for_each_entry(pos, head, node) |
1579 | if (strcmp(pos->name, name) == 0) | 1707 | if (strcmp(pos->long_name, name) == 0) |
1580 | return pos; | 1708 | return pos; |
1581 | return NULL; | 1709 | return NULL; |
1582 | } | 1710 | } |
1583 | 1711 | ||
1584 | struct dso *dsos__findnew(const char *name) | 1712 | struct dso *__dsos__findnew(struct list_head *head, const char *name) |
1585 | { | 1713 | { |
1586 | struct dso *dso = dsos__find(&dsos__user, name); | 1714 | struct dso *dso = dsos__find(head, name); |
1587 | 1715 | ||
1588 | if (!dso) { | 1716 | if (!dso) { |
1589 | dso = dso__new(name); | 1717 | dso = dso__new(name); |
1590 | if (dso != NULL) { | 1718 | if (dso != NULL) { |
1591 | dsos__add(&dsos__user, dso); | 1719 | dsos__add(head, dso); |
1592 | dso__set_basename(dso); | 1720 | dso__set_basename(dso); |
1593 | } | 1721 | } |
1594 | } | 1722 | } |
@@ -1613,42 +1741,59 @@ void dsos__fprintf(FILE *fp) | |||
1613 | __dsos__fprintf(&dsos__user, fp); | 1741 | __dsos__fprintf(&dsos__user, fp); |
1614 | } | 1742 | } |
1615 | 1743 | ||
1616 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp) | 1744 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
1745 | bool with_hits) | ||
1617 | { | 1746 | { |
1618 | struct dso *pos; | 1747 | struct dso *pos; |
1619 | size_t ret = 0; | 1748 | size_t ret = 0; |
1620 | 1749 | ||
1621 | list_for_each_entry(pos, head, node) { | 1750 | list_for_each_entry(pos, head, node) { |
1751 | if (with_hits && !pos->hit) | ||
1752 | continue; | ||
1622 | ret += dso__fprintf_buildid(pos, fp); | 1753 | ret += dso__fprintf_buildid(pos, fp); |
1623 | ret += fprintf(fp, " %s\n", pos->long_name); | 1754 | ret += fprintf(fp, " %s\n", pos->long_name); |
1624 | } | 1755 | } |
1625 | return ret; | 1756 | return ret; |
1626 | } | 1757 | } |
1627 | 1758 | ||
1628 | size_t dsos__fprintf_buildid(FILE *fp) | 1759 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits) |
1629 | { | 1760 | { |
1630 | return (__dsos__fprintf_buildid(&dsos__kernel, fp) + | 1761 | return (__dsos__fprintf_buildid(&dsos__kernel, fp, with_hits) + |
1631 | __dsos__fprintf_buildid(&dsos__user, fp)); | 1762 | __dsos__fprintf_buildid(&dsos__user, fp, with_hits)); |
1632 | } | 1763 | } |
1633 | 1764 | ||
1634 | static struct dso *dsos__create_kernel( const char *vmlinux) | 1765 | struct dso *dso__new_kernel(const char *name) |
1635 | { | 1766 | { |
1636 | struct dso *kernel = dso__new(vmlinux ?: "[kernel.kallsyms]"); | 1767 | struct dso *self = dso__new(name ?: "[kernel.kallsyms]"); |
1768 | |||
1769 | if (self != NULL) { | ||
1770 | self->short_name = "[kernel]"; | ||
1771 | self->kernel = 1; | ||
1772 | } | ||
1773 | |||
1774 | return self; | ||
1775 | } | ||
1776 | |||
1777 | void dso__read_running_kernel_build_id(struct dso *self) | ||
1778 | { | ||
1779 | if (sysfs__read_build_id("/sys/kernel/notes", self->build_id, | ||
1780 | sizeof(self->build_id)) == 0) | ||
1781 | self->has_build_id = true; | ||
1782 | } | ||
1783 | |||
1784 | static struct dso *dsos__create_kernel(const char *vmlinux) | ||
1785 | { | ||
1786 | struct dso *kernel = dso__new_kernel(vmlinux); | ||
1637 | 1787 | ||
1638 | if (kernel == NULL) | 1788 | if (kernel == NULL) |
1639 | return NULL; | 1789 | return NULL; |
1640 | 1790 | ||
1641 | kernel->short_name = "[kernel]"; | ||
1642 | kernel->kernel = 1; | ||
1643 | |||
1644 | vdso = dso__new("[vdso]"); | 1791 | vdso = dso__new("[vdso]"); |
1645 | if (vdso == NULL) | 1792 | if (vdso == NULL) |
1646 | goto out_delete_kernel_dso; | 1793 | goto out_delete_kernel_dso; |
1647 | dso__set_loaded(vdso, MAP__FUNCTION); | 1794 | dso__set_loaded(vdso, MAP__FUNCTION); |
1648 | 1795 | ||
1649 | if (sysfs__read_build_id("/sys/kernel/notes", kernel->build_id, | 1796 | dso__read_running_kernel_build_id(kernel); |
1650 | sizeof(kernel->build_id)) == 0) | ||
1651 | kernel->has_build_id = true; | ||
1652 | 1797 | ||
1653 | dsos__add(&dsos__kernel, kernel); | 1798 | dsos__add(&dsos__kernel, kernel); |
1654 | dsos__add(&dsos__user, vdso); | 1799 | dsos__add(&dsos__user, vdso); |
@@ -1660,32 +1805,37 @@ out_delete_kernel_dso: | |||
1660 | return NULL; | 1805 | return NULL; |
1661 | } | 1806 | } |
1662 | 1807 | ||
1663 | static int map_groups__create_kernel_maps(struct map_groups *self, const char *vmlinux) | 1808 | int __map_groups__create_kernel_maps(struct map_groups *self, |
1809 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
1810 | struct dso *kernel) | ||
1664 | { | 1811 | { |
1665 | struct map *functions, *variables; | 1812 | enum map_type type; |
1666 | struct dso *kernel = dsos__create_kernel(vmlinux); | ||
1667 | |||
1668 | if (kernel == NULL) | ||
1669 | return -1; | ||
1670 | 1813 | ||
1671 | functions = map__new2(0, kernel, MAP__FUNCTION); | 1814 | for (type = 0; type < MAP__NR_TYPES; ++type) { |
1672 | if (functions == NULL) | 1815 | vmlinux_maps[type] = map__new2(0, kernel, type); |
1673 | return -1; | 1816 | if (vmlinux_maps[type] == NULL) |
1817 | return -1; | ||
1674 | 1818 | ||
1675 | variables = map__new2(0, kernel, MAP__VARIABLE); | 1819 | vmlinux_maps[type]->map_ip = |
1676 | if (variables == NULL) { | 1820 | vmlinux_maps[type]->unmap_ip = identity__map_ip; |
1677 | map__delete(functions); | 1821 | map_groups__insert(self, vmlinux_maps[type]); |
1678 | return -1; | ||
1679 | } | 1822 | } |
1680 | 1823 | ||
1681 | functions->map_ip = functions->unmap_ip = | ||
1682 | variables->map_ip = variables->unmap_ip = identity__map_ip; | ||
1683 | map_groups__insert(self, functions); | ||
1684 | map_groups__insert(self, variables); | ||
1685 | |||
1686 | return 0; | 1824 | return 0; |
1687 | } | 1825 | } |
1688 | 1826 | ||
1827 | static int map_groups__create_kernel_maps(struct map_groups *self, | ||
1828 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
1829 | const char *vmlinux) | ||
1830 | { | ||
1831 | struct dso *kernel = dsos__create_kernel(vmlinux); | ||
1832 | |||
1833 | if (kernel == NULL) | ||
1834 | return -1; | ||
1835 | |||
1836 | return __map_groups__create_kernel_maps(self, vmlinux_maps, kernel); | ||
1837 | } | ||
1838 | |||
1689 | static void vmlinux_path__exit(void) | 1839 | static void vmlinux_path__exit(void) |
1690 | { | 1840 | { |
1691 | while (--vmlinux_path__nr_entries >= 0) { | 1841 | while (--vmlinux_path__nr_entries >= 0) { |
@@ -1793,7 +1943,7 @@ out_free_comm_list: | |||
1793 | 1943 | ||
1794 | int perf_session__create_kernel_maps(struct perf_session *self) | 1944 | int perf_session__create_kernel_maps(struct perf_session *self) |
1795 | { | 1945 | { |
1796 | if (map_groups__create_kernel_maps(&self->kmaps, | 1946 | if (map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps, |
1797 | symbol_conf.vmlinux_name) < 0) | 1947 | symbol_conf.vmlinux_name) < 0) |
1798 | return -1; | 1948 | return -1; |
1799 | 1949 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 8aded2356f79..124302778c09 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -8,6 +8,8 @@ | |||
8 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
9 | #include "event.h" | 9 | #include "event.h" |
10 | 10 | ||
11 | #define DEBUG_CACHE_DIR ".debug" | ||
12 | |||
11 | #ifdef HAVE_CPLUS_DEMANGLE | 13 | #ifdef HAVE_CPLUS_DEMANGLE |
12 | extern char *cplus_demangle(const char *, int); | 14 | extern char *cplus_demangle(const char *, int); |
13 | 15 | ||
@@ -58,7 +60,8 @@ struct symbol_conf { | |||
58 | sort_by_name, | 60 | sort_by_name, |
59 | show_nr_samples, | 61 | show_nr_samples, |
60 | use_callchain, | 62 | use_callchain, |
61 | exclude_other; | 63 | exclude_other, |
64 | full_paths; | ||
62 | const char *vmlinux_name, | 65 | const char *vmlinux_name, |
63 | *field_sep; | 66 | *field_sep; |
64 | char *dso_list_str, | 67 | char *dso_list_str, |
@@ -94,6 +97,7 @@ struct dso { | |||
94 | u8 slen_calculated:1; | 97 | u8 slen_calculated:1; |
95 | u8 has_build_id:1; | 98 | u8 has_build_id:1; |
96 | u8 kernel:1; | 99 | u8 kernel:1; |
100 | u8 hit:1; | ||
97 | unsigned char origin; | 101 | unsigned char origin; |
98 | u8 sorted_by_name; | 102 | u8 sorted_by_name; |
99 | u8 loaded; | 103 | u8 loaded; |
@@ -105,6 +109,7 @@ struct dso { | |||
105 | }; | 109 | }; |
106 | 110 | ||
107 | struct dso *dso__new(const char *name); | 111 | struct dso *dso__new(const char *name); |
112 | struct dso *dso__new_kernel(const char *name); | ||
108 | void dso__delete(struct dso *self); | 113 | void dso__delete(struct dso *self); |
109 | 114 | ||
110 | bool dso__loaded(const struct dso *self, enum map_type type); | 115 | bool dso__loaded(const struct dso *self, enum map_type type); |
@@ -112,18 +117,30 @@ bool dso__sorted_by_name(const struct dso *self, enum map_type type); | |||
112 | 117 | ||
113 | void dso__sort_by_name(struct dso *self, enum map_type type); | 118 | void dso__sort_by_name(struct dso *self, enum map_type type); |
114 | 119 | ||
120 | extern struct list_head dsos__user, dsos__kernel; | ||
121 | |||
122 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | ||
123 | |||
124 | static inline struct dso *dsos__findnew(const char *name) | ||
125 | { | ||
126 | return __dsos__findnew(&dsos__user, name); | ||
127 | } | ||
128 | |||
115 | struct perf_session; | 129 | struct perf_session; |
116 | 130 | ||
117 | struct dso *dsos__findnew(const char *name); | ||
118 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, | 131 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, |
119 | symbol_filter_t filter); | 132 | symbol_filter_t filter); |
133 | int dso__load_vmlinux_path(struct dso *self, struct map *map, | ||
134 | struct perf_session *session, symbol_filter_t filter); | ||
120 | void dsos__fprintf(FILE *fp); | 135 | void dsos__fprintf(FILE *fp); |
121 | size_t dsos__fprintf_buildid(FILE *fp); | 136 | size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); |
122 | 137 | ||
123 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | 138 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); |
124 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | 139 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
125 | char dso__symtab_origin(const struct dso *self); | 140 | char dso__symtab_origin(const struct dso *self); |
141 | void dso__set_long_name(struct dso *self, char *name); | ||
126 | void dso__set_build_id(struct dso *self, void *build_id); | 142 | void dso__set_build_id(struct dso *self, void *build_id); |
143 | void dso__read_running_kernel_build_id(struct dso *self); | ||
127 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); | 144 | struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr); |
128 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | 145 | struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, |
129 | const char *name); | 146 | const char *name); |
@@ -131,11 +148,17 @@ struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type, | |||
131 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 148 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
132 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | 149 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); |
133 | bool dsos__read_build_ids(void); | 150 | bool dsos__read_build_ids(void); |
134 | int build_id__sprintf(u8 *self, int len, char *bf); | 151 | int build_id__sprintf(const u8 *self, int len, char *bf); |
152 | int kallsyms__parse(const char *filename, void *arg, | ||
153 | int (*process_symbol)(void *arg, const char *name, | ||
154 | char type, u64 start)); | ||
135 | 155 | ||
136 | int symbol__init(void); | 156 | int symbol__init(void); |
157 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | ||
158 | |||
137 | int perf_session__create_kernel_maps(struct perf_session *self); | 159 | int perf_session__create_kernel_maps(struct perf_session *self); |
138 | 160 | ||
139 | extern struct list_head dsos__user, dsos__kernel; | 161 | struct map *perf_session__new_module_map(struct perf_session *self, u64 start, |
162 | const char *filename); | ||
140 | extern struct dso *vdso; | 163 | extern struct dso *vdso; |
141 | #endif /* __PERF_SYMBOL */ | 164 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index c206f72c8881..e35653c1817c 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -48,6 +48,11 @@ static inline struct map *thread__find_map(struct thread *self, | |||
48 | return self ? map_groups__find(&self->mg, type, addr) : NULL; | 48 | return self ? map_groups__find(&self->mg, type, addr) : NULL; |
49 | } | 49 | } |
50 | 50 | ||
51 | void thread__find_addr_map(struct thread *self, | ||
52 | struct perf_session *session, u8 cpumode, | ||
53 | enum map_type type, u64 addr, | ||
54 | struct addr_location *al); | ||
55 | |||
51 | void thread__find_addr_location(struct thread *self, | 56 | void thread__find_addr_location(struct thread *self, |
52 | struct perf_session *session, u8 cpumode, | 57 | struct perf_session *session, u8 cpumode, |
53 | enum map_type type, u64 addr, | 58 | enum map_type type, u64 addr, |
@@ -67,4 +72,8 @@ map_groups__find_function(struct map_groups *self, struct perf_session *session, | |||
67 | 72 | ||
68 | struct map *map_groups__find_by_name(struct map_groups *self, | 73 | struct map *map_groups__find_by_name(struct map_groups *self, |
69 | enum map_type type, const char *name); | 74 | enum map_type type, const char *name); |
75 | |||
76 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
77 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
78 | struct dso *kernel); | ||
70 | #endif /* __PERF_THREAD_H */ | 79 | #endif /* __PERF_THREAD_H */ |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index cace35595530..5ea8973ad331 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -20,6 +20,7 @@ | |||
20 | */ | 20 | */ |
21 | #define _GNU_SOURCE | 21 | #define _GNU_SOURCE |
22 | #include <dirent.h> | 22 | #include <dirent.h> |
23 | #include <mntent.h> | ||
23 | #include <stdio.h> | 24 | #include <stdio.h> |
24 | #include <stdlib.h> | 25 | #include <stdlib.h> |
25 | #include <string.h> | 26 | #include <string.h> |
@@ -37,6 +38,7 @@ | |||
37 | 38 | ||
38 | #include "../perf.h" | 39 | #include "../perf.h" |
39 | #include "trace-event.h" | 40 | #include "trace-event.h" |
41 | #include "debugfs.h" | ||
40 | 42 | ||
41 | #define VERSION "0.5" | 43 | #define VERSION "0.5" |
42 | 44 | ||
@@ -101,32 +103,12 @@ void *malloc_or_die(unsigned int size) | |||
101 | 103 | ||
102 | static const char *find_debugfs(void) | 104 | static const char *find_debugfs(void) |
103 | { | 105 | { |
104 | static char debugfs[MAX_PATH+1]; | 106 | const char *path = debugfs_mount(NULL); |
105 | static int debugfs_found; | ||
106 | char type[100]; | ||
107 | FILE *fp; | ||
108 | |||
109 | if (debugfs_found) | ||
110 | return debugfs; | ||
111 | |||
112 | if ((fp = fopen("/proc/mounts","r")) == NULL) | ||
113 | die("Can't open /proc/mounts for read"); | ||
114 | |||
115 | while (fscanf(fp, "%*s %" | ||
116 | STR(MAX_PATH) | ||
117 | "s %99s %*s %*d %*d\n", | ||
118 | debugfs, type) == 2) { | ||
119 | if (strcmp(type, "debugfs") == 0) | ||
120 | break; | ||
121 | } | ||
122 | fclose(fp); | ||
123 | |||
124 | if (strcmp(type, "debugfs") != 0) | ||
125 | die("debugfs not mounted, please mount"); | ||
126 | 107 | ||
127 | debugfs_found = 1; | 108 | if (!path) |
109 | die("Your kernel not support debugfs filesystem"); | ||
128 | 110 | ||
129 | return debugfs; | 111 | return path; |
130 | } | 112 | } |
131 | 113 | ||
132 | /* | 114 | /* |
@@ -271,6 +253,8 @@ static void read_header_files(void) | |||
271 | write_or_die("header_page", 12); | 253 | write_or_die("header_page", 12); |
272 | write_or_die(&size, 8); | 254 | write_or_die(&size, 8); |
273 | check_size = copy_file_fd(fd); | 255 | check_size = copy_file_fd(fd); |
256 | close(fd); | ||
257 | |||
274 | if (size != check_size) | 258 | if (size != check_size) |
275 | die("wrong size for '%s' size=%lld read=%lld", | 259 | die("wrong size for '%s' size=%lld read=%lld", |
276 | path, size, check_size); | 260 | path, size, check_size); |
@@ -289,6 +273,7 @@ static void read_header_files(void) | |||
289 | if (size != check_size) | 273 | if (size != check_size) |
290 | die("wrong size for '%s'", path); | 274 | die("wrong size for '%s'", path); |
291 | put_tracing_file(path); | 275 | put_tracing_file(path); |
276 | close(fd); | ||
292 | } | 277 | } |
293 | 278 | ||
294 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 279 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -317,7 +302,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
317 | die("can't read directory '%s'", sys); | 302 | die("can't read directory '%s'", sys); |
318 | 303 | ||
319 | while ((dent = readdir(dir))) { | 304 | while ((dent = readdir(dir))) { |
320 | if (strcmp(dent->d_name, ".") == 0 || | 305 | if (dent->d_type != DT_DIR || |
306 | strcmp(dent->d_name, ".") == 0 || | ||
321 | strcmp(dent->d_name, "..") == 0 || | 307 | strcmp(dent->d_name, "..") == 0 || |
322 | !name_in_tp_list(dent->d_name, tps)) | 308 | !name_in_tp_list(dent->d_name, tps)) |
323 | continue; | 309 | continue; |
@@ -334,7 +320,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
334 | 320 | ||
335 | rewinddir(dir); | 321 | rewinddir(dir); |
336 | while ((dent = readdir(dir))) { | 322 | while ((dent = readdir(dir))) { |
337 | if (strcmp(dent->d_name, ".") == 0 || | 323 | if (dent->d_type != DT_DIR || |
324 | strcmp(dent->d_name, ".") == 0 || | ||
338 | strcmp(dent->d_name, "..") == 0 || | 325 | strcmp(dent->d_name, "..") == 0 || |
339 | !name_in_tp_list(dent->d_name, tps)) | 326 | !name_in_tp_list(dent->d_name, tps)) |
340 | continue; | 327 | continue; |
@@ -353,6 +340,7 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
353 | 340 | ||
354 | free(format); | 341 | free(format); |
355 | } | 342 | } |
343 | closedir(dir); | ||
356 | } | 344 | } |
357 | 345 | ||
358 | static void read_ftrace_files(struct tracepoint_path *tps) | 346 | static void read_ftrace_files(struct tracepoint_path *tps) |
@@ -394,26 +382,21 @@ static void read_event_files(struct tracepoint_path *tps) | |||
394 | die("can't read directory '%s'", path); | 382 | die("can't read directory '%s'", path); |
395 | 383 | ||
396 | while ((dent = readdir(dir))) { | 384 | while ((dent = readdir(dir))) { |
397 | if (strcmp(dent->d_name, ".") == 0 || | 385 | if (dent->d_type != DT_DIR || |
386 | strcmp(dent->d_name, ".") == 0 || | ||
398 | strcmp(dent->d_name, "..") == 0 || | 387 | strcmp(dent->d_name, "..") == 0 || |
399 | strcmp(dent->d_name, "ftrace") == 0 || | 388 | strcmp(dent->d_name, "ftrace") == 0 || |
400 | !system_in_tp_list(dent->d_name, tps)) | 389 | !system_in_tp_list(dent->d_name, tps)) |
401 | continue; | 390 | continue; |
402 | sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2); | 391 | count++; |
403 | sprintf(sys, "%s/%s", path, dent->d_name); | ||
404 | ret = stat(sys, &st); | ||
405 | free(sys); | ||
406 | if (ret < 0) | ||
407 | continue; | ||
408 | if (S_ISDIR(st.st_mode)) | ||
409 | count++; | ||
410 | } | 392 | } |
411 | 393 | ||
412 | write_or_die(&count, 4); | 394 | write_or_die(&count, 4); |
413 | 395 | ||
414 | rewinddir(dir); | 396 | rewinddir(dir); |
415 | while ((dent = readdir(dir))) { | 397 | while ((dent = readdir(dir))) { |
416 | if (strcmp(dent->d_name, ".") == 0 || | 398 | if (dent->d_type != DT_DIR || |
399 | strcmp(dent->d_name, ".") == 0 || | ||
417 | strcmp(dent->d_name, "..") == 0 || | 400 | strcmp(dent->d_name, "..") == 0 || |
418 | strcmp(dent->d_name, "ftrace") == 0 || | 401 | strcmp(dent->d_name, "ftrace") == 0 || |
419 | !system_in_tp_list(dent->d_name, tps)) | 402 | !system_in_tp_list(dent->d_name, tps)) |
@@ -422,14 +405,13 @@ static void read_event_files(struct tracepoint_path *tps) | |||
422 | sprintf(sys, "%s/%s", path, dent->d_name); | 405 | sprintf(sys, "%s/%s", path, dent->d_name); |
423 | ret = stat(sys, &st); | 406 | ret = stat(sys, &st); |
424 | if (ret >= 0) { | 407 | if (ret >= 0) { |
425 | if (S_ISDIR(st.st_mode)) { | 408 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); |
426 | write_or_die(dent->d_name, strlen(dent->d_name) + 1); | 409 | copy_event_system(sys, tps); |
427 | copy_event_system(sys, tps); | ||
428 | } | ||
429 | } | 410 | } |
430 | free(sys); | 411 | free(sys); |
431 | } | 412 | } |
432 | 413 | ||
414 | closedir(dir); | ||
433 | put_tracing_file(path); | 415 | put_tracing_file(path); |
434 | } | 416 | } |
435 | 417 | ||
@@ -533,7 +515,7 @@ int read_tracing_data(int fd, struct perf_event_attr *pattrs, int nb_events) | |||
533 | write_or_die(buf, 1); | 515 | write_or_die(buf, 1); |
534 | 516 | ||
535 | /* save page_size */ | 517 | /* save page_size */ |
536 | page_size = getpagesize(); | 518 | page_size = sysconf(_SC_PAGESIZE); |
537 | write_or_die(&page_size, 4); | 519 | write_or_die(&page_size, 4); |
538 | 520 | ||
539 | read_header_files(); | 521 | read_header_files(); |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c new file mode 100644 index 000000000000..f9b890fde681 --- /dev/null +++ b/tools/perf/util/util.c | |||
@@ -0,0 +1,94 @@ | |||
1 | #include "util.h" | ||
2 | #include <sys/mman.h> | ||
3 | |||
4 | int mkdir_p(char *path, mode_t mode) | ||
5 | { | ||
6 | struct stat st; | ||
7 | int err; | ||
8 | char *d = path; | ||
9 | |||
10 | if (*d != '/') | ||
11 | return -1; | ||
12 | |||
13 | if (stat(path, &st) == 0) | ||
14 | return 0; | ||
15 | |||
16 | while (*++d == '/'); | ||
17 | |||
18 | while ((d = strchr(d, '/'))) { | ||
19 | *d = '\0'; | ||
20 | err = stat(path, &st) && mkdir(path, mode); | ||
21 | *d++ = '/'; | ||
22 | if (err) | ||
23 | return -1; | ||
24 | while (*d == '/') | ||
25 | ++d; | ||
26 | } | ||
27 | return (stat(path, &st) && mkdir(path, mode)) ? -1 : 0; | ||
28 | } | ||
29 | |||
30 | static int slow_copyfile(const char *from, const char *to) | ||
31 | { | ||
32 | int err = 0; | ||
33 | char *line = NULL; | ||
34 | size_t n; | ||
35 | FILE *from_fp = fopen(from, "r"), *to_fp; | ||
36 | |||
37 | if (from_fp == NULL) | ||
38 | goto out; | ||
39 | |||
40 | to_fp = fopen(to, "w"); | ||
41 | if (to_fp == NULL) | ||
42 | goto out_fclose_from; | ||
43 | |||
44 | while (getline(&line, &n, from_fp) > 0) | ||
45 | if (fputs(line, to_fp) == EOF) | ||
46 | goto out_fclose_to; | ||
47 | err = 0; | ||
48 | out_fclose_to: | ||
49 | fclose(to_fp); | ||
50 | free(line); | ||
51 | out_fclose_from: | ||
52 | fclose(from_fp); | ||
53 | out: | ||
54 | return err; | ||
55 | } | ||
56 | |||
57 | int copyfile(const char *from, const char *to) | ||
58 | { | ||
59 | int fromfd, tofd; | ||
60 | struct stat st; | ||
61 | void *addr; | ||
62 | int err = -1; | ||
63 | |||
64 | if (stat(from, &st)) | ||
65 | goto out; | ||
66 | |||
67 | if (st.st_size == 0) /* /proc? do it slowly... */ | ||
68 | return slow_copyfile(from, to); | ||
69 | |||
70 | fromfd = open(from, O_RDONLY); | ||
71 | if (fromfd < 0) | ||
72 | goto out; | ||
73 | |||
74 | tofd = creat(to, 0755); | ||
75 | if (tofd < 0) | ||
76 | goto out_close_from; | ||
77 | |||
78 | addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fromfd, 0); | ||
79 | if (addr == MAP_FAILED) | ||
80 | goto out_close_to; | ||
81 | |||
82 | if (write(tofd, addr, st.st_size) == st.st_size) | ||
83 | err = 0; | ||
84 | |||
85 | munmap(addr, st.st_size); | ||
86 | out_close_to: | ||
87 | close(tofd); | ||
88 | if (err) | ||
89 | unlink(to); | ||
90 | out_close_from: | ||
91 | close(fromfd); | ||
92 | out: | ||
93 | return err; | ||
94 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c673d8825883..0f5b2a6f1080 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -403,4 +403,7 @@ void git_qsort(void *base, size_t nmemb, size_t size, | |||
403 | #endif | 403 | #endif |
404 | #endif | 404 | #endif |
405 | 405 | ||
406 | int mkdir_p(char *path, mode_t mode); | ||
407 | int copyfile(const char *from, const char *to); | ||
408 | |||
406 | #endif | 409 | #endif |
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 1c15e39f99e3..cfa55d686e3b 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c | |||
@@ -169,6 +169,7 @@ static void perf_read_values__display_pretty(FILE *fp, | |||
169 | counterwidth[j], values->value[i][j]); | 169 | counterwidth[j], values->value[i][j]); |
170 | fprintf(fp, "\n"); | 170 | fprintf(fp, "\n"); |
171 | } | 171 | } |
172 | free(counterwidth); | ||
172 | } | 173 | } |
173 | 174 | ||
174 | static void perf_read_values__display_raw(FILE *fp, | 175 | static void perf_read_values__display_raw(FILE *fp, |