From 6522b60c72a16749be15750fa1f6962038dc3046 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Thu, 8 Sep 2016 21:42:43 +0300 Subject: [PATCH] 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 --- build/Jamfile.v2 | 12 ++++ include/boost/stacktrace.hpp | 4 +- .../boost/stacktrace/detail/stacktrace.ipp | 4 +- .../detail/stacktrace_libunwind.hpp | 21 ++----- .../stacktrace/detail/stacktrace_linux.hpp | 11 +--- .../stacktrace/detail/stacktrace_noop.hpp | 29 ++++++++++ .../stacktrace/detail/stacktrace_windows.hpp | 2 +- src/noop.cpp | 8 +++ test/Jamfile.v2 | 4 ++ test/test.cpp | 55 +++++++++---------- test/test_impl.cpp | 8 +-- test/test_noop.cpp | 36 ++++++++++++ 12 files changed, 131 insertions(+), 63 deletions(-) create mode 100644 include/boost/stacktrace/detail/stacktrace_noop.hpp create mode 100644 src/noop.cpp create mode 100644 test/test_noop.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 08a2745..7fb905b 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -33,6 +33,18 @@ explicit backtrace ; mp-run-simple has_windbg.cpp : : : : WinDbg ; explicit WinDbg ; +lib boost_stacktrace_noop + : # sources + ../src/noop.cpp + : # requirements + all + : # default build + : # usage-requirements + shared:BOOST_TEST_DYN_LINK=1 + ; + +boost-install boost_stacktrace_noop ; + lib boost_stacktrace_libunwind : # sources ../src/libunwind.cpp diff --git a/include/boost/stacktrace.hpp b/include/boost/stacktrace.hpp index 3271587..0e9b19d 100644 --- a/include/boost/stacktrace.hpp +++ b/include/boost/stacktrace.hpp @@ -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; }; diff --git a/include/boost/stacktrace/detail/stacktrace.ipp b/include/boost/stacktrace/detail/stacktrace.ipp index 8332e57..9d58635 100644 --- a/include/boost/stacktrace/detail/stacktrace.ipp +++ b/include/boost/stacktrace/detail/stacktrace.ipp @@ -15,7 +15,9 @@ #include #include -#if defined(BOOST_WINDOWS) || defined(BOOST_STACKTRACE_USE_WINDBG) +#if defined(BOOST_STACKTRACE_USE_NOOP) +# include +#elif defined(BOOST_WINDOWS) || defined(BOOST_STACKTRACE_USE_WINDBG) # include #elif defined(BOOST_STACKTRACE_USE_LIBUNWIND) # include diff --git a/include/boost/stacktrace/detail/stacktrace_libunwind.hpp b/include/boost/stacktrace/detail/stacktrace_libunwind.hpp index 547c4c1..547a17e 100644 --- a/include/boost/stacktrace/detail/stacktrace_libunwind.hpp +++ b/include/boost/stacktrace/detail/stacktrace_libunwind.hpp @@ -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 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(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]; diff --git a/include/boost/stacktrace/detail/stacktrace_linux.hpp b/include/boost/stacktrace/detail/stacktrace_linux.hpp index 0a048d9..3fcf15c 100644 --- a/include/boost/stacktrace/detail/stacktrace_linux.hpp +++ b/include/boost/stacktrace/detail/stacktrace_linux.hpp @@ -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; } diff --git a/include/boost/stacktrace/detail/stacktrace_noop.hpp b/include/boost/stacktrace/detail/stacktrace_noop.hpp new file mode 100644 index 0000000..f7638d5 --- /dev/null +++ b/include/boost/stacktrace/detail/stacktrace_noop.hpp @@ -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 +#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 diff --git a/include/boost/stacktrace/detail/stacktrace_windows.hpp b/include/boost/stacktrace/detail/stacktrace_windows.hpp index 0cde56d..23f5ddf 100644 --- a/include/boost/stacktrace/detail/stacktrace_windows.hpp +++ b/include/boost/stacktrace/detail/stacktrace_windows.hpp @@ -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()) { diff --git a/src/noop.cpp b/src/noop.cpp new file mode 100644 index 0000000..705e12a --- /dev/null +++ b/src/noop.cpp @@ -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 diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 1d950db..63b59e6 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -20,20 +20,24 @@ project lib test_unwind_lib : test_impl.cpp : shared : : /boost/stacktrace//boost_stacktrace_libunwind [ check-target-builds ../build//libunwind : : no ] ; lib test_backtrace_lib : test_impl.cpp : shared : : /boost/stacktrace//boost_stacktrace_backtrace [ check-target-builds ../build//backtrace : : no ] ; lib test_windbg_lib : test_impl.cpp : shared : : /boost/stacktrace//boost_stacktrace_windbg [ check-target-builds ../build//WinDbg : : no ] ; +lib test_noop_lib : test_impl.cpp : shared : : /boost/stacktrace//boost_stacktrace_noop ; local HONLY_UNW = BOOST_STACKTRACE_HEADER_ONLY BOOST_STACKTRACE_USE_LIBUNWIND unwind [ check-target-builds ../build//libunwind : : no ] ; local HONLY_BT = BOOST_STACKTRACE_HEADER_ONLY BOOST_STACKTRACE_USE_BACKTRACE dl [ check-target-builds ../build//backtrace : : no ] ; local HONLY_WIND = BOOST_STACKTRACE_HEADER_ONLY BOOST_STACKTRACE_USE_WINDBG Dbghelp [ check-target-builds ../build//WinDbg : : no ] ; +local HONLY_NOOP = BOOST_STACKTRACE_HEADER_ONLY 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 : : : .//test_unwind_lib [ check-target-builds ../build//libunwind : : no ] : libunwind_lib ] [ run test.cpp : : : .//test_backtrace_lib [ check-target-builds ../build//backtrace : : no ] : backtrace_lib ] [ run test.cpp : : : .//test_windbg_lib [ check-target-builds ../build//WinDbg : : no ] : windbg_lib ] + [ run test_noop.cpp : : : .//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 ] diff --git a/test/test.cpp b/test/test.cpp index db9bdf1..8c4ffbe 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -9,13 +9,23 @@ #include #include #include -#include +#include using boost::stacktrace::stacktrace; BOOST_SYMBOL_IMPORT std::pair foo2(int i); BOOST_SYMBOL_IMPORT std::pair 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 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(); } diff --git a/test/test_impl.cpp b/test/test_impl.cpp index a83ab2f..9919d3a 100644 --- a/test/test_impl.cpp +++ b/test/test_impl.cpp @@ -9,8 +9,8 @@ #include using namespace boost::stacktrace; -BOOST_SYMBOL_EXPORT std::pair foo1(int i); -BOOST_SYMBOL_EXPORT std::pair foo2(int i); +BOOST_SYMBOL_EXPORT BOOST_NOINLINE std::pair foo1(int i); +BOOST_SYMBOL_EXPORT BOOST_NOINLINE std::pair foo2(int i); std::pair 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 diff --git a/test/test_noop.cpp b/test/test_noop.cpp new file mode 100644 index 0000000..8b997f0 --- /dev/null +++ b/test/test_noop.cpp @@ -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 +#include + +using boost::stacktrace::stacktrace; +BOOST_SYMBOL_IMPORT std::pair foo2(int i); +BOOST_SYMBOL_IMPORT std::pair 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 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(); +}