summaryrefslogtreecommitdiffstats
path: root/crawl-ref
diff options
context:
space:
mode:
Diffstat (limited to 'crawl-ref')
-rw-r--r--crawl-ref/source/acr.cc2
-rw-r--r--crawl-ref/source/debug.cc83
-rw-r--r--crawl-ref/source/debug.h1
-rw-r--r--crawl-ref/source/libdos.cc12
-rw-r--r--crawl-ref/source/libdos.h5
-rw-r--r--crawl-ref/source/libunix.cc146
-rw-r--r--crawl-ref/source/libunix.h6
-rw-r--r--crawl-ref/source/libw32c.cc12
-rw-r--r--crawl-ref/source/libw32c.h5
-rw-r--r--crawl-ref/source/makefile.unix2
-rw-r--r--crawl-ref/source/makefile_tiles.unix2
-rw-r--r--crawl-ref/source/message.cc13
-rw-r--r--crawl-ref/source/message.h1
-rw-r--r--crawl-ref/source/state.cc76
-rw-r--r--crawl-ref/source/state.h5
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;