• 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 
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 
50 static struct hash_table* symbols_hash;
51 static 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  */
57 static const char *
symbol_name_cached(unw_cursor_t * cursor,unw_proc_info_t * pip)58 symbol_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 
87 void
debug_backtrace_capture(struct debug_stack_frame * backtrace,unsigned start_frame,unsigned nr_frames)88 debug_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 
124 static const void *
frame_ip(const struct debug_stack_frame * frame)125 frame_ip(const struct debug_stack_frame *frame)
126 {
127    return (void *)(uintptr_t)(frame->start_ip + frame->off);
128 }
129 
130 static const char *
frame_info(const struct debug_stack_frame * frame,unsigned * offset)131 frame_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 
147 void
debug_backtrace_dump(const struct debug_stack_frame * backtrace,unsigned nr_frames)148 debug_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 
164 void
debug_backtrace_print(FILE * f,const struct debug_stack_frame * backtrace,unsigned nr_frames)165 debug_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  */
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    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 
301 void
debug_backtrace_dump(const struct debug_stack_frame * backtrace,unsigned nr_frames)302 debug_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 
315 void
debug_backtrace_print(FILE * f,const struct debug_stack_frame * backtrace,unsigned nr_frames)316 debug_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