Blame lib/isc/backtrace.c

Packit Service ae04f2
/*
Packit Service ae04f2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
Packit Service ae04f2
 *
Packit Service ae04f2
 * This Source Code Form is subject to the terms of the Mozilla Public
Packit Service ae04f2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit Service ae04f2
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
Packit Service ae04f2
 *
Packit Service ae04f2
 * See the COPYRIGHT file distributed with this work for additional
Packit Service ae04f2
 * information regarding copyright ownership.
Packit Service ae04f2
 */
Packit Service ae04f2
Packit Service ae04f2
Packit Service ae04f2
/*! \file */
Packit Service ae04f2
Packit Service ae04f2
#include <config.h>
Packit Service ae04f2
Packit Service ae04f2
#include <string.h>
Packit Service ae04f2
#include <stdlib.h>
Packit Service ae04f2
#ifdef HAVE_LIBCTRACE
Packit Service ae04f2
#include <execinfo.h>
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
#include <isc/backtrace.h>
Packit Service ae04f2
#include <isc/result.h>
Packit Service ae04f2
#include <isc/util.h>
Packit Service ae04f2
Packit Service ae04f2
#ifdef ISC_PLATFORM_USEBACKTRACE
Packit Service ae04f2
/*
Packit Service ae04f2
 * Getting a back trace of a running process is tricky and highly platform
Packit Service ae04f2
 * dependent.  Our current approach is as follows:
Packit Service ae04f2
 * 1. If the system library supports the "backtrace()" function, use it.
Packit Service ae04f2
 * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64,
Packit Service ae04f2
 *    then use gcc's (hidden) Unwind_Backtrace() function.  Note that this
Packit Service ae04f2
 *    function doesn't work for C programs on many other architectures.
Packit Service ae04f2
 * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack
Packit Service ae04f2
 *    frame following frame pointers.  This assumes the executable binary
Packit Service ae04f2
 *    compiled with frame pointers; this is not always true for x86_64 (rather,
Packit Service ae04f2
 *    compiler optimizations often disable frame pointers).  The validation
Packit Service ae04f2
 *    checks in getnextframeptr() hopefully rejects bogus values stored in
Packit Service ae04f2
 *    the RBP register in such a case.  If the backtrace function itself crashes
Packit Service ae04f2
 *    due to this problem, the whole package should be rebuilt with
Packit Service ae04f2
 *    --disable-backtrace.
Packit Service ae04f2
 */
Packit Service ae04f2
#ifdef HAVE_LIBCTRACE
Packit Service ae04f2
#define BACKTRACE_LIBC
Packit Service ae04f2
#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__))
Packit Service ae04f2
#define BACKTRACE_GCC
Packit Service ae04f2
#elif defined(WIN32)
Packit Service ae04f2
#define BACKTRACE_WIN32
Packit Service ae04f2
#elif defined(__x86_64__) || defined(__i386__)
Packit Service ae04f2
#define BACKTRACE_X86STACK
Packit Service ae04f2
#else
Packit Service ae04f2
#define BACKTRACE_DISABLED
Packit Service ae04f2
#endif  /* HAVE_LIBCTRACE */
Packit Service ae04f2
#else	/* !ISC_PLATFORM_USEBACKTRACE */
Packit Service ae04f2
#define BACKTRACE_DISABLED
Packit Service ae04f2
#endif	/* ISC_PLATFORM_USEBACKTRACE */
Packit Service ae04f2
Packit Service ae04f2
#ifdef BACKTRACE_LIBC
Packit Service ae04f2
isc_result_t
Packit Service ae04f2
isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
Packit Service ae04f2
	int n;
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * Validate the arguments: intentionally avoid using REQUIRE().
Packit Service ae04f2
	 * See notes in backtrace.h.
Packit Service ae04f2
	 */
Packit Service ae04f2
	if (addrs == NULL || nframes == NULL)
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * backtrace(3) includes this function itself in the address array,
Packit Service ae04f2
	 * which should be eliminated from the returned sequence.
Packit Service ae04f2
	 */
Packit Service ae04f2
	n = backtrace(addrs, maxaddrs);
Packit Service ae04f2
	if (n < 2)
Packit Service ae04f2
		return (ISC_R_NOTFOUND);
Packit Service ae04f2
	n--;
Packit Service ae04f2
	memmove(addrs, &addrs[1], sizeof(void *) * n);
Packit Service ae04f2
	*nframes = n;
Packit Service ae04f2
	return (ISC_R_SUCCESS);
