VirtualBox

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

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

IPRT: Starting to get somewhere with DWARF unwinding. bugref:3897

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