VirtualBox

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

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

*: Preparing for iprt/string.h, iprt/json.h and iprt/serialport.h no longer including iprt/err.h and string.h no longer including latin1.h (it needs err.h). bugref:9344

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