From 6d491b37e70daeb963e3b589b746d99b8b4b1357 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 10 May 2023 23:27:25 -0700 Subject: perf annotate browser: Add '<' and '>' keys for navigation hists__find_annotations() allows to move to next or previous symbols for annotation using the arrow keys. But TUI annotate_browser__run() uses the RIGHT key as ENTER to handle jump/call instructions. That makes the navigation to the next function impossible. I'd like to change it back to move the next symbol but I'm afraid if some users get confused. So I added a new pair of keys to handle that. Signed-off-by: Namhyung Kim Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20230511062725.514752-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools/perf/ui/browsers/annotate.c') diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 12c3ce530e42..70bad42b807b 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -781,9 +781,9 @@ static int annotate_browser__run(struct annotate_browser *browser, ui_browser__help_window(&browser->b, "UP/DOWN/PGUP\n" "PGDN/SPACE Navigate\n" + " Move to prev/next symbol\n" "q/ESC/CTRL+C Exit\n\n" "ENTER Go to target\n" - "ESC Exit\n" "H Go to hottest instruction\n" "TAB/shift+TAB Cycle thru hottest instructions\n" "j Toggle showing jump to target arrows\n" @@ -913,6 +913,8 @@ show_sup_ins: annotation__toggle_full_addr(notes, ms); continue; case K_LEFT: + case '<': + case '>': case K_ESC: case 'q': case CTRL('c'): -- cgit v1.2.3 From 2e9f9d4a729f12b4bc3fa60406374327b1809abe Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 14 Jun 2023 21:07:15 -0700 Subject: perf annotation: Switch lock from a mutex to a sharded_mutex Remove the "struct mutex lock" variable from annotation that is allocated per symbol. This removes in the region of 40 bytes per symbol allocation. Use a sharded mutex where the number of shards is set to the number of CPUs. Assuming good hashing of the annotation (done based on the pointer), this means in order to contend there needs to be more threads than CPUs, which is not currently true in any perf command. Were contention an issue it is straightforward to increase the number of shards in the mutex. On my Debian/glibc based machine, this reduces the size of struct annotation from 136 bytes to 96 bytes, or nearly 30%. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Andres Freund Cc: Mark Rutland Cc: Yuan Can Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Huacai Chen Cc: Jiri Olsa Cc: Masami Hiramatsu Cc: Alexander Shishkin Cc: Kan Liang Cc: Ingo Molnar Link: https://lore.kernel.org/r/20230615040715.2064350-2-irogers@google.com Signed-off-by: Namhyung Kim --- tools/perf/builtin-top.c | 14 ++++----- tools/perf/ui/browsers/annotate.c | 10 +++--- tools/perf/util/annotate.c | 66 +++++++++++++++++++++++++++++++++------ tools/perf/util/annotate.h | 11 +++++-- 4 files changed, 77 insertions(+), 24 deletions(-) (limited to 'tools/perf/ui/browsers/annotate.c') diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index c363c04e16df..1baa2acb3ced 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -137,10 +137,10 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he) } notes = symbol__annotation(sym); - mutex_lock(¬es->lock); + annotation__lock(notes); if (!symbol__hists(sym, top->evlist->core.nr_entries)) { - mutex_unlock(¬es->lock); + annotation__unlock(notes); pr_err("Not enough memory for annotating '%s' symbol!\n", sym->name); sleep(1); @@ -156,7 +156,7 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he) pr_err("Couldn't annotate %s: %s\n", sym->name, msg); } - mutex_unlock(¬es->lock); + annotation__unlock(notes); return err; } @@ -211,12 +211,12 @@ static void perf_top__record_precise_ip(struct perf_top *top, notes = symbol__annotation(sym); - if (!mutex_trylock(¬es->lock)) + if (!annotation__trylock(notes)) return; err = hist_entry__inc_addr_samples(he, sample, evsel, ip); - mutex_unlock(¬es->lock); + annotation__unlock(notes); if (unlikely(err)) { /* @@ -253,7 +253,7 @@ static void perf_top__show_details(struct perf_top *top) symbol = he->ms.sym; notes = symbol__annotation(symbol); - mutex_lock(¬es->lock); + annotation__lock(notes); symbol__calc_percent(symbol, evsel); @@ -274,7 +274,7 @@ static void perf_top__show_details(struct perf_top *top) if (more != 0) printf("%d lines not displayed, maybe increase display entries [e]\n", more); out_unlock: - mutex_unlock(¬es->lock); + annotation__unlock(notes); } static void perf_top__resort_hists(struct perf_top *t) diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 70bad42b807b..ccdb2cd11fbf 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -314,7 +314,7 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, browser->entries = RB_ROOT; - mutex_lock(¬es->lock); + annotation__lock(notes); symbol__calc_percent(sym, evsel); @@ -343,7 +343,7 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, } disasm_rb_tree__insert(browser, &pos->al); } - mutex_unlock(¬es->lock); + annotation__unlock(notes); browser->curr_hot = rb_last(&browser->entries); } @@ -470,10 +470,10 @@ static bool annotate_browser__callq(struct annotate_browser *browser, } notes = symbol__annotation(dl->ops.target.sym); - mutex_lock(¬es->lock); + annotation__lock(notes); if (!symbol__hists(dl->ops.target.sym, evsel->evlist->core.nr_entries)) { - mutex_unlock(¬es->lock); + annotation__unlock(notes); ui__warning("Not enough memory for annotating '%s' symbol!\n", dl->ops.target.sym->name); return true; @@ -482,7 +482,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, target_ms.maps = ms->maps; target_ms.map = ms->map; target_ms.sym = dl->ops.target.sym; - mutex_unlock(¬es->lock); + annotation__unlock(notes); symbol__tui_annotate(&target_ms, evsel, hbt, browser->opts); sym_title(ms->sym, ms->map, title, sizeof(title), browser->opts->percent_type); ui_browser__show_title(&browser->b, title); diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 43865601f96c..77c816400719 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -32,6 +32,7 @@ #include "block-range.h" #include "string2.h" #include "util/event.h" +#include "util/sharded_mutex.h" #include "arch/common.h" #include "namespaces.h" #include @@ -856,7 +857,7 @@ void symbol__annotate_zero_histograms(struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); - mutex_lock(¬es->lock); + annotation__lock(notes); if (notes->src != NULL) { memset(notes->src->histograms, 0, notes->src->nr_histograms * notes->src->sizeof_sym_hist); @@ -864,7 +865,7 @@ void symbol__annotate_zero_histograms(struct symbol *sym) memset(notes->src->cycles_hist, 0, symbol__size(sym) * sizeof(struct cyc_hist)); } - mutex_unlock(¬es->lock); + annotation__unlock(notes); } static int __symbol__account_cycles(struct cyc_hist *ch, @@ -1121,7 +1122,7 @@ void annotation__compute_ipc(struct annotation *notes, size_t size) notes->hit_insn = 0; notes->cover_insn = 0; - mutex_lock(¬es->lock); + annotation__lock(notes); for (offset = size - 1; offset >= 0; --offset) { struct cyc_hist *ch; @@ -1140,7 +1141,7 @@ void annotation__compute_ipc(struct annotation *notes, size_t size) notes->have_cycles = true; } } - mutex_unlock(¬es->lock); + annotation__unlock(notes); } int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, @@ -1291,17 +1292,64 @@ int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool r return ins__scnprintf(&dl->ins, bf, size, &dl->ops, max_ins_name); } -void annotation__init(struct annotation *notes) +void annotation__exit(struct annotation *notes) { - mutex_init(¬es->lock); + annotated_source__delete(notes->src); } -void annotation__exit(struct annotation *notes) +static struct sharded_mutex *sharded_mutex; + +static void annotation__init_sharded_mutex(void) { - annotated_source__delete(notes->src); - mutex_destroy(¬es->lock); + /* As many mutexes as there are CPUs. */ + sharded_mutex = sharded_mutex__new(cpu__max_present_cpu().cpu); +} + +static size_t annotation__hash(const struct annotation *notes) +{ + return (size_t)notes; } +static struct mutex *annotation__get_mutex(const struct annotation *notes) +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + + pthread_once(&once, annotation__init_sharded_mutex); + if (!sharded_mutex) + return NULL; + + return sharded_mutex__get_mutex(sharded_mutex, annotation__hash(notes)); +} + +void annotation__lock(struct annotation *notes) + NO_THREAD_SAFETY_ANALYSIS +{ + struct mutex *mutex = annotation__get_mutex(notes); + + if (mutex) + mutex_lock(mutex); +} + +void annotation__unlock(struct annotation *notes) + NO_THREAD_SAFETY_ANALYSIS +{ + struct mutex *mutex = annotation__get_mutex(notes); + + if (mutex) + mutex_unlock(mutex); +} + +bool annotation__trylock(struct annotation *notes) +{ + struct mutex *mutex = annotation__get_mutex(notes); + + if (!mutex) + return false; + + return mutex_trylock(mutex); +} + + static void annotation_line__add(struct annotation_line *al, struct list_head *head) { list_add_tail(&al->node, head); diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 1c6335b8333a..962780559176 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -271,8 +271,7 @@ struct annotated_source { struct sym_hist *histograms; }; -struct annotation { - struct mutex lock; +struct LOCKABLE annotation { u64 max_coverage; u64 start; u64 hit_cycles; @@ -298,9 +297,15 @@ struct annotation { struct annotated_source *src; }; -void annotation__init(struct annotation *notes); +static inline void annotation__init(struct annotation *notes __maybe_unused) +{ +} void annotation__exit(struct annotation *notes); +void annotation__lock(struct annotation *notes) EXCLUSIVE_LOCK_FUNCTION(*notes); +void annotation__unlock(struct annotation *notes) UNLOCK_FUNCTION(*notes); +bool annotation__trylock(struct annotation *notes) EXCLUSIVE_TRYLOCK_FUNCTION(true, *notes); + static inline int annotation__cycles_width(struct annotation *notes) { if (notes->have_cycles && notes->options->show_minmax_cycle) -- cgit v1.2.3