Split up cpptrace.hpp
This commit is contained in:
parent
c95ab97a48
commit
4c59e73a01
@ -226,7 +226,10 @@ target_sources(
|
|||||||
src/binary/safe_dl.cpp
|
src/binary/safe_dl.cpp
|
||||||
src/cpptrace.cpp
|
src/cpptrace.cpp
|
||||||
src/ctrace.cpp
|
src/ctrace.cpp
|
||||||
|
src/exceptions.cpp
|
||||||
src/from_current.cpp
|
src/from_current.cpp
|
||||||
|
src/options.cpp
|
||||||
|
src/utils.cpp
|
||||||
src/demangle/demangle_with_cxxabi.cpp
|
src/demangle/demangle_with_cxxabi.cpp
|
||||||
src/demangle/demangle_with_nothing.cpp
|
src/demangle/demangle_with_nothing.cpp
|
||||||
src/demangle/demangle_with_winapi.cpp
|
src/demangle/demangle_with_winapi.cpp
|
||||||
|
|||||||
234
include/cpptrace/basic.hpp
Normal file
234
include/cpptrace/basic.hpp
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
#ifndef CPPTRACE_BASIC_HPP
|
||||||
|
#define CPPTRACE_BASIC_HPP
|
||||||
|
|
||||||
|
#include <cpptrace/forward.hpp>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define CPPTRACE_EXPORT_ATTR __declspec(dllexport)
|
||||||
|
#define CPPTRACE_IMPORT_ATTR __declspec(dllimport)
|
||||||
|
#else
|
||||||
|
#define CPPTRACE_EXPORT_ATTR __attribute__((visibility("default")))
|
||||||
|
#define CPPTRACE_IMPORT_ATTR __attribute__((visibility("default")))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CPPTRACE_STATIC_DEFINE
|
||||||
|
# define CPPTRACE_EXPORT
|
||||||
|
# define CPPTRACE_NO_EXPORT
|
||||||
|
#else
|
||||||
|
# ifndef CPPTRACE_EXPORT
|
||||||
|
# ifdef cpptrace_lib_EXPORTS
|
||||||
|
/* We are building this library */
|
||||||
|
# define CPPTRACE_EXPORT CPPTRACE_EXPORT_ATTR
|
||||||
|
# else
|
||||||
|
/* We are using this library */
|
||||||
|
# define CPPTRACE_EXPORT CPPTRACE_IMPORT_ATTR
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define CPPTRACE_FORCE_NO_INLINE __declspec(noinline)
|
||||||
|
#else
|
||||||
|
#define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
||||||
|
// reason
|
||||||
|
// 4275 is the same thing but for base classes
|
||||||
|
#pragma warning(disable: 4251; disable: 4275)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
struct CPPTRACE_EXPORT raw_trace {
|
||||||
|
std::vector<frame_ptr> frames;
|
||||||
|
static raw_trace current(std::size_t skip = 0);
|
||||||
|
static raw_trace current(std::size_t skip, std::size_t max_depth);
|
||||||
|
object_trace resolve_object_trace() const;
|
||||||
|
stacktrace resolve() const;
|
||||||
|
void clear();
|
||||||
|
bool empty() const noexcept;
|
||||||
|
|
||||||
|
using iterator = std::vector<frame_ptr>::iterator;
|
||||||
|
using const_iterator = std::vector<frame_ptr>::const_iterator;
|
||||||
|
inline iterator begin() noexcept { return frames.begin(); }
|
||||||
|
inline iterator end() noexcept { return frames.end(); }
|
||||||
|
inline const_iterator begin() const noexcept { return frames.begin(); }
|
||||||
|
inline const_iterator end() const noexcept { return frames.end(); }
|
||||||
|
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
||||||
|
inline const_iterator cend() const noexcept { return frames.cend(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPPTRACE_EXPORT object_frame {
|
||||||
|
frame_ptr raw_address;
|
||||||
|
frame_ptr object_address;
|
||||||
|
std::string object_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPPTRACE_EXPORT object_trace {
|
||||||
|
std::vector<object_frame> frames;
|
||||||
|
static object_trace current(std::size_t skip = 0);
|
||||||
|
static object_trace current(std::size_t skip, std::size_t max_depth);
|
||||||
|
stacktrace resolve() const;
|
||||||
|
void clear();
|
||||||
|
bool empty() const noexcept;
|
||||||
|
|
||||||
|
using iterator = std::vector<object_frame>::iterator;
|
||||||
|
using const_iterator = std::vector<object_frame>::const_iterator;
|
||||||
|
inline iterator begin() noexcept { return frames.begin(); }
|
||||||
|
inline iterator end() noexcept { return frames.end(); }
|
||||||
|
inline const_iterator begin() const noexcept { return frames.begin(); }
|
||||||
|
inline const_iterator end() const noexcept { return frames.end(); }
|
||||||
|
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
||||||
|
inline const_iterator cend() const noexcept { return frames.cend(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// This represents a nullable integer type
|
||||||
|
// The max value of the type is used as a sentinel
|
||||||
|
// This is used over std::optional because the library is C++11 and also std::optional is a bit heavy-duty for this
|
||||||
|
// use.
|
||||||
|
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
|
||||||
|
struct nullable {
|
||||||
|
T raw_value;
|
||||||
|
nullable& operator=(T value) {
|
||||||
|
raw_value = value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool has_value() const noexcept {
|
||||||
|
return raw_value != (std::numeric_limits<T>::max)();
|
||||||
|
}
|
||||||
|
T& value() noexcept {
|
||||||
|
return raw_value;
|
||||||
|
}
|
||||||
|
const T& value() const noexcept {
|
||||||
|
return raw_value;
|
||||||
|
}
|
||||||
|
T value_or(T alternative) const noexcept {
|
||||||
|
return has_value() ? raw_value : alternative;
|
||||||
|
}
|
||||||
|
void swap(nullable& other) noexcept {
|
||||||
|
std::swap(raw_value, other.raw_value);
|
||||||
|
}
|
||||||
|
void reset() noexcept {
|
||||||
|
raw_value = (std::numeric_limits<T>::max)();
|
||||||
|
}
|
||||||
|
bool operator==(const nullable& other) const noexcept {
|
||||||
|
return raw_value == other.raw_value;
|
||||||
|
}
|
||||||
|
bool operator!=(const nullable& other) const noexcept {
|
||||||
|
return raw_value != other.raw_value;
|
||||||
|
}
|
||||||
|
constexpr static nullable null() noexcept {
|
||||||
|
return { (std::numeric_limits<T>::max)() };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPPTRACE_EXPORT stacktrace_frame {
|
||||||
|
frame_ptr raw_address;
|
||||||
|
frame_ptr object_address;
|
||||||
|
nullable<std::uint32_t> line;
|
||||||
|
nullable<std::uint32_t> column;
|
||||||
|
std::string filename;
|
||||||
|
std::string symbol;
|
||||||
|
bool is_inline;
|
||||||
|
|
||||||
|
bool operator==(const stacktrace_frame& other) const {
|
||||||
|
return raw_address == other.raw_address
|
||||||
|
&& object_address == other.object_address
|
||||||
|
&& line == other.line
|
||||||
|
&& column == other.column
|
||||||
|
&& filename == other.filename
|
||||||
|
&& symbol == other.symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const stacktrace_frame& other) const {
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_frame get_object_info() const;
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
|
friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPPTRACE_EXPORT stacktrace {
|
||||||
|
std::vector<stacktrace_frame> frames;
|
||||||
|
static stacktrace current(std::size_t skip = 0);
|
||||||
|
static stacktrace current(std::size_t skip, std::size_t max_depth);
|
||||||
|
void print() const;
|
||||||
|
void print(std::ostream& stream) const;
|
||||||
|
void print(std::ostream& stream, bool color) const;
|
||||||
|
void print_with_snippets() const;
|
||||||
|
void print_with_snippets(std::ostream& stream) const;
|
||||||
|
void print_with_snippets(std::ostream& stream, bool color) const;
|
||||||
|
void clear();
|
||||||
|
bool empty() const noexcept;
|
||||||
|
std::string to_string(bool color = false) const;
|
||||||
|
friend std::ostream& operator<<(std::ostream& stream, const stacktrace& trace);
|
||||||
|
|
||||||
|
using iterator = std::vector<stacktrace_frame>::iterator;
|
||||||
|
using const_iterator = std::vector<stacktrace_frame>::const_iterator;
|
||||||
|
inline iterator begin() noexcept { return frames.begin(); }
|
||||||
|
inline iterator end() noexcept { return frames.end(); }
|
||||||
|
inline const_iterator begin() const noexcept { return frames.begin(); }
|
||||||
|
inline const_iterator end() const noexcept { return frames.end(); }
|
||||||
|
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
||||||
|
inline const_iterator cend() const noexcept { return frames.cend(); }
|
||||||
|
private:
|
||||||
|
void print(std::ostream& stream, bool color, bool newline_at_end, const char* header) const;
|
||||||
|
void print_with_snippets(std::ostream& stream, bool color, bool newline_at_end, const char* header) const;
|
||||||
|
friend void print_terminate_trace();
|
||||||
|
};
|
||||||
|
|
||||||
|
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip = 0);
|
||||||
|
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth);
|
||||||
|
CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip = 0);
|
||||||
|
CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip, std::size_t max_depth);
|
||||||
|
CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip = 0);
|
||||||
|
CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip, std::size_t max_depth);
|
||||||
|
|
||||||
|
// Path max isn't so simple, so I'm choosing 4096 which seems to encompass what all major OS's expect and should be
|
||||||
|
// fine in all reasonable cases.
|
||||||
|
// https://eklitzke.org/path-max-is-tricky
|
||||||
|
// https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
|
||||||
|
#define CPPTRACE_PATH_MAX 4096
|
||||||
|
|
||||||
|
// safe tracing interface
|
||||||
|
// signal-safe
|
||||||
|
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
|
||||||
|
frame_ptr* buffer,
|
||||||
|
std::size_t size,
|
||||||
|
std::size_t skip = 0
|
||||||
|
);
|
||||||
|
// signal-safe
|
||||||
|
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
|
||||||
|
frame_ptr* buffer,
|
||||||
|
std::size_t size,
|
||||||
|
std::size_t skip,
|
||||||
|
std::size_t max_depth
|
||||||
|
);
|
||||||
|
struct CPPTRACE_EXPORT safe_object_frame {
|
||||||
|
frame_ptr raw_address;
|
||||||
|
// This ends up being the real object address. It was named at a time when I thought the object base address
|
||||||
|
// still needed to be added in
|
||||||
|
frame_ptr address_relative_to_object_start;
|
||||||
|
char object_path[CPPTRACE_PATH_MAX + 1];
|
||||||
|
// To be called outside a signal handler. Not signal safe.
|
||||||
|
object_frame resolve() const;
|
||||||
|
};
|
||||||
|
// signal-safe
|
||||||
|
CPPTRACE_EXPORT void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
||||||
|
CPPTRACE_EXPORT bool can_signal_safe_unwind();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,498 +1,9 @@
|
|||||||
#ifndef CPPTRACE_HPP
|
#ifndef CPPTRACE_HPP
|
||||||
#define CPPTRACE_HPP
|
#define CPPTRACE_HPP
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cpptrace/basic.hpp>
|
||||||
#include <exception>
|
#include <cpptrace/utils.hpp>
|
||||||
#include <limits>
|
#include <cpptrace/exceptions.hpp>
|
||||||
#include <iosfwd>
|
#include <cpptrace/io.hpp>
|
||||||
#include <string>
|
|
||||||
#include <system_error>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define CPPTRACE_EXPORT_ATTR __declspec(dllexport)
|
|
||||||
#define CPPTRACE_IMPORT_ATTR __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
#define CPPTRACE_EXPORT_ATTR __attribute__((visibility("default")))
|
|
||||||
#define CPPTRACE_IMPORT_ATTR __attribute__((visibility("default")))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CPPTRACE_STATIC_DEFINE
|
|
||||||
# define CPPTRACE_EXPORT
|
|
||||||
# define CPPTRACE_NO_EXPORT
|
|
||||||
#else
|
|
||||||
# ifndef CPPTRACE_EXPORT
|
|
||||||
# ifdef cpptrace_lib_EXPORTS
|
|
||||||
/* We are building this library */
|
|
||||||
# define CPPTRACE_EXPORT CPPTRACE_EXPORT_ATTR
|
|
||||||
# else
|
|
||||||
/* We are using this library */
|
|
||||||
# define CPPTRACE_EXPORT CPPTRACE_IMPORT_ATTR
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CPPTRACE_NO_STD_FORMAT
|
|
||||||
#if __cplusplus >= 202002L
|
|
||||||
#ifdef __has_include
|
|
||||||
#if __has_include(<format>)
|
|
||||||
#define CPPTRACE_STD_FORMAT
|
|
||||||
#include <format>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define CPPTRACE_FORCE_NO_INLINE __declspec(noinline)
|
|
||||||
#else
|
|
||||||
#define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
|
||||||
// reason
|
|
||||||
// 4275 is the same thing but for base classes
|
|
||||||
#pragma warning(disable: 4251; disable: 4275)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cpptrace {
|
|
||||||
struct object_trace;
|
|
||||||
struct stacktrace;
|
|
||||||
|
|
||||||
// Some type sufficient for an instruction pointer, currently always an alias to std::uintptr_t
|
|
||||||
using frame_ptr = std::uintptr_t;
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT raw_trace {
|
|
||||||
std::vector<frame_ptr> frames;
|
|
||||||
static raw_trace current(std::size_t skip = 0);
|
|
||||||
static raw_trace current(std::size_t skip, std::size_t max_depth);
|
|
||||||
object_trace resolve_object_trace() const;
|
|
||||||
stacktrace resolve() const;
|
|
||||||
void clear();
|
|
||||||
bool empty() const noexcept;
|
|
||||||
|
|
||||||
using iterator = std::vector<frame_ptr>::iterator;
|
|
||||||
using const_iterator = std::vector<frame_ptr>::const_iterator;
|
|
||||||
inline iterator begin() noexcept { return frames.begin(); }
|
|
||||||
inline iterator end() noexcept { return frames.end(); }
|
|
||||||
inline const_iterator begin() const noexcept { return frames.begin(); }
|
|
||||||
inline const_iterator end() const noexcept { return frames.end(); }
|
|
||||||
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
|
||||||
inline const_iterator cend() const noexcept { return frames.cend(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT object_frame {
|
|
||||||
frame_ptr raw_address;
|
|
||||||
frame_ptr object_address;
|
|
||||||
std::string object_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT object_trace {
|
|
||||||
std::vector<object_frame> frames;
|
|
||||||
static object_trace current(std::size_t skip = 0);
|
|
||||||
static object_trace current(std::size_t skip, std::size_t max_depth);
|
|
||||||
stacktrace resolve() const;
|
|
||||||
void clear();
|
|
||||||
bool empty() const noexcept;
|
|
||||||
|
|
||||||
using iterator = std::vector<object_frame>::iterator;
|
|
||||||
using const_iterator = std::vector<object_frame>::const_iterator;
|
|
||||||
inline iterator begin() noexcept { return frames.begin(); }
|
|
||||||
inline iterator end() noexcept { return frames.end(); }
|
|
||||||
inline const_iterator begin() const noexcept { return frames.begin(); }
|
|
||||||
inline const_iterator end() const noexcept { return frames.end(); }
|
|
||||||
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
|
||||||
inline const_iterator cend() const noexcept { return frames.cend(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
// This represents a nullable integer type
|
|
||||||
// The max value of the type is used as a sentinel
|
|
||||||
// This is used over std::optional because the library is C++11 and also std::optional is a bit heavy-duty for this
|
|
||||||
// use.
|
|
||||||
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
|
|
||||||
struct nullable {
|
|
||||||
T raw_value;
|
|
||||||
nullable& operator=(T value) {
|
|
||||||
raw_value = value;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
bool has_value() const noexcept {
|
|
||||||
return raw_value != (std::numeric_limits<T>::max)();
|
|
||||||
}
|
|
||||||
T& value() noexcept {
|
|
||||||
return raw_value;
|
|
||||||
}
|
|
||||||
const T& value() const noexcept {
|
|
||||||
return raw_value;
|
|
||||||
}
|
|
||||||
T value_or(T alternative) const noexcept {
|
|
||||||
return has_value() ? raw_value : alternative;
|
|
||||||
}
|
|
||||||
void swap(nullable& other) noexcept {
|
|
||||||
std::swap(raw_value, other.raw_value);
|
|
||||||
}
|
|
||||||
void reset() noexcept {
|
|
||||||
raw_value = (std::numeric_limits<T>::max)();
|
|
||||||
}
|
|
||||||
bool operator==(const nullable& other) const noexcept {
|
|
||||||
return raw_value == other.raw_value;
|
|
||||||
}
|
|
||||||
bool operator!=(const nullable& other) const noexcept {
|
|
||||||
return raw_value != other.raw_value;
|
|
||||||
}
|
|
||||||
constexpr static nullable null() noexcept {
|
|
||||||
return { (std::numeric_limits<T>::max)() };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT stacktrace_frame {
|
|
||||||
frame_ptr raw_address;
|
|
||||||
frame_ptr object_address;
|
|
||||||
nullable<std::uint32_t> line;
|
|
||||||
nullable<std::uint32_t> column;
|
|
||||||
std::string filename;
|
|
||||||
std::string symbol;
|
|
||||||
bool is_inline;
|
|
||||||
|
|
||||||
bool operator==(const stacktrace_frame& other) const {
|
|
||||||
return raw_address == other.raw_address
|
|
||||||
&& object_address == other.object_address
|
|
||||||
&& line == other.line
|
|
||||||
&& column == other.column
|
|
||||||
&& filename == other.filename
|
|
||||||
&& symbol == other.symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const stacktrace_frame& other) const {
|
|
||||||
return !operator==(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
object_frame get_object_info() const;
|
|
||||||
|
|
||||||
std::string to_string() const;
|
|
||||||
friend std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPPTRACE_EXPORT stacktrace {
|
|
||||||
std::vector<stacktrace_frame> frames;
|
|
||||||
static stacktrace current(std::size_t skip = 0);
|
|
||||||
static stacktrace current(std::size_t skip, std::size_t max_depth);
|
|
||||||
void print() const;
|
|
||||||
void print(std::ostream& stream) const;
|
|
||||||
void print(std::ostream& stream, bool color) const;
|
|
||||||
void print_with_snippets() const;
|
|
||||||
void print_with_snippets(std::ostream& stream) const;
|
|
||||||
void print_with_snippets(std::ostream& stream, bool color) const;
|
|
||||||
void clear();
|
|
||||||
bool empty() const noexcept;
|
|
||||||
std::string to_string(bool color = false) const;
|
|
||||||
friend std::ostream& operator<<(std::ostream& stream, const stacktrace& trace);
|
|
||||||
|
|
||||||
using iterator = std::vector<stacktrace_frame>::iterator;
|
|
||||||
using const_iterator = std::vector<stacktrace_frame>::const_iterator;
|
|
||||||
inline iterator begin() noexcept { return frames.begin(); }
|
|
||||||
inline iterator end() noexcept { return frames.end(); }
|
|
||||||
inline const_iterator begin() const noexcept { return frames.begin(); }
|
|
||||||
inline const_iterator end() const noexcept { return frames.end(); }
|
|
||||||
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
|
|
||||||
inline const_iterator cend() const noexcept { return frames.cend(); }
|
|
||||||
private:
|
|
||||||
void print(std::ostream& stream, bool color, bool newline_at_end, const char* header) const;
|
|
||||||
void print_with_snippets(std::ostream& stream, bool color, bool newline_at_end, const char* header) const;
|
|
||||||
friend void print_terminate_trace();
|
|
||||||
};
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip = 0);
|
|
||||||
CPPTRACE_EXPORT raw_trace generate_raw_trace(std::size_t skip, std::size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip = 0);
|
|
||||||
CPPTRACE_EXPORT object_trace generate_object_trace(std::size_t skip, std::size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip = 0);
|
|
||||||
CPPTRACE_EXPORT stacktrace generate_trace(std::size_t skip, std::size_t max_depth);
|
|
||||||
|
|
||||||
// Path max isn't so simple, so I'm choosing 4096 which seems to encompass what all major OS's expect and should be
|
|
||||||
// fine in all reasonable cases.
|
|
||||||
// https://eklitzke.org/path-max-is-tricky
|
|
||||||
// https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
|
|
||||||
#define CPPTRACE_PATH_MAX 4096
|
|
||||||
|
|
||||||
// safe tracing interface
|
|
||||||
// signal-safe
|
|
||||||
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
|
|
||||||
frame_ptr* buffer,
|
|
||||||
std::size_t size,
|
|
||||||
std::size_t skip = 0
|
|
||||||
);
|
|
||||||
// signal-safe
|
|
||||||
CPPTRACE_EXPORT std::size_t safe_generate_raw_trace(
|
|
||||||
frame_ptr* buffer,
|
|
||||||
std::size_t size,
|
|
||||||
std::size_t skip,
|
|
||||||
std::size_t max_depth
|
|
||||||
);
|
|
||||||
struct CPPTRACE_EXPORT safe_object_frame {
|
|
||||||
frame_ptr raw_address;
|
|
||||||
// This ends up being the real object address. It was named at a time when I thought the object base address
|
|
||||||
// still needed to be added in
|
|
||||||
frame_ptr address_relative_to_object_start;
|
|
||||||
char object_path[CPPTRACE_PATH_MAX + 1];
|
|
||||||
// To be called outside a signal handler. Not signal safe.
|
|
||||||
object_frame resolve() const;
|
|
||||||
};
|
|
||||||
// signal-safe
|
|
||||||
CPPTRACE_EXPORT void get_safe_object_frame(frame_ptr address, safe_object_frame* out);
|
|
||||||
CPPTRACE_EXPORT bool can_signal_safe_unwind();
|
|
||||||
|
|
||||||
// utilities:
|
|
||||||
CPPTRACE_EXPORT std::string demangle(const std::string& name);
|
|
||||||
CPPTRACE_EXPORT std::string get_snippet(
|
|
||||||
const std::string& path,
|
|
||||||
std::size_t line,
|
|
||||||
std::size_t context_size,
|
|
||||||
bool color = false
|
|
||||||
);
|
|
||||||
CPPTRACE_EXPORT bool isatty(int fd);
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT extern const int stdin_fileno;
|
|
||||||
CPPTRACE_EXPORT extern const int stderr_fileno;
|
|
||||||
CPPTRACE_EXPORT extern const int stdout_fileno;
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT void register_terminate_handler();
|
|
||||||
|
|
||||||
// configuration:
|
|
||||||
CPPTRACE_EXPORT void absorb_trace_exceptions(bool absorb);
|
|
||||||
CPPTRACE_EXPORT void enable_inlined_call_resolution(bool enable);
|
|
||||||
|
|
||||||
enum class cache_mode {
|
|
||||||
// Only minimal lookup tables
|
|
||||||
prioritize_memory = 0,
|
|
||||||
// Build lookup tables but don't keep them around between trace calls
|
|
||||||
hybrid = 1,
|
|
||||||
// Build lookup tables as needed
|
|
||||||
prioritize_speed = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace experimental {
|
|
||||||
CPPTRACE_EXPORT void set_cache_mode(cache_mode mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tracing exceptions:
|
|
||||||
namespace detail {
|
|
||||||
// This is a helper utility, if the library weren't C++11 an std::variant would be used
|
|
||||||
class CPPTRACE_EXPORT lazy_trace_holder {
|
|
||||||
bool resolved;
|
|
||||||
union {
|
|
||||||
raw_trace trace;
|
|
||||||
stacktrace resolved_trace;
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
// constructors
|
|
||||||
lazy_trace_holder() : resolved(false), trace() {}
|
|
||||||
explicit lazy_trace_holder(raw_trace&& _trace) : resolved(false), trace(std::move(_trace)) {}
|
|
||||||
explicit lazy_trace_holder(stacktrace&& _resolved_trace) : resolved(true), resolved_trace(std::move(_resolved_trace)) {}
|
|
||||||
// logistics
|
|
||||||
lazy_trace_holder(const lazy_trace_holder& other);
|
|
||||||
lazy_trace_holder(lazy_trace_holder&& other) noexcept;
|
|
||||||
lazy_trace_holder& operator=(const lazy_trace_holder& other);
|
|
||||||
lazy_trace_holder& operator=(lazy_trace_holder&& other) noexcept;
|
|
||||||
~lazy_trace_holder();
|
|
||||||
// access
|
|
||||||
const raw_trace& get_raw_trace() const;
|
|
||||||
stacktrace& get_resolved_trace();
|
|
||||||
const stacktrace& get_resolved_trace() const;
|
|
||||||
private:
|
|
||||||
void clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth);
|
|
||||||
CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip = 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface for a traced exception object
|
|
||||||
class CPPTRACE_EXPORT exception : public std::exception {
|
|
||||||
public:
|
|
||||||
const char* what() const noexcept override = 0;
|
|
||||||
virtual const char* message() const noexcept = 0;
|
|
||||||
virtual const stacktrace& trace() const noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cpptrace traced exception object
|
|
||||||
// I hate to have to expose anything about implementation detail but the idea here is that
|
|
||||||
class CPPTRACE_EXPORT lazy_exception : public exception {
|
|
||||||
mutable detail::lazy_trace_holder trace_holder;
|
|
||||||
mutable std::string what_string;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit lazy_exception(
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) : trace_holder(std::move(trace)) {}
|
|
||||||
// std::exception
|
|
||||||
const char* what() const noexcept override;
|
|
||||||
// cpptrace::exception
|
|
||||||
const char* message() const noexcept override;
|
|
||||||
const stacktrace& trace() const noexcept override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT exception_with_message : public lazy_exception {
|
|
||||||
mutable std::string user_message;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit exception_with_message(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept : lazy_exception(std::move(trace)), user_message(std::move(message_arg)) {}
|
|
||||||
|
|
||||||
const char* message() const noexcept override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT logic_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit logic_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT domain_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit domain_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT invalid_argument : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit invalid_argument(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT length_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit length_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT out_of_range : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit out_of_range(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT runtime_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit runtime_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT range_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit range_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT overflow_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit overflow_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT underflow_error : public exception_with_message {
|
|
||||||
public:
|
|
||||||
explicit underflow_error(
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT nested_exception : public lazy_exception {
|
|
||||||
std::exception_ptr ptr;
|
|
||||||
mutable std::string message_value;
|
|
||||||
public:
|
|
||||||
explicit nested_exception(
|
|
||||||
const std::exception_ptr& exception_ptr,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept
|
|
||||||
: lazy_exception(std::move(trace)), ptr(exception_ptr) {}
|
|
||||||
|
|
||||||
const char* message() const noexcept override;
|
|
||||||
std::exception_ptr nested_ptr() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CPPTRACE_EXPORT system_error : public runtime_error {
|
|
||||||
std::error_code ec;
|
|
||||||
public:
|
|
||||||
explicit system_error(
|
|
||||||
int error_code,
|
|
||||||
std::string&& message_arg,
|
|
||||||
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
|
||||||
) noexcept;
|
|
||||||
const std::error_code& code() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
// [[noreturn]] must come first due to old clang
|
|
||||||
[[noreturn]] CPPTRACE_EXPORT void rethrow_and_wrap_if_needed(std::size_t skip = 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CPPTRACE_STD_FORMAT) && defined(__cpp_lib_format)
|
|
||||||
template <>
|
|
||||||
struct std::formatter<cpptrace::stacktrace_frame> : std::formatter<std::string> {
|
|
||||||
auto format(cpptrace::stacktrace_frame frame, format_context& ctx) const {
|
|
||||||
return formatter<string>::format(frame.to_string(), ctx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct std::formatter<cpptrace::stacktrace> : std::formatter<std::string> {
|
|
||||||
auto format(cpptrace::stacktrace trace, format_context& ctx) const {
|
|
||||||
return formatter<string>::format(trace.to_string(), ctx);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Exception wrapper utilities
|
|
||||||
#define CPPTRACE_WRAP_BLOCK(statements) do { \
|
|
||||||
try { \
|
|
||||||
statements \
|
|
||||||
} catch(...) { \
|
|
||||||
::cpptrace::rethrow_and_wrap_if_needed(); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define CPPTRACE_WRAP(expression) [&] () -> decltype((expression)) { \
|
|
||||||
try { \
|
|
||||||
return expression; \
|
|
||||||
} catch(...) { \
|
|
||||||
::cpptrace::rethrow_and_wrap_if_needed(1); \
|
|
||||||
} \
|
|
||||||
} ()
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
218
include/cpptrace/exceptions.hpp
Normal file
218
include/cpptrace/exceptions.hpp
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
#ifndef CPPTRACE_EXCEPTIONS_HPP
|
||||||
|
#define CPPTRACE_EXCEPTIONS_HPP
|
||||||
|
|
||||||
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
||||||
|
// reason
|
||||||
|
// 4275 is the same thing but for base classes
|
||||||
|
#pragma warning(disable: 4251; disable: 4275)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
// tracing exceptions:
|
||||||
|
namespace detail {
|
||||||
|
// This is a helper utility, if the library weren't C++11 an std::variant would be used
|
||||||
|
class CPPTRACE_EXPORT lazy_trace_holder {
|
||||||
|
bool resolved;
|
||||||
|
union {
|
||||||
|
raw_trace trace;
|
||||||
|
stacktrace resolved_trace;
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
// constructors
|
||||||
|
lazy_trace_holder() : resolved(false), trace() {}
|
||||||
|
explicit lazy_trace_holder(raw_trace&& _trace) : resolved(false), trace(std::move(_trace)) {}
|
||||||
|
explicit lazy_trace_holder(stacktrace&& _resolved_trace) : resolved(true), resolved_trace(std::move(_resolved_trace)) {}
|
||||||
|
// logistics
|
||||||
|
lazy_trace_holder(const lazy_trace_holder& other);
|
||||||
|
lazy_trace_holder(lazy_trace_holder&& other) noexcept;
|
||||||
|
lazy_trace_holder& operator=(const lazy_trace_holder& other);
|
||||||
|
lazy_trace_holder& operator=(lazy_trace_holder&& other) noexcept;
|
||||||
|
~lazy_trace_holder();
|
||||||
|
// access
|
||||||
|
const raw_trace& get_raw_trace() const;
|
||||||
|
stacktrace& get_resolved_trace();
|
||||||
|
const stacktrace& get_resolved_trace() const;
|
||||||
|
private:
|
||||||
|
void clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth);
|
||||||
|
CPPTRACE_EXPORT raw_trace get_raw_trace_and_absorb(std::size_t skip = 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface for a traced exception object
|
||||||
|
class CPPTRACE_EXPORT exception : public std::exception {
|
||||||
|
public:
|
||||||
|
const char* what() const noexcept override = 0;
|
||||||
|
virtual const char* message() const noexcept = 0;
|
||||||
|
virtual const stacktrace& trace() const noexcept = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cpptrace traced exception object
|
||||||
|
// I hate to have to expose anything about implementation detail but the idea here is that
|
||||||
|
class CPPTRACE_EXPORT lazy_exception : public exception {
|
||||||
|
mutable detail::lazy_trace_holder trace_holder;
|
||||||
|
mutable std::string what_string;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit lazy_exception(
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) : trace_holder(std::move(trace)) {}
|
||||||
|
// std::exception
|
||||||
|
const char* what() const noexcept override;
|
||||||
|
// cpptrace::exception
|
||||||
|
const char* message() const noexcept override;
|
||||||
|
const stacktrace& trace() const noexcept override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT exception_with_message : public lazy_exception {
|
||||||
|
mutable std::string user_message;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit exception_with_message(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept : lazy_exception(std::move(trace)), user_message(std::move(message_arg)) {}
|
||||||
|
|
||||||
|
const char* message() const noexcept override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT logic_error : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit logic_error(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT domain_error : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit domain_error(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT invalid_argument : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit invalid_argument(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT length_error : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit length_error(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT out_of_range : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit out_of_range(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT runtime_error : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit runtime_error(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT range_error : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit range_error(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT overflow_error : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit overflow_error(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT underflow_error : public exception_with_message {
|
||||||
|
public:
|
||||||
|
explicit underflow_error(
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: exception_with_message(std::move(message_arg), std::move(trace)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT nested_exception : public lazy_exception {
|
||||||
|
std::exception_ptr ptr;
|
||||||
|
mutable std::string message_value;
|
||||||
|
public:
|
||||||
|
explicit nested_exception(
|
||||||
|
const std::exception_ptr& exception_ptr,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept
|
||||||
|
: lazy_exception(std::move(trace)), ptr(exception_ptr) {}
|
||||||
|
|
||||||
|
const char* message() const noexcept override;
|
||||||
|
std::exception_ptr nested_ptr() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CPPTRACE_EXPORT system_error : public runtime_error {
|
||||||
|
std::error_code ec;
|
||||||
|
public:
|
||||||
|
explicit system_error(
|
||||||
|
int error_code,
|
||||||
|
std::string&& message_arg,
|
||||||
|
raw_trace&& trace = detail::get_raw_trace_and_absorb()
|
||||||
|
) noexcept;
|
||||||
|
const std::error_code& code() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
// [[noreturn]] must come first due to old clang
|
||||||
|
[[noreturn]] CPPTRACE_EXPORT void rethrow_and_wrap_if_needed(std::size_t skip = 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exception wrapper utilities
|
||||||
|
#define CPPTRACE_WRAP_BLOCK(statements) do { \
|
||||||
|
try { \
|
||||||
|
statements \
|
||||||
|
} catch(...) { \
|
||||||
|
::cpptrace::rethrow_and_wrap_if_needed(); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define CPPTRACE_WRAP(expression) [&] () -> decltype((expression)) { \
|
||||||
|
try { \
|
||||||
|
return expression; \
|
||||||
|
} catch(...) { \
|
||||||
|
::cpptrace::rethrow_and_wrap_if_needed(1); \
|
||||||
|
} \
|
||||||
|
} ()
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
18
include/cpptrace/forward.hpp
Normal file
18
include/cpptrace/forward.hpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef CPPTRACE_FORWARD_HPP
|
||||||
|
#define CPPTRACE_FORWARD_HPP
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
// Some type sufficient for an instruction pointer, currently always an alias to std::uintptr_t
|
||||||
|
using frame_ptr = std::uintptr_t;
|
||||||
|
|
||||||
|
struct raw_trace;
|
||||||
|
struct object_trace;
|
||||||
|
struct stacktrace;
|
||||||
|
|
||||||
|
struct object_frame;
|
||||||
|
struct stacktrace_frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef CPPTRACE_FROM_CURRENT_HPP
|
#ifndef CPPTRACE_FROM_CURRENT_HPP
|
||||||
#define CPPTRACE_FROM_CURRENT_HPP
|
#define CPPTRACE_FROM_CURRENT_HPP
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
CPPTRACE_EXPORT const raw_trace& raw_trace_from_current_exception();
|
CPPTRACE_EXPORT const raw_trace& raw_trace_from_current_exception();
|
||||||
|
|||||||
52
include/cpptrace/io.hpp
Normal file
52
include/cpptrace/io.hpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef CPPTRACE_IO_HPP
|
||||||
|
#define CPPTRACE_IO_HPP
|
||||||
|
|
||||||
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
#ifndef CPPTRACE_NO_STD_FORMAT
|
||||||
|
#if __cplusplus >= 202002L
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include(<format>)
|
||||||
|
#define CPPTRACE_STD_FORMAT
|
||||||
|
#include <format>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
||||||
|
// reason
|
||||||
|
// 4275 is the same thing but for base classes
|
||||||
|
#pragma warning(disable: 4251; disable: 4275)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
std::ostream& operator<<(std::ostream& stream, const stacktrace_frame& frame);
|
||||||
|
std::ostream& operator<<(std::ostream& stream, const stacktrace& trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CPPTRACE_STD_FORMAT) && defined(__cpp_lib_format)
|
||||||
|
template <>
|
||||||
|
struct std::formatter<cpptrace::stacktrace_frame> : std::formatter<std::string> {
|
||||||
|
auto format(cpptrace::stacktrace_frame frame, format_context& ctx) const {
|
||||||
|
return formatter<string>::format(frame.to_string(), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct std::formatter<cpptrace::stacktrace> : std::formatter<std::string> {
|
||||||
|
auto format(cpptrace::stacktrace trace, format_context& ctx) const {
|
||||||
|
return formatter<string>::format(trace.to_string(), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
48
include/cpptrace/utils.hpp
Normal file
48
include/cpptrace/utils.hpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#ifndef CPPTRACE_UTILS_HPP
|
||||||
|
#define CPPTRACE_UTILS_HPP
|
||||||
|
|
||||||
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
// warning C4251: using non-dll-exported type in dll-exported type, firing on std::vector<frame_ptr> and others for some
|
||||||
|
// reason
|
||||||
|
// 4275 is the same thing but for base classes
|
||||||
|
#pragma warning(disable: 4251; disable: 4275)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
CPPTRACE_EXPORT std::string demangle(const std::string& name);
|
||||||
|
CPPTRACE_EXPORT std::string get_snippet(
|
||||||
|
const std::string& path,
|
||||||
|
std::size_t line,
|
||||||
|
std::size_t context_size,
|
||||||
|
bool color = false
|
||||||
|
);
|
||||||
|
CPPTRACE_EXPORT bool isatty(int fd);
|
||||||
|
|
||||||
|
CPPTRACE_EXPORT extern const int stdin_fileno;
|
||||||
|
CPPTRACE_EXPORT extern const int stderr_fileno;
|
||||||
|
CPPTRACE_EXPORT extern const int stdout_fileno;
|
||||||
|
|
||||||
|
CPPTRACE_EXPORT void register_terminate_handler();
|
||||||
|
|
||||||
|
// options:
|
||||||
|
CPPTRACE_EXPORT void absorb_trace_exceptions(bool absorb);
|
||||||
|
CPPTRACE_EXPORT void enable_inlined_call_resolution(bool enable);
|
||||||
|
|
||||||
|
enum class cache_mode {
|
||||||
|
// Only minimal lookup tables
|
||||||
|
prioritize_memory = 0,
|
||||||
|
// Build lookup tables but don't keep them around between trace calls
|
||||||
|
hybrid = 1,
|
||||||
|
// Build lookup tables as needed
|
||||||
|
prioritize_speed = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace experimental {
|
||||||
|
CPPTRACE_EXPORT void set_cache_mode(cache_mode mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
276
src/cpptrace.cpp
276
src/cpptrace.cpp
@ -1,21 +1,17 @@
|
|||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/cpptrace.hpp>
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <new>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "unwind/unwind.hpp"
|
#include "unwind/unwind.hpp"
|
||||||
#include "demangle/demangle.hpp"
|
#include "demangle/demangle.hpp"
|
||||||
#include "platform/exception_type.hpp"
|
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/microfmt.hpp"
|
#include "utils/microfmt.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
@ -425,276 +421,4 @@ namespace cpptrace {
|
|||||||
bool can_signal_safe_unwind() {
|
bool can_signal_safe_unwind() {
|
||||||
return detail::has_safe_unwind();
|
return detail::has_safe_unwind();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string demangle(const std::string& name) {
|
|
||||||
return detail::demangle(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string get_snippet(const std::string& path, std::size_t line, std::size_t context_size, bool color) {
|
|
||||||
return detail::get_snippet(path, line, context_size, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isatty(int fd) {
|
|
||||||
return detail::isatty(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern const int stdin_fileno = detail::fileno(stdin);
|
|
||||||
extern const int stdout_fileno = detail::fileno(stdout);
|
|
||||||
extern const int stderr_fileno = detail::fileno(stderr);
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE void print_terminate_trace() {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
generate_trace(1).print(
|
|
||||||
std::cerr,
|
|
||||||
isatty(stderr_fileno),
|
|
||||||
true,
|
|
||||||
"Stack trace to reach terminate handler (most recent call first):"
|
|
||||||
);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] void terminate_handler() {
|
|
||||||
// TODO: Support std::nested_exception?
|
|
||||||
try {
|
|
||||||
auto ptr = std::current_exception();
|
|
||||||
if(ptr == nullptr) {
|
|
||||||
fputs("terminate called without an active exception", stderr);
|
|
||||||
print_terminate_trace();
|
|
||||||
} else {
|
|
||||||
std::rethrow_exception(ptr);
|
|
||||||
}
|
|
||||||
} catch(cpptrace::exception& e) {
|
|
||||||
microfmt::print(
|
|
||||||
stderr,
|
|
||||||
"Terminate called after throwing an instance of {}: {}\n",
|
|
||||||
demangle(typeid(e).name()),
|
|
||||||
e.message()
|
|
||||||
);
|
|
||||||
e.trace().print(std::cerr, isatty(stderr_fileno));
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
microfmt::print(
|
|
||||||
stderr, "Terminate called after throwing an instance of {}: {}\n", demangle(typeid(e).name()), e.what()
|
|
||||||
);
|
|
||||||
print_terminate_trace();
|
|
||||||
} catch(...) {
|
|
||||||
microfmt::print(
|
|
||||||
stderr, "Terminate called after throwing an instance of {}\n", detail::exception_type_name()
|
|
||||||
);
|
|
||||||
print_terminate_trace();
|
|
||||||
}
|
|
||||||
std::flush(std::cerr);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_terminate_handler() {
|
|
||||||
std::set_terminate(terminate_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
std::atomic_bool absorb_trace_exceptions(true); // NOSONAR
|
|
||||||
std::atomic_bool resolve_inlined_calls(true); // NOSONAR
|
|
||||||
std::atomic<cache_mode> current_cache_mode(cache_mode::prioritize_speed); // NOSONAR
|
|
||||||
}
|
|
||||||
|
|
||||||
void absorb_trace_exceptions(bool absorb) {
|
|
||||||
detail::absorb_trace_exceptions = absorb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_inlined_call_resolution(bool enable) {
|
|
||||||
detail::resolve_inlined_calls = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace experimental {
|
|
||||||
void set_cache_mode(cache_mode mode) {
|
|
||||||
detail::current_cache_mode = mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
bool should_absorb_trace_exceptions() {
|
|
||||||
return absorb_trace_exceptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool should_resolve_inlined_calls() {
|
|
||||||
return resolve_inlined_calls;
|
|
||||||
}
|
|
||||||
|
|
||||||
cache_mode get_cache_mode() {
|
|
||||||
return current_cache_mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth) {
|
|
||||||
try {
|
|
||||||
return generate_raw_trace(skip + 1, max_depth);
|
|
||||||
} catch(const std::exception& e) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
// TODO: Append to message somehow
|
|
||||||
std::fprintf(
|
|
||||||
stderr,
|
|
||||||
"Cpptrace: Exception occurred while resolving trace in cpptrace::exception object:\n%s\n",
|
|
||||||
e.what()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return raw_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
raw_trace get_raw_trace_and_absorb(std::size_t skip) {
|
|
||||||
try { // try/catch can never be hit but it's needed to prevent TCO
|
|
||||||
return get_raw_trace_and_absorb(skip + 1, SIZE_MAX);
|
|
||||||
} catch(...) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return raw_trace{};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_trace_holder::lazy_trace_holder(const lazy_trace_holder& other) : resolved(other.resolved) {
|
|
||||||
if(other.resolved) {
|
|
||||||
new (&resolved_trace) stacktrace(other.resolved_trace);
|
|
||||||
} else {
|
|
||||||
new (&trace) raw_trace(other.trace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lazy_trace_holder::lazy_trace_holder(lazy_trace_holder&& other) noexcept : resolved(other.resolved) {
|
|
||||||
if(other.resolved) {
|
|
||||||
new (&resolved_trace) stacktrace(std::move(other.resolved_trace));
|
|
||||||
} else {
|
|
||||||
new (&trace) raw_trace(std::move(other.trace));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lazy_trace_holder& lazy_trace_holder::operator=(const lazy_trace_holder& other) {
|
|
||||||
clear();
|
|
||||||
resolved = other.resolved;
|
|
||||||
if(other.resolved) {
|
|
||||||
new (&resolved_trace) stacktrace(other.resolved_trace);
|
|
||||||
} else {
|
|
||||||
new (&trace) raw_trace(other.trace);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
lazy_trace_holder& lazy_trace_holder::operator=(lazy_trace_holder&& other) noexcept {
|
|
||||||
clear();
|
|
||||||
resolved = other.resolved;
|
|
||||||
if(other.resolved) {
|
|
||||||
new (&resolved_trace) stacktrace(std::move(other.resolved_trace));
|
|
||||||
} else {
|
|
||||||
new (&trace) raw_trace(std::move(other.trace));
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
lazy_trace_holder::~lazy_trace_holder() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
// access
|
|
||||||
const raw_trace& lazy_trace_holder::get_raw_trace() const {
|
|
||||||
if(resolved) {
|
|
||||||
throw std::logic_error(
|
|
||||||
"cpptrace::detail::lazy_trace_holder::get_resolved_trace called on resolved holder"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return trace;
|
|
||||||
}
|
|
||||||
stacktrace& lazy_trace_holder::get_resolved_trace() {
|
|
||||||
if(!resolved) {
|
|
||||||
raw_trace old_trace = std::move(trace);
|
|
||||||
*this = lazy_trace_holder(stacktrace{});
|
|
||||||
try {
|
|
||||||
if(!old_trace.empty()) {
|
|
||||||
resolved_trace = old_trace.resolve();
|
|
||||||
}
|
|
||||||
} catch(const std::exception& e) {
|
|
||||||
if(!detail::should_absorb_trace_exceptions()) {
|
|
||||||
// TODO: Append to message somehow?
|
|
||||||
std::fprintf(
|
|
||||||
stderr,
|
|
||||||
"Exception occurred while resolving trace in cpptrace::detail::lazy_trace_holder:\n%s\n",
|
|
||||||
e.what()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resolved_trace;
|
|
||||||
}
|
|
||||||
const stacktrace& lazy_trace_holder::get_resolved_trace() const {
|
|
||||||
if(!resolved) {
|
|
||||||
throw std::logic_error(
|
|
||||||
"cpptrace::detail::lazy_trace_holder::get_resolved_trace called on unresolved const holder"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return resolved_trace;
|
|
||||||
}
|
|
||||||
void lazy_trace_holder::clear() {
|
|
||||||
if(resolved) {
|
|
||||||
resolved_trace.~stacktrace();
|
|
||||||
} else {
|
|
||||||
trace.~raw_trace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* lazy_exception::what() const noexcept {
|
|
||||||
if(what_string.empty()) {
|
|
||||||
what_string = message() + std::string(":\n") + trace_holder.get_resolved_trace().to_string();
|
|
||||||
}
|
|
||||||
return what_string.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* lazy_exception::message() const noexcept {
|
|
||||||
return "cpptrace::lazy_exception";
|
|
||||||
}
|
|
||||||
|
|
||||||
const stacktrace& lazy_exception::trace() const noexcept {
|
|
||||||
return trace_holder.get_resolved_trace();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* exception_with_message::message() const noexcept {
|
|
||||||
return user_message.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
system_error::system_error(int error_code, std::string&& message_arg, raw_trace&& trace) noexcept
|
|
||||||
: runtime_error(
|
|
||||||
message_arg + ": " + std::error_code(error_code, std::generic_category()).message(),
|
|
||||||
std::move(trace)
|
|
||||||
),
|
|
||||||
ec(std::error_code(error_code, std::generic_category())) {}
|
|
||||||
|
|
||||||
const std::error_code& system_error::code() const noexcept {
|
|
||||||
return ec;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* nested_exception::message() const noexcept {
|
|
||||||
if(message_value.empty()) {
|
|
||||||
try {
|
|
||||||
std::rethrow_exception(ptr);
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
message_value = std::string("Nested exception: ") + e.what();
|
|
||||||
} catch(...) {
|
|
||||||
message_value = "Nested exception holding instance of " + detail::exception_type_name();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return message_value.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::exception_ptr nested_exception::nested_ptr() const noexcept {
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
CPPTRACE_FORCE_NO_INLINE
|
|
||||||
void rethrow_and_wrap_if_needed(std::size_t skip) {
|
|
||||||
try {
|
|
||||||
std::rethrow_exception(std::current_exception());
|
|
||||||
} catch(cpptrace::exception&) {
|
|
||||||
throw; // already a cpptrace::exception
|
|
||||||
} catch(...) {
|
|
||||||
throw nested_exception(std::current_exception(), detail::get_raw_trace_and_absorb(skip + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
186
src/exceptions.cpp
Normal file
186
src/exceptions.cpp
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#include <cpptrace/exceptions.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <new>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "platform/exception_type.hpp"
|
||||||
|
#include "utils/common.hpp"
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
namespace detail {
|
||||||
|
lazy_trace_holder::lazy_trace_holder(const lazy_trace_holder& other) : resolved(other.resolved) {
|
||||||
|
if(other.resolved) {
|
||||||
|
new (&resolved_trace) stacktrace(other.resolved_trace);
|
||||||
|
} else {
|
||||||
|
new (&trace) raw_trace(other.trace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lazy_trace_holder::lazy_trace_holder(lazy_trace_holder&& other) noexcept : resolved(other.resolved) {
|
||||||
|
if(other.resolved) {
|
||||||
|
new (&resolved_trace) stacktrace(std::move(other.resolved_trace));
|
||||||
|
} else {
|
||||||
|
new (&trace) raw_trace(std::move(other.trace));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lazy_trace_holder& lazy_trace_holder::operator=(const lazy_trace_holder& other) {
|
||||||
|
clear();
|
||||||
|
resolved = other.resolved;
|
||||||
|
if(other.resolved) {
|
||||||
|
new (&resolved_trace) stacktrace(other.resolved_trace);
|
||||||
|
} else {
|
||||||
|
new (&trace) raw_trace(other.trace);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
lazy_trace_holder& lazy_trace_holder::operator=(lazy_trace_holder&& other) noexcept {
|
||||||
|
clear();
|
||||||
|
resolved = other.resolved;
|
||||||
|
if(other.resolved) {
|
||||||
|
new (&resolved_trace) stacktrace(std::move(other.resolved_trace));
|
||||||
|
} else {
|
||||||
|
new (&trace) raw_trace(std::move(other.trace));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
lazy_trace_holder::~lazy_trace_holder() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
// access
|
||||||
|
const raw_trace& lazy_trace_holder::get_raw_trace() const {
|
||||||
|
if(resolved) {
|
||||||
|
throw std::logic_error(
|
||||||
|
"cpptrace::detail::lazy_trace_holder::get_resolved_trace called on resolved holder"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return trace;
|
||||||
|
}
|
||||||
|
stacktrace& lazy_trace_holder::get_resolved_trace() {
|
||||||
|
if(!resolved) {
|
||||||
|
raw_trace old_trace = std::move(trace);
|
||||||
|
*this = lazy_trace_holder(stacktrace{});
|
||||||
|
try {
|
||||||
|
if(!old_trace.empty()) {
|
||||||
|
resolved_trace = old_trace.resolve();
|
||||||
|
}
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
if(!detail::should_absorb_trace_exceptions()) {
|
||||||
|
// TODO: Append to message somehow?
|
||||||
|
std::fprintf(
|
||||||
|
stderr,
|
||||||
|
"Exception occurred while resolving trace in cpptrace::detail::lazy_trace_holder:\n%s\n",
|
||||||
|
e.what()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resolved_trace;
|
||||||
|
}
|
||||||
|
const stacktrace& lazy_trace_holder::get_resolved_trace() const {
|
||||||
|
if(!resolved) {
|
||||||
|
throw std::logic_error(
|
||||||
|
"cpptrace::detail::lazy_trace_holder::get_resolved_trace called on unresolved const holder"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return resolved_trace;
|
||||||
|
}
|
||||||
|
void lazy_trace_holder::clear() {
|
||||||
|
if(resolved) {
|
||||||
|
resolved_trace.~stacktrace();
|
||||||
|
} else {
|
||||||
|
trace.~raw_trace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
|
raw_trace get_raw_trace_and_absorb(std::size_t skip, std::size_t max_depth) {
|
||||||
|
try {
|
||||||
|
return generate_raw_trace(skip + 1, max_depth);
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
if(!detail::should_absorb_trace_exceptions()) {
|
||||||
|
// TODO: Append to message somehow
|
||||||
|
std::fprintf(
|
||||||
|
stderr,
|
||||||
|
"Cpptrace: Exception occurred while resolving trace in cpptrace::exception object:\n%s\n",
|
||||||
|
e.what()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return raw_trace{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
|
raw_trace get_raw_trace_and_absorb(std::size_t skip) {
|
||||||
|
try { // try/catch can never be hit but it's needed to prevent TCO
|
||||||
|
return get_raw_trace_and_absorb(skip + 1, SIZE_MAX);
|
||||||
|
} catch(...) {
|
||||||
|
if(!detail::should_absorb_trace_exceptions()) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return raw_trace{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* lazy_exception::what() const noexcept {
|
||||||
|
if(what_string.empty()) {
|
||||||
|
what_string = message() + std::string(":\n") + trace_holder.get_resolved_trace().to_string();
|
||||||
|
}
|
||||||
|
return what_string.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* lazy_exception::message() const noexcept {
|
||||||
|
return "cpptrace::lazy_exception";
|
||||||
|
}
|
||||||
|
|
||||||
|
const stacktrace& lazy_exception::trace() const noexcept {
|
||||||
|
return trace_holder.get_resolved_trace();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* exception_with_message::message() const noexcept {
|
||||||
|
return user_message.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
system_error::system_error(int error_code, std::string&& message_arg, raw_trace&& trace) noexcept
|
||||||
|
: runtime_error(
|
||||||
|
message_arg + ": " + std::error_code(error_code, std::generic_category()).message(),
|
||||||
|
std::move(trace)
|
||||||
|
),
|
||||||
|
ec(std::error_code(error_code, std::generic_category())) {}
|
||||||
|
|
||||||
|
const std::error_code& system_error::code() const noexcept {
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* nested_exception::message() const noexcept {
|
||||||
|
if(message_value.empty()) {
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(ptr);
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
message_value = std::string("Nested exception: ") + e.what();
|
||||||
|
} catch(...) {
|
||||||
|
message_value = "Nested exception holding instance of " + detail::exception_type_name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message_value.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::exception_ptr nested_exception::nested_ptr() const noexcept {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPPTRACE_FORCE_NO_INLINE
|
||||||
|
void rethrow_and_wrap_if_needed(std::size_t skip) {
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(std::current_exception());
|
||||||
|
} catch(cpptrace::exception&) {
|
||||||
|
throw; // already a cpptrace::exception
|
||||||
|
} catch(...) {
|
||||||
|
throw nested_exception(std::current_exception(), detail::get_raw_trace_and_absorb(skip + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/options.cpp
Normal file
41
src/options.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
|
#include "options.hpp"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
namespace detail {
|
||||||
|
std::atomic_bool absorb_trace_exceptions(true); // NOSONAR
|
||||||
|
std::atomic_bool resolve_inlined_calls(true); // NOSONAR
|
||||||
|
std::atomic<cache_mode> current_cache_mode(cache_mode::prioritize_speed); // NOSONAR
|
||||||
|
}
|
||||||
|
|
||||||
|
void absorb_trace_exceptions(bool absorb) {
|
||||||
|
detail::absorb_trace_exceptions = absorb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_inlined_call_resolution(bool enable) {
|
||||||
|
detail::resolve_inlined_calls = enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace experimental {
|
||||||
|
void set_cache_mode(cache_mode mode) {
|
||||||
|
detail::current_cache_mode = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
bool should_absorb_trace_exceptions() {
|
||||||
|
return absorb_trace_exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_resolve_inlined_calls() {
|
||||||
|
return resolve_inlined_calls;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_mode get_cache_mode() {
|
||||||
|
return current_cache_mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/options.hpp
Normal file
14
src/options.hpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef OPTIONS_HPP
|
||||||
|
#define OPTIONS_HPP
|
||||||
|
|
||||||
|
#include <cpptrace/utils.hpp>
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
namespace detail {
|
||||||
|
bool should_absorb_trace_exceptions();
|
||||||
|
bool should_resolve_inlined_calls();
|
||||||
|
cache_mode get_cache_mode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "symbols/dwarf/resolver.hpp"
|
#include "symbols/dwarf/resolver.hpp"
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/error.hpp"
|
#include "utils/error.hpp"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef DWARF_HPP
|
#ifndef DWARF_HPP
|
||||||
#define DWARF_HPP
|
#define DWARF_HPP
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "utils/error.hpp"
|
#include "utils/error.hpp"
|
||||||
#include "utils/microfmt.hpp"
|
#include "utils/microfmt.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "symbols/dwarf/resolver.hpp"
|
#include "symbols/dwarf/resolver.hpp"
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "symbols/dwarf/dwarf.hpp" // has dwarf #includes
|
#include "symbols/dwarf/dwarf.hpp" // has dwarf #includes
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef SYMBOL_RESOLVER_HPP
|
#ifndef SYMBOL_RESOLVER_HPP
|
||||||
#define SYMBOL_RESOLVER_HPP
|
#define SYMBOL_RESOLVER_HPP
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "platform/platform.hpp"
|
#include "platform/platform.hpp"
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef SYMBOLS_HPP
|
#ifndef SYMBOLS_HPP
|
||||||
#define SYMBOLS_HPP
|
#define SYMBOLS_HPP
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/microfmt.hpp"
|
#include "utils/microfmt.hpp"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "platform/dbghelp_syminit_manager.hpp"
|
#include "platform/dbghelp_syminit_manager.hpp"
|
||||||
#include "binary/object.hpp"
|
#include "binary/object.hpp"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "binary/module_base.hpp"
|
#include "binary/module_base.hpp"
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "platform/program_name.hpp"
|
#include "platform/program_name.hpp"
|
||||||
#include "utils/error.hpp"
|
#include "utils/error.hpp"
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "dwarf/resolver.hpp"
|
#include "dwarf/resolver.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "symbols/symbols.hpp"
|
#include "symbols/symbols.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#ifndef UNWIND_HPP
|
#ifndef UNWIND_HPP
|
||||||
#define UNWIND_HPP
|
#define UNWIND_HPP
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
|
#ifdef CPPTRACE_UNWIND_WITH_DBGHELP
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "unwind/unwind.hpp"
|
#include "unwind/unwind.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#ifdef CPPTRACE_UNWIND_WITH_WINAPI
|
#ifdef CPPTRACE_UNWIND_WITH_WINAPI
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
#include "unwind/unwind.hpp"
|
#include "unwind/unwind.hpp"
|
||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
|
|||||||
79
src/utils.cpp
Normal file
79
src/utils.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include <cpptrace/utils.hpp>
|
||||||
|
#include <cpptrace/exceptions.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "demangle/demangle.hpp"
|
||||||
|
#include "snippets/snippet.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
|
#include "platform/exception_type.hpp"
|
||||||
|
|
||||||
|
namespace cpptrace {
|
||||||
|
std::string demangle(const std::string& name) {
|
||||||
|
return detail::demangle(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_snippet(const std::string& path, std::size_t line, std::size_t context_size, bool color) {
|
||||||
|
return detail::get_snippet(path, line, context_size, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isatty(int fd) {
|
||||||
|
return detail::isatty(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const int stdin_fileno = detail::fileno(stdin);
|
||||||
|
extern const int stdout_fileno = detail::fileno(stdout);
|
||||||
|
extern const int stderr_fileno = detail::fileno(stderr);
|
||||||
|
|
||||||
|
CPPTRACE_FORCE_NO_INLINE void print_terminate_trace() {
|
||||||
|
try { // try/catch can never be hit but it's needed to prevent TCO
|
||||||
|
generate_trace(1).print(
|
||||||
|
std::cerr,
|
||||||
|
isatty(stderr_fileno),
|
||||||
|
true,
|
||||||
|
"Stack trace to reach terminate handler (most recent call first):"
|
||||||
|
);
|
||||||
|
} catch(...) {
|
||||||
|
if(!detail::should_absorb_trace_exceptions()) {
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void terminate_handler() {
|
||||||
|
// TODO: Support std::nested_exception?
|
||||||
|
try {
|
||||||
|
auto ptr = std::current_exception();
|
||||||
|
if(ptr == nullptr) {
|
||||||
|
fputs("terminate called without an active exception", stderr);
|
||||||
|
print_terminate_trace();
|
||||||
|
} else {
|
||||||
|
std::rethrow_exception(ptr);
|
||||||
|
}
|
||||||
|
} catch(cpptrace::exception& e) {
|
||||||
|
microfmt::print(
|
||||||
|
stderr,
|
||||||
|
"Terminate called after throwing an instance of {}: {}\n",
|
||||||
|
demangle(typeid(e).name()),
|
||||||
|
e.message()
|
||||||
|
);
|
||||||
|
e.trace().print(std::cerr, isatty(stderr_fileno));
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
microfmt::print(
|
||||||
|
stderr, "Terminate called after throwing an instance of {}: {}\n", demangle(typeid(e).name()), e.what()
|
||||||
|
);
|
||||||
|
print_terminate_trace();
|
||||||
|
} catch(...) {
|
||||||
|
microfmt::print(
|
||||||
|
stderr, "Terminate called after throwing an instance of {}\n", detail::exception_type_name()
|
||||||
|
);
|
||||||
|
print_terminate_trace();
|
||||||
|
}
|
||||||
|
std::flush(std::cerr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_terminate_handler() {
|
||||||
|
std::set_terminate(terminate_handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
#ifndef COMMON_HPP
|
#ifndef COMMON_HPP
|
||||||
#define COMMON_HPP
|
#define COMMON_HPP
|
||||||
|
|
||||||
#include <cpptrace/cpptrace.hpp>
|
#include <cpptrace/basic.hpp>
|
||||||
|
|
||||||
#include "platform/platform.hpp"
|
#include "platform/platform.hpp"
|
||||||
|
#include "options.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
@ -35,10 +36,6 @@ namespace detail {
|
|||||||
"",
|
"",
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
bool should_absorb_trace_exceptions();
|
|
||||||
bool should_resolve_inlined_calls();
|
|
||||||
cache_mode get_cache_mode();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,8 +17,6 @@
|
|||||||
#include "utils/common.hpp"
|
#include "utils/common.hpp"
|
||||||
#include "utils/error.hpp"
|
#include "utils/error.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace cpptrace {
|
namespace cpptrace {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
bool isatty(int fd);
|
bool isatty(int fd);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user