Welcome to mirror list, hosted at ThFree Co, Russian Federation.

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