Packit Service ae04f2
}
Packit Service ae04f2
#elif defined(BACKTRACE_GCC)
Packit Service ae04f2
extern int _Unwind_Backtrace(void* fn, void* a);
Packit Service ae04f2
extern void* _Unwind_GetIP(void* ctx);
Packit Service ae04f2
Packit Service ae04f2
typedef struct {
Packit Service ae04f2
	void **result;
Packit Service ae04f2
	int max_depth;
Packit Service ae04f2
	int skip_count;
Packit Service ae04f2
	int count;
Packit Service ae04f2
} trace_arg_t;
Packit Service ae04f2
Packit Service ae04f2
static int
Packit Service ae04f2
btcallback(void *uc, void *opq) {
Packit Service ae04f2
	trace_arg_t *arg = (trace_arg_t *)opq;
Packit Service ae04f2
Packit Service ae04f2
	if (arg->skip_count > 0)
Packit Service ae04f2
		arg->skip_count--;
Packit Service ae04f2
	else
Packit Service ae04f2
		arg->result[arg->count++] = (void *)_Unwind_GetIP(uc);
Packit Service ae04f2
	if (arg->count == arg->max_depth)
Packit Service ae04f2
		return (5); /* _URC_END_OF_STACK */
Packit Service ae04f2
Packit Service ae04f2
	return (0); /* _URC_NO_REASON */
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
isc_result_t
Packit Service ae04f2
isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
Packit Service ae04f2
	trace_arg_t arg;
Packit Service ae04f2
Packit Service ae04f2
	/* Argument validation: see above. */
Packit Service ae04f2
	if (addrs == NULL || nframes == NULL)
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
Packit Service ae04f2
	arg.skip_count = 1;
Packit Service ae04f2
	arg.result = addrs;
Packit Service ae04f2
	arg.max_depth = maxaddrs;
Packit Service ae04f2
	arg.count = 0;
Packit Service ae04f2
	_Unwind_Backtrace(btcallback, &arg;;
Packit Service ae04f2
Packit Service ae04f2
	*nframes = arg.count;
Packit Service ae04f2
Packit Service ae04f2
	return (ISC_R_SUCCESS);
Packit Service ae04f2
}
Packit Service ae04f2
#elif defined(BACKTRACE_WIN32)
Packit Service ae04f2
isc_result_t
Packit Service ae04f2
isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
Packit Service ae04f2
	unsigned long ftc = (unsigned long)maxaddrs;
Packit Service ae04f2
Packit Service ae04f2
	*nframes = (int)CaptureStackBackTrace(1, ftc, addrs, NULL);
Packit Service ae04f2
	return ISC_R_SUCCESS;
Packit Service ae04f2
}
Packit Service ae04f2
#elif defined(BACKTRACE_X86STACK)
Packit Service ae04f2
#ifdef __x86_64__
Packit Service ae04f2
static unsigned long
Packit Service ae04f2
getrbp(void) {
Packit Service ae04f2
	__asm("movq %rbp, %rax\n");
Packit Service ae04f2
}
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
static void **
Packit Service ae04f2
getnextframeptr(void **sp) {
Packit Service ae04f2
	void **newsp = (void **)*sp;
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * Perform sanity check for the new frame pointer, derived from
Packit Service ae04f2
	 * google glog.  This can actually be bogus depending on compiler.
Packit Service ae04f2
	 */
Packit Service ae04f2
Packit Service ae04f2
	/* prohibit the stack frames from growing downwards */
Packit Service ae04f2
	if (newsp <= sp)
Packit Service ae04f2
		return (NULL);
Packit Service ae04f2
Packit Service ae04f2
	/* A heuristics to reject "too large" frame: this actually happened. */
Packit Service ae04f2
	if ((char *)newsp - (char *)sp > 100000)
Packit Service ae04f2
		return (NULL);
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * Not sure if other checks used in glog are needed at this moment.
Packit Service ae04f2
	 * For our purposes we don't have to consider non-contiguous frames,
Packit Service ae04f2
	 * for example.
Packit Service ae04f2
	 */
Packit Service ae04f2
Packit Service ae04f2
	return (newsp);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
isc_result_t
Packit Service ae04f2
isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
Packit Service ae04f2
	int i = 0;
Packit Service ae04f2
	void **sp;
Packit Service ae04f2
Packit Service ae04f2
	/* Argument validation: see above. */
Packit Service ae04f2
	if (addrs == NULL || nframes == NULL)
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
Packit Service ae04f2
#ifdef __x86_64__
Packit Service ae04f2
	sp = (void **)getrbp();
Packit Service ae04f2
	if (sp == NULL)
Packit Service ae04f2
		return (ISC_R_NOTFOUND);
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * sp is the frame ptr of this function itself due to the call to
Packit Service ae04f2
	 * getrbp(), so need to unwind one frame for consistency.
Packit Service ae04f2
	 */
Packit Service ae04f2
	sp = getnextframeptr(sp);
Packit Service ae04f2
#else
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * i386: the frame pointer is stored 2 words below the address for the
Packit Service ae04f2
	 * first argument.  Note that the body of this function cannot be
Packit Service ae04f2
	 * inlined since it depends on the address of the function argument.
Packit Service ae04f2
	 */
Packit Service ae04f2
	sp = (void **)&addrs - 2;
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
	while (sp != NULL && i < maxaddrs) {
Packit Service ae04f2
		addrs[i++] = *(sp + 1);
Packit Service ae04f2
		sp = getnextframeptr(sp);
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	*nframes = i;
Packit Service ae04f2
Packit Service ae04f2
	return (ISC_R_SUCCESS);
Packit Service ae04f2
}
Packit Service ae04f2
#elif defined(BACKTRACE_DISABLED)
Packit Service ae04f2
isc_result_t
Packit Service ae04f2
isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) {
Packit Service ae04f2
	/* Argument validation: see above. */
Packit Service ae04f2
	if (addrs == NULL || nframes == NULL)
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
Packit Service ae04f2
	UNUSED(maxaddrs);
Packit Service ae04f2
Packit Service ae04f2
	return (ISC_R_NOTIMPLEMENTED);
Packit Service ae04f2
}
Packit Service ae04f2
#endif
Packit Service ae04f2
Packit Service ae04f2
isc_result_t
Packit Service ae04f2
isc_backtrace_getsymbolfromindex(int idx, const void **addrp,
Packit Service ae04f2
				 const char **symbolp)
