1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | //***************************************************************************** |
5 | |
6 | // |
7 | // File: DebuggerModule.cpp |
8 | // |
9 | // Stuff for tracking DebuggerModules. |
10 | // |
11 | //***************************************************************************** |
12 | |
13 | #include "stdafx.h" |
14 | #include "../inc/common.h" |
15 | #include "perflog.h" |
16 | #include "eeconfig.h" // This is here even for retail & free builds... |
17 | #include "vars.hpp" |
18 | #include <limits.h> |
19 | #include "ilformatter.h" |
20 | #include "debuginfostore.h" |
21 | #include "../../vm/methoditer.h" |
22 | |
23 | #ifndef DACCESS_COMPILE |
24 | |
25 | bool DbgIsSpecialILOffset(DWORD offset) |
26 | { |
27 | LIMITED_METHOD_CONTRACT; |
28 | |
29 | return (offset == (ULONG) ICorDebugInfo::PROLOG || |
30 | offset == (ULONG) ICorDebugInfo::EPILOG || |
31 | offset == (ULONG) ICorDebugInfo::NO_MAPPING); |
32 | } |
33 | |
34 | // Helper to use w/ the debug stores. |
35 | BYTE* InteropSafeNew(void * , size_t cBytes) |
36 | { |
37 | BYTE * p = new (interopsafe, nothrow) BYTE[cBytes]; |
38 | return p; |
39 | } |
40 | |
41 | |
42 | // |
43 | // This is only fur internal debugging. |
44 | // |
45 | #ifdef LOGGING |
46 | static void _dumpVarNativeInfo(ICorDebugInfo::NativeVarInfo* vni) |
47 | { |
48 | WRAPPER_NO_CONTRACT; |
49 | |
50 | LOG((LF_CORDB, LL_INFO1000000, "Var %02d: 0x%04x-0x%04x vlt=" , |
51 | vni->varNumber, |
52 | vni->startOffset, vni->endOffset, |
53 | vni->loc.vlType)); |
54 | |
55 | switch (vni->loc.vlType) |
56 | { |
57 | case ICorDebugInfo::VLT_REG: |
58 | LOG((LF_CORDB, LL_INFO1000000, "REG reg=%d\n" , vni->loc.vlReg.vlrReg)); |
59 | break; |
60 | |
61 | case ICorDebugInfo::VLT_REG_BYREF: |
62 | LOG((LF_CORDB, LL_INFO1000000, "REG_BYREF reg=%d\n" , vni->loc.vlReg.vlrReg)); |
63 | break; |
64 | |
65 | case ICorDebugInfo::VLT_STK: |
66 | LOG((LF_CORDB, LL_INFO1000000, "STK reg=%d off=0x%04x (%d)\n" , |
67 | vni->loc.vlStk.vlsBaseReg, |
68 | vni->loc.vlStk.vlsOffset, |
69 | vni->loc.vlStk.vlsOffset)); |
70 | break; |
71 | |
72 | case ICorDebugInfo::VLT_STK_BYREF: |
73 | LOG((LF_CORDB, LL_INFO1000000, "STK_BYREF reg=%d off=0x%04x (%d)\n" , |
74 | vni->loc.vlStk.vlsBaseReg, |
75 | vni->loc.vlStk.vlsOffset, |
76 | vni->loc.vlStk.vlsOffset)); |
77 | break; |
78 | |
79 | case ICorDebugInfo::VLT_REG_REG: |
80 | LOG((LF_CORDB, LL_INFO1000000, "REG_REG reg1=%d reg2=%d\n" , |
81 | vni->loc.vlRegReg.vlrrReg1, |
82 | vni->loc.vlRegReg.vlrrReg2)); |
83 | break; |
84 | |
85 | case ICorDebugInfo::VLT_REG_STK: |
86 | LOG((LF_CORDB, LL_INFO1000000, "REG_STK reg=%d basereg=%d off=0x%04x (%d)\n" , |
87 | vni->loc.vlRegStk.vlrsReg, |
88 | vni->loc.vlRegStk.vlrsStk.vlrssBaseReg, |
89 | vni->loc.vlRegStk.vlrsStk.vlrssOffset, |
90 | vni->loc.vlRegStk.vlrsStk.vlrssOffset)); |
91 | break; |
92 | |
93 | case ICorDebugInfo::VLT_STK_REG: |
94 | LOG((LF_CORDB, LL_INFO1000000, "STK_REG basereg=%d off=0x%04x (%d) reg=%d\n" , |
95 | vni->loc.vlStkReg.vlsrStk.vlsrsBaseReg, |
96 | vni->loc.vlStkReg.vlsrStk.vlsrsOffset, |
97 | vni->loc.vlStkReg.vlsrStk.vlsrsOffset, |
98 | vni->loc.vlStkReg.vlsrReg)); |
99 | break; |
100 | |
101 | case ICorDebugInfo::VLT_STK2: |
102 | LOG((LF_CORDB, LL_INFO1000000, "STK_STK reg=%d off=0x%04x (%d)\n" , |
103 | vni->loc.vlStk2.vls2BaseReg, |
104 | vni->loc.vlStk2.vls2Offset, |
105 | vni->loc.vlStk2.vls2Offset)); |
106 | break; |
107 | |
108 | case ICorDebugInfo::VLT_FPSTK: |
109 | LOG((LF_CORDB, LL_INFO1000000, "FPSTK reg=%d\n" , |
110 | vni->loc.vlFPstk.vlfReg)); |
111 | break; |
112 | |
113 | case ICorDebugInfo::VLT_FIXED_VA: |
114 | LOG((LF_CORDB, LL_INFO1000000, "FIXED_VA offset=%d (%d)\n" , |
115 | vni->loc.vlFixedVarArg.vlfvOffset, |
116 | vni->loc.vlFixedVarArg.vlfvOffset)); |
117 | break; |
118 | |
119 | |
120 | default: |
121 | LOG((LF_CORDB, LL_INFO1000000, "???\n" )); |
122 | break; |
123 | } |
124 | } |
125 | #endif |
126 | |
127 | #if defined(WIN64EXCEPTIONS) |
128 | void DebuggerJitInfo::InitFuncletAddress() |
129 | { |
130 | CONTRACTL |
131 | { |
132 | SO_INTOLERANT; |
133 | NOTHROW; |
134 | GC_NOTRIGGER; |
135 | } |
136 | CONTRACTL_END; |
137 | |
138 | m_funcletCount = (int)g_pEEInterface->GetFuncletStartOffsets((const BYTE*)m_addrOfCode, NULL, 0); |
139 | |
140 | if (m_funcletCount == 0) |
141 | { |
142 | _ASSERTE(m_rgFunclet == NULL); |
143 | return; |
144 | } |
145 | |
146 | m_rgFunclet = (DWORD*)(new (interopsafe, nothrow) DWORD[m_funcletCount]); |
147 | |
148 | // All bets are off for stepping this method. |
149 | if (m_rgFunclet == NULL) |
150 | { |
151 | m_funcletCount = 0; |
152 | return; |
153 | } |
154 | |
155 | // This will get the offsets relative to the parent method start as if |
156 | // the funclet was in contiguous memory (i.e. not hot/cold split). |
157 | g_pEEInterface->GetFuncletStartOffsets((const BYTE*)m_addrOfCode, m_rgFunclet, m_funcletCount); |
158 | } |
159 | |
160 | // |
161 | // DebuggerJitInfo::GetFuncletOffsetByIndex() |
162 | // |
163 | // Given a funclet index, return its starting offset. |
164 | // |
165 | // parameters: index - index of the funclet |
166 | // |
167 | // return value: starting offset of the specified funclet, or -1 if the index is invalid |
168 | // |
169 | DWORD DebuggerJitInfo::GetFuncletOffsetByIndex(int index) |
170 | { |
171 | LIMITED_METHOD_CONTRACT; |
172 | |
173 | if (index < 0 || index >= m_funcletCount) |
174 | { |
175 | return (-1); |
176 | } |
177 | |
178 | return m_rgFunclet[index]; |
179 | } |
180 | |
181 | // |
182 | // DebuggerJitInfo::GetFuncletIndex() |
183 | // |
184 | // Given an offset or an absolute address, return the index of the funclet containing it. |
185 | // |
186 | // parameters: offsetOrAddr - an offset or an absolute address in the method |
187 | // mode - whether the first argument is an offset or an absolute address |
188 | // |
189 | // return value: the index of the funclet containing the specified offset or address, |
190 | // or -1 if it's invalid |
191 | // |
192 | int DebuggerJitInfo::GetFuncletIndex(CORDB_ADDRESS offsetOrAddr, GetFuncletIndexMode mode) |
193 | { |
194 | WRAPPER_NO_CONTRACT; |
195 | |
196 | DWORD offset = 0; |
197 | if (mode == GFIM_BYOFFSET) |
198 | { |
199 | offset = (DWORD)offsetOrAddr; |
200 | } |
201 | |
202 | // If the address doesn't fall in any of the funclets (or if the |
203 | // method doesn't have any funclet at all), then return PARENT_METHOD_INDEX. |
204 | // <TODO> |
205 | // What if there's an overflow? |
206 | // </TODO> |
207 | if (!m_codeRegionInfo.IsMethodAddress((const BYTE *)(mode == GFIM_BYOFFSET ? (size_t)m_codeRegionInfo.OffsetToAddress(offset) : offsetOrAddr))) |
208 | { |
209 | return PARENT_METHOD_INDEX; |
210 | } |
211 | |
212 | if ( ( m_funcletCount == 0 ) || |
213 | ( (mode == GFIM_BYOFFSET) && (offset < m_rgFunclet[0]) ) || |
214 | ( (mode == GFIM_BYADDRESS) && (offsetOrAddr < (size_t)m_codeRegionInfo.OffsetToAddress(m_rgFunclet[0])) ) ) |
215 | { |
216 | return PARENT_METHOD_INDEX; |
217 | } |
218 | |
219 | for (int i = 0; i < m_funcletCount; i++) |
220 | { |
221 | if (i == (m_funcletCount - 1)) |
222 | { |
223 | return i; |
224 | } |
225 | else if ( ( (mode == GFIM_BYOFFSET) && (offset < m_rgFunclet[i+1]) ) || |
226 | ( (mode == GFIM_BYADDRESS) && (offsetOrAddr < (size_t)m_codeRegionInfo.OffsetToAddress(m_rgFunclet[i+1])) ) ) |
227 | { |
228 | return i; |
229 | } |
230 | } |
231 | |
232 | UNREACHABLE(); |
233 | } |
234 | |
235 | #endif // WIN64EXCEPTIONS |
236 | |
237 | // It is entirely possible that we have multiple sequence points for the |
238 | // same IL offset (because of funclets, optimization, etc.). Just to be |
239 | // uniform in all cases, let's return the sequence point with the smallest |
240 | // native offset if fWantFirst is TRUE. |
241 | #if defined(WIN64EXCEPTIONS) |
242 | #define ADJUST_MAP_ENTRY(_map, _wantFirst) \ |
243 | if ((_wantFirst)) \ |
244 | for ( ; (_map) > m_sequenceMap && (((_map)-1)->ilOffset == (_map)->ilOffset); (_map)--); \ |
245 | else \ |
246 | for ( ; (_map) < m_sequenceMap + (m_sequenceMapCount-1) && (((_map)+1)->ilOffset == (_map)->ilOffset); (_map)++); |
247 | #else |
248 | #define ADJUST_MAP_ENTRY(_map, _wantFirst) |
249 | #endif // _WIN64 |
250 | |
251 | DebuggerJitInfo::DebuggerJitInfo(DebuggerMethodInfo *minfo, MethodDesc *fd) : |
252 | m_fd(fd), |
253 | m_pLoaderModule(fd->GetLoaderModule()), |
254 | m_jitComplete(false), |
255 | #ifdef EnC_SUPPORTED |
256 | m_encBreakpointsApplied(false), |
257 | #endif //EnC_SUPPORTED |
258 | m_methodInfo(minfo), |
259 | m_addrOfCode(NULL), |
260 | m_sizeOfCode(0), m_prevJitInfo(NULL), m_nextJitInfo(NULL), |
261 | m_lastIL(0), |
262 | m_sequenceMap(NULL), |
263 | m_sequenceMapCount(0), |
264 | m_callsiteMap(NULL), |
265 | m_callsiteMapCount(0), |
266 | m_sequenceMapSorted(false), |
267 | m_varNativeInfo(NULL), m_varNativeInfoCount(0), |
268 | m_fAttemptInit(false) |
269 | #if defined(WIN64EXCEPTIONS) |
270 | ,m_rgFunclet(NULL) |
271 | , m_funcletCount(0) |
272 | #endif // defined(WIN64EXCEPTIONS) |
273 | { |
274 | WRAPPER_NO_CONTRACT; |
275 | |
276 | // A DJI is just the debugger's cache of interesting information + |
277 | // various debugger-specific state for a method (like Enc). |
278 | // So only be createing DJIs when a debugger is actually attached. |
279 | // The profiler also piggy-backs on the DJIs. |
280 | // @Todo - the managed stackwalker in the BCL also builds on DJIs. |
281 | //_ASSERTE(CORDebuggerAttached() || CORProfilerPresent()); |
282 | |
283 | _ASSERTE(minfo); |
284 | m_encVersion = minfo->GetCurrentEnCVersion(); |
285 | _ASSERTE(m_encVersion >= CorDB_DEFAULT_ENC_FUNCTION_VERSION); |
286 | LOG((LF_CORDB,LL_EVERYTHING, "DJI::DJI : created at 0x%x\n" , this)); |
287 | |
288 | // Debugger doesn't track LightWeight codegen methods. |
289 | // We should never even be creating a DJI for one. |
290 | _ASSERTE(!m_fd->IsDynamicMethod()); |
291 | } |
292 | |
293 | DebuggerILToNativeMap *DebuggerJitInfo::MapILOffsetToMapEntry(SIZE_T offset, BOOL *exact, BOOL fWantFirst) |
294 | { |
295 | CONTRACTL |
296 | { |
297 | NOTHROW; |
298 | GC_NOTRIGGER; |
299 | MODE_ANY; |
300 | CAN_TAKE_LOCK; // GetSequenceMapCount calls LazyInitBounds() which can eventually |
301 | // call ExecutionManager::IncrementReader |
302 | } |
303 | CONTRACTL_END; |
304 | |
305 | // Ideally we should be able to assert this, since the binary search in this function |
306 | // assumes that the sequence points are sorted by IL offset (NO_MAPPING, PROLOG, and EPILOG |
307 | // are actually -1, -2, and -3, respectively). However, the sequence points in pdb's use |
308 | // -1 to mean "end of the method", which is different from our semantics of using 0. |
309 | // _ASSERTE(offset != NO_MAPPING && offset != PROLOG && offset != EPILOG); |
310 | |
311 | // |
312 | // Binary search for matching map element. |
313 | // |
314 | |
315 | DebuggerILToNativeMap *mMin = GetSequenceMap(); |
316 | DebuggerILToNativeMap *mMax = mMin + GetSequenceMapCount(); |
317 | |
318 | _ASSERTE(m_sequenceMapSorted); |
319 | _ASSERTE( mMin < mMax ); //otherwise we have no code |
320 | |
321 | if (exact) |
322 | { |
323 | *exact = FALSE; |
324 | } |
325 | |
326 | if (mMin) |
327 | { |
328 | while (mMin + 1 < mMax) |
329 | { |
330 | _ASSERTE(mMin>=m_sequenceMap); |
331 | DebuggerILToNativeMap *mMid = mMin + ((mMax - mMin)>>1); |
332 | _ASSERTE(mMid>=m_sequenceMap); |
333 | |
334 | if (offset == mMid->ilOffset) |
335 | { |
336 | if (exact) |
337 | { |
338 | *exact = TRUE; |
339 | } |
340 | ADJUST_MAP_ENTRY(mMid, fWantFirst); |
341 | return mMid; |
342 | } |
343 | else if (offset < mMid->ilOffset && mMid->ilOffset != (ULONG) ICorDebugInfo::PROLOG) |
344 | { |
345 | mMax = mMid; |
346 | } |
347 | else |
348 | { |
349 | mMin = mMid; |
350 | } |
351 | } |
352 | |
353 | if (exact && offset == mMin->ilOffset) |
354 | { |
355 | *exact = TRUE; |
356 | } |
357 | ADJUST_MAP_ENTRY(mMin, fWantFirst); |
358 | } |
359 | return mMin; |
360 | } |
361 | |
362 | void DebuggerJitInfo::InitILToNativeOffsetIterator(ILToNativeOffsetIterator &iterator, SIZE_T ilOffset) |
363 | { |
364 | WRAPPER_NO_CONTRACT; |
365 | |
366 | iterator.Init(this, ilOffset); |
367 | } |
368 | |
369 | |
370 | DebuggerJitInfo::NativeOffset DebuggerJitInfo::MapILOffsetToNative(DebuggerJitInfo::ILOffset ilOffset) |
371 | { |
372 | CONTRACTL |
373 | { |
374 | SO_NOT_MAINLINE; |
375 | NOTHROW; |
376 | GC_NOTRIGGER; |
377 | } |
378 | CONTRACTL_END; |
379 | |
380 | NativeOffset resultOffset; |
381 | |
382 | DebuggerILToNativeMap *map = MapILOffsetToMapEntry(ilOffset.m_ilOffset, &(resultOffset.m_fExact)); |
383 | |
384 | #if defined(WIN64EXCEPTIONS) |
385 | // See if we want the map entry for the parent. |
386 | if (ilOffset.m_funcletIndex <= PARENT_METHOD_INDEX) |
387 | { |
388 | #endif // _WIN64 |
389 | PREFIX_ASSUME( map != NULL ); |
390 | LOG((LF_CORDB, LL_INFO10000, "DJI::MILOTN: ilOff 0x%x to nat 0x%x exact:0x%x (Entry IL Off:0x%x)\n" , |
391 | ilOffset.m_ilOffset, map->nativeStartOffset, resultOffset.m_fExact, map->ilOffset)); |
392 | |
393 | resultOffset.m_nativeOffset = map->nativeStartOffset; |
394 | |
395 | #if defined(WIN64EXCEPTIONS) |
396 | } |
397 | else |
398 | { |
399 | // funcletIndex is guaranteed to be >= 0 at this point. |
400 | if (ilOffset.m_funcletIndex > (m_funcletCount - 1)) |
401 | { |
402 | resultOffset.m_fExact = FALSE; |
403 | resultOffset.m_nativeOffset = ((SIZE_T)-1); |
404 | } |
405 | else |
406 | { |
407 | // Initialize the funclet range. |
408 | // ASSUMES that funclets are contiguous which they currently are... |
409 | DWORD funcletStartOffset = GetFuncletOffsetByIndex(ilOffset.m_funcletIndex); |
410 | DWORD funcletEndOffset; |
411 | if (ilOffset.m_funcletIndex < (m_funcletCount - 1)) |
412 | { |
413 | funcletEndOffset = GetFuncletOffsetByIndex(ilOffset.m_funcletIndex + 1); |
414 | } |
415 | else |
416 | { |
417 | funcletEndOffset = (DWORD)m_sizeOfCode; |
418 | } |
419 | |
420 | SIZE_T ilTargetOffset = map->ilOffset; |
421 | |
422 | DebuggerILToNativeMap *mapEnd = GetSequenceMap() + GetSequenceMapCount(); |
423 | |
424 | for (; map < mapEnd && map->ilOffset == ilTargetOffset; map++) |
425 | { |
426 | if ((map->nativeStartOffset >= funcletStartOffset) && |
427 | (map->nativeStartOffset < funcletEndOffset)) |
428 | { |
429 | // This is the normal case where the start offset falls in |
430 | // the range of the funclet. |
431 | resultOffset.m_nativeOffset = map->nativeStartOffset; |
432 | break; |
433 | } |
434 | } |
435 | |
436 | if (map == mapEnd || map->ilOffset != ilTargetOffset) |
437 | { |
438 | resultOffset.m_fExact = FALSE; |
439 | resultOffset.m_nativeOffset = ((SIZE_T)-1); |
440 | } |
441 | } |
442 | } |
443 | #endif // WIN64EXCEPTIONS |
444 | |
445 | return resultOffset; |
446 | } |
447 | |
448 | |
449 | DebuggerJitInfo::ILToNativeOffsetIterator::ILToNativeOffsetIterator() |
450 | { |
451 | LIMITED_METHOD_CONTRACT; |
452 | |
453 | m_dji = NULL; |
454 | m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET; |
455 | #ifdef WIN64EXCEPTIONS |
456 | m_currentILOffset.m_funcletIndex = PARENT_METHOD_INDEX; |
457 | #endif |
458 | } |
459 | |
460 | void DebuggerJitInfo::ILToNativeOffsetIterator::Init(DebuggerJitInfo* dji, SIZE_T ilOffset) |
461 | { |
462 | WRAPPER_NO_CONTRACT; |
463 | |
464 | m_dji = dji; |
465 | m_currentILOffset.m_ilOffset = ilOffset; |
466 | #ifdef WIN64EXCEPTIONS |
467 | m_currentILOffset.m_funcletIndex = PARENT_METHOD_INDEX; |
468 | #endif |
469 | |
470 | m_currentNativeOffset = m_dji->MapILOffsetToNative(m_currentILOffset); |
471 | } |
472 | |
473 | bool DebuggerJitInfo::ILToNativeOffsetIterator::IsAtEnd() |
474 | { |
475 | LIMITED_METHOD_CONTRACT; |
476 | |
477 | return (m_currentILOffset.m_ilOffset == INVALID_IL_OFFSET); |
478 | } |
479 | |
480 | SIZE_T DebuggerJitInfo::ILToNativeOffsetIterator::Current(BOOL* pfExact) |
481 | { |
482 | LIMITED_METHOD_CONTRACT; |
483 | |
484 | if (pfExact != NULL) |
485 | { |
486 | *pfExact = m_currentNativeOffset.m_fExact; |
487 | } |
488 | return m_currentNativeOffset.m_nativeOffset; |
489 | } |
490 | |
491 | SIZE_T DebuggerJitInfo::ILToNativeOffsetIterator::CurrentAssertOnlyOne(BOOL* pfExact) |
492 | { |
493 | WRAPPER_NO_CONTRACT; |
494 | |
495 | SIZE_T nativeOffset = Current(pfExact); |
496 | |
497 | Next(); |
498 | _ASSERTE(IsAtEnd()); |
499 | |
500 | return nativeOffset; |
501 | } |
502 | |
503 | void DebuggerJitInfo::ILToNativeOffsetIterator::Next() |
504 | { |
505 | #if defined(WIN64EXCEPTIONS) |
506 | NativeOffset tmpNativeOffset; |
507 | |
508 | for (m_currentILOffset.m_funcletIndex += 1; |
509 | m_currentILOffset.m_funcletIndex < m_dji->GetFuncletCount(); |
510 | m_currentILOffset.m_funcletIndex++) |
511 | { |
512 | tmpNativeOffset = m_dji->MapILOffsetToNative(m_currentILOffset); |
513 | if (tmpNativeOffset.m_nativeOffset != ((SIZE_T)-1) && |
514 | tmpNativeOffset.m_nativeOffset != m_currentNativeOffset.m_nativeOffset) |
515 | { |
516 | m_currentNativeOffset = tmpNativeOffset; |
517 | break; |
518 | } |
519 | } |
520 | |
521 | if (m_currentILOffset.m_funcletIndex == m_dji->GetFuncletCount()) |
522 | { |
523 | m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET; |
524 | } |
525 | #else // !WIN64EXCEPTIONS |
526 | m_currentILOffset.m_ilOffset = INVALID_IL_OFFSET; |
527 | #endif // !WIN64EXCEPTIONS |
528 | } |
529 | |
530 | |
531 | |
532 | // SIZE_T DebuggerJitInfo::MapSpecialToNative(): Maps something like |
533 | // a prolog to a native offset. |
534 | // CordDebugMappingResult mapping: Mapping type to be looking for. |
535 | // SIZE_T which: Which one. <TODO>For now, set to zero. <@todo Later, we'll |
536 | // change this to some value that we get back from MapNativeToILOffset |
537 | // to indicate which of the (possibly multiple epilogs) that may |
538 | // be present.</TODO> |
539 | |
540 | SIZE_T DebuggerJitInfo::MapSpecialToNative(CorDebugMappingResult mapping, |
541 | SIZE_T which, |
542 | BOOL *pfAccurate) |
543 | { |
544 | CONTRACTL |
545 | { |
546 | SO_NOT_MAINLINE; |
547 | NOTHROW; |
548 | GC_NOTRIGGER; |
549 | PRECONDITION(NULL != pfAccurate); |
550 | } |
551 | CONTRACTL_END; |
552 | |
553 | LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN map:0x%x which:0x%x\n" , mapping, which)); |
554 | |
555 | bool fFound; |
556 | SIZE_T cFound = 0; |
557 | |
558 | DebuggerILToNativeMap *m = GetSequenceMap(); |
559 | DebuggerILToNativeMap *mEnd = m + GetSequenceMapCount(); |
560 | if (m) |
561 | { |
562 | while(m < mEnd) |
563 | { |
564 | _ASSERTE(m>=GetSequenceMap()); |
565 | |
566 | fFound = false; |
567 | |
568 | if (DbgIsSpecialILOffset(m->ilOffset)) |
569 | cFound++; |
570 | |
571 | if (cFound == which) |
572 | { |
573 | _ASSERTE( (mapping == MAPPING_PROLOG && |
574 | m->ilOffset == (ULONG) ICorDebugInfo::PROLOG) || |
575 | (mapping == MAPPING_EPILOG && |
576 | m->ilOffset == (ULONG) ICorDebugInfo::EPILOG) || |
577 | ((mapping == MAPPING_NO_INFO || mapping == MAPPING_UNMAPPED_ADDRESS) && |
578 | m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING) |
579 | ); |
580 | |
581 | (*pfAccurate) = TRUE; |
582 | LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN found mapping to nat:0x%x\n" , |
583 | m->nativeStartOffset)); |
584 | return m->nativeStartOffset; |
585 | } |
586 | m++; |
587 | } |
588 | } |
589 | |
590 | LOG((LF_CORDB, LL_INFO10000, "DJI::MSTN No mapping found :(\n" )); |
591 | (*pfAccurate) = FALSE; |
592 | |
593 | return 0; |
594 | } |
595 | |
596 | #if defined(WIN64EXCEPTIONS) |
597 | // |
598 | // DebuggerJitInfo::MapILOffsetToNativeForSetIP() |
599 | // |
600 | // This function maps an IL offset to a native offset, taking into account cloned finallys and nested EH clauses. |
601 | // |
602 | // parameters: offsetILTo - the destination IP, in IL offset |
603 | // funcletIndexFrom - the funclet index of the source IP |
604 | // pEHRT - tree structure for keeping track of EH clause information |
605 | // pExact - pointer for returning whether the mapping is exact or not |
606 | // |
607 | // return value: destination IP, in native offset |
608 | // |
609 | SIZE_T DebuggerJitInfo::MapILOffsetToNativeForSetIP(SIZE_T offsetILTo, int funcletIndexFrom, |
610 | EHRangeTree* pEHRT, BOOL* pExact) |
611 | { |
612 | CONTRACTL |
613 | { |
614 | SO_NOT_MAINLINE; |
615 | NOTHROW; |
616 | GC_NOTRIGGER; |
617 | MODE_ANY; |
618 | } |
619 | CONTRACTL_END; |
620 | |
621 | DebuggerILToNativeMap* pMap = MapILOffsetToMapEntry(offsetILTo, pExact, TRUE); |
622 | DebuggerILToNativeMap* pMapEnd = GetSequenceMap() + GetSequenceMapCount(); |
623 | |
624 | _ASSERTE(pMap == m_sequenceMap || |
625 | (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::NO_MAPPING || |
626 | (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::PROLOG || |
627 | (pMap - 1)->ilOffset == (ULONG)ICorDebugInfo::EPILOG || |
628 | pMap->ilOffset > (pMap - 1)->ilOffset); |
629 | |
630 | SIZE_T offsetNatTo = pMap->nativeStartOffset; |
631 | |
632 | if (m_funcletCount == 0 || |
633 | pEHRT == NULL || |
634 | FAILED(pEHRT->m_hrInit)) |
635 | { |
636 | return offsetNatTo; |
637 | } |
638 | |
639 | // Multiple sequence points may have the same IL offset, which means that the code is duplicated in |
640 | // multiple funclets and/or in the parent method. If the destination offset maps to multiple sequence |
641 | // points (and hence to multiple funclets), we try to find the a sequence point which is in the same |
642 | // funclet as the source sequence point. If we can't find one, then the operation is going to fail |
643 | // anyway, so we just return the first sequence point we find. |
644 | for (DebuggerILToNativeMap* pMapCur = pMap + 1; |
645 | (pMapCur < pMapEnd) && (pMapCur->ilOffset == pMap->ilOffset); |
646 | pMapCur++) |
647 | { |
648 | int funcletIndexTo = GetFuncletIndex(pMapCur->nativeStartOffset, DebuggerJitInfo::GFIM_BYOFFSET); |
649 | if (funcletIndexFrom == funcletIndexTo) |
650 | { |
651 | return pMapCur->nativeStartOffset; |
652 | } |
653 | } |
654 | |
655 | return offsetNatTo; |
656 | } |
657 | #endif // _WIN64 |
658 | |
659 | // void DebuggerJitInfo::MapILRangeToMapEntryRange(): MIRTMER |
660 | // calls MapILOffsetToNative for the startOffset (putting the |
661 | // result into start), and the endOffset (putting the result into end). |
662 | // SIZE_T startOffset: IL offset from beginning of function. |
663 | // SIZE_T endOffset: IL offset from beginngin of function, |
664 | // or zero to indicate that the end of the function should be used. |
665 | // DebuggerILToNativeMap **start: Contains start & end |
666 | // native offsets that correspond to startOffset. Set to NULL if |
667 | // there is no mapping info. |
668 | // DebuggerILToNativeMap **end: Contains start & end native |
669 | // offsets that correspond to endOffset. Set to NULL if there |
670 | // is no mapping info. |
671 | void DebuggerJitInfo::MapILRangeToMapEntryRange(SIZE_T startOffset, |
672 | SIZE_T endOffset, |
673 | DebuggerILToNativeMap **start, |
674 | DebuggerILToNativeMap **end) |
675 | { |
676 | CONTRACTL |
677 | { |
678 | SO_NOT_MAINLINE; |
679 | NOTHROW; |
680 | GC_NOTRIGGER; |
681 | } |
682 | CONTRACTL_END; |
683 | |
684 | LOG((LF_CORDB, LL_INFO1000000, |
685 | "DJI::MIRTMER: IL 0x%04x-0x%04x\n" , |
686 | startOffset, endOffset)); |
687 | |
688 | if (GetSequenceMapCount() == 0) |
689 | { |
690 | *start = NULL; |
691 | *end = NULL; |
692 | return; |
693 | } |
694 | |
695 | *start = MapILOffsetToMapEntry(startOffset); |
696 | |
697 | // |
698 | // end points to the last range that endOffset maps to, not past |
699 | // the last range. |
700 | // We want to return the last IL, and exclude the epilog |
701 | if (endOffset == 0) |
702 | { |
703 | *end = GetSequenceMap() + GetSequenceMapCount() - 1; |
704 | _ASSERTE(*end>=m_sequenceMap); |
705 | |
706 | while ( ((*end)->ilOffset == (ULONG) ICorDebugInfo::EPILOG|| |
707 | (*end)->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING) |
708 | && (*end) > m_sequenceMap) |
709 | { |
710 | (*end)--; |
711 | _ASSERTE(*end>=m_sequenceMap); |
712 | |
713 | } |
714 | } |
715 | else |
716 | *end = MapILOffsetToMapEntry(endOffset - 1, NULL |
717 | WIN64_ARG(FALSE)); |
718 | |
719 | _ASSERTE(*end>=m_sequenceMap); |
720 | |
721 | |
722 | LOG((LF_CORDB, LL_INFO1000000, |
723 | "DJI::MIRTMER: IL 0x%04x-0x%04x --> 0x%04x 0x%08x-0x%08x\n" |
724 | " --> 0x%04x 0x%08x-0x%08x\n" , |
725 | startOffset, endOffset, |
726 | (*start)->ilOffset, |
727 | (*start)->nativeStartOffset, (*start)->nativeEndOffset, |
728 | (*end)->ilOffset, |
729 | (*end)->nativeStartOffset, (*end)->nativeEndOffset)); |
730 | } |
731 | |
732 | // @dbgtodo Microsoft inspection: This function has been replicated in DacDbiStructures so |
733 | // this version can be deleted when inspection is complete. |
734 | |
735 | // DWORD DebuggerJitInfo::MapNativeOffsetToIL(): Given a native |
736 | // offset for the DebuggerJitInfo, compute |
737 | // the IL offset from the beginning of the same method. |
738 | // Returns: Offset of the IL instruction that contains |
739 | // the native offset, |
740 | // SIZE_T nativeOffset: [IN] Native Offset |
741 | // CorDebugMappingResult *map: [OUT] explains the |
742 | // quality of the matching & special cases |
743 | // SIZE_T which: It's possible to have multiple EPILOGs, or |
744 | // multiple unmapped regions within a method. This opaque value |
745 | // specifies which special region we're talking about. This |
746 | // param has no meaning if map & (MAPPING_EXACT|MAPPING_APPROXIMATE) |
747 | // Basically, this gets handed back to MapSpecialToNative, later. |
748 | DWORD DebuggerJitInfo::MapNativeOffsetToIL(SIZE_T nativeOffsetToMap, |
749 | CorDebugMappingResult *map, |
750 | DWORD *which, |
751 | BOOL skipPrologs) |
752 | { |
753 | CONTRACTL |
754 | { |
755 | SO_INTOLERANT; |
756 | NOTHROW; |
757 | GC_NOTRIGGER; |
758 | PRECONDITION(map != NULL); |
759 | PRECONDITION(which != NULL); |
760 | } |
761 | CONTRACTL_END; |
762 | |
763 | DWORD nativeOffset = (DWORD)nativeOffsetToMap; |
764 | |
765 | (*which) = 0; |
766 | DebuggerILToNativeMap *m = GetSequenceMap(); |
767 | DebuggerILToNativeMap *mEnd = m + GetSequenceMapCount(); |
768 | |
769 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: nativeOffset = 0x%x\n" , nativeOffset)); |
770 | |
771 | if (m) |
772 | { |
773 | while (m < mEnd) |
774 | { |
775 | _ASSERTE(m>=m_sequenceMap); |
776 | |
777 | #ifdef LOGGING |
778 | if (m->ilOffset == (ULONG) ICorDebugInfo::PROLOG ) |
779 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:PROLOG\n" , m->nativeStartOffset, m->nativeEndOffset)); |
780 | else if (m->ilOffset == (ULONG) ICorDebugInfo::EPILOG ) |
781 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:EPILOG\n" , m->nativeStartOffset, m->nativeEndOffset)); |
782 | else if (m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING) |
783 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:NO MAP\n" , m->nativeStartOffset, m->nativeEndOffset)); |
784 | else |
785 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: m->natStart:0x%x m->natEnd:0x%x il:0x%x src:0x%x\n" , m->nativeStartOffset, m->nativeEndOffset, m->ilOffset, m->source)); |
786 | #endif // LOGGING |
787 | |
788 | if (m->ilOffset == (ULONG) ICorDebugInfo::PROLOG || |
789 | m->ilOffset == (ULONG) ICorDebugInfo::EPILOG || |
790 | m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING) |
791 | { |
792 | (*which)++; |
793 | } |
794 | |
795 | if (nativeOffset >= m->nativeStartOffset |
796 | && ((m->nativeEndOffset == 0 && |
797 | m->ilOffset != (ULONG) ICorDebugInfo::PROLOG) |
798 | || nativeOffset < m->nativeEndOffset)) |
799 | { |
800 | ULONG ilOff = m->ilOffset; |
801 | |
802 | if( m->ilOffset == (ULONG) ICorDebugInfo::PROLOG ) |
803 | { |
804 | if (skipPrologs && nativeOffset < m->nativeEndOffset) |
805 | { |
806 | // If the caller requested to skip prologs, we simply restart the walk |
807 | // with the offset set to the end of the prolog. |
808 | nativeOffset = m->nativeEndOffset; |
809 | continue; |
810 | } |
811 | |
812 | ilOff = 0; |
813 | (*map) = MAPPING_PROLOG; |
814 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI: MAPPING_PROLOG\n" )); |
815 | |
816 | } |
817 | else if (m->ilOffset == (ULONG) ICorDebugInfo::NO_MAPPING) |
818 | { |
819 | ilOff = 0; |
820 | (*map) = MAPPING_UNMAPPED_ADDRESS ; |
821 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_" |
822 | "UNMAPPED_ADDRESS\n" )); |
823 | } |
824 | else if( m->ilOffset == (ULONG) ICorDebugInfo::EPILOG ) |
825 | { |
826 | ilOff = m_lastIL; |
827 | (*map) = MAPPING_EPILOG; |
828 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_EPILOG\n" )); |
829 | } |
830 | else if (nativeOffset == m->nativeStartOffset) |
831 | { |
832 | (*map) = MAPPING_EXACT; |
833 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_EXACT\n" )); |
834 | } |
835 | else |
836 | { |
837 | (*map) = MAPPING_APPROXIMATE; |
838 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:MAPPING_" |
839 | "APPROXIMATE\n" )); |
840 | } |
841 | |
842 | return ilOff; |
843 | } |
844 | m++; |
845 | } |
846 | } |
847 | |
848 | (*map) = MAPPING_NO_INFO; |
849 | LOG((LF_CORDB,LL_INFO10000,"DJI::MNOTI:NO_INFO\n" )); |
850 | return 0; |
851 | } |
852 | |
853 | /****************************************************************************** |
854 | * |
855 | ******************************************************************************/ |
856 | DebuggerJitInfo::~DebuggerJitInfo() |
857 | { |
858 | TRACE_FREE(m_sequenceMap); |
859 | if (m_sequenceMap != NULL) |
860 | { |
861 | DeleteInteropSafe(((BYTE *)m_sequenceMap)); |
862 | } |
863 | |
864 | TRACE_FREE(m_varNativeInfo); |
865 | if (m_varNativeInfo != NULL) |
866 | { |
867 | DeleteInteropSafe(m_varNativeInfo); |
868 | } |
869 | |
870 | #if defined(WIN64EXCEPTIONS) |
871 | if (m_rgFunclet) |
872 | { |
873 | DeleteInteropSafe(m_rgFunclet); |
874 | m_rgFunclet = NULL; |
875 | } |
876 | #endif // WIN64EXCEPTIONS |
877 | |
878 | |
879 | #ifdef _DEBUG |
880 | // Trash pointers to garbage. |
881 | // Don't null out since there may be runtime checks against NULL. |
882 | // Set to a non-null random pointer value that will cause an immediate AV on deref. |
883 | m_fd = (MethodDesc*) 0x1; |
884 | m_methodInfo = (DebuggerMethodInfo*) 0x1; |
885 | m_prevJitInfo = (DebuggerJitInfo*) 0x01; |
886 | m_nextJitInfo = (DebuggerJitInfo*) 0x01; |
887 | #endif |
888 | |
889 | |
890 | LOG((LF_CORDB,LL_EVERYTHING, "DJI::~DJI : deleted at 0x%p\n" , this)); |
891 | } |
892 | |
893 | // Lazy initialize the Debugger-Jit-Info |
894 | void DebuggerJitInfo::LazyInitBounds() |
895 | { |
896 | CONTRACTL |
897 | { |
898 | SO_INTOLERANT; |
899 | NOTHROW; |
900 | GC_NOTRIGGER; |
901 | PRECONDITION(ThisMaybeHelperThread()); |
902 | PRECONDITION(!g_pDebugger->HasDebuggerDataLock()); |
903 | } CONTRACTL_END; |
904 | |
905 | LOG((LF_CORDB, LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x m_fAttemptInit %s\n" , this, m_fAttemptInit == true ? "true" : "false" )); |
906 | |
907 | // Only attempt lazy-init once |
908 | if (m_fAttemptInit) |
909 | { |
910 | return; |
911 | } |
912 | |
913 | EX_TRY |
914 | { |
915 | LOG((LF_CORDB, LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x Initing\n" , this)); |
916 | |
917 | // Should have already been jitted |
918 | _ASSERTE(this->m_jitComplete); |
919 | |
920 | MethodDesc * mdesc = this->m_fd; |
921 | DebugInfoRequest request; |
922 | |
923 | _ASSERTE(this->m_addrOfCode != NULL); // must have address to disambguate the Enc cases. |
924 | // Caller already resolved generics when they craeted the DJI, so we don't need to repeat. |
925 | // Note the MethodDesc may not yet have the jitted info, so we'll also use the starting address we got in the jit complete callback. |
926 | request.InitFromStartingAddr(mdesc, (PCODE)this->m_addrOfCode); |
927 | |
928 | // Bounds info. |
929 | ULONG32 cMap = 0; |
930 | ICorDebugInfo::OffsetMapping *pMap = NULL; |
931 | ULONG32 cVars = 0; |
932 | ICorDebugInfo::NativeVarInfo *pVars = NULL; |
933 | |
934 | BOOL fSuccess = DebugInfoManager::GetBoundariesAndVars( |
935 | request, |
936 | InteropSafeNew, NULL, // allocator |
937 | &cMap, &pMap, |
938 | &cVars, &pVars); |
939 | |
940 | LOG((LF_CORDB,LL_EVERYTHING, "DJI::LazyInitBounds: this=0x%x GetBoundariesAndVars success=0x%x\n" , this, fSuccess)); |
941 | |
942 | // SetBoundaries uses the CodeVersionManager, need to take it now for lock ordering reasons |
943 | CodeVersionManager::TableLockHolder lockHolder(m_fd->GetCodeVersionManager()); |
944 | Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger); |
945 | |
946 | if (!m_fAttemptInit) |
947 | { |
948 | if (fSuccess) |
949 | { |
950 | this->SetBoundaries(cMap, pMap); |
951 | this->SetVars(cVars, pVars); |
952 | } |
953 | m_fAttemptInit = true; |
954 | } |
955 | else |
956 | { |
957 | DeleteInteropSafe(pMap); |
958 | DeleteInteropSafe(pVars); |
959 | } |
960 | // DebuggerDataLockHolder out of scope - release implied |
961 | } |
962 | EX_CATCH |
963 | { |
964 | LOG((LF_CORDB,LL_WARNING, "DJI::LazyInitBounds: this=0x%x Exception was thrown and caught\n" , this)); |
965 | // Just catch the exception. The DJI maps may or may-not be intialized, |
966 | // but they should still be in a consistent state, so we should be ok. |
967 | } |
968 | EX_END_CATCH(SwallowAllExceptions) |
969 | } |
970 | |
971 | /****************************************************************************** |
972 | * SetVars() takes ownership of pVars |
973 | ******************************************************************************/ |
974 | void DebuggerJitInfo::SetVars(ULONG32 cVars, ICorDebugInfo::NativeVarInfo *pVars) |
975 | { |
976 | LIMITED_METHOD_CONTRACT; |
977 | |
978 | _ASSERTE(m_varNativeInfo == NULL); |
979 | |
980 | m_varNativeInfo = pVars; |
981 | m_varNativeInfoCount = cVars; |
982 | |
983 | LOG((LF_CORDB, LL_INFO1000000, "D::sV: var count is %d\n" , |
984 | m_varNativeInfoCount)); |
985 | |
986 | #ifdef LOGGING |
987 | for (unsigned int i = 0; i < m_varNativeInfoCount; i++) |
988 | { |
989 | ICorDebugInfo::NativeVarInfo* vni = &(m_varNativeInfo[i]); |
990 | _dumpVarNativeInfo(vni); |
991 | } |
992 | #endif |
993 | } |
994 | |
995 | CHECK DebuggerJitInfo::Check() const |
996 | { |
997 | LIMITED_METHOD_CONTRACT; |
998 | |
999 | CHECK_OK; |
1000 | } |
1001 | |
1002 | // Invariants for a DebuggerJitInfo |
1003 | // These should always be true at any well defined point. |
1004 | CHECK DebuggerJitInfo::Invariant() const |
1005 | { |
1006 | LIMITED_METHOD_CONTRACT; |
1007 | CHECK((m_sequenceMapCount == 0) == (m_sequenceMap == NULL)); |
1008 | CHECK(m_methodInfo != NULL); |
1009 | CHECK(m_fd != NULL); |
1010 | |
1011 | CHECK_OK; |
1012 | } |
1013 | |
1014 | |
1015 | #if !defined(DACCESS_COMPILE) |
1016 | /****************************************************************************** |
1017 | * SetBoundaries() takes ownership of pMap |
1018 | ******************************************************************************/ |
1019 | void DebuggerJitInfo::SetBoundaries(ULONG32 cMap, ICorDebugInfo::OffsetMapping *pMap) |
1020 | { |
1021 | CONTRACTL |
1022 | { |
1023 | SO_INTOLERANT; |
1024 | THROWS; |
1025 | GC_NOTRIGGER; |
1026 | PRECONDITION(CheckPointer(this)); |
1027 | } |
1028 | CONTRACTL_END; |
1029 | |
1030 | LOG((LF_CORDB,LL_EVERYTHING, "DJI::SetBoundaries: this=0x%x cMap=0x%x pMap=0x%x\n" , this, cMap, pMap)); |
1031 | _ASSERTE((cMap == 0) == (pMap == NULL)); |
1032 | _ASSERTE(m_sequenceMap == NULL); |
1033 | |
1034 | if (cMap == 0) |
1035 | return; |
1036 | |
1037 | ULONG ilLast = 0; |
1038 | #ifdef _DEBUG |
1039 | // We assume that the map is sorted by native offset |
1040 | if (cMap > 1) |
1041 | { |
1042 | for(ICorDebugInfo::OffsetMapping * pEntry = pMap; |
1043 | pEntry < (pMap + cMap - 1); |
1044 | pEntry++) |
1045 | { |
1046 | _ASSERTE(pEntry->nativeOffset <= (pEntry+1)->nativeOffset); |
1047 | } |
1048 | } |
1049 | #endif //_DEBUG |
1050 | |
1051 | // |
1052 | // <TODO>@todo perf: allocating these on the heap is slow. We could do |
1053 | // better knowing that these live for the life of the run, just |
1054 | // like the DebuggerJitInfo's.</TODO> |
1055 | // |
1056 | m_sequenceMap = (DebuggerILToNativeMap *)new (interopsafe) DebuggerILToNativeMap[cMap]; |
1057 | LOG((LF_CORDB,LL_EVERYTHING, "DJI::SetBoundaries: this=0x%x m_sequenceMap=0x%x\n" , this, m_sequenceMap)); |
1058 | _ASSERTE(m_sequenceMap != NULL); // we'll throw on null |
1059 | |
1060 | m_sequenceMapCount = cMap; |
1061 | |
1062 | DebuggerILToNativeMap *m = m_sequenceMap; |
1063 | |
1064 | // For the instrumented-IL case, we need to remove all duplicate entries. |
1065 | // So we keep a record of the last old IL offset. If the current old IL |
1066 | // offset is the same as the last old IL offset, we remove it. |
1067 | // Pick a unique initial value (-10) so that the 1st doesn't accidentally match. |
1068 | int ilPrevOld = -10; |
1069 | |
1070 | _ASSERTE(m_fd->GetCodeVersionManager()->LockOwnedByCurrentThread()); |
1071 | CodeVersionManager *pCodeVersionManager = m_fd->GetCodeVersionManager(); |
1072 | InstrumentedILOffsetMapping mapping; |
1073 | |
1074 | NativeCodeVersion nativeVersion = pCodeVersionManager->GetNativeCodeVersion(m_fd, (PCODE)CORDB_ADDRESS_TO_PTR(m_addrOfCode)); |
1075 | _ASSERTE(!nativeVersion.IsNull()); |
1076 | ILCodeVersion ilVersion = nativeVersion.GetILCodeVersion(); |
1077 | if (!ilVersion.IsDefaultVersion()) |
1078 | { |
1079 | // Did the current rejit provide a map? |
1080 | const InstrumentedILOffsetMapping *pReJitMap = ilVersion.GetInstrumentedILMap(); |
1081 | if (pReJitMap != NULL) |
1082 | { |
1083 | mapping = *pReJitMap; |
1084 | } |
1085 | } |
1086 | else if (m_methodInfo->HasInstrumentedILMap()) |
1087 | { |
1088 | // If a ReJIT hasn't happened, check for a profiler provided map. |
1089 | mapping = m_methodInfo->GetRuntimeModule()->GetInstrumentedILOffsetMapping(m_methodInfo->m_token); |
1090 | } |
1091 | |
1092 | // |
1093 | // <TODO>@todo perf: we could do the vast majority of this |
1094 | // post-processing work the first time the sequence point map is |
1095 | // demanded. That would allow us to simply hold the raw array for |
1096 | // 95% of the functions jitted while debugging, and 100% of them |
1097 | // when just running/tracking.</TODO> |
1098 | const DWORD call_inst = (DWORD)ICorDebugInfo::CALL_INSTRUCTION; |
1099 | for(ULONG32 idxJitMap = 0; idxJitMap < cMap; idxJitMap++) |
1100 | { |
1101 | const ICorDebugInfo::OffsetMapping * const pMapEntry = &pMap[idxJitMap]; |
1102 | _ASSERTE(m >= m_sequenceMap); |
1103 | _ASSERTE(m < m_sequenceMap + m_sequenceMapCount); |
1104 | |
1105 | ilLast = max((int)ilLast, (int)pMapEntry->ilOffset); |
1106 | |
1107 | // Simply copy everything over, since we translate to |
1108 | // CorDebugMappingResults immediately prior to handing |
1109 | // back to user... |
1110 | m->nativeStartOffset = pMapEntry->nativeOffset; |
1111 | m->ilOffset = pMapEntry->ilOffset; |
1112 | m->source = pMapEntry->source; |
1113 | |
1114 | // Keep in mind that if we have an instrumented code translation |
1115 | // table, we may have asked for completely different IL offsets |
1116 | // than the user thinks we did..... |
1117 | |
1118 | // If we did instrument, then we can't have any sequence points that |
1119 | // are "in-between" the old-->new map that the profiler gave us. |
1120 | // Ex, if map is: |
1121 | // (6 old -> 36 new) |
1122 | // (8 old -> 50 new) |
1123 | // And the jit gives us an entry for 44 new, that will map back to 6 old. |
1124 | // Since the map can only have one entry for 6 old, we remove 44 new. |
1125 | if (!mapping.IsNull()) |
1126 | { |
1127 | int ilThisOld = m_methodInfo->TranslateToInstIL(&mapping, |
1128 | pMapEntry->ilOffset, |
1129 | bInstrumentedToOriginal); |
1130 | |
1131 | if (ilThisOld == ilPrevOld) |
1132 | { |
1133 | // If this translated to the same old IL offset as the last entry, |
1134 | // then this is "in between". Skip it. |
1135 | m_sequenceMapCount--; // one less seq point in the DJI's map |
1136 | continue; |
1137 | } |
1138 | m->ilOffset = ilThisOld; |
1139 | ilPrevOld = ilThisOld; |
1140 | } |
1141 | |
1142 | if (m > m_sequenceMap && (m->source & call_inst) != call_inst) |
1143 | { |
1144 | DebuggerILToNativeMap *last = m-1; |
1145 | if ((last->source & call_inst) == call_inst) |
1146 | last = (last > m_sequenceMap) ? last - 1 : NULL; |
1147 | |
1148 | if (last && (last->source & call_inst) != call_inst && m->ilOffset == last->ilOffset) |
1149 | { |
1150 | // JIT gave us an extra entry (probably zero), so mush |
1151 | // it into the one we've already got. |
1152 | // <TODO> Why does this happen?</TODO> |
1153 | m_sequenceMapCount--; |
1154 | continue; |
1155 | } |
1156 | } |
1157 | |
1158 | |
1159 | // Move to next entry in the debugger's table |
1160 | m++; |
1161 | } // end for |
1162 | |
1163 | DeleteInteropSafe(pMap); |
1164 | |
1165 | _ASSERTE(m == m_sequenceMap + m_sequenceMapCount); |
1166 | |
1167 | m_lastIL = ilLast; |
1168 | |
1169 | // Set nativeEndOffset in debugger's il->native map |
1170 | // Do this before we resort by IL. |
1171 | unsigned int i; |
1172 | for(i = 0; i < m_sequenceMapCount - 1; i++) |
1173 | { |
1174 | // We need to not use CALL_INSTRUCTION's IL start offset. |
1175 | unsigned int j = i + 1; |
1176 | while ((m_sequenceMap[j].source & call_inst) == call_inst && j < m_sequenceMapCount-1) |
1177 | j++; |
1178 | |
1179 | m_sequenceMap[i].nativeEndOffset = m_sequenceMap[j].nativeStartOffset; |
1180 | } |
1181 | |
1182 | m_sequenceMap[i].nativeEndOffset = 0; |
1183 | m_sequenceMap[i].source = (ICorDebugInfo::SourceTypes) |
1184 | ((DWORD) m_sequenceMap[i].source | |
1185 | (DWORD)ICorDebugInfo::NATIVE_END_OFFSET_UNKNOWN); |
1186 | |
1187 | // Now resort by IL. |
1188 | MapSortIL isort(m_sequenceMap, m_sequenceMapCount); |
1189 | |
1190 | isort.Sort(); |
1191 | |
1192 | m_sequenceMapSorted = true; |
1193 | |
1194 | m_callsiteMapCount = m_sequenceMapCount; |
1195 | while (m_sequenceMapCount > 0 && (m_sequenceMap[m_sequenceMapCount-1].source & call_inst) == call_inst) |
1196 | m_sequenceMapCount--; |
1197 | |
1198 | m_callsiteMap = m_sequenceMap + m_sequenceMapCount; |
1199 | m_callsiteMapCount -= m_sequenceMapCount; |
1200 | |
1201 | LOG((LF_CORDB, LL_INFO100000, "DJI::SetBoundaries: this=0x%x boundary count is %d (%d callsites)\n" , |
1202 | this, m_sequenceMapCount, m_callsiteMapCount)); |
1203 | |
1204 | #ifdef LOGGING |
1205 | for (unsigned int count = 0; count < m_sequenceMapCount + m_callsiteMapCount; count++) |
1206 | { |
1207 | if( m_sequenceMap[count].ilOffset == |
1208 | (ULONG) ICorDebugInfo::PROLOG ) |
1209 | LOG((LF_CORDB, LL_INFO1000000, |
1210 | "D::sB: PROLOG --> 0x%08x -- 0x%08x" , |
1211 | m_sequenceMap[count].nativeStartOffset, |
1212 | m_sequenceMap[count].nativeEndOffset)); |
1213 | else if ( m_sequenceMap[count].ilOffset == |
1214 | (ULONG) ICorDebugInfo::EPILOG ) |
1215 | LOG((LF_CORDB, LL_INFO1000000, |
1216 | "D::sB: EPILOG --> 0x%08x -- 0x%08x" , |
1217 | m_sequenceMap[count].nativeStartOffset, |
1218 | m_sequenceMap[count].nativeEndOffset)); |
1219 | else if ( m_sequenceMap[count].ilOffset == |
1220 | (ULONG) ICorDebugInfo::NO_MAPPING ) |
1221 | LOG((LF_CORDB, LL_INFO1000000, |
1222 | "D::sB: NO MAP --> 0x%08x -- 0x%08x" , |
1223 | m_sequenceMap[count].nativeStartOffset, |
1224 | m_sequenceMap[count].nativeEndOffset)); |
1225 | else |
1226 | LOG((LF_CORDB, LL_INFO1000000, |
1227 | "D::sB: 0x%04x (Real:0x%04x) --> 0x%08x -- 0x%08x" , |
1228 | m_sequenceMap[count].ilOffset, |
1229 | m_methodInfo->TranslateToInstIL(&mapping, |
1230 | m_sequenceMap[count].ilOffset, |
1231 | bOriginalToInstrumented), |
1232 | m_sequenceMap[count].nativeStartOffset, |
1233 | m_sequenceMap[count].nativeEndOffset)); |
1234 | |
1235 | LOG((LF_CORDB, LL_INFO1000000, " Src:0x%x\n" , m_sequenceMap[count].source)); |
1236 | |
1237 | } |
1238 | #endif //LOGGING |
1239 | } |
1240 | #endif // !DACCESS_COMPILE |
1241 | |
1242 | // Init a DJI after it's jitted. |
1243 | void DebuggerJitInfo::Init(TADDR newAddress) |
1244 | { |
1245 | // Shouldn't initialize while holding the lock b/c intialzing may call functions that lock, |
1246 | // and thus we'd have a locking violation. |
1247 | _ASSERTE(!g_pDebugger->HasDebuggerDataLock()); |
1248 | |
1249 | this->m_addrOfCode = (ULONG_PTR)PTR_TO_CORDB_ADDRESS((BYTE*) newAddress); |
1250 | this->m_jitComplete = true; |
1251 | |
1252 | this->m_codeRegionInfo.InitializeFromStartAddress(PINSTRToPCODE((TADDR)this->m_addrOfCode)); |
1253 | this->m_sizeOfCode = this->m_codeRegionInfo.getSizeOfTotalCode(); |
1254 | |
1255 | this->m_encVersion = this->m_methodInfo->GetCurrentEnCVersion(); |
1256 | |
1257 | #if defined(WIN64EXCEPTIONS) |
1258 | this->InitFuncletAddress(); |
1259 | #endif // WIN64EXCEPTIONS |
1260 | |
1261 | LOG((LF_CORDB,LL_INFO10000,"De::JITCo:Got DJI 0x%p(V %d)," |
1262 | "Hot section from 0x%p to 0x%p " |
1263 | "Cold section from 0x%p to 0x%p " |
1264 | "varCount=%d seqCount=%d\n" , |
1265 | this, this->m_encVersion, |
1266 | this->m_codeRegionInfo.getAddrOfHotCode(), |
1267 | this->m_codeRegionInfo.getAddrOfHotCode() + this->m_codeRegionInfo.getSizeOfHotCode(), |
1268 | this->m_codeRegionInfo.getAddrOfColdCode(), |
1269 | this->m_codeRegionInfo.getAddrOfColdCode() + this->m_codeRegionInfo.getSizeOfColdCode(), |
1270 | (ULONG)this->m_addrOfCode, |
1271 | (ULONG)this->m_addrOfCode+(ULONG)this->m_sizeOfCode, |
1272 | this->GetVarNativeInfoCount(), |
1273 | this->GetSequenceMapCount())); |
1274 | |
1275 | #if defined(LOGGING) |
1276 | for (unsigned int i = 0; i < this->GetSequenceMapCount(); i++) |
1277 | { |
1278 | LOG((LF_CORDB, LL_INFO10000, "De::JITCo: seq map 0x%x - " |
1279 | "IL offset 0x%x native start offset 0x%x native end offset 0x%x source 0x%x\n" , |
1280 | i, this->GetSequenceMap()[i].ilOffset, |
1281 | this->GetSequenceMap()[i].nativeStartOffset, |
1282 | this->GetSequenceMap()[i].nativeEndOffset, |
1283 | this->GetSequenceMap()[i].source)); |
1284 | } |
1285 | #endif // LOGGING |
1286 | |
1287 | } |
1288 | |
1289 | /****************************************************************************** |
1290 | * |
1291 | ******************************************************************************/ |
1292 | ICorDebugInfo::SourceTypes DebuggerJitInfo::GetSrcTypeFromILOffset(SIZE_T ilOffset) |
1293 | { |
1294 | CONTRACTL |
1295 | { |
1296 | SO_NOT_MAINLINE; |
1297 | NOTHROW; |
1298 | GC_NOTRIGGER; |
1299 | } |
1300 | CONTRACTL_END; |
1301 | |
1302 | BOOL exact = FALSE; |
1303 | DebuggerILToNativeMap *pMap = MapILOffsetToMapEntry(ilOffset, &exact); |
1304 | |
1305 | LOG((LF_CORDB, LL_INFO100000, "DJI::GSTFILO: for il 0x%x, got entry 0x%p," |
1306 | "(il 0x%x) nat 0x%x to 0x%x, SourceTypes 0x%x, exact:%x\n" , ilOffset, pMap, |
1307 | pMap->ilOffset, pMap->nativeStartOffset, pMap->nativeEndOffset, pMap->source, |
1308 | exact)); |
1309 | |
1310 | if (!exact) |
1311 | { |
1312 | return ICorDebugInfo::SOURCE_TYPE_INVALID; |
1313 | } |
1314 | |
1315 | return pMap->source; |
1316 | } |
1317 | |
1318 | /****************************************************************************** |
1319 | * |
1320 | ******************************************************************************/ |
1321 | DebuggerMethodInfo::~DebuggerMethodInfo() |
1322 | { |
1323 | CONTRACTL |
1324 | { |
1325 | SO_INTOLERANT; |
1326 | NOTHROW; |
1327 | GC_NOTRIGGER; |
1328 | DESTRUCTOR_CHECK; |
1329 | } |
1330 | CONTRACTL_END; |
1331 | |
1332 | DeleteJitInfoList(); |
1333 | |
1334 | LOG((LF_CORDB,LL_EVERYTHING, "DMI::~DMI : deleted at 0x%p\n" , this)); |
1335 | } |
1336 | |
1337 | // Translate between old & new offsets (w/ respect to Instrumented IL). |
1338 | |
1339 | // Don't interpolate |
1340 | ULONG32 DebuggerMethodInfo::TranslateToInstIL(const InstrumentedILOffsetMapping * pMapping, |
1341 | ULONG32 offOrig, |
1342 | bool fOrigToInst) |
1343 | { |
1344 | LIMITED_METHOD_CONTRACT; |
1345 | |
1346 | SIZE_T iMap; |
1347 | SIZE_T cMap = pMapping->GetCount(); |
1348 | // some negative IL offsets have special meaning. Don't translate |
1349 | // those (just return as is). See ICorDebugInfo::MappingTypes |
1350 | if ((cMap == 0) || (offOrig < 0)) |
1351 | { |
1352 | return offOrig; |
1353 | } |
1354 | |
1355 | ARRAY_PTR_COR_IL_MAP rgMap = pMapping->GetOffsets(); |
1356 | |
1357 | // This assumes: |
1358 | // - map is sorted in increasing order by both old & new |
1359 | // - round down. |
1360 | if (fOrigToInst) |
1361 | { |
1362 | // Translate: old --> new |
1363 | |
1364 | // Treat it as prolog if offOrig is not in remapping range |
1365 | if ((offOrig < rgMap[0].oldOffset) || (offOrig == (ULONG32)ICorDebugInfo::PROLOG)) |
1366 | { |
1367 | return (ULONG32)ICorDebugInfo::PROLOG; |
1368 | } |
1369 | |
1370 | if (offOrig == (ULONG32)ICorDebugInfo::EPILOG) |
1371 | { |
1372 | return (ULONG32)ICorDebugInfo::EPILOG; |
1373 | } |
1374 | |
1375 | if (offOrig == (ULONG32)ICorDebugInfo::NO_MAPPING) |
1376 | { |
1377 | return (ULONG32)ICorDebugInfo::NO_MAPPING; |
1378 | } |
1379 | |
1380 | for(iMap = 1; iMap < cMap; iMap++) |
1381 | { |
1382 | if (offOrig < rgMap[iMap].oldOffset) |
1383 | return rgMap[iMap-1].newOffset; |
1384 | } |
1385 | |
1386 | return rgMap[iMap - 1].newOffset; |
1387 | } |
1388 | else |
1389 | { |
1390 | // Translate: new --> old |
1391 | |
1392 | // Treat it as prolog if offOrig is not in remapping range |
1393 | if ((offOrig < rgMap[0].newOffset) || (offOrig == (ULONG32)ICorDebugInfo::PROLOG)) |
1394 | { |
1395 | return (ULONG32)ICorDebugInfo::PROLOG; |
1396 | } |
1397 | |
1398 | if (offOrig == (ULONG32)ICorDebugInfo::EPILOG) |
1399 | { |
1400 | return (ULONG32)ICorDebugInfo::EPILOG; |
1401 | } |
1402 | |
1403 | if (offOrig == (ULONG32)ICorDebugInfo::NO_MAPPING) |
1404 | { |
1405 | return (ULONG32)ICorDebugInfo::NO_MAPPING; |
1406 | } |
1407 | |
1408 | for(iMap = 1; iMap < cMap; iMap++) |
1409 | { |
1410 | if (offOrig < rgMap[iMap].newOffset) |
1411 | return rgMap[iMap-1].oldOffset; |
1412 | } |
1413 | |
1414 | return rgMap[iMap - 1].oldOffset; |
1415 | } |
1416 | } |
1417 | |
1418 | /****************************************************************************** |
1419 | * Constructor for DebuggerMethodInfo |
1420 | ******************************************************************************/ |
1421 | DebuggerMethodInfo::DebuggerMethodInfo(Module *module, mdMethodDef token) : |
1422 | m_currentEnCVersion(CorDB_DEFAULT_ENC_FUNCTION_VERSION), |
1423 | m_module(module), |
1424 | m_token(token), |
1425 | m_prevMethodInfo(NULL), |
1426 | m_nextMethodInfo(NULL), |
1427 | m_latestJitInfo(NULL), |
1428 | m_fHasInstrumentedILMap(false) |
1429 | { |
1430 | CONTRACTL |
1431 | { |
1432 | SO_INTOLERANT; |
1433 | WRAPPER(THROWS); |
1434 | WRAPPER(GC_TRIGGERS); |
1435 | CONSTRUCTOR_CHECK; |
1436 | } |
1437 | CONTRACTL_END; |
1438 | |
1439 | LOG((LF_CORDB,LL_EVERYTHING, "DMI::DMI : created at 0x%p\n" , this)); |
1440 | |
1441 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
1442 | |
1443 | DebuggerModule * pModule = GetPrimaryModule(); |
1444 | |
1445 | m_fJMCStatus = false; |
1446 | |
1447 | // If there's no module, then this isn't a JMC function. |
1448 | // This can happen since DMIs are created for debuggable code, and |
1449 | // Modules are only created if a debugger is actually attached. |
1450 | if (pModule != NULL) |
1451 | { |
1452 | // Use the accessor so that we keep the module's count properly updated. |
1453 | SetJMCStatus(pModule->GetRuntimeModule()->GetJMCStatus()); |
1454 | } |
1455 | } |
1456 | |
1457 | |
1458 | /****************************************************************************** |
1459 | * Get the primary debugger module for this DMI. This is 1:1 w/ an EE Module. |
1460 | ******************************************************************************/ |
1461 | DebuggerModule* DebuggerMethodInfo::GetPrimaryModule() |
1462 | { |
1463 | CONTRACTL |
1464 | { |
1465 | SO_INTOLERANT; |
1466 | NOTHROW; |
1467 | GC_NOTRIGGER; |
1468 | } |
1469 | CONTRACTL_END; |
1470 | |
1471 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
1472 | |
1473 | DebuggerModuleTable * pTable = g_pDebugger->GetModuleTable(); |
1474 | |
1475 | // If we're tracking debug info but no debugger's attached, then |
1476 | // we won't have a table for the modules yet. |
1477 | if (pTable == NULL) |
1478 | return NULL; |
1479 | |
1480 | DebuggerModule * pModule = pTable->GetModule(GetRuntimeModule()); |
1481 | if (pModule == NULL) |
1482 | { |
1483 | // We may be missing the module even if we have the table. |
1484 | // 1.) If there's no debugger attached (so we're not getting ModuleLoad events). |
1485 | // 2.) If we're asking for this while in DllMain of the module we're currently loading, |
1486 | // we won't have gotten the ModuleLoad event yet. |
1487 | return NULL; |
1488 | } |
1489 | |
1490 | // Only give back primary modules... |
1491 | DebuggerModule * p2 = pModule->GetPrimaryModule(); |
1492 | _ASSERTE(p2 != NULL); |
1493 | |
1494 | return p2; |
1495 | } |
1496 | |
1497 | /****************************************************************************** |
1498 | * Get the runtime module for this DMI |
1499 | ******************************************************************************/ |
1500 | Module * DebuggerMethodInfo::GetRuntimeModule() |
1501 | { |
1502 | LIMITED_METHOD_CONTRACT; |
1503 | |
1504 | return m_module; |
1505 | } |
1506 | |
1507 | #endif // !DACCESS_COMPILE |
1508 | |
1509 | |
1510 | //--------------------------------------------------------------------------------------- |
1511 | // |
1512 | // Find the DebuggerJitInfo (DJI) for the given MethodDesc and native start address. |
1513 | // We need the native start address because generic methods may have multiple instances |
1514 | // of jitted code. This function does not create the DJI if it does not already exist. |
1515 | // |
1516 | // Arguments: |
1517 | // pMD - the MD to lookup; must be non-NULL |
1518 | // addrNativeStartAddr - the native start address of jitted code |
1519 | // |
1520 | // Return Value: |
1521 | // Returns the DJI corresponding to the specified MD and native start address. |
1522 | // NULL if the DJI is not found. |
1523 | // |
1524 | |
1525 | DebuggerJitInfo * DebuggerMethodInfo::FindJitInfo(MethodDesc * pMD, |
1526 | TADDR addrNativeStartAddr) |
1527 | { |
1528 | CONTRACTL |
1529 | { |
1530 | SO_INTOLERANT; |
1531 | SUPPORTS_DAC; |
1532 | NOTHROW; |
1533 | GC_NOTRIGGER; |
1534 | PRECONDITION(pMD != NULL); |
1535 | } |
1536 | CONTRACTL_END; |
1537 | |
1538 | |
1539 | DebuggerJitInfo * pCheck = m_latestJitInfo; |
1540 | while (pCheck != NULL) |
1541 | { |
1542 | if ( (pCheck->m_fd == dac_cast<PTR_MethodDesc>(pMD)) && |
1543 | (pCheck->m_addrOfCode == addrNativeStartAddr) ) |
1544 | { |
1545 | return pCheck; |
1546 | } |
1547 | |
1548 | pCheck = pCheck->m_prevJitInfo; |
1549 | } |
1550 | |
1551 | return NULL; |
1552 | } |
1553 | |
1554 | |
1555 | #if !defined(DACCESS_COMPILE) |
1556 | |
1557 | /* |
1558 | * FindOrCreateInitAndAddJitInfo |
1559 | * |
1560 | * This routine tries to find an existing DJI based on the method desc and start address, or allocates a new DJI, adding it to |
1561 | * the DMI. |
1562 | * |
1563 | * Parameters: |
1564 | * fd - the method desc to find or create a DJI for. |
1565 | * startAddr - the start address to find or create the DJI for. |
1566 | * |
1567 | * Returns |
1568 | * A pointer to the found or created DJI, or NULL. |
1569 | * |
1570 | */ |
1571 | |
1572 | DebuggerJitInfo *DebuggerMethodInfo::FindOrCreateInitAndAddJitInfo(MethodDesc* fd, PCODE startAddr) |
1573 | { |
1574 | CONTRACTL |
1575 | { |
1576 | SO_INTOLERANT; |
1577 | THROWS; |
1578 | GC_NOTRIGGER; |
1579 | } |
1580 | CONTRACTL_END; |
1581 | |
1582 | _ASSERTE(fd != NULL); |
1583 | |
1584 | // The debugger doesn't track Lightweight-codegen methods b/c they have no metadata. |
1585 | if (fd->IsDynamicMethod()) |
1586 | { |
1587 | return NULL; |
1588 | } |
1589 | |
1590 | if (startAddr == NULL) |
1591 | { |
1592 | // This will grab the start address for the current code version. |
1593 | startAddr = g_pEEInterface->GetFunctionAddress(fd); |
1594 | if (startAddr == NULL) |
1595 | { |
1596 | return NULL; |
1597 | } |
1598 | } |
1599 | else |
1600 | { |
1601 | _ASSERTE(g_pEEInterface->GetNativeCodeMethodDesc(startAddr) == fd); |
1602 | } |
1603 | |
1604 | // Check the lsit to see if we've already populated an entry for this JitInfo. |
1605 | // If we didn't have a JitInfo before, lazily create it now. |
1606 | // We don't care if we were prejitted or not. |
1607 | // |
1608 | // We haven't got the lock yet so we'll repeat this lookup once |
1609 | // we've taken the lock. |
1610 | ARM_ONLY(_ASSERTE((startAddr & THUMB_CODE) == 1)); |
1611 | DebuggerJitInfo * pResult = FindJitInfo(fd, startAddr); |
1612 | if (pResult != NULL) |
1613 | { |
1614 | return pResult; |
1615 | } |
1616 | |
1617 | // The DJI may already be populated in the cache, if so CreateInitAndAddJitInfo is a no-op and that is fine. |
1618 | // CreateInitAndAddJitInfo takes a lock and checks the list again, which makes this thread-safe. |
1619 | BOOL jitInfoWasCreated; |
1620 | return CreateInitAndAddJitInfo(fd, startAddr, &jitInfoWasCreated); |
1621 | } |
1622 | |
1623 | // Create a DJI around a method-desc. The EE already has all the information we need for a DJI, |
1624 | // the DJI just serves as a cache of the information for the debugger. |
1625 | // Caller makes no guarantees about whether the DJI is already in the table. (Caller should avoid this if |
1626 | // it knows it's in the table, but b/c we can't expect caller to synchronize w/ the other threads). |
1627 | DebuggerJitInfo *DebuggerMethodInfo::CreateInitAndAddJitInfo(MethodDesc* fd, TADDR startAddr, BOOL* jitInfoWasCreated) |
1628 | { |
1629 | CONTRACTL |
1630 | { |
1631 | SO_INTOLERANT; |
1632 | THROWS; |
1633 | GC_NOTRIGGER; |
1634 | PRECONDITION(!g_pDebugger->HasDebuggerDataLock()); |
1635 | } |
1636 | CONTRACTL_END; |
1637 | |
1638 | _ASSERTE(fd != NULL); |
1639 | |
1640 | // May or may-not be jitted, that's why we passed in the start addr & size explicitly. |
1641 | _ASSERTE(startAddr != NULL); |
1642 | |
1643 | *jitInfoWasCreated = FALSE; |
1644 | |
1645 | // No support for light-weight codegen methods. |
1646 | if (fd->IsDynamicMethod()) |
1647 | { |
1648 | return NULL; |
1649 | } |
1650 | |
1651 | |
1652 | DebuggerJitInfo *dji = new (interopsafe) DebuggerJitInfo(this, fd); |
1653 | _ASSERTE(dji != NULL); // throws on oom error |
1654 | |
1655 | _ASSERTE(dji->m_methodInfo == this); // this should be set |
1656 | |
1657 | TRACE_ALLOC(dji); |
1658 | |
1659 | // Init may take locks that violate the debugger-data lock, so we can't init while we hold that lock. |
1660 | // But we can't init after we add it to the table and release the lock b/c another thread may pick |
1661 | // if up in the uninitialized state. |
1662 | // So we initialize a private copy of the DJI before we take the debugger-data lock. |
1663 | dji->Init(startAddr); |
1664 | |
1665 | dji->m_nextJitInfo = NULL; |
1666 | |
1667 | // |
1668 | //<TODO>@TODO : _ASSERTE(EnC);</TODO> |
1669 | // |
1670 | { |
1671 | Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger); |
1672 | |
1673 | // We need to ensure that another thread didn't go in and add this exact same DJI? |
1674 | { |
1675 | DebuggerJitInfo * pResult = FindJitInfo(dji->m_fd, (TADDR)dji->m_addrOfCode); |
1676 | if (pResult != NULL) |
1677 | { |
1678 | // Found! |
1679 | _ASSERTE(pResult->m_sizeOfCode == dji->m_sizeOfCode); |
1680 | DeleteInteropSafe(dji); |
1681 | return pResult; |
1682 | } |
1683 | else |
1684 | { |
1685 | *jitInfoWasCreated = TRUE; |
1686 | } |
1687 | } |
1688 | |
1689 | // We know it's not in the table. Go add it! |
1690 | DebuggerJitInfo *djiPrev = m_latestJitInfo; |
1691 | |
1692 | LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: current head of dji list:0x%08x\n" , djiPrev)); |
1693 | |
1694 | if (djiPrev != NULL) |
1695 | { |
1696 | dji->m_prevJitInfo = djiPrev; |
1697 | djiPrev->m_nextJitInfo = dji; |
1698 | |
1699 | m_latestJitInfo = dji; |
1700 | |
1701 | LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: DJI version 0x%04x for %s\n" , |
1702 | GetCurrentEnCVersion(), |
1703 | dji->m_fd->m_pszDebugMethodName)); |
1704 | } |
1705 | else |
1706 | { |
1707 | m_latestJitInfo = dji; |
1708 | } |
1709 | |
1710 | } // DebuggerDataLockHolder out of scope - release implied |
1711 | |
1712 | // We've now added a new DJI into the table and released the lock. Thus any other thread |
1713 | // can come and use our DJI. Good thing we inited the DJI _before_ adding it to the table. |
1714 | |
1715 | LOG((LF_CORDB,LL_INFO10000,"DMI:CAAJI: new head of dji list:0x%08x\n" , m_latestJitInfo)); |
1716 | |
1717 | return dji; |
1718 | } |
1719 | |
1720 | /* |
1721 | * DeleteJitInfo |
1722 | * |
1723 | * This routine remove a DJI from the DMI's list and deletes the memory. |
1724 | * |
1725 | * Parameters: |
1726 | * dji - The DJI to delete. |
1727 | * |
1728 | * Returns |
1729 | * None. |
1730 | * |
1731 | */ |
1732 | |
1733 | void DebuggerMethodInfo::DeleteJitInfo(DebuggerJitInfo *dji) |
1734 | { |
1735 | CONTRACTL |
1736 | { |
1737 | SO_INTOLERANT; |
1738 | NOTHROW; |
1739 | GC_NOTRIGGER; |
1740 | } |
1741 | CONTRACTL_END; |
1742 | |
1743 | Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger); |
1744 | |
1745 | LOG((LF_CORDB,LL_INFO10000,"DMI:DJI: dji:0x%08x\n" , dji)); |
1746 | |
1747 | DebuggerJitInfo *djiPrev = dji->m_prevJitInfo; |
1748 | |
1749 | if (djiPrev != NULL) |
1750 | { |
1751 | djiPrev->m_nextJitInfo = dji->m_nextJitInfo; |
1752 | } |
1753 | |
1754 | if (dji->m_nextJitInfo != NULL) |
1755 | { |
1756 | dji->m_nextJitInfo->m_prevJitInfo = djiPrev; |
1757 | } |
1758 | else |
1759 | { |
1760 | // |
1761 | // This DJI is the head of the list |
1762 | // |
1763 | _ASSERTE(m_latestJitInfo == dji); |
1764 | |
1765 | m_latestJitInfo = djiPrev; |
1766 | } |
1767 | |
1768 | TRACE_FREE(dji); |
1769 | |
1770 | DeleteInteropSafe(dji); |
1771 | |
1772 | // DebuggerDataLockHolder out of scope - release implied |
1773 | } |
1774 | |
1775 | /* |
1776 | * DeleteJitInfoList |
1777 | * |
1778 | * This routine removes all the DJIs from the current DMI. |
1779 | * |
1780 | * Parameters: |
1781 | * None. |
1782 | * |
1783 | * Returns |
1784 | * None. |
1785 | * |
1786 | */ |
1787 | |
1788 | void DebuggerMethodInfo::DeleteJitInfoList(void) |
1789 | { |
1790 | CONTRACTL |
1791 | { |
1792 | SO_INTOLERANT; |
1793 | NOTHROW; |
1794 | GC_NOTRIGGER; |
1795 | } |
1796 | CONTRACTL_END; |
1797 | |
1798 | Debugger::DebuggerDataLockHolder debuggerDataLockHolder(g_pDebugger); |
1799 | |
1800 | while(m_latestJitInfo != NULL) |
1801 | { |
1802 | DeleteJitInfo(m_latestJitInfo); |
1803 | } |
1804 | |
1805 | // DebuggerDataLockHolder out of scope - release implied |
1806 | } |
1807 | |
1808 | |
1809 | // Iterate through all existing DJIs. See header for expected usage. |
1810 | DebuggerMethodInfo::DJIIterator::DJIIterator() |
1811 | { |
1812 | LIMITED_METHOD_CONTRACT; |
1813 | |
1814 | m_pCurrent = NULL; |
1815 | m_pLoaderModuleFilter = NULL; |
1816 | } |
1817 | |
1818 | bool DebuggerMethodInfo::DJIIterator::IsAtEnd() |
1819 | { |
1820 | LIMITED_METHOD_CONTRACT; |
1821 | |
1822 | return m_pCurrent == NULL; |
1823 | } |
1824 | |
1825 | DebuggerJitInfo * DebuggerMethodInfo::DJIIterator::Current() |
1826 | { |
1827 | LIMITED_METHOD_CONTRACT; |
1828 | |
1829 | return m_pCurrent; |
1830 | } |
1831 | |
1832 | void DebuggerMethodInfo::DJIIterator::Next(BOOL fFirst /*=FALSE*/) |
1833 | { |
1834 | CONTRACTL |
1835 | { |
1836 | NOTHROW; |
1837 | GC_NOTRIGGER; |
1838 | FORBID_FAULT; |
1839 | MODE_ANY; |
1840 | CANNOT_TAKE_LOCK; |
1841 | } |
1842 | CONTRACTL_END; |
1843 | |
1844 | if (!fFirst) |
1845 | { |
1846 | PREFIX_ASSUME(m_pCurrent != NULL); // IsAtEnd() should have caught this. |
1847 | m_pCurrent = m_pCurrent->m_prevJitInfo; |
1848 | } |
1849 | |
1850 | // Check if we're at the end of the list, in which case we're done. |
1851 | for ( ; m_pCurrent != NULL; m_pCurrent = m_pCurrent->m_prevJitInfo) |
1852 | { |
1853 | Module * pLoaderModule = m_pCurrent->m_pLoaderModule; |
1854 | |
1855 | // Obey the module filter if it's provided |
1856 | if ((m_pLoaderModuleFilter != NULL) && (m_pLoaderModuleFilter != pLoaderModule)) |
1857 | continue; |
1858 | |
1859 | //Obey the methodDesc filter if it is provided |
1860 | if ((m_pMethodDescFilter != NULL) && (m_pMethodDescFilter != m_pCurrent->m_fd)) |
1861 | continue; |
1862 | |
1863 | // Skip modules that are unloaded, but still hanging around. Note that we can't use DebuggerModule for this check |
1864 | // because of it is deleted pretty early during unloading, and we do not want to recreate it. |
1865 | if (pLoaderModule->GetLoaderAllocator()->IsUnloaded()) |
1866 | continue; |
1867 | |
1868 | break; |
1869 | } |
1870 | } |
1871 | |
1872 | |
1873 | /****************************************************************************** |
1874 | * Return true iff this method is jitted |
1875 | ******************************************************************************/ |
1876 | bool DebuggerMethodInfo::HasJitInfos() |
1877 | { |
1878 | LIMITED_METHOD_CONTRACT; |
1879 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
1880 | return (m_latestJitInfo != NULL); |
1881 | } |
1882 | |
1883 | /****************************************************************************** |
1884 | * Return true iff this has been EnCed since last time function was jitted. |
1885 | ******************************************************************************/ |
1886 | bool DebuggerMethodInfo::HasMoreRecentEnCVersion() |
1887 | { |
1888 | LIMITED_METHOD_CONTRACT; |
1889 | return ((m_latestJitInfo != NULL) && |
1890 | (m_currentEnCVersion > m_latestJitInfo->m_encVersion)); |
1891 | } |
1892 | |
1893 | /****************************************************************************** |
1894 | * Updated the instrumented-IL map |
1895 | ******************************************************************************/ |
1896 | void DebuggerMethodInfo::SetInstrumentedILMap(COR_IL_MAP * pMap, SIZE_T cEntries) |
1897 | { |
1898 | InstrumentedILOffsetMapping mapping; |
1899 | mapping.SetMappingInfo(cEntries, pMap); |
1900 | |
1901 | GetRuntimeModule()->SetInstrumentedILOffsetMapping(m_token, mapping); |
1902 | |
1903 | m_fHasInstrumentedILMap = true; |
1904 | } |
1905 | |
1906 | /****************************************************************************** |
1907 | * Get the JMC status for a given function. |
1908 | ******************************************************************************/ |
1909 | bool DebuggerMethodInfo::IsJMCFunction() |
1910 | { |
1911 | LIMITED_METHOD_CONTRACT; |
1912 | return m_fJMCStatus; |
1913 | } |
1914 | |
1915 | /****************************************************************************** |
1916 | * Set the JMC status to a given value |
1917 | ******************************************************************************/ |
1918 | void DebuggerMethodInfo::SetJMCStatus(bool fStatus) |
1919 | { |
1920 | CONTRACTL |
1921 | { |
1922 | SO_NOT_MAINLINE; |
1923 | NOTHROW; |
1924 | GC_NOTRIGGER; |
1925 | } |
1926 | CONTRACTL_END; |
1927 | |
1928 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
1929 | |
1930 | // First check if this is a no-op. |
1931 | // Do this first b/c there may be some cases where we don't have a DebuggerModule |
1932 | // yet but are still calling SetJMCStatus(false), like if we detach before attach is complete. |
1933 | bool fOldStatus = IsJMCFunction(); |
1934 | |
1935 | if (fOldStatus == fStatus) |
1936 | { |
1937 | // if no change, then there's nothing to do. |
1938 | LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, keeping old status, %d\n" , this, fStatus)); |
1939 | return; |
1940 | } |
1941 | |
1942 | // For a perf-optimization, our Module needs to know if it has any user |
1943 | // code. If it doesn't, it shouldn't dispatch through the JMC probes. |
1944 | // So modules keep a count of # of JMC functions - if the count is 0, the |
1945 | // module can set is JMC probe flag to 0 and skip the JMC probes. |
1946 | Module * pRuntimeModule = this->GetRuntimeModule(); |
1947 | |
1948 | // Update the module's count. |
1949 | if (!fStatus) |
1950 | { |
1951 | LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, changing to non-user code\n" , this)); |
1952 | _ASSERTE(pRuntimeModule->HasAnyJMCFunctions()); |
1953 | pRuntimeModule->DecJMCFuncCount(); |
1954 | } |
1955 | else |
1956 | { |
1957 | LOG((LF_CORDB,LL_EVERYTHING, "DMI::SetJMCStatus: %p, changing to user code\n" , this)); |
1958 | pRuntimeModule->IncJMCFuncCount(); |
1959 | _ASSERTE(pRuntimeModule->HasAnyJMCFunctions()); |
1960 | } |
1961 | |
1962 | m_fJMCStatus = fStatus; |
1963 | |
1964 | // We should update our module's JMC status... |
1965 | g_pDebugger->UpdateModuleJMCFlag(pRuntimeModule, DebuggerController::GetTotalMethodEnter() != 0); |
1966 | |
1967 | } |
1968 | |
1969 | // Get an iterator that will go through ALL native code-blobs (DJI) in the specified |
1970 | // AppDomain, optionally filtered by loader module (if pLoaderModuleFilter != NULL). |
1971 | // This is EnC/ Generics / Prejit aware. |
1972 | void DebuggerMethodInfo::IterateAllDJIs(AppDomain * pAppDomain, Module * pLoaderModuleFilter, MethodDesc * pMethodDescFilter, DebuggerMethodInfo::DJIIterator * pEnum) |
1973 | { |
1974 | CONTRACTL |
1975 | { |
1976 | SO_NOT_MAINLINE; |
1977 | THROWS; |
1978 | GC_NOTRIGGER; |
1979 | } |
1980 | CONTRACTL_END; |
1981 | |
1982 | _ASSERTE(pEnum != NULL); |
1983 | _ASSERTE(pAppDomain != NULL || pMethodDescFilter != NULL); |
1984 | |
1985 | // Esnure we have DJIs for everything. |
1986 | CreateDJIsForNativeBlobs(pAppDomain, pLoaderModuleFilter, pMethodDescFilter); |
1987 | |
1988 | pEnum->m_pCurrent = m_latestJitInfo; |
1989 | pEnum->m_pLoaderModuleFilter = pLoaderModuleFilter; |
1990 | pEnum->m_pMethodDescFilter = pMethodDescFilter; |
1991 | |
1992 | // Advance to the first DJI that passes the filter |
1993 | pEnum->Next(TRUE); |
1994 | } |
1995 | |
1996 | //--------------------------------------------------------------------------------------- |
1997 | // |
1998 | // Bring the DJI cache up to date. |
1999 | // |
2000 | // Arguments: |
2001 | // * pAppDomain - Create DJIs only for this AppDomain |
2002 | // * pLoaderModuleFilter - If non-NULL, create DJIs only for MethodDescs whose |
2003 | // loader module matches this one. (This can be different from m_module in the |
2004 | // case of generics defined in one module and instantiated in another). If |
2005 | // non-NULL, create DJIs for all modules in pAppDomain. |
2006 | // * pMethodDescFilter - If non-NULL, create DJIs only for this single MethodDesc. |
2007 | // |
2008 | |
2009 | void DebuggerMethodInfo::CreateDJIsForNativeBlobs(AppDomain * pAppDomain, Module * pLoaderModuleFilter, MethodDesc* pMethodDescFilter) |
2010 | { |
2011 | CONTRACTL |
2012 | { |
2013 | SO_NOT_MAINLINE; |
2014 | THROWS; |
2015 | GC_NOTRIGGER; |
2016 | } |
2017 | CONTRACTL_END; |
2018 | |
2019 | // If we're not stopped and the module we're iterating over allows types to load, |
2020 | // then it's possible new native blobs are being created underneath us. |
2021 | _ASSERTE(g_pDebugger->IsStopped() || |
2022 | ((pLoaderModuleFilter != NULL) && !pLoaderModuleFilter->IsReadyForTypeLoad()) || |
2023 | pMethodDescFilter != NULL); |
2024 | |
2025 | if (pMethodDescFilter != NULL) |
2026 | { |
2027 | CreateDJIsForMethodDesc(pMethodDescFilter); |
2028 | } |
2029 | else |
2030 | { |
2031 | // @todo - we really only need to do this if the stop-counter goes up (else we know nothing new is added). |
2032 | // B/c of generics, it's possible that new instantiations of a method may have been jitted. |
2033 | // So just loop through all known instantiations and ensure that we have all the DJIs. |
2034 | // Note that this iterator won't show previous EnC versions, but we're already guaranteed to |
2035 | // have DJIs for every verision of a method that was EnCed. |
2036 | // This also handles the possibility of getting the same methoddesc back from the iterator. |
2037 | // It also lets EnC + generics play nice together (including if an generic method was EnC-ed) |
2038 | LoadedMethodDescIterator it(pAppDomain, m_module, m_token); |
2039 | CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly; |
2040 | while (it.Next(pDomainAssembly.This())) |
2041 | { |
2042 | MethodDesc * pDesc = it.Current(); |
2043 | if (!pDesc->HasNativeCode()) |
2044 | { |
2045 | continue; |
2046 | } |
2047 | |
2048 | Module * pLoaderModule = pDesc->GetLoaderModule(); |
2049 | |
2050 | // Obey the module filter if it's provided |
2051 | if ((pLoaderModuleFilter != NULL) && (pLoaderModuleFilter != pLoaderModule)) |
2052 | continue; |
2053 | |
2054 | // Skip modules that are unloaded, but still hanging around. Note that we can't use DebuggerModule for this check |
2055 | // because of it is deleted pretty early during unloading, and we do not want to recreate it. |
2056 | if (pLoaderModule->GetLoaderAllocator()->IsUnloaded()) |
2057 | continue; |
2058 | |
2059 | CreateDJIsForMethodDesc(pDesc); |
2060 | } |
2061 | } |
2062 | } |
2063 | |
2064 | |
2065 | //--------------------------------------------------------------------------------------- |
2066 | // |
2067 | // Bring the DJI cache up to date for jitted code instances of a particular MethodDesc. |
2068 | // |
2069 | // |
2070 | void DebuggerMethodInfo::CreateDJIsForMethodDesc(MethodDesc * pMethodDesc) |
2071 | { |
2072 | CONTRACTL |
2073 | { |
2074 | SO_NOT_MAINLINE; |
2075 | THROWS; |
2076 | GC_NOTRIGGER; |
2077 | } |
2078 | CONTRACTL_END; |
2079 | |
2080 | |
2081 | // The debugger doesn't track Lightweight-codegen methods b/c they have no metadata. |
2082 | if (pMethodDesc->IsDynamicMethod()) |
2083 | { |
2084 | return; |
2085 | } |
2086 | |
2087 | #ifdef FEATURE_CODE_VERSIONING |
2088 | CodeVersionManager* pCodeVersionManager = pMethodDesc->GetCodeVersionManager(); |
2089 | // grab the code version lock to iterate available versions of the code |
2090 | { |
2091 | CodeVersionManager::TableLockHolder lock(pCodeVersionManager); |
2092 | NativeCodeVersionCollection nativeCodeVersions = pCodeVersionManager->GetNativeCodeVersions(pMethodDesc); |
2093 | |
2094 | for (NativeCodeVersionIterator itr = nativeCodeVersions.Begin(), end = nativeCodeVersions.End(); itr != end; itr++) |
2095 | { |
2096 | // Some versions may not be compiled yet - skip those for now |
2097 | // if they compile later the JitCompiled callback will add a DJI to our cache at that time |
2098 | PCODE codeAddr = itr->GetNativeCode(); |
2099 | if (codeAddr) |
2100 | { |
2101 | // The DJI may already be populated in the cache, if so CreateInitAndAdd is |
2102 | // a no-op and that is fine. |
2103 | BOOL unusedDjiWasCreated; |
2104 | CreateInitAndAddJitInfo(pMethodDesc, codeAddr, &unusedDjiWasCreated); |
2105 | } |
2106 | } |
2107 | } |
2108 | #else |
2109 | // We just ask for the DJI to ensure that it's lazily created. |
2110 | // This should only fail in an oom scenario. |
2111 | DebuggerJitInfo * djiTest = g_pDebugger->GetLatestJitInfoFromMethodDesc(pDesc); |
2112 | if (djiTest == NULL) |
2113 | { |
2114 | // We're oom. Give up. |
2115 | ThrowOutOfMemory(); |
2116 | return; |
2117 | } |
2118 | #endif |
2119 | } |
2120 | |
2121 | /* |
2122 | * GetLatestJitInfo |
2123 | * |
2124 | * This routine returns the lastest DJI we have for a particular DMI. |
2125 | * DJIs are lazily created. |
2126 | * Parameters: |
2127 | * None. |
2128 | * |
2129 | * Returns |
2130 | * a possibly NULL pointer to a DJI. |
2131 | * |
2132 | */ |
2133 | |
2134 | // For logging and other internal purposes, provide a non-initializing accessor. |
2135 | DebuggerJitInfo* DebuggerMethodInfo::GetLatestJitInfo_NoCreate() |
2136 | { |
2137 | return m_latestJitInfo; |
2138 | } |
2139 | |
2140 | |
2141 | DebuggerMethodInfoTable::DebuggerMethodInfoTable() : CHashTableAndData<CNewZeroData>(101) |
2142 | { |
2143 | CONTRACTL |
2144 | { |
2145 | WRAPPER(THROWS); |
2146 | GC_NOTRIGGER; |
2147 | |
2148 | CONSTRUCTOR_CHECK; |
2149 | } |
2150 | CONTRACTL_END; |
2151 | |
2152 | SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE; |
2153 | HRESULT hr = NewInit(101, sizeof(DebuggerMethodInfoEntry), 101); |
2154 | |
2155 | if (FAILED(hr)) |
2156 | { |
2157 | ThrowWin32(hr); |
2158 | } |
2159 | } |
2160 | |
2161 | HRESULT DebuggerMethodInfoTable::AddMethodInfo(Module *pModule, |
2162 | mdMethodDef token, |
2163 | DebuggerMethodInfo *mi) |
2164 | { |
2165 | CONTRACTL |
2166 | { |
2167 | THROWS; |
2168 | GC_NOTRIGGER; |
2169 | |
2170 | INSTANCE_CHECK; |
2171 | PRECONDITION(CheckPointer(pModule)); |
2172 | PRECONDITION(CheckPointer(mi)); |
2173 | } |
2174 | CONTRACTL_END; |
2175 | |
2176 | LOG((LF_CORDB, LL_INFO1000, "DMIT::AMI Adding dmi:0x%x Mod:0x%x tok:" |
2177 | "0x%x nVer:0x%x\n" , mi, pModule, token, mi->GetCurrentEnCVersion())); |
2178 | |
2179 | _ASSERTE(mi != NULL); |
2180 | |
2181 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
2182 | |
2183 | HRESULT hr = OverwriteMethodInfo(pModule, token, mi, TRUE); |
2184 | if (hr == S_OK) |
2185 | return hr; |
2186 | |
2187 | DebuggerMethodInfoKey dmik; |
2188 | dmik.pModule = pModule; |
2189 | dmik.token = token; |
2190 | |
2191 | DebuggerMethodInfoEntry *dmie = |
2192 | (DebuggerMethodInfoEntry *) Add(HASH(&dmik)); |
2193 | |
2194 | if (dmie != NULL) |
2195 | { |
2196 | dmie->key.pModule = pModule; |
2197 | dmie->key.token = token; |
2198 | dmie->mi = mi; |
2199 | |
2200 | LOG((LF_CORDB, LL_INFO1000, "DMIT::AJI: mod:0x%x tok:0%x " , |
2201 | pModule, token)); |
2202 | return S_OK; |
2203 | } |
2204 | |
2205 | ThrowOutOfMemory(); |
2206 | return S_OK; |
2207 | } |
2208 | |
2209 | HRESULT DebuggerMethodInfoTable::OverwriteMethodInfo(Module *pModule, |
2210 | mdMethodDef token, |
2211 | DebuggerMethodInfo *mi, |
2212 | BOOL fOnlyIfNull) |
2213 | { |
2214 | CONTRACTL |
2215 | { |
2216 | NOTHROW; |
2217 | GC_NOTRIGGER; |
2218 | |
2219 | PRECONDITION(CheckPointer(pModule)); |
2220 | PRECONDITION(CheckPointer(mi)); |
2221 | } |
2222 | CONTRACTL_END; |
2223 | |
2224 | LOG((LF_CORDB, LL_INFO1000, "DMIT::OJI: dmi:0x%x mod:0x%x tok:0x%x\n" , mi, |
2225 | pModule, token)); |
2226 | |
2227 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
2228 | |
2229 | DebuggerMethodInfoKey dmik; |
2230 | dmik.pModule = pModule; |
2231 | dmik.token = token; |
2232 | |
2233 | DebuggerMethodInfoEntry *entry |
2234 | = (DebuggerMethodInfoEntry *) Find(HASH(&dmik), KEY(&dmik)); |
2235 | if (entry != NULL) |
2236 | { |
2237 | if ( (fOnlyIfNull && |
2238 | entry->mi == NULL) || |
2239 | !fOnlyIfNull) |
2240 | { |
2241 | entry->mi = mi; |
2242 | |
2243 | LOG((LF_CORDB, LL_INFO1000, "DMIT::OJI: mod:0x%x tok:0x%x remap" |
2244 | "nVer:0x%x\n" , pModule, token, entry->nVersionLastRemapped)); |
2245 | return S_OK; |
2246 | } |
2247 | } |
2248 | |
2249 | return E_FAIL; |
2250 | } |
2251 | |
2252 | // pModule is being destroyed - remove any entries that belong to it. Why? |
2253 | // (a) Correctness: the module can be reloaded at the same address, |
2254 | // which will cause accidental matches with our hashtable (indexed by |
2255 | // {Module*,mdMethodDef} |
2256 | // (b) Perf: don't waste the memory! |
2257 | void DebuggerMethodInfoTable::ClearMethodsOfModule(Module *pModule) |
2258 | { |
2259 | WRAPPER_NO_CONTRACT; |
2260 | |
2261 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
2262 | |
2263 | LOG((LF_CORDB, LL_INFO1000000, "CMOM:mod:0x%x (%S)\n" , pModule |
2264 | ,pModule->GetDebugName())); |
2265 | |
2266 | HASHFIND info; |
2267 | |
2268 | DebuggerMethodInfoEntry *entry |
2269 | = (DebuggerMethodInfoEntry *) FindFirstEntry(&info); |
2270 | while(entry != NULL) |
2271 | { |
2272 | Module *pMod = entry->key.pModule ; |
2273 | if (pMod == pModule) |
2274 | { |
2275 | // This method actually got mitted, at least |
2276 | // once - remove all version info. |
2277 | while(entry->mi != NULL) |
2278 | { |
2279 | DeleteEntryDMI(entry); |
2280 | } |
2281 | |
2282 | Delete(HASH(&(entry->key)), (HASHENTRY*)entry); |
2283 | } |
2284 | else |
2285 | { |
2286 | // |
2287 | // Delete generic DJIs that have lifetime attached to this module |
2288 | // |
2289 | DebuggerMethodInfo * dmi = entry->mi; |
2290 | while (dmi != NULL) |
2291 | { |
2292 | DebuggerJitInfo * dji = dmi->GetLatestJitInfo_NoCreate(); |
2293 | while (dji != NULL) |
2294 | { |
2295 | DebuggerJitInfo * djiPrev = dji->m_prevJitInfo;; |
2296 | |
2297 | if (dji->m_pLoaderModule == pModule) |
2298 | dmi->DeleteJitInfo(dji); |
2299 | |
2300 | dji = djiPrev; |
2301 | } |
2302 | |
2303 | dmi = dmi->m_prevMethodInfo; |
2304 | } |
2305 | } |
2306 | |
2307 | entry = (DebuggerMethodInfoEntry *) FindNextEntry(&info); |
2308 | } |
2309 | } |
2310 | |
2311 | void DebuggerMethodInfoTable::DeleteEntryDMI(DebuggerMethodInfoEntry *entry) |
2312 | { |
2313 | CONTRACTL |
2314 | { |
2315 | NOTHROW; |
2316 | GC_NOTRIGGER; |
2317 | MODE_ANY; |
2318 | CAN_TAKE_LOCK; // DeleteInteropSafe() eventually calls DebuggerMethodInfo::DeleteJitInfoList |
2319 | // which locks. |
2320 | } |
2321 | CONTRACTL_END; |
2322 | |
2323 | DebuggerMethodInfo *dmiPrev = entry->mi->m_prevMethodInfo; |
2324 | TRACE_FREE(entry->mi); |
2325 | DeleteInteropSafe(entry->mi); |
2326 | entry->mi = dmiPrev; |
2327 | if ( dmiPrev != NULL ) |
2328 | dmiPrev->m_nextMethodInfo = NULL; |
2329 | } |
2330 | |
2331 | #endif // #ifndef DACCESS_COMPILE |
2332 | |
2333 | DebuggerJitInfo *DebuggerJitInfo::GetJitInfoByAddress(const BYTE *pbAddr ) |
2334 | { |
2335 | CONTRACTL |
2336 | { |
2337 | SO_INTOLERANT; |
2338 | NOTHROW; |
2339 | GC_NOTRIGGER; |
2340 | } |
2341 | CONTRACTL_END; |
2342 | |
2343 | DebuggerJitInfo *dji = this; |
2344 | |
2345 | #ifdef LOGGING |
2346 | LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA finding DJI " |
2347 | "corresponding to addr 0x%p, starting with 0x%p\n" , pbAddr, dji)); |
2348 | #endif //LOGGING |
2349 | |
2350 | // If it's not NULL, but not in the range m_addrOfCode to end of function, |
2351 | // then get the previous one. |
2352 | while( dji != NULL && |
2353 | !CodeRegionInfo::GetCodeRegionInfo(dji).IsMethodAddress(pbAddr)) |
2354 | { |
2355 | LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA: pbAddr 0x%p is not in code " |
2356 | "0x%p (size:0x%p)\n" , pbAddr, dji->m_addrOfCode, |
2357 | dji->m_sizeOfCode)); |
2358 | dji = dji->m_prevJitInfo; |
2359 | } |
2360 | |
2361 | #ifdef LOGGING |
2362 | if (dji == NULL) |
2363 | { |
2364 | LOG((LF_CORDB,LL_INFO10000,"DJI:GJIBA couldn't find a DJI " |
2365 | "corresponding to addr 0x%p\n" , pbAddr)); |
2366 | } |
2367 | #endif //LOGGING |
2368 | return dji; |
2369 | } |
2370 | |
2371 | PTR_DebuggerJitInfo DebuggerMethodInfo::GetLatestJitInfo(MethodDesc *mdesc) |
2372 | { |
2373 | // dac checks ngen'ed image content first, so |
2374 | // only check for existing JIT info. |
2375 | #ifndef DACCESS_COMPILE |
2376 | |
2377 | CONTRACTL |
2378 | { |
2379 | SO_INTOLERANT; |
2380 | THROWS; |
2381 | CALLED_IN_DEBUGGERDATALOCK_HOLDER_SCOPE_MAY_GC_TRIGGERS_CONTRACT; |
2382 | PRECONDITION(!g_pDebugger->HasDebuggerDataLock()); |
2383 | } |
2384 | CONTRACTL_END; |
2385 | |
2386 | |
2387 | if (m_latestJitInfo && m_latestJitInfo->m_fd == mdesc && !m_latestJitInfo->m_fd->HasClassOrMethodInstantiation()) |
2388 | return m_latestJitInfo; |
2389 | |
2390 | // This ensures that there is an entry in the DJI list for this particular MethodDesc. |
2391 | // in the case of generic code it may not be the first entry in the list. |
2392 | FindOrCreateInitAndAddJitInfo(mdesc, NULL /* startAddr */); |
2393 | |
2394 | #endif // #ifndef DACCESS_COMPILE |
2395 | |
2396 | return m_latestJitInfo; |
2397 | } |
2398 | |
2399 | DebuggerMethodInfo *DebuggerMethodInfoTable::GetMethodInfo(Module *pModule, mdMethodDef token) |
2400 | { |
2401 | WRAPPER_NO_CONTRACT; |
2402 | SUPPORTS_DAC; |
2403 | |
2404 | // CHECK_DMI_TABLE; |
2405 | |
2406 | // @review. One of the BVTs causes this to be called before the table is initialized |
2407 | // In particular, the changes to BREAKPOINT_ADD mean that this table is now consulted |
2408 | // to determine if we have ever seen the method, rather than a call to LookupMethodDesc, |
2409 | // which would have just returned NULL. In general it seems OK to consult this table |
2410 | // when it is empty, so I've added this.... |
2411 | if (this == NULL) |
2412 | return NULL; |
2413 | |
2414 | DebuggerMethodInfoKey dmik; |
2415 | dmik.pModule = dac_cast<PTR_Module>(pModule); |
2416 | dmik.token = token; |
2417 | |
2418 | DebuggerMethodInfoEntry *entry = dac_cast<PTR_DebuggerMethodInfoEntry>(Find(HASH(&dmik), KEY(&dmik))); |
2419 | |
2420 | if (entry == NULL ) |
2421 | { |
2422 | return NULL; |
2423 | } |
2424 | else |
2425 | { |
2426 | LOG((LF_CORDB, LL_INFO1000, "DMI::GMI: for methodDef 0x%x, got 0x%x prev:0x%x\n" , |
2427 | token, entry->mi, (entry->mi?entry->mi->m_prevMethodInfo:0))); |
2428 | return entry->mi; |
2429 | } |
2430 | } |
2431 | |
2432 | |
2433 | DebuggerMethodInfo *DebuggerMethodInfoTable::GetFirstMethodInfo(HASHFIND *info) |
2434 | { |
2435 | CONTRACT(DebuggerMethodInfo*) |
2436 | { |
2437 | NOTHROW; |
2438 | GC_NOTRIGGER; |
2439 | |
2440 | PRECONDITION(CheckPointer(info)); |
2441 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
2442 | } |
2443 | CONTRACT_END; |
2444 | |
2445 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
2446 | |
2447 | DebuggerMethodInfoEntry *entry = PTR_DebuggerMethodInfoEntry |
2448 | (PTR_HOST_TO_TADDR(FindFirstEntry(info))); |
2449 | if (entry == NULL) |
2450 | RETURN NULL; |
2451 | else |
2452 | RETURN entry->mi; |
2453 | } |
2454 | |
2455 | DebuggerMethodInfo *DebuggerMethodInfoTable::GetNextMethodInfo(HASHFIND *info) |
2456 | { |
2457 | CONTRACT(DebuggerMethodInfo*) |
2458 | { |
2459 | NOTHROW; |
2460 | GC_NOTRIGGER; |
2461 | |
2462 | PRECONDITION(CheckPointer(info)); |
2463 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
2464 | } |
2465 | CONTRACT_END; |
2466 | |
2467 | _ASSERTE(g_pDebugger->HasDebuggerDataLock()); |
2468 | |
2469 | DebuggerMethodInfoEntry *entry = PTR_DebuggerMethodInfoEntry |
2470 | (PTR_HOST_TO_TADDR(FindNextEntry(info))); |
2471 | |
2472 | // We may have incremented the version number |
2473 | // for methods that never got JITted, so we should |
2474 | // pretend like they don't exist here. |
2475 | while (entry != NULL && |
2476 | entry->mi == NULL) |
2477 | { |
2478 | entry = PTR_DebuggerMethodInfoEntry |
2479 | (PTR_HOST_TO_TADDR(FindNextEntry(info))); |
2480 | } |
2481 | |
2482 | if (entry == NULL) |
2483 | RETURN NULL; |
2484 | else |
2485 | RETURN entry->mi; |
2486 | } |
2487 | |
2488 | |
2489 | |
2490 | #ifdef DACCESS_COMPILE |
2491 | void |
2492 | DebuggerMethodInfoEntry::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2493 | { |
2494 | SUPPORTS_DAC; |
2495 | |
2496 | // This structure is in an array in the hash |
2497 | // so the 'this' is implicitly enumerated by the |
2498 | // array enum in CHashTable. |
2499 | |
2500 | // For a MiniDumpNormal, what is needed for modules is already enumerated elsewhere. |
2501 | // Don't waste time doing it here an extra time. Also, this will add many MB extra into the dump. |
2502 | if ((key.pModule.IsValid()) && |
2503 | CLRDATA_ENUM_MEM_MINI != flags |
2504 | && CLRDATA_ENUM_MEM_TRIAGE != flags) |
2505 | { |
2506 | key.pModule->EnumMemoryRegions(flags, true); |
2507 | } |
2508 | |
2509 | while (mi.IsValid()) |
2510 | { |
2511 | mi->EnumMemoryRegions(flags); |
2512 | mi = mi->m_prevMethodInfo; |
2513 | } |
2514 | } |
2515 | |
2516 | void |
2517 | DebuggerMethodInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2518 | { |
2519 | DAC_ENUM_DTHIS(); |
2520 | SUPPORTS_DAC; |
2521 | |
2522 | if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) |
2523 | { |
2524 | // Modules are enumerated already for minidumps, save the empty calls. |
2525 | if (m_module.IsValid()) |
2526 | { |
2527 | m_module->EnumMemoryRegions(flags, true); |
2528 | } |
2529 | |
2530 | } |
2531 | |
2532 | PTR_DebuggerJitInfo jitInfo = m_latestJitInfo; |
2533 | while (jitInfo.IsValid()) |
2534 | { |
2535 | jitInfo->EnumMemoryRegions(flags); |
2536 | jitInfo = jitInfo->m_prevJitInfo; |
2537 | } |
2538 | } |
2539 | |
2540 | void |
2541 | DebuggerJitInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2542 | { |
2543 | DAC_ENUM_DTHIS(); |
2544 | SUPPORTS_DAC; |
2545 | |
2546 | if (m_methodInfo.IsValid()) |
2547 | { |
2548 | m_methodInfo->EnumMemoryRegions(flags); |
2549 | } |
2550 | |
2551 | if (flags != CLRDATA_ENUM_MEM_MINI && flags != CLRDATA_ENUM_MEM_TRIAGE) |
2552 | { |
2553 | if (m_fd.IsValid()) |
2554 | { |
2555 | m_fd->EnumMemoryRegions(flags); |
2556 | } |
2557 | |
2558 | DacEnumMemoryRegion(PTR_TO_TADDR(GetSequenceMap()), |
2559 | GetSequenceMapCount() * sizeof(DebuggerILToNativeMap)); |
2560 | DacEnumMemoryRegion(PTR_TO_TADDR(GetVarNativeInfo()), |
2561 | GetVarNativeInfoCount() * |
2562 | sizeof(ICorDebugInfo::NativeVarInfo)); |
2563 | } |
2564 | } |
2565 | |
2566 | |
2567 | void DebuggerMethodInfoTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
2568 | { |
2569 | WRAPPER_NO_CONTRACT; |
2570 | |
2571 | DAC_ENUM_VTHIS(); |
2572 | CHashTableAndData<CNewZeroData>::EnumMemoryRegions(flags); |
2573 | |
2574 | for (ULONG i = 0; i < m_iEntries; i++) |
2575 | { |
2576 | DebuggerMethodInfoEntry* entry = |
2577 | PTR_DebuggerMethodInfoEntry(PTR_HOST_TO_TADDR(EntryPtr(i))); |
2578 | entry->EnumMemoryRegions(flags); |
2579 | } |
2580 | } |
2581 | #endif // #ifdef DACCESS_COMPILE |
2582 | |