// // // // // $Revision: 2274 $ // // Regular expresion: // ^{\t*}{(:Ll| )*{:i} *\(((.# {:i}, |\))|())^6\)*}\n\t*\{(.|\n)@\} // Output: \1 - intention \2 - declaration \3 - function name \4-9 parameters // Replace with: // \1\2\n\1{\n\1\tEnterCallback(PausedReason.Other, "\3");\n\1\t\n\1\tExitCallback_Continue();\n\1} using System; using System.Runtime.InteropServices; using Debugger.Wrappers.CorDebug; namespace Debugger { /// /// Handles all callbacks of a given process /// class ManagedCallback { Process process; bool pauseProcessInsteadOfContinue; [Debugger.Tests.Ignore] public Process Process { get { return process; } } public ManagedCallback(Process process) { this.process = process; } void EnterCallback(PausedReason pausedReason, string name, ICorDebugProcess pProcess) { process.TraceMessage("Callback: " + name); System.Diagnostics.Debug.Assert(process.CorProcess == pProcess); // Check state if (process.IsRunning || // After break is pressed we may receive some messages that were already queued process.PauseSession.PausedReason == PausedReason.ForcedBreak || // ExitProcess may be called at any time when debuggee is killed name == "ExitProcess") { if (process.IsPaused && process.PauseSession.PausedReason == PausedReason.ForcedBreak && name != "ExitProcess") { process.TraceMessage("Processing post-break callback"); // Continue the break, process is still breaked because of the callback process.Continue(); pauseProcessInsteadOfContinue = true; } else { pauseProcessInsteadOfContinue = false; } // Remove expired threads and functions foreach(Thread thread in process.Threads) { thread.CheckExpiration(); } process.NotifyPaused(new PauseSession(pausedReason)); } else { throw new DebuggerException("Invalid state at the start of callback"); } } void EnterCallback(PausedReason pausedReason, string name, ICorDebugAppDomain pAppDomain) { EnterCallback(pausedReason, name, pAppDomain.Process); } void EnterCallback(PausedReason pausedReason, string name, ICorDebugThread pThread) { EnterCallback(pausedReason, name, pThread.Process); process.SelectedThread = process.GetThread(pThread); } void ExitCallback_Continue() { if (pauseProcessInsteadOfContinue) { ExitCallback_Paused(); } else { process.Continue(); } } void ExitCallback_Paused() { if (process.Evaluating) { // Ignore events during property evaluation ExitCallback_Continue(); } else { process.Pause(process.PauseSession.PausedReason != PausedReason.EvalComplete); } } #region Program folow control public void StepComplete(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugStepper pStepper, CorDebugStepReason reason) { EnterCallback(PausedReason.StepComplete, "StepComplete (" + reason.ToString() + ")", pThread); Thread thread = process.GetThread(pThread); Stepper stepper = thread.GetStepper(pStepper); process.TraceMessage(" - stepper info: " + stepper.ToString()); thread.Steppers.Remove(stepper); stepper.OnStepComplete(); if (stepper.PauseWhenComplete) { if (process.SelectedThread.LastFunction.HasSymbols) { ExitCallback_Paused(); } else { // This can only happen when JMC is disabled (ie NET1.1 or StepOut) if (stepper.Operation == Stepper.StepperOperation.StepOut) { // Create new stepper and keep going process.TraceMessage(" - stepping out of code without symbols at " + process.SelectedThread.LastFunction.ToString()); new Stepper(process.SelectedThread.LastFunction, "Stepper out of code without symbols").StepOut(); ExitCallback_Continue(); } else { // NET1.1: There is extra step over stepper, just keep going process.TraceMessage(" - leaving code without symbols"); ExitCallback_Continue(); } } } else { ExitCallback_Continue(); } } // Do not pass the pBreakpoint parameter as ICorDebugBreakpoint - marshaling of it fails in .NET 1.1 public void Breakpoint(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, IntPtr pBreakpoint) { EnterCallback(PausedReason.Breakpoint, "Breakpoint", pThread); this.process.NotifyPaused(new PauseSession(PausedReason.Breakpoint)); ExitCallback_Paused(); // foreach (Breakpoint b in debugger.Breakpoints) { // if (b.Equals(pBreakpoint)) { // // TODO: Check that this works // b.OnHit(); // } // } } public void BreakpointSetError(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugBreakpoint pBreakpoint, uint dwError) { EnterCallback(PausedReason.Other, "BreakpointSetError", pThread); ExitCallback_Continue(); } public unsafe void Break(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread) { EnterCallback(PausedReason.Break, "Break", pThread); ExitCallback_Paused(); } public void ControlCTrap(ICorDebugProcess pProcess) { EnterCallback(PausedReason.ControlCTrap, "ControlCTrap", pProcess); ExitCallback_Paused(); } public unsafe void Exception(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, int unhandled) { // Exception2 is used in .NET Framework 2.0 if (process.DebuggeeVersion.StartsWith("v1.")) { // Forward the call to Exception2, which handles EnterCallback and ExitCallback ExceptionType exceptionType = (unhandled != 0)?ExceptionType.DEBUG_EXCEPTION_UNHANDLED:ExceptionType.DEBUG_EXCEPTION_FIRST_CHANCE; Exception2(pAppDomain, pThread, null, 0, (CorDebugExceptionCallbackType)exceptionType, 0); } else { // This callback should be ignored in v2 applications EnterCallback(PausedReason.Other, "Exception", pThread); ExitCallback_Continue(); } } #endregion #region Various public void LogSwitch(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, int lLevel, uint ulReason, string pLogSwitchName, string pParentName) { EnterCallback(PausedReason.Other, "LogSwitch", pThread); ExitCallback_Continue(); } public void LogMessage(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, int lLevel, string pLogSwitchName, string pMessage) { EnterCallback(PausedReason.Other, "LogMessage", pThread); process.OnLogMessage(new MessageEventArgs(process, lLevel, pMessage, pLogSwitchName)); ExitCallback_Continue(); } public void EditAndContinueRemap(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugFunction pFunction, int fAccurate) { EnterCallback(PausedReason.Other, "EditAndContinueRemap", pThread); ExitCallback_Continue(); } public void EvalException(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugEval corEval) { EnterCallback(PausedReason.EvalComplete, "EvalException", pThread); HandleEvalComplete(pAppDomain, pThread, corEval, true); } public void EvalComplete(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugEval corEval) { EnterCallback(PausedReason.EvalComplete, "EvalComplete", pThread); HandleEvalComplete(pAppDomain, pThread, corEval, false); } void HandleEvalComplete(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugEval corEval, bool exception) { // Let the eval know that the CorEval has finished Eval eval = process.GetEval(corEval); eval.NotifyEvaluationComplete(!exception); process.NotifyEvaluationComplete(eval); if (process.SetupNextEvaluation()) { ExitCallback_Continue(); } else { ExitCallback_Paused(); } } public void DebuggerError(ICorDebugProcess pProcess, int errorHR, uint errorCode) { EnterCallback(PausedReason.DebuggerError, "DebuggerError", pProcess); string errorText = String.Format("Debugger error: \nHR = 0x{0:X} \nCode = 0x{1:X}", errorHR, errorCode); if ((uint)errorHR == 0x80131C30) { errorText += "\n\nIf you are running a 64-bit system this setting might help:\nProject -> Project Options -> Compiling -> Target CPU = 32-bit Intel"; } System.Windows.Forms.MessageBox.Show(errorText); ExitCallback_Paused(); } public void UpdateModuleSymbols(ICorDebugAppDomain pAppDomain, ICorDebugModule pModule, IStream pSymbolStream) { EnterCallback(PausedReason.Other, "UpdateModuleSymbols", pAppDomain); ExitCallback_Continue(); } #endregion #region Start of Application public void CreateProcess(ICorDebugProcess pProcess) { EnterCallback(PausedReason.Other, "CreateProcess", pProcess); // Process is added in NDebugger.Start ExitCallback_Continue(); } public void CreateAppDomain(ICorDebugProcess pProcess, ICorDebugAppDomain pAppDomain) { EnterCallback(PausedReason.Other, "CreateAppDomain", pAppDomain); pAppDomain.Attach(); ExitCallback_Continue(); } public void LoadAssembly(ICorDebugAppDomain pAppDomain, ICorDebugAssembly pAssembly) { EnterCallback(PausedReason.Other, "LoadAssembly", pAppDomain); ExitCallback_Continue(); } public unsafe void LoadModule(ICorDebugAppDomain pAppDomain, ICorDebugModule pModule) { EnterCallback(PausedReason.Other, "LoadModule", pAppDomain); process.AddModule(pModule); ExitCallback_Continue(); } public void NameChange(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread) { if (pAppDomain != null) { EnterCallback(PausedReason.Other, "NameChange: pAppDomain", pAppDomain); ExitCallback_Continue(); } if (pThread != null) { EnterCallback(PausedReason.Other, "NameChange: pThread", pThread); Thread thread = process.GetThread(pThread); thread.HasBeenLoaded = true; ExitCallback_Continue(); } } public void CreateThread(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread) { // We can not use pThread since it has not been added yet // and we continue from this callback anyway EnterCallback(PausedReason.Other, "CreateThread " + pThread.ID, pAppDomain); process.AddThread(pThread); ExitCallback_Continue(); } public void LoadClass(ICorDebugAppDomain pAppDomain, ICorDebugClass c) { EnterCallback(PausedReason.Other, "LoadClass", pAppDomain); ExitCallback_Continue(); } #endregion #region Exit of Application public void UnloadClass(ICorDebugAppDomain pAppDomain, ICorDebugClass c) { EnterCallback(PausedReason.Other, "UnloadClass", pAppDomain); ExitCallback_Continue(); } public void UnloadModule(ICorDebugAppDomain pAppDomain, ICorDebugModule pModule) { EnterCallback(PausedReason.Other, "UnloadModule", pAppDomain); process.RemoveModule(pModule); ExitCallback_Continue(); } public void UnloadAssembly(ICorDebugAppDomain pAppDomain, ICorDebugAssembly pAssembly) { EnterCallback(PausedReason.Other, "UnloadAssembly", pAppDomain); ExitCallback_Continue(); } public void ExitThread(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread) { // It seems that ICorDebugThread is still not dead and can be used EnterCallback(PausedReason.Other, "ExitThread " + pThread.ID, pThread); process.GetThread(pThread).NotifyNativeThreadExited(); try { ExitCallback_Continue(); } catch (COMException e) { // For some reason this sometimes happens in .NET 1.1 process.TraceMessage("Continue failed in ExitThread callback: " + e.Message); } } public void ExitAppDomain(ICorDebugProcess pProcess, ICorDebugAppDomain pAppDomain) { EnterCallback(PausedReason.Other, "ExitAppDomain", pAppDomain); ExitCallback_Continue(); } public void ExitProcess(ICorDebugProcess pProcess) { EnterCallback(PausedReason.Other, "ExitProcess", pProcess); process.NotifyHasExpired(); } #endregion #region ICorDebugManagedCallback2 Members public void ChangeConnection(ICorDebugProcess pProcess, uint dwConnectionId) { EnterCallback(PausedReason.Other, "ChangeConnection", pProcess); ExitCallback_Continue(); } public void CreateConnection(ICorDebugProcess pProcess, uint dwConnectionId, IntPtr pConnName) { EnterCallback(PausedReason.Other, "CreateConnection", pProcess); ExitCallback_Continue(); } public void DestroyConnection(ICorDebugProcess pProcess, uint dwConnectionId) { EnterCallback(PausedReason.Other, "DestroyConnection", pProcess); ExitCallback_Continue(); } public void Exception2(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugFrame pFrame, uint nOffset, CorDebugExceptionCallbackType exceptionType, uint dwFlags) { EnterCallback(PausedReason.Exception, "Exception2 (type=" + exceptionType.ToString() + ")", pThread); // This callback is also called from Exception(...)!!!! (the .NET 1.1 version) // Whatch out for the zeros and null! // Exception -> Exception2(pAppDomain, pThread, null, 0, exceptionType, 0); process.SelectedThread.CurrentExceptionType = (ExceptionType)exceptionType; if (ExceptionType.DEBUG_EXCEPTION_UNHANDLED != (ExceptionType)exceptionType) { // Handled exception if (process.PauseOnHandledException) { ExitCallback_Paused(); } else { ExitCallback_Continue(); } } else { // Unhandled exception ExitCallback_Paused(); } } // public void Exception2(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugFrame pFrame, uint nOffset, CorDebugExceptionCallbackType exceptionType, uint dwFlags) // { // EnterCallback(PausedReason.Exception, "Exception2 (type=" + exceptionType.ToString() + ")", pThread); // // // This callback is also called from Exception(...)!!!! (the .NET 1.1 version) // // Watch out for the zeros and null! // // Exception -> Exception2(pAppDomain, pThread, null, 0, exceptionType, 0); // // process.SelectedThread.CurrentException = new Exception(new Value(process, new Expressions.CurrentExceptionExpression(), process.SelectedThread.CorThread.CurrentException)); // process.SelectedThread.CurrentException_DebuggeeState = process.DebuggeeState; // process.SelectedThread.CurrentExceptionType = (ExceptionType)exceptionType; // process.SelectedThread.CurrentExceptionIsUnhandled = (ExceptionType)exceptionType == ExceptionType.Unhandled; // // if (process.SelectedThread.CurrentExceptionIsUnhandled || // process.PauseOnHandledException) // { // pauseOnNextExit = true; // } // ExitCallback(); // } public void ExceptionUnwind(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, CorDebugExceptionUnwindCallbackType dwEventType, uint dwFlags) { EnterCallback(PausedReason.ExceptionIntercepted, "ExceptionUnwind", pThread); if (dwEventType == CorDebugExceptionUnwindCallbackType.DEBUG_EXCEPTION_INTERCEPTED) { ExitCallback_Paused(); } else { ExitCallback_Continue(); } } public void FunctionRemapComplete(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugFunction pFunction) { EnterCallback(PausedReason.Other, "FunctionRemapComplete", pThread); ExitCallback_Continue(); } public void FunctionRemapOpportunity(ICorDebugAppDomain pAppDomain, ICorDebugThread pThread, ICorDebugFunction pOldFunction, ICorDebugFunction pNewFunction, uint oldILOffset) { EnterCallback(PausedReason.Other, "FunctionRemapOpportunity", pThread); ExitCallback_Continue(); } public void MDANotification(ICorDebugController c, ICorDebugThread t, ICorDebugMDA mda) { if (c.Is()) { EnterCallback(PausedReason.Other, "MDANotification", c.CastTo()); } else if (c.Is()){ EnterCallback(PausedReason.Other, "MDANotification", c.CastTo()); } else { throw new System.Exception("Unknown callback argument"); } ExitCallback_Continue(); } #endregion } }