1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <[email protected]>
4 *
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/param.h>
10
11 #include "trace-cmd-private.h"
12
13 struct tep_plugin_option trace_ftrace_options[] = {
14 {
15 .name = "tailprint",
16 .plugin_alias = "fgraph",
17 .description =
18 "Print function name at function exit in function graph",
19 },
20 {
21 .name = "depth",
22 .plugin_alias = "fgraph",
23 .description =
24 "Show the depth of each entry",
25 },
26 {
27 .name = NULL,
28 }
29 };
30
31 static struct tep_plugin_option *fgraph_tail = &trace_ftrace_options[0];
32 static struct tep_plugin_option *fgraph_depth = &trace_ftrace_options[1];
33
find_ret_event(struct tracecmd_ftrace * finfo,struct tep_handle * pevent)34 static int find_ret_event(struct tracecmd_ftrace *finfo, struct tep_handle *pevent)
35 {
36 struct tep_event *event;
37
38 /* Store the func ret id and event for later use */
39 event = tep_find_event_by_name(pevent, "ftrace", "funcgraph_exit");
40 if (!event)
41 return -1;
42
43 finfo->fgraph_ret_id = event->id;
44 finfo->fgraph_ret_event = event;
45 return 0;
46 }
47
48 #define ret_event_check(finfo, pevent) \
49 do { \
50 if (!finfo->fgraph_ret_event && find_ret_event(finfo, pevent) < 0) \
51 return -1; \
52 } while (0)
53
function_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)54 static int function_handler(struct trace_seq *s, struct tep_record *record,
55 struct tep_event *event, void *context)
56 {
57 struct tep_handle *pevent = event->tep;
58 unsigned long long function;
59 const char *func;
60
61 if (tep_get_field_val(s, event, "ip", record, &function, 1))
62 return trace_seq_putc(s, '!');
63
64 func = tep_find_function(pevent, function);
65 if (func)
66 trace_seq_printf(s, "%s <-- ", func);
67 else
68 trace_seq_printf(s, "0x%llx", function);
69
70 if (tep_get_field_val(s, event, "parent_ip", record, &function, 1))
71 return trace_seq_putc(s, '!');
72
73 func = tep_find_function(pevent, function);
74 if (func)
75 trace_seq_printf(s, "%s", func);
76 else
77 trace_seq_printf(s, "0x%llx", function);
78
79 return 0;
80 }
81
82 #define TRACE_GRAPH_INDENT 2
83
84 static struct tep_record *
get_return_for_leaf(struct trace_seq * s,int cpu,int cur_pid,unsigned long long cur_func,struct tep_record * next,struct tracecmd_ftrace * finfo)85 get_return_for_leaf(struct trace_seq *s, int cpu, int cur_pid,
86 unsigned long long cur_func, struct tep_record *next,
87 struct tracecmd_ftrace *finfo)
88 {
89 unsigned long long val;
90 unsigned long long type;
91 unsigned long long pid;
92
93 /* Searching a common field, can use any event */
94 if (tep_get_common_field_val(s, finfo->fgraph_ret_event, "common_type", next, &type, 1))
95 return NULL;
96
97 if (type != finfo->fgraph_ret_id)
98 return NULL;
99
100 if (tep_get_common_field_val(s, finfo->fgraph_ret_event, "common_pid", next, &pid, 1))
101 return NULL;
102
103 if (cur_pid != pid)
104 return NULL;
105
106 /* We aleady know this is a funcgraph_ret_event */
107 if (tep_get_field_val(s, finfo->fgraph_ret_event, "func", next, &val, 1))
108 return NULL;
109
110 if (cur_func != val)
111 return NULL;
112
113 /* this is a leaf, now advance the iterator */
114 return tracecmd_read_data(tracecmd_curr_thread_handle, cpu);
115 }
116
117 /* Signal a overhead of time execution to the output */
print_graph_overhead(struct trace_seq * s,unsigned long long duration)118 static void print_graph_overhead(struct trace_seq *s,
119 unsigned long long duration)
120 {
121 /* Non nested entry or return */
122 if (duration == ~0ULL)
123 return (void)trace_seq_printf(s, " ");
124
125 /* Duration exceeded 1 sec */
126 if (duration > 1000000000ULL)
127 return (void)trace_seq_printf(s, "$ ");
128
129 /* Duration exceeded 1000 usecs */
130 if (duration > 1000000ULL)
131 return (void)trace_seq_printf(s, "# ");
132
133 /* Duration exceeded 100 usecs */
134 if (duration > 100000ULL)
135 return (void)trace_seq_printf(s, "! ");
136
137 /* Duration exceeded 10 usecs */
138 if (duration > 10000ULL)
139 return (void)trace_seq_printf(s, "+ ");
140
141 trace_seq_printf(s, " ");
142 }
143
print_graph_duration(struct trace_seq * s,unsigned long long duration)144 static void print_graph_duration(struct trace_seq *s, unsigned long long duration)
145 {
146 unsigned long usecs = duration / 1000;
147 unsigned long nsecs_rem = duration % 1000;
148 /* log10(ULONG_MAX) + '\0' */
149 char msecs_str[21];
150 char nsecs_str[5];
151 int len;
152 int i;
153
154 sprintf(msecs_str, "%lu", usecs);
155
156 /* Print msecs */
157 len = s->len;
158 trace_seq_printf(s, "%lu", usecs);
159
160 /* Print nsecs (we don't want to exceed 7 numbers) */
161 if ((s->len - len) < 7) {
162 snprintf(nsecs_str, MIN(sizeof(nsecs_str), 8 - len), "%03lu", nsecs_rem);
163 trace_seq_printf(s, ".%s", nsecs_str);
164 }
165
166 len = s->len - len;
167
168 trace_seq_puts(s, " us ");
169
170 /* Print remaining spaces to fit the row's width */
171 for (i = len; i < 7; i++)
172 trace_seq_putc(s, ' ');
173
174 trace_seq_puts(s, "| ");
175 }
176
177 static int
print_graph_entry_leaf(struct trace_seq * s,struct tep_event * event,struct tep_record * record,struct tep_record * ret_rec,struct tracecmd_ftrace * finfo)178 print_graph_entry_leaf(struct trace_seq *s,
179 struct tep_event *event,
180 struct tep_record *record,
181 struct tep_record *ret_rec,
182 struct tracecmd_ftrace *finfo)
183 {
184 struct tep_handle *pevent = event->tep;
185 unsigned long long rettime, calltime;
186 unsigned long long duration, depth;
187 unsigned long long val;
188 const char *func;
189 int ret;
190 int i;
191
192 if (tep_get_field_val(s, finfo->fgraph_ret_event, "rettime", ret_rec, &rettime, 1))
193 return trace_seq_putc(s, '!');
194
195 if (tep_get_field_val(s, finfo->fgraph_ret_event, "calltime", ret_rec, &calltime, 1))
196 return trace_seq_putc(s, '!');
197
198 duration = rettime - calltime;
199
200 /* Overhead */
201 print_graph_overhead(s, duration);
202
203 /* Duration */
204 print_graph_duration(s, duration);
205
206 if (tep_get_field_val(s, event, "depth", record, &depth, 1))
207 return trace_seq_putc(s, '!');
208
209 /* Function */
210 for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
211 trace_seq_putc(s, ' ');
212
213 if (tep_get_field_val(s, event, "func", record, &val, 1))
214 return trace_seq_putc(s, '!');
215 func = tep_find_function(pevent, val);
216
217 if (func)
218 ret = trace_seq_printf(s, "%s();", func);
219 else
220 ret = trace_seq_printf(s, "%llx();", val);
221
222 if (ret && fgraph_depth->set)
223 ret = trace_seq_printf(s, " (%lld)", depth);
224
225 return ret;
226 }
227
print_graph_nested(struct trace_seq * s,struct tep_event * event,struct tep_record * record)228 static int print_graph_nested(struct trace_seq *s,
229 struct tep_event *event,
230 struct tep_record *record)
231 {
232 struct tep_handle *pevent = event->tep;
233 unsigned long long depth;
234 unsigned long long val;
235 const char *func;
236 int ret;
237 int i;
238
239 /* No overhead */
240 print_graph_overhead(s, -1);
241
242 /* No time */
243 trace_seq_puts(s, " | ");
244
245 if (tep_get_field_val(s, event, "depth", record, &depth, 1))
246 return trace_seq_putc(s, '!');
247
248 /* Function */
249 for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
250 trace_seq_putc(s, ' ');
251
252 if (tep_get_field_val(s, event, "func", record, &val, 1))
253 return trace_seq_putc(s, '!');
254
255 func = tep_find_function(pevent, val);
256
257 if (func)
258 ret = trace_seq_printf(s, "%s() {", func);
259 else
260 ret = trace_seq_printf(s, "%llx() {", val);
261
262 if (ret && fgraph_depth->set)
263 ret = trace_seq_printf(s, " (%lld)", depth);
264
265 return ret;
266 }
267
268 static int
fgraph_ent_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)269 fgraph_ent_handler(struct trace_seq *s, struct tep_record *record,
270 struct tep_event *event, void *context)
271 {
272 struct tracecmd_ftrace *finfo = context;
273 struct tep_record *rec;
274 unsigned long long val, pid;
275 int cpu;
276
277 ret_event_check(finfo, event->tep);
278
279 if (tep_get_common_field_val(s, event, "common_pid", record, &pid, 1))
280 return trace_seq_putc(s, '!');
281
282 if (tep_get_field_val(s, event, "func", record, &val, 1))
283 return trace_seq_putc(s, '!');
284
285 rec = tracecmd_peek_next_data(tracecmd_curr_thread_handle, &cpu);
286 if (rec)
287 rec = get_return_for_leaf(s, cpu, pid, val, rec, finfo);
288
289 if (rec) {
290 /*
291 * If this is a leaf function, then get_return_for_leaf
292 * returns the return of the function
293 */
294 print_graph_entry_leaf(s, event, record, rec, finfo);
295 tracecmd_free_record(rec);
296 } else
297 print_graph_nested(s, event, record);
298
299 return 0;
300 }
301
302 static int
fgraph_ret_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)303 fgraph_ret_handler(struct trace_seq *s, struct tep_record *record,
304 struct tep_event *event, void *context)
305 {
306 struct tracecmd_ftrace *finfo = context;
307 unsigned long long rettime, calltime;
308 unsigned long long duration, depth;
309 unsigned long long val;
310 const char *func;
311 int i;
312
313 ret_event_check(finfo, event->tep);
314
315 if (tep_get_field_val(s, event, "rettime", record, &rettime, 1))
316 return trace_seq_putc(s, '!');
317
318 if (tep_get_field_val(s, event, "calltime", record, &calltime, 1))
319 return trace_seq_putc(s, '!');
320
321 duration = rettime - calltime;
322
323 /* Overhead */
324 print_graph_overhead(s, duration);
325
326 /* Duration */
327 print_graph_duration(s, duration);
328
329 if (tep_get_field_val(s, event, "depth", record, &depth, 1))
330 return trace_seq_putc(s, '!');
331
332 /* Function */
333 for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
334 trace_seq_putc(s, ' ');
335
336 trace_seq_putc(s, '}');
337
338 if (fgraph_tail->set) {
339 if (tep_get_field_val(s, event, "func", record, &val, 0))
340 return 0;
341 func = tep_find_function(event->tep, val);
342 if (!func)
343 return 0;
344 trace_seq_printf(s, " /* %s */", func);
345 }
346
347 if (fgraph_depth->set)
348 trace_seq_printf(s, " (%lld)", depth);
349
350 return 0;
351 }
352
353 /**
354 * tracecmd_ftrace_load_options - load the ftrace options
355 *
356 * This routine is used for trace-cmd list, to load the builtin
357 * ftrace options in order to list them. As the list command does
358 * not load a trace.dat file where this would normally be loaded.
359 */
tracecmd_ftrace_load_options(void)360 void tracecmd_ftrace_load_options(void)
361 {
362 tep_plugin_add_options("ftrace", trace_ftrace_options);
363 }
364
tracecmd_ftrace_overrides(struct tracecmd_input * handle,struct tracecmd_ftrace * finfo)365 int tracecmd_ftrace_overrides(struct tracecmd_input *handle,
366 struct tracecmd_ftrace *finfo)
367 {
368 struct tep_handle *pevent;
369 struct tep_event *event;
370
371 finfo->handle = handle;
372
373 pevent = tracecmd_get_tep(handle);
374
375 tep_register_event_handler(pevent, -1, "ftrace", "function",
376 function_handler, NULL);
377
378 tep_register_event_handler(pevent, -1, "ftrace", "funcgraph_entry",
379 fgraph_ent_handler, finfo);
380
381 tep_register_event_handler(pevent, -1, "ftrace", "funcgraph_exit",
382 fgraph_ret_handler, finfo);
383
384 tep_plugin_add_options("ftrace", trace_ftrace_options);
385
386 /* Store the func ret id and event for later use */
387 event = tep_find_event_by_name(pevent, "ftrace", "funcgraph_exit");
388 if (!event)
389 return 0;
390
391 finfo->long_size = tracecmd_long_size(handle);
392
393 finfo->fgraph_ret_id = event->id;
394 finfo->fgraph_ret_event = event;
395
396 return 0;
397 }
398