• 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 "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 
49 struct util_hash_table* symbols_hash;
50 static mtx_t symbols_mutex = _MTX_INITIALIZER_NP;
51 
hash_ptr(void * p)52 static unsigned hash_ptr(void* p)
53 {
54    return (unsigned)(uintptr_t)p;
55 }
56 
compare_ptr(void * a,void * b)57 static 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  */
71 static const char *
symbol_name_cached(unw_cursor_t * cursor,unw_proc_info_t * pip)72 symbol_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 
102 void
debug_backtrace_capture(struct debug_stack_frame * backtrace,unsigned start_frame,unsigned nr_frames)103 debug_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 
139 static const void *
frame_ip(const struct debug_stack_frame * frame)140 frame_ip(const struct debug_stack_frame *frame)
141 {
142    return (void *)(uintptr_t)(frame->start_ip + frame->off);
143 }
144 
145 static const char *
frame_info(const struct debug_stack_frame * frame,unsigned * offset)146 frame_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 
162 void
debug_backtrace_dump(const struct debug_stack_frame * backtrace,unsigned nr_frames)163 debug_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 
179 void
debug_backtrace_print(FILE * f,const struct debug_stack_frame * backtrace,unsigned nr_frames)180 debug_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 
197 #else /* ! HAVE_LIBUNWIND */
198 
199 #if defined(PIPE_OS_WINDOWS)
200 #include <windows.h>
201 #endif
202 
203 
204 /**
205  * Capture stack backtrace.
206  *
207  * NOTE: The implementation of this function is quite big, but it is important
208  * not to break it down in smaller functions to avoid adding new frames to the
209  * calling stack.
210  */
211 void
debug_backtrace_capture(struct debug_stack_frame * backtrace,unsigned start_frame,unsigned nr_frames)212 debug_backtrace_capture(struct debug_stack_frame *backtrace,
213                         unsigned start_frame,
214                         unsigned nr_frames)
215 {
216    const void **frame_pointer = NULL;
217    unsigned i = 0;
218 
219    if (!nr_frames) {
220       return;
221    }
222 
223    /*
224     * On Windows try obtaining the stack backtrace via CaptureStackBackTrace.
225     *
226     * It works reliably both for x86 for x86_64.
227     */
228 #if defined(PIPE_OS_WINDOWS)
229    {
230       typedef USHORT (WINAPI *PFNCAPTURESTACKBACKTRACE)(ULONG, ULONG,
231                                                         PVOID *, PULONG);
232       static PFNCAPTURESTACKBACKTRACE pfnCaptureStackBackTrace = NULL;
233 
234       if (!pfnCaptureStackBackTrace) {
235          static HMODULE hModule = NULL;
236          if (!hModule) {
237             hModule = LoadLibraryA("kernel32");
238             assert(hModule);
239          }
240          if (hModule) {
241             pfnCaptureStackBackTrace =
242                (PFNCAPTURESTACKBACKTRACE)GetProcAddress(hModule,
243                                                 "RtlCaptureStackBackTrace");
244          }
245       }
246       if (pfnCaptureStackBackTrace) {
247          /*
248           * Skip this (debug_backtrace_capture) function's frame.
249           */
250 
251          start_frame += 1;
252 
253          assert(start_frame + nr_frames < 63);
254          i = pfnCaptureStackBackTrace(start_frame, nr_frames,
255                                       (PVOID *) &backtrace->function, NULL);
256 
257          /* Pad remaing requested frames with NULL */
258          while (i < nr_frames) {
259             backtrace[i++].function = NULL;
260          }
261 
262          return;
263       }
264    }
265 #endif
266 
267 #if defined(PIPE_CC_GCC)
268    frame_pointer = ((const void **)__builtin_frame_address(1));
269 #elif defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86)
270    __asm {
271       mov frame_pointer, ebp
272    }
273    frame_pointer = (const void **)frame_pointer[0];
274 #else
275    frame_pointer = NULL;
276 #endif
277 
278 #ifdef PIPE_ARCH_X86
279    while (nr_frames) {
280       const void **next_frame_pointer;
281 
282       if (!frame_pointer)
283          break;
284 
285       if (start_frame)
286          --start_frame;
287       else {
288          backtrace[i++].function = frame_pointer[1];
289          --nr_frames;
290       }
291 
292       next_frame_pointer = (const void **)frame_pointer[0];
293 
294       /* Limit the stack walk to avoid referencing undefined memory */
295       if ((uintptr_t)next_frame_pointer <= (uintptr_t)frame_pointer ||
296           (uintptr_t)next_frame_pointer > (uintptr_t)frame_pointer + 64*1024)
297          break;
298 
299       frame_pointer = next_frame_pointer;
300    }
301 #else
302    (void) frame_pointer;
303 #endif
304 
305    while (nr_frames) {
306       backtrace[i++].function = NULL;
307       --nr_frames;
308    }
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 
318    for (i = 0; i < nr_frames; ++i) {
319       if (!backtrace[i].function)
320          break;
321       debug_symbol_print(backtrace[i].function);
322    }
323 }
324 
325 
326 void
debug_backtrace_print(FILE * f,const struct debug_stack_frame * backtrace,unsigned nr_frames)327 debug_backtrace_print(FILE *f,
328                       const struct debug_stack_frame *backtrace,
329                       unsigned nr_frames)
330 {
331    unsigned i;
332 
333    for (i = 0; i < nr_frames; ++i) {
334       const char *symbol;
335       if (!backtrace[i].function)
336          break;
337       symbol = debug_symbol_name_cached(backtrace[i].function);
338       if (symbol)
339          fprintf(f, "%s\n", symbol);
340    }
341 }
342 
343 #endif /* HAVE_LIBUNWIND */
344