#endif
#ifdef HAVE_EXECINFO
-# define MAX_BT_FRAMES 128
-# define MAX_BT_CBUFF 65536 //!< Should be a power of 2
+# ifndef MAX_BT_FRAMES
+# define MAX_BT_FRAMES 128
+# endif
+# ifndef MAX_BT_CBUFF
+# define MAX_BT_CBUFF 1048576 //!< Should be a power of 2
+# endif
# ifdef HAVE_PTHREAD_H
static pthread_mutex_t fr_debug_init = PTHREAD_MUTEX_INITIALIZER;
}
#ifdef HAVE_EXECINFO
-/** Generate a backtrace for an object during destruction
- *
- * If this is the first entry being inserted
- */
-static int _fr_do_bt(fr_bt_marker_t *marker)
-{
- fr_bt_info_t *bt;
-
- if (!fr_assert(marker->obj) || !fr_assert(marker->cbuff)) {
- return -1;
- }
-
- bt = talloc_zero(marker->cbuff, fr_bt_info_t);
- if (!bt) {
- return -1;
- }
- bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
- fr_cbuff_rp_insert(marker->cbuff, bt);
-
- return 0;
-}
-
/** Print backtrace entry for a given object
*
* @param cbuff to search in.
{
fr_bt_info_t *p;
bool found = false;
- int i = 0;
- char **frames;
while ((p = fr_cbuff_rp_next(cbuff, NULL))) {
- if ((p == obj) || !obj) {
+ if ((p->obj == obj) || !obj) {
found = true;
- frames = backtrace_symbols(p->frames, p->count);
-
- fprintf(stderr, "Stacktrace for: %p\n", p);
- for (i = 0; i < p->count; i++) {
- fprintf(stderr, "%s\n", frames[i]);
- }
- /* We were only asked to look for one */
- if (obj) {
- return;
- }
+ fprintf(stderr, "Stacktrace for: %p\n", p->obj);
+ backtrace_symbols_fd(p->frames, p->count, STDERR_FILENO);
}
};
}
}
+/** Generate a backtrace for an object
+ *
+ * If this is the first entry being inserted
+ */
+int fr_backtrace_do(fr_bt_marker_t *marker)
+{
+ fr_bt_info_t *bt;
+
+ if (!fr_assert(marker->obj) || !fr_assert(marker->cbuff)) return -1;
+
+ bt = talloc_zero(NULL, fr_bt_info_t);
+ if (!bt) return -1;
+
+ bt->obj = marker->obj;
+ bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
+
+ fr_cbuff_rp_insert(marker->cbuff, bt);
+
+ return 0;
+}
+
/** Inserts a backtrace marker into the provided context
*
* Allows for maximum laziness and will initialise a circular buffer if one has not already been created.
* Code augmentation should look something like:
@verbatim
// Create a static cbuffer pointer, the first call to backtrace_attach will initialise it
- static fr_cbuff *my_obj_bt;
+ static fr_cbuff_t *my_obj_bt;
my_obj_t *alloc_my_obj(TALLOC_CTX *ctx) {
my_obj_t *this;
if (*cbuff == NULL) {
PTHREAD_MUTEX_LOCK(&fr_debug_init);
/* Check again now we hold the mutex - eww*/
- if (*cbuff == NULL) {
- TALLOC_CTX *ctx;
-
- ctx = fr_autofree_ctx();
- *cbuff = fr_cbuff_alloc(ctx, MAX_BT_CBUFF, true);
- }
+ if (*cbuff == NULL) *cbuff = fr_cbuff_alloc(NULL, MAX_BT_CBUFF, true);
PTHREAD_MUTEX_UNLOCK(&fr_debug_init);
}
marker->obj = (void *) obj;
marker->cbuff = *cbuff;
- talloc_set_destructor(marker, _fr_do_bt);
+ fprintf(stderr, "Backtrace attached to %s %p\n", talloc_get_name(obj), obj);
+ /*
+ * Generate the backtrace for memory allocation
+ */
+ fr_backtrace_do(marker);
+ talloc_set_destructor(marker, fr_backtrace_do);
return marker;
}
/*
* Here we temporarily enable the dumpable flag so if GBD or LLDB
- * is called in the panic_action, they can pattach tot he running
+ * is called in the panic_action, they can pattach to the running
* process.
*/
if (fr_get_dumpable_flag() == 0) {
return -1;
}
- fprintf(log, "Talloc chunk lineage:\n");
- fprintf(log, "%p (%s)", ctx, talloc_get_name(ctx));
- while ((ctx = talloc_parent(ctx))) fprintf(log, " < %p (%s)", ctx, talloc_get_name(ctx));
- fprintf(log, "\n");
-
if (!ctx) {
fprintf(log, "Current state of talloced memory:\n");
talloc_report_full(talloc_null_ctx, log);
- } else do {
- fprintf(log, "Talloc context level %i:\n", i++);
- talloc_report_full(ctx, log);
- } while ((ctx = talloc_parent(ctx)) &&
- (talloc_parent(ctx) != talloc_autofree_ctx) && /* Stop before we hit the autofree ctx */
- (talloc_parent(ctx) != talloc_null_ctx)); /* Stop before we hit NULL ctx */
+ } else {
+ fprintf(log, "Talloc chunk lineage:\n");
+ fprintf(log, "%p (%s)", ctx, talloc_get_name(ctx));
+ while ((ctx = talloc_parent(ctx))) fprintf(log, " < %p (%s)", ctx, talloc_get_name(ctx));
+ fprintf(log, "\n");
+
+ do {
+ fprintf(log, "Talloc context level %i:\n", i++);
+ talloc_report_full(ctx, log);
+ } while ((ctx = talloc_parent(ctx)) &&
+ (talloc_parent(ctx) != talloc_autofree_ctx) && /* Stop before we hit the autofree ctx */
+ (talloc_parent(ctx) != talloc_null_ctx)); /* Stop before we hit NULL ctx */
+ }
fclose(log);
*/
static void _fr_fault_mem_report(int sig)
{
- fr_fault_log("CAUGHT SIGNAL: %s\n", strsignal(sig));
+ FR_FAULT_LOG("CAUGHT SIGNAL: %s", strsignal(sig));
if (fr_log_talloc_report(NULL) < 0) fr_perror("memreport");
}
/* Unsure what the side effects of changing the signal handler mid execution might be */
if (!setup) {
+ if (cmd) {
#ifdef SIGSEGV
- if (fr_set_signal(SIGSEGV, fr_fault) < 0) return -1;
+ if (fr_set_signal(SIGSEGV, fr_fault) < 0) return -1;
#endif
#ifdef SIGBUS
- if (fr_set_signal(SIGBUS, fr_fault) < 0) return -1;
+ if (fr_set_signal(SIGBUS, fr_fault) < 0) return -1;
#endif
#ifdef SIGABRT
- if (fr_set_signal(SIGABRT, fr_fault) < 0) return -1;
- /*
- * Use this instead of abort so we get a
- * full backtrace with broken versions of LLDB
- */
- talloc_set_abort_fn(_fr_talloc_fault);
+ if (fr_set_signal(SIGABRT, fr_fault) < 0) return -1;
+ /*
+ * Use this instead of abort so we get a
+ * full backtrace with broken versions of LLDB
+ */
+ talloc_set_abort_fn(_fr_talloc_fault);
#endif
#ifdef SIGFPE
- if (fr_set_signal(SIGFPE, fr_fault) < 0) return -1;
+ if (fr_set_signal(SIGFPE, fr_fault) < 0) return -1;
#endif
-
+ }
#ifdef SIGUSR1
if (fr_set_signal(SIGUSR1, fr_fault) < 0) return -1;
#endif
TALLOC_CTX *tmp;
bool *marker;
- /*
- * This should create a single NULL context used whenever
- * something is talloced without a parent.
- */
- talloc_enable_null_tracking();
tmp = talloc(NULL, bool);
talloc_null_ctx = talloc_parent(tmp);
talloc_free(tmp);
* uninitialised and freed memory, to make memory issues easier
* to track down.
*/
- mallopt(M_PERTURB, 0x42);
+ if (!getenv("TALLOC_FREE_FILL")) mallopt(M_PERTURB, 0x42);
mallopt(M_CHECK_ACTION, 3);
#endif
*/
inline void fr_verify_vp(char const *file, int line, VALUE_PAIR const *vp)
{
+ if (!vp) {
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR pointer was NULL", file, line);
+ fr_assert(0);
+ fr_exit_now(0);
+ }
+
(void) talloc_get_type_abort(vp, VALUE_PAIR);
if (vp->data.ptr) switch (vp->da->type) {
TALLOC_CTX *parent;
if (!talloc_get_type(vp->data.ptr, uint8_t)) {
- fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
- "uint8_t but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
+ "uint8_t but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
(void) talloc_get_type_abort(vp->data.ptr, uint8_t);
}
len = talloc_array_length(vp->vp_octets);
if (vp->length > len) {
- fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
- "uint8_t data buffer length %zu\n", file, line, vp->da->name, vp->length, len);
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
+ "uint8_t data buffer length %zu\n", file, line, vp->da->name, vp->length, len);
fr_assert(0);
fr_exit_now(1);
}
parent = talloc_parent(vp->data.ptr);
if (parent != vp) {
- fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer is not "
- "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
- file, line, vp->da->name,
- vp, parent, parent ? talloc_get_name(parent) : "NULL");
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer is not "
+ "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
+ file, line, vp->da->name,
+ vp, parent, parent ? talloc_get_name(parent) : "NULL");
fr_assert(0);
fr_exit_now(1);
}
TALLOC_CTX *parent;
if (!talloc_get_type(vp->data.ptr, char)) {
- fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
- "char but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" data buffer type should be "
+ "char but is %s\n", file, line, vp->da->name, talloc_get_name(vp->data.ptr));
(void) talloc_get_type_abort(vp->data.ptr, char);
}
len = (talloc_array_length(vp->vp_strvalue) - 1);
if (vp->length > len) {
- fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
- "char buffer length %zu\n", file, line, vp->da->name, vp->length, len);
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" length %zu is greater than "
+ "char buffer length %zu\n", file, line, vp->da->name, vp->length, len);
fr_assert(0);
fr_exit_now(1);
}
if (vp->vp_strvalue[vp->length] != '\0') {
- fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer not \\0 "
- "terminated\n", file, line, vp->da->name);
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" char buffer not \\0 "
+ "terminated\n", file, line, vp->da->name);
fr_assert(0);
fr_exit_now(1);
}
parent = talloc_parent(vp->data.ptr);
if (parent != vp) {
- fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" uint8_t buffer is not "
- "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
- file, line, vp->da->name,
- vp, parent, parent ? talloc_get_name(parent) : "NULL");
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: VALUE_PAIR \"%s\" uint8_t buffer is not "
+ "parented by VALUE_PAIR %p, instead parented by %p (%s)\n",
+ file, line, vp->da->name,
+ vp, parent, parent ? talloc_get_name(parent) : "NULL");
fr_assert(0);
fr_exit_now(1);
}
parent = talloc_parent(vp);
if (expected && (parent != expected)) {
- fprintf(stderr, "CONSISTENCY CHECK FAILED %s[%u]: Expected VALUE_PAIR \"%s\" to be parented "
- "by %p (%s), instead parented by %p (%s)\n",
- file, line, vp->da->name,
- expected, talloc_get_name(expected),
- parent, parent ? talloc_get_name(parent) : "NULL");
+ FR_FAULT_LOG("CONSISTENCY CHECK FAILED %s[%u]: Expected VALUE_PAIR \"%s\" to be parented "
+ "by %p (%s), instead parented by %p (%s)\n",
+ file, line, vp->da->name,
+ expected, talloc_get_name(expected),
+ parent, parent ? talloc_get_name(parent) : "NULL");
fr_log_talloc_report(expected);
if (parent) fr_log_talloc_report(parent);
}
}
#endif
+
+bool fr_assert_cond(char const *file, int line, char const *expr, bool cond)
+{
+ if (!cond) {
+ FR_FAULT_LOG("SOFT ASSERT FAILED %s[%u]: %s", file, line, expr);
+#if !defined(NDEBUG) && defined(SIGUSR1)
+ fr_fault(SIGUSR1);
+#endif
+ return false;
+ }
+
+ return cond;
+}
+
+/** Exit possibly printing a message about why we're exiting.
+ *
+ * Use the fr_exit(status) macro instead of calling this function
+ * directly.
+ *
+ * @param file where fr_exit() was called.
+ * @param line where fr_exit() was called.
+ * @param status we're exiting with.
+ */
+void NEVER_RETURNS _fr_exit(char const *file, int line, int status)
+{
+#ifndef NDEBUG
+ char const *error = fr_strerror();
+
+ if (error && (status != 0)) {
+ FR_FAULT_LOG("EXIT(%i) CALLED %s[%u]. Last error was: %s", status, file, line, error);
+ } else {
+ FR_FAULT_LOG("EXIT(%i) CALLED %s[%u]", status, file, line);
+ }
+#endif
+ fprintf(stderr, "If running under a debugger it should break <<here>>");
+ fflush(stderr);
+
+ fr_debug_break(); /* If running under GDB we'll break here */
+
+ exit(status);
+}
+
+/** Exit possibly printing a message about why we're exiting.
+ *
+ * Use the fr_exit_now(status) macro instead of calling this function
+ * directly.
+ *
+ * @param file where fr_exit_now() was called.
+ * @param line where fr_exit_now() was called.
+ * @param status we're exiting with.
+ */
+void NEVER_RETURNS _fr_exit_now(char const *file, int line, int status)
+{
+#ifndef NDEBUG
+ char const *error = fr_strerror();
+
+ if (error && (status != 0)) {
+ FR_FAULT_LOG("_EXIT(%i) CALLED %s[%u]. Last error was: %s", status, file, line, error);
+ } else {
+ FR_FAULT_LOG("_EXIT(%i) CALLED %s[%u]", status, file, line);
+ }
+#endif
+ fprintf(stderr, "If running under a debugger it should break <<here>>\n");
+ fflush(stderr);
+
+ fr_debug_break(); /* If running under GDB we'll break here */
+
+ _exit(status);
+}