VirtualBox

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

Last change on this file since 7802 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.6 KB
Line 
1/* $Id: DBGFStack.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * VMM DBGF - Debugger Facility, Call Stack Analyser.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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/dbgf.h>
24#include <VBox/selm.h>
25#include "DBGFInternal.h"
26#include <VBox/vm.h>
27#include <VBox/mm.h>
28#include <VBox/err.h>
29#include <VBox/log.h>
30#include <iprt/assert.h>
31#include <iprt/string.h>
32#include <iprt/alloca.h>
33
34
35
36/**
37 * Read stack memory.
38 */
39DECLINLINE(int) dbgfR3Read(PVM pVM, void *pvBuf, RTGCUINTPTR GCPtr, size_t cb, size_t *pcbRead)
40{
41 int rc = MMR3ReadGCVirt(pVM, pvBuf, GCPtr, cb);
42 if (VBOX_FAILURE(rc))
43 {
44 size_t cbRead;
45 for (cbRead = 0; cbRead < cb; cbRead++)
46 {
47 rc = MMR3ReadGCVirt(pVM, (uint8_t *)pvBuf + cbRead, GCPtr + cbRead, 1);
48 if (VBOX_FAILURE(rc))
49 break;
50 }
51 if (cbRead)
52 rc = VINF_SUCCESS;
53 memset((char *)pvBuf + cbRead, 0, cb - cbRead);
54 *pcbRead = cbRead;
55 }
56 else
57 *pcbRead = cb;
58 return rc;
59}
60
61
62
63
64/**
65 * Internal worker routine.
66 *
67 * On x86 the typical stack frame layout is like this:
68 * .. ..
69 * 16 parameter 2
70 * 12 parameter 1
71 * 8 parameter 0
72 * 4 return address
73 * 0 old ebp; current ebp points here
74 */
75static int dbgfR3StackWalk(PVM pVM, PDBGFSTACKFRAME pFrame)
76{
77 /*
78 * Stop if we got a read error in the previous run.
79 */
80 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_LAST)
81 return VERR_NO_MORE_FILES;
82
83 /*
84 * Read the raw frame data.
85 */
86 const DBGFADDRESS AddrOldPC = pFrame->AddrPC;
87 const unsigned cbRetAddr = DBGFReturnTypeSize(pFrame->enmReturnType);
88 unsigned cbStackItem;
89 switch (AddrOldPC.fFlags & DBGFADDRESS_FLAGS_TYPE_MASK)
90 {
91 case DBGFADDRESS_FLAGS_FAR16: cbStackItem = 2; break;
92 case DBGFADDRESS_FLAGS_FAR32: cbStackItem = 4; break;
93 case DBGFADDRESS_FLAGS_FAR64: cbStackItem = 8; break;
94 default: cbStackItem = 4; break; /// @todo 64-bit guests.
95 }
96
97 union
98 {
99 uint64_t *pu64;
100 uint32_t *pu32;
101 uint16_t *pu16;
102 uint8_t *pb;
103 void *pv;
104 } u, uRet, uArgs, uBp;
105 size_t cbRead = cbRetAddr + cbStackItem + sizeof(pFrame->Args);
106 u.pv = alloca(cbRead);
107 uBp = u;
108 uRet.pb = u.pb + cbStackItem;
109 uArgs.pb = u.pb + cbStackItem + cbRetAddr;
110
111 Assert(DBGFADDRESS_IS_VALID(&pFrame->AddrFrame));
112 int rc = dbgfR3Read(pVM, u.pv,
113 pFrame->fFlags & DBGFSTACKFRAME_FLAGS_ALL_VALID
114 ? pFrame->AddrReturnFrame.FlatPtr
115 : pFrame->AddrFrame.FlatPtr,
116 cbRead, &cbRead);
117 if ( VBOX_FAILURE(rc)
118 || cbRead < cbRetAddr + cbStackItem)
119 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_LAST;
120
121 /*
122 * The first step is taken in a different way than the others.
123 */
124 if (!(pFrame->fFlags & DBGFSTACKFRAME_FLAGS_ALL_VALID))
125 {
126 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_ALL_VALID;
127 pFrame->iFrame = 0;
128
129 /* Current PC - set by caller, just find symbol & line. */
130 if (DBGFADDRESS_IS_VALID(&pFrame->AddrPC))
131 {
132 pFrame->pSymPC = DBGFR3SymbolByAddrAlloc(pVM, pFrame->AddrPC.FlatPtr, NULL);
133 pFrame->pLinePC = DBGFR3LineByAddrAlloc(pVM, pFrame->AddrPC.FlatPtr, NULL);
134 }
135 }
136 else /* 2nd and subsequent steps */
137 {
138 /* frame, pc and stack is taken from the existing frames return members. */
139 pFrame->AddrFrame = pFrame->AddrReturnFrame;
140 pFrame->AddrPC = pFrame->AddrReturnPC;
141 pFrame->pSymPC = pFrame->pSymReturnPC;
142 pFrame->pLinePC = pFrame->pLineReturnPC;
143
144 /* increment the frame number. */
145 pFrame->iFrame++;
146 }
147
148 /*
149 * Return Frame address.
150 */
151 pFrame->AddrReturnFrame = pFrame->AddrFrame;
152 switch (cbStackItem)
153 {
154 case 2: pFrame->AddrReturnFrame.off = *uBp.pu16; break;
155 case 4: pFrame->AddrReturnFrame.off = *uBp.pu32; break;
156 case 8: pFrame->AddrReturnFrame.off = *uBp.pu64; break;
157 default: AssertMsgFailed(("cbStackItem=%d\n", cbStackItem)); return VERR_INTERNAL_ERROR;
158 }
159 pFrame->AddrReturnFrame.FlatPtr += pFrame->AddrReturnFrame.off - pFrame->AddrFrame.off;
160
161 /*
162 * Return PC and Stack Addresses.
163 */
164 /** @todo AddrReturnStack is not correct for stdcall and pascal. (requires scope info) */
165 pFrame->AddrReturnStack = pFrame->AddrFrame;
166 pFrame->AddrReturnStack.off += cbStackItem + cbRetAddr;
167 pFrame->AddrReturnStack.FlatPtr += cbStackItem + cbRetAddr;
168
169 pFrame->AddrReturnPC = pFrame->AddrPC;
170 switch (pFrame->enmReturnType)
171 {
172 case DBGFRETURNTYPE_NEAR16:
173 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
174 {
175 pFrame->AddrReturnPC.FlatPtr += *uRet.pu16 - pFrame->AddrReturnPC.off;
176 pFrame->AddrReturnPC.off = *uRet.pu16;
177 }
178 else
179 DBGFR3AddrFromFlat(pVM, &pFrame->AddrReturnPC, *uRet.pu16);
180 break;
181 case DBGFRETURNTYPE_NEAR32:
182 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
183 {
184 pFrame->AddrReturnPC.FlatPtr += *uRet.pu32 - pFrame->AddrReturnPC.off;
185 pFrame->AddrReturnPC.off = *uRet.pu32;
186 }
187 else
188 DBGFR3AddrFromFlat(pVM, &pFrame->AddrReturnPC, *uRet.pu32);
189 break;
190 case DBGFRETURNTYPE_NEAR64:
191 if (DBGFADDRESS_IS_VALID(&pFrame->AddrReturnPC))
192 {
193 pFrame->AddrReturnPC.FlatPtr += *uRet.pu64 - pFrame->AddrReturnPC.off;
194 pFrame->AddrReturnPC.off = *uRet.pu64;
195 }
196 else
197 DBGFR3AddrFromFlat(pVM, &pFrame->AddrReturnPC, *uRet.pu64);
198 break;
199 case DBGFRETURNTYPE_FAR16:
200 DBGFR3AddrFromSelOff(pVM, &pFrame->AddrReturnPC, uRet.pu16[1], uRet.pu16[0]);
201 break;
202 case DBGFRETURNTYPE_FAR32:
203 DBGFR3AddrFromSelOff(pVM, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
204 break;
205 case DBGFRETURNTYPE_FAR64:
206 DBGFR3AddrFromSelOff(pVM, &pFrame->AddrReturnPC, uRet.pu16[4], uRet.pu64[0]);
207 break;
208 case DBGFRETURNTYPE_IRET16:
209 DBGFR3AddrFromSelOff(pVM, &pFrame->AddrReturnPC, uRet.pu16[1], uRet.pu16[0]);
210 break;
211 case DBGFRETURNTYPE_IRET32:
212 DBGFR3AddrFromSelOff(pVM, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
213 break;
214 case DBGFRETURNTYPE_IRET32_PRIV:
215 DBGFR3AddrFromSelOff(pVM, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
216 break;
217 case DBGFRETURNTYPE_IRET32_V86:
218 DBGFR3AddrFromSelOff(pVM, &pFrame->AddrReturnPC, uRet.pu16[2], uRet.pu32[0]);
219 break;
220 case DBGFRETURNTYPE_IRET64:
221 DBGFR3AddrFromSelOff(pVM, &pFrame->AddrReturnPC, uRet.pu16[4], uRet.pu64[0]);
222 break;
223 default:
224 AssertMsgFailed(("enmReturnType=%d\n", pFrame->enmReturnType));
225 return VERR_INVALID_PARAMETER;
226 }
227
228 pFrame->pSymReturnPC = DBGFR3SymbolByAddrAlloc(pVM, pFrame->AddrReturnPC.FlatPtr, NULL);
229 pFrame->pLineReturnPC = DBGFR3LineByAddrAlloc(pVM, pFrame->AddrReturnPC.FlatPtr, NULL);
230
231 /*
232 * The arguments.
233 */
234 memcpy(&pFrame->Args, uArgs.pv, sizeof(pFrame->Args));
235
236 return VINF_SUCCESS;
237}
238
239
240/**
241 * Walks the entire stack allocating memory as we walk.
242 */
243static DECLCALLBACK(int) dbgfR3StackWalkCtxFull(PVM pVM, PDBGFSTACKFRAME pFrame, PCCPUMCTXCORE pCtxCore, bool fGuest)
244{
245 pFrame->pNext = NULL;
246 pFrame->pFirst = NULL;
247
248 /* alloc first frame. */
249 PDBGFSTACKFRAME pCur = (PDBGFSTACKFRAME)MMR3HeapAllocZ(pVM, MM_TAG_DBGF_STACK, sizeof(*pCur));
250 if (!pCur)
251 return VERR_NO_MEMORY;
252
253 /* copy input frame */
254 pCur->AddrFrame = pFrame->AddrFrame;
255 pCur->AddrStack = pFrame->AddrStack;
256 pCur->AddrPC = pFrame->AddrPC;
257 pCur->enmReturnType = pFrame->enmReturnType;
258 pCur->pNext = NULL;
259 pCur->pFirst = pCur;
260
261 int rc = VINF_SUCCESS;
262 if (!DBGFADDRESS_IS_VALID(&pCur->AddrPC))
263 rc = DBGFR3AddrFromSelOff(pVM, &pCur->AddrPC, pCtxCore->cs, pCtxCore->eip);
264 if (VBOX_SUCCESS(rc) /*&& pCur->enmReturnType == DBGFRETURNTYPE_INVALID*/)
265 {
266 switch (pCur->AddrPC.fFlags & DBGFADDRESS_FLAGS_TYPE_MASK)
267 {
268 case DBGFADDRESS_FLAGS_FAR16: pCur->enmReturnType = DBGFRETURNTYPE_NEAR16; break;
269 case DBGFADDRESS_FLAGS_FAR32: pCur->enmReturnType = DBGFRETURNTYPE_NEAR32; break;
270 case DBGFADDRESS_FLAGS_FAR64: pCur->enmReturnType = DBGFRETURNTYPE_NEAR64; break;
271 default: pCur->enmReturnType = DBGFRETURNTYPE_NEAR32; break; /// @todo 64-bit guests
272 }
273 }
274 uint64_t u64Mask = UINT64_MAX;
275 if (VBOX_SUCCESS(rc) && DBGFADDRESS_IS_FAR16(&pCur->AddrPC) && fGuest)
276 u64Mask = UINT16_MAX;
277 if (VBOX_SUCCESS(rc) && !DBGFADDRESS_IS_VALID(&pCur->AddrStack))
278 rc = DBGFR3AddrFromSelOff(pVM, &pCur->AddrStack, pCtxCore->ss, pCtxCore->esp & u64Mask);
279 if (VBOX_SUCCESS(rc) && !DBGFADDRESS_IS_VALID(&pCur->AddrFrame))
280 rc = DBGFR3AddrFromSelOff(pVM, &pCur->AddrFrame, pCtxCore->ss, pCtxCore->ebp & u64Mask);
281
282 /*
283 * The first frame.
284 */
285 if (VBOX_SUCCESS(rc))
286 rc = dbgfR3StackWalk(pVM, pCur);
287 if (VBOX_FAILURE(rc))
288 {
289 DBGFR3StackWalkEnd(pVM, pCur);
290 return rc;
291 }
292
293 /*
294 * The other frames.
295 */
296 DBGFSTACKFRAME Next = *pCur;
297 while (!(pCur->fFlags & (DBGFSTACKFRAME_FLAGS_LAST | DBGFSTACKFRAME_FLAGS_MAX_DEPTH | DBGFSTACKFRAME_FLAGS_LOOP)))
298 {
299 /* try walk. */
300 rc = dbgfR3StackWalk(pVM, &Next);
301 if (VBOX_FAILURE(rc))
302 break;
303
304 /* add the next frame to the chain. */
305 PDBGFSTACKFRAME pNext = (PDBGFSTACKFRAME)MMR3HeapAlloc(pVM, MM_TAG_DBGF_STACK, sizeof(*pNext));
306 if (!pNext)
307 {
308 DBGFR3StackWalkEnd(pVM, pCur);
309 return VERR_NO_MEMORY;
310 }
311 *pNext = Next;
312 pCur = pCur->pNext = pNext;
313 Assert(pCur->pNext == NULL);
314
315 /* check for loop */
316 for (PDBGFSTACKFRAME pLoop = pCur->pFirst; pLoop && pLoop != pCur; pLoop = pLoop->pNext)
317 if (pLoop->AddrFrame.FlatPtr == pCur->AddrFrame.FlatPtr)
318 {
319 pCur->fFlags |= DBGFSTACKFRAME_FLAGS_LOOP;
320 break;
321 }
322
323 /* check for insane recursion */
324 if (pCur->iFrame >= 2048)
325 pCur->fFlags |= DBGFSTACKFRAME_FLAGS_MAX_DEPTH;
326 }
327
328 *pFrame = *pCur->pFirst;
329 return rc;
330}
331
332
333/**
334 * Begins a stack walk.
335 * This will construct and obtain the first frame.
336 *
337 * @returns VINF_SUCCESS on success.
338 * @returns VERR_NO_MEMORY if we're out of memory.
339 * @param pVM The VM handle.
340 * @param pFrame The stack frame info structure.
341 * On input this structure must be memset to zero.
342 * If wanted, the AddrPC, AddrStack and AddrFrame fields may be set
343 * to valid addresses after memsetting it. Any of those fields not set
344 * will be fetched from the guest CPU state.
345 * On output the structure will contain all the information we were able to
346 * obtain about the stack frame.
347 */
348DBGFR3DECL(int) DBGFR3StackWalkBeginGuest(PVM pVM, PDBGFSTACKFRAME pFrame)
349{
350 pFrame->pFirst = NULL;
351 pFrame->pNext = NULL;
352
353 PVMREQ pReq;
354 int rc = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)dbgfR3StackWalkCtxFull, 4,
355 pVM, pFrame, CPUMGetGuestCtxCore(pVM), true);
356 if (VBOX_SUCCESS(rc))
357 rc = pReq->iStatus;
358 VMR3ReqFree(pReq);
359
360 return rc;
361}
362
363
364/**
365 * Begins a stack walk.
366 * This will construct and obtain the first frame.
367 *
368 * @returns VINF_SUCCESS on success.
369 * @returns VERR_NO_MEMORY if we're out of memory.
370 * @param pVM The VM handle.
371 * @param pFrame The stack frame info structure.
372 * On input this structure must be memset to zero.
373 * If wanted, the AddrPC, AddrStack and AddrFrame fields may be set
374 * to valid addresses after memsetting it. Any of those fields not set
375 * will be fetched from the hypervisor CPU state.
376 * On output the structure will contain all the information we were able to
377 * obtain about the stack frame.
378 */
379DBGFR3DECL(int) DBGFR3StackWalkBeginHyper(PVM pVM, PDBGFSTACKFRAME pFrame)
380{
381 pFrame->pFirst = NULL;
382 pFrame->pNext = NULL;
383
384 PVMREQ pReq;
385 int rc = VMR3ReqCall(pVM, &pReq, RT_INDEFINITE_WAIT, (PFNRT)dbgfR3StackWalkCtxFull, 4,
386 pVM, pFrame, CPUMGetHyperCtxCore(pVM), 4);
387 if (VBOX_SUCCESS(rc))
388 rc = pReq->iStatus;
389 VMR3ReqFree(pReq);
390
391 return rc;
392}
393
394
395/**
396 * Gets the next stack frame.
397 *
398 * @returns VINF_SUCCESS
399 * @returns VERR_NO_MORE_FILES if not more stack frames.
400 * @param pVM The VM handle.
401 * @param pFrame Pointer to the current frame on input, content is replaced with the next frame on successful return.
402 */
403DBGFR3DECL(int) DBGFR3StackWalkNext(PVM pVM, PDBGFSTACKFRAME pFrame)
404{
405 if (pFrame->pNext)
406 {
407 *pFrame = *pFrame->pNext;
408 return VINF_SUCCESS;
409 }
410 return VERR_NO_MORE_FILES;
411}
412
413
414/**
415 * Ends a stack walk process.
416 *
417 * This *must* be called after a successful first call to any of the stack
418 * walker functions. If not called we will leak memory or other resources.
419 *
420 * @param pVM The VM handle.
421 * @param pFrame The stackframe as returned by the last stack walk call.
422 */
423DBGFR3DECL(void) DBGFR3StackWalkEnd(PVM pVM, PDBGFSTACKFRAME pFrame)
424{
425 if (!pFrame || !pFrame->pFirst)
426 return;
427
428 pFrame = pFrame->pFirst;
429 while (pFrame)
430 {
431 PDBGFSTACKFRAME pCur = pFrame;
432 pFrame = pCur->pNext;
433 if (pFrame)
434 {
435 if (pCur->pSymReturnPC == pFrame->pSymPC)
436 pFrame->pSymPC = NULL;
437 if (pCur->pSymReturnPC == pFrame->pSymReturnPC)
438 pFrame->pSymReturnPC = NULL;
439
440 if (pCur->pSymPC == pFrame->pSymPC)
441 pFrame->pSymPC = NULL;
442 if (pCur->pSymPC == pFrame->pSymReturnPC)
443 pFrame->pSymReturnPC = NULL;
444
445 if (pCur->pLineReturnPC == pFrame->pLinePC)
446 pFrame->pLinePC = NULL;
447 if (pCur->pLineReturnPC == pFrame->pLineReturnPC)
448 pFrame->pLineReturnPC = NULL;
449
450 if (pCur->pLinePC == pFrame->pLinePC)
451 pFrame->pLinePC = NULL;
452 if (pCur->pLinePC == pFrame->pLineReturnPC)
453 pFrame->pLineReturnPC = NULL;
454 }
455
456 DBGFR3SymbolFree(pCur->pSymPC);
457 DBGFR3SymbolFree(pCur->pSymReturnPC);
458 DBGFR3LineFree(pCur->pLinePC);
459 DBGFR3LineFree(pCur->pLineReturnPC);
460
461 pCur->pNext = NULL;
462 pCur->pFirst = NULL;
463 pCur->fFlags = 0;
464 MMR3HeapFree(pCur);
465 }
466}
467
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