Browse Source

- implemented PerformanceCounterDescriptor

- added ExtendedTimeLineControl
- updated SDPS to 1.2

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5049 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Siegfried Pammer 16 years ago
parent
commit
3bc647a0f3
  1. 5
      src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs
  2. 2
      src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataWriter.cs
  3. 82
      src/AddIns/Misc/Profiler/Controller/Data/PerformanceCounterDescriptor.cs
  4. 5
      src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataProvider.cs
  5. 85
      src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs
  6. 81
      src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs
  7. 27
      src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs
  8. 12
      src/AddIns/Misc/Profiler/Controller/Data/UnitTestWriter.cs
  9. 12
      src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs
  10. 46
      src/AddIns/Misc/Profiler/Controller/Profiler.cs
  11. 22
      src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs
  12. 50
      src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml
  13. 6
      src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionPanels/OptionWrapper.cs
  14. 28
      src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs
  15. 20
      src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml
  16. 21
      src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml.cs
  17. 37
      src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml
  18. 70
      src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml.cs
  19. 14
      src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml
  20. 10
      src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml.cs
  21. 209
      src/AddIns/Misc/Profiler/Frontend/Controls/TimeLineControl.cs
  22. 1
      src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/CallTreeNodeStub.cs
  23. 9
      src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/LinqTests.cs

5
src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataSet.cs

@ -22,11 +22,6 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -22,11 +22,6 @@ namespace ICSharpCode.Profiler.Controller.Data
/// </remarks>
public interface IProfilingDataSet
{
/// <summary>
/// Gets the percent of CPU power used by the profilee at a certain point.
/// </summary>
double CpuUsage { get; }
/// <summary>
/// Gets whether this dataset is the first dataset of a profiling run.
/// </summary>

2
src/AddIns/Misc/Profiler/Controller/Data/IProfilingDataWriter.cs

@ -29,6 +29,8 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -29,6 +29,8 @@ namespace ICSharpCode.Profiler.Controller.Data
/// </summary>
void WriteMappings(IEnumerable<NameMapping> mappings);
void WritePerformanceCounterData(IEnumerable<PerformanceCounterDescriptor> counters);
/// <summary>
/// Closes and disposes the underlying data structure.
/// </summary>

82
src/AddIns/Misc/Profiler/Controller/Data/PerformanceCounterDescriptor.cs

@ -6,13 +6,83 @@ @@ -6,13 +6,83 @@
// </file>
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<float> 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<float>();
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;
}
}
}

5
src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataProvider.cs

@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
// </file>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@ -75,5 +76,9 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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);
}
}

85
src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs

@ -56,6 +56,8 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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<PerformanceCounterDescriptor> list = new List<PerformanceCounterDescriptor>();
var reader = cmd.ExecuteReader();
while (reader.Read()) {
list.Add(
new PerformanceCounterDescriptor(
reader.GetString(0),
reader.IsDBNull(1) ? null : new Nullable<float>(reader.GetFloat(1)),
reader.IsDBNull(2) ? null : new Nullable<float>(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<float> list = new List<float>();
var reader = cmd.ExecuteReader();
while (reader.Read())
list.Add(reader.GetFloat(0));
return list.ToArray();
}
}
Expression<Func<SingleCall, bool>> DataSetFilter(int startIndex, int endIndex)
{
return c => startIndex <= c.DataSetID && c.DataSetID <= endIndex;

81
src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs

@ -99,12 +99,11 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -299,5 +301,48 @@ namespace ICSharpCode.Profiler.Controller.Data
isDisposed = true;
}
public void WritePerformanceCounterData(IEnumerable<PerformanceCounterDescriptor> 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();
}
}
}
}

27
src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs

@ -37,13 +37,18 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -37,13 +37,18 @@ namespace ICSharpCode.Profiler.Controller.Data
get { return nameMappings.Select(i => i.Value).ToList().AsReadOnly(); }
}
List<PerformanceCounterDescriptor> counters = new List<PerformanceCounterDescriptor>();
public ReadOnlyCollection<PerformanceCounterDescriptor> 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 @@ -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 @@ -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 @@ -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<PerformanceCounterDescriptor> counters)
{
this.database.counters.AddRange(counters);
}
}
#endregion
@ -150,14 +160,14 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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 @@ -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);
}
/// <summary>
@ -219,6 +228,8 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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);

12
src/AddIns/Misc/Profiler/Controller/Data/UnitTestWriter.cs

