197 lines
8.5 KiB
C++
197 lines
8.5 KiB
C++
// g++ -Wall -D__SOLARIS__ -g -I /home/public/freeware/include -L /home/public/freeware/lib -I . stacktrace.cxx -lbfd -liberty
|
|
#ifndef __MRW_STACKTRACE_HPP__
|
|
#define __MRW_STACKTRACE_HPP__
|
|
#include <mrw/auto.hpp>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <string>
|
|
#include <memory>
|
|
#include <sys/mman.h>
|
|
#include <bfd.h>
|
|
|
|
#ifdef __REENTRANT
|
|
#warning "mrw::StackTrace is not thread safe yet!"
|
|
#warning "It should work, but is at least untested..."
|
|
#endif
|
|
|
|
namespace mrw {
|
|
|
|
/** @defgroup StackTrace Collect and Format a Stack Trace
|
|
|
|
Somewhere in a program, there is a fatal error, e.g. an
|
|
unexpected exception is thrown. How is it possible to debug the
|
|
problem in such a case? Sometimes you can start a debugger and
|
|
trace the execution of your program. But what if it occurs only
|
|
once a week, or if you cannot set a breakpoint, because you
|
|
don't know where the problem is located, or because only the
|
|
1000th run of a method causes a problem, or what if the problem
|
|
occurs only at your customers installation?
|
|
|
|
One way to solve these problems is to do logging, or even
|
|
function tracing, so you can narrow down the lines of code,
|
|
where the problem occurs. But sometimes this is not enough,
|
|
especially with exceptions. One of the worst things with
|
|
exceptions is, you can catch an exception somewhere, but you
|
|
don't know where it was thrown. Here it is very handy, to be
|
|
able to write a stacktrace to a logging device.
|
|
|
|
For logging, I recommend log4cxx on page:
|
|
- http://logging.apache.org/log4cxx
|
|
|
|
These classes are for collecting a stack trace and later for
|
|
formatting with source code file name, line number and the
|
|
method name.
|
|
|
|
For collecting the stack trace (the addresses):
|
|
- either the GNU gcc compiler is required
|
|
- or the GNU glibc library function @c backtrace
|
|
|
|
For extracting information from an address, the ELF library is required.
|
|
|
|
@note For all features and full operation, this class requires:
|
|
- either a GNU glibc bases system (LINUX), or the GNU gcc compiler
|
|
- a system with ELF binaries (LINUX, Solaris, ...)
|
|
- debug information, compile option @c -g
|
|
- it must be linked with @c -libery and @c -lbfd
|
|
|
|
@subsection sttech Technology
|
|
|
|
On GNU glibc based systems (Linux), the stack trace is collected
|
|
with GNU glibc's function @c backtrace(). On other systems
|
|
(Solaris) it is collected using the GNU gcc's internal function @c
|
|
__builtin_return_address(). With both functions, at most 50 steps
|
|
back are collected.
|
|
|
|
The evaluation is not done with the glibc library function @c
|
|
backtrace_symbols(), because this function is unable to print
|
|
the source file name and line number information. Instead of
|
|
this, the executable binary is loaded into the memory and
|
|
evaluated using the bdf library functions. For this the stack
|
|
tracer needs to know how to find out which executable is
|
|
running. It is possible to get this information automatically on
|
|
Linux and Solaris. On other systems, I don't have this
|
|
information, but you can either tell me, and I integrate support
|
|
for your system (when I have time to do it), or provide the
|
|
executable file name as an argument to @c
|
|
mrw::StackTrace::createSymtable().
|
|
|
|
@subsection stdrawbacks Draw Backs
|
|
|
|
Unfortunately it is not possible to extract the source file name
|
|
and line number information if the executable was not compiled
|
|
with debug option @c -g. But what's worse, it is not possible to
|
|
ger symbolic information from libraries linked to the
|
|
executable. Perhaps it could be possible, if I'd add a
|
|
possibility to read and evaluate these libraries, but that's for
|
|
a future release.
|
|
|
|
@todo Add support to read debugging information from libraries
|
|
that are linked to the executable.
|
|
|
|
@todo Add support for alternative symbol evaluation using @c
|
|
backtrace_symbols.
|
|
*/
|
|
//@{
|
|
|
|
/** @brief store and print a stack trace of the actual position in code
|
|
@pre #include <mrw/stacktrace.hpp>
|
|
|
|
In the constructor, a stack trace is stored, but not yet
|
|
evaluated. Therefore storing a stack trace is relatively
|
|
fast. The evaluation is done when the stack trace is printed on
|
|
a stream or converted to a string. "Evaluation" means, that the
|
|
addresses are mapped to the correspoding symbols, the method
|
|
names, sorce file names and line numbers are evaluated.
|
|
|
|
@note Method StackTrace::createSymtable must be called exactely
|
|
once, before evaluating the first stack trace.Best place is the
|
|
first line of the @c main function.
|
|
|
|
@note This class requires libbfd an libiberty. Debug information
|
|
is required for compiling. You nee the compile option @c -g, or
|
|
even better @c -ggdb3. To link, you need @c -lmrw, @c -lbfd and
|
|
@c -liberty.
|
|
|
|
@note The stack trace is known to work perfectly on Linux and
|
|
Solaris both with GNU gcc compiler. But it should work with the
|
|
GNU compiler on all systems, or wherever there is a glibc
|
|
library.
|
|
|
|
@note Symbol evaluation requires the ELF library and an ELF system.
|
|
*/
|
|
class StackTrace {
|
|
public:
|
|
//............................................................... typedefs
|
|
typedef std::vector<void*> AddressTrace; ///< container for the adresses
|
|
/// structure to store all evaluated information
|
|
struct CodePos {
|
|
CodePos(void* a, std::string fn, std::string fi, unsigned int l)
|
|
throw(std::bad_exception):
|
|
address(a), function(fn), file(fi), line(l) {
|
|
}
|
|
void* address; ///< the address pointer
|
|
std::string function; ///< function/method name
|
|
std::string file; ///< code file name
|
|
unsigned int line; ///< code line number
|
|
};
|
|
//................................................................ methods
|
|
/// the constructor stores the actual stack trace
|
|
StackTrace() throw(std::bad_exception);
|
|
/// evaluates the symbol table and returns the formatted stack trace
|
|
operator std::string() const throw(std::bad_exception);
|
|
/// @return list of raw stack addresses
|
|
operator const AddressTrace&() const throw(std::bad_exception) {
|
|
return _trace;
|
|
}
|
|
/// evaluate the stack trace and print it to a stream
|
|
const StackTrace& print(std::ostream& os) const throw(std::bad_exception);
|
|
/// evaluates and returns all information from a raw address
|
|
static CodePos translate(void* addr) throw(std::bad_exception);
|
|
|
|
/** @brief read the symbol table from the executable file
|
|
|
|
@param std::string The file name of the executable. On Linux
|
|
and Solaris, this can be evaluated automatically, so the
|
|
parameter is optional.
|
|
|
|
@return @c true in case of success. If @c false is returned,
|
|
the symbol table was not read and the evaluation cannot be
|
|
done. Printing then only prints the raw addresses, without
|
|
file, line nmber information and method names.
|
|
|
|
@note This method must be executed once before a stack trace
|
|
is printed the very first time. For storing a stack trace
|
|
(that means for the creation of a mrw::StackTrace object) a
|
|
call to this method is not yet needed.
|
|
|
|
@note If this method is called more than once, the symbols
|
|
are created only the first time, so you don't loose too much
|
|
time.
|
|
*/
|
|
static bool createSymtable(std::string = "") throw(std::bad_exception);
|
|
private:
|
|
//............................................................... typedefs
|
|
typedef std::map<bfd_vma, std::pair<bfd_vma, asection*> >
|
|
Translator;
|
|
//.............................................................. variables
|
|
AddressTrace _trace;
|
|
static std::auto_ptr<Translator> _dic;
|
|
static std::vector<Translator::key_type> _addrs;
|
|
static AutoBfd _bfd;
|
|
static std::auto_ptr<asymbol*> _syms;
|
|
//................................................................ methods
|
|
static std::string filename() throw(std::bad_exception);
|
|
static void buildSectionMap(bfd*, asection*, void*)
|
|
throw(std::bad_exception);
|
|
};
|
|
|
|
/// evaluate a stack trace and shift it on a stream
|
|
inline std::ostream& operator<<(std::ostream& os, const StackTrace& st)
|
|
throw(std::bad_exception) {
|
|
return os<<(std::string)st;
|
|
}
|
|
|
|
//@}
|
|
}
|
|
#endif
|