From 2a69789a246a80053996cc7bc083d68b64fa33dc Mon Sep 17 00:00:00 2001
From: Allanis <allanis@saracraft.net>
Date: Tue, 26 Nov 2013 18:23:50 +0000
Subject: [PATCH] [Add] Automagically give backtraces in DEBUG mode.

---
 bin/Makefile   | 38 +++++++++++++-------
 src/lephisto.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/lluadef.h  |  6 ++--
 src/log.h      |  4 +--
 4 files changed, 125 insertions(+), 20 deletions(-)

diff --git a/bin/Makefile b/bin/Makefile
index 1a083a4..762b283 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -26,19 +26,6 @@ CXML = $(shell xml2-config --cflags)
 CTTF = $(shell freetype-config --cflags)
 CGL  =
 CFLAGS = $(CLUA) $(CSDL) $(CXML) $(CTTF) $(CGL) $(VERSION) -D$(OS)
-ifeq ($(OS),LINUX)
-	CFLAGS += -D_POSIX_SOURCE
-endif
-ifdef DEBUG
-CFLAGS += -W -Wall -Wextra -Wunused -Wshadow -Wpointer-arith -Wmissing-prototypes \
-					-Winline -Wcast-align -Wmissing-declarations -fstack-protector \
-					-fstack-protector-all -g3 -DDEBUG -DLUA_USE_APICHECK -std=c99
-ifdef DEBUG_PARANOID
-	CFLAGS += -DDEBUG_PARANOID
-endif
-else
-CFLAGS += -O2 -funroll-loops -pipe -std=c99
-endif
 
 # LDFLAGS.
 LDLUA = ../lib/lua/liblua.a
@@ -49,6 +36,31 @@ LDGL	= -lGL
 LDPNG   = -lpng
 LDFLAGS = -lm $(LDLUA) $(LDSDL) $(LDXML) $(LDTTF) $(LDGL) $(LDPNG)
 
+# OS Stuff.
+ifeq ($(OS),LINUX)
+	CFLAGS += -D_POSIX_SOURCE
+endif
+
+# Debug stuff.
+ifdef DEBUG
+CFLAGS += -W -Wall -Wextra -Wunused -Wshadow -Wpointer-arith -Wmissing-prototypes \
+					-Winline -Wcast-align -Wmissing-declarations -fstack-protector \
+					-fstack-protector-all -g3 -DDEBUG -DLUA_USE_APICHECK -std=c99
+
+ifdef DEBUG_PARANOID
+	CFLAGS += -DDEBUG_PARANOID
+endif
+
+# Handle OS Debug stuff here.
+ifeq ($(OS), LINUX)
+	LDFLAGS += -rdynamic
+endif # Linux.
+
+else # DEBUG
+CFLAGS += -O2 -funroll-loops -pipe -std=c99
+endif
+
+
 # This is just for gstat to run some analysis on performance.
 ifdef DEBUG
 LDFLAGS += -pg
diff --git a/src/lephisto.c b/src/lephisto.c
index a58613a..0f988a8 100644
--- a/src/lephisto.c
+++ b/src/lephisto.c
@@ -10,8 +10,17 @@
  */
 
 #include <SDL/SDL.h>
-#include <string.h>
 
+/* Global. */
+#include <string.h>
+#if defined(LINUX) && !defined(NODEBUG)
+#include <signal.h>
+#include <execinfo.h>
+#include <stdlib.h>
+#include <unistd.h>
+#endif /* defined(LINUX) && !defined(NODEBUG) */
+
+/* Local. */
 #include "lephisto.h"
 #include "conf.h"
 #include "log.h"
@@ -74,15 +83,17 @@ static void print_SDLversion(void);
 static void load_screen(void);
 static void load_all(void);
 static void unload_all(void);
-void main_loop(void);
 static void display_fps(const double dt);
 static void window_caption(void);
 static void data_name(void);
+static void debug_sigInit(void);
 /* Update. */
 static void fps_control(void);
 static void update_all(void);
 static void update_routine(double dt);
 static void render_all(void);
+/* Misc. */
+void main_loop(void); /* dialogue.c */
 
 
 /**
@@ -112,6 +123,9 @@ int main(int argc, char** argv) {
   /* Initialize SDL for possible warnings. */
   SDL_Init(0);
 
+  /* Set up debug signal handlers. */
+  debug_sigInit();
+
   /* Create the home directory if needed. */
   if(lfile_dirMakeExist("."))
     WARN("Unable to create lephisto directory '%s'", lfile_basePath());
@@ -591,3 +605,82 @@ static void print_SDLversion(void) {
     WARN("SDL is older than compiled version.");
 }
 
