From fa9dcfae3538380e5474353eebc005faf950e2dd Mon Sep 17 00:00:00 2001 From: nattthebear Date: Sun, 11 Jun 2017 07:47:16 -0400 Subject: [PATCH] waterbox: When compiled in debug mode, forcibly evict self at every opportunity. This can be used to catch bugs using pointers past their valid time. Catch and fix two such bugs in the libsnes wrapper. --- BizHawk.Common/Win32Hacks.cs | 8 +++ .../Consoles/Nintendo/SNES/LibsnesApi.cs | 8 ++- .../Nintendo/SNES/LibsnesCore.ISaveRam.cs | 55 +++++++++++-------- BizHawk.Emulation.Cores/Waterbox/PeRunner.cs | 4 +- BizHawk.Emulation.Cores/Waterbox/Swappable.cs | 20 +++++++ 5 files changed, 67 insertions(+), 28 deletions(-) diff --git a/BizHawk.Common/Win32Hacks.cs b/BizHawk.Common/Win32Hacks.cs index 7126f0e7b2..9770518d38 100644 --- a/BizHawk.Common/Win32Hacks.cs +++ b/BizHawk.Common/Win32Hacks.cs @@ -458,6 +458,14 @@ namespace BizHawk.Common { DeleteFileW(path + ":Zone.Identifier"); } + + [DllImport("kernel32.dll")] + static extern bool IsDebuggerPresent(); + + public static bool IsDebuggerReallyPresent() + { + return IsDebuggerPresent(); + } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs index 62ae3c73a7..545db1b271 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs @@ -62,9 +62,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES PlainHeapSizeKB = 2 * 1024, // TODO: wasn't there more in here? SealedHeapSizeKB = 128 * 1024 }); - - _core = BizInvoker.GetInvoker(_exe, _exe); - _comm = (CommStruct*)_core.DllInit().ToPointer(); + using (_exe.EnterExit()) + { + _core = BizInvoker.GetInvoker(_exe, _exe); + _comm = (CommStruct*)_core.DllInit().ToPointer(); + } } public void Dispose() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs index 0cf3e94476..67753f69d5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.ISaveRam.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using BizHawk.Emulation.Common; +using BizHawk.Common; namespace BizHawk.Emulation.Cores.Nintendo.SNES { @@ -13,40 +14,46 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES public byte[] CloneSaveRam() { - byte* buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); - var size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); - if (buf == null) + using (Api.EnterExit()) { - buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); - size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); - } + byte* buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); + var size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); + if (buf == null) + { + buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); + size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); + } - var ret = new byte[size]; - Marshal.Copy((IntPtr)buf, ret, 0, size); - return ret; + var ret = new byte[size]; + Marshal.Copy((IntPtr)buf, ret, 0, size); + return ret; + } } public void StoreSaveRam(byte[] data) { - byte* buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); - var size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); - if (buf == null) + using (Api.EnterExit()) { - buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); - size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); - } + byte* buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); + var size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM); + if (buf == null) + { + buf = Api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); + size = Api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.SGB_CARTRAM); + } - if (size == 0) - { - return; - } + if (size == 0) + { + return; + } - if (size != data.Length) - { - throw new InvalidOperationException("Somehow, we got a mismatch between saveram size and what bsnes says the saveram size is"); - } + if (size != data.Length) + { + throw new InvalidOperationException("Somehow, we got a mismatch between saveram size and what bsnes says the saveram size is"); + } - Marshal.Copy(data, 0, (IntPtr)buf, size); + Marshal.Copy(data, 0, (IntPtr)buf, size); + } } } } diff --git a/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs b/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs index 8852d3588d..85a59ba762 100644 --- a/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs +++ b/BizHawk.Emulation.Cores/Waterbox/PeRunner.cs @@ -734,8 +734,10 @@ namespace BizHawk.Emulation.Cores.Waterbox _syscalls.Init(); Console.WriteLine("About to enter unmanaged code"); - if (System.Diagnostics.Debugger.IsAttached) + if (Win32Hacks.IsDebuggerReallyPresent() && !System.Diagnostics.Debugger.IsAttached) { + // this means that GDB or another unconventional debugger is attached. + // if that's the case, and it's observing this core, it probably wants a break System.Diagnostics.Debugger.Break(); } diff --git a/BizHawk.Emulation.Cores/Waterbox/Swappable.cs b/BizHawk.Emulation.Cores/Waterbox/Swappable.cs index 25d3d8d0e9..2eff85f9a7 100644 --- a/BizHawk.Emulation.Cores/Waterbox/Swappable.cs +++ b/BizHawk.Emulation.Cores/Waterbox/Swappable.cs @@ -54,6 +54,12 @@ namespace BizHawk.Emulation.Cores.Waterbox { public object Sync; private WeakReference LoadedRef = new WeakReference(null); +#if DEBUG + /// + /// recursive lock count + /// + public int LockCount; +#endif public Swappable Loaded { get @@ -78,6 +84,10 @@ namespace BizHawk.Emulation.Cores.Waterbox public void Enter() { Monitor.Enter(_currentLockInfo.Sync); +#if DEBUG + if (_currentLockInfo.LockCount++ != 0 && _currentLockInfo.Loaded != this) + throw new InvalidOperationException("Woops!"); +#endif if (_currentLockInfo.Loaded != this) { if (_currentLockInfo.Loaded != null) @@ -93,6 +103,16 @@ namespace BizHawk.Emulation.Cores.Waterbox /// public void Exit() { +#if DEBUG + // when debugging, if we're releasing the lock then deactivate + if (_currentLockInfo.LockCount-- == 1) + { + if (_currentLockInfo.Loaded != this) + throw new InvalidOperationException("Woops!"); + DeactivateInternal(); + _currentLockInfo.Loaded = null; + } +#endif Monitor.Exit(_currentLockInfo.Sync); }