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 @@
-
-
-
-
-
-
-
-
-
+ x:Class="ICSharpCode.Profiler.AddIn.Dialogs.ProfilerControlWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sd="http://icsharpcode.net/sharpdevelop/core"
+ Title="{sd:Localize AddIns.Profiler.ProfilerControlWindow.Title}" SizeToContent="WidthAndHeight"
+ WindowStyle="ToolWindow" Closing="WindowClosing"
+ Topmost="True">
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionPanels/OptionWrapper.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionPanels/OptionWrapper.cs
index 86aaf4f61e..7b1217d973 100644
--- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionPanels/OptionWrapper.cs
+++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionPanels/OptionWrapper.cs
@@ -4,9 +4,10 @@
//
// $Revision$
//
-using ICSharpCode.Profiler.Controller;
using System;
using ICSharpCode.Core;
+using ICSharpCode.Profiler.Controller;
+using ICSharpCode.Profiler.Controller.Data;
namespace ICSharpCode.Profiler.AddIn.OptionPanels
{
@@ -46,7 +47,8 @@ namespace ICSharpCode.Profiler.AddIn.OptionPanels
properties.Get("SharedMemorySize", ProfilerOptions.DefaultSharedMemorySize),
properties.Get("DoNotProfileNetInternals", false),
properties.Get("CombineRecursiveFunction", false),
- properties.Get("EnableDCAtStart", true)
+ properties.Get("EnableDCAtStart", true),
+ properties.Get("PerformanceCounters", ProfilerOptions.DefaultCounters)
);
}
}
diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs
index 43c1fff79d..0c6eb726b7 100644
--- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs
+++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs
@@ -81,19 +81,23 @@ namespace ICSharpCode.Profiler.AddIn
void FinishSession()
{
using (AsynchronousWaitDialog dlg = AsynchronousWaitDialog.ShowWaitDialog(StringParser.Parse("${res:AddIns.Profiler.Messages.PreparingForAnalysis}"), true)) {
- profiler.Dispose();
+ try {
+ profiler.Dispose();
- WorkbenchSingleton.SafeThreadAsyncCall(() => { controlWindow.AllowClose = true; this.controlWindow.Close(); });
- if (database != null) {
- database.WriteTo(writer, progress => !dlg.IsCancelled);
- writer.Close();
- database.Close();
- } else {
- writer.Close();
+ WorkbenchSingleton.SafeThreadAsyncCall(() => { controlWindow.AllowClose = true; this.controlWindow.Close(); });
+ if (database != null) {
+ database.WriteTo(writer, progress => !dlg.IsCancelled);
+ writer.Close();
+ database.Close();
+ } else {
+ writer.Close();
+ }
+
+ if (!dlg.IsCancelled)
+ OnRunFinished(EventArgs.Empty);
+ } catch (Exception ex) {
+ Debug.Print(ex.ToString());
}
-
- if (!dlg.IsCancelled)
- OnRunFinished(EventArgs.Empty);
}
}
diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml
index 6e1a05b5da..fa81e199df 100644
--- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml
+++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml
@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sd="http://icsharpcode.net/sharpdevelop/core"
- xmlns:y="clr-namespace:ICSharpCode.Profiler.Controls;assembly=ICSharpCode.Profiler.Controls"
+ xmlns:controls="clr-namespace:ICSharpCode.Profiler.Controls;assembly=ICSharpCode.Profiler.Controls"
xmlns:local="clr-namespace:ICSharpCode.Profiler.AddIn.Views;assembly=ICSharpCode.Profiler.AddIn">
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
+
-
+
diff --git a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml.cs b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml.cs
index f229c2ab7f..d005d9d97d 100644
--- a/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml.cs
+++ b/src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml.cs
@@ -31,11 +31,10 @@ namespace ICSharpCode.Profiler.AddIn.Views
this.provider = provider;
- this.timeLine.IsEnabled = true;
- this.timeLine.ValuesList.Clear();
- this.timeLine.ValuesList.AddRange(this.provider.DataSets.Select(i => new TimeLineInfo() { value = i.CpuUsage / 100, displayMarker = i.IsFirst }));
- this.timeLine.SelectedStartIndex = 0;
- this.timeLine.SelectedEndIndex = this.timeLine.ValuesList.Count;
+ this.timeLine2.IsEnabled = true;
+ this.timeLine2.Provider = provider;
+ this.timeLine2.SelectedStartIndex = 0;
+ this.timeLine2.SelectedEndIndex = provider.DataSets.Count;
var translation = new SharpDevelopTranslation();
@@ -45,7 +44,7 @@ namespace ICSharpCode.Profiler.AddIn.Views
view.Reporter = new ErrorReporter(UpdateErrorList);
view.Provider = provider;
view.Translation = translation;
- view.SetRange(this.timeLine.SelectedStartIndex, this.timeLine.SelectedEndIndex);
+ view.SetRange(this.timeLine2.SelectedStartIndex, this.timeLine2.SelectedEndIndex);
view.ContextMenuOpening += delegate(object sender, ContextMenuEventArgs e) {
object source = (e.OriginalSource is Shape) ? e.OriginalSource : view;
MenuService.CreateContextMenu(source, "/AddIns/Profiler/QueryView/ContextMenu").IsOpen = true;
@@ -85,9 +84,9 @@ namespace ICSharpCode.Profiler.AddIn.Views
void DoSelectAll()
{
- if (this.timeLine.IsEnabled) {
- this.timeLine.SelectedStartIndex = 0;
- this.timeLine.SelectedEndIndex = this.timeLine.ValuesList.Count;
+ if (this.timeLine2.IsEnabled) {
+ this.timeLine2.SelectedStartIndex = 0;
+ this.timeLine2.SelectedEndIndex = this.timeLine2.Provider.DataSets.Count;
}
}
@@ -99,7 +98,7 @@ namespace ICSharpCode.Profiler.AddIn.Views
void CanDoSelectAll(CanExecuteRoutedEventArgs e)
{
- e.CanExecute = this.timeLine.IsEnabled && this.timeLine.ValuesList.Count > 0;
+ e.CanExecute = this.timeLine2.IsEnabled && this.timeLine2.Provider.DataSets.Count > 0;
}
void CloseButtonClick(object sender, RoutedEventArgs e)
@@ -192,7 +191,7 @@ namespace ICSharpCode.Profiler.AddIn.Views
view.Provider = this.provider;
view.Reporter = new ErrorReporter(UpdateErrorList);
- view.SetRange(this.timeLine.SelectedStartIndex, this.timeLine.SelectedEndIndex);
+ view.SetRange(this.timeLine2.SelectedStartIndex, this.timeLine2.SelectedEndIndex);
view.CurrentQuery = query;
view.ShowQueryItems = true;
diff --git a/src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml b/src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml
index 4aa0cf4fbf..984a4dd880 100644
--- a/src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml
+++ b/src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml
@@ -1,42 +1,31 @@
+ xmlns:local="clr-namespace:ICSharpCode.Profiler.Controls">
-
+
-
+
-
+
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml.cs b/src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml.cs
index 07e81117fa..ec41272a17 100644
--- a/src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml.cs
+++ b/src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Text;
+using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
@@ -11,6 +13,8 @@ using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
+using ICSharpCode.Profiler.Controller.Data;
+
namespace ICSharpCode.Profiler.Controls
{
///
@@ -18,11 +22,77 @@ namespace ICSharpCode.Profiler.Controls
///
public partial class ExtendedTimeLineControl : UserControl
{
+ ProfilingDataProvider provider;
+
public ExtendedTimeLineControl()
{
InitializeComponent();
+
+ this.timeLine.RangeChanged += (sender, e) => { OnRangeChanged(e); };
}
+ public ProfilingDataProvider Provider
+ {
+ get { return this.provider; }
+ set {
+ this.provider = value;
+
+ this.perfCounterList.ItemsSource = this.provider.GetPerformanceCounters();
+ this.perfCounterList.SelectedIndex = 0;
+
+ Update();
+ }
+ }
+ public int SelectedStartIndex {
+ get { return this.timeLine.SelectedStartIndex; }
+ set { this.timeLine.SelectedStartIndex = value; }
+ }
+
+ public int SelectedEndIndex {
+ get { return this.timeLine.SelectedEndIndex; }
+ set { this.timeLine.SelectedEndIndex = value; }
+ }
+
+ public event EventHandler RangeChanged;
+
+ protected virtual void OnRangeChanged(RangeEventArgs e)
+ {
+ if (RangeChanged != null)
+ {
+ RangeChanged(this, e);
+ }
+ }
+
+ void Update()
+ {
+ this.timeLine.ValuesList.Clear();
+
+ var selectedPerformanceCounter = this.perfCounterList.SelectedItem as PerformanceCounterDescriptor;
+
+ if (selectedPerformanceCounter == null)
+ return;
+
+ List segments = new List();
+ var values = this.provider.GetPerformanceCounterValues(this.perfCounterList.SelectedIndex);
+ var markers = this.provider.DataSets.Select(item => item.IsFirst).ToArray();
+
+ this.timeLine.MaxValue = selectedPerformanceCounter.MaxValue ?? (values.Any() ? values.Max() : 0);
+
+ this.maxLabel.Text = (selectedPerformanceCounter.MaxValue ?? (values.Any() ? values.Max() : 0)).ToString("0");
+ this.minLabel.Text = (selectedPerformanceCounter.MinValue ?? (values.Any() ? values.Min() : 0)).ToString("0");
+
+ this.unitLabel.Text = this.timeLine.Unit = selectedPerformanceCounter.Unit;
+
+ for (int i = 0; i < values.Length; i++)
+ segments.Add(new TimeLineSegment() { Value = values[i], TimeOffset = 0, DisplayMarker = markers[i] });
+
+ this.timeLine.ValuesList.AddRange(segments);
+ }
+
+ void PerfCounterListSelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ Update();
+ }
}
}
\ No newline at end of file
diff --git a/src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml b/src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml
index a7b54f1dd8..41d112834c 100644
--- a/src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml
+++ b/src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml
@@ -331,19 +331,19 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
+
diff --git a/src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml.cs b/src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml.cs
index ca36431543..f4ea2d46bc 100644
--- a/src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml.cs
+++ b/src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml.cs
@@ -240,16 +240,6 @@ namespace ICSharpCode.Profiler.Controls
if (!this.isDirty || this.Provider == null)
return;
- if (RangeStart > RangeEnd) {
- int help = RangeStart;
- RangeStart = RangeEnd;
- RangeEnd = help;
- }
-
- if ((RangeEnd < 0 && RangeEnd >= Provider.DataSets.Count) &&
- (RangeStart < 0 && RangeStart >= Provider.DataSets.Count))
- return;
-
if (Provider.DataSets.Count == 0)
return;
diff --git a/src/AddIns/Misc/Profiler/Frontend/Controls/TimeLineControl.cs b/src/AddIns/Misc/Profiler/Frontend/Controls/TimeLineControl.cs
index e925d2a103..29a0ad306c 100644
--- a/src/AddIns/Misc/Profiler/Frontend/Controls/TimeLineControl.cs
+++ b/src/AddIns/Misc/Profiler/Frontend/Controls/TimeLineControl.cs
@@ -12,18 +12,21 @@ using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
+using System.Windows.Input;
using System.Windows.Media;
+using System.Windows.Threading;
namespace ICSharpCode.Profiler.Controls
{
- public struct TimeLineInfo {
- public double value;
- public bool displayMarker;
+ public class TimeLineSegment {
+ public float Value { get; set; }
+ public bool DisplayMarker { get; set; }
+ public long TimeOffset { get; set; }
}
public class TimeLineControl : FrameworkElement
{
- ObservableCollection valuesList;
+ ObservableCollection valuesList;
int selectedStartIndex, selectedEndIndex;
double pieceWidth;
@@ -36,7 +39,13 @@ namespace ICSharpCode.Profiler.Controls
RangeChanged(this, e);
}
}
+
+ public int SelectedPerformanceCounter { get; set; }
+
+ public float MaxValue { get; set; }
+ public string Unit { get; set; }
+
public int SelectedEndIndex
{
get { return selectedEndIndex; }
@@ -59,23 +68,32 @@ namespace ICSharpCode.Profiler.Controls
}
}
- public ObservableCollection ValuesList
+ public ObservableCollection ValuesList
{
get { return valuesList; }
}
public TimeLineControl()
{
- this.valuesList = new ObservableCollection();
+ this.valuesList = new ObservableCollection();
this.valuesList.CollectionChanged += delegate { this.InvalidateMeasure(); this.InvalidateVisual(); };
}
protected override Size MeasureOverride(Size availableSize)
{
- return new Size(1, base.MeasureOverride(availableSize).Height + 10);
+ this.pieceWidth = 25;
+ Size calculatedSize = base.MeasureOverride(availableSize);
+ return new Size(Math.Max(this.pieceWidth * this.valuesList.Count + 1, calculatedSize.Width), calculatedSize.Height + 10);
+ }
+
+ protected override void OnToolTipOpening(ToolTipEventArgs e)
+ {
+ Console.WriteLine("tooltip");
+ base.OnToolTipOpening(e);
}
- const int offset = 15;
+ const int offset = 0;
+ const int offsetFromTop = 40;
protected override void OnRender(DrawingContext drawingContext)
{
@@ -83,23 +101,23 @@ namespace ICSharpCode.Profiler.Controls
if (this.valuesList.Count == 0)
return;
-
- this.pieceWidth = (this.RenderSize.Width - offset) / (double)this.valuesList.Count;
-
- double oldX = 0, oldY = this.RenderSize.Height - offset;
+
+ double oldX = offsetFromTop, oldY = this.RenderSize.Height;
StreamGeometry geometry = new StreamGeometry();
using (StreamGeometryContext ctx = geometry.Open())
{
- ctx.BeginFigure(new Point(offset, this.RenderSize.Height - offset), true, true);
+ ctx.BeginFigure(new Point(0, this.RenderSize.Height), true, true);
List points = new List();
+
+ double maxHeight = this.RenderSize.Height - offsetFromTop;
for (int i = 0; i < this.valuesList.Count; i++)
{
- double x = this.pieceWidth / 2.0 + this.pieceWidth * i + offset;
- double y = this.RenderSize.Height - this.valuesList[i].value * (this.RenderSize.Height - offset) - offset;
+ double x = this.pieceWidth / 2.0 + this.pieceWidth * i;
+ double y = offsetFromTop + (maxHeight - maxHeight * (this.valuesList[i].Value / this.MaxValue));
points.Add(new Point(x, y));
@@ -107,7 +125,7 @@ namespace ICSharpCode.Profiler.Controls
oldY = y;
}
- points.Add(new Point(oldX, this.RenderSize.Height - offset));
+ points.Add(new Point(oldX, offsetFromTop + this.RenderSize.Height));
ctx.PolyLineTo(points, true, true);
}
@@ -116,50 +134,40 @@ namespace ICSharpCode.Profiler.Controls
Brush b = new LinearGradientBrush(Colors.Red, Colors.Orange, 90);
- drawingContext.DrawRectangle(Brushes.White, new Pen(Brushes.White, 1), new Rect(new Point(0, 0), this.RenderSize));
- drawingContext.DrawLine(new Pen(Brushes.Black, 1), new Point(offset / 2, this.RenderSize.Height - offset / 2), new Point(this.RenderSize.Width - offset, this.RenderSize.Height - offset / 2));
- drawingContext.DrawLine(new Pen(Brushes.Black, 1), new Point(offset / 2, 0), new Point(offset / 2, this.RenderSize.Height - offset / 2));
+ drawingContext.DrawRectangle(Brushes.White, new Pen(Brushes.White, 1), new Rect(new Point(0, offsetFromTop), this.RenderSize));
var p = new Pen(Brushes.DarkRed, 2);
- for (int i = 0; i < this.valuesList.Count; i++) {
- drawingContext.DrawLine(new Pen(Brushes.Black, 1),
- new Point(offset + pieceWidth / 2 + pieceWidth * i, this.RenderSize.Height - offset / 4),
- new Point(offset + pieceWidth / 2 + pieceWidth * i, this.RenderSize.Height - (offset / 4 * 3 + 3)));
-
- if (this.valuesList[i].displayMarker) {
- drawingContext.DrawLine(p, new Point(offset + pieceWidth * i, 0),
- new Point(offset + pieceWidth * i, this.RenderSize.Height - offset));
- }
-
- }
-
drawingContext.DrawGeometry(b, new Pen(b, 3), geometry);
for (int i = 0; i < this.valuesList.Count; i++) {
- if (this.valuesList[i].displayMarker)
- drawingContext.DrawLine(p, new Point(offset + pieceWidth * i, 0),
- new Point(offset + pieceWidth * i, this.RenderSize.Height - offset));
+ if (this.valuesList[i].DisplayMarker)
+ drawingContext.DrawLine(p, new Point(pieceWidth * i, offsetFromTop),
+ new Point(pieceWidth * i, this.RenderSize.Height));
}
drawingContext.DrawRectangle(
- new SolidColorBrush(Color.FromArgb(64, Colors.CornflowerBlue.R,
- Colors.CornflowerBlue.G, Colors.CornflowerBlue.B)),
+ new SolidColorBrush(Color.FromArgb(64, Colors.Blue.R, Colors.Blue.G, Colors.Blue.B)),
new Pen(Brushes.Blue, 1),
new Rect(
- new Point(this.selectedStartIndex * this.pieceWidth + offset, 0),
- new Point(this.selectedEndIndex * this.pieceWidth + offset, this.RenderSize.Height - offset)
+ new Point(Math.Min(this.selectedStartIndex, this.selectedEndIndex) * pieceWidth + offset, 0),
+ new Point(Math.Max(this.selectedStartIndex, this.selectedEndIndex) * pieceWidth + offset + pieceWidth, this.RenderSize.Height - offset)
)
);
}
protected override void OnMouseUp(System.Windows.Input.MouseButtonEventArgs e)
{
- int index = TransformToIndex(e.GetPosition(this));
- index = (index < 0) ? 0 : index;
- index = (index > this.valuesList.Count) ? this.valuesList.Count : index;
+ bool valid;
+ int index = TransformToIndex(e.GetPosition(this), out valid);
+
+ if (index < this.selectedStartIndex) {
+ this.selectedEndIndex = this.selectedStartIndex;
+ this.selectedStartIndex = index;
+ } else
+ this.selectedEndIndex = index;
- this.selectedEndIndex = index;
+ Console.WriteLine("start: {0} end: {1} count: {2}", SelectedStartIndex, SelectedEndIndex, valuesList.Count);
this.InvalidateMeasure();
this.InvalidateVisual();
@@ -169,43 +177,128 @@ namespace ICSharpCode.Profiler.Controls
protected override void OnMouseDown(System.Windows.Input.MouseButtonEventArgs e)
{
this.CaptureMouse();
-
- int index = TransformToIndex(e.GetPosition(this));
- index = (index < 0) ? 0 : index;
- index = (index > this.valuesList.Count) ? this.valuesList.Count : index;
+ bool valid;
+ int index = TransformToIndex(e.GetPosition(this), out valid);
this.selectedStartIndex = this.selectedEndIndex = index;
+
this.InvalidateMeasure();
this.InvalidateVisual();
}
- protected override void OnMouseMove(System.Windows.Input.MouseEventArgs e)
+ ToolTip tooltip;
+
+ void HandleMovement(MouseEventArgs e)
{
- int index = TransformToIndex(e.GetPosition(this));
+ bool valid;
+ int index = TransformToIndex(e.GetPosition(this), out valid);
- index = (index < 0) ? 0 : index;
- index = (index > this.valuesList.Count) ? this.valuesList.Count : index;
-
- if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
- {
+ if (e.LeftButton == MouseButtonState.Pressed) {
this.selectedEndIndex = index;
this.InvalidateMeasure();
this.InvalidateVisual();
+ } else if (tooltip == null && valid) {
+ tooltip = new ToolTip();
+ tooltip.Content = "Value: " + this.valuesList[index].Value.ToString("0.00") + " " + this.Unit;
+ tooltip .IsOpen = true;
}
}
- protected override void OnLostMouseCapture(System.Windows.Input.MouseEventArgs e)
+ protected override void OnLostMouseCapture(MouseEventArgs e)
{
base.OnLostMouseCapture(e);
- if (this.selectedEndIndex == this.selectedStartIndex)
- this.selectedEndIndex++;
OnRangeChanged(new RangeEventArgs(this.selectedStartIndex, this.selectedEndIndex));
- Debug.Print("lost capture");
}
- private int TransformToIndex(Point point)
+ int TransformToIndex(Point point, out bool valid)
+ {
+ int value = (int)Math.Floor(point.X / this.pieceWidth);
+ valid = (0 <= value && value <= this.valuesList.Count - 1);
+ return Math.Min(Math.Max(0, value), this.valuesList.Count - 1);
+ }
+
+ #region MouseHover
+ ///
+ /// The MouseHover event.
+ ///
+ public static readonly RoutedEvent MouseHoverEvent =
+ EventManager.RegisterRoutedEvent("MouseHover", RoutingStrategy.Bubble,
+ typeof(MouseEventHandler), typeof(TimeLineControl));
+
+ ///
+ /// The MouseHoverStopped event.
+ ///
+ public static readonly RoutedEvent MouseHoverStoppedEvent =
+ EventManager.RegisterRoutedEvent("MouseHoverStopped", RoutingStrategy.Bubble,
+ typeof(MouseEventHandler), typeof(TimeLineControl));
+
+ ///
+ /// Occurs when the mouse has hovered over a fixed location for some time.
+ ///
+ public event MouseEventHandler MouseHover {
+ add { AddHandler(MouseHoverEvent, value); }
+ remove { RemoveHandler(MouseHoverEvent, value); }
+ }
+
+ ///
+ /// Occurs when the mouse had previously hovered but now started moving again.
+ ///
+ public event MouseEventHandler MouseHoverStopped {
+ add { AddHandler(MouseHoverStoppedEvent, value); }
+ remove { RemoveHandler(MouseHoverStoppedEvent, value); }
+ }
+
+ DispatcherTimer mouseHoverTimer;
+ Point mouseHoverStartPoint;
+ MouseEventArgs mouseHoverLastEventArgs;
+
+ ///
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ base.OnMouseMove(e);
+ Point newPosition = e.GetPosition(this);
+ Vector mouseMovement = mouseHoverStartPoint - newPosition;
+ if (Math.Abs(mouseMovement.X) > SystemParameters.MouseHoverWidth
+ || Math.Abs(mouseMovement.Y) > SystemParameters.MouseHoverHeight)
+ {
+ StopHovering();
+ mouseHoverStartPoint = newPosition;
+ mouseHoverLastEventArgs = e;
+ mouseHoverTimer = new DispatcherTimer(SystemParameters.MouseHoverTime, DispatcherPriority.Background,
+ OnMouseHoverTimerElapsed, this.Dispatcher);
+ mouseHoverTimer.Start();
+ }
+
+ HandleMovement(e);
+ // do not set e.Handled - allow others to also handle MouseMove
+ }
+
+ ///
+ protected override void OnMouseLeave(MouseEventArgs e)
+ {
+ base.OnMouseLeave(e);
+ StopHovering();
+ // do not set e.Handled - allow others to also handle MouseLeave
+ }
+
+ void StopHovering()
+ {
+ if (mouseHoverTimer != null) {
+ mouseHoverTimer.Stop();
+ mouseHoverTimer = null;
+ }
+
+ if (this.tooltip != null) {
+ this.tooltip.IsOpen = false;
+ this.tooltip = null;
+ }
+ }
+
+ void OnMouseHoverTimerElapsed(object sender, EventArgs e)
{
- return (int)Math.Round(point.X / this.pieceWidth);
+ mouseHoverTimer.Stop();
+ mouseHoverTimer = null;
}
+ #endregion
}
}
diff --git a/src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/CallTreeNodeStub.cs b/src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/CallTreeNodeStub.cs
index 51fca5c303..65dd443786 100644
--- a/src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/CallTreeNodeStub.cs
+++ b/src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/CallTreeNodeStub.cs
@@ -135,7 +135,6 @@ namespace Profiler.Tests.Controller.Data
public class DataSetStub : IProfilingDataSet
{
- public double CpuUsage { get; set; }
public bool IsFirst { get; set; }
public CallTreeNode RootNode { get; set; }
}
diff --git a/src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/LinqTests.cs b/src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/LinqTests.cs
index 4d465e6425..f537ba746f 100644
--- a/src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/LinqTests.cs
+++ b/src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/LinqTests.cs
@@ -47,7 +47,7 @@ namespace Profiler.Tests.Controller.Data
}
}
};
- writer.WriteDataSet(new DataSetStub { CpuUsage = 0.3, IsFirst = true, RootNode = dataSet });
+ writer.WriteDataSet(new DataSetStub { IsFirst = true, RootNode = dataSet });
dataSet = new CallTreeNodeStub {
NameMappingValue = method0,
IsActiveAtStartValue = true,
@@ -65,7 +65,7 @@ namespace Profiler.Tests.Controller.Data
}
}
};
- writer.WriteDataSet(new DataSetStub { CpuUsage = 0.4, IsFirst = false, RootNode = dataSet });
+ writer.WriteDataSet(new DataSetStub { IsFirst = false, RootNode = dataSet });
dataSet = new CallTreeNodeStub {
NameMappingValue = method0,
IsActiveAtStartValue = true,
@@ -85,7 +85,7 @@ namespace Profiler.Tests.Controller.Data
}
}
};
- writer.WriteDataSet(new DataSetStub { CpuUsage = 0.1, IsFirst = false, RootNode = dataSet });
+ writer.WriteDataSet(new DataSetStub { IsFirst = false, RootNode = dataSet });
writer.Close();
}
provider = ProfilingDataSQLiteProvider.UpgradeFromOldVersion(databaseFileName);
@@ -102,9 +102,6 @@ namespace Profiler.Tests.Controller.Data
public void TestDataSets()
{
Assert.AreEqual(3, provider.DataSets.Count);
- Assert.AreEqual(0.3, provider.DataSets[0].CpuUsage);
- Assert.AreEqual(0.4, provider.DataSets[1].CpuUsage);
- Assert.AreEqual(0.1, provider.DataSets[2].CpuUsage);
Assert.IsTrue(provider.DataSets[0].IsFirst);
Assert.IsFalse(provider.DataSets[1].IsFirst);