Skip to content

Commit 2c511ed

Browse files
committed
Update clrmd to 3.1
1 parent e6fdc6b commit 2c511ed

File tree

5 files changed

+31
-74
lines changed

5 files changed

+31
-74
lines changed

src/BenchmarkDotNet/BenchmarkDotNet.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<PackageReference Include="CommandLineParser" Version="2.9.1" />
2020
<PackageReference Include="Gee.External.Capstone" Version="2.3.0" />
2121
<PackageReference Include="Iced" Version="1.17.0" />
22-
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.2.332302" />
22+
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="3.1.456101" />
2323
<PackageReference Include="Perfolizer" Version="[0.2.1]" />
2424
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.0.2" PrivateAssets="contentfiles;analyzers" />
2525
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />

src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ public void Feed(Arm64Instruction instruction)
139139
public Arm64RegisterId RegisterId { get { return _registerId; } }
140140
}
141141

142-
internal class Arm64Disassembler : ClrMdV2Disassembler
142+
internal class Arm64Disassembler : ClrMdV3Disassembler
143143
{
144144
internal sealed class RuntimeSpecificData
145145
{

src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs renamed to src/BenchmarkDotNet/Disassemblers/ClrMdV3Disassembler.cs

Lines changed: 25 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111

1212
namespace BenchmarkDotNet.Disassemblers
1313
{
14-
// This Disassembler uses ClrMd v2x. Please keep it in sync with ClrMdV1Disassembler (if possible).
15-
internal abstract class ClrMdV2Disassembler
14+
// This Disassembler uses ClrMd v3x. Please keep it in sync with ClrMdV1Disassembler (if possible).
15+
internal abstract class ClrMdV3Disassembler
16+
1617
{
1718
private static readonly ulong MinValidAddress = GetMinValidAddress();
1819

@@ -64,7 +65,7 @@ internal DisassemblyResult AttachAndDisassemble(Settings settings)
6465
state.Todo.Enqueue(
6566
new MethodInfo(
6667
// the Disassembler Entry Method is always parameterless, so check by name is enough
67-
typeWithBenchmark.Methods.Single(method => method.IsPublic && method.Name == settings.MethodName),
68+
typeWithBenchmark.Methods.Single(method => method.Attributes.HasFlag(System.Reflection.MethodAttributes.Public) && method.Name == settings.MethodName),
6869
0));
6970
}
7071

@@ -149,9 +150,10 @@ private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state,
149150

150151
if (!CanBeDisassembled(method))
151152
{
152-
if (method.IsPInvoke)
153+
if (method.Attributes.HasFlag(System.Reflection.MethodAttributes.PinvokeImpl))
153154
return CreateEmpty(method, "PInvoke method");
154-
if (method.IL is null || method.IL.Length == 0)
155+
var ilInfo = method.GetILInfo();
156+
if (ilInfo is null || ilInfo.Length == 0)
155157
return CreateEmpty(method, "Extern method");
156158
if (method.CompilationType == MethodCompilationType.None)
157159
return CreateEmpty(method, "Method was not JITted yet.");
@@ -214,60 +216,30 @@ private IEnumerable<Asm> Decode(ILToNativeMap map, State state, int depth, ClrMe
214216

215217
private static ILToNativeMap[] GetCompleteNativeMap(ClrMethod method, ClrRuntime runtime)
216218
{
217-
if (!TryReadNativeCodeAddresses(runtime, method, out ulong startAddress, out ulong endAddress))
219+
// it's better to use one single map rather than few small ones
220+
// it's simply easier to get next instruction when decoding ;)
221+
222+
var hotColdInfo = method.HotColdInfo;
223+
if (hotColdInfo.HotSize > 0 && hotColdInfo.HotStart > 0)
218224
{
219-
startAddress = method.NativeCode;
220-
endAddress = ulong.MaxValue;
225+
return hotColdInfo.ColdSize <= 0
226+
? new[] { new ILToNativeMap() { StartAddress = hotColdInfo.HotStart, EndAddress = hotColdInfo.HotStart + hotColdInfo.HotSize, ILOffset = -1 } }
227+
: new[]
228+
{
229+
new ILToNativeMap() { StartAddress = hotColdInfo.HotStart, EndAddress = hotColdInfo.HotStart + hotColdInfo.HotSize, ILOffset = -1 },
230+
new ILToNativeMap() { StartAddress = hotColdInfo.ColdStart, EndAddress = hotColdInfo.ColdStart + hotColdInfo.ColdSize, ILOffset = -1 }
231+
};
221232
}
222233

223-
ILToNativeMap[] sortedMaps = method.ILOffsetMap // CanBeDisassembled ensures that there is at least one map in ILOffsetMap
224-
.Where(map => map.StartAddress >= startAddress && map.StartAddress < endAddress) // can be false for Tier 0 maps, EndAddress is not checked on purpose here
225-
.Where(map => map.StartAddress < map.EndAddress) // some maps have 0 length (they don't have corresponding assembly code?)
234+
return method.ILOffsetMap
235+
.Where(map => map.StartAddress < map.EndAddress) // some maps have 0 length?
226236
.OrderBy(map => map.StartAddress) // we need to print in the machine code order, not IL! #536
227-
.Select(map => new ILToNativeMap()
228-
{
229-
StartAddress = map.StartAddress,
230-
// some maps have EndAddress > codeHeaderData.MethodStart + codeHeaderData.MethodSize and contain garbage (#2074). They need to be fixed!
231-
EndAddress = Math.Min(map.EndAddress, endAddress),
232-
ILOffset = map.ILOffset
233-
})
234237
.ToArray();
235-
236-
if (sortedMaps.Length == 0)
237-
{
238-
// In such situation ILOffsetMap most likely describes Tier 0, while CodeHeaderData Tier 1.
239-
// Since we care about Tier 1 (if it's present), we "fake" a Tier 1 map.
240-
return new[] { new ILToNativeMap() { StartAddress = startAddress, EndAddress = endAddress } };
241-
}
242-
else if (sortedMaps[0].StartAddress != startAddress || (sortedMaps[sortedMaps.Length - 1].EndAddress != endAddress && endAddress != ulong.MaxValue))
243-
{
244-
// In such situation ILOffsetMap most likely is missing few bytes. We just "extend" it to avoid producing "bad" instructions.
245-
return new[] { new ILToNativeMap() { StartAddress = startAddress, EndAddress = endAddress } };
246-
}
247-
248-
return sortedMaps;
249238
}
250239

251240
private static DisassembledMethod CreateEmpty(ClrMethod method, string reason)
252241
=> DisassembledMethod.Empty(method.Signature, method.NativeCode, reason);
253242

254-
protected static bool TryReadNativeCodeAddresses(ClrRuntime runtime, ClrMethod method, out ulong startAddress, out ulong endAddress)
255-
{
256-
if (method is not null
257-
&& runtime.DacLibrary.SOSDacInterface.GetCodeHeaderData(method.NativeCode, out var codeHeaderData) == HResult.S_OK
258-
&& codeHeaderData.MethodSize > 0) // false for extern methods!
259-
{
260-
// HotSize can be missing or be invalid (https://github.com/microsoft/clrmd/issues/1036).
261-
// So we fetch the method size on our own.
262-
startAddress = codeHeaderData.MethodStart;
263-
endAddress = codeHeaderData.MethodStart + codeHeaderData.MethodSize;
264-
return true;
265-
}
266-
267-
startAddress = endAddress = 0;
268-
return false;
269-
}
270-
271243
protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD, State state, int depth, ClrMethod currentMethod)
272244
{
273245
if (!IsValidAddress(address) || state.AddressToNameMapping.ContainsKey(address))
@@ -283,18 +255,10 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
283255
}
284256

285257
var method = runtime.GetMethodByInstructionPointer(address);
286-
if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0)
287-
{
288-
if (runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress))
289-
{
290-
method = runtime.GetMethodByInstructionPointer(newAddress);
291-
292-
method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, newAddress);
293-
}
294-
}
295-
else
258+
if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0
259+
&& runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress))
296260
{
297-
method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, address);
261+
method = runtime.GetMethodByInstructionPointer(newAddress);
298262
}
299263

