Added noop backend. Do not skip frames in backends anymore, because it is impossible to guess the inlining heuristics on different compilers. Fixed tests, added more tests, simplified code, improved docs

This commit is contained in:
Antony Polukhin
2016-09-08 21:42:43 +03:00
parent e798527df8
commit 6522b60c72
12 changed files with 131 additions and 63 deletions

View File

@@ -33,6 +33,18 @@ explicit backtrace ;
mp-run-simple has_windbg.cpp : : : : WinDbg ;
explicit WinDbg ;
lib boost_stacktrace_noop
: # sources
../src/noop.cpp
: # requirements
<warnings>all
: # default build
: # usage-requirements
<link>shared:<define>BOOST_TEST_DYN_LINK=1
;
boost-install boost_stacktrace_noop ;
lib boost_stacktrace_libunwind
: # sources
../src/libunwind.cpp

View File

@@ -33,7 +33,7 @@ class stacktrace {
public:
/// @brief Stores the current function call sequence inside the class.
///
/// @b Complexity: O(N) where N is call seaquence length
/// @b Complexity: O(N) where N is call seaquence length, O(1) for noop backend.
BOOST_STACKTRACE_FUNCTION stacktrace() BOOST_NOEXCEPT;
/// @b Complexity: O(1)
@@ -56,7 +56,7 @@ public:
/// @returns Function name in a human readable form.
/// @throws std::bad_alloc if not enough memory to construct resulting string.
///
/// @b Complexity: Amortized O(1)
/// @b Complexity: Amortized O(1), O(1) for noop backend.
BOOST_STACKTRACE_FUNCTION std::string operator[](std::size_t frame) const;
};

View File

@@ -15,7 +15,9 @@
#include <boost/stacktrace.hpp>
#include <boost/static_assert.hpp>
#if defined(BOOST_WINDOWS) || defined(BOOST_STACKTRACE_USE_WINDBG)
#if defined(BOOST_STACKTRACE_USE_NOOP)
# include <boost/stacktrace/detail/stacktrace_noop.hpp>
#elif defined(BOOST_WINDOWS) || defined(BOOST_STACKTRACE_USE_WINDBG)
# include <boost/stacktrace/detail/stacktrace_windows.hpp>
#elif defined(BOOST_STACKTRACE_USE_LIBUNWIND)
# include <boost/stacktrace/detail/stacktrace_libunwind.hpp>

View File

