More std::

This commit is contained in:
Jeremy 2023-10-08 22:48:12 -04:00
parent e5ed083996
commit ae5d2392fe
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
25 changed files with 155 additions and 153 deletions

View File

@ -27,7 +27,7 @@ namespace cpptrace {
struct stacktrace; struct stacktrace;
struct raw_trace { struct raw_trace {
std::vector<uintptr_t> frames; std::vector<std::uintptr_t> frames;
CPPTRACE_API static raw_trace current(std::uint_least32_t skip = 0); CPPTRACE_API static raw_trace current(std::uint_least32_t skip = 0);
CPPTRACE_API static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth); CPPTRACE_API static raw_trace current(std::uint_least32_t skip, std::uint_least32_t max_depth);
CPPTRACE_API object_trace resolve_object_trace() const; CPPTRACE_API object_trace resolve_object_trace() const;
@ -35,8 +35,8 @@ namespace cpptrace {
CPPTRACE_API void clear(); CPPTRACE_API void clear();
CPPTRACE_API bool empty() const noexcept; CPPTRACE_API bool empty() const noexcept;
using iterator = std::vector<uintptr_t>::iterator; using iterator = std::vector<std::uintptr_t>::iterator;
using const_iterator = std::vector<uintptr_t>::const_iterator; using const_iterator = std::vector<std::uintptr_t>::const_iterator;
inline iterator begin() noexcept { return frames.begin(); } inline iterator begin() noexcept { return frames.begin(); }
inline iterator end() noexcept { return frames.end(); } inline iterator end() noexcept { return frames.end(); }
inline const_iterator begin() const noexcept { return frames.begin(); } inline const_iterator begin() const noexcept { return frames.begin(); }
@ -48,8 +48,8 @@ namespace cpptrace {
struct object_frame { struct object_frame {
std::string obj_path; std::string obj_path;
std::string symbol; std::string symbol;
uintptr_t raw_address = 0; std::uintptr_t raw_address = 0;
uintptr_t obj_address = 0; std::uintptr_t obj_address = 0;
}; };
struct object_trace { struct object_trace {
@ -71,7 +71,7 @@ namespace cpptrace {
}; };
struct stacktrace_frame { struct stacktrace_frame {
uintptr_t address; std::uintptr_t address;
std::uint_least32_t line; // TODO: This should use UINT_LEAST32_MAX as a sentinel std::uint_least32_t line; // TODO: This should use UINT_LEAST32_MAX as a sentinel
std::uint_least32_t column; // UINT_LEAST32_MAX if not present std::uint_least32_t column; // UINT_LEAST32_MAX if not present
std::string filename; std::string filename;
@ -183,13 +183,13 @@ namespace cpptrace {
protected: protected:
explicit exception_with_message( explicit exception_with_message(
std::string&& message_arg, std::string&& message_arg,
uint32_t skip std::uint32_t skip
) noexcept : exception(skip + 1), message(std::move(message_arg)) {} ) noexcept : exception(skip + 1), message(std::move(message_arg)) {}
explicit exception_with_message( explicit exception_with_message(
std::string&& message_arg, std::string&& message_arg,
uint_least32_t skip, std::uint_least32_t skip,
uint_least32_t max_depth std::uint_least32_t max_depth
) noexcept : exception(skip + 1, max_depth), message(std::move(message_arg)) {} ) noexcept : exception(skip + 1, max_depth), message(std::move(message_arg)) {}
public: public:

View File

@ -118,7 +118,7 @@ namespace cpptrace {
stream stream
<< std::hex << std::hex
<< "0x" << "0x"
<< std::setw(2 * sizeof(uintptr_t)) << std::setw(2 * sizeof(std::uintptr_t))
<< std::setfill('0') << std::setfill('0')
<< frame.address << frame.address
<< std::dec << std::dec
@ -190,7 +190,7 @@ namespace cpptrace {
<< std::hex << std::hex
<< blue << blue
<< "0x" << "0x"
<< std::setw(2 * sizeof(uintptr_t)) << std::setw(2 * sizeof(std::uintptr_t))
<< std::setfill('0') << std::setfill('0')
<< frame.address << frame.address
<< std::dec << std::dec
@ -301,7 +301,7 @@ namespace cpptrace {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
stacktrace generate_trace(std::uint32_t skip, std::uint_least32_t max_depth) { stacktrace generate_trace(std::uint32_t skip, std::uint_least32_t max_depth) {
try { try {
std::vector<uintptr_t> frames = detail::capture_frames(skip + 1, max_depth); std::vector<std::uintptr_t> frames = detail::capture_frames(skip + 1, max_depth);
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames); std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
for(auto& frame : trace) { for(auto& frame : trace) {
frame.symbol = detail::demangle(frame.symbol); frame.symbol = detail::demangle(frame.symbol);
@ -406,7 +406,7 @@ namespace cpptrace {
} catch(const std::exception& e) { } catch(const std::exception& e) {
if(!detail::should_absorb_trace_exceptions()) { if(!detail::should_absorb_trace_exceptions()) {
// TODO: Append to message somehow // TODO: Append to message somehow
fprintf( std::fprintf(
stderr, stderr,
"Cpptrace: Exception ocurred while resolving trace in cpptrace::exception object:\n%s\n", "Cpptrace: Exception ocurred while resolving trace in cpptrace::exception object:\n%s\n",
e.what() e.what()
@ -450,7 +450,7 @@ namespace cpptrace {
} catch(const std::exception& e) { } catch(const std::exception& e) {
if(!detail::should_absorb_trace_exceptions()) { if(!detail::should_absorb_trace_exceptions()) {
// TODO: Append to message somehow // TODO: Append to message somehow
fprintf( std::fprintf(
stderr, stderr,
"Exception ocurred while resolving trace in cpptrace::exception object:\n%s\n", "Exception ocurred while resolving trace in cpptrace::exception object:\n%s\n",
e.what() e.what()

View File

@ -2,7 +2,6 @@
#include "demangle.hpp" #include "demangle.hpp"
#include <cstdlib>
#include <string> #include <string>
#include <windows.h> #include <windows.h>

View File

@ -51,13 +51,13 @@ namespace cpptrace {
namespace detail { namespace detail {
// Placed here instead of utils because it's used by error.hpp and utils.hpp // Placed here instead of utils because it's used by error.hpp and utils.hpp
template<typename... T> std::string stringf(T... args) { template<typename... T> std::string stringf(T... args) {
int length = snprintf(nullptr, 0, args...); int length = std::snprintf(nullptr, 0, args...);
if(length < 0) { if(length < 0) {
throw std::logic_error("invalid arguments to stringf"); throw std::logic_error("invalid arguments to stringf");
} }
std::string str(length, 0); std::string str(length, 0);
// .data is const char* in c++11, but &str[0] should be legal // .data is const char* in c++11, but &str[0] should be legal
snprintf(&str[0], length + 1, args...); std::snprintf(&str[0], length + 1, args...);
return str; return str;
} }

View File

@ -260,7 +260,7 @@ namespace libdwarf {
return; return;
} }
VERIFY(res == DW_DLV_OK); VERIFY(res == DW_DLV_OK);
for(size_t i = 0 ; i < rnglists_entries; i++) { for(std::size_t i = 0 ; i < rnglists_entries; i++) {
unsigned entrylen = 0; unsigned entrylen = 0;
unsigned rle_value_out = 0; unsigned rle_value_out = 0;
Dwarf_Unsigned raw1 = 0; Dwarf_Unsigned raw1 = 0;
@ -415,7 +415,7 @@ namespace libdwarf {
} }
void print() const { void print() const {
fprintf( std::fprintf(
stderr, stderr,
"%08llx %s %s\n", "%08llx %s %s\n",
to_ull(get_global_offset()), to_ull(get_global_offset()),

View File

@ -25,9 +25,9 @@ namespace detail {
} }
template<std::size_t Bits> template<std::size_t Bits>
static uintptr_t elf_get_module_image_base_from_program_table( static std::uintptr_t elf_get_module_image_base_from_program_table(
const std::string& obj_path, const std::string& obj_path,
FILE* file, std::FILE* file,
bool is_little_endian bool is_little_endian
) { ) {
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument"); static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
@ -49,17 +49,17 @@ namespace detail {
return 0; return 0;
} }
static uintptr_t elf_get_module_image_base(const std::string& obj_path) { static std::uintptr_t elf_get_module_image_base(const std::string& obj_path) {
auto file = raii_wrap(fopen(obj_path.c_str(), "rb"), file_deleter); auto file = raii_wrap(std::fopen(obj_path.c_str(), "rb"), file_deleter);
if(file == nullptr) { if(file == nullptr) {
throw file_error("Unable to read object file " + obj_path); throw file_error("Unable to read object file " + obj_path);
} }
// Initial checks/metadata // Initial checks/metadata
auto magic = load_bytes<std::array<char, 4>>(file, 0); auto magic = load_bytes<std::array<char, 4>>(file, 0);
VERIFY(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'}), "File is not ELF " + obj_path); VERIFY(magic == (std::array<char, 4>{0x7F, 'E', 'L', 'F'}), "File is not ELF " + obj_path);
bool is_64 = load_bytes<uint8_t>(file, 4) == 2; bool is_64 = load_bytes<std::uint8_t>(file, 4) == 2;
bool is_little_endian = load_bytes<uint8_t>(file, 5) == 1; bool is_little_endian = load_bytes<std::uint8_t>(file, 5) == 1;
VERIFY(load_bytes<uint8_t>(file, 6) == 1, "Unexpected ELF endianness " + obj_path); VERIFY(load_bytes<std::uint8_t>(file, 6) == 1, "Unexpected ELF endianness " + obj_path);
// get image base // get image base
if(is_64) { if(is_64) {
return elf_get_module_image_base_from_program_table<64>(obj_path, file, is_little_endian); return elf_get_module_image_base_from_program_table<64>(obj_path, file, is_little_endian);

View File

@ -1,7 +1,6 @@
#ifndef ERROR_HPP #ifndef ERROR_HPP
#define ERROR_HPP #define ERROR_HPP
#include <cstdio>
#include <exception> #include <exception>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>

View File

@ -23,7 +23,7 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
static bool is_mach_o(uint32_t magic) { static bool is_mach_o(std::uint32_t magic) {
switch(magic) { switch(magic) {
case FAT_MAGIC: case FAT_MAGIC:
case FAT_CIGAM: case FAT_CIGAM:
@ -37,17 +37,17 @@ namespace detail {
} }
} }
static bool is_fat_magic(uint32_t magic) { static bool is_fat_magic(std::uint32_t magic) {
return magic == FAT_MAGIC || magic == FAT_CIGAM; return magic == FAT_MAGIC || magic == FAT_CIGAM;
} }
// Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c // Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
// and https://lowlevelbits.org/parsing-mach-o-files/ // and https://lowlevelbits.org/parsing-mach-o-files/
static bool is_magic_64(uint32_t magic) { static bool is_magic_64(std::uint32_t magic) {
return magic == MH_MAGIC_64 || magic == MH_CIGAM_64; return magic == MH_MAGIC_64 || magic == MH_CIGAM_64;
} }
static bool should_swap_bytes(uint32_t magic) { static bool should_swap_bytes(std::uint32_t magic) {
return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM; return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM;
} }
@ -74,8 +74,8 @@ namespace detail {
#endif #endif
template<std::size_t Bits> template<std::size_t Bits>
static optional<uintptr_t> macho_get_text_vmaddr_mach( static optional<std::uintptr_t> macho_get_text_vmaddr_mach(
FILE* obj_file, std::FILE* obj_file,
const std::string& obj_path, const std::string& obj_path,
off_t offset, off_t offset,
bool should_swap, bool should_swap,
@ -84,15 +84,15 @@ namespace detail {
static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument"); static_assert(Bits == 32 || Bits == 64, "Unexpected Bits argument");
using Mach_Header = typename std::conditional<Bits == 32, mach_header, mach_header_64>::type; using Mach_Header = typename std::conditional<Bits == 32, mach_header, mach_header_64>::type;
using Segment_Command = typename std::conditional<Bits == 32, segment_command, segment_command_64>::type; using Segment_Command = typename std::conditional<Bits == 32, segment_command, segment_command_64>::type;
uint32_t ncmds; std::uint32_t ncmds;
off_t load_commands_offset = offset; off_t load_commands_offset = offset;
size_t header_size = sizeof(Mach_Header); std::size_t header_size = sizeof(Mach_Header);
Mach_Header header = load_bytes<Mach_Header>(obj_file, offset); Mach_Header header = load_bytes<Mach_Header>(obj_file, offset);
if(should_swap) { if(should_swap) {
swap_mach_header(header); swap_mach_header(header);
} }
thread_local static struct LP(mach_header)* mhp = _NSGetMachExecuteHeader(); thread_local static struct LP(mach_header)* mhp = _NSGetMachExecuteHeader();
//fprintf( //std::fprintf(
// stderr, // stderr,
// "----> %d %d; %d %d\n", // "----> %d %d; %d %d\n",
// header.cputype, // header.cputype,
@ -114,7 +114,7 @@ namespace detail {
load_commands_offset += header_size; load_commands_offset += header_size;
// iterate load commands // iterate load commands
off_t actual_offset = load_commands_offset; off_t actual_offset = load_commands_offset;
for(uint32_t i = 0; i < ncmds; i++) { for(std::uint32_t i = 0; i < ncmds; i++) {
load_command cmd = load_bytes<load_command>(obj_file, actual_offset); load_command cmd = load_bytes<load_command>(obj_file, actual_offset);
if(should_swap) { if(should_swap) {
swap_load_command(&cmd, NX_UnknownByteOrder); swap_load_command(&cmd, NX_UnknownByteOrder);
@ -124,7 +124,7 @@ namespace detail {
if(should_swap) { if(should_swap) {
swap_segment_command(segment); swap_segment_command(segment);
} }
if(strcmp(segment.segname, "__TEXT") == 0) { if(std::strcmp(segment.segname, "__TEXT") == 0) {
return segment.vmaddr; return segment.vmaddr;
} }
actual_offset += cmd.cmdsize; actual_offset += cmd.cmdsize;
@ -134,23 +134,27 @@ namespace detail {
return 0; return 0;
} }
static uintptr_t macho_get_text_vmaddr_fat(FILE* obj_file, const std::string& obj_path, bool should_swap) { static std::uintptr_t macho_get_text_vmaddr_fat(
size_t header_size = sizeof(fat_header); std::FILE* obj_file,
size_t arch_size = sizeof(fat_arch); const std::string& obj_path,
bool should_swap
) {
std::size_t header_size = sizeof(fat_header);
std::size_t arch_size = sizeof(fat_arch);
fat_header header = load_bytes<fat_header>(obj_file, 0); fat_header header = load_bytes<fat_header>(obj_file, 0);
if(should_swap) { if(should_swap) {
swap_fat_header(&header, NX_UnknownByteOrder); swap_fat_header(&header, NX_UnknownByteOrder);
} }
off_t arch_offset = (off_t)header_size; off_t arch_offset = (off_t)header_size;
optional<uintptr_t> text_vmaddr; optional<std::uintptr_t> text_vmaddr;
for(uint32_t i = 0; i < header.nfat_arch; i++) { for(std::uint32_t i = 0; i < header.nfat_arch; i++) {
fat_arch arch = load_bytes<fat_arch>(obj_file, arch_offset); fat_arch arch = load_bytes<fat_arch>(obj_file, arch_offset);
if(should_swap) { if(should_swap) {
swap_fat_arch(&arch, 1, NX_UnknownByteOrder); swap_fat_arch(&arch, 1, NX_UnknownByteOrder);
} }
off_t mach_header_offset = (off_t)arch.offset; off_t mach_header_offset = (off_t)arch.offset;
arch_offset += arch_size; arch_offset += arch_size;
uint32_t magic = load_bytes<uint32_t>(obj_file, mach_header_offset); std::uint32_t magic = load_bytes<std::uint32_t>(obj_file, mach_header_offset);
if(is_magic_64(magic)) { if(is_magic_64(magic)) {
text_vmaddr = macho_get_text_vmaddr_mach<64>( text_vmaddr = macho_get_text_vmaddr_mach<64>(
obj_file, obj_file,
@ -177,13 +181,13 @@ namespace detail {
return 0; return 0;
} }
static uintptr_t macho_get_text_vmaddr(const std::string& obj_path) { static std::uintptr_t macho_get_text_vmaddr(const std::string& obj_path) {
//fprintf(stderr, "--%s--\n", obj_path.c_str()); //std::fprintf(stderr, "--%s--\n", obj_path.c_str());
auto file = raii_wrap(fopen(obj_path.c_str(), "rb"), file_deleter); auto file = raii_wrap(std::fopen(obj_path.c_str(), "rb"), file_deleter);
if(file == nullptr) { if(file == nullptr) {
throw file_error("Unable to read object file " + obj_path); throw file_error("Unable to read object file " + obj_path);
} }
uint32_t magic = load_bytes<uint32_t>(file, 0); std::uint32_t magic = load_bytes<std::uint32_t>(file, 0);
VERIFY(is_mach_o(magic), "File is not Mach-O " + obj_path); VERIFY(is_mach_o(magic), "File is not Mach-O " + obj_path);
bool is_64 = is_magic_64(magic); bool is_64 = is_magic_64(magic);
bool should_swap = should_swap_bytes(magic); bool should_swap = should_swap_bytes(magic);
@ -199,33 +203,33 @@ namespace detail {
} }
inline bool macho_is_fat(const std::string& obj_path) { inline bool macho_is_fat(const std::string& obj_path) {
auto file = raii_wrap(fopen(obj_path.c_str(), "rb"), file_deleter); auto file = raii_wrap(std::fopen(obj_path.c_str(), "rb"), file_deleter);
if(file == nullptr) { if(file == nullptr) {
throw file_error("Unable to read object file " + obj_path); throw file_error("Unable to read object file " + obj_path);
} }
uint32_t magic = load_bytes<uint32_t>(file, 0); std::uint32_t magic = load_bytes<std::uint32_t>(file, 0);
return is_fat_magic(magic); return is_fat_magic(magic);
} }
// returns index of the appropriate mach-o binary in the universal binary // returns index of the appropriate mach-o binary in the universal binary
// TODO: Code duplication with macho_get_text_vmaddr_fat // TODO: Code duplication with macho_get_text_vmaddr_fat
inline unsigned get_fat_macho_index(const std::string& obj_path) { inline unsigned get_fat_macho_index(const std::string& obj_path) {
auto file = raii_wrap(fopen(obj_path.c_str(), "rb"), file_deleter); auto file = raii_wrap(std::fopen(obj_path.c_str(), "rb"), file_deleter);
if(file == nullptr) { if(file == nullptr) {
throw file_error("Unable to read object file " + obj_path); throw file_error("Unable to read object file " + obj_path);
} }
uint32_t magic = load_bytes<uint32_t>(file, 0); std::uint32_t magic = load_bytes<std::uint32_t>(file, 0);
VERIFY(is_fat_magic(magic)); VERIFY(is_fat_magic(magic));
bool should_swap = should_swap_bytes(magic); bool should_swap = should_swap_bytes(magic);
size_t header_size = sizeof(fat_header); std::size_t header_size = sizeof(fat_header);
size_t arch_size = sizeof(fat_arch); std::size_t arch_size = sizeof(fat_arch);
fat_header header = load_bytes<fat_header>(file, 0); fat_header header = load_bytes<fat_header>(file, 0);
if(should_swap) { if(should_swap) {
swap_fat_header(&header, NX_UnknownByteOrder); swap_fat_header(&header, NX_UnknownByteOrder);
} }
off_t arch_offset = (off_t)header_size; off_t arch_offset = (off_t)header_size;
thread_local static struct LP(mach_header)* mhp = _NSGetMachExecuteHeader(); thread_local static struct LP(mach_header)* mhp = _NSGetMachExecuteHeader();
for(uint32_t i = 0; i < header.nfat_arch; i++) { for(std::uint32_t i = 0; i < header.nfat_arch; i++) {
fat_arch arch = load_bytes<fat_arch>(file, arch_offset); fat_arch arch = load_bytes<fat_arch>(file, arch_offset);
if(should_swap) { if(should_swap) {
swap_fat_arch(&arch, 1, NX_UnknownByteOrder); swap_fat_arch(&arch, 1, NX_UnknownByteOrder);

View File

@ -26,10 +26,10 @@ namespace cpptrace {
namespace detail { namespace detail {
#if IS_LINUX || IS_APPLE #if IS_LINUX || IS_APPLE
#if !IS_APPLE #if !IS_APPLE
inline uintptr_t get_module_image_base(const std::string& obj_path) { inline std::uintptr_t get_module_image_base(const std::string& obj_path) {
static std::mutex mutex; static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
static std::unordered_map<std::string, uintptr_t> cache; static std::unordered_map<std::string, std::uintptr_t> cache;
auto it = cache.find(obj_path); auto it = cache.find(obj_path);
if(it == cache.end()) { if(it == cache.end()) {
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
@ -42,13 +42,13 @@ namespace detail {
} }
} }
#else #else
inline uintptr_t get_module_image_base(const std::string& obj_path) { inline std::uintptr_t get_module_image_base(const std::string& obj_path) {
// We have to parse the Mach-O to find the offset of the text section..... // We have to parse the Mach-O to find the offset of the text section.....
// I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for // I don't know how addresses are handled if there is more than one __TEXT load command. I'm assuming for
// now that there is only one, and I'm using only the first section entry within that load command. // now that there is only one, and I'm using only the first section entry within that load command.
static std::mutex mutex; static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
static std::unordered_map<std::string, uintptr_t> cache; static std::unordered_map<std::string, std::uintptr_t> cache;
auto it = cache.find(obj_path); auto it = cache.find(obj_path);
if(it == cache.end()) { if(it == cache.end()) {
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
@ -62,11 +62,11 @@ namespace detail {
} }
#endif #endif
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on // aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
inline std::vector<object_frame> get_frames_object_info(const std::vector<uintptr_t>& addrs) { inline std::vector<object_frame> get_frames_object_info(const std::vector<std::uintptr_t>& addrs) {
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
std::vector<object_frame> frames; std::vector<object_frame> frames;
frames.reserve(addrs.size()); frames.reserve(addrs.size());
for(const uintptr_t addr : addrs) { for(const std::uintptr_t addr : addrs) {
Dl_info info; Dl_info info;
object_frame frame; object_frame frame;
frame.raw_address = addr; frame.raw_address = addr;
@ -75,7 +75,7 @@ namespace detail {
// but we don't really need dli_saddr // but we don't really need dli_saddr
frame.obj_path = info.dli_fname; frame.obj_path = info.dli_fname;
frame.obj_address = addr frame.obj_address = addr
- reinterpret_cast<uintptr_t>(info.dli_fbase) - reinterpret_cast<std::uintptr_t>(info.dli_fbase)
+ get_module_image_base(info.dli_fname); + get_module_image_base(info.dli_fname);
frame.symbol = info.dli_sname ?: ""; frame.symbol = info.dli_sname ?: "";
} }
@ -92,11 +92,11 @@ namespace detail {
if(it == cache.end()) { if(it == cache.end()) {
char path[MAX_PATH]; char path[MAX_PATH];
if(GetModuleFileNameA(handle, path, sizeof(path))) { if(GetModuleFileNameA(handle, path, sizeof(path))) {
///fprintf(stderr, "path: %s base: %p\n", path, handle); ///std::fprintf(stderr, "path: %s base: %p\n", path, handle);
cache.insert(it, {handle, path}); cache.insert(it, {handle, path});
return path; return path;
} else { } else {
fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what()); std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
cache.insert(it, {handle, ""}); cache.insert(it, {handle, ""});
return ""; return "";
} }
@ -105,10 +105,10 @@ namespace detail {
} }
} }
inline uintptr_t get_module_image_base(const std::string& obj_path) { inline std::uintptr_t get_module_image_base(const std::string& obj_path) {
static std::mutex mutex; static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);
static std::unordered_map<std::string, uintptr_t> cache; static std::unordered_map<std::string, std::uintptr_t> cache;
auto it = cache.find(obj_path); auto it = cache.find(obj_path);
if(it == cache.end()) { if(it == cache.end()) {
// arguably it'd be better to release the lock while computing this, but also arguably it's good to not // arguably it'd be better to release the lock while computing this, but also arguably it's good to not
@ -122,11 +122,11 @@ namespace detail {
} }
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on // aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
inline std::vector<object_frame> get_frames_object_info(const std::vector<uintptr_t>& addrs) { inline std::vector<object_frame> get_frames_object_info(const std::vector<std::uintptr_t>& addrs) {
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
std::vector<object_frame> frames; std::vector<object_frame> frames;
frames.reserve(addrs.size()); frames.reserve(addrs.size());
for(const uintptr_t addr : addrs) { for(const std::uintptr_t addr : addrs) {
object_frame frame; object_frame frame;
frame.raw_address = addr; frame.raw_address = addr;
HMODULE handle; HMODULE handle;
@ -138,10 +138,10 @@ namespace detail {
)) { )) {
frame.obj_path = get_module_name(handle); frame.obj_path = get_module_name(handle);
frame.obj_address = addr frame.obj_address = addr
- reinterpret_cast<uintptr_t>(handle) - reinterpret_cast<std::uintptr_t>(handle)
+ get_module_image_base(frame.obj_path); + get_module_image_base(frame.obj_path);
} else { } else {
fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what()); std::fprintf(stderr, "%s\n", std::system_error(GetLastError(), std::system_category()).what());
} }
frames.push_back(frame); frames.push_back(frame);
} }

View File

@ -26,22 +26,22 @@ namespace detail {
} }
} }
inline uintptr_t pe_get_module_image_base(const std::string& obj_path) { inline std::uintptr_t pe_get_module_image_base(const std::string& obj_path) {
// https://drive.google.com/file/d/0B3_wGJkuWLytbnIxY1J5WUs4MEk/view?pli=1&resourcekey=0-n5zZ2UW39xVTH8ZSu6C2aQ // https://drive.google.com/file/d/0B3_wGJkuWLytbnIxY1J5WUs4MEk/view?pli=1&resourcekey=0-n5zZ2UW39xVTH8ZSu6C2aQ
// https://0xrick.github.io/win-internals/pe3/ // https://0xrick.github.io/win-internals/pe3/
// Endianness should always be little for dos and pe headers // Endianness should always be little for dos and pe headers
FILE* file_ptr; std::FILE* file_ptr;
errno_t ret = fopen_s(&file_ptr, obj_path.c_str(), "rb"); errno_t ret = fopen_s(&file_ptr, obj_path.c_str(), "rb");
auto file = raii_wrap(std::move(file_ptr), file_deleter); auto file = raii_wrap(std::move(file_ptr), file_deleter);
if(ret != 0 || file == nullptr) { if(ret != 0 || file == nullptr) {
throw file_error("Unable to read object file " + obj_path); throw file_error("Unable to read object file " + obj_path);
} }
auto magic = load_bytes<std::array<char, 2>>(file, 0); auto magic = load_bytes<std::array<char, 2>>(file, 0);
VERIFY(memcmp(magic.data(), "MZ", 2) == 0, "File is not a PE file " + obj_path); VERIFY(std::memcmp(magic.data(), "MZ", 2) == 0, "File is not a PE file " + obj_path);
DWORD e_lfanew = pe_byteswap_if_needed(load_bytes<DWORD>(file, 0x3c)); // dos header + 0x3c DWORD e_lfanew = pe_byteswap_if_needed(load_bytes<DWORD>(file, 0x3c)); // dos header + 0x3c
DWORD nt_header_offset = e_lfanew; DWORD nt_header_offset = e_lfanew;
auto signature = load_bytes<std::array<char, 4>>(file, nt_header_offset); // nt header + 0 auto signature = load_bytes<std::array<char, 4>>(file, nt_header_offset); // nt header + 0
VERIFY(memcmp(signature.data(), "PE\0\0", 4) == 0, "File is not a PE file " + obj_path); VERIFY(std::memcmp(signature.data(), "PE\0\0", 4) == 0, "File is not a PE file " + obj_path);
WORD size_of_optional_header = pe_byteswap_if_needed( WORD size_of_optional_header = pe_byteswap_if_needed(
load_bytes<WORD>(file, nt_header_offset + 4 + 0x10) // file header + 0x10 load_bytes<WORD>(file, nt_header_offset + 4 + 0x10) // file header + 0x10
); );
@ -56,7 +56,7 @@ namespace detail {
// finally get image base // finally get image base
if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { if(optional_header_magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
// 32 bit // 32 bit
return to<uintptr_t>( return to<std::uintptr_t>(
pe_byteswap_if_needed( pe_byteswap_if_needed(
load_bytes<DWORD>(file, nt_header_offset + 0x18 + 0x1c) // optional header + 0x1c load_bytes<DWORD>(file, nt_header_offset + 0x18 + 0x1c) // optional header + 0x1c
) )
@ -64,7 +64,7 @@ namespace detail {
} else { } else {
// 64 bit // 64 bit
// I get an "error: 'QWORD' was not declared in this scope" for some reason when using QWORD // I get an "error: 'QWORD' was not declared in this scope" for some reason when using QWORD
return to<uintptr_t>( return to<std::uintptr_t>(
pe_byteswap_if_needed( pe_byteswap_if_needed(
load_bytes<unsigned __int64>(file, nt_header_offset + 0x18 + 0x18) // optional header + 0x18 load_bytes<unsigned __int64>(file, nt_header_offset + 0x18 + 0x18) // optional header + 0x18
) )

View File

@ -38,7 +38,7 @@ namespace detail {
#endif #endif
} }
inline int fileno(FILE* stream) { inline int fileno(std::FILE* stream) {
#if IS_WINDOWS #if IS_WINDOWS
return _fileno(stream); return _fileno(stream);
#else #else
@ -48,8 +48,8 @@ namespace detail {
inline std::vector<std::string> split(const std::string& str, const std::string& delims) { inline std::vector<std::string> split(const std::string& str, const std::string& delims) {
std::vector<std::string> vec; std::vector<std::string> vec;
size_t old_pos = 0; std::size_t old_pos = 0;
size_t pos = 0; std::size_t pos = 0;
while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) { while((pos = str.find_first_of(delims, old_pos)) != std::string::npos) {
vec.emplace_back(str.substr(old_pos, pos - old_pos)); vec.emplace_back(str.substr(old_pos, pos - old_pos));
old_pos = pos + 1; old_pos = pos + 1;
@ -79,26 +79,26 @@ namespace detail {
if(str.empty()) { if(str.empty()) {
return ""; return "";
} }
const size_t left = str.find_first_not_of(whitespace); const std::size_t left = str.find_first_not_of(whitespace);
const size_t right = str.find_last_not_of(whitespace) + 1; const std::size_t right = str.find_last_not_of(whitespace) + 1;
return str.substr(left, right - left); return str.substr(left, right - left);
} }
inline std::string to_hex(uintptr_t addr) { inline std::string to_hex(std::uintptr_t addr) {
std::stringstream sstream; std::stringstream sstream;
sstream<<std::hex<<addr; sstream<<std::hex<<addr;
return std::move(sstream).str(); return std::move(sstream).str();
} }
inline bool is_little_endian() { inline bool is_little_endian() {
uint16_t num = 0x1; std::uint16_t num = 0x1;
const auto* ptr = (uint8_t*)&num; const auto* ptr = (std::uint8_t*)&num;
return ptr[0] == 1; return ptr[0] == 1;
} }
// Modified from // Modified from
// https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c // https://stackoverflow.com/questions/105252/how-do-i-convert-between-big-endian-and-little-endian-values-in-c
template<typename T, size_t N> template<typename T, std::size_t N>
struct byte_swapper; struct byte_swapper;
template<typename T> template<typename T>
@ -171,10 +171,10 @@ namespace detail {
// TODO: Re-evaluate use of off_t // TODO: Re-evaluate use of off_t
template<typename T, typename std::enable_if<std::is_trivial<T>::value, int>::type = 0> template<typename T, typename std::enable_if<std::is_trivial<T>::value, int>::type = 0>
T load_bytes(FILE* obj_file, off_t offset) { T load_bytes(std::FILE* obj_file, off_t offset) {
T object; T object;
VERIFY(fseek(obj_file, offset, SEEK_SET) == 0, "fseek error"); VERIFY(std::fseek(obj_file, offset, SEEK_SET) == 0, "fseek error");
VERIFY(fread(&object, sizeof(T), 1, obj_file) == 1, "fread error"); VERIFY(std::fread(&object, sizeof(T), 1, obj_file) == 1, "fread error");
return object; return object;
} }
@ -352,8 +352,8 @@ namespace detail {
return static_cast<unsigned long long>(t); return static_cast<unsigned long long>(t);
} }
template<typename T> template<typename T>
uintptr_t to_uintptr(T t) { std::uintptr_t to_uintptr(T t) {
return static_cast<uintptr_t>(t); return static_cast<std::uintptr_t>(t);
} }
// A way to cast to U without "warning: useless cast to type" // A way to cast to U without "warning: useless cast to type"
@ -424,7 +424,7 @@ namespace detail {
return raii_wrapper<typename std::remove_reference<T>::type, D>(obj, deleter); return raii_wrapper<typename std::remove_reference<T>::type, D>(obj, deleter);
} }
inline void file_deleter(FILE* ptr) { inline void file_deleter(std::FILE* ptr) {
fclose(ptr); fclose(ptr);
} }
} }

View File

@ -21,7 +21,7 @@ namespace detail {
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
namespace libbacktrace { namespace libbacktrace {
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames);
} }
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
@ -31,7 +31,7 @@ namespace detail {
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
namespace libdl { namespace libdl {
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames);
} }
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
@ -41,18 +41,18 @@ namespace detail {
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
namespace dbghelp { namespace dbghelp {
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames);
} }
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
namespace nothing { namespace nothing {
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames);
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames);
} }
#endif #endif
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames);
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames);
} }
} }

View File

@ -56,7 +56,7 @@ namespace detail {
|| defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \ || defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \
|| defined(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE) || defined(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
// actually need to go backwards to a void* // actually need to go backwards to a void*
std::vector<uintptr_t> raw_frames(frames.size()); std::vector<std::uintptr_t> raw_frames(frames.size());
for(std::size_t i = 0; i < frames.size(); i++) { for(std::size_t i = 0; i < frames.size(); i++) {
raw_frames[i] = frames[i].raw_address; raw_frames[i] = frames[i].raw_address;
} }
@ -82,7 +82,7 @@ namespace detail {
return trace; return trace;
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames) {
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) \ #if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) \
|| defined(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE) || defined(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
auto dlframes = get_frames_object_info(frames); auto dlframes = get_frames_object_info(frames);

View File

@ -127,7 +127,7 @@ namespace addr2line {
std::string output; std::string output;
constexpr int buffer_size = 4096; constexpr int buffer_size = 4096;
char buffer[buffer_size]; char buffer[buffer_size];
size_t count = 0; std::size_t count = 0;
while((count = read(output_pipe.read_end, buffer, buffer_size)) > 0) { while((count = read(output_pipe.read_end, buffer, buffer_size)) > 0) {
output.insert(output.end(), buffer, buffer + count); output.insert(output.end(), buffer, buffer + count);
} }
@ -145,12 +145,12 @@ namespace addr2line {
// TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later.
checked = true; checked = true;
#ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
FILE* p = popen("addr2line --version", "r"); std::FILE* p = popen("addr2line --version", "r");
#else #else
#ifndef CPPTRACE_ADDR2LINE_PATH #ifndef CPPTRACE_ADDR2LINE_PATH
#error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not"
#endif #endif
FILE* p = popen(CPPTRACE_ADDR2LINE_PATH " --version", "r"); std::FILE* p = popen(CPPTRACE_ADDR2LINE_PATH " --version", "r");
#endif #endif
if(p) { if(p) {
has_addr2line = pclose(p) == 0; has_addr2line = pclose(p) == 0;
@ -163,12 +163,12 @@ namespace addr2line {
// TODO: Popen is a hack. Implement properly with CreateProcess and pipes later. // TODO: Popen is a hack. Implement properly with CreateProcess and pipes later.
///fprintf(stderr, ("addr2line -e " + executable + " -fCp " + addresses + "\n").c_str()); ///fprintf(stderr, ("addr2line -e " + executable + " -fCp " + addresses + "\n").c_str());
#ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH #ifdef CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH
FILE* p = popen(("addr2line -e \"" + executable + "\" -fCp " + addresses).c_str(), "r"); std::FILE* p = popen(("addr2line -e \"" + executable + "\" -fCp " + addresses).c_str(), "r");
#else #else
#ifndef CPPTRACE_ADDR2LINE_PATH #ifndef CPPTRACE_ADDR2LINE_PATH
#error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not" #error "CPPTRACE_ADDR2LINE_PATH must be defined if CPPTRACE_ADDR2LINE_SEARCH_SYSTEM_PATH is not"
#endif #endif
FILE* p = popen( std::FILE* p = popen(
(CPPTRACE_ADDR2LINE_PATH " -e \"" + executable + "\" -fCp " + addresses).c_str(), (CPPTRACE_ADDR2LINE_PATH " -e \"" + executable + "\" -fCp " + addresses).c_str(),
"r" "r"
); );
@ -176,8 +176,8 @@ namespace addr2line {
std::string output; std::string output;
constexpr int buffer_size = 4096; constexpr int buffer_size = 4096;
char buffer[buffer_size]; char buffer[buffer_size];
size_t count = 0; std::size_t count = 0;
while((count = fread(buffer, 1, buffer_size, p)) > 0) { while((count = std::fread(buffer, 1, buffer_size, p)) > 0) {
output.insert(output.end(), buffer, buffer + count); output.insert(output.end(), buffer, buffer + count);
} }
pclose(p); pclose(p);
@ -186,7 +186,7 @@ namespace addr2line {
} }
#endif #endif
void update_trace(const std::string& line, size_t entry_index, const collated_vec& entries_vec) { void update_trace(const std::string& line, std::size_t entry_index, const collated_vec& entries_vec) {
#if !IS_APPLE #if !IS_APPLE
// Result will be of the form "<symbol> at path:line" // Result will be of the form "<symbol> at path:line"
// The path may be ?? if addr2line cannot resolve, line may be ? // The path may be ?? if addr2line cannot resolve, line may be ?
@ -268,7 +268,7 @@ namespace addr2line {
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
// TODO: Refactor better // TODO: Refactor better
std::vector<stacktrace_frame> trace(frames.size(), null_frame); std::vector<stacktrace_frame> trace(frames.size(), null_frame);
for(size_t i = 0; i < frames.size(); i++) { for(std::size_t i = 0; i < frames.size(); i++) {
trace[i].address = frames[i].raw_address; trace[i].address = frames[i].raw_address;
// Set what is known for now, and resolutions from addr2line should overwrite // Set what is known for now, and resolutions from addr2line should overwrite
trace[i].filename = frames[i].obj_path; trace[i].filename = frames[i].obj_path;
@ -298,7 +298,7 @@ namespace addr2line {
} }
auto output = split(trim(resolve_addresses(address_input, object_name)), "\n"); auto output = split(trim(resolve_addresses(address_input, object_name)), "\n");
VERIFY(output.size() == entries_vec.size()); VERIFY(output.size() == entries_vec.size());
for(size_t i = 0; i < output.size(); i++) { for(std::size_t i = 0; i < output.size(); i++) {
update_trace(output[i], i, entries_vec); update_trace(output[i], i, entries_vec);
} }
} catch(...) { // NOSONAR } catch(...) { // NOSONAR

View File

@ -234,7 +234,7 @@ namespace dbghelp {
return {return_type.base, "()" + return_type.extent}; return {return_type.base, "()" + return_type.extent};
} else { } else {
// alignment should be fine // alignment should be fine
size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) + std::size_t sz = sizeof(TI_FINDCHILDREN_PARAMS) +
(n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]); (n_children) * sizeof(TI_FINDCHILDREN_PARAMS::ChildId[0]);
TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz]; TI_FINDCHILDREN_PARAMS* children = (TI_FINDCHILDREN_PARAMS*) new char[sz];
children->Start = 0; children->Start = 0;
@ -325,7 +325,7 @@ namespace dbghelp {
std::recursive_mutex dbghelp_lock; std::recursive_mutex dbghelp_lock;
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
stacktrace_frame resolve_frame(HANDLE proc, uintptr_t addr) { stacktrace_frame resolve_frame(HANDLE proc, std::uintptr_t addr) {
const std::lock_guard<std::recursive_mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe const std::lock_guard<std::recursive_mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer; SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer;
@ -343,7 +343,7 @@ namespace dbghelp {
// function fails but GetLastError returns ERROR_SUCCESS." // function fails but GetLastError returns ERROR_SUCCESS."
// This is the stupidest fucking api I've ever worked with. // This is the stupidest fucking api I've ever worked with.
if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) { if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) {
fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n"); std::fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n");
return { return {
addr, addr,
static_cast<std::uint_least32_t>(line.LineNumber), static_cast<std::uint_least32_t>(line.LineNumber),
@ -390,7 +390,7 @@ namespace dbghelp {
} }
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames) {
const std::lock_guard<std::recursive_mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe const std::lock_guard<std::recursive_mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe
std::vector<stacktrace_frame> trace; std::vector<stacktrace_frame> trace;
trace.reserve(frames.size()); trace.reserve(frames.size());

View File

@ -12,7 +12,7 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
namespace libdl { namespace libdl {
stacktrace_frame resolve_frame(const uintptr_t addr) { stacktrace_frame resolve_frame(const std::uintptr_t addr) {
Dl_info info; Dl_info info;
if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread-safe if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread-safe
return { return {
@ -33,7 +33,7 @@ namespace libdl {
} }
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames) {
std::vector<stacktrace_frame> trace; std::vector<stacktrace_frame> trace;
trace.reserve(frames.size()); trace.reserve(frames.size());
for(const auto frame : frames) { for(const auto frame : frames) {

View File

@ -20,7 +20,7 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
namespace libbacktrace { namespace libbacktrace {
int full_callback(void* data, uintptr_t address, const char* file, int line, const char* symbol) { int full_callback(void* data, std::uintptr_t address, const char* file, int line, const char* symbol) {
stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data); stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data);
frame.address = address; frame.address = address;
frame.line = line; frame.line = line;
@ -29,7 +29,7 @@ namespace libbacktrace {
return 0; return 0;
} }
void syminfo_callback(void* data, uintptr_t address, const char* symbol, uintptr_t, uintptr_t) { void syminfo_callback(void* data, std::uintptr_t address, const char* symbol, std::uintptr_t, std::uintptr_t) {
stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data); stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data);
frame.address = address; frame.address = address;
frame.line = 0; frame.line = 0;
@ -55,7 +55,7 @@ namespace libbacktrace {
} }
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
stacktrace_frame resolve_frame(const uintptr_t addr) { stacktrace_frame resolve_frame(const std::uintptr_t addr) {
try { try {
stacktrace_frame frame; stacktrace_frame frame;
frame.column = UINT_LEAST32_MAX; frame.column = UINT_LEAST32_MAX;
@ -85,7 +85,7 @@ namespace libbacktrace {
} }
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames) {
std::vector<stacktrace_frame> trace; std::vector<stacktrace_frame> trace;
trace.reserve(frames.size()); trace.reserve(frames.size());
for(const auto frame : frames) { for(const auto frame : frames) {

View File

@ -233,7 +233,7 @@ namespace libdwarf {
); );
if(ret == DW_DLV_NO_ENTRY) { if(ret == DW_DLV_NO_ENTRY) {
if(dump_dwarf) { if(dump_dwarf) {
fprintf(stderr, "End walk_dbg\n"); std::fprintf(stderr, "End walk_dbg\n");
} }
return; return;
} }
@ -253,7 +253,7 @@ namespace libdwarf {
} }
} }
if(dump_dwarf) { if(dump_dwarf) {
fprintf(stderr, "End walk_compilation_units\n"); std::fprintf(stderr, "End walk_compilation_units\n");
} }
} }
@ -297,7 +297,7 @@ namespace libdwarf {
die, die,
[this, pc, dwversion, &frame, &found] (const die_object& die) { [this, pc, dwversion, &frame, &found] (const die_object& die) {
if(dump_dwarf) { if(dump_dwarf) {
fprintf( std::fprintf(
stderr, stderr,
"-------------> %08llx %s %s\n", "-------------> %08llx %s %s\n",
to_ull(die.get_global_offset()), to_ull(die.get_global_offset()),
@ -307,11 +307,11 @@ namespace libdwarf {
} }
if(!(die.get_tag() == DW_TAG_namespace || die.pc_in_die(dwversion, pc))) { if(!(die.get_tag() == DW_TAG_namespace || die.pc_in_die(dwversion, pc))) {
if(dump_dwarf) { if(dump_dwarf) {
fprintf(stderr, "pc not in die\n"); std::fprintf(stderr, "pc not in die\n");
} }
} else { } else {
if(trace_dwarf) { if(trace_dwarf) {
fprintf( std::fprintf(
stderr, stderr,
"%s %08llx %s\n", "%s %08llx %s\n",
die.get_tag() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die", die.get_tag() == DW_TAG_namespace ? "pc maybe in die (namespace)" : "pc in die",
@ -332,7 +332,7 @@ namespace libdwarf {
} }
} else { } else {
if(dump_dwarf) { if(dump_dwarf) {
fprintf(stderr, "(no child)\n"); std::fprintf(stderr, "(no child)\n");
} }
} }
} }
@ -340,7 +340,7 @@ namespace libdwarf {
} }
); );
if(dump_dwarf) { if(dump_dwarf) {
fprintf(stderr, "End walk_die_list\n"); std::fprintf(stderr, "End walk_die_list\n");
} }
return found; return found;
} }
@ -384,7 +384,7 @@ namespace libdwarf {
} }
); );
if(dump_dwarf) { if(dump_dwarf) {
fprintf(stderr, "End walk_die_list\n"); std::fprintf(stderr, "End walk_die_list\n");
} }
} }
@ -506,7 +506,7 @@ namespace libdwarf {
if(found_line) { if(found_line) {
Dwarf_Unsigned line_number = 0; Dwarf_Unsigned line_number = 0;
VERIFY(wrap(dwarf_lineno, found_line, &line_number) == DW_DLV_OK); VERIFY(wrap(dwarf_lineno, found_line, &line_number) == DW_DLV_OK);
frame.line = static_cast<uint_least32_t>(line_number); frame.line = static_cast<std::uint_least32_t>(line_number);
char* filename = nullptr; char* filename = nullptr;
VERIFY(wrap(dwarf_linesrc, found_line, &filename) == DW_DLV_OK); VERIFY(wrap(dwarf_linesrc, found_line, &filename) == DW_DLV_OK);
auto wrapper = raii_wrap( auto wrapper = raii_wrap(
@ -534,8 +534,8 @@ namespace libdwarf {
stacktrace_frame& frame stacktrace_frame& frame
) { ) {
if(dump_dwarf) { if(dump_dwarf) {
fprintf(stderr, "%s\n", obj_path.c_str()); std::fprintf(stderr, "%s\n", obj_path.c_str());
fprintf(stderr, "%llx\n", to_ull(pc)); std::fprintf(stderr, "%llx\n", to_ull(pc));
} }
// Check for .debug_aranges for fast lookup // Check for .debug_aranges for fast lookup
if(aranges) { if(aranges) {
@ -553,7 +553,7 @@ namespace libdwarf {
Dwarf_Half dwversion = 0; Dwarf_Half dwversion = 0;
VERIFY(dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size) == DW_DLV_OK); VERIFY(dwarf_get_version_of_die(cu_die.get(), &dwversion, &offset_size) == DW_DLV_OK);
if(trace_dwarf) { if(trace_dwarf) {
fprintf(stderr, "Found CU in aranges\n"); std::fprintf(stderr, "Found CU in aranges\n");
cu_die.print(); cu_die.print();
} }
retrieve_line_info(cu_die, pc, frame); // no offset for line info retrieve_line_info(cu_die, pc, frame); // no offset for line info
@ -570,11 +570,11 @@ namespace libdwarf {
//cu_die.print(); //cu_die.print();
//fprintf(stderr, " %llx, %llx\n", p.first, p.second); //fprintf(stderr, " %llx, %llx\n", p.first, p.second);
if(trace_dwarf) { if(trace_dwarf) {
fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str()); std::fprintf(stderr, "CU: %d %s\n", dwversion, cu_die.get_name().c_str());
} }
if(cu_die.pc_in_die(dwversion, pc)) { if(cu_die.pc_in_die(dwversion, pc)) {
if(trace_dwarf) { if(trace_dwarf) {
fprintf( std::fprintf(
stderr, stderr,
"pc in die %08llx %s (now searching for %08llx)\n", "pc in die %08llx %s (now searching for %08llx)\n",
to_ull(cu_die.get_global_offset()), to_ull(cu_die.get_global_offset()),
@ -624,7 +624,7 @@ namespace libdwarf {
frame.symbol = frame_info.symbol; frame.symbol = frame_info.symbol;
frame.address = frame_info.raw_address; frame.address = frame_info.raw_address;
if(trace_dwarf) { if(trace_dwarf) {
fprintf( std::fprintf(
stderr, stderr,
"Starting resolution for %s %08llx %s\n", "Starting resolution for %s %08llx %s\n",
obj_path.c_str(), obj_path.c_str(),

View File

@ -8,7 +8,7 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
namespace nothing { namespace nothing {
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<std::uintptr_t>& frames) {
return std::vector<stacktrace_frame>(frames.size(), null_frame); return std::vector<stacktrace_frame>(frames.size(), null_frame);
} }

View File

@ -10,12 +10,12 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
#ifdef CPPTRACE_HARD_MAX_FRAMES #ifdef CPPTRACE_HARD_MAX_FRAMES
constexpr size_t hard_max_frames = CPPTRACE_HARD_MAX_FRAMES; constexpr std::size_t hard_max_frames = CPPTRACE_HARD_MAX_FRAMES;
#else #else
constexpr size_t hard_max_frames = 100; constexpr std::size_t hard_max_frames = 100;
#endif #endif
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<uintptr_t> capture_frames(size_t skip, size_t max_depth); std::vector<std::uintptr_t> capture_frames(std::size_t skip, std::size_t max_depth);
} }
} }

View File

@ -26,7 +26,7 @@ namespace detail {
#pragma warning(disable: 4740) // warning C4740: flow in or out of inline asm code suppresses global optimization #pragma warning(disable: 4740) // warning C4740: flow in or out of inline asm code suppresses global optimization
#endif #endif
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<uintptr_t> capture_frames(size_t skip, size_t max_depth) { std::vector<std::uintptr_t> capture_frames(std::size_t skip, std::size_t max_depth) {
skip++; skip++;
// https://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/ // https://jpassing.com/2008/03/12/walking-the-stack-of-the-current-thread/
@ -94,7 +94,7 @@ namespace detail {
#error "Cpptrace: StackWalk64 not supported for this platform yet" #error "Cpptrace: StackWalk64 not supported for this platform yet"
#endif #endif
std::vector<uintptr_t> trace; std::vector<std::uintptr_t> trace;
// Dbghelp is is single-threaded, so acquire a lock. // Dbghelp is is single-threaded, so acquire a lock.
static std::mutex mutex; static std::mutex mutex;

View File

@ -13,17 +13,17 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<uintptr_t> capture_frames(size_t skip, size_t max_depth) { std::vector<std::uintptr_t> capture_frames(std::size_t skip, std::size_t max_depth) {
skip++; skip++;
std::vector<void*> addrs(std::min(hard_max_frames, skip + max_depth), nullptr); std::vector<void*> addrs(std::min(hard_max_frames, skip + max_depth), nullptr);
const int n_frames = backtrace(addrs.data(), static_cast<int>(addrs.size())); // thread safe const int n_frames = backtrace(addrs.data(), static_cast<int>(addrs.size())); // thread safe
// I hate the copy here but it's the only way that isn't UB // I hate the copy here but it's the only way that isn't UB
std::vector<uintptr_t> frames(n_frames - skip, 0); std::vector<std::uintptr_t> frames(n_frames - skip, 0);
for(int i = skip; i < n_frames; i++) { for(int i = skip; i < n_frames; i++) {
// On x86/x64/arm, as far as I can tell, the frame return address is always one after the call // On x86/x64/arm, as far as I can tell, the frame return address is always one after the call
// So we just decrement to get the pc back inside the `call` / `bl` // So we just decrement to get the pc back inside the `call` / `bl`
// This is done with _Unwind too but conditionally based on info from _Unwind_GetIPInfo. // This is done with _Unwind too but conditionally based on info from _Unwind_GetIPInfo.
frames[i - skip] = reinterpret_cast<uintptr_t>(addrs[i]) - 1; frames[i - skip] = reinterpret_cast<std::uintptr_t>(addrs[i]) - 1;
} }
return frames; return frames;
} }

View File

@ -7,7 +7,7 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
std::vector<uintptr_t> capture_frames(size_t, size_t) { std::vector<std::uintptr_t> capture_frames(std::size_t, std::size_t) {
return {}; return {};
} }
} }

View File

@ -17,14 +17,14 @@ namespace detail {
struct unwind_state { struct unwind_state {
std::size_t skip; std::size_t skip;
std::size_t count; std::size_t count;
std::vector<uintptr_t>& vec; std::vector<std::uintptr_t>& vec;
}; };
_Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) { _Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) {
unwind_state& state = *static_cast<unwind_state*>(arg); unwind_state& state = *static_cast<unwind_state*>(arg);
if(state.skip) { if(state.skip) {
state.skip--; state.skip--;
if(_Unwind_GetIP(context) == uintptr_t(0)) { if(_Unwind_GetIP(context) == std::uintptr_t(0)) {
return _URC_END_OF_STACK; return _URC_END_OF_STACK;
} else { } else {
return _URC_NO_REASON; return _URC_NO_REASON;
@ -36,11 +36,11 @@ namespace detail {
"Somehow cpptrace::detail::unwind_callback is overflowing a vector" "Somehow cpptrace::detail::unwind_callback is overflowing a vector"
); );
int is_before_instruction = 0; int is_before_instruction = 0;
uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction); std::uintptr_t ip = _Unwind_GetIPInfo(context, &is_before_instruction);
if(!is_before_instruction && ip != uintptr_t(0)) { if(!is_before_instruction && ip != std::uintptr_t(0)) {
ip--; ip--;
} }
if (ip == uintptr_t(0)) { if (ip == std::uintptr_t(0)) {
return _URC_END_OF_STACK; return _URC_END_OF_STACK;
} else { } else {
// TODO: push_back?... // TODO: push_back?...
@ -54,8 +54,8 @@ namespace detail {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<uintptr_t> capture_frames(size_t skip, size_t max_depth) { std::vector<std::uintptr_t> capture_frames(std::size_t skip, std::size_t max_depth) {
std::vector<uintptr_t> frames(std::min(hard_max_frames, max_depth), 0); std::vector<std::uintptr_t> frames(std::min(hard_max_frames, max_depth), 0);
unwind_state state{skip + 1, 0, frames}; unwind_state state{skip + 1, 0, frames};
_Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe _Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe
frames.resize(state.count); frames.resize(state.count);

View File

@ -19,7 +19,7 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<uintptr_t> capture_frames(size_t skip, size_t max_depth) { std::vector<std::uintptr_t> capture_frames(std::size_t skip, std::size_t max_depth) {
std::vector<void*> addrs(std::min(hard_max_frames, max_depth), nullptr); std::vector<void*> addrs(std::min(hard_max_frames, max_depth), nullptr);
int n_frames = CaptureStackBackTrace( int n_frames = CaptureStackBackTrace(
static_cast<ULONG>(skip + 1), static_cast<ULONG>(skip + 1),
@ -28,12 +28,12 @@ namespace detail {
NULL NULL
); );
// I hate the copy here but it's the only way that isn't UB // I hate the copy here but it's the only way that isn't UB
std::vector<uintptr_t> frames(n_frames, 0); std::vector<std::uintptr_t> frames(n_frames, 0);
for(std::size_t i = 0; i < n_frames; i++) { for(std::size_t i = 0; i < n_frames; i++) {
// On x86/x64/arm, as far as I can tell, the frame return address is always one after the call // On x86/x64/arm, as far as I can tell, the frame return address is always one after the call
// So we just decrement to get the pc back inside the `call` / `bl` // So we just decrement to get the pc back inside the `call` / `bl`
// This is done with _Unwind too but conditionally based on info from _Unwind_GetIPInfo. // This is done with _Unwind too but conditionally based on info from _Unwind_GetIPInfo.
frames[i] = reinterpret_cast<uintptr_t>(addrs[i]) - 1; frames[i] = reinterpret_cast<std::uintptr_t>(addrs[i]) - 1;
} }
return frames; return frames;
} }