summaryrefslogtreecommitdiffstats
path: root/kdecore
diff options
context:
space:
mode:
authorAlexander Golubev <fatzer2@gmail.com>2013-08-25 14:46:19 +0200
committerSlávek Banko <slavek.banko@axis.cz>2015-12-23 02:22:29 +0100
commit918ff4b4ae94c71f5cc5fff755a3ace1325ec816 (patch)
tree143e8020c338331bfc196ef55011ae1dce63cf5c /kdecore
parent5bb41966dbc963d39c9660de7c9bbd7bcc2b9553 (diff)
downloadtdelibs-918ff4b4ae94c71f5cc5fff755a3ace1325ec816.tar.gz
tdelibs-918ff4b4ae94c71f5cc5fff755a3ace1325ec816.zip
Improved creation backtraces
(cherry picked from commit a5ba7ad71203a7ff1b3a716e7171e919a2e2e5bb)
Diffstat (limited to 'kdecore')
-rw-r--r--kdecore/CMakeLists.txt1
-rw-r--r--kdecore/kdebug.cpp281
-rw-r--r--kdecore/kdebug.h20
3 files changed, 270 insertions, 32 deletions
diff --git a/kdecore/CMakeLists.txt b/kdecore/CMakeLists.txt
index 2e8d8b593..0ea89a3da 100644
--- a/kdecore/CMakeLists.txt
+++ b/kdecore/CMakeLists.txt
@@ -131,6 +131,7 @@ tde_add_library( ${target} SHARED AUTOMOC
VERSION 4.2.0
EMBED kdecorenetwork-static
LINK ltdlc-static ${KDESVGICONS} DCOP-shared kdefx-shared ${ZLIB_LIBRARIES} ${LIBIDN_LIBRARIES} ${XCOMPOSITE_LIBRARIES} ICE SM
+ ${LIBBFD_LIBRARIES}
DEPENDENCIES dcopidl dcopidl2cpp
DESTINATION ${LIB_INSTALL_DIR}
)
diff --git a/kdecore/kdebug.cpp b/kdecore/kdebug.cpp
index e673d4173..90e94f1b8 100644
--- a/kdecore/kdebug.cpp
+++ b/kdecore/kdebug.cpp
@@ -22,7 +22,6 @@
#ifdef NDEBUG
#undef kdDebug
-#undef kdBacktrace
#endif
#include "kdebugdcopiface.h"
@@ -54,15 +53,42 @@
#include <ctype.h> // isprint
#include <syslog.h>
#include <errno.h>
-#include <string.h>
+#include <cstring>
#include <kconfig.h>
#include "kstaticdeleter.h"
#include <config.h>
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
+
+#ifdef HAVE_ABI_CXA_DEMANGLE
+#include <cxxabi.h>
#endif
+#include <link.h>
+#ifdef WITH_LIBBFD
+/* newer versions of libbfd require some autotools-specific macros to be defined */
+/* see binutils Bug 14243 and 14072 */
+#define PACKAGE kdelibs
+#define PACKAGE_VERSION KDE_VERSION
+
+#include <bfd.h>
+
+#ifdef HAVE_DEMANGLE_H
+#include <demangle.h>
+#endif // HAVE_DEMANGLE_H
+#endif // WITH_LIBBFD
+
+#endif // HAVE_BACKTRACE
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif // HAVE_ALLOCA_H
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif // HAVE_STDINT_H
+
class KDebugEntry;
class KDebugEntry
@@ -554,36 +580,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/kdecore/kdebug.h b/kdecore/kdebug.h
index be7d031bf..e22f90e5a 100644
--- a/kdecore/kdebug.h
+++ b/kdecore/kdebug.h
@@ -601,17 +601,21 @@ KDECORE_EXPORT kdbgstream kdDebug(bool cond, int area = 0);
/**
* \relates KGlobal
* 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
*/
-KDECORE_EXPORT TQString kdBacktrace();
+KDECORE_EXPORT TQString kdBacktrace(int levels=-1);
/**
* \relates KGlobal
- * 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
*/
-KDECORE_EXPORT TQString kdBacktrace(int levels);
+KDECORE_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 @@ KDECORE_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 KGlobal
* Returns a warning stream. You can use it to print warning
@@ -658,7 +659,6 @@ KDECORE_EXPORT void kdClearDebugConfig();
#ifdef NDEBUG
#define kdDebug kndDebug
-#define kdBacktrace kndBacktrace
#endif
#endif