diff options
author | Zhang, Yanmin <yanmin_zhang@linux.intel.com> | 2010-03-18 10:36:05 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-03-18 11:21:12 -0400 |
commit | d6d901c23a9c4c7361aa901b5b2dda69703dd5e0 (patch) | |
tree | 601fc2cafac552c80b8456c8dd4b9964171552db /tools/perf/builtin-record.c | |
parent | 46be604b5ba738d53e5f5314813a4e7092864baf (diff) |
perf events: Change perf parameter --pid to process-wide collection instead of thread-wide
Parameter --pid (or -p) of perf currently means a thread-wide
collection. For exmaple, if a process whose id is 8888 has 10
threads, 'perf top -p 8888' just collects the main thread
statistics. That's misleading. Users are used to attach a whole
process when debugging a process by gdb. To follow normal usage
style, the patch change --pid to process-wide collection and add
--tid (-t) to mean a thread-wide collection.
Usage example is:
# perf top -p 8888
# perf record -p 8888 -f sleep 10
# perf stat -p 8888 -f sleep 10
Above commands collect the statistics of all threads of process
8888.
Signed-off-by: Zhang Yanmin <yanmin_zhang@linux.intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Avi Kivity <avi@redhat.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Sheng Yang <sheng@linux.intel.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Jes Sorensen <Jes.Sorensen@redhat.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Cc: zhiteng.huang@intel.com
Cc: Zachary Amsden <zamsden@redhat.com>
LKML-Reference: <1268922965-14774-3-git-send-email-acme@infradead.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 260 |
1 files changed, 159 insertions, 101 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index e2b35ad82a77..bb5b23db4239 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -27,7 +27,7 @@ | |||
27 | #include <unistd.h> | 27 | #include <unistd.h> |
28 | #include <sched.h> | 28 | #include <sched.h> |
29 | 29 | ||
30 | static int fd[MAX_NR_CPUS][MAX_COUNTERS]; | 30 | static int *fd[MAX_NR_CPUS][MAX_COUNTERS]; |
31 | 31 | ||
32 | static long default_interval = 0; | 32 | static long default_interval = 0; |
33 | 33 | ||
@@ -43,6 +43,9 @@ static int raw_samples = 0; | |||
43 | static int system_wide = 0; | 43 | static int system_wide = 0; |
44 | static int profile_cpu = -1; | 44 | static int profile_cpu = -1; |
45 | static pid_t target_pid = -1; | 45 | static pid_t target_pid = -1; |
46 | static pid_t target_tid = -1; | ||
47 | static pid_t *all_tids = NULL; | ||
48 | static int thread_num = 0; | ||
46 | static pid_t child_pid = -1; | 49 | static pid_t child_pid = -1; |
47 | static int inherit = 1; | 50 | static int inherit = 1; |
48 | static int force = 0; | 51 | static int force = 0; |
@@ -60,7 +63,7 @@ static struct timeval this_read; | |||
60 | 63 | ||
61 | static u64 bytes_written = 0; | 64 | static u64 bytes_written = 0; |
62 | 65 | ||
63 | static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS]; | 66 | static struct pollfd *event_array; |
64 | 67 | ||
65 | static int nr_poll = 0; | 68 | static int nr_poll = 0; |
66 | static int nr_cpu = 0; | 69 | static int nr_cpu = 0; |
@@ -77,7 +80,7 @@ struct mmap_data { | |||
77 | unsigned int prev; | 80 | unsigned int prev; |
78 | }; | 81 | }; |
79 | 82 | ||
80 | static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | 83 | static struct mmap_data *mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; |
81 | 84 | ||
82 | static unsigned long mmap_read_head(struct mmap_data *md) | 85 | static unsigned long mmap_read_head(struct mmap_data *md) |
83 | { | 86 | { |
@@ -225,12 +228,13 @@ static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int n | |||
225 | return h_attr; | 228 | return h_attr; |
226 | } | 229 | } |
227 | 230 | ||
228 | static void create_counter(int counter, int cpu, pid_t pid) | 231 | static void create_counter(int counter, int cpu) |
229 | { | 232 | { |
230 | char *filter = filters[counter]; | 233 | char *filter = filters[counter]; |
231 | struct perf_event_attr *attr = attrs + counter; | 234 | struct perf_event_attr *attr = attrs + counter; |
232 | struct perf_header_attr *h_attr; | 235 | struct perf_header_attr *h_attr; |
233 | int track = !counter; /* only the first counter needs these */ | 236 | int track = !counter; /* only the first counter needs these */ |
237 | int thread_index; | ||
234 | int ret; | 238 | int ret; |
235 | struct { | 239 | struct { |
236 | u64 count; | 240 | u64 count; |
@@ -280,115 +284,124 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
280 | attr->enable_on_exec = 1; | 284 | attr->enable_on_exec = 1; |
281 | } | 285 | } |
282 | 286 | ||
287 | for (thread_index = 0; thread_index < thread_num; thread_index++) { | ||
283 | try_again: | 288 | try_again: |
284 | fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0); | 289 | fd[nr_cpu][counter][thread_index] = sys_perf_event_open(attr, |
285 | 290 | all_tids[thread_index], cpu, group_fd, 0); | |
286 | if (fd[nr_cpu][counter] < 0) { | 291 | |
287 | int err = errno; | 292 | if (fd[nr_cpu][counter][thread_index] < 0) { |
288 | 293 | int err = errno; | |
289 | if (err == EPERM || err == EACCES) | 294 | |
290 | die("Permission error - are you root?\n" | 295 | if (err == EPERM || err == EACCES) |
291 | "\t Consider tweaking /proc/sys/kernel/perf_event_paranoid.\n"); | 296 | die("Permission error - are you root?\n" |
292 | else if (err == ENODEV && profile_cpu != -1) | 297 | "\t Consider tweaking" |
293 | die("No such device - did you specify an out-of-range profile CPU?\n"); | 298 | " /proc/sys/kernel/perf_event_paranoid.\n"); |
299 | else if (err == ENODEV && profile_cpu != -1) { | ||
300 | die("No such device - did you specify" | ||
301 | " an out-of-range profile CPU?\n"); | ||
302 | } | ||
294 | 303 | ||
295 | /* | 304 | /* |
296 | * If it's cycles then fall back to hrtimer | 305 | * If it's cycles then fall back to hrtimer |
297 | * based cpu-clock-tick sw counter, which | 306 | * based cpu-clock-tick sw counter, which |
298 | * is always available even if no PMU support: | 307 | * is always available even if no PMU support: |
299 | */ | 308 | */ |
300 | if (attr->type == PERF_TYPE_HARDWARE | 309 | if (attr->type == PERF_TYPE_HARDWARE |
301 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | 310 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { |
302 | 311 | ||
303 | if (verbose) | 312 | if (verbose) |
304 | warning(" ... trying to fall back to cpu-clock-ticks\n"); | 313 | warning(" ... trying to fall back to cpu-clock-ticks\n"); |
305 | attr->type = PERF_TYPE_SOFTWARE; | 314 | attr->type = PERF_TYPE_SOFTWARE; |
306 | attr->config = PERF_COUNT_SW_CPU_CLOCK; | 315 | attr->config = PERF_COUNT_SW_CPU_CLOCK; |
307 | goto try_again; | 316 | goto try_again; |
308 | } | 317 | } |
309 | printf("\n"); | 318 | printf("\n"); |
310 | error("perfcounter syscall returned with %d (%s)\n", | 319 | error("perfcounter syscall returned with %d (%s)\n", |
311 | fd[nr_cpu][counter], strerror(err)); | 320 | fd[nr_cpu][counter][thread_index], strerror(err)); |
312 | 321 | ||
313 | #if defined(__i386__) || defined(__x86_64__) | 322 | #if defined(__i386__) || defined(__x86_64__) |
314 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 323 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
315 | die("No hardware sampling interrupt available. No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it.\n"); | 324 | die("No hardware sampling interrupt available." |
325 | " No APIC? If so then you can boot the kernel" | ||
326 | " with the \"lapic\" boot parameter to" | ||
327 | " force-enable it.\n"); | ||
316 | #endif | 328 | #endif |
317 | 329 | ||
318 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 330 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
319 | exit(-1); | 331 | exit(-1); |
320 | } | 332 | } |
321 | 333 | ||
322 | h_attr = get_header_attr(attr, counter); | 334 | h_attr = get_header_attr(attr, counter); |
323 | if (h_attr == NULL) | 335 | if (h_attr == NULL) |
324 | die("nomem\n"); | 336 | die("nomem\n"); |
325 | 337 | ||
326 | if (!file_new) { | 338 | if (!file_new) { |
327 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | 339 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { |
328 | fprintf(stderr, "incompatible append\n"); | 340 | fprintf(stderr, "incompatible append\n"); |
329 | exit(-1); | 341 | exit(-1); |
342 | } | ||
330 | } | 343 | } |
331 | } | ||
332 | 344 | ||
333 | if (read(fd[nr_cpu][counter], &read_data, sizeof(read_data)) == -1) { | 345 | if (read(fd[nr_cpu][counter][thread_index], &read_data, sizeof(read_data)) == -1) { |
334 | perror("Unable to read perf file descriptor\n"); | 346 | perror("Unable to read perf file descriptor\n"); |
335 | exit(-1); | 347 | exit(-1); |
336 | } | 348 | } |
337 | 349 | ||
338 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { | 350 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { |
339 | pr_warning("Not enough memory to add id\n"); | 351 | pr_warning("Not enough memory to add id\n"); |
340 | exit(-1); | 352 | exit(-1); |
341 | } | 353 | } |
342 | 354 | ||
343 | assert(fd[nr_cpu][counter] >= 0); | 355 | assert(fd[nr_cpu][counter][thread_index] >= 0); |
344 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); | 356 | fcntl(fd[nr_cpu][counter][thread_index], F_SETFL, O_NONBLOCK); |
345 | 357 | ||
346 | /* | 358 | /* |
347 | * First counter acts as the group leader: | 359 | * First counter acts as the group leader: |
348 | */ | 360 | */ |
349 | if (group && group_fd == -1) | 361 | if (group && group_fd == -1) |
350 | group_fd = fd[nr_cpu][counter]; | 362 | group_fd = fd[nr_cpu][counter][thread_index]; |
351 | if (multiplex && multiplex_fd == -1) | 363 | if (multiplex && multiplex_fd == -1) |
352 | multiplex_fd = fd[nr_cpu][counter]; | 364 | multiplex_fd = fd[nr_cpu][counter][thread_index]; |
353 | 365 | ||
354 | if (multiplex && fd[nr_cpu][counter] != multiplex_fd) { | 366 | if (multiplex && fd[nr_cpu][counter][thread_index] != multiplex_fd) { |
355 | 367 | ||
356 | ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); | 368 | ret = ioctl(fd[nr_cpu][counter][thread_index], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); |
357 | assert(ret != -1); | 369 | assert(ret != -1); |
358 | } else { | 370 | } else { |
359 | event_array[nr_poll].fd = fd[nr_cpu][counter]; | 371 | event_array[nr_poll].fd = fd[nr_cpu][counter][thread_index]; |
360 | event_array[nr_poll].events = POLLIN; | 372 | event_array[nr_poll].events = POLLIN; |
361 | nr_poll++; | 373 | nr_poll++; |
362 | 374 | ||
363 | mmap_array[nr_cpu][counter].counter = counter; | 375 | mmap_array[nr_cpu][counter][thread_index].counter = counter; |
364 | mmap_array[nr_cpu][counter].prev = 0; | 376 | mmap_array[nr_cpu][counter][thread_index].prev = 0; |
365 | mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1; | 377 | mmap_array[nr_cpu][counter][thread_index].mask = mmap_pages*page_size - 1; |
366 | mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size, | 378 | mmap_array[nr_cpu][counter][thread_index].base = mmap(NULL, (mmap_pages+1)*page_size, |
367 | PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0); | 379 | PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter][thread_index], 0); |
368 | if (mmap_array[nr_cpu][counter].base == MAP_FAILED) { | 380 | if (mmap_array[nr_cpu][counter][thread_index].base == MAP_FAILED) { |
369 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 381 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
370 | exit(-1); | 382 | exit(-1); |
383 | } | ||
371 | } | 384 | } |
372 | } | ||
373 | 385 | ||
374 | if (filter != NULL) { | 386 | if (filter != NULL) { |
375 | ret = ioctl(fd[nr_cpu][counter], | 387 | ret = ioctl(fd[nr_cpu][counter][thread_index], |
376 | PERF_EVENT_IOC_SET_FILTER, filter); | 388 | PERF_EVENT_IOC_SET_FILTER, filter); |
377 | if (ret) { | 389 | if (ret) { |
378 | error("failed to set filter with %d (%s)\n", errno, | 390 | error("failed to set filter with %d (%s)\n", errno, |
379 | strerror(errno)); | 391 | strerror(errno)); |
380 | exit(-1); | 392 | exit(-1); |
393 | } | ||
381 | } | 394 | } |
382 | } | 395 | } |
383 | } | 396 | } |
384 | 397 | ||
385 | static void open_counters(int cpu, pid_t pid) | 398 | static void open_counters(int cpu) |
386 | { | 399 | { |
387 | int counter; | 400 | int counter; |
388 | 401 | ||
389 | group_fd = -1; | 402 | group_fd = -1; |
390 | for (counter = 0; counter < nr_counters; counter++) | 403 | for (counter = 0; counter < nr_counters; counter++) |
391 | create_counter(counter, cpu, pid); | 404 | create_counter(counter, cpu); |
392 | 405 | ||
393 | nr_cpu++; | 406 | nr_cpu++; |
394 | } | 407 | } |
@@ -529,6 +542,9 @@ static int __cmd_record(int argc, const char **argv) | |||
529 | exit(-1); | 542 | exit(-1); |
530 | } | 543 | } |
531 | 544 | ||
545 | if (!system_wide && target_tid == -1 && target_pid == -1) | ||
546 | all_tids[0] = child_pid; | ||
547 | |||
532 | close(child_ready_pipe[1]); | 548 | close(child_ready_pipe[1]); |
533 | close(go_pipe[0]); | 549 | close(go_pipe[0]); |
534 | /* | 550 | /* |
@@ -541,17 +557,12 @@ static int __cmd_record(int argc, const char **argv) | |||
541 | close(child_ready_pipe[0]); | 557 | close(child_ready_pipe[0]); |
542 | } | 558 | } |
543 | 559 | ||
544 | if (forks && target_pid == -1 && !system_wide) | ||
545 | pid = child_pid; | ||
546 | else | ||
547 | pid = target_pid; | ||
548 | |||
549 | if ((!system_wide && !inherit) || profile_cpu != -1) { | 560 | if ((!system_wide && !inherit) || profile_cpu != -1) { |
550 | open_counters(profile_cpu, pid); | 561 | open_counters(profile_cpu); |
551 | } else { | 562 | } else { |
552 | nr_cpus = read_cpu_map(); | 563 | nr_cpus = read_cpu_map(); |
553 | for (i = 0; i < nr_cpus; i++) | 564 | for (i = 0; i < nr_cpus; i++) |
554 | open_counters(cpumap[i], pid); | 565 | open_counters(cpumap[i]); |
555 | } | 566 | } |
556 | 567 | ||
557 | if (file_new) { | 568 | if (file_new) { |
@@ -576,7 +587,7 @@ static int __cmd_record(int argc, const char **argv) | |||
576 | } | 587 | } |
577 | 588 | ||
578 | if (!system_wide && profile_cpu == -1) | 589 | if (!system_wide && profile_cpu == -1) |
579 | event__synthesize_thread(target_pid, process_synthesized_event, | 590 | event__synthesize_thread(target_tid, process_synthesized_event, |
580 | session); | 591 | session); |
581 | else | 592 | else |
582 | event__synthesize_threads(process_synthesized_event, session); | 593 | event__synthesize_threads(process_synthesized_event, session); |
@@ -599,11 +610,16 @@ static int __cmd_record(int argc, const char **argv) | |||
599 | 610 | ||
600 | for (;;) { | 611 | for (;;) { |
601 | int hits = samples; | 612 | int hits = samples; |
613 | int thread; | ||
602 | 614 | ||
603 | for (i = 0; i < nr_cpu; i++) { | 615 | for (i = 0; i < nr_cpu; i++) { |
604 | for (counter = 0; counter < nr_counters; counter++) { | 616 | for (counter = 0; counter < nr_counters; counter++) { |
605 | if (mmap_array[i][counter].base) | 617 | for (thread = 0; |
606 | mmap_read(&mmap_array[i][counter]); | 618 | thread < thread_num; thread++) { |
619 | if (mmap_array[i][counter][thread].base) | ||
620 | mmap_read(&mmap_array[i][counter][thread]); | ||
621 | } | ||
622 | |||
607 | } | 623 | } |
608 | } | 624 | } |
609 | 625 | ||
@@ -616,8 +632,15 @@ static int __cmd_record(int argc, const char **argv) | |||
616 | 632 | ||
617 | if (done) { | 633 | if (done) { |
618 | for (i = 0; i < nr_cpu; i++) { | 634 | for (i = 0; i < nr_cpu; i++) { |
619 | for (counter = 0; counter < nr_counters; counter++) | 635 | for (counter = 0; |
620 | ioctl(fd[i][counter], PERF_EVENT_IOC_DISABLE); | 636 | counter < nr_counters; |
637 | counter++) { | ||
638 | for (thread = 0; | ||
639 | thread < thread_num; | ||
640 | thread++) | ||
641 | ioctl(fd[i][counter][thread], | ||
642 | PERF_EVENT_IOC_DISABLE); | ||
643 | } | ||
621 | } | 644 | } |
622 | } | 645 | } |
623 | } | 646 | } |
@@ -649,7 +672,9 @@ static const struct option options[] = { | |||
649 | OPT_CALLBACK(0, "filter", NULL, "filter", | 672 | OPT_CALLBACK(0, "filter", NULL, "filter", |
650 | "event filter", parse_filter), | 673 | "event filter", parse_filter), |
651 | OPT_INTEGER('p', "pid", &target_pid, | 674 | OPT_INTEGER('p', "pid", &target_pid, |
652 | "record events on existing pid"), | 675 | "record events on existing process id"), |
676 | OPT_INTEGER('t', "tid", &target_tid, | ||
677 | "record events on existing thread id"), | ||
653 | OPT_INTEGER('r', "realtime", &realtime_prio, | 678 | OPT_INTEGER('r', "realtime", &realtime_prio, |
654 | "collect data with this RT SCHED_FIFO priority"), | 679 | "collect data with this RT SCHED_FIFO priority"), |
655 | OPT_BOOLEAN('R', "raw-samples", &raw_samples, | 680 | OPT_BOOLEAN('R', "raw-samples", &raw_samples, |
@@ -690,10 +715,12 @@ static const struct option options[] = { | |||
690 | int cmd_record(int argc, const char **argv, const char *prefix __used) | 715 | int cmd_record(int argc, const char **argv, const char *prefix __used) |
691 | { | 716 | { |
692 | int counter; | 717 | int counter; |
718 | int i,j; | ||
693 | 719 | ||
694 | argc = parse_options(argc, argv, options, record_usage, | 720 | argc = parse_options(argc, argv, options, record_usage, |
695 | PARSE_OPT_STOP_AT_NON_OPTION); | 721 | PARSE_OPT_STOP_AT_NON_OPTION); |
696 | if (!argc && target_pid == -1 && !system_wide && profile_cpu == -1) | 722 | if (!argc && target_pid == -1 && target_tid == -1 && |
723 | !system_wide && profile_cpu == -1) | ||
697 | usage_with_options(record_usage, options); | 724 | usage_with_options(record_usage, options); |
698 | 725 | ||
699 | symbol__init(); | 726 | symbol__init(); |
@@ -704,6 +731,37 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
704 | attrs[0].config = PERF_COUNT_HW_CPU_CYCLES; | 731 | attrs[0].config = PERF_COUNT_HW_CPU_CYCLES; |
705 | } | 732 | } |
706 | 733 | ||
734 | if (target_pid != -1) { | ||
735 | target_tid = target_pid; | ||
736 | thread_num = find_all_tid(target_pid, &all_tids); | ||
737 | if (thread_num <= 0) { | ||
738 | fprintf(stderr, "Can't find all threads of pid %d\n", | ||
739 | target_pid); | ||
740 | usage_with_options(record_usage, options); | ||
741 | } | ||
742 | } else { | ||
743 | all_tids=malloc(sizeof(pid_t)); | ||
744 | if (!all_tids) | ||
745 | return -ENOMEM; | ||
746 | |||
747 | all_tids[0] = target_tid; | ||
748 | thread_num = 1; | ||
749 | } | ||
750 | |||
751 | for (i = 0; i < MAX_NR_CPUS; i++) { | ||
752 | for (j = 0; j < MAX_COUNTERS; j++) { | ||
753 | fd[i][j] = malloc(sizeof(int)*thread_num); | ||
754 | mmap_array[i][j] = malloc( | ||
755 | sizeof(struct mmap_data)*thread_num); | ||
756 | if (!fd[i][j] || !mmap_array[i][j]) | ||
757 | return -ENOMEM; | ||
758 | } | ||
759 | } | ||
760 | event_array = malloc( | ||
761 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); | ||
762 | if (!event_array) | ||
763 | return -ENOMEM; | ||
764 | |||
707 | /* | 765 | /* |
708 | * User specified count overrides default frequency. | 766 | * User specified count overrides default frequency. |
709 | */ | 767 | */ |