aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2007-07-19 04:48:57 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-19 13:04:49 -0400
commitc46261de0d98372112d8edf16f74ce418a268d46 (patch)
treeb502a5872f3d4bd11254755defac0720ef072faf
parentf20786ff4da51e56b1956acf30be2552be266746 (diff)
lockstat: human readability tweaks
Present all this fancy new lock statistics information: *warning, _wide_ output ahead* (output edited for purpose of brevity) # cat /proc/lock_stat lock_stat version 0.1 ----------------------------------------------------------------------------------------------------------------------------------------------------------------- class name contentions waittime-min waittime-max waittime-total acquisitions holdtime-min holdtime-max holdtime-total ----------------------------------------------------------------------------------------------------------------------------------------------------------------- &inode->i_mutex: 14458 6.57 398832.75 2469412.23 6768876 0.34 11398383.65 339410830.89 --------------- &inode->i_mutex 4486 [<ffffffff802a08f9>] pipe_wait+0x86/0x8d &inode->i_mutex 0 [<ffffffff802a01e8>] pipe_write_fasync+0x29/0x5d &inode->i_mutex 0 [<ffffffff802a0e18>] pipe_read+0x74/0x3a5 &inode->i_mutex 0 [<ffffffff802a1a6a>] do_lookup+0x81/0x1ae ................................................................................................................................................................. &inode->i_data.tree_lock-W: 491 0.27 62.47 493.89 2477833 0.39 468.89 1146584.25 &inode->i_data.tree_lock-R: 65 0.44 4.27 48.78 26288792 0.36 184.62 10197458.24 -------------------------- &inode->i_data.tree_lock 46 [<ffffffff80277095>] __do_page_cache_readahead+0x69/0x24f &inode->i_data.tree_lock 31 [<ffffffff8026f9fb>] add_to_page_cache+0x31/0xba &inode->i_data.tree_lock 0 [<ffffffff802770ee>] __do_page_cache_readahead+0xc2/0x24f &inode->i_data.tree_lock 0 [<ffffffff8026f6e4>] find_get_page+0x1a/0x58 ................................................................................................................................................................. proc_inum_idr.lock: 0 0.00 0.00 0.00 36 0.00 65.60 148.26 proc_subdir_lock: 0 0.00 0.00 0.00 3049859 0.00 106.81 1563212.42 shrinker_rwsem-W: 0 0.00 0.00 0.00 5 0.00 1.73 3.68 shrinker_rwsem-R: 0 0.00 0.00 0.00 633 2.57 246.57 10909.76 'contentions' and 'acquisitions' are the number of such events measured (since the last reset). The waittime- and holdtime- (min, max, total) numbers are presented in microseconds. If there are any contention points, the lock class is presented in the block format (as i_mutex and tree_lock above), otherwise a single line of output is presented. The output is sorted on absolute number of contentions (read + write), this should get the worst offenders presented first, so that: # grep : /proc/lock_stat | head will quickly show who's bad. The stats can be reset using: # echo 0 > /proc/lock_stat [bunk@stusta.de: make 2 functions static] [akpm@linux-foundation.org: fix printk warning] Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Acked-by: Ingo Molnar <mingo@elte.hu> Acked-by: Jason Baron <jbaron@redhat.com> Signed-off-by: Adrian Bunk <bunk@stusta.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--kernel/lockdep.c44
-rw-r--r--kernel/lockdep_proc.c266
2 files changed, 310 insertions, 0 deletions
diff --git a/kernel/lockdep.c b/kernel/lockdep.c
index 70ca4db28aff..a8dc99d9fef7 100644
--- a/kernel/lockdep.c
+++ b/kernel/lockdep.c
@@ -149,6 +149,50 @@ static void lock_time_inc(struct lock_time *lt, s64 time)
149 lt->nr++; 149 lt->nr++;
150} 150}
151 151
152static inline void lock_time_add(struct lock_time *src, struct lock_time *dst)
153{
154 dst->min += src->min;
155 dst->max += src->max;
156 dst->total += src->total;
157 dst->nr += src->nr;
158}
159
160struct lock_class_stats lock_stats(struct lock_class *class)
161{
162 struct lock_class_stats stats;
163 int cpu, i;
164
165 memset(&stats, 0, sizeof(struct lock_class_stats));
166 for_each_possible_cpu(cpu) {
167 struct lock_class_stats *pcs =
168 &per_cpu(lock_stats, cpu)[class - lock_classes];
169
170 for (i = 0; i < ARRAY_SIZE(stats.contention_point); i++)
171 stats.contention_point[i] += pcs->contention_point[i];
172
173 lock_time_add(&pcs->read_waittime, &stats.read_waittime);
174 lock_time_add(&pcs->write_waittime, &stats.write_waittime);
175
176 lock_time_add(&pcs->read_holdtime, &stats.read_holdtime);
177 lock_time_add(&pcs->write_holdtime, &stats.write_holdtime);
178 }
179
180 return stats;
181}
182
183void clear_lock_stats(struct lock_class *class)
184{
185 int cpu;
186
187 for_each_possible_cpu(cpu) {
188 struct lock_class_stats *cpu_stats =
189 &per_cpu(lock_stats, cpu)[class - lock_classes];
190
191 memset(cpu_stats, 0, sizeof(struct lock_class_stats));
192 }
193 memset(class->contention_point, 0, sizeof(class->contention_point));
194}
195
152static struct lock_class_stats *get_lock_stats(struct lock_class *class) 196static struct lock_class_stats *get_lock_stats(struct lock_class *class)
153{ 197{
154 return &get_cpu_var(lock_stats)[class - lock_classes]; 198 return &get_cpu_var(lock_stats)[class - lock_classes];
diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c
index 2fde34127e2e..e682926c9ad6 100644
--- a/kernel/lockdep_proc.c
+++ b/kernel/lockdep_proc.c
@@ -15,6 +15,10 @@
15#include <linux/seq_file.h> 15#include <linux/seq_file.h>
16#include <linux/kallsyms.h> 16#include <linux/kallsyms.h>
17#include <linux/debug_locks.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>
18 22
19#include "lockdep_internals.h" 23#include "lockdep_internals.h"
20 24
@@ -344,6 +348,262 @@ static const struct file_operations proc_lockdep_stats_operations = {
344 .release = seq_release, 348 .release = seq_release,
345}; 349};
346 350
351#ifdef CONFIG_LOCK_STAT
352
353struct lock_stat_data {
354 struct lock_class *class;
355 struct lock_class_stats stats;
356};
357
358struct 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 */
367static 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
378static 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
389static 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
397static 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
405static 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
413static 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
476static 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
494static 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
504static 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
518static void ls_stop(struct seq_file *m, void *v)
519{
520}
521
522static 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
530static struct seq_operations lockstat_ops = {
531 .start = ls_start,
532 .next = ls_next,
533 .stop = ls_stop,
534 .show = ls_show,
535};
536
537static 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
570static 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
589static 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
598static 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
347static int __init lockdep_proc_init(void) 607static int __init lockdep_proc_init(void)
348{ 608{
349 struct proc_dir_entry *entry; 609 struct proc_dir_entry *entry;
@@ -356,6 +616,12 @@ static int __init lockdep_proc_init(void)
356 if (entry) 616 if (entry)
357 entry->proc_fops = &proc_lockdep_stats_operations; 617 entry->proc_fops = &proc_lockdep_stats_operations;
358 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
359 return 0; 625 return 0;
360} 626}
361 627