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>
25 #include <freeradius-devel/libradius.h>
29 #if defined(HAVE_MALLOPT) && defined(HAVE_MALLOC_H)
34 * runtime backtrace functions are not POSIX but are included in
35 * glibc, OSX >= 10.5 and various BSDs
38 # include <execinfo.h>
41 #ifdef HAVE_SYS_PRCTL_H
42 # include <sys/prctl.h>
45 #ifdef HAVE_SYS_PTRACE_H
46 # include <sys/ptrace.h>
47 # if !defined(PTRACE_ATTACH) && defined(PT_ATTACH)
48 # define PTRACE_ATTACH PT_ATTACH
50 # if !defined(PTRACE_DETACH) && defined(PT_DETACH)
51 # define PTRACE_DETACH PT_DETACH
55 #ifdef HAVE_SYS_RESOURCE_H
56 # include <sys/resource.h>
60 # define PTHREAD_MUTEX_LOCK pthread_mutex_lock
61 # define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
63 # define PTHREAD_MUTEX_LOCK(_x)
64 # define PTHREAD_MUTEX_UNLOCK(_x)
68 # ifndef MAX_BT_FRAMES
69 # define MAX_BT_FRAMES 128
72 # define MAX_BT_CBUFF 1048576 //!< Should be a power of 2
75 # ifdef HAVE_PTHREAD_H
76 static pthread_mutex_t fr_debug_init = PTHREAD_MUTEX_INITIALIZER;
79 typedef struct fr_bt_info {
80 void *obj; //!< Memory address of the block of allocated memory.
81 void *frames[MAX_BT_FRAMES]; //!< Backtrace frame data
82 int count; //!< Number of frames stored
86 void *obj; //!< Pointer to the parent object, this is our needle
87 //!< when we iterate over the contents of the circular buffer.
88 fr_cbuff_t *cbuff; //!< Where we temporarily store the backtraces
92 static char panic_action[512]; //!< The command to execute when panicking.
93 static fr_fault_cb_t panic_cb = NULL; //!< Callback to execute whilst panicking, before the
96 static bool dump_core; //!< Whether we should drop a core on fatal signals.
98 static int fr_fault_log_fd = STDERR_FILENO; //!< Where to write debug output.
100 fr_debug_state_t fr_debug_state = DEBUG_STATE_UNKNOWN; //!< Whether we're attached to by a debugger.
102 #ifdef HAVE_SYS_RESOURCE_H
103 static struct rlimit core_limits;
106 static TALLOC_CTX *talloc_null_ctx;
107 static TALLOC_CTX *talloc_autofree_ctx;
109 #ifdef HAVE_SYS_PTRACE_H
111 # define _PTRACE(_x, _y) ptrace(_x, _y, NULL, NULL)
113 # define _PTRACE(_x, _y) ptrace(_x, _y, NULL, 0)
116 # ifdef HAVE_CAPABILITY_H
117 # include <sys/capability.h>
120 /** Determine if we're running under a debugger by attempting to attach using pattach
122 * @return 0 if we're not, 1 if we are, -1 if we can't tell because of an error,
123 * -2 if we can't tell because we don't have the CAP_SYS_PTRACE capability.
125 static int fr_get_debug_state(void)
129 int from_child[2] = {-1, -1};
131 #ifdef HAVE_CAPABILITY_H
132 cap_flag_value_t value;
136 * If we're running under linux, we first need to check if we have
137 * permission to to ptrace. We do that using the capabilities
140 current = cap_get_proc();
142 fr_strerror_printf("Failed getting process capabilities: %s", fr_syserror(errno));
143 return DEBUG_STATE_UNKNOWN;
146 if (cap_get_flag(current, CAP_SYS_PTRACE, CAP_PERMITTED, &value) < 0) {
147 fr_strerror_printf("Failed getting permitted ptrace capability state: %s",
150 return DEBUG_STATE_UNKNOWN;
153 if ((value == CAP_SET) && (cap_get_flag(current, CAP_SYS_PTRACE, CAP_EFFECTIVE, &value) < 0)) {
154 fr_strerror_printf("Failed getting effective ptrace capability state: %s",
157 return DEBUG_STATE_UNKNOWN;
161 * We don't have permission to ptrace, so this test will always fail.
163 if (value == CAP_CLEAR) {
164 fr_strerror_printf("ptrace capability not set. If debugger detection is required run as root or: "
165 "setcap cap_sys_ptrace+ep <path_to_radiusd>");
167 return DEBUG_STATE_UNKNOWN_NO_PTRACE_CAP;
172 if (pipe(from_child) < 0) {
173 fr_strerror_printf("Error opening internal pipe: %s", fr_syserror(errno));
174 return DEBUG_STATE_UNKNOWN;
179 fr_strerror_printf("Error forking: %s", fr_syserror(errno));
180 return DEBUG_STATE_UNKNOWN;
185 int8_t ret = DEBUG_STATE_NOT_ATTACHED;
186 int ppid = getppid();
188 /* Close parent's side */
189 close(from_child[0]);
192 * FreeBSD is extremely picky about the order of operations here
193 * we need to attach, wait *then* write whilst the parent is still
194 * suspended, then detach, continuing the process.
196 * If we don't do it in that order the read in the parent triggers
199 if (_PTRACE(PTRACE_ATTACH, ppid) == 0) {
200 /* Wait for the parent to stop */
201 waitpid(ppid, NULL, 0);
203 /* Tell the parent what happened */
204 if (write(from_child[1], &ret, sizeof(ret)) < 0) {
205 fprintf(stderr, "Writing ptrace status to parent failed: %s", fr_syserror(errno));
209 _PTRACE(PTRACE_DETACH, ppid);
213 ret = DEBUG_STATE_ATTACHED;
214 /* Tell the parent what happened */
215 if (write(from_child[1], &ret, sizeof(ret)) < 0) {
216 fprintf(stderr, "Writing ptrace status to parent failed: %s", fr_syserror(errno));
222 int8_t ret = DEBUG_STATE_UNKNOWN;
225 * The child writes errno (reason) if pattach failed else 0.
227 * This read may be interrupted by pattach,
228 * which is why we need the loop.
230 while ((read(from_child[0], &ret, sizeof(ret)) < 0) && (errno == EINTR));
232 /* Close the pipes here (if we did it above, it might race with pattach) */
233 close(from_child[1]);
234 close(from_child[0]);
236 /* Collect the status of the child */
237 waitpid(pid, NULL, 0);
243 static int fr_get_debug_state(void)
245 fr_strerror_printf("PTRACE not available");
247 return DEBUG_STATE_UNKNOWN_NO_PTRACE;
251 /** Should be run before using setuid or setgid to get useful results
253 * @note sets the fr_debug_state global.
255 void fr_store_debug_state(void)
257 fr_debug_state = fr_get_debug_state();
261 * There are many reasons why this might happen with
262 * a vanilla install, so we don't want to spam users
263 * with messages they won't understand and may not
266 if (fr_debug_state < 0) fprintf(stderr, "Getting debug state failed: %s\n", fr_strerror());
270 /** Return current value of debug_state
272 * @param state to translate into a humanly readable value.
273 * @return humanly readable version of debug state.
275 char const *fr_debug_state_to_msg(fr_debug_state_t state)
278 case DEBUG_STATE_UNKNOWN_NO_PTRACE:
279 return "Debug state unknown (ptrace functionality not available)";
281 case DEBUG_STATE_UNKNOWN_NO_PTRACE_CAP:
282 return "Debug state unknown (cap_sys_ptrace capability not set)";
284 case DEBUG_STATE_UNKNOWN:
285 return "Debug state unknown";
287 case DEBUG_STATE_ATTACHED:
288 return "Found debugger attached";
290 case DEBUG_STATE_NOT_ATTACHED:
291 return "Debugger not attached";
297 /** Break in debugger (if were running under a debugger)
299 * If the server is running under a debugger this will raise a
300 * SIGTRAP which will pause the running process.
302 * If the server is not running under debugger then this will do nothing.
304 void fr_debug_break(bool always)
306 if (always) raise(SIGTRAP);
308 if (fr_debug_state < 0) fr_debug_state = fr_get_debug_state();
309 if (fr_debug_state == DEBUG_STATE_ATTACHED) {
310 fprintf(stderr, "Debugger detected, raising SIGTRAP\n");
318 /** Print backtrace entry for a given object
320 * @param cbuff to search in.
321 * @param obj pointer to original object
323 void backtrace_print(fr_cbuff_t *cbuff, void *obj)
328 while ((p = fr_cbuff_rp_next(cbuff, NULL))) {
329 if ((p->obj == obj) || !obj) {
332 fprintf(stderr, "Stacktrace for: %p\n", p->obj);
333 backtrace_symbols_fd(p->frames, p->count, STDERR_FILENO);
338 fprintf(stderr, "No backtrace available for %p", obj);
342 /** Generate a backtrace for an object
344 * If this is the first entry being inserted
346 int fr_backtrace_do(fr_bt_marker_t *marker)
350 if (!fr_assert(marker->obj) || !fr_assert(marker->cbuff)) return -1;
352 bt = talloc_zero(NULL, fr_bt_info_t);
355 bt->obj = marker->obj;
356 bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
358 fr_cbuff_rp_insert(marker->cbuff, bt);
363 /** Inserts a backtrace marker into the provided context
365 * Allows for maximum laziness and will initialise a circular buffer if one has not already been created.
367 * Code augmentation should look something like:
369 // Create a static cbuffer pointer, the first call to backtrace_attach will initialise it
370 static fr_cbuff_t *my_obj_bt;
372 my_obj_t *alloc_my_obj(TALLOC_CTX *ctx) {
375 this = talloc(ctx, my_obj_t);
377 // Attach backtrace marker to object
378 backtrace_attach(&my_obj_bt, this);
384 * Then, later when a double free occurs:
386 (gdb) call backtrace_print(&my_obj_bt, <pointer to double freed memory>)
389 * which should print a limited backtrace to stderr. Note, this backtrace will not include any argument
390 * values, but should at least show the code path taken.
392 * @param cbuff this should be a pointer to a static *fr_cbuff.
393 * @param obj we want to generate a backtrace for.
395 fr_bt_marker_t *fr_backtrace_attach(fr_cbuff_t **cbuff, TALLOC_CTX *obj)
397 fr_bt_marker_t *marker;
399 if (*cbuff == NULL) {
400 PTHREAD_MUTEX_LOCK(&fr_debug_init);
401 /* Check again now we hold the mutex - eww*/
402 if (*cbuff == NULL) *cbuff = fr_cbuff_alloc(NULL, MAX_BT_CBUFF, true);
403 PTHREAD_MUTEX_UNLOCK(&fr_debug_init);
406 marker = talloc(obj, fr_bt_marker_t);
411 marker->obj = (void *) obj;
412 marker->cbuff = *cbuff;
414 fprintf(stderr, "Backtrace attached to %s %p\n", talloc_get_name(obj), obj);
416 * Generate the backtrace for memory allocation
418 fr_backtrace_do(marker);
419 talloc_set_destructor(marker, fr_backtrace_do);
424 void backtrace_print(UNUSED fr_cbuff_t *cbuff, UNUSED void *obj)
426 fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
428 fr_bt_marker_t *fr_backtrace_attach(UNUSED fr_cbuff_t **cbuff, UNUSED TALLOC_CTX *obj)
430 fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
433 #endif /* ifdef HAVE_EXECINFO */
435 static int _panic_on_free(UNUSED char *foo)
438 return -1; /* this should make the free fail */
441 /** Insert memory into the context of another talloc memory chunk which
442 * causes a panic when freed.
444 * @param ctx TALLOC_CTX to monitor for frees.
446 void fr_panic_on_free(TALLOC_CTX *ctx)
450 ptr = talloc(ctx, char);
451 talloc_set_destructor(ptr, _panic_on_free);
454 /** Set the dumpable flag, also controls whether processes can PATTACH
456 * @param dumpable whether we should allow core dumping
458 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
459 static int fr_set_dumpable_flag(bool dumpable)
461 if (prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0) < 0) {
462 fr_strerror_printf("Cannot re-enable core dumps: prctl(PR_SET_DUMPABLE) failed: %s",
470 static int fr_set_dumpable_flag(UNUSED bool dumpable)
472 fr_strerror_printf("Changing value of PR_DUMPABLE not supported on this system");
477 /** Get the processes dumpable flag
480 #if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_DUMPABLE)
481 static int fr_get_dumpable_flag(void)
485 ret = prctl(PR_GET_DUMPABLE);
487 fr_strerror_printf("Cannot get dumpable flag: %s", fr_syserror(errno));
492 * Linux is crazy and prctl sometimes returns 2 for disabled
494 if (ret != 1) return 0;
498 static int fr_get_dumpable_flag(void)
500 fr_strerror_printf("Getting value of PR_DUMPABLE not supported on this system");
506 /** Get the current maximum for core files
508 * Do this before anything else so as to ensure it's properly initialized.
510 int fr_set_dumpable_init(void)
512 #ifdef HAVE_SYS_RESOURCE_H
513 if (getrlimit(RLIMIT_CORE, &core_limits) < 0) {
514 fr_strerror_printf("Failed to get current core limit: %s", fr_syserror(errno));
521 /** Enable or disable core dumps
523 * @param allow_core_dumps whether to enable or disable core dumps.
525 int fr_set_dumpable(bool allow_core_dumps)
527 dump_core = allow_core_dumps;
529 * If configured, turn core dumps off.
531 if (!allow_core_dumps) {
532 #ifdef HAVE_SYS_RESOURCE_H
533 struct rlimit no_core;
535 no_core.rlim_cur = 0;
536 no_core.rlim_max = 0;
538 if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
539 fr_strerror_printf("Failed disabling core dumps: %s", fr_syserror(errno));
547 if (fr_set_dumpable_flag(true) < 0) return -1;
550 * Reset the core dump limits to their original value.
552 #ifdef HAVE_SYS_RESOURCE_H
553 if (setrlimit(RLIMIT_CORE, &core_limits) < 0) {
554 fr_strerror_printf("Cannot update core dump limit: %s", fr_syserror(errno));
562 /** Reset dumpable state to previously configured value
564 * Needed after suid up/down
566 * @return 0 on success, else -1 on failure.
568 int fr_reset_dumpable(void)
570 return fr_set_dumpable(dump_core);
573 /** Check to see if panic_action file is world writeable
575 * @return 0 if file is OK, else -1.
577 static int fr_fault_check_permissions(void)
585 * Try and guess which part of the command is the binary, and check to see if
586 * it's world writeable, to try and save the admin from their own stupidity.
588 * @fixme we should do this properly and take into account single and double
591 if ((q = strchr(panic_action, ' '))) {
593 * need to use a static buffer, because mallocing memory in a signal handler
594 * is a bad idea and can result in deadlock.
596 len = snprintf(filename, sizeof(filename), "%.*s", (int)(q - panic_action), panic_action);
597 if (is_truncated(len, sizeof(filename))) {
598 fr_strerror_printf("Failed writing panic_action to temporary buffer (truncated)");
606 if (stat(p, &statbuf) == 0) {
608 if ((statbuf.st_mode & S_IWOTH) != 0) {
609 fr_strerror_printf("panic_action file \"%s\" is globally writable", p);
618 /** Prints a simple backtrace (if execinfo is available) and calls panic_action if set.
622 NEVER_RETURNS void fr_fault(int sig)
624 char cmd[sizeof(panic_action) + 20];
626 size_t left = sizeof(cmd), ret;
628 char const *p = panic_action;
634 * If a debugger is attached, we don't want to run the panic action,
635 * as it may interfere with the operation of the debugger.
636 * If something calls us directly we just raise the signal and let
637 * the debugger handle it how it wants.
639 if (fr_debug_state == DEBUG_STATE_ATTACHED) {
640 FR_FAULT_LOG("RAISING SIGNAL: %s", strsignal(sig));
646 * Makes the backtraces slightly cleaner
648 memset(cmd, 0, sizeof(cmd));
650 FR_FAULT_LOG("CAUGHT SIGNAL: %s", strsignal(sig));
653 * Check for administrator sanity.
655 if (fr_fault_check_permissions() < 0) {
656 FR_FAULT_LOG("Refusing to execute panic action: %s", fr_strerror());
661 * Run the callback if one was registered
663 if (panic_cb && (panic_cb(sig) < 0)) goto finish;
666 * Produce a simple backtrace - They're very basic but at least give us an
667 * idea of the area of the code we hit the issue in.
669 * See below in fr_fault_setup() and
670 * https://sourceware.org/bugzilla/show_bug.cgi?id=16159
671 * for why we only print backtraces in debug builds if we're using GLIBC.
673 #if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
674 if (fr_fault_log_fd >= 0) {
676 void *stack[MAX_BT_FRAMES];
678 frame_count = backtrace(stack, MAX_BT_FRAMES);
680 FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
682 backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
686 /* No panic action set... */
687 if (panic_action[0] == '\0') {
688 FR_FAULT_LOG("No panic action set");
692 /* Substitute %p for the current PID (useful for attaching a debugger) */
693 while ((q = strstr(p, "%p"))) {
694 out += ret = snprintf(out, left, "%.*s%d", (int) (q - p), p, (int) getpid());
697 FR_FAULT_LOG("Panic action too long");
703 if (strlen(p) >= left) goto oob;
704 strlcpy(out, p, left);
706 FR_FAULT_LOG("Calling: %s", cmd);
709 bool disable = false;
712 * Here we temporarily enable the dumpable flag so if GBD or LLDB
713 * is called in the panic_action, they can pattach to the running
716 if (fr_get_dumpable_flag() == 0) {
717 if ((fr_set_dumpable_flag(true) < 0) || !fr_get_dumpable_flag()) {
718 FR_FAULT_LOG("Failed setting dumpable flag, pattach may not work: %s", fr_strerror());
722 FR_FAULT_LOG("Temporarily setting PR_DUMPABLE to 1");
728 * We only want to error out here, if dumpable was originally disabled
729 * and we managed to change the value to enabled, but failed
730 * setting it back to disabled.
733 FR_FAULT_LOG("Resetting PR_DUMPABLE to 0");
734 if (fr_set_dumpable_flag(false) < 0) {
735 FR_FAULT_LOG("Failed reseting dumpable flag to off: %s", fr_strerror());
736 FR_FAULT_LOG("Exiting due to insecure process state");
744 FR_FAULT_LOG("Panic action exited with %i", code);
750 /** Callback executed on fatal talloc error
752 * This is the simple version which mostly behaves the same way as the default
753 * one, and will not call panic_action.
755 * @param reason string provided by talloc.
757 static void _fr_talloc_fault_simple(char const *reason) CC_HINT(noreturn);
758 static void _fr_talloc_fault_simple(char const *reason)
760 FR_FAULT_LOG("talloc abort: %s\n", reason);
762 #if defined(HAVE_EXECINFO) && (!defined(NDEBUG) || !defined(__GNUC__))
763 if (fr_fault_log_fd >= 0) {
765 void *stack[MAX_BT_FRAMES];
767 frame_count = backtrace(stack, MAX_BT_FRAMES);
768 FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
769 backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
775 /** Callback executed on fatal talloc error
777 * Translates a talloc abort into a fr_fault call.
778 * Mostly to work around issues with some debuggers not being able to
779 * attach after a SIGABRT has been raised.
781 * @param reason string provided by talloc.
783 static void _fr_talloc_fault(char const *reason) CC_HINT(noreturn);
784 static void _fr_talloc_fault(char const *reason)
786 FR_FAULT_LOG("talloc abort: %s", reason);
793 /** Wrapper to pass talloc log output to our fr_fault_log function
796 static void _fr_talloc_log(char const *msg)
798 fr_fault_log("%s\n", msg);
801 /** Generate a talloc memory report for a context and print to stderr/stdout
803 * @param ctx to generate a report for, may be NULL in which case the root context is used.
805 int fr_log_talloc_report(TALLOC_CTX *ctx)
807 #define TALLOC_REPORT_MAX_DEPTH 20
812 fd = dup(fr_fault_log_fd);
814 fr_strerror_printf("Couldn't write memory report, failed to dup log fd: %s", fr_syserror(errno));
817 log = fdopen(fd, "w");
820 fr_strerror_printf("Couldn't write memory report, fdopen failed: %s", fr_syserror(errno));
825 fprintf(log, "Current state of talloced memory:\n");
826 talloc_report_full(talloc_null_ctx, log);
830 fprintf(log, "Talloc chunk lineage:\n");
831 fprintf(log, "%p (%s)", ctx, talloc_get_name(ctx));
834 while ((i < TALLOC_REPORT_MAX_DEPTH) && (ctx = talloc_parent(ctx))) {
835 fprintf(log, " < %p (%s)", ctx, talloc_get_name(ctx));
842 fprintf(log, "Talloc context level %i:\n", i++);
843 talloc_report_full(ctx, log);
844 } while ((ctx = talloc_parent(ctx)) &&
845 (i < TALLOC_REPORT_MAX_DEPTH) &&
846 (talloc_parent(ctx) != talloc_autofree_ctx) && /* Stop before we hit the autofree ctx */
847 (talloc_parent(ctx) != talloc_null_ctx)); /* Stop before we hit NULL ctx */
856 static int _fr_disable_null_tracking(UNUSED bool *p)
858 talloc_disable_null_tracking();
862 /** Register talloc fault handlers
864 * Just register the fault handlers we need to make talloc
865 * produce useful debugging output.
867 void fr_talloc_fault_setup(void)
869 talloc_set_log_fn(_fr_talloc_log);
870 talloc_set_abort_fn(_fr_talloc_fault_simple);
873 /** Registers signal handlers to execute panic_action on fatal signal
875 * May be called multiple time to change the panic_action/program.
877 * @param cmd to execute on fault. If present %p will be substituted
878 * for the parent PID before the command is executed, and %e
879 * will be substituted for the currently running program.
880 * @param program Name of program currently executing (argv[0]).
881 * @return 0 on success -1 on failure.
883 int fr_fault_setup(char const *cmd, char const *program)
885 static bool setup = false;
887 char *out = panic_action;
888 size_t left = sizeof(panic_action);
896 /* Substitute %e for the current program */
897 while ((q = strstr(p, "%e"))) {
898 out += ret = snprintf(out, left, "%.*s%s", (int) (q - p), p, program ? program : "");
901 fr_strerror_printf("Panic action too long");
907 if (strlen(p) >= left) goto oob;
908 strlcpy(out, p, left);
910 *panic_action = '\0';
914 * Check for administrator sanity.
916 if (fr_fault_check_permissions() < 0) return -1;
918 /* Unsure what the side effects of changing the signal handler mid execution might be */
921 fr_debug_state_t debug_state;
924 * Installing signal handlers interferes with some debugging
925 * operations. Give the developer control over whether the
926 * signal handlers are installed or not.
928 env = getenv("DEBUG");
929 if (!env || (strcmp(env, "no") == 0)) {
930 debug_state = DEBUG_STATE_NOT_ATTACHED;
931 } else if (!strcmp(env, "auto") || !strcmp(env, "yes")) {
933 * Figure out if we were started under a debugger
935 if (fr_debug_state < 0) fr_debug_state = fr_get_debug_state();
936 debug_state = fr_debug_state;
938 debug_state = DEBUG_STATE_ATTACHED;
941 talloc_set_log_fn(_fr_talloc_log);
944 * These signals can't be properly dealt with in the debugger
945 * if we set our own signal handlers.
947 switch (debug_state) {
950 FR_FAULT_LOG("Debugger check failed: %s", fr_strerror());
951 FR_FAULT_LOG("Signal processing in debuggers may not work as expected");
955 case DEBUG_STATE_NOT_ATTACHED:
957 if (fr_set_signal(SIGABRT, fr_fault) < 0) return -1;
960 * Use this instead of abort so we get a
961 * full backtrace with broken versions of LLDB
963 talloc_set_abort_fn(_fr_talloc_fault);
966 if (fr_set_signal(SIGILL, fr_fault) < 0) return -1;
969 if (fr_set_signal(SIGFPE, fr_fault) < 0) return -1;
972 if (fr_set_signal(SIGSEGV, fr_fault) < 0) return -1;
976 case DEBUG_STATE_ATTACHED:
981 * Needed for memory reports
987 tmp = talloc(NULL, bool);
988 talloc_null_ctx = talloc_parent(tmp);
992 * Disable null tracking on exit, else valgrind complains
994 talloc_autofree_ctx = talloc_autofree_context();
995 marker = talloc(talloc_autofree_ctx, bool);
996 talloc_set_destructor(marker, _fr_disable_null_tracking);
999 #if defined(HAVE_MALLOPT) && !defined(NDEBUG)
1001 * If were using glibc malloc > 2.4 this scribbles over
1002 * uninitialised and freed memory, to make memory issues easier
1005 if (!getenv("TALLOC_FREE_FILL")) mallopt(M_PERTURB, 0x42);
1006 mallopt(M_CHECK_ACTION, 3);
1009 #if defined(HAVE_EXECINFO) && defined(__GNUC__) && !defined(NDEBUG)
1011 * We need to pre-load lgcc_s, else we can get into a deadlock
1012 * in fr_fault, as backtrace() attempts to dlopen it.
1014 * Apparently there's a performance impact of loading lgcc_s,
1015 * so only do it if this is a debug build.
1017 * See: https://sourceware.org/bugzilla/show_bug.cgi?id=16159
1022 backtrace(stack, 10);
1031 /** Set a callback to be called before fr_fault()
1033 * @param func to execute. If callback returns < 0
1034 * fr_fault will exit before running panic_action code.
1036 void fr_fault_set_cb(fr_fault_cb_t func)
1041 /** Log output to the fr_fault_log_fd
1043 * We used to support a user defined callback, which was set to a radlog
1044 * function. Unfortunately, when logging to syslog, syslog would malloc memory
1045 * which would result in a deadlock if fr_fault was triggered from within
1048 * Now we just write directly to the FD.
1050 void fr_fault_log(char const *msg, ...)
1054 if (fr_fault_log_fd < 0) return;
1057 vdprintf(fr_fault_log_fd, msg, ap);
1061 /** Set a file descriptor to log memory reports to.
1063 * @param fd to write output to.
1065 void fr_fault_set_log_fd(int fd)
1067 fr_fault_log_fd = fd;
1070 /** A soft assertion which triggers the fault handler in debug builds
1072 * @param file the assertion failed in.
1073 * @param line of the assertion in the file.
1074 * @param expr that was evaluated.
1075 * @param cond Result of evaluating the expression.
1076 * @return the value of cond.
1078 bool fr_assert_cond(char const *file, int line, char const *expr, bool cond)
1081 FR_FAULT_LOG("SOFT ASSERT FAILED %s[%u]: %s", file, line, expr);
1082 #if !defined(NDEBUG)
1091 /** Exit possibly printing a message about why we're exiting.
1093 * @note Use the fr_exit(status) macro instead of calling this function directly.
1095 * @param file where fr_exit() was called.
1096 * @param line where fr_exit() was called.
1097 * @param status we're exiting with.
1099 void NEVER_RETURNS _fr_exit(char const *file, int line, int status)
1102 char const *error = fr_strerror();
1104 if (error && (status != 0)) {
1105 FR_FAULT_LOG("EXIT(%i) CALLED %s[%u]. Last error was: %s", status, file, line, error);
1107 FR_FAULT_LOG("EXIT(%i) CALLED %s[%u]", status, file, line);
1110 fr_debug_break(false); /* If running under GDB we'll break here */
1115 /** Exit possibly printing a message about why we're exiting.
1117 * @note Use the fr_exit_now(status) macro instead of calling this function directly.
1119 * @param file where fr_exit_now() was called.
1120 * @param line where fr_exit_now() was called.
1121 * @param status we're exiting with.
1123 void NEVER_RETURNS _fr_exit_now(char const *file, int line, int status)
1126 char const *error = fr_strerror();
1128 if (error && (status != 0)) {
1129 FR_FAULT_LOG("_EXIT(%i) CALLED %s[%u]. Last error was: %s", status, file, line, error);
1131 FR_FAULT_LOG("_EXIT(%i) CALLED %s[%u]", status, file, line);
1134 fr_debug_break(false); /* If running under GDB we'll break here */