diff options
| author | Jiri Kosina <jkosina@suse.cz> | 2014-11-20 08:42:02 -0500 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2014-11-20 08:42:02 -0500 |
| commit | a02001086bbfb4da35d1228bebc2f1b442db455f (patch) | |
| tree | 62ab47936cef06fd08657ca5b6cd1df98c19be57 /tools | |
| parent | eff264efeeb0898408e8c9df72d8a32621035bed (diff) | |
| parent | fc14f9c1272f62c3e8d01300f52467c0d9af50f9 (diff) | |
Merge Linus' tree to be be to apply submitted patches to newer code than
current trivial.git base
Diffstat (limited to 'tools')
270 files changed, 14089 insertions, 2277 deletions
diff --git a/tools/include/tools/endian.h b/tools/include/tools/endian.h new file mode 100644 index 000000000000..8001194008da --- /dev/null +++ b/tools/include/tools/endian.h | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | #ifndef _TOOLS_ENDIAN_H | ||
| 2 | #define _TOOLS_ENDIAN_H | ||
| 3 | |||
| 4 | #include <byteswap.h> | ||
| 5 | |||
| 6 | #if __BYTE_ORDER == __LITTLE_ENDIAN | ||
| 7 | |||
| 8 | #ifndef htole16 | ||
| 9 | #define htole16(x) (x) | ||
| 10 | #endif | ||
| 11 | #ifndef htole32 | ||
| 12 | #define htole32(x) (x) | ||
| 13 | #endif | ||
| 14 | #ifndef htole64 | ||
| 15 | #define htole64(x) (x) | ||
| 16 | #endif | ||
| 17 | |||
| 18 | #ifndef le16toh | ||
| 19 | #define le16toh(x) (x) | ||
| 20 | #endif | ||
| 21 | |||
| 22 | #ifndef le32toh | ||
| 23 | #define le32toh(x) (x) | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #ifndef le64toh | ||
| 27 | #define le64toh(x) (x) | ||
| 28 | #endif | ||
| 29 | |||
| 30 | #else /* __BYTE_ORDER */ | ||
| 31 | |||
| 32 | #ifndef htole16 | ||
| 33 | #define htole16(x) __bswap_16(x) | ||
| 34 | #endif | ||
| 35 | #ifndef htole32 | ||
| 36 | #define htole32(x) __bswap_32(x) | ||
| 37 | #endif | ||
| 38 | #ifndef htole64 | ||
| 39 | #define htole64(x) __bswap_64(x) | ||
| 40 | #endif | ||
| 41 | |||
| 42 | #ifndef le16toh | ||
| 43 | #define le16toh(x) __bswap_16(x) | ||
| 44 | #endif | ||
| 45 | |||
| 46 | #ifndef le32toh | ||
| 47 | #define le32toh(x) __bswap_32(x) | ||
| 48 | #endif | ||
| 49 | |||
| 50 | #ifndef le64toh | ||
| 51 | #define le64toh(x) __bswap_64(x) | ||
| 52 | #endif | ||
| 53 | |||
| 54 | #endif | ||
| 55 | |||
| 56 | #endif /* _TOOLS_ENDIAN_H */ | ||
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-diff.txt b/tools/perf/Documentation/perf-diff.txt index b3b8abae62b8..e463caa3eb49 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
| @@ -196,10 +196,10 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as: | |||
| 196 | 196 | ||
| 197 | - period being the hist entry period value | 197 | - period being the hist entry period value |
| 198 | 198 | ||
| 199 | - WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option | 199 | - WEIGHT-A/WEIGHT-B being user supplied weights in the the '-c' option |
| 200 | behind ':' separator like '-c wdiff:1,2'. | 200 | behind ':' separator like '-c wdiff:1,2'. |
| 201 | - WIEGHT-A being the weight of the data file | 201 | - WEIGHT-A being the weight of the data file |
| 202 | - WIEGHT-B being the weight of the baseline data file | 202 | - WEIGHT-B being the weight of the baseline data file |
| 203 | 203 | ||
| 204 | SEE ALSO | 204 | SEE ALSO |
| 205 | -------- | 205 | -------- |
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index 6e689dc89a2f..6252e776009c 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt | |||
| @@ -100,7 +100,7 @@ OPTIONS | |||
| 100 | STAT REPORT OPTIONS | 100 | STAT REPORT OPTIONS |
| 101 | ------------------- | 101 | ------------------- |
| 102 | --vcpu=<value>:: | 102 | --vcpu=<value>:: |
| 103 | analyze events which occures on this vcpu. (default: all vcpus) | 103 | analyze events which occur on this vcpu. (default: all vcpus) |
| 104 | 104 | ||
| 105 | --event=<value>:: | 105 | --event=<value>:: |
| 106 | event to be analyzed. Possible values: vmexit, mmio (x86 only), | 106 | event to be analyzed. Possible values: vmexit, mmio (x86 only), |
| @@ -134,7 +134,7 @@ STAT LIVE OPTIONS | |||
| 134 | Analyze events only for given process ID(s) (comma separated list). | 134 | Analyze events only for given process ID(s) (comma separated list). |
| 135 | 135 | ||
| 136 | --vcpu=<value>:: | 136 | --vcpu=<value>:: |
| 137 | analyze events which occures on this vcpu. (default: all vcpus) | 137 | analyze events which occur on this vcpu. (default: all vcpus) |
| 138 | 138 | ||
| 139 | 139 | ||
| 140 | --event=<value>:: | 140 | --event=<value>:: |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 6fce6a622206..cbb4f743d921 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
| @@ -19,7 +19,7 @@ various perf commands with the -e option. | |||
| 19 | EVENT MODIFIERS | 19 | EVENT MODIFIERS |
| 20 | --------------- | 20 | --------------- |
| 21 | 21 | ||
| 22 | Events can optionally have a modifer by appending a colon and one or | 22 | Events can optionally have a modifier by appending a colon and one or |
| 23 | more modifiers. Modifiers allow the user to restrict the events to be | 23 | more modifiers. Modifiers allow the user to restrict the events to be |
| 24 | counted. The following modifiers exist: | 24 | counted. The following modifiers exist: |
| 25 | 25 | ||
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-record.txt b/tools/perf/Documentation/perf-record.txt index d460049cae8e..398f8d53bd6d 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
| @@ -146,7 +146,7 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. | |||
| 146 | 146 | ||
| 147 | -N:: | 147 | -N:: |
| 148 | --no-buildid-cache:: | 148 | --no-buildid-cache:: |
| 149 | Do not update the builid cache. This saves some overhead in situations | 149 | Do not update the buildid cache. This saves some overhead in situations |
| 150 | where the information in the perf.data file (which includes buildids) | 150 | where the information in the perf.data file (which includes buildids) |
| 151 | is sufficient. | 151 | is sufficient. |
| 152 | 152 | ||
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-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt index d00bef231340..dfbb506d2c34 100644 --- a/tools/perf/Documentation/perf-script-perl.txt +++ b/tools/perf/Documentation/perf-script-perl.txt | |||
| @@ -181,8 +181,8 @@ strings for flag and symbolic fields. These correspond to the strings | |||
| 181 | and values parsed from the 'print fmt' fields of the event format | 181 | and values parsed from the 'print fmt' fields of the event format |
| 182 | files: | 182 | files: |
| 183 | 183 | ||
| 184 | flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name | 184 | flag_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the flag field $field_name of event $event_name |
| 185 | symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name | 185 | symbol_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the symbolic field $field_name of event $event_name |
| 186 | 186 | ||
| 187 | Perf::Trace::Context Module | 187 | Perf::Trace::Context Module |
| 188 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 188 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt index 9f1f054b8432..54acba221558 100644 --- a/tools/perf/Documentation/perf-script-python.txt +++ b/tools/perf/Documentation/perf-script-python.txt | |||
| @@ -263,7 +263,7 @@ and having the counts we've tallied as values. | |||
| 263 | 263 | ||
| 264 | The print_syscall_totals() function iterates over the entries in the | 264 | The print_syscall_totals() function iterates over the entries in the |
| 265 | dictionary and displays a line for each entry containing the syscall | 265 | dictionary and displays a line for each entry containing the syscall |
| 266 | name (the dictonary keys contain the syscall ids, which are passed to | 266 | name (the dictionary keys contain the syscall ids, which are passed to |
| 267 | the Util function syscall_name(), which translates the raw syscall | 267 | the Util function syscall_name(), which translates the raw syscall |
| 268 | numbers to the corresponding syscall name strings). The output is | 268 | numbers to the corresponding syscall name strings). The output is |
| 269 | displayed after all the events in the trace have been processed, by | 269 | displayed after all the events in the trace have been processed, by |
| @@ -576,8 +576,8 @@ strings for flag and symbolic fields. These correspond to the strings | |||
| 576 | and values parsed from the 'print fmt' fields of the event format | 576 | and values parsed from the 'print fmt' fields of the event format |
| 577 | files: | 577 | files: |
| 578 | 578 | ||
| 579 | flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name | 579 | flag_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the flag field field_name of event event_name |
| 580 | symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name | 580 | symbol_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the symbolic field field_name of event event_name |
| 581 | 581 | ||
| 582 | The *autodict* function returns a special kind of Python | 582 | The *autodict* function returns a special kind of Python |
| 583 | dictionary that implements Perl's 'autovivifying' hashes in Python | 583 | dictionary that implements Perl's 'autovivifying' hashes in Python |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 05f9a0a6784c..21494806c0ab 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
| @@ -115,7 +115,7 @@ OPTIONS | |||
| 115 | -f:: | 115 | -f:: |
| 116 | --fields:: | 116 | --fields:: |
| 117 | Comma separated list of fields to print. Options are: | 117 | Comma separated list of fields to print. Options are: |
| 118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline. | 118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff, srcline, period. |
| 119 | Field list can be prepended with the type, trace, sw or hw, | 119 | Field list can be prepended with the type, trace, sw or hw, |
| 120 | to indicate to which event type the field list applies. | 120 | to indicate to which event type the field list applies. |
| 121 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace | 121 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace |
| @@ -140,7 +140,7 @@ OPTIONS | |||
| 140 | 140 | ||
| 141 | "Overriding previous field request for all events." | 141 | "Overriding previous field request for all events." |
| 142 | 142 | ||
| 143 | Alternativey, consider the order: | 143 | Alternatively, consider the order: |
| 144 | 144 | ||
| 145 | -f comm,tid,time,ip,sym -f trace: | 145 | -f comm,tid,time,ip,sym -f trace: |
| 146 | 146 | ||
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index d1d3e5121f89..31a5c3ea7f74 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt | |||
| @@ -25,7 +25,7 @@ OPTIONS | |||
| 25 | ------- | 25 | ------- |
| 26 | -s:: | 26 | -s:: |
| 27 | --skip:: | 27 | --skip:: |
| 28 | Tests to skip (comma separater numeric list). | 28 | Tests to skip (comma separated numeric list). |
| 29 | 29 | ||
| 30 | -v:: | 30 | -v:: |
| 31 | --verbose:: | 31 | --verbose:: |
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/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 02aac831bdd9..7e1b1f2bb83c 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
| @@ -20,7 +20,7 @@ scheduling events, etc. | |||
| 20 | This is a live mode tool in addition to working with perf.data files like | 20 | This is a live mode tool in addition to working with perf.data files like |
| 21 | the other perf tools. Files can be generated using the 'perf record' command | 21 | the other perf tools. Files can be generated using the 'perf record' command |
| 22 | but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*'). | 22 | but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*'). |
| 23 | Alernatively, the 'perf trace record' can be used as a shortcut to | 23 | Alternatively, 'perf trace record' can be used as a shortcut to |
| 24 | automatically include the raw_syscalls events when writing events to a file. | 24 | automatically include the raw_syscalls events when writing events to a file. |
| 25 | 25 | ||
| 26 | The following options apply to perf trace; options to perf trace record are | 26 | The following options apply to perf trace; options to perf trace record are |
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..e7417fe97a97 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; |
| @@ -50,6 +51,7 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, | |||
| 50 | struct addr_location *al, | 51 | struct addr_location *al, |
| 51 | struct perf_annotate *ann) | 52 | struct perf_annotate *ann) |
| 52 | { | 53 | { |
| 54 | struct hists *hists = evsel__hists(evsel); | ||
| 53 | struct hist_entry *he; | 55 | struct hist_entry *he; |
| 54 | int ret; | 56 | int ret; |
| 55 | 57 | ||
| @@ -65,13 +67,12 @@ static int perf_evsel__add_sample(struct perf_evsel *evsel, | |||
| 65 | return 0; | 67 | return 0; |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | he = __hists__add_entry(&evsel->hists, al, NULL, NULL, NULL, 1, 1, 0, | 70 | he = __hists__add_entry(hists, al, NULL, NULL, NULL, 1, 1, 0, true); |
| 69 | true); | ||
| 70 | if (he == NULL) | 71 | if (he == NULL) |
| 71 | return -ENOMEM; | 72 | return -ENOMEM; |
| 72 | 73 | ||
| 73 | ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); | 74 | ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr); |
| 74 | hists__inc_nr_samples(&evsel->hists, true); | 75 | hists__inc_nr_samples(hists, true); |
| 75 | return ret; | 76 | return ret; |
| 76 | } | 77 | } |
| 77 | 78 | ||
| @@ -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,23 @@ 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 | perf_evlist__fprintf_nr_events(session->evlist, stdout); |
| 218 | goto out; | ||
| 226 | } | 219 | } |
| 227 | 220 | ||
| 228 | if (verbose > 3) | 221 | if (verbose > 3) |
| @@ -233,7 +226,7 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
| 233 | 226 | ||
| 234 | total_nr_samples = 0; | 227 | total_nr_samples = 0; |
| 235 | evlist__for_each(session->evlist, pos) { | 228 | evlist__for_each(session->evlist, pos) { |
| 236 | struct hists *hists = &pos->hists; | 229 | struct hists *hists = evsel__hists(pos); |
| 237 | u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 230 | u32 nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
| 238 | 231 | ||
| 239 | if (nr_samples > 0) { | 232 | if (nr_samples > 0) { |
| @@ -250,8 +243,8 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
| 250 | } | 243 | } |
| 251 | 244 | ||
| 252 | if (total_nr_samples == 0) { | 245 | if (total_nr_samples == 0) { |
| 253 | ui__error("The %s file has no samples!\n", file.path); | 246 | ui__error("The %s file has no samples!\n", session->file->path); |
| 254 | goto out_delete; | 247 | goto out; |
| 255 | } | 248 | } |
| 256 | 249 | ||
| 257 | if (use_browser == 2) { | 250 | if (use_browser == 2) { |
| @@ -261,24 +254,12 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
| 261 | "perf_gtk__show_annotations"); | 254 | "perf_gtk__show_annotations"); |
| 262 | if (show_annotations == NULL) { | 255 | if (show_annotations == NULL) { |
| 263 | ui__error("GTK browser not found!\n"); | 256 | ui__error("GTK browser not found!\n"); |
| 264 | goto out_delete; | 257 | goto out; |
| 265 | } | 258 | } |
| 266 | show_annotations(); | 259 | show_annotations(); |
| 267 | } | 260 | } |
| 268 | 261 | ||
| 269 | out_delete: | 262 | 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; | 263 | return ret; |
| 283 | } | 264 | } |
| 284 | 265 | ||
| @@ -297,10 +278,14 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 297 | .comm = perf_event__process_comm, | 278 | .comm = perf_event__process_comm, |
| 298 | .exit = perf_event__process_exit, | 279 | .exit = perf_event__process_exit, |
| 299 | .fork = perf_event__process_fork, | 280 | .fork = perf_event__process_fork, |
| 300 | .ordered_samples = true, | 281 | .ordered_events = true, |
| 301 | .ordering_requires_timestamps = true, | 282 | .ordering_requires_timestamps = true, |
| 302 | }, | 283 | }, |
| 303 | }; | 284 | }; |
| 285 | struct perf_data_file file = { | ||
| 286 | .path = input_name, | ||
| 287 | .mode = PERF_DATA_MODE_READ, | ||
| 288 | }; | ||
| 304 | const struct option options[] = { | 289 | const struct option options[] = { |
| 305 | OPT_STRING('i', "input", &input_name, "file", | 290 | OPT_STRING('i', "input", &input_name, "file", |
| 306 | "input file name"), | 291 | "input file name"), |
| @@ -308,7 +293,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 308 | "only consider symbols in these dsos"), | 293 | "only consider symbols in these dsos"), |
| 309 | OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol", | 294 | OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol", |
| 310 | "symbol to annotate"), | 295 | "symbol to annotate"), |
| 311 | OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"), | 296 | OPT_BOOLEAN('f', "force", &file.force, "don't complain, do it"), |
| 312 | OPT_INCR('v', "verbose", &verbose, | 297 | OPT_INCR('v', "verbose", &verbose, |
| 313 | "be more verbose (show symbol address, etc)"), | 298 | "be more verbose (show symbol address, etc)"), |
| 314 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 299 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
| @@ -341,6 +326,10 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 341 | "Show event group information together"), | 326 | "Show event group information together"), |
| 342 | OPT_END() | 327 | OPT_END() |
| 343 | }; | 328 | }; |
| 329 | int ret = hists__init(); | ||
| 330 | |||
| 331 | if (ret < 0) | ||
| 332 | return ret; | ||
| 344 | 333 | ||
| 345 | argc = parse_options(argc, argv, options, annotate_usage, 0); | 334 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
| 346 | 335 | ||
| @@ -353,11 +342,16 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 353 | 342 | ||
| 354 | setup_browser(true); | 343 | setup_browser(true); |
| 355 | 344 | ||
| 345 | annotate.session = perf_session__new(&file, false, &annotate.tool); | ||
| 346 | if (annotate.session == NULL) | ||
| 347 | return -1; | ||
| 348 | |||
| 356 | symbol_conf.priv_size = sizeof(struct annotation); | 349 | symbol_conf.priv_size = sizeof(struct annotation); |
| 357 | symbol_conf.try_vmlinux_path = true; | 350 | symbol_conf.try_vmlinux_path = true; |
| 358 | 351 | ||
| 359 | if (symbol__init() < 0) | 352 | ret = symbol__init(&annotate.session->header.env); |
| 360 | return -1; | 353 | if (ret < 0) |
| 354 | goto out_delete; | ||
| 361 | 355 | ||
| 362 | if (setup_sorting() < 0) | 356 | if (setup_sorting() < 0) |
| 363 | usage_with_options(annotate_usage, options); | 357 | usage_with_options(annotate_usage, options); |
| @@ -373,5 +367,20 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 373 | annotate.sym_hist_filter = argv[0]; | 367 | annotate.sym_hist_filter = argv[0]; |
| 374 | } | 368 | } |
| 375 | 369 | ||
| 376 | return __cmd_annotate(&annotate); | 370 | ret = __cmd_annotate(&annotate); |
| 371 | |||
| 372 | out_delete: | ||
| 373 | /* | ||
| 374 | * Speed up the exit process, for large files this can | ||
| 375 | * take quite a while. | ||
| 376 | * | ||
| 377 | * XXX Enable this when using valgrind or if we ever | ||
| 378 | * librarize this command. | ||
| 379 | * | ||
| 380 | * Also experiment with obstacks to see how much speed | ||
| 381 | * up we'll get here. | ||
| 382 | * | ||
| 383 | * perf_session__delete(session); | ||
| 384 | */ | ||
| 385 | return ret; | ||
| 377 | } | 386 | } |
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..25114c9a6801 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
| @@ -327,6 +327,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
| 327 | struct machine *machine) | 327 | struct machine *machine) |
| 328 | { | 328 | { |
| 329 | struct addr_location al; | 329 | struct addr_location al; |
| 330 | struct hists *hists = evsel__hists(evsel); | ||
| 330 | 331 | ||
| 331 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | 332 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
| 332 | pr_warning("problem processing %d event, skipping it.\n", | 333 | pr_warning("problem processing %d event, skipping it.\n", |
| @@ -334,7 +335,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
| 334 | return -1; | 335 | return -1; |
| 335 | } | 336 | } |
| 336 | 337 | ||
| 337 | if (hists__add_entry(&evsel->hists, &al, sample->period, | 338 | if (hists__add_entry(hists, &al, sample->period, |
| 338 | sample->weight, sample->transaction)) { | 339 | sample->weight, sample->transaction)) { |
| 339 | pr_warning("problem incrementing symbol period, skipping event\n"); | 340 | pr_warning("problem incrementing symbol period, skipping event\n"); |
| 340 | return -1; | 341 | return -1; |
| @@ -346,9 +347,9 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
| 346 | * hists__output_resort() and precompute needs the total | 347 | * hists__output_resort() and precompute needs the total |
| 347 | * period in order to sort entries by percentage delta. | 348 | * period in order to sort entries by percentage delta. |
| 348 | */ | 349 | */ |
| 349 | evsel->hists.stats.total_period += sample->period; | 350 | hists->stats.total_period += sample->period; |
| 350 | if (!al.filtered) | 351 | if (!al.filtered) |
| 351 | evsel->hists.stats.total_non_filtered_period += sample->period; | 352 | hists->stats.total_non_filtered_period += sample->period; |
| 352 | 353 | ||
| 353 | return 0; | 354 | return 0; |
| 354 | } | 355 | } |
| @@ -360,7 +361,7 @@ static struct perf_tool tool = { | |||
| 360 | .exit = perf_event__process_exit, | 361 | .exit = perf_event__process_exit, |
| 361 | .fork = perf_event__process_fork, | 362 | .fork = perf_event__process_fork, |
| 362 | .lost = perf_event__process_lost, | 363 | .lost = perf_event__process_lost, |
| 363 | .ordered_samples = true, | 364 | .ordered_events = true, |
| 364 | .ordering_requires_timestamps = true, | 365 | .ordering_requires_timestamps = true, |
| 365 | }; | 366 | }; |
| 366 | 367 | ||
| @@ -382,7 +383,7 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist) | |||
| 382 | struct perf_evsel *evsel; | 383 | struct perf_evsel *evsel; |
| 383 | 384 | ||
| 384 | evlist__for_each(evlist, evsel) { | 385 | evlist__for_each(evlist, evsel) { |
| 385 | struct hists *hists = &evsel->hists; | 386 | struct hists *hists = evsel__hists(evsel); |
| 386 | 387 | ||
| 387 | hists__collapse_resort(hists, NULL); | 388 | hists__collapse_resort(hists, NULL); |
| 388 | } | 389 | } |
| @@ -631,24 +632,26 @@ static void data_process(void) | |||
| 631 | bool first = true; | 632 | bool first = true; |
| 632 | 633 | ||
| 633 | evlist__for_each(evlist_base, evsel_base) { | 634 | evlist__for_each(evlist_base, evsel_base) { |
| 635 | struct hists *hists_base = evsel__hists(evsel_base); | ||
| 634 | struct data__file *d; | 636 | struct data__file *d; |
| 635 | int i; | 637 | int i; |
| 636 | 638 | ||
| 637 | data__for_each_file_new(i, d) { | 639 | data__for_each_file_new(i, d) { |
| 638 | struct perf_evlist *evlist = d->session->evlist; | 640 | struct perf_evlist *evlist = d->session->evlist; |
| 639 | struct perf_evsel *evsel; | 641 | struct perf_evsel *evsel; |
| 642 | struct hists *hists; | ||
| 640 | 643 | ||
| 641 | evsel = evsel_match(evsel_base, evlist); | 644 | evsel = evsel_match(evsel_base, evlist); |
| 642 | if (!evsel) | 645 | if (!evsel) |
| 643 | continue; | 646 | continue; |
| 644 | 647 | ||
| 645 | d->hists = &evsel->hists; | 648 | hists = evsel__hists(evsel); |
| 649 | d->hists = hists; | ||
| 646 | 650 | ||
| 647 | hists__match(&evsel_base->hists, &evsel->hists); | 651 | hists__match(hists_base, hists); |
| 648 | 652 | ||
| 649 | if (!show_baseline_only) | 653 | if (!show_baseline_only) |
| 650 | hists__link(&evsel_base->hists, | 654 | hists__link(hists_base, hists); |
| 651 | &evsel->hists); | ||
| 652 | } | 655 | } |
| 653 | 656 | ||
| 654 | fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", | 657 | fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", |
| @@ -659,7 +662,7 @@ static void data_process(void) | |||
| 659 | if (verbose || data__files_cnt > 2) | 662 | if (verbose || data__files_cnt > 2) |
| 660 | data__fprintf(); | 663 | data__fprintf(); |
| 661 | 664 | ||
| 662 | hists__process(&evsel_base->hists); | 665 | hists__process(hists_base); |
| 663 | } | 666 | } |
| 664 | } | 667 | } |
| 665 | 668 | ||
| @@ -683,7 +686,7 @@ static int __cmd_diff(void) | |||
| 683 | d->session = perf_session__new(&d->file, false, &tool); | 686 | d->session = perf_session__new(&d->file, false, &tool); |
| 684 | if (!d->session) { | 687 | if (!d->session) { |
| 685 | pr_err("Failed to open %s\n", d->file.path); | 688 | pr_err("Failed to open %s\n", d->file.path); |
| 686 | ret = -ENOMEM; | 689 | ret = -1; |
| 687 | goto out_delete; | 690 | goto out_delete; |
| 688 | } | 691 | } |
| 689 | 692 | ||
| @@ -1139,11 +1142,16 @@ static int data_init(int argc, const char **argv) | |||
| 1139 | 1142 | ||
| 1140 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | 1143 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) |
| 1141 | { | 1144 | { |
| 1145 | int ret = hists__init(); | ||
| 1146 | |||
| 1147 | if (ret < 0) | ||
| 1148 | return ret; | ||
| 1149 | |||
| 1142 | perf_config(perf_default_config, NULL); | 1150 | perf_config(perf_default_config, NULL); |
| 1143 | 1151 | ||
| 1144 | argc = parse_options(argc, argv, options, diff_usage, 0); | 1152 | argc = parse_options(argc, argv, options, diff_usage, 0); |
| 1145 | 1153 | ||
| 1146 | if (symbol__init() < 0) | 1154 | if (symbol__init(NULL) < 0) |
| 1147 | return -1; | 1155 | return -1; |
| 1148 | 1156 | ||
| 1149 | if (data_init(argc, argv) < 0) | 1157 | 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..b65eb0507b38 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
| @@ -376,7 +376,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, | |||
| 376 | struct perf_sample *sample) | 376 | struct perf_sample *sample) |
| 377 | { | 377 | { |
| 378 | /* Only kvm_entry records vcpu id. */ | 378 | /* Only kvm_entry records vcpu id. */ |
| 379 | if (!thread->priv && kvm_entry_event(evsel)) { | 379 | if (!thread__priv(thread) && kvm_entry_event(evsel)) { |
| 380 | struct vcpu_event_record *vcpu_record; | 380 | struct vcpu_event_record *vcpu_record; |
| 381 | 381 | ||
| 382 | vcpu_record = zalloc(sizeof(*vcpu_record)); | 382 | vcpu_record = zalloc(sizeof(*vcpu_record)); |
| @@ -386,10 +386,10 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, | |||
| 386 | } | 386 | } |
| 387 | 387 | ||
| 388 | vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID); | 388 | vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID); |
| 389 | thread->priv = vcpu_record; | 389 | thread__set_priv(thread, vcpu_record); |
| 390 | } | 390 | } |
| 391 | 391 | ||
| 392 | return thread->priv; | 392 | return thread__priv(thread); |
| 393 | } | 393 | } |
| 394 | 394 | ||
| 395 | static bool handle_kvm_event(struct perf_kvm_stat *kvm, | 395 | static bool handle_kvm_event(struct perf_kvm_stat *kvm, |
| @@ -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 | ||
| @@ -902,9 +896,8 @@ int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save) | |||
| 902 | 896 | ||
| 903 | static int kvm_events_live_report(struct perf_kvm_stat *kvm) | 897 | static int kvm_events_live_report(struct perf_kvm_stat *kvm) |
| 904 | { | 898 | { |
| 905 | struct pollfd *pollfds = NULL; | 899 | int nr_stdin, ret, err = -EINVAL; |
| 906 | int nr_fds, nr_stdin, ret, err = -EINVAL; | 900 | struct termios save; |
| 907 | struct termios tc, save; | ||
| 908 | 901 | ||
| 909 | /* live flag must be set first */ | 902 | /* live flag must be set first */ |
| 910 | kvm->live = true; | 903 | kvm->live = true; |
| @@ -919,41 +912,25 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
| 919 | goto out; | 912 | goto out; |
| 920 | } | 913 | } |
| 921 | 914 | ||
| 915 | set_term_quiet_input(&save); | ||
| 922 | init_kvm_event_record(kvm); | 916 | init_kvm_event_record(kvm); |
| 923 | 917 | ||
| 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); | 918 | signal(SIGINT, sig_handler); |
| 931 | signal(SIGTERM, sig_handler); | 919 | signal(SIGTERM, sig_handler); |
| 932 | 920 | ||
| 933 | /* copy pollfds -- need to add timerfd and stdin */ | ||
| 934 | nr_fds = kvm->evlist->nr_fds; | ||
| 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 | |||
| 943 | /* add timer fd */ | 921 | /* add timer fd */ |
| 944 | if (perf_kvm__timerfd_create(kvm) < 0) { | 922 | if (perf_kvm__timerfd_create(kvm) < 0) { |
| 945 | err = -1; | 923 | err = -1; |
| 946 | goto out; | 924 | goto out; |
| 947 | } | 925 | } |
| 948 | 926 | ||
| 949 | pollfds[nr_fds].fd = kvm->timerfd; | 927 | if (perf_evlist__add_pollfd(kvm->evlist, kvm->timerfd) < 0) |
| 950 | pollfds[nr_fds].events = POLLIN; | 928 | goto out; |
| 951 | nr_fds++; | 929 | |
| 930 | nr_stdin = perf_evlist__add_pollfd(kvm->evlist, fileno(stdin)); | ||
| 931 | if (nr_stdin < 0) | ||
| 932 | goto out; | ||
| 952 | 933 | ||
| 953 | pollfds[nr_fds].fd = fileno(stdin); | ||
| 954 | pollfds[nr_fds].events = POLLIN; | ||
| 955 | nr_stdin = nr_fds; | ||
| 956 | nr_fds++; | ||
| 957 | if (fd_set_nonblock(fileno(stdin)) != 0) | 934 | if (fd_set_nonblock(fileno(stdin)) != 0) |
| 958 | goto out; | 935 | goto out; |
| 959 | 936 | ||
| @@ -961,6 +938,7 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
| 961 | perf_evlist__enable(kvm->evlist); | 938 | perf_evlist__enable(kvm->evlist); |
| 962 | 939 | ||
| 963 | while (!done) { | 940 | while (!done) { |
| 941 | struct fdarray *fda = &kvm->evlist->pollfd; | ||
| 964 | int rc; | 942 | int rc; |
| 965 | 943 | ||
| 966 | rc = perf_kvm__mmap_read(kvm); | 944 | rc = perf_kvm__mmap_read(kvm); |
| @@ -971,11 +949,11 @@ static int kvm_events_live_report(struct perf_kvm_stat *kvm) | |||
| 971 | if (err) | 949 | if (err) |
| 972 | goto out; | 950 | goto out; |
| 973 | 951 | ||
| 974 | if (pollfds[nr_stdin].revents & POLLIN) | 952 | if (fda->entries[nr_stdin].revents & POLLIN) |
| 975 | done = perf_kvm__handle_stdin(&tc, &save); | 953 | done = perf_kvm__handle_stdin(); |
| 976 | 954 | ||
| 977 | if (!rc && !done) | 955 | if (!rc && !done) |
| 978 | err = poll(pollfds, nr_fds, 100); | 956 | err = fdarray__poll(fda, 100); |
| 979 | } | 957 | } |
| 980 | 958 | ||
| 981 | perf_evlist__disable(kvm->evlist); | 959 | perf_evlist__disable(kvm->evlist); |
| @@ -989,7 +967,7 @@ out: | |||
| 989 | if (kvm->timerfd >= 0) | 967 | if (kvm->timerfd >= 0) |
| 990 | close(kvm->timerfd); | 968 | close(kvm->timerfd); |
| 991 | 969 | ||
| 992 | free(pollfds); | 970 | tcsetattr(0, TCSAFLUSH, &save); |
| 993 | return err; | 971 | return err; |
| 994 | } | 972 | } |
| 995 | 973 | ||
| @@ -998,6 +976,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm) | |||
| 998 | int err, rc = -1; | 976 | int err, rc = -1; |
| 999 | struct perf_evsel *pos; | 977 | struct perf_evsel *pos; |
| 1000 | struct perf_evlist *evlist = kvm->evlist; | 978 | struct perf_evlist *evlist = kvm->evlist; |
| 979 | char sbuf[STRERR_BUFSIZE]; | ||
| 1001 | 980 | ||
| 1002 | perf_evlist__config(evlist, &kvm->opts); | 981 | perf_evlist__config(evlist, &kvm->opts); |
| 1003 | 982 | ||
| @@ -1034,12 +1013,14 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm) | |||
| 1034 | 1013 | ||
| 1035 | err = perf_evlist__open(evlist); | 1014 | err = perf_evlist__open(evlist); |
| 1036 | if (err < 0) { | 1015 | if (err < 0) { |
| 1037 | printf("Couldn't create the events: %s\n", strerror(errno)); | 1016 | printf("Couldn't create the events: %s\n", |
| 1017 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 1038 | goto out; | 1018 | goto out; |
| 1039 | } | 1019 | } |
| 1040 | 1020 | ||
| 1041 | if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) { | 1021 | if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) { |
| 1042 | ui__error("Failed to mmap the events: %s\n", strerror(errno)); | 1022 | ui__error("Failed to mmap the events: %s\n", |
| 1023 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 1043 | perf_evlist__close(evlist); | 1024 | perf_evlist__close(evlist); |
| 1044 | goto out; | 1025 | goto out; |
| 1045 | } | 1026 | } |
| @@ -1058,7 +1039,7 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
| 1058 | struct perf_tool eops = { | 1039 | struct perf_tool eops = { |
| 1059 | .sample = process_sample_event, | 1040 | .sample = process_sample_event, |
| 1060 | .comm = perf_event__process_comm, | 1041 | .comm = perf_event__process_comm, |
| 1061 | .ordered_samples = true, | 1042 | .ordered_events = true, |
| 1062 | }; | 1043 | }; |
| 1063 | struct perf_data_file file = { | 1044 | struct perf_data_file file = { |
| 1064 | .path = kvm->file_name, | 1045 | .path = kvm->file_name, |
| @@ -1069,9 +1050,11 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
| 1069 | kvm->session = perf_session__new(&file, false, &kvm->tool); | 1050 | kvm->session = perf_session__new(&file, false, &kvm->tool); |
| 1070 | if (!kvm->session) { | 1051 | if (!kvm->session) { |
| 1071 | pr_err("Initializing perf session failed\n"); | 1052 | pr_err("Initializing perf session failed\n"); |
| 1072 | return -EINVAL; | 1053 | return -1; |
| 1073 | } | 1054 | } |
| 1074 | 1055 | ||
| 1056 | symbol__init(&kvm->session->header.env); | ||
| 1057 | |||
| 1075 | if (!perf_session__has_traces(kvm->session, "kvm record")) | 1058 | if (!perf_session__has_traces(kvm->session, "kvm record")) |
| 1076 | return -EINVAL; | 1059 | return -EINVAL; |
| 1077 | 1060 | ||
| @@ -1088,8 +1071,8 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
| 1088 | 1071 | ||
| 1089 | static int parse_target_str(struct perf_kvm_stat *kvm) | 1072 | static int parse_target_str(struct perf_kvm_stat *kvm) |
| 1090 | { | 1073 | { |
| 1091 | if (kvm->pid_str) { | 1074 | if (kvm->opts.target.pid) { |
| 1092 | kvm->pid_list = intlist__new(kvm->pid_str); | 1075 | kvm->pid_list = intlist__new(kvm->opts.target.pid); |
| 1093 | if (kvm->pid_list == NULL) { | 1076 | if (kvm->pid_list == NULL) { |
| 1094 | pr_err("Error parsing process id string\n"); | 1077 | pr_err("Error parsing process id string\n"); |
| 1095 | return -EINVAL; | 1078 | return -EINVAL; |
| @@ -1191,7 +1174,7 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
| 1191 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", | 1174 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", |
| 1192 | "key for sorting: sample(sort by samples number)" | 1175 | "key for sorting: sample(sort by samples number)" |
| 1193 | " time (sort by avg time)"), | 1176 | " time (sort by avg time)"), |
| 1194 | OPT_STRING('p', "pid", &kvm->pid_str, "pid", | 1177 | OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid", |
| 1195 | "analyze events only for given process id(s)"), | 1178 | "analyze events only for given process id(s)"), |
| 1196 | OPT_END() | 1179 | OPT_END() |
| 1197 | }; | 1180 | }; |
| @@ -1201,8 +1184,6 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
| 1201 | NULL | 1184 | NULL |
| 1202 | }; | 1185 | }; |
| 1203 | 1186 | ||
| 1204 | symbol__init(); | ||
| 1205 | |||
| 1206 | if (argc) { | 1187 | if (argc) { |
| 1207 | argc = parse_options(argc, argv, | 1188 | argc = parse_options(argc, argv, |
| 1208 | kvm_events_report_options, | 1189 | kvm_events_report_options, |
| @@ -1212,6 +1193,9 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
| 1212 | kvm_events_report_options); | 1193 | kvm_events_report_options); |
| 1213 | } | 1194 | } |
| 1214 | 1195 | ||
| 1196 | if (!kvm->opts.target.pid) | ||
| 1197 | kvm->opts.target.system_wide = true; | ||
| 1198 | |||
| 1215 | return kvm_events_report_vcpu(kvm); | 1199 | return kvm_events_report_vcpu(kvm); |
| 1216 | } | 1200 | } |
| 1217 | 1201 | ||
| @@ -1311,7 +1295,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1311 | kvm->tool.exit = perf_event__process_exit; | 1295 | kvm->tool.exit = perf_event__process_exit; |
| 1312 | kvm->tool.fork = perf_event__process_fork; | 1296 | kvm->tool.fork = perf_event__process_fork; |
| 1313 | kvm->tool.lost = process_lost_event; | 1297 | kvm->tool.lost = process_lost_event; |
| 1314 | kvm->tool.ordered_samples = true; | 1298 | kvm->tool.ordered_events = true; |
| 1315 | perf_tool__fill_defaults(&kvm->tool); | 1299 | perf_tool__fill_defaults(&kvm->tool); |
| 1316 | 1300 | ||
| 1317 | /* set defaults */ | 1301 | /* set defaults */ |
| @@ -1322,7 +1306,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1322 | kvm->opts.target.uid_str = NULL; | 1306 | kvm->opts.target.uid_str = NULL; |
| 1323 | kvm->opts.target.uid = UINT_MAX; | 1307 | kvm->opts.target.uid = UINT_MAX; |
| 1324 | 1308 | ||
| 1325 | symbol__init(); | 1309 | symbol__init(NULL); |
| 1326 | disable_buildid_cache(); | 1310 | disable_buildid_cache(); |
| 1327 | 1311 | ||
| 1328 | use_browser = 0; | 1312 | use_browser = 0; |
| @@ -1369,11 +1353,12 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1369 | */ | 1353 | */ |
| 1370 | kvm->session = perf_session__new(&file, false, &kvm->tool); | 1354 | kvm->session = perf_session__new(&file, false, &kvm->tool); |
| 1371 | if (kvm->session == NULL) { | 1355 | if (kvm->session == NULL) { |
| 1372 | err = -ENOMEM; | 1356 | err = -1; |
| 1373 | goto out; | 1357 | goto out; |
| 1374 | } | 1358 | } |
| 1375 | kvm->session->evlist = kvm->evlist; | 1359 | kvm->session->evlist = kvm->evlist; |
| 1376 | perf_session__set_id_hdr_size(kvm->session); | 1360 | perf_session__set_id_hdr_size(kvm->session); |
| 1361 | ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true); | ||
| 1377 | machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, | 1362 | machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, |
| 1378 | kvm->evlist->threads, false); | 1363 | kvm->evlist->threads, false); |
| 1379 | err = kvm_live_open_events(kvm); | 1364 | err = kvm_live_open_events(kvm); |
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..7af26acf06d9 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 | ||
| @@ -372,7 +375,9 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 372 | OPT_CALLBACK('x', "exec", NULL, "executable|path", | 375 | OPT_CALLBACK('x', "exec", NULL, "executable|path", |
| 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 | "Enable 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..2583a9b04317 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include "util/parse-options.h" | 14 | #include "util/parse-options.h" |
| 15 | #include "util/parse-events.h" | 15 | #include "util/parse-events.h" |
| 16 | 16 | ||
| 17 | #include "util/callchain.h" | ||
| 18 | #include "util/cgroup.h" | ||
| 17 | #include "util/header.h" | 19 | #include "util/header.h" |
| 18 | #include "util/event.h" | 20 | #include "util/event.h" |
| 19 | #include "util/evlist.h" | 21 | #include "util/evlist.h" |
| @@ -65,8 +67,9 @@ static int process_synthesized_event(struct perf_tool *tool, | |||
| 65 | return record__write(rec, event, event->header.size); | 67 | return record__write(rec, event, event->header.size); |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | static int record__mmap_read(struct record *rec, struct perf_mmap *md) | 70 | static int record__mmap_read(struct record *rec, int idx) |
| 69 | { | 71 | { |
| 72 | struct perf_mmap *md = &rec->evlist->mmap[idx]; | ||
| 70 | unsigned int head = perf_mmap__read_head(md); | 73 | unsigned int head = perf_mmap__read_head(md); |
| 71 | unsigned int old = md->prev; | 74 | unsigned int old = md->prev; |
| 72 | unsigned char *data = md->base + page_size; | 75 | unsigned char *data = md->base + page_size; |
| @@ -102,8 +105,7 @@ static int record__mmap_read(struct record *rec, struct perf_mmap *md) | |||
| 102 | } | 105 | } |
| 103 | 106 | ||
| 104 | md->prev = old; | 107 | md->prev = old; |
| 105 | perf_mmap__write_tail(md, old); | 108 | perf_evlist__mmap_consume(rec->evlist, idx); |
| 106 | |||
| 107 | out: | 109 | out: |
| 108 | return rc; | 110 | return rc; |
| 109 | } | 111 | } |
| @@ -161,7 +163,7 @@ try_again: | |||
| 161 | 163 | ||
| 162 | if (perf_evlist__apply_filters(evlist)) { | 164 | if (perf_evlist__apply_filters(evlist)) { |
| 163 | error("failed to set filter with %d (%s)\n", errno, | 165 | error("failed to set filter with %d (%s)\n", errno, |
| 164 | strerror(errno)); | 166 | strerror_r(errno, msg, sizeof(msg))); |
| 165 | rc = -1; | 167 | rc = -1; |
| 166 | goto out; | 168 | goto out; |
| 167 | } | 169 | } |
| @@ -175,7 +177,8 @@ try_again: | |||
| 175 | "(current value: %u)\n", opts->mmap_pages); | 177 | "(current value: %u)\n", opts->mmap_pages); |
| 176 | rc = -errno; | 178 | rc = -errno; |
| 177 | } else { | 179 | } else { |
| 178 | pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 180 | pr_err("failed to mmap with %d (%s)\n", errno, |
| 181 | strerror_r(errno, msg, sizeof(msg))); | ||
| 179 | rc = -errno; | 182 | rc = -errno; |
| 180 | } | 183 | } |
| 181 | goto out; | 184 | goto out; |
| @@ -244,7 +247,7 @@ static int record__mmap_read_all(struct record *rec) | |||
| 244 | 247 | ||
| 245 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { | 248 | for (i = 0; i < rec->evlist->nr_mmaps; i++) { |
| 246 | if (rec->evlist->mmap[i].base) { | 249 | if (rec->evlist->mmap[i].base) { |
| 247 | if (record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) { | 250 | if (record__mmap_read(rec, i) != 0) { |
| 248 | rc = -1; | 251 | rc = -1; |
| 249 | goto out; | 252 | goto out; |
| 250 | } | 253 | } |
| @@ -307,7 +310,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 307 | struct record_opts *opts = &rec->opts; | 310 | struct record_opts *opts = &rec->opts; |
| 308 | struct perf_data_file *file = &rec->file; | 311 | struct perf_data_file *file = &rec->file; |
| 309 | struct perf_session *session; | 312 | struct perf_session *session; |
| 310 | bool disabled = false; | 313 | bool disabled = false, draining = false; |
| 311 | 314 | ||
| 312 | rec->progname = argv[0]; | 315 | rec->progname = argv[0]; |
| 313 | 316 | ||
| @@ -456,9 +459,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 456 | } | 459 | } |
| 457 | 460 | ||
| 458 | if (hits == rec->samples) { | 461 | if (hits == rec->samples) { |
| 459 | if (done) | 462 | if (done || draining) |
| 460 | break; | 463 | break; |
| 461 | err = poll(rec->evlist->pollfd, rec->evlist->nr_fds, -1); | 464 | err = perf_evlist__poll(rec->evlist, -1); |
| 462 | /* | 465 | /* |
| 463 | * Propagate error, only if there's any. Ignore positive | 466 | * Propagate error, only if there's any. Ignore positive |
| 464 | * number of returned events and interrupt error. | 467 | * number of returned events and interrupt error. |
| @@ -466,6 +469,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 466 | if (err > 0 || (err < 0 && errno == EINTR)) | 469 | if (err > 0 || (err < 0 && errno == EINTR)) |
| 467 | err = 0; | 470 | err = 0; |
| 468 | waking++; | 471 | waking++; |
| 472 | |||
| 473 | if (perf_evlist__filter_pollfd(rec->evlist, POLLERR | POLLHUP) == 0) | ||
| 474 | draining = true; | ||
| 469 | } | 475 | } |
| 470 | 476 | ||
| 471 | /* | 477 | /* |
| @@ -480,7 +486,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) | |||
| 480 | } | 486 | } |
| 481 | 487 | ||
| 482 | if (forks && workload_exec_errno) { | 488 | if (forks && workload_exec_errno) { |
| 483 | char msg[512]; | 489 | char msg[STRERR_BUFSIZE]; |
| 484 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); | 490 | const char *emsg = strerror_r(workload_exec_errno, msg, sizeof(msg)); |
| 485 | pr_err("Workload failed: %s\n", emsg); | 491 | pr_err("Workload failed: %s\n", emsg); |
| 486 | err = -1; | 492 | err = -1; |
| @@ -620,145 +626,56 @@ error: | |||
| 620 | return ret; | 626 | return ret; |
| 621 | } | 627 | } |
| 622 | 628 | ||
| 623 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 629 | 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 | { | 630 | { |
| 709 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; | 631 | static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF" }; |
| 710 | 632 | ||
| 711 | pr_debug("callchain: type %s\n", str[opts->call_graph]); | 633 | pr_debug("callchain: type %s\n", str[callchain_param.record_mode]); |
| 712 | 634 | ||
| 713 | if (opts->call_graph == CALLCHAIN_DWARF) | 635 | if (callchain_param.record_mode == CALLCHAIN_DWARF) |
| 714 | pr_debug("callchain: stack dump size %d\n", | 636 | pr_debug("callchain: stack dump size %d\n", |
| 715 | opts->stack_dump_size); | 637 | callchain_param.dump_size); |
| 716 | } | 638 | } |
| 717 | 639 | ||
| 718 | int record_parse_callchain_opt(const struct option *opt, | 640 | int record_parse_callchain_opt(const struct option *opt __maybe_unused, |
| 719 | const char *arg, | 641 | const char *arg, |
| 720 | int unset) | 642 | int unset) |
| 721 | { | 643 | { |
| 722 | struct record_opts *opts = opt->value; | ||
| 723 | int ret; | 644 | int ret; |
| 724 | 645 | ||
| 725 | opts->call_graph_enabled = !unset; | 646 | callchain_param.enabled = !unset; |
| 726 | 647 | ||
| 727 | /* --no-call-graph */ | 648 | /* --no-call-graph */ |
| 728 | if (unset) { | 649 | if (unset) { |
| 729 | opts->call_graph = CALLCHAIN_NONE; | 650 | callchain_param.record_mode = CALLCHAIN_NONE; |
| 730 | pr_debug("callchain: disabled\n"); | 651 | pr_debug("callchain: disabled\n"); |
| 731 | return 0; | 652 | return 0; |
| 732 | } | 653 | } |
| 733 | 654 | ||
| 734 | ret = record_parse_callchain(arg, opts); | 655 | ret = parse_callchain_record_opt(arg); |
| 735 | if (!ret) | 656 | if (!ret) |
| 736 | callchain_debug(opts); | 657 | callchain_debug(); |
| 737 | 658 | ||
| 738 | return ret; | 659 | return ret; |
| 739 | } | 660 | } |
| 740 | 661 | ||
| 741 | int record_callchain_opt(const struct option *opt, | 662 | int record_callchain_opt(const struct option *opt __maybe_unused, |
| 742 | const char *arg __maybe_unused, | 663 | const char *arg __maybe_unused, |
| 743 | int unset __maybe_unused) | 664 | int unset __maybe_unused) |
| 744 | { | 665 | { |
| 745 | struct record_opts *opts = opt->value; | 666 | callchain_param.enabled = true; |
| 746 | 667 | ||
| 747 | opts->call_graph_enabled = !unset; | 668 | if (callchain_param.record_mode == CALLCHAIN_NONE) |
| 669 | callchain_param.record_mode = CALLCHAIN_FP; | ||
| 748 | 670 | ||
| 749 | if (opts->call_graph == CALLCHAIN_NONE) | 671 | callchain_debug(); |
| 750 | opts->call_graph = CALLCHAIN_FP; | ||
| 751 | |||
| 752 | callchain_debug(opts); | ||
| 753 | return 0; | 672 | return 0; |
| 754 | } | 673 | } |
| 755 | 674 | ||
| 756 | static int perf_record_config(const char *var, const char *value, void *cb) | 675 | static int perf_record_config(const char *var, const char *value, void *cb) |
| 757 | { | 676 | { |
| 758 | struct record *rec = cb; | ||
| 759 | |||
| 760 | if (!strcmp(var, "record.call-graph")) | 677 | if (!strcmp(var, "record.call-graph")) |
| 761 | return record_parse_callchain(value, &rec->opts); | 678 | var = "call-graph.record-mode"; /* fall-through */ |
| 762 | 679 | ||
| 763 | return perf_default_config(var, value, cb); | 680 | return perf_default_config(var, value, cb); |
| 764 | } | 681 | } |
| @@ -781,6 +698,7 @@ static const char * const record_usage[] = { | |||
| 781 | */ | 698 | */ |
| 782 | static struct record record = { | 699 | static struct record record = { |
| 783 | .opts = { | 700 | .opts = { |
| 701 | .sample_time = true, | ||
| 784 | .mmap_pages = UINT_MAX, | 702 | .mmap_pages = UINT_MAX, |
| 785 | .user_freq = UINT_MAX, | 703 | .user_freq = UINT_MAX, |
| 786 | .user_interval = ULLONG_MAX, | 704 | .user_interval = ULLONG_MAX, |
| @@ -907,7 +825,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 907 | usage_with_options(record_usage, record_options); | 825 | usage_with_options(record_usage, record_options); |
| 908 | } | 826 | } |
| 909 | 827 | ||
| 910 | symbol__init(); | 828 | symbol__init(NULL); |
| 911 | 829 | ||
| 912 | if (symbol_conf.kptr_restrict) | 830 | if (symbol_conf.kptr_restrict) |
| 913 | pr_warning( | 831 | pr_warning( |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 21d830bafff3..140a6cd88351 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 | } |
| @@ -251,6 +257,13 @@ static int report__setup_sample_type(struct report *rep) | |||
| 251 | } | 257 | } |
| 252 | } | 258 | } |
| 253 | 259 | ||
| 260 | if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { | ||
| 261 | if ((sample_type & PERF_SAMPLE_REGS_USER) && | ||
| 262 | (sample_type & PERF_SAMPLE_STACK_USER)) | ||
| 263 | callchain_param.record_mode = CALLCHAIN_DWARF; | ||
| 264 | else | ||
| 265 | callchain_param.record_mode = CALLCHAIN_FP; | ||
| 266 | } | ||
| 254 | return 0; | 267 | return 0; |
| 255 | } | 268 | } |
| 256 | 269 | ||
| @@ -282,12 +295,14 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report | |||
| 282 | evname = buf; | 295 | evname = buf; |
| 283 | 296 | ||
| 284 | for_each_group_member(pos, evsel) { | 297 | for_each_group_member(pos, evsel) { |
| 298 | const struct hists *pos_hists = evsel__hists(pos); | ||
| 299 | |||
| 285 | if (symbol_conf.filter_relative) { | 300 | if (symbol_conf.filter_relative) { |
| 286 | nr_samples += pos->hists.stats.nr_non_filtered_samples; | 301 | nr_samples += pos_hists->stats.nr_non_filtered_samples; |
| 287 | nr_events += pos->hists.stats.total_non_filtered_period; | 302 | nr_events += pos_hists->stats.total_non_filtered_period; |
| 288 | } else { | 303 | } else { |
| 289 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 304 | nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
| 290 | nr_events += pos->hists.stats.total_period; | 305 | nr_events += pos_hists->stats.total_period; |
| 291 | } | 306 | } |
| 292 | } | 307 | } |
| 293 | } | 308 | } |
| @@ -312,7 +327,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
| 312 | struct perf_evsel *pos; | 327 | struct perf_evsel *pos; |
| 313 | 328 | ||
| 314 | evlist__for_each(evlist, pos) { | 329 | evlist__for_each(evlist, pos) { |
| 315 | struct hists *hists = &pos->hists; | 330 | struct hists *hists = evsel__hists(pos); |
| 316 | const char *evname = perf_evsel__name(pos); | 331 | const char *evname = perf_evsel__name(pos); |
| 317 | 332 | ||
| 318 | if (symbol_conf.event_group && | 333 | if (symbol_conf.event_group && |
| @@ -421,7 +436,7 @@ static void report__collapse_hists(struct report *rep) | |||
| 421 | ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); | 436 | ui_progress__init(&prog, rep->nr_entries, "Merging related events..."); |
| 422 | 437 | ||
| 423 | evlist__for_each(rep->session->evlist, pos) { | 438 | evlist__for_each(rep->session->evlist, pos) { |
| 424 | struct hists *hists = &pos->hists; | 439 | struct hists *hists = evsel__hists(pos); |
| 425 | 440 | ||
| 426 | if (pos->idx == 0) | 441 | if (pos->idx == 0) |
| 427 | hists->symbol_filter_str = rep->symbol_filter_str; | 442 | hists->symbol_filter_str = rep->symbol_filter_str; |
| @@ -431,7 +446,7 @@ static void report__collapse_hists(struct report *rep) | |||
| 431 | /* Non-group events are considered as leader */ | 446 | /* Non-group events are considered as leader */ |
| 432 | if (symbol_conf.event_group && | 447 | if (symbol_conf.event_group && |
| 433 | !perf_evsel__is_group_leader(pos)) { | 448 | !perf_evsel__is_group_leader(pos)) { |
| 434 | struct hists *leader_hists = &pos->leader->hists; | 449 | struct hists *leader_hists = evsel__hists(pos->leader); |
| 435 | 450 | ||
| 436 | hists__match(leader_hists, hists); | 451 | hists__match(leader_hists, hists); |
| 437 | hists__link(leader_hists, hists); | 452 | hists__link(leader_hists, hists); |
| @@ -479,6 +494,7 @@ static int __cmd_report(struct report *rep) | |||
| 479 | 494 | ||
| 480 | if (dump_trace) { | 495 | if (dump_trace) { |
| 481 | perf_session__fprintf_nr_events(session, stdout); | 496 | perf_session__fprintf_nr_events(session, stdout); |
| 497 | perf_evlist__fprintf_nr_events(session->evlist, stdout); | ||
| 482 | return 0; | 498 | return 0; |
| 483 | } | 499 | } |
| 484 | } | 500 | } |
| @@ -494,7 +510,7 @@ static int __cmd_report(struct report *rep) | |||
| 494 | } | 510 | } |
| 495 | 511 | ||
| 496 | evlist__for_each(session->evlist, pos) | 512 | evlist__for_each(session->evlist, pos) |
| 497 | hists__output_resort(&pos->hists); | 513 | hists__output_resort(evsel__hists(pos)); |
| 498 | 514 | ||
| 499 | return report__browse_hists(rep); | 515 | return report__browse_hists(rep); |
| 500 | } | 516 | } |
| @@ -559,7 +575,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 559 | struct stat st; | 575 | struct stat st; |
| 560 | bool has_br_stack = false; | 576 | bool has_br_stack = false; |
| 561 | int branch_mode = -1; | 577 | int branch_mode = -1; |
| 562 | int ret = -1; | ||
| 563 | char callchain_default_opt[] = "fractal,0.5,callee"; | 578 | char callchain_default_opt[] = "fractal,0.5,callee"; |
| 564 | const char * const report_usage[] = { | 579 | const char * const report_usage[] = { |
| 565 | "perf report [<options>]", | 580 | "perf report [<options>]", |
| @@ -578,7 +593,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 578 | .attr = perf_event__process_attr, | 593 | .attr = perf_event__process_attr, |
| 579 | .tracing_data = perf_event__process_tracing_data, | 594 | .tracing_data = perf_event__process_tracing_data, |
| 580 | .build_id = perf_event__process_build_id, | 595 | .build_id = perf_event__process_build_id, |
| 581 | .ordered_samples = true, | 596 | .ordered_events = true, |
| 582 | .ordering_requires_timestamps = true, | 597 | .ordering_requires_timestamps = true, |
| 583 | }, | 598 | }, |
| 584 | .max_stack = PERF_MAX_STACK_DEPTH, | 599 | .max_stack = PERF_MAX_STACK_DEPTH, |
| @@ -674,6 +689,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 674 | "objdump binary to use for disassembly and annotations"), | 689 | "objdump binary to use for disassembly and annotations"), |
| 675 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, | 690 | OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle, |
| 676 | "Disable symbol demangling"), | 691 | "Disable symbol demangling"), |
| 692 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
| 693 | "Enable kernel symbol demangling"), | ||
| 677 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), | 694 | OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), |
| 678 | OPT_CALLBACK(0, "percent-limit", &report, "percent", | 695 | OPT_CALLBACK(0, "percent-limit", &report, "percent", |
| 679 | "Don't show entries under that percent", parse_percent_limit), | 696 | "Don't show entries under that percent", parse_percent_limit), |
| @@ -684,6 +701,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 684 | struct perf_data_file file = { | 701 | struct perf_data_file file = { |
| 685 | .mode = PERF_DATA_MODE_READ, | 702 | .mode = PERF_DATA_MODE_READ, |
| 686 | }; | 703 | }; |
| 704 | int ret = hists__init(); | ||
| 705 | |||
| 706 | if (ret < 0) | ||
| 707 | return ret; | ||
| 687 | 708 | ||
| 688 | perf_config(report__config, &report); | 709 | perf_config(report__config, &report); |
| 689 | 710 | ||
| @@ -712,14 +733,19 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 712 | repeat: | 733 | repeat: |
| 713 | session = perf_session__new(&file, false, &report.tool); | 734 | session = perf_session__new(&file, false, &report.tool); |
| 714 | if (session == NULL) | 735 | if (session == NULL) |
| 715 | return -ENOMEM; | 736 | return -1; |
| 737 | |||
| 738 | if (report.queue_size) { | ||
| 739 | ordered_events__set_alloc_size(&session->ordered_events, | ||
| 740 | report.queue_size); | ||
| 741 | } | ||
| 716 | 742 | ||
| 717 | report.session = session; | 743 | report.session = session; |
| 718 | 744 | ||
| 719 | has_br_stack = perf_header__has_feat(&session->header, | 745 | has_br_stack = perf_header__has_feat(&session->header, |
| 720 | HEADER_BRANCH_STACK); | 746 | HEADER_BRANCH_STACK); |
| 721 | 747 | ||
| 722 | if (branch_mode == -1 && has_br_stack) { | 748 | if ((branch_mode == -1 && has_br_stack) || branch_mode == 1) { |
| 723 | sort__mode = SORT_MODE__BRANCH; | 749 | sort__mode = SORT_MODE__BRANCH; |
| 724 | symbol_conf.cumulate_callchain = false; | 750 | symbol_conf.cumulate_callchain = false; |
| 725 | } | 751 | } |
| @@ -787,7 +813,7 @@ repeat: | |||
| 787 | } | 813 | } |
| 788 | } | 814 | } |
| 789 | 815 | ||
| 790 | if (symbol__init() < 0) | 816 | if (symbol__init(&session->header.env) < 0) |
| 791 | goto error; | 817 | goto error; |
| 792 | 818 | ||
| 793 | if (argc) { | 819 | if (argc) { |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index f83c08c0dd87..891c3930080e 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 | ||
| @@ -1429,9 +1431,6 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_ | |||
| 1429 | { | 1431 | { |
| 1430 | int err = 0; | 1432 | int err = 0; |
| 1431 | 1433 | ||
| 1432 | evsel->hists.stats.total_period += sample->period; | ||
| 1433 | hists__inc_nr_samples(&evsel->hists, true); | ||
| 1434 | |||
| 1435 | if (evsel->handler != NULL) { | 1434 | if (evsel->handler != NULL) { |
| 1436 | tracepoint_handler f = evsel->handler; | 1435 | tracepoint_handler f = evsel->handler; |
| 1437 | err = f(tool, evsel, sample, machine); | 1436 | err = f(tool, evsel, sample, machine); |
| @@ -1462,6 +1461,8 @@ static int perf_sched__read_events(struct perf_sched *sched, | |||
| 1462 | return -1; | 1461 | return -1; |
| 1463 | } | 1462 | } |
| 1464 | 1463 | ||
| 1464 | symbol__init(&session->header.env); | ||
| 1465 | |||
| 1465 | if (perf_session__set_tracepoints_handlers(session, handlers)) | 1466 | if (perf_session__set_tracepoints_handlers(session, handlers)) |
| 1466 | goto out_delete; | 1467 | goto out_delete; |
| 1467 | 1468 | ||
| @@ -1662,7 +1663,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1662 | .comm = perf_event__process_comm, | 1663 | .comm = perf_event__process_comm, |
| 1663 | .lost = perf_event__process_lost, | 1664 | .lost = perf_event__process_lost, |
| 1664 | .fork = perf_sched__process_fork_event, | 1665 | .fork = perf_sched__process_fork_event, |
| 1665 | .ordered_samples = true, | 1666 | .ordered_events = true, |
| 1666 | }, | 1667 | }, |
| 1667 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), | 1668 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), |
| 1668 | .sort_list = LIST_HEAD_INIT(sched.sort_list), | 1669 | .sort_list = LIST_HEAD_INIT(sched.sort_list), |
| @@ -1747,7 +1748,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1747 | if (!strcmp(argv[0], "script")) | 1748 | if (!strcmp(argv[0], "script")) |
| 1748 | return cmd_script(argc, argv, prefix); | 1749 | return cmd_script(argc, argv, prefix); |
| 1749 | 1750 | ||
| 1750 | symbol__init(); | ||
| 1751 | if (!strncmp(argv[0], "rec", 3)) { | 1751 | if (!strncmp(argv[0], "rec", 3)) { |
| 1752 | return __cmd_record(argc, argv); | 1752 | return __cmd_record(argc, argv); |
| 1753 | } else if (!strncmp(argv[0], "lat", 3)) { | 1753 | } else if (!strncmp(argv[0], "lat", 3)) { |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index f57035b89c15..9708a1290571 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -44,6 +44,7 @@ enum perf_output_field { | |||
| 44 | PERF_OUTPUT_ADDR = 1U << 10, | 44 | PERF_OUTPUT_ADDR = 1U << 10, |
| 45 | PERF_OUTPUT_SYMOFFSET = 1U << 11, | 45 | PERF_OUTPUT_SYMOFFSET = 1U << 11, |
| 46 | PERF_OUTPUT_SRCLINE = 1U << 12, | 46 | PERF_OUTPUT_SRCLINE = 1U << 12, |
| 47 | PERF_OUTPUT_PERIOD = 1U << 13, | ||
| 47 | }; | 48 | }; |
| 48 | 49 | ||
| 49 | struct output_option { | 50 | struct output_option { |
| @@ -63,6 +64,7 @@ struct output_option { | |||
| 63 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, | 64 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, |
| 64 | {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, | 65 | {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, |
| 65 | {.str = "srcline", .field = PERF_OUTPUT_SRCLINE}, | 66 | {.str = "srcline", .field = PERF_OUTPUT_SRCLINE}, |
| 67 | {.str = "period", .field = PERF_OUTPUT_PERIOD}, | ||
| 66 | }; | 68 | }; |
| 67 | 69 | ||
| 68 | /* default set to maintain compatibility with current format */ | 70 | /* default set to maintain compatibility with current format */ |
| @@ -80,7 +82,8 @@ static struct { | |||
| 80 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 82 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
| 81 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 83 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
| 82 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 84 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
| 83 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | 85 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
| 86 | PERF_OUTPUT_PERIOD, | ||
| 84 | 87 | ||
| 85 | .invalid_fields = PERF_OUTPUT_TRACE, | 88 | .invalid_fields = PERF_OUTPUT_TRACE, |
| 86 | }, | 89 | }, |
| @@ -91,7 +94,8 @@ static struct { | |||
| 91 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 94 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
| 92 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 95 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
| 93 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 96 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
| 94 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | 97 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
| 98 | PERF_OUTPUT_PERIOD, | ||
| 95 | 99 | ||
| 96 | .invalid_fields = PERF_OUTPUT_TRACE, | 100 | .invalid_fields = PERF_OUTPUT_TRACE, |
| 97 | }, | 101 | }, |
| @@ -110,7 +114,8 @@ static struct { | |||
| 110 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 114 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
| 111 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 115 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
| 112 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 116 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
| 113 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO, | 117 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
| 118 | PERF_OUTPUT_PERIOD, | ||
| 114 | 119 | ||
| 115 | .invalid_fields = PERF_OUTPUT_TRACE, | 120 | .invalid_fields = PERF_OUTPUT_TRACE, |
| 116 | }, | 121 | }, |
| @@ -184,10 +189,6 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
| 184 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", | 189 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_IP, "IP", |
| 185 | PERF_OUTPUT_IP)) | 190 | PERF_OUTPUT_IP)) |
| 186 | return -EINVAL; | 191 | return -EINVAL; |
| 187 | |||
| 188 | if (!no_callchain && | ||
| 189 | !(attr->sample_type & PERF_SAMPLE_CALLCHAIN)) | ||
| 190 | symbol_conf.use_callchain = false; | ||
| 191 | } | 192 | } |
| 192 | 193 | ||
| 193 | if (PRINT_FIELD(ADDR) && | 194 | if (PRINT_FIELD(ADDR) && |
| @@ -233,6 +234,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
| 233 | PERF_OUTPUT_CPU)) | 234 | PERF_OUTPUT_CPU)) |
| 234 | return -EINVAL; | 235 | return -EINVAL; |
| 235 | 236 | ||
| 237 | if (PRINT_FIELD(PERIOD) && | ||
| 238 | perf_evsel__check_stype(evsel, PERF_SAMPLE_PERIOD, "PERIOD", | ||
| 239 | PERF_OUTPUT_PERIOD)) | ||
| 240 | return -EINVAL; | ||
| 241 | |||
| 236 | return 0; | 242 | return 0; |
| 237 | } | 243 | } |
| 238 | 244 | ||
| @@ -290,6 +296,19 @@ static int perf_session__check_output_opt(struct perf_session *session) | |||
| 290 | set_print_ip_opts(&evsel->attr); | 296 | set_print_ip_opts(&evsel->attr); |
| 291 | } | 297 | } |
| 292 | 298 | ||
| 299 | if (!no_callchain) { | ||
| 300 | bool use_callchain = false; | ||
| 301 | |||
| 302 | evlist__for_each(session->evlist, evsel) { | ||
| 303 | if (evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN) { | ||
| 304 | use_callchain = true; | ||
| 305 | break; | ||
| 306 | } | ||
| 307 | } | ||
| 308 | if (!use_callchain) | ||
| 309 | symbol_conf.use_callchain = false; | ||
| 310 | } | ||
| 311 | |||
| 293 | /* | 312 | /* |
| 294 | * set default for tracepoints to print symbols only | 313 | * set default for tracepoints to print symbols only |
| 295 | * if callchains are present | 314 | * if callchains are present |
| @@ -439,6 +458,9 @@ static void process_event(union perf_event *event, struct perf_sample *sample, | |||
| 439 | 458 | ||
| 440 | print_sample_start(sample, thread, evsel); | 459 | print_sample_start(sample, thread, evsel); |
| 441 | 460 | ||
| 461 | if (PRINT_FIELD(PERIOD)) | ||
| 462 | printf("%10" PRIu64 " ", sample->period); | ||
| 463 | |||
| 442 | if (PRINT_FIELD(EVNAME)) { | 464 | if (PRINT_FIELD(EVNAME)) { |
| 443 | const char *evname = perf_evsel__name(evsel); | 465 | const char *evname = perf_evsel__name(evsel); |
| 444 | printf("%s: ", evname ? evname : "[unknown]"); | 466 | printf("%s: ", evname ? evname : "[unknown]"); |
| @@ -476,6 +498,11 @@ static int default_start_script(const char *script __maybe_unused, | |||
| 476 | return 0; | 498 | return 0; |
| 477 | } | 499 | } |
| 478 | 500 | ||
| 501 | static int default_flush_script(void) | ||
| 502 | { | ||
| 503 | return 0; | ||
| 504 | } | ||
| 505 | |||
| 479 | static int default_stop_script(void) | 506 | static int default_stop_script(void) |
| 480 | { | 507 | { |
| 481 | return 0; | 508 | return 0; |
| @@ -489,6 +516,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused, | |||
| 489 | 516 | ||
| 490 | static struct scripting_ops default_scripting_ops = { | 517 | static struct scripting_ops default_scripting_ops = { |
| 491 | .start_script = default_start_script, | 518 | .start_script = default_start_script, |
| 519 | .flush_script = default_flush_script, | ||
| 492 | .stop_script = default_stop_script, | 520 | .stop_script = default_stop_script, |
| 493 | .process_event = process_event, | 521 | .process_event = process_event, |
| 494 | .generate_script = default_generate_script, | 522 | .generate_script = default_generate_script, |
| @@ -504,6 +532,11 @@ static void setup_scripting(void) | |||
| 504 | scripting_ops = &default_scripting_ops; | 532 | scripting_ops = &default_scripting_ops; |
| 505 | } | 533 | } |
| 506 | 534 | ||
| 535 | static int flush_scripting(void) | ||
| 536 | { | ||
| 537 | return scripting_ops->flush_script(); | ||
| 538 | } | ||
| 539 | |||
| 507 | static int cleanup_scripting(void) | 540 | static int cleanup_scripting(void) |
| 508 | { | 541 | { |
| 509 | pr_debug("\nperf script stopped\n"); | 542 | pr_debug("\nperf script stopped\n"); |
| @@ -552,7 +585,6 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
| 552 | 585 | ||
| 553 | scripting_ops->process_event(event, sample, evsel, thread, &al); | 586 | scripting_ops->process_event(event, sample, evsel, thread, &al); |
| 554 | 587 | ||
| 555 | evsel->hists.stats.total_period += sample->period; | ||
| 556 | return 0; | 588 | return 0; |
| 557 | } | 589 | } |
| 558 | 590 | ||
| @@ -1471,12 +1503,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1471 | bool show_full_info = false; | 1503 | bool show_full_info = false; |
| 1472 | bool header = false; | 1504 | bool header = false; |
| 1473 | bool header_only = false; | 1505 | bool header_only = false; |
| 1506 | bool script_started = false; | ||
| 1474 | char *rec_script_path = NULL; | 1507 | char *rec_script_path = NULL; |
| 1475 | char *rep_script_path = NULL; | 1508 | char *rep_script_path = NULL; |
| 1476 | struct perf_session *session; | 1509 | struct perf_session *session; |
| 1477 | char *script_path = NULL; | 1510 | char *script_path = NULL; |
| 1478 | const char **__argv; | 1511 | const char **__argv; |
| 1479 | int i, j, err; | 1512 | int i, j, err = 0; |
| 1480 | struct perf_script script = { | 1513 | struct perf_script script = { |
| 1481 | .tool = { | 1514 | .tool = { |
| 1482 | .sample = process_sample_event, | 1515 | .sample = process_sample_event, |
| @@ -1488,7 +1521,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1488 | .attr = process_attr, | 1521 | .attr = process_attr, |
| 1489 | .tracing_data = perf_event__process_tracing_data, | 1522 | .tracing_data = perf_event__process_tracing_data, |
| 1490 | .build_id = perf_event__process_build_id, | 1523 | .build_id = perf_event__process_build_id, |
| 1491 | .ordered_samples = true, | 1524 | .ordered_events = true, |
| 1492 | .ordering_requires_timestamps = true, | 1525 | .ordering_requires_timestamps = true, |
| 1493 | }, | 1526 | }, |
| 1494 | }; | 1527 | }; |
| @@ -1523,7 +1556,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1523 | "comma separated output fields prepend with 'type:'. " | 1556 | "comma separated output fields prepend with 'type:'. " |
| 1524 | "Valid types: hw,sw,trace,raw. " | 1557 | "Valid types: hw,sw,trace,raw. " |
| 1525 | "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," | 1558 | "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," |
| 1526 | "addr,symoff", parse_output_fields), | 1559 | "addr,symoff,period", parse_output_fields), |
| 1527 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1560 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
| 1528 | "system-wide collection from all CPUs"), | 1561 | "system-wide collection from all CPUs"), |
| 1529 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 1562 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
| @@ -1718,26 +1751,28 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1718 | exit(-1); | 1751 | exit(-1); |
| 1719 | } | 1752 | } |
| 1720 | 1753 | ||
| 1721 | if (symbol__init() < 0) | ||
| 1722 | return -1; | ||
| 1723 | if (!script_name) | 1754 | if (!script_name) |
| 1724 | setup_pager(); | 1755 | setup_pager(); |
| 1725 | 1756 | ||
| 1726 | session = perf_session__new(&file, false, &script.tool); | 1757 | session = perf_session__new(&file, false, &script.tool); |
| 1727 | if (session == NULL) | 1758 | if (session == NULL) |
| 1728 | return -ENOMEM; | 1759 | return -1; |
| 1729 | 1760 | ||
| 1730 | if (header || header_only) { | 1761 | if (header || header_only) { |
| 1731 | perf_session__fprintf_info(session, stdout, show_full_info); | 1762 | perf_session__fprintf_info(session, stdout, show_full_info); |
| 1732 | if (header_only) | 1763 | if (header_only) |
| 1733 | return 0; | 1764 | goto out_delete; |
| 1734 | } | 1765 | } |
| 1735 | 1766 | ||
| 1767 | if (symbol__init(&session->header.env) < 0) | ||
| 1768 | goto out_delete; | ||
| 1769 | |||
| 1736 | script.session = session; | 1770 | script.session = session; |
| 1737 | 1771 | ||
| 1738 | if (cpu_list) { | 1772 | if (cpu_list) { |
| 1739 | if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap)) | 1773 | err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); |
| 1740 | return -1; | 1774 | if (err < 0) |
| 1775 | goto out_delete; | ||
| 1741 | } | 1776 | } |
| 1742 | 1777 | ||
| 1743 | if (!no_callchain) | 1778 | if (!no_callchain) |
| @@ -1752,53 +1787,62 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1752 | if (output_set_by_user()) { | 1787 | if (output_set_by_user()) { |
| 1753 | fprintf(stderr, | 1788 | fprintf(stderr, |
| 1754 | "custom fields not supported for generated scripts"); | 1789 | "custom fields not supported for generated scripts"); |
| 1755 | return -1; | 1790 | err = -EINVAL; |
| 1791 | goto out_delete; | ||
| 1756 | } | 1792 | } |
| 1757 | 1793 | ||
| 1758 | input = open(file.path, O_RDONLY); /* input_name */ | 1794 | input = open(file.path, O_RDONLY); /* input_name */ |
| 1759 | if (input < 0) { | 1795 | if (input < 0) { |
| 1796 | err = -errno; | ||
| 1760 | perror("failed to open file"); | 1797 | perror("failed to open file"); |
| 1761 | return -1; | 1798 | goto out_delete; |
| 1762 | } | 1799 | } |
| 1763 | 1800 | ||
| 1764 | err = fstat(input, &perf_stat); | 1801 | err = fstat(input, &perf_stat); |
| 1765 | if (err < 0) { | 1802 | if (err < 0) { |
| 1766 | perror("failed to stat file"); | 1803 | perror("failed to stat file"); |
| 1767 | return -1; | 1804 | goto out_delete; |
| 1768 | } | 1805 | } |
| 1769 | 1806 | ||
| 1770 | if (!perf_stat.st_size) { | 1807 | if (!perf_stat.st_size) { |
| 1771 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 1808 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
| 1772 | return 0; | 1809 | goto out_delete; |
| 1773 | } | 1810 | } |
| 1774 | 1811 | ||
| 1775 | scripting_ops = script_spec__lookup(generate_script_lang); | 1812 | scripting_ops = script_spec__lookup(generate_script_lang); |
| 1776 | if (!scripting_ops) { | 1813 | if (!scripting_ops) { |
| 1777 | fprintf(stderr, "invalid language specifier"); | 1814 | fprintf(stderr, "invalid language specifier"); |
| 1778 | return -1; | 1815 | err = -ENOENT; |
| 1816 | goto out_delete; | ||
| 1779 | } | 1817 | } |
| 1780 | 1818 | ||
| 1781 | err = scripting_ops->generate_script(session->tevent.pevent, | 1819 | err = scripting_ops->generate_script(session->tevent.pevent, |
| 1782 | "perf-script"); | 1820 | "perf-script"); |
| 1783 | goto out; | 1821 | goto out_delete; |
| 1784 | } | 1822 | } |
| 1785 | 1823 | ||
| 1786 | if (script_name) { | 1824 | if (script_name) { |
| 1787 | err = scripting_ops->start_script(script_name, argc, argv); | 1825 | err = scripting_ops->start_script(script_name, argc, argv); |
| 1788 | if (err) | 1826 | if (err) |
| 1789 | goto out; | 1827 | goto out_delete; |
| 1790 | pr_debug("perf script started with script %s\n\n", script_name); | 1828 | pr_debug("perf script started with script %s\n\n", script_name); |
| 1829 | script_started = true; | ||
| 1791 | } | 1830 | } |
| 1792 | 1831 | ||
| 1793 | 1832 | ||
| 1794 | err = perf_session__check_output_opt(session); | 1833 | err = perf_session__check_output_opt(session); |
| 1795 | if (err < 0) | 1834 | if (err < 0) |
| 1796 | goto out; | 1835 | goto out_delete; |
| 1797 | 1836 | ||
| 1798 | err = __cmd_script(&script); | 1837 | err = __cmd_script(&script); |
| 1799 | 1838 | ||
| 1839 | flush_scripting(); | ||
| 1840 | |||
| 1841 | out_delete: | ||
| 1800 | perf_session__delete(session); | 1842 | perf_session__delete(session); |
| 1801 | cleanup_scripting(); | 1843 | |
| 1844 | if (script_started) | ||
| 1845 | cleanup_scripting(); | ||
| 1802 | out: | 1846 | out: |
| 1803 | return err; | 1847 | return err; |
| 1804 | } | 1848 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 3e80aa10cfd8..055ce9232c9e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -43,6 +43,7 @@ | |||
| 43 | 43 | ||
| 44 | #include "perf.h" | 44 | #include "perf.h" |
| 45 | #include "builtin.h" | 45 | #include "builtin.h" |
| 46 | #include "util/cgroup.h" | ||
| 46 | #include "util/util.h" | 47 | #include "util/util.h" |
| 47 | #include "util/parse-options.h" | 48 | #include "util/parse-options.h" |
| 48 | #include "util/parse-events.h" | 49 | #include "util/parse-events.h" |
| @@ -593,7 +594,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
| 593 | 594 | ||
| 594 | if (perf_evlist__apply_filters(evsel_list)) { | 595 | if (perf_evlist__apply_filters(evsel_list)) { |
| 595 | error("failed to set filter with %d (%s)\n", errno, | 596 | error("failed to set filter with %d (%s)\n", errno, |
| 596 | strerror(errno)); | 597 | strerror_r(errno, msg, sizeof(msg))); |
| 597 | return -1; | 598 | return -1; |
| 598 | } | 599 | } |
| 599 | 600 | ||
| @@ -732,7 +733,7 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr) | |||
| 732 | } | 733 | } |
| 733 | } | 734 | } |
| 734 | 735 | ||
| 735 | static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | 736 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
| 736 | { | 737 | { |
| 737 | double msecs = avg / 1e6; | 738 | double msecs = avg / 1e6; |
| 738 | const char *fmt_v, *fmt_n; | 739 | const char *fmt_v, *fmt_n; |
| @@ -741,7 +742,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"; | 742 | fmt_v = csv_output ? "%.6f%s" : "%18.6f%s"; |
| 742 | fmt_n = csv_output ? "%s" : "%-25s"; | 743 | fmt_n = csv_output ? "%s" : "%-25s"; |
| 743 | 744 | ||
| 744 | aggr_printout(evsel, cpu, nr); | 745 | aggr_printout(evsel, id, nr); |
| 745 | 746 | ||
| 746 | scnprintf(name, sizeof(name), "%s%s", | 747 | scnprintf(name, sizeof(name), "%s%s", |
| 747 | perf_evsel__name(evsel), csv_output ? "" : " (msec)"); | 748 | perf_evsel__name(evsel), csv_output ? "" : " (msec)"); |
| @@ -947,11 +948,12 @@ static void print_ll_cache_misses(int cpu, | |||
| 947 | fprintf(output, " of all LL-cache hits "); | 948 | fprintf(output, " of all LL-cache hits "); |
| 948 | } | 949 | } |
| 949 | 950 | ||
| 950 | static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) | 951 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
| 951 | { | 952 | { |
| 952 | double total, ratio = 0.0, total2; | 953 | double total, ratio = 0.0, total2; |
| 953 | double sc = evsel->scale; | 954 | double sc = evsel->scale; |
| 954 | const char *fmt; | 955 | const char *fmt; |
| 956 | int cpu = cpu_map__id_to_cpu(id); | ||
| 955 | 957 | ||
| 956 | if (csv_output) { | 958 | if (csv_output) { |
| 957 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; | 959 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; |
| @@ -962,7 +964,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"; | 964 | fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s"; |
| 963 | } | 965 | } |
| 964 | 966 | ||
| 965 | aggr_printout(evsel, cpu, nr); | 967 | aggr_printout(evsel, id, nr); |
| 966 | 968 | ||
| 967 | if (aggr_mode == AGGR_GLOBAL) | 969 | if (aggr_mode == AGGR_GLOBAL) |
| 968 | cpu = 0; | 970 | 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..0aa7747ff139 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> |
| @@ -251,6 +251,7 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
| 251 | char bf[160]; | 251 | char bf[160]; |
| 252 | int printed = 0; | 252 | int printed = 0; |
| 253 | const int win_width = top->winsize.ws_col - 1; | 253 | const int win_width = top->winsize.ws_col - 1; |
| 254 | struct hists *hists = evsel__hists(top->sym_evsel); | ||
| 254 | 255 | ||
| 255 | puts(CONSOLE_CLEAR); | 256 | puts(CONSOLE_CLEAR); |
| 256 | 257 | ||
| @@ -261,13 +262,13 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
| 261 | 262 | ||
| 262 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); | 263 | printf("%-*.*s\n", win_width, win_width, graph_dotted_line); |
| 263 | 264 | ||
| 264 | if (top->sym_evsel->hists.stats.nr_lost_warned != | 265 | if (hists->stats.nr_lost_warned != |
| 265 | top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) { | 266 | hists->stats.nr_events[PERF_RECORD_LOST]) { |
| 266 | top->sym_evsel->hists.stats.nr_lost_warned = | 267 | hists->stats.nr_lost_warned = |
| 267 | top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | 268 | hists->stats.nr_events[PERF_RECORD_LOST]; |
| 268 | color_fprintf(stdout, PERF_COLOR_RED, | 269 | color_fprintf(stdout, PERF_COLOR_RED, |
| 269 | "WARNING: LOST %d chunks, Check IO/CPU overload", | 270 | "WARNING: LOST %d chunks, Check IO/CPU overload", |
| 270 | top->sym_evsel->hists.stats.nr_lost_warned); | 271 | hists->stats.nr_lost_warned); |
| 271 | ++printed; | 272 | ++printed; |
| 272 | } | 273 | } |
| 273 | 274 | ||
| @@ -276,16 +277,19 @@ static void perf_top__print_sym_table(struct perf_top *top) | |||
| 276 | return; | 277 | return; |
| 277 | } | 278 | } |
| 278 | 279 | ||
| 279 | hists__collapse_resort(&top->sym_evsel->hists, NULL); | 280 | if (top->zero) { |
| 280 | hists__output_resort(&top->sym_evsel->hists); | 281 | hists__delete_entries(hists); |
| 281 | hists__decay_entries(&top->sym_evsel->hists, | 282 | } else { |
| 282 | top->hide_user_symbols, | 283 | hists__decay_entries(hists, top->hide_user_symbols, |
| 283 | top->hide_kernel_symbols); | 284 | top->hide_kernel_symbols); |
| 284 | hists__output_recalc_col_len(&top->sym_evsel->hists, | 285 | } |
| 285 | top->print_entries - printed); | 286 | |
| 287 | hists__collapse_resort(hists, NULL); | ||
| 288 | hists__output_resort(hists); | ||
| 289 | |||
| 290 | hists__output_recalc_col_len(hists, top->print_entries - printed); | ||
| 286 | putchar('\n'); | 291 | putchar('\n'); |
| 287 | hists__fprintf(&top->sym_evsel->hists, false, | 292 | hists__fprintf(hists, false, top->print_entries - printed, win_width, |
| 288 | top->print_entries - printed, win_width, | ||
| 289 | top->min_percent, stdout); | 293 | top->min_percent, stdout); |
| 290 | } | 294 | } |
| 291 | 295 | ||
| @@ -328,6 +332,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg) | |||
| 328 | { | 332 | { |
| 329 | char *buf = malloc(0), *p; | 333 | char *buf = malloc(0), *p; |
| 330 | struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL; | 334 | struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL; |
| 335 | struct hists *hists = evsel__hists(top->sym_evsel); | ||
| 331 | struct rb_node *next; | 336 | struct rb_node *next; |
| 332 | size_t dummy = 0; | 337 | size_t dummy = 0; |
| 333 | 338 | ||
| @@ -345,7 +350,7 @@ static void perf_top__prompt_symbol(struct perf_top *top, const char *msg) | |||
| 345 | if (p) | 350 | if (p) |
| 346 | *p = 0; | 351 | *p = 0; |
| 347 | 352 | ||
| 348 | next = rb_first(&top->sym_evsel->hists.entries); | 353 | next = rb_first(&hists->entries); |
| 349 | while (next) { | 354 | while (next) { |
| 350 | n = rb_entry(next, struct hist_entry, rb_node); | 355 | n = rb_entry(next, struct hist_entry, rb_node); |
| 351 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { | 356 | if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) { |
| @@ -427,18 +432,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) | |||
| 427 | 432 | ||
| 428 | if (!perf_top__key_mapped(top, c)) { | 433 | if (!perf_top__key_mapped(top, c)) { |
| 429 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 434 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
| 430 | struct termios tc, save; | 435 | struct termios save; |
| 431 | 436 | ||
| 432 | perf_top__print_mapped_keys(top); | 437 | perf_top__print_mapped_keys(top); |
| 433 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); | 438 | fprintf(stdout, "\nEnter selection, or unmapped key to continue: "); |
| 434 | fflush(stdout); | 439 | fflush(stdout); |
| 435 | 440 | ||
| 436 | tcgetattr(0, &save); | 441 | 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 | 442 | ||
| 443 | poll(&stdin_poll, 1, -1); | 443 | poll(&stdin_poll, 1, -1); |
| 444 | c = getc(stdin); | 444 | c = getc(stdin); |
| @@ -537,16 +537,24 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c) | |||
| 537 | static void perf_top__sort_new_samples(void *arg) | 537 | static void perf_top__sort_new_samples(void *arg) |
| 538 | { | 538 | { |
| 539 | struct perf_top *t = arg; | 539 | struct perf_top *t = arg; |
| 540 | struct hists *hists; | ||
| 541 | |||
| 540 | perf_top__reset_sample_counters(t); | 542 | perf_top__reset_sample_counters(t); |
| 541 | 543 | ||
| 542 | if (t->evlist->selected != NULL) | 544 | if (t->evlist->selected != NULL) |
| 543 | t->sym_evsel = t->evlist->selected; | 545 | t->sym_evsel = t->evlist->selected; |
| 544 | 546 | ||
| 545 | hists__collapse_resort(&t->sym_evsel->hists, NULL); | 547 | hists = evsel__hists(t->sym_evsel); |
| 546 | hists__output_resort(&t->sym_evsel->hists); | 548 | |
| 547 | hists__decay_entries(&t->sym_evsel->hists, | 549 | if (t->zero) { |
| 548 | t->hide_user_symbols, | 550 | hists__delete_entries(hists); |
| 549 | t->hide_kernel_symbols); | 551 | } else { |
| 552 | hists__decay_entries(hists, t->hide_user_symbols, | ||
| 553 | t->hide_kernel_symbols); | ||
| 554 | } | ||
| 555 | |||
| 556 | hists__collapse_resort(hists, NULL); | ||
| 557 | hists__output_resort(hists); | ||
| 550 | } | 558 | } |
| 551 | 559 | ||
| 552 | static void *display_thread_tui(void *arg) | 560 | static void *display_thread_tui(void *arg) |
| @@ -567,8 +575,10 @@ static void *display_thread_tui(void *arg) | |||
| 567 | * Zooming in/out UIDs. For now juse use whatever the user passed | 575 | * Zooming in/out UIDs. For now juse use whatever the user passed |
| 568 | * via --uid. | 576 | * via --uid. |
| 569 | */ | 577 | */ |
| 570 | evlist__for_each(top->evlist, pos) | 578 | evlist__for_each(top->evlist, pos) { |
| 571 | pos->hists.uid_filter_str = top->record_opts.target.uid_str; | 579 | struct hists *hists = evsel__hists(pos); |
| 580 | hists->uid_filter_str = top->record_opts.target.uid_str; | ||
| 581 | } | ||
| 572 | 582 | ||
| 573 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, | 583 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, top->min_percent, |
| 574 | &top->session->header.env); | 584 | &top->session->header.env); |
| @@ -577,23 +587,32 @@ static void *display_thread_tui(void *arg) | |||
| 577 | return NULL; | 587 | return NULL; |
| 578 | } | 588 | } |
| 579 | 589 | ||
| 590 | static void display_sig(int sig __maybe_unused) | ||
| 591 | { | ||
| 592 | done = 1; | ||
| 593 | } | ||
| 594 | |||
| 595 | static void display_setup_sig(void) | ||
| 596 | { | ||
| 597 | signal(SIGSEGV, display_sig); | ||
| 598 | signal(SIGFPE, display_sig); | ||
| 599 | signal(SIGINT, display_sig); | ||
| 600 | signal(SIGQUIT, display_sig); | ||
| 601 | signal(SIGTERM, display_sig); | ||
| 602 | } | ||
| 603 | |||
| 580 | static void *display_thread(void *arg) | 604 | static void *display_thread(void *arg) |
| 581 | { | 605 | { |
| 582 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 606 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
| 583 | struct termios tc, save; | 607 | struct termios save; |
| 584 | struct perf_top *top = arg; | 608 | struct perf_top *top = arg; |
| 585 | int delay_msecs, c; | 609 | int delay_msecs, c; |
| 586 | 610 | ||
| 587 | tcgetattr(0, &save); | 611 | 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(); | 612 | pthread__unblock_sigwinch(); |
| 594 | repeat: | 613 | repeat: |
| 595 | delay_msecs = top->delay_secs * 1000; | 614 | delay_msecs = top->delay_secs * 1000; |
| 596 | tcsetattr(0, TCSANOW, &tc); | 615 | set_term_quiet_input(&save); |
| 597 | /* trash return*/ | 616 | /* trash return*/ |
| 598 | getc(stdin); | 617 | getc(stdin); |
| 599 | 618 | ||
| @@ -620,13 +639,16 @@ repeat: | |||
| 620 | } | 639 | } |
| 621 | } | 640 | } |
| 622 | 641 | ||
| 642 | tcsetattr(0, TCSAFLUSH, &save); | ||
| 623 | return NULL; | 643 | return NULL; |
| 624 | } | 644 | } |
| 625 | 645 | ||
| 626 | static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym) | 646 | static int symbol_filter(struct map *map, struct symbol *sym) |
| 627 | { | 647 | { |
| 628 | const char *name = sym->name; | 648 | const char *name = sym->name; |
| 629 | 649 | ||
| 650 | if (!map->dso->kernel) | ||
| 651 | return 0; | ||
| 630 | /* | 652 | /* |
| 631 | * ppc64 uses function descriptors and appends a '.' to the | 653 | * ppc64 uses function descriptors and appends a '.' to the |
| 632 | * start of every instruction address. Remove it. | 654 | * start of every instruction address. Remove it. |
| @@ -750,6 +772,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
| 750 | } | 772 | } |
| 751 | 773 | ||
| 752 | if (al.sym == NULL || !al.sym->ignore) { | 774 | if (al.sym == NULL || !al.sym->ignore) { |
| 775 | struct hists *hists = evsel__hists(evsel); | ||
| 753 | struct hist_entry_iter iter = { | 776 | struct hist_entry_iter iter = { |
| 754 | .add_entry_cb = hist_iter__top_callback, | 777 | .add_entry_cb = hist_iter__top_callback, |
| 755 | }; | 778 | }; |
| @@ -759,14 +782,14 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
| 759 | else | 782 | else |
| 760 | iter.ops = &hist_iter_normal; | 783 | iter.ops = &hist_iter_normal; |
| 761 | 784 | ||
| 762 | pthread_mutex_lock(&evsel->hists.lock); | 785 | pthread_mutex_lock(&hists->lock); |
| 763 | 786 | ||
| 764 | err = hist_entry_iter__add(&iter, &al, evsel, sample, | 787 | err = hist_entry_iter__add(&iter, &al, evsel, sample, |
| 765 | top->max_stack, top); | 788 | top->max_stack, top); |
| 766 | if (err < 0) | 789 | if (err < 0) |
| 767 | pr_err("Problem incrementing symbol period, skipping event\n"); | 790 | pr_err("Problem incrementing symbol period, skipping event\n"); |
| 768 | 791 | ||
| 769 | pthread_mutex_unlock(&evsel->hists.lock); | 792 | pthread_mutex_unlock(&hists->lock); |
| 770 | } | 793 | } |
| 771 | 794 | ||
| 772 | return; | 795 | return; |
| @@ -831,7 +854,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
| 831 | perf_event__process_sample(&top->tool, event, evsel, | 854 | perf_event__process_sample(&top->tool, event, evsel, |
| 832 | &sample, machine); | 855 | &sample, machine); |
| 833 | } else if (event->header.type < PERF_RECORD_MAX) { | 856 | } else if (event->header.type < PERF_RECORD_MAX) { |
| 834 | hists__inc_nr_events(&evsel->hists, event->header.type); | 857 | hists__inc_nr_events(evsel__hists(evsel), event->header.type); |
| 835 | machine__process_event(machine, event, &sample); | 858 | machine__process_event(machine, event, &sample); |
| 836 | } else | 859 | } else |
| 837 | ++session->stats.nr_unknown_events; | 860 | ++session->stats.nr_unknown_events; |
| @@ -876,7 +899,7 @@ try_again: | |||
| 876 | 899 | ||
| 877 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { | 900 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { |
| 878 | ui__error("Failed to mmap with %d (%s)\n", | 901 | ui__error("Failed to mmap with %d (%s)\n", |
| 879 | errno, strerror(errno)); | 902 | errno, strerror_r(errno, msg, sizeof(msg))); |
| 880 | goto out_err; | 903 | goto out_err; |
| 881 | } | 904 | } |
| 882 | 905 | ||
| @@ -911,7 +934,7 @@ static int __cmd_top(struct perf_top *top) | |||
| 911 | 934 | ||
| 912 | top->session = perf_session__new(NULL, false, NULL); | 935 | top->session = perf_session__new(NULL, false, NULL); |
| 913 | if (top->session == NULL) | 936 | if (top->session == NULL) |
| 914 | return -ENOMEM; | 937 | return -1; |
| 915 | 938 | ||
| 916 | machines__set_symbol_filter(&top->session->machines, symbol_filter); | 939 | machines__set_symbol_filter(&top->session->machines, symbol_filter); |
| 917 | 940 | ||
| @@ -946,7 +969,7 @@ static int __cmd_top(struct perf_top *top) | |||
| 946 | perf_evlist__enable(top->evlist); | 969 | perf_evlist__enable(top->evlist); |
| 947 | 970 | ||
| 948 | /* Wait for a minimal set of events before starting the snapshot */ | 971 | /* Wait for a minimal set of events before starting the snapshot */ |
| 949 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 972 | perf_evlist__poll(top->evlist, 100); |
| 950 | 973 | ||
| 951 | perf_top__mmap_read(top); | 974 | perf_top__mmap_read(top); |
| 952 | 975 | ||
| @@ -963,7 +986,7 @@ static int __cmd_top(struct perf_top *top) | |||
| 963 | param.sched_priority = top->realtime_prio; | 986 | param.sched_priority = top->realtime_prio; |
| 964 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 987 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
| 965 | ui__error("Could not set realtime priority.\n"); | 988 | ui__error("Could not set realtime priority.\n"); |
| 966 | goto out_delete; | 989 | goto out_join; |
| 967 | } | 990 | } |
| 968 | } | 991 | } |
| 969 | 992 | ||
| @@ -973,10 +996,12 @@ static int __cmd_top(struct perf_top *top) | |||
| 973 | perf_top__mmap_read(top); | 996 | perf_top__mmap_read(top); |
| 974 | 997 | ||
| 975 | if (hits == top->samples) | 998 | if (hits == top->samples) |
| 976 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 999 | ret = perf_evlist__poll(top->evlist, 100); |
| 977 | } | 1000 | } |
| 978 | 1001 | ||
| 979 | ret = 0; | 1002 | ret = 0; |
| 1003 | out_join: | ||
| 1004 | pthread_join(thread, NULL); | ||
| 980 | out_delete: | 1005 | out_delete: |
| 981 | perf_session__delete(top->session); | 1006 | perf_session__delete(top->session); |
| 982 | top->session = NULL; | 1007 | top->session = NULL; |
| @@ -1000,10 +1025,8 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
| 1000 | 1025 | ||
| 1001 | static int perf_top_config(const char *var, const char *value, void *cb) | 1026 | static int perf_top_config(const char *var, const char *value, void *cb) |
| 1002 | { | 1027 | { |
| 1003 | struct perf_top *top = cb; | ||
| 1004 | |||
| 1005 | if (!strcmp(var, "top.call-graph")) | 1028 | if (!strcmp(var, "top.call-graph")) |
| 1006 | return record_parse_callchain(value, &top->record_opts); | 1029 | var = "call-graph.record-mode"; /* fall-through */ |
| 1007 | if (!strcmp(var, "top.children")) { | 1030 | if (!strcmp(var, "top.children")) { |
| 1008 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); | 1031 | symbol_conf.cumulate_callchain = perf_config_bool(var, value); |
| 1009 | return 0; | 1032 | return 0; |
| @@ -1024,7 +1047,6 @@ parse_percent_limit(const struct option *opt, const char *arg, | |||
| 1024 | 1047 | ||
| 1025 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | 1048 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) |
| 1026 | { | 1049 | { |
| 1027 | int status = -1; | ||
| 1028 | char errbuf[BUFSIZ]; | 1050 | char errbuf[BUFSIZ]; |
| 1029 | struct perf_top top = { | 1051 | struct perf_top top = { |
| 1030 | .count_filter = 5, | 1052 | .count_filter = 5, |
| @@ -1122,6 +1144,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1122 | "Interleave source code with assembly code (default)"), | 1144 | "Interleave source code with assembly code (default)"), |
| 1123 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | 1145 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, |
| 1124 | "Display raw encoding of assembly instructions (default)"), | 1146 | "Display raw encoding of assembly instructions (default)"), |
| 1147 | OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, | ||
| 1148 | "Enable kernel symbol demangling"), | ||
| 1125 | OPT_STRING(0, "objdump", &objdump_path, "path", | 1149 | OPT_STRING(0, "objdump", &objdump_path, "path", |
| 1126 | "objdump binary to use for disassembly and annotations"), | 1150 | "objdump binary to use for disassembly and annotations"), |
| 1127 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1151 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
| @@ -1131,12 +1155,19 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1131 | "Don't show entries under that percent", parse_percent_limit), | 1155 | "Don't show entries under that percent", parse_percent_limit), |
| 1132 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | 1156 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", |
| 1133 | "How to display percentage of filtered entries", parse_filter_percentage), | 1157 | "How to display percentage of filtered entries", parse_filter_percentage), |
| 1158 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, | ||
| 1159 | "width[,width...]", | ||
| 1160 | "don't try to adjust column width, use these fixed values"), | ||
| 1134 | OPT_END() | 1161 | OPT_END() |
| 1135 | }; | 1162 | }; |
| 1136 | const char * const top_usage[] = { | 1163 | const char * const top_usage[] = { |
| 1137 | "perf top [<options>]", | 1164 | "perf top [<options>]", |
| 1138 | NULL | 1165 | NULL |
| 1139 | }; | 1166 | }; |
| 1167 | int status = hists__init(); | ||
| 1168 | |||
| 1169 | if (status < 0) | ||
| 1170 | return status; | ||
| 1140 | 1171 | ||
| 1141 | top.evlist = perf_evlist__new(); | 1172 | top.evlist = perf_evlist__new(); |
| 1142 | if (top.evlist == NULL) | 1173 | if (top.evlist == NULL) |
| @@ -1217,7 +1248,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1217 | symbol_conf.priv_size = sizeof(struct annotation); | 1248 | symbol_conf.priv_size = sizeof(struct annotation); |
| 1218 | 1249 | ||
| 1219 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1250 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
| 1220 | if (symbol__init() < 0) | 1251 | if (symbol__init(NULL) < 0) |
| 1221 | return -1; | 1252 | return -1; |
| 1222 | 1253 | ||
| 1223 | sort__setup_elide(stdout); | 1254 | sort__setup_elide(stdout); |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index a6c375224f46..fb126459b134 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 */ }, }, |
| @@ -1163,13 +1189,13 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) | |||
| 1163 | if (thread == NULL) | 1189 | if (thread == NULL) |
| 1164 | goto fail; | 1190 | goto fail; |
| 1165 | 1191 | ||
| 1166 | if (thread->priv == NULL) | 1192 | if (thread__priv(thread) == NULL) |
| 1167 | thread->priv = thread_trace__new(); | 1193 | thread__set_priv(thread, thread_trace__new()); |
| 1168 | 1194 | ||
| 1169 | if (thread->priv == NULL) | 1195 | if (thread__priv(thread) == NULL) |
| 1170 | goto fail; | 1196 | goto fail; |
| 1171 | 1197 | ||
| 1172 | ttrace = thread->priv; | 1198 | ttrace = thread__priv(thread); |
| 1173 | ++ttrace->nr_events; | 1199 | ++ttrace->nr_events; |
| 1174 | 1200 | ||
| 1175 | return ttrace; | 1201 | return ttrace; |
| @@ -1222,7 +1248,7 @@ struct trace { | |||
| 1222 | 1248 | ||
| 1223 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) | 1249 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) |
| 1224 | { | 1250 | { |
| 1225 | struct thread_trace *ttrace = thread->priv; | 1251 | struct thread_trace *ttrace = thread__priv(thread); |
| 1226 | 1252 | ||
| 1227 | if (fd > ttrace->paths.max) { | 1253 | if (fd > ttrace->paths.max) { |
| 1228 | char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); | 1254 | char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); |
| @@ -1275,7 +1301,7 @@ static int thread__read_fd_path(struct thread *thread, int fd) | |||
| 1275 | static const char *thread__fd_path(struct thread *thread, int fd, | 1301 | static const char *thread__fd_path(struct thread *thread, int fd, |
| 1276 | struct trace *trace) | 1302 | struct trace *trace) |
| 1277 | { | 1303 | { |
| 1278 | struct thread_trace *ttrace = thread->priv; | 1304 | struct thread_trace *ttrace = thread__priv(thread); |
| 1279 | 1305 | ||
| 1280 | if (ttrace == NULL) | 1306 | if (ttrace == NULL) |
| 1281 | return NULL; | 1307 | return NULL; |
| @@ -1312,7 +1338,7 @@ static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, | |||
| 1312 | { | 1338 | { |
| 1313 | int fd = arg->val; | 1339 | int fd = arg->val; |
| 1314 | size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); | 1340 | size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); |
| 1315 | struct thread_trace *ttrace = arg->thread->priv; | 1341 | struct thread_trace *ttrace = thread__priv(arg->thread); |
| 1316 | 1342 | ||
| 1317 | if (ttrace && fd >= 0 && fd <= ttrace->paths.max) | 1343 | if (ttrace && fd >= 0 && fd <= ttrace->paths.max) |
| 1318 | zfree(&ttrace->paths.table[fd]); | 1344 | zfree(&ttrace->paths.table[fd]); |
| @@ -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 | ||
| @@ -2348,7 +2381,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) | |||
| 2348 | FILE *fp = data->fp; | 2381 | FILE *fp = data->fp; |
| 2349 | size_t printed = data->printed; | 2382 | size_t printed = data->printed; |
| 2350 | struct trace *trace = data->trace; | 2383 | struct trace *trace = data->trace; |
| 2351 | struct thread_trace *ttrace = thread->priv; | 2384 | struct thread_trace *ttrace = thread__priv(thread); |
| 2352 | double ratio; | 2385 | double ratio; |
| 2353 | 2386 | ||
| 2354 | if (ttrace == NULL) | 2387 | if (ttrace == NULL) |
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-sys.h b/tools/perf/perf-sys.h index 937e4324ad94..a3b13d7dc1d4 100644 --- a/tools/perf/perf-sys.h +++ b/tools/perf/perf-sys.h | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | 13 | #define wmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") |
| 14 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | 14 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") |
| 15 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 15 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
| 16 | #define CPUINFO_PROC "model name" | 16 | #define CPUINFO_PROC {"model name"} |
| 17 | #ifndef __NR_perf_event_open | 17 | #ifndef __NR_perf_event_open |
| 18 | # define __NR_perf_event_open 336 | 18 | # define __NR_perf_event_open 336 |
| 19 | #endif | 19 | #endif |
| @@ -30,7 +30,7 @@ | |||
| 30 | #define wmb() asm volatile("sfence" ::: "memory") | 30 | #define wmb() asm volatile("sfence" ::: "memory") |
| 31 | #define rmb() asm volatile("lfence" ::: "memory") | 31 | #define rmb() asm volatile("lfence" ::: "memory") |
| 32 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 32 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
| 33 | #define CPUINFO_PROC "model name" | 33 | #define CPUINFO_PROC {"model name"} |
| 34 | #ifndef __NR_perf_event_open | 34 | #ifndef __NR_perf_event_open |
| 35 | # define __NR_perf_event_open 298 | 35 | # define __NR_perf_event_open 298 |
| 36 | #endif | 36 | #endif |
| @@ -47,14 +47,14 @@ | |||
| 47 | #define mb() asm volatile ("sync" ::: "memory") | 47 | #define mb() asm volatile ("sync" ::: "memory") |
| 48 | #define wmb() asm volatile ("sync" ::: "memory") | 48 | #define wmb() asm volatile ("sync" ::: "memory") |
| 49 | #define rmb() asm volatile ("sync" ::: "memory") | 49 | #define rmb() asm volatile ("sync" ::: "memory") |
| 50 | #define CPUINFO_PROC "cpu" | 50 | #define CPUINFO_PROC {"cpu"} |
| 51 | #endif | 51 | #endif |
| 52 | 52 | ||
| 53 | #ifdef __s390__ | 53 | #ifdef __s390__ |
| 54 | #define mb() asm volatile("bcr 15,0" ::: "memory") | 54 | #define mb() asm volatile("bcr 15,0" ::: "memory") |
| 55 | #define wmb() asm volatile("bcr 15,0" ::: "memory") | 55 | #define wmb() asm volatile("bcr 15,0" ::: "memory") |
| 56 | #define rmb() asm volatile("bcr 15,0" ::: "memory") | 56 | #define rmb() asm volatile("bcr 15,0" ::: "memory") |
| 57 | #define CPUINFO_PROC "vendor_id" | 57 | #define CPUINFO_PROC {"vendor_id"} |
| 58 | #endif | 58 | #endif |
| 59 | 59 | ||
| 60 | #ifdef __sh__ | 60 | #ifdef __sh__ |
| @@ -67,14 +67,14 @@ | |||
| 67 | # define wmb() asm volatile("" ::: "memory") | 67 | # define wmb() asm volatile("" ::: "memory") |
| 68 | # define rmb() asm volatile("" ::: "memory") | 68 | # define rmb() asm volatile("" ::: "memory") |
| 69 | #endif | 69 | #endif |
| 70 | #define CPUINFO_PROC "cpu type" | 70 | #define CPUINFO_PROC {"cpu type"} |
| 71 | #endif | 71 | #endif |
| 72 | 72 | ||
| 73 | #ifdef __hppa__ | 73 | #ifdef __hppa__ |
| 74 | #define mb() asm volatile("" ::: "memory") | 74 | #define mb() asm volatile("" ::: "memory") |
| 75 | #define wmb() asm volatile("" ::: "memory") | 75 | #define wmb() asm volatile("" ::: "memory") |
| 76 | #define rmb() asm volatile("" ::: "memory") | 76 | #define rmb() asm volatile("" ::: "memory") |
| 77 | #define CPUINFO_PROC "cpu" | 77 | #define CPUINFO_PROC {"cpu"} |
| 78 | #endif | 78 | #endif |
| 79 | 79 | ||
| 80 | #ifdef __sparc__ | 80 | #ifdef __sparc__ |
| @@ -87,14 +87,14 @@ | |||
| 87 | #endif | 87 | #endif |
| 88 | #define wmb() asm volatile("":::"memory") | 88 | #define wmb() asm volatile("":::"memory") |
| 89 | #define rmb() asm volatile("":::"memory") | 89 | #define rmb() asm volatile("":::"memory") |
| 90 | #define CPUINFO_PROC "cpu" | 90 | #define CPUINFO_PROC {"cpu"} |
| 91 | #endif | 91 | #endif |
| 92 | 92 | ||
| 93 | #ifdef __alpha__ | 93 | #ifdef __alpha__ |
| 94 | #define mb() asm volatile("mb" ::: "memory") | 94 | #define mb() asm volatile("mb" ::: "memory") |
| 95 | #define wmb() asm volatile("wmb" ::: "memory") | 95 | #define wmb() asm volatile("wmb" ::: "memory") |
| 96 | #define rmb() asm volatile("mb" ::: "memory") | 96 | #define rmb() asm volatile("mb" ::: "memory") |
| 97 | #define CPUINFO_PROC "cpu model" | 97 | #define CPUINFO_PROC {"cpu model"} |
| 98 | #endif | 98 | #endif |
| 99 | 99 | ||
| 100 | #ifdef __ia64__ | 100 | #ifdef __ia64__ |
| @@ -102,7 +102,7 @@ | |||
| 102 | #define wmb() asm volatile ("mf" ::: "memory") | 102 | #define wmb() asm volatile ("mf" ::: "memory") |
| 103 | #define rmb() asm volatile ("mf" ::: "memory") | 103 | #define rmb() asm volatile ("mf" ::: "memory") |
| 104 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") | 104 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") |
| 105 | #define CPUINFO_PROC "model name" | 105 | #define CPUINFO_PROC {"model name"} |
| 106 | #endif | 106 | #endif |
| 107 | 107 | ||
| 108 | #ifdef __arm__ | 108 | #ifdef __arm__ |
| @@ -113,7 +113,7 @@ | |||
| 113 | #define mb() ((void(*)(void))0xffff0fa0)() | 113 | #define mb() ((void(*)(void))0xffff0fa0)() |
| 114 | #define wmb() ((void(*)(void))0xffff0fa0)() | 114 | #define wmb() ((void(*)(void))0xffff0fa0)() |
| 115 | #define rmb() ((void(*)(void))0xffff0fa0)() | 115 | #define rmb() ((void(*)(void))0xffff0fa0)() |
| 116 | #define CPUINFO_PROC "Processor" | 116 | #define CPUINFO_PROC {"model name", "Processor"} |
| 117 | #endif | 117 | #endif |
| 118 | 118 | ||
| 119 | #ifdef __aarch64__ | 119 | #ifdef __aarch64__ |
| @@ -133,28 +133,28 @@ | |||
| 133 | : "memory") | 133 | : "memory") |
| 134 | #define wmb() mb() | 134 | #define wmb() mb() |
| 135 | #define rmb() mb() | 135 | #define rmb() mb() |
| 136 | #define CPUINFO_PROC "cpu model" | 136 | #define CPUINFO_PROC {"cpu model"} |
| 137 | #endif | 137 | #endif |
| 138 | 138 | ||
| 139 | #ifdef __arc__ | 139 | #ifdef __arc__ |
| 140 | #define mb() asm volatile("" ::: "memory") | 140 | #define mb() asm volatile("" ::: "memory") |
| 141 | #define wmb() asm volatile("" ::: "memory") | 141 | #define wmb() asm volatile("" ::: "memory") |
| 142 | #define rmb() asm volatile("" ::: "memory") | 142 | #define rmb() asm volatile("" ::: "memory") |
| 143 | #define CPUINFO_PROC "Processor" | 143 | #define CPUINFO_PROC {"Processor"} |
| 144 | #endif | 144 | #endif |
| 145 | 145 | ||
| 146 | #ifdef __metag__ | 146 | #ifdef __metag__ |
| 147 | #define mb() asm volatile("" ::: "memory") | 147 | #define mb() asm volatile("" ::: "memory") |
| 148 | #define wmb() asm volatile("" ::: "memory") | 148 | #define wmb() asm volatile("" ::: "memory") |
| 149 | #define rmb() asm volatile("" ::: "memory") | 149 | #define rmb() asm volatile("" ::: "memory") |
| 150 | #define CPUINFO_PROC "CPU" | 150 | #define CPUINFO_PROC {"CPU"} |
| 151 | #endif | 151 | #endif |
| 152 | 152 | ||
| 153 | #ifdef __xtensa__ | 153 | #ifdef __xtensa__ |
| 154 | #define mb() asm volatile("memw" ::: "memory") | 154 | #define mb() asm volatile("memw" ::: "memory") |
| 155 | #define wmb() asm volatile("memw" ::: "memory") | 155 | #define wmb() asm volatile("memw" ::: "memory") |
| 156 | #define rmb() asm volatile("" ::: "memory") | 156 | #define rmb() asm volatile("" ::: "memory") |
| 157 | #define CPUINFO_PROC "core ID" | 157 | #define CPUINFO_PROC {"core ID"} |
| 158 | #endif | 158 | #endif |
| 159 | 159 | ||
| 160 | #ifdef __tile__ | 160 | #ifdef __tile__ |
| @@ -162,7 +162,7 @@ | |||
| 162 | #define wmb() asm volatile ("mf" ::: "memory") | 162 | #define wmb() asm volatile ("mf" ::: "memory") |
| 163 | #define rmb() asm volatile ("mf" ::: "memory") | 163 | #define rmb() asm volatile ("mf" ::: "memory") |
| 164 | #define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory") | 164 | #define cpu_relax() asm volatile ("mfspr zero, PASS" ::: "memory") |
| 165 | #define CPUINFO_PROC "model name" | 165 | #define CPUINFO_PROC {"model name"} |
| 166 | #endif | 166 | #endif |
| 167 | 167 | ||
| 168 | #define barrier() asm volatile ("" ::: "memory") | 168 | #define barrier() asm volatile ("" ::: "memory") |
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..162c978f1491 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <unistd.h> | 6 | #include <unistd.h> |
| 7 | #include <string.h> | 7 | #include <string.h> |
| 8 | #include "builtin.h" | 8 | #include "builtin.h" |
| 9 | #include "hist.h" | ||
| 9 | #include "intlist.h" | 10 | #include "intlist.h" |
| 10 | #include "tests.h" | 11 | #include "tests.h" |
| 11 | #include "debug.h" | 12 | #include "debug.h" |
| @@ -154,6 +155,18 @@ static struct test { | |||
| 154 | .func = test__hists_cumulate, | 155 | .func = test__hists_cumulate, |
| 155 | }, | 156 | }, |
| 156 | { | 157 | { |
| 158 | .desc = "Test tracking with sched_switch", | ||
| 159 | .func = test__switch_tracking, | ||
| 160 | }, | ||
| 161 | { | ||
| 162 | .desc = "Filter fds with revents mask in a fdarray", | ||
| 163 | .func = test__fdarray__filter, | ||
| 164 | }, | ||
| 165 | { | ||
| 166 | .desc = "Add fd to a fdarray, making it autogrow", | ||
| 167 | .func = test__fdarray__add, | ||
| 168 | }, | ||
| 169 | { | ||
| 157 | .func = NULL, | 170 | .func = NULL, |
| 158 | }, | 171 | }, |
| 159 | }; | 172 | }; |
| @@ -185,9 +198,11 @@ static bool perf_test__matches(int curr, int argc, const char *argv[]) | |||
| 185 | static int run_test(struct test *test) | 198 | static int run_test(struct test *test) |
| 186 | { | 199 | { |
| 187 | int status, err = -1, child = fork(); | 200 | int status, err = -1, child = fork(); |
| 201 | char sbuf[STRERR_BUFSIZE]; | ||
| 188 | 202 | ||
| 189 | if (child < 0) { | 203 | if (child < 0) { |
| 190 | pr_err("failed to fork test: %s\n", strerror(errno)); | 204 | pr_err("failed to fork test: %s\n", |
| 205 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 191 | return -1; | 206 | return -1; |
| 192 | } | 207 | } |
| 193 | 208 | ||
| @@ -288,6 +303,10 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 288 | OPT_END() | 303 | OPT_END() |
| 289 | }; | 304 | }; |
| 290 | struct intlist *skiplist = NULL; | 305 | struct intlist *skiplist = NULL; |
| 306 | int ret = hists__init(); | ||
| 307 | |||
| 308 | if (ret < 0) | ||
| 309 | return ret; | ||
| 291 | 310 | ||
| 292 | argc = parse_options(argc, argv, test_options, test_usage, 0); | 311 | argc = parse_options(argc, argv, test_options, test_usage, 0); |
| 293 | if (argc >= 1 && !strcmp(argv[0], "list")) | 312 | if (argc >= 1 && !strcmp(argv[0], "list")) |
| @@ -297,7 +316,7 @@ int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 297 | symbol_conf.sort_by_name = true; | 316 | symbol_conf.sort_by_name = true; |
| 298 | symbol_conf.try_vmlinux_path = true; | 317 | symbol_conf.try_vmlinux_path = true; |
| 299 | 318 | ||
| 300 | if (symbol__init() < 0) | 319 | if (symbol__init(NULL) < 0) |
| 301 | return -1; | 320 | return -1; |
| 302 | 321 | ||
| 303 | if (skip != NULL) | 322 | if (skip != NULL) |
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 96adb730b744..fc25e57f4a5d 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "perf_regs.h" | 9 | #include "perf_regs.h" |
| 10 | #include "map.h" | 10 | #include "map.h" |
| 11 | #include "thread.h" | 11 | #include "thread.h" |
| 12 | #include "callchain.h" | ||
| 12 | 13 | ||
| 13 | static int mmap_handler(struct perf_tool *tool __maybe_unused, | 14 | static int mmap_handler(struct perf_tool *tool __maybe_unused, |
| 14 | union perf_event *event, | 15 | union perf_event *event, |
| @@ -120,6 +121,8 @@ int test__dwarf_unwind(void) | |||
| 120 | return -1; | 121 | return -1; |
| 121 | } | 122 | } |
| 122 | 123 | ||
| 124 | callchain_param.record_mode = CALLCHAIN_DWARF; | ||
| 125 | |||
| 123 | if (init_live_machine(machine)) { | 126 | if (init_live_machine(machine)) { |
| 124 | pr_err("Could not init machine\n"); | 127 | pr_err("Could not init machine\n"); |
| 125 | goto out; | 128 | goto out; |
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/hists_cumulate.c b/tools/perf/tests/hists_cumulate.c index 0ac240db2e24..614d5c4978ab 100644 --- a/tools/perf/tests/hists_cumulate.c +++ b/tools/perf/tests/hists_cumulate.c | |||
| @@ -245,7 +245,7 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec | |||
| 245 | static int test1(struct perf_evsel *evsel, struct machine *machine) | 245 | static int test1(struct perf_evsel *evsel, struct machine *machine) |
| 246 | { | 246 | { |
| 247 | int err; | 247 | int err; |
| 248 | struct hists *hists = &evsel->hists; | 248 | struct hists *hists = evsel__hists(evsel); |
| 249 | /* | 249 | /* |
| 250 | * expected output: | 250 | * expected output: |
| 251 | * | 251 | * |
| @@ -295,7 +295,7 @@ out: | |||
| 295 | static int test2(struct perf_evsel *evsel, struct machine *machine) | 295 | static int test2(struct perf_evsel *evsel, struct machine *machine) |
| 296 | { | 296 | { |
| 297 | int err; | 297 | int err; |
| 298 | struct hists *hists = &evsel->hists; | 298 | struct hists *hists = evsel__hists(evsel); |
| 299 | /* | 299 | /* |
| 300 | * expected output: | 300 | * expected output: |
| 301 | * | 301 | * |
| @@ -442,7 +442,7 @@ out: | |||
| 442 | static int test3(struct perf_evsel *evsel, struct machine *machine) | 442 | static int test3(struct perf_evsel *evsel, struct machine *machine) |
| 443 | { | 443 | { |
| 444 | int err; | 444 | int err; |
| 445 | struct hists *hists = &evsel->hists; | 445 | struct hists *hists = evsel__hists(evsel); |
| 446 | /* | 446 | /* |
| 447 | * expected output: | 447 | * expected output: |
| 448 | * | 448 | * |
| @@ -498,7 +498,7 @@ out: | |||
| 498 | static int test4(struct perf_evsel *evsel, struct machine *machine) | 498 | static int test4(struct perf_evsel *evsel, struct machine *machine) |
| 499 | { | 499 | { |
| 500 | int err; | 500 | int err; |
| 501 | struct hists *hists = &evsel->hists; | 501 | struct hists *hists = evsel__hists(evsel); |
| 502 | /* | 502 | /* |
| 503 | * expected output: | 503 | * expected output: |
| 504 | * | 504 | * |
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c index 821f581fd930..5a31787cc6b9 100644 --- a/tools/perf/tests/hists_filter.c +++ b/tools/perf/tests/hists_filter.c | |||
| @@ -66,11 +66,12 @@ static int add_hist_entries(struct perf_evlist *evlist, | |||
| 66 | .ops = &hist_iter_normal, | 66 | .ops = &hist_iter_normal, |
| 67 | .hide_unresolved = false, | 67 | .hide_unresolved = false, |
| 68 | }; | 68 | }; |
| 69 | struct hists *hists = evsel__hists(evsel); | ||
| 69 | 70 | ||
| 70 | /* make sure it has no filter at first */ | 71 | /* make sure it has no filter at first */ |
| 71 | evsel->hists.thread_filter = NULL; | 72 | hists->thread_filter = NULL; |
| 72 | evsel->hists.dso_filter = NULL; | 73 | hists->dso_filter = NULL; |
| 73 | evsel->hists.symbol_filter_str = NULL; | 74 | hists->symbol_filter_str = NULL; |
| 74 | 75 | ||
| 75 | sample.pid = fake_samples[i].pid; | 76 | sample.pid = fake_samples[i].pid; |
| 76 | sample.tid = fake_samples[i].pid; | 77 | sample.tid = fake_samples[i].pid; |
| @@ -134,7 +135,7 @@ int test__hists_filter(void) | |||
| 134 | goto out; | 135 | goto out; |
| 135 | 136 | ||
| 136 | evlist__for_each(evlist, evsel) { | 137 | evlist__for_each(evlist, evsel) { |
| 137 | struct hists *hists = &evsel->hists; | 138 | struct hists *hists = evsel__hists(evsel); |
| 138 | 139 | ||
| 139 | hists__collapse_resort(hists, NULL); | 140 | hists__collapse_resort(hists, NULL); |
| 140 | hists__output_resort(hists); | 141 | hists__output_resort(hists); |
| @@ -160,7 +161,7 @@ int test__hists_filter(void) | |||
| 160 | hists->stats.total_non_filtered_period); | 161 | hists->stats.total_non_filtered_period); |
| 161 | 162 | ||
| 162 | /* now applying thread filter for 'bash' */ | 163 | /* now applying thread filter for 'bash' */ |
| 163 | evsel->hists.thread_filter = fake_samples[9].thread; | 164 | hists->thread_filter = fake_samples[9].thread; |
| 164 | hists__filter_by_thread(hists); | 165 | hists__filter_by_thread(hists); |
| 165 | 166 | ||
| 166 | if (verbose > 2) { | 167 | if (verbose > 2) { |
| @@ -185,11 +186,11 @@ int test__hists_filter(void) | |||
| 185 | hists->stats.total_non_filtered_period == 400); | 186 | hists->stats.total_non_filtered_period == 400); |
| 186 | 187 | ||
| 187 | /* remove thread filter first */ | 188 | /* remove thread filter first */ |
| 188 | evsel->hists.thread_filter = NULL; | 189 | hists->thread_filter = NULL; |
| 189 | hists__filter_by_thread(hists); | 190 | hists__filter_by_thread(hists); |
| 190 | 191 | ||
| 191 | /* now applying dso filter for 'kernel' */ | 192 | /* now applying dso filter for 'kernel' */ |
| 192 | evsel->hists.dso_filter = fake_samples[0].map->dso; | 193 | hists->dso_filter = fake_samples[0].map->dso; |
| 193 | hists__filter_by_dso(hists); | 194 | hists__filter_by_dso(hists); |
| 194 | 195 | ||
| 195 | if (verbose > 2) { | 196 | if (verbose > 2) { |
| @@ -214,7 +215,7 @@ int test__hists_filter(void) | |||
| 214 | hists->stats.total_non_filtered_period == 300); | 215 | hists->stats.total_non_filtered_period == 300); |
| 215 | 216 | ||
| 216 | /* remove dso filter first */ | 217 | /* remove dso filter first */ |
| 217 | evsel->hists.dso_filter = NULL; | 218 | hists->dso_filter = NULL; |
| 218 | hists__filter_by_dso(hists); | 219 | hists__filter_by_dso(hists); |
| 219 | 220 | ||
| 220 | /* | 221 | /* |
| @@ -224,7 +225,7 @@ int test__hists_filter(void) | |||
| 224 | * be counted as a separate entry but the sample count and | 225 | * be counted as a separate entry but the sample count and |
| 225 | * total period will be remained. | 226 | * total period will be remained. |
| 226 | */ | 227 | */ |
| 227 | evsel->hists.symbol_filter_str = "main"; | 228 | hists->symbol_filter_str = "main"; |
| 228 | hists__filter_by_symbol(hists); | 229 | hists__filter_by_symbol(hists); |
| 229 | 230 | ||
| 230 | if (verbose > 2) { | 231 | if (verbose > 2) { |
| @@ -249,8 +250,8 @@ int test__hists_filter(void) | |||
| 249 | hists->stats.total_non_filtered_period == 300); | 250 | hists->stats.total_non_filtered_period == 300); |
| 250 | 251 | ||
| 251 | /* now applying all filters at once. */ | 252 | /* now applying all filters at once. */ |
| 252 | evsel->hists.thread_filter = fake_samples[1].thread; | 253 | hists->thread_filter = fake_samples[1].thread; |
| 253 | evsel->hists.dso_filter = fake_samples[1].map->dso; | 254 | hists->dso_filter = fake_samples[1].map->dso; |
| 254 | hists__filter_by_thread(hists); | 255 | hists__filter_by_thread(hists); |
| 255 | hists__filter_by_dso(hists); | 256 | hists__filter_by_dso(hists); |
| 256 | 257 | ||
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index d4b34b0f50a2..278ba8344c23 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
| @@ -73,6 +73,8 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
| 73 | * "bash [libc] malloc" so total 9 entries will be in the tree. | 73 | * "bash [libc] malloc" so total 9 entries will be in the tree. |
| 74 | */ | 74 | */ |
| 75 | evlist__for_each(evlist, evsel) { | 75 | evlist__for_each(evlist, evsel) { |
| 76 | struct hists *hists = evsel__hists(evsel); | ||
| 77 | |||
| 76 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { | 78 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { |
| 77 | const union perf_event event = { | 79 | const union perf_event event = { |
| 78 | .header = { | 80 | .header = { |
| @@ -87,7 +89,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
| 87 | &sample) < 0) | 89 | &sample) < 0) |
| 88 | goto out; | 90 | goto out; |
| 89 | 91 | ||
| 90 | he = __hists__add_entry(&evsel->hists, &al, NULL, | 92 | he = __hists__add_entry(hists, &al, NULL, |
| 91 | NULL, NULL, 1, 1, 0, true); | 93 | NULL, NULL, 1, 1, 0, true); |
| 92 | if (he == NULL) | 94 | if (he == NULL) |
| 93 | goto out; | 95 | goto out; |
| @@ -111,7 +113,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
| 111 | &sample) < 0) | 113 | &sample) < 0) |
| 112 | goto out; | 114 | goto out; |
| 113 | 115 | ||
| 114 | he = __hists__add_entry(&evsel->hists, &al, NULL, | 116 | he = __hists__add_entry(hists, &al, NULL, |
| 115 | NULL, NULL, 1, 1, 0, true); | 117 | NULL, NULL, 1, 1, 0, true); |
| 116 | if (he == NULL) | 118 | if (he == NULL) |
| 117 | goto out; | 119 | goto out; |
| @@ -271,6 +273,7 @@ static int validate_link(struct hists *leader, struct hists *other) | |||
| 271 | int test__hists_link(void) | 273 | int test__hists_link(void) |
| 272 | { | 274 | { |
| 273 | int err = -1; | 275 | int err = -1; |
| 276 | struct hists *hists, *first_hists; | ||
| 274 | struct machines machines; | 277 | struct machines machines; |
| 275 | struct machine *machine = NULL; | 278 | struct machine *machine = NULL; |
| 276 | struct perf_evsel *evsel, *first; | 279 | struct perf_evsel *evsel, *first; |
| @@ -306,24 +309,28 @@ int test__hists_link(void) | |||
| 306 | goto out; | 309 | goto out; |
| 307 | 310 | ||
| 308 | evlist__for_each(evlist, evsel) { | 311 | evlist__for_each(evlist, evsel) { |
| 309 | hists__collapse_resort(&evsel->hists, NULL); | 312 | hists = evsel__hists(evsel); |
| 313 | hists__collapse_resort(hists, NULL); | ||
| 310 | 314 | ||
| 311 | if (verbose > 2) | 315 | if (verbose > 2) |
| 312 | print_hists_in(&evsel->hists); | 316 | print_hists_in(hists); |
| 313 | } | 317 | } |
| 314 | 318 | ||
| 315 | first = perf_evlist__first(evlist); | 319 | first = perf_evlist__first(evlist); |
| 316 | evsel = perf_evlist__last(evlist); | 320 | evsel = perf_evlist__last(evlist); |
| 317 | 321 | ||
| 322 | first_hists = evsel__hists(first); | ||
| 323 | hists = evsel__hists(evsel); | ||
| 324 | |||
| 318 | /* match common entries */ | 325 | /* match common entries */ |
| 319 | hists__match(&first->hists, &evsel->hists); | 326 | hists__match(first_hists, hists); |
| 320 | err = validate_match(&first->hists, &evsel->hists); | 327 | err = validate_match(first_hists, hists); |
| 321 | if (err) | 328 | if (err) |
| 322 | goto out; | 329 | goto out; |
| 323 | 330 | ||
| 324 | /* link common and/or dummy entries */ | 331 | /* link common and/or dummy entries */ |
| 325 | hists__link(&first->hists, &evsel->hists); | 332 | hists__link(first_hists, hists); |
| 326 | err = validate_link(&first->hists, &evsel->hists); | 333 | err = validate_link(first_hists, hists); |
| 327 | if (err) | 334 | if (err) |
| 328 | goto out; | 335 | goto out; |
| 329 | 336 | ||
diff --git a/tools/perf/tests/hists_output.c b/tools/perf/tests/hists_output.c index e3bbd6c54c1b..a748f2be1222 100644 --- a/tools/perf/tests/hists_output.c +++ b/tools/perf/tests/hists_output.c | |||
| @@ -122,7 +122,7 @@ typedef int (*test_fn_t)(struct perf_evsel *, struct machine *); | |||
| 122 | static int test1(struct perf_evsel *evsel, struct machine *machine) | 122 | static int test1(struct perf_evsel *evsel, struct machine *machine) |
| 123 | { | 123 | { |
| 124 | int err; | 124 | int err; |
| 125 | struct hists *hists = &evsel->hists; | 125 | struct hists *hists = evsel__hists(evsel); |
| 126 | struct hist_entry *he; | 126 | struct hist_entry *he; |
| 127 | struct rb_root *root; | 127 | struct rb_root *root; |
| 128 | struct rb_node *node; | 128 | struct rb_node *node; |
| @@ -159,7 +159,7 @@ static int test1(struct perf_evsel *evsel, struct machine *machine) | |||
| 159 | print_hists_out(hists); | 159 | print_hists_out(hists); |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | root = &evsel->hists.entries; | 162 | root = &hists->entries; |
| 163 | node = rb_first(root); | 163 | node = rb_first(root); |
| 164 | he = rb_entry(node, struct hist_entry, rb_node); | 164 | he = rb_entry(node, struct hist_entry, rb_node); |
| 165 | TEST_ASSERT_VAL("Invalid hist entry", | 165 | TEST_ASSERT_VAL("Invalid hist entry", |
| @@ -224,7 +224,7 @@ out: | |||
| 224 | static int test2(struct perf_evsel *evsel, struct machine *machine) | 224 | static int test2(struct perf_evsel *evsel, struct machine *machine) |
| 225 | { | 225 | { |
| 226 | int err; | 226 | int err; |
| 227 | struct hists *hists = &evsel->hists; | 227 | struct hists *hists = evsel__hists(evsel); |
| 228 | struct hist_entry *he; | 228 | struct hist_entry *he; |
| 229 | struct rb_root *root; | 229 | struct rb_root *root; |
| 230 | struct rb_node *node; | 230 | struct rb_node *node; |
| @@ -259,7 +259,7 @@ static int test2(struct perf_evsel *evsel, struct machine *machine) | |||
| 259 | print_hists_out(hists); | 259 | print_hists_out(hists); |
| 260 | } | 260 | } |
| 261 | 261 | ||
| 262 | root = &evsel->hists.entries; | 262 | root = &hists->entries; |
| 263 | node = rb_first(root); | 263 | node = rb_first(root); |
| 264 | he = rb_entry(node, struct hist_entry, rb_node); | 264 | he = rb_entry(node, struct hist_entry, rb_node); |
| 265 | TEST_ASSERT_VAL("Invalid hist entry", | 265 | TEST_ASSERT_VAL("Invalid hist entry", |
| @@ -280,7 +280,7 @@ out: | |||
| 280 | static int test3(struct perf_evsel *evsel, struct machine *machine) | 280 | static int test3(struct perf_evsel *evsel, struct machine *machine) |
| 281 | { | 281 | { |
| 282 | int err; | 282 | int err; |
| 283 | struct hists *hists = &evsel->hists; | 283 | struct hists *hists = evsel__hists(evsel); |
| 284 | struct hist_entry *he; | 284 | struct hist_entry *he; |
| 285 | struct rb_root *root; | 285 | struct rb_root *root; |
| 286 | struct rb_node *node; | 286 | struct rb_node *node; |
| @@ -313,7 +313,7 @@ static int test3(struct perf_evsel *evsel, struct machine *machine) | |||
| 313 | print_hists_out(hists); | 313 | print_hists_out(hists); |
| 314 | } | 314 | } |
| 315 | 315 | ||
| 316 | root = &evsel->hists.entries; | 316 | root = &hists->entries; |
| 317 | node = rb_first(root); | 317 | node = rb_first(root); |
| 318 | he = rb_entry(node, struct hist_entry, rb_node); | 318 | he = rb_entry(node, struct hist_entry, rb_node); |
| 319 | TEST_ASSERT_VAL("Invalid hist entry", | 319 | TEST_ASSERT_VAL("Invalid hist entry", |
| @@ -354,7 +354,7 @@ out: | |||
| 354 | static int test4(struct perf_evsel *evsel, struct machine *machine) | 354 | static int test4(struct perf_evsel *evsel, struct machine *machine) |
| 355 | { | 355 | { |
| 356 | int err; | 356 | int err; |
| 357 | struct hists *hists = &evsel->hists; | 357 | struct hists *hists = evsel__hists(evsel); |
| 358 | struct hist_entry *he; | 358 | struct hist_entry *he; |
| 359 | struct rb_root *root; | 359 | struct rb_root *root; |
| 360 | struct rb_node *node; | 360 | struct rb_node *node; |
| @@ -391,7 +391,7 @@ static int test4(struct perf_evsel *evsel, struct machine *machine) | |||
| 391 | print_hists_out(hists); | 391 | print_hists_out(hists); |
| 392 | } | 392 | } |
| 393 | 393 | ||
| 394 | root = &evsel->hists.entries; | 394 | root = &hists->entries; |
| 395 | node = rb_first(root); | 395 | node = rb_first(root); |
| 396 | he = rb_entry(node, struct hist_entry, rb_node); | 396 | he = rb_entry(node, struct hist_entry, rb_node); |
| 397 | TEST_ASSERT_VAL("Invalid hist entry", | 397 | TEST_ASSERT_VAL("Invalid hist entry", |
| @@ -456,7 +456,7 @@ out: | |||
| 456 | static int test5(struct perf_evsel *evsel, struct machine *machine) | 456 | static int test5(struct perf_evsel *evsel, struct machine *machine) |
| 457 | { | 457 | { |
| 458 | int err; | 458 | int err; |
| 459 | struct hists *hists = &evsel->hists; | 459 | struct hists *hists = evsel__hists(evsel); |
| 460 | struct hist_entry *he; | 460 | struct hist_entry *he; |
| 461 | struct rb_root *root; | 461 | struct rb_root *root; |
| 462 | struct rb_node *node; | 462 | struct rb_node *node; |
| @@ -494,7 +494,7 @@ static int test5(struct perf_evsel *evsel, struct machine *machine) | |||
| 494 | print_hists_out(hists); | 494 | print_hists_out(hists); |
| 495 | } | 495 | } |
| 496 | 496 | ||
| 497 | root = &evsel->hists.entries; | 497 | root = &hists->entries; |
| 498 | node = rb_first(root); | 498 | node = rb_first(root); |
| 499 | he = rb_entry(node, struct hist_entry, rb_node); | 499 | he = rb_entry(node, struct hist_entry, rb_node); |
| 500 | 500 | ||
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/parse-events.c b/tools/perf/tests/parse-events.c index 5941927a4b7f..7f2f51f93619 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
| @@ -457,6 +457,36 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist) | |||
| 457 | return 0; | 457 | return 0; |
| 458 | } | 458 | } |
| 459 | 459 | ||
| 460 | |||
| 461 | static int test__checkevent_pmu_events_mix(struct perf_evlist *evlist) | ||
| 462 | { | ||
| 463 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
| 464 | |||
| 465 | /* pmu-event:u */ | ||
| 466 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
| 467 | TEST_ASSERT_VAL("wrong exclude_user", | ||
| 468 | !evsel->attr.exclude_user); | ||
| 469 | TEST_ASSERT_VAL("wrong exclude_kernel", | ||
| 470 | evsel->attr.exclude_kernel); | ||
| 471 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 472 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 473 | TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned); | ||
| 474 | |||
| 475 | /* cpu/pmu-event/u*/ | ||
| 476 | evsel = perf_evsel__next(evsel); | ||
| 477 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
| 478 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
| 479 | TEST_ASSERT_VAL("wrong exclude_user", | ||
| 480 | !evsel->attr.exclude_user); | ||
| 481 | TEST_ASSERT_VAL("wrong exclude_kernel", | ||
| 482 | evsel->attr.exclude_kernel); | ||
| 483 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
| 484 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
| 485 | TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned); | ||
| 486 | |||
| 487 | return 0; | ||
| 488 | } | ||
| 489 | |||
| 460 | static int test__checkterms_simple(struct list_head *terms) | 490 | static int test__checkterms_simple(struct list_head *terms) |
| 461 | { | 491 | { |
| 462 | struct parse_events_term *term; | 492 | struct parse_events_term *term; |
| @@ -1554,6 +1584,12 @@ static int test_pmu_events(void) | |||
| 1554 | e.check = test__checkevent_pmu_events; | 1584 | e.check = test__checkevent_pmu_events; |
| 1555 | 1585 | ||
| 1556 | ret = test_event(&e); | 1586 | ret = test_event(&e); |
| 1587 | if (ret) | ||
| 1588 | break; | ||
| 1589 | snprintf(name, MAX_NAME, "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name); | ||
| 1590 | e.name = name; | ||
| 1591 | e.check = test__checkevent_pmu_events_mix; | ||
| 1592 | ret = test_event(&e); | ||
| 1557 | #undef MAX_NAME | 1593 | #undef MAX_NAME |
| 1558 | } | 1594 | } |
| 1559 | 1595 | ||
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/header.c b/tools/perf/ui/browsers/header.c index 89c16b988618..e8278c558d4a 100644 --- a/tools/perf/ui/browsers/header.c +++ b/tools/perf/ui/browsers/header.c | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #include "util/cache.h" | 1 | #include "util/cache.h" |
| 2 | #include "util/debug.h" | 2 | #include "util/debug.h" |
| 3 | #include "ui/browser.h" | 3 | #include "ui/browser.h" |
| 4 | #include "ui/keysyms.h" | ||
| 4 | #include "ui/ui.h" | 5 | #include "ui/ui.h" |
| 5 | #include "ui/util.h" | 6 | #include "ui/util.h" |
| 6 | #include "ui/libslang.h" | 7 | #include "ui/libslang.h" |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a94b11fc5e00..cfb976b3de3a 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" |
| @@ -34,7 +35,9 @@ struct hist_browser { | |||
| 34 | 35 | ||
| 35 | extern void hist_browser__init_hpp(void); | 36 | extern void hist_browser__init_hpp(void); |
| 36 | 37 | ||
| 37 | static int hists__browser_title(struct hists *hists, char *bf, size_t size); | 38 | static int hists__browser_title(struct hists *hists, |
| 39 | struct hist_browser_timer *hbt, | ||
| 40 | char *bf, size_t size); | ||
| 38 | static void hist_browser__update_nr_entries(struct hist_browser *hb); | 41 | static void hist_browser__update_nr_entries(struct hist_browser *hb); |
| 39 | 42 | ||
| 40 | static struct rb_node *hists__filter_entries(struct rb_node *nd, | 43 | static struct rb_node *hists__filter_entries(struct rb_node *nd, |
| @@ -228,8 +231,10 @@ static void callchain_node__init_have_children(struct callchain_node *node) | |||
| 228 | { | 231 | { |
| 229 | struct callchain_list *chain; | 232 | struct callchain_list *chain; |
| 230 | 233 | ||
| 231 | list_for_each_entry(chain, &node->val, list) | 234 | if (!list_empty(&node->val)) { |
| 235 | chain = list_entry(node->val.prev, struct callchain_list, list); | ||
| 232 | chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); | 236 | chain->ms.has_children = !RB_EMPTY_ROOT(&node->rb_root); |
| 237 | } | ||
| 233 | 238 | ||
| 234 | callchain_node__init_have_children_rb_tree(node); | 239 | callchain_node__init_have_children_rb_tree(node); |
| 235 | } | 240 | } |
| @@ -387,7 +392,7 @@ static int hist_browser__run(struct hist_browser *browser, | |||
| 387 | browser->b.entries = &browser->hists->entries; | 392 | browser->b.entries = &browser->hists->entries; |
| 388 | browser->b.nr_entries = hist_browser__nr_entries(browser); | 393 | browser->b.nr_entries = hist_browser__nr_entries(browser); |
| 389 | 394 | ||
| 390 | hists__browser_title(browser->hists, title, sizeof(title)); | 395 | hists__browser_title(browser->hists, hbt, title, sizeof(title)); |
| 391 | 396 | ||
| 392 | if (ui_browser__show(&browser->b, title, | 397 | if (ui_browser__show(&browser->b, title, |
| 393 | "Press '?' for help on key bindings") < 0) | 398 | "Press '?' for help on key bindings") < 0) |
| @@ -414,7 +419,8 @@ static int hist_browser__run(struct hist_browser *browser, | |||
| 414 | ui_browser__warn_lost_events(&browser->b); | 419 | ui_browser__warn_lost_events(&browser->b); |
| 415 | } | 420 | } |
| 416 | 421 | ||
| 417 | hists__browser_title(browser->hists, title, sizeof(title)); | 422 | hists__browser_title(browser->hists, |
| 423 | hbt, title, sizeof(title)); | ||
| 418 | ui_browser__show_title(&browser->b, title); | 424 | ui_browser__show_title(&browser->b, title); |
| 419 | continue; | 425 | continue; |
| 420 | } | 426 | } |
| @@ -474,26 +480,87 @@ static char *callchain_list__sym_name(struct callchain_list *cl, | |||
| 474 | return bf; | 480 | return bf; |
| 475 | } | 481 | } |
| 476 | 482 | ||
| 483 | struct callchain_print_arg { | ||
| 484 | /* for hists browser */ | ||
| 485 | off_t row_offset; | ||
| 486 | bool is_current_entry; | ||
| 487 | |||
| 488 | /* for file dump */ | ||
| 489 | FILE *fp; | ||
| 490 | int printed; | ||
| 491 | }; | ||
| 492 | |||
| 493 | typedef void (*print_callchain_entry_fn)(struct hist_browser *browser, | ||
| 494 | struct callchain_list *chain, | ||
| 495 | const char *str, int offset, | ||
| 496 | unsigned short row, | ||
| 497 | struct callchain_print_arg *arg); | ||
| 498 | |||
| 499 | static void hist_browser__show_callchain_entry(struct hist_browser *browser, | ||
| 500 | struct callchain_list *chain, | ||
| 501 | const char *str, int offset, | ||
| 502 | unsigned short row, | ||
| 503 | struct callchain_print_arg *arg) | ||
| 504 | { | ||
| 505 | int color, width; | ||
| 506 | char folded_sign = callchain_list__folded(chain); | ||
| 507 | |||
| 508 | color = HE_COLORSET_NORMAL; | ||
| 509 | width = browser->b.width - (offset + 2); | ||
| 510 | if (ui_browser__is_current_entry(&browser->b, row)) { | ||
| 511 | browser->selection = &chain->ms; | ||
| 512 | color = HE_COLORSET_SELECTED; | ||
| 513 | arg->is_current_entry = true; | ||
| 514 | } | ||
| 515 | |||
| 516 | ui_browser__set_color(&browser->b, color); | ||
| 517 | hist_browser__gotorc(browser, row, 0); | ||
| 518 | slsmg_write_nstring(" ", offset); | ||
| 519 | slsmg_printf("%c ", folded_sign); | ||
| 520 | slsmg_write_nstring(str, width); | ||
| 521 | } | ||
| 522 | |||
| 523 | static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused, | ||
| 524 | struct callchain_list *chain, | ||
| 525 | const char *str, int offset, | ||
| 526 | unsigned short row __maybe_unused, | ||
| 527 | struct callchain_print_arg *arg) | ||
| 528 | { | ||
| 529 | char folded_sign = callchain_list__folded(chain); | ||
| 530 | |||
| 531 | arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ", | ||
| 532 | folded_sign, str); | ||
| 533 | } | ||
| 534 | |||
| 535 | typedef bool (*check_output_full_fn)(struct hist_browser *browser, | ||
| 536 | unsigned short row); | ||
| 537 | |||
| 538 | static bool hist_browser__check_output_full(struct hist_browser *browser, | ||
| 539 | unsigned short row) | ||
| 540 | { | ||
| 541 | return browser->b.rows == row; | ||
| 542 | } | ||
| 543 | |||
| 544 | static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused, | ||
| 545 | unsigned short row __maybe_unused) | ||
| 546 | { | ||
| 547 | return false; | ||
| 548 | } | ||
| 549 | |||
| 477 | #define LEVEL_OFFSET_STEP 3 | 550 | #define LEVEL_OFFSET_STEP 3 |
| 478 | 551 | ||
| 479 | static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser, | 552 | static int hist_browser__show_callchain(struct hist_browser *browser, |
| 480 | struct callchain_node *chain_node, | 553 | struct rb_root *root, int level, |
| 481 | u64 total, int level, | 554 | unsigned short row, u64 total, |
| 482 | unsigned short row, | 555 | print_callchain_entry_fn print, |
| 483 | off_t *row_offset, | 556 | struct callchain_print_arg *arg, |
| 484 | bool *is_current_entry) | 557 | check_output_full_fn is_output_full) |
| 485 | { | 558 | { |
| 486 | struct rb_node *node; | 559 | struct rb_node *node; |
| 487 | int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; | 560 | int first_row = row, offset = level * LEVEL_OFFSET_STEP; |
| 488 | u64 new_total, remaining; | 561 | u64 new_total; |
| 489 | 562 | ||
| 490 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 563 | 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) { | 564 | while (node) { |
| 498 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 565 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
| 499 | struct rb_node *next = rb_next(node); | 566 | struct rb_node *next = rb_next(node); |
| @@ -503,30 +570,28 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse | |||
| 503 | int first = true; | 570 | int first = true; |
| 504 | int extra_offset = 0; | 571 | int extra_offset = 0; |
| 505 | 572 | ||
| 506 | remaining -= cumul; | ||
| 507 | |||
| 508 | list_for_each_entry(chain, &child->val, list) { | 573 | list_for_each_entry(chain, &child->val, list) { |
| 509 | char bf[1024], *alloc_str; | 574 | char bf[1024], *alloc_str; |
| 510 | const char *str; | 575 | const char *str; |
| 511 | int color; | ||
| 512 | bool was_first = first; | 576 | bool was_first = first; |
| 513 | 577 | ||
| 514 | if (first) | 578 | if (first) |
| 515 | first = false; | 579 | first = false; |
| 516 | else | 580 | else if (level > 1) |
| 517 | extra_offset = LEVEL_OFFSET_STEP; | 581 | extra_offset = LEVEL_OFFSET_STEP; |
| 518 | 582 | ||
| 519 | folded_sign = callchain_list__folded(chain); | 583 | folded_sign = callchain_list__folded(chain); |
| 520 | if (*row_offset != 0) { | 584 | if (arg->row_offset != 0) { |
| 521 | --*row_offset; | 585 | arg->row_offset--; |
| 522 | goto do_next; | 586 | goto do_next; |
| 523 | } | 587 | } |
| 524 | 588 | ||
| 525 | alloc_str = NULL; | 589 | alloc_str = NULL; |
| 526 | str = callchain_list__sym_name(chain, bf, sizeof(bf), | 590 | str = callchain_list__sym_name(chain, bf, sizeof(bf), |
| 527 | browser->show_dso); | 591 | browser->show_dso); |
| 528 | if (was_first) { | 592 | |
| 529 | double percent = cumul * 100.0 / new_total; | 593 | if (was_first && level > 1) { |
| 594 | double percent = cumul * 100.0 / total; | ||
| 530 | 595 | ||
| 531 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | 596 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) |
| 532 | str = "Not enough memory!"; | 597 | str = "Not enough memory!"; |
| @@ -534,22 +599,11 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse | |||
| 534 | str = alloc_str; | 599 | str = alloc_str; |
| 535 | } | 600 | } |
| 536 | 601 | ||
| 537 | color = HE_COLORSET_NORMAL; | 602 | 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 | 603 | ||
| 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); | 604 | free(alloc_str); |
| 551 | 605 | ||
| 552 | if (++row == browser->b.rows) | 606 | if (is_output_full(browser, ++row)) |
| 553 | goto out; | 607 | goto out; |
| 554 | do_next: | 608 | do_next: |
| 555 | if (folded_sign == '+') | 609 | if (folded_sign == '+') |
| @@ -558,89 +612,21 @@ do_next: | |||
| 558 | 612 | ||
| 559 | if (folded_sign == '-') { | 613 | if (folded_sign == '-') { |
| 560 | const int new_level = level + (extra_offset ? 2 : 1); | 614 | 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 | |||
| 585 | list_for_each_entry(chain, &node->val, list) { | ||
| 586 | char bf[1024], *s; | ||
| 587 | int color; | ||
| 588 | 615 | ||
| 589 | folded_sign = callchain_list__folded(chain); | 616 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
| 590 | 617 | new_total = child->children_hit; | |
| 591 | if (*row_offset != 0) { | 618 | else |
| 592 | --*row_offset; | 619 | new_total = total; |
| 593 | continue; | ||
| 594 | } | ||
| 595 | 620 | ||
| 596 | color = HE_COLORSET_NORMAL; | 621 | row += hist_browser__show_callchain(browser, &child->rb_root, |
| 597 | if (ui_browser__is_current_entry(&browser->b, row)) { | 622 | new_level, row, new_total, |
| 598 | browser->selection = &chain->ms; | 623 | print, arg, is_output_full); |
| 599 | color = HE_COLORSET_SELECTED; | ||
| 600 | *is_current_entry = true; | ||
| 601 | } | 624 | } |
| 602 | 625 | 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; | 626 | break; |
| 627 | node = next; | ||
| 642 | } | 628 | } |
| 643 | 629 | out: | |
| 644 | return row - first_row; | 630 | return row - first_row; |
| 645 | } | 631 | } |
| 646 | 632 | ||
| @@ -653,17 +639,18 @@ struct hpp_arg { | |||
| 653 | static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) | 639 | static int __hpp__slsmg_color_printf(struct perf_hpp *hpp, const char *fmt, ...) |
| 654 | { | 640 | { |
| 655 | struct hpp_arg *arg = hpp->ptr; | 641 | struct hpp_arg *arg = hpp->ptr; |
| 656 | int ret; | 642 | int ret, len; |
| 657 | va_list args; | 643 | va_list args; |
| 658 | double percent; | 644 | double percent; |
| 659 | 645 | ||
| 660 | va_start(args, fmt); | 646 | va_start(args, fmt); |
| 647 | len = va_arg(args, int); | ||
| 661 | percent = va_arg(args, double); | 648 | percent = va_arg(args, double); |
| 662 | va_end(args); | 649 | va_end(args); |
| 663 | 650 | ||
| 664 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); | 651 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); |
| 665 | 652 | ||
| 666 | ret = scnprintf(hpp->buf, hpp->size, fmt, percent); | 653 | ret = scnprintf(hpp->buf, hpp->size, fmt, len, percent); |
| 667 | slsmg_printf("%s", hpp->buf); | 654 | slsmg_printf("%s", hpp->buf); |
| 668 | 655 | ||
| 669 | advance_hpp(hpp, ret); | 656 | advance_hpp(hpp, ret); |
| @@ -677,12 +664,12 @@ static u64 __hpp_get_##_field(struct hist_entry *he) \ | |||
| 677 | } \ | 664 | } \ |
| 678 | \ | 665 | \ |
| 679 | static int \ | 666 | static int \ |
| 680 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ | 667 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
| 681 | struct perf_hpp *hpp, \ | 668 | struct perf_hpp *hpp, \ |
| 682 | struct hist_entry *he) \ | 669 | struct hist_entry *he) \ |
| 683 | { \ | 670 | { \ |
| 684 | return __hpp__fmt(hpp, he, __hpp_get_##_field, " %6.2f%%", \ | 671 | return hpp__fmt(fmt, hpp, he, __hpp_get_##_field, " %*.2f%%", \ |
| 685 | __hpp__slsmg_color_printf, true); \ | 672 | __hpp__slsmg_color_printf, true); \ |
| 686 | } | 673 | } |
| 687 | 674 | ||
| 688 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ | 675 | #define __HPP_COLOR_ACC_PERCENT_FN(_type, _field) \ |
| @@ -692,18 +679,20 @@ static u64 __hpp_get_acc_##_field(struct hist_entry *he) \ | |||
| 692 | } \ | 679 | } \ |
| 693 | \ | 680 | \ |
| 694 | static int \ | 681 | static int \ |
| 695 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ | 682 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ |
| 696 | struct perf_hpp *hpp, \ | 683 | struct perf_hpp *hpp, \ |
| 697 | struct hist_entry *he) \ | 684 | struct hist_entry *he) \ |
| 698 | { \ | 685 | { \ |
| 699 | if (!symbol_conf.cumulate_callchain) { \ | 686 | if (!symbol_conf.cumulate_callchain) { \ |
| 700 | int ret = scnprintf(hpp->buf, hpp->size, "%8s", "N/A"); \ | 687 | int len = fmt->user_len ?: fmt->len; \ |
| 688 | int ret = scnprintf(hpp->buf, hpp->size, \ | ||
| 689 | "%*s", len, "N/A"); \ | ||
| 701 | slsmg_printf("%s", hpp->buf); \ | 690 | slsmg_printf("%s", hpp->buf); \ |
| 702 | \ | 691 | \ |
| 703 | return ret; \ | 692 | return ret; \ |
| 704 | } \ | 693 | } \ |
| 705 | return __hpp__fmt(hpp, he, __hpp_get_acc_##_field, " %6.2f%%", \ | 694 | return hpp__fmt(fmt, hpp, he, __hpp_get_acc_##_field, \ |
| 706 | __hpp__slsmg_color_printf, true); \ | 695 | " %*.2f%%", __hpp__slsmg_color_printf, true); \ |
| 707 | } | 696 | } |
| 708 | 697 | ||
| 709 | __HPP_COLOR_PERCENT_FN(overhead, period) | 698 | __HPP_COLOR_PERCENT_FN(overhead, period) |
| @@ -812,10 +801,18 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
| 812 | --row_offset; | 801 | --row_offset; |
| 813 | 802 | ||
| 814 | if (folded_sign == '-' && row != browser->b.rows) { | 803 | if (folded_sign == '-' && row != browser->b.rows) { |
| 815 | printed += hist_browser__show_callchain(browser, &entry->sorted_chain, | 804 | u64 total = hists__total_period(entry->hists); |
| 816 | 1, row, &row_offset, | 805 | struct callchain_print_arg arg = { |
| 817 | ¤t_entry); | 806 | .row_offset = row_offset, |
| 818 | if (current_entry) | 807 | .is_current_entry = current_entry, |
| 808 | }; | ||
| 809 | |||
| 810 | printed += hist_browser__show_callchain(browser, | ||
| 811 | &entry->sorted_chain, 1, row, total, | ||
| 812 | hist_browser__show_callchain_entry, &arg, | ||
| 813 | hist_browser__check_output_full); | ||
| 814 | |||
| 815 | if (arg.is_current_entry) | ||
| 819 | browser->he_selection = entry; | 816 | browser->he_selection = entry; |
| 820 | } | 817 | } |
| 821 | 818 | ||
| @@ -847,9 +844,6 @@ static int hists__scnprintf_headers(char *buf, size_t size, struct hists *hists) | |||
| 847 | if (perf_hpp__should_skip(fmt)) | 844 | if (perf_hpp__should_skip(fmt)) |
| 848 | continue; | 845 | continue; |
| 849 | 846 | ||
| 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)); | 847 | ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists)); |
| 854 | if (advance_hpp_check(&dummy_hpp, ret)) | 848 | if (advance_hpp_check(&dummy_hpp, ret)) |
| 855 | break; | 849 | break; |
| @@ -1074,113 +1068,21 @@ do_offset: | |||
| 1074 | } | 1068 | } |
| 1075 | } | 1069 | } |
| 1076 | 1070 | ||
| 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, | 1071 | static int hist_browser__fprintf_callchain(struct hist_browser *browser, |
| 1172 | struct rb_root *chain, int level, FILE *fp) | 1072 | struct hist_entry *he, FILE *fp) |
| 1173 | { | 1073 | { |
| 1174 | struct rb_node *nd; | 1074 | u64 total = hists__total_period(he->hists); |
| 1175 | int printed = 0; | 1075 | struct callchain_print_arg arg = { |
| 1176 | 1076 | .fp = fp, | |
| 1177 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | 1077 | }; |
| 1178 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
| 1179 | 1078 | ||
| 1180 | printed += hist_browser__fprintf_callchain_node(browser, node, level, fp); | 1079 | if (symbol_conf.cumulate_callchain) |
| 1181 | } | 1080 | total = he->stat_acc->period; |
| 1182 | 1081 | ||
| 1183 | return printed; | 1082 | hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total, |
| 1083 | hist_browser__fprintf_callchain_entry, &arg, | ||
| 1084 | hist_browser__check_dump_full); | ||
| 1085 | return arg.printed; | ||
| 1184 | } | 1086 | } |
| 1185 | 1087 | ||
| 1186 | static int hist_browser__fprintf_entry(struct hist_browser *browser, | 1088 | static int hist_browser__fprintf_entry(struct hist_browser *browser, |
| @@ -1219,7 +1121,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, | |||
| 1219 | printed += fprintf(fp, "%s\n", rtrim(s)); | 1121 | printed += fprintf(fp, "%s\n", rtrim(s)); |
| 1220 | 1122 | ||
| 1221 | if (folded_sign == '-') | 1123 | if (folded_sign == '-') |
| 1222 | printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp); | 1124 | printed += hist_browser__fprintf_callchain(browser, he, fp); |
| 1223 | 1125 | ||
| 1224 | return printed; | 1126 | return printed; |
| 1225 | } | 1127 | } |
| @@ -1305,7 +1207,15 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *browser | |||
| 1305 | return browser->he_selection->thread; | 1207 | return browser->he_selection->thread; |
| 1306 | } | 1208 | } |
| 1307 | 1209 | ||
| 1308 | static int hists__browser_title(struct hists *hists, char *bf, size_t size) | 1210 | /* Check whether the browser is for 'top' or 'report' */ |
| 1211 | static inline bool is_report_browser(void *timer) | ||
| 1212 | { | ||
| 1213 | return timer == NULL; | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | static int hists__browser_title(struct hists *hists, | ||
| 1217 | struct hist_browser_timer *hbt, | ||
| 1218 | char *bf, size_t size) | ||
| 1309 | { | 1219 | { |
| 1310 | char unit; | 1220 | char unit; |
| 1311 | int printed; | 1221 | int printed; |
| @@ -1330,12 +1240,14 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size) | |||
| 1330 | ev_name = buf; | 1240 | ev_name = buf; |
| 1331 | 1241 | ||
| 1332 | for_each_group_member(pos, evsel) { | 1242 | for_each_group_member(pos, evsel) { |
| 1243 | struct hists *pos_hists = evsel__hists(pos); | ||
| 1244 | |||
| 1333 | if (symbol_conf.filter_relative) { | 1245 | if (symbol_conf.filter_relative) { |
| 1334 | nr_samples += pos->hists.stats.nr_non_filtered_samples; | 1246 | nr_samples += pos_hists->stats.nr_non_filtered_samples; |
| 1335 | nr_events += pos->hists.stats.total_non_filtered_period; | 1247 | nr_events += pos_hists->stats.total_non_filtered_period; |
| 1336 | } else { | 1248 | } else { |
| 1337 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1249 | nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
| 1338 | nr_events += pos->hists.stats.total_period; | 1250 | nr_events += pos_hists->stats.total_period; |
| 1339 | } | 1251 | } |
| 1340 | } | 1252 | } |
| 1341 | } | 1253 | } |
| @@ -1357,6 +1269,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size) | |||
| 1357 | if (dso) | 1269 | if (dso) |
| 1358 | printed += scnprintf(bf + printed, size - printed, | 1270 | printed += scnprintf(bf + printed, size - printed, |
| 1359 | ", DSO: %s", dso->short_name); | 1271 | ", DSO: %s", dso->short_name); |
| 1272 | if (!is_report_browser(hbt)) { | ||
| 1273 | struct perf_top *top = hbt->arg; | ||
| 1274 | |||
| 1275 | if (top->zero) | ||
| 1276 | printed += scnprintf(bf + printed, size - printed, " [z]"); | ||
| 1277 | } | ||
| 1278 | |||
| 1360 | return printed; | 1279 | return printed; |
| 1361 | } | 1280 | } |
| 1362 | 1281 | ||
| @@ -1368,12 +1287,6 @@ static inline void free_popup_options(char **options, int n) | |||
| 1368 | zfree(&options[i]); | 1287 | zfree(&options[i]); |
| 1369 | } | 1288 | } |
| 1370 | 1289 | ||
| 1371 | /* Check whether the browser is for 'top' or 'report' */ | ||
| 1372 | static inline bool is_report_browser(void *timer) | ||
| 1373 | { | ||
| 1374 | return timer == NULL; | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | /* | 1290 | /* |
| 1378 | * Only runtime switching of perf data file will make "input_name" point | 1291 | * Only runtime switching of perf data file will make "input_name" point |
| 1379 | * to a malloced buffer. So add "is_input_name_malloced" flag to decide | 1292 | * to a malloced buffer. So add "is_input_name_malloced" flag to decide |
| @@ -1488,7 +1401,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1488 | float min_pcnt, | 1401 | float min_pcnt, |
| 1489 | struct perf_session_env *env) | 1402 | struct perf_session_env *env) |
| 1490 | { | 1403 | { |
| 1491 | struct hists *hists = &evsel->hists; | 1404 | struct hists *hists = evsel__hists(evsel); |
| 1492 | struct hist_browser *browser = hist_browser__new(hists); | 1405 | struct hist_browser *browser = hist_browser__new(hists); |
| 1493 | struct branch_info *bi; | 1406 | struct branch_info *bi; |
| 1494 | struct pstack *fstack; | 1407 | struct pstack *fstack; |
| @@ -1498,6 +1411,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1498 | char buf[64]; | 1411 | char buf[64]; |
| 1499 | char script_opt[64]; | 1412 | char script_opt[64]; |
| 1500 | int delay_secs = hbt ? hbt->refresh : 0; | 1413 | int delay_secs = hbt ? hbt->refresh : 0; |
| 1414 | struct perf_hpp_fmt *fmt; | ||
| 1501 | 1415 | ||
| 1502 | #define HIST_BROWSER_HELP_COMMON \ | 1416 | #define HIST_BROWSER_HELP_COMMON \ |
| 1503 | "h/?/F1 Show this window\n" \ | 1417 | "h/?/F1 Show this window\n" \ |
| @@ -1529,6 +1443,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1529 | "P Print histograms to perf.hist.N\n" | 1443 | "P Print histograms to perf.hist.N\n" |
| 1530 | "t Zoom into current Thread\n" | 1444 | "t Zoom into current Thread\n" |
| 1531 | "V Verbose (DSO names in callchains, etc)\n" | 1445 | "V Verbose (DSO names in callchains, etc)\n" |
| 1446 | "z Toggle zeroing of samples\n" | ||
| 1532 | "/ Filter symbol by name"; | 1447 | "/ Filter symbol by name"; |
| 1533 | 1448 | ||
| 1534 | if (browser == NULL) | 1449 | if (browser == NULL) |
| @@ -1547,6 +1462,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1547 | 1462 | ||
| 1548 | memset(options, 0, sizeof(options)); | 1463 | memset(options, 0, sizeof(options)); |
| 1549 | 1464 | ||
| 1465 | perf_hpp__for_each_format(fmt) | ||
| 1466 | perf_hpp__reset_width(fmt, hists); | ||
| 1467 | |||
| 1468 | if (symbol_conf.col_width_list_str) | ||
| 1469 | perf_hpp__set_user_width(symbol_conf.col_width_list_str); | ||
| 1470 | |||
| 1550 | while (1) { | 1471 | while (1) { |
| 1551 | const struct thread *thread = NULL; | 1472 | const struct thread *thread = NULL; |
| 1552 | const struct dso *dso = NULL; | 1473 | const struct dso *dso = NULL; |
| @@ -1623,6 +1544,13 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
| 1623 | case 'F': | 1544 | case 'F': |
| 1624 | symbol_conf.filter_relative ^= 1; | 1545 | symbol_conf.filter_relative ^= 1; |
| 1625 | continue; | 1546 | continue; |
| 1547 | case 'z': | ||
| 1548 | if (!is_report_browser(hbt)) { | ||
| 1549 | struct perf_top *top = hbt->arg; | ||
| 1550 | |||
| 1551 | top->zero = !top->zero; | ||
| 1552 | } | ||
| 1553 | continue; | ||
| 1626 | case K_F1: | 1554 | case K_F1: |
| 1627 | case 'h': | 1555 | case 'h': |
| 1628 | case '?': | 1556 | case '?': |
| @@ -1888,8 +1816,9 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
| 1888 | struct perf_evsel_menu *menu = container_of(browser, | 1816 | struct perf_evsel_menu *menu = container_of(browser, |
| 1889 | struct perf_evsel_menu, b); | 1817 | struct perf_evsel_menu, b); |
| 1890 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); | 1818 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); |
| 1819 | struct hists *hists = evsel__hists(evsel); | ||
| 1891 | bool current_entry = ui_browser__is_current_entry(browser, row); | 1820 | bool current_entry = ui_browser__is_current_entry(browser, row); |
| 1892 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1821 | unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
| 1893 | const char *ev_name = perf_evsel__name(evsel); | 1822 | const char *ev_name = perf_evsel__name(evsel); |
| 1894 | char bf[256], unit; | 1823 | char bf[256], unit; |
| 1895 | const char *warn = " "; | 1824 | const char *warn = " "; |
| @@ -1904,7 +1833,8 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
| 1904 | ev_name = perf_evsel__group_name(evsel); | 1833 | ev_name = perf_evsel__group_name(evsel); |
| 1905 | 1834 | ||
| 1906 | for_each_group_member(pos, evsel) { | 1835 | for_each_group_member(pos, evsel) { |
| 1907 | nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1836 | struct hists *pos_hists = evsel__hists(pos); |
| 1837 | nr_events += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
| 1908 | } | 1838 | } |
| 1909 | } | 1839 | } |
| 1910 | 1840 | ||
| @@ -1913,7 +1843,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
| 1913 | unit, unit == ' ' ? "" : " ", ev_name); | 1843 | unit, unit == ' ' ? "" : " ", ev_name); |
| 1914 | slsmg_printf("%s", bf); | 1844 | slsmg_printf("%s", bf); |
| 1915 | 1845 | ||
| 1916 | nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | 1846 | nr_events = hists->stats.nr_events[PERF_RECORD_LOST]; |
| 1917 | if (nr_events != 0) { | 1847 | if (nr_events != 0) { |
| 1918 | menu->lost_events = true; | 1848 | menu->lost_events = true; |
| 1919 | if (!current_entry) | 1849 | if (!current_entry) |
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 6ca60e482cdc..fc654fb77ace 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 | } |
| @@ -319,7 +319,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | |||
| 319 | gtk_container_add(GTK_CONTAINER(window), vbox); | 319 | gtk_container_add(GTK_CONTAINER(window), vbox); |
| 320 | 320 | ||
| 321 | evlist__for_each(evlist, pos) { | 321 | evlist__for_each(evlist, pos) { |
| 322 | struct hists *hists = &pos->hists; | 322 | struct hists *hists = evsel__hists(pos); |
| 323 | const char *evname = perf_evsel__name(pos); | 323 | const char *evname = perf_evsel__name(pos); |
| 324 | GtkWidget *scrolled_window; | 324 | GtkWidget *scrolled_window; |
| 325 | GtkWidget *tab_label; | 325 | GtkWidget *tab_label; |
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..7dabde14ea54 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 | ||
| @@ -472,7 +478,7 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, | |||
| 472 | 478 | ||
| 473 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | 479 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); |
| 474 | 480 | ||
| 475 | if (addr < sym->start || addr > sym->end) | 481 | if (addr < sym->start || addr >= sym->end) |
| 476 | return -ERANGE; | 482 | return -ERANGE; |
| 477 | 483 | ||
| 478 | offset = addr - sym->start; | 484 | offset = addr - sym->start; |
| @@ -830,7 +836,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
| 830 | end = map__rip_2objdump(map, sym->end); | 836 | end = map__rip_2objdump(map, sym->end); |
| 831 | 837 | ||
| 832 | offset = line_ip - start; | 838 | offset = line_ip - start; |
| 833 | if ((u64)line_ip < start || (u64)line_ip > end) | 839 | if ((u64)line_ip < start || (u64)line_ip >= end) |
| 834 | offset = -1; | 840 | offset = -1; |
| 835 | else | 841 | else |
| 836 | parsed_line = tmp2 + 1; | 842 | parsed_line = tmp2 + 1; |
| @@ -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 | ||
| @@ -963,7 +966,7 @@ fallback: | |||
| 963 | kce.kcore_filename = symfs_filename; | 966 | kce.kcore_filename = symfs_filename; |
| 964 | kce.addr = map__rip_2objdump(map, sym->start); | 967 | kce.addr = map__rip_2objdump(map, sym->start); |
| 965 | kce.offs = sym->start; | 968 | kce.offs = sym->start; |
| 966 | kce.len = sym->end + 1 - sym->start; | 969 | kce.len = sym->end - sym->start; |
| 967 | if (!kcore_extract__create(&kce)) { | 970 | if (!kcore_extract__create(&kce)) { |
| 968 | delete_extract = true; | 971 | delete_extract = true; |
| 969 | strlcpy(symfs_filename, kce.extract_filename, | 972 | strlcpy(symfs_filename, kce.extract_filename, |
| @@ -984,7 +987,7 @@ fallback: | |||
| 984 | disassembler_style ? "-M " : "", | 987 | disassembler_style ? "-M " : "", |
| 985 | disassembler_style ? disassembler_style : "", | 988 | disassembler_style ? disassembler_style : "", |
| 986 | map__rip_2objdump(map, sym->start), | 989 | map__rip_2objdump(map, sym->start), |
| 987 | map__rip_2objdump(map, sym->end+1), | 990 | map__rip_2objdump(map, sym->end), |
| 988 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", | 991 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", |
| 989 | symbol_conf.annotate_src ? "-S" : "", | 992 | symbol_conf.annotate_src ? "-S" : "", |
| 990 | symfs_filename, filename); | 993 | symfs_filename, filename); |
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..94cfefddf4db 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; |
| @@ -62,6 +65,8 @@ struct callchain_param { | |||
| 62 | enum chain_key key; | 65 | enum chain_key key; |
| 63 | }; | 66 | }; |
| 64 | 67 | ||
| 68 | extern struct callchain_param callchain_param; | ||
| 69 | |||
| 65 | struct callchain_list { | 70 | struct callchain_list { |
| 66 | u64 ip; | 71 | u64 ip; |
| 67 | struct map_symbol ms; | 72 | struct map_symbol ms; |
| @@ -154,7 +159,6 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
| 154 | struct option; | 159 | struct option; |
| 155 | struct hist_entry; | 160 | struct hist_entry; |
| 156 | 161 | ||
| 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); | 162 | 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); | 163 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); |
| 160 | 164 | ||
| @@ -166,7 +170,9 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node * | |||
| 166 | bool hide_unresolved); | 170 | bool hide_unresolved); |
| 167 | 171 | ||
| 168 | extern const char record_callchain_help[]; | 172 | extern const char record_callchain_help[]; |
| 173 | int parse_callchain_record_opt(const char *arg); | ||
| 169 | int parse_callchain_report_opt(const char *arg); | 174 | int parse_callchain_report_opt(const char *arg); |
| 175 | int perf_callchain_config(const char *var, const char *value); | ||
| 170 | 176 | ||
| 171 | static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, | 177 | static inline void callchain_cursor_snapshot(struct callchain_cursor *dest, |
| 172 | struct callchain_cursor *src) | 178 | 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..5699e7e2a790 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; |
| @@ -188,6 +190,32 @@ enum perf_user_event_type { /* above any possible kernel type */ | |||
| 188 | PERF_RECORD_HEADER_MAX | 190 | PERF_RECORD_HEADER_MAX |
| 189 | }; | 191 | }; |
| 190 | 192 | ||
| 193 | /* | ||
| 194 | * The kernel collects the number of events it couldn't send in a stretch and | ||
| 195 | * when possible sends this number in a PERF_RECORD_LOST event. The number of | ||
| 196 | * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while | ||
| 197 | * total_lost tells exactly how many events the kernel in fact lost, i.e. it is | ||
| 198 | * the sum of all struct lost_event.lost fields reported. | ||
| 199 | * | ||
| 200 | * The total_period is needed because by default auto-freq is used, so | ||
| 201 | * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get | ||
| 202 | * the total number of low level events, it is necessary to to sum all struct | ||
| 203 | * sample_event.period and stash the result in total_period. | ||
| 204 | */ | ||
| 205 | struct events_stats { | ||
| 206 | u64 total_period; | ||
| 207 | u64 total_non_filtered_period; | ||
| 208 | u64 total_lost; | ||
| 209 | u64 total_invalid_chains; | ||
| 210 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | ||
| 211 | u32 nr_non_filtered_samples; | ||
| 212 | u32 nr_lost_warned; | ||
| 213 | u32 nr_unknown_events; | ||
| 214 | u32 nr_invalid_chains; | ||
| 215 | u32 nr_unknown_id; | ||
| 216 | u32 nr_unprocessable_samples; | ||
| 217 | }; | ||
| 218 | |||
| 191 | struct attr_event { | 219 | struct attr_event { |
| 192 | struct perf_event_header header; | 220 | struct perf_event_header header; |
| 193 | struct perf_event_attr attr; | 221 | struct perf_event_attr attr; |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 814e954c1318..3c9e77d6b4c2 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; |
| @@ -845,6 +1003,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) | |||
| 845 | 1003 | ||
| 846 | out_delete_threads: | 1004 | out_delete_threads: |
| 847 | thread_map__delete(evlist->threads); | 1005 | thread_map__delete(evlist->threads); |
| 1006 | evlist->threads = NULL; | ||
| 848 | return -1; | 1007 | return -1; |
| 849 | } | 1008 | } |
| 850 | 1009 | ||
| @@ -1017,11 +1176,51 @@ void perf_evlist__close(struct perf_evlist *evlist) | |||
| 1017 | } | 1176 | } |
| 1018 | } | 1177 | } |
| 1019 | 1178 | ||
| 1179 | static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist) | ||
| 1180 | { | ||
| 1181 | int err = -ENOMEM; | ||
| 1182 | |||
| 1183 | /* | ||
| 1184 | * Try reading /sys/devices/system/cpu/online to get | ||
| 1185 | * an all cpus map. | ||
| 1186 | * | ||
| 1187 | * FIXME: -ENOMEM is the best we can do here, the cpu_map | ||
| 1188 | * code needs an overhaul to properly forward the | ||
| 1189 | * error, and we may not want to do that fallback to a | ||
| 1190 | * default cpu identity map :-\ | ||
| 1191 | */ | ||
| 1192 | evlist->cpus = cpu_map__new(NULL); | ||
| 1193 | if (evlist->cpus == NULL) | ||
| 1194 | goto out; | ||
| 1195 | |||
| 1196 | evlist->threads = thread_map__new_dummy(); | ||
| 1197 | if (evlist->threads == NULL) | ||
| 1198 | goto out_free_cpus; | ||
| 1199 | |||
| 1200 | err = 0; | ||
| 1201 | out: | ||
| 1202 | return err; | ||
| 1203 | out_free_cpus: | ||
| 1204 | cpu_map__delete(evlist->cpus); | ||
| 1205 | evlist->cpus = NULL; | ||
| 1206 | goto out; | ||
| 1207 | } | ||
| 1208 | |||
| 1020 | int perf_evlist__open(struct perf_evlist *evlist) | 1209 | int perf_evlist__open(struct perf_evlist *evlist) |
| 1021 | { | 1210 | { |
| 1022 | struct perf_evsel *evsel; | 1211 | struct perf_evsel *evsel; |
| 1023 | int err; | 1212 | int err; |
| 1024 | 1213 | ||
| 1214 | /* | ||
| 1215 | * Default: one fd per CPU, all threads, aka systemwide | ||
| 1216 | * as sys_perf_event_open(cpu = -1, thread = -1) is EINVAL | ||
| 1217 | */ | ||
| 1218 | if (evlist->threads == NULL && evlist->cpus == NULL) { | ||
| 1219 | err = perf_evlist__create_syswide_maps(evlist); | ||
| 1220 | if (err < 0) | ||
| 1221 | goto out_err; | ||
| 1222 | } | ||
| 1223 | |||
| 1025 | perf_evlist__update_id_pos(evlist); | 1224 | perf_evlist__update_id_pos(evlist); |
| 1026 | 1225 | ||
| 1027 | evlist__for_each(evlist, evsel) { | 1226 | evlist__for_each(evlist, evsel) { |
| @@ -1061,6 +1260,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
| 1061 | } | 1260 | } |
| 1062 | 1261 | ||
| 1063 | if (!evlist->workload.pid) { | 1262 | if (!evlist->workload.pid) { |
| 1263 | int ret; | ||
| 1264 | |||
| 1064 | if (pipe_output) | 1265 | if (pipe_output) |
| 1065 | dup2(2, 1); | 1266 | dup2(2, 1); |
| 1066 | 1267 | ||
| @@ -1078,8 +1279,22 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
| 1078 | /* | 1279 | /* |
| 1079 | * Wait until the parent tells us to go. | 1280 | * Wait until the parent tells us to go. |
| 1080 | */ | 1281 | */ |
| 1081 | if (read(go_pipe[0], &bf, 1) == -1) | 1282 | ret = read(go_pipe[0], &bf, 1); |
| 1082 | perror("unable to read pipe"); | 1283 | /* |
| 1284 | * The parent will ask for the execvp() to be performed by | ||
| 1285 | * writing exactly one byte, in workload.cork_fd, usually via | ||
| 1286 | * perf_evlist__start_workload(). | ||
| 1287 | * | ||
| 1288 | * For cancelling the workload without actuallin running it, | ||
| 1289 | * the parent will just close workload.cork_fd, without writing | ||
| 1290 | * anything, i.e. read will return zero and we just exit() | ||
| 1291 | * here. | ||
| 1292 | */ | ||
| 1293 | if (ret != 1) { | ||
| 1294 | if (ret == -1) | ||
| 1295 | perror("unable to read pipe"); | ||
| 1296 | exit(ret); | ||
| 1297 | } | ||
| 1083 | 1298 | ||
| 1084 | execvp(argv[0], (char **)argv); | 1299 | execvp(argv[0], (char **)argv); |
| 1085 | 1300 | ||
| @@ -1102,8 +1317,14 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar | |||
| 1102 | sigaction(SIGUSR1, &act, NULL); | 1317 | sigaction(SIGUSR1, &act, NULL); |
| 1103 | } | 1318 | } |
| 1104 | 1319 | ||
| 1105 | if (target__none(target)) | 1320 | if (target__none(target)) { |
| 1321 | if (evlist->threads == NULL) { | ||
| 1322 | fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n", | ||
| 1323 | __func__, __LINE__); | ||
| 1324 | goto out_close_pipes; | ||
| 1325 | } | ||
| 1106 | evlist->threads->map[0] = evlist->workload.pid; | 1326 | evlist->threads->map[0] = evlist->workload.pid; |
| 1327 | } | ||
| 1107 | 1328 | ||
| 1108 | close(child_ready_pipe[1]); | 1329 | close(child_ready_pipe[1]); |
| 1109 | close(go_pipe[0]); | 1330 | close(go_pipe[0]); |
| @@ -1202,7 +1423,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | |||
| 1202 | int err, char *buf, size_t size) | 1423 | int err, char *buf, size_t size) |
| 1203 | { | 1424 | { |
| 1204 | int printed, value; | 1425 | int printed, value; |
| 1205 | char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | 1426 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); |
| 1206 | 1427 | ||
| 1207 | switch (err) { | 1428 | switch (err) { |
| 1208 | case EACCES: | 1429 | case EACCES: |
| @@ -1250,3 +1471,19 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
| 1250 | 1471 | ||
| 1251 | list_splice(&move, &evlist->entries); | 1472 | list_splice(&move, &evlist->entries); |
| 1252 | } | 1473 | } |
| 1474 | |||
| 1475 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | ||
| 1476 | struct perf_evsel *tracking_evsel) | ||
| 1477 | { | ||
| 1478 | struct perf_evsel *evsel; | ||
| 1479 | |||
| 1480 | if (tracking_evsel->tracking) | ||
| 1481 | return; | ||
| 1482 | |||
| 1483 | evlist__for_each(evlist, evsel) { | ||
| 1484 | if (evsel != tracking_evsel) | ||
| 1485 | evsel->tracking = false; | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | tracking_evsel->tracking = true; | ||
| 1489 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index f5173cd63693..649b0c597283 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 | ||
| @@ -107,6 +117,8 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
| 107 | void *ucontext)); | 117 | void *ucontext)); |
| 108 | int perf_evlist__start_workload(struct perf_evlist *evlist); | 118 | int perf_evlist__start_workload(struct perf_evlist *evlist); |
| 109 | 119 | ||
| 120 | struct option; | ||
| 121 | |||
| 110 | int perf_evlist__parse_mmap_pages(const struct option *opt, | 122 | int perf_evlist__parse_mmap_pages(const struct option *opt, |
| 111 | const char *str, | 123 | const char *str, |
| 112 | int unset); | 124 | int unset); |
| @@ -122,6 +134,8 @@ int perf_evlist__disable_event(struct perf_evlist *evlist, | |||
| 122 | struct perf_evsel *evsel); | 134 | struct perf_evsel *evsel); |
| 123 | int perf_evlist__enable_event(struct perf_evlist *evlist, | 135 | int perf_evlist__enable_event(struct perf_evlist *evlist, |
| 124 | struct perf_evsel *evsel); | 136 | struct perf_evsel *evsel); |
| 137 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, | ||
| 138 | struct perf_evsel *evsel, int idx); | ||
| 125 | 139 | ||
| 126 | void perf_evlist__set_selected(struct perf_evlist *evlist, | 140 | void perf_evlist__set_selected(struct perf_evlist *evlist, |
| 127 | struct perf_evsel *evsel); | 141 | struct perf_evsel *evsel); |
| @@ -262,4 +276,7 @@ void perf_evlist__to_front(struct perf_evlist *evlist, | |||
| 262 | #define evlist__for_each_safe(evlist, tmp, evsel) \ | 276 | #define evlist__for_each_safe(evlist, tmp, evsel) \ |
| 263 | __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) | 277 | __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) |
| 264 | 278 | ||
| 279 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | ||
| 280 | struct perf_evsel *tracking_evsel); | ||
| 281 | |||
| 265 | #endif /* __PERF_EVLIST_H */ | 282 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 21a373ebea22..2f9e68025ede 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #include <linux/perf_event.h> | 15 | #include <linux/perf_event.h> |
| 16 | #include <sys/resource.h> | 16 | #include <sys/resource.h> |
| 17 | #include "asm/bug.h" | 17 | #include "asm/bug.h" |
| 18 | #include "callchain.h" | ||
| 19 | #include "cgroup.h" | ||
| 18 | #include "evsel.h" | 20 | #include "evsel.h" |
| 19 | #include "evlist.h" | 21 | #include "evlist.h" |
| 20 | #include "util.h" | 22 | #include "util.h" |
| @@ -32,6 +34,48 @@ static struct { | |||
| 32 | bool cloexec; | 34 | bool cloexec; |
| 33 | } perf_missing_features; | 35 | } perf_missing_features; |
| 34 | 36 | ||
| 37 | static int perf_evsel__no_extra_init(struct perf_evsel *evsel __maybe_unused) | ||
| 38 | { | ||
| 39 | return 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | static void perf_evsel__no_extra_fini(struct perf_evsel *evsel __maybe_unused) | ||
| 43 | { | ||
| 44 | } | ||
| 45 | |||
| 46 | static struct { | ||
| 47 | size_t size; | ||
| 48 | int (*init)(struct perf_evsel *evsel); | ||
| 49 | void (*fini)(struct perf_evsel *evsel); | ||
| 50 | } perf_evsel__object = { | ||
| 51 | .size = sizeof(struct perf_evsel), | ||
| 52 | .init = perf_evsel__no_extra_init, | ||
| 53 | .fini = perf_evsel__no_extra_fini, | ||
| 54 | }; | ||
| 55 | |||
| 56 | int perf_evsel__object_config(size_t object_size, | ||
| 57 | int (*init)(struct perf_evsel *evsel), | ||
| 58 | void (*fini)(struct perf_evsel *evsel)) | ||
| 59 | { | ||
| 60 | |||
| 61 | if (object_size == 0) | ||
| 62 | goto set_methods; | ||
| 63 | |||
| 64 | if (perf_evsel__object.size > object_size) | ||
| 65 | return -EINVAL; | ||
| 66 | |||
| 67 | perf_evsel__object.size = object_size; | ||
| 68 | |||
| 69 | set_methods: | ||
| 70 | if (init != NULL) | ||
| 71 | perf_evsel__object.init = init; | ||
| 72 | |||
| 73 | if (fini != NULL) | ||
| 74 | perf_evsel__object.fini = fini; | ||
| 75 | |||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | |||
| 35 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 79 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
| 36 | 80 | ||
| 37 | int __perf_evsel__sample_size(u64 sample_type) | 81 | int __perf_evsel__sample_size(u64 sample_type) |
| @@ -116,16 +160,6 @@ void perf_evsel__calc_id_pos(struct perf_evsel *evsel) | |||
| 116 | evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type); | 160 | evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type); |
| 117 | } | 161 | } |
| 118 | 162 | ||
| 119 | void hists__init(struct hists *hists) | ||
| 120 | { | ||
| 121 | memset(hists, 0, sizeof(*hists)); | ||
| 122 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | ||
| 123 | hists->entries_in = &hists->entries_in_array[0]; | ||
| 124 | hists->entries_collapsed = RB_ROOT; | ||
| 125 | hists->entries = RB_ROOT; | ||
| 126 | pthread_mutex_init(&hists->lock, NULL); | ||
| 127 | } | ||
| 128 | |||
| 129 | void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, | 163 | void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, |
| 130 | enum perf_event_sample_format bit) | 164 | enum perf_event_sample_format bit) |
| 131 | { | 165 | { |
| @@ -162,19 +196,20 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
| 162 | struct perf_event_attr *attr, int idx) | 196 | struct perf_event_attr *attr, int idx) |
| 163 | { | 197 | { |
| 164 | evsel->idx = idx; | 198 | evsel->idx = idx; |
| 199 | evsel->tracking = !idx; | ||
| 165 | evsel->attr = *attr; | 200 | evsel->attr = *attr; |
| 166 | evsel->leader = evsel; | 201 | evsel->leader = evsel; |
| 167 | evsel->unit = ""; | 202 | evsel->unit = ""; |
| 168 | evsel->scale = 1.0; | 203 | evsel->scale = 1.0; |
| 169 | INIT_LIST_HEAD(&evsel->node); | 204 | INIT_LIST_HEAD(&evsel->node); |
| 170 | hists__init(&evsel->hists); | 205 | perf_evsel__object.init(evsel); |
| 171 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); | 206 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); |
| 172 | perf_evsel__calc_id_pos(evsel); | 207 | perf_evsel__calc_id_pos(evsel); |
| 173 | } | 208 | } |
| 174 | 209 | ||
| 175 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) | 210 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) |
| 176 | { | 211 | { |
| 177 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 212 | struct perf_evsel *evsel = zalloc(perf_evsel__object.size); |
| 178 | 213 | ||
| 179 | if (evsel != NULL) | 214 | if (evsel != NULL) |
| 180 | perf_evsel__init(evsel, attr, idx); | 215 | perf_evsel__init(evsel, attr, idx); |
| @@ -184,7 +219,7 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) | |||
| 184 | 219 | ||
| 185 | struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) | 220 | struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) |
| 186 | { | 221 | { |
| 187 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 222 | struct perf_evsel *evsel = zalloc(perf_evsel__object.size); |
| 188 | 223 | ||
| 189 | if (evsel != NULL) { | 224 | if (evsel != NULL) { |
| 190 | struct perf_event_attr attr = { | 225 | struct perf_event_attr attr = { |
| @@ -502,20 +537,19 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) | |||
| 502 | } | 537 | } |
| 503 | 538 | ||
| 504 | static void | 539 | static void |
| 505 | perf_evsel__config_callgraph(struct perf_evsel *evsel, | 540 | perf_evsel__config_callgraph(struct perf_evsel *evsel) |
| 506 | struct record_opts *opts) | ||
| 507 | { | 541 | { |
| 508 | bool function = perf_evsel__is_function_event(evsel); | 542 | bool function = perf_evsel__is_function_event(evsel); |
| 509 | struct perf_event_attr *attr = &evsel->attr; | 543 | struct perf_event_attr *attr = &evsel->attr; |
| 510 | 544 | ||
| 511 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); | 545 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); |
| 512 | 546 | ||
| 513 | if (opts->call_graph == CALLCHAIN_DWARF) { | 547 | if (callchain_param.record_mode == CALLCHAIN_DWARF) { |
| 514 | if (!function) { | 548 | if (!function) { |
| 515 | perf_evsel__set_sample_bit(evsel, REGS_USER); | 549 | perf_evsel__set_sample_bit(evsel, REGS_USER); |
| 516 | perf_evsel__set_sample_bit(evsel, STACK_USER); | 550 | perf_evsel__set_sample_bit(evsel, STACK_USER); |
| 517 | attr->sample_regs_user = PERF_REGS_MASK; | 551 | attr->sample_regs_user = PERF_REGS_MASK; |
| 518 | attr->sample_stack_user = opts->stack_dump_size; | 552 | attr->sample_stack_user = callchain_param.dump_size; |
| 519 | attr->exclude_callchain_user = 1; | 553 | attr->exclude_callchain_user = 1; |
| 520 | } else { | 554 | } else { |
| 521 | pr_info("Cannot use DWARF unwind for function trace event," | 555 | pr_info("Cannot use DWARF unwind for function trace event," |
| @@ -561,7 +595,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 561 | { | 595 | { |
| 562 | struct perf_evsel *leader = evsel->leader; | 596 | struct perf_evsel *leader = evsel->leader; |
| 563 | struct perf_event_attr *attr = &evsel->attr; | 597 | struct perf_event_attr *attr = &evsel->attr; |
| 564 | int track = !evsel->idx; /* only the first counter needs these */ | 598 | int track = evsel->tracking; |
| 565 | bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; | 599 | bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; |
| 566 | 600 | ||
| 567 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; | 601 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; |
| @@ -624,8 +658,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 624 | attr->mmap_data = track; | 658 | attr->mmap_data = track; |
| 625 | } | 659 | } |
| 626 | 660 | ||
| 627 | if (opts->call_graph_enabled && !evsel->no_aux_samples) | 661 | if (callchain_param.enabled && !evsel->no_aux_samples) |
| 628 | perf_evsel__config_callgraph(evsel, opts); | 662 | perf_evsel__config_callgraph(evsel); |
| 629 | 663 | ||
| 630 | if (target__has_cpu(&opts->target)) | 664 | if (target__has_cpu(&opts->target)) |
| 631 | perf_evsel__set_sample_bit(evsel, CPU); | 665 | perf_evsel__set_sample_bit(evsel, CPU); |
| @@ -633,9 +667,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 633 | if (opts->period) | 667 | if (opts->period) |
| 634 | perf_evsel__set_sample_bit(evsel, PERIOD); | 668 | perf_evsel__set_sample_bit(evsel, PERIOD); |
| 635 | 669 | ||
| 636 | if (!perf_missing_features.sample_id_all && | 670 | /* |
| 637 | (opts->sample_time || !opts->no_inherit || | 671 | * When the user explicitely disabled time don't force it here. |
| 638 | target__has_cpu(&opts->target) || per_cpu)) | 672 | */ |
| 673 | if (opts->sample_time && | ||
| 674 | (!perf_missing_features.sample_id_all && | ||
| 675 | (!opts->no_inherit || target__has_cpu(&opts->target) || per_cpu))) | ||
| 639 | perf_evsel__set_sample_bit(evsel, TIME); | 676 | perf_evsel__set_sample_bit(evsel, TIME); |
| 640 | 677 | ||
| 641 | if (opts->raw_samples && !evsel->no_aux_samples) { | 678 | if (opts->raw_samples && !evsel->no_aux_samples) { |
| @@ -689,9 +726,13 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) | |||
| 689 | } | 726 | } |
| 690 | } | 727 | } |
| 691 | 728 | ||
| 692 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 729 | static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
| 693 | { | 730 | { |
| 694 | int cpu, thread; | 731 | int cpu, thread; |
| 732 | |||
| 733 | if (evsel->system_wide) | ||
| 734 | nthreads = 1; | ||
| 735 | |||
| 695 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); | 736 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); |
| 696 | 737 | ||
| 697 | if (evsel->fd) { | 738 | if (evsel->fd) { |
| @@ -710,6 +751,9 @@ static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthrea | |||
| 710 | { | 751 | { |
| 711 | int cpu, thread; | 752 | int cpu, thread; |
| 712 | 753 | ||
| 754 | if (evsel->system_wide) | ||
| 755 | nthreads = 1; | ||
| 756 | |||
| 713 | for (cpu = 0; cpu < ncpus; cpu++) { | 757 | for (cpu = 0; cpu < ncpus; cpu++) { |
| 714 | for (thread = 0; thread < nthreads; thread++) { | 758 | for (thread = 0; thread < nthreads; thread++) { |
| 715 | int fd = FD(evsel, cpu, thread), | 759 | int fd = FD(evsel, cpu, thread), |
| @@ -740,6 +784,9 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
| 740 | 784 | ||
| 741 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 785 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
| 742 | { | 786 | { |
| 787 | if (evsel->system_wide) | ||
| 788 | nthreads = 1; | ||
| 789 | |||
| 743 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | 790 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); |
| 744 | if (evsel->sample_id == NULL) | 791 | if (evsel->sample_id == NULL) |
| 745 | return -ENOMEM; | 792 | return -ENOMEM; |
| @@ -767,13 +814,13 @@ int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | |||
| 767 | return evsel->counts != NULL ? 0 : -ENOMEM; | 814 | return evsel->counts != NULL ? 0 : -ENOMEM; |
| 768 | } | 815 | } |
| 769 | 816 | ||
| 770 | void perf_evsel__free_fd(struct perf_evsel *evsel) | 817 | static void perf_evsel__free_fd(struct perf_evsel *evsel) |
| 771 | { | 818 | { |
| 772 | xyarray__delete(evsel->fd); | 819 | xyarray__delete(evsel->fd); |
| 773 | evsel->fd = NULL; | 820 | evsel->fd = NULL; |
| 774 | } | 821 | } |
| 775 | 822 | ||
| 776 | void perf_evsel__free_id(struct perf_evsel *evsel) | 823 | static void perf_evsel__free_id(struct perf_evsel *evsel) |
| 777 | { | 824 | { |
| 778 | xyarray__delete(evsel->sample_id); | 825 | xyarray__delete(evsel->sample_id); |
| 779 | evsel->sample_id = NULL; | 826 | evsel->sample_id = NULL; |
| @@ -784,6 +831,9 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
| 784 | { | 831 | { |
| 785 | int cpu, thread; | 832 | int cpu, thread; |
| 786 | 833 | ||
| 834 | if (evsel->system_wide) | ||
| 835 | nthreads = 1; | ||
| 836 | |||
| 787 | for (cpu = 0; cpu < ncpus; cpu++) | 837 | for (cpu = 0; cpu < ncpus; cpu++) |
| 788 | for (thread = 0; thread < nthreads; ++thread) { | 838 | for (thread = 0; thread < nthreads; ++thread) { |
| 789 | close(FD(evsel, cpu, thread)); | 839 | close(FD(evsel, cpu, thread)); |
| @@ -801,16 +851,17 @@ void perf_evsel__exit(struct perf_evsel *evsel) | |||
| 801 | assert(list_empty(&evsel->node)); | 851 | assert(list_empty(&evsel->node)); |
| 802 | perf_evsel__free_fd(evsel); | 852 | perf_evsel__free_fd(evsel); |
| 803 | perf_evsel__free_id(evsel); | 853 | perf_evsel__free_id(evsel); |
| 804 | } | ||
| 805 | |||
| 806 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
| 807 | { | ||
| 808 | perf_evsel__exit(evsel); | ||
| 809 | close_cgroup(evsel->cgrp); | 854 | close_cgroup(evsel->cgrp); |
| 810 | zfree(&evsel->group_name); | 855 | zfree(&evsel->group_name); |
| 811 | if (evsel->tp_format) | 856 | if (evsel->tp_format) |
| 812 | pevent_free_format(evsel->tp_format); | 857 | pevent_free_format(evsel->tp_format); |
| 813 | zfree(&evsel->name); | 858 | zfree(&evsel->name); |
| 859 | perf_evsel__object.fini(evsel); | ||
| 860 | } | ||
| 861 | |||
| 862 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
| 863 | { | ||
| 864 | perf_evsel__exit(evsel); | ||
| 814 | free(evsel); | 865 | free(evsel); |
| 815 | } | 866 | } |
| 816 | 867 | ||
| @@ -872,6 +923,9 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
| 872 | int cpu, thread; | 923 | int cpu, thread; |
| 873 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; | 924 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; |
| 874 | 925 | ||
| 926 | if (evsel->system_wide) | ||
| 927 | nthreads = 1; | ||
| 928 | |||
| 875 | aggr->val = aggr->ena = aggr->run = 0; | 929 | aggr->val = aggr->ena = aggr->run = 0; |
| 876 | 930 | ||
| 877 | for (cpu = 0; cpu < ncpus; cpu++) { | 931 | for (cpu = 0; cpu < ncpus; cpu++) { |
| @@ -994,13 +1048,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, | 1048 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
| 995 | struct thread_map *threads) | 1049 | struct thread_map *threads) |
| 996 | { | 1050 | { |
| 997 | int cpu, thread; | 1051 | int cpu, thread, nthreads; |
| 998 | unsigned long flags = PERF_FLAG_FD_CLOEXEC; | 1052 | unsigned long flags = PERF_FLAG_FD_CLOEXEC; |
| 999 | int pid = -1, err; | 1053 | int pid = -1, err; |
| 1000 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; | 1054 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; |
| 1001 | 1055 | ||
| 1056 | if (evsel->system_wide) | ||
| 1057 | nthreads = 1; | ||
| 1058 | else | ||
| 1059 | nthreads = threads->nr; | ||
| 1060 | |||
| 1002 | if (evsel->fd == NULL && | 1061 | if (evsel->fd == NULL && |
| 1003 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 1062 | perf_evsel__alloc_fd(evsel, cpus->nr, nthreads) < 0) |
| 1004 | return -ENOMEM; | 1063 | return -ENOMEM; |
| 1005 | 1064 | ||
| 1006 | if (evsel->cgrp) { | 1065 | if (evsel->cgrp) { |
| @@ -1024,10 +1083,10 @@ retry_sample_id: | |||
| 1024 | 1083 | ||
| 1025 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 1084 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
| 1026 | 1085 | ||
| 1027 | for (thread = 0; thread < threads->nr; thread++) { | 1086 | for (thread = 0; thread < nthreads; thread++) { |
| 1028 | int group_fd; | 1087 | int group_fd; |
| 1029 | 1088 | ||
| 1030 | if (!evsel->cgrp) | 1089 | if (!evsel->cgrp && !evsel->system_wide) |
| 1031 | pid = threads->map[thread]; | 1090 | pid = threads->map[thread]; |
| 1032 | 1091 | ||
| 1033 | group_fd = get_group_fd(evsel, cpu, thread); | 1092 | group_fd = get_group_fd(evsel, cpu, thread); |
| @@ -1100,7 +1159,7 @@ out_close: | |||
| 1100 | close(FD(evsel, cpu, thread)); | 1159 | close(FD(evsel, cpu, thread)); |
| 1101 | FD(evsel, cpu, thread) = -1; | 1160 | FD(evsel, cpu, thread) = -1; |
| 1102 | } | 1161 | } |
| 1103 | thread = threads->nr; | 1162 | thread = nthreads; |
| 1104 | } while (--cpu >= 0); | 1163 | } while (--cpu >= 0); |
| 1105 | return err; | 1164 | return err; |
| 1106 | } | 1165 | } |
| @@ -2002,6 +2061,8 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | |||
| 2002 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, | 2061 | int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, |
| 2003 | int err, char *msg, size_t size) | 2062 | int err, char *msg, size_t size) |
| 2004 | { | 2063 | { |
| 2064 | char sbuf[STRERR_BUFSIZE]; | ||
| 2065 | |||
| 2005 | switch (err) { | 2066 | switch (err) { |
| 2006 | case EPERM: | 2067 | case EPERM: |
| 2007 | case EACCES: | 2068 | case EACCES: |
| @@ -2036,13 +2097,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."); | 2097 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); |
| 2037 | #endif | 2098 | #endif |
| 2038 | break; | 2099 | break; |
| 2100 | case EBUSY: | ||
| 2101 | if (find_process("oprofiled")) | ||
| 2102 | return scnprintf(msg, size, | ||
| 2103 | "The PMU counters are busy/taken by another profiler.\n" | ||
| 2104 | "We found oprofile daemon running, please stop it and try again."); | ||
| 2105 | break; | ||
| 2039 | default: | 2106 | default: |
| 2040 | break; | 2107 | break; |
| 2041 | } | 2108 | } |
| 2042 | 2109 | ||
| 2043 | return scnprintf(msg, size, | 2110 | return scnprintf(msg, size, |
| 2044 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n" | 2111 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s).\n" |
| 2045 | "/bin/dmesg may provide additional information.\n" | 2112 | "/bin/dmesg may provide additional information.\n" |
| 2046 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", | 2113 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", |
| 2047 | err, strerror(err), perf_evsel__name(evsel)); | 2114 | err, strerror_r(err, sbuf, sizeof(sbuf)), |
| 2115 | perf_evsel__name(evsel)); | ||
| 2048 | } | 2116 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index d7f93ce0ebc1..163c5604e5d1 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
| @@ -7,8 +7,6 @@ | |||
| 7 | #include <linux/perf_event.h> | 7 | #include <linux/perf_event.h> |
| 8 | #include <linux/types.h> | 8 | #include <linux/types.h> |
| 9 | #include "xyarray.h" | 9 | #include "xyarray.h" |
| 10 | #include "cgroup.h" | ||
| 11 | #include "hist.h" | ||
| 12 | #include "symbol.h" | 10 | #include "symbol.h" |
| 13 | 11 | ||
| 14 | struct perf_counts_values { | 12 | struct perf_counts_values { |
| @@ -43,6 +41,8 @@ struct perf_sample_id { | |||
| 43 | u64 period; | 41 | u64 period; |
| 44 | }; | 42 | }; |
| 45 | 43 | ||
| 44 | struct cgroup_sel; | ||
| 45 | |||
| 46 | /** struct perf_evsel - event selector | 46 | /** struct perf_evsel - event selector |
| 47 | * | 47 | * |
| 48 | * @name - Can be set to retain the original event name passed by the user, | 48 | * @name - Can be set to retain the original event name passed by the user, |
| @@ -66,7 +66,6 @@ struct perf_evsel { | |||
| 66 | struct perf_counts *prev_raw_counts; | 66 | struct perf_counts *prev_raw_counts; |
| 67 | int idx; | 67 | int idx; |
| 68 | u32 ids; | 68 | u32 ids; |
| 69 | struct hists hists; | ||
| 70 | char *name; | 69 | char *name; |
| 71 | double scale; | 70 | double scale; |
| 72 | const char *unit; | 71 | const char *unit; |
| @@ -85,6 +84,8 @@ struct perf_evsel { | |||
| 85 | bool needs_swap; | 84 | bool needs_swap; |
| 86 | bool no_aux_samples; | 85 | bool no_aux_samples; |
| 87 | bool immediate; | 86 | bool immediate; |
| 87 | bool system_wide; | ||
| 88 | bool tracking; | ||
| 88 | /* parse modifier helper */ | 89 | /* parse modifier helper */ |
| 89 | int exclude_GH; | 90 | int exclude_GH; |
| 90 | int nr_members; | 91 | int nr_members; |
| @@ -98,13 +99,16 @@ union u64_swap { | |||
| 98 | u32 val32[2]; | 99 | u32 val32[2]; |
| 99 | }; | 100 | }; |
| 100 | 101 | ||
| 101 | #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) | ||
| 102 | |||
| 103 | struct cpu_map; | 102 | struct cpu_map; |
| 103 | struct target; | ||
| 104 | struct thread_map; | 104 | struct thread_map; |
| 105 | struct perf_evlist; | 105 | struct perf_evlist; |
| 106 | struct record_opts; | 106 | struct record_opts; |
| 107 | 107 | ||
| 108 | int perf_evsel__object_config(size_t object_size, | ||
| 109 | int (*init)(struct perf_evsel *evsel), | ||
| 110 | void (*fini)(struct perf_evsel *evsel)); | ||
| 111 | |||
| 108 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); | 112 | struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); |
| 109 | 113 | ||
| 110 | static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) | 114 | static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) |
| @@ -151,12 +155,9 @@ const char *perf_evsel__name(struct perf_evsel *evsel); | |||
| 151 | const char *perf_evsel__group_name(struct perf_evsel *evsel); | 155 | const char *perf_evsel__group_name(struct perf_evsel *evsel); |
| 152 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); | 156 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); |
| 153 | 157 | ||
| 154 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
| 155 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 158 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); |
| 156 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 159 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
| 157 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); | 160 | void perf_evsel__reset_counts(struct perf_evsel *evsel, int ncpus); |
| 158 | void perf_evsel__free_fd(struct perf_evsel *evsel); | ||
| 159 | void perf_evsel__free_id(struct perf_evsel *evsel); | ||
| 160 | void perf_evsel__free_counts(struct perf_evsel *evsel); | 161 | void perf_evsel__free_counts(struct perf_evsel *evsel); |
| 161 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 162 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
| 162 | 163 | ||
| @@ -279,8 +280,6 @@ static inline int perf_evsel__read_scaled(struct perf_evsel *evsel, | |||
| 279 | return __perf_evsel__read(evsel, ncpus, nthreads, true); | 280 | return __perf_evsel__read(evsel, ncpus, nthreads, true); |
| 280 | } | 281 | } |
| 281 | 282 | ||
| 282 | void hists__init(struct hists *hists); | ||
| 283 | |||
| 284 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | 283 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, |
| 285 | struct perf_sample *sample); | 284 | struct perf_sample *sample); |
| 286 | 285 | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 158c787ce0c4..26f5b2fe5dc8 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 | ||
| @@ -575,16 +579,12 @@ static int write_version(int fd, struct perf_header *h __maybe_unused, | |||
| 575 | return do_write_string(fd, perf_version_string); | 579 | return do_write_string(fd, perf_version_string); |
| 576 | } | 580 | } |
| 577 | 581 | ||
| 578 | static int write_cpudesc(int fd, struct perf_header *h __maybe_unused, | 582 | static int __write_cpudesc(int fd, const char *cpuinfo_proc) |
| 579 | struct perf_evlist *evlist __maybe_unused) | ||
| 580 | { | 583 | { |
| 581 | #ifndef CPUINFO_PROC | ||
| 582 | #define CPUINFO_PROC NULL | ||
| 583 | #endif | ||
| 584 | FILE *file; | 584 | FILE *file; |
| 585 | char *buf = NULL; | 585 | char *buf = NULL; |
| 586 | char *s, *p; | 586 | char *s, *p; |
| 587 | const char *search = CPUINFO_PROC; | 587 | const char *search = cpuinfo_proc; |
| 588 | size_t len = 0; | 588 | size_t len = 0; |
| 589 | int ret = -1; | 589 | int ret = -1; |
| 590 | 590 | ||
| @@ -634,6 +634,25 @@ done: | |||
| 634 | return ret; | 634 | return ret; |
| 635 | } | 635 | } |
| 636 | 636 | ||
| 637 | static int write_cpudesc(int fd, struct perf_header *h __maybe_unused, | ||
| 638 | struct perf_evlist *evlist __maybe_unused) | ||
| 639 | { | ||
| 640 | #ifndef CPUINFO_PROC | ||
| 641 | #define CPUINFO_PROC {"model name", } | ||
| 642 | #endif | ||
| 643 | const char *cpuinfo_procs[] = CPUINFO_PROC; | ||
| 644 | unsigned int i; | ||
| 645 | |||
| 646 | for (i = 0; i < ARRAY_SIZE(cpuinfo_procs); i++) { | ||
| 647 | int ret; | ||
| 648 | ret = __write_cpudesc(fd, cpuinfo_procs[i]); | ||
| 649 | if (ret >= 0) | ||
| 650 | return ret; | ||
| 651 | } | ||
| 652 | return -1; | ||
| 653 | } | ||
| 654 | |||
| 655 | |||
| 637 | static int write_nrcpus(int fd, struct perf_header *h __maybe_unused, | 656 | static int write_nrcpus(int fd, struct perf_header *h __maybe_unused, |
| 638 | struct perf_evlist *evlist __maybe_unused) | 657 | struct perf_evlist *evlist __maybe_unused) |
| 639 | { | 658 | { |
| @@ -1548,7 +1567,7 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
| 1548 | struct perf_session *session) | 1567 | struct perf_session *session) |
| 1549 | { | 1568 | { |
| 1550 | int err = -1; | 1569 | int err = -1; |
| 1551 | struct list_head *head; | 1570 | struct dsos *dsos; |
| 1552 | struct machine *machine; | 1571 | struct machine *machine; |
| 1553 | u16 misc; | 1572 | u16 misc; |
| 1554 | struct dso *dso; | 1573 | struct dso *dso; |
| @@ -1563,22 +1582,22 @@ static int __event_process_build_id(struct build_id_event *bev, | |||
| 1563 | switch (misc) { | 1582 | switch (misc) { |
| 1564 | case PERF_RECORD_MISC_KERNEL: | 1583 | case PERF_RECORD_MISC_KERNEL: |
| 1565 | dso_type = DSO_TYPE_KERNEL; | 1584 | dso_type = DSO_TYPE_KERNEL; |
| 1566 | head = &machine->kernel_dsos; | 1585 | dsos = &machine->kernel_dsos; |
| 1567 | break; | 1586 | break; |
| 1568 | case PERF_RECORD_MISC_GUEST_KERNEL: | 1587 | case PERF_RECORD_MISC_GUEST_KERNEL: |
| 1569 | dso_type = DSO_TYPE_GUEST_KERNEL; | 1588 | dso_type = DSO_TYPE_GUEST_KERNEL; |
| 1570 | head = &machine->kernel_dsos; | 1589 | dsos = &machine->kernel_dsos; |
| 1571 | break; | 1590 | break; |
| 1572 | case PERF_RECORD_MISC_USER: | 1591 | case PERF_RECORD_MISC_USER: |
| 1573 | case PERF_RECORD_MISC_GUEST_USER: | 1592 | case PERF_RECORD_MISC_GUEST_USER: |
| 1574 | dso_type = DSO_TYPE_USER; | 1593 | dso_type = DSO_TYPE_USER; |
| 1575 | head = &machine->user_dsos; | 1594 | dsos = &machine->user_dsos; |
| 1576 | break; | 1595 | break; |
| 1577 | default: | 1596 | default: |
| 1578 | goto out; | 1597 | goto out; |
| 1579 | } | 1598 | } |
| 1580 | 1599 | ||
| 1581 | dso = __dsos__findnew(head, filename); | 1600 | dso = __dsos__findnew(dsos, filename); |
| 1582 | if (dso != NULL) { | 1601 | if (dso != NULL) { |
| 1583 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 1602 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
| 1584 | 1603 | ||
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 30df6187ee02..6e88b9e395df 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include "hist.h" | 3 | #include "hist.h" |
| 4 | #include "session.h" | 4 | #include "session.h" |
| 5 | #include "sort.h" | 5 | #include "sort.h" |
| 6 | #include "evlist.h" | ||
| 6 | #include "evsel.h" | 7 | #include "evsel.h" |
| 7 | #include "annotate.h" | 8 | #include "annotate.h" |
| 8 | #include <math.h> | 9 | #include <math.h> |
| @@ -14,13 +15,6 @@ static bool hists__filter_entry_by_thread(struct hists *hists, | |||
| 14 | static bool hists__filter_entry_by_symbol(struct hists *hists, | 15 | static bool hists__filter_entry_by_symbol(struct hists *hists, |
| 15 | struct hist_entry *he); | 16 | struct hist_entry *he); |
| 16 | 17 | ||
| 17 | struct callchain_param callchain_param = { | ||
| 18 | .mode = CHAIN_GRAPH_REL, | ||
| 19 | .min_percent = 0.5, | ||
| 20 | .order = ORDER_CALLEE, | ||
| 21 | .key = CCKEY_FUNCTION | ||
| 22 | }; | ||
| 23 | |||
| 24 | u16 hists__col_len(struct hists *hists, enum hist_column col) | 18 | u16 hists__col_len(struct hists *hists, enum hist_column col) |
| 25 | { | 19 | { |
| 26 | return hists->col_len[col]; | 20 | return hists->col_len[col]; |
| @@ -277,6 +271,28 @@ void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | |||
| 277 | } | 271 | } |
| 278 | } | 272 | } |
| 279 | 273 | ||
| 274 | void hists__delete_entries(struct hists *hists) | ||
| 275 | { | ||
| 276 | struct rb_node *next = rb_first(&hists->entries); | ||
| 277 | struct hist_entry *n; | ||
| 278 | |||
| 279 | while (next) { | ||
| 280 | n = rb_entry(next, struct hist_entry, rb_node); | ||
| 281 | next = rb_next(&n->rb_node); | ||
| 282 | |||
| 283 | rb_erase(&n->rb_node, &hists->entries); | ||
| 284 | |||
| 285 | if (sort__need_collapse) | ||
| 286 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
| 287 | |||
| 288 | --hists->nr_entries; | ||
| 289 | if (!n->filtered) | ||
| 290 | --hists->nr_non_filtered_entries; | ||
| 291 | |||
| 292 | hist_entry__free(n); | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 280 | /* | 296 | /* |
| 281 | * histogram, sorted on item, collects periods | 297 | * histogram, sorted on item, collects periods |
| 282 | */ | 298 | */ |
| @@ -494,6 +510,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al | |||
| 494 | { | 510 | { |
| 495 | u64 cost; | 511 | u64 cost; |
| 496 | struct mem_info *mi = iter->priv; | 512 | struct mem_info *mi = iter->priv; |
| 513 | struct hists *hists = evsel__hists(iter->evsel); | ||
| 497 | struct hist_entry *he; | 514 | struct hist_entry *he; |
| 498 | 515 | ||
| 499 | if (mi == NULL) | 516 | if (mi == NULL) |
| @@ -510,7 +527,7 @@ iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al | |||
| 510 | * and this is indirectly achieved by passing period=weight here | 527 | * and this is indirectly achieved by passing period=weight here |
| 511 | * and the he_stat__add_period() function. | 528 | * and the he_stat__add_period() function. |
| 512 | */ | 529 | */ |
| 513 | he = __hists__add_entry(&iter->evsel->hists, al, iter->parent, NULL, mi, | 530 | he = __hists__add_entry(hists, al, iter->parent, NULL, mi, |
| 514 | cost, cost, 0, true); | 531 | cost, cost, 0, true); |
| 515 | if (!he) | 532 | if (!he) |
| 516 | return -ENOMEM; | 533 | return -ENOMEM; |
| @@ -524,13 +541,14 @@ iter_finish_mem_entry(struct hist_entry_iter *iter, | |||
| 524 | struct addr_location *al __maybe_unused) | 541 | struct addr_location *al __maybe_unused) |
| 525 | { | 542 | { |
| 526 | struct perf_evsel *evsel = iter->evsel; | 543 | struct perf_evsel *evsel = iter->evsel; |
| 544 | struct hists *hists = evsel__hists(evsel); | ||
| 527 | struct hist_entry *he = iter->he; | 545 | struct hist_entry *he = iter->he; |
| 528 | int err = -EINVAL; | 546 | int err = -EINVAL; |
| 529 | 547 | ||
| 530 | if (he == NULL) | 548 | if (he == NULL) |
| 531 | goto out; | 549 | goto out; |
| 532 | 550 | ||
| 533 | hists__inc_nr_samples(&evsel->hists, he->filtered); | 551 | hists__inc_nr_samples(hists, he->filtered); |
| 534 | 552 | ||
| 535 | err = hist_entry__append_callchain(he, iter->sample); | 553 | err = hist_entry__append_callchain(he, iter->sample); |
| 536 | 554 | ||
| @@ -596,6 +614,7 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a | |||
| 596 | { | 614 | { |
| 597 | struct branch_info *bi; | 615 | struct branch_info *bi; |
| 598 | struct perf_evsel *evsel = iter->evsel; | 616 | struct perf_evsel *evsel = iter->evsel; |
| 617 | struct hists *hists = evsel__hists(evsel); | ||
| 599 | struct hist_entry *he = NULL; | 618 | struct hist_entry *he = NULL; |
| 600 | int i = iter->curr; | 619 | int i = iter->curr; |
| 601 | int err = 0; | 620 | int err = 0; |
| @@ -609,12 +628,12 @@ iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *a | |||
| 609 | * The report shows the percentage of total branches captured | 628 | * The report shows the percentage of total branches captured |
| 610 | * and not events sampled. Thus we use a pseudo period of 1. | 629 | * and not events sampled. Thus we use a pseudo period of 1. |
| 611 | */ | 630 | */ |
| 612 | he = __hists__add_entry(&evsel->hists, al, iter->parent, &bi[i], NULL, | 631 | he = __hists__add_entry(hists, al, iter->parent, &bi[i], NULL, |
| 613 | 1, 1, 0, true); | 632 | 1, 1, 0, true); |
| 614 | if (he == NULL) | 633 | if (he == NULL) |
| 615 | return -ENOMEM; | 634 | return -ENOMEM; |
| 616 | 635 | ||
| 617 | hists__inc_nr_samples(&evsel->hists, he->filtered); | 636 | hists__inc_nr_samples(hists, he->filtered); |
| 618 | 637 | ||
| 619 | out: | 638 | out: |
| 620 | iter->he = he; | 639 | iter->he = he; |
| @@ -646,7 +665,7 @@ iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location | |||
| 646 | struct perf_sample *sample = iter->sample; | 665 | struct perf_sample *sample = iter->sample; |
| 647 | struct hist_entry *he; | 666 | struct hist_entry *he; |
| 648 | 667 | ||
| 649 | he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, | 668 | he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, |
| 650 | sample->period, sample->weight, | 669 | sample->period, sample->weight, |
| 651 | sample->transaction, true); | 670 | sample->transaction, true); |
| 652 | if (he == NULL) | 671 | if (he == NULL) |
| @@ -669,7 +688,7 @@ iter_finish_normal_entry(struct hist_entry_iter *iter, | |||
| 669 | 688 | ||
| 670 | iter->he = NULL; | 689 | iter->he = NULL; |
| 671 | 690 | ||
| 672 | hists__inc_nr_samples(&evsel->hists, he->filtered); | 691 | hists__inc_nr_samples(evsel__hists(evsel), he->filtered); |
| 673 | 692 | ||
| 674 | return hist_entry__append_callchain(he, sample); | 693 | return hist_entry__append_callchain(he, sample); |
| 675 | } | 694 | } |
| @@ -702,12 +721,13 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, | |||
| 702 | struct addr_location *al) | 721 | struct addr_location *al) |
| 703 | { | 722 | { |
| 704 | struct perf_evsel *evsel = iter->evsel; | 723 | struct perf_evsel *evsel = iter->evsel; |
| 724 | struct hists *hists = evsel__hists(evsel); | ||
| 705 | struct perf_sample *sample = iter->sample; | 725 | struct perf_sample *sample = iter->sample; |
| 706 | struct hist_entry **he_cache = iter->priv; | 726 | struct hist_entry **he_cache = iter->priv; |
| 707 | struct hist_entry *he; | 727 | struct hist_entry *he; |
| 708 | int err = 0; | 728 | int err = 0; |
| 709 | 729 | ||
| 710 | he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, | 730 | he = __hists__add_entry(hists, al, iter->parent, NULL, NULL, |
| 711 | sample->period, sample->weight, | 731 | sample->period, sample->weight, |
| 712 | sample->transaction, true); | 732 | sample->transaction, true); |
| 713 | if (he == NULL) | 733 | if (he == NULL) |
| @@ -724,7 +744,7 @@ iter_add_single_cumulative_entry(struct hist_entry_iter *iter, | |||
| 724 | */ | 744 | */ |
| 725 | callchain_cursor_commit(&callchain_cursor); | 745 | callchain_cursor_commit(&callchain_cursor); |
| 726 | 746 | ||
| 727 | hists__inc_nr_samples(&evsel->hists, he->filtered); | 747 | hists__inc_nr_samples(hists, he->filtered); |
| 728 | 748 | ||
| 729 | return err; | 749 | return err; |
| 730 | } | 750 | } |
| @@ -780,7 +800,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter, | |||
| 780 | } | 800 | } |
| 781 | } | 801 | } |
| 782 | 802 | ||
| 783 | he = __hists__add_entry(&evsel->hists, al, iter->parent, NULL, NULL, | 803 | he = __hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL, |
| 784 | sample->period, sample->weight, | 804 | sample->period, sample->weight, |
| 785 | sample->transaction, false); | 805 | sample->transaction, false); |
| 786 | if (he == NULL) | 806 | if (he == NULL) |
| @@ -1386,6 +1406,21 @@ int hists__link(struct hists *leader, struct hists *other) | |||
| 1386 | return 0; | 1406 | return 0; |
| 1387 | } | 1407 | } |
| 1388 | 1408 | ||
| 1409 | |||
| 1410 | size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp) | ||
| 1411 | { | ||
| 1412 | struct perf_evsel *pos; | ||
| 1413 | size_t ret = 0; | ||
| 1414 | |||
| 1415 | evlist__for_each(evlist, pos) { | ||
| 1416 | ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); | ||
| 1417 | ret += events_stats__fprintf(&evsel__hists(pos)->stats, fp); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | return ret; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | |||
| 1389 | u64 hists__total_period(struct hists *hists) | 1424 | u64 hists__total_period(struct hists *hists) |
| 1390 | { | 1425 | { |
| 1391 | return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : | 1426 | return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period : |
| @@ -1412,3 +1447,31 @@ int perf_hist_config(const char *var, const char *value) | |||
| 1412 | 1447 | ||
| 1413 | return 0; | 1448 | return 0; |
| 1414 | } | 1449 | } |
| 1450 | |||
| 1451 | static int hists_evsel__init(struct perf_evsel *evsel) | ||
| 1452 | { | ||
| 1453 | struct hists *hists = evsel__hists(evsel); | ||
| 1454 | |||
| 1455 | memset(hists, 0, sizeof(*hists)); | ||
| 1456 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | ||
| 1457 | hists->entries_in = &hists->entries_in_array[0]; | ||
| 1458 | hists->entries_collapsed = RB_ROOT; | ||
| 1459 | hists->entries = RB_ROOT; | ||
| 1460 | pthread_mutex_init(&hists->lock, NULL); | ||
| 1461 | return 0; | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | /* | ||
| 1465 | * XXX We probably need a hists_evsel__exit() to free the hist_entries | ||
| 1466 | * stored in the rbtree... | ||
| 1467 | */ | ||
| 1468 | |||
| 1469 | int hists__init(void) | ||
| 1470 | { | ||
| 1471 | int err = perf_evsel__object_config(sizeof(struct hists_evsel), | ||
| 1472 | hists_evsel__init, NULL); | ||
| 1473 | if (err) | ||
| 1474 | fputs("FATAL ERROR: Couldn't setup hists class\n", stderr); | ||
| 1475 | |||
| 1476 | return err; | ||
| 1477 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 742f49a85725..d0ef9a19a744 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -4,12 +4,11 @@ | |||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | #include <pthread.h> | 5 | #include <pthread.h> |
| 6 | #include "callchain.h" | 6 | #include "callchain.h" |
| 7 | #include "evsel.h" | ||
| 7 | #include "header.h" | 8 | #include "header.h" |
| 8 | #include "color.h" | 9 | #include "color.h" |
| 9 | #include "ui/progress.h" | 10 | #include "ui/progress.h" |
| 10 | 11 | ||
| 11 | extern struct callchain_param callchain_param; | ||
| 12 | |||
| 13 | struct hist_entry; | 12 | struct hist_entry; |
| 14 | struct addr_location; | 13 | struct addr_location; |
| 15 | struct symbol; | 14 | struct symbol; |
| @@ -23,32 +22,6 @@ enum hist_filter { | |||
| 23 | HIST_FILTER__HOST, | 22 | HIST_FILTER__HOST, |
| 24 | }; | 23 | }; |
| 25 | 24 | ||
| 26 | /* | ||
| 27 | * The kernel collects the number of events it couldn't send in a stretch and | ||
| 28 | * when possible sends this number in a PERF_RECORD_LOST event. The number of | ||
| 29 | * such "chunks" of lost events is stored in .nr_events[PERF_EVENT_LOST] while | ||
| 30 | * total_lost tells exactly how many events the kernel in fact lost, i.e. it is | ||
| 31 | * the sum of all struct lost_event.lost fields reported. | ||
| 32 | * | ||
| 33 | * The total_period is needed because by default auto-freq is used, so | ||
| 34 | * multipling nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get | ||
| 35 | * the total number of low level events, it is necessary to to sum all struct | ||
| 36 | * sample_event.period and stash the result in total_period. | ||
| 37 | */ | ||
| 38 | struct events_stats { | ||
| 39 | u64 total_period; | ||
| 40 | u64 total_non_filtered_period; | ||
| 41 | u64 total_lost; | ||
| 42 | u64 total_invalid_chains; | ||
| 43 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | ||
| 44 | u32 nr_non_filtered_samples; | ||
| 45 | u32 nr_lost_warned; | ||
| 46 | u32 nr_unknown_events; | ||
| 47 | u32 nr_invalid_chains; | ||
| 48 | u32 nr_unknown_id; | ||
| 49 | u32 nr_unprocessable_samples; | ||
| 50 | }; | ||
| 51 | |||
| 52 | enum hist_column { | 25 | enum hist_column { |
| 53 | HISTC_SYMBOL, | 26 | HISTC_SYMBOL, |
| 54 | HISTC_DSO, | 27 | HISTC_DSO, |
| @@ -152,6 +125,7 @@ void hists__output_resort(struct hists *hists); | |||
| 152 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); | 125 | void hists__collapse_resort(struct hists *hists, struct ui_progress *prog); |
| 153 | 126 | ||
| 154 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | 127 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); |
| 128 | void hists__delete_entries(struct hists *hists); | ||
| 155 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | 129 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); |
| 156 | 130 | ||
| 157 | u64 hists__total_period(struct hists *hists); | 131 | u64 hists__total_period(struct hists *hists); |
| @@ -164,6 +138,7 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); | |||
| 164 | 138 | ||
| 165 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | 139 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, |
| 166 | int max_cols, float min_pcnt, FILE *fp); | 140 | int max_cols, float min_pcnt, FILE *fp); |
| 141 | size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp); | ||
| 167 | 142 | ||
| 168 | void hists__filter_by_dso(struct hists *hists); | 143 | void hists__filter_by_dso(struct hists *hists); |
| 169 | void hists__filter_by_thread(struct hists *hists); | 144 | void hists__filter_by_thread(struct hists *hists); |
| @@ -184,6 +159,25 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *he); | |||
| 184 | void hists__match(struct hists *leader, struct hists *other); | 159 | void hists__match(struct hists *leader, struct hists *other); |
| 185 | int hists__link(struct hists *leader, struct hists *other); | 160 | int hists__link(struct hists *leader, struct hists *other); |
| 186 | 161 | ||
| 162 | struct hists_evsel { | ||
| 163 | struct perf_evsel evsel; | ||
| 164 | struct hists hists; | ||
| 165 | }; | ||
| 166 | |||
| 167 | static inline struct perf_evsel *hists_to_evsel(struct hists *hists) | ||
| 168 | { | ||
| 169 | struct hists_evsel *hevsel = container_of(hists, struct hists_evsel, hists); | ||
| 170 | return &hevsel->evsel; | ||
| 171 | } | ||
| 172 | |||
| 173 | static inline struct hists *evsel__hists(struct perf_evsel *evsel) | ||
| 174 | { | ||
| 175 | struct hists_evsel *hevsel = (struct hists_evsel *)evsel; | ||
| 176 | return &hevsel->hists; | ||
| 177 | } | ||
| 178 | |||
| 179 | int hists__init(void); | ||
| 180 | |||
| 187 | struct perf_hpp { | 181 | struct perf_hpp { |
| 188 | char *buf; | 182 | char *buf; |
| 189 | size_t size; | 183 | size_t size; |
| @@ -192,6 +186,7 @@ struct perf_hpp { | |||
| 192 | }; | 186 | }; |
| 193 | 187 | ||
| 194 | struct perf_hpp_fmt { | 188 | struct perf_hpp_fmt { |
| 189 | const char *name; | ||
| 195 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 190 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 196 | struct perf_evsel *evsel); | 191 | struct perf_evsel *evsel); |
| 197 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 192 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| @@ -207,6 +202,8 @@ struct perf_hpp_fmt { | |||
| 207 | struct list_head list; | 202 | struct list_head list; |
| 208 | struct list_head sort_list; | 203 | struct list_head sort_list; |
| 209 | bool elide; | 204 | bool elide; |
| 205 | int len; | ||
| 206 | int user_len; | ||
| 210 | }; | 207 | }; |
| 211 | 208 | ||
| 212 | extern struct list_head perf_hpp__list; | 209 | extern struct list_head perf_hpp__list; |
| @@ -261,17 +258,19 @@ static inline bool perf_hpp__should_skip(struct perf_hpp_fmt *format) | |||
| 261 | } | 258 | } |
| 262 | 259 | ||
| 263 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); | 260 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists); |
| 261 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists); | ||
| 262 | void perf_hpp__set_user_width(const char *width_list_str); | ||
| 264 | 263 | ||
| 265 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); | 264 | typedef u64 (*hpp_field_fn)(struct hist_entry *he); |
| 266 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); | 265 | typedef int (*hpp_callback_fn)(struct perf_hpp *hpp, bool front); |
| 267 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); | 266 | typedef int (*hpp_snprint_fn)(struct perf_hpp *hpp, const char *fmt, ...); |
| 268 | 267 | ||
| 269 | int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | 268 | int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 270 | hpp_field_fn get_field, const char *fmt, | 269 | struct hist_entry *he, hpp_field_fn get_field, |
| 271 | hpp_snprint_fn print_fn, bool fmt_percent); | 270 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent); |
| 272 | int __hpp__fmt_acc(struct perf_hpp *hpp, struct hist_entry *he, | 271 | int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 273 | hpp_field_fn get_field, const char *fmt, | 272 | struct hist_entry *he, hpp_field_fn get_field, |
| 274 | hpp_snprint_fn print_fn, bool fmt_percent); | 273 | const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent); |
| 275 | 274 | ||
| 276 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | 275 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) |
| 277 | { | 276 | { |
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h index 97a800738226..6f19c548ecc0 100644 --- a/tools/perf/util/include/linux/string.h +++ b/tools/perf/util/include/linux/string.h | |||
| @@ -1,4 +1,3 @@ | |||
| 1 | #include <string.h> | 1 | #include <string.h> |
| 2 | 2 | ||
| 3 | void *memdup(const void *src, size_t len); | 3 | void *memdup(const void *src, size_t len); |
| 4 | int str_append(char **s, int *len, const char *a); | ||
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..34fc7c8672e4 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -13,12 +13,18 @@ | |||
| 13 | #include <symbol/kallsyms.h> | 13 | #include <symbol/kallsyms.h> |
| 14 | #include "unwind.h" | 14 | #include "unwind.h" |
| 15 | 15 | ||
| 16 | static void dsos__init(struct dsos *dsos) | ||
| 17 | { | ||
| 18 | INIT_LIST_HEAD(&dsos->head); | ||
| 19 | dsos->root = RB_ROOT; | ||
| 20 | } | ||
| 21 | |||
| 16 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | 22 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) |
| 17 | { | 23 | { |
| 18 | map_groups__init(&machine->kmaps); | 24 | map_groups__init(&machine->kmaps); |
| 19 | RB_CLEAR_NODE(&machine->rb_node); | 25 | RB_CLEAR_NODE(&machine->rb_node); |
| 20 | INIT_LIST_HEAD(&machine->user_dsos); | 26 | dsos__init(&machine->user_dsos); |
| 21 | INIT_LIST_HEAD(&machine->kernel_dsos); | 27 | dsos__init(&machine->kernel_dsos); |
| 22 | 28 | ||
| 23 | machine->threads = RB_ROOT; | 29 | machine->threads = RB_ROOT; |
| 24 | INIT_LIST_HEAD(&machine->dead_threads); | 30 | INIT_LIST_HEAD(&machine->dead_threads); |
| @@ -31,6 +37,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
| 31 | 37 | ||
| 32 | machine->symbol_filter = NULL; | 38 | machine->symbol_filter = NULL; |
| 33 | machine->id_hdr_size = 0; | 39 | machine->id_hdr_size = 0; |
| 40 | machine->comm_exec = false; | ||
| 41 | machine->kernel_start = 0; | ||
| 34 | 42 | ||
| 35 | machine->root_dir = strdup(root_dir); | 43 | machine->root_dir = strdup(root_dir); |
| 36 | if (machine->root_dir == NULL) | 44 | if (machine->root_dir == NULL) |
| @@ -70,11 +78,12 @@ out_delete: | |||
| 70 | return NULL; | 78 | return NULL; |
| 71 | } | 79 | } |
| 72 | 80 | ||
| 73 | static void dsos__delete(struct list_head *dsos) | 81 | static void dsos__delete(struct dsos *dsos) |
| 74 | { | 82 | { |
| 75 | struct dso *pos, *n; | 83 | struct dso *pos, *n; |
| 76 | 84 | ||
| 77 | list_for_each_entry_safe(pos, n, dsos, node) { | 85 | list_for_each_entry_safe(pos, n, &dsos->head, node) { |
| 86 | RB_CLEAR_NODE(&pos->rb_node); | ||
| 78 | list_del(&pos->node); | 87 | list_del(&pos->node); |
| 79 | dso__delete(pos); | 88 | dso__delete(pos); |
| 80 | } | 89 | } |
| @@ -179,6 +188,19 @@ void machines__set_symbol_filter(struct machines *machines, | |||
| 179 | } | 188 | } |
| 180 | } | 189 | } |
| 181 | 190 | ||
| 191 | void machines__set_comm_exec(struct machines *machines, bool comm_exec) | ||
| 192 | { | ||
| 193 | struct rb_node *nd; | ||
| 194 | |||
| 195 | machines->host.comm_exec = comm_exec; | ||
| 196 | |||
| 197 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
| 198 | struct machine *machine = rb_entry(nd, struct machine, rb_node); | ||
| 199 | |||
| 200 | machine->comm_exec = comm_exec; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 182 | struct machine *machines__find(struct machines *machines, pid_t pid) | 204 | struct machine *machines__find(struct machines *machines, pid_t pid) |
| 183 | { | 205 | { |
| 184 | struct rb_node **p = &machines->guests.rb_node; | 206 | struct rb_node **p = &machines->guests.rb_node; |
| @@ -398,17 +420,31 @@ struct thread *machine__find_thread(struct machine *machine, pid_t pid, | |||
| 398 | return __machine__findnew_thread(machine, pid, tid, false); | 420 | return __machine__findnew_thread(machine, pid, tid, false); |
| 399 | } | 421 | } |
| 400 | 422 | ||
| 423 | struct comm *machine__thread_exec_comm(struct machine *machine, | ||
| 424 | struct thread *thread) | ||
| 425 | { | ||
| 426 | if (machine->comm_exec) | ||
| 427 | return thread__exec_comm(thread); | ||
| 428 | else | ||
| 429 | return thread__comm(thread); | ||
| 430 | } | ||
| 431 | |||
| 401 | int machine__process_comm_event(struct machine *machine, union perf_event *event, | 432 | int machine__process_comm_event(struct machine *machine, union perf_event *event, |
| 402 | struct perf_sample *sample) | 433 | struct perf_sample *sample) |
| 403 | { | 434 | { |
| 404 | struct thread *thread = machine__findnew_thread(machine, | 435 | struct thread *thread = machine__findnew_thread(machine, |
| 405 | event->comm.pid, | 436 | event->comm.pid, |
| 406 | event->comm.tid); | 437 | event->comm.tid); |
| 438 | bool exec = event->header.misc & PERF_RECORD_MISC_COMM_EXEC; | ||
| 439 | |||
| 440 | if (exec) | ||
| 441 | machine->comm_exec = true; | ||
| 407 | 442 | ||
| 408 | if (dump_trace) | 443 | if (dump_trace) |
| 409 | perf_event__fprintf_comm(event, stdout); | 444 | perf_event__fprintf_comm(event, stdout); |
| 410 | 445 | ||
| 411 | if (thread == NULL || thread__set_comm(thread, event->comm.comm, sample->time)) { | 446 | if (thread == NULL || |
| 447 | __thread__set_comm(thread, event->comm.comm, sample->time, exec)) { | ||
| 412 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 448 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
| 413 | return -1; | 449 | return -1; |
| 414 | } | 450 | } |
| @@ -448,23 +484,23 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
| 448 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) | 484 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) |
| 449 | { | 485 | { |
| 450 | struct rb_node *nd; | 486 | struct rb_node *nd; |
| 451 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos, fp) + | 487 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos.head, fp) + |
| 452 | __dsos__fprintf(&machines->host.user_dsos, fp); | 488 | __dsos__fprintf(&machines->host.user_dsos.head, fp); |
| 453 | 489 | ||
| 454 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | 490 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { |
| 455 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 491 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
| 456 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | 492 | ret += __dsos__fprintf(&pos->kernel_dsos.head, fp); |
| 457 | ret += __dsos__fprintf(&pos->user_dsos, fp); | 493 | ret += __dsos__fprintf(&pos->user_dsos.head, fp); |
| 458 | } | 494 | } |
| 459 | 495 | ||
| 460 | return ret; | 496 | return ret; |
| 461 | } | 497 | } |
| 462 | 498 | ||
| 463 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | 499 | size_t machine__fprintf_dsos_buildid(struct machine *m, FILE *fp, |
| 464 | bool (skip)(struct dso *dso, int parm), int parm) | 500 | bool (skip)(struct dso *dso, int parm), int parm) |
| 465 | { | 501 | { |
| 466 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) + | 502 | return __dsos__fprintf_buildid(&m->kernel_dsos.head, fp, skip, parm) + |
| 467 | __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); | 503 | __dsos__fprintf_buildid(&m->user_dsos.head, fp, skip, parm); |
| 468 | } | 504 | } |
| 469 | 505 | ||
| 470 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, | 506 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, |
| @@ -565,8 +601,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 | 601 | * Returns the name of the start symbol in *symbol_name. Pass in NULL as |
| 566 | * symbol_name if it's not that important. | 602 | * symbol_name if it's not that important. |
| 567 | */ | 603 | */ |
| 568 | static u64 machine__get_kernel_start_addr(struct machine *machine, | 604 | static u64 machine__get_running_kernel_start(struct machine *machine, |
| 569 | const char **symbol_name) | 605 | const char **symbol_name) |
| 570 | { | 606 | { |
| 571 | char filename[PATH_MAX]; | 607 | char filename[PATH_MAX]; |
| 572 | int i; | 608 | int i; |
| @@ -593,7 +629,7 @@ static u64 machine__get_kernel_start_addr(struct machine *machine, | |||
| 593 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | 629 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) |
| 594 | { | 630 | { |
| 595 | enum map_type type; | 631 | enum map_type type; |
| 596 | u64 start = machine__get_kernel_start_addr(machine, NULL); | 632 | u64 start = machine__get_running_kernel_start(machine, NULL); |
| 597 | 633 | ||
| 598 | for (type = 0; type < MAP__NR_TYPES; ++type) { | 634 | for (type = 0; type < MAP__NR_TYPES; ++type) { |
| 599 | struct kmap *kmap; | 635 | struct kmap *kmap; |
| @@ -912,7 +948,7 @@ int machine__create_kernel_maps(struct machine *machine) | |||
| 912 | { | 948 | { |
| 913 | struct dso *kernel = machine__get_kernel(machine); | 949 | struct dso *kernel = machine__get_kernel(machine); |
| 914 | const char *name; | 950 | const char *name; |
| 915 | u64 addr = machine__get_kernel_start_addr(machine, &name); | 951 | u64 addr = machine__get_running_kernel_start(machine, &name); |
| 916 | if (!addr) | 952 | if (!addr) |
| 917 | return -1; | 953 | return -1; |
| 918 | 954 | ||
| @@ -965,7 +1001,7 @@ static bool machine__uses_kcore(struct machine *machine) | |||
| 965 | { | 1001 | { |
| 966 | struct dso *dso; | 1002 | struct dso *dso; |
| 967 | 1003 | ||
| 968 | list_for_each_entry(dso, &machine->kernel_dsos, node) { | 1004 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { |
| 969 | if (dso__is_kcore(dso)) | 1005 | if (dso__is_kcore(dso)) |
| 970 | return true; | 1006 | return true; |
| 971 | } | 1007 | } |
| @@ -1285,6 +1321,16 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread, | |||
| 1285 | 1321 | ||
| 1286 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, | 1322 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, |
| 1287 | &al); | 1323 | &al); |
| 1324 | if (al.map == NULL) { | ||
| 1325 | /* | ||
| 1326 | * some shared data regions have execute bit set which puts | ||
| 1327 | * their mapping in the MAP__FUNCTION type array. | ||
| 1328 | * Check there as a fallback option before dropping the sample. | ||
| 1329 | */ | ||
| 1330 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr, | ||
| 1331 | &al); | ||
| 1332 | } | ||
| 1333 | |||
| 1288 | ams->addr = addr; | 1334 | ams->addr = addr; |
| 1289 | ams->al_addr = al.addr; | 1335 | ams->al_addr = al.addr; |
| 1290 | ams->sym = al.sym; | 1336 | ams->sym = al.sym; |
| @@ -1531,3 +1577,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid, | |||
| 1531 | 1577 | ||
| 1532 | return 0; | 1578 | return 0; |
| 1533 | } | 1579 | } |
| 1580 | |||
| 1581 | int machine__get_kernel_start(struct machine *machine) | ||
| 1582 | { | ||
| 1583 | struct map *map = machine__kernel_map(machine, MAP__FUNCTION); | ||
| 1584 | int err = 0; | ||
| 1585 | |||
| 1586 | /* | ||
| 1587 | * The only addresses above 2^63 are kernel addresses of a 64-bit | ||
| 1588 | * kernel. Note that addresses are unsigned so that on a 32-bit system | ||
| 1589 | * all addresses including kernel addresses are less than 2^32. In | ||
| 1590 | * that case (32-bit system), if the kernel mapping is unknown, all | ||
| 1591 | * addresses will be assumed to be in user space - see | ||
| 1592 | * machine__kernel_ip(). | ||
| 1593 | */ | ||
| 1594 | machine->kernel_start = 1ULL << 63; | ||
| 1595 | if (map) { | ||
| 1596 | err = map__load(map, machine->symbol_filter); | ||
| 1597 | if (map->start) | ||
| 1598 | machine->kernel_start = map->start; | ||
| 1599 | } | ||
| 1600 | return err; | ||
| 1601 | } | ||
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..2137c4596ec7 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 | ||
| @@ -555,7 +556,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, | |||
| 555 | 556 | ||
| 556 | int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) | 557 | int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) |
| 557 | { | 558 | { |
| 558 | if (ams->addr < ams->map->start || ams->addr > ams->map->end) { | 559 | if (ams->addr < ams->map->start || ams->addr >= ams->map->end) { |
| 559 | if (ams->map->groups == NULL) | 560 | if (ams->map->groups == NULL) |
| 560 | return -1; | 561 | return -1; |
| 561 | ams->map = map_groups__find(ams->map->groups, ams->map->type, | 562 | ams->map = map_groups__find(ams->map->groups, ams->map->type, |
| @@ -663,7 +664,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, | |||
| 663 | goto move_map; | 664 | goto move_map; |
| 664 | } | 665 | } |
| 665 | 666 | ||
| 666 | before->end = map->start - 1; | 667 | before->end = map->start; |
| 667 | map_groups__insert(mg, before); | 668 | map_groups__insert(mg, before); |
| 668 | if (verbose >= 2) | 669 | if (verbose >= 2) |
| 669 | map__fprintf(before, fp); | 670 | map__fprintf(before, fp); |
| @@ -677,7 +678,7 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, | |||
| 677 | goto move_map; | 678 | goto move_map; |
| 678 | } | 679 | } |
| 679 | 680 | ||
| 680 | after->start = map->end + 1; | 681 | after->start = map->end; |
| 681 | map_groups__insert(mg, after); | 682 | map_groups__insert(mg, after); |
| 682 | if (verbose >= 2) | 683 | if (verbose >= 2) |
| 683 | map__fprintf(after, fp); | 684 | map__fprintf(after, fp); |
| @@ -751,7 +752,7 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
| 751 | m = rb_entry(parent, struct map, rb_node); | 752 | m = rb_entry(parent, struct map, rb_node); |
| 752 | if (ip < m->start) | 753 | if (ip < m->start) |
| 753 | p = &(*p)->rb_left; | 754 | p = &(*p)->rb_left; |
| 754 | else if (ip > m->end) | 755 | else if (ip >= m->end) |
| 755 | p = &(*p)->rb_right; | 756 | p = &(*p)->rb_right; |
| 756 | else | 757 | else |
| 757 | return m; | 758 | return m; |
diff --git a/tools/perf/util/ordered-events.c b/tools/perf/util/ordered-events.c new file mode 100644 index 000000000000..fd4be94125fb --- /dev/null +++ b/tools/perf/util/ordered-events.c | |||
| @@ -0,0 +1,286 @@ | |||
| 1 | #include <linux/list.h> | ||
| 2 | #include <linux/compiler.h> | ||
| 3 | #include <linux/string.h> | ||
| 4 | #include "ordered-events.h" | ||
| 5 | #include "evlist.h" | ||
| 6 | #include "session.h" | ||
| 7 | #include "asm/bug.h" | ||
| 8 | #include "debug.h" | ||
| 9 | |||
| 10 | #define pr_N(n, fmt, ...) \ | ||
| 11 | eprintf(n, debug_ordered_events, fmt, ##__VA_ARGS__) | ||
| 12 | |||
| 13 | #define pr(fmt, ...) pr_N(1, pr_fmt(fmt), ##__VA_ARGS__) | ||
| 14 | |||
| 15 | static void queue_event(struct ordered_events *oe, struct ordered_event *new) | ||
| 16 | { | ||
| 17 | struct ordered_event *last = oe->last; | ||
| 18 | u64 timestamp = new->timestamp; | ||
| 19 | struct list_head *p; | ||
| 20 | |||
| 21 | ++oe->nr_events; | ||
| 22 | oe->last = new; | ||
| 23 | |||
| 24 | pr_oe_time2(timestamp, "queue_event nr_events %u\n", oe->nr_events); | ||
| 25 | |||
| 26 | if (!last) { | ||
| 27 | list_add(&new->list, &oe->events); | ||
| 28 | oe->max_timestamp = timestamp; | ||
| 29 | return; | ||
| 30 | } | ||
| 31 | |||
| 32 | /* | ||
| 33 | * last event might point to some random place in the list as it's | ||
| 34 | * the last queued event. We expect that the new event is close to | ||
| 35 | * this. | ||
| 36 | */ | ||
| 37 | if (last->timestamp <= timestamp) { | ||
| 38 | while (last->timestamp <= timestamp) { | ||
| 39 | p = last->list.next; | ||
| 40 | if (p == &oe->events) { | ||
| 41 | list_add_tail(&new->list, &oe->events); | ||
| 42 | oe->max_timestamp = timestamp; | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | last = list_entry(p, struct ordered_event, list); | ||
| 46 | } | ||
| 47 | list_add_tail(&new->list, &last->list); | ||
| 48 | } else { | ||
| 49 | while (last->timestamp > timestamp) { | ||
| 50 | p = last->list.prev; | ||
| 51 | if (p == &oe->events) { | ||
| 52 | list_add(&new->list, &oe->events); | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | last = list_entry(p, struct ordered_event, list); | ||
| 56 | } | ||
| 57 | list_add(&new->list, &last->list); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | static union perf_event *__dup_event(struct ordered_events *oe, | ||
| 62 | union perf_event *event) | ||
| 63 | { | ||
| 64 | union perf_event *new_event = NULL; | ||
| 65 | |||
| 66 | if (oe->cur_alloc_size < oe->max_alloc_size) { | ||
| 67 | new_event = memdup(event, event->header.size); | ||
| 68 | if (new_event) | ||
| 69 | oe->cur_alloc_size += event->header.size; | ||
| 70 | } | ||
| 71 | |||
| 72 | return new_event; | ||
| 73 | } | ||
| 74 | |||
| 75 | static union perf_event *dup_event(struct ordered_events *oe, | ||
| 76 | union perf_event *event) | ||
| 77 | { | ||
| 78 | return oe->copy_on_queue ? __dup_event(oe, event) : event; | ||
| 79 | } | ||
| 80 | |||
| 81 | static void free_dup_event(struct ordered_events *oe, union perf_event *event) | ||
| 82 | { | ||
| 83 | if (oe->copy_on_queue) { | ||
| 84 | oe->cur_alloc_size -= event->header.size; | ||
| 85 | free(event); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event)) | ||
| 90 | static struct ordered_event *alloc_event(struct ordered_events *oe, | ||
| 91 | union perf_event *event) | ||
| 92 | { | ||
| 93 | struct list_head *cache = &oe->cache; | ||
| 94 | struct ordered_event *new = NULL; | ||
| 95 | union perf_event *new_event; | ||
| 96 | |||
| 97 | new_event = dup_event(oe, event); | ||
| 98 | if (!new_event) | ||
| 99 | return NULL; | ||
| 100 | |||
| 101 | if (!list_empty(cache)) { | ||
| 102 | new = list_entry(cache->next, struct ordered_event, list); | ||
| 103 | list_del(&new->list); | ||
| 104 | } else if (oe->buffer) { | ||
| 105 | new = oe->buffer + oe->buffer_idx; | ||
| 106 | if (++oe->buffer_idx == MAX_SAMPLE_BUFFER) | ||
| 107 | oe->buffer = NULL; | ||
| 108 | } else if (oe->cur_alloc_size < oe->max_alloc_size) { | ||
| 109 | size_t size = MAX_SAMPLE_BUFFER * sizeof(*new); | ||
| 110 | |||
| 111 | oe->buffer = malloc(size); | ||
| 112 | if (!oe->buffer) { | ||
| 113 | free_dup_event(oe, new_event); | ||
| 114 | return NULL; | ||
| 115 | } | ||
| 116 | |||
| 117 | pr("alloc size %" PRIu64 "B (+%zu), max %" PRIu64 "B\n", | ||
| 118 | oe->cur_alloc_size, size, oe->max_alloc_size); | ||
| 119 | |||
| 120 | oe->cur_alloc_size += size; | ||
| 121 | list_add(&oe->buffer->list, &oe->to_free); | ||
| 122 | |||
| 123 | /* First entry is abused to maintain the to_free list. */ | ||
| 124 | oe->buffer_idx = 2; | ||
| 125 | new = oe->buffer + 1; | ||
| 126 | } else { | ||
| 127 | pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size); | ||
| 128 | } | ||
| 129 | |||
| 130 | new->event = new_event; | ||
| 131 | return new; | ||
| 132 | } | ||
| 133 | |||
| 134 | struct ordered_event * | ||
| 135 | ordered_events__new(struct ordered_events *oe, u64 timestamp, | ||
| 136 | union perf_event *event) | ||
| 137 | { | ||
| 138 | struct ordered_event *new; | ||
| 139 | |||
| 140 | new = alloc_event(oe, event); | ||
| 141 | if (new) { | ||
| 142 | new->timestamp = timestamp; | ||
| 143 | queue_event(oe, new); | ||
| 144 | } | ||
| 145 | |||
| 146 | return new; | ||
| 147 | } | ||
| 148 | |||
| 149 | void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event) | ||
| 150 | { | ||
| 151 | list_move(&event->list, &oe->cache); | ||
| 152 | oe->nr_events--; | ||
| 153 | free_dup_event(oe, event->event); | ||
| 154 | } | ||
| 155 | |||
| 156 | static int __ordered_events__flush(struct perf_session *s, | ||
| 157 | struct perf_tool *tool) | ||
| 158 | { | ||
| 159 | struct ordered_events *oe = &s->ordered_events; | ||
| 160 | struct list_head *head = &oe->events; | ||
| 161 | struct ordered_event *tmp, *iter; | ||
| 162 | struct perf_sample sample; | ||
| 163 | u64 limit = oe->next_flush; | ||
| 164 | u64 last_ts = oe->last ? oe->last->timestamp : 0ULL; | ||
| 165 | bool show_progress = limit == ULLONG_MAX; | ||
| 166 | struct ui_progress prog; | ||
| 167 | int ret; | ||
| 168 | |||
| 169 | if (!tool->ordered_events || !limit) | ||
| 170 | return 0; | ||
| 171 | |||
| 172 | if (show_progress) | ||
| 173 | ui_progress__init(&prog, oe->nr_events, "Processing time ordered events..."); | ||
| 174 | |||
| 175 | list_for_each_entry_safe(iter, tmp, head, list) { | ||
| 176 | if (session_done()) | ||
| 177 | return 0; | ||
| 178 | |||
| 179 | if (iter->timestamp > limit) | ||
| 180 | break; | ||
| 181 | |||
| 182 | ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample); | ||
| 183 | if (ret) | ||
| 184 | pr_err("Can't parse sample, err = %d\n", ret); | ||
| 185 | else { | ||
| 186 | ret = perf_session__deliver_event(s, iter->event, &sample, tool, | ||
| 187 | iter->file_offset); | ||
| 188 | if (ret) | ||
| 189 | return ret; | ||
| 190 | } | ||
| 191 | |||
| 192 | ordered_events__delete(oe, iter); | ||
| 193 | oe->last_flush = iter->timestamp; | ||
| 194 | |||
| 195 | if (show_progress) | ||
| 196 | ui_progress__update(&prog, 1); | ||
| 197 | } | ||
| 198 | |||
| 199 | if (list_empty(head)) | ||
| 200 | oe->last = NULL; | ||
| 201 | else if (last_ts <= limit) | ||
| 202 | oe->last = list_entry(head->prev, struct ordered_event, list); | ||
| 203 | |||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | |||
| 207 | int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, | ||
| 208 | enum oe_flush how) | ||
| 209 | { | ||
| 210 | struct ordered_events *oe = &s->ordered_events; | ||
| 211 | static const char * const str[] = { | ||
| 212 | "NONE", | ||
| 213 | "FINAL", | ||
| 214 | "ROUND", | ||
| 215 | "HALF ", | ||
| 216 | }; | ||
| 217 | int err; | ||
| 218 | |||
| 219 | switch (how) { | ||
| 220 | case OE_FLUSH__FINAL: | ||
| 221 | oe->next_flush = ULLONG_MAX; | ||
| 222 | break; | ||
| 223 | |||
| 224 | case OE_FLUSH__HALF: | ||
| 225 | { | ||
| 226 | struct ordered_event *first, *last; | ||
| 227 | struct list_head *head = &oe->events; | ||
| 228 | |||
| 229 | first = list_entry(head->next, struct ordered_event, list); | ||
| 230 | last = oe->last; | ||
| 231 | |||
| 232 | /* Warn if we are called before any event got allocated. */ | ||
| 233 | if (WARN_ONCE(!last || list_empty(head), "empty queue")) | ||
| 234 | return 0; | ||
| 235 | |||
| 236 | oe->next_flush = first->timestamp; | ||
| 237 | oe->next_flush += (last->timestamp - first->timestamp) / 2; | ||
| 238 | break; | ||
| 239 | } | ||
| 240 | |||
| 241 | case OE_FLUSH__ROUND: | ||
| 242 | case OE_FLUSH__NONE: | ||
| 243 | default: | ||
| 244 | break; | ||
| 245 | }; | ||
| 246 | |||
| 247 | pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush PRE %s, nr_events %u\n", | ||
| 248 | str[how], oe->nr_events); | ||
| 249 | pr_oe_time(oe->max_timestamp, "max_timestamp\n"); | ||
| 250 | |||
| 251 | err = __ordered_events__flush(s, tool); | ||
| 252 | |||
| 253 | if (!err) { | ||
| 254 | if (how == OE_FLUSH__ROUND) | ||
| 255 | oe->next_flush = oe->max_timestamp; | ||
| 256 | |||
| 257 | oe->last_flush_type = how; | ||
| 258 | } | ||
| 259 | |||
| 260 | pr_oe_time(oe->next_flush, "next_flush - ordered_events__flush POST %s, nr_events %u\n", | ||
| 261 | str[how], oe->nr_events); | ||
| 262 | pr_oe_time(oe->last_flush, "last_flush\n"); | ||
| 263 | |||
| 264 | return err; | ||
| 265 | } | ||
| 266 | |||
| 267 | void ordered_events__init(struct ordered_events *oe) | ||
| 268 | { | ||
| 269 | INIT_LIST_HEAD(&oe->events); | ||
| 270 | INIT_LIST_HEAD(&oe->cache); | ||
| 271 | INIT_LIST_HEAD(&oe->to_free); | ||
| 272 | oe->max_alloc_size = (u64) -1; | ||
| 273 | oe->cur_alloc_size = 0; | ||
| 274 | } | ||
| 275 | |||
| 276 | void ordered_events__free(struct ordered_events *oe) | ||
| 277 | { | ||
| 278 | while (!list_empty(&oe->to_free)) { | ||
| 279 | struct ordered_event *event; | ||
| 280 | |||
| 281 | event = list_entry(oe->to_free.next, struct ordered_event, list); | ||
| 282 | list_del(&event->list); | ||
| 283 | free_dup_event(oe, event->event); | ||
| 284 | free(event); | ||
| 285 | } | ||
| 286 | } | ||
diff --git a/tools/perf/util/ordered-events.h b/tools/perf/util/ordered-events.h new file mode 100644 index 000000000000..7b8f9b011f38 --- /dev/null +++ b/tools/perf/util/ordered-events.h | |||
| @@ -0,0 +1,59 @@ | |||
| 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 | bool copy_on_queue; | ||
| 38 | }; | ||
| 39 | |||
| 40 | struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp, | ||
| 41 | union perf_event *event); | ||
| 42 | void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event); | ||
| 43 | int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, | ||
| 44 | enum oe_flush how); | ||
| 45 | void ordered_events__init(struct ordered_events *oe); | ||
| 46 | void ordered_events__free(struct ordered_events *oe); | ||
| 47 | |||
| 48 | static inline | ||
| 49 | void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size) | ||
| 50 | { | ||
| 51 | oe->max_alloc_size = size; | ||
| 52 | } | ||
| 53 | |||
| 54 | static inline | ||
| 55 | void ordered_events__set_copy_on_queue(struct ordered_events *oe, bool copy) | ||
| 56 | { | ||
| 57 | oe->copy_on_queue = copy; | ||
| 58 | } | ||
| 59 | #endif /* __ORDERED_EVENTS_H */ | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 1e15df10a88c..c659a3ca1283 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -6,10 +6,11 @@ | |||
| 6 | #include "parse-options.h" | 6 | #include "parse-options.h" |
| 7 | #include "parse-events.h" | 7 | #include "parse-events.h" |
| 8 | #include "exec_cmd.h" | 8 | #include "exec_cmd.h" |
| 9 | #include "linux/string.h" | 9 | #include "string.h" |
| 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 |
| @@ -29,6 +30,15 @@ extern int parse_events_debug; | |||
| 29 | #endif | 30 | #endif |
| 30 | int parse_events_parse(void *data, void *scanner); | 31 | int parse_events_parse(void *data, void *scanner); |
| 31 | 32 | ||
| 33 | static struct perf_pmu_event_symbol *perf_pmu_events_list; | ||
| 34 | /* | ||
| 35 | * The variable indicates the number of supported pmu event symbols. | ||
| 36 | * 0 means not initialized and ready to init | ||
| 37 | * -1 means failed to init, don't try anymore | ||
| 38 | * >0 is the number of supported pmu event symbols | ||
| 39 | */ | ||
| 40 | static int perf_pmu_events_list_num; | ||
| 41 | |||
| 32 | static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { | 42 | static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { |
| 33 | [PERF_COUNT_HW_CPU_CYCLES] = { | 43 | [PERF_COUNT_HW_CPU_CYCLES] = { |
| 34 | .symbol = "cpu-cycles", | 44 | .symbol = "cpu-cycles", |
| @@ -633,18 +643,28 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
| 633 | char *name, struct list_head *head_config) | 643 | char *name, struct list_head *head_config) |
| 634 | { | 644 | { |
| 635 | struct perf_event_attr attr; | 645 | struct perf_event_attr attr; |
| 646 | struct perf_pmu_info info; | ||
| 636 | struct perf_pmu *pmu; | 647 | struct perf_pmu *pmu; |
| 637 | struct perf_evsel *evsel; | 648 | struct perf_evsel *evsel; |
| 638 | const char *unit; | ||
| 639 | double scale; | ||
| 640 | 649 | ||
| 641 | pmu = perf_pmu__find(name); | 650 | pmu = perf_pmu__find(name); |
| 642 | if (!pmu) | 651 | if (!pmu) |
| 643 | return -EINVAL; | 652 | return -EINVAL; |
| 644 | 653 | ||
| 645 | memset(&attr, 0, sizeof(attr)); | 654 | if (pmu->default_config) { |
| 655 | memcpy(&attr, pmu->default_config, | ||
| 656 | sizeof(struct perf_event_attr)); | ||
| 657 | } else { | ||
| 658 | memset(&attr, 0, sizeof(attr)); | ||
| 659 | } | ||
| 646 | 660 | ||
| 647 | if (perf_pmu__check_alias(pmu, head_config, &unit, &scale)) | 661 | if (!head_config) { |
| 662 | attr.type = pmu->type; | ||
| 663 | evsel = __add_event(list, idx, &attr, NULL, pmu->cpus); | ||
| 664 | return evsel ? 0 : -ENOMEM; | ||
| 665 | } | ||
| 666 | |||
| 667 | if (perf_pmu__check_alias(pmu, head_config, &info)) | ||
| 648 | return -EINVAL; | 668 | return -EINVAL; |
| 649 | 669 | ||
| 650 | /* | 670 | /* |
| @@ -659,8 +679,8 @@ int parse_events_add_pmu(struct list_head *list, int *idx, | |||
| 659 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), | 679 | evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), |
| 660 | pmu->cpus); | 680 | pmu->cpus); |
| 661 | if (evsel) { | 681 | if (evsel) { |
| 662 | evsel->unit = unit; | 682 | evsel->unit = info.unit; |
| 663 | evsel->scale = scale; | 683 | evsel->scale = info.scale; |
| 664 | } | 684 | } |
| 665 | 685 | ||
| 666 | return evsel ? 0 : -ENOMEM; | 686 | return evsel ? 0 : -ENOMEM; |
| @@ -852,30 +872,111 @@ int parse_events_name(struct list_head *list, char *name) | |||
| 852 | return 0; | 872 | return 0; |
| 853 | } | 873 | } |
| 854 | 874 | ||
| 855 | static int parse_events__scanner(const char *str, void *data, int start_token); | 875 | static int |
| 876 | comp_pmu(const void *p1, const void *p2) | ||
| 877 | { | ||
| 878 | struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; | ||
| 879 | struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; | ||
| 856 | 880 | ||
| 857 | static int parse_events_fixup(int ret, const char *str, void *data, | 881 | return strcmp(pmu1->symbol, pmu2->symbol); |
| 858 | int start_token) | 882 | } |
| 883 | |||
| 884 | static void perf_pmu__parse_cleanup(void) | ||
| 859 | { | 885 | { |
| 860 | char *o = strdup(str); | 886 | if (perf_pmu_events_list_num > 0) { |
| 861 | char *s = NULL; | 887 | struct perf_pmu_event_symbol *p; |
| 862 | char *t = o; | 888 | int i; |
| 863 | char *p; | 889 | |
| 890 | for (i = 0; i < perf_pmu_events_list_num; i++) { | ||
| 891 | p = perf_pmu_events_list + i; | ||
| 892 | free(p->symbol); | ||
| 893 | } | ||
| 894 | free(perf_pmu_events_list); | ||
| 895 | perf_pmu_events_list = NULL; | ||
| 896 | perf_pmu_events_list_num = 0; | ||
| 897 | } | ||
| 898 | } | ||
| 899 | |||
| 900 | #define SET_SYMBOL(str, stype) \ | ||
| 901 | do { \ | ||
| 902 | p->symbol = str; \ | ||
| 903 | if (!p->symbol) \ | ||
| 904 | goto err; \ | ||
| 905 | p->type = stype; \ | ||
| 906 | } while (0) | ||
| 907 | |||
| 908 | /* | ||
| 909 | * Read the pmu events list from sysfs | ||
| 910 | * Save it into perf_pmu_events_list | ||
| 911 | */ | ||
| 912 | static void perf_pmu__parse_init(void) | ||
| 913 | { | ||
| 914 | |||
| 915 | struct perf_pmu *pmu = NULL; | ||
| 916 | struct perf_pmu_alias *alias; | ||
| 864 | int len = 0; | 917 | int len = 0; |
| 865 | 918 | ||
| 866 | if (!o) | 919 | pmu = perf_pmu__find("cpu"); |
| 867 | return ret; | 920 | if ((pmu == NULL) || list_empty(&pmu->aliases)) { |
| 868 | while ((p = strsep(&t, ",")) != NULL) { | 921 | perf_pmu_events_list_num = -1; |
| 869 | if (s) | 922 | return; |
| 870 | str_append(&s, &len, ","); | ||
| 871 | str_append(&s, &len, "cpu/"); | ||
| 872 | str_append(&s, &len, p); | ||
| 873 | str_append(&s, &len, "/"); | ||
| 874 | } | 923 | } |
| 875 | free(o); | 924 | list_for_each_entry(alias, &pmu->aliases, list) { |
| 876 | if (!s) | 925 | if (strchr(alias->name, '-')) |
| 877 | return -ENOMEM; | 926 | len++; |
| 878 | return parse_events__scanner(s, data, start_token); | 927 | len++; |
| 928 | } | ||
| 929 | perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len); | ||
| 930 | if (!perf_pmu_events_list) | ||
| 931 | return; | ||
| 932 | perf_pmu_events_list_num = len; | ||
| 933 | |||
| 934 | len = 0; | ||
| 935 | list_for_each_entry(alias, &pmu->aliases, list) { | ||
| 936 | struct perf_pmu_event_symbol *p = perf_pmu_events_list + len; | ||
| 937 | char *tmp = strchr(alias->name, '-'); | ||
| 938 | |||
| 939 | if (tmp != NULL) { | ||
| 940 | SET_SYMBOL(strndup(alias->name, tmp - alias->name), | ||
| 941 | PMU_EVENT_SYMBOL_PREFIX); | ||
| 942 | p++; | ||
| 943 | SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX); | ||
| 944 | len += 2; | ||
| 945 | } else { | ||
| 946 | SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL); | ||
| 947 | len++; | ||
| 948 | } | ||
| 949 | } | ||
| 950 | qsort(perf_pmu_events_list, len, | ||
| 951 | sizeof(struct perf_pmu_event_symbol), comp_pmu); | ||
| 952 | |||
| 953 | return; | ||
| 954 | err: | ||
| 955 | perf_pmu__parse_cleanup(); | ||
| 956 | } | ||
| 957 | |||
| 958 | enum perf_pmu_event_symbol_type | ||
| 959 | perf_pmu__parse_check(const char *name) | ||
| 960 | { | ||
| 961 | struct perf_pmu_event_symbol p, *r; | ||
| 962 | |||
| 963 | /* scan kernel pmu events from sysfs if needed */ | ||
| 964 | if (perf_pmu_events_list_num == 0) | ||
| 965 | perf_pmu__parse_init(); | ||
| 966 | /* | ||
| 967 | * name "cpu" could be prefix of cpu-cycles or cpu// events. | ||
| 968 | * cpu-cycles has been handled by hardcode. | ||
| 969 | * So it must be cpu// events, not kernel pmu event. | ||
| 970 | */ | ||
| 971 | if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu")) | ||
| 972 | return PMU_EVENT_SYMBOL_ERR; | ||
| 973 | |||
| 974 | p.symbol = strdup(name); | ||
| 975 | r = bsearch(&p, perf_pmu_events_list, | ||
| 976 | (size_t) perf_pmu_events_list_num, | ||
| 977 | sizeof(struct perf_pmu_event_symbol), comp_pmu); | ||
| 978 | free(p.symbol); | ||
| 979 | return r ? r->type : PMU_EVENT_SYMBOL_ERR; | ||
| 879 | } | 980 | } |
| 880 | 981 | ||
| 881 | static int parse_events__scanner(const char *str, void *data, int start_token) | 982 | static int parse_events__scanner(const char *str, void *data, int start_token) |
| @@ -898,8 +999,6 @@ static int parse_events__scanner(const char *str, void *data, int start_token) | |||
| 898 | parse_events__flush_buffer(buffer, scanner); | 999 | parse_events__flush_buffer(buffer, scanner); |
| 899 | parse_events__delete_buffer(buffer, scanner); | 1000 | parse_events__delete_buffer(buffer, scanner); |
| 900 | parse_events_lex_destroy(scanner); | 1001 | parse_events_lex_destroy(scanner); |
| 901 | if (ret && !strchr(str, '/')) | ||
| 902 | ret = parse_events_fixup(ret, str, data, start_token); | ||
| 903 | return ret; | 1002 | return ret; |
| 904 | } | 1003 | } |
| 905 | 1004 | ||
| @@ -934,6 +1033,7 @@ int parse_events(struct perf_evlist *evlist, const char *str) | |||
| 934 | int ret; | 1033 | int ret; |
| 935 | 1034 | ||
| 936 | ret = parse_events__scanner(str, &data, PE_START_EVENTS); | 1035 | ret = parse_events__scanner(str, &data, PE_START_EVENTS); |
| 1036 | perf_pmu__parse_cleanup(); | ||
| 937 | if (!ret) { | 1037 | if (!ret) { |
| 938 | int entries = data.idx - evlist->nr_entries; | 1038 | int entries = data.idx - evlist->nr_entries; |
| 939 | perf_evlist__splice_list_tail(evlist, &data.list, entries); | 1039 | perf_evlist__splice_list_tail(evlist, &data.list, entries); |
| @@ -973,7 +1073,7 @@ int parse_filter(const struct option *opt, const char *str, | |||
| 973 | 1073 | ||
| 974 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 1074 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
| 975 | fprintf(stderr, | 1075 | fprintf(stderr, |
| 976 | "-F option should follow a -e tracepoint option\n"); | 1076 | "--filter option should follow a -e tracepoint option\n"); |
| 977 | return -1; | 1077 | return -1; |
| 978 | } | 1078 | } |
| 979 | 1079 | ||
| @@ -1006,9 +1106,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | |||
| 1006 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 1106 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
| 1007 | char evt_path[MAXPATHLEN]; | 1107 | char evt_path[MAXPATHLEN]; |
| 1008 | char dir_path[MAXPATHLEN]; | 1108 | char dir_path[MAXPATHLEN]; |
| 1109 | char sbuf[STRERR_BUFSIZE]; | ||
| 1009 | 1110 | ||
| 1010 | if (debugfs_valid_mountpoint(tracing_events_path)) { | 1111 | if (debugfs_valid_mountpoint(tracing_events_path)) { |
| 1011 | printf(" [ Tracepoints not available: %s ]\n", strerror(errno)); | 1112 | printf(" [ Tracepoints not available: %s ]\n", |
| 1113 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 1012 | return; | 1114 | return; |
| 1013 | } | 1115 | } |
| 1014 | 1116 | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index df094b4ed5ed..db2cf78ff0f3 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -35,6 +35,18 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); | |||
| 35 | 35 | ||
| 36 | #define EVENTS_HELP_MAX (128*1024) | 36 | #define EVENTS_HELP_MAX (128*1024) |
| 37 | 37 | ||
| 38 | enum perf_pmu_event_symbol_type { | ||
| 39 | PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */ | ||
| 40 | PMU_EVENT_SYMBOL, /* normal style PMU event */ | ||
| 41 | PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */ | ||
| 42 | PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */ | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct perf_pmu_event_symbol { | ||
| 46 | char *symbol; | ||
| 47 | enum perf_pmu_event_symbol_type type; | ||
| 48 | }; | ||
| 49 | |||
| 38 | enum { | 50 | enum { |
| 39 | PARSE_EVENTS__TERM_TYPE_NUM, | 51 | PARSE_EVENTS__TERM_TYPE_NUM, |
| 40 | PARSE_EVENTS__TERM_TYPE_STR, | 52 | PARSE_EVENTS__TERM_TYPE_STR, |
| @@ -95,6 +107,8 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, | |||
| 95 | void *ptr, char *type); | 107 | void *ptr, char *type); |
| 96 | int parse_events_add_pmu(struct list_head *list, int *idx, | 108 | int parse_events_add_pmu(struct list_head *list, int *idx, |
| 97 | char *pmu , struct list_head *head_config); | 109 | char *pmu , struct list_head *head_config); |
| 110 | enum perf_pmu_event_symbol_type | ||
| 111 | perf_pmu__parse_check(const char *name); | ||
| 98 | void parse_events__set_leader(char *name, struct list_head *list); | 112 | void parse_events__set_leader(char *name, struct list_head *list); |
| 99 | void parse_events_update_lists(struct list_head *list_event, | 113 | void parse_events_update_lists(struct list_head *list_event, |
| 100 | struct list_head *list_all); | 114 | struct list_head *list_all); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 343299575b30..906630bbf8eb 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
| @@ -51,6 +51,24 @@ static int str(yyscan_t scanner, int token) | |||
| 51 | return token; | 51 | return token; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | static int pmu_str_check(yyscan_t scanner) | ||
| 55 | { | ||
| 56 | YYSTYPE *yylval = parse_events_get_lval(scanner); | ||
| 57 | char *text = parse_events_get_text(scanner); | ||
| 58 | |||
| 59 | yylval->str = strdup(text); | ||
| 60 | switch (perf_pmu__parse_check(text)) { | ||
| 61 | case PMU_EVENT_SYMBOL_PREFIX: | ||
| 62 | return PE_PMU_EVENT_PRE; | ||
| 63 | case PMU_EVENT_SYMBOL_SUFFIX: | ||
| 64 | return PE_PMU_EVENT_SUF; | ||
| 65 | case PMU_EVENT_SYMBOL: | ||
| 66 | return PE_KERNEL_PMU_EVENT; | ||
| 67 | default: | ||
| 68 | return PE_NAME; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 54 | static int sym(yyscan_t scanner, int type, int config) | 72 | static int sym(yyscan_t scanner, int type, int config) |
| 55 | { | 73 | { |
| 56 | YYSTYPE *yylval = parse_events_get_lval(scanner); | 74 | YYSTYPE *yylval = parse_events_get_lval(scanner); |
| @@ -178,6 +196,16 @@ alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_AL | |||
| 178 | emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } | 196 | emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } |
| 179 | dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } | 197 | dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } |
| 180 | 198 | ||
| 199 | /* | ||
| 200 | * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately. | ||
| 201 | * Because the prefix cycles is mixed up with cpu-cycles. | ||
| 202 | * loads and stores are mixed up with cache event | ||
| 203 | */ | ||
| 204 | cycles-ct { return str(yyscanner, PE_KERNEL_PMU_EVENT); } | ||
| 205 | cycles-t { return str(yyscanner, PE_KERNEL_PMU_EVENT); } | ||
| 206 | mem-loads { return str(yyscanner, PE_KERNEL_PMU_EVENT); } | ||
| 207 | mem-stores { return str(yyscanner, PE_KERNEL_PMU_EVENT); } | ||
| 208 | |||
| 181 | L1-dcache|l1-d|l1d|L1-data | | 209 | L1-dcache|l1-d|l1d|L1-data | |
| 182 | L1-icache|l1-i|l1i|L1-instruction | | 210 | L1-icache|l1-i|l1i|L1-instruction | |
| 183 | LLC|L2 | | 211 | LLC|L2 | |
| @@ -199,7 +227,7 @@ r{num_raw_hex} { return raw(yyscanner); } | |||
| 199 | {num_hex} { return value(yyscanner, 16); } | 227 | {num_hex} { return value(yyscanner, 16); } |
| 200 | 228 | ||
| 201 | {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } | 229 | {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } |
| 202 | {name} { return str(yyscanner, PE_NAME); } | 230 | {name} { return pmu_str_check(yyscanner); } |
| 203 | "/" { BEGIN(config); return '/'; } | 231 | "/" { BEGIN(config); return '/'; } |
| 204 | - { return '-'; } | 232 | - { return '-'; } |
| 205 | , { BEGIN(event); return ','; } | 233 | , { BEGIN(event); return ','; } |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 0bc87ba46bf3..93c4c9fbc922 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
| @@ -47,6 +47,7 @@ static inc_group_count(struct list_head *list, | |||
| 47 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT | 47 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT |
| 48 | %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP | 48 | %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP |
| 49 | %token PE_ERROR | 49 | %token PE_ERROR |
| 50 | %token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT | ||
| 50 | %type <num> PE_VALUE | 51 | %type <num> PE_VALUE |
| 51 | %type <num> PE_VALUE_SYM_HW | 52 | %type <num> PE_VALUE_SYM_HW |
| 52 | %type <num> PE_VALUE_SYM_SW | 53 | %type <num> PE_VALUE_SYM_SW |
| @@ -58,6 +59,7 @@ static inc_group_count(struct list_head *list, | |||
| 58 | %type <str> PE_MODIFIER_EVENT | 59 | %type <str> PE_MODIFIER_EVENT |
| 59 | %type <str> PE_MODIFIER_BP | 60 | %type <str> PE_MODIFIER_BP |
| 60 | %type <str> PE_EVENT_NAME | 61 | %type <str> PE_EVENT_NAME |
| 62 | %type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT | ||
| 61 | %type <num> value_sym | 63 | %type <num> value_sym |
| 62 | %type <head> event_config | 64 | %type <head> event_config |
| 63 | %type <term> event_term | 65 | %type <term> event_term |
| @@ -210,6 +212,54 @@ PE_NAME '/' event_config '/' | |||
| 210 | parse_events__free_terms($3); | 212 | parse_events__free_terms($3); |
| 211 | $$ = list; | 213 | $$ = list; |
| 212 | } | 214 | } |
| 215 | | | ||
| 216 | PE_NAME '/' '/' | ||
| 217 | { | ||
| 218 | struct parse_events_evlist *data = _data; | ||
| 219 | struct list_head *list; | ||
| 220 | |||
| 221 | ALLOC_LIST(list); | ||
| 222 | ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL)); | ||
| 223 | $$ = list; | ||
| 224 | } | ||
| 225 | | | ||
| 226 | PE_KERNEL_PMU_EVENT sep_dc | ||
| 227 | { | ||
| 228 | struct parse_events_evlist *data = _data; | ||
| 229 | struct list_head *head; | ||
| 230 | struct parse_events_term *term; | ||
| 231 | struct list_head *list; | ||
| 232 | |||
| 233 | ALLOC_LIST(head); | ||
| 234 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | ||
| 235 | $1, 1)); | ||
| 236 | list_add_tail(&term->list, head); | ||
| 237 | |||
| 238 | ALLOC_LIST(list); | ||
| 239 | ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head)); | ||
| 240 | parse_events__free_terms(head); | ||
| 241 | $$ = list; | ||
| 242 | } | ||
| 243 | | | ||
| 244 | PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc | ||
| 245 | { | ||
| 246 | struct parse_events_evlist *data = _data; | ||
| 247 | struct list_head *head; | ||
| 248 | struct parse_events_term *term; | ||
| 249 | struct list_head *list; | ||
| 250 | char pmu_name[128]; | ||
| 251 | snprintf(&pmu_name, 128, "%s-%s", $1, $3); | ||
| 252 | |||
| 253 | ALLOC_LIST(head); | ||
| 254 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, | ||
| 255 | &pmu_name, 1)); | ||
| 256 | list_add_tail(&term->list, head); | ||
| 257 | |||
| 258 | ALLOC_LIST(list); | ||
| 259 | ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head)); | ||
| 260 | parse_events__free_terms(head); | ||
| 261 | $$ = list; | ||
| 262 | } | ||
| 213 | 263 | ||
| 214 | value_sym: | 264 | value_sym: |
| 215 | PE_VALUE_SYM_HW | 265 | PE_VALUE_SYM_HW |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 7a811eb61f75..e243ad962a4d 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> |
| @@ -10,16 +12,6 @@ | |||
| 10 | #include "parse-events.h" | 12 | #include "parse-events.h" |
| 11 | #include "cpumap.h" | 13 | #include "cpumap.h" |
| 12 | 14 | ||
| 13 | #define UNIT_MAX_LEN 31 /* max length for event unit name */ | ||
| 14 | |||
| 15 | struct perf_pmu_alias { | ||
| 16 | char *name; | ||
| 17 | struct list_head terms; | ||
| 18 | struct list_head list; | ||
| 19 | char unit[UNIT_MAX_LEN+1]; | ||
| 20 | double scale; | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct perf_pmu_format { | 15 | struct perf_pmu_format { |
| 24 | char *name; | 16 | char *name; |
| 25 | int value; | 17 | int value; |
| @@ -208,6 +200,19 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI | |||
| 208 | return 0; | 200 | return 0; |
| 209 | } | 201 | } |
| 210 | 202 | ||
| 203 | static inline bool pmu_alias_info_file(char *name) | ||
| 204 | { | ||
| 205 | size_t len; | ||
| 206 | |||
| 207 | len = strlen(name); | ||
| 208 | if (len > 5 && !strcmp(name + len - 5, ".unit")) | ||
| 209 | return true; | ||
| 210 | if (len > 6 && !strcmp(name + len - 6, ".scale")) | ||
| 211 | return true; | ||
| 212 | |||
| 213 | return false; | ||
| 214 | } | ||
| 215 | |||
| 211 | /* | 216 | /* |
| 212 | * Process all the sysfs attributes located under the directory | 217 | * Process all the sysfs attributes located under the directory |
| 213 | * specified in 'dir' parameter. | 218 | * specified in 'dir' parameter. |
| @@ -216,7 +221,6 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
| 216 | { | 221 | { |
| 217 | struct dirent *evt_ent; | 222 | struct dirent *evt_ent; |
| 218 | DIR *event_dir; | 223 | DIR *event_dir; |
| 219 | size_t len; | ||
| 220 | int ret = 0; | 224 | int ret = 0; |
| 221 | 225 | ||
| 222 | event_dir = opendir(dir); | 226 | event_dir = opendir(dir); |
| @@ -232,13 +236,9 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
| 232 | continue; | 236 | continue; |
| 233 | 237 | ||
| 234 | /* | 238 | /* |
| 235 | * skip .unit and .scale info files | 239 | * skip info files parsed in perf_pmu__new_alias() |
| 236 | * parsed in perf_pmu__new_alias() | ||
| 237 | */ | 240 | */ |
| 238 | len = strlen(name); | 241 | 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; | 242 | continue; |
| 243 | 243 | ||
| 244 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | 244 | snprintf(path, PATH_MAX, "%s/%s", dir, name); |
| @@ -387,6 +387,12 @@ static struct cpu_map *pmu_cpumask(const char *name) | |||
| 387 | return cpus; | 387 | return cpus; |
| 388 | } | 388 | } |
| 389 | 389 | ||
| 390 | struct perf_event_attr *__attribute__((weak)) | ||
| 391 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) | ||
| 392 | { | ||
| 393 | return NULL; | ||
| 394 | } | ||
| 395 | |||
| 390 | static struct perf_pmu *pmu_lookup(const char *name) | 396 | static struct perf_pmu *pmu_lookup(const char *name) |
| 391 | { | 397 | { |
| 392 | struct perf_pmu *pmu; | 398 | struct perf_pmu *pmu; |
| @@ -421,6 +427,9 @@ static struct perf_pmu *pmu_lookup(const char *name) | |||
| 421 | pmu->name = strdup(name); | 427 | pmu->name = strdup(name); |
| 422 | pmu->type = type; | 428 | pmu->type = type; |
| 423 | list_add_tail(&pmu->list, &pmus); | 429 | list_add_tail(&pmu->list, &pmus); |
| 430 | |||
| 431 | pmu->default_config = perf_pmu__get_default_config(pmu); | ||
| 432 | |||
| 424 | return pmu; | 433 | return pmu; |
| 425 | } | 434 | } |
| 426 | 435 | ||
| @@ -479,28 +488,24 @@ pmu_find_format(struct list_head *formats, char *name) | |||
| 479 | } | 488 | } |
| 480 | 489 | ||
| 481 | /* | 490 | /* |
| 482 | * Returns value based on the format definition (format parameter) | 491 | * Sets value based on the format definition (format parameter) |
| 483 | * and unformated value (value parameter). | 492 | * and unformated value (value parameter). |
| 484 | * | ||
| 485 | * TODO maybe optimize a little ;) | ||
| 486 | */ | 493 | */ |
| 487 | static __u64 pmu_format_value(unsigned long *format, __u64 value) | 494 | static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, |
| 495 | bool zero) | ||
| 488 | { | 496 | { |
| 489 | unsigned long fbit, vbit; | 497 | unsigned long fbit, vbit; |
| 490 | __u64 v = 0; | ||
| 491 | 498 | ||
| 492 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { | 499 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { |
| 493 | 500 | ||
| 494 | if (!test_bit(fbit, format)) | 501 | if (!test_bit(fbit, format)) |
| 495 | continue; | 502 | continue; |
| 496 | 503 | ||
| 497 | if (!(value & (1llu << vbit++))) | 504 | if (value & (1llu << vbit++)) |
| 498 | continue; | 505 | *v |= (1llu << fbit); |
| 499 | 506 | else if (zero) | |
| 500 | v |= (1llu << fbit); | 507 | *v &= ~(1llu << fbit); |
| 501 | } | 508 | } |
| 502 | |||
| 503 | return v; | ||
| 504 | } | 509 | } |
| 505 | 510 | ||
| 506 | /* | 511 | /* |
| @@ -509,7 +514,8 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) | |||
| 509 | */ | 514 | */ |
| 510 | static int pmu_config_term(struct list_head *formats, | 515 | static int pmu_config_term(struct list_head *formats, |
| 511 | struct perf_event_attr *attr, | 516 | struct perf_event_attr *attr, |
| 512 | struct parse_events_term *term) | 517 | struct parse_events_term *term, |
| 518 | bool zero) | ||
| 513 | { | 519 | { |
| 514 | struct perf_pmu_format *format; | 520 | struct perf_pmu_format *format; |
| 515 | __u64 *vp; | 521 | __u64 *vp; |
| @@ -548,18 +554,19 @@ static int pmu_config_term(struct list_head *formats, | |||
| 548 | * non-hardcoded terms, here's the place to translate | 554 | * non-hardcoded terms, here's the place to translate |
| 549 | * them into value. | 555 | * them into value. |
| 550 | */ | 556 | */ |
| 551 | *vp |= pmu_format_value(format->bits, term->val.num); | 557 | pmu_format_value(format->bits, term->val.num, vp, zero); |
| 552 | return 0; | 558 | return 0; |
| 553 | } | 559 | } |
| 554 | 560 | ||
| 555 | int perf_pmu__config_terms(struct list_head *formats, | 561 | int perf_pmu__config_terms(struct list_head *formats, |
| 556 | struct perf_event_attr *attr, | 562 | struct perf_event_attr *attr, |
| 557 | struct list_head *head_terms) | 563 | struct list_head *head_terms, |
| 564 | bool zero) | ||
| 558 | { | 565 | { |
| 559 | struct parse_events_term *term; | 566 | struct parse_events_term *term; |
| 560 | 567 | ||
| 561 | list_for_each_entry(term, head_terms, list) | 568 | list_for_each_entry(term, head_terms, list) |
| 562 | if (pmu_config_term(formats, attr, term)) | 569 | if (pmu_config_term(formats, attr, term, zero)) |
| 563 | return -EINVAL; | 570 | return -EINVAL; |
| 564 | 571 | ||
| 565 | return 0; | 572 | return 0; |
| @@ -573,8 +580,10 @@ int perf_pmu__config_terms(struct list_head *formats, | |||
| 573 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 580 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
| 574 | struct list_head *head_terms) | 581 | struct list_head *head_terms) |
| 575 | { | 582 | { |
| 583 | bool zero = !!pmu->default_config; | ||
| 584 | |||
| 576 | attr->type = pmu->type; | 585 | attr->type = pmu->type; |
| 577 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); | 586 | return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero); |
| 578 | } | 587 | } |
| 579 | 588 | ||
| 580 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, | 589 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
| @@ -634,7 +643,7 @@ static int check_unit_scale(struct perf_pmu_alias *alias, | |||
| 634 | * defined for the alias | 643 | * defined for the alias |
| 635 | */ | 644 | */ |
| 636 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 645 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
| 637 | const char **unit, double *scale) | 646 | struct perf_pmu_info *info) |
| 638 | { | 647 | { |
| 639 | struct parse_events_term *term, *h; | 648 | struct parse_events_term *term, *h; |
| 640 | struct perf_pmu_alias *alias; | 649 | struct perf_pmu_alias *alias; |
| @@ -644,8 +653,8 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
| 644 | * Mark unit and scale as not set | 653 | * Mark unit and scale as not set |
| 645 | * (different from default values, see below) | 654 | * (different from default values, see below) |
| 646 | */ | 655 | */ |
| 647 | *unit = NULL; | 656 | info->unit = NULL; |
| 648 | *scale = 0.0; | 657 | info->scale = 0.0; |
| 649 | 658 | ||
| 650 | list_for_each_entry_safe(term, h, head_terms, list) { | 659 | list_for_each_entry_safe(term, h, head_terms, list) { |
| 651 | alias = pmu_find_alias(pmu, term); | 660 | alias = pmu_find_alias(pmu, term); |
| @@ -655,7 +664,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
| 655 | if (ret) | 664 | if (ret) |
| 656 | return ret; | 665 | return ret; |
| 657 | 666 | ||
| 658 | ret = check_unit_scale(alias, unit, scale); | 667 | ret = check_unit_scale(alias, &info->unit, &info->scale); |
| 659 | if (ret) | 668 | if (ret) |
| 660 | return ret; | 669 | return ret; |
| 661 | 670 | ||
| @@ -668,11 +677,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | |||
| 668 | * set defaults as for evsel | 677 | * set defaults as for evsel |
| 669 | * unit cannot left to NULL | 678 | * unit cannot left to NULL |
| 670 | */ | 679 | */ |
| 671 | if (*unit == NULL) | 680 | if (info->unit == NULL) |
| 672 | *unit = ""; | 681 | info->unit = ""; |
| 673 | 682 | ||
| 674 | if (*scale == 0.0) | 683 | if (info->scale == 0.0) |
| 675 | *scale = 1.0; | 684 | info->scale = 1.0; |
| 676 | 685 | ||
| 677 | return 0; | 686 | return 0; |
| 678 | } | 687 | } |
| @@ -794,3 +803,39 @@ bool pmu_have_event(const char *pname, const char *name) | |||
| 794 | } | 803 | } |
| 795 | return false; | 804 | return false; |
| 796 | } | 805 | } |
| 806 | |||
| 807 | static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) | ||
| 808 | { | ||
| 809 | struct stat st; | ||
| 810 | char path[PATH_MAX]; | ||
| 811 | const char *sysfs; | ||
| 812 | |||
| 813 | sysfs = sysfs__mountpoint(); | ||
| 814 | if (!sysfs) | ||
| 815 | return NULL; | ||
| 816 | |||
| 817 | snprintf(path, PATH_MAX, | ||
| 818 | "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name); | ||
| 819 | |||
| 820 | if (stat(path, &st) < 0) | ||
| 821 | return NULL; | ||
| 822 | |||
| 823 | return fopen(path, "r"); | ||
| 824 | } | ||
| 825 | |||
| 826 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | ||
| 827 | ...) | ||
| 828 | { | ||
| 829 | va_list args; | ||
| 830 | FILE *file; | ||
| 831 | int ret = EOF; | ||
| 832 | |||
| 833 | va_start(args, fmt); | ||
| 834 | file = perf_pmu__open_file(pmu, name); | ||
| 835 | if (file) { | ||
| 836 | ret = vfscanf(file, fmt, args); | ||
| 837 | fclose(file); | ||
| 838 | } | ||
| 839 | va_end(args); | ||
| 840 | return ret; | ||
| 841 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index c14a543ce1f3..fe9dfbee8eed 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
| @@ -13,13 +13,31 @@ 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; | ||
| 31 | }; | ||
| 32 | |||
| 33 | #define UNIT_MAX_LEN 31 /* max length for event unit name */ | ||
| 34 | |||
| 35 | struct perf_pmu_alias { | ||
| 36 | char *name; | ||
| 37 | struct list_head terms; /* HEAD struct parse_events_term -> list */ | ||
| 38 | struct list_head list; /* ELEM */ | ||
| 39 | char unit[UNIT_MAX_LEN+1]; | ||
| 40 | double scale; | ||
| 23 | }; | 41 | }; |
| 24 | 42 | ||
| 25 | struct perf_pmu *perf_pmu__find(const char *name); | 43 | struct perf_pmu *perf_pmu__find(const char *name); |
| @@ -27,9 +45,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
| 27 | struct list_head *head_terms); | 45 | struct list_head *head_terms); |
| 28 | int perf_pmu__config_terms(struct list_head *formats, | 46 | int perf_pmu__config_terms(struct list_head *formats, |
| 29 | struct perf_event_attr *attr, | 47 | struct perf_event_attr *attr, |
| 30 | struct list_head *head_terms); | 48 | struct list_head *head_terms, |
| 49 | bool zero); | ||
| 31 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, | 50 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, |
| 32 | const char **unit, double *scale); | 51 | struct perf_pmu_info *info); |
| 33 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 52 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
| 34 | struct list_head *head_terms); | 53 | struct list_head *head_terms); |
| 35 | int perf_pmu_wrap(void); | 54 | int perf_pmu_wrap(void); |
| @@ -45,5 +64,11 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | |||
| 45 | void print_pmu_events(const char *event_glob, bool name_only); | 64 | void print_pmu_events(const char *event_glob, bool name_only); |
| 46 | bool pmu_have_event(const char *pname, const char *name); | 65 | bool pmu_have_event(const char *pname, const char *name); |
| 47 | 66 | ||
| 67 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | ||
| 68 | ...) __attribute__((format(scanf, 3, 4))); | ||
| 69 | |||
| 48 | int perf_pmu__test(void); | 70 | int perf_pmu__test(void); |
| 71 | |||
| 72 | struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu); | ||
| 73 | |||
| 49 | #endif /* __PMU_H */ | 74 | #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..496f21cadd97 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | 28 | ||
| 29 | #include "../../perf.h" | 29 | #include "../../perf.h" |
| 30 | #include "../debug.h" | 30 | #include "../debug.h" |
| 31 | #include "../callchain.h" | ||
| 31 | #include "../evsel.h" | 32 | #include "../evsel.h" |
| 32 | #include "../util.h" | 33 | #include "../util.h" |
| 33 | #include "../event.h" | 34 | #include "../event.h" |
| @@ -73,6 +74,35 @@ static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObj | |||
| 73 | Py_DECREF(val); | 74 | Py_DECREF(val); |
| 74 | } | 75 | } |
| 75 | 76 | ||
| 77 | static PyObject *get_handler(const char *handler_name) | ||
| 78 | { | ||
| 79 | PyObject *handler; | ||
| 80 | |||
| 81 | handler = PyDict_GetItemString(main_dict, handler_name); | ||
| 82 | if (handler && !PyCallable_Check(handler)) | ||
| 83 | return NULL; | ||
| 84 | return handler; | ||
| 85 | } | ||
| 86 | |||
| 87 | static void call_object(PyObject *handler, PyObject *args, const char *die_msg) | ||
| 88 | { | ||
| 89 | PyObject *retval; | ||
| 90 | |||
| 91 | retval = PyObject_CallObject(handler, args); | ||
| 92 | if (retval == NULL) | ||
| 93 | handler_call_die(die_msg); | ||
| 94 | Py_DECREF(retval); | ||
| 95 | } | ||
| 96 | |||
| 97 | static void try_call_object(const char *handler_name, PyObject *args) | ||
| 98 | { | ||
| 99 | PyObject *handler; | ||
| 100 | |||
| 101 | handler = get_handler(handler_name); | ||
| 102 | if (handler) | ||
| 103 | call_object(handler, args, handler_name); | ||
| 104 | } | ||
| 105 | |||
| 76 | static void define_value(enum print_arg_type field_type, | 106 | static void define_value(enum print_arg_type field_type, |
| 77 | const char *ev_name, | 107 | const char *ev_name, |
| 78 | const char *field_name, | 108 | const char *field_name, |
| @@ -80,7 +110,7 @@ static void define_value(enum print_arg_type field_type, | |||
| 80 | const char *field_str) | 110 | const char *field_str) |
| 81 | { | 111 | { |
| 82 | const char *handler_name = "define_flag_value"; | 112 | const char *handler_name = "define_flag_value"; |
| 83 | PyObject *handler, *t, *retval; | 113 | PyObject *t; |
| 84 | unsigned long long value; | 114 | unsigned long long value; |
| 85 | unsigned n = 0; | 115 | unsigned n = 0; |
| 86 | 116 | ||
| @@ -98,13 +128,7 @@ static void define_value(enum print_arg_type field_type, | |||
| 98 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); | 128 | PyTuple_SetItem(t, n++, PyInt_FromLong(value)); |
| 99 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); | 129 | PyTuple_SetItem(t, n++, PyString_FromString(field_str)); |
| 100 | 130 | ||
| 101 | handler = PyDict_GetItemString(main_dict, handler_name); | 131 | 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 | 132 | ||
| 109 | Py_DECREF(t); | 133 | Py_DECREF(t); |
| 110 | } | 134 | } |
| @@ -127,7 +151,7 @@ static void define_field(enum print_arg_type field_type, | |||
| 127 | const char *delim) | 151 | const char *delim) |
| 128 | { | 152 | { |
| 129 | const char *handler_name = "define_flag_field"; | 153 | const char *handler_name = "define_flag_field"; |
| 130 | PyObject *handler, *t, *retval; | 154 | PyObject *t; |
| 131 | unsigned n = 0; | 155 | unsigned n = 0; |
| 132 | 156 | ||
| 133 | if (field_type == PRINT_SYMBOL) | 157 | if (field_type == PRINT_SYMBOL) |
| @@ -145,13 +169,7 @@ static void define_field(enum print_arg_type field_type, | |||
| 145 | if (field_type == PRINT_FLAGS) | 169 | if (field_type == PRINT_FLAGS) |
| 146 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); | 170 | PyTuple_SetItem(t, n++, PyString_FromString(delim)); |
| 147 | 171 | ||
| 148 | handler = PyDict_GetItemString(main_dict, handler_name); | 172 | 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 | 173 | ||
| 156 | Py_DECREF(t); | 174 | Py_DECREF(t); |
| 157 | } | 175 | } |
| @@ -362,7 +380,7 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 362 | struct thread *thread, | 380 | struct thread *thread, |
| 363 | struct addr_location *al) | 381 | struct addr_location *al) |
| 364 | { | 382 | { |
| 365 | PyObject *handler, *retval, *context, *t, *obj, *callchain; | 383 | PyObject *handler, *context, *t, *obj, *callchain; |
| 366 | PyObject *dict = NULL; | 384 | PyObject *dict = NULL; |
| 367 | static char handler_name[256]; | 385 | static char handler_name[256]; |
| 368 | struct format_field *field; | 386 | struct format_field *field; |
| @@ -387,9 +405,7 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 387 | 405 | ||
| 388 | sprintf(handler_name, "%s__%s", event->system, event->name); | 406 | sprintf(handler_name, "%s__%s", event->system, event->name); |
| 389 | 407 | ||
| 390 | handler = PyDict_GetItemString(main_dict, handler_name); | 408 | handler = get_handler(handler_name); |
| 391 | if (handler && !PyCallable_Check(handler)) | ||
| 392 | handler = NULL; | ||
| 393 | if (!handler) { | 409 | if (!handler) { |
| 394 | dict = PyDict_New(); | 410 | dict = PyDict_New(); |
| 395 | if (!dict) | 411 | if (!dict) |
| @@ -450,19 +466,9 @@ static void python_process_tracepoint(struct perf_sample *sample, | |||
| 450 | Py_FatalError("error resizing Python tuple"); | 466 | Py_FatalError("error resizing Python tuple"); |
| 451 | 467 | ||
| 452 | if (handler) { | 468 | if (handler) { |
| 453 | retval = PyObject_CallObject(handler, t); | 469 | call_object(handler, t, handler_name); |
| 454 | if (retval == NULL) | ||
| 455 | handler_call_die(handler_name); | ||
| 456 | Py_DECREF(retval); | ||
| 457 | } else { | 470 | } else { |
| 458 | handler = PyDict_GetItemString(main_dict, "trace_unhandled"); | 471 | 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); | 472 | Py_DECREF(dict); |
| 467 | } | 473 | } |
| 468 | 474 | ||
| @@ -474,7 +480,7 @@ static void python_process_general_event(struct perf_sample *sample, | |||
| 474 | struct thread *thread, | 480 | struct thread *thread, |
| 475 | struct addr_location *al) | 481 | struct addr_location *al) |
| 476 | { | 482 | { |
| 477 | PyObject *handler, *retval, *t, *dict, *callchain, *dict_sample; | 483 | PyObject *handler, *t, *dict, *callchain, *dict_sample; |
| 478 | static char handler_name[64]; | 484 | static char handler_name[64]; |
| 479 | unsigned n = 0; | 485 | unsigned n = 0; |
| 480 | 486 | ||
| @@ -496,8 +502,8 @@ static void python_process_general_event(struct perf_sample *sample, | |||
| 496 | 502 | ||
| 497 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); | 503 | snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); |
| 498 | 504 | ||
| 499 | handler = PyDict_GetItemString(main_dict, handler_name); | 505 | handler = get_handler(handler_name); |
| 500 | if (!handler || !PyCallable_Check(handler)) | 506 | if (!handler) |
| 501 | goto exit; | 507 | goto exit; |
| 502 | 508 | ||
| 503 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); | 509 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); |
| @@ -539,10 +545,7 @@ static void python_process_general_event(struct perf_sample *sample, | |||
| 539 | if (_PyTuple_Resize(&t, n) == -1) | 545 | if (_PyTuple_Resize(&t, n) == -1) |
| 540 | Py_FatalError("error resizing Python tuple"); | 546 | Py_FatalError("error resizing Python tuple"); |
| 541 | 547 | ||
| 542 | retval = PyObject_CallObject(handler, t); | 548 | call_object(handler, t, handler_name); |
| 543 | if (retval == NULL) | ||
| 544 | handler_call_die(handler_name); | ||
| 545 | Py_DECREF(retval); | ||
| 546 | exit: | 549 | exit: |
| 547 | Py_DECREF(dict); | 550 | Py_DECREF(dict); |
| 548 | Py_DECREF(t); | 551 | Py_DECREF(t); |
| @@ -566,36 +569,24 @@ static void python_process_event(union perf_event *event __maybe_unused, | |||
| 566 | 569 | ||
| 567 | static int run_start_sub(void) | 570 | static int run_start_sub(void) |
| 568 | { | 571 | { |
| 569 | PyObject *handler, *retval; | ||
| 570 | int err = 0; | ||
| 571 | |||
| 572 | main_module = PyImport_AddModule("__main__"); | 572 | main_module = PyImport_AddModule("__main__"); |
| 573 | if (main_module == NULL) | 573 | if (main_module == NULL) |
| 574 | return -1; | 574 | return -1; |
| 575 | Py_INCREF(main_module); | 575 | Py_INCREF(main_module); |
| 576 | 576 | ||
| 577 | main_dict = PyModule_GetDict(main_module); | 577 | main_dict = PyModule_GetDict(main_module); |
| 578 | if (main_dict == NULL) { | 578 | if (main_dict == NULL) |
| 579 | err = -1; | ||
| 580 | goto error; | 579 | goto error; |
| 581 | } | ||
| 582 | Py_INCREF(main_dict); | 580 | Py_INCREF(main_dict); |
| 583 | 581 | ||
| 584 | handler = PyDict_GetItemString(main_dict, "trace_begin"); | 582 | try_call_object("trace_begin", NULL); |
| 585 | if (handler == NULL || !PyCallable_Check(handler)) | ||
| 586 | goto out; | ||
| 587 | 583 | ||
| 588 | retval = PyObject_CallObject(handler, NULL); | 584 | return 0; |
| 589 | if (retval == NULL) | ||
| 590 | handler_call_die("trace_begin"); | ||
| 591 | 585 | ||
| 592 | Py_DECREF(retval); | ||
| 593 | return err; | ||
| 594 | error: | 586 | error: |
| 595 | Py_XDECREF(main_dict); | 587 | Py_XDECREF(main_dict); |
| 596 | Py_XDECREF(main_module); | 588 | Py_XDECREF(main_module); |
| 597 | out: | 589 | return -1; |
| 598 | return err; | ||
| 599 | } | 590 | } |
| 600 | 591 | ||
| 601 | /* | 592 | /* |
| @@ -649,28 +640,23 @@ error: | |||
| 649 | return err; | 640 | return err; |
| 650 | } | 641 | } |
| 651 | 642 | ||
| 643 | static int python_flush_script(void) | ||
| 644 | { | ||
| 645 | return 0; | ||
| 646 | } | ||
| 647 | |||
| 652 | /* | 648 | /* |
| 653 | * Stop trace script | 649 | * Stop trace script |
| 654 | */ | 650 | */ |
| 655 | static int python_stop_script(void) | 651 | static int python_stop_script(void) |
| 656 | { | 652 | { |
| 657 | PyObject *handler, *retval; | 653 | try_call_object("trace_end", NULL); |
| 658 | int err = 0; | ||
| 659 | 654 | ||
| 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); | 655 | Py_XDECREF(main_dict); |
| 670 | Py_XDECREF(main_module); | 656 | Py_XDECREF(main_module); |
| 671 | Py_Finalize(); | 657 | Py_Finalize(); |
| 672 | 658 | ||
| 673 | return err; | 659 | return 0; |
| 674 | } | 660 | } |
| 675 | 661 | ||
| 676 | static int python_generate_script(struct pevent *pevent, const char *outfile) | 662 | static int python_generate_script(struct pevent *pevent, const char *outfile) |
| @@ -843,6 +829,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) | |||
| 843 | struct scripting_ops python_scripting_ops = { | 829 | struct scripting_ops python_scripting_ops = { |
| 844 | .name = "Python", | 830 | .name = "Python", |
| 845 | .start_script = python_start_script, | 831 | .start_script = python_start_script, |
| 832 | .flush_script = python_flush_script, | ||
| 846 | .stop_script = python_stop_script, | 833 | .stop_script = python_stop_script, |
| 847 | .process_event = python_process_event, | 834 | .process_event = python_process_event, |
| 848 | .generate_script = python_generate_script, | 835 | .generate_script = python_generate_script, |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 88dfef70c13d..6702ac28754b 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,42 @@ 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; | ||
| 640 | } | ||
| 641 | 525 | ||
| 642 | if (!list_empty(sc)) { | 526 | pr_oe_time(timestamp, "out of order event"); |
| 643 | new = list_entry(sc->next, struct sample_queue, list); | 527 | pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n", |
| 644 | list_del(&new->list); | 528 | oe->last_flush_type); |
| 645 | } else if (os->sample_buffer) { | 529 | |
| 646 | new = os->sample_buffer + os->sample_buffer_idx; | 530 | /* We could get out of order messages after forced flush. */ |
| 647 | if (++os->sample_buffer_idx == MAX_SAMPLE_BUFFER) | 531 | if (oe->last_flush_type != OE_FLUSH__HALF) |
| 648 | os->sample_buffer = NULL; | 532 | return -EINVAL; |
| 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 | } | 533 | } |
| 657 | 534 | ||
| 658 | new->timestamp = timestamp; | 535 | new = ordered_events__new(oe, timestamp, event); |
| 659 | new->file_offset = file_offset; | 536 | if (!new) { |
| 660 | new->event = event; | 537 | ordered_events__flush(s, tool, OE_FLUSH__HALF); |
| 538 | new = ordered_events__new(oe, timestamp, event); | ||
| 539 | } | ||
| 661 | 540 | ||
| 662 | __queue_event(new, s); | 541 | if (!new) |
| 542 | return -ENOMEM; | ||
| 663 | 543 | ||
| 544 | new->file_offset = file_offset; | ||
| 664 | return 0; | 545 | return 0; |
| 665 | } | 546 | } |
| 666 | 547 | ||
| @@ -920,11 +801,10 @@ perf_session__deliver_sample(struct perf_session *session, | |||
| 920 | &sample->read.one, machine); | 801 | &sample->read.one, machine); |
| 921 | } | 802 | } |
| 922 | 803 | ||
| 923 | static int perf_session_deliver_event(struct perf_session *session, | 804 | int perf_session__deliver_event(struct perf_session *session, |
| 924 | union perf_event *event, | 805 | union perf_event *event, |
| 925 | struct perf_sample *sample, | 806 | struct perf_sample *sample, |
| 926 | struct perf_tool *tool, | 807 | struct perf_tool *tool, u64 file_offset) |
| 927 | u64 file_offset) | ||
| 928 | { | 808 | { |
| 929 | struct perf_evsel *evsel; | 809 | struct perf_evsel *evsel; |
| 930 | struct machine *machine; | 810 | struct machine *machine; |
| @@ -932,22 +812,6 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
| 932 | dump_event(session, event, file_offset, sample); | 812 | dump_event(session, event, file_offset, sample); |
| 933 | 813 | ||
| 934 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); | 814 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); |
| 935 | if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) { | ||
| 936 | /* | ||
| 937 | * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here | ||
| 938 | * because the tools right now may apply filters, discarding | ||
| 939 | * some of the samples. For consistency, in the future we | ||
| 940 | * should have something like nr_filtered_samples and remove | ||
| 941 | * the sample->period from total_sample_period, etc, KISS for | ||
| 942 | * now tho. | ||
| 943 | * | ||
| 944 | * Also testing against NULL allows us to handle files without | ||
| 945 | * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the | ||
| 946 | * future probably it'll be a good idea to restrict event | ||
| 947 | * processing via perf_session to files with both set. | ||
| 948 | */ | ||
| 949 | hists__inc_nr_events(&evsel->hists, event->header.type); | ||
| 950 | } | ||
| 951 | 815 | ||
| 952 | machine = perf_session__find_machine_for_cpumode(session, event, | 816 | machine = perf_session__find_machine_for_cpumode(session, event, |
| 953 | sample); | 817 | sample); |
| @@ -1005,8 +869,10 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
| 1005 | switch (event->header.type) { | 869 | switch (event->header.type) { |
| 1006 | case PERF_RECORD_HEADER_ATTR: | 870 | case PERF_RECORD_HEADER_ATTR: |
| 1007 | err = tool->attr(tool, event, &session->evlist); | 871 | err = tool->attr(tool, event, &session->evlist); |
| 1008 | if (err == 0) | 872 | if (err == 0) { |
| 1009 | perf_session__set_id_hdr_size(session); | 873 | perf_session__set_id_hdr_size(session); |
| 874 | perf_session__set_comm_exec(session); | ||
| 875 | } | ||
| 1010 | return err; | 876 | return err; |
| 1011 | case PERF_RECORD_HEADER_EVENT_TYPE: | 877 | case PERF_RECORD_HEADER_EVENT_TYPE: |
| 1012 | /* | 878 | /* |
| @@ -1036,6 +902,61 @@ static void event_swap(union perf_event *event, bool sample_id_all) | |||
| 1036 | swap(event, sample_id_all); | 902 | swap(event, sample_id_all); |
| 1037 | } | 903 | } |
| 1038 | 904 | ||
| 905 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
| 906 | void *buf, size_t buf_sz, | ||
| 907 | union perf_event **event_ptr, | ||
| 908 | struct perf_sample *sample) | ||
| 909 | { | ||
| 910 | union perf_event *event; | ||
| 911 | size_t hdr_sz, rest; | ||
| 912 | int fd; | ||
| 913 | |||
| 914 | if (session->one_mmap && !session->header.needs_swap) { | ||
| 915 | event = file_offset - session->one_mmap_offset + | ||
| 916 | session->one_mmap_addr; | ||
| 917 | goto out_parse_sample; | ||
| 918 | } | ||
| 919 | |||
| 920 | if (perf_data_file__is_pipe(session->file)) | ||
| 921 | return -1; | ||
| 922 | |||
| 923 | fd = perf_data_file__fd(session->file); | ||
| 924 | hdr_sz = sizeof(struct perf_event_header); | ||
| 925 | |||
| 926 | if (buf_sz < hdr_sz) | ||
| 927 | return -1; | ||
| 928 | |||
| 929 | if (lseek(fd, file_offset, SEEK_SET) == (off_t)-1 || | ||
| 930 | readn(fd, &buf, hdr_sz) != (ssize_t)hdr_sz) | ||
| 931 | return -1; | ||
| 932 | |||
| 933 | event = (union perf_event *)buf; | ||
| 934 | |||
| 935 | if (session->header.needs_swap) | ||
| 936 | perf_event_header__bswap(&event->header); | ||
| 937 | |||
| 938 | if (event->header.size < hdr_sz) | ||
| 939 | return -1; | ||
| 940 | |||
| 941 | rest = event->header.size - hdr_sz; | ||
| 942 | |||
| 943 | if (readn(fd, &buf, rest) != (ssize_t)rest) | ||
| 944 | return -1; | ||
| 945 | |||
| 946 | if (session->header.needs_swap) | ||
| 947 | event_swap(event, perf_evlist__sample_id_all(session->evlist)); | ||
| 948 | |||
| 949 | out_parse_sample: | ||
| 950 | |||
| 951 | if (sample && event->header.type < PERF_RECORD_USER_TYPE_START && | ||
| 952 | perf_evlist__parse_sample(session->evlist, event, sample)) | ||
| 953 | return -1; | ||
| 954 | |||
| 955 | *event_ptr = event; | ||
| 956 | |||
| 957 | return 0; | ||
| 958 | } | ||
| 959 | |||
| 1039 | static s64 perf_session__process_event(struct perf_session *session, | 960 | static s64 perf_session__process_event(struct perf_session *session, |
| 1040 | union perf_event *event, | 961 | union perf_event *event, |
| 1041 | struct perf_tool *tool, | 962 | struct perf_tool *tool, |
| @@ -1062,15 +983,15 @@ static s64 perf_session__process_event(struct perf_session *session, | |||
| 1062 | if (ret) | 983 | if (ret) |
| 1063 | return ret; | 984 | return ret; |
| 1064 | 985 | ||
| 1065 | if (tool->ordered_samples) { | 986 | if (tool->ordered_events) { |
| 1066 | ret = perf_session_queue_event(session, event, &sample, | 987 | ret = perf_session_queue_event(session, event, tool, &sample, |
| 1067 | file_offset); | 988 | file_offset); |
| 1068 | if (ret != -ETIME) | 989 | if (ret != -ETIME) |
| 1069 | return ret; | 990 | return ret; |
| 1070 | } | 991 | } |
| 1071 | 992 | ||
| 1072 | return perf_session_deliver_event(session, event, &sample, tool, | 993 | return perf_session__deliver_event(session, event, &sample, tool, |
| 1073 | file_offset); | 994 | file_offset); |
| 1074 | } | 995 | } |
| 1075 | 996 | ||
| 1076 | void perf_event_header__bswap(struct perf_event_header *hdr) | 997 | void perf_event_header__bswap(struct perf_event_header *hdr) |
| @@ -1222,12 +1143,11 @@ more: | |||
| 1222 | goto more; | 1143 | goto more; |
| 1223 | done: | 1144 | done: |
| 1224 | /* do the final flush for ordered samples */ | 1145 | /* do the final flush for ordered samples */ |
| 1225 | session->ordered_samples.next_flush = ULLONG_MAX; | 1146 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
| 1226 | err = flush_sample_queue(session, tool); | ||
| 1227 | out_err: | 1147 | out_err: |
| 1228 | free(buf); | 1148 | free(buf); |
| 1229 | perf_session__warn_about_errors(session, tool); | 1149 | perf_session__warn_about_errors(session, tool); |
| 1230 | perf_session_free_sample_buffers(session); | 1150 | ordered_events__free(&session->ordered_events); |
| 1231 | return err; | 1151 | return err; |
| 1232 | } | 1152 | } |
| 1233 | 1153 | ||
| @@ -1368,12 +1288,11 @@ more: | |||
| 1368 | 1288 | ||
| 1369 | out: | 1289 | out: |
| 1370 | /* do the final flush for ordered samples */ | 1290 | /* do the final flush for ordered samples */ |
| 1371 | session->ordered_samples.next_flush = ULLONG_MAX; | 1291 | err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); |
| 1372 | err = flush_sample_queue(session, tool); | ||
| 1373 | out_err: | 1292 | out_err: |
| 1374 | ui_progress__finish(); | 1293 | ui_progress__finish(); |
| 1375 | perf_session__warn_about_errors(session, tool); | 1294 | perf_session__warn_about_errors(session, tool); |
| 1376 | perf_session_free_sample_buffers(session); | 1295 | ordered_events__free(&session->ordered_events); |
| 1377 | session->one_mmap = false; | 1296 | session->one_mmap = false; |
| 1378 | return err; | 1297 | return err; |
| 1379 | } | 1298 | } |
| @@ -1455,16 +1374,9 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp | |||
| 1455 | 1374 | ||
| 1456 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | 1375 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) |
| 1457 | { | 1376 | { |
| 1458 | struct perf_evsel *pos; | ||
| 1459 | size_t ret = fprintf(fp, "Aggregated stats:\n"); | 1377 | size_t ret = fprintf(fp, "Aggregated stats:\n"); |
| 1460 | 1378 | ||
| 1461 | ret += events_stats__fprintf(&session->stats, fp); | 1379 | ret += events_stats__fprintf(&session->stats, fp); |
| 1462 | |||
| 1463 | evlist__for_each(session->evlist, pos) { | ||
| 1464 | ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); | ||
| 1465 | ret += events_stats__fprintf(&pos->hists.stats, fp); | ||
| 1466 | } | ||
| 1467 | |||
| 1468 | return ret; | 1380 | return ret; |
| 1469 | } | 1381 | } |
| 1470 | 1382 | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 0321013bd9fd..a4be851f1a90 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -2,33 +2,19 @@ | |||
| 2 | #define __PERF_SESSION_H | 2 | #define __PERF_SESSION_H |
| 3 | 3 | ||
| 4 | #include "trace-event.h" | 4 | #include "trace-event.h" |
| 5 | #include "hist.h" | ||
| 6 | #include "event.h" | 5 | #include "event.h" |
| 7 | #include "header.h" | 6 | #include "header.h" |
| 8 | #include "machine.h" | 7 | #include "machine.h" |
| 9 | #include "symbol.h" | 8 | #include "symbol.h" |
| 10 | #include "thread.h" | 9 | #include "thread.h" |
| 11 | #include "data.h" | 10 | #include "data.h" |
| 11 | #include "ordered-events.h" | ||
| 12 | #include <linux/rbtree.h> | 12 | #include <linux/rbtree.h> |
| 13 | #include <linux/perf_event.h> | 13 | #include <linux/perf_event.h> |
| 14 | 14 | ||
| 15 | struct sample_queue; | ||
| 16 | struct ip_callchain; | 15 | struct ip_callchain; |
| 17 | struct thread; | 16 | struct thread; |
| 18 | 17 | ||
| 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 { | 18 | struct perf_session { |
| 33 | struct perf_header header; | 19 | struct perf_header header; |
| 34 | struct machines machines; | 20 | struct machines machines; |
| @@ -39,7 +25,7 @@ struct perf_session { | |||
| 39 | bool one_mmap; | 25 | bool one_mmap; |
| 40 | void *one_mmap_addr; | 26 | void *one_mmap_addr; |
| 41 | u64 one_mmap_offset; | 27 | u64 one_mmap_offset; |
| 42 | struct ordered_samples ordered_samples; | 28 | struct ordered_events ordered_events; |
| 43 | struct perf_data_file *file; | 29 | struct perf_data_file *file; |
| 44 | }; | 30 | }; |
| 45 | 31 | ||
| @@ -58,6 +44,11 @@ void perf_session__delete(struct perf_session *session); | |||
| 58 | 44 | ||
| 59 | void perf_event_header__bswap(struct perf_event_header *hdr); | 45 | void perf_event_header__bswap(struct perf_event_header *hdr); |
| 60 | 46 | ||
| 47 | int perf_session__peek_event(struct perf_session *session, off_t file_offset, | ||
| 48 | void *buf, size_t buf_sz, | ||
| 49 | union perf_event **event_ptr, | ||
| 50 | struct perf_sample *sample); | ||
| 51 | |||
| 61 | int __perf_session__process_events(struct perf_session *session, | 52 | int __perf_session__process_events(struct perf_session *session, |
| 62 | u64 data_offset, u64 data_size, u64 size, | 53 | u64 data_offset, u64 data_size, u64 size, |
| 63 | struct perf_tool *tool); | 54 | struct perf_tool *tool); |
| @@ -65,10 +56,16 @@ int perf_session__process_events(struct perf_session *session, | |||
| 65 | struct perf_tool *tool); | 56 | struct perf_tool *tool); |
| 66 | 57 | ||
| 67 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 58 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
| 68 | struct perf_sample *sample, u64 file_offset); | 59 | struct perf_tool *tool, struct perf_sample *sample, |
| 60 | u64 file_offset); | ||
| 69 | 61 | ||
| 70 | void perf_tool__fill_defaults(struct perf_tool *tool); | 62 | void perf_tool__fill_defaults(struct perf_tool *tool); |
| 71 | 63 | ||
| 64 | int perf_session__deliver_event(struct perf_session *session, | ||
| 65 | union perf_event *event, | ||
| 66 | struct perf_sample *sample, | ||
| 67 | struct perf_tool *tool, u64 file_offset); | ||
| 68 | |||
| 72 | int perf_session__resolve_callchain(struct perf_session *session, | 69 | int perf_session__resolve_callchain(struct perf_session *session, |
| 73 | struct perf_evsel *evsel, | 70 | struct perf_evsel *evsel, |
| 74 | struct thread *thread, | 71 | struct thread *thread, |
| @@ -128,5 +125,5 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
| 128 | 125 | ||
| 129 | extern volatile int session_done; | 126 | extern volatile int session_done; |
| 130 | 127 | ||
| 131 | #define session_done() (*(volatile int *)(&session_done)) | 128 | #define session_done() ACCESS_ONCE(session_done) |
| 132 | #endif /* __PERF_SESSION_H */ | 129 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 14e5a039bc45..9402885a77f3 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 = { |
| @@ -369,6 +373,9 @@ struct sort_entry sort_cpu = { | |||
| 369 | static int64_t | 373 | static int64_t |
| 370 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | 374 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) |
| 371 | { | 375 | { |
| 376 | if (!left->branch_info || !right->branch_info) | ||
| 377 | return cmp_null(left->branch_info, right->branch_info); | ||
| 378 | |||
| 372 | return _sort__dso_cmp(left->branch_info->from.map, | 379 | return _sort__dso_cmp(left->branch_info->from.map, |
| 373 | right->branch_info->from.map); | 380 | right->branch_info->from.map); |
| 374 | } | 381 | } |
| @@ -376,13 +383,19 @@ sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 376 | static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, | 383 | static int hist_entry__dso_from_snprintf(struct hist_entry *he, char *bf, |
| 377 | size_t size, unsigned int width) | 384 | size_t size, unsigned int width) |
| 378 | { | 385 | { |
| 379 | return _hist_entry__dso_snprintf(he->branch_info->from.map, | 386 | if (he->branch_info) |
| 380 | bf, size, width); | 387 | return _hist_entry__dso_snprintf(he->branch_info->from.map, |
| 388 | bf, size, width); | ||
| 389 | else | ||
| 390 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
| 381 | } | 391 | } |
| 382 | 392 | ||
| 383 | static int64_t | 393 | static int64_t |
| 384 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | 394 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) |
| 385 | { | 395 | { |
| 396 | if (!left->branch_info || !right->branch_info) | ||
| 397 | return cmp_null(left->branch_info, right->branch_info); | ||
| 398 | |||
| 386 | return _sort__dso_cmp(left->branch_info->to.map, | 399 | return _sort__dso_cmp(left->branch_info->to.map, |
| 387 | right->branch_info->to.map); | 400 | right->branch_info->to.map); |
| 388 | } | 401 | } |
| @@ -390,8 +403,11 @@ sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 390 | static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, | 403 | static int hist_entry__dso_to_snprintf(struct hist_entry *he, char *bf, |
| 391 | size_t size, unsigned int width) | 404 | size_t size, unsigned int width) |
| 392 | { | 405 | { |
| 393 | return _hist_entry__dso_snprintf(he->branch_info->to.map, | 406 | if (he->branch_info) |
| 394 | bf, size, width); | 407 | return _hist_entry__dso_snprintf(he->branch_info->to.map, |
| 408 | bf, size, width); | ||
| 409 | else | ||
| 410 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
| 395 | } | 411 | } |
| 396 | 412 | ||
| 397 | static int64_t | 413 | static int64_t |
| @@ -400,6 +416,12 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 400 | struct addr_map_symbol *from_l = &left->branch_info->from; | 416 | struct addr_map_symbol *from_l = &left->branch_info->from; |
| 401 | struct addr_map_symbol *from_r = &right->branch_info->from; | 417 | struct addr_map_symbol *from_r = &right->branch_info->from; |
| 402 | 418 | ||
| 419 | if (!left->branch_info || !right->branch_info) | ||
| 420 | return cmp_null(left->branch_info, right->branch_info); | ||
| 421 | |||
| 422 | from_l = &left->branch_info->from; | ||
| 423 | from_r = &right->branch_info->from; | ||
| 424 | |||
| 403 | if (!from_l->sym && !from_r->sym) | 425 | if (!from_l->sym && !from_r->sym) |
| 404 | return _sort__addr_cmp(from_l->addr, from_r->addr); | 426 | return _sort__addr_cmp(from_l->addr, from_r->addr); |
| 405 | 427 | ||
| @@ -409,8 +431,13 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 409 | static int64_t | 431 | static int64_t |
| 410 | sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | 432 | sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) |
| 411 | { | 433 | { |
| 412 | struct addr_map_symbol *to_l = &left->branch_info->to; | 434 | struct addr_map_symbol *to_l, *to_r; |
| 413 | struct addr_map_symbol *to_r = &right->branch_info->to; | 435 | |
| 436 | if (!left->branch_info || !right->branch_info) | ||
| 437 | return cmp_null(left->branch_info, right->branch_info); | ||
| 438 | |||
| 439 | to_l = &left->branch_info->to; | ||
| 440 | to_r = &right->branch_info->to; | ||
| 414 | 441 | ||
| 415 | if (!to_l->sym && !to_r->sym) | 442 | if (!to_l->sym && !to_r->sym) |
| 416 | return _sort__addr_cmp(to_l->addr, to_r->addr); | 443 | return _sort__addr_cmp(to_l->addr, to_r->addr); |
| @@ -421,19 +448,27 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 421 | static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, | 448 | static int hist_entry__sym_from_snprintf(struct hist_entry *he, char *bf, |
| 422 | size_t size, unsigned int width) | 449 | size_t size, unsigned int width) |
| 423 | { | 450 | { |
| 424 | struct addr_map_symbol *from = &he->branch_info->from; | 451 | if (he->branch_info) { |
| 425 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | 452 | struct addr_map_symbol *from = &he->branch_info->from; |
| 426 | he->level, bf, size, width); | ||
| 427 | 453 | ||
| 454 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | ||
| 455 | he->level, bf, size, width); | ||
| 456 | } | ||
| 457 | |||
| 458 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
| 428 | } | 459 | } |
| 429 | 460 | ||
| 430 | static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, | 461 | static int hist_entry__sym_to_snprintf(struct hist_entry *he, char *bf, |
| 431 | size_t size, unsigned int width) | 462 | size_t size, unsigned int width) |
| 432 | { | 463 | { |
| 433 | struct addr_map_symbol *to = &he->branch_info->to; | 464 | if (he->branch_info) { |
| 434 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | 465 | struct addr_map_symbol *to = &he->branch_info->to; |
| 435 | he->level, bf, size, width); | ||
| 436 | 466 | ||
| 467 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | ||
| 468 | he->level, bf, size, width); | ||
| 469 | } | ||
| 470 | |||
| 471 | return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A"); | ||
| 437 | } | 472 | } |
| 438 | 473 | ||
| 439 | struct sort_entry sort_dso_from = { | 474 | struct sort_entry sort_dso_from = { |
| @@ -467,11 +502,13 @@ struct sort_entry sort_sym_to = { | |||
| 467 | static int64_t | 502 | static int64_t |
| 468 | sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) | 503 | sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) |
| 469 | { | 504 | { |
| 470 | const unsigned char mp = left->branch_info->flags.mispred != | 505 | unsigned char mp, p; |
| 471 | right->branch_info->flags.mispred; | ||
| 472 | const unsigned char p = left->branch_info->flags.predicted != | ||
| 473 | right->branch_info->flags.predicted; | ||
| 474 | 506 | ||
| 507 | if (!left->branch_info || !right->branch_info) | ||
| 508 | return cmp_null(left->branch_info, right->branch_info); | ||
| 509 | |||
| 510 | mp = left->branch_info->flags.mispred != right->branch_info->flags.mispred; | ||
| 511 | p = left->branch_info->flags.predicted != right->branch_info->flags.predicted; | ||
| 475 | return mp || p; | 512 | return mp || p; |
| 476 | } | 513 | } |
| 477 | 514 | ||
| @@ -479,12 +516,14 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *he, char *bf, | |||
| 479 | size_t size, unsigned int width){ | 516 | size_t size, unsigned int width){ |
| 480 | static const char *out = "N/A"; | 517 | static const char *out = "N/A"; |
| 481 | 518 | ||
| 482 | if (he->branch_info->flags.predicted) | 519 | if (he->branch_info) { |
| 483 | out = "N"; | 520 | if (he->branch_info->flags.predicted) |
| 484 | else if (he->branch_info->flags.mispred) | 521 | out = "N"; |
| 485 | out = "Y"; | 522 | else if (he->branch_info->flags.mispred) |
| 523 | out = "Y"; | ||
| 524 | } | ||
| 486 | 525 | ||
| 487 | return repsep_snprintf(bf, size, "%-*s", width, out); | 526 | return repsep_snprintf(bf, size, "%-*.*s", width, width, out); |
| 488 | } | 527 | } |
| 489 | 528 | ||
| 490 | /* --sort daddr_sym */ | 529 | /* --sort daddr_sym */ |
| @@ -985,6 +1024,9 @@ struct sort_entry sort_mem_dcacheline = { | |||
| 985 | static int64_t | 1024 | static int64_t |
| 986 | sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) | 1025 | sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) |
| 987 | { | 1026 | { |
| 1027 | if (!left->branch_info || !right->branch_info) | ||
| 1028 | return cmp_null(left->branch_info, right->branch_info); | ||
| 1029 | |||
| 988 | return left->branch_info->flags.abort != | 1030 | return left->branch_info->flags.abort != |
| 989 | right->branch_info->flags.abort; | 1031 | right->branch_info->flags.abort; |
| 990 | } | 1032 | } |
| @@ -992,10 +1034,15 @@ sort__abort_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 992 | static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, | 1034 | static int hist_entry__abort_snprintf(struct hist_entry *he, char *bf, |
| 993 | size_t size, unsigned int width) | 1035 | size_t size, unsigned int width) |
| 994 | { | 1036 | { |
| 995 | static const char *out = "."; | 1037 | static const char *out = "N/A"; |
| 1038 | |||
| 1039 | if (he->branch_info) { | ||
| 1040 | if (he->branch_info->flags.abort) | ||
| 1041 | out = "A"; | ||
| 1042 | else | ||
| 1043 | out = "."; | ||
| 1044 | } | ||
| 996 | 1045 | ||
| 997 | if (he->branch_info->flags.abort) | ||
| 998 | out = "A"; | ||
| 999 | return repsep_snprintf(bf, size, "%-*s", width, out); | 1046 | return repsep_snprintf(bf, size, "%-*s", width, out); |
| 1000 | } | 1047 | } |
| 1001 | 1048 | ||
| @@ -1009,6 +1056,9 @@ struct sort_entry sort_abort = { | |||
| 1009 | static int64_t | 1056 | static int64_t |
| 1010 | sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) | 1057 | sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) |
| 1011 | { | 1058 | { |
| 1059 | if (!left->branch_info || !right->branch_info) | ||
| 1060 | return cmp_null(left->branch_info, right->branch_info); | ||
| 1061 | |||
| 1012 | return left->branch_info->flags.in_tx != | 1062 | return left->branch_info->flags.in_tx != |
| 1013 | right->branch_info->flags.in_tx; | 1063 | right->branch_info->flags.in_tx; |
| 1014 | } | 1064 | } |
| @@ -1016,10 +1066,14 @@ sort__in_tx_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 1016 | static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, | 1066 | static int hist_entry__in_tx_snprintf(struct hist_entry *he, char *bf, |
| 1017 | size_t size, unsigned int width) | 1067 | size_t size, unsigned int width) |
| 1018 | { | 1068 | { |
| 1019 | static const char *out = "."; | 1069 | static const char *out = "N/A"; |
| 1020 | 1070 | ||
| 1021 | if (he->branch_info->flags.in_tx) | 1071 | if (he->branch_info) { |
| 1022 | out = "T"; | 1072 | if (he->branch_info->flags.in_tx) |
| 1073 | out = "T"; | ||
| 1074 | else | ||
| 1075 | out = "."; | ||
| 1076 | } | ||
| 1023 | 1077 | ||
| 1024 | return repsep_snprintf(bf, size, "%-*s", width, out); | 1078 | return repsep_snprintf(bf, size, "%-*s", width, out); |
| 1025 | } | 1079 | } |
| @@ -1194,7 +1248,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; | 1248 | return hse_a->se == hse_b->se; |
| 1195 | } | 1249 | } |
| 1196 | 1250 | ||
| 1197 | void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | 1251 | void perf_hpp__reset_sort_width(struct perf_hpp_fmt *fmt, struct hists *hists) |
| 1198 | { | 1252 | { |
| 1199 | struct hpp_sort_entry *hse; | 1253 | struct hpp_sort_entry *hse; |
| 1200 | 1254 | ||
| @@ -1202,20 +1256,21 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) | |||
| 1202 | return; | 1256 | return; |
| 1203 | 1257 | ||
| 1204 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1258 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
| 1205 | hists__new_col_len(hists, hse->se->se_width_idx, | 1259 | hists__new_col_len(hists, hse->se->se_width_idx, strlen(fmt->name)); |
| 1206 | strlen(hse->se->se_header)); | ||
| 1207 | } | 1260 | } |
| 1208 | 1261 | ||
| 1209 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 1262 | static int __sort__hpp_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 1210 | struct perf_evsel *evsel) | 1263 | struct perf_evsel *evsel) |
| 1211 | { | 1264 | { |
| 1212 | struct hpp_sort_entry *hse; | 1265 | struct hpp_sort_entry *hse; |
| 1213 | size_t len; | 1266 | size_t len = fmt->user_len; |
| 1214 | 1267 | ||
| 1215 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1268 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
| 1216 | len = hists__col_len(&evsel->hists, hse->se->se_width_idx); | ||
| 1217 | 1269 | ||
| 1218 | return scnprintf(hpp->buf, hpp->size, "%-*s", len, hse->se->se_header); | 1270 | if (!len) |
| 1271 | len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); | ||
| 1272 | |||
| 1273 | return scnprintf(hpp->buf, hpp->size, "%-*.*s", len, len, fmt->name); | ||
| 1219 | } | 1274 | } |
| 1220 | 1275 | ||
| 1221 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | 1276 | static int __sort__hpp_width(struct perf_hpp_fmt *fmt, |
| @@ -1223,20 +1278,26 @@ static int __sort__hpp_width(struct perf_hpp_fmt *fmt, | |||
| 1223 | struct perf_evsel *evsel) | 1278 | struct perf_evsel *evsel) |
| 1224 | { | 1279 | { |
| 1225 | struct hpp_sort_entry *hse; | 1280 | struct hpp_sort_entry *hse; |
| 1281 | size_t len = fmt->user_len; | ||
| 1226 | 1282 | ||
| 1227 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1283 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
| 1228 | 1284 | ||
| 1229 | return hists__col_len(&evsel->hists, hse->se->se_width_idx); | 1285 | if (!len) |
| 1286 | len = hists__col_len(evsel__hists(evsel), hse->se->se_width_idx); | ||
| 1287 | |||
| 1288 | return len; | ||
| 1230 | } | 1289 | } |
| 1231 | 1290 | ||
| 1232 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | 1291 | static int __sort__hpp_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
| 1233 | struct hist_entry *he) | 1292 | struct hist_entry *he) |
| 1234 | { | 1293 | { |
| 1235 | struct hpp_sort_entry *hse; | 1294 | struct hpp_sort_entry *hse; |
| 1236 | size_t len; | 1295 | size_t len = fmt->user_len; |
| 1237 | 1296 | ||
| 1238 | hse = container_of(fmt, struct hpp_sort_entry, hpp); | 1297 | hse = container_of(fmt, struct hpp_sort_entry, hpp); |
| 1239 | len = hists__col_len(he->hists, hse->se->se_width_idx); | 1298 | |
| 1299 | if (!len) | ||
| 1300 | len = hists__col_len(he->hists, hse->se->se_width_idx); | ||
| 1240 | 1301 | ||
| 1241 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); | 1302 | return hse->se->se_snprintf(he, hpp->buf, hpp->size, len); |
| 1242 | } | 1303 | } |
| @@ -1253,6 +1314,7 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
| 1253 | } | 1314 | } |
| 1254 | 1315 | ||
| 1255 | hse->se = sd->entry; | 1316 | hse->se = sd->entry; |
| 1317 | hse->hpp.name = sd->entry->se_header; | ||
| 1256 | hse->hpp.header = __sort__hpp_header; | 1318 | hse->hpp.header = __sort__hpp_header; |
| 1257 | hse->hpp.width = __sort__hpp_width; | 1319 | hse->hpp.width = __sort__hpp_width; |
| 1258 | hse->hpp.entry = __sort__hpp_entry; | 1320 | hse->hpp.entry = __sort__hpp_entry; |
| @@ -1265,6 +1327,8 @@ __sort_dimension__alloc_hpp(struct sort_dimension *sd) | |||
| 1265 | INIT_LIST_HEAD(&hse->hpp.list); | 1327 | INIT_LIST_HEAD(&hse->hpp.list); |
| 1266 | INIT_LIST_HEAD(&hse->hpp.sort_list); | 1328 | INIT_LIST_HEAD(&hse->hpp.sort_list); |
| 1267 | hse->hpp.elide = false; | 1329 | hse->hpp.elide = false; |
| 1330 | hse->hpp.len = 0; | ||
| 1331 | hse->hpp.user_len = 0; | ||
| 1268 | 1332 | ||
| 1269 | return hse; | 1333 | return hse; |
| 1270 | } | 1334 | } |
| @@ -1432,14 +1496,49 @@ static const char *get_default_sort_order(void) | |||
| 1432 | return default_sort_orders[sort__mode]; | 1496 | return default_sort_orders[sort__mode]; |
| 1433 | } | 1497 | } |
| 1434 | 1498 | ||
| 1499 | static int setup_sort_order(void) | ||
| 1500 | { | ||
| 1501 | char *new_sort_order; | ||
| 1502 | |||
| 1503 | /* | ||
| 1504 | * Append '+'-prefixed sort order to the default sort | ||
| 1505 | * order string. | ||
| 1506 | */ | ||
| 1507 | if (!sort_order || is_strict_order(sort_order)) | ||
| 1508 | return 0; | ||
| 1509 | |||
| 1510 | if (sort_order[1] == '\0') { | ||
| 1511 | error("Invalid --sort key: `+'"); | ||
| 1512 | return -EINVAL; | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | /* | ||
| 1516 | * We allocate new sort_order string, but we never free it, | ||
| 1517 | * because it's checked over the rest of the code. | ||
| 1518 | */ | ||
| 1519 | if (asprintf(&new_sort_order, "%s,%s", | ||
| 1520 | get_default_sort_order(), sort_order + 1) < 0) { | ||
| 1521 | error("Not enough memory to set up --sort"); | ||
| 1522 | return -ENOMEM; | ||
| 1523 | } | ||
| 1524 | |||
| 1525 | sort_order = new_sort_order; | ||
| 1526 | return 0; | ||
| 1527 | } | ||
| 1528 | |||
| 1435 | static int __setup_sorting(void) | 1529 | static int __setup_sorting(void) |
| 1436 | { | 1530 | { |
| 1437 | char *tmp, *tok, *str; | 1531 | char *tmp, *tok, *str; |
| 1438 | const char *sort_keys = sort_order; | 1532 | const char *sort_keys; |
| 1439 | int ret = 0; | 1533 | int ret = 0; |
| 1440 | 1534 | ||
| 1535 | ret = setup_sort_order(); | ||
| 1536 | if (ret) | ||
| 1537 | return ret; | ||
| 1538 | |||
| 1539 | sort_keys = sort_order; | ||
| 1441 | if (sort_keys == NULL) { | 1540 | if (sort_keys == NULL) { |
| 1442 | if (field_order) { | 1541 | if (is_strict_order(field_order)) { |
| 1443 | /* | 1542 | /* |
| 1444 | * If user specified field order but no sort order, | 1543 | * If user specified field order but no sort order, |
| 1445 | * we'll honor it and not add default sort orders. | 1544 | * we'll honor it and not add default sort orders. |
| @@ -1625,23 +1724,36 @@ static void reset_dimensions(void) | |||
| 1625 | memory_sort_dimensions[i].taken = 0; | 1724 | memory_sort_dimensions[i].taken = 0; |
| 1626 | } | 1725 | } |
| 1627 | 1726 | ||
| 1727 | bool is_strict_order(const char *order) | ||
| 1728 | { | ||
| 1729 | return order && (*order != '+'); | ||
| 1730 | } | ||
| 1731 | |||
| 1628 | static int __setup_output_field(void) | 1732 | static int __setup_output_field(void) |
| 1629 | { | 1733 | { |
| 1630 | char *tmp, *tok, *str; | 1734 | char *tmp, *tok, *str, *strp; |
| 1631 | int ret = 0; | 1735 | int ret = -EINVAL; |
| 1632 | 1736 | ||
| 1633 | if (field_order == NULL) | 1737 | if (field_order == NULL) |
| 1634 | return 0; | 1738 | return 0; |
| 1635 | 1739 | ||
| 1636 | reset_dimensions(); | 1740 | reset_dimensions(); |
| 1637 | 1741 | ||
| 1638 | str = strdup(field_order); | 1742 | strp = str = strdup(field_order); |
| 1639 | if (str == NULL) { | 1743 | if (str == NULL) { |
| 1640 | error("Not enough memory to setup output fields"); | 1744 | error("Not enough memory to setup output fields"); |
| 1641 | return -ENOMEM; | 1745 | return -ENOMEM; |
| 1642 | } | 1746 | } |
| 1643 | 1747 | ||
| 1644 | for (tok = strtok_r(str, ", ", &tmp); | 1748 | if (!is_strict_order(field_order)) |
| 1749 | strp++; | ||
| 1750 | |||
| 1751 | if (!strlen(strp)) { | ||
| 1752 | error("Invalid --fields key: `+'"); | ||
| 1753 | goto out; | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | for (tok = strtok_r(strp, ", ", &tmp); | ||
| 1645 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 1757 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
| 1646 | ret = output_field_add(tok); | 1758 | ret = output_field_add(tok); |
| 1647 | if (ret == -EINVAL) { | 1759 | if (ret == -EINVAL) { |
| @@ -1653,6 +1765,7 @@ static int __setup_output_field(void) | |||
| 1653 | } | 1765 | } |
| 1654 | } | 1766 | } |
| 1655 | 1767 | ||
| 1768 | out: | ||
| 1656 | free(str); | 1769 | free(str); |
| 1657 | return ret; | 1770 | return ret; |
| 1658 | } | 1771 | } |
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..6afd6106ceb5 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; | ||
| 42 | mega: | ||
| 43 | unit = K * K; | ||
| 44 | break; | ||
| 45 | case 'G': | ||
| 46 | if (str[i + 1] != 'B') | ||
| 47 | goto out_err; | 23 | goto out_err; |
| 48 | else | 24 | case '\0': |
| 49 | goto giga; | 25 | return length; |
| 50 | case 'g': | 26 | default: |
| 51 | if (str[i + 1] != 'b') | 27 | goto out_err; |
| 52 | goto out_err; | 28 | /* two-letter suffices */ |
| 53 | giga: | 29 | case 'k': case 'K': |
| 54 | unit = K * K * K; | 30 | length <<= 10; |
| 55 | break; | 31 | break; |
| 56 | case 'T': | 32 | case 'm': case 'M': |
| 57 | if (str[i + 1] != 'B') | 33 | length <<= 20; |
| 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; | 34 | break; |
| 67 | case '\0': /* only specified figures */ | 35 | case 'g': case 'G': |
| 68 | unit = 1; | 36 | length <<= 30; |
| 69 | break; | 37 | break; |
| 70 | default: | 38 | case 't': case 'T': |
| 71 | if (!isdigit(str[i])) | 39 | length <<= 40; |
| 72 | goto out_err; | ||
| 73 | break; | 40 | 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 | /* |
| @@ -387,27 +357,3 @@ void *memdup(const void *src, size_t len) | |||
| 387 | 357 | ||
| 388 | return p; | 358 | return p; |
| 389 | } | 359 | } |
| 390 | |||
| 391 | /** | ||
| 392 | * str_append - reallocate string and append another | ||
| 393 | * @s: pointer to string pointer | ||
| 394 | * @len: pointer to len (initialized) | ||
| 395 | * @a: string to append. | ||
| 396 | */ | ||
| 397 | int str_append(char **s, int *len, const char *a) | ||
| 398 | { | ||
| 399 | int olen = *s ? strlen(*s) : 0; | ||
| 400 | int nlen = olen + strlen(a) + 1; | ||
| 401 | if (*len < nlen) { | ||
| 402 | *len = *len * 2; | ||
| 403 | if (*len < nlen) | ||
| 404 | *len = nlen; | ||
| 405 | *s = realloc(*s, *len); | ||
| 406 | if (!*s) | ||
| 407 | return -ENOMEM; | ||
| 408 | if (olen == 0) | ||
| 409 | **s = 0; | ||
| 410 | } | ||
| 411 | strcat(*s, a); | ||
| 412 | return 0; | ||
| 413 | } | ||
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..078331140d8c 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 = "", |
| @@ -184,7 +186,7 @@ void symbols__fixup_end(struct rb_root *symbols) | |||
| 184 | curr = rb_entry(nd, struct symbol, rb_node); | 186 | curr = rb_entry(nd, struct symbol, rb_node); |
| 185 | 187 | ||
| 186 | if (prev->end == prev->start && prev->end != curr->start) | 188 | if (prev->end == prev->start && prev->end != curr->start) |
| 187 | prev->end = curr->start - 1; | 189 | prev->end = curr->start; |
| 188 | } | 190 | } |
| 189 | 191 | ||
| 190 | /* Last entry */ | 192 | /* Last entry */ |
| @@ -205,7 +207,7 @@ void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | |||
| 205 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | 207 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
| 206 | prev = curr; | 208 | prev = curr; |
| 207 | curr = rb_entry(nd, struct map, rb_node); | 209 | curr = rb_entry(nd, struct map, rb_node); |
| 208 | prev->end = curr->start - 1; | 210 | prev->end = curr->start; |
| 209 | } | 211 | } |
| 210 | 212 | ||
| 211 | /* | 213 | /* |
| @@ -227,7 +229,7 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) | |||
| 227 | sym = ((void *)sym) + symbol_conf.priv_size; | 229 | sym = ((void *)sym) + symbol_conf.priv_size; |
| 228 | 230 | ||
| 229 | sym->start = start; | 231 | sym->start = start; |
| 230 | sym->end = len ? start + len - 1 : start; | 232 | sym->end = len ? start + len : start; |
| 231 | sym->binding = binding; | 233 | sym->binding = binding; |
| 232 | sym->namelen = namelen - 1; | 234 | sym->namelen = namelen - 1; |
| 233 | 235 | ||
| @@ -323,7 +325,7 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) | |||
| 323 | 325 | ||
| 324 | if (ip < s->start) | 326 | if (ip < s->start) |
| 325 | n = n->rb_left; | 327 | n = n->rb_left; |
| 326 | else if (ip > s->end) | 328 | else if (ip >= s->end) |
| 327 | n = n->rb_right; | 329 | n = n->rb_right; |
| 328 | else | 330 | else |
| 329 | return s; | 331 | return s; |
| @@ -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..eb2c19bf8d90 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 |
| @@ -93,7 +95,7 @@ void symbols__delete(struct rb_root *symbols); | |||
| 93 | 95 | ||
| 94 | static inline size_t symbol__size(const struct symbol *sym) | 96 | static inline size_t symbol__size(const struct symbol *sym) |
| 95 | { | 97 | { |
| 96 | return sym->end - sym->start + 1; | 98 | return sym->end - sym->start; |
| 97 | } | 99 | } |
| 98 | 100 | ||
| 99 | struct strlist; | 101 | struct strlist; |
| @@ -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..c41411726c7a 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "util.h" | 7 | #include "util.h" |
| 8 | #include "debug.h" | 8 | #include "debug.h" |
| 9 | #include "comm.h" | 9 | #include "comm.h" |
| 10 | #include "unwind.h" | ||
| 10 | 11 | ||
| 11 | int thread__init_map_groups(struct thread *thread, struct machine *machine) | 12 | int thread__init_map_groups(struct thread *thread, struct machine *machine) |
| 12 | { | 13 | { |
| @@ -37,17 +38,21 @@ struct thread *thread__new(pid_t pid, pid_t tid) | |||
| 37 | thread->cpu = -1; | 38 | thread->cpu = -1; |
| 38 | INIT_LIST_HEAD(&thread->comm_list); | 39 | INIT_LIST_HEAD(&thread->comm_list); |
| 39 | 40 | ||
| 41 | if (unwind__prepare_access(thread) < 0) | ||
| 42 | goto err_thread; | ||
| 43 | |||
| 40 | comm_str = malloc(32); | 44 | comm_str = malloc(32); |
| 41 | if (!comm_str) | 45 | if (!comm_str) |
| 42 | goto err_thread; | 46 | goto err_thread; |
| 43 | 47 | ||
| 44 | snprintf(comm_str, 32, ":%d", tid); | 48 | snprintf(comm_str, 32, ":%d", tid); |
| 45 | comm = comm__new(comm_str, 0); | 49 | comm = comm__new(comm_str, 0, false); |
| 46 | free(comm_str); | 50 | free(comm_str); |
| 47 | if (!comm) | 51 | if (!comm) |
| 48 | goto err_thread; | 52 | goto err_thread; |
| 49 | 53 | ||
| 50 | list_add(&comm->list, &thread->comm_list); | 54 | list_add(&comm->list, &thread->comm_list); |
| 55 | |||
| 51 | } | 56 | } |
| 52 | 57 | ||
| 53 | return thread; | 58 | return thread; |
| @@ -69,6 +74,7 @@ void thread__delete(struct thread *thread) | |||
| 69 | list_del(&comm->list); | 74 | list_del(&comm->list); |
| 70 | comm__free(comm); | 75 | comm__free(comm); |
| 71 | } | 76 | } |
| 77 | unwind__finish_access(thread); | ||
| 72 | 78 | ||
| 73 | free(thread); | 79 | free(thread); |
| 74 | } | 80 | } |
| @@ -81,22 +87,39 @@ struct comm *thread__comm(const struct thread *thread) | |||
| 81 | return list_first_entry(&thread->comm_list, struct comm, list); | 87 | return list_first_entry(&thread->comm_list, struct comm, list); |
| 82 | } | 88 | } |
| 83 | 89 | ||
| 90 | struct comm *thread__exec_comm(const struct thread *thread) | ||
| 91 | { | ||
| 92 | struct comm *comm, *last = NULL; | ||
| 93 | |||
| 94 | list_for_each_entry(comm, &thread->comm_list, list) { | ||
| 95 | if (comm->exec) | ||
| 96 | return comm; | ||
| 97 | last = comm; | ||
| 98 | } | ||
| 99 | |||
| 100 | return last; | ||
| 101 | } | ||
| 102 | |||
| 84 | /* CHECKME: time should always be 0 if event aren't ordered */ | 103 | /* CHECKME: time should always be 0 if event aren't ordered */ |
| 85 | int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) | 104 | int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, |
| 105 | bool exec) | ||
| 86 | { | 106 | { |
| 87 | struct comm *new, *curr = thread__comm(thread); | 107 | struct comm *new, *curr = thread__comm(thread); |
| 88 | int err; | 108 | int err; |
| 89 | 109 | ||
| 90 | /* Override latest entry if it had no specific time coverage */ | 110 | /* Override latest entry if it had no specific time coverage */ |
| 91 | if (!curr->start) { | 111 | if (!curr->start && !curr->exec) { |
| 92 | err = comm__override(curr, str, timestamp); | 112 | err = comm__override(curr, str, timestamp, exec); |
| 93 | if (err) | 113 | if (err) |
| 94 | return err; | 114 | return err; |
| 95 | } else { | 115 | } else { |
| 96 | new = comm__new(str, timestamp); | 116 | new = comm__new(str, timestamp, exec); |
| 97 | if (!new) | 117 | if (!new) |
| 98 | return -ENOMEM; | 118 | return -ENOMEM; |
| 99 | list_add(&new->list, &thread->comm_list); | 119 | list_add(&new->list, &thread->comm_list); |
| 120 | |||
| 121 | if (exec) | ||
| 122 | unwind__flush_access(thread); | ||
| 100 | } | 123 | } |
| 101 | 124 | ||
| 102 | thread->comm_set = true; | 125 | thread->comm_set = true; |
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/thread_map.c b/tools/perf/util/thread_map.c index 5d3215912105..f93b9734735b 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c | |||
| @@ -214,6 +214,17 @@ out_free_threads: | |||
| 214 | goto out; | 214 | goto out; |
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | struct thread_map *thread_map__new_dummy(void) | ||
| 218 | { | ||
| 219 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
| 220 | |||
| 221 | if (threads != NULL) { | ||
| 222 | threads->map[0] = -1; | ||
| 223 | threads->nr = 1; | ||
| 224 | } | ||
| 225 | return threads; | ||
| 226 | } | ||
| 227 | |||
| 217 | static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) | 228 | static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) |
| 218 | { | 229 | { |
| 219 | struct thread_map *threads = NULL, *nt; | 230 | struct thread_map *threads = NULL, *nt; |
| @@ -224,14 +235,8 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) | |||
| 224 | struct strlist *slist; | 235 | struct strlist *slist; |
| 225 | 236 | ||
| 226 | /* perf-stat expects threads to be generated even if tid not given */ | 237 | /* perf-stat expects threads to be generated even if tid not given */ |
| 227 | if (!tid_str) { | 238 | if (!tid_str) |
| 228 | threads = malloc(sizeof(*threads) + sizeof(pid_t)); | 239 | return thread_map__new_dummy(); |
| 229 | if (threads != NULL) { | ||
| 230 | threads->map[0] = -1; | ||
| 231 | threads->nr = 1; | ||
| 232 | } | ||
| 233 | return threads; | ||
| 234 | } | ||
| 235 | 240 | ||
| 236 | slist = strlist__new(false, tid_str); | 241 | slist = strlist__new(false, tid_str); |
| 237 | if (!slist) | 242 | if (!slist) |
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 0cd8b3108084..95313f43cc0f 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
| @@ -9,6 +9,7 @@ struct thread_map { | |||
| 9 | pid_t map[]; | 9 | pid_t map[]; |
| 10 | }; | 10 | }; |
| 11 | 11 | ||
| 12 | struct thread_map *thread_map__new_dummy(void); | ||
| 12 | struct thread_map *thread_map__new_by_pid(pid_t pid); | 13 | struct thread_map *thread_map__new_by_pid(pid_t pid); |
| 13 | struct thread_map *thread_map__new_by_tid(pid_t tid); | 14 | struct thread_map *thread_map__new_by_tid(pid_t tid); |
| 14 | struct thread_map *thread_map__new_by_uid(uid_t uid); | 15 | struct thread_map *thread_map__new_by_uid(uid_t uid); |
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/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 92b56db52471..4d45c0dfe343 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include <linux/list.h> | 24 | #include <linux/list.h> |
| 25 | #include <libunwind.h> | 25 | #include <libunwind.h> |
| 26 | #include <libunwind-ptrace.h> | 26 | #include <libunwind-ptrace.h> |
| 27 | #include "callchain.h" | ||
| 27 | #include "thread.h" | 28 | #include "thread.h" |
| 28 | #include "session.h" | 29 | #include "session.h" |
| 29 | #include "perf_regs.h" | 30 | #include "perf_regs.h" |
| @@ -525,12 +526,12 @@ static unw_accessors_t accessors = { | |||
| 525 | .get_proc_name = get_proc_name, | 526 | .get_proc_name = get_proc_name, |
| 526 | }; | 527 | }; |
| 527 | 528 | ||
| 528 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | 529 | int unwind__prepare_access(struct thread *thread) |
| 529 | void *arg, int max_stack) | ||
| 530 | { | 530 | { |
| 531 | unw_addr_space_t addr_space; | 531 | unw_addr_space_t addr_space; |
| 532 | unw_cursor_t c; | 532 | |
| 533 | int ret; | 533 | if (callchain_param.record_mode != CALLCHAIN_DWARF) |
| 534 | return 0; | ||
| 534 | 535 | ||
| 535 | addr_space = unw_create_addr_space(&accessors, 0); | 536 | addr_space = unw_create_addr_space(&accessors, 0); |
| 536 | if (!addr_space) { | 537 | if (!addr_space) { |
| @@ -538,6 +539,45 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
| 538 | return -ENOMEM; | 539 | return -ENOMEM; |
| 539 | } | 540 | } |
| 540 | 541 | ||
| 542 | unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); | ||
| 543 | thread__set_priv(thread, addr_space); | ||
| 544 | |||
| 545 | return 0; | ||
| 546 | } | ||
| 547 | |||
| 548 | void unwind__flush_access(struct thread *thread) | ||
| 549 | { | ||
| 550 | unw_addr_space_t addr_space; | ||
| 551 | |||
| 552 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | ||
| 553 | return; | ||
| 554 | |||
| 555 | addr_space = thread__priv(thread); | ||
| 556 | unw_flush_cache(addr_space, 0, 0); | ||
| 557 | } | ||
| 558 | |||
| 559 | void unwind__finish_access(struct thread *thread) | ||
| 560 | { | ||
| 561 | unw_addr_space_t addr_space; | ||
| 562 | |||
| 563 | if (callchain_param.record_mode != CALLCHAIN_DWARF) | ||
| 564 | return; | ||
| 565 | |||
| 566 | addr_space = thread__priv(thread); | ||
| 567 | unw_destroy_addr_space(addr_space); | ||
| 568 | } | ||
| 569 | |||
| 570 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | ||
| 571 | void *arg, int max_stack) | ||
| 572 | { | ||
| 573 | unw_addr_space_t addr_space; | ||
| 574 | unw_cursor_t c; | ||
| 575 | int ret; | ||
| 576 | |||
| 577 | addr_space = thread__priv(ui->thread); | ||
| 578 | if (addr_space == NULL) | ||
| 579 | return -1; | ||
| 580 | |||
| 541 | ret = unw_init_remote(&c, addr_space, ui); | 581 | ret = unw_init_remote(&c, addr_space, ui); |
| 542 | if (ret) | 582 | if (ret) |
| 543 | display_error(ret); | 583 | display_error(ret); |
| @@ -549,7 +589,6 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
| 549 | ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0; | 589 | ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0; |
| 550 | } | 590 | } |
| 551 | 591 | ||
| 552 | unw_destroy_addr_space(addr_space); | ||
| 553 | return ret; | 592 | return ret; |
| 554 | } | 593 | } |
| 555 | 594 | ||
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index f03061260b4e..f50b737235eb 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | #include "event.h" | 5 | #include "event.h" |
| 6 | #include "symbol.h" | 6 | #include "symbol.h" |
| 7 | #include "thread.h" | ||
| 7 | 8 | ||
| 8 | struct unwind_entry { | 9 | struct unwind_entry { |
| 9 | struct map *map; | 10 | struct map *map; |
| @@ -21,6 +22,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
| 21 | /* libunwind specific */ | 22 | /* libunwind specific */ |
| 22 | #ifdef HAVE_LIBUNWIND_SUPPORT | 23 | #ifdef HAVE_LIBUNWIND_SUPPORT |
| 23 | int libunwind__arch_reg_id(int regnum); | 24 | int libunwind__arch_reg_id(int regnum); |
| 25 | int unwind__prepare_access(struct thread *thread); | ||
| 26 | void unwind__flush_access(struct thread *thread); | ||
| 27 | void unwind__finish_access(struct thread *thread); | ||
| 28 | #else | ||
| 29 | static inline int unwind__prepare_access(struct thread *thread __maybe_unused) | ||
| 30 | { | ||
| 31 | return 0; | ||
| 32 | } | ||
| 33 | |||
| 34 | static inline void unwind__flush_access(struct thread *thread __maybe_unused) {} | ||
| 35 | static inline void unwind__finish_access(struct thread *thread __maybe_unused) {} | ||
| 24 | #endif | 36 | #endif |
| 25 | #else | 37 | #else |
| 26 | static inline int | 38 | static inline int |
| @@ -33,5 +45,13 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, | |||
| 33 | { | 45 | { |
| 34 | return 0; | 46 | return 0; |
| 35 | } | 47 | } |
| 48 | |||
| 49 | static inline int unwind__prepare_access(struct thread *thread __maybe_unused) | ||
| 50 | { | ||
| 51 | return 0; | ||
| 52 | } | ||
| 53 | |||
| 54 | static inline void unwind__flush_access(struct thread *thread __maybe_unused) {} | ||
| 55 | static inline void unwind__finish_access(struct thread *thread __maybe_unused) {} | ||
| 36 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ | 56 | #endif /* HAVE_DWARF_UNWIND_SUPPORT */ |
| 37 | #endif /* __UNWIND_H */ | 57 | #endif /* __UNWIND_H */ |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index e52e7461911b..d5eab3f3323f 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
| @@ -13,6 +13,15 @@ | |||
| 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> | ||
| 17 | #include "callchain.h" | ||
| 18 | |||
| 19 | struct callchain_param callchain_param = { | ||
| 20 | .mode = CHAIN_GRAPH_REL, | ||
| 21 | .min_percent = 0.5, | ||
| 22 | .order = ORDER_CALLEE, | ||
| 23 | .key = CCKEY_FUNCTION | ||
| 24 | }; | ||
| 16 | 25 | ||
| 17 | /* | 26 | /* |
| 18 | * XXX We need to find a better place for these things... | 27 | * XXX We need to find a better place for these things... |
| @@ -282,6 +291,18 @@ void get_term_dimensions(struct winsize *ws) | |||
| 282 | ws->ws_col = 80; | 291 | ws->ws_col = 80; |
| 283 | } | 292 | } |
| 284 | 293 | ||
| 294 | void set_term_quiet_input(struct termios *old) | ||
| 295 | { | ||
| 296 | struct termios tc; | ||
| 297 | |||
| 298 | tcgetattr(0, old); | ||
| 299 | tc = *old; | ||
| 300 | tc.c_lflag &= ~(ICANON | ECHO); | ||
| 301 | tc.c_cc[VMIN] = 0; | ||
| 302 | tc.c_cc[VTIME] = 0; | ||
| 303 | tcsetattr(0, TCSANOW, &tc); | ||
| 304 | } | ||
| 305 | |||
| 285 | static void set_tracing_events_path(const char *mountpoint) | 306 | static void set_tracing_events_path(const char *mountpoint) |
| 286 | { | 307 | { |
| 287 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | 308 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", |
| @@ -443,6 +464,7 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep) | |||
| 443 | size_t size = 0, alloc_size = 0; | 464 | size_t size = 0, alloc_size = 0; |
| 444 | void *bf = NULL, *nbf; | 465 | void *bf = NULL, *nbf; |
| 445 | int fd, n, err = 0; | 466 | int fd, n, err = 0; |
| 467 | char sbuf[STRERR_BUFSIZE]; | ||
| 446 | 468 | ||
| 447 | fd = open(filename, O_RDONLY); | 469 | fd = open(filename, O_RDONLY); |
| 448 | if (fd < 0) | 470 | if (fd < 0) |
| @@ -463,8 +485,8 @@ int filename__read_str(const char *filename, char **buf, size_t *sizep) | |||
| 463 | n = read(fd, bf + size, alloc_size - size); | 485 | n = read(fd, bf + size, alloc_size - size); |
| 464 | if (n < 0) { | 486 | if (n < 0) { |
| 465 | if (size) { | 487 | if (size) { |
| 466 | pr_warning("read failed %d: %s\n", | 488 | pr_warning("read failed %d: %s\n", errno, |
| 467 | errno, strerror(errno)); | 489 | strerror_r(errno, sbuf, sizeof(sbuf))); |
| 468 | err = 0; | 490 | err = 0; |
| 469 | } else | 491 | } else |
| 470 | err = -errno; | 492 | err = -errno; |
| @@ -536,3 +558,39 @@ void mem_bswap_64(void *src, int byte_size) | |||
| 536 | ++m; | 558 | ++m; |
| 537 | } | 559 | } |
| 538 | } | 560 | } |
| 561 | |||
| 562 | bool find_process(const char *name) | ||
| 563 | { | ||
| 564 | size_t len = strlen(name); | ||
| 565 | DIR *dir; | ||
| 566 | struct dirent *d; | ||
| 567 | int ret = -1; | ||
| 568 | |||
| 569 | dir = opendir(procfs__mountpoint()); | ||
| 570 | if (!dir) | ||
| 571 | return -1; | ||
| 572 | |||
| 573 | /* Walk through the directory. */ | ||
| 574 | while (ret && (d = readdir(dir)) != NULL) { | ||
| 575 | char path[PATH_MAX]; | ||
| 576 | char *data; | ||
| 577 | size_t size; | ||
| 578 | |||
| 579 | if ((d->d_type != DT_DIR) || | ||
| 580 | !strcmp(".", d->d_name) || | ||
| 581 | !strcmp("..", d->d_name)) | ||
| 582 | continue; | ||
| 583 | |||
| 584 | scnprintf(path, sizeof(path), "%s/%s/comm", | ||
| 585 | procfs__mountpoint(), d->d_name); | ||
| 586 | |||
| 587 | if (filename__read_str(path, &data, &size)) | ||
| 588 | continue; | ||
| 589 | |||
| 590 | ret = strncmp(name, data, len); | ||
| 591 | free(data); | ||
| 592 | } | ||
| 593 | |||
| 594 | closedir(dir); | ||
| 595 | return ret ? false : true; | ||
| 596 | } | ||
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/power/acpi/os_specific/service_layers/osunixxf.c b/tools/power/acpi/os_specific/service_layers/osunixxf.c index 60b58cd18410..7ccb073f8316 100644 --- a/tools/power/acpi/os_specific/service_layers/osunixxf.c +++ b/tools/power/acpi/os_specific/service_layers/osunixxf.c | |||
| @@ -122,6 +122,14 @@ static void os_enter_line_edit_mode(void) | |||
| 122 | { | 122 | { |
| 123 | struct termios local_term_attributes; | 123 | struct termios local_term_attributes; |
| 124 | 124 | ||
| 125 | term_attributes_were_set = 0; | ||
| 126 | |||
| 127 | /* STDIN must be a terminal */ | ||
| 128 | |||
| 129 | if (!isatty(STDIN_FILENO)) { | ||
| 130 | return; | ||
| 131 | } | ||
| 132 | |||
| 125 | /* Get and keep the original attributes */ | 133 | /* Get and keep the original attributes */ |
| 126 | 134 | ||
| 127 | if (tcgetattr(STDIN_FILENO, &original_term_attributes)) { | 135 | if (tcgetattr(STDIN_FILENO, &original_term_attributes)) { |
diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c index 53cee781e24e..24d32968802d 100644 --- a/tools/power/acpi/tools/acpidump/apdump.c +++ b/tools/power/acpi/tools/acpidump/apdump.c | |||
| @@ -146,7 +146,7 @@ u32 ap_get_table_length(struct acpi_table_header *table) | |||
| 146 | 146 | ||
| 147 | if (ACPI_VALIDATE_RSDP_SIG(table->signature)) { | 147 | if (ACPI_VALIDATE_RSDP_SIG(table->signature)) { |
| 148 | rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table); | 148 | rsdp = ACPI_CAST_PTR(struct acpi_table_rsdp, table); |
| 149 | return (rsdp->length); | 149 | return (acpi_tb_get_rsdp_length(rsdp)); |
| 150 | } | 150 | } |
| 151 | 151 | ||
| 152 | /* Normal ACPI table */ | 152 | /* Normal ACPI table */ |
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index d0396af99fa0..5b1b807265a1 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c | |||
| @@ -267,90 +267,90 @@ int get_msr(int cpu, off_t offset, unsigned long long *msr) | |||
| 267 | /* | 267 | /* |
| 268 | * Example Format w/ field column widths: | 268 | * Example Format w/ field column widths: |
| 269 | * | 269 | * |
| 270 | * Package Core CPU Avg_MHz Bzy_MHz TSC_MHz SMI %Busy CPU_%c1 CPU_%c3 CPU_%c6 CPU_%c7 CoreTmp PkgTmp Pkg%pc2 Pkg%pc3 Pkg%pc6 Pkg%pc7 PkgWatt CorWatt GFXWatt | 270 | * Package Core CPU Avg_MHz Bzy_MHz TSC_MHz SMI %Busy CPU_%c1 CPU_%c3 CPU_%c6 CPU_%c7 CoreTmp PkgTmp Pkg%pc2 Pkg%pc3 Pkg%pc6 Pkg%pc7 PkgWatt CorWatt GFXWatt |
| 271 | * 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 1234567 | 271 | * 123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678 |
| 272 | */ | 272 | */ |
| 273 | 273 | ||
| 274 | void print_header(void) | 274 | void print_header(void) |
| 275 | { | 275 | { |
| 276 | if (show_pkg) | 276 | if (show_pkg) |
| 277 | outp += sprintf(outp, "Package "); | 277 | outp += sprintf(outp, " Package"); |
| 278 | if (show_core) | 278 | if (show_core) |
| 279 | outp += sprintf(outp, " Core "); | 279 | outp += sprintf(outp, " Core"); |
| 280 | if (show_cpu) | 280 | if (show_cpu) |
| 281 | outp += sprintf(outp, " CPU "); | 281 | outp += sprintf(outp, " CPU"); |
| 282 | if (has_aperf) | 282 | if (has_aperf) |
| 283 | outp += sprintf(outp, "Avg_MHz "); | 283 | outp += sprintf(outp, " Avg_MHz"); |
| 284 | if (do_nhm_cstates) | 284 | if (do_nhm_cstates) |
| 285 | outp += sprintf(outp, " %%Busy "); | 285 | outp += sprintf(outp, " %%Busy"); |
| 286 | if (has_aperf) | 286 | if (has_aperf) |
| 287 | outp += sprintf(outp, "Bzy_MHz "); | 287 | outp += sprintf(outp, " Bzy_MHz"); |
| 288 | outp += sprintf(outp, "TSC_MHz "); | 288 | outp += sprintf(outp, " TSC_MHz"); |
| 289 | if (do_smi) | 289 | if (do_smi) |
| 290 | outp += sprintf(outp, " SMI "); | 290 | outp += sprintf(outp, " SMI"); |
| 291 | if (extra_delta_offset32) | 291 | if (extra_delta_offset32) |
| 292 | outp += sprintf(outp, " count 0x%03X ", extra_delta_offset32); | 292 | outp += sprintf(outp, " count 0x%03X", extra_delta_offset32); |
| 293 | if (extra_delta_offset64) | 293 | if (extra_delta_offset64) |
| 294 | outp += sprintf(outp, " COUNT 0x%03X ", extra_delta_offset64); | 294 | outp += sprintf(outp, " COUNT 0x%03X", extra_delta_offset64); |
| 295 | if (extra_msr_offset32) | 295 | if (extra_msr_offset32) |
| 296 | outp += sprintf(outp, " MSR 0x%03X ", extra_msr_offset32); | 296 | outp += sprintf(outp, " MSR 0x%03X", extra_msr_offset32); |
| 297 | if (extra_msr_offset64) | 297 | if (extra_msr_offset64) |
| 298 | outp += sprintf(outp, " MSR 0x%03X ", extra_msr_offset64); | 298 | outp += sprintf(outp, " MSR 0x%03X", extra_msr_offset64); |
| 299 | if (do_nhm_cstates) | 299 | if (do_nhm_cstates) |
| 300 | outp += sprintf(outp, " CPU%%c1 "); | 300 | outp += sprintf(outp, " CPU%%c1"); |
| 301 | if (do_nhm_cstates && !do_slm_cstates) | 301 | if (do_nhm_cstates && !do_slm_cstates) |
| 302 | outp += sprintf(outp, " CPU%%c3 "); | 302 | outp += sprintf(outp, " CPU%%c3"); |
| 303 | if (do_nhm_cstates) | 303 | if (do_nhm_cstates) |
| 304 | outp += sprintf(outp, " CPU%%c6 "); | 304 | outp += sprintf(outp, " CPU%%c6"); |
| 305 | if (do_snb_cstates) | 305 | if (do_snb_cstates) |
| 306 | outp += sprintf(outp, " CPU%%c7 "); | 306 | outp += sprintf(outp, " CPU%%c7"); |
| 307 | 307 | ||
| 308 | if (do_dts) | 308 | if (do_dts) |
| 309 | outp += sprintf(outp, "CoreTmp "); | 309 | outp += sprintf(outp, " CoreTmp"); |
| 310 | if (do_ptm) | 310 | if (do_ptm) |
| 311 | outp += sprintf(outp, " PkgTmp "); | 311 | outp += sprintf(outp, " PkgTmp"); |
| 312 | 312 | ||
| 313 | if (do_snb_cstates) | 313 | if (do_snb_cstates) |
| 314 | outp += sprintf(outp, "Pkg%%pc2 "); | 314 | outp += sprintf(outp, " Pkg%%pc2"); |
| 315 | if (do_nhm_cstates && !do_slm_cstates) | 315 | if (do_nhm_cstates && !do_slm_cstates) |
| 316 | outp += sprintf(outp, "Pkg%%pc3 "); | 316 | outp += sprintf(outp, " Pkg%%pc3"); |
| 317 | if (do_nhm_cstates && !do_slm_cstates) | 317 | if (do_nhm_cstates && !do_slm_cstates) |
| 318 | outp += sprintf(outp, "Pkg%%pc6 "); | 318 | outp += sprintf(outp, " Pkg%%pc6"); |
| 319 | if (do_snb_cstates) | 319 | if (do_snb_cstates) |
| 320 | outp += sprintf(outp, "Pkg%%pc7 "); | 320 | outp += sprintf(outp, " Pkg%%pc7"); |
| 321 | if (do_c8_c9_c10) { | 321 | if (do_c8_c9_c10) { |
| 322 | outp += sprintf(outp, "Pkg%%pc8 "); | 322 | outp += sprintf(outp, " Pkg%%pc8"); |
| 323 | outp += sprintf(outp, "Pkg%%pc9 "); | 323 | outp += sprintf(outp, " Pkg%%pc9"); |
| 324 | outp += sprintf(outp, "Pk%%pc10 "); | 324 | outp += sprintf(outp, " Pk%%pc10"); |
| 325 | } | 325 | } |
| 326 | 326 | ||
| 327 | if (do_rapl && !rapl_joules) { | 327 | if (do_rapl && !rapl_joules) { |
| 328 | if (do_rapl & RAPL_PKG) | 328 | if (do_rapl & RAPL_PKG) |
| 329 | outp += sprintf(outp, "PkgWatt "); | 329 | outp += sprintf(outp, " PkgWatt"); |
| 330 | if (do_rapl & RAPL_CORES) | 330 | if (do_rapl & RAPL_CORES) |
| 331 | outp += sprintf(outp, "CorWatt "); | 331 | outp += sprintf(outp, " CorWatt"); |
| 332 | if (do_rapl & RAPL_GFX) | 332 | if (do_rapl & RAPL_GFX) |
| 333 | outp += sprintf(outp, "GFXWatt "); | 333 | outp += sprintf(outp, " GFXWatt"); |
| 334 | if (do_rapl & RAPL_DRAM) | 334 | if (do_rapl & RAPL_DRAM) |
| 335 | outp += sprintf(outp, "RAMWatt "); | 335 | outp += sprintf(outp, " RAMWatt"); |
| 336 | if (do_rapl & RAPL_PKG_PERF_STATUS) | 336 | if (do_rapl & RAPL_PKG_PERF_STATUS) |
| 337 | outp += sprintf(outp, " PKG_%% "); | 337 | outp += sprintf(outp, " PKG_%%"); |
| 338 | if (do_rapl & RAPL_DRAM_PERF_STATUS) | 338 | if (do_rapl & RAPL_DRAM_PERF_STATUS) |
| 339 | outp += sprintf(outp, " RAM_%% "); | 339 | outp += sprintf(outp, " RAM_%%"); |
| 340 | } else { | 340 | } else { |
| 341 | if (do_rapl & RAPL_PKG) | 341 | if (do_rapl & RAPL_PKG) |
| 342 | outp += sprintf(outp, " Pkg_J "); | 342 | outp += sprintf(outp, " Pkg_J"); |
| 343 | if (do_rapl & RAPL_CORES) | 343 | if (do_rapl & RAPL_CORES) |
| 344 | outp += sprintf(outp, " Cor_J "); | 344 | outp += sprintf(outp, " Cor_J"); |
| 345 | if (do_rapl & RAPL_GFX) | 345 | if (do_rapl & RAPL_GFX) |
| 346 | outp += sprintf(outp, " GFX_J "); | 346 | outp += sprintf(outp, " GFX_J"); |
| 347 | if (do_rapl & RAPL_DRAM) | 347 | if (do_rapl & RAPL_DRAM) |
| 348 | outp += sprintf(outp, " RAM_W "); | 348 | outp += sprintf(outp, " RAM_W"); |
| 349 | if (do_rapl & RAPL_PKG_PERF_STATUS) | 349 | if (do_rapl & RAPL_PKG_PERF_STATUS) |
| 350 | outp += sprintf(outp, " PKG_%% "); | 350 | outp += sprintf(outp, " PKG_%%"); |
| 351 | if (do_rapl & RAPL_DRAM_PERF_STATUS) | 351 | if (do_rapl & RAPL_DRAM_PERF_STATUS) |
| 352 | outp += sprintf(outp, " RAM_%% "); | 352 | outp += sprintf(outp, " RAM_%%"); |
| 353 | outp += sprintf(outp, " time "); | 353 | outp += sprintf(outp, " time"); |
| 354 | 354 | ||
| 355 | } | 355 | } |
| 356 | outp += sprintf(outp, "\n"); | 356 | outp += sprintf(outp, "\n"); |
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 d10f95ce2ea4..45f145c6f843 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
| @@ -2,8 +2,10 @@ TARGETS = breakpoints | |||
| 2 | TARGETS += cpu-hotplug | 2 | TARGETS += cpu-hotplug |
| 3 | TARGETS += efivarfs | 3 | TARGETS += efivarfs |
| 4 | TARGETS += kcmp | 4 | TARGETS += kcmp |
| 5 | TARGETS += memfd | ||
| 5 | TARGETS += memory-hotplug | 6 | TARGETS += memory-hotplug |
| 6 | TARGETS += mqueue | 7 | TARGETS += mqueue |
| 8 | TARGETS += mount | ||
| 7 | TARGETS += net | 9 | TARGETS += net |
| 8 | TARGETS += ptrace | 10 | TARGETS += ptrace |
| 9 | TARGETS += timers | 11 | TARGETS += timers |
| @@ -12,6 +14,7 @@ TARGETS += powerpc | |||
| 12 | TARGETS += user | 14 | TARGETS += user |
| 13 | TARGETS += sysctl | 15 | TARGETS += sysctl |
| 14 | TARGETS += firmware | 16 | TARGETS += firmware |
| 17 | TARGETS += ftrace | ||
| 15 | 18 | ||
| 16 | TARGETS_HOTPLUG = cpu-hotplug | 19 | TARGETS_HOTPLUG = cpu-hotplug |
| 17 | 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..515247601df4 --- /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' ' | head -1` | ||
| 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/ipc/Makefile b/tools/testing/selftests/ipc/Makefile index 5386fd7c43ae..74bbefdeaf4c 100644 --- a/tools/testing/selftests/ipc/Makefile +++ b/tools/testing/selftests/ipc/Makefile | |||
| @@ -1,18 +1,18 @@ | |||
| 1 | uname_M := $(shell uname -m 2>/dev/null || echo not) | 1 | uname_M := $(shell uname -m 2>/dev/null || echo not) |
| 2 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) | 2 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) |
| 3 | ifeq ($(ARCH),i386) | 3 | ifeq ($(ARCH),i386) |
| 4 | ARCH := X86 | 4 | ARCH := x86 |
| 5 | CFLAGS := -DCONFIG_X86_32 -D__i386__ | 5 | CFLAGS := -DCONFIG_X86_32 -D__i386__ |
| 6 | endif | 6 | endif |
| 7 | ifeq ($(ARCH),x86_64) | 7 | ifeq ($(ARCH),x86_64) |
| 8 | ARCH := X86 | 8 | ARCH := x86 |
| 9 | CFLAGS := -DCONFIG_X86_64 -D__x86_64__ | 9 | CFLAGS := -DCONFIG_X86_64 -D__x86_64__ |
| 10 | endif | 10 | endif |
| 11 | 11 | ||
| 12 | CFLAGS += -I../../../../usr/include/ | 12 | CFLAGS += -I../../../../usr/include/ |
| 13 | 13 | ||
| 14 | all: | 14 | all: |
| 15 | ifeq ($(ARCH),X86) | 15 | ifeq ($(ARCH),x86) |
| 16 | gcc $(CFLAGS) msgque.c -o msgque_test | 16 | gcc $(CFLAGS) msgque.c -o msgque_test |
| 17 | else | 17 | else |
| 18 | echo "Not an x86 target, can't build msgque selftest" | 18 | echo "Not an x86 target, can't build msgque selftest" |
diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile index d7d6bbeeff2f..8aabd82db9e4 100644 --- a/tools/testing/selftests/kcmp/Makefile +++ b/tools/testing/selftests/kcmp/Makefile | |||
| @@ -1,11 +1,11 @@ | |||
| 1 | uname_M := $(shell uname -m 2>/dev/null || echo not) | 1 | uname_M := $(shell uname -m 2>/dev/null || echo not) |
| 2 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) | 2 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) |
| 3 | ifeq ($(ARCH),i386) | 3 | ifeq ($(ARCH),i386) |
| 4 | ARCH := X86 | 4 | ARCH := x86 |
| 5 | CFLAGS := -DCONFIG_X86_32 -D__i386__ | 5 | CFLAGS := -DCONFIG_X86_32 -D__i386__ |
| 6 | endif | 6 | endif |
| 7 | ifeq ($(ARCH),x86_64) | 7 | ifeq ($(ARCH),x86_64) |
| 8 | ARCH := X86 | 8 | ARCH := x86 |
| 9 | CFLAGS := -DCONFIG_X86_64 -D__x86_64__ | 9 | CFLAGS := -DCONFIG_X86_64 -D__x86_64__ |
| 10 | endif | 10 | endif |
| 11 | 11 | ||
| @@ -15,7 +15,7 @@ CFLAGS += -I../../../../usr/include/ | |||
| 15 | CFLAGS += -I../../../../arch/x86/include/ | 15 | CFLAGS += -I../../../../arch/x86/include/ |
| 16 | 16 | ||
| 17 | all: | 17 | all: |
| 18 | ifeq ($(ARCH),X86) | 18 | ifeq ($(ARCH),x86) |
| 19 | gcc $(CFLAGS) kcmp_test.c -o kcmp_test | 19 | gcc $(CFLAGS) kcmp_test.c -o kcmp_test |
| 20 | else | 20 | else |
| 21 | echo "Not an x86 target, can't build kcmp selftest" | 21 | echo "Not an x86 target, can't build kcmp selftest" |
diff --git a/tools/testing/selftests/memfd/.gitignore b/tools/testing/selftests/memfd/.gitignore new file mode 100644 index 000000000000..afe87c40ac80 --- /dev/null +++ b/tools/testing/selftests/memfd/.gitignore | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | fuse_mnt | ||
| 2 | fuse_test | ||
| 3 | memfd_test | ||
| 4 | memfd-test-file | ||
diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile new file mode 100644 index 000000000000..b80cd10d53ba --- /dev/null +++ b/tools/testing/selftests/memfd/Makefile | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | CFLAGS += -D_FILE_OFFSET_BITS=64 | ||
| 2 | CFLAGS += -I../../../../include/uapi/ | ||
| 3 | CFLAGS += -I../../../../include/ | ||
| 4 | |||
| 5 | all: | ||
| 6 | gcc $(CFLAGS) memfd_test.c -o memfd_test | ||
| 7 | |||
| 8 | run_tests: all | ||
| 9 | gcc $(CFLAGS) memfd_test.c -o memfd_test | ||
| 10 | @./memfd_test || echo "memfd_test: [FAIL]" | ||
| 11 | |||
| 12 | build_fuse: | ||
| 13 | gcc $(CFLAGS) fuse_mnt.c `pkg-config fuse --cflags --libs` -o fuse_mnt | ||
| 14 | gcc $(CFLAGS) fuse_test.c -o fuse_test | ||
| 15 | |||
| 16 | run_fuse: build_fuse | ||
| 17 | @./run_fuse_test.sh || echo "fuse_test: [FAIL]" | ||
| 18 | |||
| 19 | clean: | ||
| 20 | $(RM) memfd_test fuse_test | ||
diff --git a/tools/testing/selftests/memfd/fuse_mnt.c b/tools/testing/selftests/memfd/fuse_mnt.c new file mode 100644 index 000000000000..feacf1280fcd --- /dev/null +++ b/tools/testing/selftests/memfd/fuse_mnt.c | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | /* | ||
| 2 | * memfd test file-system | ||
| 3 | * This file uses FUSE to create a dummy file-system with only one file /memfd. | ||
| 4 | * This file is read-only and takes 1s per read. | ||
| 5 | * | ||
| 6 | * This file-system is used by the memfd test-cases to force the kernel to pin | ||
| 7 | * pages during reads(). Due to the 1s delay of this file-system, this is a | ||
| 8 | * nice way to test race-conditions against get_user_pages() in the kernel. | ||
| 9 | * | ||
| 10 | * We use direct_io==1 to force the kernel to use direct-IO for this | ||
| 11 | * file-system. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #define FUSE_USE_VERSION 26 | ||
| 15 | |||
| 16 | #include <fuse.h> | ||
| 17 | #include <stdio.h> | ||
| 18 | #include <string.h> | ||
| 19 | #include <errno.h> | ||
| 20 | #include <fcntl.h> | ||
| 21 | #include <unistd.h> | ||
| 22 | |||
| 23 | static const char memfd_content[] = "memfd-example-content"; | ||
| 24 | static const char memfd_path[] = "/memfd"; | ||
| 25 | |||
| 26 | static int memfd_getattr(const char *path, struct stat *st) | ||
| 27 | { | ||
| 28 | memset(st, 0, sizeof(*st)); | ||
| 29 | |||
| 30 | if (!strcmp(path, "/")) { | ||
| 31 | st->st_mode = S_IFDIR | 0755; | ||
| 32 | st->st_nlink = 2; | ||
| 33 | } else if (!strcmp(path, memfd_path)) { | ||
| 34 | st->st_mode = S_IFREG | 0444; | ||
| 35 | st->st_nlink = 1; | ||
| 36 | st->st_size = strlen(memfd_content); | ||
| 37 | } else { | ||
| 38 | return -ENOENT; | ||
| 39 | } | ||
| 40 | |||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | static int memfd_readdir(const char *path, | ||
| 45 | void *buf, | ||
| 46 | fuse_fill_dir_t filler, | ||
| 47 | off_t offset, | ||
| 48 | struct fuse_file_info *fi) | ||
| 49 | { | ||
| 50 | if (strcmp(path, "/")) | ||
| 51 | return -ENOENT; | ||
| 52 | |||
| 53 | filler(buf, ".", NULL, 0); | ||
| 54 | filler(buf, "..", NULL, 0); | ||
| 55 | filler(buf, memfd_path + 1, NULL, 0); | ||
| 56 | |||
| 57 | return 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | static int memfd_open(const char *path, struct fuse_file_info *fi) | ||
| 61 | { | ||
| 62 | if (strcmp(path, memfd_path)) | ||
| 63 | return -ENOENT; | ||
| 64 | |||
| 65 | if ((fi->flags & 3) != O_RDONLY) | ||
| 66 | return -EACCES; | ||
| 67 | |||
| 68 | /* force direct-IO */ | ||
| 69 | fi->direct_io = 1; | ||
| 70 | |||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | static int memfd_read(const char *path, | ||
| 75 | char *buf, | ||
| 76 | size_t size, | ||
| 77 | off_t offset, | ||
| 78 | struct fuse_file_info *fi) | ||
| 79 | { | ||
| 80 | size_t len; | ||
| 81 | |||
| 82 | if (strcmp(path, memfd_path) != 0) | ||
| 83 | return -ENOENT; | ||
| 84 | |||
| 85 | sleep(1); | ||
| 86 | |||
| 87 | len = strlen(memfd_content); | ||
| 88 | if (offset < len) { | ||
| 89 | if (offset + size > len) | ||
| 90 | size = len - offset; | ||
| 91 | |||
| 92 | memcpy(buf, memfd_content + offset, size); | ||
| 93 | } else { | ||
| 94 | size = 0; | ||
| 95 | } | ||
| 96 | |||
| 97 | return size; | ||
| 98 | } | ||
| 99 | |||
| 100 | static struct fuse_operations memfd_ops = { | ||
| 101 | .getattr = memfd_getattr, | ||
| 102 | .readdir = memfd_readdir, | ||
| 103 | .open = memfd_open, | ||
| 104 | .read = memfd_read, | ||
| 105 | }; | ||
| 106 | |||
| 107 | int main(int argc, char *argv[]) | ||
| 108 | { | ||
| 109 | return fuse_main(argc, argv, &memfd_ops, NULL); | ||
| 110 | } | ||
diff --git a/tools/testing/selftests/memfd/fuse_test.c b/tools/testing/selftests/memfd/fuse_test.c new file mode 100644 index 000000000000..67908b18f035 --- /dev/null +++ b/tools/testing/selftests/memfd/fuse_test.c | |||
| @@ -0,0 +1,311 @@ | |||
| 1 | /* | ||
| 2 | * memfd GUP test-case | ||
| 3 | * This tests memfd interactions with get_user_pages(). We require the | ||
| 4 | * fuse_mnt.c program to provide a fake direct-IO FUSE mount-point for us. This | ||
| 5 | * file-system delays _all_ reads by 1s and forces direct-IO. This means, any | ||
| 6 | * read() on files in that file-system will pin the receive-buffer pages for at | ||
| 7 | * least 1s via get_user_pages(). | ||
| 8 | * | ||
| 9 | * We use this trick to race ADD_SEALS against a write on a memfd object. The | ||
| 10 | * ADD_SEALS must fail if the memfd pages are still pinned. Note that we use | ||
| 11 | * the read() syscall with our memory-mapped memfd object as receive buffer to | ||
| 12 | * force the kernel to write into our memfd object. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #define _GNU_SOURCE | ||
| 16 | #define __EXPORTED_HEADERS__ | ||
| 17 | |||
| 18 | #include <errno.h> | ||
| 19 | #include <inttypes.h> | ||
| 20 | #include <limits.h> | ||
| 21 | #include <linux/falloc.h> | ||
| 22 | #include <linux/fcntl.h> | ||
| 23 | #include <linux/memfd.h> | ||
| 24 | #include <sched.h> | ||
| 25 | #include <stdio.h> | ||
| 26 | #include <stdlib.h> | ||
| 27 | #include <signal.h> | ||
| 28 | #include <string.h> | ||
| 29 | #include <sys/mman.h> | ||
| 30 | #include <sys/stat.h> | ||
| 31 | #include <sys/syscall.h> | ||
| 32 | #include <sys/wait.h> | ||
| 33 | #include <unistd.h> | ||
| 34 | |||
| 35 | #define MFD_DEF_SIZE 8192 | ||
| 36 | #define STACK_SIZE 65535 | ||
| 37 | |||
| 38 | static int sys_memfd_create(const char *name, | ||
| 39 | unsigned int flags) | ||
| 40 | { | ||
| 41 | return syscall(__NR_memfd_create, name, flags); | ||
| 42 | } | ||
| 43 | |||
| 44 | static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags) | ||
| 45 | { | ||
| 46 | int r, fd; | ||
| 47 | |||
| 48 | fd = sys_memfd_create(name, flags); | ||
| 49 | if (fd < 0) { | ||
| 50 | printf("memfd_create(\"%s\", %u) failed: %m\n", | ||
| 51 | name, flags); | ||
| 52 | abort(); | ||
| 53 | } | ||
| 54 | |||
| 55 | r = ftruncate(fd, sz); | ||
| 56 | if (r < 0) { | ||
| 57 | printf("ftruncate(%llu) failed: %m\n", (unsigned long long)sz); | ||
| 58 | abort(); | ||
| 59 | } | ||
| 60 | |||
| 61 | return fd; | ||
| 62 | } | ||
| 63 | |||
| 64 | static __u64 mfd_assert_get_seals(int fd) | ||
| 65 | { | ||
| 66 | long r; | ||
| 67 | |||
| 68 | r = fcntl(fd, F_GET_SEALS); | ||
| 69 | if (r < 0) { | ||
| 70 | printf("GET_SEALS(%d) failed: %m\n", fd); | ||
| 71 | abort(); | ||
| 72 | } | ||
| 73 | |||
| 74 | return r; | ||
| 75 | } | ||
| 76 | |||
| 77 | static void mfd_assert_has_seals(int fd, __u64 seals) | ||
| 78 | { | ||
| 79 | __u64 s; | ||
| 80 | |||
| 81 | s = mfd_assert_get_seals(fd); | ||
| 82 | if (s != seals) { | ||
| 83 | printf("%llu != %llu = GET_SEALS(%d)\n", | ||
| 84 | (unsigned long long)seals, (unsigned long long)s, fd); | ||
| 85 | abort(); | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | static void mfd_assert_add_seals(int fd, __u64 seals) | ||
| 90 | { | ||
| 91 | long r; | ||
| 92 | __u64 s; | ||
| 93 | |||
| 94 | s = mfd_assert_get_seals(fd); | ||
| 95 | r = fcntl(fd, F_ADD_SEALS, seals); | ||
| 96 | if (r < 0) { | ||
| 97 | printf("ADD_SEALS(%d, %llu -> %llu) failed: %m\n", | ||
| 98 | fd, (unsigned long long)s, (unsigned long long)seals); | ||
| 99 | abort(); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | static int mfd_busy_add_seals(int fd, __u64 seals) | ||
| 104 | { | ||
| 105 | long r; | ||
| 106 | __u64 s; | ||
| 107 | |||
| 108 | r = fcntl(fd, F_GET_SEALS); | ||
| 109 | if (r < 0) | ||
| 110 | s = 0; | ||
| 111 | else | ||
| 112 | s = r; | ||
| 113 | |||
| 114 | r = fcntl(fd, F_ADD_SEALS, seals); | ||
| 115 | if (r < 0 && errno != EBUSY) { | ||
| 116 | printf("ADD_SEALS(%d, %llu -> %llu) didn't fail as expected with EBUSY: %m\n", | ||
| 117 | fd, (unsigned long long)s, (unsigned long long)seals); | ||
| 118 | abort(); | ||
| 119 | } | ||
| 120 | |||
| 121 | return r; | ||
| 122 | } | ||
| 123 | |||
| 124 | static void *mfd_assert_mmap_shared(int fd) | ||
| 125 | { | ||
| 126 | void *p; | ||
| 127 | |||
| 128 | p = mmap(NULL, | ||
| 129 | MFD_DEF_SIZE, | ||
| 130 | PROT_READ | PROT_WRITE, | ||
| 131 | MAP_SHARED, | ||
| 132 | fd, | ||
| 133 | 0); | ||
| 134 | if (p == MAP_FAILED) { | ||
| 135 | printf("mmap() failed: %m\n"); | ||
| 136 | abort(); | ||
| 137 | } | ||
| 138 | |||
| 139 | return p; | ||
| 140 | } | ||
| 141 | |||
| 142 | static void *mfd_assert_mmap_private(int fd) | ||
| 143 | { | ||
| 144 | void *p; | ||
| 145 | |||
| 146 | p = mmap(NULL, | ||
| 147 | MFD_DEF_SIZE, | ||
| 148 | PROT_READ | PROT_WRITE, | ||
| 149 | MAP_PRIVATE, | ||
| 150 | fd, | ||
| 151 | 0); | ||
| 152 | if (p == MAP_FAILED) { | ||
| 153 | printf("mmap() failed: %m\n"); | ||
| 154 | abort(); | ||
| 155 | } | ||
| 156 | |||
| 157 | return p; | ||
| 158 | } | ||
| 159 | |||
| 160 | static int global_mfd = -1; | ||
| 161 | static void *global_p = NULL; | ||
| 162 | |||
| 163 | static int sealing_thread_fn(void *arg) | ||
| 164 | { | ||
| 165 | int sig, r; | ||
| 166 | |||
| 167 | /* | ||
| 168 | * This thread first waits 200ms so any pending operation in the parent | ||
| 169 | * is correctly started. After that, it tries to seal @global_mfd as | ||
| 170 | * SEAL_WRITE. This _must_ fail as the parent thread has a read() into | ||
| 171 | * that memory mapped object still ongoing. | ||
| 172 | * We then wait one more second and try sealing again. This time it | ||
| 173 | * must succeed as there shouldn't be anyone else pinning the pages. | ||
| 174 | */ | ||
| 175 | |||
| 176 | /* wait 200ms for FUSE-request to be active */ | ||
| 177 | usleep(200000); | ||
| 178 | |||
| 179 | /* unmount mapping before sealing to avoid i_mmap_writable failures */ | ||
| 180 | munmap(global_p, MFD_DEF_SIZE); | ||
| 181 | |||
| 182 | /* Try sealing the global file; expect EBUSY or success. Current | ||
| 183 | * kernels will never succeed, but in the future, kernels might | ||
| 184 | * implement page-replacements or other fancy ways to avoid racing | ||
| 185 | * writes. */ | ||
| 186 | r = mfd_busy_add_seals(global_mfd, F_SEAL_WRITE); | ||
| 187 | if (r >= 0) { | ||
| 188 | printf("HURRAY! This kernel fixed GUP races!\n"); | ||
| 189 | } else { | ||
| 190 | /* wait 1s more so the FUSE-request is done */ | ||
| 191 | sleep(1); | ||
| 192 | |||
| 193 | /* try sealing the global file again */ | ||
| 194 | mfd_assert_add_seals(global_mfd, F_SEAL_WRITE); | ||
| 195 | } | ||
| 196 | |||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | static pid_t spawn_sealing_thread(void) | ||
| 201 | { | ||
| 202 | uint8_t *stack; | ||
| 203 | pid_t pid; | ||
| 204 | |||
| 205 | stack = malloc(STACK_SIZE); | ||
| 206 | if (!stack) { | ||
| 207 | printf("malloc(STACK_SIZE) failed: %m\n"); | ||
| 208 | abort(); | ||
| 209 | } | ||
| 210 | |||
| 211 | pid = clone(sealing_thread_fn, | ||
| 212 | stack + STACK_SIZE, | ||
| 213 | SIGCHLD | CLONE_FILES | CLONE_FS | CLONE_VM, | ||
| 214 | NULL); | ||
| 215 | if (pid < 0) { | ||
| 216 | printf("clone() failed: %m\n"); | ||
| 217 | abort(); | ||
| 218 | } | ||
| 219 | |||
| 220 | return pid; | ||
| 221 | } | ||
| 222 | |||
| 223 | static void join_sealing_thread(pid_t pid) | ||
| 224 | { | ||
| 225 | waitpid(pid, NULL, 0); | ||
| 226 | } | ||
| 227 | |||
| 228 | int main(int argc, char **argv) | ||
| 229 | { | ||
| 230 | static const char zero[MFD_DEF_SIZE]; | ||
| 231 | int fd, mfd, r; | ||
| 232 | void *p; | ||
| 233 | int was_sealed; | ||
| 234 | pid_t pid; | ||
| 235 | |||
| 236 | if (argc < 2) { | ||
| 237 | printf("error: please pass path to file in fuse_mnt mount-point\n"); | ||
| 238 | abort(); | ||
| 239 | } | ||
| 240 | |||
| 241 | /* open FUSE memfd file for GUP testing */ | ||
| 242 | printf("opening: %s\n", argv[1]); | ||
| 243 | fd = open(argv[1], O_RDONLY | O_CLOEXEC); | ||
| 244 | if (fd < 0) { | ||
| 245 | printf("cannot open(\"%s\"): %m\n", argv[1]); | ||
| 246 | abort(); | ||
| 247 | } | ||
| 248 | |||
| 249 | /* create new memfd-object */ | ||
| 250 | mfd = mfd_assert_new("kern_memfd_fuse", | ||
| 251 | MFD_DEF_SIZE, | ||
| 252 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 253 | |||
| 254 | /* mmap memfd-object for writing */ | ||
| 255 | p = mfd_assert_mmap_shared(mfd); | ||
| 256 | |||
| 257 | /* pass mfd+mapping to a separate sealing-thread which tries to seal | ||
| 258 | * the memfd objects with SEAL_WRITE while we write into it */ | ||
| 259 | global_mfd = mfd; | ||
| 260 | global_p = p; | ||
| 261 | pid = spawn_sealing_thread(); | ||
| 262 | |||
| 263 | /* Use read() on the FUSE file to read into our memory-mapped memfd | ||
| 264 | * object. This races the other thread which tries to seal the | ||
| 265 | * memfd-object. | ||
| 266 | * If @fd is on the memfd-fake-FUSE-FS, the read() is delayed by 1s. | ||
| 267 | * This guarantees that the receive-buffer is pinned for 1s until the | ||
| 268 | * data is written into it. The racing ADD_SEALS should thus fail as | ||
| 269 | * the pages are still pinned. */ | ||
| 270 | r = read(fd, p, MFD_DEF_SIZE); | ||
| 271 | if (r < 0) { | ||
| 272 | printf("read() failed: %m\n"); | ||
| 273 | abort(); | ||
| 274 | } else if (!r) { | ||
| 275 | printf("unexpected EOF on read()\n"); | ||
| 276 | abort(); | ||
| 277 | } | ||
| 278 | |||
| 279 | was_sealed = mfd_assert_get_seals(mfd) & F_SEAL_WRITE; | ||
| 280 | |||
| 281 | /* Wait for sealing-thread to finish and verify that it | ||
| 282 | * successfully sealed the file after the second try. */ | ||
| 283 | join_sealing_thread(pid); | ||
| 284 | mfd_assert_has_seals(mfd, F_SEAL_WRITE); | ||
| 285 | |||
| 286 | /* *IF* the memfd-object was sealed at the time our read() returned, | ||
| 287 | * then the kernel did a page-replacement or canceled the read() (or | ||
| 288 | * whatever magic it did..). In that case, the memfd object is still | ||
| 289 | * all zero. | ||
| 290 | * In case the memfd-object was *not* sealed, the read() was successfull | ||
| 291 | * and the memfd object must *not* be all zero. | ||
| 292 | * Note that in real scenarios, there might be a mixture of both, but | ||
| 293 | * in this test-cases, we have explicit 200ms delays which should be | ||
| 294 | * enough to avoid any in-flight writes. */ | ||
| 295 | |||
| 296 | p = mfd_assert_mmap_private(mfd); | ||
| 297 | if (was_sealed && memcmp(p, zero, MFD_DEF_SIZE)) { | ||
| 298 | printf("memfd sealed during read() but data not discarded\n"); | ||
| 299 | abort(); | ||
| 300 | } else if (!was_sealed && !memcmp(p, zero, MFD_DEF_SIZE)) { | ||
| 301 | printf("memfd sealed after read() but data discarded\n"); | ||
| 302 | abort(); | ||
| 303 | } | ||
| 304 | |||
| 305 | close(mfd); | ||
| 306 | close(fd); | ||
| 307 | |||
| 308 | printf("fuse: DONE\n"); | ||
| 309 | |||
| 310 | return 0; | ||
| 311 | } | ||
diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c new file mode 100644 index 000000000000..0b9eafb7ab7b --- /dev/null +++ b/tools/testing/selftests/memfd/memfd_test.c | |||
| @@ -0,0 +1,911 @@ | |||
| 1 | #define _GNU_SOURCE | ||
| 2 | #define __EXPORTED_HEADERS__ | ||
| 3 | |||
| 4 | #include <errno.h> | ||
| 5 | #include <inttypes.h> | ||
| 6 | #include <limits.h> | ||
| 7 | #include <linux/falloc.h> | ||
| 8 | #include <linux/fcntl.h> | ||
| 9 | #include <linux/memfd.h> | ||
| 10 | #include <sched.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <signal.h> | ||
| 14 | #include <string.h> | ||
| 15 | #include <sys/mman.h> | ||
| 16 | #include <sys/stat.h> | ||
| 17 | #include <sys/syscall.h> | ||
| 18 | #include <unistd.h> | ||
| 19 | |||
| 20 | #define MFD_DEF_SIZE 8192 | ||
| 21 | #define STACK_SIZE 65535 | ||
| 22 | |||
| 23 | static int sys_memfd_create(const char *name, | ||
| 24 | unsigned int flags) | ||
| 25 | { | ||
| 26 | return syscall(__NR_memfd_create, name, flags); | ||
| 27 | } | ||
| 28 | |||
| 29 | static int mfd_assert_new(const char *name, loff_t sz, unsigned int flags) | ||
| 30 | { | ||
| 31 | int r, fd; | ||
| 32 | |||
| 33 | fd = sys_memfd_create(name, flags); | ||
| 34 | if (fd < 0) { | ||
| 35 | printf("memfd_create(\"%s\", %u) failed: %m\n", | ||
| 36 | name, flags); | ||
| 37 | abort(); | ||
| 38 | } | ||
| 39 | |||
| 40 | r = ftruncate(fd, sz); | ||
| 41 | if (r < 0) { | ||
| 42 | printf("ftruncate(%llu) failed: %m\n", (unsigned long long)sz); | ||
| 43 | abort(); | ||
| 44 | } | ||
| 45 | |||
| 46 | return fd; | ||
| 47 | } | ||
| 48 | |||
| 49 | static void mfd_fail_new(const char *name, unsigned int flags) | ||
| 50 | { | ||
| 51 | int r; | ||
| 52 | |||
| 53 | r = sys_memfd_create(name, flags); | ||
| 54 | if (r >= 0) { | ||
| 55 | printf("memfd_create(\"%s\", %u) succeeded, but failure expected\n", | ||
| 56 | name, flags); | ||
| 57 | close(r); | ||
| 58 | abort(); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | static unsigned int mfd_assert_get_seals(int fd) | ||
| 63 | { | ||
| 64 | int r; | ||
| 65 | |||
| 66 | r = fcntl(fd, F_GET_SEALS); | ||
| 67 | if (r < 0) { | ||
| 68 | printf("GET_SEALS(%d) failed: %m\n", fd); | ||
| 69 | abort(); | ||
| 70 | } | ||
| 71 | |||
| 72 | return (unsigned int)r; | ||
| 73 | } | ||
| 74 | |||
| 75 | static void mfd_assert_has_seals(int fd, unsigned int seals) | ||
| 76 | { | ||
| 77 | unsigned int s; | ||
| 78 | |||
| 79 | s = mfd_assert_get_seals(fd); | ||
| 80 | if (s != seals) { | ||
| 81 | printf("%u != %u = GET_SEALS(%d)\n", seals, s, fd); | ||
| 82 | abort(); | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | static void mfd_assert_add_seals(int fd, unsigned int seals) | ||
| 87 | { | ||
| 88 | int r; | ||
| 89 | unsigned int s; | ||
| 90 | |||
| 91 | s = mfd_assert_get_seals(fd); | ||
| 92 | r = fcntl(fd, F_ADD_SEALS, seals); | ||
| 93 | if (r < 0) { | ||
| 94 | printf("ADD_SEALS(%d, %u -> %u) failed: %m\n", fd, s, seals); | ||
| 95 | abort(); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | static void mfd_fail_add_seals(int fd, unsigned int seals) | ||
| 100 | { | ||
| 101 | int r; | ||
| 102 | unsigned int s; | ||
| 103 | |||
| 104 | r = fcntl(fd, F_GET_SEALS); | ||
| 105 | if (r < 0) | ||
| 106 | s = 0; | ||
| 107 | else | ||
| 108 | s = (unsigned int)r; | ||
| 109 | |||
| 110 | r = fcntl(fd, F_ADD_SEALS, seals); | ||
| 111 | if (r >= 0) { | ||
| 112 | printf("ADD_SEALS(%d, %u -> %u) didn't fail as expected\n", | ||
| 113 | fd, s, seals); | ||
| 114 | abort(); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | static void mfd_assert_size(int fd, size_t size) | ||
| 119 | { | ||
| 120 | struct stat st; | ||
| 121 | int r; | ||
| 122 | |||
| 123 | r = fstat(fd, &st); | ||
| 124 | if (r < 0) { | ||
| 125 | printf("fstat(%d) failed: %m\n", fd); | ||
| 126 | abort(); | ||
| 127 | } else if (st.st_size != size) { | ||
| 128 | printf("wrong file size %lld, but expected %lld\n", | ||
| 129 | (long long)st.st_size, (long long)size); | ||
| 130 | abort(); | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | static int mfd_assert_dup(int fd) | ||
| 135 | { | ||
| 136 | int r; | ||
| 137 | |||
| 138 | r = dup(fd); | ||
| 139 | if (r < 0) { | ||
| 140 | printf("dup(%d) failed: %m\n", fd); | ||
| 141 | abort(); | ||
| 142 | } | ||
| 143 | |||
| 144 | return r; | ||
| 145 | } | ||
| 146 | |||
| 147 | static void *mfd_assert_mmap_shared(int fd) | ||
| 148 | { | ||
| 149 | void *p; | ||
| 150 | |||
| 151 | p = mmap(NULL, | ||
| 152 | MFD_DEF_SIZE, | ||
| 153 | PROT_READ | PROT_WRITE, | ||
| 154 | MAP_SHARED, | ||
| 155 | fd, | ||
| 156 | 0); | ||
| 157 | if (p == MAP_FAILED) { | ||
| 158 | printf("mmap() failed: %m\n"); | ||
| 159 | abort(); | ||
| 160 | } | ||
| 161 | |||
| 162 | return p; | ||
| 163 | } | ||
| 164 | |||
| 165 | static void *mfd_assert_mmap_private(int fd) | ||
| 166 | { | ||
| 167 | void *p; | ||
| 168 | |||
| 169 | p = mmap(NULL, | ||
| 170 | MFD_DEF_SIZE, | ||
| 171 | PROT_READ, | ||
| 172 | MAP_PRIVATE, | ||
| 173 | fd, | ||
| 174 | 0); | ||
| 175 | if (p == MAP_FAILED) { | ||
| 176 | printf("mmap() failed: %m\n"); | ||
| 177 | abort(); | ||
| 178 | } | ||
| 179 | |||
| 180 | return p; | ||
| 181 | } | ||
| 182 | |||
| 183 | static int mfd_assert_open(int fd, int flags, mode_t mode) | ||
| 184 | { | ||
| 185 | char buf[512]; | ||
| 186 | int r; | ||
| 187 | |||
| 188 | sprintf(buf, "/proc/self/fd/%d", fd); | ||
| 189 | r = open(buf, flags, mode); | ||
| 190 | if (r < 0) { | ||
| 191 | printf("open(%s) failed: %m\n", buf); | ||
| 192 | abort(); | ||
| 193 | } | ||
| 194 | |||
| 195 | return r; | ||
| 196 | } | ||
| 197 | |||
| 198 | static void mfd_fail_open(int fd, int flags, mode_t mode) | ||
| 199 | { | ||
| 200 | char buf[512]; | ||
| 201 | int r; | ||
| 202 | |||
| 203 | sprintf(buf, "/proc/self/fd/%d", fd); | ||
| 204 | r = open(buf, flags, mode); | ||
| 205 | if (r >= 0) { | ||
| 206 | printf("open(%s) didn't fail as expected\n", buf); | ||
| 207 | abort(); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | static void mfd_assert_read(int fd) | ||
| 212 | { | ||
| 213 | char buf[16]; | ||
| 214 | void *p; | ||
| 215 | ssize_t l; | ||
| 216 | |||
| 217 | l = read(fd, buf, sizeof(buf)); | ||
| 218 | if (l != sizeof(buf)) { | ||
| 219 | printf("read() failed: %m\n"); | ||
| 220 | abort(); | ||
| 221 | } | ||
| 222 | |||
| 223 | /* verify PROT_READ *is* allowed */ | ||
| 224 | p = mmap(NULL, | ||
| 225 | MFD_DEF_SIZE, | ||
| 226 | PROT_READ, | ||
| 227 | MAP_PRIVATE, | ||
| 228 | fd, | ||
| 229 | 0); | ||
| 230 | if (p == MAP_FAILED) { | ||
| 231 | printf("mmap() failed: %m\n"); | ||
| 232 | abort(); | ||
| 233 | } | ||
| 234 | munmap(p, MFD_DEF_SIZE); | ||
| 235 | |||
| 236 | /* verify MAP_PRIVATE is *always* allowed (even writable) */ | ||
| 237 | p = mmap(NULL, | ||
| 238 | MFD_DEF_SIZE, | ||
| 239 | PROT_READ | PROT_WRITE, | ||
| 240 | MAP_PRIVATE, | ||
| 241 | fd, | ||
| 242 | 0); | ||
| 243 | if (p == MAP_FAILED) { | ||
| 244 | printf("mmap() failed: %m\n"); | ||
| 245 | abort(); | ||
| 246 | } | ||
| 247 | munmap(p, MFD_DEF_SIZE); | ||
| 248 | } | ||
| 249 | |||
| 250 | static void mfd_assert_write(int fd) | ||
| 251 | { | ||
| 252 | ssize_t l; | ||
| 253 | void *p; | ||
| 254 | int r; | ||
| 255 | |||
| 256 | /* verify write() succeeds */ | ||
| 257 | l = write(fd, "\0\0\0\0", 4); | ||
| 258 | if (l != 4) { | ||
| 259 | printf("write() failed: %m\n"); | ||
| 260 | abort(); | ||
| 261 | } | ||
| 262 | |||
| 263 | /* verify PROT_READ | PROT_WRITE is allowed */ | ||
| 264 | p = mmap(NULL, | ||
| 265 | MFD_DEF_SIZE, | ||
| 266 | PROT_READ | PROT_WRITE, | ||
| 267 | MAP_SHARED, | ||
| 268 | fd, | ||
| 269 | 0); | ||
| 270 | if (p == MAP_FAILED) { | ||
| 271 | printf("mmap() failed: %m\n"); | ||
| 272 | abort(); | ||
| 273 | } | ||
| 274 | *(char *)p = 0; | ||
| 275 | munmap(p, MFD_DEF_SIZE); | ||
| 276 | |||
| 277 | /* verify PROT_WRITE is allowed */ | ||
| 278 | p = mmap(NULL, | ||
| 279 | MFD_DEF_SIZE, | ||
| 280 | PROT_WRITE, | ||
| 281 | MAP_SHARED, | ||
| 282 | fd, | ||
| 283 | 0); | ||
| 284 | if (p == MAP_FAILED) { | ||
| 285 | printf("mmap() failed: %m\n"); | ||
| 286 | abort(); | ||
| 287 | } | ||
| 288 | *(char *)p = 0; | ||
| 289 | munmap(p, MFD_DEF_SIZE); | ||
| 290 | |||
| 291 | /* verify PROT_READ with MAP_SHARED is allowed and a following | ||
| 292 | * mprotect(PROT_WRITE) allows writing */ | ||
| 293 | p = mmap(NULL, | ||
| 294 | MFD_DEF_SIZE, | ||
| 295 | PROT_READ, | ||
| 296 | MAP_SHARED, | ||
| 297 | fd, | ||
| 298 | 0); | ||
| 299 | if (p == MAP_FAILED) { | ||
| 300 | printf("mmap() failed: %m\n"); | ||
| 301 | abort(); | ||
| 302 | } | ||
| 303 | |||
| 304 | r = mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE); | ||
| 305 | if (r < 0) { | ||
| 306 | printf("mprotect() failed: %m\n"); | ||
| 307 | abort(); | ||
| 308 | } | ||
| 309 | |||
| 310 | *(char *)p = 0; | ||
| 311 | munmap(p, MFD_DEF_SIZE); | ||
| 312 | |||
| 313 | /* verify PUNCH_HOLE works */ | ||
| 314 | r = fallocate(fd, | ||
| 315 | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, | ||
| 316 | 0, | ||
| 317 | MFD_DEF_SIZE); | ||
| 318 | if (r < 0) { | ||
| 319 | printf("fallocate(PUNCH_HOLE) failed: %m\n"); | ||
| 320 | abort(); | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | static void mfd_fail_write(int fd) | ||
| 325 | { | ||
| 326 | ssize_t l; | ||
| 327 | void *p; | ||
| 328 | int r; | ||
| 329 | |||
| 330 | /* verify write() fails */ | ||
| 331 | l = write(fd, "data", 4); | ||
| 332 | if (l != -EPERM) { | ||
| 333 | printf("expected EPERM on write(), but got %d: %m\n", (int)l); | ||
| 334 | abort(); | ||
| 335 | } | ||
| 336 | |||
| 337 | /* verify PROT_READ | PROT_WRITE is not allowed */ | ||
| 338 | p = mmap(NULL, | ||
| 339 | MFD_DEF_SIZE, | ||
| 340 | PROT_READ | PROT_WRITE, | ||
| 341 | MAP_SHARED, | ||
| 342 | fd, | ||
| 343 | 0); | ||
| 344 | if (p != MAP_FAILED) { | ||
| 345 | printf("mmap() didn't fail as expected\n"); | ||
| 346 | abort(); | ||
| 347 | } | ||
| 348 | |||
| 349 | /* verify PROT_WRITE is not allowed */ | ||
| 350 | p = mmap(NULL, | ||
| 351 | MFD_DEF_SIZE, | ||
| 352 | PROT_WRITE, | ||
| 353 | MAP_SHARED, | ||
| 354 | fd, | ||
| 355 | 0); | ||
| 356 | if (p != MAP_FAILED) { | ||
| 357 | printf("mmap() didn't fail as expected\n"); | ||
| 358 | abort(); | ||
| 359 | } | ||
| 360 | |||
| 361 | /* Verify PROT_READ with MAP_SHARED with a following mprotect is not | ||
| 362 | * allowed. Note that for r/w the kernel already prevents the mmap. */ | ||
| 363 | p = mmap(NULL, | ||
| 364 | MFD_DEF_SIZE, | ||
| 365 | PROT_READ, | ||
| 366 | MAP_SHARED, | ||
| 367 | fd, | ||
| 368 | 0); | ||
| 369 | if (p != MAP_FAILED) { | ||
| 370 | r = mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE); | ||
| 371 | if (r >= 0) { | ||
| 372 | printf("mmap()+mprotect() didn't fail as expected\n"); | ||
| 373 | abort(); | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | /* verify PUNCH_HOLE fails */ | ||
| 378 | r = fallocate(fd, | ||
| 379 | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, | ||
| 380 | 0, | ||
| 381 | MFD_DEF_SIZE); | ||
| 382 | if (r >= 0) { | ||
| 383 | printf("fallocate(PUNCH_HOLE) didn't fail as expected\n"); | ||
| 384 | abort(); | ||
| 385 | } | ||
| 386 | } | ||
| 387 | |||
| 388 | static void mfd_assert_shrink(int fd) | ||
| 389 | { | ||
| 390 | int r, fd2; | ||
| 391 | |||
| 392 | r = ftruncate(fd, MFD_DEF_SIZE / 2); | ||
| 393 | if (r < 0) { | ||
| 394 | printf("ftruncate(SHRINK) failed: %m\n"); | ||
| 395 | abort(); | ||
| 396 | } | ||
| 397 | |||
| 398 | mfd_assert_size(fd, MFD_DEF_SIZE / 2); | ||
| 399 | |||
| 400 | fd2 = mfd_assert_open(fd, | ||
| 401 | O_RDWR | O_CREAT | O_TRUNC, | ||
| 402 | S_IRUSR | S_IWUSR); | ||
| 403 | close(fd2); | ||
| 404 | |||
| 405 | mfd_assert_size(fd, 0); | ||
| 406 | } | ||
| 407 | |||
| 408 | static void mfd_fail_shrink(int fd) | ||
| 409 | { | ||
| 410 | int r; | ||
| 411 | |||
| 412 | r = ftruncate(fd, MFD_DEF_SIZE / 2); | ||
| 413 | if (r >= 0) { | ||
| 414 | printf("ftruncate(SHRINK) didn't fail as expected\n"); | ||
| 415 | abort(); | ||
| 416 | } | ||
| 417 | |||
| 418 | mfd_fail_open(fd, | ||
| 419 | O_RDWR | O_CREAT | O_TRUNC, | ||
| 420 | S_IRUSR | S_IWUSR); | ||
| 421 | } | ||
| 422 | |||
| 423 | static void mfd_assert_grow(int fd) | ||
| 424 | { | ||
| 425 | int r; | ||
| 426 | |||
| 427 | r = ftruncate(fd, MFD_DEF_SIZE * 2); | ||
| 428 | if (r < 0) { | ||
| 429 | printf("ftruncate(GROW) failed: %m\n"); | ||
| 430 | abort(); | ||
| 431 | } | ||
| 432 | |||
| 433 | mfd_assert_size(fd, MFD_DEF_SIZE * 2); | ||
| 434 | |||
| 435 | r = fallocate(fd, | ||
| 436 | 0, | ||
| 437 | 0, | ||
| 438 | MFD_DEF_SIZE * 4); | ||
| 439 | if (r < 0) { | ||
| 440 | printf("fallocate(ALLOC) failed: %m\n"); | ||
| 441 | abort(); | ||
| 442 | } | ||
| 443 | |||
| 444 | mfd_assert_size(fd, MFD_DEF_SIZE * 4); | ||
| 445 | } | ||
| 446 | |||
| 447 | static void mfd_fail_grow(int fd) | ||
| 448 | { | ||
| 449 | int r; | ||
| 450 | |||
| 451 | r = ftruncate(fd, MFD_DEF_SIZE * 2); | ||
| 452 | if (r >= 0) { | ||
| 453 | printf("ftruncate(GROW) didn't fail as expected\n"); | ||
| 454 | abort(); | ||
| 455 | } | ||
| 456 | |||
| 457 | r = fallocate(fd, | ||
| 458 | 0, | ||
| 459 | 0, | ||
| 460 | MFD_DEF_SIZE * 4); | ||
| 461 | if (r >= 0) { | ||
| 462 | printf("fallocate(ALLOC) didn't fail as expected\n"); | ||
| 463 | abort(); | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | static void mfd_assert_grow_write(int fd) | ||
| 468 | { | ||
| 469 | static char buf[MFD_DEF_SIZE * 8]; | ||
| 470 | ssize_t l; | ||
| 471 | |||
| 472 | l = pwrite(fd, buf, sizeof(buf), 0); | ||
| 473 | if (l != sizeof(buf)) { | ||
| 474 | printf("pwrite() failed: %m\n"); | ||
| 475 | abort(); | ||
| 476 | } | ||
| 477 | |||
| 478 | mfd_assert_size(fd, MFD_DEF_SIZE * 8); | ||
| 479 | } | ||
| 480 | |||
| 481 | static void mfd_fail_grow_write(int fd) | ||
| 482 | { | ||
| 483 | static char buf[MFD_DEF_SIZE * 8]; | ||
| 484 | ssize_t l; | ||
| 485 | |||
| 486 | l = pwrite(fd, buf, sizeof(buf), 0); | ||
| 487 | if (l == sizeof(buf)) { | ||
| 488 | printf("pwrite() didn't fail as expected\n"); | ||
| 489 | abort(); | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | static int idle_thread_fn(void *arg) | ||
| 494 | { | ||
| 495 | sigset_t set; | ||
| 496 | int sig; | ||
| 497 | |||
| 498 | /* dummy waiter; SIGTERM terminates us anyway */ | ||
| 499 | sigemptyset(&set); | ||
| 500 | sigaddset(&set, SIGTERM); | ||
| 501 | sigwait(&set, &sig); | ||
| 502 | |||
| 503 | return 0; | ||
| 504 | } | ||
| 505 | |||
| 506 | static pid_t spawn_idle_thread(unsigned int flags) | ||
| 507 | { | ||
| 508 | uint8_t *stack; | ||
| 509 | pid_t pid; | ||
| 510 | |||
| 511 | stack = malloc(STACK_SIZE); | ||
| 512 | if (!stack) { | ||
| 513 | printf("malloc(STACK_SIZE) failed: %m\n"); | ||
| 514 | abort(); | ||
| 515 | } | ||
| 516 | |||
| 517 | pid = clone(idle_thread_fn, | ||
| 518 | stack + STACK_SIZE, | ||
| 519 | SIGCHLD | flags, | ||
| 520 | NULL); | ||
| 521 | if (pid < 0) { | ||
| 522 | printf("clone() failed: %m\n"); | ||
| 523 | abort(); | ||
| 524 | } | ||
| 525 | |||
| 526 | return pid; | ||
| 527 | } | ||
| 528 | |||
| 529 | static void join_idle_thread(pid_t pid) | ||
| 530 | { | ||
| 531 | kill(pid, SIGTERM); | ||
| 532 | waitpid(pid, NULL, 0); | ||
| 533 | } | ||
| 534 | |||
| 535 | /* | ||
| 536 | * Test memfd_create() syscall | ||
| 537 | * Verify syscall-argument validation, including name checks, flag validation | ||
| 538 | * and more. | ||
| 539 | */ | ||
| 540 | static void test_create(void) | ||
| 541 | { | ||
| 542 | char buf[2048]; | ||
| 543 | int fd; | ||
| 544 | |||
| 545 | /* test NULL name */ | ||
| 546 | mfd_fail_new(NULL, 0); | ||
| 547 | |||
| 548 | /* test over-long name (not zero-terminated) */ | ||
| 549 | memset(buf, 0xff, sizeof(buf)); | ||
| 550 | mfd_fail_new(buf, 0); | ||
| 551 | |||
| 552 | /* test over-long zero-terminated name */ | ||
| 553 | memset(buf, 0xff, sizeof(buf)); | ||
| 554 | buf[sizeof(buf) - 1] = 0; | ||
| 555 | mfd_fail_new(buf, 0); | ||
| 556 | |||
| 557 | /* verify "" is a valid name */ | ||
| 558 | fd = mfd_assert_new("", 0, 0); | ||
| 559 | close(fd); | ||
| 560 | |||
| 561 | /* verify invalid O_* open flags */ | ||
| 562 | mfd_fail_new("", 0x0100); | ||
| 563 | mfd_fail_new("", ~MFD_CLOEXEC); | ||
| 564 | mfd_fail_new("", ~MFD_ALLOW_SEALING); | ||
| 565 | mfd_fail_new("", ~0); | ||
| 566 | mfd_fail_new("", 0x80000000U); | ||
| 567 | |||
| 568 | /* verify MFD_CLOEXEC is allowed */ | ||
| 569 | fd = mfd_assert_new("", 0, MFD_CLOEXEC); | ||
| 570 | close(fd); | ||
| 571 | |||
| 572 | /* verify MFD_ALLOW_SEALING is allowed */ | ||
| 573 | fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING); | ||
| 574 | close(fd); | ||
| 575 | |||
| 576 | /* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */ | ||
| 577 | fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC); | ||
| 578 | close(fd); | ||
| 579 | } | ||
| 580 | |||
| 581 | /* | ||
| 582 | * Test basic sealing | ||
| 583 | * A very basic sealing test to see whether setting/retrieving seals works. | ||
| 584 | */ | ||
| 585 | static void test_basic(void) | ||
| 586 | { | ||
| 587 | int fd; | ||
| 588 | |||
| 589 | fd = mfd_assert_new("kern_memfd_basic", | ||
| 590 | MFD_DEF_SIZE, | ||
| 591 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 592 | |||
| 593 | /* add basic seals */ | ||
| 594 | mfd_assert_has_seals(fd, 0); | ||
| 595 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | | ||
| 596 | F_SEAL_WRITE); | ||
| 597 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | ||
| 598 | F_SEAL_WRITE); | ||
| 599 | |||
| 600 | /* add them again */ | ||
| 601 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | | ||
| 602 | F_SEAL_WRITE); | ||
| 603 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | ||
| 604 | F_SEAL_WRITE); | ||
| 605 | |||
| 606 | /* add more seals and seal against sealing */ | ||
| 607 | mfd_assert_add_seals(fd, F_SEAL_GROW | F_SEAL_SEAL); | ||
| 608 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | | ||
| 609 | F_SEAL_GROW | | ||
| 610 | F_SEAL_WRITE | | ||
| 611 | F_SEAL_SEAL); | ||
| 612 | |||
| 613 | /* verify that sealing no longer works */ | ||
| 614 | mfd_fail_add_seals(fd, F_SEAL_GROW); | ||
| 615 | mfd_fail_add_seals(fd, 0); | ||
| 616 | |||
| 617 | close(fd); | ||
| 618 | |||
| 619 | /* verify sealing does not work without MFD_ALLOW_SEALING */ | ||
| 620 | fd = mfd_assert_new("kern_memfd_basic", | ||
| 621 | MFD_DEF_SIZE, | ||
| 622 | MFD_CLOEXEC); | ||
| 623 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | ||
| 624 | mfd_fail_add_seals(fd, F_SEAL_SHRINK | | ||
| 625 | F_SEAL_GROW | | ||
| 626 | F_SEAL_WRITE); | ||
| 627 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | ||
| 628 | close(fd); | ||
| 629 | } | ||
| 630 | |||
| 631 | /* | ||
| 632 | * Test SEAL_WRITE | ||
| 633 | * Test whether SEAL_WRITE actually prevents modifications. | ||
| 634 | */ | ||
| 635 | static void test_seal_write(void) | ||
| 636 | { | ||
| 637 | int fd; | ||
| 638 | |||
| 639 | fd = mfd_assert_new("kern_memfd_seal_write", | ||
| 640 | MFD_DEF_SIZE, | ||
| 641 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 642 | mfd_assert_has_seals(fd, 0); | ||
| 643 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | ||
| 644 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | ||
| 645 | |||
| 646 | mfd_assert_read(fd); | ||
| 647 | mfd_fail_write(fd); | ||
| 648 | mfd_assert_shrink(fd); | ||
| 649 | mfd_assert_grow(fd); | ||
| 650 | mfd_fail_grow_write(fd); | ||
| 651 | |||
| 652 | close(fd); | ||
| 653 | } | ||
| 654 | |||
| 655 | /* | ||
| 656 | * Test SEAL_SHRINK | ||
| 657 | * Test whether SEAL_SHRINK actually prevents shrinking | ||
| 658 | */ | ||
| 659 | static void test_seal_shrink(void) | ||
| 660 | { | ||
| 661 | int fd; | ||
| 662 | |||
| 663 | fd = mfd_assert_new("kern_memfd_seal_shrink", | ||
| 664 | MFD_DEF_SIZE, | ||
| 665 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 666 | mfd_assert_has_seals(fd, 0); | ||
| 667 | mfd_assert_add_seals(fd, F_SEAL_SHRINK); | ||
| 668 | mfd_assert_has_seals(fd, F_SEAL_SHRINK); | ||
| 669 | |||
| 670 | mfd_assert_read(fd); | ||
| 671 | mfd_assert_write(fd); | ||
| 672 | mfd_fail_shrink(fd); | ||
| 673 | mfd_assert_grow(fd); | ||
| 674 | mfd_assert_grow_write(fd); | ||
| 675 | |||
| 676 | close(fd); | ||
| 677 | } | ||
| 678 | |||
| 679 | /* | ||
| 680 | * Test SEAL_GROW | ||
| 681 | * Test whether SEAL_GROW actually prevents growing | ||
| 682 | */ | ||
| 683 | static void test_seal_grow(void) | ||
| 684 | { | ||
| 685 | int fd; | ||
| 686 | |||
| 687 | fd = mfd_assert_new("kern_memfd_seal_grow", | ||
| 688 | MFD_DEF_SIZE, | ||
| 689 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 690 | mfd_assert_has_seals(fd, 0); | ||
| 691 | mfd_assert_add_seals(fd, F_SEAL_GROW); | ||
| 692 | mfd_assert_has_seals(fd, F_SEAL_GROW); | ||
| 693 | |||
| 694 | mfd_assert_read(fd); | ||
| 695 | mfd_assert_write(fd); | ||
| 696 | mfd_assert_shrink(fd); | ||
| 697 | mfd_fail_grow(fd); | ||
| 698 | mfd_fail_grow_write(fd); | ||
| 699 | |||
| 700 | close(fd); | ||
| 701 | } | ||
| 702 | |||
| 703 | /* | ||
| 704 | * Test SEAL_SHRINK | SEAL_GROW | ||
| 705 | * Test whether SEAL_SHRINK | SEAL_GROW actually prevents resizing | ||
| 706 | */ | ||
| 707 | static void test_seal_resize(void) | ||
| 708 | { | ||
| 709 | int fd; | ||
| 710 | |||
| 711 | fd = mfd_assert_new("kern_memfd_seal_resize", | ||
| 712 | MFD_DEF_SIZE, | ||
| 713 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 714 | mfd_assert_has_seals(fd, 0); | ||
| 715 | mfd_assert_add_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW); | ||
| 716 | mfd_assert_has_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW); | ||
| 717 | |||
| 718 | mfd_assert_read(fd); | ||
| 719 | mfd_assert_write(fd); | ||
| 720 | mfd_fail_shrink(fd); | ||
| 721 | mfd_fail_grow(fd); | ||
| 722 | mfd_fail_grow_write(fd); | ||
| 723 | |||
| 724 | close(fd); | ||
| 725 | } | ||
| 726 | |||
| 727 | /* | ||
| 728 | * Test sharing via dup() | ||
| 729 | * Test that seals are shared between dupped FDs and they're all equal. | ||
| 730 | */ | ||
| 731 | static void test_share_dup(void) | ||
| 732 | { | ||
| 733 | int fd, fd2; | ||
| 734 | |||
| 735 | fd = mfd_assert_new("kern_memfd_share_dup", | ||
| 736 | MFD_DEF_SIZE, | ||
| 737 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 738 | mfd_assert_has_seals(fd, 0); | ||
| 739 | |||
| 740 | fd2 = mfd_assert_dup(fd); | ||
| 741 | mfd_assert_has_seals(fd2, 0); | ||
| 742 | |||
| 743 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | ||
| 744 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | ||
| 745 | mfd_assert_has_seals(fd2, F_SEAL_WRITE); | ||
| 746 | |||
| 747 | mfd_assert_add_seals(fd2, F_SEAL_SHRINK); | ||
| 748 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | ||
| 749 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | ||
| 750 | |||
| 751 | mfd_assert_add_seals(fd, F_SEAL_SEAL); | ||
| 752 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | ||
| 753 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | ||
| 754 | |||
| 755 | mfd_fail_add_seals(fd, F_SEAL_GROW); | ||
| 756 | mfd_fail_add_seals(fd2, F_SEAL_GROW); | ||
| 757 | mfd_fail_add_seals(fd, F_SEAL_SEAL); | ||
| 758 | mfd_fail_add_seals(fd2, F_SEAL_SEAL); | ||
| 759 | |||
| 760 | close(fd2); | ||
| 761 | |||
| 762 | mfd_fail_add_seals(fd, F_SEAL_GROW); | ||
| 763 | close(fd); | ||
| 764 | } | ||
| 765 | |||
| 766 | /* | ||
| 767 | * Test sealing with active mmap()s | ||
| 768 | * Modifying seals is only allowed if no other mmap() refs exist. | ||
| 769 | */ | ||
| 770 | static void test_share_mmap(void) | ||
| 771 | { | ||
| 772 | int fd; | ||
| 773 | void *p; | ||
| 774 | |||
| 775 | fd = mfd_assert_new("kern_memfd_share_mmap", | ||
| 776 | MFD_DEF_SIZE, | ||
| 777 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 778 | mfd_assert_has_seals(fd, 0); | ||
| 779 | |||
| 780 | /* shared/writable ref prevents sealing WRITE, but allows others */ | ||
| 781 | p = mfd_assert_mmap_shared(fd); | ||
| 782 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | ||
| 783 | mfd_assert_has_seals(fd, 0); | ||
| 784 | mfd_assert_add_seals(fd, F_SEAL_SHRINK); | ||
| 785 | mfd_assert_has_seals(fd, F_SEAL_SHRINK); | ||
| 786 | munmap(p, MFD_DEF_SIZE); | ||
| 787 | |||
| 788 | /* readable ref allows sealing */ | ||
| 789 | p = mfd_assert_mmap_private(fd); | ||
| 790 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | ||
| 791 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | ||
| 792 | munmap(p, MFD_DEF_SIZE); | ||
| 793 | |||
| 794 | close(fd); | ||
| 795 | } | ||
| 796 | |||
| 797 | /* | ||
| 798 | * Test sealing with open(/proc/self/fd/%d) | ||
| 799 | * Via /proc we can get access to a separate file-context for the same memfd. | ||
| 800 | * This is *not* like dup(), but like a real separate open(). Make sure the | ||
| 801 | * semantics are as expected and we correctly check for RDONLY / WRONLY / RDWR. | ||
| 802 | */ | ||
| 803 | static void test_share_open(void) | ||
| 804 | { | ||
| 805 | int fd, fd2; | ||
| 806 | |||
| 807 | fd = mfd_assert_new("kern_memfd_share_open", | ||
| 808 | MFD_DEF_SIZE, | ||
| 809 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 810 | mfd_assert_has_seals(fd, 0); | ||
| 811 | |||
| 812 | fd2 = mfd_assert_open(fd, O_RDWR, 0); | ||
| 813 | mfd_assert_add_seals(fd, F_SEAL_WRITE); | ||
| 814 | mfd_assert_has_seals(fd, F_SEAL_WRITE); | ||
| 815 | mfd_assert_has_seals(fd2, F_SEAL_WRITE); | ||
| 816 | |||
| 817 | mfd_assert_add_seals(fd2, F_SEAL_SHRINK); | ||
| 818 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | ||
| 819 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | ||
| 820 | |||
| 821 | close(fd); | ||
| 822 | fd = mfd_assert_open(fd2, O_RDONLY, 0); | ||
| 823 | |||
| 824 | mfd_fail_add_seals(fd, F_SEAL_SEAL); | ||
| 825 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); | ||
| 826 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK); | ||
| 827 | |||
| 828 | close(fd2); | ||
| 829 | fd2 = mfd_assert_open(fd, O_RDWR, 0); | ||
| 830 | |||
| 831 | mfd_assert_add_seals(fd2, F_SEAL_SEAL); | ||
| 832 | mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | ||
| 833 | mfd_assert_has_seals(fd2, F_SEAL_WRITE | F_SEAL_SHRINK | F_SEAL_SEAL); | ||
| 834 | |||
| 835 | close(fd2); | ||
| 836 | close(fd); | ||
| 837 | } | ||
| 838 | |||
| 839 | /* | ||
| 840 | * Test sharing via fork() | ||
| 841 | * Test whether seal-modifications work as expected with forked childs. | ||
| 842 | */ | ||
| 843 | static void test_share_fork(void) | ||
| 844 | { | ||
| 845 | int fd; | ||
| 846 | pid_t pid; | ||
| 847 | |||
| 848 | fd = mfd_assert_new("kern_memfd_share_fork", | ||
| 849 | MFD_DEF_SIZE, | ||
| 850 | MFD_CLOEXEC | MFD_ALLOW_SEALING); | ||
| 851 | mfd_assert_has_seals(fd, 0); | ||
| 852 | |||
| 853 | pid = spawn_idle_thread(0); | ||
| 854 | mfd_assert_add_seals(fd, F_SEAL_SEAL); | ||
| 855 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | ||
| 856 | |||
| 857 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | ||
| 858 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | ||
| 859 | |||
| 860 | join_idle_thread(pid); | ||
| 861 | |||
| 862 | mfd_fail_add_seals(fd, F_SEAL_WRITE); | ||
| 863 | mfd_assert_has_seals(fd, F_SEAL_SEAL); | ||
| 864 | |||
| 865 | close(fd); | ||
| 866 | } | ||
| 867 | |||
| 868 | int main(int argc, char **argv) | ||
| 869 | { | ||
| 870 | pid_t pid; | ||
| 871 | |||
| 872 | printf("memfd: CREATE\n"); | ||
| 873 | test_create(); | ||
| 874 | printf("memfd: BASIC\n"); | ||
| 875 | test_basic(); | ||
| 876 | |||
| 877 | printf("memfd: SEAL-WRITE\n"); | ||
| 878 | test_seal_write(); | ||
| 879 | printf("memfd: SEAL-SHRINK\n"); | ||
| 880 | test_seal_shrink(); | ||
| 881 | printf("memfd: SEAL-GROW\n"); | ||
| 882 | test_seal_grow(); | ||
| 883 | printf("memfd: SEAL-RESIZE\n"); | ||
| 884 | test_seal_resize(); | ||
| 885 | |||
| 886 | printf("memfd: SHARE-DUP\n"); | ||
| 887 | test_share_dup(); | ||
| 888 | printf("memfd: SHARE-MMAP\n"); | ||
| 889 | test_share_mmap(); | ||
| 890 | printf("memfd: SHARE-OPEN\n"); | ||
| 891 | test_share_open(); | ||
| 892 | printf("memfd: SHARE-FORK\n"); | ||
| 893 | test_share_fork(); | ||
| 894 | |||
| 895 | /* Run test-suite in a multi-threaded environment with a shared | ||
| 896 | * file-table. */ | ||
| 897 | pid = spawn_idle_thread(CLONE_FILES | CLONE_FS | CLONE_VM); | ||
| 898 | printf("memfd: SHARE-DUP (shared file-table)\n"); | ||
| 899 | test_share_dup(); | ||
| 900 | printf("memfd: SHARE-MMAP (shared file-table)\n"); | ||
| 901 | test_share_mmap(); | ||
| 902 | printf("memfd: SHARE-OPEN (shared file-table)\n"); | ||
| 903 | test_share_open(); | ||
| 904 | printf("memfd: SHARE-FORK (shared file-table)\n"); | ||
| 905 | test_share_fork(); | ||
| 906 | join_idle_thread(pid); | ||
| 907 | |||
| 908 | printf("memfd: DONE\n"); | ||
| 909 | |||
| 910 | return 0; | ||
| 911 | } | ||
diff --git a/tools/testing/selftests/memfd/run_fuse_test.sh b/tools/testing/selftests/memfd/run_fuse_test.sh new file mode 100644 index 000000000000..69b930e1e041 --- /dev/null +++ b/tools/testing/selftests/memfd/run_fuse_test.sh | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | if test -d "./mnt" ; then | ||
| 4 | fusermount -u ./mnt | ||
| 5 | rmdir ./mnt | ||
| 6 | fi | ||
| 7 | |||
| 8 | set -e | ||
| 9 | |||
| 10 | mkdir mnt | ||
| 11 | ./fuse_mnt ./mnt | ||
| 12 | ./fuse_test ./mnt/memfd | ||
| 13 | fusermount -u ./mnt | ||
| 14 | rmdir ./mnt | ||
diff --git a/tools/testing/selftests/mount/Makefile b/tools/testing/selftests/mount/Makefile new file mode 100644 index 000000000000..337d853c2b72 --- /dev/null +++ b/tools/testing/selftests/mount/Makefile | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # Makefile for mount selftests. | ||
| 2 | |||
| 3 | all: unprivileged-remount-test | ||
| 4 | |||
| 5 | unprivileged-remount-test: unprivileged-remount-test.c | ||
| 6 | gcc -Wall -O2 unprivileged-remount-test.c -o unprivileged-remount-test | ||
| 7 | |||
| 8 | # Allow specific tests to be selected. | ||
| 9 | test_unprivileged_remount: unprivileged-remount-test | ||
| 10 | @if [ -f /proc/self/uid_map ] ; then ./unprivileged-remount-test ; fi | ||
| 11 | |||
| 12 | run_tests: all test_unprivileged_remount | ||
| 13 | |||
| 14 | clean: | ||
| 15 | rm -f unprivileged-remount-test | ||
| 16 | |||
| 17 | .PHONY: all test_unprivileged_remount | ||
diff --git a/tools/testing/selftests/mount/unprivileged-remount-test.c b/tools/testing/selftests/mount/unprivileged-remount-test.c new file mode 100644 index 000000000000..1b3ff2fda4d0 --- /dev/null +++ b/tools/testing/selftests/mount/unprivileged-remount-test.c | |||
| @@ -0,0 +1,242 @@ | |||
| 1 | #define _GNU_SOURCE | ||
| 2 | #include <sched.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <errno.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <sys/types.h> | ||
| 7 | #include <sys/mount.h> | ||
| 8 | #include <sys/wait.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <unistd.h> | ||
| 11 | #include <fcntl.h> | ||
| 12 | #include <grp.h> | ||
| 13 | #include <stdbool.h> | ||
| 14 | #include <stdarg.h> | ||
| 15 | |||
| 16 | #ifndef CLONE_NEWNS | ||
| 17 | # define CLONE_NEWNS 0x00020000 | ||
| 18 | #endif | ||
| 19 | #ifndef CLONE_NEWUTS | ||
| 20 | # define CLONE_NEWUTS 0x04000000 | ||
| 21 | #endif | ||
| 22 | #ifndef CLONE_NEWIPC | ||
| 23 | # define CLONE_NEWIPC 0x08000000 | ||
| 24 | #endif | ||
| 25 | #ifndef CLONE_NEWNET | ||
| 26 | # define CLONE_NEWNET 0x40000000 | ||
| 27 | #endif | ||
| 28 | #ifndef CLONE_NEWUSER | ||
| 29 | # define CLONE_NEWUSER 0x10000000 | ||
| 30 | #endif | ||
| 31 | #ifndef CLONE_NEWPID | ||
| 32 | # define CLONE_NEWPID 0x20000000 | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #ifndef MS_RELATIME | ||
| 36 | #define MS_RELATIME (1 << 21) | ||
| 37 | #endif | ||
| 38 | #ifndef MS_STRICTATIME | ||
| 39 | #define MS_STRICTATIME (1 << 24) | ||
| 40 | #endif | ||
| 41 | |||
| 42 | static void die(char *fmt, ...) | ||
| 43 | { | ||
| 44 | va_list ap; | ||
| 45 | va_start(ap, fmt); | ||
| 46 | vfprintf(stderr, fmt, ap); | ||
| 47 | va_end(ap); | ||
| 48 | exit(EXIT_FAILURE); | ||
| 49 | } | ||
| 50 | |||
| 51 | static void write_file(char *filename, char *fmt, ...) | ||
| 52 | { | ||
| 53 | char buf[4096]; | ||
| 54 | int fd; | ||
| 55 | ssize_t written; | ||
| 56 | int buf_len; | ||
| 57 | va_list ap; | ||
| 58 | |||
| 59 | va_start(ap, fmt); | ||
| 60 | buf_len = vsnprintf(buf, sizeof(buf), fmt, ap); | ||
| 61 | va_end(ap); | ||
| 62 | if (buf_len < 0) { | ||
| 63 | die("vsnprintf failed: %s\n", | ||
| 64 | strerror(errno)); | ||
| 65 | } | ||
| 66 | if (buf_len >= sizeof(buf)) { | ||
| 67 | die("vsnprintf output truncated\n"); | ||
| 68 | } | ||
| 69 | |||
| 70 | fd = open(filename, O_WRONLY); | ||
| 71 | if (fd < 0) { | ||
| 72 | die("open of %s failed: %s\n", | ||
| 73 | filename, strerror(errno)); | ||
| 74 | } | ||
| 75 | written = write(fd, buf, buf_len); | ||
| 76 | if (written != buf_len) { | ||
| 77 | if (written >= 0) { | ||
| 78 | die("short write to %s\n", filename); | ||
| 79 | } else { | ||
| 80 | die("write to %s failed: %s\n", | ||
| 81 | filename, strerror(errno)); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | if (close(fd) != 0) { | ||
| 85 | die("close of %s failed: %s\n", | ||
| 86 | filename, strerror(errno)); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | static void create_and_enter_userns(void) | ||
| 91 | { | ||
| 92 | uid_t uid; | ||
| 93 | gid_t gid; | ||
| 94 | |||
| 95 | uid = getuid(); | ||
| 96 | gid = getgid(); | ||
| 97 | |||
| 98 | if (unshare(CLONE_NEWUSER) !=0) { | ||
| 99 | die("unshare(CLONE_NEWUSER) failed: %s\n", | ||
| 100 | strerror(errno)); | ||
| 101 | } | ||
| 102 | |||
| 103 | write_file("/proc/self/uid_map", "0 %d 1", uid); | ||
| 104 | write_file("/proc/self/gid_map", "0 %d 1", gid); | ||
| 105 | |||
| 106 | if (setgroups(0, NULL) != 0) { | ||
| 107 | die("setgroups failed: %s\n", | ||
| 108 | strerror(errno)); | ||
| 109 | } | ||
| 110 | if (setgid(0) != 0) { | ||
| 111 | die ("setgid(0) failed %s\n", | ||
| 112 | strerror(errno)); | ||
| 113 | } | ||
| 114 | if (setuid(0) != 0) { | ||
| 115 | die("setuid(0) failed %s\n", | ||
| 116 | strerror(errno)); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | static | ||
| 121 | bool test_unpriv_remount(int mount_flags, int remount_flags, int invalid_flags) | ||
| 122 | { | ||
| 123 | pid_t child; | ||
| 124 | |||
| 125 | child = fork(); | ||
| 126 | if (child == -1) { | ||
| 127 | die("fork failed: %s\n", | ||
| 128 | strerror(errno)); | ||
| 129 | } | ||
| 130 | if (child != 0) { /* parent */ | ||
| 131 | pid_t pid; | ||
| 132 | int status; | ||
| 133 | pid = waitpid(child, &status, 0); | ||
| 134 | if (pid == -1) { | ||
| 135 | die("waitpid failed: %s\n", | ||
| 136 | strerror(errno)); | ||
| 137 | } | ||
| 138 | if (pid != child) { | ||
| 139 | die("waited for %d got %d\n", | ||
| 140 | child, pid); | ||
| 141 | } | ||
| 142 | if (!WIFEXITED(status)) { | ||
| 143 | die("child did not terminate cleanly\n"); | ||
| 144 | } | ||
| 145 | return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; | ||
| 146 | } | ||
| 147 | |||
| 148 | create_and_enter_userns(); | ||
| 149 | if (unshare(CLONE_NEWNS) != 0) { | ||
| 150 | die("unshare(CLONE_NEWNS) failed: %s\n", | ||
| 151 | strerror(errno)); | ||
| 152 | } | ||
| 153 | |||
| 154 | if (mount("testing", "/tmp", "ramfs", mount_flags, NULL) != 0) { | ||
| 155 | die("mount of /tmp failed: %s\n", | ||
| 156 | strerror(errno)); | ||
| 157 | } | ||
| 158 | |||
| 159 | create_and_enter_userns(); | ||
| 160 | |||
| 161 | if (unshare(CLONE_NEWNS) != 0) { | ||
| 162 | die("unshare(CLONE_NEWNS) failed: %s\n", | ||
| 163 | strerror(errno)); | ||
| 164 | } | ||
| 165 | |||
| 166 | if (mount("/tmp", "/tmp", "none", | ||
| 167 | MS_REMOUNT | MS_BIND | remount_flags, NULL) != 0) { | ||
| 168 | /* system("cat /proc/self/mounts"); */ | ||
| 169 | die("remount of /tmp failed: %s\n", | ||
| 170 | strerror(errno)); | ||
| 171 | } | ||
| 172 | |||
| 173 | if (mount("/tmp", "/tmp", "none", | ||
| 174 | MS_REMOUNT | MS_BIND | invalid_flags, NULL) == 0) { | ||
| 175 | /* system("cat /proc/self/mounts"); */ | ||
| 176 | die("remount of /tmp with invalid flags " | ||
| 177 | "succeeded unexpectedly\n"); | ||
| 178 | } | ||
| 179 | exit(EXIT_SUCCESS); | ||
| 180 | } | ||
| 181 | |||
| 182 | static bool test_unpriv_remount_simple(int mount_flags) | ||
| 183 | { | ||
| 184 | return test_unpriv_remount(mount_flags, mount_flags, 0); | ||
| 185 | } | ||
| 186 | |||
| 187 | static bool test_unpriv_remount_atime(int mount_flags, int invalid_flags) | ||
| 188 | { | ||
| 189 | return test_unpriv_remount(mount_flags, mount_flags, invalid_flags); | ||
| 190 | } | ||
| 191 | |||
| 192 | int main(int argc, char **argv) | ||
| 193 | { | ||
| 194 | if (!test_unpriv_remount_simple(MS_RDONLY|MS_NODEV)) { | ||
| 195 | die("MS_RDONLY malfunctions\n"); | ||
| 196 | } | ||
| 197 | if (!test_unpriv_remount_simple(MS_NODEV)) { | ||
| 198 | die("MS_NODEV malfunctions\n"); | ||
| 199 | } | ||
| 200 | if (!test_unpriv_remount_simple(MS_NOSUID|MS_NODEV)) { | ||
| 201 | die("MS_NOSUID malfunctions\n"); | ||
| 202 | } | ||
| 203 | if (!test_unpriv_remount_simple(MS_NOEXEC|MS_NODEV)) { | ||
| 204 | die("MS_NOEXEC malfunctions\n"); | ||
| 205 | } | ||
| 206 | if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODEV, | ||
| 207 | MS_NOATIME|MS_NODEV)) | ||
| 208 | { | ||
| 209 | die("MS_RELATIME malfunctions\n"); | ||
| 210 | } | ||
| 211 | if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODEV, | ||
| 212 | MS_NOATIME|MS_NODEV)) | ||
| 213 | { | ||
| 214 | die("MS_STRICTATIME malfunctions\n"); | ||
| 215 | } | ||
| 216 | if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODEV, | ||
| 217 | MS_STRICTATIME|MS_NODEV)) | ||
| 218 | { | ||
| 219 | die("MS_RELATIME malfunctions\n"); | ||
| 220 | } | ||
| 221 | if (!test_unpriv_remount_atime(MS_RELATIME|MS_NODIRATIME|MS_NODEV, | ||
| 222 | MS_NOATIME|MS_NODEV)) | ||
| 223 | { | ||
| 224 | die("MS_RELATIME malfunctions\n"); | ||
| 225 | } | ||
| 226 | if (!test_unpriv_remount_atime(MS_STRICTATIME|MS_NODIRATIME|MS_NODEV, | ||
| 227 | MS_NOATIME|MS_NODEV)) | ||
| 228 | { | ||
| 229 | die("MS_RELATIME malfunctions\n"); | ||
| 230 | } | ||
| 231 | if (!test_unpriv_remount_atime(MS_NOATIME|MS_NODIRATIME|MS_NODEV, | ||
| 232 | MS_STRICTATIME|MS_NODEV)) | ||
| 233 | { | ||
| 234 | die("MS_RELATIME malfunctions\n"); | ||
| 235 | } | ||
| 236 | if (!test_unpriv_remount(MS_STRICTATIME|MS_NODEV, MS_NODEV, | ||
| 237 | MS_NOATIME|MS_NODEV)) | ||
| 238 | { | ||
| 239 | die("Default atime malfunctions\n"); | ||
| 240 | } | ||
| 241 | return EXIT_SUCCESS; | ||
| 242 | } | ||
diff --git a/tools/testing/selftests/net/psock_fanout.c b/tools/testing/selftests/net/psock_fanout.c index 57b9c2b7c4ff..6f6733331d95 100644 --- a/tools/testing/selftests/net/psock_fanout.c +++ b/tools/testing/selftests/net/psock_fanout.c | |||
| @@ -128,7 +128,7 @@ static int sock_fanout_read_ring(int fd, void *ring) | |||
| 128 | struct tpacket2_hdr *header = ring; | 128 | struct tpacket2_hdr *header = ring; |
| 129 | int count = 0; | 129 | int count = 0; |
| 130 | 130 | ||
| 131 | while (header->tp_status & TP_STATUS_USER && count < RING_NUM_FRAMES) { | 131 | while (count < RING_NUM_FRAMES && header->tp_status & TP_STATUS_USER) { |
| 132 | count++; | 132 | count++; |
| 133 | header = ring + (count * getpagesize()); | 133 | header = ring + (count * getpagesize()); |
| 134 | } | 134 | } |
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index 54833a791a44..f6ff90a76bd7 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile | |||
| @@ -13,14 +13,14 @@ 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 | ||
| 20 | all: | 20 | all: $(TARGETS) |
| 21 | @for TARGET in $(TARGETS); do \ | 21 | |
| 22 | $(MAKE) -C $$TARGET all; \ | 22 | $(TARGETS): |
| 23 | done; | 23 | $(MAKE) -k -C $@ all |
| 24 | 24 | ||
| 25 | run_tests: all | 25 | run_tests: all |
| 26 | @for TARGET in $(TARGETS); do \ | 26 | @for TARGET in $(TARGETS); do \ |
| @@ -36,4 +36,4 @@ clean: | |||
| 36 | tags: | 36 | tags: |
| 37 | find . -name '*.c' -o -name '*.h' | xargs ctags | 37 | find . -name '*.c' -o -name '*.h' | xargs ctags |
| 38 | 38 | ||
| 39 | .PHONY: all run_tests clean tags | 39 | .PHONY: all run_tests clean tags $(TARGETS) |
diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile index b9ff0db42c79..c9f4263906a5 100644 --- a/tools/testing/selftests/powerpc/pmu/Makefile +++ b/tools/testing/selftests/powerpc/pmu/Makefile | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | noarg: | 1 | noarg: |
| 2 | $(MAKE) -C ../ | 2 | $(MAKE) -C ../ |
| 3 | 3 | ||
| 4 | PROGS := count_instructions | 4 | PROGS := count_instructions l3_bank_test per_event_excludes |
| 5 | EXTRA_SOURCES := ../harness.c event.c | 5 | EXTRA_SOURCES := ../harness.c event.c lib.c |
| 6 | 6 | ||
| 7 | all: $(PROGS) sub_all | 7 | SUB_TARGETS = ebb |
| 8 | |||
| 9 | all: $(PROGS) $(SUB_TARGETS) | ||
| 8 | 10 | ||
| 9 | $(PROGS): $(EXTRA_SOURCES) | 11 | $(PROGS): $(EXTRA_SOURCES) |
| 10 | 12 | ||
| @@ -20,13 +22,8 @@ run_tests: all sub_run_tests | |||
| 20 | clean: sub_clean | 22 | clean: sub_clean |
| 21 | rm -f $(PROGS) loop.o | 23 | rm -f $(PROGS) loop.o |
| 22 | 24 | ||
| 23 | 25 | $(SUB_TARGETS): | |
| 24 | SUB_TARGETS = ebb | 26 | $(MAKE) -k -C $@ all |
| 25 | |||
| 26 | sub_all: | ||
| 27 | @for TARGET in $(SUB_TARGETS); do \ | ||
| 28 | $(MAKE) -C $$TARGET all; \ | ||
| 29 | done; | ||
| 30 | 27 | ||
| 31 | sub_run_tests: all | 28 | sub_run_tests: all |
| 32 | @for TARGET in $(SUB_TARGETS); do \ | 29 | @for TARGET in $(SUB_TARGETS); do \ |
| @@ -38,4 +35,4 @@ sub_clean: | |||
| 38 | $(MAKE) -C $$TARGET clean; \ | 35 | $(MAKE) -C $$TARGET clean; \ |
| 39 | done; | 36 | done; |
| 40 | 37 | ||
| 41 | .PHONY: all run_tests clean sub_all sub_run_tests sub_clean | 38 | .PHONY: all run_tests clean sub_run_tests sub_clean $(SUB_TARGETS) |
diff --git a/tools/testing/selftests/powerpc/pmu/count_instructions.c b/tools/testing/selftests/powerpc/pmu/count_instructions.c index 312b4f0fd27c..4622117b24c0 100644 --- a/tools/testing/selftests/powerpc/pmu/count_instructions.c +++ b/tools/testing/selftests/powerpc/pmu/count_instructions.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | #include "event.h" | 13 | #include "event.h" |
| 14 | #include "utils.h" | 14 | #include "utils.h" |
| 15 | #include "lib.h" | ||
| 15 | 16 | ||
| 16 | extern void thirty_two_instruction_loop(u64 loops); | 17 | extern void thirty_two_instruction_loop(u64 loops); |
| 17 | 18 | ||
| @@ -90,7 +91,7 @@ static u64 determine_overhead(struct event *events) | |||
| 90 | return overhead; | 91 | return overhead; |
| 91 | } | 92 | } |
| 92 | 93 | ||
| 93 | static int count_instructions(void) | 94 | static int test_body(void) |
| 94 | { | 95 | { |
| 95 | struct event events[2]; | 96 | struct event events[2]; |
| 96 | u64 overhead; | 97 | u64 overhead; |
| @@ -111,17 +112,23 @@ static int count_instructions(void) | |||
| 111 | overhead = determine_overhead(events); | 112 | overhead = determine_overhead(events); |
| 112 | printf("Overhead of null loop: %llu instructions\n", overhead); | 113 | printf("Overhead of null loop: %llu instructions\n", overhead); |
| 113 | 114 | ||
| 114 | /* Run for 1M instructions */ | 115 | /* Run for 1Mi instructions */ |
| 115 | FAIL_IF(do_count_loop(events, 0x100000, overhead, true)); | 116 | FAIL_IF(do_count_loop(events, 1000000, overhead, true)); |
| 117 | |||
| 118 | /* Run for 10Mi instructions */ | ||
| 119 | FAIL_IF(do_count_loop(events, 10000000, overhead, true)); | ||
| 120 | |||
| 121 | /* Run for 100Mi instructions */ | ||
| 122 | FAIL_IF(do_count_loop(events, 100000000, overhead, true)); | ||
| 116 | 123 | ||
| 117 | /* Run for 10M instructions */ | 124 | /* Run for 1Bi instructions */ |
| 118 | FAIL_IF(do_count_loop(events, 0xa00000, overhead, true)); | 125 | FAIL_IF(do_count_loop(events, 1000000000, overhead, true)); |
| 119 | 126 | ||
| 120 | /* Run for 100M instructions */ | 127 | /* Run for 16Bi instructions */ |
| 121 | FAIL_IF(do_count_loop(events, 0x6400000, overhead, true)); | 128 | FAIL_IF(do_count_loop(events, 16000000000, overhead, true)); |
| 122 | 129 | ||
| 123 | /* Run for 1G instructions */ | 130 | /* Run for 64Bi instructions */ |
| 124 | FAIL_IF(do_count_loop(events, 0x40000000, overhead, true)); | 131 | FAIL_IF(do_count_loop(events, 64000000000, overhead, true)); |
| 125 | 132 | ||
| 126 | event_close(&events[0]); | 133 | event_close(&events[0]); |
| 127 | event_close(&events[1]); | 134 | event_close(&events[1]); |
| @@ -129,6 +136,11 @@ static int count_instructions(void) | |||
| 129 | return 0; | 136 | return 0; |
| 130 | } | 137 | } |
| 131 | 138 | ||
| 139 | static int count_instructions(void) | ||
| 140 | { | ||
| 141 | return eat_cpu(test_body); | ||
| 142 | } | ||
| 143 | |||
| 132 | int main(void) | 144 | int main(void) |
| 133 | { | 145 | { |
| 134 | return test_harness(count_instructions, "count_instructions"); | 146 | return test_harness(count_instructions, "count_instructions"); |
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile index edbba2affc2c..3dc4332698cb 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/Makefile +++ b/tools/testing/selftests/powerpc/pmu/ebb/Makefile | |||
| @@ -13,11 +13,12 @@ PROGS := reg_access_test event_attributes_test cycles_test \ | |||
| 13 | close_clears_pmcc_test instruction_count_test \ | 13 | close_clears_pmcc_test instruction_count_test \ |
| 14 | fork_cleanup_test ebb_on_child_test \ | 14 | fork_cleanup_test ebb_on_child_test \ |
| 15 | ebb_on_willing_child_test back_to_back_ebbs_test \ | 15 | ebb_on_willing_child_test back_to_back_ebbs_test \ |
| 16 | lost_exception_test no_handler_test | 16 | lost_exception_test no_handler_test \ |
| 17 | cycles_with_mmcr2_test | ||
| 17 | 18 | ||
| 18 | all: $(PROGS) | 19 | all: $(PROGS) |
| 19 | 20 | ||
| 20 | $(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c | 21 | $(PROGS): ../../harness.c ../event.c ../lib.c ebb.c ebb_handler.S trace.c busy_loop.S |
| 21 | 22 | ||
| 22 | instruction_count_test: ../loop.S | 23 | instruction_count_test: ../loop.S |
| 23 | 24 | ||
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S b/tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S new file mode 100644 index 000000000000..c7e4093f1cd3 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/busy_loop.S | |||
| @@ -0,0 +1,271 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2014, Michael Ellerman, IBM Corp. | ||
| 3 | * Licensed under GPLv2. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <ppc-asm.h> | ||
| 7 | |||
| 8 | .text | ||
| 9 | |||
| 10 | FUNC_START(core_busy_loop) | ||
| 11 | stdu %r1, -168(%r1) | ||
| 12 | std r14, 160(%r1) | ||
| 13 | std r15, 152(%r1) | ||
| 14 | std r16, 144(%r1) | ||
| 15 | std r17, 136(%r1) | ||
| 16 | std r18, 128(%r1) | ||
| 17 | std r19, 120(%r1) | ||
| 18 | std r20, 112(%r1) | ||
| 19 | std r21, 104(%r1) | ||
| 20 | std r22, 96(%r1) | ||
| 21 | std r23, 88(%r1) | ||
| 22 | std r24, 80(%r1) | ||
| 23 | std r25, 72(%r1) | ||
| 24 | std r26, 64(%r1) | ||
| 25 | std r27, 56(%r1) | ||
| 26 | std r28, 48(%r1) | ||
| 27 | std r29, 40(%r1) | ||
| 28 | std r30, 32(%r1) | ||
| 29 | std r31, 24(%r1) | ||
| 30 | |||
| 31 | li r3, 0x3030 | ||
| 32 | std r3, -96(%r1) | ||
| 33 | li r4, 0x4040 | ||
| 34 | std r4, -104(%r1) | ||
| 35 | li r5, 0x5050 | ||
| 36 | std r5, -112(%r1) | ||
| 37 | li r6, 0x6060 | ||
| 38 | std r6, -120(%r1) | ||
| 39 | li r7, 0x7070 | ||
| 40 | std r7, -128(%r1) | ||
| 41 | li r8, 0x0808 | ||
| 42 | std r8, -136(%r1) | ||
| 43 | li r9, 0x0909 | ||
| 44 | std r9, -144(%r1) | ||
| 45 | li r10, 0x1010 | ||
| 46 | std r10, -152(%r1) | ||
| 47 | li r11, 0x1111 | ||
| 48 | std r11, -160(%r1) | ||
| 49 | li r14, 0x1414 | ||
| 50 | std r14, -168(%r1) | ||
| 51 | li r15, 0x1515 | ||
| 52 | std r15, -176(%r1) | ||
| 53 | li r16, 0x1616 | ||
| 54 | std r16, -184(%r1) | ||
| 55 | li r17, 0x1717 | ||
| 56 | std r17, -192(%r1) | ||
| 57 | li r18, 0x1818 | ||
| 58 | std r18, -200(%r1) | ||
| 59 | li r19, 0x1919 | ||
| 60 | std r19, -208(%r1) | ||
| 61 | li r20, 0x2020 | ||
| 62 | std r20, -216(%r1) | ||
| 63 | li r21, 0x2121 | ||
| 64 | std r21, -224(%r1) | ||
| 65 | li r22, 0x2222 | ||
| 66 | std r22, -232(%r1) | ||
| 67 | li r23, 0x2323 | ||
| 68 | std r23, -240(%r1) | ||
| 69 | li r24, 0x2424 | ||
| 70 | std r24, -248(%r1) | ||
| 71 | li r25, 0x2525 | ||
| 72 | std r25, -256(%r1) | ||
| 73 | li r26, 0x2626 | ||
| 74 | std r26, -264(%r1) | ||
| 75 | li r27, 0x2727 | ||
| 76 | std r27, -272(%r1) | ||
| 77 | li r28, 0x2828 | ||
| 78 | std r28, -280(%r1) | ||
| 79 | li r29, 0x2929 | ||
| 80 | std r29, -288(%r1) | ||
| 81 | li r30, 0x3030 | ||
| 82 | li r31, 0x3131 | ||
| 83 | |||
| 84 | li r3, 0 | ||
| 85 | 0: addi r3, r3, 1 | ||
| 86 | cmpwi r3, 100 | ||
| 87 | blt 0b | ||
| 88 | |||
| 89 | /* Return 1 (fail) unless we get through all the checks */ | ||
| 90 | li r3, 1 | ||
| 91 | |||
| 92 | /* Check none of our registers have been corrupted */ | ||
| 93 | cmpwi r4, 0x4040 | ||
| 94 | bne 1f | ||
| 95 | cmpwi r5, 0x5050 | ||
| 96 | bne 1f | ||
| 97 | cmpwi r6, 0x6060 | ||
| 98 | bne 1f | ||
| 99 | cmpwi r7, 0x7070 | ||
| 100 | bne 1f | ||
| 101 | cmpwi r8, 0x0808 | ||
| 102 | bne 1f | ||
| 103 | cmpwi r9, 0x0909 | ||
| 104 | bne 1f | ||
| 105 | cmpwi r10, 0x1010 | ||
| 106 | bne 1f | ||
| 107 | cmpwi r11, 0x1111 | ||
| 108 | bne 1f | ||
| 109 | cmpwi r14, 0x1414 | ||
| 110 | bne 1f | ||
| 111 | cmpwi r15, 0x1515 | ||
| 112 | bne 1f | ||
| 113 | cmpwi r16, 0x1616 | ||
| 114 | bne 1f | ||
| 115 | cmpwi r17, 0x1717 | ||
| 116 | bne 1f | ||
| 117 | cmpwi r18, 0x1818 | ||
| 118 | bne 1f | ||
| 119 | cmpwi r19, 0x1919 | ||
| 120 | bne 1f | ||
| 121 | cmpwi r20, 0x2020 | ||
| 122 | bne 1f | ||
| 123 | cmpwi r21, 0x2121 | ||
| 124 | bne 1f | ||
| 125 | cmpwi r22, 0x2222 | ||
| 126 | bne 1f | ||
| 127 | cmpwi r23, 0x2323 | ||
| 128 | bne 1f | ||
| 129 | cmpwi r24, 0x2424 | ||
| 130 | bne 1f | ||
| 131 | cmpwi r25, 0x2525 | ||
| 132 | bne 1f | ||
| 133 | cmpwi r26, 0x2626 | ||
| 134 | bne 1f | ||
| 135 | cmpwi r27, 0x2727 | ||
| 136 | bne 1f | ||
| 137 | cmpwi r28, 0x2828 | ||
| 138 | bne 1f | ||
| 139 | cmpwi r29, 0x2929 | ||
| 140 | bne 1f | ||
| 141 | cmpwi r30, 0x3030 | ||
| 142 | bne 1f | ||
| 143 | cmpwi r31, 0x3131 | ||
| 144 | bne 1f | ||
| 145 | |||
| 146 | /* Load junk into all our registers before we reload them from the stack. */ | ||
| 147 | li r3, 0xde | ||
| 148 | li r4, 0xad | ||
| 149 | li r5, 0xbe | ||
| 150 | li r6, 0xef | ||
| 151 | li r7, 0xde | ||
| 152 | li r8, 0xad | ||
| 153 | li r9, 0xbe | ||
| 154 | li r10, 0xef | ||
| 155 | li r11, 0xde | ||
| 156 | li r14, 0xad | ||
| 157 | li r15, 0xbe | ||
| 158 | li r16, 0xef | ||
| 159 | li r17, 0xde | ||
| 160 | li r18, 0xad | ||
| 161 | li r19, 0xbe | ||
| 162 | li r20, 0xef | ||
| 163 | li r21, 0xde | ||
| 164 | li r22, 0xad | ||
| 165 | li r23, 0xbe | ||
| 166 | li r24, 0xef | ||
| 167 | li r25, 0xde | ||
| 168 | li r26, 0xad | ||
| 169 | li r27, 0xbe | ||
| 170 | li r28, 0xef | ||
| 171 | li r29, 0xdd | ||
| 172 | |||
| 173 | ld r3, -96(%r1) | ||
| 174 | cmpwi r3, 0x3030 | ||
| 175 | bne 1f | ||
| 176 | ld r4, -104(%r1) | ||
| 177 | cmpwi r4, 0x4040 | ||
| 178 | bne 1f | ||
| 179 | ld r5, -112(%r1) | ||
| 180 | cmpwi r5, 0x5050 | ||
| 181 | bne 1f | ||
| 182 | ld r6, -120(%r1) | ||
| 183 | cmpwi r6, 0x6060 | ||
| 184 | bne 1f | ||
| 185 | ld r7, -128(%r1) | ||
| 186 | cmpwi r7, 0x7070 | ||
| 187 | bne 1f | ||
| 188 | ld r8, -136(%r1) | ||
| 189 | cmpwi r8, 0x0808 | ||
| 190 | bne 1f | ||
| 191 | ld r9, -144(%r1) | ||
| 192 | cmpwi r9, 0x0909 | ||
| 193 | bne 1f | ||
| 194 | ld r10, -152(%r1) | ||
| 195 | cmpwi r10, 0x1010 | ||
| 196 | bne 1f | ||
| 197 | ld r11, -160(%r1) | ||
| 198 | cmpwi r11, 0x1111 | ||
| 199 | bne 1f | ||
| 200 | ld r14, -168(%r1) | ||
| 201 | cmpwi r14, 0x1414 | ||
| 202 | bne 1f | ||
| 203 | ld r15, -176(%r1) | ||
| 204 | cmpwi r15, 0x1515 | ||
| 205 | bne 1f | ||
| 206 | ld r16, -184(%r1) | ||
| 207 | cmpwi r16, 0x1616 | ||
| 208 | bne 1f | ||
| 209 | ld r17, -192(%r1) | ||
| 210 | cmpwi r17, 0x1717 | ||
| 211 | bne 1f | ||
| 212 | ld r18, -200(%r1) | ||
| 213 | cmpwi r18, 0x1818 | ||
| 214 | bne 1f | ||
| 215 | ld r19, -208(%r1) | ||
| 216 | cmpwi r19, 0x1919 | ||
| 217 | bne 1f | ||
| 218 | ld r20, -216(%r1) | ||
| 219 | cmpwi r20, 0x2020 | ||
| 220 | bne 1f | ||
| 221 | ld r21, -224(%r1) | ||
| 222 | cmpwi r21, 0x2121 | ||
| 223 | bne 1f | ||
| 224 | ld r22, -232(%r1) | ||
| 225 | cmpwi r22, 0x2222 | ||
| 226 | bne 1f | ||
| 227 | ld r23, -240(%r1) | ||
| 228 | cmpwi r23, 0x2323 | ||
| 229 | bne 1f | ||
| 230 | ld r24, -248(%r1) | ||
| 231 | cmpwi r24, 0x2424 | ||
| 232 | bne 1f | ||
| 233 | ld r25, -256(%r1) | ||
| 234 | cmpwi r25, 0x2525 | ||
| 235 | bne 1f | ||
| 236 | ld r26, -264(%r1) | ||
| 237 | cmpwi r26, 0x2626 | ||
| 238 | bne 1f | ||
| 239 | ld r27, -272(%r1) | ||
| 240 | cmpwi r27, 0x2727 | ||
| 241 | bne 1f | ||
| 242 | ld r28, -280(%r1) | ||
| 243 | cmpwi r28, 0x2828 | ||
| 244 | bne 1f | ||
| 245 | ld r29, -288(%r1) | ||
| 246 | cmpwi r29, 0x2929 | ||
| 247 | bne 1f | ||
| 248 | |||
| 249 | /* Load 0 (success) to return */ | ||
| 250 | li r3, 0 | ||
| 251 | |||
| 252 | 1: ld r14, 160(%r1) | ||
| 253 | ld r15, 152(%r1) | ||
| 254 | ld r16, 144(%r1) | ||
| 255 | ld r17, 136(%r1) | ||
| 256 | ld r18, 128(%r1) | ||
| 257 | ld r19, 120(%r1) | ||
| 258 | ld r20, 112(%r1) | ||
| 259 | ld r21, 104(%r1) | ||
| 260 | ld r22, 96(%r1) | ||
| 261 | ld r23, 88(%r1) | ||
| 262 | ld r24, 80(%r1) | ||
| 263 | ld r25, 72(%r1) | ||
| 264 | ld r26, 64(%r1) | ||
| 265 | ld r27, 56(%r1) | ||
| 266 | ld r28, 48(%r1) | ||
| 267 | ld r29, 40(%r1) | ||
| 268 | ld r30, 32(%r1) | ||
| 269 | ld r31, 24(%r1) | ||
| 270 | addi %r1, %r1, 168 | ||
| 271 | blr | ||
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c new file mode 100644 index 000000000000..d43029b0800c --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/ebb/cycles_with_mmcr2_test.c | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2014, Michael Ellerman, IBM Corp. | ||
| 3 | * Licensed under GPLv2. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <stdlib.h> | ||
| 8 | #include <stdbool.h> | ||
| 9 | |||
| 10 | #include "ebb.h" | ||
| 11 | |||
| 12 | |||
| 13 | /* | ||
| 14 | * Test of counting cycles while manipulating the user accessible bits in MMCR2. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* We use two values because the first freezes PMC1 and so we would get no EBBs */ | ||
| 18 | #define MMCR2_EXPECTED_1 0x4020100804020000UL /* (FC1P|FC2P|FC3P|FC4P|FC5P|FC6P) */ | ||
| 19 | #define MMCR2_EXPECTED_2 0x0020100804020000UL /* ( FC2P|FC3P|FC4P|FC5P|FC6P) */ | ||
| 20 | |||
| 21 | |||
| 22 | int cycles_with_mmcr2(void) | ||
| 23 | { | ||
| 24 | struct event event; | ||
| 25 | uint64_t val, expected[2], actual; | ||
| 26 | int i; | ||
| 27 | bool bad_mmcr2; | ||
| 28 | |||
| 29 | event_init_named(&event, 0x1001e, "cycles"); | ||
| 30 | event_leader_ebb_init(&event); | ||
| 31 | |||
| 32 | event.attr.exclude_kernel = 1; | ||
| 33 | event.attr.exclude_hv = 1; | ||
| 34 | event.attr.exclude_idle = 1; | ||
| 35 | |||
| 36 | FAIL_IF(event_open(&event)); | ||
| 37 | |||
| 38 | ebb_enable_pmc_counting(1); | ||
| 39 | setup_ebb_handler(standard_ebb_callee); | ||
| 40 | ebb_global_enable(); | ||
| 41 | |||
| 42 | FAIL_IF(ebb_event_enable(&event)); | ||
| 43 | |||
| 44 | mtspr(SPRN_PMC1, pmc_sample_period(sample_period)); | ||
| 45 | |||
| 46 | /* XXX Set of MMCR2 must be after enable */ | ||
| 47 | expected[0] = MMCR2_EXPECTED_1; | ||
| 48 | expected[1] = MMCR2_EXPECTED_2; | ||
| 49 | i = 0; | ||
| 50 | bad_mmcr2 = false; | ||
| 51 | |||
| 52 | /* Make sure we loop until we take at least one EBB */ | ||
| 53 | while ((ebb_state.stats.ebb_count < 20 && !bad_mmcr2) || | ||
| 54 | ebb_state.stats.ebb_count < 1) | ||
| 55 | { | ||
| 56 | mtspr(SPRN_MMCR2, expected[i % 2]); | ||
| 57 | |||
| 58 | FAIL_IF(core_busy_loop()); | ||
| 59 | |||
| 60 | val = mfspr(SPRN_MMCR2); | ||
| 61 | if (val != expected[i % 2]) { | ||
| 62 | bad_mmcr2 = true; | ||
| 63 | actual = val; | ||
| 64 | } | ||
| 65 | |||
| 66 | i++; | ||
| 67 | } | ||
| 68 | |||
| 69 | ebb_global_disable(); | ||
| 70 | ebb_freeze_pmcs(); | ||
| 71 | |||
| 72 | count_pmc(1, sample_period); | ||
| 73 | |||
| 74 | dump_ebb_state(); | ||
| 75 | |||
| 76 | event_close(&event); | ||
| 77 | |||
| 78 | FAIL_IF(ebb_state.stats.ebb_count == 0); | ||
| 79 | |||
| 80 | if (bad_mmcr2) | ||
| 81 | printf("Bad MMCR2 value seen is 0x%lx\n", actual); | ||
| 82 | |||
| 83 | FAIL_IF(bad_mmcr2); | ||
| 84 | |||
| 85 | return 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | int main(void) | ||
| 89 | { | ||
| 90 | return test_harness(cycles_with_mmcr2, "cycles_with_mmcr2"); | ||
| 91 | } | ||
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c index 1b46be94b64c..d7a72ce696b5 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/ebb.c +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.c | |||
| @@ -224,6 +224,7 @@ void dump_ebb_hw_state(void) | |||
| 224 | 224 | ||
| 225 | printf("HW state:\n" \ | 225 | printf("HW state:\n" \ |
| 226 | "MMCR0 0x%016x %s\n" \ | 226 | "MMCR0 0x%016x %s\n" \ |
| 227 | "MMCR2 0x%016lx\n" \ | ||
| 227 | "EBBHR 0x%016lx\n" \ | 228 | "EBBHR 0x%016lx\n" \ |
| 228 | "BESCR 0x%016llx %s\n" \ | 229 | "BESCR 0x%016llx %s\n" \ |
| 229 | "PMC1 0x%016lx\n" \ | 230 | "PMC1 0x%016lx\n" \ |
| @@ -233,10 +234,11 @@ void dump_ebb_hw_state(void) | |||
| 233 | "PMC5 0x%016lx\n" \ | 234 | "PMC5 0x%016lx\n" \ |
| 234 | "PMC6 0x%016lx\n" \ | 235 | "PMC6 0x%016lx\n" \ |
| 235 | "SIAR 0x%016lx\n", | 236 | "SIAR 0x%016lx\n", |
| 236 | mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_EBBHR), bescr, | 237 | mmcr0, decode_mmcr0(mmcr0), mfspr(SPRN_MMCR2), |
| 237 | decode_bescr(bescr), mfspr(SPRN_PMC1), mfspr(SPRN_PMC2), | 238 | mfspr(SPRN_EBBHR), bescr, decode_bescr(bescr), |
| 238 | mfspr(SPRN_PMC3), mfspr(SPRN_PMC4), mfspr(SPRN_PMC5), | 239 | mfspr(SPRN_PMC1), mfspr(SPRN_PMC2), mfspr(SPRN_PMC3), |
| 239 | mfspr(SPRN_PMC6), mfspr(SPRN_SIAR)); | 240 | mfspr(SPRN_PMC4), mfspr(SPRN_PMC5), mfspr(SPRN_PMC6), |
| 241 | mfspr(SPRN_SIAR)); | ||
| 240 | } | 242 | } |
| 241 | 243 | ||
| 242 | void dump_ebb_state(void) | 244 | void dump_ebb_state(void) |
| @@ -335,257 +337,6 @@ void event_leader_ebb_init(struct event *e) | |||
| 335 | e->attr.pinned = 1; | 337 | e->attr.pinned = 1; |
| 336 | } | 338 | } |
| 337 | 339 | ||
| 338 | int core_busy_loop(void) | ||
| 339 | { | ||
| 340 | int rc; | ||
| 341 | |||
| 342 | asm volatile ( | ||
| 343 | "li 3, 0x3030\n" | ||
| 344 | "std 3, -96(1)\n" | ||
| 345 | "li 4, 0x4040\n" | ||
| 346 | "std 4, -104(1)\n" | ||
| 347 | "li 5, 0x5050\n" | ||
| 348 | "std 5, -112(1)\n" | ||
| 349 | "li 6, 0x6060\n" | ||
| 350 | "std 6, -120(1)\n" | ||
| 351 | "li 7, 0x7070\n" | ||
| 352 | "std 7, -128(1)\n" | ||
| 353 | "li 8, 0x0808\n" | ||
| 354 | "std 8, -136(1)\n" | ||
| 355 | "li 9, 0x0909\n" | ||
| 356 | "std 9, -144(1)\n" | ||
| 357 | "li 10, 0x1010\n" | ||
| 358 | "std 10, -152(1)\n" | ||
| 359 | "li 11, 0x1111\n" | ||
| 360 | "std 11, -160(1)\n" | ||
| 361 | "li 14, 0x1414\n" | ||
| 362 | "std 14, -168(1)\n" | ||
| 363 | "li 15, 0x1515\n" | ||
| 364 | "std 15, -176(1)\n" | ||
| 365 | "li 16, 0x1616\n" | ||
| 366 | "std 16, -184(1)\n" | ||
| 367 | "li 17, 0x1717\n" | ||
| 368 | "std 17, -192(1)\n" | ||
| 369 | "li 18, 0x1818\n" | ||
| 370 | "std 18, -200(1)\n" | ||
| 371 | "li 19, 0x1919\n" | ||
| 372 | "std 19, -208(1)\n" | ||
| 373 | "li 20, 0x2020\n" | ||
| 374 | "std 20, -216(1)\n" | ||
| 375 | "li 21, 0x2121\n" | ||
| 376 | "std 21, -224(1)\n" | ||
| 377 | "li 22, 0x2222\n" | ||
| 378 | "std 22, -232(1)\n" | ||
| 379 | "li 23, 0x2323\n" | ||
| 380 | "std 23, -240(1)\n" | ||
| 381 | "li 24, 0x2424\n" | ||
| 382 | "std 24, -248(1)\n" | ||
| 383 | "li 25, 0x2525\n" | ||
| 384 | "std 25, -256(1)\n" | ||
| 385 | "li 26, 0x2626\n" | ||
| 386 | "std 26, -264(1)\n" | ||
| 387 | "li 27, 0x2727\n" | ||
| 388 | "std 27, -272(1)\n" | ||
| 389 | "li 28, 0x2828\n" | ||
| 390 | "std 28, -280(1)\n" | ||
| 391 | "li 29, 0x2929\n" | ||
| 392 | "std 29, -288(1)\n" | ||
| 393 | "li 30, 0x3030\n" | ||
| 394 | "li 31, 0x3131\n" | ||
| 395 | |||
| 396 | "li 3, 0\n" | ||
| 397 | "0: " | ||
| 398 | "addi 3, 3, 1\n" | ||
| 399 | "cmpwi 3, 100\n" | ||
| 400 | "blt 0b\n" | ||
| 401 | |||
| 402 | /* Return 1 (fail) unless we get through all the checks */ | ||
| 403 | "li 0, 1\n" | ||
| 404 | |||
| 405 | /* Check none of our registers have been corrupted */ | ||
| 406 | "cmpwi 4, 0x4040\n" | ||
| 407 | "bne 1f\n" | ||
| 408 | "cmpwi 5, 0x5050\n" | ||
| 409 | "bne 1f\n" | ||
| 410 | "cmpwi 6, 0x6060\n" | ||
| 411 | "bne 1f\n" | ||
| 412 | "cmpwi 7, 0x7070\n" | ||
| 413 | "bne 1f\n" | ||
| 414 | "cmpwi 8, 0x0808\n" | ||
| 415 | "bne 1f\n" | ||
| 416 | "cmpwi 9, 0x0909\n" | ||
| 417 | "bne 1f\n" | ||
| 418 | "cmpwi 10, 0x1010\n" | ||
| 419 | "bne 1f\n" | ||
| 420 | "cmpwi 11, 0x1111\n" | ||
| 421 | "bne 1f\n" | ||
| 422 | "cmpwi 14, 0x1414\n" | ||
| 423 | "bne 1f\n" | ||
| 424 | "cmpwi 15, 0x1515\n" | ||
| 425 | "bne 1f\n" | ||
| 426 | "cmpwi 16, 0x1616\n" | ||
| 427 | "bne 1f\n" | ||
| 428 | "cmpwi 17, 0x1717\n" | ||
| 429 | "bne 1f\n" | ||
| 430 | "cmpwi 18, 0x1818\n" | ||
| 431 | "bne 1f\n" | ||
| 432 | "cmpwi 19, 0x1919\n" | ||
| 433 | "bne 1f\n" | ||
| 434 | "cmpwi 20, 0x2020\n" | ||
| 435 | "bne 1f\n" | ||
| 436 | "cmpwi 21, 0x2121\n" | ||
| 437 | "bne 1f\n" | ||
| 438 | "cmpwi 22, 0x2222\n" | ||
| 439 | "bne 1f\n" | ||
| 440 | "cmpwi 23, 0x2323\n" | ||
| 441 | "bne 1f\n" | ||
| 442 | "cmpwi 24, 0x2424\n" | ||
| 443 | "bne 1f\n" | ||
| 444 | "cmpwi 25, 0x2525\n" | ||
| 445 | "bne 1f\n" | ||
| 446 | "cmpwi 26, 0x2626\n" | ||
| 447 | "bne 1f\n" | ||
| 448 | "cmpwi 27, 0x2727\n" | ||
| 449 | "bne 1f\n" | ||
| 450 | "cmpwi 28, 0x2828\n" | ||
| 451 | "bne 1f\n" | ||
| 452 | "cmpwi 29, 0x2929\n" | ||
| 453 | "bne 1f\n" | ||
| 454 | "cmpwi 30, 0x3030\n" | ||
| 455 | "bne 1f\n" | ||
| 456 | "cmpwi 31, 0x3131\n" | ||
| 457 | "bne 1f\n" | ||
| 458 | |||
| 459 | /* Load junk into all our registers before we reload them from the stack. */ | ||
| 460 | "li 3, 0xde\n" | ||
| 461 | "li 4, 0xad\n" | ||
| 462 | "li 5, 0xbe\n" | ||
| 463 | "li 6, 0xef\n" | ||
| 464 | "li 7, 0xde\n" | ||
| 465 | "li 8, 0xad\n" | ||
| 466 | "li 9, 0xbe\n" | ||
| 467 | "li 10, 0xef\n" | ||
| 468 | "li 11, 0xde\n" | ||
| 469 | "li 14, 0xad\n" | ||
| 470 | "li 15, 0xbe\n" | ||
| 471 | "li 16, 0xef\n" | ||
| 472 | "li 17, 0xde\n" | ||
| 473 | "li 18, 0xad\n" | ||
| 474 | "li 19, 0xbe\n" | ||
| 475 | "li 20, 0xef\n" | ||
| 476 | "li 21, 0xde\n" | ||
| 477 | "li 22, 0xad\n" | ||
| 478 | "li 23, 0xbe\n" | ||
| 479 | "li 24, 0xef\n" | ||
| 480 | "li 25, 0xde\n" | ||
| 481 | "li 26, 0xad\n" | ||
| 482 | "li 27, 0xbe\n" | ||
| 483 | "li 28, 0xef\n" | ||
| 484 | "li 29, 0xdd\n" | ||
| 485 | |||
| 486 | "ld 3, -96(1)\n" | ||
| 487 | "cmpwi 3, 0x3030\n" | ||
| 488 | "bne 1f\n" | ||
| 489 | "ld 4, -104(1)\n" | ||
| 490 | "cmpwi 4, 0x4040\n" | ||
| 491 | "bne 1f\n" | ||
| 492 | "ld 5, -112(1)\n" | ||
| 493 | "cmpwi 5, 0x5050\n" | ||
| 494 | "bne 1f\n" | ||
| 495 | "ld 6, -120(1)\n" | ||
| 496 | "cmpwi 6, 0x6060\n" | ||
| 497 | "bne 1f\n" | ||
| 498 | "ld 7, -128(1)\n" | ||
| 499 | "cmpwi 7, 0x7070\n" | ||
| 500 | "bne 1f\n" | ||
| 501 | "ld 8, -136(1)\n" | ||
| 502 | "cmpwi 8, 0x0808\n" | ||
| 503 | "bne 1f\n" | ||
| 504 | "ld 9, -144(1)\n" | ||
| 505 | "cmpwi 9, 0x0909\n" | ||
| 506 | "bne 1f\n" | ||
| 507 | "ld 10, -152(1)\n" | ||
| 508 | "cmpwi 10, 0x1010\n" | ||
| 509 | "bne 1f\n" | ||
| 510 | "ld 11, -160(1)\n" | ||
| 511 | "cmpwi 11, 0x1111\n" | ||
| 512 | "bne 1f\n" | ||
| 513 | "ld 14, -168(1)\n" | ||
| 514 | "cmpwi 14, 0x1414\n" | ||
| 515 | "bne 1f\n" | ||
| 516 | "ld 15, -176(1)\n" | ||
| 517 | "cmpwi 15, 0x1515\n" | ||
| 518 | "bne 1f\n" | ||
| 519 | "ld 16, -184(1)\n" | ||
| 520 | "cmpwi 16, 0x1616\n" | ||
| 521 | "bne 1f\n" | ||
| 522 | "ld 17, -192(1)\n" | ||
| 523 | "cmpwi 17, 0x1717\n" | ||
| 524 | "bne 1f\n" | ||
| 525 | "ld 18, -200(1)\n" | ||
| 526 | "cmpwi 18, 0x1818\n" | ||
| 527 | "bne 1f\n" | ||
| 528 | "ld 19, -208(1)\n" | ||
| 529 | "cmpwi 19, 0x1919\n" | ||
| 530 | "bne 1f\n" | ||
| 531 | "ld 20, -216(1)\n" | ||
| 532 | "cmpwi 20, 0x2020\n" | ||
| 533 | "bne 1f\n" | ||
| 534 | "ld 21, -224(1)\n" | ||
| 535 | "cmpwi 21, 0x2121\n" | ||
| 536 | "bne 1f\n" | ||
| 537 | "ld 22, -232(1)\n" | ||
| 538 | "cmpwi 22, 0x2222\n" | ||
| 539 | "bne 1f\n" | ||
| 540 | "ld 23, -240(1)\n" | ||
| 541 | "cmpwi 23, 0x2323\n" | ||
| 542 | "bne 1f\n" | ||
| 543 | "ld 24, -248(1)\n" | ||
| 544 | "cmpwi 24, 0x2424\n" | ||
| 545 | "bne 1f\n" | ||
| 546 | "ld 25, -256(1)\n" | ||
| 547 | "cmpwi 25, 0x2525\n" | ||
| 548 | "bne 1f\n" | ||
| 549 | "ld 26, -264(1)\n" | ||
| 550 | "cmpwi 26, 0x2626\n" | ||
| 551 | "bne 1f\n" | ||
| 552 | "ld 27, -272(1)\n" | ||
| 553 | "cmpwi 27, 0x2727\n" | ||
| 554 | "bne 1f\n" | ||
| 555 | "ld 28, -280(1)\n" | ||
| 556 | "cmpwi 28, 0x2828\n" | ||
| 557 | "bne 1f\n" | ||
| 558 | "ld 29, -288(1)\n" | ||
| 559 | "cmpwi 29, 0x2929\n" | ||
| 560 | "bne 1f\n" | ||
| 561 | |||
| 562 | /* Load 0 (success) to return */ | ||
| 563 | "li 0, 0\n" | ||
| 564 | |||
| 565 | "1: mr %0, 0\n" | ||
| 566 | |||
| 567 | : "=r" (rc) | ||
| 568 | : /* no inputs */ | ||
| 569 | : "3", "4", "5", "6", "7", "8", "9", "10", "11", "14", | ||
| 570 | "15", "16", "17", "18", "19", "20", "21", "22", "23", | ||
| 571 | "24", "25", "26", "27", "28", "29", "30", "31", | ||
| 572 | "memory" | ||
| 573 | ); | ||
| 574 | |||
| 575 | return rc; | ||
| 576 | } | ||
| 577 | |||
| 578 | int core_busy_loop_with_freeze(void) | ||
| 579 | { | ||
| 580 | int rc; | ||
| 581 | |||
| 582 | mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) & ~MMCR0_FC); | ||
| 583 | rc = core_busy_loop(); | ||
| 584 | mtspr(SPRN_MMCR0, mfspr(SPRN_MMCR0) | MMCR0_FC); | ||
| 585 | |||
| 586 | return rc; | ||
| 587 | } | ||
| 588 | |||
| 589 | int ebb_child(union pipe read_pipe, union pipe write_pipe) | 340 | int ebb_child(union pipe read_pipe, union pipe write_pipe) |
| 590 | { | 341 | { |
| 591 | struct event event; | 342 | struct event event; |
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb.h b/tools/testing/selftests/powerpc/pmu/ebb/ebb.h index e62bde05bf78..e44eee5d97ca 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/ebb.h +++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb.h | |||
| @@ -70,7 +70,6 @@ int ebb_check_mmcr0(void); | |||
| 70 | extern u64 sample_period; | 70 | extern u64 sample_period; |
| 71 | 71 | ||
| 72 | int core_busy_loop(void); | 72 | int core_busy_loop(void); |
| 73 | int core_busy_loop_with_freeze(void); | ||
| 74 | int ebb_child(union pipe read_pipe, union pipe write_pipe); | 73 | int ebb_child(union pipe read_pipe, union pipe write_pipe); |
| 75 | int catch_sigill(void (*func)(void)); | 74 | int catch_sigill(void (*func)(void)); |
| 76 | void write_pmc1(void); | 75 | void write_pmc1(void); |
diff --git a/tools/testing/selftests/powerpc/pmu/l3_bank_test.c b/tools/testing/selftests/powerpc/pmu/l3_bank_test.c new file mode 100644 index 000000000000..77472f31441e --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/l3_bank_test.c | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2014, Michael Ellerman, IBM Corp. | ||
| 3 | * Licensed under GPLv2. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <stdlib.h> | ||
| 8 | |||
| 9 | #include "event.h" | ||
| 10 | #include "utils.h" | ||
| 11 | |||
| 12 | #define MALLOC_SIZE (0x10000 * 10) /* Ought to be enough .. */ | ||
| 13 | |||
| 14 | /* | ||
| 15 | * Tests that the L3 bank handling is correct. We fixed it in commit e9aaac1. | ||
| 16 | */ | ||
| 17 | static int l3_bank_test(void) | ||
| 18 | { | ||
| 19 | struct event event; | ||
| 20 | char *p; | ||
| 21 | int i; | ||
| 22 | |||
| 23 | p = malloc(MALLOC_SIZE); | ||
| 24 | FAIL_IF(!p); | ||
| 25 | |||
| 26 | event_init(&event, 0x84918F); | ||
| 27 | |||
| 28 | FAIL_IF(event_open(&event)); | ||
| 29 | |||
| 30 | for (i = 0; i < MALLOC_SIZE; i += 0x10000) | ||
| 31 | p[i] = i; | ||
| 32 | |||
| 33 | event_read(&event); | ||
| 34 | event_report(&event); | ||
| 35 | |||
| 36 | FAIL_IF(event.result.running == 0); | ||
| 37 | FAIL_IF(event.result.enabled == 0); | ||
| 38 | |||
| 39 | event_close(&event); | ||
| 40 | free(p); | ||
| 41 | |||
| 42 | return 0; | ||
| 43 | } | ||
| 44 | |||
| 45 | int main(void) | ||
| 46 | { | ||
| 47 | return test_harness(l3_bank_test, "l3_bank_test"); | ||
| 48 | } | ||
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c index 0f6a4731d546..9768dea37bf3 100644 --- a/tools/testing/selftests/powerpc/pmu/lib.c +++ b/tools/testing/selftests/powerpc/pmu/lib.c | |||
| @@ -5,10 +5,15 @@ | |||
| 5 | 5 | ||
| 6 | #define _GNU_SOURCE /* For CPU_ZERO etc. */ | 6 | #define _GNU_SOURCE /* For CPU_ZERO etc. */ |
| 7 | 7 | ||
| 8 | #include <elf.h> | ||
| 8 | #include <errno.h> | 9 | #include <errno.h> |
| 10 | #include <fcntl.h> | ||
| 11 | #include <link.h> | ||
| 9 | #include <sched.h> | 12 | #include <sched.h> |
| 10 | #include <setjmp.h> | 13 | #include <setjmp.h> |
| 11 | #include <stdlib.h> | 14 | #include <stdlib.h> |
| 15 | #include <sys/stat.h> | ||
| 16 | #include <sys/types.h> | ||
| 12 | #include <sys/wait.h> | 17 | #include <sys/wait.h> |
| 13 | 18 | ||
| 14 | #include "utils.h" | 19 | #include "utils.h" |
| @@ -177,8 +182,8 @@ struct addr_range libc, vdso; | |||
| 177 | 182 | ||
| 178 | int parse_proc_maps(void) | 183 | int parse_proc_maps(void) |
| 179 | { | 184 | { |
| 185 | unsigned long start, end; | ||
| 180 | char execute, name[128]; | 186 | char execute, name[128]; |
| 181 | uint64_t start, end; | ||
| 182 | FILE *f; | 187 | FILE *f; |
| 183 | int rc; | 188 | int rc; |
| 184 | 189 | ||
| @@ -250,3 +255,46 @@ out_close: | |||
| 250 | out: | 255 | out: |
| 251 | return rc; | 256 | return rc; |
| 252 | } | 257 | } |
| 258 | |||
| 259 | static char auxv[4096]; | ||
| 260 | |||
| 261 | void *get_auxv_entry(int type) | ||
| 262 | { | ||
| 263 | ElfW(auxv_t) *p; | ||
| 264 | void *result; | ||
| 265 | ssize_t num; | ||
| 266 | int fd; | ||
| 267 | |||
| 268 | fd = open("/proc/self/auxv", O_RDONLY); | ||
| 269 | if (fd == -1) { | ||
| 270 | perror("open"); | ||
| 271 | return NULL; | ||
| 272 | } | ||
| 273 | |||
| 274 | result = NULL; | ||
| 275 | |||
| 276 | num = read(fd, auxv, sizeof(auxv)); | ||
| 277 | if (num < 0) { | ||
| 278 | perror("read"); | ||
| 279 | goto out; | ||
| 280 | } | ||
| 281 | |||
| 282 | if (num > sizeof(auxv)) { | ||
| 283 | printf("Overflowed auxv buffer\n"); | ||
| 284 | goto out; | ||
| 285 | } | ||
| 286 | |||
| 287 | p = (ElfW(auxv_t) *)auxv; | ||
| 288 | |||
| 289 | while (p->a_type != AT_NULL) { | ||
| 290 | if (p->a_type == type) { | ||
| 291 | result = (void *)p->a_un.a_val; | ||
| 292 | break; | ||
| 293 | } | ||
| 294 | |||
| 295 | p++; | ||
| 296 | } | ||
| 297 | out: | ||
| 298 | close(fd); | ||
| 299 | return result; | ||
| 300 | } | ||
diff --git a/tools/testing/selftests/powerpc/pmu/lib.h b/tools/testing/selftests/powerpc/pmu/lib.h index ca5d72ae3be6..0f0339c8a6f6 100644 --- a/tools/testing/selftests/powerpc/pmu/lib.h +++ b/tools/testing/selftests/powerpc/pmu/lib.h | |||
| @@ -29,6 +29,7 @@ extern int notify_parent(union pipe write_pipe); | |||
| 29 | extern int notify_parent_of_error(union pipe write_pipe); | 29 | extern int notify_parent_of_error(union pipe write_pipe); |
| 30 | extern pid_t eat_cpu(int (test_function)(void)); | 30 | extern pid_t eat_cpu(int (test_function)(void)); |
| 31 | extern bool require_paranoia_below(int level); | 31 | extern bool require_paranoia_below(int level); |
| 32 | extern void *get_auxv_entry(int type); | ||
| 32 | 33 | ||
| 33 | struct addr_range { | 34 | struct addr_range { |
| 34 | uint64_t first, last; | 35 | uint64_t first, last; |
diff --git a/tools/testing/selftests/powerpc/pmu/per_event_excludes.c b/tools/testing/selftests/powerpc/pmu/per_event_excludes.c new file mode 100644 index 000000000000..fddbbc9cae2f --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/per_event_excludes.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2014, Michael Ellerman, IBM Corp. | ||
| 3 | * Licensed under GPLv2. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #define _GNU_SOURCE | ||
| 7 | |||
| 8 | #include <elf.h> | ||
| 9 | #include <limits.h> | ||
| 10 | #include <stdio.h> | ||
| 11 | #include <stdbool.h> | ||
| 12 | #include <string.h> | ||
| 13 | #include <sys/prctl.h> | ||
| 14 | |||
| 15 | #include "event.h" | ||
| 16 | #include "lib.h" | ||
| 17 | #include "utils.h" | ||
| 18 | |||
| 19 | /* | ||
| 20 | * Test that per-event excludes work. | ||
| 21 | */ | ||
| 22 | |||
| 23 | static int per_event_excludes(void) | ||
| 24 | { | ||
| 25 | struct event *e, events[4]; | ||
| 26 | char *platform; | ||
| 27 | int i; | ||
| 28 | |||
| 29 | platform = (char *)get_auxv_entry(AT_BASE_PLATFORM); | ||
| 30 | FAIL_IF(!platform); | ||
| 31 | SKIP_IF(strcmp(platform, "power8") != 0); | ||
| 32 | |||
| 33 | /* | ||
| 34 | * We need to create the events disabled, otherwise the running/enabled | ||
| 35 | * counts don't match up. | ||
| 36 | */ | ||
| 37 | e = &events[0]; | ||
| 38 | event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS, | ||
| 39 | PERF_TYPE_HARDWARE, "instructions"); | ||
| 40 | e->attr.disabled = 1; | ||
| 41 | |||
| 42 | e = &events[1]; | ||
| 43 | event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS, | ||
| 44 | PERF_TYPE_HARDWARE, "instructions(k)"); | ||
| 45 | e->attr.disabled = 1; | ||
| 46 | e->attr.exclude_user = 1; | ||
| 47 | e->attr.exclude_hv = 1; | ||
| 48 | |||
| 49 | e = &events[2]; | ||
| 50 | event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS, | ||
| 51 | PERF_TYPE_HARDWARE, "instructions(h)"); | ||
| 52 | e->attr.disabled = 1; | ||
| 53 | e->attr.exclude_user = 1; | ||
| 54 | e->attr.exclude_kernel = 1; | ||
| 55 | |||
| 56 | e = &events[3]; | ||
| 57 | event_init_opts(e, PERF_COUNT_HW_INSTRUCTIONS, | ||
| 58 | PERF_TYPE_HARDWARE, "instructions(u)"); | ||
| 59 | e->attr.disabled = 1; | ||
| 60 | e->attr.exclude_hv = 1; | ||
| 61 | e->attr.exclude_kernel = 1; | ||
| 62 | |||
| 63 | FAIL_IF(event_open(&events[0])); | ||
| 64 | |||
| 65 | /* | ||
| 66 | * The open here will fail if we don't have per event exclude support, | ||
| 67 | * because the second event has an incompatible set of exclude settings | ||
| 68 | * and we're asking for the events to be in a group. | ||
| 69 | */ | ||
| 70 | for (i = 1; i < 4; i++) | ||
| 71 | FAIL_IF(event_open_with_group(&events[i], events[0].fd)); | ||
| 72 | |||
| 73 | /* | ||
| 74 | * Even though the above will fail without per-event excludes we keep | ||
| 75 | * testing in order to be thorough. | ||
| 76 | */ | ||
| 77 | prctl(PR_TASK_PERF_EVENTS_ENABLE); | ||
| 78 | |||
| 79 | /* Spin for a while */ | ||
| 80 | for (i = 0; i < INT_MAX; i++) | ||
| 81 | asm volatile("" : : : "memory"); | ||
| 82 | |||
| 83 | prctl(PR_TASK_PERF_EVENTS_DISABLE); | ||
| 84 | |||
| 85 | for (i = 0; i < 4; i++) { | ||
| 86 | FAIL_IF(event_read(&events[i])); | ||
| 87 | event_report(&events[i]); | ||
| 88 | } | ||
| 89 | |||
| 90 | /* | ||
| 91 | * We should see that all events have enabled == running. That | ||
| 92 | * shows that they were all on the PMU at once. | ||
| 93 | */ | ||
| 94 | for (i = 0; i < 4; i++) | ||
| 95 | FAIL_IF(events[i].result.running != events[i].result.enabled); | ||
| 96 | |||
| 97 | /* | ||
| 98 | * We can also check that the result for instructions is >= all the | ||
| 99 | * other counts. That's because it is counting all instructions while | ||
| 100 | * the others are counting a subset. | ||
| 101 | */ | ||
| 102 | for (i = 1; i < 4; i++) | ||
| 103 | FAIL_IF(events[0].result.value < events[i].result.value); | ||
| 104 | |||
| 105 | for (i = 0; i < 4; i++) | ||
| 106 | event_close(&events[i]); | ||
| 107 | |||
| 108 | return 0; | ||
| 109 | } | ||
| 110 | |||
| 111 | int main(void) | ||
| 112 | { | ||
| 113 | return test_harness(per_event_excludes, "per_event_excludes"); | ||
| 114 | } | ||
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/ptrace/peeksiginfo.c b/tools/testing/selftests/ptrace/peeksiginfo.c index d46558b1f58d..c34cd8ac8aaa 100644 --- a/tools/testing/selftests/ptrace/peeksiginfo.c +++ b/tools/testing/selftests/ptrace/peeksiginfo.c | |||
| @@ -31,6 +31,10 @@ static int sys_ptrace(int request, pid_t pid, void *addr, void *data) | |||
| 31 | #define TEST_SICODE_PRIV -1 | 31 | #define TEST_SICODE_PRIV -1 |
| 32 | #define TEST_SICODE_SHARE -2 | 32 | #define TEST_SICODE_SHARE -2 |
| 33 | 33 | ||
| 34 | #ifndef PAGE_SIZE | ||
| 35 | #define PAGE_SIZE sysconf(_SC_PAGESIZE) | ||
| 36 | #endif | ||
| 37 | |||
| 34 | #define err(fmt, ...) \ | 38 | #define err(fmt, ...) \ |
| 35 | fprintf(stderr, \ | 39 | fprintf(stderr, \ |
| 36 | "Error (%s:%d): " fmt, \ | 40 | "Error (%s:%d): " fmt, \ |
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/usb/ffs-test.c b/tools/usb/ffs-test.c index a87e99f37c52..88d5e71be044 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * ffs-test.c.c -- user mode filesystem api for usb composite function | 2 | * ffs-test.c -- user mode filesystem api for usb composite function |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2010 Samsung Electronics | 4 | * Copyright (C) 2010 Samsung Electronics |
| 5 | * Author: Michal Nazarewicz <mina86@mina86.com> | 5 | * Author: Michal Nazarewicz <mina86@mina86.com> |
| @@ -29,6 +29,7 @@ | |||
| 29 | #include <fcntl.h> | 29 | #include <fcntl.h> |
| 30 | #include <pthread.h> | 30 | #include <pthread.h> |
| 31 | #include <stdarg.h> | 31 | #include <stdarg.h> |
| 32 | #include <stdbool.h> | ||
| 32 | #include <stdio.h> | 33 | #include <stdio.h> |
| 33 | #include <stdlib.h> | 34 | #include <stdlib.h> |
| 34 | #include <string.h> | 35 | #include <string.h> |
| @@ -106,7 +107,9 @@ static void _msg(unsigned level, const char *fmt, ...) | |||
| 106 | /******************** Descriptors and Strings *******************************/ | 107 | /******************** Descriptors and Strings *******************************/ |
| 107 | 108 | ||
| 108 | static const struct { | 109 | static const struct { |
| 109 | struct usb_functionfs_descs_head header; | 110 | struct usb_functionfs_descs_head_v2 header; |
| 111 | __le32 fs_count; | ||
| 112 | __le32 hs_count; | ||
| 110 | struct { | 113 | struct { |
| 111 | struct usb_interface_descriptor intf; | 114 | struct usb_interface_descriptor intf; |
| 112 | struct usb_endpoint_descriptor_no_audio sink; | 115 | struct usb_endpoint_descriptor_no_audio sink; |
| @@ -114,11 +117,12 @@ static const struct { | |||
| 114 | } __attribute__((packed)) fs_descs, hs_descs; | 117 | } __attribute__((packed)) fs_descs, hs_descs; |
| 115 | } __attribute__((packed)) descriptors = { | 118 | } __attribute__((packed)) descriptors = { |
| 116 | .header = { | 119 | .header = { |
| 117 | .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), | 120 | .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2), |
| 121 | .flags = cpu_to_le32(FUNCTIONFS_HAS_FS_DESC | | ||
| 122 | FUNCTIONFS_HAS_HS_DESC), | ||
| 118 | .length = cpu_to_le32(sizeof descriptors), | 123 | .length = cpu_to_le32(sizeof descriptors), |
| 119 | .fs_count = cpu_to_le32(3), | ||
| 120 | .hs_count = cpu_to_le32(3), | ||
| 121 | }, | 124 | }, |
| 125 | .fs_count = cpu_to_le32(3), | ||
| 122 | .fs_descs = { | 126 | .fs_descs = { |
| 123 | .intf = { | 127 | .intf = { |
| 124 | .bLength = sizeof descriptors.fs_descs.intf, | 128 | .bLength = sizeof descriptors.fs_descs.intf, |
| @@ -142,6 +146,7 @@ static const struct { | |||
| 142 | /* .wMaxPacketSize = autoconfiguration (kernel) */ | 146 | /* .wMaxPacketSize = autoconfiguration (kernel) */ |
| 143 | }, | 147 | }, |
| 144 | }, | 148 | }, |
| 149 | .hs_count = cpu_to_le32(3), | ||
| 145 | .hs_descs = { | 150 | .hs_descs = { |
| 146 | .intf = { | 151 | .intf = { |
| 147 | .bLength = sizeof descriptors.fs_descs.intf, | 152 | .bLength = sizeof descriptors.fs_descs.intf, |
| @@ -168,6 +173,89 @@ static const struct { | |||
| 168 | }, | 173 | }, |
| 169 | }; | 174 | }; |
| 170 | 175 | ||
| 176 | static size_t descs_to_legacy(void **legacy, const void *descriptors_v2) | ||
| 177 | { | ||
| 178 | const unsigned char *descs_end, *descs_start; | ||
| 179 | __u32 length, fs_count = 0, hs_count = 0, count; | ||
| 180 | |||
| 181 | /* Read v2 header */ | ||
| 182 | { | ||
| 183 | const struct { | ||
| 184 | const struct usb_functionfs_descs_head_v2 header; | ||
| 185 | const __le32 counts[]; | ||
| 186 | } __attribute__((packed)) *const in = descriptors_v2; | ||
| 187 | const __le32 *counts = in->counts; | ||
| 188 | __u32 flags; | ||
| 189 | |||
| 190 | if (le32_to_cpu(in->header.magic) != | ||
| 191 | FUNCTIONFS_DESCRIPTORS_MAGIC_V2) | ||
| 192 | return 0; | ||
| 193 | length = le32_to_cpu(in->header.length); | ||
| 194 | if (length <= sizeof in->header) | ||
| 195 | return 0; | ||
| 196 | length -= sizeof in->header; | ||
| 197 | flags = le32_to_cpu(in->header.flags); | ||
| 198 | if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | | ||
| 199 | FUNCTIONFS_HAS_SS_DESC)) | ||
| 200 | return 0; | ||
| 201 | |||
| 202 | #define GET_NEXT_COUNT_IF_FLAG(ret, flg) do { \ | ||
| 203 | if (!(flags & (flg))) \ | ||
| 204 | break; \ | ||
| 205 | if (length < 4) \ | ||
| 206 | return 0; \ | ||
| 207 | ret = le32_to_cpu(*counts); \ | ||
| 208 | length -= 4; \ | ||
| 209 | ++counts; \ | ||
| 210 | } while (0) | ||
| 211 | |||
| 212 | GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC); | ||
| 213 | GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC); | ||
| 214 | GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC); | ||
| 215 | |||
| 216 | count = fs_count + hs_count; | ||
| 217 | if (!count) | ||
| 218 | return 0; | ||
| 219 | descs_start = (const void *)counts; | ||
| 220 | |||
| 221 | #undef GET_NEXT_COUNT_IF_FLAG | ||
| 222 | } | ||
| 223 | |||
| 224 | /* | ||
| 225 | * Find the end of FS and HS USB descriptors. SS descriptors | ||
| 226 | * are ignored since legacy format does not support them. | ||
| 227 | */ | ||
| 228 | descs_end = descs_start; | ||
| 229 | do { | ||
| 230 | if (length < *descs_end) | ||
| 231 | return 0; | ||
| 232 | length -= *descs_end; | ||
| 233 | descs_end += *descs_end; | ||
| 234 | } while (--count); | ||
| 235 | |||
| 236 | /* Allocate legacy descriptors and copy the data. */ | ||
| 237 | { | ||
| 238 | #pragma GCC diagnostic push | ||
| 239 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | ||
| 240 | struct { | ||
| 241 | struct usb_functionfs_descs_head header; | ||
| 242 | __u8 descriptors[]; | ||
| 243 | } __attribute__((packed)) *out; | ||
| 244 | #pragma GCC diagnostic pop | ||
| 245 | |||
| 246 | length = sizeof out->header + (descs_end - descs_start); | ||
| 247 | out = malloc(length); | ||
| 248 | out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC); | ||
| 249 | out->header.length = cpu_to_le32(length); | ||
| 250 | out->header.fs_count = cpu_to_le32(fs_count); | ||
| 251 | out->header.hs_count = cpu_to_le32(hs_count); | ||
| 252 | memcpy(out->descriptors, descs_start, descs_end - descs_start); | ||
| 253 | *legacy = out; | ||
| 254 | } | ||
| 255 | |||
| 256 | return length; | ||
| 257 | } | ||
| 258 | |||
| 171 | 259 | ||
| 172 | #define STR_INTERFACE_ "Source/Sink" | 260 | #define STR_INTERFACE_ "Source/Sink" |
| 173 | 261 | ||
| @@ -487,12 +575,29 @@ ep0_consume(struct thread *ignore, const void *buf, size_t nbytes) | |||
| 487 | return nbytes; | 575 | return nbytes; |
| 488 | } | 576 | } |
| 489 | 577 | ||
| 490 | static void ep0_init(struct thread *t) | 578 | static void ep0_init(struct thread *t, bool legacy_descriptors) |
| 491 | { | 579 | { |
| 580 | void *legacy; | ||
| 492 | ssize_t ret; | 581 | ssize_t ret; |
| 582 | size_t len; | ||
| 583 | |||
| 584 | if (legacy_descriptors) { | ||
| 585 | info("%s: writing descriptors\n", t->filename); | ||
| 586 | goto legacy; | ||
| 587 | } | ||
| 493 | 588 | ||
| 494 | info("%s: writing descriptors\n", t->filename); | 589 | info("%s: writing descriptors (in v2 format)\n", t->filename); |
| 495 | ret = write(t->fd, &descriptors, sizeof descriptors); | 590 | ret = write(t->fd, &descriptors, sizeof descriptors); |
| 591 | |||
| 592 | if (ret < 0 && errno == EINVAL) { | ||
| 593 | warn("%s: new format rejected, trying legacy\n", t->filename); | ||
| 594 | legacy: | ||
| 595 | len = descs_to_legacy(&legacy, &descriptors); | ||
| 596 | if (len) { | ||
| 597 | ret = write(t->fd, legacy, len); | ||
| 598 | free(legacy); | ||
| 599 | } | ||
| 600 | } | ||
| 496 | die_on(ret < 0, "%s: write: descriptors", t->filename); | 601 | die_on(ret < 0, "%s: write: descriptors", t->filename); |
| 497 | 602 | ||
| 498 | info("%s: writing strings\n", t->filename); | 603 | info("%s: writing strings\n", t->filename); |
| @@ -503,14 +608,15 @@ static void ep0_init(struct thread *t) | |||
| 503 | 608 | ||
| 504 | /******************** Main **************************************************/ | 609 | /******************** Main **************************************************/ |
| 505 | 610 | ||
| 506 | int main(void) | 611 | int main(int argc, char **argv) |
| 507 | { | 612 | { |
| 613 | bool legacy_descriptors; | ||
| 508 | unsigned i; | 614 | unsigned i; |
| 509 | 615 | ||
| 510 | /* XXX TODO: Argument parsing missing */ | 616 | legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l"); |
| 511 | 617 | ||
| 512 | init_thread(threads); | 618 | init_thread(threads); |
| 513 | ep0_init(threads); | 619 | ep0_init(threads, legacy_descriptors); |
| 514 | 620 | ||
| 515 | for (i = 1; i < sizeof threads / sizeof *threads; ++i) | 621 | for (i = 1; i < sizeof threads / sizeof *threads; ++i) |
| 516 | init_thread(threads + i); | 622 | init_thread(threads + i); |
diff --git a/tools/usb/usbip/.gitignore b/tools/usb/usbip/.gitignore new file mode 100644 index 000000000000..9aad9e30a8ba --- /dev/null +++ b/tools/usb/usbip/.gitignore | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | Makefile | ||
| 2 | Makefile.in | ||
| 3 | aclocal.m4 | ||
| 4 | autom4te.cache/ | ||
| 5 | config.guess | ||
| 6 | config.h | ||
| 7 | config.h.in | ||
| 8 | config.log | ||
| 9 | config.status | ||
| 10 | config.sub | ||
| 11 | configure | ||
| 12 | depcomp | ||
| 13 | install-sh | ||
| 14 | libsrc/Makefile | ||
| 15 | libsrc/Makefile.in | ||
| 16 | libtool | ||
| 17 | ltmain.sh | ||
| 18 | missing | ||
| 19 | src/Makefile | ||
| 20 | src/Makefile.in | ||
| 21 | stamp-h1 | ||
| 22 | libsrc/libusbip.la | ||
| 23 | libsrc/libusbip_la-names.lo | ||
| 24 | libsrc/libusbip_la-usbip_common.lo | ||
| 25 | libsrc/libusbip_la-usbip_host_driver.lo | ||
| 26 | libsrc/libusbip_la-vhci_driver.lo | ||
| 27 | src/usbip | ||
| 28 | src/usbipd | ||
diff --git a/tools/usb/usbip/AUTHORS b/tools/usb/usbip/AUTHORS new file mode 100644 index 000000000000..a27ea8d03aec --- /dev/null +++ b/tools/usb/usbip/AUTHORS | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | Takahiro Hirofuchi | ||
| 2 | Robert Leibl | ||
| 3 | matt mooney <mfm@muteddisk.com> | ||
diff --git a/tools/usb/usbip/COPYING b/tools/usb/usbip/COPYING new file mode 100644 index 000000000000..c5611e48a8e1 --- /dev/null +++ b/tools/usb/usbip/COPYING | |||
| @@ -0,0 +1,340 @@ | |||
| 1 | GNU GENERAL PUBLIC LICENSE | ||
| 2 | Version 2, June 1991 | ||
| 3 | |||
| 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||
| 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 6 | Everyone is permitted to copy and distribute verbatim copies | ||
| 7 | of this license document, but changing it is not allowed. | ||
| 8 | |||
| 9 | Preamble | ||
| 10 | |||
| 11 | The licenses for most software are designed to take away your | ||
| 12 | freedom to share and change it. By contrast, the GNU General Public | ||
| 13 | License is intended to guarantee your freedom to share and change free | ||
| 14 | software--to make sure the software is free for all its users. This | ||
| 15 | General Public License applies to most of the Free Software | ||
| 16 | Foundation's software and to any other program whose authors commit to | ||
| 17 | using it. (Some other Free Software Foundation software is covered by | ||
| 18 | the GNU Library General Public License instead.) You can apply it to | ||
| 19 | your programs, too. | ||
| 20 | |||
| 21 | When we speak of free software, we are referring to freedom, not | ||
| 22 | price. Our General Public Licenses are designed to make sure that you | ||
| 23 | have the freedom to distribute copies of free software (and charge for | ||
| 24 | this service if you wish), that you receive source code or can get it | ||
| 25 | if you want it, that you can change the software or use pieces of it | ||
| 26 | in new free programs; and that you know you can do these things. | ||
| 27 | |||
| 28 | To protect your rights, we need to make restrictions that forbid | ||
| 29 | anyone to deny you these rights or to ask you to surrender the rights. | ||
| 30 | These restrictions translate to certain responsibilities for you if you | ||
| 31 | distribute copies of the software, or if you modify it. | ||
| 32 | |||
| 33 | For example, if you distribute copies of such a program, whether | ||
| 34 | gratis or for a fee, you must give the recipients all the rights that | ||
| 35 | you have. You must make sure that they, too, receive or can get the | ||
| 36 | source code. And you must show them these terms so they know their | ||
| 37 | rights. | ||
| 38 | |||
| 39 | We protect your rights with two steps: (1) copyright the software, and | ||
| 40 | (2) offer you this license which gives you legal permission to copy, | ||
| 41 | distribute and/or modify the software. | ||
| 42 | |||
| 43 | Also, for each author's protection and ours, we want to make certain | ||
| 44 | that everyone understands that there is no warranty for this free | ||
| 45 | software. If the software is modified by someone else and passed on, we | ||
| 46 | want its recipients to know that what they have is not the original, so | ||
| 47 | that any problems introduced by others will not reflect on the original | ||
| 48 | authors' reputations. | ||
| 49 | |||
| 50 | Finally, any free program is threatened constantly by software | ||
| 51 | patents. We wish to avoid the danger that redistributors of a free | ||
| 52 | program will individually obtain patent licenses, in effect making the | ||
| 53 | program proprietary. To prevent this, we have made it clear that any | ||
| 54 | patent must be licensed for everyone's free use or not licensed at all. | ||
| 55 | |||
| 56 | The precise terms and conditions for copying, distribution and | ||
| 57 | modification follow. | ||
| 58 | |||
| 59 | GNU GENERAL PUBLIC LICENSE | ||
| 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||
| 61 | |||
| 62 | 0. This License applies to any program or other work which contains | ||
| 63 | a notice placed by the copyright holder saying it may be distributed | ||
| 64 | under the terms of this General Public License. The "Program", below, | ||
| 65 | refers to any such program or work, and a "work based on the Program" | ||
| 66 | means either the Program or any derivative work under copyright law: | ||
| 67 | that is to say, a work containing the Program or a portion of it, | ||
| 68 | either verbatim or with modifications and/or translated into another | ||
| 69 | language. (Hereinafter, translation is included without limitation in | ||
| 70 | the term "modification".) Each licensee is addressed as "you". | ||
| 71 | |||
| 72 | Activities other than copying, distribution and modification are not | ||
| 73 | covered by this License; they are outside its scope. The act of | ||
| 74 | running the Program is not restricted, and the output from the Program | ||
| 75 | is covered only if its contents constitute a work based on the | ||
| 76 | Program (independent of having been made by running the Program). | ||
| 77 | Whether that is true depends on what the Program does. | ||
| 78 | |||
| 79 | 1. You may copy and distribute verbatim copies of the Program's | ||
| 80 | source code as you receive it, in any medium, provided that you | ||
| 81 | conspicuously and appropriately publish on each copy an appropriate | ||
| 82 | copyright notice and disclaimer of warranty; keep intact all the | ||
| 83 | notices that refer to this License and to the absence of any warranty; | ||
| 84 | and give any other recipients of the Program a copy of this License | ||
| 85 | along with the Program. | ||
| 86 | |||
| 87 | You may charge a fee for the physical act of transferring a copy, and | ||
| 88 | you may at your option offer warranty protection in exchange for a fee. | ||
| 89 | |||
| 90 | 2. You may modify your copy or copies of the Program or any portion | ||
| 91 | of it, thus forming a work based on the Program, and copy and | ||
| 92 | distribute such modifications or work under the terms of Section 1 | ||
| 93 | above, provided that you also meet all of these conditions: | ||
| 94 | |||
| 95 | a) You must cause the modified files to carry prominent notices | ||
| 96 | stating that you changed the files and the date of any change. | ||
| 97 | |||
| 98 | b) You must cause any work that you distribute or publish, that in | ||
| 99 | whole or in part contains or is derived from the Program or any | ||
| 100 | part thereof, to be licensed as a whole at no charge to all third | ||
| 101 | parties under the terms of this License. | ||
| 102 | |||
| 103 | c) If the modified program normally reads commands interactively | ||
| 104 | when run, you must cause it, when started running for such | ||
| 105 | interactive use in the most ordinary way, to print or display an | ||
| 106 | announcement including an appropriate copyright notice and a | ||
| 107 | notice that there is no warranty (or else, saying that you provide | ||
| 108 | a warranty) and that users may redistribute the program under | ||
| 109 | these conditions, and telling the user how to view a copy of this | ||
| 110 | License. (Exception: if the Program itself is interactive but | ||
| 111 | does not normally print such an announcement, your work based on | ||
| 112 | the Program is not required to print an announcement.) | ||
| 113 | |||
| 114 | These requirements apply to the modified work as a whole. If | ||
| 115 | identifiable sections of that work are not derived from the Program, | ||
| 116 | and can be reasonably considered independent and separate works in | ||
| 117 | themselves, then this License, and its terms, do not apply to those | ||
| 118 | sections when you distribute them as separate works. But when you | ||
| 119 | distribute the same sections as part of a whole which is a work based | ||
| 120 | on the Program, the distribution of the whole must be on the terms of | ||
| 121 | this License, whose permissions for other licensees extend to the | ||
| 122 | entire whole, and thus to each and every part regardless of who wrote it. | ||
| 123 | |||
| 124 | Thus, it is not the intent of this section to claim rights or contest | ||
| 125 | your rights to work written entirely by you; rather, the intent is to | ||
| 126 | exercise the right to control the distribution of derivative or | ||
| 127 | collective works based on the Program. | ||
| 128 | |||
| 129 | In addition, mere aggregation of another work not based on the Program | ||
| 130 | with the Program (or with a work based on the Program) on a volume of | ||
| 131 | a storage or distribution medium does not bring the other work under | ||
| 132 | the scope of this License. | ||
| 133 | |||
| 134 | 3. You may copy and distribute the Program (or a work based on it, | ||
| 135 | under Section 2) in object code or executable form under the terms of | ||
| 136 | Sections 1 and 2 above provided that you also do one of the following: | ||
| 137 | |||
| 138 | a) Accompany it with the complete corresponding machine-readable | ||
| 139 | source code, which must be distributed under the terms of Sections | ||
| 140 | 1 and 2 above on a medium customarily used for software interchange; or, | ||
| 141 | |||
| 142 | b) Accompany it with a written offer, valid for at least three | ||
| 143 | years, to give any third party, for a charge no more than your | ||
| 144 | cost of physically performing source distribution, a complete | ||
| 145 | machine-readable copy of the corresponding source code, to be | ||
| 146 | distributed under the terms of Sections 1 and 2 above on a medium | ||
| 147 | customarily used for software interchange; or, | ||
| 148 | |||
| 149 | c) Accompany it with the information you received as to the offer | ||
| 150 | to distribute corresponding source code. (This alternative is | ||
| 151 | allowed only for noncommercial distribution and only if you | ||
| 152 | received the program in object code or executable form with such | ||
| 153 | an offer, in accord with Subsection b above.) | ||
| 154 | |||
| 155 | The source code for a work means the preferred form of the work for | ||
| 156 | making modifications to it. For an executable work, complete source | ||
| 157 | code means all the source code for all modules it contains, plus any | ||
| 158 | associated interface definition files, plus the scripts used to | ||
| 159 | control compilation and installation of the executable. However, as a | ||
| 160 | special exception, the source code distributed need not include | ||
| 161 | anything that is normally distributed (in either source or binary | ||
| 162 | form) with the major components (compiler, kernel, and so on) of the | ||
| 163 | operating system on which the executable runs, unless that component | ||
| 164 | itself accompanies the executable. | ||
| 165 | |||
| 166 | If distribution of executable or object code is made by offering | ||
| 167 | access to copy from a designated place, then offering equivalent | ||
| 168 | access to copy the source code from the same place counts as | ||
| 169 | distribution of the source code, even though third parties are not | ||
| 170 | compelled to copy the source along with the object code. | ||
| 171 | |||
| 172 | 4. You may not copy, modify, sublicense, or distribute the Program | ||
| 173 | except as expressly provided under this License. Any attempt | ||
| 174 | otherwise to copy, modify, sublicense or distribute the Program is | ||
| 175 | void, and will automatically terminate your rights under this License. | ||
| 176 | However, parties who have received copies, or rights, from you under | ||
| 177 | this License will not have their licenses terminated so long as such | ||
| 178 | parties remain in full compliance. | ||
| 179 | |||
| 180 | 5. You are not required to accept this License, since you have not | ||
| 181 | signed it. However, nothing else grants you permission to modify or | ||
| 182 | distribute the Program or its derivative works. These actions are | ||
| 183 | prohibited by law if you do not accept this License. Therefore, by | ||
| 184 | modifying or distributing the Program (or any work based on the | ||
| 185 | Program), you indicate your acceptance of this License to do so, and | ||
| 186 | all its terms and conditions for copying, distributing or modifying | ||
| 187 | the Program or works based on it. | ||
| 188 | |||
| 189 | 6. Each time you redistribute the Program (or any work based on the | ||
| 190 | Program), the recipient automatically receives a license from the | ||
| 191 | original licensor to copy, distribute or modify the Program subject to | ||
| 192 | these terms and conditions. You may not impose any further | ||
| 193 | restrictions on the recipients' exercise of the rights granted herein. | ||
| 194 | You are not responsible for enforcing compliance by third parties to | ||
| 195 | this License. | ||
| 196 | |||
| 197 | 7. If, as a consequence of a court judgment or allegation of patent | ||
| 198 | infringement or for any other reason (not limited to patent issues), | ||
| 199 | conditions are imposed on you (whether by court order, agreement or | ||
| 200 | otherwise) that contradict the conditions of this License, they do not | ||
| 201 | excuse you from the conditions of this License. If you cannot | ||
| 202 | distribute so as to satisfy simultaneously your obligations under this | ||
| 203 | License and any other pertinent obligations, then as a consequence you | ||
| 204 | may not distribute the Program at all. For example, if a patent | ||
| 205 | license would not permit royalty-free redistribution of the Program by | ||
| 206 | all those who receive copies directly or indirectly through you, then | ||
| 207 | the only way you could satisfy both it and this License would be to | ||
| 208 | refrain entirely from distribution of the Program. | ||
| 209 | |||
| 210 | If any portion of this section is held invalid or unenforceable under | ||
| 211 | any particular circumstance, the balance of the section is intended to | ||
| 212 | apply and the section as a whole is intended to apply in other | ||
| 213 | circumstances. | ||
| 214 | |||
| 215 | It is not the purpose of this section to induce you to infringe any | ||
| 216 | patents or other property right claims or to contest validity of any | ||
| 217 | such claims; this section has the sole purpose of protecting the | ||
| 218 | integrity of the free software distribution system, which is | ||
| 219 | implemented by public license practices. Many people have made | ||
| 220 | generous contributions to the wide range of software distributed | ||
| 221 | through that system in reliance on consistent application of that | ||
| 222 | system; it is up to the author/donor to decide if he or she is willing | ||
| 223 | to distribute software through any other system and a licensee cannot | ||
| 224 | impose that choice. | ||
| 225 | |||
| 226 | This section is intended to make thoroughly clear what is believed to | ||
| 227 | be a consequence of the rest of this License. | ||
| 228 | |||
| 229 | 8. If the distribution and/or use of the Program is restricted in | ||
| 230 | certain countries either by patents or by copyrighted interfaces, the | ||
| 231 | original copyright holder who places the Program under this License | ||
| 232 | may add an explicit geographical distribution limitation excluding | ||
| 233 | those countries, so that distribution is permitted only in or among | ||
| 234 | countries not thus excluded. In such case, this License incorporates | ||
| 235 | the limitation as if written in the body of this License. | ||
| 236 | |||
| 237 | 9. The Free Software Foundation may publish revised and/or new versions | ||
| 238 | of the General Public License from time to time. Such new versions will | ||
| 239 | be similar in spirit to the present version, but may differ in detail to | ||
| 240 | address new problems or concerns. | ||
| 241 | |||
| 242 | Each version is given a distinguishing version number. If the Program | ||
| 243 | specifies a version number of this License which applies to it and "any | ||
| 244 | later version", you have the option of following the terms and conditions | ||
| 245 | either of that version or of any later version published by the Free | ||
| 246 | Software Foundation. If the Program does not specify a version number of | ||
| 247 | this License, you may choose any version ever published by the Free Software | ||
| 248 | Foundation. | ||
| 249 | |||
| 250 | 10. If you wish to incorporate parts of the Program into other free | ||
| 251 | programs whose distribution conditions are different, write to the author | ||
| 252 | to ask for permission. For software which is copyrighted by the Free | ||
| 253 | Software Foundation, write to the Free Software Foundation; we sometimes | ||
| 254 | make exceptions for this. Our decision will be guided by the two goals | ||
| 255 | of preserving the free status of all derivatives of our free software and | ||
| 256 | of promoting the sharing and reuse of software generally. | ||
| 257 | |||
| 258 | NO WARRANTY | ||
| 259 | |||
| 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||
| 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | ||
| 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | ||
| 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | ||
| 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
| 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | ||
| 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | ||
| 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | ||
| 268 | REPAIR OR CORRECTION. | ||
| 269 | |||
| 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||
| 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | ||
| 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | ||
| 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | ||
| 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | ||
| 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | ||
| 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||
| 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||
| 278 | POSSIBILITY OF SUCH DAMAGES. | ||
| 279 | |||
| 280 | END OF TERMS AND CONDITIONS | ||
| 281 | |||
| 282 | How to Apply These Terms to Your New Programs | ||
| 283 | |||
| 284 | If you develop a new program, and you want it to be of the greatest | ||
| 285 | possible use to the public, the best way to achieve this is to make it | ||
| 286 | free software which everyone can redistribute and change under these terms. | ||
| 287 | |||
| 288 | To do so, attach the following notices to the program. It is safest | ||
| 289 | to attach them to the start of each source file to most effectively | ||
| 290 | convey the exclusion of warranty; and each file should have at least | ||
| 291 | the "copyright" line and a pointer to where the full notice is found. | ||
| 292 | |||
| 293 | <one line to give the program's name and a brief idea of what it does.> | ||
| 294 | Copyright (C) <year> <name of author> | ||
| 295 | |||
| 296 | This program is free software; you can redistribute it and/or modify | ||
| 297 | it under the terms of the GNU General Public License as published by | ||
| 298 | the Free Software Foundation; either version 2 of the License, or | ||
| 299 | (at your option) any later version. | ||
| 300 | |||
| 301 | This program is distributed in the hope that it will be useful, | ||
| 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 304 | GNU General Public License for more details. | ||
| 305 | |||
| 306 | You should have received a copy of the GNU General Public License | ||
| 307 | along with this program; if not, write to the Free Software | ||
| 308 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 309 | |||
| 310 | |||
| 311 | Also add information on how to contact you by electronic and paper mail. | ||
| 312 | |||
| 313 | If the program is interactive, make it output a short notice like this | ||
| 314 | when it starts in an interactive mode: | ||
| 315 | |||
| 316 | Gnomovision version 69, Copyright (C) year name of author | ||
| 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||
| 318 | This is free software, and you are welcome to redistribute it | ||
| 319 | under certain conditions; type `show c' for details. | ||
| 320 | |||
| 321 | The hypothetical commands `show w' and `show c' should show the appropriate | ||
| 322 | parts of the General Public License. Of course, the commands you use may | ||
| 323 | be called something other than `show w' and `show c'; they could even be | ||
| 324 | mouse-clicks or menu items--whatever suits your program. | ||
| 325 | |||
| 326 | You should also get your employer (if you work as a programmer) or your | ||
| 327 | school, if any, to sign a "copyright disclaimer" for the program, if | ||
| 328 | necessary. Here is a sample; alter the names: | ||
| 329 | |||
| 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program | ||
| 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. | ||
| 332 | |||
| 333 | <signature of Ty Coon>, 1 April 1989 | ||
| 334 | Ty Coon, President of Vice | ||
| 335 | |||
| 336 | This General Public License does not permit incorporating your program into | ||
| 337 | proprietary programs. If your program is a subroutine library, you may | ||
| 338 | consider it more useful to permit linking proprietary applications with the | ||
| 339 | library. If this is what you want to do, use the GNU Library General | ||
| 340 | Public License instead of this License. | ||
diff --git a/tools/usb/usbip/INSTALL b/tools/usb/usbip/INSTALL new file mode 100644 index 000000000000..d3c5b40a9409 --- /dev/null +++ b/tools/usb/usbip/INSTALL | |||
| @@ -0,0 +1,237 @@ | |||
| 1 | Installation Instructions | ||
| 2 | ************************* | ||
| 3 | |||
| 4 | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, | ||
| 5 | 2006, 2007 Free Software Foundation, Inc. | ||
| 6 | |||
| 7 | This file is free documentation; the Free Software Foundation gives | ||
| 8 | unlimited permission to copy, distribute and modify it. | ||
| 9 | |||
| 10 | Basic Installation | ||
| 11 | ================== | ||
| 12 | |||
| 13 | Briefly, the shell commands `./configure; make; make install' should | ||
| 14 | configure, build, and install this package. The following | ||
| 15 | more-detailed instructions are generic; see the `README' file for | ||
| 16 | instructions specific to this package. | ||
| 17 | |||
| 18 | The `configure' shell script attempts to guess correct values for | ||
| 19 | various system-dependent variables used during compilation. It uses | ||
| 20 | those values to create a `Makefile' in each directory of the package. | ||
| 21 | It may also create one or more `.h' files containing system-dependent | ||
| 22 | definitions. Finally, it creates a shell script `config.status' that | ||
| 23 | you can run in the future to recreate the current configuration, and a | ||
| 24 | file `config.log' containing compiler output (useful mainly for | ||
| 25 | debugging `configure'). | ||
| 26 | |||
| 27 | It can also use an optional file (typically called `config.cache' | ||
| 28 | and enabled with `--cache-file=config.cache' or simply `-C') that saves | ||
| 29 | the results of its tests to speed up reconfiguring. Caching is | ||
| 30 | disabled by default to prevent problems with accidental use of stale | ||
| 31 | cache files. | ||
| 32 | |||
| 33 | If you need to do unusual things to compile the package, please try | ||
| 34 | to figure out how `configure' could check whether to do them, and mail | ||
| 35 | diffs or instructions to the address given in the `README' so they can | ||
| 36 | be considered for the next release. If you are using the cache, and at | ||
| 37 | some point `config.cache' contains results you don't want to keep, you | ||
| 38 | may remove or edit it. | ||
| 39 | |||
| 40 | The file `configure.ac' (or `configure.in') is used to create | ||
| 41 | `configure' by a program called `autoconf'. You need `configure.ac' if | ||
| 42 | you want to change it or regenerate `configure' using a newer version | ||
| 43 | of `autoconf'. | ||
| 44 | |||
| 45 | The simplest way to compile this package is: | ||
| 46 | |||
| 47 | 1. `cd' to the directory containing the package's source code and type | ||
| 48 | `./configure' to configure the package for your system. | ||
| 49 | |||
| 50 | Running `configure' might take a while. While running, it prints | ||
| 51 | some messages telling which features it is checking for. | ||
| 52 | |||
| 53 | 2. Type `make' to compile the package. | ||
| 54 | |||
| 55 | 3. Optionally, type `make check' to run any self-tests that come with | ||
| 56 | the package. | ||
| 57 | |||
| 58 | 4. Type `make install' to install the programs and any data files and | ||
| 59 | documentation. | ||
| 60 | |||
| 61 | 5. You can remove the program binaries and object files from the | ||
| 62 | source code directory by typing `make clean'. To also remove the | ||
| 63 | files that `configure' created (so you can compile the package for | ||
| 64 | a different kind of computer), type `make distclean'. There is | ||
| 65 | also a `make maintainer-clean' target, but that is intended mainly | ||
| 66 | for the package's developers. If you use it, you may have to get | ||
| 67 | all sorts of other programs in order to regenerate files that came | ||
| 68 | with the distribution. | ||
| 69 | |||
| 70 | 6. Often, you can also type `make uninstall' to remove the installed | ||
| 71 | files again. | ||
| 72 | |||
| 73 | Compilers and Options | ||
| 74 | ===================== | ||
| 75 | |||
| 76 | Some systems require unusual options for compilation or linking that the | ||
| 77 | `configure' script does not know about. Run `./configure --help' for | ||
| 78 | details on some of the pertinent environment variables. | ||
| 79 | |||
| 80 | You can give `configure' initial values for configuration parameters | ||
| 81 | by setting variables in the command line or in the environment. Here | ||
| 82 | is an example: | ||
| 83 | |||
| 84 | ./configure CC=c99 CFLAGS=-g LIBS=-lposix | ||
| 85 | |||
| 86 | *Note Defining Variables::, for more details. | ||
| 87 | |||
| 88 | Compiling For Multiple Architectures | ||
| 89 | ==================================== | ||
| 90 | |||
| 91 | You can compile the package for more than one kind of computer at the | ||
| 92 | same time, by placing the object files for each architecture in their | ||
| 93 | own directory. To do this, you can use GNU `make'. `cd' to the | ||
| 94 | directory where you want the object files and executables to go and run | ||
| 95 | the `configure' script. `configure' automatically checks for the | ||
| 96 | source code in the directory that `configure' is in and in `..'. | ||
| 97 | |||
| 98 | With a non-GNU `make', it is safer to compile the package for one | ||
| 99 | architecture at a time in the source code directory. After you have | ||
| 100 | installed the package for one architecture, use `make distclean' before | ||
| 101 | reconfiguring for another architecture. | ||
| 102 | |||
| 103 | Installation Names | ||
| 104 | ================== | ||
| 105 | |||
| 106 | By default, `make install' installs the package's commands under | ||
| 107 | `/usr/local/bin', include files under `/usr/local/include', etc. You | ||
| 108 | can specify an installation prefix other than `/usr/local' by giving | ||
| 109 | `configure' the option `--prefix=PREFIX'. | ||
| 110 | |||
| 111 | You can specify separate installation prefixes for | ||
| 112 | architecture-specific files and architecture-independent files. If you | ||
| 113 | pass the option `--exec-prefix=PREFIX' to `configure', the package uses | ||
| 114 | PREFIX as the prefix for installing programs and libraries. | ||
| 115 | Documentation and other data files still use the regular prefix. | ||
| 116 | |||
| 117 | In addition, if you use an unusual directory layout you can give | ||
| 118 | options like `--bindir=DIR' to specify different values for particular | ||
| 119 | kinds of files. Run `configure --help' for a list of the directories | ||
| 120 | you can set and what kinds of files go in them. | ||
| 121 | |||
| 122 | If the package supports it, you can cause programs to be installed | ||
| 123 | with an extra prefix or suffix on their names by giving `configure' the | ||
| 124 | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. | ||
| 125 | |||
| 126 | Optional Features | ||
| 127 | ================= | ||
| 128 | |||
| 129 | Some packages pay attention to `--enable-FEATURE' options to | ||
| 130 | `configure', where FEATURE indicates an optional part of the package. | ||
| 131 | They may also pay attention to `--with-PACKAGE' options, where PACKAGE | ||
| 132 | is something like `gnu-as' or `x' (for the X Window System). The | ||
| 133 | `README' should mention any `--enable-' and `--with-' options that the | ||
| 134 | package recognizes. | ||
| 135 | |||
| 136 | For packages that use the X Window System, `configure' can usually | ||
| 137 | find the X include and library files automatically, but if it doesn't, | ||
| 138 | you can use the `configure' options `--x-includes=DIR' and | ||
| 139 | `--x-libraries=DIR' to specify their locations. | ||
| 140 | |||
| 141 | Specifying the System Type | ||
| 142 | ========================== | ||
| 143 | |||
| 144 | There may be some features `configure' cannot figure out automatically, | ||
| 145 | but needs to determine by the type of machine the package will run on. | ||
| 146 | Usually, assuming the package is built to be run on the _same_ | ||
| 147 | architectures, `configure' can figure that out, but if it prints a | ||
| 148 | message saying it cannot guess the machine type, give it the | ||
| 149 | `--build=TYPE' option. TYPE can either be a short name for the system | ||
| 150 | type, such as `sun4', or a canonical name which has the form: | ||
| 151 | |||
| 152 | CPU-COMPANY-SYSTEM | ||
| 153 | |||
| 154 | where SYSTEM can have one of these forms: | ||
| 155 | |||
| 156 | OS KERNEL-OS | ||
| 157 | |||
| 158 | See the file `config.sub' for the possible values of each field. If | ||
| 159 | `config.sub' isn't included in this package, then this package doesn't | ||
| 160 | need to know the machine type. | ||
| 161 | |||
| 162 | If you are _building_ compiler tools for cross-compiling, you should | ||
| 163 | use the option `--target=TYPE' to select the type of system they will | ||
| 164 | produce code for. | ||
| 165 | |||
| 166 | If you want to _use_ a cross compiler, that generates code for a | ||
| 167 | platform different from the build platform, you should specify the | ||
| 168 | "host" platform (i.e., that on which the generated programs will | ||
| 169 | eventually be run) with `--host=TYPE'. | ||
| 170 | |||
| 171 | Sharing Defaults | ||
| 172 | ================ | ||
| 173 | |||
| 174 | If you want to set default values for `configure' scripts to share, you | ||
| 175 | can create a site shell script called `config.site' that gives default | ||
| 176 | values for variables like `CC', `cache_file', and `prefix'. | ||
| 177 | `configure' looks for `PREFIX/share/config.site' if it exists, then | ||
| 178 | `PREFIX/etc/config.site' if it exists. Or, you can set the | ||
| 179 | `CONFIG_SITE' environment variable to the location of the site script. | ||
| 180 | A warning: not all `configure' scripts look for a site script. | ||
| 181 | |||
| 182 | Defining Variables | ||
| 183 | ================== | ||
| 184 | |||
| 185 | Variables not defined in a site shell script can be set in the | ||
| 186 | environment passed to `configure'. However, some packages may run | ||
| 187 | configure again during the build, and the customized values of these | ||
| 188 | variables may be lost. In order to avoid this problem, you should set | ||
| 189 | them in the `configure' command line, using `VAR=value'. For example: | ||
| 190 | |||
| 191 | ./configure CC=/usr/local2/bin/gcc | ||
| 192 | |||
| 193 | causes the specified `gcc' to be used as the C compiler (unless it is | ||
| 194 | overridden in the site shell script). | ||
| 195 | |||
| 196 | Unfortunately, this technique does not work for `CONFIG_SHELL' due to | ||
| 197 | an Autoconf bug. Until the bug is fixed you can use this workaround: | ||
| 198 | |||
| 199 | CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash | ||
| 200 | |||
| 201 | `configure' Invocation | ||
| 202 | ====================== | ||
| 203 | |||
| 204 | `configure' recognizes the following options to control how it operates. | ||
| 205 | |||
| 206 | `--help' | ||
| 207 | `-h' | ||
| 208 | Print a summary of the options to `configure', and exit. | ||
| 209 | |||
| 210 | `--version' | ||
| 211 | `-V' | ||
| 212 | Print the version of Autoconf used to generate the `configure' | ||
| 213 | script, and exit. | ||
| 214 | |||
| 215 | `--cache-file=FILE' | ||
| 216 | Enable the cache: use and save the results of the tests in FILE, | ||
| 217 | traditionally `config.cache'. FILE defaults to `/dev/null' to | ||
| 218 | disable caching. | ||
| 219 | |||
| 220 | `--config-cache' | ||
| 221 | `-C' | ||
| 222 | Alias for `--cache-file=config.cache'. | ||
| 223 | |||
| 224 | `--quiet' | ||
| 225 | `--silent' | ||
| 226 | `-q' | ||
| 227 | Do not print messages saying which checks are being made. To | ||
| 228 | suppress all normal output, redirect it to `/dev/null' (any error | ||
| 229 | messages will still be shown). | ||
| 230 | |||
| 231 | `--srcdir=DIR' | ||
| 232 | Look for the package's source code in directory DIR. Usually | ||
| 233 | `configure' can determine that directory automatically. | ||
| 234 | |||
| 235 | `configure' also accepts some other, not widely useful, options. Run | ||
| 236 | `configure --help' for more details. | ||
| 237 | |||
diff --git a/tools/usb/usbip/Makefile.am b/tools/usb/usbip/Makefile.am new file mode 100644 index 000000000000..66f8bf038c9f --- /dev/null +++ b/tools/usb/usbip/Makefile.am | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | SUBDIRS := libsrc src | ||
| 2 | includedir = @includedir@/usbip | ||
| 3 | include_HEADERS := $(addprefix libsrc/, \ | ||
| 4 | usbip_common.h vhci_driver.h usbip_host_driver.h) | ||
| 5 | |||
| 6 | dist_man_MANS := $(addprefix doc/, usbip.8 usbipd.8) | ||
diff --git a/tools/usb/usbip/README b/tools/usb/usbip/README new file mode 100644 index 000000000000..831f49fea3ce --- /dev/null +++ b/tools/usb/usbip/README | |||
| @@ -0,0 +1,202 @@ | |||
| 1 | # | ||
| 2 | # README for usbip-utils | ||
| 3 | # | ||
| 4 | # Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 5 | # 2005-2008 Takahiro Hirofuchi | ||
| 6 | |||
| 7 | |||
| 8 | [Requirements] | ||
| 9 | - USB/IP device drivers | ||
| 10 | Found in the staging directory of the Linux kernel. | ||
| 11 | |||
| 12 | - libudev >= 2.0 | ||
| 13 | libudev library | ||
| 14 | |||
| 15 | - libwrap0-dev | ||
| 16 | tcp wrapper library | ||
| 17 | |||
| 18 | - gcc >= 4.0 | ||
| 19 | |||
| 20 | - libtool, automake >= 1.9, autoconf >= 2.5.0, pkg-config | ||
| 21 | |||
| 22 | [Optional] | ||
| 23 | - hwdata | ||
| 24 | Contains USB device identification data. | ||
| 25 | |||
| 26 | |||
| 27 | [Install] | ||
| 28 | 0. Generate configuration scripts. | ||
| 29 | $ ./autogen.sh | ||
| 30 | |||
| 31 | 1. Compile & install the userspace utilities. | ||
| 32 | $ ./configure [--with-tcp-wrappers=no] [--with-usbids-dir=<dir>] | ||
| 33 | $ make install | ||
| 34 | |||
| 35 | 2. Compile & install USB/IP drivers. | ||
| 36 | |||
| 37 | |||
| 38 | [Usage] | ||
| 39 | server:# (Physically attach your USB device.) | ||
| 40 | |||
| 41 | server:# insmod usbip-core.ko | ||
| 42 | server:# insmod usbip-host.ko | ||
| 43 | |||
| 44 | server:# usbipd -D | ||
| 45 | - Start usbip daemon. | ||
| 46 | |||
| 47 | server:# usbip list -l | ||
| 48 | - List driver assignments for USB devices. | ||
| 49 | |||
| 50 | server:# usbip bind --busid 1-2 | ||
| 51 | - Bind usbip-host.ko to the device with busid 1-2. | ||
| 52 | - The USB device 1-2 is now exportable to other hosts! | ||
| 53 | - Use `usbip unbind --busid 1-2' to stop exporting the device. | ||
| 54 | |||
| 55 | client:# insmod usbip-core.ko | ||
| 56 | client:# insmod vhci-hcd.ko | ||
| 57 | |||
| 58 | client:# usbip list --remote <host> | ||
| 59 | - List exported USB devices on the <host>. | ||
| 60 | |||
| 61 | client:# usbip attach --remote <host> --busid 1-2 | ||
| 62 | - Connect the remote USB device. | ||
| 63 | |||
| 64 | client:# usbip port | ||
| 65 | - Show virtual port status. | ||
| 66 | |||
| 67 | client:# usbip detach --port <port> | ||
| 68 | - Detach the USB device. | ||
| 69 | |||
| 70 | |||
| 71 | [Example] | ||
| 72 | --------------------------- | ||
| 73 | SERVER SIDE | ||
| 74 | --------------------------- | ||
| 75 | Physically attach your USB devices to this host. | ||
| 76 | |||
| 77 | trois:# insmod path/to/usbip-core.ko | ||
| 78 | trois:# insmod path/to/usbip-host.ko | ||
| 79 | trois:# usbipd -D | ||
| 80 | |||
| 81 | In another terminal, let's look up what USB devices are physically | ||
| 82 | attached to this host. | ||
| 83 | |||
| 84 | trois:# usbip list -l | ||
| 85 | Local USB devices | ||
| 86 | ================= | ||
| 87 | - busid 1-1 (05a9:a511) | ||
| 88 | 1-1:1.0 -> ov511 | ||
| 89 | |||
| 90 | - busid 3-2 (0711:0902) | ||
| 91 | 3-2:1.0 -> none | ||
| 92 | |||
| 93 | - busid 3-3.1 (08bb:2702) | ||
| 94 | 3-3.1:1.0 -> snd-usb-audio | ||
| 95 | 3-3.1:1.1 -> snd-usb-audio | ||
| 96 | |||
| 97 | - busid 3-3.2 (04bb:0206) | ||
| 98 | 3-3.2:1.0 -> usb-storage | ||
| 99 | |||
| 100 | - busid 3-3 (0409:0058) | ||
| 101 | 3-3:1.0 -> hub | ||
| 102 | |||
| 103 | - busid 4-1 (046d:08b2) | ||
| 104 | 4-1:1.0 -> none | ||
| 105 | 4-1:1.1 -> none | ||
| 106 | 4-1:1.2 -> none | ||
| 107 | |||
| 108 | - busid 5-2 (058f:9254) | ||
| 109 | 5-2:1.0 -> hub | ||
| 110 | |||
| 111 | A USB storage device of busid 3-3.2 is now bound to the usb-storage | ||
| 112 | driver. To export this device, we first mark the device as | ||
| 113 | "exportable"; the device is bound to the usbip-host driver. Please | ||
| 114 | remember you can not export a USB hub. | ||
| 115 | |||
| 116 | Mark the device of busid 3-3.2 as exportable: | ||
| 117 | |||
| 118 | trois:# usbip --debug bind --busid 3-3.2 | ||
| 119 | ... | ||
| 120 | usbip debug: usbip_bind.c:162:[unbind_other] 3-3.2:1.0 -> usb-storage | ||
| 121 | ... | ||
| 122 | bind device on busid 3-3.2: complete | ||
| 123 | |||
| 124 | trois:# usbip list -l | ||
| 125 | Local USB devices | ||
| 126 | ================= | ||
| 127 | ... | ||
| 128 | |||
| 129 | - busid 3-3.2 (04bb:0206) | ||
| 130 | 3-3.2:1.0 -> usbip-host | ||
| 131 | ... | ||
| 132 | |||
| 133 | --------------------------- | ||
| 134 | CLIENT SIDE | ||
| 135 | --------------------------- | ||
| 136 | First, let's list available remote devices that are marked as | ||
| 137 | exportable on the host. | ||
| 138 | |||
| 139 | deux:# insmod path/to/usbip-core.ko | ||
| 140 | deux:# insmod path/to/vhci-hcd.ko | ||
| 141 | |||
| 142 | deux:# usbip list --remote 10.0.0.3 | ||
| 143 | Exportable USB devices | ||
| 144 | ====================== | ||
| 145 | - 10.0.0.3 | ||
| 146 | 1-1: Prolific Technology, Inc. : unknown product (067b:3507) | ||
| 147 | : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-1 | ||
| 148 | : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00) | ||
| 149 | : 0 - Mass Storage / SCSI / Bulk (Zip) (08/06/50) | ||
| 150 | |||
| 151 | 1-2.2.1: Apple Computer, Inc. : unknown product (05ac:0203) | ||
| 152 | : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.1 | ||
| 153 | : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00) | ||
| 154 | : 0 - Human Interface Devices / Boot Interface Subclass / Keyboard (03/01/01) | ||
| 155 | |||
| 156 | 1-2.2.3: OmniVision Technologies, Inc. : OV511+ WebCam (05a9:a511) | ||
| 157 | : /sys/devices/pci0000:00/0000:00:1f.2/usb1/1-2/1-2.2/1-2.2.3 | ||
| 158 | : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00) | ||
| 159 | : 0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/00) | ||
| 160 | |||
| 161 | 3-1: Logitech, Inc. : QuickCam Pro 4000 (046d:08b2) | ||
| 162 | : /sys/devices/pci0000:00/0000:00:1e.0/0000:02:0a.0/usb3/3-1 | ||
| 163 | : (Defined at Interface level) / unknown subclass / unknown protocol (00/00/00) | ||
| 164 | : 0 - Data / unknown subclass / unknown protocol (0a/ff/00) | ||
| 165 | : 1 - Audio / Control Device / unknown protocol (01/01/00) | ||
| 166 | : 2 - Audio / Streaming / unknown protocol (01/02/00) | ||
| 167 | |||
| 168 | Attach a remote USB device: | ||
| 169 | |||
| 170 | deux:# usbip attach --remote 10.0.0.3 --busid 1-1 | ||
| 171 | port 0 attached | ||
| 172 | |||
| 173 | Show the devices attached to this client: | ||
| 174 | |||
| 175 | deux:# usbip port | ||
| 176 | Port 00: <Port in Use> at Full Speed(12Mbps) | ||
| 177 | Prolific Technology, Inc. : unknown product (067b:3507) | ||
| 178 | 6-1 -> usbip://10.0.0.3:3240/1-1 (remote bus/dev 001/004) | ||
| 179 | 6-1:1.0 used by usb-storage | ||
| 180 | /sys/class/scsi_device/0:0:0:0/device | ||
| 181 | /sys/class/scsi_host/host0/device | ||
| 182 | /sys/block/sda/device | ||
| 183 | |||
| 184 | Detach the imported device: | ||
| 185 | |||
| 186 | deux:# usbip detach --port 0 | ||
| 187 | port 0 detached | ||
| 188 | |||
| 189 | |||
| 190 | [Checklist] | ||
| 191 | - See 'Debug Tips' on the project wiki. | ||
| 192 | - http://usbip.wiki.sourceforge.net/how-to-debug-usbip | ||
| 193 | - usbip-host.ko must be bound to the target device. | ||
| 194 | - See /proc/bus/usb/devices and find "Driver=..." lines of the device. | ||
| 195 | - Shutdown firewall. | ||
| 196 | - usbip now uses TCP port 3240. | ||
| 197 | - Disable SELinux. | ||
| 198 | - Check the kernel and daemon messages. | ||
| 199 | |||
| 200 | |||
| 201 | [Contact] | ||
| 202 | Mailing List: linux-usb@vger.kernel.org | ||
diff --git a/tools/usb/usbip/autogen.sh b/tools/usb/usbip/autogen.sh new file mode 100755 index 000000000000..e1112d3fcbf6 --- /dev/null +++ b/tools/usb/usbip/autogen.sh | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #!/bin/sh -x | ||
| 2 | |||
| 3 | #aclocal | ||
| 4 | #autoheader | ||
| 5 | #libtoolize --copy --force | ||
| 6 | #automake-1.9 -acf | ||
| 7 | #autoconf | ||
| 8 | |||
| 9 | autoreconf -i -f -v | ||
diff --git a/tools/usb/usbip/cleanup.sh b/tools/usb/usbip/cleanup.sh new file mode 100755 index 000000000000..955c3ccb729a --- /dev/null +++ b/tools/usb/usbip/cleanup.sh | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | if [ -r Makefile ]; then | ||
| 4 | make distclean | ||
| 5 | fi | ||
| 6 | |||
| 7 | FILES="aclocal.m4 autom4te.cache compile config.guess config.h.in config.log \ | ||
| 8 | config.status config.sub configure cscope.out depcomp install-sh \ | ||
| 9 | libsrc/Makefile libsrc/Makefile.in libtool ltmain.sh Makefile \ | ||
| 10 | Makefile.in missing src/Makefile src/Makefile.in" | ||
| 11 | |||
| 12 | rm -vRf $FILES | ||
diff --git a/tools/usb/usbip/configure.ac b/tools/usb/usbip/configure.ac new file mode 100644 index 000000000000..607d05c5ccfd --- /dev/null +++ b/tools/usb/usbip/configure.ac | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | dnl Process this file with autoconf to produce a configure script. | ||
| 2 | |||
| 3 | AC_PREREQ(2.59) | ||
| 4 | AC_INIT([usbip-utils], [2.0], [linux-usb@vger.kernel.org]) | ||
| 5 | AC_DEFINE([USBIP_VERSION], [0x00000111], [binary-coded decimal version number]) | ||
| 6 | |||
| 7 | CURRENT=0 | ||
| 8 | REVISION=1 | ||
| 9 | AGE=0 | ||
| 10 | AC_SUBST([LIBUSBIP_VERSION], [$CURRENT:$REVISION:$AGE], [library version]) | ||
| 11 | |||
| 12 | AC_CONFIG_SRCDIR([src/usbipd.c]) | ||
| 13 | AC_CONFIG_HEADERS([config.h]) | ||
| 14 | |||
| 15 | AM_INIT_AUTOMAKE([foreign]) | ||
| 16 | LT_INIT | ||
| 17 | |||
| 18 | # Silent build for automake >= 1.11 | ||
| 19 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) | ||
| 20 | |||
| 21 | AC_SUBST([EXTRA_CFLAGS], ["-Wall -Werror -Wextra -std=gnu99"]) | ||
| 22 | |||
| 23 | # Checks for programs. | ||
| 24 | AC_PROG_CC | ||
| 25 | AC_PROG_INSTALL | ||
| 26 | AC_PROG_MAKE_SET | ||
| 27 | |||
| 28 | # Checks for header files. | ||
| 29 | AC_HEADER_DIRENT | ||
| 30 | AC_HEADER_STDC | ||
| 31 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdint.h stdlib.h dnl | ||
| 32 | string.h sys/socket.h syslog.h unistd.h]) | ||
| 33 | |||
| 34 | # Checks for typedefs, structures, and compiler characteristics. | ||
| 35 | AC_TYPE_INT32_T | ||
| 36 | AC_TYPE_SIZE_T | ||
| 37 | AC_TYPE_SSIZE_T | ||
| 38 | AC_TYPE_UINT16_T | ||
| 39 | AC_TYPE_UINT32_T | ||
| 40 | AC_TYPE_UINT8_T | ||
| 41 | |||
| 42 | # Checks for library functions. | ||
| 43 | AC_FUNC_REALLOC | ||
| 44 | AC_CHECK_FUNCS([memset mkdir regcomp socket strchr strerror strstr dnl | ||
| 45 | strtoul]) | ||
| 46 | |||
| 47 | AC_CHECK_HEADER([libudev.h], | ||
| 48 | [AC_CHECK_LIB([udev], [udev_new], | ||
| 49 | [LIBS="$LIBS -ludev"], | ||
| 50 | [AC_MSG_ERROR([Missing udev library!])])], | ||
| 51 | [AC_MSG_ERROR([Missing /usr/include/libudev.h])]) | ||
| 52 | |||
| 53 | # Checks for libwrap library. | ||
| 54 | AC_MSG_CHECKING([whether to use the libwrap (TCP wrappers) library]) | ||
| 55 | AC_ARG_WITH([tcp-wrappers], | ||
| 56 | [AS_HELP_STRING([--with-tcp-wrappers], | ||
| 57 | [use the libwrap (TCP wrappers) library])], | ||
| 58 | dnl [ACTION-IF-GIVEN] | ||
| 59 | [if test "$withval" = "yes"; then | ||
| 60 | AC_MSG_RESULT([yes]) | ||
| 61 | AC_MSG_CHECKING([for hosts_access in -lwrap]) | ||
| 62 | saved_LIBS="$LIBS" | ||
| 63 | LIBS="-lwrap $saved_LIBS" | ||
| 64 | AC_TRY_LINK( | ||
| 65 | [int hosts_access(); int allow_severity, deny_severity;], | ||
| 66 | [hosts_access()], | ||
| 67 | [AC_MSG_RESULT([yes]); | ||
| 68 | AC_DEFINE([HAVE_LIBWRAP], [1], | ||
| 69 | [use tcp wrapper]) wrap_LIB="-lwrap"], | ||
| 70 | [AC_MSG_RESULT([not found]); exit 1]) | ||
| 71 | else | ||
| 72 | AC_MSG_RESULT([no]); | ||
| 73 | fi], | ||
| 74 | dnl [ACTION-IF-NOT-GIVEN] | ||
| 75 | [AC_MSG_RESULT([(default)]) | ||
| 76 | AC_MSG_CHECKING([for hosts_access in -lwrap]) | ||
| 77 | saved_LIBS="$LIBS" | ||
| 78 | LIBS="-lwrap $saved_LIBS" | ||
| 79 | AC_TRY_LINK( | ||
| 80 | [int hosts_access(); int allow_severity, deny_severity;], | ||
| 81 | [hosts_access()], | ||
| 82 | [AC_MSG_RESULT([yes]); | ||
| 83 | AC_DEFINE([HAVE_LIBWRAP], [1], [use tcp wrapper])], | ||
| 84 | [AC_MSG_RESULT([no]); LIBS="$saved_LIBS"])]) | ||
| 85 | |||
| 86 | # Sets directory containing usb.ids. | ||
| 87 | AC_ARG_WITH([usbids-dir], | ||
| 88 | [AS_HELP_STRING([--with-usbids-dir=DIR], | ||
| 89 | [where usb.ids is found (default /usr/share/hwdata/)])], | ||
| 90 | [USBIDS_DIR=$withval], [USBIDS_DIR="/usr/share/hwdata/"]) | ||
| 91 | AC_SUBST([USBIDS_DIR]) | ||
| 92 | |||
| 93 | # use _FORTIFY_SOURCE | ||
| 94 | AC_MSG_CHECKING([whether to use fortify]) | ||
| 95 | AC_ARG_WITH([fortify], | ||
| 96 | [AS_HELP_STRING([--with-fortify], | ||
| 97 | [use _FORTIFY_SROUCE option when compiling)])], | ||
| 98 | dnl [ACTION-IF-GIVEN] | ||
| 99 | [if test "$withval" = "yes"; then | ||
| 100 | AC_MSG_RESULT([yes]) | ||
| 101 | CFLAGS="$CFLAGS -D_FORTIFY_SOURCE -O" | ||
| 102 | else | ||
| 103 | AC_MSG_RESULT([no]) | ||
| 104 | CFLAGS="$CFLAGS -U_FORTIFY_SOURCE" | ||
| 105 | fi | ||
| 106 | ], | ||
| 107 | dnl [ACTION-IF-NOT-GIVEN] | ||
| 108 | [AC_MSG_RESULT([default])]) | ||
| 109 | |||
| 110 | AC_CONFIG_FILES([Makefile libsrc/Makefile src/Makefile]) | ||
| 111 | AC_OUTPUT | ||
diff --git a/tools/usb/usbip/doc/usbip.8 b/tools/usb/usbip/doc/usbip.8 new file mode 100644 index 000000000000..a6097be25d28 --- /dev/null +++ b/tools/usb/usbip/doc/usbip.8 | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | .TH USBIP "8" "February 2009" "usbip" "System Administration Utilities" | ||
| 2 | .SH NAME | ||
| 3 | usbip \- manage USB/IP devices | ||
| 4 | .SH SYNOPSIS | ||
| 5 | .B usbip | ||
| 6 | [\fIoptions\fR] <\fIcommand\fR> <\fIargs\fR> | ||
| 7 | |||
| 8 | .SH DESCRIPTION | ||
| 9 | On a USB/IP server, devices can be listed, bound, and unbound using | ||
| 10 | this program. On a USB/IP client, devices exported by USB/IP servers | ||
| 11 | can be listed, attached and detached. | ||
| 12 | |||
| 13 | .SH OPTIONS | ||
| 14 | .HP | ||
| 15 | \fB\-\-debug\fR | ||
| 16 | .IP | ||
| 17 | Print debugging information. | ||
| 18 | .PP | ||
| 19 | |||
| 20 | .HP | ||
| 21 | \fB\-\-log\fR | ||
| 22 | .IP | ||
| 23 | Log to syslog. | ||
| 24 | .PP | ||
| 25 | |||
| 26 | .HP | ||
| 27 | \fB\-\-tcp-port PORT\fR | ||
| 28 | .IP | ||
| 29 | Connect to PORT on remote host (used for attach and list --remote). | ||
| 30 | .PP | ||
| 31 | |||
| 32 | .SH COMMANDS | ||
| 33 | .HP | ||
| 34 | \fBversion\fR | ||
| 35 | .IP | ||
| 36 | Show version and exit. | ||
| 37 | .PP | ||
| 38 | |||
| 39 | .HP | ||
| 40 | \fBhelp\fR [\fIcommand\fR] | ||
| 41 | .IP | ||
| 42 | Print the program help message, or help on a specific command, and | ||
| 43 | then exit. | ||
| 44 | .PP | ||
| 45 | |||
| 46 | .HP | ||
| 47 | \fBattach\fR \-\-remote=<\fIhost\fR> \-\-busid=<\fIbus_id\fR> | ||
| 48 | .IP | ||
| 49 | Attach a remote USB device. | ||
| 50 | .PP | ||
| 51 | |||
| 52 | .HP | ||
| 53 | \fBdetach\fR \-\-port=<\fIport\fR> | ||
| 54 | .IP | ||
| 55 | Detach an imported USB device. | ||
| 56 | .PP | ||
| 57 | |||
| 58 | .HP | ||
| 59 | \fBbind\fR \-\-busid=<\fIbusid\fR> | ||
| 60 | .IP | ||
| 61 | Make a device exportable. | ||
| 62 | .PP | ||
| 63 | |||
| 64 | .HP | ||
| 65 | \fBunbind\fR \-\-busid=<\fIbusid\fR> | ||
| 66 | .IP | ||
| 67 | Stop exporting a device so it can be used by a local driver. | ||
| 68 | .PP | ||
| 69 | |||
| 70 | .HP | ||
| 71 | \fBlist\fR \-\-remote=<\fIhost\fR> | ||
| 72 | .IP | ||
| 73 | List USB devices exported by a remote host. | ||
| 74 | .PP | ||
| 75 | |||
| 76 | .HP | ||
| 77 | \fBlist\fR \-\-local | ||
| 78 | .IP | ||
| 79 | List local USB devices. | ||
| 80 | .PP | ||
| 81 | |||
| 82 | |||
| 83 | .SH EXAMPLES | ||
| 84 | |||
| 85 | client:# usbip list --remote=server | ||
| 86 | - List exportable usb devices on the server. | ||
| 87 | |||
| 88 | client:# usbip attach --remote=server --busid=1-2 | ||
| 89 | - Connect the remote USB device. | ||
| 90 | |||
| 91 | client:# usbip detach --port=0 | ||
| 92 | - Detach the usb device. | ||
| 93 | |||
| 94 | .SH "SEE ALSO" | ||
| 95 | \fBusbipd\fP\fB(8)\fB\fP | ||
diff --git a/tools/usb/usbip/doc/usbipd.8 b/tools/usb/usbip/doc/usbipd.8 new file mode 100644 index 000000000000..ac4635db3f03 --- /dev/null +++ b/tools/usb/usbip/doc/usbipd.8 | |||
| @@ -0,0 +1,91 @@ | |||
| 1 | .TH USBIP "8" "February 2009" "usbip" "System Administration Utilities" | ||
| 2 | .SH NAME | ||
| 3 | usbipd \- USB/IP server daemon | ||
| 4 | .SH SYNOPSIS | ||
| 5 | .B usbipd | ||
| 6 | [\fIoptions\fR] | ||
| 7 | |||
| 8 | .SH DESCRIPTION | ||
| 9 | .B usbipd | ||
| 10 | provides USB/IP clients access to exported USB devices. | ||
| 11 | |||
| 12 | Devices have to explicitly be exported using | ||
| 13 | .B usbip bind | ||
| 14 | before usbipd makes them available to other hosts. | ||
| 15 | |||
| 16 | The daemon accepts connections from USB/IP clients | ||
| 17 | on TCP port 3240 by default. | ||
| 18 | |||
| 19 | .SH OPTIONS | ||
| 20 | .HP | ||
| 21 | \fB\-4\fR, \fB\-\-ipv4\fR | ||
| 22 | .IP | ||
| 23 | Bind to IPv4. Default is both. | ||
| 24 | .PP | ||
| 25 | |||
| 26 | .HP | ||
| 27 | \fB\-6\fR, \fB\-\-ipv6\fR | ||
| 28 | .IP | ||
| 29 | Bind to IPv6. Default is both. | ||
| 30 | .PP | ||
| 31 | |||
| 32 | .HP | ||
| 33 | \fB\-D\fR, \fB\-\-daemon\fR | ||
| 34 | .IP | ||
| 35 | Run as a daemon process. | ||
| 36 | .PP | ||
| 37 | |||
| 38 | .HP | ||
| 39 | \fB\-d\fR, \fB\-\-debug\fR | ||
| 40 | .IP | ||
| 41 | Print debugging information. | ||
| 42 | .PP | ||
| 43 | |||
| 44 | .HP | ||
| 45 | \fB\-PFILE\fR, \fB\-\-pid FILE\fR | ||
| 46 | .IP | ||
| 47 | Write process id to FILE. | ||
| 48 | .br | ||
| 49 | If no FILE specified, use /var/run/usbipd.pid | ||
| 50 | .PP | ||
| 51 | |||
| 52 | \fB\-tPORT\fR, \fB\-\-tcp\-port PORT\fR | ||
| 53 | .IP | ||
| 54 | Listen on TCP/IP port PORT. | ||
| 55 | .PP | ||
| 56 | |||
| 57 | \fB\-h\fR, \fB\-\-help\fR | ||
| 58 | .IP | ||
| 59 | Print the program help message and exit. | ||
| 60 | .PP | ||
| 61 | |||
| 62 | .HP | ||
| 63 | \fB\-v\fR, \fB\-\-version\fR | ||
| 64 | .IP | ||
| 65 | Show version. | ||
| 66 | .PP | ||
| 67 | |||
| 68 | .SH LIMITATIONS | ||
| 69 | |||
| 70 | .B usbipd | ||
| 71 | offers no authentication or authorization for USB/IP. Any | ||
| 72 | USB/IP client can connect and use exported devices. | ||
| 73 | |||
| 74 | .SH EXAMPLES | ||
| 75 | |||
| 76 | server:# modprobe usbip | ||
| 77 | |||
| 78 | server:# usbipd -D | ||
| 79 | - Start usbip daemon. | ||
| 80 | |||
| 81 | server:# usbip list --local | ||
| 82 | - List driver assignments for usb devices. | ||
| 83 | |||
| 84 | server:# usbip bind --busid=1-2 | ||
| 85 | - Bind usbip-host.ko to the device of busid 1-2. | ||
| 86 | - A usb device 1-2 is now exportable to other hosts! | ||
| 87 | - Use 'usbip unbind --busid=1-2' when you want to shutdown exporting and use the device locally. | ||
| 88 | |||
| 89 | .SH "SEE ALSO" | ||
| 90 | \fBusbip\fP\fB(8)\fB\fP | ||
| 91 | |||
diff --git a/tools/usb/usbip/libsrc/Makefile.am b/tools/usb/usbip/libsrc/Makefile.am new file mode 100644 index 000000000000..7c8f8a4d54e4 --- /dev/null +++ b/tools/usb/usbip/libsrc/Makefile.am | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | libusbip_la_CPPFLAGS = -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"' | ||
| 2 | libusbip_la_CFLAGS = @EXTRA_CFLAGS@ | ||
| 3 | libusbip_la_LDFLAGS = -version-info @LIBUSBIP_VERSION@ | ||
| 4 | |||
| 5 | lib_LTLIBRARIES := libusbip.la | ||
| 6 | libusbip_la_SOURCES := names.c names.h usbip_host_driver.c usbip_host_driver.h \ | ||
| 7 | usbip_common.c usbip_common.h vhci_driver.c vhci_driver.h \ | ||
| 8 | sysfs_utils.c sysfs_utils.h | ||
diff --git a/tools/usb/usbip/libsrc/list.h b/tools/usb/usbip/libsrc/list.h new file mode 100644 index 000000000000..8d0c936e184f --- /dev/null +++ b/tools/usb/usbip/libsrc/list.h | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | #ifndef _LIST_H | ||
| 2 | #define _LIST_H | ||
| 3 | |||
| 4 | /* Stripped down implementation of linked list taken | ||
| 5 | * from the Linux Kernel. | ||
| 6 | */ | ||
| 7 | |||
| 8 | /* | ||
| 9 | * Simple doubly linked list implementation. | ||
| 10 | * | ||
| 11 | * Some of the internal functions ("__xxx") are useful when | ||
| 12 | * manipulating whole lists rather than single entries, as | ||
| 13 | * sometimes we already know the next/prev entries and we can | ||
| 14 | * generate better code by using them directly rather than | ||
| 15 | * using the generic single-entry routines. | ||
| 16 | */ | ||
| 17 | |||
| 18 | struct list_head { | ||
| 19 | struct list_head *next, *prev; | ||
| 20 | }; | ||
| 21 | |||
| 22 | #define LIST_HEAD_INIT(name) { &(name), &(name) } | ||
| 23 | |||
| 24 | #define LIST_HEAD(name) \ | ||
| 25 | struct list_head name = LIST_HEAD_INIT(name) | ||
| 26 | |||
| 27 | static inline void INIT_LIST_HEAD(struct list_head *list) | ||
| 28 | { | ||
| 29 | list->next = list; | ||
| 30 | list->prev = list; | ||
| 31 | } | ||
| 32 | |||
| 33 | /* | ||
| 34 | * Insert a new entry between two known consecutive entries. | ||
| 35 | * | ||
| 36 | * This is only for internal list manipulation where we know | ||
| 37 | * the prev/next entries already! | ||
| 38 | */ | ||
| 39 | static inline void __list_add(struct list_head *new, | ||
| 40 | struct list_head *prev, | ||
| 41 | struct list_head *next) | ||
| 42 | { | ||
| 43 | next->prev = new; | ||
| 44 | new->next = next; | ||
| 45 | new->prev = prev; | ||
| 46 | prev->next = new; | ||
| 47 | } | ||
| 48 | |||
| 49 | /** | ||
| 50 | * list_add - add a new entry | ||
| 51 | * @new: new entry to be added | ||
| 52 | * @head: list head to add it after | ||
| 53 | * | ||
| 54 | * Insert a new entry after the specified head. | ||
| 55 | * This is good for implementing stacks. | ||
| 56 | */ | ||
| 57 | static inline void list_add(struct list_head *new, struct list_head *head) | ||
| 58 | { | ||
| 59 | __list_add(new, head, head->next); | ||
| 60 | } | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Delete a list entry by making the prev/next entries | ||
| 64 | * point to each other. | ||
| 65 | * | ||
| 66 | * This is only for internal list manipulation where we know | ||
| 67 | * the prev/next entries already! | ||
| 68 | */ | ||
| 69 | static inline void __list_del(struct list_head * prev, struct list_head * next) | ||
| 70 | { | ||
| 71 | next->prev = prev; | ||
| 72 | prev->next = next; | ||
| 73 | } | ||
| 74 | |||
| 75 | #define POISON_POINTER_DELTA 0 | ||
| 76 | #define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA) | ||
| 77 | #define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA) | ||
| 78 | |||
| 79 | /** | ||
| 80 | * list_del - deletes entry from list. | ||
| 81 | * @entry: the element to delete from the list. | ||
| 82 | * Note: list_empty() on entry does not return true after this, the entry is | ||
| 83 | * in an undefined state. | ||
| 84 | */ | ||
| 85 | static inline void __list_del_entry(struct list_head *entry) | ||
| 86 | { | ||
| 87 | __list_del(entry->prev, entry->next); | ||
| 88 | } | ||
| 89 | |||
| 90 | static inline void list_del(struct list_head *entry) | ||
| 91 | { | ||
| 92 | __list_del(entry->prev, entry->next); | ||
| 93 | entry->next = LIST_POISON1; | ||
| 94 | entry->prev = LIST_POISON2; | ||
| 95 | } | ||
| 96 | |||
| 97 | /** | ||
| 98 | * list_entry - get the struct for this entry | ||
| 99 | * @ptr: the &struct list_head pointer. | ||
| 100 | * @type: the type of the struct this is embedded in. | ||
| 101 | * @member: the name of the list_struct within the struct. | ||
| 102 | */ | ||
| 103 | #define list_entry(ptr, type, member) \ | ||
| 104 | container_of(ptr, type, member) | ||
| 105 | /** | ||
| 106 | * list_for_each - iterate over a list | ||
| 107 | * @pos: the &struct list_head to use as a loop cursor. | ||
| 108 | * @head: the head for your list. | ||
| 109 | */ | ||
| 110 | #define list_for_each(pos, head) \ | ||
| 111 | for (pos = (head)->next; pos != (head); pos = pos->next) | ||
| 112 | |||
| 113 | /** | ||
| 114 | * list_for_each_safe - iterate over a list safe against removal of list entry | ||
| 115 | * @pos: the &struct list_head to use as a loop cursor. | ||
| 116 | * @n: another &struct list_head to use as temporary storage | ||
| 117 | * @head: the head for your list. | ||
| 118 | */ | ||
| 119 | #define list_for_each_safe(pos, n, head) \ | ||
| 120 | for (pos = (head)->next, n = pos->next; pos != (head); \ | ||
| 121 | pos = n, n = pos->next) | ||
| 122 | |||
| 123 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | ||
| 124 | |||
| 125 | /** | ||
| 126 | * container_of - cast a member of a structure out to the containing structure | ||
| 127 | * @ptr: the pointer to the member. | ||
| 128 | * @type: the type of the container struct this is embedded in. | ||
| 129 | * @member: the name of the member within the struct. | ||
| 130 | * | ||
| 131 | */ | ||
| 132 | #define container_of(ptr, type, member) ({ \ | ||
| 133 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ | ||
| 134 | (type *)( (char *)__mptr - offsetof(type,member) );}) | ||
| 135 | |||
| 136 | #endif | ||
diff --git a/tools/usb/usbip/libsrc/names.c b/tools/usb/usbip/libsrc/names.c new file mode 100644 index 000000000000..81ff8522405c --- /dev/null +++ b/tools/usb/usbip/libsrc/names.c | |||
| @@ -0,0 +1,504 @@ | |||
| 1 | /* | ||
| 2 | * names.c -- USB name database manipulation routines | ||
| 3 | * | ||
| 4 | * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | * | ||
| 20 | * | ||
| 21 | * | ||
| 22 | * | ||
| 23 | * | ||
| 24 | * Copyright (C) 2005 Takahiro Hirofuchi | ||
| 25 | * - names_deinit() is added. | ||
| 26 | * | ||
| 27 | */ | ||
| 28 | |||
| 29 | #include <sys/types.h> | ||
| 30 | #include <sys/stat.h> | ||
| 31 | #include <fcntl.h> | ||
| 32 | #include <dirent.h> | ||
| 33 | #include <string.h> | ||
| 34 | #include <errno.h> | ||
| 35 | #include <stdlib.h> | ||
| 36 | #include <unistd.h> | ||
| 37 | #include <stdio.h> | ||
| 38 | #include <ctype.h> | ||
| 39 | |||
| 40 | #include "names.h" | ||
| 41 | #include "usbip_common.h" | ||
| 42 | |||
| 43 | struct vendor { | ||
| 44 | struct vendor *next; | ||
| 45 | u_int16_t vendorid; | ||
| 46 | char name[1]; | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct product { | ||
| 50 | struct product *next; | ||
| 51 | u_int16_t vendorid, productid; | ||
| 52 | char name[1]; | ||
| 53 | }; | ||
| 54 | |||
| 55 | struct class { | ||
| 56 | struct class *next; | ||
| 57 | u_int8_t classid; | ||
| 58 | char name[1]; | ||
| 59 | }; | ||
| 60 | |||
| 61 | struct subclass { | ||
| 62 | struct subclass *next; | ||
| 63 | u_int8_t classid, subclassid; | ||
| 64 | char name[1]; | ||
| 65 | }; | ||
| 66 | |||
| 67 | struct protocol { | ||
| 68 | struct protocol *next; | ||
| 69 | u_int8_t classid, subclassid, protocolid; | ||
| 70 | char name[1]; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct genericstrtable { | ||
| 74 | struct genericstrtable *next; | ||
| 75 | unsigned int num; | ||
| 76 | char name[1]; | ||
| 77 | }; | ||
| 78 | |||
| 79 | |||
| 80 | #define HASH1 0x10 | ||
| 81 | #define HASH2 0x02 | ||
| 82 | #define HASHSZ 16 | ||
| 83 | |||
| 84 | static unsigned int hashnum(unsigned int num) | ||
| 85 | { | ||
| 86 | unsigned int mask1 = HASH1 << 27, mask2 = HASH2 << 27; | ||
| 87 | |||
| 88 | for (; mask1 >= HASH1; mask1 >>= 1, mask2 >>= 1) | ||
| 89 | if (num & mask1) | ||
| 90 | num ^= mask2; | ||
| 91 | return num & (HASHSZ-1); | ||
| 92 | } | ||
| 93 | |||
| 94 | |||
| 95 | static struct vendor *vendors[HASHSZ] = { NULL, }; | ||
| 96 | static struct product *products[HASHSZ] = { NULL, }; | ||
| 97 | static struct class *classes[HASHSZ] = { NULL, }; | ||
| 98 | static struct subclass *subclasses[HASHSZ] = { NULL, }; | ||
| 99 | static struct protocol *protocols[HASHSZ] = { NULL, }; | ||
| 100 | |||
| 101 | const char *names_vendor(u_int16_t vendorid) | ||
| 102 | { | ||
| 103 | struct vendor *v; | ||
| 104 | |||
| 105 | v = vendors[hashnum(vendorid)]; | ||
| 106 | for (; v; v = v->next) | ||
| 107 | if (v->vendorid == vendorid) | ||
| 108 | return v->name; | ||
| 109 | return NULL; | ||
| 110 | } | ||
| 111 | |||
| 112 | const char *names_product(u_int16_t vendorid, u_int16_t productid) | ||
| 113 | { | ||
| 114 | struct product *p; | ||
| 115 | |||
| 116 | p = products[hashnum((vendorid << 16) | productid)]; | ||
| 117 | for (; p; p = p->next) | ||
| 118 | if (p->vendorid == vendorid && p->productid == productid) | ||
| 119 | return p->name; | ||
| 120 | return NULL; | ||
| 121 | } | ||
| 122 | |||
| 123 | const char *names_class(u_int8_t classid) | ||
| 124 | { | ||
| 125 | struct class *c; | ||
| 126 | |||
| 127 | c = classes[hashnum(classid)]; | ||
| 128 | for (; c; c = c->next) | ||
| 129 | if (c->classid == classid) | ||
| 130 | return c->name; | ||
| 131 | return NULL; | ||
| 132 | } | ||
| 133 | |||
| 134 | const char *names_subclass(u_int8_t classid, u_int8_t subclassid) | ||
| 135 | { | ||
| 136 | struct subclass *s; | ||
| 137 | |||
| 138 | s = subclasses[hashnum((classid << 8) | subclassid)]; | ||
| 139 | for (; s; s = s->next) | ||
| 140 | if (s->classid == classid && s->subclassid == subclassid) | ||
| 141 | return s->name; | ||
| 142 | return NULL; | ||
| 143 | } | ||
| 144 | |||
| 145 | const char *names_protocol(u_int8_t classid, u_int8_t subclassid, | ||
| 146 | u_int8_t protocolid) | ||
| 147 | { | ||
| 148 | struct protocol *p; | ||
| 149 | |||
| 150 | p = protocols[hashnum((classid << 16) | (subclassid << 8) | ||
| 151 | | protocolid)]; | ||
| 152 | for (; p; p = p->next) | ||
| 153 | if (p->classid == classid && p->subclassid == subclassid && | ||
| 154 | p->protocolid == protocolid) | ||
| 155 | return p->name; | ||
| 156 | return NULL; | ||
| 157 | } | ||
| 158 | |||
| 159 | /* add a cleanup function by takahiro */ | ||
| 160 | struct pool { | ||
| 161 | struct pool *next; | ||
| 162 | void *mem; | ||
| 163 | }; | ||
| 164 | |||
| 165 | static struct pool *pool_head; | ||
| 166 | |||
| 167 | static void *my_malloc(size_t size) | ||
| 168 | { | ||
| 169 | struct pool *p; | ||
| 170 | |||
| 171 | p = calloc(1, sizeof(struct pool)); | ||
| 172 | if (!p) | ||
| 173 | return NULL; | ||
| 174 | |||
| 175 | p->mem = calloc(1, size); | ||
| 176 | if (!p->mem) { | ||
| 177 | free(p); | ||
| 178 | return NULL; | ||
| 179 | } | ||
| 180 | |||
| 181 | p->next = pool_head; | ||
| 182 | pool_head = p; | ||
| 183 | |||
| 184 | return p->mem; | ||
| 185 | } | ||
| 186 | |||
| 187 | void names_free(void) | ||
| 188 | { | ||
| 189 | struct pool *pool; | ||
| 190 | |||
| 191 | if (!pool_head) | ||
| 192 | return; | ||
| 193 | |||
| 194 | for (pool = pool_head; pool != NULL; ) { | ||
| 195 | struct pool *tmp; | ||
| 196 | |||
| 197 | if (pool->mem) | ||
| 198 | free(pool->mem); | ||
| 199 | |||
| 200 | tmp = pool; | ||
| 201 | pool = pool->next; | ||
| 202 | free(tmp); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | static int new_vendor(const char *name, u_int16_t vendorid) | ||
| 207 | { | ||
| 208 | struct vendor *v; | ||
| 209 | unsigned int h = hashnum(vendorid); | ||
| 210 | |||
| 211 | v = vendors[h]; | ||
| 212 | for (; v; v = v->next) | ||
| 213 | if (v->vendorid == vendorid) | ||
| 214 | return -1; | ||
| 215 | v = my_malloc(sizeof(struct vendor) + strlen(name)); | ||
| 216 | if (!v) | ||
| 217 | return -1; | ||
| 218 | strcpy(v->name, name); | ||
| 219 | v->vendorid = vendorid; | ||
| 220 | v->next = vendors[h]; | ||
| 221 | vendors[h] = v; | ||
| 222 | return 0; | ||
| 223 | } | ||
| 224 | |||
| 225 | static int new_product(const char *name, u_int16_t vendorid, | ||
| 226 | u_int16_t productid) | ||
| 227 | { | ||
| 228 | struct product *p; | ||
| 229 | unsigned int h = hashnum((vendorid << 16) | productid); | ||
| 230 | |||
| 231 | p = products[h]; | ||
| 232 | for (; p; p = p->next) | ||
| 233 | if (p->vendorid == vendorid && p->productid == productid) | ||
| 234 | return -1; | ||
| 235 | p = my_malloc(sizeof(struct product) + strlen(name)); | ||
| 236 | if (!p) | ||
| 237 | return -1; | ||
| 238 | strcpy(p->name, name); | ||
| 239 | p->vendorid = vendorid; | ||
| 240 | p->productid = productid; | ||
| 241 | p->next = products[h]; | ||
| 242 | products[h] = p; | ||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | static int new_class(const char *name, u_int8_t classid) | ||
| 247 | { | ||
| 248 | struct class *c; | ||
| 249 | unsigned int h = hashnum(classid); | ||
| 250 | |||
| 251 | c = classes[h]; | ||
| 252 | for (; c; c = c->next) | ||
| 253 | if (c->classid == classid) | ||
| 254 | return -1; | ||
| 255 | c = my_malloc(sizeof(struct class) + strlen(name)); | ||
| 256 | if (!c) | ||
| 257 | return -1; | ||
| 258 | strcpy(c->name, name); | ||
| 259 | c->classid = classid; | ||
| 260 | c->next = classes[h]; | ||
| 261 | classes[h] = c; | ||
| 262 | return 0; | ||
| 263 | } | ||
| 264 | |||
| 265 | static int new_subclass(const char *name, u_int8_t classid, u_int8_t subclassid) | ||
| 266 | { | ||
| 267 | struct subclass *s; | ||
| 268 | unsigned int h = hashnum((classid << 8) | subclassid); | ||
| 269 | |||
| 270 | s = subclasses[h]; | ||
| 271 | for (; s; s = s->next) | ||
| 272 | if (s->classid == classid && s->subclassid == subclassid) | ||
| 273 | return -1; | ||
| 274 | s = my_malloc(sizeof(struct subclass) + strlen(name)); | ||
| 275 | if (!s) | ||
| 276 | return -1; | ||
| 277 | strcpy(s->name, name); | ||
| 278 | s->classid = classid; | ||
| 279 | s->subclassid = subclassid; | ||
| 280 | s->next = subclasses[h]; | ||
| 281 | subclasses[h] = s; | ||
| 282 | return 0; | ||
| 283 | } | ||
| 284 | |||
| 285 | static int new_protocol(const char *name, u_int8_t classid, u_int8_t subclassid, | ||
| 286 | u_int8_t protocolid) | ||
| 287 | { | ||
| 288 | struct protocol *p; | ||
| 289 | unsigned int h = hashnum((classid << 16) | (subclassid << 8) | ||
| 290 | | protocolid); | ||
| 291 | |||
| 292 | p = protocols[h]; | ||
| 293 | for (; p; p = p->next) | ||
| 294 | if (p->classid == classid && p->subclassid == subclassid | ||
| 295 | && p->protocolid == protocolid) | ||
| 296 | return -1; | ||
| 297 | p = my_malloc(sizeof(struct protocol) + strlen(name)); | ||
| 298 | if (!p) | ||
| 299 | return -1; | ||
| 300 | strcpy(p->name, name); | ||
| 301 | p->classid = classid; | ||
| 302 | p->subclassid = subclassid; | ||
| 303 | p->protocolid = protocolid; | ||
| 304 | p->next = protocols[h]; | ||
| 305 | protocols[h] = p; | ||
| 306 | return 0; | ||
| 307 | } | ||
| 308 | |||
| 309 | static void parse(FILE *f) | ||
| 310 | { | ||
| 311 | char buf[512], *cp; | ||
| 312 | unsigned int linectr = 0; | ||
| 313 | int lastvendor = -1; | ||
| 314 | int lastclass = -1; | ||
| 315 | int lastsubclass = -1; | ||
| 316 | int lasthut = -1; | ||
| 317 | int lastlang = -1; | ||
| 318 | unsigned int u; | ||
| 319 | |||
| 320 | while (fgets(buf, sizeof(buf), f)) { | ||
| 321 | linectr++; | ||
| 322 | /* remove line ends */ | ||
| 323 | cp = strchr(buf, '\r'); | ||
| 324 | if (cp) | ||
| 325 | *cp = 0; | ||
| 326 | cp = strchr(buf, '\n'); | ||
| 327 | if (cp) | ||
| 328 | *cp = 0; | ||
| 329 | if (buf[0] == '#' || !buf[0]) | ||
| 330 | continue; | ||
| 331 | cp = buf; | ||
| 332 | if (buf[0] == 'P' && buf[1] == 'H' && buf[2] == 'Y' && | ||
| 333 | buf[3] == 'S' && buf[4] == 'D' && | ||
| 334 | buf[5] == 'E' && buf[6] == 'S' && /*isspace(buf[7])*/ | ||
| 335 | buf[7] == ' ') { | ||
| 336 | continue; | ||
| 337 | } | ||
| 338 | if (buf[0] == 'P' && buf[1] == 'H' && | ||
| 339 | buf[2] == 'Y' && /*isspace(buf[3])*/ buf[3] == ' ') { | ||
| 340 | continue; | ||
| 341 | } | ||
| 342 | if (buf[0] == 'B' && buf[1] == 'I' && buf[2] == 'A' && | ||
| 343 | buf[3] == 'S' && /*isspace(buf[4])*/ buf[4] == ' ') { | ||
| 344 | continue; | ||
| 345 | } | ||
| 346 | if (buf[0] == 'L' && /*isspace(buf[1])*/ buf[1] == ' ') { | ||
| 347 | lasthut = lastclass = lastvendor = lastsubclass = -1; | ||
| 348 | /* | ||
| 349 | * set 1 as pseudo-id to indicate that the parser is | ||
| 350 | * in a `L' section. | ||
| 351 | */ | ||
| 352 | lastlang = 1; | ||
| 353 | continue; | ||
| 354 | } | ||
| 355 | if (buf[0] == 'C' && /*isspace(buf[1])*/ buf[1] == ' ') { | ||
| 356 | /* class spec */ | ||
| 357 | cp = buf+2; | ||
| 358 | while (isspace(*cp)) | ||
| 359 | cp++; | ||
| 360 | if (!isxdigit(*cp)) { | ||
| 361 | err("Invalid class spec at line %u", linectr); | ||
| 362 | continue; | ||
| 363 | } | ||
| 364 | u = strtoul(cp, &cp, 16); | ||
| 365 | while (isspace(*cp)) | ||
| 366 | cp++; | ||
| 367 | if (!*cp) { | ||
| 368 | err("Invalid class spec at line %u", linectr); | ||
| 369 | continue; | ||
| 370 | } | ||
| 371 | if (new_class(cp, u)) | ||
| 372 | err("Duplicate class spec at line %u class %04x %s", | ||
| 373 | linectr, u, cp); | ||
| 374 | dbg("line %5u class %02x %s", linectr, u, cp); | ||
| 375 | lasthut = lastlang = lastvendor = lastsubclass = -1; | ||
| 376 | lastclass = u; | ||
| 377 | continue; | ||
| 378 | } | ||
| 379 | if (buf[0] == 'A' && buf[1] == 'T' && isspace(buf[2])) { | ||
| 380 | /* audio terminal type spec */ | ||
| 381 | continue; | ||
| 382 | } | ||
| 383 | if (buf[0] == 'H' && buf[1] == 'C' && buf[2] == 'C' | ||
| 384 | && isspace(buf[3])) { | ||
| 385 | /* HID Descriptor bCountryCode */ | ||
| 386 | continue; | ||
| 387 | } | ||
| 388 | if (isxdigit(*cp)) { | ||
| 389 | /* vendor */ | ||
| 390 | u = strtoul(cp, &cp, 16); | ||
| 391 | while (isspace(*cp)) | ||
| 392 | cp++; | ||
| 393 | if (!*cp) { | ||
| 394 | err("Invalid vendor spec at line %u", linectr); | ||
| 395 | continue; | ||
| 396 | } | ||
| 397 | if (new_vendor(cp, u)) | ||
| 398 | err("Duplicate vendor spec at line %u vendor %04x %s", | ||
| 399 | linectr, u, cp); | ||
| 400 | dbg("line %5u vendor %04x %s", linectr, u, cp); | ||
| 401 | lastvendor = u; | ||
| 402 | lasthut = lastlang = lastclass = lastsubclass = -1; | ||
| 403 | continue; | ||
| 404 | } | ||
| 405 | if (buf[0] == '\t' && isxdigit(buf[1])) { | ||
| 406 | /* product or subclass spec */ | ||
| 407 | u = strtoul(buf+1, &cp, 16); | ||
| 408 | while (isspace(*cp)) | ||
| 409 | cp++; | ||
| 410 | if (!*cp) { | ||
| 411 | err("Invalid product/subclass spec at line %u", | ||
| 412 | linectr); | ||
| 413 | continue; | ||
| 414 | } | ||
| 415 | if (lastvendor != -1) { | ||
| 416 | if (new_product(cp, lastvendor, u)) | ||
| 417 | err("Duplicate product spec at line %u product %04x:%04x %s", | ||
| 418 | linectr, lastvendor, u, cp); | ||
| 419 | dbg("line %5u product %04x:%04x %s", linectr, | ||
| 420 | lastvendor, u, cp); | ||
| 421 | continue; | ||
| 422 | } | ||
| 423 | if (lastclass != -1) { | ||
| 424 | if (new_subclass(cp, lastclass, u)) | ||
| 425 | err("Duplicate subclass spec at line %u class %02x:%02x %s", | ||
| 426 | linectr, lastclass, u, cp); | ||
| 427 | dbg("line %5u subclass %02x:%02x %s", linectr, | ||
| 428 | lastclass, u, cp); | ||
| 429 | lastsubclass = u; | ||
| 430 | continue; | ||
| 431 | } | ||
| 432 | if (lasthut != -1) { | ||
| 433 | /* do not store hut */ | ||
| 434 | continue; | ||
| 435 | } | ||
| 436 | if (lastlang != -1) { | ||
| 437 | /* do not store langid */ | ||
| 438 | continue; | ||
| 439 | } | ||
| 440 | err("Product/Subclass spec without prior Vendor/Class spec at line %u", | ||
| 441 | linectr); | ||
| 442 | continue; | ||
| 443 | } | ||
| 444 | if (buf[0] == '\t' && buf[1] == '\t' && isxdigit(buf[2])) { | ||
| 445 | /* protocol spec */ | ||
| 446 | u = strtoul(buf+2, &cp, 16); | ||
| 447 | while (isspace(*cp)) | ||
| 448 | cp++; | ||
| 449 | if (!*cp) { | ||
| 450 | err("Invalid protocol spec at line %u", | ||
| 451 | linectr); | ||
| 452 | continue; | ||
| 453 | } | ||
| 454 | if (lastclass != -1 && lastsubclass != -1) { | ||
| 455 | if (new_protocol(cp, lastclass, lastsubclass, | ||
| 456 | u)) | ||
| 457 | err("Duplicate protocol spec at line %u class %02x:%02x:%02x %s", | ||
| 458 | linectr, lastclass, lastsubclass, | ||
| 459 | u, cp); | ||
| 460 | dbg("line %5u protocol %02x:%02x:%02x %s", | ||
| 461 | linectr, lastclass, lastsubclass, u, cp); | ||
| 462 | continue; | ||
| 463 | } | ||
| 464 | err("Protocol spec without prior Class and Subclass spec at line %u", | ||
| 465 | linectr); | ||
| 466 | continue; | ||
| 467 | } | ||
| 468 | if (buf[0] == 'H' && buf[1] == 'I' && | ||
| 469 | buf[2] == 'D' && /*isspace(buf[3])*/ buf[3] == ' ') { | ||
| 470 | continue; | ||
| 471 | } | ||
| 472 | if (buf[0] == 'H' && buf[1] == 'U' && | ||
| 473 | buf[2] == 'T' && /*isspace(buf[3])*/ buf[3] == ' ') { | ||
| 474 | lastlang = lastclass = lastvendor = lastsubclass = -1; | ||
| 475 | /* | ||
| 476 | * set 1 as pseudo-id to indicate that the parser is | ||
| 477 | * in a `HUT' section. | ||
| 478 | */ | ||
| 479 | lasthut = 1; | ||
| 480 | continue; | ||
| 481 | } | ||
| 482 | if (buf[0] == 'R' && buf[1] == ' ') | ||
| 483 | continue; | ||
| 484 | |||
| 485 | if (buf[0] == 'V' && buf[1] == 'T') | ||
| 486 | continue; | ||
| 487 | |||
| 488 | err("Unknown line at line %u", linectr); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | |||
| 492 | |||
| 493 | int names_init(char *n) | ||
| 494 | { | ||
| 495 | FILE *f; | ||
| 496 | |||
| 497 | f = fopen(n, "r"); | ||
| 498 | if (!f) | ||
| 499 | return errno; | ||
| 500 | |||
| 501 | parse(f); | ||
| 502 | fclose(f); | ||
| 503 | return 0; | ||
| 504 | } | ||
diff --git a/tools/usb/usbip/libsrc/names.h b/tools/usb/usbip/libsrc/names.h new file mode 100644 index 000000000000..680926512de2 --- /dev/null +++ b/tools/usb/usbip/libsrc/names.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* | ||
| 2 | * names.h -- USB name database manipulation routines | ||
| 3 | * | ||
| 4 | * Copyright (C) 1999, 2000 Thomas Sailer (sailer@ife.ee.ethz.ch) | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 19 | * | ||
| 20 | * | ||
| 21 | * | ||
| 22 | * Copyright (C) 2005 Takahiro Hirofuchi | ||
| 23 | * - names_free() is added. | ||
| 24 | */ | ||
| 25 | |||
| 26 | #ifndef _NAMES_H | ||
| 27 | #define _NAMES_H | ||
| 28 | |||
| 29 | #include <sys/types.h> | ||
| 30 | |||
| 31 | /* used by usbip_common.c */ | ||
| 32 | extern const char *names_vendor(u_int16_t vendorid); | ||
| 33 | extern const char *names_product(u_int16_t vendorid, u_int16_t productid); | ||
| 34 | extern const char *names_class(u_int8_t classid); | ||
| 35 | extern const char *names_subclass(u_int8_t classid, u_int8_t subclassid); | ||
| 36 | extern const char *names_protocol(u_int8_t classid, u_int8_t subclassid, | ||
| 37 | u_int8_t protocolid); | ||
| 38 | extern int names_init(char *n); | ||
| 39 | extern void names_free(void); | ||
| 40 | |||
| 41 | #endif /* _NAMES_H */ | ||
diff --git a/tools/usb/usbip/libsrc/sysfs_utils.c b/tools/usb/usbip/libsrc/sysfs_utils.c new file mode 100644 index 000000000000..36ac88ece0b8 --- /dev/null +++ b/tools/usb/usbip/libsrc/sysfs_utils.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #include <sys/types.h> | ||
| 2 | #include <sys/stat.h> | ||
| 3 | #include <fcntl.h> | ||
| 4 | #include <errno.h> | ||
| 5 | |||
| 6 | #include "sysfs_utils.h" | ||
| 7 | #include "usbip_common.h" | ||
| 8 | |||
| 9 | int write_sysfs_attribute(const char *attr_path, const char *new_value, | ||
| 10 | size_t len) | ||
| 11 | { | ||
| 12 | int fd; | ||
| 13 | int length; | ||
| 14 | |||
| 15 | fd = open(attr_path, O_WRONLY); | ||
| 16 | if (fd < 0) { | ||
| 17 | dbg("error opening attribute %s", attr_path); | ||
| 18 | return -1; | ||
| 19 | } | ||
| 20 | |||
| 21 | length = write(fd, new_value, len); | ||
| 22 | if (length < 0) { | ||
| 23 | dbg("error writing to attribute %s", attr_path); | ||
| 24 | close(fd); | ||
| 25 | return -1; | ||
| 26 | } | ||
| 27 | |||
| 28 | close(fd); | ||
| 29 | |||
| 30 | return 0; | ||
| 31 | } | ||
diff --git a/tools/usb/usbip/libsrc/sysfs_utils.h b/tools/usb/usbip/libsrc/sysfs_utils.h new file mode 100644 index 000000000000..32ac1d105d18 --- /dev/null +++ b/tools/usb/usbip/libsrc/sysfs_utils.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | |||
| 2 | #ifndef __SYSFS_UTILS_H | ||
| 3 | #define __SYSFS_UTILS_H | ||
| 4 | |||
| 5 | int write_sysfs_attribute(const char *attr_path, const char *new_value, | ||
| 6 | size_t len); | ||
| 7 | |||
| 8 | #endif | ||
diff --git a/tools/usb/usbip/libsrc/usbip_common.c b/tools/usb/usbip/libsrc/usbip_common.c new file mode 100644 index 000000000000..ac73710473de --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_common.c | |||
| @@ -0,0 +1,285 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2007 Takahiro Hirofuchi | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include <libudev.h> | ||
| 6 | #include "usbip_common.h" | ||
| 7 | #include "names.h" | ||
| 8 | |||
| 9 | #undef PROGNAME | ||
| 10 | #define PROGNAME "libusbip" | ||
| 11 | |||
| 12 | int usbip_use_syslog; | ||
| 13 | int usbip_use_stderr; | ||
| 14 | int usbip_use_debug; | ||
| 15 | |||
| 16 | extern struct udev *udev_context; | ||
| 17 | |||
| 18 | struct speed_string { | ||
| 19 | int num; | ||
| 20 | char *speed; | ||
| 21 | char *desc; | ||
| 22 | }; | ||
| 23 | |||
| 24 | static const struct speed_string speed_strings[] = { | ||
| 25 | { USB_SPEED_UNKNOWN, "unknown", "Unknown Speed"}, | ||
| 26 | { USB_SPEED_LOW, "1.5", "Low Speed(1.5Mbps)" }, | ||
| 27 | { USB_SPEED_FULL, "12", "Full Speed(12Mbps)" }, | ||
| 28 | { USB_SPEED_HIGH, "480", "High Speed(480Mbps)" }, | ||
| 29 | { USB_SPEED_WIRELESS, "53.3-480", "Wireless"}, | ||
| 30 | { USB_SPEED_SUPER, "5000", "Super Speed(5000Mbps)" }, | ||
| 31 | { 0, NULL, NULL } | ||
| 32 | }; | ||
| 33 | |||
| 34 | struct portst_string { | ||
| 35 | int num; | ||
| 36 | char *desc; | ||
| 37 | }; | ||
| 38 | |||
| 39 | static struct portst_string portst_strings[] = { | ||
| 40 | { SDEV_ST_AVAILABLE, "Device Available" }, | ||
| 41 | { SDEV_ST_USED, "Device in Use" }, | ||
| 42 | { SDEV_ST_ERROR, "Device Error"}, | ||
| 43 | { VDEV_ST_NULL, "Port Available"}, | ||
| 44 | { VDEV_ST_NOTASSIGNED, "Port Initializing"}, | ||
| 45 | { VDEV_ST_USED, "Port in Use"}, | ||
| 46 | { VDEV_ST_ERROR, "Port Error"}, | ||
| 47 | { 0, NULL} | ||
| 48 | }; | ||
| 49 | |||
| 50 | const char *usbip_status_string(int32_t status) | ||
| 51 | { | ||
| 52 | for (int i = 0; portst_strings[i].desc != NULL; i++) | ||
| 53 | if (portst_strings[i].num == status) | ||
| 54 | return portst_strings[i].desc; | ||
| 55 | |||
| 56 | return "Unknown Status"; | ||
| 57 | } | ||
| 58 | |||
| 59 | const char *usbip_speed_string(int num) | ||
| 60 | { | ||
| 61 | for (int i = 0; speed_strings[i].speed != NULL; i++) | ||
| 62 | if (speed_strings[i].num == num) | ||
| 63 | return speed_strings[i].desc; | ||
| 64 | |||
| 65 | return "Unknown Speed"; | ||
| 66 | } | ||
| 67 | |||
| 68 | |||
| 69 | #define DBG_UDEV_INTEGER(name)\ | ||
| 70 | dbg("%-20s = %x", to_string(name), (int) udev->name) | ||
| 71 | |||
| 72 | #define DBG_UINF_INTEGER(name)\ | ||
| 73 | dbg("%-20s = %x", to_string(name), (int) uinf->name) | ||
| 74 | |||
| 75 | void dump_usb_interface(struct usbip_usb_interface *uinf) | ||
| 76 | { | ||
| 77 | char buff[100]; | ||
| 78 | |||
| 79 | usbip_names_get_class(buff, sizeof(buff), | ||
| 80 | uinf->bInterfaceClass, | ||
| 81 | uinf->bInterfaceSubClass, | ||
| 82 | uinf->bInterfaceProtocol); | ||
| 83 | dbg("%-20s = %s", "Interface(C/SC/P)", buff); | ||
| 84 | } | ||
| 85 | |||
| 86 | void dump_usb_device(struct usbip_usb_device *udev) | ||
| 87 | { | ||
| 88 | char buff[100]; | ||
| 89 | |||
| 90 | dbg("%-20s = %s", "path", udev->path); | ||
| 91 | dbg("%-20s = %s", "busid", udev->busid); | ||
| 92 | |||
| 93 | usbip_names_get_class(buff, sizeof(buff), | ||
| 94 | udev->bDeviceClass, | ||
| 95 | udev->bDeviceSubClass, | ||
| 96 | udev->bDeviceProtocol); | ||
| 97 | dbg("%-20s = %s", "Device(C/SC/P)", buff); | ||
| 98 | |||
| 99 | DBG_UDEV_INTEGER(bcdDevice); | ||
| 100 | |||
| 101 | usbip_names_get_product(buff, sizeof(buff), | ||
| 102 | udev->idVendor, | ||
| 103 | udev->idProduct); | ||
| 104 | dbg("%-20s = %s", "Vendor/Product", buff); | ||
| 105 | |||
| 106 | DBG_UDEV_INTEGER(bNumConfigurations); | ||
| 107 | DBG_UDEV_INTEGER(bNumInterfaces); | ||
| 108 | |||
| 109 | dbg("%-20s = %s", "speed", | ||
| 110 | usbip_speed_string(udev->speed)); | ||
| 111 | |||
| 112 | DBG_UDEV_INTEGER(busnum); | ||
| 113 | DBG_UDEV_INTEGER(devnum); | ||
| 114 | } | ||
| 115 | |||
| 116 | |||
| 117 | int read_attr_value(struct udev_device *dev, const char *name, | ||
| 118 | const char *format) | ||
| 119 | { | ||
| 120 | const char *attr; | ||
| 121 | int num = 0; | ||
| 122 | int ret; | ||
| 123 | |||
| 124 | attr = udev_device_get_sysattr_value(dev, name); | ||
| 125 | if (!attr) { | ||
| 126 | err("udev_device_get_sysattr_value failed"); | ||
| 127 | goto err; | ||
| 128 | } | ||
| 129 | |||
| 130 | /* The client chooses the device configuration | ||
| 131 | * when attaching it so right after being bound | ||
| 132 | * to usbip-host on the server the device will | ||
| 133 | * have no configuration. | ||
| 134 | * Therefore, attributes such as bConfigurationValue | ||
| 135 | * and bNumInterfaces will not exist and sscanf will | ||
| 136 | * fail. Check for these cases and don't treat them | ||
| 137 | * as errors. | ||
| 138 | */ | ||
| 139 | |||
| 140 | ret = sscanf(attr, format, &num); | ||
| 141 | if (ret < 1) { | ||
| 142 | if (strcmp(name, "bConfigurationValue") && | ||
| 143 | strcmp(name, "bNumInterfaces")) { | ||
| 144 | err("sscanf failed for attribute %s", name); | ||
| 145 | goto err; | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | err: | ||
| 150 | |||
| 151 | return num; | ||
| 152 | } | ||
| 153 | |||
| 154 | |||
| 155 | int read_attr_speed(struct udev_device *dev) | ||
| 156 | { | ||
| 157 | const char *speed; | ||
| 158 | |||
| 159 | speed = udev_device_get_sysattr_value(dev, "speed"); | ||
| 160 | if (!speed) { | ||
| 161 | err("udev_device_get_sysattr_value failed"); | ||
| 162 | goto err; | ||
| 163 | } | ||
| 164 | |||
| 165 | for (int i = 0; speed_strings[i].speed != NULL; i++) { | ||
| 166 | if (!strcmp(speed, speed_strings[i].speed)) | ||
| 167 | return speed_strings[i].num; | ||
| 168 | } | ||
| 169 | |||
| 170 | err: | ||
| 171 | |||
| 172 | return USB_SPEED_UNKNOWN; | ||
| 173 | } | ||
| 174 | |||
| 175 | #define READ_ATTR(object, type, dev, name, format) \ | ||
| 176 | do { \ | ||
| 177 | (object)->name = (type) read_attr_value(dev, to_string(name), \ | ||
| 178 | format); \ | ||
| 179 | } while (0) | ||
| 180 | |||
| 181 | |||
| 182 | int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev) | ||
| 183 | { | ||
| 184 | uint32_t busnum, devnum; | ||
| 185 | const char *path, *name; | ||
| 186 | |||
| 187 | READ_ATTR(udev, uint8_t, sdev, bDeviceClass, "%02x\n"); | ||
| 188 | READ_ATTR(udev, uint8_t, sdev, bDeviceSubClass, "%02x\n"); | ||
| 189 | READ_ATTR(udev, uint8_t, sdev, bDeviceProtocol, "%02x\n"); | ||
| 190 | |||
| 191 | READ_ATTR(udev, uint16_t, sdev, idVendor, "%04x\n"); | ||
| 192 | READ_ATTR(udev, uint16_t, sdev, idProduct, "%04x\n"); | ||
| 193 | READ_ATTR(udev, uint16_t, sdev, bcdDevice, "%04x\n"); | ||
| 194 | |||
| 195 | READ_ATTR(udev, uint8_t, sdev, bConfigurationValue, "%02x\n"); | ||
| 196 | READ_ATTR(udev, uint8_t, sdev, bNumConfigurations, "%02x\n"); | ||
| 197 | READ_ATTR(udev, uint8_t, sdev, bNumInterfaces, "%02x\n"); | ||
| 198 | |||
| 199 | READ_ATTR(udev, uint8_t, sdev, devnum, "%d\n"); | ||
| 200 | udev->speed = read_attr_speed(sdev); | ||
| 201 | |||
| 202 | path = udev_device_get_syspath(sdev); | ||
| 203 | name = udev_device_get_sysname(sdev); | ||
| 204 | |||
| 205 | strncpy(udev->path, path, SYSFS_PATH_MAX); | ||
| 206 | strncpy(udev->busid, name, SYSFS_BUS_ID_SIZE); | ||
| 207 | |||
| 208 | sscanf(name, "%u-%u", &busnum, &devnum); | ||
| 209 | udev->busnum = busnum; | ||
| 210 | |||
| 211 | return 0; | ||
| 212 | } | ||
| 213 | |||
| 214 | int read_usb_interface(struct usbip_usb_device *udev, int i, | ||
| 215 | struct usbip_usb_interface *uinf) | ||
| 216 | { | ||
| 217 | char busid[SYSFS_BUS_ID_SIZE]; | ||
| 218 | struct udev_device *sif; | ||
| 219 | |||
| 220 | sprintf(busid, "%s:%d.%d", udev->busid, udev->bConfigurationValue, i); | ||
| 221 | |||
| 222 | sif = udev_device_new_from_subsystem_sysname(udev_context, "usb", busid); | ||
| 223 | if (!sif) { | ||
| 224 | err("udev_device_new_from_subsystem_sysname %s failed", busid); | ||
| 225 | return -1; | ||
| 226 | } | ||
| 227 | |||
| 228 | READ_ATTR(uinf, uint8_t, sif, bInterfaceClass, "%02x\n"); | ||
| 229 | READ_ATTR(uinf, uint8_t, sif, bInterfaceSubClass, "%02x\n"); | ||
| 230 | READ_ATTR(uinf, uint8_t, sif, bInterfaceProtocol, "%02x\n"); | ||
| 231 | |||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | int usbip_names_init(char *f) | ||
| 236 | { | ||
| 237 | return names_init(f); | ||
| 238 | } | ||
| 239 | |||
| 240 | void usbip_names_free(void) | ||
| 241 | { | ||
| 242 | names_free(); | ||
| 243 | } | ||
| 244 | |||
| 245 | void usbip_names_get_product(char *buff, size_t size, uint16_t vendor, | ||
| 246 | uint16_t product) | ||
| 247 | { | ||
| 248 | const char *prod, *vend; | ||
| 249 | |||
| 250 | prod = names_product(vendor, product); | ||
| 251 | if (!prod) | ||
| 252 | prod = "unknown product"; | ||
| 253 | |||
| 254 | |||
| 255 | vend = names_vendor(vendor); | ||
| 256 | if (!vend) | ||
| 257 | vend = "unknown vendor"; | ||
| 258 | |||
| 259 | snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product); | ||
| 260 | } | ||
| 261 | |||
| 262 | void usbip_names_get_class(char *buff, size_t size, uint8_t class, | ||
| 263 | uint8_t subclass, uint8_t protocol) | ||
| 264 | { | ||
| 265 | const char *c, *s, *p; | ||
| 266 | |||
| 267 | if (class == 0 && subclass == 0 && protocol == 0) { | ||
| 268 | snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", class, subclass, protocol); | ||
| 269 | return; | ||
| 270 | } | ||
| 271 | |||
| 272 | p = names_protocol(class, subclass, protocol); | ||
| 273 | if (!p) | ||
| 274 | p = "unknown protocol"; | ||
| 275 | |||
| 276 | s = names_subclass(class, subclass); | ||
| 277 | if (!s) | ||
| 278 | s = "unknown subclass"; | ||
| 279 | |||
| 280 | c = names_class(class); | ||
| 281 | if (!c) | ||
| 282 | c = "unknown class"; | ||
| 283 | |||
| 284 | snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, class, subclass, protocol); | ||
| 285 | } | ||
diff --git a/tools/usb/usbip/libsrc/usbip_common.h b/tools/usb/usbip/libsrc/usbip_common.h new file mode 100644 index 000000000000..15fe792e1e96 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_common.h | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2007 Takahiro Hirofuchi | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef __USBIP_COMMON_H | ||
| 6 | #define __USBIP_COMMON_H | ||
| 7 | |||
| 8 | #include <libudev.h> | ||
| 9 | |||
| 10 | #include <stdint.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <string.h> | ||
| 14 | |||
| 15 | #include <syslog.h> | ||
| 16 | #include <unistd.h> | ||
| 17 | #include <linux/usb/ch9.h> | ||
| 18 | #include <linux/usbip.h> | ||
| 19 | |||
| 20 | #ifndef USBIDS_FILE | ||
| 21 | #define USBIDS_FILE "/usr/share/hwdata/usb.ids" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #ifndef VHCI_STATE_PATH | ||
| 25 | #define VHCI_STATE_PATH "/var/run/vhci_hcd" | ||
| 26 | #endif | ||
| 27 | |||
| 28 | /* kernel module names */ | ||
| 29 | #define USBIP_CORE_MOD_NAME "usbip-core" | ||
| 30 | #define USBIP_HOST_DRV_NAME "usbip-host" | ||
| 31 | #define USBIP_VHCI_DRV_NAME "vhci_hcd" | ||
| 32 | |||
| 33 | /* sysfs constants */ | ||
| 34 | #define SYSFS_MNT_PATH "/sys" | ||
| 35 | #define SYSFS_BUS_NAME "bus" | ||
| 36 | #define SYSFS_BUS_TYPE "usb" | ||
| 37 | #define SYSFS_DRIVERS_NAME "drivers" | ||
| 38 | |||
| 39 | #define SYSFS_PATH_MAX 256 | ||
| 40 | #define SYSFS_BUS_ID_SIZE 32 | ||
| 41 | |||
| 42 | extern int usbip_use_syslog; | ||
| 43 | extern int usbip_use_stderr; | ||
| 44 | extern int usbip_use_debug ; | ||
| 45 | |||
| 46 | #define PROGNAME "usbip" | ||
| 47 | |||
| 48 | #define pr_fmt(fmt) "%s: %s: " fmt "\n", PROGNAME | ||
| 49 | #define dbg_fmt(fmt) pr_fmt("%s:%d:[%s] " fmt), "debug", \ | ||
| 50 | __FILE__, __LINE__, __func__ | ||
| 51 | |||
| 52 | #define err(fmt, args...) \ | ||
| 53 | do { \ | ||
| 54 | if (usbip_use_syslog) { \ | ||
| 55 | syslog(LOG_ERR, pr_fmt(fmt), "error", ##args); \ | ||
| 56 | } \ | ||
| 57 | if (usbip_use_stderr) { \ | ||
| 58 | fprintf(stderr, pr_fmt(fmt), "error", ##args); \ | ||
| 59 | } \ | ||
| 60 | } while (0) | ||
| 61 | |||
| 62 | #define info(fmt, args...) \ | ||
| 63 | do { \ | ||
| 64 | if (usbip_use_syslog) { \ | ||
| 65 | syslog(LOG_INFO, pr_fmt(fmt), "info", ##args); \ | ||
| 66 | } \ | ||
| 67 | if (usbip_use_stderr) { \ | ||
| 68 | fprintf(stderr, pr_fmt(fmt), "info", ##args); \ | ||
| 69 | } \ | ||
| 70 | } while (0) | ||
| 71 | |||
| 72 | #define dbg(fmt, args...) \ | ||
| 73 | do { \ | ||
| 74 | if (usbip_use_debug) { \ | ||
| 75 | if (usbip_use_syslog) { \ | ||
| 76 | syslog(LOG_DEBUG, dbg_fmt(fmt), ##args); \ | ||
| 77 | } \ | ||
| 78 | if (usbip_use_stderr) { \ | ||
| 79 | fprintf(stderr, dbg_fmt(fmt), ##args); \ | ||
| 80 | } \ | ||
| 81 | } \ | ||
| 82 | } while (0) | ||
| 83 | |||
| 84 | #define BUG() \ | ||
| 85 | do { \ | ||
| 86 | err("sorry, it's a bug!"); \ | ||
| 87 | abort(); \ | ||
| 88 | } while (0) | ||
| 89 | |||
| 90 | struct usbip_usb_interface { | ||
| 91 | uint8_t bInterfaceClass; | ||
| 92 | uint8_t bInterfaceSubClass; | ||
| 93 | uint8_t bInterfaceProtocol; | ||
| 94 | uint8_t padding; /* alignment */ | ||
| 95 | } __attribute__((packed)); | ||
| 96 | |||
| 97 | struct usbip_usb_device { | ||
| 98 | char path[SYSFS_PATH_MAX]; | ||
| 99 | char busid[SYSFS_BUS_ID_SIZE]; | ||
| 100 | |||
| 101 | uint32_t busnum; | ||
| 102 | uint32_t devnum; | ||
| 103 | uint32_t speed; | ||
| 104 | |||
| 105 | uint16_t idVendor; | ||
| 106 | uint16_t idProduct; | ||
| 107 | uint16_t bcdDevice; | ||
| 108 | |||
| 109 | uint8_t bDeviceClass; | ||
| 110 | uint8_t bDeviceSubClass; | ||
| 111 | uint8_t bDeviceProtocol; | ||
| 112 | uint8_t bConfigurationValue; | ||
| 113 | uint8_t bNumConfigurations; | ||
| 114 | uint8_t bNumInterfaces; | ||
| 115 | } __attribute__((packed)); | ||
| 116 | |||
| 117 | #define to_string(s) #s | ||
| 118 | |||
| 119 | void dump_usb_interface(struct usbip_usb_interface *); | ||
| 120 | void dump_usb_device(struct usbip_usb_device *); | ||
| 121 | int read_usb_device(struct udev_device *sdev, struct usbip_usb_device *udev); | ||
| 122 | int read_attr_value(struct udev_device *dev, const char *name, | ||
| 123 | const char *format); | ||
| 124 | int read_usb_interface(struct usbip_usb_device *udev, int i, | ||
| 125 | struct usbip_usb_interface *uinf); | ||
| 126 | |||
| 127 | const char *usbip_speed_string(int num); | ||
| 128 | const char *usbip_status_string(int32_t status); | ||
| 129 | |||
| 130 | int usbip_names_init(char *); | ||
| 131 | void usbip_names_free(void); | ||
| 132 | void usbip_names_get_product(char *buff, size_t size, uint16_t vendor, | ||
| 133 | uint16_t product); | ||
| 134 | void usbip_names_get_class(char *buff, size_t size, uint8_t class, | ||
| 135 | uint8_t subclass, uint8_t protocol); | ||
| 136 | |||
| 137 | #endif /* __USBIP_COMMON_H */ | ||
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c new file mode 100644 index 000000000000..bef08d5c44e8 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_driver.c | |||
| @@ -0,0 +1,280 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <sys/types.h> | ||
| 20 | #include <sys/stat.h> | ||
| 21 | #include <fcntl.h> | ||
| 22 | |||
| 23 | #include <errno.h> | ||
| 24 | #include <unistd.h> | ||
| 25 | |||
| 26 | #include <libudev.h> | ||
| 27 | |||
| 28 | #include "usbip_common.h" | ||
| 29 | #include "usbip_host_driver.h" | ||
| 30 | #include "list.h" | ||
| 31 | #include "sysfs_utils.h" | ||
| 32 | |||
| 33 | #undef PROGNAME | ||
| 34 | #define PROGNAME "libusbip" | ||
| 35 | |||
| 36 | struct usbip_host_driver *host_driver; | ||
| 37 | struct udev *udev_context; | ||
| 38 | |||
| 39 | static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) | ||
| 40 | { | ||
| 41 | char status_attr_path[SYSFS_PATH_MAX]; | ||
| 42 | int fd; | ||
| 43 | int length; | ||
| 44 | char status; | ||
| 45 | int value = 0; | ||
| 46 | |||
| 47 | snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status", | ||
| 48 | udev->path); | ||
| 49 | |||
| 50 | fd = open(status_attr_path, O_RDONLY); | ||
| 51 | if (fd < 0) { | ||
| 52 | err("error opening attribute %s", status_attr_path); | ||
| 53 | return -1; | ||
| 54 | } | ||
| 55 | |||
| 56 | length = read(fd, &status, 1); | ||
| 57 | if (length < 0) { | ||
| 58 | err("error reading attribute %s", status_attr_path); | ||
| 59 | close(fd); | ||
| 60 | return -1; | ||
| 61 | } | ||
| 62 | |||
| 63 | value = atoi(&status); | ||
| 64 | |||
| 65 | return value; | ||
| 66 | } | ||
| 67 | |||
| 68 | static | ||
| 69 | struct usbip_exported_device *usbip_exported_device_new(const char *sdevpath) | ||
| 70 | { | ||
| 71 | struct usbip_exported_device *edev = NULL; | ||
| 72 | struct usbip_exported_device *edev_old; | ||
| 73 | size_t size; | ||
| 74 | int i; | ||
| 75 | |||
| 76 | edev = calloc(1, sizeof(struct usbip_exported_device)); | ||
| 77 | |||
| 78 | edev->sudev = udev_device_new_from_syspath(udev_context, sdevpath); | ||
| 79 | if (!edev->sudev) { | ||
| 80 | err("udev_device_new_from_syspath: %s", sdevpath); | ||
| 81 | goto err; | ||
| 82 | } | ||
| 83 | |||
| 84 | read_usb_device(edev->sudev, &edev->udev); | ||
| 85 | |||
| 86 | edev->status = read_attr_usbip_status(&edev->udev); | ||
| 87 | if (edev->status < 0) | ||
| 88 | goto err; | ||
| 89 | |||
| 90 | /* reallocate buffer to include usb interface data */ | ||
| 91 | size = sizeof(struct usbip_exported_device) + | ||
| 92 | edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface); | ||
| 93 | |||
| 94 | edev_old = edev; | ||
| 95 | edev = realloc(edev, size); | ||
| 96 | if (!edev) { | ||
| 97 | edev = edev_old; | ||
| 98 | dbg("realloc failed"); | ||
| 99 | goto err; | ||
| 100 | } | ||
| 101 | |||
| 102 | for (i = 0; i < edev->udev.bNumInterfaces; i++) | ||
| 103 | read_usb_interface(&edev->udev, i, &edev->uinf[i]); | ||
| 104 | |||
| 105 | return edev; | ||
| 106 | err: | ||
| 107 | if (edev->sudev) | ||
| 108 | udev_device_unref(edev->sudev); | ||
| 109 | if (edev) | ||
| 110 | free(edev); | ||
| 111 | |||
| 112 | return NULL; | ||
| 113 | } | ||
| 114 | |||
| 115 | static int refresh_exported_devices(void) | ||
| 116 | { | ||
| 117 | struct usbip_exported_device *edev; | ||
| 118 | struct udev_enumerate *enumerate; | ||
| 119 | struct udev_list_entry *devices, *dev_list_entry; | ||
| 120 | struct udev_device *dev; | ||
| 121 | const char *path; | ||
| 122 | const char *driver; | ||
| 123 | |||
| 124 | enumerate = udev_enumerate_new(udev_context); | ||
| 125 | udev_enumerate_add_match_subsystem(enumerate, "usb"); | ||
| 126 | udev_enumerate_scan_devices(enumerate); | ||
| 127 | |||
| 128 | devices = udev_enumerate_get_list_entry(enumerate); | ||
| 129 | |||
| 130 | udev_list_entry_foreach(dev_list_entry, devices) { | ||
| 131 | path = udev_list_entry_get_name(dev_list_entry); | ||
| 132 | dev = udev_device_new_from_syspath(udev_context, path); | ||
| 133 | if (dev == NULL) | ||
| 134 | continue; | ||
| 135 | |||
| 136 | /* Check whether device uses usbip-host driver. */ | ||
| 137 | driver = udev_device_get_driver(dev); | ||
| 138 | if (driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME)) { | ||
| 139 | edev = usbip_exported_device_new(path); | ||
| 140 | if (!edev) { | ||
| 141 | dbg("usbip_exported_device_new failed"); | ||
| 142 | continue; | ||
| 143 | } | ||
| 144 | |||
| 145 | list_add(&edev->node, &host_driver->edev_list); | ||
| 146 | host_driver->ndevs++; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | static void usbip_exported_device_destroy(void) | ||
| 154 | { | ||
| 155 | struct list_head *i, *tmp; | ||
| 156 | struct usbip_exported_device *edev; | ||
| 157 | |||
| 158 | list_for_each_safe(i, tmp, &host_driver->edev_list) { | ||
| 159 | edev = list_entry(i, struct usbip_exported_device, node); | ||
| 160 | list_del(i); | ||
| 161 | free(edev); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | int usbip_host_driver_open(void) | ||
| 166 | { | ||
| 167 | int rc; | ||
| 168 | |||
| 169 | udev_context = udev_new(); | ||
| 170 | if (!udev_context) { | ||
| 171 | err("udev_new failed"); | ||
| 172 | return -1; | ||
| 173 | } | ||
| 174 | |||
| 175 | host_driver = calloc(1, sizeof(*host_driver)); | ||
| 176 | |||
| 177 | host_driver->ndevs = 0; | ||
| 178 | INIT_LIST_HEAD(&host_driver->edev_list); | ||
| 179 | |||
| 180 | rc = refresh_exported_devices(); | ||
| 181 | if (rc < 0) | ||
| 182 | goto err_free_host_driver; | ||
| 183 | |||
| 184 | return 0; | ||
| 185 | |||
| 186 | err_free_host_driver: | ||
| 187 | free(host_driver); | ||
| 188 | host_driver = NULL; | ||
| 189 | |||
| 190 | udev_unref(udev_context); | ||
| 191 | |||
| 192 | return -1; | ||
| 193 | } | ||
| 194 | |||
| 195 | void usbip_host_driver_close(void) | ||
| 196 | { | ||
| 197 | if (!host_driver) | ||
| 198 | return; | ||
| 199 | |||
| 200 | usbip_exported_device_destroy(); | ||
| 201 | |||
| 202 | free(host_driver); | ||
| 203 | host_driver = NULL; | ||
| 204 | |||
| 205 | udev_unref(udev_context); | ||
| 206 | } | ||
| 207 | |||
| 208 | int usbip_host_refresh_device_list(void) | ||
| 209 | { | ||
| 210 | int rc; | ||
| 211 | |||
| 212 | usbip_exported_device_destroy(); | ||
| 213 | |||
| 214 | host_driver->ndevs = 0; | ||
| 215 | INIT_LIST_HEAD(&host_driver->edev_list); | ||
| 216 | |||
| 217 | rc = refresh_exported_devices(); | ||
| 218 | if (rc < 0) | ||
| 219 | return -1; | ||
| 220 | |||
| 221 | return 0; | ||
| 222 | } | ||
| 223 | |||
| 224 | int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd) | ||
| 225 | { | ||
| 226 | char attr_name[] = "usbip_sockfd"; | ||
| 227 | char sockfd_attr_path[SYSFS_PATH_MAX]; | ||
| 228 | char sockfd_buff[30]; | ||
| 229 | int ret; | ||
| 230 | |||
| 231 | if (edev->status != SDEV_ST_AVAILABLE) { | ||
| 232 | dbg("device not available: %s", edev->udev.busid); | ||
| 233 | switch (edev->status) { | ||
| 234 | case SDEV_ST_ERROR: | ||
| 235 | dbg("status SDEV_ST_ERROR"); | ||
| 236 | break; | ||
| 237 | case SDEV_ST_USED: | ||
| 238 | dbg("status SDEV_ST_USED"); | ||
| 239 | break; | ||
| 240 | default: | ||
| 241 | dbg("status unknown: 0x%x", edev->status); | ||
| 242 | } | ||
| 243 | return -1; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* only the first interface is true */ | ||
| 247 | snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", | ||
| 248 | edev->udev.path, attr_name); | ||
| 249 | |||
| 250 | snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); | ||
| 251 | |||
| 252 | ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff, | ||
| 253 | strlen(sockfd_buff)); | ||
| 254 | if (ret < 0) { | ||
| 255 | err("write_sysfs_attribute failed: sockfd %s to %s", | ||
| 256 | sockfd_buff, sockfd_attr_path); | ||
| 257 | return ret; | ||
| 258 | } | ||
| 259 | |||
| 260 | info("connect: %s", edev->udev.busid); | ||
| 261 | |||
| 262 | return ret; | ||
| 263 | } | ||
| 264 | |||
| 265 | struct usbip_exported_device *usbip_host_get_device(int num) | ||
| 266 | { | ||
| 267 | struct list_head *i; | ||
| 268 | struct usbip_exported_device *edev; | ||
| 269 | int cnt = 0; | ||
| 270 | |||
| 271 | list_for_each(i, &host_driver->edev_list) { | ||
| 272 | edev = list_entry(i, struct usbip_exported_device, node); | ||
| 273 | if (num == cnt) | ||
| 274 | return edev; | ||
| 275 | else | ||
| 276 | cnt++; | ||
| 277 | } | ||
| 278 | |||
| 279 | return NULL; | ||
| 280 | } | ||
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.h b/tools/usb/usbip/libsrc/usbip_host_driver.h new file mode 100644 index 000000000000..2a31f855c616 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_driver.h | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #ifndef __USBIP_HOST_DRIVER_H | ||
| 20 | #define __USBIP_HOST_DRIVER_H | ||
| 21 | |||
| 22 | #include <stdint.h> | ||
| 23 | #include "usbip_common.h" | ||
| 24 | #include "list.h" | ||
| 25 | |||
| 26 | struct usbip_host_driver { | ||
| 27 | int ndevs; | ||
| 28 | /* list of exported device */ | ||
| 29 | struct list_head edev_list; | ||
| 30 | }; | ||
| 31 | |||
| 32 | struct usbip_exported_device { | ||
| 33 | struct udev_device *sudev; | ||
| 34 | int32_t status; | ||
| 35 | struct usbip_usb_device udev; | ||
| 36 | struct list_head node; | ||
| 37 | struct usbip_usb_interface uinf[]; | ||
| 38 | }; | ||
| 39 | |||
| 40 | extern struct usbip_host_driver *host_driver; | ||
| 41 | |||
| 42 | int usbip_host_driver_open(void); | ||
| 43 | void usbip_host_driver_close(void); | ||
| 44 | |||
| 45 | int usbip_host_refresh_device_list(void); | ||
| 46 | int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd); | ||
| 47 | struct usbip_exported_device *usbip_host_get_device(int num); | ||
| 48 | |||
| 49 | #endif /* __USBIP_HOST_DRIVER_H */ | ||
diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c new file mode 100644 index 000000000000..ad9204773533 --- /dev/null +++ b/tools/usb/usbip/libsrc/vhci_driver.c | |||
| @@ -0,0 +1,411 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2007 Takahiro Hirofuchi | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include "usbip_common.h" | ||
| 6 | #include "vhci_driver.h" | ||
| 7 | #include <limits.h> | ||
| 8 | #include <netdb.h> | ||
| 9 | #include <libudev.h> | ||
| 10 | #include "sysfs_utils.h" | ||
| 11 | |||
| 12 | #undef PROGNAME | ||
| 13 | #define PROGNAME "libusbip" | ||
| 14 | |||
| 15 | struct usbip_vhci_driver *vhci_driver; | ||
| 16 | struct udev *udev_context; | ||
| 17 | |||
| 18 | static struct usbip_imported_device * | ||
| 19 | imported_device_init(struct usbip_imported_device *idev, char *busid) | ||
| 20 | { | ||
| 21 | struct udev_device *sudev; | ||
| 22 | |||
| 23 | sudev = udev_device_new_from_subsystem_sysname(udev_context, | ||
| 24 | "usb", busid); | ||
| 25 | if (!sudev) { | ||
| 26 | dbg("udev_device_new_from_subsystem_sysname failed: %s", busid); | ||
| 27 | goto err; | ||
| 28 | } | ||
| 29 | read_usb_device(sudev, &idev->udev); | ||
| 30 | udev_device_unref(sudev); | ||
| 31 | |||
| 32 | return idev; | ||
| 33 | |||
| 34 | err: | ||
| 35 | return NULL; | ||
| 36 | } | ||
| 37 | |||
| 38 | |||
| 39 | |||
| 40 | static int parse_status(const char *value) | ||
| 41 | { | ||
| 42 | int ret = 0; | ||
| 43 | char *c; | ||
| 44 | |||
| 45 | |||
| 46 | for (int i = 0; i < vhci_driver->nports; i++) | ||
| 47 | memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i])); | ||
| 48 | |||
| 49 | |||
| 50 | /* skip a header line */ | ||
| 51 | c = strchr(value, '\n'); | ||
| 52 | if (!c) | ||
| 53 | return -1; | ||
| 54 | c++; | ||
| 55 | |||
| 56 | while (*c != '\0') { | ||
| 57 | int port, status, speed, devid; | ||
| 58 | unsigned long socket; | ||
| 59 | char lbusid[SYSFS_BUS_ID_SIZE]; | ||
| 60 | |||
| 61 | ret = sscanf(c, "%d %d %d %x %lx %31s\n", | ||
| 62 | &port, &status, &speed, | ||
| 63 | &devid, &socket, lbusid); | ||
| 64 | |||
| 65 | if (ret < 5) { | ||
| 66 | dbg("sscanf failed: %d", ret); | ||
| 67 | BUG(); | ||
| 68 | } | ||
| 69 | |||
| 70 | dbg("port %d status %d speed %d devid %x", | ||
| 71 | port, status, speed, devid); | ||
| 72 | dbg("socket %lx lbusid %s", socket, lbusid); | ||
| 73 | |||
| 74 | |||
| 75 | /* if a device is connected, look at it */ | ||
| 76 | { | ||
| 77 | struct usbip_imported_device *idev = &vhci_driver->idev[port]; | ||
| 78 | |||
| 79 | idev->port = port; | ||
| 80 | idev->status = status; | ||
| 81 | |||
| 82 | idev->devid = devid; | ||
| 83 | |||
| 84 | idev->busnum = (devid >> 16); | ||
| 85 | idev->devnum = (devid & 0x0000ffff); | ||
| 86 | |||
| 87 | if (idev->status != VDEV_ST_NULL | ||
| 88 | && idev->status != VDEV_ST_NOTASSIGNED) { | ||
| 89 | idev = imported_device_init(idev, lbusid); | ||
| 90 | if (!idev) { | ||
| 91 | dbg("imported_device_init failed"); | ||
| 92 | return -1; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | |||
| 98 | /* go to the next line */ | ||
| 99 | c = strchr(c, '\n'); | ||
| 100 | if (!c) | ||
| 101 | break; | ||
| 102 | c++; | ||
| 103 | } | ||
| 104 | |||
| 105 | dbg("exit"); | ||
| 106 | |||
| 107 | return 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | static int refresh_imported_device_list(void) | ||
| 111 | { | ||
| 112 | const char *attr_status; | ||
| 113 | |||
| 114 | attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, | ||
| 115 | "status"); | ||
| 116 | if (!attr_status) { | ||
| 117 | err("udev_device_get_sysattr_value failed"); | ||
| 118 | return -1; | ||
| 119 | } | ||
| 120 | |||
| 121 | return parse_status(attr_status); | ||
| 122 | } | ||
| 123 | |||
| 124 | static int get_nports(void) | ||
| 125 | { | ||
| 126 | char *c; | ||
| 127 | int nports = 0; | ||
| 128 | const char *attr_status; | ||
| 129 | |||
| 130 | attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, | ||
| 131 | "status"); | ||
| 132 | if (!attr_status) { | ||
| 133 | err("udev_device_get_sysattr_value failed"); | ||
| 134 | return -1; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* skip a header line */ | ||
| 138 | c = strchr(attr_status, '\n'); | ||
| 139 | if (!c) | ||
| 140 | return 0; | ||
| 141 | c++; | ||
| 142 | |||
| 143 | while (*c != '\0') { | ||
| 144 | /* go to the next line */ | ||
| 145 | c = strchr(c, '\n'); | ||
| 146 | if (!c) | ||
| 147 | return nports; | ||
| 148 | c++; | ||
| 149 | nports += 1; | ||
| 150 | } | ||
| 151 | |||
| 152 | return nports; | ||
| 153 | } | ||
| 154 | |||
| 155 | /* | ||
| 156 | * Read the given port's record. | ||
| 157 | * | ||
| 158 | * To avoid buffer overflow we will read the entire line and | ||
| 159 | * validate each part's size. The initial buffer is padded by 4 to | ||
| 160 | * accommodate the 2 spaces, 1 newline and an additional character | ||
| 161 | * which is needed to properly validate the 3rd part without it being | ||
| 162 | * truncated to an acceptable length. | ||
| 163 | */ | ||
| 164 | static int read_record(int rhport, char *host, unsigned long host_len, | ||
| 165 | char *port, unsigned long port_len, char *busid) | ||
| 166 | { | ||
| 167 | int part; | ||
| 168 | FILE *file; | ||
| 169 | char path[PATH_MAX+1]; | ||
| 170 | char *buffer, *start, *end; | ||
| 171 | char delim[] = {' ', ' ', '\n'}; | ||
| 172 | int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE}; | ||
| 173 | size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4; | ||
| 174 | |||
| 175 | buffer = malloc(buffer_len); | ||
| 176 | if (!buffer) | ||
| 177 | return -1; | ||
| 178 | |||
| 179 | snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport); | ||
| 180 | |||
| 181 | file = fopen(path, "r"); | ||
| 182 | if (!file) { | ||
| 183 | err("fopen"); | ||
| 184 | free(buffer); | ||
| 185 | return -1; | ||
| 186 | } | ||
| 187 | |||
| 188 | if (fgets(buffer, buffer_len, file) == NULL) { | ||
| 189 | err("fgets"); | ||
| 190 | free(buffer); | ||
| 191 | fclose(file); | ||
| 192 | return -1; | ||
| 193 | } | ||
| 194 | fclose(file); | ||
| 195 | |||
| 196 | /* validate the length of each of the 3 parts */ | ||
| 197 | start = buffer; | ||
| 198 | for (part = 0; part < 3; part++) { | ||
| 199 | end = strchr(start, delim[part]); | ||
| 200 | if (end == NULL || (end - start) > max_len[part]) { | ||
| 201 | free(buffer); | ||
| 202 | return -1; | ||
| 203 | } | ||
| 204 | start = end + 1; | ||
| 205 | } | ||
| 206 | |||
| 207 | if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) { | ||
| 208 | err("sscanf"); | ||
| 209 | free(buffer); | ||
| 210 | return -1; | ||
| 211 | } | ||
| 212 | |||
| 213 | free(buffer); | ||
| 214 | |||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* ---------------------------------------------------------------------- */ | ||
| 219 | |||
| 220 | int usbip_vhci_driver_open(void) | ||
| 221 | { | ||
| 222 | udev_context = udev_new(); | ||
| 223 | if (!udev_context) { | ||
| 224 | err("udev_new failed"); | ||
| 225 | return -1; | ||
| 226 | } | ||
| 227 | |||
| 228 | vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver)); | ||
| 229 | |||
| 230 | /* will be freed in usbip_driver_close() */ | ||
| 231 | vhci_driver->hc_device = | ||
| 232 | udev_device_new_from_subsystem_sysname(udev_context, | ||
| 233 | USBIP_VHCI_BUS_TYPE, | ||
| 234 | USBIP_VHCI_DRV_NAME); | ||
| 235 | if (!vhci_driver->hc_device) { | ||
| 236 | err("udev_device_new_from_subsystem_sysname failed"); | ||
| 237 | goto err; | ||
| 238 | } | ||
| 239 | |||
| 240 | vhci_driver->nports = get_nports(); | ||
| 241 | |||
| 242 | dbg("available ports: %d", vhci_driver->nports); | ||
| 243 | |||
| 244 | if (refresh_imported_device_list()) | ||
| 245 | goto err; | ||
| 246 | |||
| 247 | return 0; | ||
| 248 | |||
| 249 | err: | ||
| 250 | udev_device_unref(vhci_driver->hc_device); | ||
| 251 | |||
| 252 | if (vhci_driver) | ||
| 253 | free(vhci_driver); | ||
| 254 | |||
| 255 | vhci_driver = NULL; | ||
| 256 | |||
| 257 | udev_unref(udev_context); | ||
| 258 | |||
| 259 | return -1; | ||
| 260 | } | ||
| 261 | |||
| 262 | |||
| 263 | void usbip_vhci_driver_close(void) | ||
| 264 | { | ||
| 265 | if (!vhci_driver) | ||
| 266 | return; | ||
| 267 | |||
| 268 | udev_device_unref(vhci_driver->hc_device); | ||
| 269 | |||
| 270 | free(vhci_driver); | ||
| 271 | |||
| 272 | vhci_driver = NULL; | ||
| 273 | |||
| 274 | udev_unref(udev_context); | ||
| 275 | } | ||
| 276 | |||
| 277 | |||
| 278 | int usbip_vhci_refresh_device_list(void) | ||
| 279 | { | ||
| 280 | |||
| 281 | if (refresh_imported_device_list()) | ||
| 282 | goto err; | ||
| 283 | |||
| 284 | return 0; | ||
| 285 | err: | ||
| 286 | dbg("failed to refresh device list"); | ||
| 287 | return -1; | ||
| 288 | } | ||
| 289 | |||
| 290 | |||
| 291 | int usbip_vhci_get_free_port(void) | ||
| 292 | { | ||
| 293 | for (int i = 0; i < vhci_driver->nports; i++) { | ||
| 294 | if (vhci_driver->idev[i].status == VDEV_ST_NULL) | ||
| 295 | return i; | ||
| 296 | } | ||
| 297 | |||
| 298 | return -1; | ||
| 299 | } | ||
| 300 | |||
| 301 | int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, | ||
| 302 | uint32_t speed) { | ||
| 303 | char buff[200]; /* what size should be ? */ | ||
| 304 | char attach_attr_path[SYSFS_PATH_MAX]; | ||
| 305 | char attr_attach[] = "attach"; | ||
| 306 | const char *path; | ||
| 307 | int ret; | ||
| 308 | |||
| 309 | snprintf(buff, sizeof(buff), "%u %d %u %u", | ||
| 310 | port, sockfd, devid, speed); | ||
| 311 | dbg("writing: %s", buff); | ||
| 312 | |||
| 313 | path = udev_device_get_syspath(vhci_driver->hc_device); | ||
| 314 | snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s", | ||
| 315 | path, attr_attach); | ||
| 316 | dbg("attach attribute path: %s", attach_attr_path); | ||
| 317 | |||
| 318 | ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff)); | ||
| 319 | if (ret < 0) { | ||
| 320 | dbg("write_sysfs_attribute failed"); | ||
| 321 | return -1; | ||
| 322 | } | ||
| 323 | |||
| 324 | dbg("attached port: %d", port); | ||
| 325 | |||
| 326 | return 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | static unsigned long get_devid(uint8_t busnum, uint8_t devnum) | ||
| 330 | { | ||
| 331 | return (busnum << 16) | devnum; | ||
| 332 | } | ||
| 333 | |||
| 334 | /* will be removed */ | ||
| 335 | int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum, | ||
| 336 | uint8_t devnum, uint32_t speed) | ||
| 337 | { | ||
| 338 | int devid = get_devid(busnum, devnum); | ||
| 339 | |||
| 340 | return usbip_vhci_attach_device2(port, sockfd, devid, speed); | ||
| 341 | } | ||
| 342 | |||
| 343 | int usbip_vhci_detach_device(uint8_t port) | ||
| 344 | { | ||
| 345 | char detach_attr_path[SYSFS_PATH_MAX]; | ||
| 346 | char attr_detach[] = "detach"; | ||
| 347 | char buff[200]; /* what size should be ? */ | ||
| 348 | const char *path; | ||
| 349 | int ret; | ||
| 350 | |||
| 351 | snprintf(buff, sizeof(buff), "%u", port); | ||
| 352 | dbg("writing: %s", buff); | ||
| 353 | |||
| 354 | path = udev_device_get_syspath(vhci_driver->hc_device); | ||
| 355 | snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s", | ||
| 356 | path, attr_detach); | ||
| 357 | dbg("detach attribute path: %s", detach_attr_path); | ||
| 358 | |||
| 359 | ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff)); | ||
| 360 | if (ret < 0) { | ||
| 361 | dbg("write_sysfs_attribute failed"); | ||
| 362 | return -1; | ||
| 363 | } | ||
| 364 | |||
| 365 | dbg("detached port: %d", port); | ||
| 366 | |||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | |||
| 370 | int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev) | ||
| 371 | { | ||
| 372 | char product_name[100]; | ||
| 373 | char host[NI_MAXHOST] = "unknown host"; | ||
| 374 | char serv[NI_MAXSERV] = "unknown port"; | ||
| 375 | char remote_busid[SYSFS_BUS_ID_SIZE]; | ||
| 376 | int ret; | ||
| 377 | int read_record_error = 0; | ||
| 378 | |||
| 379 | if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED) | ||
| 380 | return 0; | ||
| 381 | |||
| 382 | ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv), | ||
| 383 | remote_busid); | ||
| 384 | if (ret) { | ||
| 385 | err("read_record"); | ||
| 386 | read_record_error = 1; | ||
| 387 | } | ||
| 388 | |||
| 389 | printf("Port %02d: <%s> at %s\n", idev->port, | ||
| 390 | usbip_status_string(idev->status), | ||
| 391 | usbip_speed_string(idev->udev.speed)); | ||
| 392 | |||
| 393 | usbip_names_get_product(product_name, sizeof(product_name), | ||
| 394 | idev->udev.idVendor, idev->udev.idProduct); | ||
| 395 | |||
| 396 | printf(" %s\n", product_name); | ||
| 397 | |||
| 398 | if (!read_record_error) { | ||
| 399 | printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid, | ||
| 400 | host, serv, remote_busid); | ||
| 401 | printf("%10s -> remote bus/dev %03d/%03d\n", " ", | ||
| 402 | idev->busnum, idev->devnum); | ||
| 403 | } else { | ||
| 404 | printf("%10s -> unknown host, remote port and remote busid\n", | ||
| 405 | idev->udev.busid); | ||
| 406 | printf("%10s -> remote bus/dev %03d/%03d\n", " ", | ||
| 407 | idev->busnum, idev->devnum); | ||
| 408 | } | ||
| 409 | |||
| 410 | return 0; | ||
| 411 | } | ||
diff --git a/tools/usb/usbip/libsrc/vhci_driver.h b/tools/usb/usbip/libsrc/vhci_driver.h new file mode 100644 index 000000000000..fa2316cf2cac --- /dev/null +++ b/tools/usb/usbip/libsrc/vhci_driver.h | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2007 Takahiro Hirofuchi | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef __VHCI_DRIVER_H | ||
| 6 | #define __VHCI_DRIVER_H | ||
| 7 | |||
| 8 | #include <libudev.h> | ||
| 9 | #include <stdint.h> | ||
| 10 | |||
| 11 | #include "usbip_common.h" | ||
| 12 | |||
| 13 | #define USBIP_VHCI_BUS_TYPE "platform" | ||
| 14 | #define MAXNPORT 128 | ||
| 15 | |||
| 16 | struct usbip_imported_device { | ||
| 17 | uint8_t port; | ||
| 18 | uint32_t status; | ||
| 19 | |||
| 20 | uint32_t devid; | ||
| 21 | |||
| 22 | uint8_t busnum; | ||
| 23 | uint8_t devnum; | ||
| 24 | |||
| 25 | /* usbip_class_device list */ | ||
| 26 | struct usbip_usb_device udev; | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct usbip_vhci_driver { | ||
| 30 | |||
| 31 | /* /sys/devices/platform/vhci_hcd */ | ||
| 32 | struct udev_device *hc_device; | ||
| 33 | |||
| 34 | int nports; | ||
| 35 | struct usbip_imported_device idev[MAXNPORT]; | ||
| 36 | }; | ||
| 37 | |||
| 38 | |||
| 39 | extern struct usbip_vhci_driver *vhci_driver; | ||
| 40 | |||
| 41 | int usbip_vhci_driver_open(void); | ||
| 42 | void usbip_vhci_driver_close(void); | ||
| 43 | |||
| 44 | int usbip_vhci_refresh_device_list(void); | ||
| 45 | |||
| 46 | |||
| 47 | int usbip_vhci_get_free_port(void); | ||
| 48 | int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, | ||
| 49 | uint32_t speed); | ||
| 50 | |||
| 51 | /* will be removed */ | ||
| 52 | int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum, | ||
| 53 | uint8_t devnum, uint32_t speed); | ||
| 54 | |||
| 55 | int usbip_vhci_detach_device(uint8_t port); | ||
| 56 | |||
| 57 | int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev); | ||
| 58 | |||
| 59 | #endif /* __VHCI_DRIVER_H */ | ||
diff --git a/tools/usb/usbip/src/Makefile.am b/tools/usb/usbip/src/Makefile.am new file mode 100644 index 000000000000..e81a4ebadeff --- /dev/null +++ b/tools/usb/usbip/src/Makefile.am | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | AM_CPPFLAGS = -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"' | ||
| 2 | AM_CFLAGS = @EXTRA_CFLAGS@ | ||
| 3 | LDADD = $(top_builddir)/libsrc/libusbip.la | ||
| 4 | |||
| 5 | sbin_PROGRAMS := usbip usbipd | ||
| 6 | |||
| 7 | usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \ | ||
| 8 | usbip_attach.c usbip_detach.c usbip_list.c \ | ||
| 9 | usbip_bind.c usbip_unbind.c usbip_port.c | ||
| 10 | |||
| 11 | usbipd_SOURCES := usbip_network.h usbipd.c usbip_network.c | ||
diff --git a/tools/usb/usbip/src/usbip.c b/tools/usb/usbip/src/usbip.c new file mode 100644 index 000000000000..d7599d943529 --- /dev/null +++ b/tools/usb/usbip/src/usbip.c | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | /* | ||
| 2 | * command structure borrowed from udev | ||
| 3 | * (git://git.kernel.org/pub/scm/linux/hotplug/udev.git) | ||
| 4 | * | ||
| 5 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 6 | * 2005-2007 Takahiro Hirofuchi | ||
| 7 | * | ||
| 8 | * This program is free software: you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation, either version 2 of the License, or | ||
| 11 | * (at your option) any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 20 | */ | ||
| 21 | |||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | |||
| 25 | #include <getopt.h> | ||
| 26 | #include <syslog.h> | ||
| 27 | |||
| 28 | #include "usbip_common.h" | ||
| 29 | #include "usbip_network.h" | ||
| 30 | #include "usbip.h" | ||
| 31 | |||
| 32 | static int usbip_help(int argc, char *argv[]); | ||
| 33 | static int usbip_version(int argc, char *argv[]); | ||
| 34 | |||
| 35 | static const char usbip_version_string[] = PACKAGE_STRING; | ||
| 36 | |||
| 37 | static const char usbip_usage_string[] = | ||
| 38 | "usbip [--debug] [--log] [--tcp-port PORT] [version]\n" | ||
| 39 | " [help] <command> <args>\n"; | ||
| 40 | |||
| 41 | static void usbip_usage(void) | ||
| 42 | { | ||
| 43 | printf("usage: %s", usbip_usage_string); | ||
| 44 | } | ||
| 45 | |||
| 46 | struct command { | ||
| 47 | const char *name; | ||
| 48 | int (*fn)(int argc, char *argv[]); | ||
| 49 | const char *help; | ||
| 50 | void (*usage)(void); | ||
| 51 | }; | ||
| 52 | |||
| 53 | static const struct command cmds[] = { | ||
| 54 | { | ||
| 55 | .name = "help", | ||
| 56 | .fn = usbip_help, | ||
| 57 | .help = NULL, | ||
| 58 | .usage = NULL | ||
| 59 | }, | ||
| 60 | { | ||
| 61 | .name = "version", | ||
| 62 | .fn = usbip_version, | ||
| 63 | .help = NULL, | ||
| 64 | .usage = NULL | ||
| 65 | }, | ||
| 66 | { | ||
| 67 | .name = "attach", | ||
| 68 | .fn = usbip_attach, | ||
| 69 | .help = "Attach a remote USB device", | ||
| 70 | .usage = usbip_attach_usage | ||
| 71 | }, | ||
| 72 | { | ||
| 73 | .name = "detach", | ||
| 74 | .fn = usbip_detach, | ||
| 75 | .help = "Detach a remote USB device", | ||
| 76 | .usage = usbip_detach_usage | ||
| 77 | }, | ||
| 78 | { | ||
| 79 | .name = "list", | ||
| 80 | .fn = usbip_list, | ||
| 81 | .help = "List exportable or local USB devices", | ||
| 82 | .usage = usbip_list_usage | ||
| 83 | }, | ||
| 84 | { | ||
| 85 | .name = "bind", | ||
| 86 | .fn = usbip_bind, | ||
| 87 | .help = "Bind device to " USBIP_HOST_DRV_NAME ".ko", | ||
| 88 | .usage = usbip_bind_usage | ||
| 89 | }, | ||
| 90 | { | ||
| 91 | .name = "unbind", | ||
| 92 | .fn = usbip_unbind, | ||
| 93 | .help = "Unbind device from " USBIP_HOST_DRV_NAME ".ko", | ||
| 94 | .usage = usbip_unbind_usage | ||
| 95 | }, | ||
| 96 | { | ||
| 97 | .name = "port", | ||
| 98 | .fn = usbip_port_show, | ||
| 99 | .help = "Show imported USB devices", | ||
| 100 | .usage = NULL | ||
| 101 | }, | ||
| 102 | { NULL, NULL, NULL, NULL } | ||
| 103 | }; | ||
| 104 | |||
| 105 | static int usbip_help(int argc, char *argv[]) | ||
| 106 | { | ||
| 107 | const struct command *cmd; | ||
| 108 | int i; | ||
| 109 | int ret = 0; | ||
| 110 | |||
| 111 | if (argc > 1 && argv++) { | ||
| 112 | for (i = 0; cmds[i].name != NULL; i++) | ||
| 113 | if (!strcmp(cmds[i].name, argv[0]) && cmds[i].usage) { | ||
| 114 | cmds[i].usage(); | ||
| 115 | goto done; | ||
| 116 | } | ||
| 117 | ret = -1; | ||
| 118 | } | ||
| 119 | |||
| 120 | usbip_usage(); | ||
| 121 | printf("\n"); | ||
| 122 | for (cmd = cmds; cmd->name != NULL; cmd++) | ||
| 123 | if (cmd->help != NULL) | ||
| 124 | printf(" %-10s %s\n", cmd->name, cmd->help); | ||
| 125 | printf("\n"); | ||
| 126 | done: | ||
| 127 | return ret; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int usbip_version(int argc, char *argv[]) | ||
| 131 | { | ||
| 132 | (void) argc; | ||
| 133 | (void) argv; | ||
| 134 | |||
| 135 | printf(PROGNAME " (%s)\n", usbip_version_string); | ||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int run_command(const struct command *cmd, int argc, char *argv[]) | ||
| 140 | { | ||
| 141 | dbg("running command: `%s'", cmd->name); | ||
| 142 | return cmd->fn(argc, argv); | ||
| 143 | } | ||
| 144 | |||
| 145 | int main(int argc, char *argv[]) | ||
| 146 | { | ||
| 147 | static const struct option opts[] = { | ||
| 148 | { "debug", no_argument, NULL, 'd' }, | ||
| 149 | { "log", no_argument, NULL, 'l' }, | ||
| 150 | { "tcp-port", required_argument, NULL, 't' }, | ||
| 151 | { NULL, 0, NULL, 0 } | ||
| 152 | }; | ||
| 153 | |||
| 154 | char *cmd; | ||
| 155 | int opt; | ||
| 156 | int i, rc = -1; | ||
| 157 | |||
| 158 | usbip_use_stderr = 1; | ||
| 159 | opterr = 0; | ||
| 160 | for (;;) { | ||
| 161 | opt = getopt_long(argc, argv, "+dlt:", opts, NULL); | ||
| 162 | |||
| 163 | if (opt == -1) | ||
| 164 | break; | ||
| 165 | |||
| 166 | switch (opt) { | ||
| 167 | case 'd': | ||
| 168 | usbip_use_debug = 1; | ||
| 169 | break; | ||
| 170 | case 'l': | ||
| 171 | usbip_use_syslog = 1; | ||
| 172 | openlog("", LOG_PID, LOG_USER); | ||
| 173 | break; | ||
| 174 | case 't': | ||
| 175 | usbip_setup_port_number(optarg); | ||
| 176 | break; | ||
| 177 | case '?': | ||
| 178 | printf("usbip: invalid option\n"); | ||
| 179 | default: | ||
| 180 | usbip_usage(); | ||
| 181 | goto out; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | cmd = argv[optind]; | ||
| 186 | if (cmd) { | ||
| 187 | for (i = 0; cmds[i].name != NULL; i++) | ||
| 188 | if (!strcmp(cmds[i].name, cmd)) { | ||
| 189 | argc -= optind; | ||
| 190 | argv += optind; | ||
| 191 | optind = 0; | ||
| 192 | rc = run_command(&cmds[i], argc, argv); | ||
| 193 | goto out; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | /* invalid command */ | ||
| 198 | usbip_help(0, NULL); | ||
| 199 | out: | ||
| 200 | return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE); | ||
| 201 | } | ||
diff --git a/tools/usb/usbip/src/usbip.h b/tools/usb/usbip/src/usbip.h new file mode 100644 index 000000000000..84fe66a9d8ad --- /dev/null +++ b/tools/usb/usbip/src/usbip.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #ifndef __USBIP_H | ||
| 20 | #define __USBIP_H | ||
| 21 | |||
| 22 | #ifdef HAVE_CONFIG_H | ||
| 23 | #include "../config.h" | ||
| 24 | #endif | ||
| 25 | |||
| 26 | /* usbip commands */ | ||
| 27 | int usbip_attach(int argc, char *argv[]); | ||
| 28 | int usbip_detach(int argc, char *argv[]); | ||
| 29 | int usbip_list(int argc, char *argv[]); | ||
| 30 | int usbip_bind(int argc, char *argv[]); | ||
| 31 | int usbip_unbind(int argc, char *argv[]); | ||
| 32 | int usbip_port_show(int argc, char *argv[]); | ||
| 33 | |||
| 34 | void usbip_attach_usage(void); | ||
| 35 | void usbip_detach_usage(void); | ||
| 36 | void usbip_list_usage(void); | ||
| 37 | void usbip_bind_usage(void); | ||
| 38 | void usbip_unbind_usage(void); | ||
| 39 | |||
| 40 | #endif /* __USBIP_H */ | ||
diff --git a/tools/usb/usbip/src/usbip_attach.c b/tools/usb/usbip/src/usbip_attach.c new file mode 100644 index 000000000000..d58a14dfc094 --- /dev/null +++ b/tools/usb/usbip/src/usbip_attach.c | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <sys/stat.h> | ||
| 20 | |||
| 21 | #include <limits.h> | ||
| 22 | #include <stdint.h> | ||
| 23 | #include <stdio.h> | ||
| 24 | #include <string.h> | ||
| 25 | |||
| 26 | #include <fcntl.h> | ||
| 27 | #include <getopt.h> | ||
| 28 | #include <unistd.h> | ||
| 29 | #include <errno.h> | ||
| 30 | |||
| 31 | #include "vhci_driver.h" | ||
| 32 | #include "usbip_common.h" | ||
| 33 | #include "usbip_network.h" | ||
| 34 | #include "usbip.h" | ||
| 35 | |||
| 36 | static const char usbip_attach_usage_string[] = | ||
| 37 | "usbip attach <args>\n" | ||
| 38 | " -r, --remote=<host> The machine with exported USB devices\n" | ||
| 39 | " -b, --busid=<busid> Busid of the device on <host>\n"; | ||
| 40 | |||
| 41 | void usbip_attach_usage(void) | ||
| 42 | { | ||
| 43 | printf("usage: %s", usbip_attach_usage_string); | ||
| 44 | } | ||
| 45 | |||
| 46 | #define MAX_BUFF 100 | ||
| 47 | static int record_connection(char *host, char *port, char *busid, int rhport) | ||
| 48 | { | ||
| 49 | int fd; | ||
| 50 | char path[PATH_MAX+1]; | ||
| 51 | char buff[MAX_BUFF+1]; | ||
| 52 | int ret; | ||
| 53 | |||
| 54 | ret = mkdir(VHCI_STATE_PATH, 0700); | ||
| 55 | if (ret < 0) { | ||
| 56 | /* if VHCI_STATE_PATH exists, then it better be a directory */ | ||
| 57 | if (errno == EEXIST) { | ||
| 58 | struct stat s; | ||
| 59 | |||
| 60 | ret = stat(VHCI_STATE_PATH, &s); | ||
| 61 | if (ret < 0) | ||
| 62 | return -1; | ||
| 63 | if (!(s.st_mode & S_IFDIR)) | ||
| 64 | return -1; | ||
| 65 | } else | ||
| 66 | return -1; | ||
| 67 | } | ||
| 68 | |||
| 69 | snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport); | ||
| 70 | |||
| 71 | fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU); | ||
| 72 | if (fd < 0) | ||
| 73 | return -1; | ||
| 74 | |||
| 75 | snprintf(buff, MAX_BUFF, "%s %s %s\n", | ||
| 76 | host, port, busid); | ||
| 77 | |||
| 78 | ret = write(fd, buff, strlen(buff)); | ||
| 79 | if (ret != (ssize_t) strlen(buff)) { | ||
| 80 | close(fd); | ||
| 81 | return -1; | ||
| 82 | } | ||
| 83 | |||
| 84 | close(fd); | ||
| 85 | |||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | static int import_device(int sockfd, struct usbip_usb_device *udev) | ||
| 90 | { | ||
| 91 | int rc; | ||
| 92 | int port; | ||
| 93 | |||
| 94 | rc = usbip_vhci_driver_open(); | ||
| 95 | if (rc < 0) { | ||
| 96 | err("open vhci_driver"); | ||
| 97 | return -1; | ||
| 98 | } | ||
| 99 | |||
| 100 | port = usbip_vhci_get_free_port(); | ||
| 101 | if (port < 0) { | ||
| 102 | err("no free port"); | ||
| 103 | usbip_vhci_driver_close(); | ||
| 104 | return -1; | ||
| 105 | } | ||
| 106 | |||
| 107 | rc = usbip_vhci_attach_device(port, sockfd, udev->busnum, | ||
| 108 | udev->devnum, udev->speed); | ||
| 109 | if (rc < 0) { | ||
| 110 | err("import device"); | ||
| 111 | usbip_vhci_driver_close(); | ||
| 112 | return -1; | ||
| 113 | } | ||
| 114 | |||
| 115 | usbip_vhci_driver_close(); | ||
| 116 | |||
| 117 | return port; | ||
| 118 | } | ||
| 119 | |||
| 120 | static int query_import_device(int sockfd, char *busid) | ||
| 121 | { | ||
| 122 | int rc; | ||
| 123 | struct op_import_request request; | ||
| 124 | struct op_import_reply reply; | ||
| 125 | uint16_t code = OP_REP_IMPORT; | ||
| 126 | |||
| 127 | memset(&request, 0, sizeof(request)); | ||
| 128 | memset(&reply, 0, sizeof(reply)); | ||
| 129 | |||
| 130 | /* send a request */ | ||
| 131 | rc = usbip_net_send_op_common(sockfd, OP_REQ_IMPORT, 0); | ||
| 132 | if (rc < 0) { | ||
| 133 | err("send op_common"); | ||
| 134 | return -1; | ||
| 135 | } | ||
| 136 | |||
| 137 | strncpy(request.busid, busid, SYSFS_BUS_ID_SIZE-1); | ||
| 138 | |||
| 139 | PACK_OP_IMPORT_REQUEST(0, &request); | ||
| 140 | |||
| 141 | rc = usbip_net_send(sockfd, (void *) &request, sizeof(request)); | ||
| 142 | if (rc < 0) { | ||
| 143 | err("send op_import_request"); | ||
| 144 | return -1; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* receive a reply */ | ||
| 148 | rc = usbip_net_recv_op_common(sockfd, &code); | ||
| 149 | if (rc < 0) { | ||
| 150 | err("recv op_common"); | ||
| 151 | return -1; | ||
| 152 | } | ||
| 153 | |||
| 154 | rc = usbip_net_recv(sockfd, (void *) &reply, sizeof(reply)); | ||
| 155 | if (rc < 0) { | ||
| 156 | err("recv op_import_reply"); | ||
| 157 | return -1; | ||
| 158 | } | ||
| 159 | |||
| 160 | PACK_OP_IMPORT_REPLY(0, &reply); | ||
| 161 | |||
| 162 | /* check the reply */ | ||
| 163 | if (strncmp(reply.udev.busid, busid, SYSFS_BUS_ID_SIZE)) { | ||
| 164 | err("recv different busid %s", reply.udev.busid); | ||
| 165 | return -1; | ||
| 166 | } | ||
| 167 | |||
| 168 | /* import a device */ | ||
| 169 | return import_device(sockfd, &reply.udev); | ||
| 170 | } | ||
| 171 | |||
| 172 | static int attach_device(char *host, char *busid) | ||
| 173 | { | ||
| 174 | int sockfd; | ||
| 175 | int rc; | ||
| 176 | int rhport; | ||
| 177 | |||
| 178 | sockfd = usbip_net_tcp_connect(host, usbip_port_string); | ||
| 179 | if (sockfd < 0) { | ||
| 180 | err("tcp connect"); | ||
| 181 | return -1; | ||
| 182 | } | ||
| 183 | |||
| 184 | rhport = query_import_device(sockfd, busid); | ||
| 185 | if (rhport < 0) { | ||
| 186 | err("query"); | ||
| 187 | return -1; | ||
| 188 | } | ||
| 189 | |||
| 190 | close(sockfd); | ||
| 191 | |||
| 192 | rc = record_connection(host, usbip_port_string, busid, rhport); | ||
| 193 | if (rc < 0) { | ||
| 194 | err("record connection"); | ||
| 195 | return -1; | ||
| 196 | } | ||
| 197 | |||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | int usbip_attach(int argc, char *argv[]) | ||
| 202 | { | ||
| 203 | static const struct option opts[] = { | ||
| 204 | { "remote", required_argument, NULL, 'r' }, | ||
| 205 | { "busid", required_argument, NULL, 'b' }, | ||
| 206 | { NULL, 0, NULL, 0 } | ||
| 207 | }; | ||
| 208 | char *host = NULL; | ||
| 209 | char *busid = NULL; | ||
| 210 | int opt; | ||
| 211 | int ret = -1; | ||
| 212 | |||
| 213 | for (;;) { | ||
| 214 | opt = getopt_long(argc, argv, "r:b:", opts, NULL); | ||
| 215 | |||
| 216 | if (opt == -1) | ||
| 217 | break; | ||
| 218 | |||
| 219 | switch (opt) { | ||
| 220 | case 'r': | ||
| 221 | host = optarg; | ||
| 222 | break; | ||
| 223 | case 'b': | ||
| 224 | busid = optarg; | ||
| 225 | break; | ||
| 226 | default: | ||
| 227 | goto err_out; | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | if (!host || !busid) | ||
| 232 | goto err_out; | ||
| 233 | |||
| 234 | ret = attach_device(host, busid); | ||
| 235 | goto out; | ||
| 236 | |||
| 237 | err_out: | ||
| 238 | usbip_attach_usage(); | ||
| 239 | out: | ||
| 240 | return ret; | ||
| 241 | } | ||
diff --git a/tools/usb/usbip/src/usbip_bind.c b/tools/usb/usbip/src/usbip_bind.c new file mode 100644 index 000000000000..fa46141ae68b --- /dev/null +++ b/tools/usb/usbip/src/usbip_bind.c | |||
| @@ -0,0 +1,214 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <libudev.h> | ||
| 20 | |||
| 21 | #include <errno.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | |||
| 26 | #include <getopt.h> | ||
| 27 | |||
| 28 | #include "usbip_common.h" | ||
| 29 | #include "utils.h" | ||
| 30 | #include "usbip.h" | ||
| 31 | #include "sysfs_utils.h" | ||
| 32 | |||
| 33 | enum unbind_status { | ||
| 34 | UNBIND_ST_OK, | ||
| 35 | UNBIND_ST_USBIP_HOST, | ||
| 36 | UNBIND_ST_FAILED | ||
| 37 | }; | ||
| 38 | |||
| 39 | static const char usbip_bind_usage_string[] = | ||
| 40 | "usbip bind <args>\n" | ||
| 41 | " -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device " | ||
| 42 | "on <busid>\n"; | ||
| 43 | |||
| 44 | void usbip_bind_usage(void) | ||
| 45 | { | ||
| 46 | printf("usage: %s", usbip_bind_usage_string); | ||
| 47 | } | ||
| 48 | |||
| 49 | /* call at unbound state */ | ||
| 50 | static int bind_usbip(char *busid) | ||
| 51 | { | ||
| 52 | char attr_name[] = "bind"; | ||
| 53 | char bind_attr_path[SYSFS_PATH_MAX]; | ||
| 54 | int rc = -1; | ||
| 55 | |||
| 56 | snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s", | ||
| 57 | SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE, | ||
| 58 | SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, attr_name); | ||
| 59 | |||
| 60 | rc = write_sysfs_attribute(bind_attr_path, busid, strlen(busid)); | ||
| 61 | if (rc < 0) { | ||
| 62 | err("error binding device %s to driver: %s", busid, | ||
| 63 | strerror(errno)); | ||
| 64 | return -1; | ||
| 65 | } | ||
| 66 | |||
| 67 | return 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* buggy driver may cause dead lock */ | ||
| 71 | static int unbind_other(char *busid) | ||
| 72 | { | ||
| 73 | enum unbind_status status = UNBIND_ST_OK; | ||
| 74 | |||
| 75 | char attr_name[] = "unbind"; | ||
| 76 | char unbind_attr_path[SYSFS_PATH_MAX]; | ||
| 77 | int rc = -1; | ||
| 78 | |||
| 79 | struct udev *udev; | ||
| 80 | struct udev_device *dev; | ||
| 81 | const char *driver; | ||
| 82 | const char *bDevClass; | ||
| 83 | |||
| 84 | /* Create libudev context. */ | ||
| 85 | udev = udev_new(); | ||
| 86 | |||
| 87 | /* Get the device. */ | ||
| 88 | dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid); | ||
| 89 | if (!dev) { | ||
| 90 | dbg("unable to find device with bus ID %s", busid); | ||
| 91 | goto err_close_busid_dev; | ||
| 92 | } | ||
| 93 | |||
| 94 | /* Check what kind of device it is. */ | ||
| 95 | bDevClass = udev_device_get_sysattr_value(dev, "bDeviceClass"); | ||
| 96 | if (!bDevClass) { | ||
| 97 | dbg("unable to get bDevClass device attribute"); | ||
| 98 | goto err_close_busid_dev; | ||
| 99 | } | ||
| 100 | |||
| 101 | if (!strncmp(bDevClass, "09", strlen(bDevClass))) { | ||
| 102 | dbg("skip unbinding of hub"); | ||
| 103 | goto err_close_busid_dev; | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Get the device driver. */ | ||
| 107 | driver = udev_device_get_driver(dev); | ||
| 108 | if (!driver) { | ||
| 109 | /* No driver bound to this device. */ | ||
| 110 | goto out; | ||
| 111 | } | ||
| 112 | |||
| 113 | if (!strncmp(USBIP_HOST_DRV_NAME, driver, | ||
| 114 | strlen(USBIP_HOST_DRV_NAME))) { | ||
| 115 | /* Already bound to usbip-host. */ | ||
| 116 | status = UNBIND_ST_USBIP_HOST; | ||
| 117 | goto out; | ||
| 118 | } | ||
| 119 | |||
| 120 | /* Unbind device from driver. */ | ||
| 121 | snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s", | ||
| 122 | SYSFS_MNT_PATH, SYSFS_BUS_NAME, SYSFS_BUS_TYPE, | ||
| 123 | SYSFS_DRIVERS_NAME, driver, attr_name); | ||
| 124 | |||
| 125 | rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid)); | ||
| 126 | if (rc < 0) { | ||
| 127 | err("error unbinding device %s from driver", busid); | ||
| 128 | goto err_close_busid_dev; | ||
| 129 | } | ||
| 130 | |||
| 131 | goto out; | ||
| 132 | |||
| 133 | err_close_busid_dev: | ||
| 134 | status = UNBIND_ST_FAILED; | ||
| 135 | out: | ||
| 136 | udev_device_unref(dev); | ||
| 137 | udev_unref(udev); | ||
| 138 | |||
| 139 | return status; | ||
| 140 | } | ||
| 141 | |||
| 142 | static int bind_device(char *busid) | ||
| 143 | { | ||
| 144 | int rc; | ||
| 145 | struct udev *udev; | ||
| 146 | struct udev_device *dev; | ||
| 147 | |||
| 148 | /* Check whether the device with this bus ID exists. */ | ||
| 149 | udev = udev_new(); | ||
| 150 | dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid); | ||
| 151 | if (!dev) { | ||
| 152 | err("device with the specified bus ID does not exist"); | ||
| 153 | return -1; | ||
| 154 | } | ||
| 155 | udev_unref(udev); | ||
| 156 | |||
| 157 | rc = unbind_other(busid); | ||
| 158 | if (rc == UNBIND_ST_FAILED) { | ||
| 159 | err("could not unbind driver from device on busid %s", busid); | ||
| 160 | return -1; | ||
| 161 | } else if (rc == UNBIND_ST_USBIP_HOST) { | ||
| 162 | err("device on busid %s is already bound to %s", busid, | ||
| 163 | USBIP_HOST_DRV_NAME); | ||
| 164 | return -1; | ||
| 165 | } | ||
| 166 | |||
| 167 | rc = modify_match_busid(busid, 1); | ||
| 168 | if (rc < 0) { | ||
| 169 | err("unable to bind device on %s", busid); | ||
| 170 | return -1; | ||
| 171 | } | ||
| 172 | |||
| 173 | rc = bind_usbip(busid); | ||
| 174 | if (rc < 0) { | ||
| 175 | err("could not bind device to %s", USBIP_HOST_DRV_NAME); | ||
| 176 | modify_match_busid(busid, 0); | ||
| 177 | return -1; | ||
| 178 | } | ||
| 179 | |||
| 180 | info("bind device on busid %s: complete", busid); | ||
| 181 | |||
| 182 | return 0; | ||
| 183 | } | ||
| 184 | |||
| 185 | int usbip_bind(int argc, char *argv[]) | ||
| 186 | { | ||
| 187 | static const struct option opts[] = { | ||
| 188 | { "busid", required_argument, NULL, 'b' }, | ||
| 189 | { NULL, 0, NULL, 0 } | ||
| 190 | }; | ||
| 191 | |||
| 192 | int opt; | ||
| 193 | int ret = -1; | ||
| 194 | |||
| 195 | for (;;) { | ||
| 196 | opt = getopt_long(argc, argv, "b:", opts, NULL); | ||
| 197 | |||
| 198 | if (opt == -1) | ||
| 199 | break; | ||
| 200 | |||
| 201 | switch (opt) { | ||
| 202 | case 'b': | ||
| 203 | ret = bind_device(optarg); | ||
| 204 | goto out; | ||
| 205 | default: | ||
| 206 | goto err_out; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | err_out: | ||
| 211 | usbip_bind_usage(); | ||
| 212 | out: | ||
| 213 | return ret; | ||
| 214 | } | ||
diff --git a/tools/usb/usbip/src/usbip_detach.c b/tools/usb/usbip/src/usbip_detach.c new file mode 100644 index 000000000000..05c6d15856eb --- /dev/null +++ b/tools/usb/usbip/src/usbip_detach.c | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <ctype.h> | ||
| 20 | #include <limits.h> | ||
| 21 | #include <stdint.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #include <string.h> | ||
| 25 | |||
| 26 | #include <getopt.h> | ||
| 27 | #include <unistd.h> | ||
| 28 | |||
| 29 | #include "vhci_driver.h" | ||
| 30 | #include "usbip_common.h" | ||
| 31 | #include "usbip_network.h" | ||
| 32 | #include "usbip.h" | ||
| 33 | |||
| 34 | static const char usbip_detach_usage_string[] = | ||
| 35 | "usbip detach <args>\n" | ||
| 36 | " -p, --port=<port> " USBIP_VHCI_DRV_NAME | ||
| 37 | " port the device is on\n"; | ||
| 38 | |||
| 39 | void usbip_detach_usage(void) | ||
| 40 | { | ||
| 41 | printf("usage: %s", usbip_detach_usage_string); | ||
| 42 | } | ||
| 43 | |||
| 44 | static int detach_port(char *port) | ||
| 45 | { | ||
| 46 | int ret; | ||
| 47 | uint8_t portnum; | ||
| 48 | char path[PATH_MAX+1]; | ||
| 49 | |||
| 50 | for (unsigned int i = 0; i < strlen(port); i++) | ||
| 51 | if (!isdigit(port[i])) { | ||
| 52 | err("invalid port %s", port); | ||
| 53 | return -1; | ||
| 54 | } | ||
| 55 | |||
| 56 | /* check max port */ | ||
| 57 | |||
| 58 | portnum = atoi(port); | ||
| 59 | |||
| 60 | /* remove the port state file */ | ||
| 61 | |||
| 62 | snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", portnum); | ||
| 63 | |||
| 64 | remove(path); | ||
| 65 | rmdir(VHCI_STATE_PATH); | ||
| 66 | |||
| 67 | ret = usbip_vhci_driver_open(); | ||
| 68 | if (ret < 0) { | ||
| 69 | err("open vhci_driver"); | ||
| 70 | return -1; | ||
| 71 | } | ||
| 72 | |||
| 73 | ret = usbip_vhci_detach_device(portnum); | ||
| 74 | if (ret < 0) | ||
| 75 | return -1; | ||
| 76 | |||
| 77 | usbip_vhci_driver_close(); | ||
| 78 | |||
| 79 | return ret; | ||
| 80 | } | ||
| 81 | |||
| 82 | int usbip_detach(int argc, char *argv[]) | ||
| 83 | { | ||
| 84 | static const struct option opts[] = { | ||
| 85 | { "port", required_argument, NULL, 'p' }, | ||
| 86 | { NULL, 0, NULL, 0 } | ||
| 87 | }; | ||
| 88 | int opt; | ||
| 89 | int ret = -1; | ||
| 90 | |||
| 91 | for (;;) { | ||
| 92 | opt = getopt_long(argc, argv, "p:", opts, NULL); | ||
| 93 | |||
| 94 | if (opt == -1) | ||
| 95 | break; | ||
| 96 | |||
| 97 | switch (opt) { | ||
| 98 | case 'p': | ||
| 99 | ret = detach_port(optarg); | ||
| 100 | goto out; | ||
| 101 | default: | ||
| 102 | goto err_out; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | err_out: | ||
| 107 | usbip_detach_usage(); | ||
| 108 | out: | ||
| 109 | return ret; | ||
| 110 | } | ||
diff --git a/tools/usb/usbip/src/usbip_list.c b/tools/usb/usbip/src/usbip_list.c new file mode 100644 index 000000000000..d5ce34a410e7 --- /dev/null +++ b/tools/usb/usbip/src/usbip_list.c | |||
| @@ -0,0 +1,283 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <sys/types.h> | ||
| 20 | #include <libudev.h> | ||
| 21 | |||
| 22 | #include <errno.h> | ||
| 23 | #include <stdbool.h> | ||
| 24 | #include <stdint.h> | ||
| 25 | #include <stdio.h> | ||
| 26 | #include <stdlib.h> | ||
| 27 | #include <string.h> | ||
| 28 | |||
| 29 | #include <getopt.h> | ||
| 30 | #include <netdb.h> | ||
| 31 | #include <unistd.h> | ||
| 32 | |||
| 33 | #include "usbip_common.h" | ||
| 34 | #include "usbip_network.h" | ||
| 35 | #include "usbip.h" | ||
| 36 | |||
| 37 | static const char usbip_list_usage_string[] = | ||
| 38 | "usbip list [-p|--parsable] <args>\n" | ||
| 39 | " -p, --parsable Parsable list format\n" | ||
| 40 | " -r, --remote=<host> List the exportable USB devices on <host>\n" | ||
| 41 | " -l, --local List the local USB devices\n"; | ||
| 42 | |||
| 43 | void usbip_list_usage(void) | ||
| 44 | { | ||
| 45 | printf("usage: %s", usbip_list_usage_string); | ||
| 46 | } | ||
| 47 | |||
| 48 | static int get_exported_devices(char *host, int sockfd) | ||
| 49 | { | ||
| 50 | char product_name[100]; | ||
| 51 | char class_name[100]; | ||
| 52 | struct op_devlist_reply reply; | ||
| 53 | uint16_t code = OP_REP_DEVLIST; | ||
| 54 | struct usbip_usb_device udev; | ||
| 55 | struct usbip_usb_interface uintf; | ||
| 56 | unsigned int i; | ||
| 57 | int rc, j; | ||
| 58 | |||
| 59 | rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0); | ||
| 60 | if (rc < 0) { | ||
| 61 | dbg("usbip_net_send_op_common failed"); | ||
| 62 | return -1; | ||
| 63 | } | ||
| 64 | |||
| 65 | rc = usbip_net_recv_op_common(sockfd, &code); | ||
| 66 | if (rc < 0) { | ||
| 67 | dbg("usbip_net_recv_op_common failed"); | ||
| 68 | return -1; | ||
| 69 | } | ||
| 70 | |||
| 71 | memset(&reply, 0, sizeof(reply)); | ||
| 72 | rc = usbip_net_recv(sockfd, &reply, sizeof(reply)); | ||
| 73 | if (rc < 0) { | ||
| 74 | dbg("usbip_net_recv_op_devlist failed"); | ||
| 75 | return -1; | ||
| 76 | } | ||
| 77 | PACK_OP_DEVLIST_REPLY(0, &reply); | ||
| 78 | dbg("exportable devices: %d\n", reply.ndev); | ||
| 79 | |||
| 80 | if (reply.ndev == 0) { | ||
| 81 | info("no exportable devices found on %s", host); | ||
| 82 | return 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | printf("Exportable USB devices\n"); | ||
| 86 | printf("======================\n"); | ||
| 87 | printf(" - %s\n", host); | ||
| 88 | |||
| 89 | for (i = 0; i < reply.ndev; i++) { | ||
| 90 | memset(&udev, 0, sizeof(udev)); | ||
| 91 | rc = usbip_net_recv(sockfd, &udev, sizeof(udev)); | ||
| 92 | if (rc < 0) { | ||
| 93 | dbg("usbip_net_recv failed: usbip_usb_device[%d]", i); | ||
| 94 | return -1; | ||
| 95 | } | ||
| 96 | usbip_net_pack_usb_device(0, &udev); | ||
| 97 | |||
| 98 | usbip_names_get_product(product_name, sizeof(product_name), | ||
| 99 | udev.idVendor, udev.idProduct); | ||
| 100 | usbip_names_get_class(class_name, sizeof(class_name), | ||
| 101 | udev.bDeviceClass, udev.bDeviceSubClass, | ||
| 102 | udev.bDeviceProtocol); | ||
| 103 | printf("%11s: %s\n", udev.busid, product_name); | ||
| 104 | printf("%11s: %s\n", "", udev.path); | ||
| 105 | printf("%11s: %s\n", "", class_name); | ||
| 106 | |||
| 107 | for (j = 0; j < udev.bNumInterfaces; j++) { | ||
| 108 | rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf)); | ||
| 109 | if (rc < 0) { | ||
| 110 | err("usbip_net_recv failed: usbip_usb_intf[%d]", | ||
| 111 | j); | ||
| 112 | |||
| 113 | return -1; | ||
| 114 | } | ||
| 115 | usbip_net_pack_usb_interface(0, &uintf); | ||
| 116 | |||
| 117 | usbip_names_get_class(class_name, sizeof(class_name), | ||
| 118 | uintf.bInterfaceClass, | ||
| 119 | uintf.bInterfaceSubClass, | ||
| 120 | uintf.bInterfaceProtocol); | ||
| 121 | printf("%11s: %2d - %s\n", "", j, class_name); | ||
| 122 | } | ||
| 123 | |||
| 124 | printf("\n"); | ||
| 125 | } | ||
| 126 | |||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int list_exported_devices(char *host) | ||
| 131 | { | ||
| 132 | int rc; | ||
| 133 | int sockfd; | ||
| 134 | |||
| 135 | sockfd = usbip_net_tcp_connect(host, usbip_port_string); | ||
| 136 | if (sockfd < 0) { | ||
| 137 | err("could not connect to %s:%s: %s", host, | ||
| 138 | usbip_port_string, gai_strerror(sockfd)); | ||
| 139 | return -1; | ||
| 140 | } | ||
| 141 | dbg("connected to %s:%s", host, usbip_port_string); | ||
| 142 | |||
| 143 | rc = get_exported_devices(host, sockfd); | ||
| 144 | if (rc < 0) { | ||
| 145 | err("failed to get device list from %s", host); | ||
| 146 | return -1; | ||
| 147 | } | ||
| 148 | |||
| 149 | close(sockfd); | ||
| 150 | |||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | |||
| 154 | static void print_device(const char *busid, const char *vendor, | ||
| 155 | const char *product, bool parsable) | ||
| 156 | { | ||
| 157 | if (parsable) | ||
| 158 | printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product); | ||
| 159 | else | ||
| 160 | printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product); | ||
| 161 | } | ||
| 162 | |||
| 163 | static void print_product_name(char *product_name, bool parsable) | ||
| 164 | { | ||
| 165 | if (!parsable) | ||
| 166 | printf(" %s\n", product_name); | ||
| 167 | } | ||
| 168 | |||
| 169 | static int list_devices(bool parsable) | ||
| 170 | { | ||
| 171 | struct udev *udev; | ||
| 172 | struct udev_enumerate *enumerate; | ||
| 173 | struct udev_list_entry *devices, *dev_list_entry; | ||
| 174 | struct udev_device *dev; | ||
| 175 | const char *path; | ||
| 176 | const char *idVendor; | ||
| 177 | const char *idProduct; | ||
| 178 | const char *bConfValue; | ||
| 179 | const char *bNumIntfs; | ||
| 180 | const char *busid; | ||
| 181 | char product_name[128]; | ||
| 182 | int ret = -1; | ||
| 183 | |||
| 184 | /* Create libudev context. */ | ||
| 185 | udev = udev_new(); | ||
| 186 | |||
| 187 | /* Create libudev device enumeration. */ | ||
| 188 | enumerate = udev_enumerate_new(udev); | ||
| 189 | |||
| 190 | /* Take only USB devices that are not hubs and do not have | ||
| 191 | * the bInterfaceNumber attribute, i.e. are not interfaces. | ||
| 192 | */ | ||
| 193 | udev_enumerate_add_match_subsystem(enumerate, "usb"); | ||
| 194 | udev_enumerate_add_nomatch_sysattr(enumerate, "bDeviceClass", "09"); | ||
| 195 | udev_enumerate_add_nomatch_sysattr(enumerate, "bInterfaceNumber", NULL); | ||
| 196 | udev_enumerate_scan_devices(enumerate); | ||
| 197 | |||
| 198 | devices = udev_enumerate_get_list_entry(enumerate); | ||
| 199 | |||
| 200 | /* Show information about each device. */ | ||
| 201 | udev_list_entry_foreach(dev_list_entry, devices) { | ||
| 202 | path = udev_list_entry_get_name(dev_list_entry); | ||
| 203 | dev = udev_device_new_from_syspath(udev, path); | ||
| 204 | |||
| 205 | /* Get device information. */ | ||
| 206 | idVendor = udev_device_get_sysattr_value(dev, "idVendor"); | ||
| 207 | idProduct = udev_device_get_sysattr_value(dev, "idProduct"); | ||
| 208 | bConfValue = udev_device_get_sysattr_value(dev, "bConfigurationValue"); | ||
| 209 | bNumIntfs = udev_device_get_sysattr_value(dev, "bNumInterfaces"); | ||
| 210 | busid = udev_device_get_sysname(dev); | ||
| 211 | if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) { | ||
| 212 | err("problem getting device attributes: %s", | ||
| 213 | strerror(errno)); | ||
| 214 | goto err_out; | ||
| 215 | } | ||
| 216 | |||
| 217 | /* Get product name. */ | ||
| 218 | usbip_names_get_product(product_name, sizeof(product_name), | ||
| 219 | strtol(idVendor, NULL, 16), | ||
| 220 | strtol(idProduct, NULL, 16)); | ||
| 221 | |||
| 222 | /* Print information. */ | ||
| 223 | print_device(busid, idVendor, idProduct, parsable); | ||
| 224 | print_product_name(product_name, parsable); | ||
| 225 | |||
| 226 | printf("\n"); | ||
| 227 | |||
| 228 | udev_device_unref(dev); | ||
| 229 | } | ||
| 230 | |||
| 231 | ret = 0; | ||
| 232 | |||
| 233 | err_out: | ||
| 234 | udev_enumerate_unref(enumerate); | ||
| 235 | udev_unref(udev); | ||
| 236 | |||
| 237 | return ret; | ||
| 238 | } | ||
| 239 | |||
| 240 | int usbip_list(int argc, char *argv[]) | ||
| 241 | { | ||
| 242 | static const struct option opts[] = { | ||
| 243 | { "parsable", no_argument, NULL, 'p' }, | ||
| 244 | { "remote", required_argument, NULL, 'r' }, | ||
| 245 | { "local", no_argument, NULL, 'l' }, | ||
| 246 | { NULL, 0, NULL, 0 } | ||
| 247 | }; | ||
| 248 | |||
| 249 | bool parsable = false; | ||
| 250 | int opt; | ||
| 251 | int ret = -1; | ||
| 252 | |||
| 253 | if (usbip_names_init(USBIDS_FILE)) | ||
| 254 | err("failed to open %s", USBIDS_FILE); | ||
| 255 | |||
| 256 | for (;;) { | ||
| 257 | opt = getopt_long(argc, argv, "pr:l", opts, NULL); | ||
| 258 | |||
| 259 | if (opt == -1) | ||
| 260 | break; | ||
| 261 | |||
| 262 | switch (opt) { | ||
| 263 | case 'p': | ||
| 264 | parsable = true; | ||
| 265 | break; | ||
| 266 | case 'r': | ||
| 267 | ret = list_exported_devices(optarg); | ||
| 268 | goto out; | ||
| 269 | case 'l': | ||
| 270 | ret = list_devices(parsable); | ||
| 271 | goto out; | ||
| 272 | default: | ||
| 273 | goto err_out; | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | err_out: | ||
| 278 | usbip_list_usage(); | ||
| 279 | out: | ||
| 280 | usbip_names_free(); | ||
| 281 | |||
| 282 | return ret; | ||
| 283 | } | ||
diff --git a/tools/usb/usbip/src/usbip_network.c b/tools/usb/usbip/src/usbip_network.c new file mode 100644 index 000000000000..b4c37e76a6e0 --- /dev/null +++ b/tools/usb/usbip/src/usbip_network.c | |||
| @@ -0,0 +1,303 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <sys/socket.h> | ||
| 20 | |||
| 21 | #include <string.h> | ||
| 22 | |||
| 23 | #include <arpa/inet.h> | ||
| 24 | #include <netdb.h> | ||
| 25 | #include <netinet/tcp.h> | ||
| 26 | #include <unistd.h> | ||
| 27 | |||
| 28 | #ifdef HAVE_LIBWRAP | ||
| 29 | #include <tcpd.h> | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #include "usbip_common.h" | ||
| 33 | #include "usbip_network.h" | ||
| 34 | |||
| 35 | int usbip_port = 3240; | ||
| 36 | char *usbip_port_string = "3240"; | ||
| 37 | |||
| 38 | void usbip_setup_port_number(char *arg) | ||
| 39 | { | ||
| 40 | dbg("parsing port arg '%s'", arg); | ||
| 41 | char *end; | ||
| 42 | unsigned long int port = strtoul(arg, &end, 10); | ||
| 43 | |||
| 44 | if (end == arg) { | ||
| 45 | err("port: could not parse '%s' as a decimal integer", arg); | ||
| 46 | return; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (*end != '\0') { | ||
| 50 | err("port: garbage at end of '%s'", arg); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | |||
| 54 | if (port > UINT16_MAX) { | ||
| 55 | err("port: %s too high (max=%d)", | ||
| 56 | arg, UINT16_MAX); | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | |||
| 60 | usbip_port = port; | ||
| 61 | usbip_port_string = arg; | ||
| 62 | info("using port %d (\"%s\")", usbip_port, usbip_port_string); | ||
| 63 | } | ||
| 64 | |||
| 65 | void usbip_net_pack_uint32_t(int pack, uint32_t *num) | ||
| 66 | { | ||
| 67 | uint32_t i; | ||
| 68 | |||
| 69 | if (pack) | ||
| 70 | i = htonl(*num); | ||
| 71 | else | ||
| 72 | i = ntohl(*num); | ||
| 73 | |||
| 74 | *num = i; | ||
| 75 | } | ||
| 76 | |||
| 77 | void usbip_net_pack_uint16_t(int pack, uint16_t *num) | ||
| 78 | { | ||
| 79 | uint16_t i; | ||
| 80 | |||
| 81 | if (pack) | ||
| 82 | i = htons(*num); | ||
| 83 | else | ||
| 84 | i = ntohs(*num); | ||
| 85 | |||
| 86 | *num = i; | ||
| 87 | } | ||
| 88 | |||
| 89 | void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev) | ||
| 90 | { | ||
| 91 | usbip_net_pack_uint32_t(pack, &udev->busnum); | ||
| 92 | usbip_net_pack_uint32_t(pack, &udev->devnum); | ||
| 93 | usbip_net_pack_uint32_t(pack, &udev->speed); | ||
| 94 | |||
| 95 | usbip_net_pack_uint16_t(pack, &udev->idVendor); | ||
| 96 | usbip_net_pack_uint16_t(pack, &udev->idProduct); | ||
| 97 | usbip_net_pack_uint16_t(pack, &udev->bcdDevice); | ||
| 98 | } | ||
| 99 | |||
| 100 | void usbip_net_pack_usb_interface(int pack __attribute__((unused)), | ||
| 101 | struct usbip_usb_interface *udev | ||
| 102 | __attribute__((unused))) | ||
| 103 | { | ||
| 104 | /* uint8_t members need nothing */ | ||
| 105 | } | ||
| 106 | |||
| 107 | static ssize_t usbip_net_xmit(int sockfd, void *buff, size_t bufflen, | ||
| 108 | int sending) | ||
| 109 | { | ||
| 110 | ssize_t nbytes; | ||
| 111 | ssize_t total = 0; | ||
| 112 | |||
| 113 | if (!bufflen) | ||
| 114 | return 0; | ||
| 115 | |||
| 116 | do { | ||
| 117 | if (sending) | ||
| 118 | nbytes = send(sockfd, buff, bufflen, 0); | ||
| 119 | else | ||
| 120 | nbytes = recv(sockfd, buff, bufflen, MSG_WAITALL); | ||
| 121 | |||
| 122 | if (nbytes <= 0) | ||
| 123 | return -1; | ||
| 124 | |||
| 125 | buff = (void *)((intptr_t) buff + nbytes); | ||
| 126 | bufflen -= nbytes; | ||
| 127 | total += nbytes; | ||
| 128 | |||
| 129 | } while (bufflen > 0); | ||
| 130 | |||
| 131 | return total; | ||
| 132 | } | ||
| 133 | |||
| 134 | ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen) | ||
| 135 | { | ||
| 136 | return usbip_net_xmit(sockfd, buff, bufflen, 0); | ||
| 137 | } | ||
| 138 | |||
| 139 | ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen) | ||
| 140 | { | ||
| 141 | return usbip_net_xmit(sockfd, buff, bufflen, 1); | ||
| 142 | } | ||
| 143 | |||
| 144 | int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status) | ||
| 145 | { | ||
| 146 | struct op_common op_common; | ||
| 147 | int rc; | ||
| 148 | |||
| 149 | memset(&op_common, 0, sizeof(op_common)); | ||
| 150 | |||
| 151 | op_common.version = USBIP_VERSION; | ||
| 152 | op_common.code = code; | ||
| 153 | op_common.status = status; | ||
| 154 | |||
| 155 | PACK_OP_COMMON(1, &op_common); | ||
| 156 | |||
| 157 | rc = usbip_net_send(sockfd, &op_common, sizeof(op_common)); | ||
| 158 | if (rc < 0) { | ||
| 159 | dbg("usbip_net_send failed: %d", rc); | ||
| 160 | return -1; | ||
| 161 | } | ||
| 162 | |||
| 163 | return 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | int usbip_net_recv_op_common(int sockfd, uint16_t *code) | ||
| 167 | { | ||
| 168 | struct op_common op_common; | ||
| 169 | int rc; | ||
| 170 | |||
| 171 | memset(&op_common, 0, sizeof(op_common)); | ||
| 172 | |||
| 173 | rc = usbip_net_recv(sockfd, &op_common, sizeof(op_common)); | ||
| 174 | if (rc < 0) { | ||
| 175 | dbg("usbip_net_recv failed: %d", rc); | ||
| 176 | goto err; | ||
| 177 | } | ||
| 178 | |||
| 179 | PACK_OP_COMMON(0, &op_common); | ||
| 180 | |||
| 181 | if (op_common.version != USBIP_VERSION) { | ||
| 182 | dbg("version mismatch: %d %d", op_common.version, | ||
| 183 | USBIP_VERSION); | ||
| 184 | goto err; | ||
| 185 | } | ||
| 186 | |||
| 187 | switch (*code) { | ||
| 188 | case OP_UNSPEC: | ||
| 189 | break; | ||
| 190 | default: | ||
| 191 | if (op_common.code != *code) { | ||
| 192 | dbg("unexpected pdu %#0x for %#0x", op_common.code, | ||
| 193 | *code); | ||
| 194 | goto err; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | if (op_common.status != ST_OK) { | ||
| 199 | dbg("request failed at peer: %d", op_common.status); | ||
| 200 | goto err; | ||
| 201 | } | ||
| 202 | |||
| 203 | *code = op_common.code; | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | err: | ||
| 207 | return -1; | ||
| 208 | } | ||
| 209 | |||
| 210 | int usbip_net_set_reuseaddr(int sockfd) | ||
| 211 | { | ||
| 212 | const int val = 1; | ||
| 213 | int ret; | ||
| 214 | |||
| 215 | ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); | ||
| 216 | if (ret < 0) | ||
| 217 | dbg("setsockopt: SO_REUSEADDR"); | ||
| 218 | |||
| 219 | return ret; | ||
| 220 | } | ||
| 221 | |||
| 222 | int usbip_net_set_nodelay(int sockfd) | ||
| 223 | { | ||
| 224 | const int val = 1; | ||
| 225 | int ret; | ||
| 226 | |||
| 227 | ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); | ||
| 228 | if (ret < 0) | ||
| 229 | dbg("setsockopt: TCP_NODELAY"); | ||
| 230 | |||
| 231 | return ret; | ||
| 232 | } | ||
| 233 | |||
| 234 | int usbip_net_set_keepalive(int sockfd) | ||
| 235 | { | ||
| 236 | const int val = 1; | ||
| 237 | int ret; | ||
| 238 | |||
| 239 | ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); | ||
| 240 | if (ret < 0) | ||
| 241 | dbg("setsockopt: SO_KEEPALIVE"); | ||
| 242 | |||
| 243 | return ret; | ||
| 244 | } | ||
| 245 | |||
| 246 | int usbip_net_set_v6only(int sockfd) | ||
| 247 | { | ||
| 248 | const int val = 1; | ||
| 249 | int ret; | ||
| 250 | |||
| 251 | ret = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); | ||
| 252 | if (ret < 0) | ||
| 253 | dbg("setsockopt: IPV6_V6ONLY"); | ||
| 254 | |||
| 255 | return ret; | ||
| 256 | } | ||
| 257 | |||
| 258 | /* | ||
| 259 | * IPv6 Ready | ||
| 260 | */ | ||
| 261 | int usbip_net_tcp_connect(char *hostname, char *service) | ||
| 262 | { | ||
| 263 | struct addrinfo hints, *res, *rp; | ||
| 264 | int sockfd; | ||
| 265 | int ret; | ||
| 266 | |||
| 267 | memset(&hints, 0, sizeof(hints)); | ||
| 268 | hints.ai_family = AF_UNSPEC; | ||
| 269 | hints.ai_socktype = SOCK_STREAM; | ||
| 270 | |||
| 271 | /* get all possible addresses */ | ||
| 272 | ret = getaddrinfo(hostname, service, &hints, &res); | ||
| 273 | if (ret < 0) { | ||
| 274 | dbg("getaddrinfo: %s service %s: %s", hostname, service, | ||
| 275 | gai_strerror(ret)); | ||
| 276 | return ret; | ||
| 277 | } | ||
| 278 | |||
| 279 | /* try the addresses */ | ||
| 280 | for (rp = res; rp; rp = rp->ai_next) { | ||
| 281 | sockfd = socket(rp->ai_family, rp->ai_socktype, | ||
| 282 | rp->ai_protocol); | ||
| 283 | if (sockfd < 0) | ||
| 284 | continue; | ||
| 285 | |||
| 286 | /* should set TCP_NODELAY for usbip */ | ||
| 287 | usbip_net_set_nodelay(sockfd); | ||
| 288 | /* TODO: write code for heartbeat */ | ||
| 289 | usbip_net_set_keepalive(sockfd); | ||
| 290 | |||
| 291 | if (connect(sockfd, rp->ai_addr, rp->ai_addrlen) == 0) | ||
| 292 | break; | ||
| 293 | |||
| 294 | close(sockfd); | ||
| 295 | } | ||
| 296 | |||
| 297 | freeaddrinfo(res); | ||
| 298 | |||
| 299 | if (!rp) | ||
| 300 | return EAI_SYSTEM; | ||
| 301 | |||
| 302 | return sockfd; | ||
| 303 | } | ||
diff --git a/tools/usb/usbip/src/usbip_network.h b/tools/usb/usbip/src/usbip_network.h new file mode 100644 index 000000000000..c1e875cf1078 --- /dev/null +++ b/tools/usb/usbip/src/usbip_network.h | |||
| @@ -0,0 +1,185 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2007 Takahiro Hirofuchi | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef __USBIP_NETWORK_H | ||
| 6 | #define __USBIP_NETWORK_H | ||
| 7 | |||
| 8 | #ifdef HAVE_CONFIG_H | ||
| 9 | #include "../config.h" | ||
| 10 | #endif | ||
| 11 | |||
| 12 | #include <sys/types.h> | ||
| 13 | |||
| 14 | #include <stdint.h> | ||
| 15 | |||
| 16 | extern int usbip_port; | ||
| 17 | extern char *usbip_port_string; | ||
| 18 | void usbip_setup_port_number(char *arg); | ||
| 19 | |||
| 20 | /* ---------------------------------------------------------------------- */ | ||
| 21 | /* Common header for all the kinds of PDUs. */ | ||
| 22 | struct op_common { | ||
| 23 | uint16_t version; | ||
| 24 | |||
| 25 | #define OP_REQUEST (0x80 << 8) | ||
| 26 | #define OP_REPLY (0x00 << 8) | ||
| 27 | uint16_t code; | ||
| 28 | |||
| 29 | /* add more error code */ | ||
| 30 | #define ST_OK 0x00 | ||
| 31 | #define ST_NA 0x01 | ||
| 32 | uint32_t status; /* op_code status (for reply) */ | ||
| 33 | |||
| 34 | } __attribute__((packed)); | ||
| 35 | |||
| 36 | #define PACK_OP_COMMON(pack, op_common) do {\ | ||
| 37 | usbip_net_pack_uint16_t(pack, &(op_common)->version);\ | ||
| 38 | usbip_net_pack_uint16_t(pack, &(op_common)->code);\ | ||
| 39 | usbip_net_pack_uint32_t(pack, &(op_common)->status);\ | ||
| 40 | } while (0) | ||
| 41 | |||
| 42 | /* ---------------------------------------------------------------------- */ | ||
| 43 | /* Dummy Code */ | ||
| 44 | #define OP_UNSPEC 0x00 | ||
| 45 | #define OP_REQ_UNSPEC OP_UNSPEC | ||
| 46 | #define OP_REP_UNSPEC OP_UNSPEC | ||
| 47 | |||
| 48 | /* ---------------------------------------------------------------------- */ | ||
| 49 | /* Retrieve USB device information. (still not used) */ | ||
| 50 | #define OP_DEVINFO 0x02 | ||
| 51 | #define OP_REQ_DEVINFO (OP_REQUEST | OP_DEVINFO) | ||
| 52 | #define OP_REP_DEVINFO (OP_REPLY | OP_DEVINFO) | ||
| 53 | |||
| 54 | struct op_devinfo_request { | ||
| 55 | char busid[SYSFS_BUS_ID_SIZE]; | ||
| 56 | } __attribute__((packed)); | ||
| 57 | |||
| 58 | struct op_devinfo_reply { | ||
| 59 | struct usbip_usb_device udev; | ||
| 60 | struct usbip_usb_interface uinf[]; | ||
| 61 | } __attribute__((packed)); | ||
| 62 | |||
| 63 | /* ---------------------------------------------------------------------- */ | ||
| 64 | /* Import a remote USB device. */ | ||
| 65 | #define OP_IMPORT 0x03 | ||
| 66 | #define OP_REQ_IMPORT (OP_REQUEST | OP_IMPORT) | ||
| 67 | #define OP_REP_IMPORT (OP_REPLY | OP_IMPORT) | ||
| 68 | |||
| 69 | struct op_import_request { | ||
| 70 | char busid[SYSFS_BUS_ID_SIZE]; | ||
| 71 | } __attribute__((packed)); | ||
| 72 | |||
| 73 | struct op_import_reply { | ||
| 74 | struct usbip_usb_device udev; | ||
| 75 | // struct usbip_usb_interface uinf[]; | ||
| 76 | } __attribute__((packed)); | ||
| 77 | |||
| 78 | #define PACK_OP_IMPORT_REQUEST(pack, request) do {\ | ||
| 79 | } while (0) | ||
| 80 | |||
| 81 | #define PACK_OP_IMPORT_REPLY(pack, reply) do {\ | ||
| 82 | usbip_net_pack_usb_device(pack, &(reply)->udev);\ | ||
| 83 | } while (0) | ||
| 84 | |||
| 85 | /* ---------------------------------------------------------------------- */ | ||
| 86 | /* Export a USB device to a remote host. */ | ||
| 87 | #define OP_EXPORT 0x06 | ||
| 88 | #define OP_REQ_EXPORT (OP_REQUEST | OP_EXPORT) | ||
| 89 | #define OP_REP_EXPORT (OP_REPLY | OP_EXPORT) | ||
| 90 | |||
| 91 | struct op_export_request { | ||
| 92 | struct usbip_usb_device udev; | ||
| 93 | } __attribute__((packed)); | ||
| 94 | |||
| 95 | struct op_export_reply { | ||
| 96 | int returncode; | ||
| 97 | } __attribute__((packed)); | ||
| 98 | |||
| 99 | |||
| 100 | #define PACK_OP_EXPORT_REQUEST(pack, request) do {\ | ||
| 101 | usbip_net_pack_usb_device(pack, &(request)->udev);\ | ||
| 102 | } while (0) | ||
| 103 | |||
| 104 | #define PACK_OP_EXPORT_REPLY(pack, reply) do {\ | ||
| 105 | } while (0) | ||
| 106 | |||
| 107 | /* ---------------------------------------------------------------------- */ | ||
| 108 | /* un-Export a USB device from a remote host. */ | ||
| 109 | #define OP_UNEXPORT 0x07 | ||
| 110 | #define OP_REQ_UNEXPORT (OP_REQUEST | OP_UNEXPORT) | ||
| 111 | #define OP_REP_UNEXPORT (OP_REPLY | OP_UNEXPORT) | ||
| 112 | |||
| 113 | struct op_unexport_request { | ||
| 114 | struct usbip_usb_device udev; | ||
| 115 | } __attribute__((packed)); | ||
| 116 | |||
| 117 | struct op_unexport_reply { | ||
| 118 | int returncode; | ||
| 119 | } __attribute__((packed)); | ||
| 120 | |||
| 121 | #define PACK_OP_UNEXPORT_REQUEST(pack, request) do {\ | ||
| 122 | usbip_net_pack_usb_device(pack, &(request)->udev);\ | ||
| 123 | } while (0) | ||
| 124 | |||
| 125 | #define PACK_OP_UNEXPORT_REPLY(pack, reply) do {\ | ||
| 126 | } while (0) | ||
| 127 | |||
| 128 | /* ---------------------------------------------------------------------- */ | ||
| 129 | /* Negotiate IPSec encryption key. (still not used) */ | ||
| 130 | #define OP_CRYPKEY 0x04 | ||
| 131 | #define OP_REQ_CRYPKEY (OP_REQUEST | OP_CRYPKEY) | ||
| 132 | #define OP_REP_CRYPKEY (OP_REPLY | OP_CRYPKEY) | ||
| 133 | |||
| 134 | struct op_crypkey_request { | ||
| 135 | /* 128bit key */ | ||
| 136 | uint32_t key[4]; | ||
| 137 | } __attribute__((packed)); | ||
| 138 | |||
| 139 | struct op_crypkey_reply { | ||
| 140 | uint32_t __reserved; | ||
| 141 | } __attribute__((packed)); | ||
| 142 | |||
| 143 | |||
| 144 | /* ---------------------------------------------------------------------- */ | ||
| 145 | /* Retrieve the list of exported USB devices. */ | ||
| 146 | #define OP_DEVLIST 0x05 | ||
| 147 | #define OP_REQ_DEVLIST (OP_REQUEST | OP_DEVLIST) | ||
| 148 | #define OP_REP_DEVLIST (OP_REPLY | OP_DEVLIST) | ||
| 149 | |||
| 150 | struct op_devlist_request { | ||
| 151 | } __attribute__((packed)); | ||
| 152 | |||
| 153 | struct op_devlist_reply { | ||
| 154 | uint32_t ndev; | ||
| 155 | /* followed by reply_extra[] */ | ||
| 156 | } __attribute__((packed)); | ||
| 157 | |||
| 158 | struct op_devlist_reply_extra { | ||
| 159 | struct usbip_usb_device udev; | ||
| 160 | struct usbip_usb_interface uinf[]; | ||
| 161 | } __attribute__((packed)); | ||
| 162 | |||
| 163 | #define PACK_OP_DEVLIST_REQUEST(pack, request) do {\ | ||
| 164 | } while (0) | ||
| 165 | |||
| 166 | #define PACK_OP_DEVLIST_REPLY(pack, reply) do {\ | ||
| 167 | usbip_net_pack_uint32_t(pack, &(reply)->ndev);\ | ||
| 168 | } while (0) | ||
| 169 | |||
| 170 | void usbip_net_pack_uint32_t(int pack, uint32_t *num); | ||
| 171 | void usbip_net_pack_uint16_t(int pack, uint16_t *num); | ||
| 172 | void usbip_net_pack_usb_device(int pack, struct usbip_usb_device *udev); | ||
| 173 | void usbip_net_pack_usb_interface(int pack, struct usbip_usb_interface *uinf); | ||
| 174 | |||
| 175 | ssize_t usbip_net_recv(int sockfd, void *buff, size_t bufflen); | ||
| 176 | ssize_t usbip_net_send(int sockfd, void *buff, size_t bufflen); | ||
| 177 | int usbip_net_send_op_common(int sockfd, uint32_t code, uint32_t status); | ||
| 178 | int usbip_net_recv_op_common(int sockfd, uint16_t *code); | ||
| 179 | int usbip_net_set_reuseaddr(int sockfd); | ||
| 180 | int usbip_net_set_nodelay(int sockfd); | ||
| 181 | int usbip_net_set_keepalive(int sockfd); | ||
| 182 | int usbip_net_set_v6only(int sockfd); | ||
| 183 | int usbip_net_tcp_connect(char *hostname, char *port); | ||
| 184 | |||
| 185 | #endif /* __USBIP_NETWORK_H */ | ||
diff --git a/tools/usb/usbip/src/usbip_port.c b/tools/usb/usbip/src/usbip_port.c new file mode 100644 index 000000000000..a2e884fd9226 --- /dev/null +++ b/tools/usb/usbip/src/usbip_port.c | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include "vhci_driver.h" | ||
| 17 | #include "usbip_common.h" | ||
| 18 | |||
| 19 | static int list_imported_devices(void) | ||
| 20 | { | ||
| 21 | int i; | ||
| 22 | struct usbip_imported_device *idev; | ||
| 23 | int ret; | ||
| 24 | |||
| 25 | ret = usbip_vhci_driver_open(); | ||
| 26 | if (ret < 0) { | ||
| 27 | err("open vhci_driver"); | ||
| 28 | return -1; | ||
| 29 | } | ||
| 30 | |||
| 31 | printf("Imported USB devices\n"); | ||
| 32 | printf("====================\n"); | ||
| 33 | |||
| 34 | for (i = 0; i < vhci_driver->nports; i++) { | ||
| 35 | idev = &vhci_driver->idev[i]; | ||
| 36 | |||
| 37 | if (usbip_vhci_imported_device_dump(idev) < 0) | ||
| 38 | ret = -1; | ||
| 39 | } | ||
| 40 | |||
| 41 | usbip_vhci_driver_close(); | ||
| 42 | |||
| 43 | return ret; | ||
| 44 | |||
| 45 | } | ||
| 46 | |||
| 47 | int usbip_port_show(__attribute__((unused)) int argc, | ||
| 48 | __attribute__((unused)) char *argv[]) | ||
| 49 | { | ||
| 50 | int ret; | ||
| 51 | |||
| 52 | ret = list_imported_devices(); | ||
| 53 | if (ret < 0) | ||
| 54 | err("list imported devices"); | ||
| 55 | |||
| 56 | return ret; | ||
| 57 | } | ||
diff --git a/tools/usb/usbip/src/usbip_unbind.c b/tools/usb/usbip/src/usbip_unbind.c new file mode 100644 index 000000000000..a4a496c9cbaf --- /dev/null +++ b/tools/usb/usbip/src/usbip_unbind.c | |||
| @@ -0,0 +1,141 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <libudev.h> | ||
| 20 | |||
| 21 | #include <errno.h> | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <string.h> | ||
| 24 | |||
| 25 | #include <getopt.h> | ||
| 26 | |||
| 27 | #include "usbip_common.h" | ||
| 28 | #include "utils.h" | ||
| 29 | #include "usbip.h" | ||
| 30 | #include "sysfs_utils.h" | ||
| 31 | |||
| 32 | static const char usbip_unbind_usage_string[] = | ||
| 33 | "usbip unbind <args>\n" | ||
| 34 | " -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from " | ||
| 35 | "device on <busid>\n"; | ||
| 36 | |||
| 37 | void usbip_unbind_usage(void) | ||
| 38 | { | ||
| 39 | printf("usage: %s", usbip_unbind_usage_string); | ||
| 40 | } | ||
| 41 | |||
| 42 | static int unbind_device(char *busid) | ||
| 43 | { | ||
| 44 | char bus_type[] = "usb"; | ||
| 45 | int rc, ret = -1; | ||
| 46 | |||
| 47 | char unbind_attr_name[] = "unbind"; | ||
| 48 | char unbind_attr_path[SYSFS_PATH_MAX]; | ||
| 49 | char rebind_attr_name[] = "rebind"; | ||
| 50 | char rebind_attr_path[SYSFS_PATH_MAX]; | ||
| 51 | |||
| 52 | struct udev *udev; | ||
| 53 | struct udev_device *dev; | ||
| 54 | const char *driver; | ||
| 55 | |||
| 56 | /* Create libudev context. */ | ||
| 57 | udev = udev_new(); | ||
| 58 | |||
| 59 | /* Check whether the device with this bus ID exists. */ | ||
| 60 | dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid); | ||
| 61 | if (!dev) { | ||
| 62 | err("device with the specified bus ID does not exist"); | ||
| 63 | goto err_close_udev; | ||
| 64 | } | ||
| 65 | |||
| 66 | /* Check whether the device is using usbip-host driver. */ | ||
| 67 | driver = udev_device_get_driver(dev); | ||
| 68 | if (!driver || strcmp(driver, "usbip-host")) { | ||
| 69 | err("device is not bound to usbip-host driver"); | ||
| 70 | goto err_close_udev; | ||
| 71 | } | ||
| 72 | |||
| 73 | /* Unbind device from driver. */ | ||
| 74 | snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s", | ||
| 75 | SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME, | ||
| 76 | USBIP_HOST_DRV_NAME, unbind_attr_name); | ||
| 77 | |||
| 78 | rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid)); | ||
| 79 | if (rc < 0) { | ||
| 80 | err("error unbinding device %s from driver", busid); | ||
| 81 | goto err_close_udev; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* Notify driver of unbind. */ | ||
| 85 | rc = modify_match_busid(busid, 0); | ||
| 86 | if (rc < 0) { | ||
| 87 | err("unable to unbind device on %s", busid); | ||
| 88 | goto err_close_udev; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Trigger new probing. */ | ||
| 92 | snprintf(rebind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s", | ||
| 93 | SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME, | ||
| 94 | USBIP_HOST_DRV_NAME, rebind_attr_name); | ||
| 95 | |||
| 96 | rc = write_sysfs_attribute(rebind_attr_path, busid, strlen(busid)); | ||
| 97 | if (rc < 0) { | ||
| 98 | err("error rebinding"); | ||
| 99 | goto err_close_udev; | ||
| 100 | } | ||
| 101 | |||
| 102 | ret = 0; | ||
| 103 | info("unbind device on busid %s: complete", busid); | ||
| 104 | |||
| 105 | err_close_udev: | ||
| 106 | udev_device_unref(dev); | ||
| 107 | udev_unref(udev); | ||
| 108 | |||
| 109 | return ret; | ||
| 110 | } | ||
| 111 | |||
| 112 | int usbip_unbind(int argc, char *argv[]) | ||
| 113 | { | ||
| 114 | static const struct option opts[] = { | ||
| 115 | { "busid", required_argument, NULL, 'b' }, | ||
| 116 | { NULL, 0, NULL, 0 } | ||
| 117 | }; | ||
| 118 | |||
| 119 | int opt; | ||
| 120 | int ret = -1; | ||
| 121 | |||
| 122 | for (;;) { | ||
| 123 | opt = getopt_long(argc, argv, "b:", opts, NULL); | ||
| 124 | |||
| 125 | if (opt == -1) | ||
| 126 | break; | ||
| 127 | |||
| 128 | switch (opt) { | ||
| 129 | case 'b': | ||
| 130 | ret = unbind_device(optarg); | ||
| 131 | goto out; | ||
| 132 | default: | ||
| 133 | goto err_out; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | err_out: | ||
| 138 | usbip_unbind_usage(); | ||
| 139 | out: | ||
| 140 | return ret; | ||
| 141 | } | ||
diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c new file mode 100644 index 000000000000..2f87f2d348ba --- /dev/null +++ b/tools/usb/usbip/src/usbipd.c | |||
| @@ -0,0 +1,679 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #ifdef HAVE_CONFIG_H | ||
| 20 | #include "../config.h" | ||
| 21 | #endif | ||
| 22 | |||
| 23 | #define _GNU_SOURCE | ||
| 24 | #include <errno.h> | ||
| 25 | #include <unistd.h> | ||
| 26 | #include <netdb.h> | ||
| 27 | #include <string.h> | ||
| 28 | #include <stdlib.h> | ||
| 29 | #include <sys/types.h> | ||
| 30 | #include <sys/stat.h> | ||
| 31 | #include <arpa/inet.h> | ||
| 32 | #include <sys/socket.h> | ||
| 33 | #include <netinet/in.h> | ||
| 34 | |||
| 35 | #ifdef HAVE_LIBWRAP | ||
| 36 | #include <tcpd.h> | ||
| 37 | #endif | ||
| 38 | |||
| 39 | #include <getopt.h> | ||
| 40 | #include <signal.h> | ||
| 41 | #include <poll.h> | ||
| 42 | |||
| 43 | #include "usbip_host_driver.h" | ||
| 44 | #include "usbip_common.h" | ||
| 45 | #include "usbip_network.h" | ||
| 46 | #include "list.h" | ||
| 47 | |||
| 48 | #undef PROGNAME | ||
| 49 | #define PROGNAME "usbipd" | ||
| 50 | #define MAXSOCKFD 20 | ||
| 51 | |||
| 52 | #define MAIN_LOOP_TIMEOUT 10 | ||
| 53 | |||
| 54 | #define DEFAULT_PID_FILE "/var/run/" PROGNAME ".pid" | ||
| 55 | |||
| 56 | static const char usbip_version_string[] = PACKAGE_STRING; | ||
| 57 | |||
| 58 | static const char usbipd_help_string[] = | ||
| 59 | "usage: usbipd [options]\n" | ||
| 60 | "\n" | ||
| 61 | " -4, --ipv4\n" | ||
| 62 | " Bind to IPv4. Default is both.\n" | ||
| 63 | "\n" | ||
| 64 | " -6, --ipv6\n" | ||
| 65 | " Bind to IPv6. Default is both.\n" | ||
| 66 | "\n" | ||
| 67 | " -D, --daemon\n" | ||
| 68 | " Run as a daemon process.\n" | ||
| 69 | "\n" | ||
| 70 | " -d, --debug\n" | ||
| 71 | " Print debugging information.\n" | ||
| 72 | "\n" | ||
| 73 | " -PFILE, --pid FILE\n" | ||
| 74 | " Write process id to FILE.\n" | ||
| 75 | " If no FILE specified, use " DEFAULT_PID_FILE "\n" | ||
| 76 | "\n" | ||
| 77 | " -tPORT, --tcp-port PORT\n" | ||
| 78 | " Listen on TCP/IP port PORT.\n" | ||
| 79 | "\n" | ||
| 80 | " -h, --help\n" | ||
| 81 | " Print this help.\n" | ||
| 82 | "\n" | ||
| 83 | " -v, --version\n" | ||
| 84 | " Show version.\n"; | ||
| 85 | |||
| 86 | static void usbipd_help(void) | ||
| 87 | { | ||
| 88 | printf("%s\n", usbipd_help_string); | ||
| 89 | } | ||
| 90 | |||
| 91 | static int recv_request_import(int sockfd) | ||
| 92 | { | ||
| 93 | struct op_import_request req; | ||
| 94 | struct op_common reply; | ||
| 95 | struct usbip_exported_device *edev; | ||
| 96 | struct usbip_usb_device pdu_udev; | ||
| 97 | struct list_head *i; | ||
| 98 | int found = 0; | ||
| 99 | int error = 0; | ||
| 100 | int rc; | ||
| 101 | |||
| 102 | memset(&req, 0, sizeof(req)); | ||
| 103 | memset(&reply, 0, sizeof(reply)); | ||
| 104 | |||
| 105 | rc = usbip_net_recv(sockfd, &req, sizeof(req)); | ||
| 106 | if (rc < 0) { | ||
| 107 | dbg("usbip_net_recv failed: import request"); | ||
| 108 | return -1; | ||
| 109 | } | ||
| 110 | PACK_OP_IMPORT_REQUEST(0, &req); | ||
| 111 | |||
| 112 | list_for_each(i, &host_driver->edev_list) { | ||
| 113 | edev = list_entry(i, struct usbip_exported_device, node); | ||
| 114 | if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) { | ||
| 115 | info("found requested device: %s", req.busid); | ||
| 116 | found = 1; | ||
| 117 | break; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | if (found) { | ||
| 122 | /* should set TCP_NODELAY for usbip */ | ||
| 123 | usbip_net_set_nodelay(sockfd); | ||
| 124 | |||
| 125 | /* export device needs a TCP/IP socket descriptor */ | ||
| 126 | rc = usbip_host_export_device(edev, sockfd); | ||
| 127 | if (rc < 0) | ||
| 128 | error = 1; | ||
| 129 | } else { | ||
| 130 | info("requested device not found: %s", req.busid); | ||
| 131 | error = 1; | ||
| 132 | } | ||
| 133 | |||
| 134 | rc = usbip_net_send_op_common(sockfd, OP_REP_IMPORT, | ||
| 135 | (!error ? ST_OK : ST_NA)); | ||
| 136 | if (rc < 0) { | ||
| 137 | dbg("usbip_net_send_op_common failed: %#0x", OP_REP_IMPORT); | ||
| 138 | return -1; | ||
| 139 | } | ||
| 140 | |||
| 141 | if (error) { | ||
| 142 | dbg("import request busid %s: failed", req.busid); | ||
| 143 | return -1; | ||
| 144 | } | ||
| 145 | |||
| 146 | memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); | ||
| 147 | usbip_net_pack_usb_device(1, &pdu_udev); | ||
| 148 | |||
| 149 | rc = usbip_net_send(sockfd, &pdu_udev, sizeof(pdu_udev)); | ||
| 150 | if (rc < 0) { | ||
| 151 | dbg("usbip_net_send failed: devinfo"); | ||
| 152 | return -1; | ||
| 153 | } | ||
| 154 | |||
| 155 | dbg("import request busid %s: complete", req.busid); | ||
| 156 | |||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | static int send_reply_devlist(int connfd) | ||
| 161 | { | ||
| 162 | struct usbip_exported_device *edev; | ||
| 163 | struct usbip_usb_device pdu_udev; | ||
| 164 | struct usbip_usb_interface pdu_uinf; | ||
| 165 | struct op_devlist_reply reply; | ||
| 166 | struct list_head *j; | ||
| 167 | int rc, i; | ||
| 168 | |||
| 169 | reply.ndev = 0; | ||
| 170 | /* number of exported devices */ | ||
| 171 | list_for_each(j, &host_driver->edev_list) { | ||
| 172 | reply.ndev += 1; | ||
| 173 | } | ||
| 174 | info("exportable devices: %d", reply.ndev); | ||
| 175 | |||
| 176 | rc = usbip_net_send_op_common(connfd, OP_REP_DEVLIST, ST_OK); | ||
| 177 | if (rc < 0) { | ||
| 178 | dbg("usbip_net_send_op_common failed: %#0x", OP_REP_DEVLIST); | ||
| 179 | return -1; | ||
| 180 | } | ||
| 181 | PACK_OP_DEVLIST_REPLY(1, &reply); | ||
| 182 | |||
| 183 | rc = usbip_net_send(connfd, &reply, sizeof(reply)); | ||
| 184 | if (rc < 0) { | ||
| 185 | dbg("usbip_net_send failed: %#0x", OP_REP_DEVLIST); | ||
| 186 | return -1; | ||
| 187 | } | ||
| 188 | |||
| 189 | list_for_each(j, &host_driver->edev_list) { | ||
| 190 | edev = list_entry(j, struct usbip_exported_device, node); | ||
| 191 | dump_usb_device(&edev->udev); | ||
| 192 | memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev)); | ||
| 193 | usbip_net_pack_usb_device(1, &pdu_udev); | ||
| 194 | |||
| 195 | rc = usbip_net_send(connfd, &pdu_udev, sizeof(pdu_udev)); | ||
| 196 | if (rc < 0) { | ||
| 197 | dbg("usbip_net_send failed: pdu_udev"); | ||
| 198 | return -1; | ||
| 199 | } | ||
| 200 | |||
| 201 | for (i = 0; i < edev->udev.bNumInterfaces; i++) { | ||
| 202 | dump_usb_interface(&edev->uinf[i]); | ||
| 203 | memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf)); | ||
| 204 | usbip_net_pack_usb_interface(1, &pdu_uinf); | ||
| 205 | |||
| 206 | rc = usbip_net_send(connfd, &pdu_uinf, | ||
| 207 | sizeof(pdu_uinf)); | ||
| 208 | if (rc < 0) { | ||
| 209 | err("usbip_net_send failed: pdu_uinf"); | ||
| 210 | return -1; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | return 0; | ||
| 216 | } | ||
| 217 | |||
| 218 | static int recv_request_devlist(int connfd) | ||
| 219 | { | ||
| 220 | struct op_devlist_request req; | ||
| 221 | int rc; | ||
| 222 | |||
| 223 | memset(&req, 0, sizeof(req)); | ||
| 224 | |||
| 225 | rc = usbip_net_recv(connfd, &req, sizeof(req)); | ||
| 226 | if (rc < 0) { | ||
| 227 | dbg("usbip_net_recv failed: devlist request"); | ||
| 228 | return -1; | ||
| 229 | } | ||
| 230 | |||
| 231 | rc = send_reply_devlist(connfd); | ||
| 232 | if (rc < 0) { | ||
| 233 | dbg("send_reply_devlist failed"); | ||
| 234 | return -1; | ||
| 235 | } | ||
| 236 | |||
| 237 | return 0; | ||
| 238 | } | ||
| 239 | |||
| 240 | static int recv_pdu(int connfd) | ||
| 241 | { | ||
| 242 | uint16_t code = OP_UNSPEC; | ||
| 243 | int ret; | ||
| 244 | |||
| 245 | ret = usbip_net_recv_op_common(connfd, &code); | ||
| 246 | if (ret < 0) { | ||
| 247 | dbg("could not receive opcode: %#0x", code); | ||
| 248 | return -1; | ||
| 249 | } | ||
| 250 | |||
| 251 | ret = usbip_host_refresh_device_list(); | ||
| 252 | if (ret < 0) { | ||
| 253 | dbg("could not refresh device list: %d", ret); | ||
| 254 | return -1; | ||
| 255 | } | ||
| 256 | |||
| 257 | info("received request: %#0x(%d)", code, connfd); | ||
| 258 | switch (code) { | ||
| 259 | case OP_REQ_DEVLIST: | ||
| 260 | ret = recv_request_devlist(connfd); | ||
| 261 | break; | ||
| 262 | case OP_REQ_IMPORT: | ||
| 263 | ret = recv_request_import(connfd); | ||
| 264 | break; | ||
| 265 | case OP_REQ_DEVINFO: | ||
| 266 | case OP_REQ_CRYPKEY: | ||
| 267 | default: | ||
| 268 | err("received an unknown opcode: %#0x", code); | ||
| 269 | ret = -1; | ||
| 270 | } | ||
| 271 | |||
| 272 | if (ret == 0) | ||
| 273 | info("request %#0x(%d): complete", code, connfd); | ||
| 274 | else | ||
| 275 | info("request %#0x(%d): failed", code, connfd); | ||
| 276 | |||
| 277 | return ret; | ||
| 278 | } | ||
| 279 | |||
| 280 | #ifdef HAVE_LIBWRAP | ||
| 281 | static int tcpd_auth(int connfd) | ||
| 282 | { | ||
| 283 | struct request_info request; | ||
| 284 | int rc; | ||
| 285 | |||
| 286 | request_init(&request, RQ_DAEMON, PROGNAME, RQ_FILE, connfd, 0); | ||
| 287 | fromhost(&request); | ||
| 288 | rc = hosts_access(&request); | ||
| 289 | if (rc == 0) | ||
| 290 | return -1; | ||
| 291 | |||
| 292 | return 0; | ||
| 293 | } | ||
| 294 | #endif | ||
| 295 | |||
| 296 | static int do_accept(int listenfd) | ||
| 297 | { | ||
| 298 | int connfd; | ||
| 299 | struct sockaddr_storage ss; | ||
| 300 | socklen_t len = sizeof(ss); | ||
| 301 | char host[NI_MAXHOST], port[NI_MAXSERV]; | ||
| 302 | int rc; | ||
| 303 | |||
| 304 | memset(&ss, 0, sizeof(ss)); | ||
| 305 | |||
| 306 | connfd = accept(listenfd, (struct sockaddr *)&ss, &len); | ||
| 307 | if (connfd < 0) { | ||
| 308 | err("failed to accept connection"); | ||
| 309 | return -1; | ||
| 310 | } | ||
| 311 | |||
| 312 | rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host), | ||
| 313 | port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); | ||
| 314 | if (rc) | ||
| 315 | err("getnameinfo: %s", gai_strerror(rc)); | ||
| 316 | |||
| 317 | #ifdef HAVE_LIBWRAP | ||
| 318 | rc = tcpd_auth(connfd); | ||
| 319 | if (rc < 0) { | ||
| 320 | info("denied access from %s", host); | ||
| 321 | close(connfd); | ||
| 322 | return -1; | ||
| 323 | } | ||
| 324 | #endif | ||
| 325 | info("connection from %s:%s", host, port); | ||
| 326 | |||
| 327 | return connfd; | ||
| 328 | } | ||
| 329 | |||
| 330 | int process_request(int listenfd) | ||
| 331 | { | ||
| 332 | pid_t childpid; | ||
| 333 | int connfd; | ||
| 334 | |||
| 335 | connfd = do_accept(listenfd); | ||
| 336 | if (connfd < 0) | ||
| 337 | return -1; | ||
| 338 | childpid = fork(); | ||
| 339 | if (childpid == 0) { | ||
| 340 | close(listenfd); | ||
| 341 | recv_pdu(connfd); | ||
| 342 | exit(0); | ||
| 343 | } | ||
| 344 | close(connfd); | ||
| 345 | return 0; | ||
| 346 | } | ||
| 347 | |||
| 348 | static void addrinfo_to_text(struct addrinfo *ai, char buf[], | ||
| 349 | const size_t buf_size) | ||
| 350 | { | ||
| 351 | char hbuf[NI_MAXHOST]; | ||
| 352 | char sbuf[NI_MAXSERV]; | ||
| 353 | int rc; | ||
| 354 | |||
| 355 | buf[0] = '\0'; | ||
| 356 | |||
| 357 | rc = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), | ||
| 358 | sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); | ||
| 359 | if (rc) | ||
| 360 | err("getnameinfo: %s", gai_strerror(rc)); | ||
| 361 | |||
| 362 | snprintf(buf, buf_size, "%s:%s", hbuf, sbuf); | ||
| 363 | } | ||
| 364 | |||
| 365 | static int listen_all_addrinfo(struct addrinfo *ai_head, int sockfdlist[], | ||
| 366 | int maxsockfd) | ||
| 367 | { | ||
| 368 | struct addrinfo *ai; | ||
| 369 | int ret, nsockfd = 0; | ||
| 370 | const size_t ai_buf_size = NI_MAXHOST + NI_MAXSERV + 2; | ||
| 371 | char ai_buf[ai_buf_size]; | ||
| 372 | |||
| 373 | for (ai = ai_head; ai && nsockfd < maxsockfd; ai = ai->ai_next) { | ||
| 374 | int sock; | ||
| 375 | |||
| 376 | addrinfo_to_text(ai, ai_buf, ai_buf_size); | ||
| 377 | dbg("opening %s", ai_buf); | ||
| 378 | sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | ||
| 379 | if (sock < 0) { | ||
| 380 | err("socket: %s: %d (%s)", | ||
| 381 | ai_buf, errno, strerror(errno)); | ||
| 382 | continue; | ||
| 383 | } | ||
| 384 | |||
| 385 | usbip_net_set_reuseaddr(sock); | ||
| 386 | usbip_net_set_nodelay(sock); | ||
| 387 | /* We use seperate sockets for IPv4 and IPv6 | ||
| 388 | * (see do_standalone_mode()) */ | ||
| 389 | usbip_net_set_v6only(sock); | ||
| 390 | |||
| 391 | if (sock >= FD_SETSIZE) { | ||
| 392 | err("FD_SETSIZE: %s: sock=%d, max=%d", | ||
| 393 | ai_buf, sock, FD_SETSIZE); | ||
| 394 | close(sock); | ||
| 395 | continue; | ||
| 396 | } | ||
| 397 | |||
| 398 | ret = bind(sock, ai->ai_addr, ai->ai_addrlen); | ||
| 399 | if (ret < 0) { | ||
| 400 | err("bind: %s: %d (%s)", | ||
| 401 | ai_buf, errno, strerror(errno)); | ||
| 402 | close(sock); | ||
| 403 | continue; | ||
| 404 | } | ||
| 405 | |||
| 406 | ret = listen(sock, SOMAXCONN); | ||
| 407 | if (ret < 0) { | ||
| 408 | err("listen: %s: %d (%s)", | ||
| 409 | ai_buf, errno, strerror(errno)); | ||
| 410 | close(sock); | ||
| 411 | continue; | ||
| 412 | } | ||
| 413 | |||
| 414 | info("listening on %s", ai_buf); | ||
| 415 | sockfdlist[nsockfd++] = sock; | ||
| 416 | } | ||
| 417 | |||
| 418 | return nsockfd; | ||
| 419 | } | ||
| 420 | |||
| 421 | static struct addrinfo *do_getaddrinfo(char *host, int ai_family) | ||
| 422 | { | ||
| 423 | struct addrinfo hints, *ai_head; | ||
| 424 | int rc; | ||
| 425 | |||
| 426 | memset(&hints, 0, sizeof(hints)); | ||
| 427 | hints.ai_family = ai_family; | ||
| 428 | hints.ai_socktype = SOCK_STREAM; | ||
| 429 | hints.ai_flags = AI_PASSIVE; | ||
| 430 | |||
| 431 | rc = getaddrinfo(host, usbip_port_string, &hints, &ai_head); | ||
| 432 | if (rc) { | ||
| 433 | err("failed to get a network address %s: %s", usbip_port_string, | ||
| 434 | gai_strerror(rc)); | ||
| 435 | return NULL; | ||
| 436 | } | ||
| 437 | |||
| 438 | return ai_head; | ||
| 439 | } | ||
| 440 | |||
| 441 | static void signal_handler(int i) | ||
| 442 | { | ||
| 443 | dbg("received '%s' signal", strsignal(i)); | ||
| 444 | } | ||
| 445 | |||
| 446 | static void set_signal(void) | ||
| 447 | { | ||
| 448 | struct sigaction act; | ||
| 449 | |||
| 450 | memset(&act, 0, sizeof(act)); | ||
| 451 | act.sa_handler = signal_handler; | ||
| 452 | sigemptyset(&act.sa_mask); | ||
| 453 | sigaction(SIGTERM, &act, NULL); | ||
| 454 | sigaction(SIGINT, &act, NULL); | ||
| 455 | act.sa_handler = SIG_IGN; | ||
| 456 | sigaction(SIGCLD, &act, NULL); | ||
| 457 | } | ||
| 458 | |||
| 459 | static const char *pid_file; | ||
| 460 | |||
| 461 | static void write_pid_file(void) | ||
| 462 | { | ||
| 463 | if (pid_file) { | ||
| 464 | dbg("creating pid file %s", pid_file); | ||
| 465 | FILE *fp; | ||
| 466 | |||
| 467 | fp = fopen(pid_file, "w"); | ||
| 468 | if (!fp) { | ||
| 469 | err("pid_file: %s: %d (%s)", | ||
| 470 | pid_file, errno, strerror(errno)); | ||
| 471 | return; | ||
| 472 | } | ||
| 473 | fprintf(fp, "%d\n", getpid()); | ||
| 474 | fclose(fp); | ||
| 475 | } | ||
| 476 | } | ||
| 477 | |||
| 478 | static void remove_pid_file(void) | ||
| 479 | { | ||
| 480 | if (pid_file) { | ||
| 481 | dbg("removing pid file %s", pid_file); | ||
| 482 | unlink(pid_file); | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | static int do_standalone_mode(int daemonize, int ipv4, int ipv6) | ||
| 487 | { | ||
| 488 | struct addrinfo *ai_head; | ||
| 489 | int sockfdlist[MAXSOCKFD]; | ||
| 490 | int nsockfd, family; | ||
| 491 | int i, terminate; | ||
| 492 | struct pollfd *fds; | ||
| 493 | struct timespec timeout; | ||
| 494 | sigset_t sigmask; | ||
| 495 | |||
| 496 | if (usbip_host_driver_open()) { | ||
| 497 | err("please load " USBIP_CORE_MOD_NAME ".ko and " | ||
| 498 | USBIP_HOST_DRV_NAME ".ko!"); | ||
| 499 | return -1; | ||
| 500 | } | ||
| 501 | |||
| 502 | if (daemonize) { | ||
| 503 | if (daemon(0, 0) < 0) { | ||
| 504 | err("daemonizing failed: %s", strerror(errno)); | ||
| 505 | usbip_host_driver_close(); | ||
| 506 | return -1; | ||
| 507 | } | ||
| 508 | umask(0); | ||
| 509 | usbip_use_syslog = 1; | ||
| 510 | } | ||
| 511 | set_signal(); | ||
| 512 | write_pid_file(); | ||
| 513 | |||
| 514 | info("starting " PROGNAME " (%s)", usbip_version_string); | ||
| 515 | |||
| 516 | /* | ||
| 517 | * To suppress warnings on systems with bindv6only disabled | ||
| 518 | * (default), we use seperate sockets for IPv6 and IPv4 and set | ||
| 519 | * IPV6_V6ONLY on the IPv6 sockets. | ||
| 520 | */ | ||
| 521 | if (ipv4 && ipv6) | ||
| 522 | family = AF_UNSPEC; | ||
| 523 | else if (ipv4) | ||
| 524 | family = AF_INET; | ||
| 525 | else | ||
| 526 | family = AF_INET6; | ||
| 527 | |||
| 528 | ai_head = do_getaddrinfo(NULL, family); | ||
| 529 | if (!ai_head) { | ||
| 530 | usbip_host_driver_close(); | ||
| 531 | return -1; | ||
| 532 | } | ||
| 533 | nsockfd = listen_all_addrinfo(ai_head, sockfdlist, | ||
| 534 | sizeof(sockfdlist) / sizeof(*sockfdlist)); | ||
| 535 | freeaddrinfo(ai_head); | ||
| 536 | if (nsockfd <= 0) { | ||
| 537 | err("failed to open a listening socket"); | ||
| 538 | usbip_host_driver_close(); | ||
| 539 | return -1; | ||
| 540 | } | ||
| 541 | |||
| 542 | dbg("listening on %d address%s", nsockfd, (nsockfd == 1) ? "" : "es"); | ||
| 543 | |||
| 544 | fds = calloc(nsockfd, sizeof(struct pollfd)); | ||
| 545 | for (i = 0; i < nsockfd; i++) { | ||
| 546 | fds[i].fd = sockfdlist[i]; | ||
| 547 | fds[i].events = POLLIN; | ||
| 548 | } | ||
| 549 | timeout.tv_sec = MAIN_LOOP_TIMEOUT; | ||
| 550 | timeout.tv_nsec = 0; | ||
| 551 | |||
| 552 | sigfillset(&sigmask); | ||
| 553 | sigdelset(&sigmask, SIGTERM); | ||
| 554 | sigdelset(&sigmask, SIGINT); | ||
| 555 | |||
| 556 | terminate = 0; | ||
| 557 | while (!terminate) { | ||
| 558 | int r; | ||
| 559 | |||
| 560 | r = ppoll(fds, nsockfd, &timeout, &sigmask); | ||
| 561 | if (r < 0) { | ||
| 562 | dbg("%s", strerror(errno)); | ||
| 563 | terminate = 1; | ||
| 564 | } else if (r) { | ||
| 565 | for (i = 0; i < nsockfd; i++) { | ||
| 566 | if (fds[i].revents & POLLIN) { | ||
| 567 | dbg("read event on fd[%d]=%d", | ||
| 568 | i, sockfdlist[i]); | ||
| 569 | process_request(sockfdlist[i]); | ||
| 570 | } | ||
| 571 | } | ||
| 572 | } else { | ||
| 573 | dbg("heartbeat timeout on ppoll()"); | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | info("shutting down " PROGNAME); | ||
| 578 | free(fds); | ||
| 579 | usbip_host_driver_close(); | ||
| 580 | |||
| 581 | return 0; | ||
| 582 | } | ||
| 583 | |||
| 584 | int main(int argc, char *argv[]) | ||
| 585 | { | ||
| 586 | static const struct option longopts[] = { | ||
| 587 | { "ipv4", no_argument, NULL, '4' }, | ||
| 588 | { "ipv6", no_argument, NULL, '6' }, | ||
| 589 | { "daemon", no_argument, NULL, 'D' }, | ||
| 590 | { "daemon", no_argument, NULL, 'D' }, | ||
| 591 | { "debug", no_argument, NULL, 'd' }, | ||
| 592 | { "pid", optional_argument, NULL, 'P' }, | ||
| 593 | { "tcp-port", required_argument, NULL, 't' }, | ||
| 594 | { "help", no_argument, NULL, 'h' }, | ||
| 595 | { "version", no_argument, NULL, 'v' }, | ||
| 596 | { NULL, 0, NULL, 0 } | ||
| 597 | }; | ||
| 598 | |||
| 599 | enum { | ||
| 600 | cmd_standalone_mode = 1, | ||
| 601 | cmd_help, | ||
| 602 | cmd_version | ||
| 603 | } cmd; | ||
| 604 | |||
| 605 | int daemonize = 0; | ||
| 606 | int ipv4 = 0, ipv6 = 0; | ||
| 607 | int opt, rc = -1; | ||
| 608 | |||
| 609 | pid_file = NULL; | ||
| 610 | |||
| 611 | usbip_use_stderr = 1; | ||
| 612 | usbip_use_syslog = 0; | ||
| 613 | |||
| 614 | if (geteuid() != 0) | ||
| 615 | err("not running as root?"); | ||
| 616 | |||
| 617 | cmd = cmd_standalone_mode; | ||
| 618 | for (;;) { | ||
| 619 | opt = getopt_long(argc, argv, "46DdP::t:hv", longopts, NULL); | ||
| 620 | |||
| 621 | if (opt == -1) | ||
| 622 | break; | ||
| 623 | |||
| 624 | switch (opt) { | ||
| 625 | case '4': | ||
| 626 | ipv4 = 1; | ||
| 627 | break; | ||
| 628 | case '6': | ||
| 629 | ipv6 = 1; | ||
| 630 | break; | ||
| 631 | case 'D': | ||
| 632 | daemonize = 1; | ||
| 633 | break; | ||
| 634 | case 'd': | ||
| 635 | usbip_use_debug = 1; | ||
| 636 | break; | ||
| 637 | case 'h': | ||
| 638 | cmd = cmd_help; | ||
| 639 | break; | ||
| 640 | case 'P': | ||
| 641 | pid_file = optarg ? optarg : DEFAULT_PID_FILE; | ||
| 642 | break; | ||
| 643 | case 't': | ||
| 644 | usbip_setup_port_number(optarg); | ||
| 645 | break; | ||
| 646 | case 'v': | ||
| 647 | cmd = cmd_version; | ||
| 648 | break; | ||
| 649 | case '?': | ||
| 650 | usbipd_help(); | ||
| 651 | default: | ||
| 652 | goto err_out; | ||
| 653 | } | ||
| 654 | } | ||
| 655 | |||
| 656 | if (!ipv4 && !ipv6) | ||
| 657 | ipv4 = ipv6 = 1; | ||
| 658 | |||
| 659 | switch (cmd) { | ||
| 660 | case cmd_standalone_mode: | ||
| 661 | rc = do_standalone_mode(daemonize, ipv4, ipv6); | ||
| 662 | remove_pid_file(); | ||
| 663 | break; | ||
| 664 | case cmd_version: | ||
| 665 | printf(PROGNAME " (%s)\n", usbip_version_string); | ||
| 666 | rc = 0; | ||
| 667 | break; | ||
| 668 | case cmd_help: | ||
| 669 | usbipd_help(); | ||
| 670 | rc = 0; | ||
| 671 | break; | ||
| 672 | default: | ||
| 673 | usbipd_help(); | ||
| 674 | goto err_out; | ||
| 675 | } | ||
| 676 | |||
| 677 | err_out: | ||
| 678 | return (rc > -1 ? EXIT_SUCCESS : EXIT_FAILURE); | ||
| 679 | } | ||
diff --git a/tools/usb/usbip/src/utils.c b/tools/usb/usbip/src/utils.c new file mode 100644 index 000000000000..2b3d6d235015 --- /dev/null +++ b/tools/usb/usbip/src/utils.c | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <errno.h> | ||
| 20 | #include <stdio.h> | ||
| 21 | #include <string.h> | ||
| 22 | |||
| 23 | #include "usbip_common.h" | ||
| 24 | #include "utils.h" | ||
| 25 | #include "sysfs_utils.h" | ||
| 26 | |||
| 27 | int modify_match_busid(char *busid, int add) | ||
| 28 | { | ||
| 29 | char attr_name[] = "match_busid"; | ||
| 30 | char command[SYSFS_BUS_ID_SIZE + 4]; | ||
| 31 | char match_busid_attr_path[SYSFS_PATH_MAX]; | ||
| 32 | int rc; | ||
| 33 | |||
| 34 | snprintf(match_busid_attr_path, sizeof(match_busid_attr_path), | ||
| 35 | "%s/%s/%s/%s/%s/%s", SYSFS_MNT_PATH, SYSFS_BUS_NAME, | ||
| 36 | SYSFS_BUS_TYPE, SYSFS_DRIVERS_NAME, USBIP_HOST_DRV_NAME, | ||
| 37 | attr_name); | ||
| 38 | |||
| 39 | if (add) | ||
| 40 | snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s", busid); | ||
| 41 | else | ||
| 42 | snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s", busid); | ||
| 43 | |||
| 44 | rc = write_sysfs_attribute(match_busid_attr_path, command, | ||
| 45 | sizeof(command)); | ||
| 46 | if (rc < 0) { | ||
| 47 | dbg("failed to write match_busid: %s", strerror(errno)); | ||
| 48 | return -1; | ||
| 49 | } | ||
| 50 | |||
| 51 | return 0; | ||
| 52 | } | ||
diff --git a/tools/usb/usbip/src/utils.h b/tools/usb/usbip/src/utils.h new file mode 100644 index 000000000000..5916fd3e02a6 --- /dev/null +++ b/tools/usb/usbip/src/utils.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
| 3 | * 2005-2007 Takahiro Hirofuchi | ||
| 4 | * | ||
| 5 | * This program is free software: you can redistribute it and/or modify | ||
| 6 | * it under the terms of the GNU General Public License as published by | ||
| 7 | * the Free Software Foundation, either version 2 of the License, or | ||
| 8 | * (at your option) any later version. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | * | ||
| 15 | * You should have received a copy of the GNU General Public License | ||
| 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #ifndef __UTILS_H | ||
| 20 | #define __UTILS_H | ||
| 21 | |||
| 22 | int modify_match_busid(char *busid, int add); | ||
| 23 | |||
| 24 | #endif /* __UTILS_H */ | ||
| 25 | |||
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", |
