Skip to content

Commit e4d37d0

Browse files
Fix native memory profiler (#1451)
* Add BuildResult to DiagnoserResults * Update Microsoft.Diagnostics.Tracing.TraceEvent * Improve common.props * Fix NativeMemoryProfiler for .Net Framework * Fix NativeMemoryProfiler for .Net Core
1 parent c9f1581 commit e4d37d0

File tree

10 files changed

+72
-23
lines changed

10 files changed

+72
-23
lines changed

build/common.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
</PropertyGroup>
2929

3030
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
31-
<AssemblyOriginatorKeyFile>..\..\build\strongNameKey.snk</AssemblyOriginatorKeyFile>
31+
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)strongNameKey.snk</AssemblyOriginatorKeyFile>
3232
<SignAssembly>true</SignAssembly>
3333
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
3434
<GenerateDocumentationFile>true</GenerateDocumentationFile>
@@ -73,4 +73,4 @@
7373
<PrivateAssets>all</PrivateAssets>
7474
</PackageReference>
7575
</ItemGroup>
76-
</Project>
76+
</Project>

src/BenchmarkDotNet.Diagnostics.Windows/BenchmarkDotNet.Diagnostics.Windows.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
<ProjectReference Include="..\BenchmarkDotNet\BenchmarkDotNet.csproj" />
1313
</ItemGroup>
1414
<ItemGroup>
15-
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.49" PrivateAssets="contentfiles;analyzers" />
15+
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.57" PrivateAssets="contentfiles;analyzers" />
1616
</ItemGroup>
17-
</Project>
17+
</Project>

src/BenchmarkDotNet.Diagnostics.Windows/EtwProfilerConfig.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public EtwProfilerConfig(
6262
| ClrTraceEventParser.Keywords.GC
6363
| ClrTraceEventParser.Keywords.Jit
6464
| ClrTraceEventParser.Keywords.JitTracing // for the inlining events
65+
| ClrTraceEventParser.Keywords.JittedMethodILToNativeMap // Fix NativeMemoryProfiler for .Net Framework
6566
| ClrTraceEventParser.Keywords.Loader
6667
| ClrTraceEventParser.Keywords.NGen),
6768
new TraceEventProviderOptions { StacksEnabled = false }), // stacks are too expensive for our purposes

src/BenchmarkDotNet.Diagnostics.Windows/NativeMemoryProfiler.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public IEnumerable<Metric> ProcessResults(DiagnoserResults results)
5555
if (!etwProfiler.BenchmarkToEtlFile.TryGetValue(results.BenchmarkCase, out var traceFilePath))
5656
return Enumerable.Empty<Metric>();
5757

58-
return new NativeMemoryLogParser(traceFilePath, results.BenchmarkCase, logger).Parse();
58+
return new NativeMemoryLogParser(traceFilePath, results.BenchmarkCase, logger, results.BuildResult.ArtifactsPaths.ProgramName).Parse();
5959
}
6060

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

6969
return new EtwProfilerConfig(
70-
providers: Enumerable.Empty<(Guid providerGuid, TraceEventLevel providerLevel, ulong keywords, TraceEventProviderOptions options)>().ToList(),
7170
performExtraBenchmarksRun: true,
7271
kernelKeywords: kernelKeywords,
7372
createHeapSession: true);

