Browse Source

* refactored exception: split stack trace and exception in two separate classes

master
bergmann 4 years ago
parent
commit
52d1b9a600
3 changed files with 303 additions and 34 deletions
  1. +2
    -2
      include/cppcore/conversion/string.inl
  2. +123
    -14
      include/cppcore/misc/exception.h
  3. +178
    -18
      include/cppcore/misc/exception.inl

+ 2
- 2
include/cppcore/conversion/string.inl View File

@@ -55,7 +55,7 @@ namespace cppcore
|| *i == '\0'))
{
std::string tmp(s, convert_cast<size_t>(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<T>& value) const
{
std::list<T> 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());
});


+ 123
- 14
include/cppcore/misc/exception.h View File

@@ -11,27 +11,137 @@
namespace cppcore
{

/**
* @brief Basic exception with stack trace and some more small features.
*/
struct exception
: public std::exception
template<size_t N>
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<print_flag>;

private:
using symbols = std::unique_ptr<char*, decltype(&free)>;

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<max_stack_size>;
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.


+ 178
- 18
include/cppcore/misc/exception.inl View File

@@ -1,17 +1,185 @@
#pragma once

#include <memory>

#include "exception.h"

namespace cppcore
{

/* stack_trace */

template<size_t N>
stack_trace<N>::stack_trace()
: _stack_size (static_cast<size_t>(backtrace(&_stack[0], max_stack_size)))
, _symbols (nullptr, &free)
{ }

template<size_t N>
stack_trace<N>::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<size_t N>
stack_trace<N>::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<size_t N>
stack_trace<N>& stack_trace<N>::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<size_t N>
stack_trace<N>& stack_trace<N>::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 N>
size_t stack_trace<N>::size() const
{ return _stack_size; }

template<size_t N>
void* stack_trace<N>::address(size_t index) const
{
return index >= _stack_size
? nullptr
: _stack[index];
}

template<size_t N>
const char* stack_trace<N>::symbol(size_t index) const
{
if (!_symbols)
_symbols.reset(backtrace_symbols(_stack, static_cast<int>(_stack_size)));
return index >= _stack_size
? nullptr
: _symbols.get()[index];
}

template<size_t N>
std::string stack_trace<N>::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<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> 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<size_t N>
void stack_trace<N>::print(std::ostream& os, const print_flags& flags) const
{
for (size_t i = 0; i < size(); ++i)
print(os, i, flags);
}

template<size_t N>
std::string stack_trace<N>::print(const print_flags& flags) const
{
std::ostringstream os;
print(os, flags);
return os.str();
}

template<size_t N>
void stack_trace<N>::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<uintptr_t>(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<size_t N>
std::string stack_trace<N>::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<uintptr_t>(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);


Loading…
Cancel
Save