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>
|
|
|
|
|
|
|
|
*/
|
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>
|
|
|
|
|
2005-11-29 12:42:01 +00:00
|
|
|
#if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) && _REENTRANT && !_MT
|
|
|
|
#define _MT
|
|
|
|
#endif
|
|
|
|
#ifdef _MT
|
2005-03-11 23:22:58 +00:00
|
|
|
#include <boost/thread/recursive_mutex.hpp>
|
2004-04-21 06:39:20 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
namespace mrw {
|
|
|
|
|
2005-04-07 20:51:30 +00:00
|
|
|
/** @defgroup debug Debug Utilities */
|
|
|
|
//@{
|
|
|
|
|
2005-01-28 12:13:10 +00:00
|
|
|
/** @defgroup grpStackTrace Collect and Format a Stack Trace
|
2004-04-21 06:39:20 +00:00
|
|
|
|
|
|
|
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
|
2005-11-29 12:42:01 +00:00
|
|
|
@pre \#include <mrw/stacktrace.hpp>
|
2004-04-27 20:26:50 +00:00
|
|
|
|
|
|
|
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
|
2005-03-11 23:22:58 +00:00
|
|
|
once, before evaluating the first stack trace. Best place is the
|
2004-04-27 20:26:50 +00:00
|
|
|
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
|
|
|
|
2004-11-25 18:30:20 +00:00
|
|
|
@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:
|
2004-10-13 10:47:15 +00:00
|
|
|
|
|
|
|
@code
|
2004-11-25 18:30:20 +00:00
|
|
|
CXXFLAGS="-g -fno-inline" ./configure && make clean check
|
2004-10-13 10:47:15 +00:00
|
|
|
@endcode
|
2004-11-25 18:30:20 +00:00
|
|
|
|
2005-03-11 23:22:58 +00:00
|
|
|
@todo Should I add support for alternative symbol evaluation using @c
|
|
|
|
backtrace_symbols? I think rather not...?
|
2004-04-21 06:39:20 +00:00
|
|
|
*/
|
|
|
|
class StackTrace {
|
2005-11-29 12:42:01 +00:00
|
|
|
friend class StackTraceTest;
|
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 {
|
2005-11-29 12:42:01 +00:00
|
|
|
CodePos(void const*const a, const std::string& fu,
|
|
|
|
const std::string& fi, unsigned int l):
|
|
|
|
address(a), function(fu), file(fi), line(l) {
|
2004-04-21 06:39:20 +00:00
|
|
|
}
|
2005-11-29 12:42:01 +00:00
|
|
|
void const*const address; ///< the address pointer
|
|
|
|
std::string function; ///< function/method name
|
|
|
|
std::string file; ///< code file name
|
|
|
|
unsigned int line; ///< code line number
|
2004-04-21 06:39:20 +00:00
|
|
|
};
|
|
|
|
//................................................................ methods
|
|
|
|
/// the constructor stores the actual stack trace
|
|
|
|
StackTrace() throw(std::bad_exception);
|
2007-08-05 08:20:01 +00:00
|
|
|
/// depending on how we got the stack trace, we may have to free memory
|
|
|
|
~StackTrace() throw();
|
2004-04-21 06:39:20 +00:00
|
|
|
/// 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
|
2005-03-11 23:22:58 +00:00
|
|
|
/** @classmutex _mutex */
|
|
|
|
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.
|
2005-03-11 23:22:58 +00:00
|
|
|
|
|
|
|
@classmutex _mutex
|
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);
|
2005-11-29 12:42:01 +00:00
|
|
|
|
|
|
|
/** @brief get the error text if @ref createSymtable returns false
|
|
|
|
|
|
|
|
In case @ref createSymtable is not successful, you can use
|
|
|
|
this method to retrieve the error text of the exception that
|
|
|
|
was caught in @ref createSymtable.
|
|
|
|
|
|
|
|
@return error text */
|
|
|
|
static const std::string& error() throw() {
|
|
|
|
return _error;
|
|
|
|
}
|
2004-10-07 16:59:12 +00:00
|
|
|
|
|
|
|
/** @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.
|
2005-03-11 23:22:58 +00:00
|
|
|
|
|
|
|
@classmutex _mutex
|
2004-10-11 15:58:51 +00:00
|
|
|
*/
|
2005-02-18 15:48:56 +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
|
2005-02-18 15:48:56 +00:00
|
|
|
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;
|
2005-11-29 12:42:01 +00:00
|
|
|
friend class mrw::AutoResource
|
|
|
|
<bfd*, int(*)(bfd*), StackTrace::bfdClose, int>;
|
2004-04-21 06:39:20 +00:00
|
|
|
//.............................................................. 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;
|
2007-08-05 08:20:01 +00:00
|
|
|
static std::map<std::string, mrw::AutoPtrAry<asymbol*> > _syms;
|
2005-11-29 12:42:01 +00:00
|
|
|
#ifdef _MT
|
2005-03-11 23:22:58 +00:00
|
|
|
static boost::recursive_mutex _mutex;
|
|
|
|
#endif
|
2005-11-29 12:42:01 +00:00
|
|
|
static std::string _error;
|
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
|
|
|
//@}
|
2005-04-07 20:51:30 +00:00
|
|
|
//@}
|
2004-04-21 06:39:20 +00:00
|
|
|
}
|
2004-10-11 15:58:51 +00:00
|
|
|
|
2005-01-28 12:13:10 +00:00
|
|
|
/** @addtogroup grpStackTrace */
|
2004-10-11 15:58:51 +00:00
|
|
|
//@{
|
|
|
|
|
|
|
|
/// 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
|