From abbc960e9631e9ef6d17c8906b987e69e36f9dea Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 10 Jun 2016 19:29:43 +0100 Subject: [PATCH 1/5] Linux crashlog: Use sigaction to get more info on fatal signals. --- config.lib | 28 ++++++++++++++++++++ src/os/unix/crashlog_unix.cpp | 50 ++++++++++++++++++++++++++++++++--- src/saveload/afterload.cpp | 42 +++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/config.lib b/config.lib index 4ff37a8e20..feee569613 100644 --- a/config.lib +++ b/config.lib @@ -1689,6 +1689,34 @@ EOL CFLAGS="$CFLAGS -g1" fi fi + + log 2 "executing $cc_host $CFLAGS $LDFLAGS $STATIC_FLAGS -o tmp.config.sigaction -x c++ - -ldl" + "$cc_host" $CFLAGS $LDFLAGS $STATIC_FLAGS -o tmp.config.sigaction -x c++ - -ldl 2> /dev/null << EOL + #include + void *addr; + int code; + void handler(int sig, siginfo_t *si, void *context) { + addr = si->si_addr; + code = si->si_code; + } + int main() { + struct sigaction sa; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = handler; + sigaction(SIGSEGV, &sa, 0); + return 0; + } +EOL + ret=$? + rm -f tmp.config.sigaction + log 2 " exit code $ret" + if [ $ret -ne 0 ]; then + log 1 "checking sigaction... no" + else + log 1 "checking sigaction... found" + CFLAGS="$CFLAGS -DWITH_SIGACTION" + fi fi if [ "$os" = "MINGW" ]; then diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index d8f95908b4..5ab8e2a10c 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -78,6 +78,9 @@ static void LogStacktraceSigSegvHandler(int sig) class CrashLogUnix : public CrashLog { /** Signal that has been thrown. */ int signum; +#ifdef WITH_SIGACTION + siginfo_t *si; +#endif /* virtual */ char *LogOSVersion(char *buffer, const char *last) const { @@ -101,14 +104,29 @@ class CrashLogUnix : public CrashLog { /* virtual */ char *LogError(char *buffer, const char *last, const char *message) const { - return buffer + seprintf(buffer, last, + buffer += seprintf(buffer, last, "Crash reason:\n" - " Signal: %s (%d)\n" - " Message: %s\n\n", + " Signal: %s (%d)\n", strsignal(this->signum), - this->signum, + this->signum); +#ifdef WITH_SIGACTION + if (this->si) { + buffer += seprintf(buffer, last, + " si_code: %d\n", + this->si->si_code); + if (this->signum != SIGABRT) { + buffer += seprintf(buffer, last, + " fault address: %p\n", + this->si->si_addr); + } + } +#endif + buffer += seprintf(buffer, last, + " Message: %s\n\n", message == NULL ? "" : message ); + + return buffer; } #if defined(SUNOS) @@ -378,10 +396,17 @@ public: * A crash log is always generated by signal. * @param signum the signal that was caused by the crash. */ +#ifdef WITH_SIGACTION + CrashLogUnix(int signum, siginfo_t *si) : + signum(signum), si(si) + { + } +#else CrashLogUnix(int signum) : signum(signum) { } +#endif }; /** The signals we want our crash handler to handle. */ @@ -392,7 +417,11 @@ static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGI * @note Not static so it shows up in the backtrace. * @param signum the signal that caused us to crash. */ +#ifdef WITH_SIGACTION +static void CDECL HandleCrash(int signum, siginfo_t *si, void *context) +#else static void CDECL HandleCrash(int signum) +#endif { /* Disable all handling of signals by us, so we don't go into infinite loops. */ for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) { @@ -412,7 +441,11 @@ static void CDECL HandleCrash(int signum) abort(); } +#ifdef WITH_SIGACTION + CrashLogUnix log(signum, si); +#else CrashLogUnix log(signum); +#endif log.MakeCrashLog(); CrashLog::AfterCrashLogCleanup(); @@ -422,6 +455,15 @@ static void CDECL HandleCrash(int signum) /* static */ void CrashLog::InitialiseCrashLog() { for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) { +#ifdef WITH_SIGACTION + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_RESTART; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = HandleCrash; + sigaction(*i, &sa, NULL); +#else signal(*i, HandleCrash); +#endif } } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 9690481154..972a5edd82 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -302,12 +302,20 @@ static void InitializeWindowsAndCaches() BuildOwnerLegend(); } +#ifdef WITH_SIGACTION +static struct sigaction _prev_segfault; +static struct sigaction _prev_abort; +static struct sigaction _prev_fpe; + +static void CDECL HandleSavegameLoadCrash(int signum, siginfo_t *si, void *context); +#else typedef void (CDECL *SignalHandlerPointer)(int); static SignalHandlerPointer _prev_segfault = NULL; static SignalHandlerPointer _prev_abort = NULL; static SignalHandlerPointer _prev_fpe = NULL; static void CDECL HandleSavegameLoadCrash(int signum); +#endif /** * Replaces signal handlers of SIGSEGV and SIGABRT @@ -315,9 +323,20 @@ static void CDECL HandleSavegameLoadCrash(int signum); */ static void SetSignalHandlers() { +#ifdef WITH_SIGACTION + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_SIGINFO | SA_RESTART; + sigemptyset(&sa.sa_mask); + sa.sa_sigaction = HandleSavegameLoadCrash; + sigaction(SIGSEGV, &sa, &_prev_segfault); + sigaction(SIGABRT, &sa, &_prev_abort); + sigaction(SIGFPE, &sa, &_prev_fpe); +#else _prev_segfault = signal(SIGSEGV, HandleSavegameLoadCrash); _prev_abort = signal(SIGABRT, HandleSavegameLoadCrash); _prev_fpe = signal(SIGFPE, HandleSavegameLoadCrash); +#endif } /** @@ -325,9 +344,15 @@ static void SetSignalHandlers() */ static void ResetSignalHandlers() { +#ifdef WITH_SIGACTION + sigaction(SIGSEGV, &_prev_segfault, NULL); + sigaction(SIGABRT, &_prev_abort, NULL); + sigaction(SIGFPE, &_prev_fpe, NULL); +#else signal(SIGSEGV, _prev_segfault); signal(SIGABRT, _prev_abort); signal(SIGFPE, _prev_fpe); +#endif } /** @@ -367,7 +392,11 @@ bool SaveloadCrashWithMissingNewGRFs() * NewGRFs that are required by the savegame. * @param signum received signal */ +#ifdef WITH_SIGACTION +static void CDECL HandleSavegameLoadCrash(int signum, siginfo_t *si, void *context) +#else static void CDECL HandleSavegameLoadCrash(int signum) +#endif { ResetSignalHandlers(); @@ -414,14 +443,27 @@ static void CDECL HandleSavegameLoadCrash(int signum) ShowInfo(buffer); +#ifdef WITH_SIGACTION + struct sigaction call; +#else SignalHandlerPointer call = NULL; +#endif switch (signum) { case SIGSEGV: call = _prev_segfault; break; case SIGABRT: call = _prev_abort; break; case SIGFPE: call = _prev_fpe; break; default: NOT_REACHED(); } +#ifdef WITH_SIGACTION + if (call.sa_flags & SA_SIGINFO) { + if (call.sa_sigaction != NULL) call.sa_sigaction(signum, si, context); + } else { + if (call.sa_handler != NULL) call.sa_handler(signum); + } +#else if (call != NULL) call(signum); +#endif + } /** From 411f91731d5d0740fb7de24483fe73a4d5729b30 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 10 Jun 2016 19:55:28 +0100 Subject: [PATCH 2/5] Config: Fix --with-self-gdb-debug config re-exec --- config.lib | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config.lib b/config.lib index feee569613..4974ef3470 100644 --- a/config.lib +++ b/config.lib @@ -97,7 +97,7 @@ set_default() { with_sse="1" with_libbfd="1" with_bfd_extra_debug="1" - with_dbg_gdb="1" + with_self_gdb_debug="1" save_params_array=" build @@ -177,7 +177,7 @@ set_default() { with_sse with_libbfd with_bfd_extra_debug - with_dbg_gdb + with_self_gdb_debug CC CXX CFLAGS CXXFLAGS LDFLAGS CFLAGS_BUILD CXXFLAGS_BUILD LDFLAGS_BUILD" } @@ -479,9 +479,9 @@ detect_params() { --with-bfd-extra-debug) with_bfd_extra_debug="1";; --with-bfd-extra-debug=*) with_bfd_extra_debug="$optarg";; - --without-self-gdb-debug) with_dbg_gdb="0";; - --with-self-gdb-debug) with_dbg_gdb="1";; - --with-self-gdb-debug=*) with_dbg_gdb="$optarg";; + --without-self-gdb-debug) with_self_gdb_debug="0";; + --with-self-gdb-debug) with_self_gdb_debug="1";; + --with-self-gdb-debug=*) with_self_gdb_debug="$optarg";; CC=* | --CC=*) CC="$optarg";; CXX=* | --CXX=*) CXX="$optarg";; @@ -1639,7 +1639,7 @@ EOL fi HAVE_GDB_DBG= - if [ "$with_dbg_gdb" = "1" ]; then + if [ "$with_self_gdb_debug" = "1" ]; then log 2 "executing $cc_host $CFLAGS $LDFLAGS $STATIC_FLAGS -o tmp.config.dbggdb -x c++ -" "$cc_host" $CFLAGS $LDFLAGS $STATIC_FLAGS -o tmp.config.dbggdb -x c++ - 2> /dev/null << EOL #include From 0951cb320c36361a3f49ac86a7309d98d08b0c86 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 10 Jun 2016 19:57:51 +0100 Subject: [PATCH 3/5] Show ./configure invocation in crash log. --- Makefile.src.in | 8 ++++++-- config.lib | 8 ++++++++ projects/determineversion.vbs | 1 + src/crashlog.cpp | 6 ++++-- src/rev.cpp.in | 6 ++++++ src/rev.h | 1 + 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Makefile.src.in b/Makefile.src.in index 1d654d283f..fcdd9ecc8c 100644 --- a/Makefile.src.in +++ b/Makefile.src.in @@ -38,11 +38,13 @@ MAKEDEPEND = !!MAKEDEPEND!! CFLAGS_MAKEDEP = !!CFLAGS_MAKEDEP!! SORT = !!SORT!! AWK = !!AWK!! +CONFIGURE_INVOCATION = !!CONFIGURE_INVOCATION!! CONFIG_CACHE_COMPILER = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_COMPILER!! CONFIG_CACHE_LINKER = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_LINKER!! CONFIG_CACHE_ENDIAN = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_ENDIAN!! CONFIG_CACHE_SOURCE = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_SOURCE!! CONFIG_CACHE_VERSION = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_VERSION!! +CONFIG_CACHE_INVOCATION = $(SRC_OBJS_DIR)/!!CONFIG_CACHE_INVOCATION!! OBJS_C := !!OBJS_C!! OBJS_CPP := !!OBJS_CPP!! @@ -114,6 +116,8 @@ RES := $(shell if [ "`cat $(CONFIG_CACHE_ENDIAN) 2>/dev/null`" != "$(ENDIAN_FORC RES := $(shell if [ "`cat $(CONFIG_CACHE_SOURCE) 2>/dev/null`" != "$(SRCS)" ]; then echo "$(SRCS)" > $(CONFIG_CACHE_SOURCE); fi ) # If there is a change in the revision, make sure we recompile rev.cpp RES := $(shell if [ "`cat $(CONFIG_CACHE_VERSION) 2>/dev/null`" != "$(REV) $(MODIFIED)" ]; then echo "$(REV) $(MODIFIED)" > $(CONFIG_CACHE_VERSION); fi ) +# If there is a change in the configure invocation, make sure we recompile rev.cpp +RES := $(shell if [ "`cat $(CONFIG_CACHE_INVOCATION) 2>/dev/null`" != "$(CONFIGURE_INVOCATION)" ]; then echo "$(CONFIGURE_INVOCATION)" > $(CONFIG_CACHE_INVOCATION); fi ) ifndef MAKEDEPEND # The slow, but always correct, dep-check @@ -292,8 +296,8 @@ $(ENDIAN_CHECK): $(SRC_DIR)/endian_check.cpp # Revision files -$(SRC_DIR)/rev.cpp: $(CONFIG_CACHE_VERSION) $(SRC_DIR)/rev.cpp.in - $(Q)cat $(SRC_DIR)/rev.cpp.in | sed "s@\!\!REVISION\!\!@$(REV_NR)@g;s@!!VERSION!!@$(REV)@g;s@!!MODIFIED!!@$(MODIFIED)@g;s@!!DATE!!@`date +%d.%m.%y`@g" > $(SRC_DIR)/rev.cpp +$(SRC_DIR)/rev.cpp: $(CONFIG_CACHE_VERSION) $(CONFIG_CACHE_INVOCATION) $(SRC_DIR)/rev.cpp.in + $(Q)cat $(SRC_DIR)/rev.cpp.in | sed "s@\!\!REVISION\!\!@$(REV_NR)@g;s@!!VERSION!!@$(REV)@g;s@!!MODIFIED!!@$(MODIFIED)@g;s@!!DATE!!@`date +%d.%m.%y`@g;s@\!\!CONFIGURE_INVOCATION\!\!@$(CONFIGURE_INVOCATION)@g;" > $(SRC_DIR)/rev.cpp $(SRC_DIR)/os/windows/ottdres.rc: $(CONFIG_CACHE_VERSION) $(SRC_DIR)/os/windows/ottdres.rc.in $(Q)cat $(SRC_DIR)/os/windows/ottdres.rc.in | sed "s@\!\!REVISION\!\!@$(REV_NR)@g;s@!!VERSION!!@$(REV)@g;s@!!DATE!!@`date +%d.%m.%y`@g" > $(SRC_DIR)/os/windows/ottdres.rc diff --git a/config.lib b/config.lib index 4974ef3470..2fee81b3e6 100644 --- a/config.lib +++ b/config.lib @@ -513,6 +513,12 @@ detect_params() { # Clean the logfile echo "" > $config_log log 2 "Invocation: $0 $*" + if [ "$ignore_extra_parameters" = "0" -o ! -f config.invocation ]; then + echo "$0 $*" > config.invocation + CONFIGURE_INVOCATION="$0 $*" + else + CONFIGURE_INVOCATION="`cat config.invocation`" + fi } save_params() { @@ -3440,6 +3446,7 @@ make_sed() { s@!!CONFIG_CACHE_VERSION!!@config.cache.version@g; s@!!CONFIG_CACHE_SOURCE_LIST!!@config.cache.source.list@g; s@!!CONFIG_CACHE_PWD!!@config.cache.pwd@g; + s@!!CONFIG_CACHE_INVOCATION!!@config.cache.invocation@g; s@!!LANG_SUPPRESS!!@$lang_suppress@g; s@!!OBJS_C!!@$OBJS_C@g; s@!!OBJS_CPP!!@$OBJS_CPP@g; @@ -3452,6 +3459,7 @@ make_sed() { s@!!DISTCC!!@$distcc@g; s@!!NFORENUM!!@$nforenum@g; s@!!GRFCODEC!!@$grfcodec@g; + s@!!CONFIGURE_INVOCATION!!@$CONFIGURE_INVOCATION@g; " if [ "$icon_theme_dir" != "" ]; then diff --git a/projects/determineversion.vbs b/projects/determineversion.vbs index e738569e3a..82ccde5f2d 100755 --- a/projects/determineversion.vbs +++ b/projects/determineversion.vbs @@ -27,6 +27,7 @@ Sub UpdateFile(modified, revision, version, cur_date, filename) FindReplaceInFile filename, "!!REVISION!!", revision FindReplaceInFile filename, "!!VERSION!!", version FindReplaceInFile filename, "!!DATE!!", cur_date + FindReplaceInFile filename, "!!CONFIGURE_INVOCATION!!", "" End Sub Sub UpdateFiles(version) diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 05ae410b67..7749bef3d4 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -103,7 +103,8 @@ char *CrashLog::LogOpenTTDVersion(char *buffer, const char *last) const " Bits: %d\n" " Endian: %s\n" " Dedicated: %s\n" - " Build date: %s\n\n", + " Build date: %s\n" + " Configure: %s\n\n", _openttd_revision, _openttd_revision_modified, _openttd_newgrf_version, @@ -122,7 +123,8 @@ char *CrashLog::LogOpenTTDVersion(char *buffer, const char *last) const #else "no", #endif - _openttd_build_date + _openttd_build_date, + _openttd_build_configure ); } diff --git a/src/rev.cpp.in b/src/rev.cpp.in index 7e27859ce0..fb5b6d8c73 100644 --- a/src/rev.cpp.in +++ b/src/rev.cpp.in @@ -49,6 +49,12 @@ const char _openttd_revision[] = "!!VERSION!!"; */ const char _openttd_build_date[] = __DATE__ " " __TIME__; + +/** + * The configure invocation used to build OpenTTD + */ +const char _openttd_build_configure[] = "!!CONFIGURE_INVOCATION!!"; + /** * Let us know if current build was modified. This detection * works even in the case when revision string is overridden by diff --git a/src/rev.h b/src/rev.h index d31dbb51cc..68e19a3632 100644 --- a/src/rev.h +++ b/src/rev.h @@ -14,6 +14,7 @@ extern const char _openttd_revision[]; extern const char _openttd_build_date[]; +extern const char _openttd_build_configure[]; extern const byte _openttd_revision_modified; extern const uint32 _openttd_newgrf_version; From 8d078acb4eb7d2523ff909d1e300337a9b1b09ce Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 12 Jun 2016 19:34:06 +0100 Subject: [PATCH 4/5] Log registers and fault instruction in Unix crashlog. --- config.lib | 25 +++++++ src/crashlog.cpp | 2 +- src/crashlog.h | 2 + src/os/unix/crashlog_unix.cpp | 137 ++++++++++++++++++++++++++++++---- 4 files changed, 152 insertions(+), 14 deletions(-) diff --git a/config.lib b/config.lib index 2fee81b3e6..e3b2139ab2 100644 --- a/config.lib +++ b/config.lib @@ -1723,6 +1723,31 @@ EOL log 1 "checking sigaction... found" CFLAGS="$CFLAGS -DWITH_SIGACTION" fi + + log 2 "executing $cc_host $CFLAGS $LDFLAGS $STATIC_FLAGS -o tmp.config.ucontext -x c++ - -ldl" + "$cc_host" $CFLAGS $LDFLAGS $STATIC_FLAGS -o tmp.config.ucontext -x c++ - -ldl 2> /dev/null << EOL + #include + int main() { + ucontext_t context; +#if defined(__x86_64__) + void *ptr = (void *) context.uc_mcontext.gregs[REG_RIP]; +#elif defined(__i386) + void *ptr = (void *) context.uc_mcontext.gregs[REG_EIP]; +#else +#error Unknown arch +#endif + return 0; + } +EOL + ret=$? + rm -f tmp.config.ucontext + log 2 " exit code $ret" + if [ $ret -ne 0 ]; then + log 1 "checking ucontext... no" + else + log 1 "checking ucontext... found" + CFLAGS="$CFLAGS -DWITH_UCONTEXT" + fi fi if [ "$os" = "MINGW" ]; then diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 7749bef3d4..8aef6e8045 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -346,8 +346,8 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const #endif buffer = this->LogOpenTTDVersion(buffer, last); - buffer = this->LogRegisters(buffer, last); buffer = this->LogStacktrace(buffer, last); + buffer = this->LogRegisters(buffer, last); buffer = this->LogOSVersion(buffer, last); buffer = this->LogCompiler(buffer, last); buffer = this->LogConfiguration(buffer, last); diff --git a/src/crashlog.h b/src/crashlog.h index 7894dea0ad..9703290fd8 100644 --- a/src/crashlog.h +++ b/src/crashlog.h @@ -127,6 +127,8 @@ public: static void SetErrorMessage(const char *message); static void AfterCrashLogCleanup(); + + inline const char *GetMessage() const { return this->message; } }; #endif /* CRASHLOG_H */ diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index 5ab8e2a10c..1f9394b7d4 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -55,6 +55,8 @@ #include #endif +#include + #include "../../safeguards.h" #if defined(__GLIBC__) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) @@ -80,6 +82,9 @@ class CrashLogUnix : public CrashLog { int signum; #ifdef WITH_SIGACTION siginfo_t *si; + void *context; + bool signal_instruction_ptr_valid; + void *signal_instruction_ptr; #endif /* virtual */ char *LogOSVersion(char *buffer, const char *last) const @@ -116,8 +121,13 @@ class CrashLogUnix : public CrashLog { this->si->si_code); if (this->signum != SIGABRT) { buffer += seprintf(buffer, last, - " fault address: %p\n", + " Fault address: %p\n", this->si->si_addr); + if (this->signal_instruction_ptr_valid) { + buffer += seprintf(buffer, last, + " Instruction address: %p\n", + this->signal_instruction_ptr); + } } } #endif @@ -163,7 +173,69 @@ class CrashLogUnix : public CrashLog { #endif /** - * Get a stack backtrace of the current thread's stack using the gdb debugger, if available. + * Show registers if possible + * + * Also log GDB information if available + */ + /* virtual */ char *LogRegisters(char *buffer, const char *last) const + { + buffer = LogGdbInfo(buffer, last); + +#ifdef WITH_UCONTEXT + ucontext_t *ucontext = static_cast(context); +#if defined(__x86_64__) + const gregset_t &gregs = ucontext->uc_mcontext.gregs; + buffer += seprintf(buffer, last, + "Registers:\n" + " rax: %#16llx rbx: %#16llx rcx: %#16llx rdx: %#16llx\n" + " rsi: %#16llx rdi: %#16llx rbp: %#16llx rsp: %#16llx\n" + " r8: %#16llx r9: %#16llx r10: %#16llx r11: %#16llx\n" + " r12: %#16llx r13: %#16llx r14: %#16llx r15: %#16llx\n" + " rip: %#16llx eflags: %#8llx\n\n", + gregs[REG_RAX], + gregs[REG_RBX], + gregs[REG_RCX], + gregs[REG_RDX], + gregs[REG_RSI], + gregs[REG_RDI], + gregs[REG_RBP], + gregs[REG_RSP], + gregs[REG_R8], + gregs[REG_R9], + gregs[REG_R10], + gregs[REG_R11], + gregs[REG_R12], + gregs[REG_R13], + gregs[REG_R14], + gregs[REG_R15], + gregs[REG_RIP], + gregs[REG_EFL] + ); +#elif defined(__i386) + const gregset_t &gregs = ucontext->uc_mcontext.gregs; + buffer += seprintf(buffer, last, + "Registers:\n" + " eax: %#8x ebx: %#8x ecx: %#8x edx: %#8x\n" + " esi: %#8x edi: %#8x ebp: %#8x esp: %#8x\n" + " eip: %#8x eflags: %#8x\n\n", + gregs[REG_EAX], + gregs[REG_EBX], + gregs[REG_ECX], + gregs[REG_EDX], + gregs[REG_ESI], + gregs[REG_EDI], + gregs[REG_EBP], + gregs[REG_ESP], + gregs[REG_EIP], + gregs[REG_EFL] + ); +#endif +#endif + return buffer; + } + + /** + * Get a stack backtrace of the current thread's stack and other info using the gdb debugger, if available. * * Using GDB is useful as it knows about inlined functions and locals, and generally can * do a more thorough job than in LogStacktrace. @@ -171,7 +243,7 @@ class CrashLogUnix : public CrashLog { * and there is some potentially useful information in the output from LogStacktrace * which is not in gdb's output. */ - char *LogStacktraceGdb(char *buffer, const char *last) const + char *LogGdbInfo(char *buffer, const char *last) const { #if defined(WITH_DBG_GDB) @@ -198,9 +270,38 @@ class CrashLogUnix : public CrashLog { dup2(null_fd, STDERR_FILENO); dup2(null_fd, STDIN_FILENO); } - char buffer[16]; - seprintf(buffer, lastof(buffer), "%d", tid); - execlp("gdb", "gdb", "-n", "-p", buffer, "-batch", "-ex", "bt full", NULL); + + char tid_buffer[16]; + char disasm_buffer[32]; + + seprintf(tid_buffer, lastof(tid_buffer), "%d", tid); + + std::vector args; + args.push_back("gdb"); + args.push_back("-n"); + args.push_back("-p"); + args.push_back(tid_buffer); + args.push_back("-batch"); + + args.push_back("-ex"); + args.push_back("echo \\nBacktrace:\\n"); + args.push_back("-ex"); + args.push_back("bt full"); + +#ifdef WITH_SIGACTION + if (this->GetMessage() == NULL && this->signal_instruction_ptr_valid) { + seprintf(disasm_buffer, lastof(disasm_buffer), "x/1i %p", this->signal_instruction_ptr); + args.push_back("-ex"); + args.push_back("set disassembly-flavor intel"); + args.push_back("-ex"); + args.push_back("echo \\nFault instruction:\\n"); + args.push_back("-ex"); + args.push_back(disasm_buffer); + } +#endif + + args.push_back(NULL); + execvp("gdb", const_cast(&(args[0]))); exit(42); } @@ -210,7 +311,7 @@ class CrashLogUnix : public CrashLog { char *buffer_orig = buffer; - buffer += seprintf(buffer, last, "Stacktrace (GDB):\n"); + buffer += seprintf(buffer, last, "GDB info:\n"); while (buffer < last) { ssize_t res = read(pipefd[0], buffer, last - buffer); if (res < 0) { @@ -262,8 +363,6 @@ class CrashLogUnix : public CrashLog { */ /* virtual */ char *LogStacktrace(char *buffer, const char *last) const { - buffer = LogStacktraceGdb(buffer, last); - buffer += seprintf(buffer, last, "Stacktrace:\n"); #if defined(__GLIBC__) @@ -397,16 +496,28 @@ public: * @param signum the signal that was caused by the crash. */ #ifdef WITH_SIGACTION - CrashLogUnix(int signum, siginfo_t *si) : - signum(signum), si(si) + CrashLogUnix(int signum, siginfo_t *si, void *context) : + signum(signum), si(si), context(context) { + this->signal_instruction_ptr_valid = false; + +#ifdef WITH_UCONTEXT + ucontext_t *ucontext = static_cast(context); +#if defined(__x86_64__) + this->signal_instruction_ptr = (void *) ucontext->uc_mcontext.gregs[REG_RIP]; + this->signal_instruction_ptr_valid = true; +#elif defined(__i386) + this->signal_instruction_ptr = (void *) ucontext->uc_mcontext.gregs[REG_EIP]; + this->signal_instruction_ptr_valid = true; +#endif +#endif /* WITH_UCONTEXT */ } #else CrashLogUnix(int signum) : signum(signum) { } -#endif +#endif /* WITH_SIGACTION */ }; /** The signals we want our crash handler to handle. */ @@ -442,7 +553,7 @@ static void CDECL HandleCrash(int signum) } #ifdef WITH_SIGACTION - CrashLogUnix log(signum, si); + CrashLogUnix log(signum, si, context); #else CrashLogUnix log(signum); #endif From 62631cba02af8248c32f301ac8641ed863e38dc6 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 12 Jun 2016 20:58:52 +0100 Subject: [PATCH 5/5] Attempt to log distro name/version in Unix crashlog. Factor out fork/execve/read stdout code into own function. --- src/crashlog.cpp | 7 ++ src/crashlog.h | 8 ++ src/os/unix/crashlog_unix.cpp | 174 +++++++++++++++++++--------------- 3 files changed, 115 insertions(+), 74 deletions(-) diff --git a/src/crashlog.cpp b/src/crashlog.cpp index 8aef6e8045..507cc55947 100644 --- a/src/crashlog.cpp +++ b/src/crashlog.cpp @@ -69,6 +69,12 @@ char *CrashLog::LogCompiler(char *buffer, const char *last) const #endif } +/* virtual */ char *CrashLog::LogOSVersionDetail(char *buffer, const char *last) const +{ + /* Stub implementation; not all OSes support this. */ + return buffer; +} + /* virtual */ char *CrashLog::LogRegisters(char *buffer, const char *last) const { /* Stub implementation; not all OSes support this. */ @@ -350,6 +356,7 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const buffer = this->LogRegisters(buffer, last); buffer = this->LogOSVersion(buffer, last); buffer = this->LogCompiler(buffer, last); + buffer = this->LogOSVersionDetail(buffer, last); buffer = this->LogConfiguration(buffer, last); buffer = this->LogLibraries(buffer, last); buffer = this->LogModules(buffer, last); diff --git a/src/crashlog.h b/src/crashlog.h index 9703290fd8..37ac9ae10e 100644 --- a/src/crashlog.h +++ b/src/crashlog.h @@ -44,6 +44,14 @@ protected: */ virtual char *LogCompiler(char *buffer, const char *last) const; + /** + * Writes OS' version detail to the buffer, if available. + * @param buffer The begin where to write at. + * @param last The last position in the buffer to write to. + * @return the position of the \c '\0' character after the buffer. + */ + virtual char *LogOSVersionDetail(char *buffer, const char *last) const; + /** * Writes actually encountered error to the buffer. * @param buffer The begin where to write at. diff --git a/src/os/unix/crashlog_unix.cpp b/src/os/unix/crashlog_unix.cpp index 1f9394b7d4..8816b70dd1 100644 --- a/src/os/unix/crashlog_unix.cpp +++ b/src/os/unix/crashlog_unix.cpp @@ -20,14 +20,13 @@ #include #include #include - -#if defined(WITH_DBG_GDB) #include -#include #include -#include #include #include + +#if defined(WITH_DBG_GDB) +#include #endif /* WITH_DBG_GDB */ #if defined(WITH_PRCTL_PT) @@ -74,6 +73,60 @@ static void LogStacktraceSigSegvHandler(int sig) } #endif +static bool ExecReadStdout(const char *file, char *const *args, char *&buffer, const char *last) +{ + int pipefd[2]; + if (pipe(pipefd) == -1) return false; + + int pid = fork(); + if (pid < 0) return false; + + if (pid == 0) { + /* child */ + + close(pipefd[0]); /* Close unused read end */ + dup2(pipefd[1], STDOUT_FILENO); + close(pipefd[1]); + int null_fd = open("/dev/null", O_RDWR); + if (null_fd != -1) { + dup2(null_fd, STDERR_FILENO); + dup2(null_fd, STDIN_FILENO); + } + + execvp(file, args); + exit(42); + } + + /* parent */ + + close(pipefd[1]); /* Close unused write end */ + + while (buffer < last) { + ssize_t res = read(pipefd[0], buffer, last - buffer); + if (res < 0) { + if (errno == EINTR) continue; + break; + } else if (res == 0) { + break; + } else { + buffer += res; + } + } + buffer += seprintf(buffer, last, "\n"); + + close(pipefd[0]); /* close read end */ + + int status; + int wait_ret = waitpid(pid, &status, 0); + if (wait_ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { + /* command did not appear to run successfully */ + return false; + } else { + /* command executed successfully */ + return true; + } +} + /** * Unix implementation for the crash logger. */ @@ -107,6 +160,23 @@ class CrashLogUnix : public CrashLog { ); } + /* virtual */ char *LogOSVersionDetail(char *buffer, const char *last) const + { + struct utsname name; + if (uname(&name) < 0) return buffer; + + if (strcmp(name.sysname, "Linux") == 0) { + char *buffer_orig = buffer; + buffer += seprintf(buffer, last, "Distro version:\n"); + + const char *args[] = { "/bin/sh", "-c", "lsb_release -a || find /etc -maxdepth 1 -type f -a \\( -name '*release' -o -name '*version' \\) -exec head -v {} \\+", NULL }; + if (!ExecReadStdout("/bin/sh", const_cast(args), buffer, last)) { + buffer = buffer_orig; + } + } + return buffer; + } + /* virtual */ char *LogError(char *buffer, const char *last, const char *message) const { buffer += seprintf(buffer, last, @@ -253,84 +323,40 @@ class CrashLogUnix : public CrashLog { pid_t tid = syscall(SYS_gettid); - int pipefd[2]; - if (pipe(pipefd) == -1) return buffer; + char *buffer_orig = buffer; + buffer += seprintf(buffer, last, "GDB info:\n"); - int pid = fork(); - if (pid < 0) return buffer; + char tid_buffer[16]; + char disasm_buffer[32]; - if (pid == 0) { - /* child */ + seprintf(tid_buffer, lastof(tid_buffer), "%d", tid); - close(pipefd[0]); /* Close unused read end */ - dup2(pipefd[1], STDOUT_FILENO); - close(pipefd[1]); - int null_fd = open("/dev/null", O_RDWR); - if (null_fd != -1) { - dup2(null_fd, STDERR_FILENO); - dup2(null_fd, STDIN_FILENO); - } + std::vector args; + args.push_back("gdb"); + args.push_back("-n"); + args.push_back("-p"); + args.push_back(tid_buffer); + args.push_back("-batch"); - char tid_buffer[16]; - char disasm_buffer[32]; - - seprintf(tid_buffer, lastof(tid_buffer), "%d", tid); - - std::vector args; - args.push_back("gdb"); - args.push_back("-n"); - args.push_back("-p"); - args.push_back(tid_buffer); - args.push_back("-batch"); - - args.push_back("-ex"); - args.push_back("echo \\nBacktrace:\\n"); - args.push_back("-ex"); - args.push_back("bt full"); + args.push_back("-ex"); + args.push_back("echo \\nBacktrace:\\n"); + args.push_back("-ex"); + args.push_back("bt full"); #ifdef WITH_SIGACTION - if (this->GetMessage() == NULL && this->signal_instruction_ptr_valid) { - seprintf(disasm_buffer, lastof(disasm_buffer), "x/1i %p", this->signal_instruction_ptr); - args.push_back("-ex"); - args.push_back("set disassembly-flavor intel"); - args.push_back("-ex"); - args.push_back("echo \\nFault instruction:\\n"); - args.push_back("-ex"); - args.push_back(disasm_buffer); - } + if (this->GetMessage() == NULL && this->signal_instruction_ptr_valid) { + seprintf(disasm_buffer, lastof(disasm_buffer), "x/1i %p", this->signal_instruction_ptr); + args.push_back("-ex"); + args.push_back("set disassembly-flavor intel"); + args.push_back("-ex"); + args.push_back("echo \\nFault instruction:\\n"); + args.push_back("-ex"); + args.push_back(disasm_buffer); + } #endif - args.push_back(NULL); - execvp("gdb", const_cast(&(args[0]))); - exit(42); - } - - /* parent */ - - close(pipefd[1]); /* Close unused write end */ - - char *buffer_orig = buffer; - - buffer += seprintf(buffer, last, "GDB info:\n"); - while (buffer < last) { - ssize_t res = read(pipefd[0], buffer, last - buffer); - if (res < 0) { - if (errno == EINTR) continue; - break; - } else if (res == 0) { - break; - } else { - buffer += res; - } - } - buffer += seprintf(buffer, last, "\n"); - - close(pipefd[0]); /* close read end */ - - int status; - int wait_ret = waitpid(pid, &status, 0); - if (wait_ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { - /* gdb did not appear to run successfully */ + args.push_back(NULL); + if (!ExecReadStdout("gdb", const_cast(&(args[0])), buffer, last)) { buffer = buffer_orig; } #endif /* WITH_DBG_GDB */