perf lock contention: Add -l/--lock-addr option
The -l/--lock-addr option is to implement per-lock-instance contention stat using LOCK_AGGR_ADDR. It displays lock address and optionally symbol name if exists. $ sudo ./perf lock con -abl sleep 1 contended total wait max wait avg wait address symbol 1 36.28 us 36.28 us 36.28 us ffff92615d6448b8 9 10.91 us 1.84 us 1.21 us ffffffffbaed50c0 rcu_state 1 10.49 us 10.49 us 10.49 us ffff9262ac4f0c80 8 4.68 us 1.67 us 585 ns ffffffffbae07a40 jiffies_lock 3 3.03 us 1.45 us 1.01 us ffff9262277861e0 1 924 ns 924 ns 924 ns ffff926095ba9d20 1 436 ns 436 ns 436 ns ffff9260bfda4f60 Signed-off-by: Namhyung Kim <namhyung@kernel.org> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Blake Jones <blakejones@google.com> Cc: Ian Rogers <irogers@google.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Song Liu <song@kernel.org> Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20221209190727.759804-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
eca949b2b4
commit
688d2e8de2
|
@ -168,6 +168,10 @@ CONTENTION OPTIONS
|
|||
--entries=<value>::
|
||||
Display this many entries.
|
||||
|
||||
-l::
|
||||
--lock-addr::
|
||||
Show lock contention stat by address
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
|
|
@ -56,6 +56,7 @@ static struct rb_root thread_stats;
|
|||
|
||||
static bool combine_locks;
|
||||
static bool show_thread_stats;
|
||||
static bool show_lock_addrs;
|
||||
static bool use_bpf;
|
||||
static unsigned long bpf_map_entries = 10240;
|
||||
static int max_stack_depth = CONTENTION_STACK_DEPTH;
|
||||
|
@ -999,13 +1000,32 @@ static int report_lock_contention_begin_event(struct evsel *evsel,
|
|||
ls = lock_stat_find(key);
|
||||
if (!ls) {
|
||||
char buf[128];
|
||||
const char *caller = buf;
|
||||
const char *name = "";
|
||||
unsigned int flags = evsel__intval(evsel, sample, "flags");
|
||||
struct machine *machine = &session->machines.host;
|
||||
struct map *kmap;
|
||||
struct symbol *sym;
|
||||
|
||||
if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0)
|
||||
caller = "Unknown";
|
||||
switch (aggr_mode) {
|
||||
case LOCK_AGGR_ADDR:
|
||||
/* make sure it loads the kernel map to find lock symbols */
|
||||
map__load(machine__kernel_map(machine));
|
||||
|
||||
ls = lock_stat_findnew(key, caller, flags);
|
||||
sym = machine__find_kernel_symbol(machine, key, &kmap);
|
||||
if (sym)
|
||||
name = sym->name;
|
||||
break;
|
||||
case LOCK_AGGR_CALLER:
|
||||
name = buf;
|
||||
if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0)
|
||||
name = "Unknown";
|
||||
break;
|
||||
case LOCK_AGGR_TASK:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ls = lock_stat_findnew(key, name, flags);
|
||||
if (!ls)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1460,10 +1480,19 @@ static void print_contention_result(struct lock_contention *con)
|
|||
list_for_each_entry(key, &lock_keys, list)
|
||||
pr_info("%*s ", key->len, key->header);
|
||||
|
||||
if (show_thread_stats)
|
||||
switch (aggr_mode) {
|
||||
case LOCK_AGGR_TASK:
|
||||
pr_info(" %10s %s\n\n", "pid", "comm");
|
||||
else
|
||||
break;
|
||||
case LOCK_AGGR_CALLER:
|
||||
pr_info(" %10s %s\n\n", "type", "caller");
|
||||
break;
|
||||
case LOCK_AGGR_ADDR:
|
||||
pr_info(" %16s %s\n\n", "address", "symbol");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bad = total = printed = 0;
|
||||
|
@ -1471,6 +1500,9 @@ static void print_contention_result(struct lock_contention *con)
|
|||
bad = bad_hist[BROKEN_CONTENDED];
|
||||
|
||||
while ((st = pop_from_result())) {
|
||||
struct thread *t;
|
||||
int pid;
|
||||
|
||||
total += use_bpf ? st->nr_contended : 1;
|
||||
if (st->broken)
|
||||
bad++;
|
||||
|
@ -1480,18 +1512,24 @@ static void print_contention_result(struct lock_contention *con)
|
|||
pr_info(" ");
|
||||
}
|
||||
|
||||
if (show_thread_stats) {
|
||||
struct thread *t;
|
||||
int pid = st->addr;
|
||||
|
||||
/* st->addr contains tid of thread */
|
||||
switch (aggr_mode) {
|
||||
case LOCK_AGGR_CALLER:
|
||||
pr_info(" %10s %s\n", get_type_str(st), st->name);
|
||||
break;
|
||||
case LOCK_AGGR_TASK:
|
||||
pid = st->addr;
|
||||
t = perf_session__findnew(session, pid);
|
||||
pr_info(" %10d %s\n", pid, thread__comm_str(t));
|
||||
goto next;
|
||||
break;
|
||||
case LOCK_AGGR_ADDR:
|
||||
pr_info(" %016llx %s\n", (unsigned long long)st->addr,
|
||||
st->name ? : "");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info(" %10s %s\n", get_type_str(st), st->name);
|
||||
if (verbose) {
|
||||
if (aggr_mode == LOCK_AGGR_CALLER && verbose) {
|
||||
struct map *kmap;
|
||||
struct symbol *sym;
|
||||
char buf[128];
|
||||
|
@ -1508,7 +1546,6 @@ static void print_contention_result(struct lock_contention *con)
|
|||
}
|
||||
}
|
||||
|
||||
next:
|
||||
if (++printed >= print_nr_entries)
|
||||
break;
|
||||
}
|
||||
|
@ -1616,7 +1653,6 @@ static int __cmd_contention(int argc, const char **argv)
|
|||
.map_nr_entries = bpf_map_entries,
|
||||
.max_stack = max_stack_depth,
|
||||
.stack_skip = stack_skip,
|
||||
.aggr_mode = show_thread_stats ? LOCK_AGGR_TASK : LOCK_AGGR_CALLER,
|
||||
};
|
||||
|
||||
session = perf_session__new(use_bpf ? NULL : &data, &eops);
|
||||
|
@ -1627,6 +1663,9 @@ static int __cmd_contention(int argc, const char **argv)
|
|||
|
||||
con.machine = &session->machines.host;
|
||||
|
||||
con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK :
|
||||
show_lock_addrs ? LOCK_AGGR_ADDR : LOCK_AGGR_CALLER;
|
||||
|
||||
/* for lock function check */
|
||||
symbol_conf.sort_by_name = true;
|
||||
symbol__init(&session->header.env);
|
||||
|
@ -1907,6 +1946,7 @@ int cmd_lock(int argc, const char **argv)
|
|||
"Set the number of stack depth to skip when finding a lock caller, "
|
||||
"Default: " __stringify(CONTENTION_STACK_SKIP)),
|
||||
OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"),
|
||||
OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"),
|
||||
OPT_PARENT(lock_options)
|
||||
};
|
||||
|
||||
|
@ -1976,6 +2016,16 @@ int cmd_lock(int argc, const char **argv)
|
|||
argc = parse_options(argc, argv, contention_options,
|
||||
contention_usage, 0);
|
||||
}
|
||||
|
||||
if (show_thread_stats && show_lock_addrs) {
|
||||
pr_err("Cannot use thread and addr mode together\n");
|
||||
parse_options_usage(contention_usage, contention_options,
|
||||
"threads", 0);
|
||||
parse_options_usage(NULL, contention_options,
|
||||
"lock-addr", 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = __cmd_contention(argc, argv);
|
||||
} else {
|
||||
usage_with_options(lock_usage, lock_options);
|
||||
|
|
|
@ -137,11 +137,15 @@ int lock_contention_read(struct lock_contention *con)
|
|||
thread__set_comm(idle, "swapper", /*timestamp=*/0);
|
||||
}
|
||||
|
||||
/* make sure it loads the kernel map */
|
||||
map__load(maps__first(machine->kmaps));
|
||||
|
||||
prev_key = NULL;
|
||||
while (!bpf_map_get_next_key(fd, prev_key, &key)) {
|
||||
struct map *kmap;
|
||||
struct symbol *sym;
|
||||
int idx = 0;
|
||||
s32 stack_id;
|
||||
|
||||
/* to handle errors in the loop body */
|
||||
err = -1;
|
||||
|
@ -160,24 +164,31 @@ int lock_contention_read(struct lock_contention *con)
|
|||
st->avg_wait_time = data.total_time / data.count;
|
||||
|
||||
st->flags = data.flags;
|
||||
st->addr = key.aggr_key;
|
||||
|
||||
if (con->aggr_mode == LOCK_AGGR_TASK) {
|
||||
struct contention_task_data task;
|
||||
struct thread *t;
|
||||
|
||||
st->addr = key.stack_or_task_id;
|
||||
int pid = key.aggr_key;
|
||||
|
||||
/* do not update idle comm which contains CPU number */
|
||||
if (st->addr) {
|
||||
bpf_map_lookup_elem(task_fd, &key, &task);
|
||||
t = __machine__findnew_thread(machine, /*pid=*/-1,
|
||||
key.stack_or_task_id);
|
||||
bpf_map_lookup_elem(task_fd, &pid, &task);
|
||||
t = __machine__findnew_thread(machine, /*pid=*/-1, pid);
|
||||
thread__set_comm(t, task.comm, /*timestamp=*/0);
|
||||
}
|
||||
goto next;
|
||||
}
|
||||
|
||||
bpf_map_lookup_elem(stack, &key, stack_trace);
|
||||
if (con->aggr_mode == LOCK_AGGR_ADDR) {
|
||||
sym = machine__find_kernel_symbol(machine, st->addr, &kmap);
|
||||
if (sym)
|
||||
st->name = strdup(sym->name);
|
||||
goto next;
|
||||
}
|
||||
|
||||
stack_id = key.aggr_key;
|
||||
bpf_map_lookup_elem(stack, &stack_id, stack_trace);
|
||||
|
||||
/* skip lock internal functions */
|
||||
while (machine__is_lock_function(machine, stack_trace[idx]) &&
|
||||
|
|
|
@ -168,11 +168,20 @@ int contention_end(u64 *ctx)
|
|||
|
||||
duration = bpf_ktime_get_ns() - pelem->timestamp;
|
||||
|
||||
if (aggr_mode == LOCK_AGGR_CALLER) {
|
||||
key.stack_or_task_id = pelem->stack_id;
|
||||
} else {
|
||||
key.stack_or_task_id = pid;
|
||||
switch (aggr_mode) {
|
||||
case LOCK_AGGR_CALLER:
|
||||
key.aggr_key = pelem->stack_id;
|
||||
break;
|
||||
case LOCK_AGGR_TASK:
|
||||
key.aggr_key = pid;
|
||||
update_task_data(pid);
|
||||
break;
|
||||
case LOCK_AGGR_ADDR:
|
||||
key.aggr_key = pelem->lock;
|
||||
break;
|
||||
default:
|
||||
/* should not happen */
|
||||
return 0;
|
||||
}
|
||||
|
||||
data = bpf_map_lookup_elem(&lock_stat, &key);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#define UTIL_BPF_SKEL_LOCK_DATA_H
|
||||
|
||||
struct contention_key {
|
||||
s32 stack_or_task_id;
|
||||
u64 aggr_key; /* can be stack_id, pid or lock addr */
|
||||
};
|
||||
|
||||
#define TASK_COMM_LEN 16
|
||||
|
|
Loading…
Reference in New Issue