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