]> granicus.if.org Git - nethack/commitdiff
PANICTRACE (stacktrace on panic or signal) + bits
authorkeni <keni>
Fri, 15 Jan 2010 19:54:37 +0000 (19:54 +0000)
committerkeni <keni>
Fri, 15 Jan 2010 19:54:37 +0000 (19:54 +0000)
On crash signal or panic(), use a configurable method to get a stacktrace
the user can easily report to us.  Currently only for Unix/Linux and only
ifdef BETA.  Hopefully ports can add additional methods.

Bits:
- linux hints file had PREFIX definition in the wrong place
- sample sysconf file used wrong delimiter for WIZARDS
- fix grammar error in support message when using sysconf.wizards
- options.c comment typo
- capitalize "Crash test" output from #panic command

14 files changed:
include/decl.h
include/extern.h
include/global.h
include/sys.h
src/cmd.c
src/decl.c
src/end.c
src/files.c
src/options.c
src/pager.c
src/sys.c
sys/unix/hints/linux
sys/unix/sysconf
sys/unix/unixmain.c

index a1a1c2cb929dc6914d447cb8ffafc9e7eb4ee6c9..021fa51f3d9e7fed0ed8babac5284ac0eaa54d33 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 decl.h  $Date$  $Revision$ */
-/*     SCCS Id: @(#)decl.h     3.5     2008/07/20      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -390,6 +389,10 @@ struct autopickup_exception {
 };
 #endif /* AUTOPICKUP_EXCEPTIONS */
 
+#ifdef PANICTRACE
+E char *ARGV0;
+#endif
+
 #undef E
 
 #endif /* DECL_H */
index 5d5e29413ebe083120e7174ed338f8e442b25715..a8aff13aee56eb7af65c2190494e830bb1b46ebf 100644 (file)
@@ -643,6 +643,9 @@ E void FDECL(dealloc_killer, (struct kinfo*));
 E void FDECL(save_killers, (int,int));
 E void FDECL(restore_killers, (int));
 E char *FDECL(build_english_list, (char *));
+#if defined(PANICTRACE) && !defined(NO_SIGNAL)
+E void FDECL(panictrace_setsignals, (boolean));
+#endif
 
 /* ### engrave.c ### */
 
index 3ffb8df71a814f027400de1f81c7fd3b5f6a9870..dc9a87d1d576dcb8cec516fde2fbca042af1f97d 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 global.h        $Date$  $Revision$ */
-/*     SCCS Id: @(#)global.h   3.5     2007/01/12      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -366,4 +365,18 @@ struct savefile_info {
 #define MAXMONNO       120     /* extinct monst after this number created */
 #define MHPMAX         500     /* maximum monster hp */
 
+#ifdef BETA
+/* see end.c */
+# ifndef PANICTRACE
+#  define PANICTRACE
+# endif
+#endif
+/* The following are meaningless if PANICTRACE is not defined: */
+#if defined(__linux__) && defined(__GLIBC__) && (__GLIBC__ >= 2)
+# define PANICTRACE_GLIBC
+#endif
+#ifdef UNIX
+# define PANICTRACE_GDB
+#endif
+
 #endif /* GLOBAL_H */
index 78a7cd4c82fc7c333fc1d28600314a529b727ea7..8002538cadb0cfd7c082dfed4d1af29b486d812d 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 sys.h   $Date$  $Revision$ */
-/*    SCCS Id: @(#)sys.h      3.5     2008/01/30      */
 /* Copyright (c) Kenneth Lorber, Kensington, Maryland, 2008. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -21,6 +20,14 @@ struct sysopt {
        int pers_is_uid;
        int entrymax;
        int pointsmin;
+#ifdef PANICTRACE
+               /* panic options */
+       char *gdbpath;
+       int  panictrace_gdb;
+# ifdef PANICTRACE_GLIBC
+       int panictrace_glibc;
+# endif
+#endif
 };
 E struct sysopt sysopt;
 
