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. 88
      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. 71
      src/AddIns/Misc/Profiler/Controller/ExtensionMethods.cs
  8. 12
      src/AddIns/Misc/Profiler/Controller/Interprocess/UnmanagedCircularBuffer.cs
  9. 4
      src/AddIns/Misc/Profiler/Controller/Profiler.cs
  10. 10
      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 @@ -109,9 +109,9 @@ namespace ICSharpCode.Profiler.Controller.Data
}
/// <summary>
/// Determines whether this node is a thread node or not.
/// Determines whether this node is a thread node.
/// </summary>
public bool IsThread {
public virtual bool IsThread {
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 @@ -16,6 +16,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
static class KnownMembers
{
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_CallCount = PropertyOf((CallTreeNode c) => c.CallCount);
public static readonly PropertyInfo CallTreeNode_NameMapping = PropertyOf((CallTreeNode c) => c.NameMapping);
@ -29,8 +30,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -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 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 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 Glob = MethodOf(() => GlobImpl("", ""));

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

@ -105,12 +105,19 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -105,12 +105,19 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
return expression + " AS " + newName;
}
public virtual IQueryable<CallTreeNode> Execute(SQLiteQueryProvider provider)
public IQueryable<CallTreeNode> Execute(SQLiteQueryProvider provider, QueryExecutionOptions options)
{
StringBuilder b = new StringBuilder();
BuildSql(b, new SqlQueryContext(provider));
Console.WriteLine(b.ToString());
return provider.RunSQLNodeList(b.ToString()).AsQueryable();
if (options.HasLoggers)
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();
}
}

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

@ -7,6 +7,8 @@ @@ -7,6 +7,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@ -82,8 +84,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -82,8 +84,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
Translation rules for expression importer:
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.IsThread -> Glob(c.NameMapping.Name, "Thread#*")
s.StartsWith(constantString, StringComparison.Ordinal) -> Glob(s, constantString + "*");
s.StartsWith(constantString, StringComparison.OrdinalIgnoreCase) -> Like(s, constantString + "%");
@ -159,31 +162,43 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -159,31 +162,43 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
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);
Console.WriteLine("Partially evaluated expression: " + expression);
expression = new ConvertToQueryAstVisitor().Visit(expression);
Console.WriteLine("Converted to Query AST: " + expression);
QueryExecutionOptions options = new QueryExecutionOptions();
Expression expression = new ConvertToQueryAstVisitor(options).Visit(partiallyEvaluatedExpression);
// options have been initialized by ConvertToQueryAstVisitor, start logging:
if (options.HasLoggers) {
options.WriteLogLine("Input expression: " + inputExpression);
options.WriteLogLine("Partially evaluated expression: " + partiallyEvaluatedExpression);
options.WriteLogLine("Converted to Query AST: " + 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:
QueryNode query = expression as QueryNode;
if (query != null)
return query.Execute(this);
if (query != null) {
result = query.Execute(this, options);
} else {
// 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) {
expression = Expression.Convert(expression, typeof(object));
}
var lambdaExpression = Expression.Lambda<Func<object>>(expression);
return lambdaExpression.Compile()();
result = lambdaExpression.Compile()();
}
watch.Stop();
options.WriteLogLine("Total query execution time: " + watch.Elapsed);
return result;
}
static bool CanBeEvaluatedStatically(Expression expression)
@ -194,6 +209,13 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -194,6 +209,13 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
#region Convert Expression Tree To Query AST
sealed class ConvertToQueryAstVisitor : System.Linq.Expressions.ExpressionVisitor
{
readonly QueryExecutionOptions options;
public ConvertToQueryAstVisitor(QueryExecutionOptions options)
{
this.options = options;
}
protected override Expression VisitExtension(Expression node)
{
// 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 @@ -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) {
ConstantExpression ce = (ConstantExpression)node.Arguments[1];
if (ce.Type == typeof(int)) {
@ -269,10 +294,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -269,10 +294,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
return base.VisitMethodCall(node);
}
}
#endregion
#region SafeExpressionImporter
sealed class SafeExpressionImporter
{
ParameterExpression callTreeNodeParameter;
readonly ParameterExpression callTreeNodeParameter;
public SafeExpressionImporter(ParameterExpression callTreeNodeParameter)
{
@ -335,6 +362,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -335,6 +362,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
Expression.Property(callTreeNodeParameter, KnownMembers.CallTreeNode_NameMapping),
KnownMembers.NameMapping_ID),
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)
@ -437,21 +468,44 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -437,21 +468,44 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
sealed class ExecuteAllQueriesVisitor : System.Linq.Expressions.ExpressionVisitor
{
readonly SQLiteQueryProvider sqliteProvider;
readonly QueryExecutionOptions options;
public ExecuteAllQueriesVisitor(SQLiteQueryProvider sqliteProvider)
public ExecuteAllQueriesVisitor(SQLiteQueryProvider sqliteProvider, QueryExecutionOptions options)
{
this.sqliteProvider = sqliteProvider;
this.options = options;
}
protected override Expression VisitExtension(Expression node)
{
QueryNode query = node as QueryNode;
if (query != null)
return Expression.Constant(query.Execute(sqliteProvider));
return Expression.Constant(query.Execute(sqliteProvider, options));
else
return base.VisitExtension(node);
}
}
#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 @@ -65,7 +65,7 @@ namespace ICSharpCode.Profiler.Controller.Data
/// </summary>
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 @@ -274,7 +274,7 @@ namespace ICSharpCode.Profiler.Controller.Data
var query = queryProvider.CreateQuery(new MergeByName(new Filter(AllCalls.Instance, DataSetFilter(startIndex, endIndex))));
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;
}

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

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@ -36,21 +37,77 @@ namespace ICSharpCode.Profiler.Controller @@ -36,21 +37,77 @@ namespace ICSharpCode.Profiler.Controller
/// <summary>
/// Returns a CallTreeNode with all merged items.
/// </summary>
public static CallTreeNode Merge(this IEnumerable<CallTreeNode> items)
public static CallTreeNode Merge(this IQueryable<CallTreeNode> items)
{
if (items == null)
throw new ArgumentNullException("items");
Debug.Assert(MethodBase.GetCurrentMethod() == Data.Linq.KnownMembers.Merge);
return items.Provider.Execute<CallTreeNode>(
Expression.Call(Data.Linq.KnownMembers.Queryable_Merge, items.Expression));
}
/// <summary>
/// Returns a CallTreeNode with all merged items.
/// </summary>
public static CallTreeNode Merge(this IEnumerable<CallTreeNode> items)
{
if (items == null)
throw new ArgumentNullException("items");
IQueryable<CallTreeNode> query = items as IQueryable<CallTreeNode>;
if (query != null) {
return query.Provider.Execute<CallTreeNode>(
Expression.Call(Data.Linq.KnownMembers.Merge, query.Expression));
} else {
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");
return items.Provider.CreateQuery<CallTreeNode>(
Expression.Call(Data.Linq.KnownMembers.Queryable_MergeByName, items.Expression));
}
/// <summary>
/// Merges CallTreeNodes with identical name mappings.
/// </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>

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

@ -165,13 +165,19 @@ namespace ICSharpCode.Profiler.Interprocess @@ -165,13 +165,19 @@ namespace ICSharpCode.Profiler.Interprocess
/// <summary>
/// Closes the circular buffer.
/// </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) {
if (isClosed)
return;
isClosed = true;
nonEmptyEvent.Set();
if (waitForReaderThreadExit)
Monitor.Wait(closeLock);
}
nonEmptyEvent.Close();
@ -181,7 +187,7 @@ namespace ICSharpCode.Profiler.Interprocess @@ -181,7 +187,7 @@ namespace ICSharpCode.Profiler.Interprocess
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly")]
void IDisposable.Dispose()
{
Close();
Close(false);
}
#endregion
@ -374,7 +380,7 @@ namespace ICSharpCode.Profiler.Interprocess @@ -374,7 +380,7 @@ namespace ICSharpCode.Profiler.Interprocess
int writeCount = Math.Min(count, writeEndOffset - endOffset);
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);
endOffset += writeCount; // advance endOffset

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

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

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

