Browse Source

Added support for query options.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5031 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
6e2178b5b2
  1. 4
      src/AddIns/Misc/Profiler/Controller/Data/CallTreeNode.cs
  2. 7
      src/AddIns/Misc/Profiler/Controller/Data/Linq/KnownMembers.cs
  3. 13
      src/AddIns/Misc/Profiler/Controller/Data/Linq/QueryAst.cs
  4. 96
      src/AddIns/Misc/Profiler/Controller/Data/Linq/SQLiteQueryProvider.cs
  5. 2
      src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataProvider.cs
  6. 2
      src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs
  7. 77
      src/AddIns/Misc/Profiler/Controller/ExtensionMethods.cs
  8. 14
      src/AddIns/Misc/Profiler/Controller/Interprocess/UnmanagedCircularBuffer.cs
  9. 4
      src/AddIns/Misc/Profiler/Controller/Profiler.cs
  10. 50
      src/AddIns/Misc/Profiler/Frontend/Controls/QueryView.xaml.cs
  11. 3
      src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/Data/LinqTests.cs
  12. 5
      src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/UnmanagedCircularBufferTest.cs
  13. 1
      src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Profiler.Tests.csproj

4
src/AddIns/Misc/Profiler/Controller/Data/CallTreeNode.cs

@ -109,9 +109,9 @@ namespace ICSharpCode.Profiler.Controller.Data
} }
/// <summary> /// <summary>
/// Determines whether this node is a thread node or not. /// Determines whether this node is a thread node.
/// </summary> /// </summary>
public bool IsThread { public virtual bool IsThread {
get { return this.Name.StartsWith("Thread#", StringComparison.Ordinal); } get { return this.Name.StartsWith("Thread#", StringComparison.Ordinal); }
} }

7
src/AddIns/Misc/Profiler/Controller/Data/Linq/KnownMembers.cs

@ -16,6 +16,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
static class KnownMembers static class KnownMembers
{ {
public static readonly PropertyInfo CallTreeNode_IsUserCode = PropertyOf((CallTreeNode c) => c.IsUserCode); public static readonly PropertyInfo CallTreeNode_IsUserCode = PropertyOf((CallTreeNode c) => c.IsUserCode);
public static readonly PropertyInfo CallTreeNode_IsThread = PropertyOf((CallTreeNode c) => c.IsThread);
public static readonly PropertyInfo CallTreeNode_CpuCyclesSpent = PropertyOf((CallTreeNode c) => c.CpuCyclesSpent); public static readonly PropertyInfo CallTreeNode_CpuCyclesSpent = PropertyOf((CallTreeNode c) => c.CpuCyclesSpent);
public static readonly PropertyInfo CallTreeNode_CallCount = PropertyOf((CallTreeNode c) => c.CallCount); public static readonly PropertyInfo CallTreeNode_CallCount = PropertyOf((CallTreeNode c) => c.CallCount);
public static readonly PropertyInfo CallTreeNode_NameMapping = PropertyOf((CallTreeNode c) => c.NameMapping); public static readonly PropertyInfo CallTreeNode_NameMapping = PropertyOf((CallTreeNode c) => c.NameMapping);
@ -29,8 +30,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
public static readonly MethodInfo QueryableOfCallTreeNode_Take = MethodOf((IQueryable<CallTreeNode> q) => q.Take(1)); public static readonly MethodInfo QueryableOfCallTreeNode_Take = MethodOf((IQueryable<CallTreeNode> q) => q.Take(1));
public static readonly MethodInfo Queryable_OrderBy = MethodOf((IQueryable<CallTreeNode> q) => q.OrderBy(x => x)).GetGenericMethodDefinition(); public static readonly MethodInfo Queryable_OrderBy = MethodOf((IQueryable<CallTreeNode> q) => q.OrderBy(x => x)).GetGenericMethodDefinition();
public static readonly MethodInfo QueryableOfCallTreeNode_OrderByDesc = MethodOf((IOrderedQueryable<CallTreeNode> q) => q.OrderByDescending(x => default(int))); public static readonly MethodInfo QueryableOfCallTreeNode_OrderByDesc = MethodOf((IOrderedQueryable<CallTreeNode> q) => q.OrderByDescending(x => default(int)));
public static readonly MethodInfo Merge = MethodOf((IQueryable<CallTreeNode> q) => q.Merge());
public static readonly MethodInfo String_StartsWith = MethodOf((string s) => s.StartsWith(s, default(StringComparison))); public static readonly MethodInfo String_StartsWith = MethodOf((string s) => s.StartsWith(s, default(StringComparison)));
public static readonly MethodInfo Queryable_Merge = MethodOf((IQueryable<CallTreeNode> q) => q.Merge());
public static readonly MethodInfo Queryable_MergeByName = MethodOf((IQueryable<CallTreeNode> q) => q.MergeByName());
public static readonly MethodInfo Queryable_WithQueryLog = MethodOf((IQueryable<CallTreeNode> q) => q.WithQueryLog(null));
public static readonly MethodInfo Like = MethodOf(() => LikeImpl("", "")); public static readonly MethodInfo Like = MethodOf(() => LikeImpl("", ""));
public static readonly MethodInfo Glob = MethodOf(() => GlobImpl("", "")); public static readonly MethodInfo Glob = MethodOf(() => GlobImpl("", ""));

13
src/AddIns/Misc/Profiler/Controller/Data/Linq/QueryAst.cs

@ -105,12 +105,19 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
return expression + " AS " + newName; return expression + " AS " + newName;
} }
public virtual IQueryable<CallTreeNode> Execute(SQLiteQueryProvider provider) public IQueryable<CallTreeNode> Execute(SQLiteQueryProvider provider, QueryExecutionOptions options)
{ {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
BuildSql(b, new SqlQueryContext(provider)); BuildSql(b, new SqlQueryContext(provider));
Console.WriteLine(b.ToString()); if (options.HasLoggers)
return provider.RunSQLNodeList(b.ToString()).AsQueryable(); options.WriteLogLine(b.ToString());
Stopwatch w = Stopwatch.StartNew();
IList<CallTreeNode> result = provider.RunSQLNodeList(b.ToString());
w.Stop();
if (options.HasLoggers) {
options.WriteLogLine("Query returned " + result.Count + " rows in " + w.Elapsed);
}
return result.AsQueryable();
} }
} }

