]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
sys: improve error and crash handling
authorbones_was_here <bones_was_here@xonotic.au>
Sun, 21 Jan 2024 21:52:26 +0000 (07:52 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Sun, 28 Jan 2024 03:24:47 +0000 (13:24 +1000)
Renames Sys_Error to Sys_Abort (now using the Engine Abort message) for
clarity (as distinct from the non-fatal Host_Error).

Includes the engine version in the Crash and Abort SDL dialogs.

Linux: includes the glibc backtrace in the Engine Crash SDL dialog.
Fixes CTRL+C not working while the SDL dialog is open.

Fixes a double Sys_Error/Sys_Abort when memory corruption was detected.

Windows: omits colours when printing a Crash to stderr (not supported).

Makes signal handling (mostly) POSIX async-signal-safe.

Fixes a slight bug in PRVM_ShortStackTrace().

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
28 files changed:
cap_avi.c
cap_ogg.c
cl_demo.c
cl_main.c
cl_particles.c
common.h
gl_backend.c
gl_rmain.c
gl_textures.c
host.c
host.h
menu.c
model_shared.c
phys.c
prvm_cmds.c
prvm_exec.c
r_modules.c
snd_oss.c
snd_sdl.c
sv_send.c
sys.h
sys_null.c
sys_sdl.c
sys_shared.c
vid_sdl.c
vid_shared.c
world.c
zone.c

index ea0e35445a4be10f3959c012cd9955f7eb2f11eb..136a9ec24471358beed5599ca5b31b397d147ec5 100644 (file)
--- a/cap_avi.c
+++ b/cap_avi.c
@@ -188,10 +188,10 @@ static void SCR_CaptureVideo_RIFF_IndexEntry(const char *chunkfourcc, int chunks
 {
        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);
@@ -209,7 +209,7 @@ static void SCR_CaptureVideo_RIFF_MakeIxChunk(const char *fcc, const char *dwChu
        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;
@@ -312,7 +312,7 @@ static void SCR_CaptureVideo_RIFF_OverflowCheck(int framesize)
        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;
@@ -707,7 +707,7 @@ void SCR_CaptureVideo_Avi_BeginVideo(void)
                // 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)
                {
index e0f85994c661605f6791bde5e1f43ccf2c5c0ea7..d6f4e6dba49d40146ba350bdbeffafefe4db5fd1 100644 (file)
--- a/cap_ogg.c
+++ b/cap_ogg.c
@@ -660,7 +660,7 @@ static void SCR_CaptureVideo_Ogg_Interleave(void)
                                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);
                        }
@@ -670,7 +670,7 @@ static void SCR_CaptureVideo_Ogg_Interleave(void)
                                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);
                        }
@@ -765,7 +765,7 @@ static void SCR_CaptureVideo_Ogg_EndVideo(void)
        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);
@@ -777,7 +777,7 @@ static void SCR_CaptureVideo_Ogg_EndVideo(void)
                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);
@@ -1082,7 +1082,7 @@ void SCR_CaptureVideo_Ogg_BeginVideo(void)
                {
                        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);
@@ -1094,7 +1094,7 @@ void SCR_CaptureVideo_Ogg_BeginVideo(void)
                {
                        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);
index a11d63b5ba33f5939dc7de352f4940d2e1b85030..cd884e5a6002834aa7ce6af005d33e7cfaf230ff 100644 (file)
--- a/cl_demo.c
+++ b/cl_demo.c
@@ -145,7 +145,7 @@ void CL_CutDemo (unsigned char **buf, fs_offset_t *filesize)
        // 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);
 }
 
index 6a69648eb0a54de5e3916c7388b1dfc7b951e05a..cba1b7560298eed2df02b2cdc9815b64dd3de0c8 100644 (file)
--- a/cl_main.c
+++ b/cl_main.c
@@ -302,7 +302,7 @@ void CL_ExpandEntities(int num)
        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;
index 31abace8711e9e2dfbd2bfe20b37460c12519785..1757b80eaccea2fca2923a9cd9f86c385b17d731 100644 (file)
@@ -2025,7 +2025,7 @@ static void setuptex(int texnum, unsigned char *data, unsigned char *particletex
        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);
 }
