Let Asterisk find better backtrace information with libbfd.

The menuselect option BETTER_BACKTRACES, if enabled, will use libbfd to search
for better symbol information within both the Asterisk binary, as well as
loaded modules, to assist when using inline backtraces to track down problems.


git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.4@298905 65c4cc65-6c06-0410-ace0-fbb531ad65f3
1.4
Tilghman Lesher 15 years ago
parent f47ff91be3
commit 78febf3647

@ -14,4 +14,9 @@
</member>
<member name="TEST_FRAMEWORK" displayname="Enable Test Framework API">
</member>
<member name="BETTER_BACKTRACES" displayname="Use libbfd to generate better inline backtraces">
<depend>BFD</depend>
<depend>DLADDR</depend>
<defaultenabled>no</defaultenabled>
</member>
</category>

@ -1,8 +1,10 @@
ASOUND=@PBX_ALSA@
BFD=@PBX_BFD@
BISON=@PBX_BISON@
CURL=@PBX_CURL@
DAHDI=@PBX_DAHDI@
DAHDI_TRANSCODE=@PBX_DAHDI_TRANSCODE@
DLADDR=@PBX_DLADDR@
FLEX=@PBX_FLEX@
FREETDS=@PBX_FREETDS@
GSM=@PBX_GSM@

192
configure vendored

