Browse Source

Suspend other threads during stepping

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5175 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 16 years ago
parent
commit
9da4702054
  1. 43
      src/AddIns/Misc/Debugger/Debugger.Core/Eval.cs
  2. 2
      src/AddIns/Misc/Debugger/Debugger.Core/Interop/CorDebug.cs
  3. 2
      src/AddIns/Misc/Debugger/Debugger.Core/Interop/CorDebugExtensionMethods.generated.cs
  4. 21
      src/AddIns/Misc/Debugger/Debugger.Core/ManagedCallback.cs
  5. 1
      src/AddIns/Misc/Debugger/Debugger.Core/Options.cs
  6. 47
      src/AddIns/Misc/Debugger/Debugger.Core/Process.cs
  7. 13
      src/AddIns/Misc/Debugger/Debugger.Core/StackFrame.cs
  8. 17
      src/AddIns/Misc/Debugger/Debugger.Core/Thread.cs
  9. 31
      src/AddIns/Misc/Debugger/Debugger.Tests/Tests/ExpressionEvaluator_Tests.cs

43
src/AddIns/Misc/Debugger/Debugger.Core/Eval.cs

@ -34,6 +34,7 @@ namespace Debugger @@ -34,6 +34,7 @@ namespace Debugger
string description;
ICorDebugEval corEval;
Thread thread;
Value result;
EvalState state;
@ -91,7 +92,7 @@ namespace Debugger @@ -91,7 +92,7 @@ namespace Debugger
this.description = description;
this.state = EvalState.Evaluating;
this.corEval = CreateCorEval(appDomain);
this.corEval = CreateCorEval(appDomain, out this.thread);
try {
evalStarter(this);
@ -116,38 +117,43 @@ namespace Debugger @@ -116,38 +117,43 @@ namespace Debugger
}
appDomain.Process.ActiveEvals.Add(this);
appDomain.Process.AsyncContinue(DebuggeeStateAction.Keep);
if (appDomain.Process.Options.SuspendOtherThreads) {
appDomain.Process.AsyncContinue(DebuggeeStateAction.Keep, new Thread[] { thread });
} else {
appDomain.Process.AsyncContinue(DebuggeeStateAction.Keep, this.Process.UnsuspendedThreads);
}
}
static ICorDebugEval CreateCorEval(AppDomain appDomain)
static ICorDebugEval CreateCorEval(AppDomain appDomain, out Thread thread)
{
appDomain.Process.AssertPaused();
Thread targetThread = appDomain.Process.SelectedThread;
thread = appDomain.Process.SelectedThread;
if (targetThread.CorThread.GetAppDomain().GetID() != appDomain.ID) {
foreach(Thread thread in appDomain.Process.Threads) {
if (thread.CorThread.GetAppDomain().GetID() == appDomain.ID) {
targetThread = thread;
if (thread.CorThread.GetAppDomain().GetID() != appDomain.ID) {
foreach(Thread t in appDomain.Process.Threads) {
if (t.CorThread.GetAppDomain().GetID() == appDomain.ID &&
!t.Suspended &&
!t.IsMostRecentStackFrameNative &&
t.IsAtSafePoint)
{
thread = t;
break;
}
}
}
if (targetThread == null) {
if (thread == null)
throw new GetValueException("Can not evaluate because no thread is selected");
}
if (targetThread.IsMostRecentStackFrameNative) {
if (thread.IsMostRecentStackFrameNative)
throw new GetValueException("Can not evaluate because native frame is on top of stack");
}
if (!targetThread.IsAtSafePoint) {
if (!thread.IsAtSafePoint)
throw new GetValueException("Can not evaluate because thread is not at a safe point");
}
if (targetThread.Suspended) {
if (thread.Suspended)
throw new GetValueException("Can not evaluate on suspended thread");
}
return targetThread.CorThread.CreateEval();
return thread.CorThread.CreateEval();
}
internal bool IsCorEval(ICorDebugEval corEval)
@ -279,7 +285,8 @@ namespace Debugger @@ -279,7 +285,8 @@ namespace Debugger
{
if (value == null) {
ICorDebugClass corClass = appDomain.ObjectType.CorType.GetClass();
ICorDebugEval corEval = CreateCorEval(appDomain);
Thread thread;
ICorDebugEval corEval = CreateCorEval(appDomain, out thread);
ICorDebugValue corValue = corEval.CreateValue((uint)CorElementType.CLASS, corClass);
return new Value(appDomain, corValue);
} else if (value is string) {

2
src/AddIns/Misc/Debugger/Debugger.Core/Interop/CorDebug.cs

@ -1527,7 +1527,7 @@ namespace Debugger.Interop.CorDebug @@ -1527,7 +1527,7 @@ namespace Debugger.Interop.CorDebug
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)]
void __GetCount(out uint pcelt);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)]
void __Next([In] uint celt, [Out] IntPtr threads, out uint pceltFetched);
void __Next([In] uint celt, [Out, MarshalAs(UnmanagedType.LPArray)] ICorDebugThread[] threads, out uint pceltFetched);
}
[ComImport, Guid("D613F0BB-ACE1-4C19-BD72-E4C08D5DA7F5"), InterfaceType((short) 1)]

