From 1d0eab02039c2f816ecf37c1c46e6281da594f49 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Sun, 24 Feb 2013 17:29:27 +0000 Subject: [PATCH] base: on Linux and some other architectures, dump a stack trace before crashing from an assertion failure. --- build/autotools/m4/lol-misc.m4 | 15 +++++++ configure.ac | 28 +++++++++++- src/Makefile.am | 2 +- src/base/assert.cpp | 82 ++++++++++++++++++++++++++++++++++ src/lol/base/assert.h | 3 ++ src/lol/base/map.h | 2 +- src/lolcore.vcxproj | 1 + src/lolcore.vcxproj.filters | 3 ++ 8 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 src/base/assert.cpp diff --git a/build/autotools/m4/lol-misc.m4 b/build/autotools/m4/lol-misc.m4 index 0beec842..8fce2a18 100644 --- a/build/autotools/m4/lol-misc.m4 +++ b/build/autotools/m4/lol-misc.m4 @@ -30,3 +30,18 @@ AC_DEFUN([LOL_TRY_CXXFLAGS], ifelse([$3],[],[:],[$3]) fi AC_LANG_POP(C++)]) + +dnl LOL_TRY_LDFLAGS (LDFLAGS, [ACTION-IF-WORKS], [ACTION-IF-FAILS]) +dnl check if $CC supports a given set of ldflags +AC_DEFUN([LOL_TRY_LDFLAGS], + [AC_MSG_CHECKING([if $CC supports $1 flags]) + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$1" + AC_TRY_LINK([],[],[ac_cv_try_ldflags_ok=yes],[ac_cv_try_ldflags_ok=no]) + LDFLAGS="$save_LDFLAGS" + AC_MSG_RESULT([$ac_cv_try_ldflags_ok]) + if test x"$ac_cv_try_ldflags_ok" = x"yes"; then + ifelse([$2],[],[:],[$2]) + else + ifelse([$3],[],[:],[$3]) + fi]) diff --git a/configure.ac b/configure.ac index 10f6611e..66d0c218 100644 --- a/configure.ac +++ b/configure.ac @@ -61,7 +61,6 @@ else fi -AM_CONDITIONAL(USE_GLEW, test "${ac_cv_my_have_glew}" != "no") dnl conditional builds AC_ARG_ENABLE(debug, [ --enable-debug build debug versions of the game (default no)]) @@ -72,16 +71,26 @@ AC_ARG_ENABLE(experimental, AC_ARG_ENABLE(doc, [ --enable-doc build documentation (needs doxygen and LaTeX)]) + +dnl Common C headers AC_CHECK_HEADERS(stdio.h stdarg.h inttypes.h endian.h stdint.h getopt.h) AC_CHECK_HEADERS(fastmath.h pthread.h libutil.h util.h pty.h glob.h unistd.h) +AC_CHECK_HEADERS(execinfo.h) AC_CHECK_HEADERS(sys/ioctl.h sys/ptrace.h sys/stat.h sys/syscall.h sys/user.h) AC_CHECK_HEADERS(sys/wait.h) AC_CHECK_HEADERS(linux/kdev_t.h linux/major.h) AC_CHECK_HEADERS(security/pam_appl.h security/pam_misc.h) AC_CHECK_HEADERS(pam/pam_appl.h pam/pam_misc.h) +dnl Common C++ headers +AC_LANG_PUSH(C++) +AC_CHECK_HEADERS(cxxabi.h) +AC_LANG_POP(C++) + +dnl Common C functions AC_CHECK_FUNCS(getcwd _getcwd) + if test "${enable_debug}" = "yes"; then AC_DEFINE(LOL_DEBUG, 1, Define to 1 to activate debug) OPT="-O" @@ -147,6 +156,9 @@ LOL_TRY_CXXFLAGS(-fno-rtti, [AM_CXXFLAGS="${AM_CXXFLAGS} -fno-rtti"]) dnl Optimizations AM_CXXFLAGS="${AM_CXXFLAGS} ${REL} ${OPT}" +dnl Debug symbols +LOL_TRY_LDFLAGS(-rdynamic, [AM_LDFLAGS="${AM_LDFLAGS} -rdynamic"]) + dnl Code qui fait des warnings == code de porc == deux baffes dans ta gueule LOL_TRY_CXXFLAGS(-Wall, [AM_CPPFLAGS="${AM_CPPFLAGS} -Wall"]) LOL_TRY_CXXFLAGS(-Wextra, [AM_CPPFLAGS="${AM_CPPFLAGS} -Wextra"]) @@ -169,13 +181,25 @@ LOL_TRY_CXXFLAGS(-Wparentheses, [AM_CPPFLAGS="${AM_CPPFLAGS} -Wparentheses"]) AC_CHECK_LIB(m, sin, MATH_LIBS="${MATH_LIBS} -lm") AC_CHECK_LIB(pthread, main, LIBS="$LIBS -lpthread") + +dnl Unix-specific libutil AC_CHECK_LIB(util, forkpty, [UTIL_LIBS="${UTIL_LIBS} -lutil" dnl Override future forkpty detection ac_cv_func_forkpty="yes"]) AC_CHECK_FUNCS(forkpty) -dnl Are we on the PS3? + +dnl GCC-specific symbol demangling +AC_LANG_PUSH(C++) +AC_TRY_LINK( + [#include ], + [abi::__cxa_demangle(NULL, 0, 0, NULL);], + [AC_DEFINE(HAVE_CXA_DEMANGLE, 1, Define to 1 if abi::__cxa_demangle is available)]) +AC_LANG_POP(C++) + + +dnl Are we on the PS3? ac_cv_my_have_ps3="no" AC_CHECK_LIB(sysmodule_stub, cellSysmoduleLoadModule, [ac_cv_my_have_ps3="yes" diff --git a/src/Makefile.am b/src/Makefile.am index f348d140..68a6f085 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -67,7 +67,7 @@ liblolcore_sources = \ generated/easymesh-parser.cpp generated/easymesh-parser.h \ generated/easymesh-scanner.cpp \ \ - base/hash.cpp base/log.cpp base/string.cpp \ + base/assert.cpp base/hash.cpp base/log.cpp base/string.cpp \ \ math/vector.cpp math/real.cpp math/half.cpp math/trig.cpp \ math/geometry.cpp \ diff --git a/src/base/assert.cpp b/src/base/assert.cpp new file mode 100644 index 00000000..f31c8250 --- /dev/null +++ b/src/base/assert.cpp @@ -0,0 +1,82 @@ +// +// Lol Engine +// +// Copyright: (c) 2010-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined HAVE_CXXABI_H +# include +#endif +#if defined HAVE_EXECINFO_H +# include +#endif + +#include "core.h" + +namespace lol +{ + +void DumpStack() +{ +#if defined HAVE_CXA_DEMANGLE + /* Get current stack frames */ + void *stack_ptrs[50]; + size_t size = backtrace(stack_ptrs, 50); + char **callstack = backtrace_symbols(stack_ptrs, size); + + if (size > 1) + Log::Debug("%d functions in stack trace:\n", (int)size - 1); + + /* Parse stack frames, skipping the first element (because + * that’s ourselves) and print information. */ + for (size_t i = 1; i < size; ++i) + { + char *name = 0, *offset = 0, *address = 0; + + for (char *p = callstack[i]; *p; ++p) + { + if (*p == '(') + name = p; + else if (*p == '+') + offset = p; + else if (*p == ')') + { + address = p; + break; + } + } + + if (name && offset && address && name < offset) + { + *name++ = *offset++ = *address++ = '\0'; + + int ret; + char *realname = abi::__cxa_demangle(name, 0, 0, &ret); + + if (ret == 0) + name = realname; + + Log::Debug("#%d %s: %s+%s %s\n", (int)i, + callstack[i], name, offset, address); + free(realname); + } + else + { + Log::Debug("#%d %s\n", (int)i, callstack[i]); + } + } + + free(callstack); +#endif +} + +} /* namespace lol */ + diff --git a/src/lol/base/assert.h b/src/lol/base/assert.h index 7d84e63e..dd641d36 100644 --- a/src/lol/base/assert.h +++ b/src/lol/base/assert.h @@ -34,6 +34,8 @@ static inline void DebugBreak() #endif } +extern void DumpStack(); + #define LOL_CALL(macro, args) macro args #define LOL_EVAL(a) a #define LOL_1ST(a, ...) a @@ -115,6 +117,7 @@ static inline void DebugBreak() LOL_CALL(LOL_CAT(LOL_ERROR_, LOL_CALL(LOL_COUNT_TO_3, \ (__VA_ARGS__))), \ (__VA_ARGS__)); \ + DumpStack(); \ DebugBreak(); \ Abort(); \ } diff --git a/src/lol/base/map.h b/src/lol/base/map.h index 233b3574..b6170598 100644 --- a/src/lol/base/map.h +++ b/src/lol/base/map.h @@ -35,7 +35,7 @@ public: if (m_array[i].m2 == key) return m_array[i].m3; /* XXX: this in an error! */ - ASSERT(0, "trying to read a non-existent key in map"); + ASSERT(0, "trying to read a nonexistent key in map"); return V(); } diff --git a/src/lolcore.vcxproj b/src/lolcore.vcxproj index ace9e153..522a04c8 100644 --- a/src/lolcore.vcxproj +++ b/src/lolcore.vcxproj @@ -90,6 +90,7 @@ + diff --git a/src/lolcore.vcxproj.filters b/src/lolcore.vcxproj.filters index 2f1a48c4..d27e774c 100644 --- a/src/lolcore.vcxproj.filters +++ b/src/lolcore.vcxproj.filters @@ -270,6 +270,9 @@ gpu + + base + base