src/BenchmarkDotNet.Diagnostics.Windows/Sessions.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public HeapSession(DiagnoserActionParameters details, EtwProfilerConfig config,
2222
{
2323
}
2424

25-
protected override string FileExtension => ".userheap.etl";
25+
protected override string FileExtension => "userheap.etl";
2626

2727
internal override Session EnableProviders()
2828
{
@@ -39,7 +39,7 @@ public UserSession(DiagnoserActionParameters details, EtwProfilerConfig config,
3939
{
4040
}
4141

42-
protected override string FileExtension => ".etl";
42+
protected override string FileExtension => "etl";
4343

4444
internal override Session EnableProviders()
4545
{
@@ -61,7 +61,7 @@ public KernelSession(DiagnoserActionParameters details, EtwProfilerConfig config
6161
{
6262
}
6363

64-
protected override string FileExtension => ".kernel.etl";
64+
protected override string FileExtension => "kernel.etl";
6565

6666
internal override Session EnableProviders()
6767
{
@@ -72,6 +72,8 @@ internal override Session EnableProviders()
7272
if (Details.Config.GetHardwareCounters().Any())
7373
keywords |= KernelTraceEventParser.Keywords.PMCProfile; // Precise Machine Counters
7474

75+
TraceEventSession.StackCompression = true;
76+
7577
try
7678
{
7779
TraceEventSession.EnableKernelProvider(keywords, KernelTraceEventParser.Keywords.Profile);

src/BenchmarkDotNet.Diagnostics.Windows/Tracing/NativeMemoryLogParser.cs

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics.CodeAnalysis;
34
using System.Linq;
45
using BenchmarkDotNet.Columns;
56
using BenchmarkDotNet.Diagnosers;
7+
using BenchmarkDotNet.Engines;
68
using BenchmarkDotNet.Loggers;
79
using BenchmarkDotNet.Reports;
810
using BenchmarkDotNet.Running;
911
using Microsoft.Diagnostics.Tracing.Etlx;
1012
using Microsoft.Diagnostics.Tracing.Parsers.Kernel;
13+
using Microsoft.Diagnostics.Tracing.Stacks;
1114
using Address = System.UInt64;
1215

1316
namespace BenchmarkDotNet.Diagnostics.Windows.Tracing
@@ -23,18 +26,27 @@ public class NativeMemoryLogParser
2326

2427
private readonly ILogger logger;
2528

26-
public NativeMemoryLogParser(string etlFilePath, BenchmarkCase benchmarkCase, ILogger logger)
29+
private readonly string moduleName;
30+
31+
private readonly string functionName;
32+
33+
public NativeMemoryLogParser(string etlFilePath, BenchmarkCase benchmarkCase, ILogger logger,
34+
string programName)
2735
{
2836
this.etlFilePath = etlFilePath;
2937
this.benchmarkCase = benchmarkCase;
3038
this.logger = logger;
39+
40+
moduleName = programName;
41+
functionName = nameof(EngineParameters.WorkloadActionUnroll);
3142
}
3243

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

4052
var bdnEventsParser = new EngineEventLogParser(eventSource);
@@ -77,6 +89,14 @@ public IEnumerable<Metric> Parse()
7789
return;
7890
}
7991

92+
var call = data.CallStackIndex();
93+
var frameIndex = stackSource.GetCallStack(call, data);
94+
95+
if (!IsCallStackIn(frameIndex))
96+
{
97+
return;
98+
}
99+
80100
var allocs = lastHeapAllocs;
81101
if (data.HeapHandle != lastHeapHandle)
82102
{
@@ -91,7 +111,27 @@ public IEnumerable<Metric> Parse()
91111
nativeLeakSize += data.AllocSize;
92112
totalAllocation += data.AllocSize;
93113
}
114+
115+
bool IsCallStackIn(StackSourceCallStackIndex index)
116+
{
117+
while (index != StackSourceCallStackIndex.Invalid)
118+
{
119+
var frame = stackSource.GetFrameIndex(index);
120+
var name = stackSource.GetFrameName(frame, false);
121+
122+
if (name.StartsWith(moduleName, StringComparison.Ordinal) &&
123+
name.IndexOf(functionName, StringComparison.Ordinal) > 0)
124+
{
125+
return true;
126+
}
127+
128+
index = stackSource.GetCallerIndex(index);
129+
}
130+
131+
return false;
132+
}
94133
};
134+
95135
heapParser.HeapTraceFree += delegate(HeapFreeTraceData data)
96136
{
97137
if (!start)
@@ -112,6 +152,7 @@ public IEnumerable<Metric> Parse()
112152
allocs.Remove(data.FreeAddress);
113153
}
114154
};
155+
115156
heapParser.HeapTraceReAlloc += delegate(HeapReallocTraceData data)
116157
{
117158
if (!start)
@@ -132,22 +173,23 @@ public IEnumerable<Metric> Parse()
132173
allocs = CreateHeapCache(data.HeapHandle, heaps, ref lastHeapAllocs, ref lastHeapHandle);
133174
}
134175

135-
// This is a clone of the Free code
136176
if (allocs.TryGetValue(data.OldAllocAddress, out long alloc))
137177
{
178+
// Free
138179
nativeLeakSize -= alloc;
139180

140181
allocs.Remove(data.OldAllocAddress);
141-
}
142182

143-
// This is a clone of the Alloc code (sigh don't clone code)
144-
allocs[data.NewAllocAddress] = data.NewAllocSize;
183+
// Alloc
184+
allocs[data.NewAllocAddress] = data.NewAllocSize;
145185

146-
checked
147-
{
148-
nativeLeakSize += data.NewAllocSize;
186+
checked
187+
{
188+
nativeLeakSize += data.NewAllocSize;
189+
}
149190
}
150191
};
192+
151193
heapParser.HeapTraceDestroy += delegate(HeapTraceData data)
152194
{
153195
if (!start)

src/BenchmarkDotNet/BenchmarkDotNet.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.2" />
2828
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.10.0" />
2929
<PackageReference Include="Microsoft.Diagnostics.NETCore.Client" Version="0.2.61701" />
30-
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.49" PrivateAssets="contentfiles;analyzers" />
30+
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.57" PrivateAssets="contentfiles;analyzers" />
3131
</ItemGroup>
3232
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
3333
<ProjectReference Include="..\BenchmarkDotNet.Disassembler.x64\BenchmarkDotNet.Disassembler.x64.csproj">

src/BenchmarkDotNet/Diagnosers/DiagnoserResults.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
using BenchmarkDotNet.Engines;
22
using BenchmarkDotNet.Running;
3+
using BenchmarkDotNet.Toolchains.Results;
34

45
namespace BenchmarkDotNet.Diagnosers
56
{
67
public class DiagnoserResults
78
{
8-
public DiagnoserResults(BenchmarkCase benchmarkCase, long totalOperations, GcStats gcStats, ThreadingStats threadingStats)
9+
public DiagnoserResults(BenchmarkCase benchmarkCase, long totalOperations, GcStats gcStats,
10+
ThreadingStats threadingStats, BuildResult buildResult)
911
{
1012
BenchmarkCase = benchmarkCase;
1113
TotalOperations = totalOperations;
1214
GcStats = gcStats;
1315
ThreadingStats = threadingStats;
16+
BuildResult = buildResult;
1417
}
1518

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

2225
public ThreadingStats ThreadingStats { get; }
26+
27+
public BuildResult BuildResult { get; }
2328
}
2429
}

src/BenchmarkDotNet/Running/BenchmarkRunnerClean.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ private static (bool success, List<ExecuteResult> executeResults, GcStats gcStat
428428

429429
metrics.AddRange(
430430
noOverheadCompositeDiagnoser.ProcessResults(
431-
new DiagnoserResults(benchmarkCase, measurements.Where(measurement => measurement.IsWorkload()).Sum(m => m.Operations), gcStats, threadingStats)));
431+
new DiagnoserResults(benchmarkCase, measurements.Where(measurement => measurement.IsWorkload()).Sum(m => m.Operations), gcStats, threadingStats, buildResult)));
432432
}
433433

434434
if (autoLaunchCount && launchIndex == 2 && analyzeRunToRunVariance)
@@ -467,7 +467,7 @@ private static (bool success, List<ExecuteResult> executeResults, GcStats gcStat
467467

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

472472
logger.WriteLine();
473473
}

tests/BenchmarkDotNet.IntegrationTests/ExporterIOTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private Summary GetMockSummary(string resultsDirectoryPath, IConfig config, para
134134

135135
private class MockExporter : ExporterBase
136136
{
137-
public int ExportCount = 0;
137+
public int ExportCount;
138138

139139
public override void ExportToLog(Summary summary, ILogger logger)
140140
{

0 commit comments

Comments
 (0)