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(PIPE_OS_WINDOWS)
40 #include <windows.h>
41 #endif
42
43
44 /**
45 * Capture stack backtrace.
46 *
47 * NOTE: The implementation of this function is quite big, but it is important
48 * not to break it down in smaller functions to avoid adding new frames to the
49 * calling stack.
50 */
51 void
debug_backtrace_capture(struct debug_stack_frame * backtrace,unsigned start_frame,unsigned nr_frames)52 debug_backtrace_capture(struct debug_stack_frame *backtrace,
53 unsigned start_frame,
54 unsigned nr_frames)
55 {
56 const void **frame_pointer = NULL;
57 unsigned i = 0;
58
59 if (!nr_frames) {
60 return;
61 }
62
63 /*
64 * On Windows try obtaining the stack backtrace via CaptureStackBackTrace.
65 *
66 * It works reliably both for x86 for x86_64.
67 */
68 #if defined(PIPE_OS_WINDOWS)
69 {
70 typedef USHORT (WINAPI *PFNCAPTURESTACKBACKTRACE)(ULONG, ULONG,
71 PVOID *, PULONG);
72 static PFNCAPTURESTACKBACKTRACE pfnCaptureStackBackTrace = NULL;
73
74 if (!pfnCaptureStackBackTrace) {
75 static HMODULE hModule = NULL;
76 if (!hModule) {
77 hModule = LoadLibraryA("kernel32");
78 assert(hModule);
79 }
80 if (hModule) {
81 pfnCaptureStackBackTrace =
82 (PFNCAPTURESTACKBACKTRACE)GetProcAddress(hModule,
83 "RtlCaptureStackBackTrace");
84 }
85 }
86 if (pfnCaptureStackBackTrace) {
87 /*
88 * Skip this (debug_backtrace_capture) function's frame.
89 */
90
91 start_frame += 1;
92
93 assert(start_frame + nr_frames < 63);
94 i = pfnCaptureStackBackTrace(start_frame, nr_frames,
95 (PVOID *) &backtrace->function, NULL);
96
97 /* Pad remaing requested frames with NULL */
98 while (i < nr_frames) {
99 backtrace[i++].function = NULL;
100 }
101
102 return;
103 }
104 }
105 #endif
106
107 #if defined(PIPE_CC_GCC)
108 frame_pointer = ((const void **)__builtin_frame_address(1));
109 #elif defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86)
110 __asm {
111 mov frame_pointer, ebp
112 }
113 frame_pointer = (const void **)frame_pointer[0];
114 #else
115 frame_pointer = NULL;
116 #endif
117
118 #ifdef PIPE_ARCH_X86
119 while (nr_frames) {
120 const void **next_frame_pointer;
121
122 if (!frame_pointer)
123 break;
124
125 if (start_frame)
126 --start_frame;
127 else {
128 backtrace[i++].function = frame_pointer[1];
129 --nr_frames;
130 }
131
132 next_frame_pointer = (const void **)frame_pointer[0];
133
134 /* Limit the stack walk to avoid referencing undefined memory */
135 if ((uintptr_t)next_frame_pointer <= (uintptr_t)frame_pointer ||
136 (uintptr_t)next_frame_pointer > (uintptr_t)frame_pointer + 64*1024)
137 break;
138
139 frame_pointer = next_frame_pointer;
140 }
141 #else
142 (void) frame_pointer;
143 #endif
144
145 while (nr_frames) {
146 backtrace[i++].function = NULL;
147 --nr_frames;
148 }
149 }
150
151
152 void
debug_backtrace_dump(const struct debug_stack_frame * backtrace,unsigned nr_frames)153 debug_backtrace_dump(const struct debug_stack_frame *backtrace,
154 unsigned nr_frames)
155 {
156 unsigned i;
157
158 for (i = 0; i < nr_frames; ++i) {
159 if (!backtrace[i].function)
160 break;
161 debug_symbol_print(backtrace[i].function);
162 }
163 }
164
165