diff options
-rw-r--r-- | tools/perf/builtin-diff.c | 2 | ||||
-rw-r--r-- | tools/perf/builtin-help.c | 2 | ||||
-rw-r--r-- | tools/perf/builtin-kmem.c | 4 | ||||
-rw-r--r-- | tools/perf/builtin-report.c | 2 | ||||
-rw-r--r-- | tools/perf/builtin-script.c | 21 | ||||
-rw-r--r-- | tools/perf/builtin-top.c | 4 | ||||
-rw-r--r-- | tools/perf/builtin-trace.c | 46 | ||||
-rw-r--r-- | tools/perf/jvmti/Makefile | 17 | ||||
-rw-r--r-- | tools/perf/perf.c | 16 | ||||
-rw-r--r-- | tools/perf/tests/llvm.c | 8 | ||||
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 128 | ||||
-rw-r--r-- | tools/perf/ui/gtk/hists.c | 11 | ||||
-rw-r--r-- | tools/perf/ui/hist.c | 22 | ||||
-rw-r--r-- | tools/perf/ui/stdio/hist.c | 49 | ||||
-rw-r--r-- | tools/perf/util/color.c | 5 | ||||
-rw-r--r-- | tools/perf/util/data-convert-bt.c | 2 | ||||
-rw-r--r-- | tools/perf/util/evlist.c | 3 | ||||
-rw-r--r-- | tools/perf/util/help-unknown-cmd.c | 5 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 48 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 4 | ||||
-rw-r--r-- | tools/perf/util/parse-events.y | 6 | ||||
-rw-r--r-- | tools/perf/util/scripting-engines/trace-event-perl.c | 3 | ||||
-rw-r--r-- | tools/perf/util/scripting-engines/trace-event-python.c | 3 | ||||
-rw-r--r-- | tools/perf/util/sort.c | 30 | ||||
-rw-r--r-- | tools/perf/util/sort.h | 1 |
25 files changed, 363 insertions, 79 deletions
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 36ccc2b8827f..4d72359fd15a 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -1264,8 +1264,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) if (ret < 0) return ret; - perf_config(perf_default_config, NULL); - argc = parse_options(argc, argv, options, diff_usage, 0); if (symbol__init(NULL) < 0) diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index f4dd2b48f90f..49d55e21b1b0 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -272,7 +272,7 @@ static int perf_help_config(const char *var, const char *value, void *cb) if (!prefixcmp(var, "man.")) return add_man_viewer_info(var, value); - return perf_default_config(var, value, cb); + return 0; } static struct cmdnames main_cmds, other_cmds; diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 118010553d0c..4d3340cce9a0 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -1834,7 +1834,7 @@ static int __cmd_record(int argc, const char **argv) return cmd_record(i, rec_argv, NULL); } -static int kmem_config(const char *var, const char *value, void *cb) +static int kmem_config(const char *var, const char *value, void *cb __maybe_unused) { if (!strcmp(var, "kmem.default")) { if (!strcmp(value, "slab")) @@ -1847,7 +1847,7 @@ static int kmem_config(const char *var, const char *value, void *cb) return 0; } - return perf_default_config(var, value, cb); + return 0; } int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index f4d8244449ca..7eea49f9ed46 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -90,7 +90,7 @@ static int report__config(const char *var, const char *value, void *cb) return 0; } - return perf_default_config(var, value, cb); + return 0; } static int hist_iter__report_callback(struct hist_entry_iter *iter, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index ec4fbd410a4b..57f9a7e7f7d3 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1212,23 +1212,6 @@ static struct script_spec *script_spec__find(const char *spec) return NULL; } -static struct script_spec *script_spec__findnew(const char *spec, - struct scripting_ops *ops) -{ - struct script_spec *s = script_spec__find(spec); - - if (s) - return s; - - s = script_spec__new(spec, ops); - if (!s) - return NULL; - - script_spec__add(s); - - return s; -} - int script_spec_register(const char *spec, struct scripting_ops *ops) { struct script_spec *s; @@ -1237,9 +1220,11 @@ int script_spec_register(const char *spec, struct scripting_ops *ops) if (s) return -1; - s = script_spec__findnew(spec, ops); + s = script_spec__new(spec, ops); if (!s) return -1; + else + script_spec__add(s); return 0; } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b86b623e8799..94af190f6843 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1065,7 +1065,7 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) return parse_callchain_top_opt(arg); } -static int perf_top_config(const char *var, const char *value, void *cb) +static int perf_top_config(const char *var, const char *value, void *cb __maybe_unused) { if (!strcmp(var, "top.call-graph")) var = "call-graph.record-mode"; /* fall-through */ @@ -1074,7 +1074,7 @@ static int perf_top_config(const char *var, const char *value, void *cb) return 0; } - return perf_default_config(var, value, cb); + return 0; } static int diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 20916dd77aac..26a337f939d8 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -33,6 +33,7 @@ #include "util/stat.h" #include "trace-event.h" #include "util/parse-events.h" +#include "util/bpf-loader.h" #include <libaudit.h> #include <stdlib.h> @@ -2177,6 +2178,37 @@ out_dump: return 0; } +static void bpf_output__printer(enum binary_printer_ops op, + unsigned int val, void *extra) +{ + FILE *output = extra; + unsigned char ch = (unsigned char)val; + + switch (op) { + case BINARY_PRINT_CHAR_DATA: + fprintf(output, "%c", isprint(ch) ? ch : '.'); + break; + case BINARY_PRINT_DATA_BEGIN: + case BINARY_PRINT_LINE_BEGIN: + case BINARY_PRINT_ADDR: + case BINARY_PRINT_NUM_DATA: + case BINARY_PRINT_NUM_PAD: + case BINARY_PRINT_SEP: + case BINARY_PRINT_CHAR_PAD: + case BINARY_PRINT_LINE_END: + case BINARY_PRINT_DATA_END: + default: + break; + } +} + +static void bpf_output__fprintf(struct trace *trace, + struct perf_sample *sample) +{ + print_binary(sample->raw_data, sample->raw_size, 8, + bpf_output__printer, trace->output); +} + static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, union perf_event *event __maybe_unused, struct perf_sample *sample) @@ -2189,7 +2221,9 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, fprintf(trace->output, "%s:", evsel->name); - if (evsel->tp_format) { + if (perf_evsel__is_bpf_output(evsel)) { + bpf_output__fprintf(trace, sample); + } else if (evsel->tp_format) { event_format__fprintf(evsel->tp_format, sample->cpu, sample->raw_data, sample->raw_size, trace->output); @@ -2586,6 +2620,16 @@ static int trace__run(struct trace *trace, int argc, const char **argv) if (err < 0) goto out_error_open; + err = bpf__apply_obj_config(); + if (err) { + char errbuf[BUFSIZ]; + + bpf__strerror_apply_obj_config(err, errbuf, sizeof(errbuf)); + pr_err("ERROR: Apply config to BPF failed: %s\n", + errbuf); + goto out_error_open; + } + /* * Better not use !target__has_task() here because we need to cover the * case where no threads were specified in the command line, but a diff --git a/tools/perf/jvmti/Makefile b/tools/perf/jvmti/Makefile index 0277a64b391b..5ce61a1bda9c 100644 --- a/tools/perf/jvmti/Makefile +++ b/tools/perf/jvmti/Makefile @@ -35,12 +35,21 @@ SOLIBEXT=so # The following works at least on fedora 23, you may need the next # line for other distros. -ifeq (,$(wildcard /usr/sbin/update-java-alternatives)) -JDIR=$(shell alternatives --display java | tail -1 | cut -d' ' -f 5 | sed 's%/jre/bin/java.%%g') -else +ifneq (,$(wildcard /usr/sbin/update-java-alternatives)) JDIR=$(shell /usr/sbin/update-java-alternatives -l | head -1 | cut -d ' ' -f 3) +else + ifneq (,$(wildcard /usr/sbin/alternatives)) + JDIR=$(shell alternatives --display java | tail -1 | cut -d' ' -f 5 | sed 's%/jre/bin/java.%%g') + endif endif - +ifndef JDIR +$(error Could not find alternatives command, you need to set JDIR= to point to the root of your Java directory) +else + ifeq (,$(wildcard $(JDIR)/include/jvmti.h)) + $(error the openjdk development package appears to me missing, install and try again) + endif +endif +$(info Using Java from $(JDIR)) # -lrt required in 32-bit mode for clock_gettime() LIBS=-lelf -lrt INCDIR=-I $(JDIR)/include -I $(JDIR)/include/linux diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 144047c396f0..aaee0a782747 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -454,11 +454,12 @@ static void handle_internal_command(int argc, const char **argv) static void execv_dashed_external(const char **argv) { - struct strbuf cmd = STRBUF_INIT; + char *cmd; const char *tmp; int status; - strbuf_addf(&cmd, "perf-%s", argv[0]); + if (asprintf(&cmd, "perf-%s", argv[0]) < 0) + goto do_die; /* * argv[0] must be the perf command, but the argv array @@ -467,7 +468,7 @@ static void execv_dashed_external(const char **argv) * restore it on error. */ tmp = argv[0]; - argv[0] = cmd.buf; + argv[0] = cmd; /* * if we fail because the command is not found, it is @@ -475,15 +476,16 @@ static void execv_dashed_external(const char **argv) */ status = run_command_v_opt(argv, 0); if (status != -ERR_RUN_COMMAND_EXEC) { - if (IS_RUN_COMMAND_ERR(status)) + if (IS_RUN_COMMAND_ERR(status)) { +do_die: die("unable to run '%s'", argv[0]); + } exit(-status); } errno = ENOENT; /* as if we called execvp */ argv[0] = tmp; - - strbuf_release(&cmd); + zfree(&cmd); } static int run_argv(int *argcp, const char ***argv) @@ -546,6 +548,8 @@ int main(int argc, const char **argv) srandom(time(NULL)); + perf_config(perf_default_config, NULL); + /* get debugfs/tracefs mount point from /proc/mounts */ tracing_path_mount(); diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index 70edcdfa5672..cff564fb4b66 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c @@ -6,12 +6,6 @@ #include "tests.h" #include "debug.h" -static int perf_config_cb(const char *var, const char *val, - void *arg __maybe_unused) -{ - return perf_default_config(var, val, arg); -} - #ifdef HAVE_LIBBPF_SUPPORT static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) { @@ -77,8 +71,6 @@ test_llvm__fetch_bpf_obj(void **p_obj_buf, if (should_load_fail) *should_load_fail = bpf_source_table[idx].should_load_fail; - perf_config(perf_config_cb, NULL); - /* * Skip this test if user's .perfconfig doesn't set [llvm] section * and clang is not found in $PATH, and this is not perf test -v diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 6bcd7670ce5f..5ffffcb1e3c5 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -260,6 +260,9 @@ static int hierarchy_count_rows(struct hist_browser *hb, struct hist_entry *he, if (he->leaf) return callchain__count_rows(&he->sorted_chain); + if (he->has_no_entry) + return 1; + node = rb_first(&he->hroot_out); while (node) { float percent; @@ -409,10 +412,18 @@ static bool hist_browser__toggle_fold(struct hist_browser *browser) /* account grand children */ if (symbol_conf.report_hierarchy) browser->b.nr_entries += child_rows - he->nr_rows; + + if (!he->leaf && he->nr_rows == 0) { + he->has_no_entry = true; + he->nr_rows = 1; + } } else { if (symbol_conf.report_hierarchy) browser->b.nr_entries -= child_rows - he->nr_rows; + if (he->has_no_entry) + he->has_no_entry = false; + he->nr_rows = 0; } @@ -545,6 +556,12 @@ __hist_browser__set_folding(struct hist_browser *browser, bool unfold) browser->nr_hierarchy_entries++; if (he->leaf) browser->nr_callchain_rows += he->nr_rows; + else if (unfold && !hist_entry__has_hierarchy_children(he, browser->min_pcnt)) { + browser->nr_hierarchy_entries++; + he->has_no_entry = true; + he->nr_rows = 1; + } else + he->has_no_entry = false; } } @@ -1383,8 +1400,13 @@ static int hist_browser__show_hierarchy_entry(struct hist_browser *browser, if (fmt->color) { width -= fmt->color(fmt, &hpp, entry); } else { + int i = 0; + width -= fmt->entry(fmt, &hpp, entry); - ui_browser__printf(&browser->b, "%s", s); + ui_browser__printf(&browser->b, "%s", ltrim(s)); + + while (isspace(s[i++])) + width++; } } @@ -1412,6 +1434,75 @@ show_callchain: return printed; } +static int hist_browser__show_no_entry(struct hist_browser *browser, + unsigned short row, + int level, int nr_sort_keys) +{ + int width = browser->b.width; + bool current_entry = ui_browser__is_current_entry(&browser->b, row); + bool first = true; + int column = 0; + int ret; + struct perf_hpp_fmt *fmt; + + if (current_entry) { + browser->he_selection = NULL; + browser->selection = NULL; + } + + hist_browser__gotorc(browser, row, 0); + + if (current_entry && browser->b.navkeypressed) + ui_browser__set_color(&browser->b, HE_COLORSET_SELECTED); + else + ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); + + ui_browser__write_nstring(&browser->b, "", level * HIERARCHY_INDENT); + width -= level * HIERARCHY_INDENT; + + hists__for_each_format(browser->hists, fmt) { + if (perf_hpp__should_skip(fmt, browser->hists) || + column++ < browser->b.horiz_scroll) + continue; + + if (perf_hpp__is_sort_entry(fmt) || + perf_hpp__is_dynamic_entry(fmt)) + break; + + ret = fmt->width(fmt, NULL, hists_to_evsel(browser->hists)); + + if (first) { + /* for folded sign */ + first = false; + ret++; + } else { + /* space between columns */ + ret += 2; + } + + ui_browser__write_nstring(&browser->b, "", ret); + width -= ret; + } + + ui_browser__write_nstring(&browser->b, "", nr_sort_keys * HIERARCHY_INDENT); + width -= nr_sort_keys * HIERARCHY_INDENT; + + if (column >= browser->b.horiz_scroll) { + char buf[32]; + + ret = snprintf(buf, sizeof(buf), "no entry >= %.2f%%", browser->min_pcnt); + ui_browser__printf(&browser->b, " %s", buf); + width -= ret + 2; + } + + /* The scroll bar isn't being used */ + if (!browser->b.navkeypressed) + width += 1; + + ui_browser__write_nstring(&browser->b, "", width); + return 1; +} + static int advance_hpp_check(struct perf_hpp *hpp, int inc) { advance_hpp(hpp, inc); @@ -1461,7 +1552,7 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows struct perf_hpp_fmt *fmt; size_t ret = 0; int column = 0; - int nr_sort_keys = hists->hpp_list->nr_sort_keys; + int nr_sort_keys = hists->nr_sort_keys; bool first = true; ret = scnprintf(buf, size, " "); @@ -1490,6 +1581,8 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows return ret; hists__for_each_format(hists, fmt) { + char *start; + if (!perf_hpp__is_sort_entry(fmt) && !perf_hpp__is_dynamic_entry(fmt)) continue; if (perf_hpp__should_skip(fmt, hists)) @@ -1507,7 +1600,12 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows dummy_hpp.buf[ret] = '\0'; rtrim(dummy_hpp.buf); - ret = strlen(dummy_hpp.buf); + start = ltrim(dummy_hpp.buf); + ret = strlen(start); + + if (start != dummy_hpp.buf) + memmove(dummy_hpp.buf, start, ret + 1); + if (advance_hpp_check(&dummy_hpp, ret)) break; } @@ -1546,7 +1644,7 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) u16 header_offset = 0; struct rb_node *nd; struct hist_browser *hb = container_of(browser, struct hist_browser, b); - int nr_sort = hb->hists->hpp_list->nr_sort_keys; + int nr_sort = hb->hists->nr_sort_keys; if (hb->show_headers) { hist_browser__show_headers(hb); @@ -1575,6 +1673,14 @@ static unsigned int hist_browser__refresh(struct ui_browser *browser) row += hist_browser__show_hierarchy_entry(hb, h, row, h->depth, nr_sort); + if (row == browser->rows) + break; + + if (h->has_no_entry) { + hist_browser__show_no_entry(hb, row, h->depth, + nr_sort); + row++; + } } else { row += hist_browser__show_entry(hb, h, row); } @@ -1875,7 +1981,7 @@ static int hist_browser__fprintf(struct hist_browser *browser, FILE *fp) struct rb_node *nd = hists__filter_entries(rb_first(browser->b.entries), browser->min_pcnt); int printed = 0; - int nr_sort = browser->hists->hpp_list->nr_sort_keys; + int nr_sort = browser->hists->nr_sort_keys; while (nd) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); @@ -2461,6 +2567,11 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb, while ((nd = hists__filter_entries(nd, hb->min_pcnt)) != NULL) { he = rb_entry(nd, struct hist_entry, rb_node); + if (he->has_no_entry) { + he->has_no_entry = false; + he->nr_rows = 0; + } + if (!he->leaf || !symbol_conf.use_callchain) goto next; @@ -2477,12 +2588,7 @@ static void hist_browser__update_percent_limit(struct hist_browser *hb, min_callchain_hits, &callchain_param); next: - /* - * Tentatively set unfolded so that the rb_hierarchy_next() - * can toggle children of folded entries too. - */ - he->unfolded = he->has_children; - nd = rb_hierarchy_next(nd); + nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD); /* force to re-evaluate folding state of callchains */ he->init_have_children = false; diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 7f343339eae7..a5758fdfbe1f 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c @@ -449,6 +449,17 @@ static void perf_gtk__add_hierarchy_entries(struct hists *hists, perf_gtk__add_hierarchy_entries(hists, &he->hroot_out, store, &iter, hpp, min_pcnt); + + if (!hist_entry__has_hierarchy_children(he, min_pcnt)) { + char buf[32]; + GtkTreeIter child; + + snprintf(buf, sizeof(buf), "no entry >= %.2f%%", + min_pcnt); + + gtk_tree_store_append(store, &child, &iter); + gtk_tree_store_set(store, &child, col_idx, buf, -1); + } } if (symbol_conf.use_callchain && he->leaf) { diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index edbf854e8e1c..7c0585c146e1 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -643,6 +643,28 @@ unsigned int hists__sort_list_width(struct hists *hists) return ret; } +unsigned int hists__overhead_width(struct hists *hists) +{ + struct perf_hpp_fmt *fmt; + int ret = 0; + bool first = true; + struct perf_hpp dummy_hpp; + + hists__for_each_format(hists, fmt) { + if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) + break; + + if (first) + first = false; + else + ret += 2; + + ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists)); + } + + return ret; +} + void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) { if (perf_hpp__is_sort_entry(fmt)) diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 435eaaaf2f1d..6d06fbb365b6 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -418,6 +418,7 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he, const char *sep = symbol_conf.field_sep; struct perf_hpp_fmt *fmt; char *buf = hpp->buf; + size_t size = hpp->size; int ret, printed = 0; bool first = true; @@ -457,6 +458,11 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he, (nr_sort_key - 1) * HIERARCHY_INDENT + 2, ""); advance_hpp(hpp, ret); + printed += fprintf(fp, "%s", buf); + + hpp->buf = buf; + hpp->size = size; + /* * No need to call hist_entry__snprintf_alignment() since this * fmt is always the last column in the hierarchy mode. @@ -467,7 +473,11 @@ static int hist_entry__hierarchy_fprintf(struct hist_entry *he, else fmt->entry(fmt, hpp, he); - printed += fprintf(fp, "%s\n", buf); + /* + * dynamic entries are right-aligned but we want left-aligned + * in the hierarchy mode + */ + printed += fprintf(fp, "%s\n", ltrim(buf)); if (symbol_conf.use_callchain && he->leaf) { u64 total = hists__total_period(hists); @@ -495,7 +505,7 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, size = hpp.size = bfsz; if (symbol_conf.report_hierarchy) { - int nr_sort = hists->hpp_list->nr_sort_keys; + int nr_sort = hists->nr_sort_keys; return hist_entry__hierarchy_fprintf(he, &hpp, nr_sort, hists, fp); @@ -525,11 +535,12 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, { bool first = true; int nr_sort; + int depth; unsigned width = 0; unsigned header_width = 0; struct perf_hpp_fmt *fmt; - nr_sort = hists->hpp_list->nr_sort_keys; + nr_sort = hists->nr_sort_keys; /* preserve max indent depth for column headers */ print_hierarchy_indent(sep, nr_sort, spaces, fp); @@ -558,19 +569,16 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, if (!first) header_width += fprintf(fp, " / "); else { - header_width += fprintf(fp, "%s", sep ?: " "); + fprintf(fp, "%s", sep ?: " "); first = false; } fmt->header(fmt, hpp, hists_to_evsel(hists)); rtrim(hpp->buf); - header_width += fprintf(fp, "%s", hpp->buf); + header_width += fprintf(fp, "%s", ltrim(hpp->buf)); } - /* preserve max indent depth for combined sort headers */ - print_hierarchy_indent(sep, nr_sort, spaces, fp); - fprintf(fp, "\n# "); /* preserve max indent depth for initial dots */ @@ -590,6 +598,7 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, fprintf(fp, "%.*s", width, dots); } + depth = 0; hists__for_each_format(hists, fmt) { if (!perf_hpp__is_sort_entry(fmt) && !perf_hpp__is_dynamic_entry(fmt)) continue; @@ -597,15 +606,16 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp, continue; width = fmt->width(fmt, hpp, hists_to_evsel(hists)); + width += depth * HIERARCHY_INDENT; + if (width > header_width) header_width = width; + + depth++; } fprintf(fp, "%s%-.*s", sep ?: " ", header_width, dots); - /* preserve max indent depth for dots under sort headers */ - print_hierarchy_indent(sep, nr_sort, dots, fp); - fprintf(fp, "\n#\n"); return 2; @@ -628,6 +638,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, bool first = true; size_t linesz; char *line = NULL; + unsigned indent; init_rem_hits(); @@ -704,6 +715,8 @@ print_entries: goto out; } + indent = hists__overhead_width(hists) + 4; + for (nd = rb_first(&hists->entries); nd; nd = __rb_hierarchy_next(nd, HMD_FORCE_CHILD)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); float percent; @@ -720,6 +733,20 @@ print_entries: if (max_rows && ++nr_rows >= max_rows) break; + /* + * If all children are filtered out or percent-limited, + * display "no entry >= x.xx%" message. + */ + if (!h->leaf && !hist_entry__has_hierarchy_children(h, min_pcnt)) { + int nr_sort = hists->nr_sort_keys; + + print_hierarchy_indent(sep, nr_sort + h->depth + 1, spaces, fp); + fprintf(fp, "%*sno entry >= %.2f%%\n", indent, "", min_pcnt); + + if (max_rows && ++nr_rows >= max_rows) + break; + } + if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(h->thread->mg, MAP__FUNCTION, fp); diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index e5fb88bab9e1..43e84aa27e4a 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -32,14 +32,15 @@ int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty) return 0; } -int perf_color_default_config(const char *var, const char *value, void *cb) +int perf_color_default_config(const char *var, const char *value, + void *cb __maybe_unused) { if (!strcmp(var, "color.ui")) { perf_use_color_default = perf_config_colorbool(var, value, -1); return 0; } - return perf_default_config(var, value, cb); + return 0; } static int __color_vsnprintf(char *bf, size_t size, const char *color, diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index b722e57d5a87..6729f4d9df7c 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1117,7 +1117,7 @@ static int convert__config(const char *var, const char *value, void *cb) return 0; } - return perf_default_config(var, value, cb); + return 0; } int bt_convert__perf2ctf(const char *input, const char *path, bool force) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c42e1967e970..86a03836a83f 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1223,6 +1223,9 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) int err = 0; evlist__for_each(evlist, evsel) { + if (evsel->attr.type != PERF_TYPE_TRACEPOINT) + continue; + err = perf_evsel__set_filter(evsel, filter); if (err) break; diff --git a/tools/perf/util/help-unknown-cmd.c b/tools/perf/util/help-unknown-cmd.c index dc1e41c9b054..43a98a4dc1e1 100644 --- a/tools/perf/util/help-unknown-cmd.c +++ b/tools/perf/util/help-unknown-cmd.c @@ -6,7 +6,8 @@ static int autocorrect; static struct cmdnames aliases; -static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) +static int perf_unknown_cmd_config(const char *var, const char *value, + void *cb __maybe_unused) { if (!strcmp(var, "help.autocorrect")) autocorrect = perf_config_int(var,value); @@ -14,7 +15,7 @@ static int perf_unknown_cmd_config(const char *var, const char *value, void *cb) if (!prefixcmp(var, "alias.")) add_cmdname(&aliases, var + 6, strlen(var + 6)); - return perf_default_config(var, value, cb); + return 0; } static int levenshtein_compare(const void *p1, const void *p2) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 1c530428e087..4b8b67bc0cd8 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1002,6 +1002,10 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; hists__for_each_sort_list(hists, fmt) { + if (perf_hpp__is_dynamic_entry(fmt) && + !perf_hpp__defined_dynamic_entry(fmt, hists)) + continue; + cmp = fmt->cmp(fmt, left, right); if (cmp) break; @@ -1018,6 +1022,10 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) int64_t cmp = 0; hists__for_each_sort_list(hists, fmt) { + if (perf_hpp__is_dynamic_entry(fmt) && + !perf_hpp__defined_dynamic_entry(fmt, hists)) + continue; + cmp = fmt->collapse(fmt, left, right); if (cmp) break; @@ -1117,7 +1125,7 @@ static struct hist_entry *hierarchy_insert_entry(struct hists *hists, new->fmt = fmt; /* some fields are now passed to 'new' */ - if (perf_hpp__is_trace_entry(fmt)) + if (perf_hpp__is_trace_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) he->trace_output = NULL; else new->trace_output = NULL; @@ -1363,6 +1371,10 @@ static void hierarchy_insert_output_entry(struct rb_root *root, rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, root); + + /* update column width of dynamic entry */ + if (perf_hpp__is_dynamic_entry(he->fmt)) + he->fmt->sort(he->fmt, he, NULL); } static void hists__hierarchy_output_resort(struct hists *hists, @@ -1432,6 +1444,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, struct rb_node **p = &entries->rb_node; struct rb_node *parent = NULL; struct hist_entry *iter; + struct perf_hpp_fmt *fmt; if (use_callchain) { if (callchain_param.mode == CHAIN_GRAPH_REL) { @@ -1458,6 +1471,12 @@ static void __hists__insert_output_entry(struct rb_root *entries, rb_link_node(&he->rb_node, parent, p); rb_insert_color(&he->rb_node, entries); + + perf_hpp_list__for_each_sort_list(&perf_hpp_list, fmt) { + if (perf_hpp__is_dynamic_entry(fmt) && + perf_hpp__defined_dynamic_entry(fmt, he->hists)) + fmt->sort(fmt, he, NULL); /* update column width */ + } } static void output_resort(struct hists *hists, struct ui_progress *prog, @@ -1582,6 +1601,31 @@ struct rb_node *rb_hierarchy_prev(struct rb_node *node) return &he->rb_node; } +bool hist_entry__has_hierarchy_children(struct hist_entry *he, float limit) +{ + struct rb_node *node; + struct hist_entry *child; + float percent; + + if (he->leaf) + return false; + + node = rb_first(&he->hroot_out); + child = rb_entry(node, struct hist_entry, rb_node); + + while (node && child->filtered) { + node = rb_next(node); + child = rb_entry(node, struct hist_entry, rb_node); + } + + if (node) + percent = hist_entry__get_percent_limit(child); + else + percent = 0; + + return node && percent >= limit; +} + static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, enum hist_filter filter) { @@ -1600,6 +1644,7 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h /* force fold unfiltered entry for simplicity */ parent->unfolded = false; + parent->has_no_entry = false; parent->row_offset = 0; parent->nr_rows = 0; next: @@ -1612,6 +1657,7 @@ next: /* force fold unfiltered entry for simplicity */ h->unfolded = false; + h->has_no_entry = false; h->row_offset = 0; h->nr_rows = 0; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 722aa447f705..da5e50586bfd 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -78,6 +78,7 @@ struct hists { u16 col_len[HISTC_NR_COLS]; int socket_filter; struct perf_hpp_list *hpp_list; + int nr_sort_keys; }; struct hist_entry_iter; @@ -410,6 +411,7 @@ static inline int script_browse(const char *script_opt __maybe_unused) #endif unsigned int hists__sort_list_width(struct hists *hists); +unsigned int hists__overhead_width(struct hists *hists); void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, struct perf_sample *sample, bool nonany_branch_mode); @@ -439,4 +441,6 @@ static inline struct rb_node *rb_hierarchy_next(struct rb_node *node) #define HIERARCHY_INDENT 3 +bool hist_entry__has_hierarchy_children(struct hist_entry *he, float limit); + #endif /* __PERF_HIST_H */ diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index d1fbcabbe70d..85c44ba79cad 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -217,14 +217,14 @@ event_def: event_pmu | event_bpf_file event_pmu: -PE_NAME '/' event_config '/' +PE_NAME opt_event_config { struct parse_events_evlist *data = _data; struct list_head *list; ALLOC_LIST(list); - ABORT_ON(parse_events_add_pmu(data, list, $1, $3)); - parse_events_terms__delete($3); + ABORT_ON(parse_events_add_pmu(data, list, $1, $2)); + parse_events_terms__delete($2); $$ = list; } | diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 544509c159ce..b3aabc0d4eb0 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -187,6 +187,9 @@ static void define_event_symbols(struct event_format *event, const char *ev_name, struct print_arg *args) { + if (args == NULL) + return; + switch (args->type) { case PRINT_NULL: break; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index d72fafc1c800..309d90fa7698 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -205,6 +205,9 @@ static void define_event_symbols(struct event_format *event, const char *ev_name, struct print_arg *args) { + if (args == NULL) + return; + switch (args->type) { case PRINT_NULL: break; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 6bee8bdfb91b..5888bfe9a193 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -1764,6 +1764,9 @@ static int __sort__hde_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, if (hde->raw_trace) goto raw_field; + if (!he->trace_output) + he->trace_output = get_trace_output(he); + field = hde->field; namelen = strlen(field->name); str = he->trace_output; @@ -1813,6 +1816,11 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, hde = container_of(fmt, struct hpp_dynamic_entry, hpp); + if (b == NULL) { + update_dynamic_len(hde, a); + return 0; + } + field = hde->field; if (field->flags & FIELD_IS_DYNAMIC) { unsigned long long dyn; @@ -1827,9 +1835,6 @@ static int64_t __sort__hde_cmp(struct perf_hpp_fmt *fmt, } else { offset = field->offset; size = field->size; - - update_dynamic_len(hde, a); - update_dynamic_len(hde, b); } return memcmp(a->raw_data + offset, b->raw_data + offset, size); @@ -2633,6 +2638,9 @@ out: int setup_sorting(struct perf_evlist *evlist) { int err; + struct hists *hists; + struct perf_evsel *evsel; + struct perf_hpp_fmt *fmt; err = __setup_sorting(evlist); if (err < 0) @@ -2644,6 +2652,22 @@ int setup_sorting(struct perf_evlist *evlist) return err; } + evlist__for_each(evlist, evsel) { + hists = evsel__hists(evsel); + hists->nr_sort_keys = perf_hpp_list.nr_sort_keys; + + /* + * If dynamic entries were used, it might add multiple + * entries to each evsel for a single field name. Set + * actual number of sort keys for each hists. + */ + perf_hpp_list__for_each_sort_list(&perf_hpp_list, fmt) { + if (perf_hpp__is_dynamic_entry(fmt) && + !perf_hpp__defined_dynamic_entry(fmt, hists)) + hists->nr_sort_keys--; + } + } + reset_dimensions(); /* diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index a8d53ffe0916..25a5529a94e4 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -117,6 +117,7 @@ struct hist_entry { bool init_have_children; bool unfolded; bool has_children; + bool has_no_entry; }; }; char *srcline; |