Skip to content

Commit 2c52646

Browse files
committed
Pass OutputPath, IntermediateOutputPath, OutDir properties to dotnet command.
Removed `--no-dependencies` build fallback. Release pdb files when they are no longer needed for disassembly. Added v0.14.0 changelog.
1 parent 1d95e55 commit 2c52646

File tree

22 files changed

+242
-125
lines changed

22 files changed

+242
-125
lines changed

docs/_changelog/header/v0.14.0.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
## Highlights
2+
3+
* The default build toolchains have been updated to pass `IntermediateOutputPath`, `OutputPath`, and `OutDir` properties to the `dotnet build` command. This change forces all build outputs to be placed in a new directory generated by BenchmarkDotNet, and fixes many issues that have been reported with builds. You can also access these paths in your own `.csproj` and `.props` from those properties if you need to copy custom files to the output.
4+
5+
## Bug fixes
6+
7+
* Fixed multiple build-related bugs including passing MsBuildArguments and .Net 8's `UseArtifactsOutput`.
8+
9+
## Breaking Changes
10+
11+
* `DotNetCliBuilder` removed `retryFailedBuildWithNoDeps` constructor option.
12+
* `DotNetCliCommand` removed `RetryFailedBuildWithNoDeps` property and `BuildNoRestoreNoDependencies()` and `PublishNoBuildAndNoRestore()` methods (replaced with `PublishNoRestore()`).

src/BenchmarkDotNet.Disassembler.x64/ClrMdV1Disassembler.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ private static DisassembledMethod[] Disassemble(Settings settings, State state)
9393
{
9494
var result = new List<DisassembledMethod>();
9595

96+
using var sourceCodeProvider = new SourceCodeProvider();
9697
while (state.Todo.Count != 0)
9798
{
9899
var methodInfo = state.Todo.Dequeue();
@@ -101,7 +102,7 @@ private static DisassembledMethod[] Disassemble(Settings settings, State state)
101102
continue; // already handled
102103

103104
if (settings.MaxDepth >= methodInfo.Depth)
104-
result.Add(DisassembleMethod(methodInfo, state, settings));
105+
result.Add(DisassembleMethod(methodInfo, state, settings, sourceCodeProvider));
105106
}
106107

107108
return result.ToArray();
@@ -110,7 +111,7 @@ private static DisassembledMethod[] Disassemble(Settings settings, State state)
110111
private static bool CanBeDisassembled(ClrMethod method)
111112
=> !((method.ILOffsetMap is null || method.ILOffsetMap.Length == 0) && (method.HotColdInfo is null || method.HotColdInfo.HotStart == 0 || method.HotColdInfo.HotSize == 0));
112113

113-
private static DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, Settings settings)
114+
private static DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, Settings settings, SourceCodeProvider sourceCodeProvider)
114115
{
115116
var method = methodInfo.Method;
116117

@@ -133,7 +134,7 @@ private static DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State
133134
var uniqueSourceCodeLines = new HashSet<Sharp>(new SharpComparer());
134135
// for getting C# code we always use the original ILOffsetMap
135136
foreach (var map in method.ILOffsetMap.Where(map => map.StartAddress < map.EndAddress && map.ILOffset >= 0).OrderBy(map => map.StartAddress))
136-
foreach (var sharp in SourceCodeProvider.GetSource(method, map))
137+
foreach (var sharp in sourceCodeProvider.GetSource(method, map))
137138
uniqueSourceCodeLines.Add(sharp);
138139

139140
codes.AddRange(uniqueSourceCodeLines);

src/BenchmarkDotNet.Disassembler.x64/SourceCodeProvider.cs

+33-33
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,34 @@
88

99
namespace BenchmarkDotNet.Disassemblers
1010
{
11-
internal static class SourceCodeProvider
11+
// This is taken from the Samples\FileAndLineNumbers projects from microsoft/clrmd,
12+
// and replaces the previously-available SourceLocation functionality.
13+
14+
internal class SourceLocation
1215
{
13-
private static readonly Dictionary<string, string[]> SourceFileCache = new Dictionary<string, string[]>();
16+
public string FilePath;
17+
public int LineNumber;
18+
public int LineNumberEnd;
19+
public int ColStart;
20+
public int ColEnd;
21+
}
22+
23+
internal class SourceCodeProvider : IDisposable
24+
{
25+
private readonly Dictionary<string, string[]> sourceFileCache = new Dictionary<string, string[]>();
26+
private readonly Dictionary<PdbInfo, PdbReader> pdbReaders = new Dictionary<PdbInfo, PdbReader>();
27+
28+
public void Dispose()
29+
{
30+
foreach (var reader in pdbReaders.Values)
31+
{
32+
reader?.Dispose();
33+
}
34+
}
1435

15-
internal static IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map)
36+
internal IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map)
1637
{
17-
var sourceLocation = method.GetSourceLocation(map.ILOffset);
38+
var sourceLocation = GetSourceLocation(method, map.ILOffset);
1839
if (sourceLocation == null)
1940
yield break;
2041

@@ -39,16 +60,16 @@ internal static IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map
3960
}
4061
}
4162

