From 32006983786148de6c8d29f22ab279e46836acb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Fri, 22 Dec 2006 19:06:44 +0000 Subject: [PATCH] Added some performance measurements; Improved value cache git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2197 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Src/Pads/TreeListViewDebuggerItem.cs | 5 + .../Project/Debugger.Core.csproj | 4 +- .../Project/Src/Threads/Process.cs | 2 +- .../Project/Src/Util/HighPrecisionTimer.cs | 50 ++++++++ .../Src/{Debugger/Util.cs => Util/Lists.cs} | 8 +- .../Project/Src/Variables/Types/DebugType.cs | 113 ++++++------------ .../Src/Variables/Values/Value-Common.cs | 6 +- .../Project/Src/Variables/Values/Value.cs | 58 +++++++-- 8 files changed, 148 insertions(+), 98 deletions(-) create mode 100644 src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/HighPrecisionTimer.cs rename src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/{Debugger/Util.cs => Util/Lists.cs} (84%) diff --git a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs index 88a243898e..1eabf92ed0 100644 --- a/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs +++ b/src/AddIns/Misc/Debugger/Debugger.AddIn/Project/Src/Pads/TreeListViewDebuggerItem.cs @@ -69,6 +69,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads if (!dirty) return; if (!IsVisible) return; + DateTime startTime = Debugger.Util.HighPrecisionTimer.Now; + if (this.TreeListView != null) { ((DebuggerTreeListView)this.TreeListView).DelayRefresh(); Highlight = (val.AsString != SubItems[1].Text); @@ -90,6 +92,9 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads } 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() diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj index 9f2658f9d4..ac4ee06200 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Debugger.Core.csproj @@ -345,7 +345,6 @@ - @@ -379,6 +378,8 @@ + + @@ -397,6 +398,7 @@ + \ No newline at end of file diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs index 59cd0e4b99..c323e1393d 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Threads/Process.cs +++ b/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); debugger.OnDebuggerTraceMessage(new MessageEventArgs(this, message)); diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/HighPrecisionTimer.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/HighPrecisionTimer.cs new file mode 100644 index 0000000000..e54d05cd84 --- /dev/null +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/HighPrecisionTimer.cs @@ -0,0 +1,50 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Runtime.InteropServices; + +namespace Debugger.Util +{ + /// + /// HighPrecisionTimer can obtain much more accurate time measurement + /// for performace optimization + /// + 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); + } + } + } +} diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Util.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/Lists.cs similarity index 84% rename from src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Util.cs rename to src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/Lists.cs index 959ecbbd86..7e6aae3b7f 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Debugger/Util.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Util/Lists.cs @@ -1,16 +1,16 @@ -// +// // // -// +// // $Revision$ // using System; using System.Collections.Generic; -namespace Debugger +namespace Debugger.Util { - static class Util + public class Lists { public static List MergeLists(T a, IEnumerable b) { diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Types/DebugType.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Types/DebugType.cs index 7ce3b16b0f..cfdbe830ae 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Types/DebugType.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Types/DebugType.cs @@ -27,10 +27,10 @@ namespace Debugger Module module; TypeDefProps classProps; - // Cache - List fields; - List methods; - List properties; + // Members of the type; empty lists if not applicable + List fields = new List(); + List methods = new List(); + List properties = new List(); void AssertClassOrValueType() { @@ -180,9 +180,9 @@ namespace Debugger this.corClass = corType.Class; this.module = process.GetModule(corClass.Module); this.classProps = module.MetaData.GetTypeDefProps(corClass.Token); + + LoadType(); } - - process.TraceMessage("Created type " + this.Name); } /// @@ -214,66 +214,43 @@ namespace Debugger this.IsSubclassOf(objectInstance.Type); } - List GetAllFields() + void LoadType() { - AssertClassOrValueType(); + DateTime startTime = Util.HighPrecisionTimer.Now; - // Build cache - if (fields == null) { - process.TraceMessage("Loading fields for type " + this.Name); - fields = new List(); - 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 GetAllMethods() - { - AssertClassOrValueType(); + // Load fields + foreach(FieldProps field in module.MetaData.EnumFields(this.MetadataToken)) { + if (field.IsStatic && field.IsLiteral) continue; // Skip static literals TODO: Why? + fields.Add(new FieldInfo(this, field)); + }; - // Build cache - if (methods == null) { - process.TraceMessage("Loading methods for type " + this.Name); - methods = new List(); - foreach(MethodProps m in module.MetaData.EnumMethods(this.MetadataToken)) { - methods.Add(new MethodInfo(this, m)); - } + // Load methods + 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 GetAllProperties() - { - AssertClassOrValueType(); - // Build cache - if (properties == null) { - process.TraceMessage("Loading properties for type " + this.Name); - properties = new List(); - // Collect data - Dictionary methods = new Dictionary(); - Dictionary names = new Dictionary(); - foreach(MethodInfo method in GetAllMethods()) { - if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_"))) { - methods.Add(method.Name, method); - names.Add(method.Name.Remove(0,4), null); - } - } - // Pair up getters and setters - foreach(KeyValuePair 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)); + // Load properties + // TODO: Handle indexers ("get_Item") in other code + // Collect data + Dictionary accessors = new Dictionary(); + Dictionary propertyNames = new Dictionary(); + foreach(MethodInfo method in methods) { + if (method.IsSpecialName && (method.Name.StartsWith("get_") || method.Name.StartsWith("set_"))) { + accessors.Add(method.Name, method); + propertyNames[method.Name.Remove(0,4)] = null; } } - return properties; + // Pair up getters and setters + foreach(KeyValuePair 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)"); } /// Return all public fields. @@ -285,11 +262,7 @@ namespace Debugger /// Return all fields satisfing binding flags. public IList GetFields(BindingFlags bindingFlags) { - if (IsClass || IsValueType) { - return FilterMemberInfo(GetAllFields(), bindingFlags); - } else { - return new List(); - } + return FilterMemberInfo(fields, bindingFlags); } /// Return all public methods. @@ -301,11 +274,7 @@ namespace Debugger /// Return all methods satisfing binding flags. public IList GetMethods(BindingFlags bindingFlags) { - if (IsClass || IsValueType) { - return FilterMemberInfo(GetAllMethods(), bindingFlags); - } else { - return new List(); - } + return FilterMemberInfo(methods, bindingFlags); } /// Return all public properties. @@ -317,11 +286,7 @@ namespace Debugger /// Return all properties satisfing binding flags. public IList GetProperties(BindingFlags bindingFlags) { - if (IsClass || IsValueType) { - return FilterMemberInfo(GetAllProperties(), bindingFlags); - } else { - return new List(); - } + return FilterMemberInfo(properties, bindingFlags); } /// Compares two types diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value-Common.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value-Common.cs index 81502b487c..83a8d03ff0 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value-Common.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value-Common.cs @@ -25,11 +25,7 @@ namespace Debugger /// Gets a string representation of the value public string AsString { get { - if (IsNull) return ""; - 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"); + return Cache.AsString; } } } diff --git a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value.cs b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value.cs index 87cdab99a7..e08f419b51 100644 --- a/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value.cs +++ b/src/AddIns/Misc/Debugger/Debugger.Core/Project/Src/Variables/Values/Value.cs @@ -40,9 +40,6 @@ namespace Debugger CorValueGetter corValueGetter; - ICorDebugValue currentCorValue; - PauseSession currentCorValuePauseSession; - /// Occurs when the Value can not be used anymore public event EventHandler Expired; @@ -51,6 +48,47 @@ namespace Debugger 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; + } + + /// + /// Cache stores expensive or commonly used information about the value + /// + ValueCache Cache { + get { + if (this.HasExpired) throw new CannotGetValueException("Value has expired"); + + if (cache == null || (cache.PauseSession != process.PauseSession && !cache.RawCorValue.Is())) { + 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().ExactType); + + // AsString representation + if (IsNull) cache.AsString = ""; + 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; + } + } + /// The process that owns the value public Process Process { get { @@ -68,7 +106,7 @@ namespace Debugger internal ICorDebugValue CorValue { get { - return DereferenceUnbox(RawCorValue); + return Cache.CorValue; } } @@ -84,12 +122,7 @@ namespace Debugger ICorDebugValue RawCorValue { get { - if (this.HasExpired) throw new CannotGetValueException("CorValue has expired"); - if (currentCorValue == null || (currentCorValuePauseSession != process.PauseSession && !currentCorValue.Is())) { - currentCorValue = corValueGetter(); - currentCorValuePauseSession = process.PauseSession; - } - return currentCorValue; + return Cache.RawCorValue; } } @@ -153,9 +186,8 @@ namespace Debugger internal void NotifyChange() { + cache = null; if (!isExpired) { - currentCorValue = null; - currentCorValuePauseSession = null; OnChanged(new ValueEventArgs(this)); } } @@ -180,7 +212,7 @@ namespace Debugger /// Returns the of the value public DebugType Type { get { - return DebugType.Create(process, RawCorValue.As().ExactType); + return Cache.Type; } }