Skip to content

Commit 00f34fb

Browse files
Add generation of structured object file dumps (#73913)
The compiler can currently emit an XML file with everything that was emitted into the output object file. This works and it's easily human readable. But the captured information is not very structured and cannot be used to generate aggregate information like 'this assembly contributed X bytes". This adds another dumper format similar to what we do for MIBC and others: it's ECMA-335 based and uses ldtoken/ldstr/ldc to encode the data. This can be used to build better tools. There's currently none but ILDASM, but it would be good to have it for 7.0. Good hackathon project.
1 parent 06339dc commit 00f34fb

File tree

7 files changed

+253
-60
lines changed

7 files changed

+253
-60
lines changed

src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ The .NET Foundation licenses this file to you under the MIT license.
213213
<IlcArg Condition="$(NativeDebugSymbols) == 'true'" Include="-g" />
214214
<IlcArg Condition="$(IlcDwarfVersion) == '5'" Include="--gdwarf-5" />
215215
<IlcArg Condition="$(IlcGenerateMapFile) == 'true'" Include="--map:$(NativeIntermediateOutputPath)%(ManagedBinary.Filename).map.xml" />
216+
<IlcArg Condition="$(IlcGenerateMstatFile) == 'true'" Include="--mstat:$(NativeIntermediateOutputPath)%(ManagedBinary.Filename).mstat" />
216217
<IlcArg Condition="$(IlcGenerateDgmlFile) == 'true'" Include="--dgmllog:$(NativeIntermediateOutputPath)%(ManagedBinary.Filename).codegen.dgml.xml" />
217218
<IlcArg Condition="$(IlcGenerateDgmlFile) == 'true'" Include="--scandgmllog:$(NativeIntermediateOutputPath)%(ManagedBinary.Filename).scan.dgml.xml" />
218219
<IlcArg Include="@(RdXmlFile->'--rdxml:%(FullPath)')" />

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/MethodExceptionHandlingInfoNode.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public class MethodExceptionHandlingInfoNode : ObjectNode, ISymbolDefinitionNode
1414
private readonly MethodDesc _owningMethod;
1515
private readonly ObjectData _data;
1616

17+
public MethodDesc Method => _owningMethod;
18+
1719
public MethodExceptionHandlingInfoNode(MethodDesc owningMethod, ObjectData data)
1820
{
1921
_owningMethod = owningMethod;
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Reflection.Metadata;
7+
using System.Reflection.Metadata.Ecma335;
8+
9+
using Internal.Text;
10+
using Internal.TypeSystem;
11+
12+
using ILCompiler.DependencyAnalysis;
13+
14+
using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
15+
using AssemblyName = System.Reflection.AssemblyName;
16+
using System.Collections.Generic;
17+
using static ILCompiler.DependencyAnalysis.ObjectNode;
18+
19+
namespace ILCompiler
20+
{
21+
public class MstatObjectDumper : ObjectDumper
22+
{
23+
private const int VersionMajor = 1;
24+
private const int VersionMinor = 0;
25+
26+
private readonly string _fileName;
27+
private readonly TypeSystemMetadataEmitter _emitter;
28+
29+
private readonly InstructionEncoder _types = new InstructionEncoder(new BlobBuilder());
30+
31+
private Dictionary<MethodDesc, (string MangledName, int Size, int GcInfoSize)> _methods = new();
32+
private Dictionary<MethodDesc, int> _methodEhInfo = new();
33+
private Dictionary<string, int> _blobs = new();
34+
35+
private Utf8StringBuilder _utf8StringBuilder = new Utf8StringBuilder();
36+
37+
public MstatObjectDumper(string fileName, TypeSystemContext context)
38+
{
39+
_fileName = fileName;
40+
var asmName = new AssemblyName(Path.GetFileName(fileName));
41+
asmName.Version = new Version(VersionMajor, VersionMinor);
42+
_emitter = new TypeSystemMetadataEmitter(asmName, context);
43+
_emitter.AllowUseOfAddGlobalMethod();
44+
}
45+
46+
internal override void Begin()
47+
{
48+
}
49+
50+
protected override void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData)
51+
{
52+
string mangledName = null;
53+
if (node is ISymbolNode symbol)
54+
{
55+
_utf8StringBuilder.Clear();
56+
symbol.AppendMangledName(mangler, _utf8StringBuilder);
57+
mangledName = _utf8StringBuilder.ToString();
58+
}
59+
60+
switch (node)
61+
{
62+
case EETypeNode eeType:
63+
SerializeSimpleEntry(_types, eeType.Type, mangledName, objectData);
64+
break;
65+
case IMethodBodyNode methodBody:
66+
var codeInfo = (INodeWithCodeInfo)node;
67+
_methods.Add(methodBody.Method, (mangledName, objectData.Data.Length, codeInfo.GCInfo.Length));
68+
break;
69+
case MethodExceptionHandlingInfoNode ehInfoNode:
70+
_methodEhInfo.Add(ehInfoNode.Method, objectData.Data.Length);
71+
break;
72+
default:
73+
string nodeName = GetObjectNodeName(node);
74+
if (!_blobs.TryGetValue(nodeName, out int size))
75+
size = 0;
76+
_blobs[nodeName] = size + objectData.Data.Length;
77+
break;
78+
}
79+
}
80+
81+
private void SerializeSimpleEntry(InstructionEncoder encoder, TypeSystemEntity entity, string mangledName, ObjectData blob)
82+
{
83+
encoder.OpCode(ILOpCode.Ldtoken);
84+
encoder.Token(_emitter.EmitMetadataHandleForTypeSystemEntity(entity));
85+
encoder.LoadString(_emitter.GetUserStringHandle(mangledName));
86+
encoder.LoadConstantI4(blob.Data.Length);
87+
}
88+
89+
internal override void End()
90+
{
91+
var methods = new InstructionEncoder(new BlobBuilder());
92+
foreach (var m in _methods)
93+
{
94+
methods.OpCode(ILOpCode.Ldtoken);
95+
methods.Token(_emitter.EmitMetadataHandleForTypeSystemEntity(m.Key));
96+
methods.LoadString(_emitter.GetUserStringHandle(m.Value.MangledName));
97+
methods.LoadConstantI4(m.Value.Size);
98+
methods.LoadConstantI4(m.Value.GcInfoSize);
99+
methods.LoadConstantI4(_methodEhInfo.GetValueOrDefault(m.Key));
100+
}
101+
102+
var blobs = new InstructionEncoder(new BlobBuilder());
103+
foreach (var b in _blobs)
104+
{
105+
blobs.LoadString(_emitter.GetUserStringHandle(b.Key));
106+
blobs.LoadConstantI4(b.Value);
107+
}
108+
109+
_emitter.AddGlobalMethod("Methods", methods, 0);
110+
_emitter.AddGlobalMethod("Types", _types, 0);
111+
_emitter.AddGlobalMethod("Blobs", blobs, 0);
112+
113+
using (var fs = File.OpenWrite(_fileName))
114+
_emitter.SerializeToStream(fs);
115+
}
116+
}
117+
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ObjectDumper.cs

Lines changed: 35 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5-
using System.IO;
6-
using System.Security.Cryptography;
7-
using System.Xml;
5+
using System.Collections.Generic;
86

97
using Internal.Text;
108

@@ -14,31 +12,14 @@
1412

1513
namespace ILCompiler
1614
{
17-
public class ObjectDumper : IObjectDumper
15+
public abstract class ObjectDumper : IObjectDumper
1816
{
19-
private readonly string _fileName;
20-
private SHA256 _sha256;
21-
private XmlWriter _writer;
17+
internal abstract void Begin();
18+
internal abstract void End();
19+
void IObjectDumper.DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData) => DumpObjectNode(mangler, node, objectData);
20+
protected abstract void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData);
2221

23-
public ObjectDumper(string fileName)
24-
{
25-
_fileName = fileName;
26-
}
27-
28-
internal void Begin()
29-
{
30-
var settings = new XmlWriterSettings
31-
{
32-
CloseOutput = true,
33-
Indent = true,
34-
};
35-
36-
_sha256 = SHA256.Create();
37-
_writer = XmlWriter.Create(File.CreateText(_fileName), settings);
38-
_writer.WriteStartElement("ObjectNodes");
39-
}
40-
41-
private static string GetObjectNodeName(ObjectNode node)
22+
protected static string GetObjectNodeName(ObjectNode node)
4223
{
4324
string name = node.GetType().Name;
4425

@@ -54,46 +35,42 @@ private static string GetObjectNodeName(ObjectNode node)
5435
return name;
5536
}
5637

57-
void IObjectDumper.DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData)
38+
public static ObjectDumper Compose(IEnumerable<ObjectDumper> dumpers)
5839
{
59-
string name = null;
60-
61-
_writer.WriteStartElement(GetObjectNodeName(node));
62-
63-
var symbolNode = node as ISymbolNode;
64-
if (symbolNode != null)
65-
{
66-
Utf8StringBuilder sb = new Utf8StringBuilder();
67-
symbolNode.AppendMangledName(mangler, sb);
68-
name = sb.ToString();
69-
_writer.WriteAttributeString("Name", name);
70-
}
40+
var dumpersList = new ArrayBuilder<ObjectDumper>();
7141

72-
_writer.WriteAttributeString("Length", objectData.Data.Length.ToStringInvariant());
73-
_writer.WriteAttributeString("Hash", HashData(objectData.Data));
74-
_writer.WriteEndElement();
42+
foreach (var dumper in dumpers)
43+
dumpersList.Add(dumper);
7544

76-
var nodeWithCodeInfo = node as INodeWithCodeInfo;
77-
if (nodeWithCodeInfo != null)
45+
return dumpersList.Count switch
7846
{
79-
_writer.WriteStartElement("GCInfo");
80-
_writer.WriteAttributeString("Name", name);
81-
_writer.WriteAttributeString("Length", nodeWithCodeInfo.GCInfo.Length.ToStringInvariant());
82-
_writer.WriteAttributeString("Hash", HashData(nodeWithCodeInfo.GCInfo));
83-
_writer.WriteEndElement();
84-
}
47+
0 => null,
48+
1 => dumpersList[0],
49+
_ => new ComposedObjectDumper(dumpersList.ToArray()),
50+
};
8551
}
8652

