Further support for identifying modules and threads
A slight restructure too
This commit is contained in:
parent
92d6635c4e
commit
ff54eb8b9e
|
@ -63,18 +63,24 @@
|
||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Debugger.cs" />
|
<Compile Include="Debugger\Debugger.cs" />
|
||||||
<Compile Include="DebuggerEventListener.cs" />
|
<Compile Include="Debugger\DebuggerEventListener.cs" />
|
||||||
<Compile Include="DebuggerModule.cs" />
|
<Compile Include="Debugger\DebuggerInstance.cs" />
|
||||||
<Compile Include="DebuggerProcess.cs" />
|
<Compile Include="Debugger\DebuggerModule.cs" />
|
||||||
<Compile Include="DebuggerCallstack.cs" />
|
<Compile Include="Debugger\DebuggerProcess.cs" />
|
||||||
<Compile Include="DebuggerThread.cs" />
|
<Compile Include="Debugger\DebuggerCallstack.cs" />
|
||||||
|
<Compile Include="DebuggerSymbols\DebuggerSymbolProvider.cs" />
|
||||||
|
<Compile Include="Debugger\DebuggerSymbolServer.cs" />
|
||||||
|
<Compile Include="Debugger\DebuggerThread.cs" />
|
||||||
<Compile Include="Form1.cs">
|
<Compile Include="Form1.cs">
|
||||||
<SubType>Form</SubType>
|
<SubType>Form</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Form1.Designer.cs">
|
<Compile Include="Form1.Designer.cs">
|
||||||
<DependentUpon>Form1.cs</DependentUpon>
|
<DependentUpon>Form1.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="DebuggerSymbols\HLECache\HLECacheFile.cs" />
|
||||||
|
<Compile Include="DebuggerSymbols\HLECache\HLECacheProvider.cs" />
|
||||||
|
<Compile Include="DebuggerSymbols\HLECache\Utils\INIReader.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ThreadHelpers.cs" />
|
<Compile Include="ThreadHelpers.cs" />
|
||||||
|
@ -91,7 +97,6 @@
|
||||||
<Compile Include="Win32\Debugging\LOAD_DLL_DEBUG_INFO.cs" />
|
<Compile Include="Win32\Debugging\LOAD_DLL_DEBUG_INFO.cs" />
|
||||||
<Compile Include="Win32\Debugging\NativeMethods.cs" />
|
<Compile Include="Win32\Debugging\NativeMethods.cs" />
|
||||||
<Compile Include="Win32\Debugging\OUTPUT_DEBUG_STRING_INFO.cs" />
|
<Compile Include="Win32\Debugging\OUTPUT_DEBUG_STRING_INFO.cs" />
|
||||||
<Compile Include="Win32\Debugging\PTHREAD_START_ROUTINE.cs" />
|
|
||||||
<Compile Include="Win32\Debugging\RIP_INFO.cs" />
|
<Compile Include="Win32\Debugging\RIP_INFO.cs" />
|
||||||
<Compile Include="Win32\Debugging\UNLOAD_DLL_DEBUG_INFO.cs" />
|
<Compile Include="Win32\Debugging\UNLOAD_DLL_DEBUG_INFO.cs" />
|
||||||
<Compile Include="Win32\Handles\NativeMethods.cs" />
|
<Compile Include="Win32\Handles\NativeMethods.cs" />
|
||||||
|
|
|
@ -7,11 +7,10 @@ using System.Text;
|
||||||
using WinProcesses = VsChromium.Core.Win32.Processes;
|
using WinProcesses = VsChromium.Core.Win32.Processes;
|
||||||
using WinDebug = VsChromium.Core.Win32.Debugging;
|
using WinDebug = VsChromium.Core.Win32.Debugging;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using CxbxDebugger.TheadHelpers;
|
|
||||||
|
|
||||||
namespace CxbxDebugger
|
namespace CxbxDebugger
|
||||||
{
|
{
|
||||||
public class Debugger : IDisposable
|
public class Debugger : DebuggerEventListener, IDisposable
|
||||||
{
|
{
|
||||||
enum RunState
|
enum RunState
|
||||||
{
|
{
|
||||||
|
@ -32,28 +31,30 @@ namespace CxbxDebugger
|
||||||
|
|
||||||
RunState State = RunState.NotLaunched;
|
RunState State = RunState.NotLaunched;
|
||||||
|
|
||||||
List<DebuggerModule> ModuleList = new List<DebuggerModule>();
|
DebuggerInstance DebugInstance;
|
||||||
List<DebuggerThread> ThreadList = new List<DebuggerThread>();
|
|
||||||
List<DebuggerProcess> ProcessList = new List<DebuggerProcess>();
|
|
||||||
|
|
||||||
List<DebuggerEventListener> EventListeners = new List<DebuggerEventListener>();
|
List<DebuggerEventListener> EventListeners = new List<DebuggerEventListener>();
|
||||||
|
|
||||||
CONTEXT_x86 lastReadCtxt = new CONTEXT_x86
|
DebuggerSymbolServer SymbolSrv;
|
||||||
|
|
||||||
|
private void Init()
|
||||||
{
|
{
|
||||||
ContextFlags =
|
DebugInstance = null;
|
||||||
ContextFlags.CONTEXT_i386 |
|
|
||||||
ContextFlags.CONTEXT_CONTROL |
|
SymbolSrv = new DebuggerSymbolServer();
|
||||||
ContextFlags.CONTEXT_INTEGER |
|
|
||||||
ContextFlags.CONTEXT_SEGMENTS
|
RegisterEventListener(this);
|
||||||
};
|
}
|
||||||
|
|
||||||
public Debugger()
|
public Debugger()
|
||||||
{
|
{
|
||||||
//
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Debugger(string[] x_args)
|
public Debugger(string[] x_args)
|
||||||
{
|
{
|
||||||
|
Init();
|
||||||
|
|
||||||
if (x_args == null)
|
if (x_args == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -82,20 +83,17 @@ namespace CxbxDebugger
|
||||||
|
|
||||||
public void Break()
|
public void Break()
|
||||||
{
|
{
|
||||||
// This will ONLY suspend the main process
|
if (DebugInstance != null)
|
||||||
// Not ideal if there are multiple processes in play
|
|
||||||
|
|
||||||
if (!hThread.IsInvalid)
|
|
||||||
{
|
{
|
||||||
WinProcesses.NativeMethods.SuspendThread(hThread.DangerousGetHandle());
|
DebugInstance.MainProcess.Suspend();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume()
|
public void Resume()
|
||||||
{
|
{
|
||||||
if (!hThread.IsInvalid)
|
if (DebugInstance != null)
|
||||||
{
|
{
|
||||||
WinProcesses.NativeMethods.ResumeThread(hThread.DangerousGetHandle());
|
DebugInstance.MainProcess.Resume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +109,26 @@ namespace CxbxDebugger
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string CxbxDebuggerPrefix = "CxbxDebugger! ";
|
||||||
|
|
||||||
|
|
||||||
|
public override void OnDebugOutput(string Message)
|
||||||
|
{
|
||||||
|
if (Message.StartsWith(CxbxDebuggerPrefix))
|
||||||
|
{
|
||||||
|
SetupHLECacheProvider(Message.Substring(CxbxDebuggerPrefix.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupHLECacheProvider(string Filename)
|
||||||
|
{
|
||||||
|
var Provider = new HLECacheProvider();
|
||||||
|
if( Provider.Load(Filename) )
|
||||||
|
{
|
||||||
|
SymbolSrv.RegisterProvider(Provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool Launch()
|
public bool Launch()
|
||||||
{
|
{
|
||||||
if (CanLaunch() == false)
|
if (CanLaunch() == false)
|
||||||
|
@ -137,9 +155,28 @@ namespace CxbxDebugger
|
||||||
if (bRet == true)
|
if (bRet == true)
|
||||||
{
|
{
|
||||||
// Store these so they can be marshalled, and closed correctly
|
// Store these so they can be marshalled, and closed correctly
|
||||||
|
// TODO Move to Debugger* classes
|
||||||
hProcess = new WinProcesses.SafeProcessHandle(stProcessInfo.hProcess);
|
hProcess = new WinProcesses.SafeProcessHandle(stProcessInfo.hProcess);
|
||||||
hThread = new WinProcesses.SafeThreadHandle(stProcessInfo.hThread);
|
hThread = new WinProcesses.SafeThreadHandle(stProcessInfo.hThread);
|
||||||
|
|
||||||
|
var Process = new DebuggerProcess();
|
||||||
|
|
||||||
|
Process.Handle = stProcessInfo.hProcess;
|
||||||
|
Process.ProcessID = (uint)stProcessInfo.dwProcessId;
|
||||||
|
|
||||||
|
var Thread = new DebuggerThread(Process);
|
||||||
|
|
||||||
|
Thread.Handle = stProcessInfo.hThread;
|
||||||
|
Thread.ThreadID = NativeMethods.GetThreadId(Thread.Handle);
|
||||||
|
|
||||||
|
Process.Threads.Add(Thread);
|
||||||
|
Process.MainThread = Thread;
|
||||||
|
|
||||||
|
DebugInstance = new DebuggerInstance(Process);
|
||||||
|
|
||||||
|
// Register the instance to track thread creation
|
||||||
|
RegisterEventListener(DebugInstance);
|
||||||
|
|
||||||
State = RunState.Running;
|
State = RunState.Running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,8 +210,10 @@ namespace CxbxDebugger
|
||||||
|
|
||||||
private string ReadProcessString(IntPtr NamePtr, uint Length, bool Unicode)
|
private string ReadProcessString(IntPtr NamePtr, uint Length, bool Unicode)
|
||||||
{
|
{
|
||||||
|
string resultStr = "";
|
||||||
|
|
||||||
if (Length == 0 || Length > 512)
|
if (Length == 0 || Length > 512)
|
||||||
return "";
|
return resultStr;
|
||||||
|
|
||||||
byte[] buffer = new byte[Length];
|
byte[] buffer = new byte[Length];
|
||||||
|
|
||||||
|
@ -193,15 +232,18 @@ namespace CxbxDebugger
|
||||||
// FIXME Questionable Unicode support here
|
// FIXME Questionable Unicode support here
|
||||||
if (Unicode)
|
if (Unicode)
|
||||||
{
|
{
|
||||||
return Encoding.Unicode.GetString(buffer, 0, (int)numRead);
|
resultStr = Encoding.Unicode.GetString(buffer, 0, (int)numRead);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Encoding.ASCII.GetString(buffer, 0, (int)numRead);
|
// TODO Investigate why the read length includes the nul-terminator
|
||||||
|
if (numRead > 1) --numRead;
|
||||||
|
|
||||||
|
resultStr = Encoding.ASCII.GetString(buffer, 0, (int)numRead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return resultStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint GetMemory(IntPtr addr)
|
private uint GetMemory(IntPtr addr)
|
||||||
|
@ -223,61 +265,46 @@ namespace CxbxDebugger
|
||||||
return BitConverter.ToUInt32(buffer, 0);
|
return BitConverter.ToUInt32(buffer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BuildCallstack()
|
private void HandleCreateThread(WinDebug.DEBUG_EVENT DebugEvent)
|
||||||
{
|
{
|
||||||
// Skip if we have nothing to report to
|
var DebugInfo = DebugEvent.CreateThread;
|
||||||
if (EventListeners.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool Result = NativeMethods.GetThreadContext(hThread.DangerousGetHandle(), ref lastReadCtxt);
|
var Thread = new DebuggerThread(DebugInstance.MainProcess);
|
||||||
if (!Result)
|
|
||||||
{
|
|
||||||
// TODO: Handle this
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ebp = lastReadCtxt.ebp;
|
Thread.Handle = DebugInfo.hThread;
|
||||||
|
Thread.ThreadID = Thread.ThreadID = NativeMethods.GetThreadId(Thread.Handle);
|
||||||
var Callstack = new DebuggerCallstack();
|
|
||||||
Callstack.AddFrame(new DebuggerStackFrame(new IntPtr(ebp), new IntPtr(lastReadCtxt.eip)));
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
var ReturnAddr = GetMemory(new IntPtr(ebp + 4));
|
|
||||||
ebp = GetMemory(new IntPtr(ebp));
|
|
||||||
|
|
||||||
if (ebp == 0 || ReturnAddr == ebp)
|
|
||||||
break;
|
|
||||||
|
|
||||||
Callstack.AddFrame(new DebuggerStackFrame(new IntPtr(ebp), new IntPtr(ReturnAddr)));
|
|
||||||
}
|
|
||||||
while (!Callstack.HasEnoughFrames);
|
|
||||||
|
|
||||||
foreach (DebuggerEventListener EvtListener in EventListeners)
|
foreach (DebuggerEventListener EvtListener in EventListeners)
|
||||||
{
|
{
|
||||||
EvtListener.OnCallstack(Callstack);
|
EvtListener.OnThreadCreate(Thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleExitThread(WinDebug.DEBUG_EVENT DebugEvent)
|
||||||
|
{
|
||||||
|
var DebugInfo = DebugEvent.ExitThread;
|
||||||
|
|
||||||
|
var TargetThread = DebugInstance.MainProcess.Threads.Find(Thread => Thread.ThreadID == DebugEvent.dwThreadId);
|
||||||
|
//uint ExitCode = DebugInfo.dwExitCode;
|
||||||
|
|
||||||
|
if (TargetThread != null)
|
||||||
|
{
|
||||||
|
foreach (DebuggerEventListener EvtListener in EventListeners)
|
||||||
|
{
|
||||||
|
EvtListener.OnThreadExit(TargetThread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleCreateProcess(WinDebug.DEBUG_EVENT DebugEvent)
|
private void HandleCreateProcess(WinDebug.DEBUG_EVENT DebugEvent)
|
||||||
{
|
{
|
||||||
// TODO Investigate why this is throwing an exception converting to this type
|
var DebugInfo = DebugEvent.CreateProcessInfo;
|
||||||
//var DebugInfo = DebugEvent.CreateProcessInfo;
|
|
||||||
|
|
||||||
var Process = new DebuggerProcess();
|
// Stub
|
||||||
|
|
||||||
//DebugInfo.hFile
|
|
||||||
//DebugInfo.hProcess
|
|
||||||
//DebugInfo.hThread
|
|
||||||
|
|
||||||
//DebugInfo.lpBaseOfImage
|
|
||||||
//DebugInfo.lpStartAddress
|
|
||||||
|
|
||||||
ProcessList.Add(Process);
|
|
||||||
|
|
||||||
foreach (DebuggerEventListener EvtListener in EventListeners)
|
foreach (DebuggerEventListener EvtListener in EventListeners)
|
||||||
{
|
{
|
||||||
EvtListener.OnProcessCreate(Process);
|
EvtListener.OnProcessCreate(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,7 +312,7 @@ namespace CxbxDebugger
|
||||||
{
|
{
|
||||||
var DebugInfo = DebugEvent.ExitProcess;
|
var DebugInfo = DebugEvent.ExitProcess;
|
||||||
|
|
||||||
//DebugInfo.dwExitCode
|
// Stub
|
||||||
|
|
||||||
foreach (DebuggerEventListener EvtListener in EventListeners)
|
foreach (DebuggerEventListener EvtListener in EventListeners)
|
||||||
{
|
{
|
||||||
|
@ -300,8 +327,7 @@ namespace CxbxDebugger
|
||||||
var Module = new DebuggerModule();
|
var Module = new DebuggerModule();
|
||||||
|
|
||||||
Module.Path = ResolveProcessPath(DebugInfo.hFile);
|
Module.Path = ResolveProcessPath(DebugInfo.hFile);
|
||||||
|
Module.ImageBase = DebugInfo.lpBaseOfDll;
|
||||||
ModuleList.Add(Module);
|
|
||||||
|
|
||||||
foreach (DebuggerEventListener EvtListener in EventListeners)
|
foreach (DebuggerEventListener EvtListener in EventListeners)
|
||||||
{
|
{
|
||||||
|
@ -313,11 +339,14 @@ namespace CxbxDebugger
|
||||||
{
|
{
|
||||||
var DebugInfo = DebugEvent.UnloadDll;
|
var DebugInfo = DebugEvent.UnloadDll;
|
||||||
|
|
||||||
//DebugInfo.lpBaseOfDll
|
var TargetModule = DebugInstance.MainProcess.Modules.Find(Module => Module.ImageBase == DebugInfo.lpBaseOfDll);
|
||||||
|
|
||||||
|
if (TargetModule != null)
|
||||||
|
{
|
||||||
foreach (DebuggerEventListener EvtListener in EventListeners)
|
foreach (DebuggerEventListener EvtListener in EventListeners)
|
||||||
{
|
{
|
||||||
EvtListener.OnModuleUnloaded(null);
|
EvtListener.OnModuleUnloaded(TargetModule);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +364,7 @@ namespace CxbxDebugger
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
WinDebug.DEBUG_EVENT DbgEvt;
|
WinDebug.DEBUG_EVENT DbgEvt = new WinDebug.DEBUG_EVENT();
|
||||||
|
|
||||||
WinDebug.CONTINUE_STATUS ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
|
WinDebug.CONTINUE_STATUS ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
|
||||||
bool bContinue = true;
|
bool bContinue = true;
|
||||||
|
@ -353,17 +382,37 @@ namespace CxbxDebugger
|
||||||
|
|
||||||
switch (DbgEvt.dwDebugEventCode)
|
switch (DbgEvt.dwDebugEventCode)
|
||||||
{
|
{
|
||||||
|
case WinDebug.DEBUG_EVENT_CODE.EXCEPTION_DEBUG_EVENT:
|
||||||
|
{
|
||||||
|
// TODO: Handle
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WinDebug.DEBUG_EVENT_CODE.CREATE_THREAD_DEBUG_EVENT:
|
||||||
|
{
|
||||||
|
HandleCreateThread(DbgEvt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WinDebug.DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT:
|
case WinDebug.DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT:
|
||||||
{
|
{
|
||||||
|
// TODO: Limit support for multiple processes
|
||||||
HandleCreateProcess(DbgEvt);
|
HandleCreateProcess(DbgEvt);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case WinDebug.DEBUG_EVENT_CODE.EXIT_THREAD_DEBUG_EVENT:
|
||||||
|
{
|
||||||
|
HandleExitThread(DbgEvt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case WinDebug.DEBUG_EVENT_CODE.EXIT_PROCESS_DEBUG_EVENT:
|
case WinDebug.DEBUG_EVENT_CODE.EXIT_PROCESS_DEBUG_EVENT:
|
||||||
{
|
{
|
||||||
HandleExitProcess(DbgEvt);
|
HandleExitProcess(DbgEvt);
|
||||||
|
|
||||||
// XX temp
|
// XX Temporary
|
||||||
bContinue = false;
|
bContinue = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -371,29 +420,24 @@ namespace CxbxDebugger
|
||||||
case WinDebug.DEBUG_EVENT_CODE.LOAD_DLL_DEBUG_EVENT:
|
case WinDebug.DEBUG_EVENT_CODE.LOAD_DLL_DEBUG_EVENT:
|
||||||
{
|
{
|
||||||
HandleLoadDll(DbgEvt);
|
HandleLoadDll(DbgEvt);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WinDebug.DEBUG_EVENT_CODE.UNLOAD_DLL_DEBUG_EVENT:
|
case WinDebug.DEBUG_EVENT_CODE.UNLOAD_DLL_DEBUG_EVENT:
|
||||||
{
|
{
|
||||||
HandleUnloadDll(DbgEvt);
|
HandleUnloadDll(DbgEvt);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WinDebug.DEBUG_EVENT_CODE.OUTPUT_DEBUG_STRING_EVENT:
|
case WinDebug.DEBUG_EVENT_CODE.OUTPUT_DEBUG_STRING_EVENT:
|
||||||
{
|
{
|
||||||
HandleOutputDebugString(DbgEvt);
|
HandleOutputDebugString(DbgEvt);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WinDebug.DEBUG_EVENT_CODE.EXCEPTION_DEBUG_EVENT:
|
case WinDebug.DEBUG_EVENT_CODE.RIP_EVENT:
|
||||||
{
|
{
|
||||||
// TODO: Handle properly
|
// TODO: Handle
|
||||||
|
|
||||||
BuildCallstack();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
|
@ -1,20 +1,31 @@
|
||||||
using System;
|
// Written by x1nixmzeng for the Cxbx-Reloaded project
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CxbxDebugger
|
namespace CxbxDebugger
|
||||||
{
|
{
|
||||||
public class DebuggerStackFrame
|
public class DebuggerStackFrame
|
||||||
{
|
{
|
||||||
public IntPtr BasePointer { get; }
|
public IntPtr PC { get; }
|
||||||
public IntPtr CodeAddress { get; }
|
public IntPtr Base { get; }
|
||||||
|
public IntPtr Stack { get; }
|
||||||
|
|
||||||
public DebuggerStackFrame(IntPtr Base, IntPtr CodeAddr)
|
// TOOD Resolve symbol for this frame
|
||||||
|
|
||||||
|
public DebuggerStackFrame(IntPtr EIP, IntPtr EBP, IntPtr ESP)
|
||||||
{
|
{
|
||||||
BasePointer = Base;
|
PC = EIP;
|
||||||
CodeAddress = CodeAddr;
|
Base = EBP;
|
||||||
|
Stack = ESP;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DebuggerStackFrame(IntPtr EIP, IntPtr EBP)
|
||||||
|
{
|
||||||
|
PC = EIP;
|
||||||
|
Base = EBP;
|
||||||
|
Stack = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +53,7 @@ namespace CxbxDebugger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Rename this property
|
||||||
public bool HasEnoughFrames
|
public bool HasEnoughFrames
|
||||||
{
|
{
|
||||||
get
|
get
|
|
@ -5,10 +5,6 @@ namespace CxbxDebugger
|
||||||
{
|
{
|
||||||
public abstract class DebuggerEventListener
|
public abstract class DebuggerEventListener
|
||||||
{
|
{
|
||||||
public DebuggerEventListener()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void OnDebugStart() { }
|
public virtual void OnDebugStart() { }
|
||||||
public virtual void OnDebugEnd() { }
|
public virtual void OnDebugEnd() { }
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Written by x1nixmzeng for the Cxbx-Reloaded project
|
||||||
|
//
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace CxbxDebugger
|
||||||
|
{
|
||||||
|
public class DebuggerInstance : DebuggerEventListener
|
||||||
|
{
|
||||||
|
public List<DebuggerProcess> Processes { get; set; }
|
||||||
|
|
||||||
|
public DebuggerProcess MainProcess { get; set; }
|
||||||
|
public DebuggerThread CurrentThread { get; set; }
|
||||||
|
|
||||||
|
public DebuggerInstance(DebuggerProcess InitialProcess)
|
||||||
|
{
|
||||||
|
Processes = new List<DebuggerProcess>();
|
||||||
|
Processes.Add(InitialProcess);
|
||||||
|
|
||||||
|
MainProcess = InitialProcess;
|
||||||
|
CurrentThread = InitialProcess.MainThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnThreadCreate(DebuggerThread Thread)
|
||||||
|
{
|
||||||
|
MainProcess.Threads.Add(Thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnThreadExit(DebuggerThread Thread)
|
||||||
|
{
|
||||||
|
MainProcess.Threads.Remove(Thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnModuleLoaded(DebuggerModule Module)
|
||||||
|
{
|
||||||
|
MainProcess.Modules.Add(Module);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnModuleUnloaded(DebuggerModule Module)
|
||||||
|
{
|
||||||
|
MainProcess.Modules.Remove(Module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Written by x1nixmzeng for the Cxbx-Reloaded project
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CxbxDebugger
|
||||||
|
{
|
||||||
|
public class DebuggerModule
|
||||||
|
{
|
||||||
|
public IntPtr ImageBase { get; set; }
|
||||||
|
public string Path { get; set; }
|
||||||
|
|
||||||
|
// Based on DebugProcessModule
|
||||||
|
public DebuggerModule()
|
||||||
|
{
|
||||||
|
ImageBase = IntPtr.Zero;
|
||||||
|
Path = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Written by x1nixmzeng for the Cxbx-Reloaded project
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using WinProcesses = VsChromium.Core.Win32.Processes;
|
||||||
|
|
||||||
|
namespace CxbxDebugger
|
||||||
|
{
|
||||||
|
public class DebuggerProcess
|
||||||
|
{
|
||||||
|
public IntPtr Handle { get; set; }
|
||||||
|
public uint ProcessID { get; set; }
|
||||||
|
|
||||||
|
public List<DebuggerModule> Modules { get; set; }
|
||||||
|
public List<DebuggerThread> Threads { get; set; }
|
||||||
|
|
||||||
|
public DebuggerModule MainModule { get; set; }
|
||||||
|
public DebuggerThread MainThread { get; set; }
|
||||||
|
|
||||||
|
// Based on DebugProcess
|
||||||
|
public DebuggerProcess()
|
||||||
|
{
|
||||||
|
Handle = IntPtr.Zero;
|
||||||
|
ProcessID = 0;
|
||||||
|
|
||||||
|
Modules = new List<DebuggerModule>();
|
||||||
|
Threads = new List<DebuggerThread>();
|
||||||
|
|
||||||
|
MainModule = null;
|
||||||
|
MainThread = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Suspend()
|
||||||
|
{
|
||||||
|
// TODO Thread may already be in a non-suspended state
|
||||||
|
|
||||||
|
foreach (DebuggerThread Thread in Threads)
|
||||||
|
{
|
||||||
|
WinProcesses.NativeMethods.SuspendThread(Thread.Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Resume()
|
||||||
|
{
|
||||||
|
// TODO Thread may already be in a non-suspended state
|
||||||
|
|
||||||
|
foreach (DebuggerThread Thread in Threads)
|
||||||
|
{
|
||||||
|
WinProcesses.NativeMethods.ResumeThread(Thread.Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint ReadMemory(IntPtr addr)
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[4];
|
||||||
|
|
||||||
|
uint numRead = 0;
|
||||||
|
WinProcesses.NativeMethods.ReadProcessMemory
|
||||||
|
(
|
||||||
|
Handle,
|
||||||
|
addr,
|
||||||
|
buffer,
|
||||||
|
4,
|
||||||
|
out numRead
|
||||||
|
);
|
||||||
|
|
||||||
|
return BitConverter.ToUInt32(buffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Written by x1nixmzeng for the Cxbx-Reloaded project
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace CxbxDebugger
|
||||||
|
{
|
||||||
|
public class DebuggerThread
|
||||||
|
{
|
||||||
|
public DebuggerProcess OwningProcess { get; set; }
|
||||||
|
|
||||||
|
public IntPtr Handle { get; set; }
|
||||||
|
public uint ThreadID { get; set; }
|
||||||
|
public IntPtr StartAddress { get; set; }
|
||||||
|
public IntPtr ThreadBase { get; set; }
|
||||||
|
|
||||||
|
CONTEXT_x86 ContextCache = new CONTEXT_x86
|
||||||
|
{
|
||||||
|
ContextFlags =
|
||||||
|
ContextFlags.CONTEXT_i386 |
|
||||||
|
ContextFlags.CONTEXT_CONTROL |
|
||||||
|
ContextFlags.CONTEXT_INTEGER |
|
||||||
|
ContextFlags.CONTEXT_SEGMENTS
|
||||||
|
};
|
||||||
|
|
||||||
|
DebuggerCallstack CallstackCache = new DebuggerCallstack();
|
||||||
|
|
||||||
|
// Based on DebugThread
|
||||||
|
public DebuggerThread(DebuggerProcess Owner)
|
||||||
|
{
|
||||||
|
OwningProcess = Owner;
|
||||||
|
|
||||||
|
Handle = IntPtr.Zero;
|
||||||
|
ThreadID = 0;
|
||||||
|
StartAddress = IntPtr.Zero;
|
||||||
|
ThreadBase = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateContext()
|
||||||
|
{
|
||||||
|
bool Result = NativeMethods.GetThreadContext(Handle, ref ContextCache);
|
||||||
|
if (!Result)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ebp = ContextCache.ebp;
|
||||||
|
|
||||||
|
CallstackCache = new DebuggerCallstack();
|
||||||
|
|
||||||
|
CallstackCache.AddFrame(new DebuggerStackFrame(new IntPtr(ContextCache.eip), new IntPtr(ebp), new IntPtr(ContextCache.esp)));
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var ReturnAddr = OwningProcess.ReadMemory(new IntPtr(ebp + 4));
|
||||||
|
ebp = OwningProcess.ReadMemory(new IntPtr(ebp));
|
||||||
|
|
||||||
|
if (ebp == 0 || ReturnAddr == ebp)
|
||||||
|
break;
|
||||||
|
|
||||||
|
CallstackCache.AddFrame(new DebuggerStackFrame(new IntPtr(ReturnAddr), new IntPtr(ebp)));
|
||||||
|
}
|
||||||
|
while (!CallstackCache.HasEnoughFrames);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CxbxDebugger
|
|
||||||
{
|
|
||||||
public class DebuggerModule
|
|
||||||
{
|
|
||||||
public string Path { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CxbxDebugger
|
|
||||||
{
|
|
||||||
public class DebuggerProcess
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CxbxDebugger
|
|
||||||
{
|
|
||||||
public class DebuggerThread
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,7 +37,7 @@ namespace CxbxDebugger
|
||||||
SetDebugProcessActive(false);
|
SetDebugProcessActive(false);
|
||||||
|
|
||||||
// TODO: Wait for user to start this?
|
// TODO: Wait for user to start this?
|
||||||
StartDebugging();
|
//StartDebugging();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartDebugging()
|
private void StartDebugging()
|
||||||
|
@ -182,21 +182,37 @@ namespace CxbxDebugger
|
||||||
frm.DebugEvent("Ended debugging session");
|
frm.DebugEvent("Ended debugging session");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnThreadCreate(DebuggerThread Thread)
|
||||||
|
{
|
||||||
|
frm.DebugEvent(string.Format("Thread created {0}", Thread.ThreadID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnThreadExit(DebuggerThread Thread)
|
||||||
|
{
|
||||||
|
frm.DebugEvent(string.Format("Thread exited {0}", Thread.ThreadID));
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnModuleLoaded(DebuggerModule Module)
|
public override void OnModuleLoaded(DebuggerModule Module)
|
||||||
{
|
{
|
||||||
frm.DebugEvent("Loading module: " + Module.Path);
|
frm.DebugEvent(string.Format("Loaded module \"{0}\"", Module.Path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnModuleUnloaded(DebuggerModule Module)
|
||||||
|
{
|
||||||
|
frm.DebugEvent(string.Format("Unloaded module \"{0}\"", Module.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnDebugOutput(string Message)
|
public override void OnDebugOutput(string Message)
|
||||||
{
|
{
|
||||||
frm.DebugEvent("Debug string: " + Message);
|
frm.DebugEvent(string.Format("OutputDebugString \"{0}\"", Message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Update DebuggerStackFrame to query symbols
|
||||||
public override void OnCallstack(DebuggerCallstack Callstack)
|
public override void OnCallstack(DebuggerCallstack Callstack)
|
||||||
{
|
{
|
||||||
foreach( DebuggerStackFrame StackFrame in Callstack.StackFrames)
|
foreach( DebuggerStackFrame StackFrame in Callstack.StackFrames)
|
||||||
{
|
{
|
||||||
var stackFrameStr = string.Format("{0:X8} and {1:X8}", (uint)StackFrame.BasePointer, (uint)StackFrame.CodeAddress);
|
var stackFrameStr = string.Format("{0:X8} --> {1:X8}", (uint)StackFrame.Base, (uint)StackFrame.PC);
|
||||||
frm.DebugEvent("Callstack: " + stackFrameStr);
|
frm.DebugEvent("Callstack: " + stackFrameStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace CxbxDebugger.TheadHelpers
|
namespace CxbxDebugger
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum ContextFlags : uint
|
public enum ContextFlags : uint
|
||||||
|
@ -263,5 +263,8 @@ namespace CxbxDebugger.TheadHelpers
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
public static extern bool SetThreadContext(IntPtr hThread, ref CONTEXT_x86 lpContext);
|
public static extern bool SetThreadContext(IntPtr hThread, ref CONTEXT_x86 lpContext);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern uint GetThreadId(IntPtr hThread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,13 @@ namespace VsChromium.Core.Win32.Processes
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
[return: MarshalAs(UnmanagedType.Bool)]
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
public static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress,
|
public static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress,
|
||||||
[Out] byte[] buffer, uint size, out UInt32 lpNumberOfBytesRead);
|
[Out] byte[] buffer, uint size, out uint lpNumberOfBytesRead);
|
||||||
|
|
||||||
|
// x1nix: added
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
|
||||||
|
[Out] byte[] buffer, uint size, out uint lpNumberOfBytesRead);
|
||||||
|
|
||||||
[DllImport("ntdll.dll", SetLastError = true)]
|
[DllImport("ntdll.dll", SetLastError = true)]
|
||||||
public static extern int NtQueryInformationProcess(SafeProcessHandle hProcess, ProcessInfoClass pic, ref ProcessBasicInformation pbi, int cb, out int pSize);
|
public static extern int NtQueryInformationProcess(SafeProcessHandle hProcess, ProcessInfoClass pic, ref ProcessBasicInformation pbi, int cb, out int pSize);
|
||||||
|
|
|
@ -14,6 +14,10 @@ namespace LowLevelDesign.Win32.Windows
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||||
|
|
||||||
|
// x1nix: added
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern uint GetProcessId(IntPtr hProcess);
|
||||||
|
|
||||||
// x1nix: added
|
// x1nix: added
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
public static extern uint GetFinalPathNameByHandleW(IntPtr hFile, IntPtr lpszFilePath, uint cchFilePath = 0u, uint dwFlags = 0u);
|
public static extern uint GetFinalPathNameByHandleW(IntPtr hFile, IntPtr lpszFilePath, uint cchFilePath = 0u, uint dwFlags = 0u);
|
||||||
|
|
Loading…
Reference in New Issue