diff options
author | Tomáš Rylek <trylek@microsoft.com> | 2021-01-16 13:24:40 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-01-16 13:24:40 +0300 |
commit | ea4f5f2c501599b9397d14274bbfe5513b0ff03b (patch) | |
tree | 652151c60bc3c2ea4eeaa3e890e8c5b3ce4d146d /src | |
parent | 3aa65f262ac54fa8417c5e9fc08a0c2b3e14ea46 (diff) |
Initial PDB / PerfMap support in Crossgen2 + System.Private.CoreLib switchover to use Crossgen2 (#47019)
I have moved the PDB writer code to a new assembly
ILCompiler.Diagnostics so that it can be reused by Crossgen2.
I have also added an initial trivial implementation of the
PerfMap writer.
Thanks
Tomas
Diffstat (limited to 'src')
28 files changed, 753 insertions, 252 deletions
diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj index fadc1f514c8..61c31d77ab9 100644 --- a/src/coreclr/crossgen-corelib.proj +++ b/src/coreclr/crossgen-corelib.proj @@ -8,7 +8,7 @@ AfterTargets="Build"> <PropertyGroup> <!-- Default for using Crossgen2 when not set externally --> - <UseCrossgen2 Condition="'$(UseCrossgen2)' == ''">false</UseCrossgen2> + <UseCrossgen2 Condition="'$(UseCrossgen2)' == ''">true</UseCrossgen2> <OSPlatformConfig>$(TargetOS).$(TargetArchitecture).$(Configuration)</OSPlatformConfig> <RootBinDir>$([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts'))</RootBinDir> @@ -65,27 +65,22 @@ <CrossGenDllCmd>$(CrossGenDllCmd) $(CoreLibInputPath)</CrossGenDllCmd> </PropertyGroup> + <PropertyGroup Condition="$(BuildPdb)"> + <CrossGenDllCmd>$(CrossGenDllCmd) --pdb --pdb-path:$([MSBuild]::NormalizePath('$(BinDir)', 'PDB'))</CrossGenDllCmd> + </PropertyGroup> + + <PropertyGroup Condition="$(BuildPerfMap)"> + <CrossGenDllCmd>$(CrossGenDllCmd) --perfmap --perfmap-path:$(BinDir)</CrossGenDllCmd> + </PropertyGroup> + <PropertyGroup Condition="'$(UseCrossgen2)' != 'true'"> + <VsSetupCmd>call $([MSBuild]::NormalizePath('$(RepoRoot)', 'src', 'coreclr', 'setup_vs_tools.cmd')) &&</VsSetupCmd> + <CrossGenDllCmd>$(CrossGen1Cmd) /out "$(CoreLibOutputPath)"</CrossGenDllCmd> <CrossGenDllCmd>$(CrossGenDllCmd) "$(CoreLibInputPath)"</CrossGenDllCmd> - </PropertyGroup> - <!-- For now we're using Crossgen1 for generating the perf map as Crossgen2 doesn't yet implement it: --> - <!-- https://github.com/dotnet/runtime/issues/44123 --> - <PropertyGroup Condition="$(BuildPerfMap)"> <CrossGenPerfMapCmd>$(CrossGen1Cmd) /CreatePerfMap "$(BinDir)"</CrossGenPerfMapCmd> <CrossGenPerfMapCmd>$(CrossGenPerfMapCmd) "$(CoreLibOutputPath)"</CrossGenPerfMapCmd> - </PropertyGroup> - - <PropertyGroup Condition="$(BuildPdb)"> - <CrossGenPdbCmd>$(DotNetCli) $([MSBuild]::NormalizePath('$(BinDir)', 'r2rdump', 'r2rdump.dll'))</CrossGenPdbCmd> - <CrossGenPdbCmd>$(CrossGenPdbCmd) --create-pdb</CrossGenPdbCmd> - <CrossGenPdbCmd>$(CrossGenPdbCmd) --pdb-path:$([MSBuild]::NormalizePath('$(BinDir)', 'PDB'))</CrossGenPdbCmd> - <CrossGenPdbCmd>$(CrossGenPdbCmd) --in:$(CoreLibOutputPath)</CrossGenPdbCmd> - </PropertyGroup> - - <PropertyGroup Condition="$(BuildPdb) and '$(UseCrossgen2)' != 'true'"> - <VsSetupCmd>call $([MSBuild]::NormalizePath('$(RepoRoot)', 'src', 'coreclr', 'setup_vs_tools.cmd')) &&</VsSetupCmd> <CrossGenPdbCmd>$(VsSetupCmd) $(CrossGen1Cmd) /CreatePdb "$([MSBuild]::NormalizePath('$(BinDir)', 'PDB'))"</CrossGenPdbCmd> <CrossGenPdbCmd>$(CrossGenPdbCmd) "$(CoreLibOutputPath)"</CrossGenPdbCmd> @@ -97,11 +92,11 @@ <Message Condition="$(BuildPdb)" Importance="High" Text="$(CrossGenPdbCmd)" /> - <Exec Condition="$(BuildPdb)" Command="$(CrossGenPdbCmd)" /> + <Exec Condition="$(BuildPdb) and '$(CrossGenPdbCmd)' != ''" Command="$(CrossGenPdbCmd)" /> <Message Condition="$(BuildPerfMap)" Importance="High" Text="$(CrossGenPerfMapCmd)" /> - <Exec Condition="$(BuildPerfMap)" Command="$(CrossGenPerfMapCmd)" /> + <Exec Condition="$(BuildPerfMap) and '$(CrossGenPerfMapCmd)' != ''" Command="$(CrossGenPerfMapCmd)" /> <Copy Condition="!$(BuildDll)" SourceFiles="$(CoreLibInputPath)" DestinationFiles="$(CoreLibOutputPath)" UseHardlinksIfPossible="true" /> diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj new file mode 100644 index 00000000000..8c9419db564 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj @@ -0,0 +1,20 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <AssemblyName>ILCompiler.Diagnostics</AssemblyName> + <OutputType>Library</OutputType> + <IsDotNetFrameworkProductAssembly>true</IsDotNetFrameworkProductAssembly> + <TargetFramework>$(NetCoreAppToolCurrent)</TargetFramework> + <DefineConstants>READYTORUN;$(DefineConstants)</DefineConstants> + <Platforms>x64;x86;arm;arm64</Platforms> + <PlatformTarget>AnyCPU</PlatformTarget> + <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> + + <!-- We're binplacing these into an existing publish layout so that F5 build in VS updates + the same bits tests expect to see in artifacts/crossgen2. That way we never need to wonder which + binaries are up to date and which are stale. --> + <GenerateDependencyFile>false</GenerateDependencyFile> + <Configurations>Debug;Release;Checked</Configurations> + </PropertyGroup> + +</Project> diff --git a/src/coreclr/tools/r2rdump/ISymNGenWriter.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ISymNGenWriter.cs index 4c4b6b97d60..4c4b6b97d60 100644 --- a/src/coreclr/tools/r2rdump/ISymNGenWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ISymNGenWriter.cs diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/MethodInfo.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/MethodInfo.cs new file mode 100644 index 00000000000..0f1b6dec57d --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/MethodInfo.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.Diagnostics +{ + public struct MethodInfo + { + public string AssemblyName; + public uint MethodToken; + public uint HotRVA; + public uint HotLength; + public string Name; + public uint ColdRVA; + public uint ColdLength; + } +} diff --git a/src/coreclr/tools/r2rdump/PdbWriter.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs index 99216ab6736..620fb783441 100644 --- a/src/coreclr/tools/r2rdump/PdbWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs @@ -10,7 +10,7 @@ using System.Text; using Microsoft.DiaSymReader; -namespace ILCompiler.PdbWriter +namespace ILCompiler.Diagnostics { // NGEN always generates PDBs with public symbols lists (so tools can map IP ranges to // methods). This bitmask indicates what extra info should be added to the PDB @@ -23,20 +23,6 @@ namespace ILCompiler.PdbWriter kPDBLines = 0x00000001, }; - struct MethodInfo - { - public string AssemblyName; - public uint MethodToken; - public uint HotRVA; - public string Name; - public uint ColdRVA; - } - - interface IModuleData - { - IEnumerable<MethodInfo> Methods { get; } - } - public enum SymChecksumType : byte { None = 0, // indicates no checksum is available @@ -85,7 +71,7 @@ namespace ILCompiler.PdbWriter } } - class PdbWriter + public class PdbWriter { string _pdbPath; PDBExtraData _pdbExtraData; @@ -204,26 +190,33 @@ namespace ILCompiler.PdbWriter throw new NotImplementedException(); } + string dllNameWithoutExtension = Path.GetFileNameWithoutExtension(dllPath); + _pdbFilePath = Path.Combine(_pdbPath, dllNameWithoutExtension + ".pdb"); + string originalDllPath = dllPath; // Currently DiaSymReader does not work properly generating NGEN PDBS unless // the DLL whose PDB is being generated ends in .ni.*. Unfortunately, readyToRun // images do not follow this convention and end up producing bad PDBS. To fix // this (without changing diasymreader.dll which ships indepdendently of .NET Core) - // we copy the file to somethign with this convention before generating the PDB + // we copy the file to something with this convention before generating the PDB // and delete it when we are done. if (!dllPath.EndsWith(".ni.dll", StringComparison.OrdinalIgnoreCase) && !dllPath.EndsWith(".ni.exe", StringComparison.OrdinalIgnoreCase)) { - _tempSourceDllName = Path.Combine(Path.GetDirectoryName(dllPath), Path.GetFileNameWithoutExtension(dllPath) + ".ni" + Path.GetExtension(dllPath)); - File.Copy(dllPath, _tempSourceDllName); + _tempSourceDllName = Path.Combine(Path.GetDirectoryName(dllPath), dllNameWithoutExtension + ".ni" + Path.GetExtension(dllPath)); + File.Copy(dllPath, _tempSourceDllName, overwrite: true); dllPath = _tempSourceDllName; + _pdbFilePath = Path.Combine(_pdbPath, dllNameWithoutExtension + ".ni.pdb"); } - _ngenWriter = CreateNGenWriter(dllPath, _pdbPath + "\\"); + // Delete any preexisting PDB file upfront otherwise CreateNGenWriter silently opens it + File.Delete(_pdbFilePath); + + _ngenWriter = CreateNGenWriter(dllPath, _pdbFilePath); { - // PDB file is now created. Get its path and initialize _pdbFilePath so the PDB file - // can be deleted if we don't make it successfully to the end + // PDB file is now created. Get its path and update _pdbFilePath so the PDB file + // can be deleted if we don't make it successfully to the end. StringBuilder pdbFilePathBuilder = new StringBuilder(); pdbFilePathBuilder.Capacity = 1024; _ngenWriter.QueryPDBNameExW(pdbFilePathBuilder, new IntPtr(pdbFilePathBuilder.Capacity)); diff --git a/src/coreclr/tools/aot/ILCompiler.Diagnostics/PerfMapWriter.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PerfMapWriter.cs new file mode 100644 index 00000000000..4696bc9e2d1 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PerfMapWriter.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; + +namespace ILCompiler.Diagnostics +{ + public class PerfMapWriter + { + private TextWriter _writer; + + private PerfMapWriter(TextWriter writer) + { + _writer = writer; + } + + public static void Write(string perfMapFileName, IEnumerable<MethodInfo> methods) + { + using (TextWriter writer = new StreamWriter(perfMapFileName)) + { + PerfMapWriter perfMapWriter = new PerfMapWriter(writer); + foreach (MethodInfo methodInfo in methods) + { + if (methodInfo.HotRVA != 0 && methodInfo.HotLength != 0) + { + perfMapWriter.WriteLine(methodInfo.Name, methodInfo.HotRVA, methodInfo.HotLength); + } + if (methodInfo.ColdRVA != 0 && methodInfo.ColdLength != 0) + { + perfMapWriter.WriteLine(methodInfo.Name, methodInfo.ColdRVA, methodInfo.ColdLength); + } + } + } + } + + private void WriteLine(string methodName, uint rva, uint length) + { + _writer.WriteLine($@"{rva:X8} {length:X2} {methodName}"); + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs index df25e031078..47976ee402a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/CodeGen/ReadyToRunObjectWriter.cs @@ -11,6 +11,7 @@ using System.Reflection.PortableExecutable; using ILCompiler.DependencyAnalysis.ReadyToRun; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Diagnostics; using ILCompiler.PEWriter; using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; @@ -50,25 +51,56 @@ namespace ILCompiler.DependencyAnalysis private readonly IEnumerable<DependencyNode> _nodes; /// <summary> + /// Set to non-null when the executable generator should output a map or symbol file. + /// </summary> + private readonly OutputInfoBuilder _outputInfoBuilder; + + /// <summary> /// Set to non-null when the executable generator should output a map file. /// </summary> private readonly MapFileBuilder _mapFileBuilder; + + /// <summary> + /// Set to non-null when generating symbol info (PDB / PerfMap). + /// </summary> + private readonly SymbolFileBuilder _symbolFileBuilder; + /// <summary> /// True when the map file builder should emit a textual map file /// </summary> private bool _generateMapFile; + /// <summary> /// True when the map file builder should emit a CSV formatted map file /// </summary> private bool _generateMapCsvFile; /// <summary> + /// True when the map file builder should emit a PDB symbol file (only supported on Windows) + /// </summary> + private bool _generatePdbFile; + + /// <summary> + /// Explicit specification of the output PDB path + /// </summary> + private string _pdbPath; + + /// <summary> + /// True when the map file builder should emit a PerfMap file + /// </summary> + private bool _generatePerfMapFile; + + /// <summary> + /// Explicit specification of the output PerfMap path + /// </summary> + private string _perfMapPath; + + /// <summary> /// If non-zero, the PE file will be laid out such that it can naturally be mapped with a higher alignment than 4KB. /// This is used to support loading via large pages on Linux. /// </summary> private readonly int _customPESectionAlignment; - #if DEBUG private struct NodeInfo { @@ -87,7 +119,18 @@ namespace ILCompiler.DependencyAnalysis Dictionary<string, NodeInfo> _previouslyWrittenNodeNames = new Dictionary<string, NodeInfo>(); #endif - public ReadyToRunObjectWriter(string objectFilePath, EcmaModule componentModule, IEnumerable<DependencyNode> nodes, NodeFactory factory, bool generateMapFile, bool generateMapCsvFile, int customPESectionAlignment) + public ReadyToRunObjectWriter( + string objectFilePath, + EcmaModule componentModule, + IEnumerable<DependencyNode> nodes, + NodeFactory factory, + bool generateMapFile, + bool generateMapCsvFile, + bool generatePdbFile, + string pdbPath, + bool generatePerfMapFile, + string perfMapPath, + int customPESectionAlignment) { _objectFilePath = objectFilePath; _componentModule = componentModule; @@ -96,10 +139,27 @@ namespace ILCompiler.DependencyAnalysis _customPESectionAlignment = customPESectionAlignment; _generateMapFile = generateMapFile; _generateMapCsvFile = generateMapCsvFile; - - if (generateMapFile || generateMapCsvFile) + _generatePdbFile = generatePdbFile; + _pdbPath = pdbPath; + _generatePerfMapFile = generatePerfMapFile; + _perfMapPath = perfMapPath; + + bool generateMap = (generateMapFile || generateMapCsvFile); + bool generateSymbols = (generatePdbFile || generatePerfMapFile); + + if (generateMap || generateSymbols) { - _mapFileBuilder = new MapFileBuilder(); + _outputInfoBuilder = new OutputInfoBuilder(); + + if (generateMap) + { + _mapFileBuilder = new MapFileBuilder(_outputInfoBuilder); + } + + if (generateSymbols) + { + _symbolFileBuilder = new SymbolFileBuilder(_outputInfoBuilder); + } } } @@ -207,8 +267,13 @@ namespace ILCompiler.DependencyAnalysis } } - EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section, _mapFileBuilder); + EmitObjectData(r2rPeBuilder, nodeContents, nodeIndex, name, node.Section); lastWrittenObjectNode = node; + + if ((_generatePdbFile || _generatePerfMapFile) && node is MethodWithGCInfo methodNode) + { + _outputInfoBuilder.AddMethod(methodNode, nodeContents.DefinedSymbols[0]); + } } r2rPeBuilder.SetCorHeader(_nodeFactory.CopiedCorHeaderNode, _nodeFactory.CopiedCorHeaderNode.Size); @@ -247,9 +312,9 @@ namespace ILCompiler.DependencyAnalysis } } - if (_mapFileBuilder != null) + if (_outputInfoBuilder != null) { - r2rPeBuilder.AddSections(_mapFileBuilder); + r2rPeBuilder.AddSections(_outputInfoBuilder); if (_generateMapFile) { @@ -263,6 +328,26 @@ namespace ILCompiler.DependencyAnalysis string mapCsvFileName = Path.ChangeExtension(_objectFilePath, ".map.csv"); _mapFileBuilder.SaveCsv(nodeStatsCsvFileName, mapCsvFileName); } + + if (_generatePdbFile) + { + string path = _pdbPath; + if (string.IsNullOrEmpty(path)) + { + path = Path.GetDirectoryName(_objectFilePath); + } + _symbolFileBuilder.SavePdb(path, _objectFilePath); + } + + if (_generatePerfMapFile) + { + string path = _perfMapPath; + if (string.IsNullOrEmpty(path)) + { + path = Path.GetDirectoryName(_objectFilePath); + } + _symbolFileBuilder.SavePerfMap(path, _objectFilePath); + } } succeeded = true; @@ -299,8 +384,7 @@ namespace ILCompiler.DependencyAnalysis /// <param name="nodeIndex">Logical index of the emitted node for diagnostic purposes</param> /// <param name="name">Textual representation of the ObjecData blob in the map file</param> /// <param name="section">Section to emit the blob into</param> - /// <param name="mapFile">Map file output stream</param> - private void EmitObjectData(R2RPEBuilder r2rPeBuilder, ObjectData data, int nodeIndex, string name, ObjectNodeSection section, MapFileBuilder mapFileBuilder) + private void EmitObjectData(R2RPEBuilder r2rPeBuilder, ObjectData data, int nodeIndex, string name, ObjectNodeSection section) { #if DEBUG for (int symbolIndex = 0; symbolIndex < data.DefinedSymbols.Length; symbolIndex++) @@ -319,13 +403,35 @@ namespace ILCompiler.DependencyAnalysis } #endif - r2rPeBuilder.AddObjectData(data, section, name, mapFileBuilder); + r2rPeBuilder.AddObjectData(data, section, name, _outputInfoBuilder); } - public static void EmitObject(string objectFilePath, EcmaModule componentModule, IEnumerable<DependencyNode> nodes, NodeFactory factory, bool generateMapFile, bool generateMapCsvFile, int customPESectionAlignment) + public static void EmitObject( + string objectFilePath, + EcmaModule componentModule, + IEnumerable<DependencyNode> nodes, + NodeFactory factory, + bool generateMapFile, + bool generateMapCsvFile, + bool generatePdbFile, + string pdbPath, + bool generatePerfMapFile, + string perfMapPath, + int customPESectionAlignment) { Console.WriteLine($@"Emitting R2R PE file: {objectFilePath}"); - ReadyToRunObjectWriter objectWriter = new ReadyToRunObjectWriter(objectFilePath, componentModule, nodes, factory, generateMapFile, generateMapCsvFile, customPESectionAlignment); + ReadyToRunObjectWriter objectWriter = new ReadyToRunObjectWriter( + objectFilePath, + componentModule, + nodes, + factory, + generateMapFile: generateMapFile, + generateMapCsvFile: generateMapCsvFile, + generatePdbFile: generatePdbFile, + pdbPath: pdbPath, + generatePerfMapFile: generatePerfMapFile, + perfMapPath: perfMapPath, + customPESectionAlignment); objectWriter.EmitPortableExecutable(); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 79888b3e89a..a147fa3e45d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -231,6 +231,10 @@ namespace ILCompiler private bool _generateMapFile; private bool _generateMapCsvFile; + private bool _generatePdbFile; + private string _pdbPath; + private bool _generatePerfMapFile; + private string _perfMapPath; private ProfileDataManager _profileData; private ReadyToRunFileLayoutOptimizer _fileLayoutOptimizer; @@ -257,6 +261,10 @@ namespace ILCompiler bool resilient, bool generateMapFile, bool generateMapCsvFile, + bool generatePdbFile, + string pdbPath, + bool generatePerfMapFile, + string perfMapPath, int parallelism, ProfileDataManager profileData, ReadyToRunMethodLayoutAlgorithm methodLayoutAlgorithm, @@ -277,6 +285,10 @@ namespace ILCompiler _parallelism = parallelism; _generateMapFile = generateMapFile; _generateMapCsvFile = generateMapCsvFile; + _generatePdbFile = generatePdbFile; + _pdbPath = pdbPath; + _generatePerfMapFile = generatePerfMapFile; + _perfMapPath = perfMapPath; _customPESectionAlignment = customPESectionAlignment; SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory, verifyTypeAndFieldLayout); _corInfoImpls = new ConditionalWeakTable<Thread, CorInfoImpl>(); @@ -307,7 +319,18 @@ namespace ILCompiler using (PerfEventSource.StartStopEvents.EmittingEvents()) { NodeFactory.SetMarkingComplete(); - ReadyToRunObjectWriter.EmitObject(outputFile, componentModule: null, nodes, NodeFactory, _generateMapFile, _generateMapCsvFile, _customPESectionAlignment); + ReadyToRunObjectWriter.EmitObject( + outputFile, + componentModule: null, + nodes, + NodeFactory, + generateMapFile: _generateMapFile, + generateMapCsvFile: _generateMapCsvFile, + generatePdbFile: _generatePdbFile, + pdbPath: _pdbPath, + generatePerfMapFile: _generatePerfMapFile, + perfMapPath: _perfMapPath, + _customPESectionAlignment); CompilationModuleGroup moduleGroup = _nodeFactory.CompilationModuleGroup; if (moduleGroup.IsCompositeBuildMode) @@ -372,7 +395,18 @@ namespace ILCompiler } componentGraph.ComputeMarkedNodes(); componentFactory.Header.Add(Internal.Runtime.ReadyToRunSectionType.OwnerCompositeExecutable, ownerExecutableNode, ownerExecutableNode); - ReadyToRunObjectWriter.EmitObject(outputFile, componentModule: inputModule, componentGraph.MarkedNodeList, componentFactory, generateMapFile: false, generateMapCsvFile: false, customPESectionAlignment: 0); + ReadyToRunObjectWriter.EmitObject( + outputFile, + componentModule: inputModule, + componentGraph.MarkedNodeList, + componentFactory, + generateMapFile: false, + generateMapCsvFile: false, + generatePdbFile: false, + pdbPath: _pdbPath, + generatePerfMapFile: false, + perfMapPath: _perfMapPath, + customPESectionAlignment: 0); } public override void WriteDependencyLog(string outputFileName) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index 03ef31b3888..acdd53e86a3 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -24,6 +24,10 @@ namespace ILCompiler private bool _resilient; private bool _generateMapFile; private bool _generateMapCsvFile; + private bool _generatePdbFile; + private string _pdbPath; + private bool _generatePerfMapFile; + private string _perfMapPath; private int _parallelism; private InstructionSetSupport _instructionSetSupport; private ProfileDataManager _profileData; @@ -133,6 +137,20 @@ namespace ILCompiler return this; } + public ReadyToRunCodegenCompilationBuilder UsePdbFile(bool generatePdbFile, string pdbPath) + { + _generatePdbFile = generatePdbFile; + _pdbPath = pdbPath; + return this; + } + + public ReadyToRunCodegenCompilationBuilder UsePerfMapFile(bool generatePerfMapFile, string perfMapPath) + { + _generatePerfMapFile = generatePerfMapFile; + _perfMapPath = perfMapPath; + return this; + } + public ReadyToRunCodegenCompilationBuilder UseParallelism(int parallelism) { _parallelism = parallelism; @@ -245,9 +263,13 @@ namespace ILCompiler _inputFiles, _compositeRootPath, _instructionSetSupport, - _resilient, - _generateMapFile, - _generateMapCsvFile, + resilient: _resilient, + generateMapFile: _generateMapFile, + generateMapCsvFile: _generateMapCsvFile, + generatePdbFile: _generatePdbFile, + pdbPath: _pdbPath, + generatePerfMapFile: _generatePerfMapFile, + perfMapPath: _perfMapPath, _parallelism, _profileData, _r2rMethodLayoutAlgorithm, diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 0d8a93f397f..e6f136eb223 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -6,7 +6,7 @@ <AllowUnsafeBlocks>true</AllowUnsafeBlocks> <DefineConstants>READYTORUN;$(DefineConstants)</DefineConstants> <EnableDefaultCompileItems>false</EnableDefaultCompileItems> - <Platforms>x64;x86</Platforms> + <Platforms>x64;x86;arm;arm64</Platforms> <PlatformTarget>AnyCPU</PlatformTarget> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> @@ -19,6 +19,7 @@ <ItemGroup> <ProjectReference Include="..\ILCompiler.DependencyAnalysisFramework\ILCompiler.DependencyAnalysisFramework.csproj" /> + <ProjectReference Include="..\ILCompiler.Diagnostics\ILCompiler.Diagnostics.csproj" /> <ProjectReference Include="..\ILCompiler.TypeSystem.ReadyToRun\ILCompiler.TypeSystem.ReadyToRun.csproj" /> </ItemGroup> @@ -195,6 +196,8 @@ <Compile Include="IL\Stubs\PInvokeILEmitter.cs" /> <Compile Include="Interop\IL\Marshaller.ReadyToRun.cs" /> <Compile Include="Compiler\PerfEventSource.cs" /> + <Compile Include="ObjectWriter\OutputInfoBuilder.cs" /> + <Compile Include="ObjectWriter\SymbolFileBuilder.cs" /> <Compile Include="Win32Resources\ResourceData.cs" /> <Compile Include="Win32Resources\ResourceData.Reader.cs" /> <Compile Include="Win32Resources\ResourceData.ResourcesDataModel.cs" /> diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs index eb2b9e2a842..726ae5fe079 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs @@ -8,92 +8,20 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Reflection.Metadata.Ecma335; +using System.Text; + +using Internal.JitInterface; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysis.ReadyToRun; +using ILCompiler.Diagnostics; namespace ILCompiler.PEWriter { /// <summary> - /// Base class for symbols and nodes in the map file implements common logic - /// for section / offset ordering. - /// </summary> - public class MapFileItem - { - public class Comparer : IComparer<MapFileItem> - { - public readonly static Comparer Instance = new Comparer(); - - public int Compare([AllowNull] MapFileItem x, [AllowNull] MapFileItem y) - { - return (x.SectionIndex != y.SectionIndex ? x.SectionIndex.CompareTo(y.SectionIndex) : x.Offset.CompareTo(y.Offset)); - } - } - - /// <summary> - /// Item section index - /// </summary> - public readonly int SectionIndex; - - /// <summary> - /// Offset relative to section beginning - /// </summary> - public readonly int Offset; - - /// <summary> - /// Item name - /// </summary> - public readonly string Name; - - public MapFileItem(int sectionIndex, int offset, string name) - { - SectionIndex = sectionIndex; - Offset = offset; - Name = name; - } - } - - /// <summary> - /// This class represents a single node (contiguous block of data) in the output R2R PE file. - /// </summary> - public class MapFileNode : MapFileItem - { - /// <summary> - /// Node length (number of bytes). This doesn't include any external alignment - /// applied when concatenating the nodes to form sections. - /// </summary> - public readonly int Length; - - /// <summary> - /// Number of file-level relocations (.reloc section entries) used by the node. - /// </summary> - public int Relocations { get; private set; } - - public MapFileNode(int sectionIndex, int offset, int length, string name) - : base(sectionIndex, offset, name) - { - Length = length; - Relocations = 0; - } - - public void AddRelocation() - { - Relocations++; - } - } - - /// <summary> - /// Symbol is a "pointer" into the PE file. Most (but not all) symbols correspond to - /// node beginnings (most nodes have a "start symbol" representing the beginning - /// of the node). - /// </summary> - public class MapFileSymbol : MapFileItem - { - public MapFileSymbol(int sectionIndex, int offset, string name) - : base(sectionIndex, offset, name) - { - } - } - - /// <summary> /// Helper class used to collect information to be output into the map file. /// </summary> public class MapFileBuilder @@ -118,7 +46,7 @@ namespace ILCompiler.PEWriter Name = name; } - public void AddNode(MapFileNode node) + public void AddNode(OutputNode node) { Debug.Assert(Name == node.Name); Count++; @@ -126,43 +54,13 @@ namespace ILCompiler.PEWriter } } - private readonly List<MapFileNode> _nodes; - private readonly List<MapFileSymbol> _symbols; - private readonly List<Section> _sections; - - private readonly Dictionary<RelocType, int> _relocCounts; + private OutputInfoBuilder _outputInfoBuilder; private long _fileSize; - public MapFileBuilder() - { - _nodes = new List<MapFileNode>(); - _symbols = new List<MapFileSymbol>(); - _sections = new List<Section>(); - - _relocCounts = new Dictionary<RelocType, int>(); - } - - public void AddNode(MapFileNode node) - { - _nodes.Add(node); - } - - public void AddRelocation(MapFileNode node, RelocType relocType) + public MapFileBuilder(OutputInfoBuilder outputInfoBuilder) { - node.AddRelocation(); - _relocCounts.TryGetValue(relocType, out int relocTypeCount); - _relocCounts[relocType] = relocTypeCount + 1; - } - - public void AddSymbol(MapFileSymbol symbol) - { - _symbols.Add(symbol); - } - - public void AddSection(Section section) - { - _sections.Add(section); + _outputInfoBuilder = outputInfoBuilder; } public void SetFileSize(long fileSize) @@ -174,8 +72,7 @@ namespace ILCompiler.PEWriter { Console.WriteLine("Emitting map file: {0}", mapFileName); - _nodes.Sort(MapFileItem.Comparer.Instance); - _symbols.Sort(MapFileItem.Comparer.Instance); + _outputInfoBuilder.Sort(); using (StreamWriter mapWriter = new StreamWriter(mapFileName)) { @@ -191,8 +88,7 @@ namespace ILCompiler.PEWriter { Console.WriteLine("Emitting csv files: {0}, {1}", nodeStatsCsvFileName, mapCsvFileName); - _nodes.Sort(MapFileItem.Comparer.Instance); - _symbols.Sort(MapFileItem.Comparer.Instance); + _outputInfoBuilder.Sort(); using (StreamWriter nodeStatsWriter = new StreamWriter(nodeStatsCsvFileName)) { @@ -210,17 +106,17 @@ namespace ILCompiler.PEWriter WriteTitle(writer, "Summary Info"); writer.WriteLine($"Output file size: {_fileSize,10}"); - writer.WriteLine($"Section count: {_sections.Count,10}"); - writer.WriteLine($"Node count: {_nodes.Count,10}"); - writer.WriteLine($"Symbol count: {_symbols.Count,10}"); - writer.WriteLine($"Relocation count: {_relocCounts.Values.Sum(),10}"); + writer.WriteLine($"Section count: {_outputInfoBuilder.Sections.Count,10}"); + writer.WriteLine($"Node count: {_outputInfoBuilder.Nodes.Count,10}"); + writer.WriteLine($"Symbol count: {_outputInfoBuilder.Symbols.Count,10}"); + writer.WriteLine($"Relocation count: {_outputInfoBuilder.RelocCounts.Values.Sum(),10}"); } private IEnumerable<NodeTypeStatistics> GetNodeTypeStatistics() { List<NodeTypeStatistics> nodeTypeStats = new List<NodeTypeStatistics>(); Dictionary<string, int> statsNameIndex = new Dictionary<string, int>(); - foreach (MapFileNode node in _nodes) + foreach (OutputNode node in _outputInfoBuilder.Nodes) { if (!statsNameIndex.TryGetValue(node.Name, out int statsIndex)) { @@ -268,7 +164,7 @@ namespace ILCompiler.PEWriter private void WriteRelocTypeStatistics(StreamWriter writer) { - KeyValuePair<RelocType, int>[] relocTypeCounts = _relocCounts.ToArray(); + KeyValuePair<RelocType, int>[] relocTypeCounts = _outputInfoBuilder.RelocCounts.ToArray(); Array.Sort(relocTypeCounts, (a, b) => b.Value.CompareTo(a.Value)); WriteTitle(writer, "Reloc Type Statistics"); @@ -284,13 +180,12 @@ namespace ILCompiler.PEWriter WriteTitle(writer, "Top Nodes By Relocation Count"); WriteTitle(writer, " COUNT | SYMBOL (NODE)"); - foreach (MapFileNode node in _nodes.Where(node => node.Relocations != 0).OrderByDescending(node => node.Relocations).Take(NumberOfTopNodesByRelocType)) + foreach (OutputNode node in _outputInfoBuilder.Nodes.Where(node => node.Relocations != 0).OrderByDescending(node => node.Relocations).Take(NumberOfTopNodesByRelocType)) { writer.Write($"{node.Relocations,8} | "); - int symbolIndex = _symbols.BinarySearch(new MapFileSymbol(node.SectionIndex, node.Offset, name: null), MapFileItem.Comparer.Instance); - if (symbolIndex >= 0 && symbolIndex < _symbols.Count && MapFileItem.Comparer.Instance.Compare(_symbols[symbolIndex], node) == 0) + if (_outputInfoBuilder.FindSymbol(node, out int symbolIndex)) { - writer.Write($"{_symbols[symbolIndex].Name}"); + writer.Write($"{_outputInfoBuilder.Symbols[symbolIndex].Name}"); } writer.WriteLine($" ({node.Name})"); } @@ -300,9 +195,9 @@ namespace ILCompiler.PEWriter { WriteTitle(writer, "Section Map"); WriteTitle(writer, "INDEX | FILEOFFSET | RVA | END_RVA | LENGTH | NAME"); - for (int sectionIndex = 0; sectionIndex < _sections.Count; sectionIndex++) + for (int sectionIndex = 0; sectionIndex < _outputInfoBuilder.Sections.Count; sectionIndex++) { - Section section = _sections[sectionIndex]; + Section section = _outputInfoBuilder.Sections[sectionIndex]; writer.Write($"{sectionIndex,5} | "); writer.Write($"0x{section.FilePosWhenPlaced:X8} | "); writer.Write($"0x{section.RVAWhenPlaced:X8} | "); @@ -320,13 +215,15 @@ namespace ILCompiler.PEWriter int nodeIndex = 0; int symbolIndex = 0; - while (nodeIndex < _nodes.Count || symbolIndex < _symbols.Count) + while (nodeIndex < _outputInfoBuilder.Nodes.Count || symbolIndex < _outputInfoBuilder.Symbols.Count) { - if (nodeIndex >= _nodes.Count || symbolIndex < _symbols.Count && MapFileItem.Comparer.Instance.Compare(_symbols[symbolIndex], _nodes[nodeIndex]) < 0) + if (nodeIndex >= _outputInfoBuilder.Nodes.Count + || symbolIndex < _outputInfoBuilder.Symbols.Count + && OutputItem.Comparer.Instance.Compare(_outputInfoBuilder.Symbols[symbolIndex], _outputInfoBuilder.Nodes[nodeIndex]) < 0) { // No more nodes or next symbol is below next node - emit symbol - MapFileSymbol symbol = _symbols[symbolIndex++]; - Section section = _sections[symbol.SectionIndex]; + OutputSymbol symbol = _outputInfoBuilder.Symbols[symbolIndex++]; + Section section = _outputInfoBuilder.Sections[symbol.SectionIndex]; writer.Write($"0x{symbol.Offset + section.RVAWhenPlaced:X8} | "); writer.Write(" | "); writer.Write(" | "); @@ -336,16 +233,16 @@ namespace ILCompiler.PEWriter else { // Emit node and optionally symbol - MapFileNode node = _nodes[nodeIndex++]; - Section section = _sections[node.SectionIndex]; + OutputNode node = _outputInfoBuilder.Nodes[nodeIndex++]; + Section section = _outputInfoBuilder.Sections[node.SectionIndex]; writer.Write($"0x{node.Offset + section.RVAWhenPlaced:X8} | "); writer.Write($"0x{node.Length:X6} | "); writer.Write($"{node.Relocations,6} | "); writer.Write($"{GetNameHead(section),-SectionNameHeadLength} | "); - if (symbolIndex < _symbols.Count && MapFileItem.Comparer.Instance.Compare(node, _symbols[symbolIndex]) == 0) + if (symbolIndex < _outputInfoBuilder.Symbols.Count && OutputItem.Comparer.Instance.Compare(node, _outputInfoBuilder.Symbols[symbolIndex]) == 0) { - MapFileSymbol symbol = _symbols[symbolIndex++]; + OutputSymbol symbol = _outputInfoBuilder.Symbols[symbolIndex++]; writer.Write($"{symbol.Name}"); } writer.WriteLine($" ({node.Name})"); @@ -360,13 +257,15 @@ namespace ILCompiler.PEWriter int nodeIndex = 0; int symbolIndex = 0; - while (nodeIndex < _nodes.Count || symbolIndex < _symbols.Count) + while (nodeIndex < _outputInfoBuilder.Nodes.Count || symbolIndex < _outputInfoBuilder.Symbols.Count) { - if (nodeIndex >= _nodes.Count || symbolIndex < _symbols.Count && MapFileItem.Comparer.Instance.Compare(_symbols[symbolIndex], _nodes[nodeIndex]) < 0) + if (nodeIndex >= _outputInfoBuilder.Nodes.Count + || symbolIndex < _outputInfoBuilder.Symbols.Count + && OutputItem.Comparer.Instance.Compare(_outputInfoBuilder.Symbols[symbolIndex], _outputInfoBuilder.Nodes[nodeIndex]) < 0) { // No more nodes or next symbol is below next node - emit symbol - MapFileSymbol symbol = _symbols[symbolIndex++]; - Section section = _sections[symbol.SectionIndex]; + OutputSymbol symbol = _outputInfoBuilder.Symbols[symbolIndex++]; + Section section = _outputInfoBuilder.Sections[symbol.SectionIndex]; writer.Write($"0x{symbol.Offset + section.RVAWhenPlaced:X8},"); writer.Write(","); writer.Write(","); @@ -377,16 +276,16 @@ namespace ILCompiler.PEWriter else { // Emit node and optionally symbol - MapFileNode node = _nodes[nodeIndex++]; - Section section = _sections[node.SectionIndex]; + OutputNode node = _outputInfoBuilder.Nodes[nodeIndex++]; + Section section = _outputInfoBuilder.Sections[node.SectionIndex]; writer.Write($"0x{node.Offset + section.RVAWhenPlaced:X8},"); writer.Write($"{node.Length},"); writer.Write($"{node.Relocations},"); writer.Write($"{section.Name},"); - if (symbolIndex < _symbols.Count && MapFileItem.Comparer.Instance.Compare(node, _symbols[symbolIndex]) == 0) + if (symbolIndex < _outputInfoBuilder.Symbols.Count && OutputItem.Comparer.Instance.Compare(node, _outputInfoBuilder.Symbols[symbolIndex]) == 0) { - MapFileSymbol symbol = _symbols[symbolIndex++]; + OutputSymbol symbol = _outputInfoBuilder.Symbols[symbolIndex++]; writer.Write($"{symbol.Name}"); } writer.Write(","); @@ -411,5 +310,6 @@ namespace ILCompiler.PEWriter writer.WriteLine(title); writer.WriteLine(new string('-', title.Length)); } + } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/OutputInfoBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/OutputInfoBuilder.cs new file mode 100644 index 00000000000..00a73721531 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/OutputInfoBuilder.cs @@ -0,0 +1,230 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +using Internal.JitInterface; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using ILCompiler.DependencyAnalysis; +using ILCompiler.DependencyAnalysis.ReadyToRun; +using ILCompiler.Diagnostics; + +namespace ILCompiler.PEWriter +{ + /// <summary> + /// Base class for symbols and nodes in the output file implements common logic + /// for section / offset ordering. + /// </summary> + public class OutputItem + { + public class Comparer : IComparer<OutputItem> + { + public readonly static Comparer Instance = new Comparer(); + + public int Compare([AllowNull] OutputItem x, [AllowNull] OutputItem y) + { + return (x.SectionIndex != y.SectionIndex ? x.SectionIndex.CompareTo(y.SectionIndex) : x.Offset.CompareTo(y.Offset)); + } + } + + /// <summary> + /// Item section index + /// </summary> + public readonly int SectionIndex; + + /// <summary> + /// Offset relative to section beginning + /// </summary> + public readonly int Offset; + + /// <summary> + /// Item name + /// </summary> + public readonly string Name; + + public OutputItem(int sectionIndex, int offset, string name) + { + SectionIndex = sectionIndex; + Offset = offset; + Name = name; + } + } + + /// <summary> + /// This class represents a single node (contiguous block of data) in the output R2R PE file. + /// </summary> + public class OutputNode : OutputItem + { + /// <summary> + /// Node length (number of bytes). This doesn't include any external alignment + /// applied when concatenating the nodes to form sections. + /// </summary> + public readonly int Length; + + /// <summary> + /// Number of file-level relocations (.reloc section entries) used by the node. + /// </summary> + public int Relocations { get; private set; } + + public OutputNode(int sectionIndex, int offset, int length, string name) + : base(sectionIndex, offset, name) + { + Length = length; + Relocations = 0; + } + + public void AddRelocation() + { + Relocations++; + } + } + + /// <summary> + /// Symbol is a "pointer" into the PE file. Most (but not all) symbols correspond to + /// node beginnings (most nodes have a "start symbol" representing the beginning + /// of the node). + /// </summary> + public class OutputSymbol : OutputItem + { + public OutputSymbol(int sectionIndex, int offset, string name) + : base(sectionIndex, offset, name) + { + } + } + + /// <summary> + /// Common class used to collect information to use when emitting map files and symbol files. + /// </summary> + public class OutputInfoBuilder + { + private readonly List<OutputNode> _nodes; + private readonly List<OutputSymbol> _symbols; + private readonly List<Section> _sections; + + private readonly Dictionary<ISymbolDefinitionNode, OutputNode> _nodeSymbolMap; + private readonly Dictionary<ISymbolDefinitionNode, MethodWithGCInfo> _methodSymbolMap; + + private readonly Dictionary<RelocType, int> _relocCounts; + + public OutputInfoBuilder() + { + _nodes = new List<OutputNode>(); + _symbols = new List<OutputSymbol>(); + _sections = new List<Section>(); + + _nodeSymbolMap = new Dictionary<ISymbolDefinitionNode, OutputNode>(); + _methodSymbolMap = new Dictionary<ISymbolDefinitionNode, MethodWithGCInfo>(); + + _relocCounts = new Dictionary<RelocType, int>(); + } + + public void AddNode(OutputNode node, ISymbolDefinitionNode symbol) + { + _nodes.Add(node); + _nodeSymbolMap.Add(symbol, node); + } + + public void AddRelocation(OutputNode node, RelocType relocType) + { + node.AddRelocation(); + _relocCounts.TryGetValue(relocType, out int relocTypeCount); + _relocCounts[relocType] = relocTypeCount + 1; + } + + public void AddSymbol(OutputSymbol symbol) + { + _symbols.Add(symbol); + } + + public void AddSection(Section section) + { + _sections.Add(section); + } + + public void AddMethod(MethodWithGCInfo method, ISymbolDefinitionNode symbol) + { + _methodSymbolMap.Add(symbol, method); + } + + public void Sort() + { + _nodes.Sort(OutputItem.Comparer.Instance); + _symbols.Sort(OutputItem.Comparer.Instance); + } + + public bool FindSymbol(OutputItem item, out int index) + { + index = _symbols.BinarySearch(new OutputSymbol(item.SectionIndex, item.Offset, name: null), OutputItem.Comparer.Instance); + bool result = (index >= 0 && index < _symbols.Count && OutputItem.Comparer.Instance.Compare(_symbols[index], item) == 0); + if (!result) + { + index = -1; + } + return result; + } + + public IEnumerable<MethodInfo> EnumerateMethods() + { + DebugNameFormatter nameFormatter = new DebugNameFormatter(); + TypeNameFormatter typeNameFormatter = new TypeString(); + HashSet<MethodDesc> emittedMethods = new HashSet<MethodDesc>(); + foreach (KeyValuePair<ISymbolDefinitionNode, MethodWithGCInfo> symbolMethodPair in _methodSymbolMap) + { + EcmaMethod ecmaMethod = symbolMethodPair.Value.Method.GetTypicalMethodDefinition() as EcmaMethod; + if (ecmaMethod != null && emittedMethods.Add(ecmaMethod)) + { + MethodInfo methodInfo = new MethodInfo(); + methodInfo.MethodToken = (uint)MetadataTokens.GetToken(ecmaMethod.Handle); + methodInfo.AssemblyName = ecmaMethod.Module.Assembly.GetName().Name; + methodInfo.Name = FormatMethodName(symbolMethodPair.Value.Method, typeNameFormatter); + OutputNode node = _nodeSymbolMap[symbolMethodPair.Key]; + Section section = _sections[node.SectionIndex]; + methodInfo.HotRVA = (uint)(section.RVAWhenPlaced + node.Offset); + methodInfo.HotLength = (uint)node.Length; + methodInfo.ColdRVA = 0; + methodInfo.ColdLength = 0; + yield return methodInfo; + } + } + } + + private string FormatMethodName(MethodDesc method, TypeNameFormatter typeNameFormatter) + { + StringBuilder output = new StringBuilder(); + if (!method.Signature.ReturnType.IsVoid) + { + output.Append(typeNameFormatter.FormatName(method.Signature.ReturnType)); + output.Append(" "); + } + output.Append(typeNameFormatter.FormatName(method.OwningType)); + output.Append("::"); + output.Append(method.Name); + output.Append("("); + for (int paramIndex = 0; paramIndex < method.Signature.Length; paramIndex++) + { + if (paramIndex != 0) + { + output.Append(", "); + } + output.Append(typeNameFormatter.FormatName(method.Signature[paramIndex])); + } + output.Append(")"); + return output.ToString(); + } + + public IReadOnlyList<OutputNode> Nodes => _nodes; + public IReadOnlyList<Section> Sections => _sections; + public IReadOnlyList<OutputSymbol> Symbols => _symbols; + + public IReadOnlyDictionary<RelocType, int> RelocCounts => _relocCounts; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs index 759fad4ac6f..3f6764b0e80 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/R2RPEBuilder.cs @@ -241,8 +241,8 @@ namespace ILCompiler.PEWriter /// <param name="objectData">Object data to emit</param> /// <param name="section">Target section</param> /// <param name="name">Textual name of the object data for diagnostic purposese</param> - /// <param name="mapFileBuilder">Optional map file builder to output the data item to</param> - public void AddObjectData(ObjectNode.ObjectData objectData, ObjectNodeSection section, string name, MapFileBuilder mapFileBuilder) + /// <param name="outputInfoBuilder">Optional output info builder to output the data item to</param> + public void AddObjectData(DependencyAnalysis.ObjectNode.ObjectData objectData, ObjectNodeSection section, string name, OutputInfoBuilder outputInfoBuilder) { if (_written) { @@ -266,7 +266,7 @@ namespace ILCompiler.PEWriter throw new NotImplementedException(); } - _sectionBuilder.AddObjectData(objectData, targetSectionIndex, name, mapFileBuilder); + _sectionBuilder.AddObjectData(objectData, targetSectionIndex, name, outputInfoBuilder); } /// <summary> @@ -312,10 +312,10 @@ namespace ILCompiler.PEWriter /// <summary> /// Fill in map builder section table. /// </summary> - /// <param name="mapFileBuilder">Map file builder to set up</param> - public void AddSections(MapFileBuilder mapFileBuilder) + /// <param name="outputInfoBuilder">Object info builder to set up</param> + public void AddSections(OutputInfoBuilder outputInfoBuilder) { - _sectionBuilder.AddSections(mapFileBuilder); + _sectionBuilder.AddSections(outputInfoBuilder); } /// <summary> diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs index 6ba8e556368..7c3e16b763c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs @@ -437,8 +437,8 @@ namespace ILCompiler.PEWriter /// <param name="data">Block to add</param> /// <param name="sectionIndex">Section index</param> /// <param name="name">Node name to emit in the map file</param> - /// <param name="mapFileBuilder">Optional map file to emit</param> - public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, string name, MapFileBuilder mapFileBuilder) + /// <param name="outputInfoBuilder">Optional output info to collect (used for creating maps and symbols)</param> + public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, string name, OutputInfoBuilder outputInfoBuilder) { Section section = _sections[sectionIndex]; @@ -475,10 +475,10 @@ namespace ILCompiler.PEWriter } } - if (mapFileBuilder != null) + if (outputInfoBuilder != null) { - MapFileNode node = new MapFileNode(sectionIndex, alignedOffset, objectData.Data.Length, name); - mapFileBuilder.AddNode(node); + var node = new OutputNode(sectionIndex, alignedOffset, objectData.Data.Length, name); + outputInfoBuilder.AddNode(node, objectData.DefinedSymbols[0]); if (objectData.Relocs != null) { foreach (Relocation reloc in objectData.Relocs) @@ -486,7 +486,7 @@ namespace ILCompiler.PEWriter RelocType fileReloc = Relocation.GetFileRelocationType(reloc.RelocType); if (fileReloc != RelocType.IMAGE_REL_BASED_ABSOLUTE) { - mapFileBuilder.AddRelocation(node, fileReloc); + outputInfoBuilder.AddRelocation(node, fileReloc); } } } @@ -498,12 +498,12 @@ namespace ILCompiler.PEWriter { foreach (ISymbolDefinitionNode symbol in objectData.DefinedSymbols) { - if (mapFileBuilder != null) + if (outputInfoBuilder != null) { Utf8StringBuilder sb = new Utf8StringBuilder(); symbol.AppendMangledName(GetNameMangler(), sb); int sectionRelativeOffset = alignedOffset + symbol.Offset; - mapFileBuilder.AddSymbol(new MapFileSymbol(sectionIndex, sectionRelativeOffset, sb.ToString())); + outputInfoBuilder.AddSymbol(new OutputSymbol(sectionIndex, sectionRelativeOffset, sb.ToString())); } _symbolMap.Add(symbol, new SymbolTarget( sectionIndex: sectionIndex, @@ -551,11 +551,11 @@ namespace ILCompiler.PEWriter return sectionList; } - public void AddSections(MapFileBuilder mapFileBuilder) + public void AddSections(OutputInfoBuilder outputInfoBuilder) { foreach (Section section in _sections) { - mapFileBuilder.AddSection(section); + outputInfoBuilder.AddSection(section); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs new file mode 100644 index 00000000000..62eeb0494a9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SymbolFileBuilder.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using ILCompiler.Diagnostics; + +namespace ILCompiler.PEWriter +{ + public class SymbolFileBuilder + { + private readonly OutputInfoBuilder _outputInfoBuilder; + + public SymbolFileBuilder(OutputInfoBuilder outputInfoBuilder) + { + _outputInfoBuilder = outputInfoBuilder; + } + + public void SavePdb(string pdbPath, string dllFileName) + { + Console.WriteLine("Emitting PDB file: {0}", Path.Combine(pdbPath, Path.GetFileNameWithoutExtension(dllFileName) + ".ni.pdb")); + + new PdbWriter(pdbPath, PDBExtraData.None).WritePDBData(dllFileName, _outputInfoBuilder.EnumerateMethods()); + } + + public void SavePerfMap(string perfMapPath, string dllFileName) + { + string perfMapFileName = Path.Combine(perfMapPath, Path.GetFileNameWithoutExtension(dllFileName) + ".perf.map"); + Console.WriteLine("Emitting PerfMap file: {0}", perfMapFileName); + + PerfMapWriter.Write(perfMapFileName, _outputInfoBuilder.EnumerateMethods()); + } + } +} diff --git a/src/coreclr/tools/aot/crossgen2.sln b/src/coreclr/tools/aot/crossgen2.sln index e81eae9c024..cb78b75b959 100644 --- a/src/coreclr/tools/aot/crossgen2.sln +++ b/src/coreclr/tools/aot/crossgen2.sln @@ -12,6 +12,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem.Ready EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem.ReadyToRun.Tests", "ILCompiler.TypeSystem.ReadyToRun.Tests\ILCompiler.TypeSystem.ReadyToRun.Tests.csproj", "{1043373D-8C14-4224-9E2B-75F0DE645E7E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Diagnostics", "ILCompiler.Diagnostics\ILCompiler.Diagnostics.csproj", "{3EACD929-4725-4173-A845-734936BBDF87}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Checked|Any CPU = Checked|Any CPU @@ -103,6 +105,23 @@ Global {1043373D-8C14-4224-9E2B-75F0DE645E7E}.Release|x64.Build.0 = Release|x64 {1043373D-8C14-4224-9E2B-75F0DE645E7E}.Release|x86.ActiveCfg = Release|Any CPU {1043373D-8C14-4224-9E2B-75F0DE645E7E}.Release|x86.Build.0 = Release|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Checked|Any CPU.ActiveCfg = Debug|x86 + {3EACD929-4725-4173-A845-734936BBDF87}.Checked|x64.ActiveCfg = Debug|x64 + {3EACD929-4725-4173-A845-734936BBDF87}.Checked|x64.Build.0 = Debug|x64 + {3EACD929-4725-4173-A845-734936BBDF87}.Checked|x86.ActiveCfg = Debug|x86 + {3EACD929-4725-4173-A845-734936BBDF87}.Checked|x86.Build.0 = Debug|x86 + {3EACD929-4725-4173-A845-734936BBDF87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Debug|x64.ActiveCfg = Debug|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Debug|x64.Build.0 = Debug|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Debug|x86.ActiveCfg = Debug|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Debug|x86.Build.0 = Debug|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Release|Any CPU.Build.0 = Release|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Release|x64.ActiveCfg = Release|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Release|x64.Build.0 = Release|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Release|x86.ActiveCfg = Release|Any CPU + {3EACD929-4725-4173-A845-734936BBDF87}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs index ab4afd4b9e1..910743f709b 100644 --- a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs +++ b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs @@ -46,6 +46,10 @@ namespace ILCompiler public bool Resilient; public bool Map; public bool MapCsv; + public bool Pdb; + public string PdbPath; + public bool PerfMap; + public string PerfMapPath; public int Parallelism; public int CustomPESectionAlignment; public string MethodLayout; @@ -117,6 +121,10 @@ namespace ILCompiler syntax.DefineOption("custom-pe-section-alignment", ref CustomPESectionAlignment, SR.CustomPESectionAlignmentOption); syntax.DefineOption("map", ref Map, SR.MapFileOption); syntax.DefineOption("mapcsv", ref MapCsv, SR.MapCsvFileOption); + syntax.DefineOption("pdb", ref Pdb, SR.PdbFileOption); + syntax.DefineOption("pdb-path", ref PdbPath, SR.PdbFilePathOption); + syntax.DefineOption("perfmap", ref PerfMap, SR.PerfMapFileOption); + syntax.DefineOption("perfmap-path", ref PerfMapPath, SR.PerfMapFilePathOption); syntax.DefineOption("method-layout", ref MethodLayout, SR.MethodLayoutOption); syntax.DefineOption("file-layout", ref FileLayout, SR.FileLayoutOption); diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index e20b25be991..35dbcecfe59 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -582,6 +582,8 @@ namespace ILCompiler .UseResilience(_commandLineOptions.Resilient) .UseMapFile(_commandLineOptions.Map) .UseMapCsvFile(_commandLineOptions.MapCsv) + .UsePdbFile(_commandLineOptions.Pdb, _commandLineOptions.PdbPath) + .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath) .UseParallelism(_commandLineOptions.Parallelism) .UseProfileData(profileDataManager) .FileLayoutAlgorithms(_methodLayout, _fileLayout) diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index d677fe8d771..6b3e6bec126 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -309,4 +309,16 @@ <data name="WarningOverridingOptimize" xml:space="preserve"> <value>Warning: -Od overrides other optimization options</value> </data> -</root>
\ No newline at end of file + <data name="PdbFileOption" xml:space="preserve"> + <value>Generate PDB symbol information file (supported on Windows only)</value> + </data> + <data name="PerfMapFileOption" xml:space="preserve"> + <value>Generate PerfMap symbol information file for use by PerfInfo</value> + </data> + <data name="PdbFilePathOption" xml:space="preserve"> + <value>Explicit specification of the output PDB file path</value> + </data> + <data name="PerfMapFilePathOption" xml:space="preserve"> + <value>Explicit specification of the PerfMap file path</value> + </data> +</root> diff --git a/src/coreclr/tools/r2rdump/CommandLineOptions.cs b/src/coreclr/tools/r2rdump/CommandLineOptions.cs index f41ffd43744..7ada4e9748d 100644 --- a/src/coreclr/tools/r2rdump/CommandLineOptions.cs +++ b/src/coreclr/tools/r2rdump/CommandLineOptions.cs @@ -36,7 +36,9 @@ namespace R2RDump command.AddOption(new Option<bool>(new[] { "--inlineSignatureBinary", "--isb" }, "Embed binary signature into its textual representation")); command.AddOption(new Option<bool>(new[] { "--signatureBinary", "--sb" }, "Append signature binary to its textual representation")); command.AddOption(new Option<bool>(new[] { "--create-pdb" }, "Create PDB")); - command.AddOption(new Option<string>(new[] { "--pdb-path" }, "PDB output path for --createpdb")); + command.AddOption(new Option<string>(new[] { "--pdb-path" }, "PDB output path for --create-pdb")); + command.AddOption(new Option<bool>(new[] { "--create-perfmap" }, "Create PerfMap")); + command.AddOption(new Option<string>(new[] { "--perfmap-path" }, "PerfMap output path for --create-perfmap")); return command; } } diff --git a/src/coreclr/tools/r2rdump/R2RDump.cs b/src/coreclr/tools/r2rdump/R2RDump.cs index c9bc9be0455..805e479f574 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.cs +++ b/src/coreclr/tools/r2rdump/R2RDump.cs @@ -12,13 +12,14 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; + +using ILCompiler.Diagnostics; using ILCompiler.Reflection.ReadyToRun; -using ILCompiler.PdbWriter; using Internal.Runtime; -using System.Runtime.InteropServices; namespace R2RDump { @@ -51,6 +52,10 @@ namespace R2RDump public bool CreatePDB { get; set; } public string PdbPath { get; set; } + public bool CreatePerfmap { get; set; } + public string PerfmapPath { get; set; } + + public FileInfo[] Reference { get; set; } public DirectoryInfo[] ReferencePath { get; set; } @@ -361,7 +366,7 @@ namespace R2RDump public void Dump(ReadyToRunReader r2r) { _dumper.Begin(); - bool standardDump = !(_options.EntryPoints || _options.CreatePDB); + bool standardDump = !(_options.EntryPoints || _options.CreatePDB || _options.CreatePerfmap); if (_options.Header && standardDump) { @@ -410,7 +415,17 @@ namespace R2RDump pdbPath = Path.GetDirectoryName(r2r.Filename); } var pdbWriter = new PdbWriter(pdbPath, PDBExtraData.None); - pdbWriter.WritePDBData(r2r.Filename, ProducePdbWriterMethods(r2r)); + pdbWriter.WritePDBData(r2r.Filename, ProduceDebugInfoMethods(r2r)); + } + + if (_options.CreatePerfmap) + { + string perfmapPath = _options.PerfmapPath; + if (string.IsNullOrEmpty(perfmapPath)) + { + perfmapPath = Path.ChangeExtension(r2r.Filename, ".map"); + PerfMapWriter.Write(perfmapPath, ProduceDebugInfoMethods(r2r)); + } } if (standardDump) @@ -423,16 +438,18 @@ namespace R2RDump _dumper.End(); } - IEnumerable<MethodInfo> ProducePdbWriterMethods(ReadyToRunReader r2r) + IEnumerable<MethodInfo> ProduceDebugInfoMethods(ReadyToRunReader r2r) { foreach (var method in _dumper.NormalizedMethods()) { MethodInfo mi = new MethodInfo(); mi.Name = method.SignatureString; mi.HotRVA = (uint)method.RuntimeFunctions[0].StartAddress; + mi.HotLength = (uint)method.RuntimeFunctions[0].Size; mi.MethodToken = (uint)MetadataTokens.GetToken(method.ComponentReader.MetadataReader, method.MethodHandle); mi.AssemblyName = method.ComponentReader.MetadataReader.GetString(method.ComponentReader.MetadataReader.GetAssemblyDefinition().Name); mi.ColdRVA = 0; + mi.ColdLength = 0; yield return mi; } diff --git a/src/coreclr/tools/r2rdump/R2RDump.csproj b/src/coreclr/tools/r2rdump/R2RDump.csproj index f80b118cb64..65c929eeb36 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.csproj +++ b/src/coreclr/tools/r2rdump/R2RDump.csproj @@ -21,6 +21,7 @@ <PackageReference Include="System.CommandLine"> <Version>$(SystemCommandLineVersion)</Version> </PackageReference> + <ProjectReference Include="..\aot\ILCompiler.Diagnostics\ILCompiler.Diagnostics.csproj" /> <ProjectReference Include="..\aot\ILCompiler.Reflection.ReadyToRun\ILCompiler.Reflection.ReadyToRun.csproj" /> <Content Include="$(CoreDisToolsLibrary)" Condition="Exists('$(CoreDisToolsLibrary)')"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> diff --git a/src/coreclr/tools/r2rdump/R2RDump.sln b/src/coreclr/tools/r2rdump/R2RDump.sln index 0ba9942f9d0..6596a597f10 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.sln +++ b/src/coreclr/tools/r2rdump/R2RDump.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29703.146 @@ -7,6 +6,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "R2RDump", "R2RDump.csproj", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Reflection.ReadyToRun", "..\aot\ILCompiler.Reflection.ReadyToRun\ILCompiler.Reflection.ReadyToRun.csproj", "{E2A577E5-7AF3-49B3-BA78-7071B75ED64B}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Diagnostics", "..\aot\ILCompiler.Diagnostics\ILCompiler.Diagnostics.csproj", "{4E9512BA-F963-472A-B689-37D4D32456F3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,8 +16,8 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|Any CPU.ActiveCfg = Debug|x64 + {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|Any CPU.Build.0 = Debug|x64 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|x64.ActiveCfg = Debug|x64 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Debug|x64.Build.0 = Debug|x64 {00CCF6D0-5905-428E-A2A2-2A6D09D8C257}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -31,6 +32,14 @@ Global {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|Any CPU.Build.0 = Release|Any CPU {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|x64.ActiveCfg = Release|x64 {E2A577E5-7AF3-49B3-BA78-7071B75ED64B}.Release|x64.Build.0 = Release|x64 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|Any CPU.ActiveCfg = Debug|x64 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|Any CPU.Build.0 = Debug|x64 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|x64.ActiveCfg = Debug|x64 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Debug|x64.Build.0 = Debug|x64 + {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|Any CPU.Build.0 = Release|Any CPU + {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|x64.ActiveCfg = Release|Any CPU + {4E9512BA-F963-472A-B689-37D4D32456F3}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/coreclr/tools/r2rtest/BuildOptions.cs b/src/coreclr/tools/r2rtest/BuildOptions.cs index ed72c5c194a..e2e57ca9a42 100644 --- a/src/coreclr/tools/r2rtest/BuildOptions.cs +++ b/src/coreclr/tools/r2rtest/BuildOptions.cs @@ -25,6 +25,7 @@ namespace R2RTest public bool NoEtw { get; set; } public bool NoCleanup { get; set; } public bool Map { get; set; } + public bool Pdb { get; set; } public FileInfo PackageList { get; set; } public int DegreeOfParallelism { get; set; } public bool Sequential { get; set; } diff --git a/src/coreclr/tools/r2rtest/CommandLineOptions.cs b/src/coreclr/tools/r2rtest/CommandLineOptions.cs index b778264be5d..86da62ae81b 100644 --- a/src/coreclr/tools/r2rtest/CommandLineOptions.cs +++ b/src/coreclr/tools/r2rtest/CommandLineOptions.cs @@ -53,6 +53,7 @@ namespace R2RTest NoEtw(), NoCleanup(), Map(), + Pdb(), DegreeOfParallelism(), Sequential(), Framework(), @@ -90,6 +91,7 @@ namespace R2RTest NoEtw(), NoCleanup(), Map(), + Pdb(), DegreeOfParallelism(), Sequential(), Framework(), @@ -119,6 +121,8 @@ namespace R2RTest VerifyTypeAndFieldLayout(), NoCrossgen2(), NoCleanup(), + Map(), + Pdb(), Crossgen2Parallelism(), Crossgen2JitPath(), DegreeOfParallelism(), @@ -146,6 +150,8 @@ namespace R2RTest PackageList(), Crossgen(), NoCleanup(), + Map(), + Pdb(), DegreeOfParallelism(), CompilationTimeoutMinutes(), ExecutionTimeoutMinutes(), @@ -159,9 +165,11 @@ namespace R2RTest InputDirectory(), DegreeOfParallelism(), AspNetPath(), - CompositeScenario() + CompositeScenario(), + Map(), + Pdb(), }, - options => + options => { var compileSerp = new CompileSerpCommand(options); return compileSerp.CompileSerpAssemblies(); @@ -183,7 +191,7 @@ namespace R2RTest Option ReferencePath() => new Option<DirectoryInfo[]>(new[] { "--reference-path", "-r" }, "Folder containing assemblies to reference during compilation") - { Argument = new Argument<DirectoryInfo[]>() { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly() }; + { Argument = new Argument<DirectoryInfo[]>() { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly() }; Option Crossgen() => new Option<bool>(new[] { "--crossgen" }, "Compile the apps using Crossgen in the CORE_ROOT folder"); @@ -218,6 +226,9 @@ namespace R2RTest Option Map() => new Option<bool>(new[] { "--map" }, "Generate a map file (Crossgen2)"); + Option Pdb() => + new Option<bool>(new[] { "--pdb" }, "Generate PDB symbol information (Crossgen2 / Windows only)"); + Option DegreeOfParallelism() => new Option<int>(new[] { "--degree-of-parallelism", "-dop" }, "Override default compilation / execution DOP (default = logical processor count)"); diff --git a/src/coreclr/tools/r2rtest/Crossgen2Runner.cs b/src/coreclr/tools/r2rtest/Crossgen2Runner.cs index 857f62364cc..19d13dc2fc6 100644 --- a/src/coreclr/tools/r2rtest/Crossgen2Runner.cs +++ b/src/coreclr/tools/r2rtest/Crossgen2Runner.cs @@ -86,6 +86,12 @@ namespace R2RTest // Output yield return $"-o:{outputFileName}"; + if (_options.Pdb) + { + yield return $"--pdb"; + yield return $"--pdb-path:{Path.GetDirectoryName(outputFileName)}"; + } + if (_options.TargetArch != null) { yield return $"--targetarch={_options.TargetArch}"; diff --git a/src/tests/Common/CLRTest.CrossGen.targets b/src/tests/Common/CLRTest.CrossGen.targets index 34cbfe1471a..ae9d3d2acd8 100644 --- a/src/tests/Common/CLRTest.CrossGen.targets +++ b/src/tests/Common/CLRTest.CrossGen.targets @@ -22,7 +22,7 @@ WARNING: When setting properties based on their current state (for example: <BashScriptSnippetGen>$(BashScriptSnippetGen);GetCrossgenBashScript</BashScriptSnippetGen> <BatchScriptSnippetGen>$(BatchScriptSnippetGen);GetCrossgenBatchScript</BatchScriptSnippetGen> </PropertyGroup> - + <!-- This returns the portion of the execution script that generates the required lines to crossgen the test executable. @@ -72,7 +72,7 @@ if [ ! -z ${RunCrossGen2+x} ]%3B then compilationDoneFlagFile="IL-CG2/done" if [ -d IL-CG2 ]%3B then while [ ! -f $compilationDoneFlagFile ]%3B - do + do echo "Waiting for concurrent Crossgen2 compilation: $compilationDoneFlagFile" sleep 5%3B done @@ -88,19 +88,19 @@ if [ ! -z ${RunCrossGen2+x} ]%3B then fi ExtraCrossGen2Args+=" $(CrossGen2TestExtraArguments)" - + if [ ! -z ${LargeVersionBubble+x} ]%3B then ExtraCrossGen2Args+=" --inputbubble" fi - + __cg2ExitCode=0 OneFileCrossgen2() { __OutputFile=$1 - + __ResponseFile="$__OutputFile.rsp" rm $__ResponseFile - + # Suppress the GC stress COMPlus for the duration of Crossgen2 execution local gcStressModeToRestore=$COMPlus_GCStress; local heapVerifyModeToRestore=$COMPlus_HeapVerify; @@ -108,7 +108,7 @@ if [ ! -z ${RunCrossGen2+x} ]%3B then export COMPlus_GCStress= export COMPlus_HeapVerify= export COMPlus_ReadyToRun= - + __Command=$_DebuggerFullPath # Tests run locally need __TestDotNetCmd (set by runtest.py) or a compatible 5.0 dotnet runtime in the path if [ ! -z ${__TestDotNetCmd+x} ] %3B then @@ -119,27 +119,27 @@ if [ ! -z ${RunCrossGen2+x} ]%3B then __Command+=" $CORE_ROOT/crossgen2/crossgen2.dll" __Command+=" @$__ResponseFile" __Command+=" $ExtraCrossGen2Args" - + echo $2 >> $__ResponseFile - + echo -o:$__OutputFile>>$__ResponseFile echo -r:$CORE_ROOT/System.*.dll>>$__ResponseFile echo -r:$CORE_ROOT/Microsoft.*.dll>>$__ResponseFile echo -r:$CORE_ROOT/mscorlib.dll>>$__ResponseFile echo --verify-type-and-field-layout>>$__ResponseFile echo --targetarch:$(TargetArchitecture)>>$__ResponseFile - + echo "Response file: $__ResponseFile" cat $__ResponseFile echo "Running CrossGen2: $__Command" $__Command __cg2ExitCode=$? - + export COMPlus_GCStress=$gcStressModeToRestore export COMPlus_HeapVerify=$heapVerifyModeToRestore export COMPlus_ReadyToRun=$readyToRunModeToRestore } - + if [ ! -z ${CompositeBuildMode+x} ]%3B then ExtraCrossGen2Args+=" --composite" OneFileCrossgen2 "$PWD/composite-r2r.dll" "$PWD/IL-CG2/*.dll" @@ -155,7 +155,7 @@ if [ ! -z ${RunCrossGen2+x} ]%3B then fi done fi - + echo "Crossgen2 compilation finished, exit code $__cg2ExitCode" >> $compilationDoneFlagFile if [ $__cg2ExitCode -ne 0 ]; then echo Crossgen2 failed with exitcode: $__cg2ExitCode @@ -283,6 +283,10 @@ if defined RunCrossGen2 ( echo -r:!CORE_ROOT!\mscorlib.dll>>!__ResponseFile! echo -r:!CORE_ROOT!\netstandard.dll>>!__ResponseFile! + if not "$(__CreatePdb)" == "" ( + echo --pdb>>!__ResponseFile! + ) + echo Response file: !__ResponseFile! type !__ResponseFile! @@ -310,7 +314,4 @@ if defined RunCrossGen2 ( <CLRTestBatchPreCommands>$(CLRTestBatchPreCommands);$(CrossgenBatchScript)</CLRTestBatchPreCommands> </PropertyGroup> </Target> - - - </Project> diff --git a/src/tests/build.cmd b/src/tests/build.cmd index 02ba6b5a49c..985568d0712 100644 --- a/src/tests/build.cmd +++ b/src/tests/build.cmd @@ -56,6 +56,7 @@ set __TargetsWindows=1 set __DoCrossgen= set __DoCrossgen2= set __CompositeBuildMode= +set __CreatePdb= set __CopyNativeTestBinaries=0 set __CopyNativeProjectsAfterCombinedTestBuild=true set __SkipGenerateLayout=0 @@ -108,6 +109,7 @@ if /i "%1" == "buildagainstpackages" (echo error: Remove /BuildAgainstPackages if /i "%1" == "crossgen" (set __DoCrossgen=1&set __TestBuildMode=crossgen&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "crossgen2" (set __DoCrossgen2=1&set __TestBuildMode=crossgen2&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "composite" (set __CompositeBuildMode=1&set __DoCrossgen2=1&set __TestBuildMode=crossgen2&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) +if /i "%1" == "pdb" (set __CreatePdb=1&shift&goto Arg_Loop) if /i "%1" == "targetsNonWindows" (set __TargetsWindows=0&set processedArgs=!processedArgs! %1&shift&goto Arg_Loop) if /i "%1" == "Exclude" (set __Exclude=%2&set processedArgs=!processedArgs! %1 %2&shift&shift&goto Arg_Loop) if /i "%1" == "-priority" (set __Priority=%2&shift&set processedArgs=!processedArgs! %1=%2&shift&goto Arg_Loop) @@ -583,6 +585,10 @@ set "__CrossgenOutputDir=%__TestIntermediatesDir%\crossgen.out" set __CrossgenCmd="%__RepoRootDir%\dotnet.cmd" "%CORE_ROOT%\R2RTest\R2RTest.dll" compile-framework -cr "%CORE_ROOT%" --output-directory "%__CrossgenOutputDir%" --release --nocleanup --target-arch %__BuildArch% -dop %NUMBER_OF_PROCESSORS% +if defined __CreatePdb ( + set __CrossgenCmd=!__CrossgenCmd! --pdb +) + if defined __CompositeBuildMode ( set __CrossgenCmd=%__CrossgenCmd% --composite ) else ( @@ -620,7 +626,12 @@ if %__exitCode% neq 0 ( exit /b 1 ) -move "%__CrossgenOutputDir%\*.dll" %CORE_ROOT% > nul +move /Y "%__CrossgenOutputDir%\*.dll" %CORE_ROOT% > nul + +if defined __CreatePdb ( + move /Y "!__CrossgenOutputDir!\*.ni.pdb" !CORE_ROOT! > nul + copy /Y "!__BinDir!\PDB\System.Private.CoreLib.ni.pdb" !CORE_ROOT! > nul +) exit /b 0 |