@ -189,7 +189,11 @@ namespace ICSharpCode.Profiler.Controls @@ -189,7 +189,11 @@ namespace ICSharpCode.Profiler.Controls
this.Translation = new ControlsTranslation();
this.visibleColumnsSelection.ItemsSource = this.gridView.Columns.Select(col => new GridViewColumnModel(col));
this.treeView.SizeChanged += delegate(object sender, SizeChangedEventArgs e) {
this.treeView.SizeChanged += QueryView_SizeChanged;
}
void QueryView_SizeChanged(object sender, SizeChangedEventArgs e)
{
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
@ -211,7 +215,6 @@ namespace ICSharpCode.Profiler.Controls @@ -211,7 +215,6 @@ namespace ICSharpCode.Profiler.Controls
nameColumn.Width = adjustedNameColumnWidth;
}
}
};
}
public void SetRange(int start, int end)
@ -290,6 +293,9 @@ namespace ICSharpCode.Profiler.Controls @@ -290,6 +293,9 @@ namespace ICSharpCode.Profiler.Controls
try {
if (compiler.Compile()) {
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();
return new HierarchyList<CallTreeNodeViewModel>(nodes);
}

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

@ -10,6 +10,7 @@ using System; @@ -10,6 +10,7 @@ using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using ICSharpCode.Profiler.Controller;
using ICSharpCode.Profiler.Controller.Data;
using ICSharpCode.Profiler.Controller.Data.Linq;
using NUnit.Framework;
@ -129,7 +130,7 @@ namespace Profiler.Tests.Controller.Data @@ -129,7 +130,7 @@ namespace Profiler.Tests.Controller.Data
[Test]
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("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; @@ -10,6 +10,7 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using ICSharpCode.Profiler.Interprocess;
using NUnit.Framework;
namespace Profiler.Interprocess
@ -83,7 +84,7 @@ namespace Profiler.Interprocess @@ -83,7 +84,7 @@ namespace Profiler.Interprocess
using (Stream ws = ncb.CreateWritingStream()) {
ws.WriteByte(0x42);
}
using (UnmanagedCircularBuffer ncb2 = UnmanagedCircularBuffer.Open(memoryStart, memoryLength)) {
using (UnmanagedCircularBuffer ncb2 = UnmanagedCircularBuffer.Open(memoryStart)) {
using (Stream rs = ncb2.CreateReadingStream()) {
Assert.AreEqual(0x42, rs.ReadByte());
}
@ -104,7 +105,7 @@ namespace Profiler.Interprocess @@ -104,7 +105,7 @@ namespace Profiler.Interprocess
using (MemoryMappedFile mmf2 = MemoryMappedFile.Open("Local\\TestMemory")) {
using (UnmanagedMemory view2 = mmf1.MapView(0, 1024)) {
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()) {
Assert.AreEqual(0x42, rs.ReadByte());
}

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

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

Loading…
Cancel
Save