1111
1212namespace 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 )
0 commit comments