VirtualBox

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

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

dbgstackdumpself: missing blank line.

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