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
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
namespace cpptrace {
std::string demangle(const std::string& name);
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 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 {
CPPTRACE_API bool should_absorb_trace_exceptions();
CPPTRACE_API enum cache_mode get_cache_mode();
}
class exception : public std::exception {

View File

@ -26,10 +26,6 @@
#define CYAN ESC "36m"
namespace cpptrace {
namespace detail {
std::atomic_bool absorb_trace_exceptions(true);
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
raw_trace raw_trace::current(std::uint_least32_t skip) {
return generate_raw_trace(skip + 1);
@ -316,13 +312,28 @@ namespace cpptrace {
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) {
detail::absorb_trace_exceptions = absorb;
}
namespace experimental {
CPPTRACE_API void set_cache_mode(cache_mode mode) {
detail::cache_mode = mode;
}
}
namespace detail {
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?
SymSetOptions(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
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) {
try {
trace.push_back(resolve_frame(proc, frame));
@ -409,6 +415,11 @@ namespace dbghelp {
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;
}
}

View File

@ -575,7 +575,7 @@ namespace libdwarf {
// Check for .debug_aranges for fast lookup
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) {
Dwarf_Half offset_size = 0;
Dwarf_Half dwversion = 0;
@ -839,39 +839,44 @@ namespace libdwarf {
Dwarf_Half dwversion,
stacktrace_frame& frame
) {
auto off = cu_die.get_global_offset();
auto it = subprograms_cache.find(off);
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);
}
if(get_cache_mode() == cache_mode::prioritize_memory) {
retrieve_symbol_walk(cu_die, pc, dwversion, frame);
} 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);
}
} else {
if(false) { // TODO: Proper condition
if(get_cache_mode() == cache_mode::prioritize_memory) {
// walk for the cu and go from there
walk_compilation_units([this, pc, &frame] (const die_object& cu_die) {
Dwarf_Half offset_size = 0;

View File

@ -89,7 +89,13 @@ namespace detail {
//
HANDLE proc = GetCurrentProcess();
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) {
if(
!StackWalk64(
@ -122,6 +128,11 @@ namespace detail {
break;
}
}
if(get_cache_mode() != cache_mode::prioritize_speed) {
if(!SymCleanup(proc)) {
throw std::logic_error("Cpptrace SymCleanup failed");
}
}
return trace;
}
}