2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 * @brief Various functions to aid in debugging
21 * @copyright 2013 The FreeRADIUS server project
22 * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
24 #include <freeradius-devel/libradius.h>
27 * runtime backtrace functions are not POSIX but are included in
28 * glibc, OSX >= 10.5 and various BSDs
30 #ifdef HAVE_EXECINFO_H
31 # include <execinfo.h>
35 #define PTHREAD_MUTEX_LOCK pthread_mutex_lock
36 #define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
38 #define PTHREAD_MUTEX_LOCK(_x)
39 #define PTHREAD_MUTEX_UNLOCK(_x)
42 #ifdef HAVE_EXECINFO_H
43 # define MAX_BT_FRAMES 128
44 # define MAX_BT_ENTRIES 65536 //!< Should be a power of 2
46 # ifdef HAVE_PTHREAD_H
47 static pthread_mutex_t fr_debug_init = PTHREAD_MUTEX_INITIALIZER;
50 typedef struct fr_bt_info {
51 void *obj; //!< Memory address of the block of allocated memory.
52 void *frames[MAX_BT_FRAMES]; //!< Backtrace frame data
53 int count; //!< Number of frames stored
57 void *obj; //!< Pointer to the parent object, this is our needle
58 //!< when we iterate over the contents of the circular buffer.
59 fr_cbuff_t *cbuff; //!< Where we temporarily store the backtraces
63 static int fr_debugger_present = -1;
65 /** Stub callback to see if the SIGTRAP handler is overriden
67 * @param signum signal raised.
69 static void _sigtrap_handler(UNUSED int signum)
71 fr_debugger_present = 0;
72 signal(SIGTRAP, SIG_DFL);
75 /** Break in GDB (if were running under GDB)
77 * If the server is running under GDB this will raise a SIGTRAP which
78 * will pause the running process.
80 * If the server is not running under GDB then this will do nothing.
82 void fr_debug_break(void)
84 if (fr_debugger_present == -1) {
85 fr_debugger_present = 0;
86 signal(SIGTRAP, _sigtrap_handler);
88 } else if (fr_debugger_present == 1) {
93 #ifdef HAVE_EXECINFO_H
94 /** Generate a backtrace for an object during destruction
96 * If this is the first entry being inserted
98 static int _fr_do_bt(fr_bt_marker_t *marker)
102 if (!fr_assert(marker->obj) || !fr_assert(marker->cbuff)) {
106 bt = talloc_zero(marker->cbuff, fr_bt_info_t);
110 bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
111 fr_cbuff_rp_insert(marker->cbuff, bt);
116 /** Print backtrace entry for a given object
118 * @param cbuff to search in.
119 * @param obj pointer to original object
121 void backtrace_print(fr_cbuff_t *cbuff, void *obj)
128 while ((p = fr_cbuff_rp_next(cbuff, NULL))) {
129 if ((p == obj) || !obj) {
131 frames = backtrace_symbols(p->frames, p->count);
133 fprintf(stderr, "Stacktrace for: %p\n", p);
134 for (i = 0; i < p->count; i++) {
135 fprintf(stdout, "%s\n", frames[i]);
138 /* We were only asked to look for one */
146 fprintf(stderr, "No backtrace available for %p", obj);
150 /** Inserts a backtrace marker into the provided context
152 * Allows for maximum laziness and will initialise a circular buffer if one has not already been created.
154 * Code augmentation should look something like:
156 // Create a static cbuffer pointer, the first call to backtrace_attach will initialise it
157 static fr_cbuff *my_obj_bt;
159 my_obj_t *alloc_my_obj(TALLOC_CTX *ctx) {
162 this = talloc(ctx, my_obj_t);
164 // Attach backtrace marker to object
165 backtrace_attach(&my_obj_bt, this);
171 * Then, later when a double free occurs:
173 (gdb) call backtrace_print(&my_obj_bt, <pointer to double freed memory>)
176 * which should print a limited backtrace to stderr. Note, this backtrace will not include any argument
177 * values, but should at least show the code path taken.
179 * @param cbuff this should be a pointer to a static *fr_cbuff.
180 * @param obj we want to generate a backtrace for.
182 fr_bt_marker_t *fr_backtrace_attach(fr_cbuff_t **cbuff, TALLOC_CTX *obj)
184 fr_bt_marker_t *marker;
186 if (*cbuff == NULL) {
187 PTHREAD_MUTEX_LOCK(&fr_debug_init);
188 /* Check again now we hold the mutex - eww*/
189 if (*cbuff == NULL) {
192 ctx = fr_autofree_ctx();
193 *cbuff = fr_cbuff_alloc(ctx, MAX_BT_ENTRIES, true);
195 PTHREAD_MUTEX_UNLOCK(&fr_debug_init);
198 marker = talloc(obj, fr_bt_marker_t);
203 marker->obj = (void *) obj;
204 marker->cbuff = *cbuff;
206 talloc_set_destructor(marker, _fr_do_bt);
211 void backtrace_print(UNUSED fr_cbuff_t *cbuff, UNUSED void *obj)
213 fr_perror("Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo");
215 fr_bt_marker_t *fr_backtrace_attach(UNUSED fr_cbuff_t **cbuff, UNUSED TALLOC_CTX *obj)
217 fr_perror("Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo");
220 #endif /* ifdef HAVE_EXECINFO_H */