87-
private string HashData(byte[] data)
53+
private class ComposedObjectDumper : ObjectDumper
8854
{
89-
return BitConverter.ToString(_sha256.ComputeHash(data)).Replace("-", "").ToLower();
90-
}
55+
private readonly ObjectDumper[] _dumpers;
9156

92-
internal void End()
93-
{
94-
_writer.WriteEndElement();
95-
_writer.Dispose();
96-
_writer = null;
57+
public ComposedObjectDumper(ObjectDumper[] dumpers) => _dumpers = dumpers;
58+
59+
protected override void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData)
60+
{
61+
foreach (var d in _dumpers)
62+
d.DumpObjectNode(mangler, node, objectData);
63+
}
64+
internal override void Begin()
65+
{
66+
foreach (var d in _dumpers)
67+
d.Begin();
68+
}
69+
internal override void End()
70+
{
71+
foreach (var d in _dumpers)
72+
d.End();
73+
}
9774
}
9875
}
9976
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.IO;
6+
using System.Security.Cryptography;
7+
using System.Xml;
8+
9+
using Internal.Text;
10+
11+
using ILCompiler.DependencyAnalysis;
12+
13+
using ObjectData = ILCompiler.DependencyAnalysis.ObjectNode.ObjectData;
14+
15+
namespace ILCompiler
16+
{
17+
public class XmlObjectDumper : ObjectDumper
18+
{
19+
private readonly string _fileName;
20+
private SHA256 _sha256;
21+
private XmlWriter _writer;
22+
23+
public XmlObjectDumper(string fileName)
24+
{
25+
_fileName = fileName;
26+
}
27+
28+
internal override void Begin()
29+
{
30+
var settings = new XmlWriterSettings
31+
{
32+
CloseOutput = true,
33+
Indent = true,
34+
};
35+
36+
_sha256 = SHA256.Create();
37+
_writer = XmlWriter.Create(File.CreateText(_fileName), settings);
38+
_writer.WriteStartElement("ObjectNodes");
39+
}
40+
41+
protected override void DumpObjectNode(NameMangler mangler, ObjectNode node, ObjectData objectData)
42+
{
43+
string name = null;
44+
45+
_writer.WriteStartElement(GetObjectNodeName(node));
46+
47+
var symbolNode = node as ISymbolNode;
48+
if (symbolNode != null)
49+
{
50+
Utf8StringBuilder sb = new Utf8StringBuilder();
51+
symbolNode.AppendMangledName(mangler, sb);
52+
name = sb.ToString();
53+
_writer.WriteAttributeString("Name", name);
54+
}
55+
56+
_writer.WriteAttributeString("Length", objectData.Data.Length.ToStringInvariant());
57+
_writer.WriteAttributeString("Hash", HashData(objectData.Data));
58+
_writer.WriteEndElement();
59+
60+
var nodeWithCodeInfo = node as INodeWithCodeInfo;
61+
if (nodeWithCodeInfo != null)
62+
{
63+
_writer.WriteStartElement("GCInfo");
64+
_writer.WriteAttributeString("Name", name);
65+
_writer.WriteAttributeString("Length", nodeWithCodeInfo.GCInfo.Length.ToStringInvariant());
66+
_writer.WriteAttributeString("Hash", HashData(nodeWithCodeInfo.GCInfo));
67+
_writer.WriteEndElement();
68+
}
69+
}
70+
71+
private string HashData(byte[] data)
72+
{
73+
return BitConverter.ToString(_sha256.ComputeHash(data)).Replace("-", "").ToLower();
74+
}
75+
76+
internal override void End()
77+
{
78+
_writer.WriteEndElement();
79+
_writer.Dispose();
80+
_writer = null;
81+
}
82+
}
83+
}

