322 lines
12 KiB
C++
322 lines
12 KiB
C++
/** @file
|
|
|
|
$Id$
|
|
|
|
$Date$
|
|
$Author$
|
|
|
|
@copy © Marc Wäckerlin
|
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
|
|
|
$Log$
|
|
Revision 1.15 2005/04/07 20:51:30 marc
|
|
docu: new doxygen, new grouping
|
|
|
|
Revision 1.14 2005/03/11 23:22:58 marc
|
|
It's multithreaded now, thanks to a boost mutex
|
|
|
|
Revision 1.13 2005/02/18 15:48:56 marc
|
|
Dynamic loading of libbfd, no more dependency on specific libbfd version!
|
|
|
|
Revision 1.12 2005/01/28 12:13:11 marc
|
|
interference between group name StackTrace and class name StackTrace
|
|
|
|
Revision 1.11 2005/01/28 12:13:11 marc
|
|
interference between group name StackTrace and class name StackTrace
|
|
|
|
Revision 1.10 2004/11/25 18:30:20 marc
|
|
bug fixed (put solution in the documentation)
|
|
|
|
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
|
|
#include <boost/thread/recursive_mutex.hpp>
|
|
#endif
|
|
|
|
namespace mrw {
|
|
|
|
/** @defgroup debug Debug Utilities */
|
|
//@{
|
|
|
|
/** @defgroup grpStackTrace 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
|
|
*/
|
|
//@{
|
|
|
|
/** @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.
|
|
|
|
@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>
|
|
|
|
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>
|
|
|
|
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.
|
|
|
|
@note If file and line is wrong, but the function name is
|
|
correct, try to build without optimizations. For GNU compiler,
|
|
this means no option @c -O or @c -O0 and enable the debug
|
|
information with @c -g and don't inline functions with compiler
|
|
option @c -fno-inline. To build this project, you may enter:
|
|
|
|
@code
|
|
CXXFLAGS="-g -fno-inline" ./configure && make clean check
|
|
@endcode
|
|
|
|
@todo Should I add support for alternative symbol evaluation using @c
|
|
backtrace_symbols? I think rather not...?
|
|
*/
|
|
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
|
|
/** @classmutex _mutex */
|
|
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.
|
|
|
|
@classmutex _mutex
|
|
*/
|
|
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.
|
|
|
|
@classmutex _mutex
|
|
*/
|
|
static bool createSymtable(const BinFiles& files)
|
|
throw(std::bad_exception);
|
|
|
|
private:
|
|
//............................................................... typedefs
|
|
typedef std::map<bfd_vma, std::pair<bfd_vma, asection*> > Translator;
|
|
static int bfdClose(bfd*) throw();
|
|
typedef mrw::AutoResource<bfd*, int(*)(bfd*), StackTrace::bfdClose, int>
|
|
AutoBfd;
|
|
//.............................................................. 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;
|
|
#ifdef _REENTRANT
|
|
static boost::recursive_mutex _mutex;
|
|
#endif
|
|
//................................................................ methods
|
|
static BinFiles filename() throw(std::bad_exception);
|
|
static void buildSectionMap(bfd*, asection*, void*)
|
|
throw(std::bad_exception);
|
|
};
|
|
|
|
//@}
|
|
//@}
|
|
}
|
|
|
|
/** @addtogroup grpStackTrace */
|
|
//@{
|
|
|
|
/// 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
|