Update debugger types and thread report handling

This commit is contained in:
x1nixmzeng 2020-10-11 12:14:42 +01:00
parent 3a740d7933
commit 96155736e5
3 changed files with 166 additions and 143 deletions

View File

@ -9,6 +9,7 @@ using WinDebug = VsChromium.Core.Win32.Debugging;
using System.Runtime.InteropServices;
using WinLowLevel = LowLevelDesign.Win32.Windows.NativeMethods;
using System.Threading;
using System.Threading.Tasks;
namespace CxbxDebugger
{
@ -32,13 +33,18 @@ namespace CxbxDebugger
string[] args = new string[] { };
string Target = "";
public string ProcessName
{
get { return Target; }
}
RunState State = RunState.NotLaunched;
DebuggerMessages.DebuggerInit InitParams = new DebuggerMessages.DebuggerInit();
DebuggerInstance DebugInstance;
List<IDebuggerGeneralEvents> GeneralEvents = new List<IDebuggerGeneralEvents>();
List<IDebuggerSessionEvents> SessionEvents = new List<IDebuggerSessionEvents>();
List<IDebuggerProcessEvents> ProcessEvents = new List<IDebuggerProcessEvents>();
List<IDebuggerThreadEvents> ThreadEvents = new List<IDebuggerThreadEvents>();
List<IDebuggerModuleEvents> ModuleEvents = new List<IDebuggerModuleEvents>();
@ -103,7 +109,7 @@ namespace CxbxDebugger
bpStall.Set();
// Remove all events
GeneralEvents.Clear();
SessionEvents.Clear();
ProcessEvents.Clear();
ThreadEvents.Clear();
ModuleEvents.Clear();
@ -172,6 +178,9 @@ namespace CxbxDebugger
if (CanLaunch() == false)
throw new Exception("Unable to launch in this state");
if (args.Length == 0)
return false;
var DebugCreationFlags =
WinProcesses.ProcessCreationFlags.DEBUG_ONLY_THIS_PROCESS |
WinProcesses.ProcessCreationFlags.CREATE_NEW_CONSOLE;
@ -197,7 +206,7 @@ namespace CxbxDebugger
// Store so they can be marshalled and closed correctly
hProcess = new WinProcesses.SafeProcessHandle(stProcessInfo.hProcess);
hThread = new WinProcesses.SafeThreadHandle(stProcessInfo.hThread);
bContinue = true;
State = RunState.Running;
@ -313,10 +322,7 @@ namespace CxbxDebugger
Thread.StartAddress = DebugInfo.lpStartAddress;
Thread.ThreadBase = DebugInfo.lpThreadLocalBase;
foreach (IDebuggerThreadEvents Event in ThreadEvents )
{
Event.OnThreadCreate(Thread);
}
Parallel.ForEach(ThreadEvents, Event => Event.OnThreadCreate(Thread));
}
private void HandleExitThread(WinDebug.DEBUG_EVENT DebugEvent)
@ -333,10 +339,7 @@ namespace CxbxDebugger
{
uint ExitCode = DebugInfo.dwExitCode;
foreach (IDebuggerThreadEvents Event in ThreadEvents)
{
Event.OnThreadExit(TargetThread, ExitCode);
}
Parallel.ForEach(ThreadEvents, Event => Event.OnThreadExit(TargetThread, ExitCode));
}
}
@ -369,26 +372,17 @@ namespace CxbxDebugger
DebugInstance = new DebuggerInstance(Process);
RegisterEventInterfaces(DebugInstance);
foreach (IDebuggerProcessEvents Event in ProcessEvents)
{
Event.OnProcessCreate(Process);
}
Parallel.ForEach(ProcessEvents, Event => Event.OnProcessCreate(Process));
foreach (IDebuggerThreadEvents Event in ThreadEvents)
{
Event.OnThreadCreate(MainThread);
}
Parallel.ForEach(ThreadEvents, Event => Event.OnThreadCreate(MainThread));
var XboxModule = new DebuggerModule();
XboxModule.Path = Target;
XboxModule.ImageBase = DebugInfo.lpBaseOfImage;
XboxModule.Core = true;
foreach (IDebuggerModuleEvents Event in ModuleEvents)
{
Event.OnModuleLoaded(XboxModule);
}
Parallel.ForEach(ModuleEvents, Event => Event.OnModuleLoaded(XboxModule));
}
private void HandleExitProcess(WinDebug.DEBUG_EVENT DebugEvent)
@ -405,10 +399,7 @@ namespace CxbxDebugger
if (TargetProcess != null)
{
foreach (IDebuggerProcessEvents Event in ProcessEvents)
{
Event.OnProcessExit(TargetProcess, ExitCode);
}
Parallel.ForEach(ProcessEvents, Event => Event.OnProcessExit(TargetProcess, ExitCode));
}
}
@ -426,10 +417,7 @@ namespace CxbxDebugger
Module.Path = ResolveProcessPath(DebugInfo.hFile);
Module.ImageBase = DebugInfo.lpBaseOfDll;
foreach (IDebuggerModuleEvents Event in ModuleEvents)
{
Event.OnModuleLoaded(Module);
}
Parallel.ForEach(ModuleEvents, Event => Event.OnModuleLoaded(Module));
}
private void HandleUnloadDll(WinDebug.DEBUG_EVENT DebugEvent)
@ -445,10 +433,7 @@ namespace CxbxDebugger
if (TargetModule != null)
{
foreach (IDebuggerModuleEvents Event in ModuleEvents)
{
Event.OnModuleUnloaded(TargetModule);
}
Parallel.ForEach(ModuleEvents, Event => Event.OnModuleUnloaded(TargetModule));
}
}
@ -458,10 +443,7 @@ namespace CxbxDebugger
string debugString = ReadProcessString(DebugInfo.lpDebugStringData, DebugInfo.nDebugStringLength, DebugInfo.fUnicode == 1);
foreach(IDebuggerOutputEvents Event in OutputEvents)
{
Event.OnDebugOutput(debugString);
}
Parallel.ForEach(OutputEvents, Event => Event.OnDebugOutput(debugString));
}
private void HandleException(WinDebug.DEBUG_EVENT DebugEvent)
@ -482,12 +464,28 @@ namespace CxbxDebugger
}
break;
case ExceptionCode.PrivilegedInstruction:
{
// Seeing this frequently called in Win10
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_EXCEPTION_NOT_HANDLED;
}
break;
case ExceptionCode.StatusHandleNotClosable:
{
// Seeing this frequently called in Win10
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_EXCEPTION_NOT_HANDLED;
}
break;
case (ExceptionCode)DebuggerMessages.ReportType.OVERRIDE_EXCEPTION:
{
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Query = DebuggerMessages.GetExceptionHandledQuery(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Query = DebuggerMessages.GetExceptionHandledQuery(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
bool Handled = false;
foreach (IDebuggerExceptionEvents Event in ExceptionEvents)
@ -506,7 +504,7 @@ namespace CxbxDebugger
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetHLECacheReport(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Report = DebuggerMessages.GetHLECacheReport(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
SetupHLECacheProvider(Report.FileName);
}
}
@ -517,7 +515,7 @@ namespace CxbxDebugger
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetKernelPatchReport(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Report = DebuggerMessages.GetKernelPatchReport(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
KernelSymbolProvider.AddKernelSymbolFromMessage(Report);
}
@ -529,12 +527,9 @@ namespace CxbxDebugger
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetFileOpenedReport(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Report = DebuggerMessages.GetFileOpenedReport(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
foreach (IDebuggerFileEvents Event in FileEvents)
{
Event.OnFileOpened(Report);
}
Parallel.ForEach(FileEvents, Event => Event.OnFileOpened(Report));
}
}
break;
@ -544,12 +539,9 @@ namespace CxbxDebugger
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetFileReadReport(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Report = DebuggerMessages.GetFileReadReport(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
foreach (IDebuggerFileEvents Event in FileEvents)
{
Event.OnFileRead(Report);
}
Parallel.ForEach(FileEvents, Event => Event.OnFileRead(Report));
}
}
break;
@ -559,12 +551,9 @@ namespace CxbxDebugger
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetFileWriteReport(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Report = DebuggerMessages.GetFileWriteReport(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
foreach (IDebuggerFileEvents Event in FileEvents)
{
Event.OnFileWrite(Report);
}
Parallel.ForEach(FileEvents, Event => Event.OnFileWrite(Report));
}
}
break;
@ -574,13 +563,10 @@ namespace CxbxDebugger
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetFileClosedReport(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Report = DebuggerMessages.GetFileClosedReport(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
if (Report != null)
{
foreach (IDebuggerFileEvents Event in FileEvents)
{
Event.OnFileClosed(Report);
}
Parallel.ForEach(FileEvents, Event => Event.OnFileClosed(Report));
}
}
}
@ -591,14 +577,23 @@ namespace CxbxDebugger
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetDebuggerInitReport(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Report = DebuggerMessages.GetDebuggerInitReport(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
InitParams = Report;
foreach (IDebuggerGeneralEvents Event in GeneralEvents)
{
Event.OnDebugTitleLoaded(InitParams.Title);
}
Parallel.ForEach(SessionEvents, Event => Event.OnDebugTitleLoaded(InitParams.Title));
}
}
break;
case (ExceptionCode)DebuggerMessages.ReportType.DEBUGGER_NEW_TARGET:
{
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetDebuggerNewTargetReport(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
Parallel.ForEach(SessionEvents, Event => Event.OnDebugTargetChanged(Report.CommandLine));
}
}
break;
@ -608,7 +603,7 @@ namespace CxbxDebugger
var Thread = DebugInstance.MainProcess.FindThread((uint)DebugEvent.dwThreadId);
if (Thread != null)
{
var Report = DebuggerMessages.GetMSVCThreadName(Thread, DebugInfo.ExceptionRecord.ExceptionInformation);
var Report = DebuggerMessages.GetMSVCThreadName(Thread, new DebuggerMessages.DataProcessor(DebugInfo.ExceptionRecord.ExceptionInformation));
if(Report != null)
{
// Resolve the ThreadId of an invalid ID to the current thread name
@ -623,10 +618,7 @@ namespace CxbxDebugger
// Update the resolved thread name
ResolvedThread.DebugName = Report.Name;
foreach (IDebuggerThreadEvents Event in ThreadEvents)
{
Event.OnThreadNamed(Thread);
}
Parallel.ForEach(ThreadEvents, Event => Event.OnThreadNamed(Thread));
}
}
}
@ -644,10 +636,7 @@ namespace CxbxDebugger
bpStall.Reset();
foreach (IDebuggerExceptionEvents Event in ExceptionEvents)
{
Event.OnBreakpoint(Thread, BpAddr, BpCode, FirstChance);
}
Parallel.ForEach(ExceptionEvents, Event => Event.OnBreakpoint(Thread, BpAddr, BpCode, FirstChance));
bpStall.WaitOne();
}
@ -674,10 +663,7 @@ namespace CxbxDebugger
WinDebug.DEBUG_EVENT DbgEvt = new WinDebug.DEBUG_EVENT();
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
foreach (IDebuggerGeneralEvents Event in GeneralEvents)
{
Event.OnDebugStart();
}
Parallel.ForEach(SessionEvents, Event => Event.OnDebugStart());
// Loop until told to stop
while (bContinue == true)
@ -734,10 +720,7 @@ namespace CxbxDebugger
State = RunState.Ended;
foreach (IDebuggerGeneralEvents Event in GeneralEvents)
{
Event.OnDebugEnd();
}
Parallel.ForEach(SessionEvents, Event => Event.OnDebugEnd());
}
public DebuggerSymbol ResolveSymbol(uint Address)
@ -757,8 +740,8 @@ namespace CxbxDebugger
public void RegisterEventInterfaces(object EventClass)
{
IDebuggerGeneralEvents GeneralListener = EventClass as IDebuggerGeneralEvents;
if(GeneralListener != null ) GeneralEvents.Add(GeneralListener);
IDebuggerSessionEvents SessionListener = EventClass as IDebuggerSessionEvents;
if(SessionListener != null ) SessionEvents.Add(SessionListener);
IDebuggerProcessEvents ProcessListener = EventClass as IDebuggerProcessEvents;
if (ProcessListener != null) ProcessEvents.Add(ProcessListener);

View File

@ -5,11 +5,12 @@ using System;
namespace CxbxDebugger
{
public interface IDebuggerGeneralEvents
public interface IDebuggerSessionEvents
{
void OnDebugStart();
void OnDebugEnd();
void OnDebugTitleLoaded(string Title);
void OnDebugTargetChanged(string CommandLine);
}
public interface IDebuggerProcessEvents

View File

@ -9,15 +9,20 @@ namespace CxbxDebugger
{
public enum ReportType : uint
{
HLECACHE_FILE = 0x00deed00,
KERNEL_PATCH = 0x00deed01,
FILE_OPENED = 0x00deed02,
FILE_READ = 0x00deed03,
FILE_CLOSED = 0x00deed04,
DEBUGGER_INIT = 0x00deed05,
FILE_WRITE = 0x00deed06,
HLECACHE_FILE = 0x1000,
OVERRIDE_EXCEPTION = 0x00ceed01,
KERNEL_PATCH = 0x2000,
FILE_OPENED = 0x3000,
FILE_READ = 0x3001,
FILE_WRITE = 0x3002,
FILE_CLOSED = 0x3003,
DEBUGGER_INIT = 0x400,
DEBUGGER_NEW_TARGET = 0x401,
OVERRIDE_EXCEPTION = 0x500,
// Exception code from https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
MS_VC_EXCEPTION = 0x406D1388,
@ -29,22 +34,38 @@ namespace CxbxDebugger
WCHAR,
};
public class DataProcessor
{
uint[] SourceData;
uint SourceIndex;
public DataProcessor(uint[] Data)
{
SourceData = Data;
SourceIndex = 0;
}
public uint Pop()
{
return SourceData[SourceIndex++];
}
}
public class HLECache
{
public string FileName { get; set; }
}
public static HLECache GetHLECacheReport(DebuggerThread Context, uint[] Data)
public static HLECache GetHLECacheReport(DebuggerThread Context, DataProcessor Data)
{
HLECache Report = new HLECache();
StringType Type = (StringType)Data[0];
var Type = (StringType)Data.Pop();
if (Type != StringType.CHAR)
throw new Exception("GetHLECacheReport expects a string message");
uint Length = Data[1];
IntPtr MessagePtr = new IntPtr(Data[2]);
var Length = Data.Pop(); ;
var MessagePtr = new IntPtr(Data.Pop());
Report.FileName = Context.OwningProcess.ReadString(MessagePtr, Length);
@ -57,20 +78,19 @@ namespace CxbxDebugger
public IntPtr Address { get; set; }
}
public static KernelPatch GetKernelPatchReport(DebuggerThread Context, uint[] Data)
public static KernelPatch GetKernelPatchReport(DebuggerThread Context, DataProcessor Data)
{
KernelPatch Report = new KernelPatch();
StringType Type = (StringType)Data[0];
var Type = (StringType)Data.Pop();
if (Type != StringType.CHAR)
throw new Exception("GetKernelPatchReport expects a string message");
uint Length = Data[1];
IntPtr MessagePtr = new IntPtr(Data[2]);
var Length = Data.Pop();
var MessagePtr = new IntPtr(Data.Pop());
Report.Name = Context.OwningProcess.ReadString(MessagePtr, Length);
Report.Address = new IntPtr(Data[3]);
Report.Address = new IntPtr(Data.Pop());
return Report;
}
@ -82,23 +102,21 @@ namespace CxbxDebugger
public bool Succeeded { get; set; }
}
public static FileOpened GetFileOpenedReport(DebuggerThread Context, uint[] Data)
public static FileOpened GetFileOpenedReport(DebuggerThread Context, DataProcessor Data)
{
FileOpened Report = new FileOpened();
Report.Handle = new IntPtr(Data[0]);
StringType Type = (StringType)Data[1];
Report.Handle = new IntPtr(Data.Pop());
var Type = (StringType)Data.Pop();
if (Type != StringType.WCHAR)
throw new Exception("GetFileOpenedReport expects a widestring message");
uint Length = Data[2];
IntPtr MessagePtr = new IntPtr(Data[3]);
var Length = Data.Pop();
var MessagePtr = new IntPtr(Data.Pop());
Report.FileName = Context.OwningProcess.ReadWString(MessagePtr, Length);
Report.Succeeded = Data[4] != 0;
Report.Succeeded = Data.Pop() != 0;
return Report;
}
@ -110,13 +128,13 @@ namespace CxbxDebugger
public uint Offset { get; set; }
}
public static FileRead GetFileReadReport(DebuggerThread Context, uint[] Data)
public static FileRead GetFileReadReport(DebuggerThread Context, DataProcessor Data)
{
FileRead Report = new FileRead();
Report.Handle = new IntPtr(Data[0]);
Report.Length = Data[1];
Report.Offset = Data[2];
Report.Handle = new IntPtr(Data.Pop());
Report.Length = Data.Pop();
Report.Offset = Data.Pop();
return Report;
}
@ -128,13 +146,13 @@ namespace CxbxDebugger
public uint Offset { get; set; }
}
public static FileWrite GetFileWriteReport(DebuggerThread Context, uint[] Data)
public static FileWrite GetFileWriteReport(DebuggerThread Context, DataProcessor Data)
{
FileWrite Report = new FileWrite();
Report.Handle = new IntPtr(Data[0]);
Report.Length = Data[1];
Report.Offset = Data[2];
Report.Handle = new IntPtr(Data.Pop());
Report.Length = Data.Pop();
Report.Offset = Data.Pop();
return Report;
}
@ -144,18 +162,20 @@ namespace CxbxDebugger
public IntPtr Handle { get; set; }
}
public static FileClosed GetFileClosedReport(DebuggerThread Context, uint[] Data)
public static FileClosed GetFileClosedReport(DebuggerThread Context, DataProcessor Data)
{
// TODO: Restructure this library
uint InvalidHandle = (uint)VsChromium.Core.Win32.Handles.NativeMethods.INVALID_HANDLE_VALUE;
var Handle = Data.Pop();
// Skip invalid file handles
if (Data[0] == InvalidHandle)
if (Handle == InvalidHandle)
return null;
FileClosed Report = new FileClosed();
Report.Handle = new IntPtr(Data[0]);
Report.Handle = new IntPtr(Handle);
return Report;
}
@ -169,59 +189,78 @@ namespace CxbxDebugger
public IntPtr ParameterBase { get; set; }
}
public static ExceptionHandledQuery GetExceptionHandledQuery(DebuggerThread Context, uint[] Data)
public static ExceptionHandledQuery GetExceptionHandledQuery(DebuggerThread Context, DataProcessor Data)
{
ExceptionHandledQuery Query = new ExceptionHandledQuery();
Query.ReponseAddr = new IntPtr(Data[0]);
Query.ExceptionAddress = Data[1];
Query.ExceptionCode = Data[2];
Query.ParameterCount = Data[3];
Query.ParameterBase = new IntPtr(Data[4]);
Query.ReponseAddr = new IntPtr(Data.Pop());
Query.ExceptionAddress = Data.Pop();
Query.ExceptionCode = Data.Pop();
Query.ParameterCount = Data.Pop();
Query.ParameterBase = new IntPtr(Data.Pop());
return Query;
}
public class DebuggerInit
{
public uint TitleID { get; set; }
public string Title { get; set; }
}
public static DebuggerInit GetDebuggerInitReport(DebuggerThread Context, uint[] Data)
public static DebuggerInit GetDebuggerInitReport(DebuggerThread Context, DataProcessor Data)
{
DebuggerInit Report = new DebuggerInit();
Report.TitleID = Data[0];
StringType Type = (StringType)Data[1];
StringType Type = (StringType)Data.Pop();
if (Type != StringType.CHAR)
throw new Exception("GetDebuggerInitReport expects a string message");
uint Length = Data[2];
IntPtr MessagePtr = new IntPtr(Data[3]);
uint Length = Data.Pop();
IntPtr MessagePtr = new IntPtr(Data.Pop());
Report.Title = Context.OwningProcess.ReadString(MessagePtr, Length);
return Report;
}
public class DebuggerNewTarget
{
public string CommandLine { get; set; }
}
public static DebuggerNewTarget GetDebuggerNewTargetReport(DebuggerThread Context, DataProcessor Data)
{
var Report = new DebuggerNewTarget();
StringType Type = (StringType)Data.Pop();
if (Type != StringType.CHAR)
throw new Exception("GetDebuggerInitReport expects a string message");
uint Length = Data.Pop();
IntPtr MessagePtr = new IntPtr(Data.Pop());
Report.CommandLine = Context.OwningProcess.ReadString(MessagePtr, Length);
return Report;
}
public class MSVCThreadName
{
public string Name { get; set; }
public uint ThreadId { get; set; }
}
public static MSVCThreadName GetMSVCThreadName(DebuggerThread Context, uint[] Data)
public static MSVCThreadName GetMSVCThreadName(DebuggerThread Context, DataProcessor Data)
{
uint Type = Data[0];
var Type = Data.Pop();
if (Type != 0x1000)
return null;
string ReportName = "";
IntPtr MessagePtr = new IntPtr(Data[1]);
IntPtr MessagePtr = new IntPtr(Data.Pop());
if (MessagePtr != IntPtr.Zero)
{
ReportName = Context.OwningProcess.ReadString(MessagePtr);
@ -230,7 +269,7 @@ namespace CxbxDebugger
MSVCThreadName Report = new MSVCThreadName();
Report.Name = ReportName;
Report.ThreadId = Data[2];
Report.ThreadId = Data.Pop();
return Report;
}