mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2024-11-23 04:12:42 +00:00
improved crash and exit handlers that restore system settings (monitor gamma ramps, system timer resolution, terminal settings, ...) and crash reports with full QVM stack trace
This commit is contained in:
parent
d945904298
commit
1efd5b6d82
23 changed files with 1414 additions and 122 deletions
|
@ -22,7 +22,7 @@ ifeq ($(config),debug_x32)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -x c++
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../../.bin/debug_x32/libbotlib.a -ldl -lm
|
||||
LIBS += ../../../.bin/debug_x32/libbotlib.a -ldl -lm -lbacktrace
|
||||
LDDEPS += ../../../.bin/debug_x32/libbotlib.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../../.bin/debug_x32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -53,7 +53,7 @@ ifeq ($(config),debug_x64)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -x c++
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../../.bin/debug_x64/libbotlib.a -ldl -lm
|
||||
LIBS += ../../../.bin/debug_x64/libbotlib.a -ldl -lm -lbacktrace
|
||||
LDDEPS += ../../../.bin/debug_x64/libbotlib.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../../.bin/debug_x64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -84,7 +84,7 @@ ifeq ($(config),release_x32)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -x c++
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../../.bin/release_x32/libbotlib.a -ldl -lm
|
||||
LIBS += ../../../.bin/release_x32/libbotlib.a -ldl -lm -lbacktrace
|
||||
LDDEPS += ../../../.bin/release_x32/libbotlib.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../../.bin/release_x32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -115,7 +115,7 @@ ifeq ($(config),release_x64)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -x c++
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../../.bin/release_x64/libbotlib.a -ldl -lm
|
||||
LIBS += ../../../.bin/release_x64/libbotlib.a -ldl -lm -lbacktrace
|
||||
LDDEPS += ../../../.bin/release_x64/libbotlib.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../../.bin/release_x64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -142,10 +142,12 @@ OBJECTS := \
|
|||
$(OBJDIR)/cm_trace.o \
|
||||
$(OBJDIR)/cmd.o \
|
||||
$(OBJDIR)/common.o \
|
||||
$(OBJDIR)/crash.o \
|
||||
$(OBJDIR)/cvar.o \
|
||||
$(OBJDIR)/files.o \
|
||||
$(OBJDIR)/huffman.o \
|
||||
$(OBJDIR)/huffman_static.o \
|
||||
$(OBJDIR)/json.o \
|
||||
$(OBJDIR)/md4.o \
|
||||
$(OBJDIR)/md5.o \
|
||||
$(OBJDIR)/msg.o \
|
||||
|
@ -247,6 +249,9 @@ $(OBJDIR)/cmd.o: ../../code/qcommon/cmd.cpp
|
|||
$(OBJDIR)/common.o: ../../code/qcommon/common.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/crash.o: ../../code/qcommon/crash.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/cvar.o: ../../code/qcommon/cvar.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
@ -259,6 +264,9 @@ $(OBJDIR)/huffman.o: ../../code/qcommon/huffman.cpp
|
|||
$(OBJDIR)/huffman_static.o: ../../code/qcommon/huffman_static.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/json.o: ../../code/qcommon/json.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/md4.o: ../../code/qcommon/md4.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
|
|
@ -22,7 +22,7 @@ ifeq ($(config),debug_x32)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -g -Wno-unused-parameter -Wno-write-strings -pthread -x c++
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../../.bin/debug_x32/libbotlib.a ../../../.bin/debug_x32/librenderer.a ../../../.bin/debug_x32/libfreetype.a ../../../.bin/debug_x32/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
|
||||
LIBS += ../../../.bin/debug_x32/libbotlib.a ../../../.bin/debug_x32/librenderer.a ../../../.bin/debug_x32/libfreetype.a ../../../.bin/debug_x32/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
|
||||
LDDEPS += ../../../.bin/debug_x32/libbotlib.a ../../../.bin/debug_x32/librenderer.a ../../../.bin/debug_x32/libfreetype.a ../../../.bin/debug_x32/liblibjpeg-turbo.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../../.bin/debug_x32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -53,7 +53,7 @@ ifeq ($(config),debug_x64)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -g -Wno-unused-parameter -Wno-write-strings -pthread -x c++
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../../.bin/debug_x64/libbotlib.a ../../../.bin/debug_x64/librenderer.a ../../../.bin/debug_x64/libfreetype.a ../../../.bin/debug_x64/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
|
||||
LIBS += ../../../.bin/debug_x64/libbotlib.a ../../../.bin/debug_x64/librenderer.a ../../../.bin/debug_x64/libfreetype.a ../../../.bin/debug_x64/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
|
||||
LDDEPS += ../../../.bin/debug_x64/libbotlib.a ../../../.bin/debug_x64/librenderer.a ../../../.bin/debug_x64/libfreetype.a ../../../.bin/debug_x64/liblibjpeg-turbo.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../../.bin/debug_x64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -84,7 +84,7 @@ ifeq ($(config),release_x32)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m32 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -pthread -x c++
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../../.bin/release_x32/libbotlib.a ../../../.bin/release_x32/librenderer.a ../../../.bin/release_x32/libfreetype.a ../../../.bin/release_x32/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
|
||||
LIBS += ../../../.bin/release_x32/libbotlib.a ../../../.bin/release_x32/librenderer.a ../../../.bin/release_x32/libfreetype.a ../../../.bin/release_x32/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
|
||||
LDDEPS += ../../../.bin/release_x32/libbotlib.a ../../../.bin/release_x32/librenderer.a ../../../.bin/release_x32/libfreetype.a ../../../.bin/release_x32/liblibjpeg-turbo.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib32 -L../../../.bin/release_x32 -m32
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -115,7 +115,7 @@ ifeq ($(config),release_x64)
|
|||
ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS) -m64 -fomit-frame-pointer -ffast-math -Os -g -msse2 -Wno-unused-parameter -Wno-write-strings -g1 -pthread -x c++
|
||||
ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CFLAGS) -fno-exceptions -fno-rtti
|
||||
ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)
|
||||
LIBS += ../../../.bin/release_x64/libbotlib.a ../../../.bin/release_x64/librenderer.a ../../../.bin/release_x64/libfreetype.a ../../../.bin/release_x64/liblibjpeg-turbo.a -ldl -lm -lX11 -lpthread
|
||||
LIBS += ../../../.bin/release_x64/libbotlib.a ../../../.bin/release_x64/librenderer.a ../../../.bin/release_x64/libfreetype.a ../../../.bin/release_x64/liblibjpeg-turbo.a -ldl -lm -lbacktrace -lX11 -lpthread
|
||||
LDDEPS += ../../../.bin/release_x64/libbotlib.a ../../../.bin/release_x64/librenderer.a ../../../.bin/release_x64/libfreetype.a ../../../.bin/release_x64/liblibjpeg-turbo.a
|
||||
ALL_LDFLAGS += $(LDFLAGS) -L/usr/lib64 -L../../../.bin/release_x64 -m64
|
||||
LINKCMD = $(CXX) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)
|
||||
|
@ -161,10 +161,12 @@ OBJECTS := \
|
|||
$(OBJDIR)/cm_trace.o \
|
||||
$(OBJDIR)/cmd.o \
|
||||
$(OBJDIR)/common.o \
|
||||
$(OBJDIR)/crash.o \
|
||||
$(OBJDIR)/cvar.o \
|
||||
$(OBJDIR)/files.o \
|
||||
$(OBJDIR)/huffman.o \
|
||||
$(OBJDIR)/huffman_static.o \
|
||||
$(OBJDIR)/json.o \
|
||||
$(OBJDIR)/md4.o \
|
||||
$(OBJDIR)/md5.o \
|
||||
$(OBJDIR)/msg.o \
|
||||
|
@ -327,6 +329,9 @@ $(OBJDIR)/cmd.o: ../../code/qcommon/cmd.cpp
|
|||
$(OBJDIR)/common.o: ../../code/qcommon/common.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/crash.o: ../../code/qcommon/crash.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/cvar.o: ../../code/qcommon/cvar.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
@ -339,6 +344,9 @@ $(OBJDIR)/huffman.o: ../../code/qcommon/huffman.cpp
|
|||
$(OBJDIR)/huffman_static.o: ../../code/qcommon/huffman_static.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/json.o: ../../code/qcommon/json.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
$(OBJDIR)/md4.o: ../../code/qcommon/md4.cpp
|
||||
@echo $(notdir $<)
|
||||
$(SILENT) $(CXX) $(ALL_CXXFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"
|
||||
|
|
|
@ -281,10 +281,12 @@ local function ApplyExeProjectSettings(exeName, server)
|
|||
"qcommon/cm_test.cpp",
|
||||
"qcommon/cm_trace.cpp",
|
||||
"qcommon/common.cpp",
|
||||
"qcommon/crash.cpp",
|
||||
"qcommon/cvar.cpp",
|
||||
"qcommon/files.cpp",
|
||||
"qcommon/huffman.cpp",
|
||||
"qcommon/huffman_static.cpp",
|
||||
"qcommon/json.cpp",
|
||||
"qcommon/md4.cpp",
|
||||
"qcommon/md5.cpp",
|
||||
"qcommon/msg.cpp",
|
||||
|
@ -311,6 +313,7 @@ local function ApplyExeProjectSettings(exeName, server)
|
|||
{
|
||||
"win32/win_main.cpp",
|
||||
"win32/win_shared.cpp",
|
||||
"win32/win_exception.cpp",
|
||||
"win32/win_syscon.cpp"
|
||||
}
|
||||
|
||||
|
@ -349,10 +352,12 @@ local function ApplyExeProjectSettings(exeName, server)
|
|||
"qcommon/cm_test.cpp",
|
||||
"qcommon/cm_trace.cpp",
|
||||
"qcommon/common.cpp",
|
||||
"qcommon/crash.cpp",
|
||||
"qcommon/cvar.cpp",
|
||||
"qcommon/files.cpp",
|
||||
"qcommon/huffman.cpp",
|
||||
"qcommon/huffman_static.cpp",
|
||||
"qcommon/json.cpp",
|
||||
"qcommon/md4.cpp",
|
||||
"qcommon/md5.cpp",
|
||||
"qcommon/msg.cpp",
|
||||
|
@ -380,6 +385,7 @@ local function ApplyExeProjectSettings(exeName, server)
|
|||
"win32/win_input.cpp",
|
||||
"win32/win_main.cpp",
|
||||
"win32/win_shared.cpp",
|
||||
"win32/win_exception.cpp",
|
||||
"win32/win_snd.cpp",
|
||||
"win32/win_syscon.cpp",
|
||||
"win32/win_wndproc.cpp",
|
||||
|
@ -457,13 +463,13 @@ local function ApplyExeProjectSettings(exeName, server)
|
|||
debugdir(abs_path_q3)
|
||||
|
||||
filter "system:windows"
|
||||
links { "Winmm", "ws2_32" }
|
||||
links { "Winmm", "ws2_32", "Version" }
|
||||
if (server == 0) then
|
||||
links { "opengl32" }
|
||||
end
|
||||
|
||||
filter "system:not windows"
|
||||
links { "dl", "m" }
|
||||
links { "dl", "m", "backtrace" }
|
||||
if (server == 0) then
|
||||
buildoptions { "-pthread" }
|
||||
links { "X11", "pthread" }
|
||||
|
|
|
@ -115,7 +115,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\.bin\debug_x32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions> ..\..\cnq3\code\win32\winquake.res /STACK:8388608 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -143,7 +143,7 @@ copy "..\..\..\.bin\debug_x32\cnq3-server-x86.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\.bin\debug_x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions> ..\..\cnq3\code\win32\winquake.res /STACK:8388608 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -182,7 +182,7 @@ copy "..\..\..\.bin\debug_x64\cnq3-server-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\.bin\release_x32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions> ..\..\cnq3\code\win32\winquake.res /STACK:8388608 /OPT:REF /OPT:ICF %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -220,7 +220,7 @@ copy "..\..\..\.bin\release_x32\cnq3-server-x86.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\.bin\release_x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions> ..\..\cnq3\code\win32\winquake.res /STACK:8388608 /OPT:REF /OPT:ICF %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -279,6 +279,7 @@ copy "..\..\..\.bin\release_x64\cnq3-server-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<ClInclude Include="..\..\code\qcommon\cm_patch.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\cm_polylib.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\cm_public.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\crash.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\g_public.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\git.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\q_platform.h" />
|
||||
|
@ -307,10 +308,12 @@ copy "..\..\..\.bin\release_x64\cnq3-server-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<ClCompile Include="..\..\code\qcommon\cm_trace.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\cmd.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\common.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\crash.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\cvar.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\files.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\huffman.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\huffman_static.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\json.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\md4.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\md5.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\msg.cpp" />
|
||||
|
@ -331,6 +334,7 @@ copy "..\..\..\.bin\release_x64\cnq3-server-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<ClCompile Include="..\..\code\server\sv_net_chan.cpp" />
|
||||
<ClCompile Include="..\..\code\server\sv_snapshot.cpp" />
|
||||
<ClCompile Include="..\..\code\server\sv_world.cpp" />
|
||||
<ClCompile Include="..\..\code\win32\win_exception.cpp" />
|
||||
<ClCompile Include="..\..\code\win32\win_main.cpp" />
|
||||
<ClCompile Include="..\..\code\win32\win_shared.cpp" />
|
||||
<ClCompile Include="..\..\code\win32\win_syscon.cpp" />
|
||||
|
|
|
@ -159,6 +159,9 @@
|
|||
<ClInclude Include="..\..\code\qcommon\cm_public.h">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\code\qcommon\crash.h">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\code\qcommon\g_public.h">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClInclude>
|
||||
|
@ -239,6 +242,9 @@
|
|||
<ClCompile Include="..\..\code\qcommon\common.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\qcommon\crash.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\qcommon\cvar.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
|
@ -251,6 +257,9 @@
|
|||
<ClCompile Include="..\..\code\qcommon\huffman_static.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\qcommon\json.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\qcommon\md4.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
|
@ -311,6 +320,9 @@
|
|||
<ClCompile Include="..\..\code\server\sv_world.cpp">
|
||||
<Filter>server</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\win32\win_exception.cpp">
|
||||
<Filter>win32</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\win32\win_main.cpp">
|
||||
<Filter>win32</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;Version.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\.bin\debug_x32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions> ..\..\cnq3\code\win32\winquake.res /STACK:8388608 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -145,7 +145,7 @@ copy "..\..\..\.bin\debug_x32\cnq3-x86.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;Version.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\.bin\debug_x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions> ..\..\cnq3\code\win32\winquake.res /STACK:8388608 %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -185,7 +185,7 @@ copy "..\..\..\.bin\debug_x64\cnq3-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;Version.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\.bin\release_x32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions> ..\..\cnq3\code\win32\winquake.res /STACK:8388608 /OPT:REF /OPT:ICF %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -224,7 +224,7 @@ copy "..\..\..\.bin\release_x32\cnq3-x86.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Winmm.lib;ws2_32.lib;Version.lib;opengl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\..\..\.bin\release_x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions> ..\..\cnq3\code\win32\winquake.res /STACK:8388608 /OPT:REF /OPT:ICF %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
|
@ -283,6 +283,7 @@ copy "..\..\..\.bin\release_x64\cnq3-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<ClInclude Include="..\..\code\qcommon\cm_patch.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\cm_polylib.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\cm_public.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\crash.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\g_public.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\git.h" />
|
||||
<ClInclude Include="..\..\code\qcommon\q_platform.h" />
|
||||
|
@ -334,10 +335,12 @@ copy "..\..\..\.bin\release_x64\cnq3-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<ClCompile Include="..\..\code\qcommon\cm_trace.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\cmd.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\common.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\crash.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\cvar.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\files.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\huffman.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\huffman_static.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\json.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\md4.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\md5.cpp" />
|
||||
<ClCompile Include="..\..\code\qcommon\msg.cpp" />
|
||||
|
@ -358,6 +361,7 @@ copy "..\..\..\.bin\release_x64\cnq3-x64.pdb" "$(QUAKE3DIR)"</Command>
|
|||
<ClCompile Include="..\..\code\server\sv_net_chan.cpp" />
|
||||
<ClCompile Include="..\..\code\server\sv_snapshot.cpp" />
|
||||
<ClCompile Include="..\..\code\server\sv_world.cpp" />
|
||||
<ClCompile Include="..\..\code\win32\win_exception.cpp" />
|
||||
<ClCompile Include="..\..\code\win32\win_glimp.cpp" />
|
||||
<ClCompile Include="..\..\code\win32\win_input.cpp" />
|
||||
<ClCompile Include="..\..\code\win32\win_main.cpp" />
|
||||
|
|
|
@ -162,6 +162,9 @@
|
|||
<ClInclude Include="..\..\code\qcommon\cm_public.h">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\code\qcommon\crash.h">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\code\qcommon\g_public.h">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClInclude>
|
||||
|
@ -311,6 +314,9 @@
|
|||
<ClCompile Include="..\..\code\qcommon\common.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\qcommon\crash.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\qcommon\cvar.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
|
@ -323,6 +329,9 @@
|
|||
<ClCompile Include="..\..\code\qcommon\huffman_static.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\qcommon\json.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\qcommon\md4.cpp">
|
||||
<Filter>qcommon</Filter>
|
||||
</ClCompile>
|
||||
|
@ -383,6 +392,9 @@
|
|||
<ClCompile Include="..\..\code\server\sv_world.cpp">
|
||||
<Filter>server</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\win32\win_exception.cpp">
|
||||
<Filter>win32</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\code\win32\win_glimp.cpp">
|
||||
<Filter>win32</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -1892,7 +1892,7 @@ int Com_Milliseconds()
|
|||
///////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#if defined(DEBUG)
|
||||
#if defined(DEBUG) || defined(CNQ3_DEV)
|
||||
|
||||
|
||||
// throw a fatal error to test error shutdown procedures
|
||||
|
@ -2223,7 +2223,7 @@ void Com_Init( char *commandLine )
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
#if defined(DEBUG) || defined(CNQ3_DEV)
|
||||
Cmd_AddCommand( "error", Com_Error_f );
|
||||
Cmd_AddCommand( "crash", Com_Crash_f );
|
||||
Cmd_AddCommand( "freeze", Com_Freeze_f );
|
||||
|
@ -2932,3 +2932,33 @@ void Field_AutoCompleteKeyName( int startArg, int compArg )
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
const char* Q_itohex( uint64_t number, qbool uppercase, qbool prefix )
|
||||
{
|
||||
static const char* luts[2] = { "0123456789abcdef", "0123456789ABCDEF" };
|
||||
static char buffer[19];
|
||||
const int maxLength = 16;
|
||||
|
||||
const char* const lut = luts[uppercase == 0 ? 0 : 1];
|
||||
uint64_t x = number;
|
||||
int i = maxLength;
|
||||
buffer[i] = '\0';
|
||||
while ( i-- ) {
|
||||
buffer[i] = lut[x & 15];
|
||||
x >>= 4;
|
||||
}
|
||||
|
||||
int startOffset = 0;
|
||||
for ( i = 0; i < maxLength - 1; i++, startOffset++ ) {
|
||||
if ( buffer[i] != '0' )
|
||||
break;
|
||||
}
|
||||
|
||||
if ( prefix ) {
|
||||
startOffset -= 2;
|
||||
buffer[startOffset + 1] = 'x';
|
||||
}
|
||||
|
||||
return buffer + startOffset;
|
||||
}
|
||||
|
|
219
code/qcommon/crash.cpp
Normal file
219
code/qcommon/crash.cpp
Normal file
|
@ -0,0 +1,219 @@
|
|||
#include "crash.h"
|
||||
#include "git.h"
|
||||
#include "vm_local.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
char gitHeadHash[24]; // SHA-1 -> 160 bits -> 20 hex chars
|
||||
vm_t* vm;
|
||||
unsigned int crc32; // CRC32
|
||||
} vmCrashInfo_t;
|
||||
|
||||
static vmCrashInfo_t crash_vm[VM_COUNT];
|
||||
static char crash_modName[64];
|
||||
static char crash_modVersion[64];
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define NEWLINE "\n"
|
||||
#else
|
||||
# define NEWLINE "\r\n"
|
||||
#endif
|
||||
|
||||
|
||||
static qbool IsVMIndexValid(vmIndex_t vmIndex)
|
||||
{
|
||||
return vmIndex >= 0 && vmIndex < VM_COUNT;
|
||||
}
|
||||
|
||||
static const char* GetVMName(vmIndex_t vmIndex)
|
||||
{
|
||||
switch (vmIndex) {
|
||||
case VM_CGAME: return "cgame";
|
||||
case VM_GAME: return "game";
|
||||
case VM_UI: return "ui";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void Crash_SaveQVMPointer(vmIndex_t vmIndex, vm_t* vm)
|
||||
{
|
||||
if (IsVMIndexValid(vmIndex))
|
||||
crash_vm[vmIndex].vm = vm;
|
||||
}
|
||||
|
||||
void Crash_SaveQVMChecksum(vmIndex_t vmIndex, unsigned int crc32)
|
||||
{
|
||||
if (IsVMIndexValid(vmIndex))
|
||||
crash_vm[vmIndex].crc32 = crc32;
|
||||
}
|
||||
|
||||
void Crash_SaveQVMGitString(const char* varName, const char* varValue)
|
||||
{
|
||||
if (strstr(varName, "gitHeadHash") == NULL)
|
||||
return;
|
||||
|
||||
vmIndex_t index;
|
||||
if (strstr(varName, "cg_") == varName)
|
||||
index = VM_CGAME;
|
||||
else if (strstr(varName, "g_") == varName)
|
||||
index = VM_GAME;
|
||||
else if (strstr(varName, "ui_") == varName)
|
||||
index = VM_UI;
|
||||
else
|
||||
return;
|
||||
|
||||
Q_strncpyz(crash_vm[index].gitHeadHash, varValue, sizeof(crash_vm[index].gitHeadHash));
|
||||
}
|
||||
|
||||
void Crash_SaveModName(const char* modName)
|
||||
{
|
||||
if (modName && *modName != '\0')
|
||||
Q_strncpyz(crash_modName, modName, sizeof(crash_modName));
|
||||
}
|
||||
|
||||
void Crash_SaveModVersion(const char* modVersion)
|
||||
{
|
||||
if (modVersion && *modVersion != '\0')
|
||||
Q_strncpyz(crash_modVersion, modVersion, sizeof(crash_modVersion));
|
||||
}
|
||||
|
||||
static void PrintQVMInfo(vmIndex_t vmIndex)
|
||||
{
|
||||
static char callStack[MAX_VM_CALL_STACK_DEPTH * 12];
|
||||
|
||||
vmCrashInfo_t* vm = &crash_vm[vmIndex];
|
||||
if (vm->crc32 == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSONW_BeginObject();
|
||||
|
||||
JSONW_IntegerValue("index", vmIndex);
|
||||
JSONW_StringValue("name", GetVMName(vmIndex));
|
||||
JSONW_BooleanValue("loaded", vm->vm != NULL);
|
||||
if (vm->crc32)
|
||||
JSONW_HexValue("crc32", vm->crc32);
|
||||
JSONW_StringValue("git_head_hash", vm->gitHeadHash);
|
||||
|
||||
if (vm->vm != NULL) {
|
||||
vm_t* const vmp = vm->vm;
|
||||
JSONW_IntegerValue("call_stack_depth_current", vmp->callStackDepth);
|
||||
JSONW_IntegerValue("call_stack_depth_previous", vmp->lastCallStackDepth);
|
||||
|
||||
int d = vmp->callStackDepth;
|
||||
qbool current = qtrue;
|
||||
if (d <= 0 || d > MAX_VM_CALL_STACK_DEPTH) {
|
||||
d = vmp->lastCallStackDepth;
|
||||
current = qfalse;
|
||||
}
|
||||
|
||||
if (d <= 0 || d > MAX_VM_CALL_STACK_DEPTH)
|
||||
d = 0;
|
||||
|
||||
if (d > 0) {
|
||||
JSONW_BooleanValue("call_stack_current", current);
|
||||
JSONW_BooleanValue("call_stack_limit_reached", d == MAX_VM_CALL_STACK_DEPTH);
|
||||
|
||||
callStack[0] = '\0';
|
||||
for (int i = 0; i < d; i++) {
|
||||
Q_strcat(callStack, sizeof(callStack), Q_itohex(vmp->callStack[i], qtrue, qtrue));
|
||||
if (i - 1 < d)
|
||||
Q_strcat(callStack, sizeof(callStack), " ");
|
||||
}
|
||||
JSONW_StringValue("call_stack", callStack);
|
||||
}
|
||||
}
|
||||
|
||||
JSONW_EndObject();
|
||||
}
|
||||
|
||||
static qbool IsAnyVMLoaded()
|
||||
{
|
||||
for (int i = 0; i < VM_COUNT; i++) {
|
||||
if (crash_vm[i].crc32 != 0)
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
static unsigned int CRC32_HashFile(const char* filePath)
|
||||
{
|
||||
enum { BUFFER_SIZE = 16 << 10 }; // 16 KB
|
||||
static byte buffer[BUFFER_SIZE];
|
||||
|
||||
struct stat st;
|
||||
if (stat(filePath, &st) != 0 || st.st_size == 0)
|
||||
return 0;
|
||||
|
||||
FILE* const file = fopen(filePath, "rb");
|
||||
if (file == NULL)
|
||||
return 0;
|
||||
|
||||
const unsigned int fileSize = (unsigned int)st.st_size;
|
||||
const unsigned int fullBlocks = fileSize / (unsigned int)BUFFER_SIZE;
|
||||
const unsigned int lastBlockSize = fileSize - fullBlocks * (unsigned int)BUFFER_SIZE;
|
||||
|
||||
unsigned int crc32 = 0;
|
||||
crc32_init(&crc32);
|
||||
|
||||
for(unsigned int i = 0; i < fullBlocks; ++i) {
|
||||
if (fread(buffer, BUFFER_SIZE, 1, file) != 1) {
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
crc32_update(&crc32, buffer, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
if(lastBlockSize > 0) {
|
||||
if (fread(buffer, lastBlockSize, 1, file) != 1) {
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
crc32_update(&crc32, buffer, lastBlockSize);
|
||||
}
|
||||
|
||||
crc32_final(&crc32);
|
||||
|
||||
fclose(file);
|
||||
|
||||
return crc32;
|
||||
}
|
||||
|
||||
void Crash_PrintToFile(const char* engineFilePath)
|
||||
{
|
||||
JSONW_StringValue("engine_version", Q3_VERSION);
|
||||
JSONW_StringValue("engine_build_date", __DATE__);
|
||||
JSONW_StringValue("engine_arch", ARCH_STRING);
|
||||
#ifdef DEDICATED
|
||||
JSONW_BooleanValue("engine_ded_server", qtrue);
|
||||
#else
|
||||
JSONW_BooleanValue("engine_ded_server", qfalse);
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
JSONW_BooleanValue("engine_debug", qtrue);
|
||||
#else
|
||||
JSONW_BooleanValue("engine_debug", qfalse);
|
||||
#endif
|
||||
#ifdef CNQ3_DEV
|
||||
JSONW_BooleanValue("engine_dev_build", qtrue);
|
||||
#else
|
||||
JSONW_BooleanValue("engine_dev_build", qfalse);
|
||||
#endif
|
||||
JSONW_StringValue("engine_git_branch", GIT_BRANCH);
|
||||
JSONW_StringValue("engine_git_commit", GIT_COMMIT);
|
||||
const unsigned int crc32 = CRC32_HashFile(engineFilePath);
|
||||
if (crc32)
|
||||
JSONW_HexValue("engine_crc32", crc32);
|
||||
JSONW_StringValue("mod_name", crash_modName);
|
||||
JSONW_StringValue("mod_version", crash_modVersion);
|
||||
|
||||
if (IsAnyVMLoaded()) {
|
||||
JSONW_BeginNamedArray("vms");
|
||||
for (int i = 0; i < VM_COUNT; i++) {
|
||||
PrintQVMInfo((vmIndex_t)i);
|
||||
}
|
||||
JSONW_EndArray();
|
||||
}
|
||||
}
|
29
code/qcommon/crash.h
Normal file
29
code/qcommon/crash.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "q_shared.h"
|
||||
#include "qcommon.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
// json.cpp
|
||||
void JSONW_BeginFile(FILE* file);
|
||||
void JSONW_EndFile();
|
||||
void JSONW_BeginObject();
|
||||
void JSONW_BeginNamedObject(const char* name);
|
||||
void JSONW_EndObject();
|
||||
void JSONW_BeginArray();
|
||||
void JSONW_BeginNamedArray(const char* name);
|
||||
void JSONW_EndArray();
|
||||
void JSONW_IntegerValue(const char* name, int number);
|
||||
void JSONW_HexValue(const char* name, uint64_t number);
|
||||
void JSONW_BooleanValue(const char* name, qbool value);
|
||||
void JSONW_StringValue(const char* name, const char* format, ...);
|
||||
void JSONW_UnnamedHex(uint64_t number);
|
||||
void JSONW_UnnamedString(const char* format, ...);
|
||||
|
||||
// crash.cpp
|
||||
void Crash_SaveQVMPointer(vmIndex_t vmIndex, vm_t* vm);
|
||||
void Crash_SaveQVMChecksum(vmIndex_t vmIndex, unsigned int crc32);
|
||||
void Crash_SaveQVMGitString(const char* varName, const char* varValue);
|
||||
void Crash_SaveModName(const char* modName);
|
||||
void Crash_SaveModVersion(const char* modVersion);
|
||||
void Crash_PrintToFile(const char* engineFilePath);
|
|
@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
|
||||
#include "q_shared.h"
|
||||
#include "qcommon.h"
|
||||
#include "crash.h"
|
||||
#include "git.h"
|
||||
|
||||
static cvar_t* cvar_vars;
|
||||
|
@ -633,6 +634,14 @@ void Cvar_InfoStringBuffer( int bit, char* buff, int buffsize )
|
|||
void Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags )
|
||||
{
|
||||
cvar_t* cv = Cvar_Get( varName, defaultValue, flags );
|
||||
|
||||
if ( (flags & CVAR_SERVERINFO) && !Q_stricmp(varName, "gamename") )
|
||||
Crash_SaveModName( defaultValue );
|
||||
else if ( (flags & CVAR_SERVERINFO) && !Q_stricmp(varName, "gameversion") )
|
||||
Crash_SaveModVersion( defaultValue );
|
||||
else
|
||||
Crash_SaveQVMGitString( varName, defaultValue );
|
||||
|
||||
if ( !vmCvar )
|
||||
return;
|
||||
|
||||
|
|
365
code/qcommon/json.cpp
Normal file
365
code/qcommon/json.cpp
Normal file
|
@ -0,0 +1,365 @@
|
|||
#include "crash.h"
|
||||
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
typedef unsigned __int8 u8;
|
||||
typedef unsigned __int32 u32;
|
||||
#else
|
||||
typedef uint8_t u8;
|
||||
typedef uint32_t u32;
|
||||
#endif
|
||||
|
||||
|
||||
static qbool UTF8_NextCodePoint(u32* codePoint, u32* byteCount, const char* input)
|
||||
{
|
||||
if (!codePoint || !byteCount || !input || *input == '\0')
|
||||
return qfalse;
|
||||
|
||||
u8 byte0 = (u8)input[0];
|
||||
if (byte0 <= 127) {
|
||||
*codePoint = (u32)byte0;
|
||||
*byteCount = 1;
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
// Starts with 110?
|
||||
if ((byte0 >> 5) == 6) {
|
||||
const u8 byte1 = (u8)input[1];
|
||||
*codePoint = ((u32)byte1 & 63) | (((u32)byte0 & 31) << 6);
|
||||
*byteCount = 2;
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
// Starts with 1110?
|
||||
if ((byte0 >> 4) == 14) {
|
||||
const u8 byte1 = (u8)input[1];
|
||||
const u8 byte2 = (u8)input[2];
|
||||
*codePoint = ((u32)byte2 & 63) | (((u32)byte1 & 63) << 6) | (((u32)byte0 & 15) << 12);
|
||||
*byteCount = 3;
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
// Starts with 11110?
|
||||
if ((byte0 >> 3) == 30) {
|
||||
const u8 byte1 = (u8)input[1];
|
||||
const u8 byte2 = (u8)input[2];
|
||||
const u8 byte3 = (u8)input[3];
|
||||
*codePoint = ((u32)byte3 & 63) | (((u32)byte2 & 63) << 6) | (((u32)byte1 & 63) << 12) | (((u32)byte0 & 7) << 18);
|
||||
*byteCount = 4;
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
u32 CodePoint;
|
||||
char OutputChar;
|
||||
} ShortEscape;
|
||||
|
||||
#define ENTRY(HexValue, Char) { 0x##HexValue, Char }
|
||||
static const ShortEscape ShortEscapeCodePoints[] = {
|
||||
ENTRY(0022, '"'),
|
||||
ENTRY(005C, '\\'),
|
||||
ENTRY(002F, '/'),
|
||||
ENTRY(0008, 'b'),
|
||||
ENTRY(000C, 'f'),
|
||||
ENTRY(000A, 'n'),
|
||||
ENTRY(000D, 'r'),
|
||||
ENTRY(0009, 't')
|
||||
};
|
||||
#undef ENTRY
|
||||
|
||||
static const char HexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
static qbool UTF8_NeedsEscaping(u32* newLength, u32 codePoint)
|
||||
{
|
||||
if (!newLength)
|
||||
return qfalse;
|
||||
|
||||
const u32 count = (u32)ARRAY_LEN(ShortEscapeCodePoints);
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
if (codePoint == ShortEscapeCodePoints[i].CodePoint) {
|
||||
*newLength = 2; // Of form: "\A".
|
||||
return qtrue;
|
||||
}
|
||||
}
|
||||
|
||||
// Range: 0x0000 - 0x001F
|
||||
if (codePoint <= 0x001F) {
|
||||
*newLength = 6; // Of form: "\uABCD".
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
static void UTF8_WriteCodePoint(u32* newLength, char* output, u32 codePoint, const char* input)
|
||||
{
|
||||
if (!newLength || !output || !input)
|
||||
return;
|
||||
|
||||
const u32 count = (u32)ARRAY_LEN(ShortEscapeCodePoints);
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
if (codePoint == ShortEscapeCodePoints[i].CodePoint) {
|
||||
*newLength = 2;
|
||||
output[0] = '\\';
|
||||
output[1] = ShortEscapeCodePoints[i].OutputChar;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Range: 0x0000 - 0x001F
|
||||
if (codePoint <= 0x001F) {
|
||||
*newLength = 6;
|
||||
output[0] = '\\';
|
||||
output[1] = 'u';
|
||||
output[2] = HexDigits[(codePoint >> 12) & 15];
|
||||
output[3] = HexDigits[(codePoint >> 8) & 15];
|
||||
output[4] = HexDigits[(codePoint >> 4) & 15];
|
||||
output[5] = HexDigits[codePoint & 15];
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < *newLength; ++i) {
|
||||
output[i] = input[i];
|
||||
}
|
||||
}
|
||||
|
||||
static u32 UTF8_LengthForJSON(const char* input)
|
||||
{
|
||||
if (!input)
|
||||
return 0;
|
||||
|
||||
const char* s = input;
|
||||
u32 codePoint = 0;
|
||||
u32 byteCount = 0;
|
||||
u32 newLength = 0;
|
||||
while (UTF8_NextCodePoint(&codePoint, &byteCount, s)) {
|
||||
s += byteCount;
|
||||
UTF8_NeedsEscaping(&byteCount, codePoint);
|
||||
newLength += byteCount;
|
||||
}
|
||||
|
||||
return newLength;
|
||||
}
|
||||
|
||||
static void UTF8_CleanForJSON(char* output, const char* input)
|
||||
{
|
||||
if (!output || !input)
|
||||
return;
|
||||
|
||||
const char* in = input;
|
||||
char* out = output;
|
||||
u32 codePoint = 0;
|
||||
u32 inputByteCount = 0;
|
||||
while (UTF8_NextCodePoint(&codePoint, &inputByteCount, in)) {
|
||||
u32 outputByteCount = inputByteCount;
|
||||
UTF8_WriteCodePoint(&outputByteCount, out, codePoint, in);
|
||||
in += inputByteCount;
|
||||
out += outputByteCount;
|
||||
}
|
||||
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
unsigned int itemIndices[16];
|
||||
FILE* file;
|
||||
unsigned int level;
|
||||
} JSONWriter;
|
||||
|
||||
static JSONWriter writer;
|
||||
|
||||
static void JSONW_Write(const char* string)
|
||||
{
|
||||
if (!string)
|
||||
return;
|
||||
|
||||
fwrite(string, strlen(string), 1, writer.file);
|
||||
}
|
||||
|
||||
static void JSONW_WriteNewLine()
|
||||
{
|
||||
JSONW_Write("\n");
|
||||
for (u32 i = 0; i < writer.level; i++) {
|
||||
JSONW_Write("\t");
|
||||
}
|
||||
}
|
||||
|
||||
static void JSONW_WriteClean(const char* string)
|
||||
{
|
||||
static char buffer[4096];
|
||||
|
||||
if (!string)
|
||||
return;
|
||||
|
||||
const u32 length = UTF8_LengthForJSON(string);
|
||||
if (length >= sizeof(buffer)) {
|
||||
JSONW_Write("bufferSizeTooSmall");
|
||||
return;
|
||||
}
|
||||
|
||||
UTF8_CleanForJSON(buffer, string);
|
||||
JSONW_Write(buffer);
|
||||
}
|
||||
|
||||
void JSONW_BeginFile(FILE* file)
|
||||
{
|
||||
memset(&writer, 0, sizeof(writer));
|
||||
writer.file = file;
|
||||
|
||||
JSONW_Write("{");
|
||||
++writer.level;
|
||||
}
|
||||
|
||||
void JSONW_EndFile()
|
||||
{
|
||||
JSONW_Write("\r\n}");
|
||||
}
|
||||
|
||||
void JSONW_BeginObject()
|
||||
{
|
||||
if (writer.itemIndices[writer.level] > 0)
|
||||
JSONW_Write(",");
|
||||
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("{");
|
||||
++writer.level;
|
||||
writer.itemIndices[writer.level] = 0;
|
||||
}
|
||||
|
||||
void JSONW_BeginNamedObject(const char* name)
|
||||
{
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
if (writer.itemIndices[writer.level] > 0)
|
||||
JSONW_Write(",");
|
||||
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("\"");
|
||||
JSONW_WriteClean(name);
|
||||
JSONW_Write("\":");
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("{");
|
||||
++writer.level;
|
||||
writer.itemIndices[writer.level] = 0;
|
||||
}
|
||||
|
||||
void JSONW_EndObject()
|
||||
{
|
||||
--writer.level;
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("}");
|
||||
++writer.itemIndices[writer.level];
|
||||
}
|
||||
|
||||
void JSONW_BeginArray()
|
||||
{
|
||||
if (writer.itemIndices[writer.level] > 0)
|
||||
JSONW_Write(",");
|
||||
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("[");
|
||||
++writer.level;
|
||||
writer.itemIndices[writer.level] = 0;
|
||||
}
|
||||
|
||||
void JSONW_BeginNamedArray(const char* name)
|
||||
{
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
if (writer.itemIndices[writer.level] > 0)
|
||||
JSONW_Write(",");
|
||||
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("\"");
|
||||
JSONW_WriteClean(name);
|
||||
JSONW_Write("\":");
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("[");
|
||||
++writer.level;
|
||||
writer.itemIndices[writer.level] = 0;
|
||||
}
|
||||
|
||||
void JSONW_EndArray()
|
||||
{
|
||||
--writer.level;
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("]");
|
||||
++writer.itemIndices[writer.level];
|
||||
}
|
||||
|
||||
void JSONW_IntegerValue(const char* name, int number)
|
||||
{
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
JSONW_StringValue(name, "%d", number);
|
||||
}
|
||||
|
||||
void JSONW_HexValue(const char* name, uint64_t number)
|
||||
{
|
||||
if (!name)
|
||||
return;
|
||||
|
||||
JSONW_StringValue(name, Q_itohex(number, qtrue, qtrue));
|
||||
}
|
||||
|
||||
void JSONW_BooleanValue(const char* name, qbool value)
|
||||
{
|
||||
JSONW_StringValue(name, value ? "true" : "false");
|
||||
}
|
||||
|
||||
void JSONW_StringValue(const char* name, const char* format, ...)
|
||||
{
|
||||
static char buffer[4096];
|
||||
|
||||
if (!name || !format || *format == '\0')
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
Q_vsnprintf(buffer, sizeof(buffer), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (writer.itemIndices[writer.level] > 0)
|
||||
JSONW_Write(", ");
|
||||
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("\"");
|
||||
JSONW_Write(name);
|
||||
JSONW_Write("\": \"");
|
||||
JSONW_WriteClean(buffer);
|
||||
JSONW_Write("\"");
|
||||
++writer.itemIndices[writer.level];
|
||||
}
|
||||
|
||||
void JSONW_UnnamedHex(uint64_t number)
|
||||
{
|
||||
JSONW_UnnamedString(Q_itohex(number, qtrue, qtrue));
|
||||
}
|
||||
|
||||
void JSONW_UnnamedString(const char* format, ...)
|
||||
{
|
||||
static char buffer[4096];
|
||||
|
||||
if (!format || *format == '\0')
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
Q_vsnprintf(buffer, sizeof(buffer), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (writer.itemIndices[writer.level] > 0)
|
||||
JSONW_Write(", ");
|
||||
|
||||
JSONW_WriteNewLine();
|
||||
JSONW_Write("\"");
|
||||
JSONW_WriteClean(buffer);
|
||||
JSONW_Write("\"");
|
||||
++writer.itemIndices[writer.level];
|
||||
}
|
|
@ -993,4 +993,8 @@ int StatHuff_WriteSymbol( int symbol, byte* buffer, int bitIndex ); // returns
|
|||
#define SV_DECODE_START CL_ENCODE_START
|
||||
#define CL_DECODE_START SV_ENCODE_START
|
||||
|
||||
|
||||
const char* Q_itohex( uint64_t number, qbool uppercase, qbool prefix );
|
||||
|
||||
|
||||
#endif // _QCOMMON_H_
|
||||
|
|
|
@ -34,6 +34,7 @@ and one exported function: Perform
|
|||
*/
|
||||
|
||||
#include "vm_local.h"
|
||||
#include "crash.h"
|
||||
|
||||
opcode_info_t ops[ OP_MAX ] =
|
||||
{
|
||||
|
@ -435,6 +436,7 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc ) {
|
|||
}
|
||||
|
||||
vm->crc32sum = crc32sum;
|
||||
Crash_SaveQVMChecksum( vm->index, crc32sum );
|
||||
|
||||
dataLength = header->dataLength + header->litLength + header->bssLength;
|
||||
vm->dataLength = dataLength;
|
||||
|
@ -989,6 +991,8 @@ vm_t *VM_Create( vmIndex_t index, syscall_t systemCalls, vmInterpret_t interpret
|
|||
// load the map file
|
||||
VM_LoadSymbols( vm );
|
||||
|
||||
Crash_SaveQVMPointer( index, vm );
|
||||
|
||||
Com_Printf( "%s loaded in %d bytes on the hunk\n", vm->name, remaining - Hunk_MemoryRemaining() );
|
||||
|
||||
return vm;
|
||||
|
@ -1006,6 +1010,8 @@ void VM_Free( vm_t *vm ) {
|
|||
return;
|
||||
}
|
||||
|
||||
Crash_SaveQVMPointer( vm->index, NULL );
|
||||
|
||||
if ( vm->destroy )
|
||||
vm->destroy( vm );
|
||||
|
||||
|
|
|
@ -138,6 +138,22 @@ qboolean VM_PrepareInterpreter2( vm_t *vm, vmHeader_t *header )
|
|||
}
|
||||
|
||||
|
||||
static void CallStackPush( vm_t *vm, int *callStackDepth, int ip )
|
||||
{
|
||||
const int clampedDepth = min( vm->callStackDepth, MAX_VM_CALL_STACK_DEPTH - 1 );
|
||||
|
||||
vm->callStack[clampedDepth] = ip;
|
||||
vm->callStackDepth++;
|
||||
*callStackDepth = max( *callStackDepth, clampedDepth + 1 );
|
||||
}
|
||||
|
||||
|
||||
static void CallStackPop( vm_t *vm )
|
||||
{
|
||||
vm->callStackDepth--;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
==============
|
||||
VM_CallInterpreted2
|
||||
|
@ -173,6 +189,7 @@ int VM_CallInterpreted2( vm_t *vm, int *args ) {
|
|||
int *opStack, *opStackTop;
|
||||
int programStack;
|
||||
int stackOnEntry;
|
||||
int callStackDepth = 0;
|
||||
byte *image;
|
||||
int v1, v0;
|
||||
int dataMask;
|
||||
|
@ -238,9 +255,12 @@ nextInstruction2:
|
|||
if ( opStack + ((ci-1)->opStack/4) >= opStackTop ) {
|
||||
Com_Error( ERR_DROP, "VM opStack overflow" );
|
||||
}
|
||||
CallStackPush( vm, &callStackDepth, (int)(ci - (instruction_t*)vm->codeBase.ptr) - 1 );
|
||||
break;
|
||||
|
||||
case OP_LEAVE:
|
||||
CallStackPop( vm );
|
||||
|
||||
// remove our stack frame
|
||||
programStack += v0;
|
||||
|
||||
|
@ -274,9 +294,13 @@ nextInstruction2:
|
|||
for ( argn = 0; argn < ARRAY_LEN( argarr ); ++argn ) {
|
||||
argarr[ argn ] = *(int*)&image[ programStack + 4 + 4*argn ];
|
||||
}
|
||||
CallStackPush( vm, &callStackDepth, r0.i );
|
||||
v0 = vm->systemCall( &argarr[0] );
|
||||
CallStackPop( vm );
|
||||
#else
|
||||
CallStackPush( vm, &callStackDepth, r0.i );
|
||||
v0 = vm->systemCall( (intptr_t *)&image[ programStack + 4 ] );
|
||||
CallStackPop( vm );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -607,6 +631,7 @@ done:
|
|||
}
|
||||
|
||||
vm->programStack = stackOnEntry;
|
||||
vm->lastCallStackDepth = callStackDepth;
|
||||
|
||||
// return the result
|
||||
return *opStack;
|
||||
|
|
|
@ -154,6 +154,8 @@ typedef union vmFunc_u {
|
|||
void (*func)(void);
|
||||
} vmFunc_t;
|
||||
|
||||
#define MAX_VM_CALL_STACK_DEPTH 64
|
||||
|
||||
struct vm_s {
|
||||
// DO NOT MOVE OR CHANGE THESE WITHOUT CHANGING THE VM_OFFSET_* DEFINES
|
||||
// USED BY THE ASM CODE
|
||||
|
@ -202,6 +204,11 @@ struct vm_s {
|
|||
|
||||
uint32_t crc32sum;
|
||||
vmIndex_t index;
|
||||
|
||||
int callStackDepth;
|
||||
int lastCallStackDepth;
|
||||
int callStackDepthTemp; // only for vm_x86.cpp
|
||||
int callStack[MAX_VM_CALL_STACK_DEPTH];
|
||||
};
|
||||
|
||||
extern vm_t *currentVM;
|
||||
|
|
|
@ -1553,6 +1553,85 @@ static qboolean EmitMOPs(vm_t *vm, int op)
|
|||
}
|
||||
|
||||
|
||||
static void EmitCallStackPush( vm_t *vm )
|
||||
{
|
||||
const int instOffset = (int)(ci - inst);
|
||||
|
||||
#if id386
|
||||
// clampedDepth = MIN_UNSIGNED(vm->callStackDepth, MAX_VM_CALL_STACK_DEPTH - 1);
|
||||
EmitString( "8B 0D" ); // mov ecx, dword ptr [&vm->callStackDepth]
|
||||
EmitPtr( &vm->callStackDepth );
|
||||
EmitString( "BA" ); // mov edx, MAX_VM_CALL_STACK_DEPTH - 1
|
||||
Emit4( MAX_VM_CALL_STACK_DEPTH - 1 );
|
||||
EmitString( "39 D1" ); // cmp ecx, edx
|
||||
EmitString( "0F 47 CA" ); // cmova ecx, edx
|
||||
|
||||
// vm->callStack[clampedDepth] = instOffset;
|
||||
EmitString( "C7 04 8D" ); // mov dword ptr [vm->callStack + ecx*4], instOffset
|
||||
EmitPtr( vm->callStack );
|
||||
Emit4( instOffset );
|
||||
|
||||
// vm->callStackDepth++;
|
||||
EmitString( "83 05" ); // add dword ptr [&vm->callStackDepth], 1
|
||||
EmitPtr( &vm->callStackDepth );
|
||||
Emit1( 1 );
|
||||
|
||||
// vm->callStackDepthTemp = MAX(vm->callStackDepthTemp, clampedDepth + 1);
|
||||
EmitString( "83 C1 01" ); // add ecx, 1
|
||||
EmitString( "8B 15" ); // mov edx, dword ptr [&vm->callStackDepthTemp]
|
||||
EmitPtr( &vm->callStackDepthTemp );
|
||||
EmitString( "39 D1" ); // cmp ecx, edx
|
||||
EmitString( "0F 47 D1" ); // cmova edx, ecx
|
||||
EmitString( "89 15" ); // mov dword ptr [&vm->callStackDepthTemp], edx
|
||||
EmitPtr( &vm->callStackDepthTemp );
|
||||
#elif idx64
|
||||
// clampedDepth = MIN_UNSIGNED(vm->callStackDepth, MAX_VM_CALL_STACK_DEPTH - 1);
|
||||
EmitString( "A1" ); // mov eax, dword ptr [&vm->callStackDepth]
|
||||
EmitPtr( &vm->callStackDepth );
|
||||
EmitString( "BA" ); // mov edx, MAX_VM_CALL_STACK_DEPTH - 1
|
||||
Emit4( MAX_VM_CALL_STACK_DEPTH - 1 );
|
||||
EmitString( "39 D0" ); // cmp eax, edx
|
||||
EmitString( "0F 46 D0" ); // cmovbe edx, eax
|
||||
|
||||
// vm->callStack[clampedDepth] = instOffset;
|
||||
EmitString( "48 B8" ); // mov rax, vm->callStack
|
||||
EmitPtr( vm->callStack );
|
||||
EmitString( "C7 04 90" ); // mov dword ptr [rax + rdx*4], instOffset
|
||||
Emit4( instOffset );
|
||||
|
||||
// vm->callStackDepth++;
|
||||
EmitString( "48 B8" ); // mov rax, &vm->callStackDepth
|
||||
EmitPtr( &vm->callStackDepth );
|
||||
EmitString( "83 00 01" ); // add dword ptr [rax], 1
|
||||
|
||||
// vm->callStackDepthTemp = MAX(vm->callStackDepthTemp, clampedDepth + 1);
|
||||
EmitString( "48 B8" ); // mov rax, &vm->callStackDepthTemp
|
||||
EmitPtr( &vm->callStackDepthTemp );
|
||||
EmitString( "83 C2 01" ); // add edx, 1
|
||||
EmitString( "8B 08" ); // mov ecx, dword ptr [rax]
|
||||
EmitString( "39 D1" ); // cmp ecx, edx
|
||||
EmitString( "0F 47 D1" ); // cmova edx, ecx
|
||||
EmitString( "89 10" ); // mov dword ptr [rax], edx
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void EmitCallStackPop( vm_t *vm )
|
||||
{
|
||||
#if id386
|
||||
// vm->callStackDepth--;
|
||||
EmitString( "83 2D" ); // sub dword ptr [&vm->callStackDepth], 1
|
||||
EmitPtr( &vm->callStackDepth );
|
||||
Emit1( 1 );
|
||||
#elif idx64
|
||||
// vm->callStackDepth--;
|
||||
EmitString( "48 B9" ); // mov rcx, &vm->callStackDepth
|
||||
EmitPtr( &vm->callStackDepth );
|
||||
EmitString( "83 29 01" ); // sub dword ptr [rcx], 1
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
VM_Compile
|
||||
|
@ -1715,6 +1794,8 @@ __compile:
|
|||
break;
|
||||
|
||||
case OP_ENTER:
|
||||
EmitCallStackPush( vm );
|
||||
|
||||
v = ci->value;
|
||||
if ( ISS8( v ) ) {
|
||||
EmitString( "83 EE" ); // sub esi, 0x12
|
||||
|
@ -1862,8 +1943,12 @@ __compile:
|
|||
break;
|
||||
|
||||
case OP_CALL:
|
||||
EmitCallStackPush( vm );
|
||||
|
||||
EmitMovEAXEDI( vm ); // mov eax, dword ptr [edi]
|
||||
EmitCallOffset( FUNC_CALL ); // call +FUNC_CALL
|
||||
|
||||
EmitCallStackPop( vm );
|
||||
break;
|
||||
|
||||
case OP_PUSH:
|
||||
|
@ -1885,6 +1970,7 @@ __compile:
|
|||
Emit4( v );
|
||||
}
|
||||
#endif
|
||||
EmitCallStackPop( vm );
|
||||
EmitString( "C3" ); // ret
|
||||
break;
|
||||
|
||||
|
@ -2433,7 +2519,10 @@ int VM_CallCompiled( vm_t *vm, int *args )
|
|||
vm->opStack = opStack;
|
||||
vm->opStackTop = opStack + ARRAY_LEN( opStack ) - 1;
|
||||
|
||||
vm->callStackDepth = 0; // theoretically not necessary...
|
||||
vm->callStackDepthTemp = 0;
|
||||
vm->codeBase.func(); // go into generated code
|
||||
vm->lastCallStackDepth = vm->callStackDepthTemp;
|
||||
|
||||
if ( vm->opStack != &opStack[1] ) {
|
||||
Com_Error( ERR_DROP, "opStack corrupted in compiled code" );
|
||||
|
|
|
@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
|
||||
void Sys_QueEvent( int time, sysEventType_t type, int value, int value2, int ptrLength, void *ptr );
|
||||
void Sys_SendKeyEvents (void);
|
||||
void Sys_Exit(int);
|
||||
void Sys_ConsoleInputShutdown(void);
|
||||
|
||||
// Input subsystem
|
||||
void IN_Init (void);
|
||||
|
@ -42,3 +42,6 @@ void SIG_Init(void);
|
|||
|
||||
// hardware gamma ramp
|
||||
void LIN_RestoreGamma(void);
|
||||
|
||||
extern int q_argc;
|
||||
extern const char** q_argv;
|
||||
|
|
|
@ -20,9 +20,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
===========================================================================
|
||||
*/
|
||||
#include <signal.h>
|
||||
#include <execinfo.h>
|
||||
#include <backtrace.h>
|
||||
|
||||
#include "../qcommon/q_shared.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
#include "../qcommon/crash.h"
|
||||
#ifndef DEDICATED
|
||||
#include "../renderer/tr_local.h"
|
||||
#endif
|
||||
|
@ -76,6 +79,81 @@ static const char* Sig_GetName(int sig)
|
|||
}
|
||||
|
||||
|
||||
static void Sig_WriteJSON(int sig)
|
||||
{
|
||||
FILE* const file = fopen(va("%s-crash.json", q_argv[0]), "w");
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
JSONW_BeginFile(file);
|
||||
JSONW_IntegerValue("signal", sig);
|
||||
JSONW_StringValue("signal_name", Sig_GetName(sig));
|
||||
JSONW_StringValue("signal_description", Sig_GetDescription(sig));
|
||||
Crash_PrintToFile(q_argv[0]);
|
||||
JSONW_EndFile();
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
|
||||
// only uses functions safe to call in a signal handler
|
||||
static void Sig_WriteBacktraceSafe()
|
||||
{
|
||||
FILE* const file = fopen(va("%s-crash.bt", q_argv[0]), "w");
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
void* addresses[64];
|
||||
const int addresscount = backtrace(addresses, sizeof(addresses));
|
||||
if (addresscount > 0)
|
||||
{
|
||||
fprintf(file, "backtrace_symbols_fd stack trace:\r\n");
|
||||
fflush(file);
|
||||
backtrace_symbols_fd(addresses, addresscount, fileno(file));
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "The call to backtrace failed\r\n");
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
|
||||
static void libbt_ErrorCallback(void* data, const char* msg, int errnum)
|
||||
{
|
||||
fprintf((FILE*)data, "libbacktrace error: %s (%d)\r\n", msg, errnum);
|
||||
}
|
||||
|
||||
|
||||
// might not be safe in a signal handler
|
||||
static void Sig_WriteBacktraceUnsafe()
|
||||
{
|
||||
FILE* const file = fopen(va("%s-crash.bt", q_argv[0]), "a");
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
fprintf(file, "\r\n\r\n");
|
||||
|
||||
backtrace_state* const state = backtrace_create_state(q_argv[0], 0, libbt_ErrorCallback, file);
|
||||
if (state)
|
||||
{
|
||||
fprintf(file, "libbacktrace stack trace:\r\n");
|
||||
fflush(file);
|
||||
backtrace_print(state, 0, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "The call to backtrace_create_state failed\r\n");
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
|
||||
static qbool sig_crashed = qfalse;
|
||||
static int sig_signal = 0;
|
||||
|
||||
|
||||
// Every call in there needs to be safe when called more than once.
|
||||
static void SIG_HandleCrash()
|
||||
{
|
||||
|
@ -84,6 +162,14 @@ static void SIG_HandleCrash()
|
|||
#ifndef DEDICATED
|
||||
LIN_RestoreGamma();
|
||||
#endif
|
||||
Sys_ConsoleInputShutdown();
|
||||
|
||||
if (!sig_crashed)
|
||||
return;
|
||||
|
||||
Sig_WriteJSON(sig_signal);
|
||||
Sig_WriteBacktraceSafe();
|
||||
Sig_WriteBacktraceUnsafe();
|
||||
}
|
||||
|
||||
|
||||
|
@ -91,7 +177,8 @@ static void Sig_HandleSignal(int sig)
|
|||
{
|
||||
static int faultCounter = 0;
|
||||
static qbool crashHandled = qfalse;
|
||||
|
||||
|
||||
sig_signal = sig;
|
||||
faultCounter++;
|
||||
|
||||
if (faultCounter >= 3)
|
||||
|
@ -102,23 +189,23 @@ static void Sig_HandleSignal(int sig)
|
|||
exit(3);
|
||||
}
|
||||
|
||||
const qbool crashed = Sig_IsCrashSignal(sig);
|
||||
sig_crashed = Sig_IsCrashSignal(sig);
|
||||
if (faultCounter == 2)
|
||||
{
|
||||
// The termination handler failed which means that if we exit right now,
|
||||
// some system settings might still be in a bad state.
|
||||
printf("DOUBLE SIGNAL FAULT: Received signal %d (%s), exiting...\n", sig, Sig_GetName(sig));
|
||||
if (crashed && !crashHandled)
|
||||
if (sig_crashed && !crashHandled)
|
||||
{
|
||||
SIG_HandleCrash();
|
||||
}
|
||||
exit(2);
|
||||
}
|
||||
|
||||
fprintf(crashed ? stderr : stdout, "Received %s signal %d: %s (%s), exiting...\n",
|
||||
crashed ? "crash" : "termination", sig, Sig_GetName(sig), Sig_GetDescription(sig));
|
||||
fprintf(sig_crashed ? stderr : stdout, "Received %s signal %d: %s (%s), exiting...\n",
|
||||
sig_crashed ? "crash" : "termination", sig, Sig_GetName(sig), Sig_GetDescription(sig));
|
||||
|
||||
if (crashed)
|
||||
if (sig_crashed)
|
||||
{
|
||||
SIG_HandleCrash();
|
||||
crashHandled = qtrue;
|
||||
|
@ -131,12 +218,13 @@ static void Sig_HandleSignal(int sig)
|
|||
CL_Shutdown();
|
||||
#endif
|
||||
SV_Shutdown("Signal caught");
|
||||
Sys_Exit(0);
|
||||
Sys_ConsoleInputShutdown();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SIG_Init(void)
|
||||
void SIG_Init()
|
||||
{
|
||||
// This is unfortunately needed because some code might
|
||||
// call exit and bypass all the clean-up work without
|
||||
|
|
|
@ -63,6 +63,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||
///////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
int q_argc = 0;
|
||||
const char** q_argv = NULL;
|
||||
|
||||
static qboolean stdin_active = qtrue;
|
||||
|
||||
// enable/disable tty input mode
|
||||
|
@ -274,6 +277,9 @@ static void Sys_ConsoleInputInit()
|
|||
}
|
||||
} else
|
||||
ttycon_on = qfalse;
|
||||
|
||||
// make stdin non-blocking
|
||||
fcntl( 0, F_SETFL, fcntl(0, F_GETFL, 0) | FNDELAY );
|
||||
}
|
||||
|
||||
|
||||
|
@ -442,12 +448,16 @@ const char* Sys_ConsoleInput()
|
|||
|
||||
|
||||
// never exit without calling this, or your terminal will be left in a pretty bad state
|
||||
static void Sys_ConsoleInputShutdown()
|
||||
void Sys_ConsoleInputShutdown()
|
||||
{
|
||||
if (!ttycon_on)
|
||||
return;
|
||||
Com_Printf("Shutdown tty console\n");
|
||||
tcsetattr (0, TCSADRAIN, &tty_tc);
|
||||
if (ttycon_on)
|
||||
{
|
||||
tcsetattr (0, TCSADRAIN, &tty_tc);
|
||||
ttycon_on = qfalse;
|
||||
}
|
||||
|
||||
// make stdin blocking
|
||||
fcntl( 0, F_SETFL, fcntl(0, F_GETFL, 0) & ~FNDELAY );
|
||||
}
|
||||
|
||||
|
||||
|
@ -462,23 +472,11 @@ qboolean Sys_LowPhysicalMemory()
|
|||
}
|
||||
|
||||
|
||||
// single exit point (regular exit or in case of signal fault)
|
||||
void Sys_Exit( int ex )
|
||||
{
|
||||
Sys_ConsoleInputShutdown();
|
||||
exit(ex);
|
||||
}
|
||||
|
||||
|
||||
void Sys_Error( const char *error, ... )
|
||||
{
|
||||
va_list argptr;
|
||||
char string[1024];
|
||||
|
||||
// change stdin to nonblocking
|
||||
// NOTE TTimo not sure how well that goes with tty console mode
|
||||
fcntl( 0, F_SETFL, fcntl(0, F_GETFL, 0) & ~FNDELAY );
|
||||
|
||||
if (ttycon_on)
|
||||
tty_Hide();
|
||||
|
||||
|
@ -491,18 +489,15 @@ void Sys_Error( const char *error, ... )
|
|||
va_end (argptr);
|
||||
fprintf(stderr, "Sys_Error: %s\n", string);
|
||||
|
||||
Sys_Exit( 1 );
|
||||
Sys_ConsoleInputShutdown();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
void Sys_Quit()
|
||||
{
|
||||
#ifndef DEDICATED
|
||||
CL_Shutdown();
|
||||
#endif
|
||||
|
||||
fcntl( 0, F_SETFL, fcntl(0, F_GETFL, 0) & ~FNDELAY );
|
||||
Sys_Exit(0);
|
||||
Sys_ConsoleInputShutdown();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -835,8 +830,11 @@ void Sys_Init()
|
|||
}
|
||||
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
q_argc = argc;
|
||||
q_argv = argv;
|
||||
|
||||
SIG_Init();
|
||||
|
||||
// merge the command line: we need it in a single chunk
|
||||
|
@ -858,8 +856,6 @@ int main( int argc, char* argv[] )
|
|||
|
||||
Sys_ConsoleInputInit();
|
||||
|
||||
fcntl( 0, F_SETFL, fcntl(0, F_GETFL, 0) | FNDELAY );
|
||||
|
||||
while (qtrue) {
|
||||
// if running as a client but not focused, sleep a bit
|
||||
// (servers have their own sleep path)
|
||||
|
|
423
code/win32/win_exception.cpp
Normal file
423
code/win32/win_exception.cpp
Normal file
|
@ -0,0 +1,423 @@
|
|||
#include "../qcommon/q_shared.h"
|
||||
#include "../qcommon/qcommon.h"
|
||||
#include "../qcommon/crash.h"
|
||||
#include "win_local.h"
|
||||
#include <DbgHelp.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
|
||||
#if !id386 && !idx64
|
||||
# error "This architecture is not supported."
|
||||
#endif
|
||||
|
||||
|
||||
typedef void (WINAPI *FptrGeneric)();
|
||||
typedef BOOL (WINAPI *FptrSymInitialize)(HANDLE, PCSTR, BOOL);
|
||||
typedef PVOID (WINAPI *FptrSymFunctionTableAccess64)(HANDLE, DWORD64);
|
||||
typedef DWORD64 (WINAPI *FptrSymGetModuleBase64)(HANDLE, DWORD64);
|
||||
typedef BOOL (WINAPI *FptrStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
|
||||
typedef BOOL (WINAPI *FptrSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
|
||||
typedef BOOL (WINAPI *FptrMiniDumpWriteDump)(HANDLE, DWORD, HANDLE, MINIDUMP_TYPE, CONST PMINIDUMP_EXCEPTION_INFORMATION, CONST PMINIDUMP_USER_STREAM_INFORMATION, CONST PMINIDUMP_CALLBACK_INFORMATION);
|
||||
|
||||
|
||||
typedef struct {
|
||||
HMODULE libraryHandle;
|
||||
FptrSymInitialize SymInitialize;
|
||||
FptrSymFunctionTableAccess64 SymFunctionTableAccess64;
|
||||
FptrSymGetModuleBase64 SymGetModuleBase64;
|
||||
FptrStackWalk64 StackWalk64;
|
||||
FptrSymGetSymFromAddr64 SymGetSymFromAddr64;
|
||||
FptrMiniDumpWriteDump MiniDumpWriteDump;
|
||||
} debug_help_t;
|
||||
|
||||
|
||||
static void WIN_CloseDebugHelp( debug_help_t* debugHelp )
|
||||
{
|
||||
if (debugHelp->libraryHandle == NULL)
|
||||
return;
|
||||
|
||||
FreeLibrary(debugHelp->libraryHandle);
|
||||
debugHelp->libraryHandle = NULL;
|
||||
}
|
||||
|
||||
static qboolean WIN_OpenDebugHelp( debug_help_t* debugHelp )
|
||||
{
|
||||
debugHelp->libraryHandle = LoadLibraryA("dbghelp.dll");
|
||||
if (debugHelp->libraryHandle == NULL)
|
||||
return qfalse;
|
||||
|
||||
#define GET_FUNCTION(func) \
|
||||
debugHelp->func = (Fptr##func)GetProcAddress(debugHelp->libraryHandle, #func); \
|
||||
if (debugHelp->func == NULL) { \
|
||||
WIN_CloseDebugHelp(debugHelp); \
|
||||
return qfalse; \
|
||||
}
|
||||
|
||||
GET_FUNCTION(SymInitialize)
|
||||
GET_FUNCTION(SymFunctionTableAccess64)
|
||||
GET_FUNCTION(SymGetModuleBase64)
|
||||
GET_FUNCTION(StackWalk64)
|
||||
GET_FUNCTION(SymGetSymFromAddr64)
|
||||
GET_FUNCTION(MiniDumpWriteDump)
|
||||
|
||||
#undef GET_FUNCTION
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
static void WIN_DumpStackTrace( debug_help_t* debugHelp )
|
||||
{
|
||||
enum {
|
||||
BUFFER_SIZE = 1024,
|
||||
MAX_LEVELS = 256
|
||||
};
|
||||
|
||||
if (!debugHelp->SymInitialize(GetCurrentProcess(), NULL, TRUE))
|
||||
return;
|
||||
|
||||
CONTEXT context;
|
||||
#if id386
|
||||
ZeroMemory(&context, sizeof(CONTEXT));
|
||||
context.ContextFlags = CONTEXT_CONTROL;
|
||||
__asm {
|
||||
Label:
|
||||
mov[context.Ebp], ebp;
|
||||
mov[context.Esp], esp;
|
||||
mov eax, [Label];
|
||||
mov[context.Eip], eax;
|
||||
}
|
||||
#else // idx64
|
||||
RtlCaptureContext(&context);
|
||||
#endif
|
||||
|
||||
// Init the stack frame for this function
|
||||
STACKFRAME64 stackFrame;
|
||||
ZeroMemory(&stackFrame, sizeof(stackFrame));
|
||||
#if id386
|
||||
const DWORD machineType = IMAGE_FILE_MACHINE_I386;
|
||||
stackFrame.AddrPC.Offset = context.Eip;
|
||||
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
stackFrame.AddrFrame.Offset = context.Ebp;
|
||||
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
stackFrame.AddrStack.Offset = context.Esp;
|
||||
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#else // idx64
|
||||
const DWORD machineType = IMAGE_FILE_MACHINE_AMD64;
|
||||
stackFrame.AddrPC.Offset = context.Rip;
|
||||
stackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
stackFrame.AddrFrame.Offset = context.Rsp;
|
||||
stackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
stackFrame.AddrStack.Offset = context.Rsp;
|
||||
stackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#endif
|
||||
|
||||
JSONW_BeginNamedArray("stack_trace");
|
||||
|
||||
unsigned char buffer[sizeof(IMAGEHLP_SYMBOL64) + BUFFER_SIZE];
|
||||
IMAGEHLP_SYMBOL64* const symbol = (IMAGEHLP_SYMBOL64*)buffer;
|
||||
|
||||
int level = 1;
|
||||
while (level++ < (MAX_LEVELS + 1)) {
|
||||
BOOL result = debugHelp->StackWalk64(
|
||||
machineType, GetCurrentProcess(), GetCurrentThread(), &stackFrame, &context,
|
||||
NULL, debugHelp->SymFunctionTableAccess64, debugHelp->SymGetModuleBase64, NULL);
|
||||
if (!result || stackFrame.AddrPC.Offset == 0)
|
||||
break;
|
||||
|
||||
ZeroMemory(symbol, sizeof(IMAGEHLP_SYMBOL64) + BUFFER_SIZE);
|
||||
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
symbol->MaxNameLength = BUFFER_SIZE;
|
||||
|
||||
JSONW_BeginObject();
|
||||
JSONW_HexValue("program_counter", stackFrame.AddrPC.Offset);
|
||||
JSONW_HexValue("stack_pointer", stackFrame.AddrStack.Offset);
|
||||
JSONW_HexValue("frame_pointer", stackFrame.AddrFrame.Offset);
|
||||
JSONW_HexValue("return_address", stackFrame.AddrReturn.Offset);
|
||||
|
||||
DWORD64 displacement;
|
||||
result = debugHelp->SymGetSymFromAddr64(GetCurrentProcess(), stackFrame.AddrPC.Offset, &displacement, symbol);
|
||||
if (result)
|
||||
JSONW_StringValue("name", symbol->Name);
|
||||
|
||||
JSONW_EndObject();
|
||||
}
|
||||
|
||||
JSONW_EndArray();
|
||||
}
|
||||
|
||||
static BOOL WINAPI WIN_MiniDumpCallback(
|
||||
IN PVOID CallbackParam,
|
||||
IN CONST PMINIDUMP_CALLBACK_INPUT CallbackInput,
|
||||
IN OUT PMINIDUMP_CALLBACK_OUTPUT CallbackOutput )
|
||||
{
|
||||
// Keep everything except...
|
||||
if (CallbackInput->CallbackType != ModuleCallback)
|
||||
return TRUE;
|
||||
|
||||
// ...modules unreferenced by memory.
|
||||
if ((CallbackOutput->ModuleWriteFlags & ModuleReferencedByMemory) == 0) {
|
||||
CallbackOutput->ModuleWriteFlags &= ~ModuleWriteModule;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static qboolean WIN_CreateDirectoryIfNeeded( const char* path )
|
||||
{
|
||||
const BOOL success = CreateDirectoryA(path, NULL);
|
||||
if (success)
|
||||
return qtrue;
|
||||
|
||||
return GetLastError() == ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
|
||||
static char exc_reportFolderPath[MAX_PATH];
|
||||
|
||||
static void WIN_CreateDumpFilePath( char* buffer, const char* fileName, SYSTEMTIME* time )
|
||||
{
|
||||
char* const temp = getenv("TEMP");
|
||||
if (temp == NULL || !WIN_CreateDirectoryIfNeeded(va("%s\\cnq3_crash", temp)))
|
||||
return;
|
||||
|
||||
Q_strncpyz(exc_reportFolderPath, va("%s\\cnq3_crash\\", temp), sizeof(exc_reportFolderPath));
|
||||
StringCchPrintfA(
|
||||
buffer, MAX_PATH, "%s%s_%04d.%02d.%02d_%02d.%02d.%02d",
|
||||
exc_reportFolderPath, fileName, time->wYear, time->wMonth, time->wDay,
|
||||
time->wHour, time->wMinute, time->wSecond);
|
||||
}
|
||||
|
||||
static qboolean WIN_GetOSVersion( int* major, int* minor, int* revision )
|
||||
{
|
||||
enum { FILE_INFO_SIZE = 4096 };
|
||||
const DWORD fileInfoSize = min(FILE_INFO_SIZE, GetFileVersionInfoSizeA("kernel32.dll", NULL));
|
||||
if (fileInfoSize == 0)
|
||||
return qfalse;
|
||||
|
||||
char fileInfo[FILE_INFO_SIZE];
|
||||
if (!GetFileVersionInfoA("kernel32.dll", 0, fileInfoSize, fileInfo))
|
||||
return qfalse;
|
||||
|
||||
LPVOID osInfo = NULL;
|
||||
UINT osInfoSize = 0;
|
||||
if (!VerQueryValueA(&fileInfo[0], "\\", &osInfo, &osInfoSize) ||
|
||||
osInfoSize < sizeof(VS_FIXEDFILEINFO))
|
||||
return qfalse;
|
||||
|
||||
const VS_FIXEDFILEINFO* const versionInfo = (const VS_FIXEDFILEINFO*)osInfo;
|
||||
*major = HIWORD(versionInfo->dwProductVersionMS);
|
||||
*minor = LOWORD(versionInfo->dwProductVersionMS);
|
||||
*revision = HIWORD(versionInfo->dwProductVersionLS);
|
||||
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
static const char* WIN_GetExceptionCodeString( DWORD exceptionCode )
|
||||
{
|
||||
switch (exceptionCode) {
|
||||
case EXCEPTION_ACCESS_VIOLATION: return "The thread tried to read from or write to a virtual address for which it does not have the appropriate access.";
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "The thread tried to access an array element that is out of bounds and the underlying hardware supports bounds checking.";
|
||||
case EXCEPTION_BREAKPOINT: return "A breakpoint was encountered.";
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT: return "The thread tried to read or write data that is misaligned on hardware that does not provide alignment.";
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND: return "One of the operands in a floating-point operation is denormal.";
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "The thread tried to divide a floating-point value by a floating-point divisor of zero.";
|
||||
case EXCEPTION_FLT_INEXACT_RESULT: return "The result of a floating-point operation cannot be represented exactly as a decimal fraction.";
|
||||
case EXCEPTION_FLT_INVALID_OPERATION: return "This exception represents any floating-point exception not described by other codes.";
|
||||
case EXCEPTION_FLT_OVERFLOW: return "The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type.";
|
||||
case EXCEPTION_FLT_STACK_CHECK: return "The stack overflowed or underflowed as the result of a floating-point operation.";
|
||||
case EXCEPTION_FLT_UNDERFLOW: return "The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type.";
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION: return "The thread tried to execute an invalid instruction.";
|
||||
case EXCEPTION_IN_PAGE_ERROR: return "The thread tried to access a page that was not present and the system was unable to load the page.";
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO: return "The thread tried to divide an integer value by an integer divisor of zero.";
|
||||
case EXCEPTION_INT_OVERFLOW: return "The result of an integer operation caused a carry out of the most significant bit of the result.";
|
||||
case EXCEPTION_INVALID_DISPOSITION: return "An exception handler returned an invalid disposition to the exception dispatcher.";
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "The thread tried to continue execution after a noncontinuable exception occurred.";
|
||||
case EXCEPTION_PRIV_INSTRUCTION: return "The thread tried to execute an instruction whose operation is not allowed in the current machine mode.";
|
||||
case EXCEPTION_SINGLE_STEP: return "A trace trap or other single-instruction mechanism signaled that one instruction has been executed.";
|
||||
case EXCEPTION_STACK_OVERFLOW: return "The thread used up its stack.";
|
||||
default: return "Unknown exception code";
|
||||
}
|
||||
}
|
||||
|
||||
static const char* WIN_GetAccessViolationCodeString( DWORD avCode )
|
||||
{
|
||||
switch (avCode) {
|
||||
case 0: return "Read access violation";
|
||||
case 1: return "Write access violation";
|
||||
case 8: return "User-mode data execution prevention (DEP) violation";
|
||||
default: return "Unknown violation";
|
||||
}
|
||||
}
|
||||
|
||||
static void WIN_WriteTextData( const char* filePath, debug_help_t* debugHelp, EXCEPTION_RECORD* pExceptionRecord )
|
||||
{
|
||||
FILE* const file = fopen(filePath, "w");
|
||||
if (file == NULL)
|
||||
return;
|
||||
|
||||
JSONW_BeginFile(file);
|
||||
|
||||
WIN_DumpStackTrace(debugHelp);
|
||||
JSONW_HexValue("exception_code", pExceptionRecord->ExceptionCode);
|
||||
JSONW_StringValue("exception_description", WIN_GetExceptionCodeString(pExceptionRecord->ExceptionCode));
|
||||
|
||||
if (pExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
|
||||
pExceptionRecord->NumberParameters >= 2) {
|
||||
JSONW_StringValue("exception_details", "%s at address %s",
|
||||
WIN_GetAccessViolationCodeString(pExceptionRecord->ExceptionInformation[0]),
|
||||
Q_itohex(pExceptionRecord->ExceptionInformation[1], qtrue, qtrue));
|
||||
}
|
||||
|
||||
int osVersion[3];
|
||||
if (WIN_GetOSVersion(osVersion, osVersion + 1, osVersion + 2)) {
|
||||
JSONW_StringValue("windows_version", "%d.%d.%d", osVersion[0], osVersion[1], osVersion[2]);
|
||||
}
|
||||
|
||||
Crash_PrintToFile(__argv[0]);
|
||||
|
||||
JSONW_EndFile();
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
static void WIN_WriteMiniDump( const char* filePath, debug_help_t* debugHelp, EXCEPTION_POINTERS* pExceptionPointers )
|
||||
{
|
||||
const HANDLE dumpFile = CreateFileA(
|
||||
filePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
0, CREATE_ALWAYS, 0, 0);
|
||||
|
||||
if (dumpFile == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
|
||||
ZeroMemory(&exceptionInfo, sizeof(exceptionInfo));
|
||||
exceptionInfo.ThreadId = GetCurrentThreadId();
|
||||
exceptionInfo.ExceptionPointers = pExceptionPointers;
|
||||
exceptionInfo.ClientPointers = TRUE;
|
||||
|
||||
MINIDUMP_CALLBACK_INFORMATION callbackInfo;
|
||||
ZeroMemory(&callbackInfo, sizeof(callbackInfo));
|
||||
callbackInfo.CallbackRoutine = &WIN_MiniDumpCallback;
|
||||
callbackInfo.CallbackParam = NULL;
|
||||
|
||||
debugHelp->MiniDumpWriteDump(
|
||||
GetCurrentProcess(), GetCurrentProcessId(), dumpFile,
|
||||
(MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
|
||||
&exceptionInfo, NULL, &callbackInfo);
|
||||
|
||||
CloseHandle(dumpFile);
|
||||
}
|
||||
|
||||
static const char* WIN_GetFileName( const char* path )
|
||||
{
|
||||
const char* name = strrchr(path, '\\');
|
||||
if (name != path)
|
||||
return name + 1;
|
||||
|
||||
name = strrchr(path, '/');
|
||||
if (name != path)
|
||||
return name + 1;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
static qbool exc_reportWritten = qfalse;
|
||||
|
||||
static void WIN_WriteExceptionFilesImpl( EXCEPTION_POINTERS* pExceptionPointers )
|
||||
{
|
||||
debug_help_t debugHelp;
|
||||
if (!WIN_OpenDebugHelp(&debugHelp))
|
||||
return;
|
||||
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
|
||||
char modulePath[MAX_PATH];
|
||||
GetModuleFileNameA(GetModuleHandle(NULL), modulePath, sizeof(modulePath));
|
||||
|
||||
char dumpFilePath[MAX_PATH];
|
||||
WIN_CreateDumpFilePath(dumpFilePath, WIN_GetFileName(modulePath), &time);
|
||||
|
||||
WIN_WriteTextData(va("%s.json", dumpFilePath), &debugHelp, pExceptionPointers->ExceptionRecord);
|
||||
WIN_WriteMiniDump(va("%s.dmp", dumpFilePath), &debugHelp, pExceptionPointers);
|
||||
exc_reportWritten = qtrue;
|
||||
|
||||
WIN_CloseDebugHelp(&debugHelp);
|
||||
}
|
||||
|
||||
static int WINAPI WIN_WriteExceptionFiles( EXCEPTION_POINTERS* pExceptionPointers )
|
||||
{
|
||||
// No exception info?
|
||||
if (!pExceptionPointers) {
|
||||
__try {
|
||||
// Generate an exception to get a proper context.
|
||||
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL);
|
||||
} __except (WIN_WriteExceptionFiles(GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION) {}
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
// We have exception information now, so let's proceed.
|
||||
WIN_WriteExceptionFilesImpl(pExceptionPointers);
|
||||
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
//
|
||||
// The exception handler's job is to reset system settings that won't get reset
|
||||
// as part of the normal process clean-up by the OS.
|
||||
// It can't do any memory allocation or use any synchronization objects.
|
||||
// Ideally, we want it to be called before every abrupt application exit
|
||||
// and right after any legitimate crash.
|
||||
//
|
||||
// There are 2 cases where the function won't be called:
|
||||
//
|
||||
// 1. Termination through the debugger.
|
||||
// Our atexit handler never gets called.
|
||||
//
|
||||
// Work-around: Quit normally.
|
||||
//
|
||||
// 2. Breakpoints. The debugger has first-chance access and handles them.
|
||||
// Our exception handler doesn't get called.
|
||||
//
|
||||
// Work-around: None for debugging. Quit normally.
|
||||
//
|
||||
|
||||
static qbool exc_exitCalled = qfalse;
|
||||
|
||||
LONG CALLBACK WIN_HandleException( EXCEPTION_POINTERS* ep )
|
||||
{
|
||||
#if !DEDICATED
|
||||
__try {
|
||||
GLW_RestoreGamma();
|
||||
} __except(EXCEPTION_EXECUTE_HANDLER) {}
|
||||
#endif
|
||||
|
||||
__try {
|
||||
WIN_EndTimePeriod();
|
||||
} __except(EXCEPTION_EXECUTE_HANDLER) {}
|
||||
|
||||
if (exc_exitCalled || IsDebuggerPresent())
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
static const char* mbTitle = "CNQ3 Crash";
|
||||
static const char* mbMsg = "CNQ3 crashed!\n\nYes to generate a crash report\nNo to continue after attaching a debugger\nCancel to quit";
|
||||
const int result = MessageBoxA(NULL, mbMsg, mbTitle, MB_YESNOCANCEL | MB_ICONERROR);
|
||||
if (result == IDYES) {
|
||||
WIN_WriteExceptionFiles(ep);
|
||||
if (exc_reportWritten)
|
||||
ShellExecute(NULL, "open", exc_reportFolderPath, NULL, NULL, SW_SHOW);
|
||||
else
|
||||
MessageBoxA(NULL, "CNQ3's crash report generation failed!\nExiting now", mbTitle, MB_OK | MB_ICONERROR);
|
||||
} else if (result == IDNO && IsDebuggerPresent()) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
ExitProcess(666);
|
||||
}
|
||||
|
||||
void WIN_HandleExit( void )
|
||||
{
|
||||
exc_exitCalled = qtrue;
|
||||
WIN_HandleException(NULL);
|
||||
}
|
|
@ -49,7 +49,11 @@ void SNDDMA_Activate();
|
|||
|
||||
LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
|
||||
|
||||
// crash handling
|
||||
LONG CALLBACK WIN_HandleException( EXCEPTION_POINTERS* ep );
|
||||
void WIN_HandleExit();
|
||||
void GLW_RestoreGamma();
|
||||
void WIN_EndTimePeriod();
|
||||
|
||||
#define MAX_MONITOR_COUNT 16
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ WinVars_t g_wv;
|
|||
static qbool win_timePeriodActive = qfalse;
|
||||
|
||||
|
||||
static void Win_BeginTimePeriod()
|
||||
static void WIN_BeginTimePeriod()
|
||||
{
|
||||
if ( win_timePeriodActive )
|
||||
return;
|
||||
|
@ -51,7 +51,7 @@ static void Win_BeginTimePeriod()
|
|||
}
|
||||
|
||||
|
||||
static void Win_EndTimePeriod()
|
||||
void WIN_EndTimePeriod()
|
||||
{
|
||||
if ( !win_timePeriodActive )
|
||||
return;
|
||||
|
@ -88,7 +88,7 @@ void QDECL Sys_Error( const char *error, ... )
|
|||
Sys_SetErrorText( text );
|
||||
Sys_ShowConsole( 1, qtrue );
|
||||
|
||||
Win_EndTimePeriod();
|
||||
WIN_EndTimePeriod();
|
||||
|
||||
#ifndef DEDICATED
|
||||
IN_Shutdown();
|
||||
|
@ -111,7 +111,7 @@ void QDECL Sys_Error( const char *error, ... )
|
|||
|
||||
void Sys_Quit()
|
||||
{
|
||||
Win_EndTimePeriod();
|
||||
WIN_EndTimePeriod();
|
||||
#ifndef DEDICATED
|
||||
IN_Shutdown();
|
||||
#endif
|
||||
|
@ -537,7 +537,7 @@ static void Sys_Net_Restart_f( void )
|
|||
void Sys_Init()
|
||||
{
|
||||
// make sure the timer is high precision, otherwise NT gets 18ms resolution
|
||||
Win_BeginTimePeriod();
|
||||
WIN_BeginTimePeriod();
|
||||
|
||||
#ifndef DEDICATED
|
||||
Cmd_AddCommand( "in_restart", Sys_In_Restart_f );
|
||||
|
@ -697,75 +697,16 @@ int WINAPI WinMainImpl( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCm
|
|||
}
|
||||
|
||||
|
||||
//
|
||||
// The exception handler's job is to reset system settings that won't get reset
|
||||
// as part of the normal process clean-up by the OS.
|
||||
// It can't do any memory allocation or use any synchronization objects.
|
||||
// Ideally, we want it to be called before every abrupt application exit
|
||||
// and right after any legitimate crash.
|
||||
//
|
||||
// There are 2 cases where the function won't be called:
|
||||
//
|
||||
// 1. Termination through the debugger.
|
||||
// Our atexit handler never gets called.
|
||||
//
|
||||
// Work-around: Quit normally.
|
||||
//
|
||||
// 2. Breakpoints. The debugger has first-chance access and handles them.
|
||||
// Our exception handler doesn't get called.
|
||||
//
|
||||
// Work-around: None for debugging. Quit normally.
|
||||
//
|
||||
|
||||
|
||||
static qbool exitCalled = qfalse;
|
||||
|
||||
|
||||
LONG CALLBACK Win_HandleException( EXCEPTION_POINTERS* ep )
|
||||
{
|
||||
static const char* mbMsg = "CNQ3 crashed!\n\nOK to continue after attaching a debugger\nCancel to quit";
|
||||
|
||||
#if !DEDICATED
|
||||
__try {
|
||||
GLW_RestoreGamma();
|
||||
} __except( EXCEPTION_EXECUTE_HANDLER ) {}
|
||||
#endif
|
||||
|
||||
__try {
|
||||
Win_EndTimePeriod();
|
||||
} __except( EXCEPTION_EXECUTE_HANDLER ) {}
|
||||
|
||||
if ( exitCalled || IsDebuggerPresent() )
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
// ask if we want to debug the app
|
||||
if ( MessageBoxA( NULL, mbMsg, "Crash", MB_OKCANCEL | MB_ICONERROR ) == IDOK &&
|
||||
IsDebuggerPresent() )
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
#endif
|
||||
|
||||
ExitProcess( 666 );
|
||||
}
|
||||
|
||||
|
||||
static void Win_HandleExit( void )
|
||||
{
|
||||
exitCalled = qtrue;
|
||||
Win_HandleException( NULL );
|
||||
}
|
||||
|
||||
|
||||
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
|
||||
{
|
||||
// Register the exception handler for all threads present and future in this process.
|
||||
// 1 means we're inserting the handler at the front of the queue.
|
||||
// The debugger does still get first-chance access though.
|
||||
// The handler is always called in the context of the thread raising the exception.
|
||||
AddVectoredExceptionHandler( 1, Win_HandleException );
|
||||
AddVectoredExceptionHandler( 1, WIN_HandleException );
|
||||
|
||||
// Make sure we reset system settings even when someone calls exit.
|
||||
atexit( Win_HandleExit );
|
||||
atexit( WIN_HandleExit );
|
||||
|
||||
// SetErrorMode(0) gets the current flags
|
||||
// SEM_FAILCRITICALERRORS -> no abort/retry/fail errors
|
||||
|
|
Loading…
Reference in a new issue