Skip to content

Commit bc3abf9

Browse files
authored
Update clrmd to 3.1 (#2488)
1 parent ca5dfdf commit bc3abf9

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
@@ -18,7 +18,7 @@
1818
<PackageReference Include="CommandLineParser" Version="2.9.1" />
1919
<PackageReference Include="Gee.External.Capstone" Version="2.3.0" />
2020
<PackageReference Include="Iced" Version="1.17.0" />
21-
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="2.2.332302" />
21+
<PackageReference Include="Microsoft.Diagnostics.Runtime" Version="3.1.512801" />
2222
<PackageReference Include="Perfolizer" Version="[0.4.0]" />
2323
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.8" PrivateAssets="contentfiles;analyzers" />
2424
<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
@@ -12,8 +12,9 @@
1212

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

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

@@ -150,9 +151,10 @@ private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state,
150151

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

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

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

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

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

286258
var method = runtime.GetMethodByInstructionPointer(address);
287-
if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0)
288-
{
289-
if (runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress))
290-
{
291-
method = runtime.GetMethodByInstructionPointer(newAddress);
292-
293-
method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, newAddress);
294-
}
295-
}
296-
else
259+
if (method is null && (address & ((uint) runtime.DataTarget.DataReader.PointerSize - 1)) == 0
260+
&& runtime.DataTarget.DataReader.ReadPointer(address, out ulong newAddress) && IsValidAddress(newAddress))
297261
{
298-
method = WorkaroundGetMethodByInstructionPointerBug(runtime, method, address);
262+
method = runtime.GetMethodByInstructionPointer(newAddress);
299263
}
300264

301265
if (method is null)
@@ -314,7 +278,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
314278
return;
315279
}
316280

317-
var methodTableName = runtime.DacLibrary.SOSDacInterface.GetMethodTableName(address);
281+
var methodTableName = runtime.GetTypeByMethodTable(address)?.Name;
318282
if (!string.IsNullOrEmpty(methodTableName))
319283
{
320284
state.AddressToNameMapping.Add(address, $"MT_{methodTableName}");
@@ -350,13 +314,6 @@ protected void FlushCachedDataIfNeeded(IDataReader dataTargetDataReader, ulong a
350314
}
351315
}
352316

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