2
src/AddIns/Misc/Debugger/Debugger.Core/Interop/CorDebugExtensionMethods.generated.cs

@ -3145,7 +3145,7 @@ namespace Debugger.Interop.CorDebug @@ -3145,7 +3145,7 @@ namespace Debugger.Interop.CorDebug
return pcelt;
}
public static uint Next(this ICorDebugThreadEnum instance, uint celt, IntPtr threads)
public static uint Next(this ICorDebugThreadEnum instance, uint celt, ICorDebugThread[] threads)
{
uint pceltFetched;
instance.__Next(celt, threads, out pceltFetched);

21
src/AddIns/Misc/Debugger/Debugger.Core/ManagedCallback.cs

@ -53,7 +53,7 @@ namespace Debugger @@ -53,7 +53,7 @@ namespace Debugger
if (process.IsPaused && process.PauseSession.PausedReason == PausedReason.ForcedBreak) {
process.TraceMessage("Processing post-break callback");
// This compensates for the break call and we are in normal callback handling mode
process.AsyncContinue(DebuggeeStateAction.Keep);
process.AsyncContinue(DebuggeeStateAction.Keep, new Thread[] {});
// Start of call back - create new pause session (as usual)
process.NotifyPaused(pausedReason);
// Make sure we stay pause after the callback is handled
@ -83,19 +83,22 @@ namespace Debugger @@ -83,19 +83,22 @@ namespace Debugger
void ExitCallback()
{
bool hasQueuedCallbacks = process.CorProcess.HasQueuedCallbacks();
if (hasQueuedCallbacks) {
if (hasQueuedCallbacks)
process.TraceMessage("Process has queued callbacks");
}
// Ignore events during property evaluation
if (!pauseOnNextExit || process.Evaluating || hasQueuedCallbacks) {
process.AsyncContinue(DebuggeeStateAction.Keep);
} else {
if (process.Options.Verbose) {
if (hasQueuedCallbacks) {
// Exception has Exception2 queued after it
process.AsyncContinue(DebuggeeStateAction.Keep, null);
} else if (process.Evaluating) {
// Ignore events during property evaluation
process.AsyncContinue(DebuggeeStateAction.Keep, null);
} else if (pauseOnNextExit) {
if (process.Options.Verbose)
process.TraceMessage("Callback exit: Paused");
}
pauseOnNextExit = false;
Pause();
} else {
process.AsyncContinue(DebuggeeStateAction.Keep, null);
}
isInCallback = false;

1
src/AddIns/Misc/Debugger/Debugger.Core/Options.cs

@ -17,5 +17,6 @@ namespace Debugger @@ -17,5 +17,6 @@ namespace Debugger
public bool StepOverFieldAccessProperties = true;
public bool Verbose = false;
public string[] SymbolsSearchPaths = new string[0];
public bool SuspendOtherThreads = false;
}
}

47
src/AddIns/Misc/Debugger/Debugger.Core/Process.cs

@ -10,6 +10,7 @@ using System.Collections.Generic; @@ -10,6 +10,7 @@ using System.Collections.Generic;
using Debugger.Interop.CorDebug;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
using System.Runtime.InteropServices;
namespace Debugger
{
@ -246,8 +247,8 @@ namespace Debugger @@ -246,8 +247,8 @@ namespace Debugger
protected virtual void OnResumed()
{
AssertRunning();
// No real purpose - just additional check
if (callbackInterface.IsInCallback) throw new DebuggerException("Can not raise event within callback.");
if (callbackInterface.IsInCallback)
throw new DebuggerException("Can not raise event within callback.");
TraceMessage ("Debugger event: OnResumed()");
if (Resumed != null) {
Resumed(this, new ProcessEventArgs(this));
@ -399,15 +400,53 @@ namespace Debugger @@ -399,15 +400,53 @@ namespace Debugger
WaitForPause();
}
internal Thread[] UnsuspendedThreads {
get {
List<Thread> threadsToRun = new List<Thread>(this.Threads.Count);
foreach(Thread t in this.Threads) {
if (!t.Suspended)
threadsToRun.Add(t);
}
return threadsToRun.ToArray();
}
}
/// <summary>
/// Resume execution and run all threads not marked by the user as susspended.
/// </summary>
public void AsyncContinue()
{
AsyncContinue(DebuggeeStateAction.Clear);
AsyncContinue(DebuggeeStateAction.Clear, this.UnsuspendedThreads);
}
internal void AsyncContinue(DebuggeeStateAction action)
internal void AsyncContinue(DebuggeeStateAction action, Thread[] threadsToRun)
{
AssertPaused();
if (threadsToRun != null) {
// corProcess.SetAllThreadsDebugState(CorDebugThreadState.THREAD_SUSPEND, null);
// TODO: There is second unreported thread, stopping it prevents the debugee from exiting
// uint count = corProcess.EnumerateThreads().GetCount();
// ICorDebugThread[] ts = new ICorDebugThread[count];
// corProcess.EnumerateThreads().Next((uint)ts.Length, ts);
// uint helper = corProcess.GetHelperThreadID();
try {
foreach(Thread t in this.Threads) {
t.CorThread.SetDebugState(CorDebugThreadState.THREAD_SUSPEND);
}
foreach(Thread t in threadsToRun) {
t.CorThread.SetDebugState(CorDebugThreadState.THREAD_RUN);
}
} catch (COMException e) {
// The state of the thread is invalid. (Exception from HRESULT: 0x8013132D)
// It can happen for threads that have not started yet
if ((uint)e.ErrorCode == 0x8013132D) {
} else {
throw;
}
}
}
NotifyResumed(action);
corProcess.Continue(0);
if (this.Options.Verbose) {

13
src/AddIns/Misc/Debugger/Debugger.Core/StackFrame.cs

@ -185,7 +185,7 @@ namespace Debugger @@ -185,7 +185,7 @@ namespace Debugger
{
Stepper.StepOut(this, "normal");
process.AsyncContinue(DebuggeeStateAction.Clear);
AsyncContinue();
}
void AsyncStep(bool stepIn)
@ -213,7 +213,16 @@ namespace Debugger @@ -213,7 +213,16 @@ namespace Debugger
Stepper.StepOver(this, nextSt.StepRanges, "normal");
}
process.AsyncContinue(DebuggeeStateAction.Clear);
AsyncContinue();
}
void AsyncContinue()
{
if (process.Options.SuspendOtherThreads) {
process.AsyncContinue(DebuggeeStateAction.Clear, new Thread[] { this.Thread });
} else {
process.AsyncContinue(DebuggeeStateAction.Clear, this.Process.UnsuspendedThreads);
}
}
/// <summary>

17
src/AddIns/Misc/Debugger/Debugger.Core/Thread.cs

@ -123,19 +123,7 @@ namespace Debugger @@ -123,19 +123,7 @@ namespace Debugger
}
}
/// <remarks> Returns false if the thread is in invalid state </remarks>
public bool Suspended {
get {
process.AssertPaused();
if (!IsInValidState) return false;
return (CorThread.GetDebugState() == CorDebugThreadState.THREAD_SUSPEND);
}
set {
CorThread.SetDebugState((value==true) ? CorDebugThreadState.THREAD_SUSPEND : CorDebugThreadState.THREAD_RUN);
}
}
public bool Suspended { get; set; }
/// <remarks> Returns Normal if the thread is in invalid state </remarks>
public ThreadPriority Priority {
@ -239,8 +227,7 @@ namespace Debugger @@ -239,8 +227,7 @@ namespace Debugger
// May happen in release code with does not have any symbols
return false;
}
process.AsyncContinue(DebuggeeStateAction.Keep);
process.AsyncContinue(DebuggeeStateAction.Keep, new Thread[] { this /* needed */ });
process.WaitForPause();
return true;
}

31
src/AddIns/Misc/Debugger/Debugger.Tests/Tests/ExpressionEvaluator_Tests.cs

@ -116,6 +116,8 @@ namespace Debugger.Tests @@ -116,6 +116,8 @@ namespace Debugger.Tests
{
new ExpressionEvaluator_Tests().Fun("function argument");
}
static bool WorkerThreadMoved = false;
public unsafe void Fun(string arg)
{
@ -136,7 +138,13 @@ namespace Debugger.Tests @@ -136,7 +138,13 @@ namespace Debugger.Tests
int*[][,] complexType1 = new int*[][,] { new int*[,] { { (int*)0xDA1D } } };
A<int>.B.C<char>[][,] complexType2 = new A<int>.B.C<char>[0][,];
System.Threading.Thread bgWork = new System.Threading.Thread(
delegate() { WorkerThreadMoved = true; }
);
System.Diagnostics.Debugger.Break();
bgWork.Start();
System.Threading.Thread.Sleep(100);
}
}
}
@ -192,6 +200,8 @@ namespace Debugger.Tests { @@ -192,6 +200,8 @@ namespace Debugger.Tests {
public void ExpressionEvaluator_Tests()
{
StartTest();
process.SelectedStackFrame.StepOver();
process.SelectedStackFrame.StepOver(); // Start worker thread
EvalAll(expressionsInput);
@ -248,6 +258,10 @@ namespace Debugger.Tests { @@ -248,6 +258,10 @@ namespace Debugger.Tests {
ObjectDump("TypesIdentitcal", object.ReferenceEquals(locType, valType));
ObjectDump("TypesEqual", locType == valType);
ObjectDump("WorkerThreadMoved", process.SelectedStackFrame.GetThisValue().GetMemberValue("WorkerThreadMoved").AsString);
process.SelectedStackFrame.StepOver();
ObjectDump("WorkerThreadMoved", process.SelectedStackFrame.GetThisValue().GetMemberValue("WorkerThreadMoved").AsString);
EndTest();
}
@ -291,7 +305,9 @@ namespace Debugger.Tests { @@ -291,7 +305,9 @@ namespace Debugger.Tests {
<ProcessStarted />
<ModuleLoaded>mscorlib.dll (No symbols)</ModuleLoaded>
<ModuleLoaded>ExpressionEvaluator_Tests.exe (Has symbols)</ModuleLoaded>
<DebuggingPaused>Break ExpressionEvaluator_Tests.cs:139,4-139,40</DebuggingPaused>
<DebuggingPaused>Break ExpressionEvaluator_Tests.cs:145,4-145,40</DebuggingPaused>
<DebuggingPaused>StepComplete ExpressionEvaluator_Tests.cs:146,4-146,19</DebuggingPaused>
<DebuggingPaused>StepComplete ExpressionEvaluator_Tests.cs:147,4-147,39</DebuggingPaused>
<Eval> </Eval>
<Eval> b = 1 </Eval>
<Eval> i = 4 </Eval>
@ -421,6 +437,19 @@ namespace Debugger.Tests { @@ -421,6 +437,19 @@ namespace Debugger.Tests {
<TypeResulution> typeof(Debugger.Tests.ExpressionEvaluator_Tests.A&lt;System.Int32&gt;.B.C&lt;System.Char&gt;[][,]) = Debugger.Tests.ExpressionEvaluator_Tests+A`1+B+C`1[System.Int32,System.Char][,][] (ok)</TypeResulution>
<TypesIdentitcal>True</TypesIdentitcal>
<TypesEqual>True</TypesEqual>
<WorkerThreadMoved>
<Value
AsString="True"
PrimitiveValue="True"
Type="System.Boolean" />
</WorkerThreadMoved>
<DebuggingPaused>StepComplete ExpressionEvaluator_Tests.cs:148,3-148,4</DebuggingPaused>
<WorkerThreadMoved>
<Value
AsString="True"
PrimitiveValue="True"
Type="System.Boolean" />
</WorkerThreadMoved>
<ProcessExited />
</Test>
</DebuggerTests>

Loading…
Cancel
Save