root/tools/perf/util/lock-contention.c
// 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;
        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;
}