96
src/AddIns/Misc/Profiler/Controller/Data/Linq/SQLiteQueryProvider.cs

@ -7,6 +7,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
@ -82,8 +84,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
Translation rules for expression importer: Translation rules for expression importer:
Any valid expressions (as defined in 'valid expressions in QueryAst nodes') are copied over directly. Any valid expressions (as defined in 'valid expressions in QueryAst nodes') are copied over directly.
Moreover, these expressions are be converted into valid expressions: Moreover, these expressions are converted into valid expressions:
c.IsUserCode -> c.NameMapping.ID > 0 c.IsUserCode -> c.NameMapping.ID > 0
c.IsThread -> Glob(c.NameMapping.Name, "Thread#*")
s.StartsWith(constantString, StringComparison.Ordinal) -> Glob(s, constantString + "*"); s.StartsWith(constantString, StringComparison.Ordinal) -> Glob(s, constantString + "*");
s.StartsWith(constantString, StringComparison.OrdinalIgnoreCase) -> Like(s, constantString + "%"); s.StartsWith(constantString, StringComparison.OrdinalIgnoreCase) -> Like(s, constantString + "%");
@ -159,31 +162,43 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
return expression.ToString(); return expression.ToString();
} }
public override object Execute(Expression expression) public override object Execute(Expression inputExpression)
{ {
Console.WriteLine("Input expression: " + expression); Stopwatch watch = Stopwatch.StartNew();
Expression partiallyEvaluatedExpression = PartialEvaluator.Eval(inputExpression, CanBeEvaluatedStatically);
expression = PartialEvaluator.Eval(expression, CanBeEvaluatedStatically); QueryExecutionOptions options = new QueryExecutionOptions();
Console.WriteLine("Partially evaluated expression: " + expression); Expression expression = new ConvertToQueryAstVisitor(options).Visit(partiallyEvaluatedExpression);
// options have been initialized by ConvertToQueryAstVisitor, start logging:
expression = new ConvertToQueryAstVisitor().Visit(expression); if (options.HasLoggers) {
Console.WriteLine("Converted to Query AST: " + expression); options.WriteLogLine("Input expression: " + inputExpression);
options.WriteLogLine("Partially evaluated expression: " + partiallyEvaluatedExpression);
options.WriteLogLine("Converted to Query AST: " + expression);
}
expression = new OptimizeQueryExpressionVisitor().Visit(expression); expression = new OptimizeQueryExpressionVisitor().Visit(expression);
Console.WriteLine("Optimized Query AST: " + expression); if (options.HasLoggers) {
options.WriteLogLine("Optimized Query AST: " + expression);
options.WriteLogLine("Query preparation time: " + watch.Elapsed);
}
object result;
// If the whole query was converted, execute it: // If the whole query was converted, execute it:
QueryNode query = expression as QueryNode; QueryNode query = expression as QueryNode;
if (query != null) if (query != null) {
return query.Execute(this); result = query.Execute(this, options);
} else {
// Query not converted completely: we have to use a LINQ-To-Objects / LINQ-To-Profiler mix // Query not converted completely: we have to use a LINQ-To-Objects / LINQ-To-Profiler mix
expression = new ExecuteAllQueriesVisitor(this).Visit(expression); expression = new ExecuteAllQueriesVisitor(this, options).Visit(expression);
if (expression.Type.IsValueType) { if (expression.Type.IsValueType) {
expression = Expression.Convert(expression, typeof(object)); expression = Expression.Convert(expression, typeof(object));
}
var lambdaExpression = Expression.Lambda<Func<object>>(expression);
result = lambdaExpression.Compile()();
} }
var lambdaExpression = Expression.Lambda<Func<object>>(expression); watch.Stop();
return lambdaExpression.Compile()(); options.WriteLogLine("Total query execution time: " + watch.Elapsed);
return result;
} }
static bool CanBeEvaluatedStatically(Expression expression) static bool CanBeEvaluatedStatically(Expression expression)
@ -194,6 +209,13 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
#region Convert Expression Tree To Query AST #region Convert Expression Tree To Query AST
sealed class ConvertToQueryAstVisitor : System.Linq.Expressions.ExpressionVisitor sealed class ConvertToQueryAstVisitor : System.Linq.Expressions.ExpressionVisitor
{ {
readonly QueryExecutionOptions options;
public ConvertToQueryAstVisitor(QueryExecutionOptions options)
{
this.options = options;
}
protected override Expression VisitExtension(Expression node) protected override Expression VisitExtension(Expression node)
{ {
// We found a query that's already converted, let's keep it as it is. // We found a query that's already converted, let's keep it as it is.
@ -233,6 +255,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
} }
} }
} }
} else if (node.Method == KnownMembers.Queryable_WithQueryLog && node.Arguments[1].NodeType == ExpressionType.Constant) {
options.AddLogger((TextWriter)(((ConstantExpression)node.Arguments[1]).Value));
return Visit(node.Arguments[0]);
} else if (node.Method == KnownMembers.QueryableOfCallTreeNode_Take && node.Arguments[1].NodeType == ExpressionType.Constant) { } else if (node.Method == KnownMembers.QueryableOfCallTreeNode_Take && node.Arguments[1].NodeType == ExpressionType.Constant) {
ConstantExpression ce = (ConstantExpression)node.Arguments[1]; ConstantExpression ce = (ConstantExpression)node.Arguments[1];
if (ce.Type == typeof(int)) { if (ce.Type == typeof(int)) {
@ -269,10 +294,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
return base.VisitMethodCall(node); return base.VisitMethodCall(node);
} }
} }
#endregion
#region SafeExpressionImporter
sealed class SafeExpressionImporter sealed class SafeExpressionImporter
{ {
ParameterExpression callTreeNodeParameter; readonly ParameterExpression callTreeNodeParameter;
public SafeExpressionImporter(ParameterExpression callTreeNodeParameter) public SafeExpressionImporter(ParameterExpression callTreeNodeParameter)
{ {
@ -335,6 +362,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
Expression.Property(callTreeNodeParameter, KnownMembers.CallTreeNode_NameMapping), Expression.Property(callTreeNodeParameter, KnownMembers.CallTreeNode_NameMapping),
KnownMembers.NameMapping_ID), KnownMembers.NameMapping_ID),
Expression.Constant(0)); Expression.Constant(0));
} else if (me.Member == KnownMembers.CallTreeNode_IsThread) {
return Expression.Call(KnownMembers.Glob,
Expression.Property(Expression.Property(callTreeNodeParameter, KnownMembers.CallTreeNode_NameMapping), KnownMembers.NameMapping_Name),
Expression.Constant("Thread#*"));
} }
if (me.Member == KnownMembers.CallTreeNode_CallCount) if (me.Member == KnownMembers.CallTreeNode_CallCount)
@ -437,21 +468,44 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
sealed class ExecuteAllQueriesVisitor : System.Linq.Expressions.ExpressionVisitor sealed class ExecuteAllQueriesVisitor : System.Linq.Expressions.ExpressionVisitor
{ {
readonly SQLiteQueryProvider sqliteProvider; readonly SQLiteQueryProvider sqliteProvider;
readonly QueryExecutionOptions options;
public ExecuteAllQueriesVisitor(SQLiteQueryProvider sqliteProvider) public ExecuteAllQueriesVisitor(SQLiteQueryProvider sqliteProvider, QueryExecutionOptions options)
{ {
this.sqliteProvider = sqliteProvider; this.sqliteProvider = sqliteProvider;
this.options = options;
} }
protected override Expression VisitExtension(Expression node) protected override Expression VisitExtension(Expression node)
{ {
QueryNode query = node as QueryNode; QueryNode query = node as QueryNode;
if (query != null) if (query != null)
return Expression.Constant(query.Execute(sqliteProvider)); return Expression.Constant(query.Execute(sqliteProvider, options));
else else
return base.VisitExtension(node); return base.VisitExtension(node);
} }
} }
#endregion #endregion
} }
sealed class QueryExecutionOptions
{
List<TextWriter> loggers = new List<TextWriter>();
public void AddLogger(TextWriter w)
{
if (w != null)
loggers.Add(w);
}
public bool HasLoggers {
get { return loggers.Count > 0; }
}
public void WriteLogLine(string text)
{
foreach (TextWriter w in loggers)
w.WriteLine(text);
}
}
} }

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

