From 3bc647a0f3e0e69dbaeed177995589a0ca94a9fb Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Oct 2009 22:17:36 +0000 Subject: [PATCH] - implemented PerformanceCounterDescriptor - added ExtendedTimeLineControl - updated SDPS to 1.2 git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5049 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../Controller/Data/IProfilingDataSet.cs | 5 - .../Controller/Data/IProfilingDataWriter.cs | 2 + .../Data/PerformanceCounterDescriptor.cs | 82 ++++++- .../Controller/Data/ProfilingDataProvider.cs | 5 + .../Data/ProfilingDataSQLiteProvider.cs | 85 +++++-- .../Data/ProfilingDataSQLiteWriter.cs | 81 +++++-- .../Controller/Data/TempFileDatabase.cs | 27 ++- .../Controller/Data/UnitTestWriter.cs | 12 +- .../Data/UnmanagedProfilingDataSet.cs | 12 +- .../Misc/Profiler/Controller/Profiler.cs | 46 ++-- .../Profiler/Controller/ProfilerOptions.cs | 22 +- .../Src/Dialogs/ProfilerControlWindow.xaml | 50 ++--- .../AddIn/Src/OptionPanels/OptionWrapper.cs | 6 +- .../Frontend/AddIn/Src/ProfilerRunner.cs | 26 ++- .../AddIn/Src/Views/ProfilerView.xaml | 20 +- .../AddIn/Src/Views/ProfilerView.xaml.cs | 21 +- .../Controls/ExtendedTimeLineControl.xaml | 37 ++- .../Controls/ExtendedTimeLineControl.xaml.cs | 70 ++++++ .../Profiler/Frontend/Controls/QueryView.xaml | 14 +- .../Frontend/Controls/QueryView.xaml.cs | 10 - .../Frontend/Controls/TimeLineControl.cs | 211 +++++++++++++----- .../Controller/Data/CallTreeNodeStub.cs | 1 - .../Controller/Data/LinqTests.cs | 9 +- 23 files changed, 597 insertions(+), 257 deletions(-) diff --git a/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs b/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs index b481fff5fd..abc00bd3b9 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs @@ -22,11 +22,6 @@ namespace ICSharpCode.Profiler.Controller.Data /// public interface IProfilingDataSet { - /// - /// Gets the percent of CPU power used by the profilee at a certain point. - /// - double CpuUsage { get; } - /// /// Gets whether this dataset is the first dataset of a profiling run. /// diff --git a/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataWriter.cs b/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataWriter.cs index e36829bcf4..a8c3446eb3 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataWriter.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataWriter.cs @@ -29,6 +29,8 @@ namespace ICSharpCode.Profiler.Controller.Data /// void WriteMappings(IEnumerable mappings); + void WritePerformanceCounterData(IEnumerable counters); + /// /// Closes and disposes the underlying data structure. /// diff --git a/src/AddIns/Misc/Profiler/Controller/Data/PerformanceCounterDescriptor.cs b/src/AddIns/Misc/Profiler/Controller/Data/PerformanceCounterDescriptor.cs index ae3fb2e732..473ad3ffe7 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/PerformanceCounterDescriptor.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/PerformanceCounterDescriptor.cs @@ -6,13 +6,83 @@ // using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; namespace ICSharpCode.Profiler.Controller.Data { -// public class PerformanceCounterDescriptor -// { -// public PerformanceCounterDescriptor() -// { -// } -// } + public class PerformanceCounterDescriptor + { + public string Category { get; private set; } + public string Name { get; private set; } + public string Instance { get; private set; } + public string Computer { get; private set; } + public IList Values { get; private set; } + + public float? MinValue { get; private set; } + public float? MaxValue { get; private set; } + public string Unit { get; private set; } + + float defaultValue; + PerformanceCounter counter; + + public PerformanceCounterDescriptor(string category, string name, string instance, string computer, + float defaultValue, float? minValue, float? maxValue, string unit) + { + Category = category; + Name = name; + Instance = instance; + Computer = computer; + Values = new List(); + this.defaultValue = defaultValue; + MinValue = minValue; + MaxValue = maxValue; + Unit = unit; + } + + public PerformanceCounterDescriptor(string name, float? minValue, float? maxValue, string unit) + : this(null, name, null, null, 0, minValue, maxValue, unit) + { + } + + public static string GetProcessInstanceName(int pid) + { + PerformanceCounterCategory cat = new PerformanceCounterCategory("Process"); + + string[] instances = cat.GetInstanceNames(); + foreach (string instance in instances) { + using (PerformanceCounter cnt = new PerformanceCounter("Process", "ID Process", instance, true)) { + int val = (int)cnt.RawValue; + if (val == pid) + return instance; + } + } + + return null; + } + + public void Reset() + { + this.Values.Clear(); + } + + public void Collect(string instanceName) + { + if (counter == null && Instance != null) + counter = new PerformanceCounter(Category, Name, instanceName ?? Instance, Computer); + + try { + this.Values.Add(counter.NextValue()); + } catch (Exception e) { + Console.WriteLine(e.ToString()); + this.Values.Add(defaultValue); + } + } + + public override string ToString() + { + return Name; + } + } } diff --git a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataProvider.cs b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataProvider.cs index bdca6fd303..3d6d016c28 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataProvider.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataProvider.cs @@ -6,6 +6,7 @@ // using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; @@ -75,5 +76,9 @@ namespace ICSharpCode.Profiler.Controller.Data { return GetAllCalls(startIndex, endIndex).Where(c => !c.IsThread).MergeByName(); } + + public abstract PerformanceCounterDescriptor[] GetPerformanceCounters(); + + public abstract float[] GetPerformanceCounterValues(int index); } } diff --git a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs index 53d07bc0c1..dd4e0425b1 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs @@ -56,6 +56,8 @@ namespace ICSharpCode.Profiler.Controller.Data } } + const string currentVersion = "1.2"; + void CheckFileVersion(bool allowUpgrade) { string version = GetProperty("version"); @@ -63,7 +65,9 @@ namespace ICSharpCode.Profiler.Controller.Data if (version == "1.0" && allowUpgrade) { try { using (SQLiteCommand cmd = connection.CreateCommand()) { - cmd.CommandText = "ALTER TABLE DataSets ADD COLUMN isfirst INTEGER NOT NULL DEFAULT(0);"; + cmd.CommandText = "DROP TABLE CounterData;" + + "DROP TABLE PerformanceCounter;" + + "ALTER TABLE DataSets ADD COLUMN isfirst INTEGER NOT NULL DEFAULT(0);"; cmd.ExecuteNonQuery(); } } catch (SQLiteException) { @@ -73,7 +77,7 @@ namespace ICSharpCode.Profiler.Controller.Data try { using (SQLiteCommand cmd = connection.CreateCommand()) { cmd.CommandText = ProfilingDataSQLiteWriter.CallsAndFunctionsTableDefs + @" - INSERT OR REPLACE INTO Properties(name, value) VALUES('version', '1.1'); + INSERT OR REPLACE INTO Properties(name, value) VALUES('version', '" + currentVersion + @"'); INSERT INTO Calls SELECT f1.id, f1.endid, f1.parentid, f1.nameid, f1.timespent, @@ -88,8 +92,16 @@ namespace ICSharpCode.Profiler.Controller.Data JOIN FunctionData f ON c.id = f.id GROUP BY c.nameid, f.datasetid; + + INSERT INTO PerformanceCounters(id, name, minvalue, maxvalue, unit) + VALUES(0, '% Processor Time', 0, 100, '%'); + + INSERT INTO CounterData + SELECT id, 0, cpuusage + FROM DataSets; " - + "DELETE FROM FunctionData;" // I would like to do DROP TABLE, but that causes locking errors + + "DELETE FROM FunctionData;" + + "DROP TABLE FunctionData;" // I would like to do DROP TABLE, but that causes locking errors + ProfilingDataSQLiteWriter.CallsAndFunctionsIndexDefs; cmd.ExecuteNonQuery(); } @@ -99,7 +111,7 @@ namespace ICSharpCode.Profiler.Controller.Data throw; } } - version = "1.1"; // new version that was upgraded to + version = currentVersion; // new version that was upgraded to try { // VACUUM must be run outside the transaction using (SQLiteCommand cmd = connection.CreateCommand()) { @@ -111,7 +123,7 @@ namespace ICSharpCode.Profiler.Controller.Data } } - if (version != "1.1") + if (version != currentVersion) throw new IncompatibleDatabaseException(new Version(1, 0), new Version(version)); } @@ -181,7 +193,7 @@ namespace ICSharpCode.Profiler.Controller.Data using (LockAndCreateCommand(out cmd)) { SQLiteDataReader reader; - cmd.CommandText = @"SELECT d.id, d.cpuusage, d.isfirst, d.rootid, c.endid + cmd.CommandText = @"SELECT d.id, d.isfirst, d.rootid, c.endid FROM DataSets d JOIN Calls c ON c.id = d.rootid ORDER BY d.id;"; @@ -189,8 +201,8 @@ namespace ICSharpCode.Profiler.Controller.Data reader = cmd.ExecuteReader(); while (reader.Read()) { - list.Add(new SQLiteDataSet(this, reader.GetInt32(0), reader.GetDouble(1), reader.GetBoolean(2), - reader.GetInt32(3), reader.GetInt32(4))); + list.Add(new SQLiteDataSet(this, reader.GetInt32(0), reader.GetBoolean(1), + reader.GetInt32(2), reader.GetInt32(3))); } } @@ -205,26 +217,18 @@ namespace ICSharpCode.Profiler.Controller.Data { ProfilingDataSQLiteProvider provider; public readonly int ID; - double cpuUsage; bool isFirst; public readonly int RootID, CallEndID; - public SQLiteDataSet(ProfilingDataSQLiteProvider provider, int id, double cpuUsage, bool isFirst, int rootID, int callEndID) + public SQLiteDataSet(ProfilingDataSQLiteProvider provider, int id, bool isFirst, int rootID, int callEndID) { this.provider = provider; this.ID = id; - this.cpuUsage = cpuUsage; this.isFirst = isFirst; this.RootID = rootID; this.CallEndID = callEndID; } - public double CpuUsage { - get { - return cpuUsage; - } - } - public CallTreeNode RootNode { get { return this.provider.GetRoot(ID, ID); @@ -348,6 +352,53 @@ namespace ICSharpCode.Profiler.Controller.Data return query.Where(c => c.NameMapping.Id != 0 && !c.IsThread).MergeByName(); } + public override PerformanceCounterDescriptor[] GetPerformanceCounters() + { + SQLiteCommand cmd; + using (LockAndCreateCommand(out cmd)) { + cmd.CommandText = "SELECT name, minvalue, maxvalue, unit " + + "FROM PerformanceCounter " + + "ORDER BY id ASC;"; + + List list = new List(); + + var reader = cmd.ExecuteReader(); + + while (reader.Read()) { + list.Add( + new PerformanceCounterDescriptor( + reader.GetString(0), + reader.IsDBNull(1) ? null : new Nullable(reader.GetFloat(1)), + reader.IsDBNull(2) ? null : new Nullable(reader.GetFloat(2)), + reader.GetString(3) + ) + ); + } + + return list.ToArray(); + } + } + + public override float[] GetPerformanceCounterValues(int index) + { + SQLiteCommand cmd; + using (LockAndCreateCommand(out cmd)) { + cmd.CommandText = "SELECT value " + + "FROM CounterData " + + "WHERE counterid = " + index + " " + + "ORDER BY datasetid ASC;"; + + List list = new List(); + + var reader = cmd.ExecuteReader(); + + while (reader.Read()) + list.Add(reader.GetFloat(0)); + + return list.ToArray(); + } + } + Expression> DataSetFilter(int startIndex, int endIndex) { return c => startIndex <= c.DataSetID && c.DataSetID <= endIndex; diff --git a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs index 6708b412f0..8d6e3a9803 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs @@ -99,12 +99,11 @@ namespace ICSharpCode.Profiler.Controller.Data dataSetCount = 0; 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, isfirst, rootid)" + - "VALUES(?,?,?,?);"; + cmd.CommandText = "INSERT INTO DataSets(id, isfirst, rootid)" + + "VALUES(?,?,?);"; int dataSetStartId = functionInfoCount; @@ -164,7 +163,22 @@ namespace ICSharpCode.Profiler.Controller.Data activecallcount INTEGER NOT NULL, callcount INTEGER NOT NULL, hasChildren INTEGER NOT NULL - );"; + ); + + CREATE TABLE PerformanceCounter( + id INTEGER NOT NULL PRIMARY KEY, + name TEXT NOT NULL, + minvalue REAL NULL, + maxvalue REAL NULL, + unit TEXT NOT NULL + ); + + CREATE TABLE CounterData( + datasetid INTEGER NOT NULL, + counterid INTEGER NOT NULL, + value REAL NOT NULL + ); +"; internal const string CallsAndFunctionsIndexDefs = "CREATE INDEX CallsParent ON Calls(parentid ASC);" // required for searching the children @@ -175,7 +189,7 @@ namespace ICSharpCode.Profiler.Controller.Data // NameMapping { Id, ReturnType, Name, Parameters } // Calls { id, endid, parentid, nameid, cpucyclesspent, cpucyclesspentself, isactiveatstart, callcount } // Functions { datasetid, nameid, cpucyclesspent, cpucyclesspentself, activecallcount, callcount, haschildren } - // DataSets { Id, CPUUsage, RootId } + // DataSets { Id, IsFirst, RootId } // // NameMapping.Id <-> FunctionData.NameId 1:N // FunctionData.ParentId <-> FunctionData.Id 1:N @@ -195,7 +209,6 @@ 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 ); @@ -205,18 +218,7 @@ namespace ICSharpCode.Profiler.Controller.Data value TEXT NOT NULL ); - INSERT INTO Properties(name, value) VALUES('version', '1.1'); - - CREATE TABLE PerformanceCounter( - id INTEGER NOT NULL PRIMARY KEY, - name TEXT NOT NULL - ); - - CREATE TABLE CounterData( - datasetid INTEGER NOT NULL, - counterid INTEGER NOT NULL, - value INTEGER NOT NULL - ); + INSERT INTO Properties(name, value) VALUES('version', '1.2'); "; cmd.ExecuteNonQuery(); @@ -299,5 +301,48 @@ namespace ICSharpCode.Profiler.Controller.Data isDisposed = true; } + + public void WritePerformanceCounterData(IEnumerable counters) + { + using (SQLiteTransaction trans = this.connection.BeginTransaction()) { + using (SQLiteCommand cmd = this.connection.CreateCommand()) { + + SQLiteParameter idParam = new SQLiteParameter("id"); + SQLiteParameter nameParam = new SQLiteParameter("name"); + SQLiteParameter dataSetParam = new SQLiteParameter("dataset"); + SQLiteParameter valueParam = new SQLiteParameter("value"); + SQLiteParameter minParam = new SQLiteParameter("min"); + SQLiteParameter maxParam = new SQLiteParameter("max"); + SQLiteParameter unitParam = new SQLiteParameter("unit"); + + cmd.CommandText = + "INSERT OR IGNORE INTO PerformanceCounter(id, name, minvalue, maxvalue, unit)" + + "VALUES(@id,@name,@min,@max,@unit);" + + "INSERT INTO CounterData(datasetid, counterid, value)" + + "VALUES(@dataset,@id,@value);"; + + cmd.Parameters.AddRange(new SQLiteParameter[] { idParam, nameParam, dataSetParam, valueParam, minParam, maxParam, unitParam }); + + int id = 0; + + foreach (PerformanceCounterDescriptor counter in counters) { + idParam.Value = id; + nameParam.Value = counter.Name; + minParam.Value = counter.MinValue; + maxParam.Value = counter.MaxValue; + unitParam.Value = counter.Unit; + + for (int i = 0; i < counter.Values.Count; i++) { + dataSetParam.Value = i; + valueParam.Value = counter.Values[i]; + cmd.ExecuteNonQuery(); + } + + id++; + } + } + trans.Commit(); + } + } } } \ No newline at end of file diff --git a/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs b/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs index 92078c80bc..e4a60fda78 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs @@ -37,13 +37,18 @@ namespace ICSharpCode.Profiler.Controller.Data get { return nameMappings.Select(i => i.Value).ToList().AsReadOnly(); } } + List counters = new List(); + + public ReadOnlyCollection Counters { + get { return counters.AsReadOnly(); } + } + struct StreamInfo { public TargetProcessPointer NativeStartPosition { get; set; } public TargetProcessPointer NativeRootFuncInfoStartPosition { get; set; } public long StreamStartPosition { get; set; } public long StreamLength { get; set; } - public double CpuUsage { get; set; } public bool IsFirst { get; set; } } @@ -55,8 +60,8 @@ namespace ICSharpCode.Profiler.Controller.Data public DataSet(TempFileDatabase database, UnmanagedMemory view, TargetProcessPointer nativeStartPosition, TargetProcessPointer nativeRootFuncInfoStartPosition, - double cpuUsage, bool isFirst) - : base(nativeStartPosition, nativeRootFuncInfoStartPosition, view.Pointer, view.Length, cpuUsage, isFirst, database.is64Bit) + bool isFirst) + : base(nativeStartPosition, nativeRootFuncInfoStartPosition, view.Pointer, view.Length, isFirst, database.is64Bit) { this.database = database; this.view = view; @@ -117,7 +122,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, uDataSet.IsFirst); + database.AddDataset((byte *)uDataSet.StartPtr.ToPointer(), uDataSet.Length, uDataSet.NativeStartPosition, uDataSet.NativeRootFuncInfoPosition, uDataSet.IsFirst); this.database.is64Bit = uDataSet.Is64Bit; } @@ -133,6 +138,11 @@ namespace ICSharpCode.Profiler.Controller.Data foreach (NameMapping nm in mappings) this.database.nameMappings.Add(nm.Id, nm); } + + public void WritePerformanceCounterData(IEnumerable counters) + { + this.database.counters.AddRange(counters); + } } #endregion @@ -150,14 +160,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, bool isFirst) + unsafe void AddDataset(byte *ptr, long length, TargetProcessPointer nativeStartPosition, TargetProcessPointer nativeRootFuncInfoStartPosition, 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, IsFirst = isFirst }); + StreamStartPosition = this.file.Length, StreamLength = length, IsFirst = isFirst }); this.currentWrite = this.file.BeginWrite(data, 0, (int)length, null, null); } @@ -206,8 +216,7 @@ namespace ICSharpCode.Profiler.Controller.Data throw new InvalidOperationException("All writers have to be closed before reading the data from the database!"); return new DataSet(this, mmf.MapView(streamInfos[index].StreamStartPosition, streamInfos[index].StreamLength), streamInfos[index].NativeStartPosition, - streamInfos[index].NativeRootFuncInfoStartPosition, - streamInfos[index].CpuUsage, streamInfos[index].IsFirst); + streamInfos[index].NativeRootFuncInfoStartPosition, streamInfos[index].IsFirst); } /// @@ -219,6 +228,8 @@ namespace ICSharpCode.Profiler.Controller.Data { writer.ProcessorFrequency = this.processorFrequency; writer.WriteMappings(this.nameMappings.Values); + writer.WritePerformanceCounterData(this.counters); + for (int i = 0; i < this.DataSetCount; i++) { using (UnmanagedProfilingDataSet dataSet = this.LoadDataSet(i)) writer.WriteDataSet(dataSet); diff --git a/src/AddIns/Misc/Profiler/Controller/Data/UnitTestWriter.cs b/src/AddIns/Misc/Profiler/Controller/Data/UnitTestWriter.cs index e3ee17d0cc..665f803f1e 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/UnitTestWriter.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/UnitTestWriter.cs @@ -38,15 +38,12 @@ namespace ICSharpCode.Profiler.Controller.Data sealed class UnitTestDataSet : IProfilingDataSet { - public UnitTestDataSet(CallTreeNode root, bool isFirst, double cpuUsage) + public UnitTestDataSet(CallTreeNode root, bool isFirst) { this.RootNode = root; this.IsFirst = isFirst; - this.CpuUsage = cpuUsage; } - public double CpuUsage { get; private set; } - public bool IsFirst { get; private set; } public CallTreeNode RootNode { get; private set; } @@ -70,7 +67,7 @@ namespace ICSharpCode.Profiler.Controller.Data if (list.Count > 0) { this.targetWriter.WriteDataSet( - new UnitTestDataSet(new UnitTestRootCallTreeNode(list), dataSet.IsFirst, dataSet.CpuUsage) + new UnitTestDataSet(new UnitTestRootCallTreeNode(list), dataSet.IsFirst) ); } } @@ -103,5 +100,10 @@ namespace ICSharpCode.Profiler.Controller.Data { this.targetWriter.Close(); } + + public void WritePerformanceCounterData(IEnumerable counters) + { + this.targetWriter.WritePerformanceCounterData(counters); + } } } diff --git a/src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs b/src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs index 51c496c04b..c52847b20d 100644 --- a/src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs +++ b/src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs @@ -23,7 +23,6 @@ namespace ICSharpCode.Profiler.Controller.Data TargetProcessPointer nativeStartPosition; TargetProcessPointer nativeRootFuncInfoPosition; bool isDisposed; - double cpuUsage; bool isFirst; bool is64Bit; @@ -59,14 +58,6 @@ namespace ICSharpCode.Profiler.Controller.Data public IntPtr StartPtr { get { return startPtr; } } - - /// - /// Gets the percentage of the CPU usage by the profilee. - /// - public double CpuUsage - { - get { return cpuUsage; } - } internal abstract unsafe void *TranslatePointer(TargetProcessPointer ptr); @@ -83,10 +74,9 @@ namespace ICSharpCode.Profiler.Controller.Data public abstract NameMapping GetMapping(int nameId); internal UnmanagedProfilingDataSet(TargetProcessPointer nativeStartPosition, TargetProcessPointer nativeRootFuncInfoPosition, - byte *startPtr, long length, double cpuUsage, bool isFirst, bool is64Bit) + byte *startPtr, long length, bool isFirst, bool is64Bit) { this.nativeStartPosition = nativeStartPosition; - this.cpuUsage = cpuUsage; this.nativeRootFuncInfoPosition = nativeRootFuncInfoPosition; this.is64Bit = is64Bit; this.isFirst = isFirst; diff --git a/src/AddIns/Misc/Profiler/Controller/Profiler.cs b/src/AddIns/Misc/Profiler/Controller/Profiler.cs index f3ec1582ce..34d7357546 100644 --- a/src/AddIns/Misc/Profiler/Controller/Profiler.cs +++ b/src/AddIns/Misc/Profiler/Controller/Profiler.cs @@ -84,6 +84,8 @@ namespace ICSharpCode.Profiler.Controller readonly string MutexId = "Local\\Profiler.Mutex.{" + Guid.NewGuid().ToString().ToUpperInvariant() + "}"; readonly string AccessEventId = "Local\\Profiler.Events.{" + Guid.NewGuid().ToString().ToUpperInvariant() + "}"; + string performanceCounterInstanceName; + /// /// The Guid of the CProfiler class in the Hook. /// @@ -101,8 +103,7 @@ namespace ICSharpCode.Profiler.Controller UnmanagedCircularBuffer nativeToManagedBuffer; IProfilingDataWriter dataWriter; - Dictionary performanceCounters; - PerformanceCounter cpuUsageCounter; + PerformanceCounterDescriptor[] performanceCounters; /// /// The currently used data provider. @@ -118,7 +119,7 @@ namespace ICSharpCode.Profiler.Controller // before the profilee can open it. MemoryMappedFile file; - ProfilerOptions profilerOptions = new ProfilerOptions(); + ProfilerOptions profilerOptions; /// /// Gets all settings used by this profiler instance. @@ -248,7 +249,11 @@ namespace ICSharpCode.Profiler.Controller this.is64Bit = DetectBinaryType.RunsAs64Bit(info.FileName); this.profilerOutput = new StringBuilder(); - this.performanceCounters = new Dictionary(); + this.performanceCounters = options.Counters; + + foreach (var counter in performanceCounters) + counter.Reset(); + this.dataWriter = dataWriter; this.threadListMutex = new Mutex(false, MutexId); @@ -360,14 +365,13 @@ namespace ICSharpCode.Profiler.Controller item = (ThreadLocalData32*)TranslatePointer(item->Predecessor); } + 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, memHeader32->RootFuncInfoAddress); isFirstDC = false; } @@ -409,11 +413,20 @@ namespace ICSharpCode.Profiler.Controller } } - unsafe void AddDataset(byte *ptr, TargetProcessPointer nativeStartPosition, long offset, long length, double cpuUsage, bool isFirst, TargetProcessPointer nativeRootFuncInfoPosition) + unsafe void AddDataset(byte *ptr, TargetProcessPointer nativeStartPosition, long offset, long length, bool isFirst, TargetProcessPointer nativeRootFuncInfoPosition) { - using (DataSet dataSet = new DataSet(this, ptr + offset, length, nativeStartPosition, nativeRootFuncInfoPosition, cpuUsage, isFirst, is64Bit)) { + using (DataSet dataSet = new DataSet(this, ptr + offset, length, nativeStartPosition, nativeRootFuncInfoPosition, isFirst, is64Bit)) { lock (this.dataWriter) { this.dataWriter.WriteDataSet(dataSet); + + if (performanceCounterInstanceName == null) + performanceCounterInstanceName = PerformanceCounterDescriptor.GetProcessInstanceName(profilee.Id); + + if (performanceCounterInstanceName == null) + LogString("instance not found!"); + + foreach (var counter in performanceCounters) + counter.Collect(performanceCounterInstanceName); } } } @@ -519,8 +532,6 @@ namespace ICSharpCode.Profiler.Controller Debug.WriteLine("Launching profiler for " + this.psi.FileName + "..."); this.profilee.Start(); - this.cpuUsageCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total", "."); - this.logger.Start(nativeToManagedBuffer.CreateReadingStream()); // GC references currentSession @@ -599,11 +610,7 @@ namespace ICSharpCode.Profiler.Controller if (this.profilerOptions.EnableDC) this.dataCollector.Join(); - // unload all counters to prevent exception during last collection! - this.cpuUsageCounter = null; - this.performanceCounters = null; - - // Take last shot + // Do last data collection if (this.is64Bit) CollectData64(); else @@ -611,6 +618,8 @@ namespace ICSharpCode.Profiler.Controller isRunning = false; + this.dataWriter.WritePerformanceCounterData(performanceCounters); + this.dataWriter.Close(); OnSessionEnded(EventArgs.Empty); @@ -820,9 +829,8 @@ namespace ICSharpCode.Profiler.Controller Profiler profiler; public DataSet(Profiler profiler, byte *startPtr, long length, TargetProcessPointer nativeStartPosition, - TargetProcessPointer nativeRootFuncInfoPosition, - double cpuUsage, bool isFirst, bool is64Bit) - : base(nativeStartPosition, nativeRootFuncInfoPosition, startPtr, length, cpuUsage, isFirst, is64Bit) + TargetProcessPointer nativeRootFuncInfoPosition, bool isFirst, bool is64Bit) + : base(nativeStartPosition, nativeRootFuncInfoPosition, startPtr, length, isFirst, is64Bit) { this.profiler = profiler; } diff --git a/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs b/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs index adf0578b12..649adfb67d 100644 --- a/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs +++ b/src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs @@ -6,6 +6,11 @@ // using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +using ICSharpCode.Profiler.Controller.Data; namespace ICSharpCode.Profiler.Controller { @@ -19,12 +24,23 @@ namespace ICSharpCode.Profiler.Controller /// public const int DefaultSharedMemorySize = 64 * 1024 * 1024; // 64 mb + public static readonly PerformanceCounterDescriptor[] DefaultCounters = new[] { + new PerformanceCounterDescriptor("Process", "% Processor Time", "_Total", ".", 0, 0, 100, "%"), + new PerformanceCounterDescriptor("Process", "IO Data Bytes/sec", "_Total", ".", 0, null, null, "bytes/sec") + }; + bool enableDC; bool enableDCAtStart; bool dotNotProfileDotNetInternals; bool combineRecursiveFunction; int sharedMemorySize; + PerformanceCounterDescriptor[] counters; + + public PerformanceCounterDescriptor[] Counters { + get { return this.counters; } + } + /// /// Gets whether .NET internal calls are profiled or not. /// @@ -63,20 +79,22 @@ namespace ICSharpCode.Profiler.Controller /// /// Creates new ProfilerOptions using the selected settings. /// - public ProfilerOptions(bool enableDC, int sharedMemorySize, bool profileDotNetInternals, bool combineRecursiveFunction, bool enableDCAtStart) + public ProfilerOptions(bool enableDC, int sharedMemorySize, bool profileDotNetInternals, + bool combineRecursiveFunction, bool enableDCAtStart, IEnumerable counters) { this.enableDC = enableDC; this.sharedMemorySize = sharedMemorySize; this.dotNotProfileDotNetInternals = profileDotNetInternals; this.combineRecursiveFunction = combineRecursiveFunction; this.enableDCAtStart = enableDCAtStart; + this.counters = counters.ToArray(); } /// /// Creates default ProfilerOptions. /// public ProfilerOptions() - : this(true, DefaultSharedMemorySize, false, false, true) + : this(true, DefaultSharedMemorySize, false, false, true, DefaultCounters) { } } diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml index 5640a64bf6..9472efd7b7 100644 --- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml +++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml @@ -1,30 +1,26 @@  - - - - - - -