Browse Source

Refactor handling of debuggee exceptions

newNRvisualizers
David Srbecký 13 years ago
parent
commit
7d1735c54f
  1. 24
      src/AddIns/Debugger/Debugger.AddIn/Service/DebuggeeExceptionForm.cs
  2. 162
      src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
  3. 1
      src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj
  4. 110
      src/AddIns/Debugger/Debugger.Core/Exception.cs
  5. 23
      src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
  6. 11
      src/AddIns/Debugger/Debugger.Core/NDebugger.cs
  7. 2
      src/AddIns/Debugger/Debugger.Core/Process.cs
  8. 28
      src/AddIns/Debugger/Debugger.Core/Thread.cs
  9. 14
      src/AddIns/Debugger/Debugger.Tests/DebuggerTestsBase.cs
  10. 2
      src/AddIns/Debugger/Debugger.Tests/Tests/Exception_Custom.cs

24
src/AddIns/Debugger/Debugger.AddIn/Service/DebuggeeExceptionForm.cs

@ -15,13 +15,15 @@ namespace ICSharpCode.SharpDevelop.Services
internal sealed partial class DebuggeeExceptionForm internal sealed partial class DebuggeeExceptionForm
{ {
Process process; Process process;
bool isUnhandled;
public Debugger.Exception Exception { get; private set; } public bool Break { get; set; }
DebuggeeExceptionForm(Process process) DebuggeeExceptionForm(Process process)
{ {
InitializeComponent(); InitializeComponent();
this.Break = true;
this.process = process; this.process = process;
this.process.Exited += ProcessHandler; this.process.Exited += ProcessHandler;
@ -58,18 +60,20 @@ namespace ICSharpCode.SharpDevelop.Services
this.process.Resumed -= ProcessHandler; this.process.Resumed -= ProcessHandler;
} }
public static void Show(Process process, string title, string message, string stacktrace, Bitmap icon, bool isUnhandled, Debugger.Exception exception) public static bool Show(Process process, string title, string type, string stacktrace, Bitmap icon, bool isUnhandled)
{ {
DebuggeeExceptionForm form = new DebuggeeExceptionForm(process); DebuggeeExceptionForm form = new DebuggeeExceptionForm(process);
form.Text = title; form.Text = title;
form.pictureBox.Image = icon; form.pictureBox.Image = icon;
form.lblExceptionText.Text = message; form.lblExceptionText.Text = type;
form.exceptionView.Text = stacktrace; form.exceptionView.Text = stacktrace;
form.isUnhandled = isUnhandled;
form.btnContinue.Enabled = !isUnhandled; form.btnContinue.Enabled = !isUnhandled;
form.Exception = exception;
form.Show(SD.WinForms.MainWin32Window); // Showing the form as dialg seems like a resonable thing in the presence of potentially multiple
// concurent debugger evetns
form.ShowDialog(SD.WinForms.MainWin32Window);
return form.Break;
} }
void ExceptionViewDoubleClick(object sender, EventArgs e) void ExceptionViewDoubleClick(object sender, EventArgs e)
@ -106,9 +110,7 @@ namespace ICSharpCode.SharpDevelop.Services
void BtnBreakClick(object sender, EventArgs e) void BtnBreakClick(object sender, EventArgs e)
{ {
if (Exception.IsUnhandled) this.Break = true;
Close();
else if (((WindowsDebugger)DebuggerService.CurrentDebugger).BreakAndInterceptHandledException(Exception))
Close(); Close();
} }
@ -120,7 +122,7 @@ namespace ICSharpCode.SharpDevelop.Services
void BtnContinueClick(object sender, EventArgs e) void BtnContinueClick(object sender, EventArgs e)
{ {
this.process.AsyncContinue(); this.Break = false;
Close(); Close();
} }
} }

