Linux Perf
builtin-lock.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <inttypes.h>
4 #include "builtin.h"
5 #include "perf.h"
6 
7 #include "util/evlist.h"
8 #include "util/evsel.h"
9 #include "util/util.h"
10 #include "util/cache.h"
11 #include "util/symbol.h"
12 #include "util/thread.h"
13 #include "util/header.h"
14 
15 #include <subcmd/parse-options.h>
16 #include "util/trace-event.h"
17 
18 #include "util/debug.h"
19 #include "util/session.h"
20 #include "util/tool.h"
21 #include "util/data.h"
22 
23 #include <sys/types.h>
24 #include <sys/prctl.h>
25 #include <semaphore.h>
26 #include <pthread.h>
27 #include <math.h>
28 #include <limits.h>
29 
30 #include <linux/list.h>
31 #include <linux/hash.h>
32 #include <linux/kernel.h>
33 
34 static struct perf_session *session;
35 
36 /* based on kernel/lockdep.c */
37 #define LOCKHASH_BITS 12
38 #define LOCKHASH_SIZE (1UL << LOCKHASH_BITS)
39 
40 static struct list_head lockhash_table[LOCKHASH_SIZE];
41 
42 #define __lockhashfn(key) hash_long((unsigned long)key, LOCKHASH_BITS)
43 #define lockhashentry(key) (lockhash_table + __lockhashfn((key)))
44 
45 struct lock_stat {
46  struct list_head hash_entry;
47  struct rb_node rb; /* used for sorting */
48 
49  /*
50  * FIXME: perf_evsel__intval() returns u64,
51  * so address of lockdep_map should be dealed as 64bit.
52  * Is there more better solution?
53  */
54  void *addr; /* address of lockdep_map, used as ID */
55  char *name; /* for strcpy(), we cannot use const */
56 
57  unsigned int nr_acquire;
58  unsigned int nr_acquired;
59  unsigned int nr_contended;
60  unsigned int nr_release;
61 
62  unsigned int nr_readlock;
63  unsigned int nr_trylock;
64 
65  /* these times are in nano sec. */
70 
71  int discard; /* flag of blacklist */
72 };
73 
74 /*
75  * States of lock_seq_stat
76  *
77  * UNINITIALIZED is required for detecting first event of acquire.
78  * As the nature of lock events, there is no guarantee
79  * that the first event for the locks are acquire,
80  * it can be acquired, contended or release.
81  */
82 #define SEQ_STATE_UNINITIALIZED 0 /* initial state */
83 #define SEQ_STATE_RELEASED 1
84 #define SEQ_STATE_ACQUIRING 2
85 #define SEQ_STATE_ACQUIRED 3
86 #define SEQ_STATE_READ_ACQUIRED 4
87 #define SEQ_STATE_CONTENDED 5
88 
89 /*
90  * MAX_LOCK_DEPTH
91  * Imported from include/linux/sched.h.
92  * Should this be synchronized?
93  */
94 #define MAX_LOCK_DEPTH 48
95 
96 /*
97  * struct lock_seq_stat:
98  * Place to put on state of one lock sequence
99  * 1) acquire -> acquired -> release
100  * 2) acquire -> contended -> acquired -> release
101  * 3) acquire (with read or try) -> release
102  * 4) Are there other patterns?
103  */
105  struct list_head list;
106  int state;
108  void *addr;
109 
111 };
112 
113 struct thread_stat {
114  struct rb_node rb;
115 
116  u32 tid;
117  struct list_head seq_list;
118 };
119 
120 static struct rb_root thread_stats;
121 
122 static struct thread_stat *thread_stat_find(u32 tid)
123 {
124  struct rb_node *node;
125  struct thread_stat *st;
126 
127  node = thread_stats.rb_node;
128  while (node) {
129  st = container_of(node, struct thread_stat, rb);
130  if (st->tid == tid)
131  return st;
132  else if (tid < st->tid)
133  node = node->rb_left;
134  else
135  node = node->rb_right;
136  }
137 
138  return NULL;
139 }
140 
141 static void thread_stat_insert(struct thread_stat *new)
142 {
143  struct rb_node **rb = &thread_stats.rb_node;
144  struct rb_node *parent = NULL;
145  struct thread_stat *p;
146 
147  while (*rb) {
148  p = container_of(*rb, struct thread_stat, rb);
149  parent = *rb;
150 
151  if (new->tid < p->tid)
152  rb = &(*rb)->rb_left;
153  else if (new->tid > p->tid)
154  rb = &(*rb)->rb_right;
155  else
156  BUG_ON("inserting invalid thread_stat\n");
157  }
158 
159  rb_link_node(&new->rb, parent, rb);
160  rb_insert_color(&new->rb, &thread_stats);
161 }
162 
164 {
165  struct thread_stat *st;
166 
167  st = thread_stat_find(tid);
168  if (st)
169  return st;
170 
171  st = zalloc(sizeof(struct thread_stat));
172  if (!st) {
173  pr_err("memory allocation failed\n");
174  return NULL;
175  }
176 
177  st->tid = tid;
178  INIT_LIST_HEAD(&st->seq_list);
179 
180  thread_stat_insert(st);
181 
182  return st;
183 }
184 
185 static struct thread_stat *thread_stat_findnew_first(u32 tid);
186 static struct thread_stat *(*thread_stat_findnew)(u32 tid) =
187  thread_stat_findnew_first;
188 
190 {
191  struct thread_stat *st;
192 
193  st = zalloc(sizeof(struct thread_stat));
194  if (!st) {
195  pr_err("memory allocation failed\n");
196  return NULL;
197  }
198  st->tid = tid;
199  INIT_LIST_HEAD(&st->seq_list);
200 
201  rb_link_node(&st->rb, NULL, &thread_stats.rb_node);
202  rb_insert_color(&st->rb, &thread_stats);
203 
205  return st;
206 }
207 
208 /* build simple key function one is bigger than two */
209 #define SINGLE_KEY(member) \
210  static int lock_stat_key_ ## member(struct lock_stat *one, \
211  struct lock_stat *two) \
212  { \
213  return one->member > two->member; \
214  }
215 
221 
222 static int lock_stat_key_wait_time_min(struct lock_stat *one,
223  struct lock_stat *two)
224 {
225  u64 s1 = one->wait_time_min;
226  u64 s2 = two->wait_time_min;
227  if (s1 == ULLONG_MAX)
228  s1 = 0;
229  if (s2 == ULLONG_MAX)
230  s2 = 0;
231  return s1 > s2;
232 }
233 
234 struct lock_key {
235  /*
236  * name: the value for specify by user
237  * this should be simpler than raw name of member
238  * e.g. nr_acquired -> acquired, wait_time_total -> wait_total
239  */
240  const char *name;
241  int (*key)(struct lock_stat*, struct lock_stat*);
242 };
243 
244 static const char *sort_key = "acquired";
245 
246 static int (*compare)(struct lock_stat *, struct lock_stat *);
247 
248 static struct rb_root result; /* place to store sorted data */
249 
250 #define DEF_KEY_LOCK(name, fn_suffix) \
251  { #name, lock_stat_key_ ## fn_suffix }
252 struct lock_key keys[] = {
253  DEF_KEY_LOCK(acquired, nr_acquired),
254  DEF_KEY_LOCK(contended, nr_contended),
255  DEF_KEY_LOCK(avg_wait, avg_wait_time),
256  DEF_KEY_LOCK(wait_total, wait_time_total),
257  DEF_KEY_LOCK(wait_min, wait_time_min),
258  DEF_KEY_LOCK(wait_max, wait_time_max),
259 
260  /* extra comparisons much complicated should be here */
261 
262  { NULL, NULL }
263 };
264 
265 static int select_key(void)
266 {
267  int i;
268 
269  for (i = 0; keys[i].name; i++) {
270  if (!strcmp(keys[i].name, sort_key)) {
271  compare = keys[i].key;
272  return 0;
273  }
274  }
275 
276  pr_err("Unknown compare key: %s\n", sort_key);
277 
278  return -1;
279 }
280 
281 static void insert_to_result(struct lock_stat *st,
282  int (*bigger)(struct lock_stat *, struct lock_stat *))
283 {
284  struct rb_node **rb = &result.rb_node;
285  struct rb_node *parent = NULL;
286  struct lock_stat *p;
287 
288  while (*rb) {
289  p = container_of(*rb, struct lock_stat, rb);
290  parent = *rb;
291 
292  if (bigger(st, p))
293  rb = &(*rb)->rb_left;
294  else
295  rb = &(*rb)->rb_right;
296  }
297 
298  rb_link_node(&st->rb, parent, rb);
299  rb_insert_color(&st->rb, &result);
300 }
301 
302 /* returns left most element of result, and erase it */
303 static struct lock_stat *pop_from_result(void)
304 {
305  struct rb_node *node = result.rb_node;
306 
307  if (!node)
308  return NULL;
309 
310  while (node->rb_left)
311  node = node->rb_left;
312 
313  rb_erase(node, &result);
314  return container_of(node, struct lock_stat, rb);
315 }
316 
317 static struct lock_stat *lock_stat_findnew(void *addr, const char *name)
318 {
319  struct list_head *entry = lockhashentry(addr);
320  struct lock_stat *ret, *new;
321 
322  list_for_each_entry(ret, entry, hash_entry) {
323  if (ret->addr == addr)
324  return ret;
325  }
326 
327  new = zalloc(sizeof(struct lock_stat));
328  if (!new)
329  goto alloc_failed;
330 
331  new->addr = addr;
332  new->name = zalloc(sizeof(char) * strlen(name) + 1);
333  if (!new->name) {
334  free(new);
335  goto alloc_failed;
336  }
337 
338  strcpy(new->name, name);
339  new->wait_time_min = ULLONG_MAX;
340 
341  list_add(&new->hash_entry, entry);
342  return new;
343 
344 alloc_failed:
345  pr_err("memory allocation failed\n");
346  return NULL;
347 }
348 
350  int (*acquire_event)(struct perf_evsel *evsel,
351  struct perf_sample *sample);
352 
353  int (*acquired_event)(struct perf_evsel *evsel,
354  struct perf_sample *sample);
355 
356  int (*contended_event)(struct perf_evsel *evsel,
357  struct perf_sample *sample);
358 
359  int (*release_event)(struct perf_evsel *evsel,
360  struct perf_sample *sample);
361 };
362 
363 static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr)
364 {
365  struct lock_seq_stat *seq;
366 
367  list_for_each_entry(seq, &ts->seq_list, list) {
368  if (seq->addr == addr)
369  return seq;
370  }
371 
372  seq = zalloc(sizeof(struct lock_seq_stat));
373  if (!seq) {
374  pr_err("memory allocation failed\n");
375  return NULL;
376  }
378  seq->addr = addr;
379 
380  list_add(&seq->list, &ts->seq_list);
381  return seq;
382 }
383 
390 };
391 
392 static int bad_hist[BROKEN_MAX];
393 
395  TRY_LOCK = 1,
397 };
398 
399 static int report_lock_acquire_event(struct perf_evsel *evsel,
400  struct perf_sample *sample)
401 {
402  void *addr;
403  struct lock_stat *ls;
404  struct thread_stat *ts;
405  struct lock_seq_stat *seq;
406  const char *name = perf_evsel__strval(evsel, sample, "name");
407  u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
408  int flag = perf_evsel__intval(evsel, sample, "flag");
409 
410  memcpy(&addr, &tmp, sizeof(void *));
411 
412  ls = lock_stat_findnew(addr, name);
413  if (!ls)
414  return -ENOMEM;
415  if (ls->discard)
416  return 0;
417 
418  ts = thread_stat_findnew(sample->tid);
419  if (!ts)
420  return -ENOMEM;
421 
422  seq = get_seq(ts, addr);
423  if (!seq)
424  return -ENOMEM;
425 
426  switch (seq->state) {
428  case SEQ_STATE_RELEASED:
429  if (!flag) {
430  seq->state = SEQ_STATE_ACQUIRING;
431  } else {
432  if (flag & TRY_LOCK)
433  ls->nr_trylock++;
434  if (flag & READ_LOCK)
435  ls->nr_readlock++;
437  seq->read_count = 1;
438  ls->nr_acquired++;
439  }
440  break;
442  if (flag & READ_LOCK) {
443  seq->read_count++;
444  ls->nr_acquired++;
445  goto end;
446  } else {
447  goto broken;
448  }
449  break;
450  case SEQ_STATE_ACQUIRED:
451  case SEQ_STATE_ACQUIRING:
452  case SEQ_STATE_CONTENDED:
453 broken:
454  /* broken lock sequence, discard it */
455  ls->discard = 1;
456  bad_hist[BROKEN_ACQUIRE]++;
457  list_del(&seq->list);
458  free(seq);
459  goto end;
460  default:
461  BUG_ON("Unknown state of lock sequence found!\n");
462  break;
463  }
464 
465  ls->nr_acquire++;
466  seq->prev_event_time = sample->time;
467 end:
468  return 0;
469 }
470 
471 static int report_lock_acquired_event(struct perf_evsel *evsel,
472  struct perf_sample *sample)
473 {
474  void *addr;
475  struct lock_stat *ls;
476  struct thread_stat *ts;
477  struct lock_seq_stat *seq;
478  u64 contended_term;
479  const char *name = perf_evsel__strval(evsel, sample, "name");
480  u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
481 
482  memcpy(&addr, &tmp, sizeof(void *));
483 
484  ls = lock_stat_findnew(addr, name);
485  if (!ls)
486  return -ENOMEM;
487  if (ls->discard)
488  return 0;
489 
490  ts = thread_stat_findnew(sample->tid);
491  if (!ts)
492  return -ENOMEM;
493 
494  seq = get_seq(ts, addr);
495  if (!seq)
496  return -ENOMEM;
497 
498  switch (seq->state) {
500  /* orphan event, do nothing */
501  return 0;
502  case SEQ_STATE_ACQUIRING:
503  break;
504  case SEQ_STATE_CONTENDED:
505  contended_term = sample->time - seq->prev_event_time;
506  ls->wait_time_total += contended_term;
507  if (contended_term < ls->wait_time_min)
508  ls->wait_time_min = contended_term;
509  if (ls->wait_time_max < contended_term)
510  ls->wait_time_max = contended_term;
511  break;
512  case SEQ_STATE_RELEASED:
513  case SEQ_STATE_ACQUIRED:
515  /* broken lock sequence, discard it */
516  ls->discard = 1;
517  bad_hist[BROKEN_ACQUIRED]++;
518  list_del(&seq->list);
519  free(seq);
520  goto end;
521  default:
522  BUG_ON("Unknown state of lock sequence found!\n");
523  break;
524  }
525 
526  seq->state = SEQ_STATE_ACQUIRED;
527  ls->nr_acquired++;
528  ls->avg_wait_time = ls->nr_contended ? ls->wait_time_total/ls->nr_contended : 0;
529  seq->prev_event_time = sample->time;
530 end:
531  return 0;
532 }
533 
534 static int report_lock_contended_event(struct perf_evsel *evsel,
535  struct perf_sample *sample)
536 {
537  void *addr;
538  struct lock_stat *ls;
539  struct thread_stat *ts;
540  struct lock_seq_stat *seq;
541  const char *name = perf_evsel__strval(evsel, sample, "name");
542  u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
543 
544  memcpy(&addr, &tmp, sizeof(void *));
545 
546  ls = lock_stat_findnew(addr, name);
547  if (!ls)
548  return -ENOMEM;
549  if (ls->discard)
550  return 0;
551 
552  ts = thread_stat_findnew(sample->tid);
553  if (!ts)
554  return -ENOMEM;
555 
556  seq = get_seq(ts, addr);
557  if (!seq)
558  return -ENOMEM;
559 
560  switch (seq->state) {
562  /* orphan event, do nothing */
563  return 0;
564  case SEQ_STATE_ACQUIRING:
565  break;
566  case SEQ_STATE_RELEASED:
567  case SEQ_STATE_ACQUIRED:
569  case SEQ_STATE_CONTENDED:
570  /* broken lock sequence, discard it */
571  ls->discard = 1;
572  bad_hist[BROKEN_CONTENDED]++;
573  list_del(&seq->list);
574  free(seq);
575  goto end;
576  default:
577  BUG_ON("Unknown state of lock sequence found!\n");
578  break;
579  }
580 
581  seq->state = SEQ_STATE_CONTENDED;
582  ls->nr_contended++;
584  seq->prev_event_time = sample->time;
585 end:
586  return 0;
587 }
588 
589 static int report_lock_release_event(struct perf_evsel *evsel,
590  struct perf_sample *sample)
591 {
592  void *addr;
593  struct lock_stat *ls;
594  struct thread_stat *ts;
595  struct lock_seq_stat *seq;
596  const char *name = perf_evsel__strval(evsel, sample, "name");
597  u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
598 
599  memcpy(&addr, &tmp, sizeof(void *));
600 
601  ls = lock_stat_findnew(addr, name);
602  if (!ls)
603  return -ENOMEM;
604  if (ls->discard)
605  return 0;
606 
607  ts = thread_stat_findnew(sample->tid);
608  if (!ts)
609  return -ENOMEM;
610 
611  seq = get_seq(ts, addr);
612  if (!seq)
613  return -ENOMEM;
614 
615  switch (seq->state) {
617  goto end;
618  case SEQ_STATE_ACQUIRED:
619  break;
621  seq->read_count--;
622  BUG_ON(seq->read_count < 0);
623  if (!seq->read_count) {
624  ls->nr_release++;
625  goto end;
626  }
627  break;
628  case SEQ_STATE_ACQUIRING:
629  case SEQ_STATE_CONTENDED:
630  case SEQ_STATE_RELEASED:
631  /* broken lock sequence, discard it */
632  ls->discard = 1;
633  bad_hist[BROKEN_RELEASE]++;
634  goto free_seq;
635  default:
636  BUG_ON("Unknown state of lock sequence found!\n");
637  break;
638  }
639 
640  ls->nr_release++;
641 free_seq:
642  list_del(&seq->list);
643  free(seq);
644 end:
645  return 0;
646 }
647 
648 /* lock oriented handlers */
649 /* TODO: handlers for CPU oriented, thread oriented */
652  .acquired_event = report_lock_acquired_event,
653  .contended_event = report_lock_contended_event,
654  .release_event = report_lock_release_event,
655 };
656 
658 
660  struct perf_sample *sample)
661 {
662  if (trace_handler->acquire_event)
663  return trace_handler->acquire_event(evsel, sample);
664  return 0;
665 }
666 
668  struct perf_sample *sample)
669 {
670  if (trace_handler->acquired_event)
671  return trace_handler->acquired_event(evsel, sample);
672  return 0;
673 }
674 
676  struct perf_sample *sample)
677 {
678  if (trace_handler->contended_event)
679  return trace_handler->contended_event(evsel, sample);
680  return 0;
681 }
682 
684  struct perf_sample *sample)
685 {
686  if (trace_handler->release_event)
687  return trace_handler->release_event(evsel, sample);
688  return 0;
689 }
690 
691 static void print_bad_events(int bad, int total)
692 {
693  /* Output for debug, this have to be removed */
694  int i;
695  const char *name[4] =
696  { "acquire", "acquired", "contended", "release" };
697 
698  pr_info("\n=== output for debug===\n\n");
699  pr_info("bad: %d, total: %d\n", bad, total);
700  pr_info("bad rate: %.2f %%\n", (double)bad / (double)total * 100);
701  pr_info("histogram of events caused bad sequence\n");
702  for (i = 0; i < BROKEN_MAX; i++)
703  pr_info(" %10s: %d\n", name[i], bad_hist[i]);
704 }
705 
706 /* TODO: various way to print, coloring, nano or milli sec */
707 static void print_result(void)
708 {
709  struct lock_stat *st;
710  char cut_name[20];
711  int bad, total;
712 
713  pr_info("%20s ", "Name");
714  pr_info("%10s ", "acquired");
715  pr_info("%10s ", "contended");
716 
717  pr_info("%15s ", "avg wait (ns)");
718  pr_info("%15s ", "total wait (ns)");
719  pr_info("%15s ", "max wait (ns)");
720  pr_info("%15s ", "min wait (ns)");
721 
722  pr_info("\n\n");
723 
724  bad = total = 0;
725  while ((st = pop_from_result())) {
726  total++;
727  if (st->discard) {
728  bad++;
729  continue;
730  }
731  bzero(cut_name, 20);
732 
733  if (strlen(st->name) < 16) {
734  /* output raw name */
735  pr_info("%20s ", st->name);
736  } else {
737  strncpy(cut_name, st->name, 16);
738  cut_name[16] = '.';
739  cut_name[17] = '.';
740  cut_name[18] = '.';
741  cut_name[19] = '\0';
742  /* cut off name for saving output style */
743  pr_info("%20s ", cut_name);
744  }
745 
746  pr_info("%10u ", st->nr_acquired);
747  pr_info("%10u ", st->nr_contended);
748 
749  pr_info("%15" PRIu64 " ", st->avg_wait_time);
750  pr_info("%15" PRIu64 " ", st->wait_time_total);
751  pr_info("%15" PRIu64 " ", st->wait_time_max);
752  pr_info("%15" PRIu64 " ", st->wait_time_min == ULLONG_MAX ?
753  0 : st->wait_time_min);
754  pr_info("\n");
755  }
756 
757  print_bad_events(bad, total);
758 }
759 
760 static bool info_threads, info_map;
761 
762 static void dump_threads(void)
763 {
764  struct thread_stat *st;
765  struct rb_node *node;
766  struct thread *t;
767 
768  pr_info("%10s: comm\n", "Thread ID");
769 
770  node = rb_first(&thread_stats);
771  while (node) {
772  st = container_of(node, struct thread_stat, rb);
773  t = perf_session__findnew(session, st->tid);
774  pr_info("%10d: %s\n", st->tid, thread__comm_str(t));
775  node = rb_next(node);
776  thread__put(t);
777  };
778 }
779 
780 static void dump_map(void)
781 {
782  unsigned int i;
783  struct lock_stat *st;
784 
785  pr_info("Address of instance: name of class\n");
786  for (i = 0; i < LOCKHASH_SIZE; i++) {
787  list_for_each_entry(st, &lockhash_table[i], hash_entry) {
788  pr_info(" %p: %s\n", st->addr, st->name);
789  }
790  }
791 }
792 
793 static int dump_info(void)
794 {
795  int rc = 0;
796 
797  if (info_threads)
798  dump_threads();
799  else if (info_map)
800  dump_map();
801  else {
802  rc = -1;
803  pr_err("Unknown type of information\n");
804  }
805 
806  return rc;
807 }
808 
809 typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
810  struct perf_sample *sample);
811 
812 static int process_sample_event(struct perf_tool *tool __maybe_unused,
813  union perf_event *event,
814  struct perf_sample *sample,
815  struct perf_evsel *evsel,
816  struct machine *machine)
817 {
818  int err = 0;
819  struct thread *thread = machine__findnew_thread(machine, sample->pid,
820  sample->tid);
821 
822  if (thread == NULL) {
823  pr_debug("problem processing %d event, skipping it.\n",
824  event->header.type);
825  return -1;
826  }
827 
828  if (evsel->handler != NULL) {
829  tracepoint_handler f = evsel->handler;
830  err = f(evsel, sample);
831  }
832 
833  thread__put(thread);
834 
835  return err;
836 }
837 
838 static void sort_result(void)
839 {
840  unsigned int i;
841  struct lock_stat *st;
842 
843  for (i = 0; i < LOCKHASH_SIZE; i++) {
844  list_for_each_entry(st, &lockhash_table[i], hash_entry) {
846  }
847  }
848 }
849 
850 static const struct perf_evsel_str_handler lock_tracepoints[] = {
851  { "lock:lock_acquire", perf_evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */
852  { "lock:lock_acquired", perf_evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
853  { "lock:lock_contended", perf_evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
854  { "lock:lock_release", perf_evsel__process_lock_release, }, /* CONFIG_LOCKDEP */
855 };
856 
857 static bool force;
858 
859 static int __cmd_report(bool display_info)
860 {
861  int err = -EINVAL;
862  struct perf_tool eops = {
864  .comm = perf_event__process_comm,
865  .namespaces = perf_event__process_namespaces,
866  .ordered_events = true,
867  };
868  struct perf_data data = {
869  .file = {
870  .path = input_name,
871  },
872  .mode = PERF_DATA_MODE_READ,
873  .force = force,
874  };
875 
876  session = perf_session__new(&data, false, &eops);
877  if (!session) {
878  pr_err("Initializing perf session failed\n");
879  return -1;
880  }
881 
882  symbol__init(&session->header.env);
883 
884  if (!perf_session__has_traces(session, "lock record"))
885  goto out_delete;
886 
887  if (perf_session__set_tracepoints_handlers(session, lock_tracepoints)) {
888  pr_err("Initializing perf session tracepoint handlers failed\n");
889  goto out_delete;
890  }
891 
892  if (select_key())
893  goto out_delete;
894 
895  err = perf_session__process_events(session);
896  if (err)
897  goto out_delete;
898 
899  setup_pager();
900  if (display_info) /* used for info subcommand */
901  err = dump_info();
902  else {
903  sort_result();
904  print_result();
905  }
906 
907 out_delete:
908  perf_session__delete(session);
909  return err;
910 }
911 
912 static int __cmd_record(int argc, const char **argv)
913 {
914  const char *record_args[] = {
915  "record", "-R", "-m", "1024", "-c", "1",
916  };
917  unsigned int rec_argc, i, j, ret;
918  const char **rec_argv;
919 
920  for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) {
921  if (!is_valid_tracepoint(lock_tracepoints[i].name)) {
922  pr_err("tracepoint %s is not enabled. "
923  "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n",
924  lock_tracepoints[i].name);
925  return 1;
926  }
927  }
928 
929  rec_argc = ARRAY_SIZE(record_args) + argc - 1;
930  /* factor of 2 is for -e in front of each tracepoint */
931  rec_argc += 2 * ARRAY_SIZE(lock_tracepoints);
932 
933  rec_argv = calloc(rec_argc + 1, sizeof(char *));
934  if (!rec_argv)
935  return -ENOMEM;
936 
937  for (i = 0; i < ARRAY_SIZE(record_args); i++)
938  rec_argv[i] = strdup(record_args[i]);
939 
940  for (j = 0; j < ARRAY_SIZE(lock_tracepoints); j++) {
941  rec_argv[i++] = "-e";
942  rec_argv[i++] = strdup(lock_tracepoints[j].name);
943  }
944 
945  for (j = 1; j < (unsigned int)argc; j++, i++)
946  rec_argv[i] = argv[j];
947 
948  BUG_ON(i != rec_argc);
949 
950  ret = cmd_record(i, rec_argv);
951  free(rec_argv);
952  return ret;
953 }
954 
955 int cmd_lock(int argc, const char **argv)
956 {
957  const struct option lock_options[] = {
958  OPT_STRING('i', "input", &input_name, "file", "input file name"),
959  OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"),
960  OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"),
961  OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
962  OPT_END()
963  };
964 
965  const struct option info_options[] = {
966  OPT_BOOLEAN('t', "threads", &info_threads,
967  "dump thread list in perf.data"),
968  OPT_BOOLEAN('m', "map", &info_map,
969  "map of lock instances (address:name table)"),
970  OPT_PARENT(lock_options)
971  };
972 
973  const struct option report_options[] = {
974  OPT_STRING('k', "key", &sort_key, "acquired",
975  "key for sorting (acquired / contended / avg_wait / wait_total / wait_max / wait_min)"),
976  /* TODO: type */
977  OPT_PARENT(lock_options)
978  };
979 
980  const char * const info_usage[] = {
981  "perf lock info [<options>]",
982  NULL
983  };
984  const char *const lock_subcommands[] = { "record", "report", "script",
985  "info", NULL };
986  const char *lock_usage[] = {
987  NULL,
988  NULL
989  };
990  const char * const report_usage[] = {
991  "perf lock report [<options>]",
992  NULL
993  };
994  unsigned int i;
995  int rc = 0;
996 
997  for (i = 0; i < LOCKHASH_SIZE; i++)
998  INIT_LIST_HEAD(lockhash_table + i);
999 
1000  argc = parse_options_subcommand(argc, argv, lock_options, lock_subcommands,
1001  lock_usage, PARSE_OPT_STOP_AT_NON_OPTION);
1002  if (!argc)
1003  usage_with_options(lock_usage, lock_options);
1004 
1005  if (!strncmp(argv[0], "rec", 3)) {
1006  return __cmd_record(argc, argv);
1007  } else if (!strncmp(argv[0], "report", 6)) {
1008  trace_handler = &report_lock_ops;
1009  if (argc) {
1010  argc = parse_options(argc, argv,
1011  report_options, report_usage, 0);
1012  if (argc)
1013  usage_with_options(report_usage, report_options);
1014  }
1015  rc = __cmd_report(false);
1016  } else if (!strcmp(argv[0], "script")) {
1017  /* Aliased to 'perf script' */
1018  return cmd_script(argc, argv);
1019  } else if (!strcmp(argv[0], "info")) {
1020  if (argc) {
1021  argc = parse_options(argc, argv,
1022  info_options, info_usage, 0);
1023  if (argc)
1024  usage_with_options(info_usage, info_options);
1025  }
1026  /* recycling report_lock_ops */
1027  trace_handler = &report_lock_ops;
1028  rc = __cmd_report(true);
1029  } else {
1030  usage_with_options(lock_usage, lock_options);
1031  }
1032 
1033  return rc;
1034 }
static bool info_threads
Definition: builtin-lock.c:760
acquire_flags
Definition: builtin-lock.c:394
static struct thread_stat * thread_stat_findnew_first(u32 tid)
Definition: builtin-lock.c:189
Definition: mem2node.c:7
static int report_lock_acquired_event(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:471
struct lock_key keys[]
Definition: builtin-lock.c:252
int(* acquire_event)(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:350
#define SEQ_STATE_UNINITIALIZED
Definition: builtin-lock.c:82
static int report_lock_acquire_event(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:399
int cmd_record(int argc, const char **argv)
static bool info_map
Definition: builtin-lock.c:760
static int select_key(void)
Definition: builtin-lock.c:265
#define SEQ_STATE_RELEASED
Definition: builtin-lock.c:83
int int err
Definition: 5sec.c:44
struct list_head hash_entry
Definition: builtin-lock.c:46
static struct trace_lock_handler report_lock_ops
Definition: builtin-lock.c:650
static bool force
Definition: builtin-lock.c:857
static int bad_hist[BROKEN_MAX]
Definition: builtin-lock.c:392
#define LOCKHASH_SIZE
Definition: builtin-lock.c:38
struct perf_data_file file
Definition: data.h:18
static struct thread_stat *(* thread_stat_findnew)(u32 tid)
Definition: builtin-lock.c:186
struct rb_node rb
Definition: builtin-lock.c:47
struct perf_env env
Definition: header.h:82
static int perf_evsel__process_lock_release(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:683
static struct lock_stat * pop_from_result(void)
Definition: builtin-lock.c:303
static struct thread_stat * thread_stat_find(u32 tid)
Definition: builtin-lock.c:122
int perf_event__process_comm(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine)
Definition: event.c:1273
u64 wait_time_max
Definition: builtin-lock.c:69
static struct lock_seq_stat * get_seq(struct thread_stat *ts, void *addr)
Definition: builtin-lock.c:363
static int process_sample_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine)
Definition: builtin-lock.c:812
bool perf_session__has_traces(struct perf_session *session, const char *msg)
Definition: session.c:1963
#define pr_err(fmt,...)
Definition: json.h:21
int cmd_script(int argc, const char **argv)
static int perf_evsel__process_lock_acquire(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:659
unsigned int nr_release
Definition: builtin-lock.c:60
void perf_session__delete(struct perf_session *session)
Definition: session.c:187
unsigned int nr_acquired
Definition: builtin-lock.c:58
static void thread_stat_insert(struct thread_stat *new)
Definition: builtin-lock.c:141
static int dump_info(void)
Definition: builtin-lock.c:793
static struct lock_stat * lock_stat_findnew(void *addr, const char *name)
Definition: builtin-lock.c:317
static int lock_stat_key_wait_time_min(struct lock_stat *one, struct lock_stat *two)
Definition: builtin-lock.c:222
#define SEQ_STATE_READ_ACQUIRED
Definition: builtin-lock.c:86
static void insert_to_result(struct lock_stat *st, int(*bigger)(struct lock_stat *, struct lock_stat *))
Definition: builtin-lock.c:281
const char * key
Definition: bpf-loader.c:196
int(* acquired_event)(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:353
Definition: thread.h:18
static int perf_evsel__process_lock_acquired(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:667
#define SEQ_STATE_CONTENDED
Definition: builtin-lock.c:87
const char * thread__comm_str(const struct thread *thread)
Definition: thread.c:258
int(* key)(struct lock_stat *, struct lock_stat *)
Definition: builtin-lock.c:241
unsigned int nr_contended
Definition: builtin-lock.c:59
#define pr_debug(fmt,...)
Definition: json.h:27
Definition: tool.h:44
u64 wait_time_min
Definition: builtin-lock.c:68
static struct list_head lockhash_table[LOCKHASH_SIZE]
Definition: builtin-lock.c:40
static struct perf_session * session
Definition: builtin-lock.c:34
struct list_head list
Definition: builtin-lock.c:105
void * addr
Definition: builtin-lock.c:54
#define SEQ_STATE_ACQUIRING
Definition: builtin-lock.c:84
unsigned int nr_readlock
Definition: builtin-lock.c:62
int is_valid_tracepoint(const char *event_string)
static int entry(u64 ip, struct unwind_info *ui)
Definition: unwind-libdw.c:71
static char * perf_evsel__strval(struct perf_evsel *evsel, struct perf_sample *sample, const char *name)
Definition: evsel.h:292
static int report_lock_contended_event(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:534
static struct perf_tool tool
Definition: builtin-diff.c:362
struct rb_node rb
Definition: builtin-lock.c:114
u32 pid
Definition: event.h:193
u32 tid
Definition: event.h:193
const char * input_name
Definition: perf.c:40
static void print_bad_events(int bad, int total)
Definition: builtin-lock.c:691
#define new
Definition: util-cxx.h:20
#define event
u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, const char *name)
Definition: evsel.c:2722
Definition: data.h:17
x86 movsq based memcpy() in arch/x86/lib/memcpy_64.S") MEMCPY_FN(memcpy_erms
static void print_result(void)
Definition: builtin-lock.c:707
#define SEQ_STATE_ACQUIRED
Definition: builtin-lock.c:85
int cmd_lock(int argc, const char **argv)
Definition: builtin-lock.c:955
static const char * sort_key
Definition: builtin-lock.c:244
static void sort_result(void)
Definition: builtin-lock.c:838
static struct rb_root thread_stats
Definition: builtin-lock.c:120
struct perf_event_header header
Definition: event.h:624
static void dump_map(void)
Definition: builtin-lock.c:780
broken_state
Definition: builtin-lock.c:384
static int(* compare)(struct lock_stat *, struct lock_stat *)
Definition: builtin-lock.c:246
#define SINGLE_KEY(member)
Definition: builtin-lock.c:209
struct perf_session * perf_session__new(struct perf_data *data, bool repipe, struct perf_tool *tool)
Definition: session.c:116
#define lockhashentry(key)
Definition: builtin-lock.c:43
void thread__put(struct thread *thread)
Definition: thread.c:119
int perf_session__process_events(struct perf_session *session)
Definition: session.c:1945
static int report_lock_release_event(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:589
static struct trace_lock_handler * trace_handler
Definition: builtin-lock.c:657
static int __cmd_record(int argc, const char **argv)
Definition: builtin-lock.c:912
u64 time
Definition: event.h:194
unsigned int nr_trylock
Definition: builtin-lock.c:63
u64 avg_wait_time
Definition: builtin-lock.c:66
struct perf_header header
Definition: session.h:23
bool dump_trace
Definition: debug.c:27
int(* release_event)(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:359
int symbol__init(struct perf_env *env)
Definition: symbol.c:2112
#define pr_info(fmt,...)
Definition: json.h:24
void free(void *)
static FILE * f
Definition: intel-pt-log.c:30
int verbose
Definition: jevents.c:53
struct thread * perf_session__findnew(struct perf_session *session, pid_t pid)
Definition: session.c:1532
u64 wait_time_total
Definition: builtin-lock.c:67
const char * name
Definition: builtin-lock.c:240
struct list_head seq_list
Definition: builtin-lock.c:117
#define DEF_KEY_LOCK(name, fn_suffix)
Definition: builtin-lock.c:250
int(* tracepoint_handler)(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:809
static int __cmd_report(bool display_info)
Definition: builtin-lock.c:859
#define perf_session__set_tracepoints_handlers(session, array)
Definition: session.h:112
int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine)
Definition: event.c:1281
unsigned int nr_acquire
Definition: builtin-lock.c:57
static void dump_threads(void)
Definition: builtin-lock.c:762
static const struct perf_evsel_str_handler lock_tracepoints[]
Definition: builtin-lock.c:850
const char * path
Definition: data.h:13
int(* contended_event)(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:356
struct thread * machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid)
Definition: machine.c:492
static int perf_evsel__process_lock_contended(struct perf_evsel *evsel, struct perf_sample *sample)
Definition: builtin-lock.c:675
static struct thread_stat * thread_stat_findnew_after_first(u32 tid)
Definition: builtin-lock.c:163
char * name
Definition: builtin-lock.c:55
event_sample sample
Definition: tool.h:45
void static void * zalloc(size_t size)
Definition: util.h:20
void * handler
Definition: evsel.h:111