Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

285 рядки
7.4 KiB

  1. #pragma once
  2. #include <memory>
  3. #include "exception.h"
  4. namespace cppcore
  5. {
  6. /* stack_trace */
  7. template<size_t N>
  8. stack_trace<N>::stack_trace()
  9. : _stack_size (static_cast<size_t>(backtrace(&_stack[0], max_stack_size)))
  10. , _symbols (nullptr, &free)
  11. { }
  12. template<size_t N>
  13. stack_trace<N>::stack_trace(stack_trace&& other)
  14. : _stack_size (std::move(other)._stack_size)
  15. , _symbols (std::move(other)._symbols)
  16. { memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); }
  17. template<size_t N>
  18. stack_trace<N>::stack_trace(const stack_trace& other)
  19. : _stack_size (std::move(other)._stack_size)
  20. , _symbols (nullptr, &free)
  21. { memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0])); }
  22. template<size_t N>
  23. stack_trace<N>& stack_trace<N>::operator=(stack_trace&& other)
  24. {
  25. _symbols = std::move(other)._symbols;
  26. _stack_size = std::move(other)._stack_size;
  27. memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0]));
  28. }
  29. template<size_t N>
  30. stack_trace<N>& stack_trace<N>::operator=(const stack_trace& other)
  31. {
  32. _symbols.reset();
  33. _stack_size = other._stack_size;
  34. memcpy(&_stack[0], &other._stack[0], _stack_size * sizeof(_stack[0]));
  35. }
  36. template<size_t N>
  37. size_t stack_trace<N>::size() const
  38. { return _stack_size; }
  39. template<size_t N>
  40. void* stack_trace<N>::address(size_t index) const
  41. {
  42. return index >= _stack_size
  43. ? nullptr
  44. : _stack[index];
  45. }
  46. template<size_t N>
  47. const char* stack_trace<N>::symbol(size_t index) const
  48. {
  49. if (!_symbols)
  50. _symbols.reset(backtrace_symbols(_stack, static_cast<int>(_stack_size)));
  51. return index >= _stack_size
  52. ? nullptr
  53. : _symbols.get()[index];
  54. }
  55. template<size_t N>
  56. std::string stack_trace<N>::filename(size_t index) const
  57. {
  58. #if defined(__linux__)
  59. /* get the symbol */
  60. auto line = symbol(index);
  61. if (!line)
  62. return std::string();
  63. /* find the filename of the symbol */
  64. int p = 0;
  65. while ( line[p] != '('
  66. && line[p] != ' '
  67. && line[p] != 0)
  68. ++p;
  69. /* build the command */
  70. char cmd[256];
  71. sprintf(cmd, "addr2line %p -e %.*s", _stack[index], p, line);
  72. /* execute the command */
  73. std::array<char, 128> buffer;
  74. std::string result;
  75. std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
  76. if (!pipe)
  77. return std::string();
  78. /* collect the output */
  79. while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
  80. result += buffer.data();
  81. /* remove trailing whitespaces */
  82. while ( !result.empty()
  83. && ( result.back() == '\n'
  84. || result.back() == '\r'
  85. || result.back() == '\t'
  86. || result.back() == ' '))
  87. result.pop_back();
  88. return result;
  89. #else
  90. return std::string();
  91. #endif
  92. }
  93. template<size_t N>
  94. void stack_trace<N>::print(std::ostream& os, const print_flags& flags) const
  95. {
  96. for (size_t i = 0; i < size(); ++i)
  97. print(os, i, flags);
  98. }
  99. template<size_t N>
  100. std::string stack_trace<N>::print(const print_flags& flags) const
  101. {
  102. std::ostringstream os;
  103. print(os, flags);
  104. return os.str();
  105. }
  106. template<size_t N>
  107. void stack_trace<N>::print(
  108. std::ostream& os,
  109. size_t index,
  110. const print_flags& flags) const
  111. {
  112. os << " ";
  113. if (flags.is_set(print_flag::print_number))
  114. {
  115. os << "#"
  116. << std::setw(2)
  117. << std::setfill('0')
  118. << index
  119. << ' ';
  120. }
  121. os << "[0x" << std::setw(2 * sizeof(void*))
  122. << std::setfill('0')
  123. << std::hex
  124. << reinterpret_cast<uintptr_t>(address(index)) << "]";
  125. if (flags.is_set(print_flag::resolve_symbol))
  126. {
  127. os << " "
  128. << symbol(index);
  129. }
  130. if (flags.is_set(print_flag::resolve_filename))
  131. {
  132. auto str = filename(index);
  133. if (!str.empty())
  134. {
  135. if (flags.is_set(print_flag::resolve_symbol))
  136. os << std::endl << " ";
  137. os << " " << filename(index);
  138. }
  139. }
  140. os << std::endl;
  141. }
  142. template<size_t N>
  143. std::string stack_trace<N>::print(
  144. size_t index,
  145. const print_flags& flags) const
  146. {
  147. std::ostringstream os;
  148. print(os, index, flags);
  149. return os.str();
  150. }
  151. /* exception */
  152. const exception::print_flags& exception::default_print_flags()
  153. {
  154. static const print_flags value({
  155. print_flag::print_trace,
  156. print_flag::print_number,
  157. print_flag::resolve_symbol,
  158. });
  159. return value;
  160. }
  161. exception::exception()
  162. : exception("")
  163. { }
  164. exception::exception(std::string msg)
  165. : message (msg)
  166. , _msg_cache_empty (true)
  167. { }
  168. void exception::print(
  169. std::ostream& os,
  170. const stack_trace::print_flags& flags) const
  171. {
  172. print_message(os);
  173. if (flags.is_set(print_flag::print_trace))
  174. {
  175. os << std::endl;
  176. trace.print(os, flags);
  177. }
  178. }
  179. std::string exception::print(
  180. const stack_trace::print_flags& flags) const
  181. {
  182. std::ostringstream os;
  183. print(os, flags);
  184. return os.str();
  185. }
  186. const std::string& exception::to_string() const
  187. {
  188. if (_msg_cache_empty)
  189. {
  190. _msg_cache = print();
  191. _msg_cache_empty = false;
  192. }
  193. return _msg_cache;
  194. }
  195. const char* exception::what() const throw()
  196. { return to_string().c_str(); }
  197. void exception::print_message(std::ostream& os) const
  198. {
  199. os << message;
  200. }
  201. std::ostream& operator <<(std::ostream& os, const exception& ex)
  202. {
  203. ex.print(os);
  204. return os;
  205. }
  206. /* error_exception */
  207. error_exception::error_exception(int e)
  208. : exception (std::to_string(e) + " - " + strerror(e))
  209. , error (e)
  210. { }
  211. error_exception::error_exception(const std::string& msg, int e) :
  212. exception(msg + ": " + std::to_string(e) + " - " + strerror(e)),
  213. error(e)
  214. { }
  215. /* range_exception */
  216. range_exception::range_exception(size_t mi, size_t ma, size_t idx, std::string msg)
  217. : exception (msg)
  218. , min (mi)
  219. , max (ma)
  220. , index (idx)
  221. { }
  222. void range_exception::print_message(std::ostream& os) const
  223. {
  224. os << "index out of range (min=" << min << "; max=" << max << "; index=" << index << ")";
  225. if (!message.empty())
  226. os << " - " << message;
  227. }
  228. /* argument_exception */
  229. argument_exception::argument_exception(std::string arg, std::string msg)
  230. : exception (msg)
  231. , argument (arg)
  232. { }
  233. void argument_exception::print_message(std::ostream& os) const
  234. {
  235. os << "invalid argument";
  236. if (!argument.empty())
  237. os << "(" << argument << ")";
  238. if (!message.empty())
  239. os << " - " << message;
  240. }
  241. }