diff options
Diffstat (limited to 'tools/perf')
57 files changed, 3096 insertions, 1026 deletions
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index e1d60d780784..cb43289e447f 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore | |||
@@ -18,3 +18,5 @@ perf-archive | |||
18 | tags | 18 | tags |
19 | TAGS | 19 | TAGS |
20 | cscope* | 20 | cscope* |
21 | config.mak | ||
22 | config.mak.autogen | ||
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index 5d1a9500277f..c1057701a7dc 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt | |||
@@ -12,9 +12,9 @@ SYNOPSIS | |||
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command manages the build-id cache. It can add and remove files to the | 15 | This command manages the build-id cache. It can add and remove files to/from |
16 | cache. In the future it should as well purge older entries, set upper limits | 16 | the cache. In the future it should as well purge older entries, set upper |
17 | for the space used by the cache, etc. | 17 | limits for the space used by the cache, etc. |
18 | 18 | ||
19 | OPTIONS | 19 | OPTIONS |
20 | ------- | 20 | ------- |
@@ -23,7 +23,7 @@ OPTIONS | |||
23 | Add specified file to the cache. | 23 | Add specified file to the cache. |
24 | -r:: | 24 | -r:: |
25 | --remove=:: | 25 | --remove=:: |
26 | Remove specified file to the cache. | 26 | Remove specified file from the cache. |
27 | -v:: | 27 | -v:: |
28 | --verbose:: | 28 | --verbose:: |
29 | Be more verbose. | 29 | Be more verbose. |
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 94a258c96a44..27d52dae5a43 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -31,6 +31,10 @@ OPTIONS | |||
31 | --vmlinux=PATH:: | 31 | --vmlinux=PATH:: |
32 | Specify vmlinux path which has debuginfo (Dwarf binary). | 32 | Specify vmlinux path which has debuginfo (Dwarf binary). |
33 | 33 | ||
34 | -s:: | ||
35 | --source=PATH:: | ||
36 | Specify path to kernel source. | ||
37 | |||
34 | -v:: | 38 | -v:: |
35 | --verbose:: | 39 | --verbose:: |
36 | Be more verbose (show parsed arguments, etc). | 40 | Be more verbose (show parsed arguments, etc). |
@@ -90,8 +94,8 @@ Each probe argument follows below syntax. | |||
90 | 94 | ||
91 | [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] | 95 | [NAME=]LOCALVAR|$retval|%REG|@SYMBOL[:TYPE] |
92 | 96 | ||
93 | 'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) | 97 | 'NAME' specifies the name of this argument (optional). You can use the name of local variable, local data structure member (e.g. var->field, var.field2), local array with fixed index (e.g. array[1], var->array[0], var->pointer[2]), or kprobe-tracer argument format (e.g. $retval, %ax, etc). Note that the name of this argument will be set as the last member name if you specify a local data structure member (e.g. field2 for 'var->field1.field2'.) |
94 | 'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. | 98 | 'TYPE' casts the type of this argument (optional). If omitted, perf probe automatically set the type based on debuginfo. You can specify 'string' type only for the local variable or structure member which is an array of or a pointer to 'char' or 'unsigned char' type. |
95 | 99 | ||
96 | LINE SYNTAX | 100 | LINE SYNTAX |
97 | ----------- | 101 | ----------- |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 34e255fc3e2f..3ee27dccfde9 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -103,6 +103,19 @@ OPTIONS | |||
103 | --raw-samples:: | 103 | --raw-samples:: |
104 | Collect raw sample records from all opened counters (default for tracepoint counters). | 104 | Collect raw sample records from all opened counters (default for tracepoint counters). |
105 | 105 | ||
106 | -C:: | ||
107 | --cpu:: | ||
108 | Collect samples only on the list of cpus provided. Multiple CPUs can be provided as a | ||
109 | comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | ||
110 | In per-thread mode with inheritance mode on (default), samples are captured only when | ||
111 | the thread executes on the designated CPUs. Default is to monitor all CPUs. | ||
112 | |||
113 | -N:: | ||
114 | --no-buildid-cache:: | ||
115 | Do not update the builid cache. This saves some overhead in situations | ||
116 | where the information in the perf.data file (which includes buildids) | ||
117 | is sufficient. | ||
118 | |||
106 | SEE ALSO | 119 | SEE ALSO |
107 | -------- | 120 | -------- |
108 | linkperf:perf-stat[1], linkperf:perf-list[1] | 121 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 909fa766fa1c..4b3a2d46b437 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -46,6 +46,13 @@ OPTIONS | |||
46 | -B:: | 46 | -B:: |
47 | print large numbers with thousands' separators according to locale | 47 | print large numbers with thousands' separators according to locale |
48 | 48 | ||
49 | -C:: | ||
50 | --cpu=:: | ||
51 | Count only on the list of cpus provided. Multiple CPUs can be provided as a | ||
52 | comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | ||
53 | In per-thread mode, this option is ignored. The -a option is still necessary | ||
54 | to activate system-wide monitoring. Default is to count on all CPUs. | ||
55 | |||
49 | EXAMPLES | 56 | EXAMPLES |
50 | -------- | 57 | -------- |
51 | 58 | ||
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 785b9fc32a46..1f9687663f2a 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -25,9 +25,11 @@ OPTIONS | |||
25 | --count=<count>:: | 25 | --count=<count>:: |
26 | Event period to sample. | 26 | Event period to sample. |
27 | 27 | ||
28 | -C <cpu>:: | 28 | -C <cpu-list>:: |
29 | --CPU=<cpu>:: | 29 | --cpu=<cpu>:: |
30 | CPU to profile. | 30 | Monitor only on the list of cpus provided. Multiple CPUs can be provided as a |
31 | comma-sperated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | ||
32 | Default is to monitor all CPUS. | ||
31 | 33 | ||
32 | -d <seconds>:: | 34 | -d <seconds>:: |
33 | --delay=<seconds>:: | 35 | --delay=<seconds>:: |
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST new file mode 100644 index 000000000000..8c7fc0c8f0b8 --- /dev/null +++ b/tools/perf/MANIFEST | |||
@@ -0,0 +1,12 @@ | |||
1 | tools/perf | ||
2 | include/linux/perf_event.h | ||
3 | include/linux/rbtree.h | ||
4 | include/linux/list.h | ||
5 | include/linux/hash.h | ||
6 | include/linux/stringify.h | ||
7 | lib/rbtree.c | ||
8 | include/linux/swab.h | ||
9 | arch/*/include/asm/unistd*.h | ||
10 | include/linux/poison.h | ||
11 | include/linux/magic.h | ||
12 | include/linux/hw_breakpoint.h | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index d75c28a825f5..26f626d45a9e 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -285,14 +285,10 @@ else | |||
285 | QUIET_STDERR = ">/dev/null 2>&1" | 285 | QUIET_STDERR = ">/dev/null 2>&1" |
286 | endif | 286 | endif |
287 | 287 | ||
288 | BITBUCKET = "/dev/null" | 288 | -include feature-tests.mak |
289 | 289 | ||
290 | ifneq ($(shell sh -c "(echo '\#include <stdio.h>'; echo 'int main(void) { return puts(\"hi\"); }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) "$(QUIET_STDERR)" && echo y"), y) | 290 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y) |
291 | BITBUCKET = .perf.dev.null | 291 | CFLAGS := $(CFLAGS) -fstack-protector-all |
292 | endif | ||
293 | |||
294 | ifeq ($(shell sh -c "echo 'int foo(void) {char X[2]; return 3;}' | $(CC) -x c -c -Werror -fstack-protector-all - -o $(BITBUCKET) "$(QUIET_STDERR)" && echo y"), y) | ||
295 | CFLAGS := $(CFLAGS) -fstack-protector-all | ||
296 | endif | 292 | endif |
297 | 293 | ||
298 | 294 | ||
@@ -508,7 +504,8 @@ PERFLIBS = $(LIB_FILE) | |||
508 | -include config.mak | 504 | -include config.mak |
509 | 505 | ||
510 | ifndef NO_DWARF | 506 | ifndef NO_DWARF |
511 | ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo '\#include <version.h>'; echo '\#ifndef _ELFUTILS_PREREQ'; echo '\#error'; echo '\#endif'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) | 507 | FLAGS_DWARF=$(ALL_CFLAGS) -I/usr/include/elfutils -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) |
508 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) | ||
512 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); | 509 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); |
513 | NO_DWARF := 1 | 510 | NO_DWARF := 1 |
514 | endif # Dwarf support | 511 | endif # Dwarf support |
@@ -536,16 +533,18 @@ ifneq ($(OUTPUT),) | |||
536 | BASIC_CFLAGS += -I$(OUTPUT) | 533 | BASIC_CFLAGS += -I$(OUTPUT) |
537 | endif | 534 | endif |
538 | 535 | ||
539 | ifeq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) | 536 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) |
540 | ifneq ($(shell sh -c "(echo '\#include <gnu/libc-version.h>'; echo 'int main(void) { const char * version = gnu_get_libc_version(); return (long)version; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) | 537 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y) |
541 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | 538 | FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) |
539 | ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y) | ||
540 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | ||
541 | else | ||
542 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel); | ||
543 | endif | ||
542 | endif | 544 | endif |
543 | 545 | ||
544 | ifneq ($(shell sh -c "(echo '\#include <libelf.h>'; echo 'int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) | 546 | ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y) |
545 | BASIC_CFLAGS += -DLIBELF_NO_MMAP | 547 | BASIC_CFLAGS += -DLIBELF_NO_MMAP |
546 | endif | ||
547 | else | ||
548 | msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]); | ||
549 | endif | 548 | endif |
550 | 549 | ||
551 | ifndef NO_DWARF | 550 | ifndef NO_DWARF |
@@ -561,64 +560,73 @@ endif # NO_DWARF | |||
561 | ifdef NO_NEWT | 560 | ifdef NO_NEWT |
562 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT | 561 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT |
563 | else | 562 | else |
564 | ifneq ($(shell sh -c "(echo '\#include <newt.h>'; echo 'int main(void) { newtInit(); newtCls(); return newtFinished(); }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -lnewt -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y) | 563 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt |
565 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); | 564 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y) |
566 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT | 565 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); |
567 | else | 566 | BASIC_CFLAGS += -DNO_NEWT_SUPPORT |
568 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | 567 | else |
569 | BASIC_CFLAGS += -I/usr/include/slang | 568 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h |
570 | EXTLIBS += -lnewt -lslang | 569 | BASIC_CFLAGS += -I/usr/include/slang |
571 | LIB_OBJS += $(OUTPUT)util/newt.o | 570 | EXTLIBS += -lnewt -lslang |
572 | endif | 571 | LIB_OBJS += $(OUTPUT)util/newt.o |
573 | endif # NO_NEWT | 572 | endif |
574 | |||
575 | ifndef NO_LIBPERL | ||
576 | PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` | ||
577 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` | ||
578 | endif | 573 | endif |
579 | 574 | ||
580 | ifneq ($(shell sh -c "(echo '\#include <EXTERN.h>'; echo '\#include <perl.h>'; echo 'int main(void) { perl_alloc(); return 0; }') | $(CC) -x c - $(PERL_EMBED_CCOPTS) -o $(BITBUCKET) $(PERL_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) | 575 | ifdef NO_LIBPERL |
581 | BASIC_CFLAGS += -DNO_LIBPERL | 576 | BASIC_CFLAGS += -DNO_LIBPERL |
582 | else | 577 | else |
583 | ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) | 578 | PERL_EMBED_LDOPTS = `perl -MExtUtils::Embed -e ldopts 2>/dev/null` |
584 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o | 579 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` |
585 | LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o | 580 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) |
586 | endif | ||
587 | 581 | ||
588 | ifndef NO_LIBPYTHON | 582 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y) |
589 | PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` | 583 | BASIC_CFLAGS += -DNO_LIBPERL |
590 | PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` | 584 | else |
585 | ALL_LDFLAGS += $(PERL_EMBED_LDOPTS) | ||
586 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-perl.o | ||
587 | LIB_OBJS += $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o | ||
588 | endif | ||
591 | endif | 589 | endif |
592 | 590 | ||
593 | ifneq ($(shell sh -c "(echo '\#include <Python.h>'; echo 'int main(void) { Py_Initialize(); return 0; }') | $(CC) -x c - $(PYTHON_EMBED_CCOPTS) -o $(BITBUCKET) $(PYTHON_EMBED_LDOPTS) > /dev/null 2>&1 && echo y"), y) | 591 | ifdef NO_LIBPYTHON |
594 | BASIC_CFLAGS += -DNO_LIBPYTHON | 592 | BASIC_CFLAGS += -DNO_LIBPYTHON |
595 | else | 593 | else |
596 | ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) | 594 | PYTHON_EMBED_LDOPTS = `python-config --ldflags 2>/dev/null` |
597 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o | 595 | PYTHON_EMBED_CCOPTS = `python-config --cflags 2>/dev/null` |
598 | LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o | 596 | FLAGS_PYTHON_EMBED=$(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) |
597 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) | ||
598 | BASIC_CFLAGS += -DNO_LIBPYTHON | ||
599 | else | ||
600 | ALL_LDFLAGS += $(PYTHON_EMBED_LDOPTS) | ||
601 | LIB_OBJS += $(OUTPUT)util/scripting-engines/trace-event-python.o | ||
602 | LIB_OBJS += $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o | ||
603 | endif | ||
599 | endif | 604 | endif |
600 | 605 | ||
601 | ifdef NO_DEMANGLE | 606 | ifdef NO_DEMANGLE |
602 | BASIC_CFLAGS += -DNO_DEMANGLE | 607 | BASIC_CFLAGS += -DNO_DEMANGLE |
603 | else | 608 | else |
604 | ifdef HAVE_CPLUS_DEMANGLE | 609 | ifdef HAVE_CPLUS_DEMANGLE |
605 | EXTLIBS += -liberty | 610 | EXTLIBS += -liberty |
606 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 611 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
607 | else | 612 | else |
608 | has_bfd := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd "$(QUIET_STDERR)" && echo y") | 613 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd |
609 | 614 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) | |
610 | ifeq ($(has_bfd),y) | 615 | ifeq ($(has_bfd),y) |
611 | EXTLIBS += -lbfd | 616 | EXTLIBS += -lbfd |
612 | else | 617 | else |
613 | has_bfd_iberty := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd -liberty "$(QUIET_STDERR)" && echo y") | 618 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty |
619 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) | ||
614 | ifeq ($(has_bfd_iberty),y) | 620 | ifeq ($(has_bfd_iberty),y) |
615 | EXTLIBS += -lbfd -liberty | 621 | EXTLIBS += -lbfd -liberty |
616 | else | 622 | else |
617 | has_bfd_iberty_z := $(shell sh -c "(echo '\#include <bfd.h>'; echo 'int main(void) { bfd_demangle(0, 0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd -liberty -lz "$(QUIET_STDERR)" && echo y") | 623 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz |
624 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) | ||
618 | ifeq ($(has_bfd_iberty_z),y) | 625 | ifeq ($(has_bfd_iberty_z),y) |
619 | EXTLIBS += -lbfd -liberty -lz | 626 | EXTLIBS += -lbfd -liberty -lz |
620 | else | 627 | else |
621 | has_cplus_demangle := $(shell sh -c "(echo 'extern char *cplus_demangle(const char *, int);'; echo 'int main(void) { cplus_demangle(0, 0); return 0; }') | $(CC) -x c - $(ALL_CFLAGS) -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) -liberty "$(QUIET_STDERR)" && echo y") | 628 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty |
629 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) | ||
622 | ifeq ($(has_cplus_demangle),y) | 630 | ifeq ($(has_cplus_demangle),y) |
623 | EXTLIBS += -liberty | 631 | EXTLIBS += -liberty |
624 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 632 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
@@ -867,7 +875,7 @@ export TAR INSTALL DESTDIR SHELL_PATH | |||
867 | 875 | ||
868 | SHELL = $(SHELL_PATH) | 876 | SHELL = $(SHELL_PATH) |
869 | 877 | ||
870 | all:: .perf.dev.null shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS | 878 | all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) $(OUTPUT)PERF-BUILD-OPTIONS |
871 | ifneq (,$X) | 879 | ifneq (,$X) |
872 | $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) | 880 | $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test '$p' -ef '$p$X' || $(RM) '$p';) |
873 | endif | 881 | endif |
@@ -1197,11 +1205,6 @@ clean: | |||
1197 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS | 1205 | .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS |
1198 | .PHONY: .FORCE-PERF-BUILD-OPTIONS | 1206 | .PHONY: .FORCE-PERF-BUILD-OPTIONS |
1199 | 1207 | ||
1200 | .perf.dev.null: | ||
1201 | touch .perf.dev.null | ||
1202 | |||
1203 | .INTERMEDIATE: .perf.dev.null | ||
1204 | |||
1205 | ### Make sure built-ins do not have dups and listed in perf.c | 1208 | ### Make sure built-ins do not have dups and listed in perf.c |
1206 | # | 1209 | # |
1207 | check-builtins:: | 1210 | check-builtins:: |
diff --git a/tools/perf/arch/sh/Makefile b/tools/perf/arch/sh/Makefile new file mode 100644 index 000000000000..15130b50dfe3 --- /dev/null +++ b/tools/perf/arch/sh/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | ifndef NO_DWARF | ||
2 | PERF_HAVE_DWARF_REGS := 1 | ||
3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | ||
4 | endif | ||
diff --git a/tools/perf/arch/sh/util/dwarf-regs.c b/tools/perf/arch/sh/util/dwarf-regs.c new file mode 100644 index 000000000000..a11edb007a6c --- /dev/null +++ b/tools/perf/arch/sh/util/dwarf-regs.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * Mapping of DWARF debug register numbers into register names. | ||
3 | * | ||
4 | * Copyright (C) 2010 Matt Fleming <matt@console-pimps.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <libio.h> | ||
23 | #include <dwarf-regs.h> | ||
24 | |||
25 | /* | ||
26 | * Generic dwarf analysis helpers | ||
27 | */ | ||
28 | |||
29 | #define SH_MAX_REGS 18 | ||
30 | const char *sh_regs_table[SH_MAX_REGS] = { | ||
31 | "r0", | ||
32 | "r1", | ||
33 | "r2", | ||
34 | "r3", | ||
35 | "r4", | ||
36 | "r5", | ||
37 | "r6", | ||
38 | "r7", | ||
39 | "r8", | ||
40 | "r9", | ||
41 | "r10", | ||
42 | "r11", | ||
43 | "r12", | ||
44 | "r13", | ||
45 | "r14", | ||
46 | "r15", | ||
47 | "pc", | ||
48 | "pr", | ||
49 | }; | ||
50 | |||
51 | /* Return architecture dependent register string (for kprobe-tracer) */ | ||
52 | const char *get_arch_regstr(unsigned int n) | ||
53 | { | ||
54 | return (n <= SH_MAX_REGS) ? sh_regs_table[n] : NULL; | ||
55 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 96db5248e995..fd20670ce986 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -61,11 +61,9 @@ static int hists__add_entry(struct hists *self, struct addr_location *al) | |||
61 | static int process_sample_event(event_t *event, struct perf_session *session) | 61 | static int process_sample_event(event_t *event, struct perf_session *session) |
62 | { | 62 | { |
63 | struct addr_location al; | 63 | struct addr_location al; |
64 | struct sample_data data; | ||
64 | 65 | ||
65 | dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, | 66 | if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { |
66 | event->ip.pid, event->ip.ip); | ||
67 | |||
68 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { | ||
69 | pr_warning("problem processing %d event, skipping it.\n", | 67 | pr_warning("problem processing %d event, skipping it.\n", |
70 | event->header.type); | 68 | event->header.type); |
71 | return -1; | 69 | return -1; |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index f8e3d1852029..29ad20e67919 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -78,8 +78,7 @@ static int __cmd_buildid_cache(void) | |||
78 | struct str_node *pos; | 78 | struct str_node *pos; |
79 | char debugdir[PATH_MAX]; | 79 | char debugdir[PATH_MAX]; |
80 | 80 | ||
81 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | 81 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); |
82 | DEBUG_CACHE_DIR); | ||
83 | 82 | ||
84 | if (add_name_list_str) { | 83 | if (add_name_list_str) { |
85 | list = strlist__new(true, add_name_list_str); | 84 | list = strlist__new(true, add_name_list_str); |
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 99890728409e..44a47e13bd67 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -43,10 +43,8 @@ static int __cmd_buildid_list(void) | |||
43 | if (session == NULL) | 43 | if (session == NULL) |
44 | return -1; | 44 | return -1; |
45 | 45 | ||
46 | if (with_hits) { | 46 | if (with_hits) |
47 | symbol_conf.full_paths = true; | ||
48 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); | 47 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); |
49 | } | ||
50 | 48 | ||
51 | perf_session__fprintf_dsos_buildid(session, stdout, with_hits); | 49 | perf_session__fprintf_dsos_buildid(session, stdout, with_hits); |
52 | 50 | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index a6e2fdc7a04e..fca1d4402910 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -35,10 +35,7 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi | |||
35 | struct addr_location al; | 35 | struct addr_location al; |
36 | struct sample_data data = { .period = 1, }; | 36 | struct sample_data data = { .period = 1, }; |
37 | 37 | ||
38 | dump_printf("(IP, %d): %d: %#Lx\n", event->header.misc, | 38 | if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { |
39 | event->ip.pid, event->ip.ip); | ||
40 | |||
41 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { | ||
42 | pr_warning("problem processing %d event, skipping it.\n", | 39 | pr_warning("problem processing %d event, skipping it.\n", |
43 | event->header.type); | 40 | event->header.type); |
44 | return -1; | 41 | return -1; |
@@ -47,8 +44,6 @@ static int diff__process_sample_event(event_t *event, struct perf_session *sessi | |||
47 | if (al.filtered || al.sym == NULL) | 44 | if (al.filtered || al.sym == NULL) |
48 | return 0; | 45 | return 0; |
49 | 46 | ||
50 | event__parse_sample(event, session->sample_type, &data); | ||
51 | |||
52 | if (hists__add_entry(&session->hists, &al, data.period)) { | 47 | if (hists__add_entry(&session->hists, &al, data.period)) { |
53 | pr_warning("problem incrementing symbol period, skipping event\n"); | 48 | pr_warning("problem incrementing symbol period, skipping event\n"); |
54 | return -1; | 49 | return -1; |
@@ -185,8 +180,6 @@ static const struct option options[] = { | |||
185 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 180 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
186 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | 181 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, |
187 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 182 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
188 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, | ||
189 | "Don't shorten the pathnames taking into account the cwd"), | ||
190 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 183 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
191 | "only consider symbols in these dsos"), | 184 | "only consider symbols in these dsos"), |
192 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 185 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index e4a4da32a568..199d5e19554f 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -182,6 +182,8 @@ static const struct option options[] = { | |||
182 | "Show source code lines.", opt_show_lines), | 182 | "Show source code lines.", opt_show_lines), |
183 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 183 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
184 | "file", "vmlinux pathname"), | 184 | "file", "vmlinux pathname"), |
185 | OPT_STRING('s', "source", &symbol_conf.source_prefix, | ||
186 | "directory", "path to kernel source"), | ||
185 | #endif | 187 | #endif |
186 | OPT__DRY_RUN(&probe_event_dry_run), | 188 | OPT__DRY_RUN(&probe_event_dry_run), |
187 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, | 189 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
@@ -265,4 +267,3 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
265 | } | 267 | } |
266 | return 0; | 268 | return 0; |
267 | } | 269 | } |
268 | |||
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 711745f56bba..ff77b805de71 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -49,7 +49,6 @@ static int group = 0; | |||
49 | static int realtime_prio = 0; | 49 | static int realtime_prio = 0; |
50 | static bool raw_samples = false; | 50 | static bool raw_samples = false; |
51 | static bool system_wide = false; | 51 | static bool system_wide = false; |
52 | static int profile_cpu = -1; | ||
53 | static pid_t target_pid = -1; | 52 | static pid_t target_pid = -1; |
54 | static pid_t target_tid = -1; | 53 | static pid_t target_tid = -1; |
55 | static pid_t *all_tids = NULL; | 54 | static pid_t *all_tids = NULL; |
@@ -61,6 +60,7 @@ static bool call_graph = false; | |||
61 | static bool inherit_stat = false; | 60 | static bool inherit_stat = false; |
62 | static bool no_samples = false; | 61 | static bool no_samples = false; |
63 | static bool sample_address = false; | 62 | static bool sample_address = false; |
63 | static bool no_buildid = false; | ||
64 | 64 | ||
65 | static long samples = 0; | 65 | static long samples = 0; |
66 | static u64 bytes_written = 0; | 66 | static u64 bytes_written = 0; |
@@ -74,6 +74,7 @@ static int file_new = 1; | |||
74 | static off_t post_processing_offset; | 74 | static off_t post_processing_offset; |
75 | 75 | ||
76 | static struct perf_session *session; | 76 | static struct perf_session *session; |
77 | static const char *cpu_list; | ||
77 | 78 | ||
78 | struct mmap_data { | 79 | struct mmap_data { |
79 | int counter; | 80 | int counter; |
@@ -268,12 +269,17 @@ static void create_counter(int counter, int cpu) | |||
268 | if (inherit_stat) | 269 | if (inherit_stat) |
269 | attr->inherit_stat = 1; | 270 | attr->inherit_stat = 1; |
270 | 271 | ||
271 | if (sample_address) | 272 | if (sample_address) { |
272 | attr->sample_type |= PERF_SAMPLE_ADDR; | 273 | attr->sample_type |= PERF_SAMPLE_ADDR; |
274 | attr->mmap_data = track; | ||
275 | } | ||
273 | 276 | ||
274 | if (call_graph) | 277 | if (call_graph) |
275 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 278 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
276 | 279 | ||
280 | if (system_wide) | ||
281 | attr->sample_type |= PERF_SAMPLE_CPU; | ||
282 | |||
277 | if (raw_samples) { | 283 | if (raw_samples) { |
278 | attr->sample_type |= PERF_SAMPLE_TIME; | 284 | attr->sample_type |= PERF_SAMPLE_TIME; |
279 | attr->sample_type |= PERF_SAMPLE_RAW; | 285 | attr->sample_type |= PERF_SAMPLE_RAW; |
@@ -300,7 +306,7 @@ try_again: | |||
300 | die("Permission error - are you root?\n" | 306 | die("Permission error - are you root?\n" |
301 | "\t Consider tweaking" | 307 | "\t Consider tweaking" |
302 | " /proc/sys/kernel/perf_event_paranoid.\n"); | 308 | " /proc/sys/kernel/perf_event_paranoid.\n"); |
303 | else if (err == ENODEV && profile_cpu != -1) { | 309 | else if (err == ENODEV && cpu_list) { |
304 | die("No such device - did you specify" | 310 | die("No such device - did you specify" |
305 | " an out-of-range profile CPU?\n"); | 311 | " an out-of-range profile CPU?\n"); |
306 | } | 312 | } |
@@ -433,14 +439,14 @@ static void atexit_header(void) | |||
433 | 439 | ||
434 | process_buildids(); | 440 | process_buildids(); |
435 | perf_header__write(&session->header, output, true); | 441 | perf_header__write(&session->header, output, true); |
442 | perf_session__delete(session); | ||
443 | symbol__exit(); | ||
436 | } | 444 | } |
437 | } | 445 | } |
438 | 446 | ||
439 | static void event__synthesize_guest_os(struct machine *machine, void *data) | 447 | static void event__synthesize_guest_os(struct machine *machine, void *data) |
440 | { | 448 | { |
441 | int err; | 449 | int err; |
442 | char *guest_kallsyms; | ||
443 | char path[PATH_MAX]; | ||
444 | struct perf_session *psession = data; | 450 | struct perf_session *psession = data; |
445 | 451 | ||
446 | if (machine__is_host(machine)) | 452 | if (machine__is_host(machine)) |
@@ -460,13 +466,6 @@ static void event__synthesize_guest_os(struct machine *machine, void *data) | |||
460 | pr_err("Couldn't record guest kernel [%d]'s reference" | 466 | pr_err("Couldn't record guest kernel [%d]'s reference" |
461 | " relocation symbol.\n", machine->pid); | 467 | " relocation symbol.\n", machine->pid); |
462 | 468 | ||
463 | if (machine__is_default_guest(machine)) | ||
464 | guest_kallsyms = (char *) symbol_conf.default_guest_kallsyms; | ||
465 | else { | ||
466 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | ||
467 | guest_kallsyms = path; | ||
468 | } | ||
469 | |||
470 | /* | 469 | /* |
471 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms | 470 | * We use _stext for guest kernel because guest kernel's /proc/kallsyms |
472 | * have no _text sometimes. | 471 | * have no _text sometimes. |
@@ -561,12 +560,15 @@ static int __cmd_record(int argc, const char **argv) | |||
561 | if (!file_new) { | 560 | if (!file_new) { |
562 | err = perf_header__read(session, output); | 561 | err = perf_header__read(session, output); |
563 | if (err < 0) | 562 | if (err < 0) |
564 | return err; | 563 | goto out_delete_session; |
565 | } | 564 | } |
566 | 565 | ||
567 | if (have_tracepoints(attrs, nr_counters)) | 566 | if (have_tracepoints(attrs, nr_counters)) |
568 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 567 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
569 | 568 | ||
569 | /* | ||
570 | * perf_session__delete(session) will be called at atexit_header() | ||
571 | */ | ||
570 | atexit(atexit_header); | 572 | atexit(atexit_header); |
571 | 573 | ||
572 | if (forks) { | 574 | if (forks) { |
@@ -622,10 +624,15 @@ static int __cmd_record(int argc, const char **argv) | |||
622 | close(child_ready_pipe[0]); | 624 | close(child_ready_pipe[0]); |
623 | } | 625 | } |
624 | 626 | ||
625 | if ((!system_wide && no_inherit) || profile_cpu != -1) { | 627 | nr_cpus = read_cpu_map(cpu_list); |
626 | open_counters(profile_cpu); | 628 | if (nr_cpus < 1) { |
629 | perror("failed to collect number of CPUs\n"); | ||
630 | return -1; | ||
631 | } | ||
632 | |||
633 | if (!system_wide && no_inherit && !cpu_list) { | ||
634 | open_counters(-1); | ||
627 | } else { | 635 | } else { |
628 | nr_cpus = read_cpu_map(); | ||
629 | for (i = 0; i < nr_cpus; i++) | 636 | for (i = 0; i < nr_cpus; i++) |
630 | open_counters(cpumap[i]); | 637 | open_counters(cpumap[i]); |
631 | } | 638 | } |
@@ -704,7 +711,7 @@ static int __cmd_record(int argc, const char **argv) | |||
704 | if (perf_guest) | 711 | if (perf_guest) |
705 | perf_session__process_machines(session, event__synthesize_guest_os); | 712 | perf_session__process_machines(session, event__synthesize_guest_os); |
706 | 713 | ||
707 | if (!system_wide && profile_cpu == -1) | 714 | if (!system_wide) |
708 | event__synthesize_thread(target_tid, process_synthesized_event, | 715 | event__synthesize_thread(target_tid, process_synthesized_event, |
709 | session); | 716 | session); |
710 | else | 717 | else |
@@ -766,6 +773,10 @@ static int __cmd_record(int argc, const char **argv) | |||
766 | bytes_written / 24); | 773 | bytes_written / 24); |
767 | 774 | ||
768 | return 0; | 775 | return 0; |
776 | |||
777 | out_delete_session: | ||
778 | perf_session__delete(session); | ||
779 | return err; | ||
769 | } | 780 | } |
770 | 781 | ||
771 | static const char * const record_usage[] = { | 782 | static const char * const record_usage[] = { |
@@ -794,8 +805,8 @@ static const struct option options[] = { | |||
794 | "system-wide collection from all CPUs"), | 805 | "system-wide collection from all CPUs"), |
795 | OPT_BOOLEAN('A', "append", &append_file, | 806 | OPT_BOOLEAN('A', "append", &append_file, |
796 | "append to the output file to do incremental profiling"), | 807 | "append to the output file to do incremental profiling"), |
797 | OPT_INTEGER('C', "profile_cpu", &profile_cpu, | 808 | OPT_STRING('C', "cpu", &cpu_list, "cpu", |
798 | "CPU to profile on"), | 809 | "list of cpus to monitor"), |
799 | OPT_BOOLEAN('f', "force", &force, | 810 | OPT_BOOLEAN('f', "force", &force, |
800 | "overwrite existing data file (deprecated)"), | 811 | "overwrite existing data file (deprecated)"), |
801 | OPT_U64('c', "count", &user_interval, "event period to sample"), | 812 | OPT_U64('c', "count", &user_interval, "event period to sample"), |
@@ -815,17 +826,19 @@ static const struct option options[] = { | |||
815 | "Sample addresses"), | 826 | "Sample addresses"), |
816 | OPT_BOOLEAN('n', "no-samples", &no_samples, | 827 | OPT_BOOLEAN('n', "no-samples", &no_samples, |
817 | "don't sample"), | 828 | "don't sample"), |
829 | OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid, | ||
830 | "do not update the buildid cache"), | ||
818 | OPT_END() | 831 | OPT_END() |
819 | }; | 832 | }; |
820 | 833 | ||
821 | int cmd_record(int argc, const char **argv, const char *prefix __used) | 834 | int cmd_record(int argc, const char **argv, const char *prefix __used) |
822 | { | 835 | { |
823 | int i,j; | 836 | int i, j, err = -ENOMEM; |
824 | 837 | ||
825 | argc = parse_options(argc, argv, options, record_usage, | 838 | argc = parse_options(argc, argv, options, record_usage, |
826 | PARSE_OPT_STOP_AT_NON_OPTION); | 839 | PARSE_OPT_STOP_AT_NON_OPTION); |
827 | if (!argc && target_pid == -1 && target_tid == -1 && | 840 | if (!argc && target_pid == -1 && target_tid == -1 && |
828 | !system_wide && profile_cpu == -1) | 841 | !system_wide && !cpu_list) |
829 | usage_with_options(record_usage, options); | 842 | usage_with_options(record_usage, options); |
830 | 843 | ||
831 | if (force && append_file) { | 844 | if (force && append_file) { |
@@ -839,6 +852,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
839 | } | 852 | } |
840 | 853 | ||
841 | symbol__init(); | 854 | symbol__init(); |
855 | if (no_buildid) | ||
856 | disable_buildid_cache(); | ||
842 | 857 | ||
843 | if (!nr_counters) { | 858 | if (!nr_counters) { |
844 | nr_counters = 1; | 859 | nr_counters = 1; |
@@ -857,7 +872,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
857 | } else { | 872 | } else { |
858 | all_tids=malloc(sizeof(pid_t)); | 873 | all_tids=malloc(sizeof(pid_t)); |
859 | if (!all_tids) | 874 | if (!all_tids) |
860 | return -ENOMEM; | 875 | goto out_symbol_exit; |
861 | 876 | ||
862 | all_tids[0] = target_tid; | 877 | all_tids[0] = target_tid; |
863 | thread_num = 1; | 878 | thread_num = 1; |
@@ -867,13 +882,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
867 | for (j = 0; j < MAX_COUNTERS; j++) { | 882 | for (j = 0; j < MAX_COUNTERS; j++) { |
868 | fd[i][j] = malloc(sizeof(int)*thread_num); | 883 | fd[i][j] = malloc(sizeof(int)*thread_num); |
869 | if (!fd[i][j]) | 884 | if (!fd[i][j]) |
870 | return -ENOMEM; | 885 | goto out_free_fd; |
871 | } | 886 | } |
872 | } | 887 | } |
873 | event_array = malloc( | 888 | event_array = malloc( |
874 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); | 889 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); |
875 | if (!event_array) | 890 | if (!event_array) |
876 | return -ENOMEM; | 891 | goto out_free_fd; |
877 | 892 | ||
878 | if (user_interval != ULLONG_MAX) | 893 | if (user_interval != ULLONG_MAX) |
879 | default_interval = user_interval; | 894 | default_interval = user_interval; |
@@ -889,8 +904,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
889 | default_interval = freq; | 904 | default_interval = freq; |
890 | } else { | 905 | } else { |
891 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 906 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
892 | exit(EXIT_FAILURE); | 907 | err = -EINVAL; |
908 | goto out_free_event_array; | ||
893 | } | 909 | } |
894 | 910 | ||
895 | return __cmd_record(argc, argv); | 911 | err = __cmd_record(argc, argv); |
912 | |||
913 | out_free_event_array: | ||
914 | free(event_array); | ||
915 | out_free_fd: | ||
916 | for (i = 0; i < MAX_NR_CPUS; i++) { | ||
917 | for (j = 0; j < MAX_COUNTERS; j++) | ||
918 | free(fd[i][j]); | ||
919 | } | ||
920 | free(all_tids); | ||
921 | all_tids = NULL; | ||
922 | out_symbol_exit: | ||
923 | symbol__exit(); | ||
924 | return err; | ||
896 | } | 925 | } |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index fd7407c7205c..2f4b92925b26 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -155,30 +155,7 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
155 | struct addr_location al; | 155 | struct addr_location al; |
156 | struct perf_event_attr *attr; | 156 | struct perf_event_attr *attr; |
157 | 157 | ||
158 | event__parse_sample(event, session->sample_type, &data); | 158 | if (event__preprocess_sample(event, session, &al, &data, NULL) < 0) { |
159 | |||
160 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, | ||
161 | data.pid, data.tid, data.ip, data.period); | ||
162 | |||
163 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { | ||
164 | unsigned int i; | ||
165 | |||
166 | dump_printf("... chain: nr:%Lu\n", data.callchain->nr); | ||
167 | |||
168 | if (!ip_callchain__valid(data.callchain, event)) { | ||
169 | pr_debug("call-chain problem with event, " | ||
170 | "skipping it.\n"); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | if (dump_trace) { | ||
175 | for (i = 0; i < data.callchain->nr; i++) | ||
176 | dump_printf("..... %2d: %016Lx\n", | ||
177 | i, data.callchain->ips[i]); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | if (event__preprocess_sample(event, session, &al, NULL) < 0) { | ||
182 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 159 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
183 | event->header.type); | 160 | event->header.type); |
184 | return -1; | 161 | return -1; |
@@ -464,8 +441,6 @@ static const struct option options[] = { | |||
464 | "pretty printing style key: normal raw"), | 441 | "pretty printing style key: normal raw"), |
465 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 442 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
466 | "sort by key(s): pid, comm, dso, symbol, parent"), | 443 | "sort by key(s): pid, comm, dso, symbol, parent"), |
467 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, | ||
468 | "Don't shorten the pathnames taking into account the cwd"), | ||
469 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 444 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
470 | "Show sample percentage for different cpu modes"), | 445 | "Show sample percentage for different cpu modes"), |
471 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 446 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 9a39ca3c3ac4..a6b4d44f9502 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -69,7 +69,7 @@ static struct perf_event_attr default_attrs[] = { | |||
69 | }; | 69 | }; |
70 | 70 | ||
71 | static bool system_wide = false; | 71 | static bool system_wide = false; |
72 | static unsigned int nr_cpus = 0; | 72 | static int nr_cpus = 0; |
73 | static int run_idx = 0; | 73 | static int run_idx = 0; |
74 | 74 | ||
75 | static int run_count = 1; | 75 | static int run_count = 1; |
@@ -82,6 +82,7 @@ static int thread_num = 0; | |||
82 | static pid_t child_pid = -1; | 82 | static pid_t child_pid = -1; |
83 | static bool null_run = false; | 83 | static bool null_run = false; |
84 | static bool big_num = false; | 84 | static bool big_num = false; |
85 | static const char *cpu_list; | ||
85 | 86 | ||
86 | 87 | ||
87 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; | 88 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; |
@@ -158,7 +159,7 @@ static int create_perf_stat_counter(int counter) | |||
158 | PERF_FORMAT_TOTAL_TIME_RUNNING; | 159 | PERF_FORMAT_TOTAL_TIME_RUNNING; |
159 | 160 | ||
160 | if (system_wide) { | 161 | if (system_wide) { |
161 | unsigned int cpu; | 162 | int cpu; |
162 | 163 | ||
163 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 164 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
164 | fd[cpu][counter][0] = sys_perf_event_open(attr, | 165 | fd[cpu][counter][0] = sys_perf_event_open(attr, |
@@ -208,7 +209,7 @@ static inline int nsec_counter(int counter) | |||
208 | static void read_counter(int counter) | 209 | static void read_counter(int counter) |
209 | { | 210 | { |
210 | u64 count[3], single_count[3]; | 211 | u64 count[3], single_count[3]; |
211 | unsigned int cpu; | 212 | int cpu; |
212 | size_t res, nv; | 213 | size_t res, nv; |
213 | int scaled; | 214 | int scaled; |
214 | int i, thread; | 215 | int i, thread; |
@@ -542,6 +543,8 @@ static const struct option options[] = { | |||
542 | "null run - dont start any counters"), | 543 | "null run - dont start any counters"), |
543 | OPT_BOOLEAN('B', "big-num", &big_num, | 544 | OPT_BOOLEAN('B', "big-num", &big_num, |
544 | "print large numbers with thousands\' separators"), | 545 | "print large numbers with thousands\' separators"), |
546 | OPT_STRING('C', "cpu", &cpu_list, "cpu", | ||
547 | "list of cpus to monitor in system-wide"), | ||
545 | OPT_END() | 548 | OPT_END() |
546 | }; | 549 | }; |
547 | 550 | ||
@@ -566,10 +569,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
566 | } | 569 | } |
567 | 570 | ||
568 | if (system_wide) | 571 | if (system_wide) |
569 | nr_cpus = read_cpu_map(); | 572 | nr_cpus = read_cpu_map(cpu_list); |
570 | else | 573 | else |
571 | nr_cpus = 1; | 574 | nr_cpus = 1; |
572 | 575 | ||
576 | if (nr_cpus < 1) | ||
577 | usage_with_options(stat_usage, options); | ||
578 | |||
573 | if (target_pid != -1) { | 579 | if (target_pid != -1) { |
574 | target_tid = target_pid; | 580 | target_tid = target_pid; |
575 | thread_num = find_all_tid(target_pid, &all_tids); | 581 | thread_num = find_all_tid(target_pid, &all_tids); |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index a66f4272b994..b513e40974f4 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -102,6 +102,7 @@ struct sym_entry *sym_filter_entry_sched = NULL; | |||
102 | static int sym_pcnt_filter = 5; | 102 | static int sym_pcnt_filter = 5; |
103 | static int sym_counter = 0; | 103 | static int sym_counter = 0; |
104 | static int display_weighted = -1; | 104 | static int display_weighted = -1; |
105 | static const char *cpu_list; | ||
105 | 106 | ||
106 | /* | 107 | /* |
107 | * Symbols | 108 | * Symbols |
@@ -982,6 +983,7 @@ static void event__process_sample(const event_t *self, | |||
982 | u64 ip = self->ip.ip; | 983 | u64 ip = self->ip.ip; |
983 | struct sym_entry *syme; | 984 | struct sym_entry *syme; |
984 | struct addr_location al; | 985 | struct addr_location al; |
986 | struct sample_data data; | ||
985 | struct machine *machine; | 987 | struct machine *machine; |
986 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 988 | u8 origin = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
987 | 989 | ||
@@ -1024,7 +1026,8 @@ static void event__process_sample(const event_t *self, | |||
1024 | if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) | 1026 | if (self->header.misc & PERF_RECORD_MISC_EXACT_IP) |
1025 | exact_samples++; | 1027 | exact_samples++; |
1026 | 1028 | ||
1027 | if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 || | 1029 | if (event__preprocess_sample(self, session, &al, &data, |
1030 | symbol_filter) < 0 || | ||
1028 | al.filtered) | 1031 | al.filtered) |
1029 | return; | 1032 | return; |
1030 | 1033 | ||
@@ -1079,26 +1082,6 @@ static void event__process_sample(const event_t *self, | |||
1079 | } | 1082 | } |
1080 | } | 1083 | } |
1081 | 1084 | ||
1082 | static int event__process(event_t *event, struct perf_session *session) | ||
1083 | { | ||
1084 | switch (event->header.type) { | ||
1085 | case PERF_RECORD_COMM: | ||
1086 | event__process_comm(event, session); | ||
1087 | break; | ||
1088 | case PERF_RECORD_MMAP: | ||
1089 | event__process_mmap(event, session); | ||
1090 | break; | ||
1091 | case PERF_RECORD_FORK: | ||
1092 | case PERF_RECORD_EXIT: | ||
1093 | event__process_task(event, session); | ||
1094 | break; | ||
1095 | default: | ||
1096 | break; | ||
1097 | } | ||
1098 | |||
1099 | return 0; | ||
1100 | } | ||
1101 | |||
1102 | struct mmap_data { | 1085 | struct mmap_data { |
1103 | int counter; | 1086 | int counter; |
1104 | void *base; | 1087 | void *base; |
@@ -1351,8 +1334,8 @@ static const struct option options[] = { | |||
1351 | "profile events on existing thread id"), | 1334 | "profile events on existing thread id"), |
1352 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1335 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1353 | "system-wide collection from all CPUs"), | 1336 | "system-wide collection from all CPUs"), |
1354 | OPT_INTEGER('C', "CPU", &profile_cpu, | 1337 | OPT_STRING('C', "cpu", &cpu_list, "cpu", |
1355 | "CPU to profile on"), | 1338 | "list of cpus to monitor"), |
1356 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 1339 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1357 | "file", "vmlinux pathname"), | 1340 | "file", "vmlinux pathname"), |
1358 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, | 1341 | OPT_BOOLEAN('K', "hide_kernel_symbols", &hide_kernel_symbols, |
@@ -1428,10 +1411,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1428 | return -ENOMEM; | 1411 | return -ENOMEM; |
1429 | 1412 | ||
1430 | /* CPU and PID are mutually exclusive */ | 1413 | /* CPU and PID are mutually exclusive */ |
1431 | if (target_tid > 0 && profile_cpu != -1) { | 1414 | if (target_tid > 0 && cpu_list) { |
1432 | printf("WARNING: PID switch overriding CPU\n"); | 1415 | printf("WARNING: PID switch overriding CPU\n"); |
1433 | sleep(1); | 1416 | sleep(1); |
1434 | profile_cpu = -1; | 1417 | cpu_list = NULL; |
1435 | } | 1418 | } |
1436 | 1419 | ||
1437 | if (!nr_counters) | 1420 | if (!nr_counters) |
@@ -1469,10 +1452,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1469 | attrs[counter].sample_period = default_interval; | 1452 | attrs[counter].sample_period = default_interval; |
1470 | } | 1453 | } |
1471 | 1454 | ||
1472 | if (target_tid != -1 || profile_cpu != -1) | 1455 | if (target_tid != -1) |
1473 | nr_cpus = 1; | 1456 | nr_cpus = 1; |
1474 | else | 1457 | else |
1475 | nr_cpus = read_cpu_map(); | 1458 | nr_cpus = read_cpu_map(cpu_list); |
1459 | |||
1460 | if (nr_cpus < 1) | ||
1461 | usage_with_options(top_usage, options); | ||
1476 | 1462 | ||
1477 | get_term_dimensions(&winsize); | 1463 | get_term_dimensions(&winsize); |
1478 | if (print_entries == 0) { | 1464 | if (print_entries == 0) { |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index dddf3f01b5ab..294da725a57d 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -11,8 +11,9 @@ | |||
11 | 11 | ||
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 | static bool debug_ordering; | 14 | static bool debug_mode; |
15 | static u64 last_timestamp; | 15 | static u64 last_timestamp; |
16 | static u64 nr_unordered; | ||
16 | 17 | ||
17 | static int default_start_script(const char *script __unused, | 18 | static int default_start_script(const char *script __unused, |
18 | int argc __unused, | 19 | int argc __unused, |
@@ -91,13 +92,15 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
91 | } | 92 | } |
92 | 93 | ||
93 | if (session->sample_type & PERF_SAMPLE_RAW) { | 94 | if (session->sample_type & PERF_SAMPLE_RAW) { |
94 | if (debug_ordering) { | 95 | if (debug_mode) { |
95 | if (data.time < last_timestamp) { | 96 | if (data.time < last_timestamp) { |
96 | pr_err("Samples misordered, previous: %llu " | 97 | pr_err("Samples misordered, previous: %llu " |
97 | "this: %llu\n", last_timestamp, | 98 | "this: %llu\n", last_timestamp, |
98 | data.time); | 99 | data.time); |
100 | nr_unordered++; | ||
99 | } | 101 | } |
100 | last_timestamp = data.time; | 102 | last_timestamp = data.time; |
103 | return 0; | ||
101 | } | 104 | } |
102 | /* | 105 | /* |
103 | * FIXME: better resolve from pid from the struct trace_entry | 106 | * FIXME: better resolve from pid from the struct trace_entry |
@@ -113,6 +116,15 @@ static int process_sample_event(event_t *event, struct perf_session *session) | |||
113 | return 0; | 116 | return 0; |
114 | } | 117 | } |
115 | 118 | ||
119 | static u64 nr_lost; | ||
120 | |||
121 | static int process_lost_event(event_t *event, struct perf_session *session __used) | ||
122 | { | ||
123 | nr_lost += event->lost.lost; | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
116 | static struct perf_event_ops event_ops = { | 128 | static struct perf_event_ops event_ops = { |
117 | .sample = process_sample_event, | 129 | .sample = process_sample_event, |
118 | .comm = event__process_comm, | 130 | .comm = event__process_comm, |
@@ -120,6 +132,7 @@ static struct perf_event_ops event_ops = { | |||
120 | .event_type = event__process_event_type, | 132 | .event_type = event__process_event_type, |
121 | .tracing_data = event__process_tracing_data, | 133 | .tracing_data = event__process_tracing_data, |
122 | .build_id = event__process_build_id, | 134 | .build_id = event__process_build_id, |
135 | .lost = process_lost_event, | ||
123 | .ordered_samples = true, | 136 | .ordered_samples = true, |
124 | }; | 137 | }; |
125 | 138 | ||
@@ -132,9 +145,18 @@ static void sig_handler(int sig __unused) | |||
132 | 145 | ||
133 | static int __cmd_trace(struct perf_session *session) | 146 | static int __cmd_trace(struct perf_session *session) |
134 | { | 147 | { |
148 | int ret; | ||
149 | |||
135 | signal(SIGINT, sig_handler); | 150 | signal(SIGINT, sig_handler); |
136 | 151 | ||
137 | return perf_session__process_events(session, &event_ops); | 152 | ret = perf_session__process_events(session, &event_ops); |
153 | |||
154 | if (debug_mode) { | ||
155 | pr_err("Misordered timestamps: %llu\n", nr_unordered); | ||
156 | pr_err("Lost events: %llu\n", nr_lost); | ||
157 | } | ||
158 | |||
159 | return ret; | ||
138 | } | 160 | } |
139 | 161 | ||
140 | struct script_spec { | 162 | struct script_spec { |
@@ -544,8 +566,8 @@ static const struct option options[] = { | |||
544 | "generate perf-trace.xx script in specified language"), | 566 | "generate perf-trace.xx script in specified language"), |
545 | OPT_STRING('i', "input", &input_name, "file", | 567 | OPT_STRING('i', "input", &input_name, "file", |
546 | "input file name"), | 568 | "input file name"), |
547 | OPT_BOOLEAN('d', "debug-ordering", &debug_ordering, | 569 | OPT_BOOLEAN('d', "debug-mode", &debug_mode, |
548 | "check that samples time ordering is monotonic"), | 570 | "do various checks like samples ordering and lost events"), |
549 | 571 | ||
550 | OPT_END() | 572 | OPT_END() |
551 | }; | 573 | }; |
diff --git a/tools/perf/feature-tests.mak b/tools/perf/feature-tests.mak new file mode 100644 index 000000000000..ddb68e601f0e --- /dev/null +++ b/tools/perf/feature-tests.mak | |||
@@ -0,0 +1,119 @@ | |||
1 | define SOURCE_HELLO | ||
2 | #include <stdio.h> | ||
3 | int main(void) | ||
4 | { | ||
5 | return puts(\"hi\"); | ||
6 | } | ||
7 | endef | ||
8 | |||
9 | ifndef NO_DWARF | ||
10 | define SOURCE_DWARF | ||
11 | #include <dwarf.h> | ||
12 | #include <libdw.h> | ||
13 | #include <version.h> | ||
14 | #ifndef _ELFUTILS_PREREQ | ||
15 | #error | ||
16 | #endif | ||
17 | |||
18 | int main(void) | ||
19 | { | ||
20 | Dwarf *dbg = dwarf_begin(0, DWARF_C_READ); | ||
21 | return (long)dbg; | ||
22 | } | ||
23 | endef | ||
24 | endif | ||
25 | |||
26 | define SOURCE_LIBELF | ||
27 | #include <libelf.h> | ||
28 | |||
29 | int main(void) | ||
30 | { | ||
31 | Elf *elf = elf_begin(0, ELF_C_READ, 0); | ||
32 | return (long)elf; | ||
33 | } | ||
34 | endef | ||
35 | |||
36 | define SOURCE_GLIBC | ||
37 | #include <gnu/libc-version.h> | ||
38 | |||
39 | int main(void) | ||
40 | { | ||
41 | const char *version = gnu_get_libc_version(); | ||
42 | return (long)version; | ||
43 | } | ||
44 | endef | ||
45 | |||
46 | define SOURCE_ELF_MMAP | ||
47 | #include <libelf.h> | ||
48 | int main(void) | ||
49 | { | ||
50 | Elf *elf = elf_begin(0, ELF_C_READ_MMAP, 0); | ||
51 | return (long)elf; | ||
52 | } | ||
53 | endef | ||
54 | |||
55 | ifndef NO_NEWT | ||
56 | define SOURCE_NEWT | ||
57 | #include <newt.h> | ||
58 | |||
59 | int main(void) | ||
60 | { | ||
61 | newtInit(); | ||
62 | newtCls(); | ||
63 | return newtFinished(); | ||
64 | } | ||
65 | endef | ||
66 | endif | ||
67 | |||
68 | ifndef NO_LIBPERL | ||
69 | define SOURCE_PERL_EMBED | ||
70 | #include <EXTERN.h> | ||
71 | #include <perl.h> | ||
72 | |||
73 | int main(void) | ||
74 | { | ||
75 | perl_alloc(); | ||
76 | return 0; | ||
77 | } | ||
78 | endef | ||
79 | endif | ||
80 | |||
81 | ifndef NO_LIBPYTHON | ||
82 | define SOURCE_PYTHON_EMBED | ||
83 | #include <Python.h> | ||
84 | |||
85 | int main(void) | ||
86 | { | ||
87 | Py_Initialize(); | ||
88 | return 0; | ||
89 | } | ||
90 | endef | ||
91 | endif | ||
92 | |||
93 | define SOURCE_BFD | ||
94 | #include <bfd.h> | ||
95 | |||
96 | int main(void) | ||
97 | { | ||
98 | bfd_demangle(0, 0, 0); | ||
99 | return 0; | ||
100 | } | ||
101 | endef | ||
102 | |||
103 | define SOURCE_CPLUS_DEMANGLE | ||
104 | extern char *cplus_demangle(const char *, int); | ||
105 | |||
106 | int main(void) | ||
107 | { | ||
108 | cplus_demangle(0, 0); | ||
109 | return 0; | ||
110 | } | ||
111 | endef | ||
112 | |||
113 | # try-cc | ||
114 | # Usage: option = $(call try-cc, source-to-build, cc-options) | ||
115 | try-cc = $(shell sh -c \ | ||
116 | 'TMP="$(TMPOUT).$$$$"; \ | ||
117 | echo "$(1)" | \ | ||
118 | $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ | ||
119 | rm -f "$$TMP"') | ||
diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh index 2e7a4f417e20..677e59d62a8d 100644 --- a/tools/perf/perf-archive.sh +++ b/tools/perf/perf-archive.sh | |||
@@ -7,7 +7,17 @@ if [ $# -ne 0 ] ; then | |||
7 | PERF_DATA=$1 | 7 | PERF_DATA=$1 |
8 | fi | 8 | fi |
9 | 9 | ||
10 | DEBUGDIR=~/.debug/ | 10 | # |
11 | # PERF_BUILDID_DIR environment variable set by perf | ||
12 | # path to buildid directory, default to $HOME/.debug | ||
13 | # | ||
14 | if [ -z $PERF_BUILDID_DIR ]; then | ||
15 | PERF_BUILDID_DIR=~/.debug/ | ||
16 | else | ||
17 | # append / to make substitutions work | ||
18 | PERF_BUILDID_DIR=$PERF_BUILDID_DIR/ | ||
19 | fi | ||
20 | |||
11 | BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX) | 21 | BUILDIDS=$(mktemp /tmp/perf-archive-buildids.XXXXXX) |
12 | NOBUILDID=0000000000000000000000000000000000000000 | 22 | NOBUILDID=0000000000000000000000000000000000000000 |
13 | 23 | ||
@@ -22,13 +32,13 @@ MANIFEST=$(mktemp /tmp/perf-archive-manifest.XXXXXX) | |||
22 | 32 | ||
23 | cut -d ' ' -f 1 $BUILDIDS | \ | 33 | cut -d ' ' -f 1 $BUILDIDS | \ |
24 | while read build_id ; do | 34 | while read build_id ; do |
25 | linkname=$DEBUGDIR.build-id/${build_id:0:2}/${build_id:2} | 35 | linkname=$PERF_BUILDID_DIR.build-id/${build_id:0:2}/${build_id:2} |
26 | filename=$(readlink -f $linkname) | 36 | filename=$(readlink -f $linkname) |
27 | echo ${linkname#$DEBUGDIR} >> $MANIFEST | 37 | echo ${linkname#$PERF_BUILDID_DIR} >> $MANIFEST |
28 | echo ${filename#$DEBUGDIR} >> $MANIFEST | 38 | echo ${filename#$PERF_BUILDID_DIR} >> $MANIFEST |
29 | done | 39 | done |
30 | 40 | ||
31 | tar cfj $PERF_DATA.tar.bz2 -C $DEBUGDIR -T $MANIFEST | 41 | tar cfj $PERF_DATA.tar.bz2 -C $PERF_BUILDID_DIR -T $MANIFEST |
32 | rm -f $MANIFEST $BUILDIDS | 42 | rm -f $MANIFEST $BUILDIDS |
33 | echo -e "Now please run:\n" | 43 | echo -e "Now please run:\n" |
34 | echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n" | 44 | echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n" |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 6e4871191138..cdd6c03f1e14 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -458,6 +458,8 @@ int main(int argc, const char **argv) | |||
458 | handle_options(&argv, &argc, NULL); | 458 | handle_options(&argv, &argc, NULL); |
459 | commit_pager_choice(); | 459 | commit_pager_choice(); |
460 | set_debugfs_path(); | 460 | set_debugfs_path(); |
461 | set_buildid_dir(); | ||
462 | |||
461 | if (argc > 0) { | 463 | if (argc > 0) { |
462 | if (!prefixcmp(argv[0], "--")) | 464 | if (!prefixcmp(argv[0], "--")) |
463 | argv[0] += 2; | 465 | argv[0] += 2; |
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py index 1dc464ee2ca8..aad7525bca1d 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py | |||
@@ -89,3 +89,33 @@ def trace_flag_str(value): | |||
89 | value &= ~idx | 89 | value &= ~idx |
90 | 90 | ||
91 | return string | 91 | return string |
92 | |||
93 | |||
94 | def taskState(state): | ||
95 | states = { | ||
96 | 0 : "R", | ||
97 | 1 : "S", | ||
98 | 2 : "D", | ||
99 | 64: "DEAD" | ||
100 | } | ||
101 | |||
102 | if state not in states: | ||
103 | return "Unknown" | ||
104 | |||
105 | return states[state] | ||
106 | |||
107 | |||
108 | class EventHeaders: | ||
109 | def __init__(self, common_cpu, common_secs, common_nsecs, | ||
110 | common_pid, common_comm): | ||
111 | self.cpu = common_cpu | ||
112 | self.secs = common_secs | ||
113 | self.nsecs = common_nsecs | ||
114 | self.pid = common_pid | ||
115 | self.comm = common_comm | ||
116 | |||
117 | def ts(self): | ||
118 | return (self.secs * (10 ** 9)) + self.nsecs | ||
119 | |||
120 | def ts_format(self): | ||
121 | return "%d.%d" % (self.secs, int(self.nsecs / 1000)) | ||
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py new file mode 100644 index 000000000000..ae9a56e43e05 --- /dev/null +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/SchedGui.py | |||
@@ -0,0 +1,184 @@ | |||
1 | # SchedGui.py - Python extension for perf trace, basic GUI code for | ||
2 | # traces drawing and overview. | ||
3 | # | ||
4 | # Copyright (C) 2010 by Frederic Weisbecker <fweisbec@gmail.com> | ||
5 | # | ||
6 | # This software is distributed under the terms of the GNU General | ||
7 | # Public License ("GPL") version 2 as published by the Free Software | ||
8 | # Foundation. | ||
9 | |||
10 | |||
11 | try: | ||
12 | import wx | ||
13 | except ImportError: | ||
14 | raise ImportError, "You need to install the wxpython lib for this script" | ||
15 | |||
16 | |||
17 | class RootFrame(wx.Frame): | ||
18 | Y_OFFSET = 100 | ||
19 | RECT_HEIGHT = 100 | ||
20 | RECT_SPACE = 50 | ||
21 | EVENT_MARKING_WIDTH = 5 | ||
22 | |||
23 | def __init__(self, sched_tracer, title, parent = None, id = -1): | ||
24 | wx.Frame.__init__(self, parent, id, title) | ||
25 | |||
26 | (self.screen_width, self.screen_height) = wx.GetDisplaySize() | ||
27 | self.screen_width -= 10 | ||
28 | self.screen_height -= 10 | ||
29 | self.zoom = 0.5 | ||
30 | self.scroll_scale = 20 | ||
31 | self.sched_tracer = sched_tracer | ||
32 | self.sched_tracer.set_root_win(self) | ||
33 | (self.ts_start, self.ts_end) = sched_tracer.interval() | ||
34 | self.update_width_virtual() | ||
35 | self.nr_rects = sched_tracer.nr_rectangles() + 1 | ||
36 | self.height_virtual = RootFrame.Y_OFFSET + (self.nr_rects * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) | ||
37 | |||
38 | # whole window panel | ||
39 | self.panel = wx.Panel(self, size=(self.screen_width, self.screen_height)) | ||
40 | |||
41 | # scrollable container | ||
42 | self.scroll = wx.ScrolledWindow(self.panel) | ||
43 | self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale) | ||
44 | self.scroll.EnableScrolling(True, True) | ||
45 | self.scroll.SetFocus() | ||
46 | |||
47 | # scrollable drawing area | ||
48 | self.scroll_panel = wx.Panel(self.scroll, size=(self.screen_width - 15, self.screen_height / 2)) | ||
49 | self.scroll_panel.Bind(wx.EVT_PAINT, self.on_paint) | ||
50 | self.scroll_panel.Bind(wx.EVT_KEY_DOWN, self.on_key_press) | ||
51 | self.scroll_panel.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) | ||
52 | self.scroll.Bind(wx.EVT_PAINT, self.on_paint) | ||
53 | self.scroll.Bind(wx.EVT_KEY_DOWN, self.on_key_press) | ||
54 | self.scroll.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down) | ||
55 | |||
56 | self.scroll.Fit() | ||
57 | self.Fit() | ||
58 | |||
59 | self.scroll_panel.SetDimensions(-1, -1, self.width_virtual, self.height_virtual, wx.SIZE_USE_EXISTING) | ||
60 | |||
61 | self.txt = None | ||
62 | |||
63 | self.Show(True) | ||
64 | |||
65 | def us_to_px(self, val): | ||
66 | return val / (10 ** 3) * self.zoom | ||
67 | |||
68 | def px_to_us(self, val): | ||
69 | return (val / self.zoom) * (10 ** 3) | ||
70 | |||
71 | def scroll_start(self): | ||
72 | (x, y) = self.scroll.GetViewStart() | ||
73 | return (x * self.scroll_scale, y * self.scroll_scale) | ||
74 | |||
75 | def scroll_start_us(self): | ||
76 | (x, y) = self.scroll_start() | ||
77 | return self.px_to_us(x) | ||
78 | |||
79 | def paint_rectangle_zone(self, nr, color, top_color, start, end): | ||
80 | offset_px = self.us_to_px(start - self.ts_start) | ||
81 | width_px = self.us_to_px(end - self.ts_start) | ||
82 | |||
83 | offset_py = RootFrame.Y_OFFSET + (nr * (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE)) | ||
84 | width_py = RootFrame.RECT_HEIGHT | ||
85 | |||
86 | dc = self.dc | ||
87 | |||
88 | if top_color is not None: | ||
89 | (r, g, b) = top_color | ||
90 | top_color = wx.Colour(r, g, b) | ||
91 | brush = wx.Brush(top_color, wx.SOLID) | ||
92 | dc.SetBrush(brush) | ||
93 | dc.DrawRectangle(offset_px, offset_py, width_px, RootFrame.EVENT_MARKING_WIDTH) | ||
94 | width_py -= RootFrame.EVENT_MARKING_WIDTH | ||
95 | offset_py += RootFrame.EVENT_MARKING_WIDTH | ||
96 | |||
97 | (r ,g, b) = color | ||
98 | color = wx.Colour(r, g, b) | ||
99 | brush = wx.Brush(color, wx.SOLID) | ||
100 | dc.SetBrush(brush) | ||
101 | dc.DrawRectangle(offset_px, offset_py, width_px, width_py) | ||
102 | |||
103 | def update_rectangles(self, dc, start, end): | ||
104 | start += self.ts_start | ||
105 | end += self.ts_start | ||
106 | self.sched_tracer.fill_zone(start, end) | ||
107 | |||
108 | def on_paint(self, event): | ||
109 | dc = wx.PaintDC(self.scroll_panel) | ||
110 | self.dc = dc | ||
111 | |||
112 | width = min(self.width_virtual, self.screen_width) | ||
113 | (x, y) = self.scroll_start() | ||
114 | start = self.px_to_us(x) | ||
115 | end = self.px_to_us(x + width) | ||
116 | self.update_rectangles(dc, start, end) | ||
117 | |||
118 | def rect_from_ypixel(self, y): | ||
119 | y -= RootFrame.Y_OFFSET | ||
120 | rect = y / (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) | ||
121 | height = y % (RootFrame.RECT_HEIGHT + RootFrame.RECT_SPACE) | ||
122 | |||
123 | if rect < 0 or rect > self.nr_rects - 1 or height > RootFrame.RECT_HEIGHT: | ||
124 | return -1 | ||
125 | |||
126 | return rect | ||
127 | |||
128 | def update_summary(self, txt): | ||
129 | if self.txt: | ||
130 | self.txt.Destroy() | ||
131 | self.txt = wx.StaticText(self.panel, -1, txt, (0, (self.screen_height / 2) + 50)) | ||
132 | |||
133 | |||
134 | def on_mouse_down(self, event): | ||
135 | (x, y) = event.GetPositionTuple() | ||
136 | rect = self.rect_from_ypixel(y) | ||
137 | if rect == -1: | ||
138 | return | ||
139 | |||
140 | t = self.px_to_us(x) + self.ts_start | ||
141 | |||
142 | self.sched_tracer.mouse_down(rect, t) | ||
143 | |||
144 | |||
145 | def update_width_virtual(self): | ||
146 | self.width_virtual = self.us_to_px(self.ts_end - self.ts_start) | ||
147 | |||
148 | def __zoom(self, x): | ||
149 | self.update_width_virtual() | ||
150 | (xpos, ypos) = self.scroll.GetViewStart() | ||
151 | xpos = self.us_to_px(x) / self.scroll_scale | ||
152 | self.scroll.SetScrollbars(self.scroll_scale, self.scroll_scale, self.width_virtual / self.scroll_scale, self.height_virtual / self.scroll_scale, xpos, ypos) | ||
153 | self.Refresh() | ||
154 | |||
155 | def zoom_in(self): | ||
156 | x = self.scroll_start_us() | ||
157 | self.zoom *= 2 | ||
158 | self.__zoom(x) | ||
159 | |||
160 | def zoom_out(self): | ||
161 | x = self.scroll_start_us() | ||
162 | self.zoom /= 2 | ||
163 | self.__zoom(x) | ||
164 | |||
165 | |||
166 | def on_key_press(self, event): | ||
167 | key = event.GetRawKeyCode() | ||
168 | if key == ord("+"): | ||
169 | self.zoom_in() | ||
170 | return | ||
171 | if key == ord("-"): | ||
172 | self.zoom_out() | ||
173 | return | ||
174 | |||
175 | key = event.GetKeyCode() | ||
176 | (x, y) = self.scroll.GetViewStart() | ||
177 | if key == wx.WXK_RIGHT: | ||
178 | self.scroll.Scroll(x + 1, y) | ||
179 | elif key == wx.WXK_LEFT: | ||
180 | self.scroll.Scroll(x - 1, y) | ||
181 | elif key == wx.WXK_DOWN: | ||
182 | self.scroll.Scroll(x, y + 1) | ||
183 | elif key == wx.WXK_UP: | ||
184 | self.scroll.Scroll(x, y - 1) | ||
diff --git a/tools/perf/scripts/python/bin/sched-migration-record b/tools/perf/scripts/python/bin/sched-migration-record new file mode 100644 index 000000000000..17a3e9bd9e8f --- /dev/null +++ b/tools/perf/scripts/python/bin/sched-migration-record | |||
@@ -0,0 +1,2 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -m 16384 -a -e sched:sched_wakeup -e sched:sched_wakeup_new -e sched:sched_switch -e sched:sched_migrate_task $@ | ||
diff --git a/tools/perf/scripts/python/bin/sched-migration-report b/tools/perf/scripts/python/bin/sched-migration-report new file mode 100644 index 000000000000..61d05f72e443 --- /dev/null +++ b/tools/perf/scripts/python/bin/sched-migration-report | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/bin/bash | ||
2 | # description: sched migration overview | ||
3 | perf trace $@ -s ~/libexec/perf-core/scripts/python/sched-migration.py | ||
diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py new file mode 100644 index 000000000000..b934383c3364 --- /dev/null +++ b/tools/perf/scripts/python/sched-migration.py | |||
@@ -0,0 +1,461 @@ | |||
1 | #!/usr/bin/python | ||
2 | # | ||
3 | # Cpu task migration overview toy | ||
4 | # | ||
5 | # Copyright (C) 2010 Frederic Weisbecker <fweisbec@gmail.com> | ||
6 | # | ||
7 | # perf trace event handlers have been generated by perf trace -g python | ||
8 | # | ||
9 | # This software is distributed under the terms of the GNU General | ||
10 | # Public License ("GPL") version 2 as published by the Free Software | ||
11 | # Foundation. | ||
12 | |||
13 | |||
14 | import os | ||
15 | import sys | ||
16 | |||
17 | from collections import defaultdict | ||
18 | from UserList import UserList | ||
19 | |||
20 | sys.path.append(os.environ['PERF_EXEC_PATH'] + \ | ||
21 | '/scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
22 | sys.path.append('scripts/python/Perf-Trace-Util/lib/Perf/Trace') | ||
23 | |||
24 | from perf_trace_context import * | ||
25 | from Core import * | ||
26 | from SchedGui import * | ||
27 | |||
28 | |||
29 | threads = { 0 : "idle"} | ||
30 | |||
31 | def thread_name(pid): | ||
32 | return "%s:%d" % (threads[pid], pid) | ||
33 | |||
34 | class RunqueueEventUnknown: | ||
35 | @staticmethod | ||
36 | def color(): | ||
37 | return None | ||
38 | |||
39 | def __repr__(self): | ||
40 | return "unknown" | ||
41 | |||
42 | class RunqueueEventSleep: | ||
43 | @staticmethod | ||
44 | def color(): | ||
45 | return (0, 0, 0xff) | ||
46 | |||
47 | def __init__(self, sleeper): | ||
48 | self.sleeper = sleeper | ||
49 | |||
50 | def __repr__(self): | ||
51 | return "%s gone to sleep" % thread_name(self.sleeper) | ||
52 | |||
53 | class RunqueueEventWakeup: | ||
54 | @staticmethod | ||
55 | def color(): | ||
56 | return (0xff, 0xff, 0) | ||
57 | |||
58 | def __init__(self, wakee): | ||
59 | self.wakee = wakee | ||
60 | |||
61 | def __repr__(self): | ||
62 | return "%s woke up" % thread_name(self.wakee) | ||
63 | |||
64 | class RunqueueEventFork: | ||
65 | @staticmethod | ||
66 | def color(): | ||
67 | return (0, 0xff, 0) | ||
68 | |||
69 | def __init__(self, child): | ||
70 | self.child = child | ||
71 | |||
72 | def __repr__(self): | ||
73 | return "new forked task %s" % thread_name(self.child) | ||
74 | |||
75 | class RunqueueMigrateIn: | ||
76 | @staticmethod | ||
77 | def color(): | ||
78 | return (0, 0xf0, 0xff) | ||
79 | |||
80 | def __init__(self, new): | ||
81 | self.new = new | ||
82 | |||
83 | def __repr__(self): | ||
84 | return "task migrated in %s" % thread_name(self.new) | ||
85 | |||
86 | class RunqueueMigrateOut: | ||
87 | @staticmethod | ||
88 | def color(): | ||
89 | return (0xff, 0, 0xff) | ||
90 | |||
91 | def __init__(self, old): | ||
92 | self.old = old | ||
93 | |||
94 | def __repr__(self): | ||
95 | return "task migrated out %s" % thread_name(self.old) | ||
96 | |||
97 | class RunqueueSnapshot: | ||
98 | def __init__(self, tasks = [0], event = RunqueueEventUnknown()): | ||
99 | self.tasks = tuple(tasks) | ||
100 | self.event = event | ||
101 | |||
102 | def sched_switch(self, prev, prev_state, next): | ||
103 | event = RunqueueEventUnknown() | ||
104 | |||
105 | if taskState(prev_state) == "R" and next in self.tasks \ | ||
106 | and prev in self.tasks: | ||
107 | return self | ||
108 | |||
109 | if taskState(prev_state) != "R": | ||
110 | event = RunqueueEventSleep(prev) | ||
111 | |||
112 | next_tasks = list(self.tasks[:]) | ||
113 | if prev in self.tasks: | ||
114 | if taskState(prev_state) != "R": | ||
115 | next_tasks.remove(prev) | ||
116 | elif taskState(prev_state) == "R": | ||
117 | next_tasks.append(prev) | ||
118 | |||
119 | if next not in next_tasks: | ||
120 | next_tasks.append(next) | ||
121 | |||
122 | return RunqueueSnapshot(next_tasks, event) | ||
123 | |||
124 | def migrate_out(self, old): | ||
125 | if old not in self.tasks: | ||
126 | return self | ||
127 | next_tasks = [task for task in self.tasks if task != old] | ||
128 | |||
129 | return RunqueueSnapshot(next_tasks, RunqueueMigrateOut(old)) | ||
130 | |||
131 | def __migrate_in(self, new, event): | ||
132 | if new in self.tasks: | ||
133 | self.event = event | ||
134 | return self | ||
135 | next_tasks = self.tasks[:] + tuple([new]) | ||
136 | |||
137 | return RunqueueSnapshot(next_tasks, event) | ||
138 | |||
139 | def migrate_in(self, new): | ||
140 | return self.__migrate_in(new, RunqueueMigrateIn(new)) | ||
141 | |||
142 | def wake_up(self, new): | ||
143 | return self.__migrate_in(new, RunqueueEventWakeup(new)) | ||
144 | |||
145 | def wake_up_new(self, new): | ||
146 | return self.__migrate_in(new, RunqueueEventFork(new)) | ||
147 | |||
148 | def load(self): | ||
149 | """ Provide the number of tasks on the runqueue. | ||
150 | Don't count idle""" | ||
151 | return len(self.tasks) - 1 | ||
152 | |||
153 | def __repr__(self): | ||
154 | ret = self.tasks.__repr__() | ||
155 | ret += self.origin_tostring() | ||
156 | |||
157 | return ret | ||
158 | |||
159 | class TimeSlice: | ||
160 | def __init__(self, start, prev): | ||
161 | self.start = start | ||
162 | self.prev = prev | ||
163 | self.end = start | ||
164 | # cpus that triggered the event | ||
165 | self.event_cpus = [] | ||
166 | if prev is not None: | ||
167 | self.total_load = prev.total_load | ||
168 | self.rqs = prev.rqs.copy() | ||
169 | else: | ||
170 | self.rqs = defaultdict(RunqueueSnapshot) | ||
171 | self.total_load = 0 | ||
172 | |||
173 | def __update_total_load(self, old_rq, new_rq): | ||
174 | diff = new_rq.load() - old_rq.load() | ||
175 | self.total_load += diff | ||
176 | |||
177 | def sched_switch(self, ts_list, prev, prev_state, next, cpu): | ||
178 | old_rq = self.prev.rqs[cpu] | ||
179 | new_rq = old_rq.sched_switch(prev, prev_state, next) | ||
180 | |||
181 | if old_rq is new_rq: | ||
182 | return | ||
183 | |||
184 | self.rqs[cpu] = new_rq | ||
185 | self.__update_total_load(old_rq, new_rq) | ||
186 | ts_list.append(self) | ||
187 | self.event_cpus = [cpu] | ||
188 | |||
189 | def migrate(self, ts_list, new, old_cpu, new_cpu): | ||
190 | if old_cpu == new_cpu: | ||
191 | return | ||
192 | old_rq = self.prev.rqs[old_cpu] | ||
193 | out_rq = old_rq.migrate_out(new) | ||
194 | self.rqs[old_cpu] = out_rq | ||
195 | self.__update_total_load(old_rq, out_rq) | ||
196 | |||
197 | new_rq = self.prev.rqs[new_cpu] | ||
198 | in_rq = new_rq.migrate_in(new) | ||
199 | self.rqs[new_cpu] = in_rq | ||
200 | self.__update_total_load(new_rq, in_rq) | ||
201 | |||
202 | ts_list.append(self) | ||
203 | |||
204 | if old_rq is not out_rq: | ||
205 | self.event_cpus.append(old_cpu) | ||
206 | self.event_cpus.append(new_cpu) | ||
207 | |||
208 | def wake_up(self, ts_list, pid, cpu, fork): | ||
209 | old_rq = self.prev.rqs[cpu] | ||
210 | if fork: | ||
211 | new_rq = old_rq.wake_up_new(pid) | ||
212 | else: | ||
213 | new_rq = old_rq.wake_up(pid) | ||
214 | |||
215 | if new_rq is old_rq: | ||
216 | return | ||
217 | self.rqs[cpu] = new_rq | ||
218 | self.__update_total_load(old_rq, new_rq) | ||
219 | ts_list.append(self) | ||
220 | self.event_cpus = [cpu] | ||
221 | |||
222 | def next(self, t): | ||
223 | self.end = t | ||
224 | return TimeSlice(t, self) | ||
225 | |||
226 | class TimeSliceList(UserList): | ||
227 | def __init__(self, arg = []): | ||
228 | self.data = arg | ||
229 | |||
230 | def get_time_slice(self, ts): | ||
231 | if len(self.data) == 0: | ||
232 | slice = TimeSlice(ts, TimeSlice(-1, None)) | ||
233 | else: | ||
234 | slice = self.data[-1].next(ts) | ||
235 | return slice | ||
236 | |||
237 | def find_time_slice(self, ts): | ||
238 | start = 0 | ||
239 | end = len(self.data) | ||
240 | found = -1 | ||
241 | searching = True | ||
242 | while searching: | ||
243 | if start == end or start == end - 1: | ||
244 | searching = False | ||
245 | |||
246 | i = (end + start) / 2 | ||
247 | if self.data[i].start <= ts and self.data[i].end >= ts: | ||
248 | found = i | ||
249 | end = i | ||
250 | continue | ||
251 | |||
252 | if self.data[i].end < ts: | ||
253 | start = i | ||
254 | |||
255 | elif self.data[i].start > ts: | ||
256 | end = i | ||
257 | |||
258 | return found | ||
259 | |||
260 | def set_root_win(self, win): | ||
261 | self.root_win = win | ||
262 | |||
263 | def mouse_down(self, cpu, t): | ||
264 | idx = self.find_time_slice(t) | ||
265 | if idx == -1: | ||
266 | return | ||
267 | |||
268 | ts = self[idx] | ||
269 | rq = ts.rqs[cpu] | ||
270 | raw = "CPU: %d\n" % cpu | ||
271 | raw += "Last event : %s\n" % rq.event.__repr__() | ||
272 | raw += "Timestamp : %d.%06d\n" % (ts.start / (10 ** 9), (ts.start % (10 ** 9)) / 1000) | ||
273 | raw += "Duration : %6d us\n" % ((ts.end - ts.start) / (10 ** 6)) | ||
274 | raw += "Load = %d\n" % rq.load() | ||
275 | for t in rq.tasks: | ||
276 | raw += "%s \n" % thread_name(t) | ||
277 | |||
278 | self.root_win.update_summary(raw) | ||
279 | |||
280 | def update_rectangle_cpu(self, slice, cpu): | ||
281 | rq = slice.rqs[cpu] | ||
282 | |||
283 | if slice.total_load != 0: | ||
284 | load_rate = rq.load() / float(slice.total_load) | ||
285 | else: | ||
286 | load_rate = 0 | ||
287 | |||
288 | red_power = int(0xff - (0xff * load_rate)) | ||
289 | color = (0xff, red_power, red_power) | ||
290 | |||
291 | top_color = None | ||
292 | |||
293 | if cpu in slice.event_cpus: | ||
294 | top_color = rq.event.color() | ||
295 | |||
296 | self.root_win.paint_rectangle_zone(cpu, color, top_color, slice.start, slice.end) | ||
297 | |||
298 | def fill_zone(self, start, end): | ||
299 | i = self.find_time_slice(start) | ||
300 | if i == -1: | ||
301 | return | ||
302 | |||
303 | for i in xrange(i, len(self.data)): | ||
304 | timeslice = self.data[i] | ||
305 | if timeslice.start > end: | ||
306 | return | ||
307 | |||
308 | for cpu in timeslice.rqs: | ||
309 | self.update_rectangle_cpu(timeslice, cpu) | ||
310 | |||
311 | def interval(self): | ||
312 | if len(self.data) == 0: | ||
313 | return (0, 0) | ||
314 | |||
315 | return (self.data[0].start, self.data[-1].end) | ||
316 | |||
317 | def nr_rectangles(self): | ||
318 | last_ts = self.data[-1] | ||
319 | max_cpu = 0 | ||
320 | for cpu in last_ts.rqs: | ||
321 | if cpu > max_cpu: | ||
322 | max_cpu = cpu | ||
323 | return max_cpu | ||
324 | |||
325 | |||
326 | class SchedEventProxy: | ||
327 | def __init__(self): | ||
328 | self.current_tsk = defaultdict(lambda : -1) | ||
329 | self.timeslices = TimeSliceList() | ||
330 | |||
331 | def sched_switch(self, headers, prev_comm, prev_pid, prev_prio, prev_state, | ||
332 | next_comm, next_pid, next_prio): | ||
333 | """ Ensure the task we sched out this cpu is really the one | ||
334 | we logged. Otherwise we may have missed traces """ | ||
335 | |||
336 | on_cpu_task = self.current_tsk[headers.cpu] | ||
337 | |||
338 | if on_cpu_task != -1 and on_cpu_task != prev_pid: | ||
339 | print "Sched switch event rejected ts: %s cpu: %d prev: %s(%d) next: %s(%d)" % \ | ||
340 | (headers.ts_format(), headers.cpu, prev_comm, prev_pid, next_comm, next_pid) | ||
341 | |||
342 | threads[prev_pid] = prev_comm | ||
343 | threads[next_pid] = next_comm | ||
344 | self.current_tsk[headers.cpu] = next_pid | ||
345 | |||
346 | ts = self.timeslices.get_time_slice(headers.ts()) | ||
347 | ts.sched_switch(self.timeslices, prev_pid, prev_state, next_pid, headers.cpu) | ||
348 | |||
349 | def migrate(self, headers, pid, prio, orig_cpu, dest_cpu): | ||
350 | ts = self.timeslices.get_time_slice(headers.ts()) | ||
351 | ts.migrate(self.timeslices, pid, orig_cpu, dest_cpu) | ||
352 | |||
353 | def wake_up(self, headers, comm, pid, success, target_cpu, fork): | ||
354 | if success == 0: | ||
355 | return | ||
356 | ts = self.timeslices.get_time_slice(headers.ts()) | ||
357 | ts.wake_up(self.timeslices, pid, target_cpu, fork) | ||
358 | |||
359 | |||
360 | def trace_begin(): | ||
361 | global parser | ||
362 | parser = SchedEventProxy() | ||
363 | |||
364 | def trace_end(): | ||
365 | app = wx.App(False) | ||
366 | timeslices = parser.timeslices | ||
367 | frame = RootFrame(timeslices, "Migration") | ||
368 | app.MainLoop() | ||
369 | |||
370 | def sched__sched_stat_runtime(event_name, context, common_cpu, | ||
371 | common_secs, common_nsecs, common_pid, common_comm, | ||
372 | comm, pid, runtime, vruntime): | ||
373 | pass | ||
374 | |||
375 | def sched__sched_stat_iowait(event_name, context, common_cpu, | ||
376 | common_secs, common_nsecs, common_pid, common_comm, | ||
377 | comm, pid, delay): | ||
378 | pass | ||
379 | |||
380 | def sched__sched_stat_sleep(event_name, context, common_cpu, | ||
381 | common_secs, common_nsecs, common_pid, common_comm, | ||
382 | comm, pid, delay): | ||
383 | pass | ||
384 | |||
385 | def sched__sched_stat_wait(event_name, context, common_cpu, | ||
386 | common_secs, common_nsecs, common_pid, common_comm, | ||
387 | comm, pid, delay): | ||
388 | pass | ||
389 | |||
390 | def sched__sched_process_fork(event_name, context, common_cpu, | ||
391 | common_secs, common_nsecs, common_pid, common_comm, | ||
392 | parent_comm, parent_pid, child_comm, child_pid): | ||
393 | pass | ||
394 | |||
395 | def sched__sched_process_wait(event_name, context, common_cpu, | ||
396 | common_secs, common_nsecs, common_pid, common_comm, | ||
397 | comm, pid, prio): | ||
398 | pass | ||
399 | |||
400 | def sched__sched_process_exit(event_name, context, common_cpu, | ||
401 | common_secs, common_nsecs, common_pid, common_comm, | ||
402 | comm, pid, prio): | ||
403 | pass | ||
404 | |||
405 | def sched__sched_process_free(event_name, context, common_cpu, | ||
406 | common_secs, common_nsecs, common_pid, common_comm, | ||
407 | comm, pid, prio): | ||
408 | pass | ||
409 | |||
410 | def sched__sched_migrate_task(event_name, context, common_cpu, | ||
411 | common_secs, common_nsecs, common_pid, common_comm, | ||
412 | comm, pid, prio, orig_cpu, | ||
413 | dest_cpu): | ||
414 | headers = EventHeaders(common_cpu, common_secs, common_nsecs, | ||
415 | common_pid, common_comm) | ||
416 | parser.migrate(headers, pid, prio, orig_cpu, dest_cpu) | ||
417 | |||
418 | def sched__sched_switch(event_name, context, common_cpu, | ||
419 | common_secs, common_nsecs, common_pid, common_comm, | ||
420 | prev_comm, prev_pid, prev_prio, prev_state, | ||
421 | next_comm, next_pid, next_prio): | ||
422 | |||
423 | headers = EventHeaders(common_cpu, common_secs, common_nsecs, | ||
424 | common_pid, common_comm) | ||
425 | parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state, | ||
426 | next_comm, next_pid, next_prio) | ||
427 | |||
428 | def sched__sched_wakeup_new(event_name, context, common_cpu, | ||
429 | common_secs, common_nsecs, common_pid, common_comm, | ||
430 | comm, pid, prio, success, | ||
431 | target_cpu): | ||
432 | headers = EventHeaders(common_cpu, common_secs, common_nsecs, | ||
433 | common_pid, common_comm) | ||
434 | parser.wake_up(headers, comm, pid, success, target_cpu, 1) | ||
435 | |||
436 | def sched__sched_wakeup(event_name, context, common_cpu, | ||
437 | common_secs, common_nsecs, common_pid, common_comm, | ||
438 | comm, pid, prio, success, | ||
439 | target_cpu): | ||
440 | headers = EventHeaders(common_cpu, common_secs, common_nsecs, | ||
441 | common_pid, common_comm) | ||
442 | parser.wake_up(headers, comm, pid, success, target_cpu, 0) | ||
443 | |||
444 | def sched__sched_wait_task(event_name, context, common_cpu, | ||
445 | common_secs, common_nsecs, common_pid, common_comm, | ||
446 | comm, pid, prio): | ||
447 | pass | ||
448 | |||
449 | def sched__sched_kthread_stop_ret(event_name, context, common_cpu, | ||
450 | common_secs, common_nsecs, common_pid, common_comm, | ||
451 | ret): | ||
452 | pass | ||
453 | |||
454 | def sched__sched_kthread_stop(event_name, context, common_cpu, | ||
455 | common_secs, common_nsecs, common_pid, common_comm, | ||
456 | comm, pid): | ||
457 | pass | ||
458 | |||
459 | def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, | ||
460 | common_pid, common_comm): | ||
461 | pass | ||
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 70c5cf87d020..e437edb72417 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "event.h" | 12 | #include "event.h" |
13 | #include "symbol.h" | 13 | #include "symbol.h" |
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include "debug.h" | ||
15 | 16 | ||
16 | static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | 17 | static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) |
17 | { | 18 | { |
@@ -34,28 +35,43 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | |||
34 | return 0; | 35 | return 0; |
35 | } | 36 | } |
36 | 37 | ||
38 | static int event__exit_del_thread(event_t *self, struct perf_session *session) | ||
39 | { | ||
40 | struct thread *thread = perf_session__findnew(session, self->fork.tid); | ||
41 | |||
42 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | ||
43 | self->fork.ppid, self->fork.ptid); | ||
44 | |||
45 | if (thread) { | ||
46 | rb_erase(&thread->rb_node, &session->threads); | ||
47 | session->last_match = NULL; | ||
48 | thread__delete(thread); | ||
49 | } | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | |||
37 | struct perf_event_ops build_id__mark_dso_hit_ops = { | 54 | struct perf_event_ops build_id__mark_dso_hit_ops = { |
38 | .sample = build_id__mark_dso_hit, | 55 | .sample = build_id__mark_dso_hit, |
39 | .mmap = event__process_mmap, | 56 | .mmap = event__process_mmap, |
40 | .fork = event__process_task, | 57 | .fork = event__process_task, |
58 | .exit = event__exit_del_thread, | ||
41 | }; | 59 | }; |
42 | 60 | ||
43 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) | 61 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) |
44 | { | 62 | { |
45 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 63 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
46 | const char *home; | ||
47 | 64 | ||
48 | if (!self->has_build_id) | 65 | if (!self->has_build_id) |
49 | return NULL; | 66 | return NULL; |
50 | 67 | ||
51 | build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); | 68 | build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); |
52 | home = getenv("HOME"); | ||
53 | if (bf == NULL) { | 69 | if (bf == NULL) { |
54 | if (asprintf(&bf, "%s/%s/.build-id/%.2s/%s", home, | 70 | if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir, |
55 | DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2) < 0) | 71 | build_id_hex, build_id_hex + 2) < 0) |
56 | return NULL; | 72 | return NULL; |
57 | } else | 73 | } else |
58 | snprintf(bf, size, "%s/%s/.build-id/%.2s/%s", home, | 74 | snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir, |
59 | DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2); | 75 | build_id_hex, build_id_hex + 2); |
60 | return bf; | 76 | return bf; |
61 | } | 77 | } |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 65fe664fddf6..27e9ebe4076e 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -23,6 +23,7 @@ extern int perf_config(config_fn_t fn, void *); | |||
23 | extern int perf_config_int(const char *, const char *); | 23 | extern int perf_config_int(const char *, const char *); |
24 | extern int perf_config_bool(const char *, const char *); | 24 | extern int perf_config_bool(const char *, const char *); |
25 | extern int config_error_nonbool(const char *); | 25 | extern int config_error_nonbool(const char *); |
26 | extern const char *perf_config_dirname(const char *, const char *); | ||
26 | 27 | ||
27 | /* pager.c */ | 28 | /* pager.c */ |
28 | extern void setup_pager(void); | 29 | extern void setup_pager(void); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 52c777e451ed..f231f43424d2 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -18,7 +18,7 @@ | |||
18 | #include "util.h" | 18 | #include "util.h" |
19 | #include "callchain.h" | 19 | #include "callchain.h" |
20 | 20 | ||
21 | bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) | 21 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) |
22 | { | 22 | { |
23 | unsigned int chain_size = event->header.size; | 23 | unsigned int chain_size = event->header.size; |
24 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | 24 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index f2e9ee164bd8..624a96c636fd 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -63,5 +63,5 @@ int register_callchain_param(struct callchain_param *param); | |||
63 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, | 63 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, |
64 | struct map_symbol *syms, u64 period); | 64 | struct map_symbol *syms, u64 period); |
65 | 65 | ||
66 | bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); | 66 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); |
67 | #endif /* __PERF_CALLCHAIN_H */ | 67 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index dabe892d0e53..e02d78cae70f 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -11,6 +11,11 @@ | |||
11 | 11 | ||
12 | #define MAXNAME (256) | 12 | #define MAXNAME (256) |
13 | 13 | ||
14 | #define DEBUG_CACHE_DIR ".debug" | ||
15 | |||
16 | |||
17 | char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ | ||
18 | |||
14 | static FILE *config_file; | 19 | static FILE *config_file; |
15 | static const char *config_file_name; | 20 | static const char *config_file_name; |
16 | static int config_linenr; | 21 | static int config_linenr; |
@@ -127,7 +132,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len) | |||
127 | break; | 132 | break; |
128 | if (!iskeychar(c)) | 133 | if (!iskeychar(c)) |
129 | break; | 134 | break; |
130 | name[len++] = tolower(c); | 135 | name[len++] = c; |
131 | if (len >= MAXNAME) | 136 | if (len >= MAXNAME) |
132 | return -1; | 137 | return -1; |
133 | } | 138 | } |
@@ -327,6 +332,13 @@ int perf_config_bool(const char *name, const char *value) | |||
327 | return !!perf_config_bool_or_int(name, value, &discard); | 332 | return !!perf_config_bool_or_int(name, value, &discard); |
328 | } | 333 | } |
329 | 334 | ||
335 | const char *perf_config_dirname(const char *name, const char *value) | ||
336 | { | ||
337 | if (!name) | ||
338 | return NULL; | ||
339 | return value; | ||
340 | } | ||
341 | |||
330 | static int perf_default_core_config(const char *var __used, const char *value __used) | 342 | static int perf_default_core_config(const char *var __used, const char *value __used) |
331 | { | 343 | { |
332 | /* Add other config variables here and to Documentation/config.txt. */ | 344 | /* Add other config variables here and to Documentation/config.txt. */ |
@@ -428,3 +440,53 @@ int config_error_nonbool(const char *var) | |||
428 | { | 440 | { |
429 | return error("Missing value for '%s'", var); | 441 | return error("Missing value for '%s'", var); |
430 | } | 442 | } |
443 | |||
444 | struct buildid_dir_config { | ||
445 | char *dir; | ||
446 | }; | ||
447 | |||
448 | static int buildid_dir_command_config(const char *var, const char *value, | ||
449 | void *data) | ||
450 | { | ||
451 | struct buildid_dir_config *c = data; | ||
452 | const char *v; | ||
453 | |||
454 | /* same dir for all commands */ | ||
455 | if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) { | ||
456 | v = perf_config_dirname(var, value); | ||
457 | if (!v) | ||
458 | return -1; | ||
459 | strncpy(c->dir, v, MAXPATHLEN-1); | ||
460 | c->dir[MAXPATHLEN-1] = '\0'; | ||
461 | } | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static void check_buildid_dir_config(void) | ||
466 | { | ||
467 | struct buildid_dir_config c; | ||
468 | c.dir = buildid_dir; | ||
469 | perf_config(buildid_dir_command_config, &c); | ||
470 | } | ||
471 | |||
472 | void set_buildid_dir(void) | ||
473 | { | ||
474 | buildid_dir[0] = '\0'; | ||
475 | |||
476 | /* try config file */ | ||
477 | check_buildid_dir_config(); | ||
478 | |||
479 | /* default to $HOME/.debug */ | ||
480 | if (buildid_dir[0] == '\0') { | ||
481 | char *v = getenv("HOME"); | ||
482 | if (v) { | ||
483 | snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s", | ||
484 | v, DEBUG_CACHE_DIR); | ||
485 | } else { | ||
486 | strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1); | ||
487 | } | ||
488 | buildid_dir[MAXPATHLEN-1] = '\0'; | ||
489 | } | ||
490 | /* for communicating with external commands */ | ||
491 | setenv("PERF_BUILDID_DIR", buildid_dir, 1); | ||
492 | } | ||
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 4e01490e51e5..0f9b8d7a7d7e 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -20,7 +20,7 @@ static int default_cpu_map(void) | |||
20 | return nr_cpus; | 20 | return nr_cpus; |
21 | } | 21 | } |
22 | 22 | ||
23 | int read_cpu_map(void) | 23 | static int read_all_cpu_map(void) |
24 | { | 24 | { |
25 | FILE *onlnf; | 25 | FILE *onlnf; |
26 | int nr_cpus = 0; | 26 | int nr_cpus = 0; |
@@ -57,3 +57,58 @@ int read_cpu_map(void) | |||
57 | 57 | ||
58 | return default_cpu_map(); | 58 | return default_cpu_map(); |
59 | } | 59 | } |
60 | |||
61 | int read_cpu_map(const char *cpu_list) | ||
62 | { | ||
63 | unsigned long start_cpu, end_cpu = 0; | ||
64 | char *p = NULL; | ||
65 | int i, nr_cpus = 0; | ||
66 | |||
67 | if (!cpu_list) | ||
68 | return read_all_cpu_map(); | ||
69 | |||
70 | if (!isdigit(*cpu_list)) | ||
71 | goto invalid; | ||
72 | |||
73 | while (isdigit(*cpu_list)) { | ||
74 | p = NULL; | ||
75 | start_cpu = strtoul(cpu_list, &p, 0); | ||
76 | if (start_cpu >= INT_MAX | ||
77 | || (*p != '\0' && *p != ',' && *p != '-')) | ||
78 | goto invalid; | ||
79 | |||
80 | if (*p == '-') { | ||
81 | cpu_list = ++p; | ||
82 | p = NULL; | ||
83 | end_cpu = strtoul(cpu_list, &p, 0); | ||
84 | |||
85 | if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',')) | ||
86 | goto invalid; | ||
87 | |||
88 | if (end_cpu < start_cpu) | ||
89 | goto invalid; | ||
90 | } else { | ||
91 | end_cpu = start_cpu; | ||
92 | } | ||
93 | |||
94 | for (; start_cpu <= end_cpu; start_cpu++) { | ||
95 | /* check for duplicates */ | ||
96 | for (i = 0; i < nr_cpus; i++) | ||
97 | if (cpumap[i] == (int)start_cpu) | ||
98 | goto invalid; | ||
99 | |||
100 | assert(nr_cpus < MAX_NR_CPUS); | ||
101 | cpumap[nr_cpus++] = (int)start_cpu; | ||
102 | } | ||
103 | if (*p) | ||
104 | ++p; | ||
105 | |||
106 | cpu_list = p; | ||
107 | } | ||
108 | if (nr_cpus > 0) | ||
109 | return nr_cpus; | ||
110 | |||
111 | return default_cpu_map(); | ||
112 | invalid: | ||
113 | return -1; | ||
114 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 86c78bb33098..3e60f56e490e 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_CPUMAP_H | 1 | #ifndef __PERF_CPUMAP_H |
2 | #define __PERF_CPUMAP_H | 2 | #define __PERF_CPUMAP_H |
3 | 3 | ||
4 | extern int read_cpu_map(void); | 4 | extern int read_cpu_map(const char *cpu_list); |
5 | extern int cpumap[]; | 5 | extern int cpumap[]; |
6 | 6 | ||
7 | #endif /* __PERF_CPUMAP_H */ | 7 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 6cddff2bc970..318dab15d177 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -86,12 +86,10 @@ void trace_event(event_t *event) | |||
86 | dump_printf_color(" ", color); | 86 | dump_printf_color(" ", color); |
87 | for (j = 0; j < 15-(i & 15); j++) | 87 | for (j = 0; j < 15-(i & 15); j++) |
88 | dump_printf_color(" ", color); | 88 | dump_printf_color(" ", color); |
89 | for (j = 0; j < (i & 15); j++) { | 89 | for (j = i & ~15; j <= i; j++) { |
90 | if (isprint(raw_event[i-15+j])) | 90 | dump_printf_color("%c", color, |
91 | dump_printf_color("%c", color, | 91 | isprint(raw_event[j]) ? |
92 | raw_event[i-15+j]); | 92 | raw_event[j] : '.'); |
93 | else | ||
94 | dump_printf_color(".", color); | ||
95 | } | 93 | } |
96 | dump_printf_color("\n", color); | 94 | dump_printf_color("\n", color); |
97 | } | 95 | } |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 2fbf6a463c81..dab9e754a281 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -151,7 +151,6 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
151 | continue; | 151 | continue; |
152 | pbf += n + 3; | 152 | pbf += n + 3; |
153 | if (*pbf == 'x') { /* vm_exec */ | 153 | if (*pbf == 'x') { /* vm_exec */ |
154 | u64 vm_pgoff; | ||
155 | char *execname = strchr(bf, '/'); | 154 | char *execname = strchr(bf, '/'); |
156 | 155 | ||
157 | /* Catch VDSO */ | 156 | /* Catch VDSO */ |
@@ -162,12 +161,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, | |||
162 | continue; | 161 | continue; |
163 | 162 | ||
164 | pbf += 3; | 163 | pbf += 3; |
165 | n = hex2u64(pbf, &vm_pgoff); | 164 | n = hex2u64(pbf, &ev.mmap.pgoff); |
166 | /* pgoff is in bytes, not pages */ | ||
167 | if (n >= 0) | ||
168 | ev.mmap.pgoff = vm_pgoff << getpagesize(); | ||
169 | else | ||
170 | ev.mmap.pgoff = 0; | ||
171 | 165 | ||
172 | size = strlen(execname); | 166 | size = strlen(execname); |
173 | execname[size - 1] = '\0'; /* Remove \n */ | 167 | execname[size - 1] = '\0'; /* Remove \n */ |
@@ -340,30 +334,29 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
340 | return process(&ev, session); | 334 | return process(&ev, session); |
341 | } | 335 | } |
342 | 336 | ||
343 | static void thread__comm_adjust(struct thread *self) | 337 | static void thread__comm_adjust(struct thread *self, struct hists *hists) |
344 | { | 338 | { |
345 | char *comm = self->comm; | 339 | char *comm = self->comm; |
346 | 340 | ||
347 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 341 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
348 | (!symbol_conf.comm_list || | 342 | (!symbol_conf.comm_list || |
349 | strlist__has_entry(symbol_conf.comm_list, comm))) { | 343 | strlist__has_entry(symbol_conf.comm_list, comm))) { |
350 | unsigned int slen = strlen(comm); | 344 | u16 slen = strlen(comm); |
351 | 345 | ||
352 | if (slen > comms__col_width) { | 346 | if (hists__new_col_len(hists, HISTC_COMM, slen)) |
353 | comms__col_width = slen; | 347 | hists__set_col_len(hists, HISTC_THREAD, slen + 6); |
354 | threads__col_width = slen + 6; | ||
355 | } | ||
356 | } | 348 | } |
357 | } | 349 | } |
358 | 350 | ||
359 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | 351 | static int thread__set_comm_adjust(struct thread *self, const char *comm, |
352 | struct hists *hists) | ||
360 | { | 353 | { |
361 | int ret = thread__set_comm(self, comm); | 354 | int ret = thread__set_comm(self, comm); |
362 | 355 | ||
363 | if (ret) | 356 | if (ret) |
364 | return ret; | 357 | return ret; |
365 | 358 | ||
366 | thread__comm_adjust(self); | 359 | thread__comm_adjust(self, hists); |
367 | 360 | ||
368 | return 0; | 361 | return 0; |
369 | } | 362 | } |
@@ -374,7 +367,8 @@ int event__process_comm(event_t *self, struct perf_session *session) | |||
374 | 367 | ||
375 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); | 368 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); |
376 | 369 | ||
377 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { | 370 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, |
371 | &session->hists)) { | ||
378 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 372 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
379 | return -1; | 373 | return -1; |
380 | } | 374 | } |
@@ -456,6 +450,7 @@ static int event__process_kernel_mmap(event_t *self, | |||
456 | goto out_problem; | 450 | goto out_problem; |
457 | 451 | ||
458 | map->dso->short_name = name; | 452 | map->dso->short_name = name; |
453 | map->dso->sname_alloc = 1; | ||
459 | map->end = map->start + self->mmap.len; | 454 | map->end = map->start + self->mmap.len; |
460 | } else if (is_kernel_mmap) { | 455 | } else if (is_kernel_mmap) { |
461 | const char *symbol_name = (self->mmap.filename + | 456 | const char *symbol_name = (self->mmap.filename + |
@@ -514,12 +509,13 @@ int event__process_mmap(event_t *self, struct perf_session *session) | |||
514 | if (machine == NULL) | 509 | if (machine == NULL) |
515 | goto out_problem; | 510 | goto out_problem; |
516 | thread = perf_session__findnew(session, self->mmap.pid); | 511 | thread = perf_session__findnew(session, self->mmap.pid); |
512 | if (thread == NULL) | ||
513 | goto out_problem; | ||
517 | map = map__new(&machine->user_dsos, self->mmap.start, | 514 | map = map__new(&machine->user_dsos, self->mmap.start, |
518 | self->mmap.len, self->mmap.pgoff, | 515 | self->mmap.len, self->mmap.pgoff, |
519 | self->mmap.pid, self->mmap.filename, | 516 | self->mmap.pid, self->mmap.filename, |
520 | MAP__FUNCTION, session->cwd, session->cwdlen); | 517 | MAP__FUNCTION); |
521 | 518 | if (map == NULL) | |
522 | if (thread == NULL || map == NULL) | ||
523 | goto out_problem; | 519 | goto out_problem; |
524 | 520 | ||
525 | thread__insert_map(thread, map); | 521 | thread__insert_map(thread, map); |
@@ -552,6 +548,26 @@ int event__process_task(event_t *self, struct perf_session *session) | |||
552 | return 0; | 548 | return 0; |
553 | } | 549 | } |
554 | 550 | ||
551 | int event__process(event_t *event, struct perf_session *session) | ||
552 | { | ||
553 | switch (event->header.type) { | ||
554 | case PERF_RECORD_COMM: | ||
555 | event__process_comm(event, session); | ||
556 | break; | ||
557 | case PERF_RECORD_MMAP: | ||
558 | event__process_mmap(event, session); | ||
559 | break; | ||
560 | case PERF_RECORD_FORK: | ||
561 | case PERF_RECORD_EXIT: | ||
562 | event__process_task(event, session); | ||
563 | break; | ||
564 | default: | ||
565 | break; | ||
566 | } | ||
567 | |||
568 | return 0; | ||
569 | } | ||
570 | |||
555 | void thread__find_addr_map(struct thread *self, | 571 | void thread__find_addr_map(struct thread *self, |
556 | struct perf_session *session, u8 cpumode, | 572 | struct perf_session *session, u8 cpumode, |
557 | enum map_type type, pid_t pid, u64 addr, | 573 | enum map_type type, pid_t pid, u64 addr, |
@@ -641,27 +657,49 @@ void thread__find_addr_location(struct thread *self, | |||
641 | al->sym = NULL; | 657 | al->sym = NULL; |
642 | } | 658 | } |
643 | 659 | ||
644 | static void dso__calc_col_width(struct dso *self) | 660 | static void dso__calc_col_width(struct dso *self, struct hists *hists) |
645 | { | 661 | { |
646 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 662 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
647 | (!symbol_conf.dso_list || | 663 | (!symbol_conf.dso_list || |
648 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | 664 | strlist__has_entry(symbol_conf.dso_list, self->name))) { |
649 | u16 slen = self->short_name_len; | 665 | u16 slen = dso__name_len(self); |
650 | if (verbose) | 666 | hists__new_col_len(hists, HISTC_DSO, slen); |
651 | slen = self->long_name_len; | ||
652 | if (dsos__col_width < slen) | ||
653 | dsos__col_width = slen; | ||
654 | } | 667 | } |
655 | 668 | ||
656 | self->slen_calculated = 1; | 669 | self->slen_calculated = 1; |
657 | } | 670 | } |
658 | 671 | ||
659 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 672 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
660 | struct addr_location *al, symbol_filter_t filter) | 673 | struct addr_location *al, struct sample_data *data, |
674 | symbol_filter_t filter) | ||
661 | { | 675 | { |
662 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 676 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
663 | struct thread *thread = perf_session__findnew(session, self->ip.pid); | 677 | struct thread *thread; |
664 | 678 | ||
679 | event__parse_sample(self, session->sample_type, data); | ||
680 | |||
681 | dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n", | ||
682 | self->header.misc, data->pid, data->tid, data->ip, | ||
683 | data->period, data->cpu); | ||
684 | |||
685 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) { | ||
686 | unsigned int i; | ||
687 | |||
688 | dump_printf("... chain: nr:%Lu\n", data->callchain->nr); | ||
689 | |||
690 | if (!ip_callchain__valid(data->callchain, self)) { | ||
691 | pr_debug("call-chain problem with event, " | ||
692 | "skipping it.\n"); | ||
693 | goto out_filtered; | ||
694 | } | ||
695 | |||
696 | if (dump_trace) { | ||
697 | for (i = 0; i < data->callchain->nr; i++) | ||
698 | dump_printf("..... %2d: %016Lx\n", | ||
699 | i, data->callchain->ips[i]); | ||
700 | } | ||
701 | } | ||
702 | thread = perf_session__findnew(session, self->ip.pid); | ||
665 | if (thread == NULL) | 703 | if (thread == NULL) |
666 | return -1; | 704 | return -1; |
667 | 705 | ||
@@ -687,6 +725,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
687 | al->map ? al->map->dso->long_name : | 725 | al->map ? al->map->dso->long_name : |
688 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 726 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
689 | al->sym = NULL; | 727 | al->sym = NULL; |
728 | al->cpu = data->cpu; | ||
690 | 729 | ||
691 | if (al->map) { | 730 | if (al->map) { |
692 | if (symbol_conf.dso_list && | 731 | if (symbol_conf.dso_list && |
@@ -703,16 +742,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
703 | * sampled. | 742 | * sampled. |
704 | */ | 743 | */ |
705 | if (!sort_dso.elide && !al->map->dso->slen_calculated) | 744 | if (!sort_dso.elide && !al->map->dso->slen_calculated) |
706 | dso__calc_col_width(al->map->dso); | 745 | dso__calc_col_width(al->map->dso, &session->hists); |
707 | 746 | ||
708 | al->sym = map__find_symbol(al->map, al->addr, filter); | 747 | al->sym = map__find_symbol(al->map, al->addr, filter); |
709 | } else { | 748 | } else { |
710 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 749 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
711 | 750 | ||
712 | if (dsos__col_width < unresolved_col_width && | 751 | if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && |
713 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 752 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
714 | !symbol_conf.dso_list) | 753 | !symbol_conf.dso_list) |
715 | dsos__col_width = unresolved_col_width; | 754 | hists__set_col_len(&session->hists, HISTC_DSO, |
755 | unresolved_col_width); | ||
716 | } | 756 | } |
717 | 757 | ||
718 | if (symbol_conf.sym_list && al->sym && | 758 | if (symbol_conf.sym_list && al->sym && |
@@ -726,9 +766,9 @@ out_filtered: | |||
726 | return 0; | 766 | return 0; |
727 | } | 767 | } |
728 | 768 | ||
729 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | 769 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data) |
730 | { | 770 | { |
731 | u64 *array = event->sample.array; | 771 | const u64 *array = event->sample.array; |
732 | 772 | ||
733 | if (type & PERF_SAMPLE_IP) { | 773 | if (type & PERF_SAMPLE_IP) { |
734 | data->ip = event->ip.ip; | 774 | data->ip = event->ip.ip; |
@@ -767,7 +807,8 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | |||
767 | u32 *p = (u32 *)array; | 807 | u32 *p = (u32 *)array; |
768 | data->cpu = *p; | 808 | data->cpu = *p; |
769 | array++; | 809 | array++; |
770 | } | 810 | } else |
811 | data->cpu = -1; | ||
771 | 812 | ||
772 | if (type & PERF_SAMPLE_PERIOD) { | 813 | if (type & PERF_SAMPLE_PERIOD) { |
773 | data->period = *array; | 814 | data->period = *array; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 8577085db067..8e790dae7026 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -154,11 +154,13 @@ int event__process_comm(event_t *self, struct perf_session *session); | |||
154 | int event__process_lost(event_t *self, struct perf_session *session); | 154 | int event__process_lost(event_t *self, struct perf_session *session); |
155 | int event__process_mmap(event_t *self, struct perf_session *session); | 155 | int event__process_mmap(event_t *self, struct perf_session *session); |
156 | int event__process_task(event_t *self, struct perf_session *session); | 156 | int event__process_task(event_t *self, struct perf_session *session); |
157 | int event__process(event_t *event, struct perf_session *session); | ||
157 | 158 | ||
158 | struct addr_location; | 159 | struct addr_location; |
159 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 160 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
160 | struct addr_location *al, symbol_filter_t filter); | 161 | struct addr_location *al, struct sample_data *data, |
161 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); | 162 | symbol_filter_t filter); |
163 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data); | ||
162 | 164 | ||
163 | extern const char *event__name[]; | 165 | extern const char *event__name[]; |
164 | 166 | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 1f62435f96c2..d7e67b167ea3 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include "symbol.h" | 16 | #include "symbol.h" |
17 | #include "debug.h" | 17 | #include "debug.h" |
18 | 18 | ||
19 | static bool no_buildid_cache = false; | ||
20 | |||
19 | /* | 21 | /* |
20 | * Create new perf.data header attribute: | 22 | * Create new perf.data header attribute: |
21 | */ | 23 | */ |
@@ -385,8 +387,7 @@ static int perf_session__cache_build_ids(struct perf_session *self) | |||
385 | int ret; | 387 | int ret; |
386 | char debugdir[PATH_MAX]; | 388 | char debugdir[PATH_MAX]; |
387 | 389 | ||
388 | snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), | 390 | snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); |
389 | DEBUG_CACHE_DIR); | ||
390 | 391 | ||
391 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | 392 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) |
392 | return -1; | 393 | return -1; |
@@ -471,7 +472,8 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
471 | } | 472 | } |
472 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | 473 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - |
473 | buildid_sec->offset; | 474 | buildid_sec->offset; |
474 | perf_session__cache_build_ids(session); | 475 | if (!no_buildid_cache) |
476 | perf_session__cache_build_ids(session); | ||
475 | } | 477 | } |
476 | 478 | ||
477 | lseek(fd, sec_start, SEEK_SET); | 479 | lseek(fd, sec_start, SEEK_SET); |
@@ -1190,3 +1192,8 @@ int event__process_build_id(event_t *self, | |||
1190 | session); | 1192 | session); |
1191 | return 0; | 1193 | return 0; |
1192 | } | 1194 | } |
1195 | |||
1196 | void disable_buildid_cache(void) | ||
1197 | { | ||
1198 | no_buildid_cache = true; | ||
1199 | } | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 784ee0bdda77..e7263d49bcf0 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -5,11 +5,61 @@ | |||
5 | #include "sort.h" | 5 | #include "sort.h" |
6 | #include <math.h> | 6 | #include <math.h> |
7 | 7 | ||
8 | enum hist_filter { | ||
9 | HIST_FILTER__DSO, | ||
10 | HIST_FILTER__THREAD, | ||
11 | HIST_FILTER__PARENT, | ||
12 | }; | ||
13 | |||
8 | struct callchain_param callchain_param = { | 14 | struct callchain_param callchain_param = { |
9 | .mode = CHAIN_GRAPH_REL, | 15 | .mode = CHAIN_GRAPH_REL, |
10 | .min_percent = 0.5 | 16 | .min_percent = 0.5 |
11 | }; | 17 | }; |
12 | 18 | ||
19 | u16 hists__col_len(struct hists *self, enum hist_column col) | ||
20 | { | ||
21 | return self->col_len[col]; | ||
22 | } | ||
23 | |||
24 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) | ||
25 | { | ||
26 | self->col_len[col] = len; | ||
27 | } | ||
28 | |||
29 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) | ||
30 | { | ||
31 | if (len > hists__col_len(self, col)) { | ||
32 | hists__set_col_len(self, col, len); | ||
33 | return true; | ||
34 | } | ||
35 | return false; | ||
36 | } | ||
37 | |||
38 | static void hists__reset_col_len(struct hists *self) | ||
39 | { | ||
40 | enum hist_column col; | ||
41 | |||
42 | for (col = 0; col < HISTC_NR_COLS; ++col) | ||
43 | hists__set_col_len(self, col, 0); | ||
44 | } | ||
45 | |||
46 | static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | ||
47 | { | ||
48 | u16 len; | ||
49 | |||
50 | if (h->ms.sym) | ||
51 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | ||
52 | |||
53 | len = thread__comm_len(h->thread); | ||
54 | if (hists__new_col_len(self, HISTC_COMM, len)) | ||
55 | hists__set_col_len(self, HISTC_THREAD, len + 6); | ||
56 | |||
57 | if (h->ms.map) { | ||
58 | len = dso__name_len(h->ms.map->dso); | ||
59 | hists__new_col_len(self, HISTC_DSO, len); | ||
60 | } | ||
61 | } | ||
62 | |||
13 | static void hist_entry__add_cpumode_period(struct hist_entry *self, | 63 | static void hist_entry__add_cpumode_period(struct hist_entry *self, |
14 | unsigned int cpumode, u64 period) | 64 | unsigned int cpumode, u64 period) |
15 | { | 65 | { |
@@ -43,6 +93,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
43 | if (self != NULL) { | 93 | if (self != NULL) { |
44 | *self = *template; | 94 | *self = *template; |
45 | self->nr_events = 1; | 95 | self->nr_events = 1; |
96 | if (self->ms.map) | ||
97 | self->ms.map->referenced = true; | ||
46 | if (symbol_conf.use_callchain) | 98 | if (symbol_conf.use_callchain) |
47 | callchain_init(self->callchain); | 99 | callchain_init(self->callchain); |
48 | } | 100 | } |
@@ -50,11 +102,19 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
50 | return self; | 102 | return self; |
51 | } | 103 | } |
52 | 104 | ||
53 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) | 105 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) |
54 | { | 106 | { |
55 | if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) | 107 | if (!h->filtered) { |
56 | self->max_sym_namelen = entry->ms.sym->namelen; | 108 | hists__calc_col_len(self, h); |
57 | ++self->nr_entries; | 109 | ++self->nr_entries; |
110 | } | ||
111 | } | ||
112 | |||
113 | static u8 symbol__parent_filter(const struct symbol *parent) | ||
114 | { | ||
115 | if (symbol_conf.exclude_other && parent == NULL) | ||
116 | return 1 << HIST_FILTER__PARENT; | ||
117 | return 0; | ||
58 | } | 118 | } |
59 | 119 | ||
60 | struct hist_entry *__hists__add_entry(struct hists *self, | 120 | struct hist_entry *__hists__add_entry(struct hists *self, |
@@ -70,10 +130,12 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
70 | .map = al->map, | 130 | .map = al->map, |
71 | .sym = al->sym, | 131 | .sym = al->sym, |
72 | }, | 132 | }, |
133 | .cpu = al->cpu, | ||
73 | .ip = al->addr, | 134 | .ip = al->addr, |
74 | .level = al->level, | 135 | .level = al->level, |
75 | .period = period, | 136 | .period = period, |
76 | .parent = sym_parent, | 137 | .parent = sym_parent, |
138 | .filtered = symbol__parent_filter(sym_parent), | ||
77 | }; | 139 | }; |
78 | int cmp; | 140 | int cmp; |
79 | 141 | ||
@@ -191,7 +253,7 @@ void hists__collapse_resort(struct hists *self) | |||
191 | tmp = RB_ROOT; | 253 | tmp = RB_ROOT; |
192 | next = rb_first(&self->entries); | 254 | next = rb_first(&self->entries); |
193 | self->nr_entries = 0; | 255 | self->nr_entries = 0; |
194 | self->max_sym_namelen = 0; | 256 | hists__reset_col_len(self); |
195 | 257 | ||
196 | while (next) { | 258 | while (next) { |
197 | n = rb_entry(next, struct hist_entry, rb_node); | 259 | n = rb_entry(next, struct hist_entry, rb_node); |
@@ -248,7 +310,7 @@ void hists__output_resort(struct hists *self) | |||
248 | next = rb_first(&self->entries); | 310 | next = rb_first(&self->entries); |
249 | 311 | ||
250 | self->nr_entries = 0; | 312 | self->nr_entries = 0; |
251 | self->max_sym_namelen = 0; | 313 | hists__reset_col_len(self); |
252 | 314 | ||
253 | while (next) { | 315 | while (next) { |
254 | n = rb_entry(next, struct hist_entry, rb_node); | 316 | n = rb_entry(next, struct hist_entry, rb_node); |
@@ -515,8 +577,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
515 | } | 577 | } |
516 | 578 | ||
517 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | 579 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, |
518 | struct hists *pair_hists, bool show_displacement, | 580 | struct hists *hists, struct hists *pair_hists, |
519 | long displacement, bool color, u64 session_total) | 581 | bool show_displacement, long displacement, |
582 | bool color, u64 session_total) | ||
520 | { | 583 | { |
521 | struct sort_entry *se; | 584 | struct sort_entry *se; |
522 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 585 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
@@ -620,29 +683,25 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
620 | 683 | ||
621 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); | 684 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); |
622 | ret += se->se_snprintf(self, s + ret, size - ret, | 685 | ret += se->se_snprintf(self, s + ret, size - ret, |
623 | se->se_width ? *se->se_width : 0); | 686 | hists__col_len(hists, se->se_width_idx)); |
624 | } | 687 | } |
625 | 688 | ||
626 | return ret; | 689 | return ret; |
627 | } | 690 | } |
628 | 691 | ||
629 | int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, | 692 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, |
630 | bool show_displacement, long displacement, FILE *fp, | 693 | struct hists *pair_hists, bool show_displacement, |
631 | u64 session_total) | 694 | long displacement, FILE *fp, u64 session_total) |
632 | { | 695 | { |
633 | char bf[512]; | 696 | char bf[512]; |
634 | int ret; | 697 | hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, |
635 | 698 | show_displacement, displacement, | |
636 | ret = hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, | 699 | true, session_total); |
637 | show_displacement, displacement, | ||
638 | true, session_total); | ||
639 | if (!ret) | ||
640 | return 0; | ||
641 | |||
642 | return fprintf(fp, "%s\n", bf); | 700 | return fprintf(fp, "%s\n", bf); |
643 | } | 701 | } |
644 | 702 | ||
645 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, | 703 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, |
704 | struct hists *hists, FILE *fp, | ||
646 | u64 session_total) | 705 | u64 session_total) |
647 | { | 706 | { |
648 | int left_margin = 0; | 707 | int left_margin = 0; |
@@ -650,7 +709,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, | |||
650 | if (sort__first_dimension == SORT_COMM) { | 709 | if (sort__first_dimension == SORT_COMM) { |
651 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, | 710 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, |
652 | typeof(*se), list); | 711 | typeof(*se), list); |
653 | left_margin = se->se_width ? *se->se_width : 0; | 712 | left_margin = hists__col_len(hists, se->se_width_idx); |
654 | left_margin -= thread__comm_len(self->thread); | 713 | left_margin -= thread__comm_len(self->thread); |
655 | } | 714 | } |
656 | 715 | ||
@@ -721,17 +780,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
721 | continue; | 780 | continue; |
722 | } | 781 | } |
723 | width = strlen(se->se_header); | 782 | width = strlen(se->se_header); |
724 | if (se->se_width) { | 783 | if (symbol_conf.col_width_list_str) { |
725 | if (symbol_conf.col_width_list_str) { | 784 | if (col_width) { |
726 | if (col_width) { | 785 | hists__set_col_len(self, se->se_width_idx, |
727 | *se->se_width = atoi(col_width); | 786 | atoi(col_width)); |
728 | col_width = strchr(col_width, ','); | 787 | col_width = strchr(col_width, ','); |
729 | if (col_width) | 788 | if (col_width) |
730 | ++col_width; | 789 | ++col_width; |
731 | } | ||
732 | } | 790 | } |
733 | width = *se->se_width = max(*se->se_width, width); | ||
734 | } | 791 | } |
792 | if (!hists__new_col_len(self, se->se_width_idx, width)) | ||
793 | width = hists__col_len(self, se->se_width_idx); | ||
735 | fprintf(fp, " %*s", width, se->se_header); | 794 | fprintf(fp, " %*s", width, se->se_header); |
736 | } | 795 | } |
737 | fprintf(fp, "\n"); | 796 | fprintf(fp, "\n"); |
@@ -754,9 +813,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
754 | continue; | 813 | continue; |
755 | 814 | ||
756 | fprintf(fp, " "); | 815 | fprintf(fp, " "); |
757 | if (se->se_width) | 816 | width = hists__col_len(self, se->se_width_idx); |
758 | width = *se->se_width; | 817 | if (width == 0) |
759 | else | ||
760 | width = strlen(se->se_header); | 818 | width = strlen(se->se_header); |
761 | for (i = 0; i < width; i++) | 819 | for (i = 0; i < width; i++) |
762 | fprintf(fp, "."); | 820 | fprintf(fp, "."); |
@@ -767,7 +825,6 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
767 | print_entries: | 825 | print_entries: |
768 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 826 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
769 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 827 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
770 | int cnt; | ||
771 | 828 | ||
772 | if (show_displacement) { | 829 | if (show_displacement) { |
773 | if (h->pair != NULL) | 830 | if (h->pair != NULL) |
@@ -777,17 +834,12 @@ print_entries: | |||
777 | displacement = 0; | 834 | displacement = 0; |
778 | ++position; | 835 | ++position; |
779 | } | 836 | } |
780 | cnt = hist_entry__fprintf(h, pair, show_displacement, | 837 | ret += hist_entry__fprintf(h, self, pair, show_displacement, |
781 | displacement, fp, self->stats.total_period); | 838 | displacement, fp, self->stats.total_period); |
782 | /* Ignore those that didn't match the parent filter */ | ||
783 | if (!cnt) | ||
784 | continue; | ||
785 | |||
786 | ret += cnt; | ||
787 | 839 | ||
788 | if (symbol_conf.use_callchain) | 840 | if (symbol_conf.use_callchain) |
789 | ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); | 841 | ret += hist_entry__fprintf_callchain(h, self, fp, |
790 | 842 | self->stats.total_period); | |
791 | if (h->ms.map == NULL && verbose > 1) { | 843 | if (h->ms.map == NULL && verbose > 1) { |
792 | __map_groups__fprintf_maps(&h->thread->mg, | 844 | __map_groups__fprintf_maps(&h->thread->mg, |
793 | MAP__FUNCTION, verbose, fp); | 845 | MAP__FUNCTION, verbose, fp); |
@@ -800,10 +852,49 @@ print_entries: | |||
800 | return ret; | 852 | return ret; |
801 | } | 853 | } |
802 | 854 | ||
803 | enum hist_filter { | 855 | /* |
804 | HIST_FILTER__DSO, | 856 | * See hists__fprintf to match the column widths |
805 | HIST_FILTER__THREAD, | 857 | */ |
806 | }; | 858 | unsigned int hists__sort_list_width(struct hists *self) |
859 | { | ||
860 | struct sort_entry *se; | ||
861 | int ret = 9; /* total % */ | ||
862 | |||
863 | if (symbol_conf.show_cpu_utilization) { | ||
864 | ret += 7; /* count_sys % */ | ||
865 | ret += 6; /* count_us % */ | ||
866 | if (perf_guest) { | ||
867 | ret += 13; /* count_guest_sys % */ | ||
868 | ret += 12; /* count_guest_us % */ | ||
869 | } | ||
870 | } | ||
871 | |||
872 | if (symbol_conf.show_nr_samples) | ||
873 | ret += 11; | ||
874 | |||
875 | list_for_each_entry(se, &hist_entry__sort_list, list) | ||
876 | if (!se->elide) | ||
877 | ret += 2 + hists__col_len(self, se->se_width_idx); | ||
878 | |||
879 | return ret; | ||
880 | } | ||
881 | |||
882 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | ||
883 | enum hist_filter filter) | ||
884 | { | ||
885 | h->filtered &= ~(1 << filter); | ||
886 | if (h->filtered) | ||
887 | return; | ||
888 | |||
889 | ++self->nr_entries; | ||
890 | if (h->ms.unfolded) | ||
891 | self->nr_entries += h->nr_rows; | ||
892 | h->row_offset = 0; | ||
893 | self->stats.total_period += h->period; | ||
894 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
895 | |||
896 | hists__calc_col_len(self, h); | ||
897 | } | ||
807 | 898 | ||
808 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | 899 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) |
809 | { | 900 | { |
@@ -811,7 +902,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |||
811 | 902 | ||
812 | self->nr_entries = self->stats.total_period = 0; | 903 | self->nr_entries = self->stats.total_period = 0; |
813 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 904 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
814 | self->max_sym_namelen = 0; | 905 | hists__reset_col_len(self); |
815 | 906 | ||
816 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 907 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
817 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 908 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -824,15 +915,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |||
824 | continue; | 915 | continue; |
825 | } | 916 | } |
826 | 917 | ||
827 | h->filtered &= ~(1 << HIST_FILTER__DSO); | 918 | hists__remove_entry_filter(self, h, HIST_FILTER__DSO); |
828 | if (!h->filtered) { | ||
829 | ++self->nr_entries; | ||
830 | self->stats.total_period += h->period; | ||
831 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
832 | if (h->ms.sym && | ||
833 | self->max_sym_namelen < h->ms.sym->namelen) | ||
834 | self->max_sym_namelen = h->ms.sym->namelen; | ||
835 | } | ||
836 | } | 919 | } |
837 | } | 920 | } |
838 | 921 | ||
@@ -842,7 +925,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
842 | 925 | ||
843 | self->nr_entries = self->stats.total_period = 0; | 926 | self->nr_entries = self->stats.total_period = 0; |
844 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 927 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
845 | self->max_sym_namelen = 0; | 928 | hists__reset_col_len(self); |
846 | 929 | ||
847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 930 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 931 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -851,15 +934,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
851 | h->filtered |= (1 << HIST_FILTER__THREAD); | 934 | h->filtered |= (1 << HIST_FILTER__THREAD); |
852 | continue; | 935 | continue; |
853 | } | 936 | } |
854 | h->filtered &= ~(1 << HIST_FILTER__THREAD); | 937 | |
855 | if (!h->filtered) { | 938 | hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); |
856 | ++self->nr_entries; | ||
857 | self->stats.total_period += h->period; | ||
858 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
859 | if (h->ms.sym && | ||
860 | self->max_sym_namelen < h->ms.sym->namelen) | ||
861 | self->max_sym_namelen = h->ms.sym->namelen; | ||
862 | } | ||
863 | } | 939 | } |
864 | } | 940 | } |
865 | 941 | ||
@@ -1052,7 +1128,7 @@ fallback: | |||
1052 | dso, dso->long_name, sym, sym->name); | 1128 | dso, dso->long_name, sym, sym->name); |
1053 | 1129 | ||
1054 | snprintf(command, sizeof(command), | 1130 | snprintf(command, sizeof(command), |
1055 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", | 1131 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", |
1056 | map__rip_2objdump(map, sym->start), | 1132 | map__rip_2objdump(map, sym->start), |
1057 | map__rip_2objdump(map, sym->end), | 1133 | map__rip_2objdump(map, sym->end), |
1058 | filename, filename); | 1134 | filename, filename); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 83fa33a7b38b..65a48db46a29 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -56,6 +56,16 @@ struct events_stats { | |||
56 | u32 nr_unknown_events; | 56 | u32 nr_unknown_events; |
57 | }; | 57 | }; |
58 | 58 | ||
59 | enum hist_column { | ||
60 | HISTC_SYMBOL, | ||
61 | HISTC_DSO, | ||
62 | HISTC_THREAD, | ||
63 | HISTC_COMM, | ||
64 | HISTC_PARENT, | ||
65 | HISTC_CPU, | ||
66 | HISTC_NR_COLS, /* Last entry */ | ||
67 | }; | ||
68 | |||
59 | struct hists { | 69 | struct hists { |
60 | struct rb_node rb_node; | 70 | struct rb_node rb_node; |
61 | struct rb_root entries; | 71 | struct rb_root entries; |
@@ -64,7 +74,7 @@ struct hists { | |||
64 | u64 config; | 74 | u64 config; |
65 | u64 event_stream; | 75 | u64 event_stream; |
66 | u32 type; | 76 | u32 type; |
67 | u32 max_sym_namelen; | 77 | u16 col_len[HISTC_NR_COLS]; |
68 | }; | 78 | }; |
69 | 79 | ||
70 | struct hist_entry *__hists__add_entry(struct hists *self, | 80 | struct hist_entry *__hists__add_entry(struct hists *self, |
@@ -72,12 +82,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
72 | struct symbol *parent, u64 period); | 82 | struct symbol *parent, u64 period); |
73 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 83 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); |
74 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 84 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
75 | int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, | 85 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, |
76 | bool show_displacement, long displacement, FILE *fp, | 86 | struct hists *pair_hists, bool show_displacement, |
77 | u64 total); | 87 | long displacement, FILE *fp, u64 total); |
78 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | 88 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, |
79 | struct hists *pair_hists, bool show_displacement, | 89 | struct hists *hists, struct hists *pair_hists, |
80 | long displacement, bool color, u64 total); | 90 | bool show_displacement, long displacement, |
91 | bool color, u64 total); | ||
81 | void hist_entry__free(struct hist_entry *); | 92 | void hist_entry__free(struct hist_entry *); |
82 | 93 | ||
83 | void hists__output_resort(struct hists *self); | 94 | void hists__output_resort(struct hists *self); |
@@ -95,6 +106,10 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head); | |||
95 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | 106 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); |
96 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | 107 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); |
97 | 108 | ||
109 | u16 hists__col_len(struct hists *self, enum hist_column col); | ||
110 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | ||
111 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | ||
112 | |||
98 | #ifdef NO_NEWT_SUPPORT | 113 | #ifdef NO_NEWT_SUPPORT |
99 | static inline int hists__browse(struct hists *self __used, | 114 | static inline int hists__browse(struct hists *self __used, |
100 | const char *helpline __used, | 115 | const char *helpline __used, |
@@ -126,4 +141,7 @@ int hist_entry__tui_annotate(struct hist_entry *self); | |||
126 | 141 | ||
127 | int hists__tui_browse_tree(struct rb_root *self, const char *help); | 142 | int hists__tui_browse_tree(struct rb_root *self, const char *help); |
128 | #endif | 143 | #endif |
144 | |||
145 | unsigned int hists__sort_list_width(struct hists *self); | ||
146 | |||
129 | #endif /* __PERF_HIST_H */ | 147 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index e672f2fef65b..3a7eb6ec0eec 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -17,16 +17,6 @@ static inline int is_anon_memory(const char *filename) | |||
17 | return strcmp(filename, "//anon") == 0; | 17 | return strcmp(filename, "//anon") == 0; |
18 | } | 18 | } |
19 | 19 | ||
20 | static int strcommon(const char *pathname, char *cwd, int cwdlen) | ||
21 | { | ||
22 | int n = 0; | ||
23 | |||
24 | while (n < cwdlen && pathname[n] == cwd[n]) | ||
25 | ++n; | ||
26 | |||
27 | return n; | ||
28 | } | ||
29 | |||
30 | void map__init(struct map *self, enum map_type type, | 20 | void map__init(struct map *self, enum map_type type, |
31 | u64 start, u64 end, u64 pgoff, struct dso *dso) | 21 | u64 start, u64 end, u64 pgoff, struct dso *dso) |
32 | { | 22 | { |
@@ -39,11 +29,12 @@ void map__init(struct map *self, enum map_type type, | |||
39 | self->unmap_ip = map__unmap_ip; | 29 | self->unmap_ip = map__unmap_ip; |
40 | RB_CLEAR_NODE(&self->rb_node); | 30 | RB_CLEAR_NODE(&self->rb_node); |
41 | self->groups = NULL; | 31 | self->groups = NULL; |
32 | self->referenced = false; | ||
42 | } | 33 | } |
43 | 34 | ||
44 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 35 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
45 | u64 pgoff, u32 pid, char *filename, | 36 | u64 pgoff, u32 pid, char *filename, |
46 | enum map_type type, char *cwd, int cwdlen) | 37 | enum map_type type) |
47 | { | 38 | { |
48 | struct map *self = malloc(sizeof(*self)); | 39 | struct map *self = malloc(sizeof(*self)); |
49 | 40 | ||
@@ -52,16 +43,6 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
52 | struct dso *dso; | 43 | struct dso *dso; |
53 | int anon; | 44 | int anon; |
54 | 45 | ||
55 | if (cwd) { | ||
56 | int n = strcommon(filename, cwd, cwdlen); | ||
57 | |||
58 | if (n == cwdlen) { | ||
59 | snprintf(newfilename, sizeof(newfilename), | ||
60 | ".%s", filename + n); | ||
61 | filename = newfilename; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | anon = is_anon_memory(filename); | 46 | anon = is_anon_memory(filename); |
66 | 47 | ||
67 | if (anon) { | 48 | if (anon) { |
@@ -248,6 +229,39 @@ void map_groups__init(struct map_groups *self) | |||
248 | self->machine = NULL; | 229 | self->machine = NULL; |
249 | } | 230 | } |
250 | 231 | ||
232 | static void maps__delete(struct rb_root *self) | ||
233 | { | ||
234 | struct rb_node *next = rb_first(self); | ||
235 | |||
236 | while (next) { | ||
237 | struct map *pos = rb_entry(next, struct map, rb_node); | ||
238 | |||
239 | next = rb_next(&pos->rb_node); | ||
240 | rb_erase(&pos->rb_node, self); | ||
241 | map__delete(pos); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | static void maps__delete_removed(struct list_head *self) | ||
246 | { | ||
247 | struct map *pos, *n; | ||
248 | |||
249 | list_for_each_entry_safe(pos, n, self, node) { | ||
250 | list_del(&pos->node); | ||
251 | map__delete(pos); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | void map_groups__exit(struct map_groups *self) | ||
256 | { | ||
257 | int i; | ||
258 | |||
259 | for (i = 0; i < MAP__NR_TYPES; ++i) { | ||
260 | maps__delete(&self->maps[i]); | ||
261 | maps__delete_removed(&self->removed_maps[i]); | ||
262 | } | ||
263 | } | ||
264 | |||
251 | void map_groups__flush(struct map_groups *self) | 265 | void map_groups__flush(struct map_groups *self) |
252 | { | 266 | { |
253 | int type; | 267 | int type; |
@@ -374,6 +388,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
374 | { | 388 | { |
375 | struct rb_root *root = &self->maps[map->type]; | 389 | struct rb_root *root = &self->maps[map->type]; |
376 | struct rb_node *next = rb_first(root); | 390 | struct rb_node *next = rb_first(root); |
391 | int err = 0; | ||
377 | 392 | ||
378 | while (next) { | 393 | while (next) { |
379 | struct map *pos = rb_entry(next, struct map, rb_node); | 394 | struct map *pos = rb_entry(next, struct map, rb_node); |
@@ -390,20 +405,16 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
390 | 405 | ||
391 | rb_erase(&pos->rb_node, root); | 406 | rb_erase(&pos->rb_node, root); |
392 | /* | 407 | /* |
393 | * We may have references to this map, for instance in some | ||
394 | * hist_entry instances, so just move them to a separate | ||
395 | * list. | ||
396 | */ | ||
397 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | ||
398 | /* | ||
399 | * Now check if we need to create new maps for areas not | 408 | * Now check if we need to create new maps for areas not |
400 | * overlapped by the new map: | 409 | * overlapped by the new map: |
401 | */ | 410 | */ |
402 | if (map->start > pos->start) { | 411 | if (map->start > pos->start) { |
403 | struct map *before = map__clone(pos); | 412 | struct map *before = map__clone(pos); |
404 | 413 | ||
405 | if (before == NULL) | 414 | if (before == NULL) { |
406 | return -ENOMEM; | 415 | err = -ENOMEM; |
416 | goto move_map; | ||
417 | } | ||
407 | 418 | ||
408 | before->end = map->start - 1; | 419 | before->end = map->start - 1; |
409 | map_groups__insert(self, before); | 420 | map_groups__insert(self, before); |
@@ -414,14 +425,27 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
414 | if (map->end < pos->end) { | 425 | if (map->end < pos->end) { |
415 | struct map *after = map__clone(pos); | 426 | struct map *after = map__clone(pos); |
416 | 427 | ||
417 | if (after == NULL) | 428 | if (after == NULL) { |
418 | return -ENOMEM; | 429 | err = -ENOMEM; |
430 | goto move_map; | ||
431 | } | ||
419 | 432 | ||
420 | after->start = map->end + 1; | 433 | after->start = map->end + 1; |
421 | map_groups__insert(self, after); | 434 | map_groups__insert(self, after); |
422 | if (verbose >= 2) | 435 | if (verbose >= 2) |
423 | map__fprintf(after, fp); | 436 | map__fprintf(after, fp); |
424 | } | 437 | } |
438 | move_map: | ||
439 | /* | ||
440 | * If we have references, just move them to a separate list. | ||
441 | */ | ||
442 | if (pos->referenced) | ||
443 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | ||
444 | else | ||
445 | map__delete(pos); | ||
446 | |||
447 | if (err) | ||
448 | return err; | ||
425 | } | 449 | } |
426 | 450 | ||
427 | return 0; | 451 | return 0; |
@@ -493,6 +517,11 @@ void maps__insert(struct rb_root *maps, struct map *map) | |||
493 | rb_insert_color(&map->rb_node, maps); | 517 | rb_insert_color(&map->rb_node, maps); |
494 | } | 518 | } |
495 | 519 | ||
520 | void maps__remove(struct rb_root *self, struct map *map) | ||
521 | { | ||
522 | rb_erase(&map->rb_node, self); | ||
523 | } | ||
524 | |||
496 | struct map *maps__find(struct rb_root *maps, u64 ip) | 525 | struct map *maps__find(struct rb_root *maps, u64 ip) |
497 | { | 526 | { |
498 | struct rb_node **p = &maps->rb_node; | 527 | struct rb_node **p = &maps->rb_node; |
@@ -526,6 +555,31 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid) | |||
526 | return self->root_dir == NULL ? -ENOMEM : 0; | 555 | return self->root_dir == NULL ? -ENOMEM : 0; |
527 | } | 556 | } |
528 | 557 | ||
558 | static void dsos__delete(struct list_head *self) | ||
559 | { | ||
560 | struct dso *pos, *n; | ||
561 | |||
562 | list_for_each_entry_safe(pos, n, self, node) { | ||
563 | list_del(&pos->node); | ||
564 | dso__delete(pos); | ||
565 | } | ||
566 | } | ||
567 | |||
568 | void machine__exit(struct machine *self) | ||
569 | { | ||
570 | map_groups__exit(&self->kmaps); | ||
571 | dsos__delete(&self->user_dsos); | ||
572 | dsos__delete(&self->kernel_dsos); | ||
573 | free(self->root_dir); | ||
574 | self->root_dir = NULL; | ||
575 | } | ||
576 | |||
577 | void machine__delete(struct machine *self) | ||
578 | { | ||
579 | machine__exit(self); | ||
580 | free(self); | ||
581 | } | ||
582 | |||
529 | struct machine *machines__add(struct rb_root *self, pid_t pid, | 583 | struct machine *machines__add(struct rb_root *self, pid_t pid, |
530 | const char *root_dir) | 584 | const char *root_dir) |
531 | { | 585 | { |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f39134512829..78575796d5f3 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -29,7 +29,8 @@ struct map { | |||
29 | }; | 29 | }; |
30 | u64 start; | 30 | u64 start; |
31 | u64 end; | 31 | u64 end; |
32 | enum map_type type; | 32 | u8 /* enum map_type */ type; |
33 | bool referenced; | ||
33 | u32 priv; | 34 | u32 priv; |
34 | u64 pgoff; | 35 | u64 pgoff; |
35 | 36 | ||
@@ -106,7 +107,7 @@ void map__init(struct map *self, enum map_type type, | |||
106 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 107 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
107 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 108 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
108 | u64 pgoff, u32 pid, char *filename, | 109 | u64 pgoff, u32 pid, char *filename, |
109 | enum map_type type, char *cwd, int cwdlen); | 110 | enum map_type type); |
110 | void map__delete(struct map *self); | 111 | void map__delete(struct map *self); |
111 | struct map *map__clone(struct map *self); | 112 | struct map *map__clone(struct map *self); |
112 | int map__overlap(struct map *l, struct map *r); | 113 | int map__overlap(struct map *l, struct map *r); |
@@ -125,8 +126,10 @@ void map__reloc_vmlinux(struct map *self); | |||
125 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 126 | size_t __map_groups__fprintf_maps(struct map_groups *self, |
126 | enum map_type type, int verbose, FILE *fp); | 127 | enum map_type type, int verbose, FILE *fp); |
127 | void maps__insert(struct rb_root *maps, struct map *map); | 128 | void maps__insert(struct rb_root *maps, struct map *map); |
129 | void maps__remove(struct rb_root *self, struct map *map); | ||
128 | struct map *maps__find(struct rb_root *maps, u64 addr); | 130 | struct map *maps__find(struct rb_root *maps, u64 addr); |
129 | void map_groups__init(struct map_groups *self); | 131 | void map_groups__init(struct map_groups *self); |
132 | void map_groups__exit(struct map_groups *self); | ||
130 | int map_groups__clone(struct map_groups *self, | 133 | int map_groups__clone(struct map_groups *self, |
131 | struct map_groups *parent, enum map_type type); | 134 | struct map_groups *parent, enum map_type type); |
132 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); | 135 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); |
@@ -142,6 +145,8 @@ struct machine *machines__find(struct rb_root *self, pid_t pid); | |||
142 | struct machine *machines__findnew(struct rb_root *self, pid_t pid); | 145 | struct machine *machines__findnew(struct rb_root *self, pid_t pid); |
143 | char *machine__mmap_name(struct machine *self, char *bf, size_t size); | 146 | char *machine__mmap_name(struct machine *self, char *bf, size_t size); |
144 | int machine__init(struct machine *self, const char *root_dir, pid_t pid); | 147 | int machine__init(struct machine *self, const char *root_dir, pid_t pid); |
148 | void machine__exit(struct machine *self); | ||
149 | void machine__delete(struct machine *self); | ||
145 | 150 | ||
146 | /* | 151 | /* |
147 | * Default guest kernel is defined by parameter --guestkallsyms | 152 | * Default guest kernel is defined by parameter --guestkallsyms |
@@ -163,6 +168,11 @@ static inline void map_groups__insert(struct map_groups *self, struct map *map) | |||
163 | map->groups = self; | 168 | map->groups = self; |
164 | } | 169 | } |
165 | 170 | ||
171 | static inline void map_groups__remove(struct map_groups *self, struct map *map) | ||
172 | { | ||
173 | maps__remove(&self->maps[map->type], map); | ||
174 | } | ||
175 | |||
166 | static inline struct map *map_groups__find(struct map_groups *self, | 176 | static inline struct map *map_groups__find(struct map_groups *self, |
167 | enum map_type type, u64 addr) | 177 | enum map_type type, u64 addr) |
168 | { | 178 | { |
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 7537ca15900b..91de99b58445 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG | 11 | #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG |
12 | #endif | 12 | #endif |
13 | #include <slang.h> | 13 | #include <slang.h> |
14 | #include <signal.h> | ||
14 | #include <stdlib.h> | 15 | #include <stdlib.h> |
15 | #include <newt.h> | 16 | #include <newt.h> |
16 | #include <sys/ttydefaults.h> | 17 | #include <sys/ttydefaults.h> |
@@ -278,9 +279,48 @@ struct ui_browser { | |||
278 | void *first_visible_entry, *entries; | 279 | void *first_visible_entry, *entries; |
279 | u16 top, left, width, height; | 280 | u16 top, left, width, height; |
280 | void *priv; | 281 | void *priv; |
282 | unsigned int (*refresh_entries)(struct ui_browser *self); | ||
283 | void (*seek)(struct ui_browser *self, | ||
284 | off_t offset, int whence); | ||
281 | u32 nr_entries; | 285 | u32 nr_entries; |
282 | }; | 286 | }; |
283 | 287 | ||
288 | static void ui_browser__list_head_seek(struct ui_browser *self, | ||
289 | off_t offset, int whence) | ||
290 | { | ||
291 | struct list_head *head = self->entries; | ||
292 | struct list_head *pos; | ||
293 | |||
294 | switch (whence) { | ||
295 | case SEEK_SET: | ||
296 | pos = head->next; | ||
297 | break; | ||
298 | case SEEK_CUR: | ||
299 | pos = self->first_visible_entry; | ||
300 | break; | ||
301 | case SEEK_END: | ||
302 | pos = head->prev; | ||
303 | break; | ||
304 | default: | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | if (offset > 0) { | ||
309 | while (offset-- != 0) | ||
310 | pos = pos->next; | ||
311 | } else { | ||
312 | while (offset++ != 0) | ||
313 | pos = pos->prev; | ||
314 | } | ||
315 | |||
316 | self->first_visible_entry = pos; | ||
317 | } | ||
318 | |||
319 | static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) | ||
320 | { | ||
321 | return (self->first_visible_entry_idx + row) == self->index; | ||
322 | } | ||
323 | |||
284 | static void ui_browser__refresh_dimensions(struct ui_browser *self) | 324 | static void ui_browser__refresh_dimensions(struct ui_browser *self) |
285 | { | 325 | { |
286 | int cols, rows; | 326 | int cols, rows; |
@@ -297,8 +337,36 @@ static void ui_browser__refresh_dimensions(struct ui_browser *self) | |||
297 | 337 | ||
298 | static void ui_browser__reset_index(struct ui_browser *self) | 338 | static void ui_browser__reset_index(struct ui_browser *self) |
299 | { | 339 | { |
300 | self->index = self->first_visible_entry_idx = 0; | 340 | self->index = self->first_visible_entry_idx = 0; |
301 | self->first_visible_entry = NULL; | 341 | self->seek(self, 0, SEEK_SET); |
342 | } | ||
343 | |||
344 | static int ui_browser__show(struct ui_browser *self, const char *title) | ||
345 | { | ||
346 | if (self->form != NULL) { | ||
347 | newtFormDestroy(self->form); | ||
348 | newtPopWindow(); | ||
349 | } | ||
350 | ui_browser__refresh_dimensions(self); | ||
351 | newtCenteredWindow(self->width, self->height, title); | ||
352 | self->form = newt_form__new(); | ||
353 | if (self->form == NULL) | ||
354 | return -1; | ||
355 | |||
356 | self->sb = newtVerticalScrollbar(self->width, 0, self->height, | ||
357 | HE_COLORSET_NORMAL, | ||
358 | HE_COLORSET_SELECTED); | ||
359 | if (self->sb == NULL) | ||
360 | return -1; | ||
361 | |||
362 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | ||
363 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | ||
364 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | ||
365 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | ||
366 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | ||
367 | newtFormAddHotKey(self->form, NEWT_KEY_END); | ||
368 | newtFormAddComponent(self->form, self->sb); | ||
369 | return 0; | ||
302 | } | 370 | } |
303 | 371 | ||
304 | static int objdump_line__show(struct objdump_line *self, struct list_head *head, | 372 | static int objdump_line__show(struct objdump_line *self, struct list_head *head, |
@@ -352,26 +420,10 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, | |||
352 | 420 | ||
353 | static int ui_browser__refresh_entries(struct ui_browser *self) | 421 | static int ui_browser__refresh_entries(struct ui_browser *self) |
354 | { | 422 | { |
355 | struct objdump_line *pos; | 423 | int row; |
356 | struct list_head *head = self->entries; | ||
357 | struct hist_entry *he = self->priv; | ||
358 | int row = 0; | ||
359 | int len = he->ms.sym->end - he->ms.sym->start; | ||
360 | |||
361 | if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) | ||
362 | self->first_visible_entry = head->next; | ||
363 | |||
364 | pos = list_entry(self->first_visible_entry, struct objdump_line, node); | ||
365 | |||
366 | list_for_each_entry_from(pos, head, node) { | ||
367 | bool current_entry = (self->first_visible_entry_idx + row) == self->index; | ||
368 | SLsmg_gotorc(self->top + row, self->left); | ||
369 | objdump_line__show(pos, head, self->width, | ||
370 | he, len, current_entry); | ||
371 | if (++row == self->height) | ||
372 | break; | ||
373 | } | ||
374 | 424 | ||
425 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | ||
426 | row = self->refresh_entries(self); | ||
375 | SLsmg_set_color(HE_COLORSET_NORMAL); | 427 | SLsmg_set_color(HE_COLORSET_NORMAL); |
376 | SLsmg_fill_region(self->top + row, self->left, | 428 | SLsmg_fill_region(self->top + row, self->left, |
377 | self->height - row, self->width, ' '); | 429 | self->height - row, self->width, ' '); |
@@ -379,42 +431,13 @@ static int ui_browser__refresh_entries(struct ui_browser *self) | |||
379 | return 0; | 431 | return 0; |
380 | } | 432 | } |
381 | 433 | ||
382 | static int ui_browser__run(struct ui_browser *self, const char *title, | 434 | static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) |
383 | struct newtExitStruct *es) | ||
384 | { | 435 | { |
385 | if (self->form) { | ||
386 | newtFormDestroy(self->form); | ||
387 | newtPopWindow(); | ||
388 | } | ||
389 | |||
390 | ui_browser__refresh_dimensions(self); | ||
391 | newtCenteredWindow(self->width + 2, self->height, title); | ||
392 | self->form = newt_form__new(); | ||
393 | if (self->form == NULL) | ||
394 | return -1; | ||
395 | |||
396 | self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, | ||
397 | HE_COLORSET_NORMAL, | ||
398 | HE_COLORSET_SELECTED); | ||
399 | if (self->sb == NULL) | ||
400 | return -1; | ||
401 | |||
402 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | ||
403 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | ||
404 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | ||
405 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | ||
406 | newtFormAddHotKey(self->form, ' '); | ||
407 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | ||
408 | newtFormAddHotKey(self->form, NEWT_KEY_END); | ||
409 | newtFormAddHotKey(self->form, NEWT_KEY_TAB); | ||
410 | newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); | ||
411 | |||
412 | if (ui_browser__refresh_entries(self) < 0) | 436 | if (ui_browser__refresh_entries(self) < 0) |
413 | return -1; | 437 | return -1; |
414 | newtFormAddComponent(self->form, self->sb); | ||
415 | 438 | ||
416 | while (1) { | 439 | while (1) { |
417 | unsigned int offset; | 440 | off_t offset; |
418 | 441 | ||
419 | newtFormRun(self->form, es); | 442 | newtFormRun(self->form, es); |
420 | 443 | ||
@@ -428,9 +451,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
428 | break; | 451 | break; |
429 | ++self->index; | 452 | ++self->index; |
430 | if (self->index == self->first_visible_entry_idx + self->height) { | 453 | if (self->index == self->first_visible_entry_idx + self->height) { |
431 | struct list_head *pos = self->first_visible_entry; | ||
432 | ++self->first_visible_entry_idx; | 454 | ++self->first_visible_entry_idx; |
433 | self->first_visible_entry = pos->next; | 455 | self->seek(self, +1, SEEK_CUR); |
434 | } | 456 | } |
435 | break; | 457 | break; |
436 | case NEWT_KEY_UP: | 458 | case NEWT_KEY_UP: |
@@ -438,9 +460,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
438 | break; | 460 | break; |
439 | --self->index; | 461 | --self->index; |
440 | if (self->index < self->first_visible_entry_idx) { | 462 | if (self->index < self->first_visible_entry_idx) { |
441 | struct list_head *pos = self->first_visible_entry; | ||
442 | --self->first_visible_entry_idx; | 463 | --self->first_visible_entry_idx; |
443 | self->first_visible_entry = pos->prev; | 464 | self->seek(self, -1, SEEK_CUR); |
444 | } | 465 | } |
445 | break; | 466 | break; |
446 | case NEWT_KEY_PGDN: | 467 | case NEWT_KEY_PGDN: |
@@ -453,12 +474,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
453 | offset = self->nr_entries - 1 - self->index; | 474 | offset = self->nr_entries - 1 - self->index; |
454 | self->index += offset; | 475 | self->index += offset; |
455 | self->first_visible_entry_idx += offset; | 476 | self->first_visible_entry_idx += offset; |
456 | 477 | self->seek(self, +offset, SEEK_CUR); | |
457 | while (offset--) { | ||
458 | struct list_head *pos = self->first_visible_entry; | ||
459 | self->first_visible_entry = pos->next; | ||
460 | } | ||
461 | |||
462 | break; | 478 | break; |
463 | case NEWT_KEY_PGUP: | 479 | case NEWT_KEY_PGUP: |
464 | if (self->first_visible_entry_idx == 0) | 480 | if (self->first_visible_entry_idx == 0) |
@@ -471,36 +487,22 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
471 | 487 | ||
472 | self->index -= offset; | 488 | self->index -= offset; |
473 | self->first_visible_entry_idx -= offset; | 489 | self->first_visible_entry_idx -= offset; |
474 | 490 | self->seek(self, -offset, SEEK_CUR); | |
475 | while (offset--) { | ||
476 | struct list_head *pos = self->first_visible_entry; | ||
477 | self->first_visible_entry = pos->prev; | ||
478 | } | ||
479 | break; | 491 | break; |
480 | case NEWT_KEY_HOME: | 492 | case NEWT_KEY_HOME: |
481 | ui_browser__reset_index(self); | 493 | ui_browser__reset_index(self); |
482 | break; | 494 | break; |
483 | case NEWT_KEY_END: { | 495 | case NEWT_KEY_END: |
484 | struct list_head *head = self->entries; | ||
485 | offset = self->height - 1; | 496 | offset = self->height - 1; |
497 | if (offset >= self->nr_entries) | ||
498 | offset = self->nr_entries - 1; | ||
486 | 499 | ||
487 | if (offset > self->nr_entries) | 500 | self->index = self->nr_entries - 1; |
488 | offset = self->nr_entries; | 501 | self->first_visible_entry_idx = self->index - offset; |
489 | 502 | self->seek(self, -offset, SEEK_END); | |
490 | self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; | ||
491 | self->first_visible_entry = head->prev; | ||
492 | while (offset-- != 0) { | ||
493 | struct list_head *pos = self->first_visible_entry; | ||
494 | self->first_visible_entry = pos->prev; | ||
495 | } | ||
496 | } | ||
497 | break; | 503 | break; |
498 | case NEWT_KEY_RIGHT: | ||
499 | case NEWT_KEY_LEFT: | ||
500 | case NEWT_KEY_TAB: | ||
501 | return es->u.key; | ||
502 | default: | 504 | default: |
503 | continue; | 505 | return es->u.key; |
504 | } | 506 | } |
505 | if (ui_browser__refresh_entries(self) < 0) | 507 | if (ui_browser__refresh_entries(self) < 0) |
506 | return -1; | 508 | return -1; |
@@ -508,38 +510,6 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
508 | return 0; | 510 | return 0; |
509 | } | 511 | } |
510 | 512 | ||
511 | /* | ||
512 | * When debugging newt problems it was useful to be able to "unroll" | ||
513 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate | ||
514 | * a source file with the sequence of calls to these methods, to then | ||
515 | * tweak the arrays to get the intended results, so I'm keeping this code | ||
516 | * here, may be useful again in the future. | ||
517 | */ | ||
518 | #undef NEWT_DEBUG | ||
519 | |||
520 | static void newt_checkbox_tree__add(newtComponent tree, const char *str, | ||
521 | void *priv, int *indexes) | ||
522 | { | ||
523 | #ifdef NEWT_DEBUG | ||
524 | /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ | ||
525 | int i = 0, len = 40 - strlen(str); | ||
526 | |||
527 | fprintf(stderr, | ||
528 | "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", | ||
529 | len, len, " ", str, priv); | ||
530 | while (indexes[i] != NEWT_ARG_LAST) { | ||
531 | if (indexes[i] != NEWT_ARG_APPEND) | ||
532 | fprintf(stderr, " %d,", indexes[i]); | ||
533 | else | ||
534 | fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); | ||
535 | ++i; | ||
536 | } | ||
537 | fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); | ||
538 | fflush(stderr); | ||
539 | #endif | ||
540 | newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); | ||
541 | } | ||
542 | |||
543 | static char *callchain_list__sym_name(struct callchain_list *self, | 513 | static char *callchain_list__sym_name(struct callchain_list *self, |
544 | char *bf, size_t bfsize) | 514 | char *bf, size_t bfsize) |
545 | { | 515 | { |
@@ -550,144 +520,29 @@ static char *callchain_list__sym_name(struct callchain_list *self, | |||
550 | return bf; | 520 | return bf; |
551 | } | 521 | } |
552 | 522 | ||
553 | static void __callchain__append_graph_browser(struct callchain_node *self, | 523 | static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self) |
554 | newtComponent tree, u64 total, | ||
555 | int *indexes, int depth) | ||
556 | { | 524 | { |
557 | struct rb_node *node; | 525 | struct objdump_line *pos; |
558 | u64 new_total, remaining; | 526 | struct list_head *head = self->entries; |
559 | int idx = 0; | 527 | struct hist_entry *he = self->priv; |
560 | 528 | int row = 0; | |
561 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 529 | int len = he->ms.sym->end - he->ms.sym->start; |
562 | new_total = self->children_hit; | ||
563 | else | ||
564 | new_total = total; | ||
565 | |||
566 | remaining = new_total; | ||
567 | node = rb_first(&self->rb_root); | ||
568 | while (node) { | ||
569 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
570 | struct rb_node *next = rb_next(node); | ||
571 | u64 cumul = cumul_hits(child); | ||
572 | struct callchain_list *chain; | ||
573 | int first = true, printed = 0; | ||
574 | int chain_idx = -1; | ||
575 | remaining -= cumul; | ||
576 | |||
577 | indexes[depth] = NEWT_ARG_APPEND; | ||
578 | indexes[depth + 1] = NEWT_ARG_LAST; | ||
579 | |||
580 | list_for_each_entry(chain, &child->val, list) { | ||
581 | char ipstr[BITS_PER_LONG / 4 + 1], | ||
582 | *alloc_str = NULL; | ||
583 | const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
584 | |||
585 | if (first) { | ||
586 | double percent = cumul * 100.0 / new_total; | ||
587 | |||
588 | first = false; | ||
589 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
590 | str = "Not enough memory!"; | ||
591 | else | ||
592 | str = alloc_str; | ||
593 | } else { | ||
594 | indexes[depth] = idx; | ||
595 | indexes[depth + 1] = NEWT_ARG_APPEND; | ||
596 | indexes[depth + 2] = NEWT_ARG_LAST; | ||
597 | ++chain_idx; | ||
598 | } | ||
599 | newt_checkbox_tree__add(tree, str, &chain->ms, indexes); | ||
600 | free(alloc_str); | ||
601 | ++printed; | ||
602 | } | ||
603 | |||
604 | indexes[depth] = idx; | ||
605 | if (chain_idx != -1) | ||
606 | indexes[depth + 1] = chain_idx; | ||
607 | if (printed != 0) | ||
608 | ++idx; | ||
609 | __callchain__append_graph_browser(child, tree, new_total, indexes, | ||
610 | depth + (chain_idx != -1 ? 2 : 1)); | ||
611 | node = next; | ||
612 | } | ||
613 | } | ||
614 | |||
615 | static void callchain__append_graph_browser(struct callchain_node *self, | ||
616 | newtComponent tree, u64 total, | ||
617 | int *indexes, int parent_idx) | ||
618 | { | ||
619 | struct callchain_list *chain; | ||
620 | int i = 0; | ||
621 | |||
622 | indexes[1] = NEWT_ARG_APPEND; | ||
623 | indexes[2] = NEWT_ARG_LAST; | ||
624 | |||
625 | list_for_each_entry(chain, &self->val, list) { | ||
626 | char ipstr[BITS_PER_LONG / 4 + 1], *str; | ||
627 | |||
628 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
629 | continue; | ||
630 | |||
631 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
632 | continue; | ||
633 | 530 | ||
634 | str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | 531 | if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) |
635 | newt_checkbox_tree__add(tree, str, &chain->ms, indexes); | 532 | self->first_visible_entry = head->next; |
636 | } | ||
637 | 533 | ||
638 | indexes[1] = parent_idx; | 534 | pos = list_entry(self->first_visible_entry, struct objdump_line, node); |
639 | indexes[2] = NEWT_ARG_APPEND; | ||
640 | indexes[3] = NEWT_ARG_LAST; | ||
641 | __callchain__append_graph_browser(self, tree, total, indexes, 2); | ||
642 | } | ||
643 | 535 | ||
644 | static void hist_entry__append_callchain_browser(struct hist_entry *self, | 536 | list_for_each_entry_from(pos, head, node) { |
645 | newtComponent tree, u64 total, int parent_idx) | 537 | bool current_entry = ui_browser__is_current_entry(self, row); |
646 | { | 538 | SLsmg_gotorc(self->top + row, self->left); |
647 | struct rb_node *rb_node; | 539 | objdump_line__show(pos, head, self->width, |
648 | int indexes[1024] = { [0] = parent_idx, }; | 540 | he, len, current_entry); |
649 | int idx = 0; | 541 | if (++row == self->height) |
650 | struct callchain_node *chain; | ||
651 | |||
652 | rb_node = rb_first(&self->sorted_chain); | ||
653 | while (rb_node) { | ||
654 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
655 | switch (callchain_param.mode) { | ||
656 | case CHAIN_FLAT: | ||
657 | break; | ||
658 | case CHAIN_GRAPH_ABS: /* falldown */ | ||
659 | case CHAIN_GRAPH_REL: | ||
660 | callchain__append_graph_browser(chain, tree, total, indexes, idx++); | ||
661 | break; | ||
662 | case CHAIN_NONE: | ||
663 | default: | ||
664 | break; | 542 | break; |
665 | } | ||
666 | rb_node = rb_next(rb_node); | ||
667 | } | 543 | } |
668 | } | ||
669 | |||
670 | static size_t hist_entry__append_browser(struct hist_entry *self, | ||
671 | newtComponent tree, u64 total) | ||
672 | { | ||
673 | char s[256]; | ||
674 | size_t ret; | ||
675 | |||
676 | if (symbol_conf.exclude_other && !self->parent) | ||
677 | return 0; | ||
678 | 544 | ||
679 | ret = hist_entry__snprintf(self, s, sizeof(s), NULL, | 545 | return row; |
680 | false, 0, false, total); | ||
681 | if (symbol_conf.use_callchain) { | ||
682 | int indexes[2]; | ||
683 | |||
684 | indexes[0] = NEWT_ARG_APPEND; | ||
685 | indexes[1] = NEWT_ARG_LAST; | ||
686 | newt_checkbox_tree__add(tree, s, &self->ms, indexes); | ||
687 | } else | ||
688 | newtListboxAppendEntry(tree, s, &self->ms); | ||
689 | |||
690 | return ret; | ||
691 | } | 546 | } |
692 | 547 | ||
693 | int hist_entry__tui_annotate(struct hist_entry *self) | 548 | int hist_entry__tui_annotate(struct hist_entry *self) |
@@ -712,7 +567,9 @@ int hist_entry__tui_annotate(struct hist_entry *self) | |||
712 | ui_helpline__push("Press <- or ESC to exit"); | 567 | ui_helpline__push("Press <- or ESC to exit"); |
713 | 568 | ||
714 | memset(&browser, 0, sizeof(browser)); | 569 | memset(&browser, 0, sizeof(browser)); |
715 | browser.entries = &head; | 570 | browser.entries = &head; |
571 | browser.refresh_entries = hist_entry__annotate_browser_refresh; | ||
572 | browser.seek = ui_browser__list_head_seek; | ||
716 | browser.priv = self; | 573 | browser.priv = self; |
717 | list_for_each_entry(pos, &head, node) { | 574 | list_for_each_entry(pos, &head, node) { |
718 | size_t line_len = strlen(pos->line); | 575 | size_t line_len = strlen(pos->line); |
@@ -722,7 +579,9 @@ int hist_entry__tui_annotate(struct hist_entry *self) | |||
722 | } | 579 | } |
723 | 580 | ||
724 | browser.width += 18; /* Percentage */ | 581 | browser.width += 18; /* Percentage */ |
725 | ret = ui_browser__run(&browser, self->ms.sym->name, &es); | 582 | ui_browser__show(&browser, self->ms.sym->name); |
583 | newtFormAddHotKey(browser.form, ' '); | ||
584 | ret = ui_browser__run(&browser, &es); | ||
726 | newtFormDestroy(browser.form); | 585 | newtFormDestroy(browser.form); |
727 | newtPopWindow(); | 586 | newtPopWindow(); |
728 | list_for_each_entry_safe(pos, n, &head, node) { | 587 | list_for_each_entry_safe(pos, n, &head, node) { |
@@ -733,157 +592,48 @@ int hist_entry__tui_annotate(struct hist_entry *self) | |||
733 | return ret; | 592 | return ret; |
734 | } | 593 | } |
735 | 594 | ||
736 | static const void *newt__symbol_tree_get_current(newtComponent self) | ||
737 | { | ||
738 | if (symbol_conf.use_callchain) | ||
739 | return newtCheckboxTreeGetCurrent(self); | ||
740 | return newtListboxGetCurrent(self); | ||
741 | } | ||
742 | |||
743 | static void hist_browser__selection(newtComponent self, void *data) | ||
744 | { | ||
745 | const struct map_symbol **symbol_ptr = data; | ||
746 | *symbol_ptr = newt__symbol_tree_get_current(self); | ||
747 | } | ||
748 | |||
749 | struct hist_browser { | 595 | struct hist_browser { |
750 | newtComponent form, tree; | 596 | struct ui_browser b; |
751 | const struct map_symbol *selection; | 597 | struct hists *hists; |
598 | struct hist_entry *he_selection; | ||
599 | struct map_symbol *selection; | ||
752 | }; | 600 | }; |
753 | 601 | ||
754 | static struct hist_browser *hist_browser__new(void) | 602 | static void hist_browser__reset(struct hist_browser *self); |
603 | static int hist_browser__run(struct hist_browser *self, const char *title, | ||
604 | struct newtExitStruct *es); | ||
605 | static unsigned int hist_browser__refresh_entries(struct ui_browser *self); | ||
606 | static void ui_browser__hists_seek(struct ui_browser *self, | ||
607 | off_t offset, int whence); | ||
608 | |||
609 | static struct hist_browser *hist_browser__new(struct hists *hists) | ||
755 | { | 610 | { |
756 | struct hist_browser *self = malloc(sizeof(*self)); | 611 | struct hist_browser *self = zalloc(sizeof(*self)); |
757 | 612 | ||
758 | if (self != NULL) | 613 | if (self) { |
759 | self->form = NULL; | 614 | self->hists = hists; |
615 | self->b.refresh_entries = hist_browser__refresh_entries; | ||
616 | self->b.seek = ui_browser__hists_seek; | ||
617 | } | ||
760 | 618 | ||
761 | return self; | 619 | return self; |
762 | } | 620 | } |
763 | 621 | ||
764 | static void hist_browser__delete(struct hist_browser *self) | 622 | static void hist_browser__delete(struct hist_browser *self) |
765 | { | 623 | { |
766 | newtFormDestroy(self->form); | 624 | newtFormDestroy(self->b.form); |
767 | newtPopWindow(); | 625 | newtPopWindow(); |
768 | free(self); | 626 | free(self); |
769 | } | 627 | } |
770 | 628 | ||
771 | static int hist_browser__populate(struct hist_browser *self, struct hists *hists, | ||
772 | const char *title) | ||
773 | { | ||
774 | int max_len = 0, idx, cols, rows; | ||
775 | struct ui_progress *progress; | ||
776 | struct rb_node *nd; | ||
777 | u64 curr_hist = 0; | ||
778 | char seq[] = ".", unit; | ||
779 | char str[256]; | ||
780 | unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
781 | |||
782 | if (self->form) { | ||
783 | newtFormDestroy(self->form); | ||
784 | newtPopWindow(); | ||
785 | } | ||
786 | |||
787 | nr_events = convert_unit(nr_events, &unit); | ||
788 | snprintf(str, sizeof(str), "Events: %lu%c ", | ||
789 | nr_events, unit); | ||
790 | newtDrawRootText(0, 0, str); | ||
791 | |||
792 | newtGetScreenSize(NULL, &rows); | ||
793 | |||
794 | if (symbol_conf.use_callchain) | ||
795 | self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, | ||
796 | NEWT_FLAG_SCROLL); | ||
797 | else | ||
798 | self->tree = newtListbox(0, 0, rows - 5, | ||
799 | (NEWT_FLAG_SCROLL | | ||
800 | NEWT_FLAG_RETURNEXIT)); | ||
801 | |||
802 | newtComponentAddCallback(self->tree, hist_browser__selection, | ||
803 | &self->selection); | ||
804 | |||
805 | progress = ui_progress__new("Adding entries to the browser...", | ||
806 | hists->nr_entries); | ||
807 | if (progress == NULL) | ||
808 | return -1; | ||
809 | |||
810 | idx = 0; | ||
811 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
812 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
813 | int len; | ||
814 | |||
815 | if (h->filtered) | ||
816 | continue; | ||
817 | |||
818 | len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); | ||
819 | if (len > max_len) | ||
820 | max_len = len; | ||
821 | if (symbol_conf.use_callchain) | ||
822 | hist_entry__append_callchain_browser(h, self->tree, | ||
823 | hists->stats.total_period, idx++); | ||
824 | ++curr_hist; | ||
825 | if (curr_hist % 5) | ||
826 | ui_progress__update(progress, curr_hist); | ||
827 | } | ||
828 | |||
829 | ui_progress__delete(progress); | ||
830 | |||
831 | newtGetScreenSize(&cols, &rows); | ||
832 | |||
833 | if (max_len > cols) | ||
834 | max_len = cols - 3; | ||
835 | |||
836 | if (!symbol_conf.use_callchain) | ||
837 | newtListboxSetWidth(self->tree, max_len); | ||
838 | |||
839 | newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), | ||
840 | rows - 5, title); | ||
841 | self->form = newt_form__new(); | ||
842 | if (self->form == NULL) | ||
843 | return -1; | ||
844 | |||
845 | newtFormAddHotKey(self->form, 'A'); | ||
846 | newtFormAddHotKey(self->form, 'a'); | ||
847 | newtFormAddHotKey(self->form, 'D'); | ||
848 | newtFormAddHotKey(self->form, 'd'); | ||
849 | newtFormAddHotKey(self->form, 'T'); | ||
850 | newtFormAddHotKey(self->form, 't'); | ||
851 | newtFormAddHotKey(self->form, '?'); | ||
852 | newtFormAddHotKey(self->form, 'H'); | ||
853 | newtFormAddHotKey(self->form, 'h'); | ||
854 | newtFormAddHotKey(self->form, NEWT_KEY_F1); | ||
855 | newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); | ||
856 | newtFormAddHotKey(self->form, NEWT_KEY_TAB); | ||
857 | newtFormAddHotKey(self->form, NEWT_KEY_UNTAB); | ||
858 | newtFormAddComponents(self->form, self->tree, NULL); | ||
859 | self->selection = newt__symbol_tree_get_current(self->tree); | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) | 629 | static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) |
865 | { | 630 | { |
866 | int *indexes; | 631 | return self->he_selection; |
867 | |||
868 | if (!symbol_conf.use_callchain) | ||
869 | goto out; | ||
870 | |||
871 | indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); | ||
872 | if (indexes) { | ||
873 | bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; | ||
874 | free(indexes); | ||
875 | if (is_hist_entry) | ||
876 | goto out; | ||
877 | } | ||
878 | return NULL; | ||
879 | out: | ||
880 | return container_of(self->selection, struct hist_entry, ms); | ||
881 | } | 632 | } |
882 | 633 | ||
883 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | 634 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) |
884 | { | 635 | { |
885 | struct hist_entry *he = hist_browser__selected_entry(self); | 636 | return self->he_selection->thread; |
886 | return he ? he->thread : NULL; | ||
887 | } | 637 | } |
888 | 638 | ||
889 | static int hist_browser__title(char *bf, size_t size, const char *ev_name, | 639 | static int hist_browser__title(char *bf, size_t size, const char *ev_name, |
@@ -905,7 +655,7 @@ static int hist_browser__title(char *bf, size_t size, const char *ev_name, | |||
905 | 655 | ||
906 | int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | 656 | int hists__browse(struct hists *self, const char *helpline, const char *ev_name) |
907 | { | 657 | { |
908 | struct hist_browser *browser = hist_browser__new(); | 658 | struct hist_browser *browser = hist_browser__new(self); |
909 | struct pstack *fstack; | 659 | struct pstack *fstack; |
910 | const struct thread *thread_filter = NULL; | 660 | const struct thread *thread_filter = NULL; |
911 | const struct dso *dso_filter = NULL; | 661 | const struct dso *dso_filter = NULL; |
@@ -924,8 +674,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
924 | 674 | ||
925 | hist_browser__title(msg, sizeof(msg), ev_name, | 675 | hist_browser__title(msg, sizeof(msg), ev_name, |
926 | dso_filter, thread_filter); | 676 | dso_filter, thread_filter); |
927 | if (hist_browser__populate(browser, self, msg) < 0) | ||
928 | goto out_free_stack; | ||
929 | 677 | ||
930 | while (1) { | 678 | while (1) { |
931 | const struct thread *thread; | 679 | const struct thread *thread; |
@@ -934,7 +682,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
934 | int nr_options = 0, choice = 0, i, | 682 | int nr_options = 0, choice = 0, i, |
935 | annotate = -2, zoom_dso = -2, zoom_thread = -2; | 683 | annotate = -2, zoom_dso = -2, zoom_thread = -2; |
936 | 684 | ||
937 | newtFormRun(browser->form, &es); | 685 | if (hist_browser__run(browser, msg, &es)) |
686 | break; | ||
938 | 687 | ||
939 | thread = hist_browser__selected_thread(browser); | 688 | thread = hist_browser__selected_thread(browser); |
940 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | 689 | dso = browser->selection->map ? browser->selection->map->dso : NULL; |
@@ -1069,8 +818,7 @@ zoom_out_dso: | |||
1069 | hists__filter_by_dso(self, dso_filter); | 818 | hists__filter_by_dso(self, dso_filter); |
1070 | hist_browser__title(msg, sizeof(msg), ev_name, | 819 | hist_browser__title(msg, sizeof(msg), ev_name, |
1071 | dso_filter, thread_filter); | 820 | dso_filter, thread_filter); |
1072 | if (hist_browser__populate(browser, self, msg) < 0) | 821 | hist_browser__reset(browser); |
1073 | goto out; | ||
1074 | } else if (choice == zoom_thread) { | 822 | } else if (choice == zoom_thread) { |
1075 | zoom_thread: | 823 | zoom_thread: |
1076 | if (thread_filter) { | 824 | if (thread_filter) { |
@@ -1088,8 +836,7 @@ zoom_out_thread: | |||
1088 | hists__filter_by_thread(self, thread_filter); | 836 | hists__filter_by_thread(self, thread_filter); |
1089 | hist_browser__title(msg, sizeof(msg), ev_name, | 837 | hist_browser__title(msg, sizeof(msg), ev_name, |
1090 | dso_filter, thread_filter); | 838 | dso_filter, thread_filter); |
1091 | if (hist_browser__populate(browser, self, msg) < 0) | 839 | hist_browser__reset(browser); |
1092 | goto out; | ||
1093 | } | 840 | } |
1094 | } | 841 | } |
1095 | out_free_stack: | 842 | out_free_stack: |
@@ -1145,6 +892,13 @@ static struct newtPercentTreeColors { | |||
1145 | "blue", "lightgray", | 892 | "blue", "lightgray", |
1146 | }; | 893 | }; |
1147 | 894 | ||
895 | static void newt_suspend(void *d __used) | ||
896 | { | ||
897 | newtSuspend(); | ||
898 | raise(SIGTSTP); | ||
899 | newtResume(); | ||
900 | } | ||
901 | |||
1148 | void setup_browser(void) | 902 | void setup_browser(void) |
1149 | { | 903 | { |
1150 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | 904 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; |
@@ -1158,6 +912,7 @@ void setup_browser(void) | |||
1158 | use_browser = 1; | 912 | use_browser = 1; |
1159 | newtInit(); | 913 | newtInit(); |
1160 | newtCls(); | 914 | newtCls(); |
915 | newtSetSuspendCallback(newt_suspend, NULL); | ||
1161 | ui_helpline__puts(" "); | 916 | ui_helpline__puts(" "); |
1162 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | 917 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); |
1163 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | 918 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); |
@@ -1176,3 +931,638 @@ void exit_browser(bool wait_for_ok) | |||
1176 | newtFinished(); | 931 | newtFinished(); |
1177 | } | 932 | } |
1178 | } | 933 | } |
934 | |||
935 | static void hist_browser__refresh_dimensions(struct hist_browser *self) | ||
936 | { | ||
937 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ | ||
938 | self->b.width = 3 + (hists__sort_list_width(self->hists) + | ||
939 | sizeof("[k]")); | ||
940 | } | ||
941 | |||
942 | static void hist_browser__reset(struct hist_browser *self) | ||
943 | { | ||
944 | self->b.nr_entries = self->hists->nr_entries; | ||
945 | hist_browser__refresh_dimensions(self); | ||
946 | ui_browser__reset_index(&self->b); | ||
947 | } | ||
948 | |||
949 | static char tree__folded_sign(bool unfolded) | ||
950 | { | ||
951 | return unfolded ? '-' : '+'; | ||
952 | } | ||
953 | |||
954 | static char map_symbol__folded(const struct map_symbol *self) | ||
955 | { | ||
956 | return self->has_children ? tree__folded_sign(self->unfolded) : ' '; | ||
957 | } | ||
958 | |||
959 | static char hist_entry__folded(const struct hist_entry *self) | ||
960 | { | ||
961 | return map_symbol__folded(&self->ms); | ||
962 | } | ||
963 | |||
964 | static char callchain_list__folded(const struct callchain_list *self) | ||
965 | { | ||
966 | return map_symbol__folded(&self->ms); | ||
967 | } | ||
968 | |||
969 | static bool map_symbol__toggle_fold(struct map_symbol *self) | ||
970 | { | ||
971 | if (!self->has_children) | ||
972 | return false; | ||
973 | |||
974 | self->unfolded = !self->unfolded; | ||
975 | return true; | ||
976 | } | ||
977 | |||
978 | #define LEVEL_OFFSET_STEP 3 | ||
979 | |||
980 | static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | ||
981 | struct callchain_node *chain_node, | ||
982 | u64 total, int level, | ||
983 | unsigned short row, | ||
984 | off_t *row_offset, | ||
985 | bool *is_current_entry) | ||
986 | { | ||
987 | struct rb_node *node; | ||
988 | int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; | ||
989 | u64 new_total, remaining; | ||
990 | |||
991 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
992 | new_total = chain_node->children_hit; | ||
993 | else | ||
994 | new_total = total; | ||
995 | |||
996 | remaining = new_total; | ||
997 | node = rb_first(&chain_node->rb_root); | ||
998 | while (node) { | ||
999 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
1000 | struct rb_node *next = rb_next(node); | ||
1001 | u64 cumul = cumul_hits(child); | ||
1002 | struct callchain_list *chain; | ||
1003 | char folded_sign = ' '; | ||
1004 | int first = true; | ||
1005 | int extra_offset = 0; | ||
1006 | |||
1007 | remaining -= cumul; | ||
1008 | |||
1009 | list_for_each_entry(chain, &child->val, list) { | ||
1010 | char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; | ||
1011 | const char *str; | ||
1012 | int color; | ||
1013 | bool was_first = first; | ||
1014 | |||
1015 | if (first) { | ||
1016 | first = false; | ||
1017 | chain->ms.has_children = chain->list.next != &child->val || | ||
1018 | rb_first(&child->rb_root) != NULL; | ||
1019 | } else { | ||
1020 | extra_offset = LEVEL_OFFSET_STEP; | ||
1021 | chain->ms.has_children = chain->list.next == &child->val && | ||
1022 | rb_first(&child->rb_root) != NULL; | ||
1023 | } | ||
1024 | |||
1025 | folded_sign = callchain_list__folded(chain); | ||
1026 | if (*row_offset != 0) { | ||
1027 | --*row_offset; | ||
1028 | goto do_next; | ||
1029 | } | ||
1030 | |||
1031 | alloc_str = NULL; | ||
1032 | str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
1033 | if (was_first) { | ||
1034 | double percent = cumul * 100.0 / new_total; | ||
1035 | |||
1036 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
1037 | str = "Not enough memory!"; | ||
1038 | else | ||
1039 | str = alloc_str; | ||
1040 | } | ||
1041 | |||
1042 | color = HE_COLORSET_NORMAL; | ||
1043 | width = self->b.width - (offset + extra_offset + 2); | ||
1044 | if (ui_browser__is_current_entry(&self->b, row)) { | ||
1045 | self->selection = &chain->ms; | ||
1046 | color = HE_COLORSET_SELECTED; | ||
1047 | *is_current_entry = true; | ||
1048 | } | ||
1049 | |||
1050 | SLsmg_set_color(color); | ||
1051 | SLsmg_gotorc(self->b.top + row, self->b.left); | ||
1052 | slsmg_write_nstring(" ", offset + extra_offset); | ||
1053 | slsmg_printf("%c ", folded_sign); | ||
1054 | slsmg_write_nstring(str, width); | ||
1055 | free(alloc_str); | ||
1056 | |||
1057 | if (++row == self->b.height) | ||
1058 | goto out; | ||
1059 | do_next: | ||
1060 | if (folded_sign == '+') | ||
1061 | break; | ||
1062 | } | ||
1063 | |||
1064 | if (folded_sign == '-') { | ||
1065 | const int new_level = level + (extra_offset ? 2 : 1); | ||
1066 | row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, | ||
1067 | new_level, row, row_offset, | ||
1068 | is_current_entry); | ||
1069 | } | ||
1070 | if (row == self->b.height) | ||
1071 | goto out; | ||
1072 | node = next; | ||
1073 | } | ||
1074 | out: | ||
1075 | return row - first_row; | ||
1076 | } | ||
1077 | |||
1078 | static int hist_browser__show_callchain_node(struct hist_browser *self, | ||
1079 | struct callchain_node *node, | ||
1080 | int level, unsigned short row, | ||
1081 | off_t *row_offset, | ||
1082 | bool *is_current_entry) | ||
1083 | { | ||
1084 | struct callchain_list *chain; | ||
1085 | int first_row = row, | ||
1086 | offset = level * LEVEL_OFFSET_STEP, | ||
1087 | width = self->b.width - offset; | ||
1088 | char folded_sign = ' '; | ||
1089 | |||
1090 | list_for_each_entry(chain, &node->val, list) { | ||
1091 | char ipstr[BITS_PER_LONG / 4 + 1], *s; | ||
1092 | int color; | ||
1093 | /* | ||
1094 | * FIXME: This should be moved to somewhere else, | ||
1095 | * probably when the callchain is created, so as not to | ||
1096 | * traverse it all over again | ||
1097 | */ | ||
1098 | chain->ms.has_children = rb_first(&node->rb_root) != NULL; | ||
1099 | folded_sign = callchain_list__folded(chain); | ||
1100 | |||
1101 | if (*row_offset != 0) { | ||
1102 | --*row_offset; | ||
1103 | continue; | ||
1104 | } | ||
1105 | |||
1106 | color = HE_COLORSET_NORMAL; | ||
1107 | if (ui_browser__is_current_entry(&self->b, row)) { | ||
1108 | self->selection = &chain->ms; | ||
1109 | color = HE_COLORSET_SELECTED; | ||
1110 | *is_current_entry = true; | ||
1111 | } | ||
1112 | |||
1113 | s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
1114 | SLsmg_gotorc(self->b.top + row, self->b.left); | ||
1115 | SLsmg_set_color(color); | ||
1116 | slsmg_write_nstring(" ", offset); | ||
1117 | slsmg_printf("%c ", folded_sign); | ||
1118 | slsmg_write_nstring(s, width - 2); | ||
1119 | |||
1120 | if (++row == self->b.height) | ||
1121 | goto out; | ||
1122 | } | ||
1123 | |||
1124 | if (folded_sign == '-') | ||
1125 | row += hist_browser__show_callchain_node_rb_tree(self, node, | ||
1126 | self->hists->stats.total_period, | ||
1127 | level + 1, row, | ||
1128 | row_offset, | ||
1129 | is_current_entry); | ||
1130 | out: | ||
1131 | return row - first_row; | ||
1132 | } | ||
1133 | |||
1134 | static int hist_browser__show_callchain(struct hist_browser *self, | ||
1135 | struct rb_root *chain, | ||
1136 | int level, unsigned short row, | ||
1137 | off_t *row_offset, | ||
1138 | bool *is_current_entry) | ||
1139 | { | ||
1140 | struct rb_node *nd; | ||
1141 | int first_row = row; | ||
1142 | |||
1143 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | ||
1144 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
1145 | |||
1146 | row += hist_browser__show_callchain_node(self, node, level, | ||
1147 | row, row_offset, | ||
1148 | is_current_entry); | ||
1149 | if (row == self->b.height) | ||
1150 | break; | ||
1151 | } | ||
1152 | |||
1153 | return row - first_row; | ||
1154 | } | ||
1155 | |||
1156 | static int hist_browser__show_entry(struct hist_browser *self, | ||
1157 | struct hist_entry *entry, | ||
1158 | unsigned short row) | ||
1159 | { | ||
1160 | char s[256]; | ||
1161 | double percent; | ||
1162 | int printed = 0; | ||
1163 | int color, width = self->b.width; | ||
1164 | char folded_sign = ' '; | ||
1165 | bool current_entry = ui_browser__is_current_entry(&self->b, row); | ||
1166 | off_t row_offset = entry->row_offset; | ||
1167 | |||
1168 | if (current_entry) { | ||
1169 | self->he_selection = entry; | ||
1170 | self->selection = &entry->ms; | ||
1171 | } | ||
1172 | |||
1173 | if (symbol_conf.use_callchain) { | ||
1174 | entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); | ||
1175 | folded_sign = hist_entry__folded(entry); | ||
1176 | } | ||
1177 | |||
1178 | if (row_offset == 0) { | ||
1179 | hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, | ||
1180 | 0, false, self->hists->stats.total_period); | ||
1181 | percent = (entry->period * 100.0) / self->hists->stats.total_period; | ||
1182 | |||
1183 | color = HE_COLORSET_SELECTED; | ||
1184 | if (!current_entry) { | ||
1185 | if (percent >= MIN_RED) | ||
1186 | color = HE_COLORSET_TOP; | ||
1187 | else if (percent >= MIN_GREEN) | ||
1188 | color = HE_COLORSET_MEDIUM; | ||
1189 | else | ||
1190 | color = HE_COLORSET_NORMAL; | ||
1191 | } | ||
1192 | |||
1193 | SLsmg_set_color(color); | ||
1194 | SLsmg_gotorc(self->b.top + row, self->b.left); | ||
1195 | if (symbol_conf.use_callchain) { | ||
1196 | slsmg_printf("%c ", folded_sign); | ||
1197 | width -= 2; | ||
1198 | } | ||
1199 | slsmg_write_nstring(s, width); | ||
1200 | ++row; | ||
1201 | ++printed; | ||
1202 | } else | ||
1203 | --row_offset; | ||
1204 | |||
1205 | if (folded_sign == '-' && row != self->b.height) { | ||
1206 | printed += hist_browser__show_callchain(self, &entry->sorted_chain, | ||
1207 | 1, row, &row_offset, | ||
1208 | ¤t_entry); | ||
1209 | if (current_entry) | ||
1210 | self->he_selection = entry; | ||
1211 | } | ||
1212 | |||
1213 | return printed; | ||
1214 | } | ||
1215 | |||
1216 | static unsigned int hist_browser__refresh_entries(struct ui_browser *self) | ||
1217 | { | ||
1218 | unsigned row = 0; | ||
1219 | struct rb_node *nd; | ||
1220 | struct hist_browser *hb = container_of(self, struct hist_browser, b); | ||
1221 | |||
1222 | if (self->first_visible_entry == NULL) | ||
1223 | self->first_visible_entry = rb_first(&hb->hists->entries); | ||
1224 | |||
1225 | for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { | ||
1226 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1227 | |||
1228 | if (h->filtered) | ||
1229 | continue; | ||
1230 | |||
1231 | row += hist_browser__show_entry(hb, h, row); | ||
1232 | if (row == self->height) | ||
1233 | break; | ||
1234 | } | ||
1235 | |||
1236 | return row; | ||
1237 | } | ||
1238 | |||
1239 | static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) | ||
1240 | { | ||
1241 | struct rb_node *nd = rb_first(&self->rb_root); | ||
1242 | |||
1243 | for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { | ||
1244 | struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); | ||
1245 | struct callchain_list *chain; | ||
1246 | int first = true; | ||
1247 | |||
1248 | list_for_each_entry(chain, &child->val, list) { | ||
1249 | if (first) { | ||
1250 | first = false; | ||
1251 | chain->ms.has_children = chain->list.next != &child->val || | ||
1252 | rb_first(&child->rb_root) != NULL; | ||
1253 | } else | ||
1254 | chain->ms.has_children = chain->list.next == &child->val && | ||
1255 | rb_first(&child->rb_root) != NULL; | ||
1256 | } | ||
1257 | |||
1258 | callchain_node__init_have_children_rb_tree(child); | ||
1259 | } | ||
1260 | } | ||
1261 | |||
1262 | static void callchain_node__init_have_children(struct callchain_node *self) | ||
1263 | { | ||
1264 | struct callchain_list *chain; | ||
1265 | |||
1266 | list_for_each_entry(chain, &self->val, list) | ||
1267 | chain->ms.has_children = rb_first(&self->rb_root) != NULL; | ||
1268 | |||
1269 | callchain_node__init_have_children_rb_tree(self); | ||
1270 | } | ||
1271 | |||
1272 | static void callchain__init_have_children(struct rb_root *self) | ||
1273 | { | ||
1274 | struct rb_node *nd; | ||
1275 | |||
1276 | for (nd = rb_first(self); nd; nd = rb_next(nd)) { | ||
1277 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
1278 | callchain_node__init_have_children(node); | ||
1279 | } | ||
1280 | } | ||
1281 | |||
1282 | static void hist_entry__init_have_children(struct hist_entry *self) | ||
1283 | { | ||
1284 | if (!self->init_have_children) { | ||
1285 | callchain__init_have_children(&self->sorted_chain); | ||
1286 | self->init_have_children = true; | ||
1287 | } | ||
1288 | } | ||
1289 | |||
1290 | static struct rb_node *hists__filter_entries(struct rb_node *nd) | ||
1291 | { | ||
1292 | while (nd != NULL) { | ||
1293 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1294 | if (!h->filtered) | ||
1295 | return nd; | ||
1296 | |||
1297 | nd = rb_next(nd); | ||
1298 | } | ||
1299 | |||
1300 | return NULL; | ||
1301 | } | ||
1302 | |||
1303 | static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) | ||
1304 | { | ||
1305 | while (nd != NULL) { | ||
1306 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1307 | if (!h->filtered) | ||
1308 | return nd; | ||
1309 | |||
1310 | nd = rb_prev(nd); | ||
1311 | } | ||
1312 | |||
1313 | return NULL; | ||
1314 | } | ||
1315 | |||
1316 | static void ui_browser__hists_seek(struct ui_browser *self, | ||
1317 | off_t offset, int whence) | ||
1318 | { | ||
1319 | struct hist_entry *h; | ||
1320 | struct rb_node *nd; | ||
1321 | bool first = true; | ||
1322 | |||
1323 | switch (whence) { | ||
1324 | case SEEK_SET: | ||
1325 | nd = hists__filter_entries(rb_first(self->entries)); | ||
1326 | break; | ||
1327 | case SEEK_CUR: | ||
1328 | nd = self->first_visible_entry; | ||
1329 | goto do_offset; | ||
1330 | case SEEK_END: | ||
1331 | nd = hists__filter_prev_entries(rb_last(self->entries)); | ||
1332 | first = false; | ||
1333 | break; | ||
1334 | default: | ||
1335 | return; | ||
1336 | } | ||
1337 | |||
1338 | /* | ||
1339 | * Moves not relative to the first visible entry invalidates its | ||
1340 | * row_offset: | ||
1341 | */ | ||
1342 | h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); | ||
1343 | h->row_offset = 0; | ||
1344 | |||
1345 | /* | ||
1346 | * Here we have to check if nd is expanded (+), if it is we can't go | ||
1347 | * the next top level hist_entry, instead we must compute an offset of | ||
1348 | * what _not_ to show and not change the first visible entry. | ||
1349 | * | ||
1350 | * This offset increments when we are going from top to bottom and | ||
1351 | * decreases when we're going from bottom to top. | ||
1352 | * | ||
1353 | * As we don't have backpointers to the top level in the callchains | ||
1354 | * structure, we need to always print the whole hist_entry callchain, | ||
1355 | * skipping the first ones that are before the first visible entry | ||
1356 | * and stop when we printed enough lines to fill the screen. | ||
1357 | */ | ||
1358 | do_offset: | ||
1359 | if (offset > 0) { | ||
1360 | do { | ||
1361 | h = rb_entry(nd, struct hist_entry, rb_node); | ||
1362 | if (h->ms.unfolded) { | ||
1363 | u16 remaining = h->nr_rows - h->row_offset; | ||
1364 | if (offset > remaining) { | ||
1365 | offset -= remaining; | ||
1366 | h->row_offset = 0; | ||
1367 | } else { | ||
1368 | h->row_offset += offset; | ||
1369 | offset = 0; | ||
1370 | self->first_visible_entry = nd; | ||
1371 | break; | ||
1372 | } | ||
1373 | } | ||
1374 | nd = hists__filter_entries(rb_next(nd)); | ||
1375 | if (nd == NULL) | ||
1376 | break; | ||
1377 | --offset; | ||
1378 | self->first_visible_entry = nd; | ||
1379 | } while (offset != 0); | ||
1380 | } else if (offset < 0) { | ||
1381 | while (1) { | ||
1382 | h = rb_entry(nd, struct hist_entry, rb_node); | ||
1383 | if (h->ms.unfolded) { | ||
1384 | if (first) { | ||
1385 | if (-offset > h->row_offset) { | ||
1386 | offset += h->row_offset; | ||
1387 | h->row_offset = 0; | ||
1388 | } else { | ||
1389 | h->row_offset += offset; | ||
1390 | offset = 0; | ||
1391 | self->first_visible_entry = nd; | ||
1392 | break; | ||
1393 | } | ||
1394 | } else { | ||
1395 | if (-offset > h->nr_rows) { | ||
1396 | offset += h->nr_rows; | ||
1397 | h->row_offset = 0; | ||
1398 | } else { | ||
1399 | h->row_offset = h->nr_rows + offset; | ||
1400 | offset = 0; | ||
1401 | self->first_visible_entry = nd; | ||
1402 | break; | ||
1403 | } | ||
1404 | } | ||
1405 | } | ||
1406 | |||
1407 | nd = hists__filter_prev_entries(rb_prev(nd)); | ||
1408 | if (nd == NULL) | ||
1409 | break; | ||
1410 | ++offset; | ||
1411 | self->first_visible_entry = nd; | ||
1412 | if (offset == 0) { | ||
1413 | /* | ||
1414 | * Last unfiltered hist_entry, check if it is | ||
1415 | * unfolded, if it is then we should have | ||
1416 | * row_offset at its last entry. | ||
1417 | */ | ||
1418 | h = rb_entry(nd, struct hist_entry, rb_node); | ||
1419 | if (h->ms.unfolded) | ||
1420 | h->row_offset = h->nr_rows; | ||
1421 | break; | ||
1422 | } | ||
1423 | first = false; | ||
1424 | } | ||
1425 | } else { | ||
1426 | self->first_visible_entry = nd; | ||
1427 | h = rb_entry(nd, struct hist_entry, rb_node); | ||
1428 | h->row_offset = 0; | ||
1429 | } | ||
1430 | } | ||
1431 | |||
1432 | static int callchain_node__count_rows_rb_tree(struct callchain_node *self) | ||
1433 | { | ||
1434 | int n = 0; | ||
1435 | struct rb_node *nd; | ||
1436 | |||
1437 | for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { | ||
1438 | struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); | ||
1439 | struct callchain_list *chain; | ||
1440 | char folded_sign = ' '; /* No children */ | ||
1441 | |||
1442 | list_for_each_entry(chain, &child->val, list) { | ||
1443 | ++n; | ||
1444 | /* We need this because we may not have children */ | ||
1445 | folded_sign = callchain_list__folded(chain); | ||
1446 | if (folded_sign == '+') | ||
1447 | break; | ||
1448 | } | ||
1449 | |||
1450 | if (folded_sign == '-') /* Have children and they're unfolded */ | ||
1451 | n += callchain_node__count_rows_rb_tree(child); | ||
1452 | } | ||
1453 | |||
1454 | return n; | ||
1455 | } | ||
1456 | |||
1457 | static int callchain_node__count_rows(struct callchain_node *node) | ||
1458 | { | ||
1459 | struct callchain_list *chain; | ||
1460 | bool unfolded = false; | ||
1461 | int n = 0; | ||
1462 | |||
1463 | list_for_each_entry(chain, &node->val, list) { | ||
1464 | ++n; | ||
1465 | unfolded = chain->ms.unfolded; | ||
1466 | } | ||
1467 | |||
1468 | if (unfolded) | ||
1469 | n += callchain_node__count_rows_rb_tree(node); | ||
1470 | |||
1471 | return n; | ||
1472 | } | ||
1473 | |||
1474 | static int callchain__count_rows(struct rb_root *chain) | ||
1475 | { | ||
1476 | struct rb_node *nd; | ||
1477 | int n = 0; | ||
1478 | |||
1479 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | ||
1480 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
1481 | n += callchain_node__count_rows(node); | ||
1482 | } | ||
1483 | |||
1484 | return n; | ||
1485 | } | ||
1486 | |||
1487 | static bool hist_browser__toggle_fold(struct hist_browser *self) | ||
1488 | { | ||
1489 | if (map_symbol__toggle_fold(self->selection)) { | ||
1490 | struct hist_entry *he = self->he_selection; | ||
1491 | |||
1492 | hist_entry__init_have_children(he); | ||
1493 | self->hists->nr_entries -= he->nr_rows; | ||
1494 | |||
1495 | if (he->ms.unfolded) | ||
1496 | he->nr_rows = callchain__count_rows(&he->sorted_chain); | ||
1497 | else | ||
1498 | he->nr_rows = 0; | ||
1499 | self->hists->nr_entries += he->nr_rows; | ||
1500 | self->b.nr_entries = self->hists->nr_entries; | ||
1501 | |||
1502 | return true; | ||
1503 | } | ||
1504 | |||
1505 | /* If it doesn't have children, no toggling performed */ | ||
1506 | return false; | ||
1507 | } | ||
1508 | |||
1509 | static int hist_browser__run(struct hist_browser *self, const char *title, | ||
1510 | struct newtExitStruct *es) | ||
1511 | { | ||
1512 | char str[256], unit; | ||
1513 | unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1514 | |||
1515 | self->b.entries = &self->hists->entries; | ||
1516 | self->b.nr_entries = self->hists->nr_entries; | ||
1517 | |||
1518 | hist_browser__refresh_dimensions(self); | ||
1519 | |||
1520 | nr_events = convert_unit(nr_events, &unit); | ||
1521 | snprintf(str, sizeof(str), "Events: %lu%c ", | ||
1522 | nr_events, unit); | ||
1523 | newtDrawRootText(0, 0, str); | ||
1524 | |||
1525 | if (ui_browser__show(&self->b, title) < 0) | ||
1526 | return -1; | ||
1527 | |||
1528 | newtFormAddHotKey(self->b.form, 'A'); | ||
1529 | newtFormAddHotKey(self->b.form, 'a'); | ||
1530 | newtFormAddHotKey(self->b.form, '?'); | ||
1531 | newtFormAddHotKey(self->b.form, 'h'); | ||
1532 | newtFormAddHotKey(self->b.form, 'H'); | ||
1533 | newtFormAddHotKey(self->b.form, 'd'); | ||
1534 | |||
1535 | newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); | ||
1536 | newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); | ||
1537 | newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); | ||
1538 | |||
1539 | while (1) { | ||
1540 | ui_browser__run(&self->b, es); | ||
1541 | |||
1542 | if (es->reason != NEWT_EXIT_HOTKEY) | ||
1543 | break; | ||
1544 | switch (es->u.key) { | ||
1545 | case 'd': { /* Debug */ | ||
1546 | static int seq; | ||
1547 | struct hist_entry *h = rb_entry(self->b.first_visible_entry, | ||
1548 | struct hist_entry, rb_node); | ||
1549 | ui_helpline__pop(); | ||
1550 | ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", | ||
1551 | seq++, self->b.nr_entries, | ||
1552 | self->hists->nr_entries, | ||
1553 | self->b.height, | ||
1554 | self->b.index, | ||
1555 | self->b.first_visible_entry_idx, | ||
1556 | h->row_offset, h->nr_rows); | ||
1557 | } | ||
1558 | continue; | ||
1559 | case NEWT_KEY_ENTER: | ||
1560 | if (hist_browser__toggle_fold(self)) | ||
1561 | break; | ||
1562 | /* fall thru */ | ||
1563 | default: | ||
1564 | return 0; | ||
1565 | } | ||
1566 | } | ||
1567 | return 0; | ||
1568 | } | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 9bf0f402ca73..4af5bd59cfd1 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -602,8 +602,15 @@ parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | |||
602 | return EVT_FAILED; | 602 | return EVT_FAILED; |
603 | } | 603 | } |
604 | 604 | ||
605 | /* We should find a nice way to override the access type */ | 605 | /* |
606 | attr->bp_len = HW_BREAKPOINT_LEN_4; | 606 | * We should find a nice way to override the access length |
607 | * Provide some defaults for now | ||
608 | */ | ||
609 | if (attr->bp_type == HW_BREAKPOINT_X) | ||
610 | attr->bp_len = sizeof(long); | ||
611 | else | ||
612 | attr->bp_len = HW_BREAKPOINT_LEN_4; | ||
613 | |||
607 | attr->type = PERF_TYPE_BREAKPOINT; | 614 | attr->type = PERF_TYPE_BREAKPOINT; |
608 | 615 | ||
609 | return EVT_HANDLED; | 616 | return EVT_HANDLED; |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 914c67095d96..2e665cb84055 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * probe-event.c : perf-probe definition to kprobe_events format converter | 2 | * probe-event.c : perf-probe definition to probe_events format converter |
3 | * | 3 | * |
4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | 4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> |
5 | * | 5 | * |
@@ -120,8 +120,11 @@ static int open_vmlinux(void) | |||
120 | return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); | 120 | return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); |
121 | } | 121 | } |
122 | 122 | ||
123 | /* Convert trace point to probe point with debuginfo */ | 123 | /* |
124 | static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, | 124 | * Convert trace point to probe point with debuginfo |
125 | * Currently only handles kprobes. | ||
126 | */ | ||
127 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | ||
125 | struct perf_probe_point *pp) | 128 | struct perf_probe_point *pp) |
126 | { | 129 | { |
127 | struct symbol *sym; | 130 | struct symbol *sym; |
@@ -151,8 +154,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, | |||
151 | } | 154 | } |
152 | 155 | ||
153 | /* Try to find perf_probe_event with debuginfo */ | 156 | /* Try to find perf_probe_event with debuginfo */ |
154 | static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, | 157 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
155 | struct kprobe_trace_event **tevs, | 158 | struct probe_trace_event **tevs, |
156 | int max_tevs) | 159 | int max_tevs) |
157 | { | 160 | { |
158 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 161 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
@@ -169,11 +172,11 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, | |||
169 | } | 172 | } |
170 | 173 | ||
171 | /* Searching trace events corresponding to probe event */ | 174 | /* Searching trace events corresponding to probe event */ |
172 | ntevs = find_kprobe_trace_events(fd, pev, tevs, max_tevs); | 175 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); |
173 | close(fd); | 176 | close(fd); |
174 | 177 | ||
175 | if (ntevs > 0) { /* Succeeded to find trace events */ | 178 | if (ntevs > 0) { /* Succeeded to find trace events */ |
176 | pr_debug("find %d kprobe_trace_events.\n", ntevs); | 179 | pr_debug("find %d probe_trace_events.\n", ntevs); |
177 | return ntevs; | 180 | return ntevs; |
178 | } | 181 | } |
179 | 182 | ||
@@ -195,6 +198,65 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, | |||
195 | return ntevs; | 198 | return ntevs; |
196 | } | 199 | } |
197 | 200 | ||
201 | /* | ||
202 | * Find a src file from a DWARF tag path. Prepend optional source path prefix | ||
203 | * and chop off leading directories that do not exist. Result is passed back as | ||
204 | * a newly allocated path on success. | ||
205 | * Return 0 if file was found and readable, -errno otherwise. | ||
206 | */ | ||
207 | static int get_real_path(const char *raw_path, const char *comp_dir, | ||
208 | char **new_path) | ||
209 | { | ||
210 | const char *prefix = symbol_conf.source_prefix; | ||
211 | |||
212 | if (!prefix) { | ||
213 | if (raw_path[0] != '/' && comp_dir) | ||
214 | /* If not an absolute path, try to use comp_dir */ | ||
215 | prefix = comp_dir; | ||
216 | else { | ||
217 | if (access(raw_path, R_OK) == 0) { | ||
218 | *new_path = strdup(raw_path); | ||
219 | return 0; | ||
220 | } else | ||
221 | return -errno; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2)); | ||
226 | if (!*new_path) | ||
227 | return -ENOMEM; | ||
228 | |||
229 | for (;;) { | ||
230 | sprintf(*new_path, "%s/%s", prefix, raw_path); | ||
231 | |||
232 | if (access(*new_path, R_OK) == 0) | ||
233 | return 0; | ||
234 | |||
235 | if (!symbol_conf.source_prefix) | ||
236 | /* In case of searching comp_dir, don't retry */ | ||
237 | return -errno; | ||
238 | |||
239 | switch (errno) { | ||
240 | case ENAMETOOLONG: | ||
241 | case ENOENT: | ||
242 | case EROFS: | ||
243 | case EFAULT: | ||
244 | raw_path = strchr(++raw_path, '/'); | ||
245 | if (!raw_path) { | ||
246 | free(*new_path); | ||
247 | *new_path = NULL; | ||
248 | return -ENOENT; | ||
249 | } | ||
250 | continue; | ||
251 | |||
252 | default: | ||
253 | free(*new_path); | ||
254 | *new_path = NULL; | ||
255 | return -errno; | ||
256 | } | ||
257 | } | ||
258 | } | ||
259 | |||
198 | #define LINEBUF_SIZE 256 | 260 | #define LINEBUF_SIZE 256 |
199 | #define NR_ADDITIONAL_LINES 2 | 261 | #define NR_ADDITIONAL_LINES 2 |
200 | 262 | ||
@@ -244,6 +306,7 @@ int show_line_range(struct line_range *lr) | |||
244 | struct line_node *ln; | 306 | struct line_node *ln; |
245 | FILE *fp; | 307 | FILE *fp; |
246 | int fd, ret; | 308 | int fd, ret; |
309 | char *tmp; | ||
247 | 310 | ||
248 | /* Search a line range */ | 311 | /* Search a line range */ |
249 | ret = init_vmlinux(); | 312 | ret = init_vmlinux(); |
@@ -266,6 +329,15 @@ int show_line_range(struct line_range *lr) | |||
266 | return ret; | 329 | return ret; |
267 | } | 330 | } |
268 | 331 | ||
332 | /* Convert source file path */ | ||
333 | tmp = lr->path; | ||
334 | ret = get_real_path(tmp, lr->comp_dir, &lr->path); | ||
335 | free(tmp); /* Free old path */ | ||
336 | if (ret < 0) { | ||
337 | pr_warning("Failed to find source file. (%d)\n", ret); | ||
338 | return ret; | ||
339 | } | ||
340 | |||
269 | setup_pager(); | 341 | setup_pager(); |
270 | 342 | ||
271 | if (lr->function) | 343 | if (lr->function) |
@@ -308,8 +380,8 @@ end: | |||
308 | 380 | ||
309 | #else /* !DWARF_SUPPORT */ | 381 | #else /* !DWARF_SUPPORT */ |
310 | 382 | ||
311 | static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, | 383 | static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, |
312 | struct perf_probe_point *pp) | 384 | struct perf_probe_point *pp) |
313 | { | 385 | { |
314 | pp->function = strdup(tp->symbol); | 386 | pp->function = strdup(tp->symbol); |
315 | if (pp->function == NULL) | 387 | if (pp->function == NULL) |
@@ -320,8 +392,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, | |||
320 | return 0; | 392 | return 0; |
321 | } | 393 | } |
322 | 394 | ||
323 | static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, | 395 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
324 | struct kprobe_trace_event **tevs __unused, | 396 | struct probe_trace_event **tevs __unused, |
325 | int max_tevs __unused) | 397 | int max_tevs __unused) |
326 | { | 398 | { |
327 | if (perf_probe_event_need_dwarf(pev)) { | 399 | if (perf_probe_event_need_dwarf(pev)) { |
@@ -557,7 +629,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | |||
557 | /* Parse perf-probe event argument */ | 629 | /* Parse perf-probe event argument */ |
558 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | 630 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) |
559 | { | 631 | { |
560 | char *tmp; | 632 | char *tmp, *goodname; |
561 | struct perf_probe_arg_field **fieldp; | 633 | struct perf_probe_arg_field **fieldp; |
562 | 634 | ||
563 | pr_debug("parsing arg: %s into ", str); | 635 | pr_debug("parsing arg: %s into ", str); |
@@ -580,7 +652,7 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | |||
580 | pr_debug("type:%s ", arg->type); | 652 | pr_debug("type:%s ", arg->type); |
581 | } | 653 | } |
582 | 654 | ||
583 | tmp = strpbrk(str, "-."); | 655 | tmp = strpbrk(str, "-.["); |
584 | if (!is_c_varname(str) || !tmp) { | 656 | if (!is_c_varname(str) || !tmp) { |
585 | /* A variable, register, symbol or special value */ | 657 | /* A variable, register, symbol or special value */ |
586 | arg->var = strdup(str); | 658 | arg->var = strdup(str); |
@@ -590,10 +662,11 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | |||
590 | return 0; | 662 | return 0; |
591 | } | 663 | } |
592 | 664 | ||
593 | /* Structure fields */ | 665 | /* Structure fields or array element */ |
594 | arg->var = strndup(str, tmp - str); | 666 | arg->var = strndup(str, tmp - str); |
595 | if (arg->var == NULL) | 667 | if (arg->var == NULL) |
596 | return -ENOMEM; | 668 | return -ENOMEM; |
669 | goodname = arg->var; | ||
597 | pr_debug("%s, ", arg->var); | 670 | pr_debug("%s, ", arg->var); |
598 | fieldp = &arg->field; | 671 | fieldp = &arg->field; |
599 | 672 | ||
@@ -601,22 +674,38 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | |||
601 | *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); | 674 | *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); |
602 | if (*fieldp == NULL) | 675 | if (*fieldp == NULL) |
603 | return -ENOMEM; | 676 | return -ENOMEM; |
604 | if (*tmp == '.') { | 677 | if (*tmp == '[') { /* Array */ |
605 | str = tmp + 1; | 678 | str = tmp; |
606 | (*fieldp)->ref = false; | 679 | (*fieldp)->index = strtol(str + 1, &tmp, 0); |
607 | } else if (tmp[1] == '>') { | ||
608 | str = tmp + 2; | ||
609 | (*fieldp)->ref = true; | 680 | (*fieldp)->ref = true; |
610 | } else { | 681 | if (*tmp != ']' || tmp == str + 1) { |
611 | semantic_error("Argument parse error: %s\n", str); | 682 | semantic_error("Array index must be a" |
612 | return -EINVAL; | 683 | " number.\n"); |
684 | return -EINVAL; | ||
685 | } | ||
686 | tmp++; | ||
687 | if (*tmp == '\0') | ||
688 | tmp = NULL; | ||
689 | } else { /* Structure */ | ||
690 | if (*tmp == '.') { | ||
691 | str = tmp + 1; | ||
692 | (*fieldp)->ref = false; | ||
693 | } else if (tmp[1] == '>') { | ||
694 | str = tmp + 2; | ||
695 | (*fieldp)->ref = true; | ||
696 | } else { | ||
697 | semantic_error("Argument parse error: %s\n", | ||
698 | str); | ||
699 | return -EINVAL; | ||
700 | } | ||
701 | tmp = strpbrk(str, "-.["); | ||
613 | } | 702 | } |
614 | |||
615 | tmp = strpbrk(str, "-."); | ||
616 | if (tmp) { | 703 | if (tmp) { |
617 | (*fieldp)->name = strndup(str, tmp - str); | 704 | (*fieldp)->name = strndup(str, tmp - str); |
618 | if ((*fieldp)->name == NULL) | 705 | if ((*fieldp)->name == NULL) |
619 | return -ENOMEM; | 706 | return -ENOMEM; |
707 | if (*str != '[') | ||
708 | goodname = (*fieldp)->name; | ||
620 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); | 709 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); |
621 | fieldp = &(*fieldp)->next; | 710 | fieldp = &(*fieldp)->next; |
622 | } | 711 | } |
@@ -624,11 +713,13 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | |||
624 | (*fieldp)->name = strdup(str); | 713 | (*fieldp)->name = strdup(str); |
625 | if ((*fieldp)->name == NULL) | 714 | if ((*fieldp)->name == NULL) |
626 | return -ENOMEM; | 715 | return -ENOMEM; |
716 | if (*str != '[') | ||
717 | goodname = (*fieldp)->name; | ||
627 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); | 718 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); |
628 | 719 | ||
629 | /* If no name is specified, set the last field name */ | 720 | /* If no name is specified, set the last field name (not array index)*/ |
630 | if (!arg->name) { | 721 | if (!arg->name) { |
631 | arg->name = strdup((*fieldp)->name); | 722 | arg->name = strdup(goodname); |
632 | if (arg->name == NULL) | 723 | if (arg->name == NULL) |
633 | return -ENOMEM; | 724 | return -ENOMEM; |
634 | } | 725 | } |
@@ -693,16 +784,17 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) | |||
693 | return false; | 784 | return false; |
694 | } | 785 | } |
695 | 786 | ||
696 | /* Parse kprobe_events event into struct probe_point */ | 787 | /* Parse probe_events event into struct probe_point */ |
697 | int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) | 788 | static int parse_probe_trace_command(const char *cmd, |
789 | struct probe_trace_event *tev) | ||
698 | { | 790 | { |
699 | struct kprobe_trace_point *tp = &tev->point; | 791 | struct probe_trace_point *tp = &tev->point; |
700 | char pr; | 792 | char pr; |
701 | char *p; | 793 | char *p; |
702 | int ret, i, argc; | 794 | int ret, i, argc; |
703 | char **argv; | 795 | char **argv; |
704 | 796 | ||
705 | pr_debug("Parsing kprobe_events: %s\n", cmd); | 797 | pr_debug("Parsing probe_events: %s\n", cmd); |
706 | argv = argv_split(cmd, &argc); | 798 | argv = argv_split(cmd, &argc); |
707 | if (!argv) { | 799 | if (!argv) { |
708 | pr_debug("Failed to split arguments.\n"); | 800 | pr_debug("Failed to split arguments.\n"); |
@@ -734,7 +826,7 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) | |||
734 | tp->offset = 0; | 826 | tp->offset = 0; |
735 | 827 | ||
736 | tev->nargs = argc - 2; | 828 | tev->nargs = argc - 2; |
737 | tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); | 829 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
738 | if (tev->args == NULL) { | 830 | if (tev->args == NULL) { |
739 | ret = -ENOMEM; | 831 | ret = -ENOMEM; |
740 | goto out; | 832 | goto out; |
@@ -776,8 +868,11 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | |||
776 | len -= ret; | 868 | len -= ret; |
777 | 869 | ||
778 | while (field) { | 870 | while (field) { |
779 | ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", | 871 | if (field->name[0] == '[') |
780 | field->name); | 872 | ret = e_snprintf(tmp, len, "%s", field->name); |
873 | else | ||
874 | ret = e_snprintf(tmp, len, "%s%s", | ||
875 | field->ref ? "->" : ".", field->name); | ||
781 | if (ret <= 0) | 876 | if (ret <= 0) |
782 | goto error; | 877 | goto error; |
783 | tmp += ret; | 878 | tmp += ret; |
@@ -877,13 +972,13 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev) | |||
877 | } | 972 | } |
878 | #endif | 973 | #endif |
879 | 974 | ||
880 | static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, | 975 | static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref, |
881 | char **buf, size_t *buflen, | 976 | char **buf, size_t *buflen, |
882 | int depth) | 977 | int depth) |
883 | { | 978 | { |
884 | int ret; | 979 | int ret; |
885 | if (ref->next) { | 980 | if (ref->next) { |
886 | depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, | 981 | depth = __synthesize_probe_trace_arg_ref(ref->next, buf, |
887 | buflen, depth + 1); | 982 | buflen, depth + 1); |
888 | if (depth < 0) | 983 | if (depth < 0) |
889 | goto out; | 984 | goto out; |
@@ -901,9 +996,10 @@ out: | |||
901 | 996 | ||
902 | } | 997 | } |
903 | 998 | ||
904 | static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | 999 | static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, |
905 | char *buf, size_t buflen) | 1000 | char *buf, size_t buflen) |
906 | { | 1001 | { |
1002 | struct probe_trace_arg_ref *ref = arg->ref; | ||
907 | int ret, depth = 0; | 1003 | int ret, depth = 0; |
908 | char *tmp = buf; | 1004 | char *tmp = buf; |
909 | 1005 | ||
@@ -917,16 +1013,24 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | |||
917 | buf += ret; | 1013 | buf += ret; |
918 | buflen -= ret; | 1014 | buflen -= ret; |
919 | 1015 | ||
1016 | /* Special case: @XXX */ | ||
1017 | if (arg->value[0] == '@' && arg->ref) | ||
1018 | ref = ref->next; | ||
1019 | |||
920 | /* Dereferencing arguments */ | 1020 | /* Dereferencing arguments */ |
921 | if (arg->ref) { | 1021 | if (ref) { |
922 | depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, | 1022 | depth = __synthesize_probe_trace_arg_ref(ref, &buf, |
923 | &buflen, 1); | 1023 | &buflen, 1); |
924 | if (depth < 0) | 1024 | if (depth < 0) |
925 | return depth; | 1025 | return depth; |
926 | } | 1026 | } |
927 | 1027 | ||
928 | /* Print argument value */ | 1028 | /* Print argument value */ |
929 | ret = e_snprintf(buf, buflen, "%s", arg->value); | 1029 | if (arg->value[0] == '@' && arg->ref) |
1030 | ret = e_snprintf(buf, buflen, "%s%+ld", arg->value, | ||
1031 | arg->ref->offset); | ||
1032 | else | ||
1033 | ret = e_snprintf(buf, buflen, "%s", arg->value); | ||
930 | if (ret < 0) | 1034 | if (ret < 0) |
931 | return ret; | 1035 | return ret; |
932 | buf += ret; | 1036 | buf += ret; |
@@ -951,9 +1055,9 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | |||
951 | return buf - tmp; | 1055 | return buf - tmp; |
952 | } | 1056 | } |
953 | 1057 | ||
954 | char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) | 1058 | char *synthesize_probe_trace_command(struct probe_trace_event *tev) |
955 | { | 1059 | { |
956 | struct kprobe_trace_point *tp = &tev->point; | 1060 | struct probe_trace_point *tp = &tev->point; |
957 | char *buf; | 1061 | char *buf; |
958 | int i, len, ret; | 1062 | int i, len, ret; |
959 | 1063 | ||
@@ -969,7 +1073,7 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) | |||
969 | goto error; | 1073 | goto error; |
970 | 1074 | ||
971 | for (i = 0; i < tev->nargs; i++) { | 1075 | for (i = 0; i < tev->nargs; i++) { |
972 | ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, | 1076 | ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, |
973 | MAX_CMDLEN - len); | 1077 | MAX_CMDLEN - len); |
974 | if (ret <= 0) | 1078 | if (ret <= 0) |
975 | goto error; | 1079 | goto error; |
@@ -982,7 +1086,7 @@ error: | |||
982 | return NULL; | 1086 | return NULL; |
983 | } | 1087 | } |
984 | 1088 | ||
985 | int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | 1089 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, |
986 | struct perf_probe_event *pev) | 1090 | struct perf_probe_event *pev) |
987 | { | 1091 | { |
988 | char buf[64] = ""; | 1092 | char buf[64] = ""; |
@@ -995,7 +1099,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | |||
995 | return -ENOMEM; | 1099 | return -ENOMEM; |
996 | 1100 | ||
997 | /* Convert trace_point to probe_point */ | 1101 | /* Convert trace_point to probe_point */ |
998 | ret = convert_to_perf_probe_point(&tev->point, &pev->point); | 1102 | ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); |
999 | if (ret < 0) | 1103 | if (ret < 0) |
1000 | return ret; | 1104 | return ret; |
1001 | 1105 | ||
@@ -1008,7 +1112,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | |||
1008 | if (tev->args[i].name) | 1112 | if (tev->args[i].name) |
1009 | pev->args[i].name = strdup(tev->args[i].name); | 1113 | pev->args[i].name = strdup(tev->args[i].name); |
1010 | else { | 1114 | else { |
1011 | ret = synthesize_kprobe_trace_arg(&tev->args[i], | 1115 | ret = synthesize_probe_trace_arg(&tev->args[i], |
1012 | buf, 64); | 1116 | buf, 64); |
1013 | pev->args[i].name = strdup(buf); | 1117 | pev->args[i].name = strdup(buf); |
1014 | } | 1118 | } |
@@ -1059,9 +1163,9 @@ void clear_perf_probe_event(struct perf_probe_event *pev) | |||
1059 | memset(pev, 0, sizeof(*pev)); | 1163 | memset(pev, 0, sizeof(*pev)); |
1060 | } | 1164 | } |
1061 | 1165 | ||
1062 | void clear_kprobe_trace_event(struct kprobe_trace_event *tev) | 1166 | static void clear_probe_trace_event(struct probe_trace_event *tev) |
1063 | { | 1167 | { |
1064 | struct kprobe_trace_arg_ref *ref, *next; | 1168 | struct probe_trace_arg_ref *ref, *next; |
1065 | int i; | 1169 | int i; |
1066 | 1170 | ||
1067 | if (tev->event) | 1171 | if (tev->event) |
@@ -1122,7 +1226,7 @@ static int open_kprobe_events(bool readwrite) | |||
1122 | } | 1226 | } |
1123 | 1227 | ||
1124 | /* Get raw string list of current kprobe_events */ | 1228 | /* Get raw string list of current kprobe_events */ |
1125 | static struct strlist *get_kprobe_trace_command_rawlist(int fd) | 1229 | static struct strlist *get_probe_trace_command_rawlist(int fd) |
1126 | { | 1230 | { |
1127 | int ret, idx; | 1231 | int ret, idx; |
1128 | FILE *fp; | 1232 | FILE *fp; |
@@ -1190,7 +1294,7 @@ static int show_perf_probe_event(struct perf_probe_event *pev) | |||
1190 | int show_perf_probe_events(void) | 1294 | int show_perf_probe_events(void) |
1191 | { | 1295 | { |
1192 | int fd, ret; | 1296 | int fd, ret; |
1193 | struct kprobe_trace_event tev; | 1297 | struct probe_trace_event tev; |
1194 | struct perf_probe_event pev; | 1298 | struct perf_probe_event pev; |
1195 | struct strlist *rawlist; | 1299 | struct strlist *rawlist; |
1196 | struct str_node *ent; | 1300 | struct str_node *ent; |
@@ -1207,20 +1311,20 @@ int show_perf_probe_events(void) | |||
1207 | if (fd < 0) | 1311 | if (fd < 0) |
1208 | return fd; | 1312 | return fd; |
1209 | 1313 | ||
1210 | rawlist = get_kprobe_trace_command_rawlist(fd); | 1314 | rawlist = get_probe_trace_command_rawlist(fd); |
1211 | close(fd); | 1315 | close(fd); |
1212 | if (!rawlist) | 1316 | if (!rawlist) |
1213 | return -ENOENT; | 1317 | return -ENOENT; |
1214 | 1318 | ||
1215 | strlist__for_each(ent, rawlist) { | 1319 | strlist__for_each(ent, rawlist) { |
1216 | ret = parse_kprobe_trace_command(ent->s, &tev); | 1320 | ret = parse_probe_trace_command(ent->s, &tev); |
1217 | if (ret >= 0) { | 1321 | if (ret >= 0) { |
1218 | ret = convert_to_perf_probe_event(&tev, &pev); | 1322 | ret = convert_to_perf_probe_event(&tev, &pev); |
1219 | if (ret >= 0) | 1323 | if (ret >= 0) |
1220 | ret = show_perf_probe_event(&pev); | 1324 | ret = show_perf_probe_event(&pev); |
1221 | } | 1325 | } |
1222 | clear_perf_probe_event(&pev); | 1326 | clear_perf_probe_event(&pev); |
1223 | clear_kprobe_trace_event(&tev); | 1327 | clear_probe_trace_event(&tev); |
1224 | if (ret < 0) | 1328 | if (ret < 0) |
1225 | break; | 1329 | break; |
1226 | } | 1330 | } |
@@ -1230,20 +1334,19 @@ int show_perf_probe_events(void) | |||
1230 | } | 1334 | } |
1231 | 1335 | ||
1232 | /* Get current perf-probe event names */ | 1336 | /* Get current perf-probe event names */ |
1233 | static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) | 1337 | static struct strlist *get_probe_trace_event_names(int fd, bool include_group) |
1234 | { | 1338 | { |
1235 | char buf[128]; | 1339 | char buf[128]; |
1236 | struct strlist *sl, *rawlist; | 1340 | struct strlist *sl, *rawlist; |
1237 | struct str_node *ent; | 1341 | struct str_node *ent; |
1238 | struct kprobe_trace_event tev; | 1342 | struct probe_trace_event tev; |
1239 | int ret = 0; | 1343 | int ret = 0; |
1240 | 1344 | ||
1241 | memset(&tev, 0, sizeof(tev)); | 1345 | memset(&tev, 0, sizeof(tev)); |
1242 | 1346 | rawlist = get_probe_trace_command_rawlist(fd); | |
1243 | rawlist = get_kprobe_trace_command_rawlist(fd); | ||
1244 | sl = strlist__new(true, NULL); | 1347 | sl = strlist__new(true, NULL); |
1245 | strlist__for_each(ent, rawlist) { | 1348 | strlist__for_each(ent, rawlist) { |
1246 | ret = parse_kprobe_trace_command(ent->s, &tev); | 1349 | ret = parse_probe_trace_command(ent->s, &tev); |
1247 | if (ret < 0) | 1350 | if (ret < 0) |
1248 | break; | 1351 | break; |
1249 | if (include_group) { | 1352 | if (include_group) { |
@@ -1253,7 +1356,7 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) | |||
1253 | ret = strlist__add(sl, buf); | 1356 | ret = strlist__add(sl, buf); |
1254 | } else | 1357 | } else |
1255 | ret = strlist__add(sl, tev.event); | 1358 | ret = strlist__add(sl, tev.event); |
1256 | clear_kprobe_trace_event(&tev); | 1359 | clear_probe_trace_event(&tev); |
1257 | if (ret < 0) | 1360 | if (ret < 0) |
1258 | break; | 1361 | break; |
1259 | } | 1362 | } |
@@ -1266,13 +1369,13 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) | |||
1266 | return sl; | 1369 | return sl; |
1267 | } | 1370 | } |
1268 | 1371 | ||
1269 | static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) | 1372 | static int write_probe_trace_event(int fd, struct probe_trace_event *tev) |
1270 | { | 1373 | { |
1271 | int ret = 0; | 1374 | int ret = 0; |
1272 | char *buf = synthesize_kprobe_trace_command(tev); | 1375 | char *buf = synthesize_probe_trace_command(tev); |
1273 | 1376 | ||
1274 | if (!buf) { | 1377 | if (!buf) { |
1275 | pr_debug("Failed to synthesize kprobe trace event.\n"); | 1378 | pr_debug("Failed to synthesize probe trace event.\n"); |
1276 | return -EINVAL; | 1379 | return -EINVAL; |
1277 | } | 1380 | } |
1278 | 1381 | ||
@@ -1325,12 +1428,12 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
1325 | return ret; | 1428 | return ret; |
1326 | } | 1429 | } |
1327 | 1430 | ||
1328 | static int __add_kprobe_trace_events(struct perf_probe_event *pev, | 1431 | static int __add_probe_trace_events(struct perf_probe_event *pev, |
1329 | struct kprobe_trace_event *tevs, | 1432 | struct probe_trace_event *tevs, |
1330 | int ntevs, bool allow_suffix) | 1433 | int ntevs, bool allow_suffix) |
1331 | { | 1434 | { |
1332 | int i, fd, ret; | 1435 | int i, fd, ret; |
1333 | struct kprobe_trace_event *tev = NULL; | 1436 | struct probe_trace_event *tev = NULL; |
1334 | char buf[64]; | 1437 | char buf[64]; |
1335 | const char *event, *group; | 1438 | const char *event, *group; |
1336 | struct strlist *namelist; | 1439 | struct strlist *namelist; |
@@ -1339,7 +1442,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, | |||
1339 | if (fd < 0) | 1442 | if (fd < 0) |
1340 | return fd; | 1443 | return fd; |
1341 | /* Get current event names */ | 1444 | /* Get current event names */ |
1342 | namelist = get_kprobe_trace_event_names(fd, false); | 1445 | namelist = get_probe_trace_event_names(fd, false); |
1343 | if (!namelist) { | 1446 | if (!namelist) { |
1344 | pr_debug("Failed to get current event list.\n"); | 1447 | pr_debug("Failed to get current event list.\n"); |
1345 | return -EIO; | 1448 | return -EIO; |
@@ -1374,7 +1477,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, | |||
1374 | ret = -ENOMEM; | 1477 | ret = -ENOMEM; |
1375 | break; | 1478 | break; |
1376 | } | 1479 | } |
1377 | ret = write_kprobe_trace_event(fd, tev); | 1480 | ret = write_probe_trace_event(fd, tev); |
1378 | if (ret < 0) | 1481 | if (ret < 0) |
1379 | break; | 1482 | break; |
1380 | /* Add added event name to namelist */ | 1483 | /* Add added event name to namelist */ |
@@ -1411,21 +1514,21 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, | |||
1411 | return ret; | 1514 | return ret; |
1412 | } | 1515 | } |
1413 | 1516 | ||
1414 | static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, | 1517 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
1415 | struct kprobe_trace_event **tevs, | 1518 | struct probe_trace_event **tevs, |
1416 | int max_tevs) | 1519 | int max_tevs) |
1417 | { | 1520 | { |
1418 | struct symbol *sym; | 1521 | struct symbol *sym; |
1419 | int ret = 0, i; | 1522 | int ret = 0, i; |
1420 | struct kprobe_trace_event *tev; | 1523 | struct probe_trace_event *tev; |
1421 | 1524 | ||
1422 | /* Convert perf_probe_event with debuginfo */ | 1525 | /* Convert perf_probe_event with debuginfo */ |
1423 | ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs); | 1526 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); |
1424 | if (ret != 0) | 1527 | if (ret != 0) |
1425 | return ret; | 1528 | return ret; |
1426 | 1529 | ||
1427 | /* Allocate trace event buffer */ | 1530 | /* Allocate trace event buffer */ |
1428 | tev = *tevs = zalloc(sizeof(struct kprobe_trace_event)); | 1531 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); |
1429 | if (tev == NULL) | 1532 | if (tev == NULL) |
1430 | return -ENOMEM; | 1533 | return -ENOMEM; |
1431 | 1534 | ||
@@ -1438,7 +1541,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, | |||
1438 | tev->point.offset = pev->point.offset; | 1541 | tev->point.offset = pev->point.offset; |
1439 | tev->nargs = pev->nargs; | 1542 | tev->nargs = pev->nargs; |
1440 | if (tev->nargs) { | 1543 | if (tev->nargs) { |
1441 | tev->args = zalloc(sizeof(struct kprobe_trace_arg) | 1544 | tev->args = zalloc(sizeof(struct probe_trace_arg) |
1442 | * tev->nargs); | 1545 | * tev->nargs); |
1443 | if (tev->args == NULL) { | 1546 | if (tev->args == NULL) { |
1444 | ret = -ENOMEM; | 1547 | ret = -ENOMEM; |
@@ -1479,7 +1582,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, | |||
1479 | 1582 | ||
1480 | return 1; | 1583 | return 1; |
1481 | error: | 1584 | error: |
1482 | clear_kprobe_trace_event(tev); | 1585 | clear_probe_trace_event(tev); |
1483 | free(tev); | 1586 | free(tev); |
1484 | *tevs = NULL; | 1587 | *tevs = NULL; |
1485 | return ret; | 1588 | return ret; |
@@ -1487,7 +1590,7 @@ error: | |||
1487 | 1590 | ||
1488 | struct __event_package { | 1591 | struct __event_package { |
1489 | struct perf_probe_event *pev; | 1592 | struct perf_probe_event *pev; |
1490 | struct kprobe_trace_event *tevs; | 1593 | struct probe_trace_event *tevs; |
1491 | int ntevs; | 1594 | int ntevs; |
1492 | }; | 1595 | }; |
1493 | 1596 | ||
@@ -1510,7 +1613,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1510 | for (i = 0; i < npevs; i++) { | 1613 | for (i = 0; i < npevs; i++) { |
1511 | pkgs[i].pev = &pevs[i]; | 1614 | pkgs[i].pev = &pevs[i]; |
1512 | /* Convert with or without debuginfo */ | 1615 | /* Convert with or without debuginfo */ |
1513 | ret = convert_to_kprobe_trace_events(pkgs[i].pev, | 1616 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
1514 | &pkgs[i].tevs, max_tevs); | 1617 | &pkgs[i].tevs, max_tevs); |
1515 | if (ret < 0) | 1618 | if (ret < 0) |
1516 | goto end; | 1619 | goto end; |
@@ -1519,24 +1622,24 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1519 | 1622 | ||
1520 | /* Loop 2: add all events */ | 1623 | /* Loop 2: add all events */ |
1521 | for (i = 0; i < npevs && ret >= 0; i++) | 1624 | for (i = 0; i < npevs && ret >= 0; i++) |
1522 | ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1625 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
1523 | pkgs[i].ntevs, force_add); | 1626 | pkgs[i].ntevs, force_add); |
1524 | end: | 1627 | end: |
1525 | /* Loop 3: cleanup trace events */ | 1628 | /* Loop 3: cleanup trace events */ |
1526 | for (i = 0; i < npevs; i++) | 1629 | for (i = 0; i < npevs; i++) |
1527 | for (j = 0; j < pkgs[i].ntevs; j++) | 1630 | for (j = 0; j < pkgs[i].ntevs; j++) |
1528 | clear_kprobe_trace_event(&pkgs[i].tevs[j]); | 1631 | clear_probe_trace_event(&pkgs[i].tevs[j]); |
1529 | 1632 | ||
1530 | return ret; | 1633 | return ret; |
1531 | } | 1634 | } |
1532 | 1635 | ||
1533 | static int __del_trace_kprobe_event(int fd, struct str_node *ent) | 1636 | static int __del_trace_probe_event(int fd, struct str_node *ent) |
1534 | { | 1637 | { |
1535 | char *p; | 1638 | char *p; |
1536 | char buf[128]; | 1639 | char buf[128]; |
1537 | int ret; | 1640 | int ret; |
1538 | 1641 | ||
1539 | /* Convert from perf-probe event to trace-kprobe event */ | 1642 | /* Convert from perf-probe event to trace-probe event */ |
1540 | ret = e_snprintf(buf, 128, "-:%s", ent->s); | 1643 | ret = e_snprintf(buf, 128, "-:%s", ent->s); |
1541 | if (ret < 0) | 1644 | if (ret < 0) |
1542 | goto error; | 1645 | goto error; |
@@ -1562,7 +1665,7 @@ error: | |||
1562 | return ret; | 1665 | return ret; |
1563 | } | 1666 | } |
1564 | 1667 | ||
1565 | static int del_trace_kprobe_event(int fd, const char *group, | 1668 | static int del_trace_probe_event(int fd, const char *group, |
1566 | const char *event, struct strlist *namelist) | 1669 | const char *event, struct strlist *namelist) |
1567 | { | 1670 | { |
1568 | char buf[128]; | 1671 | char buf[128]; |
@@ -1579,7 +1682,7 @@ static int del_trace_kprobe_event(int fd, const char *group, | |||
1579 | strlist__for_each_safe(ent, n, namelist) | 1682 | strlist__for_each_safe(ent, n, namelist) |
1580 | if (strglobmatch(ent->s, buf)) { | 1683 | if (strglobmatch(ent->s, buf)) { |
1581 | found++; | 1684 | found++; |
1582 | ret = __del_trace_kprobe_event(fd, ent); | 1685 | ret = __del_trace_probe_event(fd, ent); |
1583 | if (ret < 0) | 1686 | if (ret < 0) |
1584 | break; | 1687 | break; |
1585 | strlist__remove(namelist, ent); | 1688 | strlist__remove(namelist, ent); |
@@ -1588,7 +1691,7 @@ static int del_trace_kprobe_event(int fd, const char *group, | |||
1588 | ent = strlist__find(namelist, buf); | 1691 | ent = strlist__find(namelist, buf); |
1589 | if (ent) { | 1692 | if (ent) { |
1590 | found++; | 1693 | found++; |
1591 | ret = __del_trace_kprobe_event(fd, ent); | 1694 | ret = __del_trace_probe_event(fd, ent); |
1592 | if (ret >= 0) | 1695 | if (ret >= 0) |
1593 | strlist__remove(namelist, ent); | 1696 | strlist__remove(namelist, ent); |
1594 | } | 1697 | } |
@@ -1612,7 +1715,7 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1612 | return fd; | 1715 | return fd; |
1613 | 1716 | ||
1614 | /* Get current event names */ | 1717 | /* Get current event names */ |
1615 | namelist = get_kprobe_trace_event_names(fd, true); | 1718 | namelist = get_probe_trace_event_names(fd, true); |
1616 | if (namelist == NULL) | 1719 | if (namelist == NULL) |
1617 | return -EINVAL; | 1720 | return -EINVAL; |
1618 | 1721 | ||
@@ -1633,7 +1736,7 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1633 | event = str; | 1736 | event = str; |
1634 | } | 1737 | } |
1635 | pr_debug("Group: %s, Event: %s\n", group, event); | 1738 | pr_debug("Group: %s, Event: %s\n", group, event); |
1636 | ret = del_trace_kprobe_event(fd, group, event, namelist); | 1739 | ret = del_trace_probe_event(fd, group, event, namelist); |
1637 | free(str); | 1740 | free(str); |
1638 | if (ret < 0) | 1741 | if (ret < 0) |
1639 | break; | 1742 | break; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index e9db1a214ca4..5af39243a25b 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -7,33 +7,33 @@ | |||
7 | extern bool probe_event_dry_run; | 7 | extern bool probe_event_dry_run; |
8 | 8 | ||
9 | /* kprobe-tracer tracing point */ | 9 | /* kprobe-tracer tracing point */ |
10 | struct kprobe_trace_point { | 10 | struct probe_trace_point { |
11 | char *symbol; /* Base symbol */ | 11 | char *symbol; /* Base symbol */ |
12 | unsigned long offset; /* Offset from symbol */ | 12 | unsigned long offset; /* Offset from symbol */ |
13 | bool retprobe; /* Return probe flag */ | 13 | bool retprobe; /* Return probe flag */ |
14 | }; | 14 | }; |
15 | 15 | ||
16 | /* kprobe-tracer tracing argument referencing offset */ | 16 | /* probe-tracer tracing argument referencing offset */ |
17 | struct kprobe_trace_arg_ref { | 17 | struct probe_trace_arg_ref { |
18 | struct kprobe_trace_arg_ref *next; /* Next reference */ | 18 | struct probe_trace_arg_ref *next; /* Next reference */ |
19 | long offset; /* Offset value */ | 19 | long offset; /* Offset value */ |
20 | }; | 20 | }; |
21 | 21 | ||
22 | /* kprobe-tracer tracing argument */ | 22 | /* kprobe-tracer tracing argument */ |
23 | struct kprobe_trace_arg { | 23 | struct probe_trace_arg { |
24 | char *name; /* Argument name */ | 24 | char *name; /* Argument name */ |
25 | char *value; /* Base value */ | 25 | char *value; /* Base value */ |
26 | char *type; /* Type name */ | 26 | char *type; /* Type name */ |
27 | struct kprobe_trace_arg_ref *ref; /* Referencing offset */ | 27 | struct probe_trace_arg_ref *ref; /* Referencing offset */ |
28 | }; | 28 | }; |
29 | 29 | ||
30 | /* kprobe-tracer tracing event (point + arg) */ | 30 | /* kprobe-tracer tracing event (point + arg) */ |
31 | struct kprobe_trace_event { | 31 | struct probe_trace_event { |
32 | char *event; /* Event name */ | 32 | char *event; /* Event name */ |
33 | char *group; /* Group name */ | 33 | char *group; /* Group name */ |
34 | struct kprobe_trace_point point; /* Trace point */ | 34 | struct probe_trace_point point; /* Trace point */ |
35 | int nargs; /* Number of args */ | 35 | int nargs; /* Number of args */ |
36 | struct kprobe_trace_arg *args; /* Arguments */ | 36 | struct probe_trace_arg *args; /* Arguments */ |
37 | }; | 37 | }; |
38 | 38 | ||
39 | /* Perf probe probing point */ | 39 | /* Perf probe probing point */ |
@@ -50,6 +50,7 @@ struct perf_probe_point { | |||
50 | struct perf_probe_arg_field { | 50 | struct perf_probe_arg_field { |
51 | struct perf_probe_arg_field *next; /* Next field */ | 51 | struct perf_probe_arg_field *next; /* Next field */ |
52 | char *name; /* Name of the field */ | 52 | char *name; /* Name of the field */ |
53 | long index; /* Array index number */ | ||
53 | bool ref; /* Referencing flag */ | 54 | bool ref; /* Referencing flag */ |
54 | }; | 55 | }; |
55 | 56 | ||
@@ -85,31 +86,25 @@ struct line_range { | |||
85 | int end; /* End line number */ | 86 | int end; /* End line number */ |
86 | int offset; /* Start line offset */ | 87 | int offset; /* Start line offset */ |
87 | char *path; /* Real path name */ | 88 | char *path; /* Real path name */ |
89 | char *comp_dir; /* Compile directory */ | ||
88 | struct list_head line_list; /* Visible lines */ | 90 | struct list_head line_list; /* Visible lines */ |
89 | }; | 91 | }; |
90 | 92 | ||
91 | /* Command string to events */ | 93 | /* Command string to events */ |
92 | extern int parse_perf_probe_command(const char *cmd, | 94 | extern int parse_perf_probe_command(const char *cmd, |
93 | struct perf_probe_event *pev); | 95 | struct perf_probe_event *pev); |
94 | extern int parse_kprobe_trace_command(const char *cmd, | ||
95 | struct kprobe_trace_event *tev); | ||
96 | 96 | ||
97 | /* Events to command string */ | 97 | /* Events to command string */ |
98 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); | 98 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); |
99 | extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); | 99 | extern char *synthesize_probe_trace_command(struct probe_trace_event *tev); |
100 | extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, | 100 | extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, |
101 | size_t len); | 101 | size_t len); |
102 | 102 | ||
103 | /* Check the perf_probe_event needs debuginfo */ | 103 | /* Check the perf_probe_event needs debuginfo */ |
104 | extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); | 104 | extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); |
105 | 105 | ||
106 | /* Convert from kprobe_trace_event to perf_probe_event */ | ||
107 | extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | ||
108 | struct perf_probe_event *pev); | ||
109 | |||
110 | /* Release event contents */ | 106 | /* Release event contents */ |
111 | extern void clear_perf_probe_event(struct perf_probe_event *pev); | 107 | extern void clear_perf_probe_event(struct perf_probe_event *pev); |
112 | extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); | ||
113 | 108 | ||
114 | /* Command string to line-range */ | 109 | /* Command string to line-range */ |
115 | extern int parse_line_range_desc(const char *cmd, struct line_range *lr); | 110 | extern int parse_line_range_desc(const char *cmd, struct line_range *lr); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index d964cb199c67..840f1aabbb74 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include "event.h" | 37 | #include "event.h" |
38 | #include "debug.h" | 38 | #include "debug.h" |
39 | #include "util.h" | 39 | #include "util.h" |
40 | #include "symbol.h" | ||
40 | #include "probe-finder.h" | 41 | #include "probe-finder.h" |
41 | 42 | ||
42 | /* Kprobe tracer basic type is up to u64 */ | 43 | /* Kprobe tracer basic type is up to u64 */ |
@@ -143,12 +144,21 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | |||
143 | return src; | 144 | return src; |
144 | } | 145 | } |
145 | 146 | ||
147 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | ||
148 | static const char *cu_get_comp_dir(Dwarf_Die *cu_die) | ||
149 | { | ||
150 | Dwarf_Attribute attr; | ||
151 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | ||
152 | return NULL; | ||
153 | return dwarf_formstring(&attr); | ||
154 | } | ||
155 | |||
146 | /* Compare diename and tname */ | 156 | /* Compare diename and tname */ |
147 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | 157 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) |
148 | { | 158 | { |
149 | const char *name; | 159 | const char *name; |
150 | name = dwarf_diename(dw_die); | 160 | name = dwarf_diename(dw_die); |
151 | return name ? strcmp(tname, name) : -1; | 161 | return name ? (strcmp(tname, name) == 0) : false; |
152 | } | 162 | } |
153 | 163 | ||
154 | /* Get type die, but skip qualifiers and typedef */ | 164 | /* Get type die, but skip qualifiers and typedef */ |
@@ -319,7 +329,7 @@ static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | |||
319 | tag = dwarf_tag(die_mem); | 329 | tag = dwarf_tag(die_mem); |
320 | if ((tag == DW_TAG_formal_parameter || | 330 | if ((tag == DW_TAG_formal_parameter || |
321 | tag == DW_TAG_variable) && | 331 | tag == DW_TAG_variable) && |
322 | (die_compare_name(die_mem, name) == 0)) | 332 | die_compare_name(die_mem, name)) |
323 | return DIE_FIND_CB_FOUND; | 333 | return DIE_FIND_CB_FOUND; |
324 | 334 | ||
325 | return DIE_FIND_CB_CONTINUE; | 335 | return DIE_FIND_CB_CONTINUE; |
@@ -338,7 +348,7 @@ static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | |||
338 | const char *name = data; | 348 | const char *name = data; |
339 | 349 | ||
340 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | 350 | if ((dwarf_tag(die_mem) == DW_TAG_member) && |
341 | (die_compare_name(die_mem, name) == 0)) | 351 | die_compare_name(die_mem, name)) |
342 | return DIE_FIND_CB_FOUND; | 352 | return DIE_FIND_CB_FOUND; |
343 | 353 | ||
344 | return DIE_FIND_CB_SIBLING; | 354 | return DIE_FIND_CB_SIBLING; |
@@ -356,14 +366,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | |||
356 | * Probe finder related functions | 366 | * Probe finder related functions |
357 | */ | 367 | */ |
358 | 368 | ||
369 | static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) | ||
370 | { | ||
371 | struct probe_trace_arg_ref *ref; | ||
372 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); | ||
373 | if (ref != NULL) | ||
374 | ref->offset = offs; | ||
375 | return ref; | ||
376 | } | ||
377 | |||
359 | /* Show a location */ | 378 | /* Show a location */ |
360 | static int convert_location(Dwarf_Op *op, struct probe_finder *pf) | 379 | static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) |
361 | { | 380 | { |
381 | Dwarf_Attribute attr; | ||
382 | Dwarf_Op *op; | ||
383 | size_t nops; | ||
362 | unsigned int regn; | 384 | unsigned int regn; |
363 | Dwarf_Word offs = 0; | 385 | Dwarf_Word offs = 0; |
364 | bool ref = false; | 386 | bool ref = false; |
365 | const char *regs; | 387 | const char *regs; |
366 | struct kprobe_trace_arg *tvar = pf->tvar; | 388 | struct probe_trace_arg *tvar = pf->tvar; |
389 | int ret; | ||
390 | |||
391 | /* TODO: handle more than 1 exprs */ | ||
392 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || | ||
393 | dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || | ||
394 | nops == 0) { | ||
395 | /* TODO: Support const_value */ | ||
396 | pr_err("Failed to find the location of %s at this address.\n" | ||
397 | " Perhaps, it has been optimized out.\n", pf->pvar->var); | ||
398 | return -ENOENT; | ||
399 | } | ||
400 | |||
401 | if (op->atom == DW_OP_addr) { | ||
402 | /* Static variables on memory (not stack), make @varname */ | ||
403 | ret = strlen(dwarf_diename(vr_die)); | ||
404 | tvar->value = zalloc(ret + 2); | ||
405 | if (tvar->value == NULL) | ||
406 | return -ENOMEM; | ||
407 | snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); | ||
408 | tvar->ref = alloc_trace_arg_ref((long)offs); | ||
409 | if (tvar->ref == NULL) | ||
410 | return -ENOMEM; | ||
411 | return 0; | ||
412 | } | ||
367 | 413 | ||
368 | /* If this is based on frame buffer, set the offset */ | 414 | /* If this is based on frame buffer, set the offset */ |
369 | if (op->atom == DW_OP_fbreg) { | 415 | if (op->atom == DW_OP_fbreg) { |
@@ -405,27 +451,72 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) | |||
405 | return -ENOMEM; | 451 | return -ENOMEM; |
406 | 452 | ||
407 | if (ref) { | 453 | if (ref) { |
408 | tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 454 | tvar->ref = alloc_trace_arg_ref((long)offs); |
409 | if (tvar->ref == NULL) | 455 | if (tvar->ref == NULL) |
410 | return -ENOMEM; | 456 | return -ENOMEM; |
411 | tvar->ref->offset = (long)offs; | ||
412 | } | 457 | } |
413 | return 0; | 458 | return 0; |
414 | } | 459 | } |
415 | 460 | ||
416 | static int convert_variable_type(Dwarf_Die *vr_die, | 461 | static int convert_variable_type(Dwarf_Die *vr_die, |
417 | struct kprobe_trace_arg *targ) | 462 | struct probe_trace_arg *tvar, |
463 | const char *cast) | ||
418 | { | 464 | { |
465 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | ||
419 | Dwarf_Die type; | 466 | Dwarf_Die type; |
420 | char buf[16]; | 467 | char buf[16]; |
421 | int ret; | 468 | int ret; |
422 | 469 | ||
470 | /* TODO: check all types */ | ||
471 | if (cast && strcmp(cast, "string") != 0) { | ||
472 | /* Non string type is OK */ | ||
473 | tvar->type = strdup(cast); | ||
474 | return (tvar->type == NULL) ? -ENOMEM : 0; | ||
475 | } | ||
476 | |||
423 | if (die_get_real_type(vr_die, &type) == NULL) { | 477 | if (die_get_real_type(vr_die, &type) == NULL) { |
424 | pr_warning("Failed to get a type information of %s.\n", | 478 | pr_warning("Failed to get a type information of %s.\n", |
425 | dwarf_diename(vr_die)); | 479 | dwarf_diename(vr_die)); |
426 | return -ENOENT; | 480 | return -ENOENT; |
427 | } | 481 | } |
428 | 482 | ||
483 | pr_debug("%s type is %s.\n", | ||
484 | dwarf_diename(vr_die), dwarf_diename(&type)); | ||
485 | |||
486 | if (cast && strcmp(cast, "string") == 0) { /* String type */ | ||
487 | ret = dwarf_tag(&type); | ||
488 | if (ret != DW_TAG_pointer_type && | ||
489 | ret != DW_TAG_array_type) { | ||
490 | pr_warning("Failed to cast into string: " | ||
491 | "%s(%s) is not a pointer nor array.", | ||
492 | dwarf_diename(vr_die), dwarf_diename(&type)); | ||
493 | return -EINVAL; | ||
494 | } | ||
495 | if (ret == DW_TAG_pointer_type) { | ||
496 | if (die_get_real_type(&type, &type) == NULL) { | ||
497 | pr_warning("Failed to get a type information."); | ||
498 | return -ENOENT; | ||
499 | } | ||
500 | while (*ref_ptr) | ||
501 | ref_ptr = &(*ref_ptr)->next; | ||
502 | /* Add new reference with offset +0 */ | ||
503 | *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref)); | ||
504 | if (*ref_ptr == NULL) { | ||
505 | pr_warning("Out of memory error\n"); | ||
506 | return -ENOMEM; | ||
507 | } | ||
508 | } | ||
509 | if (!die_compare_name(&type, "char") && | ||
510 | !die_compare_name(&type, "unsigned char")) { | ||
511 | pr_warning("Failed to cast into string: " | ||
512 | "%s is not (unsigned) char *.", | ||
513 | dwarf_diename(vr_die)); | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | tvar->type = strdup(cast); | ||
517 | return (tvar->type == NULL) ? -ENOMEM : 0; | ||
518 | } | ||
519 | |||
429 | ret = die_get_byte_size(&type) * 8; | 520 | ret = die_get_byte_size(&type) * 8; |
430 | if (ret) { | 521 | if (ret) { |
431 | /* Check the bitwidth */ | 522 | /* Check the bitwidth */ |
@@ -445,8 +536,8 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
445 | strerror(-ret)); | 536 | strerror(-ret)); |
446 | return ret; | 537 | return ret; |
447 | } | 538 | } |
448 | targ->type = strdup(buf); | 539 | tvar->type = strdup(buf); |
449 | if (targ->type == NULL) | 540 | if (tvar->type == NULL) |
450 | return -ENOMEM; | 541 | return -ENOMEM; |
451 | } | 542 | } |
452 | return 0; | 543 | return 0; |
@@ -454,22 +545,50 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
454 | 545 | ||
455 | static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | 546 | static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, |
456 | struct perf_probe_arg_field *field, | 547 | struct perf_probe_arg_field *field, |
457 | struct kprobe_trace_arg_ref **ref_ptr, | 548 | struct probe_trace_arg_ref **ref_ptr, |
458 | Dwarf_Die *die_mem) | 549 | Dwarf_Die *die_mem) |
459 | { | 550 | { |
460 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | 551 | struct probe_trace_arg_ref *ref = *ref_ptr; |
461 | Dwarf_Die type; | 552 | Dwarf_Die type; |
462 | Dwarf_Word offs; | 553 | Dwarf_Word offs; |
463 | int ret; | 554 | int ret, tag; |
464 | 555 | ||
465 | pr_debug("converting %s in %s\n", field->name, varname); | 556 | pr_debug("converting %s in %s\n", field->name, varname); |
466 | if (die_get_real_type(vr_die, &type) == NULL) { | 557 | if (die_get_real_type(vr_die, &type) == NULL) { |
467 | pr_warning("Failed to get the type of %s.\n", varname); | 558 | pr_warning("Failed to get the type of %s.\n", varname); |
468 | return -ENOENT; | 559 | return -ENOENT; |
469 | } | 560 | } |
470 | 561 | pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); | |
471 | /* Check the pointer and dereference */ | 562 | tag = dwarf_tag(&type); |
472 | if (dwarf_tag(&type) == DW_TAG_pointer_type) { | 563 | |
564 | if (field->name[0] == '[' && | ||
565 | (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { | ||
566 | if (field->next) | ||
567 | /* Save original type for next field */ | ||
568 | memcpy(die_mem, &type, sizeof(*die_mem)); | ||
569 | /* Get the type of this array */ | ||
570 | if (die_get_real_type(&type, &type) == NULL) { | ||
571 | pr_warning("Failed to get the type of %s.\n", varname); | ||
572 | return -ENOENT; | ||
573 | } | ||
574 | pr_debug2("Array real type: (%x)\n", | ||
575 | (unsigned)dwarf_dieoffset(&type)); | ||
576 | if (tag == DW_TAG_pointer_type) { | ||
577 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); | ||
578 | if (ref == NULL) | ||
579 | return -ENOMEM; | ||
580 | if (*ref_ptr) | ||
581 | (*ref_ptr)->next = ref; | ||
582 | else | ||
583 | *ref_ptr = ref; | ||
584 | } | ||
585 | ref->offset += die_get_byte_size(&type) * field->index; | ||
586 | if (!field->next) | ||
587 | /* Save vr_die for converting types */ | ||
588 | memcpy(die_mem, vr_die, sizeof(*die_mem)); | ||
589 | goto next; | ||
590 | } else if (tag == DW_TAG_pointer_type) { | ||
591 | /* Check the pointer and dereference */ | ||
473 | if (!field->ref) { | 592 | if (!field->ref) { |
474 | pr_err("Semantic error: %s must be referred by '->'\n", | 593 | pr_err("Semantic error: %s must be referred by '->'\n", |
475 | field->name); | 594 | field->name); |
@@ -486,7 +605,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
486 | return -EINVAL; | 605 | return -EINVAL; |
487 | } | 606 | } |
488 | 607 | ||
489 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 608 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); |
490 | if (ref == NULL) | 609 | if (ref == NULL) |
491 | return -ENOMEM; | 610 | return -ENOMEM; |
492 | if (*ref_ptr) | 611 | if (*ref_ptr) |
@@ -495,10 +614,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
495 | *ref_ptr = ref; | 614 | *ref_ptr = ref; |
496 | } else { | 615 | } else { |
497 | /* Verify it is a data structure */ | 616 | /* Verify it is a data structure */ |
498 | if (dwarf_tag(&type) != DW_TAG_structure_type) { | 617 | if (tag != DW_TAG_structure_type) { |
499 | pr_warning("%s is not a data structure.\n", varname); | 618 | pr_warning("%s is not a data structure.\n", varname); |
500 | return -EINVAL; | 619 | return -EINVAL; |
501 | } | 620 | } |
621 | if (field->name[0] == '[') { | ||
622 | pr_err("Semantic error: %s is not a pointor nor array.", | ||
623 | varname); | ||
624 | return -EINVAL; | ||
625 | } | ||
502 | if (field->ref) { | 626 | if (field->ref) { |
503 | pr_err("Semantic error: %s must be referred by '.'\n", | 627 | pr_err("Semantic error: %s must be referred by '.'\n", |
504 | field->name); | 628 | field->name); |
@@ -525,6 +649,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
525 | } | 649 | } |
526 | ref->offset += (long)offs; | 650 | ref->offset += (long)offs; |
527 | 651 | ||
652 | next: | ||
528 | /* Converting next field */ | 653 | /* Converting next field */ |
529 | if (field->next) | 654 | if (field->next) |
530 | return convert_variable_fields(die_mem, field->name, | 655 | return convert_variable_fields(die_mem, field->name, |
@@ -536,51 +661,32 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
536 | /* Show a variables in kprobe event format */ | 661 | /* Show a variables in kprobe event format */ |
537 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | 662 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
538 | { | 663 | { |
539 | Dwarf_Attribute attr; | ||
540 | Dwarf_Die die_mem; | 664 | Dwarf_Die die_mem; |
541 | Dwarf_Op *expr; | ||
542 | size_t nexpr; | ||
543 | int ret; | 665 | int ret; |
544 | 666 | ||
545 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) | 667 | pr_debug("Converting variable %s into trace event.\n", |
546 | goto error; | 668 | dwarf_diename(vr_die)); |
547 | /* TODO: handle more than 1 exprs */ | ||
548 | ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1); | ||
549 | if (ret <= 0 || nexpr == 0) | ||
550 | goto error; | ||
551 | 669 | ||
552 | ret = convert_location(expr, pf); | 670 | ret = convert_variable_location(vr_die, pf); |
553 | if (ret == 0 && pf->pvar->field) { | 671 | if (ret == 0 && pf->pvar->field) { |
554 | ret = convert_variable_fields(vr_die, pf->pvar->var, | 672 | ret = convert_variable_fields(vr_die, pf->pvar->var, |
555 | pf->pvar->field, &pf->tvar->ref, | 673 | pf->pvar->field, &pf->tvar->ref, |
556 | &die_mem); | 674 | &die_mem); |
557 | vr_die = &die_mem; | 675 | vr_die = &die_mem; |
558 | } | 676 | } |
559 | if (ret == 0) { | 677 | if (ret == 0) |
560 | if (pf->pvar->type) { | 678 | ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type); |
561 | pf->tvar->type = strdup(pf->pvar->type); | ||
562 | if (pf->tvar->type == NULL) | ||
563 | ret = -ENOMEM; | ||
564 | } else | ||
565 | ret = convert_variable_type(vr_die, pf->tvar); | ||
566 | } | ||
567 | /* *expr will be cached in libdw. Don't free it. */ | 679 | /* *expr will be cached in libdw. Don't free it. */ |
568 | return ret; | 680 | return ret; |
569 | error: | ||
570 | /* TODO: Support const_value */ | ||
571 | pr_err("Failed to find the location of %s at this address.\n" | ||
572 | " Perhaps, it has been optimized out.\n", pf->pvar->var); | ||
573 | return -ENOENT; | ||
574 | } | 681 | } |
575 | 682 | ||
576 | /* Find a variable in a subprogram die */ | 683 | /* Find a variable in a subprogram die */ |
577 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 684 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
578 | { | 685 | { |
579 | Dwarf_Die vr_die; | 686 | Dwarf_Die vr_die, *scopes; |
580 | char buf[32], *ptr; | 687 | char buf[32], *ptr; |
581 | int ret; | 688 | int ret, nscopes; |
582 | 689 | ||
583 | /* TODO: Support arrays */ | ||
584 | if (pf->pvar->name) | 690 | if (pf->pvar->name) |
585 | pf->tvar->name = strdup(pf->pvar->name); | 691 | pf->tvar->name = strdup(pf->pvar->name); |
586 | else { | 692 | else { |
@@ -607,18 +713,32 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
607 | pr_debug("Searching '%s' variable in context.\n", | 713 | pr_debug("Searching '%s' variable in context.\n", |
608 | pf->pvar->var); | 714 | pf->pvar->var); |
609 | /* Search child die for local variables and parameters. */ | 715 | /* Search child die for local variables and parameters. */ |
610 | if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { | 716 | if (die_find_variable(sp_die, pf->pvar->var, &vr_die)) |
717 | ret = convert_variable(&vr_die, pf); | ||
718 | else { | ||
719 | /* Search upper class */ | ||
720 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | ||
721 | if (nscopes > 0) { | ||
722 | ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, | ||
723 | 0, NULL, 0, 0, &vr_die); | ||
724 | if (ret >= 0) | ||
725 | ret = convert_variable(&vr_die, pf); | ||
726 | else | ||
727 | ret = -ENOENT; | ||
728 | free(scopes); | ||
729 | } else | ||
730 | ret = -ENOENT; | ||
731 | } | ||
732 | if (ret < 0) | ||
611 | pr_warning("Failed to find '%s' in this function.\n", | 733 | pr_warning("Failed to find '%s' in this function.\n", |
612 | pf->pvar->var); | 734 | pf->pvar->var); |
613 | return -ENOENT; | 735 | return ret; |
614 | } | ||
615 | return convert_variable(&vr_die, pf); | ||
616 | } | 736 | } |
617 | 737 | ||
618 | /* Show a probe point to output buffer */ | 738 | /* Show a probe point to output buffer */ |
619 | static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | 739 | static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) |
620 | { | 740 | { |
621 | struct kprobe_trace_event *tev; | 741 | struct probe_trace_event *tev; |
622 | Dwarf_Addr eaddr; | 742 | Dwarf_Addr eaddr; |
623 | Dwarf_Die die_mem; | 743 | Dwarf_Die die_mem; |
624 | const char *name; | 744 | const char *name; |
@@ -683,7 +803,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
683 | 803 | ||
684 | /* Find each argument */ | 804 | /* Find each argument */ |
685 | tev->nargs = pf->pev->nargs; | 805 | tev->nargs = pf->pev->nargs; |
686 | tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); | 806 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
687 | if (tev->args == NULL) | 807 | if (tev->args == NULL) |
688 | return -ENOMEM; | 808 | return -ENOMEM; |
689 | for (i = 0; i < pf->pev->nargs; i++) { | 809 | for (i = 0; i < pf->pev->nargs; i++) { |
@@ -897,7 +1017,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
897 | 1017 | ||
898 | /* Check tag and diename */ | 1018 | /* Check tag and diename */ |
899 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 1019 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
900 | die_compare_name(sp_die, pp->function) != 0) | 1020 | !die_compare_name(sp_die, pp->function)) |
901 | return DWARF_CB_OK; | 1021 | return DWARF_CB_OK; |
902 | 1022 | ||
903 | pf->fname = dwarf_decl_file(sp_die); | 1023 | pf->fname = dwarf_decl_file(sp_die); |
@@ -940,9 +1060,9 @@ static int find_probe_point_by_func(struct probe_finder *pf) | |||
940 | return _param.retval; | 1060 | return _param.retval; |
941 | } | 1061 | } |
942 | 1062 | ||
943 | /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ | 1063 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
944 | int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, | 1064 | int find_probe_trace_events(int fd, struct perf_probe_event *pev, |
945 | struct kprobe_trace_event **tevs, int max_tevs) | 1065 | struct probe_trace_event **tevs, int max_tevs) |
946 | { | 1066 | { |
947 | struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; | 1067 | struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; |
948 | struct perf_probe_point *pp = &pev->point; | 1068 | struct perf_probe_point *pp = &pev->point; |
@@ -952,7 +1072,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, | |||
952 | Dwarf *dbg; | 1072 | Dwarf *dbg; |
953 | int ret = 0; | 1073 | int ret = 0; |
954 | 1074 | ||
955 | pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); | 1075 | pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); |
956 | if (pf.tevs == NULL) | 1076 | if (pf.tevs == NULL) |
957 | return -ENOMEM; | 1077 | return -ENOMEM; |
958 | *tevs = pf.tevs; | 1078 | *tevs = pf.tevs; |
@@ -1096,7 +1216,7 @@ end: | |||
1096 | static int line_range_add_line(const char *src, unsigned int lineno, | 1216 | static int line_range_add_line(const char *src, unsigned int lineno, |
1097 | struct line_range *lr) | 1217 | struct line_range *lr) |
1098 | { | 1218 | { |
1099 | /* Copy real path */ | 1219 | /* Copy source path */ |
1100 | if (!lr->path) { | 1220 | if (!lr->path) { |
1101 | lr->path = strdup(src); | 1221 | lr->path = strdup(src); |
1102 | if (lr->path == NULL) | 1222 | if (lr->path == NULL) |
@@ -1220,7 +1340,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1220 | struct line_range *lr = lf->lr; | 1340 | struct line_range *lr = lf->lr; |
1221 | 1341 | ||
1222 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1342 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
1223 | die_compare_name(sp_die, lr->function) == 0) { | 1343 | die_compare_name(sp_die, lr->function)) { |
1224 | lf->fname = dwarf_decl_file(sp_die); | 1344 | lf->fname = dwarf_decl_file(sp_die); |
1225 | dwarf_decl_line(sp_die, &lr->offset); | 1345 | dwarf_decl_line(sp_die, &lr->offset); |
1226 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | 1346 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); |
@@ -1263,6 +1383,7 @@ int find_line_range(int fd, struct line_range *lr) | |||
1263 | size_t cuhl; | 1383 | size_t cuhl; |
1264 | Dwarf_Die *diep; | 1384 | Dwarf_Die *diep; |
1265 | Dwarf *dbg; | 1385 | Dwarf *dbg; |
1386 | const char *comp_dir; | ||
1266 | 1387 | ||
1267 | dbg = dwarf_begin(fd, DWARF_C_READ); | 1388 | dbg = dwarf_begin(fd, DWARF_C_READ); |
1268 | if (!dbg) { | 1389 | if (!dbg) { |
@@ -1298,7 +1419,18 @@ int find_line_range(int fd, struct line_range *lr) | |||
1298 | } | 1419 | } |
1299 | off = noff; | 1420 | off = noff; |
1300 | } | 1421 | } |
1301 | pr_debug("path: %lx\n", (unsigned long)lr->path); | 1422 | |
1423 | /* Store comp_dir */ | ||
1424 | if (lf.found) { | ||
1425 | comp_dir = cu_get_comp_dir(&lf.cu_die); | ||
1426 | if (comp_dir) { | ||
1427 | lr->comp_dir = strdup(comp_dir); | ||
1428 | if (!lr->comp_dir) | ||
1429 | ret = -ENOMEM; | ||
1430 | } | ||
1431 | } | ||
1432 | |||
1433 | pr_debug("path: %s\n", lr->path); | ||
1302 | dwarf_end(dbg); | 1434 | dwarf_end(dbg); |
1303 | 1435 | ||
1304 | return (ret < 0) ? ret : lf.found; | 1436 | return (ret < 0) ? ret : lf.found; |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index e1f61dcd18ff..4507d519f183 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -16,9 +16,9 @@ static inline int is_c_varname(const char *name) | |||
16 | } | 16 | } |
17 | 17 | ||
18 | #ifdef DWARF_SUPPORT | 18 | #ifdef DWARF_SUPPORT |
19 | /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ | 19 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
20 | extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, | 20 | extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, |
21 | struct kprobe_trace_event **tevs, | 21 | struct probe_trace_event **tevs, |
22 | int max_tevs); | 22 | int max_tevs); |
23 | 23 | ||
24 | /* Find a perf_probe_point from debuginfo */ | 24 | /* Find a perf_probe_point from debuginfo */ |
@@ -33,7 +33,7 @@ extern int find_line_range(int fd, struct line_range *lr); | |||
33 | 33 | ||
34 | struct probe_finder { | 34 | struct probe_finder { |
35 | struct perf_probe_event *pev; /* Target probe event */ | 35 | struct perf_probe_event *pev; /* Target probe event */ |
36 | struct kprobe_trace_event *tevs; /* Result trace events */ | 36 | struct probe_trace_event *tevs; /* Result trace events */ |
37 | int ntevs; /* Number of trace events */ | 37 | int ntevs; /* Number of trace events */ |
38 | int max_tevs; /* Max number of trace events */ | 38 | int max_tevs; /* Max number of trace events */ |
39 | 39 | ||
@@ -50,7 +50,7 @@ struct probe_finder { | |||
50 | #endif | 50 | #endif |
51 | Dwarf_Op *fb_ops; /* Frame base attribute */ | 51 | Dwarf_Op *fb_ops; /* Frame base attribute */ |
52 | struct perf_probe_arg *pvar; /* Current target variable */ | 52 | struct perf_probe_arg *pvar; /* Current target variable */ |
53 | struct kprobe_trace_arg *tvar; /* Current result variable */ | 53 | struct probe_trace_arg *tvar; /* Current result variable */ |
54 | }; | 54 | }; |
55 | 55 | ||
56 | struct line_finder { | 56 | struct line_finder { |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c422cd676313..fa9d652c2dc3 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -27,8 +27,10 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
27 | 27 | ||
28 | self->fd = open(self->filename, O_RDONLY); | 28 | self->fd = open(self->filename, O_RDONLY); |
29 | if (self->fd < 0) { | 29 | if (self->fd < 0) { |
30 | pr_err("failed to open file: %s", self->filename); | 30 | int err = errno; |
31 | if (!strcmp(self->filename, "perf.data")) | 31 | |
32 | pr_err("failed to open %s: %s", self->filename, strerror(err)); | ||
33 | if (err == ENOENT && !strcmp(self->filename, "perf.data")) | ||
32 | pr_err(" (try 'perf record' first)"); | 34 | pr_err(" (try 'perf record' first)"); |
33 | pr_err("\n"); | 35 | pr_err("\n"); |
34 | return -errno; | 36 | return -errno; |
@@ -77,6 +79,12 @@ int perf_session__create_kernel_maps(struct perf_session *self) | |||
77 | return ret; | 79 | return ret; |
78 | } | 80 | } |
79 | 81 | ||
82 | static void perf_session__destroy_kernel_maps(struct perf_session *self) | ||
83 | { | ||
84 | machine__destroy_kernel_maps(&self->host_machine); | ||
85 | machines__destroy_guest_kernel_maps(&self->machines); | ||
86 | } | ||
87 | |||
80 | struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) | 88 | struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) |
81 | { | 89 | { |
82 | size_t len = filename ? strlen(filename) + 1 : 0; | 90 | size_t len = filename ? strlen(filename) + 1 : 0; |
@@ -94,8 +102,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
94 | self->hists_tree = RB_ROOT; | 102 | self->hists_tree = RB_ROOT; |
95 | self->last_match = NULL; | 103 | self->last_match = NULL; |
96 | self->mmap_window = 32; | 104 | self->mmap_window = 32; |
97 | self->cwd = NULL; | ||
98 | self->cwdlen = 0; | ||
99 | self->machines = RB_ROOT; | 105 | self->machines = RB_ROOT; |
100 | self->repipe = repipe; | 106 | self->repipe = repipe; |
101 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); | 107 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); |
@@ -124,16 +130,43 @@ out_delete: | |||
124 | return NULL; | 130 | return NULL; |
125 | } | 131 | } |
126 | 132 | ||
133 | static void perf_session__delete_dead_threads(struct perf_session *self) | ||
134 | { | ||
135 | struct thread *n, *t; | ||
136 | |||
137 | list_for_each_entry_safe(t, n, &self->dead_threads, node) { | ||
138 | list_del(&t->node); | ||
139 | thread__delete(t); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | static void perf_session__delete_threads(struct perf_session *self) | ||
144 | { | ||
145 | struct rb_node *nd = rb_first(&self->threads); | ||
146 | |||
147 | while (nd) { | ||
148 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
149 | |||
150 | rb_erase(&t->rb_node, &self->threads); | ||
151 | nd = rb_next(nd); | ||
152 | thread__delete(t); | ||
153 | } | ||
154 | } | ||
155 | |||
127 | void perf_session__delete(struct perf_session *self) | 156 | void perf_session__delete(struct perf_session *self) |
128 | { | 157 | { |
129 | perf_header__exit(&self->header); | 158 | perf_header__exit(&self->header); |
159 | perf_session__destroy_kernel_maps(self); | ||
160 | perf_session__delete_dead_threads(self); | ||
161 | perf_session__delete_threads(self); | ||
162 | machine__exit(&self->host_machine); | ||
130 | close(self->fd); | 163 | close(self->fd); |
131 | free(self->cwd); | ||
132 | free(self); | 164 | free(self); |
133 | } | 165 | } |
134 | 166 | ||
135 | void perf_session__remove_thread(struct perf_session *self, struct thread *th) | 167 | void perf_session__remove_thread(struct perf_session *self, struct thread *th) |
136 | { | 168 | { |
169 | self->last_match = NULL; | ||
137 | rb_erase(&th->rb_node, &self->threads); | 170 | rb_erase(&th->rb_node, &self->threads); |
138 | /* | 171 | /* |
139 | * We may have references to this thread, for instance in some hist_entry | 172 | * We may have references to this thread, for instance in some hist_entry |
@@ -830,23 +863,6 @@ int perf_session__process_events(struct perf_session *self, | |||
830 | if (perf_session__register_idle_thread(self) == NULL) | 863 | if (perf_session__register_idle_thread(self) == NULL) |
831 | return -ENOMEM; | 864 | return -ENOMEM; |
832 | 865 | ||
833 | if (!symbol_conf.full_paths) { | ||
834 | char bf[PATH_MAX]; | ||
835 | |||
836 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
837 | err = -errno; | ||
838 | out_getcwd_err: | ||
839 | pr_err("failed to get the current directory\n"); | ||
840 | goto out_err; | ||
841 | } | ||
842 | self->cwd = strdup(bf); | ||
843 | if (self->cwd == NULL) { | ||
844 | err = -ENOMEM; | ||
845 | goto out_getcwd_err; | ||
846 | } | ||
847 | self->cwdlen = strlen(self->cwd); | ||
848 | } | ||
849 | |||
850 | if (!self->fd_pipe) | 866 | if (!self->fd_pipe) |
851 | err = __perf_session__process_events(self, | 867 | err = __perf_session__process_events(self, |
852 | self->header.data_offset, | 868 | self->header.data_offset, |
@@ -854,7 +870,7 @@ out_getcwd_err: | |||
854 | self->size, ops); | 870 | self->size, ops); |
855 | else | 871 | else |
856 | err = __perf_session__process_pipe_events(self, ops); | 872 | err = __perf_session__process_pipe_events(self, ops); |
857 | out_err: | 873 | |
858 | return err; | 874 | return err; |
859 | } | 875 | } |
860 | 876 | ||
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2316cb5a4116..1c61a4f4aa8a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "sort.h" | 1 | #include "sort.h" |
2 | #include "hist.h" | ||
2 | 3 | ||
3 | regex_t parent_regex; | 4 | regex_t parent_regex; |
4 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; | 5 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; |
@@ -10,10 +11,6 @@ int sort__has_parent = 0; | |||
10 | 11 | ||
11 | enum sort_type sort__first_dimension; | 12 | enum sort_type sort__first_dimension; |
12 | 13 | ||
13 | unsigned int dsos__col_width; | ||
14 | unsigned int comms__col_width; | ||
15 | unsigned int threads__col_width; | ||
16 | static unsigned int parent_symbol__col_width; | ||
17 | char * field_sep; | 14 | char * field_sep; |
18 | 15 | ||
19 | LIST_HEAD(hist_entry__sort_list); | 16 | LIST_HEAD(hist_entry__sort_list); |
@@ -28,12 +25,14 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
28 | size_t size, unsigned int width); | 25 | size_t size, unsigned int width); |
29 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | 26 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, |
30 | size_t size, unsigned int width); | 27 | size_t size, unsigned int width); |
28 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
29 | size_t size, unsigned int width); | ||
31 | 30 | ||
32 | struct sort_entry sort_thread = { | 31 | struct sort_entry sort_thread = { |
33 | .se_header = "Command: Pid", | 32 | .se_header = "Command: Pid", |
34 | .se_cmp = sort__thread_cmp, | 33 | .se_cmp = sort__thread_cmp, |
35 | .se_snprintf = hist_entry__thread_snprintf, | 34 | .se_snprintf = hist_entry__thread_snprintf, |
36 | .se_width = &threads__col_width, | 35 | .se_width_idx = HISTC_THREAD, |
37 | }; | 36 | }; |
38 | 37 | ||
39 | struct sort_entry sort_comm = { | 38 | struct sort_entry sort_comm = { |
@@ -41,27 +40,35 @@ struct sort_entry sort_comm = { | |||
41 | .se_cmp = sort__comm_cmp, | 40 | .se_cmp = sort__comm_cmp, |
42 | .se_collapse = sort__comm_collapse, | 41 | .se_collapse = sort__comm_collapse, |
43 | .se_snprintf = hist_entry__comm_snprintf, | 42 | .se_snprintf = hist_entry__comm_snprintf, |
44 | .se_width = &comms__col_width, | 43 | .se_width_idx = HISTC_COMM, |
45 | }; | 44 | }; |
46 | 45 | ||
47 | struct sort_entry sort_dso = { | 46 | struct sort_entry sort_dso = { |
48 | .se_header = "Shared Object", | 47 | .se_header = "Shared Object", |
49 | .se_cmp = sort__dso_cmp, | 48 | .se_cmp = sort__dso_cmp, |
50 | .se_snprintf = hist_entry__dso_snprintf, | 49 | .se_snprintf = hist_entry__dso_snprintf, |
51 | .se_width = &dsos__col_width, | 50 | .se_width_idx = HISTC_DSO, |
52 | }; | 51 | }; |
53 | 52 | ||
54 | struct sort_entry sort_sym = { | 53 | struct sort_entry sort_sym = { |
55 | .se_header = "Symbol", | 54 | .se_header = "Symbol", |
56 | .se_cmp = sort__sym_cmp, | 55 | .se_cmp = sort__sym_cmp, |
57 | .se_snprintf = hist_entry__sym_snprintf, | 56 | .se_snprintf = hist_entry__sym_snprintf, |
57 | .se_width_idx = HISTC_SYMBOL, | ||
58 | }; | 58 | }; |
59 | 59 | ||
60 | struct sort_entry sort_parent = { | 60 | struct sort_entry sort_parent = { |
61 | .se_header = "Parent symbol", | 61 | .se_header = "Parent symbol", |
62 | .se_cmp = sort__parent_cmp, | 62 | .se_cmp = sort__parent_cmp, |
63 | .se_snprintf = hist_entry__parent_snprintf, | 63 | .se_snprintf = hist_entry__parent_snprintf, |
64 | .se_width = &parent_symbol__col_width, | 64 | .se_width_idx = HISTC_PARENT, |
65 | }; | ||
66 | |||
67 | struct sort_entry sort_cpu = { | ||
68 | .se_header = "CPU", | ||
69 | .se_cmp = sort__cpu_cmp, | ||
70 | .se_snprintf = hist_entry__cpu_snprintf, | ||
71 | .se_width_idx = HISTC_CPU, | ||
65 | }; | 72 | }; |
66 | 73 | ||
67 | struct sort_dimension { | 74 | struct sort_dimension { |
@@ -76,6 +83,7 @@ static struct sort_dimension sort_dimensions[] = { | |||
76 | { .name = "dso", .entry = &sort_dso, }, | 83 | { .name = "dso", .entry = &sort_dso, }, |
77 | { .name = "symbol", .entry = &sort_sym, }, | 84 | { .name = "symbol", .entry = &sort_sym, }, |
78 | { .name = "parent", .entry = &sort_parent, }, | 85 | { .name = "parent", .entry = &sort_parent, }, |
86 | { .name = "cpu", .entry = &sort_cpu, }, | ||
79 | }; | 87 | }; |
80 | 88 | ||
81 | int64_t cmp_null(void *l, void *r) | 89 | int64_t cmp_null(void *l, void *r) |
@@ -242,6 +250,20 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | |||
242 | self->parent ? self->parent->name : "[other]"); | 250 | self->parent ? self->parent->name : "[other]"); |
243 | } | 251 | } |
244 | 252 | ||
253 | /* --sort cpu */ | ||
254 | |||
255 | int64_t | ||
256 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | ||
257 | { | ||
258 | return right->cpu - left->cpu; | ||
259 | } | ||
260 | |||
261 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
262 | size_t size, unsigned int width) | ||
263 | { | ||
264 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | ||
265 | } | ||
266 | |||
245 | int sort_dimension__add(const char *tok) | 267 | int sort_dimension__add(const char *tok) |
246 | { | 268 | { |
247 | unsigned int i; | 269 | unsigned int i; |
@@ -281,6 +303,8 @@ int sort_dimension__add(const char *tok) | |||
281 | sort__first_dimension = SORT_SYM; | 303 | sort__first_dimension = SORT_SYM; |
282 | else if (!strcmp(sd->name, "parent")) | 304 | else if (!strcmp(sd->name, "parent")) |
283 | sort__first_dimension = SORT_PARENT; | 305 | sort__first_dimension = SORT_PARENT; |
306 | else if (!strcmp(sd->name, "cpu")) | ||
307 | sort__first_dimension = SORT_CPU; | ||
284 | } | 308 | } |
285 | 309 | ||
286 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 310 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 0d61c4082f43..46e531d09e8b 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -36,11 +36,14 @@ extern struct sort_entry sort_comm; | |||
36 | extern struct sort_entry sort_dso; | 36 | extern struct sort_entry sort_dso; |
37 | extern struct sort_entry sort_sym; | 37 | extern struct sort_entry sort_sym; |
38 | extern struct sort_entry sort_parent; | 38 | extern struct sort_entry sort_parent; |
39 | extern unsigned int dsos__col_width; | ||
40 | extern unsigned int comms__col_width; | ||
41 | extern unsigned int threads__col_width; | ||
42 | extern enum sort_type sort__first_dimension; | 39 | extern enum sort_type sort__first_dimension; |
43 | 40 | ||
41 | /** | ||
42 | * struct hist_entry - histogram entry | ||
43 | * | ||
44 | * @row_offset - offset from the first callchain expanded to appear on screen | ||
45 | * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding | ||
46 | */ | ||
44 | struct hist_entry { | 47 | struct hist_entry { |
45 | struct rb_node rb_node; | 48 | struct rb_node rb_node; |
46 | u64 period; | 49 | u64 period; |
@@ -51,7 +54,14 @@ struct hist_entry { | |||
51 | struct map_symbol ms; | 54 | struct map_symbol ms; |
52 | struct thread *thread; | 55 | struct thread *thread; |
53 | u64 ip; | 56 | u64 ip; |
57 | s32 cpu; | ||
54 | u32 nr_events; | 58 | u32 nr_events; |
59 | |||
60 | /* XXX These two should move to some tree widget lib */ | ||
61 | u16 row_offset; | ||
62 | u16 nr_rows; | ||
63 | |||
64 | bool init_have_children; | ||
55 | char level; | 65 | char level; |
56 | u8 filtered; | 66 | u8 filtered; |
57 | struct symbol *parent; | 67 | struct symbol *parent; |
@@ -68,7 +78,8 @@ enum sort_type { | |||
68 | SORT_COMM, | 78 | SORT_COMM, |
69 | SORT_DSO, | 79 | SORT_DSO, |
70 | SORT_SYM, | 80 | SORT_SYM, |
71 | SORT_PARENT | 81 | SORT_PARENT, |
82 | SORT_CPU, | ||
72 | }; | 83 | }; |
73 | 84 | ||
74 | /* | 85 | /* |
@@ -84,7 +95,7 @@ struct sort_entry { | |||
84 | int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); | 95 | int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); |
85 | int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, | 96 | int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, |
86 | unsigned int width); | 97 | unsigned int width); |
87 | unsigned int *se_width; | 98 | u8 se_width_idx; |
88 | bool elide; | 99 | bool elide; |
89 | }; | 100 | }; |
90 | 101 | ||
@@ -104,6 +115,7 @@ extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | |||
104 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | 115 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); |
105 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | 116 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); |
106 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | 117 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); |
118 | int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); | ||
107 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | 119 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); |
108 | extern int sort_dimension__add(const char *); | 120 | extern int sort_dimension__add(const char *); |
109 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 121 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 5b276833e2bf..6f0dd90c36ce 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <fcntl.h> | 12 | #include <fcntl.h> |
13 | #include <unistd.h> | 13 | #include <unistd.h> |
14 | #include "build-id.h" | 14 | #include "build-id.h" |
15 | #include "debug.h" | ||
15 | #include "symbol.h" | 16 | #include "symbol.h" |
16 | #include "strlist.h" | 17 | #include "strlist.h" |
17 | 18 | ||
@@ -25,6 +26,8 @@ | |||
25 | #define NT_GNU_BUILD_ID 3 | 26 | #define NT_GNU_BUILD_ID 3 |
26 | #endif | 27 | #endif |
27 | 28 | ||
29 | static bool dso__build_id_equal(const struct dso *self, u8 *build_id); | ||
30 | static int elf_read_build_id(Elf *elf, void *bf, size_t size); | ||
28 | static void dsos__add(struct list_head *head, struct dso *dso); | 31 | static void dsos__add(struct list_head *head, struct dso *dso); |
29 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 32 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
30 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 33 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
@@ -40,6 +43,14 @@ struct symbol_conf symbol_conf = { | |||
40 | .try_vmlinux_path = true, | 43 | .try_vmlinux_path = true, |
41 | }; | 44 | }; |
42 | 45 | ||
46 | int dso__name_len(const struct dso *self) | ||
47 | { | ||
48 | if (verbose) | ||
49 | return self->long_name_len; | ||
50 | |||
51 | return self->short_name_len; | ||
52 | } | ||
53 | |||
43 | bool dso__loaded(const struct dso *self, enum map_type type) | 54 | bool dso__loaded(const struct dso *self, enum map_type type) |
44 | { | 55 | { |
45 | return self->loaded & (1 << type); | 56 | return self->loaded & (1 << type); |
@@ -215,7 +226,9 @@ void dso__delete(struct dso *self) | |||
215 | int i; | 226 | int i; |
216 | for (i = 0; i < MAP__NR_TYPES; ++i) | 227 | for (i = 0; i < MAP__NR_TYPES; ++i) |
217 | symbols__delete(&self->symbols[i]); | 228 | symbols__delete(&self->symbols[i]); |
218 | if (self->long_name != self->name) | 229 | if (self->sname_alloc) |
230 | free((char *)self->short_name); | ||
231 | if (self->lname_alloc) | ||
219 | free(self->long_name); | 232 | free(self->long_name); |
220 | free(self); | 233 | free(self); |
221 | } | 234 | } |
@@ -933,8 +946,28 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type | |||
933 | } | 946 | } |
934 | } | 947 | } |
935 | 948 | ||
949 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | ||
950 | { | ||
951 | Elf_Scn *sec = NULL; | ||
952 | GElf_Shdr shdr; | ||
953 | size_t cnt = 1; | ||
954 | |||
955 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
956 | gelf_getshdr(sec, &shdr); | ||
957 | |||
958 | if ((addr >= shdr.sh_addr) && | ||
959 | (addr < (shdr.sh_addr + shdr.sh_size))) | ||
960 | return cnt; | ||
961 | |||
962 | ++cnt; | ||
963 | } | ||
964 | |||
965 | return -1; | ||
966 | } | ||
967 | |||
936 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, | 968 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, |
937 | int fd, symbol_filter_t filter, int kmodule) | 969 | int fd, symbol_filter_t filter, int kmodule, |
970 | int want_symtab) | ||
938 | { | 971 | { |
939 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; | 972 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; |
940 | struct map *curr_map = map; | 973 | struct map *curr_map = map; |
@@ -944,31 +977,51 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
944 | int err = -1; | 977 | int err = -1; |
945 | uint32_t idx; | 978 | uint32_t idx; |
946 | GElf_Ehdr ehdr; | 979 | GElf_Ehdr ehdr; |
947 | GElf_Shdr shdr; | 980 | GElf_Shdr shdr, opdshdr; |
948 | Elf_Data *syms; | 981 | Elf_Data *syms, *opddata = NULL; |
949 | GElf_Sym sym; | 982 | GElf_Sym sym; |
950 | Elf_Scn *sec, *sec_strndx; | 983 | Elf_Scn *sec, *sec_strndx, *opdsec; |
951 | Elf *elf; | 984 | Elf *elf; |
952 | int nr = 0; | 985 | int nr = 0; |
986 | size_t opdidx = 0; | ||
953 | 987 | ||
954 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 988 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
955 | if (elf == NULL) { | 989 | if (elf == NULL) { |
956 | pr_err("%s: cannot read %s ELF file.\n", __func__, name); | 990 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); |
957 | goto out_close; | 991 | goto out_close; |
958 | } | 992 | } |
959 | 993 | ||
960 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 994 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
961 | pr_err("%s: cannot get elf header.\n", __func__); | 995 | pr_debug("%s: cannot get elf header.\n", __func__); |
962 | goto out_elf_end; | 996 | goto out_elf_end; |
963 | } | 997 | } |
964 | 998 | ||
999 | /* Always reject images with a mismatched build-id: */ | ||
1000 | if (self->has_build_id) { | ||
1001 | u8 build_id[BUILD_ID_SIZE]; | ||
1002 | |||
1003 | if (elf_read_build_id(elf, build_id, | ||
1004 | BUILD_ID_SIZE) != BUILD_ID_SIZE) | ||
1005 | goto out_elf_end; | ||
1006 | |||
1007 | if (!dso__build_id_equal(self, build_id)) | ||
1008 | goto out_elf_end; | ||
1009 | } | ||
1010 | |||
965 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 1011 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
966 | if (sec == NULL) { | 1012 | if (sec == NULL) { |
1013 | if (want_symtab) | ||
1014 | goto out_elf_end; | ||
1015 | |||
967 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | 1016 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
968 | if (sec == NULL) | 1017 | if (sec == NULL) |
969 | goto out_elf_end; | 1018 | goto out_elf_end; |
970 | } | 1019 | } |
971 | 1020 | ||
1021 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | ||
1022 | if (opdsec) | ||
1023 | opddata = elf_rawdata(opdsec, NULL); | ||
1024 | |||
972 | syms = elf_getdata(sec, NULL); | 1025 | syms = elf_getdata(sec, NULL); |
973 | if (syms == NULL) | 1026 | if (syms == NULL) |
974 | goto out_elf_end; | 1027 | goto out_elf_end; |
@@ -1013,6 +1066,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1013 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | 1066 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
1014 | continue; | 1067 | continue; |
1015 | 1068 | ||
1069 | if (opdsec && sym.st_shndx == opdidx) { | ||
1070 | u32 offset = sym.st_value - opdshdr.sh_addr; | ||
1071 | u64 *opd = opddata->d_buf + offset; | ||
1072 | sym.st_value = *opd; | ||
1073 | sym.st_shndx = elf_addr_to_index(elf, sym.st_value); | ||
1074 | } | ||
1075 | |||
1016 | sec = elf_getscn(elf, sym.st_shndx); | 1076 | sec = elf_getscn(elf, sym.st_shndx); |
1017 | if (!sec) | 1077 | if (!sec) |
1018 | goto out_elf_end; | 1078 | goto out_elf_end; |
@@ -1151,37 +1211,26 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
1151 | */ | 1211 | */ |
1152 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | 1212 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) |
1153 | 1213 | ||
1154 | int filename__read_build_id(const char *filename, void *bf, size_t size) | 1214 | static int elf_read_build_id(Elf *elf, void *bf, size_t size) |
1155 | { | 1215 | { |
1156 | int fd, err = -1; | 1216 | int err = -1; |
1157 | GElf_Ehdr ehdr; | 1217 | GElf_Ehdr ehdr; |
1158 | GElf_Shdr shdr; | 1218 | GElf_Shdr shdr; |
1159 | Elf_Data *data; | 1219 | Elf_Data *data; |
1160 | Elf_Scn *sec; | 1220 | Elf_Scn *sec; |
1161 | Elf_Kind ek; | 1221 | Elf_Kind ek; |
1162 | void *ptr; | 1222 | void *ptr; |
1163 | Elf *elf; | ||
1164 | 1223 | ||
1165 | if (size < BUILD_ID_SIZE) | 1224 | if (size < BUILD_ID_SIZE) |
1166 | goto out; | 1225 | goto out; |
1167 | 1226 | ||
1168 | fd = open(filename, O_RDONLY); | ||
1169 | if (fd < 0) | ||
1170 | goto out; | ||
1171 | |||
1172 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1173 | if (elf == NULL) { | ||
1174 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
1175 | goto out_close; | ||
1176 | } | ||
1177 | |||
1178 | ek = elf_kind(elf); | 1227 | ek = elf_kind(elf); |
1179 | if (ek != ELF_K_ELF) | 1228 | if (ek != ELF_K_ELF) |
1180 | goto out_elf_end; | 1229 | goto out; |
1181 | 1230 | ||
1182 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 1231 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
1183 | pr_err("%s: cannot get elf header.\n", __func__); | 1232 | pr_err("%s: cannot get elf header.\n", __func__); |
1184 | goto out_elf_end; | 1233 | goto out; |
1185 | } | 1234 | } |
1186 | 1235 | ||
1187 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 1236 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
@@ -1190,12 +1239,12 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
1190 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 1239 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
1191 | ".notes", NULL); | 1240 | ".notes", NULL); |
1192 | if (sec == NULL) | 1241 | if (sec == NULL) |
1193 | goto out_elf_end; | 1242 | goto out; |
1194 | } | 1243 | } |
1195 | 1244 | ||
1196 | data = elf_getdata(sec, NULL); | 1245 | data = elf_getdata(sec, NULL); |
1197 | if (data == NULL) | 1246 | if (data == NULL) |
1198 | goto out_elf_end; | 1247 | goto out; |
1199 | 1248 | ||
1200 | ptr = data->d_buf; | 1249 | ptr = data->d_buf; |
1201 | while (ptr < (data->d_buf + data->d_size)) { | 1250 | while (ptr < (data->d_buf + data->d_size)) { |
@@ -1217,7 +1266,31 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
1217 | } | 1266 | } |
1218 | ptr += descsz; | 1267 | ptr += descsz; |
1219 | } | 1268 | } |
1220 | out_elf_end: | 1269 | |
1270 | out: | ||
1271 | return err; | ||
1272 | } | ||
1273 | |||
1274 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
1275 | { | ||
1276 | int fd, err = -1; | ||
1277 | Elf *elf; | ||
1278 | |||
1279 | if (size < BUILD_ID_SIZE) | ||
1280 | goto out; | ||
1281 | |||
1282 | fd = open(filename, O_RDONLY); | ||
1283 | if (fd < 0) | ||
1284 | goto out; | ||
1285 | |||
1286 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1287 | if (elf == NULL) { | ||
1288 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
1289 | goto out_close; | ||
1290 | } | ||
1291 | |||
1292 | err = elf_read_build_id(elf, bf, size); | ||
1293 | |||
1221 | elf_end(elf); | 1294 | elf_end(elf); |
1222 | out_close: | 1295 | out_close: |
1223 | close(fd); | 1296 | close(fd); |
@@ -1293,11 +1366,11 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1293 | { | 1366 | { |
1294 | int size = PATH_MAX; | 1367 | int size = PATH_MAX; |
1295 | char *name; | 1368 | char *name; |
1296 | u8 build_id[BUILD_ID_SIZE]; | ||
1297 | int ret = -1; | 1369 | int ret = -1; |
1298 | int fd; | 1370 | int fd; |
1299 | struct machine *machine; | 1371 | struct machine *machine; |
1300 | const char *root_dir; | 1372 | const char *root_dir; |
1373 | int want_symtab; | ||
1301 | 1374 | ||
1302 | dso__set_loaded(self, map->type); | 1375 | dso__set_loaded(self, map->type); |
1303 | 1376 | ||
@@ -1324,13 +1397,18 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1324 | return ret; | 1397 | return ret; |
1325 | } | 1398 | } |
1326 | 1399 | ||
1327 | self->origin = DSO__ORIG_BUILD_ID_CACHE; | 1400 | /* Iterate over candidate debug images. |
1328 | if (dso__build_id_filename(self, name, size) != NULL) | 1401 | * On the first pass, only load images if they have a full symtab. |
1329 | goto open_file; | 1402 | * Failing that, do a second pass where we accept .dynsym also |
1330 | more: | 1403 | */ |
1331 | do { | 1404 | for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1; |
1332 | self->origin++; | 1405 | self->origin != DSO__ORIG_NOT_FOUND; |
1406 | self->origin++) { | ||
1333 | switch (self->origin) { | 1407 | switch (self->origin) { |
1408 | case DSO__ORIG_BUILD_ID_CACHE: | ||
1409 | if (dso__build_id_filename(self, name, size) == NULL) | ||
1410 | continue; | ||
1411 | break; | ||
1334 | case DSO__ORIG_FEDORA: | 1412 | case DSO__ORIG_FEDORA: |
1335 | snprintf(name, size, "/usr/lib/debug%s.debug", | 1413 | snprintf(name, size, "/usr/lib/debug%s.debug", |
1336 | self->long_name); | 1414 | self->long_name); |
@@ -1339,21 +1417,20 @@ more: | |||
1339 | snprintf(name, size, "/usr/lib/debug%s", | 1417 | snprintf(name, size, "/usr/lib/debug%s", |
1340 | self->long_name); | 1418 | self->long_name); |
1341 | break; | 1419 | break; |
1342 | case DSO__ORIG_BUILDID: | 1420 | case DSO__ORIG_BUILDID: { |
1343 | if (filename__read_build_id(self->long_name, build_id, | 1421 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
1344 | sizeof(build_id))) { | 1422 | |
1345 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 1423 | if (!self->has_build_id) |
1346 | build_id__sprintf(build_id, sizeof(build_id), | 1424 | continue; |
1347 | build_id_hex); | 1425 | |
1348 | snprintf(name, size, | 1426 | build_id__sprintf(self->build_id, |
1349 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 1427 | sizeof(self->build_id), |
1350 | build_id_hex, build_id_hex + 2); | 1428 | build_id_hex); |
1351 | if (self->has_build_id) | 1429 | snprintf(name, size, |
1352 | goto compare_build_id; | 1430 | "/usr/lib/debug/.build-id/%.2s/%s.debug", |
1353 | break; | 1431 | build_id_hex, build_id_hex + 2); |
1354 | } | 1432 | } |
1355 | self->origin++; | 1433 | break; |
1356 | /* Fall thru */ | ||
1357 | case DSO__ORIG_DSO: | 1434 | case DSO__ORIG_DSO: |
1358 | snprintf(name, size, "%s", self->long_name); | 1435 | snprintf(name, size, "%s", self->long_name); |
1359 | break; | 1436 | break; |
@@ -1366,36 +1443,41 @@ more: | |||
1366 | break; | 1443 | break; |
1367 | 1444 | ||
1368 | default: | 1445 | default: |
1369 | goto out; | 1446 | /* |
1447 | * If we wanted a full symtab but no image had one, | ||
1448 | * relax our requirements and repeat the search. | ||
1449 | */ | ||
1450 | if (want_symtab) { | ||
1451 | want_symtab = 0; | ||
1452 | self->origin = DSO__ORIG_BUILD_ID_CACHE; | ||
1453 | } else | ||
1454 | continue; | ||
1370 | } | 1455 | } |
1371 | 1456 | ||
1372 | if (self->has_build_id) { | 1457 | /* Name is now the name of the next image to try */ |
1373 | if (filename__read_build_id(name, build_id, | ||
1374 | sizeof(build_id)) < 0) | ||
1375 | goto more; | ||
1376 | compare_build_id: | ||
1377 | if (!dso__build_id_equal(self, build_id)) | ||
1378 | goto more; | ||
1379 | } | ||
1380 | open_file: | ||
1381 | fd = open(name, O_RDONLY); | 1458 | fd = open(name, O_RDONLY); |
1382 | } while (fd < 0); | 1459 | if (fd < 0) |
1460 | continue; | ||
1383 | 1461 | ||
1384 | ret = dso__load_sym(self, map, name, fd, filter, 0); | 1462 | ret = dso__load_sym(self, map, name, fd, filter, 0, |
1385 | close(fd); | 1463 | want_symtab); |
1464 | close(fd); | ||
1386 | 1465 | ||
1387 | /* | 1466 | /* |
1388 | * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? | 1467 | * Some people seem to have debuginfo files _WITHOUT_ debug |
1389 | */ | 1468 | * info!?!? |
1390 | if (!ret) | 1469 | */ |
1391 | goto more; | 1470 | if (!ret) |
1471 | continue; | ||
1392 | 1472 | ||
1393 | if (ret > 0) { | 1473 | if (ret > 0) { |
1394 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); | 1474 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); |
1395 | if (nr_plt > 0) | 1475 | if (nr_plt > 0) |
1396 | ret += nr_plt; | 1476 | ret += nr_plt; |
1477 | break; | ||
1478 | } | ||
1397 | } | 1479 | } |
1398 | out: | 1480 | |
1399 | free(name); | 1481 | free(name); |
1400 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) | 1482 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) |
1401 | return 0; | 1483 | return 0; |
@@ -1494,6 +1576,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, | |||
1494 | goto out; | 1576 | goto out; |
1495 | } | 1577 | } |
1496 | dso__set_long_name(map->dso, long_name); | 1578 | dso__set_long_name(map->dso, long_name); |
1579 | map->dso->lname_alloc = 1; | ||
1497 | dso__kernel_module_get_build_id(map->dso, ""); | 1580 | dso__kernel_module_get_build_id(map->dso, ""); |
1498 | } | 1581 | } |
1499 | } | 1582 | } |
@@ -1656,36 +1739,12 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1656 | { | 1739 | { |
1657 | int err = -1, fd; | 1740 | int err = -1, fd; |
1658 | 1741 | ||
1659 | if (self->has_build_id) { | ||
1660 | u8 build_id[BUILD_ID_SIZE]; | ||
1661 | |||
1662 | if (filename__read_build_id(vmlinux, build_id, | ||
1663 | sizeof(build_id)) < 0) { | ||
1664 | pr_debug("No build_id in %s, ignoring it\n", vmlinux); | ||
1665 | return -1; | ||
1666 | } | ||
1667 | if (!dso__build_id_equal(self, build_id)) { | ||
1668 | char expected_build_id[BUILD_ID_SIZE * 2 + 1], | ||
1669 | vmlinux_build_id[BUILD_ID_SIZE * 2 + 1]; | ||
1670 | |||
1671 | build_id__sprintf(self->build_id, | ||
1672 | sizeof(self->build_id), | ||
1673 | expected_build_id); | ||
1674 | build_id__sprintf(build_id, sizeof(build_id), | ||
1675 | vmlinux_build_id); | ||
1676 | pr_debug("build_id in %s is %s while expected is %s, " | ||
1677 | "ignoring it\n", vmlinux, vmlinux_build_id, | ||
1678 | expected_build_id); | ||
1679 | return -1; | ||
1680 | } | ||
1681 | } | ||
1682 | |||
1683 | fd = open(vmlinux, O_RDONLY); | 1742 | fd = open(vmlinux, O_RDONLY); |
1684 | if (fd < 0) | 1743 | if (fd < 0) |
1685 | return -1; | 1744 | return -1; |
1686 | 1745 | ||
1687 | dso__set_loaded(self, map->type); | 1746 | dso__set_loaded(self, map->type); |
1688 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0); | 1747 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0); |
1689 | close(fd); | 1748 | close(fd); |
1690 | 1749 | ||
1691 | if (err > 0) | 1750 | if (err > 0) |
@@ -2048,6 +2107,36 @@ int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) | |||
2048 | return 0; | 2107 | return 0; |
2049 | } | 2108 | } |
2050 | 2109 | ||
2110 | void machine__destroy_kernel_maps(struct machine *self) | ||
2111 | { | ||
2112 | enum map_type type; | ||
2113 | |||
2114 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
2115 | struct kmap *kmap; | ||
2116 | |||
2117 | if (self->vmlinux_maps[type] == NULL) | ||
2118 | continue; | ||
2119 | |||
2120 | kmap = map__kmap(self->vmlinux_maps[type]); | ||
2121 | map_groups__remove(&self->kmaps, self->vmlinux_maps[type]); | ||
2122 | if (kmap->ref_reloc_sym) { | ||
2123 | /* | ||
2124 | * ref_reloc_sym is shared among all maps, so free just | ||
2125 | * on one of them. | ||
2126 | */ | ||
2127 | if (type == MAP__FUNCTION) { | ||
2128 | free((char *)kmap->ref_reloc_sym->name); | ||
2129 | kmap->ref_reloc_sym->name = NULL; | ||
2130 | free(kmap->ref_reloc_sym); | ||
2131 | } | ||
2132 | kmap->ref_reloc_sym = NULL; | ||
2133 | } | ||
2134 | |||
2135 | map__delete(self->vmlinux_maps[type]); | ||
2136 | self->vmlinux_maps[type] = NULL; | ||
2137 | } | ||
2138 | } | ||
2139 | |||
2051 | int machine__create_kernel_maps(struct machine *self) | 2140 | int machine__create_kernel_maps(struct machine *self) |
2052 | { | 2141 | { |
2053 | struct dso *kernel = machine__create_kernel(self); | 2142 | struct dso *kernel = machine__create_kernel(self); |
@@ -2189,6 +2278,15 @@ out_free_comm_list: | |||
2189 | return -1; | 2278 | return -1; |
2190 | } | 2279 | } |
2191 | 2280 | ||
2281 | void symbol__exit(void) | ||
2282 | { | ||
2283 | strlist__delete(symbol_conf.sym_list); | ||
2284 | strlist__delete(symbol_conf.dso_list); | ||
2285 | strlist__delete(symbol_conf.comm_list); | ||
2286 | vmlinux_path__exit(); | ||
2287 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | ||
2288 | } | ||
2289 | |||
2192 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid) | 2290 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid) |
2193 | { | 2291 | { |
2194 | struct machine *machine = machines__findnew(self, pid); | 2292 | struct machine *machine = machines__findnew(self, pid); |
@@ -2283,6 +2381,19 @@ failure: | |||
2283 | return ret; | 2381 | return ret; |
2284 | } | 2382 | } |
2285 | 2383 | ||
2384 | void machines__destroy_guest_kernel_maps(struct rb_root *self) | ||
2385 | { | ||
2386 | struct rb_node *next = rb_first(self); | ||
2387 | |||
2388 | while (next) { | ||
2389 | struct machine *pos = rb_entry(next, struct machine, rb_node); | ||
2390 | |||
2391 | next = rb_next(&pos->rb_node); | ||
2392 | rb_erase(&pos->rb_node, self); | ||
2393 | machine__delete(pos); | ||
2394 | } | ||
2395 | } | ||
2396 | |||
2286 | int machine__load_kallsyms(struct machine *self, const char *filename, | 2397 | int machine__load_kallsyms(struct machine *self, const char *filename, |
2287 | enum map_type type, symbol_filter_t filter) | 2398 | enum map_type type, symbol_filter_t filter) |
2288 | { | 2399 | { |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 5e02d2c17154..906be20011d9 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -9,8 +9,6 @@ | |||
9 | #include <linux/rbtree.h> | 9 | #include <linux/rbtree.h> |
10 | #include <stdio.h> | 10 | #include <stdio.h> |
11 | 11 | ||
12 | #define DEBUG_CACHE_DIR ".debug" | ||
13 | |||
14 | #ifdef HAVE_CPLUS_DEMANGLE | 12 | #ifdef HAVE_CPLUS_DEMANGLE |
15 | extern char *cplus_demangle(const char *, int); | 13 | extern char *cplus_demangle(const char *, int); |
16 | 14 | ||
@@ -70,9 +68,9 @@ struct symbol_conf { | |||
70 | show_nr_samples, | 68 | show_nr_samples, |
71 | use_callchain, | 69 | use_callchain, |
72 | exclude_other, | 70 | exclude_other, |
73 | full_paths, | ||
74 | show_cpu_utilization; | 71 | show_cpu_utilization; |
75 | const char *vmlinux_name, | 72 | const char *vmlinux_name, |
73 | *source_prefix, | ||
76 | *field_sep; | 74 | *field_sep; |
77 | const char *default_guest_vmlinux_name, | 75 | const char *default_guest_vmlinux_name, |
78 | *default_guest_kallsyms, | 76 | *default_guest_kallsyms, |
@@ -103,6 +101,8 @@ struct ref_reloc_sym { | |||
103 | struct map_symbol { | 101 | struct map_symbol { |
104 | struct map *map; | 102 | struct map *map; |
105 | struct symbol *sym; | 103 | struct symbol *sym; |
104 | bool unfolded; | ||
105 | bool has_children; | ||
106 | }; | 106 | }; |
107 | 107 | ||
108 | struct addr_location { | 108 | struct addr_location { |
@@ -112,7 +112,8 @@ struct addr_location { | |||
112 | u64 addr; | 112 | u64 addr; |
113 | char level; | 113 | char level; |
114 | bool filtered; | 114 | bool filtered; |
115 | unsigned int cpumode; | 115 | u8 cpumode; |
116 | s32 cpu; | ||
116 | }; | 117 | }; |
117 | 118 | ||
118 | enum dso_kernel_type { | 119 | enum dso_kernel_type { |
@@ -125,12 +126,14 @@ struct dso { | |||
125 | struct list_head node; | 126 | struct list_head node; |
126 | struct rb_root symbols[MAP__NR_TYPES]; | 127 | struct rb_root symbols[MAP__NR_TYPES]; |
127 | struct rb_root symbol_names[MAP__NR_TYPES]; | 128 | struct rb_root symbol_names[MAP__NR_TYPES]; |
129 | enum dso_kernel_type kernel; | ||
128 | u8 adjust_symbols:1; | 130 | u8 adjust_symbols:1; |
129 | u8 slen_calculated:1; | 131 | u8 slen_calculated:1; |
130 | u8 has_build_id:1; | 132 | u8 has_build_id:1; |
131 | enum dso_kernel_type kernel; | ||
132 | u8 hit:1; | 133 | u8 hit:1; |
133 | u8 annotate_warned:1; | 134 | u8 annotate_warned:1; |
135 | u8 sname_alloc:1; | ||
136 | u8 lname_alloc:1; | ||
134 | unsigned char origin; | 137 | unsigned char origin; |
135 | u8 sorted_by_name; | 138 | u8 sorted_by_name; |
136 | u8 loaded; | 139 | u8 loaded; |
@@ -146,6 +149,8 @@ struct dso *dso__new(const char *name); | |||
146 | struct dso *dso__new_kernel(const char *name); | 149 | struct dso *dso__new_kernel(const char *name); |
147 | void dso__delete(struct dso *self); | 150 | void dso__delete(struct dso *self); |
148 | 151 | ||
152 | int dso__name_len(const struct dso *self); | ||
153 | |||
149 | bool dso__loaded(const struct dso *self, enum map_type type); | 154 | bool dso__loaded(const struct dso *self, enum map_type type); |
150 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); | 155 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); |
151 | 156 | ||
@@ -207,13 +212,16 @@ int kallsyms__parse(const char *filename, void *arg, | |||
207 | int (*process_symbol)(void *arg, const char *name, | 212 | int (*process_symbol)(void *arg, const char *name, |
208 | char type, u64 start)); | 213 | char type, u64 start)); |
209 | 214 | ||
215 | void machine__destroy_kernel_maps(struct machine *self); | ||
210 | int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); | 216 | int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); |
211 | int machine__create_kernel_maps(struct machine *self); | 217 | int machine__create_kernel_maps(struct machine *self); |
212 | 218 | ||
213 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid); | 219 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid); |
214 | int machines__create_guest_kernel_maps(struct rb_root *self); | 220 | int machines__create_guest_kernel_maps(struct rb_root *self); |
221 | void machines__destroy_guest_kernel_maps(struct rb_root *self); | ||
215 | 222 | ||
216 | int symbol__init(void); | 223 | int symbol__init(void); |
224 | void symbol__exit(void); | ||
217 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 225 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
218 | 226 | ||
219 | size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp); | 227 | size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 9a448b47400c..8c72d888e449 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -62,6 +62,13 @@ static struct thread *thread__new(pid_t pid) | |||
62 | return self; | 62 | return self; |
63 | } | 63 | } |
64 | 64 | ||
65 | void thread__delete(struct thread *self) | ||
66 | { | ||
67 | map_groups__exit(&self->mg); | ||
68 | free(self->comm); | ||
69 | free(self); | ||
70 | } | ||
71 | |||
65 | int thread__set_comm(struct thread *self, const char *comm) | 72 | int thread__set_comm(struct thread *self, const char *comm) |
66 | { | 73 | { |
67 | int err; | 74 | int err; |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index ee6bbcf277ca..688500ff826f 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -20,6 +20,8 @@ struct thread { | |||
20 | 20 | ||
21 | struct perf_session; | 21 | struct perf_session; |
22 | 22 | ||
23 | void thread__delete(struct thread *self); | ||
24 | |||
23 | int find_all_tid(int pid, pid_t ** all_tid); | 25 | int find_all_tid(int pid, pid_t ** all_tid); |
24 | int thread__set_comm(struct thread *self, const char *comm); | 26 | int thread__set_comm(struct thread *self, const char *comm); |
25 | int thread__comm_len(struct thread *self); | 27 | int thread__comm_len(struct thread *self); |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 4e8b6b0c551c..f380fed74359 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -89,6 +89,7 @@ | |||
89 | 89 | ||
90 | extern const char *graph_line; | 90 | extern const char *graph_line; |
91 | extern const char *graph_dotted_line; | 91 | extern const char *graph_dotted_line; |
92 | extern char buildid_dir[]; | ||
92 | 93 | ||
93 | /* On most systems <limits.h> would have given us this, but | 94 | /* On most systems <limits.h> would have given us this, but |
94 | * not on some systems (e.g. GNU/Hurd). | 95 | * not on some systems (e.g. GNU/Hurd). |
@@ -152,6 +153,8 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2))) | |||
152 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | 153 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); |
153 | 154 | ||
154 | extern int prefixcmp(const char *str, const char *prefix); | 155 | extern int prefixcmp(const char *str, const char *prefix); |
156 | extern void set_buildid_dir(void); | ||
157 | extern void disable_buildid_cache(void); | ||
155 | 158 | ||
156 | static inline const char *skip_prefix(const char *str, const char *prefix) | 159 | static inline const char *skip_prefix(const char *str, const char *prefix) |
157 | { | 160 | { |