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(); +}