Skip to content

Commit c0d692a

Browse files
committed
install dotnet symbols to get symbols for native runtime parts
1 parent 194f4bf commit c0d692a

File tree

4 files changed

+99
-4
lines changed

4 files changed

+99
-4
lines changed

src/BenchmarkDotNet/Diagnosers/PerfCollectProfiler.cs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@
88
using BenchmarkDotNet.Exporters;
99
using BenchmarkDotNet.Extensions;
1010
using BenchmarkDotNet.Helpers;
11+
using BenchmarkDotNet.Jobs;
1112
using BenchmarkDotNet.Loggers;
1213
using BenchmarkDotNet.Portability;
1314
using BenchmarkDotNet.Reports;
1415
using BenchmarkDotNet.Running;
16+
using BenchmarkDotNet.Toolchains;
17+
using BenchmarkDotNet.Toolchains.CoreRun;
18+
using BenchmarkDotNet.Toolchains.CsProj;
19+
using BenchmarkDotNet.Toolchains.DotNetCli;
20+
using BenchmarkDotNet.Toolchains.NativeAot;
1521
using BenchmarkDotNet.Validators;
1622
using JetBrains.Annotations;
1723
using Mono.Unix.Native;
@@ -28,6 +34,7 @@ public class PerfCollectProfiler : IProfiler
2834
private readonly PerfCollectProfilerConfig config;
2935
private readonly DateTime creationTime = DateTime.Now;
3036
private readonly Dictionary<BenchmarkCase, FileInfo> benchmarkToTraceFile = new ();
37+
private readonly HashSet<string> cliPathWithSymbolsInstalled = new ();
3138
private FileInfo perfCollectFile;
3239
private Process perfCollectProcess;
3340

@@ -101,7 +108,7 @@ private bool TryInstallPerfCollect(ValidationParameters validationParameters)
101108

102109
if (Syscall.chmod(perfCollectFile.FullName, FilePermissions.S_IXUSR) != SuccessExitCode)
103110
{
104-
logger.WriteError($"Unable to make perfcollect script an executable, the last error was: {Mono.Unix.Native.Syscall.GetLastError()}");
111+
logger.WriteError($"Unable to make perfcollect script an executable, the last error was: {Syscall.GetLastError()}");
105112
}
106113
else
107114
{
@@ -130,6 +137,8 @@ private bool TryInstallPerfCollect(ValidationParameters validationParameters)
130137

131138
private Process StartCollection(DiagnoserActionParameters parameters)
132139
{
140+
EnsureDotnetSymbolIsInstalled(parameters);
141+
133142
var traceName = new FileInfo(ArtifactFileNameHelper.GetTraceFilePath(parameters, creationTime, fileExtension: null)).Name;
134143

135144
var start = new ProcessStartInfo
@@ -184,5 +193,82 @@ private void StopCollection(DiagnoserActionParameters parameters)
184193
perfCollectProcess.Dispose();
185194
}
186195
}
196+
197+
private void EnsureDotnetSymbolIsInstalled(DiagnoserActionParameters parameters)
198+
{
199+
string cliPath = parameters.BenchmarkCase.GetToolchain() switch
200+
{
201+
CsProjCoreToolchain core => core.CustomDotNetCliPath,
202+
CoreRunToolchain coreRun => coreRun.CustomDotNetCliPath.FullName,
203+
NativeAotToolchain nativeAot => nativeAot.CustomDotNetCliPath,
204+
_ => null // custom toolchain, dotnet from $PATH will be used
205+
};
206+
207+
if (cliPathWithSymbolsInstalled.Contains(cliPath))
208+
{
209+
return;
210+
}
211+
212+
cliPathWithSymbolsInstalled.Add(cliPath);
213+
214+
ILogger logger = parameters.Config.GetCompositeLogger();
215+
DotNetCliCommand cliCommand = new (
216+
cliPath: cliPath,
217+
arguments: "--info",
218+
generateResult: null,
219+
logger: logger,
220+
buildPartition: null,
221+
environmentVariables: Array.Empty<EnvironmentVariable>(),
222+
timeout: TimeSpan.FromMinutes(3),
223+
logOutput: false);
224+
225+
var dotnetInfoResult = DotNetCliCommandExecutor.Execute(cliCommand);
226+
if (!dotnetInfoResult.IsSuccess)
227+
{
228+
logger.WriteError($"Unable to run `dotnet --info` for `{cliPath}`, dotnet symbol won't be installed");
229+
return;
230+
}
231+
232+
// sth like "Microsoft.NETCore.App 7.0.0-rc.2.22451.11 [/home/adam/projects/performance/tools/dotnet/x64/shared/Microsoft.NETCore.App]"
233+
// or "Microsoft.NETCore.App 7.0.0-rc.1.22423.16 [/usr/share/dotnet/shared/Microsoft.NETCore.App]"
234+
string netCoreAppPath = dotnetInfoResult
235+
.StandardOutput.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
236+
.Where(line => line.EndsWith("Microsoft.NETCore.App]"))
237+
.Select(line => line.Split('[')[1])
238+
.Distinct()
239+
.Single(); // I assume there will be only one such folder
240+
netCoreAppPath = netCoreAppPath.Substring(0, netCoreAppPath.Length - 1); // remove trailing `]`
241+
242+
string[] missingSymbols = Directory.GetFiles(netCoreAppPath, "lib*.so", SearchOption.AllDirectories)
243+
.Where(nativeLibPath => !File.Exists(Path.ChangeExtension(nativeLibPath, "so.dbg")))
244+
.Select(Path.GetDirectoryName)
245+
.Distinct()
246+
.ToArray();
247+
248+
if (!missingSymbols.Any())
249+
{
250+
return; // the symbol files are already where we need them!
251+
}
252+
253+
cliCommand = cliCommand.WithLogOutput(true); // the following commands might take a while and fail, let's log them
254+
255+
// We install the tool in a dedicated directory in order to always use latest version and avoid issues with broken existing configs.
256+
string toolPath = Path.Combine(Path.GetTempPath(), "BenchmarkDotNet", "symbols");
257+
var installResult = DotNetCliCommandExecutor.Execute(cliCommand.WithArguments($"tool install dotnet-symbol --tool-path \"{toolPath}\""));
258+
if (!installResult.IsSuccess)
259+
{
260+
logger.WriteError($"Unable to install dotnet symbol.");
261+
return;
262+
}
263+
264+
foreach (var directoryPath in missingSymbols)
265+
{
266+
DotNetCliCommandExecutor.Execute(cliCommand
267+
.WithCliPath(Path.Combine(toolPath, "dotnet-symbol"))
268+
.WithArguments($"--symbols --output {directoryPath} {directoryPath}/lib*.so"));
269+
}
270+
271+
DotNetCliCommandExecutor.Execute(cliCommand.WithArguments($"tool uninstall dotnet-symbol --tool-path \"{toolPath}\""));
272+
}
187273
}
188274
}

