Expanded debugger event listener interface

Also removed unused Win32 files
This commit is contained in:
x1nixmzeng 2018-01-03 20:20:01 +00:00
parent be7c796a71
commit 8eb4fa4df8
10 changed files with 234 additions and 147 deletions

View File

@ -65,6 +65,10 @@
<ItemGroup> <ItemGroup>
<Compile Include="Debugger.cs" /> <Compile Include="Debugger.cs" />
<Compile Include="DebuggerEventListener.cs" /> <Compile Include="DebuggerEventListener.cs" />
<Compile Include="DebuggerModule.cs" />
<Compile Include="DebuggerProcess.cs" />
<Compile Include="DebuggerCallstack.cs" />
<Compile Include="DebuggerThread.cs" />
<Compile Include="Form1.cs"> <Compile Include="Form1.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@ -74,7 +78,6 @@
<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" />
<Compile Include="Win32\ComPtr.cs" />
<Compile Include="Win32\Constants.cs" /> <Compile Include="Win32\Constants.cs" />
<Compile Include="Win32\Debugging\CONTINUE_STATUS.cs" /> <Compile Include="Win32\Debugging\CONTINUE_STATUS.cs" />
<Compile Include="Win32\Debugging\CREATE_PROCESS_DEBUG_INFO.cs" /> <Compile Include="Win32\Debugging\CREATE_PROCESS_DEBUG_INFO.cs" />
@ -92,7 +95,6 @@
<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" />
<Compile Include="Win32\HResults.cs" />
<Compile Include="Win32\Interop\SecurityAttributes.cs" /> <Compile Include="Win32\Interop\SecurityAttributes.cs" />
<Compile Include="Win32\Jobs\IO_COUNTERS.cs" /> <Compile Include="Win32\Jobs\IO_COUNTERS.cs" />
<Compile Include="Win32\Jobs\JobInformationLimitFlags.cs" /> <Compile Include="Win32\Jobs\JobInformationLimitFlags.cs" />

View File