@ -65,7 +65,7 @@ namespace ICSharpCode.Profiler.Controller.Data
/// </summary> /// </summary>
public virtual IQueryable<CallTreeNode> GetFunctions(int startIndex, int endIndex) public virtual IQueryable<CallTreeNode> GetFunctions(int startIndex, int endIndex)
{ {
return GetRoot(startIndex, endIndex).Descendants.GroupBy(n => n.NameMapping).Select(g => g.Merge()); return GetRoot(startIndex, endIndex).Descendants.Where(c => !c.IsThread).MergeByName();
} }
} }
} }

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

@ -274,7 +274,7 @@ namespace ICSharpCode.Profiler.Controller.Data
var query = queryProvider.CreateQuery(new MergeByName(new Filter(AllCalls.Instance, DataSetFilter(startIndex, endIndex)))); var query = queryProvider.CreateQuery(new MergeByName(new Filter(AllCalls.Instance, DataSetFilter(startIndex, endIndex))));
return from c in query return from c in query
where c.NameMapping.Id != 0 && !c.NameMapping.Name.StartsWith("Thread#", StringComparison.Ordinal) where c.NameMapping.Id != 0 && !c.IsThread
select c; select c;
} }

77
src/AddIns/Misc/Profiler/Controller/ExtensionMethods.cs

@ -2,6 +2,7 @@
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
@ -32,7 +33,19 @@ namespace ICSharpCode.Profiler.Controller
return builder.ToString(); return builder.ToString();
} }
/// <summary>
/// Returns a CallTreeNode with all merged items.
/// </summary>
public static CallTreeNode Merge(this IQueryable<CallTreeNode> items)
{
if (items == null)
throw new ArgumentNullException("items");
return items.Provider.Execute<CallTreeNode>(
Expression.Call(Data.Linq.KnownMembers.Queryable_Merge, items.Expression));
}
/// <summary> /// <summary>
/// Returns a CallTreeNode with all merged items. /// Returns a CallTreeNode with all merged items.
/// </summary> /// </summary>
@ -41,16 +54,60 @@ namespace ICSharpCode.Profiler.Controller
if (items == null) if (items == null)
throw new ArgumentNullException("items"); throw new ArgumentNullException("items");
Debug.Assert(MethodBase.GetCurrentMethod() == Data.Linq.KnownMembers.Merge); var itemList = items.ToList();
return itemList.First().Merge(items);
}
/// <summary>
/// Merges CallTreeNodes with identical name mappings.
/// </summary>
public static IQueryable<CallTreeNode> MergeByName(this IQueryable<CallTreeNode> items)
{
if (items == null)
throw new ArgumentNullException("items");
IQueryable<CallTreeNode> query = items as IQueryable<CallTreeNode>; return items.Provider.CreateQuery<CallTreeNode>(
if (query != null) { Expression.Call(Data.Linq.KnownMembers.Queryable_MergeByName, items.Expression));
return query.Provider.Execute<CallTreeNode>( }
Expression.Call(Data.Linq.KnownMembers.Merge, query.Expression));
} else { /// <summary>
var itemList = items.ToList(); /// Merges CallTreeNodes with identical name mappings.
return itemList.First().Merge(items); /// </summary>
} public static IEnumerable<CallTreeNode> MergeByName(this IEnumerable<CallTreeNode> items)
{
if (items == null)
throw new ArgumentNullException("items");
return items.GroupBy(c => c.NameMapping).Select(g => g.Merge());
}
/// <summary>
/// Tells the query execution to add logging to the query.
/// </summary>
public static IQueryable<CallTreeNode> WithQueryLog(this IQueryable<CallTreeNode> items, TextWriter logOutput)
{
if (items == null)
throw new ArgumentNullException("items");
if (logOutput == null)
throw new ArgumentNullException("logOutput");
return items.Provider.CreateQuery<CallTreeNode>(
Expression.Call(Data.Linq.KnownMembers.Queryable_WithQueryLog, items.Expression, Expression.Constant(logOutput)));
}
/// <summary>
/// Tells the query execution to add logging to the query.
/// </summary>
public static IEnumerable<CallTreeNode> WithQueryLog(this IEnumerable<CallTreeNode> items, TextWriter logOutput)
{
if (items == null)
throw new ArgumentNullException("items");
if (logOutput == null)
throw new ArgumentNullException("logOutput");
logOutput.WriteLine("The query did not use LINQ-to-Profiler.");
return items;
} }
/// <summary> /// <summary>

