VirtualBox

source: vbox/trunk/src/VBox/VMM/DBGFStack.cpp@ 19572

Last change on this file since 19572 was 19572, checked in by vboxsync, 16 years ago

Started with ring 0 stack trace dump support.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.3 KB
Line 
1/* $Id: DBGFStack.cpp 19572 2009-05-11 11:24:27Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Call Stack Analyser.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DBGF
27#include <VBox/dbgf.h>
28#include <VBox/selm.h>
29#include <VBox/mm.h>
30#include "DBGFInternal.h"
31#include <VBox/vm.h>
32#include <VBox/err.h>
33#include <VBox/log.h>
34#include <iprt/param.h>
35#include <iprt/assert.h>
36#include <iprt/string.h>
37#include <iprt/alloca.h>
38
39
40
41/**
42 * Read stack memory.
43 */
44DECLINLINE(int) dbgfR3Read(PVM pVM, VMCPUID idCpu, void *pvBuf, PCDBGFADDRESS pSrcAddr, size_t cb, size_t *pcbRead)
45{
46 int rc = DBGFR3MemRead(pVM, idCpu, pSrcAddr, pvBuf, cb);
47 if (RT_FAILURE(rc))
48 {
49 /* fallback: byte by byte and zero the ones we fail to read. */
50 size_t cbRead;
51 for (cbRead = 0; cbRead < cb; cbRead++)
52 {
53 DBGFADDRESS Addr = *pSrcAddr;
54 rc = DBGFR3MemRead(pVM, idCpu, DBGFR3AddrAdd(&Addr, cbRead), (uint8_t *)pvBuf + cbRead, 1);
55 if (RT_FAILURE(rc))
56 break;
57 }
58 if (cbRead)
59 rc = VINF_SUCCESS;
60 memset((char *)pvBuf + cbRead, 0, cb - cbRead);
61 *pcbRead = cbRead;
62 }
63 else
64 *pcbRead = cb;
65 return rc;
66}
67
68
69/**
70 * Internal worker routine.
71 *
72 * On x86 the typical stack frame layout is like this:
73 * .. ..
74 * 16 parameter 2
75 * 12 parameter 1
76 * 8 parameter 0
77 * 4 return address
78 * 0 old ebp; current ebp points here
79 *
80 * @todo Add AMD64 support (needs teaming up with the module management for
81 * unwind tables).
82 */
83static int dbgfR3StackWalk(PVM pVM, VMCPUID idCpu, PDBGFSTACKFRAME pFrame)
84{
85 /*
86 * Stop if we got a read error in the previous run.
87 */
88 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_LAST)
89 return VERR_NO_MORE_FILES;
90
91 /*
92 * Read the raw frame data.
93 */
94 const DBGFADDRESS AddrOldPC = pFrame->AddrPC;
95 const unsigned cbRetAddr = DBGFReturnTypeSize(pFrame->enmReturnType);
96 unsigned cbStackItem;
97 switch (AddrOldPC.fFlags & DBGFADDRESS_FLAGS_TYPE_MASK)
98 {
99 case DBGFADDRESS_FLAGS_FAR16: cbStackItem = 2; break;
100 case DBGFADDRESS_FLAGS_FAR32: cbStackItem = 4; break;
101 case DBGFADDRESS_FLAGS_FAR64: cbStackItem = 8; break;
102 default: cbStackItem = 4; break; /// @todo 64-bit guests.
103 }
104
105 union
106 {
107 uint64_t *pu64;
108 uint32_t *pu32;
109 uint16_t *pu16;
110 uint8_t *pb;
111 void *pv;
112 } u, uRet, uArgs, uBp;
113 size_t cbRead = cbRetAddr + cbStackItem + sizeof(pFrame->Args);
114 u.pv = alloca(cbRead);
115 uBp = u;
116 uRet.pb = u.pb + cbStackItem;
117 uArgs.pb = u.pb + cbStackItem + cbRetAddr;
118
119 Assert(DBGFADDRESS_IS_VALID(&pFrame->AddrFrame));
120 int rc = dbgfR3Read(pVM, idCpu, u.pv,
121 pFrame->fFlags & DBGFSTACKFRAME_FLAGS_ALL_VALID
122 ? &pFrame->AddrReturnFrame
123 : &pFrame->AddrFrame,
124 cbRead, &cbRead);
125 if ( RT_FAILURE(rc)
126 || cbRead < cbRetAddr + cbStackItem)
127 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_LAST;
128
129 /*
130 * The first step is taken in a different way than the others.
131 */
132 if (!(pFrame->fFlags & DBGFSTACKFRAME_FLAGS_ALL_VALID))
133 {
134 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_ALL_VALID;
135 pFrame->iFrame = 0;
136
137 /* Current PC - set by caller, just find symbol & line. */
138 if (DBGFADDRESS_IS_VALID(&pFrame->AddrPC))
139 {
140 pFrame->pSymPC = DBGFR3SymbolByAddrAlloc(pVM, pFrame->AddrPC.FlatPtr, NULL);
141 pFrame->pLinePC = DBGFR3LineByAddrAlloc(pVM, pFrame->AddrPC.FlatPtr, NULL);
142 }
143 }
144 else /* 2nd and subsequent steps */
145 {
146 /* frame, pc and stack is taken from the existing frames return members. */
147 pFrame->AddrFrame = pFrame->AddrReturnFrame;
148 pFrame->AddrPC = pFrame->AddrReturnPC;
149 pFrame->pSymPC = pFrame->pSymReturnPC;
150 pFrame->pLinePC = pFrame->pLineReturnPC;
151
152 /* increment the frame number. */
153 pFrame->iFrame++;
154 }
155
156 /*
157 * Return Frame address.
158 */
159 pFrame->AddrReturnFrame = pFrame->AddrFrame;
160 switch (cbStackItem)
161 {
162 case 2: pFrame->AddrReturnFrame.off = *uBp.pu16; break;
163 case 4: pFrame->AddrReturnFrame.off = *uBp.pu32; break;
164 case 8: pFrame->AddrReturnFrame.off = *uBp.pu64; break;
165 default: AssertMsgFailed(("cbStackItem=%d\n", cbStackItem)); return VERR_INTERNAL_ERROR;
166 }
167 pFrame->AddrReturnFrame.FlatPtr += pFrame->AddrReturnFrame.off - pFrame->AddrFrame.off;
168
169 /*
170 * Return PC and Stack Addresses.
171 */
172 /** @todo AddrReturnStack is not correct for stdcall and pascal. (requires scope info) */
173 pFrame->AddrReturnStack = pFrame->AddrFrame;
174 pFrame->AddrReturnStack.off += cbStackItem + cbRetAddr;
175 pFrame->AddrReturnStack.FlatPtr += cbStackItem + cbRetAddr;
176
177 pFrame->AddrReturnPC = pFrame->AddrPC;
178 switch (pFrame->enmReturnType)
179 {
180 case DBGFRETURNTYPE_NEAR16:
181 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
182 {
183 pFrame->AddrReturnPC.FlatPtr += *uRet.pu16 - pFrame->AddrReturnPC.off;
184 pFrame->AddrReturnPC.off = *uRet.pu16;
185 }
186 else
187 DBGFR3AddrFromFlat(pVM, &pFrame->AddrReturnPC, *uRet.pu16);
188 break;
189 case DBGFRETURNTYPE_NEAR32:
190 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
191 {
192 pFrame->AddrReturnPC.FlatPtr += *uRet.pu32 - pFrame->AddrReturnPC.off;
193 pFrame->AddrReturnPC.off = *uRet.pu32;
194 }
195 else
196 DBGFR3AddrFromFlat(pVM, &pFrame->AddrReturnPC, *uRet.pu32);
197 break;
198 case DBGFRETURNTYPE_NEAR64:
199 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
200 {
201 pFrame->AddrReturnPC.FlatPtr += *uRet.pu64 - pFrame->AddrReturnPC.off;
202 pFrame->AddrReturnPC.off = *uRet.pu64;
203 }
204 else
205 DBGFR3AddrFromFlat(pVM, &pFrame->AddrReturnPC, *uRet.pu64);
206 break;
207 case DBGFRETURNTYPE_FAR16:
208 DBGFR3AddrFromSelOff(pVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[1], uRet.pu16[0]);
209 break;
210 case DBGFRETURNTYPE_FAR32:
211 DBGFR3AddrFromSelOff(pVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
212 break;
213 case DBGFRETURNTYPE_FAR64:
214 DBGFR3AddrFromSelOff(pVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[4], uRet.pu64[0]);
215 break;
216 case DBGFRETURNTYPE_IRET16:
217 DBGFR3AddrFromSelOff(pVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[1], uRet.pu16[0]);
218 break;
219 case DBGFRETURNTYPE_IRET32:
220 DBGFR3AddrFromSelOff(pVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
221 break;
222 case DBGFRETURNTYPE_IRET32_PRIV:
223 DBGFR3AddrFromSelOff(pVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
224 break;
225 case DBGFRETURNTYPE_IRET32_V86:
226 DBGFR3AddrFromSelOff(pVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
227 break;
228 case DBGFRETURNTYPE_IRET64:
229 DBGFR3AddrFromSelOff(pVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[4], uRet.pu64[0]);
230 break;
231 default:
232 AssertMsgFailed(("enmReturnType=%d\n", pFrame->enmReturnType));
233 return VERR_INVALID_PARAMETER;
234 }
235
236 pFrame->pSymReturnPC = DBGFR3SymbolByAddrAlloc(pVM, pFrame->AddrReturnPC.FlatPtr, NULL);
237 pFrame->pLineReturnPC = DBGFR3LineByAddrAlloc(pVM, pFrame->AddrReturnPC.FlatPtr, NULL);
238
239 /*
240 * The arguments.
241 */
242 memcpy(&pFrame->Args, uArgs.pv, sizeof(pFrame->Args));
243
244 return VINF_SUCCESS;
245}
246
247
248/**
249 * Walks the entire stack allocating memory as we walk.
250 */
251static DECLCALLBACK(int) dbgfR3StackWalkCtxFull(PVM pVM, VMCPUID idCpu, PCCPUMCTXCORE pCtxCore,
252 DBGFCODETYPE enmCodeType,
253 PCDBGFADDRESS pAddrFrame,
254 PCDBGFADDRESS pAddrStack,
255 PCDBGFADDRESS pAddrPC,
256 DBGFRETURNTYPE enmReturnType,
257 PCDBGFSTACKFRAME *ppFirstFrame)
258{
259 /* alloc first frame. */
260 PDBGFSTACKFRAME pCur = (PDBGFSTACKFRAME)MMR3HeapAllocZ(pVM, MM_TAG_DBGF_STACK, sizeof(*pCur));
261 if (!pCur)
262 return VERR_NO_MEMORY;
263
264 /*
265 * Initialize the frame.
266 */
267 pCur->pNextInternal = NULL;
268 pCur->pFirstInternal = pCur;
269
270 int rc = VINF_SUCCESS;
271 if (pAddrPC)
272 pCur->AddrPC = *pAddrPC;
273 else
274 rc = DBGFR3AddrFromSelOff(pVM, idCpu, &pCur->AddrPC, pCtxCore->cs, pCtxCore->rip);
275 if (RT_SUCCESS(rc))
276 {
277 if (enmReturnType == DBGFRETURNTYPE_INVALID)
278 switch (pCur->AddrPC.fFlags & DBGFADDRESS_FLAGS_TYPE_MASK)
279 {
280 case DBGFADDRESS_FLAGS_FAR16: pCur->enmReturnType = DBGFRETURNTYPE_NEAR16; break;
281 case DBGFADDRESS_FLAGS_FAR32: pCur->enmReturnType = DBGFRETURNTYPE_NEAR32; break;
282 case DBGFADDRESS_FLAGS_FAR64: pCur->enmReturnType = DBGFRETURNTYPE_NEAR64; break;
283 default: pCur->enmReturnType = DBGFRETURNTYPE_NEAR32; break; /// @todo 64-bit guests
284 }
285
286 uint64_t fAddrMask = UINT64_MAX;
287 if (enmCodeType == DBGFCODETYPE_RING0)
288 fAddrMask = (HC_ARCH_BITS == 64) ? UINT64_MAX : UINT32_MAX;
289 else
290 if (enmCodeType == DBGFCODETYPE_HYPER)
291 fAddrMask = UINT32_MAX;
292 else if (DBGFADDRESS_IS_FAR16(&pCur->AddrPC))
293 fAddrMask = UINT16_MAX;
294 else if (DBGFADDRESS_IS_FAR32(&pCur->AddrPC))
295 fAddrMask = UINT32_MAX;
296 else if (DBGFADDRESS_IS_FLAT(&pCur->AddrPC))
297 {
298 CPUMMODE CpuMode = CPUMGetGuestMode(VMMGetCpuById(pVM, idCpu));
299 if (CpuMode == CPUMMODE_REAL)
300 fAddrMask = UINT16_MAX;
301 else if (CpuMode == CPUMMODE_PROTECTED)
302 fAddrMask = UINT32_MAX;
303 }
304
305 if (pAddrStack)
306 pCur->AddrStack = *pAddrStack;
307 else
308 rc = DBGFR3AddrFromSelOff(pVM, idCpu, &pCur->AddrStack, pCtxCore->ss, pCtxCore->rsp & fAddrMask);
309
310 if (pAddrFrame)
311 pCur->AddrFrame = *pAddrFrame;
312 else if (RT_SUCCESS(rc))
313 rc = DBGFR3AddrFromSelOff(pVM, idCpu, &pCur->AddrFrame, pCtxCore->ss, pCtxCore->rbp & fAddrMask);
314 }
315 else
316 pCur->enmReturnType = enmReturnType;
317
318 /*
319 * The first frame.
320 */
321 if (RT_SUCCESS(rc))
322 rc = dbgfR3StackWalk(pVM, idCpu, pCur);
323 if (RT_FAILURE(rc))
324 {
325 DBGFR3StackWalkEnd(pCur);
326 return rc;
327 }
328
329 /*
330 * The other frames.
331 */
332 DBGFSTACKFRAME Next = *pCur;
333 while (!(pCur->fFlags & (DBGFSTACKFRAME_FLAGS_LAST | DBGFSTACKFRAME_FLAGS_MAX_DEPTH | DBGFSTACKFRAME_FLAGS_LOOP)))
334 {
335 /* try walk. */
336 rc = dbgfR3StackWalk(pVM, idCpu, &Next);
337 if (RT_FAILURE(rc))
338 break;
339
340 /* add the next frame to the chain. */
341 PDBGFSTACKFRAME pNext = (PDBGFSTACKFRAME)MMR3HeapAlloc(pVM, MM_TAG_DBGF_STACK, sizeof(*pNext));
342 if (!pNext)
343 {
344 DBGFR3StackWalkEnd(pCur);
345 return VERR_NO_MEMORY;
346 }
347 *pNext = Next;
348 pCur->pNextInternal = pNext;
349 pCur = pNext;
350 Assert(pCur->pNextInternal == NULL);
351
352 /* check for loop */
353 for (PCDBGFSTACKFRAME pLoop = pCur->pFirstInternal;
354 pLoop && pLoop != pCur;
355 pLoop = pLoop->pNextInternal)
356 if (pLoop->AddrFrame.FlatPtr == pCur->AddrFrame.FlatPtr)
357 {
358 pCur->fFlags |= DBGFSTACKFRAME_FLAGS_LOOP;
359 break;
360 }
361
362 /* check for insane recursion */
363 if (pCur->iFrame >= 2048)
364 pCur->fFlags |= DBGFSTACKFRAME_FLAGS_MAX_DEPTH;
365 }
366
367 *ppFirstFrame = pCur->pFirstInternal;
368 return rc;
369}
370
371
372/**
373 * Common worker for DBGFR3StackWalkBeginGuestEx, DBGFR3StackWalkBeginHyperEx,
374 * DBGFR3StackWalkBeginGuest and DBGFR3StackWalkBeginHyper.
375 */
376static int dbgfR3StackWalkBeginCommon(PVM pVM,
377 VMCPUID idCpu,
378 DBGFCODETYPE enmCodeType,
379 PCDBGFADDRESS pAddrFrame,
380 PCDBGFADDRESS pAddrStack,
381 PCDBGFADDRESS pAddrPC,
382 DBGFRETURNTYPE enmReturnType,
383 PCDBGFSTACKFRAME *ppFirstFrame)
384{
385#if HC_ARCH_BITS == 64
386 /** @todo Not implemented for 64 bits hosts yet */
387 if (enmCodeType == DBGFCODETYPE_RING0)
388 return VINF_SUCCESS;
389#endif
390 /*
391 * Validate parameters.
392 */
393 *ppFirstFrame = NULL;
394 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
395 AssertReturn(idCpu < pVM->cCPUs, VERR_INVALID_CPU_ID);
396 if (pAddrFrame)
397 AssertReturn(DBGFR3AddrIsValid(pVM, pAddrFrame), VERR_INVALID_PARAMETER);
398 if (pAddrStack)
399 AssertReturn(DBGFR3AddrIsValid(pVM, pAddrStack), VERR_INVALID_PARAMETER);
400 if (pAddrPC)
401 AssertReturn(DBGFR3AddrIsValid(pVM, pAddrPC), VERR_INVALID_PARAMETER);
402 AssertReturn(enmReturnType >= DBGFRETURNTYPE_INVALID && enmReturnType < DBGFRETURNTYPE_END, VERR_INVALID_PARAMETER);
403
404 /*
405 * Get the CPUM context pointer and pass it on the specified EMT.
406 */
407 PCCPUMCTXCORE pCtxCore = (enmCodeType == DBGFCODETYPE_GUEST)
408 ? CPUMGetGuestCtxCore(VMMGetCpuById(pVM, idCpu))
409 : CPUMGetHyperCtxCore(VMMGetCpuById(pVM, idCpu));
410 PVMREQ pReq;
411 int rc = VMR3ReqCall(pVM, idCpu, &pReq, RT_INDEFINITE_WAIT,
412 (PFNRT)dbgfR3StackWalkCtxFull, 9,
413 pVM, idCpu, pCtxCore, enmCodeType,
414 pAddrFrame, pAddrStack, pAddrPC, enmReturnType, ppFirstFrame);
415 if (RT_SUCCESS(rc))
416 rc = pReq->iStatus;
417 VMR3ReqFree(pReq);
418
419 return rc;
420
421}
422
423
424/**
425 * Begins a guest stack walk, extended version.
426 *
427 * This will walk the current stack, constructing a list of info frames which is
428 * returned to the caller. The caller uses DBGFR3StackWalkNext to traverse the
429 * list and DBGFR3StackWalkEnd to release it.
430 *
431 * @returns VINF_SUCCESS on success.
432 * @returns VERR_NO_MEMORY if we're out of memory.
433 *
434 * @param pVM The VM handle.
435 * @param idCpu The ID of the virtual CPU which stack we want to walk.
436 * @param enmCodeType Code type
437 * @param pAddrFrame Frame address to start at. (Optional)
438 * @param pAddrStack Stack address to start at. (Optional)
439 * @param pAddrPC Program counter to start at. (Optional)
440 * @param enmReturnType The return address type. (Optional)
441 * @param ppFirstFrame Where to return the pointer to the first info frame.
442 */
443VMMR3DECL(int) DBGFR3StackWalkBegintEx(PVM pVM,
444 VMCPUID idCpu,
445 DBGFCODETYPE enmCodeType,
446 PCDBGFADDRESS pAddrFrame,
447 PCDBGFADDRESS pAddrStack,
448 PCDBGFADDRESS pAddrPC,
449 DBGFRETURNTYPE enmReturnType,
450 PCDBGFSTACKFRAME *ppFirstFrame)
451{
452 return dbgfR3StackWalkBeginCommon(pVM, idCpu, enmCodeType, pAddrFrame, pAddrStack, pAddrPC, enmReturnType, ppFirstFrame);
453}
454
455
456/**
457 * Begins a guest stack walk.
458 *
459 * This will walk the current stack, constructing a list of info frames which is
460 * returned to the caller. The caller uses DBGFR3StackWalkNext to traverse the
461 * list and DBGFR3StackWalkEnd to release it.
462 *
463 * @returns VINF_SUCCESS on success.
464 * @returns VERR_NO_MEMORY if we're out of memory.
465 *
466 * @param pVM The VM handle.
467 * @param idCpu The ID of the virtual CPU which stack we want to walk.
468 * @param enmCodeType Code type
469 * @param ppFirstFrame Where to return the pointer to the first info frame.
470 */
471VMMR3DECL(int) DBGFR3StackWalkBegin(PVM pVM, VMCPUID idCpu, DBGFCODETYPE enmCodeType, PCDBGFSTACKFRAME *ppFirstFrame)
472{
473 return dbgfR3StackWalkBeginCommon(pVM, idCpu, enmCodeType, NULL, NULL, NULL, DBGFRETURNTYPE_INVALID, ppFirstFrame);
474}
475
476/**
477 * Gets the next stack frame.
478 *
479 * @returns Pointer to the info for the next stack frame.
480 * NULL if no more frames.
481 *
482 * @param pCurrent Pointer to the current stack frame.
483 *
484 */
485VMMR3DECL(PCDBGFSTACKFRAME) DBGFR3StackWalkNext(PCDBGFSTACKFRAME pCurrent)
486{
487 return pCurrent
488 ? pCurrent->pNextInternal
489 : NULL;
490}
491
492
493/**
494 * Ends a stack walk process.
495 *
496 * This *must* be called after a successful first call to any of the stack
497 * walker functions. If not called we will leak memory or other resources.
498 *
499 * @param pFirstFrame The frame returned by one of the the begin
500 * functions.
501 */
502VMMR3DECL(void) DBGFR3StackWalkEnd(PCDBGFSTACKFRAME pFirstFrame)
503{
504 if ( !pFirstFrame
505 || !pFirstFrame->pFirstInternal)
506 return;
507
508 PDBGFSTACKFRAME pFrame = (PDBGFSTACKFRAME)pFirstFrame->pFirstInternal;
509 while (pFrame)
510 {
511 PDBGFSTACKFRAME pCur = pFrame;
512 pFrame = (PDBGFSTACKFRAME)pCur->pNextInternal;
513 if (pFrame)
514 {
515 if (pCur->pSymReturnPC == pFrame->pSymPC)
516 pFrame->pSymPC = NULL;
517 if (pCur->pSymReturnPC == pFrame->pSymReturnPC)
518 pFrame->pSymReturnPC = NULL;
519
520 if (pCur->pSymPC == pFrame->pSymPC)
521 pFrame->pSymPC = NULL;
522 if (pCur->pSymPC == pFrame->pSymReturnPC)
523 pFrame->pSymReturnPC = NULL;
524
525 if (pCur->pLineReturnPC == pFrame->pLinePC)
526 pFrame->pLinePC = NULL;
527 if (pCur->pLineReturnPC == pFrame->pLineReturnPC)
528 pFrame->pLineReturnPC = NULL;
529
530 if (pCur->pLinePC == pFrame->pLinePC)
531 pFrame->pLinePC = NULL;
532 if (pCur->pLinePC == pFrame->pLineReturnPC)
533 pFrame->pLineReturnPC = NULL;
534 }
535
536 DBGFR3SymbolFree(pCur->pSymPC);
537 DBGFR3SymbolFree(pCur->pSymReturnPC);
538 DBGFR3LineFree(pCur->pLinePC);
539 DBGFR3LineFree(pCur->pLineReturnPC);
540
541 pCur->pNextInternal = NULL;
542 pCur->pFirstInternal = NULL;
543 pCur->fFlags = 0;
544 MMR3HeapFree(pCur);
545 }
546}
547
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette