1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2a43783aeSArnaldo Carvalho de Melo #include <errno.h>
3fd20e811SArnaldo Carvalho de Melo #include <inttypes.h>
49b5e350cSHitoshi Mitake #include "builtin.h"
59b5e350cSHitoshi Mitake #include "perf.h"
69b5e350cSHitoshi Mitake
77ae811b1SArnaldo Carvalho de Melo #include "util/evlist.h" // for struct evsel_str_handler
8fcf65bf1SArnaldo Carvalho de Melo #include "util/evsel.h"
99b5e350cSHitoshi Mitake #include "util/symbol.h"
109b5e350cSHitoshi Mitake #include "util/thread.h"
119b5e350cSHitoshi Mitake #include "util/header.h"
126fda2405SNamhyung Kim #include "util/target.h"
130d2997f7SNamhyung Kim #include "util/callchain.h"
14407b36f6SNamhyung Kim #include "util/lock-contention.h"
15eca949b2SNamhyung Kim #include "util/bpf_skel/lock_data.h"
169b5e350cSHitoshi Mitake
17fa0d9846SArnaldo Carvalho de Melo #include <subcmd/pager.h>
184b6ab94eSJosh Poimboeuf #include <subcmd/parse-options.h>
199b5e350cSHitoshi Mitake #include "util/trace-event.h"
209b7c7728SIan Rogers #include "util/tracepoint.h"
219b5e350cSHitoshi Mitake
229b5e350cSHitoshi Mitake #include "util/debug.h"
239b5e350cSHitoshi Mitake #include "util/session.h"
2445694aa7SArnaldo Carvalho de Melo #include "util/tool.h"
25f5fc1412SJiri Olsa #include "util/data.h"
26ae0f4eb3SWei Li #include "util/string2.h"
270d2997f7SNamhyung Kim #include "util/map.h"
280a277b62SNamhyung Kim #include "util/util.h"
299b5e350cSHitoshi Mitake
30f6027053SNamhyung Kim #include <stdio.h>
319b5e350cSHitoshi Mitake #include <sys/types.h>
329b5e350cSHitoshi Mitake #include <sys/prctl.h>
339b5e350cSHitoshi Mitake #include <semaphore.h>
349b5e350cSHitoshi Mitake #include <math.h>
359b5e350cSHitoshi Mitake #include <limits.h>
36511e19b9SNamhyung Kim #include <ctype.h>
379b5e350cSHitoshi Mitake
389b5e350cSHitoshi Mitake #include <linux/list.h>
399b5e350cSHitoshi Mitake #include <linux/hash.h>
40877a7a11SArnaldo Carvalho de Melo #include <linux/kernel.h>
417f7c536fSArnaldo Carvalho de Melo #include <linux/zalloc.h>
426ef81c55SMamatha Inamdar #include <linux/err.h>
430d2997f7SNamhyung Kim #include <linux/stringify.h>
449b5e350cSHitoshi Mitake
45e4cef1f6SHitoshi Mitake static struct perf_session *session;
466fda2405SNamhyung Kim static struct target target;
47e4cef1f6SHitoshi Mitake
489b5e350cSHitoshi Mitake /* based on kernel/lockdep.c */
499b5e350cSHitoshi Mitake #define LOCKHASH_BITS 12
509b5e350cSHitoshi Mitake #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS)
519b5e350cSHitoshi Mitake
52eef4fee5SIan Rogers static struct hlist_head *lockhash_table;
539b5e350cSHitoshi Mitake
549b5e350cSHitoshi Mitake #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS)
559b5e350cSHitoshi Mitake #define lockhashentry(key) (lockhash_table + __lockhashfn((key)))
569b5e350cSHitoshi Mitake
57e4cef1f6SHitoshi Mitake static struct rb_root thread_stats;
58e4cef1f6SHitoshi Mitake
590d435bf8SNamhyung Kim static bool combine_locks;
607c3bcbdfSNamhyung Kim static bool show_thread_stats;
61688d2e8dSNamhyung Kim static bool show_lock_addrs;
623477f079SNamhyung Kim static bool show_lock_owner;
63407b36f6SNamhyung Kim static bool use_bpf;
642d8d0165SNamhyung Kim static unsigned long bpf_map_entries = MAX_ENTRIES;
6596532a83SNamhyung Kim static int max_stack_depth = CONTENTION_STACK_DEPTH;
6696532a83SNamhyung Kim static int stack_skip = CONTENTION_STACK_SKIP;
676282a1f4SNamhyung Kim static int print_nr_entries = INT_MAX / 2;
687b204399SNamhyung Kim static LIST_HEAD(callstack_filters);
69f6027053SNamhyung Kim static const char *output_name = NULL;
70f6027053SNamhyung Kim static FILE *lock_output;
717b204399SNamhyung Kim
727b204399SNamhyung Kim struct callstack_filter {
737b204399SNamhyung Kim struct list_head list;
747b204399SNamhyung Kim char name[];
757b204399SNamhyung Kim };
760d435bf8SNamhyung Kim
77b4a7eff9SNamhyung Kim static struct lock_filter filters;
78b4a7eff9SNamhyung Kim
79eca949b2SNamhyung Kim static enum lock_aggr_mode aggr_mode = LOCK_AGGR_ADDR;
80f9c695a2SNamhyung Kim
needs_callstack(void)817b204399SNamhyung Kim static bool needs_callstack(void)
827b204399SNamhyung Kim {
830fba2265SNamhyung Kim return !list_empty(&callstack_filters);
847b204399SNamhyung Kim }
857b204399SNamhyung Kim
thread_stat_find(u32 tid)86e4cef1f6SHitoshi Mitake static struct thread_stat *thread_stat_find(u32 tid)
87e4cef1f6SHitoshi Mitake {
88e4cef1f6SHitoshi Mitake struct rb_node *node;
89e4cef1f6SHitoshi Mitake struct thread_stat *st;
90e4cef1f6SHitoshi Mitake
91e4cef1f6SHitoshi Mitake node = thread_stats.rb_node;
92e4cef1f6SHitoshi Mitake while (node) {
93e4cef1f6SHitoshi Mitake st = container_of(node, struct thread_stat, rb);
94e4cef1f6SHitoshi Mitake if (st->tid == tid)
95e4cef1f6SHitoshi Mitake return st;
96e4cef1f6SHitoshi Mitake else if (tid < st->tid)
97e4cef1f6SHitoshi Mitake node = node->rb_left;
98e4cef1f6SHitoshi Mitake else
99e4cef1f6SHitoshi Mitake node = node->rb_right;
100e4cef1f6SHitoshi Mitake }
101e4cef1f6SHitoshi Mitake
102e4cef1f6SHitoshi Mitake return NULL;
103e4cef1f6SHitoshi Mitake }
104e4cef1f6SHitoshi Mitake
thread_stat_insert(struct thread_stat * new)105e4cef1f6SHitoshi Mitake static void thread_stat_insert(struct thread_stat *new)
106e4cef1f6SHitoshi Mitake {
107e4cef1f6SHitoshi Mitake struct rb_node **rb = &thread_stats.rb_node;
108e4cef1f6SHitoshi Mitake struct rb_node *parent = NULL;
109e4cef1f6SHitoshi Mitake struct thread_stat *p;
110e4cef1f6SHitoshi Mitake
111e4cef1f6SHitoshi Mitake while (*rb) {
112e4cef1f6SHitoshi Mitake p = container_of(*rb, struct thread_stat, rb);
113e4cef1f6SHitoshi Mitake parent = *rb;
114e4cef1f6SHitoshi Mitake
115e4cef1f6SHitoshi Mitake if (new->tid < p->tid)
116e4cef1f6SHitoshi Mitake rb = &(*rb)->rb_left;
117e4cef1f6SHitoshi Mitake else if (new->tid > p->tid)
118e4cef1f6SHitoshi Mitake rb = &(*rb)->rb_right;
119e4cef1f6SHitoshi Mitake else
120e4cef1f6SHitoshi Mitake BUG_ON("inserting invalid thread_stat\n");
121e4cef1f6SHitoshi Mitake }
122e4cef1f6SHitoshi Mitake
123e4cef1f6SHitoshi Mitake rb_link_node(&new->rb, parent, rb);
124e4cef1f6SHitoshi Mitake rb_insert_color(&new->rb, &thread_stats);
125e4cef1f6SHitoshi Mitake }
126e4cef1f6SHitoshi Mitake
thread_stat_findnew_after_first(u32 tid)127e4cef1f6SHitoshi Mitake static struct thread_stat *thread_stat_findnew_after_first(u32 tid)
128e4cef1f6SHitoshi Mitake {
129e4cef1f6SHitoshi Mitake struct thread_stat *st;
130e4cef1f6SHitoshi Mitake
131e4cef1f6SHitoshi Mitake st = thread_stat_find(tid);
132e4cef1f6SHitoshi Mitake if (st)
133e4cef1f6SHitoshi Mitake return st;
134e4cef1f6SHitoshi Mitake
135e4cef1f6SHitoshi Mitake st = zalloc(sizeof(struct thread_stat));
13633d6aef5SDavid Ahern if (!st) {
13733d6aef5SDavid Ahern pr_err("memory allocation failed\n");
13833d6aef5SDavid Ahern return NULL;
13933d6aef5SDavid Ahern }
140e4cef1f6SHitoshi Mitake
141e4cef1f6SHitoshi Mitake st->tid = tid;
142e4cef1f6SHitoshi Mitake INIT_LIST_HEAD(&st->seq_list);
143e4cef1f6SHitoshi Mitake
144e4cef1f6SHitoshi Mitake thread_stat_insert(st);
145e4cef1f6SHitoshi Mitake
146e4cef1f6SHitoshi Mitake return st;
147e4cef1f6SHitoshi Mitake }
148e4cef1f6SHitoshi Mitake
149e4cef1f6SHitoshi Mitake static struct thread_stat *thread_stat_findnew_first(u32 tid);
150e4cef1f6SHitoshi Mitake static struct thread_stat *(*thread_stat_findnew)(u32 tid) =
151e4cef1f6SHitoshi Mitake thread_stat_findnew_first;
152e4cef1f6SHitoshi Mitake
thread_stat_findnew_first(u32 tid)153e4cef1f6SHitoshi Mitake static struct thread_stat *thread_stat_findnew_first(u32 tid)
154e4cef1f6SHitoshi Mitake {
155e4cef1f6SHitoshi Mitake struct thread_stat *st;
156e4cef1f6SHitoshi Mitake
157e4cef1f6SHitoshi Mitake st = zalloc(sizeof(struct thread_stat));
15833d6aef5SDavid Ahern if (!st) {
15933d6aef5SDavid Ahern pr_err("memory allocation failed\n");
16033d6aef5SDavid Ahern return NULL;
16133d6aef5SDavid Ahern }
162e4cef1f6SHitoshi Mitake st->tid = tid;
163e4cef1f6SHitoshi Mitake INIT_LIST_HEAD(&st->seq_list);
164e4cef1f6SHitoshi Mitake
165e4cef1f6SHitoshi Mitake rb_link_node(&st->rb, NULL, &thread_stats.rb_node);
166e4cef1f6SHitoshi Mitake rb_insert_color(&st->rb, &thread_stats);
167e4cef1f6SHitoshi Mitake
168e4cef1f6SHitoshi Mitake thread_stat_findnew = thread_stat_findnew_after_first;
169e4cef1f6SHitoshi Mitake return st;
170e4cef1f6SHitoshi Mitake }
171e4cef1f6SHitoshi Mitake
1729b5e350cSHitoshi Mitake /* build simple key function one is bigger than two */
1739b5e350cSHitoshi Mitake #define SINGLE_KEY(member) \
1749b5e350cSHitoshi Mitake static int lock_stat_key_ ## member(struct lock_stat *one, \
1759b5e350cSHitoshi Mitake struct lock_stat *two) \
1769b5e350cSHitoshi Mitake { \
1779b5e350cSHitoshi Mitake return one->member > two->member; \
1789b5e350cSHitoshi Mitake }
1799b5e350cSHitoshi Mitake
1809b5e350cSHitoshi Mitake SINGLE_KEY(nr_acquired)
SINGLE_KEY(nr_contended)1819b5e350cSHitoshi Mitake SINGLE_KEY(nr_contended)
182f37376cdSDavidlohr Bueso SINGLE_KEY(avg_wait_time)
1839b5e350cSHitoshi Mitake SINGLE_KEY(wait_time_total)
1849b5e350cSHitoshi Mitake SINGLE_KEY(wait_time_max)
1859b5e350cSHitoshi Mitake
1869df03abeSMarcin Slusarz static int lock_stat_key_wait_time_min(struct lock_stat *one,
1879df03abeSMarcin Slusarz struct lock_stat *two)
1889df03abeSMarcin Slusarz {
1899df03abeSMarcin Slusarz u64 s1 = one->wait_time_min;
1909df03abeSMarcin Slusarz u64 s2 = two->wait_time_min;
1919df03abeSMarcin Slusarz if (s1 == ULLONG_MAX)
1929df03abeSMarcin Slusarz s1 = 0;
1939df03abeSMarcin Slusarz if (s2 == ULLONG_MAX)
1949df03abeSMarcin Slusarz s2 = 0;
1959df03abeSMarcin Slusarz return s1 > s2;
1969df03abeSMarcin Slusarz }
1979df03abeSMarcin Slusarz
1989b5e350cSHitoshi Mitake struct lock_key {
1999b5e350cSHitoshi Mitake /*
2009b5e350cSHitoshi Mitake * name: the value for specify by user
2019b5e350cSHitoshi Mitake * this should be simpler than raw name of member
2029b5e350cSHitoshi Mitake * e.g. nr_acquired -> acquired, wait_time_total -> wait_total
2039b5e350cSHitoshi Mitake */
2049b5e350cSHitoshi Mitake const char *name;
20564999e44SNamhyung Kim /* header: the string printed on the header line */
20664999e44SNamhyung Kim const char *header;
20764999e44SNamhyung Kim /* len: the printing width of the field */
20864999e44SNamhyung Kim int len;
20964999e44SNamhyung Kim /* key: a pointer to function to compare two lock stats for sorting */
2109b5e350cSHitoshi Mitake int (*key)(struct lock_stat*, struct lock_stat*);
21164999e44SNamhyung Kim /* print: a pointer to function to print a given lock stats */
21264999e44SNamhyung Kim void (*print)(struct lock_key*, struct lock_stat*);
21364999e44SNamhyung Kim /* list: list entry to link this */
21464999e44SNamhyung Kim struct list_head list;
2159b5e350cSHitoshi Mitake };
2169b5e350cSHitoshi Mitake
lock_stat_key_print_time(unsigned long long nsec,int len)217ab010176SNamhyung Kim static void lock_stat_key_print_time(unsigned long long nsec, int len)
218ab010176SNamhyung Kim {
219ab010176SNamhyung Kim static const struct {
220ab010176SNamhyung Kim float base;
221ab010176SNamhyung Kim const char *unit;
222ab010176SNamhyung Kim } table[] = {
223ab010176SNamhyung Kim { 1e9 * 3600, "h " },
224ab010176SNamhyung Kim { 1e9 * 60, "m " },
225ab010176SNamhyung Kim { 1e9, "s " },
226ab010176SNamhyung Kim { 1e6, "ms" },
227ab010176SNamhyung Kim { 1e3, "us" },
228ab010176SNamhyung Kim { 0, NULL },
229ab010176SNamhyung Kim };
230ab010176SNamhyung Kim
23169c5c993SNamhyung Kim /* for CSV output */
23269c5c993SNamhyung Kim if (len == 0) {
233f6027053SNamhyung Kim fprintf(lock_output, "%llu", nsec);
23469c5c993SNamhyung Kim return;
23569c5c993SNamhyung Kim }
23669c5c993SNamhyung Kim
237ab010176SNamhyung Kim for (int i = 0; table[i].unit; i++) {
238ab010176SNamhyung Kim if (nsec < table[i].base)
239ab010176SNamhyung Kim continue;
240ab010176SNamhyung Kim
241f6027053SNamhyung Kim fprintf(lock_output, "%*.2f %s", len - 3, nsec / table[i].base, table[i].unit);
242ab010176SNamhyung Kim return;
243ab010176SNamhyung Kim }
244ab010176SNamhyung Kim
245f6027053SNamhyung Kim fprintf(lock_output, "%*llu %s", len - 3, nsec, "ns");
246ab010176SNamhyung Kim }
247ab010176SNamhyung Kim
24864999e44SNamhyung Kim #define PRINT_KEY(member) \
24964999e44SNamhyung Kim static void lock_stat_key_print_ ## member(struct lock_key *key, \
25064999e44SNamhyung Kim struct lock_stat *ls) \
25164999e44SNamhyung Kim { \
252f6027053SNamhyung Kim fprintf(lock_output, "%*llu", key->len, (unsigned long long)ls->member);\
25364999e44SNamhyung Kim }
25464999e44SNamhyung Kim
255ab010176SNamhyung Kim #define PRINT_TIME(member) \
256ab010176SNamhyung Kim static void lock_stat_key_print_ ## member(struct lock_key *key, \
257ab010176SNamhyung Kim struct lock_stat *ls) \
258ab010176SNamhyung Kim { \
259ab010176SNamhyung Kim lock_stat_key_print_time((unsigned long long)ls->member, key->len); \
260ab010176SNamhyung Kim }
261ab010176SNamhyung Kim
26264999e44SNamhyung Kim PRINT_KEY(nr_acquired)
PRINT_KEY(nr_contended)26364999e44SNamhyung Kim PRINT_KEY(nr_contended)
264ab010176SNamhyung Kim PRINT_TIME(avg_wait_time)
265ab010176SNamhyung Kim PRINT_TIME(wait_time_total)
266ab010176SNamhyung Kim PRINT_TIME(wait_time_max)
26764999e44SNamhyung Kim
26864999e44SNamhyung Kim static void lock_stat_key_print_wait_time_min(struct lock_key *key,
26964999e44SNamhyung Kim struct lock_stat *ls)
27064999e44SNamhyung Kim {
27164999e44SNamhyung Kim u64 wait_time = ls->wait_time_min;
27264999e44SNamhyung Kim
27364999e44SNamhyung Kim if (wait_time == ULLONG_MAX)
27464999e44SNamhyung Kim wait_time = 0;
27564999e44SNamhyung Kim
276ab010176SNamhyung Kim lock_stat_key_print_time(wait_time, key->len);
27764999e44SNamhyung Kim }
27864999e44SNamhyung Kim
27964999e44SNamhyung Kim
2809b5e350cSHitoshi Mitake static const char *sort_key = "acquired";
28159f411b6SIngo Molnar
2829b5e350cSHitoshi Mitake static int (*compare)(struct lock_stat *, struct lock_stat *);
2839b5e350cSHitoshi Mitake
2840d435bf8SNamhyung Kim static struct rb_root sorted; /* place to store intermediate data */
28559f411b6SIngo Molnar static struct rb_root result; /* place to store sorted data */
28659f411b6SIngo Molnar
28764999e44SNamhyung Kim static LIST_HEAD(lock_keys);
2884bd9cab5SNamhyung Kim static const char *output_fields;
28964999e44SNamhyung Kim
29064999e44SNamhyung Kim #define DEF_KEY_LOCK(name, header, fn_suffix, len) \
29164999e44SNamhyung Kim { #name, header, len, lock_stat_key_ ## fn_suffix, lock_stat_key_print_ ## fn_suffix, {} }
29279079f21SNamhyung Kim static struct lock_key report_keys[] = {
29364999e44SNamhyung Kim DEF_KEY_LOCK(acquired, "acquired", nr_acquired, 10),
29464999e44SNamhyung Kim DEF_KEY_LOCK(contended, "contended", nr_contended, 10),
295ab010176SNamhyung Kim DEF_KEY_LOCK(avg_wait, "avg wait", avg_wait_time, 12),
296ab010176SNamhyung Kim DEF_KEY_LOCK(wait_total, "total wait", wait_time_total, 12),
297ab010176SNamhyung Kim DEF_KEY_LOCK(wait_max, "max wait", wait_time_max, 12),
298ab010176SNamhyung Kim DEF_KEY_LOCK(wait_min, "min wait", wait_time_min, 12),
2999b5e350cSHitoshi Mitake
3009b5e350cSHitoshi Mitake /* extra comparisons much complicated should be here */
30164999e44SNamhyung Kim { }
3029b5e350cSHitoshi Mitake };
3039b5e350cSHitoshi Mitake
30479079f21SNamhyung Kim static struct lock_key contention_keys[] = {
30579079f21SNamhyung Kim DEF_KEY_LOCK(contended, "contended", nr_contended, 10),
30679079f21SNamhyung Kim DEF_KEY_LOCK(wait_total, "total wait", wait_time_total, 12),
30779079f21SNamhyung Kim DEF_KEY_LOCK(wait_max, "max wait", wait_time_max, 12),
30879079f21SNamhyung Kim DEF_KEY_LOCK(wait_min, "min wait", wait_time_min, 12),
30979079f21SNamhyung Kim DEF_KEY_LOCK(avg_wait, "avg wait", avg_wait_time, 12),
31079079f21SNamhyung Kim
31179079f21SNamhyung Kim /* extra comparisons much complicated should be here */
31279079f21SNamhyung Kim { }
31379079f21SNamhyung Kim };
31479079f21SNamhyung Kim
select_key(bool contention)31579079f21SNamhyung Kim static int select_key(bool contention)
3169b5e350cSHitoshi Mitake {
3179b5e350cSHitoshi Mitake int i;
31879079f21SNamhyung Kim struct lock_key *keys = report_keys;
31979079f21SNamhyung Kim
32079079f21SNamhyung Kim if (contention)
32179079f21SNamhyung Kim keys = contention_keys;
3229b5e350cSHitoshi Mitake
3239b5e350cSHitoshi Mitake for (i = 0; keys[i].name; i++) {
3249b5e350cSHitoshi Mitake if (!strcmp(keys[i].name, sort_key)) {
3259b5e350cSHitoshi Mitake compare = keys[i].key;
3264bd9cab5SNamhyung Kim
3274bd9cab5SNamhyung Kim /* selected key should be in the output fields */
3284bd9cab5SNamhyung Kim if (list_empty(&keys[i].list))
3294bd9cab5SNamhyung Kim list_add_tail(&keys[i].list, &lock_keys);
3304bd9cab5SNamhyung Kim
33133d6aef5SDavid Ahern return 0;
3329b5e350cSHitoshi Mitake }
3339b5e350cSHitoshi Mitake }
3349b5e350cSHitoshi Mitake
33533d6aef5SDavid Ahern pr_err("Unknown compare key: %s\n", sort_key);
33633d6aef5SDavid Ahern return -1;
3379b5e350cSHitoshi Mitake }
3389b5e350cSHitoshi Mitake
add_output_field(bool contention,char * name)33979079f21SNamhyung Kim static int add_output_field(bool contention, char *name)
34064999e44SNamhyung Kim {
34164999e44SNamhyung Kim int i;
34279079f21SNamhyung Kim struct lock_key *keys = report_keys;
34379079f21SNamhyung Kim
34479079f21SNamhyung Kim if (contention)
34579079f21SNamhyung Kim keys = contention_keys;
34664999e44SNamhyung Kim
3474bd9cab5SNamhyung Kim for (i = 0; keys[i].name; i++) {
3484bd9cab5SNamhyung Kim if (strcmp(keys[i].name, name))
3494bd9cab5SNamhyung Kim continue;
3504bd9cab5SNamhyung Kim
3514bd9cab5SNamhyung Kim /* prevent double link */
3524bd9cab5SNamhyung Kim if (list_empty(&keys[i].list))
35379079f21SNamhyung Kim list_add_tail(&keys[i].list, &lock_keys);
35464999e44SNamhyung Kim
35564999e44SNamhyung Kim return 0;
35664999e44SNamhyung Kim }
35764999e44SNamhyung Kim
3584bd9cab5SNamhyung Kim pr_err("Unknown output field: %s\n", name);
3594bd9cab5SNamhyung Kim return -1;
3604bd9cab5SNamhyung Kim }
3614bd9cab5SNamhyung Kim
setup_output_field(bool contention,const char * str)36279079f21SNamhyung Kim static int setup_output_field(bool contention, const char *str)
3634bd9cab5SNamhyung Kim {
3644bd9cab5SNamhyung Kim char *tok, *tmp, *orig;
3654bd9cab5SNamhyung Kim int i, ret = 0;
36679079f21SNamhyung Kim struct lock_key *keys = report_keys;
36779079f21SNamhyung Kim
36879079f21SNamhyung Kim if (contention)
36979079f21SNamhyung Kim keys = contention_keys;
3704bd9cab5SNamhyung Kim
3714bd9cab5SNamhyung Kim /* no output field given: use all of them */
3724bd9cab5SNamhyung Kim if (str == NULL) {
3734bd9cab5SNamhyung Kim for (i = 0; keys[i].name; i++)
3744bd9cab5SNamhyung Kim list_add_tail(&keys[i].list, &lock_keys);
3754bd9cab5SNamhyung Kim return 0;
3764bd9cab5SNamhyung Kim }
3774bd9cab5SNamhyung Kim
3784bd9cab5SNamhyung Kim for (i = 0; keys[i].name; i++)
3794bd9cab5SNamhyung Kim INIT_LIST_HEAD(&keys[i].list);
3804bd9cab5SNamhyung Kim
3814bd9cab5SNamhyung Kim orig = tmp = strdup(str);
3824bd9cab5SNamhyung Kim if (orig == NULL)
3834bd9cab5SNamhyung Kim return -ENOMEM;
3844bd9cab5SNamhyung Kim
3854bd9cab5SNamhyung Kim while ((tok = strsep(&tmp, ",")) != NULL){
38679079f21SNamhyung Kim ret = add_output_field(contention, tok);
3874bd9cab5SNamhyung Kim if (ret < 0)
3884bd9cab5SNamhyung Kim break;
3894bd9cab5SNamhyung Kim }
3904bd9cab5SNamhyung Kim free(orig);
3914bd9cab5SNamhyung Kim
3924bd9cab5SNamhyung Kim return ret;
3934bd9cab5SNamhyung Kim }
3944bd9cab5SNamhyung Kim
combine_lock_stats(struct lock_stat * st)3950d435bf8SNamhyung Kim static void combine_lock_stats(struct lock_stat *st)
3960d435bf8SNamhyung Kim {
3970d435bf8SNamhyung Kim struct rb_node **rb = &sorted.rb_node;
3980d435bf8SNamhyung Kim struct rb_node *parent = NULL;
3990d435bf8SNamhyung Kim struct lock_stat *p;
4000d435bf8SNamhyung Kim int ret;
4010d435bf8SNamhyung Kim
4020d435bf8SNamhyung Kim while (*rb) {
4030d435bf8SNamhyung Kim p = container_of(*rb, struct lock_stat, rb);
4040d435bf8SNamhyung Kim parent = *rb;
4050d435bf8SNamhyung Kim
4060d435bf8SNamhyung Kim if (st->name && p->name)
4070d435bf8SNamhyung Kim ret = strcmp(st->name, p->name);
4080d435bf8SNamhyung Kim else
4090d435bf8SNamhyung Kim ret = !!st->name - !!p->name;
4100d435bf8SNamhyung Kim
4110d435bf8SNamhyung Kim if (ret == 0) {
4120d435bf8SNamhyung Kim p->nr_acquired += st->nr_acquired;
4130d435bf8SNamhyung Kim p->nr_contended += st->nr_contended;
4140d435bf8SNamhyung Kim p->wait_time_total += st->wait_time_total;
4150d435bf8SNamhyung Kim
4160d435bf8SNamhyung Kim if (p->nr_contended)
4170d435bf8SNamhyung Kim p->avg_wait_time = p->wait_time_total / p->nr_contended;
4180d435bf8SNamhyung Kim
4190d435bf8SNamhyung Kim if (p->wait_time_min > st->wait_time_min)
4200d435bf8SNamhyung Kim p->wait_time_min = st->wait_time_min;
4210d435bf8SNamhyung Kim if (p->wait_time_max < st->wait_time_max)
4220d435bf8SNamhyung Kim p->wait_time_max = st->wait_time_max;
4230d435bf8SNamhyung Kim
42479d9333bSNamhyung Kim p->broken |= st->broken;
4250d435bf8SNamhyung Kim st->combined = 1;
4260d435bf8SNamhyung Kim return;
4270d435bf8SNamhyung Kim }
4280d435bf8SNamhyung Kim
4290d435bf8SNamhyung Kim if (ret < 0)
4300d435bf8SNamhyung Kim rb = &(*rb)->rb_left;
4310d435bf8SNamhyung Kim else
4320d435bf8SNamhyung Kim rb = &(*rb)->rb_right;
4330d435bf8SNamhyung Kim }
4340d435bf8SNamhyung Kim
4350d435bf8SNamhyung Kim rb_link_node(&st->rb, parent, rb);
4360d435bf8SNamhyung Kim rb_insert_color(&st->rb, &sorted);
4370d435bf8SNamhyung Kim }
4380d435bf8SNamhyung Kim
insert_to_result(struct lock_stat * st,int (* bigger)(struct lock_stat *,struct lock_stat *))4399b5e350cSHitoshi Mitake static void insert_to_result(struct lock_stat *st,
44059f411b6SIngo Molnar int (*bigger)(struct lock_stat *, struct lock_stat *))
4419b5e350cSHitoshi Mitake {
4429b5e350cSHitoshi Mitake struct rb_node **rb = &result.rb_node;
4439b5e350cSHitoshi Mitake struct rb_node *parent = NULL;
4449b5e350cSHitoshi Mitake struct lock_stat *p;
4459b5e350cSHitoshi Mitake
4460d435bf8SNamhyung Kim if (combine_locks && st->combined)
4470d435bf8SNamhyung Kim return;
4480d435bf8SNamhyung Kim
4499b5e350cSHitoshi Mitake while (*rb) {
4509b5e350cSHitoshi Mitake p = container_of(*rb, struct lock_stat, rb);
4519b5e350cSHitoshi Mitake parent = *rb;
4529b5e350cSHitoshi Mitake
4539b5e350cSHitoshi Mitake if (bigger(st, p))
4549b5e350cSHitoshi Mitake rb = &(*rb)->rb_left;
4559b5e350cSHitoshi Mitake else
4569b5e350cSHitoshi Mitake rb = &(*rb)->rb_right;
4579b5e350cSHitoshi Mitake }
4589b5e350cSHitoshi Mitake
4599b5e350cSHitoshi Mitake rb_link_node(&st->rb, parent, rb);
4609b5e350cSHitoshi Mitake rb_insert_color(&st->rb, &result);
4619b5e350cSHitoshi Mitake }
4629b5e350cSHitoshi Mitake
4639b5e350cSHitoshi Mitake /* returns left most element of result, and erase it */
pop_from_result(void)4649b5e350cSHitoshi Mitake static struct lock_stat *pop_from_result(void)
4659b5e350cSHitoshi Mitake {
4669b5e350cSHitoshi Mitake struct rb_node *node = result.rb_node;
4679b5e350cSHitoshi Mitake
4689b5e350cSHitoshi Mitake if (!node)
4699b5e350cSHitoshi Mitake return NULL;
4709b5e350cSHitoshi Mitake
4719b5e350cSHitoshi Mitake while (node->rb_left)
4729b5e350cSHitoshi Mitake node = node->rb_left;
4739b5e350cSHitoshi Mitake
4749b5e350cSHitoshi Mitake rb_erase(node, &result);
4759b5e350cSHitoshi Mitake return container_of(node, struct lock_stat, rb);
4769b5e350cSHitoshi Mitake }
4779b5e350cSHitoshi Mitake
lock_stat_find(u64 addr)47816cad1d3SNamhyung Kim struct lock_stat *lock_stat_find(u64 addr)
4793ae03f26SNamhyung Kim {
4803ae03f26SNamhyung Kim struct hlist_head *entry = lockhashentry(addr);
4813ae03f26SNamhyung Kim struct lock_stat *ret;
4823ae03f26SNamhyung Kim
4833ae03f26SNamhyung Kim hlist_for_each_entry(ret, entry, hash_entry) {
4843ae03f26SNamhyung Kim if (ret->addr == addr)
4853ae03f26SNamhyung Kim return ret;
4863ae03f26SNamhyung Kim }
4873ae03f26SNamhyung Kim return NULL;
4883ae03f26SNamhyung Kim }
4893ae03f26SNamhyung Kim
lock_stat_findnew(u64 addr,const char * name,int flags)49016cad1d3SNamhyung Kim struct lock_stat *lock_stat_findnew(u64 addr, const char *name, int flags)
4919b5e350cSHitoshi Mitake {
4927672d00aSNamhyung Kim struct hlist_head *entry = lockhashentry(addr);
4939b5e350cSHitoshi Mitake struct lock_stat *ret, *new;
4949b5e350cSHitoshi Mitake
4957672d00aSNamhyung Kim hlist_for_each_entry(ret, entry, hash_entry) {
4969b5e350cSHitoshi Mitake if (ret->addr == addr)
4979b5e350cSHitoshi Mitake return ret;
4989b5e350cSHitoshi Mitake }
4999b5e350cSHitoshi Mitake
5009b5e350cSHitoshi Mitake new = zalloc(sizeof(struct lock_stat));
5019b5e350cSHitoshi Mitake if (!new)
5029b5e350cSHitoshi Mitake goto alloc_failed;
5039b5e350cSHitoshi Mitake
5049b5e350cSHitoshi Mitake new->addr = addr;
505fb87158bSNamhyung Kim new->name = strdup(name);
5060a98c7feSDavidlohr Bueso if (!new->name) {
5070a98c7feSDavidlohr Bueso free(new);
5089b5e350cSHitoshi Mitake goto alloc_failed;
5090a98c7feSDavidlohr Bueso }
5109b5e350cSHitoshi Mitake
511fb87158bSNamhyung Kim new->flags = flags;
5129b5e350cSHitoshi Mitake new->wait_time_min = ULLONG_MAX;
5139b5e350cSHitoshi Mitake
5147672d00aSNamhyung Kim hlist_add_head(&new->hash_entry, entry);
5159b5e350cSHitoshi Mitake return new;
5169b5e350cSHitoshi Mitake
5179b5e350cSHitoshi Mitake alloc_failed:
51833d6aef5SDavid Ahern pr_err("memory allocation failed\n");
51933d6aef5SDavid Ahern return NULL;
5209b5e350cSHitoshi Mitake }
5219b5e350cSHitoshi Mitake
match_callstack_filter(struct machine * machine,u64 * callstack)522ebab2916SNamhyung Kim bool match_callstack_filter(struct machine *machine, u64 *callstack)
523ebab2916SNamhyung Kim {
524ebab2916SNamhyung Kim struct map *kmap;
525ebab2916SNamhyung Kim struct symbol *sym;
526ebab2916SNamhyung Kim u64 ip;
527*84f254d9SKajol Jain const char *arch = perf_env__arch(machine->env);
528ebab2916SNamhyung Kim
529ebab2916SNamhyung Kim if (list_empty(&callstack_filters))
530ebab2916SNamhyung Kim return true;
531ebab2916SNamhyung Kim
532ebab2916SNamhyung Kim for (int i = 0; i < max_stack_depth; i++) {
533ebab2916SNamhyung Kim struct callstack_filter *filter;
534ebab2916SNamhyung Kim
535*84f254d9SKajol Jain /*
536*84f254d9SKajol Jain * In powerpc, the callchain saved by kernel always includes
537*84f254d9SKajol Jain * first three entries as the NIP (next instruction pointer),
538*84f254d9SKajol Jain * LR (link register), and the contents of LR save area in the
539*84f254d9SKajol Jain * second stack frame. In certain scenarios its possible to have
540*84f254d9SKajol Jain * invalid kernel instruction addresses in either LR or the second
541*84f254d9SKajol Jain * stack frame's LR. In that case, kernel will store that address as
542*84f254d9SKajol Jain * zero.
543*84f254d9SKajol Jain *
544*84f254d9SKajol Jain * The below check will continue to look into callstack,
545*84f254d9SKajol Jain * incase first or second callstack index entry has 0
546*84f254d9SKajol Jain * address for powerpc.
547*84f254d9SKajol Jain */
548*84f254d9SKajol Jain if (!callstack || (!callstack[i] && (strcmp(arch, "powerpc") ||
549*84f254d9SKajol Jain (i != 1 && i != 2))))
550ebab2916SNamhyung Kim break;
551ebab2916SNamhyung Kim
552ebab2916SNamhyung Kim ip = callstack[i];
553ebab2916SNamhyung Kim sym = machine__find_kernel_symbol(machine, ip, &kmap);
554ebab2916SNamhyung Kim if (sym == NULL)
555ebab2916SNamhyung Kim continue;
556ebab2916SNamhyung Kim
557ebab2916SNamhyung Kim list_for_each_entry(filter, &callstack_filters, list) {
558ebab2916SNamhyung Kim if (strstr(sym->name, filter->name))
559ebab2916SNamhyung Kim return true;
560ebab2916SNamhyung Kim }
561ebab2916SNamhyung Kim }
562ebab2916SNamhyung Kim return false;
563ebab2916SNamhyung Kim }
564ebab2916SNamhyung Kim
5659b5e350cSHitoshi Mitake struct trace_lock_handler {
566166a9764SNamhyung Kim /* it's used on CONFIG_LOCKDEP */
56732dcd021SJiri Olsa int (*acquire_event)(struct evsel *evsel,
568746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample);
5699b5e350cSHitoshi Mitake
570166a9764SNamhyung Kim /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */
57132dcd021SJiri Olsa int (*acquired_event)(struct evsel *evsel,
572746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample);
5739b5e350cSHitoshi Mitake
574166a9764SNamhyung Kim /* it's used on CONFIG_LOCKDEP && CONFIG_LOCK_STAT */
57532dcd021SJiri Olsa int (*contended_event)(struct evsel *evsel,
576746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample);
5779b5e350cSHitoshi Mitake
578166a9764SNamhyung Kim /* it's used on CONFIG_LOCKDEP */
57932dcd021SJiri Olsa int (*release_event)(struct evsel *evsel,
580746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample);
581166a9764SNamhyung Kim
582166a9764SNamhyung Kim /* it's used when CONFIG_LOCKDEP is off */
583166a9764SNamhyung Kim int (*contention_begin_event)(struct evsel *evsel,
584166a9764SNamhyung Kim struct perf_sample *sample);
585166a9764SNamhyung Kim
586166a9764SNamhyung Kim /* it's used when CONFIG_LOCKDEP is off */
587166a9764SNamhyung Kim int (*contention_end_event)(struct evsel *evsel,
588166a9764SNamhyung Kim struct perf_sample *sample);
5899b5e350cSHitoshi Mitake };
5909b5e350cSHitoshi Mitake
get_seq(struct thread_stat * ts,u64 addr)591e1c3177bSNamhyung Kim static struct lock_seq_stat *get_seq(struct thread_stat *ts, u64 addr)
592e4cef1f6SHitoshi Mitake {
593e4cef1f6SHitoshi Mitake struct lock_seq_stat *seq;
594e4cef1f6SHitoshi Mitake
595e4cef1f6SHitoshi Mitake list_for_each_entry(seq, &ts->seq_list, list) {
596e4cef1f6SHitoshi Mitake if (seq->addr == addr)
597e4cef1f6SHitoshi Mitake return seq;
598e4cef1f6SHitoshi Mitake }
599e4cef1f6SHitoshi Mitake
600e4cef1f6SHitoshi Mitake seq = zalloc(sizeof(struct lock_seq_stat));
60133d6aef5SDavid Ahern if (!seq) {
60233d6aef5SDavid Ahern pr_err("memory allocation failed\n");
60333d6aef5SDavid Ahern return NULL;
60433d6aef5SDavid Ahern }
605e4cef1f6SHitoshi Mitake seq->state = SEQ_STATE_UNINITIALIZED;
606e4cef1f6SHitoshi Mitake seq->addr = addr;
607e4cef1f6SHitoshi Mitake
608e4cef1f6SHitoshi Mitake list_add(&seq->list, &ts->seq_list);
609e4cef1f6SHitoshi Mitake return seq;
610e4cef1f6SHitoshi Mitake }
611e4cef1f6SHitoshi Mitake
61210350ec3SFrederic Weisbecker enum broken_state {
61310350ec3SFrederic Weisbecker BROKEN_ACQUIRE,
61410350ec3SFrederic Weisbecker BROKEN_ACQUIRED,
61510350ec3SFrederic Weisbecker BROKEN_CONTENDED,
61610350ec3SFrederic Weisbecker BROKEN_RELEASE,
61710350ec3SFrederic Weisbecker BROKEN_MAX,
61810350ec3SFrederic Weisbecker };
61910350ec3SFrederic Weisbecker
62010350ec3SFrederic Weisbecker static int bad_hist[BROKEN_MAX];
621e4cef1f6SHitoshi Mitake
62284c7a217SFrederic Weisbecker enum acquire_flags {
62384c7a217SFrederic Weisbecker TRY_LOCK = 1,
62484c7a217SFrederic Weisbecker READ_LOCK = 2,
62584c7a217SFrederic Weisbecker };
62684c7a217SFrederic Weisbecker
get_key_by_aggr_mode_simple(u64 * key,u64 addr,u32 tid)6270f405f87SShang XiaoJing static int get_key_by_aggr_mode_simple(u64 *key, u64 addr, u32 tid)
6280f405f87SShang XiaoJing {
6290f405f87SShang XiaoJing switch (aggr_mode) {
6300f405f87SShang XiaoJing case LOCK_AGGR_ADDR:
6310f405f87SShang XiaoJing *key = addr;
6320f405f87SShang XiaoJing break;
6330f405f87SShang XiaoJing case LOCK_AGGR_TASK:
6340f405f87SShang XiaoJing *key = tid;
6350f405f87SShang XiaoJing break;
6360f405f87SShang XiaoJing case LOCK_AGGR_CALLER:
6370f405f87SShang XiaoJing default:
6380f405f87SShang XiaoJing pr_err("Invalid aggregation mode: %d\n", aggr_mode);
6390f405f87SShang XiaoJing return -EINVAL;
6400f405f87SShang XiaoJing }
6410f405f87SShang XiaoJing return 0;
6420f405f87SShang XiaoJing }
6430f405f87SShang XiaoJing
6440f405f87SShang XiaoJing static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample);
6450f405f87SShang XiaoJing
get_key_by_aggr_mode(u64 * key,u64 addr,struct evsel * evsel,struct perf_sample * sample)6460f405f87SShang XiaoJing static int get_key_by_aggr_mode(u64 *key, u64 addr, struct evsel *evsel,
6470f405f87SShang XiaoJing struct perf_sample *sample)
6480f405f87SShang XiaoJing {
6490f405f87SShang XiaoJing if (aggr_mode == LOCK_AGGR_CALLER) {
6500f405f87SShang XiaoJing *key = callchain_id(evsel, sample);
6510f405f87SShang XiaoJing return 0;
6520f405f87SShang XiaoJing }
6530f405f87SShang XiaoJing return get_key_by_aggr_mode_simple(key, addr, sample->tid);
6540f405f87SShang XiaoJing }
6550f405f87SShang XiaoJing
report_lock_acquire_event(struct evsel * evsel,struct perf_sample * sample)65632dcd021SJiri Olsa static int report_lock_acquire_event(struct evsel *evsel,
657746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample)
6589b5e350cSHitoshi Mitake {
659e4cef1f6SHitoshi Mitake struct lock_stat *ls;
660e4cef1f6SHitoshi Mitake struct thread_stat *ts;
661e4cef1f6SHitoshi Mitake struct lock_seq_stat *seq;
662efc0cdc9SArnaldo Carvalho de Melo const char *name = evsel__strval(evsel, sample, "name");
663e1c3177bSNamhyung Kim u64 addr = evsel__intval(evsel, sample, "lockdep_addr");
664e24a87b5SLeo Yan int flag = evsel__intval(evsel, sample, "flags");
665f9c695a2SNamhyung Kim u64 key;
6660f405f87SShang XiaoJing int ret;
6679b5e350cSHitoshi Mitake
6680f405f87SShang XiaoJing ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);
6690f405f87SShang XiaoJing if (ret < 0)
6700f405f87SShang XiaoJing return ret;
6717c3bcbdfSNamhyung Kim
672f9c695a2SNamhyung Kim ls = lock_stat_findnew(key, name, 0);
67333d6aef5SDavid Ahern if (!ls)
674b33492adSDavidlohr Bueso return -ENOMEM;
6759b5e350cSHitoshi Mitake
67601d95524SArnaldo Carvalho de Melo ts = thread_stat_findnew(sample->tid);
67733d6aef5SDavid Ahern if (!ts)
678b33492adSDavidlohr Bueso return -ENOMEM;
67933d6aef5SDavid Ahern
680746f16ecSArnaldo Carvalho de Melo seq = get_seq(ts, addr);
68133d6aef5SDavid Ahern if (!seq)
682b33492adSDavidlohr Bueso return -ENOMEM;
683e4cef1f6SHitoshi Mitake
684e4cef1f6SHitoshi Mitake switch (seq->state) {
685e4cef1f6SHitoshi Mitake case SEQ_STATE_UNINITIALIZED:
686e4cef1f6SHitoshi Mitake case SEQ_STATE_RELEASED:
687746f16ecSArnaldo Carvalho de Melo if (!flag) {
688e4cef1f6SHitoshi Mitake seq->state = SEQ_STATE_ACQUIRING;
689e4cef1f6SHitoshi Mitake } else {
690746f16ecSArnaldo Carvalho de Melo if (flag & TRY_LOCK)
691e4cef1f6SHitoshi Mitake ls->nr_trylock++;
692746f16ecSArnaldo Carvalho de Melo if (flag & READ_LOCK)
693e4cef1f6SHitoshi Mitake ls->nr_readlock++;
694e4cef1f6SHitoshi Mitake seq->state = SEQ_STATE_READ_ACQUIRED;
695e4cef1f6SHitoshi Mitake seq->read_count = 1;
696e4cef1f6SHitoshi Mitake ls->nr_acquired++;
697e4cef1f6SHitoshi Mitake }
6989b5e350cSHitoshi Mitake break;
699e4cef1f6SHitoshi Mitake case SEQ_STATE_READ_ACQUIRED:
700746f16ecSArnaldo Carvalho de Melo if (flag & READ_LOCK) {
701e4cef1f6SHitoshi Mitake seq->read_count++;
702e4cef1f6SHitoshi Mitake ls->nr_acquired++;
703e4cef1f6SHitoshi Mitake goto end;
704e4cef1f6SHitoshi Mitake } else {
705e4cef1f6SHitoshi Mitake goto broken;
706e4cef1f6SHitoshi Mitake }
707e4cef1f6SHitoshi Mitake break;
708e4cef1f6SHitoshi Mitake case SEQ_STATE_ACQUIRED:
709e4cef1f6SHitoshi Mitake case SEQ_STATE_ACQUIRING:
710e4cef1f6SHitoshi Mitake case SEQ_STATE_CONTENDED:
711e4cef1f6SHitoshi Mitake broken:
71279d9333bSNamhyung Kim /* broken lock sequence */
71379d9333bSNamhyung Kim if (!ls->broken) {
71479d9333bSNamhyung Kim ls->broken = 1;
71510350ec3SFrederic Weisbecker bad_hist[BROKEN_ACQUIRE]++;
71679d9333bSNamhyung Kim }
717e56fbc9dSArnaldo Carvalho de Melo list_del_init(&seq->list);
718e4cef1f6SHitoshi Mitake free(seq);
719e4cef1f6SHitoshi Mitake goto end;
7209b5e350cSHitoshi Mitake default:
721e4cef1f6SHitoshi Mitake BUG_ON("Unknown state of lock sequence found!\n");
7229b5e350cSHitoshi Mitake break;
7239b5e350cSHitoshi Mitake }
7249b5e350cSHitoshi Mitake
725e4cef1f6SHitoshi Mitake ls->nr_acquire++;
72601d95524SArnaldo Carvalho de Melo seq->prev_event_time = sample->time;
727e4cef1f6SHitoshi Mitake end:
72833d6aef5SDavid Ahern return 0;
7299b5e350cSHitoshi Mitake }
7309b5e350cSHitoshi Mitake
report_lock_acquired_event(struct evsel * evsel,struct perf_sample * sample)73132dcd021SJiri Olsa static int report_lock_acquired_event(struct evsel *evsel,
732746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample)
7339b5e350cSHitoshi Mitake {
734e4cef1f6SHitoshi Mitake struct lock_stat *ls;
735e4cef1f6SHitoshi Mitake struct thread_stat *ts;
736e4cef1f6SHitoshi Mitake struct lock_seq_stat *seq;
737e4cef1f6SHitoshi Mitake u64 contended_term;
738efc0cdc9SArnaldo Carvalho de Melo const char *name = evsel__strval(evsel, sample, "name");
739e1c3177bSNamhyung Kim u64 addr = evsel__intval(evsel, sample, "lockdep_addr");
740f9c695a2SNamhyung Kim u64 key;
7410f405f87SShang XiaoJing int ret;
742746f16ecSArnaldo Carvalho de Melo
7430f405f87SShang XiaoJing ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);
7440f405f87SShang XiaoJing if (ret < 0)
7450f405f87SShang XiaoJing return ret;
7467c3bcbdfSNamhyung Kim
747f9c695a2SNamhyung Kim ls = lock_stat_findnew(key, name, 0);
74833d6aef5SDavid Ahern if (!ls)
749b33492adSDavidlohr Bueso return -ENOMEM;
7509b5e350cSHitoshi Mitake
75101d95524SArnaldo Carvalho de Melo ts = thread_stat_findnew(sample->tid);
75233d6aef5SDavid Ahern if (!ts)
753b33492adSDavidlohr Bueso return -ENOMEM;
75433d6aef5SDavid Ahern
755746f16ecSArnaldo Carvalho de Melo seq = get_seq(ts, addr);
75633d6aef5SDavid Ahern if (!seq)
757b33492adSDavidlohr Bueso return -ENOMEM;
758e4cef1f6SHitoshi Mitake
759e4cef1f6SHitoshi Mitake switch (seq->state) {
760e4cef1f6SHitoshi Mitake case SEQ_STATE_UNINITIALIZED:
761e4cef1f6SHitoshi Mitake /* orphan event, do nothing */
76233d6aef5SDavid Ahern return 0;
763e4cef1f6SHitoshi Mitake case SEQ_STATE_ACQUIRING:
7649b5e350cSHitoshi Mitake break;
765e4cef1f6SHitoshi Mitake case SEQ_STATE_CONTENDED:
766746f16ecSArnaldo Carvalho de Melo contended_term = sample->time - seq->prev_event_time;
767e4cef1f6SHitoshi Mitake ls->wait_time_total += contended_term;
768e4cef1f6SHitoshi Mitake if (contended_term < ls->wait_time_min)
769e4cef1f6SHitoshi Mitake ls->wait_time_min = contended_term;
77090c0e5fcSFrederic Weisbecker if (ls->wait_time_max < contended_term)
771e4cef1f6SHitoshi Mitake ls->wait_time_max = contended_term;
7729b5e350cSHitoshi Mitake break;
773e4cef1f6SHitoshi Mitake case SEQ_STATE_RELEASED:
774e4cef1f6SHitoshi Mitake case SEQ_STATE_ACQUIRED:
775e4cef1f6SHitoshi Mitake case SEQ_STATE_READ_ACQUIRED:
77679d9333bSNamhyung Kim /* broken lock sequence */
77779d9333bSNamhyung Kim if (!ls->broken) {
77879d9333bSNamhyung Kim ls->broken = 1;
77910350ec3SFrederic Weisbecker bad_hist[BROKEN_ACQUIRED]++;
78079d9333bSNamhyung Kim }
781e56fbc9dSArnaldo Carvalho de Melo list_del_init(&seq->list);
782e4cef1f6SHitoshi Mitake free(seq);
783e4cef1f6SHitoshi Mitake goto end;
7849b5e350cSHitoshi Mitake default:
785e4cef1f6SHitoshi Mitake BUG_ON("Unknown state of lock sequence found!\n");
7869b5e350cSHitoshi Mitake break;
7879b5e350cSHitoshi Mitake }
7889b5e350cSHitoshi Mitake
789e4cef1f6SHitoshi Mitake seq->state = SEQ_STATE_ACQUIRED;
790e4cef1f6SHitoshi Mitake ls->nr_acquired++;
791f37376cdSDavidlohr Bueso ls->avg_wait_time = ls->nr_contended ? ls->wait_time_total/ls->nr_contended : 0;
792746f16ecSArnaldo Carvalho de Melo seq->prev_event_time = sample->time;
793e4cef1f6SHitoshi Mitake end:
79433d6aef5SDavid Ahern return 0;
7959b5e350cSHitoshi Mitake }
7969b5e350cSHitoshi Mitake
report_lock_contended_event(struct evsel * evsel,struct perf_sample * sample)79732dcd021SJiri Olsa static int report_lock_contended_event(struct evsel *evsel,
798746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample)
7999b5e350cSHitoshi Mitake {
800e4cef1f6SHitoshi Mitake struct lock_stat *ls;
801e4cef1f6SHitoshi Mitake struct thread_stat *ts;
802e4cef1f6SHitoshi Mitake struct lock_seq_stat *seq;
803efc0cdc9SArnaldo Carvalho de Melo const char *name = evsel__strval(evsel, sample, "name");
804e1c3177bSNamhyung Kim u64 addr = evsel__intval(evsel, sample, "lockdep_addr");
805f9c695a2SNamhyung Kim u64 key;
8060f405f87SShang XiaoJing int ret;
807746f16ecSArnaldo Carvalho de Melo
8080f405f87SShang XiaoJing ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);
8090f405f87SShang XiaoJing if (ret < 0)
8100f405f87SShang XiaoJing return ret;
8117c3bcbdfSNamhyung Kim
812f9c695a2SNamhyung Kim ls = lock_stat_findnew(key, name, 0);
81333d6aef5SDavid Ahern if (!ls)
814b33492adSDavidlohr Bueso return -ENOMEM;
8159b5e350cSHitoshi Mitake
81601d95524SArnaldo Carvalho de Melo ts = thread_stat_findnew(sample->tid);
81733d6aef5SDavid Ahern if (!ts)
818b33492adSDavidlohr Bueso return -ENOMEM;
81933d6aef5SDavid Ahern
820746f16ecSArnaldo Carvalho de Melo seq = get_seq(ts, addr);
82133d6aef5SDavid Ahern if (!seq)
822b33492adSDavidlohr Bueso return -ENOMEM;
823e4cef1f6SHitoshi Mitake
824e4cef1f6SHitoshi Mitake switch (seq->state) {
825e4cef1f6SHitoshi Mitake case SEQ_STATE_UNINITIALIZED:
826e4cef1f6SHitoshi Mitake /* orphan event, do nothing */
82733d6aef5SDavid Ahern return 0;
828e4cef1f6SHitoshi Mitake case SEQ_STATE_ACQUIRING:
8299b5e350cSHitoshi Mitake break;
830e4cef1f6SHitoshi Mitake case SEQ_STATE_RELEASED:
831e4cef1f6SHitoshi Mitake case SEQ_STATE_ACQUIRED:
832e4cef1f6SHitoshi Mitake case SEQ_STATE_READ_ACQUIRED:
833e4cef1f6SHitoshi Mitake case SEQ_STATE_CONTENDED:
83479d9333bSNamhyung Kim /* broken lock sequence */
83579d9333bSNamhyung Kim if (!ls->broken) {
83679d9333bSNamhyung Kim ls->broken = 1;
83710350ec3SFrederic Weisbecker bad_hist[BROKEN_CONTENDED]++;
83879d9333bSNamhyung Kim }
839e56fbc9dSArnaldo Carvalho de Melo list_del_init(&seq->list);
840e4cef1f6SHitoshi Mitake free(seq);
841e4cef1f6SHitoshi Mitake goto end;
8429b5e350cSHitoshi Mitake default:
843e4cef1f6SHitoshi Mitake BUG_ON("Unknown state of lock sequence found!\n");
8449b5e350cSHitoshi Mitake break;
8459b5e350cSHitoshi Mitake }
8469b5e350cSHitoshi Mitake
847e4cef1f6SHitoshi Mitake seq->state = SEQ_STATE_CONTENDED;
848e4cef1f6SHitoshi Mitake ls->nr_contended++;
849f37376cdSDavidlohr Bueso ls->avg_wait_time = ls->wait_time_total/ls->nr_contended;
85001d95524SArnaldo Carvalho de Melo seq->prev_event_time = sample->time;
851e4cef1f6SHitoshi Mitake end:
85233d6aef5SDavid Ahern return 0;
8539b5e350cSHitoshi Mitake }
8549b5e350cSHitoshi Mitake
report_lock_release_event(struct evsel * evsel,struct perf_sample * sample)85532dcd021SJiri Olsa static int report_lock_release_event(struct evsel *evsel,
856746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample)
8579b5e350cSHitoshi Mitake {
858e4cef1f6SHitoshi Mitake struct lock_stat *ls;
859e4cef1f6SHitoshi Mitake struct thread_stat *ts;
860e4cef1f6SHitoshi Mitake struct lock_seq_stat *seq;
861efc0cdc9SArnaldo Carvalho de Melo const char *name = evsel__strval(evsel, sample, "name");
862e1c3177bSNamhyung Kim u64 addr = evsel__intval(evsel, sample, "lockdep_addr");
863f9c695a2SNamhyung Kim u64 key;
8640f405f87SShang XiaoJing int ret;
865746f16ecSArnaldo Carvalho de Melo
8660f405f87SShang XiaoJing ret = get_key_by_aggr_mode_simple(&key, addr, sample->tid);
8670f405f87SShang XiaoJing if (ret < 0)
8680f405f87SShang XiaoJing return ret;
8697c3bcbdfSNamhyung Kim
870f9c695a2SNamhyung Kim ls = lock_stat_findnew(key, name, 0);
87133d6aef5SDavid Ahern if (!ls)
872b33492adSDavidlohr Bueso return -ENOMEM;
8739b5e350cSHitoshi Mitake
87401d95524SArnaldo Carvalho de Melo ts = thread_stat_findnew(sample->tid);
87533d6aef5SDavid Ahern if (!ts)
876b33492adSDavidlohr Bueso return -ENOMEM;
87733d6aef5SDavid Ahern
878746f16ecSArnaldo Carvalho de Melo seq = get_seq(ts, addr);
87933d6aef5SDavid Ahern if (!seq)
880b33492adSDavidlohr Bueso return -ENOMEM;
881e4cef1f6SHitoshi Mitake
882e4cef1f6SHitoshi Mitake switch (seq->state) {
883e4cef1f6SHitoshi Mitake case SEQ_STATE_UNINITIALIZED:
884e4cef1f6SHitoshi Mitake goto end;
885e4cef1f6SHitoshi Mitake case SEQ_STATE_ACQUIRED:
886e4cef1f6SHitoshi Mitake break;
887e4cef1f6SHitoshi Mitake case SEQ_STATE_READ_ACQUIRED:
888e4cef1f6SHitoshi Mitake seq->read_count--;
889e4cef1f6SHitoshi Mitake BUG_ON(seq->read_count < 0);
890b0e5a05cSLeo Yan if (seq->read_count) {
891e4cef1f6SHitoshi Mitake ls->nr_release++;
8929b5e350cSHitoshi Mitake goto end;
8939b5e350cSHitoshi Mitake }
894e4cef1f6SHitoshi Mitake break;
895e4cef1f6SHitoshi Mitake case SEQ_STATE_ACQUIRING:
896e4cef1f6SHitoshi Mitake case SEQ_STATE_CONTENDED:
897e4cef1f6SHitoshi Mitake case SEQ_STATE_RELEASED:
89879d9333bSNamhyung Kim /* broken lock sequence */
89979d9333bSNamhyung Kim if (!ls->broken) {
90079d9333bSNamhyung Kim ls->broken = 1;
90110350ec3SFrederic Weisbecker bad_hist[BROKEN_RELEASE]++;
90279d9333bSNamhyung Kim }
903e4cef1f6SHitoshi Mitake goto free_seq;
9049b5e350cSHitoshi Mitake default:
905e4cef1f6SHitoshi Mitake BUG_ON("Unknown state of lock sequence found!\n");
9069b5e350cSHitoshi Mitake break;
9079b5e350cSHitoshi Mitake }
9089b5e350cSHitoshi Mitake
909e4cef1f6SHitoshi Mitake ls->nr_release++;
910e4cef1f6SHitoshi Mitake free_seq:
911e56fbc9dSArnaldo Carvalho de Melo list_del_init(&seq->list);
912e4cef1f6SHitoshi Mitake free(seq);
9139b5e350cSHitoshi Mitake end:
91433d6aef5SDavid Ahern return 0;
9159b5e350cSHitoshi Mitake }
9169b5e350cSHitoshi Mitake
get_symbol_name_offset(struct map * map,struct symbol * sym,u64 ip,char * buf,int size)917637522ceSNamhyung Kim static int get_symbol_name_offset(struct map *map, struct symbol *sym, u64 ip,
918637522ceSNamhyung Kim char *buf, int size)
919637522ceSNamhyung Kim {
920637522ceSNamhyung Kim u64 offset;
921637522ceSNamhyung Kim
922637522ceSNamhyung Kim if (map == NULL || sym == NULL) {
923637522ceSNamhyung Kim buf[0] = '\0';
924637522ceSNamhyung Kim return 0;
925637522ceSNamhyung Kim }
926637522ceSNamhyung Kim
92778a1f7cdSIan Rogers offset = map__map_ip(map, ip) - sym->start;
928637522ceSNamhyung Kim
929637522ceSNamhyung Kim if (offset)
930637522ceSNamhyung Kim return scnprintf(buf, size, "%s+%#lx", sym->name, offset);
931637522ceSNamhyung Kim else
932637522ceSNamhyung Kim return strlcpy(buf, sym->name, size);
933637522ceSNamhyung Kim }
lock_contention_caller(struct evsel * evsel,struct perf_sample * sample,char * buf,int size)9340d2997f7SNamhyung Kim static int lock_contention_caller(struct evsel *evsel, struct perf_sample *sample,
9350d2997f7SNamhyung Kim char *buf, int size)
9360d2997f7SNamhyung Kim {
9370d2997f7SNamhyung Kim struct thread *thread;
9388ab12a20SIan Rogers struct callchain_cursor *cursor;
93977d54a2cSNamhyung Kim struct machine *machine = &session->machines.host;
9400d2997f7SNamhyung Kim struct symbol *sym;
9410d2997f7SNamhyung Kim int skip = 0;
9420d2997f7SNamhyung Kim int ret;
9430d2997f7SNamhyung Kim
9440d2997f7SNamhyung Kim /* lock names will be replaced to task name later */
9450d2997f7SNamhyung Kim if (show_thread_stats)
9460d2997f7SNamhyung Kim return -1;
9470d2997f7SNamhyung Kim
94877d54a2cSNamhyung Kim thread = machine__findnew_thread(machine, -1, sample->pid);
9490d2997f7SNamhyung Kim if (thread == NULL)
9500d2997f7SNamhyung Kim return -1;
9510d2997f7SNamhyung Kim
9528ab12a20SIan Rogers cursor = get_tls_callchain_cursor();
9538ab12a20SIan Rogers
9540d2997f7SNamhyung Kim /* use caller function name from the callchain */
9550d2997f7SNamhyung Kim ret = thread__resolve_callchain(thread, cursor, evsel, sample,
95696532a83SNamhyung Kim NULL, NULL, max_stack_depth);
9570d2997f7SNamhyung Kim if (ret != 0) {
9580d2997f7SNamhyung Kim thread__put(thread);
9590d2997f7SNamhyung Kim return -1;
9600d2997f7SNamhyung Kim }
9610d2997f7SNamhyung Kim
9620d2997f7SNamhyung Kim callchain_cursor_commit(cursor);
9630d2997f7SNamhyung Kim thread__put(thread);
9640d2997f7SNamhyung Kim
9650d2997f7SNamhyung Kim while (true) {
9660d2997f7SNamhyung Kim struct callchain_cursor_node *node;
9670d2997f7SNamhyung Kim
9680d2997f7SNamhyung Kim node = callchain_cursor_current(cursor);
9690d2997f7SNamhyung Kim if (node == NULL)
9700d2997f7SNamhyung Kim break;
9710d2997f7SNamhyung Kim
9720d2997f7SNamhyung Kim /* skip first few entries - for lock functions */
97396532a83SNamhyung Kim if (++skip <= stack_skip)
9740d2997f7SNamhyung Kim goto next;
9750d2997f7SNamhyung Kim
9760d2997f7SNamhyung Kim sym = node->ms.sym;
977cc2367eeSArnaldo Carvalho de Melo if (sym && !machine__is_lock_function(machine, node->ip)) {
978637522ceSNamhyung Kim get_symbol_name_offset(node->ms.map, sym, node->ip,
979637522ceSNamhyung Kim buf, size);
9800d2997f7SNamhyung Kim return 0;
9810d2997f7SNamhyung Kim }
9820d2997f7SNamhyung Kim
9830d2997f7SNamhyung Kim next:
9840d2997f7SNamhyung Kim callchain_cursor_advance(cursor);
9850d2997f7SNamhyung Kim }
9860d2997f7SNamhyung Kim return -1;
9870d2997f7SNamhyung Kim }
9880d2997f7SNamhyung Kim
callchain_id(struct evsel * evsel,struct perf_sample * sample)989528b9cabSNamhyung Kim static u64 callchain_id(struct evsel *evsel, struct perf_sample *sample)
990528b9cabSNamhyung Kim {
9918ab12a20SIan Rogers struct callchain_cursor *cursor;
99277d54a2cSNamhyung Kim struct machine *machine = &session->machines.host;
993528b9cabSNamhyung Kim struct thread *thread;
994528b9cabSNamhyung Kim u64 hash = 0;
995528b9cabSNamhyung Kim int skip = 0;
996528b9cabSNamhyung Kim int ret;
997528b9cabSNamhyung Kim
99877d54a2cSNamhyung Kim thread = machine__findnew_thread(machine, -1, sample->pid);
999528b9cabSNamhyung Kim if (thread == NULL)
1000528b9cabSNamhyung Kim return -1;
1001528b9cabSNamhyung Kim
10028ab12a20SIan Rogers cursor = get_tls_callchain_cursor();
1003528b9cabSNamhyung Kim /* use caller function name from the callchain */
1004528b9cabSNamhyung Kim ret = thread__resolve_callchain(thread, cursor, evsel, sample,
100596532a83SNamhyung Kim NULL, NULL, max_stack_depth);
1006528b9cabSNamhyung Kim thread__put(thread);
1007528b9cabSNamhyung Kim
1008528b9cabSNamhyung Kim if (ret != 0)
1009528b9cabSNamhyung Kim return -1;
1010528b9cabSNamhyung Kim
1011528b9cabSNamhyung Kim callchain_cursor_commit(cursor);
1012528b9cabSNamhyung Kim
1013528b9cabSNamhyung Kim while (true) {
1014528b9cabSNamhyung Kim struct callchain_cursor_node *node;
1015528b9cabSNamhyung Kim
1016528b9cabSNamhyung Kim node = callchain_cursor_current(cursor);
1017528b9cabSNamhyung Kim if (node == NULL)
1018528b9cabSNamhyung Kim break;
1019528b9cabSNamhyung Kim
1020528b9cabSNamhyung Kim /* skip first few entries - for lock functions */
102196532a83SNamhyung Kim if (++skip <= stack_skip)
1022528b9cabSNamhyung Kim goto next;
1023528b9cabSNamhyung Kim
1024cc2367eeSArnaldo Carvalho de Melo if (node->ms.sym && machine__is_lock_function(machine, node->ip))
1025528b9cabSNamhyung Kim goto next;
1026528b9cabSNamhyung Kim
1027528b9cabSNamhyung Kim hash ^= hash_long((unsigned long)node->ip, 64);
1028528b9cabSNamhyung Kim
1029528b9cabSNamhyung Kim next:
1030528b9cabSNamhyung Kim callchain_cursor_advance(cursor);
1031528b9cabSNamhyung Kim }
1032528b9cabSNamhyung Kim return hash;
1033528b9cabSNamhyung Kim }
1034528b9cabSNamhyung Kim
get_callstack(struct perf_sample * sample,int max_stack)1035a6eaf966SNamhyung Kim static u64 *get_callstack(struct perf_sample *sample, int max_stack)
1036a6eaf966SNamhyung Kim {
1037a6eaf966SNamhyung Kim u64 *callstack;
1038a6eaf966SNamhyung Kim u64 i;
1039a6eaf966SNamhyung Kim int c;
1040a6eaf966SNamhyung Kim
1041a6eaf966SNamhyung Kim callstack = calloc(max_stack, sizeof(*callstack));
1042a6eaf966SNamhyung Kim if (callstack == NULL)
1043a6eaf966SNamhyung Kim return NULL;
1044a6eaf966SNamhyung Kim
1045a6eaf966SNamhyung Kim for (i = 0, c = 0; i < sample->callchain->nr && c < max_stack; i++) {
1046a6eaf966SNamhyung Kim u64 ip = sample->callchain->ips[i];
1047a6eaf966SNamhyung Kim
1048a6eaf966SNamhyung Kim if (ip >= PERF_CONTEXT_MAX)
1049a6eaf966SNamhyung Kim continue;
1050a6eaf966SNamhyung Kim
1051a6eaf966SNamhyung Kim callstack[c++] = ip;
1052a6eaf966SNamhyung Kim }
1053a6eaf966SNamhyung Kim return callstack;
1054a6eaf966SNamhyung Kim }
1055a6eaf966SNamhyung Kim
report_lock_contention_begin_event(struct evsel * evsel,struct perf_sample * sample)10563ae03f26SNamhyung Kim static int report_lock_contention_begin_event(struct evsel *evsel,
10573ae03f26SNamhyung Kim struct perf_sample *sample)
10583ae03f26SNamhyung Kim {
10593ae03f26SNamhyung Kim struct lock_stat *ls;
10603ae03f26SNamhyung Kim struct thread_stat *ts;
10613ae03f26SNamhyung Kim struct lock_seq_stat *seq;
10623ae03f26SNamhyung Kim u64 addr = evsel__intval(evsel, sample, "lock_addr");
1063b4a7eff9SNamhyung Kim unsigned int flags = evsel__intval(evsel, sample, "flags");
1064f9c695a2SNamhyung Kim u64 key;
1065b4a7eff9SNamhyung Kim int i, ret;
1066511e19b9SNamhyung Kim static bool kmap_loaded;
1067511e19b9SNamhyung Kim struct machine *machine = &session->machines.host;
1068511e19b9SNamhyung Kim struct map *kmap;
1069511e19b9SNamhyung Kim struct symbol *sym;
10703ae03f26SNamhyung Kim
10710f405f87SShang XiaoJing ret = get_key_by_aggr_mode(&key, addr, evsel, sample);
10720f405f87SShang XiaoJing if (ret < 0)
10730f405f87SShang XiaoJing return ret;
10743ae03f26SNamhyung Kim
1075511e19b9SNamhyung Kim if (!kmap_loaded) {
1076511e19b9SNamhyung Kim unsigned long *addrs;
1077511e19b9SNamhyung Kim
1078511e19b9SNamhyung Kim /* make sure it loads the kernel map to find lock symbols */
1079511e19b9SNamhyung Kim map__load(machine__kernel_map(machine));
1080511e19b9SNamhyung Kim kmap_loaded = true;
1081511e19b9SNamhyung Kim
1082511e19b9SNamhyung Kim /* convert (kernel) symbols to addresses */
1083511e19b9SNamhyung Kim for (i = 0; i < filters.nr_syms; i++) {
1084511e19b9SNamhyung Kim sym = machine__find_kernel_symbol_by_name(machine,
1085511e19b9SNamhyung Kim filters.syms[i],
1086511e19b9SNamhyung Kim &kmap);
1087511e19b9SNamhyung Kim if (sym == NULL) {
1088511e19b9SNamhyung Kim pr_warning("ignore unknown symbol: %s\n",
1089511e19b9SNamhyung Kim filters.syms[i]);
1090511e19b9SNamhyung Kim continue;
1091511e19b9SNamhyung Kim }
1092511e19b9SNamhyung Kim
1093511e19b9SNamhyung Kim addrs = realloc(filters.addrs,
1094511e19b9SNamhyung Kim (filters.nr_addrs + 1) * sizeof(*addrs));
1095511e19b9SNamhyung Kim if (addrs == NULL) {
1096511e19b9SNamhyung Kim pr_warning("memory allocation failure\n");
1097511e19b9SNamhyung Kim return -ENOMEM;
1098511e19b9SNamhyung Kim }
1099511e19b9SNamhyung Kim
110078a1f7cdSIan Rogers addrs[filters.nr_addrs++] = map__unmap_ip(kmap, sym->start);
1101511e19b9SNamhyung Kim filters.addrs = addrs;
1102511e19b9SNamhyung Kim }
1103511e19b9SNamhyung Kim }
1104511e19b9SNamhyung Kim
1105f9c695a2SNamhyung Kim ls = lock_stat_find(key);
11060d2997f7SNamhyung Kim if (!ls) {
11070d2997f7SNamhyung Kim char buf[128];
1108688d2e8dSNamhyung Kim const char *name = "";
11090d2997f7SNamhyung Kim
1110688d2e8dSNamhyung Kim switch (aggr_mode) {
1111688d2e8dSNamhyung Kim case LOCK_AGGR_ADDR:
1112688d2e8dSNamhyung Kim sym = machine__find_kernel_symbol(machine, key, &kmap);
1113688d2e8dSNamhyung Kim if (sym)
1114688d2e8dSNamhyung Kim name = sym->name;
1115688d2e8dSNamhyung Kim break;
1116688d2e8dSNamhyung Kim case LOCK_AGGR_CALLER:
1117688d2e8dSNamhyung Kim name = buf;
11180d2997f7SNamhyung Kim if (lock_contention_caller(evsel, sample, buf, sizeof(buf)) < 0)
1119688d2e8dSNamhyung Kim name = "Unknown";
1120688d2e8dSNamhyung Kim break;
1121688d2e8dSNamhyung Kim case LOCK_AGGR_TASK:
1122688d2e8dSNamhyung Kim default:
1123688d2e8dSNamhyung Kim break;
1124688d2e8dSNamhyung Kim }
11250d2997f7SNamhyung Kim
1126688d2e8dSNamhyung Kim ls = lock_stat_findnew(key, name, flags);
11273ae03f26SNamhyung Kim if (!ls)
11283ae03f26SNamhyung Kim return -ENOMEM;
11290d2997f7SNamhyung Kim }
11303ae03f26SNamhyung Kim
1131b4a7eff9SNamhyung Kim if (filters.nr_types) {
1132b4a7eff9SNamhyung Kim bool found = false;
1133b4a7eff9SNamhyung Kim
1134b4a7eff9SNamhyung Kim for (i = 0; i < filters.nr_types; i++) {
1135b4a7eff9SNamhyung Kim if (flags == filters.types[i]) {
1136b4a7eff9SNamhyung Kim found = true;
1137b4a7eff9SNamhyung Kim break;
1138b4a7eff9SNamhyung Kim }
1139b4a7eff9SNamhyung Kim }
1140b4a7eff9SNamhyung Kim
1141b4a7eff9SNamhyung Kim if (!found)
1142b4a7eff9SNamhyung Kim return 0;
1143b4a7eff9SNamhyung Kim }
1144b4a7eff9SNamhyung Kim
1145511e19b9SNamhyung Kim if (filters.nr_addrs) {
1146511e19b9SNamhyung Kim bool found = false;
1147511e19b9SNamhyung Kim
1148511e19b9SNamhyung Kim for (i = 0; i < filters.nr_addrs; i++) {
1149511e19b9SNamhyung Kim if (addr == filters.addrs[i]) {
1150511e19b9SNamhyung Kim found = true;
1151511e19b9SNamhyung Kim break;
1152511e19b9SNamhyung Kim }
1153511e19b9SNamhyung Kim }
1154511e19b9SNamhyung Kim
1155511e19b9SNamhyung Kim if (!found)
1156511e19b9SNamhyung Kim return 0;
1157511e19b9SNamhyung Kim }
1158511e19b9SNamhyung Kim
1159ebab2916SNamhyung Kim if (needs_callstack()) {
1160ebab2916SNamhyung Kim u64 *callstack = get_callstack(sample, max_stack_depth);
1161ebab2916SNamhyung Kim if (callstack == NULL)
1162ebab2916SNamhyung Kim return -ENOMEM;
1163ebab2916SNamhyung Kim
1164ebab2916SNamhyung Kim if (!match_callstack_filter(machine, callstack)) {
1165ebab2916SNamhyung Kim free(callstack);
1166ebab2916SNamhyung Kim return 0;
1167ebab2916SNamhyung Kim }
1168ebab2916SNamhyung Kim
1169ebab2916SNamhyung Kim if (ls->callstack == NULL)
1170ebab2916SNamhyung Kim ls->callstack = callstack;
1171ebab2916SNamhyung Kim else
1172ebab2916SNamhyung Kim free(callstack);
1173ebab2916SNamhyung Kim }
1174ebab2916SNamhyung Kim
11753ae03f26SNamhyung Kim ts = thread_stat_findnew(sample->tid);
11763ae03f26SNamhyung Kim if (!ts)
11773ae03f26SNamhyung Kim return -ENOMEM;
11783ae03f26SNamhyung Kim
11793ae03f26SNamhyung Kim seq = get_seq(ts, addr);
11803ae03f26SNamhyung Kim if (!seq)
11813ae03f26SNamhyung Kim return -ENOMEM;
11823ae03f26SNamhyung Kim
11833ae03f26SNamhyung Kim switch (seq->state) {
11843ae03f26SNamhyung Kim case SEQ_STATE_UNINITIALIZED:
11853ae03f26SNamhyung Kim case SEQ_STATE_ACQUIRED:
11863ae03f26SNamhyung Kim break;
11873ae03f26SNamhyung Kim case SEQ_STATE_CONTENDED:
11883ae03f26SNamhyung Kim /*
11893ae03f26SNamhyung Kim * It can have nested contention begin with mutex spinning,
11903ae03f26SNamhyung Kim * then we would use the original contention begin event and
11913ae03f26SNamhyung Kim * ignore the second one.
11923ae03f26SNamhyung Kim */
11933ae03f26SNamhyung Kim goto end;
11943ae03f26SNamhyung Kim case SEQ_STATE_ACQUIRING:
11953ae03f26SNamhyung Kim case SEQ_STATE_READ_ACQUIRED:
11963ae03f26SNamhyung Kim case SEQ_STATE_RELEASED:
11973ae03f26SNamhyung Kim /* broken lock sequence */
11983ae03f26SNamhyung Kim if (!ls->broken) {
11993ae03f26SNamhyung Kim ls->broken = 1;
12003ae03f26SNamhyung Kim bad_hist[BROKEN_CONTENDED]++;
12013ae03f26SNamhyung Kim }
12023ae03f26SNamhyung Kim list_del_init(&seq->list);
12033ae03f26SNamhyung Kim free(seq);
12043ae03f26SNamhyung Kim goto end;
12053ae03f26SNamhyung Kim default:
12063ae03f26SNamhyung Kim BUG_ON("Unknown state of lock sequence found!\n");
12073ae03f26SNamhyung Kim break;
12083ae03f26SNamhyung Kim }
12093ae03f26SNamhyung Kim
12103ae03f26SNamhyung Kim if (seq->state != SEQ_STATE_CONTENDED) {
12113ae03f26SNamhyung Kim seq->state = SEQ_STATE_CONTENDED;
12123ae03f26SNamhyung Kim seq->prev_event_time = sample->time;
12133ae03f26SNamhyung Kim ls->nr_contended++;
12143ae03f26SNamhyung Kim }
12153ae03f26SNamhyung Kim end:
12163ae03f26SNamhyung Kim return 0;
12173ae03f26SNamhyung Kim }
12183ae03f26SNamhyung Kim
report_lock_contention_end_event(struct evsel * evsel,struct perf_sample * sample)12193ae03f26SNamhyung Kim static int report_lock_contention_end_event(struct evsel *evsel,
12203ae03f26SNamhyung Kim struct perf_sample *sample)
12213ae03f26SNamhyung Kim {
12223ae03f26SNamhyung Kim struct lock_stat *ls;
12233ae03f26SNamhyung Kim struct thread_stat *ts;
12243ae03f26SNamhyung Kim struct lock_seq_stat *seq;
12253ae03f26SNamhyung Kim u64 contended_term;
12263ae03f26SNamhyung Kim u64 addr = evsel__intval(evsel, sample, "lock_addr");
1227f9c695a2SNamhyung Kim u64 key;
12280f405f87SShang XiaoJing int ret;
12293ae03f26SNamhyung Kim
12300f405f87SShang XiaoJing ret = get_key_by_aggr_mode(&key, addr, evsel, sample);
12310f405f87SShang XiaoJing if (ret < 0)
12320f405f87SShang XiaoJing return ret;
12333ae03f26SNamhyung Kim
1234f9c695a2SNamhyung Kim ls = lock_stat_find(key);
12353ae03f26SNamhyung Kim if (!ls)
12363ae03f26SNamhyung Kim return 0;
12373ae03f26SNamhyung Kim
12383ae03f26SNamhyung Kim ts = thread_stat_find(sample->tid);
12393ae03f26SNamhyung Kim if (!ts)
12403ae03f26SNamhyung Kim return 0;
12413ae03f26SNamhyung Kim
12423ae03f26SNamhyung Kim seq = get_seq(ts, addr);
12433ae03f26SNamhyung Kim if (!seq)
12443ae03f26SNamhyung Kim return -ENOMEM;
12453ae03f26SNamhyung Kim
12463ae03f26SNamhyung Kim switch (seq->state) {
12473ae03f26SNamhyung Kim case SEQ_STATE_UNINITIALIZED:
12483ae03f26SNamhyung Kim goto end;
12493ae03f26SNamhyung Kim case SEQ_STATE_CONTENDED:
12503ae03f26SNamhyung Kim contended_term = sample->time - seq->prev_event_time;
12513ae03f26SNamhyung Kim ls->wait_time_total += contended_term;
12523ae03f26SNamhyung Kim if (contended_term < ls->wait_time_min)
12533ae03f26SNamhyung Kim ls->wait_time_min = contended_term;
12543ae03f26SNamhyung Kim if (ls->wait_time_max < contended_term)
12553ae03f26SNamhyung Kim ls->wait_time_max = contended_term;
12563ae03f26SNamhyung Kim break;
12573ae03f26SNamhyung Kim case SEQ_STATE_ACQUIRING:
12583ae03f26SNamhyung Kim case SEQ_STATE_ACQUIRED:
12593ae03f26SNamhyung Kim case SEQ_STATE_READ_ACQUIRED:
12603ae03f26SNamhyung Kim case SEQ_STATE_RELEASED:
12613ae03f26SNamhyung Kim /* broken lock sequence */
12623ae03f26SNamhyung Kim if (!ls->broken) {
12633ae03f26SNamhyung Kim ls->broken = 1;
12643ae03f26SNamhyung Kim bad_hist[BROKEN_ACQUIRED]++;
12653ae03f26SNamhyung Kim }
12663ae03f26SNamhyung Kim list_del_init(&seq->list);
12673ae03f26SNamhyung Kim free(seq);
12683ae03f26SNamhyung Kim goto end;
12693ae03f26SNamhyung Kim default:
12703ae03f26SNamhyung Kim BUG_ON("Unknown state of lock sequence found!\n");
12713ae03f26SNamhyung Kim break;
12723ae03f26SNamhyung Kim }
12733ae03f26SNamhyung Kim
12743ae03f26SNamhyung Kim seq->state = SEQ_STATE_ACQUIRED;
12753ae03f26SNamhyung Kim ls->nr_acquired++;
12763ae03f26SNamhyung Kim ls->avg_wait_time = ls->wait_time_total/ls->nr_acquired;
12773ae03f26SNamhyung Kim end:
12783ae03f26SNamhyung Kim return 0;
12793ae03f26SNamhyung Kim }
12803ae03f26SNamhyung Kim
12819b5e350cSHitoshi Mitake /* lock oriented handlers */
12829b5e350cSHitoshi Mitake /* TODO: handlers for CPU oriented, thread oriented */
128359f411b6SIngo Molnar static struct trace_lock_handler report_lock_ops = {
128459f411b6SIngo Molnar .acquire_event = report_lock_acquire_event,
128559f411b6SIngo Molnar .acquired_event = report_lock_acquired_event,
128659f411b6SIngo Molnar .contended_event = report_lock_contended_event,
128759f411b6SIngo Molnar .release_event = report_lock_release_event,
12883ae03f26SNamhyung Kim .contention_begin_event = report_lock_contention_begin_event,
12893ae03f26SNamhyung Kim .contention_end_event = report_lock_contention_end_event,
12909b5e350cSHitoshi Mitake };
12919b5e350cSHitoshi Mitake
1292528b9cabSNamhyung Kim static struct trace_lock_handler contention_lock_ops = {
1293528b9cabSNamhyung Kim .contention_begin_event = report_lock_contention_begin_event,
1294528b9cabSNamhyung Kim .contention_end_event = report_lock_contention_end_event,
1295528b9cabSNamhyung Kim };
1296528b9cabSNamhyung Kim
1297528b9cabSNamhyung Kim
12989b5e350cSHitoshi Mitake static struct trace_lock_handler *trace_handler;
12999b5e350cSHitoshi Mitake
evsel__process_lock_acquire(struct evsel * evsel,struct perf_sample * sample)13003d655813SArnaldo Carvalho de Melo static int evsel__process_lock_acquire(struct evsel *evsel, struct perf_sample *sample)
13019b5e350cSHitoshi Mitake {
130259f411b6SIngo Molnar if (trace_handler->acquire_event)
1303746f16ecSArnaldo Carvalho de Melo return trace_handler->acquire_event(evsel, sample);
1304746f16ecSArnaldo Carvalho de Melo return 0;
13059b5e350cSHitoshi Mitake }
13069b5e350cSHitoshi Mitake
evsel__process_lock_acquired(struct evsel * evsel,struct perf_sample * sample)13073d655813SArnaldo Carvalho de Melo static int evsel__process_lock_acquired(struct evsel *evsel, struct perf_sample *sample)
13089b5e350cSHitoshi Mitake {
130933d6aef5SDavid Ahern if (trace_handler->acquired_event)
1310746f16ecSArnaldo Carvalho de Melo return trace_handler->acquired_event(evsel, sample);
1311746f16ecSArnaldo Carvalho de Melo return 0;
13129b5e350cSHitoshi Mitake }
13139b5e350cSHitoshi Mitake
evsel__process_lock_contended(struct evsel * evsel,struct perf_sample * sample)13143d655813SArnaldo Carvalho de Melo static int evsel__process_lock_contended(struct evsel *evsel, struct perf_sample *sample)
13159b5e350cSHitoshi Mitake {
131633d6aef5SDavid Ahern if (trace_handler->contended_event)
1317746f16ecSArnaldo Carvalho de Melo return trace_handler->contended_event(evsel, sample);
1318746f16ecSArnaldo Carvalho de Melo return 0;
13199b5e350cSHitoshi Mitake }
13209b5e350cSHitoshi Mitake
evsel__process_lock_release(struct evsel * evsel,struct perf_sample * sample)13213d655813SArnaldo Carvalho de Melo static int evsel__process_lock_release(struct evsel *evsel, struct perf_sample *sample)
13229b5e350cSHitoshi Mitake {
132333d6aef5SDavid Ahern if (trace_handler->release_event)
1324746f16ecSArnaldo Carvalho de Melo return trace_handler->release_event(evsel, sample);
1325746f16ecSArnaldo Carvalho de Melo return 0;
13269b5e350cSHitoshi Mitake }
13279b5e350cSHitoshi Mitake
evsel__process_contention_begin(struct evsel * evsel,struct perf_sample * sample)1328166a9764SNamhyung Kim static int evsel__process_contention_begin(struct evsel *evsel, struct perf_sample *sample)
1329166a9764SNamhyung Kim {
1330166a9764SNamhyung Kim if (trace_handler->contention_begin_event)
1331166a9764SNamhyung Kim return trace_handler->contention_begin_event(evsel, sample);
1332166a9764SNamhyung Kim return 0;
1333166a9764SNamhyung Kim }
1334166a9764SNamhyung Kim
evsel__process_contention_end(struct evsel * evsel,struct perf_sample * sample)1335166a9764SNamhyung Kim static int evsel__process_contention_end(struct evsel *evsel, struct perf_sample *sample)
1336166a9764SNamhyung Kim {
1337166a9764SNamhyung Kim if (trace_handler->contention_end_event)
1338166a9764SNamhyung Kim return trace_handler->contention_end_event(evsel, sample);
1339166a9764SNamhyung Kim return 0;
1340166a9764SNamhyung Kim }
1341166a9764SNamhyung Kim
print_bad_events(int bad,int total)134210350ec3SFrederic Weisbecker static void print_bad_events(int bad, int total)
134310350ec3SFrederic Weisbecker {
134410350ec3SFrederic Weisbecker /* Output for debug, this have to be removed */
134510350ec3SFrederic Weisbecker int i;
13469565c918SNamhyung Kim int broken = 0;
134710350ec3SFrederic Weisbecker const char *name[4] =
134810350ec3SFrederic Weisbecker { "acquire", "acquired", "contended", "release" };
134910350ec3SFrederic Weisbecker
13509565c918SNamhyung Kim for (i = 0; i < BROKEN_MAX; i++)
13519565c918SNamhyung Kim broken += bad_hist[i];
13529565c918SNamhyung Kim
135335bf007eSNamhyung Kim if (quiet || total == 0 || (broken == 0 && verbose <= 0))
13549565c918SNamhyung Kim return;
13559565c918SNamhyung Kim
1356f6027053SNamhyung Kim fprintf(lock_output, "\n=== output for debug ===\n\n");
1357f6027053SNamhyung Kim fprintf(lock_output, "bad: %d, total: %d\n", bad, total);
1358f6027053SNamhyung Kim fprintf(lock_output, "bad rate: %.2f %%\n", (double)bad / (double)total * 100);
1359f6027053SNamhyung Kim fprintf(lock_output, "histogram of events caused bad sequence\n");
136010350ec3SFrederic Weisbecker for (i = 0; i < BROKEN_MAX; i++)
1361f6027053SNamhyung Kim fprintf(lock_output, " %10s: %d\n", name[i], bad_hist[i]);
136210350ec3SFrederic Weisbecker }
136310350ec3SFrederic Weisbecker
13649b5e350cSHitoshi Mitake /* TODO: various way to print, coloring, nano or milli sec */
print_result(void)13659b5e350cSHitoshi Mitake static void print_result(void)
13669b5e350cSHitoshi Mitake {
13679b5e350cSHitoshi Mitake struct lock_stat *st;
136864999e44SNamhyung Kim struct lock_key *key;
13699b5e350cSHitoshi Mitake char cut_name[20];
13706282a1f4SNamhyung Kim int bad, total, printed;
13719b5e350cSHitoshi Mitake
13726bbc4820SNamhyung Kim if (!quiet) {
1373f6027053SNamhyung Kim fprintf(lock_output, "%20s ", "Name");
137464999e44SNamhyung Kim list_for_each_entry(key, &lock_keys, list)
1375f6027053SNamhyung Kim fprintf(lock_output, "%*s ", key->len, key->header);
1376f6027053SNamhyung Kim fprintf(lock_output, "\n\n");
13776bbc4820SNamhyung Kim }
13789b5e350cSHitoshi Mitake
13796282a1f4SNamhyung Kim bad = total = printed = 0;
13809b5e350cSHitoshi Mitake while ((st = pop_from_result())) {
1381e4cef1f6SHitoshi Mitake total++;
138279d9333bSNamhyung Kim if (st->broken)
1383e4cef1f6SHitoshi Mitake bad++;
138479d9333bSNamhyung Kim if (!st->nr_acquired)
1385e4cef1f6SHitoshi Mitake continue;
138679d9333bSNamhyung Kim
13879b5e350cSHitoshi Mitake bzero(cut_name, 20);
13889b5e350cSHitoshi Mitake
1389ba8a56c7SNamhyung Kim if (strlen(st->name) < 20) {
13909b5e350cSHitoshi Mitake /* output raw name */
13917c3bcbdfSNamhyung Kim const char *name = st->name;
13927c3bcbdfSNamhyung Kim
13937c3bcbdfSNamhyung Kim if (show_thread_stats) {
13947c3bcbdfSNamhyung Kim struct thread *t;
13957c3bcbdfSNamhyung Kim
13967c3bcbdfSNamhyung Kim /* st->addr contains tid of thread */
13977c3bcbdfSNamhyung Kim t = perf_session__findnew(session, st->addr);
13987c3bcbdfSNamhyung Kim name = thread__comm_str(t);
13997c3bcbdfSNamhyung Kim }
14007c3bcbdfSNamhyung Kim
1401f6027053SNamhyung Kim fprintf(lock_output, "%20s ", name);
14029b5e350cSHitoshi Mitake } else {
14039b5e350cSHitoshi Mitake strncpy(cut_name, st->name, 16);
14049b5e350cSHitoshi Mitake cut_name[16] = '.';
14059b5e350cSHitoshi Mitake cut_name[17] = '.';
14069b5e350cSHitoshi Mitake cut_name[18] = '.';
14079b5e350cSHitoshi Mitake cut_name[19] = '\0';
14089b5e350cSHitoshi Mitake /* cut off name for saving output style */
1409f6027053SNamhyung Kim fprintf(lock_output, "%20s ", cut_name);
14109b5e350cSHitoshi Mitake }
14119b5e350cSHitoshi Mitake
141264999e44SNamhyung Kim list_for_each_entry(key, &lock_keys, list) {
141364999e44SNamhyung Kim key->print(key, st);
1414f6027053SNamhyung Kim fprintf(lock_output, " ");
141564999e44SNamhyung Kim }
1416f6027053SNamhyung Kim fprintf(lock_output, "\n");
14176282a1f4SNamhyung Kim
14186282a1f4SNamhyung Kim if (++printed >= print_nr_entries)
14196282a1f4SNamhyung Kim break;
14209b5e350cSHitoshi Mitake }
1421e4cef1f6SHitoshi Mitake
142210350ec3SFrederic Weisbecker print_bad_events(bad, total);
14239b5e350cSHitoshi Mitake }
14249b5e350cSHitoshi Mitake
14258035458fSArnaldo Carvalho de Melo static bool info_threads, info_map;
142626242d85SHitoshi Mitake
dump_threads(void)142726242d85SHitoshi Mitake static void dump_threads(void)
142826242d85SHitoshi Mitake {
142926242d85SHitoshi Mitake struct thread_stat *st;
143026242d85SHitoshi Mitake struct rb_node *node;
143126242d85SHitoshi Mitake struct thread *t;
143226242d85SHitoshi Mitake
1433f6027053SNamhyung Kim fprintf(lock_output, "%10s: comm\n", "Thread ID");
143426242d85SHitoshi Mitake
143526242d85SHitoshi Mitake node = rb_first(&thread_stats);
143626242d85SHitoshi Mitake while (node) {
143726242d85SHitoshi Mitake st = container_of(node, struct thread_stat, rb);
143826242d85SHitoshi Mitake t = perf_session__findnew(session, st->tid);
1439f6027053SNamhyung Kim fprintf(lock_output, "%10d: %s\n", st->tid, thread__comm_str(t));
144026242d85SHitoshi Mitake node = rb_next(node);
1441b91fc39fSArnaldo Carvalho de Melo thread__put(t);
14428284bbeaSZou Wei }
144326242d85SHitoshi Mitake }
144426242d85SHitoshi Mitake
compare_maps(struct lock_stat * a,struct lock_stat * b)1445f4cf2d75SNamhyung Kim static int compare_maps(struct lock_stat *a, struct lock_stat *b)
1446f4cf2d75SNamhyung Kim {
1447f4cf2d75SNamhyung Kim int ret;
1448f4cf2d75SNamhyung Kim
1449f4cf2d75SNamhyung Kim if (a->name && b->name)
1450f4cf2d75SNamhyung Kim ret = strcmp(a->name, b->name);
1451f4cf2d75SNamhyung Kim else
1452f4cf2d75SNamhyung Kim ret = !!a->name - !!b->name;
1453f4cf2d75SNamhyung Kim
1454f4cf2d75SNamhyung Kim if (!ret)
1455f4cf2d75SNamhyung Kim return a->addr < b->addr;
1456f4cf2d75SNamhyung Kim else
1457f4cf2d75SNamhyung Kim return ret < 0;
1458f4cf2d75SNamhyung Kim }
1459f4cf2d75SNamhyung Kim
dump_map(void)14609b5e350cSHitoshi Mitake static void dump_map(void)
14619b5e350cSHitoshi Mitake {
14629b5e350cSHitoshi Mitake unsigned int i;
14639b5e350cSHitoshi Mitake struct lock_stat *st;
14649b5e350cSHitoshi Mitake
1465f6027053SNamhyung Kim fprintf(lock_output, "Address of instance: name of class\n");
14669b5e350cSHitoshi Mitake for (i = 0; i < LOCKHASH_SIZE; i++) {
14677672d00aSNamhyung Kim hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {
1468f4cf2d75SNamhyung Kim insert_to_result(st, compare_maps);
1469f4cf2d75SNamhyung Kim }
1470f4cf2d75SNamhyung Kim }
1471f4cf2d75SNamhyung Kim
1472f4cf2d75SNamhyung Kim while ((st = pop_from_result()))
1473f6027053SNamhyung Kim fprintf(lock_output, " %#llx: %s\n", (unsigned long long)st->addr, st->name);
14749b5e350cSHitoshi Mitake }
14759b5e350cSHitoshi Mitake
dump_info(void)147633d6aef5SDavid Ahern static int dump_info(void)
147726242d85SHitoshi Mitake {
147833d6aef5SDavid Ahern int rc = 0;
147933d6aef5SDavid Ahern
148026242d85SHitoshi Mitake if (info_threads)
148126242d85SHitoshi Mitake dump_threads();
148226242d85SHitoshi Mitake else if (info_map)
148326242d85SHitoshi Mitake dump_map();
148433d6aef5SDavid Ahern else {
148533d6aef5SDavid Ahern rc = -1;
148633d6aef5SDavid Ahern pr_err("Unknown type of information\n");
148733d6aef5SDavid Ahern }
148833d6aef5SDavid Ahern
148933d6aef5SDavid Ahern return rc;
149026242d85SHitoshi Mitake }
149126242d85SHitoshi Mitake
149230b331d2SNamhyung Kim static const struct evsel_str_handler lock_tracepoints[] = {
149330b331d2SNamhyung Kim { "lock:lock_acquire", evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */
149430b331d2SNamhyung Kim { "lock:lock_acquired", evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
149530b331d2SNamhyung Kim { "lock:lock_contended", evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
149630b331d2SNamhyung Kim { "lock:lock_release", evsel__process_lock_release, }, /* CONFIG_LOCKDEP */
149730b331d2SNamhyung Kim };
149830b331d2SNamhyung Kim
149930b331d2SNamhyung Kim static const struct evsel_str_handler contention_tracepoints[] = {
150030b331d2SNamhyung Kim { "lock:contention_begin", evsel__process_contention_begin, },
150130b331d2SNamhyung Kim { "lock:contention_end", evsel__process_contention_end, },
150230b331d2SNamhyung Kim };
150330b331d2SNamhyung Kim
process_event_update(struct perf_tool * tool,union perf_event * event,struct evlist ** pevlist)150430b331d2SNamhyung Kim static int process_event_update(struct perf_tool *tool,
150530b331d2SNamhyung Kim union perf_event *event,
150630b331d2SNamhyung Kim struct evlist **pevlist)
150730b331d2SNamhyung Kim {
150830b331d2SNamhyung Kim int ret;
150930b331d2SNamhyung Kim
151030b331d2SNamhyung Kim ret = perf_event__process_event_update(tool, event, pevlist);
151130b331d2SNamhyung Kim if (ret < 0)
151230b331d2SNamhyung Kim return ret;
151330b331d2SNamhyung Kim
151430b331d2SNamhyung Kim /* this can return -EEXIST since we call it for each evsel */
151530b331d2SNamhyung Kim perf_session__set_tracepoints_handlers(session, lock_tracepoints);
151630b331d2SNamhyung Kim perf_session__set_tracepoints_handlers(session, contention_tracepoints);
151730b331d2SNamhyung Kim return 0;
151830b331d2SNamhyung Kim }
151930b331d2SNamhyung Kim
152032dcd021SJiri Olsa typedef int (*tracepoint_handler)(struct evsel *evsel,
1521746f16ecSArnaldo Carvalho de Melo struct perf_sample *sample);
1522746f16ecSArnaldo Carvalho de Melo
process_sample_event(struct perf_tool * tool __maybe_unused,union perf_event * event,struct perf_sample * sample,struct evsel * evsel,struct machine * machine)15231d037ca1SIrina Tirdea static int process_sample_event(struct perf_tool *tool __maybe_unused,
1524d20deb64SArnaldo Carvalho de Melo union perf_event *event,
15259e69c210SArnaldo Carvalho de Melo struct perf_sample *sample,
152632dcd021SJiri Olsa struct evsel *evsel,
1527743eb868SArnaldo Carvalho de Melo struct machine *machine)
1528c61e52eeSFrederic Weisbecker {
1529b91fc39fSArnaldo Carvalho de Melo int err = 0;
1530314add6bSAdrian Hunter struct thread *thread = machine__findnew_thread(machine, sample->pid,
1531314add6bSAdrian Hunter sample->tid);
1532c61e52eeSFrederic Weisbecker
1533c61e52eeSFrederic Weisbecker if (thread == NULL) {
1534c61e52eeSFrederic Weisbecker pr_debug("problem processing %d event, skipping it.\n",
15358115d60cSArnaldo Carvalho de Melo event->header.type);
1536c61e52eeSFrederic Weisbecker return -1;
1537c61e52eeSFrederic Weisbecker }
1538c61e52eeSFrederic Weisbecker
1539744a9719SArnaldo Carvalho de Melo if (evsel->handler != NULL) {
1540744a9719SArnaldo Carvalho de Melo tracepoint_handler f = evsel->handler;
1541b91fc39fSArnaldo Carvalho de Melo err = f(evsel, sample);
1542746f16ecSArnaldo Carvalho de Melo }
1543746f16ecSArnaldo Carvalho de Melo
1544b91fc39fSArnaldo Carvalho de Melo thread__put(thread);
1545b91fc39fSArnaldo Carvalho de Melo
1546b91fc39fSArnaldo Carvalho de Melo return err;
1547c61e52eeSFrederic Weisbecker }
1548c61e52eeSFrederic Weisbecker
combine_result(void)15490d435bf8SNamhyung Kim static void combine_result(void)
15500d435bf8SNamhyung Kim {
15510d435bf8SNamhyung Kim unsigned int i;
15520d435bf8SNamhyung Kim struct lock_stat *st;
15530d435bf8SNamhyung Kim
15540d435bf8SNamhyung Kim if (!combine_locks)
15550d435bf8SNamhyung Kim return;
15560d435bf8SNamhyung Kim
15570d435bf8SNamhyung Kim for (i = 0; i < LOCKHASH_SIZE; i++) {
15580d435bf8SNamhyung Kim hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {
15590d435bf8SNamhyung Kim combine_lock_stats(st);
15600d435bf8SNamhyung Kim }
15610d435bf8SNamhyung Kim }
15620d435bf8SNamhyung Kim }
15630d435bf8SNamhyung Kim
sort_result(void)15649b5e350cSHitoshi Mitake static void sort_result(void)
15659b5e350cSHitoshi Mitake {
15669b5e350cSHitoshi Mitake unsigned int i;
15679b5e350cSHitoshi Mitake struct lock_stat *st;
15689b5e350cSHitoshi Mitake
15699b5e350cSHitoshi Mitake for (i = 0; i < LOCKHASH_SIZE; i++) {
15707672d00aSNamhyung Kim hlist_for_each_entry(st, &lockhash_table[i], hash_entry) {
15719b5e350cSHitoshi Mitake insert_to_result(st, compare);
15729b5e350cSHitoshi Mitake }
15739b5e350cSHitoshi Mitake }
15749b5e350cSHitoshi Mitake }
15759b5e350cSHitoshi Mitake
1576528b9cabSNamhyung Kim static const struct {
1577528b9cabSNamhyung Kim unsigned int flags;
15784f701063SNamhyung Kim const char *str;
1579528b9cabSNamhyung Kim const char *name;
158059119c09SNamhyung Kim } lock_type_table[] = {
15814f701063SNamhyung Kim { 0, "semaphore", "semaphore" },
15824f701063SNamhyung Kim { LCB_F_SPIN, "spinlock", "spinlock" },
15834f701063SNamhyung Kim { LCB_F_SPIN | LCB_F_READ, "rwlock:R", "rwlock" },
15844f701063SNamhyung Kim { LCB_F_SPIN | LCB_F_WRITE, "rwlock:W", "rwlock" },
15854f701063SNamhyung Kim { LCB_F_READ, "rwsem:R", "rwsem" },
15864f701063SNamhyung Kim { LCB_F_WRITE, "rwsem:W", "rwsem" },
1587d783ea8fSNamhyung Kim { LCB_F_RT, "rt-mutex", "rt-mutex" },
15884f701063SNamhyung Kim { LCB_F_RT | LCB_F_READ, "rwlock-rt:R", "rwlock-rt" },
15894f701063SNamhyung Kim { LCB_F_RT | LCB_F_WRITE, "rwlock-rt:W", "rwlock-rt" },
15904f701063SNamhyung Kim { LCB_F_PERCPU | LCB_F_READ, "pcpu-sem:R", "percpu-rwsem" },
15914f701063SNamhyung Kim { LCB_F_PERCPU | LCB_F_WRITE, "pcpu-sem:W", "percpu-rwsem" },
15924f701063SNamhyung Kim { LCB_F_MUTEX, "mutex", "mutex" },
15934f701063SNamhyung Kim { LCB_F_MUTEX | LCB_F_SPIN, "mutex", "mutex" },
1594b4a7eff9SNamhyung Kim /* alias for get_type_flag() */
15954f701063SNamhyung Kim { LCB_F_MUTEX | LCB_F_SPIN, "mutex-spin", "mutex" },
1596528b9cabSNamhyung Kim };
1597528b9cabSNamhyung Kim
get_type_str(unsigned int flags)159859119c09SNamhyung Kim static const char *get_type_str(unsigned int flags)
159959119c09SNamhyung Kim {
16004f701063SNamhyung Kim flags &= LCB_F_MAX_FLAGS - 1;
16014f701063SNamhyung Kim
16024f701063SNamhyung Kim for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {
16034f701063SNamhyung Kim if (lock_type_table[i].flags == flags)
16044f701063SNamhyung Kim return lock_type_table[i].str;
16054f701063SNamhyung Kim }
16064f701063SNamhyung Kim return "unknown";
16074f701063SNamhyung Kim }
16084f701063SNamhyung Kim
get_type_name(unsigned int flags)16094f701063SNamhyung Kim static const char *get_type_name(unsigned int flags)
16104f701063SNamhyung Kim {
16114f701063SNamhyung Kim flags &= LCB_F_MAX_FLAGS - 1;
16124f701063SNamhyung Kim
161359119c09SNamhyung Kim for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {
161459119c09SNamhyung Kim if (lock_type_table[i].flags == flags)
161559119c09SNamhyung Kim return lock_type_table[i].name;
1616528b9cabSNamhyung Kim }
1617528b9cabSNamhyung Kim return "unknown";
1618528b9cabSNamhyung Kim }
1619528b9cabSNamhyung Kim
get_type_flag(const char * str)1620b4a7eff9SNamhyung Kim static unsigned int get_type_flag(const char *str)
1621b4a7eff9SNamhyung Kim {
1622b4a7eff9SNamhyung Kim for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {
1623b4a7eff9SNamhyung Kim if (!strcmp(lock_type_table[i].name, str))
1624b4a7eff9SNamhyung Kim return lock_type_table[i].flags;
1625b4a7eff9SNamhyung Kim }
1626d783ea8fSNamhyung Kim for (unsigned int i = 0; i < ARRAY_SIZE(lock_type_table); i++) {
1627d783ea8fSNamhyung Kim if (!strcmp(lock_type_table[i].str, str))
1628d783ea8fSNamhyung Kim return lock_type_table[i].flags;
1629d783ea8fSNamhyung Kim }
1630b4a7eff9SNamhyung Kim return UINT_MAX;
1631b4a7eff9SNamhyung Kim }
1632b4a7eff9SNamhyung Kim
lock_filter_finish(void)1633b4a7eff9SNamhyung Kim static void lock_filter_finish(void)
1634b4a7eff9SNamhyung Kim {
1635b4a7eff9SNamhyung Kim zfree(&filters.types);
1636b4a7eff9SNamhyung Kim filters.nr_types = 0;
1637511e19b9SNamhyung Kim
1638511e19b9SNamhyung Kim zfree(&filters.addrs);
1639511e19b9SNamhyung Kim filters.nr_addrs = 0;
1640511e19b9SNamhyung Kim
1641511e19b9SNamhyung Kim for (int i = 0; i < filters.nr_syms; i++)
1642511e19b9SNamhyung Kim free(filters.syms[i]);
1643511e19b9SNamhyung Kim
1644511e19b9SNamhyung Kim zfree(&filters.syms);
1645511e19b9SNamhyung Kim filters.nr_syms = 0;
1646b4a7eff9SNamhyung Kim }
1647b4a7eff9SNamhyung Kim
sort_contention_result(void)1648528b9cabSNamhyung Kim static void sort_contention_result(void)
1649528b9cabSNamhyung Kim {
1650528b9cabSNamhyung Kim sort_result();
1651528b9cabSNamhyung Kim }
1652528b9cabSNamhyung Kim
print_header_stdio(void)165369c5c993SNamhyung Kim static void print_header_stdio(void)
165484c3a2bbSNamhyung Kim {
1655528b9cabSNamhyung Kim struct lock_key *key;
1656528b9cabSNamhyung Kim
1657528b9cabSNamhyung Kim list_for_each_entry(key, &lock_keys, list)
1658f6027053SNamhyung Kim fprintf(lock_output, "%*s ", key->len, key->header);
1659528b9cabSNamhyung Kim
1660688d2e8dSNamhyung Kim switch (aggr_mode) {
1661688d2e8dSNamhyung Kim case LOCK_AGGR_TASK:
1662f6027053SNamhyung Kim fprintf(lock_output, " %10s %s\n\n", "pid",
16633477f079SNamhyung Kim show_lock_owner ? "owner" : "comm");
1664688d2e8dSNamhyung Kim break;
1665688d2e8dSNamhyung Kim case LOCK_AGGR_CALLER:
1666f6027053SNamhyung Kim fprintf(lock_output, " %10s %s\n\n", "type", "caller");
1667688d2e8dSNamhyung Kim break;
1668688d2e8dSNamhyung Kim case LOCK_AGGR_ADDR:
1669f6027053SNamhyung Kim fprintf(lock_output, " %16s %s\n\n", "address", "symbol");
1670688d2e8dSNamhyung Kim break;
1671688d2e8dSNamhyung Kim default:
1672688d2e8dSNamhyung Kim break;
1673688d2e8dSNamhyung Kim }
16746bbc4820SNamhyung Kim }
1675528b9cabSNamhyung Kim
print_header_csv(const char * sep)167669c5c993SNamhyung Kim static void print_header_csv(const char *sep)
167769c5c993SNamhyung Kim {
167869c5c993SNamhyung Kim struct lock_key *key;
16796d499a6bSNamhyung Kim
1680f6027053SNamhyung Kim fprintf(lock_output, "# output: ");
168169c5c993SNamhyung Kim list_for_each_entry(key, &lock_keys, list)
1682f6027053SNamhyung Kim fprintf(lock_output, "%s%s ", key->header, sep);
168369c5c993SNamhyung Kim
168469c5c993SNamhyung Kim switch (aggr_mode) {
168569c5c993SNamhyung Kim case LOCK_AGGR_TASK:
1686f6027053SNamhyung Kim fprintf(lock_output, "%s%s %s\n", "pid", sep,
168769c5c993SNamhyung Kim show_lock_owner ? "owner" : "comm");
168869c5c993SNamhyung Kim break;
168969c5c993SNamhyung Kim case LOCK_AGGR_CALLER:
1690f6027053SNamhyung Kim fprintf(lock_output, "%s%s %s", "type", sep, "caller");
169169c5c993SNamhyung Kim if (verbose > 0)
1692f6027053SNamhyung Kim fprintf(lock_output, "%s %s", sep, "stacktrace");
1693f6027053SNamhyung Kim fprintf(lock_output, "\n");
169469c5c993SNamhyung Kim break;
169569c5c993SNamhyung Kim case LOCK_AGGR_ADDR:
1696f6027053SNamhyung Kim fprintf(lock_output, "%s%s %s%s %s\n", "address", sep, "symbol", sep, "type");
169769c5c993SNamhyung Kim break;
169869c5c993SNamhyung Kim default:
169969c5c993SNamhyung Kim break;
170069c5c993SNamhyung Kim }
170169c5c993SNamhyung Kim }
170269c5c993SNamhyung Kim
print_header(void)170369c5c993SNamhyung Kim static void print_header(void)
170469c5c993SNamhyung Kim {
170569c5c993SNamhyung Kim if (!quiet) {
170669c5c993SNamhyung Kim if (symbol_conf.field_sep)
170769c5c993SNamhyung Kim print_header_csv(symbol_conf.field_sep);
170869c5c993SNamhyung Kim else
170969c5c993SNamhyung Kim print_header_stdio();
171069c5c993SNamhyung Kim }
171169c5c993SNamhyung Kim }
171269c5c993SNamhyung Kim
print_lock_stat_stdio(struct lock_contention * con,struct lock_stat * st)171369c5c993SNamhyung Kim static void print_lock_stat_stdio(struct lock_contention *con, struct lock_stat *st)
171469c5c993SNamhyung Kim {
171569c5c993SNamhyung Kim struct lock_key *key;
1716688d2e8dSNamhyung Kim struct thread *t;
1717688d2e8dSNamhyung Kim int pid;
1718688d2e8dSNamhyung Kim
1719528b9cabSNamhyung Kim list_for_each_entry(key, &lock_keys, list) {
1720528b9cabSNamhyung Kim key->print(key, st);
1721f6027053SNamhyung Kim fprintf(lock_output, " ");
1722528b9cabSNamhyung Kim }
1723528b9cabSNamhyung Kim
1724688d2e8dSNamhyung Kim switch (aggr_mode) {
1725688d2e8dSNamhyung Kim case LOCK_AGGR_CALLER:
1726f6027053SNamhyung Kim fprintf(lock_output, " %10s %s\n", get_type_str(st->flags), st->name);
1727688d2e8dSNamhyung Kim break;
1728688d2e8dSNamhyung Kim case LOCK_AGGR_TASK:
1729688d2e8dSNamhyung Kim pid = st->addr;
17301ab55323SNamhyung Kim t = perf_session__findnew(session, pid);
1731f6027053SNamhyung Kim fprintf(lock_output, " %10d %s\n",
17323477f079SNamhyung Kim pid, pid == -1 ? "Unknown" : thread__comm_str(t));
1733688d2e8dSNamhyung Kim break;
1734688d2e8dSNamhyung Kim case LOCK_AGGR_ADDR:
1735f6027053SNamhyung Kim fprintf(lock_output, " %016llx %s (%s)\n", (unsigned long long)st->addr,
17364f701063SNamhyung Kim st->name, get_type_name(st->flags));
1737688d2e8dSNamhyung Kim break;
1738688d2e8dSNamhyung Kim default:
1739688d2e8dSNamhyung Kim break;
17401ab55323SNamhyung Kim }
17411ab55323SNamhyung Kim
17427c0a6144SYang Jihong if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {
1743a6eaf966SNamhyung Kim struct map *kmap;
1744a6eaf966SNamhyung Kim struct symbol *sym;
1745a6eaf966SNamhyung Kim char buf[128];
1746a6eaf966SNamhyung Kim u64 ip;
1747a6eaf966SNamhyung Kim
174896532a83SNamhyung Kim for (int i = 0; i < max_stack_depth; i++) {
1749a6eaf966SNamhyung Kim if (!st->callstack || !st->callstack[i])
1750a6eaf966SNamhyung Kim break;
1751a6eaf966SNamhyung Kim
1752a6eaf966SNamhyung Kim ip = st->callstack[i];
1753a6eaf966SNamhyung Kim sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
1754a6eaf966SNamhyung Kim get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
1755f6027053SNamhyung Kim fprintf(lock_output, "\t\t\t%#lx %s\n", (unsigned long)ip, buf);
1756a6eaf966SNamhyung Kim }
1757a6eaf966SNamhyung Kim }
175869c5c993SNamhyung Kim }
175969c5c993SNamhyung Kim
print_lock_stat_csv(struct lock_contention * con,struct lock_stat * st,const char * sep)176069c5c993SNamhyung Kim static void print_lock_stat_csv(struct lock_contention *con, struct lock_stat *st,
176169c5c993SNamhyung Kim const char *sep)
176269c5c993SNamhyung Kim {
176369c5c993SNamhyung Kim struct lock_key *key;
176469c5c993SNamhyung Kim struct thread *t;
176569c5c993SNamhyung Kim int pid;
176669c5c993SNamhyung Kim
176769c5c993SNamhyung Kim list_for_each_entry(key, &lock_keys, list) {
176869c5c993SNamhyung Kim key->print(key, st);
1769f6027053SNamhyung Kim fprintf(lock_output, "%s ", sep);
177069c5c993SNamhyung Kim }
177169c5c993SNamhyung Kim
177269c5c993SNamhyung Kim switch (aggr_mode) {
177369c5c993SNamhyung Kim case LOCK_AGGR_CALLER:
1774f6027053SNamhyung Kim fprintf(lock_output, "%s%s %s", get_type_str(st->flags), sep, st->name);
177569c5c993SNamhyung Kim if (verbose <= 0)
1776f6027053SNamhyung Kim fprintf(lock_output, "\n");
177769c5c993SNamhyung Kim break;
177869c5c993SNamhyung Kim case LOCK_AGGR_TASK:
177969c5c993SNamhyung Kim pid = st->addr;
178069c5c993SNamhyung Kim t = perf_session__findnew(session, pid);
1781f6027053SNamhyung Kim fprintf(lock_output, "%d%s %s\n", pid, sep,
1782f6027053SNamhyung Kim pid == -1 ? "Unknown" : thread__comm_str(t));
178369c5c993SNamhyung Kim break;
178469c5c993SNamhyung Kim case LOCK_AGGR_ADDR:
1785f6027053SNamhyung Kim fprintf(lock_output, "%llx%s %s%s %s\n", (unsigned long long)st->addr, sep,
178669c5c993SNamhyung Kim st->name, sep, get_type_name(st->flags));
178769c5c993SNamhyung Kim break;
178869c5c993SNamhyung Kim default:
178969c5c993SNamhyung Kim break;
179069c5c993SNamhyung Kim }
179169c5c993SNamhyung Kim
179269c5c993SNamhyung Kim if (aggr_mode == LOCK_AGGR_CALLER && verbose > 0) {
179369c5c993SNamhyung Kim struct map *kmap;
179469c5c993SNamhyung Kim struct symbol *sym;
179569c5c993SNamhyung Kim char buf[128];
179669c5c993SNamhyung Kim u64 ip;
179769c5c993SNamhyung Kim
179869c5c993SNamhyung Kim for (int i = 0; i < max_stack_depth; i++) {
179969c5c993SNamhyung Kim if (!st->callstack || !st->callstack[i])
180069c5c993SNamhyung Kim break;
180169c5c993SNamhyung Kim
180269c5c993SNamhyung Kim ip = st->callstack[i];
180369c5c993SNamhyung Kim sym = machine__find_kernel_symbol(con->machine, ip, &kmap);
180469c5c993SNamhyung Kim get_symbol_name_offset(kmap, sym, ip, buf, sizeof(buf));
1805f6027053SNamhyung Kim fprintf(lock_output, "%s %#lx %s", i ? ":" : sep, (unsigned long) ip, buf);
180669c5c993SNamhyung Kim }
1807f6027053SNamhyung Kim fprintf(lock_output, "\n");
180869c5c993SNamhyung Kim }
180969c5c993SNamhyung Kim }
181069c5c993SNamhyung Kim
print_lock_stat(struct lock_contention * con,struct lock_stat * st)181169c5c993SNamhyung Kim static void print_lock_stat(struct lock_contention *con, struct lock_stat *st)
181269c5c993SNamhyung Kim {
181369c5c993SNamhyung Kim if (symbol_conf.field_sep)
181469c5c993SNamhyung Kim print_lock_stat_csv(con, st, symbol_conf.field_sep);
181569c5c993SNamhyung Kim else
181669c5c993SNamhyung Kim print_lock_stat_stdio(con, st);
181769c5c993SNamhyung Kim }
181869c5c993SNamhyung Kim
print_footer_stdio(int total,int bad,struct lock_contention_fails * fails)181969c5c993SNamhyung Kim static void print_footer_stdio(int total, int bad, struct lock_contention_fails *fails)
182069c5c993SNamhyung Kim {
182169c5c993SNamhyung Kim /* Output for debug, this have to be removed */
182269c5c993SNamhyung Kim int broken = fails->task + fails->stack + fails->time + fails->data;
182369c5c993SNamhyung Kim
182469c5c993SNamhyung Kim if (!use_bpf)
182569c5c993SNamhyung Kim print_bad_events(bad, total);
182669c5c993SNamhyung Kim
182769c5c993SNamhyung Kim if (quiet || total == 0 || (broken == 0 && verbose <= 0))
182869c5c993SNamhyung Kim return;
182969c5c993SNamhyung Kim
183069c5c993SNamhyung Kim total += broken;
1831f6027053SNamhyung Kim fprintf(lock_output, "\n=== output for debug ===\n\n");
1832f6027053SNamhyung Kim fprintf(lock_output, "bad: %d, total: %d\n", broken, total);
1833f6027053SNamhyung Kim fprintf(lock_output, "bad rate: %.2f %%\n", 100.0 * broken / total);
183469c5c993SNamhyung Kim
1835f6027053SNamhyung Kim fprintf(lock_output, "histogram of failure reasons\n");
1836f6027053SNamhyung Kim fprintf(lock_output, " %10s: %d\n", "task", fails->task);
1837f6027053SNamhyung Kim fprintf(lock_output, " %10s: %d\n", "stack", fails->stack);
1838f6027053SNamhyung Kim fprintf(lock_output, " %10s: %d\n", "time", fails->time);
1839f6027053SNamhyung Kim fprintf(lock_output, " %10s: %d\n", "data", fails->data);
184069c5c993SNamhyung Kim }
184169c5c993SNamhyung Kim
print_footer_csv(int total,int bad,struct lock_contention_fails * fails,const char * sep)184269c5c993SNamhyung Kim static void print_footer_csv(int total, int bad, struct lock_contention_fails *fails,
184369c5c993SNamhyung Kim const char *sep)
184469c5c993SNamhyung Kim {
184569c5c993SNamhyung Kim /* Output for debug, this have to be removed */
184669c5c993SNamhyung Kim if (use_bpf)
184769c5c993SNamhyung Kim bad = fails->task + fails->stack + fails->time + fails->data;
184869c5c993SNamhyung Kim
184969c5c993SNamhyung Kim if (quiet || total == 0 || (bad == 0 && verbose <= 0))
185069c5c993SNamhyung Kim return;
185169c5c993SNamhyung Kim
185269c5c993SNamhyung Kim total += bad;
1853f6027053SNamhyung Kim fprintf(lock_output, "# debug: total=%d%s bad=%d", total, sep, bad);
185469c5c993SNamhyung Kim
185569c5c993SNamhyung Kim if (use_bpf) {
1856f6027053SNamhyung Kim fprintf(lock_output, "%s bad_%s=%d", sep, "task", fails->task);
1857f6027053SNamhyung Kim fprintf(lock_output, "%s bad_%s=%d", sep, "stack", fails->stack);
1858f6027053SNamhyung Kim fprintf(lock_output, "%s bad_%s=%d", sep, "time", fails->time);
1859f6027053SNamhyung Kim fprintf(lock_output, "%s bad_%s=%d", sep, "data", fails->data);
186069c5c993SNamhyung Kim } else {
186169c5c993SNamhyung Kim int i;
186269c5c993SNamhyung Kim const char *name[4] = { "acquire", "acquired", "contended", "release" };
186369c5c993SNamhyung Kim
186469c5c993SNamhyung Kim for (i = 0; i < BROKEN_MAX; i++)
1865f6027053SNamhyung Kim fprintf(lock_output, "%s bad_%s=%d", sep, name[i], bad_hist[i]);
186669c5c993SNamhyung Kim }
1867f6027053SNamhyung Kim fprintf(lock_output, "\n");
186869c5c993SNamhyung Kim }
186969c5c993SNamhyung Kim
print_footer(int total,int bad,struct lock_contention_fails * fails)187069c5c993SNamhyung Kim static void print_footer(int total, int bad, struct lock_contention_fails *fails)
187169c5c993SNamhyung Kim {
187269c5c993SNamhyung Kim if (symbol_conf.field_sep)
187369c5c993SNamhyung Kim print_footer_csv(total, bad, fails, symbol_conf.field_sep);
187469c5c993SNamhyung Kim else
187569c5c993SNamhyung Kim print_footer_stdio(total, bad, fails);
187669c5c993SNamhyung Kim }
187769c5c993SNamhyung Kim
print_contention_result(struct lock_contention * con)187869c5c993SNamhyung Kim static void print_contention_result(struct lock_contention *con)
187969c5c993SNamhyung Kim {
188069c5c993SNamhyung Kim struct lock_stat *st;
188169c5c993SNamhyung Kim int bad, total, printed;
188269c5c993SNamhyung Kim
188369c5c993SNamhyung Kim if (!quiet)
188469c5c993SNamhyung Kim print_header();
188569c5c993SNamhyung Kim
188669c5c993SNamhyung Kim bad = total = printed = 0;
188769c5c993SNamhyung Kim
188869c5c993SNamhyung Kim while ((st = pop_from_result())) {
188969c5c993SNamhyung Kim total += use_bpf ? st->nr_contended : 1;
189069c5c993SNamhyung Kim if (st->broken)
189169c5c993SNamhyung Kim bad++;
189269c5c993SNamhyung Kim
189369c5c993SNamhyung Kim if (!st->wait_time_total)
189469c5c993SNamhyung Kim continue;
189569c5c993SNamhyung Kim
189669c5c993SNamhyung Kim print_lock_stat(con, st);
18976282a1f4SNamhyung Kim
18986282a1f4SNamhyung Kim if (++printed >= print_nr_entries)
18996282a1f4SNamhyung Kim break;
1900528b9cabSNamhyung Kim }
1901528b9cabSNamhyung Kim
1902aae7e453SNamhyung Kim if (print_nr_entries) {
1903aae7e453SNamhyung Kim /* update the total/bad stats */
1904aae7e453SNamhyung Kim while ((st = pop_from_result())) {
1905aae7e453SNamhyung Kim total += use_bpf ? st->nr_contended : 1;
1906aae7e453SNamhyung Kim if (st->broken)
1907aae7e453SNamhyung Kim bad++;
1908aae7e453SNamhyung Kim }
1909aae7e453SNamhyung Kim }
1910aae7e453SNamhyung Kim /* some entries are collected but hidden by the callstack filter */
1911aae7e453SNamhyung Kim total += con->nr_filtered;
1912aae7e453SNamhyung Kim
191369c5c993SNamhyung Kim print_footer(total, bad, &con->fails);
1914528b9cabSNamhyung Kim }
1915528b9cabSNamhyung Kim
1916c4ac732aSYunlong Song static bool force;
1917c4ac732aSYunlong Song
__cmd_report(bool display_info)1918375eb2beSDavidlohr Bueso static int __cmd_report(bool display_info)
19199b5e350cSHitoshi Mitake {
1920375eb2beSDavidlohr Bueso int err = -EINVAL;
1921375eb2beSDavidlohr Bueso struct perf_tool eops = {
192230b331d2SNamhyung Kim .attr = perf_event__process_attr,
192330b331d2SNamhyung Kim .event_update = process_event_update,
1924375eb2beSDavidlohr Bueso .sample = process_sample_event,
1925375eb2beSDavidlohr Bueso .comm = perf_event__process_comm,
19260d2997f7SNamhyung Kim .mmap = perf_event__process_mmap,
1927f3b3614aSHari Bathini .namespaces = perf_event__process_namespaces,
192830b331d2SNamhyung Kim .tracing_data = perf_event__process_tracing_data,
19290a8cb85cSJiri Olsa .ordered_events = true,
1930375eb2beSDavidlohr Bueso };
19318ceb41d7SJiri Olsa struct perf_data data = {
1932f5fc1412SJiri Olsa .path = input_name,
1933f5fc1412SJiri Olsa .mode = PERF_DATA_MODE_READ,
1934c4ac732aSYunlong Song .force = force,
1935f5fc1412SJiri Olsa };
1936375eb2beSDavidlohr Bueso
19372681bd85SNamhyung Kim session = perf_session__new(&data, &eops);
19386ef81c55SMamatha Inamdar if (IS_ERR(session)) {
1939375eb2beSDavidlohr Bueso pr_err("Initializing perf session failed\n");
19406ef81c55SMamatha Inamdar return PTR_ERR(session);
1941375eb2beSDavidlohr Bueso }
1942375eb2beSDavidlohr Bueso
1943d8d85ce8SThomas Richter symbol_conf.allow_aliases = true;
19440a7e6d1bSNamhyung Kim symbol__init(&session->header.env);
19456fd6c6b4SNamhyung Kim
194630b331d2SNamhyung Kim if (!data.is_pipe) {
1947375eb2beSDavidlohr Bueso if (!perf_session__has_traces(session, "lock record"))
1948375eb2beSDavidlohr Bueso goto out_delete;
1949375eb2beSDavidlohr Bueso
1950375eb2beSDavidlohr Bueso if (perf_session__set_tracepoints_handlers(session, lock_tracepoints)) {
1951375eb2beSDavidlohr Bueso pr_err("Initializing perf session tracepoint handlers failed\n");
1952375eb2beSDavidlohr Bueso goto out_delete;
1953375eb2beSDavidlohr Bueso }
1954375eb2beSDavidlohr Bueso
19553ae03f26SNamhyung Kim if (perf_session__set_tracepoints_handlers(session, contention_tracepoints)) {
19563ae03f26SNamhyung Kim pr_err("Initializing perf session tracepoint handlers failed\n");
19573ae03f26SNamhyung Kim goto out_delete;
19583ae03f26SNamhyung Kim }
195930b331d2SNamhyung Kim }
19603ae03f26SNamhyung Kim
196179079f21SNamhyung Kim if (setup_output_field(false, output_fields))
196264999e44SNamhyung Kim goto out_delete;
196364999e44SNamhyung Kim
196479079f21SNamhyung Kim if (select_key(false))
1965375eb2beSDavidlohr Bueso goto out_delete;
1966375eb2beSDavidlohr Bueso
1967f9c695a2SNamhyung Kim if (show_thread_stats)
1968f9c695a2SNamhyung Kim aggr_mode = LOCK_AGGR_TASK;
1969f9c695a2SNamhyung Kim
1970b7b61cbeSArnaldo Carvalho de Melo err = perf_session__process_events(session);
1971375eb2beSDavidlohr Bueso if (err)
1972375eb2beSDavidlohr Bueso goto out_delete;
1973375eb2beSDavidlohr Bueso
19749b5e350cSHitoshi Mitake setup_pager();
1975375eb2beSDavidlohr Bueso if (display_info) /* used for info subcommand */
1976375eb2beSDavidlohr Bueso err = dump_info();
1977375eb2beSDavidlohr Bueso else {
19780d435bf8SNamhyung Kim combine_result();
19799b5e350cSHitoshi Mitake sort_result();
19809b5e350cSHitoshi Mitake print_result();
1981375eb2beSDavidlohr Bueso }
198233d6aef5SDavid Ahern
1983375eb2beSDavidlohr Bueso out_delete:
1984375eb2beSDavidlohr Bueso perf_session__delete(session);
1985375eb2beSDavidlohr Bueso return err;
19869b5e350cSHitoshi Mitake }
19879b5e350cSHitoshi Mitake
sighandler(int sig __maybe_unused)1988407b36f6SNamhyung Kim static void sighandler(int sig __maybe_unused)
1989407b36f6SNamhyung Kim {
1990407b36f6SNamhyung Kim }
1991407b36f6SNamhyung Kim
check_lock_contention_options(const struct option * options,const char * const * usage)19923477f079SNamhyung Kim static int check_lock_contention_options(const struct option *options,
19933477f079SNamhyung Kim const char * const *usage)
19943477f079SNamhyung Kim
19953477f079SNamhyung Kim {
19963477f079SNamhyung Kim if (show_thread_stats && show_lock_addrs) {
19973477f079SNamhyung Kim pr_err("Cannot use thread and addr mode together\n");
19983477f079SNamhyung Kim parse_options_usage(usage, options, "threads", 0);
19993477f079SNamhyung Kim parse_options_usage(NULL, options, "lock-addr", 0);
20003477f079SNamhyung Kim return -1;
20013477f079SNamhyung Kim }
20023477f079SNamhyung Kim
20033477f079SNamhyung Kim if (show_lock_owner && !use_bpf) {
20043477f079SNamhyung Kim pr_err("Lock owners are available only with BPF\n");
20053477f079SNamhyung Kim parse_options_usage(usage, options, "lock-owner", 0);
20063477f079SNamhyung Kim parse_options_usage(NULL, options, "use-bpf", 0);
20073477f079SNamhyung Kim return -1;
20083477f079SNamhyung Kim }
20093477f079SNamhyung Kim
20103477f079SNamhyung Kim if (show_lock_owner && show_lock_addrs) {
20113477f079SNamhyung Kim pr_err("Cannot use owner and addr mode together\n");
20123477f079SNamhyung Kim parse_options_usage(usage, options, "lock-owner", 0);
20133477f079SNamhyung Kim parse_options_usage(NULL, options, "lock-addr", 0);
20143477f079SNamhyung Kim return -1;
20153477f079SNamhyung Kim }
20163477f079SNamhyung Kim
201769c5c993SNamhyung Kim if (symbol_conf.field_sep) {
201869c5c993SNamhyung Kim if (strstr(symbol_conf.field_sep, ":") || /* part of type flags */
201969c5c993SNamhyung Kim strstr(symbol_conf.field_sep, "+") || /* part of caller offset */
202069c5c993SNamhyung Kim strstr(symbol_conf.field_sep, ".")) { /* can be in a symbol name */
202169c5c993SNamhyung Kim pr_err("Cannot use the separator that is already used\n");
202269c5c993SNamhyung Kim parse_options_usage(usage, options, "x", 1);
202369c5c993SNamhyung Kim return -1;
202469c5c993SNamhyung Kim }
202569c5c993SNamhyung Kim }
202669c5c993SNamhyung Kim
20273477f079SNamhyung Kim if (show_lock_owner)
20283477f079SNamhyung Kim show_thread_stats = true;
20293477f079SNamhyung Kim
20303477f079SNamhyung Kim return 0;
20313477f079SNamhyung Kim }
20323477f079SNamhyung Kim
__cmd_contention(int argc,const char ** argv)20336fda2405SNamhyung Kim static int __cmd_contention(int argc, const char **argv)
2034528b9cabSNamhyung Kim {
2035528b9cabSNamhyung Kim int err = -EINVAL;
2036528b9cabSNamhyung Kim struct perf_tool eops = {
203730b331d2SNamhyung Kim .attr = perf_event__process_attr,
203830b331d2SNamhyung Kim .event_update = process_event_update,
2039528b9cabSNamhyung Kim .sample = process_sample_event,
2040528b9cabSNamhyung Kim .comm = perf_event__process_comm,
2041528b9cabSNamhyung Kim .mmap = perf_event__process_mmap,
204230b331d2SNamhyung Kim .tracing_data = perf_event__process_tracing_data,
2043528b9cabSNamhyung Kim .ordered_events = true,
2044528b9cabSNamhyung Kim };
2045528b9cabSNamhyung Kim struct perf_data data = {
2046528b9cabSNamhyung Kim .path = input_name,
2047528b9cabSNamhyung Kim .mode = PERF_DATA_MODE_READ,
2048528b9cabSNamhyung Kim .force = force,
2049528b9cabSNamhyung Kim };
2050447ec4e5SNamhyung Kim struct lock_contention con = {
2051447ec4e5SNamhyung Kim .target = &target,
2052ceb13bfcSNamhyung Kim .map_nr_entries = bpf_map_entries,
205396532a83SNamhyung Kim .max_stack = max_stack_depth,
205496532a83SNamhyung Kim .stack_skip = stack_skip,
2055529772c4SNamhyung Kim .filters = &filters,
20567b204399SNamhyung Kim .save_callstack = needs_callstack(),
20573477f079SNamhyung Kim .owner = show_lock_owner,
2058447ec4e5SNamhyung Kim };
2059528b9cabSNamhyung Kim
2060eef4fee5SIan Rogers lockhash_table = calloc(LOCKHASH_SIZE, sizeof(*lockhash_table));
2061eef4fee5SIan Rogers if (!lockhash_table)
2062eef4fee5SIan Rogers return -ENOMEM;
2063eef4fee5SIan Rogers
2064eef4fee5SIan Rogers con.result = &lockhash_table[0];
2065eef4fee5SIan Rogers
2066407b36f6SNamhyung Kim session = perf_session__new(use_bpf ? NULL : &data, &eops);
2067528b9cabSNamhyung Kim if (IS_ERR(session)) {
2068528b9cabSNamhyung Kim pr_err("Initializing perf session failed\n");
2069eef4fee5SIan Rogers err = PTR_ERR(session);
2070abaf1e03SArnaldo Carvalho de Melo session = NULL;
2071eef4fee5SIan Rogers goto out_delete;
2072528b9cabSNamhyung Kim }
2073528b9cabSNamhyung Kim
2074a6eaf966SNamhyung Kim con.machine = &session->machines.host;
2075a6eaf966SNamhyung Kim
2076688d2e8dSNamhyung Kim con.aggr_mode = aggr_mode = show_thread_stats ? LOCK_AGGR_TASK :
2077688d2e8dSNamhyung Kim show_lock_addrs ? LOCK_AGGR_ADDR : LOCK_AGGR_CALLER;
2078688d2e8dSNamhyung Kim
207955e39185SNamhyung Kim if (con.aggr_mode == LOCK_AGGR_CALLER)
208055e39185SNamhyung Kim con.save_callstack = true;
208155e39185SNamhyung Kim
2082d8d85ce8SThomas Richter symbol_conf.allow_aliases = true;
2083528b9cabSNamhyung Kim symbol__init(&session->header.env);
2084528b9cabSNamhyung Kim
2085407b36f6SNamhyung Kim if (use_bpf) {
20866fda2405SNamhyung Kim err = target__validate(&target);
20876fda2405SNamhyung Kim if (err) {
20886fda2405SNamhyung Kim char errbuf[512];
20896fda2405SNamhyung Kim
20906fda2405SNamhyung Kim target__strerror(&target, err, errbuf, 512);
20916fda2405SNamhyung Kim pr_err("%s\n", errbuf);
20926fda2405SNamhyung Kim goto out_delete;
2093407b36f6SNamhyung Kim }
2094407b36f6SNamhyung Kim
2095407b36f6SNamhyung Kim signal(SIGINT, sighandler);
2096407b36f6SNamhyung Kim signal(SIGCHLD, sighandler);
2097407b36f6SNamhyung Kim signal(SIGTERM, sighandler);
20986fda2405SNamhyung Kim
2099447ec4e5SNamhyung Kim con.evlist = evlist__new();
2100447ec4e5SNamhyung Kim if (con.evlist == NULL) {
21016fda2405SNamhyung Kim err = -ENOMEM;
21026fda2405SNamhyung Kim goto out_delete;
21036fda2405SNamhyung Kim }
21046fda2405SNamhyung Kim
2105447ec4e5SNamhyung Kim err = evlist__create_maps(con.evlist, &target);
21066fda2405SNamhyung Kim if (err < 0)
21076fda2405SNamhyung Kim goto out_delete;
21086fda2405SNamhyung Kim
21096fda2405SNamhyung Kim if (argc) {
2110447ec4e5SNamhyung Kim err = evlist__prepare_workload(con.evlist, &target,
21116fda2405SNamhyung Kim argv, false, NULL);
21126fda2405SNamhyung Kim if (err < 0)
21136fda2405SNamhyung Kim goto out_delete;
21146fda2405SNamhyung Kim }
21156fda2405SNamhyung Kim
2116447ec4e5SNamhyung Kim if (lock_contention_prepare(&con) < 0) {
21176fda2405SNamhyung Kim pr_err("lock contention BPF setup failed\n");
21186fda2405SNamhyung Kim goto out_delete;
21196fda2405SNamhyung Kim }
212030b331d2SNamhyung Kim } else if (!data.is_pipe) {
2121528b9cabSNamhyung Kim if (!perf_session__has_traces(session, "lock record"))
2122528b9cabSNamhyung Kim goto out_delete;
2123528b9cabSNamhyung Kim
2124407b36f6SNamhyung Kim if (!evlist__find_evsel_by_str(session->evlist,
2125407b36f6SNamhyung Kim "lock:contention_begin")) {
2126528b9cabSNamhyung Kim pr_err("lock contention evsel not found\n");
2127528b9cabSNamhyung Kim goto out_delete;
2128528b9cabSNamhyung Kim }
2129528b9cabSNamhyung Kim
2130407b36f6SNamhyung Kim if (perf_session__set_tracepoints_handlers(session,
2131407b36f6SNamhyung Kim contention_tracepoints)) {
2132528b9cabSNamhyung Kim pr_err("Initializing perf session tracepoint handlers failed\n");
2133528b9cabSNamhyung Kim goto out_delete;
2134528b9cabSNamhyung Kim }
2135407b36f6SNamhyung Kim }
2136528b9cabSNamhyung Kim
213779079f21SNamhyung Kim if (setup_output_field(true, output_fields))
2138528b9cabSNamhyung Kim goto out_delete;
2139528b9cabSNamhyung Kim
214079079f21SNamhyung Kim if (select_key(true))
2141528b9cabSNamhyung Kim goto out_delete;
2142528b9cabSNamhyung Kim
214369c5c993SNamhyung Kim if (symbol_conf.field_sep) {
214469c5c993SNamhyung Kim int i;
214569c5c993SNamhyung Kim struct lock_key *keys = contention_keys;
214669c5c993SNamhyung Kim
214769c5c993SNamhyung Kim /* do not align output in CSV format */
214869c5c993SNamhyung Kim for (i = 0; keys[i].name; i++)
214969c5c993SNamhyung Kim keys[i].len = 0;
215069c5c993SNamhyung Kim }
215169c5c993SNamhyung Kim
2152407b36f6SNamhyung Kim if (use_bpf) {
2153407b36f6SNamhyung Kim lock_contention_start();
21546fda2405SNamhyung Kim if (argc)
2155447ec4e5SNamhyung Kim evlist__start_workload(con.evlist);
2156407b36f6SNamhyung Kim
2157407b36f6SNamhyung Kim /* wait for signal */
2158407b36f6SNamhyung Kim pause();
2159407b36f6SNamhyung Kim
2160407b36f6SNamhyung Kim lock_contention_stop();
2161447ec4e5SNamhyung Kim lock_contention_read(&con);
2162407b36f6SNamhyung Kim } else {
2163528b9cabSNamhyung Kim err = perf_session__process_events(session);
2164528b9cabSNamhyung Kim if (err)
2165528b9cabSNamhyung Kim goto out_delete;
2166407b36f6SNamhyung Kim }
2167528b9cabSNamhyung Kim
2168528b9cabSNamhyung Kim setup_pager();
2169528b9cabSNamhyung Kim
2170528b9cabSNamhyung Kim sort_contention_result();
2171a6eaf966SNamhyung Kim print_contention_result(&con);
2172528b9cabSNamhyung Kim
2173528b9cabSNamhyung Kim out_delete:
2174b4a7eff9SNamhyung Kim lock_filter_finish();
2175447ec4e5SNamhyung Kim evlist__delete(con.evlist);
2176407b36f6SNamhyung Kim lock_contention_finish();
2177528b9cabSNamhyung Kim perf_session__delete(session);
2178eef4fee5SIan Rogers zfree(&lockhash_table);
2179528b9cabSNamhyung Kim return err;
2180528b9cabSNamhyung Kim }
2181528b9cabSNamhyung Kim
2182528b9cabSNamhyung Kim
__cmd_record(int argc,const char ** argv)21839b5e350cSHitoshi Mitake static int __cmd_record(int argc, const char **argv)
21849b5e350cSHitoshi Mitake {
2185c75d98afSArnaldo Carvalho de Melo const char *record_args[] = {
21862762c488SNamhyung Kim "record", "-R", "-m", "1024", "-c", "1", "--synth", "task",
2187c75d98afSArnaldo Carvalho de Melo };
21880d2997f7SNamhyung Kim const char *callgraph_args[] = {
21890d2997f7SNamhyung Kim "--call-graph", "fp," __stringify(CONTENTION_STACK_DEPTH),
21900d2997f7SNamhyung Kim };
21910a98c7feSDavidlohr Bueso unsigned int rec_argc, i, j, ret;
2192166a9764SNamhyung Kim unsigned int nr_tracepoints;
21930d2997f7SNamhyung Kim unsigned int nr_callgraph_args = 0;
21949b5e350cSHitoshi Mitake const char **rec_argv;
2195166a9764SNamhyung Kim bool has_lock_stat = true;
21969b5e350cSHitoshi Mitake
2197d25dcba8SDavid Ahern for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) {
2198746f16ecSArnaldo Carvalho de Melo if (!is_valid_tracepoint(lock_tracepoints[i].name)) {
2199166a9764SNamhyung Kim pr_debug("tracepoint %s is not enabled. "
2200d25dcba8SDavid Ahern "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n",
2201746f16ecSArnaldo Carvalho de Melo lock_tracepoints[i].name);
2202166a9764SNamhyung Kim has_lock_stat = false;
2203166a9764SNamhyung Kim break;
2204166a9764SNamhyung Kim }
2205166a9764SNamhyung Kim }
2206166a9764SNamhyung Kim
2207166a9764SNamhyung Kim if (has_lock_stat)
2208166a9764SNamhyung Kim goto setup_args;
2209166a9764SNamhyung Kim
2210166a9764SNamhyung Kim for (i = 0; i < ARRAY_SIZE(contention_tracepoints); i++) {
2211166a9764SNamhyung Kim if (!is_valid_tracepoint(contention_tracepoints[i].name)) {
2212166a9764SNamhyung Kim pr_err("tracepoint %s is not enabled.\n",
2213166a9764SNamhyung Kim contention_tracepoints[i].name);
2214d25dcba8SDavid Ahern return 1;
2215d25dcba8SDavid Ahern }
2216d25dcba8SDavid Ahern }
22179b5e350cSHitoshi Mitake
22180d2997f7SNamhyung Kim nr_callgraph_args = ARRAY_SIZE(callgraph_args);
22190d2997f7SNamhyung Kim
2220166a9764SNamhyung Kim setup_args:
22210d2997f7SNamhyung Kim rec_argc = ARRAY_SIZE(record_args) + nr_callgraph_args + argc - 1;
2222166a9764SNamhyung Kim
2223166a9764SNamhyung Kim if (has_lock_stat)
2224166a9764SNamhyung Kim nr_tracepoints = ARRAY_SIZE(lock_tracepoints);
2225166a9764SNamhyung Kim else
2226166a9764SNamhyung Kim nr_tracepoints = ARRAY_SIZE(contention_tracepoints);
2227166a9764SNamhyung Kim
2228d25dcba8SDavid Ahern /* factor of 2 is for -e in front of each tracepoint */
2229166a9764SNamhyung Kim rec_argc += 2 * nr_tracepoints;
2230d25dcba8SDavid Ahern
2231d25dcba8SDavid Ahern rec_argv = calloc(rec_argc + 1, sizeof(char *));
22320a98c7feSDavidlohr Bueso if (!rec_argv)
2233ce47dc56SChris Samuel return -ENOMEM;
2234ce47dc56SChris Samuel
22359b5e350cSHitoshi Mitake for (i = 0; i < ARRAY_SIZE(record_args); i++)
22369b5e350cSHitoshi Mitake rec_argv[i] = strdup(record_args[i]);
22379b5e350cSHitoshi Mitake
2238166a9764SNamhyung Kim for (j = 0; j < nr_tracepoints; j++) {
2239166a9764SNamhyung Kim const char *ev_name;
2240166a9764SNamhyung Kim
2241166a9764SNamhyung Kim if (has_lock_stat)
2242166a9764SNamhyung Kim ev_name = strdup(lock_tracepoints[j].name);
2243166a9764SNamhyung Kim else
2244166a9764SNamhyung Kim ev_name = strdup(contention_tracepoints[j].name);
2245166a9764SNamhyung Kim
2246166a9764SNamhyung Kim if (!ev_name)
2247166a9764SNamhyung Kim return -ENOMEM;
2248166a9764SNamhyung Kim
2249d25dcba8SDavid Ahern rec_argv[i++] = "-e";
2250166a9764SNamhyung Kim rec_argv[i++] = ev_name;
2251d25dcba8SDavid Ahern }
2252d25dcba8SDavid Ahern
22530d2997f7SNamhyung Kim for (j = 0; j < nr_callgraph_args; j++, i++)
22540d2997f7SNamhyung Kim rec_argv[i] = callgraph_args[j];
22550d2997f7SNamhyung Kim
22569b5e350cSHitoshi Mitake for (j = 1; j < (unsigned int)argc; j++, i++)
22579b5e350cSHitoshi Mitake rec_argv[i] = argv[j];
22589b5e350cSHitoshi Mitake
22599b5e350cSHitoshi Mitake BUG_ON(i != rec_argc);
22609b5e350cSHitoshi Mitake
2261b0ad8ea6SArnaldo Carvalho de Melo ret = cmd_record(i, rec_argv);
22620a98c7feSDavidlohr Bueso free(rec_argv);
22630a98c7feSDavidlohr Bueso return ret;
22649b5e350cSHitoshi Mitake }
22659b5e350cSHitoshi Mitake
parse_map_entry(const struct option * opt,const char * str,int unset __maybe_unused)2266ceb13bfcSNamhyung Kim static int parse_map_entry(const struct option *opt, const char *str,
2267ceb13bfcSNamhyung Kim int unset __maybe_unused)
2268ceb13bfcSNamhyung Kim {
2269ceb13bfcSNamhyung Kim unsigned long *len = (unsigned long *)opt->value;
2270ceb13bfcSNamhyung Kim unsigned long val;
2271ceb13bfcSNamhyung Kim char *endptr;
2272ceb13bfcSNamhyung Kim
2273ceb13bfcSNamhyung Kim errno = 0;
2274ceb13bfcSNamhyung Kim val = strtoul(str, &endptr, 0);
2275ceb13bfcSNamhyung Kim if (*endptr != '\0' || errno != 0) {
2276ceb13bfcSNamhyung Kim pr_err("invalid BPF map length: %s\n", str);
2277ceb13bfcSNamhyung Kim return -1;
2278ceb13bfcSNamhyung Kim }
2279ceb13bfcSNamhyung Kim
2280ceb13bfcSNamhyung Kim *len = val;
2281ceb13bfcSNamhyung Kim return 0;
2282ceb13bfcSNamhyung Kim }
2283ceb13bfcSNamhyung Kim
parse_max_stack(const struct option * opt,const char * str,int unset __maybe_unused)22840a277b62SNamhyung Kim static int parse_max_stack(const struct option *opt, const char *str,
22850a277b62SNamhyung Kim int unset __maybe_unused)
22860a277b62SNamhyung Kim {
22870a277b62SNamhyung Kim unsigned long *len = (unsigned long *)opt->value;
22880a277b62SNamhyung Kim long val;
22890a277b62SNamhyung Kim char *endptr;
22900a277b62SNamhyung Kim
22910a277b62SNamhyung Kim errno = 0;
22920a277b62SNamhyung Kim val = strtol(str, &endptr, 0);
22930a277b62SNamhyung Kim if (*endptr != '\0' || errno != 0) {
22940a277b62SNamhyung Kim pr_err("invalid max stack depth: %s\n", str);
22950a277b62SNamhyung Kim return -1;
22960a277b62SNamhyung Kim }
22970a277b62SNamhyung Kim
22980a277b62SNamhyung Kim if (val < 0 || val > sysctl__max_stack()) {
22990a277b62SNamhyung Kim pr_err("invalid max stack depth: %ld\n", val);
23000a277b62SNamhyung Kim return -1;
23010a277b62SNamhyung Kim }
23020a277b62SNamhyung Kim
23030a277b62SNamhyung Kim *len = val;
23040a277b62SNamhyung Kim return 0;
23050a277b62SNamhyung Kim }
23060a277b62SNamhyung Kim
add_lock_type(unsigned int flags)2307b4a7eff9SNamhyung Kim static bool add_lock_type(unsigned int flags)
2308b4a7eff9SNamhyung Kim {
2309b4a7eff9SNamhyung Kim unsigned int *tmp;
2310b4a7eff9SNamhyung Kim
2311b4a7eff9SNamhyung Kim tmp = realloc(filters.types, (filters.nr_types + 1) * sizeof(*filters.types));
2312b4a7eff9SNamhyung Kim if (tmp == NULL)
2313b4a7eff9SNamhyung Kim return false;
2314b4a7eff9SNamhyung Kim
2315b4a7eff9SNamhyung Kim tmp[filters.nr_types++] = flags;
2316b4a7eff9SNamhyung Kim filters.types = tmp;
2317b4a7eff9SNamhyung Kim return true;
2318b4a7eff9SNamhyung Kim }
2319b4a7eff9SNamhyung Kim
parse_lock_type(const struct option * opt __maybe_unused,const char * str,int unset __maybe_unused)2320b4a7eff9SNamhyung Kim static int parse_lock_type(const struct option *opt __maybe_unused, const char *str,
2321b4a7eff9SNamhyung Kim int unset __maybe_unused)
2322b4a7eff9SNamhyung Kim {
2323b4a7eff9SNamhyung Kim char *s, *tmp, *tok;
2324b4a7eff9SNamhyung Kim int ret = 0;
2325b4a7eff9SNamhyung Kim
2326b4a7eff9SNamhyung Kim s = strdup(str);
2327b4a7eff9SNamhyung Kim if (s == NULL)
2328b4a7eff9SNamhyung Kim return -1;
2329b4a7eff9SNamhyung Kim
2330b4a7eff9SNamhyung Kim for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {
2331b4a7eff9SNamhyung Kim unsigned int flags = get_type_flag(tok);
2332b4a7eff9SNamhyung Kim
2333b4a7eff9SNamhyung Kim if (flags == -1U) {
2334d783ea8fSNamhyung Kim pr_err("Unknown lock flags: %s\n", tok);
2335b4a7eff9SNamhyung Kim ret = -1;
2336b4a7eff9SNamhyung Kim break;
2337b4a7eff9SNamhyung Kim }
2338b4a7eff9SNamhyung Kim
2339b4a7eff9SNamhyung Kim if (!add_lock_type(flags)) {
2340b4a7eff9SNamhyung Kim ret = -1;
2341b4a7eff9SNamhyung Kim break;
2342b4a7eff9SNamhyung Kim }
2343b4a7eff9SNamhyung Kim }
2344b4a7eff9SNamhyung Kim
2345b4a7eff9SNamhyung Kim free(s);
2346b4a7eff9SNamhyung Kim return ret;
2347b4a7eff9SNamhyung Kim }
2348b4a7eff9SNamhyung Kim
add_lock_addr(unsigned long addr)2349511e19b9SNamhyung Kim static bool add_lock_addr(unsigned long addr)
2350511e19b9SNamhyung Kim {
2351511e19b9SNamhyung Kim unsigned long *tmp;
2352511e19b9SNamhyung Kim
2353511e19b9SNamhyung Kim tmp = realloc(filters.addrs, (filters.nr_addrs + 1) * sizeof(*filters.addrs));
2354511e19b9SNamhyung Kim if (tmp == NULL) {
2355511e19b9SNamhyung Kim pr_err("Memory allocation failure\n");
2356511e19b9SNamhyung Kim return false;
2357511e19b9SNamhyung Kim }
2358511e19b9SNamhyung Kim
2359511e19b9SNamhyung Kim tmp[filters.nr_addrs++] = addr;
2360511e19b9SNamhyung Kim filters.addrs = tmp;
2361511e19b9SNamhyung Kim return true;
2362511e19b9SNamhyung Kim }
2363511e19b9SNamhyung Kim
add_lock_sym(char * name)2364511e19b9SNamhyung Kim static bool add_lock_sym(char *name)
2365511e19b9SNamhyung Kim {
2366511e19b9SNamhyung Kim char **tmp;
2367511e19b9SNamhyung Kim char *sym = strdup(name);
2368511e19b9SNamhyung Kim
2369511e19b9SNamhyung Kim if (sym == NULL) {
2370511e19b9SNamhyung Kim pr_err("Memory allocation failure\n");
2371511e19b9SNamhyung Kim return false;
2372511e19b9SNamhyung Kim }
2373511e19b9SNamhyung Kim
2374511e19b9SNamhyung Kim tmp = realloc(filters.syms, (filters.nr_syms + 1) * sizeof(*filters.syms));
2375511e19b9SNamhyung Kim if (tmp == NULL) {
2376511e19b9SNamhyung Kim pr_err("Memory allocation failure\n");
2377511e19b9SNamhyung Kim free(sym);
2378511e19b9SNamhyung Kim return false;
2379511e19b9SNamhyung Kim }
2380511e19b9SNamhyung Kim
2381511e19b9SNamhyung Kim tmp[filters.nr_syms++] = sym;
2382511e19b9SNamhyung Kim filters.syms = tmp;
2383511e19b9SNamhyung Kim return true;
2384511e19b9SNamhyung Kim }
2385511e19b9SNamhyung Kim
parse_lock_addr(const struct option * opt __maybe_unused,const char * str,int unset __maybe_unused)2386511e19b9SNamhyung Kim static int parse_lock_addr(const struct option *opt __maybe_unused, const char *str,
2387511e19b9SNamhyung Kim int unset __maybe_unused)
2388511e19b9SNamhyung Kim {
2389511e19b9SNamhyung Kim char *s, *tmp, *tok;
2390511e19b9SNamhyung Kim int ret = 0;
2391511e19b9SNamhyung Kim u64 addr;
2392511e19b9SNamhyung Kim
2393511e19b9SNamhyung Kim s = strdup(str);
2394511e19b9SNamhyung Kim if (s == NULL)
2395511e19b9SNamhyung Kim return -1;
2396511e19b9SNamhyung Kim
2397511e19b9SNamhyung Kim for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {
2398511e19b9SNamhyung Kim char *end;
2399511e19b9SNamhyung Kim
2400511e19b9SNamhyung Kim addr = strtoul(tok, &end, 16);
2401511e19b9SNamhyung Kim if (*end == '\0') {
2402511e19b9SNamhyung Kim if (!add_lock_addr(addr)) {
2403511e19b9SNamhyung Kim ret = -1;
2404511e19b9SNamhyung Kim break;
2405511e19b9SNamhyung Kim }
2406511e19b9SNamhyung Kim continue;
2407511e19b9SNamhyung Kim }
2408511e19b9SNamhyung Kim
2409511e19b9SNamhyung Kim /*
2410511e19b9SNamhyung Kim * At this moment, we don't have kernel symbols. Save the symbols
2411511e19b9SNamhyung Kim * in a separate list and resolve them to addresses later.
2412511e19b9SNamhyung Kim */
2413511e19b9SNamhyung Kim if (!add_lock_sym(tok)) {
2414511e19b9SNamhyung Kim ret = -1;
2415511e19b9SNamhyung Kim break;
2416511e19b9SNamhyung Kim }
2417511e19b9SNamhyung Kim }
2418511e19b9SNamhyung Kim
2419511e19b9SNamhyung Kim free(s);
2420511e19b9SNamhyung Kim return ret;
2421511e19b9SNamhyung Kim }
2422511e19b9SNamhyung Kim
parse_call_stack(const struct option * opt __maybe_unused,const char * str,int unset __maybe_unused)24237b204399SNamhyung Kim static int parse_call_stack(const struct option *opt __maybe_unused, const char *str,
24247b204399SNamhyung Kim int unset __maybe_unused)
24257b204399SNamhyung Kim {
24267b204399SNamhyung Kim char *s, *tmp, *tok;
24277b204399SNamhyung Kim int ret = 0;
24287b204399SNamhyung Kim
24297b204399SNamhyung Kim s = strdup(str);
24307b204399SNamhyung Kim if (s == NULL)
24317b204399SNamhyung Kim return -1;
24327b204399SNamhyung Kim
24337b204399SNamhyung Kim for (tok = strtok_r(s, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {
24347b204399SNamhyung Kim struct callstack_filter *entry;
24357b204399SNamhyung Kim
24367b204399SNamhyung Kim entry = malloc(sizeof(*entry) + strlen(tok) + 1);
24377b204399SNamhyung Kim if (entry == NULL) {
24387b204399SNamhyung Kim pr_err("Memory allocation failure\n");
24397b204399SNamhyung Kim return -1;
24407b204399SNamhyung Kim }
24417b204399SNamhyung Kim
24427b204399SNamhyung Kim strcpy(entry->name, tok);
24437b204399SNamhyung Kim list_add_tail(&entry->list, &callstack_filters);
24447b204399SNamhyung Kim }
24457b204399SNamhyung Kim
24467b204399SNamhyung Kim free(s);
24477b204399SNamhyung Kim return ret;
24487b204399SNamhyung Kim }
24497b204399SNamhyung Kim
parse_output(const struct option * opt __maybe_unused,const char * str,int unset __maybe_unused)2450f6027053SNamhyung Kim static int parse_output(const struct option *opt __maybe_unused, const char *str,
2451f6027053SNamhyung Kim int unset __maybe_unused)
2452f6027053SNamhyung Kim {
2453f6027053SNamhyung Kim const char **name = (const char **)opt->value;
2454f6027053SNamhyung Kim
2455f6027053SNamhyung Kim if (str == NULL)
2456f6027053SNamhyung Kim return -1;
2457f6027053SNamhyung Kim
2458f6027053SNamhyung Kim lock_output = fopen(str, "w");
2459f6027053SNamhyung Kim if (lock_output == NULL) {
2460f6027053SNamhyung Kim pr_err("Cannot open %s\n", str);
2461f6027053SNamhyung Kim return -1;
2462f6027053SNamhyung Kim }
2463f6027053SNamhyung Kim
2464f6027053SNamhyung Kim *name = str;
2465f6027053SNamhyung Kim return 0;
2466f6027053SNamhyung Kim }
2467f6027053SNamhyung Kim
cmd_lock(int argc,const char ** argv)2468b0ad8ea6SArnaldo Carvalho de Melo int cmd_lock(int argc, const char **argv)
24699b5e350cSHitoshi Mitake {
2470c75d98afSArnaldo Carvalho de Melo const struct option lock_options[] = {
2471c75d98afSArnaldo Carvalho de Melo OPT_STRING('i', "input", &input_name, "file", "input file name"),
2472f6027053SNamhyung Kim OPT_CALLBACK(0, "output", &output_name, "file", "output file name", parse_output),
2473c75d98afSArnaldo Carvalho de Melo OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),
2474c75d98afSArnaldo Carvalho de Melo OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),
2475b40e3612SArnaldo Carvalho de Melo OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
2476309e133dSNamhyung Kim OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name,
2477309e133dSNamhyung Kim "file", "vmlinux pathname"),
2478309e133dSNamhyung Kim OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
2479309e133dSNamhyung Kim "file", "kallsyms pathname"),
2480a527c2c1SJames Clark OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any warnings or messages"),
2481c75d98afSArnaldo Carvalho de Melo OPT_END()
2482c75d98afSArnaldo Carvalho de Melo };
2483249eed53SChangbin Du
2484249eed53SChangbin Du const struct option info_options[] = {
2485249eed53SChangbin Du OPT_BOOLEAN('t', "threads", &info_threads,
2486249eed53SChangbin Du "dump thread list in perf.data"),
2487249eed53SChangbin Du OPT_BOOLEAN('m', "map", &info_map,
2488249eed53SChangbin Du "map of lock instances (address:name table)"),
2489249eed53SChangbin Du OPT_PARENT(lock_options)
2490249eed53SChangbin Du };
2491249eed53SChangbin Du
2492c75d98afSArnaldo Carvalho de Melo const struct option report_options[] = {
2493c75d98afSArnaldo Carvalho de Melo OPT_STRING('k', "key", &sort_key, "acquired",
2494f37376cdSDavidlohr Bueso "key for sorting (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),
24954bd9cab5SNamhyung Kim OPT_STRING('F', "field", &output_fields, NULL,
24964bd9cab5SNamhyung Kim "output fields (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),
2497c75d98afSArnaldo Carvalho de Melo /* TODO: type */
24980d435bf8SNamhyung Kim OPT_BOOLEAN('c', "combine-locks", &combine_locks,
24990d435bf8SNamhyung Kim "combine locks in the same class"),
25007c3bcbdfSNamhyung Kim OPT_BOOLEAN('t', "threads", &show_thread_stats,
25017c3bcbdfSNamhyung Kim "show per-thread lock stats"),
25026282a1f4SNamhyung Kim OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"),
2503249eed53SChangbin Du OPT_PARENT(lock_options)
2504c75d98afSArnaldo Carvalho de Melo };
2505249eed53SChangbin Du
2506407b36f6SNamhyung Kim struct option contention_options[] = {
250779079f21SNamhyung Kim OPT_STRING('k', "key", &sort_key, "wait_total",
250879079f21SNamhyung Kim "key for sorting (contended / wait_total / wait_max / wait_min / avg_wait)"),
250979079f21SNamhyung Kim OPT_STRING('F', "field", &output_fields, "contended,wait_total,wait_max,avg_wait",
251079079f21SNamhyung Kim "output fields (contended / wait_total / wait_max / wait_min / avg_wait)"),
25111ab55323SNamhyung Kim OPT_BOOLEAN('t', "threads", &show_thread_stats,
25121ab55323SNamhyung Kim "show per-thread lock stats"),
2513407b36f6SNamhyung Kim OPT_BOOLEAN('b', "use-bpf", &use_bpf, "use BPF program to collect lock contention stats"),
25146fda2405SNamhyung Kim OPT_BOOLEAN('a', "all-cpus", &target.system_wide,
25156fda2405SNamhyung Kim "System-wide collection from all CPUs"),
25166fda2405SNamhyung Kim OPT_STRING('C', "cpu", &target.cpu_list, "cpu",
25176fda2405SNamhyung Kim "List of cpus to monitor"),
25186fda2405SNamhyung Kim OPT_STRING('p', "pid", &target.pid, "pid",
25196fda2405SNamhyung Kim "Trace on existing process id"),
25206fda2405SNamhyung Kim OPT_STRING(0, "tid", &target.tid, "tid",
25216fda2405SNamhyung Kim "Trace on existing thread id (exclusive to --pid)"),
252284b91920SNamhyung Kim OPT_CALLBACK('M', "map-nr-entries", &bpf_map_entries, "num",
2523ceb13bfcSNamhyung Kim "Max number of BPF map entries", parse_map_entry),
25240a277b62SNamhyung Kim OPT_CALLBACK(0, "max-stack", &max_stack_depth, "num",
25250f2418fdSKajol Jain "Set the maximum stack depth when collecting lock contention, "
25260a277b62SNamhyung Kim "Default: " __stringify(CONTENTION_STACK_DEPTH), parse_max_stack),
252796532a83SNamhyung Kim OPT_INTEGER(0, "stack-skip", &stack_skip,
252896532a83SNamhyung Kim "Set the number of stack depth to skip when finding a lock caller, "
252996532a83SNamhyung Kim "Default: " __stringify(CONTENTION_STACK_SKIP)),
25306282a1f4SNamhyung Kim OPT_INTEGER('E', "entries", &print_nr_entries, "display this many functions"),
2531688d2e8dSNamhyung Kim OPT_BOOLEAN('l', "lock-addr", &show_lock_addrs, "show lock stats by address"),
2532b4a7eff9SNamhyung Kim OPT_CALLBACK('Y', "type-filter", NULL, "FLAGS",
2533b4a7eff9SNamhyung Kim "Filter specific type of locks", parse_lock_type),
2534511e19b9SNamhyung Kim OPT_CALLBACK('L', "lock-filter", NULL, "ADDRS/NAMES",
2535511e19b9SNamhyung Kim "Filter specific address/symbol of locks", parse_lock_addr),
25367b204399SNamhyung Kim OPT_CALLBACK('S', "callstack-filter", NULL, "NAMES",
25377b204399SNamhyung Kim "Filter specific function in the callstack", parse_call_stack),
25383477f079SNamhyung Kim OPT_BOOLEAN('o', "lock-owner", &show_lock_owner, "show lock owners instead of waiters"),
253969c5c993SNamhyung Kim OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep, "separator",
254069c5c993SNamhyung Kim "print result in CSV format with custom separator"),
2541528b9cabSNamhyung Kim OPT_PARENT(lock_options)
2542528b9cabSNamhyung Kim };
2543528b9cabSNamhyung Kim
2544c75d98afSArnaldo Carvalho de Melo const char * const info_usage[] = {
2545c75d98afSArnaldo Carvalho de Melo "perf lock info [<options>]",
2546c75d98afSArnaldo Carvalho de Melo NULL
2547c75d98afSArnaldo Carvalho de Melo };
2548a2368c31SRamkumar Ramachandra const char *const lock_subcommands[] = { "record", "report", "script",
25493705a6efSYang Jihong "info", "contention", NULL };
2550a2368c31SRamkumar Ramachandra const char *lock_usage[] = {
2551a2368c31SRamkumar Ramachandra NULL,
2552c75d98afSArnaldo Carvalho de Melo NULL
2553c75d98afSArnaldo Carvalho de Melo };
2554c75d98afSArnaldo Carvalho de Melo const char * const report_usage[] = {
2555c75d98afSArnaldo Carvalho de Melo "perf lock report [<options>]",
2556c75d98afSArnaldo Carvalho de Melo NULL
2557c75d98afSArnaldo Carvalho de Melo };
2558528b9cabSNamhyung Kim const char * const contention_usage[] = {
2559528b9cabSNamhyung Kim "perf lock contention [<options>]",
2560528b9cabSNamhyung Kim NULL
2561528b9cabSNamhyung Kim };
25629b5e350cSHitoshi Mitake unsigned int i;
256333d6aef5SDavid Ahern int rc = 0;
25649b5e350cSHitoshi Mitake
2565eef4fee5SIan Rogers lockhash_table = calloc(LOCKHASH_SIZE, sizeof(*lockhash_table));
2566eef4fee5SIan Rogers if (!lockhash_table)
2567eef4fee5SIan Rogers return -ENOMEM;
2568eef4fee5SIan Rogers
25699b5e350cSHitoshi Mitake for (i = 0; i < LOCKHASH_SIZE; i++)
25707672d00aSNamhyung Kim INIT_HLIST_HEAD(lockhash_table + i);
25719b5e350cSHitoshi Mitake
2572f6027053SNamhyung Kim lock_output = stderr;
2573a2368c31SRamkumar Ramachandra argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands,
2574a2368c31SRamkumar Ramachandra lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);
25759b5e350cSHitoshi Mitake if (!argc)
25769b5e350cSHitoshi Mitake usage_with_options(lock_usage, lock_options);
25779b5e350cSHitoshi Mitake
2578ae0f4eb3SWei Li if (strlen(argv[0]) > 2 && strstarts("record", argv[0])) {
25799b5e350cSHitoshi Mitake return __cmd_record(argc, argv);
2580ae0f4eb3SWei Li } else if (strlen(argv[0]) > 2 && strstarts("report", argv[0])) {
258159f411b6SIngo Molnar trace_handler = &report_lock_ops;
25829b5e350cSHitoshi Mitake if (argc) {
25839b5e350cSHitoshi Mitake argc = parse_options(argc, argv,
258459f411b6SIngo Molnar report_options, report_usage, 0);
25859b5e350cSHitoshi Mitake if (argc)
258659f411b6SIngo Molnar usage_with_options(report_usage, report_options);
25879b5e350cSHitoshi Mitake }
2588375eb2beSDavidlohr Bueso rc = __cmd_report(false);
2589133dc4c3SIngo Molnar } else if (!strcmp(argv[0], "script")) {
2590133dc4c3SIngo Molnar /* Aliased to 'perf script' */
2591eef4fee5SIan Rogers rc = cmd_script(argc, argv);
259226242d85SHitoshi Mitake } else if (!strcmp(argv[0], "info")) {
259326242d85SHitoshi Mitake if (argc) {
259426242d85SHitoshi Mitake argc = parse_options(argc, argv,
259526242d85SHitoshi Mitake info_options, info_usage, 0);
259626242d85SHitoshi Mitake if (argc)
259726242d85SHitoshi Mitake usage_with_options(info_usage, info_options);
259826242d85SHitoshi Mitake }
259959f411b6SIngo Molnar /* recycling report_lock_ops */
260059f411b6SIngo Molnar trace_handler = &report_lock_ops;
2601375eb2beSDavidlohr Bueso rc = __cmd_report(true);
2602528b9cabSNamhyung Kim } else if (strlen(argv[0]) > 2 && strstarts("contention", argv[0])) {
2603528b9cabSNamhyung Kim trace_handler = &contention_lock_ops;
260479079f21SNamhyung Kim sort_key = "wait_total";
260579079f21SNamhyung Kim output_fields = "contended,wait_total,wait_max,avg_wait";
260679079f21SNamhyung Kim
2607407b36f6SNamhyung Kim #ifndef HAVE_BPF_SKEL
2608407b36f6SNamhyung Kim set_option_nobuild(contention_options, 'b', "use-bpf",
26099a2d5178SArnaldo Carvalho de Melo "no BUILD_BPF_SKEL=1", false);
2610407b36f6SNamhyung Kim #endif
2611528b9cabSNamhyung Kim if (argc) {
2612528b9cabSNamhyung Kim argc = parse_options(argc, argv, contention_options,
2613528b9cabSNamhyung Kim contention_usage, 0);
2614528b9cabSNamhyung Kim }
2615688d2e8dSNamhyung Kim
26163477f079SNamhyung Kim if (check_lock_contention_options(contention_options,
26173477f079SNamhyung Kim contention_usage) < 0)
2618688d2e8dSNamhyung Kim return -1;
2619688d2e8dSNamhyung Kim
26206fda2405SNamhyung Kim rc = __cmd_contention(argc, argv);
26219b5e350cSHitoshi Mitake } else {
26229b5e350cSHitoshi Mitake usage_with_options(lock_usage, lock_options);
26239b5e350cSHitoshi Mitake }
26249b5e350cSHitoshi Mitake
2625eef4fee5SIan Rogers zfree(&lockhash_table);
262633d6aef5SDavid Ahern return rc;
26279b5e350cSHitoshi Mitake }
2628