未验证 提交 311bd7ea 编写于 作者: D David Wrighton 提交者: GitHub

Profile driven method layout (#37469)

* Profile driven layout
- Update to mpgo file format to hold weighted call graph data, and exclusive hit sample data
- Implementation of call graph data capture from both jitted and R2R code
- Add mechanism for parsing entrypoints from R2R files into arbitrary type systems insetad of just strings
- Update command line parser in dotnet-pgo to use named properties on class instead of arguments to main method
- Update dotnet-pgo to be able to use a etl.zip file
- Implement infrastructure for performing layout of PE file based on profile guided information
- Implement various profile guided method ordering routines
  - There are a variety of possible algorithms, these are simple to
  implement. More capable ones will generally use the call graph data.
  - Sort by exclusive weight
  - Sort by Hot (known to be used) and cold (not known to be used)
  - Sort by Hot (known to be used for more than 128 sample) Warm (known
  to be used) and Cold (not known to be used)
- Since the BulkType logging lock seems to be losing some types, skip the lock for MethodDetails data
上级 128adb4c
......@@ -12,7 +12,7 @@
namespace ILCompiler.DependencyAnalysis
{
public abstract class SortableDependencyNode : DependencyNodeCore<NodeFactory>, ISortableNode
public abstract partial class SortableDependencyNode : DependencyNodeCore<NodeFactory>, ISortableNode
{
#if !SUPPORT_JIT
/// <summary>
......@@ -154,6 +154,8 @@ public int Compare(DependencyNodeCore<NodeFactory> x1, DependencyNodeCore<NodeFa
}
}
static partial void ApplyCustomSort(SortableDependencyNode x, SortableDependencyNode y, ref int result);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int CompareImpl(SortableDependencyNode x, SortableDependencyNode y, CompilerComparer comparer)
{
......@@ -162,6 +164,11 @@ public static int CompareImpl(SortableDependencyNode x, SortableDependencyNode y
if (phaseX == phaseY)
{
int customSort = 0;
ApplyCustomSort(x, y, ref customSort);
if (customSort != 0)
return customSort;
int codeX = x.ClassCode;
int codeY = y.ClassCode;
if (codeX == codeY)
......
......@@ -114,6 +114,22 @@ public int Length
}
}
public bool HasEmbeddedSignatureData
{
get
{
return _embeddedSignatureData != null;
}
}
public EmbeddedSignatureData[] GetEmbeddedSignatureData()
{
if ((_embeddedSignatureData == null) || (_embeddedSignatureData.Length == 0))
return null;
return (EmbeddedSignatureData[])_embeddedSignatureData.Clone();
}
public bool Equals(MethodSignature otherSignature)
{
return Equals(otherSignature, allowCovariantReturn: false);
......
......@@ -45,6 +45,8 @@ public void SetCode(ObjectData data)
public List<ISymbolNode> Fixups => _fixups;
public int Size => _methodCode.Data.Length;
public bool IsEmpty => _methodCode.Data.Length == 0;
public override ObjectData GetData(NodeFactory factory, bool relocsOnly)
......@@ -293,6 +295,7 @@ public void InitializeDebugEHClauseInfos(DebugEHClauseInfo[] debugEHClauseInfos)
Debug.Assert(_debugEHClauseInfos == null);
_debugEHClauseInfos = debugEHClauseInfos;
}
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
{
MethodWithGCInfo otherNode = (MethodWithGCInfo)other;
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using ILCompiler.DependencyAnalysisFramework;
using Internal.TypeSystem;
namespace ILCompiler.DependencyAnalysis
{
partial class SortableDependencyNode
{
// Custom sort order. Used to override the default sorting mechanics.
public int CustomSort = int.MaxValue;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static partial void ApplyCustomSort(SortableDependencyNode x, SortableDependencyNode y, ref int result)
{
result = x.CustomSort.CompareTo(y.CustomSort);
}
}
}
......@@ -36,15 +36,20 @@ public enum MethodProfilingDataFlags
public class MethodProfileData
{
public MethodProfileData(MethodDesc method, MethodProfilingDataFlags flags, uint scenarioMask)
public MethodProfileData(MethodDesc method, MethodProfilingDataFlags flags, double exclusiveWeight, Dictionary<MethodDesc, int> callWeights, uint scenarioMask)
{
Method = method;
Flags = flags;
ScenarioMask = scenarioMask;
ExclusiveWeight = exclusiveWeight;
CallWeights = callWeights;
}
public readonly MethodDesc Method;
public readonly MethodProfilingDataFlags Flags;
public readonly uint ScenarioMask;
public readonly double ExclusiveWeight;
public readonly Dictionary<MethodDesc, int> CallWeights;
}
public abstract class ProfileData
......@@ -179,7 +184,27 @@ private void MergeProfileData(ref bool partialNgen, Dictionary<MethodDesc, Metho
MethodProfileData dataToMerge;
if (mergedProfileData.TryGetValue(data.Method, out dataToMerge))
{
mergedProfileData[data.Method] = new MethodProfileData(data.Method, dataToMerge.Flags | data.Flags, dataToMerge.ScenarioMask | data.ScenarioMask);
var mergedCallWeights = data.CallWeights;
if (mergedCallWeights == null)
{
mergedCallWeights = dataToMerge.CallWeights;
}
else if (dataToMerge.CallWeights != null)
{
mergedCallWeights = new Dictionary<MethodDesc, int>(data.CallWeights);
foreach (var entry in dataToMerge.CallWeights)
{
if (mergedCallWeights.TryGetValue(entry.Key, out var initialWeight))
{
mergedCallWeights[entry.Key] = initialWeight + entry.Value;
}
else
{
mergedCallWeights[entry.Key] = entry.Value;
}
}
}
mergedProfileData[data.Method] = new MethodProfileData(data.Method, dataToMerge.Flags | data.Flags, data.ExclusiveWeight + dataToMerge.ExclusiveWeight, mergedCallWeights, dataToMerge.ScenarioMask | data.ScenarioMask);
}
else
{
......@@ -214,5 +239,14 @@ public bool IsMethodInProfileData(MethodDesc method)
{
return _placedProfileMethodsAll.Contains(method);
}
public MethodProfileData this[MethodDesc method]
{
get
{
_mergedProfileData.TryGetValue(method, out var profileData);
return profileData;
}
}
}
}
......@@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
......@@ -20,7 +19,6 @@
using ILCompiler.DependencyAnalysis.ReadyToRun;
using ILCompiler.DependencyAnalysisFramework;
using Internal.TypeSystem.Ecma;
using System.Linq;
namespace ILCompiler
{
......@@ -230,6 +228,9 @@ public sealed class ReadyToRunCodegenCompilation : Compilation
private bool _generateMapFile;
private ProfileDataManager _profileData;
private ReadyToRunFileLayoutOptimizer _fileLayoutOptimizer;
public ReadyToRunSymbolNodeFactory SymbolNodeFactory { get; }
public ReadyToRunCompilationModuleGroupBase CompilationModuleGroup { get; }
......@@ -244,7 +245,10 @@ public sealed class ReadyToRunCodegenCompilation : Compilation
InstructionSetSupport instructionSetSupport,
bool resilient,
bool generateMapFile,
int parallelism)
int parallelism,
ProfileDataManager profileData,
ReadyToRunMethodLayoutAlgorithm methodLayoutAlgorithm,
ReadyToRunFileLayoutAlgorithm fileLayoutAlgorithm)
: base(
dependencyGraph,
nodeFactory,
......@@ -268,13 +272,21 @@ public sealed class ReadyToRunCodegenCompilation : Compilation
string instructionSetSupportString = ReadyToRunInstructionSetSupportSignature.ToInstructionSetSupportString(instructionSetSupport);
ReadyToRunInstructionSetSupportSignature instructionSetSupportSig = new ReadyToRunInstructionSetSupportSignature(instructionSetSupportString);
_dependencyGraph.AddRoot(new Import(NodeFactory.EagerImports, instructionSetSupportSig), "Baseline instruction set support");
_profileData = profileData;
_fileLayoutOptimizer = new ReadyToRunFileLayoutOptimizer(methodLayoutAlgorithm, fileLayoutAlgorithm, profileData, _nodeFactory);
}
public override void Compile(string outputFile)
{
_dependencyGraph.ComputeMarkedNodes();
var nodes = _dependencyGraph.MarkedNodeList;
nodes = _fileLayoutOptimizer.ApplyProfilerGuidedMethodSort(nodes);
using (PerfEventSource.StartStopEvents.EmittingEvents())
{
NodeFactory.SetMarkingComplete();
......
......@@ -26,6 +26,9 @@ public sealed class ReadyToRunCodegenCompilationBuilder : CompilationBuilder
private bool _generateMapFile;
private int _parallelism;
private InstructionSetSupport _instructionSetSupport;
private ProfileDataManager _profileData;
private ReadyToRunMethodLayoutAlgorithm _r2rMethodLayoutAlgorithm;
private ReadyToRunFileLayoutAlgorithm _r2rFileLayoutAlgorithm;
private string _jitPath;
private string _outputFile;
......@@ -98,6 +101,19 @@ public ReadyToRunCodegenCompilationBuilder UseResilience(bool resilient)
return this;
}
public ReadyToRunCodegenCompilationBuilder UseProfileData(ProfileDataManager profileData)
{
_profileData = profileData;
return this;
}
public ReadyToRunCodegenCompilationBuilder FileLayoutAlgorithms(ReadyToRunMethodLayoutAlgorithm r2rMethodLayoutAlgorithm, ReadyToRunFileLayoutAlgorithm r2rFileLayoutAlgorithm)
{
_r2rMethodLayoutAlgorithm = r2rMethodLayoutAlgorithm;
_r2rFileLayoutAlgorithm = r2rFileLayoutAlgorithm;
return this;
}
public ReadyToRunCodegenCompilationBuilder UseMapFile(bool generateMapFile)
{
_generateMapFile = generateMapFile;
......@@ -205,7 +221,10 @@ public override ICompilation ToCompilation()
_instructionSetSupport,
_resilient,
_generateMapFile,
_parallelism);
_parallelism,
_profileData,
_r2rMethodLayoutAlgorithm,
_r2rFileLayoutAlgorithm);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Internal.TypeSystem;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysis.ReadyToRun;
using ILCompiler.DependencyAnalysisFramework;
using System.Linq;
using System.Collections.Immutable;
using System.Text;
using System.Reflection.Metadata.Ecma335;
namespace ILCompiler
{
public enum ReadyToRunMethodLayoutAlgorithm
{
DefaultSort,
ExclusiveWeight,
HotCold,
HotWarmCold
}
public enum ReadyToRunFileLayoutAlgorithm
{
DefaultSort,
MethodOrder
}
class ReadyToRunFileLayoutOptimizer
{
public ReadyToRunFileLayoutOptimizer (ReadyToRunMethodLayoutAlgorithm methodAlgorithm,
ReadyToRunFileLayoutAlgorithm fileAlgorithm,
ProfileDataManager profileData,
NodeFactory nodeFactory)
{
_methodLayoutAlgorithm = methodAlgorithm;
_fileLayoutAlgorithm = fileAlgorithm;
_profileData = profileData;
_nodeFactory = nodeFactory;
}
private ReadyToRunMethodLayoutAlgorithm _methodLayoutAlgorithm = ReadyToRunMethodLayoutAlgorithm.DefaultSort;
private ReadyToRunFileLayoutAlgorithm _fileLayoutAlgorithm = ReadyToRunFileLayoutAlgorithm.DefaultSort;
private ProfileDataManager _profileData;
private NodeFactory _nodeFactory;
public ImmutableArray<DependencyNodeCore<NodeFactory>> ApplyProfilerGuidedMethodSort(ImmutableArray<DependencyNodeCore<NodeFactory>> nodes)
{
if (_methodLayoutAlgorithm == ReadyToRunMethodLayoutAlgorithm.DefaultSort)
return nodes;
List<MethodWithGCInfo> methods = new List<MethodWithGCInfo>();
foreach (var node in nodes)
{
if (node is MethodWithGCInfo method)
{
methods.Add(method);
}
}
if (_methodLayoutAlgorithm == ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight)
{
methods.MergeSortAllowDuplicates(sortMethodWithGCInfoByWeight);
int sortMethodWithGCInfoByWeight(MethodWithGCInfo left, MethodWithGCInfo right)
{
return -MethodWithGCInfoToWeight(left).CompareTo(MethodWithGCInfoToWeight(right));
}
}
else if (_methodLayoutAlgorithm == ReadyToRunMethodLayoutAlgorithm.HotCold)
{
methods.MergeSortAllowDuplicates((MethodWithGCInfo left, MethodWithGCInfo right) => ComputeHotColdRegion(left).CompareTo(ComputeHotColdRegion(right)));
int ComputeHotColdRegion(MethodWithGCInfo method)
{
return MethodWithGCInfoToWeight(method) > 0 ? 0 : 1;
}
}
else if (_methodLayoutAlgorithm == ReadyToRunMethodLayoutAlgorithm.HotWarmCold)
{
methods.MergeSortAllowDuplicates((MethodWithGCInfo left, MethodWithGCInfo right) => ComputeHotWarmColdRegion(left).CompareTo(ComputeHotWarmColdRegion(right)));
int ComputeHotWarmColdRegion(MethodWithGCInfo method)
{
double weight = MethodWithGCInfoToWeight(method);
// If weight is greater than 128 its probably signicantly used at runtime
if (weight > 128)
return 0;
// If weight is less than 128 but greater than 0, then its probably used at startup
// or some at runtime, but is less critical than the hot code
if (weight > 0)
return 1;
// Methods without weight are probably relatively rarely used
return 2;
};
}
int sortOrder = 0;
List<MethodWithGCInfo> sortedMethodsList = methods;
foreach (var methodNode in sortedMethodsList)
{
methodNode.CustomSort = sortOrder;
sortOrder++;
}
if (_fileLayoutAlgorithm == ReadyToRunFileLayoutAlgorithm.MethodOrder)
{
// Sort the dependencies of methods by the method order
foreach (var method in sortedMethodsList)
{
ApplySortToDependencies(method, 0);
}
}
var newNodesArray = nodes.ToArray();
newNodesArray.MergeSortAllowDuplicates(new SortableDependencyNode.ObjectNodeComparer(new CompilerComparer()));
return newNodesArray.ToImmutableArray();
double MethodWithGCInfoToWeight(MethodWithGCInfo method)
{
var profileData = _profileData[method.Method];
double weight = 0;
if (profileData != null)
{
weight = profileData.ExclusiveWeight;
}
return weight;
}
void ApplySortToDependencies(DependencyNodeCore<NodeFactory> node, int depth)
{
if (depth > 5)
return;
if (node is SortableDependencyNode sortableNode)
{
if (sortableNode.CustomSort != Int32.MaxValue)
return; // Node already sorted
sortableNode.CustomSort += sortOrder++;
}
foreach (var dependency in node.GetStaticDependencies(_nodeFactory))
{
ApplySortToDependencies(dependency.Node, depth + 1);
}
}
}
}
}
......@@ -141,7 +141,7 @@ public ProfileData ParseIBCDataFromModule(EcmaModule ecmaModule)
{
if (methodsFoundInData.Add(associatedMethod))
{
methodProfileData.Add(new MethodProfileData(associatedMethod, (MethodProfilingDataFlags)entry.Flags, scenarioMask));
methodProfileData.Add(new MethodProfileData(associatedMethod, (MethodProfilingDataFlags)entry.Flags, 0, null, scenarioMask));
}
else
{
......
......@@ -13,6 +13,7 @@
using System.Linq;
using System.IO;
using System.Diagnostics;
namespace ILCompiler.IBC
{
......@@ -126,16 +127,38 @@ public static ProfileData ParseMIbcFile(CompilerTypeSystemContext tsc, string fi
}
}
enum MibcGroupParseState
{
LookingForNextMethod,
LookingForOptionalData,
ProcessingExclusiveWeight,
ProcessingCallgraphCount,
ProcessingCallgraphToken,
ProcessingCallgraphWeight,
}
/// <summary>
/// Parse MIbcGroup method and return enumerable of MethodProfileData
///
/// Like the AssemblyDictionary method, data is encoded via IL instructions. The format is
///
/// ldtoken methodInProfileData
/// Any series of instructions that does not include pop
/// Any series of instructions that does not include pop. Expansion data is encoded via ldstr "id"
/// followed by a expansion specific sequence of il opcodes.
/// pop
/// {Repeat N times for N methods described}
///
/// Extensions supported with current parser:
///
/// ldstr "ExclusiveWeight"
/// Any ldc.i4 or ldc.r4 or ldc.r8 instruction to indicate the exclusive weight
///
/// ldstr "WeightedCallData"
/// ldc.i4 <Count of methods called>
/// Repeat <Count of methods called times>
/// ldtoken <Method called from this method>
/// ldc.i4 <Weight associated with calling the <Method called from this method>>
///
/// This format is designed to be extensible to hold more data as we add new per method profile data without breaking existing parsers.
/// </summary>
static IEnumerable<MethodProfileData> ReadMIbcGroup(TypeSystemContext tsc, EcmaMethod method)
......@@ -143,19 +166,28 @@ static IEnumerable<MethodProfileData> ReadMIbcGroup(TypeSystemContext tsc, EcmaM
EcmaMethodIL ilBody = EcmaMethodIL.Create(method);
byte[] ilBytes = ilBody.GetILBytes();
int currentOffset = 0;
object methodInProgress = null;
object metadataNotResolvable = new object();
object metadataObject = null;
object methodNotResolvable = new object();
MibcGroupParseState state = MibcGroupParseState.LookingForNextMethod;
int intValue = 0;
int weightedCallGraphSize = 0;
int profileEntryFound = 0;
double exclusiveWeight = 0;
Dictionary<MethodDesc, int> weights = null;
bool processIntValue = false;
while (currentOffset < ilBytes.Length)
{
ILOpcode opcode = (ILOpcode)ilBytes[currentOffset];
if (opcode == ILOpcode.prefix1)
opcode = 0x100 + (ILOpcode)ilBytes[currentOffset + 1];
processIntValue = false;
switch (opcode)
{
case ILOpcode.ldtoken:
if (metadataObject == null)
{
uint token = (uint)(ilBytes[currentOffset + 1] + (ilBytes[currentOffset + 2] << 8) + (ilBytes[currentOffset + 3] << 16) + (ilBytes[currentOffset + 4] << 24));
uint token = BitConverter.ToUInt32(ilBytes.AsSpan(currentOffset + 1, 4));
metadataObject = null;
try
{
metadataObject = ilBody.GetObject((int)token);
......@@ -163,21 +195,188 @@ static IEnumerable<MethodProfileData> ReadMIbcGroup(TypeSystemContext tsc, EcmaM
catch (TypeSystemException)
{
// The method being referred to may be missing. In that situation,
// use the methodNotResolvable sentinel to indicate that this record should be ignored
metadataObject = methodNotResolvable;
// use the metadataNotResolvable sentinel to indicate that this record should be ignored
metadataObject = metadataNotResolvable;
}
switch (state)
{
case MibcGroupParseState.ProcessingCallgraphToken:
state = MibcGroupParseState.ProcessingCallgraphWeight;
break;
case MibcGroupParseState.LookingForNextMethod:
methodInProgress = metadataObject;
state = MibcGroupParseState.LookingForOptionalData;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
}
break;
case ILOpcode.ldc_r4:
{
float fltValue = BitConverter.ToSingle(ilBytes.AsSpan(currentOffset + 1, 4));
switch (state)
{
case MibcGroupParseState.ProcessingExclusiveWeight:
exclusiveWeight = fltValue;
state = MibcGroupParseState.LookingForOptionalData;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
break;
}
case ILOpcode.ldc_r8:
{
double dblValue = BitConverter.ToDouble(ilBytes.AsSpan(currentOffset + 1, 8));
switch (state)
{
case MibcGroupParseState.ProcessingExclusiveWeight:
exclusiveWeight = dblValue;
state = MibcGroupParseState.LookingForOptionalData;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
break;
}
case ILOpcode.ldc_i4_0:
intValue = 0;
processIntValue = true;
break;
case ILOpcode.ldc_i4_1:
intValue = 1;
processIntValue = true;
break;
case ILOpcode.ldc_i4_2:
intValue = 2;
processIntValue = true;
break;
case ILOpcode.ldc_i4_3:
intValue = 3;
processIntValue = true;
break;
case ILOpcode.ldc_i4_4:
intValue = 4;
processIntValue = true;
break;
case ILOpcode.ldc_i4_5:
intValue = 5;
processIntValue = true;
break;
case ILOpcode.ldc_i4_6:
intValue = 6;
processIntValue = true;
break;
case ILOpcode.ldc_i4_7:
intValue = 7;
processIntValue = true;
break;
case ILOpcode.ldc_i4_8:
intValue = 8;
processIntValue = true;
break;
case ILOpcode.ldc_i4_m1:
intValue = -1;
processIntValue = true;
break;
case ILOpcode.ldc_i4_s:
intValue = (sbyte)ilBytes[currentOffset + 1];
processIntValue = true;
break;
case ILOpcode.ldc_i4:
intValue = BitConverter.ToInt32(ilBytes.AsSpan(currentOffset + 1, 4));
processIntValue = true;
break;
case ILOpcode.ldstr:
{
UInt32 userStringToken = BitConverter.ToUInt32(ilBytes.AsSpan(currentOffset + 1, 4));
string optionalDataName = (string)ilBody.GetObject((int)userStringToken);
switch (optionalDataName)
{
case "ExclusiveWeight":
state = MibcGroupParseState.ProcessingExclusiveWeight;
break;
case "WeightedCallData":
state = MibcGroupParseState.ProcessingCallgraphCount;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
}
break;
case ILOpcode.pop:
if (metadataObject != methodNotResolvable)
if (methodInProgress != metadataNotResolvable)
{
MethodProfileData mibcData = new MethodProfileData((MethodDesc)metadataObject, MethodProfilingDataFlags.ReadMethodCode, 0xFFFFFFFF);
profileEntryFound++;
if (exclusiveWeight == 0)
{
// If no exclusive weight is found assign a non zero value that assumes the order in the pgo file is significant.
exclusiveWeight = Math.Min(1000000.0 - profileEntryFound, 0.0) / 1000000.0;
}
MethodProfileData mibcData = new MethodProfileData((MethodDesc)methodInProgress, MethodProfilingDataFlags.ReadMethodCode, exclusiveWeight, weights, 0xFFFFFFFF);
state = MibcGroupParseState.LookingForNextMethod;
exclusiveWeight = 0;
weights = null;
yield return mibcData;
}
metadataObject = null;
methodInProgress = null;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
if (processIntValue)
{
switch (state)
{
case MibcGroupParseState.ProcessingExclusiveWeight:
exclusiveWeight = intValue;
state = MibcGroupParseState.LookingForOptionalData;
break;
case MibcGroupParseState.ProcessingCallgraphCount:
weightedCallGraphSize = intValue;
weights = new Dictionary<MethodDesc, int>();
if (weightedCallGraphSize > 0)
state = MibcGroupParseState.ProcessingCallgraphToken;
else
state = MibcGroupParseState.LookingForOptionalData;
break;
case MibcGroupParseState.ProcessingCallgraphWeight:
if (metadataObject != metadataNotResolvable)
{
weights.Add((MethodDesc)metadataObject, intValue);
}
weightedCallGraphSize--;
if (weightedCallGraphSize > 0)
state = MibcGroupParseState.ProcessingCallgraphToken;
else
state = MibcGroupParseState.LookingForOptionalData;
break;
default:
state = MibcGroupParseState.LookingForOptionalData;
break;
}
}
// This isn't correct if there is a switch opcode, but since we won't do that, its ok
currentOffset += opcode.GetSize();
}
......
......@@ -169,6 +169,7 @@
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\TypesTableNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\Win32ResourcesNode.cs" />
<Compile Include="Compiler\DependencyAnalysis\ReadyToRunSymbolNodeFactory.cs" />
<Compile Include="Compiler\DependencyAnalysis\SortableDependencyNodeCompilerSpecific.cs" />
<Compile Include="Compiler\DependencyAnalysis\TypeAndMethod.cs" />
<Compile Include="Compiler\IRootingServiceProvider.cs" />
<Compile Include="Compiler\MethodExtensions.cs" />
......@@ -183,6 +184,7 @@
<Compile Include="Compiler\ReadyToRunCompilerContext.cs" />
<Compile Include="Compiler\ReadyToRunCodegenCompilation.cs" />
<Compile Include="Compiler\ReadyToRunCodegenCompilationBuilder.cs" />
<Compile Include="Compiler\ReadyToRunFileLayoutOptimizer.cs" />
<Compile Include="Compiler\ReadyToRunMetadataFieldLayoutAlgorithm.cs" />
<Compile Include="Compiler\ReadyToRunSingleAssemblyCompilationModuleGroup.cs" />
<Compile Include="Compiler\ReadyToRunTableManager.cs" />
......
......@@ -23,13 +23,69 @@ public DisassemblingGenericContext(string[] typeParameters, string[] methodParam
public string[] TypeParameters { get; }
}
// Test implementation of ISignatureTypeProvider<TType, TGenericContext> that uses strings in ilasm syntax as TType.
// A real provider in any sort of perf constraints would not want to allocate strings freely like this, but it keeps test code simple.
public class DisassemblingTypeProvider : ISignatureTypeProvider<string, DisassemblingGenericContext>
public abstract class StringTypeProviderBase<TGenericContext> : ISignatureTypeProvider<string, TGenericContext>
{
public virtual string GetPrimitiveType(PrimitiveTypeCode typeCode)
{
return typeCode.ToString();
switch (typeCode)
{
case PrimitiveTypeCode.Void:
return "void";
case PrimitiveTypeCode.Boolean:
return "bool";
case PrimitiveTypeCode.Char:
return "char";
case PrimitiveTypeCode.SByte:
return "sbyte";
case PrimitiveTypeCode.Byte:
return "byte";
case PrimitiveTypeCode.Int16:
return "short";
case PrimitiveTypeCode.UInt16:
return "ushort";
case PrimitiveTypeCode.Int32:
return "int";
case PrimitiveTypeCode.UInt32:
return "uint";
case PrimitiveTypeCode.Int64:
return "long";
case PrimitiveTypeCode.UInt64:
return "ulong";
case PrimitiveTypeCode.Single:
return "float";
case PrimitiveTypeCode.Double:
return "double";
case PrimitiveTypeCode.String:
return "string";
case PrimitiveTypeCode.TypedReference:
return "typedbyref";
case PrimitiveTypeCode.IntPtr:
return "IntPtr";
case PrimitiveTypeCode.UIntPtr:
return "UIntPtr";
case PrimitiveTypeCode.Object:
return "object";
default:
throw new NotImplementedException();
}
}
public virtual string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind = 0)
......@@ -42,11 +98,6 @@ public virtual string GetTypeFromReference(MetadataReader reader, TypeReferenceH
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetTypeFromSpecification(MetadataReader reader, DisassemblingGenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind = 0)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetSZArrayType(string elementType)
{
return elementType + "[]";
......@@ -62,24 +113,6 @@ public virtual string GetByReferenceType(string elementType)
return "ref " + elementType;
}
public virtual string GetGenericMethodParameter(DisassemblingGenericContext genericContext, int index)
{
if (genericContext.MethodParameters == null || index >= genericContext.MethodParameters.Length)
{
return "!!" + index.ToString();
}
return genericContext.MethodParameters[index];
}
public virtual string GetGenericTypeParameter(DisassemblingGenericContext genericContext, int index)
{
if (genericContext.TypeParameters == null || index >= genericContext.TypeParameters.Length)
{
return "!" + index.ToString();
}
return genericContext.TypeParameters[index];
}
public virtual string GetPinnedType(string elementType)
{
return elementType + " pinned";
......@@ -125,11 +158,6 @@ public virtual string GetArrayType(string elementType, ArrayShape shape)
return builder.ToString();
}
public virtual string GetTypeFromHandle(MetadataReader reader, DisassemblingGenericContext genericContext, EntityHandle handle)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public virtual string GetModifiedType(string modifierType, string unmodifiedType, bool isRequired)
{
return unmodifiedType + (isRequired ? " modreq(" : " modopt(") + modifierType + ")";
......@@ -172,6 +200,43 @@ public virtual string GetFunctionPointerType(MethodSignature<string> signature)
builder.Append(')');
return builder.ToString();
}
public abstract string GetGenericMethodParameter(TGenericContext genericContext, int index);
public abstract string GetGenericTypeParameter(TGenericContext genericContext, int index);
public abstract string GetTypeFromSpecification(MetadataReader reader, TGenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind);
}
// Test implementation of ISignatureTypeProvider<TType, TGenericContext> that uses strings in ilasm syntax as TType.
// A real provider in any sort of perf constraints would not want to allocate strings freely like this, but it keeps test code simple.
public class DisassemblingTypeProvider : StringTypeProviderBase<DisassemblingGenericContext>
{
public override string GetGenericMethodParameter(DisassemblingGenericContext genericContext, int index)
{
if (genericContext.MethodParameters == null || index >= genericContext.MethodParameters.Length)
{
return "!!" + index.ToString();
}
return genericContext.MethodParameters[index];
}
public override string GetGenericTypeParameter(DisassemblingGenericContext genericContext, int index)
{
if (genericContext.TypeParameters == null || index >= genericContext.TypeParameters.Length)
{
return "!" + index.ToString();
}
return genericContext.TypeParameters[index];
}
public override string GetTypeFromSpecification(MetadataReader reader, DisassemblingGenericContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public string GetTypeFromHandle(MetadataReader reader, DisassemblingGenericContext genericContext, EntityHandle handle)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
}
}
......@@ -449,12 +449,46 @@ private void EnsureMethods()
bool[] isEntryPoint = new bool[nRuntimeFunctions];
// initialize R2RMethods
ParseMethodDefEntrypoints(isEntryPoint);
ParseMethodDefEntrypoints((section, reader) => ParseMethodDefEntrypointsSection(section, reader, isEntryPoint));
ParseInstanceMethodEntrypoints(isEntryPoint);
CountRuntimeFunctions(isEntryPoint);
}
}
private Dictionary<int, ReadyToRunMethod> _runtimeFunctionToMethod = null;
private void EnsureEntrypointRuntimeFunctionToReadyToRunMethodDict()
{
EnsureMethods();
if (_runtimeFunctionToMethod == null)
{
_runtimeFunctionToMethod = new Dictionary<int, ReadyToRunMethod>();
foreach (var section in _methods)
{
foreach (var method in section.Value)
{
if (!_runtimeFunctionToMethod.ContainsKey(method.EntryPointRuntimeFunctionId))
_runtimeFunctionToMethod.Add(method.EntryPointRuntimeFunctionId, method);
}
}
}
}
public IReadOnlyDictionary<TMethod, ReadyToRunMethod> GetCustomMethodToRuntimeFunctionMapping<TType, TMethod, TGenericContext>(IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> provider)
{
EnsureEntrypointRuntimeFunctionToReadyToRunMethodDict();
Dictionary<TMethod, ReadyToRunMethod> customMethods = new Dictionary<TMethod, ReadyToRunMethod>();
if (ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.RuntimeFunctions, out ReadyToRunSection runtimeFunctionSection))
{
ParseMethodDefEntrypoints((section, reader) => ParseMethodDefEntrypointsSectionCustom<TType, TMethod, TGenericContext>(provider, customMethods, section, reader));
ParseInstanceMethodEntrypointsCustom<TType, TMethod, TGenericContext>(provider, customMethods);
}
return customMethods;
}
private bool TryLocateNativeReadyToRunHeader()
{
PEExportTable exportTable = PEReader.GetExportTable();
......@@ -667,12 +701,12 @@ internal int CalculateRuntimeFunctionSize()
/// <summary>
/// Initialize non-generic R2RMethods with method signatures from MethodDefHandle, and runtime function indices from MethodDefEntryPoints
/// </summary>
private void ParseMethodDefEntrypoints(bool[] isEntryPoint)
private void ParseMethodDefEntrypoints(Action<ReadyToRunSection, MetadataReader> methodDefSectionReader)
{
ReadyToRunSection methodEntryPointSection;
if (ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out methodEntryPointSection))
{
ParseMethodDefEntrypointsSection(methodEntryPointSection, GetGlobalMetadataReader(), isEntryPoint);
methodDefSectionReader(methodEntryPointSection, GetGlobalMetadataReader());
}
else if (ReadyToRunAssemblyHeaders != null)
{
......@@ -680,7 +714,7 @@ private void ParseMethodDefEntrypoints(bool[] isEntryPoint)
{
if (ReadyToRunAssemblyHeaders[assemblyIndex].Sections.TryGetValue(ReadyToRunSectionType.MethodDefEntryPoints, out methodEntryPointSection))
{
ParseMethodDefEntrypointsSection(methodEntryPointSection, OpenReferenceAssembly(assemblyIndex + 1), isEntryPoint);
methodDefSectionReader(methodEntryPointSection, OpenReferenceAssembly(assemblyIndex + 1));
}
}
}
......@@ -725,6 +759,69 @@ private void ParseMethodDefEntrypointsSection(ReadyToRunSection section, Metadat
}
}
/// <summary>
/// Parse a single method def entrypoint section. For composite R2R images, this method is called multiple times
/// are method entrypoints are stored separately for each component assembly of the composite R2R executable.
/// </summary>
/// <param name="section">Method entrypoint section to parse</param>
/// <param name="metadataReader">ECMA metadata reader representing this method entrypoint section</param>
/// <param name="isEntryPoint">Set to true for each runtime function index representing a method entrypoint</param>
private void ParseMethodDefEntrypointsSectionCustom<TType, TMethod, TGenericContext>(IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> provider, Dictionary<TMethod, ReadyToRunMethod> foundMethods, ReadyToRunSection section, MetadataReader metadataReader)
{
int methodDefEntryPointsOffset = GetOffset(section.RelativeVirtualAddress);
NativeArray methodEntryPoints = new NativeArray(Image, (uint)methodDefEntryPointsOffset);
uint nMethodEntryPoints = methodEntryPoints.GetCount();
for (uint rid = 1; rid <= nMethodEntryPoints; rid++)
{
int offset = 0;
if (methodEntryPoints.TryGetAt(Image, rid - 1, ref offset))
{
EntityHandle methodHandle = MetadataTokens.MethodDefinitionHandle((int)rid);
int runtimeFunctionId;
int? fixupOffset;
GetRuntimeFunctionIndexFromOffset(offset, out runtimeFunctionId, out fixupOffset);
ReadyToRunMethod r2rMethod = _runtimeFunctionToMethod[runtimeFunctionId];
var customMethod = provider.GetMethodFromMethodDef(metadataReader, MetadataTokens.MethodDefinitionHandle((int)rid), default(TType));
if (!Object.ReferenceEquals(customMethod, null) && !foundMethods.ContainsKey(customMethod))
foundMethods.Add(customMethod, r2rMethod);
}
}
}
/// <summary>
/// Initialize generic method instances with argument types and runtime function indices from InstanceMethodEntrypoints
/// </summary>
private void ParseInstanceMethodEntrypointsCustom<TType, TMethod, TGenericContext>(IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> provider, Dictionary<TMethod, ReadyToRunMethod> foundMethods)
{
if (!ReadyToRunHeader.Sections.TryGetValue(ReadyToRunSectionType.InstanceMethodEntryPoints, out ReadyToRunSection instMethodEntryPointSection))
{
return;
}
int instMethodEntryPointsOffset = GetOffset(instMethodEntryPointSection.RelativeVirtualAddress);
NativeParser parser = new NativeParser(Image, (uint)instMethodEntryPointsOffset);
NativeHashtable instMethodEntryPoints = new NativeHashtable(Image, parser, (uint)(instMethodEntryPointsOffset + instMethodEntryPointSection.Size));
NativeHashtable.AllEntriesEnumerator allEntriesEnum = instMethodEntryPoints.EnumerateAllEntries();
NativeParser curParser = allEntriesEnum.GetNext();
while (!curParser.IsNull())
{
MetadataReader mdReader = _composite ? null : _assemblyCache[0];
var decoder = new R2RSignatureDecoder<TType, TMethod, TGenericContext>(provider, default(TGenericContext), mdReader, this, (int)curParser.Offset);
TMethod customMethod = decoder.ParseMethod();
int runtimeFunctionId;
int? fixupOffset;
GetRuntimeFunctionIndexFromOffset((int)decoder.Offset, out runtimeFunctionId, out fixupOffset);
ReadyToRunMethod r2rMethod = _runtimeFunctionToMethod[runtimeFunctionId];
if (!Object.ReferenceEquals(customMethod, null) && !foundMethods.ContainsKey(customMethod))
foundMethods.Add(customMethod, r2rMethod);
foundMethods.Add(customMethod, r2rMethod);
curParser = allEntriesEnum.GetNext();
}
}
/// <summary>
/// Initialize generic method instances with argument types and runtime function indices from InstanceMethodEntrypoints
/// </summary>
......
......@@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Text;
......@@ -364,15 +366,25 @@ private string EmitString(StringHandle handle)
}
}
public interface IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> : ISignatureTypeProvider<TType, TGenericContext>
{
TType GetCanonType();
TMethod GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TType owningTypeOverride);
TMethod GetMethodFromMemberRef(MetadataReader reader, MemberReferenceHandle handle, TType owningTypeOverride);
TMethod GetInstantiatedMethod(TMethod uninstantiatedMethod, ImmutableArray<TType> instantiation);
TMethod GetConstrainedMethod(TMethod method, TType constraint);
TMethod GetMethodWithFlags(ReadyToRunMethodSigFlags flags, TMethod method);
}
/// <summary>
/// Helper class used as state machine for decoding a single signature.
/// </summary>
public class SignatureDecoder
public class R2RSignatureDecoder<TType, TMethod, TGenericContext>
{
/// <summary>
/// ECMA reader is used to access the embedded MSIL metadata blob in the R2R file.
/// </summary>
private readonly MetadataReader _metadataReader;
protected readonly MetadataReader _metadataReader;
/// <summary>
/// Outer ECMA reader is used as the default context for generic parameters.
......@@ -382,17 +394,12 @@ public class SignatureDecoder
/// <summary>
/// ECMA reader representing the reference module of the signature being decoded.
/// </summary>
private readonly ReadyToRunReader _contextReader;
/// <summary>
/// Dump options are used to specify details of signature formatting.
/// </summary>
private readonly IAssemblyResolver _options;
protected readonly ReadyToRunReader _contextReader;
/// <summary>
/// Byte array representing the R2R PE file read from disk.
/// </summary>
private readonly byte[] _image;
protected readonly byte[] _image;
/// <summary>
/// Offset within the image file.
......@@ -404,17 +411,26 @@ public class SignatureDecoder
/// </summary>
public int Offset => _offset;
private IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> _provider;
protected void UpdateOffset(int offset)
{
_offset = offset;
}
public TGenericContext Context { get; }
/// <summary>
/// Construct the signature decoder by storing the image byte array and offset within the array.
/// </summary>
/// <param name="options">Dump options and paths</param>
/// <param name="r2rReader">R2RReader object representing the PE file containing the ECMA metadata</param>
/// <param name="offset">Signature offset within the PE file byte array</param>
public SignatureDecoder(IAssemblyResolver options, MetadataReader metadataReader, ReadyToRunReader r2rReader, int offset)
public R2RSignatureDecoder(IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> provider, TGenericContext context, MetadataReader metadataReader, ReadyToRunReader r2rReader, int offset)
{
_metadataReader = metadataReader;
_outerReader = metadataReader;
_options = options;
Context = context;
_provider = provider;
_image = r2rReader.Image;
_offset = offset;
_contextReader = r2rReader;
......@@ -429,9 +445,10 @@ public SignatureDecoder(IAssemblyResolver options, MetadataReader metadataReader
/// <param name="offset">Signature offset within the signature byte array</param>
/// <param name="outerReader">Metadata reader representing the outer signature context</param>
/// <param name="contextReader">Top-level signature context reader</param>
private SignatureDecoder(IAssemblyResolver options, MetadataReader metadataReader, byte[] signature, int offset, MetadataReader outerReader, ReadyToRunReader contextReader)
public R2RSignatureDecoder(IR2RSignatureTypeProvider<TType, TMethod, TGenericContext> provider, TGenericContext context, MetadataReader metadataReader, byte[] signature, int offset, MetadataReader outerReader, ReadyToRunReader contextReader)
{
_options = options;
Context = context;
_provider = provider;
_metadataReader = metadataReader;
_image = signature;
_offset = offset;
......@@ -537,6 +554,400 @@ public CorElementType PeekElementType()
return (CorElementType)(_image[_offset] & 0x7F);
}
/// <summary>
/// Decode a type from the signature stream.
/// </summary>
/// <param name="builder"></param>
public TType ParseType()
{
CorElementType corElemType = ReadElementType();
switch (corElemType)
{
case CorElementType.ELEMENT_TYPE_VOID:
case CorElementType.ELEMENT_TYPE_BOOLEAN:
case CorElementType.ELEMENT_TYPE_CHAR:
case CorElementType.ELEMENT_TYPE_I1:
case CorElementType.ELEMENT_TYPE_U1:
case CorElementType.ELEMENT_TYPE_I2:
case CorElementType.ELEMENT_TYPE_U2:
case CorElementType.ELEMENT_TYPE_I4:
case CorElementType.ELEMENT_TYPE_I8:
case CorElementType.ELEMENT_TYPE_U4:
case CorElementType.ELEMENT_TYPE_U8:
case CorElementType.ELEMENT_TYPE_R4:
case CorElementType.ELEMENT_TYPE_R8:
case CorElementType.ELEMENT_TYPE_STRING:
case CorElementType.ELEMENT_TYPE_OBJECT:
case CorElementType.ELEMENT_TYPE_I:
case CorElementType.ELEMENT_TYPE_U:
case CorElementType.ELEMENT_TYPE_TYPEDBYREF:
return _provider.GetPrimitiveType((PrimitiveTypeCode)corElemType);
case CorElementType.ELEMENT_TYPE_PTR:
return _provider.GetPointerType(ParseType());
case CorElementType.ELEMENT_TYPE_BYREF:
return _provider.GetByReferenceType(ParseType());
case CorElementType.ELEMENT_TYPE_VALUETYPE:
case CorElementType.ELEMENT_TYPE_CLASS:
return ParseTypeDefOrRef(corElemType);
case CorElementType.ELEMENT_TYPE_VAR:
{
uint varIndex = ReadUInt();
return _provider.GetGenericTypeParameter(Context, (int)varIndex);
}
case CorElementType.ELEMENT_TYPE_ARRAY:
{
TType elementType = ParseType();
uint rank = ReadUInt();
if (rank == 0)
return _provider.GetSZArrayType(elementType);
uint sizeCount = ReadUInt(); // number of sizes
uint[] sizes = new uint[sizeCount];
for (uint sizeIndex = 0; sizeIndex < sizeCount; sizeIndex++)
{
sizes[sizeIndex] = ReadUInt();
}
uint lowerBoundCount = ReadUInt(); // number of lower bounds
int[] lowerBounds = new int[lowerBoundCount];
for (uint lowerBoundIndex = 0; lowerBoundIndex < lowerBoundCount; lowerBoundIndex++)
{
lowerBounds[lowerBoundIndex] = ReadInt();
}
ArrayShape arrayShape = new ArrayShape((int)rank, ((int[])(object)sizes).ToImmutableArray(), lowerBounds.ToImmutableArray());
return _provider.GetArrayType(elementType, arrayShape);
}
case CorElementType.ELEMENT_TYPE_GENERICINST:
{
TType genericType = ParseType();
uint typeArgCount = ReadUInt();
var outerDecoder = new R2RSignatureDecoder<TType, TMethod, TGenericContext>(_provider, Context, _outerReader, _image, _offset, _outerReader, _contextReader);
List<TType> parsedTypes = new List<TType>();
for (uint paramIndex = 0; paramIndex < typeArgCount; paramIndex++)
{
parsedTypes.Add(outerDecoder.ParseType());
}
_offset = outerDecoder.Offset;
return _provider.GetGenericInstantiation(genericType, parsedTypes.ToImmutableArray());
}
case CorElementType.ELEMENT_TYPE_FNPTR:
var sigHeader = new SignatureHeader(ReadByte());
int genericParamCount = 0;
if (sigHeader.IsGeneric)
{
genericParamCount = (int)ReadUInt();
}
int paramCount = (int)ReadUInt();
TType returnType = ParseType();
TType[] paramTypes = new TType[paramCount];
int requiredParamCount = -1;
for (int i = 0; i < paramCount; i++)
{
while (PeekElementType() == CorElementType.ELEMENT_TYPE_SENTINEL)
{
requiredParamCount = i;
ReadElementType(); // Skip over sentinel
}
paramTypes[i] = ParseType();
}
if (requiredParamCount == -1)
requiredParamCount = paramCount;
MethodSignature<TType> methodSig = new MethodSignature<TType>(sigHeader, returnType, requiredParamCount, genericParamCount, paramTypes.ToImmutableArray());
return _provider.GetFunctionPointerType(methodSig);
case CorElementType.ELEMENT_TYPE_SZARRAY:
return _provider.GetSZArrayType(ParseType());
case CorElementType.ELEMENT_TYPE_MVAR:
{
uint varIndex = ReadUInt();
return _provider.GetGenericMethodParameter(Context, (int)varIndex);
}
case CorElementType.ELEMENT_TYPE_CMOD_REQD:
return _provider.GetModifiedType(ParseTypeDefOrRefOrSpec(corElemType), ParseType(), true);
case CorElementType.ELEMENT_TYPE_CMOD_OPT:
return _provider.GetModifiedType(ParseTypeDefOrRefOrSpec(corElemType), ParseType(), false);
case CorElementType.ELEMENT_TYPE_HANDLE:
throw new BadImageFormatException("handle");
case CorElementType.ELEMENT_TYPE_SENTINEL:
throw new BadImageFormatException("sentinel");
case CorElementType.ELEMENT_TYPE_PINNED:
return _provider.GetPinnedType(ParseType());
case CorElementType.ELEMENT_TYPE_VAR_ZAPSIG:
throw new BadImageFormatException("var_zapsig");
case CorElementType.ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
throw new BadImageFormatException("native_valuetype_zapsig");
case CorElementType.ELEMENT_TYPE_CANON_ZAPSIG:
return _provider.GetCanonType();
case CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG:
{
int moduleIndex = (int)ReadUInt();
MetadataReader refAsmReader = _contextReader.OpenReferenceAssembly(moduleIndex);
var refAsmDecoder = new R2RSignatureDecoder<TType, TMethod, TGenericContext>(_provider, Context, refAsmReader, _image, _offset, _outerReader, _contextReader);
var result = refAsmDecoder.ParseType();
_offset = refAsmDecoder.Offset;
return result;
}
default:
throw new NotImplementedException();
}
}
private TType ParseTypeDefOrRef(CorElementType corElemType)
{
uint token = ReadToken();
var handle = MetadataTokens.Handle((int)token);
switch (handle.Kind)
{
case HandleKind.TypeDefinition:
return _provider.GetTypeFromDefinition(_metadataReader, (TypeDefinitionHandle)handle, (byte)corElemType);
case HandleKind.TypeReference:
return _provider.GetTypeFromReference(_metadataReader, (TypeReferenceHandle)handle, (byte)corElemType);
default:
throw new BadImageFormatException();
}
}
private TType ParseTypeDefOrRefOrSpec(CorElementType corElemType)
{
uint token = ReadToken();
var handle = MetadataTokens.Handle((int)token);
switch (handle.Kind)
{
case HandleKind.TypeDefinition:
return _provider.GetTypeFromDefinition(_metadataReader, (TypeDefinitionHandle)handle, (byte)corElemType);
case HandleKind.TypeReference:
return _provider.GetTypeFromReference(_metadataReader, (TypeReferenceHandle)handle, (byte)corElemType);
case HandleKind.TypeSpecification:
return _provider.GetTypeFromSpecification(_metadataReader, Context, (TypeSpecificationHandle)handle, (byte)corElemType);
default:
throw new BadImageFormatException();
}
}
public TMethod ParseMethod()
{
uint methodFlags = ReadUInt();
TType owningTypeOverride = default(TType);
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
{
owningTypeOverride = ParseType();
methodFlags &= ~(uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType;
}
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_SlotInsteadOfToken) != 0)
{
throw new NotImplementedException();
}
TMethod result;
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken) != 0)
{
methodFlags &= ~(uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken;
result = ParseMethodRefToken(owningTypeOverride: owningTypeOverride);
}
else
{
result = ParseMethodDefToken(owningTypeOverride: owningTypeOverride);
}
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0)
{
methodFlags &= ~(uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation;
uint typeArgCount = ReadUInt();
TType[] instantiationArgs = new TType[typeArgCount];
for (int typeArgIndex = 0; typeArgIndex < typeArgCount; typeArgIndex++)
{
instantiationArgs[typeArgIndex] = ParseType();
}
result = _provider.GetInstantiatedMethod(result, instantiationArgs.ToImmutableArray());
}
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_Constrained) != 0)
{
methodFlags &= ~(uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_Constrained;
result = _provider.GetConstrainedMethod(result, ParseType());
}
// Any other flags should just be directly recorded
if (methodFlags != 0)
result = _provider.GetMethodWithFlags((ReadyToRunMethodSigFlags)methodFlags, result);
return result;
}
/// <summary>
/// Read a methodDef token from the signature and output the corresponding object to the builder.
/// </summary>
/// <param name="builder">Output string builder</param>
private TMethod ParseMethodDefToken(TType owningTypeOverride)
{
uint rid = ReadUInt();
return _provider.GetMethodFromMethodDef(_metadataReader, MetadataTokens.MethodDefinitionHandle((int)rid), owningTypeOverride);
}
/// <summary>
/// Read a memberRef token from the signature and output the corresponding object to the builder.
/// </summary>
/// <param name="builder">Output string builder</param>
/// <param name="owningTypeOverride">Explicit owning type override</param>
private TMethod ParseMethodRefToken(TType owningTypeOverride)
{
uint rid = ReadUInt();
return _provider.GetMethodFromMemberRef(_metadataReader, MetadataTokens.MemberReferenceHandle((int)rid), owningTypeOverride);
}
}
public class TextSignatureDecoderContext
{
public TextSignatureDecoderContext(IAssemblyResolver options)
{
Options = options;
}
/// <summary>
/// Dump options are used to specify details of signature formatting.
/// </summary>
public IAssemblyResolver Options { get; }
}
/// <summary>
/// Helper class used as state machine for decoding a single signature.
/// </summary>
public class SignatureDecoder : R2RSignatureDecoder<string, string, TextSignatureDecoderContext>
{
private class TextTypeProvider : StringTypeProviderBase<TextSignatureDecoderContext>, IR2RSignatureTypeProvider<string, string, TextSignatureDecoderContext>
{
private TextTypeProvider()
{
}
public static readonly TextTypeProvider Singleton = new TextTypeProvider();
public override string GetGenericMethodParameter(TextSignatureDecoderContext genericContext, int index)
{
return $"mvar #{index}";
}
public override string GetGenericTypeParameter(TextSignatureDecoderContext genericContext, int index)
{
return $"var #{index}";
}
public override string GetTypeFromSpecification(MetadataReader reader, TextSignatureDecoderContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
return MetadataNameFormatter.FormatHandle(reader, handle);
}
public string GetCanonType()
{
return "__Canon";
}
public string GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, string owningTypeOverride)
{
uint methodDefToken = (uint)MetadataTokens.GetToken(handle);
return MetadataNameFormatter.FormatHandle(
reader,
MetadataTokens.Handle((int)methodDefToken),
namespaceQualified: true,
owningTypeOverride: owningTypeOverride);
}
public string GetMethodFromMemberRef(MetadataReader reader, MemberReferenceHandle handle, string owningTypeOverride)
{
uint methodRefToken = (uint)MetadataTokens.GetToken(handle);
return MetadataNameFormatter.FormatHandle(
reader,
MetadataTokens.Handle((int)methodRefToken),
namespaceQualified: true,
owningTypeOverride: owningTypeOverride);
}
public string GetInstantiatedMethod(string uninstantiatedMethod, ImmutableArray<string> instantiation)
{
StringBuilder builder = new StringBuilder();
builder.Append(uninstantiatedMethod);
builder.Append("<");
for (int typeArgIndex = 0; typeArgIndex < instantiation.Length; typeArgIndex++)
{
if (typeArgIndex != 0)
{
builder.Append(", ");
}
builder.Append(instantiation[typeArgIndex]);
}
builder.Append(">");
return builder.ToString();
}
public string GetConstrainedMethod(string method, string constraint)
{
return $"{method} @ {constraint}";
}
public string GetMethodWithFlags(ReadyToRunMethodSigFlags flags, string method)
{
StringBuilder builder = new StringBuilder();
if ((flags & ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0)
{
builder.Append("[UNBOX] ");
}
if ((flags & ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0)
{
builder.Append("[INST] ");
}
builder.Append(method);
return builder.ToString();
}
}
/// <summary>
/// Construct the signature decoder by storing the image byte array and offset within the array.
/// </summary>
/// <param name="options">Dump options and paths</param>
/// <param name="r2rReader">R2RReader object representing the PE file containing the ECMA metadata</param>
/// <param name="offset">Signature offset within the PE file byte array</param>
public SignatureDecoder(IAssemblyResolver options, MetadataReader metadataReader, ReadyToRunReader r2rReader, int offset) :
base(TextTypeProvider.Singleton, new TextSignatureDecoderContext(options), metadataReader, r2rReader, offset)
{
}
/// <summary>
/// Construct the signature decoder by storing the image byte array and offset within the array.
/// </summary>
/// <param name="options">Dump options and paths</param>
/// <param name="metadataReader">Metadata reader for the R2R image</param>
/// <param name="signature">Signature to parse</param>
/// <param name="offset">Signature offset within the signature byte array</param>
/// <param name="outerReader">Metadata reader representing the outer signature context</param>
/// <param name="contextReader">Top-level signature context reader</param>
private SignatureDecoder(IAssemblyResolver options, MetadataReader metadataReader, byte[] signature, int offset, MetadataReader outerReader, ReadyToRunReader contextReader) :
base(TextTypeProvider.Singleton, new TextSignatureDecoderContext(options), metadataReader, signature, offset, outerReader, contextReader)
{
}
/// <summary>
/// Decode a R2R import signature. The signature starts with the fixup type followed
/// by custom encoding per fixup type.
......@@ -546,7 +957,7 @@ public string ReadR2RSignature(out ReadyToRunSignature result)
{
result = null;
StringBuilder builder = new StringBuilder();
int startOffset = _offset;
int startOffset = Offset;
try
{
result = ParseSignature(builder);
......@@ -563,7 +974,7 @@ public string ReadR2RSignature(out ReadyToRunSignature result)
public string ReadTypeSignature()
{
StringBuilder builder = new StringBuilder();
int startOffset = _offset;
int startOffset = Offset;
try
{
ParseType(builder);
......@@ -594,12 +1005,12 @@ public string ReadTypeSignatureNoEmit()
private void EmitInlineSignatureBinaryForm(StringBuilder builder, int startOffset)
{
EmitInlineSignatureBinaryBytes(builder, _offset - startOffset);
EmitInlineSignatureBinaryBytes(builder, Offset - startOffset);
}
private void EmitInlineSignatureBinaryBytes(StringBuilder builder, int count)
{
if (_options.InlineSignatureBinary)
if (Context.Options.InlineSignatureBinary)
{
if (builder.Length > 0 && Char.IsDigit(builder[builder.Length - 1]))
{
......@@ -612,7 +1023,7 @@ private void EmitInlineSignatureBinaryBytes(StringBuilder builder, int count)
{
builder.Append('-');
}
builder.Append(_image[_offset - count + index].ToString("x2"));
builder.Append(_image[Offset - count + index].ToString("x2"));
}
builder.Append("-");
}
......@@ -620,33 +1031,17 @@ private void EmitInlineSignatureBinaryBytes(StringBuilder builder, int count)
private uint ReadUIntAndEmitInlineSignatureBinary(StringBuilder builder)
{
int startOffset = _offset;
int startOffset = Offset;
uint value = ReadUInt();
EmitInlineSignatureBinaryForm(builder, startOffset);
return value;
}
private int ReadIntAndEmitInlineSignatureBinary(StringBuilder builder)
{
int startOffset = _offset;
int value = ReadInt();
EmitInlineSignatureBinaryForm(builder, startOffset);
return value;
}
private uint ReadTokenAndEmitInlineSignatureBinary(StringBuilder builder)
{
int startOffset = _offset;
uint value = ReadToken();
EmitInlineSignatureBinaryForm(builder, startOffset);
return value;
}
private void EmitSignatureBinaryFrom(StringBuilder builder, int startOffset)
{
if (_options.SignatureBinary)
if (Context.Options.SignatureBinary)
{
for (int offset = startOffset; offset < _offset; offset++)
for (int offset = startOffset; offset < Offset; offset++)
{
builder.Append(offset == startOffset ? " [" : "-");
builder.Append(_image[offset].ToString("x2"));
......@@ -672,11 +1067,11 @@ private ReadyToRunSignature ParseSignature(StringBuilder builder)
fixupType &= ~(uint)ReadyToRunFixupKind.ModuleOverride;
int moduleIndex = (int)ReadUIntAndEmitInlineSignatureBinary(builder);
MetadataReader refAsmEcmaReader = _contextReader.OpenReferenceAssembly(moduleIndex);
moduleDecoder = new SignatureDecoder(_options, refAsmEcmaReader, _image, _offset, refAsmEcmaReader, _contextReader);
moduleDecoder = new SignatureDecoder(Context.Options, refAsmEcmaReader, _image, Offset, refAsmEcmaReader, _contextReader);
}
ReadyToRunSignature result = moduleDecoder.ParseSignature((ReadyToRunFixupKind)fixupType, builder);
_offset = moduleDecoder.Offset;
UpdateOffset(moduleDecoder.Offset);
return result;
}
......@@ -732,14 +1127,14 @@ private ReadyToRunSignature ParseSignature(ReadyToRunFixupKind fixupType, String
case ReadyToRunFixupKind.MethodEntry_DefToken:
uint methodDefToken = ParseMethodDefToken(builder, owningTypeOverride: null);
builder.Append(" (METHOD_ENTRY");
builder.Append(_options.Naked ? ")" : "_DEF_TOKEN)");
builder.Append(Context.Options.Naked ? ")" : "_DEF_TOKEN)");
result = new MethodDefEntrySignature { MethodDefToken = methodDefToken };
break;
case ReadyToRunFixupKind.MethodEntry_RefToken:
uint methodRefToken = ParseMethodRefToken(builder, owningTypeOverride: null);
builder.Append(" (METHOD_ENTRY");
builder.Append(_options.Naked ? ")" : "_REF_TOKEN)");
builder.Append(Context.Options.Naked ? ")" : "_REF_TOKEN)");
result = new MethodRefEntrySignature { MethodRefToken = methodRefToken };
break;
......@@ -752,13 +1147,13 @@ private ReadyToRunSignature ParseSignature(ReadyToRunFixupKind fixupType, String
case ReadyToRunFixupKind.VirtualEntry_DefToken:
ParseMethodDefToken(builder, owningTypeOverride: null);
builder.Append(" (VIRTUAL_ENTRY");
builder.Append(_options.Naked ? ")" : "_DEF_TOKEN)");
builder.Append(Context.Options.Naked ? ")" : "_DEF_TOKEN)");
break;
case ReadyToRunFixupKind.VirtualEntry_RefToken:
ParseMethodRefToken(builder, owningTypeOverride: null);
builder.Append(" (VIRTUAL_ENTRY");
builder.Append(_options.Naked ? ")" : "_REF_TOKEN)");
builder.Append(Context.Options.Naked ? ")" : "_REF_TOKEN)");
break;
case ReadyToRunFixupKind.VirtualEntry_Slot:
......@@ -948,318 +1343,33 @@ private ReadyToRunSignature ParseSignature(ReadyToRunFixupKind fixupType, String
/// <param name="builder"></param>
private void ParseType(StringBuilder builder)
{
CorElementType corElemType = ReadElementType();
EmitInlineSignatureBinaryBytes(builder, 1);
switch (corElemType)
{
case CorElementType.ELEMENT_TYPE_VOID:
builder.Append("void");
break;
case CorElementType.ELEMENT_TYPE_BOOLEAN:
builder.Append("bool");
break;
case CorElementType.ELEMENT_TYPE_CHAR:
builder.Append("char");
break;
case CorElementType.ELEMENT_TYPE_I1:
builder.Append("sbyte");
break;
case CorElementType.ELEMENT_TYPE_U1:
builder.Append("byte");
break;
case CorElementType.ELEMENT_TYPE_I2:
builder.Append("short");
break;
case CorElementType.ELEMENT_TYPE_U2:
builder.Append("ushort");
break;
case CorElementType.ELEMENT_TYPE_I4:
builder.Append("int");
break;
case CorElementType.ELEMENT_TYPE_U4:
builder.Append("uint");
break;
case CorElementType.ELEMENT_TYPE_I8:
builder.Append("long");
break;
case CorElementType.ELEMENT_TYPE_U8:
builder.Append("ulong");
break;
case CorElementType.ELEMENT_TYPE_R4:
builder.Append("float");
break;
case CorElementType.ELEMENT_TYPE_R8:
builder.Append("double");
break;
case CorElementType.ELEMENT_TYPE_STRING:
builder.Append("string");
break;
case CorElementType.ELEMENT_TYPE_PTR:
ParseType(builder);
builder.Append('*');
break;
case CorElementType.ELEMENT_TYPE_BYREF:
builder.Append("byref");
break;
case CorElementType.ELEMENT_TYPE_VALUETYPE:
case CorElementType.ELEMENT_TYPE_CLASS:
ParseTypeToken(builder);
break;
case CorElementType.ELEMENT_TYPE_VAR:
uint varIndex = ReadUIntAndEmitInlineSignatureBinary(builder);
builder.Append("var #");
builder.Append(varIndex);
break;
case CorElementType.ELEMENT_TYPE_ARRAY:
ParseType(builder);
{
builder.Append('[');
int startOffset = _offset;
uint rank = ReadUIntAndEmitInlineSignatureBinary(builder);
if (rank != 0)
{
uint sizeCount = ReadUIntAndEmitInlineSignatureBinary(builder); // number of sizes
uint[] sizes = new uint[sizeCount];
for (uint sizeIndex = 0; sizeIndex < sizeCount; sizeIndex++)
{
sizes[sizeIndex] = ReadUIntAndEmitInlineSignatureBinary(builder);
}
uint lowerBoundCount = ReadUIntAndEmitInlineSignatureBinary(builder); // number of lower bounds
int[] lowerBounds = new int[lowerBoundCount];
for (uint lowerBoundIndex = 0; lowerBoundIndex < lowerBoundCount; lowerBoundIndex++)
{
lowerBounds[lowerBoundIndex] = ReadIntAndEmitInlineSignatureBinary(builder);
}
for (int index = 0; index < rank; index++)
{
if (index > 0)
{
builder.Append(',');
}
if (lowerBoundCount > index && lowerBounds[index] != 0)
{
builder.Append(lowerBounds[index]);
builder.Append("..");
if (sizeCount > index)
{
builder.Append(lowerBounds[index] + sizes[index] - 1);
}
}
else if (sizeCount > index)
{
builder.Append(sizes[index]);
}
else if (rank == 1)
{
builder.Append('*');
}
}
}
builder.Append(']');
}
break;
case CorElementType.ELEMENT_TYPE_GENERICINST:
ParseGenericTypeInstance(builder);
break;
case CorElementType.ELEMENT_TYPE_TYPEDBYREF:
builder.Append("typedbyref");
break;
case CorElementType.ELEMENT_TYPE_I:
builder.Append("IntPtr");
break;
case CorElementType.ELEMENT_TYPE_U:
builder.Append("UIntPtr");
break;
case CorElementType.ELEMENT_TYPE_FNPTR:
builder.Append("fnptr");
break;
case CorElementType.ELEMENT_TYPE_OBJECT:
builder.Append("object");
break;
case CorElementType.ELEMENT_TYPE_SZARRAY:
ParseType(builder);
builder.Append("[]");
break;
case CorElementType.ELEMENT_TYPE_MVAR:
uint mvarIndex = ReadUIntAndEmitInlineSignatureBinary(builder);
builder.Append("mvar #");
builder.Append(mvarIndex);
break;
case CorElementType.ELEMENT_TYPE_CMOD_REQD:
builder.Append("cmod_reqd");
break;
case CorElementType.ELEMENT_TYPE_CMOD_OPT:
builder.Append("cmod_opt");
break;
case CorElementType.ELEMENT_TYPE_HANDLE:
builder.Append("handle");
break;
case CorElementType.ELEMENT_TYPE_SENTINEL:
builder.Append("sentinel");
break;
case CorElementType.ELEMENT_TYPE_PINNED:
builder.Append("pinned");
break;
case CorElementType.ELEMENT_TYPE_VAR_ZAPSIG:
builder.Append("var_zapsig");
break;
case CorElementType.ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
builder.Append("native_valuetype_zapsig");
break;
case CorElementType.ELEMENT_TYPE_CANON_ZAPSIG:
builder.Append("__Canon");
break;
case CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG:
{
int moduleIndex = (int)ReadUIntAndEmitInlineSignatureBinary(builder);
MetadataReader refAsmReader = _contextReader.OpenReferenceAssembly(moduleIndex);
SignatureDecoder refAsmDecoder = new SignatureDecoder(_options, refAsmReader, _image, _offset, _outerReader, _contextReader);
refAsmDecoder.ParseType(builder);
_offset = refAsmDecoder.Offset;
}
break;
default:
throw new NotImplementedException();
}
builder.Append(base.ParseType());
}
public MetadataReader GetMetadataReaderFromModuleOverride()
{
if (PeekElementType() == CorElementType.ELEMENT_TYPE_MODULE_ZAPSIG)
{
var currentOffset = _offset;
var currentOffset = Offset;
ReadElementType();
int moduleIndex = (int)ReadUInt();
MetadataReader refAsmReader = _contextReader.OpenReferenceAssembly(moduleIndex);
_offset = currentOffset;
UpdateOffset(currentOffset);
return refAsmReader;
}
return null;
}
private void ParseGenericTypeInstance(StringBuilder builder)
{
ParseType(builder);
uint typeArgCount = ReadUIntAndEmitInlineSignatureBinary(builder);
SignatureDecoder outerDecoder = new SignatureDecoder(_options, _outerReader, _image, _offset, _outerReader, _contextReader);
builder.Append("<");
for (uint paramIndex = 0; paramIndex < typeArgCount; paramIndex++)
{
if (paramIndex > 0)
{
builder.Append(", ");
}
outerDecoder.ParseType(builder);
}
builder.Append(">");
_offset = outerDecoder.Offset;
}
private void ParseTypeToken(StringBuilder builder)
{
StringBuilder signaturePrefixBuilder = new StringBuilder();
uint token = ReadTokenAndEmitInlineSignatureBinary(signaturePrefixBuilder);
builder.Append(MetadataNameFormatter.FormatHandle(
_metadataReader,
MetadataTokens.Handle((int)token),
owningTypeOverride: null,
signaturePrefix: signaturePrefixBuilder.ToString()));
}
/// <summary>
/// Parse an arbitrary method signature.
/// </summary>
/// <param name="builder">Output string builder to receive the textual signature representation</param>
private void ParseMethod(StringBuilder builder)
{
uint methodFlags = ReadUIntAndEmitInlineSignatureBinary(builder);
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UnboxingStub) != 0)
{
builder.Append("[UNBOX] ");
}
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0)
{
builder.Append("[INST] ");
}
string owningTypeOverride = null;
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0)
{
owningTypeOverride = ReadTypeSignatureNoEmit();
}
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_SlotInsteadOfToken) != 0)
{
throw new NotImplementedException();
}
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken) != 0)
{
ParseMethodRefToken(builder, owningTypeOverride: owningTypeOverride);
}
else
{
ParseMethodDefToken(builder, owningTypeOverride: owningTypeOverride);
}
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0)
{
uint typeArgCount = ReadUIntAndEmitInlineSignatureBinary(builder);
builder.Append("<");
for (int typeArgIndex = 0; typeArgIndex < typeArgCount; typeArgIndex++)
{
if (typeArgIndex != 0)
{
builder.Append(", ");
}
ParseType(builder);
}
builder.Append(">");
}
if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_Constrained) != 0)
{
builder.Append(" @ ");
ParseType(builder);
}
builder.Append(ParseMethod());
}
/// <summary>
......
......@@ -38,8 +38,8 @@ public class CommandLineOptions
public bool Resilient { get; set; }
public bool Map { get; set; }
public int Parallelism { get; set; }
public ReadyToRunMethodLayoutAlgorithm MethodLayout { get; set; }
public ReadyToRunFileLayoutAlgorithm FileLayout { get; set; }
public string SingleMethodTypeName { get; set; }
public string SingleMethodName { get; set; }
public string[] SingleMethodGenericArgs { get; set; }
......@@ -178,6 +178,14 @@ public static Command RootCommand()
{
Argument = new Argument<bool>()
},
new Option(new[] { "--method-layout" }, SR.MethodLayoutOption)
{
Argument = new Argument<ReadyToRunMethodLayoutAlgorithm>()
},
new Option(new[] { "--file-layout" }, SR.FileLayoutOption)
{
Argument = new Argument<ReadyToRunFileLayoutAlgorithm>()
},
};
}
}
......
......@@ -474,6 +474,8 @@ private int Run()
.UseResilience(_commandLineOptions.Resilient)
.UseMapFile(_commandLineOptions.Map)
.UseParallelism(_commandLineOptions.Parallelism)
.UseProfileData(profileDataManager)
.FileLayoutAlgorithms(_commandLineOptions.MethodLayout, _commandLineOptions.FileLayout)
.UseJitPath(_commandLineOptions.JitPath)
.UseInstructionSetSupport(instructionSetSupport)
.GenerateOutputFile(_commandLineOptions.OutputFilePath.FullName)
......
......@@ -261,4 +261,10 @@
<data name="ErrorMultipleInputFilesCompositeModeOnly" xml:space="preserve">
<value>Error: multiple input files are only supported in composite build mode: {0}</value>
</data>
<data name="MethodLayoutOption" xml:space="preserve">
<value>Layout methods in file using profile driven optimization assuming the layout algorithm specified. The default value is DefaultSort, which indicates that complex layout is disabled</value>
</data>
<data name="FileLayoutOption" xml:space="preserve">
<value>Layout non-method contents of file using profile driven optimization assuming the layout algorithm specified. The default value is DefaultSort, which indicates that complex layout is disabled</value>
</data>
</root>
\ No newline at end of file
......@@ -22,6 +22,10 @@
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.IO.Compression;
using Microsoft.Diagnostics.Tracing.Parsers.Kernel;
using System.Diagnostics.CodeAnalysis;
using ILCompiler.Reflection.ReadyToRun;
using Microsoft.Diagnostics.Tools.Pgo;
namespace Microsoft.Diagnostics.Tools.Pgo
{
......@@ -39,6 +43,27 @@ public enum jittraceoptions
showtimestamp = 2,
}
class CommandLineOptions
{
public FileInfo TraceFile { get; set; }
public FileInfo OutputFileName { get; set; }
public int? Pid { get; set; }
public string ProcessName { get; set; }
public PgoFileType? PgoFileType { get; set; }
public IEnumerable<FileInfo> Reference { get; set; }
public int? ClrInstanceId { get; set; }
public bool ProcessJitEvents { get; set; }
public bool ProcessR2REvents { get; set; }
public bool DisplayProcessedEvents { get; set; }
public bool ValidateOutputFile { get; set; }
public bool GenerateCallGraph { get; set; }
public bool VerboseWarnings { get; set; }
public jittraceoptions JitTraceOptions { get; set; }
public double ExcludeEventsBefore { get; set; }
public double ExcludeEventsAfter { get; set; }
public bool Warnings { get; set; }
}
class Program
{
static bool s_reachedInnerMain;
......@@ -52,7 +77,7 @@ static int Main(string []args)
Description = "Specify the trace file to be parsed",
Argument = new Argument<FileInfo>()
{
Arity = ArgumentArity.ExactlyOne
Arity = ArgumentArity.ExactlyOne,
}
},
new Option("--output-file-name")
......@@ -106,47 +131,52 @@ static int Main(string []args)
new Option("--process-jit-events")
{
Description = "Process JIT events. Defaults to true",
Argument = new Argument<bool>()
Argument = new Argument<bool>(() => true)
},
new Option("--process-r2r-events")
{
Description = "Process R2R events. Defaults to true",
Argument = new Argument<bool>()
Argument = new Argument<bool>(() => true)
},
new Option("--display-processed-events")
{
Description = "Process R2R events. Defaults to true",
Argument = new Argument<bool>()
Argument = new Argument<bool>(() => false)
},
new Option("--warnings")
{
Description = "Display warnings for methods which could not be processed. Defaults to true",
Argument = new Argument<bool>()
Argument = new Argument<bool>(() => true)
},
new Option("--verbose-warnings")
{
Description = "Display information about why jit events may be not processed. Defaults to false",
Argument = new Argument<bool>()
Argument = new Argument<bool>(() => false)
},
new Option("--validate-output-file")
{
Description = "Validate output file. Defaults to true. Not all output formats support validation",
Argument = new Argument<bool>()
Argument = new Argument<bool>(() => true)
},
new Option("--jittrace-options")
{
Description = "Jit Trace emit options (defaults to sorted) Valid options are 'none', 'sorted', 'showtimestamp', 'sorted,showtimestamp'",
Argument = new Argument<jittraceoptions>()
Argument = new Argument<jittraceoptions>(() => jittraceoptions.sorted)
},
new Option("--exclude-events-before")
{
Description = "Exclude data from events before specified time",
Argument = new Argument<double>()
Argument = new Argument<double>(() => 0)
},
new Option("--exclude-events-after")
{
Description = "Exclude data from events after specified time",
Argument = new Argument<double>()
Argument = new Argument<double>(() => Double.MaxValue)
},
new Option("--generate-call-graph")
{
Description = "Generate Call Graph using sampling data",
Argument = new Argument<bool>(() => true)
}
};
......@@ -154,7 +184,7 @@ static int Main(string []args)
try
{
s_reachedInnerMain = false;
rootCommand.Handler = CommandHandler.Create(new Func<FileInfo, FileInfo, int?, string, PgoFileType?, IEnumerable<FileInfo>, int?, bool, bool, bool, bool, bool, jittraceoptions, double, double, bool, int>(InnerMain));
rootCommand.Handler = CommandHandler.Create<CommandLineOptions>((CommandLineOptions options) => InnerMain(options));
Task<int> command = rootCommand.InvokeAsync(args);
command.Wait();
......@@ -211,61 +241,122 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
Millisecond = millisecond;
Method = method;
Reason = reason;
WeightedCallData = null;
ExclusiveWeight = 0;
}
public readonly double Millisecond;
public readonly MethodDesc Method;
public readonly string Reason;
public Dictionary<MethodDesc, int> WeightedCallData;
public int ExclusiveWeight;
}
struct InstructionPointerRange : IComparable<InstructionPointerRange>
{
public InstructionPointerRange(ulong startAddress, int size)
{
StartAddress = startAddress;
EndAddress = startAddress + (ulong)size;
}
public ulong StartAddress;
public ulong EndAddress;
public int CompareTo(InstructionPointerRange other)
{
if (StartAddress < other.StartAddress)
{
return -1;
}
if (StartAddress > other.StartAddress)
{
return 1;
}
return (int)((long)EndAddress - (long)other.EndAddress);
}
}
static int InnerMain(FileInfo traceFile,
FileInfo outputFileName,
int? pid,
string processName,
PgoFileType? pgoFileType,
IEnumerable<FileInfo> reference,
int? clrInstanceId = null,
bool processJitEvents = true,
bool processR2REvents = true,
bool displayProcessedEvents = false,
bool validateOutputFile = true,
bool verboseWarnings = false,
jittraceoptions jitTraceOptions = jittraceoptions.sorted,
double excludeEventsBefore = 0,
double excludeEventsAfter = Double.MaxValue,
bool warnings = true)
internal static void UnZipIfNecessary(ref string inputFileName, TextWriter log)
{
if (inputFileName.EndsWith(".trace.zip", StringComparison.OrdinalIgnoreCase))
{
log.WriteLine($"'{inputFileName}' is a linux trace.");
return;
}
var extension = Path.GetExtension(inputFileName);
if (string.Compare(extension, ".zip", StringComparison.OrdinalIgnoreCase) == 0 ||
string.Compare(extension, ".vspx", StringComparison.OrdinalIgnoreCase) == 0)
{
string unzipedEtlFile;
if (inputFileName.EndsWith(".etl.zip", StringComparison.OrdinalIgnoreCase))
{
unzipedEtlFile = inputFileName.Substring(0, inputFileName.Length - 4);
}
else if (inputFileName.EndsWith(".vspx", StringComparison.OrdinalIgnoreCase))
{
unzipedEtlFile = Path.ChangeExtension(inputFileName, ".etl");
}
else
{
throw new ApplicationException("File does not end with the .etl.zip file extension");
}
ZippedETLReader etlReader = new ZippedETLReader(inputFileName, log);
etlReader.EtlFileName = unzipedEtlFile;
// Figure out where to put the symbols.
var inputDir = Path.GetDirectoryName(inputFileName);
if (inputDir.Length == 0)
{
inputDir = ".";
}
var symbolsDir = Path.Combine(inputDir, "symbols");
etlReader.SymbolDirectory = symbolsDir;
if (!Directory.Exists(symbolsDir))
Directory.CreateDirectory(symbolsDir);
log.WriteLine("Putting symbols in {0}", etlReader.SymbolDirectory);
etlReader.UnpackArchive();
inputFileName = unzipedEtlFile;
}
}
static int InnerMain(CommandLineOptions commandLineOptions)
{
s_reachedInnerMain = true;
if (traceFile == null)
if (commandLineOptions.TraceFile == null)
{
PrintUsage("--trace-file must be specified");
return -8;
}
if (outputFileName != null)
if (commandLineOptions.OutputFileName != null)
{
if (!pgoFileType.HasValue)
if (!commandLineOptions.PgoFileType.HasValue)
{
PrintUsage($"--pgo-file-type must be specified");
return -9;
}
if ((pgoFileType.Value != PgoFileType.jittrace) && (pgoFileType != PgoFileType.mibc))
if ((commandLineOptions.PgoFileType.Value != PgoFileType.jittrace) && (commandLineOptions.PgoFileType != PgoFileType.mibc))
{
PrintUsage($"Invalid output pgo type {pgoFileType} specified.");
PrintUsage($"Invalid output pgo type {commandLineOptions.PgoFileType} specified.");
return -9;
}
if (pgoFileType == PgoFileType.jittrace)
if (commandLineOptions.PgoFileType == PgoFileType.jittrace)
{
if (!outputFileName.Name.EndsWith(".jittrace"))
if (!commandLineOptions.OutputFileName.Name.EndsWith(".jittrace"))
{
PrintUsage($"jittrace output file name must end with .jittrace");
return -9;
}
}
if (pgoFileType == PgoFileType.mibc)
if (commandLineOptions.PgoFileType == PgoFileType.mibc)
{
if (!outputFileName.Name.EndsWith(".mibc"))
if (!commandLineOptions.OutputFileName.Name.EndsWith(".mibc"))
{
PrintUsage($"jittrace output file name must end with .mibc");
return -9;
......@@ -273,28 +364,30 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
}
}
string etlFileName = traceFile.FullName;
string etlFileName = commandLineOptions.TraceFile.FullName;
foreach (string nettraceExtension in new string[] { ".netperf", ".netperf.zip", ".nettrace" })
{
if (traceFile.FullName.EndsWith(nettraceExtension))
if (commandLineOptions.TraceFile.FullName.EndsWith(nettraceExtension))
{
etlFileName = traceFile.FullName.Substring(0, traceFile.FullName.Length - nettraceExtension.Length) + ".etlx";
Console.WriteLine($"Creating ETLX file {etlFileName} from {traceFile.FullName}");
TraceLog.CreateFromEventPipeDataFile(traceFile.FullName, etlFileName);
etlFileName = commandLineOptions.TraceFile.FullName.Substring(0, commandLineOptions.TraceFile.FullName.Length - nettraceExtension.Length) + ".etlx";
Console.WriteLine($"Creating ETLX file {etlFileName} from {commandLineOptions.TraceFile.FullName}");
TraceLog.CreateFromEventPipeDataFile(commandLineOptions.TraceFile.FullName, etlFileName);
}
}
string lttngExtension = ".trace.zip";
if (traceFile.FullName.EndsWith(lttngExtension))
if (commandLineOptions.TraceFile.FullName.EndsWith(lttngExtension))
{
etlFileName = traceFile.FullName.Substring(0, traceFile.FullName.Length - lttngExtension.Length) + ".etlx";
Console.WriteLine($"Creating ETLX file {etlFileName} from {traceFile.FullName}");
TraceLog.CreateFromLttngTextDataFile(traceFile.FullName, etlFileName);
etlFileName = commandLineOptions.TraceFile.FullName.Substring(0, commandLineOptions.TraceFile.FullName.Length - lttngExtension.Length) + ".etlx";
Console.WriteLine($"Creating ETLX file {etlFileName} from {commandLineOptions.TraceFile.FullName}");
TraceLog.CreateFromLttngTextDataFile(commandLineOptions.TraceFile.FullName, etlFileName);
}
UnZipIfNecessary(ref etlFileName, Console.Out);
using (var traceLog = TraceLog.OpenOrConvert(etlFileName))
{
if ((!pid.HasValue && processName == null) && traceLog.Processes.Count != 1)
if ((!commandLineOptions.Pid.HasValue && commandLineOptions.ProcessName == null) && traceLog.Processes.Count != 1)
{
Console.WriteLine("Either a pid or process name from the following list must be specified");
foreach (TraceProcess proc in traceLog.Processes)
......@@ -304,7 +397,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
return 0;
}
if (pid.HasValue && (processName != null))
if (commandLineOptions.Pid.HasValue && (commandLineOptions.ProcessName != null))
{
PrintError("--pid and --process-name cannot be specified together");
return -1;
......@@ -312,16 +405,16 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
// For a particular process
TraceProcess p;
if (pid.HasValue)
if (commandLineOptions.Pid.HasValue)
{
p = traceLog.Processes.LastProcessWithID(pid.Value);
p = traceLog.Processes.LastProcessWithID(commandLineOptions.Pid.Value);
}
else if (processName != null)
else if (commandLineOptions.ProcessName != null)
{
List<TraceProcess> matchingProcesses = new List<TraceProcess>();
foreach (TraceProcess proc in traceLog.Processes)
{
if (String.Compare(proc.Name, processName, StringComparison.OrdinalIgnoreCase) == 0)
if (String.Compare(proc.Name, commandLineOptions.ProcessName, StringComparison.OrdinalIgnoreCase) == 0)
{
matchingProcesses.Add(proc);
}
......@@ -375,7 +468,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
return -5;
}
if (processR2REvents)
if (commandLineOptions.ProcessR2REvents)
{
if (!p.EventsInProcess.ByEventType<R2RGetEntryPointTraceData>().Any())
{
......@@ -384,7 +477,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
}
PgoTraceProcess pgoProcess = new PgoTraceProcess(p);
int? clrInstanceId = commandLineOptions.ClrInstanceId;
if (!clrInstanceId.HasValue)
{
HashSet<int> clrInstanceIds = new HashSet<int>();
......@@ -427,13 +520,13 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
var tsc = new TraceTypeSystemContext(pgoProcess, clrInstanceId.Value, s_logger);
if (verboseWarnings)
if (commandLineOptions.VerboseWarnings)
Console.WriteLine($"{traceLog.EventsLost} Lost events");
bool filePathError = false;
if (reference != null)
if (commandLineOptions.Reference != null)
{
foreach (FileInfo fileReference in reference)
foreach (FileInfo fileReference in commandLineOptions.Reference)
{
if (!File.Exists(fileReference.FullName))
{
......@@ -455,7 +548,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
SortedDictionary<int, ProcessedMethodData> methodsToAttemptToPrepare = new SortedDictionary<int, ProcessedMethodData>();
if (processR2REvents)
if (commandLineOptions.ProcessR2REvents)
{
foreach (var e in p.EventsInProcess.ByEventType<R2RGetEntryPointTraceData>())
{
......@@ -465,7 +558,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
string methodNameFromEventDirectly = retArg + e.MethodNamespace + "." + e.MethodName + paramsArgs;
if (e.ClrInstanceID != clrInstanceId.Value)
{
if (!warnings)
if (!commandLineOptions.Warnings)
continue;
PrintWarning($"Skipped R2REntryPoint {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}");
......@@ -475,7 +568,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
string extraWarningText = null;
try
{
method = idParser.ResolveMethodID(e.MethodID, verboseWarnings);
method = idParser.ResolveMethodID(e.MethodID, commandLineOptions.VerboseWarnings);
}
catch (Exception exception)
{
......@@ -484,7 +577,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
if (method == null)
{
if ((e.MethodNamespace == "dynamicClass") || !warnings)
if ((e.MethodNamespace == "dynamicClass") || !commandLineOptions.Warnings)
continue;
PrintWarning($"Unable to parse {methodNameFromEventDirectly} when looking up R2R methods");
......@@ -492,13 +585,14 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
PrintWarning(extraWarningText);
continue;
}
if ((e.TimeStampRelativeMSec >= excludeEventsBefore) && (e.TimeStampRelativeMSec <= excludeEventsAfter))
if ((e.TimeStampRelativeMSec >= commandLineOptions.ExcludeEventsBefore) && (e.TimeStampRelativeMSec <= commandLineOptions.ExcludeEventsAfter))
methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "R2RLoad"));
}
}
// Find all the jitStart events.
if (processJitEvents)
if (commandLineOptions.ProcessJitEvents)
{
foreach (var e in p.EventsInProcess.ByEventType<MethodJittingStartedTraceData>())
{
......@@ -508,7 +602,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
string methodNameFromEventDirectly = retArg + e.MethodNamespace + "." + e.MethodName + paramsArgs;
if (e.ClrInstanceID != clrInstanceId.Value)
{
if (!warnings)
if (!commandLineOptions.Warnings)
continue;
PrintWarning($"Skipped {methodNameFromEventDirectly} due to ClrInstanceID of {e.ClrInstanceID}");
......@@ -519,7 +613,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
string extraWarningText = null;
try
{
method = idParser.ResolveMethodID(e.MethodID, verboseWarnings);
method = idParser.ResolveMethodID(e.MethodID, commandLineOptions.VerboseWarnings);
}
catch (Exception exception)
{
......@@ -528,7 +622,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
if (method == null)
{
if (!warnings)
if (!commandLineOptions.Warnings)
continue;
PrintWarning($"Unable to parse {methodNameFromEventDirectly}");
......@@ -537,12 +631,199 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
continue;
}
if ((e.TimeStampRelativeMSec >= excludeEventsBefore) && (e.TimeStampRelativeMSec <= excludeEventsAfter))
if ((e.TimeStampRelativeMSec >= commandLineOptions.ExcludeEventsBefore) && (e.TimeStampRelativeMSec <= commandLineOptions.ExcludeEventsAfter))
methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, method, "JitStart"));
}
}
if (displayProcessedEvents)
Dictionary<MethodDesc, Dictionary<MethodDesc, int>> callGraph = null;
Dictionary<MethodDesc, int> exclusiveSamples = null;
if (commandLineOptions.GenerateCallGraph)
{
HashSet<MethodDesc> methodsListedToPrepare = new HashSet<MethodDesc>();
foreach (var entry in methodsToAttemptToPrepare)
{
methodsListedToPrepare.Add(entry.Value.Method);
}
callGraph = new Dictionary<MethodDesc, Dictionary<MethodDesc, int>>();
exclusiveSamples = new Dictionary<MethodDesc, int>();
// Capture the addresses of jitted code
List<ValueTuple<InstructionPointerRange, MethodDesc>> codeLocations = new List<(InstructionPointerRange, MethodDesc)>();
foreach (var e in p.EventsInProcess.ByEventType<MethodLoadUnloadTraceData>())
{
if (e.ClrInstanceID != clrInstanceId.Value)
{
continue;
}
MethodDesc method = null;
try
{
method = idParser.ResolveMethodID(e.MethodID, commandLineOptions.VerboseWarnings);
}
catch (Exception)
{
}
if (method != null)
{
codeLocations.Add((new InstructionPointerRange(e.MethodStartAddress, e.MethodSize), method));
}
}
foreach (var e in p.EventsInProcess.ByEventType<MethodLoadUnloadVerboseTraceData>())
{
if (e.ClrInstanceID != clrInstanceId.Value)
{
continue;
}
MethodDesc method = null;
try
{
method = idParser.ResolveMethodID(e.MethodID, commandLineOptions.VerboseWarnings);
}
catch (Exception)
{
}
if (method != null)
{
codeLocations.Add((new InstructionPointerRange(e.MethodStartAddress, e.MethodSize), method));
}
}
var sigProvider = new R2RSignatureTypeProvider(tsc);
foreach (var module in p.LoadedModules)
{
if (module.FilePath == "")
continue;
if (!File.Exists(module.FilePath))
continue;
try
{
var reader = new ILCompiler.Reflection.ReadyToRun.ReadyToRunReader(tsc, module.FilePath);
foreach (var methodEntry in reader.GetCustomMethodToRuntimeFunctionMapping<TypeDesc, MethodDesc, R2RSigProviderContext>(sigProvider))
{
foreach (var runtimeFunction in methodEntry.Value.RuntimeFunctions)
{
codeLocations.Add((new InstructionPointerRange(module.ImageBase + (ulong)runtimeFunction.StartAddress, runtimeFunction.Size), methodEntry.Key));
}
}
}
catch { }
}
InstructionPointerRange[] instructionPointerRanges = new InstructionPointerRange[codeLocations.Count];
MethodDesc[] methods = new MethodDesc[codeLocations.Count];
for (int i = 0; i < codeLocations.Count; i++)
{
instructionPointerRanges[i] = codeLocations[i].Item1;
methods[i] = codeLocations[i].Item2;
}
Array.Sort(instructionPointerRanges, methods);
foreach (var e in p.EventsInProcess.ByEventType<SampledProfileTraceData>())
{
var callstack = e.CallStack();
if (callstack == null)
continue;
ulong address1 = callstack.CodeAddress.Address;
MethodDesc topOfStackMethod = LookupMethodByAddress(address1);
MethodDesc nextMethod = null;
if (callstack.Caller != null)
{
ulong address2 = callstack.Caller.CodeAddress.Address;
nextMethod = LookupMethodByAddress(address2);
}
if (topOfStackMethod != null)
{
if (!methodsListedToPrepare.Contains(topOfStackMethod))
{
methodsListedToPrepare.Add(topOfStackMethod);
methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, topOfStackMethod, "SampleMethod"));
}
if (exclusiveSamples.TryGetValue(topOfStackMethod, out int count))
{
exclusiveSamples[topOfStackMethod] = count + 1;
}
else
{
exclusiveSamples[topOfStackMethod] = 1;
}
}
if (topOfStackMethod != null && nextMethod != null)
{
if (!methodsListedToPrepare.Contains(nextMethod))
{
methodsListedToPrepare.Add(nextMethod);
methodsToAttemptToPrepare.Add((int)e.EventIndex, new ProcessedMethodData(e.TimeStampRelativeMSec, nextMethod, "SampleMethodCaller"));
}
if (!callGraph.TryGetValue(nextMethod, out var innerDictionary))
{
innerDictionary = new Dictionary<MethodDesc, int>();
callGraph[nextMethod] = innerDictionary;
}
if (innerDictionary.TryGetValue(topOfStackMethod, out int count))
{
innerDictionary[topOfStackMethod] = count + 1;
}
else
{
innerDictionary[topOfStackMethod] = 1;
}
}
}
MethodDesc LookupMethodByAddress(ulong address)
{
int index = Array.BinarySearch(instructionPointerRanges, new InstructionPointerRange(address, 1));
if (index >= 0)
{
return methods[index];
}
else
{
index = ~index;
if (index >= instructionPointerRanges.Length)
return null;
if (instructionPointerRanges[index].StartAddress < address)
{
if (instructionPointerRanges[index].EndAddress > address)
{
return methods[index];
}
}
if (index == 0)
return null;
index--;
if (instructionPointerRanges[index].StartAddress < address)
{
if (instructionPointerRanges[index].EndAddress > address)
{
return methods[index];
}
}
return null;
}
}
}
if (commandLineOptions.DisplayProcessedEvents)
{
foreach (var entry in methodsToAttemptToPrepare)
{
......@@ -554,7 +835,7 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
Console.WriteLine($"Done processing input file");
if (outputFileName == null)
if (commandLineOptions.OutputFileName == null)
{
return 0;
}
......@@ -566,14 +847,20 @@ public ProcessedMethodData(double millisecond, MethodDesc method, string reason)
{
if (methodsInListAlready.Add(entry.Value.Method))
{
methodsUsedInProcess.Add(entry.Value);
var methodData = entry.Value;
if (commandLineOptions.GenerateCallGraph)
{
exclusiveSamples.TryGetValue(methodData.Method, out methodData.ExclusiveWeight);
callGraph.TryGetValue(methodData.Method, out methodData.WeightedCallData);
}
methodsUsedInProcess.Add(methodData);
}
}
if (pgoFileType.Value == PgoFileType.jittrace)
GenerateJittraceFile(outputFileName, methodsUsedInProcess, jitTraceOptions);
else if (pgoFileType.Value == PgoFileType.mibc)
return GenerateMibcFile(tsc, outputFileName, methodsUsedInProcess, validateOutputFile);
if (commandLineOptions.PgoFileType.Value == PgoFileType.jittrace)
GenerateJittraceFile(commandLineOptions.OutputFileName, methodsUsedInProcess, commandLineOptions.JitTraceOptions);
else if (commandLineOptions.PgoFileType.Value == PgoFileType.mibc)
return GenerateMibcFile(tsc, commandLineOptions.OutputFileName, methodsUsedInProcess, commandLineOptions.ValidateOutputFile);
}
return 0;
}
......@@ -602,13 +889,41 @@ public void AddProcessedMethodData(ProcessedMethodData processedMethodData)
// Format is
// ldtoken method
// variable amount of extra metadata about the method
// variable amount of extra metadata about the method, Extension data is encoded via ldstr "id"
// pop
// Extensions generated by this emitter:
//
// ldstr "ExclusiveWeight"
// Any ldc.i4 or ldc.r4 or ldc.r8 instruction to indicate the exclusive weight
//
// ldstr "WeightedCallData"
// ldc.i4 <Count of methods called>
// Repeat <Count of methods called times>
// ldtoken <Method called from this method>
// ldc.i4 <Weight associated with calling the <Method called from this method>>
try
{
EntityHandle methodHandle = _emitter.GetMethodRef(method);
_il.OpCode(ILOpCode.Ldtoken);
_il.Token(methodHandle);
if (processedMethodData.ExclusiveWeight != 0)
{
_il.LoadString(_emitter.GetUserStringHandle("ExclusiveWeight"));
_il.LoadConstantI4(processedMethodData.ExclusiveWeight);
}
if (processedMethodData.WeightedCallData != null)
{
_il.LoadString(_emitter.GetUserStringHandle("WeightedCallData"));
_il.LoadConstantI4(processedMethodData.WeightedCallData.Count);
foreach (var entry in processedMethodData.WeightedCallData)
{
EntityHandle calledMethod = _emitter.GetMethodRef(entry.Key);
_il.OpCode(ILOpCode.Ldtoken);
_il.Token(calledMethod);
_il.LoadConstantI4(entry.Value);
}
}
_il.OpCode(ILOpCode.Pop);
}
catch (Exception ex)
......@@ -806,12 +1121,16 @@ static IEnumerable<MIbcData> ReadMIbcGroup(TypeSystemContext tsc, EcmaMethod met
{
case ILOpcode.ldtoken:
UInt32 token = (UInt32)(ilBytes[currentOffset + 1] + (ilBytes[currentOffset + 2] << 8) + (ilBytes[currentOffset + 3] << 16) + (ilBytes[currentOffset + 4] << 24));
metadataObject = ilBody.GetObject((int)token);
if (metadataObject == null)
metadataObject = ilBody.GetObject((int)token);
break;
case ILOpcode.pop:
MIbcData mibcData = new MIbcData();
mibcData.MetadataObject = metadataObject;
yield return mibcData;
metadataObject = null;
break;
}
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
using System.Linq;
using System.Reflection.Metadata;
using ILCompiler.Reflection.ReadyToRun;
using Internal.ReadyToRunConstants;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
namespace Microsoft.Diagnostics.Tools.Pgo
{
struct R2RSigProviderContext
{
}
class R2RSignatureTypeProvider : IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>
{
public R2RSignatureTypeProvider(TraceTypeSystemContext tsc)
{
_tsc = tsc;
}
TraceTypeSystemContext _tsc;
TypeDesc IConstructedTypeProvider<TypeDesc>.GetArrayType(TypeDesc elementType, ArrayShape shape)
{
if (elementType == null)
return null;
return elementType.MakeArrayType(shape.Rank);
}
TypeDesc IConstructedTypeProvider<TypeDesc>.GetByReferenceType(TypeDesc elementType)
{
if (elementType == null)
return null;
return elementType.MakeByRefType();
}
TypeDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetCanonType()
{
return _tsc.CanonType;
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetConstrainedMethod(MethodDesc method, TypeDesc constraint)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetFunctionPointerType(MethodSignature<TypeDesc> signature)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc IConstructedTypeProvider<TypeDesc>.GetGenericInstantiation(TypeDesc genericType, ImmutableArray<TypeDesc> typeArguments)
{
if (genericType == null)
return null;
foreach (var type in typeArguments)
{
if (type == null)
return null;
}
return _tsc.GetInstantiatedType((MetadataType)genericType, new Instantiation(typeArguments.ToArray()));
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetGenericMethodParameter(R2RSigProviderContext genericContext, int index)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetGenericTypeParameter(R2RSigProviderContext genericContext, int index)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetInstantiatedMethod(MethodDesc uninstantiatedMethod, ImmutableArray<TypeDesc> instantiation)
{
if (uninstantiatedMethod == null)
return null;
foreach (var type in instantiation)
{
if (type == null)
return null;
}
return uninstantiatedMethod.MakeInstantiatedMethod(instantiation.ToArray());
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetMethodFromMemberRef(MetadataReader reader, MemberReferenceHandle handle, TypeDesc owningTypeOverride)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
var method = (MethodDesc)ecmaModule.GetObject(handle);
if (owningTypeOverride != null)
{
return _tsc.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)owningTypeOverride);
}
return method;
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetMethodFromMethodDef(MetadataReader reader, MethodDefinitionHandle handle, TypeDesc owningTypeOverride)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
var method = (MethodDesc)ecmaModule.GetObject(handle);
if (owningTypeOverride != null)
{
return _tsc.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)owningTypeOverride);
}
return method;
}
MethodDesc IR2RSignatureTypeProvider<TypeDesc, MethodDesc, R2RSigProviderContext>.GetMethodWithFlags(ReadyToRunMethodSigFlags flags, MethodDesc method)
{
return method;
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetModifiedType(TypeDesc modifier, TypeDesc unmodifiedType, bool isRequired)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetPinnedType(TypeDesc elementType)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc IConstructedTypeProvider<TypeDesc>.GetPointerType(TypeDesc elementType)
{
// Cannot exist in entrypoint definition
throw new System.NotImplementedException();
}
TypeDesc ISimpleTypeProvider<TypeDesc>.GetPrimitiveType(PrimitiveTypeCode typeCode)
{
WellKnownType wkt = 0;
switch (typeCode)
{
case PrimitiveTypeCode.Void:
wkt = WellKnownType.Void;
break;
case PrimitiveTypeCode.Boolean:
wkt = WellKnownType.Boolean;
break;
case PrimitiveTypeCode.Char:
wkt = WellKnownType.Char;
break;
case PrimitiveTypeCode.SByte:
wkt = WellKnownType.SByte;
break;
case PrimitiveTypeCode.Byte:
wkt = WellKnownType.Byte;
break;
case PrimitiveTypeCode.Int16:
wkt = WellKnownType.Int16;
break;
case PrimitiveTypeCode.UInt16:
wkt = WellKnownType.UInt16;
break;
case PrimitiveTypeCode.Int32:
wkt = WellKnownType.Int32;
break;
case PrimitiveTypeCode.UInt32:
wkt = WellKnownType.UInt32;
break;
case PrimitiveTypeCode.Int64:
wkt = WellKnownType.Int64;
break;
case PrimitiveTypeCode.UInt64:
wkt = WellKnownType.UInt64;
break;
case PrimitiveTypeCode.Single:
wkt = WellKnownType.Single;
break;
case PrimitiveTypeCode.Double:
wkt = WellKnownType.Double;
break;
case PrimitiveTypeCode.String:
wkt = WellKnownType.String;
break;
case PrimitiveTypeCode.TypedReference:
wkt = WellKnownType.TypedReference;
break;
case PrimitiveTypeCode.IntPtr:
wkt = WellKnownType.IntPtr;
break;
case PrimitiveTypeCode.UIntPtr:
wkt = WellKnownType.UIntPtr;
break;
case PrimitiveTypeCode.Object:
wkt = WellKnownType.Object;
break;
}
return _tsc.GetWellKnownType(wkt);
}
TypeDesc ISZArrayTypeProvider<TypeDesc>.GetSZArrayType(TypeDesc elementType)
{
if (elementType == null)
return null;
return elementType.MakeArrayType();
}
TypeDesc ISimpleTypeProvider<TypeDesc>.GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
return (TypeDesc)ecmaModule.GetObject(handle);
}
TypeDesc ISimpleTypeProvider<TypeDesc>.GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
return (TypeDesc)ecmaModule.GetObject(handle);
}
TypeDesc ISignatureTypeProvider<TypeDesc, R2RSigProviderContext>.GetTypeFromSpecification(MetadataReader reader, R2RSigProviderContext genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
var ecmaModule = (EcmaModule)_tsc.GetModuleForSimpleName(reader.GetString(reader.GetAssemblyDefinition().Name));
return (TypeDesc)ecmaModule.GetObject(handle);
}
}
}
......@@ -14,6 +14,7 @@
using System.Reflection;
using System.IO;
using System.Text;
using System.Net;
namespace Microsoft.Diagnostics.Tools.Pgo
{
......@@ -84,6 +85,11 @@ public TypeHandleInfo(long id, TraceTypeData traceData)
public readonly long ID;
public TypeDesc Type;
public readonly TraceTypeData TypeValue;
public override string ToString()
{
return Type != null ? Type.ToString() : "NULL";
}
}
class ModuleDescInfo
......@@ -103,6 +109,8 @@ public ModuleDescInfo(long id, TraceManagedModule traceManagedModule)
private readonly Dictionary<long, TypeHandleInfo> _types = new Dictionary<long, TypeHandleInfo>();
private readonly Dictionary<long, ModuleDescInfo> _modules = new Dictionary<long, ModuleDescInfo>();
private readonly object _lock = new object();
private readonly int s_bulkTypeEvents = 0;
private readonly int s_bulkTypeTypes = 0;
public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemContext context, int clrInstanceID)
{
......@@ -157,6 +165,8 @@ public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemCon
foreach (var bulkTypeTrace in traceProcess.EventsInProcess.ByEventType<GCBulkTypeTraceData>())
{
s_bulkTypeEvents++;
if (bulkTypeTrace.ClrInstanceID != _clrInstanceID)
continue;
......@@ -164,6 +174,7 @@ public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemCon
{
TypeHandleInfo currentInfo;
var typeTrace = bulkTypeTrace.Values(i);
s_bulkTypeTypes++;
if (_types.TryGetValue((long)typeTrace.TypeID, out currentInfo))
{
......@@ -242,6 +253,13 @@ public TraceRuntimeDescToTypeSystemDesc(TraceProcess traceProcess, TypeSystemCon
}
}
}
// Fill in all the types
foreach (var entry in _types)
{
ResolveTypeHandle(entry.Key, false);
}
}
public ModuleDesc ResolveModuleID(long handle, bool throwIfNotFound = true)
......
......@@ -16,10 +16,11 @@
using Microsoft.Diagnostics.Tracing.Parsers.Clr;
using System.Reflection.Metadata;
using ILCompiler.Reflection.ReadyToRun;
namespace Microsoft.Diagnostics.Tools.Pgo
{
class TraceTypeSystemContext : MetadataTypeSystemContext, IMetadataStringDecoderProvider
class TraceTypeSystemContext : MetadataTypeSystemContext, IMetadataStringDecoderProvider, IAssemblyResolver
{
private readonly PgoTraceProcess _pgoTraceProcess;
private readonly ModuleLoadLogger _moduleLoadLogger;
......@@ -359,5 +360,22 @@ public MetadataStringDecoder GetMetadataStringDecoder()
_metadataStringDecoder = new CachingMetadataStringDecoder(0x10000); // TODO: Tune the size
return _metadataStringDecoder;
}
MetadataReader IAssemblyResolver.FindAssembly(MetadataReader metadataReader, AssemblyReferenceHandle assemblyReferenceHandle, string parentFile)
{
return ((EcmaAssembly)this.GetModuleForSimpleName(metadataReader.GetString(metadataReader.GetAssemblyReference(assemblyReferenceHandle).Name), false)).MetadataReader;
}
MetadataReader IAssemblyResolver.FindAssembly(string simpleName, string parentFile)
{
return ((EcmaAssembly)this.GetModuleForSimpleName(simpleName, false)).MetadataReader;
}
bool IAssemblyResolver.Naked => false;
bool IAssemblyResolver.SignatureBinary => false;
bool IAssemblyResolver.InlineSignatureBinary => false;
}
}
......@@ -158,7 +158,7 @@ public EntityHandle GetTypeRef(MetadataType type)
else
{
var typeSpecSignature = new BlobBuilder();
EncodeType(typeSpecSignature, type);
EncodeType(typeSpecSignature, type, EmbeddedSignatureDataEmitter.EmptySingleton);
var blobSigHandle = _metadataBuilder.GetOrAddBlob(typeSpecSignature);
typeHandle = _metadataBuilder.AddTypeSpecification(blobSigHandle);
}
......@@ -183,7 +183,7 @@ public EntityHandle GetMethodRef(MethodDesc method)
BlobEncoder methodSpecEncoder = new BlobEncoder(methodSpecSig);
methodSpecEncoder.MethodSpecificationSignature(method.Instantiation.Length);
foreach (var type in method.Instantiation)
EncodeType(methodSpecSig, type);
EncodeType(methodSpecSig, type, EmbeddedSignatureDataEmitter.EmptySingleton);
var methodSpecSigHandle = _metadataBuilder.GetOrAddBlob(methodSpecSig);
methodHandle = _metadataBuilder.AddMethodSpecification(uninstantiatedHandle, methodSpecSigHandle);
......@@ -194,8 +194,21 @@ public EntityHandle GetMethodRef(MethodDesc method)
StringHandle methodName = _metadataBuilder.GetOrAddString(method.Name);
var sig = method.GetTypicalMethodDefinition().Signature;
EmbeddedSignatureDataEmitter signatureDataEmitter;
if (sig.HasEmbeddedSignatureData)
{
signatureDataEmitter = new EmbeddedSignatureDataEmitter(sig.GetEmbeddedSignatureData(), this);
}
else
{
signatureDataEmitter = EmbeddedSignatureDataEmitter.EmptySingleton;
}
BlobBuilder memberRefSig = new BlobBuilder();
EncodeMethodSignature(memberRefSig, sig);
EncodeMethodSignature(memberRefSig, sig, signatureDataEmitter);
if (!signatureDataEmitter.Complete)
throw new ArgumentException();
var sigBlob = _metadataBuilder.GetOrAddBlob(memberRefSig);
methodHandle = _metadataBuilder.AddMemberReference(typeHandle, methodName, sigBlob);
......@@ -205,8 +218,14 @@ public EntityHandle GetMethodRef(MethodDesc method)
return methodHandle;
}
private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
private void EncodeType(BlobBuilder blobBuilder, TypeDesc type, EmbeddedSignatureDataEmitter signatureDataEmitter)
{
signatureDataEmitter.Push();
signatureDataEmitter.Push();
signatureDataEmitter.EmitAtCurrentIndexStack(blobBuilder);
signatureDataEmitter.Pop();
signatureDataEmitter.Push();
if (type.IsPrimitive)
{
SignatureTypeCode primitiveCode;
......@@ -266,13 +285,13 @@ private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
else if (type.IsSzArray)
{
blobBuilder.WriteByte((byte)SignatureTypeCode.SZArray);
EncodeType(blobBuilder, type.GetParameterType());
EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter);
}
else if (type.IsArray)
{
var arrayType = (ArrayType)type;
blobBuilder.WriteByte((byte)SignatureTypeCode.Array);
EncodeType(blobBuilder, type.GetParameterType());
EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter);
var shapeEncoder = new ArrayShapeEncoder(blobBuilder);
// TODO Add support for non-standard array shapes
shapeEncoder.Shape(arrayType.Rank, default(ImmutableArray<int>), default(ImmutableArray<int>));
......@@ -280,17 +299,17 @@ private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
else if (type.IsPointer)
{
blobBuilder.WriteByte((byte)SignatureTypeCode.Pointer);
EncodeType(blobBuilder, type.GetParameterType());
EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter);
}
else if (type.IsFunctionPointer)
{
FunctionPointerType fnptrType = (FunctionPointerType)type;
EncodeMethodSignature(blobBuilder, fnptrType.Signature);
EncodeMethodSignature(blobBuilder, fnptrType.Signature, signatureDataEmitter);
}
else if (type.IsByRef)
{
blobBuilder.WriteByte((byte)SignatureTypeCode.ByReference);
EncodeType(blobBuilder, type.GetParameterType());
EncodeType(blobBuilder, type.GetParameterType(), signatureDataEmitter);
}
else if (type.IsObject)
{
......@@ -318,10 +337,10 @@ private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
else if (type is InstantiatedType)
{
blobBuilder.WriteByte((byte)SignatureTypeCode.GenericTypeInstance);
EncodeType(blobBuilder, type.GetTypeDefinition());
EncodeType(blobBuilder, type.GetTypeDefinition(), signatureDataEmitter);
blobBuilder.WriteCompressedInteger(type.Instantiation.Length);
foreach (var instantiationArg in type.Instantiation)
EncodeType(blobBuilder, instantiationArg);
EncodeType(blobBuilder, instantiationArg, signatureDataEmitter);
}
else if (type is MetadataType)
{
......@@ -335,10 +354,96 @@ private void EncodeType(BlobBuilder blobBuilder, TypeDesc type)
{
throw new Exception("Unexpected type");
}
signatureDataEmitter.Pop();
signatureDataEmitter.Pop();
}
void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature sig)
class EmbeddedSignatureDataEmitter
{
EmbeddedSignatureData[] _embeddedData;
int _embeddedDataIndex;
Stack<int> _indexStack = new Stack<int>();
TypeSystemMetadataEmitter _metadataEmitter;
public static EmbeddedSignatureDataEmitter EmptySingleton = new EmbeddedSignatureDataEmitter(null, null);
public EmbeddedSignatureDataEmitter(EmbeddedSignatureData[] embeddedData, TypeSystemMetadataEmitter metadataEmitter)
{
_embeddedData = embeddedData;
_indexStack.Push(0);
_metadataEmitter = metadataEmitter;
}
public void Push()
{
if (!Complete)
{
int was = _indexStack.Pop();
_indexStack.Push(was + 1);
_indexStack.Push(0);
}
}
public void EmitAtCurrentIndexStack(BlobBuilder signatureBuilder)
{
if (!Complete)
{
if (_embeddedDataIndex < _embeddedData.Length)
{
string indexData = string.Join(".", _indexStack);
while ((_embeddedDataIndex < _embeddedData.Length) && _embeddedData[_embeddedDataIndex].index == indexData)
{
switch (_embeddedData[_embeddedDataIndex].kind)
{
case EmbeddedSignatureDataKind.OptionalCustomModifier:
{
signatureBuilder.WriteByte((byte)SignatureTypeCode.OptionalModifier);
EntityHandle handle = _metadataEmitter.GetTypeRef((MetadataType)_embeddedData[_embeddedDataIndex].type);
signatureBuilder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(handle));
}
break;
case EmbeddedSignatureDataKind.RequiredCustomModifier:
{
signatureBuilder.WriteByte((byte)SignatureTypeCode.RequiredModifier);
EntityHandle handle = _metadataEmitter.GetTypeRef((MetadataType)_embeddedData[_embeddedDataIndex].type);
signatureBuilder.WriteCompressedInteger(CodedIndex.TypeDefOrRefOrSpec(handle));
}
break;
default:
throw new NotImplementedException();
}
_embeddedDataIndex++;
}
}
}
}
public bool Complete
{
get
{
if (_embeddedData == null)
return true;
return _embeddedDataIndex >= _embeddedData.Length;
}
}
public void Pop()
{
if (!Complete)
{
_indexStack.Pop();
}
}
}
void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature sig, EmbeddedSignatureDataEmitter signatureDataEmitter)
{
signatureDataEmitter.Push();
BlobEncoder signatureEncoder = new BlobEncoder(signatureBuilder);
int genericParameterCount = sig.GenericParameterCount;
bool isInstanceMethod = !sig.IsStatic;
......@@ -362,9 +467,11 @@ void EncodeMethodSignature(BlobBuilder signatureBuilder, MethodSignature sig)
signatureEncoder.MethodSignature(sigCallingConvention, genericParameterCount, isInstanceMethod);
signatureBuilder.WriteCompressedInteger(sig.Length);
// TODO Process custom modifiers in some way
EncodeType(signatureBuilder, sig.ReturnType);
EncodeType(signatureBuilder, sig.ReturnType, signatureDataEmitter);
for (int i = 0; i < sig.Length; i++)
EncodeType(signatureBuilder, sig[i]);
EncodeType(signatureBuilder, sig[i], signatureDataEmitter);
signatureDataEmitter.Pop();
}
public UserStringHandle GetUserStringHandle(string userString)
......
......@@ -14,6 +14,7 @@
<ItemGroup>
<ProjectReference Include="../crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj" />
<ProjectReference Include="../crossgen2/ILCompiler.Reflection.ReadyToRun/ILCompiler.Reflection.ReadyToRun.csproj" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.55" />
<PackageReference Include="System.CommandLine" Version="$(SystemCommandLineVersion)" />
<PackageReference Include="System.Reflection.Metadata" Version="1.8.1" />
......

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-pgo", "dotnet-pgo.csproj", "{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.Reflection.ReadyToRun", "..\crossgen2\ILCompiler.Reflection.ReadyToRun\ILCompiler.Reflection.ReadyToRun.csproj", "{ED3FE303-74EB-43D1-BEA1-14484A14B22E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ILCompiler.TypeSystem.ReadyToRun", "..\crossgen2\ILCompiler.TypeSystem.ReadyToRun\ILCompiler.TypeSystem.ReadyToRun.csproj", "{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Checked|Any CPU = Checked|Any CPU
Checked|x64 = Checked|x64
Checked|x86 = Checked|x86
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|Any CPU.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|Any CPU.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|x64.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|x64.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|x86.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Checked|x86.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|x64.ActiveCfg = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|x64.Build.0 = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|x86.ActiveCfg = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Debug|x86.Build.0 = Debug|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|Any CPU.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|x64.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|x64.Build.0 = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|x86.ActiveCfg = Release|Any CPU
{7DA4CC22-F01D-4505-845F-57C06E5C3F9F}.Release|x86.Build.0 = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|Any CPU.ActiveCfg = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|Any CPU.Build.0 = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|x64.ActiveCfg = Release|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|x64.Build.0 = Release|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|x86.ActiveCfg = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Checked|x86.Build.0 = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|x64.ActiveCfg = Debug|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|x64.Build.0 = Debug|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|x86.ActiveCfg = Debug|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Debug|x86.Build.0 = Debug|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|Any CPU.Build.0 = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|x64.ActiveCfg = Release|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|x64.Build.0 = Release|x64
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|x86.ActiveCfg = Release|Any CPU
{ED3FE303-74EB-43D1-BEA1-14484A14B22E}.Release|x86.Build.0 = Release|Any CPU
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|Any CPU.ActiveCfg = Checked|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|x64.ActiveCfg = Checked|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|x64.Build.0 = Checked|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|x86.ActiveCfg = Checked|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Checked|x86.Build.0 = Checked|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|Any CPU.ActiveCfg = Debug|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|x64.ActiveCfg = Debug|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|x64.Build.0 = Debug|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|x86.ActiveCfg = Debug|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Debug|x86.Build.0 = Debug|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|Any CPU.ActiveCfg = Release|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|x64.ActiveCfg = Release|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|x64.Build.0 = Release|x64
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|x86.ActiveCfg = Release|x86
{D6BA6C4F-F7DF-4414-94BE-8E124CDDDEE6}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EEBDF807-A078-4F0D-A7F3-A6386B6C0A68}
EndGlobalSection
EndGlobal
......@@ -6286,7 +6286,7 @@ VOID ETW::MethodLog::SendMethodDetailsEvent(MethodDesc *pMethodDesc)
BulkTypeEventLogger typeLogger;
ULONGLONG typeID = (ULONGLONG)pMethodDesc->GetMethodTable_NoLogging();
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(&typeLogger, typeID, ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(&typeLogger, typeID, ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLog);
ULONGLONG loaderModuleID = (ULONGLONG)pMethodDesc->GetLoaderModule();
StackSArray<ULONGLONG> rgTypeParameters;
......@@ -6312,7 +6312,7 @@ VOID ETW::MethodLog::SendMethodDetailsEvent(MethodDesc *pMethodDesc)
// Log any referenced parameter types
for (COUNT_T i=0; i < cParams; i++)
{
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(&typeLogger, rgTypeParameters[i], ETW::TypeSystemLog::kTypeLogBehaviorTakeLockAndLogIfFirstTime);
ETW::TypeSystemLog::LogTypeAndParametersIfNecessary(&typeLogger, rgTypeParameters[i], ETW::TypeSystemLog::kTypeLogBehaviorAlwaysLog);
}
typeLogger.FireBulkTypeEvent();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册