~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/perf/builtin-lock.c

Version: ~ [ linux-5.13-rc5 ] ~ [ linux-5.12.9 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.42 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.124 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.193 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.235 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.271 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.271 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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. */
 66         u64                     avg_wait_time;
 67         u64                     wait_time_total;
 68         u64                     wait_time_min;
 69         u64                     wait_time_max;
 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  */
104 struct lock_seq_stat {
105         struct list_head        list;
106         int                     state;
107         u64                     prev_event_time;
108         void                    *addr;
109 
110         int                     read_count;
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 
163 static struct thread_stat *thread_stat_findnew_after_first(u32 tid)
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 
189 static struct thread_stat *thread_stat_findnew_first(u32 tid)
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 
204         thread_stat_findnew = thread_stat_findnew_after_first;
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 
216 SINGLE_KEY(nr_acquired)
217 SINGLE_KEY(nr_contended)
218 SINGLE_KEY(avg_wait_time)
219 SINGLE_KEY(wait_time_total)
220 SINGLE_KEY(wait_time_max)
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 
349 struct trace_lock_handler {
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         }
377         seq->state = SEQ_STATE_UNINITIALIZED;
378         seq->addr = addr;
379 
380         list_add(&seq->list, &ts->seq_list);
381         return seq;
382 }
383 
384 enum broken_state {
385         BROKEN_ACQUIRE,
386         BROKEN_ACQUIRED,
387         BROKEN_CONTENDED,
388         BROKEN_RELEASE,
389         BROKEN_MAX,
390 };
391 
392 static int bad_hist[BROKEN_MAX];
393 
394 enum acquire_flags {
395         TRY_LOCK = 1,
396         READ_LOCK = 2,
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) {
427         case SEQ_STATE_UNINITIALIZED:
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++;
436                         seq->state = SEQ_STATE_READ_ACQUIRED;
437                         seq->read_count = 1;
438                         ls->nr_acquired++;
439                 }
440                 break;
441         case SEQ_STATE_READ_ACQUIRED:
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) {
499         case SEQ_STATE_UNINITIALIZED:
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:
514         case SEQ_STATE_READ_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) {
561         case SEQ_STATE_UNINITIALIZED:
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:
568         case SEQ_STATE_READ_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++;
583         ls->avg_wait_time = ls->wait_time_total/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) {
616         case SEQ_STATE_UNINITIALIZED:
617                 goto end;
618         case SEQ_STATE_ACQUIRED:
619                 break;
620         case SEQ_STATE_READ_ACQUIRED:
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 */
650 static struct trace_lock_handler report_lock_ops  = {
651         .acquire_event          = report_lock_acquire_event,
652         .acquired_event         = report_lock_acquired_event,
653         .contended_event        = report_lock_contended_event,
654         .release_event          = report_lock_release_event,
655 };
656 
657 static struct trace_lock_handler *trace_handler;
658 
659 static int perf_evsel__process_lock_acquire(struct perf_evsel *evsel,
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 
667 static int perf_evsel__process_lock_acquired(struct perf_evsel *evsel,
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 
675 static int perf_evsel__process_lock_contended(struct perf_evsel *evsel,
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 
683 static int perf_evsel__process_lock_release(struct perf_evsel *evsel,
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) {
845                         insert_to_result(st, compare);
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 = {
863                 .sample          = process_sample_event,
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 }
1035 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp