diff options
Diffstat (limited to 'crawl-ref')
-rw-r--r-- | crawl-ref/source/acr.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/debug.cc | 83 | ||||
-rw-r--r-- | crawl-ref/source/debug.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/libdos.cc | 12 | ||||
-rw-r--r-- | crawl-ref/source/libdos.h | 5 | ||||
-rw-r--r-- | crawl-ref/source/libunix.cc | 146 | ||||
-rw-r--r-- | crawl-ref/source/libunix.h | 6 | ||||
-rw-r--r-- | crawl-ref/source/libw32c.cc | 12 | ||||
-rw-r--r-- | crawl-ref/source/libw32c.h | 5 | ||||
-rw-r--r-- | crawl-ref/source/makefile.unix | 2 | ||||
-rw-r--r-- | crawl-ref/source/makefile_tiles.unix | 2 | ||||
-rw-r--r-- | crawl-ref/source/message.cc | 13 | ||||
-rw-r--r-- | crawl-ref/source/message.h | 1 | ||||
-rw-r--r-- | crawl-ref/source/state.cc | 76 | ||||
-rw-r--r-- | crawl-ref/source/state.h | 5 |
15 files changed, 366 insertions, 5 deletions
diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index 9ce37018c5..e584530dae 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -196,6 +196,8 @@ int main( int argc, char *argv[] ) { _compile_time_asserts(); // Just to quiet "unused static function" warning. + init_crash_handler(); + // Hardcoded initial keybindings. init_keybindings(); diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index dd8b259b0c..2e0572b70e 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -44,11 +44,7 @@ REVISION("$Rev$"); #include "files.h" #include "food.h" #include "ghost.h" - -#ifdef DEBUG_DIAGNOSTICS #include "initfile.h" -#endif - #include "invent.h" #include "it_use2.h" #include "itemname.h" @@ -5505,6 +5501,85 @@ void debug_miscast( int target_index ) } #endif +void do_crash_dump() +{ + std::string dir = (!Options.morgue_dir.empty() ? Options.morgue_dir : + !SysEnv.crawl_dir.empty() ? SysEnv.crawl_dir + : ""); + + if (!dir.empty() && dir[dir.length() - 1] != FILE_SEPARATOR) + dir += FILE_SEPARATOR; + + char name[180]; + + sprintf(name, "%scrash-%s-%d.txt", dir.c_str(), + you.your_name, (int) time(NULL)); + + FILE* file = fopen(name, "w"); + + if (file == NULL) + { + fprintf(stderr, "\r\nUnable to open file '%s' for writing: %s\r\n", + name, strerror(errno)); + file = stderr; + } + else + fprintf(stderr, "\r\nWriting crash info to %s\r\n", name); + + set_msg_dump_file(file); + + // First get the immediate cause of the crash and the stack trace, + // since that's most important and later attempts to get more information + // might themselves cause crashes. + dump_crash_info(file); + // Ignore the top five stack frames, since the first three involve + // signal handling and the last two are do_crash_dump() and + // write_stack_trace(). + write_stack_trace(file, 5); + + if (Generating_Level) + { + fprintf(file, "\nCrashed while generating level.\r\n"); + fprintf(file, "your_level = %d, level_type = %d, type_name = %s\r\n", + you.your_level, you.level_type, you.level_type_name.c_str()); + + extern std::string dgn_Build_Method; + extern bool river_level, lake_level, many_pools_level; + + fprintf(file, "dgn_Build_Method = %s\r\n", dgn_Build_Method.c_str()); + fprintf(file, "dgn_Layout_Type = %s\r\n", dgn_Layout_Type.c_str()); + + extern bool river_level, lake_level, many_pools_level; + + if (river_level) + fprintf(file, "river level\r\n"); + if (lake_level) + fprintf(file, "lake level\r\n"); + if (many_pools_level) + fprintf(file, "many pools level\r\n"); + + fprintf(file, "\r\n"); + } + + // Dumping the crawl state is least likely to cause another crash, + // so do that next. + crawl_state.dump(file); + + // Next item and monster scans. Any messages will be sent straight to + // the file because of set_msg_dump_file() +#if DEBUG_ITEM_SCAN + debug_item_scan(); +#endif +#if DEBUG_MONS_SCAN + debug_mons_scan(); +#endif + + set_msg_dump_file(NULL); + + if (file != stderr) + fclose(file); +} + #ifdef DEBUG_DIAGNOSTICS // Map statistics generation. diff --git a/crawl-ref/source/debug.h b/crawl-ref/source/debug.h index 22a62a0333..1e23f14eb9 100644 --- a/crawl-ref/source/debug.h +++ b/crawl-ref/source/debug.h @@ -144,4 +144,5 @@ bool get_item_by_name(item_def *item, char* specs, object_class_type class_wanted, bool create_for_real = false); +void do_crash_dump(); #endif diff --git a/crawl-ref/source/libdos.cc b/crawl-ref/source/libdos.cc index 8442bf9e06..f9683ff8c8 100644 --- a/crawl-ref/source/libdos.cc +++ b/crawl-ref/source/libdos.cc @@ -139,4 +139,16 @@ void putwch(unsigned c) putch(static_cast<char>(c)); } +void init_crash_handler() +{ +} + +void dump_crash_info(FILE* file) +{ +} + +void write_stack_trace(FILE* file, int ignore_count) +{ +} + #endif /* #if defined(DOS) */ diff --git a/crawl-ref/source/libdos.h b/crawl-ref/source/libdos.h index a6502c06ec..e9a7941563 100644 --- a/crawl-ref/source/libdos.h +++ b/crawl-ref/source/libdos.h @@ -12,6 +12,7 @@ #define __LIBDOS_H__ #include <conio.h> +#include <stdio.h> typedef unsigned char screen_buffer_t; @@ -44,4 +45,8 @@ inline void put_colour_ch(int colour, unsigned ch) putwch(ch); } +void init_crash_handler(); +void dump_crash_info(FILE* file); +void write_stack_trace(FILE* file, int ignore_count); + #endif diff --git a/crawl-ref/source/libunix.cc b/crawl-ref/source/libunix.cc index 5ecf73c57e..c4ef372837 100644 --- a/crawl-ref/source/libunix.cc +++ b/crawl-ref/source/libunix.cc @@ -33,6 +33,7 @@ REVISION("$Rev$"); #include "enum.h" #include "externs.h" #include "files.h" +#include "initfile.h" #include "state.h" #include "stuff.h" #include "view.h" @@ -69,6 +70,10 @@ static struct termios game_term; #include CURSES_INCLUDE_FILE #endif +#ifdef __GLIBC__ +#include <execinfo.h> +#endif + // Globals holding current text/backg. colors static short FG_COL = WHITE; static short BG_COL = BLACK; @@ -1136,3 +1141,144 @@ extern "C" { return (str); } } + +///////////////////////////////////////////////////////////////////////////// +// Code for printing out debugging info on a crash. +//////////////////////////////////////////////////////////////////////////// +static int _crash_signal = 0; + +static void _crash_signal_handler(int sig_num) +{ + if (crawl_state.game_crashed) + { + fprintf(stderr, "Recursive crash.\n"); + + std::string dir = (!Options.morgue_dir.empty() ? Options.morgue_dir : + !SysEnv.crawl_dir.empty() ? SysEnv.crawl_dir + : ""); + + if (!dir.empty() && dir[dir.length() - 1] != FILE_SEPARATOR) + dir += FILE_SEPARATOR; + + char name[180]; + + sprintf(name, "%scrash-recursive-%s-%d.txt", dir.c_str(), + you.your_name, (int) time(NULL)); + + FILE* file = fopen(name, "w"); + + if (file == NULL) + file = stderr; + + write_stack_trace(file, 0); + + if (file != stderr) + fclose(file); + return; + } + + _crash_signal = sig_num; + crawl_state.game_crashed = true; + + do_crash_dump(); + + // Now crash for real. + signal(sig_num, SIG_DFL); + raise(sig_num); +} + +void init_crash_handler() +{ +#if defined(USE_UNIX_SIGNALS) + + for (int i = 1; i <= 64; i++) + { +#ifdef SIGHUP_SAVE + if (i == SIGHUP) + continue; +#endif +#ifdef SIGQUIT + if (i == SIGQUIT) + continue; +#endif +#ifdef SIGINT + if (i == SIGINT) + continue; +#endif +#ifdef SIGCHLD + if (i == SIGCHLD) + continue; +#endif +#ifdef SIGTSTP + if (i == SIGTSTP) + continue; +#endif +#ifdef SIGCONT + if (i == SIGCONT) + continue; +#endif +#ifdef SIGIO + if (i == SIGIO) + continue; +#endif +#ifdef SIGPROF + if (i == SIGPROF) + continue; +#endif + if (i == SIGWINCH) + continue; + + signal(i, _crash_signal_handler); + } + +#endif // if defined(USE_UNIX_SIGNALS) +} + +void dump_crash_info(FILE* file) +{ + const char *name = strsignal(_crash_signal); + if (name == NULL) + name = "INVALID"; + + fprintf(file, "Crash caused by signal #%d: %s\r\n", _crash_signal, + name); +} + +#ifdef __GLIBC__ +// NOTE: This should work on OS X, according to +// http://developer.apple.com/DOCUMENTATION/DARWIN/Reference/ManPages/man3/backtrace_symbols.3.html + +void write_stack_trace(FILE* file, int ignore_count) +{ + void* frames[50]; + + int num_frames = backtrace(frames, ARRAYSZ(frames)); + + char **symbols = backtrace_symbols(frames, num_frames); + + if (symbols == NULL) + { + fprintf(stderr, "Out of memroy.\r\n"); + fprintf(file, "Out of memory.\r\n"); + + // backtrace_symbols_fd() can print out the stack trace even if + // malloc() can't find any free memory. + backtrace_symbols_fd(frames, num_frames, fileno(file)); + return; + } + + for (int i = ignore_count; i < num_frames; i++) + { + fprintf(file, "%s\r\n", symbols[i]); + } + + free(symbols); +} +#else // ifdef __GLIBC__ +void write_stack_trace(FILE* file, int ignore_count) +{ + const char* msg = "Unable to get stack trace on this platform.\n"; + fprintf(stderr, msg); + fprintf(file, msg); +} +#endif diff --git a/crawl-ref/source/libunix.h b/crawl-ref/source/libunix.h index b1c2090f83..12602014fb 100644 --- a/crawl-ref/source/libunix.h +++ b/crawl-ref/source/libunix.h @@ -22,6 +22,8 @@ typedef unsigned int screen_buffer_t; typedef unsigned short screen_buffer_t; #endif +#include <stdio.h> + char getche(void); void message_out(int mline, int colour, const char *str, int firstcol = 0, @@ -66,6 +68,10 @@ inline bool is_smart_cursor_enabled() { return (false); } void set_mouse_enabled(bool enabled); +void init_crash_handler(); +void dump_crash_info(FILE* file); +void write_stack_trace(FILE* file, int ignore_count); + #ifndef _LIBUNIX_IMPLEMENTATION /* Some stuff from curses, to remove compiling warnings.. */ extern "C" diff --git a/crawl-ref/source/libw32c.cc b/crawl-ref/source/libw32c.cc index 73d1701873..e0b612b4ad 100644 --- a/crawl-ref/source/libw32c.cc +++ b/crawl-ref/source/libw32c.cc @@ -1147,4 +1147,16 @@ int ftruncate(int fp, int size) #endif /* #if _MSC_VER */ +void init_crash_handler() +{ +} + +void dump_crash_info(FILE* file) +{ +} + +void write_stack_trace(FILE* file, int ignore_count) +{ +} + #endif /* #if defined(WIN32CONSOLE) */ diff --git a/crawl-ref/source/libw32c.h b/crawl-ref/source/libw32c.h index 1a54543451..500e69f131 100644 --- a/crawl-ref/source/libw32c.h +++ b/crawl-ref/source/libw32c.h @@ -7,6 +7,7 @@ #include <string> #include <stdarg.h> +#include <stdio.h> typedef unsigned short screen_buffer_t; @@ -60,4 +61,8 @@ inline void put_colour_ch(int colour, unsigned ch) putwch(ch); } +void init_crash_handler(); +void dump_crash_info(FILE* file); +void write_stack_trace(FILE* file, int ignore_count); + #endif diff --git a/crawl-ref/source/makefile.unix b/crawl-ref/source/makefile.unix index 31a9d2cad3..c5b3e9fcf6 100644 --- a/crawl-ref/source/makefile.unix +++ b/crawl-ref/source/makefile.unix @@ -16,6 +16,8 @@ CXX = g++ DELETE = rm -f COPY = cp OS_TYPE = UNIX +# To get stack trace symbols. +LDFLAGS = -rdynamic # Change this to y (case-sensitive!) if you want to use Unicode glyphs # in the map, and you have libncursesw available. diff --git a/crawl-ref/source/makefile_tiles.unix b/crawl-ref/source/makefile_tiles.unix index 70fcdf8bea..ed6998aa73 100644 --- a/crawl-ref/source/makefile_tiles.unix +++ b/crawl-ref/source/makefile_tiles.unix @@ -20,6 +20,8 @@ DELETE = rm -f COPY = cp OS_TYPE = UNIX EXTRA_INCLUDES := -DUSE_TILE -DCLUA_BINDINGS +# To get stack trace symbols. +LDFLAGS = -rdynamic # Change this to y if you want to use Unicode glyphs in the map, and you have # libncursesw available. diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc index aedfa7ba65..7bda843e10 100644 --- a/crawl-ref/source/message.cc +++ b/crawl-ref/source/message.cc @@ -61,6 +61,8 @@ int Next_Message = 0; // end of messages int Message_Line = 0; // line of next (previous?) message int New_Message_Count = 0; +static FILE* _msg_dump_file = NULL; + static bool suppress_messages = false; static void base_mpr(const char *inf, msg_channel_type channel, int param, unsigned char colour); @@ -417,6 +419,12 @@ void mprf( const char *format, ... ) void mpr(const char *inf, msg_channel_type channel, int param) { + if (_msg_dump_file != NULL) + fprintf(_msg_dump_file, "%s\n", inf); + + if (crawl_state.game_crashed) + return; + if (crawl_state.arena) { switch(channel) @@ -1275,3 +1283,8 @@ void replay_messages(void) return; } // end replay_messages() + +void set_msg_dump_file(FILE* file) +{ + _msg_dump_file = file; +} diff --git a/crawl-ref/source/message.h b/crawl-ref/source/message.h index 4f7cb8f1de..123551047b 100644 --- a/crawl-ref/source/message.h +++ b/crawl-ref/source/message.h @@ -127,5 +127,6 @@ namespace msg std::ostream& operator<<(std::ostream& os, const msg::setparam& sp); +void set_msg_dump_file(FILE* file); #endif diff --git a/crawl-ref/source/state.cc b/crawl-ref/source/state.cc index febc6e872d..1b2e1f773e 100644 --- a/crawl-ref/source/state.cc +++ b/crawl-ref/source/state.cc @@ -19,12 +19,13 @@ REVISION("$Rev$"); #include "mon-util.h" #include "output.h" #include "player.h" +#include "religion.h" #include "state.h" #include "tutorial.h" #include "view.h" game_state::game_state() - : mouse_enabled(false), waiting_for_command(false), + : game_crashed(false), mouse_enabled(false), waiting_for_command(false), terminal_resized(false), io_inited(false), need_save(false), saving_game(false), updating_scores(false), seen_hups(0), map_stat_gen(false), arena(false), arena_suspended(false), @@ -372,3 +373,76 @@ std::vector<god_act_state> game_state::other_gods_acting() const ASSERT(is_god_acting()); return god_act_stack; } + +void game_state::dump(FILE* file) +{ + fprintf(file, "\r\nGame state:\r\n\r\n"); + + fprintf(file, "mouse_enabled: %d, waiting_for_command: %d " + "terminal_resized: %d\r\n", + mouse_enabled, waiting_for_command, terminal_resized); + fprintf(file, "io_inited: %d, need_save: %d, saving_game: %d, " + "updating_scores: %d\r\n:", + io_inited, need_save, saving_game, updating_scores); + fprintf(file, "seen_hups: %d, map_stat_gen: %d, arena: %d, " + "arena_suspended: %d, unicode_ok: %d\r\n", + seen_hups, map_stat_gen, arena, arena_suspended, unicode_ok); + + fprintf(file, "\r\n"); + + if (!startup_errors.empty()) + { + fprintf(file, "Startup errors:\r\n"); + for (unsigned int i = 0; i < startup_errors.size(); i++) + fprintf(file, "%s\n", startup_errors[i].c_str()); + fprintf(file, "\r\n"); + } + + fprintf(file, "prev_cmd = %s\r\n", command_to_name(prev_cmd).c_str()); + + if (doing_prev_cmd_again) + { + fprintf(file, "Doing prev_cmd again with keys: "); + for (unsigned int i = 0; i < prev_cmd_keys.size(); i++) + fprintf(file, "%d, ", prev_cmd_keys[i]); + fprintf(file, "\r\n"); + fprintf(file, "As ASCII keys: "); + for (unsigned int i = 0; i < prev_cmd_keys.size(); i++) + fprintf(file, "%c", (char) prev_cmd_keys[i]); + fprintf(file, "\r\n\r\n"); + } + fprintf(file, "repeat_cmd = %s\r\n", command_to_name(repeat_cmd).c_str()); + + if (cmd_repeat_count > 0 || cmd_repeat_goal > 0) + { + fprintf(file, "Doing command repitition: \r\n"); + fprintf(file, "cmd_repeat_start:%d, cmd_repeat_count: %d, " + "cmd_repeat_goal:%d\r\nprev_cmd_repeat_goal: %d\r\n", + cmd_repeat_start, cmd_repeat_count, cmd_repeat_goal, + prev_cmd_repeat_goal); + fprintf(file, "Keys being repeated: "); + for (unsigned int i = 0; i < repeat_cmd_keys.size(); i++) + fprintf(file, "%d, ", repeat_cmd_keys[i]); + fprintf(file, "\r\n"); + fprintf(file, "As ASCII keys: "); + for (unsigned int i = 0; i < repeat_cmd_keys.size(); i++) + fprintf(file, "%c", (char) repeat_cmd_keys[i]); + fprintf(file, "\r\n\r\n"); + } + + if (god_act.which_god != GOD_NO_GOD || god_act.depth != 0) + { + fprintf(file, "God %s currently acting with depth %d\r\n\r\n", + god_name(god_act.which_god).c_str(), god_act.depth); + } + + if (god_act_stack.size() != 0) + { + fprintf(file, "Other gods acting: \r\n"); + for (unsigned int i = 0; i < god_act_stack.size(); i++) + fprintf(file, "God %s with depth %d\r\n", + god_name(god_act_stack[i].which_god).c_str(), + god_act_stack[i].depth); + fprintf(file, "\r\n"); + } +} diff --git a/crawl-ref/source/state.h b/crawl-ref/source/state.h index 85abe5853b..23c6675dfd 100644 --- a/crawl-ref/source/state.h +++ b/crawl-ref/source/state.h @@ -11,6 +11,7 @@ #include "enum.h" #include <vector> +#include <stdio.h> struct god_act_state { @@ -27,6 +28,8 @@ public: // Track various aspects of Crawl game state. struct game_state { + bool game_crashed; // The game crashed and is now in the process of + // dumping crash info. bool mouse_enabled; // True if mouse input is currently relevant. bool waiting_for_command; // True when the game is waiting for a command. @@ -116,6 +119,8 @@ public: void clear_god_acting(); std::vector<god_act_state> other_gods_acting() const; + + void dump(FILE* file); }; extern game_state crawl_state; |