| // SPDX-License-Identifier: GPL-2.0 | |
| #include "debug.h" | |
| #include "env.h" | |
| #include "lock-contention.h" | |
| #include "machine.h" | |
| #include "symbol.h" | |
| #include <limits.h> | |
| #include <string.h> | |
| #include <linux/hash.h> | |
| #include <linux/zalloc.h> | |
| #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS) | |
| #define lockhashentry(key) (lockhash_table + __lockhashfn((key))) | |
| struct callstack_filter { | |
| struct list_head list; | |
| char name[]; | |
| }; | |
| static LIST_HEAD(callstack_filters); | |
| struct hlist_head *lockhash_table; | |
| 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; | |
| } | |
| bool needs_callstack(void) | |
| { | |
| return !list_empty(&callstack_filters); | |
| } | |
| 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, int max_stack_depth) | |
| { | |
| struct map *kmap; | |
| struct symbol *sym; | |
| u64 ip; | |
| uint16_t e_machine = perf_env__e_machine(machine->env, /*e_flags=*/NULL); | |
| bool is_powerpc = e_machine == EM_PPC64 || e_machine == EM_PPC; | |
| 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] && (!is_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; | |
| } |