summaryrefslogtreecommitdiffstats
path: root/crawl-ref/source
diff options
context:
space:
mode:
authorzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2009-01-18 05:12:24 +0000
committerzelgadis <zelgadis@c06c8d41-db1a-0410-9941-cceddc491573>2009-01-18 05:12:24 +0000
commitddb951a57ed0ac94c749e971f4c0556794110275 (patch)
treeb43fb8c51041edf4f9f846ede3aa0a6c79a0ffb3 /crawl-ref/source
parentb8f4f50faab8b771ad053c154e781d129ef55005 (diff)
downloadcrawl-ref-ddb951a57ed0ac94c749e971f4c0556794110275.tar.gz
crawl-ref-ddb951a57ed0ac94c749e971f4c0556794110275.zip
Implemented crash data reporting, though it's only some stubs on Windows and
DOS. On UNIX with USE_UNIX_SIGNALS defined, when any crash causing signal happens it will dump to a file the current crawl_state, anything caught by the items and monsters scans, and level building info if the crash happened during level generation. Also, if crawl is linked against the GNU C library (and the exectuable is in ELF format) it will dump the stack trace. The code attempts to automatically detect the presence of glibc, but that might not work on all systems. This should work on OS X, since there's an OS X man page for the glibc functions that get the stack trace. Don't know if it would work with MinGW. Actually getting function names for the stack trace requires the use of the "-rdynamic" linker option, which increases the size of the stripped executable by 27% (yikes!), but still prints the function names even when stripped. All of the function names in the stack trace are mangled C++ ones, but that shouldn't be too much of a problem. git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@8532 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'crawl-ref/source')
-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;