@ -38,15 +38,12 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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 @@ -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 @@ -103,5 +100,10 @@ namespace ICSharpCode.Profiler.Controller.Data
{
this.targetWriter.Close();
}
public void WritePerformanceCounterData(IEnumerable<PerformanceCounterDescriptor> counters)
{
this.targetWriter.WritePerformanceCounterData(counters);
}
}
}

12
src/AddIns/Misc/Profiler/Controller/Data/UnmanagedProfilingDataSet.cs

@ -23,7 +23,6 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -23,7 +23,6 @@ namespace ICSharpCode.Profiler.Controller.Data
TargetProcessPointer nativeStartPosition;
TargetProcessPointer nativeRootFuncInfoPosition;
bool isDisposed;
double cpuUsage;
bool isFirst;
bool is64Bit;
@ -60,14 +59,6 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -60,14 +59,6 @@ namespace ICSharpCode.Profiler.Controller.Data
get { return startPtr; }
}
/// <summary>
/// Gets the percentage of the CPU usage by the profilee.
/// </summary>
public double CpuUsage
{
get { return cpuUsage; }
}
internal abstract unsafe void *TranslatePointer(TargetProcessPointer ptr);
/// <summary>
@ -83,10 +74,9 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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;

46
src/AddIns/Misc/Profiler/Controller/Profiler.cs

@ -84,6 +84,8 @@ namespace ICSharpCode.Profiler.Controller @@ -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;
/// <summary>
/// The Guid of the CProfiler class in the Hook.
/// </summary>
@ -101,8 +103,7 @@ namespace ICSharpCode.Profiler.Controller @@ -101,8 +103,7 @@ namespace ICSharpCode.Profiler.Controller
UnmanagedCircularBuffer nativeToManagedBuffer;
IProfilingDataWriter dataWriter;
Dictionary<string, PerformanceCounter> performanceCounters;
PerformanceCounter cpuUsageCounter;
PerformanceCounterDescriptor[] performanceCounters;
/// <summary>
/// The currently used data provider.
@ -118,7 +119,7 @@ namespace ICSharpCode.Profiler.Controller @@ -118,7 +119,7 @@ namespace ICSharpCode.Profiler.Controller
// before the profilee can open it.
MemoryMappedFile file;
ProfilerOptions profilerOptions = new ProfilerOptions();
ProfilerOptions profilerOptions;
/// <summary>
/// Gets all settings used by this profiler instance.
@ -248,7 +249,11 @@ namespace ICSharpCode.Profiler.Controller @@ -248,7 +249,11 @@ namespace ICSharpCode.Profiler.Controller
this.is64Bit = DetectBinaryType.RunsAs64Bit(info.FileName);
this.profilerOutput = new StringBuilder();
this.performanceCounters = new Dictionary<string, PerformanceCounter>();
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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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;
}

22
src/AddIns/Misc/Profiler/Controller/ProfilerOptions.cs

@ -6,6 +6,11 @@ @@ -6,6 +6,11 @@
// </file>
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 @@ -19,12 +24,23 @@ namespace ICSharpCode.Profiler.Controller
/// </summary>
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; }
}
/// <summary>
/// Gets whether .NET internal calls are profiled or not.
/// </summary>
@ -63,20 +79,22 @@ namespace ICSharpCode.Profiler.Controller @@ -63,20 +79,22 @@ namespace ICSharpCode.Profiler.Controller
/// <summary>
/// Creates new ProfilerOptions using the selected settings.
/// </summary>
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<PerformanceCounterDescriptor> counters)
{
this.enableDC = enableDC;
this.sharedMemorySize = sharedMemorySize;
this.dotNotProfileDotNetInternals = profileDotNetInternals;
this.combineRecursiveFunction = combineRecursiveFunction;
this.enableDCAtStart = enableDCAtStart;
this.counters = counters.ToArray();
}
/// <summary>
/// Creates default ProfilerOptions.
/// </summary>
public ProfilerOptions()
: this(true, DefaultSharedMemorySize, false, false, true)
: this(true, DefaultSharedMemorySize, false, false, true, DefaultCounters)
{
}
}

50
src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Dialogs/ProfilerControlWindow.xaml

