diff --git a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
index d314741a634586..acd1232752009a 100644
--- a/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
+++ b/src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets
@@ -213,6 +213,7 @@ The .NET Foundation licenses this file to you under the MIT license.
+
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs
index 3e24857b2cd46a..d2f867d5ee296a 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs
@@ -14,6 +14,8 @@ public class MethodExceptionHandlingInfoNode : ObjectNode, ISymbolDefinitionNode
private readonly MethodDesc _owningMethod;
private readonly ObjectData _data;
+ public MethodDesc Method => _owningMethod;
+
public MethodExceptionHandlingInfoNode(MethodDesc owningMethod, ObjectData data)
{
_owningMethod = owningMethod;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MstatObjectDumper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MstatObjectDumper.cs
new file mode 100644
index 00000000000000..0f0af381c2e6ed
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MstatObjectDumper.cs
@@ -0,0 +1,117 @@
+// 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.IO;
+using System.Reflection.Metadata;
+using System.Reflection.Metadata.Ecma335;
+
+using Internal.Text;
+using Internal.TypeSystem;
+
+using ILCompiler.DependencyAnalysis;
+
+using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
+using AssemblyName = System.Reflection.AssemblyName;
+using System.Collections.Generic;
+using static ILCompiler.DependencyAnalysis.ObjectNode;
+
+namespace ILCompiler
+{
+ public class MstatObjectDumper : ObjectDumper
+ {
+ private const int VersionMajor = 1;
+ private const int VersionMinor = 0;
+
+ private readonly string _fileName;
+ private readonly TypeSystemMetadataEmitter _emitter;
+
+ private readonly InstructionEncoder _types = new InstructionEncoder(new BlobBuilder());
+
+ private Dictionary _methods = new();
+ private Dictionary _methodEhInfo = new();
+ private Dictionary _blobs = new();
+
+ private Utf8StringBuilder _utf8StringBuilder = new Utf8StringBuilder();
+
+ public MstatObjectDumper(string fileName, TypeSystemContext context)
+ {
+ _fileName = fileName;
+ var asmName = new AssemblyName(Path.GetFileName(fileName));
+ asmName.Version = new Version(VersionMajor, VersionMinor);
+ _emitter = new TypeSystemMetadataEmitter(asmName, context);
+ _emitter.AllowUseOfAddGlobalMethod();
+ }
+
+ internal override void Begin()
+ {
+ }
+
+ protected override void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData)
+ {
+ string mangledName = null;
+ if (node is ISymbolNode symbol)
+ {
+ _utf8StringBuilder.Clear();
+ symbol.AppendMangledName(mangler, _utf8StringBuilder);
+ mangledName = _utf8StringBuilder.ToString();
+ }
+
+ switch (node)
+ {
+ case EETypeNode eeType:
+ SerializeSimpleEntry(_types, eeType.Type, mangledName, objectData);
+ break;
+ case IMethodBodyNode methodBody:
+ var codeInfo = (INodeWithCodeInfo)node;
+ _methods.Add(methodBody.Method, (mangledName, objectData.Data.Length, codeInfo.GCInfo.Length));
+ break;
+ case MethodExceptionHandlingInfoNode ehInfoNode:
+ _methodEhInfo.Add(ehInfoNode.Method, objectData.Data.Length);
+ break;
+ default:
+ string nodeName = GetObjectNodeName(node);
+ if (!_blobs.TryGetValue(nodeName, out int size))
+ size = 0;
+ _blobs[nodeName] = size + objectData.Data.Length;
+ break;
+ }
+ }
+
+ private void SerializeSimpleEntry(InstructionEncoder encoder, TypeSystemEntity entity, string mangledName, ObjectData blob)
+ {
+ encoder.OpCode(ILOpCode.Ldtoken);
+ encoder.Token(_emitter.EmitMetadataHandleForTypeSystemEntity(entity));
+ encoder.LoadString(_emitter.GetUserStringHandle(mangledName));
+ encoder.LoadConstantI4(blob.Data.Length);
+ }
+
+ internal override void End()
+ {
+ var methods = new InstructionEncoder(new BlobBuilder());
+ foreach (var m in _methods)
+ {
+ methods.OpCode(ILOpCode.Ldtoken);
+ methods.Token(_emitter.EmitMetadataHandleForTypeSystemEntity(m.Key));
+ methods.LoadString(_emitter.GetUserStringHandle(m.Value.MangledName));
+ methods.LoadConstantI4(m.Value.Size);
+ methods.LoadConstantI4(m.Value.GcInfoSize);
+ methods.LoadConstantI4(_methodEhInfo.GetValueOrDefault(m.Key));
+ }
+
+ var blobs = new InstructionEncoder(new BlobBuilder());
+ foreach (var b in _blobs)
+ {
+ blobs.LoadString(_emitter.GetUserStringHandle(b.Key));
+ blobs.LoadConstantI4(b.Value);
+ }
+
+ _emitter.AddGlobalMethod("Methods", methods, 0);
+ _emitter.AddGlobalMethod("Types", _types, 0);
+ _emitter.AddGlobalMethod("Blobs", blobs, 0);
+
+ using (var fs = File.OpenWrite(_fileName))
+ _emitter.SerializeToStream(fs);
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDumper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDumper.cs
index 8d56104eb2502b..10d229da90c47e 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDumper.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDumper.cs
@@ -2,9 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-using System.IO;
-using System.Security.Cryptography;
-using System.Xml;
+using System.Collections.Generic;
using Internal.Text;
@@ -14,31 +12,14 @@
namespace ILCompiler
{
- public class ObjectDumper : IObjectDumper
+ public abstract class ObjectDumper : IObjectDumper
{
- private readonly string _fileName;
- private SHA256 _sha256;
- private XmlWriter _writer;
+ internal abstract void Begin();
+ internal abstract void End();
+ void IObjectDumper.DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData) => DumpObjectNode(mangler, node, objectData);
+ protected abstract void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData);
- public ObjectDumper(string fileName)
- {
- _fileName = fileName;
- }
-
- internal void Begin()
- {
- var settings = new XmlWriterSettings
- {
- CloseOutput = true,
- Indent = true,
- };
-
- _sha256 = SHA256.Create();
- _writer = XmlWriter.Create(File.CreateText(_fileName), settings);
- _writer.WriteStartElement("ObjectNodes");
- }
-
- private static string GetObjectNodeName(ObjectNode node)
+ protected static string GetObjectNodeName(ObjectNode node)
{
string name = node.GetType().Name;
@@ -54,46 +35,42 @@ private static string GetObjectNodeName(ObjectNode node)
return name;
}
- void IObjectDumper.DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData)
+ public static ObjectDumper Compose(IEnumerable dumpers)
{
- string name = null;
-
- _writer.WriteStartElement(GetObjectNodeName(node));
-
- var symbolNode = node as ISymbolNode;
- if (symbolNode != null)
- {
- Utf8StringBuilder sb = new Utf8StringBuilder();
- symbolNode.AppendMangledName(mangler, sb);
- name = sb.ToString();
- _writer.WriteAttributeString("Name", name);
- }
+ var dumpersList = new ArrayBuilder();
- _writer.WriteAttributeString("Length", objectData.Data.Length.ToStringInvariant());
- _writer.WriteAttributeString("Hash", HashData(objectData.Data));
- _writer.WriteEndElement();
+ foreach (var dumper in dumpers)
+ dumpersList.Add(dumper);
- var nodeWithCodeInfo = node as INodeWithCodeInfo;
- if (nodeWithCodeInfo != null)
+ return dumpersList.Count switch
{
- _writer.WriteStartElement("GCInfo");
- _writer.WriteAttributeString("Name", name);
- _writer.WriteAttributeString("Length", nodeWithCodeInfo.GCInfo.Length.ToStringInvariant());
- _writer.WriteAttributeString("Hash", HashData(nodeWithCodeInfo.GCInfo));
- _writer.WriteEndElement();
- }
+ 0 => null,
+ 1 => dumpersList[0],
+ _ => new ComposedObjectDumper(dumpersList.ToArray()),
+ };
}
- private string HashData(byte[] data)
+ private class ComposedObjectDumper : ObjectDumper
{
- return BitConverter.ToString(_sha256.ComputeHash(data)).Replace("-", "").ToLower();
- }
+ private readonly ObjectDumper[] _dumpers;
- internal void End()
- {
- _writer.WriteEndElement();
- _writer.Dispose();
- _writer = null;
+ public ComposedObjectDumper(ObjectDumper[] dumpers) => _dumpers = dumpers;
+
+ protected override void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData)
+ {
+ foreach (var d in _dumpers)
+ d.DumpObjectNode(mangler, node, objectData);
+ }
+ internal override void Begin()
+ {
+ foreach (var d in _dumpers)
+ d.Begin();
+ }
+ internal override void End()
+ {
+ foreach (var d in _dumpers)
+ d.End();
+ }
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/XmlObjectDumper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/XmlObjectDumper.cs
new file mode 100644
index 00000000000000..b1fa747348f296
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/XmlObjectDumper.cs
@@ -0,0 +1,83 @@
+// 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.IO;
+using System.Security.Cryptography;
+using System.Xml;
+
+using Internal.Text;
+
+using ILCompiler.DependencyAnalysis;
+
+using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
+
+namespace ILCompiler
+{
+ public class XmlObjectDumper : ObjectDumper
+ {
+ private readonly string _fileName;
+ private SHA256 _sha256;
+ private XmlWriter _writer;
+
+ public XmlObjectDumper(string fileName)
+ {
+ _fileName = fileName;
+ }
+
+ internal override void Begin()
+ {
+ var settings = new XmlWriterSettings
+ {
+ CloseOutput = true,
+ Indent = true,
+ };
+
+ _sha256 = SHA256.Create();
+ _writer = XmlWriter.Create(File.CreateText(_fileName), settings);
+ _writer.WriteStartElement("ObjectNodes");
+ }
+
+ protected override void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData)
+ {
+ string name = null;
+
+ _writer.WriteStartElement(GetObjectNodeName(node));
+
+ var symbolNode = node as ISymbolNode;
+ if (symbolNode != null)
+ {
+ Utf8StringBuilder sb = new Utf8StringBuilder();
+ symbolNode.AppendMangledName(mangler, sb);
+ name = sb.ToString();
+ _writer.WriteAttributeString("Name", name);
+ }
+
+ _writer.WriteAttributeString("Length", objectData.Data.Length.ToStringInvariant());
+ _writer.WriteAttributeString("Hash", HashData(objectData.Data));
+ _writer.WriteEndElement();
+
+ var nodeWithCodeInfo = node as INodeWithCodeInfo;
+ if (nodeWithCodeInfo != null)
+ {
+ _writer.WriteStartElement("GCInfo");
+ _writer.WriteAttributeString("Name", name);
+ _writer.WriteAttributeString("Length", nodeWithCodeInfo.GCInfo.Length.ToStringInvariant());
+ _writer.WriteAttributeString("Hash", HashData(nodeWithCodeInfo.GCInfo));
+ _writer.WriteEndElement();
+ }
+ }
+
+ private string HashData(byte[] data)
+ {
+ return BitConverter.ToString(_sha256.ComputeHash(data)).Replace("-", "").ToLower();
+ }
+
+ internal override void End()
+ {
+ _writer.WriteEndElement();
+ _writer.Dispose();
+ _writer = null;
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
index 2117a90d60bf76..035e1a74c9688e 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
@@ -177,6 +177,9 @@
TypeSystem\CodeGen\NativeStructType.CodeGen.cs
+
+ TypeSystem\MetadataEmitter\TypeSystemMetadataEmitter.cs
+
Common\GCDescEncoder.cs
@@ -422,6 +425,7 @@
+
@@ -592,6 +596,7 @@
+
diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs
index 8e00759aaa857c..164957c23b32a7 100644
--- a/src/coreclr/tools/aot/ILCompiler/Program.cs
+++ b/src/coreclr/tools/aot/ILCompiler/Program.cs
@@ -53,6 +53,7 @@ internal class Program
private bool _noPreinitStatics;
private bool _emitStackTraceData;
private string _mapFileName;
+ private string _mstatFileName;
private string _metadataLogFileName;
private bool _noMetadataBlocking;
private string _reflectionData;
@@ -202,6 +203,7 @@ private ArgumentSyntax ParseCommandLine(string[] args)
syntax.DefineOptionList("codegenopt", ref _codegenOptions, "Define a codegen option");
syntax.DefineOptionList("rdxml", ref _rdXmlFilePaths, "RD.XML file(s) for compilation");
syntax.DefineOption("map", ref _mapFileName, "Generate a map file");
+ syntax.DefineOption("mstat", ref _mstatFileName, "Generate an mstat file");
syntax.DefineOption("metadatalog", ref _metadataLogFileName, "Generate a metadata log file");
syntax.DefineOption("nometadatablocking", ref _noMetadataBlocking, "Ignore metadata blocking for internal implementation details");
syntax.DefineOption("completetypemetadata", ref _completeTypesMetadata, "Generate complete metadata for types");
@@ -925,9 +927,15 @@ void RunScanner()
ICompilation compilation = builder.ToCompilation();
- ObjectDumper dumper = _mapFileName != null ? new ObjectDumper(_mapFileName) : null;
+ List dumpers = new List();
- CompilationResults compilationResults = compilation.Compile(_outputFilePath, dumper);
+ if (_mapFileName != null)
+ dumpers.Add(new XmlObjectDumper(_mapFileName));
+
+ if (_mstatFileName != null)
+ dumpers.Add(new MstatObjectDumper(_mstatFileName, typeSystemContext));
+
+ CompilationResults compilationResults = compilation.Compile(_outputFilePath, ObjectDumper.Compose(dumpers));
if (_exportsFile != null)
{
ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, _exportsFile);