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.
297 lines
12 KiB
297 lines
12 KiB
21 years ago
|
/** @file
|
||
|
|
||
|
$Id$
|
||
|
|
||
|
$Date$
|
||
|
$Author$
|
||
|
|
||
|
@copy © Marc Wäckerlin
|
||
|
@license LGPL, see file <a href="license.html">COPYING</a>
|
||
|
|
||
|
*/
|
||
22 years ago
|
#ifndef __MRW_STACKTRACE_HPP__
|
||
|
#define __MRW_STACKTRACE_HPP__
|
||
14 years ago
|
#include <mrw/auto.hxx>
|
||
22 years ago
|
#include <vector>
|
||
21 years ago
|
#include <list>
|
||
22 years ago
|
#include <map>
|
||
|
#include <string>
|
||
|
#include <memory>
|
||
18 years ago
|
#include <iostream>
|
||
22 years ago
|
#include <sys/mman.h>
|
||
|
#include <bfd.h>
|
||
|
|
||
18 years ago
|
#if (__GNUC__==3 && __GNUC_MINOR__<4 || __GNUC__<3) \
|
||
|
&& defined(_REENTRANT) && !defined(_MT)
|
||
20 years ago
|
#define _MT
|
||
|
#endif
|
||
|
#ifdef _MT
|
||
14 years ago
|
#include <boost/thread/recursive_mutex.hxx>
|
||
22 years ago
|
#endif
|
||
|
|
||
|
namespace mrw {
|
||
|
|
||
21 years ago
|
/** @defgroup debug Debug Utilities */
|
||
|
//@{
|
||
|
|
||
21 years ago
|
/** @defgroup grpStackTrace Collect and Format a Stack Trace
|
||
22 years ago
|
|
||
|
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
|
||
22 years ago
|
*/
|
||
|
//@{
|
||
|
|
||
|
/** @brief store and print a stack trace of the actual position in code
|
||
14 years ago
|
@pre \#include <mrw/stacktrace.hxx>
|
||
22 years ago
|
|
||
|
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
|
||
21 years ago
|
once, before evaluating the first stack trace. Best place is the
|
||
22 years ago
|
first line of the @c main function.
|
||
|
|
||
21 years ago
|
@note Debug information is required for compiling. You nee the
|
||
|
compile option @c -g, or even better @c -ggdb3.
|
||
22 years ago
|
|
||
22 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.
|
||
|
|
||
21 years ago
|
<h3>Technology</h3>
|
||
22 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().
|
||
|
|
||
21 years ago
|
<h3>Draw Backs</h3>
|
||
22 years ago
|
|
||
|
Unfortunately it is not possible to extract the source file name
|
||
|
and line number information if the executable was not compiled
|
||
21 years ago
|
with debug option @c -g.
|
||
22 years ago
|
|
||
21 years ago
|
@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:
|
||
21 years ago
|
|
||
|
@code
|
||
21 years ago
|
CXXFLAGS="-g -fno-inline" ./configure && make clean check
|
||
21 years ago
|
@endcode
|
||
21 years ago
|
|
||
21 years ago
|
@todo Should I add support for alternative symbol evaluation using @c
|
||
|
backtrace_symbols? I think rather not...?
|
||
22 years ago
|
*/
|
||
|
class StackTrace {
|
||
20 years ago
|
friend class StackTraceTest;
|
||
21 years ago
|
public:
|
||
22 years ago
|
//............................................................... typedefs
|
||
21 years ago
|
/// 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;
|
||
22 years ago
|
/// structure to store all evaluated information
|
||
|
struct CodePos {
|
||
20 years ago
|
CodePos(void const*const a, const std::string& fu,
|
||
|
const std::string& fi, unsigned int l):
|
||
|
address(a), function(fu), file(fi), line(l) {
|
||
22 years ago
|
}
|
||
20 years ago
|
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
|
||
22 years ago
|
};
|
||
|
//................................................................ methods
|
||
|
/// the constructor stores the actual stack trace
|
||
|
StackTrace() throw(std::bad_exception);
|
||
18 years ago
|
/// depending on how we got the stack trace, we may have to free memory
|
||
|
~StackTrace() throw();
|
||
22 years ago
|
/// evaluates the symbol table and returns the formatted stack trace
|
||
|
operator std::string() const throw(std::bad_exception);
|
||
21 years ago
|
/** @return list of raw stack addresses */
|
||
22 years ago
|
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
|
||
21 years ago
|
/** @classmutex _mutex */
|
||
|
static CodePos translate(void* addr) throw(std::bad_exception);
|
||
22 years ago
|
|
||
21 years ago
|
/** @brief read the symbol table from the executable file or a
|
||
|
shared library
|
||
|
|
||
21 years ago
|
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.
|
||
22 years ago
|
|
||
21 years ago
|
@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.
|
||
22 years ago
|
|
||
21 years ago
|
@param offs Offset of the address space. It is 0 for
|
||
21 years ago
|
executables, but must be given for shared libraries. Use @c
|
||
|
ldd on the executable to find out the offset.
|
||
21 years ago
|
|
||
22 years ago
|
@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.
|
||
|
|
||
21 years ago
|
@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.
|
||
22 years ago
|
|
||
21 years ago
|
@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.
|
||
21 years ago
|
|
||
21 years ago
|
@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.
|
||
21 years ago
|
|
||
|
@classmutex _mutex
|
||
21 years ago
|
*/
|
||
21 years ago
|
static bool createSymtable(const std::string& fname="", void* offs=0)
|
||
21 years ago
|
throw(std::bad_exception);
|
||
20 years ago
|
|
||
|
/** @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;
|
||
|
}
|
||
21 years ago
|
|
||
|
/** @brief read the symbol table from a list of an executable file and
|
||
|
shared libraries
|
||
|
|
||
21 years ago
|
@param files The list of file names that must contain the
|
||
21 years ago
|
executable plus all shared libraries that should be included
|
||
21 years ago
|
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.
|
||
21 years ago
|
|
||
|
@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.
|
||
|
|
||
21 years ago
|
@note In general, call only one of both createSymtable methods!
|
||
21 years ago
|
|
||
21 years ago
|
@note This method calls the other one for all files in
|
||
|
parameter @c files.
|
||
21 years ago
|
|
||
|
@classmutex _mutex
|
||
21 years ago
|
*/
|
||
21 years ago
|
static bool createSymtable(const BinFiles& files)
|
||
|
throw(std::bad_exception);
|
||
21 years ago
|
|
||
22 years ago
|
private:
|
||
|
//............................................................... typedefs
|
||
21 years ago
|
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;
|
||
20 years ago
|
friend class mrw::AutoResource
|
||
|
<bfd*, int(*)(bfd*), StackTrace::bfdClose, int>;
|
||
22 years ago
|
//.............................................................. variables
|
||
|
AddressTrace _trace;
|
||
21 years ago
|
static std::map<std::string, Translator> _dic;
|
||
|
static std::map<Translator::key_type, std::string> _addrs;
|
||
|
static std::map<std::string, AutoBfd> _bfd;
|
||
18 years ago
|
static std::map<std::string, mrw::AutoPtrAry<asymbol*> > _syms;
|
||
20 years ago
|
#ifdef _MT
|
||
21 years ago
|
static boost::recursive_mutex _mutex;
|
||
|
#endif
|
||
20 years ago
|
static std::string _error;
|
||
|
|
||
22 years ago
|
//................................................................ methods
|
||
21 years ago
|
static BinFiles filename() throw(std::bad_exception);
|
||
22 years ago
|
static void buildSectionMap(bfd*, asection*, void*)
|
||
|
throw(std::bad_exception);
|
||
|
};
|
||
|
|
||
|
//@}
|
||
21 years ago
|
//@}
|
||
22 years ago
|
}
|
||
21 years ago
|
|
||
21 years ago
|
/** @addtogroup grpStackTrace */
|
||
21 years ago
|
//@{
|
||
|
|
||
|
/// 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;
|
||
|
}
|
||
|
|
||
|
//@}
|
||
|
|
||
22 years ago
|
#endif
|