|
|
@@ -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); |
|
|
|