Commit c81b995f authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull perf fixes from Thomas Gleixner:
 "A pile of perf updates:

  Kernel side:

   - Remove an incorrect warning in uprobe_init_insn() when
     insn_get_length() fails. The error return code is handled at the
     call site.

   - Move the inline keyword to the right place in the perf ringbuffer
     code to address a W=1 build warning.

  Tooling:

  perf stat:

   - Fix metric column header display alignment

   - Improve error messages for default attributes, providing better
     output for error in command line.

   - Add --interval-clear option, to provide a 'watch' like printing

  perf script:

   - Show hw-cache events too

  perf c2c:

   - Fix data dependency problem in layout of 'struct c2c_hist_entry'

  Core:

   - Do not blindly assume that 'struct perf_evsel' can be obtained via
     a straight forward container_of() as there are call sites which
     hand in a plain 'struct hist' which is not part of a container.

   - Fix error index in the PMU event parser, so that error messages can
     point to the problematic token"

* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  perf/core: Move the inline keyword at the beginning of the function declaration
  uprobes/x86: Remove incorrect WARN_ON() in uprobe_init_insn()
  perf script: Show hw-cache events
  perf c2c: Keep struct hist_entry at the end of struct c2c_hist_entry
  perf stat: Add event parsing error handling to add_default_attributes
  perf stat: Allow to specify specific metric column len
  perf stat: Fix metric column header display alignment
  perf stat: Use only color_fprintf call in print_metric_only
  perf stat: Add --interval-clear option
  perf tools: Fix error index for pmu event parser
  perf hists: Reimplement hists__has_callchains()
  perf hists browser gtk: Use hist_entry__has_callchains()
  perf hists: Make hist_entry__has_callchains() work with 'perf c2c'
  perf hists: Save the callchain_size in struct hist_entry
