diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-08-10 14:48:51 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-08-10 14:48:51 -0400 |
| commit | d00aa6695b67a31be2ce5f7464da32c20cb50699 (patch) | |
| tree | 4e4a2bbd1ab710ddca3bd1a611a6c3e9a00f52f9 /tools/perf/builtin-report.c | |
| parent | cec36911b5fa4ac342f6de856b12a9f71f84e6e5 (diff) | |
| parent | 1853db0e02ae4088f102b0d8e59e83dc98f93f03 (diff) | |
Merge branch 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'perfcounters-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (27 commits)
perf_counter: Zero dead bytes from ftrace raw samples size alignment
perf_counter: Subtract the buffer size field from the event record size
perf_counter: Require CAP_SYS_ADMIN for raw tracepoint data
perf_counter: Correct PERF_SAMPLE_RAW output
perf tools: callchain: Fix bad rounding of minimum rate
perf_counter tools: Fix libbfd detection for systems with libz dependency
perf: "Longum est iter per praecepta, breve et efficax per exempla"
perf_counter: Fix a race on perf_counter_ctx
perf_counter: Fix tracepoint sampling to be part of generic sampling
perf_counter: Work around gcc warning by initializing tracepoint record unconditionally
perf tools: callchain: Fix sum of percentages to be 100% by displaying amount of ignored chains in fractal mode
perf tools: callchain: Fix 'perf report' display to be callchain by default
perf tools: callchain: Fix spurious 'perf report' warnings: ignore empty callchains
perf record: Fix the -A UI for empty or non-existent perf.data
perf util: Fix do_read() to fail on EOF instead of busy-looping
perf list: Fix the output to not include tracepoints without an id
perf_counter/powerpc: Fix oops on cpus without perf_counter hardware support
perf stat: Fix tool option consistency: rename -S/--scale to -c/--scale
perf report: Add debug help for the finding of symbol bugs - show the symtab origin (DSO, build-id, kernel, etc)
perf report: Fix per task mult-counter stat reporting
...
Diffstat (limited to 'tools/perf/builtin-report.c')
| -rw-r--r-- | tools/perf/builtin-report.c | 99 |
1 files changed, 89 insertions, 10 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8cb58d68a006..99274cec0adb 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -68,7 +68,7 @@ static int callchain; | |||
| 68 | 68 | ||
| 69 | static | 69 | static |
| 70 | struct callchain_param callchain_param = { | 70 | struct callchain_param callchain_param = { |
| 71 | .mode = CHAIN_GRAPH_ABS, | 71 | .mode = CHAIN_GRAPH_REL, |
| 72 | .min_percent = 0.5 | 72 | .min_percent = 0.5 |
| 73 | }; | 73 | }; |
| 74 | 74 | ||
| @@ -112,7 +112,9 @@ struct read_event { | |||
| 112 | struct perf_event_header header; | 112 | struct perf_event_header header; |
| 113 | u32 pid,tid; | 113 | u32 pid,tid; |
| 114 | u64 value; | 114 | u64 value; |
| 115 | u64 format[3]; | 115 | u64 time_enabled; |
| 116 | u64 time_running; | ||
| 117 | u64 id; | ||
| 116 | }; | 118 | }; |
| 117 | 119 | ||
| 118 | typedef union event_union { | 120 | typedef union event_union { |
| @@ -698,7 +700,8 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | |||
| 698 | size_t ret = 0; | 700 | size_t ret = 0; |
| 699 | 701 | ||
| 700 | if (verbose) | 702 | if (verbose) |
| 701 | ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip); | 703 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, |
| 704 | dso__symtab_origin(self->dso)); | ||
| 702 | 705 | ||
| 703 | ret += repsep_fprintf(fp, "[%c] ", self->level); | 706 | ret += repsep_fprintf(fp, "[%c] ", self->level); |
| 704 | if (self->sym) { | 707 | if (self->sym) { |
| @@ -888,6 +891,21 @@ ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, | |||
| 888 | return ret; | 891 | return ret; |
| 889 | } | 892 | } |
| 890 | 893 | ||
| 894 | static struct symbol *rem_sq_bracket; | ||
| 895 | static struct callchain_list rem_hits; | ||
| 896 | |||
| 897 | static void init_rem_hits(void) | ||
| 898 | { | ||
| 899 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | ||
| 900 | if (!rem_sq_bracket) { | ||
| 901 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | ||
| 902 | return; | ||
| 903 | } | ||
| 904 | |||
| 905 | strcpy(rem_sq_bracket->name, "[...]"); | ||
| 906 | rem_hits.sym = rem_sq_bracket; | ||
| 907 | } | ||
| 908 | |||
| 891 | static size_t | 909 | static size_t |
| 892 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 910 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, |
| 893 | u64 total_samples, int depth, int depth_mask) | 911 | u64 total_samples, int depth, int depth_mask) |
| @@ -897,25 +915,34 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 897 | struct callchain_list *chain; | 915 | struct callchain_list *chain; |
| 898 | int new_depth_mask = depth_mask; | 916 | int new_depth_mask = depth_mask; |
| 899 | u64 new_total; | 917 | u64 new_total; |
| 918 | u64 remaining; | ||
| 900 | size_t ret = 0; | 919 | size_t ret = 0; |
| 901 | int i; | 920 | int i; |
| 902 | 921 | ||
| 903 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 922 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
| 904 | new_total = self->cumul_hit; | 923 | new_total = self->children_hit; |
| 905 | else | 924 | else |
| 906 | new_total = total_samples; | 925 | new_total = total_samples; |
| 907 | 926 | ||
| 927 | remaining = new_total; | ||
| 928 | |||
| 908 | node = rb_first(&self->rb_root); | 929 | node = rb_first(&self->rb_root); |
| 909 | while (node) { | 930 | while (node) { |
| 931 | u64 cumul; | ||
| 932 | |||
| 910 | child = rb_entry(node, struct callchain_node, rb_node); | 933 | child = rb_entry(node, struct callchain_node, rb_node); |
| 934 | cumul = cumul_hits(child); | ||
| 935 | remaining -= cumul; | ||
| 911 | 936 | ||
| 912 | /* | 937 | /* |
| 913 | * The depth mask manages the output of pipes that show | 938 | * The depth mask manages the output of pipes that show |
| 914 | * the depth. We don't want to keep the pipes of the current | 939 | * the depth. We don't want to keep the pipes of the current |
| 915 | * level for the last child of this depth | 940 | * level for the last child of this depth. |
| 941 | * Except if we have remaining filtered hits. They will | ||
| 942 | * supersede the last child | ||
| 916 | */ | 943 | */ |
| 917 | next = rb_next(node); | 944 | next = rb_next(node); |
| 918 | if (!next) | 945 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) |
| 919 | new_depth_mask &= ~(1 << (depth - 1)); | 946 | new_depth_mask &= ~(1 << (depth - 1)); |
| 920 | 947 | ||
| 921 | /* | 948 | /* |
| @@ -930,7 +957,7 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 930 | ret += ipchain__fprintf_graph(fp, chain, depth, | 957 | ret += ipchain__fprintf_graph(fp, chain, depth, |
| 931 | new_depth_mask, i++, | 958 | new_depth_mask, i++, |
| 932 | new_total, | 959 | new_total, |
| 933 | child->cumul_hit); | 960 | cumul); |
| 934 | } | 961 | } |
| 935 | ret += callchain__fprintf_graph(fp, child, new_total, | 962 | ret += callchain__fprintf_graph(fp, child, new_total, |
| 936 | depth + 1, | 963 | depth + 1, |
| @@ -938,6 +965,19 @@ callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 938 | node = next; | 965 | node = next; |
| 939 | } | 966 | } |
| 940 | 967 | ||
| 968 | if (callchain_param.mode == CHAIN_GRAPH_REL && | ||
| 969 | remaining && remaining != new_total) { | ||
| 970 | |||
| 971 | if (!rem_sq_bracket) | ||
| 972 | return ret; | ||
| 973 | |||
| 974 | new_depth_mask &= ~(1 << (depth - 1)); | ||
| 975 | |||
| 976 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | ||
| 977 | new_depth_mask, 0, new_total, | ||
| 978 | remaining); | ||
| 979 | } | ||
| 980 | |||
| 941 | return ret; | 981 | return ret; |
| 942 | } | 982 | } |
| 943 | 983 | ||
| @@ -1358,6 +1398,8 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
| 1358 | unsigned int width; | 1398 | unsigned int width; |
| 1359 | char *col_width = col_width_list_str; | 1399 | char *col_width = col_width_list_str; |
| 1360 | 1400 | ||
| 1401 | init_rem_hits(); | ||
| 1402 | |||
| 1361 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); | 1403 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
| 1362 | fprintf(fp, "#\n"); | 1404 | fprintf(fp, "#\n"); |
| 1363 | 1405 | ||
| @@ -1429,6 +1471,8 @@ print_entries: | |||
| 1429 | } | 1471 | } |
| 1430 | fprintf(fp, "\n"); | 1472 | fprintf(fp, "\n"); |
| 1431 | 1473 | ||
| 1474 | free(rem_sq_bracket); | ||
| 1475 | |||
| 1432 | return ret; | 1476 | return ret; |
| 1433 | } | 1477 | } |
| 1434 | 1478 | ||
| @@ -1690,14 +1734,37 @@ static void trace_event(event_t *event) | |||
| 1690 | dprintf(".\n"); | 1734 | dprintf(".\n"); |
| 1691 | } | 1735 | } |
| 1692 | 1736 | ||
| 1737 | static struct perf_header *header; | ||
| 1738 | |||
| 1739 | static struct perf_counter_attr *perf_header__find_attr(u64 id) | ||
| 1740 | { | ||
| 1741 | int i; | ||
| 1742 | |||
| 1743 | for (i = 0; i < header->attrs; i++) { | ||
| 1744 | struct perf_header_attr *attr = header->attr[i]; | ||
| 1745 | int j; | ||
| 1746 | |||
| 1747 | for (j = 0; j < attr->ids; j++) { | ||
| 1748 | if (attr->id[j] == id) | ||
| 1749 | return &attr->attr; | ||
| 1750 | } | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | return NULL; | ||
| 1754 | } | ||
| 1755 | |||
| 1693 | static int | 1756 | static int |
| 1694 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | 1757 | process_read_event(event_t *event, unsigned long offset, unsigned long head) |
| 1695 | { | 1758 | { |
| 1696 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %Lu\n", | 1759 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); |
| 1760 | |||
| 1761 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", | ||
| 1697 | (void *)(offset + head), | 1762 | (void *)(offset + head), |
| 1698 | (void *)(long)(event->header.size), | 1763 | (void *)(long)(event->header.size), |
| 1699 | event->read.pid, | 1764 | event->read.pid, |
| 1700 | event->read.tid, | 1765 | event->read.tid, |
| 1766 | attr ? __event_name(attr->type, attr->config) | ||
| 1767 | : "FAIL", | ||
| 1701 | event->read.value); | 1768 | event->read.value); |
| 1702 | 1769 | ||
| 1703 | return 0; | 1770 | return 0; |
| @@ -1743,8 +1810,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1743 | return 0; | 1810 | return 0; |
| 1744 | } | 1811 | } |
| 1745 | 1812 | ||
| 1746 | static struct perf_header *header; | ||
| 1747 | |||
| 1748 | static u64 perf_header__sample_type(void) | 1813 | static u64 perf_header__sample_type(void) |
| 1749 | { | 1814 | { |
| 1750 | u64 sample_type = 0; | 1815 | u64 sample_type = 0; |
| @@ -1812,6 +1877,13 @@ static int __cmd_report(void) | |||
| 1812 | " -g?\n"); | 1877 | " -g?\n"); |
| 1813 | exit(-1); | 1878 | exit(-1); |
| 1814 | } | 1879 | } |
| 1880 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { | ||
| 1881 | callchain = 1; | ||
| 1882 | if (register_callchain_param(&callchain_param) < 0) { | ||
| 1883 | fprintf(stderr, "Can't register callchain" | ||
| 1884 | " params\n"); | ||
| 1885 | exit(-1); | ||
| 1886 | } | ||
| 1815 | } | 1887 | } |
| 1816 | 1888 | ||
| 1817 | if (load_kernel() < 0) { | 1889 | if (load_kernel() < 0) { |
| @@ -1950,6 +2022,13 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
| 1950 | else if (!strncmp(tok, "fractal", strlen(arg))) | 2022 | else if (!strncmp(tok, "fractal", strlen(arg))) |
| 1951 | callchain_param.mode = CHAIN_GRAPH_REL; | 2023 | callchain_param.mode = CHAIN_GRAPH_REL; |
| 1952 | 2024 | ||
| 2025 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
| 2026 | callchain_param.mode = CHAIN_NONE; | ||
| 2027 | callchain = 0; | ||
| 2028 | |||
| 2029 | return 0; | ||
| 2030 | } | ||
| 2031 | |||
| 1953 | else | 2032 | else |
| 1954 | return -1; | 2033 | return -1; |
| 1955 | 2034 | ||
