Add cache mode config

This commit is contained in:
Jeremy 2023-09-29 12:50:47 -04:00
parent 93dde93802
commit d84a3167d9
No known key found for this signature in database
GPG Key ID: 3E11861CB34E158C
6 changed files with 110 additions and 41 deletions

View File

@ -224,10 +224,27 @@ The library makes an attempt to fail silently and continue during trace generati
`cpptrace::absorb_trace_exceptions` can be used to configure whether these exceptions are absorbed silently internally `cpptrace::absorb_trace_exceptions` can be used to configure whether these exceptions are absorbed silently internally
or wether they're rethrown to the caller. or wether they're rethrown to the caller.
`cpptrace::experimental::set_cache_mode` can be used to control time-memory tradeoffs within the library. By default
speed is prioritized. If using this function, set the cache mode at the very start of your program before any traces are
performed.
```cpp ```cpp
namespace cpptrace { namespace cpptrace {
std::string demangle(const std::string& name); std::string demangle(const std::string& name);
void absorb_trace_exceptions(bool absorb); void absorb_trace_exceptions(bool absorb);
enum class cache_mode {
// Only minimal lookup tables
prioritize_memory,
// Build lookup tables but don't keep them around between trace calls
hybrid,
// Build lookup tables as needed
prioritize_speed
};
namespace experimental {
void set_cache_mode(cache_mode mode);
}
} }
``` ```

View File

