Index: XIOS3/trunk/extern/cpptrace/src/common.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/common.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/common.hpp	(revision 2573)
@@ -0,0 +1,450 @@
+#ifndef COMMON_HPP
+#define COMMON_HPP
+
+#ifdef _MSC_VER
+#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
+#define CPPTRACE_FORCE_NO_INLINE __attribute__((noinline))
+#define CPPTRACE_PFUNC __extension__ __PRETTY_FUNCTION__
+#define CPPTRACE_MAYBE_UNUSED __attribute__((unused))
+#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_LINUX 0
+#define IS_APPLE 0
+
+#if defined(_WIN32)
+ #undef IS_WINDOWS
+ #define IS_WINDOWS 1
+#elif defined(__linux)
+ #undef IS_LINUX
+ #define IS_LINUX 1
+#elif defined(__APPLE__)
+ #undef IS_APPLE
+ #define IS_APPLE 1
+#else
+ #error "Unexpected platform"
+#endif
+
+#define IS_CLANG 0
+#define IS_GCC 0
+#define IS_MSVC 0
+
+#if defined(__clang__)
+ #undef IS_CLANG
+ #define IS_CLANG 1
+#elif defined(__GNUC__) || defined(__GNUG__)
+ #undef IS_GCC
+ #define IS_GCC 1
+#elif defined(_MSC_VER)
+ #undef IS_MSVC
+ #define IS_MSVC 1
+#else
+ #error "Unsupported compiler"
+#endif
+
+#if IS_WINDOWS
+ #include <windows.h>
+#else
+ #include <sys/stat.h>
+#endif
+
+// 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()
+        const char* _file     = __FILE__,
+        int _line             = __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;
+        }
+    }
+    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
Index: XIOS3/trunk/extern/cpptrace/src/cpptrace.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/cpptrace.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/cpptrace.cpp	(revision 2573)
@@ -0,0 +1,103 @@
+#include "cpptrace.hpp"
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <vector>
+#include <iomanip>
+#include <iostream>
+
+#if !(defined(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE) || defined(CPPTRACE_FULL_TRACE_WITH_STACKTRACE))
+
+#include "symbols.hpp"
+#include "unwind.hpp"
+#include "demangle.hpp"
+#include "common.hpp"
+
+namespace cpptrace {
+    CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
+    std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
+        std::vector<void*> frames = detail::capture_frames(skip + 1);
+        std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
+        for(auto& frame : trace) {
+            frame.symbol = detail::demangle(frame.symbol);
+        }
+        return trace;
+    }
+}
+
+#else
+
+// full trace
+
+#include "full_trace.hpp"
+#include "demangle.hpp"
+
+namespace cpptrace {
+    CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
+    std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
+        auto trace = detail::generate_trace(skip + 1);
+        for(auto& entry : trace) {
+            entry.symbol = detail::demangle(entry.symbol);
+        }
+        return trace;
+    }
+}
+
+#endif
+
+#define ESC     "\033["
+#define RESET   ESC "0m"
+#define RED     ESC "31m"
+#define GREEN   ESC "32m"
+#define YELLOW  ESC "33m"
+#define BLUE    ESC "34m"
+#define MAGENTA ESC "35m"
+#define CYAN    ESC "36m"
+
+namespace cpptrace {
+    CPPTRACE_API
+    void print_trace(std::uint32_t skip) {
+        enable_virtual_terminal_processing_if_needed();
+        std::cerr<<"Stack trace (most recent call first):"<<std::endl;
+        std::size_t counter = 0;
+        const auto trace = generate_trace(skip + 1);
+        if(trace.empty()) {
+            std::cerr<<"<empty trace>"<<std::endl;
+            return;
+        }
+        const auto frame_number_width = n_digits(static_cast<int>(trace.size()) - 1);
+        for(const auto& frame : trace) {
+            std::cerr
+                << '#'
+                << std::setw(static_cast<int>(frame_number_width))
+                << std::left
+                << counter++
+                << std::right
+                << " "
+                << std::hex
+                << BLUE
+                << "0x"
+                << std::setw(2 * sizeof(uintptr_t))
+                << std::setfill('0')
+                << frame.address
+                << std::dec
+                << std::setfill(' ')
+                << RESET
+                << " in "
+                << YELLOW
+                << frame.symbol
+                << RESET
+                << " at "
+                << GREEN
+                << frame.filename
+                << RESET
+                << ":"
+                << BLUE
+                << frame.line
+                << RESET
+                << (frame.col > 0 ? ":" BLUE + std::to_string(frame.col) + RESET : "")
+                << std::endl;
+        }
+    }
+}
Index: XIOS3/trunk/extern/cpptrace/src/cpptrace.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/cpptrace.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/cpptrace.hpp	(revision 2573)
@@ -0,0 +1,28 @@
+#ifndef CPPTRACE_HPP
+#define CPPTRACE_HPP
+
+#include "cpptrace_default.hpp"
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+ #define CPPTRACE_API __declspec(dllexport)
+#else
+ #define CPPTRACE_API
+#endif
+
+namespace cpptrace {
+    struct stacktrace_frame {
+        uintptr_t address;
+        std::uint_least32_t line;
+        std::uint_least32_t col;
+        std::string filename;
+        std::string symbol;
+    };
+    CPPTRACE_API std::vector<stacktrace_frame> generate_trace(std::uint32_t skip = 0);
+    CPPTRACE_API void print_trace(std::uint32_t skip = 0);
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/cpptrace_default.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/cpptrace_default.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/cpptrace_default.hpp	(revision 2573)
@@ -0,0 +1,16 @@
+#ifndef CPPTRACE_DEFAULT_HPP
+#define CPPTRACE_DEFAULT_HPP
+
+#if !defined(CPPTRACE_UNWIND_WITH_UNWIND) && !defined(CPPTRACE_UNWIND_WITH_EXECINFO) && !defined(CPPTRACE_UNWIND_WITH_WINAPI) && !defined(CPPTRACE_UNWIND_WITH_NOTHING)
+#define CPPTRACE_UNWIND_WITH_NOTHING
+#endif
+
+#if !defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) && !defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) && !defined(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) && !defined(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) && !defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDL)
+#define CPPTRACE_GET_SYMBOLS_WITH_NOTHING
+#endif
+
+#if !defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF)
+#define CPPTRACE_DEMANGLE_WITH_NOTHING
+#endif
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/demangle.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/demangle.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/demangle.hpp	(revision 2573)
@@ -0,0 +1,14 @@
+#ifndef DEMANGLE_HPP
+#define DEMANGLE_HPP
+
+#include "cpptrace_default.hpp"
+
+#include <string>
+
+namespace cpptrace {
+    namespace detail {
+        std::string demangle(const std::string&);
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/demangle_with_cxxabi.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/demangle_with_cxxabi.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/demangle_with_cxxabi.cpp	(revision 2573)
@@ -0,0 +1,28 @@
+#ifdef CPPTRACE_DEMANGLE_WITH_CXXABI
+
+#include "demangle.hpp"
+
+#include <cxxabi.h>
+
+#include <cstdlib>
+#include <string>
+
+namespace cpptrace {
+    namespace detail {
+        std::string demangle(const std::string& name) {
+            int status;
+            // presumably thread-safe
+            char* demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
+            if(demangled) {
+                std::string str = demangled;
+                // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
+                free(demangled);
+                return str;
+            } else {
+                return name;
+            }
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/demangle_with_nothing.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/demangle_with_nothing.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/demangle_with_nothing.cpp	(revision 2573)
@@ -0,0 +1,16 @@
+#include "cpptrace_default.hpp"
+#ifdef CPPTRACE_DEMANGLE_WITH_NOTHING
+
+#include "demangle.hpp"
+
+#include <string>
+
+namespace cpptrace {
+    namespace detail {
+        std::string demangle(const std::string& name) {
+            return name;
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/elf.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/elf.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/elf.hpp	(revision 2573)
@@ -0,0 +1,93 @@
+#ifndef ELF_HPP
+#define ELF_HPP
+
+#include "common.hpp"
+
+#if IS_LINUX
+#include <array>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+
+#include <elf.h>
+
+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) {
+    if(is_little_endian() == elf_is_little) {
+        return value;
+    } else {
+        return byteswap(value);
+    }
+}
+
+// TODO: Address code duplication here. Do we actually have to care about 32-bit if the library is compiled as 64-bit?
+// I think probably not...
+
+// TODO: Re-evaluate use of off_t
+// I think we can rely on PT_PHDR https://stackoverflow.com/q/61568612/15675011...
+static uintptr_t elf_get_module_image_base_from_program_table(
+    FILE* file,
+    bool is_64,
+    bool is_little_endian,
+    off_t e_phoff,
+    off_t e_phentsize,
+    int e_phnum
+) {
+    for(int i = 0; i < e_phnum; i++) {
+        if(is_64) {
+            Elf64_Phdr program_header = load_bytes<Elf64_Phdr>(file, e_phoff + e_phentsize * i);
+            if(elf_byteswap_if_needed(program_header.p_type, is_little_endian) == PT_PHDR) {
+                return elf_byteswap_if_needed(program_header.p_vaddr, is_little_endian)
+                        - elf_byteswap_if_needed(program_header.p_offset, is_little_endian);
+            }
+        } else {
+            Elf32_Phdr program_header = load_bytes<Elf32_Phdr>(file, e_phoff + e_phentsize * i);
+            if(elf_byteswap_if_needed(program_header.p_type, is_little_endian) == PT_PHDR) {
+                return elf_byteswap_if_needed(program_header.p_vaddr, is_little_endian)
+                        - elf_byteswap_if_needed(program_header.p_offset, is_little_endian);
+            }
+        }
+    }
+    return 0;
+}
+
+static uintptr_t elf_get_module_image_base(const std::string& obj_path) {
+    FILE* file = fopen(obj_path.c_str(), "rb");
+    if(file == nullptr) {
+        throw file_error();
+    }
+    // Initial checks/metadata
+    auto magic = load_bytes<std::array<char, 4>>(file, 0);
+    internal_verify(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'}));
+    bool is_64 = load_bytes<uint8_t>(file, 4) == 2;
+    bool is_little_endian = load_bytes<uint8_t>(file, 5) == 1;
+    internal_verify(load_bytes<uint8_t>(file, 6) == 1, "Unexpected ELF version");
+    //
+    if(is_64) {
+        Elf64_Ehdr file_header = load_bytes<Elf64_Ehdr>(file, 0);
+        internal_verify(file_header.e_ehsize == sizeof(Elf64_Ehdr));
+        return elf_get_module_image_base_from_program_table(
+            file,
+            is_64,
+            is_little_endian,
+            elf_byteswap_if_needed(file_header.e_phoff, is_little_endian),
+            elf_byteswap_if_needed(file_header.e_phentsize, is_little_endian),
+            elf_byteswap_if_needed(file_header.e_phnum, is_little_endian)
+        );
+    } else {
+        Elf32_Ehdr file_header = load_bytes<Elf32_Ehdr>(file, 0);
+        internal_verify(file_header.e_ehsize == sizeof(Elf32_Ehdr));
+        return elf_get_module_image_base_from_program_table(
+            file,
+            is_64,
+            is_little_endian,
+            elf_byteswap_if_needed(file_header.e_phoff, is_little_endian),
+            elf_byteswap_if_needed(file_header.e_phentsize, is_little_endian),
+            elf_byteswap_if_needed(file_header.e_phnum, is_little_endian)
+        );
+    }
+}
+
+#endif
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/full_trace.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/full_trace.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/full_trace.hpp	(revision 2573)
@@ -0,0 +1,17 @@
+#ifndef FULL_TRACE_HPP
+#define FULL_TRACE_HPP
+
+#include "cpptrace.hpp"
+#include "common.hpp"
+
+#include <cstddef>
+#include <vector>
+
+namespace cpptrace {
+    namespace detail {
+        CPPTRACE_FORCE_NO_INLINE
+        std::vector<stacktrace_frame> generate_trace(size_t skip);
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/full_trace_with_libbacktrace.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/full_trace_with_libbacktrace.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/full_trace_with_libbacktrace.cpp	(revision 2573)
@@ -0,0 +1,93 @@
+#ifdef CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE
+
+#include "cpptrace.hpp"
+#include "program_name.hpp"
+#include "common.hpp"
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <mutex>
+#include <vector>
+
+#ifdef CPPTRACE_BACKTRACE_PATH
+#include CPPTRACE_BACKTRACE_PATH
+#else
+#include <backtrace.h>
+#endif
+
+namespace cpptrace {
+    namespace detail {
+        struct trace_data {
+            // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
+            std::vector<stacktrace_frame>& frames;
+            // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
+            size_t& skip;
+        };
+
+        int full_callback(void* data_pointer, uintptr_t address, const char* file, int line, const char* symbol) {
+            trace_data& data = *reinterpret_cast<trace_data*>(data_pointer);
+            if(data.skip > 0) {
+                data.skip--;
+            } else if(address == uintptr_t(-1)) {
+                // sentinel for libbacktrace, stop tracing
+                return 1;
+            } else {
+                data.frames.push_back({
+                    address,
+                    static_cast<std::uint_least32_t>(line),
+                    0,
+                    file ? file : "",
+                    symbol ? symbol : ""
+                });
+            }
+            return 0;
+        }
+
+        void syminfo_callback(void* data, uintptr_t, const char* symbol, uintptr_t, uintptr_t) {
+            stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data);
+            frame.symbol = symbol ? symbol : "";
+        }
+
+        void error_callback(void*, const char* msg, int errnum) {
+            fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum);
+        }
+
+        backtrace_state* get_backtrace_state() {
+            static std::mutex mutex;
+            const std::lock_guard<std::mutex> lock(mutex);
+            // backtrace_create_state must be called only one time per program
+            // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+            static backtrace_state* state = nullptr;
+            static bool called = false;
+            if(!called) {
+                state = backtrace_create_state(nullptr, true, error_callback, nullptr);
+                called = true;
+            }
+            return state;
+        }
+
+        CPPTRACE_FORCE_NO_INLINE
+        std::vector<stacktrace_frame> generate_trace(size_t skip) {
+            std::vector<stacktrace_frame> frames;
+            skip++; // add one for this call
+            trace_data data { frames, skip };
+            backtrace_full(get_backtrace_state(), 0, full_callback, error_callback, &data);
+            for(auto& frame : frames) {
+                if(frame.symbol.empty()) {
+                    // fallback, try to at least recover the symbol name with backtrace_syminfo
+                    backtrace_syminfo(
+                        get_backtrace_state(),
+                        frame.address,
+                        syminfo_callback,
+                        error_callback,
+                        &frame
+                    );
+                }
+            }
+            return frames;
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/full_trace_with_stacktrace.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/full_trace_with_stacktrace.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/full_trace_with_stacktrace.cpp	(revision 2573)
@@ -0,0 +1,30 @@
+#ifdef CPPTRACE_FULL_TRACE_WITH_STACKTRACE
+
+#include "cpptrace.hpp"
+#include "full_trace.hpp"
+#include "common.hpp"
+
+#include <vector>
+#include <stacktrace>
+
+namespace cpptrace {
+    namespace detail {
+        CPPTRACE_FORCE_NO_INLINE
+        std::vector<stacktrace_frame> generate_trace(size_t skip) {
+            std::vector<stacktrace_frame> frames;
+            std::stacktrace trace = std::stacktrace::current(skip + 1);
+            for(const auto entry : trace) {
+                frames.push_back({
+                    entry.native_handle(),
+                    entry.source_line(),
+                    0,
+                    entry.source_file(),
+                    entry.description()
+                });
+            }
+            return frames;
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/mach-o.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/mach-o.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/mach-o.hpp	(revision 2573)
@@ -0,0 +1,157 @@
+#ifndef MACHO_HPP
+#define MACHO_HPP
+
+#include "common.hpp"
+
+#if IS_APPLE
+#include <cstdio>
+#include <cstring>
+#include <type_traits>
+
+#include <mach-o/loader.h>
+#include <mach-o/swap.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__)
+ #define CURRENT_CPU CPU_TYPE_ARM64
+#elif defined(__arm__) && defined(__thumb__)
+ #define CURRENT_CPU CPU_TYPE_ARM
+#elif defined(__amd64__)
+ #define CURRENT_CPU CPU_TYPE_X86_64
+#elif defined(__i386__)
+ #define CURRENT_CPU CPU_TYPE_I386
+#else
+ #error "Unknown CPU architecture"
+#endif
+
+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;
+    for(uint32_t i = 0; i < ncmds; i++) {
+        load_command cmd = load_bytes<load_command>(obj_file, actual_offset);
+        if(should_swap) {
+            swap_load_command(&cmd, NX_UnknownByteOrder);
+        }
+        if(cmd.cmd == LC_SEGMENT_64) {
+            segment_command_64 segment = load_bytes<segment_command_64>(obj_file, actual_offset);
+            if(should_swap) {
+                swap_segment_command_64(&segment, NX_UnknownByteOrder);
+            }
+            //printf("segname(64): %s\n", segment.segname);
+            //printf("             %d\n", segment.nsects);
+            //printf("             %p\n", segment.vmaddr);
+            //printf("             %p\n", segment.vmsize);
+            if(strcmp(segment.segname, "__TEXT") == 0) {
+                return segment.vmaddr;
+            }
+        } else if(cmd.cmd == LC_SEGMENT) {
+            segment_command segment = load_bytes<segment_command>(obj_file, actual_offset);
+            if(should_swap) {
+                swap_segment_command(&segment, NX_UnknownByteOrder);
+            }
+            //printf("segname: %s\n", segment.segname);
+            if(strcmp(segment.segname, "__TEXT") == 0) {
+                return segment.vmaddr;
+            }
+        }
+        actual_offset += cmd.cmdsize;
+    }
+    // somehow no __TEXT section was found...
+    return 0;
+}
+
+static uintptr_t macho_get_text_vmaddr_mach(FILE* obj_file, off_t offset, bool is_64, bool should_swap) {
+    uint32_t ncmds;
+    off_t load_commands_offset = offset;
+    if(is_64) {
+        size_t header_size = sizeof(mach_header_64);
+        mach_header_64 header = load_bytes<mach_header_64>(obj_file, offset);
+        //if(offset != 0) { // if fat the offset will be non-zero, if not fat the offset will be zero
+            if(header.cputype != CURRENT_CPU) {
+                return 0;
+            }
+        //}
+        if(should_swap) {
+            swap_mach_header_64(&header, NX_UnknownByteOrder);
+        }
+        ncmds = header.ncmds;
+        load_commands_offset += header_size;
+    } else {
+        size_t header_size = sizeof(mach_header);
+        mach_header header = load_bytes<mach_header>(obj_file, offset);
+        //if(offset != 0) { // if fat the offset will be non-zero, if not fat the offset will be zero
+            if(header.cputype != CURRENT_CPU) {
+                return 0;
+            }
+        //}
+        if(should_swap) {
+            swap_mach_header(&header, NX_UnknownByteOrder);
+        }
+        ncmds = header.ncmds;
+        load_commands_offset += header_size;
+    }
+    return macho_get_text_vmaddr_from_segments(obj_file, load_commands_offset, should_swap, ncmds);
+}
+
+static uintptr_t macho_get_text_vmaddr_fat(FILE* obj_file, bool should_swap) {
+    size_t header_size = sizeof(fat_header);
+    size_t arch_size = sizeof(fat_arch);
+    fat_header header = load_bytes<fat_header>(obj_file, 0);
+    if(should_swap) {
+        swap_fat_header(&header, NX_UnknownByteOrder);
+    }
+    off_t arch_offset = (off_t)header_size;
+    uintptr_t text_vmaddr = 0;
+    for(uint32_t i = 0; i < header.nfat_arch; i++) {
+        fat_arch arch = load_bytes<fat_arch>(obj_file, arch_offset);
+        if(should_swap) {
+            swap_fat_arch(&arch, 1, NX_UnknownByteOrder);
+        }
+        off_t mach_header_offset = (off_t)arch.offset;
+        arch_offset += arch_size;
+        uint32_t magic = load_bytes<uint32_t>(obj_file, mach_header_offset);
+        text_vmaddr = macho_get_text_vmaddr_mach(
+            obj_file,
+            mach_header_offset,
+            is_magic_64(magic),
+            should_swap_bytes(magic)
+        );
+        if(text_vmaddr != 0) {
+            return text_vmaddr;
+        }
+    }
+    // If this is reached... something went wrong. The cpu we're on wasn't found.
+    return text_vmaddr;
+}
+
+static uintptr_t macho_get_text_vmaddr(const char* path) {
+    FILE* obj_file = fopen(path, "rb");
+    if(obj_file == nullptr) {
+        throw file_error();
+    }
+    uint32_t magic = load_bytes<uint32_t>(obj_file, 0);
+    bool is_64 = is_magic_64(magic);
+    bool should_swap = should_swap_bytes(magic);
+    uintptr_t addr;
+    if(magic == FAT_MAGIC || magic == FAT_CIGAM) {
+        addr = macho_get_text_vmaddr_fat(obj_file, should_swap);
+    } else {
+        addr = macho_get_text_vmaddr_mach(obj_file, 0, is_64, should_swap);
+    }
+    fclose(obj_file);
+    return addr;
+}
+
+#endif
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/object.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/object.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/object.hpp	(revision 2573)
@@ -0,0 +1,154 @@
+#ifndef OBJECT_HPP
+#define OBJECT_HPP
+
+#include "common.hpp"
+
+#include <string>
+#include <vector>
+#include <mutex>
+#include <unordered_map>
+
+#if IS_LINUX || IS_APPLE
+ #include <unistd.h>
+ #include <dlfcn.h>
+ #if IS_APPLE
+  #include "mach-o.hpp"
+ #else
+  #include "elf.hpp"
+ #endif
+#elif IS_WINDOWS
+ #include <windows.h>
+ #include "pe.hpp"
+#endif
+
+struct dlframe {
+    std::string obj_path;
+    std::string symbol;
+    uintptr_t raw_address = 0;
+    uintptr_t obj_address = 0;
+};
+
+#if IS_LINUX || IS_APPLE
+#if !IS_APPLE
+static uintptr_t get_module_image_base(const std::string& obj_path) {
+    static std::mutex mutex;
+    std::lock_guard<std::mutex> lock(mutex);
+    static std::unordered_map<std::string, uintptr_t> cache;
+    auto it = cache.find(obj_path);
+    if(it == cache.end()) {
+        // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
+        // have two threads try to do the same computation
+        auto base = elf_get_module_image_base(obj_path);
+        cache.insert(it, {obj_path, base});
+        return base;
+    } else {
+        return it->second;
+    }
+}
+#else
+static 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.....
+    // 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.
+    static std::mutex mutex;
+    std::lock_guard<std::mutex> lock(mutex);
+    static std::unordered_map<std::string, uintptr_t> cache;
+    auto it = cache.find(obj_path);
+    if(it == cache.end()) {
+        // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
+        // have two threads try to do the same computation
+        auto base = macho_get_text_vmaddr(obj_path.c_str());
+        cache.insert(it, {obj_path, base});
+        return base;
+    } else {
+        return it->second;
+    }
+}
+#endif
+// 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) {
+    // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
+    std::vector<dlframe> frames;
+    frames.reserve(addrs.size());
+    for(const void* addr : addrs) {
+        Dl_info info;
+        dlframe frame;
+        frame.raw_address = reinterpret_cast<uintptr_t>(addr);
+        if(dladdr(addr, &info)) { // thread safe
+            // dli_sname and dli_saddr are only present with -rdynamic, sname will be included
+            // but we don't really need dli_saddr
+            frame.obj_path = info.dli_fname;
+            frame.obj_address = reinterpret_cast<uintptr_t>(addr)
+                                - reinterpret_cast<uintptr_t>(info.dli_fbase)
+                                + get_module_image_base(info.dli_fname);
+            frame.symbol = info.dli_sname ?: "";
+        }
+        frames.push_back(frame);
+    }
+    return frames;
+}
+#else
+static std::string get_module_name(HMODULE handle) {
+    static std::mutex mutex;
+    std::lock_guard<std::mutex> lock(mutex);
+    static std::unordered_map<HMODULE, std::string> cache;
+    auto it = cache.find(handle);
+    if(it == cache.end()) {
+        char path[MAX_PATH];
+        if(GetModuleFileNameA(handle, path, sizeof(path))) {
+            ///fprintf(stderr, "path: %s base: %p\n", path, handle);
+            cache.insert(it, {handle, path});
+            return path;
+        } else {
+            fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
+            cache.insert(it, {handle, ""});
+            return "";
+        }
+    } else {
+        return it->second;
+    }
+}
+static uintptr_t get_module_image_base(const std::string& obj_path) {
+    static std::mutex mutex;
+    std::lock_guard<std::mutex> lock(mutex);
+    static std::unordered_map<std::string, uintptr_t> cache;
+    auto it = cache.find(obj_path);
+    if(it == cache.end()) {
+        // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
+        // have two threads try to do the same computation
+        auto base = pe_get_module_image_base(obj_path);
+        cache.insert(it, {obj_path, base});
+        return base;
+    } else {
+        return it->second;
+    }
+}
+// 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) {
+    // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
+    std::vector<dlframe> frames;
+    frames.reserve(addrs.size());
+    for(const void* addr : addrs) {
+        dlframe frame;
+        frame.raw_address = reinterpret_cast<uintptr_t>(addr);
+        HMODULE handle;
+        // Multithread safe as long as another thread doesn't come along and free the module
+        if(GetModuleHandleExA(
+            GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+            static_cast<const char*>(addr),
+            &handle
+        )) {
+            frame.obj_path = get_module_name(handle);
+            frame.obj_address = reinterpret_cast<uintptr_t>(addr)
+                                - reinterpret_cast<uintptr_t>(handle)
+                                + get_module_image_base(frame.obj_path);
+        } else {
+            fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
+        }
+        frames.push_back(frame);
+    }
+    return frames;
+}
+#endif
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/pe.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/pe.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/pe.hpp	(revision 2573)
@@ -0,0 +1,64 @@
+#ifndef PE_HPP
+#define PE_HPP
+
+#include "common.hpp"
+
+#if IS_WINDOWS
+#include <array>
+#include <cstddef>
+#include <cstdio>
+#include <cstring>
+#include <string>
+
+#include <windows.h>
+
+template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
+T pe_byteswap_if_needed(T value) {
+    // PE header values are little endian
+    if(!is_little_endian()) {
+        return byteswap(value);
+    } else {
+        return value;
+    }
+}
+
+static uintptr_t pe_get_module_image_base(const std::string& obj_path) {
+    FILE* file;
+    errno_t ret = fopen_s(&file, obj_path.c_str(), "rb");
+    if(ret != 0 || file == nullptr) {
+        throw file_error();
+        return 0;
+    }
+    auto magic = load_bytes<std::array<char, 2>>(file, 0);
+    internal_verify(memcmp(magic.data(), "MZ", 2) == 0);
+    DWORD e_lfanew = pe_byteswap_if_needed(load_bytes<DWORD>(file, 0x3c)); // dos header + 0x3c
+    long nt_header_offset = e_lfanew;
+    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);
+    WORD size_of_optional_header = pe_byteswap_if_needed(
+        load_bytes<WORD>(file, nt_header_offset + 4 + 0x10) // file header + 0x10
+    );
+    internal_verify(size_of_optional_header != 0);
+    WORD optional_header_magic = pe_byteswap_if_needed(
+        load_bytes<WORD>(file, nt_header_offset + 0x18) // optional header + 0x0
+    );
+    internal_verify(optional_header_magic == IMAGE_NT_OPTIONAL_HDR_MAGIC);
+    uintptr_t image_base;
+    if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+        // 32 bit
+        image_base = pe_byteswap_if_needed(
+            load_bytes<DWORD>(file, nt_header_offset + 0x18 + 0x1c) // optional header + 0x1c
+        );
+    } else {
+        // 64 bit
+        // I get an "error: 'QWORD' was not declared in this scope" for some reason when using QWORD
+        image_base = pe_byteswap_if_needed(
+            load_bytes<unsigned __int64>(file, nt_header_offset + 0x18 + 0x18) // optional header + 0x18
+        );
+    }
+    fclose(file);
+    return image_base;
+}
+#endif
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/program_name.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/program_name.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/program_name.hpp	(revision 2573)
@@ -0,0 +1,92 @@
+#ifndef PROGRAM_NAME_HPP
+#define PROGRAM_NAME_HPP
+
+#include <mutex>
+#include <string>
+
+#if defined(_WIN32)
+#include <windows.h>
+
+namespace cpptrace {
+    namespace detail {
+        inline std::string program_name() {
+            static std::mutex mutex;
+            const std::lock_guard<std::mutex> lock(mutex);
+            static std::string name;
+            static bool did_init = false;
+            static bool valid = false;
+            if(!did_init) {
+                did_init = true;
+                char buffer[MAX_PATH + 1];
+                int res = GetModuleFileNameA(nullptr, buffer, MAX_PATH);
+                if(res) {
+                    name = buffer;
+                    valid = true;
+                }
+            }
+            return valid && !name.empty() ? name.c_str() : nullptr;
+        }
+    }
+}
+
+#elif defined(__APPLE__)
+
+#include <cstdint>
+#include <mach-o/dyld.h>
+#include <sys/syslimits.h>
+
+namespace cpptrace {
+    namespace detail {
+        inline const char* program_name() {
+            static std::mutex mutex;
+            const std::lock_guard<std::mutex> lock(mutex);
+            static std::string name;
+            static bool did_init = false;
+            static bool valid = false;
+            if(!did_init) {
+                did_init = true;
+                std::uint32_t bufferSize = PATH_MAX + 1;
+                char buffer[bufferSize];
+                if(_NSGetExecutablePath(buffer, &bufferSize) == 0) {
+                    name.assign(buffer, bufferSize);
+                    valid = true;
+                }
+            }
+            return valid && !name.empty() ? name.c_str() : nullptr;
+        }
+    }
+}
+
+#elif defined(__linux__)
+
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace cpptrace {
+    namespace detail {
+        inline const char* program_name() {
+            static std::mutex mutex;
+            const std::lock_guard<std::mutex> lock(mutex);
+            static std::string name;
+            static bool did_init = false;
+            static bool valid = false;
+            if(!did_init) {
+                did_init = true;
+                char buffer[PATH_MAX + 1];
+                const ssize_t size = readlink("/proc/self/exe", buffer, PATH_MAX);
+                if(size == -1) {
+                    return nullptr;
+                }
+                buffer[size] = 0;
+                name = buffer;
+                valid = true;
+            }
+            return valid && !name.empty() ? name.c_str() : nullptr;
+        }
+    }
+}
+
+#endif
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/symbols.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/symbols.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/symbols.hpp	(revision 2573)
@@ -0,0 +1,45 @@
+#ifndef SYMBOLS_HPP
+#define SYMBOLS_HPP
+
+#include "cpptrace.hpp"
+
+#include <memory>
+#include <vector>
+
+namespace cpptrace {
+    namespace detail {
+        #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
+        namespace libbacktrace {
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
+        }
+        #endif
+        #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
+        namespace libdwarf {
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
+        }
+        #endif
+        #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
+        namespace libdl {
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
+        }
+        #endif
+        #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
+        namespace addr2line {
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
+        }
+        #endif
+        #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
+        namespace dbghelp {
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
+        }
+        #endif
+        #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
+        namespace nothing {
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
+        }
+        #endif
+        std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames);
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/symbols_core.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/symbols_core.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/symbols_core.cpp	(revision 2573)
@@ -0,0 +1,53 @@
+#include "symbols.hpp"
+
+#include <vector>
+
+namespace cpptrace {
+    namespace detail {
+        void apply_trace(
+            std::vector<stacktrace_frame>& result,
+            std::vector<stacktrace_frame>&& trace
+        ) {
+            for(std::size_t i = 0; i < result.size(); i++) {
+                if(result[i].address == 0) {
+                    result[i].address = trace[i].address;
+                }
+                if(result[i].line == 0) {
+                    result[i].line = trace[i].line;
+                }
+                if(result[i].col == 0) {
+                    result[i].col = trace[i].col;
+                }
+                if(result[i].filename.empty()) {
+                    result[i].filename = std::move(trace[i].filename);
+                }
+                if(result[i].symbol.empty()) {
+                    result[i].symbol = std::move(trace[i].symbol);
+                }
+            }
+        }
+
+        std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
+            std::vector<stacktrace_frame> trace(frames.size());
+            #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
+            apply_trace(trace, libdl::resolve_frames(frames));
+            #endif
+            #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
+            apply_trace(trace, libdwarf::resolve_frames(frames));
+            #endif
+            #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
+            apply_trace(trace, dbghelp::resolve_frames(frames));
+            #endif
+            #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
+            apply_trace(trace, addr2line::resolve_frames(frames));
+            #endif
+            #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
+            apply_trace(trace, libbacktrace::resolve_frames(frames));
+            #endif
+            #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
+            apply_trace(trace, nothing::resolve_frames(frames));
+            #endif
+            return trace;
+        }
+    }
+}
Index: XIOS3/trunk/extern/cpptrace/src/symbols_with_addr2line.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/symbols_with_addr2line.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/symbols_with_addr2line.cpp	(revision 2573)
@@ -0,0 +1,351 @@
+#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
+
+#include "cpptrace.hpp"
+#include "symbols.hpp"
+#include "common.hpp"
+
+#include <cstdint>
+#include <cstdio>
+#include <functional>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#if IS_LINUX || IS_APPLE
+ #include <unistd.h>
+ // NOLINTNEXTLINE(misc-include-cleaner)
+ #include <sys/types.h>
+ #include <sys/wait.h>
+#endif
+
+#include "object.hpp"
+
+namespace cpptrace {
+    namespace detail {
+        namespace addr2line {
+            #if IS_LINUX || IS_APPLE
+            bool has_addr2line() {
+                static std::mutex mutex;
+                static bool has_addr2line = false;
+                static bool checked = false;
+                std::lock_guard<std::mutex> lock(mutex);
+                if(!checked) {
+                    checked = true;
+                    // Detects if addr2line exists by trying to invoke addr2line --help
+                    constexpr int magic = 42;
+                    // NOLINTNEXTLINE(misc-include-cleaner)
+                    const pid_t pid = fork();
+                    if(pid == -1) { return false; }
+                    if(pid == 0) { // child
+                        close(STDOUT_FILENO);
+                        close(STDERR_FILENO); // atos --help writes to stderr
+                        #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
+                        #if !IS_APPLE
+                        execlp("addr2line", "addr2line", "--help", nullptr);
+                        #else
+                        execlp("atos", "atos", "--help", nullptr);
+                        #endif
+                        #else
+                        #ifndef CPPTRACE_ADDR2LINE_PATH
+                        #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not"
+                        #endif
+                        execl(CPPTRACE_ADDR2LINE_PATH, CPPTRACE_ADDR2LINE_PATH, "--help", nullptr);
+                        #endif
+                        _exit(magic);
+                    }
+                    int status;
+                    waitpid(pid, &status, 0);
+                    // NOLINTNEXTLINE(misc-include-cleaner)
+                    has_addr2line = WEXITSTATUS(status) == 0;
+                }
+                return has_addr2line;
+            }
+
+            struct pipe_t {
+                union {
+                    struct {
+                        int read_end;
+                        int write_end;
+                    };
+                    int data[2];
+                };
+            };
+            static_assert(sizeof(pipe_t) == 2 * sizeof(int), "Unexpected struct packing");
+
+            std::string resolve_addresses(const std::string& addresses, const std::string& executable) {
+                pipe_t output_pipe;
+                pipe_t input_pipe;
+                internal_verify(pipe(output_pipe.data) == 0);
+                internal_verify(pipe(input_pipe.data) == 0);
+                // NOLINTNEXTLINE(misc-include-cleaner)
+                const pid_t pid = fork();
+                if(pid == -1) { return ""; } // error? TODO: Diagnostic
+                if(pid == 0) { // child
+                    dup2(output_pipe.write_end, STDOUT_FILENO);
+                    dup2(input_pipe.read_end, STDIN_FILENO);
+                    close(output_pipe.read_end);
+                    close(output_pipe.write_end);
+                    close(input_pipe.read_end);
+                    close(input_pipe.write_end);
+                    close(STDERR_FILENO); // TODO: Might be worth conditionally enabling or piping
+                    #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
+                    #if !IS_APPLE
+                    execlp("addr2line", "addr2line", "-e", executable.c_str(), "-f", "-C", "-p", nullptr);
+                    #else
+                    execlp("atos", "atos", "-o", executable.c_str(), "-fullPath", nullptr);
+                    #endif
+                    #else
+                    #ifndef CPPTRACE_ADDR2LINE_PATH
+                    #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not"
+                    #endif
+                    #if !IS_APPLE
+                    execl(
+                        CPPTRACE_ADDR2LINE_PATH,
+                        CPPTRACE_ADDR2LINE_PATH,
+                        "-e",
+                        executable.c_str(),
+                        "-f",
+                        "-C",
+                        "-p",
+                        nullptr
+                    );
+                    #else
+                    execl(
+                        CPPTRACE_ADDR2LINE_PATH,
+                        CPPTRACE_ADDR2LINE_PATH,
+                        "-o", executable.c_str(),
+                        "-fullPath",
+                        nullptr
+                    );
+                    #endif
+                    #endif
+                    _exit(1); // TODO: Diagnostic?
+                }
+                internal_verify(write(input_pipe.write_end, addresses.data(), addresses.size()) != -1);
+                close(input_pipe.read_end);
+                close(input_pipe.write_end);
+                close(output_pipe.write_end);
+                std::string output;
+                constexpr int buffer_size = 4096;
+                char buffer[buffer_size];
+                size_t count = 0;
+                while((count = read(output_pipe.read_end, buffer, buffer_size)) > 0) {
+                    output.insert(output.end(), buffer, buffer + count);
+                }
+                // TODO: check status from addr2line?
+                waitpid(pid, nullptr, 0);
+                return output;
+            }
+            #elif IS_WINDOWS
+            bool has_addr2line() {
+                static std::mutex mutex;
+                static bool has_addr2line = false;
+                static bool checked = false;
+                std::lock_guard<std::mutex> lock(mutex);
+                if(!checked) {
+                    // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later.
+                    checked = true;
+                    #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
+                    FILE* p = popen("addr2line --version", "r");
+                    #else
+                    #ifndef CPPTRACE_ADDR2LINE_PATH
+                    #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not"
+                    #endif
+                    FILE* p = popen(CPPTRACE_ADDR2LINE_PATH " --version", "r");
+                    #endif
+                    if(p) {
+                        has_addr2line = pclose(p) == 0;
+                    }
+                }
+                return has_addr2line;
+            }
+
+            std::string resolve_addresses(const std::string& addresses, const std::string& executable) {
+                // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later.
+                ///fprintf(stderr, ("addr2line -e " + executable + " -fCp " + addresses + "\n").c_str());
+                #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
+                FILE* p = popen(("addr2line -e \"" + executable + "\" -fCp " + addresses).c_str(), "r");
+                #else
+                #ifndef CPPTRACE_ADDR2LINE_PATH
+                #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not"
+                #endif
+                FILE* p = popen(
+                    (CPPTRACE_ADDR2LINE_PATH " -e \"" + executable + "\" -fCp " + addresses).c_str(),
+                    "r"
+                );
+                #endif
+                std::string output;
+                constexpr int buffer_size = 4096;
+                char buffer[buffer_size];
+                size_t count = 0;
+                while((count = fread(buffer, 1, buffer_size, p)) > 0) {
+                    output.insert(output.end(), buffer, buffer + count);
+                }
+                pclose(p);
+                ///fprintf(stderr, "%s\n", output.c_str());
+                return output;
+            }
+            #endif
+
+            using target_vec = std::vector<std::pair<std::string, std::reference_wrapper<stacktrace_frame>>>;
+
+            // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
+            std::unordered_map<std::string, target_vec> get_addr2line_targets(
+                const std::vector<dlframe>& dlframes,
+                std::vector<stacktrace_frame>& trace
+            ) {
+                std::unordered_map<std::string, target_vec> entries;
+                for(std::size_t i = 0; i < dlframes.size(); i++) {
+                    const auto& entry = dlframes[i];
+                    // If libdl fails to find the shared object for a frame, the path will be empty. I've observed this
+                    // on macos when looking up the shared object containing `start`.
+                    if(!entry.obj_path.empty()) {
+                        ///fprintf(
+                        ///    stderr,
+                        ///    "%s %s\n",
+                        ///    to_hex(entry.raw_address).c_str(),
+                        ///    to_hex(entry.raw_address - entry.obj_base + base).c_str()
+                        ///);
+                        try {
+                            entries[entry.obj_path].emplace_back(
+                                to_hex(entry.obj_address),
+                                trace[i]
+                            );
+                        } catch(file_error&) {
+                            //
+                        } catch(...) {
+                            throw;
+                        }
+                        // Set what is known for now, and resolutions from addr2line should overwrite
+                        trace[i].filename = entry.obj_path;
+                        trace[i].symbol = entry.symbol;
+                    }
+                }
+                return entries;
+            }
+
+            // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
+            void update_trace(const std::string& line, size_t entry_index, const target_vec& entries_vec) {
+                #if !IS_APPLE
+                // Result will be of the form "<symbol> at path:line"
+                // The path may be ?? if addr2line cannot resolve, line may be ?
+                // Edge cases:
+                // ?? ??:0
+                // symbol :?
+                const std::size_t at_location = line.find(" at ");
+                std::size_t symbol_end;
+                std::size_t filename_start;
+                if(at_location != std::string::npos) {
+                    symbol_end = at_location;
+                    filename_start = at_location + 4;
+                } else {
+                    internal_verify(line.find("?? ") == 0, "Unexpected edge case while processing addr2line output");
+                    symbol_end = 2;
+                    filename_start = 3;
+                }
+                auto symbol = line.substr(0, symbol_end);
+                auto colon = line.rfind(':');
+                internal_verify(colon != std::string::npos);
+                internal_verify(colon >= filename_start); // :? to deal with "symbol :?" edge case
+                auto filename = line.substr(filename_start, colon - filename_start);
+                auto line_number = line.substr(colon + 1);
+                if(line_number != "?") {
+                    entries_vec[entry_index].second.get().line = std::stoi(line_number);
+                }
+                if(!filename.empty() && filename != "??") {
+                    entries_vec[entry_index].second.get().filename = filename;
+                }
+                if(!symbol.empty()) {
+                    entries_vec[entry_index].second.get().symbol = symbol;
+                }
+                #else
+                // Result will be of the form "<symbol> (in <object name>) (file:line)"
+                // The symbol may just be the given address if atos can't resolve it
+                // Examples:
+                // trace() (in demo) (demo.cpp:8)
+                // 0x100003b70 (in demo)
+                // 0xffffffffffffffff
+                // foo (in bar) + 14
+                // I'm making some assumptions here. Support may need to be improved later. This is tricky output to
+                // parse.
+                const std::size_t in_location = line.find(" (in ");
+                if(in_location == std::string::npos) {
+                    // presumably the 0xffffffffffffffff case
+                    return;
+                }
+                const std::size_t symbol_end = in_location;
+                entries_vec[entry_index].second.get().symbol = line.substr(0, symbol_end);
+                const std::size_t obj_end = line.find(")", in_location);
+                internal_verify(
+                    obj_end != std::string::npos,
+                    "Unexpected edge case while processing addr2line/atos output"
+                );
+                const std::size_t filename_start = line.find(") (", obj_end);
+                if(filename_start == std::string::npos) {
+                    // presumably something like 0x100003b70 (in demo) or foo (in bar) + 14
+                    return;
+                }
+                const std::size_t filename_end = line.find(":", filename_start);
+                internal_verify(
+                    filename_end != std::string::npos,
+                    "Unexpected edge case while processing addr2line/atos output"
+                );
+                entries_vec[entry_index].second.get().filename = line.substr(
+                    filename_start + 3,
+                    filename_end - filename_start - 3
+                );
+                const std::size_t line_start = filename_end + 1;
+                const std::size_t line_end = line.find(")", filename_end);
+                internal_verify(
+                    line_end == line.size() - 1,
+                    "Unexpected edge case while processing addr2line/atos output"
+                );
+                entries_vec[entry_index].second.get().line = std::stoi(line.substr(line_start, line_end - line_start));
+                #endif
+            }
+
+            // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
+                // TODO: Refactor better
+                std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" });
+                for(size_t i = 0; i < frames.size(); i++) {
+                    trace[i].address = reinterpret_cast<uintptr_t>(frames[i]);
+                }
+                if(has_addr2line()) {
+                    const std::vector<dlframe> dlframes = get_frames_object_info(frames);
+                    const auto entries = get_addr2line_targets(dlframes, trace);
+                    for(const auto& entry : entries) {
+                        const auto& object_name = entry.first;
+                        const auto& entries_vec = entry.second;
+                        // You may ask why it'd ever happen that there could be an empty entries_vec array, if there're
+                        // no addresses why would get_addr2line_targets do anything? The reason is because if things in
+                        // get_addr2line_targets fail it will silently skip. This is partly an optimization but also an
+                        // assertion below will fail if addr2line is given an empty input.
+                        if(entries_vec.empty()) {
+                            continue;
+                        }
+                        std::string address_input;
+                        for(const auto& pair : entries_vec) {
+                            address_input += pair.first;
+                            #if !IS_WINDOWS
+                                address_input += '\n';
+                            #else
+                                address_input += ' ';
+                            #endif
+                        }
+                        auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
+                        internal_verify(output.size() == entries_vec.size());
+                        for(size_t i = 0; i < output.size(); i++) {
+                            update_trace(output[i], i, entries_vec);
+                        }
+                    }
+                }
+                return trace;
+            }
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/symbols_with_dbghelp.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/symbols_with_dbghelp.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/symbols_with_dbghelp.cpp	(revision 2573)
@@ -0,0 +1,429 @@
+#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
+
+#include "cpptrace.hpp"
+#include "symbols.hpp"
+#include "program_name.hpp"
+
+#include <memory>
+#include <regex>
+#include <stdexcept>
+#include <system_error>
+#include <vector>
+
+#include <windows.h>
+#include <dbghelp.h>
+
+namespace cpptrace {
+    namespace detail {
+        namespace dbghelp {
+
+            // SymFromAddr only returns the function's name. In order to get information about parameters,
+            // important for C++ stack traces where functions may be overloaded, we have to manually use
+            // Windows DIA to walk debug info structures. Resources:
+            // https://web.archive.org/web/20201027025750/http://www.debuginfo.com/articles/dbghelptypeinfo.html
+            // https://web.archive.org/web/20201203160805/http://www.debuginfo.com/articles/dbghelptypeinfofigures.html
+            // https://github.com/DynamoRIO/dynamorio/blob/master/ext/drsyms/drsyms_windows.c#L1370-L1439
+            // TODO: Currently unable to detect rvalue references
+            // TODO: Currently unable to detect const
+            enum class SymTagEnum {
+                SymTagNull, SymTagExe, SymTagCompiland, SymTagCompilandDetails, SymTagCompilandEnv,
+                SymTagFunction, SymTagBlock, SymTagData, SymTagAnnotation, SymTagLabel, SymTagPublicSymbol,
+                SymTagUDT, SymTagEnum, SymTagFunctionType, SymTagPointerType, SymTagArrayType,
+                SymTagBaseType, SymTagTypedef, SymTagBaseClass, SymTagFriend, SymTagFunctionArgType,
+                SymTagFuncDebugStart, SymTagFuncDebugEnd, SymTagUsingNamespace, SymTagVTableShape,
+                SymTagVTable, SymTagCustom, SymTagThunk, SymTagCustomType, SymTagManagedType,
+                SymTagDimension, SymTagCallSite, SymTagInlineSite, SymTagBaseInterface, SymTagVectorType,
+                SymTagMatrixType, SymTagHLSLType, SymTagCaller, SymTagCallee, SymTagExport,
+                SymTagHeapAllocationSite, SymTagCoffGroup, SymTagMax
+            };
+
+            enum class IMAGEHLP_SYMBOL_TYPE_INFO {
+                TI_GET_SYMTAG, TI_GET_SYMNAME, TI_GET_LENGTH, TI_GET_TYPE, TI_GET_TYPEID, TI_GET_BASETYPE,
+                TI_GET_ARRAYINDEXTYPEID, TI_FINDCHILDREN, TI_GET_DATAKIND, TI_GET_ADDRESSOFFSET,
+                TI_GET_OFFSET, TI_GET_VALUE, TI_GET_COUNT, TI_GET_CHILDRENCOUNT, TI_GET_BITPOSITION,
+                TI_GET_VIRTUALBASECLASS, TI_GET_VIRTUALTABLESHAPEID, TI_GET_VIRTUALBASEPOINTEROFFSET,
+                TI_GET_CLASSPARENTID, TI_GET_NESTED, TI_GET_SYMINDEX, TI_GET_LEXICALPARENT, TI_GET_ADDRESS,
+                TI_GET_THISADJUST, TI_GET_UDTKIND, TI_IS_EQUIV_TO, TI_GET_CALLING_CONVENTION,
+                TI_IS_CLOSE_EQUIV_TO, TI_GTIEX_REQS_VALID, TI_GET_VIRTUALBASEOFFSET,
+                TI_GET_VIRTUALBASEDISPINDEX, TI_GET_IS_REFERENCE, TI_GET_INDIRECTVIRTUALBASECLASS,
+                TI_GET_VIRTUALBASETABLETYPE, TI_GET_OBJECTPOINTERTYPE, IMAGEHLP_SYMBOL_TYPE_INFO_MAX
+            };
+
+            enum class BasicType {
+                btNoType = 0, btVoid = 1, btChar = 2, btWChar = 3, btInt = 6, btUInt = 7, btFloat = 8,
+                btBCD = 9, btBool = 10, btLong = 13, btULong = 14, btCurrency = 25, btDate = 26,
+                btVariant = 27, btComplex = 28, btBit = 29, btBSTR = 30, btHresult = 31
+            };
+
+            // SymGetTypeInfo utility
+            template<typename T, IMAGEHLP_SYMBOL_TYPE_INFO SymType, bool FAILABLE = false>
+            T get_info(ULONG type_index, HANDLE proc, ULONG64 modbase) {
+                T info;
+                if(
+                    !SymGetTypeInfo(
+                        proc,
+                        modbase,
+                        type_index,
+                        static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType),
+                        &info
+                    )
+                ) {
+                    if(FAILABLE) {
+                        return (T)-1;
+                    } else {
+                        throw std::logic_error(
+                            std::string("SymGetTypeInfo failed: ")
+                            + std::system_error(GetLastError(), std::system_category()).what()
+                        );
+                    }
+                }
+                return info;
+            }
+
+            template<IMAGEHLP_SYMBOL_TYPE_INFO SymType, bool FAILABLE = false>
+            std::string get_info_wchar(ULONG type_index, HANDLE proc, ULONG64 modbase) {
+                WCHAR* info;
+                if(
+                    !SymGetTypeInfo(proc, modbase, type_index, static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(SymType), &info)
+                ) {
+                    throw std::logic_error(
+                        std::string("SymGetTypeInfo failed: ")
+                        + std::system_error(GetLastError(), std::system_category()).what()
+                    );
+                }
+                // special case to properly free a buffer and convert string to narrow chars, only used for
+                // TI_GET_SYMNAME
+                static_assert(
+                    SymType == IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_SYMNAME,
+                    "get_info_wchar called with unexpected IMAGEHLP_SYMBOL_TYPE_INFO"
+                );
+                std::wstring wstr(info);
+                std::string str;
+                str.reserve(wstr.size());
+                for(const auto c : wstr) {
+                    str.push_back(static_cast<char>(c));
+                }
+                LocalFree(info);
+                return str;
+            }
+
+            // Translate basic types to string
+            static std::string get_basic_type(ULONG type_index, HANDLE proc, ULONG64 modbase) {
+                auto basic_type = get_info<BasicType, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_BASETYPE>(
+                    type_index,
+                    proc,
+                    modbase
+                );
+                //auto length = get_info<ULONG64, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_LENGTH>(type_index, proc, modbase);
+                switch(basic_type) {
+                    case BasicType::btNoType:
+                        return "<no basic type>";
+                    case BasicType::btVoid:
+                        return "void";
+                    case BasicType::btChar:
+                        return "char";
+                    case BasicType::btWChar:
+                        return "wchar_t";
+                    case BasicType::btInt:
+                        return "int";
+                    case BasicType::btUInt:
+                        return "unsigned int";
+                    case BasicType::btFloat:
+                        return "float";
+                    case BasicType::btBool:
+                        return "bool";
+                    case BasicType::btLong:
+                        return "long";
+                    case BasicType::btULong:
+                        return "unsigned long";
+                    default:
+                        return "<unknown basic type>";
+                }
+            }
+
+            static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase);
+
+            struct class_name_result {
+                bool has_class_name;
+                std::string name;
+            };
+            // Helper for member pointers
+            static class_name_result lookup_class_name(ULONG type_index, HANDLE proc, ULONG64 modbase) {
+                DWORD class_parent_id = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_CLASSPARENTID, true>(
+                    type_index,
+                    proc,
+                    modbase
+                );
+                if(class_parent_id == (DWORD)-1) {
+                    return {false, ""};
+                } else {
+                    return {true, resolve_type(class_parent_id, proc, modbase)};
+                }
+            }
+
+            struct type_result {
+                std::string base;
+                std::string extent;
+            };
+            // Resolve more complex types
+            // returns [base, extent]
+            static type_result lookup_type(ULONG type_index, HANDLE proc, ULONG64 modbase) {
+                auto tag = get_info<SymTagEnum, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_SYMTAG>(type_index, proc, modbase);
+                switch(tag) {
+                    case SymTagEnum::SymTagBaseType:
+                        return {get_basic_type(type_index, proc, modbase), ""};
+                    case SymTagEnum::SymTagPointerType: {
+                        DWORD underlying_type_id = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_TYPEID>(
+                            type_index,
+                            proc,
+                            modbase
+                        );
+                        bool is_ref = get_info<BOOL, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_IS_REFERENCE>(
+                            type_index,
+                            proc,
+                            modbase
+                        );
+                        std::string pp = is_ref ? "&" : "*"; // pointer punctuator
+                        auto class_name_res = lookup_class_name(type_index, proc, modbase);
+                        if(class_name_res.has_class_name) {
+                            pp = class_name_res.name + "::" + pp;
+                        }
+                        const auto type = lookup_type(underlying_type_id, proc, modbase);
+                        if(type.extent.empty()) {
+                            return {type.base + (pp.size() > 1 ? " " : "") + pp, ""};
+                        } else {
+                            return {type.base + "(" + pp, ")" + type.extent};
+                        }
+                    }
+                    case SymTagEnum::SymTagArrayType: {
+                        DWORD underlying_type_id = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_TYPEID>(
+                            type_index,
+                            proc,
+                            modbase
+                        );
+                        DWORD length = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_COUNT>(
+                            type_index,
+                            proc,
+                            modbase
+                        );
+                        const auto type = lookup_type(underlying_type_id, proc, modbase);
+                        return {type.base, "[" + std::to_string(length) + "]" + type.extent};
+                    }
+                    case SymTagEnum::SymTagFunctionType: {
+                        DWORD return_type_id = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_TYPEID>(
+                            type_index,
+                            proc,
+                            modbase
+                        );
+                        DWORD n_children = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_COUNT, true>(
+                            type_index,
+                            proc,
+                            modbase
+                        );
+                        DWORD class_parent_id = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_CLASSPARENTID, true>(
+                            type_index,
+                            proc,
+                            modbase
+                        );
+                        int n_ignore = class_parent_id != (DWORD)-1; // ignore this param
+                        // this must be ignored before TI_FINDCHILDREN_PARAMS::Count is set, else error
+                        n_children -= n_ignore;
+                        // return type
+                        const auto return_type = lookup_type(return_type_id, proc, modbase);
+                        if(n_children == 0) {
+                            return {return_type.base, "()" + return_type.extent};
+                        } else {
+                            // alignment should be fine
+                            size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) +
+                                            (n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]);
+                            TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz];
+                            children->Start = 0;
+                            children->Count = n_children;
+                            if(
+                                !SymGetTypeInfo(
+                                    proc, modbase, type_index,
+                                    static_cast<::IMAGEHLP_SYMBOL_TYPE_INFO>(
+                                        IMAGEHLP_SYMBOL_TYPE_INFO::TI_FINDCHILDREN
+                                    ),
+                                    children
+                                )
+                            ) {
+                                throw std::logic_error(
+                                    std::string("SymGetTypeInfo failed: ")
+                                    + std::system_error(GetLastError(), std::system_category()).what()
+                                );
+                            }
+                            // get children type
+                            std::string extent = "(";
+                            if(children->Start != 0) {
+                                throw std::logic_error("Error: children->Start == 0");
+                            }
+                            for(std::size_t i = 0; i < n_children; i++) {
+                                extent += (i == 0 ? "" : ", ") + resolve_type(children->ChildId[i], proc, modbase);
+                            }
+                            extent += ")";
+                            delete[] (char*) children;
+                            return {return_type.base, extent + return_type.extent};
+                        }
+                    }
+                    case SymTagEnum::SymTagFunctionArgType: {
+                        DWORD underlying_type_id =
+                            get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_TYPEID>(type_index, proc, modbase);
+                        return {resolve_type(underlying_type_id, proc, modbase), ""};
+                    }
+                    case SymTagEnum::SymTagTypedef:
+                    case SymTagEnum::SymTagEnum:
+                    case SymTagEnum::SymTagUDT:
+                    case SymTagEnum::SymTagBaseClass:
+                        return {
+                            get_info_wchar<IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_SYMNAME>(type_index, proc, modbase), ""
+                        };
+                    default:
+                        return {
+                            "<unknown type " +
+                                std::to_string(static_cast<std::underlying_type<SymTagEnum>::type>(tag)) +
+                                ">",
+                            ""
+                        };
+                };
+            }
+
+            static std::string resolve_type(ULONG type_index, HANDLE proc, ULONG64 modbase) {
+                const auto type = lookup_type(type_index, proc, modbase);
+                return type.base + type.extent;
+            }
+
+            struct function_info {
+                HANDLE proc;
+                ULONG64 modbase;
+                int counter;
+                int n_children;
+                int n_ignore;
+                std::string str;
+            };
+
+            // Enumerates function parameters
+            static BOOL __stdcall enumerator_callback(
+                PSYMBOL_INFO symbol_info,
+                ULONG,
+                PVOID data
+            ) {
+                function_info* ctx = (function_info*)data;
+                if(ctx->counter++ >= ctx->n_children) {
+                    return false;
+                }
+                if(ctx->n_ignore-- > 0) {
+                    return true; // just skip
+                }
+                ctx->str += resolve_type(symbol_info->TypeIndex, ctx->proc, ctx->modbase);
+                if(ctx->counter < ctx->n_children) {
+                    ctx->str += ", ";
+                }
+                return true;
+            }
+
+            std::mutex dbghelp_lock;
+
+            // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
+            stacktrace_frame resolve_frame(HANDLE proc, void* addr) {
+                const std::lock_guard<std::mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe
+                alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
+                SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer;
+                symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+                symbol->MaxNameLen = MAX_SYM_NAME;
+                union { DWORD64 a; DWORD b; } displacement;
+                IMAGEHLP_LINE64 line;
+                bool got_line = SymGetLineFromAddr64(proc, (DWORD64)addr, &displacement.b, &line);
+                if(SymFromAddr(proc, (DWORD64)addr, &displacement.a, symbol)) {
+                    if(got_line) {
+                        IMAGEHLP_STACK_FRAME frame;
+                        frame.InstructionOffset = symbol->Address;
+                        // https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetcontext
+                        // "If you call SymSetContext to set the context to its current value, the
+                        // function fails but GetLastError returns ERROR_SUCCESS."
+                        // This is the stupidest fucking api I've ever worked with.
+                        if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) {
+                            fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n");
+                            return {
+                                reinterpret_cast<uintptr_t>(addr),
+                                static_cast<std::uint_least32_t>(line.LineNumber),
+                                0,
+                                line.FileName,
+                                symbol->Name
+                            };
+                        }
+                        DWORD n_children = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_COUNT, true>(
+                            symbol->TypeIndex,
+                            proc,
+                            symbol->ModBase
+                        );
+                        DWORD class_parent_id = get_info<DWORD, IMAGEHLP_SYMBOL_TYPE_INFO::TI_GET_CLASSPARENTID, true>(
+                            symbol->TypeIndex,
+                            proc,
+                            symbol->ModBase
+                        );
+                        function_info fi {
+                            proc,
+                            symbol->ModBase,
+                            0,
+                            int(n_children),
+                            class_parent_id != (DWORD)-1,
+                            ""
+                        };
+                        SymEnumSymbols(proc, 0, nullptr, enumerator_callback, &fi);
+                        std::string signature = symbol->Name + std::string("(") + fi.str + ")";
+                        // There's a phenomina with DIA not inserting commas after template parameters. Fix them here.
+                        static std::regex comma_re(R"(,(?=\S))");
+                        signature = std::regex_replace(signature, comma_re, ", ");
+                        return {
+                            reinterpret_cast<uintptr_t>(addr),
+                            static_cast<std::uint_least32_t>(line.LineNumber),
+                            0,
+                            line.FileName,
+                            signature
+                        };
+                    } else {
+                        return {
+                            reinterpret_cast<uintptr_t>(addr),
+                            0,
+                            0,
+                            "",
+                            symbol->Name
+                        };
+                    }
+                } else {
+                    return {
+                        reinterpret_cast<uintptr_t>(addr),
+                        0,
+                        0,
+                        "",
+                        ""
+                    };
+                }
+            }
+
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
+                std::vector<stacktrace_frame> trace;
+                trace.reserve(frames.size());
+
+                // TODO: When does this need to be called? Can it be moved to the symbolizer?
+                SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
+                HANDLE proc = GetCurrentProcess();
+                if(!SymInitialize(proc, NULL, TRUE)) {
+                    //TODO?
+                    throw std::logic_error("SymInitialize failed");
+                }
+                for(const auto frame : frames) {
+                    trace.push_back(resolve_frame(proc, frame));
+                }
+                if(!SymCleanup(proc)) {
+                    //throw std::logic_error("SymCleanup failed");
+                }
+
+                return trace;
+            }
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/symbols_with_dl.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/symbols_with_dl.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/symbols_with_dl.cpp	(revision 2573)
@@ -0,0 +1,48 @@
+#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
+
+#include "cpptrace.hpp"
+#include "symbols.hpp"
+
+#include <cstdint>
+#include <memory>
+#include <vector>
+
+#include <dlfcn.h>
+
+namespace cpptrace {
+    namespace detail {
+        namespace libdl {
+            stacktrace_frame resolve_frame(const void* addr) {
+                Dl_info info;
+                if(dladdr(addr, &info)) { // thread-safe
+                    return {
+                        reinterpret_cast<uintptr_t>(addr),
+                        0,
+                        0,
+                        info.dli_fname ? info.dli_fname : "",
+                        info.dli_sname ? info.dli_sname : ""
+                    };
+                } else {
+                    return {
+                        reinterpret_cast<uintptr_t>(addr),
+                        0,
+                        0,
+                        "",
+                        ""
+                    };
+                }
+            }
+
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
+                std::vector<stacktrace_frame> trace;
+                trace.reserve(frames.size());
+                for(const void* frame : frames) {
+                    trace.push_back(resolve_frame(frame));
+                }
+                return trace;
+            }
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/symbols_with_libbacktrace.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/symbols_with_libbacktrace.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/symbols_with_libbacktrace.cpp	(revision 2573)
@@ -0,0 +1,96 @@
+#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
+
+#include "cpptrace.hpp"
+#include "symbols.hpp"
+#include "program_name.hpp"
+
+#include <cstdint>
+#include <cstdio>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#ifdef CPPTRACE_BACKTRACE_PATH
+#include CPPTRACE_BACKTRACE_PATH
+#else
+#include <backtrace.h>
+#endif
+
+namespace cpptrace {
+    namespace detail {
+        namespace libbacktrace {
+            int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) {
+                stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data);
+                if(line == 0) {
+                    ///fprintf(stderr, "Getting bad data for some reason\n"); // TODO: Eliminate
+                }
+                frame.address = address;
+                frame.line = line;
+                frame.filename = file ? file : "";
+                frame.symbol = symbol ? symbol : "";
+                return 0;
+            }
+
+            void syminfo_callback(void* data, uintptr_t address, const char* symbol, uintptr_t, uintptr_t) {
+                stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data);
+                frame.address = address;
+                frame.line = 0;
+                frame.filename = "";
+                frame.symbol = symbol ? symbol : "";
+            }
+
+            void error_callback(void*, const char* msg, int errnum) {
+                fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum);
+            }
+
+            backtrace_state* get_backtrace_state() {
+                static std::mutex mutex;
+                const std::lock_guard<std::mutex> lock(mutex);
+                // backtrace_create_state must be called only one time per program
+                // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+                static backtrace_state* state = nullptr;
+                static bool called = false;
+                if(!called) {
+                    state = backtrace_create_state(program_name(), true, error_callback, nullptr);
+                    called = true;
+                }
+                return state;
+            }
+
+            // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
+            stacktrace_frame resolve_frame(const void* addr) {
+                stacktrace_frame frame;
+                frame.col = 0;
+                backtrace_pcinfo(
+                    get_backtrace_state(),
+                    reinterpret_cast<uintptr_t>(addr),
+                    full_callback,
+                    error_callback,
+                    &frame
+                );
+                if(frame.symbol.empty()) {
+                    // fallback, try to at least recover the symbol name with backtrace_syminfo
+                    backtrace_syminfo(
+                        get_backtrace_state(),
+                        reinterpret_cast<uintptr_t>(addr),
+                        syminfo_callback,
+                        error_callback,
+                        &frame
+                    );
+                }
+                return frame;
+            }
+
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
+                std::vector<stacktrace_frame> trace;
+                trace.reserve(frames.size());
+                for(const void* frame : frames) {
+                    trace.push_back(resolve_frame(frame));
+                }
+                return trace;
+            }
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/symbols_with_libdwarf.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/symbols_with_libdwarf.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/symbols_with_libdwarf.cpp	(revision 2573)
@@ -0,0 +1,1057 @@
+#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
+
+#include "cpptrace.hpp"
+#include "symbols.hpp"
+#include "common.hpp"
+#include "program_name.hpp"
+#include "object.hpp"
+
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <type_traits>
+#include <vector>
+
+#include <libdwarf.h>
+#include <dwarf.h>
+
+// some stuff is based on https://github.com/davea42/libdwarf-addr2line/blob/master/addr2line.c, mainly line handling
+// then much expanded for symbols and efficiency
+// dwarf5_ranges and dwarf4_ranges utility functions are taken from there directly, also pc_in_die
+
+// TODO
+// Inlined calls
+// Memoizing / lazy loading
+// More utils to clean this up, some wrapper for unique_ptr
+// Ensure memory is being cleaned up properly
+// Efficiency tricks
+// Implementation cleanup
+// Properly get the image base
+
+#define DW_PR_DUx "llx"
+#define DW_PR_DUu "llu"
+
+Dwarf_Unsigned get_ranges_offset(Dwarf_Attribute attr) {
+    Dwarf_Unsigned off = 0;
+    Dwarf_Half attrform = 0;
+    dwarf_whatform(attr, &attrform, nullptr);
+    if (attrform == DW_FORM_rnglistx) {
+        int fres = dwarf_formudata(attr, &off, nullptr);
+        assert(fres == DW_DLV_OK);
+    } else {
+        int fres = dwarf_global_formref(attr, &off, nullptr);
+        assert(fres == DW_DLV_OK);
+    }
+    return off;
+}
+
+static int dwarf5_ranges(Dwarf_Die cu_die, Dwarf_Addr *lowest, Dwarf_Addr *highest) {
+    Dwarf_Unsigned offset = 0;
+    Dwarf_Attribute attr = 0;
+    Dwarf_Half attrform = 0;
+    Dwarf_Unsigned i = 0;
+    int res = 0;
+
+    res = dwarf_attr(cu_die, DW_AT_ranges, &attr, nullptr);
+    if(res != DW_DLV_OK) {
+        return res;
+    }
+    offset = get_ranges_offset(attr);
+    if(true) {
+        Dwarf_Unsigned rlesetoffset = 0;
+        Dwarf_Unsigned rnglists_count = 0;
+        Dwarf_Rnglists_Head head = 0;
+
+        dwarf_whatform(attr, &attrform, nullptr);
+        /* offset is in .debug_rnglists */
+        res = dwarf_rnglists_get_rle_head(
+            attr,
+            attrform,
+            offset,
+            &head,
+            &rnglists_count,
+            &rlesetoffset,
+            nullptr
+        );
+        if(res != DW_DLV_OK) {
+            /* ASSERT: is DW_DLV_NO_ENTRY */
+            dwarf_dealloc_attribute(attr);
+            return res;
+        }
+        for( ; i < rnglists_count; ++i) {
+            unsigned entrylen = 0;
+            unsigned rle_val = 0;
+            Dwarf_Unsigned raw1 = 0;
+            Dwarf_Unsigned raw2 = 0;
+            Dwarf_Bool unavail = 0;
+            Dwarf_Unsigned cooked1 = 0;
+            Dwarf_Unsigned cooked2 = 0;
+
+            res = dwarf_get_rnglists_entry_fields_a(
+                head,
+                i,
+                &entrylen,
+                &rle_val,
+                &raw1,
+                &raw2,
+                &unavail,
+                &cooked1,
+                &cooked2,
+                nullptr
+            );
+            if(res != DW_DLV_OK) {
+                /* ASSERT: is DW_DLV_NO_ENTRY */
+                continue;
+            }
+            if(unavail) {
+                continue;
+            }
+            switch(rle_val) {
+            case DW_RLE_end_of_list:
+            case DW_RLE_base_address:
+            case DW_RLE_base_addressx:
+                /* These are accounted for already */
+                break;
+            case DW_RLE_offset_pair:
+            case DW_RLE_startx_endx:
+            case DW_RLE_start_end:
+            case DW_RLE_startx_length:
+            case DW_RLE_start_length:
+                if(cooked1 < *lowest) {
+                    *lowest = cooked1;
+                }
+                if(cooked2 > *highest) {
+                    *highest = cooked2;
+                }
+            default:
+                /* Something is wrong. */
+                break;
+
+            }
+        }
+        dwarf_dealloc_rnglists_head(head);
+    }
+    dwarf_dealloc_attribute(attr);
+    return DW_DLV_OK;
+}
+
+static int dwarf4_ranges(
+    Dwarf_Debug dbg,
+    Dwarf_Die cu_die,
+    Dwarf_Addr cu_lowpc,
+    Dwarf_Addr *lowest,
+    Dwarf_Addr *highest
+) {
+    Dwarf_Unsigned offset;
+    Dwarf_Attribute attr = 0;
+    int res = 0;
+
+    res = dwarf_attr(cu_die, DW_AT_ranges, &attr, nullptr);
+    if(res != DW_DLV_OK) {
+        return res;
+    }
+    if(dwarf_global_formref(attr, &offset, nullptr) == DW_DLV_OK) {
+        Dwarf_Signed count = 0;
+        Dwarf_Ranges *ranges = 0;
+        Dwarf_Addr baseaddr = 0;
+        if(cu_lowpc != 0xffffffffffffffff) {
+            baseaddr = cu_lowpc;
+        }
+        res = dwarf_get_ranges_b(
+            dbg,
+            offset,
+            cu_die,
+            nullptr,
+            &ranges,
+            &count,
+            nullptr,
+            nullptr
+        );
+        for(int i = 0; i < count; i++) {
+            Dwarf_Ranges *cur = ranges + i;
+
+            if(cur->dwr_type == DW_RANGES_ENTRY) {
+                Dwarf_Addr rng_lowpc, rng_highpc;
+                rng_lowpc = baseaddr + cur->dwr_addr1;
+                rng_highpc = baseaddr + cur->dwr_addr2;
+                if(rng_lowpc < *lowest) {
+                    *lowest = rng_lowpc;
+                }
+                if(rng_highpc > *highest) {
+                    *highest = rng_highpc;
+                }
+            } else if(cur->dwr_type ==
+                DW_RANGES_ADDRESS_SELECTION) {
+                baseaddr = cur->dwr_addr2;
+            } else {  // DW_RANGES_END
+                baseaddr = cu_lowpc;
+            }
+        }
+        dwarf_dealloc_ranges(dbg, ranges, count);
+    }
+    dwarf_dealloc_attribute(attr);
+    return DW_DLV_OK;
+}
+
+namespace cpptrace {
+    namespace detail {
+        namespace libdwarf {
+            // printbugging as we go
+            constexpr bool dump_dwarf = false;
+            constexpr bool trace_dwarf = false;
+
+            static void err_handler(Dwarf_Error err, Dwarf_Ptr errarg) {
+                printf("libdwarf error reading %s: %lu %s\n", "xx", (unsigned long)dwarf_errno(err), dwarf_errmsg(err));
+                if(errarg) {
+                    printf("Error: errarg is nonnull but it should be null\n");
+                }
+                printf("Giving up");
+                exit(1);
+            }
+
+            static void print_line(Dwarf_Debug dbg, Dwarf_Line line, Dwarf_Addr pc, stacktrace_frame& frame) {
+                char what[] = "??";
+                char *         linesrc = what;
+                Dwarf_Unsigned lineno = 0;
+
+                (void)pc;
+
+                if(line) {
+                    /*  These never return DW_DLV_NO_ENTRY */
+                    dwarf_linesrc(line, &linesrc, nullptr);
+                    dwarf_lineno(line, &lineno, nullptr);
+                }
+                if(dump_dwarf) {
+                    printf("%s:%" DW_PR_DUu "\n", linesrc, lineno);
+                }
+                frame.line = static_cast<uint_least32_t>(lineno);
+                frame.filename = linesrc;
+                if(line) {
+                    dwarf_dealloc(dbg, linesrc, DW_DLA_STRING);
+                }
+            }
+
+            static Dwarf_Bool pc_in_die(Dwarf_Debug dbg, Dwarf_Die die,int version, Dwarf_Addr pc) {
+                int ret;
+                Dwarf_Addr cu_lowpc = 0xffffffffffffffff;
+                Dwarf_Addr cu_highpc = 0;
+                enum Dwarf_Form_Class highpc_cls;
+                Dwarf_Addr lowest = 0xffffffffffffffff;
+                Dwarf_Addr highest = 0;
+
+                ret = dwarf_lowpc(die, &cu_lowpc, nullptr);
+                if(ret == DW_DLV_OK) {
+                    if(pc == cu_lowpc) {
+                        return true;
+                    }
+                    ret = dwarf_highpc_b(die, &cu_highpc,
+                        nullptr, &highpc_cls, nullptr);
+                    if(ret == DW_DLV_OK) {
+                        if(highpc_cls == DW_FORM_CLASS_CONSTANT) {
+                            cu_highpc += cu_lowpc;
+                        }
+                        //fprintf(stderr, "low: %llx  high: %llx  pc: %llx\n", cu_lowpc, cu_highpc, pc);
+                        if(pc >= cu_lowpc && pc < cu_highpc) {
+                            return true;
+                        }
+                    }
+                }
+                if(version >= 5) {
+                    ret = dwarf5_ranges(die,
+                        &lowest,&highest);
+                } else {
+                    ret = dwarf4_ranges(dbg,die,cu_lowpc,
+                        &lowest,&highest);
+                }
+                //fprintf(stderr, "low: %llu  high: %llu\n", lowest, highest);
+                if(pc >= lowest && pc < highest) {
+                    return true;
+                }
+                return false;
+            }
+
+            static_assert(std::is_pointer<Dwarf_Die>::value, "Dwarf_Die not a pointer");
+            static_assert(std::is_pointer<Dwarf_Debug>::value, "Dwarf_Debug not a pointer");
+
+            struct die_object {
+                Dwarf_Debug dbg = nullptr;
+                Dwarf_Die die = nullptr;
+
+                die_object(Dwarf_Debug dbg, Dwarf_Die die) : dbg(dbg), die(die) {}
+
+                ~die_object() {
+                    if(die) {
+                        dwarf_dealloc(dbg, die, DW_DLA_DIE);
+                    }
+                }
+
+                die_object(const die_object&) = delete;
+
+                die_object& operator=(const die_object&) = delete;
+
+                die_object(die_object&& other) : dbg(other.dbg), die(other.die) {
+                    other.die = nullptr;
+                }
+
+                die_object& operator=(die_object&& other) {
+                    dbg = other.dbg;
+                    die = other.die;
+                    other.die = nullptr;
+                    return *this;
+                }
+
+                die_object get_child() const {
+                    Dwarf_Die child = nullptr;
+                    int ret = dwarf_child(
+                        die,
+                        &child,
+                        nullptr
+                    );
+                    if(ret == DW_DLV_OK) {
+                        return die_object(dbg, child);
+                    } else if(ret == DW_DLV_NO_ENTRY) {
+                        return die_object(dbg, 0);
+                    } else {
+                        fprintf(stderr, "Error\n");
+                        exit(1);
+                    }
+                }
+
+                die_object get_sibling() const {
+                    Dwarf_Die sibling = 0;
+                    int ret = dwarf_siblingof_b(dbg, die, true, &sibling, nullptr);
+                    if(ret == DW_DLV_OK) {
+                        return die_object(dbg, sibling);
+                    } else if(ret == DW_DLV_NO_ENTRY) {
+                        return die_object(dbg, 0);
+                    } else {
+                        fprintf(stderr, "Error\n");
+                        exit(1);
+                    }
+                }
+
+                operator bool() const {
+                    return die != nullptr;
+                }
+
+                Dwarf_Die get() const {
+                    return die;
+                }
+
+                std::string get_name() const {
+                    char* name;
+                    int ret = dwarf_diename(die, &name, nullptr);
+                    std::string str;
+                    if(ret != DW_DLV_NO_ENTRY) {
+                        str = name;
+                        dwarf_dealloc(dbg, name, DW_DLA_STRING);
+                    }
+                    return name;
+                }
+
+                optional<std::string> get_string_attribute(Dwarf_Half dw_attrnum) const {
+                    Dwarf_Attribute attr;
+                    int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr);
+                    if(ret == DW_DLV_OK) {
+                        char* raw_str;
+                        std::string str;
+                        ret = dwarf_formstring(attr, &raw_str, nullptr);
+                        assert(ret == DW_DLV_OK);
+                        str = raw_str;
+                        dwarf_dealloc(dbg, raw_str, DW_DLA_STRING);
+                        dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
+                        return str;
+                    } else {
+                        return nullopt;
+                    }
+                }
+
+                bool has_attr(Dwarf_Half dw_attrnum) const {
+                    Dwarf_Attribute attr;
+                    int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr);
+                    if(ret == DW_DLV_NO_ENTRY) {
+                        return false;
+                    } else if(ret == DW_DLV_OK) {
+                        // TODO: Better impl that doesn't require allocation....?
+                        dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
+                        return true;
+                    } else {
+                        fprintf(stderr, "Error\n");
+                        exit(1);
+                    }
+                }
+
+                Dwarf_Half get_tag() const {
+                    Dwarf_Half tag = 0;
+                    dwarf_tag(die, &tag, nullptr);
+                    return tag;
+                }
+
+                const char* get_tag_name() const {
+                    const char* tag_name;
+                    dwarf_get_TAG_name(get_tag(), &tag_name);
+                    return tag_name;
+                }
+
+                Dwarf_Off get_global_offset() const {
+                    Dwarf_Off off;
+                    int ret = dwarf_dieoffset(die, &off, nullptr);
+                    assert(ret == DW_DLV_OK);
+                    return off;
+                }
+
+                die_object resolve_reference_attribute(Dwarf_Half dw_attrnum) const {
+                    Dwarf_Attribute attr;
+                    int ret = dwarf_attr(die, dw_attrnum, &attr, nullptr);
+                    Dwarf_Half form = 0;
+                    ret = dwarf_whatform(attr, &form, nullptr);
+                    switch(form) {
+                        case DW_FORM_ref1:
+                        case DW_FORM_ref2:
+                        case DW_FORM_ref4:
+                        case DW_FORM_ref8:
+                        case DW_FORM_ref_udata:
+                            {
+                                Dwarf_Off off = 0;
+                                Dwarf_Bool is_info = dwarf_get_die_infotypes_flag(die);
+                                ret = dwarf_formref(attr, &off, &is_info, nullptr);
+                                assert(ret == DW_DLV_OK);
+                                Dwarf_Off goff = 0;
+                                ret = dwarf_convert_to_global_offset(attr, off, &goff, nullptr);
+                                assert(ret == DW_DLV_OK);
+                                Dwarf_Die targ_die_a = 0;
+                                ret = dwarf_offdie_b(dbg, goff, is_info, &targ_die_a, nullptr);
+                                assert(ret == DW_DLV_OK);
+                                return die_object(dbg, targ_die_a);
+                            }
+                        case DW_FORM_ref_addr:
+                            {
+                                Dwarf_Off off;
+                                ret = dwarf_global_formref(attr, &off, nullptr);
+                                int is_info_a = dwarf_get_die_infotypes_flag(die);
+                                Dwarf_Die targ_die_a = 0;
+                                ret = dwarf_offdie_b(dbg, off, is_info_a, &targ_die_a, nullptr);
+                                assert(ret == DW_DLV_OK);
+                                return die_object(dbg, targ_die_a);
+                            }
+                        case DW_FORM_ref_sig8:
+                            {
+                                Dwarf_Sig8 signature;
+                                ret = dwarf_formsig8(attr, &signature, nullptr);
+                                assert(ret == DW_DLV_OK);
+                                Dwarf_Die  targdie = 0;
+                                Dwarf_Bool targ_is_info = false;
+                                ret = dwarf_find_die_given_sig8(dbg, &signature, &targdie, &targ_is_info, nullptr);
+                                assert(ret == DW_DLV_OK);
+                                return die_object(dbg, targdie);
+                            }
+                        default:
+                            fprintf(stderr, "unknown form for attribute %d %d\n", dw_attrnum, form);
+                            exit(1);
+                    }
+                }
+            };
+
+            void walk_die_list(
+                Dwarf_Debug dbg,
+                const die_object& die,
+                std::function<void(Dwarf_Debug, const die_object&)> fn
+            ) {
+                fn(dbg, die);
+                die_object current = die.get_sibling();
+                while(true) {
+                    if(!current) {
+                        if(dump_dwarf) {
+                            fprintf(stderr, "End walk_die_list\n");
+                        }
+                        return;
+                    }
+                    fn(dbg, current);
+                    current = current.get_sibling();
+                }
+            }
+
+            void walk_die_list_recursive(
+                Dwarf_Debug dbg,
+                const die_object& die,
+                std::function<void(Dwarf_Debug, const die_object&)> fn
+            ) {
+                walk_die_list(
+                    dbg,
+                    die,
+                    [&fn](Dwarf_Debug dbg, const die_object& die) {
+                        auto child = die.get_child();
+                        if(child) {
+                            walk_die_list_recursive(dbg, child, fn);
+                        }
+                        fn(dbg, die);
+                    }
+                );
+            }
+
+            die_object get_type_die(Dwarf_Debug dbg, const die_object& die) {
+                Dwarf_Off type_offset;
+                Dwarf_Bool is_info;
+                int ret = dwarf_dietype_offset(die.get(), &type_offset, &is_info, nullptr);
+                if(ret == DW_DLV_OK) {
+                    Dwarf_Die type_die;
+                    ret = dwarf_offdie_b(
+                        dbg,
+                        type_offset,
+                        is_info,
+                        &type_die,
+                        nullptr
+                    );
+                    if(ret == DW_DLV_OK) {
+                        return die_object(dbg, type_die);
+                    } else {
+                        fprintf(stderr, "Error\n");
+                        exit(1);
+                    }
+                } else {
+                    fprintf(stderr, "no type offset??\n");
+                }
+                return die_object(dbg, nullptr);
+            }
+
+            bool has_type(Dwarf_Debug dbg, const die_object& die) {
+                Dwarf_Attribute attr;
+                int ret = dwarf_attr(die.get(), DW_AT_type, &attr, nullptr);
+                if(ret == DW_DLV_NO_ENTRY) {
+                    return false;
+                } else if(ret == DW_DLV_OK) {
+                    dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
+                    return true;
+                } else {
+                    fprintf(stderr, "Error\n");
+                    exit(1);
+                }
+            }
+
+            struct type_result {
+                std::string base;
+                std::string extent;
+
+                std::string get_type() {
+                    return base + extent;
+                }
+            };
+
+            // TODO: ::*, namespace lookup, arrays
+            // DW_TAG_namespace
+            const char* tag_to_keyword(Dwarf_Half tag) {
+                switch(tag) {
+                    case DW_TAG_atomic_type:
+                        return "_Atomic";
+                    case DW_TAG_const_type:
+                        return "const";
+                    case DW_TAG_volatile_type:
+                        return "volatile";
+                    case DW_TAG_restrict_type:
+                        return "restrict";
+                    default:
+                        {
+                            const char* tag_name = nullptr;
+                            dwarf_get_TAG_name(tag, &tag_name);
+                            fprintf(stderr, "tag_to_keyword unknown tag %s\n", tag_name);
+                            exit(1);
+                        }
+                }
+            }
+            const char* tag_to_ptr_ref(Dwarf_Half tag) {
+                switch(tag) {
+                    case DW_TAG_pointer_type:
+                        return "*";
+                    case DW_TAG_ptr_to_member_type:
+                        return "::*"; // TODO
+                    case DW_TAG_reference_type:
+                        return "&";
+                    case DW_TAG_rvalue_reference_type:
+                        return "&&";
+                    default:
+                        {
+                            const char* tag_name = nullptr;
+                            dwarf_get_TAG_name(tag, &tag_name);
+                            fprintf(stderr, "tag_to_ptr_ref unknown tag %s\n", tag_name);
+                            exit(1);
+                        }
+                }
+            }
+
+            /*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) {
+                assert(die.get_tag() == DW_TAG_array_type);
+                std::string extents = "";
+                walk_die_list(dbg, die.get_child(), [&extents](Dwarf_Debug dbg, const die_object& subrange) {
+                    if(subrange.get_tag() == DW_TAG_subrange_type) {
+                        Dwarf_Attribute attr = 0;
+                        int res = 0;
+                        res = dwarf_attr(subrange.get(), DW_AT_upper_bound, &attr, nullptr);
+                        if(res != DW_DLV_OK) {
+                            fprintf(stderr, "Error\n");
+                            return;
+                        }
+                        Dwarf_Half form;
+                        res = dwarf_whatform(attr, &form, nullptr);
+                        if(res != DW_DLV_OK) {
+                            fprintf(stderr, "Error\n");
+                            return;
+                        }
+                        //fprintf(stderr, "form: %d\n", form);
+                        Dwarf_Unsigned val;
+                        res = dwarf_formudata(attr, &val, nullptr);
+                        if(res != DW_DLV_OK) {
+                            fprintf(stderr, "Error\n");
+                            return;
+                        }
+                        extents += "[" + std::to_string(val + 1) + "]";
+                        dwarf_dealloc_attribute(attr);
+                    } else {
+                        fprintf(stderr, "unknown tag %s\n", subrange.get_tag_name());
+                    }
+                });
+                return extents;
+            }
+
+            std::string get_parameters(Dwarf_Debug dbg, const die_object& die) {
+                assert(die.get_tag() == DW_TAG_subroutine_type);
+                std::vector<std::string> params;
+                walk_die_list(dbg, die.get_child(), [&params](Dwarf_Debug dbg, const die_object& die) {
+                    if(die.get_tag() == DW_TAG_formal_parameter) {
+                        // TODO: Ignore DW_AT_artificial
+                        params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
+                    }
+                });
+                return "(" + join(params, ", ") + ")";
+            }
+
+            std::string resolve_type(Dwarf_Debug dbg, const die_object& die, std::string build) {
+                switch(auto tag = die.get_tag()) {
+                    case DW_TAG_base_type:
+                    case DW_TAG_class_type:
+                    case DW_TAG_structure_type:
+                    case DW_TAG_union_type:
+                    case DW_TAG_enumeration_type:
+                        return die.get_name() + build;
+                    case DW_TAG_typedef:
+                        return resolve_type(dbg, get_type_die(dbg, die));
+                    //case DW_TAG_subroutine_type:
+                    //    {
+                    //        // If there's no DW_AT_type then it's a void
+                    //        std::vector<std::string> params;
+                    //        // TODO: Code duplication with retrieve_symbol_for_subprogram?
+                    //        walk_die_list(dbg, die.get_child(), [&params] (Dwarf_Debug dbg, const die_object& die) {
+                    //            if(die.get_tag() == DW_TAG_formal_parameter) {
+                    //                // TODO: Ignore DW_AT_artificial
+                    //                params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
+                    //            }
+                    //        });
+                    //        if(!has_type(dbg, die)) {
+                    //            return "void" + (build.empty() ? "" : "(" + build + ")") + "(" + join(params, ", ") + ")";
+                    //        } else {
+                    //            // resolving return type, building on build
+                    //            return resolve_type(
+                    //                dbg, get_type_die(dbg, die),
+                    //                (build.empty() ? "" : "(" + build + ")")
+                    //                    + "("
+                    //                    + join(params, ", ")
+                    //                    + ")"
+                    //            );
+                    //        }
+                    //    }
+                    //case DW_TAG_array_type:
+                    //    return resolve_type(dbg, get_type_die(dbg, die), (build.empty() ? "" : "(" + build + ")") + "[" + "x" + "]");
+                    case DW_TAG_pointer_type:
+                    case DW_TAG_reference_type:
+                    case DW_TAG_rvalue_reference_type:
+                    case DW_TAG_ptr_to_member_type:
+                        {
+                            const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child
+                            const auto child_tag = child.get_tag();
+                            switch(child_tag) {
+                                case DW_TAG_subroutine_type:
+                                    if(!has_type(dbg, child)) {
+                                        return "void(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child);
+                                    } else {
+                                        return resolve_type(
+                                            dbg,
+                                            get_type_die(dbg, child),
+                                            "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_parameters(dbg, child)
+                                        );
+                                    }
+                                case DW_TAG_array_type:
+                                    return resolve_type(
+                                        dbg,
+                                        get_type_die(dbg, child),
+                                        "(" + std::string(tag_to_ptr_ref(tag)) + build + ")" + get_array_extents(dbg, child)
+                                    );
+                                default:
+                                    if(build.empty()) {
+                                        return resolve_type(dbg, get_type_die(dbg, die), tag_to_ptr_ref(tag));
+                                    } else {
+                                        return resolve_type(
+                                            dbg,
+                                            get_type_die(dbg, die),
+                                            std::string(tag_to_ptr_ref(tag)) + " " + build
+                                        );
+                                    }
+                            }
+                        }
+                    case DW_TAG_const_type:
+                    case DW_TAG_atomic_type:
+                    case DW_TAG_volatile_type:
+                    case DW_TAG_restrict_type:
+                        {
+                            const auto child = get_type_die(dbg, die); // AST child, rather than dwarf child
+                            const auto child_tag = child.get_tag();
+                            switch(child_tag) {
+                                case DW_TAG_base_type:
+                                case DW_TAG_class_type:
+                                case DW_TAG_typedef:
+                                    return std::string(tag_to_keyword(tag))
+                                            + " "
+                                            + resolve_type(dbg, get_type_die(dbg, die), build);
+                                default:
+                                    return resolve_type(
+                                        dbg,
+                                        get_type_die(dbg, die),
+                                        std::string(tag_to_keyword(tag)) + " " + build
+                                    );
+                            }
+                        }
+                    default:
+                        {
+                            fprintf(stderr, "unknown tag %s\n", die.get_tag_name());
+                            exit(1);
+                        }
+                }
+                return {"<unknown>", "<unknown>"};
+            }*/
+
+            bool is_mangled_name(const std::string& name) {
+                return name.find("_Z") || name.find("?h@@");
+            }
+
+            void retrieve_symbol_for_subprogram(
+                Dwarf_Debug dbg,
+                const die_object& die,
+                Dwarf_Addr pc,
+                Dwarf_Half dwversion,
+                stacktrace_frame& frame
+            ) {
+                assert(die.get_tag() == DW_TAG_subprogram);
+                optional<std::string> name;
+                if(auto linkage_name = die.get_string_attribute(DW_AT_linkage_name)) {
+                    name = std::move(linkage_name);
+                } else if(auto linkage_name = die.get_string_attribute(DW_AT_MIPS_linkage_name)) {
+                    name = std::move(linkage_name);
+                } else if(auto linkage_name = die.get_string_attribute(DW_AT_name)) {
+                    name = std::move(linkage_name);
+                }
+                if(name) {
+                    frame.symbol = std::move(name).unwrap();
+                } else {
+                    if(die.has_attr(DW_AT_specification)) {
+                        die_object spec = die.resolve_reference_attribute(DW_AT_specification);
+                        // TODO: Passing pc here is misleading
+                        return retrieve_symbol_for_subprogram(dbg, spec, pc, dwversion, frame);
+                    }
+                }
+                // TODO: Handle namespaces
+                // TODO: Disabled for now
+                /*std::string name = die.get_name();
+                std::vector<std::string> params;
+                auto child = die.get_child();
+                if(child) {
+                    walk_die_list_recursive(
+                        dbg,
+                        child,
+                        [pc, dwversion, &frame, &params] (Dwarf_Debug dbg, const die_object& die) {
+                            if(die.get_tag() == DW_TAG_formal_parameter) {
+                                // TODO: Ignore DW_AT_artificial
+                                params.push_back(resolve_type(dbg, get_type_die(dbg, die)));
+                            }
+                        }
+                    );
+                } else {
+                    fprintf(stderr, "no child %s\n", name.c_str());
+                }
+                frame.symbol = name + "(" + join(params, ", ") + ")";*/
+            }
+
+            void retrieve_symbol(
+                Dwarf_Debug dbg,
+                const die_object& die,
+                Dwarf_Addr pc,
+                Dwarf_Half dwversion,
+                stacktrace_frame& frame
+            ) {
+                walk_die_list(
+                    dbg,
+                    die,
+                    [pc, dwversion, &frame] (Dwarf_Debug dbg, const die_object& die) {
+                        if(dump_dwarf) {
+                            fprintf(
+                                stderr,
+                                "-------------> %d %s %s\n",
+                                dwversion,
+                                die.get_tag_name(),
+                                die.get_name().c_str()
+                            );
+                        }
+
+                        if(!pc_in_die(dbg, die.get(), dwversion, pc)) {
+                            if(dump_dwarf) {
+                                fprintf(stderr, "pc not in die\n");
+                            }
+                        } else {
+                            if(trace_dwarf) {
+                                fprintf(
+                                    stderr,
+                                    "pc in die %08llx %s\n",
+                                    (unsigned long long) die.get_global_offset(),
+                                    die.get_tag_name()
+                                );
+                            }
+                            if(dump_dwarf) {
+                                fprintf(stderr, "pc in die <-----------------------------------\n");
+                            }
+                            if(die.get_tag() == DW_TAG_subprogram) {
+                                retrieve_symbol_for_subprogram(dbg, die, pc, dwversion, frame);
+                            }
+                            auto child = die.get_child();
+                            if(child) {
+                                retrieve_symbol(dbg, child, pc, dwversion, frame);
+                            } else {
+                                if(dump_dwarf) {
+                                    fprintf(stderr, "(no child)\n");
+                                }
+                            }
+                        }
+                    }
+                );
+            }
+
+            void retrieve_line_info(
+                Dwarf_Debug dbg,
+                const die_object& die,
+                Dwarf_Addr pc,
+                Dwarf_Half dwversion,
+                stacktrace_frame& frame
+            ) {
+                Dwarf_Unsigned version;
+                Dwarf_Small table_count;
+                Dwarf_Line_Context ctxt;
+                Dwarf_Bool is_found = false;
+                (void)dwversion;
+                int ret = dwarf_srclines_b(
+                    die.get(),
+                    &version,
+                    &table_count,
+                    &ctxt,
+                    nullptr
+                );
+                if(ret == DW_DLV_NO_ENTRY) {
+                    fprintf(stderr, "dwarf_srclines_b error\n");
+                    return;
+                }
+                if(table_count == 1) {
+                    Dwarf_Line *linebuf = 0;
+                    Dwarf_Signed linecount = 0;
+                    Dwarf_Addr prev_lineaddr = 0;
+
+                    dwarf_srclines_from_linecontext(ctxt, &linebuf,
+                        &linecount, nullptr);
+                    Dwarf_Line prev_line = 0;
+                    for(int i = 0; i < linecount; i++) {
+                        Dwarf_Line line = linebuf[i];
+                        Dwarf_Addr lineaddr = 0;
+
+                        dwarf_lineaddr(line, &lineaddr, nullptr);
+                        if(pc == lineaddr) {
+                            /* Print the last line entry containing current pc. */
+                            Dwarf_Line last_pc_line = line;
+
+                            for(int j = i + 1; j < linecount; j++) {
+                                Dwarf_Line j_line = linebuf[j];
+                                dwarf_lineaddr(j_line, &lineaddr, nullptr);
+
+                                if(pc == lineaddr) {
+                                    last_pc_line = j_line;
+                                }
+                            }
+                            is_found = true;
+                            print_line(dbg, last_pc_line, pc, frame);
+                            break;
+                        } else if(prev_line && pc > prev_lineaddr &&
+                            pc < lineaddr) {
+                            is_found = true;
+                            print_line(dbg, prev_line, pc, frame);
+                            break;
+                        }
+                        Dwarf_Bool is_lne;
+                        dwarf_lineendsequence(line, &is_lne, nullptr);
+                        if(is_lne) {
+                            prev_line = 0;
+                        } else {
+                            prev_lineaddr = lineaddr;
+                            prev_line = line;
+                        }
+                    }
+                }
+                dwarf_srclines_dealloc_b(ctxt);
+            }
+
+            void walk_compilation_units(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) {
+                // 0 passed as the dieto the first call of dwarf_siblingof_b immediately after dwarf_next_cu_header_d to
+                // fetch the cu die
+                die_object cu_die(dbg, nullptr);
+                cu_die = cu_die.get_sibling();
+                if(!cu_die) {
+                    if(dump_dwarf) {
+                        fprintf(stderr, "End walk_compilation_units\n");
+                    }
+                    return;
+                }
+                walk_die_list(
+                    dbg,
+                    cu_die,
+                    [&frame, pc] (Dwarf_Debug dbg, const die_object& cu_die) {
+                        Dwarf_Half offset_size = 0;
+                        Dwarf_Half dwversion = 0;
+                        dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size);
+                        if(trace_dwarf) {
+                            fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str());
+                        }
+                        /*auto child = cu_die.get_child();
+                        if(child) {
+                            walk_die_list_recursive(
+                                dbg,
+                                child,
+                                [&frame, pc, dwversion] (Dwarf_Debug dbg, const die_object& cu_die) {
+
+                                }
+                            );
+                        }*/
+                        //walk_die(dbg, cu_die, pc, dwversion, false, frame);
+                        if(pc_in_die(dbg, cu_die.get(), dwversion, pc)) {
+                            retrieve_line_info(dbg, cu_die, pc, dwversion, frame);
+                            retrieve_symbol(dbg, cu_die, pc, dwversion, frame);
+                        }
+                    }
+                );
+            }
+
+            void walk_dbg(Dwarf_Debug dbg, Dwarf_Addr pc, stacktrace_frame& frame) {
+                // libdwarf keeps track of where it is in the file, dwarf_next_cu_header_d is statefull
+                Dwarf_Unsigned next_cu_header;
+                Dwarf_Half header_cu_type;
+                while(true) {
+                    int ret = dwarf_next_cu_header_d(
+                        dbg,
+                        true,
+                        nullptr,
+                        nullptr,
+                        nullptr,
+                        nullptr,
+                        nullptr,
+                        nullptr,
+                        nullptr,
+                        nullptr,
+                        &next_cu_header,
+                        &header_cu_type,
+                        nullptr
+                    );
+                    if(ret == DW_DLV_NO_ENTRY) {
+                        if(dump_dwarf) {
+                            fprintf(stderr, "End walk_dbg\n");
+                        }
+                        return;
+                    }
+                    if(ret != DW_DLV_OK) {
+                        fprintf(stderr, "Error\n");
+                        return;
+                    }
+                    walk_compilation_units(dbg, pc, frame);
+                }
+            }
+
+            void lookup_pc(
+                const char* object,
+                Dwarf_Addr pc,
+                stacktrace_frame& frame
+            ) {
+                if(dump_dwarf) {
+                    fprintf(stderr, "%s\n", object);
+                    fprintf(stderr, "%llx\n", pc);
+                }
+                Dwarf_Debug dbg;
+                Dwarf_Ptr errarg = 0;
+                auto ret = dwarf_init_path(
+                    object,
+                    nullptr,
+                    0,
+                    DW_GROUPNUMBER_ANY,
+                    err_handler,
+                    errarg,
+                    &dbg,
+                    nullptr
+                );
+                if(ret == DW_DLV_NO_ENTRY) {
+                    // fail, no debug info
+                } else if(ret != DW_DLV_OK) {
+                    fprintf(stderr, "Error\n");
+                } else {
+                    walk_dbg(dbg, pc, frame);
+                }
+                dwarf_finish(dbg);
+            }
+
+            stacktrace_frame resolve_frame(const dlframe& frame_info) {
+                stacktrace_frame frame{};
+                frame.filename = frame_info.obj_path;
+                frame.symbol = frame_info.symbol;
+                frame.address = frame_info.raw_address;
+                std::string obj_path = frame_info.obj_path;
+                #if IS_APPLE
+                //std::string obj_path = frame_info.obj_path;
+                //char* dir = dirname(obj_path.data());
+                //std::string dsym =
+                if(directory_exists(obj_path + ".dSYM")) {
+                    obj_path += ".dSYM/Contents/Resources/DWARF/" + basename(frame_info.obj_path);
+                }
+                #endif
+                if(trace_dwarf) {
+                    fprintf(
+                        stderr,
+                        "%s %08llx %s\n",
+                        obj_path.c_str(),
+                        (unsigned long long)frame_info.obj_address,
+                        frame_info.symbol.c_str()
+                    );
+                }
+                lookup_pc(
+                    obj_path.c_str(),
+                    frame_info.obj_address,
+                    frame
+                );
+                return frame;
+            }
+
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
+                std::vector<stacktrace_frame> trace;
+                trace.reserve(frames.size());
+                for(const auto& frame : get_frames_object_info(frames)) {
+                    trace.push_back(resolve_frame(frame));
+                }
+                return trace;
+            }
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/symbols_with_nothing.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/symbols_with_nothing.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/symbols_with_nothing.cpp	(revision 2573)
@@ -0,0 +1,25 @@
+#include "cpptrace_default.hpp"
+#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
+
+#include <cpptrace.hpp>
+#include "symbols.hpp"
+
+#include <vector>
+
+namespace cpptrace {
+    namespace detail {
+        namespace nothing {
+            std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
+                return std::vector<stacktrace_frame>(frames.size(), {
+                    0,
+                    0,
+                    0,
+                    "",
+                    ""
+                });
+            }
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/unwind.hpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/unwind.hpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/unwind.hpp	(revision 2573)
@@ -0,0 +1,22 @@
+#ifndef UNWIND_HPP
+#define UNWIND_HPP
+
+#include "cpptrace_default.hpp"
+#include "common.hpp"
+
+#include <cstddef>
+#include <vector>
+
+namespace cpptrace {
+    namespace detail {
+        #ifdef CPPTRACE_HARD_MAX_FRAMES
+        constexpr size_t hard_max_frames = CPPTRACE_HARD_MAX_FRAMES;
+        #else
+        constexpr size_t hard_max_frames = 100;
+        #endif
+        CPPTRACE_FORCE_NO_INLINE
+        std::vector<void*> capture_frames(size_t skip);
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/unwind_with_execinfo.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/unwind_with_execinfo.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/unwind_with_execinfo.cpp	(revision 2573)
@@ -0,0 +1,26 @@
+#ifdef CPPTRACE_UNWIND_WITH_EXECINFO
+
+#include "unwind.hpp"
+#include "common.hpp"
+
+#include <algorithm>
+#include <cstddef>
+#include <vector>
+
+#include <execinfo.h>
+
+namespace cpptrace {
+    namespace detail {
+        CPPTRACE_FORCE_NO_INLINE
+        std::vector<void*> capture_frames(size_t skip) {
+            std::vector<void*> frames(hard_max_frames + skip, nullptr);
+            const int n_frames = backtrace(frames.data(), int(hard_max_frames + skip)); // thread safe
+            frames.resize(n_frames);
+            frames.erase(frames.begin(), frames.begin() + ptrdiff_t(std::min(skip + 1, frames.size())));
+            frames.shrink_to_fit();
+            return frames;
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/unwind_with_nothing.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/unwind_with_nothing.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/unwind_with_nothing.cpp	(revision 2573)
@@ -0,0 +1,17 @@
+#include "cpptrace_default.hpp"
+#ifdef CPPTRACE_UNWIND_WITH_NOTHING
+
+#include "unwind.hpp"
+
+#include <cstddef>
+#include <vector>
+
+namespace cpptrace {
+    namespace detail {
+        std::vector<void*> capture_frames(size_t) {
+            return {};
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/unwind_with_unwind.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/unwind_with_unwind.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/unwind_with_unwind.cpp	(revision 2573)
@@ -0,0 +1,59 @@
+#ifdef CPPTRACE_UNWIND_WITH_UNWIND
+
+#include "unwind.hpp"
+#include "common.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <vector>
+
+#include <unwind.h>
+
+namespace cpptrace {
+    namespace detail {
+        struct unwind_state {
+            std::size_t skip;
+            std::size_t count;
+            std::vector<void*>& vec;
+        };
+
+        _Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) {
+            unwind_state& state = *static_cast<unwind_state*>(arg);
+            if(state.skip) {
+                state.skip--;
+                if(_Unwind_GetIP(context) == uintptr_t(0)) {
+                    return _URC_END_OF_STACK;
+                } else {
+                    return _URC_NO_REASON;
+                }
+            }
+
+            assert(state.count < state.vec.size());
+            int is_before_instruction = 0;
+            uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction);
+            if(!is_before_instruction && ip != uintptr_t(0)) {
+                ip--;
+            }
+            if (ip == uintptr_t(0) || state.count == state.vec.size()) {
+                return _URC_END_OF_STACK;
+            } else {
+                // TODO: push_back?...
+                state.vec[state.count++] = (void*)ip;
+                return _URC_NO_REASON;
+            }
+        }
+
+        CPPTRACE_FORCE_NO_INLINE
+        std::vector<void*> capture_frames(size_t skip) {
+            std::vector<void*> frames(hard_max_frames, nullptr);
+            unwind_state state{skip + 1, 0, frames};
+            _Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe
+            frames.resize(state.count);
+            frames.shrink_to_fit();
+            return frames;
+        }
+    }
+}
+
+#endif
Index: XIOS3/trunk/extern/cpptrace/src/unwind_with_winapi.cpp
===================================================================
--- XIOS3/trunk/extern/cpptrace/src/unwind_with_winapi.cpp	(revision 2573)
+++ XIOS3/trunk/extern/cpptrace/src/unwind_with_winapi.cpp	(revision 2573)
@@ -0,0 +1,24 @@
+#ifdef CPPTRACE_UNWIND_WITH_WINAPI
+
+#include "cpptrace.hpp"
+#include "unwind.hpp"
+#include "common.hpp"
+
+#include <vector>
+
+#include <windows.h>
+
+namespace cpptrace {
+    namespace detail {
+        CPPTRACE_FORCE_NO_INLINE
+        std::vector<void*> capture_frames(size_t skip) {
+            std::vector<PVOID> addrs(hard_max_frames, nullptr);
+            int frames = CaptureStackBackTrace(static_cast<DWORD>(skip + 1), hard_max_frames, addrs.data(), NULL);
+            addrs.resize(frames);
+            addrs.shrink_to_fit();
+            return addrs;
+        }
+    }
+}
+
+#endif
