• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
51 static struct hash_table* symbols_hash;
52 static 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  */
58 static const char *
symbol_name_cached(unw_cursor_t * cursor,unw_proc_info_t * pip)59 symbol_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 
88 void
debug_backtrace_capture(struct debug_stack_frame * backtrace,unsigned start_frame,unsigned nr_frames)89 debug_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 
125 static const void *
frame_ip(const struct debug_stack_frame * frame)126 frame_ip(const struct debug_stack_frame *frame)
127 {
128    return (void *)(uintptr_t)(frame->start_ip + frame->off);
129 }
130 
131 static const char *
frame_info(const struct debug_stack_frame * frame,unsigned * offset)132 frame_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 
148 void
debug_backtrace_dump(const struct debug_stack_frame * backtrace,unsigned nr_frames)149 debug_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 
165 void
debug_backtrace_print(FILE * f,const struct debug_stack_frame * backtrace,unsigned nr_frames)166 debug_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  */
197 void
debug_backtrace_capture(struct debug_stack_frame * backtrace,unsigned start_frame,unsigned nr_frames)198 debug_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 
299 static mtx_t backtrace_mutex;
300 
301 static void
initialize_backtrace_mutex()302 initialize_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 
312 void
debug_backtrace_dump(const struct debug_stack_frame * backtrace,unsigned nr_frames)313 debug_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 
328 void
debug_backtrace_print(FILE * f,const struct debug_stack_frame * backtrace,unsigned nr_frames)329 debug_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