@ -1,30 +1,26 @@ @@ -1,30 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
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}"
WindowStyle="ToolWindow" Closing="WindowClosing"
Topmost="True"
Height="60"
Width="170">
<Grid>
<Grid.RowDefinitions>
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<ToolBar
Grid.Column="0"
Grid.Row="0"
HorizontalAlignment="Left"
VerticalAlignment="Stretch">
<ToggleButton
Content="{sd:Localize AddIns.Profiler.ProfilerControlWindow.CollectData}"
x:Name="collectData"
Checked="CollectDataChecked"
Unchecked="CollectDataUnchecked" />
<Button
Content="{sd:Localize AddIns.Profiler.ProfilerControlWindow.Shutdown}"
x:Name="shutdown"
Click="ShutdownClick" />
</ToolBar>
</Grid>
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">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Menu>
<ToggleButton
Content="{sd:Localize AddIns.Profiler.ProfilerControlWindow.CollectData}"
x:Name="collectData"
Checked="CollectDataChecked"
Unchecked="CollectDataUnchecked" />
<Button
Content="{sd:Localize AddIns.Profiler.ProfilerControlWindow.Shutdown}"
x:Name="shutdown"
Click="ShutdownClick" />
</Menu>
</Grid>
</Window>

6
src/AddIns/Misc/Profiler/Frontend/AddIn/Src/OptionPanels/OptionWrapper.cs

@ -4,9 +4,10 @@ @@ -4,9 +4,10 @@
// <owner name="Siegfried Pammer" email="siegfriedpammer@gmail.com"/>
// <version>$Revision$</version>
// </file>
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 @@ -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)
);
}
}

28
src/AddIns/Misc/Profiler/Frontend/AddIn/Src/ProfilerRunner.cs

@ -81,19 +81,23 @@ namespace ICSharpCode.Profiler.AddIn @@ -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();
WorkbenchSingleton.SafeThreadAsyncCall(() => { controlWindow.AllowClose = true; this.controlWindow.Close(); });
if (database != null) {
database.WriteTo(writer, progress => !dlg.IsCancelled);
writer.Close();
database.Close();
} else {
writer.Close();
}
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();
}
if (!dlg.IsCancelled)
OnRunFinished(EventArgs.Empty);
if (!dlg.IsCancelled)
OnRunFinished(EventArgs.Empty);
} catch (Exception ex) {
Debug.Print(ex.ToString());
}
}
}

20
src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml

@ -2,7 +2,7 @@ @@ -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">
<UserControl.Resources>
<Style x:Key="CloseButton" TargetType="{x:Type Button}">
@ -24,10 +24,14 @@ @@ -24,10 +24,14 @@
</Style>
</UserControl.Resources>
<Grid>
<GridSplitter Width="0" Margin="0,202.5,0,204.5" />
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" Margin="0,30,0,0" Height="90.5" HorizontalAlignment="Stretch" VerticalAlignment="Top">
<y:TimeLineControl x:Name="timeLine" RangeChanged="TimeLineRangeChanged" />
</ScrollViewer>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.Row="1">
<controls:ExtendedTimeLineControl x:Name="timeLine2" RangeChanged="TimeLineRangeChanged" />
</StackPanel>
<ToolBar Height="27" x:Name="toolBar1" VerticalAlignment="Top">
<Menu>
<MenuItem x:Name="mnuQueryHistory" Header="{sd:Localize AddIns.Profiler.ProfilingView.QueryHistoryText}">
@ -36,12 +40,12 @@ @@ -36,12 +40,12 @@
</MenuItem>
</Menu>
</ToolBar>
<TabControl x:Name="tabView" HorizontalAlignment="Stretch" Margin="0,120,0,0" SelectionChanged="TabViewSelectionChanged">
<TabControl x:Name="tabView" HorizontalAlignment="Stretch" Grid.Row="2" SelectionChanged="TabViewSelectionChanged">
<TabItem Header="{sd:Localize AddIns.Profiler.ProfilingView.OverviewTabText}">
<y:QueryView x:Name="treeView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowQueryItems="False" CurrentQuery="from t in Threads select t" IsQueryModifiable="False" />
<controls:QueryView x:Name="treeView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowQueryItems="False" CurrentQuery="from t in Threads select t" IsQueryModifiable="False" />
</TabItem>
<TabItem Header="{sd:Localize AddIns.Profiler.ProfilingView.Top20TabText}">
<y:QueryView x:Name="top20View" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowQueryItems="False" CurrentQuery="(from f in Functions where f.IsUserCode orderby f.CpuCyclesSpent descending select f).Take(20)" IsQueryModifiable="False" />
<controls:QueryView x:Name="top20View" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowQueryItems="False" CurrentQuery="(from f in Functions where f.IsUserCode orderby f.CpuCyclesSpent descending select f).Take(20)" IsQueryModifiable="False" />
</TabItem>
<TabItem x:Name="dummyTab" />
</TabControl>

