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", |