Browse Source

Removed lifetime tracking of StackFrame. It was quite expensive and it is not necessary for the expression-based variable model.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2805 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 18 years ago
parent
commit
bff4657a88
  1. 1
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/TreeModel/Adapters/TreeViewNode.cs
  2. 1
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj
  3. 5
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Internal/ManagedCallback.cs
  4. 54
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/FrameID.cs
  5. 6
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs
  6. 55
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/StackFrame.cs
  7. 93
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Thread.cs
  8. 17
      src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs
  9. 112
      src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/TestPrograms/FunctionLifetime.xml

1
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/TreeModel/Adapters/TreeViewNode.cs

@ -88,6 +88,7 @@ namespace Debugger.AddIn.TreeModel
if (loadChildsWhenExpanding) { if (loadChildsWhenExpanding) {
loadChildsWhenExpanding = false; loadChildsWhenExpanding = false;
SetContentRecursive(this.Tree, this.Children, this.Content.ChildNodes); SetContentRecursive(this.Tree, this.Children, this.Content.ChildNodes);
this.IsExpandedOnce = true;
} }
} }

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

@ -202,7 +202,6 @@
<Compile Include="Src\Tests\ToStringOnlyAttribute.cs" /> <Compile Include="Src\Tests\ToStringOnlyAttribute.cs" />
<Compile Include="Src\Threads\Exception.cs" /> <Compile Include="Src\Threads\Exception.cs" />
<Compile Include="Src\Threads\ExceptionType.cs" /> <Compile Include="Src\Threads\ExceptionType.cs" />
<Compile Include="Src\Threads\FrameID.cs" />
<Compile Include="Src\Threads\StackFrame.cs" /> <Compile Include="Src\Threads\StackFrame.cs" />
<Compile Include="Src\Threads\NDebugger-Processes.cs" /> <Compile Include="Src\Threads\NDebugger-Processes.cs" />
<Compile Include="Src\Threads\Process-Threads.cs" /> <Compile Include="Src\Threads\Process-Threads.cs" />

5
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Internal/ManagedCallback.cs

@ -58,11 +58,6 @@ namespace Debugger
pauseProcessInsteadOfContinue = false; pauseProcessInsteadOfContinue = false;
} }
// Remove expired threads and functions
foreach(Thread thread in process.Threads) {
thread.CheckExpiration();
}
process.NotifyPaused(new PauseSession(pausedReason)); process.NotifyPaused(new PauseSession(pausedReason));
} else { } else {
throw new DebuggerException("Invalid state at the start of callback"); throw new DebuggerException("Invalid state at the start of callback");

54
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/FrameID.cs

@ -1,54 +0,0 @@
// <file>
// <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version>
// </file>
using System;
namespace Debugger
{
/// <summary>
/// Identifies frame on thread callstack
/// </summary>
struct FrameID {
uint chainIndex;
uint frameIndex;
public uint ChainIndex {
get {
return chainIndex;
}
}
public uint FrameIndex {
get {
return frameIndex;
}
}
public FrameID(uint chainIndex, uint frameIndex)
{
this.chainIndex = chainIndex;
this.frameIndex = frameIndex;
}
public override int GetHashCode()
{
return chainIndex.GetHashCode() ^ frameIndex.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is FrameID)) return false;
FrameID myFrameID = (FrameID)obj;
return this.chainIndex == myFrameID.chainIndex && this.frameIndex == myFrameID.frameIndex;
}
public override string ToString()
{
return string.Format("{0},{1}", this.chainIndex, this.frameIndex);
}
}
}

6
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs

@ -141,12 +141,6 @@ namespace Debugger
corProcess.Stop(5000); // TODO: Hardcoded value corProcess.Stop(5000); // TODO: Hardcoded value
pauseSession = new PauseSession(PausedReason.ForcedBreak); pauseSession = new PauseSession(PausedReason.ForcedBreak);
// TODO: Code duplication from enter callback
// Remove expired threads and functions
foreach(Thread thread in this.Threads) {
thread.CheckExpiration();
}
Pause(true); Pause(true);
} }

55
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/StackFrame.cs

