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/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 1a86e16..e648927 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#define _GNU_SOURCE 1
#include <elf.h>
#include <inttypes.h>
#include <stdint.h>
+#include <string.h>
#include <sys/types.h>
#include <unistd.h>
@@ -28,35 +30,33 @@
namespace unwindstack {
-void Unwinder::FillInFrame(MapInfo* map_info, uint64_t* rel_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
frame->num = frame_num;
frame->pc = regs_->pc();
frame->sp = regs_->sp();
- frame->rel_pc = frame->pc;
+ frame->rel_pc = rel_pc;
if (map_info == nullptr) {
return;
}
- Elf* elf = map_info->GetElf(process_memory_, true);
- *rel_pc = elf->GetRelPc(regs_->pc(), map_info);
- if (frame_num != 0) {
+ if (adjust_pc) {
// Don't adjust the first frame pc.
- frame->rel_pc = regs_->GetAdjustedPc(*rel_pc, elf);
+ frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
// Adjust the original pc.
- frame->pc -= *rel_pc - frame->rel_pc;
- } else {
- frame->rel_pc = *rel_pc;
+ frame->pc -= rel_pc - frame->rel_pc;
}
frame->map_name = map_info->name;
frame->map_offset = map_info->elf_offset;
frame->map_start = map_info->start;
frame->map_end = map_info->end;
+ frame->map_flags = map_info->flags;
+ frame->map_load_bias = elf->GetLoadBias();
if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
frame->function_name = "";
@@ -64,25 +64,59 @@
}
}
-void Unwinder::Unwind() {
+void Unwinder::Unwind(std::set<std::string>* initial_map_names_to_skip) {
frames_.clear();
bool return_address_attempt = false;
+ bool adjust_pc = false;
for (; frames_.size() < max_frames_;) {
MapInfo* map_info = maps_->Find(regs_->pc());
uint64_t rel_pc;
- FillInFrame(map_info, &rel_pc);
+ Elf* elf;
+ if (map_info == nullptr) {
+ rel_pc = regs_->pc();
+ } else {
+ elf = map_info->GetElf(process_memory_, true);
+ rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+ }
+
+ if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
+ initial_map_names_to_skip->find(basename(map_info->name.c_str())) ==
+ initial_map_names_to_skip->end()) {
+ FillInFrame(map_info, elf, rel_pc, adjust_pc);
+ // Once a frame is added, stop skipping frames.
+ initial_map_names_to_skip = nullptr;
+ }
+ adjust_pc = true;
bool stepped;
+ bool in_device_map = false;
if (map_info == nullptr) {
stepped = false;
} else {
- bool finished;
- stepped = map_info->elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(),
- &finished);
- if (stepped && finished) {
- break;
+ if (map_info->flags & MAPS_FLAGS_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 {
+ MapInfo* sp_info = maps_->Find(regs_->sp());
+ if (sp_info != nullptr && sp_info->flags & MAPS_FLAGS_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;
+ }
+ }
}
}
if (!stepped) {
@@ -90,6 +124,10 @@
// Remove the speculative frame.
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 {
// Steping didn't work, try this secondary method.
if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {