VirtualBox

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

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

Even more debugger fixes.

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