{
LOAD_FORMATSPECIFIC_AVI();
if(!format->canseek)
- Sys_Error("SCR_CaptureVideo_RIFF_IndexEntry called on non-seekable AVI");
+ Sys_Abort("SCR_CaptureVideo_RIFF_IndexEntry called on non-seekable AVI");
if (format->riffstacklevel != 2)
- Sys_Error("SCR_Capturevideo_RIFF_IndexEntry: RIFF stack level is %i (should be 2)\n", format->riffstacklevel);
+ Sys_Abort("SCR_Capturevideo_RIFF_IndexEntry: RIFF stack level is %i (should be 2)\n", format->riffstacklevel);
GrowBuf(&format->riffindexbuffer, 16);
SCR_CaptureVideo_RIFF_Flush();
MSG_WriteUnterminatedString(&format->riffindexbuffer, chunkfourcc);
fs_offset_t pos, sz;
if(!format->canseek)
- Sys_Error("SCR_CaptureVideo_RIFF_MakeIxChunk called on non-seekable AVI");
+ Sys_Abort("SCR_CaptureVideo_RIFF_MakeIxChunk called on non-seekable AVI");
if(*masteridx_count >= AVI_MASTER_INDEX_SIZE)
return;
fs_offset_t cursize;
//fs_offset_t curfilesize;
if (format->riffstacklevel != 2)
- Sys_Error("SCR_CaptureVideo_RIFF_OverflowCheck: chunk stack leakage!\n");
+ Sys_Abort("SCR_CaptureVideo_RIFF_OverflowCheck: chunk stack leakage!\n");
if(!format->canseek)
return;
// we're done with the headers now...
SCR_CaptureVideo_RIFF_Flush();
if (format->riffstacklevel != 2)
- Sys_Error("SCR_CaptureVideo_BeginVideo: broken AVI writing code (stack level is %i (should be 2) at end of headers)\n", format->riffstacklevel);
+ Sys_Abort("SCR_CaptureVideo_BeginVideo: broken AVI writing code (stack level is %i (should be 2) at end of headers)\n", format->riffstacklevel);
if(!format->canseek)
{
format->videopage.len = pg.header_len + pg.body_len;
format->videopage.time = qtheora_granule_time(&format->ts, qogg_page_granulepos(&pg));
if(format->videopage.len > sizeof(format->videopage.data))
- Sys_Error("video page too long");
+ Sys_Abort("video page too long");
memcpy(format->videopage.data, pg.header, pg.header_len);
memcpy(format->videopage.data + pg.header_len, pg.body, pg.body_len);
}
format->audiopage.len = pg.header_len + pg.body_len;
format->audiopage.time = qvorbis_granule_time(&format->vd, qogg_page_granulepos(&pg));
if(format->audiopage.len > sizeof(format->audiopage.data))
- Sys_Error("audio page too long");
+ Sys_Abort("audio page too long");
memcpy(format->audiopage.data, pg.header, pg.header_len);
memcpy(format->audiopage.data + pg.header_len, pg.body, pg.body_len);
}
while (1) {
int result = qogg_stream_flush (&format->to, &pg);
if (result < 0)
- fprintf (stderr, "Internal Ogg library error.\n"); // TODO Sys_Error
+ fprintf (stderr, "Internal Ogg library error.\n"); // TODO Sys_Abort
if (result <= 0)
break;
FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
while (1) {
int result = qogg_stream_flush (&format->vo, &pg);
if (result < 0)
- fprintf (stderr, "Internal Ogg library error.\n"); // TODO Sys_Error
+ fprintf (stderr, "Internal Ogg library error.\n"); // TODO Sys_Abort
if (result <= 0)
break;
FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
{
int result = qogg_stream_flush (&format->to, &pg);
if (result < 0)
- fprintf (stderr, "Internal Ogg library error.\n"); // TODO Sys_Error
+ fprintf (stderr, "Internal Ogg library error.\n"); // TODO Sys_Abort
if (result <= 0)
break;
FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
{
int result = qogg_stream_flush (&format->vo, &pg);
if (result < 0)
- fprintf (stderr, "Internal Ogg library error.\n"); // TODO Sys_Error
+ fprintf (stderr, "Internal Ogg library error.\n"); // TODO Sys_Abort
if (result <= 0)
break;
FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len);
// restart the demo recording
cls.demofile = FS_OpenRealFile(cls.demoname, "wb", false);
if(!cls.demofile)
- Sys_Error("failed to reopen the demo file");
+ Sys_Abort("failed to reopen the demo file");
FS_Printf(cls.demofile, "%i\n", cls.forcetrack);
}
if (num >= cl.max_entities)
{
if (!cl.entities)
- Sys_Error("CL_ExpandEntities: cl.entities not initialized");
+ Sys_Abort("CL_ExpandEntities: cl.entities not initialized");
if (num >= MAX_EDICTS)
Host_Error("CL_ExpandEntities: num %i >= %i", num, MAX_EDICTS);
oldmaxentities = cl.max_entities;
int basex, basey, w, h, y;
CL_Particle_PixelCoordsForTexnum(texnum, &basex, &basey, &w, &h);
if(w != PARTICLETEXTURESIZE || h != PARTICLETEXTURESIZE)
- Sys_Error("invalid particle texture size for autogenerating");
+ Sys_Abort("invalid particle texture size for autogenerating");
for (y = 0;y < PARTICLETEXTURESIZE;y++)
memcpy(particletexturedata + ((basey + y) * PARTICLEFONTSIZE + basex) * 4, data + y * PARTICLETEXTURESIZE * 4, PARTICLETEXTURESIZE * 4);
}
typedef struct sizebuf_s
{
- qbool allowoverflow; ///< if false, do a Sys_Error
+ qbool allowoverflow; ///< if false, do a Sys_Abort
qbool overflowed; ///< set to true if the buffer size failed
unsigned char *data;
int maxsize;
}
#endif
-#define BACKENDACTIVECHECK if (!gl_state.active) Sys_Error("GL backend function called when backend is not active");
+#define BACKENDACTIVECHECK if (!gl_state.active) Sys_Abort("GL backend function called when backend is not active");
void SCR_ScreenShot_f(cmd_state_t *cmd);
{
gltextureunit_t *unit = gl_state.units + unitnum;
if (unitnum >= MAX_TEXTUREUNITS)
- Sys_Error("R_Mesh_TexCoordPointer: unitnum %i > max units %i\n", unitnum, MAX_TEXTUREUNITS);
+ Sys_Abort("R_Mesh_TexCoordPointer: unitnum %i > max units %i\n", unitnum, MAX_TEXTUREUNITS);
// update array settings
// note: there is no need to check bufferobject here because all cases
// that involve a valid bufferobject also supply a texcoord array
{
gltextureunit_t *unit = gl_state.units + unitnum;
if (unitnum >= MAX_TEXTUREUNITS)
- Sys_Error("R_Mesh_TexCoordPointer: unitnum %i > max units %i\n", unitnum, MAX_TEXTUREUNITS);
+ Sys_Abort("R_Mesh_TexCoordPointer: unitnum %i > max units %i\n", unitnum, MAX_TEXTUREUNITS);
if (id == GL_TEXTURE_2D)
return unit->t2d;
if (id == GL_TEXTURE_3D)
gltextureunit_t *unit = gl_state.units + unitnum;
int texnum;
if (unitnum >= MAX_TEXTUREUNITS)
- Sys_Error("R_Mesh_TexBind: unitnum %i > max units %i\n", unitnum, MAX_TEXTUREUNITS);
+ Sys_Abort("R_Mesh_TexBind: unitnum %i > max units %i\n", unitnum, MAX_TEXTUREUNITS);
switch(vid.renderpath)
{
case RENDERPATH_GL32:
// if the resize did not give us enough memory, fail
if (!r_bufferdata_buffer[r_bufferdata_cycle][type] || r_bufferdata_buffer[r_bufferdata_cycle][type]->current + padsize > r_bufferdata_buffer[r_bufferdata_cycle][type]->size)
- Sys_Error("R_BufferData_Store: failed to create a new buffer of sufficient size\n");
+ Sys_Abort("R_BufferData_Store: failed to create a new buffer of sufficient size\n");
mem = r_bufferdata_buffer[r_bufferdata_cycle][type];
offset = (int)mem->current;
if (c >= rsurface.modelsurfaces[j].num_firstvertex && c < (rsurface.modelsurfaces[j].num_firstvertex + rsurface.modelsurfaces[j].num_vertices))
{
if (rsurface.modelsurfaces[j].texture != rsurface.texture)
- Sys_Error("RSurf_DrawBatch: index %i uses different texture (%s) than surface %i which it belongs to (which uses %s)\n", c, rsurface.texture->name, j, rsurface.modelsurfaces[j].texture->name);
+ Sys_Abort("RSurf_DrawBatch: index %i uses different texture (%s) than surface %i which it belongs to (which uses %s)\n", c, rsurface.texture->name, j, rsurface.modelsurfaces[j].texture->name);
break;
}
}
static void R_UploadPartialTexture(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
{
if (data == NULL)
- Sys_Error("R_UploadPartialTexture \"%s\": partial update with NULL pixels", glt->identifier);
+ Sys_Abort("R_UploadPartialTexture \"%s\": partial update with NULL pixels", glt->identifier);
if (glt->texturetype != GLTEXTURETYPE_2D)
- Sys_Error("R_UploadPartialTexture \"%s\": partial update of type other than 2D", glt->identifier);
+ Sys_Abort("R_UploadPartialTexture \"%s\": partial update of type other than 2D", glt->identifier);
if (glt->textype->textype == TEXTYPE_PALETTE)
- Sys_Error("R_UploadPartialTexture \"%s\": partial update of paletted texture", glt->identifier);
+ Sys_Abort("R_UploadPartialTexture \"%s\": partial update of paletted texture", glt->identifier);
if (glt->flags & (TEXF_MIPMAP | TEXF_PICMIP))
- Sys_Error("R_UploadPartialTexture \"%s\": partial update not supported with MIPMAP or PICMIP flags", glt->identifier);
+ Sys_Abort("R_UploadPartialTexture \"%s\": partial update not supported with MIPMAP or PICMIP flags", glt->identifier);
if (glt->inputwidth != glt->tilewidth || glt->inputheight != glt->tileheight || glt->tiledepth != 1)
- Sys_Error("R_UploadPartialTexture \"%s\": partial update not supported with stretched or special textures", glt->identifier);
+ Sys_Abort("R_UploadPartialTexture \"%s\": partial update not supported with stretched or special textures", glt->identifier);
// update a portion of the image
// error out if a stretch is needed on special texture types
if (glt->texturetype != GLTEXTURETYPE_2D && (glt->tilewidth != glt->inputwidth || glt->tileheight != glt->inputheight || glt->tiledepth != glt->inputdepth))
- Sys_Error("R_UploadFullTexture \"%s\": stretch uploads allowed only on 2D textures\n", glt->identifier);
+ Sys_Abort("R_UploadFullTexture \"%s\": stretch uploads allowed only on 2D textures\n", glt->identifier);
// when picmip or maxsize is applied, we scale up to a power of 2 multiple
// of the target size and then use the mipmap reduction function to get
flags |= TEXF_ALPHA;
break;
default:
- Sys_Error("R_LoadTexture: unknown texture type");
+ Sys_Abort("R_LoadTexture: unknown texture type");
}
texinfo2 = R_GetTexTypeInfo(textype, flags);
Con_Printf(CON_ERROR "Host_Error: %s\n", hosterrorstring1);
// LadyHavoc: if crashing very early, or currently shutting down, do
- // Sys_Error instead
+ // Sys_Abort instead
if (host.framecount < 3 || host.state == host_shutdown)
- Sys_Error ("Host_Error: %s", hosterrorstring1);
+ Sys_Abort ("Host_Error during %s: %s", host.framecount < 3 ? "startup" : "shutdown", hosterrorstring1);
if (hosterror)
- Sys_Error ("Host_Error: recursively entered (original error was: %s new error is: %s)", hosterrorstring2, hosterrorstring1);
+ Sys_Abort ("Host_Error: recursively entered (original error was: %s new error is: %s)", hosterrorstring2, hosterrorstring1);
hosterror = true;
dp_strlcpy(hosterrorstring2, hosterrorstring1, sizeof(hosterrorstring2));
host.hook.SV_Shutdown();
if (cls.state == ca_dedicated)
- Sys_Error ("Host_Error: %s",hosterrorstring2); // dedicated servers exit
+ Sys_Abort ("Host_Error: %s",hosterrorstring2); // dedicated servers exit
// prevent an endless loop if the error was triggered by a command
Cbuf_Clear(cmd_local->cbuf);
+ // DP8 TODO: send a disconnect message indicating we errored out, see Sys_Abort() and Sys_HandleCrash()
CL_Disconnect();
cls.demonum = -1;
}
else
{
- Sys_Error("session lock %s could not be acquired. Please run with -sessionid and an unique session name.\n", p);
+ Sys_Abort("session lock %s could not be acquired. Please run with -sessionid and an unique session name.\n", p);
}
}
}
host.state = host_init;
if (setjmp(host.abortframe)) // Huh?!
- Sys_Error("Engine initialization failed. Check the console (if available) for additional information.\n");
+ Sys_Abort("Engine initialization failed. Check the console (if available) for additional information.\n");
if (Sys_CheckParm("-profilegameonly"))
Sys_AllowProfiling(false);
===============
Host_Shutdown
-FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
+FIXME: this is a callback from Sys_Quit(). It would be better
to run quit through here before the final handoff to the sys code.
===============
*/
host_shutdown,
host_init,
host_loading,
- host_active
+ host_active,
+ host_failed ///< crashed or aborted, SDL dialog open
} host_state_t;
typedef struct host_static_s
// Only sections? There may be a problem somewhere...
if (keys_cursor >= numcommands)
- Sys_Error ("M_Init: The key binding list only contains sections");
+ Sys_Abort ("M_Init: The key binding list only contains sections");
}
}
va_end (argptr);
if (host.framecount < 3)
- Sys_Error("Menu_Error: %s\n", errorstring);
+ Sys_Abort("Menu_Error: %s\n", errorstring);
Con_Printf( "Menu_Error: %s\n", errorstring );
Con_Printf(CON_ERROR "Mod_LoadModel: model \"%s\" is of unknown/unsupported type\n", mod->name);
}
else if (crash)
- // LadyHavoc: Sys_Error was *ANNOYING*
+ // LadyHavoc: Sys_Abort was *ANNOYING*
Con_Printf (CON_ERROR "Mod_LoadModel: %s not found\n", mod->name);
// no fatal errors occurred, so this model is ready to use.
separation = cl_gameplayfix_nudgeoutofsolid_separation.value;
}
else
- Sys_Error("PHYS_NudgeOutOfSolid: cannot be called from %s VM\n", prog->name);
+ Sys_Abort("PHYS_NudgeOutOfSolid: cannot be called from %s VM\n", prog->name);
VectorCopy(PRVM_serveredictvector(ent, mins), stuckmins);
VectorCopy(PRVM_serveredictvector(ent, maxs), stuckmaxs);
else if (prog == CLVM_prog)
CL_LinkEdict(ent);
else
- Sys_Error("PHYS_NudgeOutOfSolid: cannot be called from %s VM\n", prog->name);
+ Sys_Abort("PHYS_NudgeOutOfSolid: cannot be called from %s VM\n", prog->name);
}
}
void PRVM_ShortStackTrace(prvm_prog_t *prog, char *buf, size_t bufsize)
{
- mfunction_t *f;
- int i;
+ mfunction_t *f;
+ int i;
char vabuf[1024];
char *p;
if(prog)
{
- p = buf + max(0, dpsnprintf(buf, bufsize, "(%s) ", prog->name));
+ i = dpsnprintf(buf, bufsize, "(%s) ", prog->name);
+ p = buf + max(0, i);
}
else
{
}
}
if (i >= MAXRENDERMODULES)
- Sys_Error("R_RegisterModule: ran out of renderer module slots (%i)", MAXRENDERMODULES);
+ Sys_Abort("R_RegisterModule: ran out of renderer module slots (%i)", MAXRENDERMODULES);
rendermodule[i].active = 0;
rendermodule[i].name = name;
rendermodule[i].start = start;
factor = snd_renderbuffer->format.width * snd_renderbuffer->format.channels;
if (written % factor != 0)
- Sys_Error ("SndSys_Write: nb of bytes written (%d) isn't aligned to a frame sample!\n",
+ Sys_Abort ("SndSys_Write: nb of bytes written (%d) isn't aligned to a frame sample!\n",
written);
snd_renderbuffer->startframe += written / factor;
factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
if ((unsigned int)len % factor != 0)
- Sys_Error("SDL sound: invalid buffer length passed to Buffer_Callback (%d bytes)\n", len);
+ Sys_Abort("SDL sound: invalid buffer length passed to Buffer_Callback (%d bytes)\n", len);
RequestedFrames = (unsigned int)len / factor;
int i, prepared = false;
if (sv.protocol == PROTOCOL_QUAKEWORLD)
- Sys_Error("SV_SendClientMessages: no quakeworld support\n");
+ Sys_Abort("SV_SendClientMessages: no quakeworld support\n");
SV_FlushBroadcastMessages();
// system IO interface (these are the sys functions that need to be implemented in a new driver atm)
//
-/// an error will cause the entire program to exit
-void Sys_Error (const char *error, ...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN;
+/// Causes the entire program to exit ASAP.
+/// Trailing \n should be omitted.
+void Sys_Abort (const char *error, ...) DP_FUNC_PRINTF(1) DP_FUNC_NORETURN;
/// (may) output text to terminal which launched program
+/// is POSIX async-signal-safe
/// textlen excludes any (optional) \0 terminator
void Sys_Print(const char *text, size_t textlen);
/// used to report failures inside Con_Printf()
qbool sys_supportsdlgetticks = false;
unsigned int Sys_SDL_GetTicks (void)
{
- Sys_Error("Called Sys_SDL_GetTicks on non-SDL target");
+ Sys_Abort("Called Sys_SDL_GetTicks on non-SDL target");
return 0;
}
void Sys_SDL_Delay (unsigned int milliseconds)
{
- Sys_Error("Called Sys_SDL_Delay on non-SDL target");
+ Sys_Abort("Called Sys_SDL_Delay on non-SDL target");
}
SDL_Quit();
}
-// Sys_Error early in startup might screw with automated
+// Sys_Abort early in startup might screw with automated
// workflows or something if we show the dialog by default.
static qbool nocrashdialog = true;
void Sys_SDL_Dialog(const char *title, const char *string)
{
// we don't know which systems we'll want to init, yet...
if (SDL_Init(0) < 0)
- Sys_Error("SDL_Init failed: %s\n", SDL_GetError());
+ Sys_Abort("SDL_Init failed: %s\n", SDL_GetError());
// COMMANDLINEOPTION: sdl: -nocrashdialog disables "Engine Error" crash dialog boxes
if(!Sys_CheckParm("-nocrashdialog"))
double old_benchmark_time = benchmark_time;
benchmark_time += 1;
if(benchmark_time == old_benchmark_time)
- Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
+ Sys_Abort("sys_usenoclockbutbenchmark cannot run any longer, sorry");
return benchmark_time * 0.000001;
}
#if HAVE_QUERYPERFORMANCECOUNTER
}
#else
// fallback for using the SDL timer if no other timer is available
- // this calls Sys_Error() if not linking against SDL
+ // this calls Sys_Abort() if not linking against SDL
return (double) Sys_SDL_GetTicks() / 1000.0;
#endif
}
double old_benchmark_time = benchmark_time;
benchmark_time += microseconds;
if(benchmark_time == old_benchmark_time)
- Sys_Error("sys_usenoclockbutbenchmark cannot run any longer, sorry");
+ Sys_Abort("sys_usenoclockbutbenchmark cannot run any longer, sorry");
return 0;
}
===============================================================================
*/
+// NOTE: use only POSIX async-signal-safe library functions here (see: man signal-safety)
void Sys_Print(const char *text, size_t textlen)
{
#ifdef __ANDROID__
===============================================================================
*/
-void Sys_Error (const char *error, ...)
+void Sys_Abort (const char *error, ...)
{
va_list argptr;
char string[MAX_INPUTLINE];
+ int i;
// set output to blocking stderr
sys.outfd = fileno(stderr);
dpvsnprintf (string, sizeof (string), error, argptr);
va_end (argptr);
- Con_Printf(CON_ERROR "Engine Error: %s\n", string);
+ Con_Printf(CON_ERROR "Engine Abort: %s\n^9%s\n", string, engineversion);
- // don't want a dead window left blocking the OS UI or the crash dialog
- Host_Shutdown();
+ dp_strlcat(string, "\n\n", sizeof(string));
+ dp_strlcat(string, engineversion, sizeof(string));
- Sys_SDL_Dialog("Engine Error", string);
+ // Most shutdown funcs can't be called here as they could error while we error.
+
+ // DP8 TODO: send a disconnect message indicating we aborted, see Host_Error() and Sys_HandleCrash()
+
+ if (cls.demorecording)
+ CL_Stop_f(cmd_local);
+ if (sv.active)
+ {
+ sv.active = false; // make SV_DropClient() skip the QC stuff to avoid recursive errors
+ for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
+ if (host_client->active)
+ SV_DropClient(false, "Server abort!"); // closes demo file
+ }
+ // don't want a dead window left blocking the OS UI or the abort dialog
+ VID_Shutdown();
+ S_StopAllSounds();
+
+ host.state = host_failed; // make Sys_HandleSignal() call exit()
+ Sys_SDL_Dialog("Engine Abort", string);
fflush(stderr);
}
#endif
+
+static const char *Sys_SigDesc(int sig)
+{
+ switch (sig)
+ {
+ // Windows only supports the C99 signals
+ case SIGINT: return "Interrupt";
+ case SIGILL: return "Illegal instruction";
+ case SIGABRT: return "Aborted";
+ case SIGFPE: return "Floating point exception";
+ case SIGSEGV: return "Segmentation fault";
+ case SIGTERM: return "Termination";
+#ifndef WIN32
+ // POSIX has several others worth catching
+ case SIGHUP: return "Hangup";
+ case SIGQUIT: return "Quit";
+ case SIGBUS: return "Bus error (bad memory access)";
+ case SIGPIPE: return "Broken pipe";
+#endif
+ default: return "Yo dawg, we bugged out while bugging out";
+ }
+}
+
/** Halt and try not to catch fire.
* Writing to any file could corrupt it,
* any uneccessary code could crash while we crash.
- * No malloc() (libgcc should be loaded already) or Con_Printf() allowed here.
+ * Try to use only POSIX async-signal-safe library functions here (see: man signal-safety).
*/
static void Sys_HandleCrash(int sig)
{
#include <execinfo.h>
void *stackframes[32];
int framecount = backtrace(stackframes, 32);
+ char **btstrings;
#endif
+ char dialogtext[3072];
+ const char *sigdesc = Sys_SigDesc(sig);
- // Windows doesn't have strsignal()
- const char *sigdesc;
- switch (sig)
- {
-#ifndef WIN32 // or SIGBUS
- case SIGBUS: sigdesc = "Bus error"; break;
-#endif
- case SIGILL: sigdesc = "Illegal instruction"; break;
- case SIGABRT: sigdesc = "Aborted"; break;
- case SIGFPE: sigdesc = "Floating point exception"; break;
- case SIGSEGV: sigdesc = "Segmentation fault"; break;
- default: sigdesc = "Yo dawg, we hit a bug while hitting a bug";
- }
-
- // set output to blocking stderr
- sys.outfd = fileno(stderr);
+ // set output to blocking stderr and print header, backtrace, version
+ sys.outfd = fileno(stderr); // not async-signal-safe :(
#ifndef WIN32
fcntl(sys.outfd, F_SETFL, fcntl(sys.outfd, F_GETFL, 0) & ~O_NONBLOCK);
-#endif
-
- fprintf(stderr, "\n\n\e[1;37;41m Engine Crash: %s (%d) \e[m\n", sigdesc, sig);
-#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+ Sys_Print("\n\n\e[1;37;41m Engine Crash: ", 30);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print(" \e[m\n", 8);
+ #if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
// the first two addresses will be in this function and in signal() in libc
backtrace_symbols_fd(stackframes + 2, framecount - 2, sys.outfd);
+ #endif
+ Sys_Print("\e[1m", 4);
+ Sys_Print(engineversion, strlen(engineversion));
+ Sys_Print("\e[m\n", 4);
+#else // Windows console doesn't support colours
+ Sys_Print("\n\nEngine Crash: ", 16);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print("\n", 1);
+ Sys_Print(engineversion, strlen(engineversion));
+ Sys_Print("\n", 1);
#endif
- fprintf(stderr, "\e[1m%s\e[m\n", engineversion);
- // DP8 TODO: send a disconnect message indicating we crashed, see CL_DisconnectEx()
+ // DP8 TODO: send a disconnect message indicating we crashed, see Sys_Abort() and Host_Error()
// don't want a dead window left blocking the OS UI or the crash dialog
VID_Shutdown();
S_StopAllSounds();
- Sys_SDL_Dialog("Engine Crash", sigdesc);
+ // prepare the dialogtext: signal, backtrace, version
+ // the dp_st* funcs are POSIX async-signal-safe IF we don't trigger their warnings
+ dp_strlcpy(dialogtext, sigdesc, sizeof(dialogtext));
+ dp_strlcat(dialogtext, "\n\n", sizeof(dialogtext));
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
+ btstrings = backtrace_symbols(stackframes + 2, framecount - 2); // calls malloc :(
+ if (btstrings)
+ for (int i = 0; i < framecount - 2; ++i)
+ {
+ dp_strlcat(dialogtext, btstrings[i], sizeof(dialogtext));
+ dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
+ }
+#endif
+ dp_strlcat(dialogtext, "\n", sizeof(dialogtext));
+ dp_strlcat(dialogtext, engineversion, sizeof(dialogtext));
- fflush(stderr);
+ host.state = host_failed; // make Sys_HandleSignal() call _Exit()
+ Sys_SDL_Dialog("Engine Crash", dialogtext);
- exit (sig);
+ fflush(stderr); // not async-signal-safe :(
+ _Exit(sig);
}
static void Sys_HandleSignal(int sig)
{
-#ifdef WIN32
- // Windows users will likely never see this so no point replicating strsignal()
- Con_Printf("\nReceived signal %d, exiting...\n", sig);
-#else
- Con_Printf("\nReceived %s signal (%d), exiting...\n", strsignal(sig), sig);
-#endif
+ const char *sigdesc = Sys_SigDesc(sig);
+ Sys_Print("\nReceived ", 10);
+ Sys_Print(sigdesc, strlen(sigdesc));
+ Sys_Print(" signal, exiting...\n", 20);
+ if (host.state == host_failed)
+ {
+ // user is trying to kill the process while the dialog is open
+ fflush(stderr); // not async-signal-safe :(
+ _Exit(sig);
+ }
host.state = host_shutdown;
}
/// SDL2 only handles SIGINT and SIGTERM by default and doesn't log anything
static void Sys_InitSignals(void)
{
-// Windows docs say its signal() only accepts these ones
+ // Windows only supports the C99 signals
+ signal(SIGINT, Sys_HandleSignal);
+ signal(SIGILL, Sys_HandleCrash);
signal(SIGABRT, Sys_HandleCrash);
signal(SIGFPE, Sys_HandleCrash);
- signal(SIGILL, Sys_HandleCrash);
- signal(SIGINT, Sys_HandleSignal);
signal(SIGSEGV, Sys_HandleCrash);
signal(SIGTERM, Sys_HandleSignal);
#ifndef WIN32
+ // POSIX has several others worth catching
signal(SIGHUP, Sys_HandleSignal);
signal(SIGQUIT, Sys_HandleSignal);
signal(SIGBUS, Sys_HandleCrash);
Cvar_RegisterCallback(&vid_minimize_on_focus_loss, VID_SetHints_c);
if (SDL_Init(SDL_INIT_VIDEO) < 0)
- Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
+ Sys_Abort ("Failed to init SDL video subsystem: %s", SDL_GetError());
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
Con_Printf(CON_ERROR "Failed to init SDL joystick subsystem: %s\n", SDL_GetError());
context = SDL_GL_CreateContext(window);
if (context == NULL)
- Sys_Error("Failed to initialize OpenGL context: %s\n", SDL_GetError());
+ Sys_Abort("Failed to initialize OpenGL context: %s\n", SDL_GetError());
GL_InitFunctions();
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
context = SDL_GL_CreateContext(window);
if (context == NULL)
- Sys_Error("Failed to initialize OpenGL context: %s\n", SDL_GetError());
+ Sys_Abort("Failed to initialize OpenGL context: %s\n", SDL_GetError());
}
#endif
steelstorm_showing_mousecursor = Cvar_FindVar(&cvars_all, "steelstorm_showing_mousecursor", ~0);
if (!SDL_WasInit(SDL_INIT_VIDEO) && SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
- Sys_Error ("Failed to init SDL video subsystem: %s", SDL_GetError());
+ Sys_Abort ("Failed to init SDL video subsystem: %s", SDL_GetError());
Cvar_SetValueQuick(&vid_touchscreen_supportshowkeyboard, SDL_HasScreenKeyboardSupport() ? 1 : 0);
return VID_InitModeGL(mode);
}
if (missingrequiredfuncs)
- Sys_Error("OpenGL driver/hardware lacks required features:\n%s", missingfuncs);
+ Sys_Abort("OpenGL driver/hardware lacks required features:\n%s", missingfuncs);
#endif
}
vid.support.glversion = 10 * majorv + minorv;
if (vid.support.glversion < 32)
// fallback, should never get here: GL context creation should have failed
- Sys_Error("OpenGL driver/hardware supports version %i.%i but 3.2 is the minimum\n", majorv, minorv);
+ Sys_Abort("OpenGL driver/hardware supports version %i.%i but 3.2 is the minimum\n", majorv, minorv);
qglGetIntegerv(GL_NUM_EXTENSIONS, &numextensions);
Con_DPrint("GL_EXTENSIONS:\n");
{
Con_Print("Video mode change failed\n");
if (!VID_Mode(vid.mode.fullscreen, vid.mode.width, vid.mode.height, vid.mode.bitsperpixel, vid.mode.refreshrate, vid.mode.stereobuffer))
- Sys_Error("Unable to restore to last working video mode");
+ Sys_Abort("Unable to restore to last working video mode");
}
R_Modules_Start();
Key_ReleaseAll();
success = VID_Mode(vid_fullscreen.integer, vid_width.integer, vid_height.integer, vid_bitsperpixel.integer, vid_refreshrate.value, vid_stereobuffer.integer);
}
if (!success)
- Sys_Error("Video modes failed");
+ Sys_Abort("Video modes failed");
}
R_Modules_Start();
break;
case 0:
default:
- Sys_Error("what? but above the joint was valid...\n");
+ Sys_Abort("what? but above the joint was valid...\n");
break;
}
#undef SETPARAMS
dMassSetCylinderTotal(&mass, massval, 3, radius, length);
break;
default:
- Sys_Error("World_Physics_BodyFromEntity: unrecognized geomtype value %i was accepted by filter\n", solid);
+ Sys_Abort("World_Physics_BodyFromEntity: unrecognized geomtype value %i was accepted by filter\n", solid);
// this goto only exists to prevent warnings from the compiler
// about uninitialized variables (mass), while allowing it to
// catch legitimate uninitialized variable warnings
return NULL;
}
if (clump->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel1))
- Sys_Error("Clump_AllocBlock: trashed sentinel1\n");
+ Sys_Abort("Clump_AllocBlock: trashed sentinel1\n");
if (clump->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel2))
- Sys_Error("Clump_AllocBlock: trashed sentinel2\n");
+ Sys_Abort("Clump_AllocBlock: trashed sentinel2\n");
startbit = 0;
endbit = startbit + needbits;
array = clump->bits;
// TODO: optimize
for (bit = startbit;bit < endbit;bit++)
if (clump->bits[bit>>5] & (1<<(bit & 31)))
- Sys_Error("Clump_AllocBlock: internal error (%i needbits)\n", needbits);
+ Sys_Abort("Clump_AllocBlock: internal error (%i needbits)\n", needbits);
for (bit = startbit;bit < endbit;bit++)
clump->bits[bit>>5] |= (1<<(bit & 31));
clump->blocksinuse += needbits;
if (start >= clump->block && start < clump->block + MEMCLUMPSIZE)
{
if (clump->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel1))
- Sys_Error("Clump_FreeBlock: trashed sentinel1\n");
+ Sys_Abort("Clump_FreeBlock: trashed sentinel1\n");
if (clump->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel2))
- Sys_Error("Clump_FreeBlock: trashed sentinel2\n");
+ Sys_Abort("Clump_FreeBlock: trashed sentinel2\n");
if (start + size > clump->block + MEMCLUMPSIZE)
- Sys_Error("Clump_FreeBlock: block overrun\n");
+ Sys_Abort("Clump_FreeBlock: block overrun\n");
// the block belongs to this clump, clear the range
needbits = (size + MEMUNIT - 1) / MEMUNIT;
startbit = (start - clump->block) / MEMUNIT;
// first verify all bits are set, otherwise this may be misaligned or a double free
for (bit = startbit;bit < endbit;bit++)
if ((clump->bits[bit>>5] & (1<<(bit & 31))) == 0)
- Sys_Error("Clump_FreeBlock: double free\n");
+ Sys_Abort("Clump_FreeBlock: double free\n");
for (bit = startbit;bit < endbit;bit++)
clump->bits[bit>>5] &= ~(1<<(bit & 31));
clump->blocksinuse -= needbits;
if(olddata)
pool = ((memheader_t *)((unsigned char *) olddata - sizeof(memheader_t)))->pool;
else
- Sys_Error("Mem_Alloc: pool == NULL (alloc at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_Alloc: pool == NULL (alloc at %s:%i)", filename, fileline);
}
if (mem_mutex)
Thread_LockMutex(mem_mutex);
Mem_PrintStats();
Mem_PrintList(1<<30);
Mem_PrintStats();
- Sys_Error("Mem_Alloc: out of memory (alloc of size %f (%.3fMB) at %s:%i)", (double)realsize, (double)realsize / (1 << 20), filename, fileline);
+ Sys_Abort("Mem_Alloc: out of memory (alloc of size %f (%.3fMB) at %s:%i)", (double)realsize, (double)realsize / (1 << 20), filename, fileline);
}
// calculate address that aligns the end of the memheader_t to the specified alignment
mem = (memheader_t*)((((size_t)base + sizeof(memheader_t) + (alignment-1)) & ~(alignment-1)) - sizeof(memheader_t));
sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&mem->sentinel);
sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS((unsigned char *) mem + sizeof(memheader_t) + mem->size);
if (mem->sentinel != sentinel1)
- Sys_Error("Mem_Free: trashed head sentinel (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline);
+ Sys_Abort("Mem_Free: trashed head sentinel (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline);
if (memcmp((unsigned char *) mem + sizeof(memheader_t) + mem->size, &sentinel2, sizeof(sentinel2)))
- Sys_Error("Mem_Free: trashed tail sentinel (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline);
+ Sys_Abort("Mem_Free: trashed tail sentinel (alloc at %s:%i, free at %s:%i)", mem->filename, mem->fileline, filename, fileline);
pool = mem->pool;
if (developer_memory.integer)
Con_DPrintf("Mem_Free: pool %s, alloc %s:%i, free %s:%i, size %i bytes\n", pool->name, mem->filename, mem->fileline, filename, fileline, (int)(mem->size));
// unlink memheader from doubly linked list
if (mem->list.prev->next != &mem->list || mem->list.next->prev != &mem->list)
- Sys_Error("Mem_Free: not allocated or double freed (free at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_Free: not allocated or double freed (free at %s:%i)", filename, fileline);
if (mem_mutex)
Thread_LockMutex(mem_mutex);
List_Delete(&mem->list);
{
//_Mem_CheckSentinelsGlobal(filename, fileline);
if (!Mem_IsAllocated(NULL, data))
- Sys_Error("Mem_Free: data is not allocated (called at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_Free: data is not allocated (called at %s:%i)", filename, fileline);
}
_Mem_FreeBlock((memheader_t *)((unsigned char *) data - sizeof(memheader_t)), filename, fileline);
Mem_PrintStats();
Mem_PrintList(1<<30);
Mem_PrintStats();
- Sys_Error("Mem_AllocPool: out of memory (allocpool at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_AllocPool: out of memory (allocpool at %s:%i)", filename, fileline);
}
memset(pool, 0, sizeof(mempool_t));
pool->sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1);
// unlink pool from chain
for (chainaddress = &poolchain;*chainaddress && *chainaddress != pool;chainaddress = &((*chainaddress)->next));
if (*chainaddress != pool)
- Sys_Error("Mem_FreePool: pool already free (freepool at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_FreePool: pool already free (freepool at %s:%i)", filename, fileline);
if (pool->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1))
- Sys_Error("Mem_FreePool: trashed pool sentinel 1 (allocpool at %s:%i, freepool at %s:%i)", pool->filename, pool->fileline, filename, fileline);
+ Sys_Abort("Mem_FreePool: trashed pool sentinel 1 (allocpool at %s:%i, freepool at %s:%i)", pool->filename, pool->fileline, filename, fileline);
if (pool->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2))
- Sys_Error("Mem_FreePool: trashed pool sentinel 2 (allocpool at %s:%i, freepool at %s:%i)", pool->filename, pool->fileline, filename, fileline);
+ Sys_Abort("Mem_FreePool: trashed pool sentinel 2 (allocpool at %s:%i, freepool at %s:%i)", pool->filename, pool->fileline, filename, fileline);
*chainaddress = pool->next;
// free memory owned by the pool
if (chainaddress == pool)
break;
if (!chainaddress)
- Sys_Error("Mem_EmptyPool: pool is already free (emptypool at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_EmptyPool: pool is already free (emptypool at %s:%i)", filename, fileline);
}
if (pool == NULL)
- Sys_Error("Mem_EmptyPool: pool == NULL (emptypool at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_EmptyPool: pool == NULL (emptypool at %s:%i)", filename, fileline);
if (pool->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1))
- Sys_Error("Mem_EmptyPool: trashed pool sentinel 1 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline);
+ Sys_Abort("Mem_EmptyPool: trashed pool sentinel 1 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline);
if (pool->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2))
- Sys_Error("Mem_EmptyPool: trashed pool sentinel 2 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline);
+ Sys_Abort("Mem_EmptyPool: trashed pool sentinel 2 (allocpool at %s:%i, emptypool at %s:%i)", pool->filename, pool->fileline, filename, fileline);
// free memory owned by the pool
while (!List_Is_Empty(&pool->chain))
unsigned int sentinel2;
if (data == NULL)
- Sys_Error("Mem_CheckSentinels: data == NULL (sentinel check at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_CheckSentinels: data == NULL (sentinel check at %s:%i)", filename, fileline);
mem = (memheader_t *)((unsigned char *) data - sizeof(memheader_t));
sentinel1 = MEMHEADER_SENTINEL_FOR_ADDRESS(&mem->sentinel);
sentinel2 = MEMHEADER_SENTINEL_FOR_ADDRESS((unsigned char *) mem + sizeof(memheader_t) + mem->size);
if (mem->sentinel != sentinel1)
- Sys_Error("Mem_Free: trashed head sentinel (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline);
+ Sys_Abort("Mem_Free: trashed head sentinel (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline);
if (memcmp((unsigned char *) mem + sizeof(memheader_t) + mem->size, &sentinel2, sizeof(sentinel2)))
- Sys_Error("Mem_Free: trashed tail sentinel (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline);
+ Sys_Abort("Mem_Free: trashed tail sentinel (alloc at %s:%i, sentinel check at %s:%i)", mem->filename, mem->fileline, filename, fileline);
}
#if MEMCLUMPING
{
// this isn't really very useful
if (clump->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel1))
- Sys_Error("Mem_CheckClumpSentinels: trashed sentinel 1 (sentinel check at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_CheckClumpSentinels: trashed sentinel 1 (sentinel check at %s:%i)", filename, fileline);
if (clump->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&clump->sentinel2))
- Sys_Error("Mem_CheckClumpSentinels: trashed sentinel 2 (sentinel check at %s:%i)", filename, fileline);
+ Sys_Abort("Mem_CheckClumpSentinels: trashed sentinel 2 (sentinel check at %s:%i)", filename, fileline);
}
#endif
for (pool = poolchain;pool;pool = pool->next)
{
if (pool->sentinel1 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel1))
- Sys_Error("Mem_CheckSentinelsGlobal: trashed pool sentinel 1 (allocpool at %s:%i, sentinel check at %s:%i)", pool->filename, pool->fileline, filename, fileline);
+ Sys_Abort("Mem_CheckSentinelsGlobal: trashed pool sentinel 1 (allocpool at %s:%i, sentinel check at %s:%i)", pool->filename, pool->fileline, filename, fileline);
if (pool->sentinel2 != MEMHEADER_SENTINEL_FOR_ADDRESS(&pool->sentinel2))
- Sys_Error("Mem_CheckSentinelsGlobal: trashed pool sentinel 2 (allocpool at %s:%i, sentinel check at %s:%i)", pool->filename, pool->fileline, filename, fileline);
+ Sys_Abort("Mem_CheckSentinelsGlobal: trashed pool sentinel 2 (allocpool at %s:%i, sentinel check at %s:%i)", pool->filename, pool->fileline, filename, fileline);
}
for (pool = poolchain;pool;pool = pool->next)
List_For_Each_Entry(mem, &pool->chain, memheader_t, list)
{
j = (p - l->arrays[i].data) / l->recordsize;
if (p != l->arrays[i].data + j * l->recordsize)
- Sys_Error("Mem_ExpandableArray_FreeRecord: no such record %p\n", (void *)p);
+ Sys_Abort("Mem_ExpandableArray_FreeRecord: no such record %p\n", (void *)p);
if (!l->arrays[i].allocflags[j])
- Sys_Error("Mem_ExpandableArray_FreeRecord: record %p is already free!\n", (void *)p);
+ Sys_Abort("Mem_ExpandableArray_FreeRecord: record %p is already free!\n", (void *)p);
l->arrays[i].allocflags[j] = false;
l->arrays[i].numflaggedrecords--;
return;