index 200e432b0ac8fdc425ea0690af00423f2a45bef9..1414e633eb938a2a8ae436e0c7fe1c03492673ea 100644 (file)
--- a/common.h
+++ b/common.h
@@ -47,7 +47,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 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;
index b5336a226223f62d852b5760a5b8e534117c8546..096147429da5eace2afd4f49fea5d8898a2fdfcb 100644 (file)
@@ -108,7 +108,7 @@ static void GLAPIENTRY GL_DebugOutputCallback(GLenum source, GLenum type, GLuint
 }
 #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);
 
@@ -1978,7 +1978,7 @@ void R_Mesh_TexCoordPointer(unsigned int unitnum, int components, int gltype, si
 {
        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
@@ -2027,7 +2027,7 @@ int R_Mesh_TexBound(unsigned int unitnum, int id)
 {
        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)
@@ -2068,7 +2068,7 @@ void R_Mesh_TexBind(unsigned int unitnum, rtexture_t *tex)
        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:
index ec88c18736f00ecda75e1cc3087de9a2d9cd78ac..cb77104b063eacf27d2667955ecdd8fbe0c683b6 100644 (file)
@@ -3733,7 +3733,7 @@ r_meshbuffer_t *R_BufferData_Store(size_t datasize, const void *data, r_bufferda
 
        // 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;
@@ -8549,7 +8549,7 @@ void RSurf_DrawBatch(void)
                                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;
                                }
                        }
index 7065432e8a4142c4de7818759949a33b475b08cf..b1cc74a114cc9395727c7dea64be377d8ac05114 100644 (file)
@@ -928,19 +928,19 @@ static void GL_SetupTextureParameters(int flags, textype_t textype, int texturet
 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
 
@@ -971,7 +971,7 @@ static void R_UploadFullTexture(gltexture_t *glt, const unsigned char *data)
 
        // 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
@@ -1280,7 +1280,7 @@ static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *iden
                flags |= TEXF_ALPHA;
                break;
        default:
-               Sys_Error("R_LoadTexture: unknown texture type");
+               Sys_Abort("R_LoadTexture: unknown texture type");
        }
 
        texinfo2 = R_GetTexTypeInfo(textype, flags);
diff --git a/host.c b/host.c
index d291799ce7f5b9f3bafd91a60f248a5f62a601b3..bb1b83fcf82ea4c87d1cb3dbd401ff07ba8066df 100644 (file)
--- a/host.c
+++ b/host.c
@@ -105,12 +105,12 @@ void Host_Error (const char *error, ...)
        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));
@@ -135,11 +135,12 @@ void Host_Error (const char *error, ...)
                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;
 
@@ -344,7 +345,7 @@ void Host_LockSession(void)
                        }
                        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);
                        }
                }
        }
@@ -389,7 +390,7 @@ static void Host_Init (void)
        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);
@@ -573,7 +574,7 @@ static void Host_Init (void)
 ===============
 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.
 ===============
 */
diff --git a/host.h b/host.h
index 61182b5242c1140449824f6488854c03f3c4a780..78bd8083f5ffaacc1ed1d898505559688c6209d1 100644 (file)
--- a/host.h
+++ b/host.h
@@ -23,7 +23,8 @@ typedef enum host_state_e
        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
diff --git a/menu.c b/menu.c
index f2d359839af9372f405e72b32e89c690f506a1bb..e26e74d71f604cbce2fdfb7499843e14d315f581 100644 (file)
--- a/menu.c
+++ b/menu.c
@@ -2573,7 +2573,7 @@ void M_Menu_Keys_f(cmd_state_t *cmd)
 
                // 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");
        }
 }
 
@@ -5221,7 +5221,7 @@ void MVM_error_cmd(const char *format, ...)
        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 );
 
index 11b0d61c8b124fa5d801cabecb28c0d9daac3921..fd181f355685198e8b6aea6815380855d2606bce 100644 (file)
@@ -550,7 +550,7 @@ model_t *Mod_LoadModel(model_t *mod, qbool crash, qbool checkdisk)
                        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.
