CrashWatcher
The crash window of CatLib: a small Windows program that waits for the game to close and, if it crashed or froze, shows what happened and keeps a report. Comes with CatLib.Changelog
1.2.0
Reads session files of format 2, which CatLib 0.7.4 writes.
- The end of the BepInEx log is complete: CatLib keeps a copy of the last 64 KB of the log in memory, and the watcher reads it
at the moment of a crash or a hang. The lines the log file did not have yet go into the report and at the end of the copied
LogOutput.log. Before, the last lines before a native crash were often missing, the message of a crash command of the developer menu too. - Text on the stack: the report lists the strings of .NET and IL2CPP code in the registers and on the stack of the crashing thread, the window the first three. A crash in .NET code shows only "JIT-compiled .NET code" in the stacks, the texts usually say what it did.
- An access violation inside .NET code, which closes the game, is noted when it is raised, so its report has the text on the stack and the log.
- Unfinished operations: what CatLib or a mod had begun and not finished when the game closed, from the session file, in the report and the window.
- Known causes: a line in the report and the window names a cause found before, such as Steam crashing while the game shut it down.
- A report without a memory dump no longer takes one of the three places for dumps, and a game closed through its console window keeps its report without a dump.
- The module list is read once per loaded DLL instead of at every exception that may become a crash.
1.1.0
- Reports name the method of the game a crash happened in, for example
EntityInteractableStore.Start() + 0x60instead of onlyGameAssembly.dll + 0x4d5f30. The names come fromBepInEx/interop/MethodAddressToToken.db, which BepInEx writes with the interop assemblies; the watcher reads it only after a crash and checks that it belongs to the running game build. - Every report of a crash or a hang has the stacks of all game threads: the crashing thread and the game thread in
report.txt, every thread instacks.txt, threads with the same stack grouped. The watcher walks them with the unwind data of the game's DLLs while the game is stopped at the crash, or pauses it for a moment when it hangs. - Frames outside the game's methods say what they are: the IL2CPP runtime, generic code of IL2CPP, a function a DLL exports,
or .NET code compiled while the game runs. Frames found by searching the stack are marked with
?. - When the crash is in Windows, Unity or another DLL, the report and the window also name the nearest method of the game
on the crashing thread, for example the method that called
RaiseException. - A call through a bad pointer shows the bad address as "not code" and the frame that made the call below it.
- C++ names of exported functions are shown readable, for example
il2cpp_baselib::Baselib_SystemFutex_Wait. - The window shows the method next to the module in the details.
- Generic methods have names: the watcher reads the IL2CPP metadata (
global-metadata.dat) and the registrations inGameAssembly.dllitself, so a frame showsDictionary<string, int>.TryInsert(string, int, InsertionBehavior)instead of "IL2CPP code that is not a game method". It reads metadata versions 29 to 106 and uses them only when they agree with the BepInEx method map. - Functions of
UnityPlayer.dll,baselib.dll, the game's.exe,ntdll.dll,KERNELBASE.dll,kernel32.dllandcoreclr.dllhave names from their public symbols. The watcher downloads them from the symbol servers of Unity and Microsoft once per version, half a minute after the game starts, intoBepInEx/CatLib/Symbols;--no-symbolsturns the downloads off. A.pdbnext to a DLL is used first. - C++ names are shown the way Windows undecorates them, for example
ScriptingInvocation::Invoke. - Symbols are also downloaded for
user32.dll,win32u.dll,d3d11.dll,dxgi.dll, the C runtime andclrjit.dll. - When a crash is inside Windows or the C runtime, such as
RaiseException, the report, the window andwatcher.logname the first frame outside them in a "Called from" line. - Methods interop gave a made-up name, like
Method_Private_IEnumerator_PDM_0, show their original name from the IL2CPP metadata with the interop name after it. - Code IL2CPP shares between all type arguments shows the generic parameter names,
QuickSort<T>(Span<T>, …), and nested types of generic types read like C#,Dictionary<int, string>.Enumerator.
1.0.2
- A stack overflow keeps its memory dump. .NET closes the game with 0x800703E9 after a stack overflow, the watcher took that for another exit and removed the dump it wrote when the overflow happened. Now that dump stays, and the report names the place of the overflow instead of "no crash record". The same goes for any crash within 10 seconds after an exception the game did not survive.
- The exit code 0x800703E9 is named as a stack overflow.
- The game no longer closes when it stops at a breakpoint. Steam and other libraries stop there only when a debugger follows the game, and the watcher is one, so the game closed with 0x80000003 where it goes on without the watcher, for example when the host returned to the menu right after a player left. The watcher now lets the game go on and writes the first stops into its log.
1.0.0
- A package of its own. Until now the crash watcher came inside CatLib (0.6.0 and 0.6.1); CatLib 0.6.2 and later depend on this package
and find the program in any folder of
BepInEx/plugins. - The crash window in every language of the game, with a different cozy phrase every time, the details, and buttons to open the report folder and to copy the report.
- A report folder in
BepInEx/CatLib/Crashesfor every crash: the report for mod authors, both game logs, the recorded session and a memory dump. - Memory dumps of the moment of the crash, also for crashes that Unity closes itself on the game thread.
- A hang while quitting is noticed after 8 seconds, with a dump of every thread at that moment.
- The report names its own version and warns when CatLib records more than this version can read.
