]>
Commit | Line | Data |
---|---|---|
fb52607a FW |
1 | /* |
2 | * | |
3 | * Function graph tracer. | |
4 | * Copyright (c) 2008 Frederic Weisbecker <[email protected]> | |
5 | * Mostly borrowed from function tracer which | |
6 | * is Copyright (c) Steven Rostedt <[email protected]> | |
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 | 30 | /* pid on the last trace processed */ |
437f24fb | 31 | static pid_t last_pid[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 }; |
fb52607a FW |
32 | |
33 | static int graph_trace_init(struct trace_array *tr) | |
34 | { | |
660c7f9b SR |
35 | int cpu, ret; |
36 | ||
fb52607a FW |
37 | for_each_online_cpu(cpu) |
38 | tracing_reset(tr, cpu); | |
39 | ||
660c7f9b | 40 | ret = register_ftrace_graph(&trace_graph_return, |
287b6e68 | 41 | &trace_graph_entry); |
660c7f9b SR |
42 | if (ret) |
43 | return ret; | |
44 | tracing_start_cmdline_record(); | |
45 | ||
46 | return 0; | |
fb52607a FW |
47 | } |
48 | ||
49 | static void graph_trace_reset(struct trace_array *tr) | |
50 | { | |
660c7f9b SR |
51 | tracing_stop_cmdline_record(); |
52 | unregister_ftrace_graph(); | |
fb52607a FW |
53 | } |
54 | ||
287b6e68 | 55 | /* If the pid changed since the last trace, output this event */ |
437f24fb | 56 | static int verif_pid(struct trace_seq *s, pid_t pid, int cpu) |
287b6e68 | 57 | { |
660c7f9b SR |
58 | char *comm; |
59 | ||
437f24fb | 60 | if (last_pid[cpu] != -1 && last_pid[cpu] == pid) |
287b6e68 | 61 | return 1; |
fb52607a | 62 | |
437f24fb | 63 | last_pid[cpu] = pid; |
660c7f9b SR |
64 | comm = trace_find_cmdline(pid); |
65 | ||
437f24fb SR |
66 | return trace_seq_printf(s, "\nCPU[%03d]" |
67 | " ------------8<---------- thread %s-%d" | |
287b6e68 | 68 | " ------------8<----------\n\n", |
437f24fb | 69 | cpu, comm, pid); |
287b6e68 FW |
70 | } |
71 | ||
72 | static enum print_line_t | |
73 | print_graph_entry(struct ftrace_graph_ent *call, struct trace_seq *s, | |
437f24fb | 74 | struct trace_entry *ent, int cpu) |
fb52607a | 75 | { |
287b6e68 | 76 | int i; |
fb52607a FW |
77 | int ret; |
78 | ||
437f24fb SR |
79 | if (!verif_pid(s, ent->pid, cpu)) |
80 | return TRACE_TYPE_PARTIAL_LINE; | |
81 | ||
82 | ret = trace_seq_printf(s, "CPU[%03d] ", cpu); | |
83 | if (!ret) | |
287b6e68 | 84 | return TRACE_TYPE_PARTIAL_LINE; |
fb52607a | 85 | |
287b6e68 FW |
86 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { |
87 | ret = trace_seq_printf(s, " "); | |
fb52607a FW |
88 | if (!ret) |
89 | return TRACE_TYPE_PARTIAL_LINE; | |
287b6e68 FW |
90 | } |
91 | ||
92 | ret = seq_print_ip_sym(s, call->func, 0); | |
93 | if (!ret) | |
94 | return TRACE_TYPE_PARTIAL_LINE; | |
95 | ||
96 | ret = trace_seq_printf(s, "() {\n"); | |
97 | if (!ret) | |
98 | return TRACE_TYPE_PARTIAL_LINE; | |
99 | return TRACE_TYPE_HANDLED; | |
100 | } | |
101 | ||
102 | static enum print_line_t | |
103 | print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |
437f24fb | 104 | struct trace_entry *ent, int cpu) |
287b6e68 FW |
105 | { |
106 | int i; | |
107 | int ret; | |
108 | ||
437f24fb SR |
109 | if (!verif_pid(s, ent->pid, cpu)) |
110 | return TRACE_TYPE_PARTIAL_LINE; | |
111 | ||
112 | ret = trace_seq_printf(s, "CPU[%03d] ", cpu); | |
113 | if (!ret) | |
287b6e68 | 114 | return TRACE_TYPE_PARTIAL_LINE; |
fb52607a | 115 | |
287b6e68 FW |
116 | for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) { |
117 | ret = trace_seq_printf(s, " "); | |
fb52607a FW |
118 | if (!ret) |
119 | return TRACE_TYPE_PARTIAL_LINE; | |
287b6e68 FW |
120 | } |
121 | ||
122 | ret = trace_seq_printf(s, "} "); | |
123 | if (!ret) | |
124 | return TRACE_TYPE_PARTIAL_LINE; | |
fb52607a | 125 | |
287b6e68 FW |
126 | ret = trace_seq_printf(s, "%llu\n", trace->rettime - trace->calltime); |
127 | if (!ret) | |
128 | return TRACE_TYPE_PARTIAL_LINE; | |
fb52607a | 129 | |
287b6e68 FW |
130 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { |
131 | ret = trace_seq_printf(s, " (Overruns: %lu)\n", | |
132 | trace->overrun); | |
fb52607a FW |
133 | if (!ret) |
134 | return TRACE_TYPE_PARTIAL_LINE; | |
287b6e68 FW |
135 | } |
136 | return TRACE_TYPE_HANDLED; | |
137 | } | |
138 | ||
139 | enum print_line_t | |
140 | print_graph_function(struct trace_iterator *iter) | |
141 | { | |
142 | struct trace_seq *s = &iter->seq; | |
143 | struct trace_entry *entry = iter->ent; | |
fb52607a | 144 | |
287b6e68 FW |
145 | switch (entry->type) { |
146 | case TRACE_GRAPH_ENT: { | |
147 | struct ftrace_graph_ent_entry *field; | |
148 | trace_assign_type(field, entry); | |
437f24fb SR |
149 | return print_graph_entry(&field->graph_ent, s, entry, |
150 | iter->cpu); | |
287b6e68 FW |
151 | } |
152 | case TRACE_GRAPH_RET: { | |
153 | struct ftrace_graph_ret_entry *field; | |
154 | trace_assign_type(field, entry); | |
437f24fb | 155 | return print_graph_return(&field->ret, s, entry, iter->cpu); |
287b6e68 FW |
156 | } |
157 | default: | |
158 | return TRACE_TYPE_UNHANDLED; | |
fb52607a | 159 | } |
fb52607a FW |
160 | } |
161 | ||
162 | static struct tracer graph_trace __read_mostly = { | |
163 | .name = "function-graph", | |
164 | .init = graph_trace_init, | |
165 | .reset = graph_trace_reset, | |
166 | .print_line = print_graph_function, | |
167 | .flags = &tracer_flags, | |
168 | }; | |
169 | ||
170 | static __init int init_graph_trace(void) | |
171 | { | |
172 | return register_tracer(&graph_trace); | |
173 | } | |
174 | ||
175 | device_initcall(init_graph_trace); |