diff options
author | Dave Hansen <dave.hansen@linux.intel.com> | 2013-06-21 11:51:38 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-06-23 05:52:58 -0400 |
commit | 0c4df02d739fed5ab081b330d67403206dd3967e (patch) | |
tree | 37296c48ff5adef1537fd5c64977a3c1a0e8cff2 | |
parent | 14c63f17b1fde5a575a28e96547a22b451c71fb5 (diff) |
x86: Add NMI duration tracepoints
This patch has been invaluable in my adventures finding
issues in the perf NMI handler. I'm as big a fan of
printk() as anybody is, but using printk() in NMIs is
deadly when they're happening frequently.
Even hacking in trace_printk() ended up eating enough
CPU to throw off some of the measurements I was making.
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Acked-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: paulus@samba.org
Cc: acme@ghostprotocols.net
Cc: Dave Hansen <dave@sr71.net>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | Documentation/trace/events-nmi.txt | 43 | ||||
-rw-r--r-- | arch/x86/kernel/nmi.c | 9 | ||||
-rw-r--r-- | include/trace/events/nmi.h | 37 |
3 files changed, 87 insertions, 2 deletions
diff --git a/Documentation/trace/events-nmi.txt b/Documentation/trace/events-nmi.txt new file mode 100644 index 000000000000..c03c8c89f08d --- /dev/null +++ b/Documentation/trace/events-nmi.txt | |||
@@ -0,0 +1,43 @@ | |||
1 | NMI Trace Events | ||
2 | |||
3 | These events normally show up here: | ||
4 | |||
5 | /sys/kernel/debug/tracing/events/nmi | ||
6 | |||
7 | -- | ||
8 | |||
9 | nmi_handler: | ||
10 | |||
11 | You might want to use this tracepoint if you suspect that your | ||
12 | NMI handlers are hogging large amounts of CPU time. The kernel | ||
13 | will warn if it sees long-running handlers: | ||
14 | |||
15 | INFO: NMI handler took too long to run: 9.207 msecs | ||
16 | |||
17 | and this tracepoint will allow you to drill down and get some | ||
18 | more details. | ||
19 | |||
20 | Let's say you suspect that perf_event_nmi_handler() is causing | ||
21 | you some problems and you only want to trace that handler | ||
22 | specifically. You need to find its address: | ||
23 | |||
24 | $ grep perf_event_nmi_handler /proc/kallsyms | ||
25 | ffffffff81625600 t perf_event_nmi_handler | ||
26 | |||
27 | Let's also say you are only interested in when that function is | ||
28 | really hogging a lot of CPU time, like a millisecond at a time. | ||
29 | Note that the kernel's output is in milliseconds, but the input | ||
30 | to the filter is in nanoseconds! You can filter on 'delta_ns': | ||
31 | |||
32 | cd /sys/kernel/debug/tracing/events/nmi/nmi_handler | ||
33 | echo 'handler==0xffffffff81625600 && delta_ns>1000000' > filter | ||
34 | echo 1 > enable | ||
35 | |||
36 | Your output would then look like: | ||
37 | |||
38 | $ cat /sys/kernel/debug/tracing/trace_pipe | ||
39 | <idle>-0 [000] d.h3 505.397558: nmi_handler: perf_event_nmi_handler() delta_ns: 3236765 handled: 1 | ||
40 | <idle>-0 [000] d.h3 505.805893: nmi_handler: perf_event_nmi_handler() delta_ns: 3174234 handled: 1 | ||
41 | <idle>-0 [000] d.h3 506.158206: nmi_handler: perf_event_nmi_handler() delta_ns: 3084642 handled: 1 | ||
42 | <idle>-0 [000] d.h3 506.334346: nmi_handler: perf_event_nmi_handler() delta_ns: 3080351 handled: 1 | ||
43 | |||
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index e9bae4c2f2dd..0920212e6159 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c | |||
@@ -30,6 +30,9 @@ | |||
30 | #include <asm/nmi.h> | 30 | #include <asm/nmi.h> |
31 | #include <asm/x86_init.h> | 31 | #include <asm/x86_init.h> |
32 | 32 | ||
33 | #define CREATE_TRACE_POINTS | ||
34 | #include <trace/events/nmi.h> | ||
35 | |||
33 | struct nmi_desc { | 36 | struct nmi_desc { |
34 | spinlock_t lock; | 37 | spinlock_t lock; |
35 | struct list_head head; | 38 | struct list_head head; |
@@ -108,11 +111,13 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2 | |||
108 | */ | 111 | */ |
109 | list_for_each_entry_rcu(a, &desc->head, list) { | 112 | list_for_each_entry_rcu(a, &desc->head, list) { |
110 | u64 before, delta, whole_msecs; | 113 | u64 before, delta, whole_msecs; |
111 | int decimal_msecs; | 114 | int decimal_msecs, thishandled; |
112 | 115 | ||
113 | before = local_clock(); | 116 | before = local_clock(); |
114 | handled += a->handler(type, regs); | 117 | thishandled = a->handler(type, regs); |
118 | handled += thishandled; | ||
115 | delta = local_clock() - before; | 119 | delta = local_clock() - before; |
120 | trace_nmi_handler(a->handler, (int)delta, thishandled); | ||
116 | 121 | ||
117 | if (delta < nmi_longest_ns) | 122 | if (delta < nmi_longest_ns) |
118 | continue; | 123 | continue; |
diff --git a/include/trace/events/nmi.h b/include/trace/events/nmi.h new file mode 100644 index 000000000000..da3ee96b8d03 --- /dev/null +++ b/include/trace/events/nmi.h | |||
@@ -0,0 +1,37 @@ | |||
1 | #undef TRACE_SYSTEM | ||
2 | #define TRACE_SYSTEM nmi | ||
3 | |||
4 | #if !defined(_TRACE_NMI_H) || defined(TRACE_HEADER_MULTI_READ) | ||
5 | #define _TRACE_NMI_H | ||
6 | |||
7 | #include <linux/ktime.h> | ||
8 | #include <linux/tracepoint.h> | ||
9 | |||
10 | TRACE_EVENT(nmi_handler, | ||
11 | |||
12 | TP_PROTO(void *handler, s64 delta_ns, int handled), | ||
13 | |||
14 | TP_ARGS(handler, delta_ns, handled), | ||
15 | |||
16 | TP_STRUCT__entry( | ||
17 | __field( void *, handler ) | ||
18 | __field( s64, delta_ns) | ||
19 | __field( int, handled ) | ||
20 | ), | ||
21 | |||
22 | TP_fast_assign( | ||
23 | __entry->handler = handler; | ||
24 | __entry->delta_ns = delta_ns; | ||
25 | __entry->handled = handled; | ||
26 | ), | ||
27 | |||
28 | TP_printk("%ps() delta_ns: %lld handled: %d", | ||
29 | __entry->handler, | ||
30 | __entry->delta_ns, | ||
31 | __entry->handled) | ||
32 | ); | ||
33 | |||
34 | #endif /* _TRACE_NMI_H */ | ||
35 | |||
36 | /* This part ust be outside protection */ | ||
37 | #include <trace/define_trace.h> | ||