static bool combine_locks;
static bool show_thread_stats;
static bool show_lock_addrs;
+static bool show_lock_owner;
static bool use_bpf;
static unsigned long bpf_map_entries = 10240;
static int max_stack_depth = CONTENTION_STACK_DEPTH;
switch (aggr_mode) {
case LOCK_AGGR_TASK:
- pr_info(" %10s %s\n\n", "pid", "comm");
+ pr_info(" %10s %s\n\n", "pid",
+ show_lock_owner ? "owner" : "comm");
break;
case LOCK_AGGR_CALLER:
pr_info(" %10s %s\n\n", "type", "caller");
case LOCK_AGGR_TASK:
pid = st->addr;
t = perf_session__findnew(session, pid);
- pr_info(" %10d %s\n", pid, thread__comm_str(t));
+ pr_info(" %10d %s\n",
+ pid, pid == -1 ? "Unknown" : thread__comm_str(t));
break;
case LOCK_AGGR_ADDR:
pr_info(" %016llx %s\n", (unsigned long long)st->addr,
{
}
+static int check_lock_contention_options(const struct option *options,
+ const char * const *usage)
+
+{
+ if (show_thread_stats && show_lock_addrs) {
+ pr_err("Cannot use thread and addr mode together\n");
+ parse_options_usage(usage, options, "threads", 0);
+ parse_options_usage(NULL, options, "lock-addr", 0);
+ return -1;
+ }
+
+ if (show_lock_owner && !use_bpf) {
+ pr_err("Lock owners are available only with BPF\n");
+ parse_options_usage(usage, options, "lock-owner", 0);
+ parse_options_usage(NULL, options, "use-bpf", 0);
+ return -1;
+ }
+
+ if (show_lock_owner && show_lock_addrs) {
+ pr_err("Cannot use owner and addr mode together\n");
+ parse_options_usage(usage, options, "lock-owner", 0);
+ parse_options_usage(NULL, options, "lock-addr", 0);
+ return -1;
+ }
+
+ if (show_lock_owner)
+ show_thread_stats = true;
+
+ return 0;
+}
+
static int __cmd_contention(int argc, const char **argv)
{
int err = -EINVAL;
.stack_skip = stack_skip,
.filters = &filters,
.save_callstack = needs_callstack(),
+ .owner = show_lock_owner,
};
session = perf_session__new(use_bpf ? NULL : &data, &eops);
"Filter specific address/symbol of locks", parse_lock_addr),
OPT_CALLBACK('S', "callstack-filter", NULL, "NAMES",
"Filter specific function in the callstack", parse_call_stack),
+ OPT_BOOLEAN('o', "lock-owner", &show_lock_owner, "show lock owners instead of waiters"),
OPT_PARENT(lock_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);
+ if (check_lock_contention_options(contention_options,
+ contention_usage) < 0)
return -1;
- }
rc = __cmd_contention(argc, argv);
} else {
/* default buffer size */
#define MAX_ENTRIES 10240
+/* lock contention flags from include/trace/events/lock.h */
+#define LCB_F_SPIN (1U << 0)
+#define LCB_F_READ (1U << 1)
+#define LCB_F_WRITE (1U << 2)
+#define LCB_F_RT (1U << 3)
+#define LCB_F_PERCPU (1U << 4)
+#define LCB_F_MUTEX (1U << 5)
+
struct tstamp_data {
__u64 timestamp;
__u64 lock;
int has_addr;
int needs_callstack;
int stack_skip;
+int lock_owner;
/* determine the key of lock stat */
int aggr_mode;
return 1;
}
-static inline void update_task_data(__u32 pid)
+static inline int update_task_data(struct task_struct *task)
{
struct contention_task_data *p;
+ int pid, err;
+
+ err = bpf_core_read(&pid, sizeof(pid), &task->pid);
+ if (err)
+ return -1;
p = bpf_map_lookup_elem(&task_data, &pid);
if (p == NULL) {
- struct contention_task_data data;
+ struct contention_task_data data = {};
- bpf_get_current_comm(data.comm, sizeof(data.comm));
+ BPF_CORE_READ_STR_INTO(&data.comm, task, comm);
bpf_map_update_elem(&task_data, &pid, &data, BPF_NOEXIST);
}
+
+ return 0;
}
SEC("tp_btf/contention_begin")
BPF_F_FAST_STACK_CMP | stack_skip);
if (pelem->stack_id < 0)
lost++;
+ } else if (aggr_mode == LOCK_AGGR_TASK) {
+ struct task_struct *task;
+
+ if (lock_owner) {
+ if (pelem->flags & LCB_F_MUTEX) {
+ struct mutex *lock = (void *)pelem->lock;
+ unsigned long owner = BPF_CORE_READ(lock, owner.counter);
+
+ task = (void *)(owner & ~7UL);
+ } else if (pelem->flags == LCB_F_READ || pelem->flags == LCB_F_WRITE) {
+ struct rw_semaphore *lock = (void *)pelem->lock;
+ unsigned long owner = BPF_CORE_READ(lock, owner.counter);
+
+ task = (void *)(owner & ~7UL);
+ } else {
+ task = NULL;
+ }
+
+ /* The flags is not used anymore. Pass the owner pid. */
+ if (task)
+ pelem->flags = BPF_CORE_READ(task, pid);
+ else
+ pelem->flags = -1U;
+
+ } else {
+ task = bpf_get_current_task_btf();
+ }
+
+ if (task) {
+ if (update_task_data(task) < 0 && lock_owner)
+ pelem->flags = -1U;
+ }
}
return 0;
key.stack_id = pelem->stack_id;
break;
case LOCK_AGGR_TASK:
- key.pid = pid;
- update_task_data(pid);
+ if (lock_owner)
+ key.pid = pelem->flags;
+ else
+ key.pid = pid;
if (needs_callstack)
key.stack_id = pelem->stack_id;
break;