Cxbx-Reloaded/src/CxbxDebugger/Debugger/DebuggerThread.cs

123 lines
3.7 KiB
C#

// Written by x1nixmzeng for the Cxbx-Reloaded project
//
using System;
using WinProcesses = VsChromium.Core.Win32.Processes;
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; }
public string DebugName { get; set; }
private int CachedSuspendCount = 0;
public bool IsSuspended
{
get
{
// TODO: Check count is inside range [0..MAXIMUM_SUSPEND_COUNT]
return CachedSuspendCount > 0;
}
}
public bool WasSuspended
{
get
{
return CachedSuspendCount > 1;
}
}
CONTEXT_x86 ContextCache = new CONTEXT_x86
{
ContextFlags =
ContextFlags.CONTEXT_i386 |
ContextFlags.CONTEXT_CONTROL |
ContextFlags.CONTEXT_INTEGER |
ContextFlags.CONTEXT_SEGMENTS
};
public DebuggerCallstack CallstackCache { get; private set; } = new DebuggerCallstack();
// Based on DebugThread
public DebuggerThread(DebuggerProcess Owner)
{
OwningProcess = Owner;
Handle = IntPtr.Zero;
ThreadID = 0;
StartAddress = IntPtr.Zero;
ThreadBase = IntPtr.Zero;
}
public void Suspend()
{
// Early-out if this thread was already in a suspended state
if (IsSuspended)
return;
int PrevSuspendCount = (int)WinProcesses.NativeMethods.SuspendThread(Handle);
if (PrevSuspendCount == -1)
throw new Exception("SuspendThread failed");
CachedSuspendCount = PrevSuspendCount +1;
}
public void Resume(bool ForceStart = false)
{
// Early-out if this thread was not already suspended
if (!IsSuspended)
return;
// Stores the internal "suspend counter" back to the previous state - which could still be suspended
int TargetCount = (ForceStart ? 0 : CachedSuspendCount - 1);
int LastSuspendCount;
do
{
LastSuspendCount = (int)WinProcesses.NativeMethods.ResumeThread(Handle);
if (LastSuspendCount == -1)
throw new Exception("ResumeThread failed");
}
while (LastSuspendCount -1 > TargetCount);
CachedSuspendCount = TargetCount;
}
public void UpdateContext()
{
bool Result = NativeMethods.GetThreadContext(Handle, ref ContextCache);
if (!Result)
return;
uint ebp = ContextCache.ebp;
CallstackCache = new DebuggerCallstack();
CallstackCache.AddFrame(new DebuggerStackFrame(new IntPtr(ContextCache.eip), new IntPtr(ebp), new IntPtr(ContextCache.esp)));
uint ReturnAddr = 0;
do
{
if (!OwningProcess.ReadMemory(new IntPtr(ebp + 4), ref ReturnAddr))
break;
if (!OwningProcess.ReadMemory(new IntPtr(ebp), ref ebp))
break;
if (ebp == 0 || ReturnAddr == ebp)
break;
CallstackCache.AddFrame(new DebuggerStackFrame(new IntPtr(ReturnAddr), new IntPtr(ebp)));
}
while (CallstackCache.CanCollect);
}
}
}