VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/dbg/dbgstackdumpself.cpp@ 73763

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

IPRT/Dbg: Added simple stack formatter for the current thread. [bugfix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 KB
Line 
1/* $Id: dbgstackdumpself.cpp 73763 2018-08-19 13:49:00Z vboxsync $ */
2/** @file
3 * IPRT - Dump current thread stack to buffer.
4 */
5
6/*
7 * Copyright (C) 2006-2018 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/dbg.h>
33
34#include <iprt/ldr.h>
35#include <iprt/list.h>
36#include <iprt/mem.h>
37#include <iprt/path.h>
38#include <iprt/string.h>
39
40#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
41# include <iprt/x86.h>
42#else
43# error "PORTME"
44#endif
45
46#ifdef RT_OS_WINDOWS
47# include <iprt/param.h>
48# include <iprt/win/windows.h>
49#endif
50
51
52/*********************************************************************************************************************************
53* Structures and Typedefs *
54*********************************************************************************************************************************/
55/**
56 * Cached module.
57 */
58typedef struct RTDBGSTACKSELFMOD
59{
60 /** List entry. */
61 RTLISTNODE ListEntry;
62 /** The mapping address. */
63 uintptr_t uMapping;
64 /** The size of the mapping. */
65 size_t cbMapping;
66 /** The loader module handle. */
67 RTLDRMOD hLdrMod;
68 /** The debug module handle, if available. */
69 RTDBGMOD hDbgMod;
70 /** Offset into szFilename of the name part. */
71 size_t offName;
72 /** the module filename. */
73 char szFilename[RTPATH_MAX];
74} RTDBGSTACKSELFMOD;
75/** Pointer to a cached module. */
76typedef RTDBGSTACKSELFMOD *PRTDBGSTACKSELFMOD;
77
78
79/**
80 * Symbol search state.
81 */
82typedef struct RTDBGSTACKSELFSYMSEARCH
83{
84 /** The address (not RVA) we're searching for a symbol for. */
85 uintptr_t uSearch;
86 /** The distance of the current hit. This is ~(uintptr_t)0 if no hit. */
87 uintptr_t offBestDist;
88 /** Where to return symbol information. */
89 PRTDBGSYMBOL pSymInfo;
90} RTDBGSTACKSELFSYMSEARCH;
91typedef RTDBGSTACKSELFSYMSEARCH *PRTDBGSTACKSELFSYMSEARCH;
92
93
94
95/**
96 * Worker for stack and module reader callback.
97 *
98 * @returns IPRT status code
99 * @param pvDst Where to put the request memory.
100 * @param cbToRead How much to read.
101 * @param uSrcAddr Where to read the memory from.
102 */
103static int rtDbgStackDumpSelfSafeMemoryReader(void *pvDst, size_t cbToRead, uintptr_t uSrcAddr)
104{
105# ifdef RT_OS_WINDOWS
106# if 1
107 __try
108 {
109 memcpy(pvDst, (void const *)uSrcAddr, cbToRead);
110 }
111 __except(EXCEPTION_EXECUTE_HANDLER)
112 {
113 return VERR_ACCESS_DENIED;
114 }
115# else
116 SIZE_T cbActual = 0;
117 if (ReadProcessMemory(GetCurrentProcess(), (void const *)uSrcAddr, pvDst, cbToRead, &cbActual))
118 {
119 if (cbActual >= cbToRead)
120 return VINF_SUCCESS;
121 memset((uint8_t *)pvDst + cbActual, 0, cbToRead - cbActual);
122 return VINF_SUCCESS;
123 }
124# endif
125# else
126 /** @todo secure this from SIGSEGV. */
127 memcpy(pvDst, (void const *)uSrcAddr, cbToRead);
128# endif
129 return VINF_SUCCESS;
130}
131
132
133#if defined(RT_OS_WINDOWS) && 0
134/**
135 * @callback_method_impl{FNRTLDRRDRMEMREAD}
136 */
137static DECLCALLBACK(int) rtDbgStackDumpSelfModReader(void *pvBuf, size_t cb, size_t off, void *pvUser)
138{
139 PRTDBGSTACKSELFMOD pMod = (PRTDBGSTACKSELFMOD)pvUser;
140 return rtDbgStackDumpSelfSafeMemoryReader(pvBuf, cb, pMod->uMapping + off);
141}
142#endif
143
144
145/**
146 * @interface_callback_impl{RTDBGUNWINDSTATE,pfnReadStack}
147 */
148static DECLCALLBACK(int) rtDbgStackDumpSelfReader(PRTDBGUNWINDSTATE pThis, RTUINTPTR uSp, size_t cbToRead, void *pvDst)
149{
150 RT_NOREF(pThis);
151 return rtDbgStackDumpSelfSafeMemoryReader(pvDst, cbToRead, uSp);
152}
153
154#ifdef RT_OS_WINDOWS
155/**
156 * Figure the size of a loaded PE image.
157 * @returns The size.
158 * @param uBase The image base address.
159 */
160static size_t rtDbgStackDumpSelfGetPeImageSize(uintptr_t uBase)
161{
162 union
163 {
164 IMAGE_DOS_HEADER DosHdr;
165 IMAGE_NT_HEADERS NtHdrs;
166 } uBuf;
167 int rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.DosHdr), uBase);
168 if (RT_SUCCESS(rc))
169 {
170 if ( uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE
171 && uBuf.DosHdr.e_lfanew < _2M)
172 {
173 rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.NtHdrs), uBase + uBuf.DosHdr.e_lfanew);
174 if (RT_SUCCESS(rc))
175 {
176 if (uBuf.NtHdrs.Signature == IMAGE_NT_SIGNATURE)
177 return uBuf.NtHdrs.OptionalHeader.SizeOfImage;
178 }
179 }
180 }
181 return _64K;
182}
183#endif
184
185
186/**
187 * Works the module cache.
188 *
189 * @returns Pointer to module cache entry on success, NULL otherwise.
190 * @param uPc The PC to locate a module for.
191 * @param pCachedModules The module cache.
192 */
193static PRTDBGSTACKSELFMOD rtDbgStackDumpSelfQueryModForPC(uintptr_t uPc, PRTLISTANCHOR pCachedModules)
194{
195 /*
196 * Search the list first.
197 */
198 PRTDBGSTACKSELFMOD pMod;
199 RTListForEach(pCachedModules, pMod, RTDBGSTACKSELFMOD, ListEntry)
200 {
201 if (uPc - pMod->uMapping < pMod->cbMapping)
202 return pMod;
203 }
204
205 /*
206 * Try figure out the module using the native loader or similar.
207 */
208#ifdef RT_OS_WINDOWS
209 HMODULE hmod = NULL;
210 static bool volatile s_fResolvedSymbols = false;
211 static decltype(GetModuleHandleExW) *g_pfnGetModuleHandleExW = NULL;
212 decltype(GetModuleHandleExW) *pfnGetModuleHandleExW;
213 if (s_fResolvedSymbols)
214 pfnGetModuleHandleExW = g_pfnGetModuleHandleExW;
215 else
216 {
217 pfnGetModuleHandleExW = (decltype(GetModuleHandleExW) *)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
218 "GetModuleHandleExW");
219 g_pfnGetModuleHandleExW = pfnGetModuleHandleExW;
220 s_fResolvedSymbols = true;
221 }
222 if ( pfnGetModuleHandleExW
223 && pfnGetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
224 (LPCWSTR)uPc, &hmod))
225 {
226 WCHAR wszFilename[RTPATH_MAX];
227 DWORD cwcFilename = GetModuleFileNameW(hmod, wszFilename, RT_ELEMENTS(wszFilename));
228 if (cwcFilename > 0)
229 {
230 pMod = (PRTDBGSTACKSELFMOD)RTMemAllocZ(sizeof(*pMod));
231 if (pMod)
232 {
233 char *pszDst = pMod->szFilename;
234 int rc = RTUtf16ToUtf8Ex(wszFilename, cwcFilename, &pszDst, sizeof(pMod->szFilename), NULL);
235 if (RT_SUCCESS(rc))
236 {
237 const char *pszFilename = RTPathFilename(pMod->szFilename);
238 pMod->offName = pszFilename ? pszFilename - &pMod->szFilename[0] : 0;
239 pMod->uMapping = (uintptr_t)hmod & ~(uintptr_t)(PAGE_SIZE - 1);
240 pMod->cbMapping = rtDbgStackDumpSelfGetPeImageSize(pMod->uMapping);
241 pMod->hLdrMod = NIL_RTLDRMOD;
242 pMod->hDbgMod = NIL_RTDBGMOD;
243
244# if 0 /* this ain't reliable, trouble enumerate symbols in VBoxRT. But why bother when we can load it off the disk. */
245 rc = RTLdrOpenInMemory(&pMod->szFilename[pMod->offName], RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(),
246 pMod->cbMapping, rtDbgStackDumpSelfModReader, NULL /*pfnDtor*/, pMod /*pvUser*/,
247 &pMod->hLdrMod, NULL /*pErrInfo*/);
248# else
249 rc = RTLdrOpen(pMod->szFilename, RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), &pMod->hLdrMod);
250# endif
251 if (RT_SUCCESS(rc))
252 {
253 pMod->cbMapping = RTLdrSize(pMod->hLdrMod);
254
255 /* Try open debug info for the module. */
256 uint32_t uTimeDateStamp = 0;
257 RTLdrQueryProp(pMod->hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimeDateStamp, sizeof(uTimeDateStamp));
258 rc = RTDbgModCreateFromPeImage(&pMod->hDbgMod, pMod->szFilename, &pMod->szFilename[pMod->offName],
259 &pMod->hLdrMod, (uint32_t)pMod->cbMapping, uTimeDateStamp, NIL_RTDBGCFG);
260 RTListPrepend(pCachedModules, &pMod->ListEntry);
261 return pMod;
262 }
263 }
264 RTMemFree(pMod);
265 }
266 }
267 }
268#endif
269 return NULL;
270}
271
272
273/**
274 * @callback_method_impl{FNRTLDRENUMSYMS}
275 */
276static DECLCALLBACK(int) rtDbgStackdumpSelfSymbolSearchCallback(RTLDRMOD hLdrMod, const char *pszSymbol,
277 unsigned uSymbol, RTLDRADDR Value, void *pvUser)
278{
279 PRTDBGSTACKSELFSYMSEARCH pSearch = (PRTDBGSTACKSELFSYMSEARCH)pvUser;
280 if (Value >= pSearch->uSearch)
281 {
282 uintptr_t const offDist = (uintptr_t)Value - pSearch->uSearch;
283 if (offDist < pSearch->offBestDist)
284 {
285 pSearch->offBestDist = offDist;
286
287 PRTDBGSYMBOL pSymInfo = pSearch->pSymInfo;
288 pSymInfo->Value = Value;
289 pSymInfo->offSeg = Value;
290 pSymInfo->iSeg = RTDBGSEGIDX_ABS;
291 pSymInfo->iOrdinal = uSymbol;
292 pSymInfo->fFlags = 0;
293 if (pszSymbol)
294 RTStrCopy(pSymInfo->szName, sizeof(pSymInfo->szName), pszSymbol);
295 else
296 RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "Ordinal#%u", uSymbol);
297
298 if (offDist < 8)
299 return VINF_CALLBACK_RETURN;
300 }
301 }
302 RT_NOREF(hLdrMod);
303 return VINF_SUCCESS;
304}
305
306
307/**
308 * Searches for a symbol close to @a uRva.
309 *
310 * @returns true if found, false if not.
311 * @param pMod The module cache entry to search in.
312 * @param uRva The RVA to locate a symbol for.
313 * @param poffDisp Where to return the distance between uRva and the returned symbol.
314 * @param pSymInfo Where to return the symbol information.
315 */
316static bool rtDbgStackDumpSelfQuerySymbol(PRTDBGSTACKSELFMOD pMod, uintptr_t uRva, PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
317{
318 if (pMod->hDbgMod != NIL_RTDBGMOD)
319 {
320 int rc = RTDbgModSymbolByAddr(pMod->hDbgMod, RTDBGSEGIDX_RVA, uRva, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL,
321 poffDisp, pSymInfo);
322 if (RT_SUCCESS(rc))
323 return true;
324 }
325
326 if (pMod->hLdrMod != NIL_RTLDRMOD)
327 {
328 RTDBGSTACKSELFSYMSEARCH SearchInfo = { pMod->uMapping + uRva, ~(uintptr_t)0, pSymInfo };
329 int rc = RTLdrEnumSymbols(pMod->hLdrMod, 0, (const void *)pMod->uMapping, pMod->uMapping,
330 rtDbgStackdumpSelfSymbolSearchCallback, &SearchInfo);
331 if (RT_SUCCESS(rc) && SearchInfo.offBestDist != ~(intptr_t)0)
332 {
333 *poffDisp = SearchInfo.offBestDist;
334 return true;
335 }
336 }
337
338 return false;
339}
340
341
342/**
343 * Does the grunt work for RTDbgStackDumpSelf.
344 *
345 * Called thru an assembly wrapper that collects the necessary register state.
346 *
347 * @returns Length of the string returned in pszStack.
348 * @param pszStack Where to output the stack dump.
349 * @param cbStack The size of the @a pszStack buffer.
350 * @param fFlags Flags, MBZ.
351 * @param pauRegs Register state. For AMD64 and x86 this starts with the
352 * PC and us followed by the general purpose registers.
353 */
354DECLASM(DECLHIDDEN(size_t)) rtDbgStackDumpSelfWorker(char *pszStack, size_t cbStack, uint32_t fFlags, PCRTCCUINTREG pauRegs)
355{
356 RT_NOREF(fFlags);
357
358 /*
359 * Initialize the unwind state.
360 */
361 RTDBGUNWINDSTATE UnwindState;
362 RT_ZERO(UnwindState);
363
364 UnwindState.u32Magic = RTDBGUNWINDSTATE_MAGIC;
365 UnwindState.pfnReadStack = rtDbgStackDumpSelfReader;
366#ifdef RT_ARCH_AMD64
367 UnwindState.enmArch = RTLDRARCH_AMD64;
368 UnwindState.uPc = pauRegs[0];
369 UnwindState.enmRetType = RTDBGRETURNTYPE_NEAR64;
370 for (unsigned i = 0; i < 16; i++)
371 UnwindState.u.x86.auRegs[i] = pauRegs[i + 1];
372#elif defined(RT_ARCH_X86)
373 UnwindState.enmArch = RTLDRARCH_X86_32;
374 UnwindState.uPc = pauRegs[0];
375 UnwindState.enmRetType = RTDBGRETURNTYPE_NEAR32;
376 for (unsigned i = 0; i < 8; i++)
377 UnwindState.u.x86.auRegs[i] = pauRegs[i + 1];
378#else
379# error "PORTME"
380#endif
381
382 /*
383 * We cache modules.
384 */
385 PRTDBGSTACKSELFMOD pCurModule = NULL;
386 RTLISTANCHOR CachedModules;
387 RTListInit(&CachedModules);
388
389 /*
390 * Work the stack.
391 */
392 size_t offDst = 0;
393 while (offDst + 64 < cbStack)
394 {
395 /* Try locate the module containing the current PC. */
396 if ( !pCurModule
397 || UnwindState.uPc - pCurModule->uMapping >= pCurModule->cbMapping)
398 pCurModule = rtDbgStackDumpSelfQueryModForPC(UnwindState.uPc, &CachedModules);
399 bool fManualUnwind = true;
400 if (!pCurModule)
401 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p\n", UnwindState.uPc);
402 else
403 {
404 uintptr_t const uRva = UnwindState.uPc - pCurModule->uMapping;
405
406 /*
407 * Add a call stack entry with the symbol if we can.
408 */
409 union
410 {
411 RTDBGSYMBOL SymbolInfo;
412 RTDBGLINE LineInfo;
413 } uBuf;
414 RTINTPTR offDisp = 0;
415 if (!rtDbgStackDumpSelfQuerySymbol(pCurModule, uRva, &offDisp, &uBuf.SymbolInfo))
416 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s + %#zx\n",
417 UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], (size_t)uRva);
418 else if (offDisp == 0)
419 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s (rva:%#zx)\n", UnwindState.uPc,
420 &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName, (size_t)uRva);
421 else
422 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s%c%#zx (rva:%#zx)\n",
423 UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName,
424 offDisp >= 0 ? '+' : '-', (size_t)RT_ABS(offDisp), (size_t)uRva);
425
426 /*
427 * Try supply the line number.
428 */
429 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
430 {
431 offDisp = 0;
432 int rc = RTDbgModLineByAddr(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &offDisp, &uBuf.LineInfo);
433 if (RT_SUCCESS(rc) && offDisp)
434 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, " [%s:%u]\n",
435 uBuf.LineInfo.szFilename, uBuf.LineInfo.uLineNo);
436 else if (RT_SUCCESS(rc))
437 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, " [%s:%u (%c%#zx)]\n", uBuf.LineInfo.szFilename,
438 uBuf.LineInfo.uLineNo, offDisp >= 0 ? '+' : '-', (size_t)RT_ABS(offDisp));
439 }
440
441 /*
442 * Try unwind using the module info.
443 */
444 int rc;
445 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
446 rc = RTDbgModUnwindFrame(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &UnwindState);
447 else
448 rc = RTLdrUnwindFrame(pCurModule->hLdrMod, (void const *)pCurModule->uMapping, UINT32_MAX, uRva, &UnwindState);
449 if (RT_SUCCESS(rc))
450 fManualUnwind = false;
451 }
452 if (fManualUnwind)
453 {
454 break;
455 }
456 }
457
458 /*
459 * Destroy the cache.
460 */
461 PRTDBGSTACKSELFMOD pNextModule;
462 RTListForEachSafe(&CachedModules, pCurModule, pNextModule, RTDBGSTACKSELFMOD, ListEntry)
463 {
464 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
465 {
466 RTDbgModRelease(pCurModule->hDbgMod);
467 pCurModule->hDbgMod = NIL_RTDBGMOD;
468 }
469 if (pCurModule->hLdrMod != NIL_RTLDRMOD)
470 {
471 RTLdrClose(pCurModule->hLdrMod);
472 pCurModule->hLdrMod = NIL_RTLDRMOD;
473 }
474 RTMemFree(pCurModule);
475 }
476
477 return offDst;
478}
479
Note: See TracBrowser for help on using the repository browser.

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