@ -630,6 +630,7 @@ KDEDIR
KDEINIT
GSM_INTERNAL
PBX_DAHDI_TRANSCODE
PBX_DLADDR
AST_NO_STRICT_OVERFLOW
AST_FORTIFY_SOURCE
AST_DECLARATION_AFTER_STATEMENT
@ -819,6 +820,10 @@ PBX_CAP
CAP_DIR
CAP_INCLUDE
CAP_LIB
PBX_BFD
BFD_DIR
BFD_INCLUDE
BFD_LIB
PBX_ALSA
ALSA_DIR
ALSA_INCLUDE
@ -934,6 +939,7 @@ enable_option_checking
with_gnu_ld
enable_dev_mode
with_asound
with_bfd
with_cap
with_curses
with_dahdi
@ -1613,6 +1619,7 @@ Optional Packages:
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
--with-asound=PATH use Advanced Linux Sound Architecture files in PATH
--with-bfd=PATH use Debug symbol decoding files in PATH
--with-cap=PATH use POSIX 1.e capabilities files in PATH
--with-curses=PATH use curses files in PATH
--with-dahdi=PATH use DAHDI files in PATH
@ -7863,6 +7870,35 @@ PBX_ALSA=0
BFD_DESCRIP="Debug symbol decoding"
BFD_OPTION="bfd"
# Check whether --with-bfd was given.
if test "${with_bfd+set}" = set; then :
withval=$with_bfd;
case ${withval} in
n|no)
USE_BFD=no
;;
y|ye|yes)
BFD_MANDATORY="yes"
;;
*)
BFD_DIR="${withval}"
BFD_MANDATORY="yes"
;;
esac
fi
PBX_BFD=0
CAP_DESCRIP="POSIX 1.e capabilities"
CAP_OPTION="cap"
@ -13179,6 +13215,41 @@ $as_echo "#define TYPEOF_FD_SET_FDS_BITS long long" >>confdefs.h
fi ; fi ; fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dladdr in dlfcn.h" >&5
$as_echo_n "checking for dladdr in dlfcn.h... " >&6; }
PBX_DLADDR=0
old_LIBS=${LIBS}
LIBS="${LIBS} -ldl"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#define _GNU_SOURCE 1
#include <dlfcn.h>
int
main ()
{
dladdr((void *)0, (void *)0)
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
PBX_DLADDR=1
$as_echo "#define HAVE_DLADDR 1" >>confdefs.h
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=${old_LIBS}
# do the package library checks now
@ -13304,6 +13375,127 @@ fi
if test "${USE_BFD}" != "no"; then
pbxlibdir=""
if test "x${BFD_DIR}" != "x"; then
if test -d ${BFD_DIR}/lib; then
pbxlibdir="-L${BFD_DIR}/lib"
else
pbxlibdir="-L${BFD_DIR}"
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for bfd_openr in -lbfd" >&5
$as_echo_n "checking for bfd_openr in -lbfd... " >&6; }
if test "${ac_cv_lib_bfd_bfd_openr+set}" = set; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lbfd ${pbxlibdir} $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char bfd_openr ();
int
main ()
{
return bfd_openr ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_bfd_bfd_openr=yes
else
ac_cv_lib_bfd_bfd_openr=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bfd_bfd_openr" >&5
$as_echo "$ac_cv_lib_bfd_bfd_openr" >&6; }
if test "x$ac_cv_lib_bfd_bfd_openr" = x""yes; then :
AST_BFD_FOUND=yes
else
AST_BFD_FOUND=no
fi
if test "${AST_BFD_FOUND}" = "yes"; then
BFD_LIB="-lbfd "
BFD_HEADER_FOUND="1"
if test "x${BFD_DIR}" != "x"; then
BFD_LIB="${pbxlibdir} ${BFD_LIB}"
BFD_INCLUDE="-I${BFD_DIR}/include"
fi
BFD_INCLUDE="${BFD_INCLUDE} "
saved_cppflags="${CPPFLAGS}"
CPPFLAGS="${CPPFLAGS} ${BFD_INCLUDE}"
if test "xbfd.h" != "x" ; then
ac_fn_c_check_header_mongrel "$LINENO" "bfd.h" "ac_cv_header_bfd_h" "$ac_includes_default"
if test "x$ac_cv_header_bfd_h" = x""yes; then :
BFD_HEADER_FOUND=1
else
BFD_HEADER_FOUND=0
fi
fi
CPPFLAGS="${saved_cppflags}"
if test "x${BFD_HEADER_FOUND}" = "x0" ; then
if test -n "${BFD_MANDATORY}" ;
then
{ $as_echo "$as_me:${as_lineno-$LINENO}: ***" >&5
$as_echo "$as_me: ***" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: *** It appears that you do not have the bfd development package installed." >&5
$as_echo "$as_me: *** It appears that you do not have the bfd development package installed." >&6;}
if test "x${BFD_OPTION}" = "x" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: *** Please install it to include ${BFD_DESCRIP} support" >&5
$as_echo "$as_me: *** Please install it to include ${BFD_DESCRIP} support" >&6;}
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: *** Please install it to include ${BFD_DESCRIP} support, or re-run configure" >&5
$as_echo "$as_me: *** Please install it to include ${BFD_DESCRIP} support, or re-run configure" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: *** without explicitly specifying --with-${BFD_OPTION}" >&5
$as_echo "$as_me: *** without explicitly specifying --with-${BFD_OPTION}" >&6;}
fi
exit 1
fi
BFD_LIB=""
BFD_INCLUDE=""
PBX_BFD=0
else
PBX_BFD=1
cat >>confdefs.h <<_ACEOF
#define HAVE_BFD 1
_ACEOF
fi
elif test -n "${BFD_MANDATORY}";
then
{ $as_echo "$as_me:${as_lineno-$LINENO}: ***" >&5
$as_echo "$as_me: ***" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: *** The ${BFD_DESCRIP} installation on this system appears to be broken." >&5
$as_echo "$as_me: *** The ${BFD_DESCRIP} installation on this system appears to be broken." >&6;}
if test "x${BFD_OPTION}" = "x" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: *** Please correct the installation" >&5
$as_echo "$as_me: *** Please correct the installation" >&6;}
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: *** Either correct the installation, or run configure" >&5
$as_echo "$as_me: *** Either correct the installation, or run configure" >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: *** without explicitly specifying --with-${BFD_OPTION}" >&5
$as_echo "$as_me: *** without explicitly specifying --with-${BFD_OPTION}" >&6;}
fi
exit 1
fi
fi
if test "${USE_CURSES}" != "no"; then
pbxlibdir=""
if test "x${CURSES_DIR}" != "x"; then

