diff options
author | Ingo Molnar <mingo@kernel.org> | 2018-06-14 02:11:29 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-06-14 02:11:29 -0400 |
commit | a89d0c2acff952935cb37d501eaa702db5a63a05 (patch) | |
tree | 7f7b743635f9562137fa6384b58b81a6f634e977 | |
parent | d82991a8688ad128b46db1b42d5d84396487a508 (diff) | |
parent | fad76d4333fe73cf3f73704aa34d4ce523b1c458 (diff) |
Merge tag 'perf-urgent-for-mingo-4.18-20180611' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent
Pull perf/urgent fixes and improvements from Arnaldo Carvalho de Melo:
perf stat:
- Fix metric column header display alignment (Jiri Olsa)
- Improve error messages for default attributes, providing better output
for error in command lines such as:
$ perf stat -T
Cannot set up transaction events
event syntax error: '..cycles,cpu/cycles-t/,cpu/tx-start/,cpu/el-start/,cpu/cycles-ct/}'
\___ unknown term
Where the "event syntax error" line now appears (Jiri Olsa)
- Add --interval-clear option, to provide a 'watch' like printing (Jiri Olsa)
perf script:
- Show hw-cache events too (Seeteena Thoufeek)
perf c2c:
- Fix data dependency problem in layout of 'struct c2c_hist_entry', where
its member 'struct hist_entry' must be at the end because it has a ZLA
as its last member, that gets space when handling callchains (Jiri Olsa)
Core:
- We cannot assume that a 'struct perf_evsel' is to be obtained from a
container_of operation on a 'struct hists' as there are tools, such as
'perf c2c' that uses 'struct hist' instances without having them in
container structs that also have 'struct perf_evsel' in a particular
layout, so provide a different way of figuring out if a 'struct hists'
and 'struct hist_entry' have callchains (Arnaldo Carvalho de Melo)
- Fix error index in the PMU event parser, so that error messages can
point to the problematic token (Jiri Olsa)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/Documentation/perf-stat.txt | 3 | ||||
-rw-r--r-- | tools/perf/builtin-c2c.c | 10 | ||||
-rw-r--r-- | tools/perf/builtin-script.c | 12 | ||||
-rw-r--r-- | tools/perf/builtin-stat.c | 48 | ||||
-rw-r--r-- | tools/perf/ui/gtk/hists.c | 2 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 12 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 4 | ||||
-rw-r--r-- | tools/perf/util/parse-events.y | 5 | ||||
-rw-r--r-- | tools/perf/util/sort.h | 4 |
9 files changed, 67 insertions, 33 deletions
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 5dfe102fb5b5..b10a90b6a718 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -178,6 +178,9 @@ Print count deltas for fixed number of times. | |||
178 | This option should be used together with "-I" option. | 178 | This option should be used together with "-I" option. |
179 | example: 'perf stat -I 1000 --interval-count 2 -e cycles -a' | 179 | example: 'perf stat -I 1000 --interval-count 2 -e cycles -a' |
180 | 180 | ||
181 | --interval-clear:: | ||
182 | Clear the screen before next interval. | ||
183 | |||
181 | --timeout msecs:: | 184 | --timeout msecs:: |
182 | Stop the 'perf stat' session and print count deltas after N milliseconds (minimum: 10 ms). | 185 | Stop the 'perf stat' session and print count deltas after N milliseconds (minimum: 10 ms). |
183 | This option is not supported with the "-I" option. | 186 | This option is not supported with the "-I" option. |
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 307b3594525f..6a8738f7ead3 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c | |||
@@ -56,16 +56,16 @@ struct c2c_hist_entry { | |||
56 | 56 | ||
57 | struct compute_stats cstats; | 57 | struct compute_stats cstats; |
58 | 58 | ||
59 | unsigned long paddr; | ||
60 | unsigned long paddr_cnt; | ||
61 | bool paddr_zero; | ||
62 | char *nodestr; | ||
63 | |||
59 | /* | 64 | /* |
60 | * must be at the end, | 65 | * must be at the end, |
61 | * because of its callchain dynamic entry | 66 | * because of its callchain dynamic entry |
62 | */ | 67 | */ |
63 | struct hist_entry he; | 68 | struct hist_entry he; |
64 | |||
65 | unsigned long paddr; | ||
66 | unsigned long paddr_cnt; | ||
67 | bool paddr_zero; | ||
68 | char *nodestr; | ||
69 | }; | 69 | }; |
70 | 70 | ||
71 | static char const *coalesce_default = "pid,iaddr"; | 71 | static char const *coalesce_default = "pid,iaddr"; |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b3bf35512d21..a31d7082188e 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -180,6 +180,18 @@ static struct { | |||
180 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE | 180 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE |
181 | }, | 181 | }, |
182 | 182 | ||
183 | [PERF_TYPE_HW_CACHE] = { | ||
184 | .user_set = false, | ||
185 | |||
186 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | ||
187 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | ||
188 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | ||
189 | PERF_OUTPUT_SYM | PERF_OUTPUT_SYMOFFSET | | ||
190 | PERF_OUTPUT_DSO | PERF_OUTPUT_PERIOD, | ||
191 | |||
192 | .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, | ||
193 | }, | ||
194 | |||
183 | [PERF_TYPE_RAW] = { | 195 | [PERF_TYPE_RAW] = { |
184 | .user_set = false, | 196 | .user_set = false, |
185 | 197 | ||
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 096ccb25c11f..22547a490e1f 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -65,6 +65,7 @@ | |||
65 | #include "util/tool.h" | 65 | #include "util/tool.h" |
66 | #include "util/string2.h" | 66 | #include "util/string2.h" |
67 | #include "util/metricgroup.h" | 67 | #include "util/metricgroup.h" |
68 | #include "util/top.h" | ||
68 | #include "asm/bug.h" | 69 | #include "asm/bug.h" |
69 | 70 | ||
70 | #include <linux/time64.h> | 71 | #include <linux/time64.h> |
@@ -144,6 +145,8 @@ static struct target target = { | |||
144 | 145 | ||
145 | typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu); | 146 | typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu); |
146 | 147 | ||
148 | #define METRIC_ONLY_LEN 20 | ||
149 | |||
147 | static int run_count = 1; | 150 | static int run_count = 1; |
148 | static bool no_inherit = false; | 151 | static bool no_inherit = false; |
149 | static volatile pid_t child_pid = -1; | 152 | static volatile pid_t child_pid = -1; |
@@ -173,6 +176,7 @@ static struct cpu_map *aggr_map; | |||
173 | static aggr_get_id_t aggr_get_id; | 176 | static aggr_get_id_t aggr_get_id; |
174 | static bool append_file; | 177 | static bool append_file; |
175 | static bool interval_count; | 178 | static bool interval_count; |
179 | static bool interval_clear; | ||
176 | static const char *output_name; | 180 | static const char *output_name; |
177 | static int output_fd; | 181 | static int output_fd; |
178 | static int print_free_counters_hint; | 182 | static int print_free_counters_hint; |
@@ -180,6 +184,7 @@ static int print_mixed_hw_group_error; | |||
180 | static u64 *walltime_run; | 184 | static u64 *walltime_run; |
181 | static bool ru_display = false; | 185 | static bool ru_display = false; |
182 | static struct rusage ru_data; | 186 | static struct rusage ru_data; |
187 | static unsigned int metric_only_len = METRIC_ONLY_LEN; | ||
183 | 188 | ||
184 | struct perf_stat { | 189 | struct perf_stat { |
185 | bool record; | 190 | bool record; |
@@ -967,8 +972,6 @@ static void print_metric_csv(void *ctx, | |||
967 | fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); | 972 | fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); |
968 | } | 973 | } |
969 | 974 | ||
970 | #define METRIC_ONLY_LEN 20 | ||
971 | |||
972 | /* Filter out some columns that don't work well in metrics only mode */ | 975 | /* Filter out some columns that don't work well in metrics only mode */ |
973 | 976 | ||
974 | static bool valid_only_metric(const char *unit) | 977 | static bool valid_only_metric(const char *unit) |
@@ -999,22 +1002,20 @@ static void print_metric_only(void *ctx, const char *color, const char *fmt, | |||
999 | { | 1002 | { |
1000 | struct outstate *os = ctx; | 1003 | struct outstate *os = ctx; |
1001 | FILE *out = os->fh; | 1004 | FILE *out = os->fh; |
1002 | int n; | 1005 | char buf[1024], str[1024]; |
1003 | char buf[1024]; | 1006 | unsigned mlen = metric_only_len; |
1004 | unsigned mlen = METRIC_ONLY_LEN; | ||
1005 | 1007 | ||
1006 | if (!valid_only_metric(unit)) | 1008 | if (!valid_only_metric(unit)) |
1007 | return; | 1009 | return; |
1008 | unit = fixunit(buf, os->evsel, unit); | 1010 | unit = fixunit(buf, os->evsel, unit); |
1009 | if (color) | ||
1010 | n = color_fprintf(out, color, fmt, val); | ||
1011 | else | ||
1012 | n = fprintf(out, fmt, val); | ||
1013 | if (n > METRIC_ONLY_LEN) | ||
1014 | n = METRIC_ONLY_LEN; | ||
1015 | if (mlen < strlen(unit)) | 1011 | if (mlen < strlen(unit)) |
1016 | mlen = strlen(unit) + 1; | 1012 | mlen = strlen(unit) + 1; |
1017 | fprintf(out, "%*s", mlen - n, ""); | 1013 | |
1014 | if (color) | ||
1015 | mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1; | ||
1016 | |||
1017 | color_snprintf(str, sizeof(str), color ?: "", fmt, val); | ||
1018 | fprintf(out, "%*s ", mlen, str); | ||
1018 | } | 1019 | } |
1019 | 1020 | ||
1020 | static void print_metric_only_csv(void *ctx, const char *color __maybe_unused, | 1021 | static void print_metric_only_csv(void *ctx, const char *color __maybe_unused, |
@@ -1054,7 +1055,7 @@ static void print_metric_header(void *ctx, const char *color __maybe_unused, | |||
1054 | if (csv_output) | 1055 | if (csv_output) |
1055 | fprintf(os->fh, "%s%s", unit, csv_sep); | 1056 | fprintf(os->fh, "%s%s", unit, csv_sep); |
1056 | else | 1057 | else |
1057 | fprintf(os->fh, "%-*s ", METRIC_ONLY_LEN, unit); | 1058 | fprintf(os->fh, "%*s ", metric_only_len, unit); |
1058 | } | 1059 | } |
1059 | 1060 | ||
1060 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) | 1061 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
@@ -1704,9 +1705,12 @@ static void print_interval(char *prefix, struct timespec *ts) | |||
1704 | FILE *output = stat_config.output; | 1705 | FILE *output = stat_config.output; |
1705 | static int num_print_interval; | 1706 | static int num_print_interval; |
1706 | 1707 | ||
1708 | if (interval_clear) | ||
1709 | puts(CONSOLE_CLEAR); | ||
1710 | |||
1707 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); | 1711 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); |
1708 | 1712 | ||
1709 | if (num_print_interval == 0 && !csv_output) { | 1713 | if ((num_print_interval == 0 && !csv_output) || interval_clear) { |
1710 | switch (stat_config.aggr_mode) { | 1714 | switch (stat_config.aggr_mode) { |
1711 | case AGGR_SOCKET: | 1715 | case AGGR_SOCKET: |
1712 | fprintf(output, "# time socket cpus"); | 1716 | fprintf(output, "# time socket cpus"); |
@@ -1719,7 +1723,7 @@ static void print_interval(char *prefix, struct timespec *ts) | |||
1719 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | 1723 | fprintf(output, " counts %*s events\n", unit_width, "unit"); |
1720 | break; | 1724 | break; |
1721 | case AGGR_NONE: | 1725 | case AGGR_NONE: |
1722 | fprintf(output, "# time CPU"); | 1726 | fprintf(output, "# time CPU "); |
1723 | if (!metric_only) | 1727 | if (!metric_only) |
1724 | fprintf(output, " counts %*s events\n", unit_width, "unit"); | 1728 | fprintf(output, " counts %*s events\n", unit_width, "unit"); |
1725 | break; | 1729 | break; |
@@ -1738,7 +1742,7 @@ static void print_interval(char *prefix, struct timespec *ts) | |||
1738 | } | 1742 | } |
1739 | } | 1743 | } |
1740 | 1744 | ||
1741 | if (num_print_interval == 0 && metric_only) | 1745 | if ((num_print_interval == 0 && metric_only) || interval_clear) |
1742 | print_metric_headers(" ", true); | 1746 | print_metric_headers(" ", true); |
1743 | if (++num_print_interval == 25) | 1747 | if (++num_print_interval == 25) |
1744 | num_print_interval = 0; | 1748 | num_print_interval = 0; |
@@ -2057,6 +2061,8 @@ static const struct option stat_options[] = { | |||
2057 | "(overhead is possible for values <= 100ms)"), | 2061 | "(overhead is possible for values <= 100ms)"), |
2058 | OPT_INTEGER(0, "interval-count", &stat_config.times, | 2062 | OPT_INTEGER(0, "interval-count", &stat_config.times, |
2059 | "print counts for fixed number of times"), | 2063 | "print counts for fixed number of times"), |
2064 | OPT_BOOLEAN(0, "interval-clear", &interval_clear, | ||
2065 | "clear screen in between new interval"), | ||
2060 | OPT_UINTEGER(0, "timeout", &stat_config.timeout, | 2066 | OPT_UINTEGER(0, "timeout", &stat_config.timeout, |
2061 | "stop workload and print counts after a timeout period in ms (>= 10ms)"), | 2067 | "stop workload and print counts after a timeout period in ms (>= 10ms)"), |
2062 | OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode, | 2068 | OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode, |
@@ -2436,14 +2442,13 @@ static int add_default_attributes(void) | |||
2436 | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | | 2442 | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | |
2437 | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, | 2443 | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, |
2438 | }; | 2444 | }; |
2445 | struct parse_events_error errinfo; | ||
2439 | 2446 | ||
2440 | /* Set attrs if no event is selected and !null_run: */ | 2447 | /* Set attrs if no event is selected and !null_run: */ |
2441 | if (null_run) | 2448 | if (null_run) |
2442 | return 0; | 2449 | return 0; |
2443 | 2450 | ||
2444 | if (transaction_run) { | 2451 | if (transaction_run) { |
2445 | struct parse_events_error errinfo; | ||
2446 | |||
2447 | if (pmu_have_event("cpu", "cycles-ct") && | 2452 | if (pmu_have_event("cpu", "cycles-ct") && |
2448 | pmu_have_event("cpu", "el-start")) | 2453 | pmu_have_event("cpu", "el-start")) |
2449 | err = parse_events(evsel_list, transaction_attrs, | 2454 | err = parse_events(evsel_list, transaction_attrs, |
@@ -2454,6 +2459,7 @@ static int add_default_attributes(void) | |||
2454 | &errinfo); | 2459 | &errinfo); |
2455 | if (err) { | 2460 | if (err) { |
2456 | fprintf(stderr, "Cannot set up transaction events\n"); | 2461 | fprintf(stderr, "Cannot set up transaction events\n"); |
2462 | parse_events_print_error(&errinfo, transaction_attrs); | ||
2457 | return -1; | 2463 | return -1; |
2458 | } | 2464 | } |
2459 | return 0; | 2465 | return 0; |
@@ -2479,10 +2485,11 @@ static int add_default_attributes(void) | |||
2479 | pmu_have_event("msr", "smi")) { | 2485 | pmu_have_event("msr", "smi")) { |
2480 | if (!force_metric_only) | 2486 | if (!force_metric_only) |
2481 | metric_only = true; | 2487 | metric_only = true; |
2482 | err = parse_events(evsel_list, smi_cost_attrs, NULL); | 2488 | err = parse_events(evsel_list, smi_cost_attrs, &errinfo); |
2483 | } else { | 2489 | } else { |
2484 | fprintf(stderr, "To measure SMI cost, it needs " | 2490 | fprintf(stderr, "To measure SMI cost, it needs " |
2485 | "msr/aperf/, msr/smi/ and cpu/cycles/ support\n"); | 2491 | "msr/aperf/, msr/smi/ and cpu/cycles/ support\n"); |
2492 | parse_events_print_error(&errinfo, smi_cost_attrs); | ||
2486 | return -1; | 2493 | return -1; |
2487 | } | 2494 | } |
2488 | if (err) { | 2495 | if (err) { |
@@ -2517,12 +2524,13 @@ static int add_default_attributes(void) | |||
2517 | if (topdown_attrs[0] && str) { | 2524 | if (topdown_attrs[0] && str) { |
2518 | if (warn) | 2525 | if (warn) |
2519 | arch_topdown_group_warn(); | 2526 | arch_topdown_group_warn(); |
2520 | err = parse_events(evsel_list, str, NULL); | 2527 | err = parse_events(evsel_list, str, &errinfo); |
2521 | if (err) { | 2528 | if (err) { |
2522 | fprintf(stderr, | 2529 | fprintf(stderr, |
2523 | "Cannot set up top down events %s: %d\n", | 2530 | "Cannot set up top down events %s: %d\n", |
2524 | str, err); | 2531 | str, err); |
2525 | free(str); | 2532 | free(str); |
2533 | parse_events_print_error(&errinfo, str); | ||
2526 | return -1; | 2534 | return -1; |
2527 | } | 2535 | } |
2528 | } else { | 2536 | } else { |
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index b085f1b3e34d..4ab663ec3e5e 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c | |||
@@ -382,7 +382,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
382 | gtk_tree_store_set(store, &iter, col_idx++, s, -1); | 382 | gtk_tree_store_set(store, &iter, col_idx++, s, -1); |
383 | } | 383 | } |
384 | 384 | ||
385 | if (hists__has_callchains(hists) && | 385 | if (hist_entry__has_callchains(h) && |
386 | symbol_conf.use_callchain && hists__has(hists, sym)) { | 386 | symbol_conf.use_callchain && hists__has(hists, sym)) { |
387 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 387 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
388 | total = symbol_conf.cumulate_callchain ? | 388 | total = symbol_conf.cumulate_callchain ? |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 52e8fda93a47..828cb9794c76 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -370,9 +370,11 @@ void hists__delete_entries(struct hists *hists) | |||
370 | 370 | ||
371 | static int hist_entry__init(struct hist_entry *he, | 371 | static int hist_entry__init(struct hist_entry *he, |
372 | struct hist_entry *template, | 372 | struct hist_entry *template, |
373 | bool sample_self) | 373 | bool sample_self, |
374 | size_t callchain_size) | ||
374 | { | 375 | { |
375 | *he = *template; | 376 | *he = *template; |
377 | he->callchain_size = callchain_size; | ||
376 | 378 | ||
377 | if (symbol_conf.cumulate_callchain) { | 379 | if (symbol_conf.cumulate_callchain) { |
378 | he->stat_acc = malloc(sizeof(he->stat)); | 380 | he->stat_acc = malloc(sizeof(he->stat)); |
@@ -473,7 +475,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, | |||
473 | 475 | ||
474 | he = ops->new(callchain_size); | 476 | he = ops->new(callchain_size); |
475 | if (he) { | 477 | if (he) { |
476 | err = hist_entry__init(he, template, sample_self); | 478 | err = hist_entry__init(he, template, sample_self, callchain_size); |
477 | if (err) { | 479 | if (err) { |
478 | ops->free(he); | 480 | ops->free(he); |
479 | he = NULL; | 481 | he = NULL; |
@@ -619,9 +621,11 @@ __hists__add_entry(struct hists *hists, | |||
619 | .raw_data = sample->raw_data, | 621 | .raw_data = sample->raw_data, |
620 | .raw_size = sample->raw_size, | 622 | .raw_size = sample->raw_size, |
621 | .ops = ops, | 623 | .ops = ops, |
622 | }; | 624 | }, *he = hists__findnew_entry(hists, &entry, al, sample_self); |
623 | 625 | ||
624 | return hists__findnew_entry(hists, &entry, al, sample_self); | 626 | if (!hists->has_callchains && he && he->callchain_size != 0) |
627 | hists->has_callchains = true; | ||
628 | return he; | ||
625 | } | 629 | } |
626 | 630 | ||
627 | struct hist_entry *hists__add_entry(struct hists *hists, | 631 | struct hist_entry *hists__add_entry(struct hists *hists, |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 06607c434949..73049f7f0f60 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -85,6 +85,7 @@ struct hists { | |||
85 | struct events_stats stats; | 85 | struct events_stats stats; |
86 | u64 event_stream; | 86 | u64 event_stream; |
87 | u16 col_len[HISTC_NR_COLS]; | 87 | u16 col_len[HISTC_NR_COLS]; |
88 | bool has_callchains; | ||
88 | int socket_filter; | 89 | int socket_filter; |
89 | struct perf_hpp_list *hpp_list; | 90 | struct perf_hpp_list *hpp_list; |
90 | struct list_head hpp_formats; | 91 | struct list_head hpp_formats; |
@@ -222,8 +223,7 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel) | |||
222 | 223 | ||
223 | static __pure inline bool hists__has_callchains(struct hists *hists) | 224 | static __pure inline bool hists__has_callchains(struct hists *hists) |
224 | { | 225 | { |
225 | const struct perf_evsel *evsel = hists_to_evsel(hists); | 226 | return hists->has_callchains; |
226 | return evsel__has_callchain(evsel); | ||
227 | } | 227 | } |
228 | 228 | ||
229 | int hists__init(void); | 229 | int hists__init(void); |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 155d2570274f..da8fe57691b8 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -227,11 +227,16 @@ event_def: event_pmu | | |||
227 | event_pmu: | 227 | event_pmu: |
228 | PE_NAME opt_pmu_config | 228 | PE_NAME opt_pmu_config |
229 | { | 229 | { |
230 | struct parse_events_state *parse_state = _parse_state; | ||
231 | struct parse_events_error *error = parse_state->error; | ||
230 | struct list_head *list, *orig_terms, *terms; | 232 | struct list_head *list, *orig_terms, *terms; |
231 | 233 | ||
232 | if (parse_events_copy_term_list($2, &orig_terms)) | 234 | if (parse_events_copy_term_list($2, &orig_terms)) |
233 | YYABORT; | 235 | YYABORT; |
234 | 236 | ||
237 | if (error) | ||
238 | error->idx = @1.first_column; | ||
239 | |||
235 | ALLOC_LIST(list); | 240 | ALLOC_LIST(list); |
236 | if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) { | 241 | if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) { |
237 | struct perf_pmu *pmu = NULL; | 242 | struct perf_pmu *pmu = NULL; |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 7cf2d5cc038e..8bf302cafcec 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -112,6 +112,8 @@ struct hist_entry { | |||
112 | 112 | ||
113 | char level; | 113 | char level; |
114 | u8 filtered; | 114 | u8 filtered; |
115 | |||
116 | u16 callchain_size; | ||
115 | union { | 117 | union { |
116 | /* | 118 | /* |
117 | * Since perf diff only supports the stdio output, TUI | 119 | * Since perf diff only supports the stdio output, TUI |
@@ -153,7 +155,7 @@ struct hist_entry { | |||
153 | 155 | ||
154 | static __pure inline bool hist_entry__has_callchains(struct hist_entry *he) | 156 | static __pure inline bool hist_entry__has_callchains(struct hist_entry *he) |
155 | { | 157 | { |
156 | return hists__has_callchains(he->hists); | 158 | return he->callchain_size != 0; |
157 | } | 159 | } |
158 | 160 | ||
159 | static inline bool hist_entry__has_pairs(struct hist_entry *he) | 161 | static inline bool hist_entry__has_pairs(struct hist_entry *he) |