Thread safety

This commit is contained in:
Jeremy 2023-07-23 17:20:27 -04:00
parent 116904ed20
commit b246613045
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
9 changed files with 56 additions and 19 deletions

View File

@ -11,6 +11,7 @@ namespace cpptrace {
namespace detail {
std::string demangle(const std::string& name) {
int status;
// presumably thread-safe
char* demangled = abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status);
if(demangled) {
std::string str = demangled;

View File

@ -7,6 +7,7 @@
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <mutex>
#include <vector>
#ifdef CPPTRACE_BACKTRACE_PATH
@ -52,7 +53,10 @@ namespace cpptrace {
fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum);
}
std::mutex state_mutex;
backtrace_state* get_backtrace_state() {
const std::lock_guard<std::mutex> lock(state_mutex);
// backtrace_create_state must be called only one time per program
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static backtrace_state* state = nullptr;

View File

@ -1,23 +1,39 @@
#ifndef PROGRAM_NAME_HPP
#define PROGRAM_NAME_HPP
#include <mutex>
#include <string>
namespace cpptrace {
namespace detail {
inline std::mutex& get_program_name_mutex() { // stupid workaround for an inline variable
static std::mutex mutex;
return mutex;
}
}
}
#if defined(_WIN32)
#include <windows.h>
namespace cpptrace {
namespace detail {
inline std::string program_name() {
// TODO: Cache this better
const std::lock_guard<std::mutex> lock(get_program_name_mutex());
static std::string name;
static bool did_init = false;
static bool valid = false;
if(!did_init) {
did_init = true;
char buffer[MAX_PATH + 1];
int res = GetModuleFileNameA(nullptr, buffer, MAX_PATH);
if(res) {
return buffer;
} else {
return "";
name = buffer;
valid = true;
}
}
return valid && !name.empty() ? name.c_str() : nullptr;
}
}
}
@ -30,16 +46,20 @@ namespace cpptrace {
namespace cpptrace {
namespace detail {
inline const char* program_name() {
// TODO: Cache this better
const std::lock_guard<std::mutex> lock(get_program_name_mutex());
static std::string name;
if (!name.empty()) {
static bool did_init = false;
static bool valid = false;
if(!did_init) {
did_init = true;
std::uint32_t bufferSize = PATH_MAX + 1;
char buffer[bufferSize];
if (_NSGetExecutablePath(buffer, &bufferSize) != 0)
return nullptr;
if(_NSGetExecutablePath(buffer, &bufferSize) == 0) {
name.assign(buffer, bufferSize);
valid = true;
}
return name.c_str();
}
return valid && !name.empty() ? name.c_str() : nullptr;
}
}
}
@ -53,6 +73,7 @@ namespace cpptrace {
namespace cpptrace {
namespace detail {
inline const char* program_name() {
const std::lock_guard<std::mutex> lock(get_program_name_mutex());
static std::string name;
static bool did_init = false;
static bool valid = false;
@ -67,7 +88,7 @@ namespace cpptrace {
name = buffer;
valid = true;
}
return valid ? name.c_str() : nullptr;
return valid && !name.empty() ? name.c_str() : nullptr;
}
}
}

View File

@ -44,7 +44,7 @@ namespace cpptrace {
Dl_info info;
dlframe frame;
frame.raw_address = reinterpret_cast<uintptr_t>(addr);
if(dladdr(addr, &info)) {
if(dladdr(addr, &info)) { // thread safe
// dli_sname and dli_saddr are only present with -rdynamic, sname will be included
// but we don't really need dli_saddr
frame.obj_path = info.dli_fname;
@ -153,13 +153,15 @@ namespace cpptrace {
dlframe frame;
frame.raw_address = reinterpret_cast<uintptr_t>(addr);
HMODULE handle;
// Multithread safe as long as another thread doesn't come along and free the module
if(GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
static_cast<const char*>(addr),
&handle
)) {
fflush(stderr);
fflush(stderr); // TODO: remove
char path[MAX_PATH];
// TODO: Memoize
if(GetModuleFileNameA(handle, path, sizeof(path))) {
///fprintf(stderr, "path: %s base: %p\n", path, handle);
frame.obj_path = path;
@ -177,6 +179,7 @@ namespace cpptrace {
}
bool has_addr2line() {
// TODO: Memoize
// TODO: Popen is a hack. Implement properly with CreateProcess and pipes later.
FILE* p = popen("addr2line --version", "r");
return pclose(p) == 0;

View File

@ -299,6 +299,8 @@ namespace cpptrace {
return true;
}
std::mutex dbghelp_lock;
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
struct symbolizer::impl {
bool good = true;
@ -320,6 +322,7 @@ namespace cpptrace {
}
stacktrace_frame resolve_frame(void* addr) {
const std::lock_guard<std::mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

View File

@ -15,7 +15,7 @@ namespace cpptrace {
// NOLINTNEXTLINE(readability-convert-member-functions-to-static)
stacktrace_frame resolve_frame(const void* addr) {
Dl_info info;
if(dladdr(addr, &info)) {
if(dladdr(addr, &info)) { // thread-safe
return {
reinterpret_cast<uintptr_t>(addr),
0,

View File

@ -7,6 +7,7 @@
#include <cstdint>
#include <cstdio>
#include <memory>
#include <mutex>
#include <vector>
#ifdef CPPTRACE_BACKTRACE_PATH
@ -41,7 +42,10 @@ namespace cpptrace {
fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum);
}
std::mutex state_mutex;
backtrace_state* get_backtrace_state() {
const std::lock_guard<std::mutex> lock(state_mutex);
// backtrace_create_state must be called only one time per program
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static backtrace_state* state = nullptr;

View File

@ -14,7 +14,7 @@ namespace cpptrace {
CPPTRACE_FORCE_NO_INLINE
std::vector<void*> capture_frames(size_t skip) {
std::vector<void*> frames(hard_max_frames + skip, nullptr);
const int n_frames = backtrace(frames.data(), int(hard_max_frames + skip));
const int n_frames = backtrace(frames.data(), int(hard_max_frames + skip)); // thread safe
frames.resize(n_frames);
frames.erase(frames.begin(), frames.begin() + ptrdiff_t(std::min(skip + 1, frames.size())));
frames.shrink_to_fit();

View File

@ -38,6 +38,7 @@ namespace cpptrace {
if (ip == uintptr_t(0) || state.count == state.vec.size()) {
return _URC_END_OF_STACK;
} else {
// TODO: push_back?...
state.vec[state.count++] = (void*)ip;
return _URC_NO_REASON;
}
@ -47,7 +48,7 @@ namespace cpptrace {
std::vector<void*> capture_frames(size_t skip) {
std::vector<void*> frames(hard_max_frames, nullptr);
unwind_state state{skip + 1, 0, frames};
_Unwind_Backtrace(unwind_callback, &state);
_Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe
frames.resize(state.count);
frames.shrink_to_fit();
return frames;