diff options
author | Ingo Molnar <mingo@elte.hu> | 2011-01-04 02:10:28 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-01-04 02:10:28 -0500 |
commit | 928585536ff5a8f320e60efc60e2b7ef2a5f548d (patch) | |
tree | 5b12281a00ec049e8c35f1fb2810e111b22c90c0 /tools/perf/builtin-record.c | |
parent | cc2221969906a166a638aecdbae84a3d0462719e (diff) | |
parent | d854861c4292a4e675a5d3bfd862c5f7421c81e8 (diff) |
Merge branch 'perf/test' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 152 |
1 files changed, 68 insertions, 84 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 50efbd509b8f..7bc049035484 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -18,6 +18,7 @@ | |||
18 | 18 | ||
19 | #include "util/header.h" | 19 | #include "util/header.h" |
20 | #include "util/event.h" | 20 | #include "util/event.h" |
21 | #include "util/evsel.h" | ||
21 | #include "util/debug.h" | 22 | #include "util/debug.h" |
22 | #include "util/session.h" | 23 | #include "util/session.h" |
23 | #include "util/symbol.h" | 24 | #include "util/symbol.h" |
@@ -27,18 +28,18 @@ | |||
27 | #include <sched.h> | 28 | #include <sched.h> |
28 | #include <sys/mman.h> | 29 | #include <sys/mman.h> |
29 | 30 | ||
31 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
32 | |||
30 | enum write_mode_t { | 33 | enum write_mode_t { |
31 | WRITE_FORCE, | 34 | WRITE_FORCE, |
32 | WRITE_APPEND | 35 | WRITE_APPEND |
33 | }; | 36 | }; |
34 | 37 | ||
35 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; | ||
36 | |||
37 | static u64 user_interval = ULLONG_MAX; | 38 | static u64 user_interval = ULLONG_MAX; |
38 | static u64 default_interval = 0; | 39 | static u64 default_interval = 0; |
39 | static u64 sample_type; | 40 | static u64 sample_type; |
40 | 41 | ||
41 | static int nr_cpus = 0; | 42 | static struct cpu_map *cpus; |
42 | static unsigned int page_size; | 43 | static unsigned int page_size; |
43 | static unsigned int mmap_pages = 128; | 44 | static unsigned int mmap_pages = 128; |
44 | static unsigned int user_freq = UINT_MAX; | 45 | static unsigned int user_freq = UINT_MAX; |
@@ -53,8 +54,7 @@ static bool sample_id_all_avail = true; | |||
53 | static bool system_wide = false; | 54 | static bool system_wide = false; |
54 | static pid_t target_pid = -1; | 55 | static pid_t target_pid = -1; |
55 | static pid_t target_tid = -1; | 56 | static pid_t target_tid = -1; |
56 | static pid_t *all_tids = NULL; | 57 | static struct thread_map *threads; |
57 | static int thread_num = 0; | ||
58 | static pid_t child_pid = -1; | 58 | static pid_t child_pid = -1; |
59 | static bool no_inherit = false; | 59 | static bool no_inherit = false; |
60 | static enum write_mode_t write_mode = WRITE_FORCE; | 60 | static enum write_mode_t write_mode = WRITE_FORCE; |
@@ -81,7 +81,6 @@ static struct perf_session *session; | |||
81 | static const char *cpu_list; | 81 | static const char *cpu_list; |
82 | 82 | ||
83 | struct mmap_data { | 83 | struct mmap_data { |
84 | int counter; | ||
85 | void *base; | 84 | void *base; |
86 | unsigned int mask; | 85 | unsigned int mask; |
87 | unsigned int prev; | 86 | unsigned int prev; |
@@ -229,12 +228,12 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n | |||
229 | return h_attr; | 228 | return h_attr; |
230 | } | 229 | } |
231 | 230 | ||
232 | static void create_counter(int counter, int cpu) | 231 | static void create_counter(struct perf_evsel *evsel, int cpu) |
233 | { | 232 | { |
234 | char *filter = filters[counter]; | 233 | char *filter = evsel->filter; |
235 | struct perf_event_attr *attr = attrs + counter; | 234 | struct perf_event_attr *attr = &evsel->attr; |
236 | struct perf_header_attr *h_attr; | 235 | struct perf_header_attr *h_attr; |
237 | int track = !counter; /* only the first counter needs these */ | 236 | int track = !evsel->idx; /* only the first counter needs these */ |
238 | int thread_index; | 237 | int thread_index; |
239 | int ret; | 238 | int ret; |
240 | struct { | 239 | struct { |
@@ -318,12 +317,11 @@ static void create_counter(int counter, int cpu) | |||
318 | retry_sample_id: | 317 | retry_sample_id: |
319 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | 318 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; |
320 | 319 | ||
321 | for (thread_index = 0; thread_index < thread_num; thread_index++) { | 320 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { |
322 | try_again: | 321 | try_again: |
323 | fd[nr_cpu][counter][thread_index] = sys_perf_event_open(attr, | 322 | FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); |
324 | all_tids[thread_index], cpu, group_fd, 0); | ||
325 | 323 | ||
326 | if (fd[nr_cpu][counter][thread_index] < 0) { | 324 | if (FD(evsel, nr_cpu, thread_index) < 0) { |
327 | int err = errno; | 325 | int err = errno; |
328 | 326 | ||
329 | if (err == EPERM || err == EACCES) | 327 | if (err == EPERM || err == EACCES) |
@@ -360,7 +358,7 @@ try_again: | |||
360 | } | 358 | } |
361 | printf("\n"); | 359 | printf("\n"); |
362 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 360 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", |
363 | fd[nr_cpu][counter][thread_index], strerror(err)); | 361 | FD(evsel, nr_cpu, thread_index), strerror(err)); |
364 | 362 | ||
365 | #if defined(__i386__) || defined(__x86_64__) | 363 | #if defined(__i386__) || defined(__x86_64__) |
366 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 364 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
@@ -374,7 +372,7 @@ try_again: | |||
374 | exit(-1); | 372 | exit(-1); |
375 | } | 373 | } |
376 | 374 | ||
377 | h_attr = get_header_attr(attr, counter); | 375 | h_attr = get_header_attr(attr, evsel->idx); |
378 | if (h_attr == NULL) | 376 | if (h_attr == NULL) |
379 | die("nomem\n"); | 377 | die("nomem\n"); |
380 | 378 | ||
@@ -385,7 +383,7 @@ try_again: | |||
385 | } | 383 | } |
386 | } | 384 | } |
387 | 385 | ||
388 | if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) { | 386 | if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { |
389 | perror("Unable to read perf file descriptor"); | 387 | perror("Unable to read perf file descriptor"); |
390 | exit(-1); | 388 | exit(-1); |
391 | } | 389 | } |
@@ -395,43 +393,44 @@ try_again: | |||
395 | exit(-1); | 393 | exit(-1); |
396 | } | 394 | } |
397 | 395 | ||
398 | assert(fd[nr_cpu][counter][thread_index] >= 0); | 396 | assert(FD(evsel, nr_cpu, thread_index) >= 0); |
399 | fcntl(fd[nr_cpu][counter][thread_index], F_SETFL, O_NONBLOCK); | 397 | fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK); |
400 | 398 | ||
401 | /* | 399 | /* |
402 | * First counter acts as the group leader: | 400 | * First counter acts as the group leader: |
403 | */ | 401 | */ |
404 | if (group && group_fd == -1) | 402 | if (group && group_fd == -1) |
405 | group_fd = fd[nr_cpu][counter][thread_index]; | 403 | group_fd = FD(evsel, nr_cpu, thread_index); |
406 | 404 | ||
407 | if (counter || thread_index) { | 405 | if (evsel->idx || thread_index) { |
408 | ret = ioctl(fd[nr_cpu][counter][thread_index], | 406 | struct perf_evsel *first; |
409 | PERF_EVENT_IOC_SET_OUTPUT, | 407 | first = list_entry(evsel_list.next, struct perf_evsel, node); |
410 | fd[nr_cpu][0][0]); | 408 | ret = ioctl(FD(evsel, nr_cpu, thread_index), |
409 | PERF_EVENT_IOC_SET_OUTPUT, | ||
410 | FD(first, nr_cpu, 0)); | ||
411 | if (ret) { | 411 | if (ret) { |
412 | error("failed to set output: %d (%s)\n", errno, | 412 | error("failed to set output: %d (%s)\n", errno, |
413 | strerror(errno)); | 413 | strerror(errno)); |
414 | exit(-1); | 414 | exit(-1); |
415 | } | 415 | } |
416 | } else { | 416 | } else { |
417 | mmap_array[nr_cpu].counter = counter; | ||
418 | mmap_array[nr_cpu].prev = 0; | 417 | mmap_array[nr_cpu].prev = 0; |
419 | mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; | 418 | mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; |
420 | mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, | 419 | mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, |
421 | PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter][thread_index], 0); | 420 | PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0); |
422 | if (mmap_array[nr_cpu].base == MAP_FAILED) { | 421 | if (mmap_array[nr_cpu].base == MAP_FAILED) { |
423 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 422 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
424 | exit(-1); | 423 | exit(-1); |
425 | } | 424 | } |
426 | 425 | ||
427 | event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index]; | 426 | event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index); |
428 | event_array[nr_poll].events = POLLIN; | 427 | event_array[nr_poll].events = POLLIN; |
429 | nr_poll++; | 428 | nr_poll++; |
430 | } | 429 | } |
431 | 430 | ||
432 | if (filter != NULL) { | 431 | if (filter != NULL) { |
433 | ret = ioctl(fd[nr_cpu][counter][thread_index], | 432 | ret = ioctl(FD(evsel, nr_cpu, thread_index), |
434 | PERF_EVENT_IOC_SET_FILTER, filter); | 433 | PERF_EVENT_IOC_SET_FILTER, filter); |
435 | if (ret) { | 434 | if (ret) { |
436 | error("failed to set filter with %d (%s)\n", errno, | 435 | error("failed to set filter with %d (%s)\n", errno, |
437 | strerror(errno)); | 436 | strerror(errno)); |
@@ -446,11 +445,12 @@ try_again: | |||
446 | 445 | ||
447 | static void open_counters(int cpu) | 446 | static void open_counters(int cpu) |
448 | { | 447 | { |
449 | int counter; | 448 | struct perf_evsel *pos; |
450 | 449 | ||
451 | group_fd = -1; | 450 | group_fd = -1; |
452 | for (counter = 0; counter < nr_counters; counter++) | 451 | |
453 | create_counter(counter, cpu); | 452 | list_for_each_entry(pos, &evsel_list, node) |
453 | create_counter(pos, cpu); | ||
454 | 454 | ||
455 | nr_cpu++; | 455 | nr_cpu++; |
456 | } | 456 | } |
@@ -537,7 +537,7 @@ static void mmap_read_all(void) | |||
537 | 537 | ||
538 | static int __cmd_record(int argc, const char **argv) | 538 | static int __cmd_record(int argc, const char **argv) |
539 | { | 539 | { |
540 | int i, counter; | 540 | int i; |
541 | struct stat st; | 541 | struct stat st; |
542 | int flags; | 542 | int flags; |
543 | int err; | 543 | int err; |
@@ -604,7 +604,7 @@ static int __cmd_record(int argc, const char **argv) | |||
604 | goto out_delete_session; | 604 | goto out_delete_session; |
605 | } | 605 | } |
606 | 606 | ||
607 | if (have_tracepoints(attrs, nr_counters)) | 607 | if (have_tracepoints(&evsel_list)) |
608 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 608 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
609 | 609 | ||
610 | /* | 610 | /* |
@@ -652,7 +652,7 @@ static int __cmd_record(int argc, const char **argv) | |||
652 | } | 652 | } |
653 | 653 | ||
654 | if (!system_wide && target_tid == -1 && target_pid == -1) | 654 | if (!system_wide && target_tid == -1 && target_pid == -1) |
655 | all_tids[0] = child_pid; | 655 | threads->map[0] = child_pid; |
656 | 656 | ||
657 | close(child_ready_pipe[1]); | 657 | close(child_ready_pipe[1]); |
658 | close(go_pipe[0]); | 658 | close(go_pipe[0]); |
@@ -666,17 +666,11 @@ static int __cmd_record(int argc, const char **argv) | |||
666 | close(child_ready_pipe[0]); | 666 | close(child_ready_pipe[0]); |
667 | } | 667 | } |
668 | 668 | ||
669 | nr_cpus = read_cpu_map(cpu_list); | ||
670 | if (nr_cpus < 1) { | ||
671 | perror("failed to collect number of CPUs"); | ||
672 | return -1; | ||
673 | } | ||
674 | |||
675 | if (!system_wide && no_inherit && !cpu_list) { | 669 | if (!system_wide && no_inherit && !cpu_list) { |
676 | open_counters(-1); | 670 | open_counters(-1); |
677 | } else { | 671 | } else { |
678 | for (i = 0; i < nr_cpus; i++) | 672 | for (i = 0; i < cpus->nr; i++) |
679 | open_counters(cpumap[i]); | 673 | open_counters(cpus->map[i]); |
680 | } | 674 | } |
681 | 675 | ||
682 | perf_session__set_sample_type(session, sample_type); | 676 | perf_session__set_sample_type(session, sample_type); |
@@ -711,7 +705,7 @@ static int __cmd_record(int argc, const char **argv) | |||
711 | return err; | 705 | return err; |
712 | } | 706 | } |
713 | 707 | ||
714 | if (have_tracepoints(attrs, nr_counters)) { | 708 | if (have_tracepoints(&evsel_list)) { |
715 | /* | 709 | /* |
716 | * FIXME err <= 0 here actually means that | 710 | * FIXME err <= 0 here actually means that |
717 | * there were no tracepoints so its not really | 711 | * there were no tracepoints so its not really |
@@ -720,8 +714,7 @@ static int __cmd_record(int argc, const char **argv) | |||
720 | * return this more properly and also | 714 | * return this more properly and also |
721 | * propagate errors that now are calling die() | 715 | * propagate errors that now are calling die() |
722 | */ | 716 | */ |
723 | err = event__synthesize_tracing_data(output, attrs, | 717 | err = event__synthesize_tracing_data(output, &evsel_list, |
724 | nr_counters, | ||
725 | process_synthesized_event, | 718 | process_synthesized_event, |
726 | session); | 719 | session); |
727 | if (err <= 0) { | 720 | if (err <= 0) { |
@@ -795,13 +788,13 @@ static int __cmd_record(int argc, const char **argv) | |||
795 | 788 | ||
796 | if (done) { | 789 | if (done) { |
797 | for (i = 0; i < nr_cpu; i++) { | 790 | for (i = 0; i < nr_cpu; i++) { |
798 | for (counter = 0; | 791 | struct perf_evsel *pos; |
799 | counter < nr_counters; | 792 | |
800 | counter++) { | 793 | list_for_each_entry(pos, &evsel_list, node) { |
801 | for (thread = 0; | 794 | for (thread = 0; |
802 | thread < thread_num; | 795 | thread < threads->nr; |
803 | thread++) | 796 | thread++) |
804 | ioctl(fd[i][counter][thread], | 797 | ioctl(FD(pos, i, thread), |
805 | PERF_EVENT_IOC_DISABLE); | 798 | PERF_EVENT_IOC_DISABLE); |
806 | } | 799 | } |
807 | } | 800 | } |
@@ -887,7 +880,8 @@ const struct option record_options[] = { | |||
887 | 880 | ||
888 | int cmd_record(int argc, const char **argv, const char *prefix __used) | 881 | int cmd_record(int argc, const char **argv, const char *prefix __used) |
889 | { | 882 | { |
890 | int i, j, err = -ENOMEM; | 883 | int err = -ENOMEM; |
884 | struct perf_evsel *pos; | ||
891 | 885 | ||
892 | argc = parse_options(argc, argv, record_options, record_usage, | 886 | argc = parse_options(argc, argv, record_options, record_usage, |
893 | PARSE_OPT_STOP_AT_NON_OPTION); | 887 | PARSE_OPT_STOP_AT_NON_OPTION); |
@@ -910,38 +904,32 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
910 | if (no_buildid_cache || no_buildid) | 904 | if (no_buildid_cache || no_buildid) |
911 | disable_buildid_cache(); | 905 | disable_buildid_cache(); |
912 | 906 | ||
913 | if (!nr_counters) { | 907 | if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) { |
914 | nr_counters = 1; | 908 | pr_err("Not enough memory for event selector list\n"); |
915 | attrs[0].type = PERF_TYPE_HARDWARE; | 909 | goto out_symbol_exit; |
916 | attrs[0].config = PERF_COUNT_HW_CPU_CYCLES; | ||
917 | } | 910 | } |
918 | 911 | ||
919 | if (target_pid != -1) { | 912 | if (target_pid != -1) |
920 | target_tid = target_pid; | 913 | target_tid = target_pid; |
921 | thread_num = find_all_tid(target_pid, &all_tids); | ||
922 | if (thread_num <= 0) { | ||
923 | fprintf(stderr, "Can't find all threads of pid %d\n", | ||
924 | target_pid); | ||
925 | usage_with_options(record_usage, record_options); | ||
926 | } | ||
927 | } else { | ||
928 | all_tids=malloc(sizeof(pid_t)); | ||
929 | if (!all_tids) | ||
930 | goto out_symbol_exit; | ||
931 | 914 | ||
932 | all_tids[0] = target_tid; | 915 | threads = thread_map__new(target_pid, target_tid); |
933 | thread_num = 1; | 916 | if (threads == NULL) { |
917 | pr_err("Problems finding threads of monitor\n"); | ||
918 | usage_with_options(record_usage, record_options); | ||
934 | } | 919 | } |
935 | 920 | ||
936 | for (i = 0; i < MAX_NR_CPUS; i++) { | 921 | cpus = cpu_map__new(cpu_list); |
937 | for (j = 0; j < MAX_COUNTERS; j++) { | 922 | if (cpus == NULL) { |
938 | fd[i][j] = malloc(sizeof(int)*thread_num); | 923 | perror("failed to parse CPUs map"); |
939 | if (!fd[i][j]) | 924 | return -1; |
940 | goto out_free_fd; | 925 | } |
941 | } | 926 | |
927 | list_for_each_entry(pos, &evsel_list, node) { | ||
928 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | ||
929 | goto out_free_fd; | ||
942 | } | 930 | } |
943 | event_array = malloc( | 931 | event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS * |
944 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); | 932 | MAX_COUNTERS * threads->nr)); |
945 | if (!event_array) | 933 | if (!event_array) |
946 | goto out_free_fd; | 934 | goto out_free_fd; |
947 | 935 | ||
@@ -968,12 +956,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
968 | out_free_event_array: | 956 | out_free_event_array: |
969 | free(event_array); | 957 | free(event_array); |
970 | out_free_fd: | 958 | out_free_fd: |
971 | for (i = 0; i < MAX_NR_CPUS; i++) { | 959 | thread_map__delete(threads); |
972 | for (j = 0; j < MAX_COUNTERS; j++) | 960 | threads = NULL; |
973 | free(fd[i][j]); | ||
974 | } | ||
975 | free(all_tids); | ||
976 | all_tids = NULL; | ||
977 | out_symbol_exit: | 961 | out_symbol_exit: |
978 | symbol__exit(); | 962 | symbol__exit(); |
979 | return err; | 963 | return err; |