summaryrefslogtreecommitdiff
path: root/src/common/nullpo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/nullpo.c')
-rw-r--r--src/common/nullpo.c133
1 files changed, 120 insertions, 13 deletions
diff --git a/src/common/nullpo.c b/src/common/nullpo.c
index dfb938708..11741745c 100644
--- a/src/common/nullpo.c
+++ b/src/common/nullpo.c
@@ -23,18 +23,106 @@
#include "nullpo.h"
#include "common/showmsg.h"
+#include "common/strlib.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
-#ifdef HAVE_EXECINFO
+#if defined(HAVE_LIBBACKTRACE)
+#include "libbacktrace/backtrace.h"
+#include "libbacktrace/backtrace-supported.h"
+# if defined(WIN32)
+# include <windows.h>
+# elif defined(__sun)
+# include <limits.h>
+# elif defined(__linux) || defined(__linux__)
+# include <unistd.h>
+# include <limits.h>
+# elif defined(__APPLE__) && defined(__MACH__)
+# include <mach-o/dyld.h>
+# elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__)
+# include <sys/types.h>
+# include <sys/sysctl.h>
+# endif
+#elif defined(HAVE_EXECINFO)
#include <execinfo.h>
-#endif // HAVE_EXECINFO
+#endif // HAVE_LIBBACKTRACE
+
static struct nullpo_interface nullpo_s;
struct nullpo_interface *nullpo;
+#ifdef HAVE_LIBBACKTRACE
+static char executable_path[PATH_MAX];
+
+static void nullpo_error_callback(void *data, const char *msg, int errnum)
+{
+ ShowError("Error: %s (%d)", msg, errnum);
+}
+
+static int nullpo_print_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function)
+{
+ ShowError("0x%lx %s\n",
+ (unsigned long) pc,
+ function == NULL ? "???" : function);
+ ShowError("\t%s:%d\n",
+ filename == NULL ? "???" : filename,
+ lineno);
+ return 0;
+}
+
+static void nullpo_backtrace_print(struct backtrace_state *state)
+{
+ backtrace_full(state, 0, nullpo_print_callback, nullpo_error_callback, state);
+}
+
+static bool nullpo_backtrace_get_executable_path(char *buf, size_t length)
+{
+#if defined(WIN32)
+ char *exe_path = NULL;
+ if (_get_pgmptr(&exe_path) != 0)
+ return false;
+ safestrncpy(buf, exe_path, length);
+ return true;
+#elif defined(__sun)
+ if (length < MAX_PATH)
+ return false;
+ if (realpath(getexecname(), buf) == NULL)
+ return false;
+ buf[length - 1] = '\0';
+ return true;
+#elif defined(__linux) || defined(__linux__)
+ ssize_t len = readlink("/proc/self/exe", buf, length);
+ if (len <= 0 || len == length)
+ return false;
+ buf[len] = '\0';
+ return true;
+#elif defined(__APPLE__) && defined(__MACH__)
+ uint32_t len = (uint32_t)length;
+ if (_NSGetExecutablePath(buf, &len) != 0)
+ return false; // buffer too small (!)
+ // resolve symlinks, ., .. if possible
+ char *canonical_path = realpath(buf, NULL);
+ if (canonical_path != NULL) {
+ safestrncpy(buf, canonical_path, length);
+ free(canonical_path);
+ }
+ return true;
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__)
+ int mib[4];
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+ if (sysctl(mib, 4, buf, &length, NULL, 0) != 0)
+ return false;
+ return true;
+#endif
+ return false;
+}
+#endif // HAVE_LIBBACKTRACE
+
/**
* Reports failed assertions or NULL pointers
*
@@ -46,12 +134,6 @@ struct nullpo_interface *nullpo;
*/
static void assert_report(const char *file, int line, const char *func, const char *targetname, const char *title)
{
-#ifdef HAVE_EXECINFO
- void *array[10];
- int size;
- char **strings;
- int i;
-#endif // HAVE_EXECINFO
if (file == NULL)
file = "??";
@@ -60,21 +142,46 @@ static void assert_report(const char *file, int line, const char *func, const ch
ShowError("--- %s --------------------------------------------\n", title);
ShowError("%s:%d: '%s' in function `%s'\n", file, line, targetname, func);
-#ifdef HAVE_EXECINFO
- size = (int)backtrace(array, 10);
- strings = backtrace_symbols(array, size);
- for (i = 0; i < size; i++)
+#ifdef HAVE_LIBBACKTRACE
+ if (nullpo->backtrace_state != NULL)
+ nullpo_backtrace_print(nullpo->backtrace_state);
+#elif defined(HAVE_EXECINFO)
+ void *array[10];
+ int size = (int)backtrace(array, 10);
+ char **strings = backtrace_symbols(array, size);
+ for (int i = 0; i < size; i++)
ShowError("%s\n", strings[i]);
free(strings);
-#endif // HAVE_EXECINFO
+#endif // HAVE_LIBBACKTRACE
ShowError("--- end %s ----------------------------------------\n", title);
}
+static void nullpo_init(void)
+{
+#ifdef HAVE_LIBBACKTRACE
+ if (!nullpo_backtrace_get_executable_path(executable_path, sizeof executable_path)) {
+ safestrncpy(executable_path, "hercules", sizeof executable_path);
+ }
+ nullpo->backtrace_state = backtrace_create_state(executable_path, BACKTRACE_SUPPORTS_THREADS, nullpo_error_callback, NULL);
+#endif
+}
+
+static void nullpo_final(void)
+{
+ // FIXME: libbacktrace doesn't provide a backtrace_free_state, and it's unsafe to pass the state to
+ // backtrace_free (the function itself uses the state internally). For the time being, we'll leave the state
+ // allocated until program termination as shown in their examples.
+}
+
/**
*
**/
void nullpo_defaults(void)
{
nullpo = &nullpo_s;
+ nullpo->init = nullpo_init;
+ nullpo->final = nullpo_final;
nullpo->assert_report = assert_report;
+
+ nullpo->backtrace_state = NULL;
}