| 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 | // ShimStackWalk.cpp |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | // This file contains the implementation of the Arrowhead stackwalking shim. This shim builds on top of |
| 10 | // the public Arrowhead ICD stackwalking API, and it is intended to be backward-compatible with the existing |
| 11 | // debuggers using the V2.0 ICD API. |
| 12 | // |
| 13 | // ====================================================================================== |
| 14 | |
| 15 | #include "stdafx.h" |
| 16 | #include "primitives.h" |
| 17 | |
| 18 | #if defined(_TARGET_X86_) |
| 19 | static const ULONG32 REGISTER_X86_MAX = REGISTER_X86_FPSTACK_7 + 1; |
| 20 | static const ULONG32 MAX_MASK_COUNT = (REGISTER_X86_MAX + 7) >> 3; |
| 21 | #elif defined(_TARGET_AMD64_) |
| 22 | static const ULONG32 REGISTER_AMD64_MAX = REGISTER_AMD64_XMM15 + 1; |
| 23 | static const ULONG32 MAX_MASK_COUNT = (REGISTER_AMD64_MAX + 7) >> 3; |
| 24 | #endif |
| 25 | |
| 26 | ShimStackWalk::ShimStackWalk(ShimProcess * pProcess, ICorDebugThread * pThread) |
| 27 | : m_pChainEnumList(NULL), |
| 28 | m_pFrameEnumList(NULL) |
| 29 | { |
| 30 | // The following assignments increment the ref count. |
| 31 | m_pProcess.Assign(pProcess); |
| 32 | m_pThread.Assign(pThread); |
| 33 | |
| 34 | Populate(); |
| 35 | } |
| 36 | |
| 37 | ShimStackWalk::~ShimStackWalk() |
| 38 | { |
| 39 | Clear(); |
| 40 | } |
| 41 | |
| 42 | // ---------------------------------------------------------------------------- |
| 43 | // ShimStackWalk::Clear |
| 44 | // |
| 45 | // Description: |
| 46 | // Clear all the memory used by this ShimStackWalk, including the array of frames, the array of chains, |
| 47 | // the linked list of ShimChainEnums, and the linked list of ShimFrameEnums. |
| 48 | // |
| 49 | |
| 50 | void ShimStackWalk::Clear() |
| 51 | { |
| 52 | // call Release() on each of the ShimChains |
| 53 | for (int i = 0; i < m_stackChains.Count(); i++) |
| 54 | { |
| 55 | (*m_stackChains.Get(i))->Neuter(); |
| 56 | (*m_stackChains.Get(i))->Release(); |
| 57 | } |
| 58 | m_stackChains.Clear(); |
| 59 | |
| 60 | // call Release() on each of the ICDFrames |
| 61 | for (int i = 0; i < m_stackFrames.Count(); i++) |
| 62 | { |
| 63 | (*m_stackFrames.Get(i))->Release(); |
| 64 | } |
| 65 | m_stackFrames.Clear(); |
| 66 | |
| 67 | // call Release() on each of the ShimChainEnums |
| 68 | while (m_pChainEnumList != NULL) |
| 69 | { |
| 70 | ShimChainEnum * pCur = m_pChainEnumList; |
| 71 | m_pChainEnumList = m_pChainEnumList->GetNext(); |
| 72 | pCur->Neuter(); |
| 73 | pCur->Release(); |
| 74 | } |
| 75 | |
| 76 | // call Release() on each of the ShimFrameEnums |
| 77 | while (m_pFrameEnumList != NULL) |
| 78 | { |
| 79 | ShimFrameEnum * pCur = m_pFrameEnumList; |
| 80 | m_pFrameEnumList = m_pFrameEnumList->GetNext(); |
| 81 | pCur->Neuter(); |
| 82 | pCur->Release(); |
| 83 | } |
| 84 | |
| 85 | // release the references |
| 86 | m_pProcess.Clear(); |
| 87 | m_pThread.Clear(); |
| 88 | } |
| 89 | |
| 90 | //--------------------------------------------------------------------------------------- |
| 91 | // |
| 92 | // Helper used by the stackwalker to determine whether a given UM chain should be tracked |
| 93 | // during the stackwalk for eventual transmission to the debugger. This function is the |
| 94 | // V4 equivalent of Whidbey's code:ShouldSendUMLeafChain (which ran on the LS, from |
| 95 | // Debug\EE\frameinfo.cpp). |
| 96 | // |
| 97 | // Note that code:ShouldSendUMLeafChain still exists today (to facilitate some in-process |
| 98 | // debugging stackwalks that are still necessary). So consult the comments in |
| 99 | // code:ShouldSendUMLeafChain for a more thorough discussion of why we do the checks we |
| 100 | // do to decide whether to track the chain. |
| 101 | // |
| 102 | // Arguments: |
| 103 | // pswInfo - StackWalkInfo representing the frame in question |
| 104 | // |
| 105 | // Return Value: |
| 106 | // nonzero iff the chain should be tracked |
| 107 | // |
| 108 | |
| 109 | BOOL ShimStackWalk::ShouldTrackUMChain(StackWalkInfo * pswInfo) |
| 110 | { |
| 111 | _ASSERTE (pswInfo != NULL); |
| 112 | |
| 113 | // Always track chains for non-leaf UM frames |
| 114 | if (!pswInfo->IsLeafFrame()) |
| 115 | return TRUE; |
| 116 | |
| 117 | // Sometimes we want to track leaf UM chains, and sometimes we don't. Check all the |
| 118 | // reasons not to track the chain, and return FALSE if any of them are hit. |
| 119 | |
| 120 | CorDebugUserState threadUserState; |
| 121 | HRESULT hr = m_pThread->GetUserState(&threadUserState); |
| 122 | IfFailThrow(hr); |
| 123 | |
| 124 | // ShouldSendUMLeafChain checked IsInWaitSleepJoin which is just USER_WAIT_SLEEP_JOIN |
| 125 | if ((threadUserState & USER_WAIT_SLEEP_JOIN) != 0) |
| 126 | return FALSE; |
| 127 | |
| 128 | // This check is the same as Thread::IsUnstarted() from ShouldSendUMLeafChain |
| 129 | if ((threadUserState & USER_UNSTARTED) != 0) |
| 130 | return FALSE; |
| 131 | |
| 132 | // This check is the same as Thread::IsDead() from ShouldSendUMLeafChain |
| 133 | if ((threadUserState & USER_STOPPED) != 0) |
| 134 | return FALSE; |
| 135 | |
| 136 | // #DacShimSwWorkAround |
| 137 | // |
| 138 | // This part cannot be determined using DBI alone. We must call through to the DAC |
| 139 | // because we have no other way to get at TS_Hijacked & TS_SyncSuspended. When the |
| 140 | // rearchitecture is complete, this DAC call should be able to go away, and we |
| 141 | // should be able to use DBI for all the info we need. |
| 142 | // |
| 143 | // One might think one could avoid the DAC for TS_SyncSuspended by just checking |
| 144 | // USER_SUSPENDED, but that won't work. Although USER_SUSPENDED will be returned some |
| 145 | // of the time when TS_SyncSuspended is set, that will not be the case when the |
| 146 | // debugger must suspend the thread. Example: if the given thread is in the middle of |
| 147 | // throwing a managed exception when the debugger breaks in, then TS_SyncSuspended |
| 148 | // will be set due to the debugger's breaking, but USER_SUSPENDED will not be set, so |
| 149 | // we'll think the UM chain should be tracked, resulting in a stack that differs from |
| 150 | // Whidbey. |
| 151 | if (m_pProcess->IsThreadSuspendedOrHijacked(m_pThread)) |
| 152 | return FALSE; |
| 153 | |
| 154 | return TRUE; |
| 155 | } |
| 156 | |
| 157 | // ---------------------------------------------------------------------------- |
| 158 | // ShimStackWalk::Populate |
| 159 | // |
| 160 | // Description: |
| 161 | // Walk the entire stack and populate the arrays of stack frames and stack chains. |
| 162 | // |
| 163 | |
| 164 | void ShimStackWalk::Populate() |
| 165 | { |
| 166 | HRESULT hr = S_OK; |
| 167 | |
| 168 | // query for the ICDThread3 interface |
| 169 | RSExtSmartPtr<ICorDebugThread3> pThread3; |
| 170 | hr = m_pThread->QueryInterface(IID_ICorDebugThread3, reinterpret_cast<void **>(&pThread3)); |
| 171 | IfFailThrow(hr); |
| 172 | |
| 173 | // create the ICDStackWalk |
| 174 | RSExtSmartPtr<ICorDebugStackWalk> pSW; |
| 175 | hr = pThread3->CreateStackWalk(&pSW); |
| 176 | IfFailThrow(hr); |
| 177 | |
| 178 | // structs used to store information during the stackwalk |
| 179 | ChainInfo chainInfo; |
| 180 | StackWalkInfo swInfo; |
| 181 | |
| 182 | // use the ICDStackWalk to retrieve the internal frames |
| 183 | hr = pThread3->GetActiveInternalFrames(0, &(swInfo.m_cInternalFrames), NULL); |
| 184 | IfFailThrow(hr); |
| 185 | |
| 186 | // allocate memory for the internal frames |
| 187 | if (swInfo.m_cInternalFrames > 0) |
| 188 | { |
| 189 | // allocate memory for the array of RSExtSmartPtrs |
| 190 | swInfo.m_ppInternalFrame2.AllocOrThrow(swInfo.m_cInternalFrames); |
| 191 | |
| 192 | // create a temporary buffer of raw ICDInternalFrame2 to pass to the ICD API |
| 193 | NewArrayHolder<ICorDebugInternalFrame2 *> pTmpArray(new ICorDebugInternalFrame2* [swInfo.m_cInternalFrames]); |
| 194 | hr = pThread3->GetActiveInternalFrames(swInfo.m_cInternalFrames, |
| 195 | &(swInfo.m_cInternalFrames), |
| 196 | pTmpArray); |
| 197 | IfFailThrow(hr); |
| 198 | |
| 199 | // transfer the raw array to the RSExtSmartPtr array |
| 200 | for (UINT32 i = 0; i < swInfo.m_cInternalFrames; i++) |
| 201 | { |
| 202 | // Assign() increments the ref count |
| 203 | swInfo.m_ppInternalFrame2.Assign(i, pTmpArray[i]); |
| 204 | pTmpArray[i]->Release(); |
| 205 | } |
| 206 | pTmpArray.Clear(); |
| 207 | } |
| 208 | |
| 209 | // |
| 210 | // This is basically how the loop works: |
| 211 | // 1) Determine whether we should process the next internal frame or the next stack frame. |
| 212 | // 2) If we are skipping frames, the only thing we need to do is to check whether we have reached |
| 213 | // the parent frame. |
| 214 | // 3) Process CHAIN_ENTER_MANAGED/CHAIN_ENTER_UNMANAGED chains |
| 215 | // 4) Append the frame to the cache. |
| 216 | // 5) Handle other types of chains. |
| 217 | // 6) Advance to the next frame. |
| 218 | // 7) Check if we should exit the loop. |
| 219 | // |
| 220 | while (true) |
| 221 | { |
| 222 | // reset variables used in the loop |
| 223 | swInfo.ResetForNextFrame(); |
| 224 | |
| 225 | // retrieve the next stack frame if it's available |
| 226 | RSExtSmartPtr<ICorDebugFrame> pFrame; |
| 227 | if (!swInfo.ExhaustedAllStackFrames()) |
| 228 | { |
| 229 | hr = pSW->GetFrame(&pFrame); |
| 230 | IfFailThrow(hr); |
| 231 | } |
| 232 | |
| 233 | // This next clause processes the current frame, regardless of whether it's an internal frame or a |
| 234 | // stack frame. Normally, "pFrame != NULL" is a good enough check, except for the case where we |
| 235 | // have exhausted all the stack frames but still have internal frames to process. |
| 236 | if ((pFrame != NULL) || swInfo.ExhaustedAllStackFrames()) |
| 237 | { |
| 238 | // prefetch the internal frame type |
| 239 | if (!swInfo.ExhaustedAllInternalFrames()) |
| 240 | { |
| 241 | swInfo.m_internalFrameType = GetInternalFrameType(swInfo.GetCurrentInternalFrame()); |
| 242 | } |
| 243 | |
| 244 | // We cannot have exhausted both the stack frames and the internal frames when we get to here. |
| 245 | // We should have exited the loop if we have exhausted both types of frames. |
| 246 | if (swInfo.ExhaustedAllStackFrames()) |
| 247 | { |
| 248 | swInfo.m_fProcessingInternalFrame = true; |
| 249 | } |
| 250 | else if (swInfo.ExhaustedAllInternalFrames()) |
| 251 | { |
| 252 | swInfo.m_fProcessingInternalFrame = false; |
| 253 | } |
| 254 | else |
| 255 | { |
| 256 | // check whether we should process the next internal frame or the next stack frame |
| 257 | swInfo.m_fProcessingInternalFrame = (CheckInternalFrame(pFrame, &swInfo, pThread3, pSW) == TRUE); |
| 258 | } |
| 259 | |
| 260 | // The only thing we do while we are skipping frames is to check whether we have reached the |
| 261 | // parent frame, and we only need to check if we are processing a stack frame. |
| 262 | if (swInfo.IsSkippingFrame()) |
| 263 | { |
| 264 | if (!swInfo.m_fProcessingInternalFrame) |
| 265 | { |
| 266 | // Check whether we have reached the parent frame yet. |
| 267 | RSExtSmartPtr<ICorDebugNativeFrame2> pNFrame2; |
| 268 | hr = pFrame->QueryInterface(IID_ICorDebugNativeFrame2, reinterpret_cast<void **>(&pNFrame2)); |
| 269 | IfFailThrow(hr); |
| 270 | |
| 271 | BOOL fIsParent = FALSE; |
| 272 | hr = swInfo.m_pChildFrame->IsMatchingParentFrame(pNFrame2, &fIsParent); |
| 273 | IfFailThrow(hr); |
| 274 | |
| 275 | if (fIsParent) |
| 276 | { |
| 277 | swInfo.m_pChildFrame.Clear(); |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | else if(swInfo.m_fProcessingInternalFrame && !chainInfo.m_fLeafNativeContextIsValid && |
| 282 | swInfo.m_internalFrameType == STUBFRAME_M2U) |
| 283 | { |
| 284 | // Filter this frame out entirely |
| 285 | // This occurs because InlinedCallFrames get placed inside leaf managed methods. |
| 286 | // The frame gets erected before the native call is made and destroyed afterwards |
| 287 | // but there is a window in which the debugger could stop where the internal frame |
| 288 | // is live but we are executing jitted code. See Dev10 issue 743230 |
| 289 | // It is quite possible other frames have this same pattern if the debugger were |
| 290 | // stopped right at the spot where they are being constructed. And that is |
| 291 | // just a facet of the general data structure consistency problems the debugger |
| 292 | // will always face |
| 293 | } |
| 294 | else |
| 295 | { |
| 296 | // Don't add any frame just yet. We need to deal with any unmanaged chain |
| 297 | // we are tracking first. |
| 298 | |
| 299 | // track the current enter-unmanaged chain and/or enter-managed chain |
| 300 | TrackUMChain(&chainInfo, &swInfo); |
| 301 | |
| 302 | if (swInfo.m_fProcessingInternalFrame) |
| 303 | { |
| 304 | // Check if this is a leaf internal frame. If so, check its frame type. |
| 305 | // In V2, code:DebuggerWalkStackProc doesn't expose chains derived from leaf internal |
| 306 | // frames of type TYPE_INTERNAL. However, V2 still exposes leaf M2U and U2M internal |
| 307 | // frames. |
| 308 | if (swInfo.IsLeafFrame()) |
| 309 | { |
| 310 | if (swInfo.m_internalFrameType == STUBFRAME_EXCEPTION) |
| 311 | { |
| 312 | // We need to make sure we don't accidentally send an enter-unmanaged chain |
| 313 | // because of the leaf STUBFRAME_EXCEPTION. |
| 314 | chainInfo.CancelUMChain(); |
| 315 | swInfo.m_fSkipChain = true; |
| 316 | } |
| 317 | } |
| 318 | |
| 319 | _ASSERTE(!swInfo.IsSkippingFrame()); |
| 320 | if (ConvertInternalFrameToDynamicMethod(&swInfo)) |
| 321 | { |
| 322 | // We have just converted a STUBFRAME_JIT_COMPILATION to a |
| 323 | // STUBFRAME_LIGHTWEIGHT_FUNCTION (or to NULL). Since the latter frame type doesn't |
| 324 | // map to any chain in V2, let's skip the chain handling. |
| 325 | swInfo.m_fSkipChain = true; |
| 326 | |
| 327 | // We may have converted to NULL, which means that we are dealing with an IL stub |
| 328 | // and we shouldn't expose it. |
| 329 | if (swInfo.GetCurrentInternalFrame() != NULL) |
| 330 | { |
| 331 | AppendFrame(swInfo.GetCurrentInternalFrame(), &swInfo); |
| 332 | } |
| 333 | } |
| 334 | else |
| 335 | { |
| 336 | // One more check before we append the internal frame: make sure the frame type is a |
| 337 | // V2 frame type first. |
| 338 | if (!IsV3FrameType(swInfo.m_internalFrameType)) |
| 339 | { |
| 340 | AppendFrame(swInfo.GetCurrentInternalFrame(), &swInfo); |
| 341 | } |
| 342 | } |
| 343 | } |
| 344 | else |
| 345 | { |
| 346 | if (!chainInfo.m_fNeedEnterManagedChain) |
| 347 | { |
| 348 | // If we have hit any managed stack frame, then we may need to send |
| 349 | // an enter-managed chain later. Save the CONTEXT now. |
| 350 | SaveChainContext(pSW, &chainInfo, &(chainInfo.m_leafManagedContext)); |
| 351 | chainInfo.m_fNeedEnterManagedChain = true; |
| 352 | } |
| 353 | |
| 354 | // We are processing a stack frame. |
| 355 | // Only append the frame if it's NOT a dynamic method. |
| 356 | _ASSERTE(!swInfo.IsSkippingFrame()); |
| 357 | if (ConvertStackFrameToDynamicMethod(pFrame, &swInfo)) |
| 358 | { |
| 359 | // We have converted a ICDNativeFrame for an IL method without metadata to an |
| 360 | // ICDInternalFrame of type STUBFRAME_LIGHTWEIGHT_FUNCTION (or to NULL). |
| 361 | // Fortunately, we don't have to update any state here |
| 362 | // (e.g. m_fProcessingInternalFrame) because the rest of the loop doesn't care. |
| 363 | if (swInfo.GetCurrentInternalFrame() != NULL) |
| 364 | { |
| 365 | AppendFrame(swInfo.GetCurrentInternalFrame(), &swInfo); |
| 366 | } |
| 367 | } |
| 368 | else |
| 369 | { |
| 370 | AppendFrame(pFrame, &swInfo); |
| 371 | } |
| 372 | |
| 373 | // If we have just processed a child frame, we should start skipping. |
| 374 | // Get the ICDNativeFrame2 pointer to check. |
| 375 | RSExtSmartPtr<ICorDebugNativeFrame2> pNFrame2; |
| 376 | hr = pFrame->QueryInterface(IID_ICorDebugNativeFrame2, reinterpret_cast<void **>(&pNFrame2)); |
| 377 | IfFailThrow(hr); |
| 378 | |
| 379 | if (pNFrame2 != NULL) |
| 380 | { |
| 381 | BOOL fIsChild = FALSE; |
| 382 | hr = pNFrame2->IsChild(&fIsChild); |
| 383 | IfFailThrow(hr); |
| 384 | |
| 385 | if (fIsChild) |
| 386 | { |
| 387 | swInfo.m_pChildFrame.Assign(pNFrame2); |
| 388 | } |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | } // process the current frame (managed stack frame or internal frame) |
| 393 | |
| 394 | // We can take care of other types of chains here, but only do so if we are not currently skipping |
| 395 | // child frames. |
| 396 | if (!swInfo.IsSkippingFrame()) |
| 397 | { |
| 398 | if ((pFrame == NULL) && |
| 399 | !swInfo.ExhaustedAllStackFrames()) |
| 400 | { |
| 401 | // We are here because we are processing a native marker stack frame, not because |
| 402 | // we have exhausted all the stack frames. |
| 403 | |
| 404 | // We need to save the CONTEXT to start tracking an unmanaged chain. |
| 405 | SaveChainContext(pSW, &chainInfo, &(chainInfo.m_leafNativeContext)); |
| 406 | chainInfo.m_fLeafNativeContextIsValid = true; |
| 407 | |
| 408 | // begin tracking UM chain if we're supposed to |
| 409 | if (ShouldTrackUMChain(&swInfo)) |
| 410 | { |
| 411 | chainInfo.m_reason = CHAIN_ENTER_UNMANAGED; |
| 412 | } |
| 413 | } |
| 414 | else |
| 415 | { |
| 416 | // handle other types of chains |
| 417 | if (swInfo.m_fProcessingInternalFrame) |
| 418 | { |
| 419 | if (!swInfo.m_fSkipChain) |
| 420 | { |
| 421 | BOOL fNewChain = FALSE; |
| 422 | |
| 423 | switch (swInfo.m_internalFrameType) |
| 424 | { |
| 425 | case STUBFRAME_M2U: // fall through |
| 426 | case STUBFRAME_U2M: // fall through |
| 427 | // These frame types are tracked specially. |
| 428 | break; |
| 429 | |
| 430 | case STUBFRAME_APPDOMAIN_TRANSITION: // fall through |
| 431 | case STUBFRAME_LIGHTWEIGHT_FUNCTION: // fall through |
| 432 | case STUBFRAME_INTERNALCALL: |
| 433 | // These frame types don't correspond to chains. |
| 434 | break; |
| 435 | |
| 436 | case STUBFRAME_FUNC_EVAL: |
| 437 | chainInfo.m_reason = CHAIN_FUNC_EVAL; |
| 438 | fNewChain = TRUE; |
| 439 | break; |
| 440 | |
| 441 | case STUBFRAME_CLASS_INIT: // fall through |
| 442 | case STUBFRAME_JIT_COMPILATION: |
| 443 | // In Whidbey, these two frame types are the same. |
| 444 | chainInfo.m_reason = CHAIN_CLASS_INIT; |
| 445 | fNewChain = TRUE; |
| 446 | break; |
| 447 | |
| 448 | case STUBFRAME_EXCEPTION: |
| 449 | chainInfo.m_reason = CHAIN_EXCEPTION_FILTER; |
| 450 | fNewChain = TRUE; |
| 451 | break; |
| 452 | |
| 453 | case STUBFRAME_SECURITY: |
| 454 | chainInfo.m_reason = CHAIN_SECURITY; |
| 455 | fNewChain = TRUE; |
| 456 | break; |
| 457 | |
| 458 | default: |
| 459 | // We can only reach this case if we have converted an IL stub to NULL. |
| 460 | _ASSERTE(swInfo.HasConvertedFrame()); |
| 461 | break; |
| 462 | } |
| 463 | |
| 464 | if (fNewChain) |
| 465 | { |
| 466 | chainInfo.m_rootFP = GetFramePointerForChain(swInfo.GetCurrentInternalFrame()); |
| 467 | AppendChain(&chainInfo, &swInfo); |
| 468 | } |
| 469 | } |
| 470 | } // chain handling for an internl frame |
| 471 | } // chain handling for a managed stack frame or an internal frame |
| 472 | } // chain handling |
| 473 | |
| 474 | // Reset the flag for leaf frame if we have processed any frame. The only case where we should |
| 475 | // not reset this flag is if the ICDStackWalk is stopped at a native stack frame on creation. |
| 476 | if (swInfo.IsLeafFrame()) |
| 477 | { |
| 478 | if (swInfo.m_fProcessingInternalFrame || (pFrame != NULL)) |
| 479 | { |
| 480 | swInfo.m_fLeafFrame = false; |
| 481 | } |
| 482 | } |
| 483 | |
| 484 | // advance to the next frame |
| 485 | if (swInfo.m_fProcessingInternalFrame) |
| 486 | { |
| 487 | swInfo.m_curInternalFrame += 1; |
| 488 | } |
| 489 | else |
| 490 | { |
| 491 | hr = pSW->Next(); |
| 492 | IfFailThrow(hr); |
| 493 | |
| 494 | // check for the end of stack condition |
| 495 | if (hr == CORDBG_S_AT_END_OF_STACK) |
| 496 | { |
| 497 | // By the time we finish the stackwalk, all child frames should have been matched with their |
| 498 | // respective parent frames. |
| 499 | _ASSERTE(!swInfo.IsSkippingFrame()); |
| 500 | |
| 501 | swInfo.m_fExhaustedAllStackFrames = true; |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | // Break out of the loop if we have exhausted all the frames. |
| 506 | if (swInfo.ExhaustedAllFrames()) |
| 507 | { |
| 508 | break; |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | // top off the stackwalk with a thread start chain |
| 513 | chainInfo.m_reason = CHAIN_THREAD_START; |
| 514 | chainInfo.m_rootFP = ROOT_MOST_FRAME; // In Whidbey, we actually use the cached stack base value. |
| 515 | AppendChain(&chainInfo, &swInfo); |
| 516 | } |
| 517 | |
| 518 | // the caller is responsible for addref and release |
| 519 | ICorDebugThread * ShimStackWalk::GetThread() |
| 520 | { |
| 521 | return m_pThread; |
| 522 | } |
| 523 | |
| 524 | // the caller is responsible for addref and release |
| 525 | ShimChain * ShimStackWalk::GetChain(UINT32 index) |
| 526 | { |
| 527 | if (index >= (UINT32)(m_stackChains.Count())) |
| 528 | { |
| 529 | return NULL; |
| 530 | } |
| 531 | else |
| 532 | { |
| 533 | return *(m_stackChains.Get((int)index)); |
| 534 | } |
| 535 | } |
| 536 | |
| 537 | // the caller is responsible for addref and release |
| 538 | ICorDebugFrame * ShimStackWalk::GetFrame(UINT32 index) |
| 539 | { |
| 540 | if (index >= (UINT32)(m_stackFrames.Count())) |
| 541 | { |
| 542 | return NULL; |
| 543 | } |
| 544 | else |
| 545 | { |
| 546 | return *(m_stackFrames.Get((int)index)); |
| 547 | } |
| 548 | } |
| 549 | |
| 550 | ULONG ShimStackWalk::GetChainCount() |
| 551 | { |
| 552 | return m_stackChains.Count(); |
| 553 | } |
| 554 | |
| 555 | ULONG ShimStackWalk::GetFrameCount() |
| 556 | { |
| 557 | return m_stackFrames.Count(); |
| 558 | } |
| 559 | |
| 560 | RSLock * ShimStackWalk::GetShimLock() |
| 561 | { |
| 562 | return m_pProcess->GetShimLock(); |
| 563 | } |
| 564 | |
| 565 | |
| 566 | // ---------------------------------------------------------------------------- |
| 567 | // ShimStackWalk::AddChainEnum |
| 568 | // |
| 569 | // Description: |
| 570 | // Add the specified ShimChainEnum to the head of the linked list of ShimChainEnums on the ShimStackWalk. |
| 571 | // |
| 572 | // Arguments: |
| 573 | // * pChainEnum - the ShimChainEnum to be added |
| 574 | // |
| 575 | |
| 576 | void ShimStackWalk::AddChainEnum(ShimChainEnum * pChainEnum) |
| 577 | { |
| 578 | pChainEnum->SetNext(m_pChainEnumList); |
| 579 | if (m_pChainEnumList != NULL) |
| 580 | { |
| 581 | m_pChainEnumList->Release(); |
| 582 | } |
| 583 | |
| 584 | m_pChainEnumList = pChainEnum; |
| 585 | if (m_pChainEnumList != NULL) |
| 586 | { |
| 587 | m_pChainEnumList->AddRef(); |
| 588 | } |
| 589 | } |
| 590 | |
| 591 | // ---------------------------------------------------------------------------- |
| 592 | // ShimStackWalk::AddFrameEnum |
| 593 | // |
| 594 | // Description: |
| 595 | // Add the specified ShimFrameEnum to the head of the linked list of ShimFrameEnums on the ShimStackWalk. |
| 596 | // |
| 597 | // Arguments: |
| 598 | // * pFrameEnum - the ShimFrameEnum to be added |
| 599 | // |
| 600 | |
| 601 | void ShimStackWalk::AddFrameEnum(ShimFrameEnum * pFrameEnum) |
| 602 | { |
| 603 | pFrameEnum->SetNext(m_pFrameEnumList); |
| 604 | if (m_pFrameEnumList != NULL) |
| 605 | { |
| 606 | m_pFrameEnumList->Release(); |
| 607 | } |
| 608 | |
| 609 | m_pFrameEnumList = pFrameEnum; |
| 610 | if (m_pFrameEnumList != NULL) |
| 611 | { |
| 612 | m_pFrameEnumList->AddRef(); |
| 613 | } |
| 614 | } |
| 615 | |
| 616 | // Return the ICDThread associated with the current ShimStackWalk as a key for ShimStackWalkHashTableTraits. |
| 617 | ICorDebugThread * ShimStackWalk::GetKey() |
| 618 | { |
| 619 | return m_pThread; |
| 620 | } |
| 621 | |
| 622 | // Hash a given ICDThread, which is used as the key for ShimStackWalkHashTableTraits. |
| 623 | //static |
| 624 | UINT32 ShimStackWalk::Hash(ICorDebugThread * pThread) |
| 625 | { |
| 626 | // just return the pointer value |
| 627 | return (UINT32)(size_t)pThread; |
| 628 | } |
| 629 | |
| 630 | // ---------------------------------------------------------------------------- |
| 631 | // ShimStackWalk::IsLeafFrame |
| 632 | // |
| 633 | // Description: |
| 634 | // Check whether the specified frame is the leaf frame. |
| 635 | // |
| 636 | // Arguments: |
| 637 | // * pFrame - frame to be checked |
| 638 | // |
| 639 | // Return Value: |
| 640 | // Return TRUE if the specified frame is the leaf frame. |
| 641 | // Return FALSE otherwise. |
| 642 | // |
| 643 | // Notes: |
| 644 | // * The definition of the leaf frame in V2 is the frame at the leaf of the leaf chain. |
| 645 | // |
| 646 | |
| 647 | BOOL ShimStackWalk::IsLeafFrame(ICorDebugFrame * pFrame) |
| 648 | { |
| 649 | // check if we have any chain |
| 650 | if (GetChainCount() > 0) |
| 651 | { |
| 652 | // check if the leaf chain has any frame |
| 653 | if (GetChain(0)->GetLastFrameIndex() > 0) |
| 654 | { |
| 655 | return IsSameFrame(pFrame, GetFrame(0)); |
| 656 | } |
| 657 | } |
| 658 | return FALSE; |
| 659 | } |
| 660 | |
| 661 | // ---------------------------------------------------------------------------- |
| 662 | // ShimStackWalk::IsSameFrame |
| 663 | // |
| 664 | // Description: |
| 665 | // Given two ICDFrames, check if they refer to the same frame. |
| 666 | // This is much more than a pointer comparison. This function actually checks the frame address, |
| 667 | // the stack pointer, etc. to make sure if the frames are the same. |
| 668 | // |
| 669 | // Arguments: |
| 670 | // * pLeft - frame to be compared |
| 671 | // * pRight - frame to be compared |
| 672 | // |
| 673 | // Return Value: |
| 674 | // Return TRUE if the two ICDFrames represent the same frame. |
| 675 | // |
| 676 | |
| 677 | BOOL ShimStackWalk::IsSameFrame(ICorDebugFrame * pLeft, ICorDebugFrame * pRight) |
| 678 | { |
| 679 | HRESULT hr = E_FAIL; |
| 680 | |
| 681 | // Quick check #1: If the pointers are the same then the two frames are the same (duh!). |
| 682 | if (pLeft == pRight) |
| 683 | { |
| 684 | return TRUE; |
| 685 | } |
| 686 | |
| 687 | RSExtSmartPtr<ICorDebugNativeFrame> pLeftNativeFrame; |
| 688 | hr = pLeft->QueryInterface(IID_ICorDebugNativeFrame, reinterpret_cast<void **>(&pLeftNativeFrame)); |
| 689 | |
| 690 | if (SUCCEEDED(hr)) |
| 691 | { |
| 692 | // The left frame is a stack frame. |
| 693 | RSExtSmartPtr<ICorDebugNativeFrame> pRightNativeFrame; |
| 694 | hr = pRight->QueryInterface(IID_ICorDebugNativeFrame, reinterpret_cast<void **>(&pRightNativeFrame)); |
| 695 | |
| 696 | if (FAILED(hr)) |
| 697 | { |
| 698 | // The right frame is NOT a stack frame. |
| 699 | return FALSE; |
| 700 | } |
| 701 | else |
| 702 | { |
| 703 | // Quick check #2: If the IPs are different then the two frames are not the same (duh!). |
| 704 | ULONG32 leftOffset; |
| 705 | ULONG32 rightOffset; |
| 706 | |
| 707 | hr = pLeftNativeFrame->GetIP(&leftOffset); |
| 708 | IfFailThrow(hr); |
| 709 | |
| 710 | hr = pRightNativeFrame->GetIP(&rightOffset); |
| 711 | IfFailThrow(hr); |
| 712 | |
| 713 | if (leftOffset != rightOffset) |
| 714 | { |
| 715 | return FALSE; |
| 716 | } |
| 717 | |
| 718 | // real check |
| 719 | CORDB_ADDRESS leftStart; |
| 720 | CORDB_ADDRESS leftEnd; |
| 721 | CORDB_ADDRESS rightStart; |
| 722 | CORDB_ADDRESS rightEnd; |
| 723 | |
| 724 | hr = pLeftNativeFrame->GetStackRange(&leftStart, &leftEnd); |
| 725 | IfFailThrow(hr); |
| 726 | |
| 727 | hr = pRightNativeFrame->GetStackRange(&rightStart, &rightEnd); |
| 728 | IfFailThrow(hr); |
| 729 | |
| 730 | return ((leftStart == rightStart) && (leftEnd == rightEnd)); |
| 731 | } |
| 732 | } |
| 733 | else |
| 734 | { |
| 735 | RSExtSmartPtr<ICorDebugInternalFrame2> pLeftInternalFrame2; |
| 736 | hr = pLeft->QueryInterface(IID_ICorDebugInternalFrame2, |
| 737 | reinterpret_cast<void **>(&pLeftInternalFrame2)); |
| 738 | |
| 739 | if (SUCCEEDED(hr)) |
| 740 | { |
| 741 | // The left frame is an internal frame. |
| 742 | RSExtSmartPtr<ICorDebugInternalFrame2> pRightInternalFrame2; |
| 743 | hr = pRight->QueryInterface(IID_ICorDebugInternalFrame2, |
| 744 | reinterpret_cast<void **>(&pRightInternalFrame2)); |
| 745 | |
| 746 | if (FAILED(hr)) |
| 747 | { |
| 748 | return FALSE; |
| 749 | } |
| 750 | else |
| 751 | { |
| 752 | // The right frame is also an internal frame. |
| 753 | |
| 754 | // Check the frame address. |
| 755 | CORDB_ADDRESS leftFrameAddr; |
| 756 | CORDB_ADDRESS rightFrameAddr; |
| 757 | |
| 758 | hr = pLeftInternalFrame2->GetAddress(&leftFrameAddr); |
| 759 | IfFailThrow(hr); |
| 760 | |
| 761 | hr = pRightInternalFrame2->GetAddress(&rightFrameAddr); |
| 762 | IfFailThrow(hr); |
| 763 | |
| 764 | return (leftFrameAddr == rightFrameAddr); |
| 765 | } |
| 766 | } |
| 767 | |
| 768 | return FALSE; |
| 769 | } |
| 770 | } |
| 771 | |
| 772 | // This is the shim implementation of ICDThread::EnumerateChains(). |
| 773 | void ShimStackWalk::EnumerateChains(ICorDebugChainEnum ** ppChainEnum) |
| 774 | { |
| 775 | NewHolder<ShimChainEnum> pChainEnum(new ShimChainEnum(this, GetShimLock())); |
| 776 | |
| 777 | *ppChainEnum = pChainEnum; |
| 778 | (*ppChainEnum)->AddRef(); |
| 779 | AddChainEnum(pChainEnum); |
| 780 | |
| 781 | pChainEnum.SuppressRelease(); |
| 782 | } |
| 783 | |
| 784 | // This is the shim implementation of ICDThread::GetActiveChain(). |
| 785 | void ShimStackWalk::GetActiveChain(ICorDebugChain ** ppChain) |
| 786 | { |
| 787 | if (GetChainCount() == 0) |
| 788 | { |
| 789 | *ppChain = NULL; |
| 790 | } |
| 791 | else |
| 792 | { |
| 793 | *ppChain = static_cast<ICorDebugChain *>(GetChain(0)); |
| 794 | (*ppChain)->AddRef(); |
| 795 | } |
| 796 | } |
| 797 | |
| 798 | // This is the shim implementation of ICDThread::GetActiveFrame(). |
| 799 | void ShimStackWalk::GetActiveFrame(ICorDebugFrame ** ppFrame) |
| 800 | { |
| 801 | // |
| 802 | // Make sure two things: |
| 803 | // 1) We have at least one frame. |
| 804 | // 2) The leaf frame is in the leaf chain, i.e. the leaf chain is not empty. |
| 805 | // |
| 806 | if ((GetFrameCount() == 0) || |
| 807 | (GetChain(0)->GetLastFrameIndex() == 0)) |
| 808 | { |
| 809 | *ppFrame = NULL; |
| 810 | } |
| 811 | else |
| 812 | { |
| 813 | *ppFrame = GetFrame(0); |
| 814 | (*ppFrame)->AddRef(); |
| 815 | } |
| 816 | } |
| 817 | |
| 818 | // This is the shim implementation of ICDThread::GetRegisterSet(). |
| 819 | void ShimStackWalk::GetActiveRegisterSet(ICorDebugRegisterSet ** ppRegisterSet) |
| 820 | { |
| 821 | _ASSERTE(GetChainCount() != 0); |
| 822 | _ASSERTE(GetChain(0) != NULL); |
| 823 | |
| 824 | // Return the register set of the leaf chain. |
| 825 | HRESULT hr = GetChain(0)->GetRegisterSet(ppRegisterSet); |
| 826 | IfFailThrow(hr); |
| 827 | } |
| 828 | |
| 829 | // This is the shim implementation of ICDFrame::GetChain(). |
| 830 | void ShimStackWalk::GetChainForFrame(ICorDebugFrame * pFrame, ICorDebugChain ** ppChain) |
| 831 | { |
| 832 | CORDB_ADDRESS frameStart; |
| 833 | CORDB_ADDRESS frameEnd; |
| 834 | IfFailThrow(pFrame->GetStackRange(&frameStart, &frameEnd)); |
| 835 | |
| 836 | for (UINT32 i = 0; i < GetChainCount(); i++) |
| 837 | { |
| 838 | ShimChain * pCurChain = GetChain(i); |
| 839 | |
| 840 | CORDB_ADDRESS chainStart; |
| 841 | CORDB_ADDRESS chainEnd; |
| 842 | IfFailThrow(pCurChain->GetStackRange(&chainStart, &chainEnd)); |
| 843 | |
| 844 | if ((chainStart <= frameStart) && (frameEnd <= chainEnd)) |
| 845 | { |
| 846 | // We need to check the next chain as well since some chains overlap at the boundary. |
| 847 | // If the current chain is the last one, no additional checking is required. |
| 848 | if (i < (GetChainCount() - 1)) |
| 849 | { |
| 850 | ShimChain * pNextChain = GetChain(i + 1); |
| 851 | |
| 852 | CORDB_ADDRESS nextChainStart; |
| 853 | CORDB_ADDRESS nextChainEnd; |
| 854 | IfFailThrow(pNextChain->GetStackRange(&nextChainStart, &nextChainEnd)); |
| 855 | |
| 856 | if ((nextChainStart <= frameStart) && (frameEnd <= nextChainEnd)) |
| 857 | { |
| 858 | // The frame lies in the stack ranges of two chains. This can only happn at the boundary. |
| 859 | if (pCurChain->GetFirstFrameIndex() == pCurChain->GetLastFrameIndex()) |
| 860 | { |
| 861 | // Make sure the next chain is not empty. |
| 862 | _ASSERTE(pNextChain->GetFirstFrameIndex() != pNextChain->GetLastFrameIndex()); |
| 863 | |
| 864 | // The current chain is empty, so the chain we want is the next one. |
| 865 | pCurChain = pNextChain; |
| 866 | } |
| 867 | // If the next chain is empty, then we'll just return the current chain and no additional |
| 868 | // work is needed. If the next chain is not empty, then we have more checking to do. |
| 869 | else if (pNextChain->GetFirstFrameIndex() != pNextChain->GetLastFrameIndex()) |
| 870 | { |
| 871 | // Both chains are non-empty. |
| 872 | if (IsSameFrame(GetFrame(pNextChain->GetFirstFrameIndex()), pFrame)) |
| 873 | { |
| 874 | // The same frame cannot be in both chains. |
| 875 | _ASSERTE(!IsSameFrame(GetFrame(pCurChain->GetLastFrameIndex() - 1), pFrame)); |
| 876 | pCurChain = pNextChain; |
| 877 | } |
| 878 | else |
| 879 | { |
| 880 | _ASSERTE(IsSameFrame(GetFrame(pCurChain->GetLastFrameIndex() - 1), pFrame)); |
| 881 | } |
| 882 | } |
| 883 | } |
| 884 | } |
| 885 | |
| 886 | *ppChain = static_cast<ICorDebugChain *>(pCurChain); |
| 887 | (*ppChain)->AddRef(); |
| 888 | return; |
| 889 | } |
| 890 | } |
| 891 | } |
| 892 | |
| 893 | // This is the shim implementation of ICDFrame::GetCaller(). |
| 894 | void ShimStackWalk::GetCallerForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCallerFrame) |
| 895 | { |
| 896 | for (UINT32 i = 0; i < GetChainCount(); i++) |
| 897 | { |
| 898 | ShimChain * pCurChain = GetChain(i); |
| 899 | |
| 900 | for (UINT32 j = pCurChain->GetFirstFrameIndex(); j < pCurChain->GetLastFrameIndex(); j++) |
| 901 | { |
| 902 | if (IsSameFrame(GetFrame(j), pFrame)) |
| 903 | { |
| 904 | // Check whether this is the last frame in the chain. |
| 905 | UINT32 callerFrameIndex = j + 1; |
| 906 | if (callerFrameIndex < pCurChain->GetLastFrameIndex()) |
| 907 | { |
| 908 | *ppCallerFrame = static_cast<ICorDebugFrame *>(GetFrame(callerFrameIndex)); |
| 909 | (*ppCallerFrame)->AddRef(); |
| 910 | } |
| 911 | else |
| 912 | { |
| 913 | *ppCallerFrame = NULL; |
| 914 | } |
| 915 | return; |
| 916 | } |
| 917 | } |
| 918 | } |
| 919 | } |
| 920 | |
| 921 | // This is the shim implementation of ICDFrame::GetCallee(). |
| 922 | void ShimStackWalk::GetCalleeForFrame(ICorDebugFrame * pFrame, ICorDebugFrame ** ppCalleeFrame) |
| 923 | { |
| 924 | for (UINT32 i = 0; i < GetChainCount(); i++) |
| 925 | { |
| 926 | ShimChain * pCurChain = GetChain(i); |
| 927 | |
| 928 | for (UINT32 j = pCurChain->GetFirstFrameIndex(); j < pCurChain->GetLastFrameIndex(); j++) |
| 929 | { |
| 930 | if (IsSameFrame(GetFrame(j), pFrame)) |
| 931 | { |
| 932 | // Check whether this is the first frame in the chain. |
| 933 | if (j > pCurChain->GetFirstFrameIndex()) |
| 934 | { |
| 935 | UINT32 calleeFrameIndex = j - 1; |
| 936 | *ppCalleeFrame = static_cast<ICorDebugFrame *>(GetFrame(calleeFrameIndex)); |
| 937 | (*ppCalleeFrame)->AddRef(); |
| 938 | } |
| 939 | else |
| 940 | { |
| 941 | *ppCalleeFrame = NULL; |
| 942 | } |
| 943 | return; |
| 944 | } |
| 945 | } |
| 946 | } |
| 947 | } |
| 948 | |
| 949 | FramePointer ShimStackWalk::GetFramePointerForChain(DT_CONTEXT * pContext) |
| 950 | { |
| 951 | return FramePointer::MakeFramePointer(CORDbgGetSP(pContext)); |
| 952 | } |
| 953 | |
| 954 | FramePointer ShimStackWalk::GetFramePointerForChain(ICorDebugInternalFrame2 * pInternalFrame2) |
| 955 | { |
| 956 | CORDB_ADDRESS frameAddr; |
| 957 | HRESULT hr = pInternalFrame2->GetAddress(&frameAddr); |
| 958 | IfFailThrow(hr); |
| 959 | |
| 960 | return FramePointer::MakeFramePointer(reinterpret_cast<void *>(frameAddr)); |
| 961 | } |
| 962 | |
| 963 | CorDebugInternalFrameType ShimStackWalk::GetInternalFrameType(ICorDebugInternalFrame2 * pFrame2) |
| 964 | { |
| 965 | HRESULT hr = E_FAIL; |
| 966 | |
| 967 | // Retrieve the frame type of the internal frame. |
| 968 | RSExtSmartPtr<ICorDebugInternalFrame> pFrame; |
| 969 | hr = pFrame2->QueryInterface(IID_ICorDebugInternalFrame, reinterpret_cast<void **>(&pFrame)); |
| 970 | IfFailThrow(hr); |
| 971 | |
| 972 | CorDebugInternalFrameType type; |
| 973 | hr = pFrame->GetFrameType(&type); |
| 974 | IfFailThrow(hr); |
| 975 | |
| 976 | return type; |
| 977 | } |
| 978 | |
| 979 | // ---------------------------------------------------------------------------- |
| 980 | // ShimStackWalk::AppendFrame |
| 981 | // |
| 982 | // Description: |
| 983 | // Append the specified frame to the array and increment the counter. |
| 984 | // |
| 985 | // Arguments: |
| 986 | // * pFrame - the frame to be added |
| 987 | // * pStackWalkInfo - contains information of the stackwalk |
| 988 | // |
| 989 | |
| 990 | void ShimStackWalk::AppendFrame(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo) |
| 991 | { |
| 992 | // grow the |
| 993 | ICorDebugFrame ** ppFrame = m_stackFrames.AppendThrowing(); |
| 994 | |
| 995 | // Be careful of the AddRef() below. Once we do the addref, we need to save the pointer and |
| 996 | // explicitly release it. |
| 997 | *ppFrame = pFrame; |
| 998 | (*ppFrame)->AddRef(); |
| 999 | |
| 1000 | pStackWalkInfo->m_cFrame += 1; |
| 1001 | } |
| 1002 | |
| 1003 | // ---------------------------------------------------------------------------- |
| 1004 | // Refer to comment of the overloaded function. |
| 1005 | // |
| 1006 | |
| 1007 | void ShimStackWalk::AppendFrame(ICorDebugInternalFrame2 * pInternalFrame2, StackWalkInfo * pStackWalkInfo) |
| 1008 | { |
| 1009 | RSExtSmartPtr<ICorDebugFrame> pFrame; |
| 1010 | HRESULT hr = pInternalFrame2->QueryInterface(IID_ICorDebugFrame, reinterpret_cast<void **>(&pFrame)); |
| 1011 | IfFailThrow(hr); |
| 1012 | |
| 1013 | AppendFrame(pFrame, pStackWalkInfo); |
| 1014 | } |
| 1015 | |
| 1016 | // ---------------------------------------------------------------------------- |
| 1017 | // ShimStackWalk::AppendChainWorker |
| 1018 | // |
| 1019 | // Description: |
| 1020 | // Append the specified chain to the array. |
| 1021 | // |
| 1022 | // Arguments: |
| 1023 | // * pStackWalkInfo - contains information regarding the stackwalk |
| 1024 | // * pLeafContext - the leaf CONTEXT of the chain to be added |
| 1025 | // * fpRoot - the root boundary of the chain to be added |
| 1026 | // * chainReason - the chain reason of the chain to be added |
| 1027 | // * fIsManagedChain - whether the chain to be added is managed |
| 1028 | // |
| 1029 | |
| 1030 | void ShimStackWalk::AppendChainWorker(StackWalkInfo * pStackWalkInfo, |
| 1031 | DT_CONTEXT * pLeafContext, |
| 1032 | FramePointer fpRoot, |
| 1033 | CorDebugChainReason chainReason, |
| 1034 | BOOL fIsManagedChain) |
| 1035 | { |
| 1036 | // first, create the chain |
| 1037 | NewHolder<ShimChain> pChain(new ShimChain(this, |
| 1038 | pLeafContext, |
| 1039 | fpRoot, |
| 1040 | pStackWalkInfo->m_cChain, |
| 1041 | pStackWalkInfo->m_firstFrameInChain, |
| 1042 | pStackWalkInfo->m_cFrame, |
| 1043 | chainReason, |
| 1044 | fIsManagedChain, |
| 1045 | GetShimLock())); |
| 1046 | |
| 1047 | // Grow the array and add the newly created chain. |
| 1048 | // Once we call AddRef() we own the ShimChain and need to release it. |
| 1049 | ShimChain ** ppChain = m_stackChains.AppendThrowing(); |
| 1050 | *ppChain = pChain; |
| 1051 | (*ppChain)->AddRef(); |
| 1052 | |
| 1053 | // update the counters on the StackWalkInfo |
| 1054 | pStackWalkInfo->m_cChain += 1; |
| 1055 | pStackWalkInfo->m_firstFrameInChain = pStackWalkInfo->m_cFrame; |
| 1056 | |
| 1057 | // If all goes well, suppress the release so that the ShimChain won't go away. |
| 1058 | pChain.SuppressRelease(); |
| 1059 | } |
| 1060 | |
| 1061 | // ---------------------------------------------------------------------------- |
| 1062 | // ShimStackWalk::AppendChain |
| 1063 | // |
| 1064 | // Description: |
| 1065 | // Append the chain to the array. This function is also smart enough to send an enter-managed chain |
| 1066 | // if necessary. In other words, this function may append two chains at the same time. |
| 1067 | // |
| 1068 | // Arguments: |
| 1069 | // * pChainInfo - information on the chain to be added |
| 1070 | // * pStackWalkInfo - information regarding the current stackwalk |
| 1071 | // |
| 1072 | |
| 1073 | void ShimStackWalk::AppendChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo) |
| 1074 | { |
| 1075 | // Check if the chain to be added is managed or not. |
| 1076 | BOOL fManagedChain = FALSE; |
| 1077 | if ((pChainInfo->m_reason == CHAIN_ENTER_MANAGED) || |
| 1078 | (pChainInfo->m_reason == CHAIN_CLASS_INIT) || |
| 1079 | (pChainInfo->m_reason == CHAIN_SECURITY) || |
| 1080 | (pChainInfo->m_reason == CHAIN_FUNC_EVAL)) |
| 1081 | { |
| 1082 | fManagedChain = TRUE; |
| 1083 | } |
| 1084 | |
| 1085 | DT_CONTEXT * pChainContext = NULL; |
| 1086 | if (fManagedChain) |
| 1087 | { |
| 1088 | // The chain to be added is managed itself. So we don't need to send an enter-managed chain. |
| 1089 | pChainInfo->m_fNeedEnterManagedChain = false; |
| 1090 | pChainContext = &(pChainInfo->m_leafManagedContext); |
| 1091 | } |
| 1092 | else |
| 1093 | { |
| 1094 | // The chain to be added is unmanaged. Check if we need to send an enter-managed chain. |
| 1095 | if (pChainInfo->m_fNeedEnterManagedChain) |
| 1096 | { |
| 1097 | // We need to send an extra enter-managed chain. |
| 1098 | _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid); |
| 1099 | BYTE * sp = reinterpret_cast<BYTE *>(CORDbgGetSP(&(pChainInfo->m_leafNativeContext))); |
| 1100 | #if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_) |
| 1101 | // Dev11 324806: on ARM we use the caller's SP for a frame's ending delimiter so we cannot |
| 1102 | // subtract 4 bytes from the chain's ending delimiter else the frame might never be in range. |
| 1103 | // TODO: revisit overlapping ranges on ARM, it would be nice to make it consistent with the other architectures. |
| 1104 | sp -= sizeof(LPVOID); |
| 1105 | #endif |
| 1106 | FramePointer fp = FramePointer::MakeFramePointer(sp); |
| 1107 | |
| 1108 | AppendChainWorker(pStackWalkInfo, |
| 1109 | &(pChainInfo->m_leafManagedContext), |
| 1110 | fp, |
| 1111 | CHAIN_ENTER_MANAGED, |
| 1112 | TRUE); |
| 1113 | |
| 1114 | pChainInfo->m_fNeedEnterManagedChain = false; |
| 1115 | } |
| 1116 | _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid); |
| 1117 | pChainContext = &(pChainInfo->m_leafNativeContext); |
| 1118 | } |
| 1119 | |
| 1120 | // Add the actual chain. |
| 1121 | AppendChainWorker(pStackWalkInfo, |
| 1122 | pChainContext, |
| 1123 | pChainInfo->m_rootFP, |
| 1124 | pChainInfo->m_reason, |
| 1125 | fManagedChain); |
| 1126 | } |
| 1127 | |
| 1128 | // ---------------------------------------------------------------------------- |
| 1129 | // ShimStackWalk::SaveChainContext |
| 1130 | // |
| 1131 | // Description: |
| 1132 | // Save the current CONTEXT on the ICDStackWalk into the specified CONTEXT. Also update the root end |
| 1133 | // of the chain on the ChainInfo. |
| 1134 | // |
| 1135 | // Arguments: |
| 1136 | // * pSW - the ICDStackWalk for the current stackwalk |
| 1137 | // * pChainInfo - the ChainInfo keeping track of the current chain |
| 1138 | // * pContext - the destination CONTEXT |
| 1139 | // |
| 1140 | |
| 1141 | void ShimStackWalk::SaveChainContext(ICorDebugStackWalk * pSW, ChainInfo * pChainInfo, DT_CONTEXT * pContext) |
| 1142 | { |
| 1143 | HRESULT hr = pSW->GetContext(CONTEXT_FULL, |
| 1144 | sizeof(*pContext), |
| 1145 | NULL, |
| 1146 | reinterpret_cast<BYTE *>(pContext)); |
| 1147 | IfFailThrow(hr); |
| 1148 | |
| 1149 | pChainInfo->m_rootFP = GetFramePointerForChain(pContext); |
| 1150 | } |
| 1151 | |
| 1152 | // ---------------------------------------------------------------------------- |
| 1153 | // ShimStackWalk::CheckInternalFrame |
| 1154 | // |
| 1155 | // Description: |
| 1156 | // Check whether the next frame to be processed should be the next internal frame or the next stack frame. |
| 1157 | // |
| 1158 | // Arguments: |
| 1159 | // * pNextStackFrame - the next stack frame |
| 1160 | // * pStackWalkInfo - information regarding the current stackwalk; also contains the next internal frame |
| 1161 | // * pThread3 - the thread we are walking |
| 1162 | // * pSW - the current stackwalk |
| 1163 | // |
| 1164 | // Return Value: |
| 1165 | // Return TRUE if we should process an internal frame next. |
| 1166 | // |
| 1167 | |
| 1168 | BOOL ShimStackWalk::CheckInternalFrame(ICorDebugFrame * pNextStackFrame, |
| 1169 | StackWalkInfo * pStackWalkInfo, |
| 1170 | ICorDebugThread3 * pThread3, |
| 1171 | ICorDebugStackWalk * pSW) |
| 1172 | { |
| 1173 | _ASSERTE(pNextStackFrame != NULL); |
| 1174 | _ASSERTE(!pStackWalkInfo->ExhaustedAllInternalFrames()); |
| 1175 | |
| 1176 | HRESULT hr = E_FAIL; |
| 1177 | BOOL fIsInternalFrameFirst = FALSE; |
| 1178 | |
| 1179 | // Special handling for the case where a managed method contains a M2U internal frame. |
| 1180 | // Normally only IL stubs contain M2U internal frames, but we may have inlined pinvoke calls in |
| 1181 | // optimized code. In that case, we would have an InlinedCallFrame in a normal managed method on x86. |
| 1182 | // On WIN64, we would have a normal NDirectMethodFrame* in a normal managed method. |
| 1183 | if (pStackWalkInfo->m_internalFrameType == STUBFRAME_M2U) |
| 1184 | { |
| 1185 | // create a temporary ICDStackWalk |
| 1186 | RSExtSmartPtr<ICorDebugStackWalk> pTmpSW; |
| 1187 | hr = pThread3->CreateStackWalk(&pTmpSW); |
| 1188 | IfFailThrow(hr); |
| 1189 | |
| 1190 | // retrieve the current CONTEXT |
| 1191 | DT_CONTEXT ctx; |
| 1192 | ctx.ContextFlags = DT_CONTEXT_FULL; |
| 1193 | hr = pSW->GetContext(ctx.ContextFlags, sizeof(ctx), NULL, reinterpret_cast<BYTE *>(&ctx)); |
| 1194 | IfFailThrow(hr); |
| 1195 | |
| 1196 | // set the CONTEXT on the temporary ICDStackWalk |
| 1197 | hr = pTmpSW->SetContext(SET_CONTEXT_FLAG_ACTIVE_FRAME, sizeof(ctx), reinterpret_cast<BYTE *>(&ctx)); |
| 1198 | IfFailThrow(hr); |
| 1199 | |
| 1200 | // unwind the temporary ICDStackWalk by one frame |
| 1201 | hr = pTmpSW->Next(); |
| 1202 | IfFailThrow(hr); |
| 1203 | |
| 1204 | // Unwinding from a managed stack frame will land us either in a managed stack frame or a native |
| 1205 | // stack frame. In either case, we have a CONTEXT. |
| 1206 | hr = pTmpSW->GetContext(ctx.ContextFlags, sizeof(ctx), NULL, reinterpret_cast<BYTE *>(&ctx)); |
| 1207 | IfFailThrow(hr); |
| 1208 | |
| 1209 | // Get the SP from the CONTEXT. This is the caller SP. |
| 1210 | CORDB_ADDRESS sp = PTR_TO_CORDB_ADDRESS(CORDbgGetSP(&ctx)); |
| 1211 | |
| 1212 | // get the frame address |
| 1213 | CORDB_ADDRESS frameAddr = 0; |
| 1214 | hr = pStackWalkInfo->GetCurrentInternalFrame()->GetAddress(&frameAddr); |
| 1215 | IfFailThrow(hr); |
| 1216 | |
| 1217 | // Compare the frame address with the caller SP of the stack frame for the IL method without metadata. |
| 1218 | fIsInternalFrameFirst = (frameAddr < sp); |
| 1219 | } |
| 1220 | else |
| 1221 | { |
| 1222 | hr = pStackWalkInfo->GetCurrentInternalFrame()->IsCloserToLeaf(pNextStackFrame, &fIsInternalFrameFirst); |
| 1223 | IfFailThrow(hr); |
| 1224 | } |
| 1225 | |
| 1226 | return fIsInternalFrameFirst; |
| 1227 | } |
| 1228 | |
| 1229 | // ---------------------------------------------------------------------------- |
| 1230 | // ShimStackWalk::ConvertInternalFrameToDynamicMethod |
| 1231 | // |
| 1232 | // Description: |
| 1233 | // In V2, PrestubMethodFrames (PMFs) are exposed as one of two things: a chain of type |
| 1234 | // CHAIN_CLASS_INIT in most cases, or an internal frame of type STUBFRAME_LIGHTWEIGHT_FUNCTION if |
| 1235 | // the method being jitted is a dynamic method. On the other hand, in Arrowhead, we consistently expose |
| 1236 | // PMFs as STUBFRAME_JIT_COMPILATION. This function determines if a STUBFRAME_JIT_COMPILATION should |
| 1237 | // be exposed, and, if so, how to expose it. In the case where conversion is necessary, this function |
| 1238 | // also updates the stackwalk information with the converted frame. |
| 1239 | // |
| 1240 | // Here are the rules for conversion: |
| 1241 | // 1) If the method being jitted is an IL stub, we set the converted frame to NULL, and we return TRUE. |
| 1242 | // 2) If the method being jitted is an LCG method, we set the converted frame to a |
| 1243 | // STUBFRAME_LIGHTWEIGHT_FUNCTION, and we return NULL. |
| 1244 | // 3) Otherwise, we return FALSE. |
| 1245 | // |
| 1246 | // Arguments: |
| 1247 | // * pStackWalkInfo - information about the current stackwalk |
| 1248 | // |
| 1249 | // Return Value: |
| 1250 | // Return TRUE if a conversion has taken place. |
| 1251 | // |
| 1252 | |
| 1253 | BOOL ShimStackWalk::ConvertInternalFrameToDynamicMethod(StackWalkInfo * pStackWalkInfo) |
| 1254 | { |
| 1255 | HRESULT hr = E_FAIL; |
| 1256 | |
| 1257 | // QI for ICDFrame |
| 1258 | RSExtSmartPtr<ICorDebugFrame> pOriginalFrame; |
| 1259 | hr = pStackWalkInfo->GetCurrentInternalFrame()->QueryInterface( |
| 1260 | IID_ICorDebugFrame, |
| 1261 | reinterpret_cast<void **>(&pOriginalFrame)); |
| 1262 | IfFailThrow(hr); |
| 1263 | |
| 1264 | // Ask the RS to do the real work. |
| 1265 | CordbThread * pThread = static_cast<CordbThread *>(m_pThread.GetValue()); |
| 1266 | pStackWalkInfo->m_fHasConvertedFrame = (TRUE == pThread->ConvertFrameForILMethodWithoutMetadata( |
| 1267 | pOriginalFrame, |
| 1268 | &(pStackWalkInfo->m_pConvertedInternalFrame2))); |
| 1269 | |
| 1270 | if (pStackWalkInfo->HasConvertedFrame()) |
| 1271 | { |
| 1272 | // We have a conversion. |
| 1273 | if (pStackWalkInfo->GetCurrentInternalFrame() != NULL) |
| 1274 | { |
| 1275 | // We have a converted internal frame, so let's update the internal frame type. |
| 1276 | RSExtSmartPtr<ICorDebugInternalFrame> pInternalFrame; |
| 1277 | hr = pStackWalkInfo->GetCurrentInternalFrame()->QueryInterface( |
| 1278 | IID_ICorDebugInternalFrame, |
| 1279 | reinterpret_cast<void **>(&pInternalFrame)); |
| 1280 | IfFailThrow(hr); |
| 1281 | |
| 1282 | hr = pInternalFrame->GetFrameType(&(pStackWalkInfo->m_internalFrameType)); |
| 1283 | IfFailThrow(hr); |
| 1284 | } |
| 1285 | else |
| 1286 | { |
| 1287 | // The method being jitted is an IL stub, so let's not expose it. |
| 1288 | pStackWalkInfo->m_internalFrameType = STUBFRAME_NONE; |
| 1289 | } |
| 1290 | } |
| 1291 | |
| 1292 | return pStackWalkInfo->HasConvertedFrame(); |
| 1293 | } |
| 1294 | |
| 1295 | // ---------------------------------------------------------------------------- |
| 1296 | // ShimStackWalk::ConvertInternalFrameToDynamicMethod |
| 1297 | // |
| 1298 | // Description: |
| 1299 | // In V2, LCG methods are exposed as internal frames of type STUBFRAME_LIGHTWEIGHT_FUNCTION. However, |
| 1300 | // in Arrowhead, LCG methods are exposed as first-class stack frames, not internal frames. Thus, |
| 1301 | // the shim needs to convert an ICDNativeFrame for a dynamic method in Arrowhead to an |
| 1302 | // ICDInternalFrame of type STUBFRAME_LIGHTWEIGHT_FUNCTION in V2. Furthermore, IL stubs are not exposed |
| 1303 | // in V2 at all. |
| 1304 | // |
| 1305 | // Here are the rules for conversion: |
| 1306 | // 1) If the stack frame is for an IL stub, we set the converted frame to NULL, and we return TRUE. |
| 1307 | // 2) If the stack frame is for an LCG method, we set the converted frame to a |
| 1308 | // STUBFRAME_LIGHTWEIGHT_FUNCTION, and we return NULL. |
| 1309 | // 3) Otherwise, we return FALSE. |
| 1310 | // |
| 1311 | // Arguments: |
| 1312 | // * pFrame - the frame to be checked and converted if necessary |
| 1313 | // * pStackWalkInfo - information about the current stackwalk |
| 1314 | // |
| 1315 | // Return Value: |
| 1316 | // Return TRUE if a conversion has taken place. |
| 1317 | // |
| 1318 | |
| 1319 | BOOL ShimStackWalk::ConvertStackFrameToDynamicMethod(ICorDebugFrame * pFrame, StackWalkInfo * pStackWalkInfo) |
| 1320 | { |
| 1321 | // If this is not a dynamic method (i.e. LCG method or IL stub), then we don't need to do a conversion. |
| 1322 | if (!IsILFrameWithoutMetadata(pFrame)) |
| 1323 | { |
| 1324 | return FALSE; |
| 1325 | } |
| 1326 | |
| 1327 | // Ask the RS to do the real work. |
| 1328 | CordbThread * pThread = static_cast<CordbThread *>(m_pThread.GetValue()); |
| 1329 | pStackWalkInfo->m_fHasConvertedFrame = (TRUE == pThread->ConvertFrameForILMethodWithoutMetadata( |
| 1330 | pFrame, |
| 1331 | &(pStackWalkInfo->m_pConvertedInternalFrame2))); |
| 1332 | |
| 1333 | return pStackWalkInfo->HasConvertedFrame(); |
| 1334 | } |
| 1335 | |
| 1336 | // ---------------------------------------------------------------------------- |
| 1337 | // ShimStackWalk::TrackUMChain |
| 1338 | // |
| 1339 | // Description: |
| 1340 | // Keep track of enter-unmanaged chains. Extend or cancel the chain as necesasry. |
| 1341 | // |
| 1342 | // Arguments: |
| 1343 | // * pChainInfo - information on the current chain we are tracking |
| 1344 | // * pStackWalkInfo - information regarding the current stackwalk |
| 1345 | // |
| 1346 | // Notes: |
| 1347 | // * This logic is based on code:TrackUMChain on the LS. |
| 1348 | // |
| 1349 | |
| 1350 | void ShimStackWalk::TrackUMChain(ChainInfo * pChainInfo, StackWalkInfo * pStackWalkInfo) |
| 1351 | { |
| 1352 | if (!pChainInfo->IsTrackingUMChain()) |
| 1353 | { |
| 1354 | if (pStackWalkInfo->m_fProcessingInternalFrame) |
| 1355 | { |
| 1356 | if (pStackWalkInfo->m_internalFrameType == STUBFRAME_M2U) |
| 1357 | { |
| 1358 | // If we hit an M2U frame out in the wild, convert it to an enter-unmanaged chain. |
| 1359 | |
| 1360 | // We can't hit an M2U frame without hitting a native stack frame |
| 1361 | // first (we filter those). We should have already saved the CONTEXT. |
| 1362 | // So just update the chain reason. |
| 1363 | pChainInfo->m_reason = CHAIN_ENTER_UNMANAGED; |
| 1364 | } |
| 1365 | } |
| 1366 | } |
| 1367 | |
| 1368 | BOOL fCreateUMChain = FALSE; |
| 1369 | if (pChainInfo->IsTrackingUMChain()) |
| 1370 | { |
| 1371 | if (pStackWalkInfo->m_fProcessingInternalFrame) |
| 1372 | { |
| 1373 | // Extend the root end of the unmanaged chain. |
| 1374 | pChainInfo->m_rootFP = GetFramePointerForChain(pStackWalkInfo->GetCurrentInternalFrame()); |
| 1375 | |
| 1376 | // Sometimes we may not want to show an UM chain b/c we know it's just |
| 1377 | // code inside of mscorwks. (Eg: Funcevals & AD transitions both fall into this category). |
| 1378 | // These are perfectly valid UM chains and we could give them if we wanted to. |
| 1379 | if ((pStackWalkInfo->m_internalFrameType == STUBFRAME_APPDOMAIN_TRANSITION) || |
| 1380 | (pStackWalkInfo->m_internalFrameType == STUBFRAME_FUNC_EVAL)) |
| 1381 | { |
| 1382 | pChainInfo->CancelUMChain(); |
| 1383 | } |
| 1384 | else if (pStackWalkInfo->m_internalFrameType == STUBFRAME_M2U) |
| 1385 | { |
| 1386 | // If we hit an M2U frame, then go ahead and dispatch the UM chain now. |
| 1387 | // This will likely also be an exit frame. |
| 1388 | fCreateUMChain = TRUE; |
| 1389 | } |
| 1390 | else if ((pStackWalkInfo->m_internalFrameType == STUBFRAME_CLASS_INIT) || |
| 1391 | (pStackWalkInfo->m_internalFrameType == STUBFRAME_EXCEPTION) || |
| 1392 | (pStackWalkInfo->m_internalFrameType == STUBFRAME_SECURITY) || |
| 1393 | (pStackWalkInfo->m_internalFrameType == STUBFRAME_JIT_COMPILATION)) |
| 1394 | { |
| 1395 | fCreateUMChain = TRUE; |
| 1396 | } |
| 1397 | } |
| 1398 | else |
| 1399 | { |
| 1400 | // If we hit a managed stack frame when we are processing an unmanaged chain, then |
| 1401 | // the chain is done. |
| 1402 | fCreateUMChain = TRUE; |
| 1403 | } |
| 1404 | } |
| 1405 | |
| 1406 | if (fCreateUMChain) |
| 1407 | { |
| 1408 | // check whether we get any stack range |
| 1409 | _ASSERTE(pChainInfo->m_fLeafNativeContextIsValid); |
| 1410 | FramePointer fpLeaf = GetFramePointerForChain(&(pChainInfo->m_leafNativeContext)); |
| 1411 | |
| 1412 | // Don't bother creating an unmanaged chain if the stack range is empty. |
| 1413 | if (fpLeaf != pChainInfo->m_rootFP) |
| 1414 | { |
| 1415 | AppendChain(pChainInfo, pStackWalkInfo); |
| 1416 | } |
| 1417 | pChainInfo->CancelUMChain(); |
| 1418 | } |
| 1419 | } |
| 1420 | |
| 1421 | BOOL ShimStackWalk::IsV3FrameType(CorDebugInternalFrameType type) |
| 1422 | { |
| 1423 | // These frame types are either new in Arrowhead or not used in V2. |
| 1424 | if ((type == STUBFRAME_INTERNALCALL) || |
| 1425 | (type == STUBFRAME_CLASS_INIT) || |
| 1426 | (type == STUBFRAME_EXCEPTION) || |
| 1427 | (type == STUBFRAME_SECURITY) || |
| 1428 | (type == STUBFRAME_JIT_COMPILATION)) |
| 1429 | { |
| 1430 | return TRUE; |
| 1431 | } |
| 1432 | else |
| 1433 | { |
| 1434 | return FALSE; |
| 1435 | } |
| 1436 | } |
| 1437 | |
| 1438 | // Check whether a stack frame is for a dynamic method. The way to tell is if the stack frame has |
| 1439 | // an ICDNativeFrame but no ICDILFrame. |
| 1440 | BOOL ShimStackWalk::IsILFrameWithoutMetadata(ICorDebugFrame * pFrame) |
| 1441 | { |
| 1442 | HRESULT hr = E_FAIL; |
| 1443 | |
| 1444 | RSExtSmartPtr<ICorDebugNativeFrame> pNativeFrame; |
| 1445 | hr = pFrame->QueryInterface(IID_ICorDebugNativeFrame, reinterpret_cast<void **>(&pNativeFrame)); |
| 1446 | IfFailThrow(hr); |
| 1447 | |
| 1448 | if (pNativeFrame != NULL) |
| 1449 | { |
| 1450 | RSExtSmartPtr<ICorDebugILFrame> pILFrame; |
| 1451 | hr = pFrame->QueryInterface(IID_ICorDebugILFrame, reinterpret_cast<void **>(&pILFrame)); |
| 1452 | |
| 1453 | if (FAILED(hr) || (pILFrame == NULL)) |
| 1454 | { |
| 1455 | return TRUE; |
| 1456 | } |
| 1457 | } |
| 1458 | |
| 1459 | return FALSE; |
| 1460 | } |
| 1461 | |
| 1462 | ShimStackWalk::StackWalkInfo::StackWalkInfo() |
| 1463 | : m_cChain(0), |
| 1464 | m_cFrame(0), |
| 1465 | m_firstFrameInChain(0), |
| 1466 | m_cInternalFrames(0), |
| 1467 | m_curInternalFrame(0), |
| 1468 | m_internalFrameType(STUBFRAME_NONE), |
| 1469 | m_fExhaustedAllStackFrames(false), |
| 1470 | m_fProcessingInternalFrame(false), |
| 1471 | m_fSkipChain(false), |
| 1472 | m_fLeafFrame(true), |
| 1473 | m_fHasConvertedFrame(false) |
| 1474 | { |
| 1475 | m_pChildFrame.Assign(NULL); |
| 1476 | m_pConvertedInternalFrame2.Assign(NULL); |
| 1477 | } |
| 1478 | |
| 1479 | ShimStackWalk::StackWalkInfo::~StackWalkInfo() |
| 1480 | { |
| 1481 | if (m_pChildFrame != NULL) |
| 1482 | { |
| 1483 | m_pChildFrame.Clear(); |
| 1484 | } |
| 1485 | |
| 1486 | if (m_pConvertedInternalFrame2 != NULL) |
| 1487 | { |
| 1488 | m_pConvertedInternalFrame2.Clear(); |
| 1489 | } |
| 1490 | |
| 1491 | if (!m_ppInternalFrame2.IsEmpty()) |
| 1492 | { |
| 1493 | m_ppInternalFrame2.Clear(); |
| 1494 | } |
| 1495 | } |
| 1496 | |
| 1497 | void ShimStackWalk::StackWalkInfo::ResetForNextFrame() |
| 1498 | { |
| 1499 | m_pConvertedInternalFrame2.Clear(); |
| 1500 | m_internalFrameType = STUBFRAME_NONE; |
| 1501 | m_fProcessingInternalFrame = false; |
| 1502 | m_fSkipChain = false; |
| 1503 | m_fHasConvertedFrame = false; |
| 1504 | } |
| 1505 | |
| 1506 | // Check whether we have exhausted both internal frames and stack frames. |
| 1507 | bool ShimStackWalk::StackWalkInfo::ExhaustedAllFrames() |
| 1508 | { |
| 1509 | return (ExhaustedAllStackFrames() && ExhaustedAllInternalFrames()); |
| 1510 | } |
| 1511 | |
| 1512 | bool ShimStackWalk::StackWalkInfo::ExhaustedAllStackFrames() |
| 1513 | { |
| 1514 | return m_fExhaustedAllStackFrames; |
| 1515 | } |
| 1516 | |
| 1517 | bool ShimStackWalk::StackWalkInfo::ExhaustedAllInternalFrames() |
| 1518 | { |
| 1519 | return (m_curInternalFrame == m_cInternalFrames); |
| 1520 | } |
| 1521 | |
| 1522 | ICorDebugInternalFrame2 * ShimStackWalk::StackWalkInfo::GetCurrentInternalFrame() |
| 1523 | { |
| 1524 | _ASSERTE(!ExhaustedAllInternalFrames() || HasConvertedFrame()); |
| 1525 | |
| 1526 | if (HasConvertedFrame()) |
| 1527 | { |
| 1528 | return m_pConvertedInternalFrame2; |
| 1529 | } |
| 1530 | else |
| 1531 | { |
| 1532 | return m_ppInternalFrame2[m_curInternalFrame]; |
| 1533 | } |
| 1534 | } |
| 1535 | |
| 1536 | BOOL ShimStackWalk::StackWalkInfo::IsLeafFrame() |
| 1537 | { |
| 1538 | return m_fLeafFrame; |
| 1539 | } |
| 1540 | |
| 1541 | BOOL ShimStackWalk::StackWalkInfo::IsSkippingFrame() |
| 1542 | { |
| 1543 | return (m_pChildFrame != NULL); |
| 1544 | } |
| 1545 | |
| 1546 | BOOL ShimStackWalk::StackWalkInfo::HasConvertedFrame() |
| 1547 | { |
| 1548 | return m_fHasConvertedFrame; |
| 1549 | } |
| 1550 | |
| 1551 | |
| 1552 | ShimChain::ShimChain(ShimStackWalk * pSW, |
| 1553 | DT_CONTEXT * pContext, |
| 1554 | FramePointer fpRoot, |
| 1555 | UINT32 chainIndex, |
| 1556 | UINT32 frameStartIndex, |
| 1557 | UINT32 frameEndIndex, |
| 1558 | CorDebugChainReason chainReason, |
| 1559 | BOOL fIsManaged, |
| 1560 | RSLock * pShimLock) |
| 1561 | : m_context(*pContext), |
| 1562 | m_fpRoot(fpRoot), |
| 1563 | m_pStackWalk(pSW), |
| 1564 | m_refCount(0), |
| 1565 | m_chainIndex(chainIndex), |
| 1566 | m_frameStartIndex(frameStartIndex), |
| 1567 | m_frameEndIndex(frameEndIndex), |
| 1568 | m_chainReason(chainReason), |
| 1569 | m_fIsManaged(fIsManaged), |
| 1570 | m_fIsNeutered(FALSE), |
| 1571 | m_pShimLock(pShimLock) |
| 1572 | { |
| 1573 | } |
| 1574 | |
| 1575 | ShimChain::~ShimChain() |
| 1576 | { |
| 1577 | _ASSERTE(IsNeutered()); |
| 1578 | } |
| 1579 | |
| 1580 | void ShimChain::Neuter() |
| 1581 | { |
| 1582 | m_fIsNeutered = TRUE; |
| 1583 | } |
| 1584 | |
| 1585 | BOOL ShimChain::IsNeutered() |
| 1586 | { |
| 1587 | return m_fIsNeutered; |
| 1588 | } |
| 1589 | |
| 1590 | ULONG STDMETHODCALLTYPE ShimChain::AddRef() |
| 1591 | { |
| 1592 | return InterlockedIncrement((LONG *)&m_refCount); |
| 1593 | } |
| 1594 | |
| 1595 | ULONG STDMETHODCALLTYPE ShimChain::Release() |
| 1596 | { |
| 1597 | LONG newRefCount = InterlockedDecrement((LONG *)&m_refCount); |
| 1598 | _ASSERTE(newRefCount >= 0); |
| 1599 | |
| 1600 | if (newRefCount == 0) |
| 1601 | { |
| 1602 | delete this; |
| 1603 | } |
| 1604 | return newRefCount; |
| 1605 | } |
| 1606 | |
| 1607 | HRESULT ShimChain::QueryInterface(REFIID id, void ** pInterface) |
| 1608 | { |
| 1609 | if (id == IID_ICorDebugChain) |
| 1610 | { |
| 1611 | *pInterface = static_cast<ICorDebugChain *>(this); |
| 1612 | } |
| 1613 | else if (id == IID_IUnknown) |
| 1614 | { |
| 1615 | *pInterface = static_cast<IUnknown *>(static_cast<ICorDebugChain *>(this)); |
| 1616 | } |
| 1617 | else |
| 1618 | { |
| 1619 | *pInterface = NULL; |
| 1620 | return E_NOINTERFACE; |
| 1621 | } |
| 1622 | |
| 1623 | AddRef(); |
| 1624 | return S_OK; |
| 1625 | } |
| 1626 | |
| 1627 | // Returns the thread to which this chain belongs. |
| 1628 | HRESULT ShimChain::GetThread(ICorDebugThread ** ppThread) |
| 1629 | { |
| 1630 | RSLockHolder lockHolder(m_pShimLock); |
| 1631 | FAIL_IF_NEUTERED(this); |
| 1632 | VALIDATE_POINTER_TO_OBJECT(ppThread, ICorDebugThread **); |
| 1633 | |
| 1634 | *ppThread = m_pStackWalk->GetThread(); |
| 1635 | (*ppThread)->AddRef(); |
| 1636 | |
| 1637 | return S_OK; |
| 1638 | } |
| 1639 | |
| 1640 | // Get the range on the stack that this chain matches against. |
| 1641 | // pStart is the leafmost; pEnd is the rootmost. |
| 1642 | // This is particularly used in interop-debugging to get native stack traces |
| 1643 | // for the UM portions of the stack |
| 1644 | HRESULT ShimChain::GetStackRange(CORDB_ADDRESS * pStart, CORDB_ADDRESS * pEnd) |
| 1645 | { |
| 1646 | HRESULT hr = S_OK; |
| 1647 | EX_TRY |
| 1648 | { |
| 1649 | THROW_IF_NEUTERED(this); |
| 1650 | |
| 1651 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pStart, CORDB_ADDRESS *); |
| 1652 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pEnd, CORDB_ADDRESS *); |
| 1653 | |
| 1654 | // Return the leafmost end of the stack range. |
| 1655 | // The leafmost end is represented by the register set. |
| 1656 | if (pStart) |
| 1657 | { |
| 1658 | *pStart = PTR_TO_CORDB_ADDRESS(CORDbgGetSP(&m_context)); |
| 1659 | } |
| 1660 | |
| 1661 | // Return the rootmost end of the stack range. It is represented by the frame pointer of the chain. |
| 1662 | if (pEnd) |
| 1663 | { |
| 1664 | *pEnd = PTR_TO_CORDB_ADDRESS(m_fpRoot.GetSPValue()); |
| 1665 | } |
| 1666 | } |
| 1667 | EX_CATCH_HRESULT(hr); |
| 1668 | return hr; |
| 1669 | } |
| 1670 | |
| 1671 | HRESULT ShimChain::GetContext(ICorDebugContext ** ppContext) |
| 1672 | { |
| 1673 | return E_NOTIMPL; |
| 1674 | } |
| 1675 | |
| 1676 | // Return the next chain which is closer to the root. |
| 1677 | // Currently this is just a wrapper over GetNext(). |
| 1678 | HRESULT ShimChain::GetCaller(ICorDebugChain ** ppChain) |
| 1679 | { |
| 1680 | RSLockHolder lockHolder(m_pShimLock); |
| 1681 | FAIL_IF_NEUTERED(this); |
| 1682 | VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **); |
| 1683 | |
| 1684 | return GetNext(ppChain); |
| 1685 | } |
| 1686 | |
| 1687 | // Return the previous chain which is closer to the leaf. |
| 1688 | // Currently this is just a wrapper over GetPrevious(). |
| 1689 | HRESULT ShimChain::GetCallee(ICorDebugChain ** ppChain) |
| 1690 | { |
| 1691 | RSLockHolder lockHolder(m_pShimLock); |
| 1692 | FAIL_IF_NEUTERED(this); |
| 1693 | VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **); |
| 1694 | |
| 1695 | return GetPrevious(ppChain); |
| 1696 | } |
| 1697 | |
| 1698 | // Return the previous chain which is closer to the leaf. |
| 1699 | HRESULT ShimChain::GetPrevious(ICorDebugChain ** ppChain) |
| 1700 | { |
| 1701 | RSLockHolder lockHolder(m_pShimLock); |
| 1702 | FAIL_IF_NEUTERED(this); |
| 1703 | VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **); |
| 1704 | |
| 1705 | *ppChain = NULL; |
| 1706 | if (m_chainIndex != 0) |
| 1707 | { |
| 1708 | *ppChain = m_pStackWalk->GetChain(m_chainIndex - 1); |
| 1709 | } |
| 1710 | |
| 1711 | if (*ppChain != NULL) |
| 1712 | { |
| 1713 | (*ppChain)->AddRef(); |
| 1714 | } |
| 1715 | |
| 1716 | return S_OK; |
| 1717 | } |
| 1718 | |
| 1719 | // Return the next chain which is closer to the root. |
| 1720 | HRESULT ShimChain::GetNext(ICorDebugChain ** ppChain) |
| 1721 | { |
| 1722 | RSLockHolder lockHolder(m_pShimLock); |
| 1723 | FAIL_IF_NEUTERED(this); |
| 1724 | VALIDATE_POINTER_TO_OBJECT(ppChain, ICorDebugChain **); |
| 1725 | |
| 1726 | *ppChain = m_pStackWalk->GetChain(m_chainIndex + 1); |
| 1727 | if (*ppChain != NULL) |
| 1728 | { |
| 1729 | (*ppChain)->AddRef(); |
| 1730 | } |
| 1731 | |
| 1732 | return S_OK; |
| 1733 | } |
| 1734 | |
| 1735 | // Return whether the chain contains frames running managed code. |
| 1736 | HRESULT ShimChain::IsManaged(BOOL * pManaged) |
| 1737 | { |
| 1738 | RSLockHolder lockHolder(m_pShimLock); |
| 1739 | FAIL_IF_NEUTERED(this); |
| 1740 | VALIDATE_POINTER_TO_OBJECT(pManaged, BOOL *); |
| 1741 | |
| 1742 | *pManaged = m_fIsManaged; |
| 1743 | |
| 1744 | return S_OK; |
| 1745 | } |
| 1746 | |
| 1747 | // Return an enumerator to iterate through the frames contained in this chain. |
| 1748 | HRESULT ShimChain::EnumerateFrames(ICorDebugFrameEnum ** ppFrames) |
| 1749 | { |
| 1750 | RSLockHolder lockHolder(m_pShimLock); |
| 1751 | FAIL_IF_NEUTERED(this); |
| 1752 | VALIDATE_POINTER_TO_OBJECT(ppFrames, ICorDebugFrameEnum **); |
| 1753 | |
| 1754 | HRESULT hr = S_OK; |
| 1755 | EX_TRY |
| 1756 | { |
| 1757 | ShimStackWalk * pSW = GetShimStackWalk(); |
| 1758 | NewHolder<ShimFrameEnum> pFrameEnum(new ShimFrameEnum(pSW, this, m_frameStartIndex, m_frameEndIndex, m_pShimLock)); |
| 1759 | |
| 1760 | *ppFrames = pFrameEnum; |
| 1761 | (*ppFrames)->AddRef(); |
| 1762 | |
| 1763 | // link the new ShimFramEnum into the list on the ShimStackWalk |
| 1764 | pSW->AddFrameEnum(pFrameEnum); |
| 1765 | |
| 1766 | pFrameEnum.SuppressRelease(); |
| 1767 | } |
| 1768 | EX_CATCH_HRESULT(hr); |
| 1769 | |
| 1770 | return hr; |
| 1771 | } |
| 1772 | |
| 1773 | // Return an enumerator to iterate through the frames contained in this chain. |
| 1774 | // Note that this function will only succeed if the cached stack trace is valid. |
| 1775 | HRESULT ShimChain::GetActiveFrame(ICorDebugFrame ** ppFrame) |
| 1776 | { |
| 1777 | RSLockHolder lockHolder(m_pShimLock); |
| 1778 | FAIL_IF_NEUTERED(this); |
| 1779 | VALIDATE_POINTER_TO_OBJECT(ppFrame, ICorDebugFrame **); |
| 1780 | (*ppFrame) = NULL; |
| 1781 | |
| 1782 | HRESULT hr = S_OK; |
| 1783 | |
| 1784 | // Chains may be empty, so they have no active frame. |
| 1785 | if (m_frameStartIndex == m_frameEndIndex) |
| 1786 | { |
| 1787 | *ppFrame = NULL; |
| 1788 | } |
| 1789 | else |
| 1790 | { |
| 1791 | *ppFrame = m_pStackWalk->GetFrame(m_frameStartIndex); |
| 1792 | (*ppFrame)->AddRef(); |
| 1793 | } |
| 1794 | |
| 1795 | return hr; |
| 1796 | } |
| 1797 | |
| 1798 | // Return the register set of the leaf end of the chain |
| 1799 | HRESULT ShimChain::GetRegisterSet(ICorDebugRegisterSet ** ppRegisters) |
| 1800 | { |
| 1801 | FAIL_IF_NEUTERED(this); |
| 1802 | VALIDATE_POINTER_TO_OBJECT(ppRegisters, ICorDebugRegisterSet **); |
| 1803 | |
| 1804 | HRESULT hr = S_OK; |
| 1805 | EX_TRY |
| 1806 | { |
| 1807 | CordbThread * pThread = static_cast<CordbThread *>(m_pStackWalk->GetThread()); |
| 1808 | |
| 1809 | // This is a private hook for calling back into the RS. Alternatively, we could have created a |
| 1810 | // ShimRegisterSet, but that's too much work for now. |
| 1811 | pThread->CreateCordbRegisterSet(&m_context, |
| 1812 | (m_chainIndex == 0), |
| 1813 | m_chainReason, |
| 1814 | ppRegisters); |
| 1815 | } |
| 1816 | EX_CATCH_HRESULT(hr); |
| 1817 | return hr; |
| 1818 | } |
| 1819 | |
| 1820 | // Return the chain reason |
| 1821 | HRESULT ShimChain::GetReason(CorDebugChainReason * pReason) |
| 1822 | { |
| 1823 | RSLockHolder lockHolder(m_pShimLock); |
| 1824 | FAIL_IF_NEUTERED(this); |
| 1825 | VALIDATE_POINTER_TO_OBJECT(pReason, CorDebugChainReason *); |
| 1826 | |
| 1827 | *pReason = m_chainReason; |
| 1828 | |
| 1829 | return S_OK; |
| 1830 | } |
| 1831 | |
| 1832 | ShimStackWalk * ShimChain::GetShimStackWalk() |
| 1833 | { |
| 1834 | return m_pStackWalk; |
| 1835 | } |
| 1836 | |
| 1837 | UINT32 ShimChain::GetFirstFrameIndex() |
| 1838 | { |
| 1839 | return this->m_frameStartIndex; |
| 1840 | } |
| 1841 | |
| 1842 | UINT32 ShimChain::GetLastFrameIndex() |
| 1843 | { |
| 1844 | return this->m_frameEndIndex; |
| 1845 | } |
| 1846 | |
| 1847 | |
| 1848 | ShimChainEnum::ShimChainEnum(ShimStackWalk * pSW, RSLock * pShimLock) |
| 1849 | : m_pStackWalk(pSW), |
| 1850 | m_pNext(NULL), |
| 1851 | m_currentChainIndex(0), |
| 1852 | m_refCount(0), |
| 1853 | m_fIsNeutered(FALSE), |
| 1854 | m_pShimLock(pShimLock) |
| 1855 | { |
| 1856 | } |
| 1857 | |
| 1858 | ShimChainEnum::~ShimChainEnum() |
| 1859 | { |
| 1860 | _ASSERTE(IsNeutered()); |
| 1861 | } |
| 1862 | |
| 1863 | void ShimChainEnum::Neuter() |
| 1864 | { |
| 1865 | if (IsNeutered()) |
| 1866 | { |
| 1867 | return; |
| 1868 | } |
| 1869 | |
| 1870 | m_fIsNeutered = TRUE; |
| 1871 | } |
| 1872 | |
| 1873 | BOOL ShimChainEnum::IsNeutered() |
| 1874 | { |
| 1875 | return m_fIsNeutered; |
| 1876 | } |
| 1877 | |
| 1878 | |
| 1879 | ULONG STDMETHODCALLTYPE ShimChainEnum::AddRef() |
| 1880 | { |
| 1881 | return InterlockedIncrement((LONG *)&m_refCount); |
| 1882 | } |
| 1883 | |
| 1884 | ULONG STDMETHODCALLTYPE ShimChainEnum::Release() |
| 1885 | { |
| 1886 | LONG newRefCount = InterlockedDecrement((LONG *)&m_refCount); |
| 1887 | _ASSERTE(newRefCount >= 0); |
| 1888 | |
| 1889 | if (newRefCount == 0) |
| 1890 | { |
| 1891 | delete this; |
| 1892 | } |
| 1893 | return newRefCount; |
| 1894 | } |
| 1895 | |
| 1896 | HRESULT ShimChainEnum::QueryInterface(REFIID id, void ** ppInterface) |
| 1897 | { |
| 1898 | if (id == IID_ICorDebugChainEnum) |
| 1899 | { |
| 1900 | *ppInterface = static_cast<ICorDebugChainEnum *>(this); |
| 1901 | } |
| 1902 | else if (id == IID_ICorDebugEnum) |
| 1903 | { |
| 1904 | *ppInterface = static_cast<ICorDebugEnum *>(static_cast<ICorDebugChainEnum *>(this)); |
| 1905 | } |
| 1906 | else if (id == IID_IUnknown) |
| 1907 | { |
| 1908 | *ppInterface = static_cast<IUnknown *>(static_cast<ICorDebugChainEnum *>(this)); |
| 1909 | } |
| 1910 | else |
| 1911 | { |
| 1912 | *ppInterface = NULL; |
| 1913 | return E_NOINTERFACE; |
| 1914 | } |
| 1915 | |
| 1916 | AddRef(); |
| 1917 | return S_OK; |
| 1918 | } |
| 1919 | |
| 1920 | // Skip the specified number of chains. |
| 1921 | HRESULT ShimChainEnum::Skip(ULONG celt) |
| 1922 | { |
| 1923 | RSLockHolder lockHolder(m_pShimLock); |
| 1924 | FAIL_IF_NEUTERED(this); |
| 1925 | |
| 1926 | // increment the index by the specified amount |
| 1927 | m_currentChainIndex += celt; |
| 1928 | return S_OK; |
| 1929 | } |
| 1930 | |
| 1931 | HRESULT ShimChainEnum::Reset() |
| 1932 | { |
| 1933 | m_currentChainIndex = 0; |
| 1934 | return S_OK; |
| 1935 | } |
| 1936 | |
| 1937 | // Clone the chain enumerator and set the new one to the same current chain |
| 1938 | HRESULT ShimChainEnum::Clone(ICorDebugEnum ** ppEnum) |
| 1939 | { |
| 1940 | RSLockHolder lockHolder(m_pShimLock); |
| 1941 | FAIL_IF_NEUTERED(this); |
| 1942 | VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **); |
| 1943 | |
| 1944 | HRESULT hr = S_OK; |
| 1945 | EX_TRY |
| 1946 | { |
| 1947 | NewHolder<ShimChainEnum> pChainEnum(new ShimChainEnum(m_pStackWalk, m_pShimLock)); |
| 1948 | |
| 1949 | // set the index in the new enumerator |
| 1950 | pChainEnum->m_currentChainIndex = this->m_currentChainIndex; |
| 1951 | |
| 1952 | *ppEnum = pChainEnum; |
| 1953 | (*ppEnum)->AddRef(); |
| 1954 | m_pStackWalk->AddChainEnum(pChainEnum); |
| 1955 | |
| 1956 | pChainEnum.SuppressRelease(); |
| 1957 | } |
| 1958 | EX_CATCH_HRESULT(hr); |
| 1959 | return hr; |
| 1960 | } |
| 1961 | |
| 1962 | // Return the number of chains on the thread |
| 1963 | HRESULT ShimChainEnum::GetCount(ULONG * pcChains) |
| 1964 | { |
| 1965 | RSLockHolder lockHolder(m_pShimLock); |
| 1966 | FAIL_IF_NEUTERED(this); |
| 1967 | VALIDATE_POINTER_TO_OBJECT(pcChains, ULONG *); |
| 1968 | |
| 1969 | *pcChains = m_pStackWalk->GetChainCount(); |
| 1970 | return S_OK; |
| 1971 | } |
| 1972 | |
| 1973 | // Retrieve the next x number of chains on the thread into "chains", where x is specified by "celt". |
| 1974 | // "pcChainsFetched" is set to be the actual number of chains retrieved. |
| 1975 | // Return S_FALSE if the number of chains actually retrieved is less than the number of chains requested. |
| 1976 | HRESULT ShimChainEnum::Next(ULONG cChains, ICorDebugChain * rgpChains[], ULONG * pcChainsFetched) |
| 1977 | { |
| 1978 | RSLockHolder lockHolder(m_pShimLock); |
| 1979 | FAIL_IF_NEUTERED(this); |
| 1980 | VALIDATE_POINTER_TO_OBJECT_ARRAY(rgpChains, ICorDebugChain *, cChains, true, true); |
| 1981 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcChainsFetched, ULONG *); |
| 1982 | |
| 1983 | // if the out parameter is NULL, then we can only return one chain at a time |
| 1984 | if ((pcChainsFetched == NULL) && (cChains != 1)) |
| 1985 | { |
| 1986 | return E_INVALIDARG; |
| 1987 | } |
| 1988 | |
| 1989 | // Check for the trivial case where no chain is actually requested. |
| 1990 | // This is probably a user error. |
| 1991 | if (cChains == 0) |
| 1992 | { |
| 1993 | if (pcChainsFetched != NULL) |
| 1994 | { |
| 1995 | *pcChainsFetched = 0; |
| 1996 | } |
| 1997 | return S_OK; |
| 1998 | } |
| 1999 | |
| 2000 | ICorDebugChain ** ppCurrentChain = rgpChains; |
| 2001 | |
| 2002 | while ((m_currentChainIndex < m_pStackWalk->GetChainCount()) && |
| 2003 | (cChains > 0)) |
| 2004 | { |
| 2005 | *ppCurrentChain = m_pStackWalk->GetChain(m_currentChainIndex); |
| 2006 | (*ppCurrentChain)->AddRef(); |
| 2007 | |
| 2008 | ppCurrentChain++; // increment the pointer into the buffer |
| 2009 | m_currentChainIndex++; // increment the index |
| 2010 | cChains--; |
| 2011 | } |
| 2012 | |
| 2013 | // set the number of chains actually returned |
| 2014 | if (pcChainsFetched != NULL) |
| 2015 | { |
| 2016 | *pcChainsFetched = (ULONG)(ppCurrentChain - rgpChains); |
| 2017 | } |
| 2018 | |
| 2019 | // |
| 2020 | // If we reached the end of the enumeration, but not the end |
| 2021 | // of the number of requested items, we return S_FALSE. |
| 2022 | // |
| 2023 | if (cChains > 0) |
| 2024 | { |
| 2025 | return S_FALSE; |
| 2026 | } |
| 2027 | |
| 2028 | return S_OK; |
| 2029 | } |
| 2030 | |
| 2031 | ShimChainEnum * ShimChainEnum::GetNext() |
| 2032 | { |
| 2033 | return m_pNext; |
| 2034 | } |
| 2035 | |
| 2036 | void ShimChainEnum::SetNext(ShimChainEnum * pNext) |
| 2037 | { |
| 2038 | if (m_pNext != NULL) |
| 2039 | { |
| 2040 | m_pNext->Release(); |
| 2041 | } |
| 2042 | |
| 2043 | m_pNext = pNext; |
| 2044 | |
| 2045 | if (m_pNext != NULL) |
| 2046 | { |
| 2047 | m_pNext->AddRef(); |
| 2048 | } |
| 2049 | } |
| 2050 | |
| 2051 | |
| 2052 | ShimFrameEnum::ShimFrameEnum(ShimStackWalk * pSW, |
| 2053 | ShimChain * pChain, |
| 2054 | UINT32 frameStartIndex, |
| 2055 | UINT32 frameEndIndex, |
| 2056 | RSLock * pShimLock) |
| 2057 | : m_pStackWalk(pSW), |
| 2058 | m_pChain(pChain), |
| 2059 | m_pShimLock(pShimLock), |
| 2060 | m_pNext(NULL), |
| 2061 | m_currentFrameIndex(frameStartIndex), |
| 2062 | m_endFrameIndex(frameEndIndex), |
| 2063 | m_refCount(0), |
| 2064 | m_fIsNeutered(FALSE) |
| 2065 | { |
| 2066 | } |
| 2067 | |
| 2068 | ShimFrameEnum::~ShimFrameEnum() |
| 2069 | { |
| 2070 | _ASSERTE(IsNeutered()); |
| 2071 | } |
| 2072 | |
| 2073 | void ShimFrameEnum::Neuter() |
| 2074 | { |
| 2075 | if (IsNeutered()) |
| 2076 | { |
| 2077 | return; |
| 2078 | } |
| 2079 | |
| 2080 | m_fIsNeutered = TRUE; |
| 2081 | } |
| 2082 | |
| 2083 | BOOL ShimFrameEnum::IsNeutered() |
| 2084 | { |
| 2085 | return m_fIsNeutered; |
| 2086 | } |
| 2087 | |
| 2088 | |
| 2089 | ULONG STDMETHODCALLTYPE ShimFrameEnum::AddRef() |
| 2090 | { |
| 2091 | return InterlockedIncrement((LONG *)&m_refCount); |
| 2092 | } |
| 2093 | |
| 2094 | ULONG STDMETHODCALLTYPE ShimFrameEnum::Release() |
| 2095 | { |
| 2096 | LONG newRefCount = InterlockedDecrement((LONG *)&m_refCount); |
| 2097 | _ASSERTE(newRefCount >= 0); |
| 2098 | |
| 2099 | if (newRefCount == 0) |
| 2100 | { |
| 2101 | delete this; |
| 2102 | } |
| 2103 | return newRefCount; |
| 2104 | } |
| 2105 | |
| 2106 | HRESULT ShimFrameEnum::QueryInterface(REFIID id, void ** ppInterface) |
| 2107 | { |
| 2108 | if (id == IID_ICorDebugFrameEnum) |
| 2109 | { |
| 2110 | *ppInterface = static_cast<ICorDebugFrameEnum *>(this); |
| 2111 | } |
| 2112 | else if (id == IID_ICorDebugEnum) |
| 2113 | { |
| 2114 | *ppInterface = static_cast<ICorDebugEnum *>(static_cast<ICorDebugFrameEnum *>(this)); |
| 2115 | } |
| 2116 | else if (id == IID_IUnknown) |
| 2117 | { |
| 2118 | *ppInterface = static_cast<IUnknown *>(static_cast<ICorDebugFrameEnum *>(this)); |
| 2119 | } |
| 2120 | else |
| 2121 | { |
| 2122 | *ppInterface = NULL; |
| 2123 | return E_NOINTERFACE; |
| 2124 | } |
| 2125 | |
| 2126 | AddRef(); |
| 2127 | return S_OK; |
| 2128 | } |
| 2129 | |
| 2130 | // Skip the specified number of chains. |
| 2131 | HRESULT ShimFrameEnum::Skip(ULONG celt) |
| 2132 | { |
| 2133 | RSLockHolder lockHolder(m_pShimLock); |
| 2134 | FAIL_IF_NEUTERED(this); |
| 2135 | |
| 2136 | // increment the index by the specified amount |
| 2137 | m_currentFrameIndex += celt; |
| 2138 | return S_OK; |
| 2139 | } |
| 2140 | |
| 2141 | HRESULT ShimFrameEnum::Reset() |
| 2142 | { |
| 2143 | RSLockHolder lockHolder(m_pShimLock); |
| 2144 | FAIL_IF_NEUTERED(this); |
| 2145 | |
| 2146 | m_currentFrameIndex = m_pChain->GetFirstFrameIndex(); |
| 2147 | return S_OK; |
| 2148 | } |
| 2149 | |
| 2150 | // Clone the chain enumerator and set the new one to the same current chain |
| 2151 | HRESULT ShimFrameEnum::Clone(ICorDebugEnum ** ppEnum) |
| 2152 | { |
| 2153 | RSLockHolder lockHolder(m_pShimLock); |
| 2154 | FAIL_IF_NEUTERED(this); |
| 2155 | VALIDATE_POINTER_TO_OBJECT(ppEnum, ICorDebugEnum **); |
| 2156 | |
| 2157 | HRESULT hr = S_OK; |
| 2158 | EX_TRY |
| 2159 | { |
| 2160 | NewHolder<ShimFrameEnum> pFrameEnum(new ShimFrameEnum(m_pStackWalk, |
| 2161 | m_pChain, |
| 2162 | m_currentFrameIndex, |
| 2163 | m_endFrameIndex, |
| 2164 | m_pShimLock)); |
| 2165 | |
| 2166 | *ppEnum = pFrameEnum; |
| 2167 | (*ppEnum)->AddRef(); |
| 2168 | m_pStackWalk->AddFrameEnum(pFrameEnum); |
| 2169 | |
| 2170 | pFrameEnum.SuppressRelease(); |
| 2171 | } |
| 2172 | EX_CATCH_HRESULT(hr); |
| 2173 | |
| 2174 | return hr; |
| 2175 | } |
| 2176 | |
| 2177 | // Return the number of chains on the thread |
| 2178 | HRESULT ShimFrameEnum::GetCount(ULONG * pcFrames) |
| 2179 | { |
| 2180 | RSLockHolder lockHolder(m_pShimLock); |
| 2181 | FAIL_IF_NEUTERED(this); |
| 2182 | VALIDATE_POINTER_TO_OBJECT(pcFrames, ULONG *); |
| 2183 | |
| 2184 | *pcFrames = m_pChain->GetLastFrameIndex() - m_pChain->GetFirstFrameIndex(); |
| 2185 | return S_OK; |
| 2186 | } |
| 2187 | |
| 2188 | // Retrieve the next x number of chains on the thread into "chains", where x is specified by "celt". |
| 2189 | // "pcChainsFetched" is set to be the actual number of chains retrieved. |
| 2190 | // Return S_FALSE if the number of chains actually retrieved is less than the number of chains requested. |
| 2191 | HRESULT ShimFrameEnum::Next(ULONG cFrames, ICorDebugFrame * rgpFrames[], ULONG * pcFramesFetched) |
| 2192 | { |
| 2193 | RSLockHolder lockHolder(m_pShimLock); |
| 2194 | FAIL_IF_NEUTERED(this); |
| 2195 | VALIDATE_POINTER_TO_OBJECT_ARRAY(rgpFrames, ICorDebugFrame *, cFrames, true, true); |
| 2196 | VALIDATE_POINTER_TO_OBJECT_OR_NULL(pcFramesFetched, ULONG *); |
| 2197 | |
| 2198 | // if the out parameter is NULL, then we can only return one chain at a time |
| 2199 | if ((pcFramesFetched == NULL) && (cFrames != 1)) |
| 2200 | { |
| 2201 | return E_INVALIDARG; |
| 2202 | } |
| 2203 | |
| 2204 | // Check for the trivial case where no chain is actually requested. |
| 2205 | // This is probably a user error. |
| 2206 | if (cFrames == 0) |
| 2207 | { |
| 2208 | if (pcFramesFetched != NULL) |
| 2209 | { |
| 2210 | *pcFramesFetched = 0; |
| 2211 | } |
| 2212 | return S_OK; |
| 2213 | } |
| 2214 | |
| 2215 | ICorDebugFrame ** ppCurrentFrame = rgpFrames; |
| 2216 | |
| 2217 | while ((m_currentFrameIndex < m_endFrameIndex) && |
| 2218 | (cFrames > 0)) |
| 2219 | { |
| 2220 | *ppCurrentFrame = m_pStackWalk->GetFrame(m_currentFrameIndex); |
| 2221 | (*ppCurrentFrame)->AddRef(); |
| 2222 | |
| 2223 | ppCurrentFrame++; // increment the pointer into the buffer |
| 2224 | m_currentFrameIndex++; // increment the index |
| 2225 | cFrames--; |
| 2226 | } |
| 2227 | |
| 2228 | // set the number of chains actually returned |
| 2229 | if (pcFramesFetched != NULL) |
| 2230 | { |
| 2231 | *pcFramesFetched = (ULONG)(ppCurrentFrame - rgpFrames); |
| 2232 | } |
| 2233 | |
| 2234 | // |
| 2235 | // If we reached the end of the enumeration, but not the end |
| 2236 | // of the number of requested items, we return S_FALSE. |
| 2237 | // |
| 2238 | if (cFrames > 0) |
| 2239 | { |
| 2240 | return S_FALSE; |
| 2241 | } |
| 2242 | |
| 2243 | return S_OK; |
| 2244 | } |
| 2245 | |
| 2246 | ShimFrameEnum * ShimFrameEnum::GetNext() |
| 2247 | { |
| 2248 | return m_pNext; |
| 2249 | } |
| 2250 | |
| 2251 | void ShimFrameEnum::SetNext(ShimFrameEnum * pNext) |
| 2252 | { |
| 2253 | if (m_pNext != NULL) |
| 2254 | { |
| 2255 | m_pNext->Release(); |
| 2256 | } |
| 2257 | |
| 2258 | m_pNext = pNext; |
| 2259 | |
| 2260 | if (m_pNext != NULL) |
| 2261 | { |
| 2262 | m_pNext->AddRef(); |
| 2263 | } |
| 2264 | } |
| 2265 | |