]>
Commit | Line | Data |
---|---|---|
fb52607a FW |
1 | /* |
2 | * | |
3 | * Function graph tracer. | |
4 | * Copyright (c) 2008 Frederic Weisbecker <fweisbec@gmail.com> | |
5 | * Mostly borrowed from function tracer which | |
6 | * is Copyright (c) Steven Rostedt <srostedt@redhat.com> | |
7 | * | |
8 | */ | |
9 | #include <linux/debugfs.h> | |
10 | #include <linux/uaccess.h> | |
11 | #include <linux/ftrace.h> | |
12 | #include <linux/fs.h> | |
13 | ||
14 | #include "trace.h" | |
15 | ||
287b6e68 | 16 | #define TRACE_GRAPH_INDENT 2 |
fb52607a FW |
17 | |
18 | #define TRACE_GRAPH_PRINT_OVERRUN 0x1 | |
19 | static struct tracer_opt trace_opts[] = { | |
20 | /* Display overruns or not */ | |
21 | { TRACER_OPT(overrun, TRACE_GRAPH_PRINT_OVERRUN) }, | |
22 | { } /* Empty entry */ | |
23 | }; | |
24 | ||
25 | static struct tracer_flags tracer_flags = { | |
26 | .val = 0, /* Don't display overruns by default */ | |
27 | .opts = trace_opts | |
28 | }; | |
29 | ||
287b6e68 FW |
30 | /* pid on the last trace processed */ |
31 | static pid_t last_pid = -1; | |
fb52607a FW |
32 | |
33 | static int graph_trace_init(struct trace_array *tr) | |
34 | { | |
35 | int cpu; | |
36 | for_each_online_cpu(cpu) | |
37 | tracing_reset(tr, cpu); | |
38 | ||
287b6e68 FW |
39 | return register_ftrace_graph(&trace_graph_return, |
40 | &trace_graph_entry); | |
fb52607a FW |
41 | } |
42 | ||
43 | static void graph_trace_reset(struct trace_array *tr) | |
44 | { | |
45 | unregister_ftrace_graph(); | |
46 | } | |
47 | ||
287b6e68 FW |
48 | /* If the pid changed since the last trace, output this event */ |
49 | static int verif_pid(struct trace_seq *s, pid_t pid) | |
50 | { | |
51 | if (last_pid != -1 && last_pid == pid) | |
52 | return 1; | |
fb52607a | 53 | |
287b6e68 FW |
54 | last_pid = pid; |
55 | return trace_seq_printf(s, "\n------------8<---------- thread %d" | |
56 | " ------------8<----------\n\n", | |
57 | pid); | |
58 | } | |
59 | ||
60 | static enum print_line_t | |
61 | print_graph_entry(struct ftrace_graph_ent *call, struct trace_seq *s, | |
62 | struct trace_entry *ent) | |
fb52607a | 63 | { |
287b6e68 | 64 | int i; |
fb52607a FW |
65 | int ret; |
66 | ||
287b6e68 FW |
67 | if (!verif_pid(s, ent->pid)) |
68 | return TRACE_TYPE_PARTIAL_LINE; | |
fb52607a | 69 | |
287b6e68 FW |
70 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { |
71 | ret = trace_seq_printf(s, " "); | |
fb52607a FW |
72 | if (!ret) |
73 | return TRACE_TYPE_PARTIAL_LINE; | |
287b6e68 FW |
74 | } |
75 | ||
76 | ret = seq_print_ip_sym(s, call->func, 0); | |
77 | if (!ret) | |
78 | return TRACE_TYPE_PARTIAL_LINE; | |
79 | ||
80 | ret = trace_seq_printf(s, "() {\n"); | |
81 | if (!ret) | |
82 | return TRACE_TYPE_PARTIAL_LINE; | |
83 | return TRACE_TYPE_HANDLED; | |
84 | } | |
85 | ||
86 | static enum print_line_t | |
87 | print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |
88 | struct trace_entry *ent) | |
89 | { | |
90 | int i; | |
91 | int ret; | |
92 | ||
93 | if (!verif_pid(s, ent->pid)) | |
94 | return TRACE_TYPE_PARTIAL_LINE; | |
fb52607a | 95 | |
287b6e68 FW |
96 | for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) { |
97 | ret = trace_seq_printf(s, " "); | |
fb52607a FW |
98 | if (!ret) |
99 | return TRACE_TYPE_PARTIAL_LINE; | |
287b6e68 FW |
100 | } |
101 | ||
102 | ret = trace_seq_printf(s, "} "); | |
103 | if (!ret) | |
104 | return TRACE_TYPE_PARTIAL_LINE; | |
fb52607a | 105 | |
287b6e68 FW |
106 | ret = trace_seq_printf(s, "%llu\n", trace->rettime - trace->calltime); |
107 | if (!ret) | |
108 | return TRACE_TYPE_PARTIAL_LINE; | |
fb52607a | 109 | |
287b6e68 FW |
110 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { |
111 | ret = trace_seq_printf(s, " (Overruns: %lu)\n", | |
112 | trace->overrun); | |
fb52607a FW |
113 | if (!ret) |
114 | return TRACE_TYPE_PARTIAL_LINE; | |
287b6e68 FW |
115 | } |
116 | return TRACE_TYPE_HANDLED; | |
117 | } | |
118 | ||
119 | enum print_line_t | |
120 | print_graph_function(struct trace_iterator *iter) | |
121 | { | |
122 | struct trace_seq *s = &iter->seq; | |
123 | struct trace_entry *entry = iter->ent; | |
fb52607a | 124 | |
287b6e68 FW |
125 | switch (entry->type) { |
126 | case TRACE_GRAPH_ENT: { | |
127 | struct ftrace_graph_ent_entry *field; | |
128 | trace_assign_type(field, entry); | |
129 | return print_graph_entry(&field->graph_ent, s, entry); | |
130 | } | |
131 | case TRACE_GRAPH_RET: { | |
132 | struct ftrace_graph_ret_entry *field; | |
133 | trace_assign_type(field, entry); | |
134 | return print_graph_return(&field->ret, s, entry); | |
135 | } | |
136 | default: | |
137 | return TRACE_TYPE_UNHANDLED; | |
fb52607a | 138 | } |
fb52607a FW |
139 | } |
140 | ||
141 | static struct tracer graph_trace __read_mostly = { | |
142 | .name = "function-graph", | |
143 | .init = graph_trace_init, | |
144 | .reset = graph_trace_reset, | |
145 | .print_line = print_graph_function, | |
146 | .flags = &tracer_flags, | |
147 | }; | |
148 | ||
149 | static __init int init_graph_trace(void) | |
150 | { | |
151 | return register_tracer(&graph_trace); | |
152 | } | |
153 | ||
154 | device_initcall(init_graph_trace); |