2004-08-28 16:21:25 +00:00
|
|
|
/** @file
|
|
|
|
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
$Date$
|
|
|
|
$Author$
|
|
|
|
|
|
|
|
@copy © Marc Wäckerlin
|
|
|
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
|
|
|
|
|
|
|
$Log$
|
2004-10-13 10:47:15 +00:00
|
|
|
Revision 1.9 2004/10/13 10:47:15 marc
|
|
|
|
no more need for ldd in StackTrace, read from /proc/self/maps
|
|
|
|
|
2004-10-11 16:49:32 +00:00
|
|
|
Revision 1.8 2004/10/11 16:49:32 marc
|
|
|
|
Better comment for new shared library feature
|
|
|
|
|
2004-10-11 15:58:51 +00:00
|
|
|
Revision 1.7 2004/10/11 15:58:51 marc
|
|
|
|
First version with working support for shared libraries!
|
|
|
|
|
2004-10-07 16:59:12 +00:00
|
|
|
Revision 1.6 2004/10/07 16:59:12 marc
|
|
|
|
new method createSymtable that takes a list of arguments
|
|
|
|
-> untested!
|
|
|
|
|
2004-10-07 09:32:45 +00:00
|
|
|
Revision 1.5 2004/10/07 09:32:45 marc
|
|
|
|
correction in parameter (const&)
|
|
|
|
|
2004-08-28 16:21:25 +00:00
|
|
|
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
|
|
|
|
|
|
|
|
*/
|
2004-04-21 06:39:20 +00:00
|
|
|
#ifndef __MRW_STACKTRACE_HPP__
|
|
|
|
#define __MRW_STACKTRACE_HPP__
|
|
|
|
#include <mrw/auto.hpp>
|
|
|
|
#include <vector>
|
2004-10-07 16:59:12 +00:00
|
|
|
#include <list>
|
2004-04-21 06:39:20 +00:00
|
|
|
#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
|
2004-04-27 20:26:50 +00:00
|
|
|
*/
|
|
|
|
//@{
|
|
|
|
|
|
|
|
/** @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.
|
|
|
|
|
2004-10-13 10:47:15 +00:00
|
|
|
@note Debug information is required for compiling. You nee the
|
|
|
|
compile option @c -g, or even better @c -ggdb3.
|
2004-04-23 16:03:29 +00:00
|
|
|
|
2004-04-27 20:26:50 +00:00
|
|
|
@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.
|
|
|
|
|
2004-08-28 16:21:25 +00:00
|
|
|
<h3>Technology</h3>
|
2004-04-23 16:03:29 +00:00
|
|
|
|
|
|
|
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().
|
|
|
|
|
2004-08-28 16:21:25 +00:00
|
|
|
<h3>Draw Backs</h3>
|
2004-04-23 16:03:29 +00:00
|
|
|
|
|
|
|
Unfortunately it is not possible to extract the source file name
|
|
|
|
and line number information if the executable was not compiled
|
2004-10-13 10:47:15 +00:00
|
|
|
with debug option @c -g.
|
2004-04-23 16:03:29 +00:00
|
|
|
|
|
|
|
@todo Add support for alternative symbol evaluation using @c
|
|
|
|
backtrace_symbols.
|
2004-10-13 10:47:15 +00:00
|
|
|
|
|
|
|
@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
|
2004-04-21 06:39:20 +00:00
|
|
|
*/
|
|
|
|
class StackTrace {
|
2004-10-11 15:58:51 +00:00
|
|
|
public:
|
2004-04-21 06:39:20 +00:00
|
|
|
//............................................................... typedefs
|
2004-10-11 15:58:51 +00:00
|
|
|
/// 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;
|
2004-04-21 06:39:20 +00:00
|
|
|
/// 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);
|
2004-08-28 16:21:25 +00:00
|
|
|
/** @return list of raw stack addresses */
|
2004-04-21 06:39:20 +00:00
|
|
|
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
|
2004-10-07 16:59:12 +00:00
|
|
|
static CodePos translate(void* addr)
|
|
|
|
throw(std::bad_exception);
|
2004-04-21 06:39:20 +00:00
|
|
|
|
2004-10-11 16:49:32 +00:00
|
|
|
/** @brief read the symbol table from the executable file or a
|
|
|
|
shared library
|
|
|
|
|
2004-10-13 10:47:15 +00:00
|
|
|
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.
|
2004-04-21 06:39:20 +00:00
|
|
|
|
2004-10-11 16:49:32 +00:00
|
|
|
@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.
|
2004-04-21 06:39:20 +00:00
|
|
|
|
2004-10-11 15:58:51 +00:00
|
|
|
@param offs Offset of the address space. It is 0 for
|
2004-10-11 16:49:32 +00:00
|
|
|
executables, but must be given for shared libraries. Use @c
|
|
|
|
ldd on the executable to find out the offset.
|
2004-10-11 15:58:51 +00:00
|
|
|
|
2004-04-21 06:39:20 +00:00
|
|
|
@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.
|
|
|
|
|
2004-10-11 16:49:32 +00:00
|
|
|
@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.
|
2004-04-21 06:39:20 +00:00
|
|
|
|
2004-10-11 16:49:32 +00:00
|
|
|
@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.
|
2004-10-07 16:59:12 +00:00
|
|
|
|
2004-10-11 16:49:32 +00:00
|
|
|
@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.
|
2004-10-07 16:59:12 +00:00
|
|
|
*/
|
2004-10-11 15:58:51 +00:00
|
|
|
static bool createSymtable(const std::string& fname="", void* offs=0)
|
2004-10-07 16:59:12 +00:00
|
|
|
throw(std::bad_exception);
|
|
|
|
|
|
|
|
/** @brief read the symbol table from a list of an executable file and
|
|
|
|
shared libraries
|
|
|
|
|
2004-10-11 15:58:51 +00:00
|
|
|
@param files The list of file names that must contain the
|
2004-10-07 16:59:12 +00:00
|
|
|
executable plus all shared libraries that should be included
|
2004-10-11 15:58:51 +00:00
|
|
|
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.
|
2004-10-07 16:59:12 +00:00
|
|
|
|
|
|
|
@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.
|
|
|
|
|
2004-10-11 16:49:32 +00:00
|
|
|
@note In general, call only one of both createSymtable methods!
|
2004-10-07 16:59:12 +00:00
|
|
|
|
2004-10-11 16:49:32 +00:00
|
|
|
@note This method calls the other one for all files in
|
|
|
|
parameter @c files.
|
2004-10-11 15:58:51 +00:00
|
|
|
*/
|
|
|
|
static bool createSymtable(const BinFiles& files) throw(std::bad_exception);
|
2004-10-07 16:59:12 +00:00
|
|
|
|
2004-04-21 06:39:20 +00:00
|
|
|
private:
|
|
|
|
//............................................................... typedefs
|
|
|
|
typedef std::map<bfd_vma, std::pair<bfd_vma, asection*> >
|
|
|
|
Translator;
|
|
|
|
//.............................................................. variables
|
|
|
|
AddressTrace _trace;
|
2004-10-11 15:58:51 +00:00
|
|
|
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;
|
2004-04-21 06:39:20 +00:00
|
|
|
//................................................................ methods
|
2004-10-11 15:58:51 +00:00
|
|
|
static BinFiles filename() throw(std::bad_exception);
|
2004-04-21 06:39:20 +00:00
|
|
|
static void buildSectionMap(bfd*, asection*, void*)
|
|
|
|
throw(std::bad_exception);
|
|
|
|
};
|
|
|
|
|
|
|
|
//@}
|
|
|
|
}
|
2004-10-11 15:58:51 +00:00
|
|
|
|
|
|
|
/** @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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//@}
|
|
|
|
|
2004-04-21 06:39:20 +00:00
|
|
|
#endif
|