From 52d1b9a600ec0e1dfdd149def677055eacd9ad41 Mon Sep 17 00:00:00 2001 From: bergmann Date: Sat, 31 Aug 2019 14:17:15 +0200 Subject: [PATCH] * refactored exception: split stack trace and exception in two separate classes --- include/cppcore/conversion/string.inl | 4 +- include/cppcore/misc/exception.h | 137 ++++++++++++++++-- include/cppcore/misc/exception.inl | 196 +++++++++++++++++++++++--- 3 files changed, 303 insertions(+), 34 deletions(-) diff --git a/include/cppcore/conversion/string.inl b/include/cppcore/conversion/string.inl index d3b3dbd..be9cafb 100644 --- a/include/cppcore/conversion/string.inl +++ b/include/cppcore/conversion/string.inl @@ -55,7 +55,7 @@ namespace cppcore || *i == '\0')) { std::string tmp(s, convert_cast(i - s)); - if (!predicate(tmp)) + if (!predicate(std::move(tmp))) return false; s = i + 1; } @@ -436,7 +436,7 @@ namespace cppcore inline bool operator()(const std::string& str, std::list& value) const { std::list tmp; - auto ret = string_split(str, ',', [&tmp](auto& s) { + auto ret = string_split(str, ',', [&tmp](auto&& s) { tmp.emplace_back(); return try_from_string(s, tmp.back()); }); diff --git a/include/cppcore/misc/exception.h b/include/cppcore/misc/exception.h index b113b7f..add5aad 100644 --- a/include/cppcore/misc/exception.h +++ b/include/cppcore/misc/exception.h @@ -11,27 +11,137 @@ namespace cppcore { - /** - * @brief Basic exception with stack trace and some more small features. - */ - struct exception - : public std::exception + template + struct stack_trace { public: enum class print_flag { - print_trace, - resolve_address, + print_trace, //!< print the stack trace + print_number, //!< add a number before the stack entry + resolve_symbol, //!< resolve the symbol + resolve_filename, //!< resolve the filename }; using print_flags = shifted_flags; + private: + using symbols = std::unique_ptr; + + private: + void* _stack[N]; //!< Stack addresses. + size_t _stack_size; //!< Number of elements actually stored in _stack. + mutable symbols _symbols; //!< List of resolved symbols. + + public: + static const size_t max_stack_size = N; + + /** + * @brief Constructor. + */ + inline stack_trace(); + + /** + * @brief Move constructor. + */ + inline stack_trace(stack_trace&& other); + + /** + * @brief Copy constructor. + */ + inline stack_trace(const stack_trace& other); + + /** + * @brief Move assignment constructor. + */ + inline stack_trace& operator=(stack_trace&& other); + + /** + * @brief Copy assigment constructor. + */ + inline stack_trace& operator=(const stack_trace& other); + + /** + * @brief Get the number of stack entries. + */ + inline size_t size() const; + + /** + * @brief Get the stack entry at the given index. + */ + inline void* address(size_t index) const; + + /** + * @brief Get the symbol at the given index. + */ + inline const char* symbol(size_t index) const; + + /** + * @brief Get the filename at the given index. + */ + inline std::string filename(size_t index) const; + + /** + * @brief Print the whole stack trace to the passed stream. + * + * @param[in] os Stream to print exception to. + * @param[in] flags Flags to use for printing. + */ + inline void print( + std::ostream& os, + const print_flags& flags) const; + + /** + * @brief Convert the stack trace to string. + * + * @param[in] flags Flags to use for printing. + * + * @return String representation of the exception. + */ + inline std::string print( + const print_flags& flags) const; + + /** + * @brief Print the whole stack trace to the passed stream. + * + * @param[in] os Stream to print exception to. + * @param[in] index Index of trace element to print. + * @param[in] flags Flags to use for printing. + */ + inline void print( + std::ostream& os, + size_t index, + const print_flags& flags) const; + + /** + * @brief Convert the stack trace to string. + * + * @param[in] index Index of trace element to print. + * @param[in] flags Flags to use for printing. + * + * @return String representation of the exception. + */ + inline std::string print( + size_t index, + const print_flags& flags) const; + }; + + /** + * @brief Basic exception with stack trace and some more small features. + */ + struct exception + : public std::exception + { public: static constexpr size_t max_stack_size = 15; + using stack_trace = cppcore::stack_trace; + using print_flag = typename stack_trace::print_flag; + using print_flags = typename stack_trace::print_flags; + /** - * @brief Get the default print flags. + * @brief Get the default exception print flags. */ - static inline const print_flags& default_print_flag(); + static inline const print_flags& default_print_flags(); /** * @brief Print the exception to the passed stream; @@ -48,9 +158,8 @@ namespace cppcore mutable std::string _msg_cache; //!< Caches the message of the exception. public: - std::string message; //!< message of the exception - void* stack[max_stack_size]; //!< stack trace - int stack_size; //!< number of entries stored in stack + std::string message; //!< message of the exception + stack_trace trace; //!< stack trace public: /** @@ -76,7 +185,7 @@ namespace cppcore */ inline void print( std::ostream& os, - const print_flags& flags = default_print_flag()) const; + const stack_trace::print_flags& flags = default_print_flags()) const; /** * @brief Convert the exception to string. @@ -86,7 +195,7 @@ namespace cppcore * @return String representation of the exception. */ inline std::string print( - const print_flags& flags = default_print_flag()) const; + const stack_trace::print_flags& flags = default_print_flags()) const; /** * @brief Convert the exception to a string. diff --git a/include/cppcore/misc/exception.inl b/include/cppcore/misc/exception.inl index 75cccca..0b60153 100644 --- a/include/cppcore/misc/exception.inl +++ b/include/cppcore/misc/exception.inl @@ -1,17 +1,185 @@ #pragma once +#include + #include "exception.h" namespace cppcore { + /* stack_trace */ + + template + stack_trace::stack_trace() + : _stack_size (static_cast(backtrace(&_stack[0], max_stack_size))) + , _symbols (nullptr, &free) + { } + + template + stack_trace::stack_trace(stack_trace&& other) + : _stack_size (std::move(other)._stack_size) + , _symbols (std::move(other)._symbols) + { memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); } + + template + stack_trace::stack_trace(const stack_trace& other) + : _stack_size (std::move(other)._stack_size) + , _symbols (nullptr, &free) + { memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); } + + template + stack_trace& stack_trace::operator=(stack_trace&& other) + { + _symbols = std::move(other)._symbols; + _stack_size = std::move(other)._stack_size; + memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); + } + + template + stack_trace& stack_trace::operator=(const stack_trace& other) + { + _symbols.reset(); + _stack_size = other._stack_size; + memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); + } + + template + size_t stack_trace::size() const + { return _stack_size; } + + template + void* stack_trace::address(size_t index) const + { + return index >= _stack_size + ? nullptr + : _stack[index]; + } + + template + const char* stack_trace::symbol(size_t index) const + { + if (!_symbols) + _symbols.reset(backtrace_symbols(_stack, static_cast(_stack_size))); + return index >= _stack_size + ? nullptr + : _symbols.get()[index]; + } + + template + std::string stack_trace::filename(size_t index) const + { + #if defined(__linux__) + /* get the symbol */ + auto line = symbol(index); + if (!line) + return std::string(); + + /* find the filename of the symbol */ + int p = 0; + while ( line[p] != '(' + && line[p] != ' ' + && line[p] != 0) + ++p; + + /* build the command */ + char cmd[256]; + sprintf(cmd, "addr2line %p -e %.*s", _stack[index], p, line); + + /* execute the command */ + std::array buffer; + std::string result; + std::unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) + return std::string(); + + /* collect the output */ + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) + result += buffer.data(); + + /* remove trailing whitespaces */ + while ( !result.empty() + && ( result.back() == '\n' + || result.back() == '\r' + || result.back() == '\t' + || result.back() == ' ')) + result.pop_back(); + + return result; + #else + return std::string(); + #endif + } + + template + void stack_trace::print(std::ostream& os, const print_flags& flags) const + { + for (size_t i = 0; i < size(); ++i) + print(os, i, flags); + } + + template + std::string stack_trace::print(const print_flags& flags) const + { + std::ostringstream os; + print(os, flags); + return os.str(); + } + + template + void stack_trace::print( + std::ostream& os, + size_t index, + const print_flags& flags) const + { + os << " "; + if (flags.is_set(print_flag::print_number)) + { + os << "#" + << std::setw(2) + << std::setfill('0') + << index + << ' '; + } + os << "[0x" << std::setw(2 * sizeof(void*)) + << std::setfill('0') + << std::hex + << reinterpret_cast(address(index)) << "]"; + if (flags.is_set(print_flag::resolve_symbol)) + { + os << " " + << symbol(index); + } + if (flags.is_set(print_flag::resolve_filename)) + { + auto str = filename(index); + if (!str.empty()) + { + if (flags.is_set(print_flag::resolve_symbol)) + os << std::endl << " "; + os << " " << filename(index); + } + } + os << std::endl; + } + + template + std::string stack_trace::print( + size_t index, + const print_flags& flags) const + { + std::ostringstream os; + print(os, index, flags); + return os.str(); + } + /* exception */ - const exception::print_flags& exception::default_print_flag() + const exception::print_flags& exception::default_print_flags() { static const print_flags value({ print_flag::print_trace, - print_flag::resolve_address, + print_flag::print_number, + print_flag::resolve_symbol, }); return value; } @@ -22,31 +190,23 @@ namespace cppcore exception::exception(std::string msg) : message (msg) - , stack_size (0) , _msg_cache_empty (true) - { - stack_size = backtrace(&stack[0], max_stack_size); - } + { } - void exception::print(std::ostream& os, const print_flags& flags) const + void exception::print( + std::ostream& os, + const stack_trace::print_flags& flags) const { print_message(os); - if (!flags.is_set(print_flag::print_trace)) - return; - os << std::endl; - char** lines = nullptr; - if (flags.is_set(print_flag::resolve_address)) - lines = backtrace_symbols(stack, stack_size); - for (int i = 0; i < stack_size; ++i) + if (flags.is_set(print_flag::print_trace)) { - os << " [0x" << std::setw(2 * sizeof(void*)) << std::setfill('0') << std::hex << reinterpret_cast(stack[i]) << "]"; - if (lines && lines[i]) - os << " " << lines[i]; os << std::endl; + trace.print(os, flags); } } - std::string exception::print(const print_flags& flags) const + std::string exception::print( + const stack_trace::print_flags& flags) const { std::ostringstream os; print(os, flags);