Browse Source

Added some performance measurements; Improved value cache

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2197 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
David Srbecký 19 years ago
parent
commit
3200698378
  1. 5
      src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs
  2. 4
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj
  3. 2
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs
  4. 50
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/HighPrecisionTimer.cs
  5. 8
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/Lists.cs
  6. 113
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Types/DebugType.cs
  7. 6
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value-Common.cs
  8. 58
      src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value.cs

5
src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs

@ -69,6 +69,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
if (!dirty) return; if (!dirty) return;
if (!IsVisible) return; if (!IsVisible) return;
DateTime startTime = Debugger.Util.HighPrecisionTimer.Now;
if (this.TreeListView != null) { if (this.TreeListView != null) {
((DebuggerTreeListView)this.TreeListView).DelayRefresh(); ((DebuggerTreeListView)this.TreeListView).DelayRefresh();
Highlight = (val.AsString != SubItems[1].Text); Highlight = (val.AsString != SubItems[1].Text);
@ -90,6 +92,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
} }
dirty = false; dirty = false;
TimeSpan totalTime = Debugger.Util.HighPrecisionTimer.Now - startTime;
//val.Process.TraceMessage("Local Variables Pad item updated: " + val.Name + " (" + totalTime.TotalMilliseconds + " ms)");
} }
public void BeforeExpand() public void BeforeExpand()

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

@ -345,7 +345,6 @@
<Compile Include="Src\Wrappers\CorDebug\ICorDebugChain.cs" /> <Compile Include="Src\Wrappers\CorDebug\ICorDebugChain.cs" />
<Compile Include="Src\Wrappers\CorDebug\ICorDebugFrame.cs" /> <Compile Include="Src\Wrappers\CorDebug\ICorDebugFrame.cs" />
<Compile Include="Src\Debugger\IExpirable.cs" /> <Compile Include="Src\Debugger\IExpirable.cs" />
<Compile Include="Src\Debugger\Util.cs" />
<Compile Include="Src\Debugger\IMutable.cs" /> <Compile Include="Src\Debugger\IMutable.cs" />
<Compile Include="Src\Debugger\ExceptionEventArgs.cs" /> <Compile Include="Src\Debugger\ExceptionEventArgs.cs" />
<Compile Include="Src\Debugger\Internal\ManagedCallbackSwitch.cs" /> <Compile Include="Src\Debugger\Internal\ManagedCallbackSwitch.cs" />
@ -379,6 +378,8 @@
<Compile Include="Src\Interop\Enums\CorElementType.cs" /> <Compile Include="Src\Interop\Enums\CorElementType.cs" />
<Compile Include="Src\Interop\Enums\CorMethodAttr.cs" /> <Compile Include="Src\Interop\Enums\CorMethodAttr.cs" />
<Compile Include="Src\Interop\Enums\CorTokenType.cs" /> <Compile Include="Src\Interop\Enums\CorTokenType.cs" />
<Compile Include="Src\Util\HighPrecisionTimer.cs" />
<Compile Include="Src\Util\Lists.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="README.TXT" /> <Content Include="README.TXT" />
@ -397,6 +398,7 @@
<Folder Include="Src\Variables\Variables" /> <Folder Include="Src\Variables\Variables" />
<Content Include="Src\Stepping.txt" /> <Content Include="Src\Stepping.txt" />
<Folder Include="Src\Interop\Enums" /> <Folder Include="Src\Interop\Enums" />
<Folder Include="Src\Util" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSHARP.Targets" />
</Project> </Project>

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