@ -11,20 +11,15 @@ using CxbxDebugger.TheadHelpers;
namespace CxbxDebugger namespace CxbxDebugger
{ {
public class Stackframe
{
public IntPtr BasePointer { get; }
public IntPtr CodeAddress { get; }
public Stackframe(IntPtr Base, IntPtr CodeAddr)
{
BasePointer = Base;
CodeAddress = CodeAddr;
}
}
public class Debugger : IDisposable public class Debugger : IDisposable
{ {
enum RunState
{
NotLaunched,
Running,
Ended
};
// Cached process information // Cached process information
WinProcesses.PROCESS_INFORMATION stProcessInfo = new WinProcesses.PROCESS_INFORMATION(); WinProcesses.PROCESS_INFORMATION stProcessInfo = new WinProcesses.PROCESS_INFORMATION();
WinProcesses.STARTUPINFO stStartInfo = new WinProcesses.STARTUPINFO(); WinProcesses.STARTUPINFO stStartInfo = new WinProcesses.STARTUPINFO();
@ -34,8 +29,13 @@ namespace CxbxDebugger
WinProcesses.SafeThreadHandle hThread = new WinProcesses.SafeThreadHandle(); WinProcesses.SafeThreadHandle hThread = new WinProcesses.SafeThreadHandle();
string[] args = new string[] { }; string[] args = new string[] { };
bool Running = false;
RunState State = RunState.NotLaunched;
List<DebuggerModule> ModuleList = new List<DebuggerModule>();
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 CONTEXT_x86 lastReadCtxt = new CONTEXT_x86
@ -99,8 +99,23 @@ namespace CxbxDebugger
} }
} }
public bool CanLaunch()
{
switch (State)
{
case RunState.NotLaunched:
case RunState.Ended:
return true;
}
return false;
}
public bool Launch() public bool Launch()
{ {
if (CanLaunch() == false)
throw new Exception("Unable to launch in this state");
var DebugCreationFlags = var DebugCreationFlags =
WinProcesses.ProcessCreationFlags.DEBUG_ONLY_THIS_PROCESS | WinProcesses.ProcessCreationFlags.DEBUG_ONLY_THIS_PROCESS |
WinProcesses.ProcessCreationFlags.CREATE_NEW_CONSOLE; WinProcesses.ProcessCreationFlags.CREATE_NEW_CONSOLE;
@ -125,7 +140,7 @@ namespace CxbxDebugger
hProcess = new WinProcesses.SafeProcessHandle(stProcessInfo.hProcess); hProcess = new WinProcesses.SafeProcessHandle(stProcessInfo.hProcess);
hThread = new WinProcesses.SafeThreadHandle(stProcessInfo.hThread); hThread = new WinProcesses.SafeThreadHandle(stProcessInfo.hThread);
Running = true; State = RunState.Running;
} }
return bRet; return bRet;
@ -208,7 +223,6 @@ namespace CxbxDebugger
return BitConverter.ToUInt32(buffer, 0); return BitConverter.ToUInt32(buffer, 0);
} }
private void BuildCallstack() private void BuildCallstack()
{ {
// Skip if we have nothing to report to // Skip if we have nothing to report to
@ -222,14 +236,11 @@ namespace CxbxDebugger
return; return;
} }
// TODO: Expose as variable
const int MaxFrames = 16;
var Callstack = new List<Stackframe>();
var ebp = lastReadCtxt.ebp; var ebp = lastReadCtxt.ebp;
Callstack.Add(new Stackframe(new IntPtr(ebp), new IntPtr(lastReadCtxt.eip)));
var Callstack = new DebuggerCallstack();
Callstack.AddFrame(new DebuggerStackFrame(new IntPtr(ebp), new IntPtr(lastReadCtxt.eip)));
do do
{ {
var ReturnAddr = GetMemory(new IntPtr(ebp + 4)); var ReturnAddr = GetMemory(new IntPtr(ebp + 4));
@ -238,32 +249,97 @@ namespace CxbxDebugger
if (ebp == 0 || ReturnAddr == ebp) if (ebp == 0 || ReturnAddr == ebp)
break; break;
Callstack.Add(new Stackframe(new IntPtr(ebp), new IntPtr(ReturnAddr))); Callstack.AddFrame(new DebuggerStackFrame(new IntPtr(ebp), new IntPtr(ReturnAddr)));
}
while (Callstack.Count < MaxFrames);
var strList = new List<string>(Callstack.Count);
foreach (Stackframe sf in Callstack)
{
// TODO: Resolve BasePointer to symbol database
strList.Add(string.Format("{0:X8} and {1:X8}", (uint)sf.BasePointer, (uint)sf.CodeAddress));
} }
while (!Callstack.HasEnoughFrames);
foreach (DebuggerEventListener EvtListener in EventListeners) foreach (DebuggerEventListener EvtListener in EventListeners)
{ {
EvtListener.OnCallstack(strList.ToArray()); EvtListener.OnCallstack(Callstack);
} }
} }
private void HandleCreateProcess(WinDebug.DEBUG_EVENT DebugEvent)
{
// TODO Investigate why this is throwing an exception converting to this type
//var DebugInfo = DebugEvent.CreateProcessInfo;
var Process = new DebuggerProcess();
//DebugInfo.hFile
//DebugInfo.hProcess
//DebugInfo.hThread
//DebugInfo.lpBaseOfImage
//DebugInfo.lpStartAddress
ProcessList.Add(Process);
foreach (DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnProcessCreate(Process);
}
}
private void HandleExitProcess(WinDebug.DEBUG_EVENT DebugEvent)
{
var DebugInfo = DebugEvent.ExitProcess;
//DebugInfo.dwExitCode
foreach (DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnProcessExit(null);
}
}
private void HandleLoadDll(WinDebug.DEBUG_EVENT DebugEvent)
{
var DebugInfo = DebugEvent.LoadDll;
var Module = new DebuggerModule();
Module.Path = ResolveProcessPath(DebugInfo.hFile);
ModuleList.Add(Module);
foreach (DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnModuleLoaded(Module);
}
}
private void HandleUnloadDll(WinDebug.DEBUG_EVENT DebugEvent)
{
var DebugInfo = DebugEvent.UnloadDll;
//DebugInfo.lpBaseOfDll
foreach (DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnModuleUnloaded(null);
}
}
private void HandleOutputDebugString(WinDebug.DEBUG_EVENT DebugEvent)
{
var DebugInfo = DebugEvent.DebugString;
string debugString = ReadProcessString(DebugInfo.lpDebugStringData, DebugInfo.nDebugStringLength, DebugInfo.fUnicode == 1);
foreach (DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnDebugOutput(debugString);
}
}
public void Run() public void Run()
{ {
WinDebug.DEBUG_EVENT DbgEvt; WinDebug.DEBUG_EVENT DbgEvt;
WinDebug.CONTINUE_STATUS ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE; WinDebug.CONTINUE_STATUS ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
bool bContinue = true; bool bContinue = true;
foreach (DebuggerEventListener EvtListener in EventListeners) foreach (DebuggerEventListener EvtListener in EventListeners)
{ {
EvtListener.OnDebugStart(); EvtListener.OnDebugStart();
@ -279,67 +355,37 @@ namespace CxbxDebugger
{ {
case WinDebug.DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT: case WinDebug.DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT:
{ {
// TODO: Handle properly, storing process info HandleCreateProcess(DbgEvt);
foreach (DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnProcessCreate();
}
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
} }
break; break;
case WinDebug.DEBUG_EVENT_CODE.EXIT_PROCESS_DEBUG_EVENT: case WinDebug.DEBUG_EVENT_CODE.EXIT_PROCESS_DEBUG_EVENT:
{ {
foreach (DebuggerEventListener EvtListener in EventListeners) HandleExitProcess(DbgEvt);
{
EvtListener.OnProcessExit();
}
// XX temp
bContinue = false; bContinue = false;
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
} }
break; break;
case WinDebug.DEBUG_EVENT_CODE.LOAD_DLL_DEBUG_EVENT: case WinDebug.DEBUG_EVENT_CODE.LOAD_DLL_DEBUG_EVENT:
{ {
// TODO: Handle properly HandleLoadDll(DbgEvt);
string Name = ResolveProcessPath(DbgEvt.LoadDll.hFile);
foreach (DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnModuleLoaded(Name);
}
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
} }
break; break;
case WinDebug.DEBUG_EVENT_CODE.UNLOAD_DLL_DEBUG_EVENT: case WinDebug.DEBUG_EVENT_CODE.UNLOAD_DLL_DEBUG_EVENT:
{ {
// TODO: Handle properly, looking up module being unloaded HandleUnloadDll(DbgEvt);
foreach (DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnModuleUnloaded();
}
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
} }
break; break;
case WinDebug.DEBUG_EVENT_CODE.OUTPUT_DEBUG_STRING_EVENT: case WinDebug.DEBUG_EVENT_CODE.OUTPUT_DEBUG_STRING_EVENT:
{ {
string dbgStr = ReadProcessString(DbgEvt.DebugString.lpDebugStringData, DbgEvt.DebugString.nDebugStringLength, DbgEvt.DebugString.fUnicode == 1); HandleOutputDebugString(DbgEvt);
//Console.WriteLine(dbgStr);
foreach(DebuggerEventListener EvtListener in EventListeners)
{
EvtListener.OnDebugOutput(dbgStr);
}
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
} }
break; break;
@ -348,24 +394,14 @@ namespace CxbxDebugger
// TODO: Handle properly // TODO: Handle properly
BuildCallstack(); BuildCallstack();
// Temporary. Should wait until owning systems are happy to continue
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
}
break;
default:
{
// For any other events, just continue
ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE;
} }
break; break;
} }
// Continue on our merry way
// TODO: Wait until our handlers are happy to continue
WinDebug.NativeMethods.ContinueDebugEvent(DbgEvt.dwProcessId, DbgEvt.dwThreadId, ContinueStatus); WinDebug.NativeMethods.ContinueDebugEvent(DbgEvt.dwProcessId, DbgEvt.dwThreadId, ContinueStatus);
} }
State = RunState.Ended;
foreach (DebuggerEventListener EvtListener in EventListeners) foreach (DebuggerEventListener EvtListener in EventListeners)
{ {

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CxbxDebugger
{
public class DebuggerStackFrame
{
public IntPtr BasePointer { get; }
public IntPtr CodeAddress { get; }
public DebuggerStackFrame(IntPtr Base, IntPtr CodeAddr)
{
BasePointer = Base;
CodeAddress = CodeAddr;
}
}
public class DebuggerCallstack
{
int NumSupportedFrames = 16;
public List<DebuggerStackFrame> StackFrames { get; }
public DebuggerCallstack()
{
StackFrames = new List<DebuggerStackFrame>(NumSupportedFrames);
}
public DebuggerCallstack(int MaxFrames)
{
NumSupportedFrames = MaxFrames;
StackFrames = new List<DebuggerStackFrame>(NumSupportedFrames);
}
public void AddFrame(DebuggerStackFrame StackFrame)
{
if (!HasEnoughFrames)
{
StackFrames.Add(StackFrame);
}
}
public bool HasEnoughFrames
{
get
{
return StackFrames.Count < NumSupportedFrames;
}
}
}
}

View File

@ -12,16 +12,16 @@ namespace CxbxDebugger
public virtual void OnDebugStart() { } public virtual void OnDebugStart() { }
public virtual void OnDebugEnd() { } public virtual void OnDebugEnd() { }
public virtual void OnProcessCreate() { } public virtual void OnProcessCreate(DebuggerProcess Process) { }
public virtual void OnProcessExit() { } public virtual void OnProcessExit(DebuggerProcess Process) { }
public virtual void OnThreadCreate() { } public virtual void OnThreadCreate(DebuggerThread Thread) { }
public virtual void OnThreadExit() { } public virtual void OnThreadExit(DebuggerThread Thread) { }
public virtual void OnModuleLoaded(string ModuleName) { } public virtual void OnModuleLoaded(DebuggerModule Module) { }
public virtual void OnModuleUnloaded() { } public virtual void OnModuleUnloaded(DebuggerModule Module) { }
public virtual void OnCallstack(string[] Callstack) { } public virtual void OnCallstack(DebuggerCallstack Callstack) { }
public virtual void OnDebugOutput(string Message) { } public virtual void OnDebugOutput(string Message) { }
} }

