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