]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - kernel/lockdep_proc.c
lockstat: human readability tweaks
[linux-2.6-omap-h63xx.git] / kernel / lockdep_proc.c
1 /*
2  * kernel/lockdep_proc.c
3  *
4  * Runtime locking correctness validator
5  *
6  * Started by Ingo Molnar:
7  *
8  *  Copyright (C) 2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
9  *
10  * Code for /proc/lockdep and /proc/lockdep_stats:
11  *
12  */
13 #include <linux/module.h>
14 #include <linux/proc_fs.h>
15 #include <linux/seq_file.h>
16 #include <linux/kallsyms.h>
17 #include <linux/debug_locks.h>
18 #include <linux/vmalloc.h>
19 #include <linux/sort.h>
20 #include <asm/uaccess.h>
21 #include <asm/div64.h>
22
23 #include "lockdep_internals.h"
24
25 static void *l_next(struct seq_file *m, void *v, loff_t *pos)
26 {
27         struct lock_class *class = v;
28
29         (*pos)++;
30
31         if (class->lock_entry.next != &all_lock_classes)
32                 class = list_entry(class->lock_entry.next, struct lock_class,
33                                   lock_entry);
34         else
35                 class = NULL;
36         m->private = class;
37
38         return class;
39 }
40
41 static void *l_start(struct seq_file *m, loff_t *pos)
42 {
43         struct lock_class *class = m->private;
44
45         if (&class->lock_entry == all_lock_classes.next)
46                 seq_printf(m, "all lock classes:\n");
47
48         return class;
49 }
50
51 static void l_stop(struct seq_file *m, void *v)
52 {
53 }
54
55 static unsigned long count_forward_deps(struct lock_class *class)
56 {
57         struct lock_list *entry;
58         unsigned long ret = 1;
59
60         /*
61          * Recurse this class's dependency list:
62          */
63         list_for_each_entry(entry, &class->locks_after, entry)
64                 ret += count_forward_deps(entry->class);
65
66         return ret;
67 }
68
69 static unsigned long count_backward_deps(struct lock_class *class)
70 {
71         struct lock_list *entry;
72         unsigned long ret = 1;
73
74         /*
75          * Recurse this class's dependency list:
76          */
77         list_for_each_entry(entry, &class->locks_before, entry)
78                 ret += count_backward_deps(entry->class);
79
80         return ret;
81 }
82
83 static void print_name(struct seq_file *m, struct lock_class *class)
84 {
85         char str[128];
86         const char *name = class->name;
87
88         if (!name) {
89                 name = __get_key_name(class->key, str);
90                 seq_printf(m, "%s", name);
91         } else{
92                 seq_printf(m, "%s", name);
93                 if (class->name_version > 1)
94                         seq_printf(m, "#%d", class->name_version);
95                 if (class->subclass)
96                         seq_printf(m, "/%d", class->subclass);
97         }
98 }
99
100 static int l_show(struct seq_file *m, void *v)
101 {
102         unsigned long nr_forward_deps, nr_backward_deps;
103         struct lock_class *class = m->private;
104         struct lock_list *entry;
105         char c1, c2, c3, c4;
106
107         seq_printf(m, "%p", class->key);
108 #ifdef CONFIG_DEBUG_LOCKDEP
109         seq_printf(m, " OPS:%8ld", class->ops);
110 #endif
111         nr_forward_deps = count_forward_deps(class);
112         seq_printf(m, " FD:%5ld", nr_forward_deps);
113
114         nr_backward_deps = count_backward_deps(class);
115         seq_printf(m, " BD:%5ld", nr_backward_deps);
116
117         get_usage_chars(class, &c1, &c2, &c3, &c4);
118         seq_printf(m, " %c%c%c%c", c1, c2, c3, c4);
119
120         seq_printf(m, ": ");
121         print_name(m, class);
122         seq_puts(m, "\n");
123
124         list_for_each_entry(entry, &class->locks_after, entry) {
125                 if (entry->distance == 1) {
126                         seq_printf(m, " -> [%p] ", entry->class);
127                         print_name(m, entry->class);
128                         seq_puts(m, "\n");
129                 }
130         }
131         seq_puts(m, "\n");
132
133         return 0;
134 }
135
136 static const struct seq_operations lockdep_ops = {
137         .start  = l_start,
138         .next   = l_next,
139         .stop   = l_stop,
140         .show   = l_show,
141 };
142
143 static int lockdep_open(struct inode *inode, struct file *file)
144 {
145         int res = seq_open(file, &lockdep_ops);
146         if (!res) {
147                 struct seq_file *m = file->private_data;
148
149                 if (!list_empty(&all_lock_classes))
150                         m->private = list_entry(all_lock_classes.next,
151                                         struct lock_class, lock_entry);
152                 else
153                         m->private = NULL;
154         }
155         return res;
156 }
157
158 static const struct file_operations proc_lockdep_operations = {
159         .open           = lockdep_open,
160         .read           = seq_read,
161         .llseek         = seq_lseek,
162         .release        = seq_release,
163 };
164
165 static void lockdep_stats_debug_show(struct seq_file *m)
166 {
167 #ifdef CONFIG_DEBUG_LOCKDEP
168         unsigned int hi1 = debug_atomic_read(&hardirqs_on_events),
169                      hi2 = debug_atomic_read(&hardirqs_off_events),
170                      hr1 = debug_atomic_read(&redundant_hardirqs_on),
171                      hr2 = debug_atomic_read(&redundant_hardirqs_off),
172                      si1 = debug_atomic_read(&softirqs_on_events),
173                      si2 = debug_atomic_read(&softirqs_off_events),
174                      sr1 = debug_atomic_read(&redundant_softirqs_on),
175                      sr2 = debug_atomic_read(&redundant_softirqs_off);
176
177         seq_printf(m, " chain lookup misses:           %11u\n",
178                 debug_atomic_read(&chain_lookup_misses));
179         seq_printf(m, " chain lookup hits:             %11u\n",
180                 debug_atomic_read(&chain_lookup_hits));
181         seq_printf(m, " cyclic checks:                 %11u\n",
182                 debug_atomic_read(&nr_cyclic_checks));
183         seq_printf(m, " cyclic-check recursions:       %11u\n",
184                 debug_atomic_read(&nr_cyclic_check_recursions));
185         seq_printf(m, " find-mask forwards checks:     %11u\n",
186                 debug_atomic_read(&nr_find_usage_forwards_checks));
187         seq_printf(m, " find-mask forwards recursions: %11u\n",
188                 debug_atomic_read(&nr_find_usage_forwards_recursions));
189         seq_printf(m, " find-mask backwards checks:    %11u\n",
190                 debug_atomic_read(&nr_find_usage_backwards_checks));
191         seq_printf(m, " find-mask backwards recursions:%11u\n",
192                 debug_atomic_read(&nr_find_usage_backwards_recursions));
193
194         seq_printf(m, " hardirq on events:             %11u\n", hi1);
195         seq_printf(m, " hardirq off events:            %11u\n", hi2);
196         seq_printf(m, " redundant hardirq ons:         %11u\n", hr1);
197         seq_printf(m, " redundant hardirq offs:        %11u\n", hr2);
198         seq_printf(m, " softirq on events:             %11u\n", si1);
199         seq_printf(m, " softirq off events:            %11u\n", si2);
200         seq_printf(m, " redundant softirq ons:         %11u\n", sr1);
201         seq_printf(m, " redundant softirq offs:        %11u\n", sr2);
202 #endif
203 }
204
205 static int lockdep_stats_show(struct seq_file *m, void *v)
206 {
207         struct lock_class *class;
208         unsigned long nr_unused = 0, nr_uncategorized = 0,
209                       nr_irq_safe = 0, nr_irq_unsafe = 0,
210                       nr_softirq_safe = 0, nr_softirq_unsafe = 0,
211                       nr_hardirq_safe = 0, nr_hardirq_unsafe = 0,
212                       nr_irq_read_safe = 0, nr_irq_read_unsafe = 0,
213                       nr_softirq_read_safe = 0, nr_softirq_read_unsafe = 0,
214                       nr_hardirq_read_safe = 0, nr_hardirq_read_unsafe = 0,
215                       sum_forward_deps = 0, factor = 0;
216
217         list_for_each_entry(class, &all_lock_classes, lock_entry) {
218
219                 if (class->usage_mask == 0)
220                         nr_unused++;
221                 if (class->usage_mask == LOCKF_USED)
222                         nr_uncategorized++;
223                 if (class->usage_mask & LOCKF_USED_IN_IRQ)
224                         nr_irq_safe++;
225                 if (class->usage_mask & LOCKF_ENABLED_IRQS)
226                         nr_irq_unsafe++;
227                 if (class->usage_mask & LOCKF_USED_IN_SOFTIRQ)
228                         nr_softirq_safe++;
229                 if (class->usage_mask & LOCKF_ENABLED_SOFTIRQS)
230                         nr_softirq_unsafe++;
231                 if (class->usage_mask & LOCKF_USED_IN_HARDIRQ)
232                         nr_hardirq_safe++;
233                 if (class->usage_mask & LOCKF_ENABLED_HARDIRQS)
234                         nr_hardirq_unsafe++;
235                 if (class->usage_mask & LOCKF_USED_IN_IRQ_READ)
236                         nr_irq_read_safe++;
237                 if (class->usage_mask & LOCKF_ENABLED_IRQS_READ)
238                         nr_irq_read_unsafe++;
239                 if (class->usage_mask & LOCKF_USED_IN_SOFTIRQ_READ)
240                         nr_softirq_read_safe++;
241                 if (class->usage_mask & LOCKF_ENABLED_SOFTIRQS_READ)
242                         nr_softirq_read_unsafe++;
243                 if (class->usage_mask & LOCKF_USED_IN_HARDIRQ_READ)
244                         nr_hardirq_read_safe++;
245                 if (class->usage_mask & LOCKF_ENABLED_HARDIRQS_READ)
246                         nr_hardirq_read_unsafe++;
247
248                 sum_forward_deps += count_forward_deps(class);
249         }
250 #ifdef CONFIG_DEBUG_LOCKDEP
251         DEBUG_LOCKS_WARN_ON(debug_atomic_read(&nr_unused_locks) != nr_unused);
252 #endif
253         seq_printf(m, " lock-classes:                  %11lu [max: %lu]\n",
254                         nr_lock_classes, MAX_LOCKDEP_KEYS);
255         seq_printf(m, " direct dependencies:           %11lu [max: %lu]\n",
256                         nr_list_entries, MAX_LOCKDEP_ENTRIES);
257         seq_printf(m, " indirect dependencies:         %11lu\n",
258                         sum_forward_deps);
259
260         /*
261          * Total number of dependencies:
262          *
263          * All irq-safe locks may nest inside irq-unsafe locks,
264          * plus all the other known dependencies:
265          */
266         seq_printf(m, " all direct dependencies:       %11lu\n",
267                         nr_irq_unsafe * nr_irq_safe +
268                         nr_hardirq_unsafe * nr_hardirq_safe +
269                         nr_list_entries);
270
271         /*
272          * Estimated factor between direct and indirect
273          * dependencies:
274          */
275         if (nr_list_entries)
276                 factor = sum_forward_deps / nr_list_entries;
277
278 #ifdef CONFIG_PROVE_LOCKING
279         seq_printf(m, " dependency chains:             %11lu [max: %lu]\n",
280                         nr_lock_chains, MAX_LOCKDEP_CHAINS);
281 #endif
282
283 #ifdef CONFIG_TRACE_IRQFLAGS
284         seq_printf(m, " in-hardirq chains:             %11u\n",
285                         nr_hardirq_chains);
286         seq_printf(m, " in-softirq chains:             %11u\n",
287                         nr_softirq_chains);
288 #endif
289         seq_printf(m, " in-process chains:             %11u\n",
290                         nr_process_chains);
291         seq_printf(m, " stack-trace entries:           %11lu [max: %lu]\n",
292                         nr_stack_trace_entries, MAX_STACK_TRACE_ENTRIES);
293         seq_printf(m, " combined max dependencies:     %11u\n",
294                         (nr_hardirq_chains + 1) *
295                         (nr_softirq_chains + 1) *
296                         (nr_process_chains + 1)
297         );
298         seq_printf(m, " hardirq-safe locks:            %11lu\n",
299                         nr_hardirq_safe);
300         seq_printf(m, " hardirq-unsafe locks:          %11lu\n",
301                         nr_hardirq_unsafe);
302         seq_printf(m, " softirq-safe locks:            %11lu\n",
303                         nr_softirq_safe);
304         seq_printf(m, " softirq-unsafe locks:          %11lu\n",
305                         nr_softirq_unsafe);
306         seq_printf(m, " irq-safe locks:                %11lu\n",
307                         nr_irq_safe);
308         seq_printf(m, " irq-unsafe locks:              %11lu\n",
309                         nr_irq_unsafe);
310
311         seq_printf(m, " hardirq-read-safe locks:       %11lu\n",
312                         nr_hardirq_read_safe);
313         seq_printf(m, " hardirq-read-unsafe locks:     %11lu\n",
314                         nr_hardirq_read_unsafe);
315         seq_printf(m, " softirq-read-safe locks:       %11lu\n",
316                         nr_softirq_read_safe);
317         seq_printf(m, " softirq-read-unsafe locks:     %11lu\n",
318                         nr_softirq_read_unsafe);
319         seq_printf(m, " irq-read-safe locks:           %11lu\n",
320                         nr_irq_read_safe);
321         seq_printf(m, " irq-read-unsafe locks:         %11lu\n",
322                         nr_irq_read_unsafe);
323
324         seq_printf(m, " uncategorized locks:           %11lu\n",
325                         nr_uncategorized);
326         seq_printf(m, " unused locks:                  %11lu\n",
327                         nr_unused);
328         seq_printf(m, " max locking depth:             %11u\n",
329                         max_lockdep_depth);
330         seq_printf(m, " max recursion depth:           %11u\n",
331                         max_recursion_depth);
332         lockdep_stats_debug_show(m);
333         seq_printf(m, " debug_locks:                   %11u\n",
334                         debug_locks);
335
336         return 0;
337 }
338
339 static int lockdep_stats_open(struct inode *inode, struct file *file)
340 {
341         return single_open(file, lockdep_stats_show, NULL);
342 }
343
344 static const struct file_operations proc_lockdep_stats_operations = {
345         .open           = lockdep_stats_open,
346         .read           = seq_read,
347         .llseek         = seq_lseek,
348         .release        = seq_release,
349 };
350
351 #ifdef CONFIG_LOCK_STAT
352
353 struct lock_stat_data {
354         struct lock_class *class;
355         struct lock_class_stats stats;
356 };
357
358 struct lock_stat_seq {
359         struct lock_stat_data *iter;
360         struct lock_stat_data *iter_end;
361         struct lock_stat_data stats[MAX_LOCKDEP_KEYS];
362 };
363
364 /*
365  * sort on absolute number of contentions
366  */
367 static int lock_stat_cmp(const void *l, const void *r)
368 {
369         const struct lock_stat_data *dl = l, *dr = r;
370         unsigned long nl, nr;
371
372         nl = dl->stats.read_waittime.nr + dl->stats.write_waittime.nr;
373         nr = dr->stats.read_waittime.nr + dr->stats.write_waittime.nr;
374
375         return nr - nl;
376 }
377
378 static void seq_line(struct seq_file *m, char c, int offset, int length)
379 {
380         int i;
381
382         for (i = 0; i < offset; i++)
383                 seq_puts(m, " ");
384         for (i = 0; i < length; i++)
385                 seq_printf(m, "%c", c);
386         seq_puts(m, "\n");
387 }
388
389 static void snprint_time(char *buf, size_t bufsiz, s64 nr)
390 {
391         unsigned long rem;
392
393         rem = do_div(nr, 1000); /* XXX: do_div_signed */
394         snprintf(buf, bufsiz, "%lld.%02d", (long long)nr, ((int)rem+5)/10);
395 }
396
397 static void seq_time(struct seq_file *m, s64 time)
398 {
399         char num[15];
400
401         snprint_time(num, sizeof(num), time);
402         seq_printf(m, " %14s", num);
403 }
404
405 static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
406 {
407         seq_printf(m, "%14lu", lt->nr);
408         seq_time(m, lt->min);
409         seq_time(m, lt->max);
410         seq_time(m, lt->total);
411 }
412
413 static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
414 {
415         char name[39];
416         struct lock_class *class;
417         struct lock_class_stats *stats;
418         int i, namelen;
419
420         class = data->class;
421         stats = &data->stats;
422
423         snprintf(name, 38, "%s", class->name);
424         namelen = strlen(name);
425
426         if (stats->write_holdtime.nr) {
427                 if (stats->read_holdtime.nr)
428                         seq_printf(m, "%38s-W:", name);
429                 else
430                         seq_printf(m, "%40s:", name);
431
432                 seq_lock_time(m, &stats->write_waittime);
433                 seq_puts(m, " ");
434                 seq_lock_time(m, &stats->write_holdtime);
435                 seq_puts(m, "\n");
436         }
437
438         if (stats->read_holdtime.nr) {
439                 seq_printf(m, "%38s-R:", name);
440                 seq_lock_time(m, &stats->read_waittime);
441                 seq_puts(m, " ");
442                 seq_lock_time(m, &stats->read_holdtime);
443                 seq_puts(m, "\n");
444         }
445
446         if (stats->read_waittime.nr + stats->write_waittime.nr == 0)
447                 return;
448
449         if (stats->read_holdtime.nr)
450                 namelen += 2;
451
452         for (i = 0; i < ARRAY_SIZE(class->contention_point); i++) {
453                 char sym[KSYM_SYMBOL_LEN];
454                 char ip[32];
455
456                 if (class->contention_point[i] == 0)
457                         break;
458
459                 if (!i)
460                         seq_line(m, '-', 40-namelen, namelen);
461
462                 sprint_symbol(sym, class->contention_point[i]);
463                 snprintf(ip, sizeof(ip), "[<%p>]",
464                                 (void *)class->contention_point[i]);
465                 seq_printf(m, "%40s %14lu %29s %s\n", name,
466                                 stats->contention_point[i],
467                                 ip, sym);
468         }
469         if (i) {
470                 seq_puts(m, "\n");
471                 seq_line(m, '.', 0, 40 + 1 + 8 * (14 + 1));
472                 seq_puts(m, "\n");
473         }
474 }
475
476 static void seq_header(struct seq_file *m)
477 {
478         seq_printf(m, "lock_stat version 0.1\n");
479         seq_line(m, '-', 0, 40 + 1 + 8 * (14 + 1));
480         seq_printf(m, "%40s %14s %14s %14s %14s %14s %14s %14s %14s\n",
481                         "class name",
482                         "contentions",
483                         "waittime-min",
484                         "waittime-max",
485                         "waittime-total",
486                         "acquisitions",
487                         "holdtime-min",
488                         "holdtime-max",
489                         "holdtime-total");
490         seq_line(m, '-', 0, 40 + 1 + 8 * (14 + 1));
491         seq_printf(m, "\n");
492 }
493
494 static void *ls_start(struct seq_file *m, loff_t *pos)
495 {
496         struct lock_stat_seq *data = m->private;
497
498         if (data->iter == data->stats)
499                 seq_header(m);
500
501         return data->iter;
502 }
503
504 static void *ls_next(struct seq_file *m, void *v, loff_t *pos)
505 {
506         struct lock_stat_seq *data = m->private;
507
508         (*pos)++;
509
510         data->iter = v;
511         data->iter++;
512         if (data->iter == data->iter_end)
513                 data->iter = NULL;
514
515         return data->iter;
516 }
517
518 static void ls_stop(struct seq_file *m, void *v)
519 {
520 }
521
522 static int ls_show(struct seq_file *m, void *v)
523 {
524         struct lock_stat_seq *data = m->private;
525
526         seq_stats(m, data->iter);
527         return 0;
528 }
529
530 static struct seq_operations lockstat_ops = {
531         .start  = ls_start,
532         .next   = ls_next,
533         .stop   = ls_stop,
534         .show   = ls_show,
535 };
536
537 static int lock_stat_open(struct inode *inode, struct file *file)
538 {
539         int res;
540         struct lock_class *class;
541         struct lock_stat_seq *data = vmalloc(sizeof(struct lock_stat_seq));
542
543         if (!data)
544                 return -ENOMEM;
545
546         res = seq_open(file, &lockstat_ops);
547         if (!res) {
548                 struct lock_stat_data *iter = data->stats;
549                 struct seq_file *m = file->private_data;
550
551                 data->iter = iter;
552                 list_for_each_entry(class, &all_lock_classes, lock_entry) {
553                         iter->class = class;
554                         iter->stats = lock_stats(class);
555                         iter++;
556                 }
557                 data->iter_end = iter;
558
559                 sort(data->stats, data->iter_end - data->iter,
560                                 sizeof(struct lock_stat_data),
561                                 lock_stat_cmp, NULL);
562
563                 m->private = data;
564         } else
565                 vfree(data);
566
567         return res;
568 }
569
570 static ssize_t lock_stat_write(struct file *file, const char __user *buf,
571                                size_t count, loff_t *ppos)
572 {
573         struct lock_class *class;
574         char c;
575
576         if (count) {
577                 if (get_user(c, buf))
578                         return -EFAULT;
579
580                 if (c != '0')
581                         return count;
582
583                 list_for_each_entry(class, &all_lock_classes, lock_entry)
584                         clear_lock_stats(class);
585         }
586         return count;
587 }
588
589 static int lock_stat_release(struct inode *inode, struct file *file)
590 {
591         struct seq_file *seq = file->private_data;
592
593         vfree(seq->private);
594         seq->private = NULL;
595         return seq_release(inode, file);
596 }
597
598 static const struct file_operations proc_lock_stat_operations = {
599         .open           = lock_stat_open,
600         .write          = lock_stat_write,
601         .read           = seq_read,
602         .llseek         = seq_lseek,
603         .release        = lock_stat_release,
604 };
605 #endif /* CONFIG_LOCK_STAT */
606
607 static int __init lockdep_proc_init(void)
608 {
609         struct proc_dir_entry *entry;
610
611         entry = create_proc_entry("lockdep", S_IRUSR, NULL);
612         if (entry)
613                 entry->proc_fops = &proc_lockdep_operations;
614
615         entry = create_proc_entry("lockdep_stats", S_IRUSR, NULL);
616         if (entry)
617                 entry->proc_fops = &proc_lockdep_stats_operations;
618
619 #ifdef CONFIG_LOCK_STAT
620         entry = create_proc_entry("lock_stat", S_IRUSR, NULL);
621         if (entry)
622                 entry->proc_fops = &proc_lock_stat_operations;
623 #endif
624
625         return 0;
626 }
627
628 __initcall(lockdep_proc_init);
629