diff options
-rw-r--r-- | include/linux/ftrace.h | 2 | ||||
-rw-r--r-- | kernel/sysctl.c | 10 | ||||
-rw-r--r-- | kernel/trace/trace.c | 29 | ||||
-rw-r--r-- | scripts/tracing/draw_functrace.py | 130 |
4 files changed, 168 insertions, 3 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 703eb53cfa2b..899ec4b26b6c 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
@@ -181,6 +181,8 @@ static inline void __ftrace_enabled_restore(int enabled) | |||
181 | #endif | 181 | #endif |
182 | 182 | ||
183 | #ifdef CONFIG_TRACING | 183 | #ifdef CONFIG_TRACING |
184 | extern int ftrace_dump_on_oops; | ||
185 | |||
184 | extern void | 186 | extern void |
185 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3); | 187 | ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3); |
186 | 188 | ||
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 9d048fa2d902..6b6b727258b5 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -484,6 +484,16 @@ static struct ctl_table kern_table[] = { | |||
484 | .proc_handler = &ftrace_enable_sysctl, | 484 | .proc_handler = &ftrace_enable_sysctl, |
485 | }, | 485 | }, |
486 | #endif | 486 | #endif |
487 | #ifdef CONFIG_TRACING | ||
488 | { | ||
489 | .ctl_name = CTL_UNNUMBERED, | ||
490 | .procname = "ftrace_dump_on_opps", | ||
491 | .data = &ftrace_dump_on_oops, | ||
492 | .maxlen = sizeof(int), | ||
493 | .mode = 0644, | ||
494 | .proc_handler = &proc_dointvec, | ||
495 | }, | ||
496 | #endif | ||
487 | #ifdef CONFIG_MODULES | 497 | #ifdef CONFIG_MODULES |
488 | { | 498 | { |
489 | .ctl_name = KERN_MODPROBE, | 499 | .ctl_name = KERN_MODPROBE, |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index a610ca771558..8741e5c4313b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -64,6 +64,28 @@ static cpumask_t __read_mostly tracing_buffer_mask; | |||
64 | 64 | ||
65 | static int tracing_disabled = 1; | 65 | static int tracing_disabled = 1; |
66 | 66 | ||
67 | /* | ||
68 | * ftrace_dump_on_oops - variable to dump ftrace buffer on oops | ||
69 | * | ||
70 | * If there is an oops (or kernel panic) and the ftrace_dump_on_oops | ||
71 | * is set, then ftrace_dump is called. This will output the contents | ||
72 | * of the ftrace buffers to the console. This is very useful for | ||
73 | * capturing traces that lead to crashes and outputing it to a | ||
74 | * serial console. | ||
75 | * | ||
76 | * It is default off, but you can enable it with either specifying | ||
77 | * "ftrace_dump_on_oops" in the kernel command line, or setting | ||
78 | * /proc/sys/kernel/ftrace_dump_on_oops to true. | ||
79 | */ | ||
80 | int ftrace_dump_on_oops; | ||
81 | |||
82 | static int __init set_ftrace_dump_on_oops(char *str) | ||
83 | { | ||
84 | ftrace_dump_on_oops = 1; | ||
85 | return 1; | ||
86 | } | ||
87 | __setup("ftrace_dump_on_oops", set_ftrace_dump_on_oops); | ||
88 | |||
67 | long | 89 | long |
68 | ns2usecs(cycle_t nsec) | 90 | ns2usecs(cycle_t nsec) |
69 | { | 91 | { |
@@ -3020,7 +3042,8 @@ EXPORT_SYMBOL_GPL(__ftrace_printk); | |||
3020 | static int trace_panic_handler(struct notifier_block *this, | 3042 | static int trace_panic_handler(struct notifier_block *this, |
3021 | unsigned long event, void *unused) | 3043 | unsigned long event, void *unused) |
3022 | { | 3044 | { |
3023 | ftrace_dump(); | 3045 | if (ftrace_dump_on_oops) |
3046 | ftrace_dump(); | ||
3024 | return NOTIFY_OK; | 3047 | return NOTIFY_OK; |
3025 | } | 3048 | } |
3026 | 3049 | ||
@@ -3036,7 +3059,8 @@ static int trace_die_handler(struct notifier_block *self, | |||
3036 | { | 3059 | { |
3037 | switch (val) { | 3060 | switch (val) { |
3038 | case DIE_OOPS: | 3061 | case DIE_OOPS: |
3039 | ftrace_dump(); | 3062 | if (ftrace_dump_on_oops) |
3063 | ftrace_dump(); | ||
3040 | break; | 3064 | break; |
3041 | default: | 3065 | default: |
3042 | break; | 3066 | break; |
@@ -3077,7 +3101,6 @@ trace_printk_seq(struct trace_seq *s) | |||
3077 | trace_seq_reset(s); | 3101 | trace_seq_reset(s); |
3078 | } | 3102 | } |
3079 | 3103 | ||
3080 | |||
3081 | void ftrace_dump(void) | 3104 | void ftrace_dump(void) |
3082 | { | 3105 | { |
3083 | static DEFINE_SPINLOCK(ftrace_dump_lock); | 3106 | static DEFINE_SPINLOCK(ftrace_dump_lock); |
diff --git a/scripts/tracing/draw_functrace.py b/scripts/tracing/draw_functrace.py new file mode 100644 index 000000000000..902f9a992620 --- /dev/null +++ b/scripts/tracing/draw_functrace.py | |||
@@ -0,0 +1,130 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | """ | ||
4 | Copyright 2008 (c) Frederic Weisbecker <fweisbec@gmail.com> | ||
5 | Licensed under the terms of the GNU GPL License version 2 | ||
6 | |||
7 | This script parses a trace provided by the function tracer in | ||
8 | kernel/trace/trace_functions.c | ||
9 | The resulted trace is processed into a tree to produce a more human | ||
10 | view of the call stack by drawing textual but hierarchical tree of | ||
11 | calls. Only the functions's names and the the call time are provided. | ||
12 | |||
13 | Usage: | ||
14 | Be sure that you have CONFIG_FUNCTION_TRACER | ||
15 | # mkdir /debugfs | ||
16 | # mount -t debug debug /debug | ||
17 | # echo function > /debug/tracing/current_tracer | ||
18 | $ cat /debug/tracing/trace_pipe > ~/raw_trace_func | ||
19 | Wait some times but not too much, the script is a bit slow. | ||
20 | Break the pipe (Ctrl + Z) | ||
21 | $ scripts/draw_functrace.py < raw_trace_func > draw_functrace | ||
22 | Then you have your drawn trace in draw_functrace | ||
23 | """ | ||
24 | |||
25 | |||
26 | import sys, re | ||
27 | |||
28 | class CallTree: | ||
29 | """ This class provides a tree representation of the functions | ||
30 | call stack. If a function has no parent in the kernel (interrupt, | ||
31 | syscall, kernel thread...) then it is attached to a virtual parent | ||
32 | called ROOT. | ||
33 | """ | ||
34 | ROOT = None | ||
35 | |||
36 | def __init__(self, func, time = None, parent = None): | ||
37 | self._func = func | ||
38 | self._time = time | ||
39 | if parent is None: | ||
40 | self._parent = CallTree.ROOT | ||
41 | else: | ||
42 | self._parent = parent | ||
43 | self._children = [] | ||
44 | |||
45 | def calls(self, func, calltime): | ||
46 | """ If a function calls another one, call this method to insert it | ||
47 | into the tree at the appropriate place. | ||
48 | @return: A reference to the newly created child node. | ||
49 | """ | ||
50 | child = CallTree(func, calltime, self) | ||
51 | self._children.append(child) | ||
52 | return child | ||
53 | |||
54 | def getParent(self, func): | ||
55 | """ Retrieve the last parent of the current node that | ||
56 | has the name given by func. If this function is not | ||
57 | on a parent, then create it as new child of root | ||
58 | @return: A reference to the parent. | ||
59 | """ | ||
60 | tree = self | ||
61 | while tree != CallTree.ROOT and tree._func != func: | ||
62 | tree = tree._parent | ||
63 | if tree == CallTree.ROOT: | ||
64 | child = CallTree.ROOT.calls(func, None) | ||
65 | return child | ||
66 | return tree | ||
67 | |||
68 | def __repr__(self): | ||
69 | return self.__toString("", True) | ||
70 | |||
71 | def __toString(self, branch, lastChild): | ||
72 | if self._time is not None: | ||
73 | s = "%s----%s (%s)\n" % (branch, self._func, self._time) | ||
74 | else: | ||
75 | s = "%s----%s\n" % (branch, self._func) | ||
76 | |||
77 | i = 0 | ||
78 | if lastChild: | ||
79 | branch = branch[:-1] + " " | ||
80 | while i < len(self._children): | ||
81 | if i != len(self._children) - 1: | ||
82 | s += "%s" % self._children[i].__toString(branch +\ | ||
83 | " |", False) | ||
84 | else: | ||
85 | s += "%s" % self._children[i].__toString(branch +\ | ||
86 | " |", True) | ||
87 | i += 1 | ||
88 | return s | ||
89 | |||
90 | class BrokenLineException(Exception): | ||
91 | """If the last line is not complete because of the pipe breakage, | ||
92 | we want to stop the processing and ignore this line. | ||
93 | """ | ||
94 | pass | ||
95 | |||
96 | class CommentLineException(Exception): | ||
97 | """ If the line is a comment (as in the beginning of the trace file), | ||
98 | just ignore it. | ||
99 | """ | ||
100 | pass | ||
101 | |||
102 | |||
103 | def parseLine(line): | ||
104 | line = line.strip() | ||
105 | if line.startswith("#"): | ||
106 | raise CommentLineException | ||
107 | m = re.match("[^]]+?\\] +([0-9.]+): (\\w+) <-(\\w+)", line) | ||
108 | if m is None: | ||
109 | raise BrokenLineException | ||
110 | return (m.group(1), m.group(2), m.group(3)) | ||
111 | |||
112 | |||
113 | def main(): | ||
114 | CallTree.ROOT = CallTree("Root (Nowhere)", None, None) | ||
115 | tree = CallTree.ROOT | ||
116 | |||
117 | for line in sys.stdin: | ||
118 | try: | ||
119 | calltime, callee, caller = parseLine(line) | ||
120 | except BrokenLineException: | ||
121 | break | ||
122 | except CommentLineException: | ||
123 | continue | ||
124 | tree = tree.getParent(caller) | ||
125 | tree = tree.calls(callee, calltime) | ||
126 | |||
127 | print CallTree.ROOT | ||
128 | |||
129 | if __name__ == "__main__": | ||
130 | main() | ||