From a5ba7ad71203a7ff1b3a716e7171e919a2e2e5bb Mon Sep 17 00:00:00 2001 From: Alexander Golubev Date: Sun, 25 Aug 2013 14:46:19 +0200 Subject: [PATCH] Improved creation backtraces --- CMakeLists.txt | 37 +++- config.h.cmake | 67 ++++++- tdecore/CMakeLists.txt | 1 + tdecore/kdebug.cpp | 281 ++++++++++++++++++++++++--- tdecore/kdebug.h | 20 +- tdeio/tdeio/CMakeLists.txt | 6 - tdeio/tdeio/backtrace_symbols.c | 325 -------------------------------- tdeio/tdeio/slavebase.cpp | 46 ++--- 8 files changed, 385 insertions(+), 398 deletions(-) delete mode 100644 tdeio/tdeio/backtrace_symbols.c diff --git a/CMakeLists.txt b/CMakeLists.txt index ba552432a..7700f7391 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ OPTION( WITH_NETWORK_MANAGER_BACKEND "Enable network-manager support" OFF ) OPTION( WITH_SUDO_TDESU_BACKEND "Use sudo as backend for tdesu (default is su)" OFF ) OPTION( WITH_OLD_XDG_STD "Use the pre R14.0.0 XDG standard where both TDE and KDE are recognized in desktop files" OFF ) OPTION( WITH_LZMA "Enable support for LZMA/XZ" ${WITH_ALL_OPTIONS} ) +OPTION( WITH_LIBBFD "Enable pretty backtraces with libbfd from GNU binutils" OFF ) OPTION( WITH_KDE4_MENU_SUFFIX "Add [KDE4] tag to KDE4 menu items" OFF ) OPTION( WITH_ASPELL "Enable aspell support" ${WITH_ALL_OPTIONS} ) @@ -204,7 +205,6 @@ check_include_file( "termio.h" HAVE_TERMIO_H ) check_include_file( "unistd.h" HAVE_UNISTD_H ) check_include_file( "util.h" HAVE_UTIL_H ) check_include_file( "values.h" HAVE_VALUES_H ) -check_include_file( "demangle.h" HAVE_DEMANGLE_H ) # FIXME I'm not sure if test TIME_WITH_SYS_TIME are correct check_include_file( "sys/time.h" HAVE_SYS_TIME_H ) @@ -272,10 +272,33 @@ check_symbol_exists( gethostbyname_r "netdb.h" HAVE_GETHOSTBYNAME_R ) check_symbol_exists( gai_strerror "sys/types.h;sys/socket.h;netdb.h" HAVE_GAI_STRERROR ) check_symbol_exists( getaddrinfo "sys/types.h;sys/socket.h;netdb.h" HAVE_GETADDRINFO ) check_symbol_exists( backtrace "execinfo.h" HAVE_BACKTRACE ) +check_cxx_source_compiles( "#include + int main() { abi::__cxa_demangle(0, 0, 0, 0); return 0; }" + HAVE_ABI_CXA_DEMANGLE ) + +# Some declarations are needed by demangle.h (libiberty.h) and/or bfd.h but we +# will check them uncondionally +# See WITH_LIBBFD also see binutils bugs 14243 and 14072 +# NOTE: those headers use HAVE_DECL_* names but we would follow our macro +# naming style +check_symbol_exists( basename "libgen.h" HAVE_BASENAME_PROTO ) +check_symbol_exists( ffs "string.h" HAVE_FFS_PROTO ) +check_symbol_exists( asprintf "stdio.h" HAVE_ASPRINTF_PROTO ) +check_symbol_exists( vasprintf "stdio.h" HAVE_VASPRINTF_PROTO ) +check_symbol_exists( snprintf "stdio.h" HAVE_SNPRINTF_PROTO ) +check_symbol_exists( vsnprintf "stdarg.h" HAVE_VSNPRINTF_PROTO ) +check_symbol_exists( strvercmp "string.h" HAVE_STRVERCMP_PROTO ) + check_function_exists( usleep HAVE_USLEEP ) check_symbol_exists( usleep "unistd.h" HAVE_USLEEP_PROTO ) +if (HAVE_ALLOCA_H ) + check_symbol_exists( alloca "alloca.h" HAVE_ALLOCA ) +else (HAVE_ALLOCA_H ) + check_symbol_exists( alloca "stdlib.h" HAVE_ALLOCA ) +endif (HAVE_ALLOCA_H ) + check_function_exists( getmntinfo HAVE_GETMNTINFO ) check_function_exists( getnameinfo HAVE_GETNAMEINFO ) check_function_exists( getpagesize HAVE_GETPAGESIZE ) @@ -331,7 +354,6 @@ check_function_exists( getcwd HAVE_GETCWD ) check_function_exists( dlerror HAVE_DLERROR ) check_function_exists( crypt HAVE_CRYPT ) check_function_exists( bcopy HAVE_BCOPY ) -check_function_exists( alloca HAVE_ALLOCA ) check_function_exists( mmap HAVE_MMAP ) check_function_exists( munmap HAVE_MUNMAP ) @@ -703,6 +725,17 @@ if( WITH_LIBIDN ) check_include_file( "stringprep.h" HAVE_STRINGPREP_H ) endif( WITH_LIBIDN ) +##### check for libbfq ########################## + +if( WITH_LIBBFD ) + check_library_exists( bfd bfd_init "" HAVE_LIBBFD ) + if( NOT HAVE_LIBBFD ) + tde_message_fatal( "libbfd support is requested, but not found on your system" ) + endif( NOT HAVE_LIBBFD ) + set( LIBBFD_LIBRARIES bfd ) + check_include_file( "demangle.h" HAVE_DEMANGLE_H ) +endif( WITH_LIBBFD ) + ##### check for openssl ######################### diff --git a/config.h.cmake b/config.h.cmake index a0e3e92fd..d9703caa4 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -47,9 +47,74 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_AWE_VOICE_H 1 -/* Define if execinfo.h exists and defines backtrace (GLIBC >= 2.1) */ +/* Define if you have basename prototype */ +#cmakedefine HAVE_BASENAME_PROTO 1 + +/* Define if you have ffs prototype */ +#cmakedefine HAVE_FFS_PROTO 1 + +/* Define if you have asprintf prototype */ +#cmakedefine HAVE_ASPRINTF_PROTO 1 + +/* Define if you have vasprintf prototype */ +#cmakedefine HAVE_VASPRINTF_PROTO 1 + +/* Define if you have snsprintf prototype */ +#cmakedefine HAVE_SNPRINTF_PROTO 1 + +/* Define if you have vsnsprintf prototype */ +#cmakedefine HAVE_VSNPRINTF_PROTO 1 + +/* Define if you have strvercmp prototype */ +#cmakedefine HAVE_STRVERCMP_PROTO 1 + +/* Define to 1 if execinfo.h exists and defines backtrace (GLIBC >= 2.1) */ #cmakedefine HAVE_BACKTRACE 1 +/* Define to 1 if gcc (or may be some over compiller) provides abi::__cxa_demangle() */ +#cmakedefine HAVE_ABI_CXA_DEMANGLE 1 + +/* Define to 1 if compiled with libbfd support */ +#cmakedefine WITH_LIBBFD 1 + +#ifdef WITH_LIBBFD +#cmakedefine HAVE_DECL_BASENAME 1 +/* Some declarations are needed by demangle.h (libiberty.h) and/or bfd.h */ +/* those heders use HAVE_DECL_* format but we decided to follow our macro style */ + +#ifdef HAVE_BASENAME_PROTO +#define HAVE_DECL_BASENAME 1 +#endif /* HAVE_BASENAME_PROTO */ + +#ifdef HAVE_FFS_PROTO +#define HAVE_DECL_FFS 1 +#endif /* HAVE_FFS_PROTO */ + +#ifdef HAVE_ASPRINTF_PROTO +#define HAVE_DECL_ASPRINTF 1 +#endif /* HAVE_ASPRINTF_PROTO */ + +#ifdef HAVE_VASPRINTF_PROTO +#define HAVE_DECL_VASPRINTF 1 +#endif /* HAVE_VASPRINTF_PROTO */ + +#ifdef HAVE_SNPRINTF_PROTO +#define HAVE_DECL_SNPRINTF 1 +#endif /* HAVE_SNPRINTF_PROTO */ + +#ifdef HAVE_VSNPRINTF_PROTO +#define HAVE_DECL_VSNPRINTF 1 +#endif /* HAVE_VSNPRINTF_PROTO */ + +#ifdef HAVE_STRVERCMP_PROTO +#define HAVE_DECL_STRVERCMP 1 +#endif /* HAVE_STRVERCMP_PROTO */ + +#endif /* HAVE_BASENAME_PROTO */ + +/* Define to 1 if libbfd provides demangle.h header */ +#cmakedefine HAVE_DEMANGLE_H 1 + /* Define to 1 if you have the `bcopy' function. */ #cmakedefine HAVE_BCOPY 1 diff --git a/tdecore/CMakeLists.txt b/tdecore/CMakeLists.txt index 6a9e605a3..f1e8f3e60 100644 --- a/tdecore/CMakeLists.txt +++ b/tdecore/CMakeLists.txt @@ -134,6 +134,7 @@ tde_add_library( ${target} SHARED AUTOMOC EMBED tdecorenetwork-static tdehw-static LINK ltdlc-static ${KDESVGICONS} DCOP-shared tdefx-shared ${ZLIB_LIBRARIES} ${LIBIDN_LIBRARIES} ${XCOMPOSITE_LIBRARIES} ICE SM ${GAMIN_LIBRARIES} + ${LIBBFD_LIBRARIES} DEPENDENCIES dcopidl dcopidl2cpp DESTINATION ${LIB_INSTALL_DIR} ) diff --git a/tdecore/kdebug.cpp b/tdecore/kdebug.cpp index d7b0372a6..5df09fbac 100644 --- a/tdecore/kdebug.cpp +++ b/tdecore/kdebug.cpp @@ -22,7 +22,6 @@ #ifdef NDEBUG #undef kdDebug -#undef kdBacktrace #endif #include "kdebugdcopiface.h" @@ -54,15 +53,42 @@ #include // isprint #include #include -#include +#include #include #include "kstaticdeleter.h" #include #ifdef HAVE_BACKTRACE #include + +#ifdef HAVE_ABI_CXA_DEMANGLE +#include #endif +#include +#ifdef WITH_LIBBFD +/* newer versions of libbfd require some autotools-specific macros to be defined */ +/* see binutils Bug 14243 and 14072 */ +#define PACKAGE tdelibs +#define PACKAGE_VERSION TDE_VERSION + +#include + +#ifdef HAVE_DEMANGLE_H +#include +#endif // HAVE_DEMANGLE_H +#endif // WITH_LIBBFD + +#endif // HAVE_BACKTRACE + +#ifdef HAVE_ALLOCA_H +#include +#endif // HAVE_ALLOCA_H + +#ifdef HAVE_STDINT_H +#include +#endif // HAVE_STDINT_H + class KDebugEntry; class KDebugEntry @@ -556,36 +582,247 @@ kdbgstream& kdbgstream::operator<<( const TQByteArray& data) { return *this; } +#ifdef HAVE_BACKTRACE +struct BacktraceFunctionInfo { + const void *addr; //< the address of function returned by backtrace() + const char* fileName; //< the file of binary owning the function (e.g. shared library or current header) + const void *base; //< the base address there the binary is loaded to + uintptr_t offset; //< offset of the function in binary (base - address) + TQString functionName; //< mangled name of function + TQString prettyName; //< demangled name of function + TQString sourceName; //< name of source file function declared in + unsigned sourceLine; //< line where function defined +}; + +#ifdef WITH_LIBBFD + +// load symbol table from file +asymbol** bfdLoadSymtab (bfd *abfd) { + long symCount; // count of entries in symbol table + long symtab_sz; // size of the table + asymbol** rv; + bfd_boolean dynamic = FALSE; + + // make shure the file has symbol table + if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0){ + return 0; + } + + // determin the amount of space we'll need to store the table + symtab_sz = bfd_get_symtab_upper_bound (abfd); + if (symtab_sz == 0) { + symtab_sz = bfd_get_dynamic_symtab_upper_bound (abfd); + dynamic = TRUE; + } + if (symtab_sz < 0) { + return 0; + } + + // allocate memory + rv = (asymbol **) malloc(symtab_sz); // dunno, why not malloc + if ( !rv ) { + return 0; + } + + // actually load the table + if (dynamic) { + symCount = bfd_canonicalize_dynamic_symtab (abfd, rv); + } else { + symCount = bfd_canonicalize_symtab (abfd, rv); + } + + if (symCount < 0) { + if (rv) { + free(rv); + } + return 0; + } + + return rv; +} + +void bfdFillAdditionalFunctionsInfo(BacktraceFunctionInfo &func) { + static bool inited=0; + if (!inited) { + bfd_init(); + inited=1; + } + + bfd *abfd = bfd_openr(func.fileName, 0); // a bfd object + if( !abfd ) { + return; + } + + // check format of the object + if( !bfd_check_format(abfd, bfd_object) ) { + bfd_close(abfd); + return; + } + + // load symbol table + asymbol **syms= bfdLoadSymtab(abfd); + if(!syms) { + bfd_close(abfd); + return; + } + + // found source file and line for given address + for (asection *sect = abfd->sections; sect != NULL; sect = sect->next) { + + if (bfd_get_section_flags(abfd, sect) & SEC_ALLOC) { + bfd_vma sectStart = bfd_get_section_vma(abfd, sect); + bfd_vma sectEnd = sectStart + bfd_section_size(abfd, sect); + if (sectStart <= func.offset && func.offset < sectEnd) { + bfd_vma sectOffset = func.offset - sectStart; + const char* functionName; + const char* sourceName; + unsigned sourceLine; + if (bfd_find_nearest_line(abfd, sect, syms, sectOffset, + &sourceName, &functionName, &sourceLine)) + { + func.sourceName = sourceName; + func.sourceLine = sourceLine; + if(func.functionName.isEmpty()) { + func.functionName = TQString::fromAscii(functionName); + } + break; + } + } + } + } +#ifdef HAVE_DEMANGLE_H + if(func.prettyName.isEmpty() && !func.functionName.isEmpty()) { + char *demangled = bfd_demangle(abfd, func.functionName.ascii(), DMGL_AUTO | DMGL_PARAMS); + if (demangled) { + func.prettyName = demangled; + free(demangled); + } + } +#endif // HAVE_DEMANGLE_H + + if( syms ) { + free(syms); + } + bfd_close(abfd); +} + +#endif // WITH_LIBBFD + +void fillAdditionalFunctionsInfo(BacktraceFunctionInfo &func) { +#ifdef WITH_LIBBFD + bfdFillAdditionalFunctionsInfo(func); +#endif // WITH_LIBBFD + +#ifdef HAVE_ABI_CXA_DEMANGLE + if(func.prettyName.isEmpty() && !func.functionName.isEmpty()) { + int status=0; + char *demangled = abi::__cxa_demangle(func.functionName.ascii(), 0, 0, &status); + if (demangled) { + func.prettyName = demangled; + free(demangled); + } + } +#endif // HAVE_ABI_CXA_DEMANGLE + +} + +TQString formatBacktrace(void *addr) { + TQString rv; + BacktraceFunctionInfo func; + func.addr = addr; + + // NOTE: if somebody would compile for some non-linux-glibc platform + // check if dladdr function is avalible there + Dl_info info; + dladdr(func.addr, &info); // obtain information about the function. + + func.fileName = info.dli_fname; + func.base = info.dli_fbase; + func.offset = (uintptr_t)func.addr - (uintptr_t)func.base; + func.functionName = TQString::fromAscii(info.dli_sname); + func.sourceLine = 0; + + fillAdditionalFunctionsInfo(func); + + rv.sprintf("0x%0*lx", (int) sizeof(void*)*2, (uintptr_t) func.addr); + + rv += " in "; + if (!func.prettyName.isEmpty()) { + rv += func.prettyName; + } else if (!func.functionName.isEmpty()) { + rv += func.functionName; + } else { + rv += "??"; + } + + if (!func.sourceName.isEmpty()) { + rv += " in "; + rv += func.sourceName; + rv += ":"; + rv += func.sourceLine ? TQString::number(func.sourceLine) : "??"; + } else if (func.fileName && func.fileName[0]) { + rv += TQString().sprintf(" from %s:0x%08lx",func.fileName, func.offset); + } else { + rv += " from ??"; + } + + return rv; +} +#endif // HAVE_BACKTRACE + + TQString kdBacktrace(int levels) { - TQString s; + TQString rv; #ifdef HAVE_BACKTRACE - void* trace[256]; - int n = backtrace(trace, 256); - if (!n) - return s; - char** strings = backtrace_symbols (trace, n); - - if ( levels != -1 ) - n = QMIN( n, levels ); - s = "[\n"; - - for (int i = 0; i < n; ++i) - s += TQString::number(i) + - TQString::fromLatin1(": ") + - TQString::fromLatin1(strings[i]) + TQString::fromLatin1("\n"); - s += "]\n"; - if (strings) - free (strings); -#endif - return s; + if (levels < 0 || levels > 256 ) { + levels = 256; + } + + rv = "[\n"; + + if (levels) { +#ifdef HAVE_ALLOCA + void** trace = (void**)alloca(levels * sizeof(void*)); +#else // HAVE_ALLOCA + void* trace[256]; +#endif // HAVE_ALLOCA + levels = backtrace(trace, levels); + + if (levels) { + for (int i = 0; i < levels; ++i) { + rv += QString().sprintf("#%-2d ", i); + rv += formatBacktrace(trace[i]); + rv += '\n'; + } + } else { + rv += "backtrace() failed\n"; + } + } + + rv += "]\n"; +#endif // HAVE_BACKTRACE + return rv; } +// Keep for ABI compatability for some time +// FIXME remove this (2013-08-18, 18:09, Fat-Zer) TQString kdBacktrace() { return kdBacktrace(-1 /*all*/); } +void kdBacktraceFD(int fd) { +#ifdef HAVE_BACKTRACE + void *trace[256]; + int levels; + + levels = backtrace(trace, 256); + if (levels) { + backtrace_symbols_fd(trace, levels, fd); + } +#endif // HAVE_BACKTRACE +} void kdClearDebugConfig() { if (kDebug_data) { diff --git a/tdecore/kdebug.h b/tdecore/kdebug.h index d8a1998f9..46af54e97 100644 --- a/tdecore/kdebug.h +++ b/tdecore/kdebug.h @@ -601,17 +601,21 @@ TDECORE_EXPORT kdbgstream kdDebug(bool cond, int area = 0); /** * \relates TDEGlobal * Returns a backtrace. + * @param levels the number of levels of the backtrace. Defauls to -1 (as much as avalible). * @return a backtrace + * @since 3.1 */ -TDECORE_EXPORT TQString kdBacktrace(); +TDECORE_EXPORT TQString kdBacktrace(int levels=-1); /** * \relates TDEGlobal - * Returns a backtrace. - * @param levels the number of levels of the backtrace - * @return a backtrace - * @since 3.1 + * Writes a backtrace to the given file descriptor. In contrast to + * kdBacktrace, this function doesn't call any malloc(). So it supposed to be + * used in situations than any extra memmmory allocation may lead to yet + * another crash. As a limitation it doesn't produce any symbol demangling. + * @param fd a file descriptor to write to. Defaults to 2 (stderr) + * @since 14.0 */ -TDECORE_EXPORT TQString kdBacktrace(int levels); +TDECORE_EXPORT void kdBacktraceFD(int fd=2); /** * Returns a dummy debug stream. The stream does not print anything. * @param area an id to identify the output, 0 for default @@ -619,9 +623,6 @@ TDECORE_EXPORT TQString kdBacktrace(int levels); */ inline kndbgstream kndDebug(int area = 0) { Q_UNUSED(area); return kndbgstream(); } inline kndbgstream kndDebug(bool , int = 0) { return kndbgstream(); } -inline TQString kndBacktrace() { return TQString::null; } -inline TQString kndBacktrace(int) { return TQString::null; } - /** * \relates TDEGlobal * Returns a warning stream. You can use it to print warning @@ -658,7 +659,6 @@ TDECORE_EXPORT void kdClearDebugConfig(); #ifdef NDEBUG #define kdDebug kndDebug -#define kdBacktrace kndBacktrace #endif #endif diff --git a/tdeio/tdeio/CMakeLists.txt b/tdeio/tdeio/CMakeLists.txt index cf5c272a9..e2b96ebb1 100644 --- a/tdeio/tdeio/CMakeLists.txt +++ b/tdeio/tdeio/CMakeLists.txt @@ -84,15 +84,9 @@ set( ${target}_SRCS kmimetypechooser.cpp ) -if( HAVE_BACKTRACE AND HAVE_DEMANGLE_H ) - list( APPEND ${target}_SRCS backtrace_symbols.c ) - set( BACKTRACE_LIBRARY bfd ) -endif( ) - tde_add_library( ${target} STATIC_PIC AUTOMOC SOURCES ${${target}_SRCS} DEPENDENCIES dcopidl - LINK ${BACKTRACE_LIBRARY} ) diff --git a/tdeio/tdeio/backtrace_symbols.c b/tdeio/tdeio/backtrace_symbols.c deleted file mode 100644 index 69aa2d740..000000000 --- a/tdeio/tdeio/backtrace_symbols.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - A hacky replacement for backtrace_symbols in glibc - - backtrace_symbols in glibc looks up symbols using dladdr which is limited in - the symbols that it sees. libbacktracesymbols opens the executable and shared - libraries using libbfd and will look up backtrace information using the symbol - table and the dwarf line information. - - It may make more sense for this program to use libelf instead of libbfd. - However, I have not investigated that yet. - - Derived from addr2line.c from GNU Binutils by Jeff Muizelaar - - Copyright 2007 Jeff Muizelaar - Copyright 2012 Timothy Pearson -*/ - -/* addr2line.c -- convert addresses to line number and function name - Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. - Contributed by Ulrich Lauther - - This file was part of GNU Binutils. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#define fatal(a, b) exit(1) -#define bfd_fatal(a) exit(1) -#define bfd_nonfatal(a) exit(1) -#define list_matching_formats(a) exit(1) - -/* 2 characters for each byte, plus 1 each for 0, x, and NULL */ -#define PTRSTR_LEN (sizeof(void *) * 2 + 3) -#define true 1 -#define false 0 - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -static asymbol **syms; /* Symbol table. */ - -/* 150 isn't special; it's just an arbitrary non-ASCII char value. */ -#define OPTION_DEMANGLER (150) - -static void slurp_symtab(bfd * abfd); -static void find_address_in_section(bfd *abfd, asection *section, void *data); - -/* Read in the symbol table. */ - -static void slurp_symtab(bfd * abfd) -{ - long symcount; - unsigned int size; - - if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) - return; - - symcount = (long)bfd_read_minisymbols(abfd, false, (void**) & syms, &size); - if (symcount == 0) - symcount = bfd_read_minisymbols(abfd, true /* dynamic */ , - (void**) & syms, &size); - - if (symcount < 0) - bfd_fatal(bfd_get_filename(abfd)); -} - -/* These global variables are used to pass information between - translate_addresses and find_address_in_section. */ - -static bfd_vma pc; -static const char *filename; -static const char *functionname; -static unsigned int line; -static int found; - -/* Look for an address in a section. This is called via - bfd_map_over_sections. */ - -static void find_address_in_section(bfd *abfd, asection *section, void *data __attribute__ ((__unused__)) ) -{ - bfd_vma vma; - bfd_size_type size; - - if (found) - return; - - if ((bfd_get_section_flags(abfd, section) & SEC_ALLOC) == 0) - return; - - vma = bfd_get_section_vma(abfd, section); - if (pc < vma) - return; - - size = bfd_section_size(abfd, section); - if (pc >= vma + size) - return; - - found = bfd_find_nearest_line(abfd, section, syms, pc - vma, - &filename, &functionname, &line); -} - -/* Read hexadecimal addresses from stdin, translate into - file_name:line_number and optionally function name. */ - -enum { Count, Print }; - -static char** translate_addresses_buf(bfd * abfd, bfd_vma *addr, int naddr) -{ - int naddr_orig = naddr; - char b; - int total = 0; - int state; - char *buf = &b; - int len = 0; - char **ret_buf = NULL; - - /* iterate over the formating twice. - * the first time we count how much space we need - * the second time we do the actual printing */ - for (state=Count; state<=Print; state++) { - if (state == Print) { - ret_buf = (char**)malloc(total + sizeof(char*)*naddr); - buf = (char*)(ret_buf + naddr); - len = total; - } - while (naddr) { - if (state == Print) - ret_buf[naddr-1] = buf; - pc = addr[naddr-1]; - - found = false; - bfd_map_over_sections(abfd, find_address_in_section, - (PTR) NULL); - - if (!found) { - total += snprintf(buf, len, "[0x%llx] \?\?() \?\?:0",(long long unsigned int) addr[naddr-1]) + 1; - } else { - const char *name; - - name = functionname; - if (name == NULL || *name == '\0') - name = "??"; - if (filename != NULL) { - const char *h; - - h = strrchr(filename, '/'); - if (h != NULL) - filename = h + 1; - } - - char* funcname = cplus_demangle(name, DMGL_AUTO); - if (funcname) { - // demangling succeeded - total += snprintf(buf, len, "%s:%u\t%s()", filename ? filename : "??", line, funcname) + 1; - } - else { - // demangling failed - total += snprintf(buf, len, "%s:%u\t%s()", filename ? filename : "??", line, name) + 1; - } - } - if (state == Print) { - /* set buf just past the end of string */ - buf = buf + total + 1; - } - naddr--; - } - naddr = naddr_orig; - } - return ret_buf; -} -/* Process a file. */ - -static char **process_file(const char *file_name, bfd_vma *addr, int naddr) -{ - bfd *abfd; - char **matching; - char **ret_buf; - - abfd = bfd_openr(file_name, NULL); - - if (abfd == NULL) - bfd_fatal(file_name); - - if (bfd_check_format(abfd, bfd_archive)) - fatal("%s: can not get addresses from archive", file_name); - - if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { - bfd_nonfatal(bfd_get_filename(abfd)); - if (bfd_get_error() == - bfd_error_file_ambiguously_recognized) { - list_matching_formats(matching); - free(matching); - } - xexit(1); - } - - slurp_symtab(abfd); - - ret_buf = translate_addresses_buf(abfd, addr, naddr); - - if (syms != NULL) { - free(syms); - syms = NULL; - } - - bfd_close(abfd); - return ret_buf; -} - -#define MAX_DEPTH 16 - -struct file_match { - const char *file; - void *address; - void *base; - void *hdr; -}; - -static int find_matching_file(struct dl_phdr_info *info, - size_t size, void *data) -{ - struct file_match *match = data; - /* This code is modeled from Gfind_proc_info-lsb.c:callback() from libunwind */ - long n; - const ElfW(Phdr) *phdr; - ElfW(Addr) load_base = info->dlpi_addr; - phdr = info->dlpi_phdr; - for (n = info->dlpi_phnum; --n >= 0; phdr++) { - if (phdr->p_type == PT_LOAD) { - ElfW(Addr) vaddr = phdr->p_vaddr + load_base; - if (match->address >= (void*)vaddr && match->address < (void*)(vaddr + phdr->p_memsz)) { - /* we found a match */ - match->file = info->dlpi_name; - match->base = (void*)info->dlpi_addr; - } - } - } - return 0; -} - -char **backtrace_symbols(void *const *buffer, int size) -{ - int stack_depth = size - 1; - int x,y; - /* discard calling function */ - int total = 0; - - char ***locations; - char **final; - char *f_strings; - - locations = (char***)malloc(sizeof(char**) * (stack_depth+1)); - - bfd_init(); - for(x=stack_depth, y=0; x>=0; x--, y++){ - struct file_match match = { .address = buffer[x] }; - char **ret_buf; - bfd_vma addr; - dl_iterate_phdr(find_matching_file, &match); - addr = buffer[x] - match.base; - if (match.file && strlen(match.file)) - ret_buf = process_file(match.file, &addr, 1); - else - ret_buf = process_file("/proc/self/exe", &addr, 1); - locations[x] = ret_buf; - total += strlen(ret_buf[0]) + 1; - } - - /* allocate the array of char* we are going to return and extra space for - * all of the strings */ - final = (char**)malloc(total + (stack_depth + 1) * sizeof(char*)); - /* get a pointer to the extra space */ - f_strings = (char*)(final + stack_depth + 1); - - /* fill in all of strings and pointers */ - for(x=stack_depth; x>=0; x--){ - strcpy(f_strings, locations[x][0]); - free(locations[x]); - final[x] = f_strings; - f_strings += strlen(f_strings) + 1; - } - - free(locations); - - return final; -} - -void -backtrace_symbols_fd(void *const *buffer, int size, int fd) -{ - int j; - char **strings; - - strings = backtrace_symbols(buffer, size); - if (strings == NULL) { - perror("backtrace_symbols"); - exit(EXIT_FAILURE); - } - - for (j = 0; j < size; j++) - printf("%s\n", strings[j]); - - free(strings); -} - diff --git a/tdeio/tdeio/slavebase.cpp b/tdeio/tdeio/slavebase.cpp index 83b2a9889..e7989ec6b 100644 --- a/tdeio/tdeio/slavebase.cpp +++ b/tdeio/tdeio/slavebase.cpp @@ -60,35 +60,6 @@ #include "uiserver_stub.h" -#ifndef NDEBUG -#ifdef HAVE_BACKTRACE -#include -#endif -#endif - -#ifndef NDEBUG -void print_trace() -{ -#if defined(HAVE_BACKTRACE) && defined(HAVE_DEMANGLE_H) - void *array[10]; - size_t size; - char **strings; - size_t i; - - size = backtrace (array, 10); - strings = backtrace_symbols (array, size); - - printf ("[tdeioslave] Obtained %zd stack frames.\n\r", size); - - for (i = 0; i < size; i++) { - printf ("[tdeioslave] %s\n\r", strings[i]); - } - - free (strings); -#endif // defined(HAVE_BACKTRACE) && defined(HAVE_DEMANGLE_H) -} -#endif // NDEBUG - using namespace TDEIO; template class TQPtrList >; @@ -764,9 +735,20 @@ void SlaveBase::sigsegv_handler(int sig) char buffer[120]; snprintf(buffer, sizeof(buffer), "tdeioslave: ####### CRASH ###### protocol = %s pid = %d signal = %d\n", s_protocol, getpid(), sig); write(2, buffer, strlen(buffer)); -#ifndef NDEBUG - print_trace(); -#endif +#ifdef SECURE_DEBUG + kdBacktraceFD(); +#else // SECURE_DEBUG + // Screw the malloc issue! We want nice demangled backtrace! + // Anyway we are not supposed to go into infinite loop because next signal + // will kill us. If you are unlucky and there is a second crash during + // backtrase in your system, you can define SECURE_DEBUG to avoid it + + // Extra sync here so we are sure even if the backtrace will fail + // we will pass at least some crash message. + fsync(2); + TQString backtrace = kdBacktrace(); + write(2, backtrace.ascii(), backtrace.length()); +#endif // SECURE_DEBUG ::exit(1); #endif }