1275970Scy/*
2275970Scy * Copyright (C) 2009  Internet Systems Consortium, Inc. ("ISC")
3275970Scy *
4275970Scy * Permission to use, copy, modify, and/or distribute this software for any
5275970Scy * purpose with or without fee is hereby granted, provided that the above
6275970Scy * copyright notice and this permission notice appear in all copies.
7275970Scy *
8275970Scy * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9275970Scy * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10275970Scy * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11275970Scy * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12275970Scy * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13275970Scy * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14275970Scy * PERFORMANCE OF THIS SOFTWARE.
15275970Scy */
16275970Scy
17275970Scy/* $Id: backtrace.c,v 1.3 2009/09/02 23:48:02 tbox Exp $ */
18275970Scy
19275970Scy/*! \file */
20275970Scy
21275970Scy#include "config.h"
22275970Scy
23275970Scy#include <string.h>
24275970Scy#include <stdlib.h>
25275970Scy#ifdef HAVE_LIBCTRACE
26275970Scy#include <execinfo.h>
27275970Scy#endif
28275970Scy
29275970Scy#include <isc/backtrace.h>
30275970Scy#include <isc/result.h>
31275970Scy#include <isc/util.h>
32275970Scy
33275970Scy#ifdef ISC_PLATFORM_USEBACKTRACE
34275970Scy/*
35275970Scy * Getting a back trace of a running process is tricky and highly platform
36275970Scy * dependent.  Our current approach is as follows:
37275970Scy * 1. If the system library supports the "backtrace()" function, use it.
38275970Scy * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64,
39275970Scy *    then use gcc's (hidden) Unwind_Backtrace() function.  Note that this
40275970Scy *    function doesn't work for C programs on many other architectures.
41275970Scy * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack
42275970Scy *    frame following frame pointers.  This assumes the executable binary
43275970Scy *    compiled with frame pointers; this is not always true for x86_64 (rather,
44275970Scy *    compiler optimizations often disable frame pointers).  The validation
45275970Scy *    checks in getnextframeptr() hopefully rejects bogus values stored in
46275970Scy *    the RBP register in such a case.  If the backtrace function itself crashes
47275970Scy *    due to this problem, the whole package should be rebuilt with
48275970Scy *    --disable-backtrace.
49275970Scy */
50275970Scy#ifdef HAVE_LIBCTRACE
51275970Scy#define BACKTRACE_LIBC
52275970Scy#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__))
53275970Scy#define BACKTRACE_GCC
54275970Scy#elif defined(__x86_64__) || defined(__i386__)
55275970Scy#define BACKTRACE_X86STACK
56275970Scy#else
57275970Scy#define BACKTRACE_DISABLED
58275970Scy#endif  /* HAVE_LIBCTRACE */
59275970Scy#else	/* !ISC_PLATFORM_USEBACKTRACE */
60275970Scy#define BACKTRACE_DISABLED
61275970Scy#endif	/* ISC_PLATFORM_USEBACKTRACE */
62275970Scy
63275970Scy#ifdef BACKTRACE_LIBC
64275970Scyisc_result_t
65275970Scyisc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
66275970Scy	int n;
67275970Scy
68275970Scy	/*
69275970Scy	 * Validate the arguments: intentionally avoid using REQUIRE().
70275970Scy	 * See notes in backtrace.h.
71275970Scy	 */
72275970Scy	if (addrs == NULL || nframes == NULL)
73275970Scy		return (ISC_R_FAILURE);
74275970Scy
75275970Scy	/*
76275970Scy	 * backtrace(3) includes this function itself in the address array,
77275970Scy	 * which should be eliminated from the returned sequence.
78275970Scy	 */
79275970Scy	n = backtrace(addrs, maxaddrs);
80275970Scy	if (n < 2)
81275970Scy		return (ISC_R_NOTFOUND);
82275970Scy	n--;
83275970Scy	memmove(addrs, &addrs[1], sizeof(void *) * n);
84275970Scy	*nframes = n;
85275970Scy	return (ISC_R_SUCCESS);
86275970Scy}
87275970Scy#elif defined(BACKTRACE_GCC)
88275970Scyextern int _Unwind_Backtrace(void* fn, void* a);
89275970Scyextern void* _Unwind_GetIP(void* ctx);
90275970Scy
91275970Scytypedef struct {
92275970Scy	void **result;
93275970Scy	int max_depth;
94275970Scy	int skip_count;
95275970Scy	int count;
96275970Scy} trace_arg_t;
97275970Scy
98275970Scystatic int
99275970Scybtcallback(void *uc, void *opq) {
100275970Scy	trace_arg_t *arg = (trace_arg_t *)opq;
101275970Scy
102275970Scy	if (arg->skip_count > 0)
103275970Scy		arg->skip_count--;
104275970Scy	else
105275970Scy		arg->result[arg->count++] = (void *)_Unwind_GetIP(uc);
106275970Scy	if (arg->count == arg->max_depth)
107275970Scy		return (5); /* _URC_END_OF_STACK */
108275970Scy
109275970Scy	return (0); /* _URC_NO_REASON */
110275970Scy}
111275970Scy
112275970Scyisc_result_t
113275970Scyisc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
114275970Scy	trace_arg_t arg;
115275970Scy
116275970Scy	/* Argument validation: see above. */
117275970Scy	if (addrs == NULL || nframes == NULL)
118275970Scy		return (ISC_R_FAILURE);
119275970Scy
120275970Scy	arg.skip_count = 1;
121275970Scy	arg.result = addrs;
122275970Scy	arg.max_depth = maxaddrs;
123275970Scy	arg.count = 0;
124275970Scy	_Unwind_Backtrace(btcallback, &arg);
125275970Scy
126275970Scy	*nframes = arg.count;
127275970Scy
128275970Scy	return (ISC_R_SUCCESS);
129275970Scy}
130275970Scy#elif defined(BACKTRACE_X86STACK)
131275970Scy#ifdef __x86_64__
132275970Scystatic unsigned long
133275970Scygetrbp() {
134275970Scy	__asm("movq %rbp, %rax\n");
135275970Scy}
136275970Scy#endif
137275970Scy
138275970Scystatic void **
139275970Scygetnextframeptr(void **sp) {
140275970Scy	void **newsp = (void **)*sp;
141275970Scy
142275970Scy	/*
143275970Scy	 * Perform sanity check for the new frame pointer, derived from
144275970Scy	 * google glog.  This can actually be bogus depending on compiler.
145275970Scy	 */
146275970Scy
147275970Scy	/* prohibit the stack frames from growing downwards */
148275970Scy	if (newsp <= sp)
149275970Scy		return (NULL);
150275970Scy
151275970Scy	/* A heuristics to reject "too large" frame: this actually happened. */
152275970Scy	if ((char *)newsp - (char *)sp > 100000)
153275970Scy		return (NULL);
154275970Scy
155275970Scy	/*
156275970Scy	 * Not sure if other checks used in glog are needed at this moment.
157275970Scy	 * For our purposes we don't have to consider non-contiguous frames,
158275970Scy	 * for example.
159275970Scy	 */
160275970Scy
161275970Scy	return (newsp);
162275970Scy}
163275970Scy
164275970Scyisc_result_t
165275970Scyisc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
166275970Scy	int i = 0;
167275970Scy	void **sp;
168275970Scy
169275970Scy	/* Argument validation: see above. */
170275970Scy	if (addrs == NULL || nframes == NULL)
171275970Scy		return (ISC_R_FAILURE);
172275970Scy
173275970Scy#ifdef __x86_64__
174275970Scy	sp = (void **)getrbp();
175275970Scy	if (sp == NULL)
176275970Scy		return (ISC_R_NOTFOUND);
177275970Scy	/*
178275970Scy	 * sp is the frame ptr of this function itself due to the call to
179275970Scy	 * getrbp(), so need to unwind one frame for consistency.
180275970Scy	 */
181275970Scy	sp = getnextframeptr(sp);
182275970Scy#else
183275970Scy	/*
184275970Scy	 * i386: the frame pointer is stored 2 words below the address for the
185275970Scy	 * first argument.  Note that the body of this function cannot be
186275970Scy	 * inlined since it depends on the address of the function argument.
187275970Scy	 */
188275970Scy	sp = (void **)&addrs - 2;
189275970Scy#endif
190275970Scy
191275970Scy	while (sp != NULL && i < maxaddrs) {
192275970Scy		addrs[i++] = *(sp + 1);
193275970Scy		sp = getnextframeptr(sp);
194275970Scy	}
195275970Scy
196275970Scy	*nframes = i;
197275970Scy
198275970Scy	return (ISC_R_SUCCESS);
199275970Scy}
200275970Scy#elif defined(BACKTRACE_DISABLED)
201275970Scyisc_result_t
202275970Scyisc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
203275970Scy	/* Argument validation: see above. */
204275970Scy	if (addrs == NULL || nframes == NULL)
205275970Scy		return (ISC_R_FAILURE);
206275970Scy
207275970Scy	UNUSED(maxaddrs);
208275970Scy
209275970Scy	return (ISC_R_NOTIMPLEMENTED);
210275970Scy}
211275970Scy#endif
212275970Scy
213275970Scyisc_result_t
214280849Scyisc_backtrace_getsymbolfromindex(int idx, const void **addrp,
215275970Scy				 const char **symbolp)
216275970Scy{
217275970Scy	REQUIRE(addrp != NULL && *addrp == NULL);
218275970Scy	REQUIRE(symbolp != NULL && *symbolp == NULL);
219275970Scy
220280849Scy	if (idx < 0 || idx >= isc__backtrace_nsymbols)
221275970Scy		return (ISC_R_RANGE);
222275970Scy
223280849Scy	*addrp = isc__backtrace_symtable[idx].addr;
224280849Scy	*symbolp = isc__backtrace_symtable[idx].symbol;
225275970Scy	return (ISC_R_SUCCESS);
226275970Scy}
227275970Scy
228275970Scystatic int
229275970Scysymtbl_compare(const void *addr, const void *entryarg) {
230275970Scy	const isc_backtrace_symmap_t *entry = entryarg;
231275970Scy	const isc_backtrace_symmap_t *end =
232275970Scy		&isc__backtrace_symtable[isc__backtrace_nsymbols - 1];
233275970Scy
234275970Scy	if (isc__backtrace_nsymbols == 1 || entry == end) {
235275970Scy		if (addr >= entry->addr) {
236275970Scy			/*
237275970Scy			 * If addr is equal to or larger than that of the last
238275970Scy			 * entry of the table, we cannot be sure if this is
239275970Scy			 * within a valid range so we consider it valid.
240275970Scy			 */
241275970Scy			return (0);
242275970Scy		}
243275970Scy		return (-1);
244275970Scy	}
245275970Scy
246275970Scy	/* entry + 1 is a valid entry from now on. */
247275970Scy	if (addr < entry->addr)
248275970Scy		return (-1);
249275970Scy	else if (addr >= (entry + 1)->addr)
250275970Scy		return (1);
251275970Scy	return (0);
252275970Scy}
253275970Scy
254275970Scyisc_result_t
255275970Scyisc_backtrace_getsymbol(const void *addr, const char **symbolp,
256275970Scy			unsigned long *offsetp)
257275970Scy{
258275970Scy	isc_result_t result = ISC_R_SUCCESS;
259275970Scy	isc_backtrace_symmap_t *found;
260275970Scy
261275970Scy	/*
262275970Scy	 * Validate the arguments: intentionally avoid using REQUIRE().
263275970Scy	 * See notes in backtrace.h.
264275970Scy	 */
265275970Scy	if (symbolp == NULL || *symbolp != NULL || offsetp == NULL)
266275970Scy		return (ISC_R_FAILURE);
267275970Scy
268275970Scy	if (isc__backtrace_nsymbols < 1)
269275970Scy		return (ISC_R_NOTFOUND);
270275970Scy
271275970Scy	/*
272275970Scy	 * Search the table for the entry that meets:
273275970Scy	 * entry.addr <= addr < next_entry.addr.
274275970Scy	 */
275275970Scy	found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols,
276275970Scy			sizeof(isc__backtrace_symtable[0]), symtbl_compare);
277275970Scy	if (found == NULL)
278275970Scy		result = ISC_R_NOTFOUND;
279275970Scy	else {
280275970Scy		*symbolp = found->symbol;
281293650Sglebius		*offsetp = (u_long)((const char *)addr - (char *)found->addr);
282275970Scy	}
283275970Scy
284275970Scy	return (result);
285275970Scy}
286