diff --git a/phys.c b/phys.c
index 7128ea3ddc9a440dc42b88bd581ebd0dcd824c9a..04c5449d2a216131268dd9530ff4e5233a5a0af8 100644 (file)
--- a/phys.c
+++ b/phys.c
@@ -26,7 +26,7 @@ int PHYS_NudgeOutOfSolid(prvm_prog_t *prog, prvm_edict_t *ent)
                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);
index aefac6720752672d703bcb45b87a28f6fa3c2ebf..6bc211c66cacfd5bc3ca5820864dde82dafabaab 100644 (file)
@@ -5687,7 +5687,7 @@ void VM_nudgeoutofsolid(prvm_prog_t *prog)
                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);
        }
 }
 
index ee738b9c3cbc0254d27ba7b199bae9676b1df96f..4c50ff6ecd160493b7c8828703ea7617c50f38ab 100644 (file)
@@ -431,14 +431,15 @@ void PRVM_StackTrace (prvm_prog_t *prog)
 
 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
        {
index dd76b6cae9d4be1fcf74b17d74e09f2e85a0838e..c6c0419187d574c01a7a8685d4a785cf96757954 100644 (file)
@@ -36,7 +36,7 @@ void R_RegisterModule(const char *name, void(*start)(void), void(*shutdown)(void
                }
        }
        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;
index 05f91c92ff50ded803d3f5d9712294a692eece33..ecf18333573b37f90ef8721b06309ffd143f66dc 100644 (file)
--- a/snd_oss.c
+++ b/snd_oss.c
@@ -222,7 +222,7 @@ static int SndSys_Write (const unsigned char* buffer, unsigned int nb_bytes)
 
        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;
index e93d302113f31e5cb509faa5f5824c584ae21a1f..f3479ba942d9c7c58900ddecebbe7d2e2e8eba59 100644 (file)
--- a/snd_sdl.c
+++ b/snd_sdl.c
@@ -37,7 +37,7 @@ static void Buffer_Callback (void *userdata, Uint8 *stream, int len)
 
        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;
 
index a8a44347724791acfc0f179655e2c010ae2dd65a..68856c1ae9beb38534ec49434d2c2bbe9e3aeaf3 100644 (file)
--- a/sv_send.c
+++ b/sv_send.c
@@ -1697,7 +1697,7 @@ void SV_SendClientMessages(void)
        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();
 
diff --git a/sys.h b/sys.h
index 2255a6a3064502f597341232da317e9eb089d470..baa7146bc292a295dd8e9c38fee639afd021a8e1 100644 (file)
--- a/sys.h
+++ b/sys.h
@@ -204,10 +204,12 @@ char *Sys_TimeString(const char *timeformat);
 // 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()
index e2b9b0a0876e18834dffbb7bc545d52f98cac41b..3b1f356dac79051ee857fd687abb8951ad53cd57 100644 (file)
@@ -26,10 +26,10 @@ void Sys_SDL_Init(void)
 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");
 }
index f96bc87eabfc7aa5dc15177c0e9f3b6f77477a39..c811cdd9cc2dbd5d25629bd316aae15269b3a785 100644 (file)
--- a/sys_sdl.c
+++ b/sys_sdl.c
@@ -23,7 +23,7 @@ void Sys_SDL_Shutdown(void)
        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)
@@ -53,7 +53,7 @@ void Sys_SDL_Init(void)
 {
        // 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"))
index 26bd27c25e440a57bef9fe50d3632c72bedddd0b..6b5eef4343cce9360a15676626ca2671932fe173 100644 (file)
@@ -413,7 +413,7 @@ double Sys_DirtyTime(void)
                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
@@ -494,7 +494,7 @@ double Sys_DirtyTime(void)
        }
 #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
 }
@@ -523,7 +523,7 @@ double Sys_Sleep(double time)
                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;
        }
 
@@ -596,6 +596,7 @@ STDIO
 ===============================================================================
 */
 
+// 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__
@@ -704,10 +705,11 @@ Startup and Shutdown
 ===============================================================================
 */
 