@ -130,8 +130,22 @@ namespace cpptrace {
CPPTRACE_API std::string demangle(const std::string& name); CPPTRACE_API std::string demangle(const std::string& name);
CPPTRACE_API void absorb_trace_exceptions(bool absorb); CPPTRACE_API void absorb_trace_exceptions(bool absorb);
enum class cache_mode {
// Only minimal lookup tables
prioritize_memory,
// Build lookup tables but don't keep them around between trace calls
hybrid,
// Build lookup tables as needed
prioritize_speed
};
namespace experimental {
CPPTRACE_API void set_cache_mode(cache_mode mode);
}
namespace detail { namespace detail {
CPPTRACE_API bool should_absorb_trace_exceptions(); CPPTRACE_API bool should_absorb_trace_exceptions();
CPPTRACE_API enum cache_mode get_cache_mode();
} }
class exception : public std::exception { class exception : public std::exception {

View File

@ -26,10 +26,6 @@
#define CYAN ESC "36m" #define CYAN ESC "36m"
namespace cpptrace { namespace cpptrace {
namespace detail {
std::atomic_bool absorb_trace_exceptions(true);
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
raw_trace raw_trace::current(std::uint_least32_t skip) { raw_trace raw_trace::current(std::uint_least32_t skip) {
return generate_raw_trace(skip + 1); return generate_raw_trace(skip + 1);
@ -316,13 +312,28 @@ namespace cpptrace {
return detail::demangle(name); return detail::demangle(name);
} }
namespace detail {
std::atomic_bool absorb_trace_exceptions(true);
std::atomic<enum cache_mode> cache_mode(cache_mode::prioritize_speed);
}
CPPTRACE_API void absorb_trace_exceptions(bool absorb) { CPPTRACE_API void absorb_trace_exceptions(bool absorb) {
detail::absorb_trace_exceptions = absorb; detail::absorb_trace_exceptions = absorb;
} }
namespace experimental {
CPPTRACE_API void set_cache_mode(cache_mode mode) {
detail::cache_mode = mode;
}
}
namespace detail { namespace detail {
CPPTRACE_API bool should_absorb_trace_exceptions() { CPPTRACE_API bool should_absorb_trace_exceptions() {
return detail::absorb_trace_exceptions; return absorb_trace_exceptions;
}
CPPTRACE_API enum cache_mode get_cache_mode() {
return cache_mode;
} }
} }
} }

View File

@ -398,7 +398,13 @@ namespace dbghelp {
// TODO: When does this need to be called? Can it be moved to the symbolizer? // TODO: When does this need to be called? Can it be moved to the symbolizer?
SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS); SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
HANDLE proc = GetCurrentProcess(); HANDLE proc = GetCurrentProcess();
get_syminit_manager().init(proc); if(get_cache_mode() == cache_mode::prioritize_speed) {
get_syminit_manager().init(proc);
} else {
if(!SymInitialize(proc, NULL, TRUE)) {
throw std::logic_error("Cpptrace SymInitialize failed");
}
}
for(const auto frame : frames) { for(const auto frame : frames) {
try { try {
trace.push_back(resolve_frame(proc, frame)); trace.push_back(resolve_frame(proc, frame));
@ -409,6 +415,11 @@ namespace dbghelp {
trace.push_back(null_frame); trace.push_back(null_frame);
} }
} }
if(get_cache_mode() != cache_mode::prioritize_speed) {
if(!SymCleanup(proc)) {
throw std::logic_error("Cpptrace SymCleanup failed");
}
}
return trace; return trace;
} }
} }

View File

@ -575,7 +575,7 @@ namespace libdwarf {
// Check for .debug_aranges for fast lookup // Check for .debug_aranges for fast lookup
wrap(dwarf_get_aranges, dbg, &aranges, &arange_count); wrap(dwarf_get_aranges, dbg, &aranges, &arange_count);
} }
if(ok && !aranges) { if(ok && !aranges && get_cache_mode() != cache_mode::prioritize_memory) {
walk_compilation_units([this] (const die_object& cu_die) { walk_compilation_units([this] (const die_object& cu_die) {
Dwarf_Half offset_size = 0; Dwarf_Half offset_size = 0;
Dwarf_Half dwversion = 0; Dwarf_Half dwversion = 0;
@ -839,39 +839,44 @@ namespace libdwarf {
Dwarf_Half dwversion, Dwarf_Half dwversion,
stacktrace_frame& frame stacktrace_frame& frame
) { ) {
auto off = cu_die.get_global_offset(); if(get_cache_mode() == cache_mode::prioritize_memory) {
auto it = subprograms_cache.find(off); retrieve_symbol_walk(cu_die, pc, dwversion, frame);
if(it == subprograms_cache.end()) {
std::vector<subprogram_entry> vec;
preprocess_subprograms(cu_die, dwversion, vec);
std::sort(vec.begin(), vec.end(), [] (const subprogram_entry& a, const subprogram_entry& b) {
return a.low < b.low;
});
subprograms_cache.emplace(off, std::move(vec));
it = subprograms_cache.find(off);
}
auto& vec = it->second;
auto vec_it = std::lower_bound(
vec.begin(),
vec.end(),
pc,
[] (const subprogram_entry& entry, Dwarf_Addr pc) {
return entry.low < pc;
}
);
// vec_it is first >= pc
// we want first <= pc
if(vec_it != vec.begin()) {
vec_it--;
}
// If the vector has been empty this can happen
if(vec_it != vec.end()) {
//vec_it->die.print();
if(vec_it->die.pc_in_die(dwversion, pc)) {
retrieve_symbol_for_subprogram(vec_it->die, dwversion, frame);
}
} else { } else {
ASSERT(vec.size() == 0, "Vec should be empty?"); auto off = cu_die.get_global_offset();
auto it = subprograms_cache.find(off);
if(it == subprograms_cache.end()) {
// TODO: Refactor. Do the sort in the preprocess function and return the vec directly.
std::vector<subprogram_entry> vec;
preprocess_subprograms(cu_die, dwversion, vec);
std::sort(vec.begin(), vec.end(), [] (const subprogram_entry& a, const subprogram_entry& b) {
return a.low < b.low;
});
subprograms_cache.emplace(off, std::move(vec));
it = subprograms_cache.find(off);
}
auto& vec = it->second;
auto vec_it = std::lower_bound(
vec.begin(),
vec.end(),
pc,
[] (const subprogram_entry& entry, Dwarf_Addr pc) {
return entry.low < pc;
}
);
// vec_it is first >= pc
// we want first <= pc
if(vec_it != vec.begin()) {
vec_it--;
}
// If the vector has been empty this can happen
if(vec_it != vec.end()) {
//vec_it->die.print();
if(vec_it->die.pc_in_die(dwversion, pc)) {
retrieve_symbol_for_subprogram(vec_it->die, dwversion, frame);
}
} else {
ASSERT(vec.size() == 0, "Vec should be empty?");
}
} }
} }
@ -998,7 +1003,7 @@ namespace libdwarf {
retrieve_symbol(cu_die, pc, dwversion, frame); retrieve_symbol(cu_die, pc, dwversion, frame);
} }
} else { } else {
if(false) { // TODO: Proper condition if(get_cache_mode() == cache_mode::prioritize_memory) {
// walk for the cu and go from there // walk for the cu and go from there
walk_compilation_units([this, pc, &frame] (const die_object& cu_die) { walk_compilation_units([this, pc, &frame] (const die_object& cu_die) {
Dwarf_Half offset_size = 0; Dwarf_Half offset_size = 0;

View File

@ -89,7 +89,13 @@ namespace detail {
// //
HANDLE proc = GetCurrentProcess(); HANDLE proc = GetCurrentProcess();
HANDLE thread = GetCurrentThread(); HANDLE thread = GetCurrentThread();
get_syminit_manager().init(proc); if(get_cache_mode() == cache_mode::prioritize_speed) {
get_syminit_manager().init(proc);
} else {
if(!SymInitialize(proc, NULL, TRUE)) {
throw std::logic_error("Cpptrace SymInitialize failed");
}
}
while(trace.size() < max_depth) { while(trace.size() < max_depth) {
if( if(
!StackWalk64( !StackWalk64(
@ -122,6 +128,11 @@ namespace detail {
break; break;
} }
} }
if(get_cache_mode() != cache_mode::prioritize_speed) {
if(!SymCleanup(proc)) {
throw std::logic_error("Cpptrace SymCleanup failed");
}
}
return trace; return trace;
} }
} }