42-
private static string ReadSourceLine(string file, int line)
63+
private string ReadSourceLine(string file, int line)
4364
{
44-
if (!SourceFileCache.TryGetValue(file, out string[] contents))
65+
if (!sourceFileCache.TryGetValue(file, out string[] contents))
4566
{
4667
// sometimes the symbols report some disk location from MS CI machine like "E:\A\_work\308\s\src\mscorlib\shared\System\Random.cs" for .NET Core 2.0
4768
if (!File.Exists(file))
4869
return null;
4970

5071
contents = File.ReadAllLines(file);
51-
SourceFileCache.Add(file, contents);
72+
sourceFileCache.Add(file, contents);
5273
}
5374

5475
return line - 1 < contents.Length
@@ -84,29 +105,8 @@ private static string GetSmartPointer(string sourceLine, int? start, int? end)
84105

85106
return new string(prefix);
86107
}
87-
}
88-
89-
90-
// This is taken from the Samples\FileAndLineNumbers projects from microsoft/clrmd,
91-
// and replaces the previously-available SourceLocation functionality.
92-
93-
internal class SourceLocation
94-
{
95-
public string FilePath;
96-
public int LineNumber;
97-
public int LineNumberEnd;
98-
public int ColStart;
99-
public int ColEnd;
100-
}
101-
102-
internal static class ClrSourceExtensions
103-
{
104-
// TODO Not sure we want this to be a shared dictionary, especially without
105-
// any synchronization. Probably want to put this hanging off the Context
106-
// somewhere, or inside SymbolCache.
107-
private static readonly Dictionary<PdbInfo, PdbReader> s_pdbReaders = new Dictionary<PdbInfo, PdbReader>();
108108

109-
internal static SourceLocation GetSourceLocation(this ClrMethod method, int ilOffset)
109+
internal SourceLocation GetSourceLocation(ClrMethod method, int ilOffset)
110110
{
111111
PdbReader reader = GetReaderForMethod(method);
112112
if (reader == null)
@@ -116,7 +116,7 @@ internal static SourceLocation GetSourceLocation(this ClrMethod method, int ilOf
116116
return FindNearestLine(function, ilOffset);
117117
}
118118

119-
internal static SourceLocation GetSourceLocation(this ClrStackFrame frame)
119+
internal SourceLocation GetSourceLocation(ClrStackFrame frame)
120120
{
121121
PdbReader reader = GetReaderForMethod(frame.Method);
122122
if (reader == null)
@@ -178,15 +178,15 @@ private static int FindIlOffset(ClrStackFrame frame)
178178
return last;
179179
}
180180

181-
private static PdbReader GetReaderForMethod(ClrMethod method)
181+
private PdbReader GetReaderForMethod(ClrMethod method)
182182
{
183183
ClrModule module = method?.Type?.Module;
184184
PdbInfo info = module?.Pdb;
185185

186186
PdbReader? reader = null;
187187
if (info != null)
188188
{
189-
if (!s_pdbReaders.TryGetValue(info, out reader))
189+
if (!pdbReaders.TryGetValue(info, out reader))
190190
{
191191
SymbolLocator locator = GetSymbolLocator(module);
192192
string pdbPath = locator.FindPdb(info);
@@ -207,7 +207,7 @@ private static PdbReader GetReaderForMethod(ClrMethod method)
207207
}
208208
}
209209

210-
s_pdbReaders[info] = reader;
210+
pdbReaders[info] = reader;
211211
}
212212
}
213213

src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ private DisassembledMethod[] Disassemble(Settings settings, State state)
126126
var result = new List<DisassembledMethod>();
127127
DisassemblySyntax syntax = (DisassemblySyntax)Enum.Parse(typeof(DisassemblySyntax), settings.Syntax);
128128

129+
using var sourceCodeProvider = new SourceCodeProvider();
129130
while (state.Todo.Count != 0)
130131
{
131132
var methodInfo = state.Todo.Dequeue();
@@ -134,15 +135,15 @@ private DisassembledMethod[] Disassemble(Settings settings, State state)
134135
continue; // already handled
135136

136137
if (settings.MaxDepth >= methodInfo.Depth)
137-
result.Add(DisassembleMethod(methodInfo, state, settings, syntax));
138+
result.Add(DisassembleMethod(methodInfo, state, settings, syntax, sourceCodeProvider));
138139
}
139140

140141
return result.ToArray();
141142
}
142143