@ -227,7 +227,7 @@ namespace Debugger
} }
} }
internal void TraceMessage(string message) public void TraceMessage(string message)
{ {
System.Diagnostics.Debug.WriteLine("Debugger:" + message); System.Diagnostics.Debug.WriteLine("Debugger:" + message);
debugger.OnDebuggerTraceMessage(new MessageEventArgs(this, message)); debugger.OnDebuggerTraceMessage(new MessageEventArgs(this, message));

50
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/HighPrecisionTimer.cs

@ -0,0 +1,50 @@
// <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;
using System.Runtime.InteropServices;
namespace Debugger.Util
{
/// <summary>
/// HighPrecisionTimer can obtain much more accurate time measurement
/// for performace optimization
/// </summary>
public static class HighPrecisionTimer
{
[DllImport("kernel32.dll")]
static extern int QueryPerformanceFrequency(out long frequency);
[DllImport("kernel32.dll")]
static extern int QueryPerformanceCounter(out long count);
static DateTime startTime;
static long startTicks;
static HighPrecisionTimer()
{
startTime = DateTime.Now;
startTicks = Ticks;
}
static long Ticks {
get {
long frequency;
long count;
QueryPerformanceFrequency(out frequency);
QueryPerformanceCounter(out count);
return (count / frequency) * TimeSpan.TicksPerSecond + (count % frequency) * TimeSpan.TicksPerSecond / frequency;
}
}
public static DateTime Now {
get {
return startTime.AddTicks(Ticks - startTicks);
}
}
}
}

8
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Util.cs → src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/Lists.cs

@ -1,16 +1,16 @@
// <file> // <file>
// <copyright see="prj:///doc/copyright.txt"/> // <copyright see="prj:///doc/copyright.txt"/>
// <license see="prj:///doc/license.txt"/> // <license see="prj:///doc/license.txt"/>
// <owner name="David Srbecký" email="dsrbecky@gmail.com"/> // <owner name="David Srbecký" email="dsrbecky@gmail.com"/>
// <version>$Revision$</version> // <version>$Revision$</version>
// </file> // </file>
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Debugger namespace Debugger.Util
{ {
static class Util public class Lists
{ {
public static List<T> MergeLists<T>(T a, IEnumerable<T> b) public static List<T> MergeLists<T>(T a, IEnumerable<T> b)
{ {

113
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Types/DebugType.cs

@ -27,10 +27,10 @@ namespace Debugger
Module module; Module module;
TypeDefProps classProps; TypeDefProps classProps;
// Cache // Members of the type; empty lists if not applicable
List<FieldInfo> fields; List<FieldInfo> fields = new List<FieldInfo>();
List<MethodInfo> methods; List<MethodInfo> methods = new List<MethodInfo>();
List<PropertyInfo> properties; List<PropertyInfo> properties = new List<PropertyInfo>();
void AssertClassOrValueType() void AssertClassOrValueType()
{ {
@ -180,9 +180,9 @@ namespace Debugger
this.corClass = corType.Class; this.corClass = corType.Class;
this.module = process.GetModule(corClass.Module); this.module = process.GetModule(corClass.Module);
this.classProps = module.MetaData.GetTypeDefProps(corClass.Token); this.classProps = module.MetaData.GetTypeDefProps(corClass.Token);
LoadType();
} }
process.TraceMessage("Created type " + this.Name);
} }
/// <summary> /// <summary>
@ -214,66 +214,43 @@ namespace Debugger
this.IsSubclassOf(objectInstance.Type); this.IsSubclassOf(objectInstance.Type);
} }
List<FieldInfo> GetAllFields() void LoadType()
{ {
AssertClassOrValueType(); DateTime startTime = Util.HighPrecisionTimer.Now;
// Build cache // Load fields
if (fields == null) { foreach(FieldProps field in module.MetaData.EnumFields(this.MetadataToken)) {
process.TraceMessage("Loading fields for type " + this.Name); if (field.IsStatic && field.IsLiteral) continue; // Skip static literals TODO: Why?
fields = new List<FieldInfo>(); fields.Add(new FieldInfo(this, field));
foreach(FieldProps field in module.MetaData.EnumFields(this.MetadataToken)) { };
// TODO: Why?
if (field.IsStatic && field.IsLiteral) continue; // Skip static literals
fields.Add(new FieldInfo(this, field));
};
}
return fields;
}
List<MethodInfo> GetAllMethods()
{
AssertClassOrValueType();
// Build cache // Load methods
if (methods == null) { foreach(MethodProps m in module.MetaData.EnumMethods(this.MetadataToken)) {
process.TraceMessage("Loading methods for type " + this.Name); methods.Add(new MethodInfo(this, m));
methods = new List<MethodInfo>();
foreach(MethodProps m in module.MetaData.EnumMethods(this.MetadataToken)) {
methods.Add(new MethodInfo(this, m));
}
} }
return methods;
}
// TODO: Handle indexers ("get_Item") in other code
List<PropertyInfo> GetAllProperties()
{
AssertClassOrValueType();
// Build cache // Load properties
if (properties == null) { // TODO: Handle indexers ("get_Item") in other code
process.TraceMessage("Loading properties for type " + this.Name); // Collect data
properties = new List<PropertyInfo>(); Dictionary<string, MethodInfo> accessors = new Dictionary<string, MethodInfo>();
// Collect data Dictionary<string, object> propertyNames = new Dictionary<string, object>();
Dictionary<string, MethodInfo> methods = new Dictionary<string, MethodInfo>(); foreach(MethodInfo method in methods) {
Dictionary<string, object> names = new Dictionary<string, object>(); if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_"))) {
foreach(MethodInfo method in GetAllMethods()) { accessors.Add(method.Name, method);
if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_"))) { propertyNames[method.Name.Remove(0,4)] = null;
methods.Add(method.Name, method);
names.Add(method.Name.Remove(0,4), null);
}
}
// Pair up getters and setters
foreach(KeyValuePair<string, object> kvp in names) {
MethodInfo getter = null;
MethodInfo setter = null;
methods.TryGetValue("get_" + kvp.Key, out getter);
methods.TryGetValue("set_" + kvp.Key, out setter);
properties.Add(new PropertyInfo(this, getter, setter));
} }
} }
return properties; // Pair up getters and setters
foreach(KeyValuePair<string, object> kvp in propertyNames) {
MethodInfo getter = null;
MethodInfo setter = null;
accessors.TryGetValue("get_" + kvp.Key, out getter);
accessors.TryGetValue("set_" + kvp.Key, out setter);
properties.Add(new PropertyInfo(this, getter, setter));
}
TimeSpan totalTime = Util.HighPrecisionTimer.Now - startTime;
process.TraceMessage("Loaded type " + this.Name + " (" + totalTime.TotalMilliseconds + " ms)");
} }
/// <summary> Return all public fields.</summary> /// <summary> Return all public fields.</summary>
@ -285,11 +262,7 @@ namespace Debugger
/// <summary> Return all fields satisfing binding flags.</summary> /// <summary> Return all fields satisfing binding flags.</summary>
public IList<FieldInfo> GetFields(BindingFlags bindingFlags) public IList<FieldInfo> GetFields(BindingFlags bindingFlags)
{ {
if (IsClass || IsValueType) { return FilterMemberInfo(fields, bindingFlags);
return FilterMemberInfo(GetAllFields(), bindingFlags);
} else {
return new List<FieldInfo>();
}
} }
/// <summary> Return all public methods.</summary> /// <summary> Return all public methods.</summary>
@ -301,11 +274,7 @@ namespace Debugger
/// <summary> Return all methods satisfing binding flags.</summary> /// <summary> Return all methods satisfing binding flags.</summary>
public IList<MethodInfo> GetMethods(BindingFlags bindingFlags) public IList<MethodInfo> GetMethods(BindingFlags bindingFlags)
{ {
if (IsClass || IsValueType) { return FilterMemberInfo(methods, bindingFlags);
return FilterMemberInfo(GetAllMethods(), bindingFlags);
} else {
return new List<MethodInfo>();
}
} }
/// <summary> Return all public properties.</summary> /// <summary> Return all public properties.</summary>
@ -317,11 +286,7 @@ namespace Debugger
/// <summary> Return all properties satisfing binding flags.</summary> /// <summary> Return all properties satisfing binding flags.</summary>
public IList<PropertyInfo> GetProperties(BindingFlags bindingFlags) public IList<PropertyInfo> GetProperties(BindingFlags bindingFlags)
{ {
if (IsClass || IsValueType) { return FilterMemberInfo(properties, bindingFlags);
return FilterMemberInfo(GetAllProperties(), bindingFlags);
} else {
return new List<PropertyInfo>();
}
} }
/// <summary> Compares two types </summary> /// <summary> Compares two types </summary>

6
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value-Common.cs

@ -25,11 +25,7 @@ namespace Debugger
/// <summary> Gets a string representation of the value </summary> /// <summary> Gets a string representation of the value </summary>
public string AsString { public string AsString {
get { get {
if (IsNull) return "<null reference>"; return Cache.AsString;
if (IsArray) return "{" + this.Type.Name + "}";
if (IsObject) return "{" + this.Type.Name + "}";
if (IsPrimitive) return PrimitiveValue != null ? PrimitiveValue.ToString() : String.Empty;
throw new DebuggerException("Unknown value type");
} }
} }
} }

