From 8eb4fa4df886cd36b7299982ec80ee495acb5f64 Mon Sep 17 00:00:00 2001 From: x1nixmzeng Date: Wed, 3 Jan 2018 20:20:01 +0000 Subject: [PATCH] Expanded debugger event listener interface Also removed unused Win32 files --- src/CxbxDebugger/CxbxDebugger.csproj | 6 +- src/CxbxDebugger/Debugger.cs | 204 +++++++++++++--------- src/CxbxDebugger/DebuggerCallstack.cs | 53 ++++++ src/CxbxDebugger/DebuggerEventListener.cs | 14 +- src/CxbxDebugger/DebuggerModule.cs | 13 ++ src/CxbxDebugger/DebuggerProcess.cs | 13 ++ src/CxbxDebugger/DebuggerThread.cs | 13 ++ src/CxbxDebugger/Form1.cs | 20 ++- src/CxbxDebugger/Win32/ComPtr.cs | 39 ----- src/CxbxDebugger/Win32/HResults.cs | 6 - 10 files changed, 234 insertions(+), 147 deletions(-) create mode 100644 src/CxbxDebugger/DebuggerCallstack.cs create mode 100644 src/CxbxDebugger/DebuggerModule.cs create mode 100644 src/CxbxDebugger/DebuggerProcess.cs create mode 100644 src/CxbxDebugger/DebuggerThread.cs delete mode 100644 src/CxbxDebugger/Win32/ComPtr.cs delete mode 100644 src/CxbxDebugger/Win32/HResults.cs diff --git a/src/CxbxDebugger/CxbxDebugger.csproj b/src/CxbxDebugger/CxbxDebugger.csproj index 03db42a83..cd8f3e1c5 100644 --- a/src/CxbxDebugger/CxbxDebugger.csproj +++ b/src/CxbxDebugger/CxbxDebugger.csproj @@ -65,6 +65,10 @@ + + + + Form @@ -74,7 +78,6 @@ - @@ -92,7 +95,6 @@ - diff --git a/src/CxbxDebugger/Debugger.cs b/src/CxbxDebugger/Debugger.cs index f6b504ae6..d138acf98 100644 --- a/src/CxbxDebugger/Debugger.cs +++ b/src/CxbxDebugger/Debugger.cs @@ -11,20 +11,15 @@ using CxbxDebugger.TheadHelpers; 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 { + enum RunState + { + NotLaunched, + Running, + Ended + }; + // Cached process information WinProcesses.PROCESS_INFORMATION stProcessInfo = new WinProcesses.PROCESS_INFORMATION(); WinProcesses.STARTUPINFO stStartInfo = new WinProcesses.STARTUPINFO(); @@ -34,8 +29,13 @@ namespace CxbxDebugger WinProcesses.SafeThreadHandle hThread = new WinProcesses.SafeThreadHandle(); string[] args = new string[] { }; - bool Running = false; + RunState State = RunState.NotLaunched; + + List ModuleList = new List(); + List ThreadList = new List(); + List ProcessList = new List(); + List EventListeners = new List(); 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() { + if (CanLaunch() == false) + throw new Exception("Unable to launch in this state"); + var DebugCreationFlags = WinProcesses.ProcessCreationFlags.DEBUG_ONLY_THIS_PROCESS | WinProcesses.ProcessCreationFlags.CREATE_NEW_CONSOLE; @@ -125,7 +140,7 @@ namespace CxbxDebugger hProcess = new WinProcesses.SafeProcessHandle(stProcessInfo.hProcess); hThread = new WinProcesses.SafeThreadHandle(stProcessInfo.hThread); - Running = true; + State = RunState.Running; } return bRet; @@ -208,7 +223,6 @@ namespace CxbxDebugger return BitConverter.ToUInt32(buffer, 0); } - private void BuildCallstack() { // Skip if we have nothing to report to @@ -222,14 +236,11 @@ namespace CxbxDebugger return; } - // TODO: Expose as variable - const int MaxFrames = 16; - - var Callstack = new List(); 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 { var ReturnAddr = GetMemory(new IntPtr(ebp + 4)); @@ -238,32 +249,97 @@ namespace CxbxDebugger if (ebp == 0 || ReturnAddr == ebp) break; - Callstack.Add(new Stackframe(new IntPtr(ebp), new IntPtr(ReturnAddr))); - } - while (Callstack.Count < MaxFrames); - - - var strList = new List(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)); + Callstack.AddFrame(new DebuggerStackFrame(new IntPtr(ebp), new IntPtr(ReturnAddr))); } + while (!Callstack.HasEnoughFrames); 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() { WinDebug.DEBUG_EVENT DbgEvt; WinDebug.CONTINUE_STATUS ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE; bool bContinue = true; - + foreach (DebuggerEventListener EvtListener in EventListeners) { EvtListener.OnDebugStart(); @@ -279,67 +355,37 @@ namespace CxbxDebugger { case WinDebug.DEBUG_EVENT_CODE.CREATE_PROCESS_DEBUG_EVENT: { - // TODO: Handle properly, storing process info - - foreach (DebuggerEventListener EvtListener in EventListeners) - { - EvtListener.OnProcessCreate(); - } - - ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE; + HandleCreateProcess(DbgEvt); } break; case WinDebug.DEBUG_EVENT_CODE.EXIT_PROCESS_DEBUG_EVENT: { - foreach (DebuggerEventListener EvtListener in EventListeners) - { - EvtListener.OnProcessExit(); - } + HandleExitProcess(DbgEvt); + // XX temp bContinue = false; - ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE; } break; case WinDebug.DEBUG_EVENT_CODE.LOAD_DLL_DEBUG_EVENT: { - // TODO: Handle properly - - string Name = ResolveProcessPath(DbgEvt.LoadDll.hFile); - foreach (DebuggerEventListener EvtListener in EventListeners) - { - EvtListener.OnModuleLoaded(Name); - } - - ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE; + HandleLoadDll(DbgEvt); + } break; case WinDebug.DEBUG_EVENT_CODE.UNLOAD_DLL_DEBUG_EVENT: { - // TODO: Handle properly, looking up module being unloaded - - foreach (DebuggerEventListener EvtListener in EventListeners) - { - EvtListener.OnModuleUnloaded(); - } + HandleUnloadDll(DbgEvt); - ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE; } break; case WinDebug.DEBUG_EVENT_CODE.OUTPUT_DEBUG_STRING_EVENT: { - string dbgStr = ReadProcessString(DbgEvt.DebugString.lpDebugStringData, DbgEvt.DebugString.nDebugStringLength, DbgEvt.DebugString.fUnicode == 1); - //Console.WriteLine(dbgStr); - - foreach(DebuggerEventListener EvtListener in EventListeners) - { - EvtListener.OnDebugOutput(dbgStr); - } - - ContinueStatus = WinDebug.CONTINUE_STATUS.DBG_CONTINUE; + HandleOutputDebugString(DbgEvt); + } break; @@ -348,24 +394,14 @@ namespace CxbxDebugger // TODO: Handle properly 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; } - // Continue on our merry way - // TODO: Wait until our handlers are happy to continue WinDebug.NativeMethods.ContinueDebugEvent(DbgEvt.dwProcessId, DbgEvt.dwThreadId, ContinueStatus); } + + State = RunState.Ended; foreach (DebuggerEventListener EvtListener in EventListeners) { diff --git a/src/CxbxDebugger/DebuggerCallstack.cs b/src/CxbxDebugger/DebuggerCallstack.cs new file mode 100644 index 000000000..4139c91be --- /dev/null +++ b/src/CxbxDebugger/DebuggerCallstack.cs @@ -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 StackFrames { get; } + + public DebuggerCallstack() + { + StackFrames = new List(NumSupportedFrames); + } + + public DebuggerCallstack(int MaxFrames) + { + NumSupportedFrames = MaxFrames; + StackFrames = new List(NumSupportedFrames); + } + + public void AddFrame(DebuggerStackFrame StackFrame) + { + if (!HasEnoughFrames) + { + StackFrames.Add(StackFrame); + } + } + + public bool HasEnoughFrames + { + get + { + return StackFrames.Count < NumSupportedFrames; + } + } + } +} diff --git a/src/CxbxDebugger/DebuggerEventListener.cs b/src/CxbxDebugger/DebuggerEventListener.cs index 0a198d604..c0eecef64 100644 --- a/src/CxbxDebugger/DebuggerEventListener.cs +++ b/src/CxbxDebugger/DebuggerEventListener.cs @@ -12,16 +12,16 @@ namespace CxbxDebugger public virtual void OnDebugStart() { } public virtual void OnDebugEnd() { } - public virtual void OnProcessCreate() { } - public virtual void OnProcessExit() { } + public virtual void OnProcessCreate(DebuggerProcess Process) { } + public virtual void OnProcessExit(DebuggerProcess Process) { } - public virtual void OnThreadCreate() { } - public virtual void OnThreadExit() { } + public virtual void OnThreadCreate(DebuggerThread Thread) { } + public virtual void OnThreadExit(DebuggerThread Thread) { } - public virtual void OnModuleLoaded(string ModuleName) { } - public virtual void OnModuleUnloaded() { } + public virtual void OnModuleLoaded(DebuggerModule Module) { } + public virtual void OnModuleUnloaded(DebuggerModule Module) { } - public virtual void OnCallstack(string[] Callstack) { } + public virtual void OnCallstack(DebuggerCallstack Callstack) { } public virtual void OnDebugOutput(string Message) { } } diff --git a/src/CxbxDebugger/DebuggerModule.cs b/src/CxbxDebugger/DebuggerModule.cs new file mode 100644 index 000000000..df58fa78d --- /dev/null +++ b/src/CxbxDebugger/DebuggerModule.cs @@ -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; } + } +} diff --git a/src/CxbxDebugger/DebuggerProcess.cs b/src/CxbxDebugger/DebuggerProcess.cs new file mode 100644 index 000000000..388019ddb --- /dev/null +++ b/src/CxbxDebugger/DebuggerProcess.cs @@ -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 + { + + } +} diff --git a/src/CxbxDebugger/DebuggerThread.cs b/src/CxbxDebugger/DebuggerThread.cs new file mode 100644 index 000000000..142674a45 --- /dev/null +++ b/src/CxbxDebugger/DebuggerThread.cs @@ -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 + { + + } +} diff --git a/src/CxbxDebugger/Form1.cs b/src/CxbxDebugger/Form1.cs index d15891607..3677478f8 100644 --- a/src/CxbxDebugger/Form1.cs +++ b/src/CxbxDebugger/Form1.cs @@ -114,17 +114,19 @@ namespace CxbxDebugger 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 lbConsole.Invoke(new MethodInvoker(delegate () { - lbConsole.Items.Add(Message); + lbConsole.Items.Insert(0, MessageStamped); })); } else { - lbConsole.Items.Add(Message); + lbConsole.Items.Insert(0, MessageStamped); } } @@ -180,9 +182,9 @@ namespace CxbxDebugger 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) @@ -190,12 +192,12 @@ namespace CxbxDebugger frm.DebugEvent("Debug string: " + Message); } - public override void OnCallstack(string[] Callstack) + public override void OnCallstack(DebuggerCallstack Callstack) { - // Placeholder display - foreach( string str in Callstack) + foreach( DebuggerStackFrame StackFrame in Callstack.StackFrames) { - frm.DebugEvent("Callstack: " + str); + var stackFrameStr = string.Format("{0:X8} and {1:X8}", (uint)StackFrame.BasePointer, (uint)StackFrame.CodeAddress); + frm.DebugEvent("Callstack: " + stackFrameStr); } } } diff --git a/src/CxbxDebugger/Win32/ComPtr.cs b/src/CxbxDebugger/Win32/ComPtr.cs deleted file mode 100644 index 8b2fa6acd..000000000 --- a/src/CxbxDebugger/Win32/ComPtr.cs +++ /dev/null @@ -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 : 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 Create(T ptr) where T : class { - return new ComPtr(ptr); - } - } -} diff --git a/src/CxbxDebugger/Win32/HResults.cs b/src/CxbxDebugger/Win32/HResults.cs deleted file mode 100644 index 55ff7f9d7..000000000 --- a/src/CxbxDebugger/Win32/HResults.cs +++ /dev/null @@ -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); - } -} \ No newline at end of file