diff options
-rw-r--r-- | tools/perf/Documentation/perf-lock.txt | 6 | ||||
-rw-r--r-- | tools/perf/builtin-lock.c | 68 | ||||
-rw-r--r-- | tools/perf/util/bpf_lock_contention.c | 2 | ||||
-rw-r--r-- | tools/perf/util/lock-contention.h | 1 |
4 files changed, 75 insertions, 2 deletions
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index 0f9f720e599d..11b8901d8d13 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -187,6 +187,12 @@ CONTENTION OPTIONS --lock-filter=<value>:: Show lock contention only for given lock addresses or names (comma separated list). +-S:: +--callstack-filter=<value>:: + Show lock contention only if the callstack contains the given string. + Note that it matches the substring so 'rq' would match both 'raw_spin_rq_lock' + and 'irq_enter_rcu'. + SEE ALSO -------- diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 506c2fe42d52..216a9a252bf4 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -63,11 +63,22 @@ static unsigned long bpf_map_entries = 10240; static int max_stack_depth = CONTENTION_STACK_DEPTH; static int stack_skip = CONTENTION_STACK_SKIP; static int print_nr_entries = INT_MAX / 2; +static LIST_HEAD(callstack_filters); + +struct callstack_filter { + struct list_head list; + char name[]; +}; static struct lock_filter filters; static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR; +static bool needs_callstack(void) +{ + return verbose > 0 || !list_empty(&callstack_filters); +} + static struct thread_stat *thread_stat_find(u32 tid) { struct rb_node *node; @@ -1060,7 +1071,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel, if (!ls) return -ENOMEM; - if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) { + if (aggr_mode == LOCK_AGGR_CALLER && needs_callstack()) { ls->callstack = get_callstack(sample, max_stack_depth); if (ls->callstack == NULL) return -ENOMEM; @@ -1595,6 +1606,31 @@ static void print_contention_result(struct lock_contention *con) if (!st->wait_time_total) continue; + if (aggr_mode == LOCK_AGGR_CALLER && !list_empty(&callstack_filters)) { + struct map *kmap; + struct symbol *sym; + u64 ip; + + for (int i = 0; i < max_stack_depth; i++) { + struct callstack_filter *filter; + + if (!st->callstack || !st->callstack[i]) + break; + + ip = st->callstack[i]; + sym = machine__find_kernel_symbol(con->machine, ip, &kmap); + if (sym == NULL) + continue; + + list_for_each_entry(filter, &callstack_filters, list) { + if (strstr(sym->name, filter->name)) + goto found; + } + } + continue; + } + +found: list_for_each_entry(key, &lock_keys, list) { key->print(key, st); pr_info(" "); @@ -1743,6 +1779,7 @@ static int __cmd_contention(int argc, const char **argv) .max_stack = max_stack_depth, .stack_skip = stack_skip, .filters = &filters, + .save_callstack = needs_callstack(), }; session = perf_session__new(use_bpf ? NULL : &data, &eops); @@ -2123,6 +2160,33 @@ static int parse_lock_addr(const struct option *opt __maybe_unused, const char * return ret; } +static int parse_call_stack(const struct option *opt __maybe_unused, const char *str, + int unset __maybe_unused) +{ + char *s, *tmp, *tok; + int ret = 0; + + s = strdup(str); + if (s == NULL) + return -1; + + for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { + struct callstack_filter *entry; + + entry = malloc(sizeof(*entry) + strlen(tok) + 1); + if (entry == NULL) { + pr_err("Memory allocation failure\n"); + return -1; + } + + strcpy(entry->name, tok); + list_add_tail(&entry->list, &callstack_filters); + } + + free(s); + return ret; +} + int cmd_lock(int argc, const char **argv) { const struct option lock_options[] = { @@ -2190,6 +2254,8 @@ int cmd_lock(int argc, const char **argv) "Filter specific type of locks", parse_lock_type), OPT_CALLBACK('L', "lock-filter", NULL, "ADDRS/NAMES", "Filter specific address/symbol of locks", parse_lock_addr), + OPT_CALLBACK('S', "callstack-filter", NULL, "NAMES", + "Filter specific function in the callstack", parse_call_stack), OPT_PARENT(lock_options) }; diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c index 0236334fd69b..4902ac331f41 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -268,7 +268,7 @@ int lock_contention_read(struct lock_contention *con) break; } - if (verbose > 0) { + if (con->save_callstack) { st->callstack = memdup(stack_trace, stack_size); if (st->callstack == NULL) break; diff --git a/tools/perf/util/lock-contention.h b/tools/perf/util/lock-contention.h index b99e83fccf5c..17e594d57a61 100644 --- a/tools/perf/util/lock-contention.h +++ b/tools/perf/util/lock-contention.h @@ -128,6 +128,7 @@ struct lock_contention { int max_stack; int stack_skip; int aggr_mode; + bool save_callstack; }; #ifdef HAVE_BPF_SKEL |