123 lines
3.7 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|