mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-05 18:39:59 +00:00
perf/urgent fixes and improvements:
perf stat: . Add --interval-clear option, to provide a 'watch' like printing (Jiri Olsa) . 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) 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> -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEELb9bqkb7Te0zijNb1lAW81NSqkAFAlsesIYACgkQ1lAW81NS qkCvJA//YInlTK5uSKDTSvn7jnw5qz+aOawOi/ggSC2y1QeDh/98GhKyAb4UF23u Jfe/DA/h8T9NEbPkCjbDwDGsMAiFswZgiZ535f1BklvOriuY7k0Z2s0oYlXjgruy vn1rMqy/zIVVFUYfoQe9wpilr0hioCLvMiN3kFZPIWUeZlijs6oapagd4wgfdQgK yFdplNhLA3siLj3j4SKfGcDyMya5Uy6P37Wffh2S4GbOn4kr302YKhNpBAxsezKa hoZe9GWAM7MKTo5pJD9IigRcRYWKjs7qnR7aewgwFbITTDf3M+gs8NlRqWMCziN1 /kfH+Xi6Rw0XUwSxoo5t74u5vziwYU5Tif+6Wj2bVs7Djj4XAboFMr/ovT2u56s3 a4y+hy38jowgtanH0s1GR86Xh44t1i4d1dUgkJawA3Z+CsK0P/2sakcgPFfpvUIh vUkj/Zf5OPht4y1bqnxLsKBuF11WvBJpWxNFTNWVXYlWlXdupjCa0yeFYCWutpwL gmOI6PDphXO0oAe476inDKJYrFHIL1MlSXprNqz3y4A+U86vIlDUchq7hW1WFdex vuOFwP5bSlJ84ch6C3N7BAKD6X3fT+MTYmmnmLXKMutg2A/u/nOSeed+3nrLNgN0 xxQ0uB1VuR1wszsP6HOY0Ib3HfrLLdhm/0Dq45HVmKWXSZ775pw= =rF+V -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
a89d0c2acf
9 changed files with 67 additions and 33 deletions
|
@ -178,6 +178,9 @@ Print count deltas for fixed number of times.
|
|||
This option should be used together with "-I" option.
|
||||
example: 'perf stat -I 1000 --interval-count 2 -e cycles -a'
|
||||
|
||||
--interval-clear::
|
||||
Clear the screen before next interval.
|
||||
|
||||
--timeout msecs::
|
||||
Stop the 'perf stat' session and print count deltas after N milliseconds (minimum: 10 ms).
|
||||
This option is not supported with the "-I" option.
|
||||
|
|
|
@ -56,16 +56,16 @@ struct c2c_hist_entry {
|
|||
|
||||
struct compute_stats cstats;
|
||||
|
||||
unsigned long paddr;
|
||||
unsigned long paddr_cnt;
|
||||
bool paddr_zero;
|
||||
char *nodestr;
|
||||
|
||||
/*
|
||||
* must be at the end,
|
||||
* because of its callchain dynamic entry
|
||||
*/
|
||||
struct hist_entry he;
|
||||
|
||||
unsigned long paddr;
|
||||
unsigned long paddr_cnt;
|
||||
bool paddr_zero;
|
||||
char *nodestr;
|
||||
};
|
||||
|
||||
static char const *coalesce_default = "pid,iaddr";
|
||||
|
|
|
@ -180,6 +180,18 @@ static struct {
|
|||
PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE
|
||||
},
|
||||
|
||||
[PERF_TYPE_HW_CACHE] = {
|
||||
.user_set = false,
|
||||
|
||||
.fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
|
||||
PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
|
||||
PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
|
||||
PERF_OUTPUT_SYM | PERF_OUTPUT_SYMOFFSET |
|
||||
PERF_OUTPUT_DSO | PERF_OUTPUT_PERIOD,
|
||||
|
||||
.invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
|
||||
},
|
||||
|
||||
[PERF_TYPE_RAW] = {
|
||||
.user_set = false,
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#include "util/tool.h"
|
||||
#include "util/string2.h"
|
||||
#include "util/metricgroup.h"
|
||||
#include "util/top.h"
|
||||
#include "asm/bug.h"
|
||||
|
||||
#include <linux/time64.h>
|
||||
|
@ -144,6 +145,8 @@ static struct target target = {
|
|||
|
||||
typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu);
|
||||
|
||||
#define METRIC_ONLY_LEN 20
|
||||
|
||||
static int run_count = 1;
|
||||
static bool no_inherit = false;
|
||||
static volatile pid_t child_pid = -1;
|
||||
|
@ -173,6 +176,7 @@ static struct cpu_map *aggr_map;
|
|||
static aggr_get_id_t aggr_get_id;
|
||||
static bool append_file;
|
||||
static bool interval_count;
|
||||
static bool interval_clear;
|
||||
static const char *output_name;
|
||||
static int output_fd;
|
||||
static int print_free_counters_hint;
|
||||
|
@ -180,6 +184,7 @@ static int print_mixed_hw_group_error;
|
|||
static u64 *walltime_run;
|
||||
static bool ru_display = false;
|
||||
static struct rusage ru_data;
|
||||
static unsigned int metric_only_len = METRIC_ONLY_LEN;
|
||||
|
||||
struct perf_stat {
|
||||
bool record;
|
||||
|
@ -967,8 +972,6 @@ static void print_metric_csv(void *ctx,
|
|||
fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit);
|
||||
}
|
||||
|
||||
#define METRIC_ONLY_LEN 20
|
||||
|
||||
/* Filter out some columns that don't work well in metrics only mode */
|
||||
|
||||
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,
|
|||
{
|
||||
struct outstate *os = ctx;
|
||||
FILE *out = os->fh;
|
||||
int n;
|
||||
char buf[1024];
|
||||
unsigned mlen = METRIC_ONLY_LEN;
|
||||
char buf[1024], str[1024];
|
||||
unsigned mlen = metric_only_len;
|
||||
|
||||
if (!valid_only_metric(unit))
|
||||
return;
|
||||
unit = fixunit(buf, os->evsel, unit);
|
||||
if (color)
|
||||
n = color_fprintf(out, color, fmt, val);
|
||||
else
|
||||
n = fprintf(out, fmt, val);
|
||||
if (n > METRIC_ONLY_LEN)
|
||||
n = METRIC_ONLY_LEN;
|
||||
if (mlen < strlen(unit))
|
||||
mlen = strlen(unit) + 1;
|
||||
fprintf(out, "%*s", mlen - n, "");
|
||||
|
||||
if (color)
|
||||
mlen += strlen(color) + sizeof(PERF_COLOR_RESET) - 1;
|
||||
|
||||
color_snprintf(str, sizeof(str), color ?: "", fmt, val);
|
||||
fprintf(out, "%*s ", mlen, str);
|
||||
}
|
||||
|
||||
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,
|
|||
if (csv_output)
|
||||
fprintf(os->fh, "%s%s", unit, csv_sep);
|
||||
else
|
||||
fprintf(os->fh, "%-*s ", METRIC_ONLY_LEN, unit);
|
||||
fprintf(os->fh, "%*s ", metric_only_len, unit);
|
||||
}
|
||||
|
||||
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)
|
|||
FILE *output = stat_config.output;
|
||||
static int num_print_interval;
|
||||
|
||||
if (interval_clear)
|
||||
puts(CONSOLE_CLEAR);
|
||||
|
||||
sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep);
|
||||
|
||||
if (num_print_interval == 0 && !csv_output) {
|
||||
if ((num_print_interval == 0 && !csv_output) || interval_clear) {
|
||||
switch (stat_config.aggr_mode) {
|
||||
case AGGR_SOCKET:
|
||||
fprintf(output, "# time socket cpus");
|
||||
|
@ -1719,7 +1723,7 @@ static void print_interval(char *prefix, struct timespec *ts)
|
|||
fprintf(output, " counts %*s events\n", unit_width, "unit");
|
||||
break;
|
||||
case AGGR_NONE:
|
||||
fprintf(output, "# time CPU");
|
||||
fprintf(output, "# time CPU ");
|
||||
if (!metric_only)
|
||||
fprintf(output, " counts %*s events\n", unit_width, "unit");
|
||||
break;
|
||||
|
@ -1738,7 +1742,7 @@ static void print_interval(char *prefix, struct timespec *ts)
|
|||
}
|
||||
}
|
||||
|
||||
if (num_print_interval == 0 && metric_only)
|
||||
if ((num_print_interval == 0 && metric_only) || interval_clear)
|
||||
print_metric_headers(" ", true);
|
||||
if (++num_print_interval == 25)
|
||||
num_print_interval = 0;
|
||||
|
@ -2057,6 +2061,8 @@ static const struct option stat_options[] = {
|
|||
"(overhead is possible for values <= 100ms)"),
|
||||
OPT_INTEGER(0, "interval-count", &stat_config.times,
|
||||
"print counts for fixed number of times"),
|
||||
OPT_BOOLEAN(0, "interval-clear", &interval_clear,
|
||||
"clear screen in between new interval"),
|
||||
OPT_UINTEGER(0, "timeout", &stat_config.timeout,
|
||||
"stop workload and print counts after a timeout period in ms (>= 10ms)"),
|
||||
OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode,
|
||||
|
@ -2436,14 +2442,13 @@ static int add_default_attributes(void)
|
|||
(PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
|
||||
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
|
||||
};
|
||||
struct parse_events_error errinfo;
|
||||
|
||||
/* Set attrs if no event is selected and !null_run: */
|
||||
if (null_run)
|
||||
return 0;
|
||||
|
||||
if (transaction_run) {
|
||||
struct parse_events_error errinfo;
|
||||
|
||||
if (pmu_have_event("cpu", "cycles-ct") &&
|
||||
pmu_have_event("cpu", "el-start"))
|
||||
err = parse_events(evsel_list, transaction_attrs,
|
||||
|
@ -2454,6 +2459,7 @@ static int add_default_attributes(void)
|
|||
&errinfo);
|
||||
if (err) {
|
||||
fprintf(stderr, "Cannot set up transaction events\n");
|
||||
parse_events_print_error(&errinfo, transaction_attrs);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -2479,10 +2485,11 @@ static int add_default_attributes(void)
|
|||
pmu_have_event("msr", "smi")) {
|
||||
if (!force_metric_only)
|
||||
metric_only = true;
|
||||
err = parse_events(evsel_list, smi_cost_attrs, NULL);
|
||||
err = parse_events(evsel_list, smi_cost_attrs, &errinfo);
|
||||
} else {
|
||||
fprintf(stderr, "To measure SMI cost, it needs "
|
||||
"msr/aperf/, msr/smi/ and cpu/cycles/ support\n");
|
||||
parse_events_print_error(&errinfo, smi_cost_attrs);
|
||||
return -1;
|
||||
}
|
||||
if (err) {
|
||||
|
@ -2517,12 +2524,13 @@ static int add_default_attributes(void)
|
|||
if (topdown_attrs[0] && str) {
|
||||
if (warn)
|
||||
arch_topdown_group_warn();
|
||||
err = parse_events(evsel_list, str, NULL);
|
||||
err = parse_events(evsel_list, str, &errinfo);
|
||||
if (err) {
|
||||
fprintf(stderr,
|
||||
"Cannot set up top down events %s: %d\n",
|
||||
str, err);
|
||||
free(str);
|
||||
parse_events_print_error(&errinfo, str);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -382,7 +382,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
if (hists__has_callchains(hists) &&
|
||||
if (hist_entry__has_callchains(h) &&
|
||||
symbol_conf.use_callchain && hists__has(hists, sym)) {
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
total = symbol_conf.cumulate_callchain ?
|
||||
|
|
|
@ -370,9 +370,11 @@ void hists__delete_entries(struct hists *hists)
|
|||
|
||||
static int hist_entry__init(struct hist_entry *he,
|
||||
struct hist_entry *template,
|
||||
bool sample_self)
|
||||
bool sample_self,
|
||||
size_t callchain_size)
|
||||
{
|
||||
*he = *template;
|
||||
he->callchain_size = callchain_size;
|
||||
|
||||
if (symbol_conf.cumulate_callchain) {
|
||||
he->stat_acc = malloc(sizeof(he->stat));
|
||||
|
@ -473,7 +475,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
|
|||
|
||||
he = ops->new(callchain_size);
|
||||
if (he) {
|
||||
err = hist_entry__init(he, template, sample_self);
|
||||
err = hist_entry__init(he, template, sample_self, callchain_size);
|
||||
if (err) {
|
||||
ops->free(he);
|
||||
he = NULL;
|
||||
|
@ -619,9 +621,11 @@ __hists__add_entry(struct hists *hists,
|
|||
.raw_data = sample->raw_data,
|
||||
.raw_size = sample->raw_size,
|
||||
.ops = ops,
|
||||
};
|
||||
}, *he = hists__findnew_entry(hists, &entry, al, sample_self);
|
||||
|
||||
return hists__findnew_entry(hists, &entry, al, sample_self);
|
||||
if (!hists->has_callchains && he && he->callchain_size != 0)
|
||||
hists->has_callchains = true;
|
||||
return he;
|
||||
}
|
||||
|
||||
struct hist_entry *hists__add_entry(struct hists *hists,
|
||||
|
|
|
@ -85,6 +85,7 @@ struct hists {
|
|||
struct events_stats stats;
|
||||
u64 event_stream;
|
||||
u16 col_len[HISTC_NR_COLS];
|
||||
bool has_callchains;
|
||||
int socket_filter;
|
||||
struct perf_hpp_list *hpp_list;
|
||||
struct list_head hpp_formats;
|
||||
|
@ -222,8 +223,7 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel)
|
|||
|
||||
static __pure inline bool hists__has_callchains(struct hists *hists)
|
||||
{
|
||||
const struct perf_evsel *evsel = hists_to_evsel(hists);
|
||||
return evsel__has_callchain(evsel);
|
||||
return hists->has_callchains;
|
||||
}
|
||||
|
||||
int hists__init(void);
|
||||
|
|
|
@ -227,11 +227,16 @@ event_def: event_pmu |
|
|||
event_pmu:
|
||||
PE_NAME opt_pmu_config
|
||||
{
|
||||
struct parse_events_state *parse_state = _parse_state;
|
||||
struct parse_events_error *error = parse_state->error;
|
||||
struct list_head *list, *orig_terms, *terms;
|
||||
|
||||
if (parse_events_copy_term_list($2, &orig_terms))
|
||||
YYABORT;
|
||||
|
||||
if (error)
|
||||
error->idx = @1.first_column;
|
||||
|
||||
ALLOC_LIST(list);
|
||||
if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) {
|
||||
struct perf_pmu *pmu = NULL;
|
||||
|
|
|
@ -112,6 +112,8 @@ struct hist_entry {
|
|||
|
||||
char level;
|
||||
u8 filtered;
|
||||
|
||||
u16 callchain_size;
|
||||
union {
|
||||
/*
|
||||
* Since perf diff only supports the stdio output, TUI
|
||||
|
@ -153,7 +155,7 @@ struct hist_entry {
|
|||
|
||||
static __pure inline bool hist_entry__has_callchains(struct hist_entry *he)
|
||||
{
|
||||
return hists__has_callchains(he->hists);
|
||||
return he->callchain_size != 0;
|
||||
}
|
||||
|
||||
static inline bool hist_entry__has_pairs(struct hist_entry *he)
|
||||
|
|
Loading…
Reference in a new issue