@@ -24,11 +24,10 @@
namespace boost { namespace stacktrace { namespace detail {
struct backtrace_holder {
BOOST_STATIC_CONSTEXPR std::size_t skip_frames = 1u;
std::size_t frames_count;
boost::shared_ptr<std::string[]> frames;
backtrace_holder() BOOST_NOEXCEPT
BOOST_FORCEINLINE backtrace_holder() BOOST_NOEXCEPT
: frames_count(0)
{
unw_context_t uc;
@@ -44,11 +43,6 @@ struct backtrace_holder {
while (unw_step(&cursor) > 0) {
++ frames_count;
}
if (frames_count <= skip_frames) {
frames_count = 0;
return;
}
frames_count -= skip_frames;
}
unw_cursor_t cursor;
@@ -57,13 +51,6 @@ struct backtrace_holder {
return;
}
for (std::size_t i = 0; i < skip_frames; ++i) {
if (unw_step(&cursor) <= 0) {
frames_count = 0;
return;
}
}
BOOST_TRY {
frames = boost::make_shared<std::string[]>(frames_count);
std::size_t i = 0;
@@ -75,11 +62,11 @@ struct backtrace_holder {
BOOST_CATCH_END
}
std::size_t size() const BOOST_NOEXCEPT {
inline std::size_t size() const BOOST_NOEXCEPT {
return frames_count;
}
std::string get_frame(std::size_t frame) const {
inline std::string get_frame(std::size_t frame) const {
if (frame < frames_count) {
return frames[frame];
} else {
@@ -87,7 +74,7 @@ struct backtrace_holder {
}
}
static std::string get_frame_impl(unw_cursor_t& cursor) {
static inline std::string get_frame_impl(unw_cursor_t& cursor) {
std::string res;
unw_word_t offp;
char data[256];

View File

@@ -26,23 +26,16 @@ struct backtrace_holder {
BOOST_STATIC_CONSTEXPR std::size_t max_size = 100u;
void* buffer[max_size];
#ifdef BOOST_STACKTRACE_HEADER_ONLY
BOOST_STATIC_CONSTEXPR std::size_t skip_frames = 0u;
#else
BOOST_STATIC_CONSTEXPR std::size_t skip_frames = 2u;
#endif
inline backtrace_holder() BOOST_NOEXCEPT {
BOOST_FORCEINLINE backtrace_holder() BOOST_NOEXCEPT {
frames_count = ::backtrace(buffer, max_size);
}
inline std::size_t size() const BOOST_NOEXCEPT {
return frames_count > skip_frames ? frames_count - skip_frames : 1u;
return frames_count;
}
inline std::string get_frame(std::size_t frame) const {
std::string res;
frame += skip_frames;
if (frame >= frames_count) {
return res;
}

View File

@@ -0,0 +1,29 @@
// Copyright Antony Polukhin, 2016.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_STACKTRACE_DETAIL_STACKTRACE_NOOP_HPP
#define BOOST_STACKTRACE_DETAIL_STACKTRACE_NOOP_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
namespace boost { namespace stacktrace { namespace detail {
struct backtrace_holder {
inline std::size_t size() const BOOST_NOEXCEPT {
return 0u;
}
inline std::string get_frame(std::size_t /*frame*/) const {
return std::string();
}
};
}}} // namespace boost::stacktrace::detail
#endif // BOOST_STACKTRACE_DETAIL_STACKTRACE_NOOP_HPP

View File

@@ -34,7 +34,7 @@ struct backtrace_holder {
void* buffer[max_size];
HANDLE process;
inline backtrace_holder() BOOST_NOEXCEPT
BOOST_FORCEINLINE backtrace_holder() BOOST_NOEXCEPT
: frames_count(0)
, process(GetCurrentProcess())
{

8
src/noop.cpp Normal file
View File

@@ -0,0 +1,8 @@
// Copyright Antony Polukhin, 2016.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#define BOOST_STACKTRACE_USE_NOOP
#include <boost/stacktrace/detail/stacktrace.ipp>

View File

@@ -20,20 +20,24 @@ project
lib test_unwind_lib : test_impl.cpp : <link>shared : : <library>/boost/stacktrace//boost_stacktrace_libunwind [ check-target-builds ../build//libunwind : : <build>no ] ;
lib test_backtrace_lib : test_impl.cpp : <link>shared : : <library>/boost/stacktrace//boost_stacktrace_backtrace [ check-target-builds ../build//backtrace : : <build>no ] ;
lib test_windbg_lib : test_impl.cpp : <link>shared : : <library>/boost/stacktrace//boost_stacktrace_windbg [ check-target-builds ../build//WinDbg : : <build>no ] ;
lib test_noop_lib : test_impl.cpp : <link>shared : : <library>/boost/stacktrace//boost_stacktrace_noop ;
local HONLY_UNW = <define>BOOST_STACKTRACE_HEADER_ONLY <define>BOOST_STACKTRACE_USE_LIBUNWIND <library>unwind [ check-target-builds ../build//libunwind : : <build>no ] ;
local HONLY_BT = <define>BOOST_STACKTRACE_HEADER_ONLY <define>BOOST_STACKTRACE_USE_BACKTRACE <library>dl [ check-target-builds ../build//backtrace : : <build>no ] ;
local HONLY_WIND = <define>BOOST_STACKTRACE_HEADER_ONLY <define>BOOST_STACKTRACE_USE_WINDBG <library>Dbghelp [ check-target-builds ../build//WinDbg : : <build>no ] ;
local HONLY_NOOP = <define>BOOST_STACKTRACE_HEADER_ONLY <define>BOOST_STACKTRACE_USE_NOOP ;
test-suite stacktrace
:
[ run test.cpp test_impl.cpp : : : $(HONLY_UNW) : libunwind_ho ]
[ run test.cpp test_impl.cpp : : : $(HONLY_BT) : backtrace_ho ]
[ run test.cpp test_impl.cpp : : : $(HONLY_WIND) : windbg_ho ]
[ run test_noop.cpp test_impl.cpp : : : $(HONLY_NOOP) : noop_ho ]
[ run test.cpp : : : <library>.//test_unwind_lib [ check-target-builds ../build//libunwind : : <build>no ] : libunwind_lib ]
[ run test.cpp : : : <library>.//test_backtrace_lib [ check-target-builds ../build//backtrace : : <build>no ] : backtrace_lib ]
[ run test.cpp : : : <library>.//test_windbg_lib [ check-target-builds ../build//WinDbg : : <build>no ] : windbg_lib ]
[ run test_noop.cpp : : : <library>.//test_noop_lib : noop_lib ]
[ run ../example/getting_started.cpp : : : $(HONLY_UNW) : libunwind_getting_started ]
[ run ../example/getting_started.cpp : : : $(HONLY_BT) : backtrace_getting_started ]

View File

@@ -9,13 +9,23 @@
#include <stdexcept>
#include <iostream>
#include <sstream>
#include <cassert>
#include <boost/core/lightweight_test.hpp>
using boost::stacktrace::stacktrace;
BOOST_SYMBOL_IMPORT std::pair<stacktrace, stacktrace> foo2(int i);
BOOST_SYMBOL_IMPORT std::pair<stacktrace, stacktrace> foo1(int i);
BOOST_SYMBOL_IMPORT stacktrace return_from_nested_namespaces();
void test_deeply_nested_namespaces() {
std::stringstream ss;
ss << return_from_nested_namespaces();
std::cout << ss.str() << '\n';
BOOST_TEST(ss.str().find("main") != std::string::npos);
#if !defined(BOOST_STACKTRACE_HEADER_ONLY) || !defined(BOOST_STACKTRACE_USE_BACKTRACE)
BOOST_TEST(ss.str().find("get_backtrace_from_nested_namespaces") != std::string::npos);
#endif
}
void test_nested() {
std::pair<stacktrace, stacktrace> res = foo2(15);
@@ -24,43 +34,30 @@ void test_nested() {
ss1 << res.first;
ss2 << res.second;
std::cout << "'" << ss1.str() << "'\n\n" << ss2.str() << std::endl;
assert(!ss1.str().empty());
assert(!ss2.str().empty());
BOOST_TEST(!ss1.str().empty());
BOOST_TEST(!ss2.str().empty());
assert(ss1.str().find(" 0# ") != std::string::npos);
assert(ss2.str().find(" 0# ") != std::string::npos);
BOOST_TEST(ss1.str().find(" 0# ") != std::string::npos);
BOOST_TEST(ss2.str().find(" 0# ") != std::string::npos);
assert(ss1.str().find(" 1# ") != std::string::npos);
assert(ss2.str().find(" 1# ") != std::string::npos);
BOOST_TEST(ss1.str().find(" 1# ") != std::string::npos);
BOOST_TEST(ss2.str().find(" 1# ") != std::string::npos);
assert(ss1.str().find("main") != std::string::npos);
assert(ss2.str().find("main") != std::string::npos);
assert(ss2.str().find("stacktrace") == std::string::npos);
BOOST_TEST(ss1.str().find("main") != std::string::npos);
BOOST_TEST(ss2.str().find("main") != std::string::npos);
#if !defined(BOOST_STACKTRACE_HEADER_ONLY) || !defined(BOOST_STACKTRACE_USE_BACKTRACE)
assert(ss1.str().find("stacktrace") != std::string::npos);
assert(ss1.str().find("pair") != std::string::npos);
assert(ss1.str().find("foo1") != std::string::npos);
assert(ss1.str().find("foo2") != std::string::npos);
assert(ss2.str().find("foo1") != std::string::npos);
assert(ss2.str().find("foo2") != std::string::npos);
BOOST_TEST(ss1.str().find("foo1") != std::string::npos);
BOOST_TEST(ss1.str().find("foo2") != std::string::npos);
BOOST_TEST(ss2.str().find("foo1") != std::string::npos);
BOOST_TEST(ss2.str().find("foo2") != std::string::npos);
#endif
}
int main() {
std::stringstream ss;
ss << return_from_nested_namespaces();
std::cout << ss.str() << '\n';
assert(ss.str().find("main") != std::string::npos);
assert(ss.str().find("stacktrace") == std::string::npos);
#if !defined(BOOST_STACKTRACE_HEADER_ONLY) || !defined(BOOST_STACKTRACE_USE_BACKTRACE)
assert(ss.str().find("get_backtrace_from_nested_namespaces") != std::string::npos);
#endif
test_deeply_nested_namespaces();
test_nested();
return boost::report_errors();
}

View File

@@ -9,8 +9,8 @@
#include <stdexcept>
using namespace boost::stacktrace;
BOOST_SYMBOL_EXPORT std::pair<stacktrace, stacktrace> foo1(int i);
BOOST_SYMBOL_EXPORT std::pair<stacktrace, stacktrace> foo2(int i);
BOOST_SYMBOL_EXPORT BOOST_NOINLINE std::pair<stacktrace, stacktrace> foo1(int i);
BOOST_SYMBOL_EXPORT BOOST_NOINLINE std::pair<stacktrace, stacktrace> foo2(int i);
std::pair<stacktrace, stacktrace> foo1(int i) {
if (i) {
@@ -45,12 +45,12 @@ namespace very_very_very_very_very_very_long_namespace {
namespace very_very_very_very_very_very_long_namespace {
namespace very_very_very_very_very_very_long_namespace {
namespace very_very_very_very_very_very_long_namespace {
BOOST_SYMBOL_EXPORT stacktrace get_backtrace_from_nested_namespaces() {
BOOST_SYMBOL_EXPORT BOOST_NOINLINE stacktrace get_backtrace_from_nested_namespaces() {
return stacktrace();
}
}}}}}}}}}}
BOOST_SYMBOL_EXPORT stacktrace return_from_nested_namespaces() {
BOOST_SYMBOL_EXPORT BOOST_NOINLINE stacktrace return_from_nested_namespaces() {
using very_very_very_very_very_very_long_namespace::very_very_very_very_very_very_long_namespace::very_very_very_very_very_very_long_namespace
::very_very_very_very_very_very_long_namespace::very_very_very_very_very_very_long_namespace::very_very_very_very_very_very_long_namespace
::very_very_very_very_very_very_long_namespace::very_very_very_very_very_very_long_namespace::very_very_very_very_very_very_long_namespace

36
test/test_noop.cpp Normal file
View File

@@ -0,0 +1,36 @@
// Copyright Antony Polukhin, 2016.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/stacktrace.hpp>
#include <boost/core/lightweight_test.hpp>
using boost::stacktrace::stacktrace;
BOOST_SYMBOL_IMPORT std::pair<stacktrace, stacktrace> foo2(int i);
BOOST_SYMBOL_IMPORT std::pair<stacktrace, stacktrace> foo1(int i);
BOOST_SYMBOL_IMPORT stacktrace return_from_nested_namespaces();
void test_deeply_nested_namespaces() {
BOOST_TEST(return_from_nested_namespaces().size() == 0);
BOOST_TEST(return_from_nested_namespaces()[0] == "");
}
void test_nested() {
std::pair<stacktrace, stacktrace> res = foo2(15);
BOOST_TEST(res.first.size() == 0);
BOOST_TEST(res.first[0] == "");
BOOST_TEST(res.second.size() == 0);
BOOST_TEST(res.second[0] == "");
}
int main() {
test_deeply_nested_namespaces();
test_nested();
return boost::report_errors();
}