143144
private static bool CanBeDisassembled(ClrMethod method) => method.ILOffsetMap.Length > 0 && method.NativeCode > 0;
144145

145-
private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, Settings settings, DisassemblySyntax syntax)
146+
private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, Settings settings, DisassemblySyntax syntax, SourceCodeProvider sourceCodeProvider)
146147
{
147148
var method = methodInfo.Method;
148149

@@ -165,7 +166,7 @@ private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state,
165166
var uniqueSourceCodeLines = new HashSet<Sharp>(new SharpComparer());
166167
// for getting C# code we always use the original ILOffsetMap
167168
foreach (var map in method.ILOffsetMap.Where(map => map.StartAddress < map.EndAddress && map.ILOffset >= 0).OrderBy(map => map.StartAddress))
168-
foreach (var sharp in SourceCodeProvider.GetSource(method, map))
169+
foreach (var sharp in sourceCodeProvider.GetSource(method, map))
169170
uniqueSourceCodeLines.Add(sharp);
170171

171172
codes.AddRange(uniqueSourceCodeLines);

src/BenchmarkDotNet/Disassemblers/SourceCodeProvider.cs

+23-25
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,21 @@
77

88
namespace BenchmarkDotNet.Disassemblers
99
{
10-
internal static class SourceCodeProvider
10+
internal class SourceCodeProvider : IDisposable
1111
{
12-
private static readonly Dictionary<SourceFile, string[]> SourceFileCache = new Dictionary<SourceFile, string[]>();
13-
private static readonly Dictionary<SourceFile, string> SourceFilePathsCache = new Dictionary<SourceFile, string>();
12+
private readonly Dictionary<SourceFile, string[]> sourceFileCache = new Dictionary<SourceFile, string[]>();
13+
private readonly Dictionary<SourceFile, string> sourceFilePathsCache = new Dictionary<SourceFile, string>();
14+
private readonly Dictionary<PdbInfo, ManagedSymbolModule> pdbReaders = new Dictionary<PdbInfo, ManagedSymbolModule>();
15+
private readonly SymbolReader symbolReader = new SymbolReader(TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath };
1416

15-
internal static IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map)
17+
public void Dispose()
1618
{
17-
var sourceLocation = method.GetSourceLocation(map.ILOffset);
19+
symbolReader.Dispose();
20+
}
21+
22+
internal IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map)
23+
{
24+
var sourceLocation = GetSourceLocation(method, map.ILOffset);
1825
if (sourceLocation == null)
1926
yield break;
2027

@@ -39,12 +46,12 @@ internal static IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map
3946
}
4047
}
4148

42-
private static string GetFilePath(SourceFile sourceFile)
43-
=> SourceFilePathsCache.TryGetValue(sourceFile, out string filePath) ? filePath : sourceFile.Url;
49+
private string GetFilePath(SourceFile sourceFile)
50+
=> sourceFilePathsCache.TryGetValue(sourceFile, out string filePath) ? filePath : sourceFile.Url;
4451

