Skip to content

Commit 9684485

Browse files
authored
Disassembler realiability fixes (#2234)
* re-enable the failing tests * add a fix * translate only jump and call instruction addresses, allows for avoiding AV
1 parent 82f03f4 commit 9684485

File tree

4 files changed

+43
-5
lines changed

4 files changed

+43
-5
lines changed

src/BenchmarkDotNet/Disassemblers/Arm64Disassembler.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ protected override IEnumerable<Asm> Decode(byte[] code, ulong startAddress, Stat
206206
// is at an address one memory page higher than the code.
207207
byte[] buffer = new byte[12];
208208

209+
FlushCachedDataIfNeeded(state.Runtime.DataTarget.DataReader, address, buffer);
210+
209211
if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length)
210212
{
211213
if (buffer.SequenceEqual(callCountingStubTemplate))

src/BenchmarkDotNet/Disassemblers/ClrMdV2Disassembler.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@
77
using System.IO;
88
using System.Linq;
99
using System.Text.RegularExpressions;
10+
using BenchmarkDotNet.Portability;
1011

1112
namespace BenchmarkDotNet.Disassemblers
1213
{
1314
// This Disassembler uses ClrMd v2x. Please keep it in sync with ClrMdV1Disassembler (if possible).
1415
internal abstract class ClrMdV2Disassembler
1516
{
17+
// Translating an address to a method can cause AV and a process crash (https://github.com/dotnet/BenchmarkDotNet/issues/2070).
18+
// It was fixed in https://github.com/dotnet/runtime/pull/79846,
19+
// and most likely will be backported to 7.0.2 very soon (https://github.com/dotnet/runtime/pull/79862).
20+
protected static readonly bool IsVulnerableToAvInDac = !RuntimeInformation.IsWindows() && Environment.Version < new Version(7, 0, 2);
21+
1622
internal DisassemblyResult AttachAndDisassemble(Settings settings)
1723
{
1824
using (var dataTarget = DataTarget.AttachToProcess(
@@ -270,7 +276,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
270276

271277
if (method is null)
272278
{
273-
if (isAddressPrecodeMD || this is not Arm64Disassembler)
279+
if (isAddressPrecodeMD || !IsVulnerableToAvInDac)
274280
{
275281
var methodDescriptor = runtime.GetMethodByHandle(address);
276282
if (!(methodDescriptor is null))
@@ -287,7 +293,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
287293
}
288294
}
289295

290-
if (this is not Arm64Disassembler)
296+
if (!IsVulnerableToAvInDac)
291297
{
292298
var methodTableName = runtime.DacLibrary.SOSDacInterface.GetMethodTableName(address);
293299
if (!string.IsNullOrEmpty(methodTableName))
@@ -311,6 +317,22 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
311317
state.AddressToNameMapping.Add(address, methodName);
312318
}
313319

320+
protected void FlushCachedDataIfNeeded(IDataReader dataTargetDataReader, ulong address, byte[] buffer)
321+
{
322+
if (!RuntimeInformation.IsWindows())
323+
{
324+
if (dataTargetDataReader.Read(address, buffer) <= 0)
325+
{
326+
// We don't suspend the benchmark process for the time of disassembling,
327+
// as it would require sudo privileges.
328+
// Because of that, the Tiered JIT thread might still re-compile some methods
329+
// in the meantime when the host process it trying to disassemble the code.
330+
// In such case, Tiered JIT thread might create stubs which requires flushing of the cached data.
331+
dataTargetDataReader.FlushCachedData();
332+
}
333+
}
334+
}
335+
314336
// GetMethodByInstructionPointer sometimes returns wrong methods.
315337
// In case given address does not belong to the methods range, null is returned.
316338
private static ClrMethod WorkaroundGetMethodByInstructionPointerBug(ClrRuntime runtime, ClrMethod method, ulong newAddress)

src/BenchmarkDotNet/Disassemblers/IntelDisassembler.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ protected override IEnumerable<Asm> Decode(byte[] code, ulong startAddress, Stat
5151
// is at an address one memory page higher than the code.
5252
byte[] buffer = new byte[10];
5353

54+
FlushCachedDataIfNeeded(state.Runtime.DataTarget.DataReader, address, buffer);
55+
5456
if (state.Runtime.DataTarget.DataReader.Read(address, buffer) == buffer.Length && buffer.SequenceEqual(callCountingStubTemplate))
5557
{
5658
const ulong TargetMethodAddressSlotOffset = 8;
@@ -82,7 +84,10 @@ protected override IEnumerable<Asm> Decode(byte[] code, ulong startAddress, Stat
8284

8385
if (address > ushort.MaxValue)
8486
{
85-
TryTranslateAddressToName(address, isPrestubMD, state, isIndirect, depth, currentMethod);
87+
if (!IsVulnerableToAvInDac || IsCallOrJump(instruction))
88+
{
89+
TryTranslateAddressToName(address, isPrestubMD, state, isIndirect, depth, currentMethod);
90+
}
8691
}
8792
}
8893

@@ -97,6 +102,17 @@ protected override IEnumerable<Asm> Decode(byte[] code, ulong startAddress, Stat
97102
}
98103
}
99104

105+
private static bool IsCallOrJump(Instruction instruction)
106+
=> instruction.FlowControl switch
107+
{
108+
FlowControl.Call => true,
109+
FlowControl.IndirectCall => true,
110+
FlowControl.ConditionalBranch => true,
111+
FlowControl.IndirectBranch => true,
112+
FlowControl.UnconditionalBranch => true,
113+
_ => false
114+
};
115+
100116
private static bool TryGetReferencedAddress(Instruction instruction, uint pointerSize, out ulong referencedAddress)
101117
{
102118
for (int i = 0; i < instruction.OpCount; i++)

tests/BenchmarkDotNet.IntegrationTests/DisassemblyDiagnoserTests.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ public void Recursive()
8383
public void CanDisassembleAllMethodCalls(Jit jit, Platform platform, Runtime runtime)
8484
{
8585
if (RuntimeInformation.IsMacOS()) return; // currently not supported
86-
if (RuntimeInformation.IsLinux()) return; // https://github.com/dotnet/BenchmarkDotNet/issues/2223
8786

8887
var disassemblyDiagnoser = new DisassemblyDiagnoser(
8988
new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3));
@@ -150,7 +149,6 @@ [Benchmark] public void JustReturn() { }
150149
public void CanDisassembleInlinableBenchmarks(Jit jit, Platform platform, Runtime runtime)
151150
{
152151
if (RuntimeInformation.IsMacOS()) return; // currently not supported
153-
if (RuntimeInformation.IsLinux()) return; // https://github.com/dotnet/BenchmarkDotNet/issues/2223
154152

155153
var disassemblyDiagnoser = new DisassemblyDiagnoser(
156154
new DisassemblyDiagnoserConfig(printSource: true, maxDepth: 3));

0 commit comments

Comments
 (0)