21
src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml.cs

@ -31,11 +31,10 @@ namespace ICSharpCode.Profiler.AddIn.Views @@ -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 @@ -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 @@ -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 @@ -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 @@ -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;

37
src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml

@ -1,42 +1,31 @@ @@ -1,42 +1,31 @@
<UserControl x:Class="ICSharpCode.Profiler.Controls.ExtendedTimeLineControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:y="clr-namespace:ICSharpCode.Profiler.Controls">
xmlns:local="clr-namespace:ICSharpCode.Profiler.Controls">
<DockPanel>
<DockPanel Margin="3,0">
<TextBlock Text="Performance Counters" DockPanel.Dock="Top" />
<ListBox x:Name="perfCounterList" ItemsSource="{Binding}" />
<ListBox x:Name="perfCounterList" SelectionChanged="PerfCounterListSelectionChanged" />
</DockPanel>
<Grid Margin="3,0">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="20" />
<RowDefinition Height="Auto" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Time" />
<TextBlock Text="Time" />
<TextBlock Grid.Row="1" Text="Events" />
<TextBlock Grid.Row="2" Text="Graph" />
<DockPanel Grid.Row="2">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock Text="Graph " />
<TextBlock x:Name="maxLabel" HorizontalAlignment="Right" />
</StackPanel>
<TextBlock x:Name="minLabel" DockPanel.Dock="Bottom" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
<TextBlock x:Name="unitLabel" VerticalAlignment="Center" HorizontalAlignment="Right" />
</DockPanel>
</Grid>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" Margin="3,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="20" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<TextBlock Text="Test" />
<TextBlock Text="a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa" />
<TextBlock Text="a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa" />
<TextBlock Text="a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa" />
<TextBlock Text="a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa" />
<TextBlock Text="a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa" />
<TextBlock Text="a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa" />
<TextBlock Text="a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa aaaaaaaaa" />
</StackPanel>
<y:TimeLineControl x:Name="t1" Grid.Row="1" />
<TextBlock Grid.Row="2" Text="Test" />
</Grid>
<local:TimeLineControl x:Name="timeLine" />
</ScrollViewer>
</DockPanel>
</UserControl>

70
src/AddIns/Misc/Profiler/Frontend/Controls/ExtendedTimeLineControl.xaml.cs

@ -1,6 +1,8 @@ @@ -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; @@ -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
{
/// <summary>
@ -18,11 +22,77 @@ namespace ICSharpCode.Profiler.Controls @@ -18,11 +22,77 @@ namespace ICSharpCode.Profiler.Controls
/// </summary>
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<RangeEventArgs> 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<TimeLineSegment> segments = new List<TimeLineSegment>();
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();
}
}
}

14
src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml

@ -331,19 +331,19 @@ @@ -331,19 +331,19 @@
</Style>
</ListView.ItemContainerStyle>
</local:TreeListView>
<ToolBar Height="26" Grid.Row="0" Grid.ColumnSpan="2">
<Label Content="{Binding Translation.SearchLabelText, ElementName=queryView}" />
<TextBox Name="txtSearch" Width="150" KeyDown="txtSearchKeyDown" />
<CheckBox Content="{Binding Translation.ShowQueryBarText, ElementName=queryView}" IsChecked="{Binding ShowQueryItems}" />
<Button Content="{Binding Translation.ExpandHotPathSubtreeText, ElementName=queryView}" Name="btnExpandHotPathSubtree" Click="BtnExpandHotPathSubtreeClick" />
<ComboBox Name="visibleColumnsSelection" Style="{StaticResource styleColumnsComboBox}">
<StackPanel Grid.Row="0" Grid.ColumnSpan="2" Orientation="Horizontal">
<Label Content="{Binding Translation.SearchLabelText, ElementName=queryView}" Margin="3" VerticalAlignment="Center" />
<TextBox Name="txtSearch" Width="150" KeyDown="txtSearchKeyDown" VerticalAlignment="Center" />
<ToggleButton Content="{Binding Translation.ShowQueryBarText, ElementName=queryView}" IsChecked="{Binding ShowQueryItems}" Margin="3" VerticalAlignment="Center" />
<Button Content="{Binding Translation.ExpandHotPathSubtreeText, ElementName=queryView}" Name="btnExpandHotPathSubtree" Click="BtnExpandHotPathSubtreeClick" Margin="3" VerticalAlignment="Center" />
<ComboBox Name="visibleColumnsSelection" Style="{StaticResource styleColumnsComboBox}" Margin="3" VerticalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsVisible, Mode=TwoWay}" Content="{Binding Column.Header}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</ToolBar>
</StackPanel>
<DockPanel Name="queryPanel" Visibility="{Binding ShowQueryItems, Converter={StaticResource VisibilityConverter}}" Grid.Row="1" Grid.ColumnSpan="2">
<Button Name="btnExecuteQuery" DockPanel.Dock="Right" Click="btnExecuteQueryClick" Content="{Binding Translation.ExecuteQueryText, ElementName=queryView}" />
<TextBox Name="txtQuery" TextChanged="txtQueryTextChanged" KeyDown="txtQueryKeyDown" />