58
src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value.cs

@ -40,9 +40,6 @@ namespace Debugger
CorValueGetter corValueGetter; CorValueGetter corValueGetter;
ICorDebugValue currentCorValue;
PauseSession currentCorValuePauseSession;
/// <summary> Occurs when the Value can not be used anymore </summary> /// <summary> Occurs when the Value can not be used anymore </summary>
public event EventHandler Expired; public event EventHandler Expired;
@ -51,6 +48,47 @@ namespace Debugger
bool isExpired = false; bool isExpired = false;
ValueCache cache;
private class ValueCache
{
public PauseSession PauseSession;
public ICorDebugValue RawCorValue;
public ICorDebugValue CorValue;
public DebugType Type;
public string AsString = String.Empty;
}
/// <summary>
/// Cache stores expensive or commonly used information about the value
/// </summary>
ValueCache Cache {
get {
if (this.HasExpired) throw new CannotGetValueException("Value has expired");
if (cache == null || (cache.PauseSession != process.PauseSession && !cache.RawCorValue.Is<ICorDebugHandleValue>())) {
DateTime startTime = Util.HighPrecisionTimer.Now;
cache = new ValueCache();
cache.PauseSession = process.PauseSession;
cache.RawCorValue = corValueGetter();
cache.CorValue = DereferenceUnbox(RawCorValue);
cache.Type = DebugType.Create(process, RawCorValue.As<ICorDebugValue2>().ExactType);
// AsString representation
if (IsNull) cache.AsString = "<null reference>";
if (IsArray) cache.AsString = "{" + this.Type.Name + "}";
if (IsObject) cache.AsString = "{" + this.Type.Name + "}";
if (IsPrimitive) cache.AsString = PrimitiveValue != null ? PrimitiveValue.ToString() : String.Empty;
TimeSpan totalTime = Util.HighPrecisionTimer.Now - startTime;
process.TraceMessage("Obtained value: " + cache.AsString + " (" + totalTime.TotalMilliseconds + " ms)");
}
return cache;
}
}
/// <summary> The process that owns the value </summary> /// <summary> The process that owns the value </summary>
public Process Process { public Process Process {
get { get {
@ -68,7 +106,7 @@ namespace Debugger
internal ICorDebugValue CorValue { internal ICorDebugValue CorValue {
get { get {
return DereferenceUnbox(RawCorValue); return Cache.CorValue;
} }
} }
@ -84,12 +122,7 @@ namespace Debugger
ICorDebugValue RawCorValue { ICorDebugValue RawCorValue {
get { get {
if (this.HasExpired) throw new CannotGetValueException("CorValue has expired"); return Cache.RawCorValue;
if (currentCorValue == null || (currentCorValuePauseSession != process.PauseSession && !currentCorValue.Is<ICorDebugHandleValue>())) {
currentCorValue = corValueGetter();
currentCorValuePauseSession = process.PauseSession;
}
return currentCorValue;
} }
} }
@ -153,9 +186,8 @@ namespace Debugger
internal void NotifyChange() internal void NotifyChange()
{ {
cache = null;
if (!isExpired) { if (!isExpired) {
currentCorValue = null;
currentCorValuePauseSession = null;
OnChanged(new ValueEventArgs(this)); OnChanged(new ValueEventArgs(this));
} }
} }
@ -180,7 +212,7 @@ namespace Debugger
/// <summary> Returns the <see cref="Debugger.DebugType"/> of the value </summary> /// <summary> Returns the <see cref="Debugger.DebugType"/> of the value </summary>
public DebugType Type { public DebugType Type {
get { get {
return DebugType.Create(process, RawCorValue.As<ICorDebugValue2>().ExactType); return Cache.Type;
} }
} }

Loading…
Cancel
Save