12
12
13
13
namespace BenchmarkDotNet . Disassemblers
14
14
{
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
+
17
18
{
18
19
private static readonly ulong MinValidAddress = GetMinValidAddress ( ) ;
19
20
@@ -65,7 +66,7 @@ internal DisassemblyResult AttachAndDisassemble(Settings settings)
65
66
state . Todo . Enqueue (
66
67
new MethodInfo (
67
68
// 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 ) ,
69
70
0 ) ) ;
70
71
}
71
72
@@ -150,9 +151,10 @@ private DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State state,
150
151
151
152
if ( ! CanBeDisassembled ( method ) )
152
153
{
153
- if ( method . IsPInvoke )
154
+ if ( method . Attributes . HasFlag ( System . Reflection . MethodAttributes . PinvokeImpl ) )
154
155
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 )
156
158
return CreateEmpty ( method , "Extern method" ) ;
157
159
if ( method . CompilationType == MethodCompilationType . None )
158
160
return CreateEmpty ( method , "Method was not JITted yet." ) ;
@@ -215,60 +217,30 @@ private IEnumerable<Asm> Decode(ILToNativeMap map, State state, int depth, ClrMe
215
217
216
218
private static ILToNativeMap [ ] GetCompleteNativeMap ( ClrMethod method , ClrRuntime runtime )
217
219
{
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 )
219
225
{
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
+ } ;
222
233
}
223
234
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?
227
237
. 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
- } )
235
238
. 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 ;
250
239
}
251
240
252
241
private static DisassembledMethod CreateEmpty ( ClrMethod method , string reason )
253
242
=> DisassembledMethod . Empty ( method . Signature , method . NativeCode , reason ) ;
254
243
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
-
272
244
protected void TryTranslateAddressToName ( ulong address , bool isAddressPrecodeMD , State state , int depth , ClrMethod currentMethod )
273
245
{
274
246
if ( ! IsValidAddress ( address ) || state . AddressToNameMapping . ContainsKey ( address ) )
@@ -284,18 +256,10 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
284
256
}
285
257
286
258
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 ) )
297
261
{
298
- method = WorkaroundGetMethodByInstructionPointerBug ( runtime , method , address ) ;
262
+ method = runtime . GetMethodByInstructionPointer ( newAddress ) ;
299
263
}
300
264
301
265
if ( method is null )
@@ -314,7 +278,7 @@ protected void TryTranslateAddressToName(ulong address, bool isAddressPrecodeMD,
314
278
return ;
315
279
}
316
280
317
- var methodTableName = runtime . DacLibrary . SOSDacInterface . GetMethodTableName ( address ) ;
281
+ var methodTableName = runtime . GetTypeByMethodTable ( address ) ? . Name ;
318
282
if ( ! string . IsNullOrEmpty ( methodTableName ) )
319
283
{
320
284
state . AddressToNameMapping . Add ( address , $ "MT_{ methodTableName } ") ;
@@ -350,13 +314,6 @@ protected void FlushCachedDataIfNeeded(IDataReader dataTargetDataReader, ulong a
350
314
}
351
315
}
352
316
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
-
360
317
private class SharpComparer : IEqualityComparer < Sharp >
361
318
{
362
319
public bool Equals ( Sharp x , Sharp y )
0 commit comments