1
1
using BenchmarkDotNet . Filters ;
2
2
using Iced . Intel ;
3
3
using Microsoft . Diagnostics . Runtime ;
4
+ using Microsoft . Diagnostics . Runtime . Utilities ;
4
5
using System ;
5
6
using System . Collections . Generic ;
7
+ using System . IO ;
6
8
using System . Linq ;
7
9
using System . Text . RegularExpressions ;
8
10
@@ -66,16 +68,29 @@ private static void FilterAndEnqueue(State state, Settings settings)
66
68
67
69
foreach ( ClrModule module in state . Runtime . EnumerateModules ( ) )
68
70
foreach ( ClrType type in module . EnumerateTypeDefToMethodTableMap ( ) . Select ( map => state . Runtime . GetTypeByMethodTable ( map . MethodTable ) ) . Where ( type => type is not null ) )
69
- foreach ( ClrMethod method in type . Methods . Where ( method => CanBeDisassembled ( method ) && method . Signature != null ) )
70
- foreach ( Regex filter in filters )
71
+ foreach ( ClrMethod method in type . Methods . Where ( method => method . Signature != null ) )
72
+ {
73
+ if ( method . NativeCode > 0 )
71
74
{
72
- if ( filter . IsMatch ( method . Signature ) )
75
+ if ( ! state . AddressToNameMapping . TryGetValue ( method . NativeCode , out _ ) )
73
76
{
74
- state . Todo . Enqueue ( new MethodInfo ( method ,
75
- depth : settings . MaxDepth ) ) ; // don't allow for recursive disassembling
76
- break ;
77
+ state . AddressToNameMapping . Add ( method . NativeCode , method . Signature ) ;
77
78
}
78
79
}
80
+
81
+ if ( CanBeDisassembled ( method ) )
82
+ {
83
+ foreach ( Regex filter in filters )
84
+ {
85
+ if ( filter . IsMatch ( method . Signature ) )
86
+ {
87
+ state . Todo . Enqueue ( new MethodInfo ( method ,
88
+ depth : settings . MaxDepth ) ) ; // don't allow for recursive disassembling
89
+ break ;
90
+ }
91
+ }
92
+ }
93
+ }
79
94
}
80
95
81
96
private static DisassembledMethod [ ] Disassemble ( Settings settings , State state )
@@ -96,8 +111,7 @@ private static DisassembledMethod[] Disassemble(Settings settings, State state)
96
111
return result . ToArray ( ) ;
97
112
}
98
113
99
- private static bool CanBeDisassembled ( ClrMethod method )
100
- => ! ( method . ILOffsetMap . Length == 0 && ( method . HotColdInfo . HotStart == 0 || method . HotColdInfo . HotSize == 0 ) ) ;
114
+ private static bool CanBeDisassembled ( ClrMethod method ) => method . ILOffsetMap . Length > 0 && method . NativeCode > 0 ;
101
115
102
116
private static DisassembledMethod DisassembleMethod ( MethodInfo methodInfo , State state , Settings settings )
103
117
{
@@ -128,9 +142,10 @@ private static DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State
128
142
codes . AddRange ( uniqueSourceCodeLines ) ;
129
143
}
130
144
131
- // for getting ASM we try to use data from HotColdInfo if available (better for decoding)
132
- foreach ( var map in GetCompleteNativeMap ( method ) )
133
- codes . AddRange ( Decode ( map . StartAddress , ( uint ) ( map . EndAddress - map . StartAddress ) , state , methodInfo . Depth , method ) ) ;
145
+ foreach ( var map in GetCompleteNativeMap ( method , state . Runtime ) )
146
+ {
147
+ codes . AddRange ( Decode ( map , state , methodInfo . Depth , method ) ) ;
148
+ }
134
149
135
150
Map [ ] maps = settings . PrintSource
136
151
? codes . GroupBy ( code => code . InstructionPointer ) . OrderBy ( group => group . Key ) . Select ( group => new Map ( ) { SourceCodes = group . ToArray ( ) } ) . ToArray ( )
@@ -144,14 +159,25 @@ private static DisassembledMethod DisassembleMethod(MethodInfo methodInfo, State
144
159
} ;
145
160
}
146
161
147
- private static IEnumerable < Asm > Decode ( ulong startAddress , uint size , State state , int depth , ClrMethod currentMethod )
162
+ private static IEnumerable < Asm > Decode ( ILToNativeMap map , State state , int depth , ClrMethod currentMethod )
148
163
{
164
+ ulong startAddress = map . StartAddress ;
165
+ uint size = ( uint ) ( map . EndAddress - map . StartAddress ) ;
166
+
149
167
byte [ ] code = new byte [ size ] ;
150
- int bytesRead = state . Runtime . DataTarget . DataReader . Read ( startAddress , code ) ;
151
- if ( bytesRead == 0 || bytesRead != size )
152
- yield break ;
153
168
154
- var reader = new ByteArrayCodeReader ( code , 0 , bytesRead ) ;
169
+ int totalBytesRead = 0 ;
170
+ do
171
+ {
172
+ int bytesRead = state . Runtime . DataTarget . DataReader . Read ( startAddress + ( ulong ) totalBytesRead , new Span < byte > ( code , totalBytesRead , ( int ) size - totalBytesRead ) ) ;
173
+ if ( bytesRead <= 0 )
174
+ {
175
+ throw new EndOfStreamException ( $ "Tried to read { size } bytes for { currentMethod . Signature } , got only { totalBytesRead } ") ;
176
+ }
177
+ totalBytesRead += bytesRead ;
178
+ } while ( totalBytesRead != size ) ;
179
+
180
+ var reader = new ByteArrayCodeReader ( code , 0 , ( int ) size ) ;
155
181
var decoder = Decoder . Create ( state . Runtime . DataTarget . DataReader . PointerSize * 8 , reader ) ;
156
182
decoder . IP = startAddress ;
157
183
@@ -204,7 +230,15 @@ private static void TryTranslateAddressToName(Instruction instruction, State sta
204
230
if ( method is null && ( address & ( ( uint ) runtime . DataTarget . DataReader . PointerSize - 1 ) ) == 0 )
205
231
{
206
232
if ( runtime . DataTarget . DataReader . ReadPointer ( address , out ulong newAddress ) && newAddress > ushort . MaxValue )
233
+ {
207
234
method = runtime . GetMethodByInstructionPointer ( newAddress ) ;
235
+
236
+ method = WorkaroundGetMethodByInstructionPointerBug ( runtime , method , newAddress ) ;
237
+ }
238
+ }
239
+ else
240
+ {
241
+ method = WorkaroundGetMethodByInstructionPointerBug ( runtime , method , address ) ;
208
242
}
209
243
210
244
if ( method is null )
@@ -222,6 +256,13 @@ private static void TryTranslateAddressToName(Instruction instruction, State sta
222
256
state . AddressToNameMapping . Add ( address , methodName ) ;
223
257
}
224
258
259
+ // GetMethodByInstructionPointer sometimes returns wrong methods.
260
+ // In case given address does not belong to the methods range, null is returned.
261
+ private static ClrMethod WorkaroundGetMethodByInstructionPointerBug ( ClrRuntime runtime , ClrMethod method , ulong newAddress )
262
+ => TryReadNativeCodeAddresses ( runtime , method , out ulong startAddress , out ulong endAddress ) && ! ( startAddress >= newAddress && newAddress <= endAddress )
263
+ ? null
264
+ : method ;
265
+
225
266
internal static bool TryGetReferencedAddress ( Instruction instruction , uint pointerSize , out ulong referencedAddress )
226
267
{
227
268
for ( int i = 0 ; i < instruction . OpCount ; i ++ )
@@ -255,26 +296,52 @@ internal static bool TryGetReferencedAddress(Instruction instruction, uint point
255
296
return false ;
256
297
}
257
298
258
- private static ILToNativeMap [ ] GetCompleteNativeMap ( ClrMethod method )
299
+ private static ILToNativeMap [ ] GetCompleteNativeMap ( ClrMethod method , ClrRuntime runtime )
300
+ {
301
+ if ( ! TryReadNativeCodeAddresses ( runtime , method , out ulong startAddress , out ulong endAddress ) )
302
+ {
303
+ startAddress = method . NativeCode ;
304
+ endAddress = ulong . MaxValue ;
305
+ }
306
+
307
+ ILToNativeMap [ ] sortedMaps = method . ILOffsetMap // CanBeDisassembled ensures that there is at least one map in ILOffsetMap
308
+ . Where ( map => map . StartAddress >= startAddress && map . StartAddress < endAddress ) // can be false for Tier 0 maps, EndAddress is not checked on purpose here
309
+ . Where ( map => map . StartAddress < map . EndAddress ) // some maps have 0 length (they don't have corresponding assembly code?)
310
+ . OrderBy ( map => map . StartAddress ) // we need to print in the machine code order, not IL! #536
311
+ . Select ( map => new ILToNativeMap ( )
312
+ {
313
+ StartAddress = map . StartAddress ,
314
+ // some maps have EndAddress > codeHeaderData.MethodStart + codeHeaderData.MethodSize and contain garbage (#2074). They need to be fixed!
315
+ EndAddress = Math . Min ( map . EndAddress , endAddress ) ,
316
+ ILOffset = map . ILOffset
317
+ } )
318
+ . ToArray ( ) ;
319
+
320
+ if ( sortedMaps . Length == 0 )
321
+ {
322
+ // In such situation ILOffsetMap most likely describes Tier 0, while CodeHeaderData Tier 1.
323
+ // Since we care about Tier 1 (if it's present), we "fake" a Tier 1 map.
324
+ return new [ ] { new ILToNativeMap ( ) { StartAddress = startAddress , EndAddress = endAddress } } ;
325
+ }
326
+
327
+ return sortedMaps ;
328
+ }
329
+
330
+ private static bool TryReadNativeCodeAddresses ( ClrRuntime runtime , ClrMethod method , out ulong startAddress , out ulong endAddress )
259
331
{
260
- // it's better to use one single map rather than few small ones
261
- // it's simply easier to get next instruction when decoding ;)
262
- var hotColdInfo = method . HotColdInfo ;
263
- if ( hotColdInfo . HotSize > 0 && hotColdInfo . HotStart > 0 )
332
+ if ( method is not null
333
+ && runtime . DacLibrary . SOSDacInterface . GetCodeHeaderData ( method . NativeCode , out var codeHeaderData ) == HResult . S_OK
334
+ && codeHeaderData . MethodSize > 0 ) // false for extern methods!
264
335
{
265
- return hotColdInfo . ColdSize <= 0
266
- ? new [ ] { new ILToNativeMap ( ) { StartAddress = hotColdInfo . HotStart , EndAddress = hotColdInfo . HotStart + hotColdInfo . HotSize , ILOffset = - 1 } }
267
- : new [ ]
268
- {
269
- new ILToNativeMap ( ) { StartAddress = hotColdInfo . HotStart , EndAddress = hotColdInfo . HotStart + hotColdInfo . HotSize , ILOffset = - 1 } ,
270
- new ILToNativeMap ( ) { StartAddress = hotColdInfo . ColdStart , EndAddress = hotColdInfo . ColdStart + hotColdInfo . ColdSize , ILOffset = - 1 }
271
- } ;
336
+ // HotSize can be missing or be invalid (https://github.com/microsoft/clrmd/issues/1036).
337
+ // So we fetch the method size on our own.
338
+ startAddress = codeHeaderData . MethodStart ;
339
+ endAddress = codeHeaderData . MethodStart + codeHeaderData . MethodSize ;
340
+ return true ;
272
341
}
273
342
274
- return method . ILOffsetMap
275
- . Where ( map => map . StartAddress < map . EndAddress ) // some maps have 0 length?
276
- . OrderBy ( map => map . StartAddress ) // we need to print in the machine code order, not IL! #536
277
- . ToArray ( ) ;
343
+ startAddress = endAddress = 0 ;
344
+ return false ;
278
345
}
279
346
280
347
private static DisassembledMethod CreateEmpty ( ClrMethod method , string reason )
0 commit comments