diff options
53 files changed, 2293 insertions, 1413 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index da7bdc23f279..c66b34f75eea 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
| @@ -211,17 +211,11 @@ struct perf_event_attr { | |||
| 211 | __u32 wakeup_watermark; /* bytes before wakeup */ | 211 | __u32 wakeup_watermark; /* bytes before wakeup */ |
| 212 | }; | 212 | }; |
| 213 | 213 | ||
| 214 | struct { /* Hardware breakpoint info */ | ||
| 215 | __u64 bp_addr; | ||
| 216 | __u32 bp_type; | ||
| 217 | __u32 bp_len; | ||
| 218 | __u64 __bp_reserved_1; | ||
| 219 | __u64 __bp_reserved_2; | ||
| 220 | }; | ||
| 221 | |||
| 222 | __u32 __reserved_2; | 214 | __u32 __reserved_2; |
| 223 | 215 | ||
| 224 | __u64 __reserved_3; | 216 | __u64 bp_addr; |
| 217 | __u32 bp_type; | ||
| 218 | __u32 bp_len; | ||
| 225 | }; | 219 | }; |
| 226 | 220 | ||
| 227 | /* | 221 | /* |
diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 9052d6c8c9fd..8ab86988bd24 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c | |||
| @@ -782,6 +782,9 @@ static void __perf_install_in_context(void *info) | |||
| 782 | 782 | ||
| 783 | add_event_to_ctx(event, ctx); | 783 | add_event_to_ctx(event, ctx); |
| 784 | 784 | ||
| 785 | if (event->cpu != -1 && event->cpu != smp_processor_id()) | ||
| 786 | goto unlock; | ||
| 787 | |||
| 785 | /* | 788 | /* |
| 786 | * Don't put the event on if it is disabled or if | 789 | * Don't put the event on if it is disabled or if |
| 787 | * it is in a group and the group isn't on. | 790 | * it is in a group and the group isn't on. |
| @@ -925,6 +928,9 @@ static void __perf_event_enable(void *info) | |||
| 925 | goto unlock; | 928 | goto unlock; |
| 926 | __perf_event_mark_enabled(event, ctx); | 929 | __perf_event_mark_enabled(event, ctx); |
| 927 | 930 | ||
| 931 | if (event->cpu != -1 && event->cpu != smp_processor_id()) | ||
| 932 | goto unlock; | ||
| 933 | |||
| 928 | /* | 934 | /* |
| 929 | * If the event is in a group and isn't the group leader, | 935 | * If the event is in a group and isn't the group leader, |
| 930 | * then don't put it on unless the group is on. | 936 | * then don't put it on unless the group is on. |
| @@ -1595,15 +1601,12 @@ static struct perf_event_context *find_get_context(pid_t pid, int cpu) | |||
| 1595 | unsigned long flags; | 1601 | unsigned long flags; |
| 1596 | int err; | 1602 | int err; |
| 1597 | 1603 | ||
| 1598 | /* | 1604 | if (pid == -1 && cpu != -1) { |
| 1599 | * If cpu is not a wildcard then this is a percpu event: | ||
| 1600 | */ | ||
| 1601 | if (cpu != -1) { | ||
| 1602 | /* Must be root to operate on a CPU event: */ | 1605 | /* Must be root to operate on a CPU event: */ |
| 1603 | if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) | 1606 | if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) |
| 1604 | return ERR_PTR(-EACCES); | 1607 | return ERR_PTR(-EACCES); |
| 1605 | 1608 | ||
| 1606 | if (cpu < 0 || cpu > num_possible_cpus()) | 1609 | if (cpu < 0 || cpu >= nr_cpumask_bits) |
| 1607 | return ERR_PTR(-EINVAL); | 1610 | return ERR_PTR(-EINVAL); |
| 1608 | 1611 | ||
| 1609 | /* | 1612 | /* |
| @@ -4564,7 +4567,7 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, | |||
| 4564 | if (attr->type >= PERF_TYPE_MAX) | 4567 | if (attr->type >= PERF_TYPE_MAX) |
| 4565 | return -EINVAL; | 4568 | return -EINVAL; |
| 4566 | 4569 | ||
| 4567 | if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3) | 4570 | if (attr->__reserved_1 || attr->__reserved_2) |
| 4568 | return -EINVAL; | 4571 | return -EINVAL; |
| 4569 | 4572 | ||
| 4570 | if (attr->sample_type & ~(PERF_SAMPLE_MAX-1)) | 4573 | if (attr->sample_type & ~(PERF_SAMPLE_MAX-1)) |
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt new file mode 100644 index 000000000000..8974e208cba6 --- /dev/null +++ b/tools/perf/Documentation/perf-diff.txt | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | perf-diff(1) | ||
| 2 | ============== | ||
| 3 | |||
| 4 | NAME | ||
| 5 | ---- | ||
| 6 | perf-diff - Read two perf.data files and display the differential profile | ||
| 7 | |||
| 8 | SYNOPSIS | ||
| 9 | -------- | ||
| 10 | [verse] | ||
| 11 | 'perf diff' [oldfile] [newfile] | ||
| 12 | |||
| 13 | DESCRIPTION | ||
| 14 | ----------- | ||
| 15 | This command displays the performance difference amongst two perf.data files | ||
| 16 | captured via perf record. | ||
| 17 | |||
| 18 | If no parameters are passed it will assume perf.data.old and perf.data. | ||
| 19 | |||
| 20 | OPTIONS | ||
| 21 | ------- | ||
| 22 | -d:: | ||
| 23 | --dsos=:: | ||
| 24 | Only consider symbols in these dsos. CSV that understands | ||
| 25 | file://filename entries. | ||
| 26 | |||
| 27 | -C:: | ||
| 28 | --comms=:: | ||
| 29 | Only consider symbols in these comms. CSV that understands | ||
| 30 | file://filename entries. | ||
| 31 | |||
| 32 | -S:: | ||
| 33 | --symbols=:: | ||
| 34 | Only consider these symbols. CSV that understands | ||
| 35 | file://filename entries. | ||
| 36 | |||
| 37 | -s:: | ||
| 38 | --sort=:: | ||
| 39 | Sort by key(s): pid, comm, dso, symbol. | ||
| 40 | |||
| 41 | -t:: | ||
| 42 | --field-separator=:: | ||
| 43 | |||
| 44 | Use a special separator character and don't pad with spaces, replacing | ||
| 45 | all occurances of this separator in symbol names (and other output) | ||
| 46 | with a '.' character, that thus it's the only non valid separator. | ||
| 47 | |||
| 48 | -v:: | ||
| 49 | --verbose:: | ||
| 50 | Be verbose, for instance, show the raw counts in addition to the | ||
| 51 | diff. | ||
| 52 | |||
| 53 | SEE ALSO | ||
| 54 | -------- | ||
| 55 | linkperf:perf-record[1] | ||
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 8fa6bf99fcb5..250e391b4bc8 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
| @@ -49,8 +49,9 @@ PROBE SYNTAX | |||
| 49 | ------------ | 49 | ------------ |
| 50 | Probe points are defined by following syntax. | 50 | Probe points are defined by following syntax. |
| 51 | 51 | ||
| 52 | "FUNC[+OFFS|:RLN|%return][@SRC]|SRC:ALN [ARG ...]" | 52 | "[EVENT=]FUNC[+OFFS|:RLN|%return][@SRC]|SRC:ALN [ARG ...]" |
| 53 | 53 | ||
| 54 | 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'. | ||
| 54 | 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, 'RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. In addition, 'SRC' specifies a source file which has that function. | 55 | 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, 'RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. In addition, 'SRC' specifies a source file which has that function. |
| 55 | 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. | 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. |
| 56 | '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). | 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). |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 9dccb180b7af..abfabe9147a4 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -39,6 +39,10 @@ OPTIONS | |||
| 39 | Only consider these symbols. CSV that understands | 39 | Only consider these symbols. CSV that understands |
| 40 | file://filename entries. | 40 | file://filename entries. |
| 41 | 41 | ||
| 42 | -s:: | ||
| 43 | --sort=:: | ||
| 44 | Sort by key(s): pid, comm, dso, symbol, parent. | ||
| 45 | |||
| 42 | -w:: | 46 | -w:: |
| 43 | --field-width=:: | 47 | --field-width=:: |
| 44 | Force each column width to the provided list, for large terminal | 48 | Force each column width to the provided list, for large terminal |
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 07065efa60e0..60e5900da483 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
| @@ -8,18 +8,43 @@ perf-trace - Read perf.data (created by perf record) and display trace output | |||
| 8 | SYNOPSIS | 8 | SYNOPSIS |
| 9 | -------- | 9 | -------- |
| 10 | [verse] | 10 | [verse] |
| 11 | 'perf trace' [-i <file> | --input=file] symbol_name | 11 | 'perf trace' {record <script> | report <script> [args] } |
| 12 | 12 | ||
| 13 | DESCRIPTION | 13 | DESCRIPTION |
| 14 | ----------- | 14 | ----------- |
| 15 | This command reads the input file and displays the trace recorded. | 15 | This command reads the input file and displays the trace recorded. |
| 16 | 16 | ||
| 17 | There are several variants of perf trace: | ||
| 18 | |||
| 19 | 'perf trace' to see a detailed trace of the workload that was | ||
| 20 | recorded. | ||
| 21 | |||
| 22 | 'perf trace record <script>' to record the events required for 'perf | ||
| 23 | trace report'. <script> is the name displayed in the output of | ||
| 24 | 'perf trace --list' i.e. the actual script name minus any language | ||
| 25 | extension. | ||
| 26 | |||
| 27 | 'perf trace report <script>' to run and display the results of | ||
| 28 | <script>. <script> is the name displayed in the output of 'perf | ||
| 29 | trace --list' i.e. the actual script name minus any language | ||
| 30 | extension. The perf.data output from a previous run of 'perf trace | ||
| 31 | record <script>' is used and should be present for this command to | ||
| 32 | succeed. | ||
| 33 | |||
| 17 | OPTIONS | 34 | OPTIONS |
| 18 | ------- | 35 | ------- |
| 19 | -D:: | 36 | -D:: |
| 20 | --dump-raw-trace=:: | 37 | --dump-raw-trace=:: |
| 21 | Display verbose dump of the trace data. | 38 | Display verbose dump of the trace data. |
| 22 | 39 | ||
| 40 | -L:: | ||
| 41 | --Latency=:: | ||
| 42 | Show latency attributes (irqs/preemption disabled, etc). | ||
| 43 | |||
| 44 | -l:: | ||
| 45 | --list=:: | ||
| 46 | Display a list of available trace scripts. | ||
| 47 | |||
| 23 | -s:: | 48 | -s:: |
| 24 | --script=:: | 49 | --script=:: |
| 25 | Process trace data with the given script ([lang]:script[.ext]). | 50 | Process trace data with the given script ([lang]:script[.ext]). |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 406999668cab..7814dbbd401d 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -370,7 +370,6 @@ LIB_H += util/values.h | |||
| 370 | LIB_H += util/sort.h | 370 | LIB_H += util/sort.h |
| 371 | LIB_H += util/hist.h | 371 | LIB_H += util/hist.h |
| 372 | LIB_H += util/thread.h | 372 | LIB_H += util/thread.h |
| 373 | LIB_H += util/data_map.h | ||
| 374 | LIB_H += util/probe-finder.h | 373 | LIB_H += util/probe-finder.h |
| 375 | LIB_H += util/probe-event.h | 374 | LIB_H += util/probe-event.h |
| 376 | 375 | ||
| @@ -428,6 +427,7 @@ BUILTIN_OBJS += bench/sched-messaging.o | |||
| 428 | BUILTIN_OBJS += bench/sched-pipe.o | 427 | BUILTIN_OBJS += bench/sched-pipe.o |
| 429 | BUILTIN_OBJS += bench/mem-memcpy.o | 428 | BUILTIN_OBJS += bench/mem-memcpy.o |
| 430 | 429 | ||
| 430 | BUILTIN_OBJS += builtin-diff.o | ||
| 431 | BUILTIN_OBJS += builtin-help.o | 431 | BUILTIN_OBJS += builtin-help.o |
| 432 | BUILTIN_OBJS += builtin-sched.o | 432 | BUILTIN_OBJS += builtin-sched.o |
| 433 | BUILTIN_OBJS += builtin-buildid-list.o | 433 | BUILTIN_OBJS += builtin-buildid-list.o |
| @@ -996,8 +996,6 @@ install: all | |||
| 996 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 996 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
| 997 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' | 997 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' |
| 998 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 998 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
| 999 | $(INSTALL) scripts/perl/Perf-Trace-Util/Makefile.PL -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util' | ||
| 1000 | $(INSTALL) scripts/perl/Perf-Trace-Util/README -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util' | ||
| 1001 | ifdef BUILT_INS | 999 | ifdef BUILT_INS |
| 1002 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 1000 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
| 1003 | $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 1001 | $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 21a78d30d53d..593ff25006de 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -26,7 +26,6 @@ | |||
| 26 | #include "util/sort.h" | 26 | #include "util/sort.h" |
| 27 | #include "util/hist.h" | 27 | #include "util/hist.h" |
| 28 | #include "util/session.h" | 28 | #include "util/session.h" |
| 29 | #include "util/data_map.h" | ||
| 30 | 29 | ||
| 31 | static char const *input_name = "perf.data"; | 30 | static char const *input_name = "perf.data"; |
| 32 | 31 | ||
| @@ -52,11 +51,6 @@ struct sym_priv { | |||
| 52 | struct sym_ext *ext; | 51 | struct sym_ext *ext; |
| 53 | }; | 52 | }; |
| 54 | 53 | ||
| 55 | static struct symbol_conf symbol_conf = { | ||
| 56 | .priv_size = sizeof(struct sym_priv), | ||
| 57 | .try_vmlinux_path = true, | ||
| 58 | }; | ||
| 59 | |||
| 60 | static const char *sym_hist_filter; | 54 | static const char *sym_hist_filter; |
| 61 | 55 | ||
| 62 | static int symbol_filter(struct map *map __used, struct symbol *sym) | 56 | static int symbol_filter(struct map *map __used, struct symbol *sym) |
| @@ -122,30 +116,32 @@ static void hist_hit(struct hist_entry *he, u64 ip) | |||
| 122 | h->ip[offset]); | 116 | h->ip[offset]); |
| 123 | } | 117 | } |
| 124 | 118 | ||
| 125 | static int hist_entry__add(struct addr_location *al, u64 count) | 119 | static int perf_session__add_hist_entry(struct perf_session *self, |
| 120 | struct addr_location *al, u64 count) | ||
| 126 | { | 121 | { |
| 127 | bool hit; | 122 | bool hit; |
| 128 | struct hist_entry *he = __hist_entry__add(al, NULL, count, &hit); | 123 | struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL, |
| 124 | count, &hit); | ||
| 129 | if (he == NULL) | 125 | if (he == NULL) |
| 130 | return -ENOMEM; | 126 | return -ENOMEM; |
| 131 | hist_hit(he, al->addr); | 127 | hist_hit(he, al->addr); |
| 132 | return 0; | 128 | return 0; |
| 133 | } | 129 | } |
| 134 | 130 | ||
| 135 | static int process_sample_event(event_t *event) | 131 | static int process_sample_event(event_t *event, struct perf_session *session) |
| 136 | { | 132 | { |
| 137 | struct addr_location al; | 133 | struct addr_location al; |
| 138 | 134 | ||
| 139 | dump_printf("(IP, %d): %d: %p\n", event->header.misc, | 135 | dump_printf("(IP, %d): %d: %p\n", event->header.misc, |
| 140 | event->ip.pid, (void *)(long)event->ip.ip); | 136 | event->ip.pid, (void *)(long)event->ip.ip); |
| 141 | 137 | ||
| 142 | if (event__preprocess_sample(event, &al, symbol_filter) < 0) { | 138 | if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) { |
| 143 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 139 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
| 144 | event->header.type); | 140 | event->header.type); |
| 145 | return -1; | 141 | return -1; |
| 146 | } | 142 | } |
| 147 | 143 | ||
| 148 | if (hist_entry__add(&al, 1)) { | 144 | if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) { |
| 149 | fprintf(stderr, "problem incrementing symbol count, " | 145 | fprintf(stderr, "problem incrementing symbol count, " |
| 150 | "skipping event\n"); | 146 | "skipping event\n"); |
| 151 | return -1; | 147 | return -1; |
| @@ -429,11 +425,11 @@ static void annotate_sym(struct hist_entry *he) | |||
| 429 | free_source_line(he, len); | 425 | free_source_line(he, len); |
| 430 | } | 426 | } |
| 431 | 427 | ||
| 432 | static void find_annotations(void) | 428 | static void perf_session__find_annotations(struct perf_session *self) |
| 433 | { | 429 | { |
| 434 | struct rb_node *nd; | 430 | struct rb_node *nd; |
| 435 | 431 | ||
| 436 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | 432 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { |
| 437 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); | 433 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); |
| 438 | struct sym_priv *priv; | 434 | struct sym_priv *priv; |
| 439 | 435 | ||
| @@ -454,7 +450,7 @@ static void find_annotations(void) | |||
| 454 | } | 450 | } |
| 455 | } | 451 | } |
| 456 | 452 | ||
| 457 | static struct perf_file_handler file_handler = { | 453 | static struct perf_event_ops event_ops = { |
| 458 | .process_sample_event = process_sample_event, | 454 | .process_sample_event = process_sample_event, |
| 459 | .process_mmap_event = event__process_mmap, | 455 | .process_mmap_event = event__process_mmap, |
| 460 | .process_comm_event = event__process_comm, | 456 | .process_comm_event = event__process_comm, |
| @@ -463,17 +459,14 @@ static struct perf_file_handler file_handler = { | |||
| 463 | 459 | ||
| 464 | static int __cmd_annotate(void) | 460 | static int __cmd_annotate(void) |
| 465 | { | 461 | { |
| 466 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, force); | ||
| 467 | struct thread *idle; | ||
| 468 | int ret; | 462 | int ret; |
| 463 | struct perf_session *session; | ||
| 469 | 464 | ||
| 465 | session = perf_session__new(input_name, O_RDONLY, force); | ||
| 470 | if (session == NULL) | 466 | if (session == NULL) |
| 471 | return -ENOMEM; | 467 | return -ENOMEM; |
| 472 | 468 | ||
| 473 | idle = register_idle_thread(); | 469 | ret = perf_session__process_events(session, &event_ops); |
| 474 | register_perf_file_handler(&file_handler); | ||
| 475 | |||
| 476 | ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd); | ||
| 477 | if (ret) | 470 | if (ret) |
| 478 | goto out_delete; | 471 | goto out_delete; |
| 479 | 472 | ||
| @@ -483,15 +476,14 @@ static int __cmd_annotate(void) | |||
| 483 | } | 476 | } |
| 484 | 477 | ||
| 485 | if (verbose > 3) | 478 | if (verbose > 3) |
| 486 | threads__fprintf(stdout); | 479 | perf_session__fprintf(session, stdout); |
| 487 | 480 | ||
| 488 | if (verbose > 2) | 481 | if (verbose > 2) |
| 489 | dsos__fprintf(stdout); | 482 | dsos__fprintf(stdout); |
| 490 | 483 | ||
| 491 | collapse__resort(); | 484 | perf_session__collapse_resort(session); |
| 492 | output__resort(event__total[0]); | 485 | perf_session__output_resort(session, session->event_total[0]); |
| 493 | 486 | perf_session__find_annotations(session); | |
| 494 | find_annotations(); | ||
| 495 | out_delete: | 487 | out_delete: |
| 496 | perf_session__delete(session); | 488 | perf_session__delete(session); |
| 497 | 489 | ||
| @@ -524,29 +516,17 @@ static const struct option options[] = { | |||
| 524 | OPT_END() | 516 | OPT_END() |
| 525 | }; | 517 | }; |
| 526 | 518 | ||
| 527 | static void setup_sorting(void) | 519 | int cmd_annotate(int argc, const char **argv, const char *prefix __used) |
| 528 | { | 520 | { |
| 529 | char *tmp, *tok, *str = strdup(sort_order); | 521 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
| 530 | |||
| 531 | for (tok = strtok_r(str, ", ", &tmp); | ||
| 532 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
| 533 | if (sort_dimension__add(tok) < 0) { | ||
| 534 | error("Unknown --sort key: `%s'", tok); | ||
| 535 | usage_with_options(annotate_usage, options); | ||
| 536 | } | ||
| 537 | } | ||
| 538 | 522 | ||
| 539 | free(str); | 523 | symbol_conf.priv_size = sizeof(struct sym_priv); |
| 540 | } | 524 | symbol_conf.try_vmlinux_path = true; |
| 541 | 525 | ||
| 542 | int cmd_annotate(int argc, const char **argv, const char *prefix __used) | 526 | if (symbol__init() < 0) |
| 543 | { | ||
| 544 | if (symbol__init(&symbol_conf) < 0) | ||
| 545 | return -1; | 527 | return -1; |
| 546 | 528 | ||
| 547 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 529 | setup_sorting(annotate_usage, options); |
| 548 | |||
| 549 | setup_sorting(); | ||
| 550 | 530 | ||
| 551 | if (argc) { | 531 | if (argc) { |
| 552 | /* | 532 | /* |
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index bfd16a1594e4..e693e6777af5 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include "builtin.h" | 9 | #include "builtin.h" |
| 10 | #include "perf.h" | 10 | #include "perf.h" |
| 11 | #include "util/cache.h" | 11 | #include "util/cache.h" |
| 12 | #include "util/data_map.h" | ||
| 13 | #include "util/debug.h" | 12 | #include "util/debug.h" |
| 14 | #include "util/parse-options.h" | 13 | #include "util/parse-options.h" |
| 15 | #include "util/session.h" | 14 | #include "util/session.h" |
| @@ -55,8 +54,9 @@ static int perf_file_section__process_buildids(struct perf_file_section *self, | |||
| 55 | static int __cmd_buildid_list(void) | 54 | static int __cmd_buildid_list(void) |
| 56 | { | 55 | { |
| 57 | int err = -1; | 56 | int err = -1; |
| 58 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, force); | 57 | struct perf_session *session; |
| 59 | 58 | ||
| 59 | session = perf_session__new(input_name, O_RDONLY, force); | ||
| 60 | if (session == NULL) | 60 | if (session == NULL) |
| 61 | return -1; | 61 | return -1; |
| 62 | 62 | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c new file mode 100644 index 000000000000..4d33b55d5584 --- /dev/null +++ b/tools/perf/builtin-diff.c | |||
| @@ -0,0 +1,248 @@ | |||
| 1 | /* | ||
| 2 | * builtin-diff.c | ||
| 3 | * | ||
| 4 | * Builtin diff command: Analyze two perf.data input files, look up and read | ||
| 5 | * DSOs and symbol information, sort them and produce a diff. | ||
| 6 | */ | ||
| 7 | #include "builtin.h" | ||
| 8 | |||
| 9 | #include "util/debug.h" | ||
| 10 | #include "util/event.h" | ||
| 11 | #include "util/hist.h" | ||
| 12 | #include "util/session.h" | ||
| 13 | #include "util/sort.h" | ||
| 14 | #include "util/symbol.h" | ||
| 15 | #include "util/util.h" | ||
| 16 | |||
| 17 | #include <stdlib.h> | ||
| 18 | |||
| 19 | static char const *input_old = "perf.data.old", | ||
| 20 | *input_new = "perf.data"; | ||
| 21 | static char diff__default_sort_order[] = "dso,symbol"; | ||
| 22 | static int force; | ||
| 23 | static bool show_displacement; | ||
| 24 | |||
| 25 | static int perf_session__add_hist_entry(struct perf_session *self, | ||
| 26 | struct addr_location *al, u64 count) | ||
| 27 | { | ||
| 28 | bool hit; | ||
| 29 | struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL, | ||
| 30 | count, &hit); | ||
| 31 | if (he == NULL) | ||
| 32 | return -ENOMEM; | ||
| 33 | |||
| 34 | if (hit) | ||
| 35 | he->count += count; | ||
| 36 | |||
| 37 | return 0; | ||
| 38 | } | ||
| 39 | |||
| 40 | static int diff__process_sample_event(event_t *event, struct perf_session *session) | ||
| 41 | { | ||
| 42 | struct addr_location al; | ||
| 43 | struct sample_data data = { .period = 1, }; | ||
| 44 | |||
| 45 | dump_printf("(IP, %d): %d: %p\n", event->header.misc, | ||
| 46 | event->ip.pid, (void *)(long)event->ip.ip); | ||
| 47 | |||
| 48 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { | ||
| 49 | pr_warning("problem processing %d event, skipping it.\n", | ||
| 50 | event->header.type); | ||
| 51 | return -1; | ||
| 52 | } | ||
| 53 | |||
| 54 | if (al.filtered) | ||
| 55 | return 0; | ||
| 56 | |||
| 57 | event__parse_sample(event, session->sample_type, &data); | ||
| 58 | |||
| 59 | if (al.sym && perf_session__add_hist_entry(session, &al, data.period)) { | ||
| 60 | pr_warning("problem incrementing symbol count, skipping event\n"); | ||
| 61 | return -1; | ||
| 62 | } | ||
| 63 | |||
| 64 | session->events_stats.total += data.period; | ||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | static struct perf_event_ops event_ops = { | ||
| 69 | .process_sample_event = diff__process_sample_event, | ||
| 70 | .process_mmap_event = event__process_mmap, | ||
| 71 | .process_comm_event = event__process_comm, | ||
| 72 | .process_exit_event = event__process_task, | ||
| 73 | .process_fork_event = event__process_task, | ||
| 74 | .process_lost_event = event__process_lost, | ||
| 75 | }; | ||
| 76 | |||
| 77 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, | ||
| 78 | struct hist_entry *he) | ||
| 79 | { | ||
| 80 | struct rb_node **p = &root->rb_node; | ||
| 81 | struct rb_node *parent = NULL; | ||
| 82 | struct hist_entry *iter; | ||
| 83 | |||
| 84 | while (*p != NULL) { | ||
| 85 | int cmp; | ||
| 86 | parent = *p; | ||
| 87 | iter = rb_entry(parent, struct hist_entry, rb_node); | ||
| 88 | |||
| 89 | cmp = strcmp(he->map->dso->name, iter->map->dso->name); | ||
| 90 | if (cmp > 0) | ||
| 91 | p = &(*p)->rb_left; | ||
| 92 | else if (cmp < 0) | ||
| 93 | 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 | } | ||
| 102 | |||
| 103 | rb_link_node(&he->rb_node, parent, p); | ||
| 104 | rb_insert_color(&he->rb_node, root); | ||
| 105 | } | ||
| 106 | |||
| 107 | static void perf_session__resort_by_name(struct perf_session *self) | ||
| 108 | { | ||
| 109 | unsigned long position = 1; | ||
| 110 | struct rb_root tmp = RB_ROOT; | ||
| 111 | struct rb_node *next = rb_first(&self->hists); | ||
| 112 | |||
| 113 | while (next != NULL) { | ||
| 114 | struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); | ||
| 115 | |||
| 116 | next = rb_next(&n->rb_node); | ||
| 117 | rb_erase(&n->rb_node, &self->hists); | ||
| 118 | n->position = position++; | ||
| 119 | perf_session__insert_hist_entry_by_name(&tmp, n); | ||
| 120 | } | ||
| 121 | |||
| 122 | self->hists = tmp; | ||
| 123 | } | ||
| 124 | |||
| 125 | static struct hist_entry * | ||
| 126 | perf_session__find_hist_entry_by_name(struct perf_session *self, | ||
| 127 | struct hist_entry *he) | ||
| 128 | { | ||
| 129 | struct rb_node *n = self->hists.rb_node; | ||
| 130 | |||
| 131 | while (n) { | ||
| 132 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); | ||
| 133 | int cmp = strcmp(he->map->dso->name, iter->map->dso->name); | ||
| 134 | |||
| 135 | if (cmp > 0) | ||
| 136 | n = n->rb_left; | ||
| 137 | else if (cmp < 0) | ||
| 138 | n = n->rb_right; | ||
| 139 | else { | ||
| 140 | cmp = strcmp(he->sym->name, iter->sym->name); | ||
| 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 | } | ||
| 149 | |||
| 150 | return NULL; | ||
| 151 | } | ||
| 152 | |||
| 153 | static void perf_session__match_hists(struct perf_session *old_session, | ||
| 154 | struct perf_session *new_session) | ||
| 155 | { | ||
| 156 | struct rb_node *nd; | ||
| 157 | |||
| 158 | perf_session__resort_by_name(old_session); | ||
| 159 | |||
| 160 | 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); | ||
| 162 | pos->pair = perf_session__find_hist_entry_by_name(old_session, pos); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | static int __cmd_diff(void) | ||
| 167 | { | ||
| 168 | int ret, i; | ||
| 169 | struct perf_session *session[2]; | ||
| 170 | |||
| 171 | session[0] = perf_session__new(input_old, O_RDONLY, force); | ||
| 172 | session[1] = perf_session__new(input_new, O_RDONLY, force); | ||
| 173 | if (session[0] == NULL || session[1] == NULL) | ||
| 174 | return -ENOMEM; | ||
| 175 | |||
| 176 | for (i = 0; i < 2; ++i) { | ||
| 177 | ret = perf_session__process_events(session[i], &event_ops); | ||
| 178 | if (ret) | ||
| 179 | goto out_delete; | ||
| 180 | perf_session__output_resort(session[i], session[i]->events_stats.total); | ||
| 181 | } | ||
| 182 | |||
| 183 | perf_session__match_hists(session[0], session[1]); | ||
| 184 | perf_session__fprintf_hists(session[1], session[0], | ||
| 185 | show_displacement, stdout); | ||
| 186 | out_delete: | ||
| 187 | for (i = 0; i < 2; ++i) | ||
| 188 | perf_session__delete(session[i]); | ||
| 189 | return ret; | ||
| 190 | } | ||
| 191 | |||
| 192 | static const char *const diff_usage[] = { | ||
| 193 | "perf diff [<options>] [old_file] [new_file]", | ||
| 194 | }; | ||
| 195 | |||
| 196 | static const struct option options[] = { | ||
| 197 | OPT_BOOLEAN('v', "verbose", &verbose, | ||
| 198 | "be more verbose (show symbol address, etc)"), | ||
| 199 | OPT_BOOLEAN('m', "displacement", &show_displacement, | ||
| 200 | "Show position displacement relative to baseline"), | ||
| 201 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | ||
| 202 | "dump raw trace in ASCII"), | ||
| 203 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
| 204 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | ||
| 205 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | ||
| 206 | OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths, | ||
| 207 | "Don't shorten the pathnames taking into account the cwd"), | ||
| 208 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | ||
| 209 | "only consider symbols in these dsos"), | ||
| 210 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | ||
| 211 | "only consider symbols in these comms"), | ||
| 212 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | ||
| 213 | "only consider these symbols"), | ||
| 214 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | ||
| 215 | "sort by key(s): pid, comm, dso, symbol, parent"), | ||
| 216 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", | ||
| 217 | "separator for columns, no spaces will be added between " | ||
| 218 | "columns '.' is reserved."), | ||
| 219 | OPT_END() | ||
| 220 | }; | ||
| 221 | |||
| 222 | int cmd_diff(int argc, const char **argv, const char *prefix __used) | ||
| 223 | { | ||
| 224 | sort_order = diff__default_sort_order; | ||
| 225 | argc = parse_options(argc, argv, options, diff_usage, 0); | ||
| 226 | if (argc) { | ||
| 227 | if (argc > 2) | ||
| 228 | usage_with_options(diff_usage, options); | ||
| 229 | if (argc == 2) { | ||
| 230 | input_old = argv[0]; | ||
| 231 | input_new = argv[1]; | ||
| 232 | } else | ||
| 233 | input_new = argv[0]; | ||
| 234 | } | ||
| 235 | |||
| 236 | symbol_conf.exclude_other = false; | ||
| 237 | if (symbol__init() < 0) | ||
| 238 | return -1; | ||
| 239 | |||
| 240 | setup_sorting(diff_usage, options); | ||
| 241 | setup_pager(); | ||
| 242 | |||
| 243 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); | ||
| 244 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", NULL); | ||
| 245 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", NULL); | ||
| 246 | |||
| 247 | return __cmd_diff(); | ||
| 248 | } | ||
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 2071d2485913..fc21ad79dd83 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
| @@ -12,7 +12,6 @@ | |||
| 12 | #include "util/trace-event.h" | 12 | #include "util/trace-event.h" |
| 13 | 13 | ||
| 14 | #include "util/debug.h" | 14 | #include "util/debug.h" |
| 15 | #include "util/data_map.h" | ||
| 16 | 15 | ||
| 17 | #include <linux/rbtree.h> | 16 | #include <linux/rbtree.h> |
| 18 | 17 | ||
| @@ -21,8 +20,6 @@ typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); | |||
| 21 | 20 | ||
| 22 | static char const *input_name = "perf.data"; | 21 | static char const *input_name = "perf.data"; |
| 23 | 22 | ||
| 24 | static u64 sample_type; | ||
| 25 | |||
| 26 | static int alloc_flag; | 23 | static int alloc_flag; |
| 27 | static int caller_flag; | 24 | static int caller_flag; |
| 28 | 25 | ||
| @@ -312,7 +309,7 @@ process_raw_event(event_t *raw_event __used, void *data, | |||
| 312 | } | 309 | } |
| 313 | } | 310 | } |
| 314 | 311 | ||
| 315 | static int process_sample_event(event_t *event) | 312 | static int process_sample_event(event_t *event, struct perf_session *session) |
| 316 | { | 313 | { |
| 317 | struct sample_data data; | 314 | struct sample_data data; |
| 318 | struct thread *thread; | 315 | struct thread *thread; |
| @@ -322,7 +319,7 @@ static int process_sample_event(event_t *event) | |||
| 322 | data.cpu = -1; | 319 | data.cpu = -1; |
| 323 | data.period = 1; | 320 | data.period = 1; |
| 324 | 321 | ||
| 325 | event__parse_sample(event, sample_type, &data); | 322 | event__parse_sample(event, session->sample_type, &data); |
| 326 | 323 | ||
| 327 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 324 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", |
| 328 | event->header.misc, | 325 | event->header.misc, |
| @@ -330,7 +327,7 @@ static int process_sample_event(event_t *event) | |||
| 330 | (void *)(long)data.ip, | 327 | (void *)(long)data.ip, |
| 331 | (long long)data.period); | 328 | (long long)data.period); |
| 332 | 329 | ||
| 333 | thread = threads__findnew(event->ip.pid); | 330 | thread = perf_session__findnew(session, event->ip.pid); |
| 334 | if (thread == NULL) { | 331 | if (thread == NULL) { |
| 335 | pr_debug("problem processing %d event, skipping it.\n", | 332 | pr_debug("problem processing %d event, skipping it.\n", |
| 336 | event->header.type); | 333 | event->header.type); |
| @@ -345,11 +342,9 @@ static int process_sample_event(event_t *event) | |||
| 345 | return 0; | 342 | return 0; |
| 346 | } | 343 | } |
| 347 | 344 | ||
| 348 | static int sample_type_check(u64 type) | 345 | static int sample_type_check(struct perf_session *session) |
| 349 | { | 346 | { |
| 350 | sample_type = type; | 347 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { |
| 351 | |||
| 352 | if (!(sample_type & PERF_SAMPLE_RAW)) { | ||
| 353 | fprintf(stderr, | 348 | fprintf(stderr, |
| 354 | "No trace sample to read. Did you call perf record " | 349 | "No trace sample to read. Did you call perf record " |
| 355 | "without -R?"); | 350 | "without -R?"); |
| @@ -359,28 +354,12 @@ static int sample_type_check(u64 type) | |||
| 359 | return 0; | 354 | return 0; |
| 360 | } | 355 | } |
| 361 | 356 | ||
| 362 | static struct perf_file_handler file_handler = { | 357 | static struct perf_event_ops event_ops = { |
| 363 | .process_sample_event = process_sample_event, | 358 | .process_sample_event = process_sample_event, |
| 364 | .process_comm_event = event__process_comm, | 359 | .process_comm_event = event__process_comm, |
| 365 | .sample_type_check = sample_type_check, | 360 | .sample_type_check = sample_type_check, |
| 366 | }; | 361 | }; |
| 367 | 362 | ||
| 368 | static int read_events(void) | ||
| 369 | { | ||
| 370 | int err; | ||
| 371 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); | ||
| 372 | |||
| 373 | if (session == NULL) | ||
| 374 | return -ENOMEM; | ||
| 375 | |||
| 376 | register_idle_thread(); | ||
| 377 | register_perf_file_handler(&file_handler); | ||
| 378 | |||
| 379 | err = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd); | ||
| 380 | perf_session__delete(session); | ||
| 381 | return err; | ||
| 382 | } | ||
| 383 | |||
| 384 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) | 363 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) |
| 385 | { | 364 | { |
| 386 | if (n_alloc == 0) | 365 | if (n_alloc == 0) |
| @@ -389,7 +368,8 @@ static double fragmentation(unsigned long n_req, unsigned long n_alloc) | |||
| 389 | return 100.0 - (100.0 * n_req / n_alloc); | 368 | return 100.0 - (100.0 * n_req / n_alloc); |
| 390 | } | 369 | } |
| 391 | 370 | ||
| 392 | static void __print_result(struct rb_root *root, int n_lines, int is_caller) | 371 | static void __print_result(struct rb_root *root, struct perf_session *session, |
| 372 | int n_lines, int is_caller) | ||
| 393 | { | 373 | { |
| 394 | struct rb_node *next; | 374 | struct rb_node *next; |
| 395 | 375 | ||
| @@ -410,7 +390,7 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller) | |||
| 410 | if (is_caller) { | 390 | if (is_caller) { |
| 411 | addr = data->call_site; | 391 | addr = data->call_site; |
| 412 | if (!raw_ip) | 392 | if (!raw_ip) |
| 413 | sym = map_groups__find_function(kmaps, addr, NULL); | 393 | sym = map_groups__find_function(&session->kmaps, session, addr, NULL); |
| 414 | } else | 394 | } else |
| 415 | addr = data->ptr; | 395 | addr = data->ptr; |
| 416 | 396 | ||
| @@ -451,12 +431,12 @@ static void print_summary(void) | |||
| 451 | printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs); | 431 | printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs); |
| 452 | } | 432 | } |
| 453 | 433 | ||
| 454 | static void print_result(void) | 434 | static void print_result(struct perf_session *session) |
| 455 | { | 435 | { |
| 456 | if (caller_flag) | 436 | if (caller_flag) |
| 457 | __print_result(&root_caller_sorted, caller_lines, 1); | 437 | __print_result(&root_caller_sorted, session, caller_lines, 1); |
| 458 | if (alloc_flag) | 438 | if (alloc_flag) |
| 459 | __print_result(&root_alloc_sorted, alloc_lines, 0); | 439 | __print_result(&root_alloc_sorted, session, alloc_lines, 0); |
| 460 | print_summary(); | 440 | print_summary(); |
| 461 | } | 441 | } |
| 462 | 442 | ||
| @@ -524,12 +504,20 @@ static void sort_result(void) | |||
| 524 | 504 | ||
| 525 | static int __cmd_kmem(void) | 505 | static int __cmd_kmem(void) |
| 526 | { | 506 | { |
| 507 | int err; | ||
| 508 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); | ||
| 509 | if (session == NULL) | ||
| 510 | return -ENOMEM; | ||
| 511 | |||
| 527 | setup_pager(); | 512 | setup_pager(); |
| 528 | read_events(); | 513 | err = perf_session__process_events(session, &event_ops); |
| 514 | if (err != 0) | ||
| 515 | goto out_delete; | ||
| 529 | sort_result(); | 516 | sort_result(); |
| 530 | print_result(); | 517 | print_result(session); |
| 531 | 518 | out_delete: | |
| 532 | return 0; | 519 | perf_session__delete(session); |
| 520 | return err; | ||
| 533 | } | 521 | } |
| 534 | 522 | ||
| 535 | static const char * const kmem_usage[] = { | 523 | static const char * const kmem_usage[] = { |
| @@ -778,13 +766,13 @@ static int __cmd_record(int argc, const char **argv) | |||
| 778 | 766 | ||
| 779 | int cmd_kmem(int argc, const char **argv, const char *prefix __used) | 767 | int cmd_kmem(int argc, const char **argv, const char *prefix __used) |
| 780 | { | 768 | { |
| 781 | symbol__init(0); | ||
| 782 | |||
| 783 | argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); | 769 | argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); |
| 784 | 770 | ||
| 785 | if (!argc) | 771 | if (!argc) |
| 786 | usage_with_options(kmem_usage, kmem_options); | 772 | usage_with_options(kmem_usage, kmem_options); |
| 787 | 773 | ||
| 774 | symbol__init(); | ||
| 775 | |||
| 788 | if (!strncmp(argv[0], "rec", 3)) { | 776 | if (!strncmp(argv[0], "rec", 3)) { |
| 789 | return __cmd_record(argc, argv); | 777 | return __cmd_record(argc, argv); |
| 790 | } else if (!strcmp(argv[0], "stat")) { | 778 | } else if (!strcmp(argv[0], "stat")) { |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 5a47c1e11f77..7e741f54d798 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
| @@ -38,34 +38,29 @@ | |||
| 38 | #include "util/strlist.h" | 38 | #include "util/strlist.h" |
| 39 | #include "util/event.h" | 39 | #include "util/event.h" |
| 40 | #include "util/debug.h" | 40 | #include "util/debug.h" |
| 41 | #include "util/symbol.h" | ||
| 42 | #include "util/thread.h" | ||
| 43 | #include "util/session.h" | ||
| 41 | #include "util/parse-options.h" | 44 | #include "util/parse-options.h" |
| 42 | #include "util/parse-events.h" /* For debugfs_path */ | 45 | #include "util/parse-events.h" /* For debugfs_path */ |
| 43 | #include "util/probe-finder.h" | 46 | #include "util/probe-finder.h" |
| 44 | #include "util/probe-event.h" | 47 | #include "util/probe-event.h" |
| 45 | 48 | ||
| 46 | /* Default vmlinux search paths */ | ||
| 47 | #define NR_SEARCH_PATH 4 | ||
| 48 | const char *default_search_path[NR_SEARCH_PATH] = { | ||
| 49 | "/lib/modules/%s/build/vmlinux", /* Custom build kernel */ | ||
| 50 | "/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */ | ||
| 51 | "/boot/vmlinux-debug-%s", /* Ubuntu */ | ||
| 52 | "./vmlinux", /* CWD */ | ||
| 53 | }; | ||
| 54 | |||
| 55 | #define MAX_PATH_LEN 256 | 49 | #define MAX_PATH_LEN 256 |
| 56 | #define MAX_PROBES 128 | 50 | #define MAX_PROBES 128 |
| 57 | 51 | ||
| 58 | /* Session management structure */ | 52 | /* Session management structure */ |
| 59 | static struct { | 53 | static struct { |
| 60 | char *vmlinux; | 54 | bool need_dwarf; |
| 61 | char *release; | 55 | bool list_events; |
| 62 | int need_dwarf; | 56 | bool force_add; |
| 63 | int nr_probe; | 57 | int nr_probe; |
| 64 | struct probe_point probes[MAX_PROBES]; | 58 | struct probe_point probes[MAX_PROBES]; |
| 65 | struct strlist *dellist; | 59 | struct strlist *dellist; |
| 60 | struct perf_session *psession; | ||
| 61 | struct map *kmap; | ||
| 66 | } session; | 62 | } session; |
| 67 | 63 | ||
| 68 | static bool listing; | ||
| 69 | 64 | ||
| 70 | /* Parse an event definition. Note that any error must die. */ | 65 | /* Parse an event definition. Note that any error must die. */ |
| 71 | static void parse_probe_event(const char *str) | 66 | static void parse_probe_event(const char *str) |
| @@ -77,7 +72,7 @@ static void parse_probe_event(const char *str) | |||
| 77 | die("Too many probes (> %d) are specified.", MAX_PROBES); | 72 | die("Too many probes (> %d) are specified.", MAX_PROBES); |
| 78 | 73 | ||
| 79 | /* Parse perf-probe event into probe_point */ | 74 | /* Parse perf-probe event into probe_point */ |
| 80 | session.need_dwarf = parse_perf_probe_event(str, pp); | 75 | parse_perf_probe_event(str, pp, &session.need_dwarf); |
| 81 | 76 | ||
| 82 | pr_debug("%d arguments\n", pp->nr_args); | 77 | pr_debug("%d arguments\n", pp->nr_args); |
| 83 | } | 78 | } |
| @@ -120,34 +115,26 @@ static int opt_del_probe_event(const struct option *opt __used, | |||
| 120 | return 0; | 115 | return 0; |
| 121 | } | 116 | } |
| 122 | 117 | ||
| 118 | /* Currently just checking function name from symbol map */ | ||
| 119 | static void evaluate_probe_point(struct probe_point *pp) | ||
| 120 | { | ||
| 121 | struct symbol *sym; | ||
| 122 | sym = map__find_symbol_by_name(session.kmap, pp->function, | ||
| 123 | session.psession, NULL); | ||
| 124 | if (!sym) | ||
| 125 | die("Kernel symbol \'%s\' not found - probe not added.", | ||
| 126 | pp->function); | ||
| 127 | } | ||
| 128 | |||
| 123 | #ifndef NO_LIBDWARF | 129 | #ifndef NO_LIBDWARF |
| 124 | static int open_default_vmlinux(void) | 130 | static int open_vmlinux(void) |
| 125 | { | 131 | { |
| 126 | struct utsname uts; | 132 | if (map__load(session.kmap, session.psession, NULL) < 0) { |
| 127 | char fname[MAX_PATH_LEN]; | 133 | pr_debug("Failed to load kernel map.\n"); |
| 128 | int fd, ret, i; | 134 | return -EINVAL; |
| 129 | |||
| 130 | ret = uname(&uts); | ||
| 131 | if (ret) { | ||
| 132 | pr_debug("uname() failed.\n"); | ||
| 133 | return -errno; | ||
| 134 | } | ||
| 135 | session.release = uts.release; | ||
| 136 | for (i = 0; i < NR_SEARCH_PATH; i++) { | ||
| 137 | ret = snprintf(fname, MAX_PATH_LEN, | ||
| 138 | default_search_path[i], session.release); | ||
| 139 | if (ret >= MAX_PATH_LEN || ret < 0) { | ||
| 140 | pr_debug("Filename(%d,%s) is too long.\n", i, | ||
| 141 | uts.release); | ||
| 142 | errno = E2BIG; | ||
| 143 | return -E2BIG; | ||
| 144 | } | ||
| 145 | pr_debug("try to open %s\n", fname); | ||
| 146 | fd = open(fname, O_RDONLY); | ||
| 147 | if (fd >= 0) | ||
| 148 | break; | ||
| 149 | } | 135 | } |
| 150 | return fd; | 136 | pr_debug("Try to open %s\n", session.kmap->dso->long_name); |
| 137 | return open(session.kmap->dso->long_name, O_RDONLY); | ||
| 151 | } | 138 | } |
| 152 | #endif | 139 | #endif |
| 153 | 140 | ||
| @@ -163,21 +150,22 @@ static const struct option options[] = { | |||
| 163 | OPT_BOOLEAN('v', "verbose", &verbose, | 150 | OPT_BOOLEAN('v', "verbose", &verbose, |
| 164 | "be more verbose (show parsed arguments, etc)"), | 151 | "be more verbose (show parsed arguments, etc)"), |
| 165 | #ifndef NO_LIBDWARF | 152 | #ifndef NO_LIBDWARF |
| 166 | OPT_STRING('k', "vmlinux", &session.vmlinux, "file", | 153 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
| 167 | "vmlinux/module pathname"), | 154 | "file", "vmlinux pathname"), |
| 168 | #endif | 155 | #endif |
| 169 | OPT_BOOLEAN('l', "list", &listing, "list up current probe events"), | 156 | OPT_BOOLEAN('l', "list", &session.list_events, |
| 157 | "list up current probe events"), | ||
| 170 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", | 158 | OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.", |
| 171 | opt_del_probe_event), | 159 | opt_del_probe_event), |
| 172 | OPT_CALLBACK('a', "add", NULL, | 160 | OPT_CALLBACK('a', "add", NULL, |
| 173 | #ifdef NO_LIBDWARF | 161 | #ifdef NO_LIBDWARF |
| 174 | "FUNC[+OFFS|%return] [ARG ...]", | 162 | "[EVENT=]FUNC[+OFFS|%return] [ARG ...]", |
| 175 | #else | 163 | #else |
| 176 | "FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]", | 164 | "[EVENT=]FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]", |
| 177 | #endif | 165 | #endif |
| 178 | "probe point definition, where\n" | 166 | "probe point definition, where\n" |
| 179 | "\t\tGRP:\tGroup name (optional)\n" | 167 | "\t\tGROUP:\tGroup name (optional)\n" |
| 180 | "\t\tNAME:\tEvent name\n" | 168 | "\t\tEVENT:\tEvent name\n" |
| 181 | "\t\tFUNC:\tFunction name\n" | 169 | "\t\tFUNC:\tFunction name\n" |
| 182 | "\t\tOFFS:\tOffset from function entry (in byte)\n" | 170 | "\t\tOFFS:\tOffset from function entry (in byte)\n" |
| 183 | "\t\t%return:\tPut the probe at function return\n" | 171 | "\t\t%return:\tPut the probe at function return\n" |
| @@ -191,6 +179,8 @@ static const struct option options[] = { | |||
| 191 | #endif | 179 | #endif |
| 192 | "\t\t\tkprobe-tracer argument format.)\n", | 180 | "\t\t\tkprobe-tracer argument format.)\n", |
| 193 | opt_add_probe_event), | 181 | opt_add_probe_event), |
| 182 | OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events" | ||
| 183 | " with existing name"), | ||
| 194 | OPT_END() | 184 | OPT_END() |
| 195 | }; | 185 | }; |
| 196 | 186 | ||
| @@ -204,13 +194,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
| 204 | 194 | ||
| 205 | argc = parse_options(argc, argv, options, probe_usage, | 195 | argc = parse_options(argc, argv, options, probe_usage, |
| 206 | PARSE_OPT_STOP_AT_NON_OPTION); | 196 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 207 | if (argc > 0) | 197 | if (argc > 0) { |
| 198 | if (strcmp(argv[0], "-") == 0) { | ||
| 199 | pr_warning(" Error: '-' is not supported.\n"); | ||
| 200 | usage_with_options(probe_usage, options); | ||
| 201 | } | ||
| 208 | parse_probe_event_argv(argc, argv); | 202 | parse_probe_event_argv(argc, argv); |
| 203 | } | ||
| 209 | 204 | ||
| 210 | if ((session.nr_probe == 0 && !session.dellist && !listing)) | 205 | if ((!session.nr_probe && !session.dellist && !session.list_events)) |
| 211 | usage_with_options(probe_usage, options); | 206 | usage_with_options(probe_usage, options); |
| 212 | 207 | ||
| 213 | if (listing) { | 208 | if (session.list_events) { |
| 214 | if (session.nr_probe != 0 || session.dellist) { | 209 | if (session.nr_probe != 0 || session.dellist) { |
| 215 | pr_warning(" Error: Don't use --list with" | 210 | pr_warning(" Error: Don't use --list with" |
| 216 | " --add/--del.\n"); | 211 | " --add/--del.\n"); |
| @@ -227,17 +222,28 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
| 227 | return 0; | 222 | return 0; |
| 228 | } | 223 | } |
| 229 | 224 | ||
| 225 | /* Initialize symbol maps for vmlinux */ | ||
| 226 | symbol_conf.sort_by_name = true; | ||
| 227 | if (symbol_conf.vmlinux_name == NULL) | ||
| 228 | symbol_conf.try_vmlinux_path = true; | ||
| 229 | if (symbol__init() < 0) | ||
| 230 | die("Failed to init symbol map."); | ||
| 231 | session.psession = perf_session__new(NULL, O_WRONLY, false); | ||
| 232 | if (session.psession == NULL) | ||
| 233 | die("Failed to init perf_session."); | ||
| 234 | session.kmap = map_groups__find_by_name(&session.psession->kmaps, | ||
| 235 | MAP__FUNCTION, | ||
| 236 | "[kernel.kallsyms]"); | ||
| 237 | if (!session.kmap) | ||
| 238 | die("Could not find kernel map.\n"); | ||
| 239 | |||
| 230 | if (session.need_dwarf) | 240 | if (session.need_dwarf) |
| 231 | #ifdef NO_LIBDWARF | 241 | #ifdef NO_LIBDWARF |
| 232 | die("Debuginfo-analysis is not supported"); | 242 | die("Debuginfo-analysis is not supported"); |
| 233 | #else /* !NO_LIBDWARF */ | 243 | #else /* !NO_LIBDWARF */ |
| 234 | pr_debug("Some probes require debuginfo.\n"); | 244 | pr_debug("Some probes require debuginfo.\n"); |
| 235 | 245 | ||
| 236 | if (session.vmlinux) { | 246 | fd = open_vmlinux(); |
| 237 | pr_debug("Try to open %s.", session.vmlinux); | ||
| 238 | fd = open(session.vmlinux, O_RDONLY); | ||
| 239 | } else | ||
| 240 | fd = open_default_vmlinux(); | ||
| 241 | if (fd < 0) { | 247 | if (fd < 0) { |
| 242 | if (session.need_dwarf) | 248 | if (session.need_dwarf) |
| 243 | die("Could not open debuginfo file."); | 249 | die("Could not open debuginfo file."); |
| @@ -255,15 +261,22 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
| 255 | 261 | ||
| 256 | lseek(fd, SEEK_SET, 0); | 262 | lseek(fd, SEEK_SET, 0); |
| 257 | ret = find_probepoint(fd, pp); | 263 | ret = find_probepoint(fd, pp); |
| 258 | if (ret < 0) { | 264 | if (ret > 0) |
| 259 | if (session.need_dwarf) | 265 | continue; |
| 260 | die("Could not analyze debuginfo."); | 266 | if (ret == 0) { /* No error but failed to find probe point. */ |
| 261 | 267 | synthesize_perf_probe_point(pp); | |
| 262 | pr_warning("An error occurred in debuginfo analysis. Try to use symbols.\n"); | 268 | die("Probe point '%s' not found. - probe not added.", |
| 263 | break; | 269 | pp->probes[0]); |
| 270 | } | ||
| 271 | /* Error path */ | ||
| 272 | if (session.need_dwarf) { | ||
| 273 | if (ret == -ENOENT) | ||
| 274 | pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
| 275 | die("Could not analyze debuginfo."); | ||
| 264 | } | 276 | } |
| 265 | if (ret == 0) /* No error but failed to find probe point. */ | 277 | pr_debug("An error occurred in debuginfo analysis." |
| 266 | die("No probe point found."); | 278 | " Try to use symbols.\n"); |
| 279 | break; | ||
| 267 | } | 280 | } |
| 268 | close(fd); | 281 | close(fd); |
| 269 | 282 | ||
| @@ -276,6 +289,7 @@ end_dwarf: | |||
| 276 | if (pp->found) /* This probe is already found. */ | 289 | if (pp->found) /* This probe is already found. */ |
| 277 | continue; | 290 | continue; |
| 278 | 291 | ||
| 292 | evaluate_probe_point(pp); | ||
| 279 | ret = synthesize_trace_kprobe_event(pp); | 293 | ret = synthesize_trace_kprobe_event(pp); |
| 280 | if (ret == -E2BIG) | 294 | if (ret == -E2BIG) |
| 281 | die("probe point definition becomes too long."); | 295 | die("probe point definition becomes too long."); |
| @@ -284,7 +298,8 @@ end_dwarf: | |||
| 284 | } | 298 | } |
| 285 | 299 | ||
| 286 | /* Settng up probe points */ | 300 | /* Settng up probe points */ |
| 287 | add_trace_kprobe_events(session.probes, session.nr_probe); | 301 | add_trace_kprobe_events(session.probes, session.nr_probe, |
| 302 | session.force_add); | ||
| 288 | return 0; | 303 | return 0; |
| 289 | } | 304 | } |
| 290 | 305 | ||
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4decbd14eaed..63136d0534d4 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -123,7 +123,8 @@ static void write_event(event_t *buf, size_t size) | |||
| 123 | write_output(buf, size); | 123 | write_output(buf, size); |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | static int process_synthesized_event(event_t *event) | 126 | static int process_synthesized_event(event_t *event, |
| 127 | struct perf_session *self __used) | ||
| 127 | { | 128 | { |
| 128 | write_event(event, event->header.size); | 129 | write_event(event, event->header.size); |
| 129 | return 0; | 130 | return 0; |
| @@ -277,7 +278,7 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
| 277 | 278 | ||
| 278 | attr->mmap = track; | 279 | attr->mmap = track; |
| 279 | attr->comm = track; | 280 | attr->comm = track; |
| 280 | attr->inherit = (cpu < 0) && inherit; | 281 | attr->inherit = inherit; |
| 281 | attr->disabled = 1; | 282 | attr->disabled = 1; |
| 282 | 283 | ||
| 283 | try_again: | 284 | try_again: |
| @@ -401,7 +402,7 @@ static void atexit_header(void) | |||
| 401 | perf_header__write(&session->header, output, true); | 402 | perf_header__write(&session->header, output, true); |
| 402 | } | 403 | } |
| 403 | 404 | ||
| 404 | static int __cmd_record(int argc, const char **argv) | 405 | static int __cmd_record(int argc __used, const char **argv) |
| 405 | { | 406 | { |
| 406 | int i, counter; | 407 | int i, counter; |
| 407 | struct stat st; | 408 | struct stat st; |
| @@ -409,6 +410,8 @@ static int __cmd_record(int argc, const char **argv) | |||
| 409 | int flags; | 410 | int flags; |
| 410 | int err; | 411 | int err; |
| 411 | unsigned long waking = 0; | 412 | unsigned long waking = 0; |
| 413 | int child_ready_pipe[2], go_pipe[2]; | ||
| 414 | char buf; | ||
| 412 | 415 | ||
| 413 | page_size = sysconf(_SC_PAGE_SIZE); | 416 | page_size = sysconf(_SC_PAGE_SIZE); |
| 414 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | 417 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); |
| @@ -419,11 +422,25 @@ static int __cmd_record(int argc, const char **argv) | |||
| 419 | signal(SIGCHLD, sig_handler); | 422 | signal(SIGCHLD, sig_handler); |
| 420 | signal(SIGINT, sig_handler); | 423 | signal(SIGINT, sig_handler); |
| 421 | 424 | ||
| 425 | if (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0) { | ||
| 426 | perror("failed to create pipes"); | ||
| 427 | exit(-1); | ||
| 428 | } | ||
| 429 | |||
| 422 | if (!stat(output_name, &st) && st.st_size) { | 430 | if (!stat(output_name, &st) && st.st_size) { |
| 423 | if (!force && !append_file) { | 431 | if (!force) { |
| 424 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", | 432 | if (!append_file) { |
| 425 | output_name); | 433 | pr_err("Error, output file %s exists, use -A " |
| 426 | exit(-1); | 434 | "to append or -f to overwrite.\n", |
| 435 | output_name); | ||
| 436 | exit(-1); | ||
| 437 | } | ||
| 438 | } else { | ||
| 439 | char oldname[PATH_MAX]; | ||
| 440 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
| 441 | output_name); | ||
| 442 | unlink(oldname); | ||
| 443 | rename(output_name, oldname); | ||
| 427 | } | 444 | } |
| 428 | } else { | 445 | } else { |
| 429 | append_file = 0; | 446 | append_file = 0; |
| @@ -466,19 +483,65 @@ static int __cmd_record(int argc, const char **argv) | |||
| 466 | 483 | ||
| 467 | atexit(atexit_header); | 484 | atexit(atexit_header); |
| 468 | 485 | ||
| 469 | if (!system_wide) { | 486 | if (target_pid == -1) { |
| 470 | pid = target_pid; | 487 | pid = fork(); |
| 471 | if (pid == -1) | 488 | if (pid < 0) { |
| 472 | pid = getpid(); | 489 | perror("failed to fork"); |
| 490 | exit(-1); | ||
| 491 | } | ||
| 473 | 492 | ||
| 474 | open_counters(profile_cpu, pid); | 493 | if (!pid) { |
| 475 | } else { | 494 | close(child_ready_pipe[0]); |
| 476 | if (profile_cpu != -1) { | 495 | close(go_pipe[1]); |
| 477 | open_counters(profile_cpu, target_pid); | 496 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); |
| 478 | } else { | 497 | |
| 479 | for (i = 0; i < nr_cpus; i++) | 498 | /* |
| 480 | open_counters(i, target_pid); | 499 | * Do a dummy execvp to get the PLT entry resolved, |
| 500 | * so we avoid the resolver overhead on the real | ||
| 501 | * execvp call. | ||
| 502 | */ | ||
| 503 | execvp("", (char **)argv); | ||
| 504 | |||
| 505 | /* | ||
| 506 | * Tell the parent we're ready to go | ||
| 507 | */ | ||
| 508 | close(child_ready_pipe[1]); | ||
| 509 | |||
| 510 | /* | ||
| 511 | * Wait until the parent tells us to go. | ||
| 512 | */ | ||
| 513 | if (read(go_pipe[0], &buf, 1) == -1) | ||
| 514 | perror("unable to read pipe"); | ||
| 515 | |||
| 516 | execvp(argv[0], (char **)argv); | ||
| 517 | |||
| 518 | perror(argv[0]); | ||
| 519 | exit(-1); | ||
| 481 | } | 520 | } |
| 521 | |||
| 522 | child_pid = pid; | ||
| 523 | |||
| 524 | if (!system_wide) | ||
| 525 | target_pid = pid; | ||
| 526 | |||
| 527 | close(child_ready_pipe[1]); | ||
| 528 | close(go_pipe[0]); | ||
| 529 | /* | ||
| 530 | * wait for child to settle | ||
| 531 | */ | ||
| 532 | if (read(child_ready_pipe[0], &buf, 1) == -1) { | ||
| 533 | perror("unable to read pipe"); | ||
| 534 | exit(-1); | ||
| 535 | } | ||
| 536 | close(child_ready_pipe[0]); | ||
| 537 | } | ||
| 538 | |||
| 539 | |||
| 540 | if ((!system_wide && !inherit) || profile_cpu != -1) { | ||
| 541 | open_counters(profile_cpu, target_pid); | ||
| 542 | } else { | ||
| 543 | for (i = 0; i < nr_cpus; i++) | ||
| 544 | open_counters(i, target_pid); | ||
| 482 | } | 545 | } |
| 483 | 546 | ||
| 484 | if (file_new) { | 547 | if (file_new) { |
| @@ -488,33 +551,10 @@ static int __cmd_record(int argc, const char **argv) | |||
| 488 | } | 551 | } |
| 489 | 552 | ||
| 490 | if (!system_wide) | 553 | if (!system_wide) |
| 491 | event__synthesize_thread(pid, process_synthesized_event); | 554 | event__synthesize_thread(pid, process_synthesized_event, |
| 555 | session); | ||
| 492 | else | 556 | else |
| 493 | event__synthesize_threads(process_synthesized_event); | 557 | event__synthesize_threads(process_synthesized_event, session); |
| 494 | |||
| 495 | if (target_pid == -1 && argc) { | ||
| 496 | pid = fork(); | ||
| 497 | if (pid < 0) | ||
| 498 | die("failed to fork"); | ||
| 499 | |||
| 500 | if (!pid) { | ||
| 501 | if (execvp(argv[0], (char **)argv)) { | ||
| 502 | perror(argv[0]); | ||
| 503 | exit(-1); | ||
| 504 | } | ||
| 505 | } else { | ||
| 506 | /* | ||
| 507 | * Wait a bit for the execv'ed child to appear | ||
| 508 | * and be updated in /proc | ||
| 509 | * FIXME: Do you know a less heuristical solution? | ||
| 510 | */ | ||
| 511 | usleep(1000); | ||
| 512 | event__synthesize_thread(pid, | ||
| 513 | process_synthesized_event); | ||
| 514 | } | ||
| 515 | |||
| 516 | child_pid = pid; | ||
| 517 | } | ||
| 518 | 558 | ||
| 519 | if (realtime_prio) { | 559 | if (realtime_prio) { |
| 520 | struct sched_param param; | 560 | struct sched_param param; |
| @@ -526,6 +566,11 @@ static int __cmd_record(int argc, const char **argv) | |||
| 526 | } | 566 | } |
| 527 | } | 567 | } |
| 528 | 568 | ||
| 569 | /* | ||
| 570 | * Let the child rip | ||
| 571 | */ | ||
| 572 | close(go_pipe[1]); | ||
| 573 | |||
| 529 | for (;;) { | 574 | for (;;) { |
| 530 | int hits = samples; | 575 | int hits = samples; |
| 531 | 576 | ||
| @@ -620,13 +665,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
| 620 | { | 665 | { |
| 621 | int counter; | 666 | int counter; |
| 622 | 667 | ||
| 623 | symbol__init(0); | ||
| 624 | |||
| 625 | argc = parse_options(argc, argv, options, record_usage, | 668 | argc = parse_options(argc, argv, options, record_usage, |
| 626 | PARSE_OPT_STOP_AT_NON_OPTION); | 669 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 627 | if (!argc && target_pid == -1 && !system_wide) | 670 | if (!argc && target_pid == -1 && (!system_wide || profile_cpu == -1)) |
| 628 | usage_with_options(record_usage, options); | 671 | usage_with_options(record_usage, options); |
| 629 | 672 | ||
| 673 | symbol__init(); | ||
| 674 | |||
| 630 | if (!nr_counters) { | 675 | if (!nr_counters) { |
| 631 | nr_counters = 1; | 676 | nr_counters = 1; |
| 632 | attrs[0].type = PERF_TYPE_HARDWARE; | 677 | attrs[0].type = PERF_TYPE_HARDWARE; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index e2ec49a9b731..e50a6b10ee6f 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -27,467 +27,41 @@ | |||
| 27 | #include "util/parse-options.h" | 27 | #include "util/parse-options.h" |
| 28 | #include "util/parse-events.h" | 28 | #include "util/parse-events.h" |
| 29 | 29 | ||
| 30 | #include "util/data_map.h" | ||
| 31 | #include "util/thread.h" | 30 | #include "util/thread.h" |
| 32 | #include "util/sort.h" | 31 | #include "util/sort.h" |
| 33 | #include "util/hist.h" | 32 | #include "util/hist.h" |
| 34 | 33 | ||
| 35 | static char const *input_name = "perf.data"; | 34 | static char const *input_name = "perf.data"; |
| 36 | 35 | ||
| 37 | static char *dso_list_str, *comm_list_str, *sym_list_str, | ||
| 38 | *col_width_list_str; | ||
| 39 | static struct strlist *dso_list, *comm_list, *sym_list; | ||
| 40 | |||
| 41 | static int force; | 36 | static int force; |
| 42 | 37 | ||
| 43 | static int full_paths; | ||
| 44 | static int show_nr_samples; | ||
| 45 | |||
| 46 | static int show_threads; | 38 | static int show_threads; |
| 47 | static struct perf_read_values show_threads_values; | 39 | static struct perf_read_values show_threads_values; |
| 48 | 40 | ||
| 49 | static char default_pretty_printing_style[] = "normal"; | 41 | static char default_pretty_printing_style[] = "normal"; |
| 50 | static char *pretty_printing_style = default_pretty_printing_style; | 42 | static char *pretty_printing_style = default_pretty_printing_style; |
| 51 | 43 | ||
| 52 | static int exclude_other = 1; | ||
| 53 | |||
| 54 | static char callchain_default_opt[] = "fractal,0.5"; | 44 | static char callchain_default_opt[] = "fractal,0.5"; |
| 55 | 45 | ||
| 56 | static struct perf_session *session; | 46 | static int perf_session__add_hist_entry(struct perf_session *self, |
| 57 | 47 | struct addr_location *al, | |
| 58 | static u64 sample_type; | 48 | struct ip_callchain *chain, u64 count) |
| 59 | |||
| 60 | struct symbol_conf symbol_conf; | ||
| 61 | |||
| 62 | |||
| 63 | static size_t | ||
| 64 | callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
| 65 | { | ||
| 66 | int i; | ||
| 67 | int ret; | ||
| 68 | |||
| 69 | ret = fprintf(fp, " "); | ||
| 70 | |||
| 71 | for (i = 0; i < left_margin; i++) | ||
| 72 | ret += fprintf(fp, " "); | ||
| 73 | |||
| 74 | return ret; | ||
| 75 | } | ||
| 76 | |||
| 77 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
| 78 | int left_margin) | ||
| 79 | { | ||
| 80 | int i; | ||
| 81 | size_t ret = 0; | ||
| 82 | |||
| 83 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 84 | |||
| 85 | for (i = 0; i < depth; i++) | ||
| 86 | if (depth_mask & (1 << i)) | ||
| 87 | ret += fprintf(fp, "| "); | ||
| 88 | else | ||
| 89 | ret += fprintf(fp, " "); | ||
| 90 | |||
| 91 | ret += fprintf(fp, "\n"); | ||
| 92 | |||
| 93 | return ret; | ||
| 94 | } | ||
| 95 | static size_t | ||
| 96 | ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | ||
| 97 | int depth_mask, int count, u64 total_samples, | ||
| 98 | int hits, int left_margin) | ||
| 99 | { | ||
| 100 | int i; | ||
| 101 | size_t ret = 0; | ||
| 102 | |||
| 103 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 104 | for (i = 0; i < depth; i++) { | ||
| 105 | if (depth_mask & (1 << i)) | ||
| 106 | ret += fprintf(fp, "|"); | ||
| 107 | else | ||
| 108 | ret += fprintf(fp, " "); | ||
| 109 | if (!count && i == depth - 1) { | ||
| 110 | double percent; | ||
| 111 | |||
| 112 | percent = hits * 100.0 / total_samples; | ||
| 113 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
| 114 | } else | ||
| 115 | ret += fprintf(fp, "%s", " "); | ||
| 116 | } | ||
| 117 | if (chain->sym) | ||
| 118 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
| 119 | else | ||
| 120 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
| 121 | |||
| 122 | return ret; | ||
| 123 | } | ||
| 124 | |||
| 125 | static struct symbol *rem_sq_bracket; | ||
| 126 | static struct callchain_list rem_hits; | ||
| 127 | |||
| 128 | static void init_rem_hits(void) | ||
| 129 | { | ||
| 130 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
| 131 | if (!rem_sq_bracket) { | ||
| 132 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | |||
| 136 | strcpy(rem_sq_bracket->name, "[...]"); | ||
| 137 | rem_hits.sym = rem_sq_bracket; | ||
| 138 | } | ||
| 139 | |||
| 140 | static size_t | ||
| 141 | __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
| 142 | u64 total_samples, int depth, int depth_mask, | ||
| 143 | int left_margin) | ||
| 144 | { | ||
| 145 | struct rb_node *node, *next; | ||
| 146 | struct callchain_node *child; | ||
| 147 | struct callchain_list *chain; | ||
| 148 | int new_depth_mask = depth_mask; | ||
| 149 | u64 new_total; | ||
| 150 | u64 remaining; | ||
| 151 | size_t ret = 0; | ||
| 152 | int i; | ||
| 153 | |||
| 154 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
| 155 | new_total = self->children_hit; | ||
| 156 | else | ||
| 157 | new_total = total_samples; | ||
| 158 | |||
| 159 | remaining = new_total; | ||
| 160 | |||
| 161 | node = rb_first(&self->rb_root); | ||
| 162 | while (node) { | ||
| 163 | u64 cumul; | ||
| 164 | |||
| 165 | child = rb_entry(node, struct callchain_node, rb_node); | ||
| 166 | cumul = cumul_hits(child); | ||
| 167 | remaining -= cumul; | ||
| 168 | |||
| 169 | /* | ||
| 170 | * The depth mask manages the output of pipes that show | ||
| 171 | * the depth. We don't want to keep the pipes of the current | ||
| 172 | * level for the last child of this depth. | ||
| 173 | * Except if we have remaining filtered hits. They will | ||
| 174 | * supersede the last child | ||
| 175 | */ | ||
| 176 | next = rb_next(node); | ||
| 177 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
| 178 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 179 | |||
| 180 | /* | ||
| 181 | * But we keep the older depth mask for the line seperator | ||
| 182 | * to keep the level link until we reach the last child | ||
| 183 | */ | ||
| 184 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
| 185 | left_margin); | ||
| 186 | i = 0; | ||
| 187 | list_for_each_entry(chain, &child->val, list) { | ||
| 188 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 189 | continue; | ||
| 190 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
| 191 | new_depth_mask, i++, | ||
| 192 | new_total, | ||
| 193 | cumul, | ||
| 194 | left_margin); | ||
| 195 | } | ||
| 196 | ret += __callchain__fprintf_graph(fp, child, new_total, | ||
| 197 | depth + 1, | ||
| 198 | new_depth_mask | (1 << depth), | ||
| 199 | left_margin); | ||
| 200 | node = next; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
| 204 | remaining && remaining != new_total) { | ||
| 205 | |||
| 206 | if (!rem_sq_bracket) | ||
| 207 | return ret; | ||
| 208 | |||
| 209 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 210 | |||
| 211 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
| 212 | new_depth_mask, 0, new_total, | ||
| 213 | remaining, left_margin); | ||
| 214 | } | ||
| 215 | |||
| 216 | return ret; | ||
| 217 | } | ||
| 218 | |||
| 219 | |||
| 220 | static size_t | ||
| 221 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
| 222 | u64 total_samples, int left_margin) | ||
| 223 | { | ||
| 224 | struct callchain_list *chain; | ||
| 225 | bool printed = false; | ||
| 226 | int i = 0; | ||
| 227 | int ret = 0; | ||
| 228 | |||
| 229 | list_for_each_entry(chain, &self->val, list) { | ||
| 230 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 231 | continue; | ||
| 232 | |||
| 233 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
| 234 | continue; | ||
| 235 | |||
| 236 | if (!printed) { | ||
| 237 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 238 | ret += fprintf(fp, "|\n"); | ||
| 239 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 240 | ret += fprintf(fp, "---"); | ||
| 241 | |||
| 242 | left_margin += 3; | ||
| 243 | printed = true; | ||
| 244 | } else | ||
| 245 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 246 | |||
| 247 | if (chain->sym) | ||
| 248 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
| 249 | else | ||
| 250 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
| 251 | } | ||
| 252 | |||
| 253 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | ||
| 254 | |||
| 255 | return ret; | ||
| 256 | } | ||
| 257 | |||
| 258 | static size_t | ||
| 259 | callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
| 260 | u64 total_samples) | ||
| 261 | { | ||
| 262 | struct callchain_list *chain; | ||
| 263 | size_t ret = 0; | ||
| 264 | |||
| 265 | if (!self) | ||
| 266 | return 0; | ||
| 267 | |||
| 268 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
| 269 | |||
| 270 | |||
| 271 | list_for_each_entry(chain, &self->val, list) { | ||
| 272 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 273 | continue; | ||
| 274 | if (chain->sym) | ||
| 275 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
| 276 | else | ||
| 277 | ret += fprintf(fp, " %p\n", | ||
| 278 | (void *)(long)chain->ip); | ||
| 279 | } | ||
| 280 | |||
| 281 | return ret; | ||
| 282 | } | ||
| 283 | |||
| 284 | static size_t | ||
| 285 | hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
| 286 | u64 total_samples, int left_margin) | ||
| 287 | { | ||
| 288 | struct rb_node *rb_node; | ||
| 289 | struct callchain_node *chain; | ||
| 290 | size_t ret = 0; | ||
| 291 | |||
| 292 | rb_node = rb_first(&self->sorted_chain); | ||
| 293 | while (rb_node) { | ||
| 294 | double percent; | ||
| 295 | |||
| 296 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
| 297 | percent = chain->hit * 100.0 / total_samples; | ||
| 298 | switch (callchain_param.mode) { | ||
| 299 | case CHAIN_FLAT: | ||
| 300 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
| 301 | percent); | ||
| 302 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
| 303 | break; | ||
| 304 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
| 305 | case CHAIN_GRAPH_REL: | ||
| 306 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
| 307 | left_margin); | ||
| 308 | case CHAIN_NONE: | ||
| 309 | default: | ||
| 310 | break; | ||
| 311 | } | ||
| 312 | ret += fprintf(fp, "\n"); | ||
| 313 | rb_node = rb_next(rb_node); | ||
| 314 | } | ||
| 315 | |||
| 316 | return ret; | ||
| 317 | } | ||
| 318 | |||
| 319 | static size_t | ||
| 320 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | ||
| 321 | { | ||
| 322 | struct sort_entry *se; | ||
| 323 | size_t ret; | ||
| 324 | |||
| 325 | if (exclude_other && !self->parent) | ||
| 326 | return 0; | ||
| 327 | |||
| 328 | if (total_samples) | ||
| 329 | ret = percent_color_fprintf(fp, | ||
| 330 | field_sep ? "%.2f" : " %6.2f%%", | ||
| 331 | (self->count * 100.0) / total_samples); | ||
| 332 | else | ||
| 333 | ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); | ||
| 334 | |||
| 335 | if (show_nr_samples) { | ||
| 336 | if (field_sep) | ||
| 337 | fprintf(fp, "%c%lld", *field_sep, self->count); | ||
| 338 | else | ||
| 339 | fprintf(fp, "%11lld", self->count); | ||
| 340 | } | ||
| 341 | |||
| 342 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 343 | if (se->elide) | ||
| 344 | continue; | ||
| 345 | |||
| 346 | fprintf(fp, "%s", field_sep ?: " "); | ||
| 347 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
| 348 | } | ||
| 349 | |||
| 350 | ret += fprintf(fp, "\n"); | ||
| 351 | |||
| 352 | if (callchain) { | ||
| 353 | int left_margin = 0; | ||
| 354 | |||
| 355 | if (sort__first_dimension == SORT_COMM) { | ||
| 356 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | ||
| 357 | list); | ||
| 358 | left_margin = se->width ? *se->width : 0; | ||
| 359 | left_margin -= thread__comm_len(self->thread); | ||
| 360 | } | ||
| 361 | |||
| 362 | hist_entry_callchain__fprintf(fp, self, total_samples, | ||
| 363 | left_margin); | ||
| 364 | } | ||
| 365 | |||
| 366 | return ret; | ||
| 367 | } | ||
| 368 | |||
| 369 | /* | ||
| 370 | * | ||
| 371 | */ | ||
| 372 | |||
| 373 | static void dso__calc_col_width(struct dso *self) | ||
| 374 | { | ||
| 375 | if (!col_width_list_str && !field_sep && | ||
| 376 | (!dso_list || strlist__has_entry(dso_list, self->name))) { | ||
| 377 | unsigned int slen = strlen(self->name); | ||
| 378 | if (slen > dsos__col_width) | ||
| 379 | dsos__col_width = slen; | ||
| 380 | } | ||
| 381 | |||
| 382 | self->slen_calculated = 1; | ||
| 383 | } | ||
| 384 | |||
| 385 | static void thread__comm_adjust(struct thread *self) | ||
| 386 | { | ||
| 387 | char *comm = self->comm; | ||
| 388 | |||
| 389 | if (!col_width_list_str && !field_sep && | ||
| 390 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
| 391 | unsigned int slen = strlen(comm); | ||
| 392 | |||
| 393 | if (slen > comms__col_width) { | ||
| 394 | comms__col_width = slen; | ||
| 395 | threads__col_width = slen + 6; | ||
| 396 | } | ||
| 397 | } | ||
| 398 | } | ||
| 399 | |||
| 400 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | ||
| 401 | { | ||
| 402 | int ret = thread__set_comm(self, comm); | ||
| 403 | |||
| 404 | if (ret) | ||
| 405 | return ret; | ||
| 406 | |||
| 407 | thread__comm_adjust(self); | ||
| 408 | |||
| 409 | return 0; | ||
| 410 | } | ||
| 411 | |||
| 412 | static int call__match(struct symbol *sym) | ||
| 413 | { | ||
| 414 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
| 415 | return 1; | ||
| 416 | |||
| 417 | return 0; | ||
| 418 | } | ||
| 419 | |||
| 420 | static struct symbol **resolve_callchain(struct thread *thread, | ||
| 421 | struct ip_callchain *chain, | ||
| 422 | struct symbol **parent) | ||
| 423 | { | ||
| 424 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
| 425 | struct symbol **syms = NULL; | ||
| 426 | unsigned int i; | ||
| 427 | |||
| 428 | if (callchain) { | ||
| 429 | syms = calloc(chain->nr, sizeof(*syms)); | ||
| 430 | if (!syms) { | ||
| 431 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
| 432 | exit(-1); | ||
| 433 | } | ||
| 434 | } | ||
| 435 | |||
| 436 | for (i = 0; i < chain->nr; i++) { | ||
| 437 | u64 ip = chain->ips[i]; | ||
| 438 | struct addr_location al; | ||
| 439 | |||
| 440 | if (ip >= PERF_CONTEXT_MAX) { | ||
| 441 | switch (ip) { | ||
| 442 | case PERF_CONTEXT_HV: | ||
| 443 | cpumode = PERF_RECORD_MISC_HYPERVISOR; break; | ||
| 444 | case PERF_CONTEXT_KERNEL: | ||
| 445 | cpumode = PERF_RECORD_MISC_KERNEL; break; | ||
| 446 | case PERF_CONTEXT_USER: | ||
| 447 | cpumode = PERF_RECORD_MISC_USER; break; | ||
| 448 | default: | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | continue; | ||
| 452 | } | ||
| 453 | |||
| 454 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, | ||
| 455 | ip, &al, NULL); | ||
| 456 | if (al.sym != NULL) { | ||
| 457 | if (sort__has_parent && !*parent && | ||
| 458 | call__match(al.sym)) | ||
| 459 | *parent = al.sym; | ||
| 460 | if (!callchain) | ||
| 461 | break; | ||
| 462 | syms[i] = al.sym; | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | return syms; | ||
| 467 | } | ||
| 468 | |||
| 469 | /* | ||
| 470 | * collect histogram counts | ||
| 471 | */ | ||
| 472 | |||
| 473 | static int hist_entry__add(struct addr_location *al, | ||
| 474 | struct ip_callchain *chain, u64 count) | ||
| 475 | { | 49 | { |
| 476 | struct symbol **syms = NULL, *parent = NULL; | 50 | struct symbol **syms = NULL, *parent = NULL; |
| 477 | bool hit; | 51 | bool hit; |
| 478 | struct hist_entry *he; | 52 | struct hist_entry *he; |
| 479 | 53 | ||
| 480 | if ((sort__has_parent || callchain) && chain) | 54 | if ((sort__has_parent || symbol_conf.use_callchain) && chain) |
| 481 | syms = resolve_callchain(al->thread, chain, &parent); | 55 | syms = perf_session__resolve_callchain(self, al->thread, |
| 482 | 56 | chain, &parent); | |
| 483 | he = __hist_entry__add(al, parent, count, &hit); | 57 | he = __perf_session__add_hist_entry(self, al, parent, count, &hit); |
| 484 | if (he == NULL) | 58 | if (he == NULL) |
| 485 | return -ENOMEM; | 59 | return -ENOMEM; |
| 486 | 60 | ||
| 487 | if (hit) | 61 | if (hit) |
| 488 | he->count += count; | 62 | he->count += count; |
| 489 | 63 | ||
| 490 | if (callchain) { | 64 | if (symbol_conf.use_callchain) { |
| 491 | if (!hit) | 65 | if (!hit) |
| 492 | callchain_init(&he->callchain); | 66 | callchain_init(&he->callchain); |
| 493 | append_chain(&he->callchain, chain, syms); | 67 | append_chain(&he->callchain, chain, syms); |
| @@ -497,100 +71,6 @@ static int hist_entry__add(struct addr_location *al, | |||
| 497 | return 0; | 71 | return 0; |
| 498 | } | 72 | } |
| 499 | 73 | ||
| 500 | static size_t output__fprintf(FILE *fp, u64 total_samples) | ||
| 501 | { | ||
| 502 | struct hist_entry *pos; | ||
| 503 | struct sort_entry *se; | ||
| 504 | struct rb_node *nd; | ||
| 505 | size_t ret = 0; | ||
| 506 | unsigned int width; | ||
| 507 | char *col_width = col_width_list_str; | ||
| 508 | int raw_printing_style; | ||
| 509 | |||
| 510 | raw_printing_style = !strcmp(pretty_printing_style, "raw"); | ||
| 511 | |||
| 512 | init_rem_hits(); | ||
| 513 | |||
| 514 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); | ||
| 515 | fprintf(fp, "#\n"); | ||
| 516 | |||
| 517 | fprintf(fp, "# Overhead"); | ||
| 518 | if (show_nr_samples) { | ||
| 519 | if (field_sep) | ||
| 520 | fprintf(fp, "%cSamples", *field_sep); | ||
| 521 | else | ||
| 522 | fputs(" Samples ", fp); | ||
| 523 | } | ||
| 524 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 525 | if (se->elide) | ||
| 526 | continue; | ||
| 527 | if (field_sep) { | ||
| 528 | fprintf(fp, "%c%s", *field_sep, se->header); | ||
| 529 | continue; | ||
| 530 | } | ||
| 531 | width = strlen(se->header); | ||
| 532 | if (se->width) { | ||
| 533 | if (col_width_list_str) { | ||
| 534 | if (col_width) { | ||
| 535 | *se->width = atoi(col_width); | ||
| 536 | col_width = strchr(col_width, ','); | ||
| 537 | if (col_width) | ||
| 538 | ++col_width; | ||
| 539 | } | ||
| 540 | } | ||
| 541 | width = *se->width = max(*se->width, width); | ||
| 542 | } | ||
| 543 | fprintf(fp, " %*s", width, se->header); | ||
| 544 | } | ||
| 545 | fprintf(fp, "\n"); | ||
| 546 | |||
| 547 | if (field_sep) | ||
| 548 | goto print_entries; | ||
| 549 | |||
| 550 | fprintf(fp, "# ........"); | ||
| 551 | if (show_nr_samples) | ||
| 552 | fprintf(fp, " .........."); | ||
| 553 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 554 | unsigned int i; | ||
| 555 | |||
| 556 | if (se->elide) | ||
| 557 | continue; | ||
| 558 | |||
| 559 | fprintf(fp, " "); | ||
| 560 | if (se->width) | ||
| 561 | width = *se->width; | ||
| 562 | else | ||
| 563 | width = strlen(se->header); | ||
| 564 | for (i = 0; i < width; i++) | ||
| 565 | fprintf(fp, "."); | ||
| 566 | } | ||
| 567 | fprintf(fp, "\n"); | ||
| 568 | |||
| 569 | fprintf(fp, "#\n"); | ||
| 570 | |||
| 571 | print_entries: | ||
| 572 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | ||
| 573 | pos = rb_entry(nd, struct hist_entry, rb_node); | ||
| 574 | ret += hist_entry__fprintf(fp, pos, total_samples); | ||
| 575 | } | ||
| 576 | |||
| 577 | if (sort_order == default_sort_order && | ||
| 578 | parent_pattern == default_parent_pattern) { | ||
| 579 | fprintf(fp, "#\n"); | ||
| 580 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); | ||
| 581 | fprintf(fp, "#\n"); | ||
| 582 | } | ||
| 583 | fprintf(fp, "\n"); | ||
| 584 | |||
| 585 | free(rem_sq_bracket); | ||
| 586 | |||
| 587 | if (show_threads) | ||
| 588 | perf_read_values_display(fp, &show_threads_values, | ||
| 589 | raw_printing_style); | ||
| 590 | |||
| 591 | return ret; | ||
| 592 | } | ||
| 593 | |||
| 594 | static int validate_chain(struct ip_callchain *chain, event_t *event) | 74 | static int validate_chain(struct ip_callchain *chain, event_t *event) |
| 595 | { | 75 | { |
| 596 | unsigned int chain_size; | 76 | unsigned int chain_size; |
| @@ -604,17 +84,12 @@ static int validate_chain(struct ip_callchain *chain, event_t *event) | |||
| 604 | return 0; | 84 | return 0; |
| 605 | } | 85 | } |
| 606 | 86 | ||
| 607 | static int process_sample_event(event_t *event) | 87 | static int process_sample_event(event_t *event, struct perf_session *session) |
| 608 | { | 88 | { |
| 609 | struct sample_data data; | 89 | struct sample_data data = { .period = 1, }; |
| 610 | int cpumode; | ||
| 611 | struct addr_location al; | 90 | struct addr_location al; |
| 612 | struct thread *thread; | ||
| 613 | |||
| 614 | memset(&data, 0, sizeof(data)); | ||
| 615 | data.period = 1; | ||
| 616 | 91 | ||
| 617 | event__parse_sample(event, sample_type, &data); | 92 | event__parse_sample(event, session->sample_type, &data); |
| 618 | 93 | ||
| 619 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 94 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", |
| 620 | event->header.misc, | 95 | event->header.misc, |
| @@ -622,7 +97,7 @@ static int process_sample_event(event_t *event) | |||
| 622 | (void *)(long)data.ip, | 97 | (void *)(long)data.ip, |
| 623 | (long long)data.period); | 98 | (long long)data.period); |
| 624 | 99 | ||
| 625 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { | 100 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { |
| 626 | unsigned int i; | 101 | unsigned int i; |
| 627 | 102 | ||
| 628 | dump_printf("... chain: nr:%Lu\n", data.callchain->nr); | 103 | dump_printf("... chain: nr:%Lu\n", data.callchain->nr); |
| @@ -640,65 +115,25 @@ static int process_sample_event(event_t *event) | |||
| 640 | } | 115 | } |
| 641 | } | 116 | } |
| 642 | 117 | ||
| 643 | thread = threads__findnew(data.pid); | 118 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { |
| 644 | if (thread == NULL) { | 119 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
| 645 | pr_debug("problem processing %d event, skipping it.\n", | ||
| 646 | event->header.type); | 120 | event->header.type); |
| 647 | return -1; | 121 | return -1; |
| 648 | } | 122 | } |
| 649 | 123 | ||
| 650 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 124 | if (al.filtered) |
| 651 | |||
| 652 | if (comm_list && !strlist__has_entry(comm_list, thread->comm)) | ||
| 653 | return 0; | ||
| 654 | |||
| 655 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
| 656 | |||
| 657 | thread__find_addr_location(thread, cpumode, | ||
| 658 | MAP__FUNCTION, data.ip, &al, NULL); | ||
| 659 | /* | ||
| 660 | * We have to do this here as we may have a dso with no symbol hit that | ||
| 661 | * has a name longer than the ones with symbols sampled. | ||
| 662 | */ | ||
| 663 | if (al.map && !sort_dso.elide && !al.map->dso->slen_calculated) | ||
| 664 | dso__calc_col_width(al.map->dso); | ||
| 665 | |||
| 666 | if (dso_list && | ||
| 667 | (!al.map || !al.map->dso || | ||
| 668 | !(strlist__has_entry(dso_list, al.map->dso->short_name) || | ||
| 669 | (al.map->dso->short_name != al.map->dso->long_name && | ||
| 670 | strlist__has_entry(dso_list, al.map->dso->long_name))))) | ||
| 671 | return 0; | ||
| 672 | |||
| 673 | if (sym_list && al.sym && !strlist__has_entry(sym_list, al.sym->name)) | ||
| 674 | return 0; | 125 | return 0; |
| 675 | 126 | ||
| 676 | if (hist_entry__add(&al, data.callchain, data.period)) { | 127 | if (perf_session__add_hist_entry(session, &al, data.callchain, data.period)) { |
| 677 | pr_debug("problem incrementing symbol count, skipping event\n"); | 128 | pr_debug("problem incrementing symbol count, skipping event\n"); |
| 678 | return -1; | 129 | return -1; |
| 679 | } | 130 | } |
| 680 | 131 | ||
| 681 | event__stats.total += data.period; | 132 | session->events_stats.total += data.period; |
| 682 | |||
| 683 | return 0; | 133 | return 0; |
| 684 | } | 134 | } |
| 685 | 135 | ||
| 686 | static int process_comm_event(event_t *event) | 136 | static int process_read_event(event_t *event, struct perf_session *session __used) |
| 687 | { | ||
| 688 | struct thread *thread = threads__findnew(event->comm.pid); | ||
| 689 | |||
| 690 | dump_printf(": %s:%d\n", event->comm.comm, event->comm.pid); | ||
| 691 | |||
| 692 | if (thread == NULL || | ||
| 693 | thread__set_comm_adjust(thread, event->comm.comm)) { | ||
| 694 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
| 695 | return -1; | ||
| 696 | } | ||
| 697 | |||
| 698 | return 0; | ||
| 699 | } | ||
| 700 | |||
| 701 | static int process_read_event(event_t *event) | ||
| 702 | { | 137 | { |
| 703 | struct perf_event_attr *attr; | 138 | struct perf_event_attr *attr; |
| 704 | 139 | ||
| @@ -721,25 +156,23 @@ static int process_read_event(event_t *event) | |||
| 721 | return 0; | 156 | return 0; |
| 722 | } | 157 | } |
| 723 | 158 | ||
| 724 | static int sample_type_check(u64 type) | 159 | static int sample_type_check(struct perf_session *session) |
| 725 | { | 160 | { |
| 726 | sample_type = type; | 161 | if (!(session->sample_type & PERF_SAMPLE_CALLCHAIN)) { |
| 727 | |||
| 728 | if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { | ||
| 729 | if (sort__has_parent) { | 162 | if (sort__has_parent) { |
| 730 | fprintf(stderr, "selected --sort parent, but no" | 163 | fprintf(stderr, "selected --sort parent, but no" |
| 731 | " callchain data. Did you call" | 164 | " callchain data. Did you call" |
| 732 | " perf record without -g?\n"); | 165 | " perf record without -g?\n"); |
| 733 | return -1; | 166 | return -1; |
| 734 | } | 167 | } |
| 735 | if (callchain) { | 168 | if (symbol_conf.use_callchain) { |
| 736 | fprintf(stderr, "selected -g but no callchain data." | 169 | fprintf(stderr, "selected -g but no callchain data." |
| 737 | " Did you call perf record without" | 170 | " Did you call perf record without" |
| 738 | " -g?\n"); | 171 | " -g?\n"); |
| 739 | return -1; | 172 | return -1; |
| 740 | } | 173 | } |
| 741 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { | 174 | } else if (callchain_param.mode != CHAIN_NONE && !symbol_conf.use_callchain) { |
| 742 | callchain = 1; | 175 | symbol_conf.use_callchain = true; |
| 743 | if (register_callchain_param(&callchain_param) < 0) { | 176 | if (register_callchain_param(&callchain_param) < 0) { |
| 744 | fprintf(stderr, "Can't register callchain" | 177 | fprintf(stderr, "Can't register callchain" |
| 745 | " params\n"); | 178 | " params\n"); |
| @@ -750,10 +183,10 @@ static int sample_type_check(u64 type) | |||
| 750 | return 0; | 183 | return 0; |
| 751 | } | 184 | } |
| 752 | 185 | ||
| 753 | static struct perf_file_handler file_handler = { | 186 | static struct perf_event_ops event_ops = { |
| 754 | .process_sample_event = process_sample_event, | 187 | .process_sample_event = process_sample_event, |
| 755 | .process_mmap_event = event__process_mmap, | 188 | .process_mmap_event = event__process_mmap, |
| 756 | .process_comm_event = process_comm_event, | 189 | .process_comm_event = event__process_comm, |
| 757 | .process_exit_event = event__process_task, | 190 | .process_exit_event = event__process_task, |
| 758 | .process_fork_event = event__process_task, | 191 | .process_fork_event = event__process_task, |
| 759 | .process_lost_event = event__process_lost, | 192 | .process_lost_event = event__process_lost, |
| @@ -764,23 +197,17 @@ static struct perf_file_handler file_handler = { | |||
| 764 | 197 | ||
| 765 | static int __cmd_report(void) | 198 | static int __cmd_report(void) |
| 766 | { | 199 | { |
| 767 | struct thread *idle; | ||
| 768 | int ret; | 200 | int ret; |
| 201 | struct perf_session *session; | ||
| 769 | 202 | ||
| 770 | session = perf_session__new(input_name, O_RDONLY, force); | 203 | session = perf_session__new(input_name, O_RDONLY, force); |
| 771 | if (session == NULL) | 204 | if (session == NULL) |
| 772 | return -ENOMEM; | 205 | return -ENOMEM; |
| 773 | 206 | ||
| 774 | idle = register_idle_thread(); | ||
| 775 | thread__comm_adjust(idle); | ||
| 776 | |||
| 777 | if (show_threads) | 207 | if (show_threads) |
| 778 | perf_read_values_init(&show_threads_values); | 208 | perf_read_values_init(&show_threads_values); |
| 779 | 209 | ||
| 780 | register_perf_file_handler(&file_handler); | 210 | ret = perf_session__process_events(session, &event_ops); |
| 781 | |||
| 782 | ret = perf_session__process_events(session, full_paths, | ||
| 783 | &event__cwdlen, &event__cwd); | ||
| 784 | if (ret) | 211 | if (ret) |
| 785 | goto out_delete; | 212 | goto out_delete; |
| 786 | 213 | ||
| @@ -790,17 +217,25 @@ static int __cmd_report(void) | |||
| 790 | } | 217 | } |
| 791 | 218 | ||
| 792 | if (verbose > 3) | 219 | if (verbose > 3) |
| 793 | threads__fprintf(stdout); | 220 | perf_session__fprintf(session, stdout); |
| 794 | 221 | ||
| 795 | if (verbose > 2) | 222 | if (verbose > 2) |
| 796 | dsos__fprintf(stdout); | 223 | dsos__fprintf(stdout); |
| 797 | 224 | ||
| 798 | collapse__resort(); | 225 | perf_session__collapse_resort(session); |
| 799 | output__resort(event__stats.total); | 226 | perf_session__output_resort(session, session->events_stats.total); |
| 800 | output__fprintf(stdout, event__stats.total); | 227 | fprintf(stdout, "# Samples: %ld\n#\n", session->events_stats.total); |
| 228 | perf_session__fprintf_hists(session, NULL, false, stdout); | ||
| 229 | if (sort_order == default_sort_order && | ||
| 230 | parent_pattern == default_parent_pattern) | ||
| 231 | fprintf(stdout, "#\n# (For a higher level overview, try: perf report --sort comm,dso)\n#\n"); | ||
| 801 | 232 | ||
| 802 | if (show_threads) | 233 | if (show_threads) { |
| 234 | bool raw_printing_style = !strcmp(pretty_printing_style, "raw"); | ||
| 235 | perf_read_values_display(stdout, &show_threads_values, | ||
| 236 | raw_printing_style); | ||
| 803 | perf_read_values_destroy(&show_threads_values); | 237 | perf_read_values_destroy(&show_threads_values); |
| 238 | } | ||
| 804 | out_delete: | 239 | out_delete: |
| 805 | perf_session__delete(session); | 240 | perf_session__delete(session); |
| 806 | return ret; | 241 | return ret; |
| @@ -813,7 +248,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
| 813 | char *tok; | 248 | char *tok; |
| 814 | char *endptr; | 249 | char *endptr; |
| 815 | 250 | ||
| 816 | callchain = 1; | 251 | symbol_conf.use_callchain = true; |
| 817 | 252 | ||
| 818 | if (!arg) | 253 | if (!arg) |
| 819 | return 0; | 254 | return 0; |
| @@ -834,7 +269,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
| 834 | 269 | ||
| 835 | else if (!strncmp(tok, "none", strlen(arg))) { | 270 | else if (!strncmp(tok, "none", strlen(arg))) { |
| 836 | callchain_param.mode = CHAIN_NONE; | 271 | callchain_param.mode = CHAIN_NONE; |
| 837 | callchain = 0; | 272 | symbol_conf.use_callchain = true; |
| 838 | 273 | ||
| 839 | return 0; | 274 | return 0; |
| 840 | } | 275 | } |
| @@ -877,7 +312,7 @@ static const struct option options[] = { | |||
| 877 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 312 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
| 878 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | 313 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, |
| 879 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 314 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
| 880 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | 315 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
| 881 | "Show a column with the number of samples"), | 316 | "Show a column with the number of samples"), |
| 882 | OPT_BOOLEAN('T', "threads", &show_threads, | 317 | OPT_BOOLEAN('T', "threads", &show_threads, |
| 883 | "Show per-thread event counters"), | 318 | "Show per-thread event counters"), |
| @@ -885,78 +320,46 @@ static const struct option options[] = { | |||
| 885 | "pretty printing style key: normal raw"), | 320 | "pretty printing style key: normal raw"), |
| 886 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 321 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
| 887 | "sort by key(s): pid, comm, dso, symbol, parent"), | 322 | "sort by key(s): pid, comm, dso, symbol, parent"), |
| 888 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 323 | OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths, |
| 889 | "Don't shorten the pathnames taking into account the cwd"), | 324 | "Don't shorten the pathnames taking into account the cwd"), |
| 890 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 325 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
| 891 | "regex filter to identify parent, see: '--sort parent'"), | 326 | "regex filter to identify parent, see: '--sort parent'"), |
| 892 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 327 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
| 893 | "Only display entries with parent-match"), | 328 | "Only display entries with parent-match"), |
| 894 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", | 329 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", |
| 895 | "Display callchains using output_type and min percent threshold. " | 330 | "Display callchains using output_type and min percent threshold. " |
| 896 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), | 331 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), |
| 897 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", | 332 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
| 898 | "only consider symbols in these dsos"), | 333 | "only consider symbols in these dsos"), |
| 899 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", | 334 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
| 900 | "only consider symbols in these comms"), | 335 | "only consider symbols in these comms"), |
| 901 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", | 336 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
| 902 | "only consider these symbols"), | 337 | "only consider these symbols"), |
| 903 | OPT_STRING('w', "column-widths", &col_width_list_str, | 338 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, |
| 904 | "width[,width...]", | 339 | "width[,width...]", |
| 905 | "don't try to adjust column width, use these fixed values"), | 340 | "don't try to adjust column width, use these fixed values"), |
| 906 | OPT_STRING('t', "field-separator", &field_sep, "separator", | 341 | OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator", |
| 907 | "separator for columns, no spaces will be added between " | 342 | "separator for columns, no spaces will be added between " |
| 908 | "columns '.' is reserved."), | 343 | "columns '.' is reserved."), |
| 909 | OPT_END() | 344 | OPT_END() |
| 910 | }; | 345 | }; |
| 911 | 346 | ||
| 912 | static void setup_sorting(void) | 347 | int cmd_report(int argc, const char **argv, const char *prefix __used) |
| 913 | { | 348 | { |
| 914 | char *tmp, *tok, *str = strdup(sort_order); | 349 | argc = parse_options(argc, argv, options, report_usage, 0); |
| 915 | |||
| 916 | for (tok = strtok_r(str, ", ", &tmp); | ||
| 917 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
| 918 | if (sort_dimension__add(tok) < 0) { | ||
| 919 | error("Unknown --sort key: `%s'", tok); | ||
| 920 | usage_with_options(report_usage, options); | ||
| 921 | } | ||
| 922 | } | ||
| 923 | |||
| 924 | free(str); | ||
| 925 | } | ||
| 926 | 350 | ||
| 927 | static void setup_list(struct strlist **list, const char *list_str, | 351 | setup_pager(); |
| 928 | struct sort_entry *se, const char *list_name, | ||
| 929 | FILE *fp) | ||
| 930 | { | ||
| 931 | if (list_str) { | ||
| 932 | *list = strlist__new(true, list_str); | ||
| 933 | if (!*list) { | ||
| 934 | fprintf(stderr, "problems parsing %s list\n", | ||
| 935 | list_name); | ||
| 936 | exit(129); | ||
| 937 | } | ||
| 938 | if (strlist__nr_entries(*list) == 1) { | ||
| 939 | fprintf(fp, "# %s: %s\n", list_name, | ||
| 940 | strlist__entry(*list, 0)->s); | ||
| 941 | se->elide = true; | ||
| 942 | } | ||
| 943 | } | ||
| 944 | } | ||
| 945 | 352 | ||
| 946 | int cmd_report(int argc, const char **argv, const char *prefix __used) | 353 | if (symbol__init() < 0) |
| 947 | { | ||
| 948 | if (symbol__init(&symbol_conf) < 0) | ||
| 949 | return -1; | 354 | return -1; |
| 950 | 355 | ||
| 951 | argc = parse_options(argc, argv, options, report_usage, 0); | 356 | setup_sorting(report_usage, options); |
| 952 | |||
| 953 | setup_sorting(); | ||
| 954 | 357 | ||
| 955 | if (parent_pattern != default_parent_pattern) { | 358 | if (parent_pattern != default_parent_pattern) { |
| 956 | sort_dimension__add("parent"); | 359 | sort_dimension__add("parent"); |
| 957 | sort_parent.elide = 1; | 360 | sort_parent.elide = 1; |
| 958 | } else | 361 | } else |
| 959 | exclude_other = 0; | 362 | symbol_conf.exclude_other = false; |
| 960 | 363 | ||
| 961 | /* | 364 | /* |
| 962 | * Any (unrecognized) arguments left? | 365 | * Any (unrecognized) arguments left? |
| @@ -964,17 +367,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 964 | if (argc) | 367 | if (argc) |
| 965 | usage_with_options(report_usage, options); | 368 | usage_with_options(report_usage, options); |
| 966 | 369 | ||
| 967 | setup_pager(); | 370 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); |
| 968 | 371 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | |
| 969 | setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); | 372 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); |
| 970 | setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); | ||
| 971 | setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); | ||
| 972 | |||
| 973 | if (field_sep && *field_sep == '.') { | ||
| 974 | fputs("'.' is the only non valid --field-separator argument\n", | ||
| 975 | stderr); | ||
| 976 | exit(129); | ||
| 977 | } | ||
| 978 | 373 | ||
| 979 | return __cmd_report(); | 374 | return __cmd_report(); |
| 980 | } | 375 | } |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 65021fe1361e..80209df6cfe8 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
| @@ -12,7 +12,6 @@ | |||
| 12 | #include "util/trace-event.h" | 12 | #include "util/trace-event.h" |
| 13 | 13 | ||
| 14 | #include "util/debug.h" | 14 | #include "util/debug.h" |
| 15 | #include "util/data_map.h" | ||
| 16 | 15 | ||
| 17 | #include <sys/prctl.h> | 16 | #include <sys/prctl.h> |
| 18 | 17 | ||
| @@ -22,8 +21,6 @@ | |||
| 22 | 21 | ||
| 23 | static char const *input_name = "perf.data"; | 22 | static char const *input_name = "perf.data"; |
| 24 | 23 | ||
| 25 | static u64 sample_type; | ||
| 26 | |||
| 27 | static char default_sort_order[] = "avg, max, switch, runtime"; | 24 | static char default_sort_order[] = "avg, max, switch, runtime"; |
| 28 | static char *sort_order = default_sort_order; | 25 | static char *sort_order = default_sort_order; |
| 29 | 26 | ||
| @@ -731,18 +728,21 @@ struct trace_migrate_task_event { | |||
| 731 | 728 | ||
| 732 | struct trace_sched_handler { | 729 | struct trace_sched_handler { |
| 733 | void (*switch_event)(struct trace_switch_event *, | 730 | void (*switch_event)(struct trace_switch_event *, |
| 731 | struct perf_session *, | ||
| 734 | struct event *, | 732 | struct event *, |
| 735 | int cpu, | 733 | int cpu, |
| 736 | u64 timestamp, | 734 | u64 timestamp, |
| 737 | struct thread *thread); | 735 | struct thread *thread); |
| 738 | 736 | ||
| 739 | void (*runtime_event)(struct trace_runtime_event *, | 737 | void (*runtime_event)(struct trace_runtime_event *, |
| 738 | struct perf_session *, | ||
| 740 | struct event *, | 739 | struct event *, |
| 741 | int cpu, | 740 | int cpu, |
| 742 | u64 timestamp, | 741 | u64 timestamp, |
| 743 | struct thread *thread); | 742 | struct thread *thread); |
| 744 | 743 | ||
| 745 | void (*wakeup_event)(struct trace_wakeup_event *, | 744 | void (*wakeup_event)(struct trace_wakeup_event *, |
| 745 | struct perf_session *, | ||
| 746 | struct event *, | 746 | struct event *, |
| 747 | int cpu, | 747 | int cpu, |
| 748 | u64 timestamp, | 748 | u64 timestamp, |
| @@ -755,6 +755,7 @@ struct trace_sched_handler { | |||
| 755 | struct thread *thread); | 755 | struct thread *thread); |
| 756 | 756 | ||
| 757 | void (*migrate_task_event)(struct trace_migrate_task_event *, | 757 | void (*migrate_task_event)(struct trace_migrate_task_event *, |
| 758 | struct perf_session *session, | ||
| 758 | struct event *, | 759 | struct event *, |
| 759 | int cpu, | 760 | int cpu, |
| 760 | u64 timestamp, | 761 | u64 timestamp, |
| @@ -764,6 +765,7 @@ struct trace_sched_handler { | |||
| 764 | 765 | ||
| 765 | static void | 766 | static void |
| 766 | replay_wakeup_event(struct trace_wakeup_event *wakeup_event, | 767 | replay_wakeup_event(struct trace_wakeup_event *wakeup_event, |
| 768 | struct perf_session *session __used, | ||
| 767 | struct event *event, | 769 | struct event *event, |
| 768 | int cpu __used, | 770 | int cpu __used, |
| 769 | u64 timestamp __used, | 771 | u64 timestamp __used, |
| @@ -790,6 +792,7 @@ static u64 cpu_last_switched[MAX_CPUS]; | |||
| 790 | 792 | ||
| 791 | static void | 793 | static void |
| 792 | replay_switch_event(struct trace_switch_event *switch_event, | 794 | replay_switch_event(struct trace_switch_event *switch_event, |
| 795 | struct perf_session *session __used, | ||
| 793 | struct event *event, | 796 | struct event *event, |
| 794 | int cpu, | 797 | int cpu, |
| 795 | u64 timestamp, | 798 | u64 timestamp, |
| @@ -1023,6 +1026,7 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp) | |||
| 1023 | 1026 | ||
| 1024 | static void | 1027 | static void |
| 1025 | latency_switch_event(struct trace_switch_event *switch_event, | 1028 | latency_switch_event(struct trace_switch_event *switch_event, |
| 1029 | struct perf_session *session, | ||
| 1026 | struct event *event __used, | 1030 | struct event *event __used, |
| 1027 | int cpu, | 1031 | int cpu, |
| 1028 | u64 timestamp, | 1032 | u64 timestamp, |
| @@ -1046,8 +1050,8 @@ latency_switch_event(struct trace_switch_event *switch_event, | |||
| 1046 | die("hm, delta: %Ld < 0 ?\n", delta); | 1050 | die("hm, delta: %Ld < 0 ?\n", delta); |
| 1047 | 1051 | ||
| 1048 | 1052 | ||
| 1049 | sched_out = threads__findnew(switch_event->prev_pid); | 1053 | sched_out = perf_session__findnew(session, switch_event->prev_pid); |
| 1050 | sched_in = threads__findnew(switch_event->next_pid); | 1054 | sched_in = perf_session__findnew(session, switch_event->next_pid); |
| 1051 | 1055 | ||
| 1052 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); | 1056 | out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid); |
| 1053 | if (!out_events) { | 1057 | if (!out_events) { |
| @@ -1075,12 +1079,13 @@ latency_switch_event(struct trace_switch_event *switch_event, | |||
| 1075 | 1079 | ||
| 1076 | static void | 1080 | static void |
| 1077 | latency_runtime_event(struct trace_runtime_event *runtime_event, | 1081 | latency_runtime_event(struct trace_runtime_event *runtime_event, |
| 1082 | struct perf_session *session, | ||
| 1078 | struct event *event __used, | 1083 | struct event *event __used, |
| 1079 | int cpu, | 1084 | int cpu, |
| 1080 | u64 timestamp, | 1085 | u64 timestamp, |
| 1081 | struct thread *this_thread __used) | 1086 | struct thread *this_thread __used) |
| 1082 | { | 1087 | { |
| 1083 | struct thread *thread = threads__findnew(runtime_event->pid); | 1088 | struct thread *thread = perf_session__findnew(session, runtime_event->pid); |
| 1084 | struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); | 1089 | struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid); |
| 1085 | 1090 | ||
| 1086 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); | 1091 | BUG_ON(cpu >= MAX_CPUS || cpu < 0); |
| @@ -1097,6 +1102,7 @@ latency_runtime_event(struct trace_runtime_event *runtime_event, | |||
| 1097 | 1102 | ||
| 1098 | static void | 1103 | static void |
| 1099 | latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | 1104 | latency_wakeup_event(struct trace_wakeup_event *wakeup_event, |
| 1105 | struct perf_session *session, | ||
| 1100 | struct event *__event __used, | 1106 | struct event *__event __used, |
| 1101 | int cpu __used, | 1107 | int cpu __used, |
| 1102 | u64 timestamp, | 1108 | u64 timestamp, |
| @@ -1110,7 +1116,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | |||
| 1110 | if (!wakeup_event->success) | 1116 | if (!wakeup_event->success) |
| 1111 | return; | 1117 | return; |
| 1112 | 1118 | ||
| 1113 | wakee = threads__findnew(wakeup_event->pid); | 1119 | wakee = perf_session__findnew(session, wakeup_event->pid); |
| 1114 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); | 1120 | atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid); |
| 1115 | if (!atoms) { | 1121 | if (!atoms) { |
| 1116 | thread_atoms_insert(wakee); | 1122 | thread_atoms_insert(wakee); |
| @@ -1144,6 +1150,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event, | |||
| 1144 | 1150 | ||
| 1145 | static void | 1151 | static void |
| 1146 | latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, | 1152 | latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, |
| 1153 | struct perf_session *session, | ||
| 1147 | struct event *__event __used, | 1154 | struct event *__event __used, |
| 1148 | int cpu __used, | 1155 | int cpu __used, |
| 1149 | u64 timestamp, | 1156 | u64 timestamp, |
| @@ -1159,7 +1166,7 @@ latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, | |||
| 1159 | if (profile_cpu == -1) | 1166 | if (profile_cpu == -1) |
| 1160 | return; | 1167 | return; |
| 1161 | 1168 | ||
| 1162 | migrant = threads__findnew(migrate_task_event->pid); | 1169 | migrant = perf_session__findnew(session, migrate_task_event->pid); |
| 1163 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); | 1170 | atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid); |
| 1164 | if (!atoms) { | 1171 | if (!atoms) { |
| 1165 | thread_atoms_insert(migrant); | 1172 | thread_atoms_insert(migrant); |
| @@ -1354,7 +1361,7 @@ static void sort_lat(void) | |||
| 1354 | static struct trace_sched_handler *trace_handler; | 1361 | static struct trace_sched_handler *trace_handler; |
| 1355 | 1362 | ||
| 1356 | static void | 1363 | static void |
| 1357 | process_sched_wakeup_event(void *data, | 1364 | process_sched_wakeup_event(void *data, struct perf_session *session, |
| 1358 | struct event *event, | 1365 | struct event *event, |
| 1359 | int cpu __used, | 1366 | int cpu __used, |
| 1360 | u64 timestamp __used, | 1367 | u64 timestamp __used, |
| @@ -1371,7 +1378,8 @@ process_sched_wakeup_event(void *data, | |||
| 1371 | FILL_FIELD(wakeup_event, cpu, event, data); | 1378 | FILL_FIELD(wakeup_event, cpu, event, data); |
| 1372 | 1379 | ||
| 1373 | if (trace_handler->wakeup_event) | 1380 | if (trace_handler->wakeup_event) |
| 1374 | trace_handler->wakeup_event(&wakeup_event, event, cpu, timestamp, thread); | 1381 | trace_handler->wakeup_event(&wakeup_event, session, event, |
| 1382 | cpu, timestamp, thread); | ||
| 1375 | } | 1383 | } |
| 1376 | 1384 | ||
| 1377 | /* | 1385 | /* |
| @@ -1389,6 +1397,7 @@ static char next_shortname2 = '0'; | |||
| 1389 | 1397 | ||
| 1390 | static void | 1398 | static void |
| 1391 | map_switch_event(struct trace_switch_event *switch_event, | 1399 | map_switch_event(struct trace_switch_event *switch_event, |
| 1400 | struct perf_session *session, | ||
| 1392 | struct event *event __used, | 1401 | struct event *event __used, |
| 1393 | int this_cpu, | 1402 | int this_cpu, |
| 1394 | u64 timestamp, | 1403 | u64 timestamp, |
| @@ -1416,8 +1425,8 @@ map_switch_event(struct trace_switch_event *switch_event, | |||
| 1416 | die("hm, delta: %Ld < 0 ?\n", delta); | 1425 | die("hm, delta: %Ld < 0 ?\n", delta); |
| 1417 | 1426 | ||
| 1418 | 1427 | ||
| 1419 | sched_out = threads__findnew(switch_event->prev_pid); | 1428 | sched_out = perf_session__findnew(session, switch_event->prev_pid); |
| 1420 | sched_in = threads__findnew(switch_event->next_pid); | 1429 | sched_in = perf_session__findnew(session, switch_event->next_pid); |
| 1421 | 1430 | ||
| 1422 | curr_thread[this_cpu] = sched_in; | 1431 | curr_thread[this_cpu] = sched_in; |
| 1423 | 1432 | ||
| @@ -1467,7 +1476,7 @@ map_switch_event(struct trace_switch_event *switch_event, | |||
| 1467 | 1476 | ||
| 1468 | 1477 | ||
| 1469 | static void | 1478 | static void |
| 1470 | process_sched_switch_event(void *data, | 1479 | process_sched_switch_event(void *data, struct perf_session *session, |
| 1471 | struct event *event, | 1480 | struct event *event, |
| 1472 | int this_cpu, | 1481 | int this_cpu, |
| 1473 | u64 timestamp __used, | 1482 | u64 timestamp __used, |
| @@ -1494,13 +1503,14 @@ process_sched_switch_event(void *data, | |||
| 1494 | nr_context_switch_bugs++; | 1503 | nr_context_switch_bugs++; |
| 1495 | } | 1504 | } |
| 1496 | if (trace_handler->switch_event) | 1505 | if (trace_handler->switch_event) |
| 1497 | trace_handler->switch_event(&switch_event, event, this_cpu, timestamp, thread); | 1506 | trace_handler->switch_event(&switch_event, session, event, |
| 1507 | this_cpu, timestamp, thread); | ||
| 1498 | 1508 | ||
| 1499 | curr_pid[this_cpu] = switch_event.next_pid; | 1509 | curr_pid[this_cpu] = switch_event.next_pid; |
| 1500 | } | 1510 | } |
| 1501 | 1511 | ||
| 1502 | static void | 1512 | static void |
| 1503 | process_sched_runtime_event(void *data, | 1513 | process_sched_runtime_event(void *data, struct perf_session *session, |
| 1504 | struct event *event, | 1514 | struct event *event, |
| 1505 | int cpu __used, | 1515 | int cpu __used, |
| 1506 | u64 timestamp __used, | 1516 | u64 timestamp __used, |
| @@ -1514,7 +1524,7 @@ process_sched_runtime_event(void *data, | |||
| 1514 | FILL_FIELD(runtime_event, vruntime, event, data); | 1524 | FILL_FIELD(runtime_event, vruntime, event, data); |
| 1515 | 1525 | ||
| 1516 | if (trace_handler->runtime_event) | 1526 | if (trace_handler->runtime_event) |
| 1517 | trace_handler->runtime_event(&runtime_event, event, cpu, timestamp, thread); | 1527 | trace_handler->runtime_event(&runtime_event, session, event, cpu, timestamp, thread); |
| 1518 | } | 1528 | } |
| 1519 | 1529 | ||
| 1520 | static void | 1530 | static void |
| @@ -1534,7 +1544,8 @@ process_sched_fork_event(void *data, | |||
| 1534 | FILL_FIELD(fork_event, child_pid, event, data); | 1544 | FILL_FIELD(fork_event, child_pid, event, data); |
| 1535 | 1545 | ||
| 1536 | if (trace_handler->fork_event) | 1546 | if (trace_handler->fork_event) |
| 1537 | trace_handler->fork_event(&fork_event, event, cpu, timestamp, thread); | 1547 | trace_handler->fork_event(&fork_event, event, |
| 1548 | cpu, timestamp, thread); | ||
| 1538 | } | 1549 | } |
| 1539 | 1550 | ||
| 1540 | static void | 1551 | static void |
| @@ -1548,7 +1559,7 @@ process_sched_exit_event(struct event *event, | |||
| 1548 | } | 1559 | } |
| 1549 | 1560 | ||
| 1550 | static void | 1561 | static void |
| 1551 | process_sched_migrate_task_event(void *data, | 1562 | process_sched_migrate_task_event(void *data, struct perf_session *session, |
| 1552 | struct event *event, | 1563 | struct event *event, |
| 1553 | int cpu __used, | 1564 | int cpu __used, |
| 1554 | u64 timestamp __used, | 1565 | u64 timestamp __used, |
| @@ -1564,12 +1575,13 @@ process_sched_migrate_task_event(void *data, | |||
| 1564 | FILL_FIELD(migrate_task_event, cpu, event, data); | 1575 | FILL_FIELD(migrate_task_event, cpu, event, data); |
| 1565 | 1576 | ||
| 1566 | if (trace_handler->migrate_task_event) | 1577 | if (trace_handler->migrate_task_event) |
| 1567 | trace_handler->migrate_task_event(&migrate_task_event, event, cpu, timestamp, thread); | 1578 | trace_handler->migrate_task_event(&migrate_task_event, session, |
| 1579 | event, cpu, timestamp, thread); | ||
| 1568 | } | 1580 | } |
| 1569 | 1581 | ||
| 1570 | static void | 1582 | static void |
| 1571 | process_raw_event(event_t *raw_event __used, void *data, | 1583 | process_raw_event(event_t *raw_event __used, struct perf_session *session, |
| 1572 | int cpu, u64 timestamp, struct thread *thread) | 1584 | void *data, int cpu, u64 timestamp, struct thread *thread) |
| 1573 | { | 1585 | { |
| 1574 | struct event *event; | 1586 | struct event *event; |
| 1575 | int type; | 1587 | int type; |
| @@ -1579,27 +1591,27 @@ process_raw_event(event_t *raw_event __used, void *data, | |||
| 1579 | event = trace_find_event(type); | 1591 | event = trace_find_event(type); |
| 1580 | 1592 | ||
| 1581 | if (!strcmp(event->name, "sched_switch")) | 1593 | if (!strcmp(event->name, "sched_switch")) |
| 1582 | process_sched_switch_event(data, event, cpu, timestamp, thread); | 1594 | process_sched_switch_event(data, session, event, cpu, timestamp, thread); |
| 1583 | if (!strcmp(event->name, "sched_stat_runtime")) | 1595 | if (!strcmp(event->name, "sched_stat_runtime")) |
| 1584 | process_sched_runtime_event(data, event, cpu, timestamp, thread); | 1596 | process_sched_runtime_event(data, session, event, cpu, timestamp, thread); |
| 1585 | if (!strcmp(event->name, "sched_wakeup")) | 1597 | if (!strcmp(event->name, "sched_wakeup")) |
| 1586 | process_sched_wakeup_event(data, event, cpu, timestamp, thread); | 1598 | process_sched_wakeup_event(data, session, event, cpu, timestamp, thread); |
| 1587 | if (!strcmp(event->name, "sched_wakeup_new")) | 1599 | if (!strcmp(event->name, "sched_wakeup_new")) |
| 1588 | process_sched_wakeup_event(data, event, cpu, timestamp, thread); | 1600 | process_sched_wakeup_event(data, session, event, cpu, timestamp, thread); |
| 1589 | if (!strcmp(event->name, "sched_process_fork")) | 1601 | if (!strcmp(event->name, "sched_process_fork")) |
| 1590 | process_sched_fork_event(data, event, cpu, timestamp, thread); | 1602 | process_sched_fork_event(data, event, cpu, timestamp, thread); |
| 1591 | if (!strcmp(event->name, "sched_process_exit")) | 1603 | if (!strcmp(event->name, "sched_process_exit")) |
| 1592 | process_sched_exit_event(event, cpu, timestamp, thread); | 1604 | process_sched_exit_event(event, cpu, timestamp, thread); |
| 1593 | if (!strcmp(event->name, "sched_migrate_task")) | 1605 | if (!strcmp(event->name, "sched_migrate_task")) |
| 1594 | process_sched_migrate_task_event(data, event, cpu, timestamp, thread); | 1606 | process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread); |
| 1595 | } | 1607 | } |
| 1596 | 1608 | ||
| 1597 | static int process_sample_event(event_t *event) | 1609 | static int process_sample_event(event_t *event, struct perf_session *session) |
| 1598 | { | 1610 | { |
| 1599 | struct sample_data data; | 1611 | struct sample_data data; |
| 1600 | struct thread *thread; | 1612 | struct thread *thread; |
| 1601 | 1613 | ||
| 1602 | if (!(sample_type & PERF_SAMPLE_RAW)) | 1614 | if (!(session->sample_type & PERF_SAMPLE_RAW)) |
| 1603 | return 0; | 1615 | return 0; |
| 1604 | 1616 | ||
| 1605 | memset(&data, 0, sizeof(data)); | 1617 | memset(&data, 0, sizeof(data)); |
| @@ -1607,7 +1619,7 @@ static int process_sample_event(event_t *event) | |||
| 1607 | data.cpu = -1; | 1619 | data.cpu = -1; |
| 1608 | data.period = -1; | 1620 | data.period = -1; |
| 1609 | 1621 | ||
| 1610 | event__parse_sample(event, sample_type, &data); | 1622 | event__parse_sample(event, session->sample_type, &data); |
| 1611 | 1623 | ||
| 1612 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 1624 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", |
| 1613 | event->header.misc, | 1625 | event->header.misc, |
| @@ -1615,7 +1627,7 @@ static int process_sample_event(event_t *event) | |||
| 1615 | (void *)(long)data.ip, | 1627 | (void *)(long)data.ip, |
| 1616 | (long long)data.period); | 1628 | (long long)data.period); |
| 1617 | 1629 | ||
| 1618 | thread = threads__findnew(data.pid); | 1630 | thread = perf_session__findnew(session, data.pid); |
| 1619 | if (thread == NULL) { | 1631 | if (thread == NULL) { |
| 1620 | pr_debug("problem processing %d event, skipping it.\n", | 1632 | pr_debug("problem processing %d event, skipping it.\n", |
| 1621 | event->header.type); | 1633 | event->header.type); |
| @@ -1627,12 +1639,13 @@ static int process_sample_event(event_t *event) | |||
| 1627 | if (profile_cpu != -1 && profile_cpu != (int)data.cpu) | 1639 | if (profile_cpu != -1 && profile_cpu != (int)data.cpu) |
| 1628 | return 0; | 1640 | return 0; |
| 1629 | 1641 | ||
| 1630 | process_raw_event(event, data.raw_data, data.cpu, data.time, thread); | 1642 | process_raw_event(event, session, data.raw_data, data.cpu, data.time, thread); |
| 1631 | 1643 | ||
| 1632 | return 0; | 1644 | return 0; |
| 1633 | } | 1645 | } |
| 1634 | 1646 | ||
| 1635 | static int process_lost_event(event_t *event __used) | 1647 | static int process_lost_event(event_t *event __used, |
| 1648 | struct perf_session *session __used) | ||
| 1636 | { | 1649 | { |
| 1637 | nr_lost_chunks++; | 1650 | nr_lost_chunks++; |
| 1638 | nr_lost_events += event->lost.lost; | 1651 | nr_lost_events += event->lost.lost; |
| @@ -1640,11 +1653,9 @@ static int process_lost_event(event_t *event __used) | |||
| 1640 | return 0; | 1653 | return 0; |
| 1641 | } | 1654 | } |
| 1642 | 1655 | ||
| 1643 | static int sample_type_check(u64 type) | 1656 | static int sample_type_check(struct perf_session *session __used) |
| 1644 | { | 1657 | { |
| 1645 | sample_type = type; | 1658 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { |
| 1646 | |||
| 1647 | if (!(sample_type & PERF_SAMPLE_RAW)) { | ||
| 1648 | fprintf(stderr, | 1659 | fprintf(stderr, |
| 1649 | "No trace sample to read. Did you call perf record " | 1660 | "No trace sample to read. Did you call perf record " |
| 1650 | "without -R?"); | 1661 | "without -R?"); |
| @@ -1654,7 +1665,7 @@ static int sample_type_check(u64 type) | |||
| 1654 | return 0; | 1665 | return 0; |
| 1655 | } | 1666 | } |
| 1656 | 1667 | ||
| 1657 | static struct perf_file_handler file_handler = { | 1668 | static struct perf_event_ops event_ops = { |
| 1658 | .process_sample_event = process_sample_event, | 1669 | .process_sample_event = process_sample_event, |
| 1659 | .process_comm_event = event__process_comm, | 1670 | .process_comm_event = event__process_comm, |
| 1660 | .process_lost_event = process_lost_event, | 1671 | .process_lost_event = process_lost_event, |
| @@ -1665,14 +1676,10 @@ static int read_events(void) | |||
| 1665 | { | 1676 | { |
| 1666 | int err; | 1677 | int err; |
| 1667 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); | 1678 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0); |
| 1668 | |||
| 1669 | if (session == NULL) | 1679 | if (session == NULL) |
| 1670 | return -ENOMEM; | 1680 | return -ENOMEM; |
| 1671 | 1681 | ||
| 1672 | register_idle_thread(); | 1682 | err = perf_session__process_events(session, &event_ops); |
| 1673 | register_perf_file_handler(&file_handler); | ||
| 1674 | |||
| 1675 | err = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd); | ||
| 1676 | perf_session__delete(session); | 1683 | perf_session__delete(session); |
| 1677 | return err; | 1684 | return err; |
| 1678 | } | 1685 | } |
| @@ -1904,7 +1911,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used) | |||
| 1904 | if (!strcmp(argv[0], "trace")) | 1911 | if (!strcmp(argv[0], "trace")) |
| 1905 | return cmd_trace(argc, argv, prefix); | 1912 | return cmd_trace(argc, argv, prefix); |
| 1906 | 1913 | ||
| 1907 | symbol__init(0); | 1914 | symbol__init(); |
| 1908 | if (!strncmp(argv[0], "rec", 3)) { | 1915 | if (!strncmp(argv[0], "rec", 3)) { |
| 1909 | return __cmd_record(argc, argv); | 1916 | return __cmd_record(argc, argv); |
| 1910 | } else if (!strncmp(argv[0], "lat", 3)) { | 1917 | } else if (!strncmp(argv[0], "lat", 3)) { |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 759dd2b35fdb..a589a43112d6 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
| @@ -30,15 +30,12 @@ | |||
| 30 | #include "util/parse-options.h" | 30 | #include "util/parse-options.h" |
| 31 | #include "util/parse-events.h" | 31 | #include "util/parse-events.h" |
| 32 | #include "util/event.h" | 32 | #include "util/event.h" |
| 33 | #include "util/data_map.h" | 33 | #include "util/session.h" |
| 34 | #include "util/svghelper.h" | 34 | #include "util/svghelper.h" |
| 35 | 35 | ||
| 36 | static char const *input_name = "perf.data"; | 36 | static char const *input_name = "perf.data"; |
| 37 | static char const *output_name = "output.svg"; | 37 | static char const *output_name = "output.svg"; |
| 38 | 38 | ||
| 39 | |||
| 40 | static u64 sample_type; | ||
| 41 | |||
| 42 | static unsigned int numcpus; | 39 | static unsigned int numcpus; |
| 43 | static u64 min_freq; /* Lowest CPU frequency seen */ | 40 | static u64 min_freq; /* Lowest CPU frequency seen */ |
| 44 | static u64 max_freq; /* Highest CPU frequency seen */ | 41 | static u64 max_freq; /* Highest CPU frequency seen */ |
| @@ -281,21 +278,19 @@ static int cpus_cstate_state[MAX_CPUS]; | |||
| 281 | static u64 cpus_pstate_start_times[MAX_CPUS]; | 278 | static u64 cpus_pstate_start_times[MAX_CPUS]; |
| 282 | static u64 cpus_pstate_state[MAX_CPUS]; | 279 | static u64 cpus_pstate_state[MAX_CPUS]; |
| 283 | 280 | ||
| 284 | static int | 281 | static int process_comm_event(event_t *event, struct perf_session *session __used) |
| 285 | process_comm_event(event_t *event) | ||
| 286 | { | 282 | { |
| 287 | pid_set_comm(event->comm.pid, event->comm.comm); | 283 | pid_set_comm(event->comm.pid, event->comm.comm); |
| 288 | return 0; | 284 | return 0; |
| 289 | } | 285 | } |
| 290 | static int | 286 | |
| 291 | process_fork_event(event_t *event) | 287 | static int process_fork_event(event_t *event, struct perf_session *session __used) |
| 292 | { | 288 | { |
| 293 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); | 289 | pid_fork(event->fork.pid, event->fork.ppid, event->fork.time); |
| 294 | return 0; | 290 | return 0; |
| 295 | } | 291 | } |
| 296 | 292 | ||
| 297 | static int | 293 | static int process_exit_event(event_t *event, struct perf_session *session __used) |
| 298 | process_exit_event(event_t *event) | ||
| 299 | { | 294 | { |
| 300 | pid_exit(event->fork.pid, event->fork.time); | 295 | pid_exit(event->fork.pid, event->fork.time); |
| 301 | return 0; | 296 | return 0; |
| @@ -480,17 +475,16 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) | |||
| 480 | } | 475 | } |
| 481 | 476 | ||
| 482 | 477 | ||
| 483 | static int | 478 | static int process_sample_event(event_t *event, struct perf_session *session) |
| 484 | process_sample_event(event_t *event) | ||
| 485 | { | 479 | { |
| 486 | struct sample_data data; | 480 | struct sample_data data; |
| 487 | struct trace_entry *te; | 481 | struct trace_entry *te; |
| 488 | 482 | ||
| 489 | memset(&data, 0, sizeof(data)); | 483 | memset(&data, 0, sizeof(data)); |
| 490 | 484 | ||
| 491 | event__parse_sample(event, sample_type, &data); | 485 | event__parse_sample(event, session->sample_type, &data); |
| 492 | 486 | ||
| 493 | if (sample_type & PERF_SAMPLE_TIME) { | 487 | if (session->sample_type & PERF_SAMPLE_TIME) { |
| 494 | if (!first_time || first_time > data.time) | 488 | if (!first_time || first_time > data.time) |
| 495 | first_time = data.time; | 489 | first_time = data.time; |
| 496 | if (last_time < data.time) | 490 | if (last_time < data.time) |
| @@ -498,7 +492,7 @@ process_sample_event(event_t *event) | |||
| 498 | } | 492 | } |
| 499 | 493 | ||
| 500 | te = (void *)data.raw_data; | 494 | te = (void *)data.raw_data; |
| 501 | if (sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) { | 495 | if (session->sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) { |
| 502 | char *event_str; | 496 | char *event_str; |
| 503 | struct power_entry *pe; | 497 | struct power_entry *pe; |
| 504 | 498 | ||
| @@ -575,16 +569,16 @@ static void end_sample_processing(void) | |||
| 575 | } | 569 | } |
| 576 | } | 570 | } |
| 577 | 571 | ||
| 578 | static u64 sample_time(event_t *event) | 572 | static u64 sample_time(event_t *event, const struct perf_session *session) |
| 579 | { | 573 | { |
| 580 | int cursor; | 574 | int cursor; |
| 581 | 575 | ||
| 582 | cursor = 0; | 576 | cursor = 0; |
| 583 | if (sample_type & PERF_SAMPLE_IP) | 577 | if (session->sample_type & PERF_SAMPLE_IP) |
| 584 | cursor++; | 578 | cursor++; |
| 585 | if (sample_type & PERF_SAMPLE_TID) | 579 | if (session->sample_type & PERF_SAMPLE_TID) |
| 586 | cursor++; | 580 | cursor++; |
| 587 | if (sample_type & PERF_SAMPLE_TIME) | 581 | if (session->sample_type & PERF_SAMPLE_TIME) |
| 588 | return event->sample.array[cursor]; | 582 | return event->sample.array[cursor]; |
| 589 | return 0; | 583 | return 0; |
| 590 | } | 584 | } |
| @@ -594,8 +588,7 @@ static u64 sample_time(event_t *event) | |||
| 594 | * We first queue all events, sorted backwards by insertion. | 588 | * We first queue all events, sorted backwards by insertion. |
| 595 | * The order will get flipped later. | 589 | * The order will get flipped later. |
| 596 | */ | 590 | */ |
| 597 | static int | 591 | static int queue_sample_event(event_t *event, struct perf_session *session) |
| 598 | queue_sample_event(event_t *event) | ||
| 599 | { | 592 | { |
| 600 | struct sample_wrapper *copy, *prev; | 593 | struct sample_wrapper *copy, *prev; |
| 601 | int size; | 594 | int size; |
| @@ -609,7 +602,7 @@ queue_sample_event(event_t *event) | |||
| 609 | memset(copy, 0, size); | 602 | memset(copy, 0, size); |
| 610 | 603 | ||
| 611 | copy->next = NULL; | 604 | copy->next = NULL; |
| 612 | copy->timestamp = sample_time(event); | 605 | copy->timestamp = sample_time(event, session); |
| 613 | 606 | ||
| 614 | memcpy(©->data, event, event->sample.header.size); | 607 | memcpy(©->data, event, event->sample.header.size); |
| 615 | 608 | ||
| @@ -1021,7 +1014,7 @@ static void write_svg_file(const char *filename) | |||
| 1021 | svg_close(); | 1014 | svg_close(); |
| 1022 | } | 1015 | } |
| 1023 | 1016 | ||
| 1024 | static void process_samples(void) | 1017 | static void process_samples(struct perf_session *session) |
| 1025 | { | 1018 | { |
| 1026 | struct sample_wrapper *cursor; | 1019 | struct sample_wrapper *cursor; |
| 1027 | event_t *event; | 1020 | event_t *event; |
| @@ -1032,15 +1025,13 @@ static void process_samples(void) | |||
| 1032 | while (cursor) { | 1025 | while (cursor) { |
| 1033 | event = (void *)&cursor->data; | 1026 | event = (void *)&cursor->data; |
| 1034 | cursor = cursor->next; | 1027 | cursor = cursor->next; |
| 1035 | process_sample_event(event); | 1028 | process_sample_event(event, session); |
| 1036 | } | 1029 | } |
| 1037 | } | 1030 | } |
| 1038 | 1031 | ||
| 1039 | static int sample_type_check(u64 type) | 1032 | static int sample_type_check(struct perf_session *session) |
| 1040 | { | 1033 | { |
| 1041 | sample_type = type; | 1034 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { |
| 1042 | |||
| 1043 | if (!(sample_type & PERF_SAMPLE_RAW)) { | ||
| 1044 | fprintf(stderr, "No trace samples found in the file.\n" | 1035 | fprintf(stderr, "No trace samples found in the file.\n" |
| 1045 | "Have you used 'perf timechart record' to record it?\n"); | 1036 | "Have you used 'perf timechart record' to record it?\n"); |
| 1046 | return -1; | 1037 | return -1; |
| @@ -1049,7 +1040,7 @@ static int sample_type_check(u64 type) | |||
| 1049 | return 0; | 1040 | return 0; |
| 1050 | } | 1041 | } |
| 1051 | 1042 | ||
| 1052 | static struct perf_file_handler file_handler = { | 1043 | static struct perf_event_ops event_ops = { |
| 1053 | .process_comm_event = process_comm_event, | 1044 | .process_comm_event = process_comm_event, |
| 1054 | .process_fork_event = process_fork_event, | 1045 | .process_fork_event = process_fork_event, |
| 1055 | .process_exit_event = process_exit_event, | 1046 | .process_exit_event = process_exit_event, |
| @@ -1065,13 +1056,11 @@ static int __cmd_timechart(void) | |||
| 1065 | if (session == NULL) | 1056 | if (session == NULL) |
| 1066 | return -ENOMEM; | 1057 | return -ENOMEM; |
| 1067 | 1058 | ||
| 1068 | register_perf_file_handler(&file_handler); | 1059 | ret = perf_session__process_events(session, &event_ops); |
| 1069 | |||
| 1070 | ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd); | ||
| 1071 | if (ret) | 1060 | if (ret) |
| 1072 | goto out_delete; | 1061 | goto out_delete; |
| 1073 | 1062 | ||
| 1074 | process_samples(); | 1063 | process_samples(session); |
| 1075 | 1064 | ||
| 1076 | end_sample_processing(); | 1065 | end_sample_processing(); |
| 1077 | 1066 | ||
| @@ -1148,11 +1137,11 @@ static const struct option options[] = { | |||
| 1148 | 1137 | ||
| 1149 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) | 1138 | int cmd_timechart(int argc, const char **argv, const char *prefix __used) |
| 1150 | { | 1139 | { |
| 1151 | symbol__init(0); | ||
| 1152 | |||
| 1153 | argc = parse_options(argc, argv, options, timechart_usage, | 1140 | argc = parse_options(argc, argv, options, timechart_usage, |
| 1154 | PARSE_OPT_STOP_AT_NON_OPTION); | 1141 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 1155 | 1142 | ||
| 1143 | symbol__init(); | ||
| 1144 | |||
| 1156 | if (argc && !strncmp(argv[0], "rec", 3)) | 1145 | if (argc && !strncmp(argv[0], "rec", 3)) |
| 1157 | return __cmd_record(argc, argv); | 1146 | return __cmd_record(argc, argv); |
| 1158 | else if (argc) | 1147 | else if (argc) |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e0a374d0e43a..ddc584b64871 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -20,8 +20,9 @@ | |||
| 20 | 20 | ||
| 21 | #include "perf.h" | 21 | #include "perf.h" |
| 22 | 22 | ||
| 23 | #include "util/symbol.h" | ||
| 24 | #include "util/color.h" | 23 | #include "util/color.h" |
| 24 | #include "util/session.h" | ||
| 25 | #include "util/symbol.h" | ||
| 25 | #include "util/thread.h" | 26 | #include "util/thread.h" |
| 26 | #include "util/util.h" | 27 | #include "util/util.h" |
| 27 | #include <linux/rbtree.h> | 28 | #include <linux/rbtree.h> |
| @@ -79,7 +80,6 @@ static int dump_symtab = 0; | |||
| 79 | static bool hide_kernel_symbols = false; | 80 | static bool hide_kernel_symbols = false; |
| 80 | static bool hide_user_symbols = false; | 81 | static bool hide_user_symbols = false; |
| 81 | static struct winsize winsize; | 82 | static struct winsize winsize; |
| 82 | struct symbol_conf symbol_conf; | ||
| 83 | 83 | ||
| 84 | /* | 84 | /* |
| 85 | * Source | 85 | * Source |
| @@ -926,7 +926,8 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
| 926 | return 0; | 926 | return 0; |
| 927 | } | 927 | } |
| 928 | 928 | ||
| 929 | static void event__process_sample(const event_t *self, int counter) | 929 | static void event__process_sample(const event_t *self, |
| 930 | struct perf_session *session, int counter) | ||
| 930 | { | 931 | { |
| 931 | u64 ip = self->ip.ip; | 932 | u64 ip = self->ip.ip; |
| 932 | struct sym_entry *syme; | 933 | struct sym_entry *syme; |
| @@ -946,8 +947,8 @@ static void event__process_sample(const event_t *self, int counter) | |||
| 946 | return; | 947 | return; |
| 947 | } | 948 | } |
| 948 | 949 | ||
| 949 | if (event__preprocess_sample(self, &al, symbol_filter) < 0 || | 950 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || |
| 950 | al.sym == NULL) | 951 | al.sym == NULL || al.filtered) |
| 951 | return; | 952 | return; |
| 952 | 953 | ||
| 953 | syme = symbol__priv(al.sym); | 954 | syme = symbol__priv(al.sym); |
| @@ -965,14 +966,14 @@ static void event__process_sample(const event_t *self, int counter) | |||
| 965 | } | 966 | } |
| 966 | } | 967 | } |
| 967 | 968 | ||
| 968 | static int event__process(event_t *event) | 969 | static int event__process(event_t *event, struct perf_session *session) |
| 969 | { | 970 | { |
| 970 | switch (event->header.type) { | 971 | switch (event->header.type) { |
| 971 | case PERF_RECORD_COMM: | 972 | case PERF_RECORD_COMM: |
| 972 | event__process_comm(event); | 973 | event__process_comm(event, session); |
| 973 | break; | 974 | break; |
| 974 | case PERF_RECORD_MMAP: | 975 | case PERF_RECORD_MMAP: |
| 975 | event__process_mmap(event); | 976 | event__process_mmap(event, session); |
| 976 | break; | 977 | break; |
| 977 | default: | 978 | default: |
| 978 | break; | 979 | break; |
| @@ -999,7 +1000,8 @@ static unsigned int mmap_read_head(struct mmap_data *md) | |||
| 999 | return head; | 1000 | return head; |
| 1000 | } | 1001 | } |
| 1001 | 1002 | ||
| 1002 | static void mmap_read_counter(struct mmap_data *md) | 1003 | static void perf_session__mmap_read_counter(struct perf_session *self, |
| 1004 | struct mmap_data *md) | ||
| 1003 | { | 1005 | { |
| 1004 | unsigned int head = mmap_read_head(md); | 1006 | unsigned int head = mmap_read_head(md); |
| 1005 | unsigned int old = md->prev; | 1007 | unsigned int old = md->prev; |
| @@ -1052,9 +1054,9 @@ static void mmap_read_counter(struct mmap_data *md) | |||
| 1052 | } | 1054 | } |
| 1053 | 1055 | ||
| 1054 | if (event->header.type == PERF_RECORD_SAMPLE) | 1056 | if (event->header.type == PERF_RECORD_SAMPLE) |
| 1055 | event__process_sample(event, md->counter); | 1057 | event__process_sample(event, self, md->counter); |
| 1056 | else | 1058 | else |
| 1057 | event__process(event); | 1059 | event__process(event, self); |
| 1058 | old += size; | 1060 | old += size; |
| 1059 | } | 1061 | } |
| 1060 | 1062 | ||
| @@ -1064,13 +1066,13 @@ static void mmap_read_counter(struct mmap_data *md) | |||
| 1064 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; | 1066 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; |
| 1065 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | 1067 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; |
| 1066 | 1068 | ||
| 1067 | static void mmap_read(void) | 1069 | static void perf_session__mmap_read(struct perf_session *self) |
| 1068 | { | 1070 | { |
| 1069 | int i, counter; | 1071 | int i, counter; |
| 1070 | 1072 | ||
| 1071 | for (i = 0; i < nr_cpus; i++) { | 1073 | for (i = 0; i < nr_cpus; i++) { |
| 1072 | for (counter = 0; counter < nr_counters; counter++) | 1074 | for (counter = 0; counter < nr_counters; counter++) |
| 1073 | mmap_read_counter(&mmap_array[i][counter]); | 1075 | perf_session__mmap_read_counter(self, &mmap_array[i][counter]); |
| 1074 | } | 1076 | } |
| 1075 | } | 1077 | } |
| 1076 | 1078 | ||
| @@ -1155,11 +1157,18 @@ static int __cmd_top(void) | |||
| 1155 | pthread_t thread; | 1157 | pthread_t thread; |
| 1156 | int i, counter; | 1158 | int i, counter; |
| 1157 | int ret; | 1159 | int ret; |
| 1160 | /* | ||
| 1161 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | ||
| 1162 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | ||
| 1163 | */ | ||
| 1164 | struct perf_session *session = perf_session__new(NULL, O_WRONLY, false); | ||
| 1165 | if (session == NULL) | ||
| 1166 | return -ENOMEM; | ||
| 1158 | 1167 | ||
| 1159 | if (target_pid != -1) | 1168 | if (target_pid != -1) |
| 1160 | event__synthesize_thread(target_pid, event__process); | 1169 | event__synthesize_thread(target_pid, event__process, session); |
| 1161 | else | 1170 | else |
| 1162 | event__synthesize_threads(event__process); | 1171 | event__synthesize_threads(event__process, session); |
| 1163 | 1172 | ||
| 1164 | for (i = 0; i < nr_cpus; i++) { | 1173 | for (i = 0; i < nr_cpus; i++) { |
| 1165 | group_fd = -1; | 1174 | group_fd = -1; |
| @@ -1170,7 +1179,7 @@ static int __cmd_top(void) | |||
| 1170 | /* Wait for a minimal set of events before starting the snapshot */ | 1179 | /* Wait for a minimal set of events before starting the snapshot */ |
| 1171 | poll(event_array, nr_poll, 100); | 1180 | poll(event_array, nr_poll, 100); |
| 1172 | 1181 | ||
| 1173 | mmap_read(); | 1182 | perf_session__mmap_read(session); |
| 1174 | 1183 | ||
| 1175 | if (pthread_create(&thread, NULL, display_thread, NULL)) { | 1184 | if (pthread_create(&thread, NULL, display_thread, NULL)) { |
| 1176 | printf("Could not create display thread.\n"); | 1185 | printf("Could not create display thread.\n"); |
| @@ -1190,7 +1199,7 @@ static int __cmd_top(void) | |||
| 1190 | while (1) { | 1199 | while (1) { |
| 1191 | int hits = samples; | 1200 | int hits = samples; |
| 1192 | 1201 | ||
| 1193 | mmap_read(); | 1202 | perf_session__mmap_read(session); |
| 1194 | 1203 | ||
| 1195 | if (hits == samples) | 1204 | if (hits == samples) |
| 1196 | ret = poll(event_array, nr_poll, 100); | 1205 | ret = poll(event_array, nr_poll, 100); |
| @@ -1273,7 +1282,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
| 1273 | (nr_counters + 1) * sizeof(unsigned long)); | 1282 | (nr_counters + 1) * sizeof(unsigned long)); |
| 1274 | if (symbol_conf.vmlinux_name == NULL) | 1283 | if (symbol_conf.vmlinux_name == NULL) |
| 1275 | symbol_conf.try_vmlinux_path = true; | 1284 | symbol_conf.try_vmlinux_path = true; |
| 1276 | if (symbol__init(&symbol_conf) < 0) | 1285 | if (symbol__init() < 0) |
| 1277 | return -1; | 1286 | return -1; |
| 1278 | 1287 | ||
| 1279 | if (delay_secs < 1) | 1288 | if (delay_secs < 1) |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 0756664666f1..e2285e28720f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -12,7 +12,9 @@ | |||
| 12 | static char const *script_name; | 12 | static char const *script_name; |
| 13 | static char const *generate_script_lang; | 13 | static char const *generate_script_lang; |
| 14 | 14 | ||
| 15 | static int default_start_script(const char *script __attribute((unused))) | 15 | static int default_start_script(const char *script __unused, |
| 16 | int argc __unused, | ||
| 17 | const char **argv __unused) | ||
| 16 | { | 18 | { |
| 17 | return 0; | 19 | return 0; |
| 18 | } | 20 | } |
| @@ -22,7 +24,7 @@ static int default_stop_script(void) | |||
| 22 | return 0; | 24 | return 0; |
| 23 | } | 25 | } |
| 24 | 26 | ||
| 25 | static int default_generate_script(const char *outfile __attribute ((unused))) | 27 | static int default_generate_script(const char *outfile __unused) |
| 26 | { | 28 | { |
| 27 | return 0; | 29 | return 0; |
| 28 | } | 30 | } |
| @@ -57,15 +59,11 @@ static int cleanup_scripting(void) | |||
| 57 | #include "util/debug.h" | 59 | #include "util/debug.h" |
| 58 | 60 | ||
| 59 | #include "util/trace-event.h" | 61 | #include "util/trace-event.h" |
| 60 | #include "util/data_map.h" | ||
| 61 | #include "util/exec_cmd.h" | 62 | #include "util/exec_cmd.h" |
| 62 | 63 | ||
| 63 | static char const *input_name = "perf.data"; | 64 | static char const *input_name = "perf.data"; |
| 64 | 65 | ||
| 65 | static struct perf_session *session; | 66 | static int process_sample_event(event_t *event, struct perf_session *session) |
| 66 | static u64 sample_type; | ||
| 67 | |||
| 68 | static int process_sample_event(event_t *event) | ||
| 69 | { | 67 | { |
| 70 | struct sample_data data; | 68 | struct sample_data data; |
| 71 | struct thread *thread; | 69 | struct thread *thread; |
| @@ -75,7 +73,7 @@ static int process_sample_event(event_t *event) | |||
| 75 | data.cpu = -1; | 73 | data.cpu = -1; |
| 76 | data.period = 1; | 74 | data.period = 1; |
| 77 | 75 | ||
| 78 | event__parse_sample(event, sample_type, &data); | 76 | event__parse_sample(event, session->sample_type, &data); |
| 79 | 77 | ||
| 80 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", | 78 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", |
| 81 | event->header.misc, | 79 | event->header.misc, |
| @@ -83,14 +81,14 @@ static int process_sample_event(event_t *event) | |||
| 83 | (void *)(long)data.ip, | 81 | (void *)(long)data.ip, |
| 84 | (long long)data.period); | 82 | (long long)data.period); |
| 85 | 83 | ||
| 86 | thread = threads__findnew(event->ip.pid); | 84 | thread = perf_session__findnew(session, event->ip.pid); |
| 87 | if (thread == NULL) { | 85 | if (thread == NULL) { |
| 88 | pr_debug("problem processing %d event, skipping it.\n", | 86 | pr_debug("problem processing %d event, skipping it.\n", |
| 89 | event->header.type); | 87 | event->header.type); |
| 90 | return -1; | 88 | return -1; |
| 91 | } | 89 | } |
| 92 | 90 | ||
| 93 | if (sample_type & PERF_SAMPLE_RAW) { | 91 | if (session->sample_type & PERF_SAMPLE_RAW) { |
| 94 | /* | 92 | /* |
| 95 | * FIXME: better resolve from pid from the struct trace_entry | 93 | * FIXME: better resolve from pid from the struct trace_entry |
| 96 | * field, although it should be the same than this perf | 94 | * field, although it should be the same than this perf |
| @@ -100,16 +98,14 @@ static int process_sample_event(event_t *event) | |||
| 100 | data.raw_size, | 98 | data.raw_size, |
| 101 | data.time, thread->comm); | 99 | data.time, thread->comm); |
| 102 | } | 100 | } |
| 103 | event__stats.total += data.period; | ||
| 104 | 101 | ||
| 102 | session->events_stats.total += data.period; | ||
| 105 | return 0; | 103 | return 0; |
| 106 | } | 104 | } |
| 107 | 105 | ||
| 108 | static int sample_type_check(u64 type) | 106 | static int sample_type_check(struct perf_session *session) |
| 109 | { | 107 | { |
| 110 | sample_type = type; | 108 | if (!(session->sample_type & PERF_SAMPLE_RAW)) { |
| 111 | |||
| 112 | if (!(sample_type & PERF_SAMPLE_RAW)) { | ||
| 113 | fprintf(stderr, | 109 | fprintf(stderr, |
| 114 | "No trace sample to read. Did you call perf record " | 110 | "No trace sample to read. Did you call perf record " |
| 115 | "without -R?"); | 111 | "without -R?"); |
| @@ -119,26 +115,15 @@ static int sample_type_check(u64 type) | |||
| 119 | return 0; | 115 | return 0; |
| 120 | } | 116 | } |
| 121 | 117 | ||
| 122 | static struct perf_file_handler file_handler = { | 118 | static struct perf_event_ops event_ops = { |
| 123 | .process_sample_event = process_sample_event, | 119 | .process_sample_event = process_sample_event, |
| 124 | .process_comm_event = event__process_comm, | 120 | .process_comm_event = event__process_comm, |
| 125 | .sample_type_check = sample_type_check, | 121 | .sample_type_check = sample_type_check, |
| 126 | }; | 122 | }; |
| 127 | 123 | ||
| 128 | static int __cmd_trace(void) | 124 | static int __cmd_trace(struct perf_session *session) |
| 129 | { | 125 | { |
| 130 | int err; | 126 | return perf_session__process_events(session, &event_ops); |
| 131 | |||
| 132 | session = perf_session__new(input_name, O_RDONLY, 0); | ||
| 133 | if (session == NULL) | ||
| 134 | return -ENOMEM; | ||
| 135 | |||
| 136 | register_idle_thread(); | ||
| 137 | register_perf_file_handler(&file_handler); | ||
| 138 | |||
| 139 | err = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd); | ||
| 140 | perf_session__delete(session); | ||
| 141 | return err; | ||
| 142 | } | 127 | } |
| 143 | 128 | ||
| 144 | struct script_spec { | 129 | struct script_spec { |
| @@ -289,6 +274,244 @@ static int parse_scriptname(const struct option *opt __used, | |||
| 289 | return 0; | 274 | return 0; |
| 290 | } | 275 | } |
| 291 | 276 | ||
| 277 | #define for_each_lang(scripts_dir, lang_dirent, lang_next) \ | ||
| 278 | while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ | ||
| 279 | lang_next) \ | ||
| 280 | if (lang_dirent.d_type == DT_DIR && \ | ||
| 281 | (strcmp(lang_dirent.d_name, ".")) && \ | ||
| 282 | (strcmp(lang_dirent.d_name, ".."))) | ||
| 283 | |||
| 284 | #define for_each_script(lang_dir, script_dirent, script_next) \ | ||
| 285 | while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ | ||
| 286 | script_next) \ | ||
| 287 | if (script_dirent.d_type != DT_DIR) | ||
| 288 | |||
| 289 | |||
| 290 | #define RECORD_SUFFIX "-record" | ||
| 291 | #define REPORT_SUFFIX "-report" | ||
| 292 | |||
| 293 | struct script_desc { | ||
| 294 | struct list_head node; | ||
| 295 | char *name; | ||
| 296 | char *half_liner; | ||
| 297 | char *args; | ||
| 298 | }; | ||
| 299 | |||
| 300 | LIST_HEAD(script_descs); | ||
| 301 | |||
| 302 | static struct script_desc *script_desc__new(const char *name) | ||
| 303 | { | ||
| 304 | struct script_desc *s = zalloc(sizeof(*s)); | ||
| 305 | |||
| 306 | if (s != NULL) | ||
| 307 | s->name = strdup(name); | ||
| 308 | |||
| 309 | return s; | ||
| 310 | } | ||
| 311 | |||
| 312 | static void script_desc__delete(struct script_desc *s) | ||
| 313 | { | ||
| 314 | free(s->name); | ||
| 315 | free(s); | ||
| 316 | } | ||
| 317 | |||
| 318 | static void script_desc__add(struct script_desc *s) | ||
| 319 | { | ||
| 320 | list_add_tail(&s->node, &script_descs); | ||
| 321 | } | ||
| 322 | |||
| 323 | static struct script_desc *script_desc__find(const char *name) | ||
| 324 | { | ||
| 325 | struct script_desc *s; | ||
| 326 | |||
| 327 | list_for_each_entry(s, &script_descs, node) | ||
| 328 | if (strcasecmp(s->name, name) == 0) | ||
| 329 | return s; | ||
| 330 | return NULL; | ||
| 331 | } | ||
| 332 | |||
| 333 | static struct script_desc *script_desc__findnew(const char *name) | ||
| 334 | { | ||
| 335 | struct script_desc *s = script_desc__find(name); | ||
| 336 | |||
| 337 | if (s) | ||
| 338 | return s; | ||
| 339 | |||
| 340 | s = script_desc__new(name); | ||
| 341 | if (!s) | ||
| 342 | goto out_delete_desc; | ||
| 343 | |||
| 344 | script_desc__add(s); | ||
| 345 | |||
| 346 | return s; | ||
| 347 | |||
| 348 | out_delete_desc: | ||
| 349 | script_desc__delete(s); | ||
| 350 | |||
| 351 | return NULL; | ||
| 352 | } | ||
| 353 | |||
| 354 | static char *ends_with(char *str, const char *suffix) | ||
| 355 | { | ||
| 356 | size_t suffix_len = strlen(suffix); | ||
| 357 | char *p = str; | ||
| 358 | |||
| 359 | if (strlen(str) > suffix_len) { | ||
| 360 | p = str + strlen(str) - suffix_len; | ||
| 361 | if (!strncmp(p, suffix, suffix_len)) | ||
| 362 | return p; | ||
| 363 | } | ||
| 364 | |||
| 365 | return NULL; | ||
| 366 | } | ||
| 367 | |||
| 368 | static char *ltrim(char *str) | ||
| 369 | { | ||
| 370 | int len = strlen(str); | ||
| 371 | |||
| 372 | while (len && isspace(*str)) { | ||
| 373 | len--; | ||
| 374 | str++; | ||
| 375 | } | ||
| 376 | |||
| 377 | return str; | ||
| 378 | } | ||
| 379 | |||
| 380 | static int read_script_info(struct script_desc *desc, const char *filename) | ||
| 381 | { | ||
| 382 | char line[BUFSIZ], *p; | ||
| 383 | FILE *fp; | ||
| 384 | |||
| 385 | fp = fopen(filename, "r"); | ||
| 386 | if (!fp) | ||
| 387 | return -1; | ||
| 388 | |||
| 389 | while (fgets(line, sizeof(line), fp)) { | ||
| 390 | p = ltrim(line); | ||
| 391 | if (strlen(p) == 0) | ||
| 392 | continue; | ||
| 393 | if (*p != '#') | ||
| 394 | continue; | ||
| 395 | p++; | ||
| 396 | if (strlen(p) && *p == '!') | ||
| 397 | continue; | ||
| 398 | |||
| 399 | p = ltrim(p); | ||
| 400 | if (strlen(p) && p[strlen(p) - 1] == '\n') | ||
| 401 | p[strlen(p) - 1] = '\0'; | ||
| 402 | |||
| 403 | if (!strncmp(p, "description:", strlen("description:"))) { | ||
| 404 | p += strlen("description:"); | ||
| 405 | desc->half_liner = strdup(ltrim(p)); | ||
| 406 | continue; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (!strncmp(p, "args:", strlen("args:"))) { | ||
| 410 | p += strlen("args:"); | ||
| 411 | desc->args = strdup(ltrim(p)); | ||
| 412 | continue; | ||
| 413 | } | ||
| 414 | } | ||
| 415 | |||
| 416 | fclose(fp); | ||
| 417 | |||
| 418 | return 0; | ||
| 419 | } | ||
| 420 | |||
| 421 | static int list_available_scripts(const struct option *opt __used, | ||
| 422 | const char *s __used, int unset __used) | ||
| 423 | { | ||
| 424 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | ||
| 425 | char scripts_path[MAXPATHLEN]; | ||
| 426 | DIR *scripts_dir, *lang_dir; | ||
| 427 | char script_path[MAXPATHLEN]; | ||
| 428 | char lang_path[MAXPATHLEN]; | ||
| 429 | struct script_desc *desc; | ||
| 430 | char first_half[BUFSIZ]; | ||
| 431 | char *script_root; | ||
| 432 | char *str; | ||
| 433 | |||
| 434 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | ||
| 435 | |||
| 436 | scripts_dir = opendir(scripts_path); | ||
| 437 | if (!scripts_dir) | ||
| 438 | return -1; | ||
| 439 | |||
| 440 | for_each_lang(scripts_dir, lang_dirent, lang_next) { | ||
| 441 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | ||
| 442 | lang_dirent.d_name); | ||
| 443 | lang_dir = opendir(lang_path); | ||
| 444 | if (!lang_dir) | ||
| 445 | continue; | ||
| 446 | |||
| 447 | for_each_script(lang_dir, script_dirent, script_next) { | ||
| 448 | script_root = strdup(script_dirent.d_name); | ||
| 449 | str = ends_with(script_root, REPORT_SUFFIX); | ||
| 450 | if (str) { | ||
| 451 | *str = '\0'; | ||
| 452 | desc = script_desc__findnew(script_root); | ||
| 453 | snprintf(script_path, MAXPATHLEN, "%s/%s", | ||
| 454 | lang_path, script_dirent.d_name); | ||
| 455 | read_script_info(desc, script_path); | ||
| 456 | } | ||
| 457 | free(script_root); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | |||
| 461 | fprintf(stdout, "List of available trace scripts:\n"); | ||
| 462 | list_for_each_entry(desc, &script_descs, node) { | ||
| 463 | sprintf(first_half, "%s %s", desc->name, | ||
| 464 | desc->args ? desc->args : ""); | ||
| 465 | fprintf(stdout, " %-36s %s\n", first_half, | ||
| 466 | desc->half_liner ? desc->half_liner : ""); | ||
| 467 | } | ||
| 468 | |||
| 469 | exit(0); | ||
| 470 | } | ||
| 471 | |||
| 472 | static char *get_script_path(const char *script_root, const char *suffix) | ||
| 473 | { | ||
| 474 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | ||
| 475 | char scripts_path[MAXPATHLEN]; | ||
| 476 | char script_path[MAXPATHLEN]; | ||
| 477 | DIR *scripts_dir, *lang_dir; | ||
| 478 | char lang_path[MAXPATHLEN]; | ||
| 479 | char *str, *__script_root; | ||
| 480 | char *path = NULL; | ||
| 481 | |||
| 482 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | ||
| 483 | |||
| 484 | scripts_dir = opendir(scripts_path); | ||
| 485 | if (!scripts_dir) | ||
| 486 | return NULL; | ||
| 487 | |||
| 488 | for_each_lang(scripts_dir, lang_dirent, lang_next) { | ||
| 489 | snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, | ||
| 490 | lang_dirent.d_name); | ||
| 491 | lang_dir = opendir(lang_path); | ||
| 492 | if (!lang_dir) | ||
| 493 | continue; | ||
| 494 | |||
| 495 | for_each_script(lang_dir, script_dirent, script_next) { | ||
| 496 | __script_root = strdup(script_dirent.d_name); | ||
| 497 | str = ends_with(__script_root, suffix); | ||
| 498 | if (str) { | ||
| 499 | *str = '\0'; | ||
| 500 | if (strcmp(__script_root, script_root)) | ||
| 501 | continue; | ||
| 502 | snprintf(script_path, MAXPATHLEN, "%s/%s", | ||
| 503 | lang_path, script_dirent.d_name); | ||
| 504 | path = strdup(script_path); | ||
| 505 | free(__script_root); | ||
| 506 | break; | ||
| 507 | } | ||
| 508 | free(__script_root); | ||
| 509 | } | ||
| 510 | } | ||
| 511 | |||
| 512 | return path; | ||
| 513 | } | ||
| 514 | |||
| 292 | static const char * const annotate_usage[] = { | 515 | static const char * const annotate_usage[] = { |
| 293 | "perf trace [<options>] <command>", | 516 | "perf trace [<options>] <command>", |
| 294 | NULL | 517 | NULL |
| @@ -299,8 +522,10 @@ static const struct option options[] = { | |||
| 299 | "dump raw trace in ASCII"), | 522 | "dump raw trace in ASCII"), |
| 300 | OPT_BOOLEAN('v', "verbose", &verbose, | 523 | OPT_BOOLEAN('v', "verbose", &verbose, |
| 301 | "be more verbose (show symbol address, etc)"), | 524 | "be more verbose (show symbol address, etc)"), |
| 302 | OPT_BOOLEAN('l', "latency", &latency_format, | 525 | OPT_BOOLEAN('L', "Latency", &latency_format, |
| 303 | "show latency attributes (irqs/preemption disabled, etc)"), | 526 | "show latency attributes (irqs/preemption disabled, etc)"), |
| 527 | OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts", | ||
| 528 | list_available_scripts), | ||
| 304 | OPT_CALLBACK('s', "script", NULL, "name", | 529 | OPT_CALLBACK('s', "script", NULL, "name", |
| 305 | "script file name (lang:script name, script name, or *)", | 530 | "script file name (lang:script name, script name, or *)", |
| 306 | parse_scriptname), | 531 | parse_scriptname), |
| @@ -312,24 +537,61 @@ static const struct option options[] = { | |||
| 312 | 537 | ||
| 313 | int cmd_trace(int argc, const char **argv, const char *prefix __used) | 538 | int cmd_trace(int argc, const char **argv, const char *prefix __used) |
| 314 | { | 539 | { |
| 315 | int err; | 540 | struct perf_session *session; |
| 541 | const char *suffix = NULL; | ||
| 542 | const char **__argv; | ||
| 543 | char *script_path; | ||
| 544 | int i, err; | ||
| 545 | |||
| 546 | if (argc >= 2 && strncmp(argv[1], "rec", strlen("rec")) == 0) { | ||
| 547 | if (argc < 3) { | ||
| 548 | fprintf(stderr, | ||
| 549 | "Please specify a record script\n"); | ||
| 550 | return -1; | ||
| 551 | } | ||
| 552 | suffix = RECORD_SUFFIX; | ||
| 553 | } | ||
| 316 | 554 | ||
| 317 | symbol__init(0); | 555 | if (argc >= 2 && strncmp(argv[1], "rep", strlen("rep")) == 0) { |
| 556 | if (argc < 3) { | ||
| 557 | fprintf(stderr, | ||
| 558 | "Please specify a report script\n"); | ||
| 559 | return -1; | ||
| 560 | } | ||
| 561 | suffix = REPORT_SUFFIX; | ||
| 562 | } | ||
| 318 | 563 | ||
| 319 | setup_scripting(); | 564 | if (suffix) { |
| 565 | script_path = get_script_path(argv[2], suffix); | ||
| 566 | if (!script_path) { | ||
| 567 | fprintf(stderr, "script not found\n"); | ||
| 568 | return -1; | ||
| 569 | } | ||
| 320 | 570 | ||
| 321 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 571 | __argv = malloc((argc + 1) * sizeof(const char *)); |
| 322 | if (argc) { | 572 | __argv[0] = "/bin/sh"; |
| 323 | /* | 573 | __argv[1] = script_path; |
| 324 | * Special case: if there's an argument left then assume tha | 574 | for (i = 3; i < argc; i++) |
| 325 | * it's a symbol filter: | 575 | __argv[i - 1] = argv[i]; |
| 326 | */ | 576 | __argv[argc - 1] = NULL; |
| 327 | if (argc > 1) | 577 | |
| 328 | usage_with_options(annotate_usage, options); | 578 | execvp("/bin/sh", (char **)__argv); |
| 579 | exit(-1); | ||
| 329 | } | 580 | } |
| 330 | 581 | ||
| 582 | setup_scripting(); | ||
| 583 | |||
| 584 | argc = parse_options(argc, argv, options, annotate_usage, | ||
| 585 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
| 586 | |||
| 587 | if (symbol__init() < 0) | ||
| 588 | return -1; | ||
| 331 | setup_pager(); | 589 | setup_pager(); |
| 332 | 590 | ||
| 591 | session = perf_session__new(input_name, O_RDONLY, 0); | ||
| 592 | if (session == NULL) | ||
| 593 | return -ENOMEM; | ||
| 594 | |||
| 333 | if (generate_script_lang) { | 595 | if (generate_script_lang) { |
| 334 | struct stat perf_stat; | 596 | struct stat perf_stat; |
| 335 | 597 | ||
| @@ -362,13 +624,14 @@ int cmd_trace(int argc, const char **argv, const char *prefix __used) | |||
| 362 | } | 624 | } |
| 363 | 625 | ||
| 364 | if (script_name) { | 626 | if (script_name) { |
| 365 | err = scripting_ops->start_script(script_name); | 627 | err = scripting_ops->start_script(script_name, argc, argv); |
| 366 | if (err) | 628 | if (err) |
| 367 | goto out; | 629 | goto out; |
| 368 | } | 630 | } |
| 369 | 631 | ||
| 370 | err = __cmd_trace(); | 632 | err = __cmd_trace(session); |
| 371 | 633 | ||
| 634 | perf_session__delete(session); | ||
| 372 | cleanup_scripting(); | 635 | cleanup_scripting(); |
| 373 | out: | 636 | out: |
| 374 | return err; | 637 | return err; |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index a3d8bf65f26c..18035b1f16c7 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
| @@ -17,6 +17,7 @@ extern int check_pager_config(const char *cmd); | |||
| 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_list(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_diff(int argc, const char **argv, const char *prefix); | ||
| 20 | extern int cmd_help(int argc, const char **argv, const char *prefix); | 21 | extern int cmd_help(int argc, const char **argv, const char *prefix); |
| 21 | extern int cmd_sched(int argc, const char **argv, const char *prefix); | 22 | extern int cmd_sched(int argc, const char **argv, const char *prefix); |
| 22 | extern int cmd_list(int argc, const char **argv, const char *prefix); | 23 | extern int cmd_list(int argc, const char **argv, const char *prefix); |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 02b09ea17a3e..71dc7c3fe7b2 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | perf-annotate mainporcelain common | 5 | perf-annotate mainporcelain common |
| 6 | perf-bench mainporcelain common | 6 | perf-bench mainporcelain common |
| 7 | perf-buildid-list mainporcelain common | 7 | perf-buildid-list mainporcelain common |
| 8 | perf-diff mainporcelain common | ||
| 8 | perf-list mainporcelain common | 9 | perf-list mainporcelain common |
| 9 | perf-sched mainporcelain common | 10 | perf-sched mainporcelain common |
| 10 | perf-record mainporcelain common | 11 | perf-record mainporcelain common |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index cf64049bc9bd..873e55fab375 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
| @@ -286,6 +286,7 @@ static void handle_internal_command(int argc, const char **argv) | |||
| 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-list", cmd_buildid_list, 0 }, | 288 | { "buildid-list", cmd_buildid_list, 0 }, |
| 289 | { "diff", cmd_diff, 0 }, | ||
| 289 | { "help", cmd_help, 0 }, | 290 | { "help", cmd_help, 0 }, |
| 290 | { "list", cmd_list, 0 }, | 291 | { "list", cmd_list, 0 }, |
| 291 | { "record", cmd_record, 0 }, | 292 | { "record", cmd_record, 0 }, |
diff --git a/tools/perf/scripts/perl/bin/check-perf-trace-report b/tools/perf/scripts/perl/bin/check-perf-trace-report index 89948b015020..7fc4a033dd49 100644 --- a/tools/perf/scripts/perl/bin/check-perf-trace-report +++ b/tools/perf/scripts/perl/bin/check-perf-trace-report | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #!/bin/bash | 1 | #!/bin/bash |
| 2 | # description: useless but exhaustive test script | ||
| 2 | perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl | 3 | perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl |
| 3 | 4 | ||
| 4 | 5 | ||
diff --git a/tools/perf/scripts/perl/bin/rw-by-file-report b/tools/perf/scripts/perl/bin/rw-by-file-report index f5dcf9cb5bd2..eddb9ccce6a5 100644 --- a/tools/perf/scripts/perl/bin/rw-by-file-report +++ b/tools/perf/scripts/perl/bin/rw-by-file-report | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | #!/bin/bash | 1 | #!/bin/bash |
| 2 | perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl | 2 | # description: r/w activity for a program, by file |
| 3 | # args: <comm> | ||
| 4 | perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1 | ||
| 3 | 5 | ||
| 4 | 6 | ||
| 5 | 7 | ||
diff --git a/tools/perf/scripts/perl/bin/rw-by-pid-report b/tools/perf/scripts/perl/bin/rw-by-pid-report index cea16f78a3a2..7f44c25cc857 100644 --- a/tools/perf/scripts/perl/bin/rw-by-pid-report +++ b/tools/perf/scripts/perl/bin/rw-by-pid-report | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #!/bin/bash | 1 | #!/bin/bash |
| 2 | # description: system-wide r/w activity | ||
| 2 | perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl | 3 | perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl |
| 3 | 4 | ||
| 4 | 5 | ||
diff --git a/tools/perf/scripts/perl/bin/wakeup-latency-report b/tools/perf/scripts/perl/bin/wakeup-latency-report index 85769dc456eb..fce3adcb3249 100644 --- a/tools/perf/scripts/perl/bin/wakeup-latency-report +++ b/tools/perf/scripts/perl/bin/wakeup-latency-report | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #!/bin/bash | 1 | #!/bin/bash |
| 2 | # description: system-wide min/max/avg wakeup latency | ||
| 2 | perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl | 3 | perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl |
| 3 | 4 | ||
| 4 | 5 | ||
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report index aa68435be926..71cfbd182fb9 100644 --- a/tools/perf/scripts/perl/bin/workqueue-stats-report +++ b/tools/perf/scripts/perl/bin/workqueue-stats-report | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #!/bin/bash | 1 | #!/bin/bash |
| 2 | # description: workqueue stats (ins/exe/create/destroy) | ||
| 2 | perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl | 3 | perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl |
| 3 | 4 | ||
| 4 | 5 | ||
diff --git a/tools/perf/scripts/perl/rw-by-file.pl b/tools/perf/scripts/perl/rw-by-file.pl index 61f91561d848..2a39097687b9 100644 --- a/tools/perf/scripts/perl/rw-by-file.pl +++ b/tools/perf/scripts/perl/rw-by-file.pl | |||
| @@ -18,8 +18,9 @@ use lib "./Perf-Trace-Util/lib"; | |||
| 18 | use Perf::Trace::Core; | 18 | use Perf::Trace::Core; |
| 19 | use Perf::Trace::Util; | 19 | use Perf::Trace::Util; |
| 20 | 20 | ||
| 21 | # change this to the comm of the program you're interested in | 21 | my $usage = "perf trace -s rw-by-file.pl <comm>\n"; |
| 22 | my $for_comm = "perf"; | 22 | |
| 23 | my $for_comm = shift or die $usage; | ||
| 23 | 24 | ||
| 24 | my %reads; | 25 | my %reads; |
| 25 | my %writes; | 26 | my %writes; |
diff --git a/tools/perf/util/data_map.c b/tools/perf/util/data_map.c index 6d46dda53a29..b557b836de3d 100644 --- a/tools/perf/util/data_map.c +++ b/tools/perf/util/data_map.c | |||
| @@ -1,20 +1,17 @@ | |||
| 1 | #include "data_map.h" | ||
| 2 | #include "symbol.h" | 1 | #include "symbol.h" |
| 3 | #include "util.h" | 2 | #include "util.h" |
| 4 | #include "debug.h" | 3 | #include "debug.h" |
| 4 | #include "thread.h" | ||
| 5 | #include "session.h" | ||
| 5 | 6 | ||
| 6 | 7 | static int process_event_stub(event_t *event __used, | |
| 7 | static struct perf_file_handler *curr_handler; | 8 | struct perf_session *session __used) |
| 8 | static unsigned long mmap_window = 32; | ||
| 9 | static char __cwd[PATH_MAX]; | ||
| 10 | |||
| 11 | static int process_event_stub(event_t *event __used) | ||
| 12 | { | 9 | { |
| 13 | dump_printf(": unhandled!\n"); | 10 | dump_printf(": unhandled!\n"); |
| 14 | return 0; | 11 | return 0; |
| 15 | } | 12 | } |
| 16 | 13 | ||
| 17 | void register_perf_file_handler(struct perf_file_handler *handler) | 14 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) |
| 18 | { | 15 | { |
| 19 | if (!handler->process_sample_event) | 16 | if (!handler->process_sample_event) |
| 20 | handler->process_sample_event = process_event_stub; | 17 | handler->process_sample_event = process_event_stub; |
| @@ -34,8 +31,6 @@ void register_perf_file_handler(struct perf_file_handler *handler) | |||
| 34 | handler->process_throttle_event = process_event_stub; | 31 | handler->process_throttle_event = process_event_stub; |
| 35 | if (!handler->process_unthrottle_event) | 32 | if (!handler->process_unthrottle_event) |
| 36 | handler->process_unthrottle_event = process_event_stub; | 33 | handler->process_unthrottle_event = process_event_stub; |
| 37 | |||
| 38 | curr_handler = handler; | ||
| 39 | } | 34 | } |
| 40 | 35 | ||
| 41 | static const char *event__name[] = { | 36 | static const char *event__name[] = { |
| @@ -61,8 +56,9 @@ void event__print_totals(void) | |||
| 61 | event__name[i], event__total[i]); | 56 | event__name[i], event__total[i]); |
| 62 | } | 57 | } |
| 63 | 58 | ||
| 64 | static int | 59 | static int process_event(event_t *event, struct perf_session *session, |
| 65 | process_event(event_t *event, unsigned long offset, unsigned long head) | 60 | struct perf_event_ops *ops, |
| 61 | unsigned long offset, unsigned long head) | ||
| 66 | { | 62 | { |
| 67 | trace_event(event); | 63 | trace_event(event); |
| 68 | 64 | ||
| @@ -77,25 +73,25 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 77 | 73 | ||
| 78 | switch (event->header.type) { | 74 | switch (event->header.type) { |
| 79 | case PERF_RECORD_SAMPLE: | 75 | case PERF_RECORD_SAMPLE: |
| 80 | return curr_handler->process_sample_event(event); | 76 | return ops->process_sample_event(event, session); |
| 81 | case PERF_RECORD_MMAP: | 77 | case PERF_RECORD_MMAP: |
| 82 | return curr_handler->process_mmap_event(event); | 78 | return ops->process_mmap_event(event, session); |
| 83 | case PERF_RECORD_COMM: | 79 | case PERF_RECORD_COMM: |
| 84 | return curr_handler->process_comm_event(event); | 80 | return ops->process_comm_event(event, session); |
| 85 | case PERF_RECORD_FORK: | 81 | case PERF_RECORD_FORK: |
| 86 | return curr_handler->process_fork_event(event); | 82 | return ops->process_fork_event(event, session); |
| 87 | case PERF_RECORD_EXIT: | 83 | case PERF_RECORD_EXIT: |
| 88 | return curr_handler->process_exit_event(event); | 84 | return ops->process_exit_event(event, session); |
| 89 | case PERF_RECORD_LOST: | 85 | case PERF_RECORD_LOST: |
| 90 | return curr_handler->process_lost_event(event); | 86 | return ops->process_lost_event(event, session); |
| 91 | case PERF_RECORD_READ: | 87 | case PERF_RECORD_READ: |
| 92 | return curr_handler->process_read_event(event); | 88 | return ops->process_read_event(event, session); |
| 93 | case PERF_RECORD_THROTTLE: | 89 | case PERF_RECORD_THROTTLE: |
| 94 | return curr_handler->process_throttle_event(event); | 90 | return ops->process_throttle_event(event, session); |
| 95 | case PERF_RECORD_UNTHROTTLE: | 91 | case PERF_RECORD_UNTHROTTLE: |
| 96 | return curr_handler->process_unthrottle_event(event); | 92 | return ops->process_unthrottle_event(event, session); |
| 97 | default: | 93 | default: |
| 98 | curr_handler->total_unknown++; | 94 | ops->total_unknown++; |
| 99 | return -1; | 95 | return -1; |
| 100 | } | 96 | } |
| 101 | } | 97 | } |
| @@ -129,44 +125,58 @@ out: | |||
| 129 | return err; | 125 | return err; |
| 130 | } | 126 | } |
| 131 | 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 | |||
| 132 | int perf_session__process_events(struct perf_session *self, | 140 | int perf_session__process_events(struct perf_session *self, |
| 133 | int full_paths, int *cwdlen, char **cwd) | 141 | struct perf_event_ops *ops) |
| 134 | { | 142 | { |
| 135 | int err; | 143 | int err; |
| 136 | unsigned long head, shift; | 144 | unsigned long head, shift; |
| 137 | unsigned long offset = 0; | 145 | unsigned long offset = 0; |
| 138 | size_t page_size; | 146 | size_t page_size; |
| 139 | u64 sample_type; | ||
| 140 | event_t *event; | 147 | event_t *event; |
| 141 | uint32_t size; | 148 | uint32_t size; |
| 142 | char *buf; | 149 | char *buf; |
| 143 | 150 | ||
| 144 | if (curr_handler == NULL) { | 151 | if (perf_session__register_idle_thread(self) == NULL) |
| 145 | pr_debug("Forgot to register perf file handler\n"); | 152 | return -ENOMEM; |
| 146 | return -EINVAL; | 153 | |
| 147 | } | 154 | perf_event_ops__fill_defaults(ops); |
| 148 | 155 | ||
| 149 | page_size = getpagesize(); | 156 | page_size = getpagesize(); |
| 150 | 157 | ||
| 151 | head = self->header.data_offset; | 158 | head = self->header.data_offset; |
| 152 | sample_type = perf_header__sample_type(&self->header); | 159 | self->sample_type = perf_header__sample_type(&self->header); |
| 153 | 160 | ||
| 154 | err = -EINVAL; | 161 | err = -EINVAL; |
| 155 | if (curr_handler->sample_type_check && | 162 | if (ops->sample_type_check && ops->sample_type_check(self) < 0) |
| 156 | curr_handler->sample_type_check(sample_type) < 0) | ||
| 157 | goto out_err; | 163 | goto out_err; |
| 158 | 164 | ||
| 159 | if (!full_paths) { | 165 | if (!ops->full_paths) { |
| 160 | if (getcwd(__cwd, sizeof(__cwd)) == NULL) { | 166 | char bf[PATH_MAX]; |
| 161 | pr_err("failed to get the current directory\n"); | 167 | |
| 168 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
| 162 | err = -errno; | 169 | err = -errno; |
| 170 | out_getcwd_err: | ||
| 171 | pr_err("failed to get the current directory\n"); | ||
| 163 | goto out_err; | 172 | goto out_err; |
| 164 | } | 173 | } |
| 165 | *cwd = __cwd; | 174 | self->cwd = strdup(bf); |
| 166 | *cwdlen = strlen(*cwd); | 175 | if (self->cwd == NULL) { |
| 167 | } else { | 176 | err = -ENOMEM; |
| 168 | *cwd = NULL; | 177 | goto out_getcwd_err; |
| 169 | *cwdlen = 0; | 178 | } |
| 179 | self->cwdlen = strlen(self->cwd); | ||
| 170 | } | 180 | } |
| 171 | 181 | ||
| 172 | shift = page_size * (head / page_size); | 182 | shift = page_size * (head / page_size); |
| @@ -174,7 +184,7 @@ int perf_session__process_events(struct perf_session *self, | |||
| 174 | head -= shift; | 184 | head -= shift; |
| 175 | 185 | ||
| 176 | remap: | 186 | remap: |
| 177 | buf = mmap(NULL, page_size * mmap_window, PROT_READ, | 187 | buf = mmap(NULL, page_size * self->mmap_window, PROT_READ, |
| 178 | MAP_SHARED, self->fd, offset); | 188 | MAP_SHARED, self->fd, offset); |
| 179 | if (buf == MAP_FAILED) { | 189 | if (buf == MAP_FAILED) { |
| 180 | pr_err("failed to mmap file\n"); | 190 | pr_err("failed to mmap file\n"); |
| @@ -189,12 +199,12 @@ more: | |||
| 189 | if (!size) | 199 | if (!size) |
| 190 | size = 8; | 200 | size = 8; |
| 191 | 201 | ||
| 192 | if (head + event->header.size >= page_size * mmap_window) { | 202 | if (head + event->header.size >= page_size * self->mmap_window) { |
| 193 | int munmap_ret; | 203 | int munmap_ret; |
| 194 | 204 | ||
| 195 | shift = page_size * (head / page_size); | 205 | shift = page_size * (head / page_size); |
| 196 | 206 | ||
| 197 | munmap_ret = munmap(buf, page_size * mmap_window); | 207 | munmap_ret = munmap(buf, page_size * self->mmap_window); |
| 198 | assert(munmap_ret == 0); | 208 | assert(munmap_ret == 0); |
| 199 | 209 | ||
| 200 | offset += shift; | 210 | offset += shift; |
| @@ -209,7 +219,7 @@ more: | |||
| 209 | (void *)(long)event->header.size, | 219 | (void *)(long)event->header.size, |
| 210 | event->header.type); | 220 | event->header.type); |
| 211 | 221 | ||
| 212 | if (!size || process_event(event, offset, head) < 0) { | 222 | if (!size || process_event(event, self, ops, offset, head) < 0) { |
| 213 | 223 | ||
| 214 | dump_printf("%p [%p]: skipping unknown header type: %d\n", | 224 | dump_printf("%p [%p]: skipping unknown header type: %d\n", |
| 215 | (void *)(offset + head), | 225 | (void *)(offset + head), |
diff --git a/tools/perf/util/data_map.h b/tools/perf/util/data_map.h deleted file mode 100644 index 98c5b823388c..000000000000 --- a/tools/perf/util/data_map.h +++ /dev/null | |||
| @@ -1,29 +0,0 @@ | |||
| 1 | #ifndef __PERF_DATAMAP_H | ||
| 2 | #define __PERF_DATAMAP_H | ||
| 3 | |||
| 4 | #include "event.h" | ||
| 5 | #include "header.h" | ||
| 6 | #include "session.h" | ||
| 7 | |||
| 8 | typedef int (*event_type_handler_t)(event_t *); | ||
| 9 | |||
| 10 | struct perf_file_handler { | ||
| 11 | event_type_handler_t process_sample_event; | ||
| 12 | event_type_handler_t process_mmap_event; | ||
| 13 | event_type_handler_t process_comm_event; | ||
| 14 | event_type_handler_t process_fork_event; | ||
| 15 | event_type_handler_t process_exit_event; | ||
| 16 | event_type_handler_t process_lost_event; | ||
| 17 | event_type_handler_t process_read_event; | ||
| 18 | event_type_handler_t process_throttle_event; | ||
| 19 | event_type_handler_t process_unthrottle_event; | ||
| 20 | int (*sample_type_check)(u64 sample_type); | ||
| 21 | unsigned long total_unknown; | ||
| 22 | }; | ||
| 23 | |||
| 24 | void register_perf_file_handler(struct perf_file_handler *handler); | ||
| 25 | int perf_session__process_events(struct perf_session *self, | ||
| 26 | int full_paths, int *cwdlen, char **cwd); | ||
| 27 | int perf_header__read_build_ids(int input, u64 offset, u64 file_size); | ||
| 28 | |||
| 29 | #endif | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index ba0de90cd3d4..bb0fd6da2d56 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -1,11 +1,16 @@ | |||
| 1 | #include <linux/types.h> | 1 | #include <linux/types.h> |
| 2 | #include "event.h" | 2 | #include "event.h" |
| 3 | #include "debug.h" | 3 | #include "debug.h" |
| 4 | #include "session.h" | ||
| 5 | #include "sort.h" | ||
| 4 | #include "string.h" | 6 | #include "string.h" |
| 7 | #include "strlist.h" | ||
| 5 | #include "thread.h" | 8 | #include "thread.h" |
| 6 | 9 | ||
| 7 | static pid_t event__synthesize_comm(pid_t pid, int full, | 10 | static pid_t event__synthesize_comm(pid_t pid, int full, |
| 8 | int (*process)(event_t *event)) | 11 | int (*process)(event_t *event, |
| 12 | struct perf_session *session), | ||
| 13 | struct perf_session *session) | ||
| 9 | { | 14 | { |
| 10 | event_t ev; | 15 | event_t ev; |
| 11 | char filename[PATH_MAX]; | 16 | char filename[PATH_MAX]; |
| @@ -54,7 +59,7 @@ out_race: | |||
| 54 | if (!full) { | 59 | if (!full) { |
| 55 | ev.comm.tid = pid; | 60 | ev.comm.tid = pid; |
| 56 | 61 | ||
| 57 | process(&ev); | 62 | process(&ev, session); |
| 58 | goto out_fclose; | 63 | goto out_fclose; |
| 59 | } | 64 | } |
| 60 | 65 | ||
| @@ -72,7 +77,7 @@ out_race: | |||
| 72 | 77 | ||
| 73 | ev.comm.tid = pid; | 78 | ev.comm.tid = pid; |
| 74 | 79 | ||
| 75 | process(&ev); | 80 | process(&ev, session); |
| 76 | } | 81 | } |
| 77 | closedir(tasks); | 82 | closedir(tasks); |
| 78 | 83 | ||
| @@ -86,7 +91,9 @@ out_failure: | |||
| 86 | } | 91 | } |
| 87 | 92 | ||
| 88 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | 93 | static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, |
| 89 | int (*process)(event_t *event)) | 94 | int (*process)(event_t *event, |
| 95 | struct perf_session *session), | ||
| 96 | struct perf_session *session) | ||
| 90 | { | 97 | { |
| 91 | char filename[PATH_MAX]; | 98 | char filename[PATH_MAX]; |
| 92 | FILE *fp; | 99 | FILE *fp; |
| @@ -141,7 +148,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
| 141 | ev.mmap.pid = tgid; | 148 | ev.mmap.pid = tgid; |
| 142 | ev.mmap.tid = pid; | 149 | ev.mmap.tid = pid; |
| 143 | 150 | ||
| 144 | process(&ev); | 151 | process(&ev, session); |
| 145 | } | 152 | } |
| 146 | } | 153 | } |
| 147 | 154 | ||
| @@ -149,15 +156,20 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
| 149 | return 0; | 156 | return 0; |
| 150 | } | 157 | } |
| 151 | 158 | ||
| 152 | int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)) | 159 | int event__synthesize_thread(pid_t pid, |
| 160 | int (*process)(event_t *event, | ||
| 161 | struct perf_session *session), | ||
| 162 | struct perf_session *session) | ||
| 153 | { | 163 | { |
| 154 | pid_t tgid = event__synthesize_comm(pid, 1, process); | 164 | pid_t tgid = event__synthesize_comm(pid, 1, process, session); |
| 155 | if (tgid == -1) | 165 | if (tgid == -1) |
| 156 | return -1; | 166 | return -1; |
| 157 | return event__synthesize_mmap_events(pid, tgid, process); | 167 | return event__synthesize_mmap_events(pid, tgid, process, session); |
| 158 | } | 168 | } |
| 159 | 169 | ||
| 160 | void event__synthesize_threads(int (*process)(event_t *event)) | 170 | void event__synthesize_threads(int (*process)(event_t *event, |
| 171 | struct perf_session *session), | ||
| 172 | struct perf_session *session) | ||
| 161 | { | 173 | { |
| 162 | DIR *proc; | 174 | DIR *proc; |
| 163 | struct dirent dirent, *next; | 175 | struct dirent dirent, *next; |
| @@ -171,24 +183,47 @@ void event__synthesize_threads(int (*process)(event_t *event)) | |||
| 171 | if (*end) /* only interested in proper numerical dirents */ | 183 | if (*end) /* only interested in proper numerical dirents */ |
| 172 | continue; | 184 | continue; |
| 173 | 185 | ||
| 174 | event__synthesize_thread(pid, process); | 186 | event__synthesize_thread(pid, process, session); |
| 175 | } | 187 | } |
| 176 | 188 | ||
| 177 | closedir(proc); | 189 | closedir(proc); |
| 178 | } | 190 | } |
| 179 | 191 | ||
| 180 | char *event__cwd; | 192 | static void thread__comm_adjust(struct thread *self) |
| 181 | int event__cwdlen; | 193 | { |
| 194 | char *comm = self->comm; | ||
| 182 | 195 | ||
| 183 | struct events_stats event__stats; | 196 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
| 197 | (!symbol_conf.comm_list || | ||
| 198 | strlist__has_entry(symbol_conf.comm_list, comm))) { | ||
| 199 | unsigned int slen = strlen(comm); | ||
| 184 | 200 | ||
| 185 | int event__process_comm(event_t *self) | 201 | if (slen > comms__col_width) { |
| 202 | comms__col_width = slen; | ||
| 203 | threads__col_width = slen + 6; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | ||
| 186 | { | 209 | { |
| 187 | struct thread *thread = threads__findnew(self->comm.pid); | 210 | int ret = thread__set_comm(self, comm); |
| 211 | |||
| 212 | if (ret) | ||
| 213 | return ret; | ||
| 214 | |||
| 215 | thread__comm_adjust(self); | ||
| 216 | |||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | int event__process_comm(event_t *self, struct perf_session *session) | ||
| 221 | { | ||
| 222 | struct thread *thread = perf_session__findnew(session, self->comm.pid); | ||
| 188 | 223 | ||
| 189 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); | 224 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); |
| 190 | 225 | ||
| 191 | if (thread == NULL || thread__set_comm(thread, self->comm.comm)) { | 226 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { |
| 192 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 227 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
| 193 | return -1; | 228 | return -1; |
| 194 | } | 229 | } |
| @@ -196,18 +231,18 @@ int event__process_comm(event_t *self) | |||
| 196 | return 0; | 231 | return 0; |
| 197 | } | 232 | } |
| 198 | 233 | ||
| 199 | int event__process_lost(event_t *self) | 234 | int event__process_lost(event_t *self, struct perf_session *session) |
| 200 | { | 235 | { |
| 201 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); | 236 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); |
| 202 | event__stats.lost += self->lost.lost; | 237 | session->events_stats.lost += self->lost.lost; |
| 203 | return 0; | 238 | return 0; |
| 204 | } | 239 | } |
| 205 | 240 | ||
| 206 | int event__process_mmap(event_t *self) | 241 | int event__process_mmap(event_t *self, struct perf_session *session) |
| 207 | { | 242 | { |
| 208 | struct thread *thread = threads__findnew(self->mmap.pid); | 243 | struct thread *thread = perf_session__findnew(session, self->mmap.pid); |
| 209 | struct map *map = map__new(&self->mmap, MAP__FUNCTION, | 244 | struct map *map = map__new(&self->mmap, MAP__FUNCTION, |
| 210 | event__cwd, event__cwdlen); | 245 | session->cwd, session->cwdlen); |
| 211 | 246 | ||
| 212 | dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", | 247 | dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n", |
| 213 | self->mmap.pid, self->mmap.tid, | 248 | self->mmap.pid, self->mmap.tid, |
| @@ -224,10 +259,10 @@ int event__process_mmap(event_t *self) | |||
| 224 | return 0; | 259 | return 0; |
| 225 | } | 260 | } |
| 226 | 261 | ||
| 227 | int event__process_task(event_t *self) | 262 | int event__process_task(event_t *self, struct perf_session *session) |
| 228 | { | 263 | { |
| 229 | struct thread *thread = threads__findnew(self->fork.pid); | 264 | struct thread *thread = perf_session__findnew(session, self->fork.pid); |
| 230 | struct thread *parent = threads__findnew(self->fork.ppid); | 265 | struct thread *parent = perf_session__findnew(session, self->fork.ppid); |
| 231 | 266 | ||
| 232 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | 267 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, |
| 233 | self->fork.ppid, self->fork.ptid); | 268 | self->fork.ppid, self->fork.ptid); |
| @@ -249,7 +284,8 @@ int event__process_task(event_t *self) | |||
| 249 | return 0; | 284 | return 0; |
| 250 | } | 285 | } |
| 251 | 286 | ||
| 252 | void thread__find_addr_location(struct thread *self, u8 cpumode, | 287 | void thread__find_addr_location(struct thread *self, |
| 288 | struct perf_session *session, u8 cpumode, | ||
| 253 | enum map_type type, u64 addr, | 289 | enum map_type type, u64 addr, |
| 254 | struct addr_location *al, | 290 | struct addr_location *al, |
| 255 | symbol_filter_t filter) | 291 | symbol_filter_t filter) |
| @@ -261,7 +297,7 @@ void thread__find_addr_location(struct thread *self, u8 cpumode, | |||
| 261 | 297 | ||
| 262 | if (cpumode & PERF_RECORD_MISC_KERNEL) { | 298 | if (cpumode & PERF_RECORD_MISC_KERNEL) { |
| 263 | al->level = 'k'; | 299 | al->level = 'k'; |
| 264 | mg = kmaps; | 300 | mg = &session->kmaps; |
| 265 | } else if (cpumode & PERF_RECORD_MISC_USER) | 301 | } else if (cpumode & PERF_RECORD_MISC_USER) |
| 266 | al->level = '.'; | 302 | al->level = '.'; |
| 267 | else { | 303 | else { |
| @@ -282,33 +318,73 @@ try_again: | |||
| 282 | * "[vdso]" dso, but for now lets use the old trick of looking | 318 | * "[vdso]" dso, but for now lets use the old trick of looking |
| 283 | * in the whole kernel symbol list. | 319 | * in the whole kernel symbol list. |
| 284 | */ | 320 | */ |
| 285 | if ((long long)al->addr < 0 && mg != kmaps) { | 321 | if ((long long)al->addr < 0 && mg != &session->kmaps) { |
| 286 | mg = kmaps; | 322 | mg = &session->kmaps; |
| 287 | goto try_again; | 323 | goto try_again; |
| 288 | } | 324 | } |
| 289 | al->sym = NULL; | 325 | al->sym = NULL; |
| 290 | } else { | 326 | } else { |
| 291 | al->addr = al->map->map_ip(al->map, al->addr); | 327 | al->addr = al->map->map_ip(al->map, al->addr); |
| 292 | al->sym = map__find_symbol(al->map, al->addr, filter); | 328 | al->sym = map__find_symbol(al->map, session, al->addr, filter); |
| 293 | } | 329 | } |
| 294 | } | 330 | } |
| 295 | 331 | ||
| 296 | int event__preprocess_sample(const event_t *self, struct addr_location *al, | 332 | static void dso__calc_col_width(struct dso *self) |
| 297 | symbol_filter_t filter) | 333 | { |
| 334 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
| 335 | (!symbol_conf.dso_list || | ||
| 336 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | ||
| 337 | unsigned int slen = strlen(self->name); | ||
| 338 | if (slen > dsos__col_width) | ||
| 339 | dsos__col_width = slen; | ||
| 340 | } | ||
| 341 | |||
| 342 | self->slen_calculated = 1; | ||
| 343 | } | ||
| 344 | |||
| 345 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | ||
| 346 | struct addr_location *al, symbol_filter_t filter) | ||
| 298 | { | 347 | { |
| 299 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 348 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
| 300 | struct thread *thread = threads__findnew(self->ip.pid); | 349 | struct thread *thread = perf_session__findnew(session, self->ip.pid); |
| 301 | 350 | ||
| 302 | if (thread == NULL) | 351 | if (thread == NULL) |
| 303 | return -1; | 352 | return -1; |
| 304 | 353 | ||
| 354 | if (symbol_conf.comm_list && | ||
| 355 | !strlist__has_entry(symbol_conf.comm_list, thread->comm)) | ||
| 356 | goto out_filtered; | ||
| 357 | |||
| 305 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 358 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
| 306 | 359 | ||
| 307 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, | 360 | thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, |
| 308 | self->ip.ip, al, filter); | 361 | self->ip.ip, al, filter); |
| 309 | dump_printf(" ...... dso: %s\n", | 362 | dump_printf(" ...... dso: %s\n", |
| 310 | al->map ? al->map->dso->long_name : | 363 | al->map ? al->map->dso->long_name : |
| 311 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 364 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
| 365 | /* | ||
| 366 | * We have to do this here as we may have a dso with no symbol hit that | ||
| 367 | * has a name longer than the ones with symbols sampled. | ||
| 368 | */ | ||
| 369 | if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) | ||
| 370 | dso__calc_col_width(al->map->dso); | ||
| 371 | |||
| 372 | if (symbol_conf.dso_list && | ||
| 373 | (!al->map || !al->map->dso || | ||
| 374 | !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || | ||
| 375 | (al->map->dso->short_name != al->map->dso->long_name && | ||
| 376 | strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) | ||
| 377 | goto out_filtered; | ||
| 378 | |||
| 379 | if (symbol_conf.sym_list && al->sym && | ||
| 380 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) | ||
| 381 | goto out_filtered; | ||
| 382 | |||
| 383 | al->filtered = false; | ||
| 384 | return 0; | ||
| 385 | |||
| 386 | out_filtered: | ||
| 387 | al->filtered = true; | ||
| 312 | return 0; | 388 | return 0; |
| 313 | } | 389 | } |
| 314 | 390 | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 51a96c2effde..8027309b0422 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -149,29 +149,35 @@ void map__delete(struct map *self); | |||
| 149 | struct map *map__clone(struct map *self); | 149 | struct map *map__clone(struct map *self); |
| 150 | int map__overlap(struct map *l, struct map *r); | 150 | int map__overlap(struct map *l, struct map *r); |
| 151 | size_t map__fprintf(struct map *self, FILE *fp); | 151 | size_t map__fprintf(struct map *self, FILE *fp); |
| 152 | struct symbol *map__find_symbol(struct map *self, u64 addr, | 152 | |
| 153 | symbol_filter_t filter); | 153 | struct perf_session; |
| 154 | |||
| 155 | int map__load(struct map *self, struct perf_session *session, | ||
| 156 | symbol_filter_t filter); | ||
| 157 | struct symbol *map__find_symbol(struct map *self, struct perf_session *session, | ||
| 158 | u64 addr, symbol_filter_t filter); | ||
| 154 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | 159 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, |
| 160 | struct perf_session *session, | ||
| 155 | symbol_filter_t filter); | 161 | symbol_filter_t filter); |
| 156 | void map__fixup_start(struct map *self); | 162 | void map__fixup_start(struct map *self); |
| 157 | void map__fixup_end(struct map *self); | 163 | void map__fixup_end(struct map *self); |
| 158 | 164 | ||
| 159 | int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)); | 165 | int event__synthesize_thread(pid_t pid, |
| 160 | void event__synthesize_threads(int (*process)(event_t *event)); | 166 | int (*process)(event_t *event, |
| 161 | 167 | struct perf_session *session), | |
| 162 | extern char *event__cwd; | 168 | struct perf_session *session); |
| 163 | extern int event__cwdlen; | 169 | void event__synthesize_threads(int (*process)(event_t *event, |
| 164 | extern struct events_stats event__stats; | 170 | struct perf_session *session), |
| 165 | extern unsigned long event__total[PERF_RECORD_MAX]; | 171 | struct perf_session *session); |
| 166 | 172 | ||
| 167 | int event__process_comm(event_t *self); | 173 | int event__process_comm(event_t *self, struct perf_session *session); |
| 168 | int event__process_lost(event_t *self); | 174 | int event__process_lost(event_t *self, struct perf_session *session); |
| 169 | int event__process_mmap(event_t *self); | 175 | int event__process_mmap(event_t *self, struct perf_session *session); |
| 170 | int event__process_task(event_t *self); | 176 | int event__process_task(event_t *self, struct perf_session *session); |
| 171 | 177 | ||
| 172 | struct addr_location; | 178 | struct addr_location; |
| 173 | int event__preprocess_sample(const event_t *self, struct addr_location *al, | 179 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
| 174 | symbol_filter_t filter); | 180 | struct addr_location *al, symbol_filter_t filter); |
| 175 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); | 181 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); |
| 176 | 182 | ||
| 177 | #endif /* __PERF_RECORD_H */ | 183 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f2e8d8715111..8a0bca55106f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -8,8 +8,8 @@ | |||
| 8 | #include "header.h" | 8 | #include "header.h" |
| 9 | #include "../perf.h" | 9 | #include "../perf.h" |
| 10 | #include "trace-event.h" | 10 | #include "trace-event.h" |
| 11 | #include "session.h" | ||
| 11 | #include "symbol.h" | 12 | #include "symbol.h" |
| 12 | #include "data_map.h" | ||
| 13 | #include "debug.h" | 13 | #include "debug.h" |
| 14 | 14 | ||
| 15 | /* | 15 | /* |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 0ebf6ee16caa..e8daf5ca6fd2 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -1,9 +1,7 @@ | |||
| 1 | #include "hist.h" | 1 | #include "hist.h" |
| 2 | 2 | #include "session.h" | |
| 3 | struct rb_root hist; | 3 | #include "sort.h" |
| 4 | struct rb_root collapse_hists; | 4 | #include <math.h> |
| 5 | struct rb_root output_hists; | ||
| 6 | int callchain; | ||
| 7 | 5 | ||
| 8 | struct callchain_param callchain_param = { | 6 | struct callchain_param callchain_param = { |
| 9 | .mode = CHAIN_GRAPH_REL, | 7 | .mode = CHAIN_GRAPH_REL, |
| @@ -14,11 +12,12 @@ struct callchain_param callchain_param = { | |||
| 14 | * histogram, sorted on item, collects counts | 12 | * histogram, sorted on item, collects counts |
| 15 | */ | 13 | */ |
| 16 | 14 | ||
| 17 | struct hist_entry *__hist_entry__add(struct addr_location *al, | 15 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, |
| 18 | struct symbol *sym_parent, | 16 | struct addr_location *al, |
| 19 | u64 count, bool *hit) | 17 | struct symbol *sym_parent, |
| 18 | u64 count, bool *hit) | ||
| 20 | { | 19 | { |
| 21 | struct rb_node **p = &hist.rb_node; | 20 | struct rb_node **p = &self->hists.rb_node; |
| 22 | struct rb_node *parent = NULL; | 21 | struct rb_node *parent = NULL; |
| 23 | struct hist_entry *he; | 22 | struct hist_entry *he; |
| 24 | struct hist_entry entry = { | 23 | struct hist_entry entry = { |
| @@ -54,7 +53,7 @@ struct hist_entry *__hist_entry__add(struct addr_location *al, | |||
| 54 | return NULL; | 53 | return NULL; |
| 55 | *he = entry; | 54 | *he = entry; |
| 56 | rb_link_node(&he->rb_node, parent, p); | 55 | rb_link_node(&he->rb_node, parent, p); |
| 57 | rb_insert_color(&he->rb_node, &hist); | 56 | rb_insert_color(&he->rb_node, &self->hists); |
| 58 | *hit = false; | 57 | *hit = false; |
| 59 | return he; | 58 | return he; |
| 60 | } | 59 | } |
| @@ -102,9 +101,9 @@ void hist_entry__free(struct hist_entry *he) | |||
| 102 | * collapse the histogram | 101 | * collapse the histogram |
| 103 | */ | 102 | */ |
| 104 | 103 | ||
| 105 | void collapse__insert_entry(struct hist_entry *he) | 104 | static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) |
| 106 | { | 105 | { |
| 107 | struct rb_node **p = &collapse_hists.rb_node; | 106 | struct rb_node **p = &root->rb_node; |
| 108 | struct rb_node *parent = NULL; | 107 | struct rb_node *parent = NULL; |
| 109 | struct hist_entry *iter; | 108 | struct hist_entry *iter; |
| 110 | int64_t cmp; | 109 | int64_t cmp; |
| @@ -128,38 +127,45 @@ void collapse__insert_entry(struct hist_entry *he) | |||
| 128 | } | 127 | } |
| 129 | 128 | ||
| 130 | rb_link_node(&he->rb_node, parent, p); | 129 | rb_link_node(&he->rb_node, parent, p); |
| 131 | rb_insert_color(&he->rb_node, &collapse_hists); | 130 | rb_insert_color(&he->rb_node, root); |
| 132 | } | 131 | } |
| 133 | 132 | ||
| 134 | void collapse__resort(void) | 133 | void perf_session__collapse_resort(struct perf_session *self) |
| 135 | { | 134 | { |
| 135 | struct rb_root tmp; | ||
| 136 | struct rb_node *next; | 136 | struct rb_node *next; |
| 137 | struct hist_entry *n; | 137 | struct hist_entry *n; |
| 138 | 138 | ||
| 139 | if (!sort__need_collapse) | 139 | if (!sort__need_collapse) |
| 140 | return; | 140 | return; |
| 141 | 141 | ||
| 142 | next = rb_first(&hist); | 142 | tmp = RB_ROOT; |
| 143 | next = rb_first(&self->hists); | ||
| 144 | |||
| 143 | while (next) { | 145 | while (next) { |
| 144 | n = rb_entry(next, struct hist_entry, rb_node); | 146 | n = rb_entry(next, struct hist_entry, rb_node); |
| 145 | next = rb_next(&n->rb_node); | 147 | next = rb_next(&n->rb_node); |
| 146 | 148 | ||
| 147 | rb_erase(&n->rb_node, &hist); | 149 | rb_erase(&n->rb_node, &self->hists); |
| 148 | collapse__insert_entry(n); | 150 | collapse__insert_entry(&tmp, n); |
| 149 | } | 151 | } |
| 152 | |||
| 153 | self->hists = tmp; | ||
| 150 | } | 154 | } |
| 151 | 155 | ||
| 152 | /* | 156 | /* |
| 153 | * reverse the map, sort on count. | 157 | * reverse the map, sort on count. |
| 154 | */ | 158 | */ |
| 155 | 159 | ||
| 156 | void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) | 160 | static void perf_session__insert_output_hist_entry(struct rb_root *root, |
| 161 | struct hist_entry *he, | ||
| 162 | u64 min_callchain_hits) | ||
| 157 | { | 163 | { |
| 158 | struct rb_node **p = &output_hists.rb_node; | 164 | struct rb_node **p = &root->rb_node; |
| 159 | struct rb_node *parent = NULL; | 165 | struct rb_node *parent = NULL; |
| 160 | struct hist_entry *iter; | 166 | struct hist_entry *iter; |
| 161 | 167 | ||
| 162 | if (callchain) | 168 | if (symbol_conf.use_callchain) |
| 163 | callchain_param.sort(&he->sorted_chain, &he->callchain, | 169 | callchain_param.sort(&he->sorted_chain, &he->callchain, |
| 164 | min_callchain_hits, &callchain_param); | 170 | min_callchain_hits, &callchain_param); |
| 165 | 171 | ||
| @@ -174,29 +180,483 @@ void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) | |||
| 174 | } | 180 | } |
| 175 | 181 | ||
| 176 | rb_link_node(&he->rb_node, parent, p); | 182 | rb_link_node(&he->rb_node, parent, p); |
| 177 | rb_insert_color(&he->rb_node, &output_hists); | 183 | rb_insert_color(&he->rb_node, root); |
| 178 | } | 184 | } |
| 179 | 185 | ||
| 180 | void output__resort(u64 total_samples) | 186 | void perf_session__output_resort(struct perf_session *self, u64 total_samples) |
| 181 | { | 187 | { |
| 188 | struct rb_root tmp; | ||
| 182 | struct rb_node *next; | 189 | struct rb_node *next; |
| 183 | struct hist_entry *n; | 190 | struct hist_entry *n; |
| 184 | struct rb_root *tree = &hist; | ||
| 185 | u64 min_callchain_hits; | 191 | u64 min_callchain_hits; |
| 186 | 192 | ||
| 187 | min_callchain_hits = | 193 | min_callchain_hits = |
| 188 | total_samples * (callchain_param.min_percent / 100); | 194 | total_samples * (callchain_param.min_percent / 100); |
| 189 | 195 | ||
| 190 | if (sort__need_collapse) | 196 | tmp = RB_ROOT; |
| 191 | tree = &collapse_hists; | 197 | next = rb_first(&self->hists); |
| 192 | |||
| 193 | next = rb_first(tree); | ||
| 194 | 198 | ||
| 195 | while (next) { | 199 | while (next) { |
| 196 | n = rb_entry(next, struct hist_entry, rb_node); | 200 | n = rb_entry(next, struct hist_entry, rb_node); |
| 197 | next = rb_next(&n->rb_node); | 201 | next = rb_next(&n->rb_node); |
| 198 | 202 | ||
| 199 | rb_erase(&n->rb_node, tree); | 203 | rb_erase(&n->rb_node, &self->hists); |
| 200 | output__insert_entry(n, min_callchain_hits); | 204 | perf_session__insert_output_hist_entry(&tmp, n, |
| 205 | min_callchain_hits); | ||
| 206 | } | ||
| 207 | |||
| 208 | self->hists = tmp; | ||
| 209 | } | ||
| 210 | |||
| 211 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | ||
| 212 | { | ||
| 213 | int i; | ||
| 214 | int ret = fprintf(fp, " "); | ||
| 215 | |||
| 216 | for (i = 0; i < left_margin; i++) | ||
| 217 | ret += fprintf(fp, " "); | ||
| 218 | |||
| 219 | return ret; | ||
| 220 | } | ||
| 221 | |||
| 222 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask, | ||
| 223 | int left_margin) | ||
| 224 | { | ||
| 225 | int i; | ||
| 226 | size_t ret = callchain__fprintf_left_margin(fp, left_margin); | ||
| 227 | |||
| 228 | for (i = 0; i < depth; i++) | ||
| 229 | if (depth_mask & (1 << i)) | ||
| 230 | ret += fprintf(fp, "| "); | ||
| 231 | else | ||
| 232 | ret += fprintf(fp, " "); | ||
| 233 | |||
| 234 | ret += fprintf(fp, "\n"); | ||
| 235 | |||
| 236 | return ret; | ||
| 237 | } | ||
| 238 | |||
| 239 | static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | ||
| 240 | int depth, int depth_mask, int count, | ||
| 241 | u64 total_samples, int hits, | ||
| 242 | int left_margin) | ||
| 243 | { | ||
| 244 | int i; | ||
| 245 | size_t ret = 0; | ||
| 246 | |||
| 247 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 248 | for (i = 0; i < depth; i++) { | ||
| 249 | if (depth_mask & (1 << i)) | ||
| 250 | ret += fprintf(fp, "|"); | ||
| 251 | else | ||
| 252 | ret += fprintf(fp, " "); | ||
| 253 | if (!count && i == depth - 1) { | ||
| 254 | double percent; | ||
| 255 | |||
| 256 | percent = hits * 100.0 / total_samples; | ||
| 257 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); | ||
| 258 | } else | ||
| 259 | ret += fprintf(fp, "%s", " "); | ||
| 260 | } | ||
| 261 | if (chain->sym) | ||
| 262 | ret += fprintf(fp, "%s\n", chain->sym->name); | ||
| 263 | else | ||
| 264 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | ||
| 265 | |||
| 266 | return ret; | ||
| 267 | } | ||
| 268 | |||
| 269 | static struct symbol *rem_sq_bracket; | ||
| 270 | static struct callchain_list rem_hits; | ||
| 271 | |||
| 272 | static void init_rem_hits(void) | ||
| 273 | { | ||
| 274 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
| 275 | if (!rem_sq_bracket) { | ||
| 276 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
| 277 | return; | ||
| 278 | } | ||
| 279 | |||
| 280 | strcpy(rem_sq_bracket->name, "[...]"); | ||
| 281 | rem_hits.sym = rem_sq_bracket; | ||
| 282 | } | ||
| 283 | |||
| 284 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
| 285 | u64 total_samples, int depth, | ||
| 286 | int depth_mask, int left_margin) | ||
| 287 | { | ||
| 288 | struct rb_node *node, *next; | ||
| 289 | struct callchain_node *child; | ||
| 290 | struct callchain_list *chain; | ||
| 291 | int new_depth_mask = depth_mask; | ||
| 292 | u64 new_total; | ||
| 293 | u64 remaining; | ||
| 294 | size_t ret = 0; | ||
| 295 | int i; | ||
| 296 | |||
| 297 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
| 298 | new_total = self->children_hit; | ||
| 299 | else | ||
| 300 | new_total = total_samples; | ||
| 301 | |||
| 302 | remaining = new_total; | ||
| 303 | |||
| 304 | node = rb_first(&self->rb_root); | ||
| 305 | while (node) { | ||
| 306 | u64 cumul; | ||
| 307 | |||
| 308 | child = rb_entry(node, struct callchain_node, rb_node); | ||
| 309 | cumul = cumul_hits(child); | ||
| 310 | remaining -= cumul; | ||
| 311 | |||
| 312 | /* | ||
| 313 | * The depth mask manages the output of pipes that show | ||
| 314 | * the depth. We don't want to keep the pipes of the current | ||
| 315 | * level for the last child of this depth. | ||
| 316 | * Except if we have remaining filtered hits. They will | ||
| 317 | * supersede the last child | ||
| 318 | */ | ||
| 319 | next = rb_next(node); | ||
| 320 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) | ||
| 321 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 322 | |||
| 323 | /* | ||
| 324 | * But we keep the older depth mask for the line seperator | ||
| 325 | * to keep the level link until we reach the last child | ||
| 326 | */ | ||
| 327 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | ||
| 328 | left_margin); | ||
| 329 | i = 0; | ||
| 330 | list_for_each_entry(chain, &child->val, list) { | ||
| 331 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 332 | continue; | ||
| 333 | ret += ipchain__fprintf_graph(fp, chain, depth, | ||
| 334 | new_depth_mask, i++, | ||
| 335 | new_total, | ||
| 336 | cumul, | ||
| 337 | left_margin); | ||
| 338 | } | ||
| 339 | ret += __callchain__fprintf_graph(fp, child, new_total, | ||
| 340 | depth + 1, | ||
| 341 | new_depth_mask | (1 << depth), | ||
| 342 | left_margin); | ||
| 343 | node = next; | ||
| 344 | } | ||
| 345 | |||
| 346 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
| 347 | remaining && remaining != new_total) { | ||
| 348 | |||
| 349 | if (!rem_sq_bracket) | ||
| 350 | return ret; | ||
| 351 | |||
| 352 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 353 | |||
| 354 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
| 355 | new_depth_mask, 0, new_total, | ||
| 356 | remaining, left_margin); | ||
| 357 | } | ||
| 358 | |||
| 359 | return ret; | ||
| 360 | } | ||
| 361 | |||
| 362 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | ||
| 363 | u64 total_samples, int left_margin) | ||
| 364 | { | ||
| 365 | struct callchain_list *chain; | ||
| 366 | bool printed = false; | ||
| 367 | int i = 0; | ||
| 368 | int ret = 0; | ||
| 369 | |||
| 370 | list_for_each_entry(chain, &self->val, list) { | ||
| 371 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 372 | continue; | ||
| 373 | |||
| 374 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
| 375 | continue; | ||
| 376 | |||
| 377 | if (!printed) { | ||
| 378 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 379 | ret += fprintf(fp, "|\n"); | ||
| 380 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 381 | ret += fprintf(fp, "---"); | ||
| 382 | |||
| 383 | left_margin += 3; | ||
| 384 | printed = true; | ||
| 385 | } else | ||
| 386 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
| 387 | |||
| 388 | if (chain->sym) | ||
| 389 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
| 390 | else | ||
| 391 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
| 392 | } | ||
| 393 | |||
| 394 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | ||
| 395 | |||
| 396 | return ret; | ||
| 397 | } | ||
| 398 | |||
| 399 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | ||
| 400 | u64 total_samples) | ||
| 401 | { | ||
| 402 | struct callchain_list *chain; | ||
| 403 | size_t ret = 0; | ||
| 404 | |||
| 405 | if (!self) | ||
| 406 | return 0; | ||
| 407 | |||
| 408 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | ||
| 409 | |||
| 410 | |||
| 411 | list_for_each_entry(chain, &self->val, list) { | ||
| 412 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
| 413 | continue; | ||
| 414 | if (chain->sym) | ||
| 415 | ret += fprintf(fp, " %s\n", chain->sym->name); | ||
| 416 | else | ||
| 417 | ret += fprintf(fp, " %p\n", | ||
| 418 | (void *)(long)chain->ip); | ||
| 419 | } | ||
| 420 | |||
| 421 | return ret; | ||
| 422 | } | ||
| 423 | |||
| 424 | static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | ||
| 425 | u64 total_samples, int left_margin) | ||
| 426 | { | ||
| 427 | struct rb_node *rb_node; | ||
| 428 | struct callchain_node *chain; | ||
| 429 | size_t ret = 0; | ||
| 430 | |||
| 431 | rb_node = rb_first(&self->sorted_chain); | ||
| 432 | while (rb_node) { | ||
| 433 | double percent; | ||
| 434 | |||
| 435 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
| 436 | percent = chain->hit * 100.0 / total_samples; | ||
| 437 | switch (callchain_param.mode) { | ||
| 438 | case CHAIN_FLAT: | ||
| 439 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | ||
| 440 | percent); | ||
| 441 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
| 442 | break; | ||
| 443 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
| 444 | case CHAIN_GRAPH_REL: | ||
| 445 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
| 446 | left_margin); | ||
| 447 | case CHAIN_NONE: | ||
| 448 | default: | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | ret += fprintf(fp, "\n"); | ||
| 452 | rb_node = rb_next(rb_node); | ||
| 453 | } | ||
| 454 | |||
| 455 | return ret; | ||
| 456 | } | ||
| 457 | |||
| 458 | static size_t hist_entry__fprintf(struct hist_entry *self, | ||
| 459 | struct perf_session *session, | ||
| 460 | struct perf_session *pair_session, | ||
| 461 | bool show_displacement, | ||
| 462 | long displacement, FILE *fp) | ||
| 463 | { | ||
| 464 | struct sort_entry *se; | ||
| 465 | u64 count, total; | ||
| 466 | const char *sep = symbol_conf.field_sep; | ||
| 467 | size_t ret; | ||
| 468 | |||
| 469 | if (symbol_conf.exclude_other && !self->parent) | ||
| 470 | return 0; | ||
| 471 | |||
| 472 | if (pair_session) { | ||
| 473 | count = self->pair ? self->pair->count : 0; | ||
| 474 | total = pair_session->events_stats.total; | ||
| 475 | } else { | ||
| 476 | count = self->count; | ||
| 477 | total = session->events_stats.total; | ||
| 478 | } | ||
| 479 | |||
| 480 | if (total) | ||
| 481 | ret = percent_color_fprintf(fp, sep ? "%.2f" : " %6.2f%%", | ||
| 482 | (count * 100.0) / total); | ||
| 483 | else | ||
| 484 | ret = fprintf(fp, sep ? "%lld" : "%12lld ", count); | ||
| 485 | |||
| 486 | if (symbol_conf.show_nr_samples) { | ||
| 487 | if (sep) | ||
| 488 | fprintf(fp, "%c%lld", *sep, count); | ||
| 489 | else | ||
| 490 | fprintf(fp, "%11lld", count); | ||
| 491 | } | ||
| 492 | |||
| 493 | if (pair_session) { | ||
| 494 | char bf[32]; | ||
| 495 | double old_percent = 0, new_percent = 0, diff; | ||
| 496 | |||
| 497 | if (total > 0) | ||
| 498 | old_percent = (count * 100.0) / total; | ||
| 499 | if (session->events_stats.total > 0) | ||
| 500 | new_percent = (self->count * 100.0) / session->events_stats.total; | ||
| 501 | |||
| 502 | diff = new_percent - old_percent; | ||
| 503 | |||
| 504 | if (fabs(diff) >= 0.01) | ||
| 505 | snprintf(bf, sizeof(bf), "%+4.2F%%", diff); | ||
| 506 | else | ||
| 507 | snprintf(bf, sizeof(bf), " "); | ||
| 508 | |||
| 509 | if (sep) | ||
| 510 | ret += fprintf(fp, "%c%s", *sep, bf); | ||
| 511 | else | ||
| 512 | ret += fprintf(fp, "%11.11s", bf); | ||
| 513 | |||
| 514 | if (show_displacement) { | ||
| 515 | if (displacement) | ||
| 516 | snprintf(bf, sizeof(bf), "%+4ld", displacement); | ||
| 517 | else | ||
| 518 | snprintf(bf, sizeof(bf), " "); | ||
| 519 | |||
| 520 | if (sep) | ||
| 521 | fprintf(fp, "%c%s", *sep, bf); | ||
| 522 | else | ||
| 523 | fprintf(fp, "%6.6s", bf); | ||
| 524 | } | ||
| 525 | } | ||
| 526 | |||
| 527 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 528 | if (se->elide) | ||
| 529 | continue; | ||
| 530 | |||
| 531 | fprintf(fp, "%s", sep ?: " "); | ||
| 532 | ret += se->print(fp, self, se->width ? *se->width : 0); | ||
| 533 | } | ||
| 534 | |||
| 535 | ret += fprintf(fp, "\n"); | ||
| 536 | |||
| 537 | if (symbol_conf.use_callchain) { | ||
| 538 | int left_margin = 0; | ||
| 539 | |||
| 540 | if (sort__first_dimension == SORT_COMM) { | ||
| 541 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | ||
| 542 | list); | ||
| 543 | left_margin = se->width ? *se->width : 0; | ||
| 544 | left_margin -= thread__comm_len(self->thread); | ||
| 545 | } | ||
| 546 | |||
| 547 | hist_entry_callchain__fprintf(fp, self, session->events_stats.total, | ||
| 548 | left_margin); | ||
| 549 | } | ||
| 550 | |||
| 551 | return ret; | ||
| 552 | } | ||
| 553 | |||
| 554 | size_t perf_session__fprintf_hists(struct perf_session *self, | ||
| 555 | struct perf_session *pair, | ||
| 556 | bool show_displacement, FILE *fp) | ||
| 557 | { | ||
| 558 | struct sort_entry *se; | ||
| 559 | struct rb_node *nd; | ||
| 560 | size_t ret = 0; | ||
| 561 | unsigned long position = 1; | ||
| 562 | long displacement = 0; | ||
| 563 | unsigned int width; | ||
| 564 | const char *sep = symbol_conf.field_sep; | ||
| 565 | char *col_width = symbol_conf.col_width_list_str; | ||
| 566 | |||
| 567 | init_rem_hits(); | ||
| 568 | |||
| 569 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | ||
| 570 | |||
| 571 | if (symbol_conf.show_nr_samples) { | ||
| 572 | if (sep) | ||
| 573 | fprintf(fp, "%cSamples", *sep); | ||
| 574 | else | ||
| 575 | fputs(" Samples ", fp); | ||
| 576 | } | ||
| 577 | |||
| 578 | if (pair) { | ||
| 579 | if (sep) | ||
| 580 | ret += fprintf(fp, "%cDelta", *sep); | ||
| 581 | else | ||
| 582 | ret += fprintf(fp, " Delta "); | ||
| 583 | |||
| 584 | if (show_displacement) { | ||
| 585 | if (sep) | ||
| 586 | ret += fprintf(fp, "%cDisplacement", *sep); | ||
| 587 | else | ||
| 588 | ret += fprintf(fp, " Displ"); | ||
| 589 | } | ||
| 590 | } | ||
| 591 | |||
| 592 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 593 | if (se->elide) | ||
| 594 | continue; | ||
| 595 | if (sep) { | ||
| 596 | fprintf(fp, "%c%s", *sep, se->header); | ||
| 597 | continue; | ||
| 598 | } | ||
| 599 | width = strlen(se->header); | ||
| 600 | if (se->width) { | ||
| 601 | if (symbol_conf.col_width_list_str) { | ||
| 602 | if (col_width) { | ||
| 603 | *se->width = atoi(col_width); | ||
| 604 | col_width = strchr(col_width, ','); | ||
| 605 | if (col_width) | ||
| 606 | ++col_width; | ||
| 607 | } | ||
| 608 | } | ||
| 609 | width = *se->width = max(*se->width, width); | ||
| 610 | } | ||
| 611 | fprintf(fp, " %*s", width, se->header); | ||
| 612 | } | ||
| 613 | fprintf(fp, "\n"); | ||
| 614 | |||
| 615 | if (sep) | ||
| 616 | goto print_entries; | ||
| 617 | |||
| 618 | fprintf(fp, "# ........"); | ||
| 619 | if (symbol_conf.show_nr_samples) | ||
| 620 | fprintf(fp, " .........."); | ||
| 621 | if (pair) { | ||
| 622 | fprintf(fp, " .........."); | ||
| 623 | if (show_displacement) | ||
| 624 | fprintf(fp, " ....."); | ||
| 625 | } | ||
| 626 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
| 627 | unsigned int i; | ||
| 628 | |||
| 629 | if (se->elide) | ||
| 630 | continue; | ||
| 631 | |||
| 632 | fprintf(fp, " "); | ||
| 633 | if (se->width) | ||
| 634 | width = *se->width; | ||
| 635 | else | ||
| 636 | width = strlen(se->header); | ||
| 637 | for (i = 0; i < width; i++) | ||
| 638 | fprintf(fp, "."); | ||
| 639 | } | ||
| 640 | |||
| 641 | fprintf(fp, "\n#\n"); | ||
| 642 | |||
| 643 | print_entries: | ||
| 644 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { | ||
| 645 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
| 646 | |||
| 647 | if (show_displacement) { | ||
| 648 | if (h->pair != NULL) | ||
| 649 | displacement = ((long)h->pair->position - | ||
| 650 | (long)position); | ||
| 651 | else | ||
| 652 | displacement = 0; | ||
| 653 | ++position; | ||
| 654 | } | ||
| 655 | ret += hist_entry__fprintf(h, self, pair, show_displacement, | ||
| 656 | displacement, fp); | ||
| 201 | } | 657 | } |
| 658 | |||
| 659 | free(rem_sq_bracket); | ||
| 660 | |||
| 661 | return ret; | ||
| 202 | } | 662 | } |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3020db0c9292..e5f99b24048b 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -1,50 +1,27 @@ | |||
| 1 | #ifndef __PERF_HIST_H | 1 | #ifndef __PERF_HIST_H |
| 2 | #define __PERF_HIST_H | 2 | #define __PERF_HIST_H |
| 3 | #include "../builtin.h" | ||
| 4 | 3 | ||
| 5 | #include "util.h" | 4 | #include <linux/types.h> |
| 6 | |||
| 7 | #include "color.h" | ||
| 8 | #include <linux/list.h> | ||
| 9 | #include "cache.h" | ||
| 10 | #include <linux/rbtree.h> | ||
| 11 | #include "symbol.h" | ||
| 12 | #include "string.h" | ||
| 13 | #include "callchain.h" | 5 | #include "callchain.h" |
| 14 | #include "strlist.h" | ||
| 15 | #include "values.h" | ||
| 16 | |||
| 17 | #include "../perf.h" | ||
| 18 | #include "debug.h" | ||
| 19 | #include "header.h" | ||
| 20 | |||
| 21 | #include "parse-options.h" | ||
| 22 | #include "parse-events.h" | ||
| 23 | 6 | ||
| 24 | #include "thread.h" | ||
| 25 | #include "sort.h" | ||
| 26 | |||
| 27 | extern struct rb_root hist; | ||
| 28 | extern struct rb_root collapse_hists; | ||
| 29 | extern struct rb_root output_hists; | ||
| 30 | extern int callchain; | ||
| 31 | extern struct callchain_param callchain_param; | 7 | extern struct callchain_param callchain_param; |
| 32 | extern unsigned long total; | ||
| 33 | extern unsigned long total_mmap; | ||
| 34 | extern unsigned long total_comm; | ||
| 35 | extern unsigned long total_fork; | ||
| 36 | extern unsigned long total_unknown; | ||
| 37 | extern unsigned long total_lost; | ||
| 38 | 8 | ||
| 39 | struct hist_entry *__hist_entry__add(struct addr_location *al, | 9 | struct perf_session; |
| 40 | struct symbol *parent, | 10 | struct hist_entry; |
| 41 | u64 count, bool *hit); | 11 | struct addr_location; |
| 12 | struct symbol; | ||
| 13 | |||
| 14 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, | ||
| 15 | struct addr_location *al, | ||
| 16 | struct symbol *parent, | ||
| 17 | u64 count, bool *hit); | ||
| 42 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 18 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); |
| 43 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 19 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
| 44 | extern void hist_entry__free(struct hist_entry *); | 20 | void hist_entry__free(struct hist_entry *); |
| 45 | extern void collapse__insert_entry(struct hist_entry *); | ||
| 46 | extern void collapse__resort(void); | ||
| 47 | extern void output__insert_entry(struct hist_entry *, u64); | ||
| 48 | extern void output__resort(u64); | ||
| 49 | 21 | ||
| 22 | void perf_session__output_resort(struct perf_session *self, u64 total_samples); | ||
| 23 | void perf_session__collapse_resort(struct perf_session *self); | ||
| 24 | size_t perf_session__fprintf_hists(struct perf_session *self, | ||
| 25 | struct perf_session *pair, | ||
| 26 | bool show_displacement, FILE *fp); | ||
| 50 | #endif /* __PERF_HIST_H */ | 27 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 76bdca640a9b..c4d55a0da2ea 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -104,11 +104,16 @@ void map__fixup_end(struct map *self) | |||
| 104 | 104 | ||
| 105 | #define DSO__DELETED "(deleted)" | 105 | #define DSO__DELETED "(deleted)" |
| 106 | 106 | ||
| 107 | static int map__load(struct map *self, symbol_filter_t filter) | 107 | int map__load(struct map *self, struct perf_session *session, |
| 108 | symbol_filter_t filter) | ||
| 108 | { | 109 | { |
| 109 | const char *name = self->dso->long_name; | 110 | const char *name = self->dso->long_name; |
| 110 | int nr = dso__load(self->dso, self, filter); | 111 | int nr; |
| 111 | 112 | ||
| 113 | if (dso__loaded(self->dso, self->type)) | ||
| 114 | return 0; | ||
| 115 | |||
| 116 | nr = dso__load(self->dso, self, session, filter); | ||
| 112 | if (nr < 0) { | 117 | if (nr < 0) { |
| 113 | if (self->dso->has_build_id) { | 118 | if (self->dso->has_build_id) { |
| 114 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 119 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
| @@ -143,19 +148,20 @@ static int map__load(struct map *self, symbol_filter_t filter) | |||
| 143 | return 0; | 148 | return 0; |
| 144 | } | 149 | } |
| 145 | 150 | ||
| 146 | struct symbol *map__find_symbol(struct map *self, u64 addr, | 151 | struct symbol *map__find_symbol(struct map *self, struct perf_session *session, |
| 147 | symbol_filter_t filter) | 152 | u64 addr, symbol_filter_t filter) |
| 148 | { | 153 | { |
| 149 | if (!dso__loaded(self->dso, self->type) && map__load(self, filter) < 0) | 154 | if (map__load(self, session, filter) < 0) |
| 150 | return NULL; | 155 | return NULL; |
| 151 | 156 | ||
| 152 | return dso__find_symbol(self->dso, self->type, addr); | 157 | return dso__find_symbol(self->dso, self->type, addr); |
| 153 | } | 158 | } |
| 154 | 159 | ||
| 155 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | 160 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, |
| 161 | struct perf_session *session, | ||
| 156 | symbol_filter_t filter) | 162 | symbol_filter_t filter) |
| 157 | { | 163 | { |
| 158 | if (!dso__loaded(self->dso, self->type) && map__load(self, filter) < 0) | 164 | if (map__load(self, session, filter) < 0) |
| 159 | return NULL; | 165 | return NULL; |
| 160 | 166 | ||
| 161 | if (!dso__sorted_by_name(self->dso, self->type)) | 167 | if (!dso__sorted_by_name(self->dso, self->type)) |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index d14a4585bcaf..2ca62154f79b 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -69,10 +69,23 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 69 | char c, nc = 0; | 69 | char c, nc = 0; |
| 70 | /* | 70 | /* |
| 71 | * <Syntax> | 71 | * <Syntax> |
| 72 | * perf probe SRC:LN | 72 | * perf probe [EVENT=]SRC:LN |
| 73 | * perf probe FUNC[+OFFS|%return][@SRC] | 73 | * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] |
| 74 | * | ||
| 75 | * TODO:Group name support | ||
| 74 | */ | 76 | */ |
| 75 | 77 | ||
| 78 | ptr = strchr(arg, '='); | ||
| 79 | if (ptr) { /* Event name */ | ||
| 80 | *ptr = '\0'; | ||
| 81 | tmp = ptr + 1; | ||
| 82 | ptr = strchr(arg, ':'); | ||
| 83 | if (ptr) /* Group name is not supported yet. */ | ||
| 84 | semantic_error("Group name is not supported yet."); | ||
| 85 | pp->event = strdup(arg); | ||
| 86 | arg = tmp; | ||
| 87 | } | ||
| 88 | |||
| 76 | ptr = strpbrk(arg, ":+@%"); | 89 | ptr = strpbrk(arg, ":+@%"); |
| 77 | if (ptr) { | 90 | if (ptr) { |
| 78 | nc = *ptr; | 91 | nc = *ptr; |
| @@ -150,10 +163,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 150 | } | 163 | } |
| 151 | 164 | ||
| 152 | /* Parse perf-probe event definition */ | 165 | /* Parse perf-probe event definition */ |
| 153 | int parse_perf_probe_event(const char *str, struct probe_point *pp) | 166 | void parse_perf_probe_event(const char *str, struct probe_point *pp, |
| 167 | bool *need_dwarf) | ||
| 154 | { | 168 | { |
| 155 | char **argv; | 169 | char **argv; |
| 156 | int argc, i, need_dwarf = 0; | 170 | int argc, i; |
| 171 | |||
| 172 | *need_dwarf = false; | ||
| 157 | 173 | ||
| 158 | argv = argv_split(str, &argc); | 174 | argv = argv_split(str, &argc); |
| 159 | if (!argv) | 175 | if (!argv) |
| @@ -164,7 +180,7 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp) | |||
| 164 | /* Parse probe point */ | 180 | /* Parse probe point */ |
| 165 | parse_perf_probe_probepoint(argv[0], pp); | 181 | parse_perf_probe_probepoint(argv[0], pp); |
| 166 | if (pp->file || pp->line) | 182 | if (pp->file || pp->line) |
| 167 | need_dwarf = 1; | 183 | *need_dwarf = true; |
| 168 | 184 | ||
| 169 | /* Copy arguments and ensure return probe has no C argument */ | 185 | /* Copy arguments and ensure return probe has no C argument */ |
| 170 | pp->nr_args = argc - 1; | 186 | pp->nr_args = argc - 1; |
| @@ -177,17 +193,15 @@ int parse_perf_probe_event(const char *str, struct probe_point *pp) | |||
| 177 | if (pp->retprobe) | 193 | if (pp->retprobe) |
| 178 | semantic_error("You can't specify local" | 194 | semantic_error("You can't specify local" |
| 179 | " variable for kretprobe"); | 195 | " variable for kretprobe"); |
| 180 | need_dwarf = 1; | 196 | *need_dwarf = true; |
| 181 | } | 197 | } |
| 182 | } | 198 | } |
| 183 | 199 | ||
| 184 | argv_free(argv); | 200 | argv_free(argv); |
| 185 | return need_dwarf; | ||
| 186 | } | 201 | } |
| 187 | 202 | ||
| 188 | /* Parse kprobe_events event into struct probe_point */ | 203 | /* Parse kprobe_events event into struct probe_point */ |
| 189 | void parse_trace_kprobe_event(const char *str, char **group, char **event, | 204 | void parse_trace_kprobe_event(const char *str, struct probe_point *pp) |
| 190 | struct probe_point *pp) | ||
| 191 | { | 205 | { |
| 192 | char pr; | 206 | char pr; |
| 193 | char *p; | 207 | char *p; |
| @@ -203,18 +217,17 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event, | |||
| 203 | 217 | ||
| 204 | /* Scan event and group name. */ | 218 | /* Scan event and group name. */ |
| 205 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", | 219 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", |
| 206 | &pr, (float *)(void *)group, (float *)(void *)event); | 220 | &pr, (float *)(void *)&pp->group, |
| 221 | (float *)(void *)&pp->event); | ||
| 207 | if (ret != 3) | 222 | if (ret != 3) |
| 208 | semantic_error("Failed to parse event name: %s", argv[0]); | 223 | semantic_error("Failed to parse event name: %s", argv[0]); |
| 209 | pr_debug("Group:%s Event:%s probe:%c\n", *group, *event, pr); | 224 | pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); |
| 210 | |||
| 211 | if (!pp) | ||
| 212 | goto end; | ||
| 213 | 225 | ||
| 214 | pp->retprobe = (pr == 'r'); | 226 | pp->retprobe = (pr == 'r'); |
| 215 | 227 | ||
| 216 | /* Scan function name and offset */ | 228 | /* Scan function name and offset */ |
| 217 | ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, &pp->offset); | 229 | ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, |
| 230 | &pp->offset); | ||
| 218 | if (ret == 1) | 231 | if (ret == 1) |
| 219 | pp->offset = 0; | 232 | pp->offset = 0; |
| 220 | 233 | ||
| @@ -233,15 +246,15 @@ void parse_trace_kprobe_event(const char *str, char **group, char **event, | |||
| 233 | die("Failed to copy argument."); | 246 | die("Failed to copy argument."); |
| 234 | } | 247 | } |
| 235 | 248 | ||
| 236 | end: | ||
| 237 | argv_free(argv); | 249 | argv_free(argv); |
| 238 | } | 250 | } |
| 239 | 251 | ||
| 240 | int synthesize_perf_probe_event(struct probe_point *pp) | 252 | /* Synthesize only probe point (not argument) */ |
| 253 | int synthesize_perf_probe_point(struct probe_point *pp) | ||
| 241 | { | 254 | { |
| 242 | char *buf; | 255 | char *buf; |
| 243 | char offs[64] = "", line[64] = ""; | 256 | char offs[64] = "", line[64] = ""; |
| 244 | int i, len, ret; | 257 | int ret; |
| 245 | 258 | ||
| 246 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | 259 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); |
| 247 | if (!buf) | 260 | if (!buf) |
| @@ -262,10 +275,24 @@ int synthesize_perf_probe_event(struct probe_point *pp) | |||
| 262 | offs, pp->retprobe ? "%return" : "", line); | 275 | offs, pp->retprobe ? "%return" : "", line); |
| 263 | else | 276 | else |
| 264 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); | 277 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); |
| 265 | if (ret <= 0) | 278 | if (ret <= 0) { |
| 266 | goto error; | 279 | error: |
| 267 | len = ret; | 280 | free(pp->probes[0]); |
| 281 | pp->probes[0] = NULL; | ||
| 282 | } | ||
| 283 | return ret; | ||
| 284 | } | ||
| 268 | 285 | ||
| 286 | int synthesize_perf_probe_event(struct probe_point *pp) | ||
| 287 | { | ||
| 288 | char *buf; | ||
| 289 | int i, len, ret; | ||
| 290 | |||
| 291 | len = synthesize_perf_probe_point(pp); | ||
| 292 | if (len < 0) | ||
| 293 | return 0; | ||
| 294 | |||
| 295 | buf = pp->probes[0]; | ||
| 269 | for (i = 0; i < pp->nr_args; i++) { | 296 | for (i = 0; i < pp->nr_args; i++) { |
| 270 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | 297 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", |
| 271 | pp->args[i]); | 298 | pp->args[i]); |
| @@ -278,6 +305,7 @@ int synthesize_perf_probe_event(struct probe_point *pp) | |||
| 278 | return pp->found; | 305 | return pp->found; |
| 279 | error: | 306 | error: |
| 280 | free(pp->probes[0]); | 307 | free(pp->probes[0]); |
| 308 | pp->probes[0] = NULL; | ||
| 281 | 309 | ||
| 282 | return ret; | 310 | return ret; |
| 283 | } | 311 | } |
| @@ -307,6 +335,7 @@ int synthesize_trace_kprobe_event(struct probe_point *pp) | |||
| 307 | return pp->found; | 335 | return pp->found; |
| 308 | error: | 336 | error: |
| 309 | free(pp->probes[0]); | 337 | free(pp->probes[0]); |
| 338 | pp->probes[0] = NULL; | ||
| 310 | 339 | ||
| 311 | return ret; | 340 | return ret; |
| 312 | } | 341 | } |
| @@ -366,6 +395,10 @@ static void clear_probe_point(struct probe_point *pp) | |||
| 366 | { | 395 | { |
| 367 | int i; | 396 | int i; |
| 368 | 397 | ||
| 398 | if (pp->event) | ||
| 399 | free(pp->event); | ||
| 400 | if (pp->group) | ||
| 401 | free(pp->group); | ||
| 369 | if (pp->function) | 402 | if (pp->function) |
| 370 | free(pp->function); | 403 | free(pp->function); |
| 371 | if (pp->file) | 404 | if (pp->file) |
| @@ -380,13 +413,15 @@ static void clear_probe_point(struct probe_point *pp) | |||
| 380 | } | 413 | } |
| 381 | 414 | ||
| 382 | /* Show an event */ | 415 | /* Show an event */ |
| 383 | static void show_perf_probe_event(const char *group, const char *event, | 416 | static void show_perf_probe_event(const char *event, const char *place, |
| 384 | const char *place, struct probe_point *pp) | 417 | struct probe_point *pp) |
| 385 | { | 418 | { |
| 386 | int i; | 419 | int i, ret; |
| 387 | char buf[128]; | 420 | char buf[128]; |
| 388 | 421 | ||
| 389 | e_snprintf(buf, 128, "%s:%s", group, event); | 422 | ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); |
| 423 | if (ret < 0) | ||
| 424 | die("Failed to copy event: %s", strerror(-ret)); | ||
| 390 | printf(" %-40s (on %s", buf, place); | 425 | printf(" %-40s (on %s", buf, place); |
| 391 | 426 | ||
| 392 | if (pp->nr_args > 0) { | 427 | if (pp->nr_args > 0) { |
| @@ -400,9 +435,7 @@ static void show_perf_probe_event(const char *group, const char *event, | |||
| 400 | /* List up current perf-probe events */ | 435 | /* List up current perf-probe events */ |
| 401 | void show_perf_probe_events(void) | 436 | void show_perf_probe_events(void) |
| 402 | { | 437 | { |
| 403 | unsigned int i; | 438 | int fd; |
| 404 | int fd, nr; | ||
| 405 | char *group, *event; | ||
| 406 | struct probe_point pp; | 439 | struct probe_point pp; |
| 407 | struct strlist *rawlist; | 440 | struct strlist *rawlist; |
| 408 | struct str_node *ent; | 441 | struct str_node *ent; |
| @@ -411,18 +444,12 @@ void show_perf_probe_events(void) | |||
| 411 | rawlist = get_trace_kprobe_event_rawlist(fd); | 444 | rawlist = get_trace_kprobe_event_rawlist(fd); |
| 412 | close(fd); | 445 | close(fd); |
| 413 | 446 | ||
| 414 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { | 447 | strlist__for_each(ent, rawlist) { |
| 415 | ent = strlist__entry(rawlist, i); | 448 | parse_trace_kprobe_event(ent->s, &pp); |
| 416 | parse_trace_kprobe_event(ent->s, &group, &event, &pp); | ||
| 417 | /* Synthesize only event probe point */ | 449 | /* Synthesize only event probe point */ |
| 418 | nr = pp.nr_args; | 450 | synthesize_perf_probe_point(&pp); |
| 419 | pp.nr_args = 0; | ||
| 420 | synthesize_perf_probe_event(&pp); | ||
| 421 | pp.nr_args = nr; | ||
| 422 | /* Show an event */ | 451 | /* Show an event */ |
| 423 | show_perf_probe_event(group, event, pp.probes[0], &pp); | 452 | show_perf_probe_event(pp.event, pp.probes[0], &pp); |
| 424 | free(group); | ||
| 425 | free(event); | ||
| 426 | clear_probe_point(&pp); | 453 | clear_probe_point(&pp); |
| 427 | } | 454 | } |
| 428 | 455 | ||
| @@ -432,26 +459,25 @@ void show_perf_probe_events(void) | |||
| 432 | /* Get current perf-probe event names */ | 459 | /* Get current perf-probe event names */ |
| 433 | static struct strlist *get_perf_event_names(int fd, bool include_group) | 460 | static struct strlist *get_perf_event_names(int fd, bool include_group) |
| 434 | { | 461 | { |
| 435 | unsigned int i; | ||
| 436 | char *group, *event; | ||
| 437 | char buf[128]; | 462 | char buf[128]; |
| 438 | struct strlist *sl, *rawlist; | 463 | struct strlist *sl, *rawlist; |
| 439 | struct str_node *ent; | 464 | struct str_node *ent; |
| 465 | struct probe_point pp; | ||
| 440 | 466 | ||
| 467 | memset(&pp, 0, sizeof(pp)); | ||
| 441 | rawlist = get_trace_kprobe_event_rawlist(fd); | 468 | rawlist = get_trace_kprobe_event_rawlist(fd); |
| 442 | 469 | ||
| 443 | sl = strlist__new(true, NULL); | 470 | sl = strlist__new(true, NULL); |
| 444 | for (i = 0; i < strlist__nr_entries(rawlist); i++) { | 471 | strlist__for_each(ent, rawlist) { |
| 445 | ent = strlist__entry(rawlist, i); | 472 | parse_trace_kprobe_event(ent->s, &pp); |
| 446 | parse_trace_kprobe_event(ent->s, &group, &event, NULL); | ||
| 447 | if (include_group) { | 473 | if (include_group) { |
| 448 | if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) | 474 | if (e_snprintf(buf, 128, "%s:%s", pp.group, |
| 475 | pp.event) < 0) | ||
| 449 | die("Failed to copy group:event name."); | 476 | die("Failed to copy group:event name."); |
| 450 | strlist__add(sl, buf); | 477 | strlist__add(sl, buf); |
| 451 | } else | 478 | } else |
| 452 | strlist__add(sl, event); | 479 | strlist__add(sl, pp.event); |
| 453 | free(group); | 480 | clear_probe_point(&pp); |
| 454 | free(event); | ||
| 455 | } | 481 | } |
| 456 | 482 | ||
| 457 | strlist__delete(rawlist); | 483 | strlist__delete(rawlist); |
| @@ -470,7 +496,7 @@ static void write_trace_kprobe_event(int fd, const char *buf) | |||
| 470 | } | 496 | } |
| 471 | 497 | ||
| 472 | static void get_new_event_name(char *buf, size_t len, const char *base, | 498 | static void get_new_event_name(char *buf, size_t len, const char *base, |
| 473 | struct strlist *namelist) | 499 | struct strlist *namelist, bool allow_suffix) |
| 474 | { | 500 | { |
| 475 | int i, ret; | 501 | int i, ret; |
| 476 | 502 | ||
| @@ -481,6 +507,12 @@ static void get_new_event_name(char *buf, size_t len, const char *base, | |||
| 481 | if (!strlist__has_entry(namelist, buf)) | 507 | if (!strlist__has_entry(namelist, buf)) |
| 482 | return; | 508 | return; |
| 483 | 509 | ||
| 510 | if (!allow_suffix) { | ||
| 511 | pr_warning("Error: event \"%s\" already exists. " | ||
| 512 | "(Use -f to force duplicates.)\n", base); | ||
| 513 | die("Can't add new event."); | ||
| 514 | } | ||
| 515 | |||
| 484 | /* Try to add suffix */ | 516 | /* Try to add suffix */ |
| 485 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | 517 | for (i = 1; i < MAX_EVENT_INDEX; i++) { |
| 486 | ret = e_snprintf(buf, len, "%s_%d", base, i); | 518 | ret = e_snprintf(buf, len, "%s_%d", base, i); |
| @@ -493,13 +525,15 @@ static void get_new_event_name(char *buf, size_t len, const char *base, | |||
| 493 | die("Too many events are on the same function."); | 525 | die("Too many events are on the same function."); |
| 494 | } | 526 | } |
| 495 | 527 | ||
| 496 | void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) | 528 | void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, |
| 529 | bool force_add) | ||
| 497 | { | 530 | { |
| 498 | int i, j, fd; | 531 | int i, j, fd; |
| 499 | struct probe_point *pp; | 532 | struct probe_point *pp; |
| 500 | char buf[MAX_CMDLEN]; | 533 | char buf[MAX_CMDLEN]; |
| 501 | char event[64]; | 534 | char event[64]; |
| 502 | struct strlist *namelist; | 535 | struct strlist *namelist; |
| 536 | bool allow_suffix; | ||
| 503 | 537 | ||
| 504 | fd = open_kprobe_events(O_RDWR, O_APPEND); | 538 | fd = open_kprobe_events(O_RDWR, O_APPEND); |
| 505 | /* Get current event names */ | 539 | /* Get current event names */ |
| @@ -507,21 +541,35 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) | |||
| 507 | 541 | ||
| 508 | for (j = 0; j < nr_probes; j++) { | 542 | for (j = 0; j < nr_probes; j++) { |
| 509 | pp = probes + j; | 543 | pp = probes + j; |
| 544 | if (!pp->event) | ||
| 545 | pp->event = strdup(pp->function); | ||
| 546 | if (!pp->group) | ||
| 547 | pp->group = strdup(PERFPROBE_GROUP); | ||
| 548 | DIE_IF(!pp->event || !pp->group); | ||
| 549 | /* If force_add is true, suffix search is allowed */ | ||
| 550 | allow_suffix = force_add; | ||
| 510 | for (i = 0; i < pp->found; i++) { | 551 | for (i = 0; i < pp->found; i++) { |
| 511 | /* Get an unused new event name */ | 552 | /* Get an unused new event name */ |
| 512 | get_new_event_name(event, 64, pp->function, namelist); | 553 | get_new_event_name(event, 64, pp->event, namelist, |
| 554 | allow_suffix); | ||
| 513 | snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", | 555 | snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", |
| 514 | pp->retprobe ? 'r' : 'p', | 556 | pp->retprobe ? 'r' : 'p', |
| 515 | PERFPROBE_GROUP, event, | 557 | pp->group, event, |
| 516 | pp->probes[i]); | 558 | pp->probes[i]); |
| 517 | write_trace_kprobe_event(fd, buf); | 559 | write_trace_kprobe_event(fd, buf); |
| 518 | printf("Added new event:\n"); | 560 | printf("Added new event:\n"); |
| 519 | /* Get the first parameter (probe-point) */ | 561 | /* Get the first parameter (probe-point) */ |
| 520 | sscanf(pp->probes[i], "%s", buf); | 562 | sscanf(pp->probes[i], "%s", buf); |
| 521 | show_perf_probe_event(PERFPROBE_GROUP, event, | 563 | show_perf_probe_event(event, buf, pp); |
| 522 | buf, pp); | ||
| 523 | /* Add added event name to namelist */ | 564 | /* Add added event name to namelist */ |
| 524 | strlist__add(namelist, event); | 565 | strlist__add(namelist, event); |
| 566 | /* | ||
| 567 | * Probes after the first probe which comes from same | ||
| 568 | * user input are always allowed to add suffix, because | ||
| 569 | * there might be several addresses corresponding to | ||
| 570 | * one code line. | ||
| 571 | */ | ||
| 572 | allow_suffix = true; | ||
| 525 | } | 573 | } |
| 526 | } | 574 | } |
| 527 | /* Show how to use the event. */ | 575 | /* Show how to use the event. */ |
| @@ -532,29 +580,55 @@ void add_trace_kprobe_events(struct probe_point *probes, int nr_probes) | |||
| 532 | close(fd); | 580 | close(fd); |
| 533 | } | 581 | } |
| 534 | 582 | ||
| 583 | static void __del_trace_kprobe_event(int fd, struct str_node *ent) | ||
| 584 | { | ||
| 585 | char *p; | ||
| 586 | char buf[128]; | ||
| 587 | |||
| 588 | /* Convert from perf-probe event to trace-kprobe event */ | ||
| 589 | if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) | ||
| 590 | die("Failed to copy event."); | ||
| 591 | p = strchr(buf + 2, ':'); | ||
| 592 | if (!p) | ||
| 593 | die("Internal error: %s should have ':' but not.", ent->s); | ||
| 594 | *p = '/'; | ||
| 595 | |||
| 596 | write_trace_kprobe_event(fd, buf); | ||
| 597 | printf("Remove event: %s\n", ent->s); | ||
| 598 | } | ||
| 599 | |||
| 535 | static void del_trace_kprobe_event(int fd, const char *group, | 600 | static void del_trace_kprobe_event(int fd, const char *group, |
| 536 | const char *event, struct strlist *namelist) | 601 | const char *event, struct strlist *namelist) |
| 537 | { | 602 | { |
| 538 | char buf[128]; | 603 | char buf[128]; |
| 604 | struct str_node *ent, *n; | ||
| 605 | int found = 0; | ||
| 539 | 606 | ||
| 540 | if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) | 607 | if (e_snprintf(buf, 128, "%s:%s", group, event) < 0) |
| 541 | die("Failed to copy event."); | 608 | die("Failed to copy event."); |
| 542 | if (!strlist__has_entry(namelist, buf)) { | ||
| 543 | pr_warning("Warning: event \"%s\" is not found.\n", buf); | ||
| 544 | return; | ||
| 545 | } | ||
| 546 | /* Convert from perf-probe event to trace-kprobe event */ | ||
| 547 | if (e_snprintf(buf, 128, "-:%s/%s", group, event) < 0) | ||
| 548 | die("Failed to copy event."); | ||
| 549 | 609 | ||
| 550 | write_trace_kprobe_event(fd, buf); | 610 | if (strpbrk(buf, "*?")) { /* Glob-exp */ |
| 551 | printf("Remove event: %s:%s\n", group, event); | 611 | strlist__for_each_safe(ent, n, namelist) |
| 612 | if (strglobmatch(ent->s, buf)) { | ||
| 613 | found++; | ||
| 614 | __del_trace_kprobe_event(fd, ent); | ||
| 615 | strlist__remove(namelist, ent); | ||
| 616 | } | ||
| 617 | } else { | ||
| 618 | ent = strlist__find(namelist, buf); | ||
| 619 | if (ent) { | ||
| 620 | found++; | ||
| 621 | __del_trace_kprobe_event(fd, ent); | ||
| 622 | strlist__remove(namelist, ent); | ||
| 623 | } | ||
| 624 | } | ||
| 625 | if (found == 0) | ||
| 626 | pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); | ||
| 552 | } | 627 | } |
| 553 | 628 | ||
| 554 | void del_trace_kprobe_events(struct strlist *dellist) | 629 | void del_trace_kprobe_events(struct strlist *dellist) |
| 555 | { | 630 | { |
| 556 | int fd; | 631 | int fd; |
| 557 | unsigned int i; | ||
| 558 | const char *group, *event; | 632 | const char *group, *event; |
| 559 | char *p, *str; | 633 | char *p, *str; |
| 560 | struct str_node *ent; | 634 | struct str_node *ent; |
| @@ -564,20 +638,21 @@ void del_trace_kprobe_events(struct strlist *dellist) | |||
| 564 | /* Get current event names */ | 638 | /* Get current event names */ |
| 565 | namelist = get_perf_event_names(fd, true); | 639 | namelist = get_perf_event_names(fd, true); |
| 566 | 640 | ||
| 567 | for (i = 0; i < strlist__nr_entries(dellist); i++) { | 641 | strlist__for_each(ent, dellist) { |
| 568 | ent = strlist__entry(dellist, i); | ||
| 569 | str = strdup(ent->s); | 642 | str = strdup(ent->s); |
| 570 | if (!str) | 643 | if (!str) |
| 571 | die("Failed to copy event."); | 644 | die("Failed to copy event."); |
| 645 | pr_debug("Parsing: %s\n", str); | ||
| 572 | p = strchr(str, ':'); | 646 | p = strchr(str, ':'); |
| 573 | if (p) { | 647 | if (p) { |
| 574 | group = str; | 648 | group = str; |
| 575 | *p = '\0'; | 649 | *p = '\0'; |
| 576 | event = p + 1; | 650 | event = p + 1; |
| 577 | } else { | 651 | } else { |
| 578 | group = PERFPROBE_GROUP; | 652 | group = "*"; |
| 579 | event = str; | 653 | event = str; |
| 580 | } | 654 | } |
| 655 | pr_debug("Group: %s, Event: %s\n", group, event); | ||
| 581 | del_trace_kprobe_event(fd, group, event, namelist); | 656 | del_trace_kprobe_event(fd, group, event, namelist); |
| 582 | free(str); | 657 | free(str); |
| 583 | } | 658 | } |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index f752159124ae..7f1d499118c0 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
| @@ -1,15 +1,18 @@ | |||
| 1 | #ifndef _PROBE_EVENT_H | 1 | #ifndef _PROBE_EVENT_H |
| 2 | #define _PROBE_EVENT_H | 2 | #define _PROBE_EVENT_H |
| 3 | 3 | ||
| 4 | #include <stdbool.h> | ||
| 4 | #include "probe-finder.h" | 5 | #include "probe-finder.h" |
| 5 | #include "strlist.h" | 6 | #include "strlist.h" |
| 6 | 7 | ||
| 7 | extern int parse_perf_probe_event(const char *str, struct probe_point *pp); | 8 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, |
| 9 | bool *need_dwarf); | ||
| 10 | extern int synthesize_perf_probe_point(struct probe_point *pp); | ||
| 8 | extern int synthesize_perf_probe_event(struct probe_point *pp); | 11 | extern int synthesize_perf_probe_event(struct probe_point *pp); |
| 9 | extern void parse_trace_kprobe_event(const char *str, char **group, | 12 | extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); |
| 10 | char **event, struct probe_point *pp); | ||
| 11 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); | 13 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); |
| 12 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes); | 14 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, |
| 15 | bool force_add); | ||
| 13 | extern void del_trace_kprobe_events(struct strlist *dellist); | 16 | extern void del_trace_kprobe_events(struct strlist *dellist); |
| 14 | extern void show_perf_probe_events(void); | 17 | extern void show_perf_probe_events(void); |
| 15 | 18 | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 4585f1d86792..4b852c0d16a5 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -687,10 +687,8 @@ int find_probepoint(int fd, struct probe_point *pp) | |||
| 687 | struct probe_finder pf = {.pp = pp}; | 687 | struct probe_finder pf = {.pp = pp}; |
| 688 | 688 | ||
| 689 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | 689 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); |
| 690 | if (ret != DW_DLV_OK) { | 690 | if (ret != DW_DLV_OK) |
| 691 | pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO.\n"); | ||
| 692 | return -ENOENT; | 691 | return -ENOENT; |
| 693 | } | ||
| 694 | 692 | ||
| 695 | pp->found = 0; | 693 | pp->found = 0; |
| 696 | while (++cu_number) { | 694 | while (++cu_number) { |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index bdebca6697d2..5e4050ce2963 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
| @@ -12,6 +12,9 @@ static inline int is_c_varname(const char *name) | |||
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | struct probe_point { | 14 | struct probe_point { |
| 15 | char *event; /* Event name */ | ||
| 16 | char *group; /* Event group */ | ||
| 17 | |||
| 15 | /* Inputs */ | 18 | /* Inputs */ |
| 16 | char *file; /* File name */ | 19 | char *file; /* File name */ |
| 17 | int line; /* Line number */ | 20 | int line; /* Line number */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 707ce1cb1621..ce3a6c8abe76 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
| 5 | 5 | ||
| 6 | #include "session.h" | 6 | #include "session.h" |
| 7 | #include "sort.h" | ||
| 7 | #include "util.h" | 8 | #include "util.h" |
| 8 | 9 | ||
| 9 | static int perf_session__open(struct perf_session *self, bool force) | 10 | static int perf_session__open(struct perf_session *self, bool force) |
| @@ -50,31 +51,100 @@ out_close: | |||
| 50 | 51 | ||
| 51 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) | 52 | struct perf_session *perf_session__new(const char *filename, int mode, bool force) |
| 52 | { | 53 | { |
| 53 | size_t len = strlen(filename) + 1; | 54 | size_t len = filename ? strlen(filename) + 1 : 0; |
| 54 | struct perf_session *self = zalloc(sizeof(*self) + len); | 55 | struct perf_session *self = zalloc(sizeof(*self) + len); |
| 55 | 56 | ||
| 56 | if (self == NULL) | 57 | if (self == NULL) |
| 57 | goto out; | 58 | goto out; |
| 58 | 59 | ||
| 59 | if (perf_header__init(&self->header) < 0) | 60 | if (perf_header__init(&self->header) < 0) |
| 60 | goto out_delete; | 61 | goto out_free; |
| 61 | 62 | ||
| 62 | memcpy(self->filename, filename, len); | 63 | memcpy(self->filename, filename, len); |
| 64 | self->threads = RB_ROOT; | ||
| 65 | self->last_match = NULL; | ||
| 66 | self->mmap_window = 32; | ||
| 67 | self->cwd = NULL; | ||
| 68 | self->cwdlen = 0; | ||
| 69 | map_groups__init(&self->kmaps); | ||
| 70 | |||
| 71 | if (perf_session__create_kernel_maps(self) < 0) | ||
| 72 | goto out_delete; | ||
| 63 | 73 | ||
| 64 | if (mode == O_RDONLY && perf_session__open(self, force) < 0) { | 74 | if (mode == O_RDONLY && perf_session__open(self, force) < 0) |
| 65 | perf_session__delete(self); | 75 | goto out_delete; |
| 66 | self = NULL; | ||
| 67 | } | ||
| 68 | out: | 76 | out: |
| 69 | return self; | 77 | return self; |
| 70 | out_delete: | 78 | out_free: |
| 71 | free(self); | 79 | free(self); |
| 72 | return NULL; | 80 | return NULL; |
| 81 | out_delete: | ||
| 82 | perf_session__delete(self); | ||
| 83 | return NULL; | ||
| 73 | } | 84 | } |
| 74 | 85 | ||
| 75 | void perf_session__delete(struct perf_session *self) | 86 | void perf_session__delete(struct perf_session *self) |
| 76 | { | 87 | { |
| 77 | perf_header__exit(&self->header); | 88 | perf_header__exit(&self->header); |
| 78 | close(self->fd); | 89 | close(self->fd); |
| 90 | free(self->cwd); | ||
| 79 | free(self); | 91 | free(self); |
| 80 | } | 92 | } |
| 93 | |||
| 94 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
| 95 | { | ||
| 96 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
| 97 | return 1; | ||
| 98 | |||
| 99 | return 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | ||
| 103 | struct thread *thread, | ||
| 104 | struct ip_callchain *chain, | ||
| 105 | struct symbol **parent) | ||
| 106 | { | ||
| 107 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
| 108 | struct symbol **syms = NULL; | ||
| 109 | unsigned int i; | ||
| 110 | |||
| 111 | if (symbol_conf.use_callchain) { | ||
| 112 | syms = calloc(chain->nr, sizeof(*syms)); | ||
| 113 | if (!syms) { | ||
| 114 | fprintf(stderr, "Can't allocate memory for symbols\n"); | ||
| 115 | exit(-1); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | for (i = 0; i < chain->nr; i++) { | ||
| 120 | u64 ip = chain->ips[i]; | ||
| 121 | struct addr_location al; | ||
| 122 | |||
| 123 | if (ip >= PERF_CONTEXT_MAX) { | ||
| 124 | switch (ip) { | ||
| 125 | case PERF_CONTEXT_HV: | ||
| 126 | cpumode = PERF_RECORD_MISC_HYPERVISOR; break; | ||
| 127 | case PERF_CONTEXT_KERNEL: | ||
| 128 | cpumode = PERF_RECORD_MISC_KERNEL; break; | ||
| 129 | case PERF_CONTEXT_USER: | ||
| 130 | cpumode = PERF_RECORD_MISC_USER; break; | ||
| 131 | default: | ||
| 132 | break; | ||
| 133 | } | ||
| 134 | continue; | ||
| 135 | } | ||
| 136 | |||
| 137 | thread__find_addr_location(thread, self, cpumode, | ||
| 138 | MAP__FUNCTION, ip, &al, NULL); | ||
| 139 | if (al.sym != NULL) { | ||
| 140 | if (sort__has_parent && !*parent && | ||
| 141 | symbol__match_parent_regex(al.sym)) | ||
| 142 | *parent = al.sym; | ||
| 143 | if (!symbol_conf.use_callchain) | ||
| 144 | break; | ||
| 145 | syms[i] = al.sym; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | return syms; | ||
| 150 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f3699c8c8ed4..32eaa1bada06 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -1,16 +1,61 @@ | |||
| 1 | #ifndef __PERF_SESSION_H | 1 | #ifndef __PERF_SESSION_H |
| 2 | #define __PERF_SESSION_H | 2 | #define __PERF_SESSION_H |
| 3 | 3 | ||
| 4 | #include "event.h" | ||
| 4 | #include "header.h" | 5 | #include "header.h" |
| 6 | #include "thread.h" | ||
| 7 | #include <linux/rbtree.h> | ||
| 8 | #include "../../../include/linux/perf_event.h" | ||
| 9 | |||
| 10 | struct ip_callchain; | ||
| 11 | struct thread; | ||
| 12 | struct symbol; | ||
| 5 | 13 | ||
| 6 | struct perf_session { | 14 | struct perf_session { |
| 7 | struct perf_header header; | 15 | struct perf_header header; |
| 8 | unsigned long size; | 16 | unsigned long size; |
| 17 | unsigned long mmap_window; | ||
| 18 | struct map_groups kmaps; | ||
| 19 | struct rb_root threads; | ||
| 20 | struct thread *last_match; | ||
| 21 | struct events_stats events_stats; | ||
| 22 | unsigned long event_total[PERF_RECORD_MAX]; | ||
| 23 | struct rb_root hists; | ||
| 24 | u64 sample_type; | ||
| 9 | int fd; | 25 | int fd; |
| 26 | int cwdlen; | ||
| 27 | char *cwd; | ||
| 10 | char filename[0]; | 28 | char filename[0]; |
| 11 | }; | 29 | }; |
| 12 | 30 | ||
| 31 | typedef int (*event_op)(event_t *self, struct perf_session *session); | ||
| 32 | |||
| 33 | struct perf_event_ops { | ||
| 34 | event_op process_sample_event; | ||
| 35 | event_op process_mmap_event; | ||
| 36 | event_op process_comm_event; | ||
| 37 | event_op process_fork_event; | ||
| 38 | event_op process_exit_event; | ||
| 39 | event_op process_lost_event; | ||
| 40 | event_op process_read_event; | ||
| 41 | event_op process_throttle_event; | ||
| 42 | event_op process_unthrottle_event; | ||
| 43 | int (*sample_type_check)(struct perf_session *session); | ||
| 44 | unsigned long total_unknown; | ||
| 45 | bool full_paths; | ||
| 46 | }; | ||
| 47 | |||
| 13 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); | 48 | struct perf_session *perf_session__new(const char *filename, int mode, bool force); |
| 14 | void perf_session__delete(struct perf_session *self); | 49 | void perf_session__delete(struct perf_session *self); |
| 15 | 50 | ||
| 51 | int perf_session__process_events(struct perf_session *self, | ||
| 52 | struct perf_event_ops *event_ops); | ||
| 53 | |||
| 54 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | ||
| 55 | struct thread *thread, | ||
| 56 | struct ip_callchain *chain, | ||
| 57 | struct symbol **parent); | ||
| 58 | |||
| 59 | int perf_header__read_build_ids(int input, u64 offset, u64 file_size); | ||
| 60 | |||
| 16 | #endif /* __PERF_SESSION_H */ | 61 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b490354d1b23..cb0f327de9e8 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -288,3 +288,29 @@ int sort_dimension__add(const char *tok) | |||
| 288 | 288 | ||
| 289 | return -ESRCH; | 289 | return -ESRCH; |
| 290 | } | 290 | } |
| 291 | |||
| 292 | void setup_sorting(const char * const usagestr[], const struct option *opts) | ||
| 293 | { | ||
| 294 | char *tmp, *tok, *str = strdup(sort_order); | ||
| 295 | |||
| 296 | for (tok = strtok_r(str, ", ", &tmp); | ||
| 297 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | ||
| 298 | if (sort_dimension__add(tok) < 0) { | ||
| 299 | error("Unknown --sort key: `%s'", tok); | ||
| 300 | usage_with_options(usagestr, opts); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | free(str); | ||
| 305 | } | ||
| 306 | |||
| 307 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | ||
| 308 | const char *list_name, FILE *fp) | ||
| 309 | { | ||
| 310 | if (list && strlist__nr_entries(list) == 1) { | ||
| 311 | if (fp != NULL) | ||
| 312 | fprintf(fp, "# %s: %s\n", list_name, | ||
| 313 | strlist__entry(list, 0)->s); | ||
| 314 | self->elide = true; | ||
| 315 | } | ||
| 316 | } | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 333e664ff45f..753f9ea99fb0 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
| @@ -49,9 +49,13 @@ struct hist_entry { | |||
| 49 | struct symbol *sym; | 49 | struct symbol *sym; |
| 50 | u64 ip; | 50 | u64 ip; |
| 51 | char level; | 51 | char level; |
| 52 | struct symbol *parent; | 52 | struct symbol *parent; |
| 53 | struct callchain_node callchain; | 53 | struct callchain_node callchain; |
| 54 | struct rb_root sorted_chain; | 54 | union { |
| 55 | unsigned long position; | ||
| 56 | struct hist_entry *pair; | ||
| 57 | struct rb_root sorted_chain; | ||
| 58 | }; | ||
| 55 | }; | 59 | }; |
| 56 | 60 | ||
| 57 | enum sort_type { | 61 | enum sort_type { |
| @@ -81,6 +85,8 @@ struct sort_entry { | |||
| 81 | extern struct sort_entry sort_thread; | 85 | extern struct sort_entry sort_thread; |
| 82 | extern struct list_head hist_entry__sort_list; | 86 | extern struct list_head hist_entry__sort_list; |
| 83 | 87 | ||
| 88 | void setup_sorting(const char * const usagestr[], const struct option *opts); | ||
| 89 | |||
| 84 | extern int repsep_fprintf(FILE *fp, const char *fmt, ...); | 90 | extern int repsep_fprintf(FILE *fp, const char *fmt, ...); |
| 85 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | 91 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); |
| 86 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | 92 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); |
| @@ -95,5 +101,7 @@ extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | |||
| 95 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | 101 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); |
| 96 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | 102 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); |
| 97 | extern int sort_dimension__add(const char *); | 103 | extern int sort_dimension__add(const char *); |
| 104 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | ||
| 105 | const char *list_name, FILE *fp); | ||
| 98 | 106 | ||
| 99 | #endif /* __PERF_SORT_H */ | 107 | #endif /* __PERF_SORT_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index f24a8cc933d5..5352d7dccc61 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
| @@ -226,3 +226,28 @@ fail: | |||
| 226 | argv_free(argv); | 226 | argv_free(argv); |
| 227 | return NULL; | 227 | return NULL; |
| 228 | } | 228 | } |
| 229 | |||
| 230 | /* Glob expression pattern matching */ | ||
| 231 | bool strglobmatch(const char *str, const char *pat) | ||
| 232 | { | ||
| 233 | while (*str && *pat && *pat != '*') { | ||
| 234 | if (*pat == '?') { | ||
| 235 | str++; | ||
| 236 | pat++; | ||
| 237 | } else | ||
| 238 | if (*str++ != *pat++) | ||
| 239 | return false; | ||
| 240 | } | ||
| 241 | /* Check wild card */ | ||
| 242 | if (*pat == '*') { | ||
| 243 | while (*pat == '*') | ||
| 244 | pat++; | ||
| 245 | if (!*pat) /* Tail wild card matches all */ | ||
| 246 | return true; | ||
| 247 | while (*str) | ||
| 248 | if (strglobmatch(str++, pat)) | ||
| 249 | return true; | ||
| 250 | } | ||
| 251 | return !*str && !*pat; | ||
| 252 | } | ||
| 253 | |||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index bfecec265a1a..02ede58c54b4 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #ifndef __PERF_STRING_H_ | 1 | #ifndef __PERF_STRING_H_ |
| 2 | #define __PERF_STRING_H_ | 2 | #define __PERF_STRING_H_ |
| 3 | 3 | ||
| 4 | #include <stdbool.h> | ||
| 4 | #include "types.h" | 5 | #include "types.h" |
| 5 | 6 | ||
| 6 | int hex2u64(const char *ptr, u64 *val); | 7 | int hex2u64(const char *ptr, u64 *val); |
| @@ -8,6 +9,7 @@ char *strxfrchar(char *s, char from, char to); | |||
| 8 | s64 perf_atoll(const char *str); | 9 | s64 perf_atoll(const char *str); |
| 9 | char **argv_split(const char *str, int *argcp); | 10 | char **argv_split(const char *str, int *argcp); |
| 10 | void argv_free(char **argv); | 11 | void argv_free(char **argv); |
| 12 | bool strglobmatch(const char *str, const char *pat); | ||
| 11 | 13 | ||
| 12 | #define _STR(x) #x | 14 | #define _STR(x) #x |
| 13 | #define STR(x) _STR(x) | 15 | #define STR(x) _STR(x) |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 7ad38171dc2b..6783a2043555 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
| @@ -102,7 +102,7 @@ void strlist__remove(struct strlist *self, struct str_node *sn) | |||
| 102 | str_node__delete(sn, self->dupstr); | 102 | str_node__delete(sn, self->dupstr); |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | bool strlist__has_entry(struct strlist *self, const char *entry) | 105 | struct str_node *strlist__find(struct strlist *self, const char *entry) |
| 106 | { | 106 | { |
| 107 | struct rb_node **p = &self->entries.rb_node; | 107 | struct rb_node **p = &self->entries.rb_node; |
| 108 | struct rb_node *parent = NULL; | 108 | struct rb_node *parent = NULL; |
| @@ -120,10 +120,10 @@ bool strlist__has_entry(struct strlist *self, const char *entry) | |||
| 120 | else if (rc < 0) | 120 | else if (rc < 0) |
| 121 | p = &(*p)->rb_right; | 121 | p = &(*p)->rb_right; |
| 122 | else | 122 | else |
| 123 | return true; | 123 | return sn; |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | return false; | 126 | return NULL; |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) | 129 | static int strlist__parse_list_entry(struct strlist *self, const char *s) |
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index cb4659306d7b..3ba839007d2c 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
| @@ -23,7 +23,12 @@ int strlist__load(struct strlist *self, const char *filename); | |||
| 23 | int strlist__add(struct strlist *self, const char *str); | 23 | int strlist__add(struct strlist *self, const char *str); |
| 24 | 24 | ||
| 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); |
| 26 | bool strlist__has_entry(struct strlist *self, const char *entry); | 26 | struct str_node *strlist__find(struct strlist *self, const char *entry); |
| 27 | |||
| 28 | static inline bool strlist__has_entry(struct strlist *self, const char *entry) | ||
| 29 | { | ||
| 30 | return strlist__find(self, entry) != NULL; | ||
| 31 | } | ||
| 27 | 32 | ||
| 28 | static inline bool strlist__empty(const struct strlist *self) | 33 | static inline bool strlist__empty(const struct strlist *self) |
| 29 | { | 34 | { |
| @@ -35,5 +40,39 @@ static inline unsigned int strlist__nr_entries(const struct strlist *self) | |||
| 35 | return self->nr_entries; | 40 | return self->nr_entries; |
| 36 | } | 41 | } |
| 37 | 42 | ||
| 43 | /* For strlist iteration */ | ||
| 44 | static inline struct str_node *strlist__first(struct strlist *self) | ||
| 45 | { | ||
| 46 | struct rb_node *rn = rb_first(&self->entries); | ||
| 47 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | ||
| 48 | } | ||
| 49 | static inline struct str_node *strlist__next(struct str_node *sn) | ||
| 50 | { | ||
| 51 | struct rb_node *rn; | ||
| 52 | if (!sn) | ||
| 53 | return NULL; | ||
| 54 | rn = rb_next(&sn->rb_node); | ||
| 55 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | ||
| 56 | } | ||
| 57 | |||
| 58 | /** | ||
| 59 | * strlist_for_each - iterate over a strlist | ||
| 60 | * @pos: the &struct str_node to use as a loop cursor. | ||
| 61 | * @self: the &struct strlist for loop. | ||
| 62 | */ | ||
| 63 | #define strlist__for_each(pos, self) \ | ||
| 64 | for (pos = strlist__first(self); pos; pos = strlist__next(pos)) | ||
| 65 | |||
| 66 | /** | ||
| 67 | * strlist_for_each_safe - iterate over a strlist safe against removal of | ||
| 68 | * str_node | ||
| 69 | * @pos: the &struct str_node to use as a loop cursor. | ||
| 70 | * @n: another &struct str_node to use as temporary storage. | ||
| 71 | * @self: the &struct strlist for loop. | ||
| 72 | */ | ||
| 73 | #define strlist__for_each_safe(pos, n, self) \ | ||
| 74 | for (pos = strlist__first(self), n = strlist__next(pos); pos;\ | ||
| 75 | pos = n, n = strlist__next(n)) | ||
| 76 | |||
| 38 | int strlist__parse_list(struct strlist *self, const char *s); | 77 | int strlist__parse_list(struct strlist *self, const char *s); |
| 39 | #endif /* __PERF_STRLIST_H */ | 78 | #endif /* __PERF_STRLIST_H */ |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index d3d9fed74f1d..ab92763edb03 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | #include "util.h" | 1 | #include "util.h" |
| 2 | #include "../perf.h" | 2 | #include "../perf.h" |
| 3 | #include "session.h" | ||
| 4 | #include "sort.h" | ||
| 3 | #include "string.h" | 5 | #include "string.h" |
| 4 | #include "symbol.h" | 6 | #include "symbol.h" |
| 5 | #include "thread.h" | 7 | #include "thread.h" |
| @@ -31,19 +33,16 @@ enum dso_origin { | |||
| 31 | static void dsos__add(struct list_head *head, struct dso *dso); | 33 | static void dsos__add(struct list_head *head, struct dso *dso); |
| 32 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 34 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
| 33 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
| 34 | struct map_groups *mg, symbol_filter_t filter); | 36 | struct perf_session *session, symbol_filter_t filter); |
| 35 | unsigned int symbol__priv_size; | ||
| 36 | static int vmlinux_path__nr_entries; | 37 | static int vmlinux_path__nr_entries; |
| 37 | static char **vmlinux_path; | 38 | static char **vmlinux_path; |
| 38 | 39 | ||
| 39 | static struct symbol_conf symbol_conf__defaults = { | 40 | struct symbol_conf symbol_conf = { |
| 41 | .exclude_other = true, | ||
| 40 | .use_modules = true, | 42 | .use_modules = true, |
| 41 | .try_vmlinux_path = true, | 43 | .try_vmlinux_path = true, |
| 42 | }; | 44 | }; |
| 43 | 45 | ||
| 44 | static struct map_groups kmaps_mem; | ||
| 45 | struct map_groups *kmaps = &kmaps_mem; | ||
| 46 | |||
| 47 | bool dso__loaded(const struct dso *self, enum map_type type) | 46 | bool dso__loaded(const struct dso *self, enum map_type type) |
| 48 | { | 47 | { |
| 49 | return self->loaded & (1 << type); | 48 | return self->loaded & (1 << type); |
| @@ -132,13 +131,13 @@ static void map_groups__fixup_end(struct map_groups *self) | |||
| 132 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) | 131 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) |
| 133 | { | 132 | { |
| 134 | size_t namelen = strlen(name) + 1; | 133 | size_t namelen = strlen(name) + 1; |
| 135 | struct symbol *self = zalloc(symbol__priv_size + | 134 | struct symbol *self = zalloc(symbol_conf.priv_size + |
| 136 | sizeof(*self) + namelen); | 135 | sizeof(*self) + namelen); |
| 137 | if (self == NULL) | 136 | if (self == NULL) |
| 138 | return NULL; | 137 | return NULL; |
| 139 | 138 | ||
| 140 | if (symbol__priv_size) | 139 | if (symbol_conf.priv_size) |
| 141 | self = ((void *)self) + symbol__priv_size; | 140 | self = ((void *)self) + symbol_conf.priv_size; |
| 142 | 141 | ||
| 143 | self->start = start; | 142 | self->start = start; |
| 144 | self->end = len ? start + len - 1 : start; | 143 | self->end = len ? start + len - 1 : start; |
| @@ -152,7 +151,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name) | |||
| 152 | 151 | ||
| 153 | static void symbol__delete(struct symbol *self) | 152 | static void symbol__delete(struct symbol *self) |
| 154 | { | 153 | { |
| 155 | free(((void *)self) - symbol__priv_size); | 154 | free(((void *)self) - symbol_conf.priv_size); |
| 156 | } | 155 | } |
| 157 | 156 | ||
| 158 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | 157 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) |
| @@ -456,7 +455,7 @@ out_failure: | |||
| 456 | * the original ELF section names vmlinux have. | 455 | * the original ELF section names vmlinux have. |
| 457 | */ | 456 | */ |
| 458 | static int dso__split_kallsyms(struct dso *self, struct map *map, | 457 | static int dso__split_kallsyms(struct dso *self, struct map *map, |
| 459 | struct map_groups *mg, symbol_filter_t filter) | 458 | struct perf_session *session, symbol_filter_t filter) |
| 460 | { | 459 | { |
| 461 | struct map *curr_map = map; | 460 | struct map *curr_map = map; |
| 462 | struct symbol *pos; | 461 | struct symbol *pos; |
| @@ -473,13 +472,13 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, | |||
| 473 | 472 | ||
| 474 | module = strchr(pos->name, '\t'); | 473 | module = strchr(pos->name, '\t'); |
| 475 | if (module) { | 474 | if (module) { |
| 476 | if (!mg->use_modules) | 475 | if (!symbol_conf.use_modules) |
| 477 | goto discard_symbol; | 476 | goto discard_symbol; |
| 478 | 477 | ||
| 479 | *module++ = '\0'; | 478 | *module++ = '\0'; |
| 480 | 479 | ||
| 481 | if (strcmp(self->name, module)) { | 480 | if (strcmp(self->name, module)) { |
| 482 | curr_map = map_groups__find_by_name(mg, map->type, module); | 481 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, module); |
| 483 | if (curr_map == NULL) { | 482 | if (curr_map == NULL) { |
| 484 | pr_debug("/proc/{kallsyms,modules} " | 483 | pr_debug("/proc/{kallsyms,modules} " |
| 485 | "inconsistency!\n"); | 484 | "inconsistency!\n"); |
| @@ -510,7 +509,7 @@ static int dso__split_kallsyms(struct dso *self, struct map *map, | |||
| 510 | } | 509 | } |
| 511 | 510 | ||
| 512 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | 511 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; |
| 513 | map_groups__insert(mg, curr_map); | 512 | map_groups__insert(&session->kmaps, curr_map); |
| 514 | ++kernel_range; | 513 | ++kernel_range; |
| 515 | } | 514 | } |
| 516 | 515 | ||
| @@ -531,7 +530,7 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
| 531 | 530 | ||
| 532 | 531 | ||
| 533 | static int dso__load_kallsyms(struct dso *self, struct map *map, | 532 | static int dso__load_kallsyms(struct dso *self, struct map *map, |
| 534 | struct map_groups *mg, symbol_filter_t filter) | 533 | struct perf_session *session, symbol_filter_t filter) |
| 535 | { | 534 | { |
| 536 | if (dso__load_all_kallsyms(self, map) < 0) | 535 | if (dso__load_all_kallsyms(self, map) < 0) |
| 537 | return -1; | 536 | return -1; |
| @@ -539,14 +538,7 @@ static int dso__load_kallsyms(struct dso *self, struct map *map, | |||
| 539 | symbols__fixup_end(&self->symbols[map->type]); | 538 | symbols__fixup_end(&self->symbols[map->type]); |
| 540 | self->origin = DSO__ORIG_KERNEL; | 539 | self->origin = DSO__ORIG_KERNEL; |
| 541 | 540 | ||
| 542 | return dso__split_kallsyms(self, map, mg, filter); | 541 | return dso__split_kallsyms(self, map, session, filter); |
| 543 | } | ||
| 544 | |||
| 545 | size_t kernel_maps__fprintf(FILE *fp) | ||
| 546 | { | ||
| 547 | size_t printed = fprintf(fp, "Kernel maps:\n"); | ||
| 548 | printed += map_groups__fprintf_maps(kmaps, fp); | ||
| 549 | return printed + fprintf(fp, "END kernel maps\n"); | ||
| 550 | } | 542 | } |
| 551 | 543 | ||
| 552 | static int dso__load_perf_map(struct dso *self, struct map *map, | 544 | static int dso__load_perf_map(struct dso *self, struct map *map, |
| @@ -873,7 +865,7 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type | |||
| 873 | } | 865 | } |
| 874 | 866 | ||
| 875 | static int dso__load_sym(struct dso *self, struct map *map, | 867 | static int dso__load_sym(struct dso *self, struct map *map, |
| 876 | struct map_groups *mg, const char *name, int fd, | 868 | struct perf_session *session, const char *name, int fd, |
| 877 | symbol_filter_t filter, int kernel, int kmodule) | 869 | symbol_filter_t filter, int kernel, int kmodule) |
| 878 | { | 870 | { |
| 879 | struct map *curr_map = map; | 871 | struct map *curr_map = map; |
| @@ -977,7 +969,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 977 | snprintf(dso_name, sizeof(dso_name), | 969 | snprintf(dso_name, sizeof(dso_name), |
| 978 | "%s%s", self->short_name, section_name); | 970 | "%s%s", self->short_name, section_name); |
| 979 | 971 | ||
| 980 | curr_map = map_groups__find_by_name(mg, map->type, dso_name); | 972 | curr_map = map_groups__find_by_name(&session->kmaps, map->type, dso_name); |
| 981 | if (curr_map == NULL) { | 973 | if (curr_map == NULL) { |
| 982 | u64 start = sym.st_value; | 974 | u64 start = sym.st_value; |
| 983 | 975 | ||
| @@ -996,7 +988,7 @@ static int dso__load_sym(struct dso *self, struct map *map, | |||
| 996 | curr_map->map_ip = identity__map_ip; | 988 | curr_map->map_ip = identity__map_ip; |
| 997 | curr_map->unmap_ip = identity__map_ip; | 989 | curr_map->unmap_ip = identity__map_ip; |
| 998 | curr_dso->origin = DSO__ORIG_KERNEL; | 990 | curr_dso->origin = DSO__ORIG_KERNEL; |
| 999 | map_groups__insert(kmaps, curr_map); | 991 | map_groups__insert(&session->kmaps, curr_map); |
| 1000 | dsos__add(&dsos__kernel, curr_dso); | 992 | dsos__add(&dsos__kernel, curr_dso); |
| 1001 | } else | 993 | } else |
| 1002 | curr_dso = curr_map->dso; | 994 | curr_dso = curr_map->dso; |
| @@ -1211,7 +1203,8 @@ char dso__symtab_origin(const struct dso *self) | |||
| 1211 | return origin[self->origin]; | 1203 | return origin[self->origin]; |
| 1212 | } | 1204 | } |
| 1213 | 1205 | ||
| 1214 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | 1206 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, |
| 1207 | symbol_filter_t filter) | ||
| 1215 | { | 1208 | { |
| 1216 | int size = PATH_MAX; | 1209 | int size = PATH_MAX; |
| 1217 | char *name; | 1210 | char *name; |
| @@ -1222,7 +1215,7 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
| 1222 | dso__set_loaded(self, map->type); | 1215 | dso__set_loaded(self, map->type); |
| 1223 | 1216 | ||
| 1224 | if (self->kernel) | 1217 | if (self->kernel) |
| 1225 | return dso__load_kernel_sym(self, map, kmaps, filter); | 1218 | return dso__load_kernel_sym(self, map, session, filter); |
| 1226 | 1219 | ||
| 1227 | name = malloc(size); | 1220 | name = malloc(size); |
| 1228 | if (!name) | 1221 | if (!name) |
| @@ -1323,7 +1316,7 @@ struct map *map_groups__find_by_name(struct map_groups *self, | |||
| 1323 | return NULL; | 1316 | return NULL; |
| 1324 | } | 1317 | } |
| 1325 | 1318 | ||
| 1326 | static int dsos__set_modules_path_dir(char *dirname) | 1319 | static int perf_session__set_modules_path_dir(struct perf_session *self, char *dirname) |
| 1327 | { | 1320 | { |
| 1328 | struct dirent *dent; | 1321 | struct dirent *dent; |
| 1329 | DIR *dir = opendir(dirname); | 1322 | DIR *dir = opendir(dirname); |
| @@ -1343,7 +1336,7 @@ static int dsos__set_modules_path_dir(char *dirname) | |||
| 1343 | 1336 | ||
| 1344 | snprintf(path, sizeof(path), "%s/%s", | 1337 | snprintf(path, sizeof(path), "%s/%s", |
| 1345 | dirname, dent->d_name); | 1338 | dirname, dent->d_name); |
| 1346 | if (dsos__set_modules_path_dir(path) < 0) | 1339 | if (perf_session__set_modules_path_dir(self, path) < 0) |
| 1347 | goto failure; | 1340 | goto failure; |
| 1348 | } else { | 1341 | } else { |
| 1349 | char *dot = strrchr(dent->d_name, '.'), | 1342 | char *dot = strrchr(dent->d_name, '.'), |
| @@ -1357,7 +1350,7 @@ static int dsos__set_modules_path_dir(char *dirname) | |||
| 1357 | (int)(dot - dent->d_name), dent->d_name); | 1350 | (int)(dot - dent->d_name), dent->d_name); |
| 1358 | 1351 | ||
| 1359 | strxfrchar(dso_name, '-', '_'); | 1352 | strxfrchar(dso_name, '-', '_'); |
| 1360 | map = map_groups__find_by_name(kmaps, MAP__FUNCTION, dso_name); | 1353 | map = map_groups__find_by_name(&self->kmaps, MAP__FUNCTION, dso_name); |
| 1361 | if (map == NULL) | 1354 | if (map == NULL) |
| 1362 | continue; | 1355 | continue; |
| 1363 | 1356 | ||
| @@ -1377,7 +1370,7 @@ failure: | |||
| 1377 | return -1; | 1370 | return -1; |
| 1378 | } | 1371 | } |
| 1379 | 1372 | ||
| 1380 | static int dsos__set_modules_path(void) | 1373 | static int perf_session__set_modules_path(struct perf_session *self) |
| 1381 | { | 1374 | { |
| 1382 | struct utsname uts; | 1375 | struct utsname uts; |
| 1383 | char modules_path[PATH_MAX]; | 1376 | char modules_path[PATH_MAX]; |
| @@ -1388,7 +1381,7 @@ static int dsos__set_modules_path(void) | |||
| 1388 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", | 1381 | snprintf(modules_path, sizeof(modules_path), "/lib/modules/%s/kernel", |
| 1389 | uts.release); | 1382 | uts.release); |
| 1390 | 1383 | ||
| 1391 | return dsos__set_modules_path_dir(modules_path); | 1384 | return perf_session__set_modules_path_dir(self, modules_path); |
| 1392 | } | 1385 | } |
| 1393 | 1386 | ||
| 1394 | /* | 1387 | /* |
| @@ -1410,7 +1403,7 @@ static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | |||
| 1410 | return self; | 1403 | return self; |
| 1411 | } | 1404 | } |
| 1412 | 1405 | ||
| 1413 | static int map_groups__create_module_maps(struct map_groups *self) | 1406 | static int perf_session__create_module_maps(struct perf_session *self) |
| 1414 | { | 1407 | { |
| 1415 | char *line = NULL; | 1408 | char *line = NULL; |
| 1416 | size_t n; | 1409 | size_t n; |
| @@ -1467,14 +1460,14 @@ static int map_groups__create_module_maps(struct map_groups *self) | |||
| 1467 | dso->has_build_id = true; | 1460 | dso->has_build_id = true; |
| 1468 | 1461 | ||
| 1469 | dso->origin = DSO__ORIG_KMODULE; | 1462 | dso->origin = DSO__ORIG_KMODULE; |
| 1470 | map_groups__insert(self, map); | 1463 | map_groups__insert(&self->kmaps, map); |
| 1471 | dsos__add(&dsos__kernel, dso); | 1464 | dsos__add(&dsos__kernel, dso); |
| 1472 | } | 1465 | } |
| 1473 | 1466 | ||
| 1474 | free(line); | 1467 | free(line); |
| 1475 | fclose(file); | 1468 | fclose(file); |
| 1476 | 1469 | ||
| 1477 | return dsos__set_modules_path(); | 1470 | return perf_session__set_modules_path(self); |
| 1478 | 1471 | ||
| 1479 | out_delete_line: | 1472 | out_delete_line: |
| 1480 | free(line); | 1473 | free(line); |
| @@ -1483,7 +1476,7 @@ out_failure: | |||
| 1483 | } | 1476 | } |
| 1484 | 1477 | ||
| 1485 | static int dso__load_vmlinux(struct dso *self, struct map *map, | 1478 | static int dso__load_vmlinux(struct dso *self, struct map *map, |
| 1486 | struct map_groups *mg, | 1479 | struct perf_session *session, |
| 1487 | const char *vmlinux, symbol_filter_t filter) | 1480 | const char *vmlinux, symbol_filter_t filter) |
| 1488 | { | 1481 | { |
| 1489 | int err = -1, fd; | 1482 | int err = -1, fd; |
| @@ -1517,14 +1510,14 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
| 1517 | return -1; | 1510 | return -1; |
| 1518 | 1511 | ||
| 1519 | dso__set_loaded(self, map->type); | 1512 | dso__set_loaded(self, map->type); |
| 1520 | err = dso__load_sym(self, map, mg, self->long_name, fd, filter, 1, 0); | 1513 | err = dso__load_sym(self, map, session, self->long_name, fd, filter, 1, 0); |
| 1521 | close(fd); | 1514 | close(fd); |
| 1522 | 1515 | ||
| 1523 | return err; | 1516 | return err; |
| 1524 | } | 1517 | } |
| 1525 | 1518 | ||
| 1526 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 1519 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
| 1527 | struct map_groups *mg, symbol_filter_t filter) | 1520 | struct perf_session *session, symbol_filter_t filter) |
| 1528 | { | 1521 | { |
| 1529 | int err; | 1522 | int err; |
| 1530 | bool is_kallsyms; | 1523 | bool is_kallsyms; |
| @@ -1534,7 +1527,7 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | |||
| 1534 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | 1527 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", |
| 1535 | vmlinux_path__nr_entries); | 1528 | vmlinux_path__nr_entries); |
| 1536 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1529 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
| 1537 | err = dso__load_vmlinux(self, map, mg, | 1530 | err = dso__load_vmlinux(self, map, session, |
| 1538 | vmlinux_path[i], filter); | 1531 | vmlinux_path[i], filter); |
| 1539 | if (err > 0) { | 1532 | if (err > 0) { |
| 1540 | pr_debug("Using %s for symbols\n", | 1533 | pr_debug("Using %s for symbols\n", |
| @@ -1550,12 +1543,12 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map, | |||
| 1550 | if (is_kallsyms) | 1543 | if (is_kallsyms) |
| 1551 | goto do_kallsyms; | 1544 | goto do_kallsyms; |
| 1552 | 1545 | ||
| 1553 | err = dso__load_vmlinux(self, map, mg, self->long_name, filter); | 1546 | err = dso__load_vmlinux(self, map, session, self->long_name, filter); |
| 1554 | if (err <= 0) { | 1547 | if (err <= 0) { |
| 1555 | pr_info("The file %s cannot be used, " | 1548 | pr_info("The file %s cannot be used, " |
| 1556 | "trying to use /proc/kallsyms...", self->long_name); | 1549 | "trying to use /proc/kallsyms...", self->long_name); |
| 1557 | do_kallsyms: | 1550 | do_kallsyms: |
| 1558 | err = dso__load_kallsyms(self, map, mg, filter); | 1551 | err = dso__load_kallsyms(self, map, session, filter); |
| 1559 | if (err > 0 && !is_kallsyms) | 1552 | if (err > 0 && !is_kallsyms) |
| 1560 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); | 1553 | dso__set_long_name(self, strdup("[kernel.kallsyms]")); |
| 1561 | } | 1554 | } |
| @@ -1748,32 +1741,69 @@ out_fail: | |||
| 1748 | return -1; | 1741 | return -1; |
| 1749 | } | 1742 | } |
| 1750 | 1743 | ||
| 1751 | int symbol__init(struct symbol_conf *conf) | 1744 | static int setup_list(struct strlist **list, const char *list_str, |
| 1745 | const char *list_name) | ||
| 1752 | { | 1746 | { |
| 1753 | const struct symbol_conf *pconf = conf ?: &symbol_conf__defaults; | 1747 | if (list_str == NULL) |
| 1748 | return 0; | ||
| 1749 | |||
| 1750 | *list = strlist__new(true, list_str); | ||
| 1751 | if (!*list) { | ||
| 1752 | pr_err("problems parsing %s list\n", list_name); | ||
| 1753 | return -1; | ||
| 1754 | } | ||
| 1755 | return 0; | ||
| 1756 | } | ||
| 1754 | 1757 | ||
| 1758 | int symbol__init(void) | ||
| 1759 | { | ||
| 1755 | elf_version(EV_CURRENT); | 1760 | elf_version(EV_CURRENT); |
| 1756 | symbol__priv_size = pconf->priv_size; | 1761 | if (symbol_conf.sort_by_name) |
| 1757 | if (pconf->sort_by_name) | 1762 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
| 1758 | symbol__priv_size += (sizeof(struct symbol_name_rb_node) - | 1763 | sizeof(struct symbol)); |
| 1759 | sizeof(struct symbol)); | ||
| 1760 | map_groups__init(kmaps); | ||
| 1761 | 1764 | ||
| 1762 | if (pconf->try_vmlinux_path && vmlinux_path__init() < 0) | 1765 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) |
| 1763 | return -1; | 1766 | return -1; |
| 1764 | 1767 | ||
| 1765 | if (map_groups__create_kernel_maps(kmaps, pconf->vmlinux_name) < 0) { | 1768 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { |
| 1766 | vmlinux_path__exit(); | 1769 | pr_err("'.' is the only non valid --field-separator argument\n"); |
| 1767 | return -1; | 1770 | return -1; |
| 1768 | } | 1771 | } |
| 1769 | 1772 | ||
| 1770 | kmaps->use_modules = pconf->use_modules; | 1773 | if (setup_list(&symbol_conf.dso_list, |
| 1771 | if (pconf->use_modules && map_groups__create_module_maps(kmaps) < 0) | 1774 | symbol_conf.dso_list_str, "dso") < 0) |
| 1772 | pr_debug("Failed to load list of modules in use, " | 1775 | return -1; |
| 1773 | "continuing...\n"); | 1776 | |
| 1777 | if (setup_list(&symbol_conf.comm_list, | ||
| 1778 | symbol_conf.comm_list_str, "comm") < 0) | ||
| 1779 | goto out_free_dso_list; | ||
| 1780 | |||
| 1781 | if (setup_list(&symbol_conf.sym_list, | ||
| 1782 | symbol_conf.sym_list_str, "symbol") < 0) | ||
| 1783 | goto out_free_comm_list; | ||
| 1784 | |||
| 1785 | return 0; | ||
| 1786 | |||
| 1787 | out_free_dso_list: | ||
| 1788 | strlist__delete(symbol_conf.dso_list); | ||
| 1789 | out_free_comm_list: | ||
| 1790 | strlist__delete(symbol_conf.comm_list); | ||
| 1791 | return -1; | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | int perf_session__create_kernel_maps(struct perf_session *self) | ||
| 1795 | { | ||
| 1796 | if (map_groups__create_kernel_maps(&self->kmaps, | ||
| 1797 | symbol_conf.vmlinux_name) < 0) | ||
| 1798 | return -1; | ||
| 1799 | |||
| 1800 | if (symbol_conf.use_modules && | ||
| 1801 | perf_session__create_module_maps(self) < 0) | ||
| 1802 | pr_debug("Failed to load list of modules for session %s, " | ||
| 1803 | "continuing...\n", self->filename); | ||
| 1774 | /* | 1804 | /* |
| 1775 | * Now that we have all the maps created, just set the ->end of them: | 1805 | * Now that we have all the maps created, just set the ->end of them: |
| 1776 | */ | 1806 | */ |
| 1777 | map_groups__fixup_end(kmaps); | 1807 | map_groups__fixup_end(&self->kmaps); |
| 1778 | return 0; | 1808 | return 0; |
| 1779 | } | 1809 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index cf99f88adf39..8aded2356f79 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -49,19 +49,32 @@ struct symbol { | |||
| 49 | char name[0]; | 49 | char name[0]; |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | struct strlist; | ||
| 53 | |||
| 52 | struct symbol_conf { | 54 | struct symbol_conf { |
| 53 | unsigned short priv_size; | 55 | unsigned short priv_size; |
| 54 | bool try_vmlinux_path, | 56 | bool try_vmlinux_path, |
| 55 | use_modules, | 57 | use_modules, |
| 56 | sort_by_name; | 58 | sort_by_name, |
| 57 | const char *vmlinux_name; | 59 | show_nr_samples, |
| 60 | use_callchain, | ||
| 61 | exclude_other; | ||
| 62 | const char *vmlinux_name, | ||
| 63 | *field_sep; | ||
| 64 | char *dso_list_str, | ||
| 65 | *comm_list_str, | ||
| 66 | *sym_list_str, | ||
| 67 | *col_width_list_str; | ||
| 68 | struct strlist *dso_list, | ||
| 69 | *comm_list, | ||
| 70 | *sym_list; | ||
| 58 | }; | 71 | }; |
| 59 | 72 | ||
| 60 | extern unsigned int symbol__priv_size; | 73 | extern struct symbol_conf symbol_conf; |
| 61 | 74 | ||
| 62 | static inline void *symbol__priv(struct symbol *self) | 75 | static inline void *symbol__priv(struct symbol *self) |
| 63 | { | 76 | { |
| 64 | return ((void *)self) - symbol__priv_size; | 77 | return ((void *)self) - symbol_conf.priv_size; |
| 65 | } | 78 | } |
| 66 | 79 | ||
| 67 | struct addr_location { | 80 | struct addr_location { |
| @@ -70,6 +83,7 @@ struct addr_location { | |||
| 70 | struct symbol *sym; | 83 | struct symbol *sym; |
| 71 | u64 addr; | 84 | u64 addr; |
| 72 | char level; | 85 | char level; |
| 86 | bool filtered; | ||
| 73 | }; | 87 | }; |
| 74 | 88 | ||
| 75 | struct dso { | 89 | struct dso { |
| @@ -98,8 +112,11 @@ bool dso__sorted_by_name(const struct dso *self, enum map_type type); | |||
| 98 | 112 | ||
| 99 | void dso__sort_by_name(struct dso *self, enum map_type type); | 113 | void dso__sort_by_name(struct dso *self, enum map_type type); |
| 100 | 114 | ||
| 115 | struct perf_session; | ||
| 116 | |||
| 101 | struct dso *dsos__findnew(const char *name); | 117 | struct dso *dsos__findnew(const char *name); |
| 102 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter); | 118 | int dso__load(struct dso *self, struct map *map, struct perf_session *session, |
| 119 | symbol_filter_t filter); | ||
| 103 | void dsos__fprintf(FILE *fp); | 120 | void dsos__fprintf(FILE *fp); |
| 104 | size_t dsos__fprintf_buildid(FILE *fp); | 121 | size_t dsos__fprintf_buildid(FILE *fp); |
| 105 | 122 | ||
| @@ -116,12 +133,9 @@ int sysfs__read_build_id(const char *filename, void *bf, size_t size); | |||
| 116 | bool dsos__read_build_ids(void); | 133 | bool dsos__read_build_ids(void); |
| 117 | int build_id__sprintf(u8 *self, int len, char *bf); | 134 | int build_id__sprintf(u8 *self, int len, char *bf); |
| 118 | 135 | ||
| 119 | size_t kernel_maps__fprintf(FILE *fp); | 136 | int symbol__init(void); |
| 120 | 137 | int perf_session__create_kernel_maps(struct perf_session *self); | |
| 121 | int symbol__init(struct symbol_conf *conf); | ||
| 122 | 138 | ||
| 123 | struct map_groups; | ||
| 124 | struct map_groups *kmaps; | ||
| 125 | extern struct list_head dsos__user, dsos__kernel; | 139 | extern struct list_head dsos__user, dsos__kernel; |
| 126 | extern struct dso *vdso; | 140 | extern struct dso *vdso; |
| 127 | #endif /* __PERF_SYMBOL */ | 141 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index b68a00ea4121..4a08dcf50b68 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -2,13 +2,11 @@ | |||
| 2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
| 3 | #include <stdio.h> | 3 | #include <stdio.h> |
| 4 | #include <string.h> | 4 | #include <string.h> |
| 5 | #include "session.h" | ||
| 5 | #include "thread.h" | 6 | #include "thread.h" |
| 6 | #include "util.h" | 7 | #include "util.h" |
| 7 | #include "debug.h" | 8 | #include "debug.h" |
| 8 | 9 | ||
| 9 | static struct rb_root threads; | ||
| 10 | static struct thread *last_match; | ||
| 11 | |||
| 12 | void map_groups__init(struct map_groups *self) | 10 | void map_groups__init(struct map_groups *self) |
| 13 | { | 11 | { |
| 14 | int i; | 12 | int i; |
| @@ -122,9 +120,9 @@ static size_t thread__fprintf(struct thread *self, FILE *fp) | |||
| 122 | map_groups__fprintf(&self->mg, fp); | 120 | map_groups__fprintf(&self->mg, fp); |
| 123 | } | 121 | } |
| 124 | 122 | ||
| 125 | struct thread *threads__findnew(pid_t pid) | 123 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) |
| 126 | { | 124 | { |
| 127 | struct rb_node **p = &threads.rb_node; | 125 | struct rb_node **p = &self->threads.rb_node; |
| 128 | struct rb_node *parent = NULL; | 126 | struct rb_node *parent = NULL; |
| 129 | struct thread *th; | 127 | struct thread *th; |
| 130 | 128 | ||
| @@ -133,15 +131,15 @@ struct thread *threads__findnew(pid_t pid) | |||
| 133 | * so most of the time we dont have to look up | 131 | * so most of the time we dont have to look up |
| 134 | * the full rbtree: | 132 | * the full rbtree: |
| 135 | */ | 133 | */ |
| 136 | if (last_match && last_match->pid == pid) | 134 | if (self->last_match && self->last_match->pid == pid) |
| 137 | return last_match; | 135 | return self->last_match; |
| 138 | 136 | ||
| 139 | while (*p != NULL) { | 137 | while (*p != NULL) { |
| 140 | parent = *p; | 138 | parent = *p; |
| 141 | th = rb_entry(parent, struct thread, rb_node); | 139 | th = rb_entry(parent, struct thread, rb_node); |
| 142 | 140 | ||
| 143 | if (th->pid == pid) { | 141 | if (th->pid == pid) { |
| 144 | last_match = th; | 142 | self->last_match = th; |
| 145 | return th; | 143 | return th; |
| 146 | } | 144 | } |
| 147 | 145 | ||
| @@ -154,25 +152,13 @@ struct thread *threads__findnew(pid_t pid) | |||
| 154 | th = thread__new(pid); | 152 | th = thread__new(pid); |
| 155 | if (th != NULL) { | 153 | if (th != NULL) { |
| 156 | rb_link_node(&th->rb_node, parent, p); | 154 | rb_link_node(&th->rb_node, parent, p); |
| 157 | rb_insert_color(&th->rb_node, &threads); | 155 | rb_insert_color(&th->rb_node, &self->threads); |
| 158 | last_match = th; | 156 | self->last_match = th; |
| 159 | } | 157 | } |
| 160 | 158 | ||
| 161 | return th; | 159 | return th; |
| 162 | } | 160 | } |
| 163 | 161 | ||
| 164 | struct thread *register_idle_thread(void) | ||
| 165 | { | ||
| 166 | struct thread *thread = threads__findnew(0); | ||
| 167 | |||
| 168 | if (!thread || thread__set_comm(thread, "swapper")) { | ||
| 169 | fprintf(stderr, "problem inserting idle task.\n"); | ||
| 170 | exit(-1); | ||
| 171 | } | ||
| 172 | |||
| 173 | return thread; | ||
| 174 | } | ||
| 175 | |||
| 176 | static void map_groups__remove_overlappings(struct map_groups *self, | 162 | static void map_groups__remove_overlappings(struct map_groups *self, |
| 177 | struct map *map) | 163 | struct map *map) |
| 178 | { | 164 | { |
| @@ -281,12 +267,12 @@ int thread__fork(struct thread *self, struct thread *parent) | |||
| 281 | return 0; | 267 | return 0; |
| 282 | } | 268 | } |
| 283 | 269 | ||
| 284 | size_t threads__fprintf(FILE *fp) | 270 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp) |
| 285 | { | 271 | { |
| 286 | size_t ret = 0; | 272 | size_t ret = 0; |
| 287 | struct rb_node *nd; | 273 | struct rb_node *nd; |
| 288 | 274 | ||
| 289 | for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { | 275 | for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) { |
| 290 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | 276 | struct thread *pos = rb_entry(nd, struct thread, rb_node); |
| 291 | 277 | ||
| 292 | ret += thread__fprintf(pos, fp); | 278 | ret += thread__fprintf(pos, fp); |
| @@ -296,13 +282,14 @@ size_t threads__fprintf(FILE *fp) | |||
| 296 | } | 282 | } |
| 297 | 283 | ||
| 298 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 284 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
| 285 | struct perf_session *session, | ||
| 299 | enum map_type type, u64 addr, | 286 | enum map_type type, u64 addr, |
| 300 | symbol_filter_t filter) | 287 | symbol_filter_t filter) |
| 301 | { | 288 | { |
| 302 | struct map *map = map_groups__find(self, type, addr); | 289 | struct map *map = map_groups__find(self, type, addr); |
| 303 | 290 | ||
| 304 | if (map != NULL) | 291 | if (map != NULL) |
| 305 | return map__find_symbol(map, map->map_ip(map, addr), filter); | 292 | return map__find_symbol(map, session, map->map_ip(map, addr), filter); |
| 306 | 293 | ||
| 307 | return NULL; | 294 | return NULL; |
| 308 | } | 295 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 1751802a09ba..c206f72c8881 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | struct map_groups { | 8 | struct map_groups { |
| 9 | struct rb_root maps[MAP__NR_TYPES]; | 9 | struct rb_root maps[MAP__NR_TYPES]; |
| 10 | struct list_head removed_maps[MAP__NR_TYPES]; | 10 | struct list_head removed_maps[MAP__NR_TYPES]; |
| 11 | bool use_modules; | ||
| 12 | }; | 11 | }; |
| 13 | 12 | ||
| 14 | struct thread { | 13 | struct thread { |
| @@ -23,12 +22,11 @@ struct thread { | |||
| 23 | void map_groups__init(struct map_groups *self); | 22 | void map_groups__init(struct map_groups *self); |
| 24 | int thread__set_comm(struct thread *self, const char *comm); | 23 | int thread__set_comm(struct thread *self, const char *comm); |
| 25 | int thread__comm_len(struct thread *self); | 24 | int thread__comm_len(struct thread *self); |
| 26 | struct thread *threads__findnew(pid_t pid); | 25 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
| 27 | struct thread *register_idle_thread(void); | ||
| 28 | void thread__insert_map(struct thread *self, struct map *map); | 26 | void thread__insert_map(struct thread *self, struct map *map); |
| 29 | int thread__fork(struct thread *self, struct thread *parent); | 27 | int thread__fork(struct thread *self, struct thread *parent); |
| 30 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); | 28 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); |
| 31 | size_t threads__fprintf(FILE *fp); | 29 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); |
| 32 | 30 | ||
| 33 | void maps__insert(struct rb_root *maps, struct map *map); | 31 | void maps__insert(struct rb_root *maps, struct map *map); |
| 34 | struct map *maps__find(struct rb_root *maps, u64 addr); | 32 | struct map *maps__find(struct rb_root *maps, u64 addr); |
| @@ -50,19 +48,21 @@ static inline struct map *thread__find_map(struct thread *self, | |||
| 50 | return self ? map_groups__find(&self->mg, type, addr) : NULL; | 48 | return self ? map_groups__find(&self->mg, type, addr) : NULL; |
| 51 | } | 49 | } |
| 52 | 50 | ||
| 53 | void thread__find_addr_location(struct thread *self, u8 cpumode, | 51 | void thread__find_addr_location(struct thread *self, |
| 52 | struct perf_session *session, u8 cpumode, | ||
| 54 | enum map_type type, u64 addr, | 53 | enum map_type type, u64 addr, |
| 55 | struct addr_location *al, | 54 | struct addr_location *al, |
| 56 | symbol_filter_t filter); | 55 | symbol_filter_t filter); |
| 57 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 56 | struct symbol *map_groups__find_symbol(struct map_groups *self, |
| 57 | struct perf_session *session, | ||
| 58 | enum map_type type, u64 addr, | 58 | enum map_type type, u64 addr, |
| 59 | symbol_filter_t filter); | 59 | symbol_filter_t filter); |
| 60 | 60 | ||
| 61 | static inline struct symbol * | 61 | static inline struct symbol * |
| 62 | map_groups__find_function(struct map_groups *self, u64 addr, | 62 | map_groups__find_function(struct map_groups *self, struct perf_session *session, |
| 63 | symbol_filter_t filter) | 63 | u64 addr, symbol_filter_t filter) |
| 64 | { | 64 | { |
| 65 | return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); | 65 | return map_groups__find_symbol(self, session, MAP__FUNCTION, addr, filter); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | struct map *map_groups__find_by_name(struct map_groups *self, | 68 | struct map *map_groups__find_by_name(struct map_groups *self, |
diff --git a/tools/perf/util/trace-event-perl.c b/tools/perf/util/trace-event-perl.c index a5ffe60db5d6..6d6d76b8a21e 100644 --- a/tools/perf/util/trace-event-perl.c +++ b/tools/perf/util/trace-event-perl.c | |||
| @@ -267,7 +267,7 @@ int common_lock_depth(struct scripting_context *context) | |||
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | static void perl_process_event(int cpu, void *data, | 269 | static void perl_process_event(int cpu, void *data, |
| 270 | int size __attribute((unused)), | 270 | int size __unused, |
| 271 | unsigned long long nsecs, char *comm) | 271 | unsigned long long nsecs, char *comm) |
| 272 | { | 272 | { |
| 273 | struct format_field *field; | 273 | struct format_field *field; |
| @@ -359,28 +359,46 @@ static void run_start_sub(void) | |||
| 359 | /* | 359 | /* |
| 360 | * Start trace script | 360 | * Start trace script |
| 361 | */ | 361 | */ |
| 362 | static int perl_start_script(const char *script) | 362 | static int perl_start_script(const char *script, int argc, const char **argv) |
| 363 | { | 363 | { |
| 364 | const char *command_line[2] = { "", NULL }; | 364 | const char **command_line; |
| 365 | int i, err = 0; | ||
| 365 | 366 | ||
| 367 | command_line = malloc((argc + 2) * sizeof(const char *)); | ||
| 368 | command_line[0] = ""; | ||
| 366 | command_line[1] = script; | 369 | command_line[1] = script; |
| 370 | for (i = 2; i < argc + 2; i++) | ||
| 371 | command_line[i] = argv[i - 2]; | ||
| 367 | 372 | ||
| 368 | my_perl = perl_alloc(); | 373 | my_perl = perl_alloc(); |
| 369 | perl_construct(my_perl); | 374 | perl_construct(my_perl); |
| 370 | 375 | ||
| 371 | if (perl_parse(my_perl, xs_init, 2, (char **)command_line, | 376 | if (perl_parse(my_perl, xs_init, argc + 2, (char **)command_line, |
| 372 | (char **)NULL)) | 377 | (char **)NULL)) { |
| 373 | return -1; | 378 | err = -1; |
| 379 | goto error; | ||
| 380 | } | ||
| 374 | 381 | ||
| 375 | perl_run(my_perl); | 382 | if (perl_run(my_perl)) { |
| 376 | if (SvTRUE(ERRSV)) | 383 | err = -1; |
| 377 | return -1; | 384 | goto error; |
| 385 | } | ||
| 386 | |||
| 387 | if (SvTRUE(ERRSV)) { | ||
| 388 | err = -1; | ||
| 389 | goto error; | ||
| 390 | } | ||
| 378 | 391 | ||
| 379 | run_start_sub(); | 392 | run_start_sub(); |
| 380 | 393 | ||
| 394 | free(command_line); | ||
| 381 | fprintf(stderr, "perf trace started with Perl script %s\n\n", script); | 395 | fprintf(stderr, "perf trace started with Perl script %s\n\n", script); |
| 382 | |||
| 383 | return 0; | 396 | return 0; |
| 397 | error: | ||
| 398 | perl_free(my_perl); | ||
| 399 | free(command_line); | ||
| 400 | |||
| 401 | return err; | ||
| 384 | } | 402 | } |
| 385 | 403 | ||
| 386 | /* | 404 | /* |
| @@ -579,7 +597,9 @@ static void print_unsupported_msg(void) | |||
| 579 | "\n etc.\n"); | 597 | "\n etc.\n"); |
| 580 | } | 598 | } |
| 581 | 599 | ||
| 582 | static int perl_start_script_unsupported(const char *script __unused) | 600 | static int perl_start_script_unsupported(const char *script __unused, |
| 601 | int argc __unused, | ||
| 602 | const char **argv __unused) | ||
| 583 | { | 603 | { |
| 584 | print_unsupported_msg(); | 604 | print_unsupported_msg(); |
| 585 | 605 | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 81698d5e6503..6ad405620c9b 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
| @@ -270,7 +270,7 @@ enum trace_flag_type { | |||
| 270 | 270 | ||
| 271 | struct scripting_ops { | 271 | struct scripting_ops { |
| 272 | const char *name; | 272 | const char *name; |
| 273 | int (*start_script) (const char *); | 273 | int (*start_script) (const char *script, int argc, const char **argv); |
| 274 | int (*stop_script) (void); | 274 | int (*stop_script) (void); |
| 275 | void (*process_event) (int cpu, void *data, int size, | 275 | void (*process_event) (int cpu, void *data, int size, |
| 276 | unsigned long long nsecs, char *comm); | 276 | unsigned long long nsecs, char *comm); |
