master
c 227 lines 7.11 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "stacktrace-common.h"
4
5 // Stacktrace cache
6 STACKTRACE_JudyLSet stacktrace_cache;
7 SPINLOCK stacktrace_lock = SPINLOCK_INITIALIZER;
8 bool cache_initialized = false;
9
10 // The signal handler function name to filter out in stack traces
11 const char *signal_handler_function = "nd_signal_handler";
12
13 // List of auxiliary functions that should not be reported as root cause
14 const char *auxiliary_functions[] = {
15 "nd_uuid_copy",
16 "out_of_memory",
17 "shutdown_timed_out",
18 NULL // Terminator
19 };
20
21 // List of logging functions to filter out
22 const char *logging_functions[] = {
23 "netdata_logger",
24 "netdata_logger_with_limit",
25 "netdata_logger_fatal",
26 NULL // Terminator
27 };
28
29 // Thread-local buffer to store the first netdata function encountered in a stack trace
30 __thread char root_cause_function[48];
31
32 // Set the signal handler function name to filter out in stack traces
33 void stacktrace_set_signal_handler_function(const char *function_name) {
34 signal_handler_function = function_name;
35 }
36
37 // Returns the first netdata function found in the stack trace
38 const char *stacktrace_root_cause_function(void) {
39 return root_cause_function[0] ? root_cause_function : NULL;
40 }
41
42 // Initialize the stacktrace cache
43 void stacktrace_cache_init(void) {
44 if (cache_initialized)
45 return;
46
47 spinlock_lock(&stacktrace_lock);
48 if (!cache_initialized) {
49 STACKTRACE_INIT(&stacktrace_cache);
50 cache_initialized = true;
51 }
52 spinlock_unlock(&stacktrace_lock);
53 }
54
55 // Main initialization function - this ensures the cache is always initialized
56 void stacktrace_init(void) {
57 // Always initialize the cache
58 stacktrace_cache_init();
59
60 // Call the backend-specific initialization
61 impl_stacktrace_init();
62 }
63
64 // Allocate a new stacktrace structure with the given number of frames
65 struct stacktrace *stacktrace_create(int num_frames) {
66 size_t size = sizeof(struct stacktrace) + ((num_frames - 1) * sizeof(void *));
67 struct stacktrace *trace = callocz(1, size);
68 trace->frame_count = num_frames;
69 return trace;
70 }
71
72 // Exact match check if a function is in the auxiliary list (strcmp)
73 bool stacktrace_is_auxiliary_function(const char *function) {
74 if (!function || !*function)
75 return false;
76
77 for (int i = 0; auxiliary_functions[i]; i++) {
78 if (strcmp(function, auxiliary_functions[i]) == 0)
79 return true;
80 }
81
82 return false;
83 }
84
85 // Exact match check if a function is a logging function (strcmp)
86 bool stacktrace_is_logging_function(const char *function) {
87 if (!function || !*function)
88 return false;
89
90 for (int i = 0; logging_functions[i]; i++) {
91 if (strcmp(function, logging_functions[i]) == 0)
92 return true;
93 }
94
95 return false;
96 }
97
98 // Substring check if a function contains a logging function name (strstr)
99 bool stacktrace_contains_logging_function(const char *text) {
100 if (!text || !*text)
101 return false;
102
103 for (int i = 0; logging_functions[i]; i++) {
104 if (strstr(text, logging_functions[i]) != NULL)
105 return true;
106 }
107
108 return false;
109 }
110
111 bool stacktrace_is_netdata_function(const char *function, const char *filename) {
112 return function && *function && filename && *filename &&
113 strstr(filename, "/src/") &&
114 !strstr(filename, "/vendored/") &&
115 !stacktrace_contains_logging_function(function);
116 }
117
118 // Exact match check if a function is the signal handler (strcmp)
119 bool stacktrace_is_signal_handler_function(const char *function) {
120 return function && *function &&
121 signal_handler_function && *signal_handler_function &&
122 strcmp(function, signal_handler_function) == 0;
123 }
124
125 // Substring check if a function contains the signal handler name (strstr)
126 bool stacktrace_contains_signal_handler_function(const char *text) {
127 return text && *text &&
128 signal_handler_function && *signal_handler_function &&
129 strstr(text, signal_handler_function) != NULL;
130 }
131
132 // Store a function name as the first netdata function found
133 void stacktrace_keep_first_root_cause_function(const char *function) {
134 if (!function || !*function || root_cause_function[0])
135 return; // Already have a function or null input
136
137 // Skip auxiliary functions and logging functions
138 if (stacktrace_is_auxiliary_function(function) || stacktrace_is_logging_function(function))
139 return;
140
141 strncpyz(root_cause_function, function, sizeof(root_cause_function) - 1);
142 }
143
144 // Get the current stacktrace - public API
145 NEVER_INLINE
146 STACKTRACE stacktrace_get(int skip_frames) {
147 // Make sure cache is initialized
148 stacktrace_cache_init();
149
150 // Get the frames from the implementation
151 void *frames[50] = {0};
152 // Add 1 to skip_frames to also skip stacktrace_get() itself
153 int num_frames = impl_stacktrace_get_frames(frames, 50, skip_frames + 1);
154
155 if (num_frames <= 0)
156 return NULL;
157
158 // Calculate hash
159 uint64_t hash = XXH3_64bits(frames, num_frames * sizeof(void *));
160
161 // Look up in cache first
162 spinlock_lock(&stacktrace_lock);
163
164 struct stacktrace *trace = STACKTRACE_GET(&stacktrace_cache, hash);
165
166 // If existing trace found, verify it's the same frames
167 // This handles hash collisions
168 if (trace) {
169 if (trace->frame_count != num_frames ||
170 memcmp(trace->frames, frames, num_frames * sizeof(void *)) != 0) {
171 // Hash collision - use linear probing
172 int i = 1;
173 uint64_t new_hash = hash;
174 do {
175 new_hash = hash + i;
176 trace = STACKTRACE_GET(&stacktrace_cache, new_hash);
177
178 if (!trace ||
179 (trace->frame_count == num_frames &&
180 memcmp(trace->frames, frames, num_frames * sizeof(void *)) == 0)) {
181 break; // Either found a match or empty slot
182 }
183 i++;
184 } while (i < 10); // Limit search to avoid infinite loops
185
186 hash = new_hash;
187 }
188 }
189
190 // If not found or hash collision, create new entry
191 if (!trace) {
192 trace = stacktrace_create(num_frames);
193 trace->hash = hash;
194 memcpy(trace->frames, frames, num_frames * sizeof(void *));
195 STACKTRACE_SET(&stacktrace_cache, hash, trace);
196 }
197
198 spinlock_unlock(&stacktrace_lock);
199
200 return trace;
201 }
202
203 // Convert a stacktrace to a buffer - public API
204 void stacktrace_to_buffer(STACKTRACE trace, BUFFER *wb) {
205 if (!trace || !wb) {
206 if (wb)
207 buffer_strcat(wb, NO_STACK_TRACE_PREFIX "invalid stacktrace");
208 return;
209 }
210
211 struct stacktrace *st = (struct stacktrace *)trace;
212
213 // If we already have cached text representation, use it
214 if (st->text) {
215 buffer_strcat(wb, st->text);
216 return;
217 }
218
219 // Use the implementation-specific function for conversion
220 impl_stacktrace_to_buffer(trace, wb);
221
222 // Cache the text representation
223 spinlock_lock(&stacktrace_lock);
224 if (!st->text)
225 st->text = strdupz(buffer_tostring(wb));
226 spinlock_unlock(&stacktrace_lock);
227 }