mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-14 14:34:28 +08:00
3477f079fe
When there're many lock contentions in the system, people sometimes want to know who caused the contention, IOW who's the owner of the locks. The -o/--lock-owner option tries to follow the lock owners for the contended mutexes and rwsems from BPF, and then attributes the contention time to the owner instead of the waiter. It's a best effort approach to get the owner info at the time of the contention and doesn't guarantee to have the precise tracking of owners if it's changing over time. Currently it only handles mutex and rwsem that have owner field in their struct and it basically points to a task_struct that owns the lock at the moment. Technically its type is atomic_long_t and it comes with some LSB bits used for other meanings. So it needs to clear them when casting it to a pointer to task_struct. Also the atomic_long_t is a typedef of the atomic 32 or 64 bit types depending on arch which is a wrapper struct for the counter value. I'm not aware of proper ways to access those kernel atomic types from BPF so I just read the internal counter value directly. Please let me know if there's a better way. When -o/--lock-owner option is used, it goes to the task aggregation mode like -t/--threads option does. However it cannot get the owner for other lock types like spinlock and sometimes even for mutex. $ sudo ./perf lock con -abo -- ./perf bench sched pipe # Running 'sched/pipe' benchmark: # Executed 1000000 pipe operations between two processes Total time: 4.766 [sec] 4.766540 usecs/op 209795 ops/sec contended total wait max wait avg wait pid owner 403 565.32 us 26.81 us 1.40 us -1 Unknown 4 27.99 us 8.57 us 7.00 us 1583145 sched-pipe 1 8.25 us 8.25 us 8.25 us 1583144 sched-pipe 1 2.03 us 2.03 us 2.03 us 5068 chrome As you can see, the owner is unknown for the most cases. But if we filter only for the mutex locks, it'd more likely get the onwers. $ sudo ./perf lock con -abo -Y mutex -- ./perf bench sched pipe # Running 'sched/pipe' benchmark: # Executed 1000000 pipe operations between two processes Total time: 4.910 [sec] 4.910435 usecs/op 203647 ops/sec contended total wait max wait avg wait pid owner 2 15.50 us 8.29 us 7.75 us 1582852 sched-pipe 7 7.20 us 2.47 us 1.03 us -1 Unknown 1 6.74 us 6.74 us 6.74 us 1582851 sched-pipe Signed-off-by: Namhyung Kim <namhyung@kernel.org> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Boqun Feng <boqun.feng@gmail.com> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Hao Luo <haoluo@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: Waiman Long <longman@redhat.com> Cc: Will Deacon <will@kernel.org> Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20230207002403.63590-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
167 lines
3.8 KiB
C
167 lines
3.8 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#ifndef PERF_LOCK_CONTENTION_H
|
|
#define PERF_LOCK_CONTENTION_H
|
|
|
|
#include <linux/list.h>
|
|
#include <linux/rbtree.h>
|
|
|
|
struct lock_filter {
|
|
int nr_types;
|
|
int nr_addrs;
|
|
int nr_syms;
|
|
unsigned int *types;
|
|
unsigned long *addrs;
|
|
char **syms;
|
|
};
|
|
|
|
struct lock_stat {
|
|
struct hlist_node hash_entry;
|
|
struct rb_node rb; /* used for sorting */
|
|
|
|
u64 addr; /* address of lockdep_map, used as ID */
|
|
char *name; /* for strcpy(), we cannot use const */
|
|
u64 *callstack;
|
|
|
|
unsigned int nr_acquire;
|
|
unsigned int nr_acquired;
|
|
unsigned int nr_contended;
|
|
unsigned int nr_release;
|
|
|
|
union {
|
|
unsigned int nr_readlock;
|
|
unsigned int flags;
|
|
};
|
|
unsigned int nr_trylock;
|
|
|
|
/* these times are in nano sec. */
|
|
u64 avg_wait_time;
|
|
u64 wait_time_total;
|
|
u64 wait_time_min;
|
|
u64 wait_time_max;
|
|
|
|
int broken; /* flag of blacklist */
|
|
int combined;
|
|
};
|
|
|
|
/*
|
|
* States of lock_seq_stat
|
|
*
|
|
* UNINITIALIZED is required for detecting first event of acquire.
|
|
* As the nature of lock events, there is no guarantee
|
|
* that the first event for the locks are acquire,
|
|
* it can be acquired, contended or release.
|
|
*/
|
|
#define SEQ_STATE_UNINITIALIZED 0 /* initial state */
|
|
#define SEQ_STATE_RELEASED 1
|
|
#define SEQ_STATE_ACQUIRING 2
|
|
#define SEQ_STATE_ACQUIRED 3
|
|
#define SEQ_STATE_READ_ACQUIRED 4
|
|
#define SEQ_STATE_CONTENDED 5
|
|
|
|
/*
|
|
* MAX_LOCK_DEPTH
|
|
* Imported from include/linux/sched.h.
|
|
* Should this be synchronized?
|
|
*/
|
|
#define MAX_LOCK_DEPTH 48
|
|
|
|
struct lock_stat *lock_stat_find(u64 addr);
|
|
struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags);
|
|
|
|
bool match_callstack_filter(struct machine *machine, u64 *callstack);
|
|
|
|
/*
|
|
* struct lock_seq_stat:
|
|
* Place to put on state of one lock sequence
|
|
* 1) acquire -> acquired -> release
|
|
* 2) acquire -> contended -> acquired -> release
|
|
* 3) acquire (with read or try) -> release
|
|
* 4) Are there other patterns?
|
|
*/
|
|
struct lock_seq_stat {
|
|
struct list_head list;
|
|
int state;
|
|
u64 prev_event_time;
|
|
u64 addr;
|
|
|
|
int read_count;
|
|
};
|
|
|
|
struct thread_stat {
|
|
struct rb_node rb;
|
|
|
|
u32 tid;
|
|
struct list_head seq_list;
|
|
};
|
|
|
|
/*
|
|
* CONTENTION_STACK_DEPTH
|
|
* Number of stack trace entries to find callers
|
|
*/
|
|
#define CONTENTION_STACK_DEPTH 8
|
|
|
|
/*
|
|
* CONTENTION_STACK_SKIP
|
|
* Number of stack trace entries to skip when finding callers.
|
|
* The first few entries belong to the locking implementation itself.
|
|
*/
|
|
#define CONTENTION_STACK_SKIP 4
|
|
|
|
/*
|
|
* flags for lock:contention_begin
|
|
* Imported 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 evlist;
|
|
struct machine;
|
|
struct target;
|
|
|
|
struct lock_contention {
|
|
struct evlist *evlist;
|
|
struct target *target;
|
|
struct machine *machine;
|
|
struct hlist_head *result;
|
|
struct lock_filter *filters;
|
|
unsigned long map_nr_entries;
|
|
int lost;
|
|
int max_stack;
|
|
int stack_skip;
|
|
int aggr_mode;
|
|
int owner;
|
|
bool save_callstack;
|
|
};
|
|
|
|
#ifdef HAVE_BPF_SKEL
|
|
|
|
int lock_contention_prepare(struct lock_contention *con);
|
|
int lock_contention_start(void);
|
|
int lock_contention_stop(void);
|
|
int lock_contention_read(struct lock_contention *con);
|
|
int lock_contention_finish(void);
|
|
|
|
#else /* !HAVE_BPF_SKEL */
|
|
|
|
static inline int lock_contention_prepare(struct lock_contention *con __maybe_unused)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline int lock_contention_start(void) { return 0; }
|
|
static inline int lock_contention_stop(void) { return 0; }
|
|
static inline int lock_contention_finish(void) { return 0; }
|
|
|
|
static inline int lock_contention_read(struct lock_contention *con __maybe_unused)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#endif /* HAVE_BPF_SKEL */
|
|
|
|
#endif /* PERF_LOCK_CONTENTION_H */
|