Packit Service ae04f2
{
Packit Service ae04f2
	REQUIRE(addrp != NULL && *addrp == NULL);
Packit Service ae04f2
	REQUIRE(symbolp != NULL && *symbolp == NULL);
Packit Service ae04f2
Packit Service ae04f2
	if (idx < 0 || idx >= isc__backtrace_nsymbols)
Packit Service ae04f2
		return (ISC_R_RANGE);
Packit Service ae04f2
Packit Service ae04f2
	*addrp = isc__backtrace_symtable[idx].addr;
Packit Service ae04f2
	*symbolp = isc__backtrace_symtable[idx].symbol;
Packit Service ae04f2
	return (ISC_R_SUCCESS);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
static int
Packit Service ae04f2
symtbl_compare(const void *addr, const void *entryarg) {
Packit Service ae04f2
	const isc_backtrace_symmap_t *entry = entryarg;
Packit Service ae04f2
	const isc_backtrace_symmap_t *end =
Packit Service ae04f2
		&isc__backtrace_symtable[isc__backtrace_nsymbols - 1];
Packit Service ae04f2
Packit Service ae04f2
	if (isc__backtrace_nsymbols == 1 || entry == end) {
Packit Service ae04f2
		if (addr >= entry->addr) {
Packit Service ae04f2
			/*
Packit Service ae04f2
			 * If addr is equal to or larger than that of the last
Packit Service ae04f2
			 * entry of the table, we cannot be sure if this is
Packit Service ae04f2
			 * within a valid range so we consider it valid.
Packit Service ae04f2
			 */
Packit Service ae04f2
			return (0);
Packit Service ae04f2
		}
Packit Service ae04f2
		return (-1);
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	/* entry + 1 is a valid entry from now on. */
Packit Service ae04f2
	if (addr < entry->addr)
Packit Service ae04f2
		return (-1);
Packit Service ae04f2
	else if (addr >= (entry + 1)->addr)
Packit Service ae04f2
		return (1);
Packit Service ae04f2
	return (0);
Packit Service ae04f2
}
Packit Service ae04f2
Packit Service ae04f2
isc_result_t
Packit Service ae04f2
isc_backtrace_getsymbol(const void *addr, const char **symbolp,
Packit Service ae04f2
			unsigned long *offsetp)
Packit Service ae04f2
{
Packit Service ae04f2
	isc_result_t result = ISC_R_SUCCESS;
Packit Service ae04f2
	isc_backtrace_symmap_t *found;
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * Validate the arguments: intentionally avoid using REQUIRE().
Packit Service ae04f2
	 * See notes in backtrace.h.
Packit Service ae04f2
	 */
Packit Service ae04f2
	if (symbolp == NULL || *symbolp != NULL || offsetp == NULL)
Packit Service ae04f2
		return (ISC_R_FAILURE);
Packit Service ae04f2
Packit Service ae04f2
	if (isc__backtrace_nsymbols < 1)
Packit Service ae04f2
		return (ISC_R_NOTFOUND);
Packit Service ae04f2
Packit Service ae04f2
	/*
Packit Service ae04f2
	 * Search the table for the entry that meets:
Packit Service ae04f2
	 * entry.addr <= addr < next_entry.addr.
Packit Service ae04f2
	 */
Packit Service ae04f2
	found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols,
Packit Service ae04f2
			sizeof(isc__backtrace_symtable[0]), symtbl_compare);
Packit Service ae04f2
	if (found == NULL)
Packit Service ae04f2
		result = ISC_R_NOTFOUND;
Packit Service ae04f2
	else {
Packit Service ae04f2
		*symbolp = found->symbol;
Packit Service ae04f2
		*offsetp = (unsigned long) ((const char *)addr -
Packit Service ae04f2
					    (char *)found->addr);
Packit Service ae04f2
	}
Packit Service ae04f2
Packit Service ae04f2
	return (result);
Packit Service ae04f2
}