300264
if (method is null)
@@ -313,7 +277,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
313277
return;
314278
}
315279

316-
var methodTableName = runtime.DacLibrary.SOSDacInterface.GetMethodTableName(address);
280+
var methodTableName = runtime.GetTypeByMethodTable(address)?.Name;
317281
if (!string.IsNullOrEmpty(methodTableName))
318282
{
319283
state.AddressToNameMapping.Add(address, $"MT_{methodTableName}");
@@ -349,13 +313,6 @@ protected void FlushCachedDataIfNeeded(IDataReader dataTargetDataReader, ulong a
349313
}
350314
}
351315

352-
// GetMethodByInstructionPointer sometimes returns wrong methods.
353-
// In case given address does not belong to the methods range, null is returned.
354-
private static ClrMethod WorkaroundGetMethodByInstructionPointerBug(ClrRuntime runtime, ClrMethod method, ulong newAddress)
355-
=> TryReadNativeCodeAddresses(runtime, method, out ulong startAddress, out ulong endAddress) && !(startAddress >= newAddress && newAddress <= endAddress)
356-
? null
357-
: method;
358-
359316
private class SharpComparer : IEqualityComparer<Sharp>
360317
{
361318
public bool Equals(Sharp x, Sharp y)

src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace BenchmarkDotNet.Disassemblers
99
{
10-
internal class IntelDisassembler : ClrMdV2Disassembler
10+
internal class IntelDisassembler : ClrMdV3Disassembler
1111
{
1212
internal sealed class RuntimeSpecificData
1313
{

src/BenchmarkDotNet/Disassemblers/SameArchitectureDisassembler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ namespace BenchmarkDotNet.Disassemblers
88
internal class SameArchitectureDisassembler
99
{
1010
private readonly DisassemblyDiagnoserConfig config;
11-
private ClrMdV2Disassembler? clrMdV2Disassembler;
11+
private ClrMdV3Disassembler? clrMdV3Disassembler;
1212

1313
internal SameArchitectureDisassembler(DisassemblyDiagnoserConfig config) => this.config = config;
1414

1515
internal DisassemblyResult Disassemble(DiagnoserActionParameters parameters)
1616
// delay the creation to avoid exceptions at DisassemblyDiagnoser ctor
17-
=> (clrMdV2Disassembler ??= CreateDisassemblerForCurrentArchitecture())
17+
=> (clrMdV3Disassembler ??= CreateDisassemblerForCurrentArchitecture())
1818
.AttachAndDisassemble(BuildDisassemblerSettings(parameters));
1919

20-
private static ClrMdV2Disassembler CreateDisassemblerForCurrentArchitecture()
20+
private static ClrMdV3Disassembler CreateDisassemblerForCurrentArchitecture()
2121
=> RuntimeInformation.GetCurrentPlatform() switch
2222
{
2323
Platform.X86 or Platform.X64 => new IntelDisassembler(),

0 commit comments

Comments
 (0)