diff --git a/src/BenchmarkDotNet/BenchmarkDotNet.csproj b/src/BenchmarkDotNet/BenchmarkDotNet.csproj
index 547c50b6e5..e4235ebd57 100644
--- a/src/BenchmarkDotNet/BenchmarkDotNet.csproj
+++ b/src/BenchmarkDotNet/BenchmarkDotNet.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs b/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs
index 16ebbde219..ce5176c4ca 100644
--- a/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs
+++ b/src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs
@@ -139,7 +139,7 @@ public void Feed(Arm64Instruction instruction)
public Arm64RegisterId RegisterId { get { return _registerId; } }
}
- internal class Arm64Disassembler : ClrMdV2Disassembler
+ internal class Arm64Disassembler : ClrMdV3Disassembler
{
internal sealed class RuntimeSpecificData
{
diff --git a/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs b/src/BenchmarkDotNet/Disassemblers/ClrMdV3Disassembler.cs
similarity index 77%
rename from src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs
rename to src/BenchmarkDotNet/Disassemblers/ClrMdV3Disassembler.cs
index aff7d276d6..6bd01426d8 100644
--- a/src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs
+++ b/src/BenchmarkDotNet/Disassemblers/ClrMdV3Disassembler.cs
@@ -12,8 +12,9 @@
namespace BenchmarkDotNet.Disassemblers
{
- // This Disassembler uses ClrMd v2x. Please keep it in sync with ClrMdV1Disassembler (if possible).
- internal abstract class ClrMdV2Disassembler
+ // This Disassembler uses ClrMd v3x. Please keep it in sync with ClrMdV1Disassembler (if possible).
+ internal abstract class ClrMdV3Disassembler
+
{
private static readonly ulong MinValidAddress = GetMinValidAddress();
@@ -65,7 +66,7 @@ internal DisassemblyResult AttachAndDisassemble(Settings settings)
state.Todo.Enqueue(
new MethodInfo(
// the Disassembler Entry Method is always parameterless, so check by name is enough
- typeWithBenchmark.Methods.Single(method => method.IsPublic && method.Name == settings.MethodName),
+ typeWithBenchmark.Methods.Single(method => method.Attributes.HasFlag(System.Reflection.MethodAttributes.Public) && method.Name == settings.MethodName),
0));
}
@@ -150,9 +151,10 @@ private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state,
if (!CanBeDisassembled(method))
{
- if (method.IsPInvoke)
+ if (method.Attributes.HasFlag(System.Reflection.MethodAttributes.PinvokeImpl))
return CreateEmpty(method, "PInvoke method");
- if (method.IL is null || method.IL.Length == 0)
+ var ilInfo = method.GetILInfo();
+ if (ilInfo is null || ilInfo.Length == 0)
return CreateEmpty(method, "Extern method");
if (method.CompilationType == MethodCompilationType.None)
return CreateEmpty(method, "Method was not JITted yet.");
@@ -215,60 +217,30 @@ private IEnumerable Decode(ILToNativeMap map, State state, int depth, ClrMe
private static ILToNativeMap[] GetCompleteNativeMap(ClrMethod method, ClrRuntime runtime)
{
- if (!TryReadNativeCodeAddresses(runtime, method, out ulong startAddress, out ulong endAddress))
+ // it's better to use one single map rather than few small ones
+ // it's simply easier to get next instruction when decoding ;)
+
+ var hotColdInfo = method.HotColdInfo;
+ if (hotColdInfo.HotSize > 0 && hotColdInfo.HotStart > 0)
{
- startAddress = method.NativeCode;
- endAddress = ulong.MaxValue;
+ return hotColdInfo.ColdSize <= 0
+ ? new[] { new ILToNativeMap() { StartAddress = hotColdInfo.HotStart, EndAddress = hotColdInfo.HotStart + hotColdInfo.HotSize, ILOffset = -1 } }
+ : new[]
+ {
+ new ILToNativeMap() { StartAddress = hotColdInfo.HotStart, EndAddress = hotColdInfo.HotStart + hotColdInfo.HotSize, ILOffset = -1 },
+ new ILToNativeMap() { StartAddress = hotColdInfo.ColdStart, EndAddress = hotColdInfo.ColdStart + hotColdInfo.ColdSize, ILOffset = -1 }
+ };
}
- ILToNativeMap[] sortedMaps = method.ILOffsetMap // CanBeDisassembled ensures that there is at least one map in ILOffsetMap
- .Where(map => map.StartAddress >= startAddress && map.StartAddress < endAddress) // can be false for Tier 0 maps, EndAddress is not checked on purpose here
- .Where(map => map.StartAddress < map.EndAddress) // some maps have 0 length (they don't have corresponding assembly code?)
+ return method.ILOffsetMap
+ .Where(map => map.StartAddress < map.EndAddress) // some maps have 0 length?
.OrderBy(map => map.StartAddress) // we need to print in the machine code order, not IL! #536
- .Select(map => new ILToNativeMap()
- {
- StartAddress = map.StartAddress,
- // some maps have EndAddress > codeHeaderData.MethodStart + codeHeaderData.MethodSize and contain garbage (#2074). They need to be fixed!
- EndAddress = Math.Min(map.EndAddress, endAddress),
- ILOffset = map.ILOffset
- })
.ToArray();
-
- if (sortedMaps.Length == 0)
- {
- // In such situation ILOffsetMap most likely describes Tier 0, while CodeHeaderData Tier 1.
- // Since we care about Tier 1 (if it's present), we "fake" a Tier 1 map.
- return new[] { new ILToNativeMap() { StartAddress = startAddress, EndAddress = endAddress } };
- }
- else if (sortedMaps[0].StartAddress != startAddress || (sortedMaps[sortedMaps.Length - 1].EndAddress != endAddress && endAddress != ulong.MaxValue))
- {
- // In such situation ILOffsetMap most likely is missing few bytes. We just "extend" it to avoid producing "bad" instructions.
- return new[] { new ILToNativeMap() { StartAddress = startAddress, EndAddress = endAddress } };
- }
-
- return sortedMaps;
}
private static DisassembledMethod CreateEmpty(ClrMethod method, string reason)
=> DisassembledMethod.Empty(method.Signature, method.NativeCode, reason);
- protected static bool TryReadNativeCodeAddresses(ClrRuntime runtime, ClrMethod method, out ulong startAddress, out ulong endAddress)
- {
- if (method is not null
- && runtime.DacLibrary.SOSDacInterface.GetCodeHeaderData(method.NativeCode, out var codeHeaderData) == HResult.S_OK
- && codeHeaderData.MethodSize > 0) // false for extern methods!
- {
- // HotSize can be missing or be invalid (https://github.com/microsoft/clrmd/issues/1036).
- // So we fetch the method size on our own.
- startAddress = codeHeaderData.MethodStart;
- endAddress = codeHeaderData.MethodStart + codeHeaderData.MethodSize;
- return true;
- }
-
- startAddress = endAddress = 0;
- return false;
- }
-
protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, State state, int depth, ClrMethod currentMethod)
{
if (!IsValidAddress(address) || state.AddressToNameMapping.ContainsKey(address))
@@ -284,18 +256,10 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
}
var method = runtime.GetMethodByInstructionPointer(address);
- if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0)
- {
- if (runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress))
- {
- method = runtime.GetMethodByInstructionPointer(newAddress);
-
- method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, newAddress);
- }
- }
- else
+ if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0
+ && runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress))
{
- method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, address);
+ method = runtime.GetMethodByInstructionPointer(newAddress);
}
if (method is null)
@@ -314,7 +278,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
return;
}
- var methodTableName = runtime.DacLibrary.SOSDacInterface.GetMethodTableName(address);
+ var methodTableName = runtime.GetTypeByMethodTable(address)?.Name;
if (!string.IsNullOrEmpty(methodTableName))
{
state.AddressToNameMapping.Add(address, $"MT_{methodTableName}");
@@ -350,13 +314,6 @@ protected void FlushCachedDataIfNeeded(IDataReader dataTargetDataReader, ulong a
}
}
- // GetMethodByInstructionPointer sometimes returns wrong methods.
- // In case given address does not belong to the methods range, null is returned.
- private static ClrMethod WorkaroundGetMethodByInstructionPointerBug(ClrRuntime runtime, ClrMethod method, ulong newAddress)
- => TryReadNativeCodeAddresses(runtime, method, out ulong startAddress, out ulong endAddress) && !(startAddress >= newAddress && newAddress <= endAddress)
- ? null
- : method;
-
private class SharpComparer : IEqualityComparer
{
public bool Equals(Sharp x, Sharp y)
diff --git a/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs b/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs
index f29faae037..3fd541528a 100644
--- a/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs
+++ b/src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs
@@ -7,7 +7,7 @@
namespace BenchmarkDotNet.Disassemblers
{
- internal class IntelDisassembler : ClrMdV2Disassembler
+ internal class IntelDisassembler : ClrMdV3Disassembler
{
internal sealed class RuntimeSpecificData
{
diff --git a/src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs b/src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs
index cf810cdd86..8ce026a5dd 100644
--- a/src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs
+++ b/src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs
@@ -8,16 +8,16 @@ namespace BenchmarkDotNet.Disassemblers
internal class SameArchitectureDisassembler
{
private readonly DisassemblyDiagnoserConfig config;
- private ClrMdV2Disassembler? clrMdV2Disassembler;
+ private ClrMdV3Disassembler? clrMdV3Disassembler;
internal SameArchitectureDisassembler(DisassemblyDiagnoserConfig config) => this.config = config;
internal DisassemblyResult Disassemble(DiagnoserActionParameters parameters)
// delay the creation to avoid exceptions at DisassemblyDiagnoser ctor
- => (clrMdV2Disassembler ??= CreateDisassemblerForCurrentArchitecture())
+ => (clrMdV3Disassembler ??= CreateDisassemblerForCurrentArchitecture())
.AttachAndDisassemble(BuildDisassemblerSettings(parameters));
- private static ClrMdV2Disassembler CreateDisassemblerForCurrentArchitecture()
+ private static ClrMdV3Disassembler CreateDisassemblerForCurrentArchitecture()
=> RuntimeInformation.GetCurrentPlatform() switch
{
Platform.X86 or Platform.X64 => new IntelDisassembler(),