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#include "c11/threads.h" 40 41#if defined(HAVE_LIBUNWIND) 42 43#ifndef _GNU_SOURCE 44#define _GNU_SOURCE 45#endif 46#include <dlfcn.h> 47 48#include "os/os_thread.h" 49#include "util/hash_table.h" 50 51static struct hash_table* symbols_hash; 52static mtx_t symbols_mutex = _MTX_INITIALIZER_NP; 53 54/* TODO with some refactoring we might be able to re-use debug_symbol_name_cached() 55 * instead.. otoh if using libunwind I think u_debug_symbol could just be excluded 56 * from build? 57 */ 58static const char * 59symbol_name_cached(unw_cursor_t *cursor, unw_proc_info_t *pip) 60{ 61 void *addr = (void *)(uintptr_t)pip->start_ip; 62 char *name; 63 64 mtx_lock(&symbols_mutex); 65 if(!symbols_hash) 66 symbols_hash = _mesa_pointer_hash_table_create(NULL); 67 struct hash_entry *entry = _mesa_hash_table_search(symbols_hash, addr); 68 if (!entry) { 69 char procname[256]; 70 unw_word_t off; 71 int ret; 72 73 ret = unw_get_proc_name(cursor, procname, sizeof(procname), &off); 74 if (ret && ret != -UNW_ENOMEM) { 75 procname[0] = '?'; 76 procname[1] = 0; 77 } 78 79 if (asprintf(&name, "%s%s", procname, ret == -UNW_ENOMEM ? "..." : "") == -1) 80 name = "??"; 81 entry = _mesa_hash_table_insert(symbols_hash, addr, (void*)name); 82 } 83 mtx_unlock(&symbols_mutex); 84 85 return entry->data; 86} 87 88void 89debug_backtrace_capture(struct debug_stack_frame *backtrace, 90 unsigned start_frame, 91 unsigned nr_frames) 92{ 93 unw_cursor_t cursor; 94 unw_context_t context; 95 unw_proc_info_t pip; 96 unsigned i = 0; 97 98 pip.unwind_info = 0; 99 100 unw_getcontext(&context); 101 unw_init_local(&cursor, &context); 102 103 while ((start_frame > 0) && (unw_step(&cursor) > 0)) 104 start_frame--; 105 106 while ((i < nr_frames) && (unw_step(&cursor) > 0)) { 107 unw_word_t ip; 108 109 unw_get_reg(&cursor, UNW_REG_IP, &ip); 110 unw_get_proc_info(&cursor, &pip); 111 112 backtrace[i].start_ip = pip.start_ip; 113 backtrace[i].off = ip - pip.start_ip; 114 backtrace[i].procname = symbol_name_cached(&cursor, &pip); 115 116 i++; 117 } 118 119 while (i < nr_frames) { 120 backtrace[i].start_ip = 0; 121 i++; 122 } 123} 124 125static const void * 126frame_ip(const struct debug_stack_frame *frame) 127{ 128 return (void *)(uintptr_t)(frame->start_ip + frame->off); 129} 130 131static const char * 132frame_info(const struct debug_stack_frame *frame, unsigned *offset) 133{ 134 Dl_info dlinfo; 135 const void *addr = frame_ip(frame); 136 137 138 if (dladdr(addr, &dlinfo) && dlinfo.dli_fname && 139 *dlinfo.dli_fname) { 140 *offset = (unsigned)((uintptr_t)addr - (uintptr_t)dlinfo.dli_fbase); 141 return dlinfo.dli_fname; 142 } 143 144 *offset = 0; 145 return "?"; 146} 147 148void 149debug_backtrace_dump(const struct debug_stack_frame *backtrace, 150 unsigned nr_frames) 151{ 152 unsigned i, offset; 153 const char *filename; 154 155 for (i = 0; i < nr_frames; ++i) { 156 if (!backtrace[i].start_ip) 157 break; 158 filename = frame_info(&backtrace[i], &offset); 159 debug_printf("\t%s(+0x%x) (%s+0x%x) [%p]\n", filename, offset, 160 backtrace[i].procname, backtrace[i].off, 161 frame_ip(&backtrace[i])); 162 } 163} 164 165void 166debug_backtrace_print(FILE *f, 167 const struct debug_stack_frame *backtrace, 168 unsigned nr_frames) 169{ 170 unsigned i, offset; 171 const char *filename; 172 173 for (i = 0; i < nr_frames; ++i) { 174 if (!backtrace[i].start_ip) 175 break; 176 filename = frame_info(&backtrace[i], &offset); 177 fprintf(f, "\t%s(+0x%x) (%s+0x%x) [%p]\n", filename, offset, 178 backtrace[i].procname, backtrace[i].off, 179 frame_ip(&backtrace[i])); 180 } 181} 182#elif defined(ANDROID) 183 /* Not implemented here; see u_debug_stack_android.cpp */ 184#else /* ! HAVE_LIBUNWIND */ 185 186#if defined(PIPE_OS_WINDOWS) 187#include <windows.h> 188#endif 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 unsigned i = 0; 203 204 if (!nr_frames) { 205 return; 206 } 207 208 /* 209 * On Windows try obtaining the stack backtrace via CaptureStackBackTrace. 210 * 211 * It works reliably both for x86 for x86_64. 212 */ 213#if defined(PIPE_OS_WINDOWS) 214 { 215 typedef USHORT (WINAPI *PFNCAPTURESTACKBACKTRACE)(ULONG, ULONG, 216 PVOID *, PULONG); 217 static PFNCAPTURESTACKBACKTRACE pfnCaptureStackBackTrace = NULL; 218 219 if (!pfnCaptureStackBackTrace) { 220 static HMODULE hModule = NULL; 221 if (!hModule) { 222 hModule = LoadLibraryA("kernel32"); 223 assert(hModule); 224 } 225 if (hModule) { 226 pfnCaptureStackBackTrace = 227 (PFNCAPTURESTACKBACKTRACE)GetProcAddress(hModule, 228 "RtlCaptureStackBackTrace"); 229 } 230 } 231 if (pfnCaptureStackBackTrace) { 232 /* 233 * Skip this (debug_backtrace_capture) function's frame. 234 */ 235 236 start_frame += 1; 237 238 assert(start_frame + nr_frames < 63); 239 i = pfnCaptureStackBackTrace(start_frame, nr_frames, 240 (PVOID *) &backtrace->function, NULL); 241 242 /* Pad remaing requested frames with NULL */ 243 while (i < nr_frames) { 244 backtrace[i++].function = NULL; 245 } 246 247 return; 248 } 249 } 250#endif 251 252#ifdef PIPE_ARCH_X86 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 const void **frame_pointer = ((const void **)__builtin_frame_address(1)); 257#pragma GCC diagnostic pop 258#elif defined(PIPE_CC_MSVC) 259 const void **frame_pointer; 260 __asm { 261 mov frame_pointer, ebp 262 } 263 frame_pointer = (const void **)frame_pointer[0]; 264#else 265 const void **frame_pointer = NULL; 266#endif 267 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#endif 291 292 while (nr_frames) { 293 backtrace[i++].function = NULL; 294 --nr_frames; 295 } 296} 297 298 299static mtx_t backtrace_mutex; 300 301static void 302initialize_backtrace_mutex() 303{ 304 static bool initialized = false; 305 306 if (!initialized) { 307 (void)mtx_init(&backtrace_mutex, mtx_plain); 308 initialized = true; 309 } 310} 311 312void 313debug_backtrace_dump(const struct debug_stack_frame *backtrace, 314 unsigned nr_frames) 315{ 316 unsigned i; 317 initialize_backtrace_mutex(); 318 mtx_lock(&backtrace_mutex); 319 for (i = 0; i < nr_frames; ++i) { 320 if (!backtrace[i].function) 321 break; 322 debug_symbol_print(backtrace[i].function); 323 } 324 mtx_unlock(&backtrace_mutex); 325} 326 327 328void 329debug_backtrace_print(FILE *f, 330 const struct debug_stack_frame *backtrace, 331 unsigned nr_frames) 332{ 333 unsigned i; 334 335 initialize_backtrace_mutex(); 336 mtx_lock(&backtrace_mutex); 337 for (i = 0; i < nr_frames; ++i) { 338 const char *symbol; 339 if (!backtrace[i].function) 340 break; 341 symbol = debug_symbol_name_cached(backtrace[i].function); 342 if (symbol) 343 fprintf(f, "%s\n", symbol); 344 } 345 fflush(f); 346 mtx_unlock(&backtrace_mutex); 347} 348 349#endif /* HAVE_LIBUNWIND */ 350