Modules that crash during execution are now isolated and will not crash the entire process.
We accomplish this by wrapping the Execute() method in MbbsModule in a try/catch and handling exceptions that come from the execution of the CPU code. From there, it generates a crash report and saves it to a file with a unique file name.
MbbsHost is notified via the MessagingService that a module has crashed and needs to be disabled. Any user in the module is gracefully exited back to the main menu and their current "in module" session is cleaned up by invoking ExitModule() on the channel (cleans up VDA, clears input, etc.)
Currently the only way to re-enable the module is through restarting MBBSEmu (as the INIT routine would need to be re-run, and can only be run at startup) -- but this is something we can address in a future update.
GUI example of crash:
Example crash report Crash_HVSTW_20230930120627.txt:
This is an MBBSEmu Module Crash Report. This report contains detailed information
to help the MBBSEmu team diagnose and debug the issue this module ran into while
running within the emulator.
You can submit this report to the MBBSEmu team by opening an issue on GitHub at:
https://github.com/mbbsemu/MBBSEmu/issues
Please include the following information (copy/paste):
---------------------------------------------------------------
MBBSEmu vDevelopment (Overwritten in Github Actions) Module Crash Report
Date: 9/30/2023
Time: 12:06 PM
Operating System: Microsoft Windows 10.0.22621
Processor: X64
Module Identifier: HVSTW
Module Path: C:\dos\mount\modules\tw2002\
DLL Name: HVSTW.DLL
DLL Size: 652572
DLL Hash: 2436FCC4
Exception: SQLite Error 19: 'You modified a non-modifiable key_2!'.
Stack Trace:
at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
at Microsoft.Data.Sqlite.SqliteDataReader.NextResult()
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteNonQuery()
at MBBSEmu.Btrieve.BtrieveFileProcessor.Update(UInt32 offset, Byte[] recordData) in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\Btrieve\BtrieveFileProcessor.cs:line 559
at MBBSEmu.Btrieve.BtrieveFileProcessor.Update(Byte[] recordData) in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\Btrieve\BtrieveFileProcessor.cs:line 510
at MBBSEmu.HostProcess.ExportedModules.Majorbbs.updateBtv() in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\HostProcess\ExportedModules\Majorbbs.cs:line 2707
at MBBSEmu.HostProcess.ExportedModules.Majorbbs.updbtv() in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\HostProcess\ExportedModules\Majorbbs.cs:line 6571
at MBBSEmu.HostProcess.ExportedModules.Majorbbs.Invoke(UInt16 ordinal, Boolean offsetsOnly) in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\HostProcess\ExportedModules\Majorbbs.cs:line 745
at MBBSEmu.HostProcess.ExecutionUnits.ExecutionUnit.ExternalFunctionDelegate(UInt16 ordinal, UInt16 functionOrdinal) in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\HostProcess\ExecutionUnits\ExecutionUnit.cs:line 76
at MBBSEmu.CPU.CpuCore.Op_Call() in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\CPU\CPUCore.cs:line 2955
at MBBSEmu.CPU.CpuCore.Tick() in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\CPU\CPUCore.cs:line 432
at MBBSEmu.HostProcess.ExecutionUnits.ExecutionUnit.Execute(FarPtr entryPoint, UInt16 channelNumber, Boolean simulateCallFar, Boolean bypassState, Queue`1 initialStackValues, UInt16 initialStackPointer) in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\HostProcess\ExecutionUnits\ExecutionUnit.cs:line 126
at MBBSEmu.Module.MbbsModule.Execute(FarPtr entryPoint, UInt16 channelNumber, Boolean simulateCallFar, Boolean bypassSetState, Queue`1 initialStackValues, UInt16 initialStackPointer) in C:\Users\eric\Documents\GitHub\MBBSEmu\MBBSEmu\Module\MBBSModule.cs:line 315
CPU Instruction:
call far ptr 0FFFFh:026Dh
CPU Registers:
AX=1000 BX=1000 CX=0008 DX=CD70 DS=003C ES=1000
SI=E43D DI=0028 SS=0000 IP=0278 SP=FFC2 BP=FFC2
F=cZso
Module Memory Stack:
-------------------------------------------------------------------------
0 bytes, 0xFFC2 -> 0xFFC2
-------------------------------------------------------------------------
No Data to Display
Modules that crash during execution are now isolated and will not crash the entire process.
We accomplish this by wrapping the
Execute()
method inMbbsModule
in atry/catch
and handling exceptions that come from the execution of the CPU code. From there, it generates a crash report and saves it to a file with a unique file name.MbbsHost
is notified via theMessagingService
that a module has crashed and needs to be disabled. Any user in the module is gracefully exited back to the main menu and their current "in module" session is cleaned up by invokingExitModule()
on the channel (cleans up VDA, clears input, etc.)Currently the only way to re-enable the module is through restarting MBBSEmu (as the INIT routine would need to be re-run, and can only be run at startup) -- but this is something we can address in a future update.
GUI example of crash:![image](https://github.com/mbbsemu/MBBSEmu/assets/1139047/9faf95da-ddf6-4753-bc3b-542b24581aa1)
Example crash report
Crash_HVSTW_20230930120627.txt
: