diff options
Diffstat (limited to 'tools')
183 files changed, 5059 insertions, 1607 deletions
diff --git a/tools/lib/api/Makefile b/tools/lib/api/Makefile index ce00f7ee6455..36c08b1f4afb 100644 --- a/tools/lib/api/Makefile +++ b/tools/lib/api/Makefile | |||
| @@ -10,9 +10,14 @@ LIB_OBJS= | |||
| 10 | 10 | ||
| 11 | LIB_H += fs/debugfs.h | 11 | LIB_H += fs/debugfs.h |
| 12 | LIB_H += fs/fs.h | 12 | LIB_H += fs/fs.h |
| 13 | # See comment below about piggybacking... | ||
| 14 | LIB_H += fd/array.h | ||
| 13 | 15 | ||
| 14 | LIB_OBJS += $(OUTPUT)fs/debugfs.o | 16 | LIB_OBJS += $(OUTPUT)fs/debugfs.o |
| 15 | LIB_OBJS += $(OUTPUT)fs/fs.o | 17 | LIB_OBJS += $(OUTPUT)fs/fs.o |
| 18 | # XXX piggybacking here, need to introduce libapikfd, or rename this | ||
| 19 | # to plain libapik.a and make it have it all api goodies | ||
| 20 | LIB_OBJS += $(OUTPUT)fd/array.o | ||
| 16 | 21 | ||
| 17 | LIBFILE = libapikfs.a | 22 | LIBFILE = libapikfs.a |
| 18 | 23 | ||
| @@ -29,7 +34,7 @@ $(LIBFILE): $(LIB_OBJS) | |||
| 29 | $(LIB_OBJS): $(LIB_H) | 34 | $(LIB_OBJS): $(LIB_H) |
| 30 | 35 | ||
| 31 | libapi_dirs: | 36 | libapi_dirs: |
| 32 | $(QUIET_MKDIR)mkdir -p $(OUTPUT)fs/ | 37 | $(QUIET_MKDIR)mkdir -p $(OUTPUT)fd $(OUTPUT)fs |
| 33 | 38 | ||
| 34 | $(OUTPUT)%.o: %.c libapi_dirs | 39 | $(OUTPUT)%.o: %.c libapi_dirs |
| 35 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 40 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< |
diff --git a/tools/lib/api/fd/array.c b/tools/lib/api/fd/array.c new file mode 100644 index 000000000000..0e636c4339b8 --- /dev/null +++ b/tools/lib/api/fd/array.c | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2014, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
| 3 | * | ||
| 4 | * Released under the GPL v2. (and only v2, not any later version) | ||
| 5 | */ | ||
| 6 | #include "array.h" | ||
| 7 | #include <errno.h> | ||
| 8 | #include <fcntl.h> | ||
| 9 | #include <poll.h> | ||
| 10 | #include <stdlib.h> | ||
| 11 | #include <unistd.h> | ||
| 12 | |||
| 13 | void fdarray__init(struct fdarray *fda, int nr_autogrow) | ||
| 14 | { | ||
| 15 | fda->entries = NULL; | ||
| 16 | fda->priv = NULL; | ||
| 17 | fda->nr = fda->nr_alloc = 0; | ||
| 18 | fda->nr_autogrow = nr_autogrow; | ||
| 19 | } | ||
| 20 | |||
| 21 | int fdarray__grow(struct fdarray *fda, int nr) | ||
| 22 | { | ||
| 23 | void *priv; | ||
| 24 | int nr_alloc = fda->nr_alloc + nr; | ||
| 25 | size_t psize = sizeof(fda->priv[0]) * nr_alloc; | ||
| 26 | size_t size = sizeof(struct pollfd) * nr_alloc; | ||
| 27 | struct pollfd *entries = realloc(fda->entries, size); | ||
| 28 | |||
| 29 | if (entries == NULL) | ||
| 30 | return -ENOMEM; | ||
| 31 | |||
| 32 | priv = realloc(fda->priv, psize); | ||
| 33 | if (priv == NULL) { | ||
| 34 | free(entries); | ||
| 35 | return -ENOMEM; | ||
| 36 | } | ||
| 37 | |||
| 38 | fda->nr_alloc = nr_alloc; | ||
| 39 | fda->entries = entries; | ||
| 40 | fda->priv = priv; | ||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow) | ||
| 45 | { | ||
| 46 | struct fdarray *fda = calloc(1, sizeof(*fda)); | ||
| 47 | |||
| 48 | if (fda != NULL) { | ||
| 49 | if (fdarray__grow(fda, nr_alloc)) { | ||
| 50 | free(fda); | ||
| 51 | fda = NULL; | ||
| 52 | } else { | ||
| 53 | fda->nr_autogrow = nr_autogrow; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | return fda; | ||
| 58 | } | ||
| 59 | |||
| 60 | void fdarray__exit(struct fdarray *fda) | ||
| 61 | { | ||
| 62 | free(fda->entries); | ||
| 63 | free(fda->priv); | ||
| 64 | fdarray__init(fda, 0); | ||
| 65 | } | ||
| 66 | |||
| 67 | void fdarray__delete(struct fdarray *fda) | ||
| 68 | { | ||
| 69 | fdarray__exit(fda); | ||
| 70 | free(fda); | ||
| 71 | } | ||
| 72 | |||
| 73 | int fdarray__add(struct fdarray *fda, int fd, short revents) | ||
| 74 | { | ||
| 75 | int pos = fda->nr; | ||
| 76 | |||
| 77 | if (fda->nr == fda->nr_alloc && | ||
| 78 | fdarray__grow(fda, fda->nr_autogrow) < 0) | ||
| 79 | return -ENOMEM; | ||
| 80 | |||
| 81 | fda->entries[fda->nr].fd = fd; | ||
| 82 | fda->entries[fda->nr].events = revents; | ||
| 83 | fda->nr++; | ||
| 84 | return pos; | ||
| 85 | } | ||
| 86 | |||
| 87 | int fdarray__filter(struct fdarray *fda, short revents, | ||
| 88 | void (*entry_destructor)(struct fdarray *fda, int fd)) | ||
| 89 | { | ||
| 90 | int fd, nr = 0; | ||
| 91 | |||
| 92 | if (fda->nr == 0) | ||
| 93 | return 0; | ||
| 94 | |||
| 95 | for (fd = 0; fd < fda->nr; ++fd) { | ||
| 96 | if (fda->entries[fd].revents & revents) { | ||
| 97 | if (entry_destructor) | ||
| 98 | entry_destructor(fda, fd); | ||
| 99 | |||
| 100 | continue; | ||
| 101 | } | ||
| 102 | |||
| 103 | if (fd != nr) { | ||
| 104 | fda->entries[nr] = fda->entries[fd]; | ||
| 105 | fda->priv[nr] = fda->priv[fd]; | ||
| 106 | } | ||
| 107 | |||
| 108 | ++nr; | ||
| 109 | } | ||
| 110 | |||
| 111 | return fda->nr = nr; | ||
| 112 | } | ||
| 113 | |||
| 114 | int fdarray__poll(struct fdarray *fda, int timeout) | ||
| 115 | { | ||
| 116 | return poll(fda->entries, fda->nr, timeout); | ||
| 117 | } | ||
| 118 | |||
| 119 | int fdarray__fprintf(struct fdarray *fda, FILE *fp) | ||
| 120 | { | ||
| 121 | int fd, printed = fprintf(fp, "%d [ ", fda->nr); | ||
| 122 | |||
| 123 | for (fd = 0; fd < fda->nr; ++fd) | ||
| 124 | printed += fprintf(fp, "%s%d", fd ? ", " : "", fda->entries[fd].fd); | ||
| 125 | |||
| 126 | return printed + fprintf(fp, " ]"); | ||
| 127 | } | ||
diff --git a/tools/lib/api/fd/array.h b/tools/lib/api/fd/array.h new file mode 100644 index 000000000000..45db01818f45 --- /dev/null +++ b/tools/lib/api/fd/array.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | #ifndef __API_FD_ARRAY__ | ||
| 2 | #define __API_FD_ARRAY__ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | |||
| 6 | struct pollfd; | ||
| 7 | |||
| 8 | /** | ||
| 9 | * struct fdarray: Array of file descriptors | ||
| 10 | * | ||
| 11 | * @priv: Per array entry priv area, users should access just its contents, | ||
| 12 | * not set it to anything, as it is kept in synch with @entries, being | ||
| 13 | * realloc'ed, * for instance, in fdarray__{grow,filter}. | ||
| 14 | * | ||
| 15 | * I.e. using 'fda->priv[N].idx = * value' where N < fda->nr is ok, | ||
| 16 | * but doing 'fda->priv = malloc(M)' is not allowed. | ||
| 17 | */ | ||
| 18 | struct fdarray { | ||
| 19 | int nr; | ||
| 20 | int nr_alloc; | ||
| 21 | int nr_autogrow; | ||
| 22 | struct pollfd *entries; | ||
| 23 | union { | ||
| 24 | int idx; | ||
| 25 | } *priv; | ||
| 26 | }; | ||
| 27 | |||
| 28 | void fdarray__init(struct fdarray *fda, int nr_autogrow); | ||
| 29 | void fdarray__exit(struct fdarray *fda); | ||
| 30 | |||
| 31 | struct fdarray *fdarray__new(int nr_alloc, int nr_autogrow); | ||
| 32 | void fdarray__delete(struct fdarray *fda); | ||
| 33 | |||
| 34 | int fdarray__add(struct fdarray *fda, int fd, short revents); | ||
| 35 | int fdarray__poll(struct fdarray *fda, int timeout); | ||
| 36 | int fdarray__filter(struct fdarray *fda, short revents, | ||
| 37 | void (*entry_destructor)(struct fdarray *fda, int fd)); | ||
| 38 | int fdarray__grow(struct fdarray *fda, int extra); | ||
| 39 | int fdarray__fprintf(struct fdarray *fda, FILE *fp); | ||
| 40 | |||
| 41 | static inline int fdarray__available_entries(struct fdarray *fda) | ||
| 42 | { | ||
| 43 | return fda->nr_alloc - fda->nr; | ||
| 44 | } | ||
| 45 | |||
| 46 | #endif /* __API_FD_ARRAY__ */ | ||
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore index 782d86e961b9..717221e98450 100644 --- a/tools/perf/.gitignore +++ b/tools/perf/.gitignore | |||
| @@ -15,6 +15,7 @@ perf.data | |||
| 15 | perf.data.old | 15 | perf.data.old |
| 16 | output.svg | 16 | output.svg |
| 17 | perf-archive | 17 | perf-archive |
| 18 | perf-with-kcore | ||
| 18 | tags | 19 | tags |
| 19 | TAGS | 20 | TAGS |
| 20 | cscope* | 21 | cscope* |
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 1513935c399b..aaa869be3dc1 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
| @@ -104,6 +104,9 @@ OPTIONS | |||
| 104 | Specify path to the executable or shared library file for user | 104 | Specify path to the executable or shared library file for user |
| 105 | space tracing. Can also be used with --funcs option. | 105 | space tracing. Can also be used with --funcs option. |
| 106 | 106 | ||
| 107 | --demangle-kernel:: | ||
| 108 | Demangle kernel symbols. | ||
| 109 | |||
| 107 | In absence of -m/-x options, perf probe checks if the first argument after | 110 | In absence of -m/-x options, perf probe checks if the first argument after |
| 108 | the options is an absolute path name. If its an absolute path, perf probe | 111 | the options is an absolute path name. If its an absolute path, perf probe |
| 109 | uses it as a target module/target user space binary to probe. | 112 | uses it as a target module/target user space binary to probe. |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index d2b59af62bc0..0927bf4e6c2a 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -147,7 +147,7 @@ OPTIONS | |||
| 147 | -w:: | 147 | -w:: |
| 148 | --column-widths=<width[,width...]>:: | 148 | --column-widths=<width[,width...]>:: |
| 149 | Force each column width to the provided list, for large terminal | 149 | Force each column width to the provided list, for large terminal |
| 150 | readability. | 150 | readability. 0 means no limit (default behavior). |
| 151 | 151 | ||
| 152 | -t:: | 152 | -t:: |
| 153 | --field-separator=:: | 153 | --field-separator=:: |
| @@ -276,6 +276,9 @@ OPTIONS | |||
| 276 | Demangle symbol names to human readable form. It's enabled by default, | 276 | Demangle symbol names to human readable form. It's enabled by default, |
| 277 | disable with --no-demangle. | 277 | disable with --no-demangle. |
| 278 | 278 | ||
| 279 | --demangle-kernel:: | ||
| 280 | Demangle kernel symbol names to human readable form (for C++ kernels). | ||
| 281 | |||
| 279 | --mem-mode:: | 282 | --mem-mode:: |
| 280 | Use the data addresses of samples in addition to instruction addresses | 283 | Use the data addresses of samples in addition to instruction addresses |
| 281 | to build the histograms. To generate meaningful output, the perf.data | 284 | to build the histograms. To generate meaningful output, the perf.data |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 180ae02137a5..3265b1070518 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
| @@ -98,6 +98,9 @@ Default is to monitor all CPUS. | |||
| 98 | --hide_user_symbols:: | 98 | --hide_user_symbols:: |
| 99 | Hide user symbols. | 99 | Hide user symbols. |
| 100 | 100 | ||
| 101 | --demangle-kernel:: | ||
| 102 | Demangle kernel symbols. | ||
| 103 | |||
| 101 | -D:: | 104 | -D:: |
| 102 | --dump-symtab:: | 105 | --dump-symtab:: |
| 103 | Dump the symbol table used for profiling. | 106 | Dump the symbol table used for profiling. |
| @@ -193,6 +196,12 @@ Default is to monitor all CPUS. | |||
| 193 | sum of shown entries will be always 100%. "absolute" means it retains | 196 | sum of shown entries will be always 100%. "absolute" means it retains |
| 194 | the original value before and after the filter is applied. | 197 | the original value before and after the filter is applied. |
| 195 | 198 | ||
| 199 | -w:: | ||
| 200 | --column-widths=<width[,width...]>:: | ||
| 201 | Force each column width to the provided list, for large terminal | ||
| 202 | readability. 0 means no limit (default behavior). | ||
| 203 | |||
| 204 | |||
| 196 | INTERACTIVE PROMPTING KEYS | 205 | INTERACTIVE PROMPTING KEYS |
| 197 | -------------------------- | 206 | -------------------------- |
| 198 | 207 | ||
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 2240974b7745..262916f4a377 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
| @@ -126,6 +126,7 @@ PYRF_OBJS = | |||
| 126 | SCRIPT_SH = | 126 | SCRIPT_SH = |
| 127 | 127 | ||
| 128 | SCRIPT_SH += perf-archive.sh | 128 | SCRIPT_SH += perf-archive.sh |
| 129 | SCRIPT_SH += perf-with-kcore.sh | ||
| 129 | 130 | ||
| 130 | grep-libs = $(filter -l%,$(1)) | 131 | grep-libs = $(filter -l%,$(1)) |
| 131 | strip-libs = $(filter-out -l%,$(1)) | 132 | strip-libs = $(filter-out -l%,$(1)) |
| @@ -263,6 +264,7 @@ LIB_H += util/xyarray.h | |||
| 263 | LIB_H += util/header.h | 264 | LIB_H += util/header.h |
| 264 | LIB_H += util/help.h | 265 | LIB_H += util/help.h |
| 265 | LIB_H += util/session.h | 266 | LIB_H += util/session.h |
| 267 | LIB_H += util/ordered-events.h | ||
| 266 | LIB_H += util/strbuf.h | 268 | LIB_H += util/strbuf.h |
| 267 | LIB_H += util/strlist.h | 269 | LIB_H += util/strlist.h |
| 268 | LIB_H += util/strfilter.h | 270 | LIB_H += util/strfilter.h |
| @@ -347,6 +349,7 @@ LIB_OBJS += $(OUTPUT)util/machine.o | |||
| 347 | LIB_OBJS += $(OUTPUT)util/map.o | 349 | LIB_OBJS += $(OUTPUT)util/map.o |
| 348 | LIB_OBJS += $(OUTPUT)util/pstack.o | 350 | LIB_OBJS += $(OUTPUT)util/pstack.o |
| 349 | LIB_OBJS += $(OUTPUT)util/session.o | 351 | LIB_OBJS += $(OUTPUT)util/session.o |
| 352 | LIB_OBJS += $(OUTPUT)util/ordered-events.o | ||
| 350 | LIB_OBJS += $(OUTPUT)util/comm.o | 353 | LIB_OBJS += $(OUTPUT)util/comm.o |
| 351 | LIB_OBJS += $(OUTPUT)util/thread.o | 354 | LIB_OBJS += $(OUTPUT)util/thread.o |
| 352 | LIB_OBJS += $(OUTPUT)util/thread_map.o | 355 | LIB_OBJS += $(OUTPUT)util/thread_map.o |
| @@ -399,6 +402,7 @@ LIB_OBJS += $(OUTPUT)tests/perf-record.o | |||
| 399 | LIB_OBJS += $(OUTPUT)tests/rdpmc.o | 402 | LIB_OBJS += $(OUTPUT)tests/rdpmc.o |
| 400 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o | 403 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o |
| 401 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o | 404 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o |
| 405 | LIB_OBJS += $(OUTPUT)tests/fdarray.o | ||
| 402 | LIB_OBJS += $(OUTPUT)tests/pmu.o | 406 | LIB_OBJS += $(OUTPUT)tests/pmu.o |
| 403 | LIB_OBJS += $(OUTPUT)tests/hists_common.o | 407 | LIB_OBJS += $(OUTPUT)tests/hists_common.o |
| 404 | LIB_OBJS += $(OUTPUT)tests/hists_link.o | 408 | LIB_OBJS += $(OUTPUT)tests/hists_link.o |
| @@ -423,6 +427,7 @@ endif | |||
| 423 | endif | 427 | endif |
| 424 | LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o | 428 | LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o |
| 425 | LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o | 429 | LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o |
| 430 | LIB_OBJS += $(OUTPUT)tests/switch-tracking.o | ||
| 426 | 431 | ||
| 427 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 432 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
| 428 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 433 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
| @@ -765,7 +770,7 @@ $(LIBTRACEEVENT)-clean: | |||
| 765 | install-traceevent-plugins: $(LIBTRACEEVENT) | 770 | install-traceevent-plugins: $(LIBTRACEEVENT) |
| 766 | $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins | 771 | $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins |
| 767 | 772 | ||
| 768 | LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch]) | 773 | LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch] $(LIB_PATH)fd/*.[ch]) |
| 769 | 774 | ||
| 770 | # if subdir is set, we've been called from above so target has been built | 775 | # if subdir is set, we've been called from above so target has been built |
| 771 | # already | 776 | # already |
| @@ -875,6 +880,8 @@ install-bin: all install-gtk | |||
| 875 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 880 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
| 876 | $(call QUIET_INSTALL, perf-archive) \ | 881 | $(call QUIET_INSTALL, perf-archive) \ |
| 877 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | 882 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' |
| 883 | $(call QUIET_INSTALL, perf-with-kcore) \ | ||
| 884 | $(INSTALL) $(OUTPUT)perf-with-kcore -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
| 878 | ifndef NO_LIBPERL | 885 | ifndef NO_LIBPERL |
| 879 | $(call QUIET_INSTALL, perl-scripts) \ | 886 | $(call QUIET_INSTALL, perl-scripts) \ |
| 880 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \ | 887 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \ |
| @@ -920,7 +927,7 @@ config-clean: | |||
| 920 | @$(MAKE) -C config/feature-checks clean >/dev/null | 927 | @$(MAKE) -C config/feature-checks clean >/dev/null |
| 921 | 928 | ||
| 922 | clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean | 929 | clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean |
| 923 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) | 930 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) |
| 924 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf | 931 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf |
| 925 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* | 932 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* |
| 926 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean | 933 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean |
diff --git a/tools/perf/arch/arm/tests/dwarf-unwind.c b/tools/perf/arch/arm/tests/dwarf-unwind.c index 9f870d27cb39..62eff847f91c 100644 --- a/tools/perf/arch/arm/tests/dwarf-unwind.c +++ b/tools/perf/arch/arm/tests/dwarf-unwind.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include "thread.h" | 3 | #include "thread.h" |
| 4 | #include "map.h" | 4 | #include "map.h" |
| 5 | #include "event.h" | 5 | #include "event.h" |
| 6 | #include "debug.h" | ||
| 6 | #include "tests/tests.h" | 7 | #include "tests/tests.h" |
| 7 | 8 | ||
| 8 | #define STACK_SIZE 8192 | 9 | #define STACK_SIZE 8192 |
diff --git a/tools/perf/arch/arm/util/unwind-libunwind.c b/tools/perf/arch/arm/util/unwind-libunwind.c index 729ed69a6664..62c397ed3d97 100644 --- a/tools/perf/arch/arm/util/unwind-libunwind.c +++ b/tools/perf/arch/arm/util/unwind-libunwind.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include <libunwind.h> | 3 | #include <libunwind.h> |
| 4 | #include "perf_regs.h" | 4 | #include "perf_regs.h" |
| 5 | #include "../../util/unwind.h" | 5 | #include "../../util/unwind.h" |
| 6 | #include "../../util/debug.h" | ||
| 6 | 7 | ||
| 7 | int libunwind__arch_reg_id(int regnum) | 8 | int libunwind__arch_reg_id(int regnum) |
| 8 | { | 9 | { |
diff --git a/tools/perf/arch/arm64/include/perf_regs.h b/tools/perf/arch/arm64/include/perf_regs.h index e9441b9e2a30..1d3f39c3aa56 100644 --- a/tools/perf/arch/arm64/include/perf_regs.h +++ b/tools/perf/arch/arm64/include/perf_regs.h | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | #include <asm/perf_regs.h> | 6 | #include <asm/perf_regs.h> |
| 7 | 7 | ||
| 8 | #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1) | 8 | #define PERF_REGS_MASK ((1ULL << PERF_REG_ARM64_MAX) - 1) |
| 9 | #define PERF_REGS_MAX PERF_REG_ARM64_MAX | ||
| 10 | |||
| 9 | #define PERF_REG_IP PERF_REG_ARM64_PC | 11 | #define PERF_REG_IP PERF_REG_ARM64_PC |
| 10 | #define PERF_REG_SP PERF_REG_ARM64_SP | 12 | #define PERF_REG_SP PERF_REG_ARM64_SP |
| 11 | 13 | ||
diff --git a/tools/perf/arch/arm64/util/unwind-libunwind.c b/tools/perf/arch/arm64/util/unwind-libunwind.c index 436ee43859dc..a87afa91a99e 100644 --- a/tools/perf/arch/arm64/util/unwind-libunwind.c +++ b/tools/perf/arch/arm64/util/unwind-libunwind.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include <libunwind.h> | 3 | #include <libunwind.h> |
| 4 | #include "perf_regs.h" | 4 | #include "perf_regs.h" |
| 5 | #include "../../util/unwind.h" | 5 | #include "../../util/unwind.h" |
| 6 | #include "../../util/debug.h" | ||
| 6 | 7 | ||
| 7 | int libunwind__arch_reg_id(int regnum) | 8 | int libunwind__arch_reg_id(int regnum) |
| 8 | { | 9 | { |
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c index 42faf369211c..49776f190abf 100644 --- a/tools/perf/arch/common.c +++ b/tools/perf/arch/common.c | |||
| @@ -12,6 +12,11 @@ const char *const arm_triplets[] = { | |||
| 12 | NULL | 12 | NULL |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| 15 | const char *const arm64_triplets[] = { | ||
| 16 | "aarch64-linux-android-", | ||
| 17 | NULL | ||
| 18 | }; | ||
| 19 | |||
| 15 | const char *const powerpc_triplets[] = { | 20 | const char *const powerpc_triplets[] = { |
| 16 | "powerpc-unknown-linux-gnu-", | 21 | "powerpc-unknown-linux-gnu-", |
| 17 | "powerpc64-unknown-linux-gnu-", | 22 | "powerpc64-unknown-linux-gnu-", |
| @@ -105,6 +110,8 @@ static const char *normalize_arch(char *arch) | |||
| 105 | return "x86"; | 110 | return "x86"; |
| 106 | if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5)) | 111 | if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5)) |
| 107 | return "sparc"; | 112 | return "sparc"; |
| 113 | if (!strcmp(arch, "aarch64") || !strcmp(arch, "arm64")) | ||
| 114 | return "arm64"; | ||
| 108 | if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110")) | 115 | if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110")) |
| 109 | return "arm"; | 116 | return "arm"; |
| 110 | if (!strncmp(arch, "s390", 4)) | 117 | if (!strncmp(arch, "s390", 4)) |
| @@ -159,6 +166,8 @@ static int perf_session_env__lookup_binutils_path(struct perf_session_env *env, | |||
| 159 | 166 | ||
| 160 | if (!strcmp(arch, "arm")) | 167 | if (!strcmp(arch, "arm")) |
| 161 | path_list = arm_triplets; | 168 | path_list = arm_triplets; |
| 169 | else if (!strcmp(arch, "arm64")) | ||
| 170 | path_list = arm64_triplets; | ||
| 162 | else if (!strcmp(arch, "powerpc")) | 171 | else if (!strcmp(arch, "powerpc")) |
| 163 | path_list = powerpc_triplets; | 172 | path_list = powerpc_triplets; |
| 164 | else if (!strcmp(arch, "sh")) | 173 | else if (!strcmp(arch, "sh")) |
diff --git a/tools/perf/arch/powerpc/Makefile b/tools/perf/arch/powerpc/Makefile index b92219b1900d..6f7782bea5dd 100644 --- a/tools/perf/arch/powerpc/Makefile +++ b/tools/perf/arch/powerpc/Makefile | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | ifndef NO_DWARF | 1 | ifndef NO_DWARF |
| 2 | PERF_HAVE_DWARF_REGS := 1 | 2 | PERF_HAVE_DWARF_REGS := 1 |
| 3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o | 3 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o |
| 4 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o | ||
| 4 | endif | 5 | endif |
| 5 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | 6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o |
| 6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/skip-callchain-idx.o | ||
diff --git a/tools/perf/arch/powerpc/util/skip-callchain-idx.c b/tools/perf/arch/powerpc/util/skip-callchain-idx.c index a7c23a4b3778..d73ef8bb08c7 100644 --- a/tools/perf/arch/powerpc/util/skip-callchain-idx.c +++ b/tools/perf/arch/powerpc/util/skip-callchain-idx.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | 15 | ||
| 16 | #include "util/thread.h" | 16 | #include "util/thread.h" |
| 17 | #include "util/callchain.h" | 17 | #include "util/callchain.h" |
| 18 | #include "util/debug.h" | ||
| 18 | 19 | ||
| 19 | /* | 20 | /* |
| 20 | * When saving the callchain on Power, the kernel conservatively saves | 21 | * When saving the callchain on Power, the kernel conservatively saves |
diff --git a/tools/perf/bench/futex-hash.c b/tools/perf/bench/futex-hash.c index a84206e9c4aa..fc9bebd2cca0 100644 --- a/tools/perf/bench/futex-hash.c +++ b/tools/perf/bench/futex-hash.c | |||
| @@ -26,6 +26,7 @@ static unsigned int nsecs = 10; | |||
| 26 | /* amount of futexes per thread */ | 26 | /* amount of futexes per thread */ |
| 27 | static unsigned int nfutexes = 1024; | 27 | static unsigned int nfutexes = 1024; |
| 28 | static bool fshared = false, done = false, silent = false; | 28 | static bool fshared = false, done = false, silent = false; |
| 29 | static int futex_flag = 0; | ||
| 29 | 30 | ||
| 30 | struct timeval start, end, runtime; | 31 | struct timeval start, end, runtime; |
| 31 | static pthread_mutex_t thread_lock; | 32 | static pthread_mutex_t thread_lock; |
| @@ -75,8 +76,7 @@ static void *workerfn(void *arg) | |||
| 75 | * such as internal waitqueue handling, thus enlarging | 76 | * such as internal waitqueue handling, thus enlarging |
| 76 | * the critical region protected by hb->lock. | 77 | * the critical region protected by hb->lock. |
| 77 | */ | 78 | */ |
| 78 | ret = futex_wait(&w->futex[i], 1234, NULL, | 79 | ret = futex_wait(&w->futex[i], 1234, NULL, futex_flag); |
| 79 | fshared ? 0 : FUTEX_PRIVATE_FLAG); | ||
| 80 | if (!silent && | 80 | if (!silent && |
| 81 | (!ret || errno != EAGAIN || errno != EWOULDBLOCK)) | 81 | (!ret || errno != EAGAIN || errno != EWOULDBLOCK)) |
| 82 | warn("Non-expected futex return call"); | 82 | warn("Non-expected futex return call"); |
| @@ -135,6 +135,9 @@ int bench_futex_hash(int argc, const char **argv, | |||
| 135 | if (!worker) | 135 | if (!worker) |
| 136 | goto errmem; | 136 | goto errmem; |
| 137 | 137 | ||
| 138 | if (!fshared) | ||
| 139 | futex_flag = FUTEX_PRIVATE_FLAG; | ||
| 140 | |||
| 138 | printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n", | 141 | printf("Run summary [PID %d]: %d threads, each operating on %d [%s] futexes for %d secs.\n\n", |
| 139 | getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs); | 142 | getpid(), nthreads, nfutexes, fshared ? "shared":"private", nsecs); |
| 140 | 143 | ||
diff --git a/tools/perf/bench/futex-requeue.c b/tools/perf/bench/futex-requeue.c index 732403bfd31a..bedff6b5b3cf 100644 --- a/tools/perf/bench/futex-requeue.c +++ b/tools/perf/bench/futex-requeue.c | |||
| @@ -30,16 +30,18 @@ static u_int32_t futex1 = 0, futex2 = 0; | |||
| 30 | static unsigned int nrequeue = 1; | 30 | static unsigned int nrequeue = 1; |
| 31 | 31 | ||
| 32 | static pthread_t *worker; | 32 | static pthread_t *worker; |
| 33 | static bool done = 0, silent = 0; | 33 | static bool done = false, silent = false, fshared = false; |
| 34 | static pthread_mutex_t thread_lock; | 34 | static pthread_mutex_t thread_lock; |
| 35 | static pthread_cond_t thread_parent, thread_worker; | 35 | static pthread_cond_t thread_parent, thread_worker; |
| 36 | static struct stats requeuetime_stats, requeued_stats; | 36 | static struct stats requeuetime_stats, requeued_stats; |
| 37 | static unsigned int ncpus, threads_starting, nthreads = 0; | 37 | static unsigned int ncpus, threads_starting, nthreads = 0; |
| 38 | static int futex_flag = 0; | ||
| 38 | 39 | ||
| 39 | static const struct option options[] = { | 40 | static const struct option options[] = { |
| 40 | OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), | 41 | OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), |
| 41 | OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"), | 42 | OPT_UINTEGER('q', "nrequeue", &nrequeue, "Specify amount of threads to requeue at once"), |
| 42 | OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), | 43 | OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), |
| 44 | OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), | ||
| 43 | OPT_END() | 45 | OPT_END() |
| 44 | }; | 46 | }; |
| 45 | 47 | ||
| @@ -70,7 +72,7 @@ static void *workerfn(void *arg __maybe_unused) | |||
| 70 | pthread_cond_wait(&thread_worker, &thread_lock); | 72 | pthread_cond_wait(&thread_worker, &thread_lock); |
| 71 | pthread_mutex_unlock(&thread_lock); | 73 | pthread_mutex_unlock(&thread_lock); |
| 72 | 74 | ||
| 73 | futex_wait(&futex1, 0, NULL, FUTEX_PRIVATE_FLAG); | 75 | futex_wait(&futex1, 0, NULL, futex_flag); |
| 74 | return NULL; | 76 | return NULL; |
| 75 | } | 77 | } |
| 76 | 78 | ||
| @@ -127,9 +129,12 @@ int bench_futex_requeue(int argc, const char **argv, | |||
| 127 | if (!worker) | 129 | if (!worker) |
| 128 | err(EXIT_FAILURE, "calloc"); | 130 | err(EXIT_FAILURE, "calloc"); |
| 129 | 131 | ||
| 130 | printf("Run summary [PID %d]: Requeuing %d threads (from %p to %p), " | 132 | if (!fshared) |
| 131 | "%d at a time.\n\n", | 133 | futex_flag = FUTEX_PRIVATE_FLAG; |
| 132 | getpid(), nthreads, &futex1, &futex2, nrequeue); | 134 | |
| 135 | printf("Run summary [PID %d]: Requeuing %d threads (from [%s] %p to %p), " | ||
| 136 | "%d at a time.\n\n", getpid(), nthreads, | ||
| 137 | fshared ? "shared":"private", &futex1, &futex2, nrequeue); | ||
| 133 | 138 | ||
| 134 | init_stats(&requeued_stats); | 139 | init_stats(&requeued_stats); |
| 135 | init_stats(&requeuetime_stats); | 140 | init_stats(&requeuetime_stats); |
| @@ -156,16 +161,20 @@ int bench_futex_requeue(int argc, const char **argv, | |||
| 156 | 161 | ||
| 157 | /* Ok, all threads are patiently blocked, start requeueing */ | 162 | /* Ok, all threads are patiently blocked, start requeueing */ |
| 158 | gettimeofday(&start, NULL); | 163 | gettimeofday(&start, NULL); |
| 159 | for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue) | 164 | for (nrequeued = 0; nrequeued < nthreads; nrequeued += nrequeue) { |
| 160 | /* | 165 | /* |
| 161 | * Do not wakeup any tasks blocked on futex1, allowing | 166 | * Do not wakeup any tasks blocked on futex1, allowing |
| 162 | * us to really measure futex_wait functionality. | 167 | * us to really measure futex_wait functionality. |
| 163 | */ | 168 | */ |
| 164 | futex_cmp_requeue(&futex1, 0, &futex2, 0, nrequeue, | 169 | futex_cmp_requeue(&futex1, 0, &futex2, 0, |
| 165 | FUTEX_PRIVATE_FLAG); | 170 | nrequeue, futex_flag); |
| 171 | } | ||
| 166 | gettimeofday(&end, NULL); | 172 | gettimeofday(&end, NULL); |
| 167 | timersub(&end, &start, &runtime); | 173 | timersub(&end, &start, &runtime); |
| 168 | 174 | ||
| 175 | if (nrequeued > nthreads) | ||
| 176 | nrequeued = nthreads; | ||
| 177 | |||
| 169 | update_stats(&requeued_stats, nrequeued); | 178 | update_stats(&requeued_stats, nrequeued); |
| 170 | update_stats(&requeuetime_stats, runtime.tv_usec); | 179 | update_stats(&requeuetime_stats, runtime.tv_usec); |
| 171 | 180 | ||
| @@ -175,7 +184,7 @@ int bench_futex_requeue(int argc, const char **argv, | |||
| 175 | } | 184 | } |
| 176 | 185 | ||
| 177 | /* everybody should be blocked on futex2, wake'em up */ | 186 | /* everybody should be blocked on futex2, wake'em up */ |
| 178 | nrequeued = futex_wake(&futex2, nthreads, FUTEX_PRIVATE_FLAG); | 187 | nrequeued = futex_wake(&futex2, nthreads, futex_flag); |
| 179 | if (nthreads != nrequeued) | 188 | if (nthreads != nrequeued) |
| 180 | warnx("couldn't wakeup all tasks (%d/%d)", nrequeued, nthreads); | 189 | warnx("couldn't wakeup all tasks (%d/%d)", nrequeued, nthreads); |
| 181 | 190 | ||
| @@ -184,7 +193,6 @@ int bench_futex_requeue(int argc, const char **argv, | |||
| 184 | if (ret) | 193 | if (ret) |
| 185 | err(EXIT_FAILURE, "pthread_join"); | 194 | err(EXIT_FAILURE, "pthread_join"); |
| 186 | } | 195 | } |
| 187 | |||
| 188 | } | 196 | } |
| 189 | 197 | ||
| 190 | /* cleanup & report results */ | 198 | /* cleanup & report results */ |
diff --git a/tools/perf/bench/futex-wake.c b/tools/perf/bench/futex-wake.c index 50022cbce87e..929f762be47e 100644 --- a/tools/perf/bench/futex-wake.c +++ b/tools/perf/bench/futex-wake.c | |||
| @@ -31,16 +31,18 @@ static u_int32_t futex1 = 0; | |||
| 31 | static unsigned int nwakes = 1; | 31 | static unsigned int nwakes = 1; |
| 32 | 32 | ||
| 33 | pthread_t *worker; | 33 | pthread_t *worker; |
| 34 | static bool done = false, silent = false; | 34 | static bool done = false, silent = false, fshared = false; |
| 35 | static pthread_mutex_t thread_lock; | 35 | static pthread_mutex_t thread_lock; |
| 36 | static pthread_cond_t thread_parent, thread_worker; | 36 | static pthread_cond_t thread_parent, thread_worker; |
| 37 | static struct stats waketime_stats, wakeup_stats; | 37 | static struct stats waketime_stats, wakeup_stats; |
| 38 | static unsigned int ncpus, threads_starting, nthreads = 0; | 38 | static unsigned int ncpus, threads_starting, nthreads = 0; |
| 39 | static int futex_flag = 0; | ||
| 39 | 40 | ||
| 40 | static const struct option options[] = { | 41 | static const struct option options[] = { |
| 41 | OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), | 42 | OPT_UINTEGER('t', "threads", &nthreads, "Specify amount of threads"), |
| 42 | OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"), | 43 | OPT_UINTEGER('w', "nwakes", &nwakes, "Specify amount of threads to wake at once"), |
| 43 | OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), | 44 | OPT_BOOLEAN( 's', "silent", &silent, "Silent mode: do not display data/details"), |
| 45 | OPT_BOOLEAN( 'S', "shared", &fshared, "Use shared futexes instead of private ones"), | ||
| 44 | OPT_END() | 46 | OPT_END() |
| 45 | }; | 47 | }; |
| 46 | 48 | ||
| @@ -58,7 +60,7 @@ static void *workerfn(void *arg __maybe_unused) | |||
| 58 | pthread_cond_wait(&thread_worker, &thread_lock); | 60 | pthread_cond_wait(&thread_worker, &thread_lock); |
| 59 | pthread_mutex_unlock(&thread_lock); | 61 | pthread_mutex_unlock(&thread_lock); |
| 60 | 62 | ||
| 61 | futex_wait(&futex1, 0, NULL, FUTEX_PRIVATE_FLAG); | 63 | futex_wait(&futex1, 0, NULL, futex_flag); |
| 62 | return NULL; | 64 | return NULL; |
| 63 | } | 65 | } |
| 64 | 66 | ||
| @@ -130,9 +132,12 @@ int bench_futex_wake(int argc, const char **argv, | |||
| 130 | if (!worker) | 132 | if (!worker) |
| 131 | err(EXIT_FAILURE, "calloc"); | 133 | err(EXIT_FAILURE, "calloc"); |
| 132 | 134 | ||
| 133 | printf("Run summary [PID %d]: blocking on %d threads (at futex %p), " | 135 | if (!fshared) |
| 136 | futex_flag = FUTEX_PRIVATE_FLAG; | ||
| 137 | |||
| 138 | printf("Run summary [PID %d]: blocking on %d threads (at [%s] futex %p), " | ||
| 134 | "waking up %d at a time.\n\n", | 139 | "waking up %d at a time.\n\n", |
| 135 | getpid(), nthreads, &futex1, nwakes); | 140 | getpid(), nthreads, fshared ? "shared":"private", &futex1, nwakes); |
| 136 | 141 | ||
| 137 | init_stats(&wakeup_stats); | 142 | init_stats(&wakeup_stats); |
| 138 | init_stats(&waketime_stats); | 143 | init_stats(&waketime_stats); |
| @@ -160,7 +165,7 @@ int bench_futex_wake(int argc, const char **argv, | |||
| 160 | /* Ok, all threads are patiently blocked, start waking folks up */ | 165 | /* Ok, all threads are patiently blocked, start waking folks up */ |
| 161 | gettimeofday(&start, NULL); | 166 | gettimeofday(&start, NULL); |
| 162 | while (nwoken != nthreads) | 167 | while (nwoken != nthreads) |
| 163 | nwoken += futex_wake(&futex1, nwakes, FUTEX_PRIVATE_FLAG); | 168 | nwoken += futex_wake(&futex1, nwakes, futex_flag); |
| 164 | gettimeofday(&end, NULL); | 169 | gettimeofday(&end, NULL); |
| 165 | timersub(&end, &start, &runtime); | 170 | timersub(&end, &start, &runtime); |
| 166 | 171 | ||
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c index 52a56599a543..d7f281c2828d 100644 --- a/tools/perf/bench/sched-messaging.c +++ b/tools/perf/bench/sched-messaging.c | |||
| @@ -26,7 +26,7 @@ | |||
| 26 | #include <sys/socket.h> | 26 | #include <sys/socket.h> |
| 27 | #include <sys/wait.h> | 27 | #include <sys/wait.h> |
| 28 | #include <sys/time.h> | 28 | #include <sys/time.h> |
| 29 | #include <sys/poll.h> | 29 | #include <poll.h> |
| 30 | #include <limits.h> | 30 | #include <limits.h> |
| 31 | #include <err.h> | 31 | #include <err.h> |
| 32 | 32 | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 1ec429fef2be..be5939418425 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -36,7 +36,8 @@ | |||
| 36 | 36 | ||
| 37 | struct perf_annotate { | 37 | struct perf_annotate { |
| 38 | struct perf_tool tool; | 38 | struct perf_tool tool; |
| 39 | bool force, use_tui, use_stdio, use_gtk; | 39 | struct perf_session *session; |
| 40 | bool use_tui, use_stdio, use_gtk; | ||
| 40 | bool full_paths; | 41 | bool full_paths; |
| 41 | bool print_line; | 42 | bool print_line; |
| 42 | bool skip_missing; | 43 | bool skip_missing; |
| @@ -188,18 +189,9 @@ find_next: | |||
| 188 | static int __cmd_annotate(struct perf_annotate *ann) | 189 | static int __cmd_annotate(struct perf_annotate *ann) |
| 189 | { | 190 | { |
| 190 | int ret; | 191 | int ret; |
| 191 | struct perf_session *session; | 192 | struct perf_session *session = ann->session; |
| 192 | struct perf_evsel *pos; | 193 | struct perf_evsel *pos; |
| 193 | u64 total_nr_samples; | 194 | u64 total_nr_samples; |
| 194 | struct perf_data_file file = { | ||
| 195 | .path = input_name, | ||
| 196 | .mode = PERF_DATA_MODE_READ, | ||
| 197 | .force = ann->force, | ||
| 198 | }; | ||
| 199 | |||
| 200 | session = perf_session__new(&file, false, &ann->tool); | ||
| 201 | if (session == NULL) | ||
| 202 | return -ENOMEM; | ||
| 203 | 195 | ||
| 204 | machines__set_symbol_filter(&session->machines, symbol__annotate_init); | 196 | machines__set_symbol_filter(&session->machines, symbol__annotate_init); |
| 205 | 197 | ||
| @@ -207,22 +199,22 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
| 207 | ret = perf_session__cpu_bitmap(session, ann->cpu_list, | 199 | ret = perf_session__cpu_bitmap(session, ann->cpu_list, |
| 208 | ann->cpu_bitmap); | 200 | ann->cpu_bitmap); |
| 209 | if (ret) | 201 | if (ret) |
| 210 | goto out_delete; | 202 | goto out; |
| 211 | } | 203 | } |
| 212 | 204 | ||
| 213 | if (!objdump_path) { | 205 | if (!objdump_path) { |
| 214 | ret = perf_session_env__lookup_objdump(&session->header.env); | 206 | ret = perf_session_env__lookup_objdump(&session->header.env); |
| 215 | if (ret) | 207 | if (ret) |
| 216 | goto out_delete; | 208 | goto out; |
| 217 | } | 209 | } |
| 218 | 210 | ||
| 219 | ret = perf_session__process_events(session, &ann->tool); | 211 | ret = perf_session__process_events(session, &ann->tool); |
| 220 | if (ret) | 212 | if (ret) |
| 221 | goto out_delete; | 213 | goto out; |
| 222 | 214 | ||
| 223 | if (dump_trace) { | 215 | if (dump_trace) { |
| 224 | perf_session__fprintf_nr_events(session, stdout); | 216 | perf_session__fprintf_nr_events(session, stdout); |
| 225 | goto out_delete; | 217 | goto out; |
| 226 | } | 218 | } |
| 227 | 219 | ||
| 228 | if (verbose > 3) | 220 | if (verbose > 3) |
| @@ -250,8 +242,8 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
| 250 | } | 242 | } |
| 251 | 243 | ||
| 252 | if (total_nr_samples == 0) { | 244 | if (total_nr_samples == 0) { |
| 253 | ui__error("The %s file has no samples!\n", file.path); | 245 | ui__error("The %s file has no samples!\n", session->file->path); |
| 254 | goto out_delete; | 246 | goto out; |
| 255 | } | 247 | } |
| 256 | 248 | ||
| 257 | if (use_browser == 2) { | 249 | if (use_browser == 2) { |
| @@ -261,24 +253,12 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
| 261 | "perf_gtk__show_annotations"); | 253 | "perf_gtk__show_annotations"); |
| 262 | if (show_annotations == NULL) { | 254 | if (show_annotations == NULL) { |
| 263 | ui__error("GTK browser not found!\n"); | 255 | ui__error("GTK browser not found!\n"); |
| 264 | goto out_delete; | 256 | goto out; |
| 265 | } | 257 | } |
| 266 | show_annotations(); | 258 | show_annotations(); |
| 267 | } | 259 | } |
| 268 | 260 | ||
| 269 | out_delete: | 261 | out: |
| 270 | /* | ||
| 271 | * Speed up the exit process, for large files this can | ||
| 272 | * take quite a while. | ||
| 273 | * | ||
| 274 | * XXX Enable this when using valgrind or if we ever | ||
| 275 | * librarize this command. | ||
| 276 | * | ||
| 277 | * Also experiment with obstacks to see how much speed | ||
| 278 | * up we'll get here. | ||
| 279 | * | ||
| 280 | * perf_session__delete(session); | ||
| 281 | */ | ||
| 282 | return ret; | 262 | return ret; |
| 283 | } | 263 | } |
| 284 | 264 | ||
| @@ -297,10 +277,14 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 297 | .comm = perf_event__process_comm, | 277 | .comm = perf_event__process_comm, |
| 298 | .exit = perf_event__process_exit, | 278 | .exit = perf_event__process_exit, |
| 299 | .fork = perf_event__process_fork, | 279 | .fork = perf_event__process_fork, |
| 300 | .ordered_samples = true, | 280 | .ordered_events = true, |
| 301 | .ordering_requires_timestamps = true, | 281 | .ordering_requires_timestamps = true, |
| 302 | }, | 282 | }, |
| 303 | }; | 283 | }; |
| 284 | struct perf_data_file file = { | ||
| 285 | .path = input_name, | ||
| 286 | .mode = PERF_DATA_MODE_READ, | ||
| 287 | }; | ||
| 304 | const struct option options[] = { | 288 | const struct option options[] = { |
| 305 | OPT_STRING('i', "input", &input_name, "file", | 289 | OPT_STRING('i', "input", &input_name, "file", |
| 306 | "input file name"), | 290 | "input file name"), |
| @@ -308,7 +292,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 308 | "only consider symbols in these dsos"), | 292 | "only consider symbols in these dsos"), |
| 309 | OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol", | 293 | OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol", |
| 310 | "symbol to annotate"), | 294 | "symbol to annotate"), |
| 311 | OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"), | 295 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), |
| 312 | OPT_INCR('v', "verbose", &verbose, | 296 | OPT_INCR('v', "verbose", &verbose, |
| 313 | "be more verbose (show symbol address, etc)"), | 297 | "be more verbose (show symbol address, etc)"), |
| 314 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 298 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
| @@ -341,6 +325,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 341 | "Show event group information together"), | 325 | "Show event group information together"), |
| 342 | OPT_END() | 326 | OPT_END() |
| 343 | }; | 327 | }; |
| 328 | int ret; | ||
| 344 | 329 | ||
| 345 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 330 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
| 346 | 331 | ||
| @@ -353,11 +338,16 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 353 | 338 | ||
| 354 | setup_browser(true); | 339 | setup_browser(true); |
| 355 | 340 | ||
| 341 | annotate.session = perf_session__new(&file, false, &annotate.tool); | ||
| 342 | if (annotate.session == NULL) | ||
| 343 | return -1; | ||
| 344 | |||
| 356 | symbol_conf.priv_size = sizeof(struct annotation); | 345 | symbol_conf.priv_size = sizeof(struct annotation); |
| 357 | symbol_conf.try_vmlinux_path = true; | 346 | symbol_conf.try_vmlinux_path = true; |
| 358 | 347 | ||
| 359 | if (symbol__init() < 0) | 348 | ret = symbol__init(&annotate.session->header.env); |
| 360 | return -1; | 349 | if (ret < 0) |
| 350 | goto out_delete; | ||
| 361 | 351 | ||
| 362 | if (setup_sorting() < 0) | 352 | if (setup_sorting() < 0) |
| 363 | usage_with_options(annotate_usage, options); | 353 | usage_with_options(annotate_usage, options); |
| @@ -373,5 +363,20 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 373 | annotate.sym_hist_filter = argv[0]; | 363 | annotate.sym_hist_filter = argv[0]; |
| 374 | } | 364 | } |
| 375 | 365 | ||
| 376 | return __cmd_annotate(&annotate); | 366 | ret = __cmd_annotate(&annotate); |
| 367 | |||
| 368 | out_delete: | ||
| 369 | /* | ||
| 370 | * Speed up the exit process, for large files this can | ||
| 371 | * take quite a while. | ||
| 372 | * | ||
| 373 | * XXX Enable this when using valgrind or if we ever | ||
| 374 | * librarize this command. | ||
| 375 | * | ||
| 376 | * Also experiment with obstacks to see how much speed | ||
| 377 | * up we'll get here. | ||
| 378 | * | ||
| 379 | * perf_session__delete(session); | ||
| 380 | */ | ||
| 381 | return ret; | ||
| 377 | } | 382 | } |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 2a2c78f80876..70385756da63 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
| @@ -246,20 +246,9 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) | |||
| 246 | return true; | 246 | return true; |
| 247 | } | 247 | } |
| 248 | 248 | ||
| 249 | static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp) | 249 | static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *fp) |
| 250 | { | 250 | { |
| 251 | struct perf_data_file file = { | ||
| 252 | .path = filename, | ||
| 253 | .mode = PERF_DATA_MODE_READ, | ||
| 254 | .force = force, | ||
| 255 | }; | ||
| 256 | struct perf_session *session = perf_session__new(&file, false, NULL); | ||
| 257 | if (session == NULL) | ||
| 258 | return -1; | ||
| 259 | |||
| 260 | perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0); | 251 | perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0); |
| 261 | perf_session__delete(session); | ||
| 262 | |||
| 263 | return 0; | 252 | return 0; |
| 264 | } | 253 | } |
| 265 | 254 | ||
| @@ -302,6 +291,12 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
| 302 | *missing_filename = NULL, | 291 | *missing_filename = NULL, |
| 303 | *update_name_list_str = NULL, | 292 | *update_name_list_str = NULL, |
| 304 | *kcore_filename; | 293 | *kcore_filename; |
| 294 | char sbuf[STRERR_BUFSIZE]; | ||
| 295 | |||
| 296 | struct perf_data_file file = { | ||
| 297 | .mode = PERF_DATA_MODE_READ, | ||
| 298 | }; | ||
| 299 | struct perf_session *session = NULL; | ||
| 305 | 300 | ||
| 306 | const struct option buildid_cache_options[] = { | 301 | const struct option buildid_cache_options[] = { |
| 307 | OPT_STRING('a', "add", &add_name_list_str, | 302 | OPT_STRING('a', "add", &add_name_list_str, |
| @@ -326,8 +321,17 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
| 326 | argc = parse_options(argc, argv, buildid_cache_options, | 321 | argc = parse_options(argc, argv, buildid_cache_options, |
| 327 | buildid_cache_usage, 0); | 322 | buildid_cache_usage, 0); |
| 328 | 323 | ||
| 329 | if (symbol__init() < 0) | 324 | if (missing_filename) { |
| 330 | return -1; | 325 | file.path = missing_filename; |
| 326 | file.force = force; | ||
| 327 | |||
| 328 | session = perf_session__new(&file, false, NULL); | ||
| 329 | if (session == NULL) | ||
| 330 | return -1; | ||
| 331 | } | ||
| 332 | |||
| 333 | if (symbol__init(session ? &session->header.env : NULL) < 0) | ||
| 334 | goto out; | ||
| 331 | 335 | ||
| 332 | setup_pager(); | 336 | setup_pager(); |
| 333 | 337 | ||
| @@ -344,7 +348,7 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
| 344 | continue; | 348 | continue; |
| 345 | } | 349 | } |
| 346 | pr_warning("Couldn't add %s: %s\n", | 350 | pr_warning("Couldn't add %s: %s\n", |
| 347 | pos->s, strerror(errno)); | 351 | pos->s, strerror_r(errno, sbuf, sizeof(sbuf))); |
| 348 | } | 352 | } |
| 349 | 353 | ||
| 350 | strlist__delete(list); | 354 | strlist__delete(list); |
| @@ -362,7 +366,7 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
| 362 | continue; | 366 | continue; |
| 363 | } | 367 | } |
| 364 | pr_warning("Couldn't remove %s: %s\n", | 368 | pr_warning("Couldn't remove %s: %s\n", |
| 365 | pos->s, strerror(errno)); | 369 | pos->s, strerror_r(errno, sbuf, sizeof(sbuf))); |
| 366 | } | 370 | } |
| 367 | 371 | ||
| 368 | strlist__delete(list); | 372 | strlist__delete(list); |
| @@ -370,7 +374,7 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
| 370 | } | 374 | } |
| 371 | 375 | ||
| 372 | if (missing_filename) | 376 | if (missing_filename) |
| 373 | ret = build_id_cache__fprintf_missing(missing_filename, force, stdout); | 377 | ret = build_id_cache__fprintf_missing(session, stdout); |
| 374 | 378 | ||
| 375 | if (update_name_list_str) { | 379 | if (update_name_list_str) { |
| 376 | list = strlist__new(true, update_name_list_str); | 380 | list = strlist__new(true, update_name_list_str); |
| @@ -383,7 +387,7 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
| 383 | continue; | 387 | continue; |
| 384 | } | 388 | } |
| 385 | pr_warning("Couldn't update %s: %s\n", | 389 | pr_warning("Couldn't update %s: %s\n", |
| 386 | pos->s, strerror(errno)); | 390 | pos->s, strerror_r(errno, sbuf, sizeof(sbuf))); |
| 387 | } | 391 | } |
| 388 | 392 | ||
| 389 | strlist__delete(list); | 393 | strlist__delete(list); |
| @@ -394,5 +398,9 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
| 394 | build_id_cache__add_kcore(kcore_filename, debugdir, force)) | 398 | build_id_cache__add_kcore(kcore_filename, debugdir, force)) |
| 395 | pr_warning("Couldn't add %s\n", kcore_filename); | 399 | pr_warning("Couldn't add %s\n", kcore_filename); |
| 396 | 400 | ||
| 401 | out: | ||
| 402 | if (session) | ||
| 403 | perf_session__delete(session); | ||
| 404 | |||
| 397 | return ret; | 405 | return ret; |
| 398 | } | 406 | } |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 9a5a035cb426..a3ce19f7aebd 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
| @@ -360,7 +360,7 @@ static struct perf_tool tool = { | |||
| 360 | .exit = perf_event__process_exit, | 360 | .exit = perf_event__process_exit, |
| 361 | .fork = perf_event__process_fork, | 361 | .fork = perf_event__process_fork, |
| 362 | .lost = perf_event__process_lost, | 362 | .lost = perf_event__process_lost, |
| 363 | .ordered_samples = true, | 363 | .ordered_events = true, |
| 364 | .ordering_requires_timestamps = true, | 364 | .ordering_requires_timestamps = true, |
| 365 | }; | 365 | }; |
| 366 | 366 | ||
| @@ -683,7 +683,7 @@ static int __cmd_diff(void) | |||
| 683 | d->session = perf_session__new(&d->file, false, &tool); | 683 | d->session = perf_session__new(&d->file, false, &tool); |
| 684 | if (!d->session) { | 684 | if (!d->session) { |
| 685 | pr_err("Failed to open %s\n", d->file.path); | 685 | pr_err("Failed to open %s\n", d->file.path); |
| 686 | ret = -ENOMEM; | 686 | ret = -1; |
| 687 | goto out_delete; | 687 | goto out_delete; |
| 688 | } | 688 | } |
| 689 | 689 | ||
| @@ -1143,7 +1143,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1143 | 1143 | ||
| 1144 | argc = parse_options(argc, argv, options, diff_usage, 0); | 1144 | argc = parse_options(argc, argv, options, diff_usage, 0); |
| 1145 | 1145 | ||
| 1146 | if (symbol__init() < 0) | 1146 | if (symbol__init(NULL) < 0) |
| 1147 | return -1; | 1147 | return -1; |
| 1148 | 1148 | ||
| 1149 | if (data_init(argc, argv) < 0) | 1149 | if (data_init(argc, argv) < 0) |
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 66e12f55c052..0f93f859b782 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c | |||
| @@ -28,7 +28,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details | |||
| 28 | 28 | ||
| 29 | session = perf_session__new(&file, 0, NULL); | 29 | session = perf_session__new(&file, 0, NULL); |
| 30 | if (session == NULL) | 30 | if (session == NULL) |
| 31 | return -ENOMEM; | 31 | return -1; |
| 32 | 32 | ||
| 33 | evlist__for_each(session->evlist, pos) | 33 | evlist__for_each(session->evlist, pos) |
| 34 | perf_evsel__fprintf(pos, details, stdout); | 34 | perf_evsel__fprintf(pos, details, stdout); |
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 0384d930480b..25d20628212e 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c | |||
| @@ -103,6 +103,8 @@ static int check_emacsclient_version(void) | |||
| 103 | 103 | ||
| 104 | static void exec_woman_emacs(const char *path, const char *page) | 104 | static void exec_woman_emacs(const char *path, const char *page) |
| 105 | { | 105 | { |
| 106 | char sbuf[STRERR_BUFSIZE]; | ||
| 107 | |||
| 106 | if (!check_emacsclient_version()) { | 108 | if (!check_emacsclient_version()) { |
| 107 | /* This works only with emacsclient version >= 22. */ | 109 | /* This works only with emacsclient version >= 22. */ |
| 108 | struct strbuf man_page = STRBUF_INIT; | 110 | struct strbuf man_page = STRBUF_INIT; |
| @@ -111,16 +113,19 @@ static void exec_woman_emacs(const char *path, const char *page) | |||
| 111 | path = "emacsclient"; | 113 | path = "emacsclient"; |
| 112 | strbuf_addf(&man_page, "(woman \"%s\")", page); | 114 | strbuf_addf(&man_page, "(woman \"%s\")", page); |
| 113 | execlp(path, "emacsclient", "-e", man_page.buf, NULL); | 115 | execlp(path, "emacsclient", "-e", man_page.buf, NULL); |
| 114 | warning("failed to exec '%s': %s", path, strerror(errno)); | 116 | warning("failed to exec '%s': %s", path, |
| 117 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 115 | } | 118 | } |
| 116 | } | 119 | } |
| 117 | 120 | ||
| 118 | static void exec_man_konqueror(const char *path, const char *page) | 121 | static void exec_man_konqueror(const char *path, const char *page) |
| 119 | { | 122 | { |
| 120 | const char *display = getenv("DISPLAY"); | 123 | const char *display = getenv("DISPLAY"); |
| 124 | |||
| 121 | if (display && *display) { | 125 | if (display && *display) { |
| 122 | struct strbuf man_page = STRBUF_INIT; | 126 | struct strbuf man_page = STRBUF_INIT; |
| 123 | const char *filename = "kfmclient"; | 127 | const char *filename = "kfmclient"; |
| 128 | char sbuf[STRERR_BUFSIZE]; | ||
| 124 | 129 | ||
| 125 | /* It's simpler to launch konqueror using kfmclient. */ | 130 | /* It's simpler to launch konqueror using kfmclient. */ |
| 126 | if (path) { | 131 | if (path) { |
| @@ -139,24 +144,31 @@ static void exec_man_konqueror(const char *path, const char *page) | |||
| 139 | path = "kfmclient"; | 144 | path = "kfmclient"; |
| 140 | strbuf_addf(&man_page, "man:%s(1)", page); | 145 | strbuf_addf(&man_page, "man:%s(1)", page); |
| 141 | execlp(path, filename, "newTab", man_page.buf, NULL); | 146 | execlp(path, filename, "newTab", man_page.buf, NULL); |
| 142 | warning("failed to exec '%s': %s", path, strerror(errno)); | 147 | warning("failed to exec '%s': %s", path, |
| 148 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 143 | } | 149 | } |
| 144 | } | 150 | } |
| 145 | 151 | ||
| 146 | static void exec_man_man(const char *path, const char *page) | 152 | static void exec_man_man(const char *path, const char *page) |
| 147 | { | 153 | { |
| 154 | char sbuf[STRERR_BUFSIZE]; | ||
| 155 | |||
| 148 | if (!path) | 156 | if (!path) |
| 149 | path = "man"; | 157 | path = "man"; |
| 150 | execlp(path, "man", page, NULL); | 158 | execlp(path, "man", page, NULL); |
| 151 | warning("failed to exec '%s': %s", path, strerror(errno)); | 159 | warning("failed to exec '%s': %s", path, |
| 160 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 152 | } | 161 | } |
| 153 | 162 | ||
| 154 | static void exec_man_cmd(const char *cmd, const char *page) | 163 | static void exec_man_cmd(const char *cmd, const char *page) |
| 155 | { | 164 | { |
| 156 | struct strbuf shell_cmd = STRBUF_INIT; | 165 | struct strbuf shell_cmd = STRBUF_INIT; |
| 166 | char sbuf[STRERR_BUFSIZE]; | ||
| 167 | |||
| 157 | strbuf_addf(&shell_cmd, "%s %s", cmd, page); | 168 | strbuf_addf(&shell_cmd, "%s %s", cmd, page); |
| 158 | execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL); | 169 | execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL); |
| 159 | warning("failed to exec '%s': %s", cmd, strerror(errno)); | 170 | warning("failed to exec '%s': %s", cmd, |
| 171 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 160 | } | 172 | } |
| 161 | 173 | ||
| 162 | static void add_man_viewer(const char *name) | 174 | static void add_man_viewer(const char *name) |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 9a02807387d6..de99ca1bb942 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | struct perf_inject { | 24 | struct perf_inject { |
| 25 | struct perf_tool tool; | 25 | struct perf_tool tool; |
| 26 | struct perf_session *session; | ||
| 26 | bool build_ids; | 27 | bool build_ids; |
| 27 | bool sched_stat; | 28 | bool sched_stat; |
| 28 | const char *input_name; | 29 | const char *input_name; |
| @@ -340,12 +341,8 @@ static int perf_evsel__check_stype(struct perf_evsel *evsel, | |||
| 340 | 341 | ||
| 341 | static int __cmd_inject(struct perf_inject *inject) | 342 | static int __cmd_inject(struct perf_inject *inject) |
| 342 | { | 343 | { |
| 343 | struct perf_session *session; | ||
| 344 | int ret = -EINVAL; | 344 | int ret = -EINVAL; |
| 345 | struct perf_data_file file = { | 345 | struct perf_session *session = inject->session; |
| 346 | .path = inject->input_name, | ||
| 347 | .mode = PERF_DATA_MODE_READ, | ||
| 348 | }; | ||
| 349 | struct perf_data_file *file_out = &inject->output; | 346 | struct perf_data_file *file_out = &inject->output; |
| 350 | 347 | ||
| 351 | signal(SIGINT, sig_handler); | 348 | signal(SIGINT, sig_handler); |
| @@ -357,16 +354,12 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 357 | inject->tool.tracing_data = perf_event__repipe_tracing_data; | 354 | inject->tool.tracing_data = perf_event__repipe_tracing_data; |
| 358 | } | 355 | } |
| 359 | 356 | ||
| 360 | session = perf_session__new(&file, true, &inject->tool); | ||
| 361 | if (session == NULL) | ||
| 362 | return -ENOMEM; | ||
| 363 | |||
| 364 | if (inject->build_ids) { | 357 | if (inject->build_ids) { |
| 365 | inject->tool.sample = perf_event__inject_buildid; | 358 | inject->tool.sample = perf_event__inject_buildid; |
| 366 | } else if (inject->sched_stat) { | 359 | } else if (inject->sched_stat) { |
| 367 | struct perf_evsel *evsel; | 360 | struct perf_evsel *evsel; |
| 368 | 361 | ||
| 369 | inject->tool.ordered_samples = true; | 362 | inject->tool.ordered_events = true; |
| 370 | 363 | ||
| 371 | evlist__for_each(session->evlist, evsel) { | 364 | evlist__for_each(session->evlist, evsel) { |
| 372 | const char *name = perf_evsel__name(evsel); | 365 | const char *name = perf_evsel__name(evsel); |
| @@ -396,8 +389,6 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 396 | perf_session__write_header(session, session->evlist, file_out->fd, true); | 389 | perf_session__write_header(session, session->evlist, file_out->fd, true); |
| 397 | } | 390 | } |
| 398 | 391 | ||
| 399 | perf_session__delete(session); | ||
| 400 | |||
| 401 | return ret; | 392 | return ret; |
| 402 | } | 393 | } |
| 403 | 394 | ||
| @@ -427,6 +418,11 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 427 | .mode = PERF_DATA_MODE_WRITE, | 418 | .mode = PERF_DATA_MODE_WRITE, |
| 428 | }, | 419 | }, |
| 429 | }; | 420 | }; |
| 421 | struct perf_data_file file = { | ||
| 422 | .mode = PERF_DATA_MODE_READ, | ||
| 423 | }; | ||
| 424 | int ret; | ||
| 425 | |||
| 430 | const struct option options[] = { | 426 | const struct option options[] = { |
| 431 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, | 427 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, |
| 432 | "Inject build-ids into the output stream"), | 428 | "Inject build-ids into the output stream"), |
| @@ -461,8 +457,17 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 461 | return -1; | 457 | return -1; |
| 462 | } | 458 | } |
| 463 | 459 | ||
| 464 | if (symbol__init() < 0) | 460 | file.path = inject.input_name; |
| 461 | inject.session = perf_session__new(&file, true, &inject.tool); | ||
| 462 | if (inject.session == NULL) | ||
| 463 | return -1; | ||
| 464 | |||
| 465 | if (symbol__init(&inject.session->header.env) < 0) | ||
| 465 | return -1; | 466 | return -1; |
| 466 | 467 | ||
| 467 | return __cmd_inject(&inject); | 468 | ret = __cmd_inject(&inject); |
| 469 | |||
| 470 | perf_session__delete(inject.session); | ||
| 471 | |||
| 472 | return ret; | ||
| 468 | } | 473 | } |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index bef3376bfaf3..f295141025bc 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
| @@ -256,7 +256,9 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
| 256 | static struct perf_tool perf_kmem = { | 256 | static struct perf_tool perf_kmem = { |
| 257 | .sample = process_sample_event, | 257 | .sample = process_sample_event, |
| 258 | .comm = perf_event__process_comm, | 258 | .comm = perf_event__process_comm, |
| 259 | .ordered_samples = true, | 259 | .mmap = perf_event__process_mmap, |
| 260 | .mmap2 = perf_event__process_mmap2, | ||
| 261 | .ordered_events = true, | ||
| 260 | }; | 262 | }; |
| 261 | 263 | ||
| 262 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) | 264 | static double fragmentation(unsigned long n_req, unsigned long n_alloc) |
| @@ -403,10 +405,9 @@ static void sort_result(void) | |||
| 403 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); | 405 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); |
| 404 | } | 406 | } |
| 405 | 407 | ||
| 406 | static int __cmd_kmem(void) | 408 | static int __cmd_kmem(struct perf_session *session) |
| 407 | { | 409 | { |
| 408 | int err = -EINVAL; | 410 | int err = -EINVAL; |
| 409 | struct perf_session *session; | ||
| 410 | const struct perf_evsel_str_handler kmem_tracepoints[] = { | 411 | const struct perf_evsel_str_handler kmem_tracepoints[] = { |
| 411 | { "kmem:kmalloc", perf_evsel__process_alloc_event, }, | 412 | { "kmem:kmalloc", perf_evsel__process_alloc_event, }, |
| 412 | { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, }, | 413 | { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, }, |
| @@ -415,34 +416,22 @@ static int __cmd_kmem(void) | |||
| 415 | { "kmem:kfree", perf_evsel__process_free_event, }, | 416 | { "kmem:kfree", perf_evsel__process_free_event, }, |
| 416 | { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, | 417 | { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, |
| 417 | }; | 418 | }; |
| 418 | struct perf_data_file file = { | ||
| 419 | .path = input_name, | ||
| 420 | .mode = PERF_DATA_MODE_READ, | ||
| 421 | }; | ||
| 422 | |||
| 423 | session = perf_session__new(&file, false, &perf_kmem); | ||
| 424 | if (session == NULL) | ||
| 425 | return -ENOMEM; | ||
| 426 | |||
| 427 | if (perf_session__create_kernel_maps(session) < 0) | ||
| 428 | goto out_delete; | ||
| 429 | 419 | ||
| 430 | if (!perf_session__has_traces(session, "kmem record")) | 420 | if (!perf_session__has_traces(session, "kmem record")) |
| 431 | goto out_delete; | 421 | goto out; |
| 432 | 422 | ||
| 433 | if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) { | 423 | if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) { |
| 434 | pr_err("Initializing perf session tracepoint handlers failed\n"); | 424 | pr_err("Initializing perf session tracepoint handlers failed\n"); |
| 435 | return -1; | 425 | goto out; |
| 436 | } | 426 | } |
| 437 | 427 | ||
| 438 | setup_pager(); | 428 | setup_pager(); |
| 439 | err = perf_session__process_events(session, &perf_kmem); | 429 | err = perf_session__process_events(session, &perf_kmem); |
| 440 | if (err != 0) | 430 | if (err != 0) |
| 441 | goto out_delete; | 431 | goto out; |
| 442 | sort_result(); | 432 | sort_result(); |
| 443 | print_result(session); | 433 | print_result(session); |
| 444 | out_delete: | 434 | out: |
| 445 | perf_session__delete(session); | ||
| 446 | return err; | 435 | return err; |
| 447 | } | 436 | } |
| 448 | 437 | ||
| @@ -689,29 +678,46 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 689 | NULL, | 678 | NULL, |
| 690 | NULL | 679 | NULL |
| 691 | }; | 680 | }; |
| 681 | struct perf_session *session; | ||
| 682 | struct perf_data_file file = { | ||
| 683 | .path = input_name, | ||
| 684 | .mode = PERF_DATA_MODE_READ, | ||
| 685 | }; | ||
| 686 | int ret = -1; | ||
| 687 | |||
| 692 | argc = parse_options_subcommand(argc, argv, kmem_options, | 688 | argc = parse_options_subcommand(argc, argv, kmem_options, |
| 693 | kmem_subcommands, kmem_usage, 0); | 689 | kmem_subcommands, kmem_usage, 0); |
| 694 | 690 | ||
| 695 | if (!argc) | 691 | if (!argc) |
| 696 | usage_with_options(kmem_usage, kmem_options); | 692 | usage_with_options(kmem_usage, kmem_options); |
| 697 | 693 | ||
| 698 | symbol__init(); | ||
| 699 | |||
| 700 | if (!strncmp(argv[0], "rec", 3)) { | 694 | if (!strncmp(argv[0], "rec", 3)) { |
| 695 | symbol__init(NULL); | ||
| 701 | return __cmd_record(argc, argv); | 696 | return __cmd_record(argc, argv); |
| 702 | } else if (!strcmp(argv[0], "stat")) { | 697 | } |
| 698 | |||
| 699 | session = perf_session__new(&file, false, &perf_kmem); | ||
| 700 | if (session == NULL) | ||
| 701 | return -1; | ||
| 702 | |||
| 703 | symbol__init(&session->header.env); | ||
| 704 | |||
| 705 | if (!strcmp(argv[0], "stat")) { | ||
| 703 | if (cpu__setup_cpunode_map()) | 706 | if (cpu__setup_cpunode_map()) |
| 704 | return -1; | 707 | goto out_delete; |
| 705 | 708 | ||
| 706 | if (list_empty(&caller_sort)) | 709 | if (list_empty(&caller_sort)) |
| 707 | setup_sorting(&caller_sort, default_sort_order); | 710 | setup_sorting(&caller_sort, default_sort_order); |
| 708 | if (list_empty(&alloc_sort)) | 711 | if (list_empty(&alloc_sort)) |
| 709 | setup_sorting(&alloc_sort, default_sort_order); | 712 | setup_sorting(&alloc_sort, default_sort_order); |
| 710 | 713 | ||
| 711 | return __cmd_kmem(); | 714 | ret = __cmd_kmem(session); |
| 712 | } else | 715 | } else |
| 713 | usage_with_options(kmem_usage, kmem_options); | 716 | usage_with_options(kmem_usage, kmem_options); |
| 714 | 717 | ||
| 715 | return 0; | 718 | out_delete: |
| 719 | perf_session__delete(session); | ||
| 720 | |||
| 721 | return ret; | ||
| 716 | } | 722 | } |
| 717 | 723 | ||
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 43367eb00510..d8bf2271f4ea 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
| @@ -543,14 +543,12 @@ static void print_vcpu_info(struct perf_kvm_stat *kvm) | |||
| 543 | 543 | ||
| 544 | pr_info("Analyze events for "); | 544 | pr_info("Analyze events for "); |
| 545 | 545 | ||
| 546 | if (kvm->live) { | 546 | if (kvm->opts.target.system_wide) |
| 547 | if (kvm->opts.target.system_wide) | 547 | pr_info("all VMs, "); |
| 548 | pr_info("all VMs, "); | 548 | else if (kvm->opts.target.pid) |
| 549 | else if (kvm->opts.target.pid) | 549 | pr_info("pid(s) %s, ", kvm->opts.target.pid); |
| 550 | pr_info("pid(s) %s, ", kvm->opts.target.pid); | 550 | else |
| 551 | else | 551 | pr_info("dazed and confused on what is monitored, "); |
| 552 | pr_info("dazed and confused on what is monitored, "); | ||
| 553 | } | ||
| 554 | 552 | ||
| 555 | if (vcpu == -1) | 553 | if (vcpu == -1) |
| 556 | pr_info("all VCPUs:\n\n"); | 554 | pr_info("all VCPUs:\n\n"); |
| @@ -592,8 +590,8 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
| 592 | pr_info("%9s ", "Samples%"); | 590 | pr_info("%9s ", "Samples%"); |
| 593 | 591 | ||
| 594 | pr_info("%9s ", "Time%"); | 592 | pr_info("%9s ", "Time%"); |
| 595 | pr_info("%10s ", "Min Time"); | 593 | pr_info("%11s ", "Min Time"); |
| 596 | pr_info("%10s ", "Max Time"); | 594 | pr_info("%11s ", "Max Time"); |
| 597 | pr_info("%16s ", "Avg time"); | 595 | pr_info("%16s ", "Avg time"); |
| 598 | pr_info("\n\n"); | 596 | pr_info("\n\n"); |
| 599 | 597 | ||
| @@ -610,8 +608,8 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
| 610 | pr_info("%10llu ", (unsigned long long)ecount); | 608 | pr_info("%10llu ", (unsigned long long)ecount); |
| 611 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); | 609 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); |
| 612 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); | 610 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); |
| 613 | pr_info("%8" PRIu64 "us ", min / 1000); | 611 | pr_info("%9.2fus ", (double)min / 1e3); |
| 614 | pr_info("%8" PRIu64 "us ", max / 1000); | 612 | pr_info("%9.2fus ", (double)max / 1e3); |
| 615 | pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3, | 613 | pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3, |
| 616 | kvm_event_rel_stddev(vcpu, event)); | 614 | kvm_event_rel_stddev(vcpu, event)); |
| 617 | pr_info("\n"); | 615 | pr_info("\n"); |
| @@ -732,7 +730,7 @@ static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx, | |||
| 732 | return -1; | 730 | return -1; |
| 733 | } | 731 | } |
| 734 | 732 | ||
| 735 | err = perf_session_queue_event(kvm->session, event, &sample, 0); | 733 | err = perf_session_queue_event(kvm->session, event, &kvm->tool, &sample, 0); |
| 736 | /* | 734 | /* |
| 737 | * FIXME: Here we can't consume the event, as perf_session_queue_event will | 735 | * FIXME: Here we can't consume the event, as perf_session_queue_event will |
| 738 | * point to it, and it'll get possibly overwritten by the kernel. | 736 | * point to it, and it'll get possibly overwritten by the kernel. |
| @@ -785,7 +783,7 @@ static int perf_kvm__mmap_read(struct perf_kvm_stat *kvm) | |||
| 785 | 783 | ||
| 786 | /* flush queue after each round in which we processed events */ | 784 | /* flush queue after each round in which we processed events */ |
| 787 | if (ntotal) { | 785 | if (ntotal) { |
| 788 | kvm->session->ordered_samples.next_flush = flush_time; | 786 | kvm->session->ordered_events.next_flush = flush_time; |
| 789 | err = kvm->tool.finished_round(&kvm->tool, NULL, kvm->session); | 787 | err = kvm->tool.finished_round(&kvm->tool, NULL, kvm->session); |
| 790 | if (err) { | 788 | if (err) { |
| 791 | if (kvm->lost_events) | 789 | if (kvm->lost_events) |
| @@ -885,15 +883,11 @@ static int fd_set_nonblock(int fd) | |||
| 885 | return 0; | 883 | return 0; |
| 886 | } | 884 | } |
| 887 | 885 | ||
| 888 | static | 886 | static int perf_kvm__handle_stdin(void) |
| 889 | int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save) | ||
| 890 | { | 887 | { |
| 891 | int c; | 888 | int c; |
| 892 | 889 | ||
| 893 | tcsetattr(0, TCSANOW, tc_now); | ||
| 894 | c = getc(stdin); | 890 | c = getc(stdin); |
| 895 | tcsetattr(0, TCSAFLUSH, tc_save); | ||
| 896 | |||
| 897 | if (c == 'q') | 891 | if (c == 'q') |
| 898 | return 1; | 892 | return 1; |
| 899 | 893 | ||
| @@ -904,7 +898,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
| 904 | { | 898 | { |
| 905 | struct pollfd *pollfds = NULL; | 899 | struct pollfd *pollfds = NULL; |
| 906 | int nr_fds, nr_stdin, ret, err = -EINVAL; | 900 | int nr_fds, nr_stdin, ret, err = -EINVAL; |
| 907 | struct termios tc, save; | 901 | struct termios save; |
| 908 | 902 | ||
| 909 | /* live flag must be set first */ | 903 | /* live flag must be set first */ |
| 910 | kvm->live = true; | 904 | kvm->live = true; |
| @@ -919,26 +913,14 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
| 919 | goto out; | 913 | goto out; |
| 920 | } | 914 | } |
| 921 | 915 | ||
| 916 | set_term_quiet_input(&save); | ||
| 922 | init_kvm_event_record(kvm); | 917 | init_kvm_event_record(kvm); |
| 923 | 918 | ||
| 924 | tcgetattr(0, &save); | ||
| 925 | tc = save; | ||
| 926 | tc.c_lflag &= ~(ICANON | ECHO); | ||
| 927 | tc.c_cc[VMIN] = 0; | ||
| 928 | tc.c_cc[VTIME] = 0; | ||
| 929 | |||
| 930 | signal(SIGINT, sig_handler); | 919 | signal(SIGINT, sig_handler); |
| 931 | signal(SIGTERM, sig_handler); | 920 | signal(SIGTERM, sig_handler); |
| 932 | 921 | ||
| 933 | /* copy pollfds -- need to add timerfd and stdin */ | 922 | /* use pollfds -- need to add timerfd and stdin */ |
| 934 | nr_fds = kvm->evlist->nr_fds; | 923 | nr_fds = kvm->evlist->pollfd.nr; |
| 935 | pollfds = zalloc(sizeof(struct pollfd) * (nr_fds + 2)); | ||
| 936 | if (!pollfds) { | ||
| 937 | err = -ENOMEM; | ||
| 938 | goto out; | ||
| 939 | } | ||
| 940 | memcpy(pollfds, kvm->evlist->pollfd, | ||
| 941 | sizeof(struct pollfd) * kvm->evlist->nr_fds); | ||
| 942 | 924 | ||
| 943 | /* add timer fd */ | 925 | /* add timer fd */ |
| 944 | if (perf_kvm__timerfd_create(kvm) < 0) { | 926 | if (perf_kvm__timerfd_create(kvm) < 0) { |
| @@ -946,17 +928,21 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
| 946 | goto out; | 928 | goto out; |
| 947 | } | 929 | } |
| 948 | 930 | ||
| 949 | pollfds[nr_fds].fd = kvm->timerfd; | 931 | if (perf_evlist__add_pollfd(kvm->evlist, kvm->timerfd)) |
| 950 | pollfds[nr_fds].events = POLLIN; | 932 | goto out; |
| 933 | |||
| 951 | nr_fds++; | 934 | nr_fds++; |
| 952 | 935 | ||
| 953 | pollfds[nr_fds].fd = fileno(stdin); | 936 | if (perf_evlist__add_pollfd(kvm->evlist, fileno(stdin))) |
| 954 | pollfds[nr_fds].events = POLLIN; | 937 | goto out; |
| 938 | |||
| 955 | nr_stdin = nr_fds; | 939 | nr_stdin = nr_fds; |
| 956 | nr_fds++; | 940 | nr_fds++; |
| 957 | if (fd_set_nonblock(fileno(stdin)) != 0) | 941 | if (fd_set_nonblock(fileno(stdin)) != 0) |
| 958 | goto out; | 942 | goto out; |
| 959 | 943 | ||
| 944 | pollfds = kvm->evlist->pollfd.entries; | ||
| 945 | |||
| 960 | /* everything is good - enable the events and process */ | 946 | /* everything is good - enable the events and process */ |
| 961 | perf_evlist__enable(kvm->evlist); | 947 | perf_evlist__enable(kvm->evlist); |
| 962 | 948 | ||
| @@ -972,7 +958,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
| 972 | goto out; | 958 | goto out; |
| 973 | 959 | ||
| 974 | if (pollfds[nr_stdin].revents & POLLIN) | 960 | if (pollfds[nr_stdin].revents & POLLIN) |
| 975 | done = perf_kvm__handle_stdin(&tc, &save); | 961 | done = perf_kvm__handle_stdin(); |
| 976 | 962 | ||
| 977 | if (!rc && !done) | 963 | if (!rc && !done) |
| 978 | err = poll(pollfds, nr_fds, 100); | 964 | err = poll(pollfds, nr_fds, 100); |
| @@ -989,7 +975,7 @@ out: | |||
| 989 | if (kvm->timerfd >= 0) | 975 | if (kvm->timerfd >= 0) |
| 990 | close(kvm->timerfd); | 976 | close(kvm->timerfd); |
| 991 | 977 | ||
| 992 | free(pollfds); | 978 | tcsetattr(0, TCSAFLUSH, &save); |
| 993 | return err; | 979 | return err; |
| 994 | } | 980 | } |
| 995 | 981 | ||
| @@ -998,6 +984,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm) | |||
| 998 | int err, rc = -1; | 984 | int err, rc = -1; |
| 999 | struct perf_evsel *pos; | 985 | struct perf_evsel *pos; |
| 1000 | struct perf_evlist *evlist = kvm->evlist; | 986 | struct perf_evlist *evlist = kvm->evlist; |
| 987 | char sbuf[STRERR_BUFSIZE]; | ||
| 1001 | 988 | ||
| 1002 | perf_evlist__config(evlist, &kvm->opts); | 989 | perf_evlist__config(evlist, &kvm->opts); |
| 1003 | 990 | ||
| @@ -1034,12 +1021,14 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm) | |||
| 1034 | 1021 | ||
| 1035 | err = perf_evlist__open(evlist); | 1022 | err = perf_evlist__open(evlist); |
| 1036 | if (err < 0) { | 1023 | if (err < 0) { |
| 1037 | printf("Couldn't create the events: %s\n", strerror(errno)); | 1024 | printf("Couldn't create the events: %s\n", |
| 1025 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 1038 | goto out; | 1026 | goto out; |
| 1039 | } | 1027 | } |
| 1040 | 1028 | ||
| 1041 | if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) { | 1029 | if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) { |
| 1042 | ui__error("Failed to mmap the events: %s\n", strerror(errno)); | 1030 | ui__error("Failed to mmap the events: %s\n", |
| 1031 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 1043 | perf_evlist__close(evlist); | 1032 | perf_evlist__close(evlist); |
| 1044 | goto out; | 1033 | goto out; |
| 1045 | } | 1034 | } |
| @@ -1058,7 +1047,7 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
| 1058 | struct perf_tool eops = { | 1047 | struct perf_tool eops = { |
| 1059 | .sample = process_sample_event, | 1048 | .sample = process_sample_event, |
| 1060 | .comm = perf_event__process_comm, | 1049 | .comm = perf_event__process_comm, |
| 1061 | .ordered_samples = true, | 1050 | .ordered_events = true, |
| 1062 | }; | 1051 | }; |
| 1063 | struct perf_data_file file = { | 1052 | struct perf_data_file file = { |
| 1064 | .path = kvm->file_name, | 1053 | .path = kvm->file_name, |
| @@ -1069,9 +1058,11 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
| 1069 | kvm->session = perf_session__new(&file, false, &kvm->tool); | 1058 | kvm->session = perf_session__new(&file, false, &kvm->tool); |
| 1070 | if (!kvm->session) { | 1059 | if (!kvm->session) { |
| 1071 | pr_err("Initializing perf session failed\n"); | 1060 | pr_err("Initializing perf session failed\n"); |
| 1072 | return -EINVAL; | 1061 | return -1; |
| 1073 | } | 1062 | } |
| 1074 | 1063 | ||
| 1064 | symbol__init(&kvm->session->header.env); | ||
| 1065 | |||
| 1075 | if (!perf_session__has_traces(kvm->session, "kvm record")) | 1066 | if (!perf_session__has_traces(kvm->session, "kvm record")) |
| 1076 | return -EINVAL; | 1067 | return -EINVAL; |
| 1077 | 1068 | ||
| @@ -1088,8 +1079,8 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
| 1088 | 1079 | ||
| 1089 | static int parse_target_str(struct perf_kvm_stat *kvm) | 1080 | static int parse_target_str(struct perf_kvm_stat *kvm) |
| 1090 | { | 1081 | { |
| 1091 | if (kvm->pid_str) { | 1082 | if (kvm->opts.target.pid) { |
| 1092 | kvm->pid_list = intlist__new(kvm->pid_str); | 1083 | kvm->pid_list = intlist__new(kvm->opts.target.pid); |
| 1093 | if (kvm->pid_list == NULL) { | 1084 | if (kvm->pid_list == NULL) { |
| 1094 | pr_err("Error parsing process id string\n"); | 1085 | pr_err("Error parsing process id string\n"); |
| 1095 | return -EINVAL; | 1086 | return -EINVAL; |
| @@ -1191,7 +1182,7 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
| 1191 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", | 1182 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", |
| 1192 | "key for sorting: sample(sort by samples number)" | 1183 | "key for sorting: sample(sort by samples number)" |
| 1193 | " time (sort by avg time)"), | 1184 | " time (sort by avg time)"), |
| 1194 | OPT_STRING('p', "pid", &kvm->pid_str, "pid", | 1185 | OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid", |
| 1195 | "analyze events only for given process id(s)"), | 1186 | "analyze events only for given process id(s)"), |
| 1196 | OPT_END() | 1187 | OPT_END() |
| 1197 | }; | 1188 | }; |
| @@ -1201,8 +1192,6 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
| 1201 | NULL | 1192 | NULL |
| 1202 | }; | 1193 | }; |
| 1203 | 1194 | ||
| 1204 | symbol__init(); | ||
| 1205 | |||
| 1206 | if (argc) { | 1195 | if (argc) { |
| 1207 | argc = parse_options(argc, argv, | 1196 | argc = parse_options(argc, argv, |
| 1208 | kvm_events_report_options, | 1197 | kvm_events_report_options, |
| @@ -1212,6 +1201,9 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
| 1212 | kvm_events_report_options); | 1201 | kvm_events_report_options); |
| 1213 | } | 1202 | } |
| 1214 | 1203 | ||
| 1204 | if (!kvm->opts.target.pid) | ||
| 1205 | kvm->opts.target.system_wide = true; | ||
| 1206 | |||
| 1215 | return kvm_events_report_vcpu(kvm); | 1207 | return kvm_events_report_vcpu(kvm); |
| 1216 | } | 1208 | } |
| 1217 | 1209 | ||
| @@ -1311,7 +1303,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1311 | kvm->tool.exit = perf_event__process_exit; | 1303 | kvm->tool.exit = perf_event__process_exit; |
| 1312 | kvm->tool.fork = perf_event__process_fork; | 1304 | kvm->tool.fork = perf_event__process_fork; |
| 1313 | kvm->tool.lost = process_lost_event; | 1305 | kvm->tool.lost = process_lost_event; |
| 1314 | kvm->tool.ordered_samples = true; | 1306 | kvm->tool.ordered_events = true; |
| 1315 | perf_tool__fill_defaults(&kvm->tool); | 1307 | perf_tool__fill_defaults(&kvm->tool); |
| 1316 | 1308 | ||
| 1317 | /* set defaults */ | 1309 | /* set defaults */ |
| @@ -1322,7 +1314,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1322 | kvm->opts.target.uid_str = NULL; | 1314 | kvm->opts.target.uid_str = NULL; |
| 1323 | kvm->opts.target.uid = UINT_MAX; | 1315 | kvm->opts.target.uid = UINT_MAX; |
| 1324 | 1316 | ||
| 1325 | symbol__init(); | 1317 | symbol__init(NULL); |
| 1326 | disable_buildid_cache(); | 1318 | disable_buildid_cache(); |
| 1327 | 1319 | ||
| 1328 | use_browser = 0; | 1320 | use_browser = 0; |
| @@ -1369,7 +1361,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1369 | */ | 1361 | */ |
| 1370 | kvm->session = perf_session__new(&file, false, &kvm->tool); | 1362 | kvm->session = perf_session__new(&file, false, &kvm->tool); |
| 1371 | if (kvm->session == NULL) { | 1363 | if (kvm->session == NULL) { |
| 1372 | err = -ENOMEM; | 1364 | err = -1; |
| 1373 | goto out; | 1365 | goto out; |
| 1374 | } | 1366 | } |
| 1375 | kvm->session->evlist = kvm->evlist; | 1367 | kvm->session->evlist = kvm->evlist; |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6148afc995c6..e7ec71589da6 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
| @@ -852,7 +852,7 @@ static int __cmd_report(bool display_info) | |||
| 852 | struct perf_tool eops = { | 852 | struct perf_tool eops = { |
| 853 | .sample = process_sample_event, | 853 | .sample = process_sample_event, |
| 854 | .comm = perf_event__process_comm, | 854 | .comm = perf_event__process_comm, |
| 855 | .ordered_samples = true, | 855 | .ordered_events = true, |
| 856 | }; | 856 | }; |
| 857 | struct perf_data_file file = { | 857 | struct perf_data_file file = { |
| 858 | .path = input_name, | 858 | .path = input_name, |
| @@ -862,9 +862,11 @@ static int __cmd_report(bool display_info) | |||
| 862 | session = perf_session__new(&file, false, &eops); | 862 | session = perf_session__new(&file, false, &eops); |
| 863 | if (!session) { | 863 | if (!session) { |
| 864 | pr_err("Initializing perf session failed\n"); | 864 | pr_err("Initializing perf session failed\n"); |
| 865 | return -ENOMEM; | 865 | return -1; |
| 866 | } | 866 | } |
| 867 | 867 | ||
| 868 | symbol__init(&session->header.env); | ||
| 869 | |||
| 868 | if (!perf_session__has_traces(session, "lock record")) | 870 | if (!perf_session__has_traces(session, "lock record")) |
| 869 | goto out_delete; | 871 | goto out_delete; |
| 870 | 872 | ||
| @@ -974,7 +976,6 @@ int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 974 | unsigned int i; | 976 | unsigned int i; |
| 975 | int rc = 0; | 977 | int rc = 0; |
| 976 | 978 | ||
| 977 | symbol__init(); | ||
| 978 | for (i = 0; i < LOCKHASH_SIZE; i++) | 979 | for (i = 0; i < LOCKHASH_SIZE; i++) |
| 979 | INIT_LIST_HEAD(lockhash_table + i); | 980 | INIT_LIST_HEAD(lockhash_table + i); |
| 980 | 981 | ||
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 4a1a6c94a5eb..24db6ffe2957 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
| @@ -124,7 +124,7 @@ static int report_raw_events(struct perf_mem *mem) | |||
| 124 | &mem->tool); | 124 | &mem->tool); |
| 125 | 125 | ||
| 126 | if (session == NULL) | 126 | if (session == NULL) |
| 127 | return -ENOMEM; | 127 | return -1; |
| 128 | 128 | ||
| 129 | if (mem->cpu_list) { | 129 | if (mem->cpu_list) { |
| 130 | ret = perf_session__cpu_bitmap(session, mem->cpu_list, | 130 | ret = perf_session__cpu_bitmap(session, mem->cpu_list, |
| @@ -133,7 +133,7 @@ static int report_raw_events(struct perf_mem *mem) | |||
| 133 | goto out_delete; | 133 | goto out_delete; |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | if (symbol__init() < 0) | 136 | if (symbol__init(&session->header.env) < 0) |
| 137 | return -1; | 137 | return -1; |
| 138 | 138 | ||
| 139 | printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n"); | 139 | printf("# PID, TID, IP, ADDR, LOCAL WEIGHT, DSRC, SYMBOL\n"); |
| @@ -194,7 +194,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 194 | .lost = perf_event__process_lost, | 194 | .lost = perf_event__process_lost, |
| 195 | .fork = perf_event__process_fork, | 195 | .fork = perf_event__process_fork, |
| 196 | .build_id = perf_event__process_build_id, | 196 | .build_id = perf_event__process_build_id, |
| 197 | .ordered_samples = true, | 197 | .ordered_events = true, |
| 198 | }, | 198 | }, |
| 199 | .input_name = "perf.data", | 199 | .input_name = "perf.data", |
| 200 | }; | 200 | }; |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index c63fa2925075..04412b4770a2 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
| @@ -290,8 +290,11 @@ static void cleanup_params(void) | |||
| 290 | 290 | ||
| 291 | static void pr_err_with_code(const char *msg, int err) | 291 | static void pr_err_with_code(const char *msg, int err) |
| 292 | { | 292 | { |
| 293 | char sbuf[STRERR_BUFSIZE]; | ||
| 294 | |||
| 293 | pr_err("%s", msg); | 295 | pr_err("%s", msg); |
| 294 | pr_debug(" Reason: %s (Code: %d)", strerror(-err), err); | 296 | pr_debug(" Reason: %s (Code: %d)", |
| 297 | strerror_r(-err, sbuf, sizeof(sbuf)), err); | ||
| 295 | pr_err("\n"); | 298 | pr_err("\n"); |
| 296 | } | 299 | } |
| 297 | 300 | ||
| @@ -373,6 +376,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 373 | "target executable name or path", opt_set_target), | 376 | "target executable name or path", opt_set_target), |
| 374 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, | 377 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, |
| 375 | "Disable symbol demangling"), | 378 | "Disable symbol demangling"), |
| 379 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
| 380 | "Enable kernel symbol demangling"), | ||
| 376 | OPT_END() | 381 | OPT_END() |
| 377 | }; | 382 | }; |
| 378 | int ret; | 383 | int ret; |
| @@ -467,7 +472,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 467 | usage_with_options(probe_usage, options); | 472 | usage_with_options(probe_usage, options); |
| 468 | } | 473 | } |
| 469 | 474 | ||
| 470 | ret = show_line_range(¶ms.line_range, params.target); | 475 | ret = show_line_range(¶ms.line_range, params.target, |
| 476 | params.uprobes); | ||
| 471 | if (ret < 0) | 477 | if (ret < 0) |
| 472 | pr_err_with_code(" Error: Failed to show lines.", ret); | 478 | pr_err_with_code(" Error: Failed to show lines.", ret); |
| 473 | return ret; | 479 | return ret; |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4869050e7194..44c6f3d55ce7 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -65,8 +65,9 @@ static int process_synthesized_event(struct perf_tool *tool, | |||
| 65 | return record__write(rec, event, event->header.size); | 65 | return record__write(rec, event, event->header.size); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | static int record__mmap_read(struct record *rec, struct perf_mmap *md) | 68 | static int record__mmap_read(struct record *rec, int idx) |
| 69 | { | 69 | { |
| 70 | struct perf_mmap *md = &rec->evlist->mmap[idx]; | ||
| 70 | unsigned int head = perf_mmap__read_head(md); | 71 | unsigned int head = perf_mmap__read_head(md); |
| 71 | unsigned int old = md->prev; | 72 | unsigned int old = md->prev; |
| 72 | unsigned char *data = md->base + page_size; | 73 | unsigned char *data = md->base + page_size; |
| @@ -102,8 +103,7 @@ static int record__mmap_read(struct record *rec, struct perf_mmap *md) | |||
| 102 | } | 103 | } |
| 103 | 104 | ||
| 104 | md->prev = old; | 105 | md->prev = old; |
| 105 | perf_mmap__write_tail(md, old); | 106 | perf_evlist__mmap_consume(rec->evlist, idx); |
| 106 | |||
| 107 | out: | 107 | out: |
| 108 | return rc; | 108 | return rc; |
| 109 | } | 109 | } |
| @@ -161,7 +161,7 @@ try_again: | |||
| 161 | 161 | ||
| 162 | if (perf_evlist__apply_filters(evlist)) { | 162 | if (perf_evlist__apply_filters(evlist)) { |
| 163 | error("failed to set filter with %d (%s)\n", errno, | 163 | error("failed to set filter with %d (%s)\n", errno, |
| 164 | strerror(errno)); | 164 | strerror_r(errno, msg, sizeof(msg))); |
| 165 | rc = -1; | 165 | rc = -1; |
| 166 | goto out; | 166 | goto out; |
| 167 | } | 167 | } |
| @@ -175,7 +175,8 @@ try_again: | |||
| 175 | "(current value: %u)\n", opts->mmap_pages); | 175 | "(current value: %u)\n", opts->mmap_pages); |
| 176 | rc = -errno; | 176 | rc = -errno; |
| 177 | } else { | 177 | } else { |
| 178 | pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 178 | pr_err("failed to mmap with %d (%s)\n", errno, |
| 179 | strerror_r(errno, msg, sizeof(msg))); | ||
| 179 | rc = -errno; | 180 | rc = -errno; |
| 180 | } | 181 | } |
| 181 | goto out; | 182 | goto out; |
| @@ -244,7 +245,7 @@ static int record__mmap_read_all(struct record *rec) | |||
| 244 | 245 | ||
| 245 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { | 246 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { |
| 246 | if (rec->evlist->mmap[i].base) { | 247 | if (rec->evlist->mmap[i].base) { |
| 247 | if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) { | 248 | if (record__mmap_read(rec, i) != 0) { |
| 248 | rc = -1; | 249 | rc = -1; |
| 249 | goto out; | 250 | goto out; |
| 250 | } | 251 | } |
| @@ -307,7 +308,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 307 | struct record_opts *opts = &rec->opts; | 308 | struct record_opts *opts = &rec->opts; |
| 308 | struct perf_data_file *file = &rec->file; | 309 | struct perf_data_file *file = &rec->file; |
| 309 | struct perf_session *session; | 310 | struct perf_session *session; |
| 310 | bool disabled = false; | 311 | bool disabled = false, draining = false; |
| 311 | 312 | ||
| 312 | rec->progname = argv[0]; | 313 | rec->progname = argv[0]; |
| 313 | 314 | ||
| @@ -456,9 +457,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 456 | } | 457 | } |
| 457 | 458 | ||
| 458 | if (hits == rec->samples) { | 459 | if (hits == rec->samples) { |
| 459 | if (done) | 460 | if (done || draining) |
| 460 | break; | 461 | break; |
| 461 | err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); | 462 | err = perf_evlist__poll(rec->evlist, -1); |
| 462 | /* | 463 | /* |
| 463 | * Propagate error, only if there's any. Ignore positive | 464 | * Propagate error, only if there's any. Ignore positive |
| 464 | * number of returned events and interrupt error. | 465 | * number of returned events and interrupt error. |
| @@ -466,6 +467,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 466 | if (err > 0 || (err < 0 && errno == EINTR)) | 467 | if (err > 0 || (err < 0 && errno == EINTR)) |
| 467 | err = 0; | 468 | err = 0; |
| 468 | waking++; | 469 | waking++; |
| 470 | |||
| 471 | if (perf_evlist__filter_pollfd(rec->evlist, POLLERR | POLLHUP) == 0) | ||
| 472 | draining = true; | ||
| 469 | } | 473 | } |
| 470 | 474 | ||
| 471 | /* | 475 | /* |
| @@ -480,7 +484,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 480 | } | 484 | } |
| 481 | 485 | ||
| 482 | if (forks && workload_exec_errno) { | 486 | if (forks && workload_exec_errno) { |
| 483 | char msg[512]; | 487 | char msg[STRERR_BUFSIZE]; |
| 484 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); | 488 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); |
| 485 | pr_err("Workload failed: %s\n", emsg); | 489 | pr_err("Workload failed: %s\n", emsg); |
| 486 | err = -1; | 490 | err = -1; |
| @@ -620,145 +624,56 @@ error: | |||
| 620 | return ret; | 624 | return ret; |
| 621 | } | 625 | } |
| 622 | 626 | ||
| 623 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 627 | static void callchain_debug(void) |
| 624 | static int get_stack_size(char *str, unsigned long *_size) | ||
| 625 | { | ||
| 626 | char *endptr; | ||
| 627 | unsigned long size; | ||
| 628 | unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); | ||
| 629 | |||
| 630 | size = strtoul(str, &endptr, 0); | ||
| 631 | |||
| 632 | do { | ||
| 633 | if (*endptr) | ||
| 634 | break; | ||
| 635 | |||
| 636 | size = round_up(size, sizeof(u64)); | ||
| 637 | if (!size || size > max_size) | ||
| 638 | break; | ||
| 639 | |||
| 640 | *_size = size; | ||
| 641 | return 0; | ||
| 642 | |||
| 643 | } while (0); | ||
| 644 | |||
| 645 | pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", | ||
| 646 | max_size, str); | ||
| 647 | return -1; | ||
| 648 | } | ||
| 649 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
| 650 | |||
| 651 | int record_parse_callchain(const char *arg, struct record_opts *opts) | ||
| 652 | { | ||
| 653 | char *tok, *name, *saveptr = NULL; | ||
| 654 | char *buf; | ||
| 655 | int ret = -1; | ||
| 656 | |||
| 657 | /* We need buffer that we know we can write to. */ | ||
| 658 | buf = malloc(strlen(arg) + 1); | ||
| 659 | if (!buf) | ||
| 660 | return -ENOMEM; | ||
| 661 | |||
| 662 | strcpy(buf, arg); | ||
| 663 | |||
| 664 | tok = strtok_r((char *)buf, ",", &saveptr); | ||
| 665 | name = tok ? : (char *)buf; | ||
| 666 | |||
| 667 | do { | ||
| 668 | /* Framepointer style */ | ||
| 669 | if (!strncmp(name, "fp", sizeof("fp"))) { | ||
| 670 | if (!strtok_r(NULL, ",", &saveptr)) { | ||
| 671 | opts->call_graph = CALLCHAIN_FP; | ||
| 672 | ret = 0; | ||
| 673 | } else | ||
| 674 | pr_err("callchain: No more arguments " | ||
| 675 | "needed for -g fp\n"); | ||
| 676 | break; | ||
| 677 | |||
| 678 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
| 679 | /* Dwarf style */ | ||
| 680 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { | ||
| 681 | const unsigned long default_stack_dump_size = 8192; | ||
| 682 | |||
| 683 | ret = 0; | ||
| 684 | opts->call_graph = CALLCHAIN_DWARF; | ||
| 685 | opts->stack_dump_size = default_stack_dump_size; | ||
| 686 | |||
| 687 | tok = strtok_r(NULL, ",", &saveptr); | ||
| 688 | if (tok) { | ||
| 689 | unsigned long size = 0; | ||
| 690 | |||
| 691 | ret = get_stack_size(tok, &size); | ||
| 692 | opts->stack_dump_size = size; | ||
| 693 | } | ||
| 694 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
| 695 | } else { | ||
| 696 | pr_err("callchain: Unknown --call-graph option " | ||
| 697 | "value: %s\n", arg); | ||
| 698 | break; | ||
| 699 | } | ||
| 700 | |||
| 701 | } while (0); | ||
| 702 | |||
| 703 | free(buf); | ||
| 704 | return ret; | ||
| 705 | } | ||
| 706 | |||
| 707 | static void callchain_debug(struct record_opts *opts) | ||
| 708 | { | 628 | { |
| 709 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; | 629 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; |
| 710 | 630 | ||
| 711 | pr_debug("callchain: type %s\n", str[opts->call_graph]); | 631 | pr_debug("callchain: type %s\n", str[callchain_param.record_mode]); |
| 712 | 632 | ||
| 713 | if (opts->call_graph == CALLCHAIN_DWARF) | 633 | if (callchain_param.record_mode == CALLCHAIN_DWARF) |
| 714 | pr_debug("callchain: stack dump size %d\n", | 634 | pr_debug("callchain: stack dump size %d\n", |
| 715 | opts->stack_dump_size); | 635 | callchain_param.dump_size); |
| 716 | } | 636 | } |
| 717 | 637 | ||
| 718 | int record_parse_callchain_opt(const struct option *opt, | 638 | int record_parse_callchain_opt(const struct option *opt __maybe_unused, |
| 719 | const char *arg, | 639 | const char *arg, |
| 720 | int unset) | 640 | int unset) |
| 721 | { | 641 | { |
| 722 | struct record_opts *opts = opt->value; | ||
| 723 | int ret; | 642 | int ret; |
| 724 | 643 | ||
| 725 | opts->call_graph_enabled = !unset; | 644 | callchain_param.enabled = !unset; |
| 726 | 645 | ||
| 727 | /* --no-call-graph */ | 646 | /* --no-call-graph */ |
| 728 | if (unset) { | 647 | if (unset) { |
| 729 | opts->call_graph = CALLCHAIN_NONE; | 648 | callchain_param.record_mode = CALLCHAIN_NONE; |
| 730 | pr_debug("callchain: disabled\n"); | 649 | pr_debug("callchain: disabled\n"); |
| 731 | return 0; | 650 | return 0; |
| 732 | } | 651 | } |
| 733 | 652 | ||
| 734 | ret = record_parse_callchain(arg, opts); | 653 | ret = parse_callchain_record_opt(arg); |
| 735 | if (!ret) | 654 | if (!ret) |
| 736 | callchain_debug(opts); | 655 | callchain_debug(); |
| 737 | 656 | ||
| 738 | return ret; | 657 | return ret; |
| 739 | } | 658 | } |
| 740 | 659 | ||
| 741 | int record_callchain_opt(const struct option *opt, | 660 | int record_callchain_opt(const struct option *opt __maybe_unused, |
| 742 | const char *arg __maybe_unused, | 661 | const char *arg __maybe_unused, |
| 743 | int unset __maybe_unused) | 662 | int unset __maybe_unused) |
| 744 | { | 663 | { |
| 745 | struct record_opts *opts = opt->value; | 664 | callchain_param.enabled = true; |
| 746 | 665 | ||
| 747 | opts->call_graph_enabled = !unset; | 666 | if (callchain_param.record_mode == CALLCHAIN_NONE) |
| 667 | callchain_param.record_mode = CALLCHAIN_FP; | ||
| 748 | 668 | ||
| 749 | if (opts->call_graph == CALLCHAIN_NONE) | 669 | callchain_debug(); |
| 750 | opts->call_graph = CALLCHAIN_FP; | ||
| 751 | |||
| 752 | callchain_debug(opts); | ||
| 753 | return 0; | 670 | return 0; |
| 754 | } | 671 | } |
| 755 | 672 | ||
| 756 | static int perf_record_config(const char *var, const char *value, void *cb) | 673 | static int perf_record_config(const char *var, const char *value, void *cb) |
| 757 | { | 674 | { |
| 758 | struct record *rec = cb; | ||
| 759 | |||
| 760 | if (!strcmp(var, "record.call-graph")) | 675 | if (!strcmp(var, "record.call-graph")) |
| 761 | return record_parse_callchain(value, &rec->opts); | 676 | var = "call-graph.record-mode"; /* fall-through */ |
| 762 | 677 | ||
| 763 | return perf_default_config(var, value, cb); | 678 | return perf_default_config(var, value, cb); |
| 764 | } | 679 | } |
| @@ -781,6 +696,7 @@ static const char * const record_usage[] = { | |||
| 781 | */ | 696 | */ |
| 782 | static struct record record = { | 697 | static struct record record = { |
| 783 | .opts = { | 698 | .opts = { |
| 699 | .sample_time = true, | ||
| 784 | .mmap_pages = UINT_MAX, | 700 | .mmap_pages = UINT_MAX, |
| 785 | .user_freq = UINT_MAX, | 701 | .user_freq = UINT_MAX, |
| 786 | .user_interval = ULLONG_MAX, | 702 | .user_interval = ULLONG_MAX, |
| @@ -907,7 +823,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 907 | usage_with_options(record_usage, record_options); | 823 | usage_with_options(record_usage, record_options); |
| 908 | } | 824 | } |
| 909 | 825 | ||
| 910 | symbol__init(); | 826 | symbol__init(NULL); |
| 911 | 827 | ||
| 912 | if (symbol_conf.kptr_restrict) | 828 | if (symbol_conf.kptr_restrict) |
| 913 | pr_warning( | 829 | pr_warning( |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 21d830bafff3..ac145fae0521 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -58,17 +58,19 @@ struct report { | |||
| 58 | const char *symbol_filter_str; | 58 | const char *symbol_filter_str; |
| 59 | float min_percent; | 59 | float min_percent; |
| 60 | u64 nr_entries; | 60 | u64 nr_entries; |
| 61 | u64 queue_size; | ||
| 61 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 62 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
| 62 | }; | 63 | }; |
| 63 | 64 | ||
| 64 | static int report__config(const char *var, const char *value, void *cb) | 65 | static int report__config(const char *var, const char *value, void *cb) |
| 65 | { | 66 | { |
| 67 | struct report *rep = cb; | ||
| 68 | |||
| 66 | if (!strcmp(var, "report.group")) { | 69 | if (!strcmp(var, "report.group")) { |
| 67 | symbol_conf.event_group = perf_config_bool(var, value); | 70 | symbol_conf.event_group = perf_config_bool(var, value); |
| 68 | return 0; | 71 | return 0; |
| 69 | } | 72 | } |
| 70 | if (!strcmp(var, "report.percent-limit")) { | 73 | if (!strcmp(var, "report.percent-limit")) { |
| 71 | struct report *rep = cb; | ||
| 72 | rep->min_percent = strtof(value, NULL); | 74 | rep->min_percent = strtof(value, NULL); |
| 73 | return 0; | 75 | return 0; |
| 74 | } | 76 | } |
| @@ -76,6 +78,10 @@ static int report__config(const char *var, const char *value, void *cb) | |||
| 76 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); | 78 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); |
| 77 | return 0; | 79 | return 0; |
| 78 | } | 80 | } |
| 81 | if (!strcmp(var, "report.queue-size")) { | ||
| 82 | rep->queue_size = perf_config_u64(var, value); | ||
| 83 | return 0; | ||
| 84 | } | ||
| 79 | 85 | ||
| 80 | return perf_default_config(var, value, cb); | 86 | return perf_default_config(var, value, cb); |
| 81 | } | 87 | } |
| @@ -578,7 +584,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 578 | .attr = perf_event__process_attr, | 584 | .attr = perf_event__process_attr, |
| 579 | .tracing_data = perf_event__process_tracing_data, | 585 | .tracing_data = perf_event__process_tracing_data, |
| 580 | .build_id = perf_event__process_build_id, | 586 | .build_id = perf_event__process_build_id, |
| 581 | .ordered_samples = true, | 587 | .ordered_events = true, |
| 582 | .ordering_requires_timestamps = true, | 588 | .ordering_requires_timestamps = true, |
| 583 | }, | 589 | }, |
| 584 | .max_stack = PERF_MAX_STACK_DEPTH, | 590 | .max_stack = PERF_MAX_STACK_DEPTH, |
| @@ -674,6 +680,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 674 | "objdump binary to use for disassembly and annotations"), | 680 | "objdump binary to use for disassembly and annotations"), |
| 675 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, | 681 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, |
| 676 | "Disable symbol demangling"), | 682 | "Disable symbol demangling"), |
| 683 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
| 684 | "Enable kernel symbol demangling"), | ||
| 677 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), | 685 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), |
| 678 | OPT_CALLBACK(0, "percent-limit", &report, "percent", | 686 | OPT_CALLBACK(0, "percent-limit", &report, "percent", |
| 679 | "Don't show entries under that percent", parse_percent_limit), | 687 | "Don't show entries under that percent", parse_percent_limit), |
| @@ -712,14 +720,19 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 712 | repeat: | 720 | repeat: |
| 713 | session = perf_session__new(&file, false, &report.tool); | 721 | session = perf_session__new(&file, false, &report.tool); |
| 714 | if (session == NULL) | 722 | if (session == NULL) |
| 715 | return -ENOMEM; | 723 | return -1; |
| 724 | |||
| 725 | if (report.queue_size) { | ||
| 726 | ordered_events__set_alloc_size(&session->ordered_events, | ||
| 727 | report.queue_size); | ||
| 728 | } | ||
| 716 | 729 | ||
| 717 | report.session = session; | 730 | report.session = session; |
| 718 | 731 | ||
| 719 | has_br_stack = perf_header__has_feat(&session->header, | 732 | has_br_stack = perf_header__has_feat(&session->header, |
| 720 | HEADER_BRANCH_STACK); | 733 | HEADER_BRANCH_STACK); |
| 721 | 734 | ||
| 722 | if (branch_mode == -1 && has_br_stack) { | 735 | if ((branch_mode == -1 && has_br_stack) || branch_mode == 1) { |
| 723 | sort__mode = SORT_MODE__BRANCH; | 736 | sort__mode = SORT_MODE__BRANCH; |
| 724 | symbol_conf.cumulate_callchain = false; | 737 | symbol_conf.cumulate_callchain = false; |
| 725 | } | 738 | } |
| @@ -787,7 +800,7 @@ repeat: | |||
| 787 | } | 800 | } |
| 788 | } | 801 | } |
| 789 | 802 | ||
| 790 | if (symbol__init() < 0) | 803 | if (symbol__init(&session->header.env) < 0) |
| 791 | goto error; | 804 | goto error; |
| 792 | 805 | ||
| 793 | if (argc) { | 806 | if (argc) { |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index f83c08c0dd87..9c9287fbf8e9 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
| @@ -428,6 +428,7 @@ static u64 get_cpu_usage_nsec_parent(void) | |||
| 428 | static int self_open_counters(void) | 428 | static int self_open_counters(void) |
| 429 | { | 429 | { |
| 430 | struct perf_event_attr attr; | 430 | struct perf_event_attr attr; |
| 431 | char sbuf[STRERR_BUFSIZE]; | ||
| 431 | int fd; | 432 | int fd; |
| 432 | 433 | ||
| 433 | memset(&attr, 0, sizeof(attr)); | 434 | memset(&attr, 0, sizeof(attr)); |
| @@ -440,7 +441,8 @@ static int self_open_counters(void) | |||
| 440 | 441 | ||
| 441 | if (fd < 0) | 442 | if (fd < 0) |
| 442 | pr_err("Error: sys_perf_event_open() syscall returned " | 443 | pr_err("Error: sys_perf_event_open() syscall returned " |
| 443 | "with %d (%s)\n", fd, strerror(errno)); | 444 | "with %d (%s)\n", fd, |
| 445 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 444 | return fd; | 446 | return fd; |
| 445 | } | 447 | } |
| 446 | 448 | ||
| @@ -1462,6 +1464,8 @@ static int perf_sched__read_events(struct perf_sched *sched, | |||
| 1462 | return -1; | 1464 | return -1; |
| 1463 | } | 1465 | } |
| 1464 | 1466 | ||
| 1467 | symbol__init(&session->header.env); | ||
| 1468 | |||
| 1465 | if (perf_session__set_tracepoints_handlers(session, handlers)) | 1469 | if (perf_session__set_tracepoints_handlers(session, handlers)) |
| 1466 | goto out_delete; | 1470 | goto out_delete; |
| 1467 | 1471 | ||
| @@ -1662,7 +1666,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1662 | .comm = perf_event__process_comm, | 1666 | .comm = perf_event__process_comm, |
| 1663 | .lost = perf_event__process_lost, | 1667 | .lost = perf_event__process_lost, |
| 1664 | .fork = perf_sched__process_fork_event, | 1668 | .fork = perf_sched__process_fork_event, |
| 1665 | .ordered_samples = true, | 1669 | .ordered_events = true, |
| 1666 | }, | 1670 | }, |
| 1667 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), | 1671 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), |
| 1668 | .sort_list = LIST_HEAD_INIT(sched.sort_list), | 1672 | .sort_list = LIST_HEAD_INIT(sched.sort_list), |
| @@ -1747,7 +1751,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1747 | if (!strcmp(argv[0], "script")) | 1751 | if (!strcmp(argv[0], "script")) |
| 1748 | return cmd_script(argc, argv, prefix); | 1752 | return cmd_script(argc, argv, prefix); |
| 1749 | 1753 | ||
| 1750 | symbol__init(); | ||
| 1751 | if (!strncmp(argv[0], "rec", 3)) { | 1754 | if (!strncmp(argv[0], "rec", 3)) { |
| 1752 | return __cmd_record(argc, argv); | 1755 | return __cmd_record(argc, argv); |
| 1753 | } else if (!strncmp(argv[0], "lat", 3)) { | 1756 | } else if (!strncmp(argv[0], "lat", 3)) { |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index f57035b89c15..b9b9e58a6c39 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -184,10 +184,6 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
| 184 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", | 184 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", |
| 185 | PERF_OUTPUT_IP)) | 185 | PERF_OUTPUT_IP)) |
| 186 | return -EINVAL; | 186 | return -EINVAL; |
| 187 | |||
| 188 | if (!no_callchain && | ||
| 189 | !(attr->sample_type & PERF_SAMPLE_CALLCHAIN)) | ||
| 190 | symbol_conf.use_callchain = false; | ||
| 191 | } | 187 | } |
| 192 | 188 | ||
| 193 | if (PRINT_FIELD(ADDR) && | 189 | if (PRINT_FIELD(ADDR) && |
| @@ -290,6 +286,19 @@ static int perf_session__check_output_opt(struct perf_session *session) | |||
| 290 | set_print_ip_opts(&evsel->attr); | 286 | set_print_ip_opts(&evsel->attr); |
| 291 | } | 287 | } |
| 292 | 288 | ||
| 289 | if (!no_callchain) { | ||
| 290 | bool use_callchain = false; | ||
| 291 | |||
| 292 | evlist__for_each(session->evlist, evsel) { | ||
| 293 | if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) { | ||
| 294 | use_callchain = true; | ||
| 295 | break; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | if (!use_callchain) | ||
| 299 | symbol_conf.use_callchain = false; | ||
| 300 | } | ||
| 301 | |||
| 293 | /* | 302 | /* |
| 294 | * set default for tracepoints to print symbols only | 303 | * set default for tracepoints to print symbols only |
| 295 | * if callchains are present | 304 | * if callchains are present |
| @@ -476,6 +485,11 @@ static int default_start_script(const char *script __maybe_unused, | |||
| 476 | return 0; | 485 | return 0; |
| 477 | } | 486 | } |
| 478 | 487 | ||
| 488 | static int default_flush_script(void) | ||
| 489 | { | ||
| 490 | return 0; | ||
| 491 | } | ||
| 492 | |||
| 479 | static int default_stop_script(void) | 493 | static int default_stop_script(void) |
| 480 | { | 494 | { |
| 481 | return 0; | 495 | return 0; |
| @@ -489,6 +503,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused, | |||
| 489 | 503 | ||
| 490 | static struct scripting_ops default_scripting_ops = { | 504 | static struct scripting_ops default_scripting_ops = { |
| 491 | .start_script = default_start_script, | 505 | .start_script = default_start_script, |
| 506 | .flush_script = default_flush_script, | ||
| 492 | .stop_script = default_stop_script, | 507 | .stop_script = default_stop_script, |
| 493 | .process_event = process_event, | 508 | .process_event = process_event, |
| 494 | .generate_script = default_generate_script, | 509 | .generate_script = default_generate_script, |
| @@ -504,6 +519,11 @@ static void setup_scripting(void) | |||
| 504 | scripting_ops = &default_scripting_ops; | 519 | scripting_ops = &default_scripting_ops; |
| 505 | } | 520 | } |
| 506 | 521 | ||
| 522 | static int flush_scripting(void) | ||
| 523 | { | ||
| 524 | return scripting_ops->flush_script(); | ||
| 525 | } | ||
| 526 | |||
| 507 | static int cleanup_scripting(void) | 527 | static int cleanup_scripting(void) |
| 508 | { | 528 | { |
| 509 | pr_debug("\nperf script stopped\n"); | 529 | pr_debug("\nperf script stopped\n"); |
| @@ -1471,12 +1491,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1471 | bool show_full_info = false; | 1491 | bool show_full_info = false; |
| 1472 | bool header = false; | 1492 | bool header = false; |
| 1473 | bool header_only = false; | 1493 | bool header_only = false; |
| 1494 | bool script_started = false; | ||
| 1474 | char *rec_script_path = NULL; | 1495 | char *rec_script_path = NULL; |
| 1475 | char *rep_script_path = NULL; | 1496 | char *rep_script_path = NULL; |
| 1476 | struct perf_session *session; | 1497 | struct perf_session *session; |
| 1477 | char *script_path = NULL; | 1498 | char *script_path = NULL; |
| 1478 | const char **__argv; | 1499 | const char **__argv; |
| 1479 | int i, j, err; | 1500 | int i, j, err = 0; |
| 1480 | struct perf_script script = { | 1501 | struct perf_script script = { |
| 1481 | .tool = { | 1502 | .tool = { |
| 1482 | .sample = process_sample_event, | 1503 | .sample = process_sample_event, |
| @@ -1488,7 +1509,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1488 | .attr = process_attr, | 1509 | .attr = process_attr, |
| 1489 | .tracing_data = perf_event__process_tracing_data, | 1510 | .tracing_data = perf_event__process_tracing_data, |
| 1490 | .build_id = perf_event__process_build_id, | 1511 | .build_id = perf_event__process_build_id, |
| 1491 | .ordered_samples = true, | 1512 | .ordered_events = true, |
| 1492 | .ordering_requires_timestamps = true, | 1513 | .ordering_requires_timestamps = true, |
| 1493 | }, | 1514 | }, |
| 1494 | }; | 1515 | }; |
| @@ -1718,26 +1739,28 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1718 | exit(-1); | 1739 | exit(-1); |
| 1719 | } | 1740 | } |
| 1720 | 1741 | ||
| 1721 | if (symbol__init() < 0) | ||
| 1722 | return -1; | ||
| 1723 | if (!script_name) | 1742 | if (!script_name) |
| 1724 | setup_pager(); | 1743 | setup_pager(); |
| 1725 | 1744 | ||
| 1726 | session = perf_session__new(&file, false, &script.tool); | 1745 | session = perf_session__new(&file, false, &script.tool); |
| 1727 | if (session == NULL) | 1746 | if (session == NULL) |
| 1728 | return -ENOMEM; | 1747 | return -1; |
| 1729 | 1748 | ||
| 1730 | if (header || header_only) { | 1749 | if (header || header_only) { |
| 1731 | perf_session__fprintf_info(session, stdout, show_full_info); | 1750 | perf_session__fprintf_info(session, stdout, show_full_info); |
| 1732 | if (header_only) | 1751 | if (header_only) |
| 1733 | return 0; | 1752 | goto out_delete; |
| 1734 | } | 1753 | } |
| 1735 | 1754 | ||
| 1755 | if (symbol__init(&session->header.env) < 0) | ||
| 1756 | goto out_delete; | ||
| 1757 | |||
| 1736 | script.session = session; | 1758 | script.session = session; |
| 1737 | 1759 | ||
| 1738 | if (cpu_list) { | 1760 | if (cpu_list) { |
| 1739 | if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap)) | 1761 | err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); |
| 1740 | return -1; | 1762 | if (err < 0) |
| 1763 | goto out_delete; | ||
| 1741 | } | 1764 | } |
| 1742 | 1765 | ||
| 1743 | if (!no_callchain) | 1766 | if (!no_callchain) |
| @@ -1752,53 +1775,62 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1752 | if (output_set_by_user()) { | 1775 | if (output_set_by_user()) { |
| 1753 | fprintf(stderr, | 1776 | fprintf(stderr, |
| 1754 | "custom fields not supported for generated scripts"); | 1777 | "custom fields not supported for generated scripts"); |
| 1755 | return -1; | 1778 | err = -EINVAL; |
| 1779 | goto out_delete; | ||
| 1756 | } | 1780 | } |
| 1757 | 1781 | ||
| 1758 | input = open(file.path, O_RDONLY); /* input_name */ | 1782 | input = open(file.path, O_RDONLY); /* input_name */ |
| 1759 | if (input < 0) { | 1783 | if (input < 0) { |
| 1784 | err = -errno; | ||
| 1760 | perror("failed to open file"); | 1785 | perror("failed to open file"); |
| 1761 | return -1; | 1786 | goto out_delete; |
| 1762 | } | 1787 | } |
| 1763 | 1788 | ||
| 1764 | err = fstat(input, &perf_stat); | 1789 | err = fstat(input, &perf_stat); |
| 1765 | if (err < 0) { | 1790 | if (err < 0) { |
| 1766 | perror("failed to stat file"); | 1791 | perror("failed to stat file"); |
| 1767 | return -1; | 1792 | goto out_delete; |
| 1768 | } | 1793 | } |
| 1769 | 1794 | ||
| 1770 | if (!perf_stat.st_size) { | 1795 | if (!perf_stat.st_size) { |
| 1771 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 1796 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
| 1772 | return 0; | 1797 | goto out_delete; |
| 1773 | } | 1798 | } |
| 1774 | 1799 | ||
| 1775 | scripting_ops = script_spec__lookup(generate_script_lang); | 1800 | scripting_ops = script_spec__lookup(generate_script_lang); |
| 1776 | if (!scripting_ops) { | 1801 | if (!scripting_ops) { |
| 1777 | fprintf(stderr, "invalid language specifier"); | 1802 | fprintf(stderr, "invalid language specifier"); |
| 1778 | return -1; | 1803 | err = -ENOENT; |
| 1804 | goto out_delete; | ||
| 1779 | } | 1805 | } |
| 1780 | 1806 | ||
| 1781 | err = scripting_ops->generate_script(session->tevent.pevent, | 1807 | err = scripting_ops->generate_script(session->tevent.pevent, |
| 1782 | "perf-script"); | 1808 | "perf-script"); |
| 1783 | goto out; | 1809 | goto out_delete; |
| 1784 | } | 1810 | } |
| 1785 | 1811 | ||
| 1786 | if (script_name) { | 1812 | if (script_name) { |
| 1787 | err = scripting_ops->start_script(script_name, argc, argv); | 1813 | err = scripting_ops->start_script(script_name, argc, argv); |
| 1788 | if (err) | 1814 | if (err) |
| 1789 | goto out; | 1815 | goto out_delete; |
| 1790 | pr_debug("perf script started with script %s\n\n", script_name); | 1816 | pr_debug("perf script started with script %s\n\n", script_name); |
| 1817 | script_started = true; | ||
| 1791 | } | 1818 | } |
| 1792 | 1819 | ||
| 1793 | 1820 | ||
| 1794 | err = perf_session__check_output_opt(session); | 1821 | err = perf_session__check_output_opt(session); |
| 1795 | if (err < 0) | 1822 | if (err < 0) |
| 1796 | goto out; | 1823 | goto out_delete; |
| 1797 | 1824 | ||
| 1798 | err = __cmd_script(&script); | 1825 | err = __cmd_script(&script); |
| 1799 | 1826 | ||
| 1827 | flush_scripting(); | ||
| 1828 | |||
| 1829 | out_delete: | ||
| 1800 | perf_session__delete(session); | 1830 | perf_session__delete(session); |
| 1801 | cleanup_scripting(); | 1831 | |
| 1832 | if (script_started) | ||
| 1833 | cleanup_scripting(); | ||
| 1802 | out: | 1834 | out: |
| 1803 | return err; | 1835 | return err; |
| 1804 | } | 1836 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 3e80aa10cfd8..b22c62f80078 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -593,7 +593,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
| 593 | 593 | ||
| 594 | if (perf_evlist__apply_filters(evsel_list)) { | 594 | if (perf_evlist__apply_filters(evsel_list)) { |
| 595 | error("failed to set filter with %d (%s)\n", errno, | 595 | error("failed to set filter with %d (%s)\n", errno, |
| 596 | strerror(errno)); | 596 | strerror_r(errno, msg, sizeof(msg))); |
| 597 | return -1; | 597 | return -1; |
| 598 | } | 598 | } |
| 599 | 599 | ||
| @@ -732,7 +732,7 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr) | |||
| 732 | } | 732 | } |
| 733 | } | 733 | } |
| 734 | 734 | ||
| 735 | static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | 735 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
| 736 | { | 736 | { |
| 737 | double msecs = avg / 1e6; | 737 | double msecs = avg / 1e6; |
| 738 | const char *fmt_v, *fmt_n; | 738 | const char *fmt_v, *fmt_n; |
| @@ -741,7 +741,7 @@ static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | |||
| 741 | fmt_v = csv_output ? "%.6f%s" : "%18.6f%s"; | 741 | fmt_v = csv_output ? "%.6f%s" : "%18.6f%s"; |
| 742 | fmt_n = csv_output ? "%s" : "%-25s"; | 742 | fmt_n = csv_output ? "%s" : "%-25s"; |
| 743 | 743 | ||
| 744 | aggr_printout(evsel, cpu, nr); | 744 | aggr_printout(evsel, id, nr); |
| 745 | 745 | ||
| 746 | scnprintf(name, sizeof(name), "%s%s", | 746 | scnprintf(name, sizeof(name), "%s%s", |
| 747 | perf_evsel__name(evsel), csv_output ? "" : " (msec)"); | 747 | perf_evsel__name(evsel), csv_output ? "" : " (msec)"); |
| @@ -947,11 +947,12 @@ static void print_ll_cache_misses(int cpu, | |||
| 947 | fprintf(output, " of all LL-cache hits "); | 947 | fprintf(output, " of all LL-cache hits "); |
| 948 | } | 948 | } |
| 949 | 949 | ||
| 950 | static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | 950 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
| 951 | { | 951 | { |
| 952 | double total, ratio = 0.0, total2; | 952 | double total, ratio = 0.0, total2; |
| 953 | double sc = evsel->scale; | 953 | double sc = evsel->scale; |
| 954 | const char *fmt; | 954 | const char *fmt; |
| 955 | int cpu = cpu_map__id_to_cpu(id); | ||
| 955 | 956 | ||
| 956 | if (csv_output) { | 957 | if (csv_output) { |
| 957 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; | 958 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; |
| @@ -962,7 +963,7 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | |||
| 962 | fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s"; | 963 | fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s"; |
| 963 | } | 964 | } |
| 964 | 965 | ||
| 965 | aggr_printout(evsel, cpu, nr); | 966 | aggr_printout(evsel, id, nr); |
| 966 | 967 | ||
| 967 | if (aggr_mode == AGGR_GLOBAL) | 968 | if (aggr_mode == AGGR_GLOBAL) |
| 968 | cpu = 0; | 969 | cpu = 0; |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 2f1a5220c090..35b425b6293f 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
| @@ -1605,7 +1605,9 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) | |||
| 1605 | int ret = -EINVAL; | 1605 | int ret = -EINVAL; |
| 1606 | 1606 | ||
| 1607 | if (session == NULL) | 1607 | if (session == NULL) |
| 1608 | return -ENOMEM; | 1608 | return -1; |
| 1609 | |||
| 1610 | symbol__init(&session->header.env); | ||
| 1609 | 1611 | ||
| 1610 | (void)perf_header__process_sections(&session->header, | 1612 | (void)perf_header__process_sections(&session->header, |
| 1611 | perf_data_file__fd(session->file), | 1613 | perf_data_file__fd(session->file), |
| @@ -1920,7 +1922,7 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1920 | .fork = process_fork_event, | 1922 | .fork = process_fork_event, |
| 1921 | .exit = process_exit_event, | 1923 | .exit = process_exit_event, |
| 1922 | .sample = process_sample_event, | 1924 | .sample = process_sample_event, |
| 1923 | .ordered_samples = true, | 1925 | .ordered_events = true, |
| 1924 | }, | 1926 | }, |
| 1925 | .proc_num = 15, | 1927 | .proc_num = 15, |
| 1926 | .min_time = 1000000, | 1928 | .min_time = 1000000, |
| @@ -1982,8 +1984,6 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1982 | return -1; | 1984 | return -1; |
| 1983 | } | 1985 | } |
| 1984 | 1986 | ||
| 1985 | symbol__init(); | ||
| 1986 | |||
| 1987 | if (argc && !strncmp(argv[0], "rec", 3)) { | 1987 | if (argc && !strncmp(argv[0], "rec", 3)) { |
| 1988 | argc = parse_options(argc, argv, record_options, record_usage, | 1988 | argc = parse_options(argc, argv, record_options, record_usage, |
| 1989 | PARSE_OPT_STOP_AT_NON_OPTION); | 1989 | PARSE_OPT_STOP_AT_NON_OPTION); |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 377971dc89a3..fc3d55f832ac 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -59,7 +59,7 @@ | |||
| 59 | 59 | ||
| 60 | #include <sys/syscall.h> | 60 | #include <sys/syscall.h> |
| 61 | #include <sys/ioctl.h> | 61 | #include <sys/ioctl.h> |
| 62 | #include <sys/poll.h> | 62 | #include <poll.h> |
| 63 | #include <sys/prctl.h> | 63 | #include <sys/prctl.h> |
| 64 | #include <sys/wait.h> | 64 | #include <sys/wait.h> |
| 65 | #include <sys/uio.h> | 65 | #include <sys/uio.h> |
| @@ -276,11 +276,17 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
| 276 | return; | 276 | return; |
| 277 | } | 277 | } |
| 278 | 278 | ||
| 279 | if (top->zero) { | ||
| 280 | hists__delete_entries(&top->sym_evsel->hists); | ||
| 281 | } else { | ||
| 282 | hists__decay_entries(&top->sym_evsel->hists, | ||
| 283 | top->hide_user_symbols, | ||
| 284 | top->hide_kernel_symbols); | ||
| 285 | } | ||
| 286 | |||
| 279 | hists__collapse_resort(&top->sym_evsel->hists, NULL); | 287 | hists__collapse_resort(&top->sym_evsel->hists, NULL); |
| 280 | hists__output_resort(&top->sym_evsel->hists); | 288 | hists__output_resort(&top->sym_evsel->hists); |
| 281 | hists__decay_entries(&top->sym_evsel->hists, | 289 | |
| 282 | top->hide_user_symbols, | ||
| 283 | top->hide_kernel_symbols); | ||
| 284 | hists__output_recalc_col_len(&top->sym_evsel->hists, | 290 | hists__output_recalc_col_len(&top->sym_evsel->hists, |
| 285 | top->print_entries - printed); | 291 | top->print_entries - printed); |
| 286 | putchar('\n'); | 292 | putchar('\n'); |
| @@ -427,18 +433,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) | |||
| 427 | 433 | ||
| 428 | if (!perf_top__key_mapped(top, c)) { | 434 | if (!perf_top__key_mapped(top, c)) { |
| 429 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 435 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
| 430 | struct termios tc, save; | 436 | struct termios save; |
| 431 | 437 | ||
| 432 | perf_top__print_mapped_keys(top); | 438 | perf_top__print_mapped_keys(top); |
| 433 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); | 439 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); |
| 434 | fflush(stdout); | 440 | fflush(stdout); |
| 435 | 441 | ||
| 436 | tcgetattr(0, &save); | 442 | set_term_quiet_input(&save); |
| 437 | tc = save; | ||
| 438 | tc.c_lflag &= ~(ICANON | ECHO); | ||
| 439 | tc.c_cc[VMIN] = 0; | ||
| 440 | tc.c_cc[VTIME] = 0; | ||
| 441 | tcsetattr(0, TCSANOW, &tc); | ||
| 442 | 443 | ||
| 443 | poll(&stdin_poll, 1, -1); | 444 | poll(&stdin_poll, 1, -1); |
| 444 | c = getc(stdin); | 445 | c = getc(stdin); |
| @@ -542,11 +543,16 @@ static void perf_top__sort_new_samples(void *arg) | |||
| 542 | if (t->evlist->selected != NULL) | 543 | if (t->evlist->selected != NULL) |
| 543 | t->sym_evsel = t->evlist->selected; | 544 | t->sym_evsel = t->evlist->selected; |
| 544 | 545 | ||
| 546 | if (t->zero) { | ||
| 547 | hists__delete_entries(&t->sym_evsel->hists); | ||
| 548 | } else { | ||
| 549 | hists__decay_entries(&t->sym_evsel->hists, | ||
| 550 | t->hide_user_symbols, | ||
| 551 | t->hide_kernel_symbols); | ||
| 552 | } | ||
| 553 | |||
| 545 | hists__collapse_resort(&t->sym_evsel->hists, NULL); | 554 | hists__collapse_resort(&t->sym_evsel->hists, NULL); |
| 546 | hists__output_resort(&t->sym_evsel->hists); | 555 | hists__output_resort(&t->sym_evsel->hists); |
| 547 | hists__decay_entries(&t->sym_evsel->hists, | ||
| 548 | t->hide_user_symbols, | ||
| 549 | t->hide_kernel_symbols); | ||
| 550 | } | 556 | } |
| 551 | 557 | ||
| 552 | static void *display_thread_tui(void *arg) | 558 | static void *display_thread_tui(void *arg) |
| @@ -577,23 +583,32 @@ static void *display_thread_tui(void *arg) | |||
| 577 | return NULL; | 583 | return NULL; |
| 578 | } | 584 | } |
| 579 | 585 | ||
| 586 | static void display_sig(int sig __maybe_unused) | ||
| 587 | { | ||
| 588 | done = 1; | ||
| 589 | } | ||
| 590 | |||
| 591 | static void display_setup_sig(void) | ||
| 592 | { | ||
| 593 | signal(SIGSEGV, display_sig); | ||
| 594 | signal(SIGFPE, display_sig); | ||
| 595 | signal(SIGINT, display_sig); | ||
| 596 | signal(SIGQUIT, display_sig); | ||
| 597 | signal(SIGTERM, display_sig); | ||
| 598 | } | ||
| 599 | |||
| 580 | static void *display_thread(void *arg) | 600 | static void *display_thread(void *arg) |
| 581 | { | 601 | { |
| 582 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 602 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
| 583 | struct termios tc, save; | 603 | struct termios save; |
| 584 | struct perf_top *top = arg; | 604 | struct perf_top *top = arg; |
| 585 | int delay_msecs, c; | 605 | int delay_msecs, c; |
| 586 | 606 | ||
| 587 | tcgetattr(0, &save); | 607 | display_setup_sig(); |
| 588 | tc = save; | ||
| 589 | tc.c_lflag &= ~(ICANON | ECHO); | ||
| 590 | tc.c_cc[VMIN] = 0; | ||
| 591 | tc.c_cc[VTIME] = 0; | ||
| 592 | |||
| 593 | pthread__unblock_sigwinch(); | 608 | pthread__unblock_sigwinch(); |
| 594 | repeat: | 609 | repeat: |
| 595 | delay_msecs = top->delay_secs * 1000; | 610 | delay_msecs = top->delay_secs * 1000; |
| 596 | tcsetattr(0, TCSANOW, &tc); | 611 | set_term_quiet_input(&save); |
| 597 | /* trash return*/ | 612 | /* trash return*/ |
| 598 | getc(stdin); | 613 | getc(stdin); |
| 599 | 614 | ||
| @@ -620,13 +635,16 @@ repeat: | |||
| 620 | } | 635 | } |
| 621 | } | 636 | } |
| 622 | 637 | ||
| 638 | tcsetattr(0, TCSAFLUSH, &save); | ||
| 623 | return NULL; | 639 | return NULL; |
| 624 | } | 640 | } |
| 625 | 641 | ||
| 626 | static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym) | 642 | static int symbol_filter(struct map *map, struct symbol *sym) |
| 627 | { | 643 | { |
| 628 | const char *name = sym->name; | 644 | const char *name = sym->name; |
| 629 | 645 | ||
| 646 | if (!map->dso->kernel) | ||
| 647 | return 0; | ||
| 630 | /* | 648 | /* |
| 631 | * ppc64 uses function descriptors and appends a '.' to the | 649 | * ppc64 uses function descriptors and appends a '.' to the |
| 632 | * start of every instruction address. Remove it. | 650 | * start of every instruction address. Remove it. |
| @@ -876,7 +894,7 @@ try_again: | |||
| 876 | 894 | ||
| 877 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { | 895 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { |
| 878 | ui__error("Failed to mmap with %d (%s)\n", | 896 | ui__error("Failed to mmap with %d (%s)\n", |
| 879 | errno, strerror(errno)); | 897 | errno, strerror_r(errno, msg, sizeof(msg))); |
| 880 | goto out_err; | 898 | goto out_err; |
| 881 | } | 899 | } |
| 882 | 900 | ||
| @@ -911,7 +929,7 @@ static int __cmd_top(struct perf_top *top) | |||
| 911 | 929 | ||
| 912 | top->session = perf_session__new(NULL, false, NULL); | 930 | top->session = perf_session__new(NULL, false, NULL); |
| 913 | if (top->session == NULL) | 931 | if (top->session == NULL) |
| 914 | return -ENOMEM; | 932 | return -1; |
| 915 | 933 | ||
| 916 | machines__set_symbol_filter(&top->session->machines, symbol_filter); | 934 | machines__set_symbol_filter(&top->session->machines, symbol_filter); |
| 917 | 935 | ||
| @@ -946,7 +964,7 @@ static int __cmd_top(struct perf_top *top) | |||
| 946 | perf_evlist__enable(top->evlist); | 964 | perf_evlist__enable(top->evlist); |
| 947 | 965 | ||
| 948 | /* Wait for a minimal set of events before starting the snapshot */ | 966 | /* Wait for a minimal set of events before starting the snapshot */ |
| 949 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 967 | perf_evlist__poll(top->evlist, 100); |
| 950 | 968 | ||
| 951 | perf_top__mmap_read(top); | 969 | perf_top__mmap_read(top); |
| 952 | 970 | ||
| @@ -963,7 +981,7 @@ static int __cmd_top(struct perf_top *top) | |||
| 963 | param.sched_priority = top->realtime_prio; | 981 | param.sched_priority = top->realtime_prio; |
| 964 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 982 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
| 965 | ui__error("Could not set realtime priority.\n"); | 983 | ui__error("Could not set realtime priority.\n"); |
| 966 | goto out_delete; | 984 | goto out_join; |
| 967 | } | 985 | } |
| 968 | } | 986 | } |
| 969 | 987 | ||
| @@ -973,10 +991,12 @@ static int __cmd_top(struct perf_top *top) | |||
| 973 | perf_top__mmap_read(top); | 991 | perf_top__mmap_read(top); |
| 974 | 992 | ||
| 975 | if (hits == top->samples) | 993 | if (hits == top->samples) |
| 976 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 994 | ret = perf_evlist__poll(top->evlist, 100); |
| 977 | } | 995 | } |
| 978 | 996 | ||
| 979 | ret = 0; | 997 | ret = 0; |
| 998 | out_join: | ||
| 999 | pthread_join(thread, NULL); | ||
| 980 | out_delete: | 1000 | out_delete: |
| 981 | perf_session__delete(top->session); | 1001 | perf_session__delete(top->session); |
| 982 | top->session = NULL; | 1002 | top->session = NULL; |
| @@ -1000,10 +1020,8 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
| 1000 | 1020 | ||
| 1001 | static int perf_top_config(const char *var, const char *value, void *cb) | 1021 | static int perf_top_config(const char *var, const char *value, void *cb) |
| 1002 | { | 1022 | { |
| 1003 | struct perf_top *top = cb; | ||
| 1004 | |||
| 1005 | if (!strcmp(var, "top.call-graph")) | 1023 | if (!strcmp(var, "top.call-graph")) |
| 1006 | return record_parse_callchain(value, &top->record_opts); | 1024 | var = "call-graph.record-mode"; /* fall-through */ |
| 1007 | if (!strcmp(var, "top.children")) { | 1025 | if (!strcmp(var, "top.children")) { |
| 1008 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); | 1026 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); |
| 1009 | return 0; | 1027 | return 0; |
| @@ -1122,6 +1140,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1122 | "Interleave source code with assembly code (default)"), | 1140 | "Interleave source code with assembly code (default)"), |
| 1123 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | 1141 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, |
| 1124 | "Display raw encoding of assembly instructions (default)"), | 1142 | "Display raw encoding of assembly instructions (default)"), |
| 1143 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
| 1144 | "Enable kernel symbol demangling"), | ||
| 1125 | OPT_STRING(0, "objdump", &objdump_path, "path", | 1145 | OPT_STRING(0, "objdump", &objdump_path, "path", |
| 1126 | "objdump binary to use for disassembly and annotations"), | 1146 | "objdump binary to use for disassembly and annotations"), |
| 1127 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1147 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
| @@ -1131,6 +1151,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1131 | "Don't show entries under that percent", parse_percent_limit), | 1151 | "Don't show entries under that percent", parse_percent_limit), |
| 1132 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | 1152 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", |
| 1133 | "How to display percentage of filtered entries", parse_filter_percentage), | 1153 | "How to display percentage of filtered entries", parse_filter_percentage), |
| 1154 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, | ||
| 1155 | "width[,width...]", | ||
| 1156 | "don't try to adjust column width, use these fixed values"), | ||
| 1134 | OPT_END() | 1157 | OPT_END() |
| 1135 | }; | 1158 | }; |
| 1136 | const char * const top_usage[] = { | 1159 | const char * const top_usage[] = { |
| @@ -1217,7 +1240,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1217 | symbol_conf.priv_size = sizeof(struct annotation); | 1240 | symbol_conf.priv_size = sizeof(struct annotation); |
| 1218 | 1241 | ||
| 1219 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1242 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
| 1220 | if (symbol__init() < 0) | 1243 | if (symbol__init(NULL) < 0) |
| 1221 | return -1; | 1244 | return -1; |
| 1222 | 1245 | ||
| 1223 | sort__setup_elide(stdout); | 1246 | sort__setup_elide(stdout); |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index a6c375224f46..09bcf2393910 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -402,6 +402,31 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, | |||
| 402 | 402 | ||
| 403 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags | 403 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags |
| 404 | 404 | ||
| 405 | static size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size, | ||
| 406 | struct syscall_arg *arg) | ||
| 407 | { | ||
| 408 | int printed = 0, flags = arg->val; | ||
| 409 | |||
| 410 | #define P_MREMAP_FLAG(n) \ | ||
| 411 | if (flags & MREMAP_##n) { \ | ||
| 412 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
| 413 | flags &= ~MREMAP_##n; \ | ||
| 414 | } | ||
| 415 | |||
| 416 | P_MREMAP_FLAG(MAYMOVE); | ||
| 417 | #ifdef MREMAP_FIXED | ||
| 418 | P_MREMAP_FLAG(FIXED); | ||
| 419 | #endif | ||
| 420 | #undef P_MREMAP_FLAG | ||
| 421 | |||
| 422 | if (flags) | ||
| 423 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | ||
| 424 | |||
| 425 | return printed; | ||
| 426 | } | ||
| 427 | |||
| 428 | #define SCA_MREMAP_FLAGS syscall_arg__scnprintf_mremap_flags | ||
| 429 | |||
| 405 | static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, | 430 | static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, |
| 406 | struct syscall_arg *arg) | 431 | struct syscall_arg *arg) |
| 407 | { | 432 | { |
| @@ -1004,6 +1029,7 @@ static struct syscall_fmt { | |||
| 1004 | [2] = SCA_MMAP_PROT, /* prot */ }, }, | 1029 | [2] = SCA_MMAP_PROT, /* prot */ }, }, |
| 1005 | { .name = "mremap", .hexret = true, | 1030 | { .name = "mremap", .hexret = true, |
| 1006 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | 1031 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ |
| 1032 | [3] = SCA_MREMAP_FLAGS, /* flags */ | ||
| 1007 | [4] = SCA_HEX, /* new_addr */ }, }, | 1033 | [4] = SCA_HEX, /* new_addr */ }, }, |
| 1008 | { .name = "munlock", .errmsg = true, | 1034 | { .name = "munlock", .errmsg = true, |
| 1009 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, | 1035 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, |
| @@ -1385,7 +1411,7 @@ static int trace__tool_process(struct perf_tool *tool, | |||
| 1385 | 1411 | ||
| 1386 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | 1412 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) |
| 1387 | { | 1413 | { |
| 1388 | int err = symbol__init(); | 1414 | int err = symbol__init(NULL); |
| 1389 | 1415 | ||
| 1390 | if (err) | 1416 | if (err) |
| 1391 | return err; | 1417 | return err; |
| @@ -1669,7 +1695,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
| 1669 | union perf_event *event __maybe_unused, | 1695 | union perf_event *event __maybe_unused, |
| 1670 | struct perf_sample *sample) | 1696 | struct perf_sample *sample) |
| 1671 | { | 1697 | { |
| 1672 | int ret; | 1698 | long ret; |
| 1673 | u64 duration = 0; | 1699 | u64 duration = 0; |
| 1674 | struct thread *thread; | 1700 | struct thread *thread; |
| 1675 | int id = perf_evsel__sc_tp_uint(evsel, id, sample); | 1701 | int id = perf_evsel__sc_tp_uint(evsel, id, sample); |
| @@ -1722,9 +1748,9 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
| 1722 | 1748 | ||
| 1723 | if (sc->fmt == NULL) { | 1749 | if (sc->fmt == NULL) { |
| 1724 | signed_print: | 1750 | signed_print: |
| 1725 | fprintf(trace->output, ") = %d", ret); | 1751 | fprintf(trace->output, ") = %ld", ret); |
| 1726 | } else if (ret < 0 && sc->fmt->errmsg) { | 1752 | } else if (ret < 0 && sc->fmt->errmsg) { |
| 1727 | char bf[256]; | 1753 | char bf[STRERR_BUFSIZE]; |
| 1728 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 1754 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
| 1729 | *e = audit_errno_to_name(-ret); | 1755 | *e = audit_errno_to_name(-ret); |
| 1730 | 1756 | ||
| @@ -1732,7 +1758,7 @@ signed_print: | |||
| 1732 | } else if (ret == 0 && sc->fmt->timeout) | 1758 | } else if (ret == 0 && sc->fmt->timeout) |
| 1733 | fprintf(trace->output, ") = 0 Timeout"); | 1759 | fprintf(trace->output, ") = 0 Timeout"); |
| 1734 | else if (sc->fmt->hexret) | 1760 | else if (sc->fmt->hexret) |
| 1735 | fprintf(trace->output, ") = %#x", ret); | 1761 | fprintf(trace->output, ") = %#lx", ret); |
| 1736 | else | 1762 | else |
| 1737 | goto signed_print; | 1763 | goto signed_print; |
| 1738 | 1764 | ||
| @@ -2018,6 +2044,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
| 2018 | int err = -1, i; | 2044 | int err = -1, i; |
| 2019 | unsigned long before; | 2045 | unsigned long before; |
| 2020 | const bool forks = argc > 0; | 2046 | const bool forks = argc > 0; |
| 2047 | bool draining = false; | ||
| 2048 | char sbuf[STRERR_BUFSIZE]; | ||
| 2021 | 2049 | ||
| 2022 | trace->live = true; | 2050 | trace->live = true; |
| 2023 | 2051 | ||
| @@ -2079,7 +2107,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
| 2079 | 2107 | ||
| 2080 | err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false); | 2108 | err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false); |
| 2081 | if (err < 0) { | 2109 | if (err < 0) { |
| 2082 | fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno)); | 2110 | fprintf(trace->output, "Couldn't mmap the events: %s\n", |
| 2111 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 2083 | goto out_delete_evlist; | 2112 | goto out_delete_evlist; |
| 2084 | } | 2113 | } |
| 2085 | 2114 | ||
| @@ -2143,8 +2172,12 @@ next_event: | |||
| 2143 | if (trace->nr_events == before) { | 2172 | if (trace->nr_events == before) { |
| 2144 | int timeout = done ? 100 : -1; | 2173 | int timeout = done ? 100 : -1; |
| 2145 | 2174 | ||
| 2146 | if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0) | 2175 | if (!draining && perf_evlist__poll(evlist, timeout) > 0) { |
| 2176 | if (perf_evlist__filter_pollfd(evlist, POLLERR | POLLHUP) == 0) | ||
| 2177 | draining = true; | ||
| 2178 | |||
| 2147 | goto again; | 2179 | goto again; |
| 2180 | } | ||
| 2148 | } else { | 2181 | } else { |
| 2149 | goto again; | 2182 | goto again; |
| 2150 | } | 2183 | } |
| @@ -2209,18 +2242,18 @@ static int trace__replay(struct trace *trace) | |||
| 2209 | trace->tool.tracing_data = perf_event__process_tracing_data; | 2242 | trace->tool.tracing_data = perf_event__process_tracing_data; |
| 2210 | trace->tool.build_id = perf_event__process_build_id; | 2243 | trace->tool.build_id = perf_event__process_build_id; |
| 2211 | 2244 | ||
| 2212 | trace->tool.ordered_samples = true; | 2245 | trace->tool.ordered_events = true; |
| 2213 | trace->tool.ordering_requires_timestamps = true; | 2246 | trace->tool.ordering_requires_timestamps = true; |
| 2214 | 2247 | ||
| 2215 | /* add tid to output */ | 2248 | /* add tid to output */ |
| 2216 | trace->multiple_threads = true; | 2249 | trace->multiple_threads = true; |
| 2217 | 2250 | ||
| 2218 | if (symbol__init() < 0) | ||
| 2219 | return -1; | ||
| 2220 | |||
| 2221 | session = perf_session__new(&file, false, &trace->tool); | 2251 | session = perf_session__new(&file, false, &trace->tool); |
| 2222 | if (session == NULL) | 2252 | if (session == NULL) |
| 2223 | return -ENOMEM; | 2253 | return -1; |
| 2254 | |||
| 2255 | if (symbol__init(&session->header.env) < 0) | ||
| 2256 | goto out; | ||
| 2224 | 2257 | ||
| 2225 | trace->host = &session->machines.host; | 2258 | trace->host = &session->machines.host; |
| 2226 | 2259 | ||
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 1f67aa02d240..58f609198c6d 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
| @@ -48,10 +48,6 @@ ifneq ($(ARCH),$(filter $(ARCH),x86 arm)) | |||
| 48 | NO_LIBDW_DWARF_UNWIND := 1 | 48 | NO_LIBDW_DWARF_UNWIND := 1 |
| 49 | endif | 49 | endif |
| 50 | 50 | ||
| 51 | ifeq ($(ARCH),powerpc) | ||
| 52 | CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX | ||
| 53 | endif | ||
| 54 | |||
| 55 | ifeq ($(LIBUNWIND_LIBS),) | 51 | ifeq ($(LIBUNWIND_LIBS),) |
| 56 | NO_LIBUNWIND := 1 | 52 | NO_LIBUNWIND := 1 |
| 57 | else | 53 | else |
| @@ -120,6 +116,29 @@ ifdef PARSER_DEBUG | |||
| 120 | CFLAGS += -DPARSER_DEBUG | 116 | CFLAGS += -DPARSER_DEBUG |
| 121 | endif | 117 | endif |
| 122 | 118 | ||
| 119 | ifndef NO_LIBPYTHON | ||
| 120 | # Try different combinations to accommodate systems that only have | ||
| 121 | # python[2][-config] in weird combinations but always preferring | ||
| 122 | # python2 and python2-config as per pep-0394. If we catch a | ||
| 123 | # python[-config] in version 3, the version check will kill it. | ||
| 124 | PYTHON2 := $(if $(call get-executable,python2),python2,python) | ||
| 125 | override PYTHON := $(call get-executable-or-default,PYTHON,$(PYTHON2)) | ||
| 126 | PYTHON2_CONFIG := \ | ||
| 127 | $(if $(call get-executable,$(PYTHON)-config),$(PYTHON)-config,python-config) | ||
| 128 | override PYTHON_CONFIG := \ | ||
| 129 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON2_CONFIG)) | ||
| 130 | |||
| 131 | PYTHON_CONFIG_SQ := $(call shell-sq,$(PYTHON_CONFIG)) | ||
| 132 | |||
| 133 | PYTHON_EMBED_LDOPTS := $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) | ||
| 134 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | ||
| 135 | |||
| 136 | FEATURE_CHECK_CFLAGS-libpython := $(PYTHON_EMBED_CCOPTS) | ||
| 137 | FEATURE_CHECK_LDFLAGS-libpython := $(PYTHON_EMBED_LDOPTS) | ||
| 138 | FEATURE_CHECK_CFLAGS-libpython-version := $(PYTHON_EMBED_CCOPTS) | ||
| 139 | FEATURE_CHECK_LDFLAGS-libpython-version := $(PYTHON_EMBED_LDOPTS) | ||
| 140 | endif | ||
| 141 | |||
| 123 | CFLAGS += -fno-omit-frame-pointer | 142 | CFLAGS += -fno-omit-frame-pointer |
| 124 | CFLAGS += -ggdb3 | 143 | CFLAGS += -ggdb3 |
| 125 | CFLAGS += -funwind-tables | 144 | CFLAGS += -funwind-tables |
| @@ -355,6 +374,12 @@ ifndef NO_LIBELF | |||
| 355 | endif # NO_DWARF | 374 | endif # NO_DWARF |
| 356 | endif # NO_LIBELF | 375 | endif # NO_LIBELF |
| 357 | 376 | ||
| 377 | ifeq ($(ARCH),powerpc) | ||
| 378 | ifndef NO_DWARF | ||
| 379 | CFLAGS += -DHAVE_SKIP_CALLCHAIN_IDX | ||
| 380 | endif | ||
| 381 | endif | ||
| 382 | |||
| 358 | ifndef NO_LIBUNWIND | 383 | ifndef NO_LIBUNWIND |
| 359 | ifneq ($(feature-libunwind), 1) | 384 | ifneq ($(feature-libunwind), 1) |
| 360 | msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR); | 385 | msg := $(warning No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR); |
| @@ -482,21 +507,14 @@ define disable-python_code | |||
| 482 | NO_LIBPYTHON := 1 | 507 | NO_LIBPYTHON := 1 |
| 483 | endef | 508 | endef |
| 484 | 509 | ||
| 485 | override PYTHON := \ | 510 | ifdef NO_LIBPYTHON |
| 486 | $(call get-executable-or-default,PYTHON,python) | 511 | $(call disable-python) |
| 487 | |||
| 488 | ifndef PYTHON | ||
| 489 | $(call disable-python,python interpreter) | ||
| 490 | else | 512 | else |
| 491 | 513 | ||
| 492 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | 514 | ifndef PYTHON |
| 493 | 515 | $(call disable-python,python interpreter) | |
| 494 | ifdef NO_LIBPYTHON | ||
| 495 | $(call disable-python) | ||
| 496 | else | 516 | else |
| 497 | 517 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | |
| 498 | override PYTHON_CONFIG := \ | ||
| 499 | $(call get-executable-or-default,PYTHON_CONFIG,$(PYTHON)-config) | ||
| 500 | 518 | ||
| 501 | ifndef PYTHON_CONFIG | 519 | ifndef PYTHON_CONFIG |
| 502 | $(call disable-python,python-config tool) | 520 | $(call disable-python,python-config tool) |
| @@ -635,11 +653,13 @@ else | |||
| 635 | sysconfdir = $(prefix)/etc | 653 | sysconfdir = $(prefix)/etc |
| 636 | ETC_PERFCONFIG = etc/perfconfig | 654 | ETC_PERFCONFIG = etc/perfconfig |
| 637 | endif | 655 | endif |
| 656 | ifndef lib | ||
| 638 | ifeq ($(IS_X86_64),1) | 657 | ifeq ($(IS_X86_64),1) |
| 639 | lib = lib64 | 658 | lib = lib64 |
| 640 | else | 659 | else |
| 641 | lib = lib | 660 | lib = lib |
| 642 | endif | 661 | endif |
| 662 | endif # lib | ||
| 643 | libdir = $(prefix)/$(lib) | 663 | libdir = $(prefix)/$(lib) |
| 644 | 664 | ||
| 645 | # Shell quote (do not use $(call) to accommodate ancient setups); | 665 | # Shell quote (do not use $(call) to accommodate ancient setups); |
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 6088f8d8a434..72ab2984718e 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile | |||
| @@ -101,25 +101,11 @@ FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) | |||
| 101 | test-libperl.bin: | 101 | test-libperl.bin: |
| 102 | $(BUILD) $(FLAGS_PERL_EMBED) | 102 | $(BUILD) $(FLAGS_PERL_EMBED) |
| 103 | 103 | ||
| 104 | override PYTHON := python | ||
| 105 | override PYTHON_CONFIG := python-config | ||
| 106 | |||
| 107 | escape-for-shell-sq = $(subst ','\'',$(1)) | ||
| 108 | shell-sq = '$(escape-for-shell-sq)' | ||
| 109 | |||
| 110 | PYTHON_CONFIG_SQ = $(call shell-sq,$(PYTHON_CONFIG)) | ||
| 111 | |||
| 112 | PYTHON_EMBED_LDOPTS = $(shell $(PYTHON_CONFIG_SQ) --ldflags 2>/dev/null) | ||
| 113 | PYTHON_EMBED_LDFLAGS = $(call strip-libs,$(PYTHON_EMBED_LDOPTS)) | ||
| 114 | PYTHON_EMBED_LIBADD = $(call grep-libs,$(PYTHON_EMBED_LDOPTS)) | ||
| 115 | PYTHON_EMBED_CCOPTS = $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | ||
| 116 | FLAGS_PYTHON_EMBED = $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) | ||
| 117 | |||
| 118 | test-libpython.bin: | 104 | test-libpython.bin: |
| 119 | $(BUILD) $(FLAGS_PYTHON_EMBED) | 105 | $(BUILD) |
| 120 | 106 | ||
| 121 | test-libpython-version.bin: | 107 | test-libpython-version.bin: |
| 122 | $(BUILD) $(FLAGS_PYTHON_EMBED) | 108 | $(BUILD) |
| 123 | 109 | ||
| 124 | test-libbfd.bin: | 110 | test-libbfd.bin: |
| 125 | $(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl | 111 | $(BUILD) -DPACKAGE='"perf"' -lbfd -lz -liberty -ldl |
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 4d985e0f03f5..7076a62d0ff7 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak | |||
| @@ -132,7 +132,7 @@ endef | |||
| 132 | # | 132 | # |
| 133 | # Usage: bool-value = $(call is-absolute,path) | 133 | # Usage: bool-value = $(call is-absolute,path) |
| 134 | # | 134 | # |
| 135 | is-absolute = $(shell echo $(shell-sq) | grep ^/ -q && echo y) | 135 | is-absolute = $(shell echo $(shell-sq) | grep -q ^/ && echo y) |
| 136 | 136 | ||
| 137 | # lookup | 137 | # lookup |
| 138 | # | 138 | # |
diff --git a/tools/perf/perf-with-kcore.sh b/tools/perf/perf-with-kcore.sh new file mode 100644 index 000000000000..c7ff90a90e4e --- /dev/null +++ b/tools/perf/perf-with-kcore.sh | |||
| @@ -0,0 +1,259 @@ | |||
| 1 | #!/bin/bash | ||
| 2 | # perf-with-kcore: use perf with a copy of kcore | ||
| 3 | # Copyright (c) 2014, Intel Corporation. | ||
| 4 | # | ||
| 5 | # This program is free software; you can redistribute it and/or modify it | ||
| 6 | # under the terms and conditions of the GNU General Public License, | ||
| 7 | # version 2, as published by the Free Software Foundation. | ||
| 8 | # | ||
| 9 | # This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | # more details. | ||
| 13 | |||
| 14 | set -e | ||
| 15 | |||
| 16 | usage() | ||
| 17 | { | ||
| 18 | echo "Usage: perf-with-kcore <perf sub-command> <perf.data directory> [<sub-command options> [ -- <workload>]]" >&2 | ||
| 19 | echo " <perf sub-command> can be record, script, report or inject" >&2 | ||
| 20 | echo " or: perf-with-kcore fix_buildid_cache_permissions" >&2 | ||
| 21 | exit 1 | ||
| 22 | } | ||
| 23 | |||
| 24 | find_perf() | ||
| 25 | { | ||
| 26 | if [ -n "$PERF" ] ; then | ||
| 27 | return | ||
| 28 | fi | ||
| 29 | PERF=`which perf || true` | ||
| 30 | if [ -z "$PERF" ] ; then | ||
| 31 | echo "Failed to find perf" >&2 | ||
| 32 | exit 1 | ||
| 33 | fi | ||
| 34 | if [ ! -x "$PERF" ] ; then | ||
| 35 | echo "Failed to find perf" >&2 | ||
| 36 | exit 1 | ||
| 37 | fi | ||
| 38 | echo "Using $PERF" | ||
| 39 | "$PERF" version | ||
| 40 | } | ||
| 41 | |||
| 42 | copy_kcore() | ||
| 43 | { | ||
| 44 | echo "Copying kcore" | ||
| 45 | |||
| 46 | if [ $EUID -eq 0 ] ; then | ||
| 47 | SUDO="" | ||
| 48 | else | ||
| 49 | SUDO="sudo" | ||
| 50 | fi | ||
| 51 | |||
| 52 | rm -f perf.data.junk | ||
| 53 | ("$PERF" record -o perf.data.junk $PERF_OPTIONS -- sleep 60) >/dev/null 2>/dev/null & | ||
| 54 | PERF_PID=$! | ||
| 55 | |||
| 56 | # Need to make sure that perf has started | ||
| 57 | sleep 1 | ||
| 58 | |||
| 59 | KCORE=$(($SUDO "$PERF" buildid-cache -v -f -k /proc/kcore >/dev/null) 2>&1) | ||
| 60 | case "$KCORE" in | ||
| 61 | "kcore added to build-id cache directory "*) | ||
| 62 | KCORE_DIR=${KCORE#"kcore added to build-id cache directory "} | ||
| 63 | ;; | ||
| 64 | *) | ||
| 65 | kill $PERF_PID | ||
| 66 | wait >/dev/null 2>/dev/null || true | ||
| 67 | rm perf.data.junk | ||
| 68 | echo "$KCORE" | ||
| 69 | echo "Failed to find kcore" >&2 | ||
| 70 | exit 1 | ||
| 71 | ;; | ||
| 72 | esac | ||
| 73 | |||
| 74 | kill $PERF_PID | ||
| 75 | wait >/dev/null 2>/dev/null || true | ||
| 76 | rm perf.data.junk | ||
| 77 | |||
| 78 | $SUDO cp -a "$KCORE_DIR" "$(pwd)/$PERF_DATA_DIR" | ||
| 79 | $SUDO rm -f "$KCORE_DIR/kcore" | ||
| 80 | $SUDO rm -f "$KCORE_DIR/kallsyms" | ||
| 81 | $SUDO rm -f "$KCORE_DIR/modules" | ||
| 82 | $SUDO rmdir "$KCORE_DIR" | ||
| 83 | |||
| 84 | KCORE_DIR_BASENAME=$(basename "$KCORE_DIR") | ||
| 85 | KCORE_DIR="$(pwd)/$PERF_DATA_DIR/$KCORE_DIR_BASENAME" | ||
| 86 | |||
| 87 | $SUDO chown $UID "$KCORE_DIR" | ||
| 88 | $SUDO chown $UID "$KCORE_DIR/kcore" | ||
| 89 | $SUDO chown $UID "$KCORE_DIR/kallsyms" | ||
| 90 | $SUDO chown $UID "$KCORE_DIR/modules" | ||
| 91 | |||
| 92 | $SUDO chgrp $GROUPS "$KCORE_DIR" | ||
| 93 | $SUDO chgrp $GROUPS "$KCORE_DIR/kcore" | ||
| 94 | $SUDO chgrp $GROUPS "$KCORE_DIR/kallsyms" | ||
| 95 | $SUDO chgrp $GROUPS "$KCORE_DIR/modules" | ||
| 96 | |||
| 97 | ln -s "$KCORE_DIR_BASENAME" "$PERF_DATA_DIR/kcore_dir" | ||
| 98 | } | ||
| 99 | |||
| 100 | fix_buildid_cache_permissions() | ||
| 101 | { | ||
| 102 | if [ $EUID -ne 0 ] ; then | ||
| 103 | echo "This script must be run as root via sudo " >&2 | ||
| 104 | exit 1 | ||
| 105 | fi | ||
| 106 | |||
| 107 | if [ -z "$SUDO_USER" ] ; then | ||
| 108 | echo "This script must be run via sudo" >&2 | ||
| 109 | exit 1 | ||
| 110 | fi | ||
| 111 | |||
| 112 | USER_HOME=$(bash <<< "echo ~$SUDO_USER") | ||
| 113 | |||
| 114 | if [ "$HOME" != "$USER_HOME" ] ; then | ||
| 115 | echo "Fix unnecessary because root has a home: $HOME" >&2 | ||
| 116 | exit 1 | ||
| 117 | fi | ||
| 118 | |||
| 119 | echo "Fixing buildid cache permissions" | ||
| 120 | |||
| 121 | find "$USER_HOME/.debug" -xdev -type d ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \; | ||
| 122 | find "$USER_HOME/.debug" -xdev -type f -links 1 ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \; | ||
| 123 | find "$USER_HOME/.debug" -xdev -type l ! -user "$SUDO_USER" -ls -exec chown -h "$SUDO_USER" \{\} \; | ||
| 124 | |||
| 125 | if [ -n "$SUDO_GID" ] ; then | ||
| 126 | find "$USER_HOME/.debug" -xdev -type d ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \; | ||
| 127 | find "$USER_HOME/.debug" -xdev -type f -links 1 ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \; | ||
| 128 | find "$USER_HOME/.debug" -xdev -type l ! -group "$SUDO_GID" -ls -exec chgrp -h "$SUDO_GID" \{\} \; | ||
| 129 | fi | ||
| 130 | |||
| 131 | echo "Done" | ||
| 132 | } | ||
| 133 | |||
| 134 | check_buildid_cache_permissions() | ||
| 135 | { | ||
| 136 | if [ $EUID -eq 0 ] ; then | ||
| 137 | return | ||
| 138 | fi | ||
| 139 | |||
| 140 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -user "$USER" -print -quit) | ||
| 141 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -user "$USER" -print -quit) | ||
| 142 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -user "$USER" -print -quit) | ||
| 143 | |||
| 144 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -group "$GROUPS" -print -quit) | ||
| 145 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -group "$GROUPS" -print -quit) | ||
| 146 | PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -group "$GROUPS" -print -quit) | ||
| 147 | |||
| 148 | if [ -n "$PERMISSIONS_OK" ] ; then | ||
| 149 | echo "*** WARNING *** buildid cache permissions may need fixing" >&2 | ||
| 150 | fi | ||
| 151 | } | ||
| 152 | |||
| 153 | record() | ||
| 154 | { | ||
| 155 | echo "Recording" | ||
| 156 | |||
| 157 | if [ $EUID -ne 0 ] ; then | ||
| 158 | |||
| 159 | if [ "$(cat /proc/sys/kernel/kptr_restrict)" -ne 0 ] ; then | ||
| 160 | echo "*** WARNING *** /proc/sys/kernel/kptr_restrict prevents access to kernel addresses" >&2 | ||
| 161 | fi | ||
| 162 | |||
| 163 | if echo "$PERF_OPTIONS" | grep -q ' -a \|^-a \| -a$\|^-a$\| --all-cpus \|^--all-cpus \| --all-cpus$\|^--all-cpus$' ; then | ||
| 164 | echo "*** WARNING *** system-wide tracing without root access will not be able to read all necessary information from /proc" >&2 | ||
| 165 | fi | ||
| 166 | |||
| 167 | if echo "$PERF_OPTIONS" | grep -q 'intel_pt\|intel_bts\| -I\|^-I' ; then | ||
| 168 | if [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt -1 ] ; then | ||
| 169 | echo "*** WARNING *** /proc/sys/kernel/perf_event_paranoid restricts buffer size and tracepoint (sched_switch) use" >&2 | ||
| 170 | fi | ||
| 171 | |||
| 172 | if echo "$PERF_OPTIONS" | grep -q ' --per-thread \|^--per-thread \| --per-thread$\|^--per-thread$' ; then | ||
| 173 | true | ||
| 174 | elif echo "$PERF_OPTIONS" | grep -q ' -t \|^-t \| -t$\|^-t$' ; then | ||
| 175 | true | ||
| 176 | elif [ ! -r /sys/kernel/debug -o ! -x /sys/kernel/debug ] ; then | ||
| 177 | echo "*** WARNING *** /sys/kernel/debug permissions prevent tracepoint (sched_switch) use" >&2 | ||
| 178 | fi | ||
| 179 | fi | ||
| 180 | fi | ||
| 181 | |||
| 182 | if [ -z "$1" ] ; then | ||
| 183 | echo "Workload is required for recording" >&2 | ||
| 184 | usage | ||
| 185 | fi | ||
| 186 | |||
| 187 | if [ -e "$PERF_DATA_DIR" ] ; then | ||
| 188 | echo "'$PERF_DATA_DIR' exists" >&2 | ||
| 189 | exit 1 | ||
| 190 | fi | ||
| 191 | |||
| 192 | find_perf | ||
| 193 | |||
| 194 | mkdir "$PERF_DATA_DIR" | ||
| 195 | |||
| 196 | echo "$PERF record -o $PERF_DATA_DIR/perf.data $PERF_OPTIONS -- $*" | ||
| 197 | "$PERF" record -o "$PERF_DATA_DIR/perf.data" $PERF_OPTIONS -- $* || true | ||
| 198 | |||
| 199 | if rmdir "$PERF_DATA_DIR" > /dev/null 2>/dev/null ; then | ||
| 200 | exit 1 | ||
| 201 | fi | ||
| 202 | |||
| 203 | copy_kcore | ||
| 204 | |||
| 205 | echo "Done" | ||
| 206 | } | ||
| 207 | |||
| 208 | subcommand() | ||
| 209 | { | ||
| 210 | find_perf | ||
| 211 | check_buildid_cache_permissions | ||
| 212 | echo "$PERF $PERF_SUB_COMMAND -i $PERF_DATA_DIR/perf.data --kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms $*" | ||
| 213 | "$PERF" $PERF_SUB_COMMAND -i "$PERF_DATA_DIR/perf.data" "--kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms" $* | ||
| 214 | } | ||
| 215 | |||
| 216 | if [ "$1" = "fix_buildid_cache_permissions" ] ; then | ||
| 217 | fix_buildid_cache_permissions | ||
| 218 | exit 0 | ||
| 219 | fi | ||
| 220 | |||
| 221 | PERF_SUB_COMMAND=$1 | ||
| 222 | PERF_DATA_DIR=$2 | ||
| 223 | shift || true | ||
| 224 | shift || true | ||
| 225 | |||
| 226 | if [ -z "$PERF_SUB_COMMAND" ] ; then | ||
| 227 | usage | ||
| 228 | fi | ||
| 229 | |||
| 230 | if [ -z "$PERF_DATA_DIR" ] ; then | ||
| 231 | usage | ||
| 232 | fi | ||
| 233 | |||
| 234 | case "$PERF_SUB_COMMAND" in | ||
| 235 | "record") | ||
| 236 | while [ "$1" != "--" ] ; do | ||
| 237 | PERF_OPTIONS+="$1 " | ||
| 238 | shift || break | ||
| 239 | done | ||
| 240 | if [ "$1" != "--" ] ; then | ||
| 241 | echo "Options and workload are required for recording" >&2 | ||
| 242 | usage | ||
| 243 | fi | ||
| 244 | shift | ||
| 245 | record $* | ||
| 246 | ;; | ||
| 247 | "script") | ||
| 248 | subcommand $* | ||
| 249 | ;; | ||
| 250 | "report") | ||
| 251 | subcommand $* | ||
| 252 | ;; | ||
| 253 | "inject") | ||
| 254 | subcommand $* | ||
| 255 | ;; | ||
| 256 | *) | ||
| 257 | usage | ||
| 258 | ;; | ||
| 259 | esac | ||
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 2282d41879a2..452a8474d29d 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
| @@ -313,6 +313,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
| 313 | int status; | 313 | int status; |
| 314 | struct stat st; | 314 | struct stat st; |
| 315 | const char *prefix; | 315 | const char *prefix; |
| 316 | char sbuf[STRERR_BUFSIZE]; | ||
| 316 | 317 | ||
| 317 | prefix = NULL; | 318 | prefix = NULL; |
| 318 | if (p->option & RUN_SETUP) | 319 | if (p->option & RUN_SETUP) |
| @@ -343,7 +344,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
| 343 | status = 1; | 344 | status = 1; |
| 344 | /* Check for ENOSPC and EIO errors.. */ | 345 | /* Check for ENOSPC and EIO errors.. */ |
| 345 | if (fflush(stdout)) { | 346 | if (fflush(stdout)) { |
| 346 | fprintf(stderr, "write failure on standard output: %s", strerror(errno)); | 347 | fprintf(stderr, "write failure on standard output: %s", |
| 348 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 347 | goto out; | 349 | goto out; |
| 348 | } | 350 | } |
| 349 | if (ferror(stdout)) { | 351 | if (ferror(stdout)) { |
| @@ -351,7 +353,8 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
| 351 | goto out; | 353 | goto out; |
| 352 | } | 354 | } |
| 353 | if (fclose(stdout)) { | 355 | if (fclose(stdout)) { |
| 354 | fprintf(stderr, "close failed on standard output: %s", strerror(errno)); | 356 | fprintf(stderr, "close failed on standard output: %s", |
| 357 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 355 | goto out; | 358 | goto out; |
| 356 | } | 359 | } |
| 357 | status = 0; | 360 | status = 0; |
| @@ -466,6 +469,7 @@ void pthread__unblock_sigwinch(void) | |||
| 466 | int main(int argc, const char **argv) | 469 | int main(int argc, const char **argv) |
| 467 | { | 470 | { |
| 468 | const char *cmd; | 471 | const char *cmd; |
| 472 | char sbuf[STRERR_BUFSIZE]; | ||
| 469 | 473 | ||
| 470 | /* The page_size is placed in util object. */ | 474 | /* The page_size is placed in util object. */ |
| 471 | page_size = sysconf(_SC_PAGE_SIZE); | 475 | page_size = sysconf(_SC_PAGE_SIZE); |
| @@ -561,7 +565,7 @@ int main(int argc, const char **argv) | |||
| 561 | } | 565 | } |
| 562 | 566 | ||
| 563 | fprintf(stderr, "Failed to run command '%s': %s\n", | 567 | fprintf(stderr, "Failed to run command '%s': %s\n", |
| 564 | cmd, strerror(errno)); | 568 | cmd, strerror_r(errno, sbuf, sizeof(sbuf))); |
| 565 | out: | 569 | out: |
| 566 | return 1; | 570 | return 1; |
| 567 | } | 571 | } |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 510c65f72858..220d44e44c1b 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -41,8 +41,6 @@ void pthread__unblock_sigwinch(void); | |||
| 41 | 41 | ||
| 42 | struct record_opts { | 42 | struct record_opts { |
| 43 | struct target target; | 43 | struct target target; |
| 44 | int call_graph; | ||
| 45 | bool call_graph_enabled; | ||
| 46 | bool group; | 44 | bool group; |
| 47 | bool inherit_stat; | 45 | bool inherit_stat; |
| 48 | bool no_buffering; | 46 | bool no_buffering; |
| @@ -60,7 +58,6 @@ struct record_opts { | |||
| 60 | u64 branch_stack; | 58 | u64 branch_stack; |
| 61 | u64 default_interval; | 59 | u64 default_interval; |
| 62 | u64 user_interval; | 60 | u64 user_interval; |
| 63 | u16 stack_dump_size; | ||
| 64 | bool sample_transaction; | 61 | bool sample_transaction; |
| 65 | unsigned initial_delay; | 62 | unsigned initial_delay; |
| 66 | }; | 63 | }; |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 6f8b01bc6033..ac655b0700e7 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
| @@ -154,6 +154,18 @@ static struct test { | |||
| 154 | .func = test__hists_cumulate, | 154 | .func = test__hists_cumulate, |
| 155 | }, | 155 | }, |
| 156 | { | 156 | { |
| 157 | .desc = "Test tracking with sched_switch", | ||
| 158 | .func = test__switch_tracking, | ||
| 159 | }, | ||
| 160 | { | ||
| 161 | .desc = "Filter fds with revents mask in a fdarray", | ||
| 162 | .func = test__fdarray__filter, | ||
| 163 | }, | ||
| 164 | { | ||
| 165 | .desc = "Add fd to a fdarray, making it autogrow", | ||
| 166 | .func = test__fdarray__add, | ||
| 167 | }, | ||
| 168 | { | ||
| 157 | .func = NULL, | 169 | .func = NULL, |
| 158 | }, | 170 | }, |
| 159 | }; | 171 | }; |
| @@ -185,9 +197,11 @@ static bool perf_test__matches(int curr, int argc, const char *argv[]) | |||
| 185 | static int run_test(struct test *test) | 197 | static int run_test(struct test *test) |
| 186 | { | 198 | { |
| 187 | int status, err = -1, child = fork(); | 199 | int status, err = -1, child = fork(); |
| 200 | char sbuf[STRERR_BUFSIZE]; | ||
| 188 | 201 | ||
| 189 | if (child < 0) { | 202 | if (child < 0) { |
| 190 | pr_err("failed to fork test: %s\n", strerror(errno)); | 203 | pr_err("failed to fork test: %s\n", |
| 204 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 191 | return -1; | 205 | return -1; |
| 192 | } | 206 | } |
| 193 | 207 | ||
| @@ -297,7 +311,7 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 297 | symbol_conf.sort_by_name = true; | 311 | symbol_conf.sort_by_name = true; |
| 298 | symbol_conf.try_vmlinux_path = true; | 312 | symbol_conf.try_vmlinux_path = true; |
| 299 | 313 | ||
| 300 | if (symbol__init() < 0) | 314 | if (symbol__init(NULL) < 0) |
| 301 | return -1; | 315 | return -1; |
| 302 | 316 | ||
| 303 | if (skip != NULL) | 317 | if (skip != NULL) |
diff --git a/tools/perf/tests/fdarray.c b/tools/perf/tests/fdarray.c new file mode 100644 index 000000000000..d24b837951d4 --- /dev/null +++ b/tools/perf/tests/fdarray.c | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | #include <api/fd/array.h> | ||
| 2 | #include "util/debug.h" | ||
| 3 | #include "tests/tests.h" | ||
| 4 | |||
| 5 | static void fdarray__init_revents(struct fdarray *fda, short revents) | ||
| 6 | { | ||
| 7 | int fd; | ||
| 8 | |||
| 9 | fda->nr = fda->nr_alloc; | ||
| 10 | |||
| 11 | for (fd = 0; fd < fda->nr; ++fd) { | ||
| 12 | fda->entries[fd].fd = fda->nr - fd; | ||
| 13 | fda->entries[fd].revents = revents; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 17 | static int fdarray__fprintf_prefix(struct fdarray *fda, const char *prefix, FILE *fp) | ||
| 18 | { | ||
| 19 | int printed = 0; | ||
| 20 | |||
| 21 | if (!verbose) | ||
| 22 | return 0; | ||
| 23 | |||
| 24 | printed += fprintf(fp, "\n%s: ", prefix); | ||
| 25 | return printed + fdarray__fprintf(fda, fp); | ||
| 26 | } | ||
| 27 | |||
| 28 | int test__fdarray__filter(void) | ||
| 29 | { | ||
| 30 | int nr_fds, expected_fd[2], fd, err = TEST_FAIL; | ||
| 31 | struct fdarray *fda = fdarray__new(5, 5); | ||
| 32 | |||
| 33 | if (fda == NULL) { | ||
| 34 | pr_debug("\nfdarray__new() failed!"); | ||
| 35 | goto out; | ||
| 36 | } | ||
| 37 | |||
| 38 | fdarray__init_revents(fda, POLLIN); | ||
| 39 | nr_fds = fdarray__filter(fda, POLLHUP, NULL); | ||
| 40 | if (nr_fds != fda->nr_alloc) { | ||
| 41 | pr_debug("\nfdarray__filter()=%d != %d shouldn't have filtered anything", | ||
| 42 | nr_fds, fda->nr_alloc); | ||
| 43 | goto out_delete; | ||
| 44 | } | ||
| 45 | |||
| 46 | fdarray__init_revents(fda, POLLHUP); | ||
| 47 | nr_fds = fdarray__filter(fda, POLLHUP, NULL); | ||
| 48 | if (nr_fds != 0) { | ||
| 49 | pr_debug("\nfdarray__filter()=%d != %d, should have filtered all fds", | ||
| 50 | nr_fds, fda->nr_alloc); | ||
| 51 | goto out_delete; | ||
| 52 | } | ||
| 53 | |||
| 54 | fdarray__init_revents(fda, POLLHUP); | ||
| 55 | fda->entries[2].revents = POLLIN; | ||
| 56 | expected_fd[0] = fda->entries[2].fd; | ||
| 57 | |||
| 58 | pr_debug("\nfiltering all but fda->entries[2]:"); | ||
| 59 | fdarray__fprintf_prefix(fda, "before", stderr); | ||
| 60 | nr_fds = fdarray__filter(fda, POLLHUP, NULL); | ||
| 61 | fdarray__fprintf_prefix(fda, " after", stderr); | ||
| 62 | if (nr_fds != 1) { | ||
| 63 | pr_debug("\nfdarray__filter()=%d != 1, should have left just one event", nr_fds); | ||
| 64 | goto out_delete; | ||
| 65 | } | ||
| 66 | |||
| 67 | if (fda->entries[0].fd != expected_fd[0]) { | ||
| 68 | pr_debug("\nfda->entries[0].fd=%d != %d\n", | ||
| 69 | fda->entries[0].fd, expected_fd[0]); | ||
| 70 | goto out_delete; | ||
| 71 | } | ||
| 72 | |||
| 73 | fdarray__init_revents(fda, POLLHUP); | ||
| 74 | fda->entries[0].revents = POLLIN; | ||
| 75 | expected_fd[0] = fda->entries[0].fd; | ||
| 76 | fda->entries[3].revents = POLLIN; | ||
| 77 | expected_fd[1] = fda->entries[3].fd; | ||
| 78 | |||
| 79 | pr_debug("\nfiltering all but (fda->entries[0], fda->entries[3]):"); | ||
| 80 | fdarray__fprintf_prefix(fda, "before", stderr); | ||
| 81 | nr_fds = fdarray__filter(fda, POLLHUP, NULL); | ||
| 82 | fdarray__fprintf_prefix(fda, " after", stderr); | ||
| 83 | if (nr_fds != 2) { | ||
| 84 | pr_debug("\nfdarray__filter()=%d != 2, should have left just two events", | ||
| 85 | nr_fds); | ||
| 86 | goto out_delete; | ||
| 87 | } | ||
| 88 | |||
| 89 | for (fd = 0; fd < 2; ++fd) { | ||
| 90 | if (fda->entries[fd].fd != expected_fd[fd]) { | ||
| 91 | pr_debug("\nfda->entries[%d].fd=%d != %d\n", fd, | ||
| 92 | fda->entries[fd].fd, expected_fd[fd]); | ||
| 93 | goto out_delete; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | pr_debug("\n"); | ||
| 98 | |||
| 99 | err = 0; | ||
| 100 | out_delete: | ||
| 101 | fdarray__delete(fda); | ||
| 102 | out: | ||
| 103 | return err; | ||
| 104 | } | ||
| 105 | |||
| 106 | int test__fdarray__add(void) | ||
| 107 | { | ||
| 108 | int err = TEST_FAIL; | ||
| 109 | struct fdarray *fda = fdarray__new(2, 2); | ||
| 110 | |||
| 111 | if (fda == NULL) { | ||
| 112 | pr_debug("\nfdarray__new() failed!"); | ||
| 113 | goto out; | ||
| 114 | } | ||
| 115 | |||
| 116 | #define FDA_CHECK(_idx, _fd, _revents) \ | ||
| 117 | if (fda->entries[_idx].fd != _fd) { \ | ||
| 118 | pr_debug("\n%d: fda->entries[%d](%d) != %d!", \ | ||
| 119 | __LINE__, _idx, fda->entries[1].fd, _fd); \ | ||
| 120 | goto out_delete; \ | ||
| 121 | } \ | ||
| 122 | if (fda->entries[_idx].events != (_revents)) { \ | ||
| 123 | pr_debug("\n%d: fda->entries[%d].revents(%d) != %d!", \ | ||
| 124 | __LINE__, _idx, fda->entries[_idx].fd, _revents); \ | ||
| 125 | goto out_delete; \ | ||
| 126 | } | ||
| 127 | |||
| 128 | #define FDA_ADD(_idx, _fd, _revents, _nr) \ | ||
| 129 | if (fdarray__add(fda, _fd, _revents) < 0) { \ | ||
| 130 | pr_debug("\n%d: fdarray__add(fda, %d, %d) failed!", \ | ||
| 131 | __LINE__,_fd, _revents); \ | ||
| 132 | goto out_delete; \ | ||
| 133 | } \ | ||
| 134 | if (fda->nr != _nr) { \ | ||
| 135 | pr_debug("\n%d: fdarray__add(fda, %d, %d)=%d != %d", \ | ||
| 136 | __LINE__,_fd, _revents, fda->nr, _nr); \ | ||
| 137 | goto out_delete; \ | ||
| 138 | } \ | ||
| 139 | FDA_CHECK(_idx, _fd, _revents) | ||
| 140 | |||
| 141 | FDA_ADD(0, 1, POLLIN, 1); | ||
| 142 | FDA_ADD(1, 2, POLLERR, 2); | ||
| 143 | |||
| 144 | fdarray__fprintf_prefix(fda, "before growing array", stderr); | ||
| 145 | |||
| 146 | FDA_ADD(2, 35, POLLHUP, 3); | ||
| 147 | |||
| 148 | if (fda->entries == NULL) { | ||
| 149 | pr_debug("\nfdarray__add(fda, 35, POLLHUP) should have allocated fda->pollfd!"); | ||
| 150 | goto out_delete; | ||
| 151 | } | ||
| 152 | |||
| 153 | fdarray__fprintf_prefix(fda, "after 3rd add", stderr); | ||
| 154 | |||
| 155 | FDA_ADD(3, 88, POLLIN | POLLOUT, 4); | ||
| 156 | |||
| 157 | fdarray__fprintf_prefix(fda, "after 4th add", stderr); | ||
| 158 | |||
| 159 | FDA_CHECK(0, 1, POLLIN); | ||
| 160 | FDA_CHECK(1, 2, POLLERR); | ||
| 161 | FDA_CHECK(2, 35, POLLHUP); | ||
| 162 | FDA_CHECK(3, 88, POLLIN | POLLOUT); | ||
| 163 | |||
| 164 | #undef FDA_ADD | ||
| 165 | #undef FDA_CHECK | ||
| 166 | |||
| 167 | pr_debug("\n"); | ||
| 168 | |||
| 169 | err = 0; | ||
| 170 | out_delete: | ||
| 171 | fdarray__delete(fda); | ||
| 172 | out: | ||
| 173 | return err; | ||
| 174 | } | ||
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 142263492f6f..9b9622a33932 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c | |||
| @@ -31,6 +31,7 @@ int test__basic_mmap(void) | |||
| 31 | unsigned int nr_events[nsyscalls], | 31 | unsigned int nr_events[nsyscalls], |
| 32 | expected_nr_events[nsyscalls], i, j; | 32 | expected_nr_events[nsyscalls], i, j; |
| 33 | struct perf_evsel *evsels[nsyscalls], *evsel; | 33 | struct perf_evsel *evsels[nsyscalls], *evsel; |
| 34 | char sbuf[STRERR_BUFSIZE]; | ||
| 34 | 35 | ||
| 35 | threads = thread_map__new(-1, getpid(), UINT_MAX); | 36 | threads = thread_map__new(-1, getpid(), UINT_MAX); |
| 36 | if (threads == NULL) { | 37 | if (threads == NULL) { |
| @@ -49,7 +50,7 @@ int test__basic_mmap(void) | |||
| 49 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | 50 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); |
| 50 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | 51 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { |
| 51 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | 52 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", |
| 52 | cpus->map[0], strerror(errno)); | 53 | cpus->map[0], strerror_r(errno, sbuf, sizeof(sbuf))); |
| 53 | goto out_free_cpus; | 54 | goto out_free_cpus; |
| 54 | } | 55 | } |
| 55 | 56 | ||
| @@ -79,7 +80,7 @@ int test__basic_mmap(void) | |||
| 79 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { | 80 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { |
| 80 | pr_debug("failed to open counter: %s, " | 81 | pr_debug("failed to open counter: %s, " |
| 81 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 82 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
| 82 | strerror(errno)); | 83 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 83 | goto out_delete_evlist; | 84 | goto out_delete_evlist; |
| 84 | } | 85 | } |
| 85 | 86 | ||
| @@ -89,7 +90,7 @@ int test__basic_mmap(void) | |||
| 89 | 90 | ||
| 90 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | 91 | if (perf_evlist__mmap(evlist, 128, true) < 0) { |
| 91 | pr_debug("failed to mmap events: %d (%s)\n", errno, | 92 | pr_debug("failed to mmap events: %d (%s)\n", errno, |
| 92 | strerror(errno)); | 93 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 93 | goto out_delete_evlist; | 94 | goto out_delete_evlist; |
| 94 | } | 95 | } |
| 95 | 96 | ||
diff --git a/tools/perf/tests/open-syscall-all-cpus.c b/tools/perf/tests/open-syscall-all-cpus.c index 5fecdbd2f5f7..8fa82d1700c7 100644 --- a/tools/perf/tests/open-syscall-all-cpus.c +++ b/tools/perf/tests/open-syscall-all-cpus.c | |||
| @@ -12,6 +12,7 @@ int test__open_syscall_event_on_all_cpus(void) | |||
| 12 | unsigned int nr_open_calls = 111, i; | 12 | unsigned int nr_open_calls = 111, i; |
| 13 | cpu_set_t cpu_set; | 13 | cpu_set_t cpu_set; |
| 14 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); | 14 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); |
| 15 | char sbuf[STRERR_BUFSIZE]; | ||
| 15 | 16 | ||
| 16 | if (threads == NULL) { | 17 | if (threads == NULL) { |
| 17 | pr_debug("thread_map__new\n"); | 18 | pr_debug("thread_map__new\n"); |
| @@ -35,7 +36,7 @@ int test__open_syscall_event_on_all_cpus(void) | |||
| 35 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | 36 | if (perf_evsel__open(evsel, cpus, threads) < 0) { |
| 36 | pr_debug("failed to open counter: %s, " | 37 | pr_debug("failed to open counter: %s, " |
| 37 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 38 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
| 38 | strerror(errno)); | 39 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 39 | goto out_evsel_delete; | 40 | goto out_evsel_delete; |
| 40 | } | 41 | } |
| 41 | 42 | ||
| @@ -56,7 +57,7 @@ int test__open_syscall_event_on_all_cpus(void) | |||
| 56 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | 57 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { |
| 57 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | 58 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", |
| 58 | cpus->map[cpu], | 59 | cpus->map[cpu], |
| 59 | strerror(errno)); | 60 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 60 | goto out_close_fd; | 61 | goto out_close_fd; |
| 61 | } | 62 | } |
| 62 | for (i = 0; i < ncalls; ++i) { | 63 | for (i = 0; i < ncalls; ++i) { |
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index 0785b64ffd6c..127dcae0b760 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c | |||
| @@ -22,6 +22,7 @@ int test__syscall_open_tp_fields(void) | |||
| 22 | struct perf_evlist *evlist = perf_evlist__new(); | 22 | struct perf_evlist *evlist = perf_evlist__new(); |
| 23 | struct perf_evsel *evsel; | 23 | struct perf_evsel *evsel; |
| 24 | int err = -1, i, nr_events = 0, nr_polls = 0; | 24 | int err = -1, i, nr_events = 0, nr_polls = 0; |
| 25 | char sbuf[STRERR_BUFSIZE]; | ||
| 25 | 26 | ||
| 26 | if (evlist == NULL) { | 27 | if (evlist == NULL) { |
| 27 | pr_debug("%s: perf_evlist__new\n", __func__); | 28 | pr_debug("%s: perf_evlist__new\n", __func__); |
| @@ -48,13 +49,15 @@ int test__syscall_open_tp_fields(void) | |||
| 48 | 49 | ||
| 49 | err = perf_evlist__open(evlist); | 50 | err = perf_evlist__open(evlist); |
| 50 | if (err < 0) { | 51 | if (err < 0) { |
| 51 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | 52 | pr_debug("perf_evlist__open: %s\n", |
| 53 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 52 | goto out_delete_evlist; | 54 | goto out_delete_evlist; |
| 53 | } | 55 | } |
| 54 | 56 | ||
| 55 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 57 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
| 56 | if (err < 0) { | 58 | if (err < 0) { |
| 57 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | 59 | pr_debug("perf_evlist__mmap: %s\n", |
| 60 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 58 | goto out_delete_evlist; | 61 | goto out_delete_evlist; |
| 59 | } | 62 | } |
| 60 | 63 | ||
| @@ -102,7 +105,7 @@ int test__syscall_open_tp_fields(void) | |||
| 102 | } | 105 | } |
| 103 | 106 | ||
| 104 | if (nr_events == before) | 107 | if (nr_events == before) |
| 105 | poll(evlist->pollfd, evlist->nr_fds, 10); | 108 | perf_evlist__poll(evlist, 10); |
| 106 | 109 | ||
| 107 | if (++nr_polls > 5) { | 110 | if (++nr_polls > 5) { |
| 108 | pr_debug("%s: no events!\n", __func__); | 111 | pr_debug("%s: no events!\n", __func__); |
diff --git a/tools/perf/tests/open-syscall.c b/tools/perf/tests/open-syscall.c index c1dc7d25f38c..a33b2daae40f 100644 --- a/tools/perf/tests/open-syscall.c +++ b/tools/perf/tests/open-syscall.c | |||
| @@ -9,6 +9,7 @@ int test__open_syscall_event(void) | |||
| 9 | struct perf_evsel *evsel; | 9 | struct perf_evsel *evsel; |
| 10 | unsigned int nr_open_calls = 111, i; | 10 | unsigned int nr_open_calls = 111, i; |
| 11 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); | 11 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); |
| 12 | char sbuf[STRERR_BUFSIZE]; | ||
| 12 | 13 | ||
| 13 | if (threads == NULL) { | 14 | if (threads == NULL) { |
| 14 | pr_debug("thread_map__new\n"); | 15 | pr_debug("thread_map__new\n"); |
| @@ -24,7 +25,7 @@ int test__open_syscall_event(void) | |||
| 24 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | 25 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { |
| 25 | pr_debug("failed to open counter: %s, " | 26 | pr_debug("failed to open counter: %s, " |
| 26 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 27 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
| 27 | strerror(errno)); | 28 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 28 | goto out_evsel_delete; | 29 | goto out_evsel_delete; |
| 29 | } | 30 | } |
| 30 | 31 | ||
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index aca1a83dd13a..7a228a2a070b 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c | |||
| @@ -59,6 +59,7 @@ int test__PERF_RECORD(void) | |||
| 59 | int err = -1, errs = 0, i, wakeups = 0; | 59 | int err = -1, errs = 0, i, wakeups = 0; |
| 60 | u32 cpu; | 60 | u32 cpu; |
| 61 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; | 61 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; |
| 62 | char sbuf[STRERR_BUFSIZE]; | ||
| 62 | 63 | ||
| 63 | if (evlist == NULL || argv == NULL) { | 64 | if (evlist == NULL || argv == NULL) { |
| 64 | pr_debug("Not enough memory to create evlist\n"); | 65 | pr_debug("Not enough memory to create evlist\n"); |
| @@ -100,7 +101,8 @@ int test__PERF_RECORD(void) | |||
| 100 | 101 | ||
| 101 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); | 102 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); |
| 102 | if (err < 0) { | 103 | if (err < 0) { |
| 103 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | 104 | pr_debug("sched__get_first_possible_cpu: %s\n", |
| 105 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 104 | goto out_delete_evlist; | 106 | goto out_delete_evlist; |
| 105 | } | 107 | } |
| 106 | 108 | ||
| @@ -110,7 +112,8 @@ int test__PERF_RECORD(void) | |||
| 110 | * So that we can check perf_sample.cpu on all the samples. | 112 | * So that we can check perf_sample.cpu on all the samples. |
| 111 | */ | 113 | */ |
| 112 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { | 114 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { |
| 113 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | 115 | pr_debug("sched_setaffinity: %s\n", |
| 116 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 114 | goto out_delete_evlist; | 117 | goto out_delete_evlist; |
| 115 | } | 118 | } |
| 116 | 119 | ||
| @@ -120,7 +123,8 @@ int test__PERF_RECORD(void) | |||
| 120 | */ | 123 | */ |
| 121 | err = perf_evlist__open(evlist); | 124 | err = perf_evlist__open(evlist); |
| 122 | if (err < 0) { | 125 | if (err < 0) { |
| 123 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | 126 | pr_debug("perf_evlist__open: %s\n", |
| 127 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 124 | goto out_delete_evlist; | 128 | goto out_delete_evlist; |
| 125 | } | 129 | } |
| 126 | 130 | ||
| @@ -131,7 +135,8 @@ int test__PERF_RECORD(void) | |||
| 131 | */ | 135 | */ |
| 132 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | 136 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); |
| 133 | if (err < 0) { | 137 | if (err < 0) { |
| 134 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | 138 | pr_debug("perf_evlist__mmap: %s\n", |
| 139 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 135 | goto out_delete_evlist; | 140 | goto out_delete_evlist; |
| 136 | } | 141 | } |
| 137 | 142 | ||
| @@ -263,7 +268,7 @@ int test__PERF_RECORD(void) | |||
| 263 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | 268 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. |
| 264 | */ | 269 | */ |
| 265 | if (total_events == before && false) | 270 | if (total_events == before && false) |
| 266 | poll(evlist->pollfd, evlist->nr_fds, -1); | 271 | perf_evlist__poll(evlist, -1); |
| 267 | 272 | ||
| 268 | sleep(1); | 273 | sleep(1); |
| 269 | if (++wakeups > 5) { | 274 | if (++wakeups > 5) { |
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index 12b322fa3475..eeb68bb1972d 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c | |||
| @@ -152,7 +152,7 @@ int test__pmu(void) | |||
| 152 | if (ret) | 152 | if (ret) |
| 153 | break; | 153 | break; |
| 154 | 154 | ||
| 155 | ret = perf_pmu__config_terms(&formats, &attr, terms); | 155 | ret = perf_pmu__config_terms(&formats, &attr, terms, false); |
| 156 | if (ret) | 156 | if (ret) |
| 157 | break; | 157 | break; |
| 158 | 158 | ||
diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c index c04d1f268576..d31f2c4d9f64 100644 --- a/tools/perf/tests/rdpmc.c +++ b/tools/perf/tests/rdpmc.c | |||
| @@ -100,6 +100,7 @@ static int __test__rdpmc(void) | |||
| 100 | }; | 100 | }; |
| 101 | u64 delta_sum = 0; | 101 | u64 delta_sum = 0; |
| 102 | struct sigaction sa; | 102 | struct sigaction sa; |
| 103 | char sbuf[STRERR_BUFSIZE]; | ||
| 103 | 104 | ||
| 104 | sigfillset(&sa.sa_mask); | 105 | sigfillset(&sa.sa_mask); |
| 105 | sa.sa_sigaction = segfault_handler; | 106 | sa.sa_sigaction = segfault_handler; |
| @@ -109,14 +110,15 @@ static int __test__rdpmc(void) | |||
| 109 | perf_event_open_cloexec_flag()); | 110 | perf_event_open_cloexec_flag()); |
| 110 | if (fd < 0) { | 111 | if (fd < 0) { |
| 111 | pr_err("Error: sys_perf_event_open() syscall returned " | 112 | pr_err("Error: sys_perf_event_open() syscall returned " |
| 112 | "with %d (%s)\n", fd, strerror(errno)); | 113 | "with %d (%s)\n", fd, |
| 114 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 113 | return -1; | 115 | return -1; |
| 114 | } | 116 | } |
| 115 | 117 | ||
| 116 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); | 118 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); |
| 117 | if (addr == (void *)(-1)) { | 119 | if (addr == (void *)(-1)) { |
| 118 | pr_err("Error: mmap() syscall returned with (%s)\n", | 120 | pr_err("Error: mmap() syscall returned with (%s)\n", |
| 119 | strerror(errno)); | 121 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 120 | goto out_close; | 122 | goto out_close; |
| 121 | } | 123 | } |
| 122 | 124 | ||
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index 983d6b8562a8..1aa21c90731b 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c | |||
| @@ -22,6 +22,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
| 22 | volatile int tmp = 0; | 22 | volatile int tmp = 0; |
| 23 | u64 total_periods = 0; | 23 | u64 total_periods = 0; |
| 24 | int nr_samples = 0; | 24 | int nr_samples = 0; |
| 25 | char sbuf[STRERR_BUFSIZE]; | ||
| 25 | union perf_event *event; | 26 | union perf_event *event; |
| 26 | struct perf_evsel *evsel; | 27 | struct perf_evsel *evsel; |
| 27 | struct perf_evlist *evlist; | 28 | struct perf_evlist *evlist; |
| @@ -62,14 +63,15 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
| 62 | 63 | ||
| 63 | err = -errno; | 64 | err = -errno; |
| 64 | pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in this test.\n", | 65 | pr_debug("Couldn't open evlist: %s\nHint: check %s, using %" PRIu64 " in this test.\n", |
| 65 | strerror(errno), knob, (u64)attr.sample_freq); | 66 | strerror_r(errno, sbuf, sizeof(sbuf)), |
| 67 | knob, (u64)attr.sample_freq); | ||
| 66 | goto out_delete_evlist; | 68 | goto out_delete_evlist; |
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | err = perf_evlist__mmap(evlist, 128, true); | 71 | err = perf_evlist__mmap(evlist, 128, true); |
| 70 | if (err < 0) { | 72 | if (err < 0) { |
| 71 | pr_debug("failed to mmap event: %d (%s)\n", errno, | 73 | pr_debug("failed to mmap event: %d (%s)\n", errno, |
| 72 | strerror(errno)); | 74 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 73 | goto out_delete_evlist; | 75 | goto out_delete_evlist; |
| 74 | } | 76 | } |
| 75 | 77 | ||
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c new file mode 100644 index 000000000000..cc68648c7c55 --- /dev/null +++ b/tools/perf/tests/switch-tracking.c | |||
| @@ -0,0 +1,572 @@ | |||
| 1 | #include <sys/time.h> | ||
| 2 | #include <sys/prctl.h> | ||
| 3 | #include <time.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | |||
| 6 | #include "parse-events.h" | ||
| 7 | #include "evlist.h" | ||
| 8 | #include "evsel.h" | ||
| 9 | #include "thread_map.h" | ||
| 10 | #include "cpumap.h" | ||
| 11 | #include "tests.h" | ||
| 12 | |||
| 13 | static int spin_sleep(void) | ||
| 14 | { | ||
| 15 | struct timeval start, now, diff, maxtime; | ||
| 16 | struct timespec ts; | ||
| 17 | int err, i; | ||
| 18 | |||
| 19 | maxtime.tv_sec = 0; | ||
| 20 | maxtime.tv_usec = 50000; | ||
| 21 | |||
| 22 | err = gettimeofday(&start, NULL); | ||
| 23 | if (err) | ||
| 24 | return err; | ||
| 25 | |||
| 26 | /* Spin for 50ms */ | ||
| 27 | while (1) { | ||
| 28 | for (i = 0; i < 1000; i++) | ||
| 29 | barrier(); | ||
| 30 | |||
| 31 | err = gettimeofday(&now, NULL); | ||
| 32 | if (err) | ||
| 33 | return err; | ||
| 34 | |||
| 35 | timersub(&now, &start, &diff); | ||
| 36 | if (timercmp(&diff, &maxtime, > /* For checkpatch */)) | ||
| 37 | break; | ||
| 38 | } | ||
| 39 | |||
| 40 | ts.tv_nsec = 50 * 1000 * 1000; | ||
| 41 | ts.tv_sec = 0; | ||
| 42 | |||
| 43 | /* Sleep for 50ms */ | ||
| 44 | err = nanosleep(&ts, NULL); | ||
| 45 | if (err == EINTR) | ||
| 46 | err = 0; | ||
| 47 | |||
| 48 | return err; | ||
| 49 | } | ||
| 50 | |||
| 51 | struct switch_tracking { | ||
| 52 | struct perf_evsel *switch_evsel; | ||
| 53 | struct perf_evsel *cycles_evsel; | ||
| 54 | pid_t *tids; | ||
| 55 | int nr_tids; | ||
| 56 | int comm_seen[4]; | ||
| 57 | int cycles_before_comm_1; | ||
| 58 | int cycles_between_comm_2_and_comm_3; | ||
| 59 | int cycles_after_comm_4; | ||
| 60 | }; | ||
| 61 | |||
| 62 | static int check_comm(struct switch_tracking *switch_tracking, | ||
| 63 | union perf_event *event, const char *comm, int nr) | ||
| 64 | { | ||
| 65 | if (event->header.type == PERF_RECORD_COMM && | ||
| 66 | (pid_t)event->comm.pid == getpid() && | ||
| 67 | (pid_t)event->comm.tid == getpid() && | ||
| 68 | strcmp(event->comm.comm, comm) == 0) { | ||
| 69 | if (switch_tracking->comm_seen[nr]) { | ||
| 70 | pr_debug("Duplicate comm event\n"); | ||
| 71 | return -1; | ||
| 72 | } | ||
| 73 | switch_tracking->comm_seen[nr] = 1; | ||
| 74 | pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr); | ||
| 75 | return 1; | ||
| 76 | } | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | static int check_cpu(struct switch_tracking *switch_tracking, int cpu) | ||
| 81 | { | ||
| 82 | int i, nr = cpu + 1; | ||
| 83 | |||
| 84 | if (cpu < 0) | ||
| 85 | return -1; | ||
| 86 | |||
| 87 | if (!switch_tracking->tids) { | ||
| 88 | switch_tracking->tids = calloc(nr, sizeof(pid_t)); | ||
| 89 | if (!switch_tracking->tids) | ||
| 90 | return -1; | ||
| 91 | for (i = 0; i < nr; i++) | ||
| 92 | switch_tracking->tids[i] = -1; | ||
| 93 | switch_tracking->nr_tids = nr; | ||
| 94 | return 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | if (cpu >= switch_tracking->nr_tids) { | ||
| 98 | void *addr; | ||
| 99 | |||
| 100 | addr = realloc(switch_tracking->tids, nr * sizeof(pid_t)); | ||
| 101 | if (!addr) | ||
| 102 | return -1; | ||
| 103 | switch_tracking->tids = addr; | ||
| 104 | for (i = switch_tracking->nr_tids; i < nr; i++) | ||
| 105 | switch_tracking->tids[i] = -1; | ||
| 106 | switch_tracking->nr_tids = nr; | ||
| 107 | return 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | static int process_sample_event(struct perf_evlist *evlist, | ||
| 114 | union perf_event *event, | ||
| 115 | struct switch_tracking *switch_tracking) | ||
| 116 | { | ||
| 117 | struct perf_sample sample; | ||
| 118 | struct perf_evsel *evsel; | ||
| 119 | pid_t next_tid, prev_tid; | ||
| 120 | int cpu, err; | ||
| 121 | |||
| 122 | if (perf_evlist__parse_sample(evlist, event, &sample)) { | ||
| 123 | pr_debug("perf_evlist__parse_sample failed\n"); | ||
| 124 | return -1; | ||
| 125 | } | ||
| 126 | |||
| 127 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
| 128 | if (evsel == switch_tracking->switch_evsel) { | ||
| 129 | next_tid = perf_evsel__intval(evsel, &sample, "next_pid"); | ||
| 130 | prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid"); | ||
| 131 | cpu = sample.cpu; | ||
| 132 | pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n", | ||
| 133 | cpu, prev_tid, next_tid); | ||
| 134 | err = check_cpu(switch_tracking, cpu); | ||
| 135 | if (err) | ||
| 136 | return err; | ||
| 137 | /* | ||
| 138 | * Check for no missing sched_switch events i.e. that the | ||
| 139 | * evsel->system_wide flag has worked. | ||
| 140 | */ | ||
| 141 | if (switch_tracking->tids[cpu] != -1 && | ||
| 142 | switch_tracking->tids[cpu] != prev_tid) { | ||
| 143 | pr_debug("Missing sched_switch events\n"); | ||
| 144 | return -1; | ||
| 145 | } | ||
| 146 | switch_tracking->tids[cpu] = next_tid; | ||
| 147 | } | ||
| 148 | |||
| 149 | if (evsel == switch_tracking->cycles_evsel) { | ||
| 150 | pr_debug3("cycles event\n"); | ||
| 151 | if (!switch_tracking->comm_seen[0]) | ||
| 152 | switch_tracking->cycles_before_comm_1 = 1; | ||
| 153 | if (switch_tracking->comm_seen[1] && | ||
| 154 | !switch_tracking->comm_seen[2]) | ||
| 155 | switch_tracking->cycles_between_comm_2_and_comm_3 = 1; | ||
| 156 | if (switch_tracking->comm_seen[3]) | ||
| 157 | switch_tracking->cycles_after_comm_4 = 1; | ||
| 158 | } | ||
| 159 | |||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | static int process_event(struct perf_evlist *evlist, union perf_event *event, | ||
| 164 | struct switch_tracking *switch_tracking) | ||
| 165 | { | ||
| 166 | if (event->header.type == PERF_RECORD_SAMPLE) | ||
| 167 | return process_sample_event(evlist, event, switch_tracking); | ||
| 168 | |||
| 169 | if (event->header.type == PERF_RECORD_COMM) { | ||
| 170 | int err, done = 0; | ||
| 171 | |||
| 172 | err = check_comm(switch_tracking, event, "Test COMM 1", 0); | ||
| 173 | if (err < 0) | ||
| 174 | return -1; | ||
| 175 | done += err; | ||
| 176 | err = check_comm(switch_tracking, event, "Test COMM 2", 1); | ||
| 177 | if (err < 0) | ||
| 178 | return -1; | ||
| 179 | done += err; | ||
| 180 | err = check_comm(switch_tracking, event, "Test COMM 3", 2); | ||
| 181 | if (err < 0) | ||
| 182 | return -1; | ||
| 183 | done += err; | ||
| 184 | err = check_comm(switch_tracking, event, "Test COMM 4", 3); | ||
| 185 | if (err < 0) | ||
| 186 | return -1; | ||
| 187 | done += err; | ||
| 188 | if (done != 1) { | ||
| 189 | pr_debug("Unexpected comm event\n"); | ||
| 190 | return -1; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | |||
| 194 | return 0; | ||
| 195 | } | ||
| 196 | |||
| 197 | struct event_node { | ||
| 198 | struct list_head list; | ||
| 199 | union perf_event *event; | ||
| 200 | u64 event_time; | ||
| 201 | }; | ||
| 202 | |||
| 203 | static int add_event(struct perf_evlist *evlist, struct list_head *events, | ||
| 204 | union perf_event *event) | ||
| 205 | { | ||
| 206 | struct perf_sample sample; | ||
| 207 | struct event_node *node; | ||
| 208 | |||
| 209 | node = malloc(sizeof(struct event_node)); | ||
| 210 | if (!node) { | ||
| 211 | pr_debug("malloc failed\n"); | ||
| 212 | return -1; | ||
| 213 | } | ||
| 214 | node->event = event; | ||
| 215 | list_add(&node->list, events); | ||
| 216 | |||
| 217 | if (perf_evlist__parse_sample(evlist, event, &sample)) { | ||
| 218 | pr_debug("perf_evlist__parse_sample failed\n"); | ||
| 219 | return -1; | ||
| 220 | } | ||
| 221 | |||
| 222 | if (!sample.time) { | ||
| 223 | pr_debug("event with no time\n"); | ||
| 224 | return -1; | ||
| 225 | } | ||
| 226 | |||
| 227 | node->event_time = sample.time; | ||
| 228 | |||
| 229 | return 0; | ||
| 230 | } | ||
| 231 | |||
| 232 | static void free_event_nodes(struct list_head *events) | ||
| 233 | { | ||
| 234 | struct event_node *node; | ||
| 235 | |||
| 236 | while (!list_empty(events)) { | ||
| 237 | node = list_entry(events->next, struct event_node, list); | ||
| 238 | list_del(&node->list); | ||
| 239 | free(node); | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | static int compar(const void *a, const void *b) | ||
| 244 | { | ||
| 245 | const struct event_node *nodea = a; | ||
| 246 | const struct event_node *nodeb = b; | ||
| 247 | s64 cmp = nodea->event_time - nodeb->event_time; | ||
| 248 | |||
| 249 | return cmp; | ||
| 250 | } | ||
| 251 | |||
| 252 | static int process_events(struct perf_evlist *evlist, | ||
| 253 | struct switch_tracking *switch_tracking) | ||
| 254 | { | ||
| 255 | union perf_event *event; | ||
| 256 | unsigned pos, cnt = 0; | ||
| 257 | LIST_HEAD(events); | ||
| 258 | struct event_node *events_array, *node; | ||
| 259 | int i, ret; | ||
| 260 | |||
| 261 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
| 262 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
| 263 | cnt += 1; | ||
| 264 | ret = add_event(evlist, &events, event); | ||
| 265 | perf_evlist__mmap_consume(evlist, i); | ||
| 266 | if (ret < 0) | ||
| 267 | goto out_free_nodes; | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | events_array = calloc(cnt, sizeof(struct event_node)); | ||
| 272 | if (!events_array) { | ||
| 273 | pr_debug("calloc failed\n"); | ||
| 274 | ret = -1; | ||
| 275 | goto out_free_nodes; | ||
| 276 | } | ||
| 277 | |||
| 278 | pos = 0; | ||
| 279 | list_for_each_entry(node, &events, list) | ||
| 280 | events_array[pos++] = *node; | ||
| 281 | |||
| 282 | qsort(events_array, cnt, sizeof(struct event_node), compar); | ||
| 283 | |||
| 284 | for (pos = 0; pos < cnt; pos++) { | ||
| 285 | ret = process_event(evlist, events_array[pos].event, | ||
| 286 | switch_tracking); | ||
| 287 | if (ret < 0) | ||
| 288 | goto out_free; | ||
| 289 | } | ||
| 290 | |||
| 291 | ret = 0; | ||
| 292 | out_free: | ||
| 293 | pr_debug("%u events recorded\n", cnt); | ||
| 294 | free(events_array); | ||
| 295 | out_free_nodes: | ||
| 296 | free_event_nodes(&events); | ||
| 297 | return ret; | ||
| 298 | } | ||
| 299 | |||
| 300 | /** | ||
| 301 | * test__switch_tracking - test using sched_switch and tracking events. | ||
| 302 | * | ||
| 303 | * This function implements a test that checks that sched_switch events and | ||
| 304 | * tracking events can be recorded for a workload (current process) using the | ||
| 305 | * evsel->system_wide and evsel->tracking flags (respectively) with other events | ||
| 306 | * sometimes enabled or disabled. | ||
| 307 | */ | ||
| 308 | int test__switch_tracking(void) | ||
| 309 | { | ||
| 310 | const char *sched_switch = "sched:sched_switch"; | ||
| 311 | struct switch_tracking switch_tracking = { .tids = NULL, }; | ||
| 312 | struct record_opts opts = { | ||
| 313 | .mmap_pages = UINT_MAX, | ||
| 314 | .user_freq = UINT_MAX, | ||
| 315 | .user_interval = ULLONG_MAX, | ||
| 316 | .freq = 4000, | ||
| 317 | .target = { | ||
| 318 | .uses_mmap = true, | ||
| 319 | }, | ||
| 320 | }; | ||
| 321 | struct thread_map *threads = NULL; | ||
| 322 | struct cpu_map *cpus = NULL; | ||
| 323 | struct perf_evlist *evlist = NULL; | ||
| 324 | struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel; | ||
| 325 | struct perf_evsel *switch_evsel, *tracking_evsel; | ||
| 326 | const char *comm; | ||
| 327 | int err = -1; | ||
| 328 | |||
| 329 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
| 330 | if (!threads) { | ||
| 331 | pr_debug("thread_map__new failed!\n"); | ||
| 332 | goto out_err; | ||
| 333 | } | ||
| 334 | |||
| 335 | cpus = cpu_map__new(NULL); | ||
| 336 | if (!cpus) { | ||
| 337 | pr_debug("cpu_map__new failed!\n"); | ||
| 338 | goto out_err; | ||
| 339 | } | ||
| 340 | |||
| 341 | evlist = perf_evlist__new(); | ||
| 342 | if (!evlist) { | ||
| 343 | pr_debug("perf_evlist__new failed!\n"); | ||
| 344 | goto out_err; | ||
| 345 | } | ||
| 346 | |||
| 347 | perf_evlist__set_maps(evlist, cpus, threads); | ||
| 348 | |||
| 349 | /* First event */ | ||
| 350 | err = parse_events(evlist, "cpu-clock:u"); | ||
| 351 | if (err) { | ||
| 352 | pr_debug("Failed to parse event dummy:u\n"); | ||
| 353 | goto out_err; | ||
| 354 | } | ||
| 355 | |||
| 356 | cpu_clocks_evsel = perf_evlist__last(evlist); | ||
| 357 | |||
| 358 | /* Second event */ | ||
| 359 | err = parse_events(evlist, "cycles:u"); | ||
| 360 | if (err) { | ||
| 361 | pr_debug("Failed to parse event cycles:u\n"); | ||
| 362 | goto out_err; | ||
| 363 | } | ||
| 364 | |||
| 365 | cycles_evsel = perf_evlist__last(evlist); | ||
| 366 | |||
| 367 | /* Third event */ | ||
| 368 | if (!perf_evlist__can_select_event(evlist, sched_switch)) { | ||
| 369 | fprintf(stderr, " (no sched_switch)"); | ||
| 370 | err = 0; | ||
| 371 | goto out; | ||
| 372 | } | ||
| 373 | |||
| 374 | err = parse_events(evlist, sched_switch); | ||
| 375 | if (err) { | ||
| 376 | pr_debug("Failed to parse event %s\n", sched_switch); | ||
| 377 | goto out_err; | ||
| 378 | } | ||
| 379 | |||
| 380 | switch_evsel = perf_evlist__last(evlist); | ||
| 381 | |||
| 382 | perf_evsel__set_sample_bit(switch_evsel, CPU); | ||
| 383 | perf_evsel__set_sample_bit(switch_evsel, TIME); | ||
| 384 | |||
| 385 | switch_evsel->system_wide = true; | ||
| 386 | switch_evsel->no_aux_samples = true; | ||
| 387 | switch_evsel->immediate = true; | ||
| 388 | |||
| 389 | /* Test moving an event to the front */ | ||
| 390 | if (cycles_evsel == perf_evlist__first(evlist)) { | ||
| 391 | pr_debug("cycles event already at front"); | ||
| 392 | goto out_err; | ||
| 393 | } | ||
| 394 | perf_evlist__to_front(evlist, cycles_evsel); | ||
| 395 | if (cycles_evsel != perf_evlist__first(evlist)) { | ||
| 396 | pr_debug("Failed to move cycles event to front"); | ||
| 397 | goto out_err; | ||
| 398 | } | ||
| 399 | |||
| 400 | perf_evsel__set_sample_bit(cycles_evsel, CPU); | ||
| 401 | perf_evsel__set_sample_bit(cycles_evsel, TIME); | ||
| 402 | |||
| 403 | /* Fourth event */ | ||
| 404 | err = parse_events(evlist, "dummy:u"); | ||
| 405 | if (err) { | ||
| 406 | pr_debug("Failed to parse event dummy:u\n"); | ||
| 407 | goto out_err; | ||
| 408 | } | ||
| 409 | |||
| 410 | tracking_evsel = perf_evlist__last(evlist); | ||
| 411 | |||
| 412 | perf_evlist__set_tracking_event(evlist, tracking_evsel); | ||
| 413 | |||
| 414 | tracking_evsel->attr.freq = 0; | ||
| 415 | tracking_evsel->attr.sample_period = 1; | ||
| 416 | |||
| 417 | perf_evsel__set_sample_bit(tracking_evsel, TIME); | ||
| 418 | |||
| 419 | /* Config events */ | ||
| 420 | perf_evlist__config(evlist, &opts); | ||
| 421 | |||
| 422 | /* Check moved event is still at the front */ | ||
| 423 | if (cycles_evsel != perf_evlist__first(evlist)) { | ||
| 424 | pr_debug("Front event no longer at front"); | ||
| 425 | goto out_err; | ||
| 426 | } | ||
| 427 | |||
| 428 | /* Check tracking event is tracking */ | ||
| 429 | if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) { | ||
| 430 | pr_debug("Tracking event not tracking\n"); | ||
| 431 | goto out_err; | ||
| 432 | } | ||
| 433 | |||
| 434 | /* Check non-tracking events are not tracking */ | ||
| 435 | evlist__for_each(evlist, evsel) { | ||
| 436 | if (evsel != tracking_evsel) { | ||
| 437 | if (evsel->attr.mmap || evsel->attr.comm) { | ||
| 438 | pr_debug("Non-tracking event is tracking\n"); | ||
| 439 | goto out_err; | ||
| 440 | } | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | if (perf_evlist__open(evlist) < 0) { | ||
| 445 | fprintf(stderr, " (not supported)"); | ||
| 446 | err = 0; | ||
| 447 | goto out; | ||
| 448 | } | ||
| 449 | |||
| 450 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | ||
| 451 | if (err) { | ||
| 452 | pr_debug("perf_evlist__mmap failed!\n"); | ||
| 453 | goto out_err; | ||
| 454 | } | ||
| 455 | |||
| 456 | perf_evlist__enable(evlist); | ||
| 457 | |||
| 458 | err = perf_evlist__disable_event(evlist, cpu_clocks_evsel); | ||
| 459 | if (err) { | ||
| 460 | pr_debug("perf_evlist__disable_event failed!\n"); | ||
| 461 | goto out_err; | ||
| 462 | } | ||
| 463 | |||
| 464 | err = spin_sleep(); | ||
| 465 | if (err) { | ||
| 466 | pr_debug("spin_sleep failed!\n"); | ||
| 467 | goto out_err; | ||
| 468 | } | ||
| 469 | |||
| 470 | comm = "Test COMM 1"; | ||
| 471 | err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0); | ||
| 472 | if (err) { | ||
| 473 | pr_debug("PR_SET_NAME failed!\n"); | ||
| 474 | goto out_err; | ||
| 475 | } | ||
| 476 | |||
| 477 | err = perf_evlist__disable_event(evlist, cycles_evsel); | ||
| 478 | if (err) { | ||
| 479 | pr_debug("perf_evlist__disable_event failed!\n"); | ||
| 480 | goto out_err; | ||
| 481 | } | ||
| 482 | |||
| 483 | comm = "Test COMM 2"; | ||
| 484 | err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0); | ||
| 485 | if (err) { | ||
| 486 | pr_debug("PR_SET_NAME failed!\n"); | ||
| 487 | goto out_err; | ||
| 488 | } | ||
| 489 | |||
| 490 | err = spin_sleep(); | ||
| 491 | if (err) { | ||
| 492 | pr_debug("spin_sleep failed!\n"); | ||
| 493 | goto out_err; | ||
| 494 | } | ||
| 495 | |||
| 496 | comm = "Test COMM 3"; | ||
| 497 | err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0); | ||
| 498 | if (err) { | ||
| 499 | pr_debug("PR_SET_NAME failed!\n"); | ||
| 500 | goto out_err; | ||
| 501 | } | ||
| 502 | |||
| 503 | err = perf_evlist__enable_event(evlist, cycles_evsel); | ||
| 504 | if (err) { | ||
| 505 | pr_debug("perf_evlist__disable_event failed!\n"); | ||
| 506 | goto out_err; | ||
| 507 | } | ||
| 508 | |||
| 509 | comm = "Test COMM 4"; | ||
| 510 | err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0); | ||
| 511 | if (err) { | ||
| 512 | pr_debug("PR_SET_NAME failed!\n"); | ||
| 513 | goto out_err; | ||
| 514 | } | ||
| 515 | |||
| 516 | err = spin_sleep(); | ||
| 517 | if (err) { | ||
| 518 | pr_debug("spin_sleep failed!\n"); | ||
| 519 | goto out_err; | ||
| 520 | } | ||
| 521 | |||
| 522 | perf_evlist__disable(evlist); | ||
| 523 | |||
| 524 | switch_tracking.switch_evsel = switch_evsel; | ||
| 525 | switch_tracking.cycles_evsel = cycles_evsel; | ||
| 526 | |||
| 527 | err = process_events(evlist, &switch_tracking); | ||
| 528 | |||
| 529 | zfree(&switch_tracking.tids); | ||
| 530 | |||
| 531 | if (err) | ||
| 532 | goto out_err; | ||
| 533 | |||
| 534 | /* Check all 4 comm events were seen i.e. that evsel->tracking works */ | ||
| 535 | if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] || | ||
| 536 | !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) { | ||
| 537 | pr_debug("Missing comm events\n"); | ||
| 538 | goto out_err; | ||
| 539 | } | ||
| 540 | |||
| 541 | /* Check cycles event got enabled */ | ||
| 542 | if (!switch_tracking.cycles_before_comm_1) { | ||
| 543 | pr_debug("Missing cycles events\n"); | ||
| 544 | goto out_err; | ||
| 545 | } | ||
| 546 | |||
| 547 | /* Check cycles event got disabled */ | ||
| 548 | if (switch_tracking.cycles_between_comm_2_and_comm_3) { | ||
| 549 | pr_debug("cycles events even though event was disabled\n"); | ||
| 550 | goto out_err; | ||
| 551 | } | ||
| 552 | |||
| 553 | /* Check cycles event got enabled again */ | ||
| 554 | if (!switch_tracking.cycles_after_comm_4) { | ||
| 555 | pr_debug("Missing cycles events\n"); | ||
| 556 | goto out_err; | ||
| 557 | } | ||
| 558 | out: | ||
| 559 | if (evlist) { | ||
| 560 | perf_evlist__disable(evlist); | ||
| 561 | perf_evlist__delete(evlist); | ||
| 562 | } else { | ||
| 563 | cpu_map__delete(cpus); | ||
| 564 | thread_map__delete(threads); | ||
| 565 | } | ||
| 566 | |||
| 567 | return err; | ||
| 568 | |||
| 569 | out_err: | ||
| 570 | err = -1; | ||
| 571 | goto out; | ||
| 572 | } | ||
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 5ff3db318f12..3a8fedef83bc 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c | |||
| @@ -42,6 +42,7 @@ int test__task_exit(void) | |||
| 42 | .uses_mmap = true, | 42 | .uses_mmap = true, |
| 43 | }; | 43 | }; |
| 44 | const char *argv[] = { "true", NULL }; | 44 | const char *argv[] = { "true", NULL }; |
| 45 | char sbuf[STRERR_BUFSIZE]; | ||
| 45 | 46 | ||
| 46 | signal(SIGCHLD, sig_handler); | 47 | signal(SIGCHLD, sig_handler); |
| 47 | 48 | ||
| @@ -82,13 +83,14 @@ int test__task_exit(void) | |||
| 82 | 83 | ||
| 83 | err = perf_evlist__open(evlist); | 84 | err = perf_evlist__open(evlist); |
| 84 | if (err < 0) { | 85 | if (err < 0) { |
| 85 | pr_debug("Couldn't open the evlist: %s\n", strerror(-err)); | 86 | pr_debug("Couldn't open the evlist: %s\n", |
| 87 | strerror_r(-err, sbuf, sizeof(sbuf))); | ||
| 86 | goto out_delete_evlist; | 88 | goto out_delete_evlist; |
| 87 | } | 89 | } |
| 88 | 90 | ||
| 89 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | 91 | if (perf_evlist__mmap(evlist, 128, true) < 0) { |
| 90 | pr_debug("failed to mmap events: %d (%s)\n", errno, | 92 | pr_debug("failed to mmap events: %d (%s)\n", errno, |
| 91 | strerror(errno)); | 93 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 92 | goto out_delete_evlist; | 94 | goto out_delete_evlist; |
| 93 | } | 95 | } |
| 94 | 96 | ||
| @@ -103,7 +105,7 @@ retry: | |||
| 103 | } | 105 | } |
| 104 | 106 | ||
| 105 | if (!exited || !nr_exit) { | 107 | if (!exited || !nr_exit) { |
| 106 | poll(evlist->pollfd, evlist->nr_fds, -1); | 108 | perf_evlist__poll(evlist, -1); |
| 107 | goto retry; | 109 | goto retry; |
| 108 | } | 110 | } |
| 109 | 111 | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index ed64790a395f..00e776a87a9c 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
| @@ -48,6 +48,9 @@ int test__mmap_thread_lookup(void); | |||
| 48 | int test__thread_mg_share(void); | 48 | int test__thread_mg_share(void); |
| 49 | int test__hists_output(void); | 49 | int test__hists_output(void); |
| 50 | int test__hists_cumulate(void); | 50 | int test__hists_cumulate(void); |
| 51 | int test__switch_tracking(void); | ||
| 52 | int test__fdarray__filter(void); | ||
| 53 | int test__fdarray__add(void); | ||
| 51 | 54 | ||
| 52 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) | 55 | #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) |
| 53 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 56 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a94b11fc5e00..8f60a970404f 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "../../util/pstack.h" | 10 | #include "../../util/pstack.h" |
| 11 | #include "../../util/sort.h" | 11 | #include "../../util/sort.h" |
| 12 | #include "../../util/util.h" | 12 | #include "../../util/util.h" |
| 13 | #include "../../util/top.h" | ||
| 13 | #include "../../arch/common.h" | 14 | #include "../../arch/common.h" |
| 14 | 15 | ||
| 15 | #include "../browser.h" | 16 | #include "../browser.h" |
| @@ -228,8 +229,10 @@ static void callchain_node__init_have_children(struct callchain_node *node) | |||
| 228 | { | 229 | { |
| 229 | struct callchain_list *chain; | 230 | struct callchain_list *chain; |
| 230 | 231 | ||
| 231 | list_for_each_entry(chain, &node->val, list) | 232 | if (!list_empty(&node->val)) { |
| 233 | chain = list_entry(node->val.prev, struct callchain_list, list); | ||
| 232 | chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); | 234 | chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); |
| 235 | } | ||
| 233 | 236 | ||
| 234 | callchain_node__init_have_children_rb_tree(node); | 237 | callchain_node__init_have_children_rb_tree(node); |
| 235 | } | 238 | } |
| @@ -474,26 +477,87 @@ static char *callchain_list__sym_name(struct callchain_list *cl, | |||
| 474 | return bf; | 477 | return bf; |
| 475 | } | 478 | } |
| 476 | 479 | ||
| 480 | struct callchain_print_arg { | ||
| 481 | /* for hists browser */ | ||
| 482 | off_t row_offset; | ||
| 483 | bool is_current_entry; | ||
| 484 | |||
| 485 | /* for file dump */ | ||
| 486 | FILE *fp; | ||
| 487 | int printed; | ||
| 488 | }; | ||
| 489 | |||
| 490 | typedef void (*print_callchain_entry_fn)(struct hist_browser *browser, | ||
| 491 | struct callchain_list *chain, | ||
| 492 | const char *str, int offset, | ||
| 493 | unsigned short row, | ||
| 494 | struct callchain_print_arg *arg); | ||
| 495 | |||
| 496 | static void hist_browser__show_callchain_entry(struct hist_browser *browser, | ||
| 497 | struct callchain_list *chain, | ||
| 498 | const char *str, int offset, | ||
| 499 | unsigned short row, | ||
| 500 | struct callchain_print_arg *arg) | ||
| 501 | { | ||
| 502 | int color, width; | ||
| 503 | char folded_sign = callchain_list__folded(chain); | ||
| 504 | |||
| 505 | color = HE_COLORSET_NORMAL; | ||
| 506 | width = browser->b.width - (offset + 2); | ||
| 507 | if (ui_browser__is_current_entry(&browser->b, row)) { | ||
| 508 | browser->selection = &chain->ms; | ||
| 509 | color = HE_COLORSET_SELECTED; | ||
| 510 | arg->is_current_entry = true; | ||
| 511 | } | ||
| 512 | |||
| 513 | ui_browser__set_color(&browser->b, color); | ||
| 514 | hist_browser__gotorc(browser, row, 0); | ||
| 515 | slsmg_write_nstring(" ", offset); | ||
| 516 | slsmg_printf("%c ", folded_sign); | ||
| 517 | slsmg_write_nstring(str, width); | ||
| 518 | } | ||
| 519 | |||
| 520 | static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, | ||
| 521 | struct callchain_list *chain, | ||
| 522 | const char *str, int offset, | ||
| 523 | unsigned short row __maybe_unused, | ||
| 524 | struct callchain_print_arg *arg) | ||
| 525 | { | ||
| 526 | char folded_sign = callchain_list__folded(chain); | ||
| 527 | |||
| 528 | arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ", | ||
| 529 | folded_sign, str); | ||
| 530 | } | ||
| 531 | |||
| 532 | typedef bool (*check_output_full_fn)(struct hist_browser *browser, | ||
| 533 | unsigned short row); | ||
| 534 | |||
| 535 | static bool hist_browser__check_output_full(struct hist_browser *browser, | ||
| 536 | unsigned short row) | ||
| 537 | { | ||
| 538 | return browser->b.rows == row; | ||
| 539 | } | ||
| 540 | |||
| 541 | static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, | ||
| 542 | unsigned short row __maybe_unused) | ||
| 543 | { | ||
| 544 | return false; | ||
| 545 | } | ||
| 546 | |||
| 477 | #define LEVEL_OFFSET_STEP 3 | 547 | #define LEVEL_OFFSET_STEP 3 |
| 478 | 548 | ||
| 479 | static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser, | 549 | static int hist_browser__show_callchain(struct hist_browser *browser, |
| 480 | struct callchain_node *chain_node, | 550 | struct rb_root *root, int level, |
| 481 | u64 total, int level, | 551 | unsigned short row, u64 total, |
| 482 | unsigned short row, | 552 | print_callchain_entry_fn print, |
| 483 | off_t *row_offset, | 553 | struct callchain_print_arg *arg, |
| 484 | bool *is_current_entry) | 554 | check_output_full_fn is_output_full) |
| 485 | { | 555 | { |
| 486 | struct rb_node *node; | 556 | struct rb_node *node; |
| 487 | int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; | 557 | int first_row = row, offset = level * LEVEL_OFFSET_STEP; |
| 488 | u64 new_total, remaining; | 558 | u64 new_total; |
| 489 | 559 | ||
| 490 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 560 | node = rb_first(root); |
| 491 | new_total = chain_node->children_hit; | ||
| 492 | else | ||
| 493 | new_total = total; | ||
| 494 | |||
| 495 | remaining = new_total; | ||
| 496 | node = rb_first(&chain_node->rb_root); | ||
| 497 | while (node) { | 561 | while (node) { |
| 498 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 562 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
| 499 | struct rb_node *next = rb_next(node); | 563 | struct rb_node *next = rb_next(node); |
| @@ -503,30 +567,28 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse | |||
| 503 | int first = true; | 567 | int first = true; |
| 504 | int extra_offset = 0; | 568 | int extra_offset = 0; |
| 505 | 569 | ||
| 506 | remaining -= cumul; | ||
| 507 | |||
| 508 | list_for_each_entry(chain, &child->val, list) { | 570 | list_for_each_entry(chain, &child->val, list) { |
| 509 | char bf[1024], *alloc_str; | 571 | char bf[1024], *alloc_str; |
| 510 | const char *str; | 572 | const char *str; |
| 511 | int color; | ||
| 512 | bool was_first = first; | 573 | bool was_first = first; |
| 513 | 574 | ||
| 514 | if (first) | 575 | if (first) |
| 515 | first = false; | 576 | first = false; |
| 516 | else | 577 | else if (level > 1) |
| 517 | extra_offset = LEVEL_OFFSET_STEP; | 578 | extra_offset = LEVEL_OFFSET_STEP; |
| 518 | 579 | ||
| 519 | folded_sign = callchain_list__folded(chain); | 580 | folded_sign = callchain_list__folded(chain); |
| 520 | if (*row_offset != 0) { | 581 | if (arg->row_offset != 0) { |
| 521 | --*row_offset; | 582 | arg->row_offset--; |
| 522 | goto do_next; | 583 | goto do_next; |
| 523 | } | 584 | } |
| 524 | 585 | ||
| 525 | alloc_str = NULL; | 586 | alloc_str = NULL; |
| 526 | str = callchain_list__sym_name(chain, bf, sizeof(bf), | 587 | str = callchain_list__sym_name(chain, bf, sizeof(bf), |
| 527 | browser->show_dso); | 588 | browser->show_dso); |
| 528 | if (was_first) { | 589 | |
| 529 | double percent = cumul * 100.0 / new_total; | 590 | if (was_first && level > 1) { |
| 591 | double percent = cumul * 100.0 / total; | ||
| 530 | 592 | ||
| 531 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | 593 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) |
| 532 | str = "Not enough memory!"; | 594 | str = "Not enough memory!"; |
| @@ -534,22 +596,11 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse | |||
| 534 | str = alloc_str; | 596 | str = alloc_str; |
| 535 | } | 597 | } |
| 536 | 598 | ||
| 537 | color = HE_COLORSET_NORMAL; | 599 | print(browser, chain, str, offset + extra_offset, row, arg); |
| 538 | width = browser->b.width - (offset + extra_offset + 2); | ||
| 539 | if (ui_browser__is_current_entry(&browser->b, row)) { | ||
| 540 | browser->selection = &chain->ms; | ||
| 541 | color = HE_COLORSET_SELECTED; | ||
| 542 | *is_current_entry = true; | ||
| 543 | } | ||
| 544 | 600 | ||
| 545 | ui_browser__set_color(&browser->b, color); | ||
| 546 | hist_browser__gotorc(browser, row, 0); | ||
| 547 | slsmg_write_nstring(" ", offset + extra_offset); | ||
| 548 | slsmg_printf("%c ", folded_sign); | ||
| 549 | slsmg_write_nstring(str, width); | ||
| 550 | free(alloc_str); | 601 | free(alloc_str); |
| 551 | 602 | ||
| 552 | if (++row == browser->b.rows) | 603 | if (is_output_full(browser, ++row)) |
| 553 | goto out; | 604 | goto out; |
| 554 | do_next: | 605 | do_next: |
| 555 | if (folded_sign == '+') | 606 | if (folded_sign == '+') |
| @@ -558,89 +609,21 @@ do_next: | |||
| 558 | 609 | ||
| 559 | if (folded_sign == '-') { | 610 | if (folded_sign == '-') { |
| 560 | const int new_level = level + (extra_offset ? 2 : 1); | 611 | const int new_level = level + (extra_offset ? 2 : 1); |
| 561 | row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total, | ||
| 562 | new_level, row, row_offset, | ||
| 563 | is_current_entry); | ||
| 564 | } | ||
| 565 | if (row == browser->b.rows) | ||
| 566 | goto out; | ||
| 567 | node = next; | ||
| 568 | } | ||
| 569 | out: | ||
| 570 | return row - first_row; | ||
| 571 | } | ||
| 572 | |||
| 573 | static int hist_browser__show_callchain_node(struct hist_browser *browser, | ||
| 574 | struct callchain_node *node, | ||
| 575 | int level, unsigned short row, | ||
| 576 | off_t *row_offset, | ||
| 577 | bool *is_current_entry) | ||
| 578 | { | ||
| 579 | struct callchain_list *chain; | ||
| 580 | int first_row = row, | ||
| 581 | offset = level * LEVEL_OFFSET_STEP, | ||
| 582 | width = browser->b.width - offset; | ||
| 583 | char folded_sign = ' '; | ||
| 584 | 612 | ||
| 585 | list_for_each_entry(chain, &node->val, list) { | 613 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
| 586 | char bf[1024], *s; | 614 | new_total = child->children_hit; |
| 587 | int color; | 615 | else |
| 588 | 616 | new_total = total; | |
| 589 | folded_sign = callchain_list__folded(chain); | ||
| 590 | |||
| 591 | if (*row_offset != 0) { | ||
| 592 | --*row_offset; | ||
| 593 | continue; | ||
| 594 | } | ||
| 595 | 617 | ||
| 596 | color = HE_COLORSET_NORMAL; | 618 | row += hist_browser__show_callchain(browser, &child->rb_root, |
| 597 | if (ui_browser__is_current_entry(&browser->b, row)) { | 619 | new_level, row, new_total, |
| 598 | browser->selection = &chain->ms; | 620 | print, arg, is_output_full); |
| 599 | color = HE_COLORSET_SELECTED; | ||
| 600 | *is_current_entry = true; | ||
| 601 | } | 621 | } |
| 602 | 622 | if (is_output_full(browser, row)) | |
| 603 | s = callchain_list__sym_name(chain, bf, sizeof(bf), | ||
| 604 | browser->show_dso); | ||
| 605 | hist_browser__gotorc(browser, row, 0); | ||
| 606 | ui_browser__set_color(&browser->b, color); | ||
| 607 | slsmg_write_nstring(" ", offset); | ||
| 608 | slsmg_printf("%c ", folded_sign); | ||
| 609 | slsmg_write_nstring(s, width - 2); | ||
| 610 | |||
| 611 | if (++row == browser->b.rows) | ||
| 612 | goto out; | ||
| 613 | } | ||
| 614 | |||
| 615 | if (folded_sign == '-') | ||
| 616 | row += hist_browser__show_callchain_node_rb_tree(browser, node, | ||
| 617 | browser->hists->stats.total_period, | ||
| 618 | level + 1, row, | ||
| 619 | row_offset, | ||
| 620 | is_current_entry); | ||
| 621 | out: | ||
| 622 | return row - first_row; | ||
| 623 | } | ||
| 624 | |||
| 625 | static int hist_browser__show_callchain(struct hist_browser *browser, | ||
| 626 | struct rb_root *chain, | ||
| 627 | int level, unsigned short row, | ||
| 628 | off_t *row_offset, | ||
| 629 | bool *is_current_entry) | ||
| 630 | { | ||
| 631 | struct rb_node *nd; | ||
| 632 | int first_row = row; | ||
| 633 | |||
| 634 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | ||
| 635 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
| 636 | |||
| 637 | row += hist_browser__show_callchain_node(browser, node, level, | ||
| 638 | row, row_offset, | ||
| 639 | is_current_entry); | ||
| 640 | if (row == browser->b.rows) | ||
| 641 | break; | 623 | break; |
| 624 | node = next; | ||
| 642 | } | 625 | } |
| 643 | 626 | out: | |
| 644 | return row - first_row; | 627 | return row - first_row; |
| 645 | } | 628 | } |
| 646 | 629 | ||
| @@ -653,17 +636,18 @@ struct hpp_arg { | |||
| 653 | static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) | 636 | static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) |
| 654 | { | 637 | { |
| 655 | struct hpp_arg *arg = hpp->ptr; | 638 | struct hpp_arg *arg = hpp->ptr; |
| 656 | int ret; | 639 | int ret, len; |
| 657 | va_list args; | 640 | va_list args; |
| 658 | double percent; | 641 | double percent; |
| 659 | 642 | ||
| 660 | va_start(args, fmt); | 643 | va_start(args, fmt); |
| 644 | len = va_arg(args, int); | ||
| 661 | percent = va_arg(args, double); | 645 | percent = va_arg(args, double); |
| 662 | va_end(args); | 646 | va_end(args); |
| 663 | 647 | ||
| 664 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); | 648 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); |
| 665 | 649 | ||
| 666 | ret = scnprintf(hpp->buf, hpp->size, fmt, percent); | 650 | ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); |
| 667 | slsmg_printf("%s", hpp->buf); | 651 | slsmg_printf("%s", hpp->buf); |
| 668 | 652 | ||
| 669 | advance_hpp(hpp, ret); | 653 | advance_hpp(hpp, ret); |
| @@ -677,12 +661,12 @@ static u64 __hpp_get_##_field(struct hist_entry *he) \ | |||
| 677 | } \ | 661 | } \ |
| 678 | \ | 662 | \ |
| 679 | static int \ | 663 | static int \ |
| 680 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ | 664 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
| 681 | struct perf_hpp *hpp, \ | 665 | struct perf_hpp *hpp, \ |
| 682 | struct hist_entry *he) \ | 666 | struct hist_entry *he) \ |
| 683 | { \ | 667 | { \ |
| 684 | return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \ | 668 | return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ |
| 685 | __hpp__slsmg_color_printf, true); \ | 669 | __hpp__slsmg_color_printf, true); \ |
| 686 | } | 670 | } |
| 687 | 671 | ||
| 688 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ | 672 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ |
| @@ -692,18 +676,20 @@ static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ | |||
| 692 | } \ | 676 | } \ |
| 693 | \ | 677 | \ |
| 694 | static int \ | 678 | static int \ |
| 695 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ | 679 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
| 696 | struct perf_hpp *hpp, \ | 680 | struct perf_hpp *hpp, \ |
| 697 | struct hist_entry *he) \ | 681 | struct hist_entry *he) \ |
| 698 | { \ | 682 | { \ |
| 699 | if (!symbol_conf.cumulate_callchain) { \ | 683 | if (!symbol_conf.cumulate_callchain) { \ |
| 700 | int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \ | 684 | int len = fmt->user_len ?: fmt->len; \ |
| 685 | int ret = scnprintf(hpp->buf, hpp->size, \ | ||
| 686 | "%*s", len, "N/A"); \ | ||
| 701 | slsmg_printf("%s", hpp->buf); \ | 687 | slsmg_printf("%s", hpp->buf); \ |
| 702 | \ | 688 | \ |
| 703 | return ret; \ | 689 | return ret; \ |
| 704 | } \ | 690 | } \ |
| 705 | return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \ | 691 | return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \ |
| 706 | __hpp__slsmg_color_printf, true); \ | 692 | " %*.2f%%", __hpp__slsmg_color_printf, true); \ |
| 707 | } | 693 | } |
| 708 | 694 | ||
| 709 | __HPP_COLOR_PERCENT_FN(overhead, period) | 695 | __HPP_COLOR_PERCENT_FN(overhead, period) |
| @@ -812,10 +798,18 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
| 812 | --row_offset; | 798 | --row_offset; |
| 813 | 799 | ||
| 814 | if (folded_sign == '-' && row != browser->b.rows) { | 800 | if (folded_sign == '-' && row != browser->b.rows) { |
| 815 | printed += hist_browser__show_callchain(browser, &entry->sorted_chain, | 801 | u64 total = hists__total_period(entry->hists); |
| 816 | 1, row, &row_offset, | 802 | struct callchain_print_arg arg = { |
| 817 | ¤t_entry); | 803 | .row_offset = row_offset, |
| 818 | if (current_entry) | 804 | .is_current_entry = current_entry, |
| 805 | }; | ||
| 806 | |||
| 807 | printed += hist_browser__show_callchain(browser, | ||
| 808 | &entry->sorted_chain, 1, row, total, | ||
| 809 | hist_browser__show_callchain_entry, &arg, | ||
| 810 | hist_browser__check_output_full); | ||
| 811 | |||
| 812 | if (arg.is_current_entry) | ||
| 819 | browser->he_selection = entry; | 813 | browser->he_selection = entry; |
| 820 | } | 814 | } |
| 821 | 815 | ||
| @@ -847,9 +841,6 @@ static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists) | |||
| 847 | if (perf_hpp__should_skip(fmt)) | 841 | if (perf_hpp__should_skip(fmt)) |
| 848 | continue; | 842 | continue; |
| 849 | 843 | ||
| 850 | /* We need to add the length of the columns header. */ | ||
| 851 | perf_hpp__reset_width(fmt, hists); | ||
| 852 | |||
| 853 | ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); | 844 | ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); |
| 854 | if (advance_hpp_check(&dummy_hpp, ret)) | 845 | if (advance_hpp_check(&dummy_hpp, ret)) |
| 855 | break; | 846 | break; |
| @@ -1074,113 +1065,21 @@ do_offset: | |||
| 1074 | } | 1065 | } |
| 1075 | } | 1066 | } |
| 1076 | 1067 | ||
| 1077 | static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser, | ||
| 1078 | struct callchain_node *chain_node, | ||
| 1079 | u64 total, int level, | ||
| 1080 | FILE *fp) | ||
| 1081 | { | ||
| 1082 | struct rb_node *node; | ||
| 1083 | int offset = level * LEVEL_OFFSET_STEP; | ||
| 1084 | u64 new_total, remaining; | ||
| 1085 | int printed = 0; | ||
| 1086 | |||
| 1087 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
| 1088 | new_total = chain_node->children_hit; | ||
| 1089 | else | ||
| 1090 | new_total = total; | ||
| 1091 | |||
| 1092 | remaining = new_total; | ||
| 1093 | node = rb_first(&chain_node->rb_root); | ||
| 1094 | while (node) { | ||
| 1095 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
| 1096 | struct rb_node *next = rb_next(node); | ||
| 1097 | u64 cumul = callchain_cumul_hits(child); | ||
| 1098 | struct callchain_list *chain; | ||
| 1099 | char folded_sign = ' '; | ||
| 1100 | int first = true; | ||
| 1101 | int extra_offset = 0; | ||
| 1102 | |||
| 1103 | remaining -= cumul; | ||
| 1104 | |||
| 1105 | list_for_each_entry(chain, &child->val, list) { | ||
| 1106 | char bf[1024], *alloc_str; | ||
| 1107 | const char *str; | ||
| 1108 | bool was_first = first; | ||
| 1109 | |||
| 1110 | if (first) | ||
| 1111 | first = false; | ||
| 1112 | else | ||
| 1113 | extra_offset = LEVEL_OFFSET_STEP; | ||
| 1114 | |||
| 1115 | folded_sign = callchain_list__folded(chain); | ||
| 1116 | |||
| 1117 | alloc_str = NULL; | ||
| 1118 | str = callchain_list__sym_name(chain, bf, sizeof(bf), | ||
| 1119 | browser->show_dso); | ||
| 1120 | if (was_first) { | ||
| 1121 | double percent = cumul * 100.0 / new_total; | ||
| 1122 | |||
| 1123 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
| 1124 | str = "Not enough memory!"; | ||
| 1125 | else | ||
| 1126 | str = alloc_str; | ||
| 1127 | } | ||
| 1128 | |||
| 1129 | printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str); | ||
| 1130 | free(alloc_str); | ||
| 1131 | if (folded_sign == '+') | ||
| 1132 | break; | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | if (folded_sign == '-') { | ||
| 1136 | const int new_level = level + (extra_offset ? 2 : 1); | ||
| 1137 | printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total, | ||
| 1138 | new_level, fp); | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | node = next; | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | return printed; | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | static int hist_browser__fprintf_callchain_node(struct hist_browser *browser, | ||
| 1148 | struct callchain_node *node, | ||
| 1149 | int level, FILE *fp) | ||
| 1150 | { | ||
| 1151 | struct callchain_list *chain; | ||
| 1152 | int offset = level * LEVEL_OFFSET_STEP; | ||
| 1153 | char folded_sign = ' '; | ||
| 1154 | int printed = 0; | ||
| 1155 | |||
| 1156 | list_for_each_entry(chain, &node->val, list) { | ||
| 1157 | char bf[1024], *s; | ||
| 1158 | |||
| 1159 | folded_sign = callchain_list__folded(chain); | ||
| 1160 | s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso); | ||
| 1161 | printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s); | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | if (folded_sign == '-') | ||
| 1165 | printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node, | ||
| 1166 | browser->hists->stats.total_period, | ||
| 1167 | level + 1, fp); | ||
| 1168 | return printed; | ||
| 1169 | } | ||
| 1170 | |||
| 1171 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, | 1068 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, |
| 1172 | struct rb_root *chain, int level, FILE *fp) | 1069 | struct hist_entry *he, FILE *fp) |
| 1173 | { | 1070 | { |
| 1174 | struct rb_node *nd; | 1071 | u64 total = hists__total_period(he->hists); |
| 1175 | int printed = 0; | 1072 | struct callchain_print_arg arg = { |
| 1073 | .fp = fp, | ||
| 1074 | }; | ||
| 1176 | 1075 | ||
| 1177 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | 1076 | if (symbol_conf.cumulate_callchain) |
| 1178 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | 1077 | total = he->stat_acc->period; |
| 1179 | 1078 | ||
| 1180 | printed += hist_browser__fprintf_callchain_node(browser, node, level, fp); | 1079 | hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total, |
| 1181 | } | 1080 | hist_browser__fprintf_callchain_entry, &arg, |
| 1182 | 1081 | hist_browser__check_dump_full); | |
| 1183 | return printed; | 1082 | return arg.printed; |
| 1184 | } | 1083 | } |
| 1185 | 1084 | ||
| 1186 | static int hist_browser__fprintf_entry(struct hist_browser *browser, | 1085 | static int hist_browser__fprintf_entry(struct hist_browser *browser, |
| @@ -1219,7 +1118,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, | |||
| 1219 | printed += fprintf(fp, "%s\n", rtrim(s)); | 1118 | printed += fprintf(fp, "%s\n", rtrim(s)); |
| 1220 | 1119 | ||
| 1221 | if (folded_sign == '-') | 1120 | if (folded_sign == '-') |
| 1222 | printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp); | 1121 | printed += hist_browser__fprintf_callchain(browser, he, fp); |
| 1223 | 1122 | ||
| 1224 | return printed; | 1123 | return printed; |
| 1225 | } | 1124 | } |
| @@ -1498,6 +1397,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1498 | char buf[64]; | 1397 | char buf[64]; |
| 1499 | char script_opt[64]; | 1398 | char script_opt[64]; |
| 1500 | int delay_secs = hbt ? hbt->refresh : 0; | 1399 | int delay_secs = hbt ? hbt->refresh : 0; |
| 1400 | struct perf_hpp_fmt *fmt; | ||
| 1501 | 1401 | ||
| 1502 | #define HIST_BROWSER_HELP_COMMON \ | 1402 | #define HIST_BROWSER_HELP_COMMON \ |
| 1503 | "h/?/F1 Show this window\n" \ | 1403 | "h/?/F1 Show this window\n" \ |
| @@ -1529,6 +1429,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1529 | "P Print histograms to perf.hist.N\n" | 1429 | "P Print histograms to perf.hist.N\n" |
| 1530 | "t Zoom into current Thread\n" | 1430 | "t Zoom into current Thread\n" |
| 1531 | "V Verbose (DSO names in callchains, etc)\n" | 1431 | "V Verbose (DSO names in callchains, etc)\n" |
| 1432 | "z Toggle zeroing of samples\n" | ||
| 1532 | "/ Filter symbol by name"; | 1433 | "/ Filter symbol by name"; |
| 1533 | 1434 | ||
| 1534 | if (browser == NULL) | 1435 | if (browser == NULL) |
| @@ -1547,6 +1448,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1547 | 1448 | ||
| 1548 | memset(options, 0, sizeof(options)); | 1449 | memset(options, 0, sizeof(options)); |
| 1549 | 1450 | ||
| 1451 | perf_hpp__for_each_format(fmt) | ||
| 1452 | perf_hpp__reset_width(fmt, hists); | ||
| 1453 | |||
| 1454 | if (symbol_conf.col_width_list_str) | ||
| 1455 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); | ||
| 1456 | |||
| 1550 | while (1) { | 1457 | while (1) { |
| 1551 | const struct thread *thread = NULL; | 1458 | const struct thread *thread = NULL; |
| 1552 | const struct dso *dso = NULL; | 1459 | const struct dso *dso = NULL; |
| @@ -1623,6 +1530,13 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1623 | case 'F': | 1530 | case 'F': |
| 1624 | symbol_conf.filter_relative ^= 1; | 1531 | symbol_conf.filter_relative ^= 1; |
| 1625 | continue; | 1532 | continue; |
| 1533 | case 'z': | ||
| 1534 | if (!is_report_browser(hbt)) { | ||
| 1535 | struct perf_top *top = hbt->arg; | ||
| 1536 | |||
| 1537 | top->zero = !top->zero; | ||
| 1538 | } | ||
| 1539 | continue; | ||
| 1626 | case K_F1: | 1540 | case K_F1: |
| 1627 | case 'h': | 1541 | case 'h': |
| 1628 | case '?': | 1542 | case '?': |
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 6ca60e482cdc..f3fa4258b256 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...) | 11 | static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...) |
| 12 | { | 12 | { |
| 13 | int ret = 0; | 13 | int ret = 0; |
| 14 | int len; | ||
| 14 | va_list args; | 15 | va_list args; |
| 15 | double percent; | 16 | double percent; |
| 16 | const char *markup; | 17 | const char *markup; |
| @@ -18,6 +19,7 @@ static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...) | |||
| 18 | size_t size = hpp->size; | 19 | size_t size = hpp->size; |
| 19 | 20 | ||
| 20 | va_start(args, fmt); | 21 | va_start(args, fmt); |
| 22 | len = va_arg(args, int); | ||
| 21 | percent = va_arg(args, double); | 23 | percent = va_arg(args, double); |
| 22 | va_end(args); | 24 | va_end(args); |
| 23 | 25 | ||
| @@ -25,7 +27,7 @@ static int __percent_color_snprintf(struct perf_hpp *hpp, const char *fmt, ...) | |||
| 25 | if (markup) | 27 | if (markup) |
| 26 | ret += scnprintf(buf, size, markup); | 28 | ret += scnprintf(buf, size, markup); |
| 27 | 29 | ||
| 28 | ret += scnprintf(buf + ret, size - ret, fmt, percent); | 30 | ret += scnprintf(buf + ret, size - ret, fmt, len, percent); |
| 29 | 31 | ||
| 30 | if (markup) | 32 | if (markup) |
| 31 | ret += scnprintf(buf + ret, size - ret, "</span>"); | 33 | ret += scnprintf(buf + ret, size - ret, "</span>"); |
| @@ -39,12 +41,12 @@ static u64 he_get_##_field(struct hist_entry *he) \ | |||
| 39 | return he->stat._field; \ | 41 | return he->stat._field; \ |
| 40 | } \ | 42 | } \ |
| 41 | \ | 43 | \ |
| 42 | static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 44 | static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
| 43 | struct perf_hpp *hpp, \ | 45 | struct perf_hpp *hpp, \ |
| 44 | struct hist_entry *he) \ | 46 | struct hist_entry *he) \ |
| 45 | { \ | 47 | { \ |
| 46 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ | 48 | return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ |
| 47 | __percent_color_snprintf, true); \ | 49 | __percent_color_snprintf, true); \ |
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ | 52 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ |
| @@ -57,8 +59,8 @@ static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, | |||
| 57 | struct perf_hpp *hpp, \ | 59 | struct perf_hpp *hpp, \ |
| 58 | struct hist_entry *he) \ | 60 | struct hist_entry *he) \ |
| 59 | { \ | 61 | { \ |
| 60 | return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \ | 62 | return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ |
| 61 | __percent_color_snprintf, true); \ | 63 | __percent_color_snprintf, true); \ |
| 62 | } | 64 | } |
| 63 | 65 | ||
| 64 | __HPP_COLOR_PERCENT_FN(overhead, period) | 66 | __HPP_COLOR_PERCENT_FN(overhead, period) |
| @@ -205,10 +207,8 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
| 205 | if (perf_hpp__is_sort_entry(fmt)) | 207 | if (perf_hpp__is_sort_entry(fmt)) |
| 206 | sym_col = col_idx; | 208 | sym_col = col_idx; |
| 207 | 209 | ||
| 208 | fmt->header(fmt, &hpp, hists_to_evsel(hists)); | ||
| 209 | |||
| 210 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | 210 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), |
| 211 | -1, ltrim(s), | 211 | -1, fmt->name, |
| 212 | renderer, "markup", | 212 | renderer, "markup", |
| 213 | col_idx++, NULL); | 213 | col_idx++, NULL); |
| 214 | } | 214 | } |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 498adb23c02e..2af18376b077 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
| @@ -15,9 +15,9 @@ | |||
| 15 | __ret; \ | 15 | __ret; \ |
| 16 | }) | 16 | }) |
| 17 | 17 | ||
| 18 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | 18 | static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, |
| 19 | hpp_field_fn get_field, const char *fmt, | 19 | hpp_field_fn get_field, const char *fmt, int len, |
| 20 | hpp_snprint_fn print_fn, bool fmt_percent) | 20 | hpp_snprint_fn print_fn, bool fmt_percent) |
| 21 | { | 21 | { |
| 22 | int ret; | 22 | int ret; |
| 23 | struct hists *hists = he->hists; | 23 | struct hists *hists = he->hists; |
| @@ -32,9 +32,9 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
| 32 | if (total) | 32 | if (total) |
| 33 | percent = 100.0 * get_field(he) / total; | 33 | percent = 100.0 * get_field(he) / total; |
| 34 | 34 | ||
| 35 | ret = hpp__call_print_fn(hpp, print_fn, fmt, percent); | 35 | ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent); |
| 36 | } else | 36 | } else |
| 37 | ret = hpp__call_print_fn(hpp, print_fn, fmt, get_field(he)); | 37 | ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he)); |
| 38 | 38 | ||
| 39 | if (perf_evsel__is_group_event(evsel)) { | 39 | if (perf_evsel__is_group_event(evsel)) { |
| 40 | int prev_idx, idx_delta; | 40 | int prev_idx, idx_delta; |
| @@ -60,19 +60,19 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
| 60 | */ | 60 | */ |
| 61 | if (fmt_percent) { | 61 | if (fmt_percent) { |
| 62 | ret += hpp__call_print_fn(hpp, print_fn, | 62 | ret += hpp__call_print_fn(hpp, print_fn, |
| 63 | fmt, 0.0); | 63 | fmt, len, 0.0); |
| 64 | } else { | 64 | } else { |
| 65 | ret += hpp__call_print_fn(hpp, print_fn, | 65 | ret += hpp__call_print_fn(hpp, print_fn, |
| 66 | fmt, 0ULL); | 66 | fmt, len, 0ULL); |
| 67 | } | 67 | } |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | if (fmt_percent) { | 70 | if (fmt_percent) { |
| 71 | ret += hpp__call_print_fn(hpp, print_fn, fmt, | 71 | ret += hpp__call_print_fn(hpp, print_fn, fmt, len, |
| 72 | 100.0 * period / total); | 72 | 100.0 * period / total); |
| 73 | } else { | 73 | } else { |
| 74 | ret += hpp__call_print_fn(hpp, print_fn, fmt, | 74 | ret += hpp__call_print_fn(hpp, print_fn, fmt, |
| 75 | period); | 75 | len, period); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | prev_idx = perf_evsel__group_idx(evsel); | 78 | prev_idx = perf_evsel__group_idx(evsel); |
| @@ -86,10 +86,10 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
| 86 | */ | 86 | */ |
| 87 | if (fmt_percent) { | 87 | if (fmt_percent) { |
| 88 | ret += hpp__call_print_fn(hpp, print_fn, | 88 | ret += hpp__call_print_fn(hpp, print_fn, |
| 89 | fmt, 0.0); | 89 | fmt, len, 0.0); |
| 90 | } else { | 90 | } else { |
| 91 | ret += hpp__call_print_fn(hpp, print_fn, | 91 | ret += hpp__call_print_fn(hpp, print_fn, |
| 92 | fmt, 0ULL); | 92 | fmt, len, 0ULL); |
| 93 | } | 93 | } |
| 94 | } | 94 | } |
| 95 | } | 95 | } |
| @@ -104,16 +104,35 @@ int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
| 104 | return ret; | 104 | return ret; |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, | 107 | int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 108 | hpp_field_fn get_field, const char *fmt, | 108 | struct hist_entry *he, hpp_field_fn get_field, |
| 109 | hpp_snprint_fn print_fn, bool fmt_percent) | 109 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent) |
| 110 | { | ||
| 111 | int len = fmt->user_len ?: fmt->len; | ||
| 112 | |||
| 113 | if (symbol_conf.field_sep) { | ||
| 114 | return __hpp__fmt(hpp, he, get_field, fmtstr, 1, | ||
| 115 | print_fn, fmt_percent); | ||
| 116 | } | ||
| 117 | |||
| 118 | if (fmt_percent) | ||
| 119 | len -= 2; /* 2 for a space and a % sign */ | ||
| 120 | else | ||
| 121 | len -= 1; | ||
| 122 | |||
| 123 | return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent); | ||
| 124 | } | ||
| 125 | |||
| 126 | int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | ||
| 127 | struct hist_entry *he, hpp_field_fn get_field, | ||
| 128 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent) | ||
| 110 | { | 129 | { |
| 111 | if (!symbol_conf.cumulate_callchain) { | 130 | if (!symbol_conf.cumulate_callchain) { |
| 112 | return snprintf(hpp->buf, hpp->size, "%*s", | 131 | int len = fmt->user_len ?: fmt->len; |
| 113 | fmt_percent ? 8 : 12, "N/A"); | 132 | return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A"); |
| 114 | } | 133 | } |
| 115 | 134 | ||
| 116 | return __hpp__fmt(hpp, he, get_field, fmt, print_fn, fmt_percent); | 135 | return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent); |
| 117 | } | 136 | } |
| 118 | 137 | ||
| 119 | static int field_cmp(u64 field_a, u64 field_b) | 138 | static int field_cmp(u64 field_a, u64 field_b) |
| @@ -190,30 +209,26 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, | |||
| 190 | return ret; | 209 | return ret; |
| 191 | } | 210 | } |
| 192 | 211 | ||
| 193 | #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | 212 | static int hpp__width_fn(struct perf_hpp_fmt *fmt, |
| 194 | static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 213 | struct perf_hpp *hpp __maybe_unused, |
| 195 | struct perf_hpp *hpp, \ | 214 | struct perf_evsel *evsel) |
| 196 | struct perf_evsel *evsel) \ | 215 | { |
| 197 | { \ | 216 | int len = fmt->user_len ?: fmt->len; |
| 198 | int len = _min_width; \ | 217 | |
| 199 | \ | 218 | if (symbol_conf.event_group) |
| 200 | if (symbol_conf.event_group) \ | 219 | len = max(len, evsel->nr_members * fmt->len); |
| 201 | len = max(len, evsel->nr_members * _unit_width); \ | 220 | |
| 202 | \ | 221 | if (len < (int)strlen(fmt->name)) |
| 203 | return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \ | 222 | len = strlen(fmt->name); |
| 204 | } | 223 | |
| 205 | 224 | return len; | |
| 206 | #define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | 225 | } |
| 207 | static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 226 | |
| 208 | struct perf_hpp *hpp __maybe_unused, \ | 227 | static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 209 | struct perf_evsel *evsel) \ | 228 | struct perf_evsel *evsel) |
| 210 | { \ | 229 | { |
| 211 | int len = _min_width; \ | 230 | int len = hpp__width_fn(fmt, hpp, evsel); |
| 212 | \ | 231 | return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); |
| 213 | if (symbol_conf.event_group) \ | ||
| 214 | len = max(len, evsel->nr_members * _unit_width); \ | ||
| 215 | \ | ||
| 216 | return len; \ | ||
| 217 | } | 232 | } |
| 218 | 233 | ||
| 219 | static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) | 234 | static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) |
| @@ -221,11 +236,12 @@ static int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) | |||
| 221 | va_list args; | 236 | va_list args; |
| 222 | ssize_t ssize = hpp->size; | 237 | ssize_t ssize = hpp->size; |
| 223 | double percent; | 238 | double percent; |
| 224 | int ret; | 239 | int ret, len; |
| 225 | 240 | ||
| 226 | va_start(args, fmt); | 241 | va_start(args, fmt); |
| 242 | len = va_arg(args, int); | ||
| 227 | percent = va_arg(args, double); | 243 | percent = va_arg(args, double); |
| 228 | ret = value_color_snprintf(hpp->buf, hpp->size, fmt, percent); | 244 | ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent); |
| 229 | va_end(args); | 245 | va_end(args); |
| 230 | 246 | ||
| 231 | return (ret >= ssize) ? (ssize - 1) : ret; | 247 | return (ret >= ssize) ? (ssize - 1) : ret; |
| @@ -250,20 +266,19 @@ static u64 he_get_##_field(struct hist_entry *he) \ | |||
| 250 | return he->stat._field; \ | 266 | return he->stat._field; \ |
| 251 | } \ | 267 | } \ |
| 252 | \ | 268 | \ |
| 253 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 269 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ |
| 254 | struct perf_hpp *hpp, struct hist_entry *he) \ | 270 | struct perf_hpp *hpp, struct hist_entry *he) \ |
| 255 | { \ | 271 | { \ |
| 256 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ | 272 | return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ |
| 257 | hpp_color_scnprintf, true); \ | 273 | hpp_color_scnprintf, true); \ |
| 258 | } | 274 | } |
| 259 | 275 | ||
| 260 | #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ | 276 | #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ |
| 261 | static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ | 277 | static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ |
| 262 | struct perf_hpp *hpp, struct hist_entry *he) \ | 278 | struct perf_hpp *hpp, struct hist_entry *he) \ |
| 263 | { \ | 279 | { \ |
| 264 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ | 280 | return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ |
| 265 | return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ | 281 | hpp_entry_scnprintf, true); \ |
| 266 | hpp_entry_scnprintf, true); \ | ||
| 267 | } | 282 | } |
| 268 | 283 | ||
| 269 | #define __HPP_SORT_FN(_type, _field) \ | 284 | #define __HPP_SORT_FN(_type, _field) \ |
| @@ -278,20 +293,19 @@ static u64 he_get_acc_##_field(struct hist_entry *he) \ | |||
| 278 | return he->stat_acc->_field; \ | 293 | return he->stat_acc->_field; \ |
| 279 | } \ | 294 | } \ |
| 280 | \ | 295 | \ |
| 281 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ | 296 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ |
| 282 | struct perf_hpp *hpp, struct hist_entry *he) \ | 297 | struct perf_hpp *hpp, struct hist_entry *he) \ |
| 283 | { \ | 298 | { \ |
| 284 | return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, " %6.2f%%", \ | 299 | return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ |
| 285 | hpp_color_scnprintf, true); \ | 300 | hpp_color_scnprintf, true); \ |
| 286 | } | 301 | } |
| 287 | 302 | ||
| 288 | #define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ | 303 | #define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ |
| 289 | static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ | 304 | static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ |
| 290 | struct perf_hpp *hpp, struct hist_entry *he) \ | 305 | struct perf_hpp *hpp, struct hist_entry *he) \ |
| 291 | { \ | 306 | { \ |
| 292 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ | 307 | return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ |
| 293 | return __hpp__fmt_acc(hpp, he, he_get_acc_##_field, fmt, \ | 308 | hpp_entry_scnprintf, true); \ |
| 294 | hpp_entry_scnprintf, true); \ | ||
| 295 | } | 309 | } |
| 296 | 310 | ||
| 297 | #define __HPP_SORT_ACC_FN(_type, _field) \ | 311 | #define __HPP_SORT_ACC_FN(_type, _field) \ |
| @@ -306,12 +320,11 @@ static u64 he_get_raw_##_field(struct hist_entry *he) \ | |||
| 306 | return he->stat._field; \ | 320 | return he->stat._field; \ |
| 307 | } \ | 321 | } \ |
| 308 | \ | 322 | \ |
| 309 | static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ | 323 | static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ |
| 310 | struct perf_hpp *hpp, struct hist_entry *he) \ | 324 | struct perf_hpp *hpp, struct hist_entry *he) \ |
| 311 | { \ | 325 | { \ |
| 312 | const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ | 326 | return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \ |
| 313 | return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, \ | 327 | hpp_entry_scnprintf, false); \ |
| 314 | hpp_entry_scnprintf, false); \ | ||
| 315 | } | 328 | } |
| 316 | 329 | ||
| 317 | #define __HPP_SORT_RAW_FN(_type, _field) \ | 330 | #define __HPP_SORT_RAW_FN(_type, _field) \ |
| @@ -321,37 +334,29 @@ static int64_t hpp__sort_##_type(struct hist_entry *a, struct hist_entry *b) \ | |||
| 321 | } | 334 | } |
| 322 | 335 | ||
| 323 | 336 | ||
| 324 | #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ | 337 | #define HPP_PERCENT_FNS(_type, _field) \ |
| 325 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
| 326 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
| 327 | __HPP_COLOR_PERCENT_FN(_type, _field) \ | 338 | __HPP_COLOR_PERCENT_FN(_type, _field) \ |
| 328 | __HPP_ENTRY_PERCENT_FN(_type, _field) \ | 339 | __HPP_ENTRY_PERCENT_FN(_type, _field) \ |
| 329 | __HPP_SORT_FN(_type, _field) | 340 | __HPP_SORT_FN(_type, _field) |
| 330 | 341 | ||
| 331 | #define HPP_PERCENT_ACC_FNS(_type, _str, _field, _min_width, _unit_width)\ | 342 | #define HPP_PERCENT_ACC_FNS(_type, _field) \ |
| 332 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
| 333 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
| 334 | __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ | 343 | __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ |
| 335 | __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ | 344 | __HPP_ENTRY_ACC_PERCENT_FN(_type, _field) \ |
| 336 | __HPP_SORT_ACC_FN(_type, _field) | 345 | __HPP_SORT_ACC_FN(_type, _field) |
| 337 | 346 | ||
| 338 | #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ | 347 | #define HPP_RAW_FNS(_type, _field) \ |
| 339 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
| 340 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
| 341 | __HPP_ENTRY_RAW_FN(_type, _field) \ | 348 | __HPP_ENTRY_RAW_FN(_type, _field) \ |
| 342 | __HPP_SORT_RAW_FN(_type, _field) | 349 | __HPP_SORT_RAW_FN(_type, _field) |
| 343 | 350 | ||
| 344 | __HPP_HEADER_FN(overhead_self, "Self", 8, 8) | 351 | HPP_PERCENT_FNS(overhead, period) |
| 345 | 352 | HPP_PERCENT_FNS(overhead_sys, period_sys) | |
| 346 | HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) | 353 | HPP_PERCENT_FNS(overhead_us, period_us) |
| 347 | HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) | 354 | HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys) |
| 348 | HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) | 355 | HPP_PERCENT_FNS(overhead_guest_us, period_guest_us) |
| 349 | HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) | 356 | HPP_PERCENT_ACC_FNS(overhead_acc, period) |
| 350 | HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) | ||
| 351 | HPP_PERCENT_ACC_FNS(overhead_acc, "Children", period, 8, 8) | ||
| 352 | 357 | ||
| 353 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) | 358 | HPP_RAW_FNS(samples, nr_events) |
| 354 | HPP_RAW_FNS(period, "Period", period, 12, 12) | 359 | HPP_RAW_FNS(period, period) |
| 355 | 360 | ||
| 356 | static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, | 361 | static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, |
| 357 | struct hist_entry *b __maybe_unused) | 362 | struct hist_entry *b __maybe_unused) |
| @@ -359,47 +364,50 @@ static int64_t hpp__nop_cmp(struct hist_entry *a __maybe_unused, | |||
| 359 | return 0; | 364 | return 0; |
| 360 | } | 365 | } |
| 361 | 366 | ||
| 362 | #define HPP__COLOR_PRINT_FNS(_name) \ | 367 | #define HPP__COLOR_PRINT_FNS(_name, _fn) \ |
| 363 | { \ | 368 | { \ |
| 364 | .header = hpp__header_ ## _name, \ | 369 | .name = _name, \ |
| 365 | .width = hpp__width_ ## _name, \ | 370 | .header = hpp__header_fn, \ |
| 366 | .color = hpp__color_ ## _name, \ | 371 | .width = hpp__width_fn, \ |
| 367 | .entry = hpp__entry_ ## _name, \ | 372 | .color = hpp__color_ ## _fn, \ |
| 373 | .entry = hpp__entry_ ## _fn, \ | ||
| 368 | .cmp = hpp__nop_cmp, \ | 374 | .cmp = hpp__nop_cmp, \ |
| 369 | .collapse = hpp__nop_cmp, \ | 375 | .collapse = hpp__nop_cmp, \ |
| 370 | .sort = hpp__sort_ ## _name, \ | 376 | .sort = hpp__sort_ ## _fn, \ |
| 371 | } | 377 | } |
| 372 | 378 | ||
| 373 | #define HPP__COLOR_ACC_PRINT_FNS(_name) \ | 379 | #define HPP__COLOR_ACC_PRINT_FNS(_name, _fn) \ |
| 374 | { \ | 380 | { \ |
| 375 | .header = hpp__header_ ## _name, \ | 381 | .name = _name, \ |
| 376 | .width = hpp__width_ ## _name, \ | 382 | .header = hpp__header_fn, \ |
| 377 | .color = hpp__color_ ## _name, \ | 383 | .width = hpp__width_fn, \ |
| 378 | .entry = hpp__entry_ ## _name, \ | 384 | .color = hpp__color_ ## _fn, \ |
| 385 | .entry = hpp__entry_ ## _fn, \ | ||
| 379 | .cmp = hpp__nop_cmp, \ | 386 | .cmp = hpp__nop_cmp, \ |
| 380 | .collapse = hpp__nop_cmp, \ | 387 | .collapse = hpp__nop_cmp, \ |
| 381 | .sort = hpp__sort_ ## _name, \ | 388 | .sort = hpp__sort_ ## _fn, \ |
| 382 | } | 389 | } |
| 383 | 390 | ||
| 384 | #define HPP__PRINT_FNS(_name) \ | 391 | #define HPP__PRINT_FNS(_name, _fn) \ |
| 385 | { \ | 392 | { \ |
| 386 | .header = hpp__header_ ## _name, \ | 393 | .name = _name, \ |
| 387 | .width = hpp__width_ ## _name, \ | 394 | .header = hpp__header_fn, \ |
| 388 | .entry = hpp__entry_ ## _name, \ | 395 | .width = hpp__width_fn, \ |
| 396 | .entry = hpp__entry_ ## _fn, \ | ||
| 389 | .cmp = hpp__nop_cmp, \ | 397 | .cmp = hpp__nop_cmp, \ |
| 390 | .collapse = hpp__nop_cmp, \ | 398 | .collapse = hpp__nop_cmp, \ |
| 391 | .sort = hpp__sort_ ## _name, \ | 399 | .sort = hpp__sort_ ## _fn, \ |
| 392 | } | 400 | } |
| 393 | 401 | ||
| 394 | struct perf_hpp_fmt perf_hpp__format[] = { | 402 | struct perf_hpp_fmt perf_hpp__format[] = { |
| 395 | HPP__COLOR_PRINT_FNS(overhead), | 403 | HPP__COLOR_PRINT_FNS("Overhead", overhead), |
| 396 | HPP__COLOR_PRINT_FNS(overhead_sys), | 404 | HPP__COLOR_PRINT_FNS("sys", overhead_sys), |
| 397 | HPP__COLOR_PRINT_FNS(overhead_us), | 405 | HPP__COLOR_PRINT_FNS("usr", overhead_us), |
| 398 | HPP__COLOR_PRINT_FNS(overhead_guest_sys), | 406 | HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys), |
| 399 | HPP__COLOR_PRINT_FNS(overhead_guest_us), | 407 | HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us), |
| 400 | HPP__COLOR_ACC_PRINT_FNS(overhead_acc), | 408 | HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc), |
| 401 | HPP__PRINT_FNS(samples), | 409 | HPP__PRINT_FNS("Samples", samples), |
| 402 | HPP__PRINT_FNS(period) | 410 | HPP__PRINT_FNS("Period", period) |
| 403 | }; | 411 | }; |
| 404 | 412 | ||
| 405 | LIST_HEAD(perf_hpp__list); | 413 | LIST_HEAD(perf_hpp__list); |
| @@ -444,14 +452,12 @@ void perf_hpp__init(void) | |||
| 444 | /* | 452 | /* |
| 445 | * If user specified field order, no need to setup default fields. | 453 | * If user specified field order, no need to setup default fields. |
| 446 | */ | 454 | */ |
| 447 | if (field_order) | 455 | if (is_strict_order(field_order)) |
| 448 | return; | 456 | return; |
| 449 | 457 | ||
| 450 | if (symbol_conf.cumulate_callchain) { | 458 | if (symbol_conf.cumulate_callchain) { |
| 451 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC); | 459 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_ACC); |
| 452 | 460 | perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self"; | |
| 453 | perf_hpp__format[PERF_HPP__OVERHEAD].header = | ||
| 454 | hpp__header_overhead_self; | ||
| 455 | } | 461 | } |
| 456 | 462 | ||
| 457 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | 463 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); |
| @@ -513,11 +519,11 @@ void perf_hpp__column_disable(unsigned col) | |||
| 513 | 519 | ||
| 514 | void perf_hpp__cancel_cumulate(void) | 520 | void perf_hpp__cancel_cumulate(void) |
| 515 | { | 521 | { |
| 516 | if (field_order) | 522 | if (is_strict_order(field_order)) |
| 517 | return; | 523 | return; |
| 518 | 524 | ||
| 519 | perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC); | 525 | perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC); |
| 520 | perf_hpp__format[PERF_HPP__OVERHEAD].header = hpp__header_overhead; | 526 | perf_hpp__format[PERF_HPP__OVERHEAD].name = "Overhead"; |
| 521 | } | 527 | } |
| 522 | 528 | ||
| 523 | void perf_hpp__setup_output_field(void) | 529 | void perf_hpp__setup_output_field(void) |
| @@ -622,3 +628,59 @@ unsigned int hists__sort_list_width(struct hists *hists) | |||
| 622 | 628 | ||
| 623 | return ret; | 629 | return ret; |
| 624 | } | 630 | } |
| 631 | |||
| 632 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | ||
| 633 | { | ||
| 634 | int idx; | ||
| 635 | |||
| 636 | if (perf_hpp__is_sort_entry(fmt)) | ||
| 637 | return perf_hpp__reset_sort_width(fmt, hists); | ||
| 638 | |||
| 639 | for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { | ||
| 640 | if (fmt == &perf_hpp__format[idx]) | ||
| 641 | break; | ||
| 642 | } | ||
| 643 | |||
| 644 | if (idx == PERF_HPP__MAX_INDEX) | ||
| 645 | return; | ||
| 646 | |||
| 647 | switch (idx) { | ||
| 648 | case PERF_HPP__OVERHEAD: | ||
| 649 | case PERF_HPP__OVERHEAD_SYS: | ||
| 650 | case PERF_HPP__OVERHEAD_US: | ||
| 651 | case PERF_HPP__OVERHEAD_ACC: | ||
| 652 | fmt->len = 8; | ||
| 653 | break; | ||
| 654 | |||
| 655 | case PERF_HPP__OVERHEAD_GUEST_SYS: | ||
| 656 | case PERF_HPP__OVERHEAD_GUEST_US: | ||
| 657 | fmt->len = 9; | ||
| 658 | break; | ||
| 659 | |||
| 660 | case PERF_HPP__SAMPLES: | ||
| 661 | case PERF_HPP__PERIOD: | ||
| 662 | fmt->len = 12; | ||
| 663 | break; | ||
| 664 | |||
| 665 | default: | ||
| 666 | break; | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | void perf_hpp__set_user_width(const char *width_list_str) | ||
| 671 | { | ||
| 672 | struct perf_hpp_fmt *fmt; | ||
| 673 | const char *ptr = width_list_str; | ||
| 674 | |||
| 675 | perf_hpp__for_each_format(fmt) { | ||
| 676 | char *p; | ||
| 677 | |||
| 678 | int len = strtol(ptr, &p, 10); | ||
| 679 | fmt->user_len = len; | ||
| 680 | |||
| 681 | if (*p == ',') | ||
| 682 | ptr = p + 1; | ||
| 683 | else | ||
| 684 | break; | ||
| 685 | } | ||
| 686 | } | ||
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 40af0acb4fe9..15b451acbde6 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
| @@ -395,10 +395,12 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
| 395 | 395 | ||
| 396 | init_rem_hits(); | 396 | init_rem_hits(); |
| 397 | 397 | ||
| 398 | |||
| 399 | perf_hpp__for_each_format(fmt) | 398 | perf_hpp__for_each_format(fmt) |
| 400 | perf_hpp__reset_width(fmt, hists); | 399 | perf_hpp__reset_width(fmt, hists); |
| 401 | 400 | ||
| 401 | if (symbol_conf.col_width_list_str) | ||
| 402 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); | ||
| 403 | |||
| 402 | if (!show_header) | 404 | if (!show_header) |
| 403 | goto print_entries; | 405 | goto print_entries; |
| 404 | 406 | ||
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 809b4c50beae..36437527dbb3 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -232,9 +232,16 @@ static int mov__parse(struct ins_operands *ops) | |||
| 232 | return -1; | 232 | return -1; |
| 233 | 233 | ||
| 234 | target = ++s; | 234 | target = ++s; |
| 235 | comment = strchr(s, '#'); | ||
| 235 | 236 | ||
| 236 | while (s[0] != '\0' && !isspace(s[0])) | 237 | if (comment != NULL) |
| 237 | ++s; | 238 | s = comment - 1; |
| 239 | else | ||
| 240 | s = strchr(s, '\0') - 1; | ||
| 241 | |||
| 242 | while (s > target && isspace(s[0])) | ||
| 243 | --s; | ||
| 244 | s++; | ||
| 238 | prev = *s; | 245 | prev = *s; |
| 239 | *s = '\0'; | 246 | *s = '\0'; |
| 240 | 247 | ||
| @@ -244,7 +251,6 @@ static int mov__parse(struct ins_operands *ops) | |||
| 244 | if (ops->target.raw == NULL) | 251 | if (ops->target.raw == NULL) |
| 245 | goto out_free_source; | 252 | goto out_free_source; |
| 246 | 253 | ||
| 247 | comment = strchr(s, '#'); | ||
| 248 | if (comment == NULL) | 254 | if (comment == NULL) |
| 249 | return 0; | 255 | return 0; |
| 250 | 256 | ||
| @@ -899,10 +905,8 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
| 899 | struct kcore_extract kce; | 905 | struct kcore_extract kce; |
| 900 | bool delete_extract = false; | 906 | bool delete_extract = false; |
| 901 | 907 | ||
| 902 | if (filename) { | 908 | if (filename) |
| 903 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | 909 | symbol__join_symfs(symfs_filename, filename); |
| 904 | symbol_conf.symfs, filename); | ||
| 905 | } | ||
| 906 | 910 | ||
| 907 | if (filename == NULL) { | 911 | if (filename == NULL) { |
| 908 | if (dso->has_build_id) { | 912 | if (dso->has_build_id) { |
| @@ -922,8 +926,7 @@ fallback: | |||
| 922 | * DSO is the same as when 'perf record' ran. | 926 | * DSO is the same as when 'perf record' ran. |
| 923 | */ | 927 | */ |
| 924 | filename = (char *)dso->long_name; | 928 | filename = (char *)dso->long_name; |
| 925 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | 929 | symbol__join_symfs(symfs_filename, filename); |
| 926 | symbol_conf.symfs, filename); | ||
| 927 | free_filename = false; | 930 | free_filename = false; |
| 928 | } | 931 | } |
| 929 | 932 | ||
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 7b176dd02e1a..5cf9e1b5989d 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
| @@ -22,6 +22,7 @@ typedef int (*config_fn_t)(const char *, const char *, void *); | |||
| 22 | extern int perf_default_config(const char *, const char *, void *); | 22 | extern int perf_default_config(const char *, const char *, void *); |
| 23 | extern int perf_config(config_fn_t fn, void *); | 23 | extern int perf_config(config_fn_t fn, void *); |
| 24 | extern int perf_config_int(const char *, const char *); | 24 | extern int perf_config_int(const char *, const char *); |
| 25 | extern u64 perf_config_u64(const char *, const char *); | ||
| 25 | extern int perf_config_bool(const char *, const char *); | 26 | extern int perf_config_bool(const char *, const char *); |
| 26 | extern int config_error_nonbool(const char *); | 27 | extern int config_error_nonbool(const char *); |
| 27 | extern const char *perf_config_dirname(const char *, const char *); | 28 | extern const char *perf_config_dirname(const char *, const char *); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 437ee09727e6..c84d3f8dcb75 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -25,77 +25,172 @@ | |||
| 25 | 25 | ||
| 26 | __thread struct callchain_cursor callchain_cursor; | 26 | __thread struct callchain_cursor callchain_cursor; |
| 27 | 27 | ||
| 28 | int | 28 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
| 29 | parse_callchain_report_opt(const char *arg) | 29 | static int get_stack_size(const char *str, unsigned long *_size) |
| 30 | { | 30 | { |
| 31 | char *tok, *tok2; | ||
| 32 | char *endptr; | 31 | char *endptr; |
| 32 | unsigned long size; | ||
| 33 | unsigned long max_size = round_down(USHRT_MAX, sizeof(u64)); | ||
| 33 | 34 | ||
| 34 | symbol_conf.use_callchain = true; | 35 | size = strtoul(str, &endptr, 0); |
| 35 | 36 | ||
| 36 | if (!arg) | 37 | do { |
| 38 | if (*endptr) | ||
| 39 | break; | ||
| 40 | |||
| 41 | size = round_up(size, sizeof(u64)); | ||
| 42 | if (!size || size > max_size) | ||
| 43 | break; | ||
| 44 | |||
| 45 | *_size = size; | ||
| 37 | return 0; | 46 | return 0; |
| 38 | 47 | ||
| 39 | tok = strtok((char *)arg, ","); | 48 | } while (0); |
| 40 | if (!tok) | ||
| 41 | return -1; | ||
| 42 | 49 | ||
| 43 | /* get the output mode */ | 50 | pr_err("callchain: Incorrect stack dump size (max %ld): %s\n", |
| 44 | if (!strncmp(tok, "graph", strlen(arg))) { | 51 | max_size, str); |
| 45 | callchain_param.mode = CHAIN_GRAPH_ABS; | 52 | return -1; |
| 53 | } | ||
| 54 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | ||
| 46 | 55 | ||
| 47 | } else if (!strncmp(tok, "flat", strlen(arg))) { | 56 | int parse_callchain_record_opt(const char *arg) |
| 48 | callchain_param.mode = CHAIN_FLAT; | 57 | { |
| 49 | } else if (!strncmp(tok, "fractal", strlen(arg))) { | 58 | char *tok, *name, *saveptr = NULL; |
| 50 | callchain_param.mode = CHAIN_GRAPH_REL; | 59 | char *buf; |
| 51 | } else if (!strncmp(tok, "none", strlen(arg))) { | 60 | int ret = -1; |
| 52 | callchain_param.mode = CHAIN_NONE; | 61 | |
| 53 | symbol_conf.use_callchain = false; | 62 | /* We need buffer that we know we can write to. */ |
| 54 | return 0; | 63 | buf = malloc(strlen(arg) + 1); |
| 55 | } else { | 64 | if (!buf) |
| 56 | return -1; | 65 | return -ENOMEM; |
| 57 | } | 66 | |
| 67 | strcpy(buf, arg); | ||
| 68 | |||
| 69 | tok = strtok_r((char *)buf, ",", &saveptr); | ||
| 70 | name = tok ? : (char *)buf; | ||
| 71 | |||
| 72 | do { | ||
| 73 | /* Framepointer style */ | ||
| 74 | if (!strncmp(name, "fp", sizeof("fp"))) { | ||
| 75 | if (!strtok_r(NULL, ",", &saveptr)) { | ||
| 76 | callchain_param.record_mode = CALLCHAIN_FP; | ||
| 77 | ret = 0; | ||
| 78 | } else | ||
| 79 | pr_err("callchain: No more arguments " | ||
| 80 | "needed for -g fp\n"); | ||
| 81 | break; | ||
| 58 | 82 | ||
| 59 | /* get the min percentage */ | 83 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
| 60 | tok = strtok(NULL, ","); | 84 | /* Dwarf style */ |
| 61 | if (!tok) | 85 | } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { |
| 62 | goto setup; | 86 | const unsigned long default_stack_dump_size = 8192; |
| 63 | 87 | ||
| 64 | callchain_param.min_percent = strtod(tok, &endptr); | 88 | ret = 0; |
| 65 | if (tok == endptr) | 89 | callchain_param.record_mode = CALLCHAIN_DWARF; |
| 66 | return -1; | 90 | callchain_param.dump_size = default_stack_dump_size; |
| 67 | 91 | ||
| 68 | /* get the print limit */ | 92 | tok = strtok_r(NULL, ",", &saveptr); |
| 69 | tok2 = strtok(NULL, ","); | 93 | if (tok) { |
| 70 | if (!tok2) | 94 | unsigned long size = 0; |
| 71 | goto setup; | ||
| 72 | 95 | ||
| 73 | if (tok2[0] != 'c') { | 96 | ret = get_stack_size(tok, &size); |
| 74 | callchain_param.print_limit = strtoul(tok2, &endptr, 0); | 97 | callchain_param.dump_size = size; |
| 75 | tok2 = strtok(NULL, ","); | 98 | } |
| 76 | if (!tok2) | 99 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ |
| 77 | goto setup; | 100 | } else { |
| 101 | pr_err("callchain: Unknown --call-graph option " | ||
| 102 | "value: %s\n", arg); | ||
| 103 | break; | ||
| 104 | } | ||
| 105 | |||
| 106 | } while (0); | ||
| 107 | |||
| 108 | free(buf); | ||
| 109 | return ret; | ||
| 110 | } | ||
| 111 | |||
| 112 | static int parse_callchain_mode(const char *value) | ||
| 113 | { | ||
| 114 | if (!strncmp(value, "graph", strlen(value))) { | ||
| 115 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | if (!strncmp(value, "flat", strlen(value))) { | ||
| 119 | callchain_param.mode = CHAIN_FLAT; | ||
| 120 | return 0; | ||
| 78 | } | 121 | } |
| 122 | if (!strncmp(value, "fractal", strlen(value))) { | ||
| 123 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
| 124 | return 0; | ||
| 125 | } | ||
| 126 | return -1; | ||
| 127 | } | ||
| 79 | 128 | ||
| 80 | /* get the call chain order */ | 129 | static int parse_callchain_order(const char *value) |
| 81 | if (!strncmp(tok2, "caller", strlen("caller"))) | 130 | { |
| 131 | if (!strncmp(value, "caller", strlen(value))) { | ||
| 82 | callchain_param.order = ORDER_CALLER; | 132 | callchain_param.order = ORDER_CALLER; |
| 83 | else if (!strncmp(tok2, "callee", strlen("callee"))) | 133 | return 0; |
| 134 | } | ||
| 135 | if (!strncmp(value, "callee", strlen(value))) { | ||
| 84 | callchain_param.order = ORDER_CALLEE; | 136 | callchain_param.order = ORDER_CALLEE; |
| 85 | else | 137 | return 0; |
| 86 | return -1; | 138 | } |
| 139 | return -1; | ||
| 140 | } | ||
| 87 | 141 | ||
| 88 | /* Get the sort key */ | 142 | static int parse_callchain_sort_key(const char *value) |
| 89 | tok2 = strtok(NULL, ","); | 143 | { |
| 90 | if (!tok2) | 144 | if (!strncmp(value, "function", strlen(value))) { |
| 91 | goto setup; | ||
| 92 | if (!strncmp(tok2, "function", strlen("function"))) | ||
| 93 | callchain_param.key = CCKEY_FUNCTION; | 145 | callchain_param.key = CCKEY_FUNCTION; |
| 94 | else if (!strncmp(tok2, "address", strlen("address"))) | 146 | return 0; |
| 147 | } | ||
| 148 | if (!strncmp(value, "address", strlen(value))) { | ||
| 95 | callchain_param.key = CCKEY_ADDRESS; | 149 | callchain_param.key = CCKEY_ADDRESS; |
| 96 | else | 150 | return 0; |
| 97 | return -1; | 151 | } |
| 98 | setup: | 152 | return -1; |
| 153 | } | ||
| 154 | |||
| 155 | int | ||
| 156 | parse_callchain_report_opt(const char *arg) | ||
| 157 | { | ||
| 158 | char *tok; | ||
| 159 | char *endptr; | ||
| 160 | bool minpcnt_set = false; | ||
| 161 | |||
| 162 | symbol_conf.use_callchain = true; | ||
| 163 | |||
| 164 | if (!arg) | ||
| 165 | return 0; | ||
| 166 | |||
| 167 | while ((tok = strtok((char *)arg, ",")) != NULL) { | ||
| 168 | if (!strncmp(tok, "none", strlen(tok))) { | ||
| 169 | callchain_param.mode = CHAIN_NONE; | ||
| 170 | symbol_conf.use_callchain = false; | ||
| 171 | return 0; | ||
| 172 | } | ||
| 173 | |||
| 174 | if (!parse_callchain_mode(tok) || | ||
| 175 | !parse_callchain_order(tok) || | ||
| 176 | !parse_callchain_sort_key(tok)) { | ||
| 177 | /* parsing ok - move on to the next */ | ||
| 178 | } else if (!minpcnt_set) { | ||
| 179 | /* try to get the min percent */ | ||
| 180 | callchain_param.min_percent = strtod(tok, &endptr); | ||
| 181 | if (tok == endptr) | ||
| 182 | return -1; | ||
| 183 | minpcnt_set = true; | ||
| 184 | } else { | ||
| 185 | /* try print limit at last */ | ||
| 186 | callchain_param.print_limit = strtoul(tok, &endptr, 0); | ||
| 187 | if (tok == endptr) | ||
| 188 | return -1; | ||
| 189 | } | ||
| 190 | |||
| 191 | arg = NULL; | ||
| 192 | } | ||
| 193 | |||
| 99 | if (callchain_register_param(&callchain_param) < 0) { | 194 | if (callchain_register_param(&callchain_param) < 0) { |
| 100 | pr_err("Can't register callchain params\n"); | 195 | pr_err("Can't register callchain params\n"); |
| 101 | return -1; | 196 | return -1; |
| @@ -103,6 +198,47 @@ setup: | |||
| 103 | return 0; | 198 | return 0; |
| 104 | } | 199 | } |
| 105 | 200 | ||
| 201 | int perf_callchain_config(const char *var, const char *value) | ||
| 202 | { | ||
| 203 | char *endptr; | ||
| 204 | |||
| 205 | if (prefixcmp(var, "call-graph.")) | ||
| 206 | return 0; | ||
| 207 | var += sizeof("call-graph.") - 1; | ||
| 208 | |||
| 209 | if (!strcmp(var, "record-mode")) | ||
| 210 | return parse_callchain_record_opt(value); | ||
| 211 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | ||
| 212 | if (!strcmp(var, "dump-size")) { | ||
| 213 | unsigned long size = 0; | ||
| 214 | int ret; | ||
| 215 | |||
| 216 | ret = get_stack_size(value, &size); | ||
| 217 | callchain_param.dump_size = size; | ||
| 218 | |||
| 219 | return ret; | ||
| 220 | } | ||
| 221 | #endif | ||
| 222 | if (!strcmp(var, "print-type")) | ||
| 223 | return parse_callchain_mode(value); | ||
| 224 | if (!strcmp(var, "order")) | ||
| 225 | return parse_callchain_order(value); | ||
| 226 | if (!strcmp(var, "sort-key")) | ||
| 227 | return parse_callchain_sort_key(value); | ||
| 228 | if (!strcmp(var, "threshold")) { | ||
| 229 | callchain_param.min_percent = strtod(value, &endptr); | ||
| 230 | if (value == endptr) | ||
| 231 | return -1; | ||
| 232 | } | ||
| 233 | if (!strcmp(var, "print-limit")) { | ||
| 234 | callchain_param.print_limit = strtod(value, &endptr); | ||
| 235 | if (value == endptr) | ||
| 236 | return -1; | ||
| 237 | } | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 106 | static void | 242 | static void |
| 107 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 243 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
| 108 | enum chain_mode mode) | 244 | enum chain_mode mode) |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index da43619d6173..2a1f5a46543a 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -54,6 +54,9 @@ enum chain_key { | |||
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | struct callchain_param { | 56 | struct callchain_param { |
| 57 | bool enabled; | ||
| 58 | enum perf_call_graph_mode record_mode; | ||
| 59 | u32 dump_size; | ||
| 57 | enum chain_mode mode; | 60 | enum chain_mode mode; |
| 58 | u32 print_limit; | 61 | u32 print_limit; |
| 59 | double min_percent; | 62 | double min_percent; |
| @@ -154,7 +157,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
| 154 | struct option; | 157 | struct option; |
| 155 | struct hist_entry; | 158 | struct hist_entry; |
| 156 | 159 | ||
| 157 | int record_parse_callchain(const char *arg, struct record_opts *opts); | ||
| 158 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); | 160 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); |
| 159 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); | 161 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); |
| 160 | 162 | ||
| @@ -166,7 +168,9 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * | |||
| 166 | bool hide_unresolved); | 168 | bool hide_unresolved); |
| 167 | 169 | ||
| 168 | extern const char record_callchain_help[]; | 170 | extern const char record_callchain_help[]; |
| 171 | int parse_callchain_record_opt(const char *arg); | ||
| 169 | int parse_callchain_report_opt(const char *arg); | 172 | int parse_callchain_report_opt(const char *arg); |
| 173 | int perf_callchain_config(const char *var, const char *value); | ||
| 170 | 174 | ||
| 171 | static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, | 175 | static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, |
| 172 | struct callchain_cursor *src) | 176 | struct callchain_cursor *src) |
diff --git a/tools/perf/util/cloexec.c b/tools/perf/util/cloexec.c index c5d05ec17220..47b78b3f0325 100644 --- a/tools/perf/util/cloexec.c +++ b/tools/perf/util/cloexec.c | |||
| @@ -1,7 +1,9 @@ | |||
| 1 | #include <sched.h> | ||
| 1 | #include "util.h" | 2 | #include "util.h" |
| 2 | #include "../perf.h" | 3 | #include "../perf.h" |
| 3 | #include "cloexec.h" | 4 | #include "cloexec.h" |
| 4 | #include "asm/bug.h" | 5 | #include "asm/bug.h" |
| 6 | #include "debug.h" | ||
| 5 | 7 | ||
| 6 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; | 8 | static unsigned long flag = PERF_FLAG_FD_CLOEXEC; |
| 7 | 9 | ||
| @@ -9,15 +11,30 @@ static int perf_flag_probe(void) | |||
| 9 | { | 11 | { |
| 10 | /* use 'safest' configuration as used in perf_evsel__fallback() */ | 12 | /* use 'safest' configuration as used in perf_evsel__fallback() */ |
| 11 | struct perf_event_attr attr = { | 13 | struct perf_event_attr attr = { |
| 12 | .type = PERF_COUNT_SW_CPU_CLOCK, | 14 | .type = PERF_TYPE_SOFTWARE, |
| 13 | .config = PERF_COUNT_SW_CPU_CLOCK, | 15 | .config = PERF_COUNT_SW_CPU_CLOCK, |
| 16 | .exclude_kernel = 1, | ||
| 14 | }; | 17 | }; |
| 15 | int fd; | 18 | int fd; |
| 16 | int err; | 19 | int err; |
| 20 | int cpu; | ||
| 21 | pid_t pid = -1; | ||
| 22 | char sbuf[STRERR_BUFSIZE]; | ||
| 17 | 23 | ||
| 18 | /* check cloexec flag */ | 24 | cpu = sched_getcpu(); |
| 19 | fd = sys_perf_event_open(&attr, 0, -1, -1, | 25 | if (cpu < 0) |
| 20 | PERF_FLAG_FD_CLOEXEC); | 26 | cpu = 0; |
| 27 | |||
| 28 | while (1) { | ||
| 29 | /* check cloexec flag */ | ||
| 30 | fd = sys_perf_event_open(&attr, pid, cpu, -1, | ||
| 31 | PERF_FLAG_FD_CLOEXEC); | ||
| 32 | if (fd < 0 && pid == -1 && errno == EACCES) { | ||
| 33 | pid = 0; | ||
| 34 | continue; | ||
| 35 | } | ||
| 36 | break; | ||
| 37 | } | ||
| 21 | err = errno; | 38 | err = errno; |
| 22 | 39 | ||
| 23 | if (fd >= 0) { | 40 | if (fd >= 0) { |
| @@ -25,17 +42,17 @@ static int perf_flag_probe(void) | |||
| 25 | return 1; | 42 | return 1; |
| 26 | } | 43 | } |
| 27 | 44 | ||
| 28 | WARN_ONCE(err != EINVAL, | 45 | WARN_ONCE(err != EINVAL && err != EBUSY, |
| 29 | "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n", | 46 | "perf_event_open(..., PERF_FLAG_FD_CLOEXEC) failed with unexpected error %d (%s)\n", |
| 30 | err, strerror(err)); | 47 | err, strerror_r(err, sbuf, sizeof(sbuf))); |
| 31 | 48 | ||
| 32 | /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ | 49 | /* not supported, confirm error related to PERF_FLAG_FD_CLOEXEC */ |
| 33 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | 50 | fd = sys_perf_event_open(&attr, pid, cpu, -1, 0); |
| 34 | err = errno; | 51 | err = errno; |
| 35 | 52 | ||
| 36 | if (WARN_ONCE(fd < 0, | 53 | if (WARN_ONCE(fd < 0 && err != EBUSY, |
| 37 | "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n", | 54 | "perf_event_open(..., 0) failed unexpectedly with error %d (%s)\n", |
| 38 | err, strerror(err))) | 55 | err, strerror_r(err, sbuf, sizeof(sbuf)))) |
| 39 | return -1; | 56 | return -1; |
| 40 | 57 | ||
| 41 | close(fd); | 58 | close(fd); |
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 87b8672eb413..f4654183d391 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
| @@ -335,3 +335,19 @@ int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
| 335 | va_end(args); | 335 | va_end(args); |
| 336 | return value_color_snprintf(bf, size, fmt, percent); | 336 | return value_color_snprintf(bf, size, fmt, percent); |
| 337 | } | 337 | } |
| 338 | |||
| 339 | int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...) | ||
| 340 | { | ||
| 341 | va_list args; | ||
| 342 | int len; | ||
| 343 | double percent; | ||
| 344 | const char *color; | ||
| 345 | |||
| 346 | va_start(args, fmt); | ||
| 347 | len = va_arg(args, int); | ||
| 348 | percent = va_arg(args, double); | ||
| 349 | va_end(args); | ||
| 350 | |||
| 351 | color = get_percent_color(percent); | ||
| 352 | return color_snprintf(bf, size, color, fmt, len, percent); | ||
| 353 | } | ||
diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index 7ff30a62a132..0a594b8a0c26 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h | |||
| @@ -41,6 +41,7 @@ int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); | |||
| 41 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); | 41 | int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); |
| 42 | int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); | 42 | int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); |
| 43 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); | 43 | int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); |
| 44 | int percent_color_len_snprintf(char *bf, size_t size, const char *fmt, ...); | ||
| 44 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); | 45 | int percent_color_fprintf(FILE *fp, const char *fmt, double percent); |
| 45 | const char *get_percent_color(double percent); | 46 | const char *get_percent_color(double percent); |
| 46 | 47 | ||
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index f9e777629e21..b2bb59df65e1 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c | |||
| @@ -74,7 +74,7 @@ static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root) | |||
| 74 | return new; | 74 | return new; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | struct comm *comm__new(const char *str, u64 timestamp) | 77 | struct comm *comm__new(const char *str, u64 timestamp, bool exec) |
| 78 | { | 78 | { |
| 79 | struct comm *comm = zalloc(sizeof(*comm)); | 79 | struct comm *comm = zalloc(sizeof(*comm)); |
| 80 | 80 | ||
| @@ -82,6 +82,7 @@ struct comm *comm__new(const char *str, u64 timestamp) | |||
| 82 | return NULL; | 82 | return NULL; |
| 83 | 83 | ||
| 84 | comm->start = timestamp; | 84 | comm->start = timestamp; |
| 85 | comm->exec = exec; | ||
| 85 | 86 | ||
| 86 | comm->comm_str = comm_str__findnew(str, &comm_str_root); | 87 | comm->comm_str = comm_str__findnew(str, &comm_str_root); |
| 87 | if (!comm->comm_str) { | 88 | if (!comm->comm_str) { |
| @@ -94,7 +95,7 @@ struct comm *comm__new(const char *str, u64 timestamp) | |||
| 94 | return comm; | 95 | return comm; |
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | int comm__override(struct comm *comm, const char *str, u64 timestamp) | 98 | int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec) |
| 98 | { | 99 | { |
| 99 | struct comm_str *new, *old = comm->comm_str; | 100 | struct comm_str *new, *old = comm->comm_str; |
| 100 | 101 | ||
| @@ -106,6 +107,8 @@ int comm__override(struct comm *comm, const char *str, u64 timestamp) | |||
| 106 | comm_str__put(old); | 107 | comm_str__put(old); |
| 107 | comm->comm_str = new; | 108 | comm->comm_str = new; |
| 108 | comm->start = timestamp; | 109 | comm->start = timestamp; |
| 110 | if (exec) | ||
| 111 | comm->exec = true; | ||
| 109 | 112 | ||
| 110 | return 0; | 113 | return 0; |
| 111 | } | 114 | } |
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h index fac5bd51befc..51c10ab257f8 100644 --- a/tools/perf/util/comm.h +++ b/tools/perf/util/comm.h | |||
| @@ -11,11 +11,13 @@ struct comm { | |||
| 11 | struct comm_str *comm_str; | 11 | struct comm_str *comm_str; |
| 12 | u64 start; | 12 | u64 start; |
| 13 | struct list_head list; | 13 | struct list_head list; |
| 14 | bool exec; | ||
| 14 | }; | 15 | }; |
| 15 | 16 | ||
| 16 | void comm__free(struct comm *comm); | 17 | void comm__free(struct comm *comm); |
| 17 | struct comm *comm__new(const char *str, u64 timestamp); | 18 | struct comm *comm__new(const char *str, u64 timestamp, bool exec); |
| 18 | const char *comm__str(const struct comm *comm); | 19 | const char *comm__str(const struct comm *comm); |
| 19 | int comm__override(struct comm *comm, const char *str, u64 timestamp); | 20 | int comm__override(struct comm *comm, const char *str, u64 timestamp, |
| 21 | bool exec); | ||
| 20 | 22 | ||
| 21 | #endif /* __PERF_COMM_H */ | 23 | #endif /* __PERF_COMM_H */ |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index 1e5e2e5af6b1..57ff826f150b 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
| @@ -222,7 +222,8 @@ static int perf_parse_file(config_fn_t fn, void *data) | |||
| 222 | const unsigned char *bomptr = utf8_bom; | 222 | const unsigned char *bomptr = utf8_bom; |
| 223 | 223 | ||
| 224 | for (;;) { | 224 | for (;;) { |
| 225 | int c = get_next_char(); | 225 | int line, c = get_next_char(); |
| 226 | |||
| 226 | if (bomptr && *bomptr) { | 227 | if (bomptr && *bomptr) { |
| 227 | /* We are at the file beginning; skip UTF8-encoded BOM | 228 | /* We are at the file beginning; skip UTF8-encoded BOM |
| 228 | * if present. Sane editors won't put this in on their | 229 | * if present. Sane editors won't put this in on their |
| @@ -261,8 +262,16 @@ static int perf_parse_file(config_fn_t fn, void *data) | |||
| 261 | if (!isalpha(c)) | 262 | if (!isalpha(c)) |
| 262 | break; | 263 | break; |
| 263 | var[baselen] = tolower(c); | 264 | var[baselen] = tolower(c); |
| 264 | if (get_value(fn, data, var, baselen+1) < 0) | 265 | |
| 266 | /* | ||
| 267 | * The get_value function might or might not reach the '\n', | ||
| 268 | * so saving the current line number for error reporting. | ||
| 269 | */ | ||
| 270 | line = config_linenr; | ||
| 271 | if (get_value(fn, data, var, baselen+1) < 0) { | ||
| 272 | config_linenr = line; | ||
| 265 | break; | 273 | break; |
| 274 | } | ||
| 266 | } | 275 | } |
| 267 | die("bad config file line %d in %s", config_linenr, config_file_name); | 276 | die("bad config file line %d in %s", config_linenr, config_file_name); |
| 268 | } | 277 | } |
| @@ -286,6 +295,21 @@ static int parse_unit_factor(const char *end, unsigned long *val) | |||
| 286 | return 0; | 295 | return 0; |
| 287 | } | 296 | } |
| 288 | 297 | ||
| 298 | static int perf_parse_llong(const char *value, long long *ret) | ||
| 299 | { | ||
| 300 | if (value && *value) { | ||
| 301 | char *end; | ||
| 302 | long long val = strtoll(value, &end, 0); | ||
| 303 | unsigned long factor = 1; | ||
| 304 | |||
| 305 | if (!parse_unit_factor(end, &factor)) | ||
| 306 | return 0; | ||
| 307 | *ret = val * factor; | ||
| 308 | return 1; | ||
| 309 | } | ||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 289 | static int perf_parse_long(const char *value, long *ret) | 313 | static int perf_parse_long(const char *value, long *ret) |
| 290 | { | 314 | { |
| 291 | if (value && *value) { | 315 | if (value && *value) { |
| @@ -307,6 +331,15 @@ static void die_bad_config(const char *name) | |||
| 307 | die("bad config value for '%s'", name); | 331 | die("bad config value for '%s'", name); |
| 308 | } | 332 | } |
| 309 | 333 | ||
| 334 | u64 perf_config_u64(const char *name, const char *value) | ||
| 335 | { | ||
| 336 | long long ret = 0; | ||
| 337 | |||
| 338 | if (!perf_parse_llong(value, &ret)) | ||
| 339 | die_bad_config(name); | ||
| 340 | return (u64) ret; | ||
| 341 | } | ||
| 342 | |||
| 310 | int perf_config_int(const char *name, const char *value) | 343 | int perf_config_int(const char *name, const char *value) |
| 311 | { | 344 | { |
| 312 | long ret = 0; | 345 | long ret = 0; |
| @@ -372,6 +405,9 @@ int perf_default_config(const char *var, const char *value, | |||
| 372 | if (!prefixcmp(var, "ui.")) | 405 | if (!prefixcmp(var, "ui.")) |
| 373 | return perf_ui_config(var, value); | 406 | return perf_ui_config(var, value); |
| 374 | 407 | ||
| 408 | if (!prefixcmp(var, "call-graph.")) | ||
| 409 | return perf_callchain_config(var, value); | ||
| 410 | |||
| 375 | /* Add other config variables here. */ | 411 | /* Add other config variables here. */ |
| 376 | return 0; | 412 | return 0; |
| 377 | } | 413 | } |
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 29d720cf5844..1921942fc2e0 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c | |||
| @@ -50,12 +50,14 @@ static int open_file_read(struct perf_data_file *file) | |||
| 50 | { | 50 | { |
| 51 | struct stat st; | 51 | struct stat st; |
| 52 | int fd; | 52 | int fd; |
| 53 | char sbuf[STRERR_BUFSIZE]; | ||
| 53 | 54 | ||
| 54 | fd = open(file->path, O_RDONLY); | 55 | fd = open(file->path, O_RDONLY); |
| 55 | if (fd < 0) { | 56 | if (fd < 0) { |
| 56 | int err = errno; | 57 | int err = errno; |
| 57 | 58 | ||
| 58 | pr_err("failed to open %s: %s", file->path, strerror(err)); | 59 | pr_err("failed to open %s: %s", file->path, |
| 60 | strerror_r(err, sbuf, sizeof(sbuf))); | ||
| 59 | if (err == ENOENT && !strcmp(file->path, "perf.data")) | 61 | if (err == ENOENT && !strcmp(file->path, "perf.data")) |
| 60 | pr_err(" (try 'perf record' first)"); | 62 | pr_err(" (try 'perf record' first)"); |
| 61 | pr_err("\n"); | 63 | pr_err("\n"); |
| @@ -88,6 +90,7 @@ static int open_file_read(struct perf_data_file *file) | |||
| 88 | static int open_file_write(struct perf_data_file *file) | 90 | static int open_file_write(struct perf_data_file *file) |
| 89 | { | 91 | { |
| 90 | int fd; | 92 | int fd; |
| 93 | char sbuf[STRERR_BUFSIZE]; | ||
| 91 | 94 | ||
| 92 | if (check_backup(file)) | 95 | if (check_backup(file)) |
| 93 | return -1; | 96 | return -1; |
| @@ -95,7 +98,8 @@ static int open_file_write(struct perf_data_file *file) | |||
| 95 | fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); | 98 | fd = open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); |
| 96 | 99 | ||
| 97 | if (fd < 0) | 100 | if (fd < 0) |
| 98 | pr_err("failed to open %s : %s\n", file->path, strerror(errno)); | 101 | pr_err("failed to open %s : %s\n", file->path, |
| 102 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 99 | 103 | ||
| 100 | return fd; | 104 | return fd; |
| 101 | } | 105 | } |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 71d419362634..ba357f3226c6 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
| @@ -13,8 +13,12 @@ | |||
| 13 | #include "util.h" | 13 | #include "util.h" |
| 14 | #include "target.h" | 14 | #include "target.h" |
| 15 | 15 | ||
| 16 | #define NSECS_PER_SEC 1000000000ULL | ||
| 17 | #define NSECS_PER_USEC 1000ULL | ||
| 18 | |||
| 16 | int verbose; | 19 | int verbose; |
| 17 | bool dump_trace = false, quiet = false; | 20 | bool dump_trace = false, quiet = false; |
| 21 | int debug_ordered_events; | ||
| 18 | 22 | ||
| 19 | static int _eprintf(int level, int var, const char *fmt, va_list args) | 23 | static int _eprintf(int level, int var, const char *fmt, va_list args) |
| 20 | { | 24 | { |
| @@ -42,6 +46,35 @@ int eprintf(int level, int var, const char *fmt, ...) | |||
| 42 | return ret; | 46 | return ret; |
| 43 | } | 47 | } |
| 44 | 48 | ||
| 49 | static int __eprintf_time(u64 t, const char *fmt, va_list args) | ||
| 50 | { | ||
| 51 | int ret = 0; | ||
| 52 | u64 secs, usecs, nsecs = t; | ||
| 53 | |||
| 54 | secs = nsecs / NSECS_PER_SEC; | ||
| 55 | nsecs -= secs * NSECS_PER_SEC; | ||
| 56 | usecs = nsecs / NSECS_PER_USEC; | ||
| 57 | |||
| 58 | ret = fprintf(stderr, "[%13" PRIu64 ".%06" PRIu64 "] ", | ||
| 59 | secs, usecs); | ||
| 60 | ret += vfprintf(stderr, fmt, args); | ||
| 61 | return ret; | ||
| 62 | } | ||
| 63 | |||
| 64 | int eprintf_time(int level, int var, u64 t, const char *fmt, ...) | ||
| 65 | { | ||
| 66 | int ret = 0; | ||
| 67 | va_list args; | ||
| 68 | |||
| 69 | if (var >= level) { | ||
| 70 | va_start(args, fmt); | ||
| 71 | ret = __eprintf_time(t, fmt, args); | ||
| 72 | va_end(args); | ||
| 73 | } | ||
| 74 | |||
| 75 | return ret; | ||
| 76 | } | ||
| 77 | |||
| 45 | /* | 78 | /* |
| 46 | * Overloading libtraceevent standard info print | 79 | * Overloading libtraceevent standard info print |
| 47 | * function, display with -v in perf. | 80 | * function, display with -v in perf. |
| @@ -110,7 +143,8 @@ static struct debug_variable { | |||
| 110 | const char *name; | 143 | const char *name; |
| 111 | int *ptr; | 144 | int *ptr; |
| 112 | } debug_variables[] = { | 145 | } debug_variables[] = { |
| 113 | { .name = "verbose", .ptr = &verbose }, | 146 | { .name = "verbose", .ptr = &verbose }, |
| 147 | { .name = "ordered-events", .ptr = &debug_ordered_events}, | ||
| 114 | { .name = NULL, } | 148 | { .name = NULL, } |
| 115 | }; | 149 | }; |
| 116 | 150 | ||
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 89fb6b0f7ab2..be264d6f3b30 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #define __PERF_DEBUG_H | 3 | #define __PERF_DEBUG_H |
| 4 | 4 | ||
| 5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
| 6 | #include <string.h> | ||
| 6 | #include "event.h" | 7 | #include "event.h" |
| 7 | #include "../ui/helpline.h" | 8 | #include "../ui/helpline.h" |
| 8 | #include "../ui/progress.h" | 9 | #include "../ui/progress.h" |
| @@ -10,6 +11,7 @@ | |||
| 10 | 11 | ||
| 11 | extern int verbose; | 12 | extern int verbose; |
| 12 | extern bool quiet, dump_trace; | 13 | extern bool quiet, dump_trace; |
| 14 | extern int debug_ordered_events; | ||
| 13 | 15 | ||
| 14 | #ifndef pr_fmt | 16 | #ifndef pr_fmt |
| 15 | #define pr_fmt(fmt) fmt | 17 | #define pr_fmt(fmt) fmt |
| @@ -29,6 +31,14 @@ extern bool quiet, dump_trace; | |||
| 29 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) | 31 | #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) |
| 30 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) | 32 | #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) |
| 31 | 33 | ||
| 34 | #define pr_time_N(n, var, t, fmt, ...) \ | ||
| 35 | eprintf_time(n, var, t, fmt, ##__VA_ARGS__) | ||
| 36 | |||
| 37 | #define pr_oe_time(t, fmt, ...) pr_time_N(1, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__) | ||
| 38 | #define pr_oe_time2(t, fmt, ...) pr_time_N(2, debug_ordered_events, t, pr_fmt(fmt), ##__VA_ARGS__) | ||
| 39 | |||
| 40 | #define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */ | ||
| 41 | |||
| 32 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 42 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
| 33 | void trace_event(union perf_event *event); | 43 | void trace_event(union perf_event *event); |
| 34 | 44 | ||
| @@ -38,6 +48,7 @@ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | |||
| 38 | void pr_stat(const char *fmt, ...); | 48 | void pr_stat(const char *fmt, ...); |
| 39 | 49 | ||
| 40 | int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4))); | 50 | int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4))); |
| 51 | int eprintf_time(int level, int var, u64 t, const char *fmt, ...) __attribute__((format(printf, 4, 5))); | ||
| 41 | 52 | ||
| 42 | int perf_debug_option(const char *str); | 53 | int perf_debug_option(const char *str); |
| 43 | 54 | ||
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 90d02c661dd4..0247acfdfaca 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
| @@ -37,6 +37,7 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
| 37 | { | 37 | { |
| 38 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 38 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
| 39 | int ret = 0; | 39 | int ret = 0; |
| 40 | size_t len; | ||
| 40 | 41 | ||
| 41 | switch (type) { | 42 | switch (type) { |
| 42 | case DSO_BINARY_TYPE__DEBUGLINK: { | 43 | case DSO_BINARY_TYPE__DEBUGLINK: { |
| @@ -60,26 +61,25 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
| 60 | break; | 61 | break; |
| 61 | 62 | ||
| 62 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | 63 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: |
| 63 | snprintf(filename, size, "%s/usr/lib/debug%s.debug", | 64 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug"); |
| 64 | symbol_conf.symfs, dso->long_name); | 65 | snprintf(filename + len, size - len, "%s.debug", dso->long_name); |
| 65 | break; | 66 | break; |
| 66 | 67 | ||
| 67 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | 68 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: |
| 68 | snprintf(filename, size, "%s/usr/lib/debug%s", | 69 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug"); |
| 69 | symbol_conf.symfs, dso->long_name); | 70 | snprintf(filename + len, size - len, "%s", dso->long_name); |
| 70 | break; | 71 | break; |
| 71 | 72 | ||
| 72 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: | 73 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: |
| 73 | { | 74 | { |
| 74 | const char *last_slash; | 75 | const char *last_slash; |
| 75 | size_t len; | ||
| 76 | size_t dir_size; | 76 | size_t dir_size; |
| 77 | 77 | ||
| 78 | last_slash = dso->long_name + dso->long_name_len; | 78 | last_slash = dso->long_name + dso->long_name_len; |
| 79 | while (last_slash != dso->long_name && *last_slash != '/') | 79 | while (last_slash != dso->long_name && *last_slash != '/') |
| 80 | last_slash--; | 80 | last_slash--; |
| 81 | 81 | ||
| 82 | len = scnprintf(filename, size, "%s", symbol_conf.symfs); | 82 | len = __symbol__join_symfs(filename, size, ""); |
| 83 | dir_size = last_slash - dso->long_name + 2; | 83 | dir_size = last_slash - dso->long_name + 2; |
| 84 | if (dir_size > (size - len)) { | 84 | if (dir_size > (size - len)) { |
| 85 | ret = -1; | 85 | ret = -1; |
| @@ -100,26 +100,24 @@ int dso__read_binary_type_filename(const struct dso *dso, | |||
| 100 | build_id__sprintf(dso->build_id, | 100 | build_id__sprintf(dso->build_id, |
| 101 | sizeof(dso->build_id), | 101 | sizeof(dso->build_id), |
| 102 | build_id_hex); | 102 | build_id_hex); |
| 103 | snprintf(filename, size, | 103 | len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/"); |
| 104 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | 104 | snprintf(filename + len, size - len, "%.2s/%s.debug", |
| 105 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | 105 | build_id_hex, build_id_hex + 2); |
| 106 | break; | 106 | break; |
| 107 | 107 | ||
| 108 | case DSO_BINARY_TYPE__VMLINUX: | 108 | case DSO_BINARY_TYPE__VMLINUX: |
| 109 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | 109 | case DSO_BINARY_TYPE__GUEST_VMLINUX: |
| 110 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | 110 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: |
| 111 | snprintf(filename, size, "%s%s", | 111 | __symbol__join_symfs(filename, size, dso->long_name); |
| 112 | symbol_conf.symfs, dso->long_name); | ||
| 113 | break; | 112 | break; |
| 114 | 113 | ||
| 115 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 114 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
| 116 | snprintf(filename, size, "%s%s%s", symbol_conf.symfs, | 115 | path__join3(filename, size, symbol_conf.symfs, |
| 117 | root_dir, dso->long_name); | 116 | root_dir, dso->long_name); |
| 118 | break; | 117 | break; |
| 119 | 118 | ||
| 120 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 119 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
| 121 | snprintf(filename, size, "%s%s", symbol_conf.symfs, | 120 | __symbol__join_symfs(filename, size, dso->long_name); |
| 122 | dso->long_name); | ||
| 123 | break; | 121 | break; |
| 124 | 122 | ||
| 125 | case DSO_BINARY_TYPE__KCORE: | 123 | case DSO_BINARY_TYPE__KCORE: |
| @@ -164,13 +162,15 @@ static void close_first_dso(void); | |||
| 164 | static int do_open(char *name) | 162 | static int do_open(char *name) |
| 165 | { | 163 | { |
| 166 | int fd; | 164 | int fd; |
| 165 | char sbuf[STRERR_BUFSIZE]; | ||
| 167 | 166 | ||
| 168 | do { | 167 | do { |
| 169 | fd = open(name, O_RDONLY); | 168 | fd = open(name, O_RDONLY); |
| 170 | if (fd >= 0) | 169 | if (fd >= 0) |
| 171 | return fd; | 170 | return fd; |
| 172 | 171 | ||
| 173 | pr_debug("dso open failed, mmap: %s\n", strerror(errno)); | 172 | pr_debug("dso open failed, mmap: %s\n", |
| 173 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 174 | if (!dso__data_open_cnt || errno != EMFILE) | 174 | if (!dso__data_open_cnt || errno != EMFILE) |
| 175 | break; | 175 | break; |
| 176 | 176 | ||
| @@ -532,10 +532,12 @@ static ssize_t cached_read(struct dso *dso, u64 offset, u8 *data, ssize_t size) | |||
| 532 | static int data_file_size(struct dso *dso) | 532 | static int data_file_size(struct dso *dso) |
| 533 | { | 533 | { |
| 534 | struct stat st; | 534 | struct stat st; |
| 535 | char sbuf[STRERR_BUFSIZE]; | ||
| 535 | 536 | ||
| 536 | if (!dso->data.file_size) { | 537 | if (!dso->data.file_size) { |
| 537 | if (fstat(dso->data.fd, &st)) { | 538 | if (fstat(dso->data.fd, &st)) { |
| 538 | pr_err("dso mmap failed, fstat: %s\n", strerror(errno)); | 539 | pr_err("dso mmap failed, fstat: %s\n", |
| 540 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 539 | return -1; | 541 | return -1; |
| 540 | } | 542 | } |
| 541 | dso->data.file_size = st.st_size; | 543 | dso->data.file_size = st.st_size; |
| @@ -651,6 +653,65 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | |||
| 651 | return dso; | 653 | return dso; |
| 652 | } | 654 | } |
| 653 | 655 | ||
| 656 | /* | ||
| 657 | * Find a matching entry and/or link current entry to RB tree. | ||
| 658 | * Either one of the dso or name parameter must be non-NULL or the | ||
| 659 | * function will not work. | ||
| 660 | */ | ||
| 661 | static struct dso *dso__findlink_by_longname(struct rb_root *root, | ||
| 662 | struct dso *dso, const char *name) | ||
| 663 | { | ||
| 664 | struct rb_node **p = &root->rb_node; | ||
| 665 | struct rb_node *parent = NULL; | ||
| 666 | |||
| 667 | if (!name) | ||
| 668 | name = dso->long_name; | ||
| 669 | /* | ||
| 670 | * Find node with the matching name | ||
| 671 | */ | ||
| 672 | while (*p) { | ||
| 673 | struct dso *this = rb_entry(*p, struct dso, rb_node); | ||
| 674 | int rc = strcmp(name, this->long_name); | ||
| 675 | |||
| 676 | parent = *p; | ||
| 677 | if (rc == 0) { | ||
| 678 | /* | ||
| 679 | * In case the new DSO is a duplicate of an existing | ||
| 680 | * one, print an one-time warning & put the new entry | ||
| 681 | * at the end of the list of duplicates. | ||
| 682 | */ | ||
| 683 | if (!dso || (dso == this)) | ||
| 684 | return this; /* Find matching dso */ | ||
| 685 | /* | ||
| 686 | * The core kernel DSOs may have duplicated long name. | ||
| 687 | * In this case, the short name should be different. | ||
| 688 | * Comparing the short names to differentiate the DSOs. | ||
| 689 | */ | ||
| 690 | rc = strcmp(dso->short_name, this->short_name); | ||
| 691 | if (rc == 0) { | ||
| 692 | pr_err("Duplicated dso name: %s\n", name); | ||
| 693 | return NULL; | ||
| 694 | } | ||
| 695 | } | ||
| 696 | if (rc < 0) | ||
| 697 | p = &parent->rb_left; | ||
| 698 | else | ||
| 699 | p = &parent->rb_right; | ||
| 700 | } | ||
| 701 | if (dso) { | ||
| 702 | /* Add new node and rebalance tree */ | ||
| 703 | rb_link_node(&dso->rb_node, parent, p); | ||
| 704 | rb_insert_color(&dso->rb_node, root); | ||
| 705 | } | ||
| 706 | return NULL; | ||
| 707 | } | ||
| 708 | |||
| 709 | static inline struct dso * | ||
| 710 | dso__find_by_longname(const struct rb_root *root, const char *name) | ||
| 711 | { | ||
| 712 | return dso__findlink_by_longname((struct rb_root *)root, NULL, name); | ||
| 713 | } | ||
| 714 | |||
| 654 | void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) | 715 | void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) |
| 655 | { | 716 | { |
| 656 | if (name == NULL) | 717 | if (name == NULL) |
| @@ -753,6 +814,7 @@ struct dso *dso__new(const char *name) | |||
| 753 | dso->a2l_fails = 1; | 814 | dso->a2l_fails = 1; |
| 754 | dso->kernel = DSO_TYPE_USER; | 815 | dso->kernel = DSO_TYPE_USER; |
| 755 | dso->needs_swap = DSO_SWAP__UNSET; | 816 | dso->needs_swap = DSO_SWAP__UNSET; |
| 817 | RB_CLEAR_NODE(&dso->rb_node); | ||
| 756 | INIT_LIST_HEAD(&dso->node); | 818 | INIT_LIST_HEAD(&dso->node); |
| 757 | INIT_LIST_HEAD(&dso->data.open_entry); | 819 | INIT_LIST_HEAD(&dso->data.open_entry); |
| 758 | } | 820 | } |
| @@ -763,6 +825,10 @@ struct dso *dso__new(const char *name) | |||
| 763 | void dso__delete(struct dso *dso) | 825 | void dso__delete(struct dso *dso) |
| 764 | { | 826 | { |
| 765 | int i; | 827 | int i; |
| 828 | |||
| 829 | if (!RB_EMPTY_NODE(&dso->rb_node)) | ||
| 830 | pr_err("DSO %s is still in rbtree when being deleted!\n", | ||
| 831 | dso->long_name); | ||
| 766 | for (i = 0; i < MAP__NR_TYPES; ++i) | 832 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 767 | symbols__delete(&dso->symbols[i]); | 833 | symbols__delete(&dso->symbols[i]); |
| 768 | 834 | ||
| @@ -849,35 +915,34 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
| 849 | return have_build_id; | 915 | return have_build_id; |
| 850 | } | 916 | } |
| 851 | 917 | ||
| 852 | void dsos__add(struct list_head *head, struct dso *dso) | 918 | void dsos__add(struct dsos *dsos, struct dso *dso) |
| 853 | { | 919 | { |
| 854 | list_add_tail(&dso->node, head); | 920 | list_add_tail(&dso->node, &dsos->head); |
| 921 | dso__findlink_by_longname(&dsos->root, dso, NULL); | ||
| 855 | } | 922 | } |
| 856 | 923 | ||
| 857 | struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short) | 924 | struct dso *dsos__find(const struct dsos *dsos, const char *name, |
| 925 | bool cmp_short) | ||
| 858 | { | 926 | { |
| 859 | struct dso *pos; | 927 | struct dso *pos; |
| 860 | 928 | ||
| 861 | if (cmp_short) { | 929 | if (cmp_short) { |
| 862 | list_for_each_entry(pos, head, node) | 930 | list_for_each_entry(pos, &dsos->head, node) |
| 863 | if (strcmp(pos->short_name, name) == 0) | 931 | if (strcmp(pos->short_name, name) == 0) |
| 864 | return pos; | 932 | return pos; |
| 865 | return NULL; | 933 | return NULL; |
| 866 | } | 934 | } |
| 867 | list_for_each_entry(pos, head, node) | 935 | return dso__find_by_longname(&dsos->root, name); |
| 868 | if (strcmp(pos->long_name, name) == 0) | ||
| 869 | return pos; | ||
| 870 | return NULL; | ||
| 871 | } | 936 | } |
| 872 | 937 | ||
| 873 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | 938 | struct dso *__dsos__findnew(struct dsos *dsos, const char *name) |
| 874 | { | 939 | { |
| 875 | struct dso *dso = dsos__find(head, name, false); | 940 | struct dso *dso = dsos__find(dsos, name, false); |
| 876 | 941 | ||
| 877 | if (!dso) { | 942 | if (!dso) { |
| 878 | dso = dso__new(name); | 943 | dso = dso__new(name); |
| 879 | if (dso != NULL) { | 944 | if (dso != NULL) { |
| 880 | dsos__add(head, dso); | 945 | dsos__add(dsos, dso); |
| 881 | dso__set_basename(dso); | 946 | dso__set_basename(dso); |
| 882 | } | 947 | } |
| 883 | } | 948 | } |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 5e463c0964d4..acb651acc7fd 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
| @@ -90,8 +90,18 @@ struct dso_cache { | |||
| 90 | char data[0]; | 90 | char data[0]; |
| 91 | }; | 91 | }; |
| 92 | 92 | ||
| 93 | /* | ||
| 94 | * DSOs are put into both a list for fast iteration and rbtree for fast | ||
| 95 | * long name lookup. | ||
| 96 | */ | ||
| 97 | struct dsos { | ||
| 98 | struct list_head head; | ||
| 99 | struct rb_root root; /* rbtree root sorted by long name */ | ||
| 100 | }; | ||
| 101 | |||
| 93 | struct dso { | 102 | struct dso { |
| 94 | struct list_head node; | 103 | struct list_head node; |
| 104 | struct rb_node rb_node; /* rbtree node sorted by long name */ | ||
| 95 | struct rb_root symbols[MAP__NR_TYPES]; | 105 | struct rb_root symbols[MAP__NR_TYPES]; |
| 96 | struct rb_root symbol_names[MAP__NR_TYPES]; | 106 | struct rb_root symbol_names[MAP__NR_TYPES]; |
| 97 | void *a2l; | 107 | void *a2l; |
| @@ -224,10 +234,10 @@ struct map *dso__new_map(const char *name); | |||
| 224 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | 234 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, |
| 225 | const char *short_name, int dso_type); | 235 | const char *short_name, int dso_type); |
| 226 | 236 | ||
| 227 | void dsos__add(struct list_head *head, struct dso *dso); | 237 | void dsos__add(struct dsos *dsos, struct dso *dso); |
| 228 | struct dso *dsos__find(const struct list_head *head, const char *name, | 238 | struct dso *dsos__find(const struct dsos *dsos, const char *name, |
| 229 | bool cmp_short); | 239 | bool cmp_short); |
| 230 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | 240 | struct dso *__dsos__findnew(struct dsos *dsos, const char *name); |
| 231 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | 241 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); |
| 232 | 242 | ||
| 233 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | 243 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1398c83d896d..4af6b279e34a 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -558,13 +558,17 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | |||
| 558 | struct map *map; | 558 | struct map *map; |
| 559 | struct kmap *kmap; | 559 | struct kmap *kmap; |
| 560 | int err; | 560 | int err; |
| 561 | union perf_event *event; | ||
| 562 | |||
| 563 | if (machine->vmlinux_maps[0] == NULL) | ||
| 564 | return -1; | ||
| 565 | |||
| 561 | /* | 566 | /* |
| 562 | * We should get this from /sys/kernel/sections/.text, but till that is | 567 | * We should get this from /sys/kernel/sections/.text, but till that is |
| 563 | * available use this, and after it is use this as a fallback for older | 568 | * available use this, and after it is use this as a fallback for older |
| 564 | * kernels. | 569 | * kernels. |
| 565 | */ | 570 | */ |
| 566 | union perf_event *event = zalloc((sizeof(event->mmap) + | 571 | event = zalloc((sizeof(event->mmap) + machine->id_hdr_size)); |
| 567 | machine->id_hdr_size)); | ||
| 568 | if (event == NULL) { | 572 | if (event == NULL) { |
| 569 | pr_debug("Not enough memory synthesizing mmap event " | 573 | pr_debug("Not enough memory synthesizing mmap event " |
| 570 | "for kernel modules\n"); | 574 | "for kernel modules\n"); |
| @@ -784,9 +788,9 @@ try_again: | |||
| 784 | * "[vdso]" dso, but for now lets use the old trick of looking | 788 | * "[vdso]" dso, but for now lets use the old trick of looking |
| 785 | * in the whole kernel symbol list. | 789 | * in the whole kernel symbol list. |
| 786 | */ | 790 | */ |
| 787 | if ((long long)al->addr < 0 && | 791 | if (cpumode == PERF_RECORD_MISC_USER && machine && |
| 788 | cpumode == PERF_RECORD_MISC_USER && | 792 | mg != &machine->kmaps && |
| 789 | machine && mg != &machine->kmaps) { | 793 | machine__kernel_ip(machine, al->addr)) { |
| 790 | mg = &machine->kmaps; | 794 | mg = &machine->kmaps; |
| 791 | load_map = true; | 795 | load_map = true; |
| 792 | goto try_again; | 796 | goto try_again; |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 94d6976180da..7eb7107731ec 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -156,6 +156,8 @@ struct perf_sample { | |||
| 156 | u32 cpu; | 156 | u32 cpu; |
| 157 | u32 raw_size; | 157 | u32 raw_size; |
| 158 | u64 data_src; | 158 | u64 data_src; |
| 159 | u32 flags; | ||
| 160 | u16 insn_len; | ||
| 159 | void *raw_data; | 161 | void *raw_data; |
| 160 | struct ip_callchain *callchain; | 162 | struct ip_callchain *callchain; |
| 161 | struct branch_stack *branch_stack; | 163 | struct branch_stack *branch_stack; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 814e954c1318..3cebc9a8d52e 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
| @@ -25,6 +25,9 @@ | |||
| 25 | #include <linux/bitops.h> | 25 | #include <linux/bitops.h> |
| 26 | #include <linux/hash.h> | 26 | #include <linux/hash.h> |
| 27 | 27 | ||
| 28 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx); | ||
| 29 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx); | ||
| 30 | |||
| 28 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 31 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
| 29 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) | 32 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) |
| 30 | 33 | ||
| @@ -37,6 +40,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | |||
| 37 | INIT_HLIST_HEAD(&evlist->heads[i]); | 40 | INIT_HLIST_HEAD(&evlist->heads[i]); |
| 38 | INIT_LIST_HEAD(&evlist->entries); | 41 | INIT_LIST_HEAD(&evlist->entries); |
| 39 | perf_evlist__set_maps(evlist, cpus, threads); | 42 | perf_evlist__set_maps(evlist, cpus, threads); |
| 43 | fdarray__init(&evlist->pollfd, 64); | ||
| 40 | evlist->workload.pid = -1; | 44 | evlist->workload.pid = -1; |
| 41 | } | 45 | } |
| 42 | 46 | ||
| @@ -102,7 +106,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist) | |||
| 102 | void perf_evlist__exit(struct perf_evlist *evlist) | 106 | void perf_evlist__exit(struct perf_evlist *evlist) |
| 103 | { | 107 | { |
| 104 | zfree(&evlist->mmap); | 108 | zfree(&evlist->mmap); |
| 105 | zfree(&evlist->pollfd); | 109 | fdarray__exit(&evlist->pollfd); |
| 106 | } | 110 | } |
| 107 | 111 | ||
| 108 | void perf_evlist__delete(struct perf_evlist *evlist) | 112 | void perf_evlist__delete(struct perf_evlist *evlist) |
| @@ -122,6 +126,7 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | |||
| 122 | { | 126 | { |
| 123 | list_add_tail(&entry->node, &evlist->entries); | 127 | list_add_tail(&entry->node, &evlist->entries); |
| 124 | entry->idx = evlist->nr_entries; | 128 | entry->idx = evlist->nr_entries; |
| 129 | entry->tracking = !entry->idx; | ||
| 125 | 130 | ||
| 126 | if (!evlist->nr_entries++) | 131 | if (!evlist->nr_entries++) |
| 127 | perf_evlist__set_id_pos(evlist); | 132 | perf_evlist__set_id_pos(evlist); |
| @@ -265,17 +270,27 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist, | |||
| 265 | return 0; | 270 | return 0; |
| 266 | } | 271 | } |
| 267 | 272 | ||
| 273 | static int perf_evlist__nr_threads(struct perf_evlist *evlist, | ||
| 274 | struct perf_evsel *evsel) | ||
| 275 | { | ||
| 276 | if (evsel->system_wide) | ||
| 277 | return 1; | ||
| 278 | else | ||
| 279 | return thread_map__nr(evlist->threads); | ||
| 280 | } | ||
| 281 | |||
| 268 | void perf_evlist__disable(struct perf_evlist *evlist) | 282 | void perf_evlist__disable(struct perf_evlist *evlist) |
| 269 | { | 283 | { |
| 270 | int cpu, thread; | 284 | int cpu, thread; |
| 271 | struct perf_evsel *pos; | 285 | struct perf_evsel *pos; |
| 272 | int nr_cpus = cpu_map__nr(evlist->cpus); | 286 | int nr_cpus = cpu_map__nr(evlist->cpus); |
| 273 | int nr_threads = thread_map__nr(evlist->threads); | 287 | int nr_threads; |
| 274 | 288 | ||
| 275 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 289 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
| 276 | evlist__for_each(evlist, pos) { | 290 | evlist__for_each(evlist, pos) { |
| 277 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 291 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
| 278 | continue; | 292 | continue; |
| 293 | nr_threads = perf_evlist__nr_threads(evlist, pos); | ||
| 279 | for (thread = 0; thread < nr_threads; thread++) | 294 | for (thread = 0; thread < nr_threads; thread++) |
| 280 | ioctl(FD(pos, cpu, thread), | 295 | ioctl(FD(pos, cpu, thread), |
| 281 | PERF_EVENT_IOC_DISABLE, 0); | 296 | PERF_EVENT_IOC_DISABLE, 0); |
| @@ -288,12 +303,13 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
| 288 | int cpu, thread; | 303 | int cpu, thread; |
| 289 | struct perf_evsel *pos; | 304 | struct perf_evsel *pos; |
| 290 | int nr_cpus = cpu_map__nr(evlist->cpus); | 305 | int nr_cpus = cpu_map__nr(evlist->cpus); |
| 291 | int nr_threads = thread_map__nr(evlist->threads); | 306 | int nr_threads; |
| 292 | 307 | ||
| 293 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 308 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
| 294 | evlist__for_each(evlist, pos) { | 309 | evlist__for_each(evlist, pos) { |
| 295 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 310 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
| 296 | continue; | 311 | continue; |
| 312 | nr_threads = perf_evlist__nr_threads(evlist, pos); | ||
| 297 | for (thread = 0; thread < nr_threads; thread++) | 313 | for (thread = 0; thread < nr_threads; thread++) |
| 298 | ioctl(FD(pos, cpu, thread), | 314 | ioctl(FD(pos, cpu, thread), |
| 299 | PERF_EVENT_IOC_ENABLE, 0); | 315 | PERF_EVENT_IOC_ENABLE, 0); |
| @@ -305,12 +321,14 @@ int perf_evlist__disable_event(struct perf_evlist *evlist, | |||
| 305 | struct perf_evsel *evsel) | 321 | struct perf_evsel *evsel) |
| 306 | { | 322 | { |
| 307 | int cpu, thread, err; | 323 | int cpu, thread, err; |
| 324 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
| 325 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
| 308 | 326 | ||
| 309 | if (!evsel->fd) | 327 | if (!evsel->fd) |
| 310 | return 0; | 328 | return 0; |
| 311 | 329 | ||
| 312 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 330 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
| 313 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 331 | for (thread = 0; thread < nr_threads; thread++) { |
| 314 | err = ioctl(FD(evsel, cpu, thread), | 332 | err = ioctl(FD(evsel, cpu, thread), |
| 315 | PERF_EVENT_IOC_DISABLE, 0); | 333 | PERF_EVENT_IOC_DISABLE, 0); |
| 316 | if (err) | 334 | if (err) |
| @@ -324,12 +342,14 @@ int perf_evlist__enable_event(struct perf_evlist *evlist, | |||
| 324 | struct perf_evsel *evsel) | 342 | struct perf_evsel *evsel) |
| 325 | { | 343 | { |
| 326 | int cpu, thread, err; | 344 | int cpu, thread, err; |
| 345 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
| 346 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
| 327 | 347 | ||
| 328 | if (!evsel->fd) | 348 | if (!evsel->fd) |
| 329 | return -EINVAL; | 349 | return -EINVAL; |
| 330 | 350 | ||
| 331 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 351 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
| 332 | for (thread = 0; thread < evlist->threads->nr; thread++) { | 352 | for (thread = 0; thread < nr_threads; thread++) { |
| 333 | err = ioctl(FD(evsel, cpu, thread), | 353 | err = ioctl(FD(evsel, cpu, thread), |
| 334 | PERF_EVENT_IOC_ENABLE, 0); | 354 | PERF_EVENT_IOC_ENABLE, 0); |
| 335 | if (err) | 355 | if (err) |
| @@ -339,21 +359,111 @@ int perf_evlist__enable_event(struct perf_evlist *evlist, | |||
| 339 | return 0; | 359 | return 0; |
| 340 | } | 360 | } |
| 341 | 361 | ||
| 342 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 362 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, |
| 363 | struct perf_evsel *evsel, int cpu) | ||
| 364 | { | ||
| 365 | int thread, err; | ||
| 366 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | ||
| 367 | |||
| 368 | if (!evsel->fd) | ||
| 369 | return -EINVAL; | ||
| 370 | |||
| 371 | for (thread = 0; thread < nr_threads; thread++) { | ||
| 372 | err = ioctl(FD(evsel, cpu, thread), | ||
| 373 | PERF_EVENT_IOC_ENABLE, 0); | ||
| 374 | if (err) | ||
| 375 | return err; | ||
| 376 | } | ||
| 377 | return 0; | ||
| 378 | } | ||
| 379 | |||
| 380 | static int perf_evlist__enable_event_thread(struct perf_evlist *evlist, | ||
| 381 | struct perf_evsel *evsel, | ||
| 382 | int thread) | ||
| 383 | { | ||
| 384 | int cpu, err; | ||
| 385 | int nr_cpus = cpu_map__nr(evlist->cpus); | ||
| 386 | |||
| 387 | if (!evsel->fd) | ||
| 388 | return -EINVAL; | ||
| 389 | |||
| 390 | for (cpu = 0; cpu < nr_cpus; cpu++) { | ||
| 391 | err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); | ||
| 392 | if (err) | ||
| 393 | return err; | ||
| 394 | } | ||
| 395 | return 0; | ||
| 396 | } | ||
| 397 | |||
| 398 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, | ||
| 399 | struct perf_evsel *evsel, int idx) | ||
| 400 | { | ||
| 401 | bool per_cpu_mmaps = !cpu_map__empty(evlist->cpus); | ||
| 402 | |||
| 403 | if (per_cpu_mmaps) | ||
| 404 | return perf_evlist__enable_event_cpu(evlist, evsel, idx); | ||
| 405 | else | ||
| 406 | return perf_evlist__enable_event_thread(evlist, evsel, idx); | ||
| 407 | } | ||
| 408 | |||
| 409 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | ||
| 343 | { | 410 | { |
| 344 | int nr_cpus = cpu_map__nr(evlist->cpus); | 411 | int nr_cpus = cpu_map__nr(evlist->cpus); |
| 345 | int nr_threads = thread_map__nr(evlist->threads); | 412 | int nr_threads = thread_map__nr(evlist->threads); |
| 346 | int nfds = nr_cpus * nr_threads * evlist->nr_entries; | 413 | int nfds = 0; |
| 347 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 414 | struct perf_evsel *evsel; |
| 348 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | 415 | |
| 416 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
| 417 | if (evsel->system_wide) | ||
| 418 | nfds += nr_cpus; | ||
| 419 | else | ||
| 420 | nfds += nr_cpus * nr_threads; | ||
| 421 | } | ||
| 422 | |||
| 423 | if (fdarray__available_entries(&evlist->pollfd) < nfds && | ||
| 424 | fdarray__grow(&evlist->pollfd, nfds) < 0) | ||
| 425 | return -ENOMEM; | ||
| 426 | |||
| 427 | return 0; | ||
| 428 | } | ||
| 429 | |||
| 430 | static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, int idx) | ||
| 431 | { | ||
| 432 | int pos = fdarray__add(&evlist->pollfd, fd, POLLIN | POLLERR | POLLHUP); | ||
| 433 | /* | ||
| 434 | * Save the idx so that when we filter out fds POLLHUP'ed we can | ||
| 435 | * close the associated evlist->mmap[] entry. | ||
| 436 | */ | ||
| 437 | if (pos >= 0) { | ||
| 438 | evlist->pollfd.priv[pos].idx = idx; | ||
| 439 | |||
| 440 | fcntl(fd, F_SETFL, O_NONBLOCK); | ||
| 441 | } | ||
| 442 | |||
| 443 | return pos; | ||
| 444 | } | ||
| 445 | |||
| 446 | int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | ||
| 447 | { | ||
| 448 | return __perf_evlist__add_pollfd(evlist, fd, -1); | ||
| 449 | } | ||
| 450 | |||
| 451 | static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd) | ||
| 452 | { | ||
| 453 | struct perf_evlist *evlist = container_of(fda, struct perf_evlist, pollfd); | ||
| 454 | |||
| 455 | perf_evlist__mmap_put(evlist, fda->priv[fd].idx); | ||
| 456 | } | ||
| 457 | |||
| 458 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask) | ||
| 459 | { | ||
| 460 | return fdarray__filter(&evlist->pollfd, revents_and_mask, | ||
| 461 | perf_evlist__munmap_filtered); | ||
| 349 | } | 462 | } |
| 350 | 463 | ||
| 351 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | 464 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout) |
| 352 | { | 465 | { |
| 353 | fcntl(fd, F_SETFL, O_NONBLOCK); | 466 | return fdarray__poll(&evlist->pollfd, timeout); |
| 354 | evlist->pollfd[evlist->nr_fds].fd = fd; | ||
| 355 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
| 356 | evlist->nr_fds++; | ||
| 357 | } | 467 | } |
| 358 | 468 | ||
| 359 | static void perf_evlist__id_hash(struct perf_evlist *evlist, | 469 | static void perf_evlist__id_hash(struct perf_evlist *evlist, |
| @@ -566,14 +676,36 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
| 566 | return event; | 676 | return event; |
| 567 | } | 677 | } |
| 568 | 678 | ||
| 679 | static bool perf_mmap__empty(struct perf_mmap *md) | ||
| 680 | { | ||
| 681 | return perf_mmap__read_head(md) != md->prev; | ||
| 682 | } | ||
| 683 | |||
| 684 | static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx) | ||
| 685 | { | ||
| 686 | ++evlist->mmap[idx].refcnt; | ||
| 687 | } | ||
| 688 | |||
| 689 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx) | ||
| 690 | { | ||
| 691 | BUG_ON(evlist->mmap[idx].refcnt == 0); | ||
| 692 | |||
| 693 | if (--evlist->mmap[idx].refcnt == 0) | ||
| 694 | __perf_evlist__munmap(evlist, idx); | ||
| 695 | } | ||
| 696 | |||
| 569 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) | 697 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) |
| 570 | { | 698 | { |
| 699 | struct perf_mmap *md = &evlist->mmap[idx]; | ||
| 700 | |||
| 571 | if (!evlist->overwrite) { | 701 | if (!evlist->overwrite) { |
| 572 | struct perf_mmap *md = &evlist->mmap[idx]; | ||
| 573 | unsigned int old = md->prev; | 702 | unsigned int old = md->prev; |
| 574 | 703 | ||
| 575 | perf_mmap__write_tail(md, old); | 704 | perf_mmap__write_tail(md, old); |
| 576 | } | 705 | } |
| 706 | |||
| 707 | if (md->refcnt == 1 && perf_mmap__empty(md)) | ||
| 708 | perf_evlist__mmap_put(evlist, idx); | ||
| 577 | } | 709 | } |
| 578 | 710 | ||
| 579 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | 711 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) |
| @@ -581,6 +713,7 @@ static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | |||
| 581 | if (evlist->mmap[idx].base != NULL) { | 713 | if (evlist->mmap[idx].base != NULL) { |
| 582 | munmap(evlist->mmap[idx].base, evlist->mmap_len); | 714 | munmap(evlist->mmap[idx].base, evlist->mmap_len); |
| 583 | evlist->mmap[idx].base = NULL; | 715 | evlist->mmap[idx].base = NULL; |
| 716 | evlist->mmap[idx].refcnt = 0; | ||
| 584 | } | 717 | } |
| 585 | } | 718 | } |
| 586 | 719 | ||
| @@ -614,6 +747,20 @@ struct mmap_params { | |||
| 614 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | 747 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, |
| 615 | struct mmap_params *mp, int fd) | 748 | struct mmap_params *mp, int fd) |
| 616 | { | 749 | { |
| 750 | /* | ||
| 751 | * The last one will be done at perf_evlist__mmap_consume(), so that we | ||
| 752 | * make sure we don't prevent tools from consuming every last event in | ||
| 753 | * the ring buffer. | ||
| 754 | * | ||
| 755 | * I.e. we can get the POLLHUP meaning that the fd doesn't exist | ||
| 756 | * anymore, but the last events for it are still in the ring buffer, | ||
| 757 | * waiting to be consumed. | ||
| 758 | * | ||
| 759 | * Tools can chose to ignore this at their own discretion, but the | ||
| 760 | * evlist layer can't just drop it when filtering events in | ||
| 761 | * perf_evlist__filter_pollfd(). | ||
| 762 | */ | ||
| 763 | evlist->mmap[idx].refcnt = 2; | ||
| 617 | evlist->mmap[idx].prev = 0; | 764 | evlist->mmap[idx].prev = 0; |
| 618 | evlist->mmap[idx].mask = mp->mask; | 765 | evlist->mmap[idx].mask = mp->mask; |
| 619 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, | 766 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, |
| @@ -625,7 +772,6 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | |||
| 625 | return -1; | 772 | return -1; |
| 626 | } | 773 | } |
| 627 | 774 | ||
| 628 | perf_evlist__add_pollfd(evlist, fd); | ||
| 629 | return 0; | 775 | return 0; |
| 630 | } | 776 | } |
| 631 | 777 | ||
| @@ -636,7 +782,12 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, | |||
| 636 | struct perf_evsel *evsel; | 782 | struct perf_evsel *evsel; |
| 637 | 783 | ||
| 638 | evlist__for_each(evlist, evsel) { | 784 | evlist__for_each(evlist, evsel) { |
| 639 | int fd = FD(evsel, cpu, thread); | 785 | int fd; |
| 786 | |||
| 787 | if (evsel->system_wide && thread) | ||
| 788 | continue; | ||
| 789 | |||
| 790 | fd = FD(evsel, cpu, thread); | ||
| 640 | 791 | ||
| 641 | if (*output == -1) { | 792 | if (*output == -1) { |
| 642 | *output = fd; | 793 | *output = fd; |
| @@ -645,6 +796,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, | |||
| 645 | } else { | 796 | } else { |
| 646 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) | 797 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) |
| 647 | return -1; | 798 | return -1; |
| 799 | |||
| 800 | perf_evlist__mmap_get(evlist, idx); | ||
| 801 | } | ||
| 802 | |||
| 803 | if (__perf_evlist__add_pollfd(evlist, fd, idx) < 0) { | ||
| 804 | perf_evlist__mmap_put(evlist, idx); | ||
| 805 | return -1; | ||
| 648 | } | 806 | } |
| 649 | 807 | ||
| 650 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 808 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
| @@ -804,7 +962,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
| 804 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | 962 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) |
| 805 | return -ENOMEM; | 963 | return -ENOMEM; |
| 806 | 964 | ||
| 807 | if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0) | 965 | if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0) |
| 808 | return -ENOMEM; | 966 | return -ENOMEM; |
| 809 | 967 | ||
| 810 | evlist->overwrite = overwrite; | 968 | evlist->overwrite = overwrite; |
| @@ -1061,6 +1219,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
| 1061 | } | 1219 | } |
| 1062 | 1220 | ||
| 1063 | if (!evlist->workload.pid) { | 1221 | if (!evlist->workload.pid) { |
| 1222 | int ret; | ||
| 1223 | |||
| 1064 | if (pipe_output) | 1224 | if (pipe_output) |
| 1065 | dup2(2, 1); | 1225 | dup2(2, 1); |
| 1066 | 1226 | ||
| @@ -1078,8 +1238,22 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
| 1078 | /* | 1238 | /* |
| 1079 | * Wait until the parent tells us to go. | 1239 | * Wait until the parent tells us to go. |
| 1080 | */ | 1240 | */ |
| 1081 | if (read(go_pipe[0], &bf, 1) == -1) | 1241 | ret = read(go_pipe[0], &bf, 1); |
| 1082 | perror("unable to read pipe"); | 1242 | /* |
| 1243 | * The parent will ask for the execvp() to be performed by | ||
| 1244 | * writing exactly one byte, in workload.cork_fd, usually via | ||
| 1245 | * perf_evlist__start_workload(). | ||
| 1246 | * | ||
| 1247 | * For cancelling the workload without actuallin running it, | ||
| 1248 | * the parent will just close workload.cork_fd, without writing | ||
| 1249 | * anything, i.e. read will return zero and we just exit() | ||
| 1250 | * here. | ||
| 1251 | */ | ||
| 1252 | if (ret != 1) { | ||
| 1253 | if (ret == -1) | ||
| 1254 | perror("unable to read pipe"); | ||
| 1255 | exit(ret); | ||
| 1256 | } | ||
| 1083 | 1257 | ||
| 1084 | execvp(argv[0], (char **)argv); | 1258 | execvp(argv[0], (char **)argv); |
| 1085 | 1259 | ||
| @@ -1202,7 +1376,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | |||
| 1202 | int err, char *buf, size_t size) | 1376 | int err, char *buf, size_t size) |
| 1203 | { | 1377 | { |
| 1204 | int printed, value; | 1378 | int printed, value; |
| 1205 | char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | 1379 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); |
| 1206 | 1380 | ||
| 1207 | switch (err) { | 1381 | switch (err) { |
| 1208 | case EACCES: | 1382 | case EACCES: |
| @@ -1250,3 +1424,19 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
| 1250 | 1424 | ||
| 1251 | list_splice(&move, &evlist->entries); | 1425 | list_splice(&move, &evlist->entries); |
| 1252 | } | 1426 | } |
| 1427 | |||
| 1428 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | ||
| 1429 | struct perf_evsel *tracking_evsel) | ||
| 1430 | { | ||
| 1431 | struct perf_evsel *evsel; | ||
| 1432 | |||
| 1433 | if (tracking_evsel->tracking) | ||
| 1434 | return; | ||
| 1435 | |||
| 1436 | evlist__for_each(evlist, evsel) { | ||
| 1437 | if (evsel != tracking_evsel) | ||
| 1438 | evsel->tracking = false; | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | tracking_evsel->tracking = true; | ||
| 1442 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index f5173cd63693..bd312b01e876 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #define __PERF_EVLIST_H 1 | 2 | #define __PERF_EVLIST_H 1 |
| 3 | 3 | ||
| 4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
| 5 | #include <api/fd/array.h> | ||
| 5 | #include <stdio.h> | 6 | #include <stdio.h> |
| 6 | #include "../perf.h" | 7 | #include "../perf.h" |
| 7 | #include "event.h" | 8 | #include "event.h" |
| @@ -17,9 +18,15 @@ struct record_opts; | |||
| 17 | #define PERF_EVLIST__HLIST_BITS 8 | 18 | #define PERF_EVLIST__HLIST_BITS 8 |
| 18 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | 19 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) |
| 19 | 20 | ||
| 21 | /** | ||
| 22 | * struct perf_mmap - perf's ring buffer mmap details | ||
| 23 | * | ||
| 24 | * @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this | ||
| 25 | */ | ||
| 20 | struct perf_mmap { | 26 | struct perf_mmap { |
| 21 | void *base; | 27 | void *base; |
| 22 | int mask; | 28 | int mask; |
| 29 | int refcnt; | ||
| 23 | unsigned int prev; | 30 | unsigned int prev; |
| 24 | char event_copy[PERF_SAMPLE_MAX_SIZE]; | 31 | char event_copy[PERF_SAMPLE_MAX_SIZE]; |
| 25 | }; | 32 | }; |
| @@ -29,7 +36,6 @@ struct perf_evlist { | |||
| 29 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | 36 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; |
| 30 | int nr_entries; | 37 | int nr_entries; |
| 31 | int nr_groups; | 38 | int nr_groups; |
| 32 | int nr_fds; | ||
| 33 | int nr_mmaps; | 39 | int nr_mmaps; |
| 34 | size_t mmap_len; | 40 | size_t mmap_len; |
| 35 | int id_pos; | 41 | int id_pos; |
| @@ -40,8 +46,8 @@ struct perf_evlist { | |||
| 40 | pid_t pid; | 46 | pid_t pid; |
| 41 | } workload; | 47 | } workload; |
| 42 | bool overwrite; | 48 | bool overwrite; |
| 49 | struct fdarray pollfd; | ||
| 43 | struct perf_mmap *mmap; | 50 | struct perf_mmap *mmap; |
| 44 | struct pollfd *pollfd; | ||
| 45 | struct thread_map *threads; | 51 | struct thread_map *threads; |
| 46 | struct cpu_map *cpus; | 52 | struct cpu_map *cpus; |
| 47 | struct perf_evsel *selected; | 53 | struct perf_evsel *selected; |
| @@ -82,7 +88,11 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | |||
| 82 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | 88 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, |
| 83 | int cpu, int thread, u64 id); | 89 | int cpu, int thread, u64 id); |
| 84 | 90 | ||
| 85 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | 91 | int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); |
| 92 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); | ||
| 93 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask); | ||
| 94 | |||
| 95 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout); | ||
| 86 | 96 | ||
| 87 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | 97 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); |
| 88 | 98 | ||
| @@ -122,6 +132,8 @@ int perf_evlist__disable_event(struct perf_evlist *evlist, | |||
| 122 | struct perf_evsel *evsel); | 132 | struct perf_evsel *evsel); |
| 123 | int perf_evlist__enable_event(struct perf_evlist *evlist, | 133 | int perf_evlist__enable_event(struct perf_evlist *evlist, |
| 124 | struct perf_evsel *evsel); | 134 | struct perf_evsel *evsel); |
| 135 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, | ||
| 136 | struct perf_evsel *evsel, int idx); | ||
| 125 | 137 | ||
| 126 | void perf_evlist__set_selected(struct perf_evlist *evlist, | 138 | void perf_evlist__set_selected(struct perf_evlist *evlist, |
| 127 | struct perf_evsel *evsel); | 139 | struct perf_evsel *evsel); |
| @@ -262,4 +274,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
| 262 | #define evlist__for_each_safe(evlist, tmp, evsel) \ | 274 | #define evlist__for_each_safe(evlist, tmp, evsel) \ |
| 263 | __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) | 275 | __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) |
| 264 | 276 | ||
| 277 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | ||
| 278 | struct perf_evsel *tracking_evsel); | ||
| 279 | |||
| 265 | #endif /* __PERF_EVLIST_H */ | 280 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 21a373ebea22..e0868a901c4a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -162,6 +162,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
| 162 | struct perf_event_attr *attr, int idx) | 162 | struct perf_event_attr *attr, int idx) |
| 163 | { | 163 | { |
| 164 | evsel->idx = idx; | 164 | evsel->idx = idx; |
| 165 | evsel->tracking = !idx; | ||
| 165 | evsel->attr = *attr; | 166 | evsel->attr = *attr; |
| 166 | evsel->leader = evsel; | 167 | evsel->leader = evsel; |
| 167 | evsel->unit = ""; | 168 | evsel->unit = ""; |
| @@ -502,20 +503,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) | |||
| 502 | } | 503 | } |
| 503 | 504 | ||
| 504 | static void | 505 | static void |
| 505 | perf_evsel__config_callgraph(struct perf_evsel *evsel, | 506 | perf_evsel__config_callgraph(struct perf_evsel *evsel) |
| 506 | struct record_opts *opts) | ||
| 507 | { | 507 | { |
| 508 | bool function = perf_evsel__is_function_event(evsel); | 508 | bool function = perf_evsel__is_function_event(evsel); |
| 509 | struct perf_event_attr *attr = &evsel->attr; | 509 | struct perf_event_attr *attr = &evsel->attr; |
| 510 | 510 | ||
| 511 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); | 511 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); |
| 512 | 512 | ||
| 513 | if (opts->call_graph == CALLCHAIN_DWARF) { | 513 | if (callchain_param.record_mode == CALLCHAIN_DWARF) { |
| 514 | if (!function) { | 514 | if (!function) { |
| 515 | perf_evsel__set_sample_bit(evsel, REGS_USER); | 515 | perf_evsel__set_sample_bit(evsel, REGS_USER); |
| 516 | perf_evsel__set_sample_bit(evsel, STACK_USER); | 516 | perf_evsel__set_sample_bit(evsel, STACK_USER); |
| 517 | attr->sample_regs_user = PERF_REGS_MASK; | 517 | attr->sample_regs_user = PERF_REGS_MASK; |
| 518 | attr->sample_stack_user = opts->stack_dump_size; | 518 | attr->sample_stack_user = callchain_param.dump_size; |
| 519 | attr->exclude_callchain_user = 1; | 519 | attr->exclude_callchain_user = 1; |
| 520 | } else { | 520 | } else { |
| 521 | pr_info("Cannot use DWARF unwind for function trace event," | 521 | pr_info("Cannot use DWARF unwind for function trace event," |
| @@ -561,7 +561,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 561 | { | 561 | { |
| 562 | struct perf_evsel *leader = evsel->leader; | 562 | struct perf_evsel *leader = evsel->leader; |
| 563 | struct perf_event_attr *attr = &evsel->attr; | 563 | struct perf_event_attr *attr = &evsel->attr; |
| 564 | int track = !evsel->idx; /* only the first counter needs these */ | 564 | int track = evsel->tracking; |
| 565 | bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; | 565 | bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; |
| 566 | 566 | ||
| 567 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; | 567 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; |
| @@ -624,8 +624,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 624 | attr->mmap_data = track; | 624 | attr->mmap_data = track; |
| 625 | } | 625 | } |
| 626 | 626 | ||
| 627 | if (opts->call_graph_enabled && !evsel->no_aux_samples) | 627 | if (callchain_param.enabled && !evsel->no_aux_samples) |
| 628 | perf_evsel__config_callgraph(evsel, opts); | 628 | perf_evsel__config_callgraph(evsel); |
| 629 | 629 | ||
| 630 | if (target__has_cpu(&opts->target)) | 630 | if (target__has_cpu(&opts->target)) |
| 631 | perf_evsel__set_sample_bit(evsel, CPU); | 631 | perf_evsel__set_sample_bit(evsel, CPU); |
| @@ -633,9 +633,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 633 | if (opts->period) | 633 | if (opts->period) |
| 634 | perf_evsel__set_sample_bit(evsel, PERIOD); | 634 | perf_evsel__set_sample_bit(evsel, PERIOD); |
| 635 | 635 | ||
| 636 | if (!perf_missing_features.sample_id_all && | 636 | /* |
| 637 | (opts->sample_time || !opts->no_inherit || | 637 | * When the user explicitely disabled time don't force it here. |
| 638 | target__has_cpu(&opts->target) || per_cpu)) | 638 | */ |
| 639 | if (opts->sample_time && | ||
| 640 | (!perf_missing_features.sample_id_all && | ||
| 641 | (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu))) | ||
| 639 | perf_evsel__set_sample_bit(evsel, TIME); | 642 | perf_evsel__set_sample_bit(evsel, TIME); |
| 640 | 643 | ||
| 641 | if (opts->raw_samples && !evsel->no_aux_samples) { | 644 | if (opts->raw_samples && !evsel->no_aux_samples) { |
| @@ -692,6 +695,10 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 692 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 695 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
| 693 | { | 696 | { |
| 694 | int cpu, thread; | 697 | int cpu, thread; |
| 698 | |||
| 699 | if (evsel->system_wide) | ||
| 700 | nthreads = 1; | ||
| 701 | |||
| 695 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); | 702 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); |
| 696 | 703 | ||
| 697 | if (evsel->fd) { | 704 | if (evsel->fd) { |
| @@ -710,6 +717,9 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea | |||
| 710 | { | 717 | { |
| 711 | int cpu, thread; | 718 | int cpu, thread; |
| 712 | 719 | ||
| 720 | if (evsel->system_wide) | ||
| 721 | nthreads = 1; | ||
| 722 | |||
| 713 | for (cpu = 0; cpu < ncpus; cpu++) { | 723 | for (cpu = 0; cpu < ncpus; cpu++) { |
| 714 | for (thread = 0; thread < nthreads; thread++) { | 724 | for (thread = 0; thread < nthreads; thread++) { |
| 715 | int fd = FD(evsel, cpu, thread), | 725 | int fd = FD(evsel, cpu, thread), |
| @@ -740,6 +750,9 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
| 740 | 750 | ||
| 741 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 751 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
| 742 | { | 752 | { |
| 753 | if (evsel->system_wide) | ||
| 754 | nthreads = 1; | ||
| 755 | |||
| 743 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | 756 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); |
| 744 | if (evsel->sample_id == NULL) | 757 | if (evsel->sample_id == NULL) |
| 745 | return -ENOMEM; | 758 | return -ENOMEM; |
| @@ -784,6 +797,9 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
| 784 | { | 797 | { |
| 785 | int cpu, thread; | 798 | int cpu, thread; |
| 786 | 799 | ||
| 800 | if (evsel->system_wide) | ||
| 801 | nthreads = 1; | ||
| 802 | |||
| 787 | for (cpu = 0; cpu < ncpus; cpu++) | 803 | for (cpu = 0; cpu < ncpus; cpu++) |
| 788 | for (thread = 0; thread < nthreads; ++thread) { | 804 | for (thread = 0; thread < nthreads; ++thread) { |
| 789 | close(FD(evsel, cpu, thread)); | 805 | close(FD(evsel, cpu, thread)); |
| @@ -872,6 +888,9 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
| 872 | int cpu, thread; | 888 | int cpu, thread; |
| 873 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; | 889 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; |
| 874 | 890 | ||
| 891 | if (evsel->system_wide) | ||
| 892 | nthreads = 1; | ||
| 893 | |||
| 875 | aggr->val = aggr->ena = aggr->run = 0; | 894 | aggr->val = aggr->ena = aggr->run = 0; |
| 876 | 895 | ||
| 877 | for (cpu = 0; cpu < ncpus; cpu++) { | 896 | for (cpu = 0; cpu < ncpus; cpu++) { |
| @@ -994,13 +1013,18 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | |||
| 994 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 1013 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 995 | struct thread_map *threads) | 1014 | struct thread_map *threads) |
| 996 | { | 1015 | { |
| 997 | int cpu, thread; | 1016 | int cpu, thread, nthreads; |
| 998 | unsigned long flags = PERF_FLAG_FD_CLOEXEC; | 1017 | unsigned long flags = PERF_FLAG_FD_CLOEXEC; |
| 999 | int pid = -1, err; | 1018 | int pid = -1, err; |
| 1000 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; | 1019 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; |
| 1001 | 1020 | ||
| 1021 | if (evsel->system_wide) | ||
| 1022 | nthreads = 1; | ||
| 1023 | else | ||
| 1024 | nthreads = threads->nr; | ||
| 1025 | |||
| 1002 | if (evsel->fd == NULL && | 1026 | if (evsel->fd == NULL && |
| 1003 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 1027 | perf_evsel__alloc_fd(evsel, cpus->nr, nthreads) < 0) |
| 1004 | return -ENOMEM; | 1028 | return -ENOMEM; |
| 1005 | 1029 | ||
| 1006 | if (evsel->cgrp) { | 1030 | if (evsel->cgrp) { |
| @@ -1024,10 +1048,10 @@ retry_sample_id: | |||
| 1024 | 1048 | ||
| 1025 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 1049 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
| 1026 | 1050 | ||
| 1027 | for (thread = 0; thread < threads->nr; thread++) { | 1051 | for (thread = 0; thread < nthreads; thread++) { |
| 1028 | int group_fd; | 1052 | int group_fd; |
| 1029 | 1053 | ||
| 1030 | if (!evsel->cgrp) | 1054 | if (!evsel->cgrp && !evsel->system_wide) |
| 1031 | pid = threads->map[thread]; | 1055 | pid = threads->map[thread]; |
| 1032 | 1056 | ||
| 1033 | group_fd = get_group_fd(evsel, cpu, thread); | 1057 | group_fd = get_group_fd(evsel, cpu, thread); |
| @@ -1100,7 +1124,7 @@ out_close: | |||
| 1100 | close(FD(evsel, cpu, thread)); | 1124 | close(FD(evsel, cpu, thread)); |
| 1101 | FD(evsel, cpu, thread) = -1; | 1125 | FD(evsel, cpu, thread) = -1; |
| 1102 | } | 1126 | } |
| 1103 | thread = threads->nr; | 1127 | thread = nthreads; |
| 1104 | } while (--cpu >= 0); | 1128 | } while (--cpu >= 0); |
| 1105 | return err; | 1129 | return err; |
| 1106 | } | 1130 | } |
| @@ -2002,6 +2026,8 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | |||
| 2002 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | 2026 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, |
| 2003 | int err, char *msg, size_t size) | 2027 | int err, char *msg, size_t size) |
| 2004 | { | 2028 | { |
| 2029 | char sbuf[STRERR_BUFSIZE]; | ||
| 2030 | |||
| 2005 | switch (err) { | 2031 | switch (err) { |
| 2006 | case EPERM: | 2032 | case EPERM: |
| 2007 | case EACCES: | 2033 | case EACCES: |
| @@ -2036,13 +2062,20 @@ int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | |||
| 2036 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); | 2062 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); |
| 2037 | #endif | 2063 | #endif |
| 2038 | break; | 2064 | break; |
| 2065 | case EBUSY: | ||
| 2066 | if (find_process("oprofiled")) | ||
| 2067 | return scnprintf(msg, size, | ||
| 2068 | "The PMU counters are busy/taken by another profiler.\n" | ||
| 2069 | "We found oprofile daemon running, please stop it and try again."); | ||
| 2070 | break; | ||
| 2039 | default: | 2071 | default: |
| 2040 | break; | 2072 | break; |
| 2041 | } | 2073 | } |
| 2042 | 2074 | ||
| 2043 | return scnprintf(msg, size, | 2075 | return scnprintf(msg, size, |
| 2044 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n" | 2076 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" |
| 2045 | "/bin/dmesg may provide additional information.\n" | 2077 | "/bin/dmesg may provide additional information.\n" |
| 2046 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", | 2078 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", |
| 2047 | err, strerror(err), perf_evsel__name(evsel)); | 2079 | err, strerror_r(err, sbuf, sizeof(sbuf)), |
| 2080 | perf_evsel__name(evsel)); | ||
| 2048 | } | 2081 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index d7f93ce0ebc1..7bc314be6a7b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -85,6 +85,8 @@ struct perf_evsel { | |||
| 85 | bool needs_swap; | 85 | bool needs_swap; |
| 86 | bool no_aux_samples; | 86 | bool no_aux_samples; |
| 87 | bool immediate; | 87 | bool immediate; |
| 88 | bool system_wide; | ||
| 89 | bool tracking; | ||
| 88 | /* parse modifier helper */ | 90 | /* parse modifier helper */ |
| 89 | int exclude_GH; | 91 | int exclude_GH; |
| 90 | int nr_members; | 92 | int nr_members; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 158c787ce0c4..ce0de00399da 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -214,11 +214,11 @@ static int machine__hit_all_dsos(struct machine *machine) | |||
| 214 | { | 214 | { |
| 215 | int err; | 215 | int err; |
| 216 | 216 | ||
| 217 | err = __dsos__hit_all(&machine->kernel_dsos); | 217 | err = __dsos__hit_all(&machine->kernel_dsos.head); |
| 218 | if (err) | 218 | if (err) |
| 219 | return err; | 219 | return err; |
| 220 | 220 | ||
| 221 | return __dsos__hit_all(&machine->user_dsos); | 221 | return __dsos__hit_all(&machine->user_dsos.head); |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | int dsos__hit_all(struct perf_session *session) | 224 | int dsos__hit_all(struct perf_session *session) |
| @@ -288,11 +288,12 @@ static int machine__write_buildid_table(struct machine *machine, int fd) | |||
| 288 | umisc = PERF_RECORD_MISC_GUEST_USER; | 288 | umisc = PERF_RECORD_MISC_GUEST_USER; |
| 289 | } | 289 | } |
| 290 | 290 | ||
| 291 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine, | 291 | err = __dsos__write_buildid_table(&machine->kernel_dsos.head, machine, |
| 292 | machine->pid, kmisc, fd); | 292 | machine->pid, kmisc, fd); |
| 293 | if (err == 0) | 293 | if (err == 0) |
| 294 | err = __dsos__write_buildid_table(&machine->user_dsos, machine, | 294 | err = __dsos__write_buildid_table(&machine->user_dsos.head, |
| 295 | machine->pid, umisc, fd); | 295 | machine, machine->pid, umisc, |
| 296 | fd); | ||
| 296 | return err; | 297 | return err; |
| 297 | } | 298 | } |
| 298 | 299 | ||
| @@ -455,9 +456,10 @@ static int __dsos__cache_build_ids(struct list_head *head, | |||
| 455 | 456 | ||
| 456 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | 457 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) |
| 457 | { | 458 | { |
| 458 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine, | 459 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos.head, machine, |
| 459 | debugdir); | 460 | debugdir); |
| 460 | ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir); | 461 | ret |= __dsos__cache_build_ids(&machine->user_dsos.head, machine, |
| 462 | debugdir); | ||
| 461 | return ret; | 463 | return ret; |
| 462 | } | 464 | } |
| 463 | 465 | ||
| @@ -483,8 +485,10 @@ static int perf_session__cache_build_ids(struct perf_session *session) | |||
| 483 | 485 | ||
| 484 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) | 486 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) |
| 485 | { | 487 | { |
| 486 | bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits); | 488 | bool ret; |
| 487 | ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits); | 489 | |
| 490 | ret = __dsos__read_build_ids(&machine->kernel_dsos.head, with_hits); | ||
| 491 | ret |= __dsos__read_build_ids(&machine->user_dsos.head, with_hits); | ||
| 488 | return ret; | 492 | return ret; |
| 489 | } | 493 | } |
| 490 | 494 | ||
| @@ -1548,7 +1552,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
| 1548 | struct perf_session *session) | 1552 | struct perf_session *session) |
| 1549 | { | 1553 | { |
| 1550 | int err = -1; | 1554 | int err = -1; |
| 1551 | struct list_head *head; | 1555 | struct dsos *dsos; |
| 1552 | struct machine *machine; | 1556 | struct machine *machine; |
| 1553 | u16 misc; | 1557 | u16 misc; |
| 1554 | struct dso *dso; | 1558 | struct dso *dso; |
| @@ -1563,22 +1567,22 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
| 1563 | switch (misc) { | 1567 | switch (misc) { |
| 1564 | case PERF_RECORD_MISC_KERNEL: | 1568 | case PERF_RECORD_MISC_KERNEL: |
| 1565 | dso_type = DSO_TYPE_KERNEL; | 1569 | dso_type = DSO_TYPE_KERNEL; |
| 1566 | head = &machine->kernel_dsos; | 1570 | dsos = &machine->kernel_dsos; |
| 1567 | break; | 1571 | break; |
| 1568 | case PERF_RECORD_MISC_GUEST_KERNEL: | 1572 | case PERF_RECORD_MISC_GUEST_KERNEL: |
| 1569 | dso_type = DSO_TYPE_GUEST_KERNEL; | 1573 | dso_type = DSO_TYPE_GUEST_KERNEL; |
| 1570 | head = &machine->kernel_dsos; | 1574 | dsos = &machine->kernel_dsos; |
| 1571 | break; | 1575 | break; |
| 1572 | case PERF_RECORD_MISC_USER: | 1576 | case PERF_RECORD_MISC_USER: |
| 1573 | case PERF_RECORD_MISC_GUEST_USER: | 1577 | case PERF_RECORD_MISC_GUEST_USER: |
| 1574 | dso_type = DSO_TYPE_USER; | 1578 | dso_type = DSO_TYPE_USER; |
| 1575 | head = &machine->user_dsos; | 1579 | dsos = &machine->user_dsos; |
| 1576 | break; | 1580 | break; |
| 1577 | default: | 1581 | default: |
| 1578 | goto out; | 1582 | goto out; |
| 1579 | } | 1583 | } |
| 1580 | 1584 | ||
| 1581 | dso = __dsos__findnew(head, filename); | 1585 | dso = __dsos__findnew(dsos, filename); |
| 1582 | if (dso != NULL) { | 1586 | if (dso != NULL) { |
| 1583 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 1587 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
| 1584 | 1588 | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 30df6187ee02..86569fa3651d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -277,6 +277,28 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | |||
| 277 | } | 277 | } |
| 278 | } | 278 | } |
| 279 | 279 | ||
| 280 | void hists__delete_entries(struct hists *hists) | ||
| 281 | { | ||
| 282 | struct rb_node *next = rb_first(&hists->entries); | ||
| 283 | struct hist_entry *n; | ||
| 284 | |||
| 285 | while (next) { | ||
| 286 | n = rb_entry(next, struct hist_entry, rb_node); | ||
| 287 | next = rb_next(&n->rb_node); | ||
| 288 | |||
| 289 | rb_erase(&n->rb_node, &hists->entries); | ||
| 290 | |||
| 291 | if (sort__need_collapse) | ||
| 292 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
| 293 | |||
| 294 | --hists->nr_entries; | ||
| 295 | if (!n->filtered) | ||
| 296 | --hists->nr_non_filtered_entries; | ||
| 297 | |||
| 298 | hist_entry__free(n); | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 280 | /* | 302 | /* |
| 281 | * histogram, sorted on item, collects periods | 303 | * histogram, sorted on item, collects periods |
| 282 | */ | 304 | */ |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 742f49a85725..8c9c70e18cbb 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -152,6 +152,7 @@ void hists__output_resort(struct hists *hists); | |||
| 152 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); | 152 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); |
| 153 | 153 | ||
| 154 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 154 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
| 155 | void hists__delete_entries(struct hists *hists); | ||
| 155 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | 156 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); |
| 156 | 157 | ||
| 157 | u64 hists__total_period(struct hists *hists); | 158 | u64 hists__total_period(struct hists *hists); |
| @@ -192,6 +193,7 @@ struct perf_hpp { | |||
| 192 | }; | 193 | }; |
| 193 | 194 | ||
| 194 | struct perf_hpp_fmt { | 195 | struct perf_hpp_fmt { |
| 196 | const char *name; | ||
| 195 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 197 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 196 | struct perf_evsel *evsel); | 198 | struct perf_evsel *evsel); |
| 197 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 199 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| @@ -207,6 +209,8 @@ struct perf_hpp_fmt { | |||
| 207 | struct list_head list; | 209 | struct list_head list; |
| 208 | struct list_head sort_list; | 210 | struct list_head sort_list; |
| 209 | bool elide; | 211 | bool elide; |
| 212 | int len; | ||
| 213 | int user_len; | ||
| 210 | }; | 214 | }; |
| 211 | 215 | ||
| 212 | extern struct list_head perf_hpp__list; | 216 | extern struct list_head perf_hpp__list; |
| @@ -261,17 +265,19 @@ static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) | |||
| 261 | } | 265 | } |
| 262 | 266 | ||
| 263 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); | 267 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); |
| 268 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists); | ||
| 269 | void perf_hpp__set_user_width(const char *width_list_str); | ||
| 264 | 270 | ||
| 265 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); | 271 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); |
| 266 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); | 272 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); |
| 267 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); | 273 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); |
| 268 | 274 | ||
| 269 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | 275 | int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 270 | hpp_field_fn get_field, const char *fmt, | 276 | struct hist_entry *he, hpp_field_fn get_field, |
| 271 | hpp_snprint_fn print_fn, bool fmt_percent); | 277 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent); |
| 272 | int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, | 278 | int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 273 | hpp_field_fn get_field, const char *fmt, | 279 | struct hist_entry *he, hpp_field_fn get_field, |
| 274 | hpp_snprint_fn print_fn, bool fmt_percent); | 280 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent); |
| 275 | 281 | ||
| 276 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | 282 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) |
| 277 | { | 283 | { |
diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h index 0b5a8cd2ee79..cf1d7913783b 100644 --- a/tools/perf/util/kvm-stat.h +++ b/tools/perf/util/kvm-stat.h | |||
| @@ -92,7 +92,6 @@ struct perf_kvm_stat { | |||
| 92 | u64 lost_events; | 92 | u64 lost_events; |
| 93 | u64 duration; | 93 | u64 duration; |
| 94 | 94 | ||
| 95 | const char *pid_str; | ||
| 96 | struct intlist *pid_list; | 95 | struct intlist *pid_list; |
| 97 | 96 | ||
| 98 | struct rb_root result; | 97 | struct rb_root result; |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 16bba9fff2c8..b7d477fbda02 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -17,8 +17,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
| 17 | { | 17 | { |
| 18 | map_groups__init(&machine->kmaps); | 18 | map_groups__init(&machine->kmaps); |
| 19 | RB_CLEAR_NODE(&machine->rb_node); | 19 | RB_CLEAR_NODE(&machine->rb_node); |
| 20 | INIT_LIST_HEAD(&machine->user_dsos); | 20 | INIT_LIST_HEAD(&machine->user_dsos.head); |
| 21 | INIT_LIST_HEAD(&machine->kernel_dsos); | 21 | INIT_LIST_HEAD(&machine->kernel_dsos.head); |
| 22 | 22 | ||
| 23 | machine->threads = RB_ROOT; | 23 | machine->threads = RB_ROOT; |
| 24 | INIT_LIST_HEAD(&machine->dead_threads); | 24 | INIT_LIST_HEAD(&machine->dead_threads); |
| @@ -31,6 +31,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
| 31 | 31 | ||
| 32 | machine->symbol_filter = NULL; | 32 | machine->symbol_filter = NULL; |
| 33 | machine->id_hdr_size = 0; | 33 | machine->id_hdr_size = 0; |
| 34 | machine->comm_exec = false; | ||
| 35 | machine->kernel_start = 0; | ||
| 34 | 36 | ||
| 35 | machine->root_dir = strdup(root_dir); | 37 | machine->root_dir = strdup(root_dir); |
| 36 | if (machine->root_dir == NULL) | 38 | if (machine->root_dir == NULL) |
| @@ -70,11 +72,12 @@ out_delete: | |||
| 70 | return NULL; | 72 | return NULL; |
| 71 | } | 73 | } |
| 72 | 74 | ||
| 73 | static void dsos__delete(struct list_head *dsos) | 75 | static void dsos__delete(struct dsos *dsos) |
| 74 | { | 76 | { |
| 75 | struct dso *pos, *n; | 77 | struct dso *pos, *n; |
| 76 | 78 | ||
| 77 | list_for_each_entry_safe(pos, n, dsos, node) { | 79 | list_for_each_entry_safe(pos, n, &dsos->head, node) { |
| 80 | RB_CLEAR_NODE(&pos->rb_node); | ||
| 78 | list_del(&pos->node); | 81 | list_del(&pos->node); |
| 79 | dso__delete(pos); | 82 | dso__delete(pos); |
| 80 | } | 83 | } |
| @@ -179,6 +182,19 @@ void machines__set_symbol_filter(struct machines *machines, | |||
| 179 | } | 182 | } |
| 180 | } | 183 | } |
| 181 | 184 | ||
| 185 | void machines__set_comm_exec(struct machines *machines, bool comm_exec) | ||
| 186 | { | ||
| 187 | struct rb_node *nd; | ||
| 188 | |||
| 189 | machines->host.comm_exec = comm_exec; | ||
| 190 | |||
| 191 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
| 192 | struct machine *machine = rb_entry(nd, struct machine, rb_node); | ||
| 193 | |||
| 194 | machine->comm_exec = comm_exec; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 182 | struct machine *machines__find(struct machines *machines, pid_t pid) | 198 | struct machine *machines__find(struct machines *machines, pid_t pid) |
| 183 | { | 199 | { |
| 184 | struct rb_node **p = &machines->guests.rb_node; | 200 | struct rb_node **p = &machines->guests.rb_node; |
| @@ -398,17 +414,31 @@ struct thread *machine__find_thread(struct machine *machine, pid_t pid, | |||
| 398 | return __machine__findnew_thread(machine, pid, tid, false); | 414 | return __machine__findnew_thread(machine, pid, tid, false); |
| 399 | } | 415 | } |
| 400 | 416 | ||
| 417 | struct comm *machine__thread_exec_comm(struct machine *machine, | ||
| 418 | struct thread *thread) | ||
| 419 | { | ||
| 420 | if (machine->comm_exec) | ||
| 421 | return thread__exec_comm(thread); | ||
| 422 | else | ||
| 423 | return thread__comm(thread); | ||
| 424 | } | ||
| 425 | |||
| 401 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 426 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
| 402 | struct perf_sample *sample) | 427 | struct perf_sample *sample) |
| 403 | { | 428 | { |
| 404 | struct thread *thread = machine__findnew_thread(machine, | 429 | struct thread *thread = machine__findnew_thread(machine, |
| 405 | event->comm.pid, | 430 | event->comm.pid, |
| 406 | event->comm.tid); | 431 | event->comm.tid); |
| 432 | bool exec = event->header.misc & PERF_RECORD_MISC_COMM_EXEC; | ||
| 433 | |||
| 434 | if (exec) | ||
| 435 | machine->comm_exec = true; | ||
| 407 | 436 | ||
| 408 | if (dump_trace) | 437 | if (dump_trace) |
| 409 | perf_event__fprintf_comm(event, stdout); | 438 | perf_event__fprintf_comm(event, stdout); |
| 410 | 439 | ||
| 411 | if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) { | 440 | if (thread == NULL || |
| 441 | __thread__set_comm(thread, event->comm.comm, sample->time, exec)) { | ||
| 412 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 442 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
| 413 | return -1; | 443 | return -1; |
| 414 | } | 444 | } |
| @@ -448,23 +478,23 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
| 448 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) | 478 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) |
| 449 | { | 479 | { |
| 450 | struct rb_node *nd; | 480 | struct rb_node *nd; |
| 451 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos, fp) + | 481 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos.head, fp) + |
| 452 | __dsos__fprintf(&machines->host.user_dsos, fp); | 482 | __dsos__fprintf(&machines->host.user_dsos.head, fp); |
| 453 | 483 | ||
| 454 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | 484 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { |
| 455 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 485 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
| 456 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | 486 | ret += __dsos__fprintf(&pos->kernel_dsos.head, fp); |
| 457 | ret += __dsos__fprintf(&pos->user_dsos, fp); | 487 | ret += __dsos__fprintf(&pos->user_dsos.head, fp); |
| 458 | } | 488 | } |
| 459 | 489 | ||
| 460 | return ret; | 490 | return ret; |
| 461 | } | 491 | } |
| 462 | 492 | ||
| 463 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | 493 | size_t machine__fprintf_dsos_buildid(struct machine *m, FILE *fp, |
| 464 | bool (skip)(struct dso *dso, int parm), int parm) | 494 | bool (skip)(struct dso *dso, int parm), int parm) |
| 465 | { | 495 | { |
| 466 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) + | 496 | return __dsos__fprintf_buildid(&m->kernel_dsos.head, fp, skip, parm) + |
| 467 | __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); | 497 | __dsos__fprintf_buildid(&m->user_dsos.head, fp, skip, parm); |
| 468 | } | 498 | } |
| 469 | 499 | ||
| 470 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, | 500 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, |
| @@ -565,8 +595,8 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL}; | |||
| 565 | * Returns the name of the start symbol in *symbol_name. Pass in NULL as | 595 | * Returns the name of the start symbol in *symbol_name. Pass in NULL as |
| 566 | * symbol_name if it's not that important. | 596 | * symbol_name if it's not that important. |
| 567 | */ | 597 | */ |
| 568 | static u64 machine__get_kernel_start_addr(struct machine *machine, | 598 | static u64 machine__get_running_kernel_start(struct machine *machine, |
| 569 | const char **symbol_name) | 599 | const char **symbol_name) |
| 570 | { | 600 | { |
| 571 | char filename[PATH_MAX]; | 601 | char filename[PATH_MAX]; |
| 572 | int i; | 602 | int i; |
| @@ -593,7 +623,7 @@ static u64 machine__get_kernel_start_addr(struct machine *machine, | |||
| 593 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | 623 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) |
| 594 | { | 624 | { |
| 595 | enum map_type type; | 625 | enum map_type type; |
| 596 | u64 start = machine__get_kernel_start_addr(machine, NULL); | 626 | u64 start = machine__get_running_kernel_start(machine, NULL); |
| 597 | 627 | ||
| 598 | for (type = 0; type < MAP__NR_TYPES; ++type) { | 628 | for (type = 0; type < MAP__NR_TYPES; ++type) { |
| 599 | struct kmap *kmap; | 629 | struct kmap *kmap; |
| @@ -912,7 +942,7 @@ int machine__create_kernel_maps(struct machine *machine) | |||
| 912 | { | 942 | { |
| 913 | struct dso *kernel = machine__get_kernel(machine); | 943 | struct dso *kernel = machine__get_kernel(machine); |
| 914 | const char *name; | 944 | const char *name; |
| 915 | u64 addr = machine__get_kernel_start_addr(machine, &name); | 945 | u64 addr = machine__get_running_kernel_start(machine, &name); |
| 916 | if (!addr) | 946 | if (!addr) |
| 917 | return -1; | 947 | return -1; |
| 918 | 948 | ||
| @@ -965,7 +995,7 @@ static bool machine__uses_kcore(struct machine *machine) | |||
| 965 | { | 995 | { |
| 966 | struct dso *dso; | 996 | struct dso *dso; |
| 967 | 997 | ||
| 968 | list_for_each_entry(dso, &machine->kernel_dsos, node) { | 998 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { |
| 969 | if (dso__is_kcore(dso)) | 999 | if (dso__is_kcore(dso)) |
| 970 | return true; | 1000 | return true; |
| 971 | } | 1001 | } |
| @@ -1285,6 +1315,16 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread, | |||
| 1285 | 1315 | ||
| 1286 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, | 1316 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, |
| 1287 | &al); | 1317 | &al); |
| 1318 | if (al.map == NULL) { | ||
| 1319 | /* | ||
| 1320 | * some shared data regions have execute bit set which puts | ||
| 1321 | * their mapping in the MAP__FUNCTION type array. | ||
| 1322 | * Check there as a fallback option before dropping the sample. | ||
| 1323 | */ | ||
| 1324 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr, | ||
| 1325 | &al); | ||
| 1326 | } | ||
| 1327 | |||
| 1288 | ams->addr = addr; | 1328 | ams->addr = addr; |
| 1289 | ams->al_addr = al.addr; | 1329 | ams->al_addr = al.addr; |
| 1290 | ams->sym = al.sym; | 1330 | ams->sym = al.sym; |
| @@ -1531,3 +1571,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, | |||
| 1531 | 1571 | ||
| 1532 | return 0; | 1572 | return 0; |
| 1533 | } | 1573 | } |
| 1574 | |||
| 1575 | int machine__get_kernel_start(struct machine *machine) | ||
| 1576 | { | ||
| 1577 | struct map *map = machine__kernel_map(machine, MAP__FUNCTION); | ||
| 1578 | int err = 0; | ||
| 1579 | |||
| 1580 | /* | ||
| 1581 | * The only addresses above 2^63 are kernel addresses of a 64-bit | ||
| 1582 | * kernel. Note that addresses are unsigned so that on a 32-bit system | ||
| 1583 | * all addresses including kernel addresses are less than 2^32. In | ||
| 1584 | * that case (32-bit system), if the kernel mapping is unknown, all | ||
| 1585 | * addresses will be assumed to be in user space - see | ||
| 1586 | * machine__kernel_ip(). | ||
| 1587 | */ | ||
| 1588 | machine->kernel_start = 1ULL << 63; | ||
| 1589 | if (map) { | ||
| 1590 | err = map__load(map, machine->symbol_filter); | ||
| 1591 | if (map->start) | ||
| 1592 | machine->kernel_start = map->start; | ||
| 1593 | } | ||
| 1594 | return err; | ||
| 1595 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index b972824e6294..2b651a7f5d0d 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
| 5 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
| 6 | #include "map.h" | 6 | #include "map.h" |
| 7 | #include "dso.h" | ||
| 7 | #include "event.h" | 8 | #include "event.h" |
| 8 | 9 | ||
| 9 | struct addr_location; | 10 | struct addr_location; |
| @@ -26,15 +27,17 @@ struct machine { | |||
| 26 | struct rb_node rb_node; | 27 | struct rb_node rb_node; |
| 27 | pid_t pid; | 28 | pid_t pid; |
| 28 | u16 id_hdr_size; | 29 | u16 id_hdr_size; |
| 30 | bool comm_exec; | ||
| 29 | char *root_dir; | 31 | char *root_dir; |
| 30 | struct rb_root threads; | 32 | struct rb_root threads; |
| 31 | struct list_head dead_threads; | 33 | struct list_head dead_threads; |
| 32 | struct thread *last_match; | 34 | struct thread *last_match; |
| 33 | struct vdso_info *vdso_info; | 35 | struct vdso_info *vdso_info; |
| 34 | struct list_head user_dsos; | 36 | struct dsos user_dsos; |
| 35 | struct list_head kernel_dsos; | 37 | struct dsos kernel_dsos; |
| 36 | struct map_groups kmaps; | 38 | struct map_groups kmaps; |
| 37 | struct map *vmlinux_maps[MAP__NR_TYPES]; | 39 | struct map *vmlinux_maps[MAP__NR_TYPES]; |
| 40 | u64 kernel_start; | ||
| 38 | symbol_filter_t symbol_filter; | 41 | symbol_filter_t symbol_filter; |
| 39 | pid_t *current_tid; | 42 | pid_t *current_tid; |
| 40 | }; | 43 | }; |
| @@ -45,8 +48,26 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type) | |||
| 45 | return machine->vmlinux_maps[type]; | 48 | return machine->vmlinux_maps[type]; |
| 46 | } | 49 | } |
| 47 | 50 | ||
| 51 | int machine__get_kernel_start(struct machine *machine); | ||
| 52 | |||
| 53 | static inline u64 machine__kernel_start(struct machine *machine) | ||
| 54 | { | ||
| 55 | if (!machine->kernel_start) | ||
| 56 | machine__get_kernel_start(machine); | ||
| 57 | return machine->kernel_start; | ||
| 58 | } | ||
| 59 | |||
| 60 | static inline bool machine__kernel_ip(struct machine *machine, u64 ip) | ||
| 61 | { | ||
| 62 | u64 kernel_start = machine__kernel_start(machine); | ||
| 63 | |||
| 64 | return ip >= kernel_start; | ||
| 65 | } | ||
| 66 | |||
| 48 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, | 67 | struct thread *machine__find_thread(struct machine *machine, pid_t pid, |
| 49 | pid_t tid); | 68 | pid_t tid); |
| 69 | struct comm *machine__thread_exec_comm(struct machine *machine, | ||
| 70 | struct thread *thread); | ||
| 50 | 71 | ||
| 51 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 72 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
| 52 | struct perf_sample *sample); | 73 | struct perf_sample *sample); |
| @@ -88,6 +109,7 @@ char *machine__mmap_name(struct machine *machine, char *bf, size_t size); | |||
| 88 | 109 | ||
| 89 | void machines__set_symbol_filter(struct machines *machines, | 110 | void machines__set_symbol_filter(struct machines *machines, |
| 90 | symbol_filter_t symbol_filter); | 111 | symbol_filter_t symbol_filter); |
| 112 | void machines__set_comm_exec(struct machines *machines, bool comm_exec); | ||
| 91 | 113 | ||
| 92 | struct machine *machine__new_host(void); | 114 | struct machine *machine__new_host(void); |
| 93 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | 115 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 31b8905dd863..b7090596ac50 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -31,6 +31,7 @@ static inline int is_anon_memory(const char *filename) | |||
| 31 | static inline int is_no_dso_memory(const char *filename) | 31 | static inline int is_no_dso_memory(const char *filename) |
| 32 | { | 32 | { |
| 33 | return !strncmp(filename, "[stack", 6) || | 33 | return !strncmp(filename, "[stack", 6) || |
| 34 | !strncmp(filename, "/SYSV",5) || | ||
| 34 | !strcmp(filename, "[heap]"); | 35 | !strcmp(filename, "[heap]"); |
| 35 | } | 36 | } |
| 36 | 37 | ||
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c new file mode 100644 index 000000000000..706ce1a66169 --- /dev/null +++ b/tools/perf/util/ordered-events.c | |||
| @@ -0,0 +1,245 @@ | |||
| 1 | #include <linux/list.h> | ||
| 2 | #include <linux/compiler.h> | ||
| 3 | #include "ordered-events.h" | ||
| 4 | #include "evlist.h" | ||
| 5 | #include "session.h" | ||
| 6 | #include "asm/bug.h" | ||
| 7 | #include "debug.h" | ||
| 8 | |||
| 9 | #define pr_N(n, fmt, ...) \ | ||
| 10 | eprintf(n, debug_ordered_events, fmt, ##__VA_ARGS__) | ||
| 11 | |||
| 12 | #define pr(fmt, ...) pr_N(1, pr_fmt(fmt), ##__VA_ARGS__) | ||
| 13 | |||
| 14 | static void queue_event(struct ordered_events *oe, struct ordered_event *new) | ||
| 15 | { | ||
| 16 | struct ordered_event *last = oe->last; | ||
| 17 | u64 timestamp = new->timestamp; | ||
| 18 | struct list_head *p; | ||
| 19 | |||
| 20 | ++oe->nr_events; | ||
| 21 | oe->last = new; | ||
| 22 | |||
| 23 | pr_oe_time2(timestamp, "queue_event nr_events %u\n", oe->nr_events); | ||
| 24 | |||
| 25 | if (!last) { | ||
| 26 | list_add(&new->list, &oe->events); | ||
| 27 | oe->max_timestamp = timestamp; | ||
| 28 | return; | ||
| 29 | } | ||
| 30 | |||
| 31 | /* | ||
| 32 | * last event might point to some random place in the list as it's | ||
| 33 | * the last queued event. We expect that the new event is close to | ||
| 34 | * this. | ||
| 35 | */ | ||
| 36 | if (last->timestamp <= timestamp) { | ||
| 37 | while (last->timestamp <= timestamp) { | ||
| 38 | p = last->list.next; | ||
| 39 | if (p == &oe->events) { | ||
| 40 | list_add_tail(&new->list, &oe->events); | ||
| 41 | oe->max_timestamp = timestamp; | ||
| 42 | return; | ||
| 43 | } | ||
| 44 | last = list_entry(p, struct ordered_event, list); | ||
| 45 | } | ||
| 46 | list_add_tail(&new->list, &last->list); | ||
| 47 | } else { | ||
| 48 | while (last->timestamp > timestamp) { | ||
| 49 | p = last->list.prev; | ||
| 50 | if (p == &oe->events) { | ||
| 51 | list_add(&new->list, &oe->events); | ||
| 52 | return; | ||
| 53 | } | ||
| 54 | last = list_entry(p, struct ordered_event, list); | ||
| 55 | } | ||
| 56 | list_add(&new->list, &last->list); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event)) | ||
| 61 | static struct ordered_event *alloc_event(struct ordered_events *oe) | ||
| 62 | { | ||
| 63 | struct list_head *cache = &oe->cache; | ||
| 64 | struct ordered_event *new = NULL; | ||
| 65 | |||
| 66 | if (!list_empty(cache)) { | ||
| 67 | new = list_entry(cache->next, struct ordered_event, list); | ||
| 68 | list_del(&new->list); | ||
| 69 | } else if (oe->buffer) { | ||
| 70 | new = oe->buffer + oe->buffer_idx; | ||
| 71 | if (++oe->buffer_idx == MAX_SAMPLE_BUFFER) | ||
| 72 | oe->buffer = NULL; | ||
| 73 | } else if (oe->cur_alloc_size < oe->max_alloc_size) { | ||
| 74 | size_t size = MAX_SAMPLE_BUFFER * sizeof(*new); | ||
| 75 | |||
| 76 | oe->buffer = malloc(size); | ||
| 77 | if (!oe->buffer) | ||
| 78 | return NULL; | ||
| 79 | |||
| 80 | pr("alloc size %" PRIu64 "B (+%zu), max %" PRIu64 "B\n", | ||
| 81 | oe->cur_alloc_size, size, oe->max_alloc_size); | ||
| 82 | |||
| 83 | oe->cur_alloc_size += size; | ||
| 84 | list_add(&oe->buffer->list, &oe->to_free); | ||
| 85 | |||
| 86 | /* First entry is abused to maintain the to_free list. */ | ||
| 87 | oe->buffer_idx = 2; | ||
| 88 | new = oe->buffer + 1; | ||
| 89 | } else { | ||
| 90 | pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size); | ||
| 91 | } | ||
| 92 | |||
| 93 | return new; | ||
| 94 | } | ||
| 95 | |||
| 96 | struct ordered_event * | ||
| 97 | ordered_events__new(struct ordered_events *oe, u64 timestamp) | ||
| 98 | { | ||
| 99 | struct ordered_event *new; | ||
| 100 | |||
| 101 | new = alloc_event(oe); | ||
| 102 | if (new) { | ||
| 103 | new->timestamp = timestamp; | ||
| 104 | queue_event(oe, new); | ||
| 105 | } | ||
| 106 | |||
| 107 | return new; | ||
| 108 | } | ||
| 109 | |||
| 110 | void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event) | ||
| 111 | { | ||
| 112 | list_move(&event->list, &oe->cache); | ||
| 113 | oe->nr_events--; | ||
| 114 | } | ||
| 115 | |||
| 116 | static int __ordered_events__flush(struct perf_session *s, | ||
| 117 | struct perf_tool *tool) | ||
| 118 | { | ||
| 119 | struct ordered_events *oe = &s->ordered_events; | ||
| 120 | struct list_head *head = &oe->events; | ||
| 121 | struct ordered_event *tmp, *iter; | ||
| 122 | struct perf_sample sample; | ||
| 123 | u64 limit = oe->next_flush; | ||
| 124 | u64 last_ts = oe->last ? oe->last->timestamp : 0ULL; | ||
| 125 | bool show_progress = limit == ULLONG_MAX; | ||
| 126 | struct ui_progress prog; | ||
| 127 | int ret; | ||
| 128 | |||
| 129 | if (!tool->ordered_events || !limit) | ||
| 130 | return 0; | ||
| 131 | |||
| 132 | if (show_progress) | ||
| 133 | ui_progress__init(&prog, oe->nr_events, "Processing time ordered events..."); | ||
| 134 | |||
| 135 | list_for_each_entry_safe(iter, tmp, head, list) { | ||
| 136 | if (session_done()) | ||
| 137 | return 0; | ||
| 138 | |||
| 139 | if (iter->timestamp > limit) | ||
| 140 | break; | ||
| 141 | |||
| 142 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); | ||
| 143 | if (ret) | ||
| 144 | pr_err("Can't parse sample, err = %d\n", ret); | ||
| 145 | else { | ||
| 146 | ret = perf_session__deliver_event(s, iter->event, &sample, tool, | ||
| 147 | iter->file_offset); | ||
| 148 | if (ret) | ||
| 149 | return ret; | ||
| 150 | } | ||
| 151 | |||
| 152 | ordered_events__delete(oe, iter); | ||
| 153 | oe->last_flush = iter->timestamp; | ||
| 154 | |||
| 155 | if (show_progress) | ||
| 156 | ui_progress__update(&prog, 1); | ||
| 157 | } | ||
| 158 | |||
| 159 | if (list_empty(head)) | ||
| 160 | oe->last = NULL; | ||
| 161 | else if (last_ts <= limit) | ||
| 162 | oe->last = list_entry(head->prev, struct ordered_event, list); | ||
| 163 | |||
| 164 | return 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, | ||
| 168 | enum oe_flush how) | ||
| 169 | { | ||
| 170 | struct ordered_events *oe = &s->ordered_events; | ||
| 171 | static const char * const str[] = { | ||
| 172 | "NONE", | ||
| 173 | "FINAL", | ||
| 174 | "ROUND", | ||
| 175 | "HALF ", | ||
| 176 | }; | ||
| 177 | int err; | ||
| 178 | |||
| 179 | switch (how) { | ||
| 180 | case OE_FLUSH__FINAL: | ||
| 181 | oe->next_flush = ULLONG_MAX; | ||
| 182 | break; | ||
| 183 | |||
| 184 | case OE_FLUSH__HALF: | ||
| 185 | { | ||
| 186 | struct ordered_event *first, *last; | ||
| 187 | struct list_head *head = &oe->events; | ||
| 188 | |||
| 189 | first = list_entry(head->next, struct ordered_event, list); | ||
| 190 | last = oe->last; | ||
| 191 | |||
| 192 | /* Warn if we are called before any event got allocated. */ | ||
| 193 | if (WARN_ONCE(!last || list_empty(head), "empty queue")) | ||
| 194 | return 0; | ||
| 195 | |||
| 196 | oe->next_flush = first->timestamp; | ||
| 197 | oe->next_flush += (last->timestamp - first->timestamp) / 2; | ||
| 198 | break; | ||
| 199 | } | ||
| 200 | |||
| 201 | case OE_FLUSH__ROUND: | ||
| 202 | case OE_FLUSH__NONE: | ||
| 203 | default: | ||
| 204 | break; | ||
| 205 | }; | ||
| 206 | |||
| 207 | pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush PRE %s, nr_events %u\n", | ||
| 208 | str[how], oe->nr_events); | ||
| 209 | pr_oe_time(oe->max_timestamp, "max_timestamp\n"); | ||
| 210 | |||
| 211 | err = __ordered_events__flush(s, tool); | ||
| 212 | |||
| 213 | if (!err) { | ||
| 214 | if (how == OE_FLUSH__ROUND) | ||
| 215 | oe->next_flush = oe->max_timestamp; | ||
| 216 | |||
| 217 | oe->last_flush_type = how; | ||
| 218 | } | ||
| 219 | |||
| 220 | pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush POST %s, nr_events %u\n", | ||
| 221 | str[how], oe->nr_events); | ||
| 222 | pr_oe_time(oe->last_flush, "last_flush\n"); | ||
| 223 | |||
| 224 | return err; | ||
| 225 | } | ||
| 226 | |||
| 227 | void ordered_events__init(struct ordered_events *oe) | ||
| 228 | { | ||
| 229 | INIT_LIST_HEAD(&oe->events); | ||
| 230 | INIT_LIST_HEAD(&oe->cache); | ||
| 231 | INIT_LIST_HEAD(&oe->to_free); | ||
| 232 | oe->max_alloc_size = (u64) -1; | ||
| 233 | oe->cur_alloc_size = 0; | ||
| 234 | } | ||
| 235 | |||
| 236 | void ordered_events__free(struct ordered_events *oe) | ||
| 237 | { | ||
| 238 | while (!list_empty(&oe->to_free)) { | ||
| 239 | struct ordered_event *event; | ||
| 240 | |||
| 241 | event = list_entry(oe->to_free.next, struct ordered_event, list); | ||
| 242 | list_del(&event->list); | ||
| 243 | free(event); | ||
| 244 | } | ||
| 245 | } | ||
diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h new file mode 100644 index 000000000000..3b2f20542a01 --- /dev/null +++ b/tools/perf/util/ordered-events.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | #ifndef __ORDERED_EVENTS_H | ||
| 2 | #define __ORDERED_EVENTS_H | ||
| 3 | |||
| 4 | #include <linux/types.h> | ||
| 5 | #include "tool.h" | ||
| 6 | |||
| 7 | struct perf_session; | ||
| 8 | |||
| 9 | struct ordered_event { | ||
| 10 | u64 timestamp; | ||
| 11 | u64 file_offset; | ||
| 12 | union perf_event *event; | ||
| 13 | struct list_head list; | ||
| 14 | }; | ||
| 15 | |||
| 16 | enum oe_flush { | ||
| 17 | OE_FLUSH__NONE, | ||
| 18 | OE_FLUSH__FINAL, | ||
| 19 | OE_FLUSH__ROUND, | ||
| 20 | OE_FLUSH__HALF, | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct ordered_events { | ||
| 24 | u64 last_flush; | ||
| 25 | u64 next_flush; | ||
| 26 | u64 max_timestamp; | ||
| 27 | u64 max_alloc_size; | ||
| 28 | u64 cur_alloc_size; | ||
| 29 | struct list_head events; | ||
| 30 | struct list_head cache; | ||
| 31 | struct list_head to_free; | ||
| 32 | struct ordered_event *buffer; | ||
| 33 | struct ordered_event *last; | ||
| 34 | int buffer_idx; | ||
| 35 | unsigned int nr_events; | ||
| 36 | enum oe_flush last_flush_type; | ||
| 37 | }; | ||
| 38 | |||
| 39 | struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp); | ||
| 40 | void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event); | ||
| 41 | int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, | ||
| 42 | enum oe_flush how); | ||
| 43 | void ordered_events__init(struct ordered_events *oe); | ||
| 44 | void ordered_events__free(struct ordered_events *oe); | ||
| 45 | |||
| 46 | static inline | ||
| 47 | void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size) | ||
| 48 | { | ||
| 49 | oe->max_alloc_size = size; | ||
| 50 | } | ||
| 51 | #endif /* __ORDERED_EVENTS_H */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 1e15df10a88c..d76aa30cb1fb 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "symbol.h" | 10 | #include "symbol.h" |
| 11 | #include "cache.h" | 11 | #include "cache.h" |
| 12 | #include "header.h" | 12 | #include "header.h" |
| 13 | #include "debug.h" | ||
| 13 | #include <api/fs/debugfs.h> | 14 | #include <api/fs/debugfs.h> |
| 14 | #include "parse-events-bison.h" | 15 | #include "parse-events-bison.h" |
| 15 | #define YY_EXTRA_TYPE int | 16 | #define YY_EXTRA_TYPE int |
| @@ -633,18 +634,28 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
| 633 | char *name, struct list_head *head_config) | 634 | char *name, struct list_head *head_config) |
| 634 | { | 635 | { |
| 635 | struct perf_event_attr attr; | 636 | struct perf_event_attr attr; |
| 637 | struct perf_pmu_info info; | ||
| 636 | struct perf_pmu *pmu; | 638 | struct perf_pmu *pmu; |
| 637 | struct perf_evsel *evsel; | 639 | struct perf_evsel *evsel; |
| 638 | const char *unit; | ||
| 639 | double scale; | ||
| 640 | 640 | ||
| 641 | pmu = perf_pmu__find(name); | 641 | pmu = perf_pmu__find(name); |
| 642 | if (!pmu) | 642 | if (!pmu) |
| 643 | return -EINVAL; | 643 | return -EINVAL; |
| 644 | 644 | ||
| 645 | memset(&attr, 0, sizeof(attr)); | 645 | if (pmu->default_config) { |
| 646 | memcpy(&attr, pmu->default_config, | ||
| 647 | sizeof(struct perf_event_attr)); | ||
| 648 | } else { | ||
| 649 | memset(&attr, 0, sizeof(attr)); | ||
| 650 | } | ||
| 651 | |||
| 652 | if (!head_config) { | ||
| 653 | attr.type = pmu->type; | ||
| 654 | evsel = __add_event(list, idx, &attr, NULL, pmu->cpus); | ||
| 655 | return evsel ? 0 : -ENOMEM; | ||
| 656 | } | ||
| 646 | 657 | ||
| 647 | if (perf_pmu__check_alias(pmu, head_config, &unit, &scale)) | 658 | if (perf_pmu__check_alias(pmu, head_config, &info)) |
| 648 | return -EINVAL; | 659 | return -EINVAL; |
| 649 | 660 | ||
| 650 | /* | 661 | /* |
| @@ -659,8 +670,8 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
| 659 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), | 670 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), |
| 660 | pmu->cpus); | 671 | pmu->cpus); |
| 661 | if (evsel) { | 672 | if (evsel) { |
| 662 | evsel->unit = unit; | 673 | evsel->unit = info.unit; |
| 663 | evsel->scale = scale; | 674 | evsel->scale = info.scale; |
| 664 | } | 675 | } |
| 665 | 676 | ||
| 666 | return evsel ? 0 : -ENOMEM; | 677 | return evsel ? 0 : -ENOMEM; |
| @@ -973,7 +984,7 @@ int parse_filter(const struct option *opt, const char *str, | |||
| 973 | 984 | ||
| 974 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 985 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
| 975 | fprintf(stderr, | 986 | fprintf(stderr, |
| 976 | "-F option should follow a -e tracepoint option\n"); | 987 | "--filter option should follow a -e tracepoint option\n"); |
| 977 | return -1; | 988 | return -1; |
| 978 | } | 989 | } |
| 979 | 990 | ||
| @@ -1006,9 +1017,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | |||
| 1006 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 1017 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
| 1007 | char evt_path[MAXPATHLEN]; | 1018 | char evt_path[MAXPATHLEN]; |
| 1008 | char dir_path[MAXPATHLEN]; | 1019 | char dir_path[MAXPATHLEN]; |
| 1020 | char sbuf[STRERR_BUFSIZE]; | ||
| 1009 | 1021 | ||
| 1010 | if (debugfs_valid_mountpoint(tracing_events_path)) { | 1022 | if (debugfs_valid_mountpoint(tracing_events_path)) { |
| 1011 | printf(" [ Tracepoints not available: %s ]\n", strerror(errno)); | 1023 | printf(" [ Tracepoints not available: %s ]\n", |
| 1024 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 1012 | return; | 1025 | return; |
| 1013 | } | 1026 | } |
| 1014 | 1027 | ||
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 0bc87ba46bf3..55fab6ad609a 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
| @@ -210,6 +210,16 @@ PE_NAME '/' event_config '/' | |||
| 210 | parse_events__free_terms($3); | 210 | parse_events__free_terms($3); |
| 211 | $$ = list; | 211 | $$ = list; |
| 212 | } | 212 | } |
| 213 | | | ||
| 214 | PE_NAME '/' '/' | ||
| 215 | { | ||
| 216 | struct parse_events_evlist *data = _data; | ||
| 217 | struct list_head *list; | ||
| 218 | |||
| 219 | ALLOC_LIST(list); | ||
| 220 | ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL)); | ||
| 221 | $$ = list; | ||
| 222 | } | ||
| 213 | 223 | ||
| 214 | value_sym: | 224 | value_sym: |
| 215 | PE_VALUE_SYM_HW | 225 | PE_VALUE_SYM_HW |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 7a811eb61f75..93a41ca96b8e 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | #include <sys/types.h> | 2 | #include <sys/types.h> |
| 3 | #include <unistd.h> | 3 | #include <unistd.h> |
| 4 | #include <stdio.h> | 4 | #include <stdio.h> |
| 5 | #include <stdbool.h> | ||
| 6 | #include <stdarg.h> | ||
| 5 | #include <dirent.h> | 7 | #include <dirent.h> |
| 6 | #include <api/fs/fs.h> | 8 | #include <api/fs/fs.h> |
| 7 | #include <locale.h> | 9 | #include <locale.h> |
| @@ -14,8 +16,8 @@ | |||
| 14 | 16 | ||
| 15 | struct perf_pmu_alias { | 17 | struct perf_pmu_alias { |
| 16 | char *name; | 18 | char *name; |
| 17 | struct list_head terms; | 19 | struct list_head terms; /* HEAD struct parse_events_term -> list */ |
| 18 | struct list_head list; | 20 | struct list_head list; /* ELEM */ |
| 19 | char unit[UNIT_MAX_LEN+1]; | 21 | char unit[UNIT_MAX_LEN+1]; |
| 20 | double scale; | 22 | double scale; |
| 21 | }; | 23 | }; |
| @@ -208,6 +210,19 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI | |||
| 208 | return 0; | 210 | return 0; |
| 209 | } | 211 | } |
| 210 | 212 | ||
| 213 | static inline bool pmu_alias_info_file(char *name) | ||
| 214 | { | ||
| 215 | size_t len; | ||
| 216 | |||
| 217 | len = strlen(name); | ||
| 218 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | ||
| 219 | return true; | ||
| 220 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | ||
| 221 | return true; | ||
| 222 | |||
| 223 | return false; | ||
| 224 | } | ||
| 225 | |||
| 211 | /* | 226 | /* |
| 212 | * Process all the sysfs attributes located under the directory | 227 | * Process all the sysfs attributes located under the directory |
| 213 | * specified in 'dir' parameter. | 228 | * specified in 'dir' parameter. |
| @@ -216,7 +231,6 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
| 216 | { | 231 | { |
| 217 | struct dirent *evt_ent; | 232 | struct dirent *evt_ent; |
| 218 | DIR *event_dir; | 233 | DIR *event_dir; |
| 219 | size_t len; | ||
| 220 | int ret = 0; | 234 | int ret = 0; |
| 221 | 235 | ||
| 222 | event_dir = opendir(dir); | 236 | event_dir = opendir(dir); |
| @@ -232,13 +246,9 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
| 232 | continue; | 246 | continue; |
| 233 | 247 | ||
| 234 | /* | 248 | /* |
| 235 | * skip .unit and .scale info files | 249 | * skip info files parsed in perf_pmu__new_alias() |
| 236 | * parsed in perf_pmu__new_alias() | ||
| 237 | */ | 250 | */ |
| 238 | len = strlen(name); | 251 | if (pmu_alias_info_file(name)) |
| 239 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | ||
| 240 | continue; | ||
| 241 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | ||
| 242 | continue; | 252 | continue; |
| 243 | 253 | ||
| 244 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | 254 | snprintf(path, PATH_MAX, "%s/%s", dir, name); |
| @@ -387,6 +397,12 @@ static struct cpu_map *pmu_cpumask(const char *name) | |||
| 387 | return cpus; | 397 | return cpus; |
| 388 | } | 398 | } |
| 389 | 399 | ||
| 400 | struct perf_event_attr *__attribute__((weak)) | ||
| 401 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) | ||
| 402 | { | ||
| 403 | return NULL; | ||
| 404 | } | ||
| 405 | |||
| 390 | static struct perf_pmu *pmu_lookup(const char *name) | 406 | static struct perf_pmu *pmu_lookup(const char *name) |
| 391 | { | 407 | { |
| 392 | struct perf_pmu *pmu; | 408 | struct perf_pmu *pmu; |
| @@ -421,6 +437,9 @@ static struct perf_pmu *pmu_lookup(const char *name) | |||
| 421 | pmu->name = strdup(name); | 437 | pmu->name = strdup(name); |
| 422 | pmu->type = type; | 438 | pmu->type = type; |
| 423 | list_add_tail(&pmu->list, &pmus); | 439 | list_add_tail(&pmu->list, &pmus); |
| 440 | |||
| 441 | pmu->default_config = perf_pmu__get_default_config(pmu); | ||
| 442 | |||
| 424 | return pmu; | 443 | return pmu; |
| 425 | } | 444 | } |
| 426 | 445 | ||
| @@ -479,28 +498,24 @@ pmu_find_format(struct list_head *formats, char *name) | |||
| 479 | } | 498 | } |
| 480 | 499 | ||
| 481 | /* | 500 | /* |
| 482 | * Returns value based on the format definition (format parameter) | 501 | * Sets value based on the format definition (format parameter) |
| 483 | * and unformated value (value parameter). | 502 | * and unformated value (value parameter). |
| 484 | * | ||
| 485 | * TODO maybe optimize a little ;) | ||
| 486 | */ | 503 | */ |
| 487 | static __u64 pmu_format_value(unsigned long *format, __u64 value) | 504 | static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, |
| 505 | bool zero) | ||
| 488 | { | 506 | { |
| 489 | unsigned long fbit, vbit; | 507 | unsigned long fbit, vbit; |
| 490 | __u64 v = 0; | ||
| 491 | 508 | ||
| 492 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { | 509 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { |
| 493 | 510 | ||
| 494 | if (!test_bit(fbit, format)) | 511 | if (!test_bit(fbit, format)) |
| 495 | continue; | 512 | continue; |
| 496 | 513 | ||
| 497 | if (!(value & (1llu << vbit++))) | 514 | if (value & (1llu << vbit++)) |
| 498 | continue; | 515 | *v |= (1llu << fbit); |
| 499 | 516 | else if (zero) | |
| 500 | v |= (1llu << fbit); | 517 | *v &= ~(1llu << fbit); |
| 501 | } | 518 | } |
| 502 | |||
| 503 | return v; | ||
| 504 | } | 519 | } |
| 505 | 520 | ||
| 506 | /* | 521 | /* |
| @@ -509,7 +524,8 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) | |||
| 509 | */ | 524 | */ |
| 510 | static int pmu_config_term(struct list_head *formats, | 525 | static int pmu_config_term(struct list_head *formats, |
| 511 | struct perf_event_attr *attr, | 526 | struct perf_event_attr *attr, |
| 512 | struct parse_events_term *term) | 527 | struct parse_events_term *term, |
| 528 | bool zero) | ||
| 513 | { | 529 | { |
| 514 | struct perf_pmu_format *format; | 530 | struct perf_pmu_format *format; |
| 515 | __u64 *vp; | 531 | __u64 *vp; |
| @@ -548,18 +564,19 @@ static int pmu_config_term(struct list_head *formats, | |||
| 548 | * non-hardcoded terms, here's the place to translate | 564 | * non-hardcoded terms, here's the place to translate |
| 549 | * them into value. | 565 | * them into value. |
| 550 | */ | 566 | */ |
| 551 | *vp |= pmu_format_value(format->bits, term->val.num); | 567 | pmu_format_value(format->bits, term->val.num, vp, zero); |
| 552 | return 0; | 568 | return 0; |
| 553 | } | 569 | } |
| 554 | 570 | ||
| 555 | int perf_pmu__config_terms(struct list_head *formats, | 571 | int perf_pmu__config_terms(struct list_head *formats, |
| 556 | struct perf_event_attr *attr, | 572 | struct perf_event_attr *attr, |
| 557 | struct list_head *head_terms) | 573 | struct list_head *head_terms, |
| 574 | bool zero) | ||
| 558 | { | 575 | { |
| 559 | struct parse_events_term *term; | 576 | struct parse_events_term *term; |
| 560 | 577 | ||
| 561 | list_for_each_entry(term, head_terms, list) | 578 | list_for_each_entry(term, head_terms, list) |
| 562 | if (pmu_config_term(formats, attr, term)) | 579 | if (pmu_config_term(formats, attr, term, zero)) |
| 563 | return -EINVAL; | 580 | return -EINVAL; |
| 564 | 581 | ||
| 565 | return 0; | 582 | return 0; |
| @@ -573,8 +590,10 @@ int perf_pmu__config_terms(struct list_head *formats, | |||
| 573 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 590 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
| 574 | struct list_head *head_terms) | 591 | struct list_head *head_terms) |
| 575 | { | 592 | { |
| 593 | bool zero = !!pmu->default_config; | ||
| 594 | |||
| 576 | attr->type = pmu->type; | 595 | attr->type = pmu->type; |
| 577 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); | 596 | return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero); |
| 578 | } | 597 | } |
| 579 | 598 | ||
| 580 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, | 599 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
| @@ -634,7 +653,7 @@ static int check_unit_scale(struct perf_pmu_alias *alias, | |||
| 634 | * defined for the alias | 653 | * defined for the alias |
| 635 | */ | 654 | */ |
| 636 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 655 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
| 637 | const char **unit, double *scale) | 656 | struct perf_pmu_info *info) |
| 638 | { | 657 | { |
| 639 | struct parse_events_term *term, *h; | 658 | struct parse_events_term *term, *h; |
| 640 | struct perf_pmu_alias *alias; | 659 | struct perf_pmu_alias *alias; |
| @@ -644,8 +663,8 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
| 644 | * Mark unit and scale as not set | 663 | * Mark unit and scale as not set |
| 645 | * (different from default values, see below) | 664 | * (different from default values, see below) |
| 646 | */ | 665 | */ |
| 647 | *unit = NULL; | 666 | info->unit = NULL; |
| 648 | *scale = 0.0; | 667 | info->scale = 0.0; |
| 649 | 668 | ||
| 650 | list_for_each_entry_safe(term, h, head_terms, list) { | 669 | list_for_each_entry_safe(term, h, head_terms, list) { |
| 651 | alias = pmu_find_alias(pmu, term); | 670 | alias = pmu_find_alias(pmu, term); |
| @@ -655,7 +674,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
| 655 | if (ret) | 674 | if (ret) |
| 656 | return ret; | 675 | return ret; |
| 657 | 676 | ||
| 658 | ret = check_unit_scale(alias, unit, scale); | 677 | ret = check_unit_scale(alias, &info->unit, &info->scale); |
| 659 | if (ret) | 678 | if (ret) |
| 660 | return ret; | 679 | return ret; |
| 661 | 680 | ||
| @@ -668,11 +687,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
| 668 | * set defaults as for evsel | 687 | * set defaults as for evsel |
| 669 | * unit cannot left to NULL | 688 | * unit cannot left to NULL |
| 670 | */ | 689 | */ |
| 671 | if (*unit == NULL) | 690 | if (info->unit == NULL) |
| 672 | *unit = ""; | 691 | info->unit = ""; |
| 673 | 692 | ||
| 674 | if (*scale == 0.0) | 693 | if (info->scale == 0.0) |
| 675 | *scale = 1.0; | 694 | info->scale = 1.0; |
| 676 | 695 | ||
| 677 | return 0; | 696 | return 0; |
| 678 | } | 697 | } |
| @@ -794,3 +813,39 @@ bool pmu_have_event(const char *pname, const char *name) | |||
| 794 | } | 813 | } |
| 795 | return false; | 814 | return false; |
| 796 | } | 815 | } |
| 816 | |||
| 817 | static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) | ||
| 818 | { | ||
| 819 | struct stat st; | ||
| 820 | char path[PATH_MAX]; | ||
| 821 | const char *sysfs; | ||
| 822 | |||
| 823 | sysfs = sysfs__mountpoint(); | ||
| 824 | if (!sysfs) | ||
| 825 | return NULL; | ||
| 826 | |||
| 827 | snprintf(path, PATH_MAX, | ||
| 828 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name); | ||
| 829 | |||
| 830 | if (stat(path, &st) < 0) | ||
| 831 | return NULL; | ||
| 832 | |||
| 833 | return fopen(path, "r"); | ||
| 834 | } | ||
| 835 | |||
| 836 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | ||
| 837 | ...) | ||
| 838 | { | ||
| 839 | va_list args; | ||
| 840 | FILE *file; | ||
| 841 | int ret = EOF; | ||
| 842 | |||
| 843 | va_start(args, fmt); | ||
| 844 | file = perf_pmu__open_file(pmu, name); | ||
| 845 | if (file) { | ||
| 846 | ret = vfscanf(file, fmt, args); | ||
| 847 | fclose(file); | ||
| 848 | } | ||
| 849 | va_end(args); | ||
| 850 | return ret; | ||
| 851 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index c14a543ce1f3..fe90a012c003 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
| @@ -13,13 +13,21 @@ enum { | |||
| 13 | 13 | ||
| 14 | #define PERF_PMU_FORMAT_BITS 64 | 14 | #define PERF_PMU_FORMAT_BITS 64 |
| 15 | 15 | ||
| 16 | struct perf_event_attr; | ||
| 17 | |||
| 16 | struct perf_pmu { | 18 | struct perf_pmu { |
| 17 | char *name; | 19 | char *name; |
| 18 | __u32 type; | 20 | __u32 type; |
| 21 | struct perf_event_attr *default_config; | ||
| 19 | struct cpu_map *cpus; | 22 | struct cpu_map *cpus; |
| 20 | struct list_head format; | 23 | struct list_head format; /* HEAD struct perf_pmu_format -> list */ |
| 21 | struct list_head aliases; | 24 | struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */ |
| 22 | struct list_head list; | 25 | struct list_head list; /* ELEM */ |
| 26 | }; | ||
| 27 | |||
| 28 | struct perf_pmu_info { | ||
| 29 | const char *unit; | ||
| 30 | double scale; | ||
| 23 | }; | 31 | }; |
| 24 | 32 | ||
| 25 | struct perf_pmu *perf_pmu__find(const char *name); | 33 | struct perf_pmu *perf_pmu__find(const char *name); |
| @@ -27,9 +35,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
| 27 | struct list_head *head_terms); | 35 | struct list_head *head_terms); |
| 28 | int perf_pmu__config_terms(struct list_head *formats, | 36 | int perf_pmu__config_terms(struct list_head *formats, |
| 29 | struct perf_event_attr *attr, | 37 | struct perf_event_attr *attr, |
| 30 | struct list_head *head_terms); | 38 | struct list_head *head_terms, |
| 39 | bool zero); | ||
| 31 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 40 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
| 32 | const char **unit, double *scale); | 41 | struct perf_pmu_info *info); |
| 33 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 42 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
| 34 | struct list_head *head_terms); | 43 | struct list_head *head_terms); |
| 35 | int perf_pmu_wrap(void); | 44 | int perf_pmu_wrap(void); |
| @@ -45,5 +54,11 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | |||
| 45 | void print_pmu_events(const char *event_glob, bool name_only); | 54 | void print_pmu_events(const char *event_glob, bool name_only); |
| 46 | bool pmu_have_event(const char *pname, const char *name); | 55 | bool pmu_have_event(const char *pname, const char *name); |
| 47 | 56 | ||
| 57 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | ||
| 58 | ...) __attribute__((format(scanf, 3, 4))); | ||
| 59 | |||
| 48 | int perf_pmu__test(void); | 60 | int perf_pmu__test(void); |
| 61 | |||
| 62 | struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu); | ||
| 63 | |||
| 49 | #endif /* __PMU_H */ | 64 | #endif /* __PMU_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 9a0a1839a377..c150ca4343eb 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -79,7 +79,7 @@ static int init_symbol_maps(bool user_only) | |||
| 79 | int ret; | 79 | int ret; |
| 80 | 80 | ||
| 81 | symbol_conf.sort_by_name = true; | 81 | symbol_conf.sort_by_name = true; |
| 82 | ret = symbol__init(); | 82 | ret = symbol__init(NULL); |
| 83 | if (ret < 0) { | 83 | if (ret < 0) { |
| 84 | pr_debug("Failed to init symbol map.\n"); | 84 | pr_debug("Failed to init symbol map.\n"); |
| 85 | goto out; | 85 | goto out; |
| @@ -184,7 +184,8 @@ static struct dso *kernel_get_module_dso(const char *module) | |||
| 184 | const char *vmlinux_name; | 184 | const char *vmlinux_name; |
| 185 | 185 | ||
| 186 | if (module) { | 186 | if (module) { |
| 187 | list_for_each_entry(dso, &host_machine->kernel_dsos, node) { | 187 | list_for_each_entry(dso, &host_machine->kernel_dsos.head, |
| 188 | node) { | ||
| 188 | if (strncmp(dso->short_name + 1, module, | 189 | if (strncmp(dso->short_name + 1, module, |
| 189 | dso->short_name_len - 2) == 0) | 190 | dso->short_name_len - 2) == 0) |
| 190 | goto found; | 191 | goto found; |
| @@ -258,21 +259,33 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) | |||
| 258 | #ifdef HAVE_DWARF_SUPPORT | 259 | #ifdef HAVE_DWARF_SUPPORT |
| 259 | 260 | ||
| 260 | /* Open new debuginfo of given module */ | 261 | /* Open new debuginfo of given module */ |
| 261 | static struct debuginfo *open_debuginfo(const char *module) | 262 | static struct debuginfo *open_debuginfo(const char *module, bool silent) |
| 262 | { | 263 | { |
| 263 | const char *path = module; | 264 | const char *path = module; |
| 265 | struct debuginfo *ret; | ||
| 264 | 266 | ||
| 265 | if (!module || !strchr(module, '/')) { | 267 | if (!module || !strchr(module, '/')) { |
| 266 | path = kernel_get_module_path(module); | 268 | path = kernel_get_module_path(module); |
| 267 | if (!path) { | 269 | if (!path) { |
| 268 | pr_err("Failed to find path of %s module.\n", | 270 | if (!silent) |
| 269 | module ?: "kernel"); | 271 | pr_err("Failed to find path of %s module.\n", |
| 272 | module ?: "kernel"); | ||
| 270 | return NULL; | 273 | return NULL; |
| 271 | } | 274 | } |
| 272 | } | 275 | } |
| 273 | return debuginfo__new(path); | 276 | ret = debuginfo__new(path); |
| 277 | if (!ret && !silent) { | ||
| 278 | pr_warning("The %s file has no debug information.\n", path); | ||
| 279 | if (!module || !strtailcmp(path, ".ko")) | ||
| 280 | pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, "); | ||
| 281 | else | ||
| 282 | pr_warning("Rebuild with -g, "); | ||
| 283 | pr_warning("or install an appropriate debuginfo package.\n"); | ||
| 284 | } | ||
| 285 | return ret; | ||
| 274 | } | 286 | } |
| 275 | 287 | ||
| 288 | |||
| 276 | static int get_text_start_address(const char *exec, unsigned long *address) | 289 | static int get_text_start_address(const char *exec, unsigned long *address) |
| 277 | { | 290 | { |
| 278 | Elf *elf; | 291 | Elf *elf; |
| @@ -333,15 +346,13 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, | |||
| 333 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, | 346 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, |
| 334 | tp->module ? : "kernel"); | 347 | tp->module ? : "kernel"); |
| 335 | 348 | ||
| 336 | dinfo = open_debuginfo(tp->module); | 349 | dinfo = open_debuginfo(tp->module, verbose == 0); |
| 337 | if (dinfo) { | 350 | if (dinfo) { |
| 338 | ret = debuginfo__find_probe_point(dinfo, | 351 | ret = debuginfo__find_probe_point(dinfo, |
| 339 | (unsigned long)addr, pp); | 352 | (unsigned long)addr, pp); |
| 340 | debuginfo__delete(dinfo); | 353 | debuginfo__delete(dinfo); |
| 341 | } else { | 354 | } else |
| 342 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", addr); | ||
| 343 | ret = -ENOENT; | 355 | ret = -ENOENT; |
| 344 | } | ||
| 345 | 356 | ||
| 346 | if (ret > 0) { | 357 | if (ret > 0) { |
| 347 | pp->retprobe = tp->retprobe; | 358 | pp->retprobe = tp->retprobe; |
| @@ -457,13 +468,11 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
| 457 | struct debuginfo *dinfo; | 468 | struct debuginfo *dinfo; |
| 458 | int ntevs, ret = 0; | 469 | int ntevs, ret = 0; |
| 459 | 470 | ||
| 460 | dinfo = open_debuginfo(target); | 471 | dinfo = open_debuginfo(target, !need_dwarf); |
| 461 | 472 | ||
| 462 | if (!dinfo) { | 473 | if (!dinfo) { |
| 463 | if (need_dwarf) { | 474 | if (need_dwarf) |
| 464 | pr_warning("Failed to open debuginfo file.\n"); | ||
| 465 | return -ENOENT; | 475 | return -ENOENT; |
| 466 | } | ||
| 467 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); | 476 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); |
| 468 | return 0; | 477 | return 0; |
| 469 | } | 478 | } |
| @@ -565,7 +574,7 @@ static int get_real_path(const char *raw_path, const char *comp_dir, | |||
| 565 | 574 | ||
| 566 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) | 575 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) |
| 567 | { | 576 | { |
| 568 | char buf[LINEBUF_SIZE]; | 577 | char buf[LINEBUF_SIZE], sbuf[STRERR_BUFSIZE]; |
| 569 | const char *color = show_num ? "" : PERF_COLOR_BLUE; | 578 | const char *color = show_num ? "" : PERF_COLOR_BLUE; |
| 570 | const char *prefix = NULL; | 579 | const char *prefix = NULL; |
| 571 | 580 | ||
| @@ -585,7 +594,8 @@ static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) | |||
| 585 | return 1; | 594 | return 1; |
| 586 | error: | 595 | error: |
| 587 | if (ferror(fp)) { | 596 | if (ferror(fp)) { |
| 588 | pr_warning("File read error: %s\n", strerror(errno)); | 597 | pr_warning("File read error: %s\n", |
| 598 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 589 | return -1; | 599 | return -1; |
| 590 | } | 600 | } |
| 591 | return 0; | 601 | return 0; |
| @@ -618,13 +628,12 @@ static int __show_line_range(struct line_range *lr, const char *module) | |||
| 618 | FILE *fp; | 628 | FILE *fp; |
| 619 | int ret; | 629 | int ret; |
| 620 | char *tmp; | 630 | char *tmp; |
| 631 | char sbuf[STRERR_BUFSIZE]; | ||
| 621 | 632 | ||
| 622 | /* Search a line range */ | 633 | /* Search a line range */ |
| 623 | dinfo = open_debuginfo(module); | 634 | dinfo = open_debuginfo(module, false); |
| 624 | if (!dinfo) { | 635 | if (!dinfo) |
| 625 | pr_warning("Failed to open debuginfo file.\n"); | ||
| 626 | return -ENOENT; | 636 | return -ENOENT; |
| 627 | } | ||
| 628 | 637 | ||
| 629 | ret = debuginfo__find_line_range(dinfo, lr); | 638 | ret = debuginfo__find_line_range(dinfo, lr); |
| 630 | debuginfo__delete(dinfo); | 639 | debuginfo__delete(dinfo); |
| @@ -656,7 +665,7 @@ static int __show_line_range(struct line_range *lr, const char *module) | |||
| 656 | fp = fopen(lr->path, "r"); | 665 | fp = fopen(lr->path, "r"); |
| 657 | if (fp == NULL) { | 666 | if (fp == NULL) { |
| 658 | pr_warning("Failed to open %s: %s\n", lr->path, | 667 | pr_warning("Failed to open %s: %s\n", lr->path, |
| 659 | strerror(errno)); | 668 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 660 | return -errno; | 669 | return -errno; |
| 661 | } | 670 | } |
| 662 | /* Skip to starting line number */ | 671 | /* Skip to starting line number */ |
| @@ -689,11 +698,11 @@ end: | |||
| 689 | return ret; | 698 | return ret; |
| 690 | } | 699 | } |
| 691 | 700 | ||
| 692 | int show_line_range(struct line_range *lr, const char *module) | 701 | int show_line_range(struct line_range *lr, const char *module, bool user) |
| 693 | { | 702 | { |
| 694 | int ret; | 703 | int ret; |
| 695 | 704 | ||
| 696 | ret = init_symbol_maps(false); | 705 | ret = init_symbol_maps(user); |
| 697 | if (ret < 0) | 706 | if (ret < 0) |
| 698 | return ret; | 707 | return ret; |
| 699 | ret = __show_line_range(lr, module); | 708 | ret = __show_line_range(lr, module); |
| @@ -768,13 +777,12 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
| 768 | int i, ret = 0; | 777 | int i, ret = 0; |
| 769 | struct debuginfo *dinfo; | 778 | struct debuginfo *dinfo; |
| 770 | 779 | ||
| 771 | ret = init_symbol_maps(false); | 780 | ret = init_symbol_maps(pevs->uprobes); |
| 772 | if (ret < 0) | 781 | if (ret < 0) |
| 773 | return ret; | 782 | return ret; |
| 774 | 783 | ||
| 775 | dinfo = open_debuginfo(module); | 784 | dinfo = open_debuginfo(module, false); |
| 776 | if (!dinfo) { | 785 | if (!dinfo) { |
| 777 | pr_warning("Failed to open debuginfo file.\n"); | ||
| 778 | ret = -ENOENT; | 786 | ret = -ENOENT; |
| 779 | goto out; | 787 | goto out; |
| 780 | } | 788 | } |
| @@ -815,7 +823,8 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
| 815 | } | 823 | } |
| 816 | 824 | ||
| 817 | int show_line_range(struct line_range *lr __maybe_unused, | 825 | int show_line_range(struct line_range *lr __maybe_unused, |
| 818 | const char *module __maybe_unused) | 826 | const char *module __maybe_unused, |
| 827 | bool user __maybe_unused) | ||
| 819 | { | 828 | { |
| 820 | pr_warning("Debuginfo-analysis is not supported.\n"); | 829 | pr_warning("Debuginfo-analysis is not supported.\n"); |
| 821 | return -ENOSYS; | 830 | return -ENOSYS; |
| @@ -1405,8 +1414,7 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | |||
| 1405 | 1414 | ||
| 1406 | return tmp - buf; | 1415 | return tmp - buf; |
| 1407 | error: | 1416 | error: |
| 1408 | pr_debug("Failed to synthesize perf probe argument: %s\n", | 1417 | pr_debug("Failed to synthesize perf probe argument: %d\n", ret); |
| 1409 | strerror(-ret)); | ||
| 1410 | return ret; | 1418 | return ret; |
| 1411 | } | 1419 | } |
| 1412 | 1420 | ||
| @@ -1455,8 +1463,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | |||
| 1455 | 1463 | ||
| 1456 | return buf; | 1464 | return buf; |
| 1457 | error: | 1465 | error: |
| 1458 | pr_debug("Failed to synthesize perf probe point: %s\n", | 1466 | pr_debug("Failed to synthesize perf probe point: %d\n", ret); |
| 1459 | strerror(-ret)); | ||
| 1460 | free(buf); | 1467 | free(buf); |
| 1461 | return NULL; | 1468 | return NULL; |
| 1462 | } | 1469 | } |
| @@ -1780,10 +1787,11 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) | |||
| 1780 | memset(tev, 0, sizeof(*tev)); | 1787 | memset(tev, 0, sizeof(*tev)); |
| 1781 | } | 1788 | } |
| 1782 | 1789 | ||
| 1783 | static void print_warn_msg(const char *file, bool is_kprobe) | 1790 | static void print_open_warning(int err, bool is_kprobe) |
| 1784 | { | 1791 | { |
| 1792 | char sbuf[STRERR_BUFSIZE]; | ||
| 1785 | 1793 | ||
| 1786 | if (errno == ENOENT) { | 1794 | if (err == -ENOENT) { |
| 1787 | const char *config; | 1795 | const char *config; |
| 1788 | 1796 | ||
| 1789 | if (!is_kprobe) | 1797 | if (!is_kprobe) |
| @@ -1791,25 +1799,43 @@ static void print_warn_msg(const char *file, bool is_kprobe) | |||
| 1791 | else | 1799 | else |
| 1792 | config = "CONFIG_KPROBE_EVENTS"; | 1800 | config = "CONFIG_KPROBE_EVENTS"; |
| 1793 | 1801 | ||
| 1794 | pr_warning("%s file does not exist - please rebuild kernel" | 1802 | pr_warning("%cprobe_events file does not exist" |
| 1795 | " with %s.\n", file, config); | 1803 | " - please rebuild kernel with %s.\n", |
| 1796 | } else | 1804 | is_kprobe ? 'k' : 'u', config); |
| 1797 | pr_warning("Failed to open %s file: %s\n", file, | 1805 | } else if (err == -ENOTSUP) |
| 1798 | strerror(errno)); | 1806 | pr_warning("Debugfs is not mounted.\n"); |
| 1807 | else | ||
| 1808 | pr_warning("Failed to open %cprobe_events: %s\n", | ||
| 1809 | is_kprobe ? 'k' : 'u', | ||
| 1810 | strerror_r(-err, sbuf, sizeof(sbuf))); | ||
| 1811 | } | ||
| 1812 | |||
| 1813 | static void print_both_open_warning(int kerr, int uerr) | ||
| 1814 | { | ||
| 1815 | /* Both kprobes and uprobes are disabled, warn it. */ | ||
| 1816 | if (kerr == -ENOTSUP && uerr == -ENOTSUP) | ||
| 1817 | pr_warning("Debugfs is not mounted.\n"); | ||
| 1818 | else if (kerr == -ENOENT && uerr == -ENOENT) | ||
| 1819 | pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS " | ||
| 1820 | "or/and CONFIG_UPROBE_EVENTS.\n"); | ||
| 1821 | else { | ||
| 1822 | char sbuf[STRERR_BUFSIZE]; | ||
| 1823 | pr_warning("Failed to open kprobe events: %s.\n", | ||
| 1824 | strerror_r(-kerr, sbuf, sizeof(sbuf))); | ||
| 1825 | pr_warning("Failed to open uprobe events: %s.\n", | ||
| 1826 | strerror_r(-uerr, sbuf, sizeof(sbuf))); | ||
| 1827 | } | ||
| 1799 | } | 1828 | } |
| 1800 | 1829 | ||
| 1801 | static int open_probe_events(const char *trace_file, bool readwrite, | 1830 | static int open_probe_events(const char *trace_file, bool readwrite) |
| 1802 | bool is_kprobe) | ||
| 1803 | { | 1831 | { |
| 1804 | char buf[PATH_MAX]; | 1832 | char buf[PATH_MAX]; |
| 1805 | const char *__debugfs; | 1833 | const char *__debugfs; |
| 1806 | int ret; | 1834 | int ret; |
| 1807 | 1835 | ||
| 1808 | __debugfs = debugfs_find_mountpoint(); | 1836 | __debugfs = debugfs_find_mountpoint(); |
| 1809 | if (__debugfs == NULL) { | 1837 | if (__debugfs == NULL) |
| 1810 | pr_warning("Debugfs is not mounted.\n"); | 1838 | return -ENOTSUP; |
| 1811 | return -ENOENT; | ||
| 1812 | } | ||
| 1813 | 1839 | ||
| 1814 | ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); | 1840 | ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); |
| 1815 | if (ret >= 0) { | 1841 | if (ret >= 0) { |
| @@ -1820,19 +1846,19 @@ static int open_probe_events(const char *trace_file, bool readwrite, | |||
| 1820 | ret = open(buf, O_RDONLY, 0); | 1846 | ret = open(buf, O_RDONLY, 0); |
| 1821 | 1847 | ||
| 1822 | if (ret < 0) | 1848 | if (ret < 0) |
| 1823 | print_warn_msg(buf, is_kprobe); | 1849 | ret = -errno; |
| 1824 | } | 1850 | } |
| 1825 | return ret; | 1851 | return ret; |
| 1826 | } | 1852 | } |
| 1827 | 1853 | ||
| 1828 | static int open_kprobe_events(bool readwrite) | 1854 | static int open_kprobe_events(bool readwrite) |
| 1829 | { | 1855 | { |
| 1830 | return open_probe_events("tracing/kprobe_events", readwrite, true); | 1856 | return open_probe_events("tracing/kprobe_events", readwrite); |
| 1831 | } | 1857 | } |
| 1832 | 1858 | ||
| 1833 | static int open_uprobe_events(bool readwrite) | 1859 | static int open_uprobe_events(bool readwrite) |
| 1834 | { | 1860 | { |
| 1835 | return open_probe_events("tracing/uprobe_events", readwrite, false); | 1861 | return open_probe_events("tracing/uprobe_events", readwrite); |
| 1836 | } | 1862 | } |
| 1837 | 1863 | ||
| 1838 | /* Get raw string list of current kprobe_events or uprobe_events */ | 1864 | /* Get raw string list of current kprobe_events or uprobe_events */ |
| @@ -1857,7 +1883,7 @@ static struct strlist *get_probe_trace_command_rawlist(int fd) | |||
| 1857 | p[idx] = '\0'; | 1883 | p[idx] = '\0'; |
| 1858 | ret = strlist__add(sl, buf); | 1884 | ret = strlist__add(sl, buf); |
| 1859 | if (ret < 0) { | 1885 | if (ret < 0) { |
| 1860 | pr_debug("strlist__add failed: %s\n", strerror(-ret)); | 1886 | pr_debug("strlist__add failed (%d)\n", ret); |
| 1861 | strlist__delete(sl); | 1887 | strlist__delete(sl); |
| 1862 | return NULL; | 1888 | return NULL; |
| 1863 | } | 1889 | } |
| @@ -1916,7 +1942,7 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) | |||
| 1916 | 1942 | ||
| 1917 | rawlist = get_probe_trace_command_rawlist(fd); | 1943 | rawlist = get_probe_trace_command_rawlist(fd); |
| 1918 | if (!rawlist) | 1944 | if (!rawlist) |
| 1919 | return -ENOENT; | 1945 | return -ENOMEM; |
| 1920 | 1946 | ||
| 1921 | strlist__for_each(ent, rawlist) { | 1947 | strlist__for_each(ent, rawlist) { |
| 1922 | ret = parse_probe_trace_command(ent->s, &tev); | 1948 | ret = parse_probe_trace_command(ent->s, &tev); |
| @@ -1940,27 +1966,34 @@ static int __show_perf_probe_events(int fd, bool is_kprobe) | |||
| 1940 | /* List up current perf-probe events */ | 1966 | /* List up current perf-probe events */ |
| 1941 | int show_perf_probe_events(void) | 1967 | int show_perf_probe_events(void) |
| 1942 | { | 1968 | { |
| 1943 | int fd, ret; | 1969 | int kp_fd, up_fd, ret; |
| 1944 | 1970 | ||
| 1945 | setup_pager(); | 1971 | setup_pager(); |
| 1946 | fd = open_kprobe_events(false); | ||
| 1947 | |||
| 1948 | if (fd < 0) | ||
| 1949 | return fd; | ||
| 1950 | 1972 | ||
| 1951 | ret = init_symbol_maps(false); | 1973 | ret = init_symbol_maps(false); |
| 1952 | if (ret < 0) | 1974 | if (ret < 0) |
| 1953 | return ret; | 1975 | return ret; |
| 1954 | 1976 | ||
| 1955 | ret = __show_perf_probe_events(fd, true); | 1977 | kp_fd = open_kprobe_events(false); |
| 1956 | close(fd); | 1978 | if (kp_fd >= 0) { |
| 1979 | ret = __show_perf_probe_events(kp_fd, true); | ||
| 1980 | close(kp_fd); | ||
| 1981 | if (ret < 0) | ||
| 1982 | goto out; | ||
| 1983 | } | ||
| 1957 | 1984 | ||
| 1958 | fd = open_uprobe_events(false); | 1985 | up_fd = open_uprobe_events(false); |
| 1959 | if (fd >= 0) { | 1986 | if (kp_fd < 0 && up_fd < 0) { |
| 1960 | ret = __show_perf_probe_events(fd, false); | 1987 | print_both_open_warning(kp_fd, up_fd); |
| 1961 | close(fd); | 1988 | ret = kp_fd; |
| 1989 | goto out; | ||
| 1962 | } | 1990 | } |
| 1963 | 1991 | ||
| 1992 | if (up_fd >= 0) { | ||
| 1993 | ret = __show_perf_probe_events(up_fd, false); | ||
| 1994 | close(up_fd); | ||
| 1995 | } | ||
| 1996 | out: | ||
| 1964 | exit_symbol_maps(); | 1997 | exit_symbol_maps(); |
| 1965 | return ret; | 1998 | return ret; |
| 1966 | } | 1999 | } |
| @@ -1976,6 +2009,8 @@ static struct strlist *get_probe_trace_event_names(int fd, bool include_group) | |||
| 1976 | 2009 | ||
| 1977 | memset(&tev, 0, sizeof(tev)); | 2010 | memset(&tev, 0, sizeof(tev)); |
| 1978 | rawlist = get_probe_trace_command_rawlist(fd); | 2011 | rawlist = get_probe_trace_command_rawlist(fd); |
| 2012 | if (!rawlist) | ||
| 2013 | return NULL; | ||
| 1979 | sl = strlist__new(true, NULL); | 2014 | sl = strlist__new(true, NULL); |
| 1980 | strlist__for_each(ent, rawlist) { | 2015 | strlist__for_each(ent, rawlist) { |
| 1981 | ret = parse_probe_trace_command(ent->s, &tev); | 2016 | ret = parse_probe_trace_command(ent->s, &tev); |
| @@ -2005,6 +2040,7 @@ static int write_probe_trace_event(int fd, struct probe_trace_event *tev) | |||
| 2005 | { | 2040 | { |
| 2006 | int ret = 0; | 2041 | int ret = 0; |
| 2007 | char *buf = synthesize_probe_trace_command(tev); | 2042 | char *buf = synthesize_probe_trace_command(tev); |
| 2043 | char sbuf[STRERR_BUFSIZE]; | ||
| 2008 | 2044 | ||
| 2009 | if (!buf) { | 2045 | if (!buf) { |
| 2010 | pr_debug("Failed to synthesize probe trace event.\n"); | 2046 | pr_debug("Failed to synthesize probe trace event.\n"); |
| @@ -2016,7 +2052,7 @@ static int write_probe_trace_event(int fd, struct probe_trace_event *tev) | |||
| 2016 | ret = write(fd, buf, strlen(buf)); | 2052 | ret = write(fd, buf, strlen(buf)); |
| 2017 | if (ret <= 0) | 2053 | if (ret <= 0) |
| 2018 | pr_warning("Failed to write event: %s\n", | 2054 | pr_warning("Failed to write event: %s\n", |
| 2019 | strerror(errno)); | 2055 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 2020 | } | 2056 | } |
| 2021 | free(buf); | 2057 | free(buf); |
| 2022 | return ret; | 2058 | return ret; |
| @@ -2030,7 +2066,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
| 2030 | /* Try no suffix */ | 2066 | /* Try no suffix */ |
| 2031 | ret = e_snprintf(buf, len, "%s", base); | 2067 | ret = e_snprintf(buf, len, "%s", base); |
| 2032 | if (ret < 0) { | 2068 | if (ret < 0) { |
| 2033 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); | 2069 | pr_debug("snprintf() failed: %d\n", ret); |
| 2034 | return ret; | 2070 | return ret; |
| 2035 | } | 2071 | } |
| 2036 | if (!strlist__has_entry(namelist, buf)) | 2072 | if (!strlist__has_entry(namelist, buf)) |
| @@ -2046,7 +2082,7 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
| 2046 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | 2082 | for (i = 1; i < MAX_EVENT_INDEX; i++) { |
| 2047 | ret = e_snprintf(buf, len, "%s_%d", base, i); | 2083 | ret = e_snprintf(buf, len, "%s_%d", base, i); |
| 2048 | if (ret < 0) { | 2084 | if (ret < 0) { |
| 2049 | pr_debug("snprintf() failed: %s\n", strerror(-ret)); | 2085 | pr_debug("snprintf() failed: %d\n", ret); |
| 2050 | return ret; | 2086 | return ret; |
| 2051 | } | 2087 | } |
| 2052 | if (!strlist__has_entry(namelist, buf)) | 2088 | if (!strlist__has_entry(namelist, buf)) |
| @@ -2075,8 +2111,11 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
| 2075 | else | 2111 | else |
| 2076 | fd = open_kprobe_events(true); | 2112 | fd = open_kprobe_events(true); |
| 2077 | 2113 | ||
| 2078 | if (fd < 0) | 2114 | if (fd < 0) { |
| 2115 | print_open_warning(fd, !pev->uprobes); | ||
| 2079 | return fd; | 2116 | return fd; |
| 2117 | } | ||
| 2118 | |||
| 2080 | /* Get current event names */ | 2119 | /* Get current event names */ |
| 2081 | namelist = get_probe_trace_event_names(fd, false); | 2120 | namelist = get_probe_trace_event_names(fd, false); |
| 2082 | if (!namelist) { | 2121 | if (!namelist) { |
| @@ -2408,7 +2447,8 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
| 2408 | printf("Removed event: %s\n", ent->s); | 2447 | printf("Removed event: %s\n", ent->s); |
| 2409 | return 0; | 2448 | return 0; |
| 2410 | error: | 2449 | error: |
| 2411 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); | 2450 | pr_warning("Failed to delete event: %s\n", |
| 2451 | strerror_r(-ret, buf, sizeof(buf))); | ||
| 2412 | return ret; | 2452 | return ret; |
| 2413 | } | 2453 | } |
| 2414 | 2454 | ||
| @@ -2449,15 +2489,18 @@ int del_perf_probe_events(struct strlist *dellist) | |||
| 2449 | 2489 | ||
| 2450 | /* Get current event names */ | 2490 | /* Get current event names */ |
| 2451 | kfd = open_kprobe_events(true); | 2491 | kfd = open_kprobe_events(true); |
| 2452 | if (kfd < 0) | 2492 | if (kfd >= 0) |
| 2453 | return kfd; | 2493 | namelist = get_probe_trace_event_names(kfd, true); |
| 2454 | 2494 | ||
| 2455 | namelist = get_probe_trace_event_names(kfd, true); | ||
| 2456 | ufd = open_uprobe_events(true); | 2495 | ufd = open_uprobe_events(true); |
| 2457 | |||
| 2458 | if (ufd >= 0) | 2496 | if (ufd >= 0) |
| 2459 | unamelist = get_probe_trace_event_names(ufd, true); | 2497 | unamelist = get_probe_trace_event_names(ufd, true); |
| 2460 | 2498 | ||
| 2499 | if (kfd < 0 && ufd < 0) { | ||
| 2500 | print_both_open_warning(kfd, ufd); | ||
| 2501 | goto error; | ||
| 2502 | } | ||
| 2503 | |||
| 2461 | if (namelist == NULL && unamelist == NULL) | 2504 | if (namelist == NULL && unamelist == NULL) |
| 2462 | goto error; | 2505 | goto error; |
| 2463 | 2506 | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 776c9347a3b6..e01e9943139f 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
| @@ -128,7 +128,8 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
| 128 | bool force_add); | 128 | bool force_add); |
| 129 | extern int del_perf_probe_events(struct strlist *dellist); | 129 | extern int del_perf_probe_events(struct strlist *dellist); |
| 130 | extern int show_perf_probe_events(void); | 130 | extern int show_perf_probe_events(void); |
| 131 | extern int show_line_range(struct line_range *lr, const char *module); | 131 | extern int show_line_range(struct line_range *lr, const char *module, |
| 132 | bool user); | ||
| 132 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 133 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
| 133 | int max_probe_points, const char *module, | 134 | int max_probe_points, const char *module, |
| 134 | struct strfilter *filter, bool externs); | 135 | struct strfilter *filter, bool externs); |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index dca9145d704c..c7918f83b300 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -281,6 +281,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
| 281 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | 281 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; |
| 282 | Dwarf_Die type; | 282 | Dwarf_Die type; |
| 283 | char buf[16]; | 283 | char buf[16]; |
| 284 | char sbuf[STRERR_BUFSIZE]; | ||
| 284 | int bsize, boffs, total; | 285 | int bsize, boffs, total; |
| 285 | int ret; | 286 | int ret; |
| 286 | 287 | ||
| @@ -367,7 +368,7 @@ formatted: | |||
| 367 | if (ret >= 16) | 368 | if (ret >= 16) |
| 368 | ret = -E2BIG; | 369 | ret = -E2BIG; |
| 369 | pr_warning("Failed to convert variable type: %s\n", | 370 | pr_warning("Failed to convert variable type: %s\n", |
| 370 | strerror(-ret)); | 371 | strerror_r(-ret, sbuf, sizeof(sbuf))); |
| 371 | return ret; | 372 | return ret; |
| 372 | } | 373 | } |
| 373 | tvar->type = strdup(buf); | 374 | tvar->type = strdup(buf); |
| @@ -608,14 +609,18 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, | |||
| 608 | return -EINVAL; | 609 | return -EINVAL; |
| 609 | } | 610 | } |
| 610 | 611 | ||
| 611 | /* Get an appropriate symbol from symtab */ | 612 | symbol = dwarf_diename(sp_die); |
| 612 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); | ||
| 613 | if (!symbol) { | 613 | if (!symbol) { |
| 614 | pr_warning("Failed to find symbol at 0x%lx\n", | 614 | /* Try to get the symbol name from symtab */ |
| 615 | (unsigned long)paddr); | 615 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); |
| 616 | return -ENOENT; | 616 | if (!symbol) { |
| 617 | pr_warning("Failed to find symbol at 0x%lx\n", | ||
| 618 | (unsigned long)paddr); | ||
| 619 | return -ENOENT; | ||
| 620 | } | ||
| 621 | eaddr = sym.st_value; | ||
| 617 | } | 622 | } |
| 618 | tp->offset = (unsigned long)(paddr - sym.st_value); | 623 | tp->offset = (unsigned long)(paddr - eaddr); |
| 619 | tp->address = (unsigned long)paddr; | 624 | tp->address = (unsigned long)paddr; |
| 620 | tp->symbol = strdup(symbol); | 625 | tp->symbol = strdup(symbol); |
| 621 | if (!tp->symbol) | 626 | if (!tp->symbol) |
| @@ -779,10 +784,12 @@ static int find_lazy_match_lines(struct intlist *list, | |||
| 779 | size_t line_len; | 784 | size_t line_len; |
| 780 | ssize_t len; | 785 | ssize_t len; |
| 781 | int count = 0, linenum = 1; | 786 | int count = 0, linenum = 1; |
| 787 | char sbuf[STRERR_BUFSIZE]; | ||
| 782 | 788 | ||
| 783 | fp = fopen(fname, "r"); | 789 | fp = fopen(fname, "r"); |
| 784 | if (!fp) { | 790 | if (!fp) { |
| 785 | pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); | 791 | pr_warning("Failed to open %s: %s\n", fname, |
| 792 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 786 | return -errno; | 793 | return -errno; |
| 787 | } | 794 | } |
| 788 | 795 | ||
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 12aa9b0d0ba1..3dda85ca50c1 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
| @@ -736,7 +736,7 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | |||
| 736 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | 736 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) |
| 737 | return NULL; | 737 | return NULL; |
| 738 | 738 | ||
| 739 | n = poll(evlist->pollfd, evlist->nr_fds, timeout); | 739 | n = perf_evlist__poll(evlist, timeout); |
| 740 | if (n < 0) { | 740 | if (n < 0) { |
| 741 | PyErr_SetFromErrno(PyExc_OSError); | 741 | PyErr_SetFromErrno(PyExc_OSError); |
| 742 | return NULL; | 742 | return NULL; |
| @@ -753,9 +753,9 @@ static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, | |||
| 753 | PyObject *list = PyList_New(0); | 753 | PyObject *list = PyList_New(0); |
| 754 | int i; | 754 | int i; |
| 755 | 755 | ||
| 756 | for (i = 0; i < evlist->nr_fds; ++i) { | 756 | for (i = 0; i < evlist->pollfd.nr; ++i) { |
| 757 | PyObject *file; | 757 | PyObject *file; |
| 758 | FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); | 758 | FILE *fp = fdopen(evlist->pollfd.entries[i].fd, "r"); |
| 759 | 759 | ||
| 760 | if (fp == NULL) | 760 | if (fp == NULL) |
| 761 | goto free_list; | 761 | goto free_list; |
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index fe8079edbdc1..cf69325b985f 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c | |||
| @@ -14,6 +14,7 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | |||
| 14 | struct perf_evsel *evsel; | 14 | struct perf_evsel *evsel; |
| 15 | unsigned long flags = perf_event_open_cloexec_flag(); | 15 | unsigned long flags = perf_event_open_cloexec_flag(); |
| 16 | int err = -EAGAIN, fd; | 16 | int err = -EAGAIN, fd; |
| 17 | static pid_t pid = -1; | ||
| 17 | 18 | ||
| 18 | evlist = perf_evlist__new(); | 19 | evlist = perf_evlist__new(); |
| 19 | if (!evlist) | 20 | if (!evlist) |
| @@ -24,14 +25,22 @@ static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | |||
| 24 | 25 | ||
| 25 | evsel = perf_evlist__first(evlist); | 26 | evsel = perf_evlist__first(evlist); |
| 26 | 27 | ||
| 27 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags); | 28 | while (1) { |
| 28 | if (fd < 0) | 29 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags); |
| 29 | goto out_delete; | 30 | if (fd < 0) { |
| 31 | if (pid == -1 && errno == EACCES) { | ||
| 32 | pid = 0; | ||
| 33 | continue; | ||
| 34 | } | ||
| 35 | goto out_delete; | ||
| 36 | } | ||
| 37 | break; | ||
| 38 | } | ||
| 30 | close(fd); | 39 | close(fd); |
| 31 | 40 | ||
| 32 | fn(evsel); | 41 | fn(evsel); |
| 33 | 42 | ||
| 34 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, flags); | 43 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, flags); |
| 35 | if (fd < 0) { | 44 | if (fd < 0) { |
| 36 | if (errno == EINVAL) | 45 | if (errno == EINVAL) |
| 37 | err = -EINVAL; | 46 | err = -EINVAL; |
| @@ -47,7 +56,7 @@ out_delete: | |||
| 47 | 56 | ||
| 48 | static bool perf_probe_api(setup_probe_fn_t fn) | 57 | static bool perf_probe_api(setup_probe_fn_t fn) |
| 49 | { | 58 | { |
| 50 | const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL}; | 59 | const char *try[] = {"cycles:u", "instructions:u", "cpu-clock:u", NULL}; |
| 51 | struct cpu_map *cpus; | 60 | struct cpu_map *cpus; |
| 52 | int cpu, ret, i = 0; | 61 | int cpu, ret, i = 0; |
| 53 | 62 | ||
| @@ -106,7 +115,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) | |||
| 106 | 115 | ||
| 107 | evlist__for_each(evlist, evsel) { | 116 | evlist__for_each(evlist, evsel) { |
| 108 | perf_evsel__config(evsel, opts); | 117 | perf_evsel__config(evsel, opts); |
| 109 | if (!evsel->idx && use_comm_exec) | 118 | if (evsel->tracking && use_comm_exec) |
| 110 | evsel->attr.comm_exec = 1; | 119 | evsel->attr.comm_exec = 1; |
| 111 | } | 120 | } |
| 112 | 121 | ||
| @@ -201,6 +210,7 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) | |||
| 201 | struct perf_evsel *evsel; | 210 | struct perf_evsel *evsel; |
| 202 | int err, fd, cpu; | 211 | int err, fd, cpu; |
| 203 | bool ret = false; | 212 | bool ret = false; |
| 213 | pid_t pid = -1; | ||
| 204 | 214 | ||
| 205 | temp_evlist = perf_evlist__new(); | 215 | temp_evlist = perf_evlist__new(); |
| 206 | if (!temp_evlist) | 216 | if (!temp_evlist) |
| @@ -221,12 +231,20 @@ bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) | |||
| 221 | cpu = evlist->cpus->map[0]; | 231 | cpu = evlist->cpus->map[0]; |
| 222 | } | 232 | } |
| 223 | 233 | ||
| 224 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, | 234 | while (1) { |
| 225 | perf_event_open_cloexec_flag()); | 235 | fd = sys_perf_event_open(&evsel->attr, pid, cpu, -1, |
| 226 | if (fd >= 0) { | 236 | perf_event_open_cloexec_flag()); |
| 227 | close(fd); | 237 | if (fd < 0) { |
| 228 | ret = true; | 238 | if (pid == -1 && errno == EACCES) { |
| 239 | pid = 0; | ||
| 240 | continue; | ||
| 241 | } | ||
| 242 | goto out_delete; | ||
| 243 | } | ||
| 244 | break; | ||
| 229 | } | 245 | } |
| 246 | close(fd); | ||
| 247 | ret = true; | ||
| 230 | 248 | ||
| 231 | out_delete: | 249 | out_delete: |
| 232 | perf_evlist__delete(temp_evlist); | 250 | perf_evlist__delete(temp_evlist); |
diff --git a/tools/perf/util/run-command.c b/tools/perf/util/run-command.c index da8e9b285f51..34622b53e733 100644 --- a/tools/perf/util/run-command.c +++ b/tools/perf/util/run-command.c | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #include "cache.h" | 1 | #include "cache.h" |
| 2 | #include "run-command.h" | 2 | #include "run-command.h" |
| 3 | #include "exec_cmd.h" | 3 | #include "exec_cmd.h" |
| 4 | #include "debug.h" | ||
| 4 | 5 | ||
| 5 | static inline void close_pair(int fd[2]) | 6 | static inline void close_pair(int fd[2]) |
| 6 | { | 7 | { |
| @@ -19,6 +20,7 @@ int start_command(struct child_process *cmd) | |||
| 19 | { | 20 | { |
| 20 | int need_in, need_out, need_err; | 21 | int need_in, need_out, need_err; |
| 21 | int fdin[2], fdout[2], fderr[2]; | 22 | int fdin[2], fdout[2], fderr[2]; |
| 23 | char sbuf[STRERR_BUFSIZE]; | ||
| 22 | 24 | ||
| 23 | /* | 25 | /* |
| 24 | * In case of errors we must keep the promise to close FDs | 26 | * In case of errors we must keep the promise to close FDs |
| @@ -99,7 +101,7 @@ int start_command(struct child_process *cmd) | |||
| 99 | 101 | ||
| 100 | if (cmd->dir && chdir(cmd->dir)) | 102 | if (cmd->dir && chdir(cmd->dir)) |
| 101 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], | 103 | die("exec %s: cd to %s failed (%s)", cmd->argv[0], |
| 102 | cmd->dir, strerror(errno)); | 104 | cmd->dir, strerror_r(errno, sbuf, sizeof(sbuf))); |
| 103 | if (cmd->env) { | 105 | if (cmd->env) { |
| 104 | for (; *cmd->env; cmd->env++) { | 106 | for (; *cmd->env; cmd->env++) { |
| 105 | if (strchr(*cmd->env, '=')) | 107 | if (strchr(*cmd->env, '=')) |
| @@ -153,6 +155,8 @@ int start_command(struct child_process *cmd) | |||
| 153 | 155 | ||
| 154 | static int wait_or_whine(pid_t pid) | 156 | static int wait_or_whine(pid_t pid) |
| 155 | { | 157 | { |
| 158 | char sbuf[STRERR_BUFSIZE]; | ||
| 159 | |||
| 156 | for (;;) { | 160 | for (;;) { |
| 157 | int status, code; | 161 | int status, code; |
| 158 | pid_t waiting = waitpid(pid, &status, 0); | 162 | pid_t waiting = waitpid(pid, &status, 0); |
| @@ -160,7 +164,8 @@ static int wait_or_whine(pid_t pid) | |||
| 160 | if (waiting < 0) { | 164 | if (waiting < 0) { |
| 161 | if (errno == EINTR) | 165 | if (errno == EINTR) |
| 162 | continue; | 166 | continue; |
| 163 | error("waitpid failed (%s)", strerror(errno)); | 167 | error("waitpid failed (%s)", |
| 168 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 164 | return -ERR_RUN_COMMAND_WAITPID; | 169 | return -ERR_RUN_COMMAND_WAITPID; |
| 165 | } | 170 | } |
| 166 | if (waiting != pid) | 171 | if (waiting != pid) |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index b2dba9c0a3a1..0a01bac4ce02 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
| @@ -432,6 +432,11 @@ error: | |||
| 432 | return err; | 432 | return err; |
| 433 | } | 433 | } |
| 434 | 434 | ||
| 435 | static int perl_flush_script(void) | ||
| 436 | { | ||
| 437 | return 0; | ||
| 438 | } | ||
| 439 | |||
| 435 | /* | 440 | /* |
| 436 | * Stop trace script | 441 | * Stop trace script |
| 437 | */ | 442 | */ |
| @@ -633,6 +638,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile) | |||
| 633 | struct scripting_ops perl_scripting_ops = { | 638 | struct scripting_ops perl_scripting_ops = { |
| 634 | .name = "Perl", | 639 | .name = "Perl", |
| 635 | .start_script = perl_start_script, | 640 | .start_script = perl_start_script, |
| 641 | .flush_script = perl_flush_script, | ||
| 636 | .stop_script = perl_stop_script, | 642 | .stop_script = perl_stop_script, |
| 637 | .process_event = perl_process_event, | 643 | .process_event = perl_process_event, |
| 638 | .generate_script = perl_generate_script, | 644 | .generate_script = perl_generate_script, |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cbce2545da45..56ba07cce549 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
| @@ -73,6 +73,35 @@ static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObj | |||
| 73 | Py_DECREF(val); | 73 | Py_DECREF(val); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | static PyObject *get_handler(const char *handler_name) | ||
| 77 | { | ||
| 78 | PyObject *handler; | ||
| 79 | |||
| 80 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
| 81 | if (handler && !PyCallable_Check(handler)) | ||
| 82 | return NULL; | ||
| 83 | return handler; | ||
| 84 | } | ||
| 85 | |||
| 86 | static void call_object(PyObject *handler, PyObject *args, const char *die_msg) | ||
| 87 | { | ||
| 88 | PyObject *retval; | ||
| 89 | |||
| 90 | retval = PyObject_CallObject(handler, args); | ||
| 91 | if (retval == NULL) | ||
| 92 | handler_call_die(die_msg); | ||
| 93 | Py_DECREF(retval); | ||
| 94 | } | ||
| 95 | |||
| 96 | static void try_call_object(const char *handler_name, PyObject *args) | ||
| 97 | { | ||
| 98 | PyObject *handler; | ||
| 99 | |||
| 100 | handler = get_handler(handler_name); | ||
| 101 | if (handler) | ||
| 102 | call_object(handler, args, handler_name); | ||
| 103 | } | ||
| 104 | |||
| 76 | static void define_value(enum print_arg_type field_type, | 105 | static void define_value(enum print_arg_type field_type, |
| 77 | const char *ev_name, | 106 | const char *ev_name, |
| 78 | const char *field_name, | 107 | const char *field_name, |
| @@ -80,7 +109,7 @@ static void define_value(enum print_arg_type field_type, | |||
| 80 | const char *field_str) | 109 | const char *field_str) |
| 81 | { | 110 | { |
| 82 | const char *handler_name = "define_flag_value"; | 111 | const char *handler_name = "define_flag_value"; |
| 83 | PyObject *handler, *t, *retval; | 112 | PyObject *t; |
| 84 | unsigned long long value; | 113 | unsigned long long value; |
| 85 | unsigned n = 0; | 114 | unsigned n = 0; |
| 86 | 115 | ||
| @@ -98,13 +127,7 @@ static void define_value(enum print_arg_type field_type, | |||
| 98 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); | 127 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); |
| 99 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); | 128 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); |
| 100 | 129 | ||
| 101 | handler = PyDict_GetItemString(main_dict, handler_name); | 130 | try_call_object(handler_name, t); |
| 102 | if (handler && PyCallable_Check(handler)) { | ||
| 103 | retval = PyObject_CallObject(handler, t); | ||
| 104 | if (retval == NULL) | ||
| 105 | handler_call_die(handler_name); | ||
| 106 | Py_DECREF(retval); | ||
| 107 | } | ||
| 108 | 131 | ||
| 109 | Py_DECREF(t); | 132 | Py_DECREF(t); |
| 110 | } | 133 | } |
| @@ -127,7 +150,7 @@ static void define_field(enum print_arg_type field_type, | |||
| 127 | const char *delim) | 150 | const char *delim) |
| 128 | { | 151 | { |
| 129 | const char *handler_name = "define_flag_field"; | 152 | const char *handler_name = "define_flag_field"; |
| 130 | PyObject *handler, *t, *retval; | 153 | PyObject *t; |
| 131 | unsigned n = 0; | 154 | unsigned n = 0; |
| 132 | 155 | ||
| 133 | if (field_type == PRINT_SYMBOL) | 156 | if (field_type == PRINT_SYMBOL) |
| @@ -145,13 +168,7 @@ static void define_field(enum print_arg_type field_type, | |||
| 145 | if (field_type == PRINT_FLAGS) | 168 | if (field_type == PRINT_FLAGS) |
| 146 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); | 169 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); |
| 147 | 170 | ||
| 148 | handler = PyDict_GetItemString(main_dict, handler_name); | 171 | try_call_object(handler_name, t); |
| 149 | if (handler && PyCallable_Check(handler)) { | ||
| 150 | retval = PyObject_CallObject(handler, t); | ||
| 151 | if (retval == NULL) | ||
| 152 | handler_call_die(handler_name); | ||
| 153 | Py_DECREF(retval); | ||
| 154 | } | ||
| 155 | 172 | ||
| 156 | Py_DECREF(t); | 173 | Py_DECREF(t); |
| 157 | } | 174 | } |
| @@ -362,7 +379,7 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 362 | struct thread *thread, | 379 | struct thread *thread, |
| 363 | struct addr_location *al) | 380 | struct addr_location *al) |
| 364 | { | 381 | { |
| 365 | PyObject *handler, *retval, *context, *t, *obj, *callchain; | 382 | PyObject *handler, *context, *t, *obj, *callchain; |
| 366 | PyObject *dict = NULL; | 383 | PyObject *dict = NULL; |
| 367 | static char handler_name[256]; | 384 | static char handler_name[256]; |
| 368 | struct format_field *field; | 385 | struct format_field *field; |
| @@ -387,9 +404,7 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 387 | 404 | ||
| 388 | sprintf(handler_name, "%s__%s", event->system, event->name); | 405 | sprintf(handler_name, "%s__%s", event->system, event->name); |
| 389 | 406 | ||
| 390 | handler = PyDict_GetItemString(main_dict, handler_name); | 407 | handler = get_handler(handler_name); |
| 391 | if (handler && !PyCallable_Check(handler)) | ||
| 392 | handler = NULL; | ||
| 393 | if (!handler) { | 408 | if (!handler) { |
| 394 | dict = PyDict_New(); | 409 | dict = PyDict_New(); |
| 395 | if (!dict) | 410 | if (!dict) |
| @@ -450,19 +465,9 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 450 | Py_FatalError("error resizing Python tuple"); | 465 | Py_FatalError("error resizing Python tuple"); |
| 451 | 466 | ||
| 452 | if (handler) { | 467 | if (handler) { |
| 453 | retval = PyObject_CallObject(handler, t); | 468 | call_object(handler, t, handler_name); |
| 454 | if (retval == NULL) | ||
| 455 | handler_call_die(handler_name); | ||
| 456 | Py_DECREF(retval); | ||
| 457 | } else { | 469 | } else { |
| 458 | handler = PyDict_GetItemString(main_dict, "trace_unhandled"); | 470 | try_call_object("trace_unhandled", t); |
| 459 | if (handler && PyCallable_Check(handler)) { | ||
| 460 | |||
| 461 | retval = PyObject_CallObject(handler, t); | ||
| 462 | if (retval == NULL) | ||
| 463 | handler_call_die("trace_unhandled"); | ||
| 464 | Py_DECREF(retval); | ||
| 465 | } | ||
| 466 | Py_DECREF(dict); | 471 | Py_DECREF(dict); |
| 467 | } | 472 | } |
| 468 | 473 | ||
| @@ -474,7 +479,7 @@ static void python_process_general_event(struct perf_sample *sample, | |||
| 474 | struct thread *thread, | 479 | struct thread *thread, |
| 475 | struct addr_location *al) | 480 | struct addr_location *al) |
| 476 | { | 481 | { |
| 477 | PyObject *handler, *retval, *t, *dict, *callchain, *dict_sample; | 482 | PyObject *handler, *t, *dict, *callchain, *dict_sample; |
| 478 | static char handler_name[64]; | 483 | static char handler_name[64]; |
| 479 | unsigned n = 0; | 484 | unsigned n = 0; |
| 480 | 485 | ||
| @@ -496,8 +501,8 @@ static void python_process_general_event(struct perf_sample *sample, | |||
| 496 | 501 | ||
| 497 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); | 502 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); |
| 498 | 503 | ||
| 499 | handler = PyDict_GetItemString(main_dict, handler_name); | 504 | handler = get_handler(handler_name); |
| 500 | if (!handler || !PyCallable_Check(handler)) | 505 | if (!handler) |
| 501 | goto exit; | 506 | goto exit; |
| 502 | 507 | ||
| 503 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); | 508 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); |
| @@ -539,10 +544,7 @@ static void python_process_general_event(struct perf_sample *sample, | |||
| 539 | if (_PyTuple_Resize(&t, n) == -1) | 544 | if (_PyTuple_Resize(&t, n) == -1) |
| 540 | Py_FatalError("error resizing Python tuple"); | 545 | Py_FatalError("error resizing Python tuple"); |
| 541 | 546 | ||
| 542 | retval = PyObject_CallObject(handler, t); | 547 | call_object(handler, t, handler_name); |
| 543 | if (retval == NULL) | ||
| 544 | handler_call_die(handler_name); | ||
| 545 | Py_DECREF(retval); | ||
| 546 | exit: | 548 | exit: |
| 547 | Py_DECREF(dict); | 549 | Py_DECREF(dict); |
| 548 | Py_DECREF(t); | 550 | Py_DECREF(t); |
| @@ -566,36 +568,24 @@ static void python_process_event(union perf_event *event __maybe_unused, | |||
| 566 | 568 | ||
| 567 | static int run_start_sub(void) | 569 | static int run_start_sub(void) |
| 568 | { | 570 | { |
| 569 | PyObject *handler, *retval; | ||
| 570 | int err = 0; | ||
| 571 | |||
| 572 | main_module = PyImport_AddModule("__main__"); | 571 | main_module = PyImport_AddModule("__main__"); |
| 573 | if (main_module == NULL) | 572 | if (main_module == NULL) |
| 574 | return -1; | 573 | return -1; |
| 575 | Py_INCREF(main_module); | 574 | Py_INCREF(main_module); |
| 576 | 575 | ||
| 577 | main_dict = PyModule_GetDict(main_module); | 576 | main_dict = PyModule_GetDict(main_module); |
| 578 | if (main_dict == NULL) { | 577 | if (main_dict == NULL) |
| 579 | err = -1; | ||
| 580 | goto error; | 578 | goto error; |
| 581 | } | ||
| 582 | Py_INCREF(main_dict); | 579 | Py_INCREF(main_dict); |
| 583 | 580 | ||
| 584 | handler = PyDict_GetItemString(main_dict, "trace_begin"); | 581 | try_call_object("trace_begin", NULL); |
| 585 | if (handler == NULL || !PyCallable_Check(handler)) | ||
| 586 | goto out; | ||
| 587 | 582 | ||
| 588 | retval = PyObject_CallObject(handler, NULL); | 583 | return 0; |
| 589 | if (retval == NULL) | ||
| 590 | handler_call_die("trace_begin"); | ||
| 591 | 584 | ||
| 592 | Py_DECREF(retval); | ||
| 593 | return err; | ||
| 594 | error: | 585 | error: |
| 595 | Py_XDECREF(main_dict); | 586 | Py_XDECREF(main_dict); |
| 596 | Py_XDECREF(main_module); | 587 | Py_XDECREF(main_module); |
| 597 | out: | 588 | return -1; |
| 598 | return err; | ||
| 599 | } | 589 | } |
| 600 | 590 | ||
| 601 | /* | 591 | /* |
| @@ -649,28 +639,23 @@ error: | |||
| 649 | return err; | 639 | return err; |
| 650 | } | 640 | } |
| 651 | 641 | ||
| 642 | static int python_flush_script(void) | ||
| 643 | { | ||
| 644 | return 0; | ||
| 645 | } | ||
| 646 | |||
| 652 | /* | 647 | /* |
| 653 | * Stop trace script | 648 | * Stop trace script |
| 654 | */ | 649 | */ |
| 655 | static int python_stop_script(void) | 650 | static int python_stop_script(void) |
| 656 | { | 651 | { |
| 657 | PyObject *handler, *retval; | 652 | try_call_object("trace_end", NULL); |
| 658 | int err = 0; | ||
| 659 | 653 | ||
| 660 | handler = PyDict_GetItemString(main_dict, "trace_end"); | ||
| 661 | if (handler == NULL || !PyCallable_Check(handler)) | ||
| 662 | goto out; | ||
| 663 | |||
| 664 | retval = PyObject_CallObject(handler, NULL); | ||
| 665 | if (retval == NULL) | ||
| 666 | handler_call_die("trace_end"); | ||
| 667 | Py_DECREF(retval); | ||
| 668 | out: | ||
| 669 | Py_XDECREF(main_dict); | 654 | Py_XDECREF(main_dict); |
| 670 | Py_XDECREF(main_module); | 655 | Py_XDECREF(main_module); |
| 671 | Py_Finalize(); | 656 | Py_Finalize(); |
| 672 | 657 | ||
| 673 | return err; | 658 | return 0; |
| 674 | } | 659 | } |
| 675 | 660 | ||
| 676 | static int python_generate_script(struct pevent *pevent, const char *outfile) | 661 | static int python_generate_script(struct pevent *pevent, const char *outfile) |
| @@ -843,6 +828,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) | |||
| 843 | struct scripting_ops python_scripting_ops = { | 828 | struct scripting_ops python_scripting_ops = { |
| 844 | .name = "Python", | 829 | .name = "Python", |
| 845 | .start_script = python_start_script, | 830 | .start_script = python_start_script, |
| 831 | .flush_script = python_flush_script, | ||
| 846 | .stop_script = python_stop_script, | 832 | .stop_script = python_stop_script, |
| 847 | .process_event = python_process_event, | 833 | .process_event = python_process_event, |
| 848 | .generate_script = python_generate_script, | 834 | .generate_script = python_generate_script, |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 88dfef70c13d..883406f4b381 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "util.h" | 14 | #include "util.h" |
| 15 | #include "cpumap.h" | 15 | #include "cpumap.h" |
| 16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
| 17 | #include "asm/bug.h" | ||
| 17 | 18 | ||
| 18 | static int perf_session__open(struct perf_session *session) | 19 | static int perf_session__open(struct perf_session *session) |
| 19 | { | 20 | { |
| @@ -66,6 +67,25 @@ static void perf_session__destroy_kernel_maps(struct perf_session *session) | |||
| 66 | machines__destroy_kernel_maps(&session->machines); | 67 | machines__destroy_kernel_maps(&session->machines); |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 70 | static bool perf_session__has_comm_exec(struct perf_session *session) | ||
| 71 | { | ||
| 72 | struct perf_evsel *evsel; | ||
| 73 | |||
| 74 | evlist__for_each(session->evlist, evsel) { | ||
| 75 | if (evsel->attr.comm_exec) | ||
| 76 | return true; | ||
| 77 | } | ||
| 78 | |||
| 79 | return false; | ||
| 80 | } | ||
| 81 | |||
| 82 | static void perf_session__set_comm_exec(struct perf_session *session) | ||
| 83 | { | ||
| 84 | bool comm_exec = perf_session__has_comm_exec(session); | ||
| 85 | |||
| 86 | machines__set_comm_exec(&session->machines, comm_exec); | ||
| 87 | } | ||
| 88 | |||
| 69 | struct perf_session *perf_session__new(struct perf_data_file *file, | 89 | struct perf_session *perf_session__new(struct perf_data_file *file, |
| 70 | bool repipe, struct perf_tool *tool) | 90 | bool repipe, struct perf_tool *tool) |
| 71 | { | 91 | { |
| @@ -75,9 +95,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
| 75 | goto out; | 95 | goto out; |
| 76 | 96 | ||
| 77 | session->repipe = repipe; | 97 | session->repipe = repipe; |
| 78 | INIT_LIST_HEAD(&session->ordered_samples.samples); | 98 | ordered_events__init(&session->ordered_events); |
| 79 | INIT_LIST_HEAD(&session->ordered_samples.sample_cache); | ||
| 80 | INIT_LIST_HEAD(&session->ordered_samples.to_free); | ||
| 81 | machines__init(&session->machines); | 99 | machines__init(&session->machines); |
| 82 | 100 | ||
| 83 | if (file) { | 101 | if (file) { |
| @@ -91,6 +109,7 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
| 91 | goto out_close; | 109 | goto out_close; |
| 92 | 110 | ||
| 93 | perf_session__set_id_hdr_size(session); | 111 | perf_session__set_id_hdr_size(session); |
| 112 | perf_session__set_comm_exec(session); | ||
| 94 | } | 113 | } |
| 95 | } | 114 | } |
| 96 | 115 | ||
| @@ -100,13 +119,13 @@ struct perf_session *perf_session__new(struct perf_data_file *file, | |||
| 100 | * kernel MMAP event, in perf_event__process_mmap(). | 119 | * kernel MMAP event, in perf_event__process_mmap(). |
| 101 | */ | 120 | */ |
| 102 | if (perf_session__create_kernel_maps(session) < 0) | 121 | if (perf_session__create_kernel_maps(session) < 0) |
| 103 | goto out_delete; | 122 | pr_warning("Cannot read kernel map\n"); |
| 104 | } | 123 | } |
| 105 | 124 | ||
| 106 | if (tool && tool->ordering_requires_timestamps && | 125 | if (tool && tool->ordering_requires_timestamps && |
| 107 | tool->ordered_samples && !perf_evlist__sample_id_all(session->evlist)) { | 126 | tool->ordered_events && !perf_evlist__sample_id_all(session->evlist)) { |
| 108 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | 127 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); |
| 109 | tool->ordered_samples = false; | 128 | tool->ordered_events = false; |
| 110 | } | 129 | } |
| 111 | 130 | ||
| 112 | return session; | 131 | return session; |
| @@ -238,7 +257,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
| 238 | if (tool->build_id == NULL) | 257 | if (tool->build_id == NULL) |
| 239 | tool->build_id = process_finished_round_stub; | 258 | tool->build_id = process_finished_round_stub; |
| 240 | if (tool->finished_round == NULL) { | 259 | if (tool->finished_round == NULL) { |
| 241 | if (tool->ordered_samples) | 260 | if (tool->ordered_events) |
| 242 | tool->finished_round = process_finished_round; | 261 | tool->finished_round = process_finished_round; |
| 243 | else | 262 | else |
| 244 | tool->finished_round = process_finished_round_stub; | 263 | tool->finished_round = process_finished_round_stub; |
| @@ -444,87 +463,6 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
| 444 | [PERF_RECORD_HEADER_MAX] = NULL, | 463 | [PERF_RECORD_HEADER_MAX] = NULL, |
| 445 | }; | 464 | }; |
| 446 | 465 | ||
| 447 | struct sample_queue { | ||
| 448 | u64 timestamp; | ||
| 449 | u64 file_offset; | ||
| 450 | union perf_event *event; | ||
| 451 | struct list_head list; | ||
| 452 | }; | ||
| 453 | |||
| 454 | static void perf_session_free_sample_buffers(struct perf_session *session) | ||
| 455 | { | ||
| 456 | struct ordered_samples *os = &session->ordered_samples; | ||
| 457 | |||
| 458 | while (!list_empty(&os->to_free)) { | ||
| 459 | struct sample_queue *sq; | ||
| 460 | |||
| 461 | sq = list_entry(os->to_free.next, struct sample_queue, list); | ||
| 462 | list_del(&sq->list); | ||
| 463 | free(sq); | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | static int perf_session_deliver_event(struct perf_session *session, | ||
| 468 | union perf_event *event, | ||
| 469 | struct perf_sample *sample, | ||
| 470 | struct perf_tool *tool, | ||
| 471 | u64 file_offset); | ||
| 472 | |||
| 473 | static int flush_sample_queue(struct perf_session *s, | ||
| 474 | struct perf_tool *tool) | ||
| 475 | { | ||
| 476 | struct ordered_samples *os = &s->ordered_samples; | ||
| 477 | struct list_head *head = &os->samples; | ||
| 478 | struct sample_queue *tmp, *iter; | ||
| 479 | struct perf_sample sample; | ||
| 480 | u64 limit = os->next_flush; | ||
| 481 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | ||
| 482 | bool show_progress = limit == ULLONG_MAX; | ||
| 483 | struct ui_progress prog; | ||
| 484 | int ret; | ||
| 485 | |||
| 486 | if (!tool->ordered_samples || !limit) | ||
| 487 | return 0; | ||
| 488 | |||
| 489 | if (show_progress) | ||
| 490 | ui_progress__init(&prog, os->nr_samples, "Processing time ordered events..."); | ||
| 491 | |||
| 492 | list_for_each_entry_safe(iter, tmp, head, list) { | ||
| 493 | if (session_done()) | ||
| 494 | return 0; | ||
| 495 | |||
| 496 | if (iter->timestamp > limit) | ||
| 497 | break; | ||
| 498 | |||
| 499 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); | ||
| 500 | if (ret) | ||
| 501 | pr_err("Can't parse sample, err = %d\n", ret); | ||
| 502 | else { | ||
| 503 | ret = perf_session_deliver_event(s, iter->event, &sample, tool, | ||
| 504 | iter->file_offset); | ||
| 505 | if (ret) | ||
| 506 | return ret; | ||
| 507 | } | ||
| 508 | |||
| 509 | os->last_flush = iter->timestamp; | ||
| 510 | list_del(&iter->list); | ||
| 511 | list_add(&iter->list, &os->sample_cache); | ||
| 512 | os->nr_samples--; | ||
| 513 | |||
| 514 | if (show_progress) | ||
| 515 | ui_progress__update(&prog, 1); | ||
| 516 | } | ||
| 517 | |||
| 518 | if (list_empty(head)) { | ||
| 519 | os->last_sample = NULL; | ||
| 520 | } else if (last_ts <= limit) { | ||
| 521 | os->last_sample = | ||
| 522 | list_entry(head->prev, struct sample_queue, list); | ||
| 523 | } | ||
| 524 | |||
| 525 | return 0; | ||
| 526 | } | ||
| 527 | |||
| 528 | /* | 466 | /* |
| 529 | * When perf record finishes a pass on every buffers, it records this pseudo | 467 | * When perf record finishes a pass on every buffers, it records this pseudo |
| 530 | * event. | 468 | * event. |
| @@ -568,99 +506,43 @@ static int process_finished_round(struct perf_tool *tool, | |||
| 568 | union perf_event *event __maybe_unused, | 506 | union perf_event *event __maybe_unused, |
| 569 | struct perf_session *session) | 507 | struct perf_session *session) |
| 570 | { | 508 | { |
| 571 | int ret = flush_sample_queue(session, tool); | 509 | return ordered_events__flush(session, tool, OE_FLUSH__ROUND); |
| 572 | if (!ret) | ||
| 573 | session->ordered_samples.next_flush = session->ordered_samples.max_timestamp; | ||
| 574 | |||
| 575 | return ret; | ||
| 576 | } | ||
| 577 | |||
| 578 | /* The queue is ordered by time */ | ||
| 579 | static void __queue_event(struct sample_queue *new, struct perf_session *s) | ||
| 580 | { | ||
| 581 | struct ordered_samples *os = &s->ordered_samples; | ||
| 582 | struct sample_queue *sample = os->last_sample; | ||
| 583 | u64 timestamp = new->timestamp; | ||
| 584 | struct list_head *p; | ||
| 585 | |||
| 586 | ++os->nr_samples; | ||
| 587 | os->last_sample = new; | ||
| 588 | |||
| 589 | if (!sample) { | ||
| 590 | list_add(&new->list, &os->samples); | ||
| 591 | os->max_timestamp = timestamp; | ||
| 592 | return; | ||
| 593 | } | ||
| 594 | |||
| 595 | /* | ||
| 596 | * last_sample might point to some random place in the list as it's | ||
| 597 | * the last queued event. We expect that the new event is close to | ||
| 598 | * this. | ||
| 599 | */ | ||
| 600 | if (sample->timestamp <= timestamp) { | ||
| 601 | while (sample->timestamp <= timestamp) { | ||
| 602 | p = sample->list.next; | ||
| 603 | if (p == &os->samples) { | ||
| 604 | list_add_tail(&new->list, &os->samples); | ||
| 605 | os->max_timestamp = timestamp; | ||
| 606 | return; | ||
| 607 | } | ||
| 608 | sample = list_entry(p, struct sample_queue, list); | ||
| 609 | } | ||
| 610 | list_add_tail(&new->list, &sample->list); | ||
| 611 | } else { | ||
| 612 | while (sample->timestamp > timestamp) { | ||
| 613 | p = sample->list.prev; | ||
| 614 | if (p == &os->samples) { | ||
| 615 | list_add(&new->list, &os->samples); | ||
| 616 | return; | ||
| 617 | } | ||
| 618 | sample = list_entry(p, struct sample_queue, list); | ||
| 619 | } | ||
| 620 | list_add(&new->list, &sample->list); | ||
| 621 | } | ||
| 622 | } | 510 | } |
| 623 | 511 | ||
| 624 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) | ||
| 625 | |||
| 626 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 512 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
| 627 | struct perf_sample *sample, u64 file_offset) | 513 | struct perf_tool *tool, struct perf_sample *sample, |
| 514 | u64 file_offset) | ||
| 628 | { | 515 | { |
| 629 | struct ordered_samples *os = &s->ordered_samples; | 516 | struct ordered_events *oe = &s->ordered_events; |
| 630 | struct list_head *sc = &os->sample_cache; | ||
| 631 | u64 timestamp = sample->time; | 517 | u64 timestamp = sample->time; |
| 632 | struct sample_queue *new; | 518 | struct ordered_event *new; |
| 633 | 519 | ||
| 634 | if (!timestamp || timestamp == ~0ULL) | 520 | if (!timestamp || timestamp == ~0ULL) |
| 635 | return -ETIME; | 521 | return -ETIME; |
| 636 | 522 | ||
| 637 | if (timestamp < s->ordered_samples.last_flush) { | 523 | if (timestamp < oe->last_flush) { |
| 638 | printf("Warning: Timestamp below last timeslice flush\n"); | 524 | WARN_ONCE(1, "Timestamp below last timeslice flush\n"); |
| 639 | return -EINVAL; | 525 | |
| 526 | pr_oe_time(timestamp, "out of order event"); | ||
| 527 | pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n", | ||
| 528 | oe->last_flush_type); | ||
| 529 | |||
| 530 | /* We could get out of order messages after forced flush. */ | ||
| 531 | if (oe->last_flush_type != OE_FLUSH__HALF) | ||
| 532 | return -EINVAL; | ||
| 640 | } | 533 | } |
| 641 | 534 | ||
| 642 | if (!list_empty(sc)) { | 535 | new = ordered_events__new(oe, timestamp); |
| 643 | new = list_entry(sc->next, struct sample_queue, list); | 536 | if (!new) { |
| 644 | list_del(&new->list); | 537 | ordered_events__flush(s, tool, OE_FLUSH__HALF); |
| 645 | } else if (os->sample_buffer) { | 538 | new = ordered_events__new(oe, timestamp); |
| 646 | new = os->sample_buffer + os->sample_buffer_idx; | ||
| 647 | if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER) | ||
| 648 | os->sample_buffer = NULL; | ||
| 649 | } else { | ||
| 650 | os->sample_buffer = malloc(MAX_SAMPLE_BUFFER * sizeof(*new)); | ||
| 651 | if (!os->sample_buffer) | ||
| 652 | return -ENOMEM; | ||
| 653 | list_add(&os->sample_buffer->list, &os->to_free); | ||
| 654 | os->sample_buffer_idx = 2; | ||
| 655 | new = os->sample_buffer + 1; | ||
| 656 | } | 539 | } |
| 657 | 540 | ||
| 658 | new->timestamp = timestamp; | 541 | if (!new) |
| 542 | return -ENOMEM; | ||
| 543 | |||
| 659 | new->file_offset = file_offset; | 544 | new->file_offset = file_offset; |
| 660 | new->event = event; | 545 | new->event = event; |
| 661 | |||
| 662 | __queue_event(new, s); | ||
| 663 | |||
| 664 | return 0; | 546 | return 0; |
| 665 | } | 547 | } |
| 666 | 548 | ||
| @@ -920,11 +802,10 @@ perf_session__deliver_sample(struct perf_session *session, | |||
| 920 | &sample->read.one, machine); | 802 | &sample->read.one, machine); |
| 921 | } | 803 | } |
| 922 | 804 | ||
| 923 | static int perf_session_deliver_event(struct perf_session *session, | 805 | int perf_session__deliver_event(struct perf_session *session, |
| 924 | union perf_event *event, | 806 | union perf_event *event, |
| 925 | struct perf_sample *sample, | 807 | struct perf_sample *sample, |
| 926 | struct perf_tool *tool, | 808 | struct perf_tool *tool, u64 file_offset) |
| 927 | u64 file_offset) | ||
| 928 | { | 809 | { |
| 929 | struct perf_evsel *evsel; | 810 | struct perf_evsel *evsel; |
| 930 | struct machine *machine; | 811 | struct machine *machine; |
| @@ -1005,8 +886,10 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
| 1005 | switch (event->header.type) { | 886 | switch (event->header.type) { |
| 1006 | case PERF_RECORD_HEADER_ATTR: | 887 | case PERF_RECORD_HEADER_ATTR: |
| 1007 | err = tool->attr(tool, event, &session->evlist); | 888 | err = tool->attr(tool, event, &session->evlist); |
| 1008 | if (err == 0) | 889 | if (err == 0) { |
| 1009 | perf_session__set_id_hdr_size(session); | 890 | perf_session__set_id_hdr_size(session); |
| 891 | perf_session__set_comm_exec(session); | ||
| 892 | } | ||
| 1010 | return err; | 893 | return err; |
| 1011 | case PERF_RECORD_HEADER_EVENT_TYPE: | 894 | case PERF_RECORD_HEADER_EVENT_TYPE: |
| 1012 | /* | 895 | /* |
| @@ -1036,6 +919,61 @@ static void event_swap(union perf_event *event, bool sample_id_all) | |||
| 1036 | swap(event, sample_id_all); | 919 | swap(event, sample_id_all); |
| 1037 | } | 920 | } |
| 1038 | 921 | ||
| 922 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
| 923 | void *buf, size_t buf_sz, | ||
| 924 | union perf_event **event_ptr, | ||
| 925 | struct perf_sample *sample) | ||
| 926 | { | ||
| 927 | union perf_event *event; | ||
| 928 | size_t hdr_sz, rest; | ||
| 929 | int fd; | ||
| 930 | |||
| 931 | if (session->one_mmap && !session->header.needs_swap) { | ||
| 932 | event = file_offset - session->one_mmap_offset + | ||
| 933 | session->one_mmap_addr; | ||
| 934 | goto out_parse_sample; | ||
| 935 | } | ||
| 936 | |||
| 937 | if (perf_data_file__is_pipe(session->file)) | ||
| 938 | return -1; | ||
| 939 | |||
| 940 | fd = perf_data_file__fd(session->file); | ||
| 941 | hdr_sz = sizeof(struct perf_event_header); | ||
| 942 | |||
| 943 | if (buf_sz < hdr_sz) | ||
| 944 | return -1; | ||
| 945 | |||
| 946 | if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 || | ||
| 947 | readn(fd, &buf, hdr_sz) != (ssize_t)hdr_sz) | ||
| 948 | return -1; | ||
| 949 | |||
| 950 | event = (union perf_event *)buf; | ||
| 951 | |||
| 952 | if (session->header.needs_swap) | ||
| 953 | perf_event_header__bswap(&event->header); | ||
| 954 | |||
| 955 | if (event->header.size < hdr_sz) | ||
| 956 | return -1; | ||
| 957 | |||
| 958 | rest = event->header.size - hdr_sz; | ||
| 959 | |||
| 960 | if (readn(fd, &buf, rest) != (ssize_t)rest) | ||
| 961 | return -1; | ||
| 962 | |||
| 963 | if (session->header.needs_swap) | ||
| 964 | event_swap(event, perf_evlist__sample_id_all(session->evlist)); | ||
| 965 | |||
| 966 | out_parse_sample: | ||
| 967 | |||
| 968 | if (sample && event->header.type < PERF_RECORD_USER_TYPE_START && | ||
| 969 | perf_evlist__parse_sample(session->evlist, event, sample)) | ||
| 970 | return -1; | ||
| 971 | |||
| 972 | *event_ptr = event; | ||
| 973 | |||
| 974 | return 0; | ||
| 975 | } | ||
| 976 | |||
| 1039 | static s64 perf_session__process_event(struct perf_session *session, | 977 | static s64 perf_session__process_event(struct perf_session *session, |
| 1040 | union perf_event *event, | 978 | union perf_event *event, |
| 1041 | struct perf_tool *tool, | 979 | struct perf_tool *tool, |
| @@ -1062,15 +1000,15 @@ static s64 perf_session__process_event(struct perf_session *session, | |||
| 1062 | if (ret) | 1000 | if (ret) |
| 1063 | return ret; | 1001 | return ret; |
| 1064 | 1002 | ||
| 1065 | if (tool->ordered_samples) { | 1003 | if (tool->ordered_events) { |
| 1066 | ret = perf_session_queue_event(session, event, &sample, | 1004 | ret = perf_session_queue_event(session, event, tool, &sample, |
| 1067 | file_offset); | 1005 | file_offset); |
| 1068 | if (ret != -ETIME) | 1006 | if (ret != -ETIME) |
| 1069 | return ret; | 1007 | return ret; |
| 1070 | } | 1008 | } |
| 1071 | 1009 | ||
| 1072 | return perf_session_deliver_event(session, event, &sample, tool, | 1010 | return perf_session__deliver_event(session, event, &sample, tool, |
| 1073 | file_offset); | 1011 | file_offset); |
| 1074 | } | 1012 | } |
| 1075 | 1013 | ||
| 1076 | void perf_event_header__bswap(struct perf_event_header *hdr) | 1014 | void perf_event_header__bswap(struct perf_event_header *hdr) |
| @@ -1222,12 +1160,11 @@ more: | |||
| 1222 | goto more; | 1160 | goto more; |
| 1223 | done: | 1161 | done: |
| 1224 | /* do the final flush for ordered samples */ | 1162 | /* do the final flush for ordered samples */ |
| 1225 | session->ordered_samples.next_flush = ULLONG_MAX; | 1163 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
| 1226 | err = flush_sample_queue(session, tool); | ||
| 1227 | out_err: | 1164 | out_err: |
| 1228 | free(buf); | 1165 | free(buf); |
| 1229 | perf_session__warn_about_errors(session, tool); | 1166 | perf_session__warn_about_errors(session, tool); |
| 1230 | perf_session_free_sample_buffers(session); | 1167 | ordered_events__free(&session->ordered_events); |
| 1231 | return err; | 1168 | return err; |
| 1232 | } | 1169 | } |
| 1233 | 1170 | ||
| @@ -1368,12 +1305,11 @@ more: | |||
| 1368 | 1305 | ||
| 1369 | out: | 1306 | out: |
| 1370 | /* do the final flush for ordered samples */ | 1307 | /* do the final flush for ordered samples */ |
| 1371 | session->ordered_samples.next_flush = ULLONG_MAX; | 1308 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
| 1372 | err = flush_sample_queue(session, tool); | ||
| 1373 | out_err: | 1309 | out_err: |
| 1374 | ui_progress__finish(); | 1310 | ui_progress__finish(); |
| 1375 | perf_session__warn_about_errors(session, tool); | 1311 | perf_session__warn_about_errors(session, tool); |
| 1376 | perf_session_free_sample_buffers(session); | 1312 | ordered_events__free(&session->ordered_events); |
| 1377 | session->one_mmap = false; | 1313 | session->one_mmap = false; |
| 1378 | return err; | 1314 | return err; |
| 1379 | } | 1315 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0321013bd9fd..ffb440462008 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -9,26 +9,13 @@ | |||
| 9 | #include "symbol.h" | 9 | #include "symbol.h" |
| 10 | #include "thread.h" | 10 | #include "thread.h" |
| 11 | #include "data.h" | 11 | #include "data.h" |
| 12 | #include "ordered-events.h" | ||
| 12 | #include <linux/rbtree.h> | 13 | #include <linux/rbtree.h> |
| 13 | #include <linux/perf_event.h> | 14 | #include <linux/perf_event.h> |
| 14 | 15 | ||
| 15 | struct sample_queue; | ||
| 16 | struct ip_callchain; | 16 | struct ip_callchain; |
| 17 | struct thread; | 17 | struct thread; |
| 18 | 18 | ||
| 19 | struct ordered_samples { | ||
| 20 | u64 last_flush; | ||
| 21 | u64 next_flush; | ||
| 22 | u64 max_timestamp; | ||
| 23 | struct list_head samples; | ||
| 24 | struct list_head sample_cache; | ||
| 25 | struct list_head to_free; | ||
| 26 | struct sample_queue *sample_buffer; | ||
| 27 | struct sample_queue *last_sample; | ||
| 28 | int sample_buffer_idx; | ||
| 29 | unsigned int nr_samples; | ||
| 30 | }; | ||
| 31 | |||
| 32 | struct perf_session { | 19 | struct perf_session { |
| 33 | struct perf_header header; | 20 | struct perf_header header; |
| 34 | struct machines machines; | 21 | struct machines machines; |
| @@ -39,7 +26,7 @@ struct perf_session { | |||
| 39 | bool one_mmap; | 26 | bool one_mmap; |
| 40 | void *one_mmap_addr; | 27 | void *one_mmap_addr; |
| 41 | u64 one_mmap_offset; | 28 | u64 one_mmap_offset; |
| 42 | struct ordered_samples ordered_samples; | 29 | struct ordered_events ordered_events; |
| 43 | struct perf_data_file *file; | 30 | struct perf_data_file *file; |
| 44 | }; | 31 | }; |
| 45 | 32 | ||
| @@ -58,6 +45,11 @@ void perf_session__delete(struct perf_session *session); | |||
| 58 | 45 | ||
| 59 | void perf_event_header__bswap(struct perf_event_header *hdr); | 46 | void perf_event_header__bswap(struct perf_event_header *hdr); |
| 60 | 47 | ||
| 48 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
| 49 | void *buf, size_t buf_sz, | ||
| 50 | union perf_event **event_ptr, | ||
| 51 | struct perf_sample *sample); | ||
| 52 | |||
| 61 | int __perf_session__process_events(struct perf_session *session, | 53 | int __perf_session__process_events(struct perf_session *session, |
| 62 | u64 data_offset, u64 data_size, u64 size, | 54 | u64 data_offset, u64 data_size, u64 size, |
| 63 | struct perf_tool *tool); | 55 | struct perf_tool *tool); |
| @@ -65,10 +57,16 @@ int perf_session__process_events(struct perf_session *session, | |||
| 65 | struct perf_tool *tool); | 57 | struct perf_tool *tool); |
| 66 | 58 | ||
| 67 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 59 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
| 68 | struct perf_sample *sample, u64 file_offset); | 60 | struct perf_tool *tool, struct perf_sample *sample, |
| 61 | u64 file_offset); | ||
| 69 | 62 | ||
| 70 | void perf_tool__fill_defaults(struct perf_tool *tool); | 63 | void perf_tool__fill_defaults(struct perf_tool *tool); |
| 71 | 64 | ||
| 65 | int perf_session__deliver_event(struct perf_session *session, | ||
| 66 | union perf_event *event, | ||
| 67 | struct perf_sample *sample, | ||
| 68 | struct perf_tool *tool, u64 file_offset); | ||
| 69 | |||
| 72 | int perf_session__resolve_callchain(struct perf_session *session, | 70 | int perf_session__resolve_callchain(struct perf_session *session, |
| 73 | struct perf_evsel *evsel, | 71 | struct perf_evsel *evsel, |
| 74 | struct thread *thread, | 72 | struct thread *thread, |
| @@ -128,5 +126,5 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
| 128 | 126 | ||
| 129 | extern volatile int session_done; | 127 | extern volatile int session_done; |
| 130 | 128 | ||
| 131 | #define session_done() (*(volatile int *)(&session_done)) | 129 | #define session_done() ACCESS_ONCE(session_done) |
| 132 | #endif /* __PERF_SESSION_H */ | 130 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 14e5a039bc45..289df9d1e65a 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -70,12 +70,14 @@ static int hist_entry__thread_snprintf(struct hist_entry *he, char *bf, | |||
| 70 | size_t size, unsigned int width) | 70 | size_t size, unsigned int width) |
| 71 | { | 71 | { |
| 72 | const char *comm = thread__comm_str(he->thread); | 72 | const char *comm = thread__comm_str(he->thread); |
| 73 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, | 73 | |
| 74 | comm ?: "", he->thread->tid); | 74 | width = max(7U, width) - 6; |
| 75 | return repsep_snprintf(bf, size, "%5d:%-*.*s", he->thread->tid, | ||
| 76 | width, width, comm ?: ""); | ||
| 75 | } | 77 | } |
| 76 | 78 | ||
| 77 | struct sort_entry sort_thread = { | 79 | struct sort_entry sort_thread = { |
| 78 | .se_header = "Command: Pid", | 80 | .se_header = " Pid:Command", |
| 79 | .se_cmp = sort__thread_cmp, | 81 | .se_cmp = sort__thread_cmp, |
| 80 | .se_snprintf = hist_entry__thread_snprintf, | 82 | .se_snprintf = hist_entry__thread_snprintf, |
| 81 | .se_width_idx = HISTC_THREAD, | 83 | .se_width_idx = HISTC_THREAD, |
| @@ -106,7 +108,7 @@ sort__comm_sort(struct hist_entry *left, struct hist_entry *right) | |||
| 106 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, | 108 | static int hist_entry__comm_snprintf(struct hist_entry *he, char *bf, |
| 107 | size_t size, unsigned int width) | 109 | size_t size, unsigned int width) |
| 108 | { | 110 | { |
| 109 | return repsep_snprintf(bf, size, "%*s", width, comm__str(he->comm)); | 111 | return repsep_snprintf(bf, size, "%-*.*s", width, width, comm__str(he->comm)); |
| 110 | } | 112 | } |
| 111 | 113 | ||
| 112 | struct sort_entry sort_comm = { | 114 | struct sort_entry sort_comm = { |
| @@ -152,10 +154,10 @@ static int _hist_entry__dso_snprintf(struct map *map, char *bf, | |||
| 152 | if (map && map->dso) { | 154 | if (map && map->dso) { |
| 153 | const char *dso_name = !verbose ? map->dso->short_name : | 155 | const char *dso_name = !verbose ? map->dso->short_name : |
| 154 | map->dso->long_name; | 156 | map->dso->long_name; |
| 155 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); | 157 | return repsep_snprintf(bf, size, "%-*.*s", width, width, dso_name); |
| 156 | } | 158 | } |
| 157 | 159 | ||
| 158 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 160 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "[unknown]"); |
| 159 | } | 161 | } |
| 160 | 162 | ||
| 161 | static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, | 163 | static int hist_entry__dso_snprintf(struct hist_entry *he, char *bf, |
| @@ -257,7 +259,10 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
| 257 | width - ret, ""); | 259 | width - ret, ""); |
| 258 | } | 260 | } |
| 259 | 261 | ||
| 260 | return ret; | 262 | if (ret > width) |
| 263 | bf[width] = '\0'; | ||
| 264 | |||
| 265 | return width; | ||
| 261 | } | 266 | } |
| 262 | 267 | ||
| 263 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, | 268 | static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, |
| @@ -302,10 +307,9 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 302 | } | 307 | } |
| 303 | 308 | ||
| 304 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, | 309 | static int hist_entry__srcline_snprintf(struct hist_entry *he, char *bf, |
| 305 | size_t size, | 310 | size_t size, unsigned int width) |
| 306 | unsigned int width __maybe_unused) | ||
| 307 | { | 311 | { |
| 308 | return repsep_snprintf(bf, size, "%s", he->srcline); | 312 | return repsep_snprintf(bf, size, "%*.*-s", width, width, he->srcline); |
| 309 | } | 313 | } |
| 310 | 314 | ||
| 311 | struct sort_entry sort_srcline = { | 315 | struct sort_entry sort_srcline = { |
| @@ -332,7 +336,7 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 332 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, | 336 | static int hist_entry__parent_snprintf(struct hist_entry *he, char *bf, |
| 333 | size_t size, unsigned int width) | 337 | size_t size, unsigned int width) |
| 334 | { | 338 | { |
| 335 | return repsep_snprintf(bf, size, "%-*s", width, | 339 | return repsep_snprintf(bf, size, "%-*.*s", width, width, |
| 336 | he->parent ? he->parent->name : "[other]"); | 340 | he->parent ? he->parent->name : "[other]"); |
| 337 | } | 341 | } |
| 338 | 342 | ||
| @@ -354,7 +358,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 354 | static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, | 358 | static int hist_entry__cpu_snprintf(struct hist_entry *he, char *bf, |
| 355 | size_t size, unsigned int width) | 359 | size_t size, unsigned int width) |
| 356 | { | 360 | { |
| 357 | return repsep_snprintf(bf, size, "%*d", width, he->cpu); | 361 | return repsep_snprintf(bf, size, "%*.*d", width, width, he->cpu); |
| 358 | } | 362 | } |
| 359 | 363 | ||
| 360 | struct sort_entry sort_cpu = { | 364 | struct sort_entry sort_cpu = { |
| @@ -484,7 +488,7 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, | |||
| 484 | else if (he->branch_info->flags.mispred) | 488 | else if (he->branch_info->flags.mispred) |
| 485 | out = "Y"; | 489 | out = "Y"; |
| 486 | 490 | ||
| 487 | return repsep_snprintf(bf, size, "%-*s", width, out); | 491 | return repsep_snprintf(bf, size, "%-*.*s", width, width, out); |
| 488 | } | 492 | } |
| 489 | 493 | ||
| 490 | /* --sort daddr_sym */ | 494 | /* --sort daddr_sym */ |
| @@ -1194,7 +1198,7 @@ bool perf_hpp__same_sort_entry(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) | |||
| 1194 | return hse_a->se == hse_b->se; | 1198 | return hse_a->se == hse_b->se; |
| 1195 | } | 1199 | } |
| 1196 | 1200 | ||
| 1197 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | 1201 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) |
| 1198 | { | 1202 | { |
| 1199 | struct hpp_sort_entry *hse; | 1203 | struct hpp_sort_entry *hse; |
| 1200 | 1204 | ||
| @@ -1202,20 +1206,21 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | |||
| 1202 | return; | 1206 | return; |
| 1203 | 1207 | ||
| 1204 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1208 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
| 1205 | hists__new_col_len(hists, hse->se->se_width_idx, | 1209 | hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name)); |
| 1206 | strlen(hse->se->se_header)); | ||
| 1207 | } | 1210 | } |
| 1208 | 1211 | ||
| 1209 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 1212 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 1210 | struct perf_evsel *evsel) | 1213 | struct perf_evsel *evsel) |
| 1211 | { | 1214 | { |
| 1212 | struct hpp_sort_entry *hse; | 1215 | struct hpp_sort_entry *hse; |
| 1213 | size_t len; | 1216 | size_t len = fmt->user_len; |
| 1214 | 1217 | ||
| 1215 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1218 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
| 1216 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
| 1217 | 1219 | ||
| 1218 | return scnprintf(hpp->buf, hpp->size, "%-*s", len, hse->se->se_header); | 1220 | if (!len) |
| 1221 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
| 1222 | |||
| 1223 | return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name); | ||
| 1219 | } | 1224 | } |
| 1220 | 1225 | ||
| 1221 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | 1226 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, |
| @@ -1223,20 +1228,26 @@ static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | |||
| 1223 | struct perf_evsel *evsel) | 1228 | struct perf_evsel *evsel) |
| 1224 | { | 1229 | { |
| 1225 | struct hpp_sort_entry *hse; | 1230 | struct hpp_sort_entry *hse; |
| 1231 | size_t len = fmt->user_len; | ||
| 1226 | 1232 | ||
| 1227 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1233 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
| 1228 | 1234 | ||
| 1229 | return hists__col_len(&evsel->hists, hse->se->se_width_idx); | 1235 | if (!len) |
| 1236 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
| 1237 | |||
| 1238 | return len; | ||
| 1230 | } | 1239 | } |
| 1231 | 1240 | ||
| 1232 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 1241 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 1233 | struct hist_entry *he) | 1242 | struct hist_entry *he) |
| 1234 | { | 1243 | { |
| 1235 | struct hpp_sort_entry *hse; | 1244 | struct hpp_sort_entry *hse; |
| 1236 | size_t len; | 1245 | size_t len = fmt->user_len; |
| 1237 | 1246 | ||
| 1238 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1247 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
| 1239 | len = hists__col_len(he->hists, hse->se->se_width_idx); | 1248 | |
| 1249 | if (!len) | ||
| 1250 | len = hists__col_len(he->hists, hse->se->se_width_idx); | ||
| 1240 | 1251 | ||
| 1241 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); | 1252 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); |
| 1242 | } | 1253 | } |
| @@ -1253,6 +1264,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
| 1253 | } | 1264 | } |
| 1254 | 1265 | ||
| 1255 | hse->se = sd->entry; | 1266 | hse->se = sd->entry; |
| 1267 | hse->hpp.name = sd->entry->se_header; | ||
| 1256 | hse->hpp.header = __sort__hpp_header; | 1268 | hse->hpp.header = __sort__hpp_header; |
| 1257 | hse->hpp.width = __sort__hpp_width; | 1269 | hse->hpp.width = __sort__hpp_width; |
| 1258 | hse->hpp.entry = __sort__hpp_entry; | 1270 | hse->hpp.entry = __sort__hpp_entry; |
| @@ -1265,6 +1277,8 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
| 1265 | INIT_LIST_HEAD(&hse->hpp.list); | 1277 | INIT_LIST_HEAD(&hse->hpp.list); |
| 1266 | INIT_LIST_HEAD(&hse->hpp.sort_list); | 1278 | INIT_LIST_HEAD(&hse->hpp.sort_list); |
| 1267 | hse->hpp.elide = false; | 1279 | hse->hpp.elide = false; |
| 1280 | hse->hpp.len = 0; | ||
| 1281 | hse->hpp.user_len = 0; | ||
| 1268 | 1282 | ||
| 1269 | return hse; | 1283 | return hse; |
| 1270 | } | 1284 | } |
| @@ -1432,14 +1446,49 @@ static const char *get_default_sort_order(void) | |||
| 1432 | return default_sort_orders[sort__mode]; | 1446 | return default_sort_orders[sort__mode]; |
| 1433 | } | 1447 | } |
| 1434 | 1448 | ||
| 1449 | static int setup_sort_order(void) | ||
| 1450 | { | ||
| 1451 | char *new_sort_order; | ||
| 1452 | |||
| 1453 | /* | ||
| 1454 | * Append '+'-prefixed sort order to the default sort | ||
| 1455 | * order string. | ||
| 1456 | */ | ||
| 1457 | if (!sort_order || is_strict_order(sort_order)) | ||
| 1458 | return 0; | ||
| 1459 | |||
| 1460 | if (sort_order[1] == '\0') { | ||
| 1461 | error("Invalid --sort key: `+'"); | ||
| 1462 | return -EINVAL; | ||
| 1463 | } | ||
| 1464 | |||
| 1465 | /* | ||
| 1466 | * We allocate new sort_order string, but we never free it, | ||
| 1467 | * because it's checked over the rest of the code. | ||
| 1468 | */ | ||
| 1469 | if (asprintf(&new_sort_order, "%s,%s", | ||
| 1470 | get_default_sort_order(), sort_order + 1) < 0) { | ||
| 1471 | error("Not enough memory to set up --sort"); | ||
| 1472 | return -ENOMEM; | ||
| 1473 | } | ||
| 1474 | |||
| 1475 | sort_order = new_sort_order; | ||
| 1476 | return 0; | ||
| 1477 | } | ||
| 1478 | |||
| 1435 | static int __setup_sorting(void) | 1479 | static int __setup_sorting(void) |
| 1436 | { | 1480 | { |
| 1437 | char *tmp, *tok, *str; | 1481 | char *tmp, *tok, *str; |
| 1438 | const char *sort_keys = sort_order; | 1482 | const char *sort_keys; |
| 1439 | int ret = 0; | 1483 | int ret = 0; |
| 1440 | 1484 | ||
| 1485 | ret = setup_sort_order(); | ||
| 1486 | if (ret) | ||
| 1487 | return ret; | ||
| 1488 | |||
| 1489 | sort_keys = sort_order; | ||
| 1441 | if (sort_keys == NULL) { | 1490 | if (sort_keys == NULL) { |
| 1442 | if (field_order) { | 1491 | if (is_strict_order(field_order)) { |
| 1443 | /* | 1492 | /* |
| 1444 | * If user specified field order but no sort order, | 1493 | * If user specified field order but no sort order, |
| 1445 | * we'll honor it and not add default sort orders. | 1494 | * we'll honor it and not add default sort orders. |
| @@ -1625,23 +1674,36 @@ static void reset_dimensions(void) | |||
| 1625 | memory_sort_dimensions[i].taken = 0; | 1674 | memory_sort_dimensions[i].taken = 0; |
| 1626 | } | 1675 | } |
| 1627 | 1676 | ||
| 1677 | bool is_strict_order(const char *order) | ||
| 1678 | { | ||
| 1679 | return order && (*order != '+'); | ||
| 1680 | } | ||
| 1681 | |||
| 1628 | static int __setup_output_field(void) | 1682 | static int __setup_output_field(void) |
| 1629 | { | 1683 | { |
| 1630 | char *tmp, *tok, *str; | 1684 | char *tmp, *tok, *str, *strp; |
| 1631 | int ret = 0; | 1685 | int ret = -EINVAL; |
| 1632 | 1686 | ||
| 1633 | if (field_order == NULL) | 1687 | if (field_order == NULL) |
| 1634 | return 0; | 1688 | return 0; |
| 1635 | 1689 | ||
| 1636 | reset_dimensions(); | 1690 | reset_dimensions(); |
| 1637 | 1691 | ||
| 1638 | str = strdup(field_order); | 1692 | strp = str = strdup(field_order); |
| 1639 | if (str == NULL) { | 1693 | if (str == NULL) { |
| 1640 | error("Not enough memory to setup output fields"); | 1694 | error("Not enough memory to setup output fields"); |
| 1641 | return -ENOMEM; | 1695 | return -ENOMEM; |
| 1642 | } | 1696 | } |
| 1643 | 1697 | ||
| 1644 | for (tok = strtok_r(str, ", ", &tmp); | 1698 | if (!is_strict_order(field_order)) |
| 1699 | strp++; | ||
| 1700 | |||
| 1701 | if (!strlen(strp)) { | ||
| 1702 | error("Invalid --fields key: `+'"); | ||
| 1703 | goto out; | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | for (tok = strtok_r(strp, ", ", &tmp); | ||
| 1645 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 1707 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
| 1646 | ret = output_field_add(tok); | 1708 | ret = output_field_add(tok); |
| 1647 | if (ret == -EINVAL) { | 1709 | if (ret == -EINVAL) { |
| @@ -1653,6 +1715,7 @@ static int __setup_output_field(void) | |||
| 1653 | } | 1715 | } |
| 1654 | } | 1716 | } |
| 1655 | 1717 | ||
| 1718 | out: | ||
| 1656 | free(str); | 1719 | free(str); |
| 1657 | return ret; | 1720 | return ret; |
| 1658 | } | 1721 | } |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 041f0c9cea2b..c03e4ff8beff 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
| @@ -218,4 +218,5 @@ void perf_hpp__set_elide(int idx, bool elide); | |||
| 218 | 218 | ||
| 219 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); | 219 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); |
| 220 | 220 | ||
| 221 | bool is_strict_order(const char *order); | ||
| 221 | #endif /* __PERF_SORT_H */ | 222 | #endif /* __PERF_SORT_H */ |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 2553e5b55b89..d87767f76903 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
| @@ -9,78 +9,48 @@ | |||
| 9 | */ | 9 | */ |
| 10 | s64 perf_atoll(const char *str) | 10 | s64 perf_atoll(const char *str) |
| 11 | { | 11 | { |
| 12 | unsigned int i; | 12 | s64 length; |
| 13 | s64 length = -1, unit = 1; | 13 | char *p; |
| 14 | char c; | ||
| 14 | 15 | ||
| 15 | if (!isdigit(str[0])) | 16 | if (!isdigit(str[0])) |
| 16 | goto out_err; | 17 | goto out_err; |
| 17 | 18 | ||
| 18 | for (i = 1; i < strlen(str); i++) { | 19 | length = strtoll(str, &p, 10); |
| 19 | switch (str[i]) { | 20 | switch (c = *p++) { |
| 20 | case 'B': | 21 | case 'b': case 'B': |
| 21 | case 'b': | 22 | if (*p) |
| 22 | break; | ||
| 23 | case 'K': | ||
| 24 | if (str[i + 1] != 'B') | ||
| 25 | goto out_err; | ||
| 26 | else | ||
| 27 | goto kilo; | ||
| 28 | case 'k': | ||
| 29 | if (str[i + 1] != 'b') | ||
| 30 | goto out_err; | ||
| 31 | kilo: | ||
| 32 | unit = K; | ||
| 33 | break; | ||
| 34 | case 'M': | ||
| 35 | if (str[i + 1] != 'B') | ||
| 36 | goto out_err; | ||
| 37 | else | ||
| 38 | goto mega; | ||
| 39 | case 'm': | ||
| 40 | if (str[i + 1] != 'b') | ||
| 41 | goto out_err; | 23 | goto out_err; |
| 42 | mega: | 24 | case '\0': |
| 43 | unit = K * K; | 25 | return length; |
| 26 | default: | ||
| 27 | goto out_err; | ||
| 28 | /* two-letter suffices */ | ||
| 29 | case 'k': case 'K': | ||
| 30 | length <<= 10; | ||
| 44 | break; | 31 | break; |
| 45 | case 'G': | 32 | case 'm': case 'M': |
| 46 | if (str[i + 1] != 'B') | 33 | length <<= 20; |
| 47 | goto out_err; | ||
| 48 | else | ||
| 49 | goto giga; | ||
| 50 | case 'g': | ||
| 51 | if (str[i + 1] != 'b') | ||
| 52 | goto out_err; | ||
| 53 | giga: | ||
| 54 | unit = K * K * K; | ||
| 55 | break; | 34 | break; |
| 56 | case 'T': | 35 | case 'g': case 'G': |
| 57 | if (str[i + 1] != 'B') | 36 | length <<= 30; |
| 58 | goto out_err; | ||
| 59 | else | ||
| 60 | goto tera; | ||
| 61 | case 't': | ||
| 62 | if (str[i + 1] != 'b') | ||
| 63 | goto out_err; | ||
| 64 | tera: | ||
| 65 | unit = K * K * K * K; | ||
| 66 | break; | 37 | break; |
| 67 | case '\0': /* only specified figures */ | 38 | case 't': case 'T': |
| 68 | unit = 1; | 39 | length <<= 40; |
| 69 | break; | 40 | break; |
| 70 | default: | ||
| 71 | if (!isdigit(str[i])) | ||
| 72 | goto out_err; | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | } | 41 | } |
| 76 | 42 | /* we want the cases to match */ | |
| 77 | length = atoll(str) * unit; | 43 | if (islower(c)) { |
| 78 | goto out; | 44 | if (strcmp(p, "b") != 0) |
| 45 | goto out_err; | ||
| 46 | } else { | ||
| 47 | if (strcmp(p, "B") != 0) | ||
| 48 | goto out_err; | ||
| 49 | } | ||
| 50 | return length; | ||
| 79 | 51 | ||
| 80 | out_err: | 52 | out_err: |
| 81 | length = -1; | 53 | return -1; |
| 82 | out: | ||
| 83 | return length; | ||
| 84 | } | 54 | } |
| 85 | 55 | ||
| 86 | /* | 56 | /* |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index d75349979e65..1e23a5bfb044 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <inttypes.h> | 6 | #include <inttypes.h> |
| 7 | 7 | ||
| 8 | #include "symbol.h" | 8 | #include "symbol.h" |
| 9 | #include "machine.h" | ||
| 9 | #include "vdso.h" | 10 | #include "vdso.h" |
| 10 | #include <symbol/kallsyms.h> | 11 | #include <symbol/kallsyms.h> |
| 11 | #include "debug.h" | 12 | #include "debug.h" |
| @@ -680,6 +681,11 @@ static u64 ref_reloc(struct kmap *kmap) | |||
| 680 | return 0; | 681 | return 0; |
| 681 | } | 682 | } |
| 682 | 683 | ||
| 684 | static bool want_demangle(bool is_kernel_sym) | ||
| 685 | { | ||
| 686 | return is_kernel_sym ? symbol_conf.demangle_kernel : symbol_conf.demangle; | ||
| 687 | } | ||
| 688 | |||
| 683 | int dso__load_sym(struct dso *dso, struct map *map, | 689 | int dso__load_sym(struct dso *dso, struct map *map, |
| 684 | struct symsrc *syms_ss, struct symsrc *runtime_ss, | 690 | struct symsrc *syms_ss, struct symsrc *runtime_ss, |
| 685 | symbol_filter_t filter, int kmodule) | 691 | symbol_filter_t filter, int kmodule) |
| @@ -712,6 +718,14 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
| 712 | symbols__delete(&dso->symbols[map->type]); | 718 | symbols__delete(&dso->symbols[map->type]); |
| 713 | 719 | ||
| 714 | if (!syms_ss->symtab) { | 720 | if (!syms_ss->symtab) { |
| 721 | /* | ||
| 722 | * If the vmlinux is stripped, fail so we will fall back | ||
| 723 | * to using kallsyms. The vmlinux runtime symbols aren't | ||
| 724 | * of much use. | ||
| 725 | */ | ||
| 726 | if (dso->kernel) | ||
| 727 | goto out_elf_end; | ||
| 728 | |||
| 715 | syms_ss->symtab = syms_ss->dynsym; | 729 | syms_ss->symtab = syms_ss->dynsym; |
| 716 | syms_ss->symshdr = syms_ss->dynshdr; | 730 | syms_ss->symshdr = syms_ss->dynshdr; |
| 717 | } | 731 | } |
| @@ -736,7 +750,7 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
| 736 | if (symstrs == NULL) | 750 | if (symstrs == NULL) |
| 737 | goto out_elf_end; | 751 | goto out_elf_end; |
| 738 | 752 | ||
| 739 | sec_strndx = elf_getscn(elf, ehdr.e_shstrndx); | 753 | sec_strndx = elf_getscn(runtime_ss->elf, runtime_ss->ehdr.e_shstrndx); |
| 740 | if (sec_strndx == NULL) | 754 | if (sec_strndx == NULL) |
| 741 | goto out_elf_end; | 755 | goto out_elf_end; |
| 742 | 756 | ||
| @@ -916,7 +930,11 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
| 916 | } | 930 | } |
| 917 | curr_dso->symtab_type = dso->symtab_type; | 931 | curr_dso->symtab_type = dso->symtab_type; |
| 918 | map_groups__insert(kmap->kmaps, curr_map); | 932 | map_groups__insert(kmap->kmaps, curr_map); |
| 919 | dsos__add(&dso->node, curr_dso); | 933 | /* |
| 934 | * The new DSO should go to the kernel DSOS | ||
| 935 | */ | ||
| 936 | dsos__add(&map->groups->machine->kernel_dsos, | ||
| 937 | curr_dso); | ||
| 920 | dso__set_loaded(curr_dso, map->type); | 938 | dso__set_loaded(curr_dso, map->type); |
| 921 | } else | 939 | } else |
| 922 | curr_dso = curr_map->dso; | 940 | curr_dso = curr_map->dso; |
| @@ -938,9 +956,12 @@ new_symbol: | |||
| 938 | * DWARF DW_compile_unit has this, but we don't always have access | 956 | * DWARF DW_compile_unit has this, but we don't always have access |
| 939 | * to it... | 957 | * to it... |
| 940 | */ | 958 | */ |
| 941 | if (symbol_conf.demangle) { | 959 | if (want_demangle(dso->kernel || kmodule)) { |
| 942 | demangled = bfd_demangle(NULL, elf_name, | 960 | int demangle_flags = DMGL_NO_OPTS; |
| 943 | DMGL_PARAMS | DMGL_ANSI); | 961 | if (verbose) |
| 962 | demangle_flags = DMGL_PARAMS | DMGL_ANSI; | ||
| 963 | |||
| 964 | demangled = bfd_demangle(NULL, elf_name, demangle_flags); | ||
| 944 | if (demangled != NULL) | 965 | if (demangled != NULL) |
| 945 | elf_name = demangled; | 966 | elf_name = demangled; |
| 946 | } | 967 | } |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index eb06746b06b2..be84f7a9838b 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "machine.h" | 15 | #include "machine.h" |
| 16 | #include "symbol.h" | 16 | #include "symbol.h" |
| 17 | #include "strlist.h" | 17 | #include "strlist.h" |
| 18 | #include "header.h" | ||
| 18 | 19 | ||
| 19 | #include <elf.h> | 20 | #include <elf.h> |
| 20 | #include <limits.h> | 21 | #include <limits.h> |
| @@ -33,6 +34,7 @@ struct symbol_conf symbol_conf = { | |||
| 33 | .try_vmlinux_path = true, | 34 | .try_vmlinux_path = true, |
| 34 | .annotate_src = true, | 35 | .annotate_src = true, |
| 35 | .demangle = true, | 36 | .demangle = true, |
| 37 | .demangle_kernel = false, | ||
| 36 | .cumulate_callchain = true, | 38 | .cumulate_callchain = true, |
| 37 | .show_hist_headers = true, | 39 | .show_hist_headers = true, |
| 38 | .symfs = "", | 40 | .symfs = "", |
| @@ -523,10 +525,15 @@ struct process_kallsyms_args { | |||
| 523 | struct dso *dso; | 525 | struct dso *dso; |
| 524 | }; | 526 | }; |
| 525 | 527 | ||
| 528 | /* | ||
| 529 | * These are symbols in the kernel image, so make sure that | ||
| 530 | * sym is from a kernel DSO. | ||
| 531 | */ | ||
| 526 | bool symbol__is_idle(struct symbol *sym) | 532 | bool symbol__is_idle(struct symbol *sym) |
| 527 | { | 533 | { |
| 528 | const char * const idle_symbols[] = { | 534 | const char * const idle_symbols[] = { |
| 529 | "cpu_idle", | 535 | "cpu_idle", |
| 536 | "cpu_startup_entry", | ||
| 530 | "intel_idle", | 537 | "intel_idle", |
| 531 | "default_idle", | 538 | "default_idle", |
| 532 | "native_safe_halt", | 539 | "native_safe_halt", |
| @@ -1468,8 +1475,7 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, | |||
| 1468 | if (vmlinux[0] == '/') | 1475 | if (vmlinux[0] == '/') |
| 1469 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); | 1476 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); |
| 1470 | else | 1477 | else |
| 1471 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | 1478 | symbol__join_symfs(symfs_vmlinux, vmlinux); |
| 1472 | symbol_conf.symfs, vmlinux); | ||
| 1473 | 1479 | ||
| 1474 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1480 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
| 1475 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | 1481 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; |
| @@ -1745,12 +1751,13 @@ static void vmlinux_path__exit(void) | |||
| 1745 | zfree(&vmlinux_path); | 1751 | zfree(&vmlinux_path); |
| 1746 | } | 1752 | } |
| 1747 | 1753 | ||
| 1748 | static int vmlinux_path__init(void) | 1754 | static int vmlinux_path__init(struct perf_session_env *env) |
| 1749 | { | 1755 | { |
| 1750 | struct utsname uts; | 1756 | struct utsname uts; |
| 1751 | char bf[PATH_MAX]; | 1757 | char bf[PATH_MAX]; |
| 1758 | char *kernel_version; | ||
| 1752 | 1759 | ||
| 1753 | vmlinux_path = malloc(sizeof(char *) * 5); | 1760 | vmlinux_path = malloc(sizeof(char *) * 6); |
| 1754 | if (vmlinux_path == NULL) | 1761 | if (vmlinux_path == NULL) |
| 1755 | return -1; | 1762 | return -1; |
| 1756 | 1763 | ||
| @@ -1763,25 +1770,37 @@ static int vmlinux_path__init(void) | |||
| 1763 | goto out_fail; | 1770 | goto out_fail; |
| 1764 | ++vmlinux_path__nr_entries; | 1771 | ++vmlinux_path__nr_entries; |
| 1765 | 1772 | ||
| 1766 | /* only try running kernel version if no symfs was given */ | 1773 | /* only try kernel version if no symfs was given */ |
| 1767 | if (symbol_conf.symfs[0] != 0) | 1774 | if (symbol_conf.symfs[0] != 0) |
| 1768 | return 0; | 1775 | return 0; |
| 1769 | 1776 | ||
| 1770 | if (uname(&uts) < 0) | 1777 | if (env) { |
| 1771 | return -1; | 1778 | kernel_version = env->os_release; |
| 1779 | } else { | ||
| 1780 | if (uname(&uts) < 0) | ||
| 1781 | goto out_fail; | ||
| 1772 | 1782 | ||
| 1773 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", uts.release); | 1783 | kernel_version = uts.release; |
| 1784 | } | ||
| 1785 | |||
| 1786 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", kernel_version); | ||
| 1774 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1787 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
| 1775 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1788 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
| 1776 | goto out_fail; | 1789 | goto out_fail; |
| 1777 | ++vmlinux_path__nr_entries; | 1790 | ++vmlinux_path__nr_entries; |
| 1778 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", uts.release); | 1791 | snprintf(bf, sizeof(bf), "/usr/lib/debug/boot/vmlinux-%s", |
| 1792 | kernel_version); | ||
| 1793 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | ||
| 1794 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | ||
| 1795 | goto out_fail; | ||
| 1796 | ++vmlinux_path__nr_entries; | ||
| 1797 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", kernel_version); | ||
| 1779 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1798 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
| 1780 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1799 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
| 1781 | goto out_fail; | 1800 | goto out_fail; |
| 1782 | ++vmlinux_path__nr_entries; | 1801 | ++vmlinux_path__nr_entries; |
| 1783 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", | 1802 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", |
| 1784 | uts.release); | 1803 | kernel_version); |
| 1785 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1804 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
| 1786 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1805 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
| 1787 | goto out_fail; | 1806 | goto out_fail; |
| @@ -1827,7 +1846,7 @@ static bool symbol__read_kptr_restrict(void) | |||
| 1827 | return value; | 1846 | return value; |
| 1828 | } | 1847 | } |
| 1829 | 1848 | ||
| 1830 | int symbol__init(void) | 1849 | int symbol__init(struct perf_session_env *env) |
| 1831 | { | 1850 | { |
| 1832 | const char *symfs; | 1851 | const char *symfs; |
| 1833 | 1852 | ||
| @@ -1842,7 +1861,7 @@ int symbol__init(void) | |||
| 1842 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | 1861 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
| 1843 | sizeof(struct symbol)); | 1862 | sizeof(struct symbol)); |
| 1844 | 1863 | ||
| 1845 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init() < 0) | 1864 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0) |
| 1846 | return -1; | 1865 | return -1; |
| 1847 | 1866 | ||
| 1848 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { | 1867 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index e7295e93cff9..bec4b7bd09de 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <libgen.h> | 13 | #include <libgen.h> |
| 14 | #include "build-id.h" | 14 | #include "build-id.h" |
| 15 | #include "event.h" | 15 | #include "event.h" |
| 16 | #include "util.h" | ||
| 16 | 17 | ||
| 17 | #ifdef HAVE_LIBELF_SUPPORT | 18 | #ifdef HAVE_LIBELF_SUPPORT |
| 18 | #include <libelf.h> | 19 | #include <libelf.h> |
| @@ -59,6 +60,7 @@ extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
| 59 | #endif | 60 | #endif |
| 60 | 61 | ||
| 61 | #ifndef DMGL_PARAMS | 62 | #ifndef DMGL_PARAMS |
| 63 | #define DMGL_NO_OPTS 0 /* For readability... */ | ||
| 62 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | 64 | #define DMGL_PARAMS (1 << 0) /* Include function args */ |
| 63 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 65 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
| 64 | #endif | 66 | #endif |
| @@ -118,6 +120,7 @@ struct symbol_conf { | |||
| 118 | annotate_src, | 120 | annotate_src, |
| 119 | event_group, | 121 | event_group, |
| 120 | demangle, | 122 | demangle, |
| 123 | demangle_kernel, | ||
| 121 | filter_relative, | 124 | filter_relative, |
| 122 | show_hist_headers; | 125 | show_hist_headers; |
| 123 | const char *vmlinux_name, | 126 | const char *vmlinux_name, |
| @@ -143,6 +146,14 @@ struct symbol_conf { | |||
| 143 | }; | 146 | }; |
| 144 | 147 | ||
| 145 | extern struct symbol_conf symbol_conf; | 148 | extern struct symbol_conf symbol_conf; |
| 149 | |||
| 150 | static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) | ||
| 151 | { | ||
| 152 | return path__join(bf, size, symbol_conf.symfs, path); | ||
| 153 | } | ||
| 154 | |||
| 155 | #define symbol__join_symfs(bf, path) __symbol__join_symfs(bf, sizeof(bf), path) | ||
| 156 | |||
| 146 | extern int vmlinux_path__nr_entries; | 157 | extern int vmlinux_path__nr_entries; |
| 147 | extern char **vmlinux_path; | 158 | extern char **vmlinux_path; |
| 148 | 159 | ||
| @@ -253,7 +264,8 @@ int modules__parse(const char *filename, void *arg, | |||
| 253 | int filename__read_debuglink(const char *filename, char *debuglink, | 264 | int filename__read_debuglink(const char *filename, char *debuglink, |
| 254 | size_t size); | 265 | size_t size); |
| 255 | 266 | ||
| 256 | int symbol__init(void); | 267 | struct perf_session_env; |
| 268 | int symbol__init(struct perf_session_env *env); | ||
| 257 | void symbol__exit(void); | 269 | void symbol__exit(void); |
| 258 | void symbol__elf_init(void); | 270 | void symbol__elf_init(void); |
| 259 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); | 271 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 12c7a253a63c..a9df7f2c6dc9 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -42,7 +42,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) | |||
| 42 | goto err_thread; | 42 | goto err_thread; |
| 43 | 43 | ||
| 44 | snprintf(comm_str, 32, ":%d", tid); | 44 | snprintf(comm_str, 32, ":%d", tid); |
| 45 | comm = comm__new(comm_str, 0); | 45 | comm = comm__new(comm_str, 0, false); |
| 46 | free(comm_str); | 46 | free(comm_str); |
| 47 | if (!comm) | 47 | if (!comm) |
| 48 | goto err_thread; | 48 | goto err_thread; |
| @@ -81,19 +81,33 @@ struct comm *thread__comm(const struct thread *thread) | |||
| 81 | return list_first_entry(&thread->comm_list, struct comm, list); | 81 | return list_first_entry(&thread->comm_list, struct comm, list); |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | struct comm *thread__exec_comm(const struct thread *thread) | ||
| 85 | { | ||
| 86 | struct comm *comm, *last = NULL; | ||
| 87 | |||
| 88 | list_for_each_entry(comm, &thread->comm_list, list) { | ||
| 89 | if (comm->exec) | ||
| 90 | return comm; | ||
| 91 | last = comm; | ||
| 92 | } | ||
| 93 | |||
| 94 | return last; | ||
| 95 | } | ||
| 96 | |||
| 84 | /* CHECKME: time should always be 0 if event aren't ordered */ | 97 | /* CHECKME: time should always be 0 if event aren't ordered */ |
| 85 | int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) | 98 | int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, |
| 99 | bool exec) | ||
| 86 | { | 100 | { |
| 87 | struct comm *new, *curr = thread__comm(thread); | 101 | struct comm *new, *curr = thread__comm(thread); |
| 88 | int err; | 102 | int err; |
| 89 | 103 | ||
| 90 | /* Override latest entry if it had no specific time coverage */ | 104 | /* Override latest entry if it had no specific time coverage */ |
| 91 | if (!curr->start) { | 105 | if (!curr->start && !curr->exec) { |
| 92 | err = comm__override(curr, str, timestamp); | 106 | err = comm__override(curr, str, timestamp, exec); |
| 93 | if (err) | 107 | if (err) |
| 94 | return err; | 108 | return err; |
| 95 | } else { | 109 | } else { |
| 96 | new = comm__new(str, timestamp); | 110 | new = comm__new(str, timestamp, exec); |
| 97 | if (!new) | 111 | if (!new) |
| 98 | return -ENOMEM; | 112 | return -ENOMEM; |
| 99 | list_add(&new->list, &thread->comm_list); | 113 | list_add(&new->list, &thread->comm_list); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 716b7723cce2..8c75fa774706 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -38,9 +38,17 @@ static inline void thread__exited(struct thread *thread) | |||
| 38 | thread->dead = true; | 38 | thread->dead = true; |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | int thread__set_comm(struct thread *thread, const char *comm, u64 timestamp); | 41 | int __thread__set_comm(struct thread *thread, const char *comm, u64 timestamp, |
| 42 | bool exec); | ||
| 43 | static inline int thread__set_comm(struct thread *thread, const char *comm, | ||
| 44 | u64 timestamp) | ||
| 45 | { | ||
| 46 | return __thread__set_comm(thread, comm, timestamp, false); | ||
| 47 | } | ||
| 48 | |||
| 42 | int thread__comm_len(struct thread *thread); | 49 | int thread__comm_len(struct thread *thread); |
| 43 | struct comm *thread__comm(const struct thread *thread); | 50 | struct comm *thread__comm(const struct thread *thread); |
| 51 | struct comm *thread__exec_comm(const struct thread *thread); | ||
| 44 | const char *thread__comm_str(const struct thread *thread); | 52 | const char *thread__comm_str(const struct thread *thread); |
| 45 | void thread__insert_map(struct thread *thread, struct map *map); | 53 | void thread__insert_map(struct thread *thread, struct map *map); |
| 46 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); | 54 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); |
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 4385816d3d49..f11636966a0f 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
| @@ -40,7 +40,7 @@ struct perf_tool { | |||
| 40 | event_op2 tracing_data; | 40 | event_op2 tracing_data; |
| 41 | event_op2 finished_round, | 41 | event_op2 finished_round, |
| 42 | build_id; | 42 | build_id; |
| 43 | bool ordered_samples; | 43 | bool ordered_events; |
| 44 | bool ordering_requires_timestamps; | 44 | bool ordering_requires_timestamps; |
| 45 | }; | 45 | }; |
| 46 | 46 | ||
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 57aaccc1692e..5c9bdd1591a9 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
| @@ -30,6 +30,11 @@ | |||
| 30 | 30 | ||
| 31 | struct scripting_context *scripting_context; | 31 | struct scripting_context *scripting_context; |
| 32 | 32 | ||
| 33 | static int flush_script_unsupported(void) | ||
| 34 | { | ||
| 35 | return 0; | ||
| 36 | } | ||
| 37 | |||
| 33 | static int stop_script_unsupported(void) | 38 | static int stop_script_unsupported(void) |
| 34 | { | 39 | { |
| 35 | return 0; | 40 | return 0; |
| @@ -74,6 +79,7 @@ static int python_generate_script_unsupported(struct pevent *pevent | |||
| 74 | struct scripting_ops python_scripting_unsupported_ops = { | 79 | struct scripting_ops python_scripting_unsupported_ops = { |
| 75 | .name = "Python", | 80 | .name = "Python", |
| 76 | .start_script = python_start_script_unsupported, | 81 | .start_script = python_start_script_unsupported, |
| 82 | .flush_script = flush_script_unsupported, | ||
| 77 | .stop_script = stop_script_unsupported, | 83 | .stop_script = stop_script_unsupported, |
| 78 | .process_event = process_event_unsupported, | 84 | .process_event = process_event_unsupported, |
| 79 | .generate_script = python_generate_script_unsupported, | 85 | .generate_script = python_generate_script_unsupported, |
| @@ -137,6 +143,7 @@ static int perl_generate_script_unsupported(struct pevent *pevent | |||
| 137 | struct scripting_ops perl_scripting_unsupported_ops = { | 143 | struct scripting_ops perl_scripting_unsupported_ops = { |
| 138 | .name = "Perl", | 144 | .name = "Perl", |
| 139 | .start_script = perl_start_script_unsupported, | 145 | .start_script = perl_start_script_unsupported, |
| 146 | .flush_script = flush_script_unsupported, | ||
| 140 | .stop_script = stop_script_unsupported, | 147 | .stop_script = stop_script_unsupported, |
| 141 | .process_event = process_event_unsupported, | 148 | .process_event = process_event_unsupported, |
| 142 | .generate_script = perl_generate_script_unsupported, | 149 | .generate_script = perl_generate_script_unsupported, |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 7b6d68688327..52aaa19e1eb1 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
| @@ -64,6 +64,7 @@ struct perf_session; | |||
| 64 | struct scripting_ops { | 64 | struct scripting_ops { |
| 65 | const char *name; | 65 | const char *name; |
| 66 | int (*start_script) (const char *script, int argc, const char **argv); | 66 | int (*start_script) (const char *script, int argc, const char **argv); |
| 67 | int (*flush_script) (void); | ||
| 67 | int (*stop_script) (void); | 68 | int (*stop_script) (void); |
| 68 | void (*process_event) (union perf_event *event, | 69 | void (*process_event) (union perf_event *event, |
| 69 | struct perf_sample *sample, | 70 | struct perf_sample *sample, |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index e52e7461911b..24e8d871b74e 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <limits.h> | 13 | #include <limits.h> |
| 14 | #include <byteswap.h> | 14 | #include <byteswap.h> |
| 15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
| 16 | #include <unistd.h> | ||
| 16 | 17 | ||
| 17 | /* | 18 | /* |
| 18 | * XXX We need to find a better place for these things... | 19 | * XXX We need to find a better place for these things... |
| @@ -282,6 +283,18 @@ void get_term_dimensions(struct winsize *ws) | |||
| 282 | ws->ws_col = 80; | 283 | ws->ws_col = 80; |
| 283 | } | 284 | } |
| 284 | 285 | ||
| 286 | void set_term_quiet_input(struct termios *old) | ||
| 287 | { | ||
| 288 | struct termios tc; | ||
| 289 | |||
| 290 | tcgetattr(0, old); | ||
| 291 | tc = *old; | ||
| 292 | tc.c_lflag &= ~(ICANON | ECHO); | ||
| 293 | tc.c_cc[VMIN] = 0; | ||
| 294 | tc.c_cc[VTIME] = 0; | ||
| 295 | tcsetattr(0, TCSANOW, &tc); | ||
| 296 | } | ||
| 297 | |||
| 285 | static void set_tracing_events_path(const char *mountpoint) | 298 | static void set_tracing_events_path(const char *mountpoint) |
| 286 | { | 299 | { |
| 287 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | 300 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", |
| @@ -443,6 +456,7 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep) | |||
| 443 | size_t size = 0, alloc_size = 0; | 456 | size_t size = 0, alloc_size = 0; |
| 444 | void *bf = NULL, *nbf; | 457 | void *bf = NULL, *nbf; |
| 445 | int fd, n, err = 0; | 458 | int fd, n, err = 0; |
| 459 | char sbuf[STRERR_BUFSIZE]; | ||
| 446 | 460 | ||
| 447 | fd = open(filename, O_RDONLY); | 461 | fd = open(filename, O_RDONLY); |
| 448 | if (fd < 0) | 462 | if (fd < 0) |
| @@ -463,8 +477,8 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep) | |||
| 463 | n = read(fd, bf + size, alloc_size - size); | 477 | n = read(fd, bf + size, alloc_size - size); |
| 464 | if (n < 0) { | 478 | if (n < 0) { |
| 465 | if (size) { | 479 | if (size) { |
| 466 | pr_warning("read failed %d: %s\n", | 480 | pr_warning("read failed %d: %s\n", errno, |
| 467 | errno, strerror(errno)); | 481 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 468 | err = 0; | 482 | err = 0; |
| 469 | } else | 483 | } else |
| 470 | err = -errno; | 484 | err = -errno; |
| @@ -536,3 +550,39 @@ void mem_bswap_64(void *src, int byte_size) | |||
| 536 | ++m; | 550 | ++m; |
| 537 | } | 551 | } |
| 538 | } | 552 | } |
| 553 | |||
| 554 | bool find_process(const char *name) | ||
| 555 | { | ||
| 556 | size_t len = strlen(name); | ||
| 557 | DIR *dir; | ||
| 558 | struct dirent *d; | ||
| 559 | int ret = -1; | ||
| 560 | |||
| 561 | dir = opendir(procfs__mountpoint()); | ||
| 562 | if (!dir) | ||
| 563 | return -1; | ||
| 564 | |||
| 565 | /* Walk through the directory. */ | ||
| 566 | while (ret && (d = readdir(dir)) != NULL) { | ||
| 567 | char path[PATH_MAX]; | ||
| 568 | char *data; | ||
| 569 | size_t size; | ||
| 570 | |||
| 571 | if ((d->d_type != DT_DIR) || | ||
| 572 | !strcmp(".", d->d_name) || | ||
| 573 | !strcmp("..", d->d_name)) | ||
| 574 | continue; | ||
| 575 | |||
| 576 | scnprintf(path, sizeof(path), "%s/%s/comm", | ||
| 577 | procfs__mountpoint(), d->d_name); | ||
| 578 | |||
| 579 | if (filename__read_str(path, &data, &size)) | ||
| 580 | continue; | ||
| 581 | |||
| 582 | ret = strncmp(name, data, len); | ||
| 583 | free(data); | ||
| 584 | } | ||
| 585 | |||
| 586 | closedir(dir); | ||
| 587 | return ret ? false : true; | ||
| 588 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 66864364ccb4..80bfdaa0e2a4 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -39,6 +39,8 @@ | |||
| 39 | 39 | ||
| 40 | #define _ALL_SOURCE 1 | 40 | #define _ALL_SOURCE 1 |
| 41 | #define _BSD_SOURCE 1 | 41 | #define _BSD_SOURCE 1 |
| 42 | /* glibc 2.20 deprecates _BSD_SOURCE in favour of _DEFAULT_SOURCE */ | ||
| 43 | #define _DEFAULT_SOURCE 1 | ||
| 42 | #define HAS_BOOL | 44 | #define HAS_BOOL |
| 43 | 45 | ||
| 44 | #include <unistd.h> | 46 | #include <unistd.h> |
| @@ -64,16 +66,18 @@ | |||
| 64 | #include <regex.h> | 66 | #include <regex.h> |
| 65 | #include <utime.h> | 67 | #include <utime.h> |
| 66 | #include <sys/wait.h> | 68 | #include <sys/wait.h> |
| 67 | #include <sys/poll.h> | 69 | #include <poll.h> |
| 68 | #include <sys/socket.h> | 70 | #include <sys/socket.h> |
| 69 | #include <sys/ioctl.h> | 71 | #include <sys/ioctl.h> |
| 70 | #include <inttypes.h> | 72 | #include <inttypes.h> |
| 73 | #include <linux/kernel.h> | ||
| 71 | #include <linux/magic.h> | 74 | #include <linux/magic.h> |
| 72 | #include <linux/types.h> | 75 | #include <linux/types.h> |
| 73 | #include <sys/ttydefaults.h> | 76 | #include <sys/ttydefaults.h> |
| 74 | #include <api/fs/debugfs.h> | 77 | #include <api/fs/debugfs.h> |
| 75 | #include <termios.h> | 78 | #include <termios.h> |
| 76 | #include <linux/bitops.h> | 79 | #include <linux/bitops.h> |
| 80 | #include <termios.h> | ||
| 77 | 81 | ||
| 78 | extern const char *graph_line; | 82 | extern const char *graph_line; |
| 79 | extern const char *graph_dotted_line; | 83 | extern const char *graph_dotted_line; |
| @@ -307,6 +311,7 @@ extern unsigned int page_size; | |||
| 307 | extern int cacheline_size; | 311 | extern int cacheline_size; |
| 308 | 312 | ||
| 309 | void get_term_dimensions(struct winsize *ws); | 313 | void get_term_dimensions(struct winsize *ws); |
| 314 | void set_term_quiet_input(struct termios *old); | ||
| 310 | 315 | ||
| 311 | struct parse_tag { | 316 | struct parse_tag { |
| 312 | char tag; | 317 | char tag; |
| @@ -317,6 +322,21 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags); | |||
| 317 | 322 | ||
| 318 | #define SRCLINE_UNKNOWN ((char *) "??:0") | 323 | #define SRCLINE_UNKNOWN ((char *) "??:0") |
| 319 | 324 | ||
| 325 | static inline int path__join(char *bf, size_t size, | ||
| 326 | const char *path1, const char *path2) | ||
| 327 | { | ||
| 328 | return scnprintf(bf, size, "%s%s%s", path1, path1[0] ? "/" : "", path2); | ||
| 329 | } | ||
| 330 | |||
| 331 | static inline int path__join3(char *bf, size_t size, | ||
| 332 | const char *path1, const char *path2, | ||
| 333 | const char *path3) | ||
| 334 | { | ||
| 335 | return scnprintf(bf, size, "%s%s%s%s%s", | ||
| 336 | path1, path1[0] ? "/" : "", | ||
| 337 | path2, path2[0] ? "/" : "", path3); | ||
| 338 | } | ||
| 339 | |||
| 320 | struct dso; | 340 | struct dso; |
| 321 | 341 | ||
| 322 | char *get_srcline(struct dso *dso, unsigned long addr); | 342 | char *get_srcline(struct dso *dso, unsigned long addr); |
| @@ -330,4 +350,5 @@ void mem_bswap_64(void *src, int byte_size); | |||
| 330 | void mem_bswap_32(void *src, int byte_size); | 350 | void mem_bswap_32(void *src, int byte_size); |
| 331 | 351 | ||
| 332 | const char *get_filename_for_perf_kvm(void); | 352 | const char *get_filename_for_perf_kvm(void); |
| 353 | bool find_process(const char *name); | ||
| 333 | #endif /* GIT_COMPAT_UTIL_H */ | 354 | #endif /* GIT_COMPAT_UTIL_H */ |
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 55ab700f6ba5..bf1398180785 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl | |||
| @@ -194,6 +194,7 @@ my $config_bisect_check; | |||
| 194 | 194 | ||
| 195 | my $patchcheck_type; | 195 | my $patchcheck_type; |
| 196 | my $patchcheck_start; | 196 | my $patchcheck_start; |
| 197 | my $patchcheck_cherry; | ||
| 197 | my $patchcheck_end; | 198 | my $patchcheck_end; |
| 198 | 199 | ||
| 199 | # set when a test is something other that just building or install | 200 | # set when a test is something other that just building or install |
| @@ -320,6 +321,7 @@ my %option_map = ( | |||
| 320 | 321 | ||
| 321 | "PATCHCHECK_TYPE" => \$patchcheck_type, | 322 | "PATCHCHECK_TYPE" => \$patchcheck_type, |
| 322 | "PATCHCHECK_START" => \$patchcheck_start, | 323 | "PATCHCHECK_START" => \$patchcheck_start, |
| 324 | "PATCHCHECK_CHERRY" => \$patchcheck_cherry, | ||
| 323 | "PATCHCHECK_END" => \$patchcheck_end, | 325 | "PATCHCHECK_END" => \$patchcheck_end, |
| 324 | ); | 326 | ); |
| 325 | 327 | ||
| @@ -1448,6 +1450,12 @@ sub wait_for_monitor { | |||
| 1448 | } | 1450 | } |
| 1449 | } | 1451 | } |
| 1450 | print "** Monitor flushed **\n"; | 1452 | print "** Monitor flushed **\n"; |
| 1453 | |||
| 1454 | # if stop is defined but wasn't hit, return error | ||
| 1455 | # used by reboot (which wants to see a reboot) | ||
| 1456 | if (defined($stop) && !$booted) { | ||
| 1457 | $bug = 1; | ||
| 1458 | } | ||
| 1451 | return $bug; | 1459 | return $bug; |
| 1452 | } | 1460 | } |
| 1453 | 1461 | ||
| @@ -2336,15 +2344,17 @@ sub success { | |||
| 2336 | 2344 | ||
| 2337 | sub answer_bisect { | 2345 | sub answer_bisect { |
| 2338 | for (;;) { | 2346 | for (;;) { |
| 2339 | doprint "Pass or fail? [p/f]"; | 2347 | doprint "Pass, fail, or skip? [p/f/s]"; |
| 2340 | my $ans = <STDIN>; | 2348 | my $ans = <STDIN>; |
| 2341 | chomp $ans; | 2349 | chomp $ans; |
| 2342 | if ($ans eq "p" || $ans eq "P") { | 2350 | if ($ans eq "p" || $ans eq "P") { |
| 2343 | return 1; | 2351 | return 1; |
| 2344 | } elsif ($ans eq "f" || $ans eq "F") { | 2352 | } elsif ($ans eq "f" || $ans eq "F") { |
| 2345 | return 0; | 2353 | return 0; |
| 2354 | } elsif ($ans eq "s" || $ans eq "S") { | ||
| 2355 | return -1; | ||
| 2346 | } else { | 2356 | } else { |
| 2347 | print "Please answer 'P' or 'F'\n"; | 2357 | print "Please answer 'p', 'f', or 's'\n"; |
| 2348 | } | 2358 | } |
| 2349 | } | 2359 | } |
| 2350 | } | 2360 | } |
| @@ -2726,15 +2736,17 @@ sub bisect { | |||
| 2726 | run_command "git bisect start$start_files" or | 2736 | run_command "git bisect start$start_files" or |
| 2727 | dodie "could not start bisect"; | 2737 | dodie "could not start bisect"; |
| 2728 | 2738 | ||
| 2729 | run_command "git bisect good $good" or | ||
| 2730 | dodie "could not set bisect good to $good"; | ||
| 2731 | |||
| 2732 | run_git_bisect "git bisect bad $bad" or | ||
| 2733 | dodie "could not set bisect bad to $bad"; | ||
| 2734 | |||
| 2735 | if (defined($replay)) { | 2739 | if (defined($replay)) { |
| 2736 | run_command "git bisect replay $replay" or | 2740 | run_command "git bisect replay $replay" or |
| 2737 | dodie "failed to run replay"; | 2741 | dodie "failed to run replay"; |
| 2742 | } else { | ||
| 2743 | |||
| 2744 | run_command "git bisect good $good" or | ||
| 2745 | dodie "could not set bisect good to $good"; | ||
| 2746 | |||
| 2747 | run_git_bisect "git bisect bad $bad" or | ||
| 2748 | dodie "could not set bisect bad to $bad"; | ||
| 2749 | |||
| 2738 | } | 2750 | } |
| 2739 | 2751 | ||
| 2740 | if (defined($start)) { | 2752 | if (defined($start)) { |
| @@ -3181,9 +3193,16 @@ sub patchcheck { | |||
| 3181 | 3193 | ||
| 3182 | my $start = $patchcheck_start; | 3194 | my $start = $patchcheck_start; |
| 3183 | 3195 | ||
| 3196 | my $cherry = $patchcheck_cherry; | ||
| 3197 | if (!defined($cherry)) { | ||
| 3198 | $cherry = 0; | ||
| 3199 | } | ||
| 3200 | |||
| 3184 | my $end = "HEAD"; | 3201 | my $end = "HEAD"; |
| 3185 | if (defined($patchcheck_end)) { | 3202 | if (defined($patchcheck_end)) { |
| 3186 | $end = $patchcheck_end; | 3203 | $end = $patchcheck_end; |
| 3204 | } elsif ($cherry) { | ||
| 3205 | die "PATCHCHECK_END must be defined with PATCHCHECK_CHERRY\n"; | ||
| 3187 | } | 3206 | } |
| 3188 | 3207 | ||
| 3189 | # Get the true sha1's since we can use things like HEAD~3 | 3208 | # Get the true sha1's since we can use things like HEAD~3 |
| @@ -3197,24 +3216,38 @@ sub patchcheck { | |||
| 3197 | $type = "boot"; | 3216 | $type = "boot"; |
| 3198 | } | 3217 | } |
| 3199 | 3218 | ||
| 3200 | open (IN, "git log --pretty=oneline $end|") or | 3219 | if ($cherry) { |
| 3201 | dodie "could not get git list"; | 3220 | open (IN, "git cherry -v $start $end|") or |
| 3221 | dodie "could not get git list"; | ||
| 3222 | } else { | ||
| 3223 | open (IN, "git log --pretty=oneline $end|") or | ||
| 3224 | dodie "could not get git list"; | ||
| 3225 | } | ||
| 3202 | 3226 | ||
| 3203 | my @list; | 3227 | my @list; |
| 3204 | 3228 | ||
| 3205 | while (<IN>) { | 3229 | while (<IN>) { |
| 3206 | chomp; | 3230 | chomp; |
| 3231 | # git cherry adds a '+' we want to remove | ||
| 3232 | s/^\+ //; | ||
| 3207 | $list[$#list+1] = $_; | 3233 | $list[$#list+1] = $_; |
| 3208 | last if (/^$start/); | 3234 | last if (/^$start/); |
| 3209 | } | 3235 | } |
| 3210 | close(IN); | 3236 | close(IN); |
| 3211 | 3237 | ||
| 3212 | if ($list[$#list] !~ /^$start/) { | 3238 | if (!$cherry) { |
| 3213 | fail "SHA1 $start not found"; | 3239 | if ($list[$#list] !~ /^$start/) { |
| 3240 | fail "SHA1 $start not found"; | ||
| 3241 | } | ||
| 3242 | |||
| 3243 | # go backwards in the list | ||
| 3244 | @list = reverse @list; | ||
| 3214 | } | 3245 | } |
| 3215 | 3246 | ||
| 3216 | # go backwards in the list | 3247 | doprint("Going to test the following commits:\n"); |
| 3217 | @list = reverse @list; | 3248 | foreach my $l (@list) { |
| 3249 | doprint "$l\n"; | ||
| 3250 | } | ||
| 3218 | 3251 | ||
| 3219 | my $save_clean = $noclean; | 3252 | my $save_clean = $noclean; |
| 3220 | my %ignored_warnings; | 3253 | my %ignored_warnings; |
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 911e45ad657a..6c58cd8bbbae 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf | |||
| @@ -906,6 +906,16 @@ | |||
| 906 | # | 906 | # |
| 907 | # PATCHCHECK_END is the last patch to check (default HEAD) | 907 | # PATCHCHECK_END is the last patch to check (default HEAD) |
| 908 | # | 908 | # |
| 909 | # PATCHCHECK_CHERRY if set to non zero, then git cherry will be | ||
| 910 | # performed against PATCHCHECK_START and PATCHCHECK_END. That is | ||
| 911 | # | ||
| 912 | # git cherry ${PATCHCHECK_START} ${PATCHCHECK_END} | ||
| 913 | # | ||
| 914 | # Then the changes found will be tested. | ||
| 915 | # | ||
| 916 | # Note, PATCHCHECK_CHERRY requires PATCHCHECK_END to be defined. | ||
| 917 | # (default 0) | ||
| 918 | # | ||
| 909 | # PATCHCHECK_TYPE is required and is the type of test to run: | 919 | # PATCHCHECK_TYPE is required and is the type of test to run: |
| 910 | # build, boot, test. | 920 | # build, boot, test. |
| 911 | # | 921 | # |
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 36ff2e4c7b6f..45f145c6f843 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
| @@ -14,6 +14,7 @@ TARGETS += powerpc | |||
| 14 | TARGETS += user | 14 | TARGETS += user |
| 15 | TARGETS += sysctl | 15 | TARGETS += sysctl |
| 16 | TARGETS += firmware | 16 | TARGETS += firmware |
| 17 | TARGETS += ftrace | ||
| 17 | 18 | ||
| 18 | TARGETS_HOTPLUG = cpu-hotplug | 19 | TARGETS_HOTPLUG = cpu-hotplug |
| 19 | TARGETS_HOTPLUG += memory-hotplug | 20 | TARGETS_HOTPLUG += memory-hotplug |
diff --git a/tools/testing/selftests/ftrace/Makefile b/tools/testing/selftests/ftrace/Makefile new file mode 100644 index 000000000000..76cc9f156267 --- /dev/null +++ b/tools/testing/selftests/ftrace/Makefile | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | all: | ||
| 2 | |||
| 3 | run_tests: | ||
| 4 | @/bin/sh ./ftracetest || echo "ftrace selftests: [FAIL]" | ||
| 5 | |||
| 6 | clean: | ||
| 7 | rm -rf logs/* | ||
diff --git a/tools/testing/selftests/ftrace/README b/tools/testing/selftests/ftrace/README new file mode 100644 index 000000000000..182e76fa4b82 --- /dev/null +++ b/tools/testing/selftests/ftrace/README | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | Linux Ftrace Testcases | ||
| 2 | |||
| 3 | This is a collection of testcases for ftrace tracing feature in the Linux | ||
| 4 | kernel. Since ftrace exports interfaces via the debugfs, we just need | ||
| 5 | shell scripts for testing. Feel free to add new test cases. | ||
| 6 | |||
| 7 | Running the ftrace testcases | ||
| 8 | ============================ | ||
| 9 | |||
| 10 | At first, you need to be the root user to run this script. | ||
| 11 | To run all testcases: | ||
| 12 | |||
| 13 | $ sudo ./ftracetest | ||
| 14 | |||
| 15 | To run specific testcases: | ||
| 16 | |||
| 17 | # ./ftracetest test.d/basic3.tc | ||
| 18 | |||
| 19 | Or you can also run testcases under given directory: | ||
| 20 | |||
| 21 | # ./ftracetest test.d/kprobe/ | ||
| 22 | |||
| 23 | Contributing new testcases | ||
| 24 | ========================== | ||
| 25 | |||
| 26 | Copy test.d/template to your testcase (whose filename must have *.tc | ||
| 27 | extension) and rewrite the test description line. | ||
| 28 | |||
| 29 | * The working directory of the script is <debugfs>/tracing/. | ||
| 30 | |||
| 31 | * Take care with side effects as the tests are run with root privilege. | ||
| 32 | |||
| 33 | * The tests should not run for a long period of time (more than 1 min.) | ||
| 34 | These are to be unit tests. | ||
| 35 | |||
| 36 | * You can add a directory for your testcases under test.d/ if needed. | ||
| 37 | |||
| 38 | * The test cases should run on dash (busybox shell) for testing on | ||
| 39 | minimal cross-build environments. | ||
| 40 | |||
| 41 | * Note that the tests are run with "set -e" (errexit) option. If any | ||
| 42 | command fails, the test will be terminated immediately. | ||
| 43 | |||
| 44 | * The tests can return some result codes instead of pass or fail by | ||
| 45 | using exit_unresolved, exit_untested, exit_unsupported and exit_xfail. | ||
| 46 | |||
| 47 | Result code | ||
| 48 | =========== | ||
| 49 | |||
| 50 | Ftracetest supports following result codes. | ||
| 51 | |||
| 52 | * PASS: The test succeeded as expected. The test which exits with 0 is | ||
| 53 | counted as passed test. | ||
| 54 | |||
| 55 | * FAIL: The test failed, but was expected to succeed. The test which exits | ||
| 56 | with !0 is counted as failed test. | ||
| 57 | |||
| 58 | * UNRESOLVED: The test produced unclear or intermidiate results. | ||
| 59 | for example, the test was interrupted | ||
| 60 | or the test depends on a previous test, which failed. | ||
| 61 | or the test was set up incorrectly | ||
| 62 | The test which is in above situation, must call exit_unresolved. | ||
| 63 | |||
| 64 | * UNTESTED: The test was not run, currently just a placeholder. | ||
| 65 | In this case, the test must call exit_untested. | ||
| 66 | |||
| 67 | * UNSUPPORTED: The test failed because of lack of feature. | ||
| 68 | In this case, the test must call exit_unsupported. | ||
| 69 | |||
| 70 | * XFAIL: The test failed, and was expected to fail. | ||
| 71 | To return XFAIL, call exit_xfail from the test. | ||
| 72 | |||
| 73 | There are some sample test scripts for result code under samples/. | ||
| 74 | You can also run samples as below: | ||
| 75 | |||
| 76 | # ./ftracetest samples/ | ||
| 77 | |||
| 78 | TODO | ||
| 79 | ==== | ||
| 80 | |||
| 81 | * Fancy colored output :) | ||
| 82 | |||
diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest new file mode 100755 index 000000000000..a8f81c782856 --- /dev/null +++ b/tools/testing/selftests/ftrace/ftracetest | |||
| @@ -0,0 +1,253 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | # ftracetest - Ftrace test shell scripts | ||
| 4 | # | ||
| 5 | # Copyright (C) Hitachi Ltd., 2014 | ||
| 6 | # Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | ||
| 7 | # | ||
| 8 | # Released under the terms of the GPL v2. | ||
| 9 | |||
| 10 | usage() { # errno [message] | ||
| 11 | [ "$2" ] && echo $2 | ||
| 12 | echo "Usage: ftracetest [options] [testcase(s)] [testcase-directory(s)]" | ||
| 13 | echo " Options:" | ||
| 14 | echo " -h|--help Show help message" | ||
| 15 | echo " -k|--keep Keep passed test logs" | ||
| 16 | echo " -d|--debug Debug mode (trace all shell commands)" | ||
| 17 | exit $1 | ||
| 18 | } | ||
| 19 | |||
| 20 | errexit() { # message | ||
| 21 | echo "Error: $1" 1>&2 | ||
| 22 | exit 1 | ||
| 23 | } | ||
| 24 | |||
| 25 | # Ensuring user privilege | ||
| 26 | if [ `id -u` -ne 0 ]; then | ||
| 27 | errexit "this must be run by root user" | ||
| 28 | fi | ||
| 29 | |||
| 30 | # Utilities | ||
| 31 | absdir() { # file_path | ||
| 32 | (cd `dirname $1`; pwd) | ||
| 33 | } | ||
| 34 | |||
| 35 | abspath() { | ||
| 36 | echo `absdir $1`/`basename $1` | ||
| 37 | } | ||
| 38 | |||
| 39 | find_testcases() { #directory | ||
| 40 | echo `find $1 -name \*.tc` | ||
| 41 | } | ||
| 42 | |||
| 43 | parse_opts() { # opts | ||
| 44 | local OPT_TEST_CASES= | ||
| 45 | local OPT_TEST_DIR= | ||
| 46 | |||
| 47 | while [ "$1" ]; do | ||
| 48 | case "$1" in | ||
| 49 | --help|-h) | ||
| 50 | usage 0 | ||
| 51 | ;; | ||
| 52 | --keep|-k) | ||
| 53 | KEEP_LOG=1 | ||
| 54 | shift 1 | ||
| 55 | ;; | ||
| 56 | --debug|-d) | ||
| 57 | DEBUG=1 | ||
| 58 | shift 1 | ||
| 59 | ;; | ||
| 60 | *.tc) | ||
| 61 | if [ -f "$1" ]; then | ||
| 62 | OPT_TEST_CASES="$OPT_TEST_CASES `abspath $1`" | ||
| 63 | shift 1 | ||
| 64 | else | ||
| 65 | usage 1 "$1 is not a testcase" | ||
| 66 | fi | ||
| 67 | ;; | ||
| 68 | *) | ||
| 69 | if [ -d "$1" ]; then | ||
| 70 | OPT_TEST_DIR=`abspath $1` | ||
| 71 | OPT_TEST_CASES="$OPT_TEST_CASES `find_testcases $OPT_TEST_DIR`" | ||
| 72 | shift 1 | ||
| 73 | else | ||
| 74 | usage 1 "Invalid option ($1)" | ||
| 75 | fi | ||
| 76 | ;; | ||
| 77 | esac | ||
| 78 | done | ||
| 79 | if [ "$OPT_TEST_CASES" ]; then | ||
| 80 | TEST_CASES=$OPT_TEST_CASES | ||
| 81 | fi | ||
| 82 | } | ||
| 83 | |||
| 84 | # Parameters | ||
| 85 | DEBUGFS_DIR=`grep debugfs /proc/mounts | cut -f2 -d' '` | ||
| 86 | TRACING_DIR=$DEBUGFS_DIR/tracing | ||
| 87 | TOP_DIR=`absdir $0` | ||
| 88 | TEST_DIR=$TOP_DIR/test.d | ||
| 89 | TEST_CASES=`find_testcases $TEST_DIR` | ||
| 90 | LOG_DIR=$TOP_DIR/logs/`date +%Y%m%d-%H%M%S`/ | ||
| 91 | KEEP_LOG=0 | ||
| 92 | DEBUG=0 | ||
| 93 | # Parse command-line options | ||
| 94 | parse_opts $* | ||
| 95 | |||
| 96 | [ $DEBUG -ne 0 ] && set -x | ||
| 97 | |||
| 98 | # Verify parameters | ||
| 99 | if [ -z "$DEBUGFS_DIR" -o ! -d "$TRACING_DIR" ]; then | ||
| 100 | errexit "No ftrace directory found" | ||
| 101 | fi | ||
| 102 | |||
| 103 | # Preparing logs | ||
| 104 | LOG_FILE=$LOG_DIR/ftracetest.log | ||
| 105 | mkdir -p $LOG_DIR || errexit "Failed to make a log directory: $LOG_DIR" | ||
| 106 | date > $LOG_FILE | ||
| 107 | prlog() { # messages | ||
| 108 | echo "$@" | tee -a $LOG_FILE | ||
| 109 | } | ||
| 110 | catlog() { #file | ||
| 111 | cat $1 | tee -a $LOG_FILE | ||
| 112 | } | ||
| 113 | prlog "=== Ftrace unit tests ===" | ||
| 114 | |||
| 115 | |||
| 116 | # Testcase management | ||
| 117 | # Test result codes - Dejagnu extended code | ||
| 118 | PASS=0 # The test succeeded. | ||
| 119 | FAIL=1 # The test failed, but was expected to succeed. | ||
| 120 | UNRESOLVED=2 # The test produced indeterminate results. (e.g. interrupted) | ||
| 121 | UNTESTED=3 # The test was not run, currently just a placeholder. | ||
| 122 | UNSUPPORTED=4 # The test failed because of lack of feature. | ||
| 123 | XFAIL=5 # The test failed, and was expected to fail. | ||
| 124 | |||
| 125 | # Accumulations | ||
| 126 | PASSED_CASES= | ||
| 127 | FAILED_CASES= | ||
| 128 | UNRESOLVED_CASES= | ||
| 129 | UNTESTED_CASES= | ||
| 130 | UNSUPPORTED_CASES= | ||
| 131 | XFAILED_CASES= | ||
| 132 | UNDEFINED_CASES= | ||
| 133 | TOTAL_RESULT=0 | ||
| 134 | |||
| 135 | CASENO=0 | ||
| 136 | testcase() { # testfile | ||
| 137 | CASENO=$((CASENO+1)) | ||
| 138 | prlog -n "[$CASENO]"`grep "^#[ \t]*description:" $1 | cut -f2 -d:` | ||
| 139 | } | ||
| 140 | |||
| 141 | eval_result() { # retval sigval | ||
| 142 | local retval=$2 | ||
| 143 | if [ $2 -eq 0 ]; then | ||
| 144 | test $1 -ne 0 && retval=$FAIL | ||
| 145 | fi | ||
| 146 | case $retval in | ||
| 147 | $PASS) | ||
| 148 | prlog " [PASS]" | ||
| 149 | PASSED_CASES="$PASSED_CASES $CASENO" | ||
| 150 | return 0 | ||
| 151 | ;; | ||
| 152 | $FAIL) | ||
| 153 | prlog " [FAIL]" | ||
| 154 | FAILED_CASES="$FAILED_CASES $CASENO" | ||
| 155 | return 1 # this is a bug. | ||
| 156 | ;; | ||
| 157 | $UNRESOLVED) | ||
| 158 | prlog " [UNRESOLVED]" | ||
| 159 | UNRESOLVED_CASES="$UNRESOLVED_CASES $CASENO" | ||
| 160 | return 1 # this is a kind of bug.. something happened. | ||
| 161 | ;; | ||
| 162 | $UNTESTED) | ||
| 163 | prlog " [UNTESTED]" | ||
| 164 | UNTESTED_CASES="$UNTESTED_CASES $CASENO" | ||
| 165 | return 0 | ||
| 166 | ;; | ||
| 167 | $UNSUPPORTED) | ||
| 168 | prlog " [UNSUPPORTED]" | ||
| 169 | UNSUPPORTED_CASES="$UNSUPPORTED_CASES $CASENO" | ||
| 170 | return 1 # this is not a bug, but the result should be reported. | ||
| 171 | ;; | ||
| 172 | $XFAIL) | ||
| 173 | prlog " [XFAIL]" | ||
| 174 | XFAILED_CASES="$XFAILED_CASES $CASENO" | ||
| 175 | return 0 | ||
| 176 | ;; | ||
| 177 | *) | ||
| 178 | prlog " [UNDEFINED]" | ||
| 179 | UNDEFINED_CASES="$UNDEFINED_CASES $CASENO" | ||
| 180 | return 1 # this must be a test bug | ||
| 181 | ;; | ||
| 182 | esac | ||
| 183 | } | ||
| 184 | |||
| 185 | # Signal handling for result codes | ||
| 186 | SIG_RESULT= | ||
| 187 | SIG_BASE=36 # Use realtime signals | ||
| 188 | SIG_PID=$$ | ||
| 189 | |||
| 190 | SIG_UNRESOLVED=$((SIG_BASE + UNRESOLVED)) | ||
| 191 | exit_unresolved () { | ||
| 192 | kill -s $SIG_UNRESOLVED $SIG_PID | ||
| 193 | exit 0 | ||
| 194 | } | ||
| 195 | trap 'SIG_RESULT=$UNRESOLVED' $SIG_UNRESOLVED | ||
| 196 | |||
| 197 | SIG_UNTESTED=$((SIG_BASE + UNTESTED)) | ||
| 198 | exit_untested () { | ||
| 199 | kill -s $SIG_UNTESTED $SIG_PID | ||
| 200 | exit 0 | ||
| 201 | } | ||
| 202 | trap 'SIG_RESULT=$UNTESTED' $SIG_UNTESTED | ||
| 203 | |||
| 204 | SIG_UNSUPPORTED=$((SIG_BASE + UNSUPPORTED)) | ||
| 205 | exit_unsupported () { | ||
| 206 | kill -s $SIG_UNSUPPORTED $SIG_PID | ||
| 207 | exit 0 | ||
| 208 | } | ||
| 209 | trap 'SIG_RESULT=$UNSUPPORTED' $SIG_UNSUPPORTED | ||
| 210 | |||
| 211 | SIG_XFAIL=$((SIG_BASE + XFAIL)) | ||
| 212 | exit_xfail () { | ||
| 213 | kill -s $SIG_XFAIL $SIG_PID | ||
| 214 | exit 0 | ||
| 215 | } | ||
| 216 | trap 'SIG_RESULT=$XFAIL' $SIG_XFAIL | ||
| 217 | |||
| 218 | # Run one test case | ||
| 219 | run_test() { # testfile | ||
| 220 | local testname=`basename $1` | ||
| 221 | local testlog=`mktemp --tmpdir=$LOG_DIR ${testname}-XXXXXX.log` | ||
| 222 | testcase $1 | ||
| 223 | echo "execute: "$1 > $testlog | ||
| 224 | SIG_RESULT=0 | ||
| 225 | # setup PID and PPID, $$ is not updated. | ||
| 226 | (cd $TRACING_DIR; read PID _ < /proc/self/stat ; | ||
| 227 | set -e; set -x; . $1) >> $testlog 2>&1 | ||
| 228 | eval_result $? $SIG_RESULT | ||
| 229 | if [ $? -eq 0 ]; then | ||
| 230 | # Remove test log if the test was done as it was expected. | ||
| 231 | [ $KEEP_LOG -eq 0 ] && rm $testlog | ||
| 232 | else | ||
| 233 | catlog $testlog | ||
| 234 | TOTAL_RESULT=1 | ||
| 235 | fi | ||
| 236 | } | ||
| 237 | |||
| 238 | # Main loop | ||
| 239 | for t in $TEST_CASES; do | ||
| 240 | run_test $t | ||
| 241 | done | ||
| 242 | |||
| 243 | prlog "" | ||
| 244 | prlog "# of passed: " `echo $PASSED_CASES | wc -w` | ||
| 245 | prlog "# of failed: " `echo $FAILED_CASES | wc -w` | ||
| 246 | prlog "# of unresolved: " `echo $UNRESOLVED_CASES | wc -w` | ||
| 247 | prlog "# of untested: " `echo $UNTESTED_CASES | wc -w` | ||
| 248 | prlog "# of unsupported: " `echo $UNSUPPORTED_CASES | wc -w` | ||
| 249 | prlog "# of xfailed: " `echo $XFAILED_CASES | wc -w` | ||
| 250 | prlog "# of undefined(test bug): " `echo $UNDEFINED_CASES | wc -w` | ||
| 251 | |||
| 252 | # if no error, return 0 | ||
| 253 | exit $TOTAL_RESULT | ||
diff --git a/tools/testing/selftests/ftrace/samples/fail.tc b/tools/testing/selftests/ftrace/samples/fail.tc new file mode 100644 index 000000000000..15e35b956e05 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/fail.tc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: failure-case example | ||
| 3 | cat non-exist-file | ||
| 4 | echo "this is not executed" | ||
diff --git a/tools/testing/selftests/ftrace/samples/pass.tc b/tools/testing/selftests/ftrace/samples/pass.tc new file mode 100644 index 000000000000..d01549370041 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/pass.tc | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: pass-case example | ||
| 3 | return 0 | ||
diff --git a/tools/testing/selftests/ftrace/samples/unresolved.tc b/tools/testing/selftests/ftrace/samples/unresolved.tc new file mode 100644 index 000000000000..41e99d3358d1 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/unresolved.tc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: unresolved-case example | ||
| 3 | trap exit_unresolved INT | ||
| 4 | kill -INT $PID | ||
diff --git a/tools/testing/selftests/ftrace/samples/unsupported.tc b/tools/testing/selftests/ftrace/samples/unsupported.tc new file mode 100644 index 000000000000..45910ff13328 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/unsupported.tc | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: unsupported-case example | ||
| 3 | exit_unsupported | ||
diff --git a/tools/testing/selftests/ftrace/samples/untested.tc b/tools/testing/selftests/ftrace/samples/untested.tc new file mode 100644 index 000000000000..35a45946ec60 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/untested.tc | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: untested-case example | ||
| 3 | exit_untested | ||
diff --git a/tools/testing/selftests/ftrace/samples/xfail.tc b/tools/testing/selftests/ftrace/samples/xfail.tc new file mode 100644 index 000000000000..9dd395323259 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/xfail.tc | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: xfail-case example | ||
| 3 | cat non-exist-file || exit_xfail | ||
diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic1.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic1.tc new file mode 100644 index 000000000000..9980ff14ae44 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic1.tc | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: Basic trace file check | ||
| 3 | test -f README -a -f trace -a -f tracing_on -a -f trace_pipe | ||
diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic2.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic2.tc new file mode 100644 index 000000000000..bf9a7b037924 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic2.tc | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: Basic test for tracers | ||
| 3 | test -f available_tracers | ||
| 4 | for t in `cat available_tracers`; do | ||
| 5 | echo $t > current_tracer | ||
| 6 | done | ||
| 7 | echo nop > current_tracer | ||
diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic3.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic3.tc new file mode 100644 index 000000000000..bde6625d9785 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic3.tc | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: Basic trace clock test | ||
| 3 | test -f trace_clock | ||
| 4 | for c in `cat trace_clock | tr -d \[\]`; do | ||
| 5 | echo $c > trace_clock | ||
| 6 | grep '\['$c'\]' trace_clock | ||
| 7 | done | ||
| 8 | echo local > trace_clock | ||
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc b/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc new file mode 100644 index 000000000000..1b8b665ab2b3 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: Kprobe dynamic event - adding and removing | ||
| 3 | |||
| 4 | [ -f kprobe_events ] || exit_unsupported # this is configurable | ||
| 5 | |||
| 6 | echo 0 > events/enable | ||
| 7 | echo > kprobe_events | ||
| 8 | echo p:myevent do_fork > kprobe_events | ||
| 9 | grep myevent kprobe_events | ||
| 10 | test -d events/kprobes/myevent | ||
| 11 | echo > kprobe_events | ||
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc new file mode 100644 index 000000000000..b55c84003587 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: Kprobe dynamic event - busy event check | ||
| 3 | |||
| 4 | [ -f kprobe_events ] || exit_unsupported | ||
| 5 | |||
| 6 | echo 0 > events/enable | ||
| 7 | echo > kprobe_events | ||
| 8 | echo p:myevent do_fork > kprobe_events | ||
| 9 | test -d events/kprobes/myevent | ||
| 10 | echo 1 > events/kprobes/myevent/enable | ||
| 11 | echo > kprobe_events && exit 1 # this must fail | ||
| 12 | echo 0 > events/kprobes/myevent/enable | ||
| 13 | echo > kprobe_events # this must succeed | ||
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc new file mode 100644 index 000000000000..a603d3f8db7b --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: Kprobe dynamic event with arguments | ||
| 3 | |||
| 4 | [ -f kprobe_events ] || exit_unsupported # this is configurable | ||
| 5 | |||
| 6 | echo 0 > events/enable | ||
| 7 | echo > kprobe_events | ||
| 8 | echo 'p:testprobe do_fork $stack $stack0 +0($stack)' > kprobe_events | ||
| 9 | grep testprobe kprobe_events | ||
| 10 | test -d events/kprobes/testprobe | ||
| 11 | echo 1 > events/kprobes/testprobe/enable | ||
| 12 | ( echo "forked") | ||
| 13 | echo 0 > events/kprobes/testprobe/enable | ||
| 14 | echo "-:testprobe" >> kprobe_events | ||
| 15 | test -d events/kprobes/testprobe && exit 1 || exit 0 | ||
| 16 | |||
diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc new file mode 100644 index 000000000000..283c29e7f7c4 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: Kretprobe dynamic event with arguments | ||
| 3 | |||
| 4 | [ -f kprobe_events ] || exit_unsupported # this is configurable | ||
| 5 | |||
| 6 | echo 0 > events/enable | ||
| 7 | echo > kprobe_events | ||
| 8 | echo 'r:testprobe2 do_fork $retval' > kprobe_events | ||
| 9 | grep testprobe2 kprobe_events | ||
| 10 | test -d events/kprobes/testprobe2 | ||
| 11 | echo 1 > events/kprobes/testprobe2/enable | ||
| 12 | ( echo "forked") | ||
| 13 | echo 0 > events/kprobes/testprobe2/enable | ||
| 14 | echo '-:testprobe2' >> kprobe_events | ||
| 15 | test -d events/kprobes/testprobe2 && exit 1 || exit 0 | ||
diff --git a/tools/testing/selftests/ftrace/test.d/template b/tools/testing/selftests/ftrace/test.d/template new file mode 100644 index 000000000000..5448f7abad5f --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/template | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # description: %HERE DESCRIBE WHAT THIS DOES% | ||
| 3 | # you have to add ".tc" extention for your testcase file | ||
| 4 | # Note that all tests are run with "errexit" option. | ||
| 5 | |||
| 6 | exit 0 # Return 0 if the test is passed, otherwise return !0 | ||
| 7 | # If the test could not run because of lack of feature, call exit_unsupported | ||
| 8 | # If the test returned unclear results, call exit_unresolved | ||
| 9 | # If the test is a dummy, or a placeholder, call exit_untested | ||
diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile index ad4ab01cd28f..b80cd10d53ba 100644 --- a/tools/testing/selftests/memfd/Makefile +++ b/tools/testing/selftests/memfd/Makefile | |||
| @@ -1,38 +1,17 @@ | |||
| 1 | uname_M := $(shell uname -m 2>/dev/null || echo not) | ||
| 2 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) | ||
| 3 | ifeq ($(ARCH),i386) | ||
| 4 | ARCH := x86 | ||
| 5 | endif | ||
| 6 | ifeq ($(ARCH),x86_64) | ||
| 7 | ARCH := x86 | ||
| 8 | endif | ||
| 9 | |||
| 10 | CFLAGS += -D_FILE_OFFSET_BITS=64 | 1 | CFLAGS += -D_FILE_OFFSET_BITS=64 |
| 11 | CFLAGS += -I../../../../arch/x86/include/generated/uapi/ | ||
| 12 | CFLAGS += -I../../../../arch/x86/include/uapi/ | ||
| 13 | CFLAGS += -I../../../../include/uapi/ | 2 | CFLAGS += -I../../../../include/uapi/ |
| 14 | CFLAGS += -I../../../../include/ | 3 | CFLAGS += -I../../../../include/ |
| 15 | 4 | ||
| 16 | all: | 5 | all: |
| 17 | ifeq ($(ARCH),x86) | ||
| 18 | gcc $(CFLAGS) memfd_test.c -o memfd_test | 6 | gcc $(CFLAGS) memfd_test.c -o memfd_test |
| 19 | else | ||
| 20 | echo "Not an x86 target, can't build memfd selftest" | ||
| 21 | endif | ||
| 22 | 7 | ||
| 23 | run_tests: all | 8 | run_tests: all |
| 24 | ifeq ($(ARCH),x86) | ||
| 25 | gcc $(CFLAGS) memfd_test.c -o memfd_test | 9 | gcc $(CFLAGS) memfd_test.c -o memfd_test |
| 26 | endif | ||
| 27 | @./memfd_test || echo "memfd_test: [FAIL]" | 10 | @./memfd_test || echo "memfd_test: [FAIL]" |
| 28 | 11 | ||
| 29 | build_fuse: | 12 | build_fuse: |
| 30 | ifeq ($(ARCH),x86) | ||
| 31 | gcc $(CFLAGS) fuse_mnt.c `pkg-config fuse --cflags --libs` -o fuse_mnt | 13 | gcc $(CFLAGS) fuse_mnt.c `pkg-config fuse --cflags --libs` -o fuse_mnt |
| 32 | gcc $(CFLAGS) fuse_test.c -o fuse_test | 14 | gcc $(CFLAGS) fuse_test.c -o fuse_test |
| 33 | else | ||
| 34 | echo "Not an x86 target, can't build memfd selftest" | ||
| 35 | endif | ||
| 36 | 15 | ||
| 37 | run_fuse: build_fuse | 16 | run_fuse: build_fuse |
| 38 | @./run_fuse_test.sh || echo "fuse_test: [FAIL]" | 17 | @./run_fuse_test.sh || echo "fuse_test: [FAIL]" |
diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index 3634c909b1b0..0b9eafb7ab7b 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c | |||
| @@ -59,9 +59,9 @@ static void mfd_fail_new(const char *name, unsigned int flags) | |||
| 59 | } | 59 | } |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | static __u64 mfd_assert_get_seals(int fd) | 62 | static unsigned int mfd_assert_get_seals(int fd) |
| 63 | { | 63 | { |
| 64 | long r; | 64 | int r; |
| 65 | 65 | ||
| 66 | r = fcntl(fd, F_GET_SEALS); | 66 | r = fcntl(fd, F_GET_SEALS); |
| 67 | if (r < 0) { | 67 | if (r < 0) { |
| @@ -69,50 +69,48 @@ static __u64 mfd_assert_get_seals(int fd) | |||
| 69 | abort(); | 69 | abort(); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | return r; | 72 | return (unsigned int)r; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | static void mfd_assert_has_seals(int fd, __u64 seals) | 75 | static void mfd_assert_has_seals(int fd, unsigned int seals) |
| 76 | { | 76 | { |
| 77 | __u64 s; | 77 | unsigned int s; |
| 78 | 78 | ||
| 79 | s = mfd_assert_get_seals(fd); | 79 | s = mfd_assert_get_seals(fd); |
| 80 | if (s != seals) { | 80 | if (s != seals) { |
| 81 | printf("%llu != %llu = GET_SEALS(%d)\n", | 81 | printf("%u != %u = GET_SEALS(%d)\n", seals, s, fd); |
| 82 | (unsigned long long)seals, (unsigned long long)s, fd); | ||
| 83 | abort(); | 82 | abort(); |
| 84 | } | 83 | } |
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | static void mfd_assert_add_seals(int fd, __u64 seals) | 86 | static void mfd_assert_add_seals(int fd, unsigned int seals) |
| 88 | { | 87 | { |
| 89 | long r; | 88 | int r; |
| 90 | __u64 s; | 89 | unsigned int s; |
| 91 | 90 | ||
| 92 | s = mfd_assert_get_seals(fd); | 91 | s = mfd_assert_get_seals(fd); |
| 93 | r = fcntl(fd, F_ADD_SEALS, seals); | 92 | r = fcntl(fd, F_ADD_SEALS, seals); |
| 94 | if (r < 0) { | 93 | if (r < 0) { |
| 95 | printf("ADD_SEALS(%d, %llu -> %llu) failed: %m\n", | 94 | printf("ADD_SEALS(%d, %u -> %u) failed: %m\n", fd, s, seals); |
| 96 | fd, (unsigned long long)s, (unsigned long long)seals); | ||
| 97 | abort(); | 95 | abort(); |
| 98 | } | 96 | } |
| 99 | } | 97 | } |
| 100 | 98 | ||
| 101 | static void mfd_fail_add_seals(int fd, __u64 seals) | 99 | static void mfd_fail_add_seals(int fd, unsigned int seals) |
| 102 | { | 100 | { |
| 103 | long r; | 101 | int r; |
| 104 | __u64 s; | 102 | unsigned int s; |
| 105 | 103 | ||
| 106 | r = fcntl(fd, F_GET_SEALS); | 104 | r = fcntl(fd, F_GET_SEALS); |
| 107 | if (r < 0) | 105 | if (r < 0) |
| 108 | s = 0; | 106 | s = 0; |
| 109 | else | 107 | else |
| 110 | s = r; | 108 | s = (unsigned int)r; |
| 111 | 109 | ||
| 112 | r = fcntl(fd, F_ADD_SEALS, seals); | 110 | r = fcntl(fd, F_ADD_SEALS, seals); |
| 113 | if (r >= 0) { | 111 | if (r >= 0) { |
| 114 | printf("ADD_SEALS(%d, %llu -> %llu) didn't fail as expected\n", | 112 | printf("ADD_SEALS(%d, %u -> %u) didn't fail as expected\n", |
| 115 | fd, (unsigned long long)s, (unsigned long long)seals); | 113 | fd, s, seals); |
| 116 | abort(); | 114 | abort(); |
| 117 | } | 115 | } |
| 118 | } | 116 | } |
| @@ -205,7 +203,7 @@ static void mfd_fail_open(int fd, int flags, mode_t mode) | |||
| 205 | sprintf(buf, "/proc/self/fd/%d", fd); | 203 | sprintf(buf, "/proc/self/fd/%d", fd); |
| 206 | r = open(buf, flags, mode); | 204 | r = open(buf, flags, mode); |
| 207 | if (r >= 0) { | 205 | if (r >= 0) { |
| 208 | printf("open(%s) didn't fail as expected\n"); | 206 | printf("open(%s) didn't fail as expected\n", buf); |
| 209 | abort(); | 207 | abort(); |
| 210 | } | 208 | } |
| 211 | } | 209 | } |
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index 74a78cedce37..f6ff90a76bd7 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile | |||
| @@ -13,7 +13,7 @@ CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CUR | |||
| 13 | 13 | ||
| 14 | export CC CFLAGS | 14 | export CC CFLAGS |
| 15 | 15 | ||
| 16 | TARGETS = pmu copyloops mm tm | 16 | TARGETS = pmu copyloops mm tm primitives |
| 17 | 17 | ||
| 18 | endif | 18 | endif |
| 19 | 19 | ||
diff --git a/tools/testing/selftests/powerpc/primitives/Makefile b/tools/testing/selftests/powerpc/primitives/Makefile new file mode 100644 index 000000000000..ea737ca01732 --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/Makefile | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | CFLAGS += -I$(CURDIR) | ||
| 2 | |||
| 3 | PROGS := load_unaligned_zeropad | ||
| 4 | |||
| 5 | all: $(PROGS) | ||
| 6 | |||
| 7 | $(PROGS): ../harness.c | ||
| 8 | |||
| 9 | run_tests: all | ||
| 10 | @-for PROG in $(PROGS); do \ | ||
| 11 | ./$$PROG; \ | ||
| 12 | done; | ||
| 13 | |||
| 14 | clean: | ||
| 15 | rm -f $(PROGS) *.o | ||
| 16 | |||
| 17 | .PHONY: all run_tests clean | ||
diff --git a/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h b/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h new file mode 120000 index 000000000000..b14255e15a25 --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/asm/asm-compat.h | |||
| @@ -0,0 +1 @@ | |||
| ../.././../../../../arch/powerpc/include/asm/asm-compat.h \ No newline at end of file | |||
diff --git a/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h b/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h | |||
diff --git a/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c new file mode 100644 index 000000000000..d1b647509596 --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | /* | ||
| 2 | * Userspace test harness for load_unaligned_zeropad. Creates two | ||
| 3 | * pages and uses mprotect to prevent access to the second page and | ||
| 4 | * a SEGV handler that walks the exception tables and runs the fixup | ||
| 5 | * routine. | ||
| 6 | * | ||
| 7 | * The results are compared against a normal load that is that is | ||
| 8 | * performed while access to the second page is enabled via mprotect. | ||
| 9 | * | ||
| 10 | * Copyright (C) 2014 Anton Blanchard <anton@au.ibm.com>, IBM | ||
| 11 | * | ||
| 12 | * This program is free software; you can redistribute it and/or | ||
| 13 | * modify it under the terms of the GNU General Public License | ||
| 14 | * as published by the Free Software Foundation; either version | ||
| 15 | * 2 of the License, or (at your option) any later version. | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <stdlib.h> | ||
| 19 | #include <string.h> | ||
| 20 | #include <stdio.h> | ||
| 21 | #include <stdbool.h> | ||
| 22 | #include <signal.h> | ||
| 23 | #include <unistd.h> | ||
| 24 | #include <sys/mman.h> | ||
| 25 | |||
| 26 | #define FIXUP_SECTION ".ex_fixup" | ||
| 27 | |||
| 28 | #include "word-at-a-time.h" | ||
| 29 | |||
| 30 | #include "utils.h" | ||
| 31 | |||
| 32 | |||
| 33 | static int page_size; | ||
| 34 | static char *mem_region; | ||
| 35 | |||
| 36 | static int protect_region(void) | ||
| 37 | { | ||
| 38 | if (mprotect(mem_region + page_size, page_size, PROT_NONE)) { | ||
| 39 | perror("mprotect"); | ||
| 40 | return 1; | ||
| 41 | } | ||
| 42 | |||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 46 | static int unprotect_region(void) | ||
| 47 | { | ||
| 48 | if (mprotect(mem_region + page_size, page_size, PROT_READ|PROT_WRITE)) { | ||
| 49 | perror("mprotect"); | ||
| 50 | return 1; | ||
| 51 | } | ||
| 52 | |||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | extern char __start___ex_table[]; | ||
| 57 | extern char __stop___ex_table[]; | ||
| 58 | |||
| 59 | #if defined(__powerpc64__) | ||
| 60 | #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.gp_regs[PT_NIP] | ||
| 61 | #elif defined(__powerpc__) | ||
| 62 | #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.uc_regs->gregs[PT_NIP] | ||
| 63 | #else | ||
| 64 | #error implement UCONTEXT_NIA | ||
| 65 | #endif | ||
| 66 | |||
| 67 | static int segv_error; | ||
| 68 | |||
| 69 | static void segv_handler(int signr, siginfo_t *info, void *ptr) | ||
| 70 | { | ||
| 71 | ucontext_t *uc = (ucontext_t *)ptr; | ||
| 72 | unsigned long addr = (unsigned long)info->si_addr; | ||
| 73 | unsigned long *ip = &UCONTEXT_NIA(uc); | ||
| 74 | unsigned long *ex_p = (unsigned long *)__start___ex_table; | ||
| 75 | |||
| 76 | while (ex_p < (unsigned long *)__stop___ex_table) { | ||
| 77 | unsigned long insn, fixup; | ||
| 78 | |||
| 79 | insn = *ex_p++; | ||
| 80 | fixup = *ex_p++; | ||
| 81 | |||
| 82 | if (insn == *ip) { | ||
| 83 | *ip = fixup; | ||
| 84 | return; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | printf("No exception table match for NIA %lx ADDR %lx\n", *ip, addr); | ||
| 89 | segv_error++; | ||
| 90 | } | ||
| 91 | |||
| 92 | static void setup_segv_handler(void) | ||
| 93 | { | ||
| 94 | struct sigaction action; | ||
| 95 | |||
| 96 | memset(&action, 0, sizeof(action)); | ||
| 97 | action.sa_sigaction = segv_handler; | ||
| 98 | action.sa_flags = SA_SIGINFO; | ||
| 99 | sigaction(SIGSEGV, &action, NULL); | ||
| 100 | } | ||
| 101 | |||
| 102 | static int do_one_test(char *p, int page_offset) | ||
| 103 | { | ||
| 104 | unsigned long should; | ||
| 105 | unsigned long got; | ||
| 106 | |||
| 107 | FAIL_IF(unprotect_region()); | ||
| 108 | should = *(unsigned long *)p; | ||
| 109 | FAIL_IF(protect_region()); | ||
| 110 | |||
| 111 | got = load_unaligned_zeropad(p); | ||
| 112 | |||
| 113 | if (should != got) | ||
| 114 | printf("offset %u load_unaligned_zeropad returned 0x%lx, should be 0x%lx\n", page_offset, got, should); | ||
| 115 | |||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | static int test_body(void) | ||
| 120 | { | ||
| 121 | unsigned long i; | ||
| 122 | |||
| 123 | page_size = getpagesize(); | ||
| 124 | mem_region = mmap(NULL, page_size * 2, PROT_READ|PROT_WRITE, | ||
| 125 | MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); | ||
| 126 | |||
| 127 | FAIL_IF(mem_region == MAP_FAILED); | ||
| 128 | |||
| 129 | for (i = 0; i < page_size; i++) | ||
| 130 | mem_region[i] = i; | ||
| 131 | |||
| 132 | memset(mem_region+page_size, 0, page_size); | ||
| 133 | |||
| 134 | setup_segv_handler(); | ||
| 135 | |||
| 136 | for (i = 0; i < page_size; i++) | ||
| 137 | FAIL_IF(do_one_test(mem_region+i, i)); | ||
| 138 | |||
| 139 | FAIL_IF(segv_error); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | int main(void) | ||
| 145 | { | ||
| 146 | return test_harness(test_body, "load_unaligned_zeropad"); | ||
| 147 | } | ||
diff --git a/tools/testing/selftests/powerpc/primitives/word-at-a-time.h b/tools/testing/selftests/powerpc/primitives/word-at-a-time.h new file mode 120000 index 000000000000..eb74401b591f --- /dev/null +++ b/tools/testing/selftests/powerpc/primitives/word-at-a-time.h | |||
| @@ -0,0 +1 @@ | |||
| ../../../../../arch/powerpc/include/asm/word-at-a-time.h \ No newline at end of file | |||
diff --git a/tools/testing/selftests/rcutorture/bin/config2frag.sh b/tools/testing/selftests/rcutorture/bin/config2frag.sh index 9f9ffcd427d3..56f51ae13d73 100644..100755 --- a/tools/testing/selftests/rcutorture/bin/config2frag.sh +++ b/tools/testing/selftests/rcutorture/bin/config2frag.sh | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/bash |
| 2 | # Usage: sh config2frag.sh < .config > configfrag | 2 | # Usage: config2frag.sh < .config > configfrag |
| 3 | # | 3 | # |
| 4 | # Converts the "# CONFIG_XXX is not set" to "CONFIG_XXX=n" so that the | 4 | # Converts the "# CONFIG_XXX is not set" to "CONFIG_XXX=n" so that the |
| 5 | # resulting file becomes a legitimate Kconfig fragment. | 5 | # resulting file becomes a legitimate Kconfig fragment. |
diff --git a/tools/testing/selftests/rcutorture/bin/configcheck.sh b/tools/testing/selftests/rcutorture/bin/configcheck.sh index d686537dd55c..eee31e261bf7 100755 --- a/tools/testing/selftests/rcutorture/bin/configcheck.sh +++ b/tools/testing/selftests/rcutorture/bin/configcheck.sh | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/bash |
| 2 | # Usage: sh configcheck.sh .config .config-template | 2 | # Usage: configcheck.sh .config .config-template |
| 3 | # | 3 | # |
| 4 | # This program is free software; you can redistribute it and/or modify | 4 | # This program is free software; you can redistribute it and/or modify |
| 5 | # it under the terms of the GNU General Public License as published by | 5 | # it under the terms of the GNU General Public License as published by |
diff --git a/tools/testing/selftests/rcutorture/bin/configinit.sh b/tools/testing/selftests/rcutorture/bin/configinit.sh index 9c3f3d39b934..15f1a17ca96e 100755 --- a/tools/testing/selftests/rcutorture/bin/configinit.sh +++ b/tools/testing/selftests/rcutorture/bin/configinit.sh | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/bash |
| 2 | # | 2 | # |
| 3 | # sh configinit.sh config-spec-file [ build output dir ] | 3 | # Usage: configinit.sh config-spec-file [ build output dir ] |
| 4 | # | 4 | # |
| 5 | # Create a .config file from the spec file. Run from the kernel source tree. | 5 | # Create a .config file from the spec file. Run from the kernel source tree. |
| 6 | # Exits with 0 if all went well, with 1 if all went well but the config | 6 | # Exits with 0 if all went well, with 1 if all went well but the config |
diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh index d01b865bb100..b325470c01b3 100644 --- a/tools/testing/selftests/rcutorture/bin/functions.sh +++ b/tools/testing/selftests/rcutorture/bin/functions.sh | |||
| @@ -64,6 +64,26 @@ configfrag_boot_params () { | |||
| 64 | fi | 64 | fi |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | # configfrag_boot_cpus bootparam-string config-fragment-file config-cpus | ||
| 68 | # | ||
| 69 | # Decreases number of CPUs based on any maxcpus= boot parameters specified. | ||
| 70 | configfrag_boot_cpus () { | ||
| 71 | local bootargs="`configfrag_boot_params "$1" "$2"`" | ||
| 72 | local maxcpus | ||
| 73 | if echo "${bootargs}" | grep -q 'maxcpus=[0-9]' | ||
| 74 | then | ||
| 75 | maxcpus="`echo "${bootargs}" | sed -e 's/^.*maxcpus=\([0-9]*\).*$/\1/'`" | ||
| 76 | if test "$3" -gt "$maxcpus" | ||
| 77 | then | ||
| 78 | echo $maxcpus | ||
| 79 | else | ||
| 80 | echo $3 | ||
| 81 | fi | ||
| 82 | else | ||
| 83 | echo $3 | ||
| 84 | fi | ||
| 85 | } | ||
| 86 | |||
| 67 | # configfrag_hotplug_cpu config-fragment-file | 87 | # configfrag_hotplug_cpu config-fragment-file |
| 68 | # | 88 | # |
| 69 | # Returns 1 if the config fragment specifies hotplug CPU. | 89 | # Returns 1 if the config fragment specifies hotplug CPU. |
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-build.sh b/tools/testing/selftests/rcutorture/bin/kvm-build.sh index 7c1e56b46de4..00cb0db2643d 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-build.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-build.sh | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | # | 2 | # |
| 3 | # Build a kvm-ready Linux kernel from the tree in the current directory. | 3 | # Build a kvm-ready Linux kernel from the tree in the current directory. |
| 4 | # | 4 | # |
| 5 | # Usage: sh kvm-build.sh config-template build-dir more-configs | 5 | # Usage: kvm-build.sh config-template build-dir more-configs |
| 6 | # | 6 | # |
| 7 | # This program is free software; you can redistribute it and/or modify | 7 | # This program is free software; you can redistribute it and/or modify |
| 8 | # it under the terms of the GNU General Public License as published by | 8 | # it under the terms of the GNU General Public License as published by |
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh index 7f1ff1a8fc4b..43f764098e50 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-lock.sh | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | # | 2 | # |
| 3 | # Analyze a given results directory for locktorture progress. | 3 | # Analyze a given results directory for locktorture progress. |
| 4 | # | 4 | # |
| 5 | # Usage: sh kvm-recheck-lock.sh resdir | 5 | # Usage: kvm-recheck-lock.sh resdir |
| 6 | # | 6 | # |
| 7 | # This program is free software; you can redistribute it and/or modify | 7 | # This program is free software; you can redistribute it and/or modify |
| 8 | # it under the terms of the GNU General Public License as published by | 8 | # it under the terms of the GNU General Public License as published by |
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh index 307c4b95f325..d6cc07fc137f 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | # | 2 | # |
| 3 | # Analyze a given results directory for rcutorture progress. | 3 | # Analyze a given results directory for rcutorture progress. |
| 4 | # | 4 | # |
| 5 | # Usage: sh kvm-recheck-rcu.sh resdir | 5 | # Usage: kvm-recheck-rcu.sh resdir |
| 6 | # | 6 | # |
| 7 | # This program is free software; you can redistribute it and/or modify | 7 | # This program is free software; you can redistribute it and/or modify |
| 8 | # it under the terms of the GNU General Public License as published by | 8 | # it under the terms of the GNU General Public License as published by |
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh index 3f6c9b78d177..4f5b20f367a9 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck.sh | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | # check the build and console output for errors. Given a directory | 4 | # check the build and console output for errors. Given a directory |
| 5 | # containing results directories, this recursively checks them all. | 5 | # containing results directories, this recursively checks them all. |
| 6 | # | 6 | # |
| 7 | # Usage: sh kvm-recheck.sh resdir ... | 7 | # Usage: kvm-recheck.sh resdir ... |
| 8 | # | 8 | # |
| 9 | # This program is free software; you can redistribute it and/or modify | 9 | # This program is free software; you can redistribute it and/or modify |
| 10 | # it under the terms of the GNU General Public License as published by | 10 | # it under the terms of the GNU General Public License as published by |
diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh index 0f69dcbf9def..f6b2b4771b78 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | # Execute this in the source tree. Do not run it as a background task | 6 | # Execute this in the source tree. Do not run it as a background task |
| 7 | # because qemu does not seem to like that much. | 7 | # because qemu does not seem to like that much. |
| 8 | # | 8 | # |
| 9 | # Usage: sh kvm-test-1-run.sh config builddir resdir minutes qemu-args boot_args | 9 | # Usage: kvm-test-1-run.sh config builddir resdir minutes qemu-args boot_args |
| 10 | # | 10 | # |
| 11 | # qemu-args defaults to "-nographic", along with arguments specifying the | 11 | # qemu-args defaults to "-nographic", along with arguments specifying the |
| 12 | # number of CPUs and other options generated from | 12 | # number of CPUs and other options generated from |
| @@ -140,6 +140,7 @@ fi | |||
| 140 | # Generate -smp qemu argument. | 140 | # Generate -smp qemu argument. |
| 141 | qemu_args="-nographic $qemu_args" | 141 | qemu_args="-nographic $qemu_args" |
| 142 | cpu_count=`configNR_CPUS.sh $config_template` | 142 | cpu_count=`configNR_CPUS.sh $config_template` |
| 143 | cpu_count=`configfrag_boot_cpus "$boot_args" "$config_template" "$cpu_count"` | ||
| 143 | vcpus=`identify_qemu_vcpus` | 144 | vcpus=`identify_qemu_vcpus` |
| 144 | if test $cpu_count -gt $vcpus | 145 | if test $cpu_count -gt $vcpus |
| 145 | then | 146 | then |
| @@ -214,7 +215,7 @@ then | |||
| 214 | fi | 215 | fi |
| 215 | if test $kruntime -ge $((seconds + grace)) | 216 | if test $kruntime -ge $((seconds + grace)) |
| 216 | then | 217 | then |
| 217 | echo "!!! Hang at $kruntime vs. $seconds seconds" >> $resdir/Warnings 2>&1 | 218 | echo "!!! PID $qemu_pid hung at $kruntime vs. $seconds seconds" >> $resdir/Warnings 2>&1 |
| 218 | kill -KILL $qemu_pid | 219 | kill -KILL $qemu_pid |
| 219 | break | 220 | break |
| 220 | fi | 221 | fi |
diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh index 589e9c38413b..e527dc952eb0 100644..100755 --- a/tools/testing/selftests/rcutorture/bin/kvm.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | # Edit the definitions below to set the locations of the various directories, | 7 | # Edit the definitions below to set the locations of the various directories, |
| 8 | # as well as the test duration. | 8 | # as well as the test duration. |
| 9 | # | 9 | # |
| 10 | # Usage: sh kvm.sh [ options ] | 10 | # Usage: kvm.sh [ options ] |
| 11 | # | 11 | # |
| 12 | # This program is free software; you can redistribute it and/or modify | 12 | # This program is free software; you can redistribute it and/or modify |
| 13 | # it under the terms of the GNU General Public License as published by | 13 | # it under the terms of the GNU General Public License as published by |
| @@ -188,7 +188,9 @@ for CF in $configs | |||
| 188 | do | 188 | do |
| 189 | if test -f "$CONFIGFRAG/$kversion/$CF" | 189 | if test -f "$CONFIGFRAG/$kversion/$CF" |
| 190 | then | 190 | then |
| 191 | echo $CF `configNR_CPUS.sh $CONFIGFRAG/$kversion/$CF` >> $T/cfgcpu | 191 | cpu_count=`configNR_CPUS.sh $CONFIGFRAG/$kversion/$CF` |
| 192 | cpu_count=`configfrag_boot_cpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$kversion/$CF" "$cpu_count"` | ||
| 193 | echo $CF $cpu_count >> $T/cfgcpu | ||
| 192 | else | 194 | else |
| 193 | echo "The --configs file $CF does not exist, terminating." | 195 | echo "The --configs file $CF does not exist, terminating." |
| 194 | exit 1 | 196 | exit 1 |
diff --git a/tools/testing/selftests/rcutorture/bin/parse-build.sh b/tools/testing/selftests/rcutorture/bin/parse-build.sh index 543230951c38..499d1e598e42 100755 --- a/tools/testing/selftests/rcutorture/bin/parse-build.sh +++ b/tools/testing/selftests/rcutorture/bin/parse-build.sh | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/bash |
| 2 | # | 2 | # |
| 3 | # Check the build output from an rcutorture run for goodness. | 3 | # Check the build output from an rcutorture run for goodness. |
| 4 | # The "file" is a pathname on the local system, and "title" is | 4 | # The "file" is a pathname on the local system, and "title" is |
| @@ -6,8 +6,7 @@ | |||
| 6 | # | 6 | # |
| 7 | # The file must contain kernel build output. | 7 | # The file must contain kernel build output. |
| 8 | # | 8 | # |
| 9 | # Usage: | 9 | # Usage: parse-build.sh file title |
| 10 | # sh parse-build.sh file title | ||
| 11 | # | 10 | # |
| 12 | # This program is free software; you can redistribute it and/or modify | 11 | # This program is free software; you can redistribute it and/or modify |
| 13 | # it under the terms of the GNU General Public License as published by | 12 | # it under the terms of the GNU General Public License as published by |
diff --git a/tools/testing/selftests/rcutorture/bin/parse-console.sh b/tools/testing/selftests/rcutorture/bin/parse-console.sh index 4185d4cab32e..f962ba4cf68b 100755 --- a/tools/testing/selftests/rcutorture/bin/parse-console.sh +++ b/tools/testing/selftests/rcutorture/bin/parse-console.sh | |||
| @@ -1,11 +1,10 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/bash |
| 2 | # | 2 | # |
| 3 | # Check the console output from an rcutorture run for oopses. | 3 | # Check the console output from an rcutorture run for oopses. |
| 4 | # The "file" is a pathname on the local system, and "title" is | 4 | # The "file" is a pathname on the local system, and "title" is |
| 5 | # a text string for error-message purposes. | 5 | # a text string for error-message purposes. |
| 6 | # | 6 | # |
| 7 | # Usage: | 7 | # Usage: parse-console.sh file title |
| 8 | # sh parse-console.sh file title | ||
| 9 | # | 8 | # |
| 10 | # This program is free software; you can redistribute it and/or modify | 9 | # This program is free software; you can redistribute it and/or modify |
| 11 | # it under the terms of the GNU General Public License as published by | 10 | # it under the terms of the GNU General Public License as published by |
| @@ -33,6 +32,10 @@ title="$2" | |||
| 33 | 32 | ||
| 34 | . functions.sh | 33 | . functions.sh |
| 35 | 34 | ||
| 35 | if grep -Pq '\x00' < $file | ||
| 36 | then | ||
| 37 | print_warning Console output contains nul bytes, old qemu still running? | ||
| 38 | fi | ||
| 36 | egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:' < $file | grep -v 'ODEBUG: ' | grep -v 'Warning: unable to open an initial console' > $T | 39 | egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:' < $file | grep -v 'ODEBUG: ' | grep -v 'Warning: unable to open an initial console' > $T |
| 37 | if test -s $T | 40 | if test -s $T |
| 38 | then | 41 | then |
diff --git a/tools/testing/selftests/rcutorture/bin/parse-torture.sh b/tools/testing/selftests/rcutorture/bin/parse-torture.sh index 3455560ab4e4..e3c5f0705696 100755 --- a/tools/testing/selftests/rcutorture/bin/parse-torture.sh +++ b/tools/testing/selftests/rcutorture/bin/parse-torture.sh | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | #!/bin/sh | 1 | #!/bin/bash |
| 2 | # | 2 | # |
| 3 | # Check the console output from a torture run for goodness. | 3 | # Check the console output from a torture run for goodness. |
| 4 | # The "file" is a pathname on the local system, and "title" is | 4 | # The "file" is a pathname on the local system, and "title" is |
| @@ -7,8 +7,7 @@ | |||
| 7 | # The file must contain torture output, but can be interspersed | 7 | # The file must contain torture output, but can be interspersed |
| 8 | # with other dmesg text, as in console-log output. | 8 | # with other dmesg text, as in console-log output. |
| 9 | # | 9 | # |
| 10 | # Usage: | 10 | # Usage: parse-torture.sh file title |
| 11 | # sh parse-torture.sh file title | ||
| 12 | # | 11 | # |
| 13 | # This program is free software; you can redistribute it and/or modify | 12 | # This program is free software; you can redistribute it and/or modify |
| 14 | # it under the terms of the GNU General Public License as published by | 13 | # it under the terms of the GNU General Public License as published by |
diff --git a/tools/testing/selftests/rcutorture/configs/lock/CFLIST b/tools/testing/selftests/rcutorture/configs/lock/CFLIST index a061b22d1892..6910b7370761 100644 --- a/tools/testing/selftests/rcutorture/configs/lock/CFLIST +++ b/tools/testing/selftests/rcutorture/configs/lock/CFLIST | |||
| @@ -1 +1,4 @@ | |||
| 1 | LOCK01 | 1 | LOCK01 |
| 2 | LOCK02 | ||
| 3 | LOCK03 | ||
| 4 | LOCK04 \ No newline at end of file | ||
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK02 b/tools/testing/selftests/rcutorture/configs/lock/LOCK02 new file mode 100644 index 000000000000..1d1da1477fc3 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK02 | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | CONFIG_SMP=y | ||
| 2 | CONFIG_NR_CPUS=4 | ||
| 3 | CONFIG_HOTPLUG_CPU=y | ||
| 4 | CONFIG_PREEMPT_NONE=n | ||
| 5 | CONFIG_PREEMPT_VOLUNTARY=n | ||
| 6 | CONFIG_PREEMPT=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK02.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK02.boot new file mode 100644 index 000000000000..5aa44b4f1b51 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK02.boot | |||
| @@ -0,0 +1 @@ | |||
| locktorture.torture_type=mutex_lock | |||
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK03 b/tools/testing/selftests/rcutorture/configs/lock/LOCK03 new file mode 100644 index 000000000000..1d1da1477fc3 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK03 | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | CONFIG_SMP=y | ||
| 2 | CONFIG_NR_CPUS=4 | ||
| 3 | CONFIG_HOTPLUG_CPU=y | ||
| 4 | CONFIG_PREEMPT_NONE=n | ||
| 5 | CONFIG_PREEMPT_VOLUNTARY=n | ||
| 6 | CONFIG_PREEMPT=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK03.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK03.boot new file mode 100644 index 000000000000..a67bbe0245c9 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK03.boot | |||
| @@ -0,0 +1 @@ | |||
| locktorture.torture_type=rwsem_lock | |||
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK04 b/tools/testing/selftests/rcutorture/configs/lock/LOCK04 new file mode 100644 index 000000000000..1d1da1477fc3 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK04 | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | CONFIG_SMP=y | ||
| 2 | CONFIG_NR_CPUS=4 | ||
| 3 | CONFIG_HOTPLUG_CPU=y | ||
| 4 | CONFIG_PREEMPT_NONE=n | ||
| 5 | CONFIG_PREEMPT_VOLUNTARY=n | ||
| 6 | CONFIG_PREEMPT=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/lock/LOCK04.boot b/tools/testing/selftests/rcutorture/configs/lock/LOCK04.boot new file mode 100644 index 000000000000..48c04fe47fb4 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/lock/LOCK04.boot | |||
| @@ -0,0 +1 @@ | |||
| locktorture.torture_type=rw_lock | |||
diff --git a/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh index 9746ea1cd6c7..252aae618984 100644 --- a/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh +++ b/tools/testing/selftests/rcutorture/configs/lock/ver_functions.sh | |||
| @@ -38,6 +38,6 @@ per_version_boot_params () { | |||
| 38 | echo $1 `locktorture_param_onoff "$1" "$2"` \ | 38 | echo $1 `locktorture_param_onoff "$1" "$2"` \ |
| 39 | locktorture.stat_interval=15 \ | 39 | locktorture.stat_interval=15 \ |
| 40 | locktorture.shutdown_secs=$3 \ | 40 | locktorture.shutdown_secs=$3 \ |
| 41 | locktorture.locktorture_runnable=1 \ | 41 | locktorture.torture_runnable=1 \ |
| 42 | locktorture.verbose=1 | 42 | locktorture.verbose=1 |
| 43 | } | 43 | } |
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/CFLIST b/tools/testing/selftests/rcutorture/configs/rcu/CFLIST index cd3d29cb0a47..a3a1a05a2b5c 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/CFLIST +++ b/tools/testing/selftests/rcutorture/configs/rcu/CFLIST | |||
| @@ -11,3 +11,6 @@ SRCU-N | |||
| 11 | SRCU-P | 11 | SRCU-P |
| 12 | TINY01 | 12 | TINY01 |
| 13 | TINY02 | 13 | TINY02 |
| 14 | TASKS01 | ||
| 15 | TASKS02 | ||
| 16 | TASKS03 | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS01 b/tools/testing/selftests/rcutorture/configs/rcu/TASKS01 new file mode 100644 index 000000000000..97f0a0b27ef7 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS01 | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | CONFIG_SMP=y | ||
| 2 | CONFIG_NR_CPUS=2 | ||
| 3 | CONFIG_HOTPLUG_CPU=y | ||
| 4 | CONFIG_PREEMPT_NONE=n | ||
| 5 | CONFIG_PREEMPT_VOLUNTARY=n | ||
| 6 | CONFIG_PREEMPT=y | ||
| 7 | CONFIG_DEBUG_LOCK_ALLOC=y | ||
| 8 | CONFIG_PROVE_RCU=y | ||
| 9 | CONFIG_TASKS_RCU=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS01.boot b/tools/testing/selftests/rcutorture/configs/rcu/TASKS01.boot new file mode 100644 index 000000000000..cd2a188eeb6d --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS01.boot | |||
| @@ -0,0 +1 @@ | |||
| rcutorture.torture_type=tasks | |||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS02 b/tools/testing/selftests/rcutorture/configs/rcu/TASKS02 new file mode 100644 index 000000000000..696d2ea74d13 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS02 | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | CONFIG_SMP=n | ||
| 2 | CONFIG_PREEMPT_NONE=y | ||
| 3 | CONFIG_PREEMPT_VOLUNTARY=n | ||
| 4 | CONFIG_PREEMPT=n | ||
| 5 | CONFIG_TASKS_RCU=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS02.boot b/tools/testing/selftests/rcutorture/configs/rcu/TASKS02.boot new file mode 100644 index 000000000000..cd2a188eeb6d --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS02.boot | |||
| @@ -0,0 +1 @@ | |||
| rcutorture.torture_type=tasks | |||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS03 b/tools/testing/selftests/rcutorture/configs/rcu/TASKS03 new file mode 100644 index 000000000000..9c60da5b5d1d --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS03 | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | CONFIG_SMP=y | ||
| 2 | CONFIG_NR_CPUS=2 | ||
| 3 | CONFIG_HOTPLUG_CPU=n | ||
| 4 | CONFIG_SUSPEND=n | ||
| 5 | CONFIG_HIBERNATION=n | ||
| 6 | CONFIG_PREEMPT_NONE=n | ||
| 7 | CONFIG_PREEMPT_VOLUNTARY=n | ||
| 8 | CONFIG_PREEMPT=y | ||
| 9 | CONFIG_TASKS_RCU=y | ||
| 10 | CONFIG_HZ_PERIODIC=n | ||
| 11 | CONFIG_NO_HZ_IDLE=n | ||
| 12 | CONFIG_NO_HZ_FULL=y | ||
| 13 | CONFIG_NO_HZ_FULL_ALL=y | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TASKS03.boot b/tools/testing/selftests/rcutorture/configs/rcu/TASKS03.boot new file mode 100644 index 000000000000..cd2a188eeb6d --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/TASKS03.boot | |||
| @@ -0,0 +1 @@ | |||
| rcutorture.torture_type=tasks | |||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01 b/tools/testing/selftests/rcutorture/configs/rcu/TREE01 index 063b7079c621..38e3895759dd 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01 | |||
| @@ -1,5 +1,4 @@ | |||
| 1 | CONFIG_SMP=y | 1 | CONFIG_SMP=y |
| 2 | CONFIG_NR_CPUS=8 | ||
| 3 | CONFIG_PREEMPT_NONE=n | 2 | CONFIG_PREEMPT_NONE=n |
| 4 | CONFIG_PREEMPT_VOLUNTARY=n | 3 | CONFIG_PREEMPT_VOLUNTARY=n |
| 5 | CONFIG_PREEMPT=y | 4 | CONFIG_PREEMPT=y |
| @@ -10,8 +9,7 @@ CONFIG_NO_HZ_FULL=n | |||
| 10 | CONFIG_RCU_FAST_NO_HZ=y | 9 | CONFIG_RCU_FAST_NO_HZ=y |
| 11 | CONFIG_RCU_TRACE=y | 10 | CONFIG_RCU_TRACE=y |
| 12 | CONFIG_HOTPLUG_CPU=y | 11 | CONFIG_HOTPLUG_CPU=y |
| 13 | CONFIG_RCU_FANOUT=8 | 12 | CONFIG_MAXSMP=y |
| 14 | CONFIG_RCU_FANOUT_EXACT=n | ||
| 15 | CONFIG_RCU_NOCB_CPU=y | 13 | CONFIG_RCU_NOCB_CPU=y |
| 16 | CONFIG_RCU_NOCB_CPU_ZERO=y | 14 | CONFIG_RCU_NOCB_CPU_ZERO=y |
| 17 | CONFIG_DEBUG_LOCK_ALLOC=n | 15 | CONFIG_DEBUG_LOCK_ALLOC=n |
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot index 0fc8a3428938..adc3abc82fb8 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot | |||
| @@ -1 +1 @@ | |||
| rcutorture.torture_type=rcu_bh | rcutorture.torture_type=rcu_bh maxcpus=8 | ||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE07 b/tools/testing/selftests/rcutorture/configs/rcu/TREE07 index ab6225506909..8f1017666aa7 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE07 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE07 | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | CONFIG_SMP=y | 1 | CONFIG_SMP=y |
| 2 | CONFIG_NR_CPUS=16 | 2 | CONFIG_NR_CPUS=16 |
| 3 | CONFIG_CPUMASK_OFFSTACK=y | ||
| 3 | CONFIG_PREEMPT_NONE=y | 4 | CONFIG_PREEMPT_NONE=y |
| 4 | CONFIG_PREEMPT_VOLUNTARY=n | 5 | CONFIG_PREEMPT_VOLUNTARY=n |
| 5 | CONFIG_PREEMPT=n | 6 | CONFIG_PREEMPT=n |
| @@ -7,7 +8,7 @@ CONFIG_PREEMPT=n | |||
| 7 | CONFIG_HZ_PERIODIC=n | 8 | CONFIG_HZ_PERIODIC=n |
| 8 | CONFIG_NO_HZ_IDLE=n | 9 | CONFIG_NO_HZ_IDLE=n |
| 9 | CONFIG_NO_HZ_FULL=y | 10 | CONFIG_NO_HZ_FULL=y |
| 10 | CONFIG_NO_HZ_FULL_ALL=y | 11 | CONFIG_NO_HZ_FULL_ALL=n |
| 11 | CONFIG_NO_HZ_FULL_SYSIDLE=y | 12 | CONFIG_NO_HZ_FULL_SYSIDLE=y |
| 12 | CONFIG_RCU_FAST_NO_HZ=n | 13 | CONFIG_RCU_FAST_NO_HZ=n |
| 13 | CONFIG_RCU_TRACE=y | 14 | CONFIG_RCU_TRACE=y |
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE07.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE07.boot new file mode 100644 index 000000000000..d44609937503 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE07.boot | |||
| @@ -0,0 +1 @@ | |||
| nohz_full=2-9 | |||
diff --git a/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh b/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh index 8977d8d31b19..ffb85ed786fa 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh +++ b/tools/testing/selftests/rcutorture/configs/rcu/ver_functions.sh | |||
| @@ -51,7 +51,7 @@ per_version_boot_params () { | |||
| 51 | `rcutorture_param_n_barrier_cbs "$1"` \ | 51 | `rcutorture_param_n_barrier_cbs "$1"` \ |
| 52 | rcutorture.stat_interval=15 \ | 52 | rcutorture.stat_interval=15 \ |
| 53 | rcutorture.shutdown_secs=$3 \ | 53 | rcutorture.shutdown_secs=$3 \ |
| 54 | rcutorture.rcutorture_runnable=1 \ | 54 | rcutorture.torture_runnable=1 \ |
| 55 | rcutorture.test_no_idle_hz=1 \ | 55 | rcutorture.test_no_idle_hz=1 \ |
| 56 | rcutorture.verbose=1 | 56 | rcutorture.verbose=1 |
| 57 | } | 57 | } |
diff --git a/tools/testing/selftests/rcutorture/doc/initrd.txt b/tools/testing/selftests/rcutorture/doc/initrd.txt index 49d134c25c04..4170e714f044 100644 --- a/tools/testing/selftests/rcutorture/doc/initrd.txt +++ b/tools/testing/selftests/rcutorture/doc/initrd.txt | |||
| @@ -6,6 +6,7 @@ this case. There are probably much better ways of doing this. | |||
| 6 | That said, here are the commands: | 6 | That said, here are the commands: |
| 7 | 7 | ||
| 8 | ------------------------------------------------------------------------ | 8 | ------------------------------------------------------------------------ |
| 9 | cd tools/testing/selftests/rcutorture | ||
| 9 | zcat /initrd.img > /tmp/initrd.img.zcat | 10 | zcat /initrd.img > /tmp/initrd.img.zcat |
| 10 | mkdir initrd | 11 | mkdir initrd |
| 11 | cd initrd | 12 | cd initrd |
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index 3f94e1afd6cf..4c4b1f631ecf 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | CC = $(CROSS_COMPILE)gcc | 3 | CC = $(CROSS_COMPILE)gcc |
| 4 | CFLAGS = -Wall | 4 | CFLAGS = -Wall |
| 5 | BINARIES = hugepage-mmap hugepage-shm map_hugetlb thuge-gen hugetlbfstest | 5 | BINARIES = hugepage-mmap hugepage-shm map_hugetlb thuge-gen hugetlbfstest |
| 6 | BINARIES += transhuge-stress | ||
| 6 | 7 | ||
| 7 | all: $(BINARIES) | 8 | all: $(BINARIES) |
| 8 | %: %.c | 9 | %: %.c |
diff --git a/tools/testing/selftests/vm/transhuge-stress.c b/tools/testing/selftests/vm/transhuge-stress.c new file mode 100644 index 000000000000..fd7f1b4a96f9 --- /dev/null +++ b/tools/testing/selftests/vm/transhuge-stress.c | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | /* | ||
| 2 | * Stress test for transparent huge pages, memory compaction and migration. | ||
| 3 | * | ||
| 4 | * Authors: Konstantin Khlebnikov <koct9i@gmail.com> | ||
| 5 | * | ||
| 6 | * This is free and unencumbered software released into the public domain. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <stdlib.h> | ||
| 10 | #include <stdio.h> | ||
| 11 | #include <stdint.h> | ||
| 12 | #include <err.h> | ||
| 13 | #include <time.h> | ||
| 14 | #include <unistd.h> | ||
| 15 | #include <fcntl.h> | ||
| 16 | #include <string.h> | ||
| 17 | #include <sys/mman.h> | ||
| 18 | |||
| 19 | #define PAGE_SHIFT 12 | ||
| 20 | #define HPAGE_SHIFT 21 | ||
| 21 | |||
| 22 | #define PAGE_SIZE (1 << PAGE_SHIFT) | ||
| 23 | #define HPAGE_SIZE (1 << HPAGE_SHIFT) | ||
| 24 | |||
| 25 | #define PAGEMAP_PRESENT(ent) (((ent) & (1ull << 63)) != 0) | ||
| 26 | #define PAGEMAP_PFN(ent) ((ent) & ((1ull << 55) - 1)) | ||
| 27 | |||
| 28 | int pagemap_fd; | ||
| 29 | |||
| 30 | int64_t allocate_transhuge(void *ptr) | ||
| 31 | { | ||
| 32 | uint64_t ent[2]; | ||
| 33 | |||
| 34 | /* drop pmd */ | ||
| 35 | if (mmap(ptr, HPAGE_SIZE, PROT_READ | PROT_WRITE, | ||
| 36 | MAP_FIXED | MAP_ANONYMOUS | | ||
| 37 | MAP_NORESERVE | MAP_PRIVATE, -1, 0) != ptr) | ||
| 38 | errx(2, "mmap transhuge"); | ||
| 39 | |||
| 40 | if (madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE)) | ||
| 41 | err(2, "MADV_HUGEPAGE"); | ||
| 42 | |||
| 43 | /* allocate transparent huge page */ | ||
| 44 | *(volatile void **)ptr = ptr; | ||
| 45 | |||
| 46 | if (pread(pagemap_fd, ent, sizeof(ent), | ||
| 47 | (uintptr_t)ptr >> (PAGE_SHIFT - 3)) != sizeof(ent)) | ||
| 48 | err(2, "read pagemap"); | ||
| 49 | |||
| 50 | if (PAGEMAP_PRESENT(ent[0]) && PAGEMAP_PRESENT(ent[1]) && | ||
| 51 | PAGEMAP_PFN(ent[0]) + 1 == PAGEMAP_PFN(ent[1]) && | ||
| 52 | !(PAGEMAP_PFN(ent[0]) & ((1 << (HPAGE_SHIFT - PAGE_SHIFT)) - 1))) | ||
| 53 | return PAGEMAP_PFN(ent[0]); | ||
| 54 | |||
| 55 | return -1; | ||
| 56 | } | ||
| 57 | |||
| 58 | int main(int argc, char **argv) | ||
| 59 | { | ||
| 60 | size_t ram, len; | ||
| 61 | void *ptr, *p; | ||
| 62 | struct timespec a, b; | ||
| 63 | double s; | ||
| 64 | uint8_t *map; | ||
| 65 | size_t map_len; | ||
| 66 | |||
| 67 | ram = sysconf(_SC_PHYS_PAGES); | ||
| 68 | if (ram > SIZE_MAX / sysconf(_SC_PAGESIZE) / 4) | ||
| 69 | ram = SIZE_MAX / 4; | ||
| 70 | else | ||
| 71 | ram *= sysconf(_SC_PAGESIZE); | ||
| 72 | |||
| 73 | if (argc == 1) | ||
| 74 | len = ram; | ||
| 75 | else if (!strcmp(argv[1], "-h")) | ||
| 76 | errx(1, "usage: %s [size in MiB]", argv[0]); | ||
| 77 | else | ||
| 78 | len = atoll(argv[1]) << 20; | ||
| 79 | |||
| 80 | warnx("allocate %zd transhuge pages, using %zd MiB virtual memory" | ||
| 81 | " and %zd MiB of ram", len >> HPAGE_SHIFT, len >> 20, | ||
| 82 | len >> (20 + HPAGE_SHIFT - PAGE_SHIFT - 1)); | ||
| 83 | |||
| 84 | pagemap_fd = open("/proc/self/pagemap", O_RDONLY); | ||
| 85 | if (pagemap_fd < 0) | ||
| 86 | err(2, "open pagemap"); | ||
| 87 | |||
| 88 | len -= len % HPAGE_SIZE; | ||
| 89 | ptr = mmap(NULL, len + HPAGE_SIZE, PROT_READ | PROT_WRITE, | ||
| 90 | MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0); | ||
| 91 | if (ptr == MAP_FAILED) | ||
| 92 | err(2, "initial mmap"); | ||
| 93 | ptr += HPAGE_SIZE - (uintptr_t)ptr % HPAGE_SIZE; | ||
| 94 | |||
| 95 | if (madvise(ptr, len, MADV_HUGEPAGE)) | ||
| 96 | err(2, "MADV_HUGEPAGE"); | ||
| 97 | |||
| 98 | map_len = ram >> (HPAGE_SHIFT - 1); | ||
| 99 | map = malloc(map_len); | ||
| 100 | if (!map) | ||
| 101 | errx(2, "map malloc"); | ||
| 102 | |||
| 103 | while (1) { | ||
| 104 | int nr_succeed = 0, nr_failed = 0, nr_pages = 0; | ||
| 105 | |||
| 106 | memset(map, 0, map_len); | ||
| 107 | |||
| 108 | clock_gettime(CLOCK_MONOTONIC, &a); | ||
| 109 | for (p = ptr; p < ptr + len; p += HPAGE_SIZE) { | ||
| 110 | int64_t pfn; | ||
| 111 | |||
| 112 | pfn = allocate_transhuge(p); | ||
| 113 | |||
| 114 | if (pfn < 0) { | ||
| 115 | nr_failed++; | ||
| 116 | } else { | ||
| 117 | size_t idx = pfn >> (HPAGE_SHIFT - PAGE_SHIFT); | ||
| 118 | |||
| 119 | nr_succeed++; | ||
| 120 | if (idx >= map_len) { | ||
| 121 | map = realloc(map, idx + 1); | ||
| 122 | if (!map) | ||
| 123 | errx(2, "map realloc"); | ||
| 124 | memset(map + map_len, 0, idx + 1 - map_len); | ||
| 125 | map_len = idx + 1; | ||
| 126 | } | ||
| 127 | if (!map[idx]) | ||
| 128 | nr_pages++; | ||
| 129 | map[idx] = 1; | ||
| 130 | } | ||
| 131 | |||
| 132 | /* split transhuge page, keep last page */ | ||
| 133 | if (madvise(p, HPAGE_SIZE - PAGE_SIZE, MADV_DONTNEED)) | ||
| 134 | err(2, "MADV_DONTNEED"); | ||
| 135 | } | ||
| 136 | clock_gettime(CLOCK_MONOTONIC, &b); | ||
| 137 | s = b.tv_sec - a.tv_sec + (b.tv_nsec - a.tv_nsec) / 1000000000.; | ||
| 138 | |||
| 139 | warnx("%.3f s/loop, %.3f ms/page, %10.3f MiB/s\t" | ||
| 140 | "%4d succeed, %4d failed, %4d different pages", | ||
| 141 | s, s * 1000 / (len >> HPAGE_SHIFT), len / s / (1 << 20), | ||
| 142 | nr_succeed, nr_failed, nr_pages); | ||
| 143 | } | ||
| 144 | } | ||
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index c4d6d2e20e0d..264fbc297e0b 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c | |||
| @@ -132,6 +132,7 @@ static const char * const page_flag_names[] = { | |||
| 132 | [KPF_NOPAGE] = "n:nopage", | 132 | [KPF_NOPAGE] = "n:nopage", |
| 133 | [KPF_KSM] = "x:ksm", | 133 | [KPF_KSM] = "x:ksm", |
| 134 | [KPF_THP] = "t:thp", | 134 | [KPF_THP] = "t:thp", |
| 135 | [KPF_BALLOON] = "o:balloon", | ||
| 135 | 136 | ||
| 136 | [KPF_RESERVED] = "r:reserved", | 137 | [KPF_RESERVED] = "r:reserved", |
| 137 | [KPF_MLOCKED] = "m:mlocked", | 138 | [KPF_MLOCKED] = "m:mlocked", |
