|
- #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_flags()
- {
- static const print_flags value({
- print_flag::print_trace,
- print_flag::print_number,
- print_flag::resolve_symbol,
- });
- return value;
- }
-
- exception::exception()
- : exception("")
- { }
-
- exception::exception(std::string msg)
- : message (msg)
- , _msg_cache_empty (true)
- { }
-
- void exception::print(
- std::ostream& os,
- const stack_trace::print_flags& flags) const
- {
- print_message(os);
- if (flags.is_set(print_flag::print_trace))
- {
- os << std::endl;
- trace.print(os, flags);
- }
- }
-
- std::string exception::print(
- const stack_trace::print_flags& flags) const
- {
- std::ostringstream os;
- print(os, flags);
- return os.str();
- }
-
- const std::string& exception::to_string() const
- {
- if (_msg_cache_empty)
- {
- _msg_cache = print();
- _msg_cache_empty = false;
- }
- return _msg_cache;
- }
-
- const char* exception::what() const throw()
- { return to_string().c_str(); }
-
- void exception::print_message(std::ostream& os) const
- {
- os << message;
- }
-
- std::ostream& operator <<(std::ostream& os, const exception& ex)
- {
- ex.print(os);
- return os;
- }
-
- /* error_exception */
-
- error_exception::error_exception(int e)
- : exception (std::to_string(e) + " - " + strerror(e))
- , error (e)
- { }
-
- error_exception::error_exception(const std::string& msg, int e) :
- exception(msg + ": " + std::to_string(e) + " - " + strerror(e)),
- error(e)
- { }
-
- /* range_exception */
-
- range_exception::range_exception(size_t mi, size_t ma, size_t idx, std::string msg)
- : exception (msg)
- , min (mi)
- , max (ma)
- , index (idx)
- { }
-
- void range_exception::print_message(std::ostream& os) const
- {
- os << "index out of range (min=" << min << "; max=" << max << "; index=" << index << ")";
- if (!message.empty())
- os << " - " << message;
- }
-
- /* argument_exception */
-
- argument_exception::argument_exception(std::string arg, std::string msg)
- : exception (msg)
- , argument (arg)
- { }
-
- void argument_exception::print_message(std::ostream& os) const
- {
- os << "invalid argument";
- if (!argument.empty())
- os << "(" << argument << ")";
- if (!message.empty())
- os << " - " << message;
- }
-
- }
|