162
src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
@ -512,35 +513,106 @@ namespace ICSharpCode.SharpDevelop.Services
CurrentThread = e.Thread; CurrentThread = e.Thread;
CurrentStackFrame = CurrentThread != null ? CurrentThread.MostRecentUserStackFrame : null; CurrentStackFrame = CurrentThread != null ? CurrentThread.MostRecentUserStackFrame : null;
if (e.ExceptionThrown != null) { // We can have several events happening at the same time
HandleException(e); bool breakProcess = e.Break;
// Handle thrown exceptions
foreach(Thread exceptionThread in e.ExceptionsThrown) {
JumpToCurrentLine();
Thread evalThread = exceptionThread;
bool isUnhandled = (exceptionThread.CurrentExceptionType == ExceptionType.Unhandled);
Value exception = exceptionThread.CurrentException.GetPermanentReferenceOfHeapValue();
List<Value> innerExceptions = new List<Value>();
for(Value innerException = exception; !innerException.IsNull; innerException = innerException.GetFieldValue("_innerException")) {
innerExceptions.Add(innerException.GetPermanentReferenceOfHeapValue());
}
// Get the exception description
string stacktrace = string.Empty;
for(int i = 0; i < innerExceptions.Count; i++) {
if (i > 0) {
stacktrace += " ---> ";
}
stacktrace += innerExceptions[i].Type.FullName;
Value messageValue = innerExceptions[i].GetFieldValue("_message");
if (!messageValue.IsNull) {
stacktrace += ": " + messageValue.AsString();
}
}
stacktrace += Environment.NewLine + Environment.NewLine;
// Get the stacktrace
string formatSymbols = StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}");
string formatNoSymbols = StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}");
if (isUnhandled) {
// Need to intercept now so that we can evaluate properties
// Intercept may fail (eg StackOverflow)
if (exceptionThread.InterceptException()) {
try {
// Try to evaluate the StackTrace property to get the .NET formated stacktrace
for(int i = innerExceptions.Count - 1; i >= 0; i--) {
Value stackTraceValue = innerExceptions[i].GetPropertyValue(evalThread, "StackTrace");
if (!stackTraceValue.IsNull) {
stacktrace += stackTraceValue.AsString() + Environment.NewLine;
}
if (i > 0) {
stacktrace += " " + StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.EndOfInnerException}") + Environment.NewLine;
}
}
} catch (GetValueException) {
stacktrace += exceptionThread.GetStackTrace(formatSymbols, formatNoSymbols);
}
} else {
stacktrace += StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Error.CannotInterceptException}") + Environment.NewLine + Environment.NewLine;
stacktrace += exceptionThread.GetStackTrace(formatSymbols, formatNoSymbols);
}
} else {
// Do not intercept handled expetions
stacktrace += exceptionThread.GetStackTrace(formatSymbols, formatNoSymbols);
}
string title = isUnhandled ? StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Title.Unhandled}") : StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Title.Handled}");
string type = string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Message}"), exception.Type);
Bitmap icon = WinFormsResourceService.GetBitmap(isUnhandled ? "Icons.32x32.Error" : "Icons.32x32.Warning");
if (DebuggeeExceptionForm.Show(e.Process, title, type, stacktrace, icon, isUnhandled)) {
breakProcess = true;
// The dialog box is allowed to kill the process
if (e.Process.HasExited) {
return; return;
} }
// Intercept handled exception *after* the user decided to break
if (!isUnhandled) {
if (!exceptionThread.InterceptException()) {
MessageService.ShowError("${res:MainWindow.Windows.Debug.ExceptionForm.Error.CannotInterceptHandledException}");
}
}
}
}
bool breakpointHit = false; // Handle breakpoints
foreach (Breakpoint breakpoint in e.BreakpointsHit) { foreach (Breakpoint breakpoint in e.BreakpointsHit) {
var bookmark = SD.BookmarkManager.Bookmarks.OfType<BreakpointBookmark>().First(bm => bm.InternalBreakpointObject == breakpoint); var bookmark = SD.BookmarkManager.Bookmarks.OfType<BreakpointBookmark>().First(bm => bm.InternalBreakpointObject == breakpoint);
if (string.IsNullOrEmpty(bookmark.Condition)) { if (string.IsNullOrEmpty(bookmark.Condition)) {
breakpointHit = true; breakProcess = true;
} else { } else {
if (EvaluateCondition(bookmark.Condition)) { if (EvaluateCondition(bookmark.Condition)) {
breakpointHit = true; breakProcess = true;
DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAtBecause}") + "\n", bookmark.LineNumber, bookmark.FileName, bookmark.Condition)); DebuggerService.PrintDebugMessage(string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Conditional.Breakpoints.BreakpointHitAtBecause}") + "\n", bookmark.LineNumber, bookmark.FileName, bookmark.Condition));
} }
} }
} }
// We can have several, potentially conditional, breakpoints at the same time if (breakProcess) {
// ... as well as stepper happening on the same line
if (e.Break || breakpointHit) {
LoggingService.Info("Jump to current line");
JumpToCurrentLine(); JumpToCurrentLine();
RefreshPads();
} else { } else {
e.Process.AsyncContinue(); e.Process.AsyncContinue();
} }
RefreshPads();
} }
void debuggedProcess_DebuggingResumed(object sender, DebuggerEventArgs e) void debuggedProcess_DebuggingResumed(object sender, DebuggerEventArgs e)
@ -554,53 +626,6 @@ namespace ICSharpCode.SharpDevelop.Services
RefreshPads(); RefreshPads();
} }
void HandleException(DebuggerPausedEventArgs e)
{
JumpToCurrentLine();
StringBuilder stacktraceBuilder = new StringBuilder();
if (e.ExceptionThrown.IsUnhandled) {
// Need to intercept now so that we can evaluate properties
if (e.Thread.InterceptException(e.ExceptionThrown)) {
stacktraceBuilder.AppendLine(e.ExceptionThrown.ToString());
string stackTrace;
try {
stackTrace = e.ExceptionThrown.GetStackTrace(e.Thread, StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.EndOfInnerException}"));
} catch (GetValueException) {
stackTrace = e.Thread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}"));
}
stacktraceBuilder.Append(stackTrace);
} else {
// For example, happens on stack overflow
stacktraceBuilder.AppendLine(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Error.CannotInterceptException}"));
stacktraceBuilder.AppendLine(e.ExceptionThrown.ToString());
stacktraceBuilder.Append(e.Thread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}")));
}
} else {
stacktraceBuilder.AppendLine(e.ExceptionThrown.ToString());
stacktraceBuilder.Append(e.Thread.GetStackTrace(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.Symbols}"), StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.LineFormat.NoSymbols}")));
}
string title = e.ExceptionThrown.IsUnhandled ? StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Title.Unhandled}") : StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Title.Handled}");
string message = string.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.ExceptionForm.Message}"), e.ExceptionThrown.Type);
Bitmap icon = WinFormsResourceService.GetBitmap(e.ExceptionThrown.IsUnhandled ? "Icons.32x32.Error" : "Icons.32x32.Warning");
DebuggeeExceptionForm.Show(e.Process, title, message, stacktraceBuilder.ToString(), icon, e.ExceptionThrown.IsUnhandled, e.ExceptionThrown);
RefreshPads();
}
public bool BreakAndInterceptHandledException(Debugger.Exception exception)
{
if (!CurrentThread.InterceptException(exception)) {
MessageService.ShowError("${res:MainWindow.Windows.Debug.ExceptionForm.Error.CannotInterceptHandledException}");
return false;
}
JumpToCurrentLine();
return true;
}
public static Value Evaluate(string code) public static Value Evaluate(string code)
{ {
if (CurrentStackFrame == null || CurrentStackFrame.NextStatement == null) if (CurrentStackFrame == null || CurrentStackFrame.NextStatement == null)
@ -620,7 +645,14 @@ namespace ICSharpCode.SharpDevelop.Services
// if (debuggedProcess.IsSelectedFrameForced()) { // if (debuggedProcess.IsSelectedFrameForced()) {
if (CurrentThread != null && CurrentStackFrame.HasSymbols) { if (CurrentThread != null && CurrentStackFrame.HasSymbols) {
JumpToSourceCode(); if (CurrentProcess == null || CurrentStackFrame == null)
return;
SourcecodeSegment nextStatement = CurrentStackFrame.NextStatement;
if (nextStatement != null) {
DebuggerService.RemoveCurrentLineMarker();
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
}
} else { } else {
#warning JumpToDecompiledCode(CurrentStackFrame); #warning JumpToDecompiledCode(CurrentStackFrame);
} }
@ -636,18 +668,6 @@ namespace ICSharpCode.SharpDevelop.Services
// } // }
} }
void JumpToSourceCode()
{
if (CurrentProcess == null || CurrentStackFrame == null)
return;
SourcecodeSegment nextStatement = CurrentStackFrame.NextStatement;
if (nextStatement != null) {
DebuggerService.RemoveCurrentLineMarker();
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
}
}
/* /*
void JumpToDecompiledCode(Debugger.StackFrame frame) void JumpToDecompiledCode(Debugger.StackFrame frame)
{ {

1
src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj

@ -69,7 +69,6 @@
<Compile Include="Breakpoint.cs" /> <Compile Include="Breakpoint.cs" />
<Compile Include="DebuggerException.cs" /> <Compile Include="DebuggerException.cs" />
<Compile Include="Eval.cs" /> <Compile Include="Eval.cs" />
<Compile Include="Exception.cs" />
<Compile Include="Interop\Common.cs" /> <Compile Include="Interop\Common.cs" />
<Compile Include="Interop\CorDebug.cs" /> <Compile Include="Interop\CorDebug.cs" />
<Compile Include="Interop\CorDebugExtensionMethods.cs" /> <Compile Include="Interop\CorDebugExtensionMethods.cs" />

110
src/AddIns/Debugger/Debugger.Core/Exception.cs

@ -1,110 +0,0 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System.Text;
namespace Debugger
{
enum ExceptionType
{
FirstChance = 1,
UserFirstChance = 2,
CatchHandlerFound = 3,
Unhandled = 4,
}
/// <summary> This convenience class provides access to an exception within the debugee. </summary>
/// <seealso cref="System.Exception" />
public class Exception: DebuggerObject
{
Value exception;
public Value Value {
get { return exception; }
}
ExceptionType ExceptionType { get; set; }
public bool IsUnhandled {
get { return this.ExceptionType == ExceptionType.Unhandled; }
}
internal Exception(Value exception, ExceptionType exceptionType)
{
this.exception = exception;
this.ExceptionType = exceptionType;
}
/// <summary> The <c>GetType().FullName</c> of the exception. </summary>
/// <seealso cref="System.Exception" />
public string Type {
get {
return exception.Type.FullName;
}
}
/// <summary> The <c>Message</c> property of the exception. </summary>
/// <seealso cref="System.Exception" />
public string Message {
get {
Value message = exception.GetFieldValue("_message");
return message.IsNull ? string.Empty : message.AsString();
}
}
/// <summary> The <c>InnerException</c> property of the exception. </summary>
/// <seealso cref="System.Exception" />
public Exception InnerException {
get {
Value innerException = exception.GetFieldValue("_innerException");
return innerException.IsNull ? null : new Exception(innerException, this.ExceptionType);
}
}
public void MakeValuePermanent()
{
exception = exception.GetPermanentReferenceOfHeapValue();
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append(this.Type);
if (!string.IsNullOrEmpty(this.Message)) {
sb.Append(": ");
sb.Append(this.Message);
}
if (this.InnerException != null) {
sb.Append(" ---> ");
sb.Append(this.InnerException.ToString());
}
return sb.ToString();
}
public string GetStackTrace(Thread evalThread)
{
return GetStackTrace(evalThread, "--- End of inner exception stack trace ---");
}
/// <summary> Returs formated stacktrace for the exception </summary>
/// <exception cref="GetValueException"> Getting the stacktrace involves property
/// evaluation so GetValueException can be thrown in some cicumstances. </exception>
public string GetStackTrace(Thread evalThread, string endOfInnerExceptionFormat)
{
StringBuilder sb = new StringBuilder();
if (this.InnerException != null) {
sb.Append(this.InnerException.GetStackTrace(evalThread, endOfInnerExceptionFormat));
sb.Append(" ");
sb.Append(endOfInnerExceptionFormat);
sb.AppendLine();
}
// Note that evaluation is not possible after a stackoverflow exception
Value stackTrace = exception.GetPropertyValue(evalThread, "StackTrace");
if (!stackTrace.IsNull) {
sb.Append(stackTrace.AsString());
sb.AppendLine();
}
return sb.ToString();
}
}
}

23
src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs

@ -48,12 +48,7 @@ namespace Debugger
// The event will be raised as soon as the callback queue is drained. // The event will be raised as soon as the callback queue is drained.
DebuggerPausedEventArgs GetPausedEventArgs() DebuggerPausedEventArgs GetPausedEventArgs()
{ {
if (pausedEventArgs == null) { return pausedEventArgs ?? (pausedEventArgs = new DebuggerPausedEventArgs(process));
pausedEventArgs = new DebuggerPausedEventArgs();
pausedEventArgs.Process = process;
pausedEventArgs.BreakpointsHit = new List<Breakpoint>();
}
return pausedEventArgs;
} }
void EnterCallback(string name, ICorDebugProcess pProcess) void EnterCallback(string name, ICorDebugProcess pProcess)
@ -67,8 +62,9 @@ namespace Debugger
if (process.IsPaused) { if (process.IsPaused) {
process.TraceMessage("Processing post-break callback"); process.TraceMessage("Processing post-break callback");
// Decrese the "break count" from 2 to 1 - does not actually continue // Decrese the "break count" from 2 to 1 - does not actually continue
// TODO: This inccorectly marks the debugger as running
process.AsyncContinue(DebuggeeStateAction.Keep, new Thread[] {}, null); process.AsyncContinue(DebuggeeStateAction.Keep, new Thread[] {}, null);
// Make sure we stay pauses after the callback is handled // Make sure we stay paused after the callback is handled
pauseOnNextExit = true; pauseOnNextExit = true;
return; return;
} }
@ -98,9 +94,7 @@ namespace Debugger
if (hasQueuedCallbacks) if (hasQueuedCallbacks)
process.TraceMessage("Process has queued callbacks"); process.TraceMessage("Process has queued callbacks");
// TODO: Can we drain the queue? if (hasQueuedCallbacks) {
if (hasQueuedCallbacks && (pausedEventArgs == null || pausedEventArgs.ExceptionThrown == null)) {
// Process queued callbacks if no exception occurred
process.AsyncContinue(DebuggeeStateAction.Keep, null, null); process.AsyncContinue(DebuggeeStateAction.Keep, null, null);
} else if (process.Evaluating) { } else if (process.Evaluating) {
// Ignore events during property evaluation // Ignore events during property evaluation
@ -534,11 +528,12 @@ namespace Debugger
bool pauseOnHandled = !process.Evaluating && process.Options != null && process.Options.PauseOnHandledExceptions; bool pauseOnHandled = !process.Evaluating && process.Options != null && process.Options.PauseOnHandledExceptions;
if (exceptionType == ExceptionType.Unhandled || (pauseOnHandled && exceptionType == ExceptionType.CatchHandlerFound)) { if (exceptionType == ExceptionType.Unhandled || (pauseOnHandled && exceptionType == ExceptionType.CatchHandlerFound)) {
Value value = new Value(process.GetAppDomain(pAppDomain), pThread.GetCurrentException()).GetPermanentReferenceOfHeapValue();
if (GetPausedEventArgs().ExceptionThrown != null) // Multiple exceptions can happen at the same time on multiple threads
throw new DebuggerException("Exception is already being processed"); // (I have managed to create a test application to trigger it)
GetPausedEventArgs().ExceptionThrown = new Exception(value, exceptionType); Thread thread = process.GetThread(pThread);
thread.CurrentExceptionType = exceptionType;
GetPausedEventArgs().ExceptionsThrown.Add(thread);
pauseOnNextExit = true; pauseOnNextExit = true;
} }

11
src/AddIns/Debugger/Debugger.Core/NDebugger.cs

@ -320,11 +320,18 @@ namespace Debugger
/// <summary> Breakpoints hit </summary> /// <summary> Breakpoints hit </summary>
public List<Breakpoint> BreakpointsHit { get; set; } public List<Breakpoint> BreakpointsHit { get; set; }
/// <summary> Exception thrown </summary> /// <summary> Threads which have exceptions </summary>
public Exception ExceptionThrown { get; set; } public List<Thread> ExceptionsThrown { get; set; }
/// <summary> Break, stepper or any other pause reason. </summary> /// <summary> Break, stepper or any other pause reason. </summary>
public bool Break { get; set; } public bool Break { get; set; }
public DebuggerPausedEventArgs(Process process)
{
this.Process = process;
this.BreakpointsHit = new List<Breakpoint>();
this.ExceptionsThrown = new List<Thread>();
}
} }
[Serializable] [Serializable]

2
src/AddIns/Debugger/Debugger.Core/Process.cs

@ -402,7 +402,7 @@ namespace Debugger
corProcess.Stop(uint.MaxValue); // Infinite; ignored anyway corProcess.Stop(uint.MaxValue); // Infinite; ignored anyway
NotifyPaused(); NotifyPaused();
OnPaused(new DebuggerPausedEventArgs() { Process = this }); OnPaused(new DebuggerPausedEventArgs(this) { Break = true });
} }
public void Detach() public void Detach()

28
src/AddIns/Debugger/Debugger.Core/Thread.cs

@ -11,6 +11,14 @@ using Debugger.Interop.CorDebug;
namespace Debugger namespace Debugger
{ {
public enum ExceptionType
{
FirstChance = 1,
UserFirstChance = 2,
CatchHandlerFound = 3,
Unhandled = 4,
}
public class Thread: DebuggerObject public class Thread: DebuggerObject
{ {
// AppDomain for thread can be changing // AppDomain for thread can be changing
@ -45,6 +53,16 @@ namespace Debugger
set { currentStepIn = value; } set { currentStepIn = value; }
} }
[Debugger.Tests.Ignore]
public ExceptionType CurrentExceptionType { get; set; }
[Debugger.Tests.Ignore]
public Value CurrentException {
get {
return new Value(this.AppDomain, this.CorThread.GetCurrentException());
}
}
/// <summary> From time to time the thread may be in invalid state. </summary> /// <summary> From time to time the thread may be in invalid state. </summary>
public bool IsInValidState { public bool IsInValidState {
get { get {
@ -143,11 +161,9 @@ namespace Debugger
} }
} }
/// <summary> Tryies to intercept the current exception. /// <summary> Tryies to intercept the current exception. </summary>
/// The intercepted expression stays available through the CurrentException property. </summary> /// <returns> False, if the exception can not be intercepted. </returns>
/// <returns> False, if the exception was already intercepted or public bool InterceptException()
/// if it can not be intercepted. </returns>
public bool InterceptException(Exception exception)
{ {
if (!(this.CorThread is ICorDebugThread2)) return false; // Is the debuggee .NET 2.0? if (!(this.CorThread is ICorDebugThread2)) return false; // Is the debuggee .NET 2.0?
if (this.CorThread.GetCurrentException() == null) return false; // Is there any exception if (this.CorThread.GetCurrentException() == null) return false; // Is there any exception
@ -166,8 +182,6 @@ namespace Debugger
if (mostRecentUnoptimized == null) return false; if (mostRecentUnoptimized == null) return false;
try { try {
// Interception will expire the CorValue so keep permanent reference
exception.MakeValuePermanent();
((ICorDebugThread2)this.CorThread).InterceptCurrentException(mostRecentUnoptimized.CorILFrame); ((ICorDebugThread2)this.CorThread).InterceptCurrentException(mostRecentUnoptimized.CorILFrame);
} catch (COMException e) { } catch (COMException e) {
// 0x80131C02: Cannot intercept this exception // 0x80131C02: Cannot intercept this exception

14
src/AddIns/Debugger/Debugger.Tests/DebuggerTestsBase.cs

@ -218,16 +218,12 @@ namespace Debugger.Tests
} else { } else {
this.CurrentStackFrame = null; this.CurrentStackFrame = null;
} }
if (e.ExceptionThrown != null) { foreach(Thread exceptionThread in e.ExceptionsThrown) {
StringBuilder msg = new StringBuilder(); Value exception = exceptionThread.CurrentException;
if (CurrentThread.InterceptException(e.ExceptionThrown)) { LogEvent("ExceptionThrown", exception.Type.FullName);
msg.Append(e.ExceptionThrown.ToString()); if (!exceptionThread.InterceptException()) {
} else { LogEvent("CanNotInterceptException", exception.Type.FullName);
// For example, happens on stack overflow
msg.Append("Could not intercept: ");
msg.Append(e.ExceptionThrown.ToString());
} }
LogEvent("ExceptionThrown", msg.ToString());
} }
LogEvent("Paused", CurrentStackFrame != null && CurrentStackFrame.NextStatement != null ? CurrentStackFrame.NextStatement.ToString() : string.Empty); LogEvent("Paused", CurrentStackFrame != null && CurrentStackFrame.NextStatement != null ? CurrentStackFrame.NextStatement.ToString() : string.Empty);
}; };

2
src/AddIns/Debugger/Debugger.Tests/Tests/Exception_Custom.cs

@ -49,7 +49,7 @@ namespace Debugger.Tests {
<Started /> <Started />
<ModuleLoaded>mscorlib.dll (No symbols)</ModuleLoaded> <ModuleLoaded>mscorlib.dll (No symbols)</ModuleLoaded>
<ModuleLoaded>Exception_Custom.exe (Has symbols)</ModuleLoaded> <ModuleLoaded>Exception_Custom.exe (Has symbols)</ModuleLoaded>
<ExceptionThrown>Debugger.Tests.MyException: test2 ---&gt; Debugger.Tests.MyException: test1</ExceptionThrown> <ExceptionThrown>Debugger.Tests.MyException</ExceptionThrown>
<Paused>Exception_Custom.cs:23,5-23,39</Paused> <Paused>Exception_Custom.cs:23,5-23,39</Paused>
<Exited /> <Exited />
</Test> </Test>

Loading…
Cancel
Save