VirtualBox

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

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

IPRT/dbgstackdumpself: Another shot at the solaris linking issue. bugref:9152

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