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
-
+