index 878ab5565481392d36b0be9eca7d1bda009a1924..6ce2259359e2bf0dd5b0e0b736fcaf5a60593373 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -682,7 +682,7 @@ STATIC_PTR int
 wiz_panic(VOID_ARGS)
 {
        if (yn("Do you want to call panic() and end your game?") == 'y')
-               panic("crash test.");
+               panic("Crash test.");
         return 0;
 }
 
index 021e4c958b666975f4b4c7bbe7987b6c4b4b7438..7009b5da147153acd726d7c712ad1f3109bb9e3a 100644 (file)
@@ -326,6 +326,10 @@ NEARDATA struct savefile_info sfrestinfo, sfsaveinfo = {
 #endif
 };
 
+#ifdef PANICTRACE
+char *ARGV0;
+#endif
+
 /* dummy routine used to force linkage */
 void
 decl_init()
index 0071dc99e630a609f7ecf97e3e8a350098b955e7..7b7ef6f73e0abb2d2058272822ba7715108fc02f 100644 (file)
--- a/src/end.c
+++ b/src/end.c
@@ -1,5 +1,4 @@
 /* NetHack 3.5 end.c   $Date$  $Revision$ */
-/*     SCCS Id: @(#)end.c      3.5     2008/02/08      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -61,18 +60,188 @@ extern void FDECL(nethack_exit,(int));
 
 #define done_stopprint program_state.stopprint
 
+#ifndef PANICTRACE
+# define NH_abort NH_abort_
+#endif
+
 #ifdef AMIGA
-# define NH_abort()    Abort(0)
+# define NH_abort_()   Abort(0)
 #else
 # ifdef SYSV
-# define NH_abort()    (void) abort()
+# define NH_abort_()   (void) abort()
 # else
 #  ifdef WIN32
-# define NH_abort()    win32_abort()
+# define NH_abort_()   win32_abort()
+#  else
+# define NH_abort_()   abort()
+#  endif
+# endif
+#endif
+
+#ifdef PANICTRACE
+# include <errno.h>
+# ifdef PANICTRACE_GLIBC
+#  include <execinfo.h>
+# endif
+
+/* What do we try and in what order?  Tradeoffs:
+ * glibc: +no external programs required
+ *        -requires newish glibc
+ *        -requires -rdynamic
+ * gdb:   +gives more detailed information
+ *        +works on more OS versions
+ *        -requires -g, which may preclude -O on some compilers
+ */
+# ifdef SYSCF
+#  define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
+#  ifdef PANICTRACE_GLIBC
+#   define SYSOPT_PANICTRACE_GLIBC sysopt.panictrace_glibc
+#  else
+#   define SYSOPT_PANICTRACE_GLIBC 0
+#  endif
+# else
+#  define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB")==0?0:2)
+#  ifdef PANICTRACE_GLIBC
+#   define SYSOPT_PANICTRACE_GLIBC 1
 #  else
-# define NH_abort()    abort()
+#   define SYSOPT_PANICTRACE_GLIBC 0
 #  endif
 # endif
