1 /**************************************************************************
3 * Copyright 2009 VMware, Inc.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 **************************************************************************/
32 * @author Jose Fonseca <jfonseca@vmware.com>
35 #include "pipe/p_compiler.h"
36 #include "os/os_thread.h"
40 #include "u_debug_symbol.h"
41 #include "u_hash_table.h"
44 #if defined(PIPE_OS_WINDOWS)
53 * SymInitialize() must be called once for each process (in this case, the
54 * current process), before any of the other functions can be called.
56 static BOOL g_bSymInitialized
= FALSE
;
60 * Lookup the address of a DbgHelp function.
63 getDbgHelpProcAddress(LPCSTR lpProcName
)
65 static HMODULE hModule
= NULL
;
68 static boolean bail
= FALSE
;
76 * DbgHelp does not understand the debug information generated by MinGW toolchain.
78 * mgwhelp.dll is a dbghelp.dll look-alike replacement, which is able to
79 * understand MinGW symbols, including on 64-bit builds.
82 hModule
= LoadLibraryA("mgwhelp.dll");
84 _debug_printf("warning: mgwhelp.dll not found: symbol names will not be resolved\n"
85 "warning: download it from http://code.google.com/p/jrfonseca/wiki/DrMingw#MgwHelp\n");
90 * bfdhelp.dll was the predecessor of mgwhelp.dll. It is available from
91 * http://people.freedesktop.org/~jrfonseca/bfdhelp/ for now.
94 hModule
= LoadLibraryA("bfdhelp.dll");
99 * Fallback to the real DbgHelp.
102 hModule
= LoadLibraryA("dbghelp.dll");
111 return GetProcAddress(hModule
, lpProcName
);
116 * Generic macro to dispatch a DbgHelp functions.
118 #define DBGHELP_DISPATCH(_name, _ret_type, _ret_default, _arg_types, _arg_names) \
119 static _ret_type WINAPI \
120 j_##_name _arg_types \
122 typedef BOOL (WINAPI *PFN) _arg_types; \
123 static PFN pfn = NULL; \
125 pfn = (PFN) getDbgHelpProcAddress(#_name); \
127 return _ret_default; \
130 return pfn _arg_names; \
133 DBGHELP_DISPATCH(SymInitialize
,
135 (HANDLE hProcess
, PSTR UserSearchPath
, BOOL fInvadeProcess
),
136 (hProcess
, UserSearchPath
, fInvadeProcess
))
138 DBGHELP_DISPATCH(SymSetOptions
,
143 DBGHELP_DISPATCH(SymFromAddr
,
145 (HANDLE hProcess
, DWORD64 Address
, PDWORD64 Displacement
, PSYMBOL_INFO Symbol
),
146 (hProcess
, Address
, Displacement
, Symbol
))
148 DBGHELP_DISPATCH(SymGetLineFromAddr64
,
150 (HANDLE hProcess
, DWORD64 dwAddr
, PDWORD pdwDisplacement
, PIMAGEHLP_LINE64 Line
),
151 (hProcess
, dwAddr
, pdwDisplacement
, Line
))
154 #undef DBGHELP_DISPATCH
157 static INLINE boolean
158 debug_symbol_name_dbghelp(const void *addr
, char* buf
, unsigned size
)
160 DWORD64 dwAddr
= (DWORD64
)(uintptr_t)addr
;
161 HANDLE hProcess
= GetCurrentProcess();
163 /* General purpose buffer, to back pSymbol and other temporary stuff.
164 * Must not be too memory hungry here to avoid stack overflows.
168 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
) buffer
;
169 DWORD64 dwDisplacement
= 0; /* Displacement of the input address, relative to the start of the symbol */
170 DWORD dwLineDisplacement
= 0;
171 IMAGEHLP_LINE64 Line
;
173 memset(pSymbol
, 0, sizeof *pSymbol
);
174 pSymbol
->SizeOfStruct
= sizeof buffer
;
175 pSymbol
->MaxNameLen
= sizeof buffer
- offsetof(SYMBOL_INFO
, Name
);
177 if (!g_bSymInitialized
) {
178 j_SymSetOptions(/* SYMOPT_UNDNAME | */ SYMOPT_LOAD_LINES
);
179 if (j_SymInitialize(hProcess
, NULL
, TRUE
)) {
180 g_bSymInitialized
= TRUE
;
184 /* Lookup symbol name */
185 if (!g_bSymInitialized
||
186 !j_SymFromAddr(hProcess
, dwAddr
, &dwDisplacement
, pSymbol
)) {
188 * We couldn't obtain symbol information. At least tell which module the address belongs.
191 HMODULE hModule
= NULL
;
193 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
199 if (GetModuleFileNameA(hModule
, buffer
, sizeof buffer
) == sizeof buffer
) {
202 util_snprintf(buf
, size
, "%p at %s+0x%lx",
204 (unsigned long)((uintptr_t)addr
- (uintptr_t)hModule
));
210 * Try to get filename and line number.
212 memset(&Line
, 0, sizeof Line
);
213 Line
.SizeOfStruct
= sizeof Line
;
214 if (!j_SymGetLineFromAddr64(hProcess
, dwAddr
, &dwLineDisplacement
, &Line
)) {
215 Line
.FileName
= NULL
;
219 util_snprintf(buf
, size
, "%s at %s:%lu", pSymbol
->Name
, Line
.FileName
, Line
.LineNumber
);
221 util_snprintf(buf
, size
, "%s", pSymbol
->Name
);
227 #endif /* PIPE_OS_WINDOWS */
230 #if defined(__GLIBC__) && !defined(__UCLIBC__)
232 #include <execinfo.h>
234 /* This can only provide dynamic symbols, or binary offsets into a file.
236 * To fix this, post-process the output with tools/addr2line.sh
238 static INLINE boolean
239 debug_symbol_name_glibc(const void *addr
, char* buf
, unsigned size
)
241 char** syms
= backtrace_symbols((void**)&addr
, 1);
245 strncpy(buf
, syms
[0], size
);
251 #endif /* defined(__GLIBC__) && !defined(__UCLIBC__) */
255 debug_symbol_name(const void *addr
, char* buf
, unsigned size
)
257 #if defined(PIPE_OS_WINDOWS)
258 if (debug_symbol_name_dbghelp(addr
, buf
, size
)) {
263 #if defined(__GLIBC__) && !defined(__UCLIBC__)
264 if (debug_symbol_name_glibc(addr
, buf
, size
)) {
269 util_snprintf(buf
, size
, "%p", addr
);
274 debug_symbol_print(const void *addr
)
277 debug_symbol_name(addr
, buf
, sizeof(buf
));
278 debug_printf("\t%s\n", buf
);
281 struct util_hash_table
* symbols_hash
;
282 pipe_static_mutex(symbols_mutex
);
284 static unsigned hash_ptr(void* p
)
286 return (unsigned)(uintptr_t)p
;
289 static int compare_ptr(void* a
, void* b
)
300 debug_symbol_name_cached(const void *addr
)
303 #ifdef PIPE_SUBSYSTEM_WINDOWS_USER
304 static boolean first
= TRUE
;
307 pipe_mutex_init(symbols_mutex
);
312 pipe_mutex_lock(symbols_mutex
);
314 symbols_hash
= util_hash_table_create(hash_ptr
, compare_ptr
);
315 name
= util_hash_table_get(symbols_hash
, (void*)addr
);
319 debug_symbol_name(addr
, buf
, sizeof(buf
));
322 util_hash_table_set(symbols_hash
, (void*)addr
, (void*)name
);
324 pipe_mutex_unlock(symbols_mutex
);