@ -232,6 +232,7 @@ AC_SUBST(AST_DEVMODE)
# by the --with option name, to make things easier for the users :-)
AST_EXT_LIB_SETUP([ALSA], [Advanced Linux Sound Architecture], [asound])
AST_EXT_LIB_SETUP([BFD], [Debug symbol decoding], [bfd])
AST_EXT_LIB_SETUP([CAP], [POSIX 1.e capabilities], [cap])
AST_EXT_LIB_SETUP([CURSES], [curses], [curses])
AST_EXT_LIB_SETUP([DAHDI], [DAHDI], [dahdi])
@ -673,11 +674,30 @@ else if test $ac_cv_sizeof_long_long = $ac_cv_sizeof_fd_set_fds_bits; then
AC_DEFINE([TYPEOF_FD_SET_FDS_BITS], [long long], [Define to a type of the same size as fd_set.fds_bits[[0]]])
fi ; fi ; fi
AC_MSG_CHECKING(for dladdr in dlfcn.h)
PBX_DLADDR=0
old_LIBS=${LIBS}
LIBS="${LIBS} -ldl"
AC_LINK_IFELSE(
AC_LANG_PROGRAM([#define _GNU_SOURCE 1
#include <dlfcn.h>],
[dladdr((void *)0, (void *)0)]
),
AC_MSG_RESULT(yes)
PBX_DLADDR=1
AC_SUBST([PBX_DLADDR])
AC_DEFINE([HAVE_DLADDR], 1, [Define to 1 if your system has the dladdr() GNU extension]),
AC_MSG_RESULT(no)
)
LIBS=${old_LIBS}
# do the package library checks now
AST_EXT_LIB_CHECK([ALSA], [asound], [snd_spcm_init], [alsa/asoundlib.h], [-lm -ldl])
AST_EXT_LIB_CHECK([BFD], [bfd], [bfd_openr], [bfd.h])
AST_EXT_LIB_CHECK([CURSES], [curses], [initscr], [curses.h])
if test "x${OSARCH}" = "xlinux-gnu" ; then

@ -58,10 +58,7 @@ even if it is running in the background. This will affect the size
of your log files.
.TP
\fB-f\fR
Do not fork or detach from controlling terminal. Overrides \fB-F\fR.
.TP
\fB-F\fR
Always fork and detach from controlling terminal. Overrides \fB-f\fR.
Do not fork or detach from controlling terminal.
.TP
\fB-g\fR
Remove resource limit on core size, thus forcing Asterisk to dump

@ -74,6 +74,9 @@
attribute. */
#undef HAVE_ATTRIBUTE_warn_unused_result
/* Define to 1 if you have the Debug symbol decoding library. */
#undef HAVE_BFD
/* Define to 1 if you have the `bzero' function. */
#undef HAVE_BZERO
@ -99,6 +102,9 @@
*/
#undef HAVE_DIRENT_H
/* Define to 1 if your system has the dladdr() GNU extension */
#undef HAVE_DLADDR
/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
#undef HAVE_DOPRNT

@ -563,4 +563,13 @@ static void force_inline _ast_assert(int condition, const char *condition_str,
#define ast_assert(a)
#endif
/*!\brief Resolve a binary to a full pathname
* \param binary Name of the executable to resolve
* \param fullpath Buffer to hold the complete pathname
* \param fullpath_size Size of \a fullpath
* \retval NULL \a binary was not found or the environment variable PATH is not set
* \return \a fullpath
*/
char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size);
#endif /* _ASTERISK_UTILS_H */

@ -50,6 +50,10 @@ else
AST_LIBS+=$(EDITLINE_LIB) -lm
endif
ifneq ($(findstring BETTER_BACKTRACES,$(MENUSELECT_CFLAGS)),)
AST_LIBS+=$(BFD_LIB)
endif
ifneq ($(findstring darwin,$(OSARCH)),)
AST_LIBS+=-lresolv
ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)

