]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - kernel/trace/trace_functions_graph.c
tracing/function-graph-tracer: adjustments of the trace informations
[mirror_ubuntu-bionic-kernel.git] / kernel / trace / trace_functions_graph.c
CommitLineData
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 17
1a056155 18/* Flag options */
fb52607a 19#define TRACE_GRAPH_PRINT_OVERRUN 0x1
1a056155
FW
20#define TRACE_GRAPH_PRINT_CPU 0x2
21#define TRACE_GRAPH_PRINT_OVERHEAD 0x4
22
fb52607a 23static struct tracer_opt trace_opts[] = {
1a056155
FW
24 /* Display overruns ? */
25 { TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) },
26 /* Display CPU ? */
27 { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) },
28 /* Display Overhead ? */
29 { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) },
fb52607a
FW
30 { } /* Empty entry */
31};
32
33static struct tracer_flags tracer_flags = {
1a056155
FW
34 /* Don't display overruns by default */
35 .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD,
fb52607a
FW
36 .opts = trace_opts
37};
38
287b6e68 39/* pid on the last trace processed */
437f24fb 40static pid_t last_pid[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 };
fb52607a
FW
41
42static int graph_trace_init(struct trace_array *tr)
43{
660c7f9b
SR
44 int cpu, ret;
45
fb52607a
FW
46 for_each_online_cpu(cpu)
47 tracing_reset(tr, cpu);
48
660c7f9b 49 ret = register_ftrace_graph(&trace_graph_return,
287b6e68 50 &trace_graph_entry);
660c7f9b
SR
51 if (ret)
52 return ret;
53 tracing_start_cmdline_record();
54
55 return 0;
fb52607a
FW
56}
57
58static void graph_trace_reset(struct trace_array *tr)
59{
660c7f9b
SR
60 tracing_stop_cmdline_record();
61 unregister_ftrace_graph();
fb52607a
FW
62}
63
1a056155
FW
64static inline int log10_cpu(int nb)
65{
66 if (nb / 100)
67 return 3;
68 if (nb / 10)
69 return 2;
70 return 1;
71}
72
73static enum print_line_t
74print_graph_cpu(struct trace_seq *s, int cpu)
75{
76 int i;
77 int ret;
78 int log10_this = log10_cpu(cpu);
79 int log10_all = log10_cpu(cpus_weight_nr(cpu_online_map));
80
81
82 for (i = 0; i < log10_all - log10_this; i++) {
83 ret = trace_seq_printf(s, " ");
84 if (!ret)
85 return TRACE_TYPE_PARTIAL_LINE;
86 }
87 ret = trace_seq_printf(s, "%d) ", cpu);
88 if (!ret)
89 return TRACE_TYPE_PARTIAL_LINE;
90 return TRACE_TYPE_HANDLED;
91}
92
93
287b6e68 94/* If the pid changed since the last trace, output this event */
437f24fb 95static int verif_pid(struct trace_seq *s, pid_t pid, int cpu)
287b6e68 96{
660c7f9b
SR
97 char *comm;
98
437f24fb 99 if (last_pid[cpu] != -1 && last_pid[cpu] == pid)
287b6e68 100 return 1;
fb52607a 101
437f24fb 102 last_pid[cpu] = pid;
660c7f9b
SR
103 comm = trace_find_cmdline(pid);
104
1a056155 105 return trace_seq_printf(s, "\n------------8<---------- thread %s-%d"
287b6e68 106 " ------------8<----------\n\n",
437f24fb 107 cpu, comm, pid);
287b6e68
FW
108}
109
83a8df61
FW
110static bool
111trace_branch_is_leaf(struct trace_iterator *iter,
112 struct ftrace_graph_ent_entry *curr)
113{
114 struct ring_buffer_iter *ring_iter;
115 struct ring_buffer_event *event;
116 struct ftrace_graph_ret_entry *next;
117
118 ring_iter = iter->buffer_iter[iter->cpu];
119
120 if (!ring_iter)
121 return false;
122
1a056155 123 event = ring_buffer_iter_peek(ring_iter, NULL);
83a8df61
FW
124
125 if (!event)
126 return false;
127
128 next = ring_buffer_event_data(event);
129
130 if (next->ent.type != TRACE_GRAPH_RET)
131 return false;
132
133 if (curr->ent.pid != next->ent.pid ||
134 curr->graph_ent.func != next->ret.func)
135 return false;
136
137 return true;
138}
139
140
141static inline int
142print_graph_duration(unsigned long long duration, struct trace_seq *s)
143{
144 unsigned long nsecs_rem = do_div(duration, 1000);
1a056155 145 return trace_seq_printf(s, "%4llu.%3lu us | ", duration, nsecs_rem);
83a8df61
FW
146}
147
148/* Signal a overhead of time execution to the output */
149static int
150print_graph_overhead(unsigned long long duration, struct trace_seq *s)
151{
152 /* Duration exceeded 100 msecs */
153 if (duration > 100000ULL)
154 return trace_seq_printf(s, "! ");
155
156 /* Duration exceeded 10 msecs */
157 if (duration > 10000ULL)
158 return trace_seq_printf(s, "+ ");
159
160 return trace_seq_printf(s, " ");
161}
162
163/* Case of a leaf function on its call entry */
287b6e68 164static enum print_line_t
83a8df61
FW
165print_graph_entry_leaf(struct trace_iterator *iter,
166 struct ftrace_graph_ent_entry *entry, struct trace_seq *s)
fb52607a 167{
83a8df61
FW
168 struct ftrace_graph_ret_entry *ret_entry;
169 struct ftrace_graph_ret *graph_ret;
170 struct ring_buffer_event *event;
171 struct ftrace_graph_ent *call;
172 unsigned long long duration;
fb52607a 173 int ret;
1a056155 174 int i;
fb52607a 175
83a8df61
FW
176 event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
177 ret_entry = ring_buffer_event_data(event);
178 graph_ret = &ret_entry->ret;
179 call = &entry->graph_ent;
180 duration = graph_ret->rettime - graph_ret->calltime;
181
1a056155
FW
182 /* Must not exceed 8 characters: 9999.999 us */
183 if (duration > 10000000ULL)
184 duration = 9999999ULL;
185
83a8df61 186 /* Overhead */
1a056155
FW
187 if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
188 ret = print_graph_overhead(duration, s);
189 if (!ret)
190 return TRACE_TYPE_PARTIAL_LINE;
191 }
192
193 /* Duration */
194 ret = print_graph_duration(duration, s);
83a8df61 195 if (!ret)
437f24fb
SR
196 return TRACE_TYPE_PARTIAL_LINE;
197
83a8df61
FW
198 /* Function */
199 for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
200 ret = trace_seq_printf(s, " ");
201 if (!ret)
202 return TRACE_TYPE_PARTIAL_LINE;
203 }
204
205 ret = seq_print_ip_sym(s, call->func, 0);
206 if (!ret)
207 return TRACE_TYPE_PARTIAL_LINE;
208
1a056155 209 ret = trace_seq_printf(s, "();\n");
83a8df61
FW
210 if (!ret)
211 return TRACE_TYPE_PARTIAL_LINE;
212
213 return TRACE_TYPE_HANDLED;
214}
215
216static enum print_line_t
217print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,
218 struct trace_seq *s)
219{
220 int i;
221 int ret;
222 struct ftrace_graph_ent *call = &entry->graph_ent;
223
224 /* No overhead */
1a056155
FW
225 if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
226 ret = trace_seq_printf(s, " ");
227 if (!ret)
228 return TRACE_TYPE_PARTIAL_LINE;
229 }
230
231 /* No time */
232 ret = trace_seq_printf(s, " | ");
83a8df61
FW
233
234 /* Function */
287b6e68
FW
235 for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
236 ret = trace_seq_printf(s, " ");
fb52607a
FW
237 if (!ret)
238 return TRACE_TYPE_PARTIAL_LINE;
287b6e68
FW
239 }
240
241 ret = seq_print_ip_sym(s, call->func, 0);
242 if (!ret)
243 return TRACE_TYPE_PARTIAL_LINE;
244
1a056155 245 ret = trace_seq_printf(s, "() {\n");
83a8df61
FW
246 if (!ret)
247 return TRACE_TYPE_PARTIAL_LINE;
248
287b6e68
FW
249 return TRACE_TYPE_HANDLED;
250}
251
83a8df61
FW
252static enum print_line_t
253print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
254 struct trace_iterator *iter, int cpu)
255{
256 int ret;
257 struct trace_entry *ent = iter->ent;
258
1a056155 259 /* Pid */
83a8df61
FW
260 if (!verif_pid(s, ent->pid, cpu))
261 return TRACE_TYPE_PARTIAL_LINE;
262
1a056155
FW
263 /* Cpu */
264 if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
265 ret = print_graph_cpu(s, cpu);
266 if (!ret)
267 return TRACE_TYPE_PARTIAL_LINE;
268 }
83a8df61
FW
269
270 if (trace_branch_is_leaf(iter, field))
271 return print_graph_entry_leaf(iter, field, s);
272 else
273 return print_graph_entry_nested(field, s);
274
275}
276
287b6e68
FW
277static enum print_line_t
278print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
437f24fb 279 struct trace_entry *ent, int cpu)
287b6e68
FW
280{
281 int i;
282 int ret;
83a8df61 283 unsigned long long duration = trace->rettime - trace->calltime;
287b6e68 284
1a056155
FW
285 /* Must not exceed 8 characters: xxxx.yyy us */
286 if (duration > 10000000ULL)
287 duration = 9999999ULL;
288
83a8df61 289 /* Pid */
437f24fb
SR
290 if (!verif_pid(s, ent->pid, cpu))
291 return TRACE_TYPE_PARTIAL_LINE;
292
83a8df61 293 /* Cpu */
1a056155
FW
294 if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
295 ret = print_graph_cpu(s, cpu);
296 if (!ret)
297 return TRACE_TYPE_PARTIAL_LINE;
298 }
fb52607a 299
83a8df61 300 /* Overhead */
1a056155
FW
301 if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
302 ret = print_graph_overhead(duration, s);
303 if (!ret)
304 return TRACE_TYPE_PARTIAL_LINE;
305 }
306
307 /* Duration */
308 ret = print_graph_duration(duration, s);
83a8df61
FW
309 if (!ret)
310 return TRACE_TYPE_PARTIAL_LINE;
311
312 /* Closing brace */
287b6e68
FW
313 for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) {
314 ret = trace_seq_printf(s, " ");
fb52607a
FW
315 if (!ret)
316 return TRACE_TYPE_PARTIAL_LINE;
287b6e68
FW
317 }
318
1a056155 319 ret = trace_seq_printf(s, "}\n");
287b6e68
FW
320 if (!ret)
321 return TRACE_TYPE_PARTIAL_LINE;
fb52607a 322
83a8df61 323 /* Overrun */
287b6e68
FW
324 if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) {
325 ret = trace_seq_printf(s, " (Overruns: %lu)\n",
326 trace->overrun);
fb52607a
FW
327 if (!ret)
328 return TRACE_TYPE_PARTIAL_LINE;
287b6e68
FW
329 }
330 return TRACE_TYPE_HANDLED;
331}
332
333enum print_line_t
334print_graph_function(struct trace_iterator *iter)
335{
336 struct trace_seq *s = &iter->seq;
337 struct trace_entry *entry = iter->ent;
fb52607a 338
287b6e68
FW
339 switch (entry->type) {
340 case TRACE_GRAPH_ENT: {
341 struct ftrace_graph_ent_entry *field;
342 trace_assign_type(field, entry);
83a8df61 343 return print_graph_entry(field, s, iter,
437f24fb 344 iter->cpu);
287b6e68
FW
345 }
346 case TRACE_GRAPH_RET: {
347 struct ftrace_graph_ret_entry *field;
348 trace_assign_type(field, entry);
437f24fb 349 return print_graph_return(&field->ret, s, entry, iter->cpu);
287b6e68
FW
350 }
351 default:
352 return TRACE_TYPE_UNHANDLED;
fb52607a 353 }
fb52607a
FW
354}
355
356static struct tracer graph_trace __read_mostly = {
83a8df61 357 .name = "function_graph",
fb52607a
FW
358 .init = graph_trace_init,
359 .reset = graph_trace_reset,
360 .print_line = print_graph_function,
361 .flags = &tracer_flags,
362};
363
364static __init int init_graph_trace(void)
365{
366 return register_tracer(&graph_trace);
367}
368
369device_initcall(init_graph_trace);