master
c 294 lines 8.28 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stacktrace-common.h"
4
5 #if defined(USE_LIBBACKTRACE)
6 #include "backtrace.h"
7
8 static struct backtrace_state *backtrace_state = NULL;
9
10 typedef struct {
11 BUFFER *wb; // Buffer to write to
12 size_t frame_count; // Number of frames processed
13 bool first_frame; // Is this the first frame?
14 bool found_signal_handler; // Have we found the signal handler frame?
15 } backtrace_data_t;
16
17 // For collecting raw frames
18 typedef struct {
19 void **frames;
20 int max_frames;
21 int num_frames;
22 int skip_frames;
23 } collect_frames_data_t;
24
25 // Simple callback for collecting PC addresses
26 static int bt_collect_frames_callback(void *data, uintptr_t pc) {
27 collect_frames_data_t *cf_data = (collect_frames_data_t *)data;
28
29 // Skip frames at the top of the stack
30 if (cf_data->skip_frames > 0) {
31 cf_data->skip_frames--;
32 return 0;
33 }
34
35 if (cf_data->num_frames < cf_data->max_frames) {
36 cf_data->frames[cf_data->num_frames++] = (void *)pc;
37 }
38
39 return 0;
40 }
41
42 // Common function to format and add a stack frame to the buffer
43 static void add_stack_frame(backtrace_data_t *bt_data, uintptr_t pc, const char *function,
44 const char *filename, int lineno) {
45 BUFFER *wb = bt_data->wb;
46
47 if (!wb)
48 return;
49
50 // Check if we found the signal handler frame
51 if (!bt_data->found_signal_handler && stacktrace_is_signal_handler_function(function)) {
52 // We found the signal handler, reset the buffer and clear function name
53 buffer_flush(wb);
54 bt_data->frame_count = 0;
55 bt_data->first_frame = true;
56 bt_data->found_signal_handler = true;
57 root_cause_function[0] = '\0';
58 return; // Skip adding the signal handler itself
59 }
60
61 // Check for logging functions, but only if we haven't found a signal handler yet
62 // This prevents double resets when crashing inside logging code
63 if (!bt_data->found_signal_handler && stacktrace_is_logging_function(function)) {
64 // Found a logging function, reset the buffer and clear function name
65 buffer_flush(wb);
66 bt_data->frame_count = 0;
67 bt_data->first_frame = true;
68 root_cause_function[0] = '\0';
69 // continue to add the function to the stack trace
70 }
71
72 // Check if this is a netdata source file and store the function name if it is
73 // (but only if we haven't already stored one)
74 if (!root_cause_function[0] && stacktrace_is_netdata_function(function, filename))
75 stacktrace_keep_first_root_cause_function(function);
76
77 // Add a newline between frames
78 if (!bt_data->first_frame)
79 buffer_putc(wb, '\n');
80 else
81 bt_data->first_frame = false;
82
83 // Format: #ID function (filename.c:NNN)
84 buffer_putc(wb, '#');
85 buffer_print_uint64(wb, bt_data->frame_count);
86 buffer_putc(wb, ' ');
87
88 if (function && *function)
89 buffer_strcat(wb, function);
90 else
91 buffer_strcat(wb, "<unknown>");
92
93 if(pc) {
94 buffer_strcat(wb, " [");
95 buffer_print_uint64_hex(wb, pc);
96 buffer_putc(wb, ']');
97 }
98
99 if (filename && *filename) {
100 buffer_strcat(wb, " (");
101
102 const char *f = strstr(filename, "/src/");
103 if (f) {
104 const char *f2 = strstr(f + 1, "/src/");
105 if(f2) f = f2;
106 }
107 if(!f) f = filename;
108
109 buffer_strcat(wb, f);
110
111 if (lineno > 0) {
112 buffer_strcat(wb, ":");
113 buffer_print_uint64(wb, (uint64_t)lineno);
114 }
115
116 buffer_putc(wb, ')');
117 }
118
119 bt_data->frame_count++;
120 }
121
122 // Error callback for libbacktrace
123 static void bt_error_handler(void *data, const char *msg, int errnum) {
124 backtrace_data_t *bt_data = (backtrace_data_t *)data;
125
126 if (!bt_data || !bt_data->wb)
127 return;
128
129 // Use <unknown> for function name in error cases
130 const char *function = "<unknown>";
131
132 // Format the error message as the filename
133 char error_buf[512] = "error: ";
134 size_t len = 7; // Length of "error: "
135
136 // Add the error message
137 if (msg)
138 len = strcatz(error_buf, len, msg, sizeof(error_buf));
139
140 // Add the error number description if available
141 if (errnum > 0) {
142 if (msg) {
143 len = strcatz(error_buf, len, ": ", sizeof(error_buf));
144 }
145 len = strcatz(error_buf, len, strerror(errnum), sizeof(error_buf));
146 }
147
148 add_stack_frame(bt_data, 0, function, error_buf, 0);
149 }
150
151 // Full callback for libbacktrace
152 static int bt_full_handler(void *data, uintptr_t pc,
153 const char *filename, int lineno,
154 const char *function) {
155 backtrace_data_t *bt_data = (backtrace_data_t *)data;
156 if (!bt_data)
157 return 0;
158
159 add_stack_frame(bt_data, pc, function, filename, lineno);
160
161 return 0; // Continue backtrace
162 }
163
164 const char *stacktrace_backend(void) {
165 #if BACKTRACE_SUPPORTS_DATA
166 #define BACKTRACE_DATA "data"
167 #else
168 #define BACKTRACE_DATA "no-data"
169 #endif
170
171 #if BACKTRACE_USES_MALLOC
172 #define BACKTRACE_MEMORY "malloc"
173 #else
174 #define BACKTRACE_MEMORY "mmap"
175 #endif
176
177 #if BACKTRACE_SUPPORTS_THREADS
178 #define BACKTRACE_THREADS "threads"
179 #else
180 #define BACKTRACE_THREADS "no-threads"
181 #endif
182
183 return "libbacktrace (" BACKTRACE_MEMORY ", " BACKTRACE_THREADS ", " BACKTRACE_DATA ")";
184 }
185
186 void impl_stacktrace_init(void) {
187 if (!backtrace_state) {
188 backtrace_state = backtrace_create_state(NULL, BACKTRACE_SUPPORTS_THREADS,
189 bt_error_handler, NULL);
190 }
191 }
192
193 void stacktrace_flush(void) {
194 // Nothing to flush with libbacktrace
195 }
196
197 bool stacktrace_capture_is_async_signal_safe(void) {
198 // libbacktrace may use malloc depending on configuration
199 // Check the BACKTRACE_USES_MALLOC define
200 #if BACKTRACE_USES_MALLOC
201 return false;
202 #else
203 return true;
204 #endif
205 }
206
207 bool stacktrace_available(void) {
208 return backtrace_state != NULL;
209 }
210
211 NEVER_INLINE
212 void stacktrace_capture(BUFFER *wb) {
213 root_cause_function[0] = '\0';
214
215 if (!backtrace_state) {
216 buffer_strcat(wb, NO_STACK_TRACE_PREFIX "libbacktrace not initialized");
217 return;
218 }
219
220 backtrace_data_t bt_data = {
221 .wb = wb,
222 .frame_count = 0,
223 .first_frame = true,
224 .found_signal_handler = false
225 };
226
227 // Skip one frame to hide stacktrace_capture() itself
228 backtrace_full(backtrace_state, 1, bt_full_handler,
229 bt_error_handler, &bt_data);
230
231 // If no frames were reported
232 if (bt_data.frame_count == 0) {
233 buffer_strcat(wb, NO_STACK_TRACE_PREFIX "libbacktrace reports no frames");
234 }
235 }
236
237 // Implementation-specific function to collect stack trace frames
238 NEVER_INLINE
239 int impl_stacktrace_get_frames(void **frames, int max_frames, int skip_frames) {
240 if (!backtrace_state || !frames || max_frames <= 0)
241 return 0;
242
243 // Collect frames
244 collect_frames_data_t data = {
245 .frames = frames,
246 .max_frames = max_frames,
247 .num_frames = 0,
248 .skip_frames = skip_frames + 1 // +1 to also skip this function itself
249 };
250
251 // Pass 0 as skip_frames to backtrace_simple and let the callback handle skipping
252 backtrace_simple(backtrace_state, 0, bt_collect_frames_callback, bt_error_handler, &data);
253
254 return data.num_frames;
255 }
256
257 // Implementation-specific function to convert a stacktrace to a buffer
258 void impl_stacktrace_to_buffer(STACKTRACE trace, BUFFER *wb) {
259 struct stacktrace *st = (struct stacktrace *)trace;
260
261 // Resolve each frame
262 backtrace_data_t bt_data = {
263 .wb = wb,
264 .frame_count = 0,
265 .first_frame = true,
266 .found_signal_handler = false
267 };
268
269 for (int i = 0; i < st->frame_count; i++) {
270 backtrace_pcinfo(
271 backtrace_state,
272 (uintptr_t)st->frames[i],
273 bt_full_handler,
274 bt_error_handler,
275 &bt_data
276 );
277 }
278
279 // If we couldn't resolve any frames, use addresses
280 if (bt_data.frame_count == 0) {
281 for (int i = 0; i < st->frame_count; i++) {
282 if (i > 0)
283 buffer_putc(wb, '\n');
284
285 buffer_putc(wb, '#');
286 buffer_print_uint64(wb, i);
287 buffer_strcat(wb, " <unknown> [");
288 buffer_print_uint64_hex(wb, (uint64_t)st->frames[i]);
289 buffer_putc(wb, ']');
290 }
291 }
292 }
293
294 #endif // USE_LIBBACKTRACE