+#if defined(LINUX) && !defined(NODEBUG)
+/**
+ * @brief Get the string related to the signal code.
+ *    @param sig Signal to which code belongs.
+ *    @param sig_code Signal code to get string of.
+ *    @return String of signal code.
+ */
+static const char* debug_sigCodeToStr(int sig, int sig_code) {
+  if(sig == SIGFPE)
+    switch(sig_code) {
+      case FPE_INTDIV: return "SIGFPE (integer divide by zero)";
+      case FPE_INTOVF: return "SIGFPE (integer overflow)";
+      case FPE_FLTDIV: return "SIGFPE (floating-point divide by zero)";
+      case FPE_FLTOVF: return "SIGFPE (floating-point overflow)";
+      case FPE_FLTUND: return "SIGFPE (floating-point underflow)";
+      case FPE_FLTRES: return "SIGFPE (floating-point inexact result)";
+      case FPE_FLTINV: return "SIGFPE (floating-point invalid operation)";
+      case FPE_FLTSUB: return "SIGFPE (subscript out of range)";
+      default: return "SIGFPE";
+    }
+  else if(sig == SIGSEGV)
+    switch(sig_code) {
+      case SEGV_MAPERR: return "SIGEGV (address not mapped to object)";
+      case SEGV_ACCERR: return "SIGEGV (invalid permissions for mapped object)";
+      default: return "SIGSEGV";
+    }
+  /* No suitable code found. */
+  return strsignal(sig);
+}
+
+/**
+ * @brief Backtrace signal handler for linux.
+ *    @param sig Signal.
+ *    @param info Signal information.
+ *    @param unused Unused.
+ */
+static void debug_sigHandler(int sig, siginfo_t* info, void* unused) {
+  (void) sig;
+  (void) unused;
+  int i, num;
+  void* buf[64];
+  char** symbols;
+
+  num = backtrace(buf, 64);
+  symbols = backtrace_symbols(buf, num);
+
+  DEBUG("LEPHISTO recieved %s!",
+      debug_sigCodeToStr(info->si_signo, info->si_code));
+  for(i = 0; i < num; i++)
+    DEBUG("   %s", symbols[i]);
+  DEBUG("Report this to project maintainer with the backtrace.");
+
+  exit(1);
+}
+#endif /* defined(LINUX) && !defined(DEBUG) */
+
+/**
+ * @brief Set up the SignalHandler for Linux.
+ */
+static void debug_sigInit(void) {
+#if defined(LINUX) && !defined(NODEBUG)
+  struct sigaction sa, so;
+
+  /* Set up handler. */
+  sa.sa_handler   = NULL;
+  sa.sa_sigaction = debug_sigHandler;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags     = SA_SIGINFO;
+
+  /* Attach signals. */
+  sigaction(SIGSEGV, &sa, &so);
+  if(so.sa_handler == SIG_IGN)
+    DEBUG("Unable to set up SIGSEGV signal handler.");
+  sigaction(SIGFPE, &sa, &so);
+  if(so.sa_handler == SIG_IGN)
+    DEBUG("Unable to set up SIGFPE signal handler.");
+#endif /* #if defined(LINUX) && !defined(NODEBUG) */
+}
+
diff --git a/src/lluadef.h b/src/lluadef.h
index e2cb88d..b3d2639 100644
--- a/src/lluadef.h
+++ b/src/lluadef.h
@@ -5,7 +5,7 @@
 #include "lualib.h"
 
 /* Debug stuff. */
-#ifdef DEBUG
+#ifdef NODEBUG
 #define LLUA_DEBUG(str, args...) \
   (fprintf(stdout, "Lua: "str"\n", ## args))
 
@@ -19,11 +19,11 @@
     LLUA_DEBUG("[%s] Too few arguments (%s:%d)", __func__, __FILE__, __LINE__); \
     return 0; \
   }
-#else /* DEBUG. */
+#else /* NODEBUG. */
 #define LLUA_DEBUG(str, args...)  do {;} while(0)
 #define LLUA_MIN_ARGS(n)          do {;} while(0)
 #define LLUA_INVALID_PARAMETER()  do {;} while(0)
-#endif /* DEBUG. */
+#endif /* NODEBUG. */
 
 /* Comfortability macros. */
 #define luaL_dobuffer(L, b, n, s) \
diff --git a/src/log.h b/src/log.h
index 2fc6801..6da5a36 100644
--- a/src/log.h
+++ b/src/log.h
@@ -9,9 +9,9 @@
 
 #define LOG(str, args...)(fprintf(stdout, str"\n", ## args))
 #ifdef DEBUG_PARANOID /* Will cause WARN's to blow up. */
-#define WARN(str, args...)(fprintf(stderr, "Warning: "str"\n", ## args), assert(0))
+#define WARN(str, args...)(fprintf(stderr, "Warning: [%s] "str"\n", __func__, ## args), assert(0))
 #else
-#define WARN(str, args...)(fprintf(stderr, "Warning: "str"\n", ## args))
+#define WARN(str, args...)(fprintf(stderr, "Warning: [%s] "str"\n", __func__, ## args))
 #endif
 
 #define ERR(str, args...) (fprintf(stderr, "ERROR %s:%d: [%s] "str"\n", \