diff options
Diffstat (limited to 'tools/perf/util/evlist.c')
-rw-r--r-- | tools/perf/util/evlist.c | 299 |
1 files changed, 292 insertions, 7 deletions
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index fbb4b4ab9cc6..fa1837088ca8 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -6,12 +6,16 @@ | |||
6 | * | 6 | * |
7 | * Released under the GPL v2. (and only v2, not any later version) | 7 | * Released under the GPL v2. (and only v2, not any later version) |
8 | */ | 8 | */ |
9 | #include "util.h" | ||
10 | #include "debugfs.h" | ||
9 | #include <poll.h> | 11 | #include <poll.h> |
10 | #include "cpumap.h" | 12 | #include "cpumap.h" |
11 | #include "thread_map.h" | 13 | #include "thread_map.h" |
12 | #include "evlist.h" | 14 | #include "evlist.h" |
13 | #include "evsel.h" | 15 | #include "evsel.h" |
14 | #include "util.h" | 16 | #include <unistd.h> |
17 | |||
18 | #include "parse-events.h" | ||
15 | 19 | ||
16 | #include <sys/mman.h> | 20 | #include <sys/mman.h> |
17 | 21 | ||
@@ -30,6 +34,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | |||
30 | INIT_HLIST_HEAD(&evlist->heads[i]); | 34 | INIT_HLIST_HEAD(&evlist->heads[i]); |
31 | INIT_LIST_HEAD(&evlist->entries); | 35 | INIT_LIST_HEAD(&evlist->entries); |
32 | perf_evlist__set_maps(evlist, cpus, threads); | 36 | perf_evlist__set_maps(evlist, cpus, threads); |
37 | evlist->workload.pid = -1; | ||
33 | } | 38 | } |
34 | 39 | ||
35 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | 40 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, |
@@ -43,6 +48,22 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | |||
43 | return evlist; | 48 | return evlist; |
44 | } | 49 | } |
45 | 50 | ||
51 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | ||
52 | struct perf_record_opts *opts) | ||
53 | { | ||
54 | struct perf_evsel *evsel; | ||
55 | |||
56 | if (evlist->cpus->map[0] < 0) | ||
57 | opts->no_inherit = true; | ||
58 | |||
59 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
60 | perf_evsel__config(evsel, opts); | ||
61 | |||
62 | if (evlist->nr_entries > 1) | ||
63 | evsel->attr.sample_type |= PERF_SAMPLE_ID; | ||
64 | } | ||
65 | } | ||
66 | |||
46 | static void perf_evlist__purge(struct perf_evlist *evlist) | 67 | static void perf_evlist__purge(struct perf_evlist *evlist) |
47 | { | 68 | { |
48 | struct perf_evsel *pos, *n; | 69 | struct perf_evsel *pos, *n; |
@@ -76,6 +97,14 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | |||
76 | ++evlist->nr_entries; | 97 | ++evlist->nr_entries; |
77 | } | 98 | } |
78 | 99 | ||
100 | static void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | ||
101 | struct list_head *list, | ||
102 | int nr_entries) | ||
103 | { | ||
104 | list_splice_tail(list, &evlist->entries); | ||
105 | evlist->nr_entries += nr_entries; | ||
106 | } | ||
107 | |||
79 | int perf_evlist__add_default(struct perf_evlist *evlist) | 108 | int perf_evlist__add_default(struct perf_evlist *evlist) |
80 | { | 109 | { |
81 | struct perf_event_attr attr = { | 110 | struct perf_event_attr attr = { |
@@ -100,6 +129,126 @@ error: | |||
100 | return -ENOMEM; | 129 | return -ENOMEM; |
101 | } | 130 | } |
102 | 131 | ||
132 | int perf_evlist__add_attrs(struct perf_evlist *evlist, | ||
133 | struct perf_event_attr *attrs, size_t nr_attrs) | ||
134 | { | ||
135 | struct perf_evsel *evsel, *n; | ||
136 | LIST_HEAD(head); | ||
137 | size_t i; | ||
138 | |||
139 | for (i = 0; i < nr_attrs; i++) { | ||
140 | evsel = perf_evsel__new(attrs + i, evlist->nr_entries + i); | ||
141 | if (evsel == NULL) | ||
142 | goto out_delete_partial_list; | ||
143 | list_add_tail(&evsel->node, &head); | ||
144 | } | ||
145 | |||
146 | perf_evlist__splice_list_tail(evlist, &head, nr_attrs); | ||
147 | |||
148 | return 0; | ||
149 | |||
150 | out_delete_partial_list: | ||
151 | list_for_each_entry_safe(evsel, n, &head, node) | ||
152 | perf_evsel__delete(evsel); | ||
153 | return -1; | ||
154 | } | ||
155 | |||
156 | static int trace_event__id(const char *evname) | ||
157 | { | ||
158 | char *filename, *colon; | ||
159 | int err = -1, fd; | ||
160 | |||
161 | if (asprintf(&filename, "%s/%s/id", tracing_events_path, evname) < 0) | ||
162 | return -1; | ||
163 | |||
164 | colon = strrchr(filename, ':'); | ||
165 | if (colon != NULL) | ||
166 | *colon = '/'; | ||
167 | |||
168 | fd = open(filename, O_RDONLY); | ||
169 | if (fd >= 0) { | ||
170 | char id[16]; | ||
171 | if (read(fd, id, sizeof(id)) > 0) | ||
172 | err = atoi(id); | ||
173 | close(fd); | ||
174 | } | ||
175 | |||
176 | free(filename); | ||
177 | return err; | ||
178 | } | ||
179 | |||
180 | int perf_evlist__add_tracepoints(struct perf_evlist *evlist, | ||
181 | const char *tracepoints[], | ||
182 | size_t nr_tracepoints) | ||
183 | { | ||
184 | int err; | ||
185 | size_t i; | ||
186 | struct perf_event_attr *attrs = zalloc(nr_tracepoints * sizeof(*attrs)); | ||
187 | |||
188 | if (attrs == NULL) | ||
189 | return -1; | ||
190 | |||
191 | for (i = 0; i < nr_tracepoints; i++) { | ||
192 | err = trace_event__id(tracepoints[i]); | ||
193 | |||
194 | if (err < 0) | ||
195 | goto out_free_attrs; | ||
196 | |||
197 | attrs[i].type = PERF_TYPE_TRACEPOINT; | ||
198 | attrs[i].config = err; | ||
199 | attrs[i].sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | | ||
200 | PERF_SAMPLE_CPU); | ||
201 | attrs[i].sample_period = 1; | ||
202 | } | ||
203 | |||
204 | err = perf_evlist__add_attrs(evlist, attrs, nr_tracepoints); | ||
205 | out_free_attrs: | ||
206 | free(attrs); | ||
207 | return err; | ||
208 | } | ||
209 | |||
210 | static struct perf_evsel * | ||
211 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) | ||
212 | { | ||
213 | struct perf_evsel *evsel; | ||
214 | |||
215 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
216 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT && | ||
217 | (int)evsel->attr.config == id) | ||
218 | return evsel; | ||
219 | } | ||
220 | |||
221 | return NULL; | ||
222 | } | ||
223 | |||
224 | int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist, | ||
225 | const struct perf_evsel_str_handler *assocs, | ||
226 | size_t nr_assocs) | ||
227 | { | ||
228 | struct perf_evsel *evsel; | ||
229 | int err; | ||
230 | size_t i; | ||
231 | |||
232 | for (i = 0; i < nr_assocs; i++) { | ||
233 | err = trace_event__id(assocs[i].name); | ||
234 | if (err < 0) | ||
235 | goto out; | ||
236 | |||
237 | evsel = perf_evlist__find_tracepoint_by_id(evlist, err); | ||
238 | if (evsel == NULL) | ||
239 | continue; | ||
240 | |||
241 | err = -EEXIST; | ||
242 | if (evsel->handler.func != NULL) | ||
243 | goto out; | ||
244 | evsel->handler.func = assocs[i].handler; | ||
245 | } | ||
246 | |||
247 | err = 0; | ||
248 | out: | ||
249 | return err; | ||
250 | } | ||
251 | |||
103 | void perf_evlist__disable(struct perf_evlist *evlist) | 252 | void perf_evlist__disable(struct perf_evlist *evlist) |
104 | { | 253 | { |
105 | int cpu, thread; | 254 | int cpu, thread; |
@@ -126,7 +275,7 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
126 | } | 275 | } |
127 | } | 276 | } |
128 | 277 | ||
129 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 278 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
130 | { | 279 | { |
131 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; | 280 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; |
132 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | 281 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); |
@@ -282,7 +431,7 @@ void perf_evlist__munmap(struct perf_evlist *evlist) | |||
282 | evlist->mmap = NULL; | 431 | evlist->mmap = NULL; |
283 | } | 432 | } |
284 | 433 | ||
285 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | 434 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) |
286 | { | 435 | { |
287 | evlist->nr_mmaps = evlist->cpus->nr; | 436 | evlist->nr_mmaps = evlist->cpus->nr; |
288 | if (evlist->cpus->map[0] == -1) | 437 | if (evlist->cpus->map[0] == -1) |
@@ -298,8 +447,10 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, | |||
298 | evlist->mmap[idx].mask = mask; | 447 | evlist->mmap[idx].mask = mask; |
299 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, | 448 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, |
300 | MAP_SHARED, fd, 0); | 449 | MAP_SHARED, fd, 0); |
301 | if (evlist->mmap[idx].base == MAP_FAILED) | 450 | if (evlist->mmap[idx].base == MAP_FAILED) { |
451 | evlist->mmap[idx].base = NULL; | ||
302 | return -1; | 452 | return -1; |
453 | } | ||
303 | 454 | ||
304 | perf_evlist__add_pollfd(evlist, fd); | 455 | perf_evlist__add_pollfd(evlist, fd); |
305 | return 0; | 456 | return 0; |
@@ -400,14 +551,22 @@ out_unmap: | |||
400 | * | 551 | * |
401 | * Using perf_evlist__read_on_cpu does this automatically. | 552 | * Using perf_evlist__read_on_cpu does this automatically. |
402 | */ | 553 | */ |
403 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite) | 554 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
555 | bool overwrite) | ||
404 | { | 556 | { |
405 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | 557 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); |
406 | int mask = pages * page_size - 1; | ||
407 | struct perf_evsel *evsel; | 558 | struct perf_evsel *evsel; |
408 | const struct cpu_map *cpus = evlist->cpus; | 559 | const struct cpu_map *cpus = evlist->cpus; |
409 | const struct thread_map *threads = evlist->threads; | 560 | const struct thread_map *threads = evlist->threads; |
410 | int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); | 561 | int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask; |
562 | |||
563 | /* 512 kiB: default amount of unprivileged mlocked memory */ | ||
564 | if (pages == UINT_MAX) | ||
565 | pages = (512 * 1024) / page_size; | ||
566 | else if (!is_power_of_2(pages)) | ||
567 | return -EINVAL; | ||
568 | |||
569 | mask = pages * page_size - 1; | ||
411 | 570 | ||
412 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | 571 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) |
413 | return -ENOMEM; | 572 | return -ENOMEM; |
@@ -512,6 +671,38 @@ u64 perf_evlist__sample_type(const struct perf_evlist *evlist) | |||
512 | return first->attr.sample_type; | 671 | return first->attr.sample_type; |
513 | } | 672 | } |
514 | 673 | ||
674 | u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist) | ||
675 | { | ||
676 | struct perf_evsel *first; | ||
677 | struct perf_sample *data; | ||
678 | u64 sample_type; | ||
679 | u16 size = 0; | ||
680 | |||
681 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
682 | |||
683 | if (!first->attr.sample_id_all) | ||
684 | goto out; | ||
685 | |||
686 | sample_type = first->attr.sample_type; | ||
687 | |||
688 | if (sample_type & PERF_SAMPLE_TID) | ||
689 | size += sizeof(data->tid) * 2; | ||
690 | |||
691 | if (sample_type & PERF_SAMPLE_TIME) | ||
692 | size += sizeof(data->time); | ||
693 | |||
694 | if (sample_type & PERF_SAMPLE_ID) | ||
695 | size += sizeof(data->id); | ||
696 | |||
697 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
698 | size += sizeof(data->stream_id); | ||
699 | |||
700 | if (sample_type & PERF_SAMPLE_CPU) | ||
701 | size += sizeof(data->cpu) * 2; | ||
702 | out: | ||
703 | return size; | ||
704 | } | ||
705 | |||
515 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist) | 706 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist) |
516 | { | 707 | { |
517 | struct perf_evsel *pos, *first; | 708 | struct perf_evsel *pos, *first; |
@@ -569,3 +760,97 @@ out_err: | |||
569 | 760 | ||
570 | return err; | 761 | return err; |
571 | } | 762 | } |
763 | |||
764 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | ||
765 | struct perf_record_opts *opts, | ||
766 | const char *argv[]) | ||
767 | { | ||
768 | int child_ready_pipe[2], go_pipe[2]; | ||
769 | char bf; | ||
770 | |||
771 | if (pipe(child_ready_pipe) < 0) { | ||
772 | perror("failed to create 'ready' pipe"); | ||
773 | return -1; | ||
774 | } | ||
775 | |||
776 | if (pipe(go_pipe) < 0) { | ||
777 | perror("failed to create 'go' pipe"); | ||
778 | goto out_close_ready_pipe; | ||
779 | } | ||
780 | |||
781 | evlist->workload.pid = fork(); | ||
782 | if (evlist->workload.pid < 0) { | ||
783 | perror("failed to fork"); | ||
784 | goto out_close_pipes; | ||
785 | } | ||
786 | |||
787 | if (!evlist->workload.pid) { | ||
788 | if (opts->pipe_output) | ||
789 | dup2(2, 1); | ||
790 | |||
791 | close(child_ready_pipe[0]); | ||
792 | close(go_pipe[1]); | ||
793 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | ||
794 | |||
795 | /* | ||
796 | * Do a dummy execvp to get the PLT entry resolved, | ||
797 | * so we avoid the resolver overhead on the real | ||
798 | * execvp call. | ||
799 | */ | ||
800 | execvp("", (char **)argv); | ||
801 | |||
802 | /* | ||
803 | * Tell the parent we're ready to go | ||
804 | */ | ||
805 | close(child_ready_pipe[1]); | ||
806 | |||
807 | /* | ||
808 | * Wait until the parent tells us to go. | ||
809 | */ | ||
810 | if (read(go_pipe[0], &bf, 1) == -1) | ||
811 | perror("unable to read pipe"); | ||
812 | |||
813 | execvp(argv[0], (char **)argv); | ||
814 | |||
815 | perror(argv[0]); | ||
816 | kill(getppid(), SIGUSR1); | ||
817 | exit(-1); | ||
818 | } | ||
819 | |||
820 | if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1) | ||
821 | evlist->threads->map[0] = evlist->workload.pid; | ||
822 | |||
823 | close(child_ready_pipe[1]); | ||
824 | close(go_pipe[0]); | ||
825 | /* | ||
826 | * wait for child to settle | ||
827 | */ | ||
828 | if (read(child_ready_pipe[0], &bf, 1) == -1) { | ||
829 | perror("unable to read pipe"); | ||
830 | goto out_close_pipes; | ||
831 | } | ||
832 | |||
833 | evlist->workload.cork_fd = go_pipe[1]; | ||
834 | close(child_ready_pipe[0]); | ||
835 | return 0; | ||
836 | |||
837 | out_close_pipes: | ||
838 | close(go_pipe[0]); | ||
839 | close(go_pipe[1]); | ||
840 | out_close_ready_pipe: | ||
841 | close(child_ready_pipe[0]); | ||
842 | close(child_ready_pipe[1]); | ||
843 | return -1; | ||
844 | } | ||
845 | |||
846 | int perf_evlist__start_workload(struct perf_evlist *evlist) | ||
847 | { | ||
848 | if (evlist->workload.cork_fd > 0) { | ||
849 | /* | ||
850 | * Remove the cork, let it rip! | ||
851 | */ | ||
852 | return close(evlist->workload.cork_fd); | ||
853 | } | ||
854 | |||
855 | return 0; | ||
856 | } | ||