@ -19,21 +19,16 @@ namespace Debugger
/// A stack frame which is being executed on some thread. /// A stack frame which is being executed on some thread.
/// Use to obtain arguments or local variables. /// Use to obtain arguments or local variables.
/// </summary> /// </summary>
public class StackFrame: DebuggerObject, IExpirable public class StackFrame: DebuggerObject
{ {
Process process; Process process;
Thread thread;
MethodInfo methodInfo;
ICorDebugFunction corFunction;
ICorDebugILFrame corILFrame; ICorDebugILFrame corILFrame;
object corILFramePauseSession; object corILFramePauseSession;
ICorDebugFunction corFunction;
Stepper stepOutStepper; MethodInfo methodInfo;
bool steppedOut = false;
Thread thread;
FrameID frameID;
/// <summary> The process in which this stack frame is executed </summary> /// <summary> The process in which this stack frame is executed </summary>
[Debugger.Tests.Ignore] [Debugger.Tests.Ignore]
@ -67,32 +62,17 @@ namespace Debugger
/// <summary> True if stack frame stepped out and is not longer valid. </summary> /// <summary> True if stack frame stepped out and is not longer valid. </summary>
public bool HasExpired { public bool HasExpired {
get { get {
return steppedOut || this.MethodInfo.Module.Unloaded; return this.corILFramePauseSession != process.PauseSession;
} }
} }
/// <summary> Occurs when stack frame expires and is no longer usable </summary> internal StackFrame(Thread thread, ICorDebugILFrame corILFrame)
public event EventHandler Expired;
/// <summary> Is called when stack frame expires and is no longer usable </summary>
internal protected virtual void OnExpired(EventArgs e)
{
if (!steppedOut) {
steppedOut = true;
process.TraceMessage("StackFrame " + this.ToString() + " expired");
if (Expired != null) {
Expired(this, e);
}
}
}
internal StackFrame(Thread thread, FrameID frameID, ICorDebugILFrame corILFrame)
{ {
this.process = thread.Process; this.process = thread.Process;
this.thread = thread; this.thread = thread;
this.frameID = frameID; this.corILFrame = corILFrame;
this.CorILFrame = corILFrame; this.corILFramePauseSession = process.PauseSession;
corFunction = corILFrame.Function; this.corFunction = corILFrame.Function;
DebugType debugType = DebugType.Create( DebugType debugType = DebugType.Create(
this.Process, this.Process,
@ -101,34 +81,19 @@ namespace Debugger
); );
MethodProps methodProps = process.GetModule(corFunction.Module).MetaData.GetMethodProps(corFunction.Token); MethodProps methodProps = process.GetModule(corFunction.Module).MetaData.GetMethodProps(corFunction.Token);
this.methodInfo = new MethodInfo(debugType, methodProps); this.methodInfo = new MethodInfo(debugType, methodProps);
// Force some callback when stack frame steps out so that we can expire it
stepOutStepper = new Stepper(this, "StackFrame Tracker");
stepOutStepper.StepOut();
stepOutStepper.PauseWhenComplete = false;
process.TraceMessage("StackFrame " + this.ToString() + " created");
} }
/// <summary> Returns diagnostic description of the frame </summary> /// <summary> Returns diagnostic description of the frame </summary>
public override string ToString() public override string ToString()
{ {
return this.MethodInfo.Name + "(" + frameID.ToString() + ")"; return this.MethodInfo.FullName;
} }
internal ICorDebugILFrame CorILFrame { internal ICorDebugILFrame CorILFrame {
get { get {
if (HasExpired) throw new DebuggerException("StackFrame has expired"); if (HasExpired) throw new DebuggerException("StackFrame has expired");
if (corILFramePauseSession != process.PauseSession) {
CorILFrame = thread.GetFrameAt(frameID).As<ICorDebugILFrame>();
}
return corILFrame; return corILFrame;
} }
set {
if (value == null) throw new DebuggerException("Can not set frame to null");
corILFrame = value;
corILFramePauseSession = process.PauseSession;
}
} }
internal uint CorInstructionPtr { internal uint CorInstructionPtr {

93
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Thread.cs

@ -14,7 +14,7 @@ using Debugger.Wrappers.CorDebug;
namespace Debugger namespace Debugger
{ {
public partial class Thread: DebuggerObject, IExpirable public partial class Thread: DebuggerObject
{ {
Process process; Process process;
@ -251,7 +251,7 @@ namespace Debugger
if (corFrame.Is<ICorDebugILFrame>()) { if (corFrame.Is<ICorDebugILFrame>()) {
StackFrame stackFrame; StackFrame stackFrame;
try { try {
stackFrame = GetStackFrameFromCache(new FrameID(corChain.Index, corFrame.Index), corFrame.As<ICorDebugILFrame>()); stackFrame = new StackFrame(this, corFrame.CastTo<ICorDebugILFrame>());
} catch (COMException) { // TODO } catch (COMException) { // TODO
continue; continue;
}; };
@ -262,95 +262,6 @@ namespace Debugger
} }
} }
Dictionary<FrameID, StackFrame> functionCache = new Dictionary<FrameID, StackFrame>();
StackFrame GetStackFrameFromCache(FrameID frameID, ICorDebugILFrame corFrame)
{
StackFrame stackFrame;
if (functionCache.TryGetValue(frameID, out stackFrame) && !stackFrame.HasExpired) {
stackFrame.CorILFrame = corFrame;
return stackFrame;
} else {
stackFrame = new StackFrame(this, frameID, corFrame);
functionCache[frameID] = stackFrame;
return stackFrame;
}
}
internal ICorDebugFrame GetFrameAt(FrameID frameID)
{
process.AssertPaused();
ICorDebugChainEnum corChainEnum = CorThread.EnumerateChains();
if (frameID.ChainIndex >= corChainEnum.Count) throw new ArgumentException("Chain index too big", "chainIndex");
corChainEnum.Skip(corChainEnum.Count - frameID.ChainIndex - 1);
ICorDebugChain corChain = corChainEnum.Next();
if (corChain.IsManaged == 0) throw new ArgumentException("Chain is not managed", "chainIndex");
ICorDebugFrameEnum corFrameEnum = corChain.EnumerateFrames();
if (frameID.FrameIndex >= corFrameEnum.Count) throw new ArgumentException("Frame index too big", "frameIndex");
corFrameEnum.Skip(corFrameEnum.Count - frameID.FrameIndex - 1);
return corFrameEnum.Next();
}
// See docs\Stepping.txt
internal void CheckExpiration()
{
try {
ICorDebugChainEnum chainEnum = CorThread.EnumerateChains();
} catch (COMException e) {
// 0x8013132D: The state of the thread is invalid.
// 0x8013134F: Object is in a zombie state
// 0x80131301: Process was terminated.
if ((uint)e.ErrorCode == 0x8013132D ||
(uint)e.ErrorCode == 0x8013134F ||
(uint)e.ErrorCode == 0x80131301) {
this.Expire();
return;
} else throw;
}
if (process.Evaluating) return;
ICorDebugChainEnum corChainEnum = CorThread.EnumerateChains();
int maxChainIndex = (int)corChainEnum.Count - 1;
ICorDebugFrameEnum corFrameEnum = corChainEnum.Next().EnumerateFrames();
// corFrameEnum.Count can return 0 in ExitThread callback
int maxFrameIndex = (int)corFrameEnum.Count - 1;
ICorDebugFrame lastFrame = corFrameEnum.Next();
// Check the token of the current stack frame - stack frame can change if there are multiple handlers for an event
StackFrame stackFrame;
if (lastFrame != null &&
functionCache.TryGetValue(new FrameID((uint)maxChainIndex, (uint)maxFrameIndex), out stackFrame) &&
stackFrame.MethodInfo.MetadataToken != lastFrame.FunctionToken) {
functionCache.Remove(new FrameID((uint)maxChainIndex, (uint)maxFrameIndex));
stackFrame.OnExpired(EventArgs.Empty);
}
// Expire all functions behind the current maximum
// Multiple functions can expire at once (test case: Step out of Button1Click in simple winforms application)
List<KeyValuePair<FrameID, StackFrame>> toBeRemoved = new List<KeyValuePair<FrameID, StackFrame>>();
foreach(KeyValuePair<FrameID, StackFrame> kvp in functionCache) {
if ((kvp.Key.ChainIndex > maxChainIndex) ||
(kvp.Key.ChainIndex == maxChainIndex && kvp.Key.FrameIndex > maxFrameIndex)) {
toBeRemoved.Add(kvp);
}
}
foreach(KeyValuePair<FrameID, StackFrame> kvp in toBeRemoved){
functionCache.Remove(kvp.Key);
kvp.Value.OnExpired(EventArgs.Empty);
}
}
[Debugger.Tests.ToStringOnly] [Debugger.Tests.ToStringOnly]
public StackFrame SelectedStackFrame { public StackFrame SelectedStackFrame {
get { get {

17
src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/DebuggerTests.cs

@ -187,27 +187,26 @@ namespace Debugger.Tests
[Test] [Test]
public void FunctionLifetime() public void FunctionLifetime()
{ {
StackFrame stackFrame;
StartTest("FunctionLifetime"); StartTest("FunctionLifetime");
WaitForPause(); WaitForPause();
stackFrame = process.SelectedStackFrame; StackFrame stackFrame = process.SelectedStackFrame;
ObjectDump("StackFrame", stackFrame); ObjectDump("SelectedStackFrame", process.SelectedStackFrame);
process.Continue(); // Go to the SubFunction process.Continue(); // Go to the SubFunction
WaitForPause(); WaitForPause();
ObjectDump("StackFrame", stackFrame); ObjectDump("Old StackFrame", stackFrame);
ObjectDump("SubStackFrame", process.SelectedStackFrame); ObjectDump("SelectedStackFrame", process.SelectedStackFrame);
process.Continue(); // Go back to Function process.Continue(); // Go back to Function
WaitForPause(); WaitForPause();
Assert.AreEqual(stackFrame, process.SelectedStackFrame); ObjectDump("Old StackFrame", stackFrame);
ObjectDump("StackFrame", stackFrame); ObjectDump("SelectedStackFrame", process.SelectedStackFrame);
process.Continue(); // Setp out of function process.Continue(); // Setp out of function
WaitForPause(); WaitForPause();
ObjectDump("Main", process.SelectedStackFrame); ObjectDump("Main", process.SelectedStackFrame);
ObjectDump("StackFrame", stackFrame); ObjectDump("Old StackFrame", stackFrame);
ObjectDump("SelectedStackFrame", process.SelectedStackFrame);
process.Continue(); process.Continue();
process.WaitForExit(); process.WaitForExit();

112
src/AddIns/Misc/Debugger/Debugger.Tests/Project/Src/TestPrograms/FunctionLifetime.xml

@ -5,7 +5,7 @@
<ModuleLoaded symbols="False">mscorlib.dll</ModuleLoaded> <ModuleLoaded symbols="False">mscorlib.dll</ModuleLoaded>
<ModuleLoaded symbols="True">FunctionLifetime.exe</ModuleLoaded> <ModuleLoaded symbols="True">FunctionLifetime.exe</ModuleLoaded>
<DebuggingPaused>Break</DebuggingPaused> <DebuggingPaused>Break</DebuggingPaused>
<ObjectDump name="StackFrame"> <ObjectDump name="SelectedStackFrame">
<StackFrame> <StackFrame>
<MethodInfo> <MethodInfo>
<MethodInfo> <MethodInfo>
@ -61,7 +61,7 @@
</StackFrame> </StackFrame>
</ObjectDump> </ObjectDump>
<DebuggingPaused>Break</DebuggingPaused> <DebuggingPaused>Break</DebuggingPaused>
<ObjectDump name="StackFrame"> <ObjectDump name="Old StackFrame">
<StackFrame> <StackFrame>
<MethodInfo> <MethodInfo>
<MethodInfo> <MethodInfo>
@ -76,8 +76,8 @@
</MethodInfo> </MethodInfo>
</MethodInfo> </MethodInfo>
<HasSymbols>True</HasSymbols> <HasSymbols>True</HasSymbols>
<HasExpired>False</HasExpired> <HasExpired>True</HasExpired>
<NextStatement>Start=23,4 End=23,18</NextStatement> <NextStatement exception="StackFrame has expired" />
<ThisValue exception="Static method does not have 'this'." /> <ThisValue exception="Static method does not have 'this'." />
<ContaingClassVariables> <ContaingClassVariables>
<ValueCollection> <ValueCollection>
@ -85,29 +85,8 @@
<Items /> <Items />
</ValueCollection> </ValueCollection>
</ContaingClassVariables> </ContaingClassVariables>
<ArgumentCount>1</ArgumentCount> <ArgumentCount exception="StackFrame has expired" />
<Arguments> <Arguments exception="StackFrame has expired" />
<ValueCollection>
<Count>1</Count>
<Items>
<Value>
<IsArray>False</IsArray>
<ArrayLenght exception="Value is not an array" />
<ArrayRank exception="Value is not an array" />
<ArrayDimensions exception="Value is not an array" />
<IsObject>False</IsObject>
<IsPrimitive>True</IsPrimitive>
<IsInteger>True</IsInteger>
<PrimitiveValue>1</PrimitiveValue>
<Name>i</Name>
<IsNull>False</IsNull>
<AsString>1</AsString>
<HasExpired>False</HasExpired>
<Type>System.Int32</Type>
</Value>
</Items>
</ValueCollection>
</Arguments>
<LocalVariables> <LocalVariables>
<ValueCollection> <ValueCollection>
<Count>0</Count> <Count>0</Count>
@ -116,7 +95,7 @@
</LocalVariables> </LocalVariables>
</StackFrame> </StackFrame>
</ObjectDump> </ObjectDump>
<ObjectDump name="SubStackFrame"> <ObjectDump name="SelectedStackFrame">
<StackFrame> <StackFrame>
<MethodInfo> <MethodInfo>
<MethodInfo> <MethodInfo>
@ -156,7 +135,41 @@
</StackFrame> </StackFrame>
</ObjectDump> </ObjectDump>
<DebuggingPaused>Break</DebuggingPaused> <DebuggingPaused>Break</DebuggingPaused>
<ObjectDump name="StackFrame"> <ObjectDump name="Old StackFrame">
<StackFrame>
<MethodInfo>
<MethodInfo>
<Name>Function</Name>
<FullName>Debugger.Tests.TestPrograms.FunctionLifetime.Function</FullName>
<IsPrivate>True</IsPrivate>
<IsPublic>False</IsPublic>
<IsSpecialName>False</IsSpecialName>
<IsStatic>True</IsStatic>
<Module>FunctionLifetime.exe</Module>
<DeclaringType>Debugger.Tests.TestPrograms.FunctionLifetime</DeclaringType>
</MethodInfo>
</MethodInfo>
<HasSymbols>True</HasSymbols>
<HasExpired>True</HasExpired>
<NextStatement exception="StackFrame has expired" />
<ThisValue exception="Static method does not have 'this'." />
<ContaingClassVariables>
<ValueCollection>
<Count>0</Count>
<Items />
</ValueCollection>
</ContaingClassVariables>
<ArgumentCount exception="StackFrame has expired" />
<Arguments exception="StackFrame has expired" />
<LocalVariables>
<ValueCollection>
<Count>0</Count>
<Items />
</ValueCollection>
</LocalVariables>
</StackFrame>
</ObjectDump>
<ObjectDump name="SelectedStackFrame">
<StackFrame> <StackFrame>
<MethodInfo> <MethodInfo>
<MethodInfo> <MethodInfo>
@ -251,7 +264,7 @@
</LocalVariables> </LocalVariables>
</StackFrame> </StackFrame>
</ObjectDump> </ObjectDump>
<ObjectDump name="StackFrame"> <ObjectDump name="Old StackFrame">
<StackFrame> <StackFrame>
<MethodInfo> <MethodInfo>
<MethodInfo> <MethodInfo>
@ -285,6 +298,45 @@
</LocalVariables> </LocalVariables>
</StackFrame> </StackFrame>
</ObjectDump> </ObjectDump>
<ObjectDump name="SelectedStackFrame">
<StackFrame>
<MethodInfo>
<MethodInfo>
<Name>Main</Name>
<FullName>Debugger.Tests.TestPrograms.FunctionLifetime.Main</FullName>
<IsPrivate>False</IsPrivate>
<IsPublic>True</IsPublic>
<IsSpecialName>False</IsSpecialName>
<IsStatic>True</IsStatic>
<Module>FunctionLifetime.exe</Module>
<DeclaringType>Debugger.Tests.TestPrograms.FunctionLifetime</DeclaringType>
</MethodInfo>
</MethodInfo>
<HasSymbols>True</HasSymbols>
<HasExpired>False</HasExpired>
<NextStatement>Start=17,4 End=17,40</NextStatement>
<ThisValue exception="Static method does not have 'this'." />
<ContaingClassVariables>
<ValueCollection>
<Count>0</Count>
<Items />
</ValueCollection>
</ContaingClassVariables>
<ArgumentCount>0</ArgumentCount>
<Arguments>
<ValueCollection>
<Count>0</Count>
<Items />
</ValueCollection>
</Arguments>
<LocalVariables>
<ValueCollection>
<Count>0</Count>
<Items />
</ValueCollection>
</LocalVariables>
</StackFrame>
</ObjectDump>
<ProcessExited /> <ProcessExited />
</Test> </Test>
</DebuggerTests> </DebuggerTests>
Loading…
Cancel
Save