VirtualBox

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

Last change on this file since 73761 was 73761, checked in by vboxsync, 6 years ago

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

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 KB
Line 
1/* $Id: dbgstackdumpself.cpp 73761 2018-08-19 13:42:25Z 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# error "PORT ME!"
127# endif
128 return VINF_SUCCESS;
129}
130
131
132#if defined(RT_OS_WINDOWS) && 0
133/**
134 * @callback_method_impl{FNRTLDRRDRMEMREAD}
135 */
136static DECLCALLBACK(int) rtDbgStackDumpSelfModReader(void *pvBuf, size_t cb, size_t off, void *pvUser)
137{
138 PRTDBGSTACKSELFMOD pMod = (PRTDBGSTACKSELFMOD)pvUser;
139 return rtDbgStackDumpSelfSafeMemoryReader(pvBuf, cb, pMod->uMapping + off);
140}
141#endif
142
143
144/**
145 * @interface_callback_impl{RTDBGUNWINDSTATE,pfnReadStack}
146 */
147static DECLCALLBACK(int) rtDbgStackDumpSelfReader(PRTDBGUNWINDSTATE pThis, RTUINTPTR uSp, size_t cbToRead, void *pvDst)
148{
149 RT_NOREF(pThis);
150 return rtDbgStackDumpSelfSafeMemoryReader(pvDst, cbToRead, uSp);
151}
152
153#ifdef RT_OS_WINDOWS
154/**
155 * Figure the size of a loaded PE image.
156 * @returns The size.
157 * @param uBase The image base address.
158 */
159static size_t rtDbgStackDumpSelfGetPeImageSize(uintptr_t uBase)
160{
161 union
162 {
163 IMAGE_DOS_HEADER DosHdr;
164 IMAGE_NT_HEADERS NtHdrs;
165 } uBuf;
166 int rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.DosHdr), uBase);
167 if (RT_SUCCESS(rc))
168 {
169 if ( uBuf.DosHdr.e_magic == IMAGE_DOS_SIGNATURE
170 && uBuf.DosHdr.e_lfanew < _2M)
171 {
172 rc = rtDbgStackDumpSelfSafeMemoryReader(&uBuf, sizeof(uBuf.NtHdrs), uBase + uBuf.DosHdr.e_lfanew);
173 if (RT_SUCCESS(rc))
174 {
175 if (uBuf.NtHdrs.Signature == IMAGE_NT_SIGNATURE)
176 return uBuf.NtHdrs.OptionalHeader.SizeOfImage;
177 }
178 }
179 }
180 return _64K;
181}
182#endif
183
184
185/**
186 * Works the module cache.
187 *
188 * @returns Pointer to module cache entry on success, NULL otherwise.
189 * @param uPc The PC to locate a module for.
190 * @param pCachedModules The module cache.
191 */
192static PRTDBGSTACKSELFMOD rtDbgStackDumpSelfQueryModForPC(uintptr_t uPc, PRTLISTANCHOR pCachedModules)
193{
194 /*
195 * Search the list first.
196 */
197 PRTDBGSTACKSELFMOD pMod;
198 RTListForEach(pCachedModules, pMod, RTDBGSTACKSELFMOD, ListEntry)
199 {
200 if (uPc - pMod->uMapping < pMod->cbMapping)
201 return pMod;
202 }
203
204 /*
205 * Try figure out the module using the native loader or similar.
206 */
207#ifdef RT_OS_WINDOWS
208 HMODULE hmod = NULL;
209 static bool volatile s_fResolvedSymbols = false;
210 static decltype(GetModuleHandleExW) *g_pfnGetModuleHandleExW = NULL;
211 decltype(GetModuleHandleExW) *pfnGetModuleHandleExW;
212 if (s_fResolvedSymbols)
213 pfnGetModuleHandleExW = g_pfnGetModuleHandleExW;
214 else
215 {
216 pfnGetModuleHandleExW = (decltype(GetModuleHandleExW) *)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
217 "GetModuleHandleExW");
218 g_pfnGetModuleHandleExW = pfnGetModuleHandleExW;
219 s_fResolvedSymbols = true;
220 }
221 if ( pfnGetModuleHandleExW
222 && pfnGetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
223 (LPCWSTR)uPc, &hmod))
224 {
225 WCHAR wszFilename[RTPATH_MAX];
226 DWORD cwcFilename = GetModuleFileNameW(hmod, wszFilename, RT_ELEMENTS(wszFilename));
227 if (cwcFilename > 0)
228 {
229 pMod = (PRTDBGSTACKSELFMOD)RTMemAllocZ(sizeof(*pMod));
230 if (pMod)
231 {
232 char *pszDst = pMod->szFilename;
233 int rc = RTUtf16ToUtf8Ex(wszFilename, cwcFilename, &pszDst, sizeof(pMod->szFilename), NULL);
234 if (RT_SUCCESS(rc))
235 {
236 const char *pszFilename = RTPathFilename(pMod->szFilename);
237 pMod->offName = pszFilename ? pszFilename - &pMod->szFilename[0] : 0;
238 pMod->uMapping = (uintptr_t)hmod & ~(uintptr_t)(PAGE_SIZE - 1);
239 pMod->cbMapping = rtDbgStackDumpSelfGetPeImageSize(pMod->uMapping);
240 pMod->hLdrMod = NIL_RTLDRMOD;
241 pMod->hDbgMod = NIL_RTDBGMOD;
242
243# if 0 /* this ain't reliable, trouble enumerate symbols in VBoxRT. But why bother when we can load it off the disk. */
244 rc = RTLdrOpenInMemory(&pMod->szFilename[pMod->offName], RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(),
245 pMod->cbMapping, rtDbgStackDumpSelfModReader, NULL /*pfnDtor*/, pMod /*pvUser*/,
246 &pMod->hLdrMod, NULL /*pErrInfo*/);
247# else
248 rc = RTLdrOpen(pMod->szFilename, RTLDR_O_FOR_DEBUG, RTLdrGetHostArch(), &pMod->hLdrMod);
249# endif
250 if (RT_SUCCESS(rc))
251 {
252 pMod->cbMapping = RTLdrSize(pMod->hLdrMod);
253
254 /* Try open debug info for the module. */
255 uint32_t uTimeDateStamp = 0;
256 RTLdrQueryProp(pMod->hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uTimeDateStamp, sizeof(uTimeDateStamp));
257 rc = RTDbgModCreateFromPeImage(&pMod->hDbgMod, pMod->szFilename, &pMod->szFilename[pMod->offName],
258 &pMod->hLdrMod, (uint32_t)pMod->cbMapping, uTimeDateStamp, NIL_RTDBGCFG);
259 RTListPrepend(pCachedModules, &pMod->ListEntry);
260 return pMod;
261 }
262 }
263 RTMemFree(pMod);
264 }
265 }
266 }
267#endif
268 return NULL;
269}
270
271
272/**
273 * @callback_method_impl{FNRTLDRENUMSYMS}
274 */
275static DECLCALLBACK(int) rtDbgStackdumpSelfSymbolSearchCallback(RTLDRMOD hLdrMod, const char *pszSymbol,
276 unsigned uSymbol, RTLDRADDR Value, void *pvUser)
277{
278 PRTDBGSTACKSELFSYMSEARCH pSearch = (PRTDBGSTACKSELFSYMSEARCH)pvUser;
279 if (Value >= pSearch->uSearch)
280 {
281 uintptr_t const offDist = (uintptr_t)Value - pSearch->uSearch;
282 if (offDist < pSearch->offBestDist)
283 {
284 pSearch->offBestDist = offDist;
285
286 PRTDBGSYMBOL pSymInfo = pSearch->pSymInfo;
287 pSymInfo->Value = Value;
288 pSymInfo->offSeg = Value;
289 pSymInfo->iSeg = RTDBGSEGIDX_ABS;
290 pSymInfo->iOrdinal = uSymbol;
291 pSymInfo->fFlags = 0;
292 if (pszSymbol)
293 RTStrCopy(pSymInfo->szName, sizeof(pSymInfo->szName), pszSymbol);
294 else
295 RTStrPrintf(pSymInfo->szName, sizeof(pSymInfo->szName), "Ordinal#%u", uSymbol);
296
297 if (offDist < 8)
298 return VINF_CALLBACK_RETURN;
299 }
300 }
301 RT_NOREF(hLdrMod);
302 return VINF_SUCCESS;
303}
304
305
306/**
307 * Searches for a symbol close to @a uRva.
308 *
309 * @returns true if found, false if not.
310 * @param pMod The module cache entry to search in.
311 * @param uRva The RVA to locate a symbol for.
312 * @param poffDisp Where to return the distance between uRva and the returned symbol.
313 * @param pSymInfo Where to return the symbol information.
314 */
315static bool rtDbgStackDumpSelfQuerySymbol(PRTDBGSTACKSELFMOD pMod, uintptr_t uRva, PRTINTPTR poffDisp, PRTDBGSYMBOL pSymInfo)
316{
317 if (pMod->hDbgMod != NIL_RTDBGMOD)
318 {
319 int rc = RTDbgModSymbolByAddr(pMod->hDbgMod, RTDBGSEGIDX_RVA, uRva, RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL,
320 poffDisp, pSymInfo);
321 if (RT_SUCCESS(rc))
322 return true;
323 }
324
325 if (pMod->hLdrMod != NIL_RTLDRMOD)
326 {
327 RTDBGSTACKSELFSYMSEARCH SearchInfo = { pMod->uMapping + uRva, ~(uintptr_t)0, pSymInfo };
328 int rc = RTLdrEnumSymbols(pMod->hLdrMod, 0, (const void *)pMod->uMapping, pMod->uMapping,
329 rtDbgStackdumpSelfSymbolSearchCallback, &SearchInfo);
330 if (RT_SUCCESS(rc) && SearchInfo.offBestDist != ~(intptr_t)0)
331 {
332 *poffDisp = SearchInfo.offBestDist;
333 return true;
334 }
335 }
336
337 return false;
338}
339
340
341/**
342 * Does the grunt work for RTDbgStackDumpSelf.
343 *
344 * Called thru an assembly wrapper that collects the necessary register state.
345 *
346 * @returns Length of the string returned in pszStack.
347 * @param pszStack Where to output the stack dump.
348 * @param cbStack The size of the @a pszStack buffer.
349 * @param fFlags Flags, MBZ.
350 * @param pauRegs Register state. For AMD64 and x86 this starts with the
351 * PC and us followed by the general purpose registers.
352 */
353DECLASM(DECLHIDDEN(size_t)) rtDbgStackDumpSelfWorker(char *pszStack, size_t cbStack, uint32_t fFlags, PCRTCCUINTREG pauRegs)
354{
355 RT_NOREF(fFlags);
356
357 /*
358 * Initialize the unwind state.
359 */
360 RTDBGUNWINDSTATE UnwindState;
361 RT_ZERO(UnwindState);
362
363 UnwindState.u32Magic = RTDBGUNWINDSTATE_MAGIC;
364 UnwindState.pfnReadStack = rtDbgStackDumpSelfReader;
365#ifdef RT_ARCH_AMD64
366 UnwindState.enmArch = RTLDRARCH_AMD64;
367 UnwindState.uPc = pauRegs[0];
368 UnwindState.enmRetType = RTDBGRETURNTYPE_NEAR64;
369 for (unsigned i = 0; i < 16; i++)
370 UnwindState.u.x86.auRegs[i] = pauRegs[i + 1];
371#elif defined(RT_ARCH_X86)
372 UnwindState.enmArch = RTLDRARCH_X86_32;
373 UnwindState.uPc = pauRegs[0];
374 UnwindState.enmRetType = RTDBGRETURNTYPE_NEAR32;
375 for (unsigned i = 0; i < 8; i++)
376 UnwindState.u.x86.auRegs[i] = pauRegs[i + 1];
377#else
378# error "PORTME"
379#endif
380
381 /*
382 * We cache modules.
383 */
384 PRTDBGSTACKSELFMOD pCurModule = NULL;
385 RTLISTANCHOR CachedModules;
386 RTListInit(&CachedModules);
387
388 /*
389 * Work the stack.
390 */
391 size_t offDst = 0;
392 while (offDst + 64 < cbStack)
393 {
394 /* Try locate the module containing the current PC. */
395 if ( !pCurModule
396 || UnwindState.uPc - pCurModule->uMapping >= pCurModule->cbMapping)
397 pCurModule = rtDbgStackDumpSelfQueryModForPC(UnwindState.uPc, &CachedModules);
398 bool fManualUnwind = true;
399 if (!pCurModule)
400 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p\n", UnwindState.uPc);
401 else
402 {
403 uintptr_t const uRva = UnwindState.uPc - pCurModule->uMapping;
404
405 /*
406 * Add a call stack entry with the symbol if we can.
407 */
408 union
409 {
410 RTDBGSYMBOL SymbolInfo;
411 RTDBGLINE LineInfo;
412 } uBuf;
413 RTINTPTR offDisp = 0;
414 if (!rtDbgStackDumpSelfQuerySymbol(pCurModule, uRva, &offDisp, &uBuf.SymbolInfo))
415 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s + %#zx\n",
416 UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], (size_t)uRva);
417 else if (offDisp == 0)
418 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s (rva:%#zx)\n", UnwindState.uPc,
419 &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName, (size_t)uRva);
420 else
421 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, "%p %s!%s%c%#zx (rva:%#zx)\n",
422 UnwindState.uPc, &pCurModule->szFilename[pCurModule->offName], uBuf.SymbolInfo.szName,
423 offDisp >= 0 ? '+' : '-', (size_t)RT_ABS(offDisp), (size_t)uRva);
424
425 /*
426 * Try supply the line number.
427 */
428 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
429 {
430 offDisp = 0;
431 int rc = RTDbgModLineByAddr(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &offDisp, &uBuf.LineInfo);
432 if (RT_SUCCESS(rc) && offDisp)
433 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, " [%s:%u]\n",
434 uBuf.LineInfo.szFilename, uBuf.LineInfo.uLineNo);
435 else if (RT_SUCCESS(rc))
436 offDst += RTStrPrintf(&pszStack[offDst], cbStack - offDst, " [%s:%u (%c%#zx)]\n", uBuf.LineInfo.szFilename,
437 uBuf.LineInfo.uLineNo, offDisp >= 0 ? '+' : '-', (size_t)RT_ABS(offDisp));
438 }
439
440 /*
441 * Try unwind using the module info.
442 */
443 int rc;
444 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
445 rc = RTDbgModUnwindFrame(pCurModule->hDbgMod, RTDBGSEGIDX_RVA, uRva, &UnwindState);
446 else
447 rc = RTLdrUnwindFrame(pCurModule->hLdrMod, (void const *)pCurModule->uMapping, UINT32_MAX, uRva, &UnwindState);
448 if (RT_SUCCESS(rc))
449 fManualUnwind = false;
450 }
451 if (fManualUnwind)
452 {
453 break;
454 }
455 }
456
457 /*
458 * Destroy the cache.
459 */
460 PRTDBGSTACKSELFMOD pNextModule;
461 RTListForEachSafe(&CachedModules, pCurModule, pNextModule, RTDBGSTACKSELFMOD, ListEntry)
462 {
463 if (pCurModule->hDbgMod != NIL_RTDBGMOD)
464 {
465 RTDbgModRelease(pCurModule->hDbgMod);
466 pCurModule->hDbgMod = NIL_RTDBGMOD;
467 }
468 if (pCurModule->hLdrMod != NIL_RTLDRMOD)
469 {
470 RTLdrClose(pCurModule->hLdrMod);
471 pCurModule->hLdrMod = NIL_RTLDRMOD;
472 }
473 RTMemFree(pCurModule);
474 }
475
476 return offDst;
477}
478
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