1 // SPDX-License-Identifier: LGPL-2.1
2 /*
3 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <[email protected]>
4 */
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "event-parse.h"
10 #include "event-utils.h"
11 #include "trace-seq.h"
12
13 #define __weak __attribute__((weak))
14
15 /* Export this for applications to override it */
16 __weak const char *tep_func_repeat_format = "%6.1000d";
17
18 static struct func_stack {
19 int size;
20 char **stack;
21 } *fstack;
22
23 static int cpus = -1;
24
25 #define STK_BLK 10
26
27 struct tep_plugin_option plugin_options[] =
28 {
29 {
30 .name = "parent",
31 .plugin_alias = "ftrace",
32 .description =
33 "Print parent of functions for function events",
34 },
35 {
36 .name = "indent",
37 .plugin_alias = "ftrace",
38 .description =
39 "Try to show function call indents, based on parents",
40 .set = 1,
41 },
42 {
43 .name = "offset",
44 .plugin_alias = "ftrace",
45 .description =
46 "Show function names as well as their offsets",
47 .set = 0,
48 },
49 {
50 .name = NULL,
51 }
52 };
53
54 static struct tep_plugin_option *ftrace_parent = &plugin_options[0];
55 static struct tep_plugin_option *ftrace_indent = &plugin_options[1];
56 static struct tep_plugin_option *ftrace_offset = &plugin_options[2];
57
add_child(struct func_stack * stack,const char * child,int pos)58 static void add_child(struct func_stack *stack, const char *child, int pos)
59 {
60 int i;
61
62 if (!child)
63 return;
64
65 if (pos < stack->size)
66 free(stack->stack[pos]);
67 else {
68 char **ptr;
69
70 ptr = realloc(stack->stack, sizeof(char *) *
71 (stack->size + STK_BLK));
72 if (!ptr) {
73 tep_warning("could not allocate plugin memory\n");
74 return;
75 }
76
77 stack->stack = ptr;
78
79 for (i = stack->size; i < stack->size + STK_BLK; i++)
80 stack->stack[i] = NULL;
81 stack->size += STK_BLK;
82 }
83
84 stack->stack[pos] = strdup(child);
85 }
86
add_and_get_index(const char * parent,const char * child,int cpu)87 static int add_and_get_index(const char *parent, const char *child, int cpu)
88 {
89 int i;
90
91 if (cpu < 0)
92 return 0;
93
94 if (cpu > cpus) {
95 struct func_stack *ptr;
96
97 ptr = realloc(fstack, sizeof(*fstack) * (cpu + 1));
98 if (!ptr) {
99 tep_warning("could not allocate plugin memory\n");
100 return 0;
101 }
102
103 fstack = ptr;
104
105 /* Account for holes in the cpu count */
106 for (i = cpus + 1; i <= cpu; i++)
107 memset(&fstack[i], 0, sizeof(fstack[i]));
108 cpus = cpu;
109 }
110
111 for (i = 0; i < fstack[cpu].size && fstack[cpu].stack[i]; i++) {
112 if (strcmp(parent, fstack[cpu].stack[i]) == 0) {
113 add_child(&fstack[cpu], child, i+1);
114 return i;
115 }
116 }
117
118 /* Not found */
119 add_child(&fstack[cpu], parent, 0);
120 add_child(&fstack[cpu], child, 1);
121 return 0;
122 }
123
show_function(struct trace_seq * s,struct tep_handle * tep,const char * func,unsigned long long function)124 static void show_function(struct trace_seq *s, struct tep_handle *tep,
125 const char *func, unsigned long long function)
126 {
127 unsigned long long offset;
128
129 trace_seq_printf(s, "%s", func);
130 if (ftrace_offset->set) {
131 offset = tep_find_function_address(tep, function);
132 trace_seq_printf(s, "+0x%x ", (int)(function - offset));
133 }
134 }
135
function_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)136 static int function_handler(struct trace_seq *s, struct tep_record *record,
137 struct tep_event *event, void *context)
138 {
139 struct tep_handle *tep = event->tep;
140 unsigned long long function;
141 unsigned long long pfunction;
142 const char *func;
143 const char *parent;
144 int index = 0;
145
146 if (tep_get_field_val(s, event, "ip", record, &function, 1))
147 return trace_seq_putc(s, '!');
148
149 func = tep_find_function(tep, function);
150
151 if (tep_get_field_val(s, event, "parent_ip", record, &pfunction, 1))
152 return trace_seq_putc(s, '!');
153
154 parent = tep_find_function(tep, pfunction);
155
156 if (parent && ftrace_indent->set)
157 index = add_and_get_index(parent, func, record->cpu);
158
159 trace_seq_printf(s, "%*s", index*3, "");
160
161 if (func)
162 show_function(s, tep, func, function);
163 else
164 trace_seq_printf(s, "0x%llx", function);
165
166 if (ftrace_parent->set) {
167 trace_seq_printf(s, " <-- ");
168 if (parent)
169 show_function(s, tep, parent, pfunction);
170 else
171 trace_seq_printf(s, "0x%llx", pfunction);
172 }
173
174 return 0;
175 }
176
trace_func_repeat_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)177 static int trace_func_repeat_handler(struct trace_seq *s, struct tep_record *record,
178 struct tep_event *event, void *context)
179 {
180 struct tep_handle *tep = event->tep;
181 unsigned long long count, top_delta, bottom_delta;
182 struct tep_record dummy;
183
184 function_handler(s, record, event, context);
185
186 if (tep_get_field_val(s, event, "count", record, &count, 1))
187 return trace_seq_putc(s, '!');
188
189 if (tep_get_field_val(s, event, "top_delta_ts", record, &top_delta, 1))
190 return trace_seq_putc(s, '!');
191
192 if (tep_get_field_val(s, event, "bottom_delta_ts", record, &bottom_delta, 1))
193 return trace_seq_putc(s, '!');
194
195 trace_seq_printf(s, " (count: %lld last_ts: ", count);
196
197 memcpy(&dummy, record, sizeof(dummy));
198 dummy.ts -= (top_delta << 32) | bottom_delta;
199
200 tep_print_event(tep, s, &dummy, tep_func_repeat_format, TEP_PRINT_TIME);
201
202 trace_seq_puts(s, ")");
203
204 return 0;
205 }
206
207 static int
trace_stack_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)208 trace_stack_handler(struct trace_seq *s, struct tep_record *record,
209 struct tep_event *event, void *context)
210 {
211 struct tep_format_field *field;
212 unsigned long long addr;
213 const char *func;
214 int long_size;
215 void *data = record->data;
216
217 field = tep_find_any_field(event, "caller");
218 if (!field) {
219 trace_seq_printf(s, "<CANT FIND FIELD %s>", "caller");
220 return 0;
221 }
222
223 trace_seq_puts(s, "<stack trace >\n");
224
225 long_size = tep_get_long_size(event->tep);
226
227 for (data += field->offset; data < record->data + record->size;
228 data += long_size) {
229 addr = tep_read_number(event->tep, data, long_size);
230
231 if ((long_size == 8 && addr == (unsigned long long)-1) ||
232 ((int)addr == -1))
233 break;
234
235 func = tep_find_function(event->tep, addr);
236 if (func) {
237 trace_seq_puts(s, "=> ");
238 show_function(s, event->tep, func, addr);
239 trace_seq_printf(s, " (%llx)\n", addr);
240 } else
241 trace_seq_printf(s, "=> %llx\n", addr);
242 }
243
244 return 0;
245 }
246
247 static int
trace_raw_data_handler(struct trace_seq * s,struct tep_record * record,struct tep_event * event,void * context)248 trace_raw_data_handler(struct trace_seq *s, struct tep_record *record,
249 struct tep_event *event, void *context)
250 {
251 struct tep_format_field *field;
252 unsigned long long id;
253 int long_size;
254 void *data = record->data;
255
256 if (tep_get_field_val(s, event, "id", record, &id, 1))
257 return trace_seq_putc(s, '!');
258
259 trace_seq_printf(s, "# %llx", id);
260
261 field = tep_find_any_field(event, "buf");
262 if (!field) {
263 trace_seq_printf(s, "<CANT FIND FIELD %s>", "buf");
264 return 0;
265 }
266
267 long_size = tep_get_long_size(event->tep);
268
269 for (data += field->offset; data < record->data + record->size;
270 data += long_size) {
271 int size = sizeof(long);
272 int left = (record->data + record->size) - data;
273 int i;
274
275 if (size > left)
276 size = left;
277
278 for (i = 0; i < size; i++)
279 trace_seq_printf(s, " %02x", *(unsigned char *)(data + i));
280 }
281
282 return 0;
283 }
284
TEP_PLUGIN_LOADER(struct tep_handle * tep)285 int TEP_PLUGIN_LOADER(struct tep_handle *tep)
286 {
287 tep_register_event_handler(tep, -1, "ftrace", "function",
288 function_handler, NULL);
289
290 tep_register_event_handler(tep, -1, "ftrace", "kernel_stack",
291 trace_stack_handler, NULL);
292
293 tep_register_event_handler(tep, -1, "ftrace", "raw_data",
294 trace_raw_data_handler, NULL);
295
296 tep_register_event_handler(tep, -1, "ftrace", "func_repeats",
297 trace_func_repeat_handler, NULL);
298
299 tep_plugin_add_options("ftrace", plugin_options);
300
301 return 0;
302 }
303
TEP_PLUGIN_UNLOADER(struct tep_handle * tep)304 void TEP_PLUGIN_UNLOADER(struct tep_handle *tep)
305 {
306 int i, x;
307
308 tep_unregister_event_handler(tep, -1, "ftrace", "function",
309 function_handler, NULL);
310
311 for (i = 0; i <= cpus; i++) {
312 for (x = 0; x < fstack[i].size && fstack[i].stack[x]; x++)
313 free(fstack[i].stack[x]);
314 free(fstack[i].stack);
315 }
316
317 tep_plugin_remove_options(plugin_options);
318
319 free(fstack);
320 fstack = NULL;
321 cpus = -1;
322 }
323