[PATCH 3/9] perf, tools, stat: Basic support for TopDown in perf stat
From: Andi Kleen Add basic plumbing for TopDown in perf stat Add a new --topdown options to enable events. When --topdown is specified set up events for all topdown events supported by the kernel. Add topdown-* as a special case to the event parser, as is needed for all events containing -. The actual code to compute the metrics is in follow-on patches. Signed-off-by: Andi Kleen --- tools/perf/Documentation/perf-stat.txt | 8 +++ tools/perf/builtin-stat.c | 124 - tools/perf/util/parse-events.l | 1 + 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 47469ab..86c03e9 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -158,6 +158,14 @@ filter out the startup phase of the program, which is often very different. Print statistics of transactional execution if supported. +--topdown:: + +Print top down level 1 metrics if supported by the CPU. This allows to +determine bottle necks in the CPU pipeline for CPU bound workloads, +by breaking it down into frontend bound, backend bound, bad speculation +and retiring. Specifying the option multiple times shows metrics even +if the don't cross a threshold. + EXAMPLES diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 2590c75..a83f26f 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -93,6 +93,15 @@ static const char * transaction_limited_attrs = { "}" }; +static const char * topdown_attrs[] = { + "topdown-total-slots", + "topdown-fetch-bubbles", + "topdown-slots-retired", + "topdown-recovery-bubbles", + "topdown-slots-issued", + NULL, +}; + static struct perf_evlist *evsel_list; static struct target target = { @@ -105,6 +114,7 @@ static volatile pid_t child_pid = -1; static boolnull_run= false; static int detailed_run= 0; static booltransaction_run; +static int topdown_run = 0; static boolbig_num = true; static int big_num_opt = -1; static const char *csv_sep= NULL; @@ -735,7 +745,8 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, first_shadow_cpu(counter, id), pm, nl, - ); + , + topdown_run); if (!csv_output) { print_noise(counter, noise); @@ -1093,12 +1104,90 @@ static int perf_stat_init_aggr_mode(void) return 0; } +static void filter_events(const char **attr, char **str, bool use_group) +{ + int off = 0; + int i; + int len = 0; + char *s; + + for (i = 0; attr[i]; i++) { + if (pmu_have_event("cpu", attr[i])) { + len += strlen(attr[i]) + 1; + attr[i - off] = attr[i]; + } else + off++; + } + attr[i - off] = NULL; + + *str = malloc(len + 1 + 2); + if (!*str) + return; + s = *str; + if (i - off == 0) { + *s = 0; + return; + } + if (use_group) + *s++ = '{'; + for (i = 0; attr[i]; i++) { + strcpy(s, attr[i]); + s += strlen(s); + *s++ = ','; + } + if (use_group) { + s[-1] = '}'; + *s = 0; + } else + s[-1] = 0; +} + +/* Caller must free result */ +static char *sysctl_read(const char *fn) +{ + int n; + char *line = NULL; + size_t linelen = 0; + FILE *f = fopen(fn, "r"); + if (!f) + return NULL; + n = getline(, , f); + fclose(f); + if (n > 0) + return line; + free(line); + return NULL; +} + +/* + * Check whether we can use a group for top down. + * Without a group may get bad results. + */ +static bool check_group(bool *warn) +{ + char *v = sysctl_read("/proc/sys/kernel/nmi_watchdog"); + int n; + + *warn = false; + if (v) { + bool res = sscanf(v, "%d", ) == 1 && n != 0; + free(v); + if (res) { + *warn = true; + return false; + } + return true; + } + return false; /* Don't know, so don't use group */ +} + /* * Add default attributes, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used:
[PATCH 3/9] perf, tools, stat: Basic support for TopDown in perf stat
From: Andi Kleen a...@linux.intel.com Add basic plumbing for TopDown in perf stat Add a new --topdown options to enable events. When --topdown is specified set up events for all topdown events supported by the kernel. Add topdown-* as a special case to the event parser, as is needed for all events containing -. The actual code to compute the metrics is in follow-on patches. Signed-off-by: Andi Kleen a...@linux.intel.com --- tools/perf/Documentation/perf-stat.txt | 8 +++ tools/perf/builtin-stat.c | 124 - tools/perf/util/parse-events.l | 1 + 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 47469ab..86c03e9 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -158,6 +158,14 @@ filter out the startup phase of the program, which is often very different. Print statistics of transactional execution if supported. +--topdown:: + +Print top down level 1 metrics if supported by the CPU. This allows to +determine bottle necks in the CPU pipeline for CPU bound workloads, +by breaking it down into frontend bound, backend bound, bad speculation +and retiring. Specifying the option multiple times shows metrics even +if the don't cross a threshold. + EXAMPLES diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 2590c75..a83f26f 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -93,6 +93,15 @@ static const char * transaction_limited_attrs = { } }; +static const char * topdown_attrs[] = { + topdown-total-slots, + topdown-fetch-bubbles, + topdown-slots-retired, + topdown-recovery-bubbles, + topdown-slots-issued, + NULL, +}; + static struct perf_evlist *evsel_list; static struct target target = { @@ -105,6 +114,7 @@ static volatile pid_t child_pid = -1; static boolnull_run= false; static int detailed_run= 0; static booltransaction_run; +static int topdown_run = 0; static boolbig_num = true; static int big_num_opt = -1; static const char *csv_sep= NULL; @@ -735,7 +745,8 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval, first_shadow_cpu(counter, id), pm, nl, - os); + os, + topdown_run); if (!csv_output) { print_noise(counter, noise); @@ -1093,12 +1104,90 @@ static int perf_stat_init_aggr_mode(void) return 0; } +static void filter_events(const char **attr, char **str, bool use_group) +{ + int off = 0; + int i; + int len = 0; + char *s; + + for (i = 0; attr[i]; i++) { + if (pmu_have_event(cpu, attr[i])) { + len += strlen(attr[i]) + 1; + attr[i - off] = attr[i]; + } else + off++; + } + attr[i - off] = NULL; + + *str = malloc(len + 1 + 2); + if (!*str) + return; + s = *str; + if (i - off == 0) { + *s = 0; + return; + } + if (use_group) + *s++ = '{'; + for (i = 0; attr[i]; i++) { + strcpy(s, attr[i]); + s += strlen(s); + *s++ = ','; + } + if (use_group) { + s[-1] = '}'; + *s = 0; + } else + s[-1] = 0; +} + +/* Caller must free result */ +static char *sysctl_read(const char *fn) +{ + int n; + char *line = NULL; + size_t linelen = 0; + FILE *f = fopen(fn, r); + if (!f) + return NULL; + n = getline(line, linelen, f); + fclose(f); + if (n 0) + return line; + free(line); + return NULL; +} + +/* + * Check whether we can use a group for top down. + * Without a group may get bad results. + */ +static bool check_group(bool *warn) +{ + char *v = sysctl_read(/proc/sys/kernel/nmi_watchdog); + int n; + + *warn = false; + if (v) { + bool res = sscanf(v, %d, n) == 1 n != 0; + free(v); + if (res) { + *warn = true; + return false; + } + return true; + } + return false; /* Don't know, so don't use group */ +} + /* * Add default attributes, if there were no attributes specified or * if