diff options
author | Ingo Molnar <mingo@elte.hu> | 2007-02-16 04:28:13 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-16 11:13:59 -0500 |
commit | 82f67cd9fca8c8762c15ba7ed0d5747588c1e221 (patch) | |
tree | 1ff7e5cc496580b85bb42fb1d7b19dcbef7b7776 /kernel/timer.c | |
parent | 8bfd9a7a229b5f3d3eda5d7d45c2eebec5b4ba16 (diff) |
[PATCH] Add debugging feature /proc/timer_stat
Add /proc/timer_stats support: debugging feature to profile timer expiration.
Both the starting site, process/PID and the expiration function is captured.
This allows the quick identification of timer event sources in a system.
Sample output:
# echo 1 > /proc/timer_stats
# cat /proc/timer_stats
Timer Stats Version: v0.1
Sample period: 4.010 s
24, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick)
11, 0 swapper sk_reset_timer (tcp_delack_timer)
6, 0 swapper hrtimer_stop_sched_tick (hrtimer_sched_tick)
2, 1 swapper queue_delayed_work_on (delayed_work_timer_fn)
17, 0 swapper hrtimer_restart_sched_tick (hrtimer_sched_tick)
2, 1 swapper queue_delayed_work_on (delayed_work_timer_fn)
4, 2050 pcscd do_nanosleep (hrtimer_wakeup)
5, 4179 sshd sk_reset_timer (tcp_write_timer)
4, 2248 yum-updatesd schedule_timeout (process_timeout)
18, 0 swapper hrtimer_restart_sched_tick (hrtimer_sched_tick)
3, 0 swapper sk_reset_timer (tcp_delack_timer)
1, 1 swapper neigh_table_init_no_netlink (neigh_periodic_timer)
2, 1 swapper e1000_up (e1000_watchdog)
1, 1 init schedule_timeout (process_timeout)
100 total events, 25.24 events/sec
[ cleanups and hrtimers support from Thomas Gleixner <tglx@linutronix.de> ]
[bunk@stusta.de: nr_entries can become static]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: john stultz <johnstul@us.ibm.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Cc: Andi Kleen <ak@suse.de>
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>
Diffstat (limited to 'kernel/timer.c')
-rw-r--r-- | kernel/timer.c | 31 |
1 files changed, 29 insertions, 2 deletions
diff --git a/kernel/timer.c b/kernel/timer.c index f058e6cfd50c..c3c67f3c0190 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/syscalls.h> | 35 | #include <linux/syscalls.h> |
36 | #include <linux/delay.h> | 36 | #include <linux/delay.h> |
37 | #include <linux/tick.h> | 37 | #include <linux/tick.h> |
38 | #include <linux/kallsyms.h> | ||
38 | 39 | ||
39 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
40 | #include <asm/unistd.h> | 41 | #include <asm/unistd.h> |
@@ -263,6 +264,18 @@ static void internal_add_timer(tvec_base_t *base, struct timer_list *timer) | |||
263 | list_add_tail(&timer->entry, vec); | 264 | list_add_tail(&timer->entry, vec); |
264 | } | 265 | } |
265 | 266 | ||
267 | #ifdef CONFIG_TIMER_STATS | ||
268 | void __timer_stats_timer_set_start_info(struct timer_list *timer, void *addr) | ||
269 | { | ||
270 | if (timer->start_site) | ||
271 | return; | ||
272 | |||
273 | timer->start_site = addr; | ||
274 | memcpy(timer->start_comm, current->comm, TASK_COMM_LEN); | ||
275 | timer->start_pid = current->pid; | ||
276 | } | ||
277 | #endif | ||
278 | |||
266 | /** | 279 | /** |
267 | * init_timer - initialize a timer. | 280 | * init_timer - initialize a timer. |
268 | * @timer: the timer to be initialized | 281 | * @timer: the timer to be initialized |
@@ -274,11 +287,16 @@ void fastcall init_timer(struct timer_list *timer) | |||
274 | { | 287 | { |
275 | timer->entry.next = NULL; | 288 | timer->entry.next = NULL; |
276 | timer->base = __raw_get_cpu_var(tvec_bases); | 289 | timer->base = __raw_get_cpu_var(tvec_bases); |
290 | #ifdef CONFIG_TIMER_STATS | ||
291 | timer->start_site = NULL; | ||
292 | timer->start_pid = -1; | ||
293 | memset(timer->start_comm, 0, TASK_COMM_LEN); | ||
294 | #endif | ||
277 | } | 295 | } |
278 | EXPORT_SYMBOL(init_timer); | 296 | EXPORT_SYMBOL(init_timer); |
279 | 297 | ||
280 | static inline void detach_timer(struct timer_list *timer, | 298 | static inline void detach_timer(struct timer_list *timer, |
281 | int clear_pending) | 299 | int clear_pending) |
282 | { | 300 | { |
283 | struct list_head *entry = &timer->entry; | 301 | struct list_head *entry = &timer->entry; |
284 | 302 | ||
@@ -325,6 +343,7 @@ int __mod_timer(struct timer_list *timer, unsigned long expires) | |||
325 | unsigned long flags; | 343 | unsigned long flags; |
326 | int ret = 0; | 344 | int ret = 0; |
327 | 345 | ||
346 | timer_stats_timer_set_start_info(timer); | ||
328 | BUG_ON(!timer->function); | 347 | BUG_ON(!timer->function); |
329 | 348 | ||
330 | base = lock_timer_base(timer, &flags); | 349 | base = lock_timer_base(timer, &flags); |
@@ -375,6 +394,7 @@ void add_timer_on(struct timer_list *timer, int cpu) | |||
375 | tvec_base_t *base = per_cpu(tvec_bases, cpu); | 394 | tvec_base_t *base = per_cpu(tvec_bases, cpu); |
376 | unsigned long flags; | 395 | unsigned long flags; |
377 | 396 | ||
397 | timer_stats_timer_set_start_info(timer); | ||
378 | BUG_ON(timer_pending(timer) || !timer->function); | 398 | BUG_ON(timer_pending(timer) || !timer->function); |
379 | spin_lock_irqsave(&base->lock, flags); | 399 | spin_lock_irqsave(&base->lock, flags); |
380 | timer->base = base; | 400 | timer->base = base; |
@@ -407,6 +427,7 @@ int mod_timer(struct timer_list *timer, unsigned long expires) | |||
407 | { | 427 | { |
408 | BUG_ON(!timer->function); | 428 | BUG_ON(!timer->function); |
409 | 429 | ||
430 | timer_stats_timer_set_start_info(timer); | ||
410 | /* | 431 | /* |
411 | * This is a common optimization triggered by the | 432 | * This is a common optimization triggered by the |
412 | * networking code - if the timer is re-modified | 433 | * networking code - if the timer is re-modified |
@@ -437,6 +458,7 @@ int del_timer(struct timer_list *timer) | |||
437 | unsigned long flags; | 458 | unsigned long flags; |
438 | int ret = 0; | 459 | int ret = 0; |
439 | 460 | ||
461 | timer_stats_timer_clear_start_info(timer); | ||
440 | if (timer_pending(timer)) { | 462 | if (timer_pending(timer)) { |
441 | base = lock_timer_base(timer, &flags); | 463 | base = lock_timer_base(timer, &flags); |
442 | if (timer_pending(timer)) { | 464 | if (timer_pending(timer)) { |
@@ -570,6 +592,8 @@ static inline void __run_timers(tvec_base_t *base) | |||
570 | fn = timer->function; | 592 | fn = timer->function; |
571 | data = timer->data; | 593 | data = timer->data; |
572 | 594 | ||
595 | timer_stats_account_timer(timer); | ||
596 | |||
573 | set_running_timer(base, timer); | 597 | set_running_timer(base, timer); |
574 | detach_timer(timer, 1); | 598 | detach_timer(timer, 1); |
575 | spin_unlock_irq(&base->lock); | 599 | spin_unlock_irq(&base->lock); |
@@ -1229,7 +1253,8 @@ static void run_timer_softirq(struct softirq_action *h) | |||
1229 | { | 1253 | { |
1230 | tvec_base_t *base = __get_cpu_var(tvec_bases); | 1254 | tvec_base_t *base = __get_cpu_var(tvec_bases); |
1231 | 1255 | ||
1232 | hrtimer_run_queues(); | 1256 | hrtimer_run_queues(); |
1257 | |||
1233 | if (time_after_eq(jiffies, base->timer_jiffies)) | 1258 | if (time_after_eq(jiffies, base->timer_jiffies)) |
1234 | __run_timers(base); | 1259 | __run_timers(base); |
1235 | } | 1260 | } |
@@ -1675,6 +1700,8 @@ void __init init_timers(void) | |||
1675 | int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE, | 1700 | int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE, |
1676 | (void *)(long)smp_processor_id()); | 1701 | (void *)(long)smp_processor_id()); |
1677 | 1702 | ||
1703 | init_timer_stats(); | ||
1704 | |||
1678 | BUG_ON(err == NOTIFY_BAD); | 1705 | BUG_ON(err == NOTIFY_BAD); |
1679 | register_cpu_notifier(&timers_nb); | 1706 | register_cpu_notifier(&timers_nb); |
1680 | open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL); | 1707 | open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL); |