From 59ad301b0338cb52b1a879fe1924ec740f3f26b3 Mon Sep 17 00:00:00 2001 From: Patrick Simpson Date: Wed, 5 Jul 2017 12:12:43 +0200 Subject: [PATCH] [KOE-130] Added wrapper tracing feature --- .../AcaciaZPushPlugin.csproj | 3 + .../DebugSupport/DebugDialog.Designer.cs | 153 ++++- .../Features/DebugSupport/DebugDialog.cs | 97 +++- .../Features/DebugSupport/DebugDialog.resx | 522 +++++++++++++++--- .../Features/DebugSupport/DebugInfo.cs | 4 +- .../AcaciaZPushPlugin/GlobalOptions.cs | 9 + .../Utils/DisposableTracer.cs | 15 + .../Utils/DisposableTracerDummy.cs | 23 + .../Utils/DisposableTracerFull.cs | 182 ++++++ .../Utils/DisposableWrapper.cs | 36 +- 10 files changed, 947 insertions(+), 97 deletions(-) create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracer.cs create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracerDummy.cs create mode 100644 src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracerFull.cs diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index e9e4b33..8f1e121 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -388,6 +388,9 @@ + + + diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.Designer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.Designer.cs index 280828f..6cc050a 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.Designer.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.Designer.cs @@ -28,23 +28,58 @@ /// private void InitializeComponent() { + System.Windows.Forms.ColumnHeader columnMethod; System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DebugDialog)); + System.Windows.Forms.ColumnHeader columnFile; + System.Windows.Forms.ColumnHeader columnLine; this.tableMain = new System.Windows.Forms.TableLayoutPanel(); this.flowButtons = new System.Windows.Forms.FlowLayoutPanel(); this.buttonGC = new System.Windows.Forms.Button(); this.buttonRefresh = new System.Windows.Forms.Button(); this.buttonClose = new System.Windows.Forms.Button(); this.buttonLog = new System.Windows.Forms.Button(); + this._tabs = new System.Windows.Forms.TabControl(); + this._tabProperties = new System.Windows.Forms.TabPage(); this.Properties = new System.Windows.Forms.PropertyGrid(); + this._tabWrapperTypes = new System.Windows.Forms.TabPage(); + this.listWrapperTypes = new System.Windows.Forms.ListView(); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this._tabWrapperLocations = new System.Windows.Forms.TabPage(); + this._layoutLocations = new System.Windows.Forms.TableLayoutPanel(); + this.listStackTrace = new System.Windows.Forms.ListView(); + this.listWrapperLocations = new System.Windows.Forms.ListView(); + this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.columnHeader4 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + columnMethod = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + columnFile = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + columnLine = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.tableMain.SuspendLayout(); this.flowButtons.SuspendLayout(); + this._tabs.SuspendLayout(); + this._tabProperties.SuspendLayout(); + this._tabWrapperTypes.SuspendLayout(); + this._tabWrapperLocations.SuspendLayout(); + this._layoutLocations.SuspendLayout(); this.SuspendLayout(); // + // columnMethod + // + resources.ApplyResources(columnMethod, "columnMethod"); + // + // columnFile + // + resources.ApplyResources(columnFile, "columnFile"); + // + // columnLine + // + resources.ApplyResources(columnLine, "columnLine"); + // // tableMain // resources.ApplyResources(this.tableMain, "tableMain"); this.tableMain.Controls.Add(this.flowButtons, 0, 1); - this.tableMain.Controls.Add(this.Properties, 0, 0); + this.tableMain.Controls.Add(this._tabs, 0, 0); this.tableMain.Name = "tableMain"; // // flowButtons @@ -85,14 +120,109 @@ this.buttonLog.UseVisualStyleBackColor = true; this.buttonLog.Click += new System.EventHandler(this.buttonLog_Click); // + // _tabs + // + this._tabs.Controls.Add(this._tabProperties); + this._tabs.Controls.Add(this._tabWrapperTypes); + this._tabs.Controls.Add(this._tabWrapperLocations); + resources.ApplyResources(this._tabs, "_tabs"); + this._tabs.Name = "_tabs"; + this._tabs.SelectedIndex = 0; + // + // _tabProperties + // + this._tabProperties.Controls.Add(this.Properties); + resources.ApplyResources(this._tabProperties, "_tabProperties"); + this._tabProperties.Name = "_tabProperties"; + this._tabProperties.UseVisualStyleBackColor = true; + // // Properties // - resources.ApplyResources(this.Properties, "Properties"); this.Properties.DisabledItemForeColor = System.Drawing.SystemColors.ControlText; + resources.ApplyResources(this.Properties, "Properties"); this.Properties.Name = "Properties"; this.Properties.PropertySort = System.Windows.Forms.PropertySort.Categorized; this.Properties.ToolbarVisible = false; // + // _tabWrapperTypes + // + this._tabWrapperTypes.Controls.Add(this.listWrapperTypes); + resources.ApplyResources(this._tabWrapperTypes, "_tabWrapperTypes"); + this._tabWrapperTypes.Name = "_tabWrapperTypes"; + this._tabWrapperTypes.UseVisualStyleBackColor = true; + // + // listWrapperTypes + // + this.listWrapperTypes.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1, + this.columnHeader2}); + resources.ApplyResources(this.listWrapperTypes, "listWrapperTypes"); + this.listWrapperTypes.Name = "listWrapperTypes"; + this.listWrapperTypes.ShowItemToolTips = true; + this.listWrapperTypes.Sorting = System.Windows.Forms.SortOrder.Descending; + this.listWrapperTypes.UseCompatibleStateImageBehavior = false; + this.listWrapperTypes.View = System.Windows.Forms.View.Details; + // + // columnHeader1 + // + resources.ApplyResources(this.columnHeader1, "columnHeader1"); + // + // columnHeader2 + // + resources.ApplyResources(this.columnHeader2, "columnHeader2"); + // + // _tabWrapperLocations + // + this._tabWrapperLocations.Controls.Add(this._layoutLocations); + resources.ApplyResources(this._tabWrapperLocations, "_tabWrapperLocations"); + this._tabWrapperLocations.Name = "_tabWrapperLocations"; + this._tabWrapperLocations.UseVisualStyleBackColor = true; + // + // _layoutLocations + // + resources.ApplyResources(this._layoutLocations, "_layoutLocations"); + this._layoutLocations.Controls.Add(this.listStackTrace, 0, 1); + this._layoutLocations.Controls.Add(this.listWrapperLocations, 0, 0); + this._layoutLocations.Name = "_layoutLocations"; + // + // listStackTrace + // + this.listStackTrace.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + columnMethod, + columnLine, + columnFile}); + resources.ApplyResources(this.listStackTrace, "listStackTrace"); + this.listStackTrace.FullRowSelect = true; + this.listStackTrace.MultiSelect = false; + this.listStackTrace.Name = "listStackTrace"; + this.listStackTrace.ShowItemToolTips = true; + this.listStackTrace.UseCompatibleStateImageBehavior = false; + this.listStackTrace.View = System.Windows.Forms.View.Details; + // + // listWrapperLocations + // + this.listWrapperLocations.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader3, + this.columnHeader4}); + resources.ApplyResources(this.listWrapperLocations, "listWrapperLocations"); + this.listWrapperLocations.FullRowSelect = true; + this.listWrapperLocations.HideSelection = false; + this.listWrapperLocations.MultiSelect = false; + this.listWrapperLocations.Name = "listWrapperLocations"; + this.listWrapperLocations.ShowItemToolTips = true; + this.listWrapperLocations.Sorting = System.Windows.Forms.SortOrder.Descending; + this.listWrapperLocations.UseCompatibleStateImageBehavior = false; + this.listWrapperLocations.View = System.Windows.Forms.View.Details; + this.listWrapperLocations.SelectedIndexChanged += new System.EventHandler(this.listWrapperLocations_SelectedIndexChanged); + // + // columnHeader3 + // + resources.ApplyResources(this.columnHeader3, "columnHeader3"); + // + // columnHeader4 + // + resources.ApplyResources(this.columnHeader4, "columnHeader4"); + // // DebugDialog // resources.ApplyResources(this, "$this"); @@ -101,13 +231,16 @@ this.Controls.Add(this.tableMain); this.MinimizeBox = false; this.Name = "DebugDialog"; - this.ShowInTaskbar = false; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; - this.TopMost = true; this.tableMain.ResumeLayout(false); this.tableMain.PerformLayout(); this.flowButtons.ResumeLayout(false); this.flowButtons.PerformLayout(); + this._tabs.ResumeLayout(false); + this._tabProperties.ResumeLayout(false); + this._tabWrapperTypes.ResumeLayout(false); + this._tabWrapperLocations.ResumeLayout(false); + this._layoutLocations.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -122,5 +255,17 @@ private System.Windows.Forms.Button buttonRefresh; private System.Windows.Forms.Button buttonClose; private System.Windows.Forms.Button buttonLog; + private System.Windows.Forms.TabControl _tabs; + private System.Windows.Forms.TabPage _tabProperties; + private System.Windows.Forms.TabPage _tabWrapperTypes; + private System.Windows.Forms.TabPage _tabWrapperLocations; + private System.Windows.Forms.ListView listWrapperTypes; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.ColumnHeader columnHeader2; + private System.Windows.Forms.TableLayoutPanel _layoutLocations; + private System.Windows.Forms.ListView listStackTrace; + private System.Windows.Forms.ListView listWrapperLocations; + private System.Windows.Forms.ColumnHeader columnHeader3; + private System.Windows.Forms.ColumnHeader columnHeader4; } } \ No newline at end of file diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.cs index 398ac6a..d6b6aaa 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.cs @@ -1,4 +1,4 @@ -/// Copyright 2016 Kopano b.v. +/// Copyright 2017 Kopano b.v. /// /// This program is free software: you can redistribute it and/or modify /// it under the terms of the GNU Affero General Public License, version 3, @@ -35,18 +35,105 @@ namespace Acacia.Features.DebugSupport { public partial class DebugDialog : KopanoDialog { + private readonly DisposableTracerFull _tracer; public DebugDialog() { InitializeComponent(); Properties.SelectedObject = new DebugInfo(); + + _tracer = DisposableWrapper.GetTracer(); + if (_tracer == null) + { + // If we don't have a wrapper tracer, hide the tabs + _tabs.SizeMode = TabSizeMode.Fixed; + _tabs.ItemSize = new Size(0, 1); + } + else + { + listWrapperTypes.ListViewItemSorter = new WrapperCountSorter(); + listWrapperLocations.ListViewItemSorter = new WrapperCountSorter(); + RefreshWrappers(); + + // Make it a bit bigger + Width = Width + 400; + Height = Height + 200; + } } private void UpdateFields() { Properties.Refresh(); + RefreshWrappers(); } + #region Wrappers + + private void RefreshWrappers() + { + // Wrapper types + listWrapperTypes.Items.Clear(); + foreach(KeyValuePair type in _tracer.GetTypes()) + { + string name = type.Key.Name; + if (type.Key.DeclaringType != null) + name = type.Key.DeclaringType.Name + "." + name; + + ListViewItem item = new ListViewItem(name); + item.ToolTipText = type.Key.FullName; + item.SubItems.Add(type.Value.ToString()); + listWrapperTypes.Items.Add(item); + } + + listWrapperTypes.Columns[0].Width = -2; + listWrapperTypes.Columns[1].Width = -2; + + // Wrapper locations + listWrapperLocations.Items.Clear(); + foreach (KeyValuePair entry in _tracer.GetLocations()) + { + ListViewItem item = new ListViewItem(entry.Key.DisplayName); + item.SubItems.Add(entry.Value.ToString()); + item.Tag = entry.Key; + listWrapperLocations.Items.Add(item); + } + + listWrapperLocations.Columns[0].Width = -2; + listWrapperLocations.Columns[1].Width = -2; + } + + private class WrapperCountSorter : IComparer + { + public int Compare(object x, object y) + { + int ix = int.Parse(((ListViewItem)x).SubItems[1].Text); + int iy = int.Parse(((ListViewItem)y).SubItems[1].Text); + return iy - ix; + } + } + + private void listWrapperLocations_SelectedIndexChanged(object sender, EventArgs e) + { + listStackTrace.Items.Clear(); + if (listWrapperLocations.SelectedItems.Count > 0) + { + DisposableTracerFull.CustomTrace trace = (DisposableTracerFull.CustomTrace)listWrapperLocations.SelectedItems[0].Tag; + foreach(DisposableTracerFull.CustomFrame frame in trace.Frames) + { + ListViewItem item = new ListViewItem(frame.MethodName); + item.SubItems.Add(frame.LineNumber.ToString()); + item.SubItems.Add(frame.FileName ?? ""); + listStackTrace.Items.Add(item); + } + } + foreach (ColumnHeader header in listStackTrace.Columns) + header.Width = -2; + } + + #endregion + + #region Cycling + private class DebugCycleInfo { private int cycleIndex = 0; @@ -108,6 +195,12 @@ namespace Acacia.Features.DebugSupport private DebugCycleInfo cycle; + /// + /// Runs the specific number of cycles. In each cycle the GAB is resynced. This is to test + /// memory errors, which show most frequently when using the GAB, as that touches most of + /// the code. + /// + /// The number of cycles to run internal void DebugCycle(int count) { GAB.FeatureGAB gab = ThisAddIn.Instance.GetFeature(); @@ -118,6 +211,8 @@ namespace Acacia.Features.DebugSupport } } + #endregion + #region Logging private const string INDENT = "+"; diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.resx b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.resx index 547d1a0..e04e14b 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.resx +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugDialog.resx @@ -117,11 +117,35 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + False + + + Method + + + + 44 + + + False + + + File + + + 44 + + + False + + + Line + Top, Bottom, Left, Right - True @@ -137,18 +161,333 @@ True + + buttonGC + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + flowButtons + + + 0 + + + buttonRefresh + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + flowButtons + + + 1 + + + buttonClose + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + flowButtons + + + 2 + + + buttonLog + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + flowButtons + + + 3 + + + RightToLeft + + + + 4, 703 + + + 4, 3, 4, 3 + + + 615, 64 + + + 1 + + + flowButtons + + + System.Windows.Forms.FlowLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableMain + + + 0 + + + Properties + + + System.Windows.Forms.PropertyGrid, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _tabProperties + + + 0 + + + 4, 33 + + + 3, 3, 3, 3 + + + 609, 657 + + + 0 + + + General + + + _tabProperties + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _tabs + + + 0 + + + listWrapperTypes + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _tabWrapperTypes + + + 0 + + + 4, 33 + + + 3, 3, 3, 3 + + + 609, 657 + + + 1 + + + Wrapper types + + + _tabWrapperTypes + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _tabs + + + 1 + + + 1 + + + Fill + + + 3, 328 + + + 597, 320 + + + 3 + + + listStackTrace + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _layoutLocations + + + 0 + + + Fill + + + 3, 3 + + + 597, 319 + + + 2 + + + listWrapperLocations + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _layoutLocations + + + 1 + + + Fill + + + 3, 3 + + + 2 + + + 603, 651 + + + 2 + + + _layoutLocations + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _tabWrapperLocations + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="listStackTrace" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="listWrapperLocations" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100,Absolute,20" /><Rows Styles="Percent,50,Percent,50" /></TableLayoutSettings> + + + 4, 33 + + + 3, 3, 3, 3 + + + 609, 657 + + + 2 + + + Wrapper locations + + + _tabWrapperLocations + + + System.Windows.Forms.TabPage, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _tabs + + + 2 + + + Fill + + + 3, 3 + + + 617, 694 + + + 3 + + + _tabs + + + System.Windows.Forms.TabControl, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + tableMain + + + 1 + + + 0, 0 + + + 4, 3, 4, 3 + + + 2 + + + 623, 770 + + + 0 + + + tableMain + + + System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="flowButtons" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="_tabs" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100" /><Rows Styles="Percent,100,AutoSize,0,Absolute,20" /></TableLayoutSettings> + True - - 352, 4 + 483, 6 - 4, 4, 4, 4 + 6, 6, 6, 6 - 91, 33 + 126, 52 0 @@ -172,13 +511,13 @@ True - 253, 4 + 346, 6 - 4, 4, 4, 4 + 6, 6, 6, 6 - 91, 33 + 125, 52 1 @@ -202,13 +541,13 @@ True - 145, 4 + 196, 6 - 4, 4, 4, 4 + 6, 6, 6, 6 - 100, 33 + 138, 52 2 @@ -232,13 +571,13 @@ True - 62, 4 + 81, 6 - 4, 4, 4, 4 + 6, 6, 6, 6 - 75, 33 + 103, 52 3 @@ -258,47 +597,20 @@ 3 - - RightToLeft - - - 3, 470 - - - 3, 2, 3, 2 - - - 447, 41 - - - 1 - - - flowButtons - - - System.Windows.Forms.FlowLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - tableMain - - - 0 - - - Top, Bottom, Left, Right + + Fill False - 3, 2 + 3, 3 - 3, 2, 3, 2 + 4, 3, 4, 3 - 447, 464 + 603, 651 2 @@ -310,55 +622,73 @@ System.Windows.Forms.PropertyGrid, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - tableMain + _tabProperties - 1 - - - 0, 0 - - - 3, 2, 3, 2 - - - 2 - - - 453, 513 - - 0 - - tableMain + + Fill - - System.Windows.Forms.TableLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 3, 3 - - $this + + 603, 651 - + 0 - - <?xml version="1.0" encoding="utf-16"?><TableLayoutSettings><Controls><Control Name="flowButtons" Row="1" RowSpan="1" Column="0" ColumnSpan="1" /><Control Name="Properties" Row="0" RowSpan="1" Column="0" ColumnSpan="1" /></Controls><Columns Styles="Percent,100" /><Rows Styles="Percent,100,AutoSize,0" /></TableLayoutSettings> + + listWrapperTypes + + + System.Windows.Forms.ListView, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + _tabWrapperTypes + + + 0 + + + Type + + + 44 + + + Count + + + 44 + + + Type + + + 44 + + + Count + + + 44 True - 8, 16 + 11, 24 True - 453, 511 + 623, 766 - 3, 2, 3, 2 + 4, 3, 4, 3 CenterParent @@ -366,10 +696,52 @@ Debug + + columnMethod + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnFile + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnLine + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader1 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader2 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader3 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + columnHeader4 + + + System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + DebugDialog - System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Acacia.UI.KopanoDialog, Kopano, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null \ No newline at end of file diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugInfo.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugInfo.cs index 695331c..68a2535 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugInfo.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/DebugSupport/DebugInfo.cs @@ -273,7 +273,7 @@ namespace Acacia.Features.DebugSupport #endregion -#region Helpers + #region Helpers private string TimeToString(Stopwatch time) { @@ -297,6 +297,6 @@ namespace Acacia.Features.DebugSupport return string.Format("{0:n1} {1}", dValue, SizeSuffixes[i]); } -#endregion + #endregion } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/GlobalOptions.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/GlobalOptions.cs index 7d7e9d2..3c281ea 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/GlobalOptions.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/GlobalOptions.cs @@ -107,6 +107,15 @@ namespace Acacia } private static readonly BoolOption COM_RELEASE = new BoolOption("COMRelease", true); + [AcaciaOption("Enables tracing of wrapper allocation. Should only be enabled for debugging, as it's very " + + "resource intensive.")] + public bool WrapperTrace + { + get { return GetOption(null, WRAPPER_TRACE); } + set { SetOption(null, WRAPPER_TRACE, value); } + } + private static readonly BoolOption WRAPPER_TRACE = new BoolOption("WrapperTrace", false); + [AcaciaOption("Enables or disables logging completely.")] public bool Logging { diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracer.cs new file mode 100644 index 0000000..d1fc373 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracer.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Acacia.Utils +{ + public interface DisposableTracer + { + void Created(DisposableWrapper wrapper); + void Deleted(DisposableWrapper wrapper, bool wasDisposed); + void Disposed(DisposableWrapper wrapper); + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracerDummy.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracerDummy.cs new file mode 100644 index 0000000..415ccf8 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracerDummy.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Acacia.Utils +{ + public class DisposableTracerDummy : DisposableTracer + { + public void Created(DisposableWrapper wrapper) + { + } + + public void Deleted(DisposableWrapper wrapper, bool wasDisposed) + { + } + + public void Disposed(DisposableWrapper wrapper) + { + } + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracerFull.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracerFull.cs new file mode 100644 index 0000000..37f5c37 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableTracerFull.cs @@ -0,0 +1,182 @@ +using Acacia.Stubs.OutlookWrappers; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Reflection; + +namespace Acacia.Utils +{ + public class DisposableTracerFull : DisposableTracer + { + public class CustomFrame + { + private readonly StackFrame _frame; + + public CustomFrame(StackFrame stackFrame) + { + this._frame = stackFrame; + } + + public string MethodName + { + get + { + // Make the qualified method name + string name = _frame.GetMethod().Name; + Type t = _frame.GetMethod().DeclaringType; + while (t != null) + { + name = t.Name + "." + name; + t = t.DeclaringType; + } + return name; + } + } + + public string FileName + { + get + { + return _frame.GetFileName(); + } + } + + public int LineNumber + { + get + { + return _frame.GetFileLineNumber(); + } + } + + public override string ToString() + { + + // Add any file information + string location = ""; + if (_frame.GetFileName() != null) + { + location += " @ " + _frame.GetFileName(); + location += ":" + _frame.GetFileLineNumber(); + } + + return MethodName + location + "\n"; + } + } + + public class CustomTrace : IComparable + { + private static bool fullTrace = false; + private readonly StackTrace _stackTrace; + private readonly CustomFrame[] _frames; + private int _nameIndex; + + public CustomTrace(StackTrace stackTrace) + { + this._stackTrace = stackTrace; + + // Find a useful name to display + StackFrame[] frames = stackTrace.GetFrames(); + for (_nameIndex = 0; _nameIndex < frames.Length; ++_nameIndex) + { + StackFrame frame = frames[_nameIndex]; + if (!IsCreationMethod(frame.GetMethod())) + break; + } + int startIndex = fullTrace ? 0 : _nameIndex; + + // Create a custom trace from the frames + _frames = new CustomFrame[stackTrace.FrameCount - startIndex]; + for (int i = 0; i < _frames.Length; ++i) + _frames[i] = new CustomFrame(frames[i + startIndex]); + } + + private bool IsCreationMethod(MethodBase method) + { + // Any method in Mapping or Wrappers is purely for creation + if (method.DeclaringType == typeof(Mapping) || + method.DeclaringType == typeof(Stubs.Wrappers)) + return true; + + // As is any ctor in OutlookWrappers. Methods there aren't, as they might created a + // wrapper for a property + if (method.IsConstructor && + method.DeclaringType.Namespace == "Acacia.Stubs.OutlookWrappers") + return true; + + return false; + } + + public override string ToString() + { + string s = ""; + foreach (CustomFrame frame in _frames) + s += frame.ToString(); + return s; + } + + public override bool Equals(object obj) + { + return ToString().Equals(obj.ToString()); + } + + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + + public int CompareTo(CustomTrace other) + { + return ToString().CompareTo(other.ToString()); + } + + public string DisplayName + { + get { return _frames[fullTrace ? _nameIndex : 0].MethodName; } + } + + public CustomFrame[] Frames + { + get { return _frames; } + } + } + + private readonly ConcurrentDictionary _types = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _locations = new ConcurrentDictionary(); + + public void Created(DisposableWrapper wrapper) + { + _types.AddOrUpdate(wrapper.GetType(), 1, (i, value) => value + 1); + _locations.AddOrUpdate(new CustomTrace(wrapper.StackTrace), 1, (i, value) => value + 1); + } + + public void Deleted(DisposableWrapper wrapper, bool wasDisposed) + { + if (!wasDisposed) + { + _types.AddOrUpdate(wrapper.GetType(), 0, (i, value) => value - 1); + _locations.AddOrUpdate(new CustomTrace(wrapper.StackTrace), 0, (i, value) => value - 1); + } + } + + public void Disposed(DisposableWrapper wrapper) + { + _types.AddOrUpdate(wrapper.GetType(), 0, (i, value) => value - 1); + _locations.AddOrUpdate(new CustomTrace(wrapper.StackTrace), 0, (i, value) => value - 1); + } + + public IEnumerable> GetTypes() + { + return _types; + } + + public IEnumerable> GetLocations() + { + return _locations; + } + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableWrapper.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableWrapper.cs index b7cefcf..8a0010c 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableWrapper.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Utils/DisposableWrapper.cs @@ -26,12 +26,28 @@ namespace Acacia.Utils { abstract public class DisposableWrapper : IDisposable { - private static Dictionary typeCounts = new Dictionary(); + private static DisposableTracer tracer = InitTracer(); + + private static DisposableTracer InitTracer() + { + if (GlobalOptions.INSTANCE.WrapperTrace) + return new DisposableTracerFull(); + return new DisposableTracerDummy(); + } + + public static DisposableTracerFull GetTracer() + { + return tracer as DisposableTracerFull; + } + + private bool _isDisposed; + public readonly System.Diagnostics.StackTrace StackTrace; protected DisposableWrapper() { Interlocked.Increment(ref Statistics.CreatedWrappers); - this._createdTrace = new System.Diagnostics.StackTrace(); + this.StackTrace = new System.Diagnostics.StackTrace(1, true); + tracer.Created(this); } ~DisposableWrapper() @@ -39,27 +55,17 @@ namespace Acacia.Utils Interlocked.Increment(ref Statistics.DeletedWrappers); if (!_isDisposed) { - Logger.Instance.Warning(this, "Undisposed wrapper: {0}", _createdTrace); - // Dispose, but don't count auto disposals, so the stats show it. + Logger.Instance.Warning(this, "Undisposed wrapper: {0}", StackTrace); DoRelease(); } - else - { - --typeCounts[GetType()]; - } + tracer.Deleted(this, _isDisposed); } - private bool _isDisposed; - private readonly System.Diagnostics.StackTrace _createdTrace; - virtual public void Dispose() { if (!_isDisposed) { - if (!typeCounts.ContainsKey(GetType())) - typeCounts.Add(GetType(), 1); - else - ++typeCounts[GetType()]; + tracer.Disposed(this); _isDisposed = true; Interlocked.Increment(ref Statistics.DisposedWrappers);