u_debug_stack.c revision 7ec681f3
1/************************************************************************** 2 * 3 * Copyright 2009 VMware, Inc. 4 * All Rights Reserved. 5 * 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: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 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. 25 * 26 **************************************************************************/ 27 28/** 29 * @file 30 * Stack backtracing. 31 * 32 * @author Jose Fonseca <jfonseca@vmware.com> 33 */ 34 35#include "util/u_debug.h" 36#include "u_debug_symbol.h" 37#include "u_debug_stack.h" 38#include "pipe/p_config.h" 39 40#if defined(HAVE_LIBUNWIND) 41 42#ifndef _GNU_SOURCE 43#define _GNU_SOURCE 44#endif 45#include <dlfcn.h> 46 47#include "os/os_thread.h" 48#include "util/hash_table.h" 49 50static struct hash_table* symbols_hash; 51static mtx_t symbols_mutex = _MTX_INITIALIZER_NP; 52 53/* TODO with some refactoring we might be able to re-use debug_symbol_name_cached() 54 * instead.. otoh if using libunwind I think u_debug_symbol could just be excluded 55 * from build? 56 */ 57static const char * 58symbol_name_cached(unw_cursor_t *cursor, unw_proc_info_t *pip) 59{ 60 void *addr = (void *)(uintptr_t)pip->start_ip; 61 char *name; 62 63 mtx_lock(&symbols_mutex); 64 if(!symbols_hash) 65 symbols_hash = _mesa_pointer_hash_table_create(NULL); 66 struct hash_entry *entry = _mesa_hash_table_search(symbols_hash, addr); 67 if (!entry) { 68 char procname[256]; 69 unw_word_t off; 70 int ret; 71 72 ret = unw_get_proc_name(cursor, procname, sizeof(procname), &off); 73 if (ret && ret != -UNW_ENOMEM) { 74 procname[0] = '?'; 75 procname[1] = 0; 76 } 77 78 if (asprintf(&name, "%s%s", procname, ret == -UNW_ENOMEM ? "..." : "") == -1) 79 name = "??"; 80 entry = _mesa_hash_table_insert(symbols_hash, addr, (void*)name); 81 } 82 mtx_unlock(&symbols_mutex); 83 84 return entry->data; 85} 86 87void 88debug_backtrace_capture(struct debug_stack_frame *backtrace, 89 unsigned start_frame, 90 unsigned nr_frames) 91{ 92 unw_cursor_t cursor; 93 unw_context_t context; 94 unw_proc_info_t pip; 95 unsigned i = 0; 96 97 pip.unwind_info = 0; 98 99 unw_getcontext(&context); 100 unw_init_local(&cursor, &context); 101 102 while ((start_frame > 0) && (unw_step(&cursor) > 0)) 103 start_frame--; 104 105 while ((i < nr_frames) && (unw_step(&cursor) > 0)) { 106 unw_word_t ip; 107 108 unw_get_reg(&cursor, UNW_REG_IP, &ip); 109 unw_get_proc_info(&cursor, &pip); 110 111 backtrace[i].start_ip = pip.start_ip; 112 backtrace[i].off = ip - pip.start_ip; 113 backtrace[i].procname = symbol_name_cached(&cursor, &pip); 114 115 i++; 116 } 117 118 while (i < nr_frames) { 119 backtrace[i].start_ip = 0; 120 i++; 121 } 122} 123 124static const void * 125frame_ip(const struct debug_stack_frame *frame) 126{ 127 return (void *)(uintptr_t)(frame->start_ip + frame->off); 128} 129 130static const char * 131frame_info(const struct debug_stack_frame *frame, unsigned *offset) 132{ 133 Dl_info dlinfo; 134 const void *addr = frame_ip(frame); 135 136 137 if (dladdr(addr, &dlinfo) && dlinfo.dli_fname && 138 *dlinfo.dli_fname) { 139 *offset = (unsigned)((uintptr_t)addr - (uintptr_t)dlinfo.dli_fbase); 140 return dlinfo.dli_fname; 141 } 142 143 *offset = 0; 144 return "?"; 145} 146 147void 148debug_backtrace_dump(const struct debug_stack_frame *backtrace, 149 unsigned nr_frames) 150{ 151 unsigned i, offset; 152 const char *filename; 153 154 for (i = 0; i < nr_frames; ++i) { 155 if (!backtrace[i].start_ip) 156 break; 157 filename = frame_info(&backtrace[i], &offset); 158 debug_printf("\t%s(+0x%x) (%s+0x%x) [%p]\n", filename, offset, 159 backtrace[i].procname, backtrace[i].off, 160 frame_ip(&backtrace[i])); 161 } 162} 163 164void 165debug_backtrace_print(FILE *f, 166 const struct debug_stack_frame *backtrace, 167 unsigned nr_frames) 168{ 169 unsigned i, offset; 170 const char *filename; 171 172 for (i = 0; i < nr_frames; ++i) { 173 if (!backtrace[i].start_ip) 174 break; 175 filename = frame_info(&backtrace[i], &offset); 176 fprintf(f, "\t%s(+0x%x) (%s+0x%x) [%p]\n", filename, offset, 177 backtrace[i].procname, backtrace[i].off, 178 frame_ip(&backtrace[i])); 179 } 180} 181#elif defined(ANDROID) 182 /* Not implemented here; see u_debug_stack_android.cpp */ 183#else /* ! HAVE_LIBUNWIND */ 184 185#if defined(PIPE_OS_WINDOWS) 186#include <windows.h> 187#endif 188 189 190/** 191 * Capture stack backtrace. 192 * 193 * NOTE: The implementation of this function is quite big, but it is important 194 * not to break it down in smaller functions to avoid adding new frames to the 195 * calling stack. 196 */ 197void 198debug_backtrace_capture(struct debug_stack_frame *backtrace, 199 unsigned start_frame, 200 unsigned nr_frames) 201{ 202 const void **frame_pointer = NULL; 203 unsigned i = 0; 204 205 if (!nr_frames) { 206 return; 207 } 208 209 /* 210 * On Windows try obtaining the stack backtrace via CaptureStackBackTrace. 211 * 212 * It works reliably both for x86 for x86_64. 213 */ 214#if defined(PIPE_OS_WINDOWS) 215 { 216 typedef USHORT (WINAPI *PFNCAPTURESTACKBACKTRACE)(ULONG, ULONG, 217 PVOID *, PULONG); 218 static PFNCAPTURESTACKBACKTRACE pfnCaptureStackBackTrace = NULL; 219 220 if (!pfnCaptureStackBackTrace) { 221 static HMODULE hModule = NULL; 222 if (!hModule) { 223 hModule = LoadLibraryA("kernel32"); 224 assert(hModule); 225 } 226 if (hModule) { 227 pfnCaptureStackBackTrace = 228 (PFNCAPTURESTACKBACKTRACE)GetProcAddress(hModule, 229 "RtlCaptureStackBackTrace"); 230 } 231 } 232 if (pfnCaptureStackBackTrace) { 233 /* 234 * Skip this (debug_backtrace_capture) function's frame. 235 */ 236 237 start_frame += 1; 238 239 assert(start_frame + nr_frames < 63); 240 i = pfnCaptureStackBackTrace(start_frame, nr_frames, 241 (PVOID *) &backtrace->function, NULL); 242 243 /* Pad remaing requested frames with NULL */ 244 while (i < nr_frames) { 245 backtrace[i++].function = NULL; 246 } 247 248 return; 249 } 250 } 251#endif 252 253#if defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION > 404) || defined(__clang__) 254#pragma GCC diagnostic push 255#pragma GCC diagnostic ignored "-Wframe-address" 256 frame_pointer = ((const void **)__builtin_frame_address(1)); 257#pragma GCC diagnostic pop 258#elif defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86) 259 __asm { 260 mov frame_pointer, ebp 261 } 262 frame_pointer = (const void **)frame_pointer[0]; 263#else 264 frame_pointer = NULL; 265#endif 266 267#ifdef PIPE_ARCH_X86 268 while (nr_frames) { 269 const void **next_frame_pointer; 270 271 if (!frame_pointer) 272 break; 273 274 if (start_frame) 275 --start_frame; 276 else { 277 backtrace[i++].function = frame_pointer[1]; 278 --nr_frames; 279 } 280 281 next_frame_pointer = (const void **)frame_pointer[0]; 282 283 /* Limit the stack walk to avoid referencing undefined memory */ 284 if ((uintptr_t)next_frame_pointer <= (uintptr_t)frame_pointer || 285 (uintptr_t)next_frame_pointer > (uintptr_t)frame_pointer + 64*1024) 286 break; 287 288 frame_pointer = next_frame_pointer; 289 } 290#else 291 (void) frame_pointer; 292#endif 293 294 while (nr_frames) { 295 backtrace[i++].function = NULL; 296 --nr_frames; 297 } 298} 299 300 301void 302debug_backtrace_dump(const struct debug_stack_frame *backtrace, 303 unsigned nr_frames) 304{ 305 unsigned i; 306 307 for (i = 0; i < nr_frames; ++i) { 308 if (!backtrace[i].function) 309 break; 310 debug_symbol_print(backtrace[i].function); 311 } 312} 313 314 315void 316debug_backtrace_print(FILE *f, 317 const struct debug_stack_frame *backtrace, 318 unsigned nr_frames) 319{ 320 unsigned i; 321 322 for (i = 0; i < nr_frames; ++i) { 323 const char *symbol; 324 if (!backtrace[i].function) 325 break; 326 symbol = debug_symbol_name_cached(backtrace[i].function); 327 if (symbol) 328 fprintf(f, "%s\n", symbol); 329 } 330} 331 332#endif /* HAVE_LIBUNWIND */ 333