The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
backtrace.c
Go to the documentation of this file.
1#include <dlfcn.h>
2
3#include <freeradius-devel/util/backtrace.h>
4#include <freeradius-devel/util/debug.h>
5#include <freeradius-devel/util/fring.h>
6#include <freeradius-devel/util/misc.h>
7
8#ifdef HAVE_BACKTRACE
9# include <freeradius-devel/backtrace/backtrace.h>
10
11static struct backtrace_state *backtrace_state = NULL; //!< Backtrace state for the backtrace functions
12 ///< This is initialised to be thread-safe, so we only need one.
13
14/** Used when building without libbacktrace to record frame information
15 */
16typedef struct {
17 char const *library; //!< Backtrace library name.
18 char const *filename; //!< Backtrace file.
19 char const *function; //!< Backtrace function.
20 bool function_guess; //!< Whether dladdr guessed the function.
21 //!< This is true if the function name is not in the
22 //!< symbol table, but was guessed from the program counter.
23 unsigned int lineno; //!< Backtrace line number.
24 unsigned int frameno; //!< Backtrace frame number.
25 uintptr_t pc; //!< Backtrace program counter.
26} fr_bt_info_frame_t;
27#elif defined(HAVE_EXECINFO)
28# include <execinfo.h>
29#endif
30
31# ifndef MAX_BT_FRAMES
32# define MAX_BT_FRAMES 128
33# endif
34# ifndef MAX_BT_CBUFF
35# define MAX_BT_CBUFF 1048576 //!< Should be a power of 2
36# endif
37
38static pthread_mutex_t fr_backtrace_lock = PTHREAD_MUTEX_INITIALIZER;
39
40typedef struct {
41 void *obj; //!< Memory address of the block of allocated memory.
42#ifdef HAVE_BACKTRACE
43 fr_bt_info_frame_t *frames[MAX_BT_FRAMES]; //!< Backtrace frame data
44#else
45 void *frames[MAX_BT_FRAMES]; //!< Backtrace frame data
46#endif
47 int count; //!< Number of frames stored
49
51 void *obj; //!< Pointer to the parent object, this is our needle
52 //!< when we iterate over the contents of the circular buffer.
53 fr_fring_t *fring; //!< Where we temporarily store the backtraces
54};
55
56#ifdef HAVE_BACKTRACE
57/** Log faults from libbacktrace
58 *
59 */
60static void _backtrace_error(UNUSED void *data, const char *msg, int errnum)
61{
62 FR_FAULT_LOG("Backtrace error: %s (%d)", msg, errnum);
63}
64
65static void backtrace_info_sanitise(fr_bt_info_frame_t *info)
66{
67 Dl_info dl_info;
68
69 if (dladdr((void *)info->pc, &dl_info) != 0) {
70 info->library = dl_info.dli_fname;
71 if (!info->function) {
72 info->function = dl_info.dli_sname;
73 info->function_guess = true;
74 }
75 }
76}
77
78static void backtrace_info_print(fr_bt_info_frame_t *frame, int fd, bool trim_path)
79{
80 if (!frame->library && !frame->filename) {
81 dprintf(fd, "#%u: 0x%lx\n",
82 frame->frameno,
83 (unsigned long)frame->pc);
84 return;
85 }
86 else if (!frame->filename) {
87 dprintf(fd, "%u: 0x%lx %s in %s()\n",
88 frame->frameno,
89 (unsigned long)frame->pc,
90 trim_path ? fr_filename(frame->library) : frame->library,
91 frame->function);
92 return;
93 }
94 dprintf(fd, "#%u: 0x%lx %s in %s() at %s:%d\n",
95 frame->frameno,
96 (unsigned long)frame->pc,
97 trim_path ? fr_filename(frame->library) : frame->library,
98 frame->function,
99 trim_path ? fr_filename_common_trim(frame->filename, frame->library) : frame->filename,
100 frame->lineno);
101
102}
103
104static int _backtrace_info_record(void *data, uintptr_t pc,
105 const char *filename, int lineno,
106 const char *function)
107{
108 fr_bt_info_t *info = talloc_get_type_abort(data, fr_bt_info_t);
109 fr_bt_info_frame_t *frame;
110
111 if (info->count >= (int)NUM_ELEMENTS(info->frames)) return 0;
112
113 frame = talloc_zero(info, fr_bt_info_frame_t);
114 if (!frame) return -1;
115
116 frame->filename = talloc_strdup(frame, filename);
117 frame->function = talloc_strdup(frame, function);
118 frame->lineno = lineno;
119 frame->frameno = info->count;
120 frame->pc = pc;
121
122 backtrace_info_sanitise(frame);
123
124 info->frames[info->count++] = frame;
125
126 return 0;
127}
128
129static void backtrace_record(fr_bt_info_t *info)
130{
131 backtrace_full(backtrace_state, 0, _backtrace_info_record, _backtrace_error, info);
132}
133
134static int _backtrace_print(void *data, uintptr_t pc,
135 const char *filename, int lineno,
136 const char *function)
137{
138 unsigned int *frame_no = ((unsigned int *)data);
139 fr_bt_info_frame_t frame = {
140 .filename = filename,
141 .lineno = lineno,
142 .function = function,
143 .frameno = *frame_no,
144 .pc = pc,
145 };
146
147 backtrace_info_sanitise(&frame);
148 backtrace_info_print(&frame, fr_fault_log_fd, true);
149
150 (*frame_no)++;
151 return 0;
152}
153
154void fr_backtrace(void)
155{
156 unsigned int frame = 0;
157
158 if ((fr_fault_log_fd >= 0) && backtrace_state) {
159 FR_FAULT_LOG("Backtrace:");
160 backtrace_full(backtrace_state, 0, _backtrace_print, _backtrace_error, &frame);
161 }
162}
163#elif defined(HAVE_EXECINFO)
164void fr_backtrace(void)
165{
166 /*
167 * Produce a simple backtrace - They're very basic but at least give us an
168 * idea of the area of the code we hit the issue in.
169 *
170 * See below in fr_fault_setup() and
171 * https://sourceware.org/bugzilla/show_bug.cgi?id=16159
172 * for why we only print backtraces in debug builds if we're using GLIBC.
173 */
174#if (!defined(NDEBUG) || !defined(__GNUC__))
175 if (fr_fault_log_fd >= 0) {
176 size_t frame_count;
177 void *stack[MAX_BT_FRAMES];
178
179 frame_count = backtrace(stack, MAX_BT_FRAMES);
180
181 FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
182
183 backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
184 }
185#endif
186 return;
187}
188#else
189void fr_backtrace(void)
190{
191 return;
192}
193#endif
194
195#if defined(HAVE_BACKTRACE) || defined(HAVE_EXECINFO)
196/** Print backtrace entry for a given object
197 *
198 * @param fring to search in.
199 * @param obj pointer to original object
200 */
201void fr_backtrace_print(fr_fring_t *fring, void *obj)
202{
203 fr_bt_info_t *p;
204 bool found = false;
205
206 while ((p = fr_fring_next(fring))) {
207 if ((p->obj == obj) || !obj) {
208 found = true;
209
210 fprintf(stderr, "Stacktrace for: %p\n", p->obj);
211#ifdef HAVE_BACKTRACE
212 {
213 int i;
214
215 for (i = 0; i < p->count; i++) {
216 backtrace_info_print(p->frames[i], fr_fault_log_fd, true);
217 }
218 }
219#else
220 backtrace_symbols_fd(p->frames, p->count, fr_fault_log_fd);
221#endif
222 }
223 }
224
225 if (!found) {
226 fprintf(stderr, "No backtrace available for %p", obj);
227 }
228}
229
230/** Generate a backtrace for an object
231 *
232 * If this is the first entry being inserted
233 */
234static int _backtrace_do(fr_bt_marker_t *marker)
235{
236 fr_bt_info_t *bt;
237
238 if (!fr_cond_assert(marker->obj) || !fr_cond_assert(marker->fring)) return -1;
239
240 bt = talloc_zero(NULL, fr_bt_info_t);
241 if (!bt) return -1;
242
243 bt->obj = marker->obj;
244#ifdef HAVE_BACKTRACE
245
246#else
247 bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
248#endif
249 fr_fring_overwrite(marker->fring, bt);
250
251 return 0;
252}
253
254/** Inserts a backtrace marker into the provided context
255 *
256 * Allows for maximum laziness and will initialise a circular buffer if one has not already been created.
257 *
258 * Code augmentation should look something like:
259@verbatim
260 // Create a static fring pointer, the first call to backtrace_attach will initialise it
261 static fr_fring_t *my_obj_bt;
262
263 my_obj_t *alloc_my_obj(TALLOC_CTX *ctx) {
264 my_obj_t *this;
265
266 this = talloc(ctx, my_obj_t);
267
268 // Attach backtrace marker to object
269 backtrace_attach(&my_obj_bt, this);
270
271 return this;
272 }
273@endverbatim
274 *
275 * Then, later when a double free occurs:
276@verbatim
277 (gdb) call backtrace_print(&my_obj_bt, <pointer to double freed memory>)
278@endverbatim
279 *
280 * which should print a limited backtrace to stderr. Note, this backtrace will not include any argument
281 * values, but should at least show the code path taken.
282 *
283 * @param fring this should be a pointer to a static *fr_fring_buffer.
284 * @param obj we want to generate a backtrace for.
285 */
286fr_bt_marker_t *fr_backtrace_attach(fr_fring_t **fring, TALLOC_CTX *obj)
287{
288 fr_bt_marker_t *marker;
289
290 if (*fring == NULL) {
291 pthread_mutex_lock(&fr_backtrace_lock);
292 if (*fring == NULL) *fring = fr_fring_alloc(NULL, MAX_BT_CBUFF, true);
293 pthread_mutex_unlock(&fr_backtrace_lock);
294 }
295
296 marker = talloc(obj, fr_bt_marker_t);
297 if (!marker) {
298 return NULL;
299 }
300
301 marker->obj = (void *) obj;
302 marker->fring = *fring;
303
304 fprintf(stderr, "Backtrace attached to %s %p\n", talloc_get_name(obj), obj);
305 /*
306 * Generate the backtrace for memory allocation
307 */
308 _backtrace_do(marker);
309 talloc_set_destructor(marker, _backtrace_do);
310
311 return marker;
312}
313#else
315{
316 fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo, or libbacktrace\n");
317 abort();
318}
319#endif
320
322#ifndef HAVE_BACKTRACE
323 UNUSED
324#endif
325 char const *program)
326{
327#ifdef HAVE_BACKTRACE
328 /*
329 * Initialise the state for libbacktrace. As per the docs
330 * these resources can never be freed, and should be ignore
331 * in any leak tracking code.
332 */
333 backtrace_state = backtrace_create_state(program, 1, _backtrace_error, NULL);
334#elif defined(HAVE_EXECINFO) && defined(__GNUC__) && !defined(NDEBUG)
335 /*
336 * We need to pre-load lgcc_s, else we can get into a deadlock
337 * in fr_fault, as backtrace() attempts to dlopen it.
338 *
339 * Apparently there's a performance impact of loading lgcc_s,
340 * so only do it if this is a debug build.
341 *
342 * See: https://sourceware.org/bugzilla/show_bug.cgi?id=16159
343 */
344 {
345 void *stack[10];
346
347 backtrace(stack, 10);
348 }
349#endif
350}
log_entry msg
Definition acutest.h:796
void * obj
Memory address of the block of allocated memory.
Definition backtrace.c:41
fr_fring_t * fring
Where we temporarily store the backtraces.
Definition backtrace.c:53
void * frames[MAX_BT_FRAMES]
Backtrace frame data.
Definition backtrace.c:45
void * obj
Pointer to the parent object, this is our needle when we iterate over the contents of the circular bu...
Definition backtrace.c:51
void fr_backtrace(void)
Definition backtrace.c:189
#define MAX_BT_CBUFF
Should be a power of 2.
Definition backtrace.c:35
#define MAX_BT_FRAMES
Definition backtrace.c:32
fr_bt_marker_t * fr_backtrace_attach(UNUSED fr_fring_t **fring, UNUSED TALLOC_CTX *obj)
Definition backtrace.c:314
void fr_backtrace_init(UNUSED char const *program)
Definition backtrace.c:321
int count
Number of frames stored.
Definition backtrace.c:47
static pthread_mutex_t fr_backtrace_lock
Definition backtrace.c:38
void fr_backtrace_print(fr_fring_t *fring, void *obj)
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
int fr_fault_log_fd
Where to write debug output.
Definition debug.c:72
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:50
int fr_fring_overwrite(fr_fring_t *fring, void *in)
Insert a new item into the circular buffer, freeing the tail if we hit it.
Definition fring.c:120
void * fr_fring_next(fr_fring_t *fring)
Remove an item from the buffer.
Definition fring.c:177
fr_fring_t * fr_fring_alloc(TALLOC_CTX *ctx, uint32_t size, bool lock)
Initialise a ring buffer with fixed element size.
Definition fring.c:78
Standard thread safe circular buffer.
Definition fring.c:36
static char * stack[MAX_STACK]
Definition radmin.c:159
char const * fr_filename_common_trim(char const *path, char const *common)
Trim a common prefix from a filename.
Definition misc.c:506
char const * fr_filename(char const *path)
Get the filename from a path.
Definition misc.c:490
static char const * program
Definition radiusd.c:85
static fr_slen_t data
Definition value.h:1326