Skip to content

Fix MsBuildArguments #2393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
12 changes: 12 additions & 0 deletions docs/_changelog/header/v0.14.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Highlights

* 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.

## Bug fixes

* Fixed multiple build-related bugs including passing MsBuildArguments and .Net 8's `UseArtifactsOutput`.

## Breaking Changes

* `DotNetCliBuilder` removed `retryFailedBuildWithNoDeps` constructor option.
* `DotNetCliCommand` removed `RetryFailedBuildWithNoDeps` property and `BuildNoRestoreNoDependencies()` and `PublishNoBuildAndNoRestore()` methods (replaced with `PublishNoRestore()`).
7 changes: 4 additions & 3 deletions src/BenchmarkDotNet.Disassembler.x64/ClrMdV1Disassembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ private static DisassembledMethod[] Disassemble(Settings settings, State state)
{
var result = new List<DisassembledMethod>();

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

if (settings.MaxDepth >= methodInfo.Depth)
result.Add(DisassembleMethod(methodInfo, state, settings));
result.Add(DisassembleMethod(methodInfo, state, settings, sourceCodeProvider));
}

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

private static DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, Settings settings)
private static DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, Settings settings, SourceCodeProvider sourceCodeProvider)
{
var method = methodInfo.Method;

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

codes.AddRange(uniqueSourceCodeLines);
Expand Down
66 changes: 33 additions & 33 deletions src/BenchmarkDotNet.Disassembler.x64/SourceCodeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,34 @@

namespace BenchmarkDotNet.Disassemblers
{
internal static class SourceCodeProvider
// This is taken from the Samples\FileAndLineNumbers projects from microsoft/clrmd,
// and replaces the previously-available SourceLocation functionality.

internal class SourceLocation
{
private static readonly Dictionary<string, string[]> SourceFileCache = new Dictionary<string, string[]>();
public string FilePath;
public int LineNumber;
public int LineNumberEnd;
public int ColStart;
public int ColEnd;
}

internal class SourceCodeProvider : IDisposable
{
private readonly Dictionary<string, string[]> sourceFileCache = new Dictionary<string, string[]>();
private readonly Dictionary<PdbInfo, PdbReader> pdbReaders = new Dictionary<PdbInfo, PdbReader>();

public void Dispose()
{
foreach (var reader in pdbReaders.Values)
{
reader?.Dispose();
}
}

internal static IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map)
internal IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map)
{
var sourceLocation = method.GetSourceLocation(map.ILOffset);
var sourceLocation = GetSourceLocation(method, map.ILOffset);
if (sourceLocation == null)
yield break;

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

private static string ReadSourceLine(string file, int line)
private string ReadSourceLine(string file, int line)
{
if (!SourceFileCache.TryGetValue(file, out string[] contents))
if (!sourceFileCache.TryGetValue(file, out string[] contents))
{
// 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
if (!File.Exists(file))
return null;

contents = File.ReadAllLines(file);
SourceFileCache.Add(file, contents);
sourceFileCache.Add(file, contents);
}

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

return new string(prefix);
}
}


// This is taken from the Samples\FileAndLineNumbers projects from microsoft/clrmd,
// and replaces the previously-available SourceLocation functionality.

internal class SourceLocation
{
public string FilePath;
public int LineNumber;
public int LineNumberEnd;
public int ColStart;
public int ColEnd;
}

internal static class ClrSourceExtensions
{
// TODO Not sure we want this to be a shared dictionary, especially without
// any synchronization. Probably want to put this hanging off the Context
// somewhere, or inside SymbolCache.
private static readonly Dictionary<PdbInfo, PdbReader> s_pdbReaders = new Dictionary<PdbInfo, PdbReader>();

internal static SourceLocation GetSourceLocation(this ClrMethod method, int ilOffset)
internal SourceLocation GetSourceLocation(ClrMethod method, int ilOffset)
{
PdbReader reader = GetReaderForMethod(method);
if (reader == null)
Expand All @@ -116,7 +116,7 @@ internal static SourceLocation GetSourceLocation(this ClrMethod method, int ilOf
return FindNearestLine(function, ilOffset);
}

internal static SourceLocation GetSourceLocation(this ClrStackFrame frame)
internal SourceLocation GetSourceLocation(ClrStackFrame frame)
{
PdbReader reader = GetReaderForMethod(frame.Method);
if (reader == null)
Expand Down Expand Up @@ -178,15 +178,15 @@ private static int FindIlOffset(ClrStackFrame frame)
return last;
}

private static PdbReader GetReaderForMethod(ClrMethod method)
private PdbReader GetReaderForMethod(ClrMethod method)
{
ClrModule module = method?.Type?.Module;
PdbInfo info = module?.Pdb;

PdbReader? reader = null;
if (info != null)
{
if (!s_pdbReaders.TryGetValue(info, out reader))
if (!pdbReaders.TryGetValue(info, out reader))
{
SymbolLocator locator = GetSymbolLocator(module);
string pdbPath = locator.FindPdb(info);
Expand All @@ -207,7 +207,7 @@ private static PdbReader GetReaderForMethod(ClrMethod method)
}
}

s_pdbReaders[info] = reader;
pdbReaders[info] = reader;
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ private DisassembledMethod[] Disassemble(Settings settings, State state)
var result = new List<DisassembledMethod>();
DisassemblySyntax syntax = (DisassemblySyntax)Enum.Parse(typeof(DisassemblySyntax), settings.Syntax);

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

if (settings.MaxDepth >= methodInfo.Depth)
result.Add(DisassembleMethod(methodInfo, state, settings, syntax));
result.Add(DisassembleMethod(methodInfo, state, settings, syntax, sourceCodeProvider));
}

return result.ToArray();
}

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

private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, Settings settings, DisassemblySyntax syntax)
private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state, Settings settings, DisassemblySyntax syntax, SourceCodeProvider sourceCodeProvider)
{
var method = methodInfo.Method;

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

codes.AddRange(uniqueSourceCodeLines);
Expand Down
48 changes: 23 additions & 25 deletions src/BenchmarkDotNet/Disassemblers/SourceCodeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@

namespace BenchmarkDotNet.Disassemblers
{
internal static class SourceCodeProvider
internal class SourceCodeProvider : IDisposable
{
private static readonly Dictionary<SourceFile, string[]> SourceFileCache = new Dictionary<SourceFile, string[]>();
private static readonly Dictionary<SourceFile, string> SourceFilePathsCache = new Dictionary<SourceFile, string>();
private readonly Dictionary<SourceFile, string[]> sourceFileCache = new Dictionary<SourceFile, string[]>();
private readonly Dictionary<SourceFile, string> sourceFilePathsCache = new Dictionary<SourceFile, string>();
private readonly Dictionary<PdbInfo, ManagedSymbolModule> pdbReaders = new Dictionary<PdbInfo, ManagedSymbolModule>();
private readonly SymbolReader symbolReader = new SymbolReader(TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath };

internal static IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map)
public void Dispose()
{
var sourceLocation = method.GetSourceLocation(map.ILOffset);
symbolReader.Dispose();
}

internal IEnumerable<Sharp> GetSource(ClrMethod method, ILToNativeMap map)
{
var sourceLocation = GetSourceLocation(method, map.ILOffset);
if (sourceLocation == null)
yield break;

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

private static string GetFilePath(SourceFile sourceFile)
=> SourceFilePathsCache.TryGetValue(sourceFile, out string filePath) ? filePath : sourceFile.Url;
private string GetFilePath(SourceFile sourceFile)
=> sourceFilePathsCache.TryGetValue(sourceFile, out string filePath) ? filePath : sourceFile.Url;

private static string ReadSourceLine(SourceFile file, int line)
private string ReadSourceLine(SourceFile file, int line)
{
if (!SourceFileCache.TryGetValue(file, out string[] contents))
if (!sourceFileCache.TryGetValue(file, out string[] contents))
{
// GetSourceFile method returns path when file is stored on the same machine
// otherwise it downloads it from the Symbol Server and returns the source code ;)
Expand All @@ -56,14 +63,14 @@ private static string ReadSourceLine(SourceFile file, int line)
if (File.Exists(wholeFileOrJustPath))
{
contents = File.ReadAllLines(wholeFileOrJustPath);
SourceFilePathsCache.Add(file, wholeFileOrJustPath);
sourceFilePathsCache.Add(file, wholeFileOrJustPath);
}
else
{
contents = wholeFileOrJustPath.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
}

SourceFileCache.Add(file, contents);
sourceFileCache.Add(file, contents);
}

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

return new string(prefix);
}
}

internal static class ClrSourceExtensions
{
// TODO Not sure we want this to be a shared dictionary, especially without
// any synchronization. Probably want to put this hanging off the Context
// somewhere, or inside SymbolCache.
private static readonly Dictionary<PdbInfo, ManagedSymbolModule> s_pdbReaders = new Dictionary<PdbInfo, ManagedSymbolModule>();
private static readonly SymbolReader symbolReader = new SymbolReader(TextWriter.Null) { SymbolPath = SymbolPath.MicrosoftSymbolServerPath };

internal static SourceLocation GetSourceLocation(this ClrMethod method, int ilOffset)
internal SourceLocation GetSourceLocation(ClrMethod method, int ilOffset)
{
var reader = GetReaderForMethod(method);
if (reader == null)
Expand All @@ -118,7 +116,7 @@ internal static SourceLocation GetSourceLocation(this ClrMethod method, int ilOf
return reader.SourceLocationForManagedCode((uint)method.MetadataToken, ilOffset);
}

internal static SourceLocation GetSourceLocation(this ClrStackFrame frame)
internal SourceLocation GetSourceLocation(ClrStackFrame frame)
{
var reader = GetReaderForMethod(frame.Method);
if (reader == null)
Expand All @@ -145,15 +143,15 @@ private static int FindIlOffset(ClrStackFrame frame)
return last;
}

private static ManagedSymbolModule GetReaderForMethod(ClrMethod method)
private ManagedSymbolModule GetReaderForMethod(ClrMethod method)
{
ClrModule module = method?.Type?.Module;
PdbInfo info = module?.Pdb;

ManagedSymbolModule? reader = null;
if (info != null)
{
if (!s_pdbReaders.TryGetValue(info, out reader))
if (!pdbReaders.TryGetValue(info, out reader))
{
string pdbPath = info.Path;
if (pdbPath != null)
Expand All @@ -173,7 +171,7 @@ private static ManagedSymbolModule GetReaderForMethod(ClrMethod method)
}
}

s_pdbReaders[info] = reader;
pdbReaders[info] = reader;
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/BenchmarkDotNet/Jobs/JobExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,5 +436,10 @@ private static Job WithCore(this Job job, Action<Job> updateCallback)
updateCallback(newJob);
return newJob;
}

internal static bool HasDynamicBuildCharacteristic(this Job job) =>
job.HasValue(InfrastructureMode.NuGetReferencesCharacteristic)
|| job.HasValue(InfrastructureMode.BuildConfigurationCharacteristic)
|| job.HasValue(InfrastructureMode.ArgumentsCharacteristic);
}
}
19 changes: 19 additions & 0 deletions src/BenchmarkDotNet/Running/BuildPartition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
using BenchmarkDotNet.Characteristics;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Toolchains;
using BenchmarkDotNet.Toolchains.CsProj;
using BenchmarkDotNet.Toolchains.DotNetCli;
using BenchmarkDotNet.Toolchains.MonoWasm;
using BenchmarkDotNet.Toolchains.Roslyn;
using JetBrains.Annotations;
Expand Down Expand Up @@ -71,5 +75,20 @@ private static string GetResolvedAssemblyLocation(Assembly assembly) =>
// in case of SingleFile, location.Length returns 0, so we use GetName() and
// manually construct the path.
assembly.Location.Length == 0 ? Path.Combine(AppContext.BaseDirectory, assembly.GetName().Name) : assembly.Location;

internal bool ForcedNoDependenciesForIntegrationTests
{
get
{
if (!XUnitHelper.IsIntegrationTest.Value || !RuntimeInformation.IsNetCore)
return false;

var job = RepresentativeBenchmarkCase.Job;
if (job.GetToolchain().Builder is not DotNetCliBuilder)
return false;

return !job.HasDynamicBuildCharacteristic();
}
}
}
}
Loading