-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);
@@ -719,12 +721,30 @@ void Sys_Error (const char *error, ...)
        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);
 
@@ -925,10 +945,33 @@ void Sys_MakeProcessMean (void)
 }
 #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)
 {
@@ -937,70 +980,89 @@ 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);
index 88ec122efb485eaabc925f6daa14e2f7db59461f..bcb2d139d7506df158665925a6a4638f229b60a1 100644 (file)
--- a/vid_sdl.c
+++ b/vid_sdl.c
@@ -1529,7 +1529,7 @@ void VID_Init (void)
        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());
 
@@ -1774,7 +1774,7 @@ static qbool VID_InitModeGL(viddef_mode_t *mode)
 
        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();
 
@@ -1789,7 +1789,7 @@ static qbool VID_InitModeGL(viddef_mode_t *mode)
                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
 
@@ -1827,7 +1827,7 @@ qbool VID_InitMode(viddef_mode_t *mode)
        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);
index a3d86d3ec060f0f55202662ef4712ac304584fbf..44bc92074495d88116b063a4a5e60a141704acd9 100644 (file)
@@ -706,7 +706,7 @@ void GL_InitFunctions(void)
        }
 
        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
 }
 
@@ -731,7 +731,7 @@ void GL_Setup(void)
        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");
@@ -1485,7 +1485,7 @@ void VID_Restart_f(cmd_state_t *cmd)
        {
                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();
@@ -1560,7 +1560,7 @@ void VID_Start(void)
                        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();
diff --git a/world.c b/world.c
index a95330fae99a4ba0ed9dc3d138d7038a6dcb9b78..fb70ab02766f01fd3c795672139f1159e385c6b0 100644 (file)
--- a/world.c
+++ b/world.c
@@ -2063,7 +2063,7 @@ static void World_Physics_Frame_JointFromEntity(world_t *world, prvm_edict_t *ed
                                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
@@ -2624,7 +2624,7 @@ treatasbox:
                        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
diff --git a/zone.c b/zone.c
index 1035eb96f264a6dd93909bae6d5a008f3585db7e..81525e4ab65dc1d4b4079663755b939653b655ec 100644 (file)
--- a/zone.c
+++ b/zone.c
@@ -230,9 +230,9 @@ static void *Clump_AllocBlock(size_t size)
                                        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;
@@ -288,7 +288,7 @@ foundblock:
                        // 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;
@@ -328,11 +328,11 @@ static void Clump_FreeBlock(void *base, size_t size)
                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;
@@ -340,7 +340,7 @@ static void Clump_FreeBlock(void *base, size_t size)
                        // 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;
@@ -388,7 +388,7 @@ void *_Mem_Alloc(mempool_t *pool, void *olddata, size_t size, size_t alignment,
                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);
@@ -406,7 +406,7 @@ void *_Mem_Alloc(mempool_t *pool, void *olddata, size_t size, size_t alignment,
                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));
@@ -456,16 +456,16 @@ static void _Mem_FreeBlock(memheader_t *mem, const char *filename, int fileline)
        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);
@@ -491,7 +491,7 @@ void _Mem_Free(void *data, const char *filename, int fileline)
        {
                //_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);
@@ -509,7 +509,7 @@ mempool_t *_Mem_AllocPool(const char *name, int flags, mempool_t *parent, const
                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);
@@ -539,11 +539,11 @@ void _Mem_FreePool(mempool_t **poolpointer, const char *filename, int fileline)
                // 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
@@ -576,14 +576,14 @@ void _Mem_EmptyPool(mempool_t *pool, const char *filename, int fileline)
                        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))
@@ -603,15 +603,15 @@ void _Mem_CheckSentinels(void *data, const char *filename, int fileline)
        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
@@ -619,9 +619,9 @@ static void _Mem_CheckClumpSentinels(memclump_t *clump, const char *filename, in
 {
        // 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
 
@@ -635,9 +635,9 @@ void _Mem_CheckSentinelsGlobal(const char *filename, int fileline)
        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)
@@ -750,9 +750,9 @@ void Mem_ExpandableArray_FreeRecord(memexpandablearray_t *l, void *record) // co
                {
                        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;