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 | // RsStackWalk.cpp |
6 | // |
7 | |
8 | // |
9 | // This file contains the implementation of the V3 managed stackwalking API. |
10 | // |
11 | // ====================================================================================== |
12 | |
13 | #include "stdafx.h" |
14 | #include "primitives.h" |
15 | |
16 | |
17 | //--------------------------------------------------------------------------------------- |
18 | // |
19 | // Constructor for CordbStackWalk. |
20 | // |
21 | // Arguments: |
22 | // pCordbThread - the thread on which this stackwalker is created |
23 | // |
24 | |
25 | CordbStackWalk::CordbStackWalk(CordbThread * pCordbThread) |
26 | : CordbBase(pCordbThread->GetProcess(), 0, enumCordbStackWalk), |
27 | m_pCordbThread(pCordbThread), |
28 | m_pSFIHandle(NULL), |
29 | m_cachedSetContextFlag(SET_CONTEXT_FLAG_ACTIVE_FRAME), |
30 | m_cachedHR(S_OK), |
31 | m_fIsOneFrameAhead(false) |
32 | { |
33 | m_pCachedFrame.Clear(); |
34 | } |
35 | |
36 | void CordbStackWalk::Init() |
37 | { |
38 | CordbProcess * pProcess = GetProcess(); |
39 | m_lastSyncFlushCounter = pProcess->m_flushCounter; |
40 | |
41 | IDacDbiInterface * pDAC = pProcess->GetDAC(); |
42 | pDAC->CreateStackWalk(m_pCordbThread->m_vmThreadToken, |
43 | &m_context, |
44 | &m_pSFIHandle); |
45 | |
46 | // see the function header of code:CordbStackWalk::CheckForLegacyHijackCase |
47 | CheckForLegacyHijackCase(); |
48 | |
49 | // Add itself to the neuter list. |
50 | m_pCordbThread->GetRefreshStackNeuterList()->Add(GetProcess(), this); |
51 | } |
52 | |
53 | // ---------------------------------------------------------------------------- |
54 | // CordbStackWalk::CheckForLegacyHijackCase |
55 | // |
56 | // Description: |
57 | // @dbgtodo legacy interop debugging - In the case of an unhandled hardware exception, the |
58 | // thread will be hijacked to code:Debugger::GenericHijackFunc, which the stackwalker doesn't know how to |
59 | // unwind. We can teach the stackwalker to recognize that hijack stub, but since it's going to be deprecated |
60 | // anyway, it's not worth the effort. So we check for the hijack CONTEXT here and use it as the CONTEXT. This |
61 | // check should be removed when we are completely |
62 | // out-of-process. |
63 | // |
64 | |
65 | void CordbStackWalk::CheckForLegacyHijackCase() |
66 | { |
67 | #if defined(FEATURE_INTEROP_DEBUGGING) |
68 | CordbProcess * pProcess = GetProcess(); |
69 | |
70 | // Only do this if we have a shim and we are interop-debugging. |
71 | if ((pProcess->GetShim() != NULL) && |
72 | pProcess->IsInteropDebugging()) |
73 | { |
74 | // And only if we have a CordbUnmanagedThread and we are hijacked to code:Debugger::GenericHijackFunc |
75 | CordbUnmanagedThread * pUT = pProcess->GetUnmanagedThread(m_pCordbThread->GetVolatileOSThreadID()); |
76 | if (pUT != NULL) |
77 | { |
78 | if (pUT->IsFirstChanceHijacked() || pUT->IsGenericHijacked()) |
79 | { |
80 | // The GetThreadContext function hides the effects of hijacking and returns the unhijacked context |
81 | m_context.ContextFlags = DT_CONTEXT_FULL; |
82 | pUT->GetThreadContext(&m_context); |
83 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
84 | pDAC->SetStackWalkCurrentContext(m_pCordbThread->m_vmThreadToken, |
85 | m_pSFIHandle, |
86 | SET_CONTEXT_FLAG_ACTIVE_FRAME, |
87 | &m_context); |
88 | } |
89 | } |
90 | } |
91 | #endif // FEATURE_INTEROP_DEBUGGING |
92 | } |
93 | |
94 | //--------------------------------------------------------------------------------------- |
95 | // |
96 | // Destructor for CordbStackWalk. |
97 | // |
98 | // Notes: |
99 | // We don't really need to do anything here since the CordbStackWalk should have been neutered already. |
100 | // |
101 | |
102 | CordbStackWalk::~CordbStackWalk() |
103 | { |
104 | _ASSERTE(IsNeutered()); |
105 | } |
106 | |
107 | //--------------------------------------------------------------------------------------- |
108 | // |
109 | // This function resets all the state on a CordbStackWalk and releases all the memory. |
110 | // It is used for neutering and refreshing. |
111 | // |
112 | |
113 | void CordbStackWalk::DeleteAll() |
114 | { |
115 | _ASSERTE(GetProcess()->GetProcessLock()->HasLock()); |
116 | |
117 | // delete allocated memory |
118 | if (m_pSFIHandle) |
119 | { |
120 | HRESULT hr = S_OK; |
121 | EX_TRY |
122 | { |
123 | #if defined(FEATURE_DBGIPC_TRANSPORT_DI) |
124 | // For Mac debugging, it's not safe to call into the DAC once |
125 | // code:INativeEventPipeline::TerminateProcess is called. This is because the transport will not |
126 | // work anymore. The sole purpose of calling DeleteStackWalk() is to release the resources and |
127 | // memory allocated for the stackwalk. In the remote debugging case, the memory is allocated in |
128 | // the debuggee process. If the process is already terminated, then it's ok to skip the call. |
129 | if (!GetProcess()->m_exiting) |
130 | #endif // FEATURE_DBGIPC_TRANSPORT_DI |
131 | { |
132 | // This Delete call shouldn't actually throw. Worst case, the DDImpl leaked memory. |
133 | GetProcess()->GetDAC()->DeleteStackWalk(m_pSFIHandle); |
134 | } |
135 | } |
136 | EX_CATCH_HRESULT(hr); |
137 | SIMPLIFYING_ASSUMPTION_SUCCEEDED(hr); |
138 | m_pSFIHandle = NULL; |
139 | } |
140 | |
141 | // clear out the cached frame |
142 | m_pCachedFrame.Clear(); |
143 | m_cachedHR = S_OK; |
144 | m_fIsOneFrameAhead = false; |
145 | } |
146 | |
147 | //--------------------------------------------------------------------------------------- |
148 | // |
149 | // Release all memory used by the stackwalker. |
150 | // |
151 | // |
152 | // Notes: |
153 | // CordbStackWalk is neutered by CordbThread or CleanupStack(). |
154 | // |
155 | |
156 | void CordbStackWalk::Neuter() |
157 | { |
158 | if (IsNeutered()) |
159 | { |
160 | return; |
161 | } |
162 | |
163 | DeleteAll(); |
164 | CordbBase::Neuter(); |
165 | } |
166 | |
167 | // standard QI function |
168 | HRESULT CordbStackWalk::QueryInterface(REFIID id, void **pInterface) |
169 | { |
170 | if (id == IID_ICorDebugStackWalk) |
171 | { |
172 | *pInterface = static_cast<ICorDebugStackWalk*>(this); |
173 | } |
174 | else if (id == IID_IUnknown) |
175 | { |
176 | *pInterface = static_cast<IUnknown*>(static_cast<ICorDebugStackWalk*>(this)); |
177 | } |
178 | else |
179 | { |
180 | *pInterface = NULL; |
181 | return E_NOINTERFACE; |
182 | } |
183 | |
184 | ExternalAddRef(); |
185 | return S_OK; |
186 | } |
187 | |
188 | //--------------------------------------------------------------------------------------- |
189 | // |
190 | // Refreshes all the state stored on the CordbStackWalk. This is necessary because sending IPC events to |
191 | // the LS flushes the DAC cache, and m_pSFIHandle is allocated entirely in DAC memory. So, we keep track |
192 | // of whether we have sent an IPC event and refresh the CordbStackWalk if necessary. |
193 | // |
194 | // Notes: |
195 | // Throws on error. |
196 | // |
197 | |
198 | void CordbStackWalk::RefreshIfNeeded() |
199 | { |
200 | CordbProcess * pProcess = GetProcess(); |
201 | _ASSERTE(pProcess->GetProcessLock()->HasLock()); |
202 | |
203 | // check if we need to refresh |
204 | if (m_lastSyncFlushCounter != pProcess->m_flushCounter) |
205 | { |
206 | // Make a local copy of the CONTEXT here. DeleteAll() will delete the CONTEXT on the cached frame, |
207 | // and CreateStackWalk() actually uses the CONTEXT buffer we pass to it. |
208 | DT_CONTEXT ctx; |
209 | if (m_fIsOneFrameAhead) |
210 | { |
211 | ctx = *(m_pCachedFrame->GetContext()); |
212 | } |
213 | else |
214 | { |
215 | ctx = m_context; |
216 | } |
217 | |
218 | // clear all the state |
219 | DeleteAll(); |
220 | |
221 | // create a new stackwalk handle |
222 | pProcess->GetDAC()->CreateStackWalk(m_pCordbThread->m_vmThreadToken, |
223 | &m_context, |
224 | &m_pSFIHandle); |
225 | |
226 | // advance the stackwalker to where we originally were |
227 | SetContextWorker(m_cachedSetContextFlag, sizeof(DT_CONTEXT), reinterpret_cast<BYTE *>(&ctx)); |
228 | |
229 | // update the sync counter |
230 | m_lastSyncFlushCounter = pProcess->m_flushCounter; |
231 | } |
232 | } // CordbStackWalk::RefreshIfNeeded() |
233 | |
234 | //--------------------------------------------------------------------------------------- |
235 | // |
236 | // Retrieves the CONTEXT of the current frame. |
237 | // |
238 | // Arguments: |
239 | // contextFlags - context flags used to determine the required size for the buffer |
240 | // contextBufSize - size of the CONTEXT buffer |
241 | // pContextSize - out parameter; returns the size required for the CONTEXT buffer |
242 | // pbContextBuf - the CONTEXT buffer |
243 | // |
244 | // Return Value: |
245 | // Return S_OK on success. |
246 | // Return CORDBG_E_PAST_END_OF_STACK if we are already at the end of the stack. |
247 | // Return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) if the buffer is too small. |
248 | // Return E_FAIL on other failures. |
249 | // |
250 | |
251 | HRESULT CordbStackWalk::GetContext(ULONG32 contextFlags, |
252 | ULONG32 contextBufSize, |
253 | ULONG32 * pContextSize, |
254 | BYTE pbContextBuf[]) |
255 | { |
256 | HRESULT hr = S_OK; |
257 | PUBLIC_REENTRANT_API_BEGIN(this) |
258 | { |
259 | RefreshIfNeeded(); |
260 | |
261 | // set the required size for the CONTEXT buffer |
262 | if (pContextSize != NULL) |
263 | { |
264 | *pContextSize = ContextSizeForFlags(contextFlags); |
265 | } |
266 | |
267 | // If all the user wants to know is the CONTEXT size, then we are done. |
268 | if ((contextBufSize != 0) && (pbContextBuf != NULL)) |
269 | { |
270 | if (contextBufSize < 4) |
271 | { |
272 | ThrowWin32(ERROR_INSUFFICIENT_BUFFER); |
273 | } |
274 | |
275 | DT_CONTEXT * pContext = reinterpret_cast<DT_CONTEXT *>(pbContextBuf); |
276 | |
277 | // Some helper functions that examine the context expect the flags to be initialized. |
278 | pContext->ContextFlags = contextFlags; |
279 | |
280 | // check the size of the incoming buffer |
281 | if (!CheckContextSizeForBuffer(contextBufSize, pbContextBuf)) |
282 | { |
283 | ThrowWin32(ERROR_INSUFFICIENT_BUFFER); |
284 | } |
285 | |
286 | // Check if we are one frame ahead. If so, returned the CONTEXT on the cached frame. |
287 | if (m_fIsOneFrameAhead) |
288 | { |
289 | if (m_pCachedFrame != NULL) |
290 | { |
291 | const DT_CONTEXT * pSrcContext = m_pCachedFrame->GetContext(); |
292 | _ASSERTE(pSrcContext); |
293 | CORDbgCopyThreadContext(pContext, pSrcContext); |
294 | } |
295 | else |
296 | { |
297 | // We encountered a problem when we were trying to initialize the CordbNativeFrame. |
298 | // However, the problem occurred after we have unwound the current frame. |
299 | // What do we do here? We don't have the CONTEXT anymore. |
300 | _ASSERTE(FAILED(m_cachedHR)); |
301 | ThrowHR(m_cachedHR); |
302 | } |
303 | } |
304 | else |
305 | { |
306 | // No easy way out in this case. We have to call the DDI. |
307 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
308 | |
309 | IDacDbiInterface::FrameType ft = pDAC->GetStackWalkCurrentFrameInfo(m_pSFIHandle, NULL); |
310 | if (ft == IDacDbiInterface::kInvalid) |
311 | { |
312 | ThrowHR(E_FAIL); |
313 | } |
314 | else if (ft == IDacDbiInterface::kAtEndOfStack) |
315 | { |
316 | ThrowHR(CORDBG_E_PAST_END_OF_STACK); |
317 | } |
318 | else if (ft == IDacDbiInterface::kExplicitFrame) |
319 | { |
320 | ThrowHR(CORDBG_E_NO_CONTEXT_FOR_INTERNAL_FRAME); |
321 | } |
322 | else |
323 | { |
324 | // We always store the current CONTEXT, so just copy it into the buffer. |
325 | CORDbgCopyThreadContext(pContext, &m_context); |
326 | } |
327 | } |
328 | } |
329 | } |
330 | PUBLIC_REENTRANT_API_END(hr); |
331 | return hr; |
332 | } |
333 | |
334 | //--------------------------------------------------------------------------------------- |
335 | // |
336 | // Set the stackwalker to the specified CONTEXT. |
337 | // |
338 | // Arguments: |
339 | // flag - context flags used to determine the size of the CONTEXT |
340 | // contextSize - the size of the CONTEXT |
341 | // context - the CONTEXT as a byte array |
342 | // |
343 | // Return Value: |
344 | // Return S_OK on success. |
345 | // Return E_INVALIDARG if context is NULL |
346 | // Return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) if the CONTEXT is too small. |
347 | // Return E_FAIL on other failures. |
348 | // |
349 | |
350 | HRESULT CordbStackWalk::SetContext(CorDebugSetContextFlag flag, ULONG32 contextSize, BYTE context[]) |
351 | { |
352 | HRESULT hr = S_OK; |
353 | PUBLIC_REENTRANT_API_BEGIN(this) |
354 | { |
355 | RefreshIfNeeded(); |
356 | SetContextWorker(flag, contextSize, context); |
357 | } |
358 | PUBLIC_REENTRANT_API_END(hr); |
359 | return hr; |
360 | } |
361 | |
362 | //--------------------------------------------------------------------------------------- |
363 | // |
364 | // Refer to the comment for code:CordbStackWalk::SetContext |
365 | // |
366 | |
367 | void CordbStackWalk::SetContextWorker(CorDebugSetContextFlag flag, ULONG32 contextSize, BYTE context[]) |
368 | { |
369 | if (context == NULL) |
370 | { |
371 | ThrowHR(E_INVALIDARG); |
372 | } |
373 | |
374 | if (!CheckContextSizeForBuffer(contextSize, context)) |
375 | { |
376 | ThrowWin32(ERROR_INSUFFICIENT_BUFFER); |
377 | } |
378 | |
379 | // invalidate the cache |
380 | m_pCachedFrame.Clear(); |
381 | m_cachedHR = S_OK; |
382 | m_fIsOneFrameAhead = false; |
383 | |
384 | DT_CONTEXT * pSrcContext = reinterpret_cast<DT_CONTEXT *>(context); |
385 | |
386 | // Check the incoming CONTEXT using a temporary CONTEXT buffer before updating our real CONTEXT buffer. |
387 | // The incoming CONTEXT is not required to have all the bits set in its CONTEXT flags, so only update |
388 | // the registers specified by the CONTEXT flags. Note that CORDbgCopyThreadContext() honours the CONTEXT |
389 | // flags on both the source and the destination CONTEXTs when it copies them. |
390 | DT_CONTEXT tmpCtx = m_context; |
391 | tmpCtx.ContextFlags |= pSrcContext->ContextFlags; |
392 | CORDbgCopyThreadContext(&tmpCtx, pSrcContext); |
393 | |
394 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
395 | IfFailThrow(pDAC->CheckContext(m_pCordbThread->m_vmThreadToken, &tmpCtx)); |
396 | |
397 | // At this point we have done all of our checks to verify that the incoming CONTEXT is sane, so we can |
398 | // update our internal CONTEXT buffer. |
399 | m_context = tmpCtx; |
400 | m_cachedSetContextFlag = flag; |
401 | |
402 | pDAC->SetStackWalkCurrentContext(m_pCordbThread->m_vmThreadToken, |
403 | m_pSFIHandle, |
404 | flag, |
405 | &m_context); |
406 | } |
407 | |
408 | //--------------------------------------------------------------------------------------- |
409 | // |
410 | // Helper to perform all the necessary operations when we unwind, including: |
411 | // 1) Unwind |
412 | // 2) Save the new unwound CONTEXT |
413 | // |
414 | // Return Value: |
415 | // Return TRUE if we successfully unwind to the next frame. |
416 | // Return FALSE if there is no more frame to walk. |
417 | // Throw on error. |
418 | // |
419 | |
420 | BOOL CordbStackWalk::UnwindStackFrame() |
421 | { |
422 | CordbProcess * pProcess = GetProcess(); |
423 | _ASSERTE(pProcess->GetProcessLock()->HasLock()); |
424 | |
425 | IDacDbiInterface * pDAC = pProcess->GetDAC(); |
426 | BOOL retVal = pDAC->UnwindStackWalkFrame(m_pSFIHandle); |
427 | |
428 | // Now that we have unwound, make sure we update the CONTEXT buffer to reflect the current stack frame. |
429 | // This call is safe regardless of whether the unwind is successful or not. |
430 | pDAC->GetStackWalkCurrentContext(m_pSFIHandle, &m_context); |
431 | |
432 | return retVal; |
433 | } // CordbStackWalk::UnwindStackWalkFrame |
434 | |
435 | //--------------------------------------------------------------------------------------- |
436 | // |
437 | // Unwind the stackwalker to the next frame. |
438 | // |
439 | // Return Value: |
440 | // Return S_OK on success. |
441 | // Return CORDBG_E_FAIL_TO_UNWIND_FRAME if the unwind fails. |
442 | // Return CORDBG_S_AT_END_OF_STACK if we have reached the end of the stack as a result of this unwind. |
443 | // Return CORDBG_E_PAST_END_OF_STACK if we are already at the end of the stack to begin with. |
444 | // |
445 | |
446 | HRESULT CordbStackWalk::Next() |
447 | { |
448 | HRESULT hr = S_OK; |
449 | PUBLIC_REENTRANT_API_BEGIN(this) |
450 | { |
451 | RefreshIfNeeded(); |
452 | if (m_fIsOneFrameAhead) |
453 | { |
454 | // We have already unwound to the next frame when we materialize the CordbNativeFrame |
455 | // for the current frame. So we just need to clear the cache because we are already at |
456 | // the next frame. |
457 | if (m_pCachedFrame != NULL) |
458 | { |
459 | m_pCachedFrame.Clear(); |
460 | } |
461 | m_cachedHR = S_OK; |
462 | m_fIsOneFrameAhead = false; |
463 | } |
464 | else |
465 | { |
466 | IDacDbiInterface * pDAC = GetProcess()->GetDAC(); |
467 | IDacDbiInterface::FrameType ft = IDacDbiInterface::kInvalid; |
468 | |
469 | ft = pDAC->GetStackWalkCurrentFrameInfo(this->m_pSFIHandle, NULL); |
470 | if (ft == IDacDbiInterface::kAtEndOfStack) |
471 | { |
472 | ThrowHR(CORDBG_E_PAST_END_OF_STACK); |
473 | } |
474 | |
475 | // update the cahced flag to indicate that we have reached an unwind CONTEXT |
476 | m_cachedSetContextFlag = SET_CONTEXT_FLAG_UNWIND_FRAME; |
477 | |
478 | if (UnwindStackFrame()) |
479 | { |
480 | hr = S_OK; |
481 | } |
482 | else |
483 | { |
484 | hr = CORDBG_S_AT_END_OF_STACK; |
485 | } |
486 | } |
487 | } |
488 | PUBLIC_REENTRANT_API_END(hr); |
489 | return hr; |
490 | } |
491 | |
492 | //--------------------------------------------------------------------------------------- |
493 | // |
494 | // Retrieves an ICDFrame corresponding to the current frame: |
495 | // Stopped At Out Parameter Return Value |
496 | // ---------- ------------- ------------ |
497 | // explicit frame CordbInternalFrame S_OK |
498 | // managed stack frame CordbNativeFrame S_OK |
499 | // native stack frame NULL S_FALSE |
500 | // |
501 | // Arguments: |
502 | // ppFrame - out parameter; return the ICDFrame |
503 | // |
504 | // Return Value: |
505 | // On success return the HRs above. |
506 | // Return CORDBG_E_PAST_END_OF_STACK if we are already at the end of the stack. |
507 | // Return E_INVALIDARG if ppFrame is NULL |
508 | // Return E_FAIL on other errors. |
509 | // |
510 | // Notes: |
511 | // This is just a wrapper with an EX_TRY/EX_CATCH_HRESULT for GetFrameWorker(). |
512 | // |
513 | |
514 | HRESULT CordbStackWalk::GetFrame(ICorDebugFrame ** ppFrame) |
515 | { |
516 | HRESULT hr = S_OK; |
517 | PUBLIC_REENTRANT_API_NO_LOCK_BEGIN(this) |
518 | { |
519 | ATT_REQUIRE_STOPPED_MAY_FAIL_OR_THROW(GetProcess(), ThrowHR); |
520 | RSLockHolder lockHolder(GetProcess()->GetProcessLock()); |
521 | |
522 | RefreshIfNeeded(); |
523 | hr = GetFrameWorker(ppFrame); |
524 | } |
525 | PUBLIC_REENTRANT_API_END(hr); |
526 | |
527 | if (FAILED(hr)) |
528 | { |
529 | if (m_fIsOneFrameAhead && (m_pCachedFrame == NULL)) |
530 | { |
531 | // We encountered a problem when we try to materialize a CordbNativeFrame. |
532 | // Cache the failure HR so that we can return it later if the caller |
533 | // calls GetFrame() again or GetContext(). |
534 | m_cachedHR = hr; |
535 | } |
536 | } |
537 | |
538 | return hr; |
539 | } |
540 | |
541 | //--------------------------------------------------------------------------------------- |
542 | // |
543 | // Refer to the comment for code:CordbStackWalk::GetFrame |
544 | // |
545 | |
546 | HRESULT CordbStackWalk::GetFrameWorker(ICorDebugFrame ** ppFrame) |
547 | { |
548 | _ASSERTE(GetProcess()->GetProcessLock()->HasLock()); |
549 | |
550 | if (ppFrame == NULL) |
551 | { |
552 | ThrowHR(E_INVALIDARG); |
553 | } |
554 | *ppFrame = NULL; |
555 | |
556 | RSInitHolder<CordbFrame> pResultFrame(NULL); |
557 | |
558 | if (m_fIsOneFrameAhead) |
559 | { |
560 | if (m_pCachedFrame != NULL) |
561 | { |
562 | pResultFrame.Assign(m_pCachedFrame); |
563 | pResultFrame.TransferOwnershipExternal(ppFrame); |
564 | return S_OK; |
565 | } |
566 | else |
567 | { |
568 | // We encountered a problem when we were trying to initialize the CordbNativeFrame. |
569 | // However, the problem occurred after we have unwound the current frame. |
570 | // Whatever error code we return, it should be the same one GetContext() returns. |
571 | _ASSERTE(FAILED(m_cachedHR)); |
572 | ThrowHR(m_cachedHR); |
573 | } |
574 | } |
575 | |
576 | IDacDbiInterface * pDAC = NULL; |
577 | DebuggerIPCE_STRData frameData; |
578 | ZeroMemory(&frameData, sizeof(frameData)); |
579 | IDacDbiInterface::FrameType ft = IDacDbiInterface::kInvalid; |
580 | |
581 | pDAC = GetProcess()->GetDAC(); |
582 | ft = pDAC->GetStackWalkCurrentFrameInfo(m_pSFIHandle, &frameData); |
583 | |
584 | if (ft == IDacDbiInterface::kInvalid) |
585 | { |
586 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CSW::GFW - invalid stackwalker (%p)" , this); |
587 | ThrowHR(E_FAIL); |
588 | } |
589 | else if (ft == IDacDbiInterface::kAtEndOfStack) |
590 | { |
591 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CSW::GFW - past end of stack (%p)" , this); |
592 | ThrowHR(CORDBG_E_PAST_END_OF_STACK); |
593 | } |
594 | else if (ft == IDacDbiInterface::kNativeStackFrame) |
595 | { |
596 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CSW::GFW - native stack frame (%p)" , this); |
597 | return S_FALSE; |
598 | } |
599 | else if (ft == IDacDbiInterface::kExplicitFrame) |
600 | { |
601 | STRESS_LOG1(LF_CORDB, LL_INFO1000, "CSW::GFW - explicit frame (%p)" , this); |
602 | |
603 | // We no longer expect to get internal frames by unwinding. |
604 | GetProcess()->TargetConsistencyCheck(false); |
605 | } |
606 | else if (ft == IDacDbiInterface::kManagedStackFrame) |
607 | { |
608 | _ASSERTE(frameData.eType == DebuggerIPCE_STRData::cMethodFrame); |
609 | |
610 | HRESULT hr = S_OK; |
611 | |
612 | // In order to find the FramePointer on x86, we need to unwind to the next frame. |
613 | // Technically, only x86 needs to do this, because the x86 runtime stackwalker doesn't uwnind |
614 | // one frame ahead of time. However, we are doing this on all platforms to keep things simple. |
615 | BOOL fSuccess = UnwindStackFrame(); |
616 | (void)fSuccess; //prevent "unused variable" error from GCC |
617 | _ASSERTE(fSuccess); |
618 | |
619 | m_fIsOneFrameAhead = true; |
620 | #if defined(DBG_TARGET_X86) |
621 | frameData.fp = pDAC->GetFramePointer(m_pSFIHandle); |
622 | #endif // DBG_TARGET_X86 |
623 | |
624 | // currentFuncData contains general information about the method. |
625 | // It has no information about any particular jitted instance of the method. |
626 | DebuggerIPCE_FuncData * pFuncData = &(frameData.v.funcData); |
627 | |
628 | // currentJITFuncData contains information about the current jitted instance of the method |
629 | // on the stack. |
630 | DebuggerIPCE_JITFuncData * pJITFuncData = &(frameData.v.jitFuncData); |
631 | |
632 | // Lookup the appdomain that the thread was in when it was executing code for this frame. We pass this |
633 | // to the frame when we create it so we can properly resolve locals in that frame later. |
634 | CordbAppDomain * pCurrentAppDomain = GetProcess()->LookupOrCreateAppDomain(frameData.vmCurrentAppDomainToken); |
635 | _ASSERTE(pCurrentAppDomain != NULL); |
636 | |
637 | // Lookup the module |
638 | CordbModule* pModule = pCurrentAppDomain->LookupOrCreateModule(pFuncData->vmDomainFile); |
639 | PREFIX_ASSUME(pModule != NULL); |
640 | |
641 | // Create or look up a CordbNativeCode. There is one for each jitted instance of a method, |
642 | // and we may have multiple instances because of generics. |
643 | CordbNativeCode * pNativeCode = pModule->LookupOrCreateNativeCode(pFuncData->funcMetadataToken, |
644 | pJITFuncData->vmNativeCodeMethodDescToken, |
645 | pJITFuncData->nativeStartAddressPtr); |
646 | IfFailThrow(hr); |
647 | |
648 | // The native code object will create the function object if needed |
649 | CordbFunction * pFunction = pNativeCode->GetFunction(); |
650 | |
651 | // A CordbFunction is theoretically the uninstantiated method, yet for back-compat we allow |
652 | // debuggers to assume that it corresponds to exactly 1 native code blob. In order for |
653 | // an open generic function to know what native code to give back, we attach an arbitrary |
654 | // native code that we located through code inspection. |
655 | // Note that not all CordbFunction objects get created via stack traces because you can also |
656 | // create them by name. In that case you still won't get code for Open generic functions |
657 | // because we will never have attached one and the lookup by token is insufficient. This |
658 | // behavior mimics our 2.0 debugging behavior though so its not a regression. |
659 | pFunction->NotifyCodeCreated(pNativeCode); |
660 | |
661 | IfFailThrow(hr); |
662 | |
663 | _ASSERTE((pFunction != NULL) && (pNativeCode != NULL)); |
664 | |
665 | // initialize the auxiliary info required for funclets |
666 | CordbMiscFrame miscFrame(pJITFuncData); |
667 | |
668 | // Create the native frame. |
669 | CordbNativeFrame* pNativeFrame = new CordbNativeFrame(m_pCordbThread, |
670 | frameData.fp, |
671 | pNativeCode, |
672 | pJITFuncData->nativeOffset, |
673 | &(frameData.rd), |
674 | frameData.v.taAmbientESP, |
675 | !!frameData.quicklyUnwound, |
676 | pCurrentAppDomain, |
677 | &miscFrame, |
678 | &(frameData.ctx)); |
679 | |
680 | pResultFrame.Assign(static_cast<CordbFrame *>(pNativeFrame)); |
681 | m_pCachedFrame.Assign(static_cast<CordbFrame *>(pNativeFrame)); |
682 | |
683 | // @dbgtodo dynamic language debugging |
684 | // If we are dealing with a dynamic method (e.g. an IL stub, a LCG method, etc.), |
685 | // then we don't have the metadata or the debug info (sequence points, etc.). |
686 | // This means that we can't do anything meaningful with a CordbJITILFrame anyway, |
687 | // so let's not create the CordbJITILFrame at all. Note that methods created with |
688 | // RefEmit are okay, i.e. they have metadata. |
689 | |
690 | // The check for IsNativeImpl() != CordbFunction::kNativeOnly catches an odd profiler |
691 | // case. A profiler can rewrite assemblies at load time so that a P/invoke becomes a |
692 | // regular managed method. mscordbi isn't yet designed to handle runtime metadata |
693 | // changes, so it still thinks the method is a p/invoke. If we only relied on |
694 | // frameData.v.fNoMetadata which is populated by the DAC, that will report |
695 | // FALSE (the method does have metadata/IL now). However pNativeCode->LoadNativeInfo |
696 | // is going to check DBI's metadata and calculate this is a p/invoke, which will |
697 | // throw an exception that the method isn't IL. |
698 | // Ideally we probably want to expose the profiler's change to the method, |
699 | // however that will take significant work. Part of that is correctly detecting and |
700 | // updating metadata in DBI, part is determinging if/how the debugger is notified, |
701 | // and part is auditing mscordbi to ensure that anything we cached based on the |
702 | // old metadata is correctly invalidated. |
703 | // Since this is a late fix going into a controlled servicing release I have |
704 | // opted for a much narrower fix. Doing the check for IsNativeImpl() != CordbFunction::kNativeOnly |
705 | // will continue to treat our new method as though it was a p/invoke, and the |
706 | // debugger will not provide IL for it. The debugger can't inspect within the profiler |
707 | // modified method, but at least the error won't leak out to interfere with inspection |
708 | // of the callstack as a whole. |
709 | if (!frameData.v.fNoMetadata && |
710 | pNativeCode->GetFunction()->IsNativeImpl() != CordbFunction::kNativeOnly) |
711 | { |
712 | pNativeCode->LoadNativeInfo(); |
713 | |
714 | // By design, when a managed exception occurs we return the sequence point containing the faulting |
715 | // instruction in the leaf frame. In the past we didn't always achieve this, |
716 | // but we are being more deliberate about this behavior now. |
717 | |
718 | // If jsutAfterILThrow is true, it means nativeOffset points to the return address of IL_Throw |
719 | // (or another JIT exception helper) after an exception has been thrown. |
720 | // In such cases we want to adjust nativeOffset, so it will point an actual exception callsite. |
721 | // By subtracting STACKWALK_CONTROLPC_ADJUST_OFFSET from nativeOffset you can get |
722 | // an address somewhere inside CALL instruction. |
723 | // This ensures more consistent placement of exception line highlighting in Visual Studio |
724 | DWORD nativeOffsetToMap = pJITFuncData->jsutAfterILThrow ? |
725 | (DWORD)pJITFuncData->nativeOffset - STACKWALK_CONTROLPC_ADJUST_OFFSET : |
726 | (DWORD)pJITFuncData->nativeOffset; |
727 | CorDebugMappingResult mappingType; |
728 | ULONG uILOffset = pNativeCode->GetSequencePoints()->MapNativeOffsetToIL( |
729 | nativeOffsetToMap, |
730 | &mappingType); |
731 | |
732 | // Find or create the IL Code, and the pJITILFrame. |
733 | RSExtSmartPtr<CordbILCode> pCode; |
734 | |
735 | // The code for populating CordbFunction ILCode looks really bizzare... it appears to only grab the |
736 | // correct version of the IL if that is still the current EnC version yet it is populated deliberately |
737 | // late bound at which point the latest version may be different. In fact even here the latest version |
738 | // could already be different, but this is no worse than what the code used to do |
739 | hr = pFunction->GetILCode(&pCode); |
740 | IfFailThrow(hr); |
741 | _ASSERTE(pCode != NULL); |
742 | |
743 | // We populate the code for ReJit eagerly to make sure we still have it if the profiler removes the |
744 | // instrumentation later. Of course the only way it will still be accessible to our caller is if he |
745 | // saves a pointer to the ILCode. |
746 | // I'm not sure if ignoring rejit for mini-dumps is the right call long term, but we aren't doing |
747 | // anything special to collect the memory at dump time so we better be prepared to not fetch it here. |
748 | // We'll attempt to treat it as not being instrumented, though I suspect the abstraction is leaky. |
749 | RSSmartPtr<CordbReJitILCode> pReJitCode; |
750 | EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY |
751 | { |
752 | VMPTR_NativeCodeVersionNode vmNativeCodeVersionNode = VMPTR_NativeCodeVersionNode::NullPtr(); |
753 | IfFailThrow(GetProcess()->GetDAC()->GetNativeCodeVersionNode(pJITFuncData->vmNativeCodeMethodDescToken, pJITFuncData->nativeStartAddressPtr, &vmNativeCodeVersionNode)); |
754 | if (!vmNativeCodeVersionNode.IsNull()) |
755 | { |
756 | VMPTR_ILCodeVersionNode vmILCodeVersionNode = VMPTR_ILCodeVersionNode::NullPtr(); |
757 | IfFailThrow(GetProcess()->GetDAC()->GetILCodeVersionNode(vmNativeCodeVersionNode, &vmILCodeVersionNode)); |
758 | if (!vmILCodeVersionNode.IsNull()) |
759 | { |
760 | IfFailThrow(pFunction->LookupOrCreateReJitILCode(vmILCodeVersionNode, &pReJitCode)); |
761 | } |
762 | } |
763 | } |
764 | EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY |
765 | |
766 | |
767 | |
768 | RSInitHolder<CordbJITILFrame> pJITILFrame(new CordbJITILFrame(pNativeFrame, |
769 | pCode, |
770 | uILOffset, |
771 | mappingType, |
772 | frameData.v.exactGenericArgsToken, |
773 | frameData.v.dwExactGenericArgsTokenIndex, |
774 | !!frameData.v.fVarArgs, |
775 | pReJitCode)); |
776 | |
777 | // Initialize the frame. This is a nop if the method is not a vararg method. |
778 | hr = pJITILFrame->Init(); |
779 | IfFailThrow(hr); |
780 | |
781 | pNativeFrame->m_JITILFrame.Assign(pJITILFrame); |
782 | pJITILFrame.ClearAndMarkDontNeuter(); |
783 | } |
784 | |
785 | STRESS_LOG3(LF_CORDB, LL_INFO1000, "CSW::GFW - managed stack frame (%p): CNF - 0x%p, CJILF - 0x%p" , |
786 | this, pNativeFrame, pNativeFrame->m_JITILFrame.GetValue()); |
787 | } // kManagedStackFrame |
788 | else if (ft == IDacDbiInterface::kNativeRuntimeUnwindableStackFrame) |
789 | { |
790 | _ASSERTE(frameData.eType == DebuggerIPCE_STRData::cRuntimeNativeFrame); |
791 | |
792 | // In order to find the FramePointer on x86, we need to unwind to the next frame. |
793 | // Technically, only x86 needs to do this, because the x86 runtime stackwalker doesn't uwnind |
794 | // one frame ahead of time. However, we are doing this on all platforms to keep things simple. |
795 | BOOL fSuccess = UnwindStackFrame(); |
796 | (void)fSuccess; //prevent "unused variable" error from GCC |
797 | _ASSERTE(fSuccess); |
798 | |
799 | m_fIsOneFrameAhead = true; |
800 | #if defined(DBG_TARGET_X86) |
801 | frameData.fp = pDAC->GetFramePointer(m_pSFIHandle); |
802 | #endif // DBG_TARGET_X86 |
803 | |
804 | // Lookup the appdomain that the thread was in when it was executing code for this frame. We pass this |
805 | // to the frame when we create it so we can properly resolve locals in that frame later. |
806 | CordbAppDomain * pCurrentAppDomain = |
807 | GetProcess()->LookupOrCreateAppDomain(frameData.vmCurrentAppDomainToken); |
808 | _ASSERTE(pCurrentAppDomain != NULL); |
809 | |
810 | CordbRuntimeUnwindableFrame * pRuntimeFrame = new CordbRuntimeUnwindableFrame(m_pCordbThread, |
811 | frameData.fp, |
812 | pCurrentAppDomain, |
813 | &(frameData.ctx)); |
814 | |
815 | pResultFrame.Assign(static_cast<CordbFrame *>(pRuntimeFrame)); |
816 | m_pCachedFrame.Assign(static_cast<CordbFrame *>(pRuntimeFrame)); |
817 | |
818 | STRESS_LOG2(LF_CORDB, LL_INFO1000, "CSW::GFW - runtime unwindable stack frame (%p): 0x%p" , |
819 | this, pRuntimeFrame); |
820 | } |
821 | |
822 | pResultFrame.TransferOwnershipExternal(ppFrame); |
823 | |
824 | return S_OK; |
825 | } |
826 | |