+
+static void NDECL(NH_abort);
+#ifndef NO_SIGNAL
+static void FDECL(panictrace_handler, (int));
+#endif
+static boolean NDECL(NH_panictrace_glibc);
+static boolean NDECL(NH_panictrace_gdb);
+
+#ifndef NO_SIGNAL
+/*ARGSUSED*/
+void
+panictrace_handler(sig_unused)   /* called as signal() handler, so sent at least one arg */
+int sig_unused;
+{
+# define SIG_MSG "\nSignal received.\n"
+       (void) write(2, SIG_MSG, sizeof(SIG_MSG)-1);
+       NH_abort();
+}
+
+void
+panictrace_setsignals(set)
+boolean set;
+{
+# define SETSIGNAL(sig) (void) signal(sig, set?(SIG_RET_TYPE)panictrace_handler:SIG_DFL);
+#  ifdef SIGILL
+       SETSIGNAL(SIGILL);
+#  endif
+#  ifdef SIGTRAP
+       SETSIGNAL(SIGTRAP);
+#  endif
+#  ifdef SIGIOT
+       SETSIGNAL(SIGIOT);
+#  endif
+#  ifdef SIGBUS
+       SETSIGNAL(SIGBUS);
+#  endif
+#  ifdef SIGFPE
+       SETSIGNAL(SIGFPE);
+#  endif
+#  ifdef SIGSEGV
+       SETSIGNAL(SIGSEGV);
+#  endif
+#  ifdef SIGSTKFLT
+       SETSIGNAL(SIGSTKFLT);
+#  endif
+#  ifdef SIGSYS
+       SETSIGNAL(SIGSYS);
+#  endif
+#  ifdef SIGEMT
+       SETSIGNAL(SIGEMT);
+#  endif
+# undef SETSIGNAL
+}
+#endif
+
+static void
+NH_abort(){
+       int gdb_prio = SYSOPT_PANICTRACE_GDB;
+       int glibc_prio = SYSOPT_PANICTRACE_GLIBC;
+       static boolean aborting = FALSE;
+
+       if(aborting) return;
+       aborting = TRUE;
+
+       if(gdb_prio == glibc_prio && gdb_prio > 0) gdb_prio++;
+
+       if(gdb_prio > glibc_prio){
+               NH_panictrace_gdb() || (glibc_prio && NH_panictrace_glibc());
+       } else {
+               NH_panictrace_glibc() || (gdb_prio && NH_panictrace_gdb());
+       }
+
+       panictrace_setsignals(FALSE);
+       NH_abort_();
+}
+
+static boolean
+NH_panictrace_glibc(){
+# ifdef PANICTRACE_GLIBC
+       void *bt[20];
+       size_t count;
+       char **info;
+       int x;
+
+       raw_print("Generating more information you may report:\n");
+       count = backtrace(bt, SIZE(bt));
+       info = backtrace_symbols(bt, count);
+       for(x=0; x<count; x++){
+               raw_printf("[%d] %s",x,info[x]);
+       }
+       /* free(info);  Don't risk it. */
+       return TRUE;
+# else
+       return FALSE;
+# endif
+}
+
+# ifdef PANICTRACE_GDB
+/* I'm going to assume /bin/grep is the right path for grep. */
+#  ifdef SYSCF
+#   define GDBPATH sysopt.gdbpath
+#  else
+#   ifndef GDBPATH
+#    define GDBPATH "/usr/bin/gdb"
+#   endif
+#  endif
+# endif
+
+static boolean
+NH_panictrace_gdb(){
+# ifdef PANICTRACE_GDB
+       /* A (more) generic method to get a stack trace - invoke
+        * gdb on ourself. */
+       char *gdbpath = GDBPATH;
+       char buf[BUFSZ];
+
+       if(gdbpath == NULL || gdbpath[0] == 0) return FALSE;
+
+       sprintf(buf, "%s -n -q %s %d 2>&1 | /bin/grep '^#'",
+                       GDBPATH, ARGV0, getpid());
+       FILE *gdb = popen(buf, "w");
+       if(gdb){
+               raw_print("Generating more information you may report:\n");
+               fprintf(gdb, "bt\nquit\ny");
+               fflush(gdb);
+               sleep(4);       /* ugly */
+               pclose(gdb);
+               return TRUE;
+       } else {
+               return FALSE;
+       }
+# else
+       return FALSE;
+# endif
+}
 #endif
 
 /*
@@ -295,7 +464,7 @@ int how;
 }
 
 #if defined(WIN32) && !defined(SYSCF)
-#define NOTIFY_NETHACK_BUGS
+# define NOTIFY_NETHACK_BUGS
 #endif
 
 /*VARARGS1*/
