VirtualBox

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

Last change on this file since 73389 was 73388, checked in by vboxsync, 7 years ago

DBGFR3Stack: Working on PE/AMD64 unwinding.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 38.1 KB
Line 
1/* $Id: DBGFStack.cpp 73388 2018-07-27 15:20:29Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Call Stack Analyser.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
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
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGF
23#include <VBox/vmm/dbgf.h>
24#include <VBox/vmm/selm.h>
25#include <VBox/vmm/mm.h>
26#include "DBGFInternal.h"
27#include <VBox/vmm/vm.h>
28#include <VBox/vmm/uvm.h>
29#include <VBox/err.h>
30#include <VBox/log.h>
31#include <iprt/param.h>
32#include <iprt/assert.h>
33#include <iprt/alloca.h>
34#include <iprt/mem.h>
35#include <iprt/string.h>
36#include <iprt/formats/pecoff.h>
37
38
39/*********************************************************************************************************************************
40* Structures and Typedefs *
41*********************************************************************************************************************************/
42/**
43 * Unwind context.
44 *
45 * @note Using a constructor and destructor here for simple+safe cleanup.
46 *
47 * @todo Generalize and move to IPRT or some such place.
48 */
49typedef struct DBGFUNWINDCTX
50{
51 PUVM m_pUVM;
52 VMCPUID m_idCpu;
53 RTDBGAS m_hAs;
54 uint64_t m_uPc;
55 uint64_t m_aGprs[16];
56
57 RTDBGMOD m_hCached;
58 RTUINTPTR m_uCachedMapping;
59 RTUINTPTR m_cbCachedMapping;
60 uint8_t *m_pbCachedInfo;
61 size_t m_cbCachedInfo;
62
63 /** Function table for PE/AMD64 (entire m_pbCachedInfo) . */
64 PCIMAGE_RUNTIME_FUNCTION_ENTRY m_paFunctions;
65 /** Number functions in m_paFunctions. */
66 size_t m_cFunctions;
67
68 DBGFUNWINDCTX(PUVM pUVM, VMCPUID idCpu, PCCPUMCTX pInitialCtx, RTDBGAS hAs)
69 {
70 if (pInitialCtx)
71 {
72 m_aGprs[X86_GREG_xAX] = pInitialCtx->rax;
73 m_aGprs[X86_GREG_xCX] = pInitialCtx->rcx;
74 m_aGprs[X86_GREG_xDX] = pInitialCtx->rdx;
75 m_aGprs[X86_GREG_xBX] = pInitialCtx->rbx;
76 m_aGprs[X86_GREG_xSP] = pInitialCtx->rsp;
77 m_aGprs[X86_GREG_xBP] = pInitialCtx->rbp;
78 m_aGprs[X86_GREG_xSI] = pInitialCtx->rsi;
79 m_aGprs[X86_GREG_xDI] = pInitialCtx->rdi;
80 m_aGprs[X86_GREG_x8 ] = pInitialCtx->r8;
81 m_aGprs[X86_GREG_x9 ] = pInitialCtx->r9;
82 m_aGprs[X86_GREG_x10] = pInitialCtx->r10;
83 m_aGprs[X86_GREG_x11] = pInitialCtx->r11;
84 m_aGprs[X86_GREG_x12] = pInitialCtx->r12;
85 m_aGprs[X86_GREG_x13] = pInitialCtx->r13;
86 m_aGprs[X86_GREG_x14] = pInitialCtx->r14;
87 m_aGprs[X86_GREG_x15] = pInitialCtx->r15;
88 m_uPc = pInitialCtx->rip;
89 }
90 else
91 {
92 RT_BZERO(m_aGprs, sizeof(m_aGprs));
93 m_uPc = 0;
94 }
95 m_pUVM = pUVM;
96 m_idCpu = idCpu;
97 m_hAs = hAs;
98
99 m_hCached = NIL_RTDBGMOD;
100 m_uCachedMapping = 0;
101 m_cbCachedMapping = 0;
102 m_pbCachedInfo = NULL;
103 m_cbCachedInfo = 0;
104 m_paFunctions = NULL;
105 m_cFunctions = 0;
106 }
107
108 ~DBGFUNWINDCTX();
109
110} DBGFUNWINDCTX;
111/** Pointer to unwind context. */
112typedef DBGFUNWINDCTX *PDBGFUNWINDCTX;
113
114
115static void dbgfR3UnwindCtxFlushCache(PDBGFUNWINDCTX pUnwindCtx)
116{
117 if (pUnwindCtx->m_hCached != NIL_RTDBGMOD)
118 {
119 RTDbgModRelease(pUnwindCtx->m_hCached);
120 pUnwindCtx->m_hCached = NIL_RTDBGMOD;
121 }
122 if (pUnwindCtx->m_pbCachedInfo)
123 {
124 RTMemFree(pUnwindCtx->m_pbCachedInfo);
125 pUnwindCtx->m_pbCachedInfo = NULL;
126 }
127 pUnwindCtx->m_cbCachedInfo = 0;
128 pUnwindCtx->m_paFunctions = NULL;
129 pUnwindCtx->m_cFunctions = 0;
130}
131
132
133DBGFUNWINDCTX::~DBGFUNWINDCTX()
134{
135 dbgfR3UnwindCtxFlushCache(this);
136}
137
138
139static bool dbgfR3UnwindCtxSetPcAndSp(PDBGFUNWINDCTX pUnwindCtx, PCDBGFADDRESS pAddrPC, PCDBGFADDRESS pAddrStack)
140{
141 pUnwindCtx->m_uPc = pAddrPC->FlatPtr;
142 pUnwindCtx->m_aGprs[X86_GREG_xSP] = pAddrStack->FlatPtr;
143 return true;
144}
145
146
147/**
148 * Try read a 64-bit value off the stack.
149 *
150 * @param pUnwindCtx The unwind context.
151 * @param uSrcAddr The stack address.
152 * @param puDst The read destination.
153 */
154static void dbgfR3UnwindCtxLoadU64(PDBGFUNWINDCTX pUnwindCtx, uint64_t uSrcAddr, uint64_t *puDst)
155{
156 DBGFADDRESS SrcAddr;
157 DBGFR3MemRead(pUnwindCtx->m_pUVM, pUnwindCtx->m_idCpu,
158 DBGFR3AddrFromFlat(pUnwindCtx->m_pUVM, &SrcAddr, uSrcAddr),
159 puDst, sizeof(*puDst));
160}
161
162
163/**
164 * Binary searches the lookup table.
165 *
166 * @returns RVA of unwind info on success, UINT32_MAX on failure.
167 * @param paFunctions The table to lookup @a offFunctionRva in.
168 * @param iEnd Size of the table.
169 * @param uRva The RVA of the function we want.
170 */
171DECLINLINE(PCIMAGE_RUNTIME_FUNCTION_ENTRY)
172dbgfR3UnwindCtxLookupUnwindInfoRva(PCIMAGE_RUNTIME_FUNCTION_ENTRY paFunctions, size_t iEnd, uint32_t uRva)
173{
174 size_t iBegin = 0;
175 while (iBegin < iEnd)
176 {
177 size_t const i = iBegin + (iEnd - iBegin) / 2;
178 PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry = &paFunctions[i];
179 if (uRva < pEntry->BeginAddress)
180 iEnd = i;
181 else if (uRva > pEntry->EndAddress)
182 iBegin = i + 1;
183 else
184 return pEntry;
185 }
186 return NULL;
187}
188
189
190static bool dbgfR3UnwindCtxDoOneFrameCached(PDBGFUNWINDCTX pUnwindCtx, uint32_t uRvaRip, PDBGFADDRESS pAddrFrame)
191{
192 /*
193 * Lookup the unwind info RVA and try read it.
194 */
195 PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry = dbgfR3UnwindCtxLookupUnwindInfoRva(pUnwindCtx->m_paFunctions,
196 pUnwindCtx->m_cFunctions, uRvaRip);
197 if (pEntry)
198 {
199 unsigned iFrameReg = ~0U;
200 unsigned offFrameReg = 0;
201
202 for (;;)
203 {
204 /*
205 * Get the info.
206 */
207 union
208 {
209 uint32_t uRva;
210 uint8_t ab[ RT_OFFSETOF(IMAGE_UNWIND_INFO, aOpcodes)
211 + sizeof(IMAGE_UNWIND_CODE) * 256
212 + sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)];
213 } uBuf;
214
215 uBuf.uRva = pEntry->UnwindInfoAddress;
216 size_t cbBuf = sizeof(uBuf);
217 int rc = RTDbgModImageQueryProp(pUnwindCtx->m_hCached, RTLDRPROP_UNWIND_INFO, &uBuf, cbBuf, &cbBuf);
218 if (RT_FAILURE(rc))
219 return false;
220
221 /*
222 * Check the info.
223 */
224 ASMCompilerBarrier(); /* we're aliasing */
225 PCIMAGE_UNWIND_INFO pInfo = (PCIMAGE_UNWIND_INFO)&uBuf;
226
227 if (pInfo->Version != 1)
228 return false;
229
230 /*
231 * Execute the opcodes.
232 */
233 unsigned const cOpcodes = pInfo->CountOfCodes;
234 unsigned iOpcode = 0;
235
236 /* First, skip opcodes that doesn't apply to us if we're the prolog. */
237 uint32_t offPc = uRvaRip - pEntry->BeginAddress;
238 if (offPc < pInfo->SizeOfProlog)
239 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.CodeOffset > offPc)
240 iOpcode++;
241
242 /* Execute. */
243 if (pInfo->FrameRegister != 0)
244 {
245 iFrameReg = pInfo->FrameRegister;
246 offFrameReg = pInfo->FrameOffset * 16;
247 }
248 while (iOpcode < cOpcodes)
249 {
250 Assert(pInfo->aOpcodes[iOpcode].u.CodeOffset <= offPc);
251 switch (pInfo->aOpcodes[iOpcode].u.UnwindOp)
252 {
253 case IMAGE_AMD64_UWOP_PUSH_NONVOL:
254 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= 8;
255 dbgfR3UnwindCtxLoadU64(pUnwindCtx, pUnwindCtx->m_aGprs[X86_GREG_xSP],
256 &pUnwindCtx->m_aGprs[pInfo->aOpcodes[iOpcode].u.OpInfo]);
257 iOpcode++;
258 break;
259
260 case IMAGE_AMD64_UWOP_ALLOC_LARGE:
261 iOpcode += 3;
262 AssertBreak(iOpcode <= cOpcodes);
263 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= *(uint32_t const *)&pInfo->aOpcodes[iOpcode - 2];
264 break;
265
266 case IMAGE_AMD64_UWOP_ALLOC_SMALL:
267 AssertBreak(iOpcode <= cOpcodes);
268 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= pInfo->aOpcodes[iOpcode].u.OpInfo * 8 + 8;
269 iOpcode++;
270 break;
271
272 case IMAGE_AMD64_UWOP_SET_FPREG:
273 iFrameReg = pInfo->aOpcodes[iOpcode].u.OpInfo;
274 offFrameReg = pInfo->FrameOffset * 16;
275 break;
276
277 case IMAGE_AMD64_UWOP_SAVE_NONVOL:
278 case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
279 {
280 bool const fFar = pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR;
281 unsigned const iGreg = pInfo->aOpcodes[iOpcode].u.OpInfo;
282 uint32_t off = 0;
283 iOpcode++;
284 if (iOpcode < cOpcodes)
285 {
286 off = pInfo->aOpcodes[iOpcode].FrameOffset;
287 iOpcode++;
288 if (fFar && iOpcode < cOpcodes)
289 {
290 off |= (uint32_t)pInfo->aOpcodes[iOpcode].FrameOffset << 16;
291 iOpcode++;
292 }
293 }
294 off *= 8;
295 dbgfR3UnwindCtxLoadU64(pUnwindCtx, pUnwindCtx->m_aGprs[X86_GREG_xSP] + off, &pUnwindCtx->m_aGprs[iGreg]);
296 break;
297 }
298
299 case IMAGE_AMD64_UWOP_SAVE_XMM128:
300 iOpcode += 2;
301 break;
302
303 case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
304 iOpcode += 3;
305 break;
306
307 case IMAGE_AMD64_UWOP_PUSH_MACHFRAME:
308 {
309 Assert(pInfo->aOpcodes[iOpcode].u.OpInfo <= 1);
310 if (pInfo->aOpcodes[iOpcode].u.OpInfo)
311 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= 8; /* error code */
312
313 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= 8; /* RIP */
314 dbgfR3UnwindCtxLoadU64(pUnwindCtx, pUnwindCtx->m_aGprs[X86_GREG_xSP], &pUnwindCtx->m_uPc);
315
316 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= 8; /* CS */
317 //dbgfR3UnwindCtxLoadU16(pUnwindCtx, pUnwindCtx->m_aGprs[X86_GREG_xSP], &pUnwindCtx->m_uCs);
318
319 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= 8; /* EFLAGS */
320 //dbgfR3UnwindCtxLoadU64(pUnwindCtx, pUnwindCtx->m_aGprs[X86_GREG_xSP], &pUnwindCtx->m_uRFlags);
321
322 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= 8; /* RSP */
323 uint64_t uNewRsp = (pUnwindCtx->m_aGprs[X86_GREG_xSP] - 8) & ~(uint64_t)15;
324 dbgfR3UnwindCtxLoadU64(pUnwindCtx, pUnwindCtx->m_aGprs[X86_GREG_xSP], &uNewRsp);
325
326 pUnwindCtx->m_aGprs[X86_GREG_xSP] -= 8; /* SS */
327 //dbgfR3UnwindCtxLoadU16(pUnwindCtx, pUnwindCtx->m_aGprs[X86_GREG_xSP], &pUnwindCtx->m_uSs);
328
329 pUnwindCtx->m_aGprs[X86_GREG_xSP] = uNewRsp;
330 return true;
331 }
332
333 case IMAGE_AMD64_UWOP_RESERVED_6:
334 AssertFailedReturn(false);
335
336 case IMAGE_AMD64_UWOP_RESERVED_7:
337 AssertFailedReturn(false);
338
339 default:
340 AssertMsgFailedReturn(("%u\n", pInfo->aOpcodes[iOpcode].u.UnwindOp), false);
341 }
342 }
343
344 /*
345 * Chained stuff?
346 */
347 if (!(pInfo->Flags & IMAGE_UNW_FLAGS_CHAININFO))
348 break;
349 /** @todo impl chains. */
350 break;
351 }
352
353 /** @todo do post processing. */
354 }
355
356 RT_NOREF_PV(pAddrFrame);
357 return false;
358}
359
360
361static bool dbgfR3UnwindCtxDoOneFrame(PDBGFUNWINDCTX pUnwindCtx, PDBGFADDRESS pAddrFrame)
362{
363 /*
364 * Hope for the same module as last time around.
365 */
366 RTUINTPTR offCache = pUnwindCtx->m_uPc - pUnwindCtx->m_uCachedMapping;
367 if (offCache < pUnwindCtx->m_cbCachedMapping)
368 return dbgfR3UnwindCtxDoOneFrameCached(pUnwindCtx, offCache, pAddrFrame);
369
370 /*
371 * Try locate the module.
372 */
373 RTDBGMOD hDbgMod = NIL_RTDBGMOD;
374 RTUINTPTR uBase = 0;
375 RTDBGSEGIDX idxSeg = NIL_RTDBGSEGIDX;
376 int rc = RTDbgAsModuleByAddr(pUnwindCtx->m_hAs, pUnwindCtx->m_uPc, &hDbgMod, &uBase, &idxSeg);
377 if (RT_SUCCESS(rc))
378 {
379 /* We cache the module regardless of unwind info. */
380 dbgfR3UnwindCtxFlushCache(pUnwindCtx);
381 pUnwindCtx->m_hCached = hDbgMod;
382 pUnwindCtx->m_uCachedMapping = uBase;
383 pUnwindCtx->m_cbCachedMapping = RTDbgModSegmentSize(hDbgMod, idxSeg);
384
385 /* Play simple for now. */
386 if ( idxSeg == NIL_RTDBGSEGIDX
387 && RTDbgModImageGetFormat(hDbgMod) == RTLDRFMT_32BIT_HACK /// @todo RTLDRFMT_PE (disabled code)
388 && RTDbgModImageGetArch(hDbgMod) == RTLDRARCH_AMD64)
389 {
390 /*
391 * Try query the unwind data.
392 */
393 size_t cbNeeded = 0;
394 rc = RTDbgModImageQueryProp(hDbgMod, RTLDRPROP_UNWIND_TABLE, NULL, 0, &cbNeeded);
395 if ( rc == VERR_BUFFER_OVERFLOW
396 && cbNeeded >= sizeof(*pUnwindCtx->m_paFunctions)
397 && cbNeeded < _64M)
398 {
399 void *pvBuf = RTMemAllocZ(cbNeeded + 32);
400 if (pvBuf)
401 {
402 rc = RTDbgModImageQueryProp(hDbgMod, RTLDRPROP_UNWIND_TABLE, pUnwindCtx->m_pbCachedInfo, cbNeeded, &cbNeeded);
403 if (RT_SUCCESS(rc))
404 {
405 pUnwindCtx->m_pbCachedInfo = (uint8_t *)pvBuf;
406 pUnwindCtx->m_cbCachedInfo = cbNeeded;
407 pUnwindCtx->m_paFunctions = (PCIMAGE_RUNTIME_FUNCTION_ENTRY)pvBuf;
408 pUnwindCtx->m_cFunctions = cbNeeded / sizeof(*pUnwindCtx->m_paFunctions);
409
410 return dbgfR3UnwindCtxDoOneFrameCached(pUnwindCtx, pUnwindCtx->m_uPc - pUnwindCtx->m_uCachedMapping,
411 pAddrFrame);
412 }
413 RTMemFree(pvBuf);
414 }
415 }
416 }
417 }
418 return false;
419}
420
421
422/**
423 * Read stack memory, will init entire buffer.
424 */
425DECLINLINE(int) dbgfR3StackRead(PUVM pUVM, VMCPUID idCpu, void *pvBuf, PCDBGFADDRESS pSrcAddr, size_t cb, size_t *pcbRead)
426{
427 int rc = DBGFR3MemRead(pUVM, idCpu, pSrcAddr, pvBuf, cb);
428 if (RT_FAILURE(rc))
429 {
430 /* fallback: byte by byte and zero the ones we fail to read. */
431 size_t cbRead;
432 for (cbRead = 0; cbRead < cb; cbRead++)
433 {
434 DBGFADDRESS Addr = *pSrcAddr;
435 rc = DBGFR3MemRead(pUVM, idCpu, DBGFR3AddrAdd(&Addr, cbRead), (uint8_t *)pvBuf + cbRead, 1);
436 if (RT_FAILURE(rc))
437 break;
438 }
439 if (cbRead)
440 rc = VINF_SUCCESS;
441 memset((char *)pvBuf + cbRead, 0, cb - cbRead);
442 *pcbRead = cbRead;
443 }
444 else
445 *pcbRead = cb;
446 return rc;
447}
448
449
450/**
451 * Internal worker routine.
452 *
453 * On x86 the typical stack frame layout is like this:
454 * .. ..
455 * 16 parameter 2
456 * 12 parameter 1
457 * 8 parameter 0
458 * 4 return address
459 * 0 old ebp; current ebp points here
460 *
461 * @todo Add AMD64 support (needs teaming up with the module management for
462 * unwind tables).
463 */
464DECL_NO_INLINE(static, int) dbgfR3StackWalk(PUVM pUVM, VMCPUID idCpu, RTDBGAS hAs, PDBGFSTACKFRAME pFrame)
465{
466 /*
467 * Stop if we got a read error in the previous run.
468 */
469 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_LAST)
470 return VERR_NO_MORE_FILES;
471
472 /*
473 * Read the raw frame data.
474 * We double cbRetAddr in case we find we have a far return.
475 */
476 const DBGFADDRESS AddrOldPC = pFrame->AddrPC;
477 unsigned cbRetAddr = DBGFReturnTypeSize(pFrame->enmReturnType);
478 unsigned cbStackItem;
479 switch (AddrOldPC.fFlags & DBGFADDRESS_FLAGS_TYPE_MASK)
480 {
481 case DBGFADDRESS_FLAGS_FAR16: cbStackItem = 2; break;
482 case DBGFADDRESS_FLAGS_FAR32: cbStackItem = 4; break;
483 case DBGFADDRESS_FLAGS_FAR64: cbStackItem = 8; break;
484 case DBGFADDRESS_FLAGS_RING0: cbStackItem = sizeof(RTHCUINTPTR); break;
485 default:
486 switch (pFrame->enmReturnType)
487 {
488 case DBGFRETURNTYPE_FAR16:
489 case DBGFRETURNTYPE_IRET16:
490 case DBGFRETURNTYPE_IRET32_V86:
491 case DBGFRETURNTYPE_NEAR16: cbStackItem = 2; break;
492
493 case DBGFRETURNTYPE_FAR32:
494 case DBGFRETURNTYPE_IRET32:
495 case DBGFRETURNTYPE_IRET32_PRIV:
496 case DBGFRETURNTYPE_NEAR32: cbStackItem = 4; break;
497
498 case DBGFRETURNTYPE_FAR64:
499 case DBGFRETURNTYPE_IRET64:
500 case DBGFRETURNTYPE_NEAR64: cbStackItem = 8; break;
501
502 default:
503 AssertMsgFailed(("%d\n", pFrame->enmReturnType));
504 cbStackItem = 4;
505 break;
506 }
507 }
508
509 union
510 {
511 uint64_t *pu64;
512 uint32_t *pu32;
513 uint16_t *pu16;
514 uint8_t *pb;
515 void *pv;
516 } u, uRet, uArgs, uBp;
517 size_t cbRead = cbRetAddr*2 + cbStackItem + sizeof(pFrame->Args);
518 u.pv = alloca(cbRead);
519 uBp = u;
520 uRet.pb = u.pb + cbStackItem;
521 uArgs.pb = u.pb + cbStackItem + cbRetAddr;
522
523 Assert(DBGFADDRESS_IS_VALID(&pFrame->AddrFrame));
524 int rc = dbgfR3StackRead(pUVM, idCpu, u.pv,
525 pFrame->fFlags & DBGFSTACKFRAME_FLAGS_ALL_VALID
526 ? &pFrame->AddrReturnFrame
527 : &pFrame->AddrFrame,
528 cbRead, &cbRead);
529 if ( RT_FAILURE(rc)
530 || cbRead < cbRetAddr + cbStackItem)
531 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_LAST;
532
533 /*
534 * The first step is taken in a different way than the others.
535 */
536 if (!(pFrame->fFlags & DBGFSTACKFRAME_FLAGS_ALL_VALID))
537 {
538 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_ALL_VALID;
539 pFrame->iFrame = 0;
540
541 /* Current PC - set by caller, just find symbol & line. */
542 if (DBGFADDRESS_IS_VALID(&pFrame->AddrPC))
543 {
544 pFrame->pSymPC = DBGFR3AsSymbolByAddrA(pUVM, hAs, &pFrame->AddrPC,
545 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
546 NULL /*poffDisp*/, NULL /*phMod*/);
547 pFrame->pLinePC = DBGFR3AsLineByAddrA(pUVM, hAs, &pFrame->AddrPC, NULL /*poffDisp*/, NULL /*phMod*/);
548 }
549 }
550 else /* 2nd and subsequent steps */
551 {
552 /* frame, pc and stack is taken from the existing frames return members. */
553 pFrame->AddrFrame = pFrame->AddrReturnFrame;
554 pFrame->AddrPC = pFrame->AddrReturnPC;
555 pFrame->pSymPC = pFrame->pSymReturnPC;
556 pFrame->pLinePC = pFrame->pLineReturnPC;
557
558 /* increment the frame number. */
559 pFrame->iFrame++;
560 }
561
562 /*
563 * Return Frame address.
564 */
565 pFrame->AddrReturnFrame = pFrame->AddrFrame;
566 switch (cbStackItem)
567 {
568 case 2: pFrame->AddrReturnFrame.off = *uBp.pu16; break;
569 case 4: pFrame->AddrReturnFrame.off = *uBp.pu32; break;
570 case 8: pFrame->AddrReturnFrame.off = *uBp.pu64; break;
571 default: AssertMsgFailedReturn(("cbStackItem=%d\n", cbStackItem), VERR_DBGF_STACK_IPE_1);
572 }
573
574 /* Watcom tries to keep the frame pointer odd for far returns. */
575 if (cbStackItem <= 4)
576 {
577 if (pFrame->AddrReturnFrame.off & 1)
578 {
579 pFrame->AddrReturnFrame.off &= ~(RTGCUINTPTR)1;
580 if (pFrame->enmReturnType == DBGFRETURNTYPE_NEAR16)
581 {
582 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_USED_ODD_EVEN;
583 pFrame->enmReturnType = DBGFRETURNTYPE_FAR16;
584 cbRetAddr = 4;
585 }
586 else if (pFrame->enmReturnType == DBGFRETURNTYPE_NEAR32)
587 {
588 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_USED_ODD_EVEN;
589 pFrame->enmReturnType = DBGFRETURNTYPE_FAR32;
590 cbRetAddr = 8;
591 }
592 }
593 else if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_USED_ODD_EVEN)
594 {
595 if (pFrame->enmReturnType == DBGFRETURNTYPE_FAR16)
596 {
597 pFrame->enmReturnType = DBGFRETURNTYPE_NEAR16;
598 cbRetAddr = 2;
599 }
600 else if (pFrame->enmReturnType == DBGFRETURNTYPE_NEAR32)
601 {
602 pFrame->enmReturnType = DBGFRETURNTYPE_FAR32;
603 cbRetAddr = 4;
604 }
605 pFrame->fFlags &= ~DBGFSTACKFRAME_FLAGS_USED_ODD_EVEN;
606 }
607 uArgs.pb = u.pb + cbStackItem + cbRetAddr;
608 }
609
610 pFrame->AddrReturnFrame.FlatPtr += pFrame->AddrReturnFrame.off - pFrame->AddrFrame.off;
611
612 /*
613 * Return PC and Stack Addresses.
614 */
615 /** @todo AddrReturnStack is not correct for stdcall and pascal. (requires scope info) */
616 pFrame->AddrReturnStack = pFrame->AddrFrame;
617 pFrame->AddrReturnStack.off += cbStackItem + cbRetAddr;
618 pFrame->AddrReturnStack.FlatPtr += cbStackItem + cbRetAddr;
619
620 pFrame->AddrReturnPC = pFrame->AddrPC;
621 switch (pFrame->enmReturnType)
622 {
623 case DBGFRETURNTYPE_NEAR16:
624 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
625 {
626 pFrame->AddrReturnPC.FlatPtr += *uRet.pu16 - pFrame->AddrReturnPC.off;
627 pFrame->AddrReturnPC.off = *uRet.pu16;
628 }
629 else
630 DBGFR3AddrFromFlat(pUVM, &pFrame->AddrReturnPC, *uRet.pu16);
631 break;
632 case DBGFRETURNTYPE_NEAR32:
633 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
634 {
635 pFrame->AddrReturnPC.FlatPtr += *uRet.pu32 - pFrame->AddrReturnPC.off;
636 pFrame->AddrReturnPC.off = *uRet.pu32;
637 }
638 else
639 DBGFR3AddrFromFlat(pUVM, &pFrame->AddrReturnPC, *uRet.pu32);
640 break;
641 case DBGFRETURNTYPE_NEAR64:
642 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
643 {
644 pFrame->AddrReturnPC.FlatPtr += *uRet.pu64 - pFrame->AddrReturnPC.off;
645 pFrame->AddrReturnPC.off = *uRet.pu64;
646 }
647 else
648 DBGFR3AddrFromFlat(pUVM, &pFrame->AddrReturnPC, *uRet.pu64);
649 break;
650 case DBGFRETURNTYPE_FAR16:
651 DBGFR3AddrFromSelOff(pUVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[1], uRet.pu16[0]);
652 break;
653 case DBGFRETURNTYPE_FAR32:
654 DBGFR3AddrFromSelOff(pUVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
655 break;
656 case DBGFRETURNTYPE_FAR64:
657 DBGFR3AddrFromSelOff(pUVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[4], uRet.pu64[0]);
658 break;
659 case DBGFRETURNTYPE_IRET16:
660 DBGFR3AddrFromSelOff(pUVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[1], uRet.pu16[0]);
661 break;
662 case DBGFRETURNTYPE_IRET32:
663 DBGFR3AddrFromSelOff(pUVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
664 break;
665 case DBGFRETURNTYPE_IRET32_PRIV:
666 DBGFR3AddrFromSelOff(pUVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
667 break;
668 case DBGFRETURNTYPE_IRET32_V86:
669 DBGFR3AddrFromSelOff(pUVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
670 break;
671 case DBGFRETURNTYPE_IRET64:
672 DBGFR3AddrFromSelOff(pUVM, idCpu, &pFrame->AddrReturnPC, uRet.pu16[4], uRet.pu64[0]);
673 break;
674 default:
675 AssertMsgFailed(("enmReturnType=%d\n", pFrame->enmReturnType));
676 return VERR_INVALID_PARAMETER;
677 }
678
679 pFrame->pSymReturnPC = DBGFR3AsSymbolByAddrA(pUVM, hAs, &pFrame->AddrReturnPC,
680 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
681 NULL /*poffDisp*/, NULL /*phMod*/);
682 pFrame->pLineReturnPC = DBGFR3AsLineByAddrA(pUVM, hAs, &pFrame->AddrReturnPC, NULL /*poffDisp*/, NULL /*phMod*/);
683
684 /*
685 * Frame bitness flag.
686 */
687 switch (cbStackItem)
688 {
689 case 2: pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_16BIT; break;
690 case 4: pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_32BIT; break;
691 case 8: pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_64BIT; break;
692 default: AssertMsgFailedReturn(("cbStackItem=%d\n", cbStackItem), VERR_DBGF_STACK_IPE_2);
693 }
694
695 /*
696 * The arguments.
697 */
698 memcpy(&pFrame->Args, uArgs.pv, sizeof(pFrame->Args));
699
700 return VINF_SUCCESS;
701}
702
703
704/**
705 * Walks the entire stack allocating memory as we walk.
706 */
707static DECLCALLBACK(int) dbgfR3StackWalkCtxFull(PUVM pUVM, VMCPUID idCpu, PCCPUMCTX pCtx, RTDBGAS hAs,
708 DBGFCODETYPE enmCodeType,
709 PCDBGFADDRESS pAddrFrame,
710 PCDBGFADDRESS pAddrStack,
711 PCDBGFADDRESS pAddrPC,
712 DBGFRETURNTYPE enmReturnType,
713 PCDBGFSTACKFRAME *ppFirstFrame)
714{
715 DBGFUNWINDCTX UnwindCtx(pUVM, idCpu, pCtx, hAs);
716
717 /* alloc first frame. */
718 PDBGFSTACKFRAME pCur = (PDBGFSTACKFRAME)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF_STACK, sizeof(*pCur));
719 if (!pCur)
720 return VERR_NO_MEMORY;
721
722 /*
723 * Initialize the frame.
724 */
725 pCur->pNextInternal = NULL;
726 pCur->pFirstInternal = pCur;
727
728 int rc = VINF_SUCCESS;
729 if (pAddrPC)
730 pCur->AddrPC = *pAddrPC;
731 else if (enmCodeType != DBGFCODETYPE_GUEST)
732 DBGFR3AddrFromFlat(pUVM, &pCur->AddrPC, pCtx->rip);
733 else
734 rc = DBGFR3AddrFromSelOff(pUVM, idCpu, &pCur->AddrPC, pCtx->cs.Sel, pCtx->rip);
735 if (RT_SUCCESS(rc))
736 {
737 uint64_t fAddrMask;
738 if (enmCodeType == DBGFCODETYPE_RING0)
739 fAddrMask = HC_ARCH_BITS == 64 ? UINT64_MAX : UINT32_MAX;
740 else if (enmCodeType == DBGFCODETYPE_HYPER)
741 fAddrMask = UINT32_MAX;
742 else if (DBGFADDRESS_IS_FAR16(&pCur->AddrPC))
743 fAddrMask = UINT16_MAX;
744 else if (DBGFADDRESS_IS_FAR32(&pCur->AddrPC))
745 fAddrMask = UINT32_MAX;
746 else if (DBGFADDRESS_IS_FAR64(&pCur->AddrPC))
747 fAddrMask = UINT64_MAX;
748 else
749 {
750 PVMCPU pVCpu = VMMGetCpuById(pUVM->pVM, idCpu);
751 CPUMMODE enmCpuMode = CPUMGetGuestMode(pVCpu);
752 if (enmCpuMode == CPUMMODE_REAL)
753 {
754 fAddrMask = UINT16_MAX;
755 if (enmReturnType == DBGFRETURNTYPE_INVALID)
756 pCur->enmReturnType = DBGFRETURNTYPE_NEAR16;
757 }
758 else if ( enmCpuMode == CPUMMODE_PROTECTED
759 || !CPUMIsGuestIn64BitCode(pVCpu))
760 {
761 fAddrMask = UINT32_MAX;
762 if (enmReturnType == DBGFRETURNTYPE_INVALID)
763 pCur->enmReturnType = DBGFRETURNTYPE_NEAR32;
764 }
765 else
766 {
767 fAddrMask = UINT64_MAX;
768 if (enmReturnType == DBGFRETURNTYPE_INVALID)
769 pCur->enmReturnType = DBGFRETURNTYPE_NEAR64;
770 }
771 }
772
773 if (enmReturnType == DBGFRETURNTYPE_INVALID)
774 switch (pCur->AddrPC.fFlags & DBGFADDRESS_FLAGS_TYPE_MASK)
775 {
776 case DBGFADDRESS_FLAGS_FAR16: pCur->enmReturnType = DBGFRETURNTYPE_NEAR16; break;
777 case DBGFADDRESS_FLAGS_FAR32: pCur->enmReturnType = DBGFRETURNTYPE_NEAR32; break;
778 case DBGFADDRESS_FLAGS_FAR64: pCur->enmReturnType = DBGFRETURNTYPE_NEAR64; break;
779 case DBGFADDRESS_FLAGS_RING0:
780 pCur->enmReturnType = HC_ARCH_BITS == 64 ? DBGFRETURNTYPE_NEAR64 : DBGFRETURNTYPE_NEAR32;
781 break;
782 default:
783 pCur->enmReturnType = DBGFRETURNTYPE_NEAR32;
784 break;
785 }
786
787
788 if (pAddrStack)
789 pCur->AddrStack = *pAddrStack;
790 else if (enmCodeType != DBGFCODETYPE_GUEST)
791 DBGFR3AddrFromFlat(pUVM, &pCur->AddrStack, pCtx->rsp & fAddrMask);
792 else
793 rc = DBGFR3AddrFromSelOff(pUVM, idCpu, &pCur->AddrStack, pCtx->ss.Sel, pCtx->rsp & fAddrMask);
794
795 if (pAddrFrame)
796 pCur->AddrFrame = *pAddrFrame;
797 else
798 {
799 if ( RT_SUCCESS(rc)
800 && dbgfR3UnwindCtxSetPcAndSp(&UnwindCtx, &pCur->AddrPC, &pCur->AddrStack)
801 && dbgfR3UnwindCtxDoOneFrame(&UnwindCtx, &pCur->AddrFrame))
802 { }
803 else if (enmCodeType != DBGFCODETYPE_GUEST)
804 DBGFR3AddrFromFlat(pUVM, &pCur->AddrFrame, pCtx->rbp & fAddrMask);
805 else if (RT_SUCCESS(rc))
806 rc = DBGFR3AddrFromSelOff(pUVM, idCpu, &pCur->AddrFrame, pCtx->ss.Sel, pCtx->rbp & fAddrMask);
807 }
808 }
809 else
810 pCur->enmReturnType = enmReturnType;
811
812 /*
813 * The first frame.
814 */
815 if (RT_SUCCESS(rc))
816 rc = dbgfR3StackWalk(pUVM, idCpu, hAs, pCur);
817 if (RT_FAILURE(rc))
818 {
819 DBGFR3StackWalkEnd(pCur);
820 return rc;
821 }
822
823 /*
824 * The other frames.
825 */
826 DBGFSTACKFRAME Next = *pCur;
827 while (!(pCur->fFlags & (DBGFSTACKFRAME_FLAGS_LAST | DBGFSTACKFRAME_FLAGS_MAX_DEPTH | DBGFSTACKFRAME_FLAGS_LOOP)))
828 {
829 /* try walk. */
830 rc = dbgfR3StackWalk(pUVM, idCpu, hAs, &Next);
831 if (RT_FAILURE(rc))
832 break;
833
834 /* add the next frame to the chain. */
835 PDBGFSTACKFRAME pNext = (PDBGFSTACKFRAME)MMR3HeapAllocU(pUVM, MM_TAG_DBGF_STACK, sizeof(*pNext));
836 if (!pNext)
837 {
838 DBGFR3StackWalkEnd(pCur);
839 return VERR_NO_MEMORY;
840 }
841 *pNext = Next;
842 pCur->pNextInternal = pNext;
843 pCur = pNext;
844 Assert(pCur->pNextInternal == NULL);
845
846 /* check for loop */
847 for (PCDBGFSTACKFRAME pLoop = pCur->pFirstInternal;
848 pLoop && pLoop != pCur;
849 pLoop = pLoop->pNextInternal)
850 if (pLoop->AddrFrame.FlatPtr == pCur->AddrFrame.FlatPtr)
851 {
852 pCur->fFlags |= DBGFSTACKFRAME_FLAGS_LOOP;
853 break;
854 }
855
856 /* check for insane recursion */
857 if (pCur->iFrame >= 2048)
858 pCur->fFlags |= DBGFSTACKFRAME_FLAGS_MAX_DEPTH;
859 }
860
861 *ppFirstFrame = pCur->pFirstInternal;
862 return rc;
863}
864
865
866/**
867 * Common worker for DBGFR3StackWalkBeginGuestEx, DBGFR3StackWalkBeginHyperEx,
868 * DBGFR3StackWalkBeginGuest and DBGFR3StackWalkBeginHyper.
869 */
870static int dbgfR3StackWalkBeginCommon(PUVM pUVM,
871 VMCPUID idCpu,
872 DBGFCODETYPE enmCodeType,
873 PCDBGFADDRESS pAddrFrame,
874 PCDBGFADDRESS pAddrStack,
875 PCDBGFADDRESS pAddrPC,
876 DBGFRETURNTYPE enmReturnType,
877 PCDBGFSTACKFRAME *ppFirstFrame)
878{
879 /*
880 * Validate parameters.
881 */
882 *ppFirstFrame = NULL;
883 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
884 PVM pVM = pUVM->pVM;
885 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
886 AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
887 if (pAddrFrame)
888 AssertReturn(DBGFR3AddrIsValid(pUVM, pAddrFrame), VERR_INVALID_PARAMETER);
889 if (pAddrStack)
890 AssertReturn(DBGFR3AddrIsValid(pUVM, pAddrStack), VERR_INVALID_PARAMETER);
891 if (pAddrPC)
892 AssertReturn(DBGFR3AddrIsValid(pUVM, pAddrPC), VERR_INVALID_PARAMETER);
893 AssertReturn(enmReturnType >= DBGFRETURNTYPE_INVALID && enmReturnType < DBGFRETURNTYPE_END, VERR_INVALID_PARAMETER);
894
895 /*
896 * Get the CPUM context pointer and pass it on the specified EMT.
897 */
898 RTDBGAS hAs;
899 PCCPUMCTX pCtx;
900 switch (enmCodeType)
901 {
902 case DBGFCODETYPE_GUEST:
903 pCtx = CPUMQueryGuestCtxPtr(VMMGetCpuById(pVM, idCpu));
904 hAs = DBGF_AS_GLOBAL;
905 break;
906 case DBGFCODETYPE_HYPER:
907 pCtx = CPUMQueryGuestCtxPtr(VMMGetCpuById(pVM, idCpu));
908 hAs = DBGF_AS_RC_AND_GC_GLOBAL;
909 break;
910 case DBGFCODETYPE_RING0:
911 pCtx = NULL; /* No valid context present. */
912 hAs = DBGF_AS_R0;
913 break;
914 default:
915 AssertFailedReturn(VERR_INVALID_PARAMETER);
916 }
917 return VMR3ReqPriorityCallWaitU(pUVM, idCpu, (PFNRT)dbgfR3StackWalkCtxFull, 10,
918 pUVM, idCpu, pCtx, hAs, enmCodeType,
919 pAddrFrame, pAddrStack, pAddrPC, enmReturnType, ppFirstFrame);
920}
921
922
923/**
924 * Begins a guest stack walk, extended version.
925 *
926 * This will walk the current stack, constructing a list of info frames which is
927 * returned to the caller. The caller uses DBGFR3StackWalkNext to traverse the
928 * list and DBGFR3StackWalkEnd to release it.
929 *
930 * @returns VINF_SUCCESS on success.
931 * @returns VERR_NO_MEMORY if we're out of memory.
932 *
933 * @param pUVM The user mode VM handle.
934 * @param idCpu The ID of the virtual CPU which stack we want to walk.
935 * @param enmCodeType Code type
936 * @param pAddrFrame Frame address to start at. (Optional)
937 * @param pAddrStack Stack address to start at. (Optional)
938 * @param pAddrPC Program counter to start at. (Optional)
939 * @param enmReturnType The return address type. (Optional)
940 * @param ppFirstFrame Where to return the pointer to the first info frame.
941 */
942VMMR3DECL(int) DBGFR3StackWalkBeginEx(PUVM pUVM,
943 VMCPUID idCpu,
944 DBGFCODETYPE enmCodeType,
945 PCDBGFADDRESS pAddrFrame,
946 PCDBGFADDRESS pAddrStack,
947 PCDBGFADDRESS pAddrPC,
948 DBGFRETURNTYPE enmReturnType,
949 PCDBGFSTACKFRAME *ppFirstFrame)
950{
951 return dbgfR3StackWalkBeginCommon(pUVM, idCpu, enmCodeType, pAddrFrame, pAddrStack, pAddrPC, enmReturnType, ppFirstFrame);
952}
953
954
955/**
956 * Begins a guest stack walk.
957 *
958 * This will walk the current stack, constructing a list of info frames which is
959 * returned to the caller. The caller uses DBGFR3StackWalkNext to traverse the
960 * list and DBGFR3StackWalkEnd to release it.
961 *
962 * @returns VINF_SUCCESS on success.
963 * @returns VERR_NO_MEMORY if we're out of memory.
964 *
965 * @param pUVM The user mode VM handle.
966 * @param idCpu The ID of the virtual CPU which stack we want to walk.
967 * @param enmCodeType Code type
968 * @param ppFirstFrame Where to return the pointer to the first info frame.
969 */
970VMMR3DECL(int) DBGFR3StackWalkBegin(PUVM pUVM, VMCPUID idCpu, DBGFCODETYPE enmCodeType, PCDBGFSTACKFRAME *ppFirstFrame)
971{
972 return dbgfR3StackWalkBeginCommon(pUVM, idCpu, enmCodeType, NULL, NULL, NULL, DBGFRETURNTYPE_INVALID, ppFirstFrame);
973}
974
975/**
976 * Gets the next stack frame.
977 *
978 * @returns Pointer to the info for the next stack frame.
979 * NULL if no more frames.
980 *
981 * @param pCurrent Pointer to the current stack frame.
982 *
983 */
984VMMR3DECL(PCDBGFSTACKFRAME) DBGFR3StackWalkNext(PCDBGFSTACKFRAME pCurrent)
985{
986 return pCurrent
987 ? pCurrent->pNextInternal
988 : NULL;
989}
990
991
992/**
993 * Ends a stack walk process.
994 *
995 * This *must* be called after a successful first call to any of the stack
996 * walker functions. If not called we will leak memory or other resources.
997 *
998 * @param pFirstFrame The frame returned by one of the begin functions.
999 */
1000VMMR3DECL(void) DBGFR3StackWalkEnd(PCDBGFSTACKFRAME pFirstFrame)
1001{
1002 if ( !pFirstFrame
1003 || !pFirstFrame->pFirstInternal)
1004 return;
1005
1006 PDBGFSTACKFRAME pFrame = (PDBGFSTACKFRAME)pFirstFrame->pFirstInternal;
1007 while (pFrame)
1008 {
1009 PDBGFSTACKFRAME pCur = pFrame;
1010 pFrame = (PDBGFSTACKFRAME)pCur->pNextInternal;
1011 if (pFrame)
1012 {
1013 if (pCur->pSymReturnPC == pFrame->pSymPC)
1014 pFrame->pSymPC = NULL;
1015 if (pCur->pSymReturnPC == pFrame->pSymReturnPC)
1016 pFrame->pSymReturnPC = NULL;
1017
1018 if (pCur->pSymPC == pFrame->pSymPC)
1019 pFrame->pSymPC = NULL;
1020 if (pCur->pSymPC == pFrame->pSymReturnPC)
1021 pFrame->pSymReturnPC = NULL;
1022
1023 if (pCur->pLineReturnPC == pFrame->pLinePC)
1024 pFrame->pLinePC = NULL;
1025 if (pCur->pLineReturnPC == pFrame->pLineReturnPC)
1026 pFrame->pLineReturnPC = NULL;
1027
1028 if (pCur->pLinePC == pFrame->pLinePC)
1029 pFrame->pLinePC = NULL;
1030 if (pCur->pLinePC == pFrame->pLineReturnPC)
1031 pFrame->pLineReturnPC = NULL;
1032 }
1033
1034 RTDbgSymbolFree(pCur->pSymPC);
1035 RTDbgSymbolFree(pCur->pSymReturnPC);
1036 RTDbgLineFree(pCur->pLinePC);
1037 RTDbgLineFree(pCur->pLineReturnPC);
1038
1039 pCur->pNextInternal = NULL;
1040 pCur->pFirstInternal = NULL;
1041 pCur->fFlags = 0;
1042 MMR3HeapFree(pCur);
1043 }
1044}
1045
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