14
src/AddIns/Misc/Profiler/Controller/Interprocess/UnmanagedCircularBuffer.cs

@ -165,14 +165,20 @@ namespace ICSharpCode.Profiler.Interprocess
/// <summary> /// <summary>
/// Closes the circular buffer. /// Closes the circular buffer.
/// </summary> /// </summary>
public void Close() /// <param name="waitForReaderThreadExit">
/// Specifies whether the close method should wait for the active reader thread to exit.
/// If you pass true, but there is no reader thread; the Close method will deadlock.
/// If you pass false, but there is a reader thread; the reader thread will crash.
/// </param>
public void Close(bool waitForReaderThreadExit)
{ {
lock (closeLock) { lock (closeLock) {
if (isClosed) if (isClosed)
return; return;
isClosed = true; isClosed = true;
nonEmptyEvent.Set(); nonEmptyEvent.Set();
Monitor.Wait(closeLock); if (waitForReaderThreadExit)
Monitor.Wait(closeLock);
} }
nonEmptyEvent.Close(); nonEmptyEvent.Close();
nonFullEvent.Close(); nonFullEvent.Close();
@ -181,7 +187,7 @@ namespace ICSharpCode.Profiler.Interprocess
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
void IDisposable.Dispose() void IDisposable.Dispose()
{ {
Close(); Close(false);
} }
#endregion #endregion
@ -374,7 +380,7 @@ namespace ICSharpCode.Profiler.Interprocess
int writeCount = Math.Min(count, writeEndOffset - endOffset); int writeCount = Math.Min(count, writeEndOffset - endOffset);
Debug.Assert(writeCount > 0); Debug.Assert(writeCount > 0);
Debug.WriteLine("UnmanagedCircularBuffer: write amount " + writeCount); //Debug.WriteLine("UnmanagedCircularBuffer: write amount " + writeCount);
Marshal.Copy(buffer, offset, new IntPtr(circularBuffer.data + endOffset), writeCount); Marshal.Copy(buffer, offset, new IntPtr(circularBuffer.data + endOffset), writeCount);
endOffset += writeCount; // advance endOffset endOffset += writeCount; // advance endOffset

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

@ -591,7 +591,7 @@ namespace ICSharpCode.Profiler.Controller
this.stopDC = true; this.stopDC = true;
Debug.WriteLine("Closing native to managed buffer"); Debug.WriteLine("Closing native to managed buffer");
nativeToManagedBuffer.Close(); nativeToManagedBuffer.Close(true);
Debug.WriteLine("Joining logger thread..."); Debug.WriteLine("Joining logger thread...");
this.logger.Join(); this.logger.Join();
@ -786,7 +786,7 @@ namespace ICSharpCode.Profiler.Controller
if (!isDisposed) { if (!isDisposed) {
isDisposed = true; isDisposed = true;
stopDC = true; stopDC = true;
nativeToManagedBuffer.Close(); nativeToManagedBuffer.Close(true);
try { try {
this.profilee.Kill(); this.profilee.Kill();
} catch (InvalidOperationException) { } catch (InvalidOperationException) {

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

@ -189,29 +189,32 @@ namespace ICSharpCode.Profiler.Controls
this.Translation = new ControlsTranslation(); this.Translation = new ControlsTranslation();
this.visibleColumnsSelection.ItemsSource = this.gridView.Columns.Select(col => new GridViewColumnModel(col)); this.visibleColumnsSelection.ItemsSource = this.gridView.Columns.Select(col => new GridViewColumnModel(col));
this.treeView.SizeChanged += delegate(object sender, SizeChangedEventArgs e) { this.treeView.SizeChanged += QueryView_SizeChanged;
if (e.NewSize.Width > 0 && e.PreviousSize.Width > 0) { }
double adjustedNameColumnWidth = nameColumn.Width + e.NewSize.Width - e.PreviousSize.Width;
double matchingNameColumnWidth = e.NewSize.Width - this.callCountColumn.Width void QueryView_SizeChanged(object sender, SizeChangedEventArgs e)
- this.percentColumn.Width - this.timeSpentColumn.Width {
- this.timeSpentSelfColumn.Width - this.timeSpentPerCallColumn.Width if (e.NewSize.Width > 0 && e.PreviousSize.Width > 0) {
- this.timeSpentSelfPerCallColumn.Width - 25; double adjustedNameColumnWidth = nameColumn.Width + e.NewSize.Width - e.PreviousSize.Width;
double matchingNameColumnWidth = e.NewSize.Width - this.callCountColumn.Width
// always keep name column at least 75 pixels wide - this.percentColumn.Width - this.timeSpentColumn.Width
if (matchingNameColumnWidth < 75) - this.timeSpentSelfColumn.Width - this.timeSpentPerCallColumn.Width
matchingNameColumnWidth = 75; - this.timeSpentSelfPerCallColumn.Width - 25;
if (e.NewSize.Width >= e.PreviousSize.Width) { // always keep name column at least 75 pixels wide
// treeView got wider: also make name column wider if there's space free if (matchingNameColumnWidth < 75)
if (adjustedNameColumnWidth <= matchingNameColumnWidth) matchingNameColumnWidth = 75;
nameColumn.Width = adjustedNameColumnWidth;
} else { if (e.NewSize.Width >= e.PreviousSize.Width) {
// treeView got smaller: make column smaller unless there's space free // treeView got wider: also make name column wider if there's space free
if (adjustedNameColumnWidth >= matchingNameColumnWidth) if (adjustedNameColumnWidth <= matchingNameColumnWidth)
nameColumn.Width = adjustedNameColumnWidth; nameColumn.Width = adjustedNameColumnWidth;
} } else {
// treeView got smaller: make column smaller unless there's space free
if (adjustedNameColumnWidth >= matchingNameColumnWidth)
nameColumn.Width = adjustedNameColumnWidth;
} }
}; }
} }
public void SetRange(int start, int end) public void SetRange(int start, int end)
@ -290,6 +293,9 @@ namespace ICSharpCode.Profiler.Controls
try { try {
if (compiler.Compile()) { if (compiler.Compile()) {
var data = compiler.ExecuteQuery(provider, rangeStart, rangeEnd); var data = compiler.ExecuteQuery(provider, rangeStart, rangeEnd);
#if DEBUG
data = data.WithQueryLog(Console.Out);
#endif
var nodes = data.Select(i => new CallTreeNodeViewModel(i, null)).ToList(); var nodes = data.Select(i => new CallTreeNodeViewModel(i, null)).ToList();
return new HierarchyList<CallTreeNodeViewModel>(nodes); return new HierarchyList<CallTreeNodeViewModel>(nodes);
} }

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

@ -10,6 +10,7 @@ using System;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
using ICSharpCode.Profiler.Controller;
using ICSharpCode.Profiler.Controller.Data; using ICSharpCode.Profiler.Controller.Data;
using ICSharpCode.Profiler.Controller.Data.Linq; using ICSharpCode.Profiler.Controller.Data.Linq;
using NUnit.Framework; using NUnit.Framework;
@ -129,7 +130,7 @@ namespace Profiler.Tests.Controller.Data
[Test] [Test]
public void TestFunctions() public void TestFunctions()
{ {
CallTreeNode[] functions = provider.GetFunctions(1, 2).OrderBy(f => f.Name).ToArray(); CallTreeNode[] functions = provider.GetFunctions(1, 2).OrderBy(f => f.Name).WithQueryLog(Console.Out).ToArray();
Assert.AreEqual(2, functions.Length); Assert.AreEqual(2, functions.Length);
Assert.AreEqual("m1", functions[0].Name); Assert.AreEqual("m1", functions[0].Name);

5
src/AddIns/Misc/Profiler/Controller/Interprocess/UnmanagedCircularBufferTest.cs → src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Controller/UnmanagedCircularBufferTest.cs

@ -10,6 +10,7 @@ using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using ICSharpCode.Profiler.Interprocess;
using NUnit.Framework; using NUnit.Framework;
namespace Profiler.Interprocess namespace Profiler.Interprocess
@ -83,7 +84,7 @@ namespace Profiler.Interprocess
using (Stream ws = ncb.CreateWritingStream()) { using (Stream ws = ncb.CreateWritingStream()) {
ws.WriteByte(0x42); ws.WriteByte(0x42);
} }
using (UnmanagedCircularBuffer ncb2 = UnmanagedCircularBuffer.Open(memoryStart, memoryLength)) { using (UnmanagedCircularBuffer ncb2 = UnmanagedCircularBuffer.Open(memoryStart)) {
using (Stream rs = ncb2.CreateReadingStream()) { using (Stream rs = ncb2.CreateReadingStream()) {
Assert.AreEqual(0x42, rs.ReadByte()); Assert.AreEqual(0x42, rs.ReadByte());
} }
@ -104,7 +105,7 @@ namespace Profiler.Interprocess
using (MemoryMappedFile mmf2 = MemoryMappedFile.Open("Local\\TestMemory")) { using (MemoryMappedFile mmf2 = MemoryMappedFile.Open("Local\\TestMemory")) {
using (UnmanagedMemory view2 = mmf1.MapView(0, 1024)) { using (UnmanagedMemory view2 = mmf1.MapView(0, 1024)) {
Assert.AreNotEqual(view1.Start, view2.Start); Assert.AreNotEqual(view1.Start, view2.Start);
using (UnmanagedCircularBuffer ncb2 = UnmanagedCircularBuffer.Open(view2.Start, (int)view2.Length)) { using (UnmanagedCircularBuffer ncb2 = UnmanagedCircularBuffer.Open(view2.Start)) {
using (Stream rs = ncb2.CreateReadingStream()) { using (Stream rs = ncb2.CreateReadingStream()) {
Assert.AreEqual(0x42, rs.ReadByte()); Assert.AreEqual(0x42, rs.ReadByte());
} }

1
src/AddIns/Misc/Profiler/Tests/Profiler.Tests/Profiler.Tests.csproj

@ -92,6 +92,7 @@
<Compile Include="Controller\Data\SQLiteTests.cs" /> <Compile Include="Controller\Data\SQLiteTests.cs" />
<Compile Include="Controller\Data\CallTreeNodeStub.cs" /> <Compile Include="Controller\Data\CallTreeNodeStub.cs" />
<Compile Include="Controller\Queries\NodePathTests.cs" /> <Compile Include="Controller\Queries\NodePathTests.cs" />
<Compile Include="Controller\UnmanagedCircularBufferTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

Loading…
Cancel
Save