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