diff options
Diffstat (limited to 'tools/perf/builtin-lock.c')
-rw-r--r-- | tools/perf/builtin-lock.c | 223 |
1 files changed, 68 insertions, 155 deletions
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 33a456980664..5d405cd8e696 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -46,15 +46,6 @@ static struct perf_session *session; static struct target target; -/* based on kernel/lockdep.c */ -#define LOCKHASH_BITS 12 -#define LOCKHASH_SIZE (1UL << LOCKHASH_BITS) - -static struct hlist_head *lockhash_table; - -#define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) -#define lockhashentry(key) (lockhash_table + __lockhashfn((key))) - static struct rb_root thread_stats; static bool combine_locks; @@ -67,24 +58,13 @@ static unsigned long bpf_map_entries = MAX_ENTRIES; 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); static const char *output_name = NULL; static FILE *lock_output; -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 !list_empty(&callstack_filters); -} - static struct thread_stat *thread_stat_find(u32 tid) { struct rb_node *node; @@ -477,93 +457,6 @@ static struct lock_stat *pop_from_result(void) return container_of(node, struct lock_stat, rb); } -struct lock_stat *lock_stat_find(u64 addr) -{ - struct hlist_head *entry = lockhashentry(addr); - struct lock_stat *ret; - - hlist_for_each_entry(ret, entry, hash_entry) { - if (ret->addr == addr) - return ret; - } - return NULL; -} - -struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags) -{ - struct hlist_head *entry = lockhashentry(addr); - struct lock_stat *ret, *new; - - hlist_for_each_entry(ret, entry, hash_entry) { - if (ret->addr == addr) - return ret; - } - - new = zalloc(sizeof(struct lock_stat)); - if (!new) - goto alloc_failed; - - new->addr = addr; - new->name = strdup(name); - if (!new->name) { - free(new); - goto alloc_failed; - } - - new->flags = flags; - new->wait_time_min = ULLONG_MAX; - - hlist_add_head(&new->hash_entry, entry); - return new; - -alloc_failed: - pr_err("memory allocation failed\n"); - return NULL; -} - -bool match_callstack_filter(struct machine *machine, u64 *callstack) -{ - struct map *kmap; - struct symbol *sym; - u64 ip; - const char *arch = perf_env__arch(machine->env); - - if (list_empty(&callstack_filters)) - return true; - - for (int i = 0; i < max_stack_depth; i++) { - struct callstack_filter *filter; - - /* - * In powerpc, the callchain saved by kernel always includes - * first three entries as the NIP (next instruction pointer), - * LR (link register), and the contents of LR save area in the - * second stack frame. In certain scenarios its possible to have - * invalid kernel instruction addresses in either LR or the second - * stack frame's LR. In that case, kernel will store that address as - * zero. - * - * The below check will continue to look into callstack, - * incase first or second callstack index entry has 0 - * address for powerpc. - */ - if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") || - (i != 1 && i != 2)))) - break; - - ip = callstack[i]; - sym = machine__find_kernel_symbol(machine, ip, &kmap); - if (sym == NULL) - continue; - - list_for_each_entry(filter, &callstack_filters, list) { - if (strstr(sym->name, filter->name)) - return true; - } - } - return false; -} - struct trace_lock_handler { /* it's used on CONFIG_LOCKDEP */ int (*acquire_event)(struct evsel *evsel, @@ -1165,7 +1058,7 @@ static int report_lock_contention_begin_event(struct evsel *evsel, if (callstack == NULL) return -ENOMEM; - if (!match_callstack_filter(machine, callstack)) { + if (!match_callstack_filter(machine, callstack, max_stack_depth)) { free(callstack); return 0; } @@ -1575,8 +1468,13 @@ static void sort_result(void) static const struct { unsigned int flags; - const char *str; - const char *name; + /* + * Name of the lock flags (access), with delimeter ':'. + * For example, rwsem:R of rwsem:W. + */ + const char *flags_name; + /* Name of the lock (type), for example, rwlock or rwsem. */ + const char *lock_name; } lock_type_table[] = { { 0, "semaphore", "semaphore" }, { LCB_F_SPIN, "spinlock", "spinlock" }, @@ -1595,24 +1493,24 @@ static const struct { { LCB_F_MUTEX | LCB_F_SPIN, "mutex:spin", "mutex-spin" }, }; -static const char *get_type_str(unsigned int flags) +static const char *get_type_flags_name(unsigned int flags) { - flags &= LCB_F_MAX_FLAGS - 1; + flags &= LCB_F_TYPE_MASK; for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) { if (lock_type_table[i].flags == flags) - return lock_type_table[i].str; + return lock_type_table[i].flags_name; } return "unknown"; } -static const char *get_type_name(unsigned int flags) +static const char *get_type_lock_name(unsigned int flags) { - flags &= LCB_F_MAX_FLAGS - 1; + flags &= LCB_F_TYPE_MASK; for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) { if (lock_type_table[i].flags == flags) - return lock_type_table[i].name; + return lock_type_table[i].lock_name; } return "unknown"; } @@ -1633,6 +1531,12 @@ static void lock_filter_finish(void) zfree(&filters.cgrps); filters.nr_cgrps = 0; + + for (int i = 0; i < filters.nr_slabs; i++) + free(filters.slabs[i]); + + zfree(&filters.slabs); + filters.nr_slabs = 0; } static void sort_contention_result(void) @@ -1719,7 +1623,7 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat switch (aggr_mode) { case LOCK_AGGR_CALLER: - fprintf(lock_output, " %10s %s\n", get_type_str(st->flags), st->name); + fprintf(lock_output, " %10s %s\n", get_type_flags_name(st->flags), st->name); break; case LOCK_AGGR_TASK: pid = st->addr; @@ -1729,7 +1633,7 @@ static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat break; case LOCK_AGGR_ADDR: fprintf(lock_output, " %016llx %s (%s)\n", (unsigned long long)st->addr, - st->name, get_type_name(st->flags)); + st->name, get_type_lock_name(st->flags)); break; case LOCK_AGGR_CGROUP: fprintf(lock_output, " %s\n", st->name); @@ -1770,7 +1674,7 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s switch (aggr_mode) { case LOCK_AGGR_CALLER: - fprintf(lock_output, "%s%s %s", get_type_str(st->flags), sep, st->name); + fprintf(lock_output, "%s%s %s", get_type_flags_name(st->flags), sep, st->name); if (verbose <= 0) fprintf(lock_output, "\n"); break; @@ -1782,7 +1686,7 @@ static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *s break; case LOCK_AGGR_ADDR: fprintf(lock_output, "%llx%s %s%s %s\n", (unsigned long long)st->addr, sep, - st->name, sep, get_type_name(st->flags)); + st->name, sep, get_type_lock_name(st->flags)); break; case LOCK_AGGR_CGROUP: fprintf(lock_output, "%s\n",st->name); @@ -2137,7 +2041,8 @@ static int __cmd_contention(int argc, const char **argv) goto out_delete; } - if (lock_contention_prepare(&con) < 0) { + err = lock_contention_prepare(&con); + if (err < 0) { pr_err("lock contention BPF setup failed\n"); goto out_delete; } @@ -2158,10 +2063,14 @@ static int __cmd_contention(int argc, const char **argv) } } - if (setup_output_field(true, output_fields)) + err = setup_output_field(true, output_fields); + if (err) { + pr_err("Failed to setup output field\n"); goto out_delete; + } - if (select_key(true)) + err = select_key(true); + if (err) goto out_delete; if (symbol_conf.field_sep) { @@ -2345,10 +2254,10 @@ static int parse_lock_type(const struct option *opt __maybe_unused, const char * for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) { bool found = false; - /* `tok` is `str` in `lock_type_table` if it contains ':'. */ + /* `tok` is a flags name if it contains ':'. */ if (strchr(tok, ':')) { for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) { - if (!strcmp(lock_type_table[i].str, tok) && + if (!strcmp(lock_type_table[i].flags_name, tok) && add_lock_type(lock_type_table[i].flags)) { found = true; break; @@ -2365,11 +2274,14 @@ static int parse_lock_type(const struct option *opt __maybe_unused, const char * } /* - * Otherwise `tok` is `name` in `lock_type_table`. + * Otherwise `tok` is a lock name. * Single lock name could contain multiple flags. + * Replace alias `pcpu-sem` with actual name `percpu-rwsem. */ + if (!strcmp(tok, "pcpu-sem")) + tok = (char *)"percpu-rwsem"; for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) { - if (!strcmp(lock_type_table[i].name, tok)) { + if (!strcmp(lock_type_table[i].lock_name, tok)) { if (add_lock_type(lock_type_table[i].flags)) { found = true; } else { @@ -2428,6 +2340,27 @@ static bool add_lock_sym(char *name) return true; } +static bool add_lock_slab(char *name) +{ + char **tmp; + char *sym = strdup(name); + + if (sym == NULL) { + pr_err("Memory allocation failure\n"); + return false; + } + + tmp = realloc(filters.slabs, (filters.nr_slabs + 1) * sizeof(*filters.slabs)); + if (tmp == NULL) { + pr_err("Memory allocation failure\n"); + return false; + } + + tmp[filters.nr_slabs++] = sym; + filters.slabs = tmp; + return true; +} + static int parse_lock_addr(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) { @@ -2451,6 +2384,14 @@ static int parse_lock_addr(const struct option *opt __maybe_unused, const char * continue; } + if (*tok == '&') { + if (!add_lock_slab(tok + 1)) { + ret = -1; + break; + } + continue; + } + /* * At this moment, we don't have kernel symbols. Save the symbols * in a separate list and resolve them to addresses later. @@ -2465,34 +2406,6 @@ 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"); - free(s); - return -1; - } - - strcpy(entry->name, tok); - list_add_tail(&entry->list, &callstack_filters); - } - - free(s); - return ret; -} - static int parse_output(const struct option *opt __maybe_unused, const char *str, int unset __maybe_unused) { |