diff --git a/src/coreclr/crossgen-corelib.proj b/src/coreclr/crossgen-corelib.proj index fadc1f514c872f..61c31d77ab97a8 100644 --- a/src/coreclr/crossgen-corelib.proj +++ b/src/coreclr/crossgen-corelib.proj @@ -8,7 +8,7 @@ AfterTargets="Build"> - false + true $(TargetOS).$(TargetArchitecture).$(Configuration) $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts')) @@ -65,27 +65,22 @@ $(CrossGenDllCmd) $(CoreLibInputPath) + + $(CrossGenDllCmd) --pdb --pdb-path:$([MSBuild]::NormalizePath('$(BinDir)', 'PDB')) + + + + $(CrossGenDllCmd) --perfmap --perfmap-path:$(BinDir) + + + call $([MSBuild]::NormalizePath('$(RepoRoot)', 'src', 'coreclr', 'setup_vs_tools.cmd')) && + $(CrossGen1Cmd) /out "$(CoreLibOutputPath)" $(CrossGenDllCmd) "$(CoreLibInputPath)" - - - - $(CrossGen1Cmd) /CreatePerfMap "$(BinDir)" $(CrossGenPerfMapCmd) "$(CoreLibOutputPath)" - - - - $(DotNetCli) $([MSBuild]::NormalizePath('$(BinDir)', 'r2rdump', 'r2rdump.dll')) - $(CrossGenPdbCmd) --create-pdb - $(CrossGenPdbCmd) --pdb-path:$([MSBuild]::NormalizePath('$(BinDir)', 'PDB')) - $(CrossGenPdbCmd) --in:$(CoreLibOutputPath) - - - - call $([MSBuild]::NormalizePath('$(RepoRoot)', 'src', 'coreclr', 'setup_vs_tools.cmd')) && $(VsSetupCmd) $(CrossGen1Cmd) /CreatePdb "$([MSBuild]::NormalizePath('$(BinDir)', 'PDB'))" $(CrossGenPdbCmd) "$(CoreLibOutputPath)" @@ -97,11 +92,11 @@ - + - + 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 00000000000000..8c9419db564aa9 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ILCompiler.Diagnostics.csproj @@ -0,0 +1,20 @@ + + + + ILCompiler.Diagnostics + Library + true + $(NetCoreAppToolCurrent) + READYTORUN;$(DefineConstants) + x64;x86;arm;arm64 + AnyCPU + false + + + false + Debug;Release;Checked + + + diff --git a/src/coreclr/tools/r2rdump/ISymNGenWriter.cs b/src/coreclr/tools/aot/ILCompiler.Diagnostics/ISymNGenWriter.cs similarity index 100% rename from src/coreclr/tools/r2rdump/ISymNGenWriter.cs rename to 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 00000000000000..0f1b6dec57d750 --- /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 similarity index 94% rename from src/coreclr/tools/r2rdump/PdbWriter.cs rename to src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs index 99216ab6736f81..620fb783441830 100644 --- a/src/coreclr/tools/r2rdump/PdbWriter.cs +++ b/src/coreclr/tools/aot/ILCompiler.Diagnostics/PdbWriter.cs @@ -10,7 +10,7 @@ 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 @@ public enum PDBExtraData kPDBLines = 0x00000001, }; - struct MethodInfo - { - public string AssemblyName; - public uint MethodToken; - public uint HotRVA; - public string Name; - public uint ColdRVA; - } - - interface IModuleData - { - IEnumerable Methods { get; } - } - public enum SymChecksumType : byte { None = 0, // indicates no checksum is available @@ -85,7 +71,7 @@ public bool Equals(SymDocument other) } } - class PdbWriter + public class PdbWriter { string _pdbPath; PDBExtraData _pdbExtraData; @@ -204,26 +190,33 @@ private void WritePDBDataHelper(string dllPath, IEnumerable methods) 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 00000000000000..4696bc9e2d18ff --- /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 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 df25e03107896c..47976ee402a723 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 ILCompiler.DependencyAnalysis.ReadyToRun; using ILCompiler.DependencyAnalysisFramework; +using ILCompiler.Diagnostics; using ILCompiler.PEWriter; using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData; @@ -49,26 +50,57 @@ internal class ReadyToRunObjectWriter /// private readonly IEnumerable _nodes; + /// + /// Set to non-null when the executable generator should output a map or symbol file. + /// + private readonly OutputInfoBuilder _outputInfoBuilder; + /// /// Set to non-null when the executable generator should output a map file. /// private readonly MapFileBuilder _mapFileBuilder; + + /// + /// Set to non-null when generating symbol info (PDB / PerfMap). + /// + private readonly SymbolFileBuilder _symbolFileBuilder; + /// /// True when the map file builder should emit a textual map file /// private bool _generateMapFile; + /// /// True when the map file builder should emit a CSV formatted map file /// private bool _generateMapCsvFile; + /// + /// True when the map file builder should emit a PDB symbol file (only supported on Windows) + /// + private bool _generatePdbFile; + + /// + /// Explicit specification of the output PDB path + /// + private string _pdbPath; + + /// + /// True when the map file builder should emit a PerfMap file + /// + private bool _generatePerfMapFile; + + /// + /// Explicit specification of the output PerfMap path + /// + private string _perfMapPath; + /// /// 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. /// private readonly int _customPESectionAlignment; - #if DEBUG private struct NodeInfo { @@ -87,7 +119,18 @@ public NodeInfo(ISymbolNode node, int nodeIndex, int symbolIndex) Dictionary _previouslyWrittenNodeNames = new Dictionary(); #endif - public ReadyToRunObjectWriter(string objectFilePath, EcmaModule componentModule, IEnumerable nodes, NodeFactory factory, bool generateMapFile, bool generateMapCsvFile, int customPESectionAlignment) + public ReadyToRunObjectWriter( + string objectFilePath, + EcmaModule componentModule, + IEnumerable 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 @@ public ReadyToRunObjectWriter(string objectFilePath, EcmaModule componentModule, _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 @@ public void EmitPortableExecutable() } } - 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 @@ public void EmitPortableExecutable() } } - if (_mapFileBuilder != null) + if (_outputInfoBuilder != null) { - r2rPeBuilder.AddSections(_mapFileBuilder); + r2rPeBuilder.AddSections(_outputInfoBuilder); if (_generateMapFile) { @@ -263,6 +328,26 @@ public void EmitPortableExecutable() 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 @@ public void EmitPortableExecutable() /// Logical index of the emitted node for diagnostic purposes /// Textual representation of the ObjecData blob in the map file /// Section to emit the blob into - /// Map file output stream - 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 @@ private void EmitObjectData(R2RPEBuilder r2rPeBuilder, ObjectData data, int node } #endif - r2rPeBuilder.AddObjectData(data, section, name, mapFileBuilder); + r2rPeBuilder.AddObjectData(data, section, name, _outputInfoBuilder); } - public static void EmitObject(string objectFilePath, EcmaModule componentModule, IEnumerable nodes, NodeFactory factory, bool generateMapFile, bool generateMapCsvFile, int customPESectionAlignment) + public static void EmitObject( + string objectFilePath, + EcmaModule componentModule, + IEnumerable 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 79888b3e89a974..a147fa3e45d727 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 @@ public sealed class ReadyToRunCodegenCompilation : Compilation 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 @@ internal ReadyToRunCodegenCompilation( bool resilient, bool generateMapFile, bool generateMapCsvFile, + bool generatePdbFile, + string pdbPath, + bool generatePerfMapFile, + string perfMapPath, int parallelism, ProfileDataManager profileData, ReadyToRunMethodLayoutAlgorithm methodLayoutAlgorithm, @@ -277,6 +285,10 @@ internal ReadyToRunCodegenCompilation( _parallelism = parallelism; _generateMapFile = generateMapFile; _generateMapCsvFile = generateMapCsvFile; + _generatePdbFile = generatePdbFile; + _pdbPath = pdbPath; + _generatePerfMapFile = generatePerfMapFile; + _perfMapPath = perfMapPath; _customPESectionAlignment = customPESectionAlignment; SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory, verifyTypeAndFieldLayout); _corInfoImpls = new ConditionalWeakTable(); @@ -307,7 +319,18 @@ public override void Compile(string outputFile) 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 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow } 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 03ef31b3888e01..acdd53e86a31bf 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 @@ public sealed class ReadyToRunCodegenCompilationBuilder : CompilationBuilder 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 @@ public ReadyToRunCodegenCompilationBuilder UseMapCsvFile(bool generateMapCsvFile 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 @@ public override ICompilation ToCompilation() _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 0d8a93f397f630..e6f136eb22377e 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 @@ true READYTORUN;$(DefineConstants) false - x64;x86 + x64;x86;arm;arm64 AnyCPU false @@ -19,6 +19,7 @@ + @@ -195,6 +196,8 @@ + + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs index eb2b9e2a842d02..726ae5fe079e6d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/MapFileBuilder.cs @@ -8,91 +8,19 @@ 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 { - /// - /// Base class for symbols and nodes in the map file implements common logic - /// for section / offset ordering. - /// - public class MapFileItem - { - public class Comparer : IComparer - { - 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)); - } - } - - /// - /// Item section index - /// - public readonly int SectionIndex; - - /// - /// Offset relative to section beginning - /// - public readonly int Offset; - - /// - /// Item name - /// - public readonly string Name; - - public MapFileItem(int sectionIndex, int offset, string name) - { - SectionIndex = sectionIndex; - Offset = offset; - Name = name; - } - } - - /// - /// This class represents a single node (contiguous block of data) in the output R2R PE file. - /// - public class MapFileNode : MapFileItem - { - /// - /// Node length (number of bytes). This doesn't include any external alignment - /// applied when concatenating the nodes to form sections. - /// - public readonly int Length; - - /// - /// Number of file-level relocations (.reloc section entries) used by the node. - /// - 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++; - } - } - - /// - /// 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). - /// - public class MapFileSymbol : MapFileItem - { - public MapFileSymbol(int sectionIndex, int offset, string name) - : base(sectionIndex, offset, name) - { - } - } - /// /// Helper class used to collect information to be output into the map file. /// @@ -118,7 +46,7 @@ public NodeTypeStatistics(string name) Name = name; } - public void AddNode(MapFileNode node) + public void AddNode(OutputNode node) { Debug.Assert(Name == node.Name); Count++; @@ -126,43 +54,13 @@ public void AddNode(MapFileNode node) } } - private readonly List _nodes; - private readonly List _symbols; - private readonly List
_sections; - - private readonly Dictionary _relocCounts; + private OutputInfoBuilder _outputInfoBuilder; private long _fileSize; - public MapFileBuilder() - { - _nodes = new List(); - _symbols = new List(); - _sections = new List
(); - - _relocCounts = new Dictionary(); - } - - 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 @@ public void SaveMap(string mapFileName) { 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 @@ public void SaveCsv(string nodeStatsCsvFileName, string mapCsvFileName) { 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 @@ private void WriteHeader(StreamWriter writer) 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 GetNodeTypeStatistics() { List nodeTypeStats = new List(); Dictionary statsNameIndex = new Dictionary(); - foreach (MapFileNode node in _nodes) + foreach (OutputNode node in _outputInfoBuilder.Nodes) { if (!statsNameIndex.TryGetValue(node.Name, out int statsIndex)) { @@ -268,7 +164,7 @@ private void WriteNodeTypeStatisticsCsv(StreamWriter writer) private void WriteRelocTypeStatistics(StreamWriter writer) { - KeyValuePair[] relocTypeCounts = _relocCounts.ToArray(); + KeyValuePair[] relocTypeCounts = _outputInfoBuilder.RelocCounts.ToArray(); Array.Sort(relocTypeCounts, (a, b) => b.Value.CompareTo(a.Value)); WriteTitle(writer, "Reloc Type Statistics"); @@ -284,13 +180,12 @@ private void WriteRelocTypeStatistics(StreamWriter writer) 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 @@ private void WriteSections(StreamWriter writer) { 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 @@ private void WriteMap(StreamWriter writer) 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 @@ private void WriteMap(StreamWriter writer) 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 @@ private void WriteMapCsv(StreamWriter writer) 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 @@ private void WriteMapCsv(StreamWriter writer) 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 @@ private void WriteTitle(StreamWriter writer, string title) 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 00000000000000..00a73721531756 --- /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 +{ + /// + /// Base class for symbols and nodes in the output file implements common logic + /// for section / offset ordering. + /// + public class OutputItem + { + public class Comparer : IComparer + { + 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)); + } + } + + /// + /// Item section index + /// + public readonly int SectionIndex; + + /// + /// Offset relative to section beginning + /// + public readonly int Offset; + + /// + /// Item name + /// + public readonly string Name; + + public OutputItem(int sectionIndex, int offset, string name) + { + SectionIndex = sectionIndex; + Offset = offset; + Name = name; + } + } + + /// + /// This class represents a single node (contiguous block of data) in the output R2R PE file. + /// + public class OutputNode : OutputItem + { + /// + /// Node length (number of bytes). This doesn't include any external alignment + /// applied when concatenating the nodes to form sections. + /// + public readonly int Length; + + /// + /// Number of file-level relocations (.reloc section entries) used by the node. + /// + 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++; + } + } + + /// + /// 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). + /// + public class OutputSymbol : OutputItem + { + public OutputSymbol(int sectionIndex, int offset, string name) + : base(sectionIndex, offset, name) + { + } + } + + /// + /// Common class used to collect information to use when emitting map files and symbol files. + /// + public class OutputInfoBuilder + { + private readonly List _nodes; + private readonly List _symbols; + private readonly List
_sections; + + private readonly Dictionary _nodeSymbolMap; + private readonly Dictionary _methodSymbolMap; + + private readonly Dictionary _relocCounts; + + public OutputInfoBuilder() + { + _nodes = new List(); + _symbols = new List(); + _sections = new List
(); + + _nodeSymbolMap = new Dictionary(); + _methodSymbolMap = new Dictionary(); + + _relocCounts = new Dictionary(); + } + + 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 EnumerateMethods() + { + DebugNameFormatter nameFormatter = new DebugNameFormatter(); + TypeNameFormatter typeNameFormatter = new TypeString(); + HashSet emittedMethods = new HashSet(); + foreach (KeyValuePair 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 Nodes => _nodes; + public IReadOnlyList
Sections => _sections; + public IReadOnlyList Symbols => _symbols; + + public IReadOnlyDictionary 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 759fad4ac6fd7f..3f6764b0e804ba 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 @@ public void SetWin32Resources(ISymbolNode symbol, int resourcesSize) /// Object data to emit /// Target section /// Textual name of the object data for diagnostic purposese - /// Optional map file builder to output the data item to - public void AddObjectData(ObjectNode.ObjectData objectData, ObjectNodeSection section, string name, MapFileBuilder mapFileBuilder) + /// Optional output info builder to output the data item to + public void AddObjectData(DependencyAnalysis.ObjectNode.ObjectData objectData, ObjectNodeSection section, string name, OutputInfoBuilder outputInfoBuilder) { if (_written) { @@ -266,7 +266,7 @@ public void AddObjectData(ObjectNode.ObjectData objectData, ObjectNodeSection se throw new NotImplementedException(); } - _sectionBuilder.AddObjectData(objectData, targetSectionIndex, name, mapFileBuilder); + _sectionBuilder.AddObjectData(objectData, targetSectionIndex, name, outputInfoBuilder); } /// @@ -312,10 +312,10 @@ public void Write(Stream outputStream, int? timeDateStamp) /// /// Fill in map builder section table. /// - /// Map file builder to set up - public void AddSections(MapFileBuilder mapFileBuilder) + /// Object info builder to set up + public void AddSections(OutputInfoBuilder outputInfoBuilder) { - _sectionBuilder.AddSections(mapFileBuilder); + _sectionBuilder.AddSections(outputInfoBuilder); } /// diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ObjectWriter/SectionBuilder.cs index 6ba8e55636831f..7c3e16b763c0ca 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 @@ private NameMangler GetNameMangler() /// Block to add /// Section index /// Node name to emit in the map file - /// Optional map file to emit - public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, string name, MapFileBuilder mapFileBuilder) + /// Optional output info to collect (used for creating maps and symbols) + public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, string name, OutputInfoBuilder outputInfoBuilder) { Section section = _sections[sectionIndex]; @@ -475,10 +475,10 @@ public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, st } } - 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 @@ public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, st 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 @@ public void AddObjectData(ObjectNode.ObjectData objectData, int sectionIndex, st { 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 @@ public IEnumerable GetSections() 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 00000000000000..62eeb0494a9ac9 --- /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 e81eae9c024067..cb78b75b95945d 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 ab4afd4b9e1302..910743f709b159 100644 --- a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs +++ b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs @@ -46,6 +46,10 @@ internal class CommandLineOptions 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 @@ public CommandLineOptions(string[] args) 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 e20b25be991282..35dbcecfe593cf 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -582,6 +582,8 @@ private int Run(string[] args) .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 d677fe8d7711e9..6b3e6bec1265c6 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -309,4 +309,16 @@ Warning: -Od overrides other optimization options - \ No newline at end of file + + Generate PDB symbol information file (supported on Windows only) + + + Generate PerfMap symbol information file for use by PerfInfo + + + Explicit specification of the output PDB file path + + + Explicit specification of the PerfMap file path + + diff --git a/src/coreclr/tools/r2rdump/CommandLineOptions.cs b/src/coreclr/tools/r2rdump/CommandLineOptions.cs index f41ffd43744a85..7ada4e9748ddab 100644 --- a/src/coreclr/tools/r2rdump/CommandLineOptions.cs +++ b/src/coreclr/tools/r2rdump/CommandLineOptions.cs @@ -36,7 +36,9 @@ public static RootCommand RootCommand() command.AddOption(new Option(new[] { "--inlineSignatureBinary", "--isb" }, "Embed binary signature into its textual representation")); command.AddOption(new Option(new[] { "--signatureBinary", "--sb" }, "Append signature binary to its textual representation")); command.AddOption(new Option(new[] { "--create-pdb" }, "Create PDB")); - command.AddOption(new Option(new[] { "--pdb-path" }, "PDB output path for --createpdb")); + command.AddOption(new Option(new[] { "--pdb-path" }, "PDB output path for --create-pdb")); + command.AddOption(new Option(new[] { "--create-perfmap" }, "Create PerfMap")); + command.AddOption(new Option(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 7d465eb36f3e2d..fa732db7aab6ce 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.cs +++ b/src/coreclr/tools/r2rdump/R2RDump.cs @@ -12,13 +12,14 @@ 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 @@ public class DumpOptions : IAssemblyResolver 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; } @@ -360,7 +365,7 @@ private void QueryRuntimeFunction(ReadyToRunReader r2r, IEnumerable quer 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) { @@ -409,7 +414,17 @@ public void Dump(ReadyToRunReader r2r) 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) @@ -421,16 +436,18 @@ public void Dump(ReadyToRunReader r2r) _dumper.End(); } - IEnumerable ProducePdbWriterMethods(ReadyToRunReader r2r) + IEnumerable 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 f80b118cb64d1d..65c929eeb3648f 100644 --- a/src/coreclr/tools/r2rdump/R2RDump.csproj +++ b/src/coreclr/tools/r2rdump/R2RDump.csproj @@ -21,6 +21,7 @@ $(SystemCommandLineVersion) + PreserveNewest diff --git a/src/coreclr/tools/r2rdump/R2RDump.sln b/src/coreclr/tools/r2rdump/R2RDump.sln index 0ba9942f9d0de6..6596a597f104a5 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 ed72c5c194ab5e..e2e57ca9a42aef 100644 --- a/src/coreclr/tools/r2rtest/BuildOptions.cs +++ b/src/coreclr/tools/r2rtest/BuildOptions.cs @@ -25,6 +25,7 @@ public class BuildOptions 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 b778264be5d7f8..86da62ae81bd11 100644 --- a/src/coreclr/tools/r2rtest/CommandLineOptions.cs +++ b/src/coreclr/tools/r2rtest/CommandLineOptions.cs @@ -53,6 +53,7 @@ Command CompileFolder() => NoEtw(), NoCleanup(), Map(), + Pdb(), DegreeOfParallelism(), Sequential(), Framework(), @@ -90,6 +91,7 @@ Command CompileSubtree() => NoEtw(), NoCleanup(), Map(), + Pdb(), DegreeOfParallelism(), Sequential(), Framework(), @@ -119,6 +121,8 @@ Command CompileFramework() => VerifyTypeAndFieldLayout(), NoCrossgen2(), NoCleanup(), + Map(), + Pdb(), Crossgen2Parallelism(), Crossgen2JitPath(), DegreeOfParallelism(), @@ -146,6 +150,8 @@ Command CompileNugetPackages() => PackageList(), Crossgen(), NoCleanup(), + Map(), + Pdb(), DegreeOfParallelism(), CompilationTimeoutMinutes(), ExecutionTimeoutMinutes(), @@ -159,9 +165,11 @@ Command CompileSerp() => InputDirectory(), DegreeOfParallelism(), AspNetPath(), - CompositeScenario() + CompositeScenario(), + Map(), + Pdb(), }, - options => + options => { var compileSerp = new CompileSerpCommand(options); return compileSerp.CompileSerpAssemblies(); @@ -183,7 +191,7 @@ Option CoreRootDirectory() => Option ReferencePath() => new Option(new[] { "--reference-path", "-r" }, "Folder containing assemblies to reference during compilation") - { Argument = new Argument() { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly() }; + { Argument = new Argument() { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly() }; Option Crossgen() => new Option(new[] { "--crossgen" }, "Compile the apps using Crossgen in the CORE_ROOT folder"); @@ -218,6 +226,9 @@ Option NoCleanup() => Option Map() => new Option(new[] { "--map" }, "Generate a map file (Crossgen2)"); + Option Pdb() => + new Option(new[] { "--pdb" }, "Generate PDB symbol information (Crossgen2 / Windows only)"); + Option DegreeOfParallelism() => new Option(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 857f62364cccbe..19d13dc2fc6073 100644 --- a/src/coreclr/tools/r2rtest/Crossgen2Runner.cs +++ b/src/coreclr/tools/r2rtest/Crossgen2Runner.cs @@ -86,6 +86,12 @@ protected override IEnumerable BuildCommandLineArguments(IEnumerable$(BashScriptSnippetGen);GetCrossgenBashScript $(BatchScriptSnippetGen);GetCrossgenBatchScript - +