Add cache mode config
This commit is contained in:
parent
93dde93802
commit
d84a3167d9
17
README.md
17
README.md
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user