xref: /aosp_15_r20/external/trace-cmd/lib/trace-cmd/trace-ftrace.c (revision 58e6ee5f017f6a8912852c892d18457e4bafb554)
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