1 // SPDX-License-Identifier: GPL-2.0
7 #include "util/evlist.h" // for struct evsel_str_handler
8 #include "util/evsel.h"
9 #include "util/symbol.h"
10 #include "util/thread.h"
11 #include "util/header.h"
12 #include "util/target.h"
13 #include "util/cgroup.h"
14 #include "util/callchain.h"
15 #include "util/lock-contention.h"
16 #include "util/bpf_skel/lock_data.h"
18 #include <subcmd/pager.h>
19 #include <subcmd/parse-options.h>
20 #include "util/trace-event.h"
21 #include "util/tracepoint.h"
23 #include "util/debug.h"
24 #include "util/session.h"
25 #include "util/tool.h"
26 #include "util/data.h"
27 #include "util/string2.h"
29 #include "util/util.h"
32 #include <sys/types.h>
33 #include <sys/prctl.h>
34 #include <semaphore.h>
39 #include <linux/list.h>
40 #include <linux/hash.h>
41 #include <linux/kernel.h>
42 #include <linux/zalloc.h>
43 #include <linux/err.h>
44 #include <linux/stringify.h>
46 static struct perf_session
*session
;
47 static struct target target
;
49 /* based on kernel/lockdep.c */
50 #define LOCKHASH_BITS 12
51 #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS)
53 static struct hlist_head
*lockhash_table
;
55 #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS)
56 #define lockhashentry(key) (lockhash_table + __lockhashfn((key)))
58 static struct rb_root thread_stats
;
60 static bool combine_locks
;
61 static bool show_thread_stats
;
62 static bool show_lock_addrs
;
63 static bool show_lock_owner
;
64 static bool show_lock_cgroups
;
66 static unsigned long bpf_map_entries
= MAX_ENTRIES
;
67 static int max_stack_depth
= CONTENTION_STACK_DEPTH
;
68 static int stack_skip
= CONTENTION_STACK_SKIP
;
69 static int print_nr_entries
= INT_MAX
/ 2;
70 static LIST_HEAD(callstack_filters
);
71 static const char *output_name
= NULL
;
72 static FILE *lock_output
;
74 struct callstack_filter
{
75 struct list_head list
;
79 static struct lock_filter filters
;
81 static enum lock_aggr_mode aggr_mode
= LOCK_AGGR_ADDR
;
83 static bool needs_callstack(void)
85 return !list_empty(&callstack_filters
);
88 static struct thread_stat
*thread_stat_find(u32 tid
)
91 struct thread_stat
*st
;
93 node
= thread_stats
.rb_node
;
95 st
= container_of(node
, struct thread_stat
, rb
);
98 else if (tid
< st
->tid
)
101 node
= node
->rb_right
;
107 static void thread_stat_insert(struct thread_stat
*new)
109 struct rb_node
**rb
= &thread_stats
.rb_node
;
110 struct rb_node
*parent
= NULL
;
111 struct thread_stat
*p
;
114 p
= container_of(*rb
, struct thread_stat
, rb
);
117 if (new->tid
< p
->tid
)
118 rb
= &(*rb
)->rb_left
;
119 else if (new->tid
> p
->tid
)
120 rb
= &(*rb
)->rb_right
;
122 BUG_ON("inserting invalid thread_stat\n");
125 rb_link_node(&new->rb
, parent
, rb
);
126 rb_insert_color(&new->rb
, &thread_stats
);
129 static struct thread_stat
*thread_stat_findnew_after_first(u32 tid
)
131 struct thread_stat
*st
;
133 st
= thread_stat_find(tid
);
137 st
= zalloc(sizeof(struct thread_stat
));
139 pr_err("memory allocation failed\n");
144 INIT_LIST_HEAD(&st
->seq_list
);
146 thread_stat_insert(st
);
151 static struct thread_stat
*thread_stat_findnew_first(u32 tid
);
152 static struct thread_stat
*(*thread_stat_findnew
)(u32 tid
) =
153 thread_stat_findnew_first
;
155 static struct thread_stat
*thread_stat_findnew_first(u32 tid
)
157 struct thread_stat
*st
;
159 st
= zalloc(sizeof(struct thread_stat
));
161 pr_err("memory allocation failed\n");
165 INIT_LIST_HEAD(&st
->seq_list
);
167 rb_link_node(&st
->rb
, NULL
, &thread_stats
.rb_node
);
168 rb_insert_color(&st
->rb
, &thread_stats
);
170 thread_stat_findnew
= thread_stat_findnew_after_first
;
174 /* build simple key function one is bigger than two */
175 #define SINGLE_KEY(member) \
176 static int lock_stat_key_ ## member(struct lock_stat *one, \
177 struct lock_stat *two) \
179 return one->member > two->member; \
182 SINGLE_KEY(nr_acquired
)
183 SINGLE_KEY(nr_contended
)
184 SINGLE_KEY(avg_wait_time
)
185 SINGLE_KEY(wait_time_total
)
186 SINGLE_KEY(wait_time_max
)
188 static int lock_stat_key_wait_time_min(struct lock_stat
*one
,
189 struct lock_stat
*two
)
191 u64 s1
= one
->wait_time_min
;
192 u64 s2
= two
->wait_time_min
;
193 if (s1
== ULLONG_MAX
)
195 if (s2
== ULLONG_MAX
)
202 * name: the value for specify by user
203 * this should be simpler than raw name of member
204 * e.g. nr_acquired -> acquired, wait_time_total -> wait_total
207 /* header: the string printed on the header line */
209 /* len: the printing width of the field */
211 /* key: a pointer to function to compare two lock stats for sorting */
212 int (*key
)(struct lock_stat
*, struct lock_stat
*);
213 /* print: a pointer to function to print a given lock stats */
214 void (*print
)(struct lock_key
*, struct lock_stat
*);
215 /* list: list entry to link this */
216 struct list_head list
;
219 static void lock_stat_key_print_time(unsigned long long nsec
, int len
)
221 static const struct {
225 { 1e9
* 3600, "h " },
235 fprintf(lock_output
, "%llu", nsec
);
239 for (int i
= 0; table
[i
].unit
; i
++) {
240 if (nsec
< table
[i
].base
)
243 fprintf(lock_output
, "%*.2f %s", len
- 3, nsec
/ table
[i
].base
, table
[i
].unit
);
247 fprintf(lock_output
, "%*llu %s", len
- 3, nsec
, "ns");
250 #define PRINT_KEY(member) \
251 static void lock_stat_key_print_ ## member(struct lock_key *key, \
252 struct lock_stat *ls) \
254 fprintf(lock_output, "%*llu", key->len, (unsigned long long)ls->member);\
257 #define PRINT_TIME(member) \
258 static void lock_stat_key_print_ ## member(struct lock_key *key, \
259 struct lock_stat *ls) \
261 lock_stat_key_print_time((unsigned long long)ls->member, key->len); \
264 PRINT_KEY(nr_acquired
)
265 PRINT_KEY(nr_contended
)
266 PRINT_TIME(avg_wait_time
)
267 PRINT_TIME(wait_time_total
)
268 PRINT_TIME(wait_time_max
)
270 static void lock_stat_key_print_wait_time_min(struct lock_key
*key
,
271 struct lock_stat
*ls
)
273 u64 wait_time
= ls
->wait_time_min
;
275 if (wait_time
== ULLONG_MAX
)
278 lock_stat_key_print_time(wait_time
, key
->len
);
282 static const char *sort_key
= "acquired";
284 static int (*compare
)(struct lock_stat
*, struct lock_stat
*);
286 static struct rb_root sorted
; /* place to store intermediate data */
287 static struct rb_root result
; /* place to store sorted data */
289 static LIST_HEAD(lock_keys
);
290 static const char *output_fields
;
292 #define DEF_KEY_LOCK(name, header, fn_suffix, len) \
293 { #name, header, len, lock_stat_key_ ## fn_suffix, lock_stat_key_print_ ## fn_suffix, {} }
294 static struct lock_key report_keys
[] = {
295 DEF_KEY_LOCK(acquired
, "acquired", nr_acquired
, 10),
296 DEF_KEY_LOCK(contended
, "contended", nr_contended
, 10),
297 DEF_KEY_LOCK(avg_wait
, "avg wait", avg_wait_time
, 12),
298 DEF_KEY_LOCK(wait_total
, "total wait", wait_time_total
, 12),
299 DEF_KEY_LOCK(wait_max
, "max wait", wait_time_max
, 12),
300 DEF_KEY_LOCK(wait_min
, "min wait", wait_time_min
, 12),
302 /* extra comparisons much complicated should be here */
306 static struct lock_key contention_keys
[] = {
307 DEF_KEY_LOCK(contended
, "contended", nr_contended
, 10),
308 DEF_KEY_LOCK(wait_total
, "total wait", wait_time_total
, 12),
309 DEF_KEY_LOCK(wait_max
, "max wait", wait_time_max
, 12),
310 DEF_KEY_LOCK(wait_min
, "min wait", wait_time_min
, 12),
311 DEF_KEY_LOCK(avg_wait
, "avg wait", avg_wait_time
, 12),
313 /* extra comparisons much complicated should be here */
317 static int select_key(bool contention
)
320 struct lock_key
*keys
= report_keys
;
323 keys
= contention_keys
;
325 for (i
= 0; keys
[i
].name
; i
++) {
326 if (!strcmp(keys
[i
].name
, sort_key
)) {
327 compare
= keys
[i
].key
;
329 /* selected key should be in the output fields */
330 if (list_empty(&keys
[i
].list
))
331 list_add_tail(&keys
[i
].list
, &lock_keys
);
337 pr_err("Unknown compare key: %s\n", sort_key
);
341 static int add_output_field(bool contention
, char *name
)
344 struct lock_key
*keys
= report_keys
;
347 keys
= contention_keys
;
349 for (i
= 0; keys
[i
].name
; i
++) {
350 if (strcmp(keys
[i
].name
, name
))
353 /* prevent double link */
354 if (list_empty(&keys
[i
].list
))
355 list_add_tail(&keys
[i
].list
, &lock_keys
);
360 pr_err("Unknown output field: %s\n", name
);
364 static int setup_output_field(bool contention
, const char *str
)
366 char *tok
, *tmp
, *orig
;
368 struct lock_key
*keys
= report_keys
;
371 keys
= contention_keys
;
373 /* no output field given: use all of them */
375 for (i
= 0; keys
[i
].name
; i
++)
376 list_add_tail(&keys
[i
].list
, &lock_keys
);
380 for (i
= 0; keys
[i
].name
; i
++)
381 INIT_LIST_HEAD(&keys
[i
].list
);
383 orig
= tmp
= strdup(str
);
387 while ((tok
= strsep(&tmp
, ",")) != NULL
){
388 ret
= add_output_field(contention
, tok
);
397 static void combine_lock_stats(struct lock_stat
*st
)
399 struct rb_node
**rb
= &sorted
.rb_node
;
400 struct rb_node
*parent
= NULL
;
405 p
= container_of(*rb
, struct lock_stat
, rb
);
408 if (st
->name
&& p
->name
)
409 ret
= strcmp(st
->name
, p
->name
);
411 ret
= !!st
->name
- !!p
->name
;
414 p
->nr_acquired
+= st
->nr_acquired
;
415 p
->nr_contended
+= st
->nr_contended
;
416 p
->wait_time_total
+= st
->wait_time_total
;
419 p
->avg_wait_time
= p
->wait_time_total
/ p
->nr_contended
;
421 if (p
->wait_time_min
> st
->wait_time_min
)
422 p
->wait_time_min
= st
->wait_time_min
;
423 if (p
->wait_time_max
< st
->wait_time_max
)
424 p
->wait_time_max
= st
->wait_time_max
;
426 p
->broken
|= st
->broken
;
432 rb
= &(*rb
)->rb_left
;
434 rb
= &(*rb
)->rb_right
;
437 rb_link_node(&st
->rb
, parent
, rb
);
438 rb_insert_color(&st
->rb
, &sorted
);
441 static void insert_to_result(struct lock_stat
*st
,
442 int (*bigger
)(struct lock_stat
*, struct lock_stat
*))
444 struct rb_node
**rb
= &result
.rb_node
;
445 struct rb_node
*parent
= NULL
;
448 if (combine_locks
&& st
->combined
)
452 p
= container_of(*rb
, struct lock_stat
, rb
);
456 rb
= &(*rb
)->rb_left
;
458 rb
= &(*rb
)->rb_right
;
461 rb_link_node(&st
->rb
, parent
, rb
);
462 rb_insert_color(&st
->rb
, &result
);
465 /* returns left most element of result, and erase it */
466 static struct lock_stat
*pop_from_result(void)
468 struct rb_node
*node
= result
.rb_node
;
473 while (node
->rb_left
)
474 node
= node
->rb_left
;
476 rb_erase(node
, &result
);
477 return container_of(node
, struct lock_stat
, rb
);
480 struct lock_stat
*lock_stat_find(u64 addr
)
482 struct hlist_head
*entry
= lockhashentry(addr
);
483 struct lock_stat
*ret
;
485 hlist_for_each_entry(ret
, entry
, hash_entry
) {
486 if (ret
->addr
== addr
)
492 struct lock_stat
*lock_stat_findnew(u64 addr
, const char *name
, int flags
)
494 struct hlist_head
*entry
= lockhashentry(addr
);
495 struct lock_stat
*ret
, *new;
497 hlist_for_each_entry(ret
, entry
, hash_entry
) {
498 if (ret
->addr
== addr
)
502 new = zalloc(sizeof(struct lock_stat
));
507 new->name
= strdup(name
);
514 new->wait_time_min
= ULLONG_MAX
;
516 hlist_add_head(&new->hash_entry
, entry
);
520 pr_err("memory allocation failed\n");
524 bool match_callstack_filter(struct machine
*machine
, u64
*callstack
)
529 const char *arch
= perf_env__arch(machine
->env
);
531 if (list_empty(&callstack_filters
))
534 for (int i
= 0; i
< max_stack_depth
; i
++) {
535 struct callstack_filter
*filter
;
538 * In powerpc, the callchain saved by kernel always includes
539 * first three entries as the NIP (next instruction pointer),
540 * LR (link register), and the contents of LR save area in the
541 * second stack frame. In certain scenarios its possible to have
542 * invalid kernel instruction addresses in either LR or the second
543 * stack frame's LR. In that case, kernel will store that address as
546 * The below check will continue to look into callstack,
547 * incase first or second callstack index entry has 0
548 * address for powerpc.
550 if (!callstack
|| (!callstack
[i
] && (strcmp(arch
, "powerpc") ||
551 (i
!= 1 && i
!= 2))))
555 sym
= machine__find_kernel_symbol(machine
, ip
, &kmap
);
559 list_for_each_entry(filter
, &callstack_filters
, list
) {
560 if (strstr(sym
->name
, filter
->name
))
567 struct trace_lock_handler
{
568 /* it's used on CONFIG_LOCKDEP */
569 int (*acquire_event
)(struct evsel
*evsel
,
570 struct perf_sample
*sample
);
572 /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */
573 int (*acquired_event
)(struct evsel
*evsel
,
574 struct perf_sample
*sample
);
576 /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */
577 int (*contended_event
)(struct evsel
*evsel
,
578 struct perf_sample
*sample
);
580 /* it's used on CONFIG_LOCKDEP */
581 int (*release_event
)(struct evsel
*evsel
,
582 struct perf_sample
*sample
);
584 /* it's used when CONFIG_LOCKDEP is off */
585 int (*contention_begin_event
)(struct evsel
*evsel
,
586 struct perf_sample
*sample
);
588 /* it's used when CONFIG_LOCKDEP is off */
589 int (*contention_end_event
)(struct evsel
*evsel
,
590 struct perf_sample
*sample
);
593 static struct lock_seq_stat
*get_seq(struct thread_stat
*ts
, u64 addr
)
595 struct lock_seq_stat
*seq
;
597 list_for_each_entry(seq
, &ts
->seq_list
, list
) {
598 if (seq
->addr
== addr
)
602 seq
= zalloc(sizeof(struct lock_seq_stat
));
604 pr_err("memory allocation failed\n");
607 seq
->state
= SEQ_STATE_UNINITIALIZED
;
610 list_add(&seq
->list
, &ts
->seq_list
);
622 static int bad_hist
[BROKEN_MAX
];
629 static int get_key_by_aggr_mode_simple(u64
*key
, u64 addr
, u32 tid
)
638 case LOCK_AGGR_CALLER
:
639 case LOCK_AGGR_CGROUP
:
641 pr_err("Invalid aggregation mode: %d\n", aggr_mode
);
647 static u64
callchain_id(struct evsel
*evsel
, struct perf_sample
*sample
);
649 static int get_key_by_aggr_mode(u64
*key
, u64 addr
, struct evsel
*evsel
,
650 struct perf_sample
*sample
)
652 if (aggr_mode
== LOCK_AGGR_CALLER
) {
653 *key
= callchain_id(evsel
, sample
);
656 return get_key_by_aggr_mode_simple(key
, addr
, sample
->tid
);
659 static int report_lock_acquire_event(struct evsel
*evsel
,
660 struct perf_sample
*sample
)
662 struct lock_stat
*ls
;
663 struct thread_stat
*ts
;
664 struct lock_seq_stat
*seq
;
665 const char *name
= evsel__strval(evsel
, sample
, "name");
666 u64 addr
= evsel__intval(evsel
, sample
, "lockdep_addr");
667 int flag
= evsel__intval(evsel
, sample
, "flags");
671 ret
= get_key_by_aggr_mode_simple(&key
, addr
, sample
->tid
);
675 ls
= lock_stat_findnew(key
, name
, 0);
679 ts
= thread_stat_findnew(sample
->tid
);
683 seq
= get_seq(ts
, addr
);
687 switch (seq
->state
) {
688 case SEQ_STATE_UNINITIALIZED
:
689 case SEQ_STATE_RELEASED
:
691 seq
->state
= SEQ_STATE_ACQUIRING
;
695 if (flag
& READ_LOCK
)
697 seq
->state
= SEQ_STATE_READ_ACQUIRED
;
702 case SEQ_STATE_READ_ACQUIRED
:
703 if (flag
& READ_LOCK
) {
711 case SEQ_STATE_ACQUIRED
:
712 case SEQ_STATE_ACQUIRING
:
713 case SEQ_STATE_CONTENDED
:
715 /* broken lock sequence */
718 bad_hist
[BROKEN_ACQUIRE
]++;
720 list_del_init(&seq
->list
);
724 BUG_ON("Unknown state of lock sequence found!\n");
729 seq
->prev_event_time
= sample
->time
;
734 static int report_lock_acquired_event(struct evsel
*evsel
,
735 struct perf_sample
*sample
)
737 struct lock_stat
*ls
;
738 struct thread_stat
*ts
;
739 struct lock_seq_stat
*seq
;
741 const char *name
= evsel__strval(evsel
, sample
, "name");
742 u64 addr
= evsel__intval(evsel
, sample
, "lockdep_addr");
746 ret
= get_key_by_aggr_mode_simple(&key
, addr
, sample
->tid
);
750 ls
= lock_stat_findnew(key
, name
, 0);
754 ts
= thread_stat_findnew(sample
->tid
);
758 seq
= get_seq(ts
, addr
);
762 switch (seq
->state
) {
763 case SEQ_STATE_UNINITIALIZED
:
764 /* orphan event, do nothing */
766 case SEQ_STATE_ACQUIRING
:
768 case SEQ_STATE_CONTENDED
:
769 contended_term
= sample
->time
- seq
->prev_event_time
;
770 ls
->wait_time_total
+= contended_term
;
771 if (contended_term
< ls
->wait_time_min
)
772 ls
->wait_time_min
= contended_term
;
773 if (ls
->wait_time_max
< contended_term
)
774 ls
->wait_time_max
= contended_term
;
776 case SEQ_STATE_RELEASED
:
777 case SEQ_STATE_ACQUIRED
:
778 case SEQ_STATE_READ_ACQUIRED
:
779 /* broken lock sequence */
782 bad_hist
[BROKEN_ACQUIRED
]++;
784 list_del_init(&seq
->list
);
788 BUG_ON("Unknown state of lock sequence found!\n");
792 seq
->state
= SEQ_STATE_ACQUIRED
;
794 ls
->avg_wait_time
= ls
->nr_contended
? ls
->wait_time_total
/ls
->nr_contended
: 0;
795 seq
->prev_event_time
= sample
->time
;
800 static int report_lock_contended_event(struct evsel
*evsel
,
801 struct perf_sample
*sample
)
803 struct lock_stat
*ls
;
804 struct thread_stat
*ts
;
805 struct lock_seq_stat
*seq
;
806 const char *name
= evsel__strval(evsel
, sample
, "name");
807 u64 addr
= evsel__intval(evsel
, sample
, "lockdep_addr");
811 ret
= get_key_by_aggr_mode_simple(&key
, addr
, sample
->tid
);
815 ls
= lock_stat_findnew(key
, name
, 0);
819 ts
= thread_stat_findnew(sample
->tid
);
823 seq
= get_seq(ts
, addr
);
827 switch (seq
->state
) {
828 case SEQ_STATE_UNINITIALIZED
:
829 /* orphan event, do nothing */
831 case SEQ_STATE_ACQUIRING
:
833 case SEQ_STATE_RELEASED
:
834 case SEQ_STATE_ACQUIRED
:
835 case SEQ_STATE_READ_ACQUIRED
:
836 case SEQ_STATE_CONTENDED
:
837 /* broken lock sequence */
840 bad_hist
[BROKEN_CONTENDED
]++;
842 list_del_init(&seq
->list
);
846 BUG_ON("Unknown state of lock sequence found!\n");
850 seq
->state
= SEQ_STATE_CONTENDED
;
852 ls
->avg_wait_time
= ls
->wait_time_total
/ls
->nr_contended
;
853 seq
->prev_event_time
= sample
->time
;
858 static int report_lock_release_event(struct evsel
*evsel
,
859 struct perf_sample
*sample
)
861 struct lock_stat
*ls
;
862 struct thread_stat
*ts
;
863 struct lock_seq_stat
*seq
;
864 const char *name
= evsel__strval(evsel
, sample
, "name");
865 u64 addr
= evsel__intval(evsel
, sample
, "lockdep_addr");
869 ret
= get_key_by_aggr_mode_simple(&key
, addr
, sample
->tid
);
873 ls
= lock_stat_findnew(key
, name
, 0);
877 ts
= thread_stat_findnew(sample
->tid
);
881 seq
= get_seq(ts
, addr
);
885 switch (seq
->state
) {
886 case SEQ_STATE_UNINITIALIZED
:
888 case SEQ_STATE_ACQUIRED
:
890 case SEQ_STATE_READ_ACQUIRED
:
892 BUG_ON(seq
->read_count
< 0);
893 if (seq
->read_count
) {
898 case SEQ_STATE_ACQUIRING
:
899 case SEQ_STATE_CONTENDED
:
900 case SEQ_STATE_RELEASED
:
901 /* broken lock sequence */
904 bad_hist
[BROKEN_RELEASE
]++;
908 BUG_ON("Unknown state of lock sequence found!\n");
914 list_del_init(&seq
->list
);
920 static int get_symbol_name_offset(struct map
*map
, struct symbol
*sym
, u64 ip
,
925 if (map
== NULL
|| sym
== NULL
) {
930 offset
= map__map_ip(map
, ip
) - sym
->start
;
933 return scnprintf(buf
, size
, "%s+%#lx", sym
->name
, offset
);
935 return strlcpy(buf
, sym
->name
, size
);
937 static int lock_contention_caller(struct evsel
*evsel
, struct perf_sample
*sample
,
940 struct thread
*thread
;
941 struct callchain_cursor
*cursor
;
942 struct machine
*machine
= &session
->machines
.host
;
947 /* lock names will be replaced to task name later */
948 if (show_thread_stats
)
951 thread
= machine__findnew_thread(machine
, -1, sample
->pid
);
955 cursor
= get_tls_callchain_cursor();
957 /* use caller function name from the callchain */
958 ret
= thread__resolve_callchain(thread
, cursor
, evsel
, sample
,
959 NULL
, NULL
, max_stack_depth
);
965 callchain_cursor_commit(cursor
);
969 struct callchain_cursor_node
*node
;
971 node
= callchain_cursor_current(cursor
);
975 /* skip first few entries - for lock functions */
976 if (++skip
<= stack_skip
)
980 if (sym
&& !machine__is_lock_function(machine
, node
->ip
)) {
981 get_symbol_name_offset(node
->ms
.map
, sym
, node
->ip
,
987 callchain_cursor_advance(cursor
);
992 static u64
callchain_id(struct evsel
*evsel
, struct perf_sample
*sample
)
994 struct callchain_cursor
*cursor
;
995 struct machine
*machine
= &session
->machines
.host
;
996 struct thread
*thread
;
1001 thread
= machine__findnew_thread(machine
, -1, sample
->pid
);
1005 cursor
= get_tls_callchain_cursor();
1006 /* use caller function name from the callchain */
1007 ret
= thread__resolve_callchain(thread
, cursor
, evsel
, sample
,
1008 NULL
, NULL
, max_stack_depth
);
1009 thread__put(thread
);
1014 callchain_cursor_commit(cursor
);
1017 struct callchain_cursor_node
*node
;
1019 node
= callchain_cursor_current(cursor
);
1023 /* skip first few entries - for lock functions */
1024 if (++skip
<= stack_skip
)
1027 if (node
->ms
.sym
&& machine__is_lock_function(machine
, node
->ip
))
1030 hash
^= hash_long((unsigned long)node
->ip
, 64);
1033 callchain_cursor_advance(cursor
);
1038 static u64
*get_callstack(struct perf_sample
*sample
, int max_stack
)
1044 callstack
= calloc(max_stack
, sizeof(*callstack
));
1045 if (callstack
== NULL
)
1048 for (i
= 0, c
= 0; i
< sample
->callchain
->nr
&& c
< max_stack
; i
++) {
1049 u64 ip
= sample
->callchain
->ips
[i
];
1051 if (ip
>= PERF_CONTEXT_MAX
)
1054 callstack
[c
++] = ip
;
1059 static int report_lock_contention_begin_event(struct evsel
*evsel
,
1060 struct perf_sample
*sample
)
1062 struct lock_stat
*ls
;
1063 struct thread_stat
*ts
;
1064 struct lock_seq_stat
*seq
;
1065 u64 addr
= evsel__intval(evsel
, sample
, "lock_addr");
1066 unsigned int flags
= evsel__intval(evsel
, sample
, "flags");
1069 static bool kmap_loaded
;
1070 struct machine
*machine
= &session
->machines
.host
;
1074 ret
= get_key_by_aggr_mode(&key
, addr
, evsel
, sample
);
1079 unsigned long *addrs
;
1081 /* make sure it loads the kernel map to find lock symbols */
1082 map__load(machine__kernel_map(machine
));
1085 /* convert (kernel) symbols to addresses */
1086 for (i
= 0; i
< filters
.nr_syms
; i
++) {
1087 sym
= machine__find_kernel_symbol_by_name(machine
,
1091 pr_warning("ignore unknown symbol: %s\n",
1096 addrs
= realloc(filters
.addrs
,
1097 (filters
.nr_addrs
+ 1) * sizeof(*addrs
));
1098 if (addrs
== NULL
) {
1099 pr_warning("memory allocation failure\n");
1103 addrs
[filters
.nr_addrs
++] = map__unmap_ip(kmap
, sym
->start
);
1104 filters
.addrs
= addrs
;
1108 ls
= lock_stat_find(key
);
1111 const char *name
= "";
1113 switch (aggr_mode
) {
1114 case LOCK_AGGR_ADDR
:
1115 sym
= machine__find_kernel_symbol(machine
, key
, &kmap
);
1119 case LOCK_AGGR_CALLER
:
1121 if (lock_contention_caller(evsel
, sample
, buf
, sizeof(buf
)) < 0)
1124 case LOCK_AGGR_CGROUP
:
1125 case LOCK_AGGR_TASK
:
1130 ls
= lock_stat_findnew(key
, name
, flags
);
1135 if (filters
.nr_types
) {
1138 for (i
= 0; i
< filters
.nr_types
; i
++) {
1139 if (flags
== filters
.types
[i
]) {
1149 if (filters
.nr_addrs
) {
1152 for (i
= 0; i
< filters
.nr_addrs
; i
++) {
1153 if (addr
== filters
.addrs
[i
]) {
1163 if (needs_callstack()) {
1164 u64
*callstack
= get_callstack(sample
, max_stack_depth
);
1165 if (callstack
== NULL
)
1168 if (!match_callstack_filter(machine
, callstack
)) {
1173 if (ls
->callstack
== NULL
)
1174 ls
->callstack
= callstack
;
1179 ts
= thread_stat_findnew(sample
->tid
);
1183 seq
= get_seq(ts
, addr
);
1187 switch (seq
->state
) {
1188 case SEQ_STATE_UNINITIALIZED
:
1189 case SEQ_STATE_ACQUIRED
:
1191 case SEQ_STATE_CONTENDED
:
1193 * It can have nested contention begin with mutex spinning,
1194 * then we would use the original contention begin event and
1195 * ignore the second one.
1198 case SEQ_STATE_ACQUIRING
:
1199 case SEQ_STATE_READ_ACQUIRED
:
1200 case SEQ_STATE_RELEASED
:
1201 /* broken lock sequence */
1204 bad_hist
[BROKEN_CONTENDED
]++;
1206 list_del_init(&seq
->list
);
1210 BUG_ON("Unknown state of lock sequence found!\n");
1214 if (seq
->state
!= SEQ_STATE_CONTENDED
) {
1215 seq
->state
= SEQ_STATE_CONTENDED
;
1216 seq
->prev_event_time
= sample
->time
;
1223 static int report_lock_contention_end_event(struct evsel
*evsel
,
1224 struct perf_sample
*sample
)
1226 struct lock_stat
*ls
;
1227 struct thread_stat
*ts
;
1228 struct lock_seq_stat
*seq
;
1230 u64 addr
= evsel__intval(evsel
, sample
, "lock_addr");
1234 ret
= get_key_by_aggr_mode(&key
, addr
, evsel
, sample
);
1238 ls
= lock_stat_find(key
);
1242 ts
= thread_stat_find(sample
->tid
);
1246 seq
= get_seq(ts
, addr
);
1250 switch (seq
->state
) {
1251 case SEQ_STATE_UNINITIALIZED
:
1253 case SEQ_STATE_CONTENDED
:
1254 contended_term
= sample
->time
- seq
->prev_event_time
;
1255 ls
->wait_time_total
+= contended_term
;
1256 if (contended_term
< ls
->wait_time_min
)
1257 ls
->wait_time_min
= contended_term
;
1258 if (ls
->wait_time_max
< contended_term
)
1259 ls
->wait_time_max
= contended_term
;
1261 case SEQ_STATE_ACQUIRING
:
1262 case SEQ_STATE_ACQUIRED
:
1263 case SEQ_STATE_READ_ACQUIRED
:
1264 case SEQ_STATE_RELEASED
:
1265 /* broken lock sequence */
1268 bad_hist
[BROKEN_ACQUIRED
]++;
1270 list_del_init(&seq
->list
);
1274 BUG_ON("Unknown state of lock sequence found!\n");
1278 seq
->state
= SEQ_STATE_ACQUIRED
;
1280 ls
->avg_wait_time
= ls
->wait_time_total
/ls
->nr_acquired
;
1285 /* lock oriented handlers */
1286 /* TODO: handlers for CPU oriented, thread oriented */
1287 static struct trace_lock_handler report_lock_ops
= {
1288 .acquire_event
= report_lock_acquire_event
,
1289 .acquired_event
= report_lock_acquired_event
,
1290 .contended_event
= report_lock_contended_event
,
1291 .release_event
= report_lock_release_event
,
1292 .contention_begin_event
= report_lock_contention_begin_event
,
1293 .contention_end_event
= report_lock_contention_end_event
,
1296 static struct trace_lock_handler contention_lock_ops
= {
1297 .contention_begin_event
= report_lock_contention_begin_event
,
1298 .contention_end_event
= report_lock_contention_end_event
,
1302 static struct trace_lock_handler
*trace_handler
;
1304 static int evsel__process_lock_acquire(struct evsel
*evsel
, struct perf_sample
*sample
)
1306 if (trace_handler
->acquire_event
)
1307 return trace_handler
->acquire_event(evsel
, sample
);
1311 static int evsel__process_lock_acquired(struct evsel
*evsel
, struct perf_sample
*sample
)
1313 if (trace_handler
->acquired_event
)
1314 return trace_handler
->acquired_event(evsel
, sample
);
1318 static int evsel__process_lock_contended(struct evsel
*evsel
, struct perf_sample
*sample
)
1320 if (trace_handler
->contended_event
)
1321 return trace_handler
->contended_event(evsel
, sample
);
1325 static int evsel__process_lock_release(struct evsel
*evsel
, struct perf_sample
*sample
)
1327 if (trace_handler
->release_event
)
1328 return trace_handler
->release_event(evsel
, sample
);
1332 static int evsel__process_contention_begin(struct evsel
*evsel
, struct perf_sample
*sample
)
1334 if (trace_handler
->contention_begin_event
)
1335 return trace_handler
->contention_begin_event(evsel
, sample
);
1339 static int evsel__process_contention_end(struct evsel
*evsel
, struct perf_sample
*sample
)
1341 if (trace_handler
->contention_end_event
)
1342 return trace_handler
->contention_end_event(evsel
, sample
);
1346 static void print_bad_events(int bad
, int total
)
1348 /* Output for debug, this have to be removed */
1351 const char *name
[4] =
1352 { "acquire", "acquired", "contended", "release" };
1354 for (i
= 0; i
< BROKEN_MAX
; i
++)
1355 broken
+= bad_hist
[i
];
1357 if (quiet
|| total
== 0 || (broken
== 0 && verbose
<= 0))
1360 fprintf(lock_output
, "\n=== output for debug ===\n\n");
1361 fprintf(lock_output
, "bad: %d, total: %d\n", bad
, total
);
1362 fprintf(lock_output
, "bad rate: %.2f %%\n", (double)bad
/ (double)total
* 100);
1363 fprintf(lock_output
, "histogram of events caused bad sequence\n");
1364 for (i
= 0; i
< BROKEN_MAX
; i
++)
1365 fprintf(lock_output
, " %10s: %d\n", name
[i
], bad_hist
[i
]);
1368 /* TODO: various way to print, coloring, nano or milli sec */
1369 static void print_result(void)
1371 struct lock_stat
*st
;
1372 struct lock_key
*key
;
1374 int bad
, total
, printed
;
1377 fprintf(lock_output
, "%20s ", "Name");
1378 list_for_each_entry(key
, &lock_keys
, list
)
1379 fprintf(lock_output
, "%*s ", key
->len
, key
->header
);
1380 fprintf(lock_output
, "\n\n");
1383 bad
= total
= printed
= 0;
1384 while ((st
= pop_from_result())) {
1388 if (!st
->nr_acquired
)
1391 bzero(cut_name
, 20);
1393 if (strlen(st
->name
) < 20) {
1394 /* output raw name */
1395 const char *name
= st
->name
;
1397 if (show_thread_stats
) {
1400 /* st->addr contains tid of thread */
1401 t
= perf_session__findnew(session
, st
->addr
);
1402 name
= thread__comm_str(t
);
1405 fprintf(lock_output
, "%20s ", name
);
1407 strncpy(cut_name
, st
->name
, 16);
1411 cut_name
[19] = '\0';
1412 /* cut off name for saving output style */
1413 fprintf(lock_output
, "%20s ", cut_name
);
1416 list_for_each_entry(key
, &lock_keys
, list
) {
1417 key
->print(key
, st
);
1418 fprintf(lock_output
, " ");
1420 fprintf(lock_output
, "\n");
1422 if (++printed
>= print_nr_entries
)
1426 print_bad_events(bad
, total
);
1429 static bool info_threads
, info_map
;
1431 static void dump_threads(void)
1433 struct thread_stat
*st
;
1434 struct rb_node
*node
;
1437 fprintf(lock_output
, "%10s: comm\n", "Thread ID");
1439 node
= rb_first(&thread_stats
);
1441 st
= container_of(node
, struct thread_stat
, rb
);
1442 t
= perf_session__findnew(session
, st
->tid
);
1443 fprintf(lock_output
, "%10d: %s\n", st
->tid
, thread__comm_str(t
));
1444 node
= rb_next(node
);
1449 static int compare_maps(struct lock_stat
*a
, struct lock_stat
*b
)
1453 if (a
->name
&& b
->name
)
1454 ret
= strcmp(a
->name
, b
->name
);
1456 ret
= !!a
->name
- !!b
->name
;
1459 return a
->addr
< b
->addr
;
1464 static void dump_map(void)
1467 struct lock_stat
*st
;
1469 fprintf(lock_output
, "Address of instance: name of class\n");
1470 for (i
= 0; i
< LOCKHASH_SIZE
; i
++) {
1471 hlist_for_each_entry(st
, &lockhash_table
[i
], hash_entry
) {
1472 insert_to_result(st
, compare_maps
);
1476 while ((st
= pop_from_result()))
1477 fprintf(lock_output
, " %#llx: %s\n", (unsigned long long)st
->addr
, st
->name
);
1480 static void dump_info(void)
1487 fputc('\n', lock_output
);
1492 static const struct evsel_str_handler lock_tracepoints
[] = {
1493 { "lock:lock_acquire", evsel__process_lock_acquire
, }, /* CONFIG_LOCKDEP */
1494 { "lock:lock_acquired", evsel__process_lock_acquired
, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
1495 { "lock:lock_contended", evsel__process_lock_contended
, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
1496 { "lock:lock_release", evsel__process_lock_release
, }, /* CONFIG_LOCKDEP */
1499 static const struct evsel_str_handler contention_tracepoints
[] = {
1500 { "lock:contention_begin", evsel__process_contention_begin
, },
1501 { "lock:contention_end", evsel__process_contention_end
, },
1504 static int process_event_update(const struct perf_tool
*tool
,
1505 union perf_event
*event
,
1506 struct evlist
**pevlist
)
1510 ret
= perf_event__process_event_update(tool
, event
, pevlist
);
1514 /* this can return -EEXIST since we call it for each evsel */
1515 perf_session__set_tracepoints_handlers(session
, lock_tracepoints
);
1516 perf_session__set_tracepoints_handlers(session
, contention_tracepoints
);
1520 typedef int (*tracepoint_handler
)(struct evsel
*evsel
,
1521 struct perf_sample
*sample
);
1523 static int process_sample_event(const struct perf_tool
*tool __maybe_unused
,
1524 union perf_event
*event
,
1525 struct perf_sample
*sample
,
1526 struct evsel
*evsel
,
1527 struct machine
*machine
)
1530 struct thread
*thread
= machine__findnew_thread(machine
, sample
->pid
,
1533 if (thread
== NULL
) {
1534 pr_debug("problem processing %d event, skipping it.\n",
1535 event
->header
.type
);
1539 if (evsel
->handler
!= NULL
) {
1540 tracepoint_handler f
= evsel
->handler
;
1541 err
= f(evsel
, sample
);
1544 thread__put(thread
);
1549 static void combine_result(void)
1552 struct lock_stat
*st
;
1557 for (i
= 0; i
< LOCKHASH_SIZE
; i
++) {
1558 hlist_for_each_entry(st
, &lockhash_table
[i
], hash_entry
) {
1559 combine_lock_stats(st
);
1564 static void sort_result(void)
1567 struct lock_stat
*st
;
1569 for (i
= 0; i
< LOCKHASH_SIZE
; i
++) {
1570 hlist_for_each_entry(st
, &lockhash_table
[i
], hash_entry
) {
1571 insert_to_result(st
, compare
);
1576 static const struct {
1580 } lock_type_table
[] = {
1581 { 0, "semaphore", "semaphore" },
1582 { LCB_F_SPIN
, "spinlock", "spinlock" },
1583 { LCB_F_SPIN
| LCB_F_READ
, "rwlock:R", "rwlock" },
1584 { LCB_F_SPIN
| LCB_F_WRITE
, "rwlock:W", "rwlock" },
1585 { LCB_F_READ
, "rwsem:R", "rwsem" },
1586 { LCB_F_WRITE
, "rwsem:W", "rwsem" },
1587 { LCB_F_RT
, "rt-mutex", "rt-mutex" },
1588 { LCB_F_RT
| LCB_F_READ
, "rwlock-rt:R", "rwlock-rt" },
1589 { LCB_F_RT
| LCB_F_WRITE
, "rwlock-rt:W", "rwlock-rt" },
1590 { LCB_F_PERCPU
| LCB_F_READ
, "pcpu-sem:R", "percpu-rwsem" },
1591 { LCB_F_PERCPU
| LCB_F_WRITE
, "pcpu-sem:W", "percpu-rwsem" },
1592 { LCB_F_MUTEX
, "mutex", "mutex" },
1593 { LCB_F_MUTEX
| LCB_F_SPIN
, "mutex", "mutex" },
1594 /* alias for get_type_flag() */
1595 { LCB_F_MUTEX
| LCB_F_SPIN
, "mutex-spin", "mutex" },
1598 static const char *get_type_str(unsigned int flags
)
1600 flags
&= LCB_F_MAX_FLAGS
- 1;
1602 for (unsigned int i
= 0; i
< ARRAY_SIZE(lock_type_table
); i
++) {
1603 if (lock_type_table
[i
].flags
== flags
)
1604 return lock_type_table
[i
].str
;
1609 static const char *get_type_name(unsigned int flags
)
1611 flags
&= LCB_F_MAX_FLAGS
- 1;
1613 for (unsigned int i
= 0; i
< ARRAY_SIZE(lock_type_table
); i
++) {
1614 if (lock_type_table
[i
].flags
== flags
)
1615 return lock_type_table
[i
].name
;
1620 static unsigned int get_type_flag(const char *str
)
1622 for (unsigned int i
= 0; i
< ARRAY_SIZE(lock_type_table
); i
++) {
1623 if (!strcmp(lock_type_table
[i
].name
, str
))
1624 return lock_type_table
[i
].flags
;
1626 for (unsigned int i
= 0; i
< ARRAY_SIZE(lock_type_table
); i
++) {
1627 if (!strcmp(lock_type_table
[i
].str
, str
))
1628 return lock_type_table
[i
].flags
;
1633 static void lock_filter_finish(void)
1635 zfree(&filters
.types
);
1636 filters
.nr_types
= 0;
1638 zfree(&filters
.addrs
);
1639 filters
.nr_addrs
= 0;
1641 for (int i
= 0; i
< filters
.nr_syms
; i
++)
1642 free(filters
.syms
[i
]);
1644 zfree(&filters
.syms
);
1645 filters
.nr_syms
= 0;
1647 zfree(&filters
.cgrps
);
1648 filters
.nr_cgrps
= 0;
1651 static void sort_contention_result(void)
1656 static void print_header_stdio(void)
1658 struct lock_key
*key
;
1660 list_for_each_entry(key
, &lock_keys
, list
)
1661 fprintf(lock_output
, "%*s ", key
->len
, key
->header
);
1663 switch (aggr_mode
) {
1664 case LOCK_AGGR_TASK
:
1665 fprintf(lock_output
, " %10s %s\n\n", "pid",
1666 show_lock_owner
? "owner" : "comm");
1668 case LOCK_AGGR_CALLER
:
1669 fprintf(lock_output
, " %10s %s\n\n", "type", "caller");
1671 case LOCK_AGGR_ADDR
:
1672 fprintf(lock_output
, " %16s %s\n\n", "address", "symbol");
1674 case LOCK_AGGR_CGROUP
:
1675 fprintf(lock_output
, " %s\n\n", "cgroup");
1682 static void print_header_csv(const char *sep
)
1684 struct lock_key
*key
;
1686 fprintf(lock_output
, "# output: ");
1687 list_for_each_entry(key
, &lock_keys
, list
)
1688 fprintf(lock_output
, "%s%s ", key
->header
, sep
);
1690 switch (aggr_mode
) {
1691 case LOCK_AGGR_TASK
:
1692 fprintf(lock_output
, "%s%s %s\n", "pid", sep
,
1693 show_lock_owner
? "owner" : "comm");
1695 case LOCK_AGGR_CALLER
:
1696 fprintf(lock_output
, "%s%s %s", "type", sep
, "caller");
1698 fprintf(lock_output
, "%s %s", sep
, "stacktrace");
1699 fprintf(lock_output
, "\n");
1701 case LOCK_AGGR_ADDR
:
1702 fprintf(lock_output
, "%s%s %s%s %s\n", "address", sep
, "symbol", sep
, "type");
1704 case LOCK_AGGR_CGROUP
:
1705 fprintf(lock_output
, "%s\n", "cgroup");
1712 static void print_header(void)
1715 if (symbol_conf
.field_sep
)
1716 print_header_csv(symbol_conf
.field_sep
);
1718 print_header_stdio();
1722 static void print_lock_stat_stdio(struct lock_contention
*con
, struct lock_stat
*st
)
1724 struct lock_key
*key
;
1728 list_for_each_entry(key
, &lock_keys
, list
) {
1729 key
->print(key
, st
);
1730 fprintf(lock_output
, " ");
1733 switch (aggr_mode
) {
1734 case LOCK_AGGR_CALLER
:
1735 fprintf(lock_output
, " %10s %s\n", get_type_str(st
->flags
), st
->name
);
1737 case LOCK_AGGR_TASK
:
1739 t
= perf_session__findnew(session
, pid
);
1740 fprintf(lock_output
, " %10d %s\n",
1741 pid
, pid
== -1 ? "Unknown" : thread__comm_str(t
));
1743 case LOCK_AGGR_ADDR
:
1744 fprintf(lock_output
, " %016llx %s (%s)\n", (unsigned long long)st
->addr
,
1745 st
->name
, get_type_name(st
->flags
));
1747 case LOCK_AGGR_CGROUP
:
1748 fprintf(lock_output
, " %s\n", st
->name
);
1754 if (aggr_mode
== LOCK_AGGR_CALLER
&& verbose
> 0) {
1760 for (int i
= 0; i
< max_stack_depth
; i
++) {
1761 if (!st
->callstack
|| !st
->callstack
[i
])
1764 ip
= st
->callstack
[i
];
1765 sym
= machine__find_kernel_symbol(con
->machine
, ip
, &kmap
);
1766 get_symbol_name_offset(kmap
, sym
, ip
, buf
, sizeof(buf
));
1767 fprintf(lock_output
, "\t\t\t%#lx %s\n", (unsigned long)ip
, buf
);
1772 static void print_lock_stat_csv(struct lock_contention
*con
, struct lock_stat
*st
,
1775 struct lock_key
*key
;
1779 list_for_each_entry(key
, &lock_keys
, list
) {
1780 key
->print(key
, st
);
1781 fprintf(lock_output
, "%s ", sep
);
1784 switch (aggr_mode
) {
1785 case LOCK_AGGR_CALLER
:
1786 fprintf(lock_output
, "%s%s %s", get_type_str(st
->flags
), sep
, st
->name
);
1788 fprintf(lock_output
, "\n");
1790 case LOCK_AGGR_TASK
:
1792 t
= perf_session__findnew(session
, pid
);
1793 fprintf(lock_output
, "%d%s %s\n", pid
, sep
,
1794 pid
== -1 ? "Unknown" : thread__comm_str(t
));
1796 case LOCK_AGGR_ADDR
:
1797 fprintf(lock_output
, "%llx%s %s%s %s\n", (unsigned long long)st
->addr
, sep
,
1798 st
->name
, sep
, get_type_name(st
->flags
));
1800 case LOCK_AGGR_CGROUP
:
1801 fprintf(lock_output
, "%s\n",st
->name
);
1807 if (aggr_mode
== LOCK_AGGR_CALLER
&& verbose
> 0) {
1813 for (int i
= 0; i
< max_stack_depth
; i
++) {
1814 if (!st
->callstack
|| !st
->callstack
[i
])
1817 ip
= st
->callstack
[i
];
1818 sym
= machine__find_kernel_symbol(con
->machine
, ip
, &kmap
);
1819 get_symbol_name_offset(kmap
, sym
, ip
, buf
, sizeof(buf
));
1820 fprintf(lock_output
, "%s %#lx %s", i
? ":" : sep
, (unsigned long) ip
, buf
);
1822 fprintf(lock_output
, "\n");
1826 static void print_lock_stat(struct lock_contention
*con
, struct lock_stat
*st
)
1828 if (symbol_conf
.field_sep
)
1829 print_lock_stat_csv(con
, st
, symbol_conf
.field_sep
);
1831 print_lock_stat_stdio(con
, st
);
1834 static void print_footer_stdio(int total
, int bad
, struct lock_contention_fails
*fails
)
1836 /* Output for debug, this have to be removed */
1837 int broken
= fails
->task
+ fails
->stack
+ fails
->time
+ fails
->data
;
1840 print_bad_events(bad
, total
);
1842 if (quiet
|| total
== 0 || (broken
== 0 && verbose
<= 0))
1846 fprintf(lock_output
, "\n=== output for debug ===\n\n");
1847 fprintf(lock_output
, "bad: %d, total: %d\n", broken
, total
);
1848 fprintf(lock_output
, "bad rate: %.2f %%\n", 100.0 * broken
/ total
);
1850 fprintf(lock_output
, "histogram of failure reasons\n");
1851 fprintf(lock_output
, " %10s: %d\n", "task", fails
->task
);
1852 fprintf(lock_output
, " %10s: %d\n", "stack", fails
->stack
);
1853 fprintf(lock_output
, " %10s: %d\n", "time", fails
->time
);
1854 fprintf(lock_output
, " %10s: %d\n", "data", fails
->data
);
1857 static void print_footer_csv(int total
, int bad
, struct lock_contention_fails
*fails
,
1860 /* Output for debug, this have to be removed */
1862 bad
= fails
->task
+ fails
->stack
+ fails
->time
+ fails
->data
;
1864 if (quiet
|| total
== 0 || (bad
== 0 && verbose
<= 0))
1868 fprintf(lock_output
, "# debug: total=%d%s bad=%d", total
, sep
, bad
);
1871 fprintf(lock_output
, "%s bad_%s=%d", sep
, "task", fails
->task
);
1872 fprintf(lock_output
, "%s bad_%s=%d", sep
, "stack", fails
->stack
);
1873 fprintf(lock_output
, "%s bad_%s=%d", sep
, "time", fails
->time
);
1874 fprintf(lock_output
, "%s bad_%s=%d", sep
, "data", fails
->data
);
1877 const char *name
[4] = { "acquire", "acquired", "contended", "release" };
1879 for (i
= 0; i
< BROKEN_MAX
; i
++)
1880 fprintf(lock_output
, "%s bad_%s=%d", sep
, name
[i
], bad_hist
[i
]);
1882 fprintf(lock_output
, "\n");
1885 static void print_footer(int total
, int bad
, struct lock_contention_fails
*fails
)
1887 if (symbol_conf
.field_sep
)
1888 print_footer_csv(total
, bad
, fails
, symbol_conf
.field_sep
);
1890 print_footer_stdio(total
, bad
, fails
);
1893 static void print_contention_result(struct lock_contention
*con
)
1895 struct lock_stat
*st
;
1896 int bad
, total
, printed
;
1901 bad
= total
= printed
= 0;
1903 while ((st
= pop_from_result())) {
1904 total
+= use_bpf
? st
->nr_contended
: 1;
1908 if (!st
->wait_time_total
)
1911 print_lock_stat(con
, st
);
1913 if (++printed
>= print_nr_entries
)
1917 if (print_nr_entries
) {
1918 /* update the total/bad stats */
1919 while ((st
= pop_from_result())) {
1920 total
+= use_bpf
? st
->nr_contended
: 1;
1925 /* some entries are collected but hidden by the callstack filter */
1926 total
+= con
->nr_filtered
;
1928 print_footer(total
, bad
, &con
->fails
);
1933 static int __cmd_report(bool display_info
)
1936 struct perf_tool eops
;
1937 struct perf_data data
= {
1939 .mode
= PERF_DATA_MODE_READ
,
1943 perf_tool__init(&eops
, /*ordered_events=*/true);
1944 eops
.attr
= perf_event__process_attr
;
1945 eops
.event_update
= process_event_update
;
1946 eops
.sample
= process_sample_event
;
1947 eops
.comm
= perf_event__process_comm
;
1948 eops
.mmap
= perf_event__process_mmap
;
1949 eops
.namespaces
= perf_event__process_namespaces
;
1950 eops
.tracing_data
= perf_event__process_tracing_data
;
1951 session
= perf_session__new(&data
, &eops
);
1952 if (IS_ERR(session
)) {
1953 pr_err("Initializing perf session failed\n");
1954 return PTR_ERR(session
);
1957 symbol_conf
.allow_aliases
= true;
1958 symbol__init(&session
->header
.env
);
1960 if (!data
.is_pipe
) {
1961 if (!perf_session__has_traces(session
, "lock record"))
1964 if (perf_session__set_tracepoints_handlers(session
, lock_tracepoints
)) {
1965 pr_err("Initializing perf session tracepoint handlers failed\n");
1969 if (perf_session__set_tracepoints_handlers(session
, contention_tracepoints
)) {
1970 pr_err("Initializing perf session tracepoint handlers failed\n");
1975 if (setup_output_field(false, output_fields
))
1978 if (select_key(false))
1981 if (show_thread_stats
)
1982 aggr_mode
= LOCK_AGGR_TASK
;
1984 err
= perf_session__process_events(session
);
1989 if (display_info
) /* used for info subcommand */
1998 perf_session__delete(session
);
2002 static void sighandler(int sig __maybe_unused
)
2006 static int check_lock_contention_options(const struct option
*options
,
2007 const char * const *usage
)
2010 if (show_thread_stats
&& show_lock_addrs
) {
2011 pr_err("Cannot use thread and addr mode together\n");
2012 parse_options_usage(usage
, options
, "threads", 0);
2013 parse_options_usage(NULL
, options
, "lock-addr", 0);
2017 if (show_lock_owner
&& !use_bpf
) {
2018 pr_err("Lock owners are available only with BPF\n");
2019 parse_options_usage(usage
, options
, "lock-owner", 0);
2020 parse_options_usage(NULL
, options
, "use-bpf", 0);
2024 if (show_lock_owner
&& show_lock_addrs
) {
2025 pr_err("Cannot use owner and addr mode together\n");
2026 parse_options_usage(usage
, options
, "lock-owner", 0);
2027 parse_options_usage(NULL
, options
, "lock-addr", 0);
2031 if (show_lock_cgroups
&& !use_bpf
) {
2032 pr_err("Cgroups are available only with BPF\n");
2033 parse_options_usage(usage
, options
, "lock-cgroup", 0);
2034 parse_options_usage(NULL
, options
, "use-bpf", 0);
2038 if (show_lock_cgroups
&& show_lock_addrs
) {
2039 pr_err("Cannot use cgroup and addr mode together\n");
2040 parse_options_usage(usage
, options
, "lock-cgroup", 0);
2041 parse_options_usage(NULL
, options
, "lock-addr", 0);
2045 if (show_lock_cgroups
&& show_thread_stats
) {
2046 pr_err("Cannot use cgroup and thread mode together\n");
2047 parse_options_usage(usage
, options
, "lock-cgroup", 0);
2048 parse_options_usage(NULL
, options
, "threads", 0);
2052 if (symbol_conf
.field_sep
) {
2053 if (strstr(symbol_conf
.field_sep
, ":") || /* part of type flags */
2054 strstr(symbol_conf
.field_sep
, "+") || /* part of caller offset */
2055 strstr(symbol_conf
.field_sep
, ".")) { /* can be in a symbol name */
2056 pr_err("Cannot use the separator that is already used\n");
2057 parse_options_usage(usage
, options
, "x", 1);
2062 if (show_lock_owner
)
2063 show_thread_stats
= true;
2068 static int __cmd_contention(int argc
, const char **argv
)
2071 struct perf_tool eops
;
2072 struct perf_data data
= {
2074 .mode
= PERF_DATA_MODE_READ
,
2077 struct lock_contention con
= {
2079 .map_nr_entries
= bpf_map_entries
,
2080 .max_stack
= max_stack_depth
,
2081 .stack_skip
= stack_skip
,
2082 .filters
= &filters
,
2083 .save_callstack
= needs_callstack(),
2084 .owner
= show_lock_owner
,
2088 lockhash_table
= calloc(LOCKHASH_SIZE
, sizeof(*lockhash_table
));
2089 if (!lockhash_table
)
2092 con
.result
= &lockhash_table
[0];
2094 perf_tool__init(&eops
, /*ordered_events=*/true);
2095 eops
.attr
= perf_event__process_attr
;
2096 eops
.event_update
= process_event_update
;
2097 eops
.sample
= process_sample_event
;
2098 eops
.comm
= perf_event__process_comm
;
2099 eops
.mmap
= perf_event__process_mmap
;
2100 eops
.tracing_data
= perf_event__process_tracing_data
;
2102 session
= perf_session__new(use_bpf
? NULL
: &data
, &eops
);
2103 if (IS_ERR(session
)) {
2104 pr_err("Initializing perf session failed\n");
2105 err
= PTR_ERR(session
);
2110 con
.machine
= &session
->machines
.host
;
2112 con
.aggr_mode
= aggr_mode
= show_thread_stats
? LOCK_AGGR_TASK
:
2113 show_lock_addrs
? LOCK_AGGR_ADDR
:
2114 show_lock_cgroups
? LOCK_AGGR_CGROUP
: LOCK_AGGR_CALLER
;
2116 if (con
.aggr_mode
== LOCK_AGGR_CALLER
)
2117 con
.save_callstack
= true;
2119 symbol_conf
.allow_aliases
= true;
2120 symbol__init(&session
->header
.env
);
2123 err
= target__validate(&target
);
2127 target__strerror(&target
, err
, errbuf
, 512);
2128 pr_err("%s\n", errbuf
);
2132 signal(SIGINT
, sighandler
);
2133 signal(SIGCHLD
, sighandler
);
2134 signal(SIGTERM
, sighandler
);
2136 con
.evlist
= evlist__new();
2137 if (con
.evlist
== NULL
) {
2142 err
= evlist__create_maps(con
.evlist
, &target
);
2147 err
= evlist__prepare_workload(con
.evlist
, &target
,
2153 if (lock_contention_prepare(&con
) < 0) {
2154 pr_err("lock contention BPF setup failed\n");
2157 } else if (!data
.is_pipe
) {
2158 if (!perf_session__has_traces(session
, "lock record"))
2161 if (!evlist__find_evsel_by_str(session
->evlist
,
2162 "lock:contention_begin")) {
2163 pr_err("lock contention evsel not found\n");
2167 if (perf_session__set_tracepoints_handlers(session
,
2168 contention_tracepoints
)) {
2169 pr_err("Initializing perf session tracepoint handlers failed\n");
2174 if (setup_output_field(true, output_fields
))
2177 if (select_key(true))
2180 if (symbol_conf
.field_sep
) {
2182 struct lock_key
*keys
= contention_keys
;
2184 /* do not align output in CSV format */
2185 for (i
= 0; keys
[i
].name
; i
++)
2190 lock_contention_start();
2192 evlist__start_workload(con
.evlist
);
2194 /* wait for signal */
2197 lock_contention_stop();
2198 lock_contention_read(&con
);
2200 err
= perf_session__process_events(session
);
2207 sort_contention_result();
2208 print_contention_result(&con
);
2211 lock_filter_finish();
2212 evlist__delete(con
.evlist
);
2213 lock_contention_finish(&con
);
2214 perf_session__delete(session
);
2215 zfree(&lockhash_table
);
2220 static int __cmd_record(int argc
, const char **argv
)
2222 const char *record_args
[] = {
2223 "record", "-R", "-m", "1024", "-c", "1", "--synth", "task",
2225 const char *callgraph_args
[] = {
2226 "--call-graph", "fp," __stringify(CONTENTION_STACK_DEPTH
),
2228 unsigned int rec_argc
, i
, j
, ret
;
2229 unsigned int nr_tracepoints
;
2230 unsigned int nr_callgraph_args
= 0;
2231 const char **rec_argv
;
2232 bool has_lock_stat
= true;
2234 for (i
= 0; i
< ARRAY_SIZE(lock_tracepoints
); i
++) {
2235 if (!is_valid_tracepoint(lock_tracepoints
[i
].name
)) {
2236 pr_debug("tracepoint %s is not enabled. "
2237 "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n",
2238 lock_tracepoints
[i
].name
);
2239 has_lock_stat
= false;
2247 for (i
= 0; i
< ARRAY_SIZE(contention_tracepoints
); i
++) {
2248 if (!is_valid_tracepoint(contention_tracepoints
[i
].name
)) {
2249 pr_err("tracepoint %s is not enabled.\n",
2250 contention_tracepoints
[i
].name
);
2255 nr_callgraph_args
= ARRAY_SIZE(callgraph_args
);
2258 rec_argc
= ARRAY_SIZE(record_args
) + nr_callgraph_args
+ argc
- 1;
2261 nr_tracepoints
= ARRAY_SIZE(lock_tracepoints
);
2263 nr_tracepoints
= ARRAY_SIZE(contention_tracepoints
);
2265 /* factor of 2 is for -e in front of each tracepoint */
2266 rec_argc
+= 2 * nr_tracepoints
;
2268 rec_argv
= calloc(rec_argc
+ 1, sizeof(char *));
2272 for (i
= 0; i
< ARRAY_SIZE(record_args
); i
++)
2273 rec_argv
[i
] = record_args
[i
];
2275 for (j
= 0; j
< nr_tracepoints
; j
++) {
2276 rec_argv
[i
++] = "-e";
2277 rec_argv
[i
++] = has_lock_stat
2278 ? lock_tracepoints
[j
].name
2279 : contention_tracepoints
[j
].name
;
2282 for (j
= 0; j
< nr_callgraph_args
; j
++, i
++)
2283 rec_argv
[i
] = callgraph_args
[j
];
2285 for (j
= 1; j
< (unsigned int)argc
; j
++, i
++)
2286 rec_argv
[i
] = argv
[j
];
2288 BUG_ON(i
!= rec_argc
);
2290 ret
= cmd_record(i
, rec_argv
);
2295 static int parse_map_entry(const struct option
*opt
, const char *str
,
2296 int unset __maybe_unused
)
2298 unsigned long *len
= (unsigned long *)opt
->value
;
2303 val
= strtoul(str
, &endptr
, 0);
2304 if (*endptr
!= '\0' || errno
!= 0) {
2305 pr_err("invalid BPF map length: %s\n", str
);
2313 static int parse_max_stack(const struct option
*opt
, const char *str
,
2314 int unset __maybe_unused
)
2316 unsigned long *len
= (unsigned long *)opt
->value
;
2321 val
= strtol(str
, &endptr
, 0);
2322 if (*endptr
!= '\0' || errno
!= 0) {
2323 pr_err("invalid max stack depth: %s\n", str
);
2327 if (val
< 0 || val
> sysctl__max_stack()) {
2328 pr_err("invalid max stack depth: %ld\n", val
);
2336 static bool add_lock_type(unsigned int flags
)
2340 tmp
= realloc(filters
.types
, (filters
.nr_types
+ 1) * sizeof(*filters
.types
));
2344 tmp
[filters
.nr_types
++] = flags
;
2345 filters
.types
= tmp
;
2349 static int parse_lock_type(const struct option
*opt __maybe_unused
, const char *str
,
2350 int unset __maybe_unused
)
2352 char *s
, *tmp
, *tok
;
2359 for (tok
= strtok_r(s
, ", ", &tmp
); tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
2360 unsigned int flags
= get_type_flag(tok
);
2363 pr_err("Unknown lock flags: %s\n", tok
);
2368 if (!add_lock_type(flags
)) {
2378 static bool add_lock_addr(unsigned long addr
)
2382 tmp
= realloc(filters
.addrs
, (filters
.nr_addrs
+ 1) * sizeof(*filters
.addrs
));
2384 pr_err("Memory allocation failure\n");
2388 tmp
[filters
.nr_addrs
++] = addr
;
2389 filters
.addrs
= tmp
;
2393 static bool add_lock_sym(char *name
)
2396 char *sym
= strdup(name
);
2399 pr_err("Memory allocation failure\n");
2403 tmp
= realloc(filters
.syms
, (filters
.nr_syms
+ 1) * sizeof(*filters
.syms
));
2405 pr_err("Memory allocation failure\n");
2410 tmp
[filters
.nr_syms
++] = sym
;
2415 static int parse_lock_addr(const struct option
*opt __maybe_unused
, const char *str
,
2416 int unset __maybe_unused
)
2418 char *s
, *tmp
, *tok
;
2426 for (tok
= strtok_r(s
, ", ", &tmp
); tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
2429 addr
= strtoul(tok
, &end
, 16);
2431 if (!add_lock_addr(addr
)) {
2439 * At this moment, we don't have kernel symbols. Save the symbols
2440 * in a separate list and resolve them to addresses later.
2442 if (!add_lock_sym(tok
)) {
2452 static int parse_call_stack(const struct option
*opt __maybe_unused
, const char *str
,
2453 int unset __maybe_unused
)
2455 char *s
, *tmp
, *tok
;
2462 for (tok
= strtok_r(s
, ", ", &tmp
); tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
2463 struct callstack_filter
*entry
;
2465 entry
= malloc(sizeof(*entry
) + strlen(tok
) + 1);
2466 if (entry
== NULL
) {
2467 pr_err("Memory allocation failure\n");
2472 strcpy(entry
->name
, tok
);
2473 list_add_tail(&entry
->list
, &callstack_filters
);
2480 static int parse_output(const struct option
*opt __maybe_unused
, const char *str
,
2481 int unset __maybe_unused
)
2483 const char **name
= (const char **)opt
->value
;
2488 lock_output
= fopen(str
, "w");
2489 if (lock_output
== NULL
) {
2490 pr_err("Cannot open %s\n", str
);
2498 static bool add_lock_cgroup(char *name
)
2501 struct cgroup
*cgrp
;
2503 cgrp
= cgroup__new(name
, /*do_open=*/false);
2505 pr_err("Failed to create cgroup: %s\n", name
);
2509 if (read_cgroup_id(cgrp
) < 0) {
2510 pr_err("Failed to read cgroup id for %s\n", name
);
2515 tmp
= realloc(filters
.cgrps
, (filters
.nr_cgrps
+ 1) * sizeof(*filters
.cgrps
));
2517 pr_err("Memory allocation failure\n");
2521 tmp
[filters
.nr_cgrps
++] = cgrp
->id
;
2522 filters
.cgrps
= tmp
;
2527 static int parse_cgroup_filter(const struct option
*opt __maybe_unused
, const char *str
,
2528 int unset __maybe_unused
)
2530 char *s
, *tmp
, *tok
;
2537 for (tok
= strtok_r(s
, ", ", &tmp
); tok
; tok
= strtok_r(NULL
, ", ", &tmp
)) {
2538 if (!add_lock_cgroup(tok
)) {
2548 int cmd_lock(int argc
, const char **argv
)
2550 const struct option lock_options
[] = {
2551 OPT_STRING('i', "input", &input_name
, "file", "input file name"),
2552 OPT_CALLBACK(0, "output", &output_name
, "file", "output file name", parse_output
),
2553 OPT_INCR('v', "verbose", &verbose
, "be more verbose (show symbol address, etc)"),
2554 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace
, "dump raw trace in ASCII"),
2555 OPT_BOOLEAN('f', "force", &force
, "don't complain, do it"),
2556 OPT_STRING(0, "vmlinux", &symbol_conf
.vmlinux_name
,
2557 "file", "vmlinux pathname"),
2558 OPT_STRING(0, "kallsyms", &symbol_conf
.kallsyms_name
,
2559 "file", "kallsyms pathname"),
2560 OPT_BOOLEAN('q', "quiet", &quiet
, "Do not show any warnings or messages"),
2564 const struct option info_options
[] = {
2565 OPT_BOOLEAN('t', "threads", &info_threads
,
2566 "dump the thread list in perf.data"),
2567 OPT_BOOLEAN('m', "map", &info_map
,
2568 "dump the map of lock instances (address:name table)"),
2569 OPT_PARENT(lock_options
)
2572 const struct option report_options
[] = {
2573 OPT_STRING('k', "key", &sort_key
, "acquired",
2574 "key for sorting (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),
2575 OPT_STRING('F', "field", &output_fields
, NULL
,
2576 "output fields (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),
2578 OPT_BOOLEAN('c', "combine-locks", &combine_locks
,
2579 "combine locks in the same class"),
2580 OPT_BOOLEAN('t', "threads", &show_thread_stats
,
2581 "show per-thread lock stats"),
2582 OPT_INTEGER('E', "entries", &print_nr_entries
, "display this many functions"),
2583 OPT_PARENT(lock_options
)
2586 struct option contention_options
[] = {
2587 OPT_STRING('k', "key", &sort_key
, "wait_total",
2588 "key for sorting (contended / wait_total / wait_max / wait_min / avg_wait)"),
2589 OPT_STRING('F', "field", &output_fields
, "contended,wait_total,wait_max,avg_wait",
2590 "output fields (contended / wait_total / wait_max / wait_min / avg_wait)"),
2591 OPT_BOOLEAN('t', "threads", &show_thread_stats
,
2592 "show per-thread lock stats"),
2593 OPT_BOOLEAN('b', "use-bpf", &use_bpf
, "use BPF program to collect lock contention stats"),
2594 OPT_BOOLEAN('a', "all-cpus", &target
.system_wide
,
2595 "System-wide collection from all CPUs"),
2596 OPT_STRING('C', "cpu", &target
.cpu_list
, "cpu",
2597 "List of cpus to monitor"),
2598 OPT_STRING('p', "pid", &target
.pid
, "pid",
2599 "Trace on existing process id"),
2600 OPT_STRING(0, "tid", &target
.tid
, "tid",
2601 "Trace on existing thread id (exclusive to --pid)"),
2602 OPT_CALLBACK('M', "map-nr-entries", &bpf_map_entries
, "num",
2603 "Max number of BPF map entries", parse_map_entry
),
2604 OPT_CALLBACK(0, "max-stack", &max_stack_depth
, "num",
2605 "Set the maximum stack depth when collecting lock contention, "
2606 "Default: " __stringify(CONTENTION_STACK_DEPTH
), parse_max_stack
),
2607 OPT_INTEGER(0, "stack-skip", &stack_skip
,
2608 "Set the number of stack depth to skip when finding a lock caller, "
2609 "Default: " __stringify(CONTENTION_STACK_SKIP
)),
2610 OPT_INTEGER('E', "entries", &print_nr_entries
, "display this many functions"),
2611 OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs
, "show lock stats by address"),
2612 OPT_CALLBACK('Y', "type-filter", NULL
, "FLAGS",
2613 "Filter specific type of locks", parse_lock_type
),
2614 OPT_CALLBACK('L', "lock-filter", NULL
, "ADDRS/NAMES",
2615 "Filter specific address/symbol of locks", parse_lock_addr
),
2616 OPT_CALLBACK('S', "callstack-filter", NULL
, "NAMES",
2617 "Filter specific function in the callstack", parse_call_stack
),
2618 OPT_BOOLEAN('o', "lock-owner", &show_lock_owner
, "show lock owners instead of waiters"),
2619 OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf
.field_sep
, "separator",
2620 "print result in CSV format with custom separator"),
2621 OPT_BOOLEAN(0, "lock-cgroup", &show_lock_cgroups
, "show lock stats by cgroup"),
2622 OPT_CALLBACK('G', "cgroup-filter", NULL
, "CGROUPS",
2623 "Filter specific cgroups", parse_cgroup_filter
),
2624 OPT_PARENT(lock_options
)
2627 const char * const info_usage
[] = {
2628 "perf lock info [<options>]",
2631 const char *const lock_subcommands
[] = { "record", "report", "script",
2632 "info", "contention", NULL
};
2633 const char *lock_usage
[] = {
2637 const char * const report_usage
[] = {
2638 "perf lock report [<options>]",
2641 const char * const contention_usage
[] = {
2642 "perf lock contention [<options>]",
2648 lockhash_table
= calloc(LOCKHASH_SIZE
, sizeof(*lockhash_table
));
2649 if (!lockhash_table
)
2652 for (i
= 0; i
< LOCKHASH_SIZE
; i
++)
2653 INIT_HLIST_HEAD(lockhash_table
+ i
);
2655 lock_output
= stderr
;
2656 argc
= parse_options_subcommand(argc
, argv
, lock_options
, lock_subcommands
,
2657 lock_usage
, PARSE_OPT_STOP_AT_NON_OPTION
);
2659 usage_with_options(lock_usage
, lock_options
);
2661 if (strlen(argv
[0]) > 2 && strstarts("record", argv
[0])) {
2662 return __cmd_record(argc
, argv
);
2663 } else if (strlen(argv
[0]) > 2 && strstarts("report", argv
[0])) {
2664 trace_handler
= &report_lock_ops
;
2666 argc
= parse_options(argc
, argv
,
2667 report_options
, report_usage
, 0);
2669 usage_with_options(report_usage
, report_options
);
2671 rc
= __cmd_report(false);
2672 } else if (!strcmp(argv
[0], "script")) {
2673 /* Aliased to 'perf script' */
2674 rc
= cmd_script(argc
, argv
);
2675 } else if (!strcmp(argv
[0], "info")) {
2677 argc
= parse_options(argc
, argv
,
2678 info_options
, info_usage
, 0);
2680 usage_with_options(info_usage
, info_options
);
2683 /* If neither threads nor map requested, display both */
2684 if (!info_threads
&& !info_map
) {
2685 info_threads
= true;
2689 /* recycling report_lock_ops */
2690 trace_handler
= &report_lock_ops
;
2691 rc
= __cmd_report(true);
2692 } else if (strlen(argv
[0]) > 2 && strstarts("contention", argv
[0])) {
2693 trace_handler
= &contention_lock_ops
;
2694 sort_key
= "wait_total";
2695 output_fields
= "contended,wait_total,wait_max,avg_wait";
2697 #ifndef HAVE_BPF_SKEL
2698 set_option_nobuild(contention_options
, 'b', "use-bpf",
2699 "no BUILD_BPF_SKEL=1", false);
2702 argc
= parse_options(argc
, argv
, contention_options
,
2703 contention_usage
, 0);
2706 if (check_lock_contention_options(contention_options
,
2707 contention_usage
) < 0)
2710 rc
= __cmd_contention(argc
, argv
);
2712 usage_with_options(lock_usage
, lock_options
);
2715 /* free usage string allocated by parse_options_subcommand */
2716 free((void *)lock_usage
[0]);
2718 zfree(&lockhash_table
);