Work on improving error handling and some general refactoring. Also trying to bring everything into cpptrace::detail.

This commit is contained in:
Jeremy 2023-09-16 20:46:30 -04:00
parent 4d04352189
commit 278ee3fcee
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
19 changed files with 926 additions and 824 deletions

View File

@ -13,6 +13,7 @@
#include "unwind/unwind.hpp" #include "unwind/unwind.hpp"
#include "demangle/demangle.hpp" #include "demangle/demangle.hpp"
#include "platform/common.hpp" #include "platform/common.hpp"
#include "platform/utils.hpp"
namespace cpptrace { namespace cpptrace {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
@ -58,7 +59,7 @@ namespace cpptrace {
namespace cpptrace { namespace cpptrace {
CPPTRACE_API CPPTRACE_API
void print_trace(std::uint32_t skip) { void print_trace(std::uint32_t skip) {
enable_virtual_terminal_processing_if_needed(); detail::enable_virtual_terminal_processing_if_needed();
std::cerr<<"Stack trace (most recent call first):"<<std::endl; std::cerr<<"Stack trace (most recent call first):"<<std::endl;
std::size_t counter = 0; std::size_t counter = 0;
const auto trace = generate_trace(skip + 1); const auto trace = generate_trace(skip + 1);
@ -66,7 +67,7 @@ namespace cpptrace {
std::cerr<<"<empty trace>"<<std::endl; std::cerr<<"<empty trace>"<<std::endl;
return; return;
} }
const auto frame_number_width = n_digits(static_cast<int>(trace.size()) - 1); const auto frame_number_width = detail::n_digits(static_cast<int>(trace.size()) - 1);
for(const auto& frame : trace) { for(const auto& frame : trace) {
std::cerr std::cerr
<< '#' << '#'

View File

@ -12,7 +12,11 @@ namespace cpptrace {
std::string demangle(const std::string& name) { std::string demangle(const std::string& name) {
int status; int status;
// presumably thread-safe // presumably thread-safe
// it appears safe to pass nullptr for status however the docs don't explicitly say it's safe so I don't
// want to rely on it
char* const demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status); char* const demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
// demangled will always be nullptr on non-zero status, and if __cxa_demangle ever fails for any reason
// we'll just quietly return the mangled name
if(demangled) { if(demangled) {
const std::string str = demangled; const std::string str = demangled;
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc) // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)

View File

@ -3,6 +3,7 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/utils.hpp"
#include <cstddef> #include <cstddef>
#include <vector> #include <vector>

View File

@ -3,10 +3,10 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "../platform/program_name.hpp" #include "../platform/program_name.hpp"
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/utils.hpp"
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio>
#include <mutex> #include <mutex>
#include <vector> #include <vector>
@ -50,7 +50,7 @@ namespace cpptrace {
} }
void error_callback(void*, const char* msg, int errnum) { void error_callback(void*, const char* msg, int errnum) {
fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum); nonfatal_error(stringf("libbacktrace error: %s, code %d", msg, errnum));
} }
backtrace_state* get_backtrace_state() { backtrace_state* get_backtrace_state() {

View File

@ -3,29 +3,10 @@
#ifdef _MSC_VER #ifdef _MSC_VER
#define CPPTRACE_FORCE_NO_INLINE __declspec(noinline) #define CPPTRACE_FORCE_NO_INLINE __declspec(noinline)
#define CPPTRACE_PFUNC __FUNCSIG__
#define CPPTRACE_MAYBE_UNUSED
#pragma warning(push)
#pragma warning(disable: 4505) // Unused local function
#else #else
#define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline)) #define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline))
#define CPPTRACE_PFUNC __extension__ __PRETTY_FUNCTION__
#define CPPTRACE_MAYBE_UNUSED __attribute__((unused))
#endif #endif
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <ios>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include <memory>
#include <new>
#define IS_WINDOWS 0 #define IS_WINDOWS 0
#define IS_LINUX 0 #define IS_LINUX 0
#define IS_APPLE 0 #define IS_APPLE 0
@ -60,389 +41,24 @@
#error "Unsupported compiler" #error "Unsupported compiler"
#endif #endif
#if IS_WINDOWS #include <cstdio>
#include <windows.h> #include <stdexcept>
#else #include <string>
#include <sys/stat.h>
#endif
// Lightweight std::source_location. namespace cpptrace {
struct source_location { namespace detail {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) // Placed here instead of utils because it's used by error.hpp and utils.hpp
const char* const file; template<typename... T> std::string stringf(T... args) {
//const char* const function; // disabled for now due to static constexpr restrictions int length = snprintf(0, 0, args...);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) if(length < 0) {
const int line; throw std::logic_error("invalid arguments to stringf");
constexpr source_location(
//const char* _function /*= __builtin_FUNCTION()*/,
const char* _file = __builtin_FILE(),
int _line = __builtin_LINE()
) : file(_file), /*function(_function),*/ line(_line) {}
};
CPPTRACE_MAYBE_UNUSED
static void primitive_assert_impl(
bool condition,
bool verify,
const char* expression,
const char* signature,
source_location location,
const char* message = nullptr
) {
if(!condition) {
const char* action = verify ? "verification" : "assertion";
const char* name = verify ? "verify" : "assert";
if(message == nullptr) {
(void) fprintf(
stderr,
"Cpptrace %s failed at %s:%d: %s\n",
action, location.file, location.line, signature
);
} else {
(void) fprintf(
stderr,
"Cpptrace %s failed at %s:%d: %s: %s\n",
action, location.file, location.line, signature, message
);
}
(void) fprintf(stderr, " primitive_%s(%s);\n", name, expression);
std::abort();
}
}
template<typename T>
void nothing() {}
#define PHONY_USE(E) (nothing<decltype(E)>())
// Still present in release mode, nonfatal
#define internal_verify(c, ...) primitive_assert_impl(c, true, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__)
#ifndef NDEBUG
#define CPPTRACE_PRIMITIVE_ASSERT(c, ...) \
primitive_assert_impl(c, false, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__)
#else
#define CPPTRACE_PRIMITIVE_ASSERT(c, ...) PHONY_USE(c)
#endif
CPPTRACE_MAYBE_UNUSED
static std::vector<std::string> split(const std::string& str, const std::string& delims) {
std::vector<std::string> vec;
size_t old_pos = 0;
size_t pos = 0;
while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) {
vec.emplace_back(str.substr(old_pos, pos - old_pos));
old_pos = pos + 1;
}
vec.emplace_back(str.substr(old_pos));
return vec;
}
template<typename C>
CPPTRACE_MAYBE_UNUSED
static std::string join(const C& container, const std::string& delim) {
auto iter = std::begin(container);
auto end = std::end(container);
std::string str;
if(std::distance(iter, end) > 0) {
str += *iter;
while(++iter != end) {
str += delim;
str += *iter;
}
} }
std::string str(length, 0);
// .data is const char* in c++11, but &str[0] should be legal
snprintf(&str[0], length + 1, args...);
return str; return str;
} }
constexpr const char* const whitespace = " \t\n\r\f\v";
CPPTRACE_MAYBE_UNUSED
static std::string trim(const std::string& str) {
if(str.empty()) {
return "";
}
const size_t left = str.find_first_not_of(whitespace);
const size_t right = str.find_last_not_of(whitespace) + 1;
return str.substr(left, right - left);
}
CPPTRACE_MAYBE_UNUSED
static std::string to_hex(uintptr_t addr) {
std::stringstream sstream;
sstream<<std::hex<<addr;
return std::move(sstream).str();
}
CPPTRACE_MAYBE_UNUSED
static bool is_little_endian() {
uint16_t num = 0x1;
auto* ptr = (uint8_t*)&num;
return ptr[0] == 1;
}
// Modified from
// https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c
template<typename T, size_t N>
struct byte_swapper;
template<typename T>
struct byte_swapper<T, 1> {
T operator()(T val) {
return val;
}
};
template<typename T>
struct byte_swapper<T, 2> {
T operator()(T val) {
return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
}
};
template<typename T>
struct byte_swapper<T, 4> {
T operator()(T val) {
return ((((val) & 0xff000000) >> 24) |
(((val) & 0x00ff0000) >> 8) |
(((val) & 0x0000ff00) << 8) |
(((val) & 0x000000ff) << 24));
}
};
template<typename T>
struct byte_swapper<T, 8> {
T operator()(T val) {
return ((((val) & 0xff00000000000000ull) >> 56) |
(((val) & 0x00ff000000000000ull) >> 40) |
(((val) & 0x0000ff0000000000ull) >> 24) |
(((val) & 0x000000ff00000000ull) >> 8 ) |
(((val) & 0x00000000ff000000ull) << 8 ) |
(((val) & 0x0000000000ff0000ull) << 24) |
(((val) & 0x000000000000ff00ull) << 40) |
(((val) & 0x00000000000000ffull) << 56));
}
};
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T byteswap(T value) {
return byte_swapper<T, sizeof(T)>{}(value);
}
CPPTRACE_MAYBE_UNUSED
inline void enable_virtual_terminal_processing_if_needed() {
// enable colors / ansi processing if necessary
#if IS_WINDOWS
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
constexpr DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
#endif
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
if(hOut == INVALID_HANDLE_VALUE) return;
if(!GetConsoleMode(hOut, &dwMode)) return;
if(dwMode != (dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
if(!SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return;
#endif
}
CPPTRACE_MAYBE_UNUSED
// NOLINTNEXTLINE(misc-no-recursion)
inline constexpr unsigned n_digits(unsigned value) {
return value < 10 ? 1 : 1 + n_digits(value / 10);
}
static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result");
static_assert(n_digits(9) == 1, "n_digits utility producing the wrong result");
static_assert(n_digits(10) == 2, "n_digits utility producing the wrong result");
static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result");
static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result");
// TODO: Re-evaluate use of off_t
template<typename T, typename std::enable_if<std::is_pod<T>::value, int>::type = 0>
T load_bytes(FILE* obj_file, off_t offset) {
T object;
internal_verify(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error");
internal_verify(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error");
return object;
}
class file_error : std::exception {
const char* what() const noexcept override {
return "Unable to read file";
}
};
struct nullopt_t {};
static constexpr nullopt_t nullopt;
template<typename T, typename std::enable_if<!std::is_same<typename std::decay<T>::type, void>::value, int>::type = 0>
class optional {
bool holds_value = false;
union {
T uvalue;
};
public:
// clang-tidy false positive
// NOLINTNEXTLINE(modernize-use-equals-default)
optional() noexcept {}
optional(nullopt_t) noexcept {}
~optional() {
reset();
}
optional(const optional& other) : holds_value(other.holds_value) {
if(holds_value) {
new (static_cast<void*>(std::addressof(uvalue))) T(other.uvalue);
} }
} }
optional(optional&& other) noexcept(std::is_nothrow_move_constructible<T>::value) : holds_value(other.holds_value) {
if(holds_value) {
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
}
}
optional& operator=(const optional& other) {
optional copy(other);
swap(*this, copy);
return *this;
}
optional& operator=(optional&& other) noexcept(
std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value
) {
reset();
if(other.holds_value) {
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
holds_value = true;
}
return *this;
}
template<
typename U = T,
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
>
// clang-tidy false positive
// NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
optional(U&& value) : holds_value(true) {
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
}
template<
typename U = T,
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
>
optional& operator=(U&& value) {
if(holds_value) {
uvalue = std::forward<U>(value);
} else {
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
holds_value = true;
}
return *this;
}
optional& operator=(nullopt_t) noexcept {
reset();
return *this;
}
void swap(optional& other) {
if(holds_value && other.holds_value) {
std::swap(uvalue, other.uvalue);
} else if(holds_value && !other.holds_value) {
new (&other.uvalue) T(std::move(uvalue));
uvalue.~T();
} else if(!holds_value && other.holds_value) {
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
other.uvalue.~T();
}
std::swap(holds_value, other.holds_value);
}
bool has_value() const {
return holds_value;
}
operator bool() const {
return holds_value;
}
void reset() {
if(holds_value) {
uvalue.~T();
}
holds_value = false;
}
T& unwrap() & {
if(!holds_value) {
throw std::runtime_error{"Optional does not contain a value"};
}
return uvalue;
}
const T& unwrap() const & {
if(!holds_value) {
throw std::runtime_error{"Optional does not contain a value"};
}
return uvalue;
}
T&& unwrap() && {
if(!holds_value) {
throw std::runtime_error{"Optional does not contain a value"};
}
return std::move(uvalue);
}
const T&& unwrap() const && {
if(!holds_value) {
throw std::runtime_error{"Optional does not contain a value"};
}
return std::move(uvalue);
}
template<typename U>
T value_or(U&& default_value) const & {
return holds_value ? uvalue : static_cast<T>(std::forward<U>(default_value));
}
template<typename U>
T value_or(U&& default_value) && {
return holds_value ? std::move(uvalue) : static_cast<T>(std::forward<U>(default_value));
}
};
// shamelessly stolen from stackoverflow
CPPTRACE_MAYBE_UNUSED
static bool directory_exists(const std::string& path) {
#if IS_WINDOWS
DWORD dwAttrib = GetFileAttributesA(path.c_str());
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
#else
struct stat sb;
return stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode);
#endif
}
CPPTRACE_MAYBE_UNUSED
static std::string basename(const std::string& path) {
// Assumes no trailing /'s
auto pos = path.rfind('/');
if(pos == std::string::npos) {
return path;
} else {
return path.substr(pos + 1);
}
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif #endif

View File

@ -2,6 +2,7 @@
#define ELF_HPP #define ELF_HPP
#include "common.hpp" #include "common.hpp"
#include "utils.hpp"
#if IS_LINUX #if IS_LINUX
#include <array> #include <array>
@ -11,6 +12,8 @@
#include <elf.h> #include <elf.h>
namespace cpptrace {
namespace detail {
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T elf_byteswap_if_needed(T value, bool elf_is_little) { T elf_byteswap_if_needed(T value, bool elf_is_little) {
if(is_little_endian() == elf_is_little) { if(is_little_endian() == elf_is_little) {
@ -20,8 +23,8 @@ T elf_byteswap_if_needed(T value, bool elf_is_little) {
} }
} }
// TODO: Address code duplication here. Do we actually have to care about 32-bit if the library is compiled as 64-bit? // TODO: Address code duplication here. Do we actually have to care about 32-bit if the library is compiled as
// I think probably not... // 64-bit? I think probably not...
// TODO: Re-evaluate use of off_t // TODO: Re-evaluate use of off_t
// I think we can rely on PT_PHDR https://stackoverflow.com/q/61568612/15675011... // I think we can rely on PT_PHDR https://stackoverflow.com/q/61568612/15675011...
@ -58,14 +61,14 @@ static uintptr_t elf_get_module_image_base(const std::string& obj_path) {
} }
// Initial checks/metadata // Initial checks/metadata
auto magic = load_bytes<std::array<char, 4>>(file, 0); auto magic = load_bytes<std::array<char, 4>>(file, 0);
internal_verify(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'})); CPPTRACE_VERIFY(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'}));
bool is_64 = load_bytes<uint8_t>(file, 4) == 2; bool is_64 = load_bytes<uint8_t>(file, 4) == 2;
bool is_little_endian = load_bytes<uint8_t>(file, 5) == 1; bool is_little_endian = load_bytes<uint8_t>(file, 5) == 1;
internal_verify(load_bytes<uint8_t>(file, 6) == 1, "Unexpected ELF version"); CPPTRACE_VERIFY(load_bytes<uint8_t>(file, 6) == 1, "Unexpected ELF version");
// //
if(is_64) { if(is_64) {
Elf64_Ehdr file_header = load_bytes<Elf64_Ehdr>(file, 0); Elf64_Ehdr file_header = load_bytes<Elf64_Ehdr>(file, 0);
internal_verify(file_header.e_ehsize == sizeof(Elf64_Ehdr)); CPPTRACE_VERIFY(file_header.e_ehsize == sizeof(Elf64_Ehdr));
return elf_get_module_image_base_from_program_table( return elf_get_module_image_base_from_program_table(
file, file,
is_64, is_64,
@ -76,7 +79,7 @@ static uintptr_t elf_get_module_image_base(const std::string& obj_path) {
); );
} else { } else {
Elf32_Ehdr file_header = load_bytes<Elf32_Ehdr>(file, 0); Elf32_Ehdr file_header = load_bytes<Elf32_Ehdr>(file, 0);
internal_verify(file_header.e_ehsize == sizeof(Elf32_Ehdr)); CPPTRACE_VERIFY(file_header.e_ehsize == sizeof(Elf32_Ehdr));
return elf_get_module_image_base_from_program_table( return elf_get_module_image_base_from_program_table(
file, file,
is_64, is_64,
@ -87,6 +90,8 @@ static uintptr_t elf_get_module_image_base(const std::string& obj_path) {
); );
} }
} }
}
}
#endif #endif

98
src/platform/error.hpp Normal file
View File

@ -0,0 +1,98 @@
#ifndef ERROR_HPP
#define ERROR_HPP
#include <cstdio>
#include <exception>
#include <stdexcept>
#include "common.hpp"
#if IS_MSVC
#define CPPTRACE_PFUNC __FUNCSIG__
#else
#define CPPTRACE_PFUNC __extension__ __PRETTY_FUNCTION__
#endif
namespace cpptrace {
namespace detail {
class file_error : public std::exception {
const char* what() const noexcept override {
return "Unable to read file";
}
};
// Lightweight std::source_location.
struct source_location {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
const char* const file;
//const char* const function; // disabled for now due to static constexpr restrictions
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
const int line;
constexpr source_location(
//const char* _function /*= __builtin_FUNCTION()*/,
const char* _file = __builtin_FILE(),
int _line = __builtin_LINE()
) : file(_file), /*function(_function),*/ line(_line) {}
};
inline void primitive_assert_impl(
bool condition,
bool verify,
const char* expression,
const char* signature,
source_location location,
const char* message = nullptr
) {
if(!condition) {
const char* action = verify ? "verification" : "assertion";
const char* name = verify ? "VERIFY" : "ASSERT";
if(message == nullptr) {
throw std::runtime_error(
stringf(
"Cpptrace %s failed at %s:%d: %s\n"
" CPPTRACE_%s(%s);\n",
action, location.file, location.line, signature,
name, expression
)
);
} else {
throw std::runtime_error(
stringf(
"Cpptrace %s failed at %s:%d: %s: %s\n"
" CPPTRACE_%s(%s);\n",
action, location.file, location.line, signature, message,
name, expression
)
);
}
}
}
template<typename T>
void nothing() {}
#define PHONY_USE(E) (nothing<decltype(E)>())
// Check condition in both debug and release. std::runtime_error on failure.
#define CPPTRACE_VERIFY(c, ...) ( \
::cpptrace::detail::primitive_assert_impl(c, true, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__) \
)
#ifndef NDEBUG
// Check condition in both debug. std::runtime_error on failure.
#define CPPTRACE_ASSERT(c, ...) ( \
::cpptrace::detail::primitive_assert_impl(c, false, #c, CPPTRACE_PFUNC, {}, ##__VA_ARGS__) \
)
#else
// Check condition in both debug. std::runtime_error on failure.
#define CPPTRACE_ASSERT(c, ...) PHONY_USE(c)
#endif
// TODO: Setting to silence these or make them fatal
inline void nonfatal_error(const std::string& message) {
fprintf(stderr, "Non-fatal cpptrace error: %s\n", message.c_str());
}
}
}
#endif

View File

@ -2,6 +2,7 @@
#define MACHO_HPP #define MACHO_HPP
#include "common.hpp" #include "common.hpp"
#include "utils.hpp"
#if IS_APPLE #if IS_APPLE
#include <cstdio> #include <cstdio>
@ -12,17 +13,6 @@
#include <mach-o/swap.h> #include <mach-o/swap.h>
#include <mach-o/fat.h> #include <mach-o/fat.h>
// Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
// and https://lowlevelbits.org/parsing-mach-o-files/
static bool is_magic_64(uint32_t magic) {
return magic == MH_MAGIC_64 || magic == MH_CIGAM_64;
}
static bool should_swap_bytes(uint32_t magic) {
return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM;
}
#if defined(__aarch64__) #if defined(__aarch64__)
#define CURRENT_CPU CPU_TYPE_ARM64 #define CURRENT_CPU CPU_TYPE_ARM64
#elif defined(__arm__) && defined(__thumb__) #elif defined(__arm__) && defined(__thumb__)
@ -35,7 +25,24 @@ static bool should_swap_bytes(uint32_t magic) {
#error "Unknown CPU architecture" #error "Unknown CPU architecture"
#endif #endif
static uintptr_t macho_get_text_vmaddr_from_segments(FILE* obj_file, off_t offset, bool should_swap, uint32_t ncmds) { namespace cpptrace {
namespace detail {
// Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
// and https://lowlevelbits.org/parsing-mach-o-files/
static bool is_magic_64(uint32_t magic) {
return magic == MH_MAGIC_64 || magic == MH_CIGAM_64;
}
static bool should_swap_bytes(uint32_t magic) {
return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM;
}
static uintptr_t macho_get_text_vmaddr_from_segments(
FILE* obj_file,
off_t offset,
bool should_swap,
uint32_t ncmds
) {
off_t actual_offset = offset; off_t actual_offset = offset;
for(uint32_t i = 0; i < ncmds; i++) { for(uint32_t i = 0; i < ncmds; i++) {
load_command cmd = load_bytes<load_command>(obj_file, actual_offset); load_command cmd = load_bytes<load_command>(obj_file, actual_offset);
@ -151,6 +158,8 @@ static uintptr_t macho_get_text_vmaddr(const char* path) {
fclose(obj_file); fclose(obj_file);
return addr; return addr;
} }
}
}
#endif #endif

View File

@ -2,6 +2,7 @@
#define OBJECT_HPP #define OBJECT_HPP
#include "common.hpp" #include "common.hpp"
#include "utils.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
@ -21,6 +22,8 @@
#include "pe.hpp" #include "pe.hpp"
#endif #endif
namespace cpptrace {
namespace detail {
struct dlframe { struct dlframe {
std::string obj_path; std::string obj_path;
std::string symbol; std::string symbol;
@ -30,7 +33,7 @@ struct dlframe {
#if IS_LINUX || IS_APPLE #if IS_LINUX || IS_APPLE
#if !IS_APPLE #if !IS_APPLE
static uintptr_t get_module_image_base(const std::string& obj_path) { inline uintptr_t get_module_image_base(const std::string& obj_path) {
static std::mutex mutex; static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
static std::unordered_map<std::string, uintptr_t> cache; static std::unordered_map<std::string, uintptr_t> cache;
@ -46,7 +49,7 @@ static uintptr_t get_module_image_base(const std::string& obj_path) {
} }
} }
#else #else
static uintptr_t get_module_image_base(const std::string& obj_path) { inline uintptr_t get_module_image_base(const std::string& obj_path) {
// We have to parse the Mach-O to find the offset of the text section..... // We have to parse the Mach-O to find the offset of the text section.....
// I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for // I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for
// now that there is only one, and I'm using only the first section entry within that load command. // now that there is only one, and I'm using only the first section entry within that load command.
@ -66,7 +69,7 @@ static uintptr_t get_module_image_base(const std::string& obj_path) {
} }
#endif #endif
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on // aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
static std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) { inline std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) {
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
std::vector<dlframe> frames; std::vector<dlframe> frames;
frames.reserve(addrs.size()); frames.reserve(addrs.size());
@ -88,7 +91,7 @@ static std::vector<dlframe> get_frames_object_info(const std::vector<void*>& add
return frames; return frames;
} }
#else #else
static std::string get_module_name(HMODULE handle) { inline std::string get_module_name(HMODULE handle) {
static std::mutex mutex; static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
static std::unordered_map<HMODULE, std::string> cache; static std::unordered_map<HMODULE, std::string> cache;
@ -108,7 +111,8 @@ static std::string get_module_name(HMODULE handle) {
return it->second; return it->second;
} }
} }
static uintptr_t get_module_image_base(const std::string& obj_path) {
inline uintptr_t get_module_image_base(const std::string& obj_path) {
static std::mutex mutex; static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
static std::unordered_map<std::string, uintptr_t> cache; static std::unordered_map<std::string, uintptr_t> cache;
@ -123,8 +127,9 @@ static uintptr_t get_module_image_base(const std::string& obj_path) {
return it->second; return it->second;
} }
} }
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on // aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
static std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) { inline std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) {
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
std::vector<dlframe> frames; std::vector<dlframe> frames;
frames.reserve(addrs.size()); frames.reserve(addrs.size());
@ -150,5 +155,7 @@ static std::vector<dlframe> get_frames_object_info(const std::vector<void*>& add
return frames; return frames;
} }
#endif #endif
}
}
#endif #endif

View File

@ -2,6 +2,8 @@
#define PE_HPP #define PE_HPP
#include "common.hpp" #include "common.hpp"
#include "error.hpp"
#include "utils.hpp"
#if IS_WINDOWS #if IS_WINDOWS
#include <array> #include <array>
@ -12,6 +14,8 @@
#include <windows.h> #include <windows.h>
namespace cpptrace {
namespace detail {
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0> template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T pe_byteswap_if_needed(T value) { T pe_byteswap_if_needed(T value) {
// PE header values are little endian // PE header values are little endian
@ -22,7 +26,7 @@ T pe_byteswap_if_needed(T value) {
} }
} }
static uintptr_t pe_get_module_image_base(const std::string& obj_path) { inline uintptr_t pe_get_module_image_base(const std::string& obj_path) {
FILE* file; FILE* file;
errno_t ret = fopen_s(&file, obj_path.c_str(), "rb"); errno_t ret = fopen_s(&file, obj_path.c_str(), "rb");
if(ret != 0 || file == nullptr) { if(ret != 0 || file == nullptr) {
@ -30,19 +34,19 @@ static uintptr_t pe_get_module_image_base(const std::string& obj_path) {
return 0; return 0;
} }
auto magic = load_bytes<std::array<char, 2>>(file, 0); auto magic = load_bytes<std::array<char, 2>>(file, 0);
internal_verify(memcmp(magic.data(), "MZ", 2) == 0); CPPTRACE_VERIFY(memcmp(magic.data(), "MZ", 2) == 0);
DWORD e_lfanew = pe_byteswap_if_needed(load_bytes<DWORD>(file, 0x3c)); // dos header + 0x3c DWORD e_lfanew = pe_byteswap_if_needed(load_bytes<DWORD>(file, 0x3c)); // dos header + 0x3c
long nt_header_offset = e_lfanew; long nt_header_offset = e_lfanew;
auto signature = load_bytes<std::array<char, 4>>(file, nt_header_offset); // nt header + 0 auto signature = load_bytes<std::array<char, 4>>(file, nt_header_offset); // nt header + 0
internal_verify(memcmp(signature.data(), "PE\0\0", 4) == 0); CPPTRACE_VERIFY(memcmp(signature.data(), "PE\0\0", 4) == 0);
WORD size_of_optional_header = pe_byteswap_if_needed( WORD size_of_optional_header = pe_byteswap_if_needed(
load_bytes<WORD>(file, nt_header_offset + 4 + 0x10) // file header + 0x10 load_bytes<WORD>(file, nt_header_offset + 4 + 0x10) // file header + 0x10
); );
internal_verify(size_of_optional_header != 0); CPPTRACE_VERIFY(size_of_optional_header != 0);
WORD optional_header_magic = pe_byteswap_if_needed( WORD optional_header_magic = pe_byteswap_if_needed(
load_bytes<WORD>(file, nt_header_offset + 0x18) // optional header + 0x0 load_bytes<WORD>(file, nt_header_offset + 0x18) // optional header + 0x0
); );
internal_verify(optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC); CPPTRACE_VERIFY(optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC);
uintptr_t image_base; uintptr_t image_base;
if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
// 32 bit // 32 bit
@ -59,6 +63,9 @@ static uintptr_t pe_get_module_image_base(const std::string& obj_path) {
fclose(file); fclose(file);
return image_base; return image_base;
} }
}
}
#endif #endif
#endif #endif

342
src/platform/utils.hpp Normal file
View File

@ -0,0 +1,342 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <ios>
#include <memory>
#include <new>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "common.hpp"
#include "error.hpp"
#include "utils.hpp"
#if IS_WINDOWS
#include <windows.h>
#else
#include <sys/stat.h>
#endif
namespace cpptrace {
namespace detail {
inline std::vector<std::string> split(const std::string& str, const std::string& delims) {
std::vector<std::string> vec;
size_t old_pos = 0;
size_t pos = 0;
while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) {
vec.emplace_back(str.substr(old_pos, pos - old_pos));
old_pos = pos + 1;
}
vec.emplace_back(str.substr(old_pos));
return vec;
}
template<typename C>
inline std::string join(const C& container, const std::string& delim) {
auto iter = std::begin(container);
auto end = std::end(container);
std::string str;
if(std::distance(iter, end) > 0) {
str += *iter;
while(++iter != end) {
str += delim;
str += *iter;
}
}
return str;
}
constexpr const char* const whitespace = " \t\n\r\f\v";
inline std::string trim(const std::string& str) {
if(str.empty()) {
return "";
}
const size_t left = str.find_first_not_of(whitespace);
const size_t right = str.find_last_not_of(whitespace) + 1;
return str.substr(left, right - left);
}
inline std::string to_hex(uintptr_t addr) {
std::stringstream sstream;
sstream<<std::hex<<addr;
return std::move(sstream).str();
}
inline bool is_little_endian() {
uint16_t num = 0x1;
auto* ptr = (uint8_t*)&num;
return ptr[0] == 1;
}
// Modified from
// https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c
template<typename T, size_t N>
struct byte_swapper;
template<typename T>
struct byte_swapper<T, 1> {
T operator()(T val) {
return val;
}
};
template<typename T>
struct byte_swapper<T, 2> {
T operator()(T val) {
return ((((val) >> 8) & 0xff) | (((val) & 0xff) << 8));
}
};
template<typename T>
struct byte_swapper<T, 4> {
T operator()(T val) {
return ((((val) & 0xff000000) >> 24) |
(((val) & 0x00ff0000) >> 8) |
(((val) & 0x0000ff00) << 8) |
(((val) & 0x000000ff) << 24));
}
};
template<typename T>
struct byte_swapper<T, 8> {
T operator()(T val) {
return ((((val) & 0xff00000000000000ull) >> 56) |
(((val) & 0x00ff000000000000ull) >> 40) |
(((val) & 0x0000ff0000000000ull) >> 24) |
(((val) & 0x000000ff00000000ull) >> 8 ) |
(((val) & 0x00000000ff000000ull) << 8 ) |
(((val) & 0x0000000000ff0000ull) << 24) |
(((val) & 0x000000000000ff00ull) << 40) |
(((val) & 0x00000000000000ffull) << 56));
}
};
template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T byteswap(T value) {
return byte_swapper<T, sizeof(T)>{}(value);
}
inline void enable_virtual_terminal_processing_if_needed() {
// enable colors / ansi processing if necessary
#if IS_WINDOWS
// https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
constexpr DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4;
#endif
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
if(hOut == INVALID_HANDLE_VALUE) return;
if(!GetConsoleMode(hOut, &dwMode)) return;
if(dwMode != (dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))
if(!SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return;
#endif
}
// NOLINTNEXTLINE(misc-no-recursion)
inline constexpr unsigned n_digits(unsigned value) {
return value < 10 ? 1 : 1 + n_digits(value / 10);
}
static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result");
static_assert(n_digits(9) == 1, "n_digits utility producing the wrong result");
static_assert(n_digits(10) == 2, "n_digits utility producing the wrong result");
static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result");
static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result");
// TODO: Re-evaluate use of off_t
template<typename T, typename std::enable_if<std::is_pod<T>::value, int>::type = 0>
T load_bytes(FILE* obj_file, off_t offset) {
T object;
CPPTRACE_VERIFY(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error");
CPPTRACE_VERIFY(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error");
return object;
}
struct nullopt_t {};
static constexpr nullopt_t nullopt;
template<
typename T,
typename std::enable_if<!std::is_same<typename std::decay<T>::type, void>::value, int>::type = 0
>
class optional {
bool holds_value = false;
union {
T uvalue;
};
public:
// clang-tidy false positive
// NOLINTNEXTLINE(modernize-use-equals-default)
optional() noexcept {}
optional(nullopt_t) noexcept {}
~optional() {
reset();
}
optional(const optional& other) : holds_value(other.holds_value) {
if(holds_value) {
new (static_cast<void*>(std::addressof(uvalue))) T(other.uvalue);
}
}
optional(optional&& other)
noexcept(std::is_nothrow_move_constructible<T>::value)
: holds_value(other.holds_value)
{
if(holds_value) {
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
}
}
optional& operator=(const optional& other) {
optional copy(other);
swap(*this, copy);
return *this;
}
optional& operator=(optional&& other)
noexcept(std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value)
{
reset();
if(other.holds_value) {
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
holds_value = true;
}
return *this;
}
template<
typename U = T,
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
>
// clang-tidy false positive
// NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
optional(U&& value) : holds_value(true) {
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
}
template<
typename U = T,
typename std::enable_if<!std::is_same<typename std::decay<U>::type, optional<T>>::value, int>::type = 0
>
optional& operator=(U&& value) {
if(holds_value) {
uvalue = std::forward<U>(value);
} else {
new (static_cast<void*>(std::addressof(uvalue))) T(std::forward<U>(value));
holds_value = true;
}
return *this;
}
optional& operator=(nullopt_t) noexcept {
reset();
return *this;
}
void swap(optional& other) {
if(holds_value && other.holds_value) {
std::swap(uvalue, other.uvalue);
} else if(holds_value && !other.holds_value) {
new (&other.uvalue) T(std::move(uvalue));
uvalue.~T();
} else if(!holds_value && other.holds_value) {
new (static_cast<void*>(std::addressof(uvalue))) T(std::move(other.uvalue));
other.uvalue.~T();
}
std::swap(holds_value, other.holds_value);
}
bool has_value() const {
return holds_value;
}
operator bool() const {
return holds_value;
}
void reset() {
if(holds_value) {
uvalue.~T();
}
holds_value = false;
}
T& unwrap() & {
if(!holds_value) {
throw std::runtime_error{"Optional does not contain a value"};
}
return uvalue;
}
const T& unwrap() const & {
if(!holds_value) {
throw std::runtime_error{"Optional does not contain a value"};
}
return uvalue;
}
T&& unwrap() && {
if(!holds_value) {
throw std::runtime_error{"Optional does not contain a value"};
}
return std::move(uvalue);
}
const T&& unwrap() const && {
if(!holds_value) {
throw std::runtime_error{"Optional does not contain a value"};
}
return std::move(uvalue);
}
template<typename U>
T value_or(U&& default_value) const & {
return holds_value ? uvalue : static_cast<T>(std::forward<U>(default_value));
}
template<typename U>
T value_or(U&& default_value) && {
return holds_value ? std::move(uvalue) : static_cast<T>(std::forward<U>(default_value));
}
};
// shamelessly stolen from stackoverflow
inline bool directory_exists(const std::string& path) {
#if IS_WINDOWS
DWORD dwAttrib = GetFileAttributesA(path.c_str());
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
#else
struct stat sb;
return stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode);
#endif
}
inline std::string basename(const std::string& path) {
// Assumes no trailing /'s
auto pos = path.rfind('/');
if(pos == std::string::npos) {
return path;
} else {
return path.substr(pos + 1);
}
}
}
}
#endif

View File

@ -2,6 +2,8 @@
#include <vector> #include <vector>
#include "../platform/object.hpp"
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
void apply_trace( void apply_trace(

View File

@ -3,6 +3,7 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "symbols.hpp" #include "symbols.hpp"
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/utils.hpp"
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
@ -77,8 +78,8 @@ namespace cpptrace {
std::string resolve_addresses(const std::string& addresses, const std::string& executable) { std::string resolve_addresses(const std::string& addresses, const std::string& executable) {
pipe_t output_pipe; pipe_t output_pipe;
pipe_t input_pipe; pipe_t input_pipe;
internal_verify(pipe(output_pipe.data) == 0); CPPTRACE_VERIFY(pipe(output_pipe.data) == 0);
internal_verify(pipe(input_pipe.data) == 0); CPPTRACE_VERIFY(pipe(input_pipe.data) == 0);
// NOLINTNEXTLINE(misc-include-cleaner) // NOLINTNEXTLINE(misc-include-cleaner)
const pid_t pid = fork(); const pid_t pid = fork();
if(pid == -1) { return ""; } // error? TODO: Diagnostic if(pid == -1) { return ""; } // error? TODO: Diagnostic
@ -123,7 +124,7 @@ namespace cpptrace {
#endif #endif
_exit(1); // TODO: Diagnostic? _exit(1); // TODO: Diagnostic?
} }
internal_verify(write(input_pipe.write_end, addresses.data(), addresses.size()) != -1); CPPTRACE_VERIFY(write(input_pipe.write_end, addresses.data(), addresses.size()) != -1);
close(input_pipe.read_end); close(input_pipe.read_end);
close(input_pipe.write_end); close(input_pipe.write_end);
close(output_pipe.write_end); close(output_pipe.write_end);
@ -241,14 +242,14 @@ namespace cpptrace {
symbol_end = at_location; symbol_end = at_location;
filename_start = at_location + 4; filename_start = at_location + 4;
} else { } else {
internal_verify(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output"); CPPTRACE_VERIFY(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output");
symbol_end = 2; symbol_end = 2;
filename_start = 3; filename_start = 3;
} }
auto symbol = line.substr(0, symbol_end); auto symbol = line.substr(0, symbol_end);
auto colon = line.rfind(':'); auto colon = line.rfind(':');
internal_verify(colon != std::string::npos); CPPTRACE_VERIFY(colon != std::string::npos);
internal_verify(colon >= filename_start); // :? to deal with "symbol :?" edge case CPPTRACE_VERIFY(colon >= filename_start); // :? to deal with "symbol :?" edge case
auto filename = line.substr(filename_start, colon - filename_start); auto filename = line.substr(filename_start, colon - filename_start);
auto line_number = line.substr(colon + 1); auto line_number = line.substr(colon + 1);
if(line_number != "?") { if(line_number != "?") {
@ -278,7 +279,7 @@ namespace cpptrace {
const std::size_t symbol_end = in_location; const std::size_t symbol_end = in_location;
entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end); entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end);
const std::size_t obj_end = line.find(")", in_location); const std::size_t obj_end = line.find(")", in_location);
internal_verify( CPPTRACE_VERIFY(
obj_end != std::string::npos, obj_end != std::string::npos,
"Unexpected edge case while processing addr2line/atos output" "Unexpected edge case while processing addr2line/atos output"
); );
@ -288,7 +289,7 @@ namespace cpptrace {
return; return;
} }
const std::size_t filename_end = line.find(":", filename_start); const std::size_t filename_end = line.find(":", filename_start);
internal_verify( CPPTRACE_VERIFY(
filename_end != std::string::npos, filename_end != std::string::npos,
"Unexpected edge case while processing addr2line/atos output" "Unexpected edge case while processing addr2line/atos output"
); );
@ -298,7 +299,7 @@ namespace cpptrace {
); );
const std::size_t line_start = filename_end + 1; const std::size_t line_start = filename_end + 1;
const std::size_t line_end = line.find(")", filename_end); const std::size_t line_end = line.find(")", filename_end);
internal_verify( CPPTRACE_VERIFY(
line_end == line.size() - 1, line_end == line.size() - 1,
"Unexpected edge case while processing addr2line/atos output" "Unexpected edge case while processing addr2line/atos output"
); );
@ -336,7 +337,7 @@ namespace cpptrace {
#endif #endif
} }
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n"); auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
internal_verify(output.size() == entries_vec.size()); CPPTRACE_VERIFY(output.size() == entries_vec.size());
for(size_t i = 0; i < output.size(); i++) { for(size_t i = 0; i < output.size(); i++) {
update_trace(output[i], i, entries_vec); update_trace(output[i], i, entries_vec);
} }

View File

@ -5,8 +5,9 @@
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/program_name.hpp" #include "../platform/program_name.hpp"
#include "../platform/object.hpp" #include "../platform/object.hpp"
#include "../platform/error.hpp"
#include "../platform/utils.hpp"
#include <cassert>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <functional> #include <functional>
@ -40,10 +41,10 @@ Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) {
dwarf_whatform(attr, &attrform, nullptr); dwarf_whatform(attr, &attrform, nullptr);
if (attrform == DW_FORM_rnglistx) { if (attrform == DW_FORM_rnglistx) {
int fres = dwarf_formudata(attr, &off, nullptr); int fres = dwarf_formudata(attr, &off, nullptr);
assert(fres == DW_DLV_OK); CPPTRACE_VERIFY(fres == DW_DLV_OK);
} else { } else {
int fres = dwarf_global_formref(attr, &off, nullptr); int fres = dwarf_global_formref(attr, &off, nullptr);
assert(fres == DW_DLV_OK); CPPTRACE_VERIFY(fres == DW_DLV_OK);
} }
return off; return off;
} }
@ -76,7 +77,7 @@ static int dwarf5_ranges(Dwarf_Die cu_die, Dwarf_Addr *lowest, Dwarf_Addr *highe
&rlesetoffset, &rlesetoffset,
nullptr nullptr
); );
assert(res == DW_DLV_OK); CPPTRACE_VERIFY(res == DW_DLV_OK);
if(res != DW_DLV_OK) { if(res != DW_DLV_OK) {
/* ASSERT: is DW_DLV_NO_ENTRY */ /* ASSERT: is DW_DLV_NO_ENTRY */
dwarf_dealloc_attribute(attr); dwarf_dealloc_attribute(attr);
@ -129,7 +130,7 @@ static int dwarf5_ranges(Dwarf_Die cu_die, Dwarf_Addr *lowest, Dwarf_Addr *highe
} }
break; break;
default: default:
assert(false); CPPTRACE_VERIFY(false);
/* Something is wrong. */ /* Something is wrong. */
break; break;
} }
@ -358,7 +359,7 @@ namespace cpptrace {
char* raw_str; char* raw_str;
std::string str; std::string str;
ret = dwarf_formstring(attr, &raw_str, nullptr); ret = dwarf_formstring(attr, &raw_str, nullptr);
assert(ret == DW_DLV_OK); CPPTRACE_VERIFY(ret == DW_DLV_OK);
str = raw_str; str = raw_str;
dwarf_dealloc(dbg, raw_str, DW_DLA_STRING); dwarf_dealloc(dbg, raw_str, DW_DLA_STRING);
dwarf_dealloc_attribute(attr); dwarf_dealloc_attribute(attr);
@ -398,7 +399,7 @@ namespace cpptrace {
Dwarf_Off get_global_offset() const { Dwarf_Off get_global_offset() const {
Dwarf_Off off; Dwarf_Off off;
int ret = dwarf_dieoffset(die, &off, nullptr); int ret = dwarf_dieoffset(die, &off, nullptr);
assert(ret == DW_DLV_OK); CPPTRACE_VERIFY(ret == DW_DLV_OK);
return off; return off;
} }
@ -417,13 +418,13 @@ namespace cpptrace {
Dwarf_Off off = 0; Dwarf_Off off = 0;
Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die); Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
ret = dwarf_formref(attr, &off, &is_info, nullptr); ret = dwarf_formref(attr, &off, &is_info, nullptr);
assert(ret == DW_DLV_OK); CPPTRACE_VERIFY(ret == DW_DLV_OK);
Dwarf_Off goff = 0; Dwarf_Off goff = 0;
ret = dwarf_convert_to_global_offset(attr, off, &goff, nullptr); ret = dwarf_convert_to_global_offset(attr, off, &goff, nullptr);
assert(ret == DW_DLV_OK); CPPTRACE_VERIFY(ret == DW_DLV_OK);
Dwarf_Die targ_die_a = 0; Dwarf_Die targ_die_a = 0;
ret = dwarf_offdie_b(dbg, goff, is_info, &targ_die_a, nullptr); ret = dwarf_offdie_b(dbg, goff, is_info, &targ_die_a, nullptr);
assert(ret == DW_DLV_OK); CPPTRACE_VERIFY(ret == DW_DLV_OK);
dwarf_dealloc_attribute(attr); dwarf_dealloc_attribute(attr);
return die_object(dbg, targ_die_a); return die_object(dbg, targ_die_a);
} }
@ -434,7 +435,7 @@ namespace cpptrace {
int is_info_a = dwarf_get_die_infotypes_flag(die); int is_info_a = dwarf_get_die_infotypes_flag(die);
Dwarf_Die targ_die_a = 0; Dwarf_Die targ_die_a = 0;
ret = dwarf_offdie_b(dbg, off, is_info_a, &targ_die_a, nullptr); ret = dwarf_offdie_b(dbg, off, is_info_a, &targ_die_a, nullptr);
assert(ret == DW_DLV_OK); CPPTRACE_VERIFY(ret == DW_DLV_OK);
dwarf_dealloc_attribute(attr); dwarf_dealloc_attribute(attr);
return die_object(dbg, targ_die_a); return die_object(dbg, targ_die_a);
} }
@ -442,11 +443,11 @@ namespace cpptrace {
{ {
Dwarf_Sig8 signature; Dwarf_Sig8 signature;
ret = dwarf_formsig8(attr, &signature, nullptr); ret = dwarf_formsig8(attr, &signature, nullptr);
assert(ret == DW_DLV_OK); CPPTRACE_VERIFY(ret == DW_DLV_OK);
Dwarf_Die targdie = 0; Dwarf_Die targdie = 0;
Dwarf_Bool targ_is_info = false; Dwarf_Bool targ_is_info = false;
ret = dwarf_find_die_given_sig8(dbg, &signature, &targdie, &targ_is_info, nullptr); ret = dwarf_find_die_given_sig8(dbg, &signature, &targdie, &targ_is_info, nullptr);
assert(ret == DW_DLV_OK); CPPTRACE_VERIFY(ret == DW_DLV_OK);
dwarf_dealloc_attribute(attr); dwarf_dealloc_attribute(attr);
return die_object(dbg, targdie); return die_object(dbg, targdie);
} }
@ -598,7 +599,7 @@ namespace cpptrace {
std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = ""); std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build = "");
std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) { std::string get_array_extents(Dwarf_Debug dbg, const die_object& die) {
assert(die.get_tag() == DW_TAG_array_type); CPPTRACE_VERIFY(die.get_tag() == DW_TAG_array_type);
std::string extents = ""; std::string extents = "";
walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) { walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) {
if(subrange.get_tag() == DW_TAG_subrange_type) { if(subrange.get_tag() == DW_TAG_subrange_type) {
@ -632,7 +633,7 @@ namespace cpptrace {
} }
std::string get_parameters(Dwarf_Debug dbg, const die_object& die) { std::string get_parameters(Dwarf_Debug dbg, const die_object& die) {
assert(die.get_tag() == DW_TAG_subroutine_type); CPPTRACE_VERIFY(die.get_tag() == DW_TAG_subroutine_type);
std::vector<std::string> params; std::vector<std::string> params;
walk_die_list(dbg, die.get_child(), [&params](Dwarf_Debug dbg, const die_object& die) { walk_die_list(dbg, die.get_child(), [&params](Dwarf_Debug dbg, const die_object& die) {
if(die.get_tag() == DW_TAG_formal_parameter) { if(die.get_tag() == DW_TAG_formal_parameter) {
@ -757,7 +758,7 @@ namespace cpptrace {
Dwarf_Half dwversion, Dwarf_Half dwversion,
stacktrace_frame& frame stacktrace_frame& frame
) { ) {
assert(die.get_tag() == DW_TAG_subprogram); CPPTRACE_VERIFY(die.get_tag() == DW_TAG_subprogram);
optional<std::string> name; optional<std::string> name;
if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) { if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) {
name = std::move(linkage_name); name = std::move(linkage_name);
@ -955,7 +956,7 @@ namespace cpptrace {
//if(dwversion >= 5) { //if(dwversion >= 5) {
// Dwarf_Attribute attr; // Dwarf_Attribute attr;
// int ret = dwarf_attr(cu_die.get(), DW_AT_rnglists_base, &attr, nullptr); // int ret = dwarf_attr(cu_die.get(), DW_AT_rnglists_base, &attr, nullptr);
// assert(ret == DW_DLV_OK); // CPPTRACE_VERIFY(ret == DW_DLV_OK);
// Dwarf_Unsigned uval = 0; // Dwarf_Unsigned uval = 0;
// ret = dwarf_global_formref(attr, &uval, nullptr); // ret = dwarf_global_formref(attr, &uval, nullptr);
// offset = uval; // offset = uval;

View File

@ -2,6 +2,7 @@
#define UNWIND_HPP #define UNWIND_HPP
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/utils.hpp"
#include <cstddef> #include <cstddef>
#include <vector> #include <vector>

View File

@ -2,6 +2,7 @@
#include "unwind.hpp" #include "unwind.hpp"
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/utils.hpp"
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>

View File

@ -2,6 +2,8 @@
#include "unwind.hpp" #include "unwind.hpp"
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/error.hpp"
#include "../platform/utils.hpp"
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
@ -29,7 +31,10 @@ namespace cpptrace {
} }
} }
assert(state.count < state.vec.size()); CPPTRACE_VERIFY(
state.count < state.vec.size(),
"Somehow cpptrace::detail::unwind_callback is overflowing a vector"
);
int is_before_instruction = 0; int is_before_instruction = 0;
uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction); uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction);
if(!is_before_instruction && ip != uintptr_t(0)) { if(!is_before_instruction && ip != uintptr_t(0)) {

View File

@ -3,6 +3,7 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
#include "unwind.hpp" #include "unwind.hpp"
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/utils.hpp"
#include <vector> #include <vector>