Browse Source

implemented Take and OrderBy

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5030 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Siegfried Pammer 17 years ago
parent
commit
894625ec43
  1. 6
      src/AddIns/Misc/Profiler/Controller/Data/Linq/ExpressionSqlWriter.cs
  2. 12
      src/AddIns/Misc/Profiler/Controller/Data/Linq/KnownMembers.cs
  3. 129
      src/AddIns/Misc/Profiler/Controller/Data/Linq/QueryAst.cs
  4. 42
      src/AddIns/Misc/Profiler/Controller/Data/Linq/SQLiteQueryProvider.cs
  5. 6
      src/AddIns/Misc/Profiler/Controller/Data/Linq/SqlQueryContext.cs
  6. 1
      src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteWriter.cs
  7. 2
      src/AddIns/Misc/Profiler/Frontend/AddIn/Src/Views/ProfilerView.xaml

6
src/AddIns/Misc/Profiler/Controller/Data/Linq/ExpressionSqlWriter.cs

@ -107,7 +107,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
void WriteMemberAccess(MemberExpression me) void WriteMemberAccess(MemberExpression me)
{ {
if (me.Expression == callTreeNodeParameter) { if (me.Expression == callTreeNodeParameter) {
if (!nameSet.IsCalls) if (me.Member.DeclaringType == typeof(SingleCall) && !nameSet.IsCalls)
throw new InvalidOperationException("SingleCall references are invalid here"); throw new InvalidOperationException("SingleCall references are invalid here");
if (me.Member == SingleCall.IDField) { if (me.Member == SingleCall.IDField) {
w.Write("id"); w.Write("id");
@ -115,6 +115,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
w.Write("datasetid"); w.Write("datasetid");
} else if (me.Member == SingleCall.ParentIDField) { } else if (me.Member == SingleCall.ParentIDField) {
w.Write("parentid"); w.Write("parentid");
} else if (me.Member == KnownMembers.CallTreeNode_CallCount) {
w.Write(nameSet.CallCount);
} else if (me.Member == KnownMembers.CallTreeNode_CpuCyclesSpent) {
w.Write(nameSet.CpuCyclesSpent);
} else { } else {
throw new NotSupportedException(me.Member.ToString()); throw new NotSupportedException(me.Member.ToString());
} }

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

@ -15,14 +15,20 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
{ {
static class KnownMembers static class KnownMembers
{ {
public static readonly MethodInfo ListOfInt_Contains = MethodOf((List<int> list) => list.Contains(0));
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_CpuCyclesSpent = PropertyOf((CallTreeNode c) => c.CpuCyclesSpent);
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);
public static readonly PropertyInfo CallTreeNode_Children = PropertyOf((CallTreeNode c) => c.Children); public static readonly PropertyInfo CallTreeNode_Children = PropertyOf((CallTreeNode c) => c.Children);
public static readonly PropertyInfo NameMapping_ID = PropertyOf((NameMapping n) => n.Id); public static readonly PropertyInfo NameMapping_ID = PropertyOf((NameMapping n) => n.Id);
public static readonly PropertyInfo NameMapping_Name = PropertyOf((NameMapping n) => n.Name); public static readonly PropertyInfo NameMapping_Name = PropertyOf((NameMapping n) => n.Name);
public static readonly MethodInfo QuerableOfCallTreeNode_Select = MethodOf((IQueryable<CallTreeNode> q) => q.Select(x => x));
public static readonly MethodInfo QuerableOfCallTreeNode_Where = MethodOf((IQueryable<CallTreeNode> q) => q.Where(x => true)); public static readonly MethodInfo ListOfInt_Contains = MethodOf((List<int> list) => list.Contains(0));
public static readonly MethodInfo QueryableOfCallTreeNode_Select = MethodOf((IQueryable<CallTreeNode> q) => q.Select(x => x));
public static readonly MethodInfo QueryableOfCallTreeNode_Where = MethodOf((IQueryable<CallTreeNode> q) => q.Where(x => true));
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 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 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 Like = MethodOf(() => LikeImpl("", "")); public static readonly MethodInfo Like = MethodOf(() => LikeImpl("", ""));

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

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
@ -84,7 +85,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
CallTreeNodeSqlNameSet newNames = new CallTreeNodeSqlNameSet(context, false); CallTreeNodeSqlNameSet newNames = new CallTreeNodeSqlNameSet(context, false);
b.Insert(0, "SELECT " + SqlAs(oldNames.ID, newNames.ID) + ", " b.Insert(0, "SELECT " + SqlAs(oldNames.ID, newNames.ID) + ", "
+ SqlAs(oldNames.NameID, newNames.NameID) + ", " + SqlAs(oldNames.NameID, newNames.NameID) + ", "
+ SqlAs(oldNames.TimeSpent, newNames.TimeSpent) + ", " + SqlAs(oldNames.CpuCyclesSpent, newNames.CpuCyclesSpent) + ", "
+ SqlAs(oldNames.CallCount, newNames.CallCount) + ", " + SqlAs(oldNames.CallCount, newNames.CallCount) + ", "
+ SqlAs(oldNames.HasChildren, newNames.HasChildren) + ", " + SqlAs(oldNames.HasChildren, newNames.HasChildren) + ", "
+ SqlAs(oldNames.ActiveCallCount, newNames.ActiveCallCount) + SqlAs(oldNames.ActiveCallCount, newNames.ActiveCallCount)
@ -143,7 +144,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
b.AppendLine("SELECT " b.AppendLine("SELECT "
+ SqlAs("id", newNames.ID) + ", " + SqlAs("id", newNames.ID) + ", "
+ SqlAs("nameid", newNames.NameID) + ", " + SqlAs("nameid", newNames.NameID) + ", "
+ SqlAs("timespent", newNames.TimeSpent) + ", " + SqlAs("timespent", newNames.CpuCyclesSpent) + ", " // TODO : change to CpuCyclesSpent
+ SqlAs("callcount", newNames.CallCount) + ", " + SqlAs("callcount", newNames.CallCount) + ", "
+ SqlAs("(id != endid)", newNames.HasChildren) + ", " + SqlAs("(id != endid)", newNames.HasChildren) + ", "
+ SqlAs("((datasetid = " + context.StartDataSetID + ") AND isActiveAtStart)", newNames.ActiveCallCount)); + SqlAs("((datasetid = " + context.StartDataSetID + ") AND isActiveAtStart)", newNames.ActiveCallCount));
@ -186,7 +187,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
b.Insert(0, "SELECT " b.Insert(0, "SELECT "
+ SqlAs("GROUP_CONCAT(" + oldNames.ID + ")", newNames.ID) + ", " + SqlAs("GROUP_CONCAT(" + oldNames.ID + ")", newNames.ID) + ", "
+ SqlAs(oldNames.NameID, newNames.NameID) + ", " + SqlAs(oldNames.NameID, newNames.NameID) + ", "
+ SqlAs("SUM(" + oldNames.TimeSpent + ")", newNames.TimeSpent) + ", " + SqlAs("SUM(" + oldNames.CpuCyclesSpent + ")", newNames.CpuCyclesSpent) + ", "
+ SqlAs("SUM(" + oldNames.CallCount + ")", newNames.CallCount) + ", " + SqlAs("SUM(" + oldNames.CallCount + ")", newNames.CallCount) + ", "
+ SqlAs("MAX(" + oldNames.HasChildren + ")", newNames.HasChildren) + ", " + SqlAs("MAX(" + oldNames.HasChildren + ")", newNames.HasChildren) + ", "
+ SqlAs("SUM(" + oldNames.ActiveCallCount + ")", newNames.ActiveCallCount) + SqlAs("SUM(" + oldNames.ActiveCallCount + ")", newNames.ActiveCallCount)
@ -284,4 +285,126 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
b.Append(w.ToString()); b.Append(w.ToString());
} }
} }
/// <summary>
/// Query node that limits the amount of data selected. Produces LIMIT in SQL.
/// </summary>
sealed class Limit : QueryNode
{
public int Start { get; private set; }
public int Length { get; private set; }
public Limit(QueryNode target, int start, int length)
: base(target)
{
this.Start = start;
this.Length = length;
}
protected override Expression VisitChildren(Func<Expression, Expression> visitor)
{
QueryNode newTarget = (QueryNode)visitor(Target);
if (newTarget == Target)
return this;
else
return new Limit(newTarget, Start, Length);
}
public override string ToString()
{
return Target + ".Limit(" + Start + "," + Length + ")";
}
public override SqlStatementKind BuildSql(StringBuilder b, SqlQueryContext context)
{
SqlStatementKind kind = Target.BuildSql(b, context);
if (kind == SqlStatementKind.SelectLimit)
WrapSqlIntoNestedStatement(b, context);
b.Append(" LIMIT " + Length);
b.AppendLine(" OFFSET " + Start);
return SqlStatementKind.SelectLimit;
}
}
struct SortArgument {
LambdaExpression arg;
bool desc;
public SortArgument(LambdaExpression arg, bool desc)
{
this.arg = arg;
this.desc = desc;
}
public bool Descending {
get { return desc; }
}
public LambdaExpression Argument {
get { return arg; }
}
}
sealed class Sort : QueryNode
{
ReadOnlyCollection<SortArgument> arguments;
public Sort(QueryNode target, LambdaExpression argument, bool desc)
: this(target, new[] { new SortArgument(argument, desc) })
{
}
Sort(QueryNode target, IList<SortArgument> args)
: base(target)
{
this.arguments = new ReadOnlyCollection<SortArgument>(args);
}
public override SqlStatementKind BuildSql(StringBuilder b, SqlQueryContext context)
{
SqlStatementKind kind = Target.BuildSql(b, context);
if (kind == SqlStatementKind.SelectOrderBy)
WrapSqlIntoNestedStatement(b, context);
b.Append(" ORDER BY ");
for (int i = 0; i < arguments.Count; i++) {
StringWriter w = new StringWriter();
ExpressionSqlWriter writer = new ExpressionSqlWriter(w, context.CurrentNameSet, arguments[i].Argument.Parameters[0]);
writer.Write(arguments[i].Argument.Body);
if (i == 0)
b.Append(w.ToString());
else
b.Append(", " + w.ToString());
if (arguments[i].Descending)
b.Append(" DESC");
else
b.Append(" ASC");
}
return SqlStatementKind.SelectOrderBy;
}
protected override Expression VisitChildren(Func<Expression, Expression> visitor)
{
QueryNode newTarget = (QueryNode)visitor(Target);
if (newTarget == Target)
return this;
else
return new Sort(newTarget, arguments);
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
foreach (var item in arguments)
builder.Append(item.Argument + " " + (item.Descending ? "DESC" : "ASC") + ",");
return Target + ".Sort( {" + builder.ToString() + "} )";
}
}
} }

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

@ -206,7 +206,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
protected override Expression VisitMethodCall(MethodCallExpression node) protected override Expression VisitMethodCall(MethodCallExpression node)
{ {
if (node.Method == KnownMembers.QuerableOfCallTreeNode_Select && node.Arguments[1].NodeType == ExpressionType.Quote) { if (node.Method == KnownMembers.QueryableOfCallTreeNode_Select && node.Arguments[1].NodeType == ExpressionType.Quote) {
// CallTreeNode[].Select: // CallTreeNode[].Select:
// selects are not supported by query evaluator, but we will detect and remove // selects are not supported by query evaluator, but we will detect and remove
// degenerate selects (.Select(x => x)). // degenerate selects (.Select(x => x)).
@ -216,7 +216,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
if (lambda.Parameters.Count == 1 && lambda.Body == lambda.Parameters[0]) if (lambda.Parameters.Count == 1 && lambda.Body == lambda.Parameters[0])
return Visit(node.Arguments[0]); return Visit(node.Arguments[0]);
} }
} else if (node.Method == KnownMembers.QuerableOfCallTreeNode_Where && node.Arguments[1].NodeType == ExpressionType.Quote) { } else if (node.Method == KnownMembers.QueryableOfCallTreeNode_Where && node.Arguments[1].NodeType == ExpressionType.Quote) {
// CallTreeNode[].Where: // CallTreeNode[].Where:
// if the target is a QueryNode and the condition can be safely imported, convert the Where call to Filter // if the target is a QueryNode and the condition can be safely imported, convert the Where call to Filter
UnaryExpression quote = (UnaryExpression)node.Arguments[1]; UnaryExpression quote = (UnaryExpression)node.Arguments[1];
@ -233,6 +233,38 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
} }
} }
} }
} else if (node.Method == KnownMembers.QueryableOfCallTreeNode_Take && node.Arguments[1].NodeType == ExpressionType.Constant) {
ConstantExpression ce = (ConstantExpression)node.Arguments[1];
if (ce.Type == typeof(int)) {
QueryNode target = Visit(node.Arguments[0]) as QueryNode;
if (target != null) {
return new Limit(target, 0, (int)ce.Value);
}
}
} else if (node.Method.IsGenericMethod && node.Method.GetGenericMethodDefinition() == KnownMembers.Queryable_OrderBy &&
node.Method.GetGenericArguments()[0] == typeof(CallTreeNode) &&
node.Arguments[1].NodeType == ExpressionType.Quote) {
UnaryExpression quote = (UnaryExpression)node.Arguments[1];
if (quote.Operand.NodeType == ExpressionType.Lambda) {
LambdaExpression lambda = (LambdaExpression)quote.Operand;
if (lambda.Parameters.Count == 1) {
QueryNode target = Visit(node.Arguments[0]) as QueryNode;
if (target != null) {
SafeExpressionImporter importer = new SafeExpressionImporter(lambda.Parameters[0]);
var imported = importer.Import(lambda.Body);
if (imported != null)
return new Sort(target, Expression.Lambda(imported, lambda.Parameters[0]), false);
}
}
}
} else if (node.Method == KnownMembers.QueryableOfCallTreeNode_OrderByDesc && node.Arguments[1].NodeType == ExpressionType.Quote) {
ConstantExpression ce = (ConstantExpression)node.Arguments[1];
if (ce.Type == typeof(int)) {
QueryNode target = Visit(node.Arguments[0]) as QueryNode;
if (target != null) {
return new Limit(target, 0, (int)ce.Value);
}
}
} }
return base.VisitMethodCall(node); return base.VisitMethodCall(node);
} }
@ -304,6 +336,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
KnownMembers.NameMapping_ID), KnownMembers.NameMapping_ID),
Expression.Constant(0)); Expression.Constant(0));
} }
if (me.Member == KnownMembers.CallTreeNode_CallCount)
return me;
if (me.Member == KnownMembers.CallTreeNode_CpuCyclesSpent)
return me;
} else if (IsNameMappingOnParameter(me.Expression)) { } else if (IsNameMappingOnParameter(me.Expression)) {
if (me.Member == KnownMembers.NameMapping_ID) if (me.Member == KnownMembers.NameMapping_ID)
return me; return me;

6
src/AddIns/Misc/Profiler/Controller/Data/Linq/SqlQueryContext.cs

@ -33,7 +33,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
{ {
public readonly string ID; public readonly string ID;
public readonly string NameID = "nameid"; public readonly string NameID = "nameid";
public readonly string TimeSpent; public readonly string CpuCyclesSpent;
public readonly string CallCount; public readonly string CallCount;
public readonly string HasChildren; public readonly string HasChildren;
public readonly string ActiveCallCount; public readonly string ActiveCallCount;
@ -50,11 +50,11 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
string prefix = c.GenerateUniqueVariableName(); string prefix = c.GenerateUniqueVariableName();
if (isCalls) { if (isCalls) {
ID = "id"; ID = "id";
TimeSpent = "timespent"; CpuCyclesSpent = "cpucyclesspent";
CallCount = "callcount"; CallCount = "callcount";
} else { } else {
ID = prefix + "ID"; ID = prefix + "ID";
TimeSpent = prefix + "TimeSpent"; CpuCyclesSpent = prefix + "CpuCyclesSpent";
CallCount = prefix + "CallCount"; CallCount = prefix + "CallCount";
} }
HasChildren = prefix + "HasChildren"; HasChildren = prefix + "HasChildren";

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

@ -185,6 +185,7 @@ namespace ICSharpCode.Profiler.Controller.Data
void InitializeTables() void InitializeTables()
{ {
//NameMapping { Id, ReturnType, Name, Parameters } //NameMapping { Id, ReturnType, Name, Parameters }
// TODO : update db schema: change FunctionData.TimeSpent to cpucyclesspent
//FunctionData { DataSetId, Id, ParentId, NameId, TimeSpent, CallCount } //FunctionData { DataSetId, Id, ParentId, NameId, TimeSpent, CallCount }
//DataSets { Id, CPUUsage, RootId } //DataSets { Id, CPUUsage, RootId }
// //

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

@ -41,7 +41,7 @@
<y:QueryView x:Name="treeView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowQueryItems="False" CurrentQuery="from t in Threads select t" IsQueryModifiable="False" /> <y:QueryView x:Name="treeView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowQueryItems="False" CurrentQuery="from t in Threads select t" IsQueryModifiable="False" />
</TabItem> </TabItem>
<TabItem Header="{sd:Localize AddIns.Profiler.ProfilingView.Top20TabText}"> <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.CpuCyclesSpentSelf descending select f).Take(20)" IsQueryModifiable="False" /> <y:QueryView x:Name="top20View" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ShowQueryItems="False" CurrentQuery="(from f in Functions where f.IsUserCode orderby f.CpuCyclesSpent select f).Take(20)" IsQueryModifiable="False" />
</TabItem> </TabItem>
<TabItem x:Name="dummyTab" /> <TabItem x:Name="dummyTab" />
</TabControl> </TabControl>

Loading…
Cancel
Save