@ -39,8 +39,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <errno.h>
#include <sys/stat.h>
#if ((defined(AST_DEVMODE)) && (defined(linux)))
#include <execinfo.h>
#define MAX_BACKTRACE_FRAMES 20
# include <execinfo.h>
# define MAX_BACKTRACE_FRAMES 20
# if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
# include <dlfcn.h>
# include <bfd.h>
# endif
#endif
#define SYSLOG_NAMES /* so we can map syslog facilities names to their numeric values,
@ -829,31 +833,160 @@ void ast_log(int level, const char *file, int line, const char *function, const
void ast_backtrace(void)
{
#ifdef linux
#ifdef AST_DEVMODE
int count=0, i=0;
# ifdef AST_DEVMODE
int stackcount = 0, stackfr;
void **addresses;
# if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
bfd *bfdobj; /* bfd.h */
Dl_info dli; /* dlfcn.h */
long allocsize;
asymbol **syms = NULL; /* bfd.h */
bfd_vma offset; /* bfd.h */
const char *lastslash;
asection *section;
const char *file, *func;
unsigned int line;
char address_str[128];
# else
char **strings;
# endif
if ((addresses = ast_calloc(MAX_BACKTRACE_FRAMES, sizeof(*addresses)))) {
count = backtrace(addresses, MAX_BACKTRACE_FRAMES);
if ((strings = backtrace_symbols(addresses, count))) {
ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", count, count != 1 ? 's' : ' ');
for (i=0; i < count ; i++) {
#if __WORDSIZE == 32
ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", i, (unsigned int)addresses[i], strings[i]);
#elif __WORDSIZE == 64
ast_log(LOG_DEBUG, "#%d: [%016lX] %s\n", i, (unsigned long)addresses[i], strings[i]);
#endif
stackcount = backtrace(addresses, MAX_BACKTRACE_FRAMES);
# if defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES)
ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", stackcount, stackcount != 1 ? 's' : ' ');
for (stackfr = 0; stackfr < stackcount; stackfr++) {
int found = 0, symbolcount;
if (!dladdr(addresses[stackfr], &dli)) {
continue;
}
if (strcmp(dli.dli_fname, "asterisk") == 0) {
char asteriskpath[256];
if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) {
/* This will fail to find symbols */
ast_log(LOG_DEBUG, "Failed to find asterisk binary for debug symbols.\n");
dli.dli_fname = "asterisk";
}
}
lastslash = strrchr(dli.dli_fname, '/');
if ( (bfdobj = bfd_openr(dli.dli_fname, NULL)) &&
bfd_check_format(bfdobj, bfd_object) &&
(allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 &&
(syms = ast_malloc(allocsize)) &&
(symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) {
if (bfdobj->flags & DYNAMIC) {
offset = addresses[stackfr] - dli.dli_fbase;
} else {
offset = addresses[stackfr] - (void *) 0;
}
for (section = bfdobj->sections; section; section = section->next) {
if ( !bfd_get_section_flags(bfdobj, section) & SEC_ALLOC ||
section->vma > offset ||
section->size + section->vma < offset) {
continue;
}
if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) {
continue;
}
/* Stack trace output */
found++;
lastslash = strrchr(file, '/');
# if __WORDSIZE == 32
if (dli.dli_saddr == NULL) {
address_str[0] = '\0';
} else {
snprintf(address_str, sizeof(address_str), " (%08lX+%lX)",
(unsigned long) dli.dli_saddr,
(unsigned long) (addresses[stackfr] - dli.dli_saddr));
}
ast_log(LOG_DEBUG, "#%d: [%08lX] %s:%u %s()%s\n", stackfr,
(unsigned long) addresses[stackfr],
lastslash ? lastslash + 1 : file, line,
S_OR(func, "???"),
address_str);
# elif __WORDSIZE == 64
if (dli.dli_saddr == NULL) {
address_str[0] = '\0';
} else {
snprintf(address_str, sizeof(address_str), " (%016lX+%lX)",
(unsigned long) dli.dli_saddr,
(unsigned long) (addresses[stackfr] - dli.dli_saddr));
}
ast_log(LOG_DEBUG, "#%d: [%016lX] %s:%u %s()%s\n", stackfr,
(unsigned long) addresses[stackfr],
lastslash ? lastslash + 1 : file, line,
S_OR(func, "???"),
address_str);
# endif
break;
}
}
if (bfdobj) {
bfd_close(bfdobj);
if (syms) {
ast_free(syms);
}
}
/* Default output, if we cannot find the information within BFD */
if (!found) {
# if __WORDSIZE == 32
if (dli.dli_saddr == NULL) {
address_str[0] = '\0';
} else {
snprintf(address_str, sizeof(address_str), " (%08lX+%lX)",
(unsigned long) dli.dli_saddr,
(unsigned long) (addresses[stackfr] - dli.dli_saddr));
}
ast_log(LOG_DEBUG, "#%d: [%08lX] %s %s()%s\n", stackfr,
(unsigned long) addresses[stackfr],
lastslash ? lastslash + 1 : dli.dli_fname,
S_OR(dli.dli_sname, "<unknown>"),
address_str);
# elif __WORDSIZE == 64
if (dli.dli_saddr == NULL) {
address_str[0] = '\0';
} else {
snprintf(address_str, sizeof(address_str), " (%016lX+%lX)",
(unsigned long) dli.dli_saddr,
(unsigned long) (addresses[stackfr] - dli.dli_saddr));
}
ast_log(LOG_DEBUG, "#%d: [%016lX] %s %s()%s\n", stackfr,
(unsigned long) addresses[stackfr],
lastslash ? lastslash + 1 : dli.dli_fname,
S_OR(dli.dli_sname, "<unknown>"),
address_str);
# endif
}
}
# else /* !defined(HAVE_DLADDR) || !defined(HAVE_BFD) || !defined(BETTER_BACKTRACES) */
if ((strings = backtrace_symbols(addresses, stackcount))) {
ast_log(LOG_DEBUG, "Got %d backtrace record%c\n", stackcount, stackcount != 1 ? 's' : ' ');
for (stackfr = 0; stackfr < stackcount ; stackfr++) {
# if __WORDSIZE == 32
ast_log(LOG_DEBUG, "#%d: [%08X] %s\n", stackfr, (unsigned int)addresses[stackfr], strings[stackfr]);
# elif __WORDSIZE == 64
ast_log(LOG_DEBUG, "#%d: [%016lX] %s\n", stackfr, (unsigned long)addresses[stackfr], strings[stackfr]);
# endif
}
free(strings);
} else {
ast_log(LOG_DEBUG, "Could not allocate memory for backtrace\n");
}
# endif /* defined(HAVE_DLADDR) && defined(HAVE_BFD) && defined(BETTER_BACKTRACES) */
free(addresses);
}
#else
# else /* !defined(AST_DEVMODE) */
ast_log(LOG_WARNING, "Must run configure with '--enable-dev-mode' for stack backtraces.\n");
#endif
# endif /* defined(AST_DEVMODE) */
#else /* ndef linux */
ast_log(LOG_WARNING, "Inline stack backtraces are only available on the Linux platform.\n");
#endif

@ -38,6 +38,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#define AST_API_MODULE /* ensure that inlinable API functions will be built in lock.h if required */
#include "asterisk/lock.h"
@ -1441,3 +1442,22 @@ int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, co
return res;
}
#endif
char *ast_utils_which(const char *binary, char *fullpath, size_t fullpath_size)
{
const char *envPATH = getenv("PATH");
char *tpath, *path;
struct stat unused;
if (!envPATH) {
return NULL;
}
tpath = ast_strdupa(envPATH);
while ((path = strsep(&tpath, ":"))) {
snprintf(fullpath, fullpath_size, "%s/%s", path, binary);
if (!stat(fullpath, &unused)) {
return fullpath;
}
}
return NULL;
}

@ -73,6 +73,9 @@ AST_FORTIFY_SOURCE=@AST_FORTIFY_SOURCE@
ASOUND_INCLUDE=@ALSA_INCLUDE@
ASOUND_LIB=@ALSA_LIB@
BFD_INCLUDE=@BFD_INCLUDE@
BFD_LIB=@BFD_LIB@
CURL_INCLUDE=@CURL_INCLUDE@
CURL_LIB=@CURL_LIB@

Loading…
Cancel
Save