From 566b84c2818ba56c2e5f27071428d0655276d31a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 3 Sep 2009 21:46:52 +0000 Subject: [PATCH] Profiler: - added translation for the profiler UI - added functionality to control data collection (only profile parts) - added new columns (Time spent (self); Time spent/call, Time spent (self)/call) - added hot path indicators - Top 20 tab is now sorted by TimeSpentSelf * fixed bug in LocalizeExtension git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@4874 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Controller/Data/IProfilingDataSet.cs | 5 + .../Data/ProfilingDataSQLiteProvider.cs | 30 +++++- .../Data/ProfilingDataSQLiteWriter.cs | 6 +- .../Controller/Data/TempFileDatabase.cs | 13 +-- .../Data/UnmanagedProfilingDataSet.cs | 11 ++- .../Misc/Profiler/Controller/Profiler.cs | 44 ++++++--- .../Profiler/Controller/ProfilerOptions.cs | 15 ++- .../Misc/Profiler/Frontend/AddIn/AddIn.csproj | 97 ++++++++++--------- .../AddIn/ICSharpCode.Profiler.AddIn.addin | 35 +++---- .../AddIn/Src/Commands/CopyStacktrace.cs | 7 -- .../AddIn/Src/Commands/ProfileExecutable.cs | 11 +-- .../Src/Dialogs/ProfilerControlWindow.xaml | 30 ++++++ .../Src/Dialogs/ProfilerControlWindow.xaml.cs | 66 +++++++++++++ .../AddIn/Src/OptionsPanels/General.cs | 47 ++++++--- .../OptionsPanels/GeneralOptionsPanel.xaml | 65 ++++++++++--- .../OptionsPanels/GeneralOptionsPanel.xaml.cs | 12 ++- .../Frontend/AddIn/Src/ProfilerRunner.cs | 12 ++- .../AddIn/Src/Views/ProfilerView.xaml.cs | 15 +-- .../Controls/CallTreeNodeViewModel.cs | 2 +- .../Frontend/Controls/Controls.csproj | 2 + .../Frontend/Controls/ControlsTranslation.cs | 22 +++++ .../Frontend/Controls/QueryView.xaml.cs | 6 +- .../Frontend/Controls/TimeLineControl.cs | 31 +++++- .../LocalizeExtension.cs | 6 +- 24 files changed, 427 insertions(+), 163 deletions(-) create mode 100644 src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml create mode 100644 src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml.cs create mode 100644 src/AddIns/Misc/Profiler/Frontend/Controls/ControlsTranslation.cs diff --git a/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs b/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs index 44b55970c3..b481fff5fd 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs @@ -27,6 +27,11 @@ namespace ICSharpCode.Profiler.Controller.Data /// double CpuUsage { get; } + /// + /// Gets whether this dataset is the first dataset of a profiling run. + /// + bool IsFirst { get; } + /// /// Gets the root node of the dataset. /// diff --git a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs index 742a1ce07f..9df90fec69 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs @@ -173,14 +173,26 @@ namespace ICSharpCode.Profiler.Controller.Data SQLiteCommand cmd; using (LockAndCreateCommand(out cmd)) { - cmd.CommandText = @"SELECT id, cpuusage + SQLiteDataReader reader; + bool isFirstAllowed = true; + try { + cmd.CommandText = @"SELECT id, cpuusage, isfirst FROM DataSets ORDER BY id;"; - - SQLiteDataReader reader = cmd.ExecuteReader(); + + reader = cmd.ExecuteReader(); + } catch (SQLiteException) { + cmd.CommandText = @"SELECT id, cpuusage + FROM DataSets + ORDER BY id;"; + + reader = cmd.ExecuteReader(); + + isFirstAllowed = false; + } while (reader.Read()) { - list.Add(new SQLiteDataSet(this, reader.GetInt32(0), reader.GetDouble(1))); + list.Add(new SQLiteDataSet(this, reader.GetInt32(0), reader.GetDouble(1), isFirstAllowed ? reader.GetBoolean(2) : false)); } } @@ -212,12 +224,14 @@ namespace ICSharpCode.Profiler.Controller.Data ProfilingDataSQLiteProvider provider; int index; double cpuUsage; + bool isFirst; - public SQLiteDataSet(ProfilingDataSQLiteProvider provider, int index, double cpuUsage) + public SQLiteDataSet(ProfilingDataSQLiteProvider provider, int index, double cpuUsage, bool isFirst) { this.provider = provider; this.index = index; this.cpuUsage = cpuUsage; + this.isFirst = isFirst; } public double CpuUsage { @@ -231,6 +245,12 @@ namespace ICSharpCode.Profiler.Controller.Data return this.provider.GetRoot(index, index); } } + + public bool IsFirst { + get { + return isFirst; + } + } } /// diff --git a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs index ca4749a2e5..3834311b37 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs @@ -106,10 +106,11 @@ namespace ICSharpCode.Profiler.Controller.Data cmd.Parameters.Add(new SQLiteParameter("id", dataSetCount)); cmd.Parameters.Add(new SQLiteParameter("cpuuage", dataSet.CpuUsage.ToString(CultureInfo.InvariantCulture))); + cmd.Parameters.Add(new SQLiteParameter("isfirst", dataSet.IsFirst)); cmd.Parameters.Add(new SQLiteParameter("rootid", functionInfoCount)); - cmd.CommandText = "INSERT INTO DataSets(id, cpuusage, rootid)" + - "VALUES(?,?,?);"; + cmd.CommandText = "INSERT INTO DataSets(id, cpuusage, isfirst, rootid)" + + "VALUES(?,?,?,?);"; using (SQLiteCommand loopCommand = this.connection.CreateCommand()) { CallTreeNode node = dataSet.RootNode; @@ -208,6 +209,7 @@ namespace ICSharpCode.Profiler.Controller.Data CREATE TABLE DataSets( id INTEGER NOT NULL PRIMARY KEY, cpuusage REAL NOT NULL, + isfirst INTEGER NOT NULL, rootid INTEGER NOT NULL ); diff --git a/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs b/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs index 411cddfd1f..fa6b63fe16 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs @@ -44,6 +44,7 @@ namespace ICSharpCode.Profiler.Controller.Data public long StreamStartPosition { get; set; } public long StreamLength { get; set; } public double CpuUsage { get; set; } + public bool IsFirst { get; set; } } #region TempFileDatabase DataSet and DataWriter implementation @@ -54,8 +55,8 @@ namespace ICSharpCode.Profiler.Controller.Data public DataSet(TempFileDatabase database, UnmanagedMemory view, TargetProcessPointer nativeStartPosition, TargetProcessPointer nativeRootFuncInfoStartPosition, - double cpuUsage) - : base(nativeStartPosition, nativeRootFuncInfoStartPosition, view.Pointer, view.Length, cpuUsage, database.is64Bit) + double cpuUsage, bool isFirst) + : base(nativeStartPosition, nativeRootFuncInfoStartPosition, view.Pointer, view.Length, cpuUsage, isFirst, database.is64Bit) { this.database = database; this.view = view; @@ -116,7 +117,7 @@ namespace ICSharpCode.Profiler.Controller.Data if (dataSet == null) throw new InvalidOperationException("TempFileDatabase cannot write DataSets other than UnmanagedProfilingDataSet!"); - database.AddDataset((byte *)uDataSet.StartPtr.ToPointer(), uDataSet.Length, uDataSet.NativeStartPosition, uDataSet.NativeRootFuncInfoPosition, uDataSet.CpuUsage); + database.AddDataset((byte *)uDataSet.StartPtr.ToPointer(), uDataSet.Length, uDataSet.NativeStartPosition, uDataSet.NativeRootFuncInfoPosition, uDataSet.CpuUsage, uDataSet.IsFirst); this.database.is64Bit = uDataSet.Is64Bit; } @@ -149,14 +150,14 @@ namespace ICSharpCode.Profiler.Controller.Data this.file = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.Asynchronous | FileOptions.DeleteOnClose); } - unsafe void AddDataset(byte *ptr, long length, TargetProcessPointer nativeStartPosition, TargetProcessPointer nativeRootFuncInfoStartPosition, double cpuUsage) + unsafe void AddDataset(byte *ptr, long length, TargetProcessPointer nativeStartPosition, TargetProcessPointer nativeRootFuncInfoStartPosition, double cpuUsage, bool isFirst) { byte[] data = new byte[length]; Marshal.Copy(new IntPtr(ptr), data, 0, (int)length); if (this.currentWrite != null) this.file.EndWrite(this.currentWrite); this.streamInfos.Add(new StreamInfo { NativeStartPosition = nativeStartPosition, NativeRootFuncInfoStartPosition = nativeRootFuncInfoStartPosition, - StreamStartPosition = this.file.Length, StreamLength = length, CpuUsage = cpuUsage }); + StreamStartPosition = this.file.Length, StreamLength = length, CpuUsage = cpuUsage, IsFirst = isFirst }); this.currentWrite = this.file.BeginWrite(data, 0, (int)length, null, null); } @@ -206,7 +207,7 @@ namespace ICSharpCode.Profiler.Controller.Data return new DataSet(this, mmf.MapView(streamInfos[index].StreamStartPosition, streamInfos[index].StreamLength), streamInfos[index].NativeStartPosition, streamInfos[index].NativeRootFuncInfoStartPosition, - streamInfos[index].CpuUsage); + streamInfos[index].CpuUsage, streamInfos[index].IsFirst); } /// diff --git a/src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs b/src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs index ee30a14d00..51c496c04b 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs @@ -24,6 +24,7 @@ namespace ICSharpCode.Profiler.Controller.Data TargetProcessPointer nativeRootFuncInfoPosition; bool isDisposed; double cpuUsage; + bool isFirst; bool is64Bit; /// @@ -82,12 +83,13 @@ namespace ICSharpCode.Profiler.Controller.Data public abstract NameMapping GetMapping(int nameId); internal UnmanagedProfilingDataSet(TargetProcessPointer nativeStartPosition, TargetProcessPointer nativeRootFuncInfoPosition, - byte *startPtr, long length, double cpuUsage, bool is64Bit) + byte *startPtr, long length, double cpuUsage, bool isFirst, bool is64Bit) { this.nativeStartPosition = nativeStartPosition; this.cpuUsage = cpuUsage; this.nativeRootFuncInfoPosition = nativeRootFuncInfoPosition; this.is64Bit = is64Bit; + this.isFirst = isFirst; this.startPtr = new IntPtr(startPtr); this.length = length; } @@ -140,5 +142,12 @@ namespace ICSharpCode.Profiler.Controller.Data { isDisposed = true; } + + /// + public bool IsFirst { + get { + return isFirst; + } + } } } diff --git a/src/AddIns/Misc/Profiler/Controller/Profiler.cs b/src/AddIns/Misc/Profiler/Controller/Profiler.cs index 0e6cfec793..7348df9f0c 100644 --- a/src/AddIns/Misc/Profiler/Controller/Profiler.cs +++ b/src/AddIns/Misc/Profiler/Controller/Profiler.cs @@ -48,6 +48,8 @@ namespace ICSharpCode.Profiler.Controller bool is64Bit; bool isRunning; volatile bool stopDC; + volatile bool enableDC; + volatile bool isFirstDC; /// /// Gets whether the profiler is running inside a 64-bit profilee process or not. @@ -198,6 +200,17 @@ namespace ICSharpCode.Profiler.Controller get { return profilerOutput.ToString(); } } + public void EnableDataCollection() + { + this.enableDC = true; + } + + public void DisableDataCollection() + { + this.enableDC = false; + this.isFirstDC = true; + } + /// /// Creates a new profiler using the path to an executable to profile and a data writer. /// @@ -302,8 +315,6 @@ namespace ICSharpCode.Profiler.Controller this.Pause(); this.threadListMutex.WaitOne(); - Debug.Print("running DC " + (is64Bit ? "x64" : "x86")); - if (this.is64Bit) CollectData64(); else @@ -342,13 +353,16 @@ namespace ICSharpCode.Profiler.Controller item = (ThreadLocalData32*)TranslatePointer(item->Predecessor); } - - this.AddDataset(fullView.Pointer, - memHeader32->NativeAddress + memHeader32->HeapOffset, - memHeader32->Allocator.startPos - memHeader32->NativeAddress, - memHeader32->Allocator.pos - memHeader32->Allocator.startPos, - (cpuUsageCounter == null) ? 0 : cpuUsageCounter.NextValue(), - memHeader32->RootFuncInfoAddress); + if (this.enableDC) { + this.AddDataset(fullView.Pointer, + memHeader32->NativeAddress + memHeader32->HeapOffset, + memHeader32->Allocator.startPos - memHeader32->NativeAddress, + memHeader32->Allocator.pos - memHeader32->Allocator.startPos, + (cpuUsageCounter == null) ? 0 : cpuUsageCounter.NextValue(), + isFirstDC, + memHeader32->RootFuncInfoAddress); + isFirstDC = false; + } ZeroMemory(new IntPtr(TranslatePointer(memHeader32->Allocator.startPos)), new IntPtr(memHeader32->Allocator.pos - memHeader32->Allocator.startPos)); @@ -388,9 +402,9 @@ namespace ICSharpCode.Profiler.Controller } } - unsafe void AddDataset(byte *ptr, TargetProcessPointer nativeStartPosition, long offset, long length, double cpuUsage, TargetProcessPointer nativeRootFuncInfoPosition) + unsafe void AddDataset(byte *ptr, TargetProcessPointer nativeStartPosition, long offset, long length, double cpuUsage, bool isFirst, TargetProcessPointer nativeRootFuncInfoPosition) { - using (DataSet dataSet = new DataSet(this, ptr + offset, length, nativeStartPosition, nativeRootFuncInfoPosition, cpuUsage, is64Bit)) { + using (DataSet dataSet = new DataSet(this, ptr + offset, length, nativeStartPosition, nativeRootFuncInfoPosition, cpuUsage, isFirst, is64Bit)) { lock (this.dataWriter) { this.dataWriter.WriteDataSet(dataSet); } @@ -492,6 +506,9 @@ namespace ICSharpCode.Profiler.Controller this.profilee.StartInfo = this.psi; this.profilee.Exited += new EventHandler(ProfileeExited); + this.enableDC = this.profilerOptions.EnableDCAtStart; + this.isFirstDC = true; + Debug.WriteLine("Launching profiler for " + this.psi.FileName + "..."); this.profilee.Start(); @@ -578,6 +595,7 @@ namespace ICSharpCode.Profiler.Controller // unload all counters to prevent exception during last collection! this.cpuUsageCounter = null; this.performanceCounters = null; + // Take last shot if (this.is64Bit) CollectData64(); @@ -796,8 +814,8 @@ namespace ICSharpCode.Profiler.Controller public DataSet(Profiler profiler, byte *startPtr, long length, TargetProcessPointer nativeStartPosition, TargetProcessPointer nativeRootFuncInfoPosition, - double cpuUsage, bool is64Bit) - : base(nativeStartPosition, nativeRootFuncInfoPosition, startPtr, length, cpuUsage, is64Bit) + double cpuUsage, bool isFirst, bool is64Bit) + : base(nativeStartPosition, nativeRootFuncInfoPosition, startPtr, length, cpuUsage, isFirst, is64Bit) { this.profiler = profiler; } diff --git a/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs b/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs index 0788b76973..adf0578b12 100644 --- a/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs +++ b/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs @@ -17,9 +17,10 @@ namespace ICSharpCode.Profiler.Controller /// /// Defines the default size of the shared memory. /// - public const int SHARED_MEMORY_SIZE = 64 * 1024 * 1024; // 64 mb + public const int DefaultSharedMemorySize = 64 * 1024 * 1024; // 64 mb bool enableDC; + bool enableDCAtStart; bool dotNotProfileDotNetInternals; bool combineRecursiveFunction; int sharedMemorySize; @@ -45,6 +46,13 @@ namespace ICSharpCode.Profiler.Controller get { return enableDC; } } + /// + /// Gets whether data collection is enabled at the start of the profiling session. + /// + public bool EnableDCAtStart { + get { return enableDCAtStart; } + } + /// /// Gets the size of the shared memory. /// @@ -55,19 +63,20 @@ namespace ICSharpCode.Profiler.Controller /// /// Creates new ProfilerOptions using the selected settings. /// - public ProfilerOptions(bool enableDC, int sharedMemorySize, bool profileDotNetInternals, bool combineRecursiveFunction) + public ProfilerOptions(bool enableDC, int sharedMemorySize, bool profileDotNetInternals, bool combineRecursiveFunction, bool enableDCAtStart) { this.enableDC = enableDC; this.sharedMemorySize = sharedMemorySize; this.dotNotProfileDotNetInternals = profileDotNetInternals; this.combineRecursiveFunction = combineRecursiveFunction; + this.enableDCAtStart = enableDCAtStart; } /// /// Creates default ProfilerOptions. /// public ProfilerOptions() - : this(true, SHARED_MEMORY_SIZE, false, false) + : this(true, DefaultSharedMemorySize, false, false, true) { } } diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/AddIn.csproj b/src/AddIns/Misc/Profiler/Frontend/AddIn/AddIn.csproj index 12abd8136d..09e47da3f7 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/AddIn.csproj +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/AddIn.csproj @@ -38,54 +38,6 @@ AnyCPU 4096 - - - ..\..\..\..\..\..\bin\ICSharpCode.Core.dll - False - - - ..\..\..\..\..\..\bin\ICSharpCode.Core.Presentation.dll - False - - - ..\..\..\..\..\..\bin\ICSharpCode.SharpDevelop.dll - False - - - ..\..\..\..\..\..\bin\ICSharpCode.SharpDevelop.Dom.dll - False - - - - - 3.0 - - - 3.0 - - - - 3.5 - - - - - - - 3.5 - - - - 3.0 - - - ..\..\..\..\..\..\AddIns\AddIns\Misc\UnitTesting\UnitTesting.dll - - - 3.0 - - - Always @@ -106,6 +58,10 @@ ProfileExecutableForm.xaml + + ProfilerControlWindow.xaml + Code + UserControl @@ -128,18 +84,63 @@ Designer MSBuild:Compile + Designer MSBuild:Compile + + + + ..\..\..\..\..\..\bin\ICSharpCode.Core.dll + False + + + ..\..\..\..\..\..\bin\ICSharpCode.Core.Presentation.dll + False + + + ..\..\..\..\..\..\bin\ICSharpCode.SharpDevelop.dll + False + + + ..\..\..\..\..\..\bin\ICSharpCode.SharpDevelop.Dom.dll + False + + + + 3.0 + + + 3.0 + + + + 3.5 + + + + ..\..\..\..\..\..\AddIns\AddIns\Misc\UnitTesting\UnitTesting.dll + False + + + 3.0 + + + 3.0 + + + {72FFB35A-C9E2-4A31-B4FA-E3E3E28DED5F} Controller + True {BDA49550-5ED1-4C6B-B648-657B2CACD8E0} Controls + True diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/ICSharpCode.Profiler.AddIn.addin b/src/AddIns/Misc/Profiler/Frontend/AddIn/ICSharpCode.Profiler.AddIn.addin index e9e90d3bc1..58370088c8 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/ICSharpCode.Profiler.AddIn.addin +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/ICSharpCode.Profiler.AddIn.addin @@ -22,13 +22,13 @@ @@ -37,58 +37,55 @@ - - + label = "{res:AddIns.Profiler.Options.Title}" + class = "ICSharpCode.Profiler.AddIn.OptionsPanels.General" + insertbefore = "TextEditorOptions" /> @@ -100,7 +97,7 @@ diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/CopyStacktrace.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/CopyStacktrace.cs index d70c76302d..7700ded6d7 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/CopyStacktrace.cs +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/CopyStacktrace.cs @@ -6,16 +6,9 @@ // using System; -using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Text; using System.Windows.Forms; -using System.Windows.Shapes; - -using ICSharpCode.Core; -using ICSharpCode.Profiler.Controller.Queries; -using ICSharpCode.Profiler.Controls; namespace ICSharpCode.Profiler.AddIn.Commands { diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/ProfileExecutable.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/ProfileExecutable.cs index eaccea7dd2..2629bcd192 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/ProfileExecutable.cs +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Commands/ProfileExecutable.cs @@ -6,17 +6,8 @@ // using System; -using System.IO; -using System.Windows.Forms; - -using ICSharpCode.Core; -using ICSharpCode.Profiler.AddIn.Views; -using ICSharpCode.Profiler.Controller.Data; -using ICSharpCode.SharpDevelop; -using ICSharpCode.SharpDevelop.Gui; -using ICSharpCode.SharpDevelop.Project; -using Microsoft.Build.BuildEngine; using System.Windows.Interop; +using ICSharpCode.SharpDevelop.Gui; namespace ICSharpCode.Profiler.AddIn.Commands { diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml new file mode 100644 index 0000000000..e454473407 --- /dev/null +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml @@ -0,0 +1,30 @@ + + + + + + + + +