10
src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml.cs

@ -240,16 +240,6 @@ namespace ICSharpCode.Profiler.Controls @@ -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;

209
src/AddIns/Misc/Profiler/Frontend/Controls/TimeLineControl.cs

@ -12,18 +12,21 @@ using System.Diagnostics; @@ -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<TimeLineInfo> valuesList;
ObservableCollection<TimeLineSegment> valuesList;
int selectedStartIndex, selectedEndIndex;
double pieceWidth;
@ -37,6 +40,12 @@ namespace ICSharpCode.Profiler.Controls @@ -37,6 +40,12 @@ namespace ICSharpCode.Profiler.Controls
}
}
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 @@ -59,23 +68,32 @@ namespace ICSharpCode.Profiler.Controls
}
}
public ObservableCollection<TimeLineInfo> ValuesList
public ObservableCollection<TimeLineSegment> ValuesList
{
get { return valuesList; }
}
public TimeLineControl()
{
this.valuesList = new ObservableCollection<TimeLineInfo>();
this.valuesList = new ObservableCollection<TimeLineSegment>();
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)
{
@ -84,22 +102,22 @@ namespace ICSharpCode.Profiler.Controls @@ -84,22 +102,22 @@ 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<Point> points = new List<Point>();
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 @@ -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 @@ -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);
this.selectedEndIndex = index;
if (index < this.selectedStartIndex) {
this.selectedEndIndex = this.selectedStartIndex;
this.selectedStartIndex = index;
} else
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 @@ -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)
{
int index = TransformToIndex(e.GetPosition(this));
ToolTip tooltip;
index = (index < 0) ? 0 : index;
index = (index > this.valuesList.Count) ? this.valuesList.Count : index;
void HandleMovement(MouseEventArgs e)
{
bool valid;
int index = TransformToIndex(e.GetPosition(this), out valid);
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
/// <summary>
/// The MouseHover event.
/// </summary>
public static readonly RoutedEvent MouseHoverEvent =
EventManager.RegisterRoutedEvent("MouseHover", RoutingStrategy.Bubble,
typeof(MouseEventHandler), typeof(TimeLineControl));
/// <summary>
/// The MouseHoverStopped event.
/// </summary>
public static readonly RoutedEvent MouseHoverStoppedEvent =
EventManager.RegisterRoutedEvent("MouseHoverStopped", RoutingStrategy.Bubble,
typeof(MouseEventHandler), typeof(TimeLineControl));
/// <summary>
/// Occurs when the mouse has hovered over a fixed location for some time.
/// </summary>
public event MouseEventHandler MouseHover {
add { AddHandler(MouseHoverEvent, value); }
remove { RemoveHandler(MouseHoverEvent, value); }
}
/// <summary>
/// Occurs when the mouse had previously hovered but now started moving again.
/// </summary>
public event MouseEventHandler MouseHoverStopped {
add { AddHandler(MouseHoverStoppedEvent, value); }
remove { RemoveHandler(MouseHoverStoppedEvent, value); }
}
DispatcherTimer mouseHoverTimer;
Point mouseHoverStartPoint;
MouseEventArgs mouseHoverLastEventArgs;
/// <inheritdoc/>
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
}
/// <inheritdoc/>
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
}
}

1
src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/CallTreeNodeStub.cs

@ -135,7 +135,6 @@ namespace Profiler.Tests.Controller.Data @@ -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; }
}

9
src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/LinqTests.cs

@ -47,7 +47,7 @@ namespace Profiler.Tests.Controller.Data @@ -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 @@ -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 @@ -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 @@ -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);

Loading…
Cancel
Save