93 lines
2.3 KiB
C++
93 lines
2.3 KiB
C++
#pragma once
|
|
|
|
#include <cxxabi.h>
|
|
#include <execinfo.h>
|
|
#include <fmt/format.h>
|
|
#include <stdexcept>
|
|
|
|
#include "utils/on_scope_exit.hpp"
|
|
|
|
namespace utils {
|
|
|
|
class Stacktrace {
|
|
public:
|
|
class Line {
|
|
public:
|
|
// cppcheck-suppress noExplicitConstructor
|
|
Line(const std::string &original) : original(original) {}
|
|
|
|
Line(const std::string &original, const std::string &function, const std::string &location)
|
|
: original(original), function(function), location(location) {}
|
|
|
|
std::string original, function, location;
|
|
};
|
|
|
|
static constexpr size_t stacktrace_depth = 128;
|
|
|
|
Stacktrace() {
|
|
void *addresses[stacktrace_depth];
|
|
auto depth = backtrace(addresses, stacktrace_depth);
|
|
|
|
// will this leak if backtrace_symbols throws?
|
|
char **symbols = nullptr;
|
|
utils::OnScopeExit on_exit([&symbols]() { free(symbols); });
|
|
|
|
symbols = backtrace_symbols(addresses, depth);
|
|
|
|
// skip the first one since it will be Stacktrace::Stacktrace()
|
|
for (int i = 1; i < depth; ++i) lines.emplace_back(format(symbols[i]));
|
|
}
|
|
|
|
auto begin() { return lines.begin(); }
|
|
auto begin() const { return lines.begin(); }
|
|
auto cbegin() const { return lines.cbegin(); }
|
|
|
|
auto end() { return lines.end(); }
|
|
auto end() const { return lines.end(); }
|
|
auto cend() const { return lines.cend(); }
|
|
|
|
const Line &operator[](size_t idx) const { return lines[idx]; }
|
|
|
|
size_t size() const { return lines.size(); }
|
|
|
|
template <class Stream>
|
|
void dump(Stream &stream) {
|
|
stream << dump();
|
|
}
|
|
|
|
std::string dump() {
|
|
std::string message;
|
|
for (size_t i = 0; i < size(); i++) {
|
|
message.append(fmt::format("at {} ({}) \n", lines[i].function, lines[i].location));
|
|
}
|
|
return message;
|
|
}
|
|
|
|
private:
|
|
std::vector<Line> lines;
|
|
|
|
Line format(const std::string &original) {
|
|
using namespace abi;
|
|
auto line = original;
|
|
|
|
auto begin = line.find('(');
|
|
auto end = line.find('+');
|
|
|
|
if (begin == std::string::npos || end == std::string::npos) return {original};
|
|
|
|
line[end] = '\0';
|
|
|
|
int s;
|
|
auto demangled = __cxa_demangle(line.data() + begin + 1, nullptr, nullptr, &s);
|
|
|
|
auto location = line.substr(0, begin);
|
|
|
|
auto function =
|
|
demangled ? std::string(demangled) : fmt::format("{}()", original.substr(begin + 1, end - begin - 1));
|
|
|
|
return {original, function, location};
|
|
}
|
|
};
|
|
|
|
} // namespace utils
|