summaryrefslogtreecommitdiff
path: root/tools/perf/util/bpf_lock_contention.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/bpf_lock_contention.c')
-rw-r--r--tools/perf/util/bpf_lock_contention.c183
1 files changed, 112 insertions, 71 deletions
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index 0236334fd69b..fadcacb9d501 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -34,13 +34,15 @@ int lock_contention_prepare(struct lock_contention *con)
bpf_map__set_max_entries(skel->maps.lock_stat, con->map_nr_entries);
bpf_map__set_max_entries(skel->maps.tstamp, con->map_nr_entries);
- if (con->aggr_mode == LOCK_AGGR_TASK) {
+ if (con->aggr_mode == LOCK_AGGR_TASK)
bpf_map__set_max_entries(skel->maps.task_data, con->map_nr_entries);
- bpf_map__set_max_entries(skel->maps.stacks, 1);
- } else {
+ else
bpf_map__set_max_entries(skel->maps.task_data, 1);
+
+ if (con->save_callstack)
bpf_map__set_max_entries(skel->maps.stacks, con->map_nr_entries);
- }
+ else
+ bpf_map__set_max_entries(skel->maps.stacks, 1);
if (target__has_cpu(target))
ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus);
@@ -146,6 +148,8 @@ int lock_contention_prepare(struct lock_contention *con)
/* these don't work well if in the rodata section */
skel->bss->stack_skip = con->stack_skip;
skel->bss->aggr_mode = con->aggr_mode;
+ skel->bss->needs_callstack = con->save_callstack;
+ skel->bss->lock_owner = con->owner;
lock_contention_bpf__attach(skel);
return 0;
@@ -163,9 +167,70 @@ int lock_contention_stop(void)
return 0;
}
+static const char *lock_contention_get_name(struct lock_contention *con,
+ struct contention_key *key,
+ u64 *stack_trace)
+{
+ int idx = 0;
+ u64 addr;
+ const char *name = "";
+ static char name_buf[KSYM_NAME_LEN];
+ struct symbol *sym;
+ struct map *kmap;
+ struct machine *machine = con->machine;
+
+ if (con->aggr_mode == LOCK_AGGR_TASK) {
+ struct contention_task_data task;
+ int pid = key->pid;
+ int task_fd = bpf_map__fd(skel->maps.task_data);
+
+ /* do not update idle comm which contains CPU number */
+ if (pid) {
+ struct thread *t = __machine__findnew_thread(machine, /*pid=*/-1, pid);
+
+ if (t == NULL)
+ return name;
+ if (!bpf_map_lookup_elem(task_fd, &pid, &task) &&
+ thread__set_comm(t, task.comm, /*timestamp=*/0))
+ name = task.comm;
+ }
+ return name;
+ }
+
+ if (con->aggr_mode == LOCK_AGGR_ADDR) {
+ sym = machine__find_kernel_symbol(machine, key->lock_addr, &kmap);
+ if (sym)
+ name = sym->name;
+ return name;
+ }
+
+ /* LOCK_AGGR_CALLER: skip lock internal functions */
+ while (machine__is_lock_function(machine, stack_trace[idx]) &&
+ idx < con->max_stack - 1)
+ idx++;
+
+ addr = stack_trace[idx];
+ sym = machine__find_kernel_symbol(machine, addr, &kmap);
+
+ if (sym) {
+ unsigned long offset;
+
+ offset = kmap->map_ip(kmap, addr) - sym->start;
+
+ if (offset == 0)
+ return sym->name;
+
+ snprintf(name_buf, sizeof(name_buf), "%s+%#lx", sym->name, offset);
+ } else {
+ snprintf(name_buf, sizeof(name_buf), "%#lx", (unsigned long)addr);
+ }
+
+ return name_buf;
+}
+
int lock_contention_read(struct lock_contention *con)
{
- int fd, stack, task_fd, err = 0;
+ int fd, stack, err = 0;
struct contention_key *prev_key, key;
struct contention_data data = {};
struct lock_stat *st = NULL;
@@ -175,7 +240,6 @@ int lock_contention_read(struct lock_contention *con)
fd = bpf_map__fd(skel->maps.lock_stat);
stack = bpf_map__fd(skel->maps.stacks);
- task_fd = bpf_map__fd(skel->maps.task_data);
con->lost = skel->bss->lost;
@@ -195,98 +259,75 @@ int lock_contention_read(struct lock_contention *con)
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;
+ s64 ls_key;
+ const char *name;
/* to handle errors in the loop body */
err = -1;
bpf_map_lookup_elem(fd, &key, &data);
- st = zalloc(sizeof(*st));
- if (st == NULL)
- break;
-
- st->nr_contended = data.count;
- st->wait_time_total = data.total_time;
- st->wait_time_max = data.max_time;
- st->wait_time_min = data.min_time;
-
- if (data.count)
- st->avg_wait_time = data.total_time / data.count;
-
- st->flags = data.flags;
- st->addr = key.aggr_key;
+ if (con->save_callstack) {
+ bpf_map_lookup_elem(stack, &key.stack_id, stack_trace);
- if (con->aggr_mode == LOCK_AGGR_TASK) {
- struct contention_task_data task;
- struct thread *t;
- int pid = key.aggr_key;
+ if (!match_callstack_filter(machine, stack_trace))
+ goto next;
+ }
- /* do not update idle comm which contains CPU number */
- if (st->addr) {
- bpf_map_lookup_elem(task_fd, &pid, &task);
- t = __machine__findnew_thread(machine, /*pid=*/-1, pid);
- thread__set_comm(t, task.comm, /*timestamp=*/0);
- }
+ switch (con->aggr_mode) {
+ case LOCK_AGGR_CALLER:
+ ls_key = key.stack_id;
+ break;
+ case LOCK_AGGR_TASK:
+ ls_key = key.pid;
+ break;
+ case LOCK_AGGR_ADDR:
+ ls_key = key.lock_addr;
+ break;
+ default:
goto next;
}
- if (con->aggr_mode == LOCK_AGGR_ADDR) {
- sym = machine__find_kernel_symbol(machine, st->addr, &kmap);
- if (sym)
- st->name = strdup(sym->name);
+ st = lock_stat_find(ls_key);
+ if (st != NULL) {
+ st->wait_time_total += data.total_time;
+ if (st->wait_time_max < data.max_time)
+ st->wait_time_max = data.max_time;
+ if (st->wait_time_min > data.min_time)
+ st->wait_time_min = data.min_time;
+
+ st->nr_contended += data.count;
+ if (st->nr_contended)
+ st->avg_wait_time = st->wait_time_total / st->nr_contended;
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]) &&
- idx < con->max_stack - 1)
- idx++;
-
- st->addr = stack_trace[idx];
- sym = machine__find_kernel_symbol(machine, st->addr, &kmap);
-
- if (sym) {
- unsigned long offset;
- int ret = 0;
-
- offset = kmap->map_ip(kmap, st->addr) - sym->start;
+ name = lock_contention_get_name(con, &key, stack_trace);
+ st = lock_stat_findnew(ls_key, name, data.flags);
+ if (st == NULL)
+ break;
- if (offset)
- ret = asprintf(&st->name, "%s+%#lx", sym->name, offset);
- else
- st->name = strdup(sym->name);
+ st->nr_contended = data.count;
+ st->wait_time_total = data.total_time;
+ st->wait_time_max = data.max_time;
+ st->wait_time_min = data.min_time;
- if (ret < 0 || st->name == NULL)
- break;
- } else if (asprintf(&st->name, "%#lx", (unsigned long)st->addr) < 0) {
- break;
- }
+ if (data.count)
+ st->avg_wait_time = data.total_time / data.count;
- if (verbose > 0) {
+ if (con->save_callstack) {
st->callstack = memdup(stack_trace, stack_size);
if (st->callstack == NULL)
break;
}
+
next:
- hlist_add_head(&st->hash_entry, con->result);
prev_key = &key;
- /* we're fine now, reset the values */
- st = NULL;
+ /* we're fine now, reset the error */
err = 0;
}
free(stack_trace);
- if (st) {
- free(st->name);
- free(st);
- }
return err;
}