VirtualBox

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

Last change on this file since 93138 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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