C++ Library containing a lot of needful things: Stack Trace, Command Line Parser, Resource Handling, Configuration Files, Unix Command Execution, Directories, Regular Expressions, Tokenizer, Function Trace, Standard Extensions.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

314 lines
13 KiB

/** @file
$Id$
$Date$
$Author$
@copy © Marc Wäckerlin
@license LGPL, see file <a href="license.html">COPYING</a>
$Log$
Revision 1.9 2004/10/13 10:47:15 marc
no more need for ldd in StackTrace, read from /proc/self/maps
Revision 1.8 2004/10/11 16:49:32 marc
Better comment for new shared library feature
Revision 1.7 2004/10/11 15:58:51 marc
First version with working support for shared libraries!
Revision 1.6 2004/10/07 16:59:12 marc
new method createSymtable that takes a list of arguments
-> untested!
Revision 1.5 2004/10/07 09:32:45 marc
correction in parameter (const&)
Revision 1.4 2004/08/28 16:21:25 marc
mrw-c++-0.92 (mrw)
- new file: version.cpp
- new file header for all sources
- work around warning in mrw::auto<T>
- possibility to compile without log4cxx
- work around bugs in demangle.h and libiberty.h
- corrections in documentation
- added simple tracing mechanism
- more warnings
- small corrections in Auto<>::Free and a new test for it
- possibility to compile without stack trace
*/
#ifndef __MRW_STACKTRACE_HPP__
#define __MRW_STACKTRACE_HPP__
#include <mrw/auto.hpp>
#include <vector>
#include <list>
#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
21 years ago
*/
//@{
/** @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 Debug information is required for compiling. You nee the
compile option @c -g, or even better @c -ggdb3.
21 years ago
21 years ago
@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.
<h3>Technology</h3>
21 years ago
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().
<h3>Draw Backs</h3>
21 years ago
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.
21 years ago
@todo Add support for alternative symbol evaluation using @c
backtrace_symbols.
@bug File and line is wrong where the exception is thrown. On
Address [0x4007830b] file and line is wrong, the file should be
/privat/home/marc/pro/mrw-c++/mrw/exception.cpp. File and line
are always wrong at the line where the exception is
instanciated. Why?!? (Could it be, because there is a string
created inline? The trace always shows basic_string.h.)
@code
UNEXPECTED EXCEPTION: ----------------------------
---------- Reason:
mrw::Exec: command execution failed
failed command was: "/bin/false"
error was: "execution failed"
---------- Stack:
1[0x8049b71] ../sysdeps/i386/elf/start.S:105 _start
[0x4022f92b] ????:0 __libc_start_main
[0x8049c96] /usr/include/g++/bits/basic_string.h:249 main
[0x40054668] ????:0 CppUnit::TextUi::TestRunner::run(std::string, bool, bool, bool)
[0x40054747] ????:0 CppUnit::TextUi::TestRunner::runTestByName(std::string, bool)
[0x400543ec] ????:0 CppUnit::TextUi::TestRunner::runTest(CppUnit::Test*, bool)
[0x4005525b] ????:0 CppUnit::TestSuite::run(CppUnit::TestResult*)
[0x4005525b] ????:0 CppUnit::TestSuite::run(CppUnit::TestResult*)
[0x4005525b] ????:0 CppUnit::TestSuite::run(CppUnit::TestResult*)
[0x4004aa06] ????:0 CppUnit::TestCase::run(CppUnit::TestResult*)
[0x804b164] /usr/include/cppunit/TestCaller.h:166 CppUnit::TestCaller<ExecTest, std::bad_exception>::runTest()
[0x804b299] /usr/include/g++/bits/stl_alloc.h:652 ExecTest::unexpectedExc()
[0x40078bda] /privat/home/marc/pro/mrw-c++/mrw/exec.cpp:77 mrw::Cmd::execute(bool) const
[0x4007830b] /usr/include/g++/bits/basic_string.h:249 mrw::Exec::execute(bool)
[0x40077941] /privat/home/marc/pro/mrw-c++/mrw/exec.cpp:38 mrw::ExecutionFailedExc::ExecutionFailedExc(std::string const&, std::string const&)
[0x400769d4] /privat/home/marc/pro/mrw-c++/mrw/exception.cpp:41 mrw::exception::exception()
[0x4007cbc5] /privat/home/marc/pro/mrw-c++/mrw/stacktrace.cpp:118 mrw::StackTrace::StackTrace()
-------------------------------------------------
@endcode
*/
class StackTrace {
public:
//............................................................... typedefs
/// container for the adresses
typedef std::vector<void*> AddressTrace;
/// binary file with offset (for shared libraries, 0 for executables)
typedef std::list< std::pair<std::string, void*> > BinFiles;
/// 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 or a
shared library
On Solaris and Linux, the executable is automatically
detected through the @c /proc file system. Only on Linux the
shared libraries are detected through @c /proc/self/maps. So
you may leave the parameter empty on these systems.
@param fname The file name of the executable or a shared
library. On Linux and Solaris, this can be evaluated
automatically, so the parameter is optional.
@param offs Offset of the address space. It is 0 for
executables, but must be given for shared libraries. Use @c
ldd on the executable to find out the offset.
@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 createSymtable must be executed at least 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 In general, call only one of both createSymtable
methods! But instead of callin the other method once, you
may also call this one several times.
@note If this method is called more than once for the same
file, the symbols are created only the first time, so you
don't loose too much time.
*/
static bool createSymtable(const std::string& fname="", void* offs=0)
throw(std::bad_exception);
/** @brief read the symbol table from a list of an executable file and
shared libraries
@param files The list of file names that must contain the
executable plus all shared libraries that should be included
in stack traces. Every file name has an offset address as
second parameter. This offset must be given for shared
libraries and can be retrieved using the program @c ldd.
@return @c true in case of success. If @c false is returned,
the symbol table was not completely 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 In general, call only one of both createSymtable methods!
@note This method calls the other one for all files in
parameter @c files.
*/
static bool createSymtable(const BinFiles& files) throw(std::bad_exception);
private:
//............................................................... typedefs
typedef std::map<bfd_vma, std::pair<bfd_vma, asection*> >
Translator;
//.............................................................. variables
AddressTrace _trace;
static std::map<std::string, Translator> _dic;
static std::map<Translator::key_type, std::string> _addrs;
static std::map<std::string, AutoBfd> _bfd;
static std::map<std::string, mrw::AutoPtr<asymbol*> > _syms;
//................................................................ methods
static BinFiles filename() throw(std::bad_exception);
static void buildSectionMap(bfd*, asection*, void*)
throw(std::bad_exception);
};
//@}
}
/** @addtogroup StackTrace */
//@{
/// evaluate a stack trace and shift it on a stream
inline std::ostream& operator<<(std::ostream& os, const mrw::StackTrace& st)
throw(std::bad_exception) {
return os<<(std::string)st;
}
//@}
#endif