2 * latencytop.c: Latency display infrastructure
4 * (C) Copyright 2008 Intel Corporation
5 * Author: Arjan van de Ven <arjan@linux.intel.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; version 2
12 #include <linux/latencytop.h>
13 #include <linux/kallsyms.h>
14 #include <linux/seq_file.h>
15 #include <linux/notifier.h>
16 #include <linux/spinlock.h>
17 #include <linux/proc_fs.h>
18 #include <linux/module.h>
19 #include <linux/sched.h>
20 #include <linux/list.h>
21 #include <linux/slab.h>
22 #include <linux/stacktrace.h>
24 static DEFINE_SPINLOCK(latency_lock
);
27 static struct latency_record latency_record
[MAXLR
];
29 int latencytop_enabled
;
31 void clear_all_latency_tracing(struct task_struct
*p
)
35 if (!latencytop_enabled
)
38 spin_lock_irqsave(&latency_lock
, flags
);
39 memset(&p
->latency_record
, 0, sizeof(p
->latency_record
));
40 p
->latency_record_count
= 0;
41 spin_unlock_irqrestore(&latency_lock
, flags
);
44 static void clear_global_latency_tracing(void)
48 spin_lock_irqsave(&latency_lock
, flags
);
49 memset(&latency_record
, 0, sizeof(latency_record
));
50 spin_unlock_irqrestore(&latency_lock
, flags
);
54 account_global_scheduler_latency(struct task_struct
*tsk
, struct latency_record
*lat
)
56 int firstnonnull
= MAXLR
+ 1;
59 if (!latencytop_enabled
)
62 /* skip kernel threads for now */
66 for (i
= 0; i
< MAXLR
; i
++) {
70 if (!latency_record
[i
].backtrace
[0]) {
75 for (q
= 0 ; q
< LT_BACKTRACEDEPTH
; q
++) {
76 if (latency_record
[i
].backtrace
[q
] !=
79 if (same
&& lat
->backtrace
[q
] == 0)
81 if (same
&& lat
->backtrace
[q
] == ULONG_MAX
)
85 latency_record
[i
].count
++;
86 latency_record
[i
].time
+= lat
->time
;
87 if (lat
->time
> latency_record
[i
].max
)
88 latency_record
[i
].max
= lat
->time
;
97 /* Allocted a new one: */
98 memcpy(&latency_record
[i
], lat
, sizeof(struct latency_record
));
101 static inline void store_stacktrace(struct task_struct
*tsk
, struct latency_record
*lat
)
103 struct stack_trace trace
;
105 memset(&trace
, 0, sizeof(trace
));
106 trace
.max_entries
= LT_BACKTRACEDEPTH
;
107 trace
.entries
= &lat
->backtrace
[0];
109 save_stack_trace_tsk(tsk
, &trace
);
113 account_scheduler_latency(struct task_struct
*tsk
, int usecs
, int inter
)
117 struct latency_record lat
;
119 if (!latencytop_enabled
)
122 /* Long interruptible waits are generally user requested... */
123 if (inter
&& usecs
> 5000)
126 memset(&lat
, 0, sizeof(lat
));
130 store_stacktrace(tsk
, &lat
);
132 spin_lock_irqsave(&latency_lock
, flags
);
134 account_global_scheduler_latency(tsk
, &lat
);
137 * short term hack; if we're > 32 we stop; future we recycle:
139 tsk
->latency_record_count
++;
140 if (tsk
->latency_record_count
>= LT_SAVECOUNT
)
143 for (i
= 0; i
< LT_SAVECOUNT
; i
++) {
144 struct latency_record
*mylat
;
146 mylat
= &tsk
->latency_record
[i
];
147 for (q
= 0 ; q
< LT_BACKTRACEDEPTH
; q
++) {
148 if (mylat
->backtrace
[q
] !=
151 if (same
&& lat
.backtrace
[q
] == 0)
153 if (same
&& lat
.backtrace
[q
] == ULONG_MAX
)
158 mylat
->time
+= lat
.time
;
159 if (lat
.time
> mylat
->max
)
160 mylat
->max
= lat
.time
;
165 /* Allocated a new one: */
166 i
= tsk
->latency_record_count
;
167 memcpy(&tsk
->latency_record
[i
], &lat
, sizeof(struct latency_record
));
170 spin_unlock_irqrestore(&latency_lock
, flags
);
173 static int lstats_show(struct seq_file
*m
, void *v
)
177 seq_puts(m
, "Latency Top version : v0.1\n");
179 for (i
= 0; i
< MAXLR
; i
++) {
180 if (latency_record
[i
].backtrace
[0]) {
182 seq_printf(m
, "%i %li %li ",
183 latency_record
[i
].count
,
184 latency_record
[i
].time
,
185 latency_record
[i
].max
);
186 for (q
= 0; q
< LT_BACKTRACEDEPTH
; q
++) {
187 char sym
[KSYM_NAME_LEN
];
189 if (!latency_record
[i
].backtrace
[q
])
191 if (latency_record
[i
].backtrace
[q
] == ULONG_MAX
)
193 sprint_symbol(sym
, latency_record
[i
].backtrace
[q
]);
194 c
= strchr(sym
, '+');
197 seq_printf(m
, "%s ", sym
);
206 lstats_write(struct file
*file
, const char __user
*buf
, size_t count
,
209 clear_global_latency_tracing();
214 static int lstats_open(struct inode
*inode
, struct file
*filp
)
216 return single_open(filp
, lstats_show
, NULL
);
219 static struct file_operations lstats_fops
= {
222 .write
= lstats_write
,
224 .release
= single_release
,
227 static int __init
init_lstats_procfs(void)
229 struct proc_dir_entry
*pe
;
231 pe
= create_proc_entry("latency_stats", 0644, NULL
);
235 pe
->proc_fops
= &lstats_fops
;
239 __initcall(init_lstats_procfs
);