/src/ntp-dev/lib/isc/backtrace.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * Permission to use, copy, modify, and/or distribute this software for any |
5 | | * purpose with or without fee is hereby granted, provided that the above |
6 | | * copyright notice and this permission notice appear in all copies. |
7 | | * |
8 | | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH |
9 | | * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY |
10 | | * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, |
11 | | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM |
12 | | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE |
13 | | * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
14 | | * PERFORMANCE OF THIS SOFTWARE. |
15 | | */ |
16 | | |
17 | | /* $Id: backtrace.c,v 1.3 2009/09/02 23:48:02 tbox Exp $ */ |
18 | | |
19 | | /*! \file */ |
20 | | |
21 | | #include "config.h" |
22 | | |
23 | | #include <string.h> |
24 | | #include <stdlib.h> |
25 | | #ifdef HAVE_LIBCTRACE |
26 | | #include <execinfo.h> |
27 | | #endif |
28 | | |
29 | | #include <isc/backtrace.h> |
30 | | #include <isc/result.h> |
31 | | #include <isc/util.h> |
32 | | |
33 | | #ifdef ISC_PLATFORM_USEBACKTRACE |
34 | | /* |
35 | | * Getting a back trace of a running process is tricky and highly platform |
36 | | * dependent. Our current approach is as follows: |
37 | | * 1. If the system library supports the "backtrace()" function, use it. |
38 | | * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64, |
39 | | * then use gcc's (hidden) Unwind_Backtrace() function. Note that this |
40 | | * function doesn't work for C programs on many other architectures. |
41 | | * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack |
42 | | * frame following frame pointers. This assumes the executable binary |
43 | | * compiled with frame pointers; this is not always true for x86_64 (rather, |
44 | | * compiler optimizations often disable frame pointers). The validation |
45 | | * checks in getnextframeptr() hopefully rejects bogus values stored in |
46 | | * the RBP register in such a case. If the backtrace function itself crashes |
47 | | * due to this problem, the whole package should be rebuilt with |
48 | | * --disable-backtrace. |
49 | | */ |
50 | | #ifdef HAVE_LIBCTRACE |
51 | | #define BACKTRACE_LIBC |
52 | | #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__)) |
53 | | #define BACKTRACE_GCC |
54 | | #elif defined(__x86_64__) || defined(__i386__) |
55 | | #define BACKTRACE_X86STACK |
56 | | #else |
57 | | #define BACKTRACE_DISABLED |
58 | | #endif /* HAVE_LIBCTRACE */ |
59 | | #else /* !ISC_PLATFORM_USEBACKTRACE */ |
60 | | #define BACKTRACE_DISABLED |
61 | | #endif /* ISC_PLATFORM_USEBACKTRACE */ |
62 | | |
63 | | #ifdef BACKTRACE_LIBC |
64 | | isc_result_t |
65 | | isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { |
66 | | int n; |
67 | | |
68 | | /* |
69 | | * Validate the arguments: intentionally avoid using REQUIRE(). |
70 | | * See notes in backtrace.h. |
71 | | */ |
72 | | if (addrs == NULL || nframes == NULL) |
73 | | return (ISC_R_FAILURE); |
74 | | |
75 | | /* |
76 | | * backtrace(3) includes this function itself in the address array, |
77 | | * which should be eliminated from the returned sequence. |
78 | | */ |
79 | | n = backtrace(addrs, maxaddrs); |
80 | | if (n < 2) |
81 | | return (ISC_R_NOTFOUND); |
82 | | n--; |
83 | | memmove(addrs, &addrs[1], sizeof(void *) * n); |
84 | | *nframes = n; |
85 | | return (ISC_R_SUCCESS); |
86 | | } |
87 | | #elif defined(BACKTRACE_GCC) |
88 | | extern int _Unwind_Backtrace(void* fn, void* a); |
89 | | extern void* _Unwind_GetIP(void* ctx); |
90 | | |
91 | | typedef struct { |
92 | | void **result; |
93 | | int max_depth; |
94 | | int skip_count; |
95 | | int count; |
96 | | } trace_arg_t; |
97 | | |
98 | | static int |
99 | | btcallback(void *uc, void *opq) { |
100 | | trace_arg_t *arg = (trace_arg_t *)opq; |
101 | | |
102 | | if (arg->skip_count > 0) |
103 | | arg->skip_count--; |
104 | | else |
105 | | arg->result[arg->count++] = (void *)_Unwind_GetIP(uc); |
106 | | if (arg->count == arg->max_depth) |
107 | | return (5); /* _URC_END_OF_STACK */ |
108 | | |
109 | | return (0); /* _URC_NO_REASON */ |
110 | | } |
111 | | |
112 | | isc_result_t |
113 | | isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { |
114 | | trace_arg_t arg; |
115 | | |
116 | | /* Argument validation: see above. */ |
117 | | if (addrs == NULL || nframes == NULL) |
118 | | return (ISC_R_FAILURE); |
119 | | |
120 | | arg.skip_count = 1; |
121 | | arg.result = addrs; |
122 | | arg.max_depth = maxaddrs; |
123 | | arg.count = 0; |
124 | | _Unwind_Backtrace(btcallback, &arg); |
125 | | |
126 | | *nframes = arg.count; |
127 | | |
128 | | return (ISC_R_SUCCESS); |
129 | | } |
130 | | #elif defined(BACKTRACE_X86STACK) |
131 | | #ifdef __x86_64__ |
132 | | static unsigned long |
133 | | getrbp() { |
134 | | __asm("movq %rbp, %rax\n"); |
135 | | } |
136 | | #endif |
137 | | |
138 | | static void ** |
139 | | getnextframeptr(void **sp) { |
140 | | void **newsp = (void **)*sp; |
141 | | |
142 | | /* |
143 | | * Perform sanity check for the new frame pointer, derived from |
144 | | * google glog. This can actually be bogus depending on compiler. |
145 | | */ |
146 | | |
147 | | /* prohibit the stack frames from growing downwards */ |
148 | | if (newsp <= sp) |
149 | | return (NULL); |
150 | | |
151 | | /* A heuristics to reject "too large" frame: this actually happened. */ |
152 | | if ((char *)newsp - (char *)sp > 100000) |
153 | | return (NULL); |
154 | | |
155 | | /* |
156 | | * Not sure if other checks used in glog are needed at this moment. |
157 | | * For our purposes we don't have to consider non-contiguous frames, |
158 | | * for example. |
159 | | */ |
160 | | |
161 | | return (newsp); |
162 | | } |
163 | | |
164 | | isc_result_t |
165 | | isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { |
166 | | int i = 0; |
167 | | void **sp; |
168 | | |
169 | | /* Argument validation: see above. */ |
170 | | if (addrs == NULL || nframes == NULL) |
171 | | return (ISC_R_FAILURE); |
172 | | |
173 | | #ifdef __x86_64__ |
174 | | sp = (void **)getrbp(); |
175 | | if (sp == NULL) |
176 | | return (ISC_R_NOTFOUND); |
177 | | /* |
178 | | * sp is the frame ptr of this function itself due to the call to |
179 | | * getrbp(), so need to unwind one frame for consistency. |
180 | | */ |
181 | | sp = getnextframeptr(sp); |
182 | | #else |
183 | | /* |
184 | | * i386: the frame pointer is stored 2 words below the address for the |
185 | | * first argument. Note that the body of this function cannot be |
186 | | * inlined since it depends on the address of the function argument. |
187 | | */ |
188 | | sp = (void **)&addrs - 2; |
189 | | #endif |
190 | | |
191 | | while (sp != NULL && i < maxaddrs) { |
192 | | addrs[i++] = *(sp + 1); |
193 | | sp = getnextframeptr(sp); |
194 | | } |
195 | | |
196 | | *nframes = i; |
197 | | |
198 | | return (ISC_R_SUCCESS); |
199 | | } |
200 | | #elif defined(BACKTRACE_DISABLED) |
201 | | isc_result_t |
202 | 0 | isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { |
203 | | /* Argument validation: see above. */ |
204 | 0 | if (addrs == NULL || nframes == NULL) |
205 | 0 | return (ISC_R_FAILURE); |
206 | | |
207 | 0 | UNUSED(maxaddrs); |
208 | |
|
209 | 0 | return (ISC_R_NOTIMPLEMENTED); |
210 | 0 | } |
211 | | #endif |
212 | | |
213 | | isc_result_t |
214 | | isc_backtrace_getsymbolfromindex(int idx, const void **addrp, |
215 | | const char **symbolp) |
216 | 0 | { |
217 | 0 | REQUIRE(addrp != NULL && *addrp == NULL); |
218 | 0 | REQUIRE(symbolp != NULL && *symbolp == NULL); |
219 | | |
220 | 0 | if (idx < 0 || idx >= isc__backtrace_nsymbols) |
221 | 0 | return (ISC_R_RANGE); |
222 | | |
223 | 0 | *addrp = isc__backtrace_symtable[idx].addr; |
224 | 0 | *symbolp = isc__backtrace_symtable[idx].symbol; |
225 | 0 | return (ISC_R_SUCCESS); |
226 | 0 | } |
227 | | |
228 | | static int |
229 | 0 | symtbl_compare(const void *addr, const void *entryarg) { |
230 | 0 | const isc_backtrace_symmap_t *entry = entryarg; |
231 | 0 | const isc_backtrace_symmap_t *end = |
232 | 0 | &isc__backtrace_symtable[isc__backtrace_nsymbols - 1]; |
233 | |
|
234 | 0 | if (isc__backtrace_nsymbols == 1 || entry == end) { |
235 | 0 | if (addr >= entry->addr) { |
236 | | /* |
237 | | * If addr is equal to or larger than that of the last |
238 | | * entry of the table, we cannot be sure if this is |
239 | | * within a valid range so we consider it valid. |
240 | | */ |
241 | 0 | return (0); |
242 | 0 | } |
243 | 0 | return (-1); |
244 | 0 | } |
245 | | |
246 | | /* entry + 1 is a valid entry from now on. */ |
247 | 0 | if (addr < entry->addr) |
248 | 0 | return (-1); |
249 | 0 | else if (addr >= (entry + 1)->addr) |
250 | 0 | return (1); |
251 | 0 | return (0); |
252 | 0 | } |
253 | | |
254 | | isc_result_t |
255 | | isc_backtrace_getsymbol(const void *addr, const char **symbolp, |
256 | | unsigned long *offsetp) |
257 | 0 | { |
258 | 0 | isc_result_t result = ISC_R_SUCCESS; |
259 | 0 | isc_backtrace_symmap_t *found; |
260 | | |
261 | | /* |
262 | | * Validate the arguments: intentionally avoid using REQUIRE(). |
263 | | * See notes in backtrace.h. |
264 | | */ |
265 | 0 | if (symbolp == NULL || *symbolp != NULL || offsetp == NULL) |
266 | 0 | return (ISC_R_FAILURE); |
267 | | |
268 | 0 | if (isc__backtrace_nsymbols < 1) |
269 | 0 | return (ISC_R_NOTFOUND); |
270 | | |
271 | | /* |
272 | | * Search the table for the entry that meets: |
273 | | * entry.addr <= addr < next_entry.addr. |
274 | | */ |
275 | 0 | found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols, |
276 | 0 | sizeof(isc__backtrace_symtable[0]), symtbl_compare); |
277 | 0 | if (found == NULL) |
278 | 0 | result = ISC_R_NOTFOUND; |
279 | 0 | else { |
280 | 0 | *symbolp = found->symbol; |
281 | 0 | *offsetp = (u_long)((const char *)addr - (char *)found->addr); |
282 | 0 | } |
283 | |
|
284 | 0 | return (result); |
285 | 0 | } |