More std::
This commit is contained in:
parent
e5ed083996
commit
ae5d2392fe
@ -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:
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "demangle.hpp"
|
#include "demangle.hpp"
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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()),
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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*)#
|
const auto* ptr = (std::uint8_t*)#
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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());
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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(),
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user