VirtualBox

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

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

Config.kmk,Makefile.kmk,iprt: extpack solaris build hacking.

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