Only register signal handlers if we have a PANIC_ACTION set
[freeradius.git] / src / lib / debug.c
index f261a49..f4c1ad4 100644 (file)
 #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;
@@ -120,28 +124,6 @@ void fr_debug_break(void)
 }
 
 #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.
@@ -151,23 +133,13 @@ void backtrace_print(fr_cbuff_t *cbuff, void *obj)
 {
        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);
                }
        };
 
@@ -176,6 +148,27 @@ void backtrace_print(fr_cbuff_t *cbuff, void *obj)
        }
 }
 
+/** 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.
@@ -183,7 +176,7 @@ void backtrace_print(fr_cbuff_t *cbuff, void *obj)
  * 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;
@@ -215,12 +208,7 @@ fr_bt_marker_t *fr_backtrace_attach(fr_cbuff_t **cbuff, TALLOC_CTX *obj)
        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);
        }
 
@@ -232,7 +220,12 @@ fr_bt_marker_t *fr_backtrace_attach(fr_cbuff_t **cbuff, TALLOC_CTX *obj)
        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;
 }
@@ -517,7 +510,7 @@ void fr_fault(int sig)
 
                /*
                 *      Here we temporarily enable the dumpable flag so if GBD or LLDB
-                *      is called in the panic_action, they can pattach tohe running
+                *      is called in the panic_action, they can pattach to the running
                 *      process.
                 */
                if (fr_get_dumpable_flag() == 0) {
@@ -599,20 +592,22 @@ int fr_log_talloc_report(TALLOC_CTX *ctx)
                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);
 
@@ -625,7 +620,7 @@ int fr_log_talloc_report(TALLOC_CTX *ctx)
  */
 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");
 }
@@ -681,24 +676,25 @@ int fr_fault_setup(char const *cmd, char const *program)
 
        /* 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
@@ -720,11 +716,6 @@ int fr_fault_setup(char const *cmd, char const *program)
                        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);
@@ -743,7 +734,7 @@ int fr_fault_setup(char const *cmd, char const *program)
                 *  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
 
@@ -818,6 +809,12 @@ void fr_fault_set_log_fd(int fd)
  */
 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) {
@@ -828,25 +825,25 @@ inline void fr_verify_vp(char const *file, int line, VALUE_PAIR const *vp)
                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);
                }
@@ -859,32 +856,32 @@ inline void fr_verify_vp(char const *file, int line, VALUE_PAIR const *vp)
                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);
                }
@@ -912,11 +909,11 @@ void fr_verify_list(char const *file, int line, TALLOC_CTX *expected, VALUE_PAIR
 
                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);
@@ -927,3 +924,72 @@ void fr_verify_list(char const *file, int line, TALLOC_CTX *expected, VALUE_PAIR
        }
 }
 #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);
+}