Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build/common.props
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<AssemblyOriginatorKeyFile>..\..\build\strongNameKey.snk</AssemblyOriginatorKeyFile>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)strongNameKey.snk</AssemblyOriginatorKeyFile>
<SignAssembly>true</SignAssembly>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down Expand Up @@ -73,4 +73,4 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
<ProjectReference Include="..\BenchmarkDotNet\BenchmarkDotNet.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.49" PrivateAssets="contentfiles;analyzers" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.54" PrivateAssets="contentfiles;analyzers" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public EtwProfilerConfig(
| ClrTraceEventParser.Keywords.GC
| ClrTraceEventParser.Keywords.Jit
| ClrTraceEventParser.Keywords.JitTracing // for the inlining events
| ClrTraceEventParser.Keywords.JittedMethodILToNativeMap // Fix NativeMemoryProfiler for .Net Framework
| ClrTraceEventParser.Keywords.Loader
| ClrTraceEventParser.Keywords.NGen),
new TraceEventProviderOptions { StacksEnabled = false }), // stacks are too expensive for our purposes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public IEnumerable<Metric> ProcessResults(DiagnoserResults results)
if (!etwProfiler.BenchmarkToEtlFile.TryGetValue(results.BenchmarkCase, out var traceFilePath))
return Enumerable.Empty<Metric>();

return new NativeMemoryLogParser(traceFilePath, results.BenchmarkCase, logger).Parse();
return new NativeMemoryLogParser(traceFilePath, results.BenchmarkCase, logger, results.BuildResult.ArtifactsPaths.ProgramName).Parse();
}

public IEnumerable<ValidationError> Validate(ValidationParameters validationParameters) => etwProfiler.Validate(validationParameters);
Expand All @@ -67,7 +67,6 @@ private static EtwProfilerConfig CreateDefaultConfig()
var kernelKeywords = KernelTraceEventParser.Keywords.VirtualAlloc | KernelTraceEventParser.Keywords.VAMap;

