PANICTRACE (stacktrace on panic or signal) + bits

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
This commit is contained in:
keni
2010-01-15 19:54:37 +00:00
parent f6e40b8bf8
commit d80fcaada4
14 changed files with 277 additions and 19 deletions

View File

@@ -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;
}

View 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()

181
src/end.c
View File

@@ -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,20 +60,190 @@ 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()
# 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 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
/*
* The order of these needs to match the macros in hack.h.
*/
@@ -295,7 +464,7 @@ int how;
}
#if defined(WIN32) && !defined(SYSCF)
#define NOTIFY_NETHACK_BUGS
# define NOTIFY_NETHACK_BUGS
#endif
/*VARARGS1*/

View 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,

View 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.
*/

View 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, "");

View File

@@ -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
}