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"
37 #include "util/u_string.h"
39 #include "util/u_debug.h"
40 #include "u_debug_symbol.h"
41 #include "util/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 https://github.com/jrfonseca/drmingw/#mgwhelp\n");
91 * Fallback to the real DbgHelp.
94 hModule
= LoadLibraryA("dbghelp.dll");
103 return GetProcAddress(hModule
, lpProcName
);
108 * Generic macro to dispatch a DbgHelp functions.
110 #define DBGHELP_DISPATCH(_name, _ret_type, _ret_default, _arg_types, _arg_names) \
111 static _ret_type WINAPI \
112 j_##_name _arg_types \
114 typedef BOOL (WINAPI *PFN) _arg_types; \
115 static PFN pfn = NULL; \
117 pfn = (PFN) getDbgHelpProcAddress(#_name); \
119 return _ret_default; \
122 return pfn _arg_names; \
125 DBGHELP_DISPATCH(SymInitialize
,
127 (HANDLE hProcess
, PSTR UserSearchPath
, BOOL fInvadeProcess
),
128 (hProcess
, UserSearchPath
, fInvadeProcess
))
130 DBGHELP_DISPATCH(SymSetOptions
,
135 DBGHELP_DISPATCH(SymFromAddr
,
137 (HANDLE hProcess
, DWORD64 Address
, PDWORD64 Displacement
, PSYMBOL_INFO Symbol
),
138 (hProcess
, Address
, Displacement
, Symbol
))
140 DBGHELP_DISPATCH(SymGetLineFromAddr64
,
142 (HANDLE hProcess
, DWORD64 dwAddr
, PDWORD pdwDisplacement
, PIMAGEHLP_LINE64 Line
),
143 (hProcess
, dwAddr
, pdwDisplacement
, Line
))
146 #undef DBGHELP_DISPATCH
149 static inline boolean
150 debug_symbol_name_dbghelp(const void *addr
, char* buf
, unsigned size
)
152 DWORD64 dwAddr
= (DWORD64
)(uintptr_t)addr
;
153 HANDLE hProcess
= GetCurrentProcess();
155 /* General purpose buffer, to back pSymbol and other temporary stuff.
156 * Must not be too memory hungry here to avoid stack overflows.
160 PSYMBOL_INFO pSymbol
= (PSYMBOL_INFO
) buffer
;
161 DWORD64 dwDisplacement
= 0; /* Displacement of the input address, relative to the start of the symbol */
162 DWORD dwLineDisplacement
= 0;
163 IMAGEHLP_LINE64 Line
;
165 memset(pSymbol
, 0, sizeof *pSymbol
);
166 pSymbol
->SizeOfStruct
= sizeof buffer
;
167 pSymbol
->MaxNameLen
= sizeof buffer
- offsetof(SYMBOL_INFO
, Name
);
169 if (!g_bSymInitialized
) {
170 j_SymSetOptions(/* SYMOPT_UNDNAME | */ SYMOPT_LOAD_LINES
);
171 if (j_SymInitialize(hProcess
, NULL
, TRUE
)) {
172 g_bSymInitialized
= TRUE
;
176 /* Lookup symbol name */
177 if (!g_bSymInitialized
||
178 !j_SymFromAddr(hProcess
, dwAddr
, &dwDisplacement
, pSymbol
)) {
180 * We couldn't obtain symbol information. At least tell which module the address belongs.
183 HMODULE hModule
= NULL
;
185 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
191 if (GetModuleFileNameA(hModule
, buffer
, sizeof buffer
) == sizeof buffer
) {
194 snprintf(buf
, size
, "%p at %s+0x%lx",
196 (unsigned long)((uintptr_t)addr
- (uintptr_t)hModule
));
202 * Try to get filename and line number.
204 memset(&Line
, 0, sizeof Line
);
205 Line
.SizeOfStruct
= sizeof Line
;
206 if (!j_SymGetLineFromAddr64(hProcess
, dwAddr
, &dwLineDisplacement
, &Line
)) {
207 Line
.FileName
= NULL
;
211 snprintf(buf
, size
, "%s at %s:%lu", pSymbol
->Name
, Line
.FileName
, Line
.LineNumber
);
213 snprintf(buf
, size
, "%s", pSymbol
->Name
);
219 #endif /* PIPE_OS_WINDOWS */
222 #if defined(HAVE_EXECINFO_H)
224 #include <execinfo.h>
226 /* This can only provide dynamic symbols, or binary offsets into a file.
228 * To fix this, post-process the output with tools/addr2line.sh
230 static inline boolean
231 debug_symbol_name_glibc(const void *addr
, char* buf
, unsigned size
)
233 char** syms
= backtrace_symbols((void**)&addr
, 1);
237 strncpy(buf
, syms
[0], size
- 1);
243 #endif /* defined(HAVE_EXECINFO_H) */
247 debug_symbol_name(const void *addr
, char* buf
, unsigned size
)
249 #if defined(PIPE_OS_WINDOWS)
250 if (debug_symbol_name_dbghelp(addr
, buf
, size
)) {
255 #if defined(HAVE_EXECINFO_H)
256 if (debug_symbol_name_glibc(addr
, buf
, size
)) {
259 #endif /* defined(HAVE_EXECINFO_H) */
261 snprintf(buf
, size
, "%p", addr
);
266 debug_symbol_print(const void *addr
)
269 debug_symbol_name(addr
, buf
, sizeof(buf
));
270 debug_printf("\t%s\n", buf
);
273 static struct hash_table
* symbols_hash
;
274 #ifdef PIPE_OS_WINDOWS
275 static mtx_t symbols_mutex
;
277 static mtx_t symbols_mutex
= _MTX_INITIALIZER_NP
;
281 debug_symbol_name_cached(const void *addr
)
284 #ifdef PIPE_OS_WINDOWS
285 static boolean first
= TRUE
;
288 (void) mtx_init(&symbols_mutex
, mtx_plain
);
293 mtx_lock(&symbols_mutex
);
295 symbols_hash
= _mesa_pointer_hash_table_create(NULL
);
296 struct hash_entry
*entry
= _mesa_hash_table_search(symbols_hash
, addr
);
299 debug_symbol_name(addr
, buf
, sizeof(buf
));
302 entry
= _mesa_hash_table_insert(symbols_hash
, addr
, (void*)name
);
304 mtx_unlock(&symbols_mutex
);