return new EtwProfilerConfig(
providers: Enumerable.Empty<(Guid providerGuid, TraceEventLevel providerLevel, ulong keywords, TraceEventProviderOptions options)>().ToList(),
performExtraBenchmarksRun: true,
kernelKeywords: kernelKeywords,
createHeapSession: true);
Expand Down
8 changes: 5 additions & 3 deletions src/BenchmarkDotNet.Diagnostics.Windows/Sessions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public HeapSession(DiagnoserActionParameters details, EtwProfilerConfig config,
{
}

protected override string FileExtension => ".userheap.etl";
protected override string FileExtension => "userheap.etl";

internal override Session EnableProviders()
{
Expand All @@ -38,7 +38,7 @@ public UserSession(DiagnoserActionParameters details, EtwProfilerConfig config,
{
}

protected override string FileExtension => ".etl";
protected override string FileExtension => "etl";

internal override Session EnableProviders()
{
Expand All @@ -60,7 +60,7 @@ public KernelSession(DiagnoserActionParameters details, EtwProfilerConfig config
{
}

protected override string FileExtension => ".kernel.etl";
protected override string FileExtension => "kernel.etl";

internal override Session EnableProviders()
{
Expand All @@ -71,6 +71,8 @@ internal override Session EnableProviders()
if (Details.Config.GetHardwareCounters().Any())
keywords |= KernelTraceEventParser.Keywords.PMCProfile; // Precise Machine Counters

TraceEventSession.StackCompression = true;

try
{
TraceEventSession.EnableKernelProvider(keywords, KernelTraceEventParser.Keywords.Profile);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Diagnosers;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using Microsoft.Diagnostics.Tracing.Etlx;
using Microsoft.Diagnostics.Tracing.Parsers.Kernel;
using Microsoft.Diagnostics.Tracing.Stacks;
using Address = System.UInt64;

namespace BenchmarkDotNet.Diagnostics.Windows.Tracing
Expand All @@ -23,18 +27,32 @@ public class NativeMemoryLogParser

private readonly ILogger logger;

public NativeMemoryLogParser(string etlFilePath, BenchmarkCase benchmarkCase, ILogger logger)
private readonly (string module, Regex function)[] benchmarkedFunctionStacks;

public NativeMemoryLogParser(string etlFilePath, BenchmarkCase benchmarkCase, ILogger logger,
string programName)
{
this.etlFilePath = etlFilePath;
this.benchmarkCase = benchmarkCase;
this.logger = logger;

var module = Path.GetFileNameWithoutExtension(benchmarkCase.Descriptor.WorkloadMethod.Module.Name);
var function = new Regex($@"{benchmarkCase.Descriptor.WorkloadMethod.DeclaringType?.FullName}\.{benchmarkCase.Descriptor.WorkloadMethod.Name}\(");

benchmarkedFunctionStacks = new (string module, Regex function)[]
{
(module, function),
(programName, new Regex(@"BenchmarkDotNet\.Autogenerated\.Runnable_[0-9]+.WorkloadActionNoUnroll\(")),
(programName, new Regex(@"BenchmarkDotNet\.Autogenerated\.Runnable_[0-9]+.WorkloadActionUnroll\("))
};
}

//Code is inspired by https://github.com/Microsoft/perfview/blob/master/src/PerfView/PerfViewData.cs#L5719-L5944
public IEnumerable<Metric> Parse()
{
using (var traceLog = new TraceLog(TraceLog.CreateFromEventTraceLogFile(etlFilePath)))
{
var stackSource = new MutableTraceEventStackSource(traceLog);
var eventSource = traceLog.Events.GetSource();

var bdnEventsParser = new EngineEventLogParser(eventSource);
Expand Down Expand Up @@ -77,6 +95,14 @@ public IEnumerable<Metric> Parse()
return;
}

var call = data.CallStackIndex();
var frameIndex = stackSource.GetCallStack(call, data);

if (!IsCallStackIn(frameIndex))
{
return;
}

var allocs = lastHeapAllocs;
if (data.HeapHandle != lastHeapHandle)
{
Expand All @@ -91,7 +117,27 @@ public IEnumerable<Metric> Parse()
nativeLeakSize += data.AllocSize;
totalAllocation += data.AllocSize;
}

bool IsCallStackIn(StackSourceCallStackIndex index)
{
var frame = stackSource.GetFrameIndex(index);
var name = stackSource.GetFrameName(frame, false);
var stack = name.Split(new []{'!'}, StringSplitOptions.RemoveEmptyEntries);
if (stack.Length == 2 &&
benchmarkedFunctionStacks.Any(s=>
string.Equals(stack[0], s.module, StringComparison.OrdinalIgnoreCase) &&
s.function.IsMatch(stack[1])))
{
return true;
}

var callerIndex = stackSource.GetCallerIndex(index);

return callerIndex != StackSourceCallStackIndex.Invalid
&& IsCallStackIn(callerIndex);
}
};

heapParser.HeapTraceFree += delegate(HeapFreeTraceData data)
{
if (!start)
Expand All @@ -112,6 +158,7 @@ public IEnumerable<Metric> Parse()
allocs.Remove(data.FreeAddress);
}
};

heapParser.HeapTraceReAlloc += delegate(HeapReallocTraceData data)
{
if (!start)
Expand All @@ -132,22 +179,23 @@ public IEnumerable<Metric> Parse()
allocs = CreateHeapCache(data.HeapHandle, heaps, ref lastHeapAllocs, ref lastHeapHandle);
}

// This is a clone of the Free code
if (allocs.TryGetValue(data.OldAllocAddress, out long alloc))
{
// Free
nativeLeakSize -= alloc;

allocs.Remove(data.OldAllocAddress);
}

// This is a clone of the Alloc code (sigh don't clone code)
allocs[data.NewAllocAddress] = data.NewAllocSize;
// Alloc
allocs[data.NewAllocAddress] = data.NewAllocSize;

checked
{
nativeLeakSize += data.NewAllocSize;
checked
{
nativeLeakSize += data.NewAllocSize;
}
}
};

heapParser.HeapTraceDestroy += delegate(HeapTraceData data)
{
if (!start)
Expand Down
2 changes: 1 addition & 1 deletion src/BenchmarkDotNet/BenchmarkDotNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.10.0" />
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.61701" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.49" PrivateAssets="contentfiles;analyzers" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.54" PrivateAssets="contentfiles;analyzers" />
</ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<ProjectReference Include="..\BenchmarkDotNet.Disassembler.x64\BenchmarkDotNet.Disassembler.x64.csproj">
Expand Down
7 changes: 6 additions & 1 deletion src/BenchmarkDotNet/Diagnosers/DiagnoserResults.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.Results;

namespace BenchmarkDotNet.Diagnosers
{
public class DiagnoserResults
{
public DiagnoserResults(BenchmarkCase benchmarkCase, long totalOperations, GcStats gcStats, ThreadingStats threadingStats)
public DiagnoserResults(BenchmarkCase benchmarkCase, long totalOperations, GcStats gcStats,
ThreadingStats threadingStats, BuildResult buildResult)
{
BenchmarkCase = benchmarkCase;
TotalOperations = totalOperations;
GcStats = gcStats;
ThreadingStats = threadingStats;
BuildResult = buildResult;
}

public BenchmarkCase BenchmarkCase { get; }
Expand All @@ -20,5 +23,7 @@ public DiagnoserResults(BenchmarkCase benchmarkCase, long totalOperations, GcSta
public GcStats GcStats { get; }

public ThreadingStats ThreadingStats { get; }

public BuildResult BuildResult { get; }
}
}
4 changes: 2 additions & 2 deletions src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ private static (bool success, List<ExecuteResult> executeResults, GcStats gcStat

metrics.AddRange(
noOverheadCompositeDiagnoser.ProcessResults(
new DiagnoserResults(benchmarkCase, measurements.Where(measurement => measurement.IsWorkload()).Sum(m => m.Operations), gcStats, threadingStats)));
new DiagnoserResults(benchmarkCase, measurements.Where(measurement => measurement.IsWorkload()).Sum(m => m.Operations), gcStats, threadingStats, buildResult)));
}

if (autoLaunchCount && launchIndex == 2 && analyzeRunToRunVariance)
Expand Down Expand Up @@ -466,7 +466,7 @@ private static (bool success, List<ExecuteResult> executeResults, GcStats gcStat

metrics.AddRange(
extraRunCompositeDiagnoser.ProcessResults(
new DiagnoserResults(benchmarkCase, allRuns.Where(measurement => measurement.IsWorkload()).Sum(m => m.Operations), gcStats, threadingStats)));
new DiagnoserResults(benchmarkCase, allRuns.Where(measurement => measurement.IsWorkload()).Sum(m => m.Operations), gcStats, threadingStats, buildResult)));

logger.WriteLine();
}
Expand Down