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