Update the Unwinder object and add tests.
Changes:
- Remove unused GetReturnAddressFromDefault function and tests.
- Modify the unwinder to stop when a pc/sp in a device map.
- Modify the unwinder to skip initial frames based on map names.
- Unit tests that exercise all of the paths in the unwinder code.
- Move the test Elf/ElfInterface objects into their own file.
- Update RegsFake to handle extra cases.
- Modify libbacktrace code to use this unwinder.
The new unwinder does not implement the ignore frame functionality since
this is not used very often and is better implemented using a skip frames
in named libraries functionality.
Test: Ran new unit tests, ran backtrace tests.
Change-Id: Ifd65e9acd66ac5e2d0e04bd32a9ad870b54610ff
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index b4481cc..41153ce 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -22,6 +22,7 @@
#include <ucontext.h>
#include <memory>
+#include <set>
#include <string>
#if !defined(__ANDROID__)
@@ -37,6 +38,8 @@
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
#include "BacktraceLog.h"
#include "UnwindStack.h"
#include "UnwindStackMap.h"
@@ -63,135 +66,42 @@
return name;
}
-static bool IsUnwindLibrary(const std::string& map_name) {
- const std::string library(basename(map_name.c_str()));
- return library == "libunwindstack.so" || library == "libbacktrace.so";
-}
-
-static void SetFrameInfo(unwindstack::Regs* regs, unwindstack::MapInfo* map_info,
- uint64_t adjusted_rel_pc, backtrace_frame_data_t* frame) {
- // This will point to the adjusted absolute pc. regs->pc() is
- // unaltered.
- frame->pc = map_info->start + adjusted_rel_pc;
- frame->sp = regs->sp();
- frame->rel_pc = adjusted_rel_pc;
- frame->stack_size = 0;
-
- frame->map.start = map_info->start;
- frame->map.end = map_info->end;
- frame->map.offset = map_info->offset;
- frame->map.flags = map_info->flags;
- frame->map.name = map_info->name;
-
- unwindstack::Elf* elf = map_info->elf;
- frame->map.load_bias = elf->GetLoadBias();
- uint64_t func_offset = 0;
- if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
- frame->func_name = demangle(frame->func_name.c_str());
- } else {
- frame->func_name = "";
- }
- frame->func_offset = func_offset;
-}
-
static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
+ static std::set<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
- unwindstack::Maps* maps = stack_map->stack_maps();
- bool adjust_rel_pc = false;
- size_t num_frames = 0;
- frames->clear();
- bool return_address_attempted = false;
auto process_memory = stack_map->process_memory();
- while (num_frames < MAX_BACKTRACE_FRAMES) {
- unwindstack::MapInfo* map_info = maps->Find(regs->pc());
- bool stepped;
- bool in_device_map = false;
- if (map_info == nullptr) {
- stepped = false;
- if (num_ignore_frames == 0) {
- frames->resize(num_frames + 1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
- frame->pc = regs->pc();
- frame->sp = regs->sp();
- frame->rel_pc = frame->pc;
- num_frames++;
- } else {
- num_ignore_frames--;
- }
- } else {
- unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
- uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+ unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
+ regs, stack_map->process_memory());
+ unwinder.Unwind(&skip_names);
- if (frames->size() != 0 || !IsUnwindLibrary(map_info->name)) {
- if (num_ignore_frames == 0) {
- uint64_t adjusted_rel_pc = rel_pc;
- if (adjust_rel_pc) {
- adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
- }
+ if (num_ignore_frames >= unwinder.NumFrames()) {
+ frames->resize(0);
+ return true;
+ }
- frames->resize(num_frames + 1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
- frame->num = num_frames;
- SetFrameInfo(regs, map_info, adjusted_rel_pc, frame);
+ frames->resize(unwinder.NumFrames() - num_ignore_frames);
+ auto unwinder_frames = unwinder.frames();
+ size_t cur_frame = 0;
+ for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, cur_frame++) {
+ auto frame = &unwinder_frames[i];
+ backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
- if (num_frames > 0) {
- // Set the stack size for the previous frame.
- backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
- prev->stack_size = frame->sp - prev->sp;
- }
- num_frames++;
- } else {
- num_ignore_frames--;
- }
- }
+ back_frame->num = frame->num;
- if (map_info->flags & PROT_DEVICE_MAP) {
- // Do not stop here, fall through in case we are
- // in the speculative unwind path and need to remove
- // some of the speculative frames.
- stepped = false;
- in_device_map = true;
- } else {
- unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
- if (sp_info->flags & PROT_DEVICE_MAP) {
- // Do not stop here, fall through in case we are
- // in the speculative unwind path and need to remove
- // some of the speculative frames.
- stepped = false;
- in_device_map = true;
- } else {
- bool finished;
- stepped = elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get(), &finished);
- if (stepped && finished) {
- break;
- }
- }
- }
- }
- adjust_rel_pc = true;
+ back_frame->rel_pc = frame->rel_pc;
+ back_frame->pc = frame->pc;
+ back_frame->sp = frame->sp;
- if (!stepped) {
- if (return_address_attempted) {
- // Remove the speculative frame.
- if (frames->size() > 0) {
- frames->pop_back();
- }
- break;
- } else if (in_device_map) {
- // Do not attempt any other unwinding, pc or sp is in a device
- // map.
- break;
- } else {
- // Stepping didn't work, try this secondary method.
- if (!regs->SetPcFromReturnAddress(process_memory.get())) {
- break;
- }
- return_address_attempted = true;
- }
- } else {
- return_address_attempted = false;
- }
+ back_frame->func_name = frame->function_name;
+ back_frame->func_offset = frame->function_offset;
+
+ back_frame->map.name = frame->map_name;
+ back_frame->map.start = frame->map_start;
+ back_frame->map.end = frame->map_end;
+ back_frame->map.offset = frame->map_offset;
+ back_frame->map.load_bias = frame->map_load_bias;
+ back_frame->map.flags = frame->map_flags;
}
return true;