how to print function backtrack with boost
2017-08-27 08:32
323 查看
How to print current call
stack
boost::stacktrace::stacktracecontains methods for working with call-stack/backtraces/stacktraces.
Here's a small example:
#include <boost/stacktrace.hpp> // ... somewhere inside the `bar(int)` function that is called recursively: std::cout << boost::stacktrace::stacktrace();
In that example:
boost::stacktrace::is the namespace that has all the classes and functions to
work with stacktraces
stacktrace()is the default constructor call; constructor
stores the current function call sequence inside the stacktrace class.
Code from above will output something like this:
0# bar(int) at /path/to/source/file.cpp:70 1# bar(int) at /path/to/source/file.cpp:70 2# bar(int) at /path/to/source/file.cpp:70 3# bar(int) at /path/to/source/file.cpp:70 4# main at /path/to/main.cpp:93 5# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6 6# _start
Note | |
---|---|
By default the Stacktrace library is very conservative in methods to decode stacktrace. If your output does not look as fancy as in example from above, see section "Configuration and Build" for allowing advanced features of the library. |
Handle
terminates, aborts and Segmentation Faults
Segmentation Faults and std::terminatecalls sometimes happen in programs. Programmers usually wish to get as much information
as possible on such incidents, so having a stacktrace will significantly improve debugging and fixing.
std::terminatecalls
std::abort, so we need to capture stack traces on Segmentation Faults and Abort signals.
Warning | |
---|---|
Writing a signal handler requires high attention! Only a few system calls allowed in signal handlers, so there's no cross platform way to print a stacktrace without a risk of deadlocking. The only way to deal with the problem - dump raw stacktrace into file/socket and parse it on program restart. |
#include <signal.h> // ::signal, ::raise #include <boost/stacktrace.hpp> void my_signal_handler(int signum) { ::signal(signum, SIG_DFL); boost::stacktrace::safe_dump_to("./backtrace.dump"); ::raise(SIGABRT); }
Registering our handler:
::signal(SIGSEGV, &my_signal_handler); ::signal(SIGABRT, &my_signal_handler);
At program start we check for a file with stacktrace and if it exist - we're writing it in human readable format:
if (boost::filesystem::exists("./backtrace.dump")) { // there is a backtrace std::ifstream ifs("./backtrace.dump"); boost::stacktrace::stacktrace st = boost::stacktrace::stacktrace::from_dump(ifs); std::cout << "Previous run crashed:\n" << st << std::endl; // cleaning up ifs.close(); boost::filesystem::remove("./backtrace.dump"); }
Now we'll get the following output on
std::terminatecall after the program restarts:
Previous run crashed: 0# 0x00007F2EC0A6A8EF 1# my_signal_handler(int) at ../example/terminate_handler.cpp:37 2# 0x00007F2EBFD84CB0 3# 0x00007F2EBFD84C37 4# 0x00007F2EBFD88028 5# 0x00007F2EC0395BBD 6# 0x00007F2EC0393B96 7# 0x00007F2EC0393BE1 8# bar(int) at ../example/terminate_handler.cpp:18 9# foo(int) at ../example/terminate_handler.cpp:22 10# bar(int) at ../example/terminate_handler.cpp:14 11# foo(int) at ../example/terminate_handler.cpp:22 12# main at ../example/terminate_handler.cpp:84 13# 0x00007F2EBFD6FF45 14# 0x0000000000402209
Note | |
---|---|
Function names from shared libraries may not be decoded due to address space layout randomization. Still better than nothing. |
Better
asserts
Pretty often assertions provide not enough information to locate the problem. For example you can see the following message on out-of-range access:../../../boost/array.hpp:123: T& boost::array<T, N>::operator[](boost::array<T, N>::size_type) [with T = int; long unsigned int N = 5ul]: Assertion '(i < N)&&("out of range")' failed. Aborted (core dumped)
That's not enough to locate the problem without debugger. There may be thousand code lines in real world examples and hundred places where that assertion could happen. Let's try to improve the assertions, and make them more informative:
// BOOST_ENABLE_ASSERT_DEBUG_HANDLER is defined for the whole project #include <stdexcept> // std::logic_error #include <iostream> // std::cerr #include <boost/stacktrace.hpp> namespace boost { inline void assertion_failed_msg(char const* expr, char const* msg, char const* function, char const* /*file*/, long /*line*/) { std::cerr << "Expression '" << expr << "' is false in function '" << function << "': " << (msg ? msg : "<...>") << ".\n" << "Backtrace:\n" << boost::stacktrace::stacktrace() << '\n'; std::abort(); } inline void assertion_failed(char const* expr, char const* function, char const* file, long line) { ::boost::assertion_failed_msg(expr, 0 /*nullptr*/, function, file, line); } } // namespace boost
We've defined the
BOOST_ENABLE_ASSERT_DEBUG_HANDLERmacro for the whole project. Now all the
BOOST_ASSERTand
BOOST_ASSERT_MSGwill call our functions
assertion_failedand
assertion_failed_msgin case of failure. In
assertion_failed_msgwe output information that was provided by the assertion macro and
boost::stacktrace::stacktrace:
Expression 'i < N' is false in function 'T& boost::array<T, N>::operator[](boost::array<T, N>::size_type) [with T = int; long unsigned int N = 5ul; boost::array<T, N>::reference = int&; boost::array<T, N>::size_type = long unsigned int]': out of range. Backtrace: 0# boost::assertion_failed_msg(char const*, char const*, char const*, char const*, long) at ../example/assert_handler.cpp:39 1# boost::array<int, 5ul>::operator[](unsigned long) at ../../../boost/array.hpp:124 2# bar(int) at ../example/assert_handler.cpp:17 3# foo(int) at ../example/assert_handler.cpp:25 4# bar(int) at ../example/assert_handler.cpp:17 5# foo(int) at ../example/assert_handler.cpp:25 6# main at ../example/assert_handler.cpp:54 7# 0x00007F991FD69F45 in /lib/x86_64-linux-gnu/libc.so.6 8# 0x0000000000401139
Now we do know the steps that led to the assertion and can find the error without debugger.
Exceptions
with stacktrace
You can provide more information along with exception by embedding stacktraces into the exception. There are many ways to do that, here's how to doe that using Boost.Exception:Declare a
boost::error_infotypedef that holds the stacktrace:
#include <boost/stacktrace.hpp> #include <boost/exception/all.hpp> typedef boost::error_info<struct tag_stacktrace, boost::stacktrace::stacktrace> traced;
Write a helper class for throwing any exception with stacktrace:
template <class E> void throw_with_trace(const E& e) { throw boost::enable_error_info(e) << traced(boost::stacktrace::stacktrace()); }
Use
throw_with_trace(E);instead of just
throw E;:
if (i >= 4) throw_with_trace(std::out_of_range("'i' must be less than 4 in oops()")); if (i <= 0) throw_with_trace(std::logic_error("'i' must not be greater than zero in oops()"));
Process exceptions:
try { foo(5); // testing assert handler } catch (const std::exception& e) { std::cerr << e.what() << '\n'; const boost::stacktrace::stacktrace* st = boost::get_error_info<traced>(e); if (st) { std::cerr << *st << '\n'; } }
Code from above will output:
'i' must not be greater than zero in oops() 0# void throw_with_trace<std::logic_error>(std::logic_error const&) at ../example/throwing_st.cpp:22 1# oops(int) at ../example/throwing_st.cpp:38 2# bar(int) at ../example/throwing_st.cpp:54 3# foo(int) at ../example/throwing_st.cpp:59 4# bar(int) at ../example/throwing_st.cpp:49 5# foo(int) at ../example/throwing_st.cpp:59 6# main at ../example/throwing_st.cpp:76 7# 0x00007FAC113BEF45 in /lib/x86_64-linux-gnu/libc.so.6 8# 0x0000000000402ED9
Enabling
and disabling stacktraces
At some point arises a requirement to easily enable/disable stacktraces for a whole project. That could be easily achieved.Just define BOOST_STACKTRACE_LINK for a whole project. Now you can enable/disable stacktraces by just linking with different libraries:
link with
boost_stacktrace_noopto disable backtracing
link with other
boost_stacktrace_*libraries
See
section "Configuration and Build" for more info.
Saving
stacktraces by specified format
boost::stacktrace::stacktraceprovides access to individual
framesof the stacktrace, so that you could save stacktrace information in your own
format. Consider the example, that saves only function addresses of each frame:
#include <boost/stacktrace.hpp> #include <iostream> // std::cout namespace bs = boost::stacktrace; void dump_compact(const bs::stacktrace& st) { for (bs::frame frame: st) { std::cout << frame.address() << ','; } std::cout << std::endl; }
Code from above will output:
0x7fbcfd17f6b5,0x400d4a,0x400d61,0x400d61,0x400d61,0x400d61,0x400d77,0x400cbf,0x400dc0,0x7fbcfc82d830,0x400a79,
Getting
function information from pointer
boost::stacktrace::frameprovides information about functions. You may construct
that class from function pointer and get the function name at runtime:
#include <signal.h> // ::signal #include <boost/stacktrace/frame.hpp> #include <iostream> // std::cerr #include <cstdlib> // std::exit void print_signal_handler_and_exit() { typedef void(*function_t)(int); function_t old_signal_function = ::signal(SIGSEGV, SIG_DFL); boost::stacktrace::frame f(old_signal_function); std::cout << f << std::endl; std::exit(0); }
Code from above will output:
my_signal_handler(int) at boost/libs/stacktrace/example/debug_function.cpp:21
Global
control over stacktrace output format
You may override the behavior of default stacktrace output operator by defining the macro from Boost.ConfigBOOST_USER_CONFIGto point to a file like following:
#ifndef USER_CONFIG_HPP #define USER_CONFIG_HPP #include <boost/stacktrace/stacktrace_fwd.hpp> #include <iosfwd> namespace boost { namespace stacktrace { template <class CharT, class TraitsT, class Allocator> std::basic_ostream<CharT, TraitsT>& do_stream_st(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt); template <class CharT, class TraitsT> std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const stacktrace& bt) { return do_stream_st(os, bt); } }} // namespace boost::stacktrace #endif // USER_CONFIG_HPP
Implementation of
do_stream_stmay be the following:
namespace boost { namespace stacktrace { template <class CharT, class TraitsT, class Allocator> std::basic_ostream<CharT, TraitsT>& do_stream_st(std::basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt) { const std::streamsize w = os.width(); const std::size_t frames = bt.size(); for (std::size_t i = 0; i < frames; ++i) { os.width(2); os << i; os.width(w); os << "# "; os << bt[i].name(); os << '\n'; } return os; } }} // namespace boost::stacktrace
Code from above will output:
Terminate called: 0# bar(int) 1# foo(int) 2# bar(int) 3# foo(int)
相关文章推荐
- A collection with the function, print one then remove one. how to implement?
- How to convert enum to string with boost library
- How To Replace jQuery 1.9.1's $.parseJSON function with the implementation from jQuery 1.8.3
- D-Bus not built with -rdynamic so unable to print a backtrace
- How to manually rebuild a backtrace with GDB on x86
- How to mock the function with output parameter by cppumock?
- How to change the text with back button?
- how to get the function back trace in php
- How to Do Everything with Your Web 2.0 Blog (Paperback), Oct.2007.eBook-BBL
- to learn how to user trackback ping
- How to Do Everything with Google Tools (Paperback), Sep.2007.eBook-BBL
- How To Install Nessus In Backtrack 5 R1 Tutorial
- [原] XAF How to use Signle() function in PersistentAlias with Many-To-Many scenario
- How to write simple HTTP proxy with Boost.Asio
- How to deal with ptxas : fatal error : Unresolved extern function 'cudaGetParameterBuffer'
- How To Add The BackTrack Repository To Ubuntu 12.10/12.04/11.10
- how to print the spend time of one function
- How to compile C++ boost library with Intel C++ compiler
- How to Detect and Track Object With OpenCV