src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@
177177
<Compile Include="..\..\Common\TypeSystem\CodeGen\NativeStructType.CodeGen.cs">
178178
<Link>TypeSystem\CodeGen\NativeStructType.CodeGen.cs</Link>
179179
</Compile>
180+
<Compile Include="..\..\Common\TypeSystem\MetadataEmitter\TypeSystemMetadataEmitter.cs">
181+
<Link>TypeSystem\MetadataEmitter\TypeSystemMetadataEmitter.cs</Link>
182+
</Compile>
180183
<Compile Include="..\..\Common\Internal\Runtime\GCDescEncoder.cs">
181184
<Link>Common\GCDescEncoder.cs</Link>
182185
</Compile>
@@ -423,6 +426,7 @@
423426
<Compile Include="Compiler\IInliningPolicy.cs" />
424427
<Compile Include="Compiler\ManifestResourceBlockingPolicy.cs" />
425428
<Compile Include="Compiler\MethodImportationErrorProvider.cs" />
429+
<Compile Include="Compiler\MstatObjectDumper.cs" />
426430
<Compile Include="Compiler\NoMetadataBlockingPolicy.cs" />
427431
<Compile Include="Compiler\DependencyAnalysis\FrozenObjectNode.cs" />
428432
<Compile Include="Compiler\DependencyAnalysis\GCStaticsPreInitDataNode.cs" />
@@ -593,6 +597,7 @@
593597
<Compile Include="Compiler\Logging\DocumentationSignatureParser.cs" />
594598
<Compile Include="Compiler\Logging\MessageContainer.cs" />
595599
<Compile Include="Compiler\Logging\MessageOrigin.cs" />
600+
<Compile Include="Compiler\XmlObjectDumper.cs" />
596601
<Compile Include="IL\ILImporter.Scanner.cs" />
597602
<Compile Include="IL\Stubs\PInvokeILProvider.cs" />
598603
<Compile Include="IL\Stubs\StartupCode\AppContextInitializerMethod.cs" />

0 commit comments

Comments
 (0)