/** * \file debug.cc * \brief Debugging Utilities * * This module contains some aids for debugging. We handle SIGSEGV * and failed assertions. * - on ELF/x86/gcc, we print a backtrace to stderr and fire the SIGSEGV * again (to catch it with the debugger). * - on other platforms, attempt to show the user the console at * least (Platform::showOutput). * * (c) 2001,2002 Stefan Reuther * * This program is free software; you can redistribute it and/or * modify it under the terms of file `COPYING' that comes with the * source code. * * 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. */ #include #include /* shotgun port from PCC II source tree */ // #include "util/debug.h" // #include "cpluslib/types.h" // #include "arch/platform.h" // #include "util/console.h" struct Platform { static void showOutput() { } }; #define console std::cerr typedef unsigned long uint32; typedef unsigned short uint16; static void sigsegv(int); /** Assertion Failure. Override the routine from cpluslib/assert.cc. */ int assertionFailed(const char* what, const char* file, int line) { console << file << ":" << line << ": INTERNAL ERROR: " << what << std::endl << std::flush; sigsegv(-1); return 0; } #if defined(__GNUC__) && __GNUC__ >= 2 && defined(__i386__) && defined(__ELF__) /******************** ELF/i386/gcc Backtrace Generator *******************/ /* This backtrace generator should be portable to any ELF platform that uses the "standard" stack layout, but I have not tested it. Basic idea: we walk the stackframe chain, and use the ELF dynamic linking information to determine symbol names. Preconditions: the executable must be dynamically linked with the `-E' linker option, i.e. `gcc -Wl,-E'. This option causes the linker to include the necessary symbols in the dynamic linker symbol table. */ /** Elf32 `Dynamic Table' Entry. */ struct Elf32_Dyn { uint32 d_tag; ///< Tag. One of DT_xxx uint32 d_val; ///< Value. Pointer or scalar. }; /** Elf32 Symbol Table Entry. */ struct Elf32_Sym { uint32 st_name; ///< Name. Index into string table. uint32 st_value; ///< Value. Usually, an address. uint32 st_size; ///< Size. For variables, the size. unsigned char st_info; ///< Type and binding. unsigned char st_other; ///< Not used. uint16 st_shndx; ///< Index into section table. Not used. }; struct I386_Stackframe { I386_Stackframe* next; void* ip; }; /** Selected Dynamic Table Entries. */ enum { DT_NULL = 0, ///< EOT marker. DT_HASH = 4, ///< Hash table position. Contains the symbol count. (?) DT_STRTAB = 5, ///< String table position. DT_SYMTAB = 6, ///< Symbol table position. DT_STRSZ = 10, ///< String table size. DT_SYMENT = 11, ///< Symbol table size. DT_MAX }; /** Dynamic Table, as mandated by the ELF standard. */ extern "C" Elf32_Dyn _DYNAMIC[]; /** Dynamic Table, indexed by type. */ static Elf32_Dyn dyna_index[DT_MAX]; static void (*old_sigsegv_handler)(int); static void (*old_sigabrt_handler)(int); /** Name the specified address. Finds out the symbol name corresponding to \c address, and prints it on \c out. */ void nameSymbol(void* address, std::ostream& out) { /* I assume the number of symbols is the number of `chain' entries in the hash table. */ if (!dyna_index[DT_HASH].d_val) { out << ""; return; } long nsymbols = ((long*) dyna_index[DT_HASH].d_val) [1]; unsigned long adr = (unsigned long) address; Elf32_Sym* sym = (Elf32_Sym*) dyna_index[DT_SYMTAB].d_val; Elf32_Sym* found = 0; while (nsymbols-- > 0) { if (adr >= sym->st_value && (!found || sym->st_value > found->st_value)) found = sym; ++sym; } if (found) { char* name = (char*) (found->st_name + dyna_index[DT_STRTAB].d_val); out << "<" << name << " + " << adr - found->st_value << ">"; } else out << ""; } /** Print backtrace. i386/ELF/gcc version. Prints the backtrace on the specified stream. This is intended to be used as \begincode cerr << ",--- Backtrace ---\n" << printBacktrace << "`--------\n" \endcode The decorative frame is to show that the trace was completely printed and did not get truncated because of deep shit happening (i.e. errors in the ostream, in the frame pointer chain, or in the symbol table). */ std::ostream& printBacktrace(std::ostream& out) { I386_Stackframe* frame_ptr = (I386_Stackframe*) __builtin_frame_address (0); int max = 10; while (frame_ptr) { out << "| ebp=" << frame_ptr->next << " eip=" << frame_ptr->ip << " "; nameSymbol(frame_ptr->ip, out); out << "\n"; frame_ptr = frame_ptr->next; if (!--max) break; } if (frame_ptr) out << "| (...more frames following)\n"; return out; } static void sigsegv(int i) { std::signal(SIGSEGV, old_sigsegv_handler); std::signal(SIGABRT, old_sigabrt_handler); std::cerr << ",---- Got signal " << i << ", backtrace follows: -------------\n" << printBacktrace << "`----------------------------------------------------\n"; Platform::showOutput(); std::raise(SIGSEGV); } /** Read dynamic table. This populates the dyna_index array. */ static void readDynamic(Elf32_Dyn* dyn) { while (dyn->d_tag != DT_NULL) { if (dyn->d_tag > 0 && dyn->d_tag < DT_MAX) dyna_index[dyn->d_tag] = *dyn; ++dyn; } } /** Initialize Debugging Functions. Installs backtrace generation on SIGSEGV. */ void initDebug(void) { old_sigsegv_handler = std::signal(SIGSEGV, sigsegv); old_sigabrt_handler = std::signal(SIGABRT, sigsegv); readDynamic(_DYNAMIC); } #else /******************* Generic (i.e. disfunctional) Part *******************/ static void (*old_sigsegv_handler)(int); static void sigsegv(int x) { std::signal(SIGSEGV, old_sigsegv_handler); console.flush(); console.write(LOG_ERROR, "\n" "## The program has just crashed. Above are its last words.\n" "## If you send a bug report, please include this information."); console << "Signal number was " << x << std::endl; Platform::showOutput(); std::exit(1); } void initDebug(void) { old_sigsegv_handler = std::signal(SIGSEGV, sigsegv); } std::ostream& printBacktrace(std::ostream& out) { return out << "Backtrace not available -- please port me\n"; } #endif