src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommand.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,19 @@ public DotNetCliCommand(string cliPath, string arguments, GenerateResult generat
4444
BuildPartition = buildPartition;
4545
EnvironmentVariables = environmentVariables;
4646
Timeout = timeout;
47-
LogOutput = logOutput || buildPartition.LogBuildOutput;
47+
LogOutput = logOutput || (buildPartition is not null && buildPartition.LogBuildOutput);
4848
RetryFailedBuildWithNoDeps = retryFailedBuildWithNoDeps;
4949
}
5050

51+
public DotNetCliCommand WithLogOutput(bool logOutput)
52+
=> new (CliPath, Arguments, GenerateResult, Logger, BuildPartition, EnvironmentVariables, Timeout, logOutput: logOutput);
53+
5154
public DotNetCliCommand WithArguments(string arguments)
5255
=> new (CliPath, arguments, GenerateResult, Logger, BuildPartition, EnvironmentVariables, Timeout, logOutput: LogOutput);
5356

57+
public DotNetCliCommand WithCliPath(string cliPath)
58+
=> new (cliPath, Arguments, GenerateResult, Logger, BuildPartition, EnvironmentVariables, Timeout, logOutput: LogOutput);
59+
5460
[PublicAPI]
5561
public BuildResult RestoreThenBuild()
5662
{

src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliCommandExecutor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ public static class DotNetCliCommandExecutor
2222
[PublicAPI]
2323
public static DotNetCliCommandResult Execute(DotNetCliCommand parameters)
2424
{
25-
using (var process = new Process { StartInfo = BuildStartInfo(parameters.CliPath, parameters.GenerateResult.ArtifactsPaths.BuildArtifactsDirectoryPath, parameters.Arguments, parameters.EnvironmentVariables) })
25+
using (var process = new Process { StartInfo = BuildStartInfo(parameters.CliPath, parameters.GenerateResult?.ArtifactsPaths.BuildArtifactsDirectoryPath, parameters.Arguments, parameters.EnvironmentVariables) })
2626
using (var outputReader = new AsyncProcessOutputReader(process, parameters.LogOutput, parameters.Logger))
2727
using (new ConsoleExitHandler(process, parameters.Logger))
2828
{
29-
parameters.Logger.WriteLineInfo($"// start {parameters.CliPath ?? "dotnet"} {parameters.Arguments} in {parameters.GenerateResult.ArtifactsPaths.BuildArtifactsDirectoryPath}");
29+
parameters.Logger.WriteLineInfo($"// start {process.StartInfo.FileName} {process.StartInfo.Arguments} in {process.StartInfo.WorkingDirectory}");
3030

3131
var stopwatch = Stopwatch.StartNew();
3232

src/BenchmarkDotNet/Toolchains/NativeAot/NativeAotToolchain.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@ internal NativeAotToolchain(string displayName,
3636
new DotNetCliPublisher(customDotNetCliPath, GetExtraArguments(runtimeIdentifier)),
3737
new Executor())
3838
{
39+
CustomDotNetCliPath = customDotNetCliPath;
3940
}
4041

42+
internal string CustomDotNetCliPath { get; }
43+
4144
public static NativeAotToolchainBuilder CreateBuilder() => NativeAotToolchainBuilder.Create();
4245

4346
public static string GetExtraArguments(string runtimeIdentifier) => $"-r {runtimeIdentifier}";

0 commit comments

Comments
 (0)