diff options
author | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-01-12 11:28:51 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2011-01-22 16:56:29 -0500 |
commit | dd7927f4f8ee75b032ff15aeef4bda49719a443a (patch) | |
tree | e839ab6250df6ae192567531d3464c1977ff3e04 | |
parent | 72cb7013e08dec29631e0447f9496b7bacd3e14b (diff) |
perf record: Use perf_evsel__open
Now its time to factor out the mmap handling bits into the perf_evsel
class.
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/builtin-record.c | 233 |
1 files changed, 113 insertions, 120 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 1614d89b4765..ec43f2eb7b72 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -72,8 +72,6 @@ static struct perf_evlist *evsel_list; | |||
72 | static long samples = 0; | 72 | static long samples = 0; |
73 | static u64 bytes_written = 0; | 73 | static u64 bytes_written = 0; |
74 | 74 | ||
75 | static int nr_cpu = 0; | ||
76 | |||
77 | static int file_new = 1; | 75 | static int file_new = 1; |
78 | static off_t post_processing_offset; | 76 | static off_t post_processing_offset; |
79 | 77 | ||
@@ -208,8 +206,6 @@ static void sig_atexit(void) | |||
208 | kill(getpid(), signr); | 206 | kill(getpid(), signr); |
209 | } | 207 | } |
210 | 208 | ||
211 | static int group_fd; | ||
212 | |||
213 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) | 209 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) |
214 | { | 210 | { |
215 | struct perf_header_attr *h_attr; | 211 | struct perf_header_attr *h_attr; |
@@ -234,7 +230,6 @@ static void create_counter(struct perf_evlist *evlist, | |||
234 | char *filter = evsel->filter; | 230 | char *filter = evsel->filter; |
235 | struct perf_event_attr *attr = &evsel->attr; | 231 | struct perf_event_attr *attr = &evsel->attr; |
236 | struct perf_header_attr *h_attr; | 232 | struct perf_header_attr *h_attr; |
237 | int track = !evsel->idx; /* only the first counter needs these */ | ||
238 | int thread_index; | 233 | int thread_index; |
239 | int ret; | 234 | int ret; |
240 | struct { | 235 | struct { |
@@ -243,19 +238,77 @@ static void create_counter(struct perf_evlist *evlist, | |||
243 | u64 time_running; | 238 | u64 time_running; |
244 | u64 id; | 239 | u64 id; |
245 | } read_data; | 240 | } read_data; |
246 | /* | 241 | |
247 | * Check if parse_single_tracepoint_event has already asked for | 242 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { |
248 | * PERF_SAMPLE_TIME. | 243 | h_attr = get_header_attr(attr, evsel->idx); |
249 | * | 244 | if (h_attr == NULL) |
250 | * XXX this is kludgy but short term fix for problems introduced by | 245 | die("nomem\n"); |
251 | * eac23d1c that broke 'perf script' by having different sample_types | 246 | |
252 | * when using multiple tracepoint events when we use a perf binary | 247 | if (!file_new) { |
253 | * that tries to use sample_id_all on an older kernel. | 248 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { |
254 | * | 249 | fprintf(stderr, "incompatible append\n"); |
255 | * We need to move counter creation to perf_session, support | 250 | exit(-1); |
256 | * different sample_types, etc. | 251 | } |
257 | */ | 252 | } |
258 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | 253 | |
254 | if (read(FD(evsel, cpu, thread_index), &read_data, sizeof(read_data)) == -1) { | ||
255 | perror("Unable to read perf file descriptor"); | ||
256 | exit(-1); | ||
257 | } | ||
258 | |||
259 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { | ||
260 | pr_warning("Not enough memory to add id\n"); | ||
261 | exit(-1); | ||
262 | } | ||
263 | |||
264 | assert(FD(evsel, cpu, thread_index) >= 0); | ||
265 | fcntl(FD(evsel, cpu, thread_index), F_SETFL, O_NONBLOCK); | ||
266 | |||
267 | if (evsel->idx || thread_index) { | ||
268 | struct perf_evsel *first; | ||
269 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
270 | ret = ioctl(FD(evsel, cpu, thread_index), | ||
271 | PERF_EVENT_IOC_SET_OUTPUT, | ||
272 | FD(first, cpu, 0)); | ||
273 | if (ret) { | ||
274 | error("failed to set output: %d (%s)\n", errno, | ||
275 | strerror(errno)); | ||
276 | exit(-1); | ||
277 | } | ||
278 | } else { | ||
279 | mmap_array[cpu].prev = 0; | ||
280 | mmap_array[cpu].mask = mmap_pages*page_size - 1; | ||
281 | mmap_array[cpu].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
282 | PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, cpu, thread_index), 0); | ||
283 | if (mmap_array[cpu].base == MAP_FAILED) { | ||
284 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
285 | exit(-1); | ||
286 | } | ||
287 | |||
288 | evlist->pollfd[evlist->nr_fds].fd = FD(evsel, cpu, thread_index); | ||
289 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
290 | evlist->nr_fds++; | ||
291 | } | ||
292 | |||
293 | if (filter != NULL) { | ||
294 | ret = ioctl(FD(evsel, cpu, thread_index), | ||
295 | PERF_EVENT_IOC_SET_FILTER, filter); | ||
296 | if (ret) { | ||
297 | error("failed to set filter with %d (%s)\n", errno, | ||
298 | strerror(errno)); | ||
299 | exit(-1); | ||
300 | } | ||
301 | } | ||
302 | } | ||
303 | |||
304 | if (!sample_type) | ||
305 | sample_type = attr->sample_type; | ||
306 | } | ||
307 | |||
308 | static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) | ||
309 | { | ||
310 | struct perf_event_attr *attr = &evsel->attr; | ||
311 | int track = !evsel->idx; /* only the first counter needs these */ | ||
259 | 312 | ||
260 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 313 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
261 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 314 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
@@ -315,19 +368,39 @@ static void create_counter(struct perf_evlist *evlist, | |||
315 | 368 | ||
316 | attr->mmap = track; | 369 | attr->mmap = track; |
317 | attr->comm = track; | 370 | attr->comm = track; |
318 | attr->inherit = !no_inherit; | 371 | |
319 | if (target_pid == -1 && target_tid == -1 && !system_wide) { | 372 | if (target_pid == -1 && target_tid == -1 && !system_wide) { |
320 | attr->disabled = 1; | 373 | attr->disabled = 1; |
321 | attr->enable_on_exec = 1; | 374 | attr->enable_on_exec = 1; |
322 | } | 375 | } |
323 | retry_sample_id: | 376 | } |
324 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
325 | 377 | ||
326 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { | 378 | static void open_counters(struct perf_evlist *evlist) |
327 | try_again: | 379 | { |
328 | FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); | 380 | struct perf_evsel *pos; |
381 | int cpu; | ||
382 | |||
383 | list_for_each_entry(pos, &evlist->entries, node) { | ||
384 | struct perf_event_attr *attr = &pos->attr; | ||
385 | /* | ||
386 | * Check if parse_single_tracepoint_event has already asked for | ||
387 | * PERF_SAMPLE_TIME. | ||
388 | * | ||
389 | * XXX this is kludgy but short term fix for problems introduced by | ||
390 | * eac23d1c that broke 'perf script' by having different sample_types | ||
391 | * when using multiple tracepoint events when we use a perf binary | ||
392 | * that tries to use sample_id_all on an older kernel. | ||
393 | * | ||
394 | * We need to move counter creation to perf_session, support | ||
395 | * different sample_types, etc. | ||
396 | */ | ||
397 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
329 | 398 | ||
330 | if (FD(evsel, nr_cpu, thread_index) < 0) { | 399 | config_attr(pos, evlist); |
400 | retry_sample_id: | ||
401 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
402 | try_again: | ||
403 | if (perf_evsel__open(pos, cpus, threads, group, !no_inherit) < 0) { | ||
331 | int err = errno; | 404 | int err = errno; |
332 | 405 | ||
333 | if (err == EPERM || err == EACCES) | 406 | if (err == EPERM || err == EACCES) |
@@ -364,7 +437,7 @@ try_again: | |||
364 | } | 437 | } |
365 | printf("\n"); | 438 | printf("\n"); |
366 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 439 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", |
367 | FD(evsel, nr_cpu, thread_index), strerror(err)); | 440 | err, strerror(err)); |
368 | 441 | ||
369 | #if defined(__i386__) || defined(__x86_64__) | 442 | #if defined(__i386__) || defined(__x86_64__) |
370 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 443 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
@@ -375,90 +448,13 @@ try_again: | |||
375 | #endif | 448 | #endif |
376 | 449 | ||
377 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 450 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
378 | exit(-1); | ||
379 | } | ||
380 | |||
381 | h_attr = get_header_attr(attr, evsel->idx); | ||
382 | if (h_attr == NULL) | ||
383 | die("nomem\n"); | ||
384 | |||
385 | if (!file_new) { | ||
386 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | ||
387 | fprintf(stderr, "incompatible append\n"); | ||
388 | exit(-1); | ||
389 | } | ||
390 | } | ||
391 | |||
392 | if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { | ||
393 | perror("Unable to read perf file descriptor"); | ||
394 | exit(-1); | ||
395 | } | ||
396 | |||
397 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { | ||
398 | pr_warning("Not enough memory to add id\n"); | ||
399 | exit(-1); | ||
400 | } | ||
401 | |||
402 | assert(FD(evsel, nr_cpu, thread_index) >= 0); | ||
403 | fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK); | ||
404 | |||
405 | /* | ||
406 | * First counter acts as the group leader: | ||
407 | */ | ||
408 | if (group && group_fd == -1) | ||
409 | group_fd = FD(evsel, nr_cpu, thread_index); | ||
410 | |||
411 | if (evsel->idx || thread_index) { | ||
412 | struct perf_evsel *first; | ||
413 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
414 | ret = ioctl(FD(evsel, nr_cpu, thread_index), | ||
415 | PERF_EVENT_IOC_SET_OUTPUT, | ||
416 | FD(first, nr_cpu, 0)); | ||
417 | if (ret) { | ||
418 | error("failed to set output: %d (%s)\n", errno, | ||
419 | strerror(errno)); | ||
420 | exit(-1); | ||
421 | } | ||
422 | } else { | ||
423 | mmap_array[nr_cpu].prev = 0; | ||
424 | mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; | ||
425 | mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
426 | PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0); | ||
427 | if (mmap_array[nr_cpu].base == MAP_FAILED) { | ||
428 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
429 | exit(-1); | ||
430 | } | ||
431 | |||
432 | evlist->pollfd[evlist->nr_fds].fd = FD(evsel, nr_cpu, thread_index); | ||
433 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
434 | evlist->nr_fds++; | ||
435 | } | ||
436 | |||
437 | if (filter != NULL) { | ||
438 | ret = ioctl(FD(evsel, nr_cpu, thread_index), | ||
439 | PERF_EVENT_IOC_SET_FILTER, filter); | ||
440 | if (ret) { | ||
441 | error("failed to set filter with %d (%s)\n", errno, | ||
442 | strerror(errno)); | ||
443 | exit(-1); | ||
444 | } | ||
445 | } | 451 | } |
446 | } | 452 | } |
447 | 453 | ||
448 | if (!sample_type) | 454 | for (cpu = 0; cpu < cpus->nr; ++cpu) { |
449 | sample_type = attr->sample_type; | 455 | list_for_each_entry(pos, &evlist->entries, node) |
450 | } | 456 | create_counter(evlist, pos, cpu); |
451 | 457 | } | |
452 | static void open_counters(struct perf_evlist *evlist, int cpu) | ||
453 | { | ||
454 | struct perf_evsel *pos; | ||
455 | |||
456 | group_fd = -1; | ||
457 | |||
458 | list_for_each_entry(pos, &evlist->entries, node) | ||
459 | create_counter(evlist, pos, cpu); | ||
460 | |||
461 | nr_cpu++; | ||
462 | } | 458 | } |
463 | 459 | ||
464 | static int process_buildids(void) | 460 | static int process_buildids(void) |
@@ -533,7 +529,7 @@ static void mmap_read_all(void) | |||
533 | { | 529 | { |
534 | int i; | 530 | int i; |
535 | 531 | ||
536 | for (i = 0; i < nr_cpu; i++) { | 532 | for (i = 0; i < cpus->nr; i++) { |
537 | if (mmap_array[i].base) | 533 | if (mmap_array[i].base) |
538 | mmap_read(&mmap_array[i]); | 534 | mmap_read(&mmap_array[i]); |
539 | } | 535 | } |
@@ -673,12 +669,7 @@ static int __cmd_record(int argc, const char **argv) | |||
673 | close(child_ready_pipe[0]); | 669 | close(child_ready_pipe[0]); |
674 | } | 670 | } |
675 | 671 | ||
676 | if (!system_wide && no_inherit && !cpu_list) { | 672 | open_counters(evsel_list); |
677 | open_counters(evsel_list, -1); | ||
678 | } else { | ||
679 | for (i = 0; i < cpus->nr; i++) | ||
680 | open_counters(evsel_list, cpus->map[i]); | ||
681 | } | ||
682 | 673 | ||
683 | perf_session__set_sample_type(session, sample_type); | 674 | perf_session__set_sample_type(session, sample_type); |
684 | 675 | ||
@@ -795,7 +786,7 @@ static int __cmd_record(int argc, const char **argv) | |||
795 | } | 786 | } |
796 | 787 | ||
797 | if (done) { | 788 | if (done) { |
798 | for (i = 0; i < nr_cpu; i++) { | 789 | for (i = 0; i < cpus->nr; i++) { |
799 | struct perf_evsel *pos; | 790 | struct perf_evsel *pos; |
800 | 791 | ||
801 | list_for_each_entry(pos, &evsel_list->entries, node) { | 792 | list_for_each_entry(pos, &evsel_list->entries, node) { |
@@ -933,11 +924,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
933 | usage_with_options(record_usage, record_options); | 924 | usage_with_options(record_usage, record_options); |
934 | } | 925 | } |
935 | 926 | ||
936 | cpus = cpu_map__new(cpu_list); | 927 | if (target_tid != -1) |
937 | if (cpus == NULL) { | 928 | cpus = cpu_map__dummy_new(); |
938 | perror("failed to parse CPUs map"); | 929 | else |
939 | return -1; | 930 | cpus = cpu_map__new(cpu_list); |
940 | } | 931 | |
932 | if (cpus == NULL) | ||
933 | usage_with_options(record_usage, record_options); | ||
941 | 934 | ||
942 | list_for_each_entry(pos, &evsel_list->entries, node) { | 935 | list_for_each_entry(pos, &evsel_list->entries, node) { |
943 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | 936 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) |