45-
private static string ReadSourceLine(SourceFile file, int line)
52+
private string ReadSourceLine(SourceFile file, int line)
4653
{
47-
if (!SourceFileCache.TryGetValue(file, out string[] contents))
54+
if (!sourceFileCache.TryGetValue(file, out string[] contents))
4855
{
4956
// GetSourceFile method returns path when file is stored on the same machine
5057
// otherwise it downloads it from the Symbol Server and returns the source code ;)
@@ -56,14 +63,14 @@ private static string ReadSourceLine(SourceFile file, int line)
5663
if (File.Exists(wholeFileOrJustPath))
5764
{
5865
contents = File.ReadAllLines(wholeFileOrJustPath);
59-
SourceFilePathsCache.Add(file, wholeFileOrJustPath);
66+
sourceFilePathsCache.Add(file, wholeFileOrJustPath);
6067
}
6168
else
6269
{
6370
contents = wholeFileOrJustPath.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
6471
}
6572

66-
SourceFileCache.Add(file, contents);
73+
sourceFileCache.Add(file, contents);
6774
}
6875

6976
return line - 1 < contents.Length
@@ -99,17 +106,8 @@ private static string GetSmartPointer(string sourceLine, int? start, int? end)
99106

100107
return new string(prefix);
101108
}
102-
}
103-
104-
internal static class ClrSourceExtensions
105-
{
106-
// TODO Not sure we want this to be a shared dictionary, especially without
107-
// any synchronization. Probably want to put this hanging off the Context
108-
// somewhere, or inside SymbolCache.
109-
private static readonly Dictionary<PdbInfo, ManagedSymbolModule> s_pdbReaders = new Dictionary<PdbInfo, ManagedSymbolModule>();
110-
private static readonly SymbolReader symbolReader = new SymbolReader(TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath };
111109

112-
internal static SourceLocation GetSourceLocation(this ClrMethod method, int ilOffset)
110+
internal SourceLocation GetSourceLocation(ClrMethod method, int ilOffset)
113111
{
114112
var reader = GetReaderForMethod(method);
115113
if (reader == null)
@@ -118,7 +116,7 @@ internal static SourceLocation GetSourceLocation(this ClrMethod method, int ilOf
118116
return reader.SourceLocationForManagedCode((uint)method.MetadataToken, ilOffset);
119117
}
120118

121-
internal static SourceLocation GetSourceLocation(this ClrStackFrame frame)
119+
internal SourceLocation GetSourceLocation(ClrStackFrame frame)
122120
{
123121
var reader = GetReaderForMethod(frame.Method);
124122
if (reader == null)
@@ -145,15 +143,15 @@ private static int FindIlOffset(ClrStackFrame frame)
145143
return last;
146144
}
147145

148-
private static ManagedSymbolModule GetReaderForMethod(ClrMethod method)
146+
private ManagedSymbolModule GetReaderForMethod(ClrMethod method)
149147
{
150148
ClrModule module = method?.Type?.Module;
151149
PdbInfo info = module?.Pdb;
152150

153151
ManagedSymbolModule? reader = null;
154152
if (info != null)
155153
{
156-
if (!s_pdbReaders.TryGetValue(info, out reader))
154+
if (!pdbReaders.TryGetValue(info, out reader))
157155
{
158156
string pdbPath = info.Path;
159157
if (pdbPath != null)
@@ -173,7 +171,7 @@ private static ManagedSymbolModule GetReaderForMethod(ClrMethod method)
173171
}
174172
}
175173

176-
s_pdbReaders[info] = reader;
174+
pdbReaders[info] = reader;
177175
}
178176
}
179177

src/BenchmarkDotNet/Jobs/JobExtensions.cs

+5
Original file line numberDiff line numberDiff line change
@@ -436,5 +436,10 @@ private static Job WithCore(this Job job, Action<Job> updateCallback)
436436
updateCallback(newJob);
437437
return newJob;
438438
}
439+
440+
internal static bool HasDynamicBuildCharacteristic(this Job job) =>
441+
job.HasValue(InfrastructureMode.NuGetReferencesCharacteristic)
442+
|| job.HasValue(InfrastructureMode.BuildConfigurationCharacteristic)
443+
|| job.HasValue(InfrastructureMode.ArgumentsCharacteristic);
439444
}
440445
}

src/BenchmarkDotNet/Running/BuildPartition.cs

+19
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
using BenchmarkDotNet.Characteristics;
55
using BenchmarkDotNet.Configs;
66
using BenchmarkDotNet.Environments;
7+
using BenchmarkDotNet.Helpers;
78
using BenchmarkDotNet.Jobs;
9+
using BenchmarkDotNet.Portability;
10+
using BenchmarkDotNet.Toolchains;
811
using BenchmarkDotNet.Toolchains.CsProj;
12+
using BenchmarkDotNet.Toolchains.DotNetCli;
913
using BenchmarkDotNet.Toolchains.MonoWasm;
1014
using BenchmarkDotNet.Toolchains.Roslyn;
1115
using JetBrains.Annotations;
@@ -71,5 +75,20 @@ private static string GetResolvedAssemblyLocation(Assembly assembly) =>
7175
// in case of SingleFile, location.Length returns 0, so we use GetName() and
7276
// manually construct the path.
7377
assembly.Location.Length == 0 ? Path.Combine(AppContext.BaseDirectory, assembly.GetName().Name) : assembly.Location;
78+
79+
internal bool ForcedNoDependenciesForIntegrationTests
80+
{
81+
get
82+
{
83+
if (!XUnitHelper.IsIntegrationTest.Value || !RuntimeInformation.IsNetCore)
84+
return false;
85+
86+
var job = RepresentativeBenchmarkCase.Job;
87+
if (job.GetToolchain().Builder is not DotNetCliBuilder)
88+
return false;
89+
90+
return !job.HasDynamicBuildCharacteristic();
91+
}
92+
}
7493
}
7594
}

0 commit comments

Comments
 (0)