index 13fe43ff509a0b5f9a3c6f072c22b7bbf7f7ab5c..efce344b2839a4ea8057ff7a065062bfe93e158f 100644 (file)
@@ -2119,6 +2119,24 @@ int              src;
                return 0;
            }
            sysopt.pointsmin = temp;
+       } else if ( (src==SET_IN_SYS) && match_varname(buf, "PANICTRACE_GLIBC", 16)) {
+           int temp = atoi(bufp);
+           if(temp < 1 || temp > 2){
+               raw_printf("Illegal value in PANICTRACE_GLIBC (not 0,1,2).");
+               return 0;
+           }
+           sysopt.panictrace_glibc = temp;
+       } else if ( (src==SET_IN_SYS) && match_varname(buf, "PANICTRACE_GDB", 14)) {
+           int temp = atoi(bufp);
+           if(temp < 1 || temp > 2){
+               raw_printf("Illegal value in PANICTRACE_GDB (not 0,1,2).");
+               return 0;
+           }
+           sysopt.panictrace_gdb = temp;
+       } else if ( (src==SET_IN_SYS) && match_varname(buf, "GDBPATH", 7)) {
+           if(sysopt.gdbpath) free(sysopt.gdbpath);
+           sysopt.gdbpath = (char*)alloc(strlen(bufp)+1);
+           Strcpy(sysopt.gdbpath, bufp);
 #endif
        } else if (match_varname(buf, "BOULDER", 3)) {
            (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE,
index 677715ce0948bd4954f73062743bd1085855b73f..c6cbcf175bf5325475ff1d52575f085780722f13 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 options.c       $Date$  $Revision$ */
-/*     SCCS Id: @(#)options.c  3.5     2008/08/22      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -532,7 +531,7 @@ boolean val_allowed;
 /* most environment variables will eventually be printed in an error
  * message if they don't work, and most error message paths go through
  * BUFSZ buffers, which could be overflowed by a maliciously long
- * environment variable.  if a variable can legitimately be long, or
+ * environment variable.  If a variable can legitimately be long, or
  * if it's put in a smaller buffer, the responsible code will have to
  * bounds-check itself.
  */
index bb969d98b919480518ea20e53b98da99b6164ebe..22cc5accc1186ba6be9f8fe349502c6b1f5d591d 100644 (file)
@@ -1,5 +1,4 @@
 /* NetHack 3.5 pager.c $Date$  $Revision$ */
-/*     SCCS Id: @(#)pager.c    3.5     2009/01/30      */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -897,7 +896,7 @@ docontact()
                putstr(cwin, 0, "");
        } else if(sysopt.wizards){
                char *tmp = build_english_list(sysopt.wizards);
-               Sprintf(buf, "To contact local support, %s", tmp);
+               Sprintf(buf, "To contact local support, contact %s.", tmp);
                free(tmp);
                putstr(cwin, 0, buf);
                putstr(cwin, 0, "");
index 1fee333a3cb10372d3ab31c003b1f7e86bc134e9..b7b43f880475b6e153aea80fb71e07ae48c5adcc 100644 (file)
--- a/src/sys.c
+++ b/src/sys.c
@@ -24,11 +24,27 @@ sys_early_init(){
        sysopt.pointsmin = POINTSMIN;
        sysopt.pers_is_uid = PERS_IS_UID;
 
-                       /* sanity checks */
+               /* sanity checks */
        if(PERSMAX<1) sysopt.persmax = 1;
        if(ENTRYMAX<10) sysopt.entrymax = 10;
        if(POINTSMIN<1) sysopt.pointsmin = 1;
        if(PERS_IS_UID != 0 && PERS_IS_UID != 1)
                panic("config error: PERS_IS_UID must be either 0 or 1");
+
+#ifdef PANICTRACE
+               /* panic options */
+       sysopt.gdbpath = NULL;
+# ifdef BETA
+       sysopt.panictrace_gdb = 1;
+#  ifdef PANICTRACE_GLIBC
+       sysopt.panictrace_glibc = 2;
+#  endif
+# else
+       sysopt.panictrace_gdb = 0;
+#  ifdef PANICTRACE_GLIBC
+       sysopt.panictrace_glibc = 0;
+#  endif
+# endif
+#endif
 }
 
index 37f137bd7946eb6138b3deff78fd4de78c0e6478..9ff45ff00f0e40ebcb4311fb98f2585f82d7130e 100644 (file)
@@ -9,15 +9,19 @@
 # for Ubuntu dapper.
 
 
-HACKDIR=$(PREFIX)/games/lib/$(GAME)dir
-SHELLDIR = $(PREFIX)/games
 #PREFIX=/usr
 PREFIX=$(wildcard ~)/nh/install
+HACKDIR=$(PREFIX)/games/lib/$(GAME)dir
+SHELLDIR = $(PREFIX)/games
 
-CFLAGS=-O -I../include -DNOTPARMDECL $(CFLAGS1) -DDLB
+CFLAGS=-g -O -I../include -DNOTPARMDECL $(CFLAGS1) -DDLB
 CFLAGS1=-DCOMPRESS=\"/bin/gzip\" -DCOMPRESS_EXTENSION=\".gz\"
+CFLAGS+=-DSYSCF -DSYSCF_FILE=\"$(HACKDIR)/sysconf\" -DSECURE
+CFLAGS+=-DHACKDIR=\"$(HACKDIR)\"
 
 LINK=$(CC)
+# Only needed for GLIBC stack trace:
+LFLAGS=-rdynamic
 
 WINSRC = $(WINTTYSRC)
 WINOBJ = $(WINTTYOBJ)
index 64433909811204bacb9e5b8766f0521534c98ecf..4090e223f47293aef1a6c4a5c775719a08a5c97b 100644 (file)
@@ -7,7 +7,7 @@
 
 # Which users can use WIZARD (debugging) mode (the -D flag).
 # A value of * allows anyone to enter debugging mode.
-WIZARDS=root,games
+WIZARDS=root games
 
 # Users allowed to use the ! (shell escape) command or to suspend the game.
 # Uses the same syntax as the WIZARDS option above.
@@ -35,3 +35,14 @@ MAXPLAYERS=10
 # Determine identity of "person" in the score file with name (0) or
 # numeric (1) user id.
 #PERS_IS_UID=1
+
+# Try to get more info in case of a program bug or crash.  Using GDB can
+# get more information and works on more systems but requires gdb be available;
+# using GLIBC only works if NetHack is linked with glibc.  Both require
+# certain compilation options.  See src/end.c and sys/unix/hints/* for
+# more information.
+GDBPATH=/usr/bin/gdb
+# Values are priorities: 0 - do not use this method, 1 - low priority,
+# 2 - high priority.  Non-zero priority methods are tried in order.
+PANICTRACE_GDB=1
+PANICTRACE_GLIBC=2
index 02b8ac45de6ab495b50f1da6ab567d5581836327..7e3342a4497d4da56a26500298b80b05642fef8e 100644 (file)
@@ -139,6 +139,12 @@ char *argv[];
                initoptions_init();
                read_config_file(SYSCF_FILE, SET_IN_SYS);
                initoptions_finish();
+#endif
+#ifdef PANICTRACE
+               ARGV0 = argv[0];        /* save for possible stack trace */
+# ifndef NO_SIGNAL
+               panictrace_setsignals(TRUE);
+# endif
 #endif
                prscore(argc, argv);
                exit(EXIT_SUCCESS);
@@ -164,6 +170,12 @@ char *argv[];
        read_config_file(SYSCF_FILE, SET_IN_SYS);
 #endif
        initoptions_finish();
+#ifdef PANICTRACE
+       ARGV0 = argv[0];        /* save for possible stack trace */
+# ifndef NO_SIGNAL
+       panictrace_setsignals(TRUE);
+# endif
+#endif
        exact_username = whoami();
 
        /*