View File

@ -0,0 +1,13 @@
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; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CxbxDebugger
{
public class DebuggerProcess
{
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CxbxDebugger
{
public class DebuggerThread
{
}
}

View File

@ -114,17 +114,19 @@ namespace CxbxDebugger
public void DebugEvent(string Message) public void DebugEvent(string Message)
{ {
if( lbConsole.InvokeRequired ) string MessageStamped = string.Format("[{0}] {1}", DateTime.Now.ToLongTimeString(), Message);
if ( lbConsole.InvokeRequired )
{ {
// Ensure we Add items on the right thread // Ensure we Add items on the right thread
lbConsole.Invoke(new MethodInvoker(delegate () lbConsole.Invoke(new MethodInvoker(delegate ()
{ {
lbConsole.Items.Add(Message); lbConsole.Items.Insert(0, MessageStamped);
})); }));
} }
else else
{ {
lbConsole.Items.Add(Message); lbConsole.Items.Insert(0, MessageStamped);
} }
} }
@ -180,9 +182,9 @@ namespace CxbxDebugger
frm.DebugEvent("Ended debugging session"); frm.DebugEvent("Ended debugging session");
} }
public override void OnModuleLoaded(string ModuleName) public override void OnModuleLoaded(DebuggerModule Module)
{ {
frm.DebugEvent("Loading module: " + ModuleName); frm.DebugEvent("Loading module: " + Module.Path);
} }
public override void OnDebugOutput(string Message) public override void OnDebugOutput(string Message)
@ -190,12 +192,12 @@ namespace CxbxDebugger
frm.DebugEvent("Debug string: " + Message); frm.DebugEvent("Debug string: " + Message);
} }
public override void OnCallstack(string[] Callstack) public override void OnCallstack(DebuggerCallstack Callstack)
{ {
// Placeholder display foreach( DebuggerStackFrame StackFrame in Callstack.StackFrames)
foreach( string str in Callstack)
{ {
frm.DebugEvent("Callstack: " + str); var stackFrameStr = string.Format("{0:X8} and {1:X8}", (uint)StackFrame.BasePointer, (uint)StackFrame.CodeAddress);
frm.DebugEvent("Callstack: " + stackFrameStr);
} }
} }
} }

View File

@ -1,39 +0,0 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace VsChromium.Core.Win32 {
public struct ComPtr<T> : IDisposable where T : class {
private T ptr;
public ComPtr(T ptr) {
Debug.Assert(ptr == null || Marshal.IsComObject(ptr));
this.ptr = ptr;
}
public T Ptr { get { return ptr; } }
public void Dispose() {
if (ptr != null)
Marshal.ReleaseComObject(ptr);
ptr = null;
}
public T Detach() {
T result = ptr;
ptr = null;
return result;
}
}
public static class ComPtr {
public static ComPtr<T> Create<T>(T ptr) where T : class {
return new ComPtr<T>(ptr);
}
}
}

View File

@ -1,6 +0,0 @@
namespace VsChromium.Core.Win32 {
public static class HResults {
public const int HR_ERROR_SEM_TIMEOUT = unchecked((int)0x80070079);
public const int HR_ERROR_NOT_SUPPORTED = unchecked((int)0x80070032);
}
}