parents 2ce413ec 57d6a793
No related merge requests found
...@@ -293,7 +293,7 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool ...@@ -293,7 +293,7 @@ static int uprobe_init_insn(struct arch_uprobe *auprobe, struct insn *insn, bool
insn_init(insn, auprobe->insn, sizeof(auprobe->insn), x86_64); insn_init(insn, auprobe->insn, sizeof(auprobe->insn), x86_64);
/* has the side-effect of processing the entire instruction */ /* has the side-effect of processing the entire instruction */
insn_get_length(insn); insn_get_length(insn);
if (WARN_ON_ONCE(!insn_complete(insn))) if (!insn_complete(insn))
return -ENOEXEC; return -ENOEXEC;
if (is_prefix_bad(insn)) if (is_prefix_bad(insn))
......
...@@ -103,7 +103,7 @@ static void perf_output_put_handle(struct perf_output_handle *handle) ...@@ -103,7 +103,7 @@ static void perf_output_put_handle(struct perf_output_handle *handle)
preempt_enable(); preempt_enable();
} }
static bool __always_inline static __always_inline bool
ring_buffer_has_space(unsigned long head, unsigned long tail, ring_buffer_has_space(unsigned long head, unsigned long tail,
unsigned long data_size, unsigned int size, unsigned long data_size, unsigned int size,
bool backward) bool backward)
...@@ -114,7 +114,7 @@ ring_buffer_has_space(unsigned long head, unsigned long tail, ...@@ -114,7 +114,7 @@ ring_buffer_has_space(unsigned long head, unsigned long tail,
return CIRC_SPACE(tail, head, data_size) >= size; return CIRC_SPACE(tail, head, data_size) >= size;
} }
static int __always_inline static __always_inline int
__perf_output_begin(struct perf_output_handle *handle, __perf_output_begin(struct perf_output_handle *handle,
struct perf_event *event, unsigned int size, struct perf_event *event, unsigned int size,
bool backward) bool backward)
...@@ -414,7 +414,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, ...@@ -414,7 +414,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle,
} }
EXPORT_SYMBOL_GPL(perf_aux_output_begin); EXPORT_SYMBOL_GPL(perf_aux_output_begin);
static bool __always_inline rb_need_aux_wakeup(struct ring_buffer *rb) static __always_inline bool rb_need_aux_wakeup(struct ring_buffer *rb)
{ {
if (rb->aux_overwrite) if (rb->aux_overwrite)
return false; return false;
......
...@@ -178,6 +178,9 @@ Print count deltas for fixed number of times. ...@@ -178,6 +178,9 @@ Print count deltas for fixed number of times.
This option should be used together with "-I" option. This option should be used together with "-I" option.
example: 'perf stat -I 1000 --interval-count 2 -e cycles -a' example: 'perf stat -I 1000 --interval-count 2 -e cycles -a'
--interval-clear::
Clear the screen before next interval.
--timeout msecs:: --timeout msecs::
Stop the 'perf stat' session and print count deltas after N milliseconds (minimum: 10 ms). Stop the 'perf stat' session and print count deltas after N milliseconds (minimum: 10 ms).
This option is not supported with the "-I" option. This option is not supported with the "-I" option.
......
...@@ -56,16 +56,16 @@ struct c2c_hist_entry { ...@@ -56,16 +56,16 @@ struct c2c_hist_entry {
struct compute_stats cstats; struct compute_stats cstats;
unsigned long paddr;
unsigned long paddr_cnt;
bool paddr_zero;
char *nodestr;
/* /*
* must be at the end, * must be at the end,
* because of its callchain dynamic entry * because of its callchain dynamic entry
*/ */
struct hist_entry he; struct hist_entry he;
unsigned long paddr;
unsigned long paddr_cnt;
bool paddr_zero;
char *nodestr;
}; };
static char const *coalesce_default = "pid,iaddr"; static char const *coalesce_default = "pid,iaddr";
......
...@@ -180,6 +180,18 @@ static struct { ...@@ -180,6 +180,18 @@ static struct {
PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE 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] = { [PERF_TYPE_RAW] = {
.user_set = false, .user_set = false,
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#include "util/tool.h" #include "util/tool.h"
#include "util/string2.h" #include "util/string2.h"
#include "util/metricgroup.h" #include "util/metricgroup.h"
#include "util/top.h"
#include "asm/bug.h" #include "asm/bug.h"
#include <linux/time64.h> #include <linux/time64.h>
...@@ -144,6 +145,8 @@ static struct target target = { ...@@ -144,6 +145,8 @@ static struct target target = {
typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu); typedef int (*aggr_get_id_t)(struct cpu_map *m, int cpu);
#define METRIC_ONLY_LEN 20
static int run_count = 1; static int run_count = 1;
static bool no_inherit = false; static bool no_inherit = false;
static volatile pid_t child_pid = -1; static volatile pid_t child_pid = -1;
...@@ -173,6 +176,7 @@ static struct cpu_map *aggr_map; ...@@ -173,6 +176,7 @@ static struct cpu_map *aggr_map;
static aggr_get_id_t aggr_get_id; static aggr_get_id_t aggr_get_id;
static bool append_file; static bool append_file;
static bool interval_count; static bool interval_count;
static bool interval_clear;
static const char *output_name; static const char *output_name;
static int output_fd; static int output_fd;
static int print_free_counters_hint; static int print_free_counters_hint;
...@@ -180,6 +184,7 @@ static int print_mixed_hw_group_error; ...@@ -180,6 +184,7 @@ static int print_mixed_hw_group_error;
static u64 *walltime_run; static u64 *walltime_run;
static bool ru_display = false; static bool ru_display = false;
static struct rusage ru_data; static struct rusage ru_data;
static unsigned int metric_only_len = METRIC_ONLY_LEN;
struct perf_stat { struct perf_stat {
bool record; bool record;
...@@ -967,8 +972,6 @@ static void print_metric_csv(void *ctx, ...@@ -967,8 +972,6 @@ static void print_metric_csv(void *ctx,
fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); 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 */ /* Filter out some columns that don't work well in metrics only mode */
static bool valid_only_metric(const char *unit) 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,22 +1002,20 @@ static void print_metric_only(void *ctx, const char *color, const char *fmt,
{ {
struct outstate *os = ctx; struct outstate *os = ctx;
FILE *out = os->fh; FILE *out = os->fh;
int n; char buf[1024], str[1024];
char buf[1024]; unsigned mlen = metric_only_len;
unsigned mlen = METRIC_ONLY_LEN;
if (!valid_only_metric(unit)) if (!valid_only_metric(unit))
return; return;
unit = fixunit(buf, os->evsel, unit); 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)) if (mlen < strlen(unit))
mlen = strlen(unit) + 1; 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, 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,7 +1055,7 @@ static void print_metric_header(void *ctx, const char *color __maybe_unused,
if (csv_output) if (csv_output)
fprintf(os->fh, "%s%s", unit, csv_sep); fprintf(os->fh, "%s%s", unit, csv_sep);
else 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) 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,9 +1705,12 @@ static void print_interval(char *prefix, struct timespec *ts)
FILE *output = stat_config.output; FILE *output = stat_config.output;
static int num_print_interval; static int num_print_interval;
if (interval_clear)
puts(CONSOLE_CLEAR);
sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); 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) { switch (stat_config.aggr_mode) {
case AGGR_SOCKET: case AGGR_SOCKET:
fprintf(output, "# time socket cpus"); fprintf(output, "# time socket cpus");
...@@ -1719,7 +1723,7 @@ static void print_interval(char *prefix, struct timespec *ts) ...@@ -1719,7 +1723,7 @@ static void print_interval(char *prefix, struct timespec *ts)
fprintf(output, " counts %*s events\n", unit_width, "unit"); fprintf(output, " counts %*s events\n", unit_width, "unit");
break; break;
case AGGR_NONE: case AGGR_NONE:
fprintf(output, "# time CPU"); fprintf(output, "# time CPU ");
if (!metric_only) if (!metric_only)
fprintf(output, " counts %*s events\n", unit_width, "unit"); fprintf(output, " counts %*s events\n", unit_width, "unit");
break; break;
...@@ -1738,7 +1742,7 @@ static void print_interval(char *prefix, struct timespec *ts) ...@@ -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); print_metric_headers(" ", true);
if (++num_print_interval == 25) if (++num_print_interval == 25)
num_print_interval = 0; num_print_interval = 0;
...@@ -2057,6 +2061,8 @@ static const struct option stat_options[] = { ...@@ -2057,6 +2061,8 @@ static const struct option stat_options[] = {
"(overhead is possible for values <= 100ms)"), "(overhead is possible for values <= 100ms)"),
OPT_INTEGER(0, "interval-count", &stat_config.times, OPT_INTEGER(0, "interval-count", &stat_config.times,
"print counts for fixed number of 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, OPT_UINTEGER(0, "timeout", &stat_config.timeout,
"stop workload and print counts after a timeout period in ms (>= 10ms)"), "stop workload and print counts after a timeout period in ms (>= 10ms)"),
OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode, OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode,
...@@ -2436,14 +2442,13 @@ static int add_default_attributes(void) ...@@ -2436,14 +2442,13 @@ static int add_default_attributes(void)
(PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) |
(PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) },
}; };
struct parse_events_error errinfo;
/* Set attrs if no event is selected and !null_run: */ /* Set attrs if no event is selected and !null_run: */
if (null_run) if (null_run)
return 0; return 0;
if (transaction_run) { if (transaction_run) {
struct parse_events_error errinfo;
if (pmu_have_event("cpu", "cycles-ct") && if (pmu_have_event("cpu", "cycles-ct") &&
pmu_have_event("cpu", "el-start")) pmu_have_event("cpu", "el-start"))
err = parse_events(evsel_list, transaction_attrs, err = parse_events(evsel_list, transaction_attrs,
...@@ -2454,6 +2459,7 @@ static int add_default_attributes(void) ...@@ -2454,6 +2459,7 @@ static int add_default_attributes(void)
&errinfo); &errinfo);
if (err) { if (err) {
fprintf(stderr, "Cannot set up transaction events\n"); fprintf(stderr, "Cannot set up transaction events\n");
parse_events_print_error(&errinfo, transaction_attrs);
return -1; return -1;
} }
return 0; return 0;
...@@ -2479,10 +2485,11 @@ static int add_default_attributes(void) ...@@ -2479,10 +2485,11 @@ static int add_default_attributes(void)
pmu_have_event("msr", "smi")) { pmu_have_event("msr", "smi")) {
if (!force_metric_only) if (!force_metric_only)
metric_only = true; metric_only = true;
err = parse_events(evsel_list, smi_cost_attrs, NULL); err = parse_events(evsel_list, smi_cost_attrs, &errinfo);
} else { } else {
fprintf(stderr, "To measure SMI cost, it needs " fprintf(stderr, "To measure SMI cost, it needs "
"msr/aperf/, msr/smi/ and cpu/cycles/ support\n"); "msr/aperf/, msr/smi/ and cpu/cycles/ support\n");
parse_events_print_error(&errinfo, smi_cost_attrs);
return -1; return -1;
} }
if (err) { if (err) {
...@@ -2517,12 +2524,13 @@ static int add_default_attributes(void) ...@@ -2517,12 +2524,13 @@ static int add_default_attributes(void)
if (topdown_attrs[0] && str) { if (topdown_attrs[0] && str) {
if (warn) if (warn)
arch_topdown_group_warn(); arch_topdown_group_warn();
err = parse_events(evsel_list, str, NULL); err = parse_events(evsel_list, str, &errinfo);
if (err) { if (err) {
fprintf(stderr, fprintf(stderr,
"Cannot set up top down events %s: %d\n", "Cannot set up top down events %s: %d\n",
str, err); str, err);
free(str); free(str);
parse_events_print_error(&errinfo, str);
return -1; return -1;
} }
} else { } else {
......
...@@ -382,7 +382,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, ...@@ -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); 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)) { symbol_conf.use_callchain && hists__has(hists, sym)) {
if (callchain_param.mode == CHAIN_GRAPH_REL) if (callchain_param.mode == CHAIN_GRAPH_REL)
total = symbol_conf.cumulate_callchain ? total = symbol_conf.cumulate_callchain ?
......
...@@ -370,9 +370,11 @@ void hists__delete_entries(struct hists *hists) ...@@ -370,9 +370,11 @@ void hists__delete_entries(struct hists *hists)
static int hist_entry__init(struct hist_entry *he, static int hist_entry__init(struct hist_entry *he,
struct hist_entry *template, struct hist_entry *template,
bool sample_self) bool sample_self,
size_t callchain_size)
{ {
*he = *template; *he = *template;
he->callchain_size = callchain_size;
if (symbol_conf.cumulate_callchain) { if (symbol_conf.cumulate_callchain) {
he->stat_acc = malloc(sizeof(he->stat)); he->stat_acc = malloc(sizeof(he->stat));
...@@ -473,7 +475,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template, ...@@ -473,7 +475,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template,
he = ops->new(callchain_size); he = ops->new(callchain_size);
if (he) { if (he) {
err = hist_entry__init(he, template, sample_self); err = hist_entry__init(he, template, sample_self, callchain_size);
if (err) { if (err) {
ops->free(he); ops->free(he);
he = NULL; he = NULL;
...@@ -619,9 +621,11 @@ __hists__add_entry(struct hists *hists, ...@@ -619,9 +621,11 @@ __hists__add_entry(struct hists *hists,
.raw_data = sample->raw_data, .raw_data = sample->raw_data,
.raw_size = sample->raw_size, .raw_size = sample->raw_size,
.ops = ops, .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, struct hist_entry *hists__add_entry(struct hists *hists,
......
...@@ -85,6 +85,7 @@ struct hists { ...@@ -85,6 +85,7 @@ struct hists {
struct events_stats stats; struct events_stats stats;
u64 event_stream; u64 event_stream;
u16 col_len[HISTC_NR_COLS]; u16 col_len[HISTC_NR_COLS];
bool has_callchains;
int socket_filter; int socket_filter;
struct perf_hpp_list *hpp_list; struct perf_hpp_list *hpp_list;
struct list_head hpp_formats; struct list_head hpp_formats;
...@@ -222,8 +223,7 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel) ...@@ -222,8 +223,7 @@ static inline struct hists *evsel__hists(struct perf_evsel *evsel)
static __pure inline bool hists__has_callchains(struct hists *hists) static __pure inline bool hists__has_callchains(struct hists *hists)
{ {
const struct perf_evsel *evsel = hists_to_evsel(hists); return hists->has_callchains;
return evsel__has_callchain(evsel);
} }
int hists__init(void); int hists__init(void);
......
...@@ -227,11 +227,16 @@ event_def: event_pmu | ...@@ -227,11 +227,16 @@ event_def: event_pmu |
event_pmu: event_pmu:
PE_NAME opt_pmu_config 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; struct list_head *list, *orig_terms, *terms;
if (parse_events_copy_term_list($2, &orig_terms)) if (parse_events_copy_term_list($2, &orig_terms))
YYABORT; YYABORT;
if (error)
error->idx = @1.first_column;
ALLOC_LIST(list); ALLOC_LIST(list);
if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) { if (parse_events_add_pmu(_parse_state, list, $1, $2, false, false)) {
struct perf_pmu *pmu = NULL; struct perf_pmu *pmu = NULL;
......
...@@ -112,6 +112,8 @@ struct hist_entry { ...@@ -112,6 +112,8 @@ struct hist_entry {
char level; char level;
u8 filtered; u8 filtered;
u16 callchain_size;
union { union {
/* /*
* Since perf diff only supports the stdio output, TUI * Since perf diff only supports the stdio output, TUI
...@@ -153,7 +155,7 @@ struct hist_entry { ...@@ -153,7 +155,7 @@ struct hist_entry {
static __pure inline bool hist_entry__has_callchains(struct hist_entry *he) 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) static inline bool hist_entry__has_pairs(struct hist_entry *he)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment